, а строки 30–45 анализируют опции. Строки 47–51 компилируют регулярное выражение, помещая результаты в
pattern
,
compilе_раttern
увеличивает значение
errors
, если была проблема. (Соединение функций посредством глобальной переменной, как здесь, обычно считается плохой манерой. Для небольших программ, подобным этой, это сойдет, но для более крупных программ такое сопряжение может стать проблемой.) Если не было ошибок, строка 51 увеличивает значение
optind
так, что оставшиеся аргументы представляют файлы для обработки.
53 if (optind == argc) /* файлов нет, по умолчанию stdin */
54 process("standard input", stdin);
55 else {
56 /* цикл с файлами */
57 for (i = optind; i < argc; i++) {
58 if (strcmp(argv[i], "-") == 0)
59 process("standard input", stdin);
60 else if ((fp = fopen(argv[i], "r")) != NULL) {
61 process(argv[i], fp);
62 fclose(fp);
63 } else {
64 fprintf(stderr, "%s: %s: could not open: %s\n",
65 argv[0], argv[i], strerror(errno));
66 errors++;
67 }
68 }
69 }
70
71 regfree(&pattern);
72 return errors != 0;
73 }
Строки 53–69 обрабатывают файлы, отыскивая соответствующие шаблону строки. Строки 53–54 обрабатывают случай, когда файлы не указаны: программа читает со стандартного ввода. В противном случае, строки 57–68 обрабатывают в цикле файлы. Строка 58 обрабатывает особый случай '
92 fprintf(stderr, "%s: pattern '%s': %s\n", myname, pat, error);
93 errors++;
94 }
95 }
Строки 75–95 определяют функцию
compile_pattern
. Она сначала устанавливает
REG_NOSUB
в
flags
, поскольку нам нужно знать лишь «подходит ли строка?», а не «где в строке располагается подходящий
текст?»
Строки 84-85 добавляют дополнительные флаги в соответствии с опциями командной строки. Строка 89 компилирует шаблон, а строки 90–94 сообщают о возникших ошибках
97 /* process --- читает строки текста и сопоставляет их с шаблоном */
, которая читает файл и выполняет сопоставление с регулярным выражением. Внешний цикл (строки 106–119) читает строки ввода. Для избежания проблем с длиной строки мы используем
getline
(см. раздел 3.2.1.9 «Только GLIBC: чтение целых строк:
getline
и
getdelim
»). Строка 107 вызывает
regexec
. Ненулевое возвращаемое значение означает либо неудачное сопоставление, либо какую-нибудь другую ошибку. Строки 109–115 соответственно проверяют
REG_NOMATCН
и выводят ошибку лишь тогда, когда возникла какая-нибудь другая проблема — неудачное сопоставление не является ошибкой
Если возвращаемое значение равно 0, строка совпала с шаблоном и соответственно строка 117 выводит имя файла и совпавшую строку.
122 /* usage --- вывод сообщения об использовании и выход */
выводит сообщение об использовании и завершает программу. Она вызывается, когда предоставлены недействительные аргументы или не предоставлен шаблон (строки 38–40 и 44–45).
Вот и все! Скромная, но тем не менее полезная версия
grep
в 130 строк кода.
12.9. Рекомендуемая литература
1. Programming Pearls, 2nd edition, by Jon Louis Bentley Addison-Wesley, Reading, Massachusetts, USA, 2000. ISBN- 0-201-65788-0. См. также веб-сайт этой книги. [131]
Проектирование программы с операторами проверки является одной из главных тем в этой книге.