Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Наконец-то добрался до полезной практической задачи по корректному разбору параметров командной строки. На языках высокого уровня это делается чуть ли не одной командой, а на Ассемблере как обычно приходится все делать самостоятельно. Решение получилось универсальным, подходит как для консольных, так и для GUI-приложений. Для использования функции ParseCmdLine в сегменте данных надо предварительно определить следующую структуру:
  1. ; Структура для командной строки
  2. struct  CMDLINE
  3.         nCount   dd ?   ; Количество аргументов
  4.         lpArgs   dd ?   ; Указатель на массив адресов строк
  5.         lpArgStr dd ?   ; Указатель на массив строк
  6. ends
Формат структуры: nCount - количество параметров командной строки, в случае успешного вызова функции это значение обязательно будет ненулевым, так как самый первый параметр - полный путь запуска программы, а остальные аргументы из хвоста командной строки будут расположены, начиная со второго элемента массива. lpArgStr - указатель на массив параметров командной строки. Все строки в этот массив записываются последовательно одна за другой в формате ASCIIZ, если строки были не в кавычках, то с начала и конца строки удаляются избыточные пробелы и символы табуляции. lpArgs - указатель на массив адресов разобранных параметров командной строки. Какого-то отдельного признака окончания массива не предусмотрено, количество элементов берется из значения nCount.

