438: else
439: newdir = newJob.progs[0].argv[1];
440: if (chdir(newdir))
441: printf("сбой при смене текущего каталога: %s\n",
442: strerror(errno));
443: return 0;
444: } else if (!strcmp(newJob.progs[0].argv[0], "jobs")) {
445: for (job = jobList->head; job; job = job->next)
446: printf(JOB_STATUS_FORMAT, job->jobId, "Выполняется",
447: job -> text);
448: return 0;
449: }
14.6.2. Добавление универсализации файловых имен
Универсализацию файловых имен, при которой оболочка разворачивает символы *
, []
и ?
в соответствующие файловые имена, в определенной мере сложно реализовать из-за разнообразных методов применения кавычек. Первая модификация заключается в построении каждого аргумента в виде строки, подходящей для передачи в glob()
. Если символ универсализации помещен в кавычки, принятые в оболочке (например, двойные кавычки), тогда символу универсализации предшествует \
с целью предотвращения его разворачивания в glob()
. Этот процесс реализуется легко, хотя с первого взгляда может показаться сложным.
Две части синтаксического разбора команд в parseCommand(
) необходимо слегка изменить. Последовательности "
и '
обрабатываются ближе к началу цикла, что обеспечивает разделение командной строки на аргументы. Если во время синтаксического разбора мы находимся в середине строки в кавычках и сталкиваемся с символом универсализации, мы заключаем его в кавычки с предваряющим символом \
, что выглядит следующим образом.
189: } else if (quote) {
190: if (*src == '\\') {
191: src++;
192: if (!*src) {
193: fprintf(stderr,
194: "после \\ ожидался символ\n");
195: freeJob(job);
196: return 1;
197: }
198:
199: /* в оболочке "\'" должен дать \' */
200: if (* src ! = quote) *buf++ = '\\';
201: } else if (*src = '*' | | *src == '?' || *src == '[' ||
202: *src == ']')
203: *buf++ = '\\';
204: *buf++ = *src;
205: } else if (isspace(*src)) {
В код были добавлены только средний else if
и оператор присваивания в его теле. Похожий код потребуется предусмотреть для обработки символов \
, встречающихся вне строк в кавычках. Это реализовано в конце главного цикла parseCommand()
. Ниже приведен измененный код.
329: case '\\':
330: src++;
331: if (!*src) {
332: freeJob(job);
333: fprintf(stderr, "после \\ ожидался символ\n");
334: return 1;
335: }
336: if (* src == '*' || *srс == '[' | | *src == ']'
337: || *srс == '?')
338: *buf++ = '\\';
339: /* сквозная обработка */
340: default:
341: *buf++ = *src;
Для заключения знаков универсализации в кавычки здесь был добавлен тот же самый код.
Эти две кодовые последовательности обеспечивают передачу каждого аргумента в glob()
без поиска неожиданных совпадений.
Теперь добавим функцию globLastArgument()
, которая универсализирует самый последний аргумент для дочерней программы и замещает его любым найденным совпадением.
Для облегчения управления памяти к struct childProgram
добавляется элемент globResult
типа glob_t
, используемый для хранения результатов всех операций универсализации. Кроме того, добавляется целочисленный элемент freeGlob
, не равный нулю, если freeJob()
должна освободить globResult
. Ниже показано полное описание struct childProgram
в ladsh3.c
.
35: struct childProgram {
36: pid; /* 0, если завершена */
37: char ** argv; /* имя и аргументы программы */
38: int numRedirections; /* элементы в массиве перенаправлений */
39: struct redirection Specifier * redirections; /* перенаправления ввода-вывода */
40: glob_t globResult; /* результат универсализации параметров */
41: int freeGlob; /* нужно ли освобождать globResult? */
42: };
Во время первого запуска для командной строки функция globLastArgument()
(когда argc
для текущей дочерней оболочки равно 1) инициализирует globResult
. Для остальных аргументов она пользуется преимуществом GLOB_APPEND
для добавления новых совпадений к существующим. Это избавляет от необходимости распределения собственной памяти для целей универсализации, поскольку одиночный glob_t
при необходимости автоматически расширяется.
Если globLastArgument()
не находит совпадений, символы \
с кавычками удаляются из аргумента. В противном случае все новые совпадения копируются в список аргументов, создаваемый для дочерней программы.
Ниже приведена полная реализация globLastArgument()
. Все сложные ее части относятся к управлению памятью; фактическая универсализация похожа на реализованную в программе globit.с
, которая представлена ранее в главе.
Читать дальше