делает dsum? Dsum - это утилита, которая выполняет проверку после копирования. Она предполагает, что файлы скопированы из каталога-источника в ката- лог-приемник. Каталог-источник назван управляющим каталогом, поскольку он следит за тем, какие файлы сравниваются. Для каждого файла в управ- ляющем каталоге печатается его имя вместе со значением его контрольной суммы и со значением контрольной суммы для скопированного файла в ката- логе-приемнике. Вся эта информация выдается в одной строке. Польза от получения всей информации от dsum в одной строке заклю- чается в том, что визуально два файла могут быть проверены очень легко. Вам нет необходимости смотреть в другое место для получения необходимой информации. Альтернативой для dsum может быть выполнение какого-либо сценария, подобного приводимому ниже. 1. Скопируйте ваши файлы в другой каталог. 2. Подсчитайте контрольную сумму всех файлов из управляющего каталога и выведите результат в какой-либо файл. 3. Подсчитайте контрольную сумму всех файлов в каталоге, содержащем копию, и выведите результат в какой-либо файл. 4. Сравните эти два файла командой diff для того, чтобы увидеть, не отличаются ли какие-либо копии. Это не дает вам даже хорошего вывода на экран о том, что происхо- дитгиЭто не дает вам даже хорошего вывода на экран о том, что происхо- Dsum не проходит вниз по дереву файлов, потому что большинство ко- пий являются копиями из каталога в каталог, а не из сегмента дерева в сегмент дерева. Из-за того, что она не выполняет такой обход, сложность программы существенно понижается. По умолчанию сравниваются ВСЕ файлы. Это предполагает, что вы ско- пировали все файлы в каталог копирования. В некоторых случаях вы можете захотеть копировать только выбранные файлы, такие как *.c (все ваши исходные файлы). В этом случае управляющий каталог содержит множество файлов, а каталог с копиями содержит только файлы с расширением .c. Для поддержки таких случаев в программу включены ключи -c и -o. Ключ -c указывает только файлы типа *.c из управляющего каталога. В ре- зультате производится проверка только файлов *.c в каталоге с копией. Ключ -o выполняет то же самое для файлов, соответствующих *.o. Примеры 1. $ mount /dev/fd0 /mnt $ cp /usr/include/* /mnt $ dsum /usr/include /mnt Монтирует гибкий диск в каталог /mnt. Копирует все файлы заголов- ков в каталоге /usr/include на гибкий диск. Проверяет копии, используя dsum для исходного каталога и для каталога с копией. Примечание: Указывая копировать *, мы вообще не попадем в каталог /usr/include/sys. 2. $ dsum . .. Используя в качестве управляющих файлов файлы в текущем каталоге, сверить каждый файл с одноименным файлом в родительском каталоге. Пояснения В строках 4-9 производится проверка на наличие ошибок. Если указа- но менее двух аргументов, значит управляющий каталог и/или каталог ко- пии не указан и в результате обнаруживается ошибка. Если количество ар- гументов превышает три, значит, указано еще что-то кроме ключа -c и двух каталогов, что также является ошибкой. Все остальное (два или три аргумента) рассматривается как допустимое значение. В строках 11-21 производится инициализация переменной FLIST. FLIST - это управляющая переменная, которая определяет имена файлов, на кото- рые надо обратить внимание. Если в командной строке указаны только име- на каталогов ($# = 2), FLIST присваивается значение по умолчанию * (все файлы) в строке 12. Значение * присваивается переменой FLIST и не трак- туется в это время как метасимвол (это особенность командного процессо- ра). Если в командной строке указан ключ ($# = 3), производится провер- ка первой переменной и FLIST получает соответствующее значение, *.c или *.o. Если указана не такая опция, выводится сообщение об ошибке и прог- рамма завершается. В строках 23-31 выполняется сама работа. Здесь выполняется цикл for, который проходит по списку слов, созданному управляющим каталогом в соответствии со значением переменной FLIST. В строке 23 переменная FLIST расширяется фактически с символа * в имя каждого файла. Тем самым цикл for получает данные для использования. Следовательно, переменная FLIST является полным маршрутным именем каждого файла в управляющем ка- талоге. Строка 25 разбирает расширение, сделанное в строке 19. Переменная BASEF получает базовое имя полного маршрута из переменной FILE. Причи- ной, по которой мы это делаем, является тот факт, что позже при ссылке на каталог копии нам необходимо только имя файла. (В системе UNIX ко- манда basename возвращает последний элемент в указанном маршруте, т.е. само имя файла, если маршрут содержит промежуточные каталоги.) Строки 26-29 выводят первую часть выходного сообщения. Оператор if-then использован потому, что нам нужно менять выходное сообщение в зависимости от того, сколько символов содержит имя файла. Строка 26 оп- ределяет длину имени файла, используя команду expr. Команда expr может быть использована для сравнения двух строк и получает количество сов- павших символов. Сравнение имени файла со "всеми символами" (*), таким образом возвращает длину строки. (У вас может возникнуть желание обра- титься к expr(1), чтобы получить информацию о других хитростях этой многоцелевой команды.) Это возвращаемое значение используется в операторе test для опре- деления, содержит ли имя файла менее семи символов: возможно всего один или два символа. В последнем случае, если мы делаем табуляцию, мы полу- чим только первую позицию табуляции. Для получения последующих табуля- ций мы отображаем семь символов для того, чтобы попасть на место следу- ющего поля табуляции. (Если было 3-6 символов, мы все равно остановимся на поле второй табуляции, т.е. это место работает верно.) Затем отобра- жаем табуляцию для того, чтобы мы попали на место окончания второй та- буляции, что нам и требовалось. Если имя файла содержит более семи символов, мы уже находимся в первой позиции табуляции или за ней. Таким образом, следующий символ табуляции передвинет нас во вторую позицию табуляции. Эффект заключа- ется в том, что для размещения колонок не имеет значения размер имени файла (кроме случая, когда оно действительно очень длинное). Это позво- ляет избавиться от "блюза ползущих колонок", когда колонки сдвигаются в зависимости от размера отображаемой информации. В качестве примера та- кого эффекта может служить стандартная команда sum. Ее выход выглядит так: -------------------------------- | 4243 3 autobkp | 247 1 can | 25167 6 cpiobr | 186 3 dosflp | 56864 2 dsum | 2782 1 log | С другой стороны, выход dsum очень ясный и четкий, не сдвигается по всему экрану. Сдвиг делает вывод изломанным и затрудняет быстрый просмотр информации. Чудо вывода в одну строку совершается в строке 27 (для файлов с именами менее 7 символов) или в строке 28 (для файлов с более длинными именами). Внутри команды echo в каждом случае мы прячем другие команды, но по-прежнему управляем тем, как их результаты выводятся на экран. Во-первых, сами имена файлов выводятся, будучи ранее извлеченными из полного маршрутного имени. Обратите внимание, что имена файлов не со- держат информацию о том, из какого они каталога. Затем мы несколько сдвигаемся и печатаем первое поле выходной суммы для этого файла (саму контрольную сумму). Это контрольная сумма версии файла в управляющем каталоге, поскольку переменная FILE была сгенерирована для этого ката- лога. Команда sum выводит три значения (контрольная сумма, число бло- ков, занятых файлом, и само имя файла). Нам нужно получить только пер- вое значение, которое извлекается путем выполнения команды sum и пере- дачи ее выхода по каналу команде cut, которая и возвращает первое поле. После того, как значение контрольной суммы напечатано, мы отображаем \c для запрещения перехода на новую строку. Это сохраняет курсор в той же строке. Здесь начинает работать строка 30. Она генерирует контрольную сум- му того же файла в каталоге копии ($2 в командной строке) и текущее имя файла, вырезая только число, и печатает его правее курсора в той же строке. Цикл завершается, когда все файлы из управляющего каталога будут проверены командой sum. ВОЗМОЖНЫЕ МОДИФИКАЦИИ КОМАНДНОГО ФАЙЛА Неплохой модификацией может быть вариант, чтобы dsum печатала не только значения, как это сделано, но и флаг в конце строки в случае, когда два файла отличаются. Тогда у нас не было бы необходимости просматривать непосредственно числа. Мы ищем флаг и сразу видим, когда что-то не так. Ниже приводится решение проблемы вывода флага для того, чтобы об- легчить вам работу. Оно не включено в представленную выше утилиту по той причине, что такого рода вещи могут быть выполнены относительно просто. Вы можете сами сделать эту модификацию в качестве упражнения. Текст программы, выполняющей эту работу, выглядит так: for FILE in $1/$FLIST do BASEF=`basename $FILE` S1=`sum $FILE 2>&1 | cut -d' ' -f1` S2=`sum $2/$BASEF 2>&1 | cut -d' ' -f1` if [ "$S1" = "$S2" ] then M="" else M="<---" fi if [ ` expr $BASEF : '.*'` -lt 7 ] then echo "$BASEF: \t$S1\t$S2 $M" else echo "$BASEF:\t$S1\t$S2 $M" fi done Подход к решению немного отличается от решения, принятого при на- писании dsum, поскольку вы не можете генерировать контрольную сумму на ходу. Вы должны перехватить выход команды sum и использовать его позже. То, что мы ищем, появляется в шестой строке. Если две контрольные суммы различны, переменная M устанавливается соответствующим образом. Если файлы различаются, переменная M получает стрелку, указывающую на то, что копия плохая. 3.4.2. log - меню доступа к файлам протокола копирования -------------------------------------------------------------------------- Имя : log _________________________________________________________________________ log Меню доступа к файлам протокола копирования НАЗНАЧЕНИЕ Обеспечивает интерфейс в виде меню к файлам протокола, полученным от утилиты autobkp. ФОРМАТ ВЫЗОВА log Пример вызова log Командный файл log 1 : 2 # @(#) log v1.0 Menu access to backup logfiles Author: Russ Sage 4 c 5 set `date` 6 echo " 8 $1, $2 $3 $4 10 Logfile Menu 11 ---------------- 12 1 - list all log file names 13 2 - display log of home backup 14 3 - display log of product backup 15 to exit 17 Enter command (1-3,<>): \c" 18 read CMD 20 case $CMD in 21 "") exit;; 22 1) echo "\nLogfile names:" 23 sed -n -e "/more/s/^.*more \(.*\);;$/\1/p" $HOME/bin/log;; 24 2) more $HOME/bin/autobkplog.home;; 25 3) more$HOME/bin/auto2.bkplogm;; 26 *) echo "log: $CMD is not a command";; 27 esac Переменные среды выполнения CMD Команда, полученная от пользователя HOME Ваш регистрационный каталог в системе Описание Зачем нам нужен log? Если вы читали эту главу, ничего не пропуская, вы уже встречались с программой autobkp. Выводные данные autobkp очень информативны и должны быть сохранены как часть операции копирования. Это еще более важно, если у вас имеются программы, запускаемые с помощью cron. Со временем некоторые из этих программ может начать работать неверно и разрушить все вами сделанные копии. Единственный способ проследить за такими вещами - это использовать файлы протокола. Файлы протокола ко- манды cron содержат некоторую информацию, но файлы протокола программы autobkp содержат ее гораздо больше. Проблема возникает, когда вы сталкиваетесь с наличием нескольких работ для autobkp. Вы можете не захотеть смешивать невзаимосвязанные копии файлов в одном и том же файле со списком маршрутов, поэтому вы создаете несколько файлов pathlist, несколько заданий для cron и несколько файлов протокола. Если вам нужно выполнить пять или десять подобных работ, каким образом вы проследите за всеми файлами протоко- лов, запомните их имена и облегчите их просмотр? Все эти проблемы реше- ны в командном файле log. Что делает log? Командный файл log - это управляемая при помощи меню утилита. Меню позволяет вам видеть текущие имена файлов в файлах протокола, поэтому вам нет необходимости помнить их. Остальные команды являются входными точками в файлы протокола, использующими команду more для просмотра. Когда мы рассматривали командный файл cpiobr, мы видели, как рабо- тает управляемая при помощи меню программа. Командный файл log несколь- ко проще в том смысле, что он делает только один проход. Кроме того, log - это "живая" программа. Она не является статичной и должна посто- янно изменяться в соответствии с вашими процедурами копирования. Посредством такой модификации log способна сообщить вам действительные имена файлов. Log является разновидностью программы, ссылающейся сама на себя. Для показа действительных имен файлов она просматривает свое содержимое и выбирает (используя sed) имена log-файлов из команды more, которая выводит их. Поскольку вы добавляете файлы протокола, программа log мо- жет хранить текущие, потому что она просматривает сама себя для опреде- ления того, что соответствует действительности. Применяя процедуру по- иска таким образом, мы избавляемся от необходимости сохранять отдельные файлы данных с именами в них или использовать какие-то соглашения об именовании выполнения той же задачи. Способность программы log обра- щаться самой к себе позволяет вам добавлять неограниченное число файлов протокола в список, и вам предоставляется свобода по выбору имен таких файлов. Возможно, вы заметили, что стратегия, использованная в командном файле log, может быть использована для обеспечения вывода на экран лю- бого набора файлов (записные книжки, документация или еще что-то). Все, что вам нужно сделать для этого - записать их в соответствии с командой more и добавить столько команд в главное меню, сколько вы хотите. Пояснения Строка 4 очищает экран, используя команду c, представленную ниже в этой книге. (Вместо этого вы снова можете использовать команду clear, если она доступна.) Строка 5 устанавливает в позиционные параметры выход команды date. Это то же самое, что мы делали в программе cpiobr. Строки 6-17 выводят меню. Здесь использован один оператор echo, как описано в cpiobr. Стро- ка 13 читает команду пользователя. Строки 20-27 выполняют основную работу программы. Если введенная команда была просто возвратом каретки (трактуется как нуль), программа завершается. В строке 23 команда sed просматривает файл $HOME/bin/log. Это требует, чтобы вы поместили log в подкаталоге двоичных модулей ва- шего регистрационного каталога. Если вы разместите ее где-либо в другом месте, вы должны изменить эту строку. Команда sed использует ключ -n, который запрещает вывод, за исключением того, что явно указано для пе- чати. Строка -e находит имена файлов. Данный подход использует функцию замены в команде sed. Таким обра- зом мы можем заменить все за исключением имени файла, а затем напеча- тать его. Смысл этой записи примерно такой: сперва мы ищем выражение more (/more/), находя тем самым все строки в файле протокола, содержа- щие слово "more". По определению, каждый файл протокола выводится на экран, используя команду more. Поскольку вы добавляете файлы протокола, каждая новая строка должна содержать слово more, поэтому файлы нахо- дятся автоматически по выражению команды sed. Затем мы указываем команде sed сделать замену. Первое выражение содержит в себе всю строку от начала до конца, но мы применяем круглые скобки для отметки внутри нее образца .*, тем самым выделяя часть стро- ки между пробелом после "more" и первой точкой с запятой в конце стро- ки. Если вы посмотрите на все строки в файле log, которые начинаются с "more", то вы увидите, что это соответствует имени файла, которое мы ищем. Затем мы указываем команде sed заменить всю строку на первый обра- зец "pattern 1". "Pattern 1" - это запись команды sed для первого отме- ченного или "отмеченного биркой" выражения. Другими словами, мы замени- ли имя файла на всю строку целиком и указали команде sed напечатать ре- зультат, тем самым выдавая на экран имя файла. Эта работа выполняется для такого количества операторов more, сколько вы имеете. Чем больше файлов log вы имеете, тем больше файлов обрабатывает команда sed. Обратите внимание, что оператор sed просмат- ривает любое количество символов от начала строки для нахождения слова "more". Не указывая в программе конкретное число символов, на которое нужно отступить, вы получаете тем самым свободу выбора ваших собствен- ных уровней отступа. Если введенная команда не является допустимой, выдается сообщение об ошибке. Эта программа не имеет цикла, поэтому срабатывает один раз. Если вы хотите запустить ее снова, вы должны снова ввести log. Пример $ log 1 После запуска программы выводится меню. Введите число 1 для того, чтобы увидеть все имена log-файлов. Теперь, когда мы изучили, как распознавать и управлять файлами во- обще, давайте рассмотрим некоторые систематические методы управления ИНФОРМАЦИЕЙ в файлах. Мы начинаем в следующей главе с файлов, которые важны для нас как для программистов.  * ГЛАВА 4. Управление программной документацией *  ВВЕДЕНИЕ 4.1. ПРОГРАММИРОВАНИЕ и УПРАВЛЕНИЕ ДОКУМЕНТАЦИЕЙ 4.2. ИЗВЛЕЧЕНИЕ ДОКУМЕНТИРУЮЩИХ ЗАГОЛОВКОВ 4.2.1. stripc - из файла на языке Си 4.2.2. stripf - из Си-функции 4.2.3. strips - из командного файла Shell 4.3. ctags - создание файла признаков исходного кода проекта ВВЕДЕНИЕ Вы решили рискнуть. Продукт на три месяца опаздывает в произ- водство и нуждается лишь в крохотной доработке. Вы уверены, что знаете, как работает функция, которая открывает входной буфер. Вы ее недавно использовали. Вы увеличиваете размер буфера в вызове функции и запуска- ете быстрый тестик. Все в порядке, поэтому вы окончательно собираете поставку на диске и отправляете ее в производство. Месяц спустя, начи- нают поступать сообщения от разгневанных заказчиков. Похоже, что если текстовый процессор, электронная таблица и база данных открыты все вместе и активны одновременно (что является одним из больших товарных достоинств вашего продукта), то просто новый буфер настолько велик, что поглощает ключевой раздел памяти и превращает высоко летающее чудо ин- тегрированного программного обеспечения в яркую руину. Почему вы не проверили документацию по этой функции? Выяснение то- го, в каком файле находится документация, заняло бы определенное время, а поскольку документацию так трудно сопровождать, то связанные с ней вещи так или иначе устаревают. Тем не менее, аналогичные провалы не должны возникать. Программирование - тяжелая работа, но это только половина работы. Хорошая документация очень важна, если вы собираетесь иметь возможность сопровождать ваш программный код, но и управление всей документацией, связанной с большим программным проектом также является тяжелой рабо- той. Происходят постоянные изменения, и обычно отсутствует единообразие подхода. Документирование исходных файлов на Сикак в целом, так и по каждой функции является хорошим первым шагом, но такая документация не очень полезна, если вы вынуждены пробираться через дюжины файлов, чтобы обнаружить, как называется конкретная функция или какие функции состав- ляют данный модуль. Если вы хотели бы изучить еще одно средство, связанное с разработ- кой, см. программу cg в главе 10. 4.1. ПРОГРАММИРОВАНИЕ И УПРАВЛЕНИЕ ДОКУМЕНТАЦИЕЙ В данной главе представлен набор командных файлов командного про- цессора для извлечения документирующей информации из исходного кода программ на Си и командных файлов командного процессора. Используются две стратегии. Первая состоит в том, что, следуя стандартной "модели документации" в исходном коде, вы можете придумать командные файлы, ко- торые просто "вытягивают" самые новые разделы с заголовочной информаци- ей из файлов с исходным кодом и собирают их затем в новый файл. Такие файлы служат в качестве каркаса для документации по программе. Следова- тельно, при условии, что заголовки исходного кода изменяются разными программистами стандартным образом, простая команда UNIX может извлечь полностью новый каркас руководства. Этот подход реализуют командные файлы stripc, stripf и strips. Stripc и stripf предоставляют листинги блоков документации уровня файла и уровня функций из ваших исходных файлов на Си, а strips извлекает доку- ментацию из командных файлов командного процессора. Второй подход - доступ к определенным видам структур (таким как функции на Си) в теле самого программного кода. Этим методом вы можете точно найти, как называется данная функция, без сосредоточенного изуче- ния горы листингов. Командный файл ctags является и полезным инструмен- том, и моделью применения этого подхода к другим видам программных структур. Ctags объединяет свой выводной файл с редактором vi/ex с целью предоставления простого способа доступа к любой заданной функции и ее просмотра, копирования или редактирования в текущей программе. Ctags делает это путем предоставления признаков, которые понимает vi, для каждой функции, обнаруженной в любом указанном наборе файлов. Таким об- разом, вы можете использовать простую команду редактора, чтобы получить то, что вам нужно. Вы больше не обязаны заботиться о том, какой файл содержит какую функцию. Ctags - отличный пример применения мощи UNIX в полном объеме. Имея такие инструментальные средства, вам не нужно изобретать ко- лесо, так как вы можете легко находить и выбирать те средства, которые необходимы вам в конкретном приложении. Вы уже написали программу уп- равления терминалом Trantor TR-101? Примените ctags и найдите ее. Более того, самодокументируемый напечатанный файл и документация о функциях, полученная с помощью этих командных файлов, дают другим программистам хороший старт в понимании того, что вы сделали. Это даже может слегка произвести впечатление на вашего начальника. Каким в общих чертах будет наш подход к созданию таких командных файлов? У нас есть некоторые потенциальные преимущества в применении такого вида доступа в системе UNIX. Прежде всего, исходные файлы не от- личаются от других текстовых файлов, поэтому мы можем использовать все имеющиеся в UNIX средства поиска и распознавания шаблонов (sed, awk и т.д.), чтобы находить символьные строки. Во-вторых, мы освоили технику обхода файловых деревьев и работы с отобранными типами файлов, описан- ную в предыдущих главах. Наш подход состоит в объединении этих средств таким образом, чтобы они обеспечивали доступ к структурированной доку- ментации, содержащейся в программных файлах. 4.2. Извлечение документирующих заголовков 4.2.1. stripc - из файла на языке Си ------------------------------------------------------------ ИМЯ: stripc ------------------------------------------------------------ stripc Извлекает документирующий заголовок из исходного файла на языке Си. НАЗНАЧЕНИЕ Печатает первый блок строк комментария в файле с исходным кодом на Си так, чтобы вы могли быстро идентифицировать назначение программы на Си. ФОРМАТ stripc файл [...] ПРИМЕР ВЫЗОВА stripc prog*.c > header Извлекает начальные блоки комментариев из всех файлов и помещает в один файл с именем header. ИСХОДНЫЙ КОД ДЛЯ stripc 1 : 2 # @(#) stripc v1.0 Strip comment header Author: Russ Sage 4 if [ "$#" -eq "0" ] 5 then echo "stripc: arg count error" >&2 6 echo "usage: stripc file [...]" >&2 7 exit 1 8 fi 10 for FILE in $@ 11 do 12 if [ ! -s $FILE ] 13 then echo "file \"$FILE\" does not exist" >&2 14 continue 15 fi 17 awk '/^\/\*/, /^ \*\// { if ($0 != " */") 18 print 19 else {print;exit} 20 }' $FILE 21 echo "^L" 22 done (Перед тем как вводить этот исходный код, обратите внимание, что в строке 21 должен быть действительно символ control- L, введенный между двумя кавычками, по причинам, рассмотренным ниже.) ПЕРЕМЕННАЯ СРЕДЫ FILE Хранит имя файла, полученное из командной строки. ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН stripc? В больших проектах по разработке программного обеспечения требу- ется обычно много времени для работы с документацией. Имеются программ- ные файлы для документирования, функциональные спецификации для написа- ния программ и, наконец, руководства и справочные карты, глоссарии, указатели и т.д. Настоящий программный код должен иметь свою собствен- ную встроенную документацию, иначе управление этим кодом становится очень трудным. Чтобы избежать путаницы, нужно создать модель документации, а за- тем сделать ее стандартом, которому должны следовать все программисты. Даже если эта модель не будет абсолютно идеальной, ее наличие является первым шагом по созданию среды, которой вы можете управлять. Следующие два инструментальные средства, которые мы предлагаем, следуют модели документации, описанной в дальнейшем тексте. Эта модель последовательна и понятна, ее можно дополнить или изменить по вашему усмотрению. ЧТО ДЕЛАЕТ stripc? Stripc печатает только первый заголовочный блок комментариев из начала исходного файла на языке Си. Желательно, чтобы этот блок содер- жал всю важную информацию о файле: его официальное имя, для чего он предназначен, кто его создал, когда он был создан и т.д. Внутри файла может размещаться одна или несколько функций или даже главная програм- ма. Эта модель предполагает, что весь ваш код содержит очень мало глав- ных программ и много независимых модулей. Рассмотрим на модельном исходном файле, какого рода информацию мы должны извлечь из исходных файлов. /* * Это документирующий заголовок для файла * с исходным кодом на языке Си. * Он поясняет, что содержится в файле (программы, функции, * библиотеки и т.д.) и идентифицирует проект. * */ Это отметка конца заголовочного комментария. ^L Инструменты извлечения применяют control-L как разделитель. /* Это документирующий заголовок для главной части программы. * Главная пометка должна объяснять, что это за программа * и что она делает. Здесь могут быть также указаны автор, * дата и история изменений. */ main() { /* Здесь находится главная Си-программа */ } ^L /* Это документирующий заголовок для определенной функции, * которая за ним следует. Документируется последователь- * ность вызова, вход и выход и общее назначение этой * функции. */ func(arg1,arg2) int arg1; char arg2; { /* Текст функции находится здесь */ } ^L /* Аналогично, этот блок комментариев документирует * следующую функцию. Наличие документации вместе с кодом * сокращает объем накладных расходов при чтении и * изменении кода. */ func(arg1,arg2) int arg1, arg2; { /* Текст функции находится здесь */ } Как указывалось ранее, функция main не обязательна и, вероятно, встречается только в одном или двух файлах, в зависимости от вида прог- рамм, которые вы пишете. Один файл может иметь столько функций, сколько вы хотите, но рекомендуемое максимальное число - от одной до трех, в зависимости от того, как эти функции взаимосвязаны. В каждом файле имейте дело только с одной программируемой идеей и ее реализацией. При изучении этой модели вы видите, что обеспечивается три уровня документации. Заголовок в начале файла извлекается с помощью stripc. Этот заголовок относится ко всему файлу в целом. Заголовок в начале главной программы относится ко всей программе и поддерживается с по- мощью stripf. Заголовок для каждой функции относится к этой функции. Эти заголовки обслуживаются командным файлом stripf, который обсужда- ется ниже. Отметим, что между функциями имеется прогон формата (символ control-L кода ASCII). В предыдущем листинге мы указали эту комбинацию клавиш с помощью символа ^L, чтобы наши текстовые процессоры не произ- водили лишних страниц при форматировании рукописи данной книги. Вам нужно в каждом случае действительно вводить control-L вместо ^L при размещении комментариев в ваших файлах и при вводе исходного кода дан- ного и последующих командных файлов. Символ прогона формата использу- ется в модели заголовка для отметки верхней границы первой функции в файле и для прогона страниц на печатающем устройстве при чистовой распечатке, чтобы каждая функция появлялась на новой странице. В начале каждой функции (перед main или перед именем функции) дол- жен существовать документирующий заголовок. Этот заголовок обычно отра- жает наиболее недавние изменения в этом модуле, и его можно считать бо- лее достоверным, чем заголовок документа, который был напечатан несколько недель или даже месяцев назад. Входом для stripc является последовательность имен файлов с исход- ным кодом. Для каждого файла в командной строке проверяется, существует ли он и имеет ли размер больше, чем ноль байт. Если он не удовлетворяет этим критериям, то печатается сообщение об ошибке и проверяется следую- щий файл. Каждый файл читается с первого байта, и в нем ищется символь- ная строка начала комментария (/*). Когда она найдена, информация до символьной строки конца комментария (*/) построчно выводится в stdout. Если правые символы не найдены, ничего не печатается, но сообщение об ошибке не выводится, чтобы не испортить выводную информацию. После того как каждый файл обработан, в конце печатается прогон формата, который разбивает выводную информацию на страницы-разделы. Это применяется в основном, когда документирующие заголовки очень длинные и нуждаются в визуальной разбивке. Отметим, что "извлечение" ("strip") здесь и в следующих двух ути- литах означает не УДАЛЕНИЕ, а копирование соответствующей информации. Никаких изменений во входных файлах не делается. Когда все файлы в командной строке обработаны, командный файл за- вершается. ПРИМЕРЫ 1. $ stripc test?.c > test.filehdrs Извлекает блок файловых комментариев из всех файлов, соответствую- щих строке "test", за которой следуют один произвольный символ, а затем .c. Сюда подходят test1, testA, testb и т.д. Все блоки комментариев по- мещаются в файл test.filehdrs в таком порядке, как они обрабатывались бы в цикле for. 2. $ for DIR in src1 src2 src3 > do > stripc $DIR/*.c > /tmp/$DIR.hdrs > done Этот цикл проходит каждый из трех каталогов src1, src2 и src3. В каждом из них имеются исходные файлы, из которых нужно извлечь докумен- тирующие заголовки. Каждый раз берется один каталог, и stripc вызыва- ется для всех исходных файлов на языке Си из данного каталога. Выход, т.е. все документирующие заголовки из файлов в этом каталоге, помеща- ется в файл в каталоге /tmp. Файлы в /tmp имеют имена /tmp/src1.hdrs, /tmp/src2.hdrs и /tmp/src3.hdrs. Отметим, что число файлов, передавае- мых stripc, имеет ограничение в 255 символов. После этого командная строка переполняется и выполнение командного файла аварийно заверша- ется. ПОЯСНЕНИЯ Строки 4-8 делают проверку на ошибки. Если число параметров в ко- мандной строке нулевое, возникает ошибка. При вызове stripc должно быть хотя бы одно имя файла. Цикл for в строках 10-22 пробегает по каждому имени файла из списка позиционных параметров в командной строке. Первым делом, в строках 12-15 проверяется существование файла. Если файл не существует, выдается соответствующее сообщение об ошибке и цикл продолжается со следующим файлом. Строки 17-20 - это цикл awk, который делает всю важную работу. Входным для awk является имя файла, которое сейчас обрабатывает цикл for. Если вы не очень знакомы с программой awk, сообщим, что она вызы- вается так: awk программа имя-файла где программа состоит из последовательности предложений, имеющих вид: шаблон { действие } Указанное действие применяется к тексту, который соответствует шаблону. Для того чтобы утилита awk работала корректно, вы должны зак- лючить всю программу в одинарные кавычки. В stripc для указания начала комментария (/*) и конца комментария (*/) используются соответственно шаблоны /^\/\*/ и /^ \*\// Для интерпретации этих обозначений нужно вспомнить регулярные вы- ражения ed, sed и grep. Регулярные выражения (РВ) должны быть ограниче- ны (символом /). awk воспринимает два выражения, разделенные запятыми, как начальный и конечный шаблоны для квалификации вводных строк. В данном примере начальное выражение означает "от начала строки (^), вслед за действительным символом косой черты (который должен быть экранирован, чтобы убрать его специальное значение, т.е. \/) и действи- тельной звездочкой (\*), использовать все строки, обнаруженные вплоть до конечного выражения". Этим выбирается только первое вхождение со- поставляемого шаблона (т.е. первого блока комментариев), так как прог- рамма awk заканчивает работу при обнаружении завершающей строки. (Остальные блоки комментариев выбираются с помощью stripf вместе с име- нем функции и аргументами.) Для каждой строки, которая соответствует набору выражений от на- чального до конечного, выполняются предложения, указанные в "действии". Строки 17, 18 и 19 содержат предложение if-then= else, которое выполня- ет всю работу. Если $0 (что является всей строкой) НЕ равно пробелу и концу комментария (*/), печатается вся строка. Формируя таким образом предложение if, мы печатаем первую строку и все последующие строки, по- ка не доберемся до конца комментария. Когда обнаружена последняя строка, результатом проверки является значение "ложь", поэтому выполняется else-часть. Поскольку мы знаем, что это последняя строка, мы печатаем ее для завершенности блока ком- ментария, а затем выходим из awk. Отметим, что благодаря вложению этих двух команд вместе в фигурные скобки ({}), они рассматриваются как одно составное предложение. После завершения awk производится эхо-отображение прогона формата на экран и берется следующий файл. Так продолжается до тех пор, пока все файлы в командной строке не будут обработаны. 4.2.2. 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,