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.с, которая представлена ранее в главе.
87: void globLastArgument(struct childProgram * prog, int * argcPtr,
88: int * argcAllocedPtr) {
89: int argc = *argcPtr;
90: int argcAlloced = *argcAllocedPtr;
91: int rc;
92: int flags;
93: int i;
94: char * src, * dst;
95:
96: if (argc >1) {/* cmd->globResult уже инициализирован */
97: flags = GLOB_APPEND;
98: i = prog->globResult.gl_pathc;
99: } else {
100: prog->freeGlob = 1;
101: flags = 0;
102: i = 0;
103: }
104:
105: rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
106: if (rc == GLOB_NOSPACE) {
107: fprintf (stderr, "не хватает памяти для выполнения универсализацииn");
108: return;
109: } else if (rc == GLOB_NOMATCH ||
110: (!rc && (prog->globResult.gl_path - i) == 1 &&
111: !strcmp(prog->argv[argc - 1],
112: prog->globResult.gl_pathv[i]))) {
113: /* необходимо удалить кавычки в , если они все еще присутствуют */
114: src = dst = prog->argv[argc - 1];
115: while (*src) {
116: if (*src ! = '\') *dst++ = *src;
117: src++;
118: }
119: *dst = ' ';
120: } else if (!rc) {
121: argcAlloced += (prog->globResult.gl_pathc - i);
122: prog->argv = realloc(prog->argv,
123: argcAlloced * sizeof(*prog->argv));
124: memcpy(prog->argv + (argc - 1),
125: prog->globResult.gl_pathv + i,
126: sizeof(*(prog->argv)) *
127: (prog->globResult.gl_pathc - i));
128: argc += (prog->globResult.gl_pathc - i - 1);
129: }
130:
131: *argcAllocedPtr = argcAlloced;
132: *argcPtr = argc;
133: }
Последними изменениями касаются вызовов globLastArgument(), которые должны совершаться после синтаксического разбора нового аргумента. Вызовы добавляются в двух местах: когда за пределами строки в кавычках найдены пробелы и когда вся командная строка разобрана. Оба вызова выглядят следующим образом:
globLastArgument(prog, &argc, &argvAlloced);
Полный код ladsh3.с доступен на Web-сайте издательства, а также на сайте, посвященном книге.
14.7. Обход деревьев файловых систем
Существуют две функции, которые облегчают приложениям просмотр всех файлов каталога, включая файлы в подкаталогах. Рекурсивный просмотр всех элементов древовидной структуры (например, файловой системы) часто называется обходом (walk) дерева и он реализуется функциями ftw() и nftw(). nftw() представляет собой усовершенствованную версию ftw.
14.7.1. Использование ftw()
#include <ftw.h>
int ftw(const char *dir, ftwFunctionPointer callback, int depth);
Функция ftw() начинает с каталога dir и вызывает указанную в callback функцию для каждого файла в этом каталоге и его подкаталогах. Функция вызывается для всех типов файлов, включая символические ссылки и каталоги. Реализация ftw() открывает каждый найденный каталог (с помощью файлового дескриптора) и для увеличения производительности не закрывает их, пока не закончит чтение всех элементов каталога. Это означает, что он использует количество файловых дескрипторов, равное количеству уровней подкаталогов. Чтобы предотвратить недостаток файловых дескрипторов в приложении, параметр depth ограничивает количество файловых дескрипторов ftw(), остающихся одновременно открытыми. Если этот предел достигается, производительность снижается, поскольку каталоги необходимо часто открывать и закрывать.
Функция, на которую указывает callback, определяется следующим образом:
int ftwCallbackFunction(const char * file, const struct stat * sb, int flag);
Эта функция вызывается один раз для каждого файла в дереве каталогов, а первый параметр, file, представляет собой имя файла, начинающееся с dir, которое передается ftw(). Например, если бы аргумент dir принимал значение ., одним из файловых имен было бы ./bashrc. Если бы вместо этого использовалось /etc, имя файла выглядело бы как /etc/hosts.
Следующий аргумент обратного вызова, sb, указывает на структуру struct stat как на результат применения stat() к файлу[102]. Аргумент flag предоставляет информацию о файле и принимает одно из следующих значений.
FTW_F Файл не является символической ссылкой или каталогом. FTW_D Файл является каталогом либо символической ссылкой, указывающей на каталог. FTW_DNR Файл является каталогом, полномочий на чтение которого у приложения нет (то есть его обход невозможен). FTW_SL Файл является символической ссылкой. FTW_NS Файл является объектом, к которому не удалось применить stat(). Примером может служить файл в каталоге, права на чтение которого приложение имеет (приложение может получить список файлов этого каталога), но не имеет права на выполнение (что предотвращает успешный вызов stat() применительно к файлам этого каталога).
Когда файл является символической ссылкой, ftw() пытается последовать за этой ссылкой и вернуть информацию о файле, на который она указывает (ftw() проходит один и тот же каталог несколько раз, если на него имеется несколько символических ссылок). Однако для поврежденной ссылки не определено, вернется FTW_SL или FTW_NS. Это хорошая причина, чтобы использовать nftw().
Если функция обратного вызова возвращает ноль, обход каталога продолжается. Если возвращается ненулевое значение, обход дерева файлов завершается и эта величина возвращается функцией ftw(). При удачном завершении обхода ftw() возвращает ноль, а в случае ошибки возвращается -1.
14.7.2. Обход дерева файлов с помощью nft()
Новая версия ftw() — nftw() — решает неоднозначность символических ссылок, присущих ftw(), и содержит несколько дополнительных свойств. С целью правильного определения nftw() заголовочными файлами значение _XOPEN_SOURCE в приложении должно быть определено 500 или больше. Ниже показан прототип nftw().