diff options
author | midipix <writeonce@midipix.org> | 2015-07-27 04:01:18 -0400 |
---|---|---|
committer | midipix <writeonce@midipix.org> | 2015-07-27 04:01:18 -0400 |
commit | dd89bb8ad4fe184a34b5dbdda237e640fc82121b (patch) | |
tree | 5e80d2da35f5892f92be29f57982b2708e6bd99b /src/argv | |
parent | dcdadc2702712fa750ed255ed1dfa354522797a0 (diff) | |
download | ntapi-dd89bb8ad4fe184a34b5dbdda237e640fc82121b.tar.bz2 ntapi-dd89bb8ad4fe184a34b5dbdda237e640fc82121b.tar.xz |
entered advanced internal development stage.
Diffstat (limited to 'src/argv')
-rw-r--r-- | src/argv/ntapi_tt_argv_envp.c | 717 | ||||
-rw-r--r-- | src/argv/ntapi_tt_array_utf16.c | 258 | ||||
-rw-r--r-- | src/argv/ntapi_tt_array_utf8.c | 117 | ||||
-rw-r--r-- | src/argv/ntapi_tt_env_vars.c | 112 | ||||
-rw-r--r-- | src/argv/ntapi_tt_get_option.c | 451 |
5 files changed, 1655 insertions, 0 deletions
diff --git a/src/argv/ntapi_tt_argv_envp.c b/src/argv/ntapi_tt_argv_envp.c new file mode 100644 index 0000000..bfa0cd2 --- /dev/null +++ b/src/argv/ntapi_tt_argv_envp.c @@ -0,0 +1,717 @@ +/********************************************************/ +/* ntapi: Native API core library */ +/* Copyright (C) 2013,2014,2015 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */ +/********************************************************/ + +#include <psxtypes/psxtypes.h> +#include <pemagine/pemagine.h> +#include <ntapi/ntapi.h> +#include "ntapi_impl.h" + + +/** + * rules for parsing the process's command line arguments + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * + * delimiters: + * ----------- + * + white space (ascii 0x20) + * + horizontal tab (ascii 0x09) + * + * quoted strings, and special characters + * -------------------------------------- + * + delimiter characters within a quoted string ("string with white space", + * or string" with white "space), stand for their literal respective + * characters. + * + * + a backslash followed by a double quote (\") stands for a literal + * double quote. + * + * + unless followed by a double quote, a backslash is just a (literal) + * backslash. + * + * + when followed by a double quotation mark, an even sequence of 2 or + * more backslashes (2n) should be interpreted as a sequence of n literal + * backslashes. The double quotation mark then designates the start + * or end of a double quoted string. + * + * + when followed by a double quotation mark, an odd sequence of 2 or + * more backslashes (2n+1) should be interpreted as a sequence of n + * literal backslashes, followed by a single literal double quote. + * + * + if found within a double quoted string, a sequence of two double + * quotation marks should be interpreted as a single literal double + * quote. + * + * + balanced nesting of syntactic double quotes is permitted. + * +**/ + +/* free-standing process runtime data */ +static nt_runtime_data __rtdata; + +int32_t __stdcall __ntapi_tt_parse_cmd_line_args_utf16( + __in wchar16_t * cmd_line, + __out int * arg_count, + __in wchar16_t * args_buffer, + __in size_t args_buffer_len, + __out size_t * args_bytes_written __optional, + __in wchar16_t ** argv_buffer, + __in size_t argv_buffer_len, + __in uint32_t arg_flags) +{ + /** + * parse the command line arguments pointed to by cmd_line, + * copy the parsed arguments to args_buffer, + * and return 0 upon success. + * + * cmd_line must be a valid pointer to a command line string, + * and args_buffer, argv_buffer, and arg_count should + * all be aligned; furthermore, args_buffer_len and + * and argv_buffer_len must be exact multiples of sizeof(size_t). + * + * In case of an error, report failure using the appropriate + * native status code. + **/ + + /** + * UTF-16: no need to fully determine the code point of the + * current character; all we need to do is validate the + * character or surrogate pair, and set the value of + * wch_next accordingly. + **/ + + #define HORIZONTAL_TAB 0x09 + #define WHITE_SPACE 0x20 + #define DOUBLE_QUOTE 0x22 + #define SINGLE_QUOTE 0x27 + #define BACKSLASH 0x5C + + #define IS_DELIMITER(x) ((x == HORIZONTAL_TAB) || (x == WHITE_SPACE)) + + #define TEST_ARGS_BUFFER(nbytes) \ + if ((uintptr_t)arg + nbytes \ + > (uintptr_t)args_buffer + args_buffer_len) { \ + return NT_STATUS_BUFFER_TOO_SMALL; \ + } + + #define ADD_N_BACKSLASHES \ + TEST_ARGS_BUFFER(backslash_count * sizeof(wchar16_t)); \ + for (islash = 0; \ + islash < backslash_count; \ + islash++) { \ + *arg = BACKSLASH; \ + arg++; \ + } \ + backslash_count = 0; + + #define ADD_SINGLE_WCHAR16_t(x) \ + TEST_ARGS_BUFFER(sizeof(wchar16_t)); \ + *arg = x; \ + arg++; + + wchar16_t * arg; /* null-terminated, copied to buffer */ + wchar16_t ** parg; /* next pointer in the argv array */ + wchar16_t * wch; /* character being processed */ + wchar16_t * wch_next; + unsigned int backslash_count; + unsigned int islash; + unsigned char quoted_state; + + /* check parameters for validity and alignment */ + if ((!(uintptr_t)cmd_line) || (*cmd_line == 0)) + /* we require at least one argument */ + return NT_STATUS_INVALID_PARAMETER_1; + + else if (__NT_IS_MISALIGNED_BUFFER(args_buffer)) + return NT_STATUS_INVALID_PARAMETER_2; + + else if (__NT_IS_MISALIGNED_LENGTH(args_buffer_len)) + return NT_STATUS_INVALID_PARAMETER_3; + + else if (__NT_IS_MISALIGNED_BUFFER(argv_buffer)) + return NT_STATUS_INVALID_PARAMETER_5; + + else if (__NT_IS_MISALIGNED_LENGTH(argv_buffer_len)) + return NT_STATUS_INVALID_PARAMETER_6; + + else if (__NT_IS_MISALIGNED_BUFFER(arg_count)) + return NT_STATUS_INVALID_PARAMETER_7; + + /* zero-out the aligned buffers */ + __ntapi->tt_aligned_block_memset(args_buffer,0,args_buffer_len); + __ntapi->tt_aligned_block_memset(argv_buffer,0,argv_buffer_len); + + /* initialize */ + wch = cmd_line; + arg = args_buffer; + parg = argv_buffer; + *parg = arg; + *arg_count = 0; + quoted_state = 0; + backslash_count = 0; + + /* arg points to the first character of a command line argument */ + /* parg points to the next pointer in argv_buffer */ + while (*wch) { + if (!(quoted_state) && (IS_DELIMITER(*wch))) { + /* pending backslashes? */ + if (backslash_count) + ADD_N_BACKSLASHES; + + /* reached a delimiter outside of a quoted string */ + /* argument: alignment and null-termination */ + arg = (wchar16_t *)((((uintptr_t)arg + sizeof(size_t)) + | (sizeof(size_t) - 1)) + ^ (sizeof(size_t) - 1)); + + /* skip this and remaining delimiters */ + wch_next = wch + 1; + while ((*wch_next) && (IS_DELIMITER(*wch_next))) + wch_next++; + + /* keep going? */ + if (*wch_next == 0) { + /* no more characters to process */ + /* nothing to do */ + } else if ((uintptr_t)parg >= \ + (uintptr_t)argv_buffer \ + + argv_buffer_len) { + /* argv_buffer is too small */ + return NT_STATUS_BUFFER_TOO_SMALL; + } else if ((uintptr_t)arg >= \ + (uintptr_t)args_buffer \ + + args_buffer_len) { + /* args_buffer is too small */ + return NT_STATUS_BUFFER_TOO_SMALL; + } else { + /* advance parg, set last member */ + parg++; + *parg = arg; + } + } else { + /* the current character is not a delimiter... */ + /* determine wch_next */ + if (((*wch >= 0x0000) && (*wch < 0xD800)) \ + || ((*wch >= 0xE000) && (*wch < 0x10000))) { + /* in the BMP, single 16-bit representation */ + wch_next = wch + 1; + } else if ((*wch >= 0xD800) && (*wch < 0xDC00)) { + /* validate surrogate pair */ + wch_next = wch + 1; + + if ((*wch_next >= 0xDC00) && (*wch_next < 0xE000)) + /* this is a valid surrogate pair */ + wch_next++; + else + return NT_STATUS_ILLEGAL_CHARACTER; + } else + return NT_STATUS_ILLEGAL_CHARACTER; + + /* we now know the position of this and the next character */ + /* continue with special cases */ + + if (quoted_state && (*wch == DOUBLE_QUOTE) \ + && (*wch_next == DOUBLE_QUOTE)) { + /** + * two consecutive double quotation marks + * within a quoted string: + * add a single quotation mark to the argument + **/ + ADD_SINGLE_WCHAR16_t(DOUBLE_QUOTE); + wch_next++; + } else if (((backslash_count % 2) == 0) \ + && (*wch == BACKSLASH) \ + && (*wch_next == DOUBLE_QUOTE)) { + /* 2n+1 backslashes followed by a double quote */ + backslash_count /= 2; + /* add n backslashes */ + ADD_N_BACKSLASHES; + /* add a literal double quotation mark */ + ADD_SINGLE_WCHAR16_t(DOUBLE_QUOTE); + /* get ready for next character */ + wch_next++; + } else if (backslash_count && (*wch == DOUBLE_QUOTE)) { + /* 2n backslashes followed by a double quote */ + backslash_count /= 2; + /* add n backslashes */ + ADD_N_BACKSLASHES; + /* turn quoted_state on/off */ + quoted_state = !quoted_state; + } else if ((*wch == BACKSLASH) \ + && (*wch_next == BACKSLASH)) { + /* this is a sequence of two backslashes */ + backslash_count += 2; + wch_next++; + } else { + /* copy pending backslashes as needed */ + if (backslash_count) + ADD_N_BACKSLASHES; + + if (*wch == DOUBLE_QUOTE) { + /* turn quoted_state on/off */ + quoted_state = !quoted_state; + } else { + /* copy either two or four bytes */ + ADD_SINGLE_WCHAR16_t(*wch); + wch++; + + /* surrogate pair? */ + if (wch < wch_next) { + ADD_SINGLE_WCHAR16_t(*wch); + } + } + } + } + + /* proceed to the next character (or null termination) */ + wch = wch_next; + } + + /* pending backslashes? */ + if (backslash_count) + ADD_N_BACKSLASHES; + + /* null termination */ + ADD_SINGLE_WCHAR16_t(0); + + /* how many arguments did you say? */ + *arg_count = (int)(((uintptr_t)parg - (uintptr_t)argv_buffer) + / sizeof(size_t) + 1); + + /* output bytes written */ + if (args_bytes_written) + *args_bytes_written = (uintptr_t)arg - (uintptr_t)args_buffer; + + return NT_STATUS_SUCCESS; +} + + +int32_t __stdcall __ntapi_tt_get_argv_envp_utf16( + __out int * argc, + __out wchar16_t *** wargv, + __out wchar16_t *** wenvp, + __in uint32_t flags, + __in void * ext_params __optional, + __out void * reserved __optional) +{ + nt_runtime_data * rtdata; + nt_argv_envp_block_info main_params_internal; + nt_argv_envp_block_info * main_params; + nt_get_argv_envp_ext_params * __ext_params; + ntapi_internals * __internals; + + unsigned idx; + int32_t status; + uintptr_t addr; + intptr_t offset; + wchar16_t * wch_s; + wchar16_t * wch_dst; + wchar16_t ** wch_p; + char ** ch_p; + uintptr_t * psrc; + uintptr_t * pdst; + uintptr_t * paligned; + wchar16_t * pboundary; + + /* init */ + __internals = __ntapi_internals(); + + /* use internal buffer? */ + if (flags & NT_GET_ARGV_ENVP_USE_CALLER_BUFFER) { + __ext_params = (nt_get_argv_envp_ext_params *)ext_params; + main_params = &(__ext_params->argv_envp_block_info); + } else { + /* pointers to internal/local structures */ + main_params = &main_params_internal; + + /* init */ + __ntapi->tt_aligned_block_memset( + main_params,0, + sizeof(*main_params)); + + /* use internal buffer */ + main_params->cmd_line = __ntapi_tt_get_cmd_line_utf16(); + main_params->wargv_buffer = __internals->ntapi_img_sec_bss->argv_envp_array; + main_params->wargv_buffer_len = __NT_BSS_ARGV_BUFFER_SIZE; + main_params->argv_envp_ptr_total = (int)(main_params->wargv_buffer_len + / sizeof(uintptr_t)); + main_params->wargs_buffer = (wchar16_t *)&(__internals->ntapi_img_sec_bss->args_envs_buffer); + main_params->wargs_buffer_len = __NT_BSS_ARGS_BUFFER_SIZE; + } + + /* (__ntapi_parse_cmd_line_args_utf16 will zero-out both buffers) */ + status = __ntapi_tt_parse_cmd_line_args_utf16( + main_params->cmd_line, + &main_params->argc, + main_params->wargs_buffer, + main_params->wargs_buffer_len, + &main_params->wargs_bytes_written, + main_params->wargv_buffer, + main_params->wargv_buffer_len, + 0); + + if (status) return status; + + /* argv[] needs a terminating null pointer */ + if (main_params->argc == main_params->argv_envp_ptr_total) + return NT_STATUS_BUFFER_TOO_SMALL; + + /* set idx to the envp[0] array index */ + idx = main_params->argc + 1; + + /* set wenvp[] to its starting address */ + main_params->wenvp_buffer = &main_params->wargv_buffer[idx]; + + /* update wargv_buffer_len and envp_buffer_len */ + main_params->wenvp_buffer_len = main_params->wargv_buffer_len + - (idx * sizeof(uintptr_t)); + + main_params->wargv_buffer_len = idx * sizeof(uintptr_t); + + /* align wenvs at pointer-size boundary */ + main_params->wargs_bytes_written += sizeof(uintptr_t) - 1; + main_params->wargs_bytes_written /= sizeof(uintptr_t); + main_params->wargs_bytes_written *= sizeof(uintptr_t); + + /* book-keeping */ + main_params->wenvs_buffer = main_params->wargs_buffer + + main_params->wargs_bytes_written; + + main_params->wenvs_buffer_len = main_params->wargs_buffer_len + - main_params->wargs_bytes_written; + + main_params->wargs_buffer_len = main_params->wargs_bytes_written; + + + /* peb environment block (read-only) */ + wch_s = __ntapi_tt_get_peb_env_block_utf16(); + + if ((!wch_s) || (!*wch_s)) + return NT_STATUS_DLL_INIT_FAILED; + + /* populate the envp[] array */ + while ((*wch_s) && (idx < main_params->argv_envp_ptr_total)) { + main_params->envc++; + wch_p = &(main_params->wargv_buffer[idx]); + *wch_p = wch_s; + + /* skip the rest of the environment variable */ + while (*++wch_s); + + /* advance to the next variable (or final null termination) */ + wch_s++; + idx++; + } + + /* envp[] needs a terminating null pointer */ + if ((*wch_s) && (idx = main_params->argv_envp_ptr_total)) + return NT_STATUS_BUFFER_TOO_SMALL; + + /* copy environment? */ + if (flags & NT_GET_ARGV_ENVP_COPY_ENVIRONMENT) { + /* wch_s now points at the final null termination */ + main_params->wenvs_bytes_used = + ((uintptr_t)wch_s + - (uintptr_t)(*main_params->wenvp_buffer)); + + /* do we have enough room? */ + if (main_params->wenvs_buffer_len < main_params->wenvs_bytes_used) + return NT_STATUS_BUFFER_TOO_SMALL; + + /* upper boundary */ + pboundary = ++wch_s; + + /* you'd expect the peb environment block to be aligned, + but one can never know... */ + wch_s = *main_params->wenvp_buffer; + wch_dst = main_params->wenvs_buffer; + + while ((uintptr_t)wch_s % sizeof(uintptr_t)) { + *wch_dst = *wch_s; + wch_s++; + wch_dst++; + } + + /* copy the aligned portion of the environment block */ + addr = (uintptr_t)(pboundary); + addr /= sizeof(uintptr_t); + addr *= sizeof(uintptr_t); + paligned = (uintptr_t *)addr; + + psrc = (uintptr_t *)wch_s; + pdst = (uintptr_t *)wch_dst; + + while (psrc < paligned) { + *pdst = *psrc; + psrc++; + pdst++; + } + + /* copy any remaining bytes */ + wch_s = (wchar16_t *)paligned; + wch_dst = (wchar16_t *)pdst; + + while (wch_s < pboundary) { + *wch_dst = *wch_s; + wch_s++; + wch_dst++; + } + + /* finally, we update the envp[] pointers */ + offset = (intptr_t)main_params->wenvs_buffer + - (intptr_t)*main_params->wenvp_buffer; + + wch_p = main_params->wenvp_buffer; + + while (*wch_p) { + addr = ((uintptr_t)*wch_p) + offset; + *wch_p = (wchar16_t *)addr; + wch_p++; + } + } + + /* (command line arguments always get validated) */ + /* validate the environment block? */ + if (flags & NT_GET_ARGV_ENVP_VALIDATE_UTF16) { + wch_p = main_params->wenvp_buffer; + + while (*wch_p) { + status = __ntapi->uc_validate_unicode_stream_utf16( + *wch_p, + 0,0,0,0,0); + + if (status != NT_STATUS_SUCCESS) + return status; + else + wch_p++; + } + } + + /* once */ + if (!__internals->rtdata) { + __ntapi->tt_get_runtime_data( + &__internals->rtdata, + main_params->wargv_buffer); + + if (!__internals->rtdata) { + __internals->rtdata = &__rtdata; + + if ((status =__ntapi->tt_init_runtime_data(&__rtdata))) + return status; + + } else if ((status =__ntapi->tt_update_runtime_data(__internals->rtdata))) + return status; + + rtdata = __internals->rtdata; + + rtdata->peb_envc = main_params->envc; + rtdata->peb_argc = main_params->argc; + rtdata->peb_wargv = main_params->wargv_buffer; + rtdata->peb_wenvp = main_params->wenvp_buffer; + + /* integral wargv, wenvp, argv, envp */ + if (rtdata->wargv) { + rtdata->wargv += (uintptr_t)rtdata / sizeof(wchar16_t *); + + for (wch_p=rtdata->wargv; *wch_p; wch_p++) + *wch_p += (uintptr_t)rtdata / sizeof(wchar16_t); + }; + + if (rtdata->wenvp) { + rtdata->wenvp += (uintptr_t)rtdata / sizeof(wchar16_t *); + + for (wch_p=rtdata->wenvp; *wch_p; wch_p++) + *wch_p += (uintptr_t)rtdata / sizeof(wchar16_t); + } + + if (rtdata->argv) { + rtdata->argv += (uintptr_t)rtdata / sizeof(char *); + + for (ch_p=rtdata->argv; *ch_p; ch_p++) + *ch_p += (uintptr_t)rtdata; + + rtdata->argc = (int32_t)(ch_p - rtdata->argv); + }; + + if (rtdata->envp) { + rtdata->envp += (uintptr_t)rtdata / sizeof(char *); + + for (ch_p=rtdata->envp; *ch_p; ch_p++) + *ch_p += (uintptr_t)rtdata; + + rtdata->envc = (int32_t)(ch_p - rtdata->envp); + }; + } + + /* we're good */ + *argc = main_params->argc; + *wargv = main_params->wargv_buffer; + *wenvp = main_params->wenvp_buffer; + + return NT_STATUS_SUCCESS; +} + + +int32_t __stdcall __ntapi_tt_get_argv_envp_utf8( + __out int * argc, + __out char *** argv, + __out char *** envp, + __in uint32_t flags, + __in void * ext_params __optional, + __out void * reserved __optional) +{ + int32_t status; + ntapi_internals * __internals; + + wchar16_t ** wargv; + wchar16_t ** wenvp; + uint32_t pcount; + + nt_get_argv_envp_ext_params __ext_params_internal; + nt_get_argv_envp_ext_params * __ext_params; + nt_argv_envp_block_info * main_params; + + /* use internal buffer? */ + if (flags & NT_GET_ARGV_ENVP_USE_CALLER_BUFFER) { + __ext_params = (nt_get_argv_envp_ext_params *)ext_params; + main_params = &__ext_params->argv_envp_block_info; + } else { + /* pointers to internal/local structures */ + __ext_params = &__ext_params_internal; + main_params = &__ext_params->argv_envp_block_info; + + /* init */ + __ntapi->tt_aligned_block_memset( + main_params,0, + sizeof(*main_params)); + + __internals = __ntapi_internals(); + + /* use internal buffer */ + main_params->cmd_line = __ntapi_tt_get_cmd_line_utf16(); + main_params->wargv_buffer = __internals->ntapi_img_sec_bss->argv_envp_array; + main_params->wargv_buffer_len = __NT_BSS_ARGV_BUFFER_SIZE; + main_params->argv_envp_ptr_total = (int)(main_params->wargv_buffer_len + / sizeof(uintptr_t)); + main_params->wargs_buffer = (wchar16_t *)&(__internals->ntapi_img_sec_bss->args_envs_buffer); + main_params->wargs_buffer_len = __NT_BSS_ARGS_BUFFER_SIZE; + } + + /* start with obtaining the utf-16 environment */ + status = __ntapi->tt_get_argv_envp_utf16( + argc, + &wargv, + &wenvp, + flags | NT_GET_ARGV_ENVP_USE_CALLER_BUFFER, + __ext_params, + reserved); + + if (status) return status; + + /* enough pointers left? */ + pcount = main_params->argc + 1 + main_params->envc + 1; + + if (pcount > (main_params->argv_envp_ptr_total / 2)) + return NT_STATUS_BUFFER_TOO_SMALL; + else if ((main_params->wenvs_buffer_len - main_params->wenvs_bytes_used) + < sizeof(uintptr_t)) + return NT_STATUS_BUFFER_TOO_SMALL; + + /* first args byte should be aligned at pointer-size boundary */ + main_params->wenvs_bytes_used += sizeof(uintptr_t) - 1; + main_params->wenvs_bytes_used /= sizeof(uintptr_t); + main_params->wenvs_bytes_used *= sizeof(uintptr_t); + + /* book-keeping */ + /* block reminder: wargs -- wenvs -- args -- envs */ + main_params->argv = (char **)main_params->wenvp_buffer; + main_params->argv += main_params->envc + 1; + + main_params->args_buffer = (char *)main_params->wenvs_buffer; + main_params->args_buffer += main_params->wenvs_bytes_used; + + main_params->args_buffer_len = main_params->wenvs_buffer_len + - main_params->wenvs_bytes_used; + + main_params->wenvs_buffer_len = main_params->wenvs_bytes_used; + + /* create a utf-8 argv[] array */ + status = __ntapi_tt_array_convert_utf16_to_utf8( + main_params->wargv_buffer, + main_params->argv, + 0, + main_params->args_buffer, + main_params->args_buffer_len, + &main_params->args_bytes_written); + + if (status) return status; + + /* first envs byte should be aligned to pointer-size boundary */ + main_params->args_bytes_written += sizeof(uintptr_t) - 1; + main_params->args_bytes_written /= sizeof(uintptr_t); + main_params->args_bytes_written *= sizeof(uintptr_t); + + /* book-keeping */ + main_params->envp = main_params->argv + main_params->argc + 1; + + main_params->envs_buffer = main_params->args_buffer + + main_params->args_bytes_written; + + main_params->envs_buffer_len = main_params->args_buffer_len + - main_params->args_bytes_written; + + main_params->args_buffer_len = main_params->args_bytes_written; + + /* subsequent streams (if any) should be aligned to pointer-size boundary */ + main_params->envs_bytes_used += sizeof(uintptr_t) - 1; + main_params->envs_bytes_used /= sizeof(uintptr_t); + main_params->envs_bytes_used *= sizeof(uintptr_t); + + /* create a utf-8 envp[] array */ + status = __ntapi_tt_array_convert_utf16_to_utf8( + main_params->wenvp_buffer, + main_params->envp, + 0, + main_params->envs_buffer, + main_params->envs_buffer_len, + &main_params->envs_bytes_used); + + if (status) return status; + + /* we're good */ + *argc = main_params->argc; + *argv = main_params->argv; + *envp = main_params->envp; + + return NT_STATUS_SUCCESS; +} + + +wchar16_t * __stdcall __ntapi_tt_get_cmd_line_utf16(void) +{ + nt_peb * peb; + nt_unicode_string cmd_line; + + peb = (nt_peb *)pe_get_peb_address(); + + if (peb) { + cmd_line = peb->process_params->command_line; + return cmd_line.buffer; + } else + return (wchar16_t *)0; +} + + +wchar16_t * __stdcall __ntapi_tt_get_peb_env_block_utf16(void) +{ + nt_peb * peb; + + peb = (nt_peb *)pe_get_peb_address(); + + if (peb) + return peb->process_params->environment; + else + return (wchar16_t *)0; +} diff --git a/src/argv/ntapi_tt_array_utf16.c b/src/argv/ntapi_tt_array_utf16.c new file mode 100644 index 0000000..d8bbb8b --- /dev/null +++ b/src/argv/ntapi_tt_array_utf16.c @@ -0,0 +1,258 @@ +/********************************************************/ +/* ntapi: Native API core library */ +/* Copyright (C) 2013,2014,2015 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */ +/********************************************************/ + +#include <psxtypes/psxtypes.h> +#include <pemagine/pemagine.h> +#include <ntapi/nt_argv.h> +#include <ntapi/ntapi.h> +#include "ntapi_impl.h" + +/** + * scenario: program -e app [arg1 arg2 ... argn] + * input: a utf-16 argument vector + * output: a utf-16 cmd_line string + * example: tty_pipe_create_child_process +**/ + +int32_t __stdcall __ntapi_tt_array_copy_utf16( + __out int * argc, + __in const wchar16_t ** wargv, + __in const wchar16_t ** wenvp, + __in const wchar16_t * image_name __optional, + __in const wchar16_t * interpreter __optional, + __in const wchar16_t * optarg __optional, + __in void * base, + __out void * buffer, + __in size_t buflen, + __out size_t * blklen) +{ + const wchar16_t ** parg; + const wchar16_t * warg; + const wchar16_t * dummy; + wchar16_t * wch; + ptrdiff_t diff; + ptrdiff_t ptrs; + size_t needed; + + /* fallback */ + dummy = 0; + wargv = wargv ? wargv : &dummy; + wenvp = wenvp ? wenvp : &dummy; + + /* ptrs, needed */ + ptrs = 0; + needed = 0; + + if (image_name) { + ptrs++; + needed += sizeof(wchar16_t *) + + __ntapi->tt_string_null_offset_short((const int16_t *)image_name) + + sizeof(wchar16_t); + } + + for (parg=wargv; *parg; parg++) + needed += sizeof(wchar16_t *) + + __ntapi->tt_string_null_offset_short((const int16_t *)*parg) + + sizeof(wchar16_t); + + ptrs += (parg - wargv); + *argc = (int)ptrs; + + for (parg=wenvp; *parg; parg++) + needed += sizeof(wchar16_t *) + + __ntapi->tt_string_null_offset_short((const int16_t *)*parg) + + sizeof(wchar16_t); + + ptrs += (parg - wenvp); + + ptrs += 2; + needed += 2*sizeof(wchar16_t *); + blklen = blklen ? blklen : &needed; + *blklen = needed; + + if (buflen < needed) + return NT_STATUS_BUFFER_TOO_SMALL; + + /* init */ + parg = (const wchar16_t **)buffer; + wch = (wchar16_t *)(parg+ptrs); + diff = (uintptr_t)base / sizeof(wchar16_t); + + /* image_name */ + if (image_name) { + *parg++ = wch-diff; + for (warg=image_name; *warg; warg++,wch++) + *wch = *warg; + *wch++ = '\0'; + } + + /* argv */ + for (; *wargv; wargv++) { + *parg++=wch-diff; + for (warg=*wargv; *warg; warg++,wch++) + *wch = *warg; + *wch++ = '\0'; + } + + *parg++ = 0; + + /* envp */ + for (; *wenvp; wenvp++) { + *parg++=wch-diff; + for (warg=*wenvp; *warg; warg++,wch++) + *wch = *warg; + *wch++ = '\0'; + } + + *parg++ = 0; + + return NT_STATUS_SUCCESS; +} + +int32_t __stdcall __ntapi_tt_array_convert_utf16_to_utf8( + __in wchar16_t ** warrv, + __in char ** arrv, + __in void * base, + __in char * buffer, + __in size_t buffer_len, + __out size_t * bytes_written) +{ + uint8_t * ubound; + uint8_t * ch; + wchar16_t * wch; + wchar16_t wx; + wchar16_t wy; + wchar16_t wz; + wchar16_t wy_low; + wchar16_t wy_high; + wchar16_t ww; + wchar16_t uuuuu; + wchar16_t u_low; + wchar16_t u_high; + ptrdiff_t diff; + + #define __UTF8_MAX_CODE_POINT_BYTES (4) + + ch = (uint8_t *)buffer; + ubound = (uint8_t *)buffer + buffer_len - __UTF8_MAX_CODE_POINT_BYTES; + diff = (uintptr_t)base / sizeof(wchar16_t); + + while (warrv && *warrv) { + *arrv = (char *)(ch-(uintptr_t)base); + wch = *warrv + diff; + + /* all utf-16 streams at stake have been validated */ + while (*wch && (ch < ubound)) { + if (*wch <= 0x7F) { + /* from: 00000000 0xxxxxxx (little endian) */ + /* to: 0xxxxxxx (utf-8) */ + *ch = (char)(*wch); + } else if (*wch <= 0x7FF) { + /* from: 00000yyy yyxxxxxx (little endian) */ + /* to: 110yyyyy 10xxxxxx (utf-8) */ + wy = *wch; + wy >>= 6; + + wx = *wch; + wx <<= 10; + wx >>= 10; + + /* write the y part */ + *ch = (char)(0xC0 | wy); + ch++; + + /* write the x part */ + *ch = (char)(0x80 | wx); + } else if ((*wch < 0xD800) || (*wch >= 0xE000)) { + /* from: zzzzyyyy yyxxxxxx (little endian) */ + /* to: 1110zzzz 10yyyyyy 10xxxxxx (utf-8) */ + wz = *wch; + wz >>= 12; + + wy = *wch; + wy <<= 4; + wy >>= 10; + + wx = *wch; + wx <<= 10; + wx >>= 10; + + /* write the z part */ + *ch = (char)(0xE0 | wz); + ch++; + + /* write the y part */ + *ch = (char)(0x80 | wy); + ch++; + + /* write the x part */ + *ch = (char)(0x80 | wx); + } else { + /* from: 110110ww wwzzzzyy 110111yy yyxxxxxx (little endian) */ + /* to: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx (utf-8) */ + + /* low two bytes */ + wx = *wch; + wx <<= 10; + wx >>= 10; + + wy_low = *wch; + wy_low <<= 6; + wy_low >>= 12; + + /* (surrogate pair) */ + wch++; + + /* high two bytes */ + wy_high = *wch; + wy_high <<= 14; + wy_high >>= 10; + + wz = *wch; + wz <<= 10; + wz >>= 12; + wz <<= 2; + + ww = *wch; + ww <<= 6; + ww >>= 12; + + uuuuu = ww + 1; + u_high = uuuuu >> 2; + u_low = ((uuuuu << 14) >> 10); + + /* 1st byte: 11110uuu */ + *ch = (char)(0xF0 | u_high); + ch++; + + /* 2nd byte: 10uuzzzz */ + *ch = (char)(0x80 | u_low | wz); + ch++; + + /* 3rd byte: 10yyyyyy */ + *ch = (char)(0x80 | wy_low | wy_high); + ch++; + + /* 4th byte: 10xxxxxx */ + *ch = (char)(0x80 | wx); + } + + ch++; + wch++; + } + + if (*wch) + return NT_STATUS_BUFFER_TOO_SMALL; + + ch++; + arrv++; + warrv++; + } + + *bytes_written = (size_t)(ch - (uint8_t *)buffer); + + return NT_STATUS_SUCCESS; +} diff --git a/src/argv/ntapi_tt_array_utf8.c b/src/argv/ntapi_tt_array_utf8.c new file mode 100644 index 0000000..8d3b837 --- /dev/null +++ b/src/argv/ntapi_tt_array_utf8.c @@ -0,0 +1,117 @@ +/********************************************************/ +/* ntapi: Native API core library */ +/* Copyright (C) 2013,2014,2015 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */ +/********************************************************/ + +#include <psxtypes/psxtypes.h> +#include <pemagine/pemagine.h> +#include <ntapi/nt_argv.h> +#include <ntapi/ntapi.h> +#include "ntapi_impl.h" + +int32_t __stdcall __ntapi_tt_array_copy_utf8( + __out int * argc, + __in const char ** argv, + __in const char ** envp, + __in const char * image_name __optional, + __in const char * interpreter __optional, + __in const char * optarg __optional, + __in void * base, + __out void * buffer, + __in size_t buflen, + __out size_t * blklen) +{ + const char ** parg; + const char * arg; + const char * dummy; + char * ch; + ptrdiff_t diff; + ptrdiff_t ptrs; + size_t needed; + + /* fallback */ + dummy = 0; + argv = argv ? argv : &dummy; + envp = envp ? envp : &dummy; + + /* ptrs, needed */ + ptrs = 0; + needed = 0; + + if (image_name) { + ptrs++; + needed += sizeof(char *) + + __ntapi->tt_string_null_offset_multibyte(image_name) + + sizeof(char); + } + + for (parg=argv; *parg; parg++) + needed += sizeof(char *) + + __ntapi->tt_string_null_offset_multibyte(*parg) + + sizeof(char); + + ptrs += (parg - argv); + *argc = (int)ptrs; + + for (parg=envp; *parg; parg++) + needed += sizeof(char *) + + __ntapi->tt_string_null_offset_multibyte(*parg) + + sizeof(char); + + ptrs += (parg - envp); + + ptrs += 2; + needed += 2*sizeof(char *); + blklen = blklen ? blklen : &needed; + *blklen = needed; + + if (buflen < needed) + return NT_STATUS_BUFFER_TOO_SMALL; + + /* init */ + parg = (const char **)buffer; + ch = (char *)(parg+ptrs); + diff = (ptrdiff_t)base; + + /* image_name */ + if (image_name) { + *parg++ = ch-diff; + for (arg=image_name; *arg; arg++,ch++) + *ch = *arg; + *ch++ = '\0'; + } + + /* argv */ + for (; *argv; argv++) { + *parg++=ch-diff; + for (arg=*argv; *arg; arg++,ch++) + *ch = *arg; + *ch++ = '\0'; + } + + *parg++ = 0; + + /* envp */ + for (; *envp; envp++) { + *parg++=ch-diff; + for (arg=*envp; *arg; arg++,ch++) + *ch = *arg; + *ch++ = '\0'; + } + + *parg++ = 0; + + return NT_STATUS_SUCCESS; +} + +int32_t __stdcall __ntapi_tt_array_convert_utf8_to_utf16( + __in char ** arrv, + __in wchar16_t ** arra, + __in void * base, + __in wchar16_t * buffer, + __in size_t buffer_len, + __out size_t * bytes_written) +{ + return NT_STATUS_SUCCESS; +} diff --git a/src/argv/ntapi_tt_env_vars.c b/src/argv/ntapi_tt_env_vars.c new file mode 100644 index 0000000..1af9b77 --- /dev/null +++ b/src/argv/ntapi_tt_env_vars.c @@ -0,0 +1,112 @@ +/********************************************************/ +/* ntapi: Native API core library */ +/* Copyright (C) 2013,2014,2015 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */ +/********************************************************/ + +#include <psxtypes/psxtypes.h> +#include <ntapi/ntapi.h> +#include "ntapi_impl.h" + +int32_t __stdcall __ntapi_tt_get_env_var_meta_utf16( + __in const uint32_t * crc32_table, + __in wchar16_t * env_var_name, + __in uint32_t env_var_name_hash __optional, + __in wchar16_t ** envp, + __out nt_env_var_meta_utf16 * env_var_meta) +{ + int idx; + uint32_t crc32; + unsigned char * byte_buffer; + wchar16_t * wch; + + #define EQUAL_SIGN 0x3D + + /* step 1: crc32 of the target env_var_name */ + if (env_var_name_hash) + crc32 = env_var_name_hash; + else { + crc32 = 0 ^ 0xFFFFFFFF; + + /* initialize byte_buffer */ + byte_buffer = (unsigned char *)env_var_name; + + /* iterate */ + while (*byte_buffer) { + /* two bytes at a time */ + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + } + crc32 = (crc32 ^ 0xFFFFFFFF); + } + + /* initialize the env_var_meta structure */ + env_var_meta->name_hash = crc32; + env_var_meta->name = (wchar16_t *)0; + env_var_meta->value = (wchar16_t *)0; + env_var_meta->value_hash = 0; + env_var_meta->envp_index = 0; + env_var_meta->flags = 0; + + /* step 2: look for the environment variable in envp[] */ + idx = 0; + while (envp[idx] && (!env_var_meta->value)) { + wch = envp[idx]; + + /* find the equal sign */ + while ((*wch) && (*wch != EQUAL_SIGN)) + wch++; + + if (*wch != EQUAL_SIGN) + return NT_STATUS_ILLEGAL_CHARACTER; + + /* hash the current environment variable */ + crc32 = 0 ^ 0xFFFFFFFF; + + /* initialize byte_buffer */ + byte_buffer = (unsigned char *)envp[idx]; + + /* iterate */ + while ((uintptr_t)(byte_buffer) < (uintptr_t)wch) { + /* two bytes at a time */ + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + } + + if (env_var_meta->name_hash == (crc32 ^ 0xFFFFFFFF)) { + /* found it, get ready to hash the value */ + wch++; + env_var_meta->name = envp[idx]; + env_var_meta->value = wch; + env_var_meta->envp_index = idx; + } else { + idx++; + } + } + + if (env_var_meta->value) { + /* hash the value: utf-16, null-terminated */ + crc32 = 0 ^ 0xFFFFFFFF; + + /* initialize byte_buffer */ + byte_buffer = (unsigned char *)env_var_meta->value; + + /* iterate */ + while (*byte_buffer) { + /* two bytes at a time */ + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + } + + env_var_meta->value_hash = (crc32 ^ 0xFFFFFFFF); + } + + return NT_STATUS_SUCCESS; +} + diff --git a/src/argv/ntapi_tt_get_option.c b/src/argv/ntapi_tt_get_option.c new file mode 100644 index 0000000..e6f0748 --- /dev/null +++ b/src/argv/ntapi_tt_get_option.c @@ -0,0 +1,451 @@ +/********************************************************/ +/* ntapi: Native API core library */ +/* Copyright (C) 2013,2014,2015 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */ +/********************************************************/ + +#include <psxtypes/psxtypes.h> +#include <ntapi/ntapi.h> +#include "ntapi_impl.h" + + +/** + * a simple facility for minimal programs or system libraries + * with no libc available at the time of invocation, as well + * as applications using the midipix free-standing development + * environment. + * + * the approach taken by this module to the support of short + * and long options reflects the above constraint, namely + * the absence of a callable libc at the time of invocation; + * there is no intent for interfaces in this module to + * be POSIXLY correct or otherwise portable. the sole + * purpose of all functions in this module is to serve + * internal or otherwise free-standing midipix applications, + * and their relevance otherwise is accordingly non-existent. + * + * all options are encoded in utf-16; note, however, that + * short options may only use code points that are located + * in the basic multilingual plane. + * + * option values are either required or not allowed altogether, + * and the first character of an option value may not be a hyphen. + * if you need the first character of an option value to be a + * hyphen, then make sure you escape it somehow (for instance by + * enclosing it in quotation marks). + * + * a short option and its value must reside in two separate + * argv[] elements (in other words: -ooutput is illegal). + * + * a long option and its value must reside in the same argv[] + * element and be separated by a single equal sign. + * + * Examples of valid options and option values: + * -------------------------------------------- + * -o + * -o value + * --long-option-with-no-value + * --long-option=value +**/ + +#define HYPHEN 0x2D +#define EQUAL_SIGN 0x3D + + +static int __inline__ __fastcall __is_bmp_code_point(wchar16_t code_point) +{ + return (((code_point >= 0x0000) && (code_point < 0xD800)) \ + || ((code_point >= 0xE000) && (code_point < 0x10000))); +} + + +static int __inline__ __fastcall __is_last_program_option( + __in nt_program_option * option) +{ + return (!(option->short_name_code)) + && (!(option->long_name)) + && (!(option->long_name_hash)); +} + + +static int __fastcall __is_short_option(wchar16_t * wch) +{ + return ((wch) && (*wch == HYPHEN) + && __is_bmp_code_point(*++wch) + && (*++wch == 0)); +} + +static int __fastcall __is_long_option(wchar16_t * wch) +{ + return ((wch) && (*wch == HYPHEN) + && (++wch) && (*wch == HYPHEN) + && (*++wch)); +} + + +static int __fastcall __is_last_option_argument(wchar16_t * wch) +{ + return ((wch) && (*wch == HYPHEN) + && (*++wch == HYPHEN) + && (*++wch == 0)); +} + + +static uint32_t __fastcall __compute_crc32_utf16_str( + __in const uint32_t * crc32_table, + __in wchar16_t * wch) +{ + uint32_t crc32; + unsigned char * byte_buffer; + + /* crc32 hash... */ + crc32 = 0 ^ 0xFFFFFFFF; + + /* initialize byte_buffer */ + byte_buffer = (unsigned char *)wch; + + /* iterate */ + while (*byte_buffer) { + /* two bytes at a time */ + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + } + + return crc32; +} + + +static uint32_t __fastcall __compute_crc32_long_option_name( + __in const uint32_t * crc32_table, + __in wchar16_t * wch_arg, + __in wchar16_t * wch_termination) +{ + uint32_t crc32; + unsigned char * byte_buffer; + + /* crc32 hash... */ + crc32 = 0 ^ 0xFFFFFFFF; + + /* initialize byte_buffer */ + byte_buffer = (unsigned char *)wch_arg; + + /* iterate */ + while ((uintptr_t)byte_buffer < (uintptr_t)wch_termination) { + /* two bytes at a time */ + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + crc32 = (crc32 >> 8) ^ crc32_table[(crc32 ^ *byte_buffer) & 0xFF]; + byte_buffer++; + } + + return crc32; +} + + +static void __fastcall __init_cmd_option_meta_utf16( + __in nt_cmd_option_meta_utf16 * cmd_opt_meta) +{ + cmd_opt_meta->short_name = (wchar16_t *)0; + cmd_opt_meta->short_name_code = 0; + cmd_opt_meta->long_name = (wchar16_t *)0; + cmd_opt_meta->long_name_hash = 0; + cmd_opt_meta->value = (wchar16_t *)0; + cmd_opt_meta->value_hash = 0; + cmd_opt_meta->argv_index = 0; + cmd_opt_meta->flags = 0; + + return; +} + + +int32_t __stdcall __ntapi_tt_get_short_option_meta_utf16( + __in const uint32_t * crc32_table, + __in wchar16_t option_name, + __in wchar16_t * argv[], + __out nt_cmd_option_meta_utf16 * cmd_opt_meta) +{ + int idx; + wchar16_t * wch; + + if (!crc32_table) + return NT_STATUS_INVALID_PARAMETER_1; + else if (!option_name) + return NT_STATUS_INVALID_PARAMETER_2; + else if (!argv) + return NT_STATUS_INVALID_PARAMETER_3; + + /* initialize cmd_opt_meta */ + __init_cmd_option_meta_utf16(cmd_opt_meta); + + /* step 1: attempt to find the short option in argv[] */ + idx = 0; + while (argv[idx] && (!cmd_opt_meta->short_name_code)) { + wch = argv[idx]; + + /* is this our option? */ + if ((*wch == HYPHEN) + && (*++wch == option_name) + && (*++wch == 0)) { + + /* found it, get ready to hash the value */ + cmd_opt_meta->short_name_code = option_name; + cmd_opt_meta->short_name = argv[idx]; + cmd_opt_meta->argv_index = idx; + } else { + idx++; + } + } + + /* if the next argument is also an option (or is null), just exit */ + idx++; + if ((!argv[idx]) || (*argv[idx] == HYPHEN)) + return NT_STATUS_SUCCESS; + + /* step 2: hash the value */ + cmd_opt_meta->value = argv[idx]; + cmd_opt_meta->value_hash = + __compute_crc32_utf16_str( + crc32_table, + argv[idx]); + + return NT_STATUS_SUCCESS; +} + + +int32_t __stdcall __ntapi_tt_get_long_option_meta_utf16( + __in const uint32_t * crc32_table, + __in wchar16_t * option_name, + __in uint32_t option_name_hash __optional, + __in wchar16_t * argv[], + __out nt_cmd_option_meta_utf16 * cmd_opt_meta) +{ + /** + * option_name must always include the two-hyphen prefix; + * and the option value must be preceded by an equal sign. + * + * the only valid long option forms in argv[] are therefore: + * --long-option + * --long-option=value + **/ + + int idx; + uint32_t crc32; + wchar16_t * wch; + + /* validation */ + if (!crc32_table) + return NT_STATUS_INVALID_PARAMETER_1; + else if ((!option_name) && (!option_name_hash)) + return NT_STATUS_INVALID_PARAMETER; + else if ((option_name) && (option_name_hash)) + return NT_STATUS_INVALID_PARAMETER_MIX; + else if (!argv) + return NT_STATUS_INVALID_PARAMETER_4; + + /* initialize cmd_opt_meta */ + __init_cmd_option_meta_utf16(cmd_opt_meta); + + /* step 1: crc32 of the target option_name */ + if (option_name_hash) + crc32 = option_name_hash; + else + option_name_hash = + __compute_crc32_utf16_str( + crc32_table, + option_name); + + /* step 2: attempt to find the long option in argv[] */ + idx = 0; + while (argv[idx] && (!cmd_opt_meta->value)) { + wch = argv[idx]; + + if (__is_long_option(wch)) { + /* find the equal sign or null termination */ + while ((*wch) && (*wch != EQUAL_SIGN)) + wch++; + + crc32 = __compute_crc32_long_option_name( + crc32_table, + argv[idx], + wch); + + if (crc32 == option_name_hash) { + /* found it, get ready to hash the value */ + cmd_opt_meta->long_name_hash = option_name_hash; + cmd_opt_meta->long_name = argv[idx]; + cmd_opt_meta->argv_index = idx; + + if (*wch) + /* skip the equal sign */ + wch++; + + cmd_opt_meta->value = wch; + } else + idx++; + } + } + + if (cmd_opt_meta->value) + cmd_opt_meta->value_hash = + __compute_crc32_utf16_str( + crc32_table, + cmd_opt_meta->value); + + return NT_STATUS_SUCCESS; +} + + +int32_t __stdcall __ntapi_tt_validate_program_options( + __in const uint32_t * crc32_table, + __in wchar16_t * argv[], + __in nt_program_option * options[], + __in nt_program_options_meta * options_meta) +{ + int idx; + int idx_arg; + int idx_option; + int idx_max; + uint32_t crc32; + nt_program_option * option; + wchar16_t * parg; + wchar16_t * pvalue; + + /* validation */ + if (!crc32_table) + return NT_STATUS_INVALID_PARAMETER_1; + else if (!argv) + return NT_STATUS_INVALID_PARAMETER_2; + else if (!options) + return NT_STATUS_INVALID_PARAMETER_3; + else if (!options_meta) + return NT_STATUS_INVALID_PARAMETER_4; + + + /* step 1: validate options[] hash the long option names */ + idx = 0; + idx_option = 0; + option = options[0]; + pvalue = (wchar16_t *)0; + + while (!__is_last_program_option(option)) { + if (option->short_name_code) { + if (!(__is_bmp_code_point(option->short_name_code))) { + options_meta->idx_invalid_short_name = idx; + return NT_STATUS_INVALID_PARAMETER; + } + } + + if (option->long_name) { + if (!(__is_long_option(option->long_name))) { + options_meta->idx_invalid_long_name = idx; + return NT_STATUS_INVALID_PARAMETER; + } + + /* update the long name hash (unconditionally) */ + option->long_name_hash = + __compute_crc32_utf16_str( + crc32_table, + option->long_name); + } + + idx++; + option++; + } + + /* book keeping */ + idx_max = idx; + + /* step 2: validate argv[] */ + parg = argv[0]; + idx_arg = 0; + + while ((parg) && (!(__is_last_option_argument(parg)))) { + if (__is_short_option(parg)) { + idx = 0; + idx_option = 0; + + while ((idx < idx_max) && (!idx_option)) { + option = options[idx]; + + if (*(parg+1) == option->short_name_code) + idx_option = idx; + else + idx++; + } + + if (idx == idx_max) { + options_meta->idx_invalid_argument = idx_arg; + return NT_STATUS_INVALID_PARAMETER; + } else { + /* get ready for the next element (or value) */ + parg++; + idx_arg++; + pvalue = parg; + } + } else if (__is_long_option(parg)) { + idx = 0; + idx_option = 0; + /* find the equal sign or null termination */ + pvalue = parg; + while ((*pvalue) && (*pvalue != EQUAL_SIGN)) + pvalue++; + + while ((idx < idx_max) && (!idx_option)) { + option = options[idx]; + crc32 = __compute_crc32_long_option_name( + crc32_table, + parg, + pvalue); + + if (crc32 == option->long_name_hash) + idx_option = idx; + else + idx++; + } + + if (idx == idx_max) { + options_meta->idx_invalid_argument = idx_arg; + return NT_STATUS_INVALID_PARAMETER; + } else { + if (*pvalue != EQUAL_SIGN) + /* skip the equal sign */ + pvalue++; + pvalue = (wchar16_t *)0; + } + } + + /* validate the occurrence */ + if (idx_option) { + if (option->flags && NT_OPTION_ALLOWED_ONCE) { + if (option->option_count) { + options_meta->idx_invalid_argument + = idx_arg; + return NT_STATUS_INVALID_PARAMETER; + } else { + option->option_count++; + } + } + + if (option->flags && NT_OPTION_VALUE_REQUIRED) { + if ((!(*pvalue)) || (*pvalue == HYPHEN)) { + options_meta->idx_missing_option_value + = idx_arg; + return NT_STATUS_INVALID_PARAMETER; + } else { + option->value = pvalue; + option->value_hash = + __compute_crc32_utf16_str( + crc32_table, + option->value); + } + } + } + + parg++; + idx_arg++; + } + + return NT_STATUS_SUCCESS; +} |