Сама функция разбора командной строки у меня получилась такая. Для работы используется память из кучи (Heap), оригинальная командная строка в памяти не модифицируется.
  1. ;------------------------------------------------------------------
  2. ; Функция разбора параметров командной строки
  3. ; Copyright (C) ManHunter / PCL
  4. ; http://www.manhunter.ru
  5. ;
  6. ; Параметры:
  7. ; CmdStr    - указатель на структуру CMDLINE
  8. ; dQuotFlag - сохранять кавычки: TRUE - сохранять, FALSE - убирать
  9. ; На выходе:
  10. ; заполненная структура CMDLINE с параметрами командной строки
  11. ;------------------------------------------------------------------ 
  12. proc ParseCmdLine CmdStr:dword, dQuotFlag:dword
  13.         local _hheap:DWORD         ; Локальные переменные
  14.         local _tmp:DWORD
  15.         local _nCount:DWORD
  16.         local _dAct:DWORD
  17.  
  18.         virtual at 0
  19.         localcmd CMDLINE           ; Локальная проекция структуры
  20.         end virtual
  21.  
  22.         pusha
  23.  
  24.         ; Получить адрес кучи
  25.         invoke  GetProcessHeap
  26.         mov     [_hheap],eax
  27.         mov     [_nCount],0        ; Обнулить счетчик аргументов
  28.         mov     [_dAct],0          ; Обнулить флаг "работаю"
  29.  
  30.         ; Выделить память для сохранения обработанных аргументов
  31.         invoke  HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,1000h
  32.         mov     edi,eax
  33.         mov     [_tmp],eax
  34.  
  35.         ; Получить командную строку
  36.         invoke  GetCommandLine
  37.         mov     esi,eax
  38.  
  39.         xor     ebx,ebx            ; Флажок-признак, что аргумент в кавычках
  40.  
  41. .parse_scan:
  42.         lodsb
  43.         or      al,al              ; Строка закончилась?
  44.         jz      .parse_end
  45.         cmp     al,'"'             ; Найдена кавычка в параметрах?
  46.         jz      .parse_quote
  47.         cmp     ebx,1              ; Уже обрабатывается строка в кавычках
  48.         je      .parse_arg_in_quote
  49.         cmp     al,' '             ; Встретили пробел, записать параметр
  50.         je      .parse_push_arg
  51.         cmp     al,09h             ; Встретили табулятор, записать параметр
  52.         je      .parse_push_arg
  53.  
  54. .parse_arg_in_quote:
  55.         stosb                      ; Записать текущий символ
  56.         mov     [_dAct],1          ; Установить флаг "работаю"
  57.         jmp     .parse_scan
  58.  
  59. .parse_push_arg:
  60.         mov     al,0               ; Окончание строки
  61.         stosb
  62.         inc     [_nCount]
  63.         mov     [_dAct],0          ; Обнулить флаг "работаю"
  64.  
  65. .parse_remove_space:
  66.         lodsb                      ; Пропустить пробелы и табуляторы
  67.         or      al,al
  68.         jz      .parse_end
  69.         cmp     al,'"'
  70.         je      .parse_start_quote
  71.         cmp     al,' '
  72.         je      .parse_remove_space
  73.         cmp     al,09h
  74.         je      .parse_remove_space
  75.  
  76.         dec     esi
  77.         jmp     .parse_scan
  78.  
  79.         ; Обработка кавычек в командной строке
  80. .parse_quote:
  81.         or      ebx,ebx
  82.         jnz     .parse_end_quote
  83. .parse_start_quote:
  84.         ; Если флаг установлен, то записать и кавычки
  85.         cmp     [dQuotFlag],TRUE
  86.         jne     @f
  87.         stosb
  88. @@:
  89.         ; Установить флаг что обрабатывается параметр в кавычках
  90.         mov     ebx,1
  91.         jmp     .parse_scan
  92. .parse_end_quote:
  93.         ; Если флаг установлен, то записать и кавычки
  94.         cmp     [dQuotFlag],TRUE
  95.         jne     @f
  96.         stosb
  97. @@:
  98.         xor     ebx,ebx
  99.         jmp     .parse_push_arg
  100.  
  101. .parse_end:
  102.         ; Окончание параметров
  103.         xor     eax,eax
  104.         stosw
  105.         mov     eax,[_dAct]
  106.         add     [_nCount],eax
  107.  
  108.         mov     ecx,edi
  109.         sub     ecx,[_tmp]
  110.         push    ecx
  111.  
  112.         ; Выделить память
  113.         invoke  HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,ecx
  114.  
  115.         pop     ecx
  116.  
  117.         mov     edi,eax
  118.         mov     esi,[_tmp]
  119.  
  120.         mov     eax,[CmdStr]
  121.         mov     [eax+localcmd.lpArgStr],edi  ; lpArgStr
  122.  
  123.         ; Перенести строку параметров
  124.         rep     movsb
  125.  
  126.         ; Прибраться за собой
  127.         invoke  HeapFree,[_hheap],0,[_tmp]
  128.  
  129.         mov     eax,[CmdStr]
  130.         mov     ebx,[_nCount]
  131.         mov     [eax+localcmd.nCount],ebx    ; nCount
  132.  
  133.         ; Размер = _nCount * 4
  134.         mov     eax,[_nCount]
  135.         shl     eax,2
  136.  
  137.         ; Выделить память
  138.         invoke  HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,eax
  139.         mov     edi,eax
  140.  
  141.         mov     eax,[CmdStr]
  142.         mov     [eax+localcmd.lpArgs],edi    ; lpArgs
  143.  
  144.         ; Заполнить таблицу индексов
  145.         mov     esi,[eax+localcmd.lpArgStr]
  146. .parse_fill_addr:
  147.         invoke  lstrlen,esi
  148.         or      eax,eax
  149.         jz      .parse_exit
  150.         mov     [edi],esi
  151.         add     edi,4
  152.         add     esi,eax
  153.         inc     esi
  154.         jmp     .parse_fill_addr
  155.  
  156. .parse_exit:
  157.         popa
  158.         ret
  159. endp
