Если меня что и раздражает в программировании, так это необходимость в десятый (или сотый) раз писать код для выполнения одной и той же задачи. С анализом командных строк дело обстоит именно так - он необходим во всех фильтрах без исключения, но после того как вы напишете этот код пару раз, задача становится на редкость скучной . Поэтому я и постарался создать некий обобщенный анализатор, который ценой минимальных усилий с моей стороны обрабатывает командную строку и присваивает нужные значения переменным. Благодаря этому я могу уделить больше внимания самому фильтру (то есть основной задаче), а не второстепенному анализатору.
Обобщенный анализатор командных строк - это вам не фунт изюма, и даже самый тривиальный вариант потребует немалых усилий. Анализатор из нашего примера обладает минимальными возможностями, но во многих приложениях этого будет вполне достаточно.
Основная идея заключается в том, чтобы определить префиксы параметров, указать тип каждого параметра и задать значения по умолчанию. Структура, содержащая всю эту информацию, передается анализатору, который обрабатывает командную строку и присваивает значения найденным параметрам. Если при обработке строки происходит ошибка (скажем, обнаружи вается неизвестный параметр или там, где должен стоять переключатель, оказывается число), анализатор выдает сообщение об ошибке, прерывает работу и уведомляет вызывающую функцию. Ну как, просто? Да, просто сказать… запрограммировать несколько сложнее.
Информация об отдельном параметре хранится в виде записи OptionsRec, описанной в листинге 1.3. В нем приведен полный исходный текст всего модуля CmdLine. Создайте новый файл в редакторе, введите и сохраните код под именем CMDLINE.PAS.
Листинг 1.3. Модуль CmdLine
{ CMDLINE.PAS - Анализатор командной строки Автор: Джим Мишель Дата последней редакции: 04/05/97 } unit cmdline; interface type OptionType = (otBool, otInt, otString, otFilename); pOptionRec = ^OptionRec; OptionRec = record OptionChar : char; case Option : OptionType of otBool : (OnOff : Boolean); otInt : (Value : Integer); otString : (Param : ShortString); otFilename : (Filename : ShortString); end; pOptionsArray = ^OptionsArray; OptionsArray = Array [1..1] of OptionRec; { GetOptionRec - возвращает указатель на запись из передаваемого массива параметров Options, соответствующую заданному префиксу. Возвращает Nil, если префикс отсутствует в массиве. } function GetOptionRec ( Options : pOptionsArray; nOptions : Integer; OptionChar : char ) : pOptionRec; { ProcessCommandLine - обрабатывает командную строку в соответствии со списком параметров, переданным в массиве Options. Возвращает True при успешном завершении и False - в случае ошибки. } function ProcessCommandLine ( Options : pOptionsArray; nOptions : Integer ) : Boolean;Перечисляемый тип OptionType описывает различные виды параметров, о которых известно функции ProcessCommandLine. Запись OptionRec содержит три поля: префикс, тип параметра и вариантную часть, в которой хранится значение данного параметра (если вы незнакомы с вариантными записями, просмотрите раздел справки с соответствующей информацией или купите простейший учебник по Паскалю в ближайшем книжном магазине).
Запись OptionRec оказывается не слишком эффективным решением, поскольку все записи независимо от типа параметра имеют максимальный размер из всех возможных вариантов. Размер типа ShortString равен 256 байтам, поэтому большинство записей будет занимать гораздо больше места, чем действительно необходимо. Существует несколько способов решения этой проблемы, самый простой из них - использовать указатели на строки (вместо самих строк) для строковых и файловых типов. Я не реализовал эту возможность, поскольку она требует дополнительного кодирования.
Другая проблема тоже связана с типом ShortString. Самая длинная строка, которая может храниться в переменной типа ShortString, состоит из 255 символов, тогда как максимальная длина пути в Windows оказывается несколько длиннее (260 байт). Я рассчитывал воспользоваться типом Delphi AnsiString (то есть «длинной строкой»), но длинные строковые типы не могут входить в вариантную часть записи. И снова самым очевидным решением будет использование указателей.
Несмотря на эти проблемы, модуль CmdLine способен принести немало пользы. Дополнительные расходы памяти не особенно страшны, поскольку в большинстве программ используется совсем немного параметров, и нас уже не страшит дурацкое ограничение в 64 Кбайт на размер статических данных. (Помните, мы живем в обширном 32-разрядном мире!) С ограничением на длину имени дело обстоит посложнее, но лично у меня найдется не так уж много знакомых, которым захотелось бы вводить 256-символьный путь в командной строке (точнее, таких вообще не найдется).
Модуль CmdLine содержит две функции, которые могут вызываться внешними программами: GetOptionRec и ProcessCommandLine. Функция GetOptionRec возвращает указатель на запись с заданным префиксным символом. Если такой записи не существует, GetOptionRec возвращает Nil. Вся настоящая работа выполняется в функции ProcessCommandLine. Вы передаете ей массив структур OptionRec, а она анализирует командную строку и заполняет поля значений для каждого параметра. Если ProcessCommandLine удается без ошибок обработать все аргументы командной строки, она возвращает True. Если в какой-то момент произойдет ошибка, функция немедленно прекращает работу, выдает сообщение об ошибке и возвращает значение False.