Разбор параметров командной строки

Наконец-то добрался до полезной практической задачи по корректному разбору параметров командной строки. На языках высокого уровня это делается чуть ли не одной командой, а на Ассемблере как обычно приходится все делать самостоятельно. Решение получилось универсальным, подходит как для консольных, так и для GUI-приложений. Для использования функции ParseCmdLine в сегменте данных надо предварительно определить следующую структуру:
; Структура для командной строки
struct  CMDLINE
        nCount   dd ?   ; Количество аргументов
        lpArgs   dd ?   ; Указатель на массив адресов строк
        lpArgStr dd ?   ; Указатель на массив строк
ends

Формат структуры: nCount - количество параметров командной строки, в случае успешного вызова функции это значение обязательно будет ненулевым, так как самый первый параметр - полный путь запуска программы, а остальные аргументы из хвоста командной строки будут расположены, начиная со второго элемента массива. lpArgStr - указатель на массив параметров командной строки. Все строки в этот массив записываются последовательно одна за другой в формате ASCIIZ, если строки были не в кавычках, то с начала и конца строки удаляются избыточные пробелы и символы табуляции. lpArgs - указатель на массив адресов разобранных параметров командной строки. Какого-то отдельного признака окончания массива не предусмотрено, количество элементов берется из значения nCount.

Сама функция разбора командной строки у меня получилась такая. Для работы используется память из кучи (Heap), оригинальная командная строка в памяти не модифицируется.
;------------------------------------------------------------------
; Функция разбора параметров командной строки
; Copyright (C) ManHunter / PCL
; http://www.manhunter.ru
;
; Параметры:
; CmdStr    - указатель на структуру CMDLINE
; dQuotFlag - сохранять кавычки: TRUE - сохранять, FALSE - убирать
; На выходе:
; заполненная структура CMDLINE с параметрами командной строки
;------------------------------------------------------------------ 
proc ParseCmdLine CmdStr:dword, dQuotFlag:dword
        local _hheap:DWORD         ; Локальные переменные
        local _tmp:DWORD
        local _nCount:DWORD
        local _dAct:DWORD
 
        virtual at 0
        localcmd CMDLINE           ; Локальная проекция структуры
        end virtual
 
        pusha
 
        ; Получить адрес кучи
        invoke  GetProcessHeap
        mov     [_hheap],eax
        mov     [_nCount],0        ; Обнулить счетчик аргументов
        mov     [_dAct],0          ; Обнулить флаг "работаю"
 
        ; Выделить память для сохранения обработанных аргументов
        invoke  HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,1000h
        mov     edi,eax
        mov     [_tmp],eax
 
        ; Получить командную строку
        invoke  GetCommandLine
        mov     esi,eax
 
        xor     ebx,ebx            ; Флажок-признак, что аргумент в кавычках
 
.parse_scan:
        lodsb
        or      al,al              ; Строка закончилась?
        jz      .parse_end
        cmp     al,'"'             ; Найдена кавычка в параметрах?
        jz      .parse_quote
        cmp     ebx,1              ; Уже обрабатывается строка в кавычках
        je      .parse_arg_in_quote
        cmp     al,' '             ; Встретили пробел, записать параметр
        je      .parse_push_arg
        cmp     al,09h             ; Встретили табулятор, записать параметр
        je      .parse_push_arg
 
.parse_arg_in_quote:
        stosb                      ; Записать текущий символ
        mov     [_dAct],1          ; Установить флаг "работаю"
        jmp     .parse_scan
 
.parse_push_arg:
        mov     al,0               ; Окончание строки
        stosb
        inc     [_nCount]
        mov     [_dAct],0          ; Обнулить флаг "работаю"
 
.parse_remove_space:
        lodsb                      ; Пропустить пробелы и табуляторы
        or      al,al
        jz      .parse_end
        cmp     al,'"'
        je      .parse_start_quote
        cmp     al,' '
        je      .parse_remove_space
        cmp     al,09h
        je      .parse_remove_space
 
        dec     esi
        jmp     .parse_scan
 
        ; Обработка кавычек в командной строке
.parse_quote:
        or      ebx,ebx
        jnz     .parse_end_quote
.parse_start_quote:
        ; Если флаг установлен, то записать и кавычки
        cmp     [dQuotFlag],TRUE
        jne     @f
        stosb
@@:
        ; Установить флаг что обрабатывается параметр в кавычках
        mov     ebx,1
        jmp     .parse_scan
.parse_end_quote:
        ; Если флаг установлен, то записать и кавычки
        cmp     [dQuotFlag],TRUE
        jne     @f
        stosb
@@:
        xor     ebx,ebx
        jmp     .parse_push_arg
 
.parse_end:
        ; Окончание параметров
        xor     eax,eax
        stosw
        mov     eax,[_dAct]
        add     [_nCount],eax
 
        mov     ecx,edi
        sub     ecx,[_tmp]
        push    ecx
 
        ; Выделить память
        invoke  HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,ecx
 
        pop     ecx
 
        mov     edi,eax
        mov     esi,[_tmp]
 
        mov     eax,[CmdStr]
        mov     [eax+localcmd.lpArgStr],edi  ; lpArgStr
 
        ; Перенести строку параметров
        rep     movsb
 
        ; Прибраться за собой
        invoke  HeapFree,[_hheap],0,[_tmp]
 
        mov     eax,[CmdStr]
        mov     ebx,[_nCount]
        mov     [eax+localcmd.nCount],ebx    ; nCount
 
        ; Размер = _nCount * 4
        mov     eax,[_nCount]
        shl     eax,2
 
        ; Выделить память
        invoke  HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,eax
        mov     edi,eax
 
        mov     eax,[CmdStr]
        mov     [eax+localcmd.lpArgs],edi    ; lpArgs
 
        ; Заполнить таблицу индексов
        mov     esi,[eax+localcmd.lpArgStr]
.parse_fill_addr:
        invoke  lstrlen,esi
        or      eax,eax
        jz      .parse_exit
        mov     [edi],esi
        add     edi,4
        add     esi,eax
        inc     esi
        jmp     .parse_fill_addr
 
.parse_exit:
        popa
        ret
endp

Параметры вызова функции: CmdStr - указатель на структуру CMDLINE, dQuotFlag - флаг, определяющий будут ли сохранены обрамляющие кавычки в массиве аргументов командной строки, значение флага: TRUE - сохранять кавычки, FALSE - убирать. После вызова функции получаем заполненную структуру CMDLINE. Пример использования:
section '.data' data readable writeable
        ...
struct  CMDLINE
        nCount   dd ?   ; Количество аргументов
        lpArgs   dd ?   ; Указатель на массив адресов строк
        lpArgStr dd ?   ; Указатель на массив строк
ends
myarg   CMDLINE         ; Это наша командная строка
        ...
 
section '.code' code readable executable
        ...
        stdcall ParseCmdLine,myarg,FALSE
        ; Если не заданы параметры в командной строке, то вывести
        ; информацию о ключах программы
        cmp     [myarg.nCount],2
        jb      show_usage
        ...

В прилагаемом исходнике функция ParseCmdLine вызывается дважды с разными флагами обработки кавычек, чтобы вы могли увидеть разницу. Для удобства в архиве есть файл demo.bat, запускающий пример сразу с несколькими параметрами командной строки.
Теги:
командная строка
Добавлено: 10 Апреля 2018 19:53:48 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...