Implementation of CommandLineToArgv for Win32 (ANSI and alternative Unicode versions)
Меня давно удивляло, что супер-полезная функция для разбора командной строки и
приведения ее к C-образному виду (argc/argv) присутствует в Win32 API только в Unicode
варианте. Называется она CommandLineToArgvW(). Получается, что для создания
компактной программы, не использующей C-RTL, есть два пути:
- необходимо делать ее полностью Unicode, и как следствие - работоспособной только под NT
- писать свой разборщик командной строки.
Это было побуждение номер раз к написанию такого разборщика. Побуждением номер
два оказалось очень сложное внутреннее устройство самого
CommandLineToArgvW(). Это сказалось, когда мне довелось писать программу,
принимающую параметры из коммандной строки для Windows NT Native subsystem.
Это как раз тот режим, в котором запускаются chkdsk, Partition Magic,
page defrag и т.п. Win32 API в этом режиме практически недоступно.
И хоть было очень облом, собственная реализация
CommandLineToArgvW() была таки написана.
Теперь написание ANSI версии - CommandLineToArgvA() - было делом нескольких минут.
Ниже приведен код CommandLineToArgvW() и CommandLineToArgvA(). Для получения CommandLineToArgvA()
достаточно заменить в CommandLineToArgvW():
- WCHAR -> CHAR
- wcslen -> strlen
- CommandLineToArgvW -> CommandLineToArgvA
И вот недавно нашлось еще одно применение для CommandLineToArgvA() - разбор строки параметров
PCHAR args в Kernel Debug Extension'ах.
CommandLineToArgvW
PWCHAR*
CommandLineToArgvW(
PWCHAR CmdLine,
int* _argc
)
{
PWCHAR* argv;
PWCHAR _argv;
ULONG len;
ULONG argc;
WCHAR a;
ULONG i, j;
BOOLEAN in_QM;
BOOLEAN in_TEXT;
BOOLEAN in_SPACE;
len = wcslen(CmdLine);
i = ((len+2)/2)*sizeof(PVOID) + sizeof(PVOID);
argv = (PWCHAR*)GlobalAlloc(GMEM_FIXED,
i + (len+2)*sizeof(WCHAR));
_argv = (PWCHAR)(((PUCHAR)argv)+i);
argc = 0;
argv[argc] = _argv;
in_QM = FALSE;
in_TEXT = FALSE;
in_SPACE = TRUE;
i = 0;
j = 0;
while( a = CmdLine[i] ) {
if(in_QM) {
if(a == '\"') {
in_QM = FALSE;
} else {
_argv[j] = a;
j++;
}
} else {
switch(a) {
case '\"':
in_QM = TRUE;
in_TEXT = TRUE;
if(in_SPACE) {
argv[argc] = _argv+j;
argc++;
}
in_SPACE = FALSE;
break;
case ' ':
case '\t':
case '\n':
case '\r':
if(in_TEXT) {
_argv[j] = '\0';
j++;
}
in_TEXT = FALSE;
in_SPACE = TRUE;
break;
default:
in_TEXT = TRUE;
if(in_SPACE) {
argv[argc] = _argv+j;
argc++;
}
_argv[j] = a;
j++;
in_SPACE = FALSE;
break;
}
}
i++;
}
_argv[j] = '\0';
argv[argc] = NULL;
(*_argc) = argc;
return argv;
}
CommandLineToArgvA
PCHAR*
CommandLineToArgvA(
PCHAR CmdLine,
int* _argc
)
{
PCHAR* argv;
PCHAR _argv;
ULONG len;
ULONG argc;
CHAR a;
ULONG i, j;
BOOLEAN in_QM;
BOOLEAN in_TEXT;
BOOLEAN in_SPACE;
len = strlen(CmdLine);
i = ((len+2)/2)*sizeof(PVOID) + sizeof(PVOID);
argv = (PCHAR*)GlobalAlloc(GMEM_FIXED,
i + (len+2)*sizeof(CHAR));
_argv = (PCHAR)(((PUCHAR)argv)+i);
argc = 0;
argv[argc] = _argv;
in_QM = FALSE;
in_TEXT = FALSE;
in_SPACE = TRUE;
i = 0;
j = 0;
while( a = CmdLine[i] ) {
if(in_QM) {
if(a == '\"') {
in_QM = FALSE;
} else {
_argv[j] = a;
j++;
}
} else {
switch(a) {
case '\"':
in_QM = TRUE;
in_TEXT = TRUE;
if(in_SPACE) {
argv[argc] = _argv+j;
argc++;
}
in_SPACE = FALSE;
break;
case ' ':
case '\t':
case '\n':
case '\r':
if(in_TEXT) {
_argv[j] = '\0';
j++;
}
in_TEXT = FALSE;
in_SPACE = TRUE;
break;
default:
in_TEXT = TRUE;
if(in_SPACE) {
argv[argc] = _argv+j;
argc++;
}
_argv[j] = a;
j++;
in_SPACE = FALSE;
break;
}
}
i++;
}
_argv[j] = '\0';
argv[argc] = NULL;
(*_argc) = argc;
return argv;
}
|