diff options
Diffstat (limited to 'src/argv/ntapi_tt_get_option.c')
-rw-r--r-- | src/argv/ntapi_tt_get_option.c | 451 |
1 files changed, 451 insertions, 0 deletions
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; +} |