Чтобы не допустить возникновения этой проблемы, функция XtAppInitialize()
принимала массивы argc
и argv
в качестве параметров и возвращала новые значения для каждого из них с параметрами, обработанными удаленной библиотекой. Несмотря на то что такой подход мог работать, с ростом количества библиотек он стал излишне громоздким.
Чтобы выйти из этой ситуации, popt
позволяет формировать вложенные таблицы параметров. Благодаря этому подходу библиотеки определяют те параметры, которые им нужны для обработки (для этого может потребоваться еще одна вложенная таблица), а главная программа может предоставить эти параметры путем вложения таблиц с параметрами библиотек внутри самих себя.
Таблица параметров, которая будет представлена в форме вложенной таблицы, определяется подобно любой другой таблице. Чтобы включить ее в другую таблицу, необходимо создать новый параметр с пустыми параметрами longName
и shortName
. В поле argInfo
должна быть назначена переменная POPT_ARG_INCLUDE_TABLE
, а член arg
должен указывать на таблицу, представляемую в форме вложенной таблицы. Ниже показан пример таблицы параметров, включающей другую таблицу.
struct poptOption nestedArgs[] = {
{ "option1", 'a', POPT_ARG_NONE, NULL, 'a' },
{ "option2", 'b', POPT_ARG_NONE, NULL, 'b' },
{ NULL, '\0', POPT_ARG_NONE, NULL, 0 }
};
struct poptOption mainArgs[] = {
{ "anoption", 'о', POPT_ARG_NONE, NULL, 'o' },
{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, nestedArgs, 0 },
{ NULL, '\0', POPT_ARG_NONE, NULL, 0 }
};
В этом примере приложение заканчивается тремя параметрами, --option1
, --option2
и --anoption
. Более сложный пример с вложенными таблицами параметров рассматривается далее в главе.
26.2. Использование таблиц параметров
26.2.1. Создание содержимого
popt
может чередовать синтаксический анализ нескольких совокупностей командных строк. Для этого она сохраняет всю информацию о состоянии для определенной совокупности аргументов командных строк в структуре данных poptContext
непрозрачного типа, которую нельзя модифицировать вне библиотеки popt
.
Новое содержимое popt
формируется с помощью функции poptGetContext()
.
#include <popt.h>
poptContext poptGetContext(char * name, int argc, const char ** argv,
struct poptOption * options, int flags);
Первый параметр, name
, используется для работы с псевдонимами и в справочных сообщениях, и должен представлять имя того приложения, параметры которого будут проходить проверку синтаксиса. Следующие два параметра определяют те аргументы командной строки, которые будут проходить проверку синтаксиса. Как правило, они передаются функции poptGetContext()
, точно так, как если бы они передавались функции main()
программы[184]. Параметр options
указывает на таблицу параметров командной строки, которая была определена в предыдущем разделе. Последний параметр, flags
, определяет способ синтаксического анализа параметров и включает перечисленные ниже флаги (которые могут быть объединены битовым "ИЛИ").
POPT_CONTEXT_KEEP_FIRST |
Как правило, popt игнорирует значение в argv[0] , которое обычно представляет имя выполняемой программы, а не аргумент командной строки. Если определить этот флаг, то popt будет обрабатывать argv[0] как параметр. |
POPT_CONTEXT_POSIXMEHADER |
Стандарт POSIX гласит, что все параметры должны стоять перед дополнительными параметрами командной строки. Например, в соответствии с POSIX, rm -f file1 file2 приведет к удалению файлов file1 и file2 , тогда как rm file1 file2 -f приведет к обычному удалению трех файлов: file1 , file2 и -f . В большинстве Linux-программы это частное условие игнорируется, поэтому popt не придерживается этого правила по умолчанию. Этот флаг сообщает библиотеке popt о необходимости анализировать синтаксис параметров в соответствии с этим условием. |
Помимо всего прочего, poptContext
следит за тем, какие параметры прошли проверку синтаксиса, а какие нет. Если программе необходимо перезапустить обработку параметров в наборе аргументов, она может восстановить исходное состояние poptContext
, передавая функции poptResetContext()
содержимое в качестве единственного аргумента.
После завершения обработки аргумента процесс должен освободить структуру poptContext
, поскольку в ней содержатся динамически размещаемые компоненты. Функция poptFreeContext()
принимает poptContext
в качестве своего единственного аргумента и освобождает ресурсы, занятые в содержимом.
Ниже представлены прототипы функций poptResetContext()
и poptFreeContext()
.
#include <popt.h>
void poptFreeContext(poptContext con);
void poptResetContext(poptContext con);
26.2.2. Синтаксический анализ командной строки
После того как приложение создаст poptContext
, оно может приступить к синтаксическому анализу аргументов. Функция poptGetNextContext()
выполняет синтаксический анализ аргумента.
#include <popt.h>
int poptGetNextOpt(poptContext con);
Принимая содержимое в качестве своего единственного аргумента, эта функция анализирует синтаксис следующего обнаруженного аргумента командной строки. После того как следующий аргумент будет обнаружен в таблице параметров, функция заполняет объект, на который указывает указатель arg
элемента таблицы параметров, если только он не равен NULL
. Если элемент val
для параметра имеет ненулевое значение, функция возвращает это значение. В противном случае функция poptGetNextOpt()
переходит к следующему аргументу.
Функция poptGetNextOpt()
возвращает значение -1
, если был проанализирован синтаксис последнего аргумента, и другие отрицательные значения в случае возникновения ошибки. Поэтому лучше всего присваивать элементам val
в таблице параметров значения больше нуля.
Если все параметры командной строки обрабатываются через указатели arg
, то синтаксический анализ командной строки сокращается до следующей строки кода:
rc = poptGetNextOpt(poptcon);
Тем не менее, для многих приложений требуется более сложный синтаксический анализ командной строки, нежели этот, и применяется показанная ниже структура.
while ((rc = poptGetNextOpt(poptcon)) > 0) {
switch (rc) {
/* здесь обрабатываются специфические аргументы */
}
}
184
Распространенной ошибкой является определение массива argv
как char **
, а не как const char **
, что является правильным вариантом. Благодаря прототипу функции poptGetContext()
компилятор генерирует предупреждающее сообщение, если массив argv
будет определен неправильно.