summaryrefslogtreecommitdiffhomepage
path: root/src/argv
diff options
context:
space:
mode:
authormidipix <writeonce@midipix.org>2015-07-27 04:01:18 -0400
committermidipix <writeonce@midipix.org>2015-07-27 04:01:18 -0400
commitdd89bb8ad4fe184a34b5dbdda237e640fc82121b (patch)
tree5e80d2da35f5892f92be29f57982b2708e6bd99b /src/argv
parentdcdadc2702712fa750ed255ed1dfa354522797a0 (diff)
downloadntapi-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.c717
-rw-r--r--src/argv/ntapi_tt_array_utf16.c258
-rw-r--r--src/argv/ntapi_tt_array_utf8.c117
-rw-r--r--src/argv/ntapi_tt_env_vars.c112
-rw-r--r--src/argv/ntapi_tt_get_option.c451
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;
+}