/********************************************************/ /* ntapi: Native API core library */ /* Copyright (C) 2013--2017 Z. Gilboa */ /* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */ /********************************************************/ #include #include #include #include #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 * interp, __in const wchar16_t * optarg, __in const wchar16_t * script, __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 * mark; wchar16_t * wch; ptrdiff_t diff; ptrdiff_t ptrs; size_t needed; const wchar16_t * dummy[2] = {0,0}; /* fallback */ wargv = wargv ? wargv : dummy; wenvp = wenvp ? wenvp : dummy; /* ptrs, needed */ ptrs = 0; needed = 0; /* interp */ if (interp) { ptrs++; needed += sizeof(wchar16_t *) + __ntapi->tt_string_null_offset_short((const int16_t *)interp) + sizeof(wchar16_t); } /* optarg */ if (interp) { ptrs++; needed += sizeof(wchar16_t *) + __ntapi->tt_string_null_offset_short((const int16_t *)optarg) + sizeof(wchar16_t); } /* script / wargv[0] */ if ((mark = script ? script : wargv[0])) { ptrs++; needed += sizeof(wchar16_t *) + __ntapi->tt_string_null_offset_short((const int16_t *)mark) + sizeof(wchar16_t); } /* wargv */ for (parg=&wargv[1]; *parg; parg++) needed += sizeof(wchar16_t *) + __ntapi->tt_string_null_offset_short((const int16_t *)*parg) + sizeof(wchar16_t); ptrs += (parg - &wargv[1]); *argc = (int)ptrs; /* wenvp */ 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); /* interp */ if (interp) { *parg++ = wch-diff; for (warg=interp; *warg; warg++,wch++) *wch = *warg; *wch++ = '\0'; } /* optarg */ if (optarg) { *parg++ = wch-diff; for (warg=optarg; *warg; warg++,wch++) *wch = *warg; *wch++ = '\0'; } /* script / wargv[0] */ if ((mark = script ? script : wargv[0])) { *parg++ = wch-diff; for (warg=mark; *warg; warg++,wch++) *wch = *warg; *wch++ = '\0'; } /* wargv */ for (++wargv; *wargv; wargv++) { *parg++=wch-diff; for (warg=*wargv; *warg; warg++,wch++) *wch = *warg; *wch++ = '\0'; } *parg++ = 0; /* wenvp */ 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; ch = (uint8_t *)buffer; ubound = (uint8_t *)buffer + buffer_len - 5; diff = (uintptr_t)base / sizeof(wchar16_t); for (; warrv && *warrv; arrv++,warrv++) { *arrv = (char *)(ch-(uintptr_t)base); wch = *warrv + diff; /* ubound already accounts for null termination, see above */ for (; *wch && (ch>= 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 if (wch[0] >= 0xDC00) { return NT_STATUS_ILLEGAL_CHARACTER; } else if (wch[1] < 0xDC00) { return NT_STATUS_ILLEGAL_CHARACTER; } else if (wch[1] >= 0xE000) { return NT_STATUS_ILLEGAL_CHARACTER; } else { /* from: 110110ww wwzzzzyy 110111yy yyxxxxxx (little endian) */ /* to: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx (utf-8) */ /* low two bytes */ wy_high = *wch; wy_high <<= 14; wy_high >>= 10; wz = *wch; wz <<= 10; wz >>= 12; ww = *wch; ww <<= 6; ww >>= 12; /* (surrogate pair) */ wch++; /* high two bytes */ wx = *wch; wx <<= 10; wx >>= 10; wy_low = *wch; wy_low <<= 6; wy_low >>= 12; /* uuuuu */ uuuuu = ww + 1; u_low = uuuuu; u_low >>= 2; u_high = uuuuu; u_high <<= 14; u_high >>= 10; /* 1st byte: 11110uuu */ *ch++ = (char)(0xF0 | u_low); /* 2nd byte: 10uuzzzz */ *ch++ = (char)(0x80 | u_high | wz); /* 3rd byte: 10yyyyyy */ *ch++ = (char)(0x80 | wy_low | wy_high); /* 4th byte: 10xxxxxx */ *ch = (char)(0x80 | wx); } ch++; wch++; } if (*wch) return NT_STATUS_BUFFER_TOO_SMALL; *ch++ = 0; } *arrv = 0; *bytes_written = (size_t)(ch - (uint8_t *)buffer); return NT_STATUS_SUCCESS; }