/********************************************************/
/* ntapi: Native API core library */
/* Copyright (C) 2013--2016 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));
}
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;
}