Stripf - из Си-функции
ИМЯ: stripf
stripf Извлекает документирующий заголовок Си-функции.
ФУНКЦИЯ
Извлекает и печатает комментирующий заголовок, имя функции с параметрами вызова и объявление типов параметров для всех функций в исходном файле на Си.
ФОРМАТ
stripf file [...]
ПРИМЕР ВЫЗОВА
stripf lib1.c
Извлекает документирующие заголовки для всех функций в файле lib1.c.
ИСХОДНЫЙ КОД ДЛЯ stripf
1 : 2 # @(#) stripf v1.0 Strip function header Author: Russ Sage 4 for FILE in $@ 5 do 6 sed -n -e ' 7 /^L$/ { 8 s/^L$/.bp/p 9 : loop 10 n 11 /^{/b exit 12 p 13 b loop 14 : exit 15 i\ 16 {} 17 b 18 }' $FILE 19 done
ПЕРЕМЕННАЯ СРЕДЫ
FILE Хранит имя файла для каждого файла из командной строки.
ОПИСАНИЕ
Зачем нам нужен stripf?
Предположим, что наш код на языке Си соответствует модели документации, представленной ранее при описании stripc. Тогда нам нужен способ поддержания изменений в документации по ходу изменений кода. Мы видели, что при хранении документации в исходных файлах более вероятно, что она будет изменена, когда изменится код. Проблема возникает, когда нам нужна твердая копия документации, которая находится внутри исходного кода.
Как нам получить ее из файлов?
Что делает stripf?
Командный файл stripf решает эту проблему. Он просматривает весь файл и печатает всю документацию для каждой ФУНКЦИИ, которая размещена в этом файле (включая "main", если она есть).
Входом для stripf являются имена файлов, переданные в командной строке. Stripf обрабатывает файлы по очереди и помещает выход в stdout. Этот выход можно перенаправить, но вход должен быть в командной строке.
К выходу применяются дополнительные модификации, чтобы сформировать данные для среды утилиты nroff, поэтому выводные файлы можно форматировать с помощью этой утилиты. Все прогоны формата заменяются на команду .bp, принятую в nroff для начала страницы. Эта команда nroff прогоняет страницу и увеличивает на единицу счетчик страниц. Возможно, вы не хотите менять нажатие клавиш control-L на это значение, но вы всегда можете указать в командном файле такие действия, какие вам нужны.
ПРИМЕРЫ
1. $ stripf module1.c | grep >"^\.bp$" | wc -l
Печатает число модулей-функций, содержащихся в файле module1.c, путем поиска каждого появления команд новой страницы программы nroff и их подсчета. Мы знаем, что одна из таких команд является выходом для каждой обнаруженной функции. Данную строку можно вложить в предложение echo, которое говорит "имеется X модулей в файле $FILE".
2. $ for FILE in *.c ../*.c $HOME/src/*.c > do > stripf $FILE > done >> /tmp/func.hdrs
Данный цикл for распространяется на все файлы в текущем каталоге, которые оканчиваются на .c, все файлы в родительском каталоге с таким же суффиксом и все файлы в подкаталоге src моего home-каталога с тем же суффиксом. Из каждого файла извлекается документация о функциях и направляется в стандартный вывод. Весь цикл перенаправлен с помощью >>, поэтому выход каждого вызова stripf ДОБАВЛЯЕТСЯ к файлу в /tmp.
ПОЯСНЕНИЯ
Вся программа - это один большой цикл for в строках 4-19. Этот цикл присваивает переменной FILE каждое имя, имеющееся в командной строке. Данный командный файл не имеет опций и обработки ошибок.
Команда sed системы UNIX вызывается для каждого имени файла. Программа sed читает весь вход и выводит измененный текст в стандартный вывод.
Опция -n используется в sed для подавления всего вывода, в противоположность действию по умолчанию, когда все печатается. Мы используем этот флаг по той причине, что мы хотим указать программе sed, когда печатать выход. Опция -e применяется, чтобы сообщить программе sed, что следующая последовательность текста между одинарными кавычками является выражением, которое нужно вычислить.
Напомним, что sed - потоковый редактор, который читает одну строку, сверяет ее с выражениями, затем читает следующую строку и делает все сначала. Первое, что мы ищем - символ control-L, стоящий в строке самостоятельно. Если мы не находим его, проверяется следующая строка и так далее, пока не будет обнаружен control-L. (Еще раз напомним, что вместо обозначения ^L в коде должен быть введен настоящий control-L.)
Когда обнаружен control-L, он активизирует все выражение, заключенное в фигурные скобки. Первым действием является подстановка команды начала страницы программы nroff, как описано ранее. Эта подстановка печатается, что является самым первым выводом. В строке 9 объявлена метка "loop". Это не приводит ни к каким действиям, но устанавливает точку перехода, которая впоследствии используется. (Управляющие структуры программы sed довольно примитивны, но они позволяют описать выполняемую работу.)
Строка 8 использует команду n программы sed, чтобы вызвать чтение следующей строки. Мы разобрались с первой строкой - строкой, которая содержит control-L - так что мы можем ее отбросить. В случае выполнения цикла мы видим, что sed продвигается по нашему требованию, но не продвигается сам.
Вспомним модель документации, рассмотренную ранее. Эта модель включает документирующий заголовок для файла в целом, выполненный в обычном стиле языка Си. Модель завершается символом control-L. Этот первый блок обрабатывается с помощью stripc, как описано ранее. Мы не хотим использовать его здесь при работе со stripf. Поэтому мы сейчас должны спозиционироваться после файлового документирующего заголовка.
Вслед за символом control-L имеется еще один набор из одной или более строк комментария языка Си, которые описывают функцию, следующую за ними. Далее идет само имя функции, объявление параметров и открывающий символ самой функции, которым является левая фигурная скобка (}).
Строка 11 ищет эту фигурную скобку. Если она найдена, выполнение переходит на метку exit (строка 14). Мы можем полагать, что мы все сделали, если найдена левая фигурная скобка, так как этот символ должен появляться только в начале функциимодуля. Когда мы находим фигурную скобку, мы уже напечатали к этому моменту всю комментирующую информацию и заголовок функции посредством строки 12, которую мы сейчас опишем. А что если фигурная скобка появляется в поле комментария? Нет проблем, поскольку поиск фигурной скобки привязан к началу строки с помощью символа ^. Он производит выражение, означающее "от первого символа в строке". Мы только тогда сопоставляем фигурную скобку этому выражению, когда она встречается в качестве самого первого символа в строке.
Затем строка 12 предполагает, что мы еще не обнаружили фигурную скобку и поэтому мы должны напечатать строку. Оператор p печатает текущую строку, которую обрабатывает sed. Этот вывод направляется на экран.
Строка 13 - безусловный переход на метку loop. Отметим, что этот переход только изменил процесс выполнения и не привел к чтению еще одной вводной записи. С этим мы должны быть осторожны при управлении процессом выполнения в программе sed. Мы только что напечатали текущую запись, поэтому теперь мы должны отбросить ее и получить следующую запись, что означает возврат в строку 9. Этот цикл печати и чтения следующей записи продолжается до обнаружения фигурной скобки, которая переводит выполнение на метку exit.
Строка 14 - это метка exit. Когда мы попадаем на нее, мы знаем, что был обнаружен control-L, напечатан комментирующий заголовок, напечатаны имя функции и объявления ее параметров и найдена фигурная скобка. Заметим, что фигурная скобка еще не напечатана. Когда мы находим ее, мы только делаем ветвление.
Строка 15 завершает вывод, вставляя некоторый текст в выводной поток. Мы не можем сказать "печатать это в буквенном виде", поэтому происходит движение вправо по тексту, как по команде echo. Мы должны сыграть на правилах, установленных программой sed. Любой вывод должен быть порожден обычными командами в стиле редактора ed. Вставка текста с помощью команды "i" делает нам это. Отметим, что мы также вставляем символ возврата каретки (или перевода строки, в зависимости от вашей осведомленности). Он может быть определен символом обратной косой черты (\). Обратная косая черта убирает специальное значение символов и при использовании в конце строки, как здесь, означает, что специальный символ, вставленный в выражение, является возвратом каретки. Вдобавок к возврату каретки, мы вставляем пару фигурных скобок. Это обозначает объявление начала-конца функции, которую мы обрабатываем. Поскольку мы вставляем текст, мы не должны говорить программе sed, что его нужно печатать.
Строка 17 - безусловный переход на себя, указывающий программе sed переход на вершину всего обрабатываемого выражения. Когда это происходит, мы завершаем поиск еще одного control-L и начинаем весь процесс снова. Таким образом, мы можем обработать все функции из одного файла независимо от того, сколько их там.
Строка 18 является концом sed-выражения и содержит также имя файла, которое должно быть передано программе sed. Это является частью обычного синтаксиса, принятого в sed, но выглядит несколько неуместным, так как не выделено специальным отступом. Когда все файлы обработаны, завершается внешний цикл и заканчивается работа командного файла.