Alter.Org.UA
 << Back Home EN en   Donate Donate

Implementation of CommandLineToArgv for Win32
(ANSI and alternative Unicode versions)

Меня давно удивляло, что супер-полезная функция для разбора командной строки и приведения ее к C-образному виду (argc/argv) присутствует в Win32 API только в Unicode варианте. Называется она CommandLineToArgvW(). Получается, что для создания компактной программы, не использующей C-RTL, есть два пути:

  1. необходимо делать ее полностью Unicode, и как следствие - работоспособной только под NT
  2. писать свой разборщик командной строки.

Это было побуждение номер раз к написанию такого разборщика. Побуждением номер два оказалось очень сложное внутреннее устройство самого CommandLineToArgvW(). Это сказалось, когда мне довелось писать программу, принимающую параметры из коммандной строки для Windows NT Native subsystem. Это как раз тот режим, в котором запускаются chkdsk, Partition Magic, page defrag и т.п. Win32 API в этом режиме практически недоступно. И хоть было очень облом, собственная реализация CommandLineToArgvW() была таки написана.

Теперь написание ANSI версии - CommandLineToArgvA() - было делом нескольких минут. Ниже приведен код CommandLineToArgvW() и CommandLineToArgvA(). Для получения CommandLineToArgvA() достаточно заменить в CommandLineToArgvW():

  1. WCHAR -> CHAR
  2. wcslen -> strlen
  3. CommandLineToArgvW -> CommandLineToArgvA
Note: вложенные кавычки не обрабатываются , thanks to Igor Levicki for report.

И вот недавно нашлось еще одно применение для 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;
    }

См. также

<< Back Автор: Alter (Александр А. Телятников) Сервер: Apache+PHP под FBSD © 2002-2024