Параметры вызова функции: CmdStr - указатель на структуру CMDLINE, dQuotFlag - флаг, определяющий будут ли сохранены обрамляющие кавычки в массиве аргументов командной строки, значение флага: TRUE - сохранять кавычки, FALSE - убирать. После вызова функции получаем заполненную структуру CMDLINE. Пример использования:
  1. section '.data' data readable writeable
  2.         ...
  3. struct  CMDLINE
  4.         nCount   dd ?   ; Количество аргументов
  5.         lpArgs   dd ?   ; Указатель на массив адресов строк
  6.         lpArgStr dd ?   ; Указатель на массив строк
  7. ends
  8. myarg   CMDLINE         ; Это наша командная строка
  9.         ...
  10.  
  11. section '.code' code readable executable
  12.         ...
  13.         stdcall ParseCmdLine,myarg,FALSE
  14.         ; Если не заданы параметры в командной строке, то вывести
  15.         ; информацию о ключах программы
  16.         cmp     [myarg.nCount],2
  17.         jb      show_usage
  18.         ...
В прилагаемом исходнике функция ParseCmdLine вызывается дважды с разными флагами обработки кавычек, чтобы вы могли увидеть разницу. Для удобства в архиве есть файл demo.bat, запускающий пример сразу с несколькими параметрами командной строки.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Command.Line.Demo.zip (4,237 bytes)


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 9110 | Комментариев: 10

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (25.10.2016 в 08:44):
*facepalm*
Завязывай лакать стекломой по утрам, от него, похоже, мозги протухают и глаза слепнут.
Дмитрий (25.10.2016 в 08:31):
Привет!
Ф можно в Демо примере убрать антисирийский лозунг:
Kill Fuck Asad!
SMaSm-94 (17.11.2014 в 10:37):
ManHunter, да уж, не думал, что моя процедура настолько неоптимизированная ... Но спасибо, в будущем постораюсь быть повнимательнее :)
ManHunter (16.11.2014 в 02:11):
Эх, молодежь....

proc get_command_line_argument nArg:DWORD
        mov     eax,[nArg]
        cmp     eax,[myarg.nCount]
        jb      @f
        xor     eax,eax
        ret
@@:
        shl     eax,2
        add     eax,[myarg.lpArgs]
        mov     eax,[eax]
        ret
endp
SMaSm-94 (12.11.2014 в 11:57):
Вот, на досуге написал процедурку для получения параметра командной строки по порядковому номеру.

; Как же без структуры))
cmdln CMDLINE

; Небольшой пример использования:
        ...
        stdcall ParseCmdLine, cmdln, FALSE
        stdcall get_command_line_argument, 1
        or      eax, eax
        jz      .null_argument
        invoke  MessageBox, HWND_DESKTOP, eax, NULL, MB_OK
        ...

; Сама процедура:
proc get_command_line_argument nArg:DWORD
        xor     eax, eax
        push    ebx
        mov     ebx, [cmdln.nCount]
        dec     ebx
        cmp     [nArg], ebx
        ja      @f
        mov     ebx, [nArg]
        shl     ebx, 2
        push    edi
        xor     edi, edi
        push    esi
        mov     esi, dword [cmdln.lpArgs]
        add     esi, ebx
        lodsd
        pop     esi
        pop     edi
    @@:
        pop     ebx
        ret
endp

Возвращаемые значения:
Если EAX = 0, то параметра под данным номером не существует,
Иначе в EAX будет содержаться адрес строки в массиве lpArgStr.
chak_xakep (04.04.2011 в 15:08):
ManHunter, ну тогда я спокоен. Спасибо ;)
ManHunter (04.04.2011 в 15:01):
Конечно.
chak_xakep (04.04.2011 в 15:01):
Да лан, не злись! :) Вопрос такой, освобождается ли память при завершении приложения?
ManHunter (04.04.2011 в 14:06):
Повторяю: тебе лучше жевать, чем говорить. Если ты НЕ понимаешь смысл написанного, то зачем вообще что-то писать? Залезь под отладчиком, посмотри что в этой "утекшей" памяти находится, после этого можешь указывать мне на "ошибки".
chak_xakep (04.04.2011 в 14:00):
Ахтунг! утечка памяти :) В 127 строке прибрались за собой (HeapFree), а в 138 строчке выделили и забыли освободить. Хотя не так уж и страшно зависит от кол-ва вызовов этой самой ParseCmdLine.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2018
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0029 сек.) / Память: 4.75 Mb
Наверх