Разделы:

Главная

О проекте

Загрузки

Документация:

Linux

BSD

Другие Unix

Программинг

HTML, XML...

Сервера

"Окна Закрой!"

MANы

 


Стандарт кодирования GNU.

==================================================================== Март 1994 Ричард Столлман (Richard Stallman) 1. Общие положения относительно Ваших программ При написании программ для проекта GNU (или любых ваших собственных программ) ни при каких обстоятельствах не должны учитываться какие-либо особенности реализации Unix. Если у Вас слабые представления о внутренней реализации Unix-программ, это совершенно не означает, что Вы не можете написать их имитацию; попытайтесь организовать эту имитацию таким образом, чтобы она не опиралась на особенности конкретной версии Unix с тем, чтобы это не влияло на получаемые результаты. Например, утилиты Unix как правило оптимизировались на минимальное использование оперативной памяти; если для Вас важна скорость, ваша программа будет реализована существенно по другому. Вы можете хранить весь входной файл в памяти и просматривать его там, вместо того, чтобы использовать stdio. Следует избегать использования временных файлов: обработку необходимо выполнять в один проход вместо двух (в частности, мы так делаем в ассемблере). Или, напротив, придавайте особое значение простоте за счет скорости. Для многих приложений скорость современных компьютеров делает возможным использование простых алгоритмов. Следует стремиться к общности. Например, Unix-программы часто используют статические таблицы или строки фиксированного размера. Вместо этого следует использовать динамическое распределение памяти. Проверьте, что Ваша программа обрабатывает символ NUL и другие необычные символы во входных файлах. Введите язык программирования для того, чтобы оставить возможность расширения возможностей и напишите часть программы на этом языке. Старайтесь помещать отдельные части программы в библиотеки, которые можно использовать независимо. По возможности следует использовать алгоритм "сборки мусора" вместо тщательного отслеживания, когда следует освобождать память, или использовать новый механизм распределения памяти obstack, разработанный в рамках GNU. 2. Принятие "вкладов" Если кто-то присылает Вам фрагмент кода для добавления его к программе, над которой Вы работаете, нам нужен юридический документ для его использования - документ того же плана, что мы должны потребовать у Вас. Каждый, кто вносит значимую программу или часть программы, должен подписать некоторый юридический документ и передать его нам для того, чтобы мы имели явно выраженное право собственности на программу. Получения документа только от основного автора недостаточно. Таким образом, перед тем, как принять некоторый вклад от другого человека, необходимо сообщить нам для того, чтобы мы могли решить все связанные с этим юридические моменты. Вы должны подождать до тех пор, пока мы не сообщим Вам, что мы получили подписанный документ, после чего Вы можете использовать переданный Вам вклад в программу. Это требование применимо для вкладов, полученных как до выпуска версии программы в свет, так и после. Если Вы получаете файл различий, исправляющий ошибку, и эти различия содержат значительные изменения, мы также должны получить юридический документ, подтверждающий право на их использование. Вам не нужно беспокоиться о получении документов для изменений нескольких строк в тех или иных местах, поскольку это не является значимым с точки зрения авторского права. Вы не должны требовать получения документов в случае, если Вы получили предложение или идею, которые не содержат кода, который Вы использовали. Все это может расстроить Вас; это расстраивает и нас также. Однако, если Вы не станете дожидаться, Вы сильно рискуете - например, что если работодатель жертвователя откажется подписать отречение от своих прав на взнос? Вам придется выкинуть полученный код из программы обратно! Самое худшее, если Вы забудете сообщить нам о других вкладчиках. Мы окажемся в очень затруднительном положении, если результатом этого станет однажды судебное разбирательство. 3. Журналы изменений Ведите журнал изменений для каждого каталога, описывающий изменения, вносимые в исходные файлы, которые расположены в этом каталоге. Целью этого является то, чтобы люди, которые в будущем будут искать ошибки, могли узнать об изменениях, которые привнесли эти ошибки. Зачастую, новая ошибка может быть найдена просмотром того, что было изменено в последнее время. Еще более важно то, что журнал изменений может помочь избежать концептуальной несогласованности между различными частями программы; он могут дать Вам историю того, как возникли конфликтующие концепции. Следует использовать команду Emacs M-x add-change для того, чтобы добавить новую запись в журнал изменений. Запись должна начинаться с символа "звездочка", за которым следует имя измененного файла и, в скобках, имена измененных функций, переменных или чего-либо еще, после чего должен стоять символ "двоеточие". Далее поместите описание выполненных Вами изменений. Несвязанные записи следует разделять пустыми строками. Когда две записи представляют собой части одного изменения, они должны находится вместе и между ними не должно быть пустых строк. В этом случае Вы можете опускать имя файла и символ "звездочка", если следующие друг за другом записи относятся к одному файлу. Некоторые примеры: * register.el (insert-register): Return nil. (jump-to-register): Likewise. * sort.el (sort-subr): Return nil. * tex-mode.el (tex-bibtex-file, tex-file, tex-region): Restart the tex shell if process is gone or stopped. (tex-shell-running): New function. * expr.c (store_one_arg): Round size up for move_block_to_reg. (expand_call): Round up when emitting USE insns. * stmt.c (assign_parms): Round size up for move_block_from_reg. Необходимо помещать полные имена изменяемых функций или переменных. Не следует сокращать или объеденять их. У тех, кто будет в будущем осуществлять поддержку текста, часто будет возникать необходимость поиска всех записей об изменениях по имени некоторой функции; если Вы сократите его, они не смогут найти все записи об изменениях. Например, некоторых разработчиков искушает соблазн сократить несколько имен функций, написав '* register.el ({insert,jump-to}-register)'; это плохо, поскольку поиск по именам jump-to-register или insert-register не обнаружит эту запись. Не нужно описывать полностью цель изменений или то, как они работают вместе. Лучше помещать такие объяснения в виде комментариев в коде. В журнале достаточно написать просто "New function", а комментарий, объясняющий что эта функция делает стоит поместить в исходный текст как комментарий. Тем не менее, иногда полезно написать одной строкой объяснение цели большой группы изменений. Журнал изменений объясняет, чем ранние версии программы отличаются от текущей версии. Интересующиеся люди могут увидеть текущую версию, и им не нужен журнал изменений для того, чтобы понять, что она из себя представляет. Однако журнал изменений должен давать четкие объяснения как изменялась программа по сравнению с более ранними версиями. Когда Вы изменяете способ вызова функции некоторым достаточно простым образом, и это изменение потребовало модификации всех вызовов этой функции, нет необходимости вводить отдельную запись для каждого измененного вызова функции. Достаточно написать в записи для вызываемой функции "All callers changed". В случае, если Вы изменяете только комментарий или строки документации, достаточно ввести запись для файла целиком, без указания имен функций, и написать просто "Doc fix.". Не нужно сохранять записи об изменениях в файлах документации, поскольку документация обычно не содержит таких ошибок, которые сложно обнаружить и исправить. Документация не содержит взаимосвязанных и взаимодействующих частей, поэтому для исправления ошибки в документации Вам обычно не нужно знать историю ее возникновения. 4. Совместимость с другими реализациями За некоторыми исключениями, утилиты и библиотеки для GNU должны быть совместимы с реализацией Berkley Unix, должны соответствовать стандарту ANSI C, если ANSI C определяет их функционирование, и соответствовать стандарту POSIX, если POSIX определяет их функционирование. Когда эти стандарты входят в противоречие друг с другом, желательно предоставить режимы работы, совместимые с каждым из них. ANSI C и POSIX препятствуют введению многих расширений. Вы можете вводить любые расширения, давая возможность выключить их путем указания опции '--ansi' или '--compatible'. Однако, если расширение таково, что оно не позволяет использовать почти любую совместимую программу или скрипт, то оно не обеспечивает совместимость вверх со стандартом. Поэтому Вам необходимо переработать соответствующий интерфейс. Многие программы GNU выключают расширения, противоречащие POSIX, если определена переменная окружения POSIXLY_CORRECT (даже если она определена с пустым значением). Разрабатывайте Ваши программы таким образом, чтобы они по возможности распознавали эту переменную. Когда некотарые возможности используются только пользователями, а не программами или командными файлами, и их обычная реализация в Unix выполнена плохо, приветствуется полная их замена чем-то абсолютно другим и лучшим. (Например, редактор vi был заменен редактором Emacs). Тем не менее, не плохо предоставить совместимость с предыдущими реализациями. Приветствуется добавление новых функциональных возможностей, которые отстутствуют в Berkley Unix. Дополнительные программы, не имеющие аналогов в Unix могут быть полезны, но в первую очередь необходимо реализовать то, что Unix уже имеет. 5. Соглашения, касающиеся Make-файлов (Makefile) Этот раздел описывает соглашения по написанию Make-файлов для программ GNU. 5.1. Общие соглашения для Makefile Каждый Makefile должен включать строку SHELL = /bin/sh для того, чтобы избежать проблем на системах, в которых переменная SHELL может быть унаследована из окружения. (Подобные сложности отсутствуют при использовании утилиты GNU make). Не следует предполагать, что '.' входит в путь для поиска исполнимых программ. Когда Вам нужно запускать программы, которые являются частью Вашего пакета, во время работы make, необходимо использовать префикс './', если программа строится в ходе работы make, или '$(srcdir)/' если файл является неизменяемой частью исходных текстов. Когда ни один из этих префиксов не указан, выполняется поиск в текущем пути. Отличие между './' и '$(srcdir)' существенно, когда используется опция --srcdir программы configure. Правило вида: foo.1: foo.man sedscript sed -e sedscript foo.man > foo.1 не будет работать, если текущий каталог не является каталогом, содержащим исходные тексты, поскольку foo.man и sedscript в этом случае не находятся в текущем каталоге. Когда используется GNU make, поиск исходных файлов может быть выполнен с помощью переменной VPATH, в случае, если в правиле присутствует только один файл зависимости. В этом случае автоматическая переменная make '$<' представляет имя исходного файла, где бы он ни находился. (Многие версии make воспринимают '$<' только в неявных правилахю) Фрагмент Make-файла типа foo.o: bar.c $(CC) -I. -I$(srcdir) $(CFLAGS) -c bar.c -o foo.c должен быть записан следующим образом: foo.c: bar.c $(CC) $(CFLAGS) $< -o $@ для того, чтобы VPATH мог бы быть правильно использован. Когда цель имеет несколько зависимостей, следует явно использовать $(srcdir). Например, приведенное выше в качестве примера правило для цели foo.1 следует записать следующим образом: foo.1: foo.man sedscript sed -s $(srcdir)/sedscript $(srcdir)/foo.man > foo.1 5.2. Использование утилит в Makefile. Команды, которые пишутся в Makefile (как, впрочем, и в любых других shell-скриптах вроде configure), должны работать в sh, а не в csh. Не следует использовать каких-либо специальных особенностей ksh или bash. Скрипт configure и правила Makefile для построения и установки программы не должны использовать никаких утилит кроме следующих: cat cmp cp echo egrep expr grep ln mkdir mv pwd rm rmdir sed test touch Следует использовать только общепринятые опции для этих программ. К примеру, не следует использовать 'mkdir -p', поскольку эта опция хоть и удобна, но большинство систем не поддерживают ее. Правила Makefile для построения и установки могут также использовать компиляторы и другие необходимые программы, но их использование должно выполняться через make-переменные для того, чтобы пользователь имел возможность заменить их определение на собственную альтернативу. Вот некоторые из программ, которые мы имеем ввиду: ar bison cc flex install ld lex make makeinfo ranlib texi2dvi yacc Когда Вы используете ranlib, Вы должны проверять его существование в системе, и использовать его только в том случае, если он присутствует. Это необходимо для того, чтобы можно было выполнять построение на системах, не имеющих ranlib. Если Вы используете символические ссылки, Вы должны поддержать возможность использования Вашего пакета в системах, которые не поддерживают символических ссылок. Возможно использование других утилит во фрагментах Makefile или скриптах, которые предназначены только для использования в данной конкретной системе, и для которых Вы уверены в их существовании. 5.3. Стандартные цели в Make Все программы в GNU должны иметь следующие цели в своих Makefile'ах. 'all' Компиляция всей программой. Эта цель должна быть целью по умолчанию. Эта цель не должна перестраивать никакие файлы документации; info-файлы должны включаться в поставку, DVI файлы должны формироваться только по явному запросу. 'install' Компиляция программы и копирование исполнымых файлов, библиотек и т.д. туда, где они должны располагаться для их обычного использования. Если возможно, по этой цели должна выполняться простая проверка того, что программа была правильно установлена. Команды должны создать все каталоги, в которых файлы будут установлены, если эти каталоги уже не существуют. Сюда входят каталоги, указанные как значения переменных prefix и exec_prefix, так же, как и все их требуемые подкаталоги. Другой способ выполнить это подразумевает использование цели installdirs, описанной ниже. Используйте символ '-' перед любой командой для установки файлов с man-страницами для того, чтобы игнорировались все ошибки. Это нужно для того, чтобы можно было устанавливать программу на систему, в которой не установлена Unix-система man-документов. Для установки info-файлов необходимо скопировать их в $(infodir) с $(INSTALL_DATA) (см. Переменные для исполнения команд), и затем выполнить программу install-info (если она имеется). install-info - это скрипт, который выполняет редактирование файла 'dir' системы Info для того, чтобы добавить или обновить элемент меню для данного Info-файла; этот скрипт является частью пакета Texinfo. Далее приводится пример правила для установки Info-файла: $(infodir)/foo.info: foo.info # There may be a newer info file in . than in srcdir -if test -f foo.info; then d=.; \ else d=$(srcdir); i; $(INSTALL_DATA) $$d/foo.info $@; \ # Run install-info only if it exists. # Use 'if' instead of just prepending '-' to the # line so we notice real errors from install-info. # We use '$(SHELL) -c' because some shells do # fail gracefuly when there is an unknown command. if $(SHELL) -c 'install-info --version' \ >/dev/null 2>&1; then \ install-info --infodir=$(infodir) $$d/foo.info; \ else true; fi 'uninstall' Выполняется удаление всех установленных файлов, созданных при исполнении цели 'install' (но не тех файлов, которые создаются при исполнении цели 'all'). 'clean' Выполняется удаление тех файлов из текущего каталога, которые были созданы при построении программы. Не удаляются файлы, в которых сохранена конфигурация. Так же сохраняются файлы, которые могут быть получены при построении, но которые тем не менее входят в поставку. Следует удалять .dvi файлы, если они не являются частью поставки. 'distclean' Удаляются все файлы из текущего каталога, которые были созданы при конфигурировании или построении программы. Если Вы распакуете исходные тексты программ, после чего построите программу не создавая самостоятельно каких-либо файлов, то 'make distclean' должно оставить только те файлы, которые входили в поставку. 'mostyclean' Работает так же, как и clean, но оставляет неудаленными некоторые файлы, которые обычно нежелательно перекомпилировать. Например, цель 'mostyclean' для GCC не удаляет файл 'libgcc.a', поскольку его перекомпиляция редко когда бывает нужна, и к тому же занимает много времени. 'realclean' Выполняется удаление из текущего каталога всего, что может быть построено с помощью Makefile. Обычно это включает в себя все то, что удаляется по distclean, исходные файлы на C, полученные с помощью построителя синтаксических анализаторов Bison, таблицу тегов, info-файлы и т.д. Имеется одно исключение: 'make realclean' не должен удалять 'configure', даже если 'configure' может быть построен используя правило в Makefile. Более того, 'make realclean' не должен удалять ничего из того, чье существование требуется для выполнения 'configure' и начального исполнения программы. 'TAGS' Обновляет таблицу тегов для программы. 'info' Выполняется построение всех требуемых info-файлов. Лучший всего написать правила по следующему образцу: info: foo.info foo.info: foo.texi chap1.texi chap2.texi $(MAKEINFO) $(srcdir)/foo.texi Вы должны определить в Makefile переменную MAKEINFO. Она должна запускать программу makeinfo, которая входит в поставку пакета Texinfo. 'dvi' Выполняется построение DVI-файлов для всей TeXinfo-документации. Пример правил: dvi: foo.dvi foo.dvi: foo.texi chap1.texi chap2.texi $(TEXI2DVI) $(srcdir)/foo.texi Вы должны определить переменную TEXI2DVI в Makefile. Она должна запускать программу texi2dvi, которая является частью поставки пакета Texinfo. Можно указать просто зависимости, тогда GNU Make сам предоставит эту команду. 'dist' Выполняется создание tar-файла, содержащего дистрибутивную поставку этой программы. tar-файл должен быть создан таким образом, чтобы имена файлов в нем начинались с подкаталога, имя которого являлось бы именем поставляемого пакета. Имя может включать в себя номер версии. Например, поставка дистрибутивного архива для GCC версии 1.40 должна распаковываться в каталог с именем 'gcc-1.40'. Простейший способ выполнить это состоит в создании названного таким образом каталога, и использовании ln или cp для установки соответствующих файлов в него. После чего необходимо выполнить tar для этого подкаталога. Цель dist должна явно зависеть от всех файлов, которые не являются исходными, но должны входить в поставку, для того, чтобы убедиться в их актуальности. Смотри раздел "Издание версий". 'check' Выполняет самотестирование (если предусмотрено). Пользователь должен построить программу перед запуском тестов, но не должен устанавливать программу; Вы должны написать тесты таким образом, чтобы они работали когда программа построена, но не установлена. Следующие цели в тех программах, в которых они нужны, должны иметь следующие стандартные имена. 'installcheck' Выполняет проверку правильности установки (если соответствующие тесты предусмотрены). Пользователь должен построить и установить программу перед запуском этих тестов. Вы не должны считать, что $(bindir) входит в путь поиска программ. 'installdirs' Полезно добавить цель с именем 'installdirs' для создания структуры каталогов, в которых будут установлены файлы. Имеется скрипт, названный 'mkinstalldirs', который подходит для этой цели. Он находится в пакете Texinfo. Вы можете написать правило по следующему образцу: # Make sure all installation directories (e.g. $(bindir)) # actually exists by making them if necessary. installdirs: mkinstalldirs $(srcdir)/mkinstalldirs $(bindir) $(datadir) \ $(libdir) $(infodir) \ $(mandir) 5.4. Переменные для указания команд. Makefile должен предоставлять переменные для того, чтобы можно было перекрыть некоторые команды, опции и т.д. В частности, Вы должны запускать большинство утилит через переменные. Так, если Вы используете Bison, введите переменную с именем BISON, чье значение по умолчанию установлено как 'BISON=bison', и ссылайтесь на нее $(BISON) везде, где Вам нужно использовать Bison. Утилиты управления файлами (такие, как ln, rm, mv и т.д.) не должны выполняться через переменные, поскольку пользователям нет необходимости заменять их другими программами. Каждой переменной с именем программы должна соответствовать переменная с опциями, которые должны передаваться этой программе. Следует добавлять 'FLAGS' к имени переменной для программы, чтобы получить имя переменной для опций - например, BISONFLAGS. (Имя CFLAGS является исключением из этого правила, но мы сохраняем его, поскольку оно общепринято.) Используйте CPPFLAGS в любой команде компиляции, которая запускает препроцессор, и LDFLAGS в любой команде компиляции, которая выполняет редактирование связей, так же, как и при явном использовании ld. Если имеются опции C-компилятора, которые должны быть использованы для правильной компиляции некоторых файлов, не включайте их в CFLAGS. Пользователи ожидают, что они могут свободно установить CFLAGS сами. Вместо этого, упорядочите передачу таких опций компилятору независимо от CFLAGS, указывая их явно в командах компиляции или определяя неявное правило как здесь: CFLAGS = -g ALL_CFLAGS = -I. $(CFLAGS) .c.o: $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $< Не следует включать опцию -g в CFLAGS, поскольку она не требуется для правильной компиляции. Вы можете рассматривать это умолчание как рекоменуемое. Если пакет установлен так, что он компилируется с использованием GCC по умолчанию, то вы можете включить '-O' в умалчиваемое значение переменной CFLAGS. Помещайте CFLAGS последним в команде компиляции, после всех других переменных, содержащих опции компилятора, для того, чтобы пользователь мог использовать CFLAGS для перекрытия опций, указанных в других переменных. Каждый Makefile должен определять переменную INSTALL, которая обозначает бызовою команду для установки файла в системе. Каждый Makefile должен также определять переменные INSTALL_PROGRAM и INSTALL_DATA. (Умолчание для этих переменных должно быть $(INSTALL).) В дальнейшем, эти переменные должны использоваться для установки соответственно исполнимых и неисполнимых файлов. Используйте эти переменные как в примере: $(INSTALL_PROGRAM) foo $(bindir)/foo $(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a Следует всегда указывать в качестве второго аргумента имя файла (не имя каталога). Надо использовать отдельную команду для каждого устанавливаемого файла. 5.5. Переменные для каталогов Каталоги для установки должны всегда именоваться посредством переменных, поскольку это упрощает установку программы в нестандартное место. Стандартные имена для таких переменных следующие: 'prefix' Префикс, используемый для построения умолчательных значений для переменных, перечисленных ниже. Значение по умолчанию для переменной prefix должно быть '/usr/local' (по крайней мере сейчас). 'exec_prefix' Префикс, используемый при построении значений по умолчанию для некоторых переменных, перечисленных ниже. Значение по умолчанию для переменной exec_prefix должно быть $(prefix). Как правило, $(prefix) используется для каталогов, которые содержат машиннозависимые файлы (такие, как исполнимые файлы и библиотеки процедур), в то время как $(prefix) используется для остальных каталогов. 'bindir' Каталог для установки исполняемых файлов программ, которые могут быть запущены пользователем. Обычно, это '/usr/local/bin', но должно быть записано как '$(exec_prefix)/bin' 'libdir' Каталог для установки исполняемых файлов, которые будут запускаться другими программами, а не пользователем. Объектные файлы и библиотеки объектного кода должны так же попадать в этот каталог. Идея состоит в том, что этот каталог используется для файлов, которые зависят от конкретной архитектуры машины, но не должны находится в пути для команд. Значение для libdir обычно '/usr/local/lib', но должно быть записано как '$(exec_prefix)/lib'. 'datadir' Каталог для установки файлов с неизменяемыми данными, которые используются программами во время их работы. Этот каталог используется для файлов, которые не зависят от используемого типа машины. Значение этой переменной обычно '/usr/local/lib', но должно быть записано как '$(prefix)/lib'. 'statedir' Каталог для установки файлов с данными, которые программы могут изменять в процессе своей работы. Эти файлы должны быть независимыми от типа используемой машины, и должны допускать разделение их между машинами при сетевой установке. Значение этой переменной обычно '/usr/local/lib', но должно быть записано как '$(prefix)/lib' 'includedir' Каталог для установки заголовочных файлов (header-файлов), которые могут быть включены другими пользовательскими программами с помощью директивы препроцессора '#include'. Значение этой переменной обычно '/usr/local/include', но должно быть записано как '$prefix/include'. Большинство компиляторов отличных от GCC не выполняют поиск заголовочных файлов в '/usr/local/include', поэтому установка заголовочных файлов в этот каталог целесообразна только для GCC. Иногда это не представляет из себя проблему, так как некоторые библиотеки предназначены для использования исключительно с GCC. Но имеются так же и библиотеки, предназначенные для работы и с другими компиляторами. Они должны устанавливать свои включаемые файлы в два места, одно из которых определено переменной includedir, а другое - oldincludedir. 'oldincludedir' Каталог для установки заголовочных файлов для использования с компиляторами, отличными от GCC. Значение этой переменной обычно '/usr/include'. Команды Makefile должны проверить, не пусто ли значение переменной oldincludedir. Если оно пусто, они не должны пытаться использовать ее и выполнять повторную установку включаемых файлов. Пакет не должен замещать существующие заголовочные файлы в этом каталоге, в случае, если заголовочный файл пришел не из того же пакета. Так, если Ваш пакет Foo предоставляет заголовочный файл 'foo.h', то он должне установить заголовочный файл в каталог, заданный oldincludedir, если (1) foo.h не существует, или (2) foo.h существует и пришел из пакета Foo. Для того, чтобы проверить, что foo.h пришел из пакета Foo, поместите специальную строку в этот файл - часть комментария - и проверьте наличие этой строки с помощью команды grep. 'mandir' Каталог для установки man-страниц (если они есть) для этого пакета. Переменная должна включать суффикс для соответсвующей секции руководства - обычно '1' для утилит. Обычно значение этой переменной '/usr/local/man/man1', но должно быть записано как '$(prefix)/man/man1' 'man1dir' Каталог для установки в раздел 1 man-страниц. 'man2dir' Каталог для установки в раздел 2 man-страниц. Используйте переменные такого рода вместо 'mandir', если пакет должен устанавливать man-страницы более чем в один раздел. 'manext' Расширение для имени файла для устанавливаемых man-страниц. Переменная должна содержать точку, за которой следует соответствующая цифра, обычно '.1'. 'man1ext' Расширение имени файла для установки в раздел 1 man-страниц. 'man2ext' Расширение имени файла для установки в раздел 2 man-страниц. Используйте переменные такого рода вместо 'manext', если пакет должен устанавливать man-страницы более чем в один раздел. 'infodir' Переменная должна содержать имя каталога для установки info-файлов для данного пакета. По умолчанию, ее значение должно быть '/usr/local/info', но должно быть записано как '$(prefix)/info'. 'srcdir' В этой переменной должно находится имя каталога, содержащего компилируемые исходные тексты. Значение этой переменной обычно вставляется скриптом configure. Пример: # Common prefix for installation directories. # NOTE: This directory must exist when you start the install. prefix = /usr/local exec_prefix = $(prefix) # Where to put the executable for the command 'gcc'. bindir = $(exec_prefix)/bin # Where to put the directories used by the compiler. libdir = $(exec_prefix)/lib # Where to put the Info files. infodir = $(prefix)/info Если Ваша программа устанавливает большое количество файлов в один из стандартных каталогов, указанных пользователем, целесообразно сгруппировать их в один подкаталог, относящийся к этой программе. Если Вы делаете это, Вы должны создать такие подкаталоги в правиле для цели install. Не ожидайте от пользователя указания имени такого подкаталога в заданном им значении переменных, перечисленных выше. Наличие однообразного набора имен переменных для каталогов, в которые будет установлена программа, позволяет пользователю указать точно такие же значения для нескольких различных GNU-пакетов. Для того, чтобы это было полезным, все пакеты должны быть разработаны таким образом, чтобы они правильно использовали значения переменных. 6. Как должно выполняться конфигурирование. Каждая поставка поставка GNU-программы должна приходить со скриптом, имеющим имя configure. Этому скрипту передаются аргументы, которые описывают тип машины и системы, для которых Вы хотите построить эту программу. Скрипт configure должен сохранять опции конфигурации таким образом, чтобы они могли влиять на ход компиляции. Один из способов достичь этого состоит в создании ссылки с каким-либо стандартным именем (например, 'config.h') на конфигурационный файл, соответствующий выбранной системе. Если Вы используете этот подход, то Ваша поставка не должна содержать файла с именем 'config.h'. Это делается так, поскольку пользователь не должен иметь принципиальной возможности построить программу не выполнив ее предварительное конфигурирование. Идея другого способа состоит в том, что скрипт configure может выполнить редактировании Make-файла. Если Вы поступаете таким образом, то Ваша поставка не должна включать файл с именем 'Makefile'. Вместо него должен присутствовать файл 'Makefile.in', который содержит заготовку для редактирования. Опять же, это нужно для того, чтобы пользователи не имели возможность выполнить компиляцию до конфигурирования. Если Makefile формируется с помощью configure, то он должен иметь цель с именем Makefile, выполнение которой приводит к повторному запуску configure, восстанавливающему ту же конфигурацию, которая была установлена прежде. Файлы, читаемые configure, должны быть установлены как зависимости Makefile. Все файлы, которые генерируются скриптом configure, должны включать в начале комментарий, говорящий о том, что этот файл был сгенерирован автоматически с помощью configure. Так же следует предупредить пользователя, что ему не следует редактировать этот файл вручную, так как эти изменения будут потеряны. Скрипт configure должен формировать файл с именем 'config.status', описывающий конфигурацию, которая была установлена во время последнего конфигурирования. Этот файл должен быть скриптом shell, восстанавливающим в случае запуска эту самую конфигурацию. Скрипт configure должен воспринимать параметр вида '--srcdir=dirname', для указания каталога, в котором должны находиться исходные файлы (если этот каталог не является текущим. Это дает возможность строить программу в отдельном каталоге, не модифицируя при этом содержимое каталога с исходными файлами. Если пользователь не указал '--srcdir', то configure должен проверить каталоги '.' и '..', пытаясь найти там исходные файлы. Если он найдет исходные файлы в одном из этих мест, он должен использовать их оттуда. В противном случае необходимо сообщить, о том, что невозможно определить местонахождение исходных файлов, и прекратить выполнение с ненулевым кодом завершения. Обычно простейший способ поддержать опцию '--srcdir' состоит в редактировании определения переменной VPATH в Make-файле. В некоторых правилах должны, тем не менее, присутствовать явные ссылки на указанный каталог. Чтобы сделать это возможным, configure может добавить в Make-файл переменную с именем srcdir, чье значение полностью определяет имя каталога. Скрипт configure должен также уметь воспринимать аргумент, который определяет тип системы, для которой выполняется построение программы. Этот аргумент должен выглядеть так: cpu-company-system Например, тип Sun 3 может быть таким: 'm68k-sun-sunos4.1'. Скрипт configure должен уметь разбирать все в принципе возможные варианты описания машин. Так, 'sun3-sunos4.1' будет вполне допустимым синонимом, точно так же, как и 'sun3-bsd4.2', поскольку Sun OS в основе своей является BSD-системой, и нет других BSD-систем, доступных на Sun. Для многих программ, 'vax-dec-ultrix' должен быть синонимом лдя 'vax-dec-bsd', просто потому что различия между Unix и BSD редко когда существенны, но некоторые программы все-же должны различать их. Имеется скрипт 'config.sub', который может быть использован как подпрограмма для проверки заданного типа системы и канонизации возможных синонимов. Другие опции предназначены для точного указания наличия или отсутствия программного обеспечения или аппаратуры на машине, и для включения или исключения необязательных частей пакета. '--enable-feature[=parameter]' Сконфигурировать пакет для построения и установки необязательных возможностей уровня пользователя. Это позволяет пользователям выбирать набор дополнительных возможностей для включения. Указание в качестве необязательного параметра 'no' должно привести к тому, что данная возможность не будет включаться, в случае если она используется по умолчанию. Опция '--enable' не должна приводить к замене одной возможности на другую. Опция '--enable' не должна заменять один вариант функционирования на другой вариант. Единственное предназначение этой опции состоит в указании, должна или не должна включаться некоторая часть программы при построении. '--with-package' Скрипт configure настраивает Ваш пакет с тем, чтобы он использовал некоторый другой предустановленный пакет. Возможные значения package: 'x', 'x-toolkit', 'gnu-as' (или 'gas'), 'gnu-ld, 'gnu-libc', 'gdb'. Не надо использовать опцию --with для указания имен файлов. '--nfp' Целевая машина не имеет процессора для выполнения операций с плавающей точкой. '--gas' В качестве ассемблера целевой машины должен использоваться gas - GNU ассемблер. Эта опция устарела: пользователи должны использовать опцию '--with-gnu-as' вместо данной. '--x' На целевой машине имеется установленная система X Window. Эта опция устарела; пользователи должны использовать опцию '--with-x' вместо данной. Все конфигурирующие скрипты должны воспринимать каждую из этих уточняющих опций, вне зависимости от того, учитываются они при построении данного пакета или нет. В частности, они должны воспринимать любые опции, которые начинаются с '--with-' или '--enable-'. Это позволяет пользователям сконфигурировать дерево исходных текстов GNU целиком, с одним и тем же набором параметров. Вы можете заметить, что категории '--with-' и '--enable-' являются конкретными: они предоставляют место для любого типа опций, которые Вам могут понадобиться. Так и задумано. Мы хотим ограничить возможные опции, используемые при конфигурировании GNU-программ. Мы не хотим, чтобы GNU-программы имели раздражающие наборы опций конфигурирования. Пакеты, которые являются частью системы программирования, могут поддерживать кросс-компиляцию. В этом случае машина, на которой программа будет работать, и целевая машина могут быть различными. Скрипт configure по умолчанию должен подразумевать, что целевая система и система, на которой программа будет выполняться, совпадают. Полученная программа будет генерировать результат для использования его на той же машине, на которой она работает. Способ построения кросс-компилятора, кросс-ассемблера и т.д. состоит в указании опции '--host=hosttype' при запуске configure. Это позволяет указать тип хост-системы без изменения типа целевой системы. Синтаксис для hosttype был приведен выше. Перенос кросс-компилятора требует компиляции его на машине, отличной от той, на которой он будет выполняться. Пакеты-компиляторы должны воспринимать опцию конфигурации '--build=hosttype' для указания типа машины, на которой Вы будете компилировать пакет, если этот тип отличен от указанного в '--host'. Программы, для которых кросс-операции не имеют смысла, не должны воспринимать опцию '--host'. Некоторые программы умеют конфигурировать себя автоматически. Если Ваша программа делает это, то скрипт configure может игнорировать большинство из своих аргументов. 7. Использование языков, отличных от C. Использование языков, отличных от C - это как использование нестандартных возможностей: они создают трудности для пользователей. Даже если GCC поддерживает другие языки, пользователи могут посчитать неудобным устанавливать компилятор для этого языка только для того, чтобы построить Вашу программу. Поэтому, пожалуйста, пишите на C. Существует три исключения из этого правила: * Допустимо использовать специальный язык, если Ваша программа содержит интерпретатор этого языка. Так, не является проблемой то, что GNU Emacs содержит код, написанный на Emacs Lisp, поскольку Emacs поставляется с интерпретатором Lisp. * Допустимо использовать другой язык в инструменте, специально предназначенном для использования с этим языком. Это допустимо, поскольку пользователям, желающие построить соответствующий инструмент, все равно приходится так или иначе иметь соответствующий язык установленным. * Если приложение не представляется интересной широкому кругу людей, то по видимому это не важно, что приложение недостаточно удобно устанавливать. 8. Форматирование Вашего исходного кода. Важно помещать открывающую скобку, которая начинает тело функции на C в нулевой колонке, и избегать появления других открывающих или закрывающих скобок в нулевой колонке. Имеется несколько инструментов, которые просматривают исходный текст и ищую открывающую фигурную скобку в нулевой колонке для того, чтобы найти начало функции. Эти инструменты не будут работать с кодом, отформатированным без учета этого соглашения. Так же важно, чтобы в описании функции ее имя начиналось в колонке ноль. Это помогает людям искать определение функции, и может так же помочь некоторым инструментам распознать его. Таким образом, правильное оформление описания функции следующее: static char * concat (s1, s2) /* Name starts in column zero here */ char *s1, *s2; { /* Open brace in column zero here */ ... } В случае, если Вы хотите использовать ANSI C, оформляйте описание следующим образом: static char * concat (char *s1, char *s2) { ... } Для ANSI C, если список аргументов не помещается на одной строке, его следует разбивать, как указано в примере: int lots_of_args (int an_integer, long a_long, short a_short, double a_double, float a_float) ... Для тела функции, мы предпочитаем форматировать код, как указано ниже: if (x < foo (y, z)) haha = bar[4] + 5; else { while (z) { haha += foo (z, z); z--; } return ++x + bar (); } Мы считаем, что проще читать программу, когда она имеет пробелы перед открывающимися скобками и после запятых. Особенно после запятых. Когда Вы разрезаете выражение на несколько строк, разбивайте его перед оператором, а не после. Правильно делать так: if (foo_this_is_long && bar > win (x, y, z) && remaining_condition) Старайтесь избегать наличия двух операторов с разным приоритетом на одном уровне отступа. Например, не следует писать так: mode = (inmode[j] == VOIDmode || GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j) ? outmode[j] : inmode[j]); Вместо этого, используйте дополнительные скобки, чтобы отступ показывал вложенность: mode = ((inmode[j] == VOIDmode || (GET_MODE_SIZE (outmode[j]) > GET_MODE_SIZE (inmode[j]))) ? outmode[j] : inmode[j]); Вставляйте дополнительные скобки, для того, чтобы Emacs самостоятельно выравнивал код. Например, следующие отступы будут выглядеть хорошо, если Вы сделаете их руками, но Emacs может все испортить: v = rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000 + rup->ru_stime.tv_sec*1000 +rup->ru_stime.tv_usec/1000; Но добавление пары скобок решает эту проблему: v = (rup->ru_utime.tv_sec*1000 + rup->ru_utime.tv_usec/1000 + rup->ru_stime.tv_sec*1000 +rup->ru_stime.tv_usec/1000); Конструкцию do-while следует форматировать следующим образом: do { a = foo (a); } while (a > 0); Следует использовать символы перевода страницы (control-L) для того, чтобы разделить программу на страницы в логических местах (но не внутри функции). Не важно, как велика страница, поскольку они не должны попадать на печатные страницы. Символ перевода страницы должен быть единственным символом в строке. 9. Комментарии Каждая программа должна начинаться с комментария, кратко говорящего, для чего она предназначена. Например: 'fmt - filter for simple filling of text'. Пожалуйста, помещайте комментарий для каждой функции, говорящий, что эта функция делает, какие аргументы получает, какие значения аргументов допустимы и для чего она используется. Не нужно дублировать словами значение описания аргументов, если типы используются обычным образом. Если имеется что-либо нестандартное в использовании аргументов (например, аргумент типа char * указывает на адрес второго символа строки, а не первого), или не каждое возможное значение может быть передано как аргумент (например, строка, содержащая символ перехода на новую строку, может быть обработана неправильно), это надо обязательно оговорить. Объясните также смысл возвращаемого значения, если оно есть. Необходимо помещать два пробела в конце предложения в Вашем комментарии, для того, чтобы правильно функционировали команды Emacs, работающие с предложениями. Пишите предложение целиком, первое слово в предложении должно начинаться с большой буквы (если это слово не является идентификатором, начинающимся с маленькой буквы: изменение написания делает его другим идентификатором). Если Вы не хотите начинать предложение с маленькой буквы, перепишите предложение другим образом (например, 'The identifier lower-case is ...'). Комментарий к функции будет намного яснее, если Вы используете мнемоничные имена аргументов, которые говорят что-то об их значениях. Имя аргумента само по себе должно быть написано маленькими буквами, большие же буквы следует использовать, когда Вы говорите о значении, а не о самой переменной. Следует писать "the inode number NODE_NUM" вместо "an inode". Обычно не нужно повторять имя функции в комментарии, предшествующему ей, потому что читатель может увидеть его и так. Исключением является случай, когда комментарий настолько длинный, что описание функции не помещается с ним на одном экране. Следует писать комментарий для каждой static-переменной, например: /* Nonzero means truncate lines in the display; zero means continue them. */ int truncate_lines; Каждый '#endif' должен иметь комментарий (за исключением случаев коротких невложенных веток условий - на несколько строк). Комментарий должен описывать завершаемое условие, учитывая его смысл. '#else' должно также иметь комментарий, описывающий условие и смысл кода, который следует за ним. Например: #ifdef foo ... #else /* not foo */ ... #endif /* not foo */ Но для '#ifndef' комментарии по смыслу должны выглядеть так: #ifndef foo ... #else /* foo */ ... #endif /* foo */ 10. Ясность использования конструкция C. Следует явно описывать все аргументы функций. Не надо опускать их, поскольку их тип - int. Описания внешних функций и функций, которые появляются позднее в исходном файле должны все быть в одном месте в начале файла (где-нибудь до первого описания функции в этом файле), или должны размещаться в заголовочном файле. Не помещайте extern-описания внутри функций. Зачастую, одна и та же локальная переменная (с именем вроде tem) используется для разных целей внутри одной функции. Лучше, однако, описывать отдельную локальную переменную для каждого конкретного использования и давать ей имя в соответствии с ее смыслом. Это не только увеличивает понимаемость программы, но так же улучшает оптимизации, выполняемые хорошими компиляторами. Вы можете также переместить описание каждой локальной переменной в наименьший блок, включающий все ее использования. Это также делает программу яснее. Не следует использовать имена локальных переменных или параметров, которые затеняют (перекрывают) описания глобальных переменных. Не нужно описывать несколько переменных в одном описании, разбитом на несколько строк. Например, вместо: int foo, bar; надо писать или: int foo, bar; или: int foo; int bar; (Если это описания глобальных переменных, каждое из них должно иметь предшествующий комментарий.) Когда Вы пишите конструкцию if-else, которая вложена в другую конструкцию if, всегда следует помещать скобки вокруг if-else. Никогда не пишите так: if (foo) if (bar) win (); else lose (); Вместо этого надо писать так: if (foo) { if (bar) win (); else lose (); } Если Вы вкладываете конструкцию if внутрь конструкции else, то или пишите else if на одной строке, как здесь: if (foo) ... else if (bar) ... с then-частью, имеющей такой же отступ, как и у предшествующей then-части, или пишите вложенный if внутри фигурных скобок, как здесь: if (foo) ... else { if (bar) ... } Не описывайте тек структуры и переменную или typedef в одном описании. Вместо этого следует описывать тег структуры отдельно и затем использовать его для описания переменной или имени типа. Старайтесь избегать присваиваний внутри условия в конструкции if. Например, не следует писать так: if ((foo = (char *) malloc (sizeof *foo)) == 0) fatal ("virtual memory exhausted"); Вместо этого надо писать так: foo = (char *) malloc (sizeof *foo); if (foo == 0) fatal ("virtual memory exhausted"); Не стоит уродовать программу для того, чтобы потакать верификатору lint. Не надо вставлять никаких приведений к void. Ноль без приведения вполне хорош как константа для нулевого указателя. 11. Именование переменных и функций. Следует использовать символ подчеркивания для разделения слов в имени, чтобы команды работы со словами Emacs могли быть использованы здесь. Используйте в именах маленькие буквы; большие буквы следует использовать для макросов и enum-констант, а также в качестве префиксов для имен, следующих общим соглашениям. Например, Вы должны использовать имена типа ignore_space_change_flag; не надо использовать имена вроде iCantReadThis. Переменные, которые показывают, была ли использована та или иная опция командной строки, должны быть названы по смыслу опции, а не по соответствующей литере. Комментарий же должен определять как точный смысл опции, так и букву. Например: /* Ignore changes in horizontal whitespace (-b). */ int ignore_space_change_flag; Когда Вы хотите определить имена с константными целочисленными значениями, используйте enum вместо '#define'. GDB знает о константах, заданных перечислимыми типами. Используйте имена файлов длиной 14 символов или менее, для того, чтобы избежать создания никчемных проблем в SYSTEM V. Вы можете использовать программу doschk для проверки того, что это условие не нарушено. doschk выполняет также проверку на наличие потенциальных конфликтов в случае, если файлы будут помещены в файловую систему MS-DOS - Вас может в некоторых случаях заботить это. 12. Использование нестандартных возможностей. Существует несколько удобных GNU-расширений для соответствующих обычных Unix-возможностей. Использовать или нет эти расширения при реализации Вашей программы - сложный вопрос. С одной стороны, использование этих расширений может сделать программу более ясной. С другой стороны, люди не смогут построить программу, если другие GNU-инструменты будут недоступны. Это может привести к тому, что программа будет работать только на некоторых типах машин. Для некоторых расширений, без особых трудностей могут быть использованы оба варианта. Например, Вы можете определить функцию с "ключевым словом" INLINE, и определить его как макрорасширение, для того, чтобы оно было расшито как inline или как ничего, в зависимости от используемого компилятора. Вообще говоря, лучше не использовать расширения, если Вы можете без трудностей обойтись без них, и, напротив, следует использовать расширения, если они дают большие преимущества. Исключение из этого правила - большие, фундаментальные программы (такие, как GNU Emacs), которые работают на самых различных системах. Такие программы будут существенно испорчены использованием GNU-расширений. Другое исключение - это программы, которые используются как часть компилятора: все, что должно быть компилировано с помощью других компиляторов для того, чтобы "раскрутить" GNU-компиляторы. Если в этом случае требовать GNU-компилятор, то никто не сможет скомпилировать его, не имея его уже установленным. Это не есть хорошо. Поскольку большинство компьютерных систем не реализуют ANSI C, использование возможностей ANSI C аналогично использованию GNU-расширений: должны быть применены такие же соглашения. (Исключение составляют те ANSI-возможности, которые мы не поддерживаем, такие как trigraphs - не надо использовать их). 13. Вызов системных функций. Реализации языка C имеют значительные отличия. ANSI C уменьшает эту несовместимость, но не позволяет ее избежать полностью; между тем, многие пользователи желают компилировать программное обеспечение GNU с помощью компиляторов, реализующий до-ANSI версию языка C. Эта глава дает рекомендации, как использовать более или менее стандартные библиотечные функции C, чтобы избежать нежелательной потери переносимости. * Не используйте значение sprintf. Она возвращает количество выведенных символов на некоторых, но не на всех системах. * Не описывайте системные функции явно. Почти любое описание функции будет неверным в некоторой системе. Чтобы минимизировать конфликты, следует использовать системные заголовочные файлы для описания системных функций. Если заголовочный файл не описывает функцию, оставте ее неописанной. Хотя использование функции без описания и выглядит неаккуратным, на практике это работает хорошо для большинства систем, в которых такая ситуация возникает. Эта проблема только теоретическая. Напротив, наличие явного описания как правило приводит к конфликтам. * Если Вы должны описать системную функцию, не указывайте типы ее аргументов. Используйте описания в старом стиле, не ANSI-прототипы. Чем больше Вы укажете про функцию, тем скорее возникнет конфликт. * В частности, никогда не надо объявлять malloc или realloc. Большинство GNU-программ используют эти функции только однажды, в функциях, которые названы соответственно xmalloc и xrealloc. Эти функции вызывают malloc и realloc соответственно, и проверяют их результат. Поскольку xmalloc и xrealloc определены в Вашей программе, Вы можете описать их в других файлах без какой-либо опасности конфликта. На большинстве систем, int имеет такую же длину, как и указатель, поэтому вызовы malloc и realloc работают правильно. Для некоторых систем-исключений (большинство 64-разрядных машин), Вы можете использовать описания malloc и realloc - или поместить описания в конфигурационные файлы, специфичные для этих систем. * Строковые функции требуют специального использования. Некоторые Unix-системы имеют заголовочный файл 'string.h', некоторые - 'strings.h'. Ни то, ни другое имя файла не переносимо. Вы можете сделать две вещи: использовать Autoconf, чтобы определить, какой файл следует использовать, либо не включать ни один из этих файлов. * Если Вы не включаете ни один из этих файлов, Вы не можете получить описания для функций обработки строк обычным способом. Это приводит не к таким большим проблемам, как можно подумать. Новые строковые функции, согласованные с ANSI, выходят за наше рассмотрение, поскольку они не поддержаны на многих системах. Вы можете использовать следующие строковые функции: strcpy strncpy strcat strncat strlen strcmp strncmp strchr strrchr Функции копирования и конкатенации работают хорошо без описания, если не используются возвращаемые значения. Использование значений без описания не работает на системах, где ширина указателя отличается от ширины int, и в некоторых других случаях. Использования значения этих функций легко избежать. Функции сравнения и strlen нормально работают на большинстве систем, возможно на всех, на которых GNU-программы работают. Вы можете найти нужным объявить их на некоторых системах. Функции поиска должны быть описаны как возвращающие char *. К счастью, нет различий в возвращаемом типе. Однако, есть разница в именах этих функций. Некоторые системы дают этим функциям имена index и rindex, другие используют имена strchr и strrchr. Некоторые системы поддерживают и ту, и другую пару имен, но ни одна пара не работает на всех системах. Вы должны выбрать одну из пар имен и использовать ее во всей Вашей программе. (Лучше использовать strchr и strrchr). Опишите оба эти имени как функции, возвращающие char *. На системах, которые не поддерживают такие имена, определите их как макросы. Например, следующее можно поместить в начало Вашего файла или в файл заголовка, если Вы хотите использовать strchr и strrchr во всем остальном тексте: #ifndef HAVE_STRCHR #define strchr index #endif #ifndef HAVE_STRRCHR #define strrchr rindex #endif char *strchr (); char *strrchr (); Мы считаем, что HAVE_STRCHR и HAVE_STRRCHR - это макросы, определенные в системах, в которых соответствующие функции существуют. Один из способов правильно определить их состоит в использовании пакета Autoconf. 14. Ожидаемое поведение для произвольных программ. Избегайте неоправданных ограничений на длину или количество любых структур данных, включая имена файлов, строки, файлы, символы, отводя память для всех таких структур динамически. Во многих Unix-утилитах "длинные строки по-тихому обрезаются". Это неприемлемо для GNU-утилит. Программы, которые читают файлы, не должны выкидывать символы NUL, или какие-либо другие непечатные символы, включая те из них, которые имеют код, больший чем 0177. Исключение составляют утилиты, специально предназначенные для работы с некоторыми типами принтеров, которые не могут обрабатывать такие символы. Выполняйте проверку кода завершения для каждого системного вызова, кроме случаев, когда Вы уверены, что хотите игнорировать ошибки. Включайте текст системного сообщения об ошибке (из perror или эквивалентной функции) в каждое сообщение об ошибке, вызванное исполнением системного вызова с неуспешным кодом завершения, а так же имя файла (если ошибка связана с каким-то файлом) и имя программы. Сообщения вроде "cannot open foo.c" или "stat failed" недостаточно. Проверяйте каждый вызов malloc или realloc на возвращение нулевого результата. Проверяйте результат realloc даже в том случае, если Вы уменьшаете размер блока; в системах, которые округляют размеры блоков до степени 2, realloc может выделить новый блок, если Вы указали меньший размер. В Unix-системах realloc может разрушить блок памяти если он вернул нулевое значение. GNU-реализация realloc не допускает такой ошибки: если выполнение этой функции невозможно, то исходный блок остается неизменным. Можете считать, что эта ошибка исправлена. Если Вы желаете выполнять Вашу программу на Unix, и хотите избежать неприятностей в этом случае, воспользуйтесь GNU-malloc. Вы должны считать, что free изменяет содержимое блока, который освобождается. Все, что Вы хотите выбрать из блока, нужно выбирать до вызова free. Используйте getopt_long для разбора аргументов, конечно если синтаксис аргументов не делает использование этой функции нецелесообразным. Если Вы выполняете запись в статические переменные во время исполнения программы, используйте явный C-код для их инициализации. Оставьте описания с инициализатором для данных, которые не изменяются. Старайтесь избегать использования низкоуровневых интерфейсов, позволяющих добраться до структур данных Unix (таких, как структуры каталогов, utmp или разметку памяти ядра), поскольку вероятнее всего это не будет совместимым. Если Вам нужно найти все файлы в каталоге, используйте readdir или какой-то другой высокоуровневый интерфейс. Это позволит поддерживать совместимость с GNU. По умолчанию, GNU-система должна предоставлять функции обработки сигналов в BSD и в POSIX. GNU-программы должны писаться с учетом этого. В проверках на ошибки, которые обнаруживают невозможные условия, следует просто прерывать исполнение. Обычно здесь не надо печатать никаких сообщений. Эти проверки показывают наличие ошибок. Если кто-то захочет исправить ошибку, пусть он прочитает исходный текст и запустит отладчик. Объясните лучше проблему в комментариях к исходному тексту. Соответствующие данные должны быть переменных, которые можно просто просмотреть с помощью отладчика, так что не надо пересылать их куда-либо еще. 15. Формат сообщений об ошибках. Сообщение об ошибке, выдаваемое компилятором должно выглядеть так: имя-исх-файла:номер-строки: сообщение Сообщение об ошибке от других неинтерактивных программ должно выглядеть так: программа:имя-исх-файла:номер-строки: сообщение если ошибка относится к какому-либо исходному файлу, либо так: программа: сообщение если ошибка не может быть привязана к файлу. В интерактивные программы (те, которые получают команды с терминала), лучше не включать имя программы в сообщение об ошибке. То, что выполняется именно эта программа, показывается либо подсказкой, либо внешним видом экрана. (Когда та же самая программа запускается, получая вход из источника, отличного от терминала, она становится неинтерактивной и по-хорошему должна печатать сообщения об ошибках в неинтерактивном стиле). Строка сообщения не должна начинаться с большой буквы, если она следует за именем файла или программы. Не следует завершать ее точкой. 16. Поведение библиотек. Старайтесь делать библиотечные функции повторновходимыми. Если они нуждаются в отведении динамической памяти, то старайтесь, по крайней мере, не делать их неповторновходимыми вне собственно вызова malloc. Следуйте некоторым соглашениям по именованию для библиотек, чтобы избежать конфликта имен. Выберите префикс для имен в библиотеке, длина которого более чем два символа. Все внешние функции и имена переменных должны начинаться с этого префикса. Кроме того, в каждом члене библиотеки должно быть определено хотя бы один символ, начинающийся с этого префикса. Обычно стараются помещать определение одного символа в один исходный файл. Исключение может быть сделано для двух внешних символов, которые всегда используются совместно, и не может быть разумной программы, которая использует один из символов без другого. В этом случае оба они могут быть определены в одном и том же файле. Имена внешних символов, которые не документированы для пользователя, должны начинаться с литеры '_' и так же содержать выбранный для библиотеки префикс, чтобы избежать коллизий с другими библиотеками. Если Вы хотите, они могут определяться в одном файле с известными для пользователя точками входа. static-функции и переменные могут быть использованы так, как Вы пожелаете, и для них не надо следовать каким-либо соглашениям по именованию. 17. Переносимость, как она понимается в GNU Многое из того, что называется "переносимым" в мире Unix, зачастую оказывается переносимым только в разные версии Unix'а. Для GNU-программ это вторично, поскольку основной целью является исполнение их под одним и только одним ядром - GNU-ядром, и компилирование их одним и только одним компилятором с языка C - компилятором GNU C. Многообразие GNU-систем на различных процессорах должно быть таким же, как многообразие систем Berkley 4.3 на различных процессорах. На сегодняшний день все пользователи используют GNU-программы на не-GNU-системах. Поэтому поддержка различных не-GNU систем нужна, хоть и не является первостепенной. Простейший способ достичь переносимости на разумный диапазон систем состоит в использовании Autoconf. Едва ли Вашей программе потребуется иметь больше информации о машине, чем может дать Autoconf, хотя бы потому, что большинство программ, которым нужны такие знания, уже существуют. Трудно сейчас сказать точно, какие возможности будет предоставлять GNU-ядро, потому что его реализация еще не завершена. Однако, Вы можете спокойно использовать все то, что есть в 4.3, просто избегайте использования форматов полувнутренних быз данных (таких, как каталоги), когда можно воспользоваться высокоуровневой альтернативой (readdir). Вы можете спокойно использовать любые стандартные возможности языка C, библиотек или ядра, поскольку мы считаем необходимым поддержать их в полной GNU-системе, неважно есть она сейчас или нет. То, что существуют ядра или компиляторы C, не предоставляющие соответствующие возможности, не важно, покуда GNU-ядро и компилятор с языка C поддерживают их. Остается беспокоиться о различиях между различными типами процессоров, такими, как разница в порядке байтов и ограничения на выравнивания. Наврядли 16-битовые машины будут поддержаны GNU, так что не стоит задумываться о том, может ли целое быть шириной менее чем 32 разряда. Вы можете считать, что все указатели имеют одинаковый формат, независимо от типа, на который они указывают, и что это на самом деле целое. Есть некоторые странные машины, для которых это неверно, но они не так и важны; не тратте время угождая им. Кроме того, со временем мы поместим прототипы функций во все GNU-программы, и это, вероятно, приведет к тому, что Ваша программа будет работать даже на таких странных машинах. Поскольку некоторые важные для нас машины (включая 68000) имеют обратный порядок байтов, не следует считать, что адрес целочисленного объекта - это так же адрес и его наименее значащего байта. Не делайте такую ошибку: int c; ... while ((c = getchar()) != EOF) write(file_descriptor, &c, 1); Вы можете считать разумным использование мегабайта памяти. Не старайтесь уменьшать использование памяти, если Вы не подходите к этому барьеру. Если Ваша программа создает сложные структуры данных, стройте их в памяти, и выдайте фатальную ошибку, если malloc вернул ноль. Если программа работает по строкам, и она может быть применена к произвольному предоставленному пользователем входному файлу, она должна хранить в памяти только одно строку, поскольку это не сложно, и пользователи смогут обрабатывать файлы большего размера, чем может поместиться в оперативной памяти. 18. Стандарты для командной строки Не следует делать поведение утилиты зависящим от имени, использованного для ее вызова. Иногда бывает полезно сделать ссылки на утилиту с различными именами, но работа программы не должна зависеть от этого имени. Вместо этого следует использовать передачу опции программе при запуске, либо по разному ее компилировать, либо и то, и другое для выбора разных вариантов поведения программы. Точно так же, не надо делать поведение программы зависящим от типа используемого выходного устройства. Независимость от устройств является важным принципом разработки систем; не следует пренебрегать им только для того, чтобы уберечь кого-либо от необходимости задать лишнюю опцию. Если Вы считаете, что один вариант работы программы более полезен при выводе на терминал, а другой - при выводе в файл или конвейер, то обычно лучше сделать, чтобы умолчанию подразумевался вывод на терминал, и ввести опции для запроса других вариантов поведения программы. Соображения совместимости требуют, чтобы некоторые программы зависели от типа устройства вывода. Будет ужасно, если ls или sh не будут делать то, к чему привыкли и чего ожидают от них все пользователи. В некоторых из этих случаев, мы добавляем альтернативную версию утилит, которая не зависит от типа выходного устройства. Например, мы предоставляем программу dir, которая работает почти как ls, но всегда имеет по умолчанию многоколоночный формат вывода. Стоит следовать требованиям POSIX для опций командной строки в программах. Простейший способ сделать это состоит в использовании getopt для их разбора. Заметим, что GNU-версия getopt обычно допускает опции везде до тех пор, пока не встретится специальный аргумент '--'. Это не входит в требования POSIX и является GNU-расширением. Следует определять опции с длинными именами, которые эквивалентны однобуквенным опциям в стиле Unix. Мы считаем, что это делает GNU более дружественным. Проще всего удовлетворить это требование, воспользовавшись функцией GNU getopt_long. Одно из преимуществ опций с длинными именами состоит в том, что они могут быть последовательно согласованы в различных программах. Например, пользователь ожидает, что режим подробной выдачи информации о работе программы будет включаться в любой программе с помощью одной и той же опции, записываемой '--verbose'. Для достижения подобной совместимости загляните в таблицу длинных общих имен опций, когда будете выбирать названия опций в своей программе. Таблица приводится ниже. Если Вы используете имена, которые еще не присутствуют в этой таблице, пошлите их список по электронной почте по адресу 'gnu@prep.ai.mit.edu' для того, чтобы их можно было внести в эту таблицу. Обычно принято в качестве обычных аргументов программы задавать имена входных файлов; имена выходных файлов должны быть заданы с помощью опции (лучше всего выбрать для этого опцию -o). Даже если Вы разрешаете указывать имена выходных файлов как обычные аргументы в командной строке из соображений совместимости, все равно следует ввести дополнительно соответствующую опцию. Это приведет к большей согласованности различных GNU-утилит друг с другом, и упростит пользователям жизнь. Программы должны поддерживать опцию '--version', которая приводит к печати номера версии программы на стандартный вывод и последующему успешному завершению, и опцию '--help', которая печатает информацию об использовании опций на стандартный вывод и так же успешно завершает выполнение программы. При использовании этих опций не должна выполняться ничего, кроме печати запрошенной информации. 'auto-check' '-a' в 'recode'. 'auto-reference' '-A' в 'ptx'. 'after-date' '-N' в 'tar'. 'all' '-a' в 'du', 'ls', 'nm', 'stty', 'uname', и 'unexpand'. 'all-text' '-a' в 'diff'. 'almost-all' '-A' в 'ls'. 'append' '-a' в 'etags', 'tee', 'time'; '-r' в 'tar'. 'archive' '-a' в 'cp'. 'arglength' '-l' в 'm4'. 'ascii' '-a' в 'diff'. 'assume-new' '-W' в Make. 'assume-old' '-o' в Make. 'backward-search' '-B' в etags. 'batch' Используется в GDB. 'baud' Используется в GDB. 'before' '-b' в 'tac'. 'binary' '-b' в 'cpio' и 'diff'. 'block-size' используется в 'cpio' и 'tar'. 'blocks' '-b' в 'head' и 'tail'. 'break-file' '-b' в 'ptx'. 'brief' Используется в различных программах для того, чтобы уменьшить объем выдаваемой информации. 'bytes' '-c' в 'head', 'split', и 'tail'. 'c++' '-C' в 'etags'. 'catenate' '-A' в 'tar'. 'cd' Используется в различных программах для указания используемого каталога. 'changes' '-c' в 'chgrp' и 'chown'. 'classify' '-F' в 'ls'. 'colons' '-c' в 'recode'. 'command' '-c' в 'su'; '-x' в GDB. 'compare' '-d' в 'tar'. 'compress' '-Z' в 'tar'. 'concatenate' '-A' в 'tar'. 'confirmation' '-w' в 'tar'. 'context' Используется в 'diff'. 'copyright' '-C' в 'ptx' и 'recode'. 'core' Используется в GDB. 'count' '-q' в 'who'. 'count-links' '-l' в 'du'. 'create' Используется в 'tar' и 'cpio'. 'cxref' '-x' в 'etags'. 'date' '-d' в 'touch'. 'debug' '-d' в Make и 'm4'; '-t' в Bison. 'define' '-D' в 'm4'. 'defines' '-d' в Bison и 'etags'. 'delete' '-D' в 'tar'. 'dereference' '-L' в 'chgrp', 'chown', 'cpio', 'du', 'ls', и 'tar'. 'dereference-args' '-D' в 'du'. 'diacritics' '-d' в 'recode'. 'dictionary-order' '-d' в 'look'. 'diff' '-d' в 'tar'. 'digits' '-n' в 'csplit'. 'directory' Указывается каталог для использования, применяется в различных программах. В 'ls' это означает показать сам каталог, а не его содержимое. В 'rm' и 'ln' это означает, что не надо особым образом обрабатывать ссылки на каталоги. 'discard-all' '-x' в 'strip'. 'discard-locals' '-X' в 'strip'. 'diversions' '-N' в 'm4'. 'dry-run' '-n' в Make. 'ed' '-e' в 'diff'. 'elide-empty-files' '-z' в 'csplit'. 'entire-new-file' '-N' в 'diff'. 'environment-overrides' '-e' в Make. 'eof' '-e' в 'xargs'. 'epoch' Используется в GDB. 'error-limit' Используется в Makeinfo. 'error-output' '-o' в 'm4'. 'escape' '-b' в 'ls'. 'exclude-from' '-X' в 'tar'. 'exec' Используется в GDB. 'exit' '-x' в 'xargs'. 'expand-tabs' '-t' в 'diff'. 'expression' '-e' в 'sed'. 'extern-only' '-g' в 'nm'. 'extract' '-i' в 'cpio'; '-x' в 'tar'. 'faces' '-f' в 'finger'. 'fast' '-f' в 'su'. 'file' '-f' в 'info', Make, 'mt', и 'tar'; '-n' в 'sed'; '-r' в 'touch'. 'file-prefix' '-b' в Bison. 'file-type' '-F' в 'ls'. 'files-from' '-T' в 'tar'. 'fill-column' Используется в Makeinfo. 'flag-truncation' '-F' в 'ptx'. 'fixed-output-files' '-y' в Bison. 'follow' '-f' в 'tail'. 'footnote-style' Используется в Makeinfo. 'force' '-f' в 'cp', 'ln', 'mv', и 'rm'. 'format' Используется в 'ls', 'time', и 'ptx'. 'forward-search' '-F' в 'etags'. 'fullname' Используется в GDB. 'gap-size' '-g' в 'ptx'. 'get' '-x' в 'tar'. 'graphic' '-i' в 'ul'. 'graphics' '-g' в 'recode'. 'group' '-g' в 'install'. 'gzip' '-z' в 'tar'. 'hashsize' '-H' в 'm4'. 'header' '-h' в 'objdump' и 'recode' 'heading' '-H' в 'who'. 'help' Используется для запроса краткой информации по использованию. 'hide-control-chars' '-q' в 'ls'. 'idle' '-u' в 'who'. 'ifdef' '-D' в 'diff'. 'ignore' '-I' в 'ls'; '-x' в 'recode'. 'ignore-all-space' '-w' в 'diff'. 'ignore-backups' '-B' в 'ls'. 'ignore-blank-lines' '-B' в 'diff'. 'ignore-case' '-f' в 'look' и 'ptx'; '-i' в 'diff'. 'ignore-errors' '-i' в Make. 'ignore-file' '-i' в 'ptx'. 'ignore-indentation' '-S' в 'etags'. 'ignore-init-file' '-f' в Oleo. 'ignore-interrupts' '-i' в 'tee'. 'ignore-matching-lines' '-I' в 'diff'. 'ignore-space-change' '-b' в 'diff'. 'ignore-zeros' '-i' в 'tar'. 'include' '-i' в 'etags'; '-I' в 'm4'. 'include-dir' '-I' в Make. 'incremental' '-G' в 'tar'. 'info' '-i', '-l', и '-m' в Finger. 'initial' '-i' в 'expand'. 'initial-tab' '-T' в 'diff'. 'inode' '-i' в 'ls'. 'interactive' '-i' в 'cp', 'ln', 'mv', 'rm'; '-e' в 'm4'; '-p' в 'xargs'; '-w' в 'tar'. 'jobs' '-j' в Make. 'just-print' '-n' в Make. 'keep-going' '-k' в Make. 'keep-files' '-k' в 'csplit'. 'kilobytes' '-k' в 'du' и 'ls'. 'line-bytes' '-C' в 'split'. 'lines' Используется в 'split', 'head', и 'tail'. 'link' '-l' в 'cpio'. 'list' '-t' в 'cpio'; '-l' в 'recode'. 'list' '-t' в 'tar'. 'literal' '-N' в 'ls'. 'load-average' '-l' в Make. 'login' Используется в 'su'. 'machine' Список использующих программ не составлен. 'macro-name' '-M' в 'ptx'. 'mail' '-m' в 'hello' и 'uname'. 'make-directories' '-d' в 'cpio'. 'makefile' '-f' в Make. 'mapped' Используется в GDB. 'max-args' '-n' в 'xargs'. 'max-chars' '-n' в 'xargs'. 'max-lines' '-l' в 'xargs'. 'max-load' '-l' в Make. 'max-procs' '-P' в 'xargs'. 'mesg' '-T' в 'who'. 'message' '-T' в 'who'. 'minimal' '-d' в 'diff'. 'mode' '-m' вn 'install', 'mkdir', и 'mkfifo'. 'modification-time' '-m' в 'tar'. 'multi-volume' '-M' в 'tar'. 'name-prefix' '-a' в Bison. 'new-file' '-W' в Make. 'no-builtin-rules' '-r' в Make. 'no-create' '-c' в 'touch'. 'no-defines' '-D' в 'etags'. 'no-dereference' '-d' в 'cp'. 'no-keep-going' '-S' в Make. 'no-lines' '-l' в Bison. 'no-prof' '-e' в 'gprof'. 'no-sort' '-p' в 'nm'. 'no-split' Используется в Makeinfo. 'no-static' '-a' в 'gprof'. 'no-time' '-E' в 'gprof'. 'no-validate' Используется в Makeinfo. 'no-warn' Используется в различных программах для подавления вывода предупреждающих сообщений. 'node' '-n' в 'info'. 'nodename' '-n' в 'uname'. 'nonmatching' '-f' в 'cpio'. 'nstuff' '-n' в 'objdump'. 'null' '-0' в 'xargs'. 'number' '-n' в 'cat'. 'number-nonblank' '-b' в 'cat'. 'numeric-sort' '-n' в 'nm'. 'numeric-uid-gid' '-n' в 'cpio' и 'ls'. 'nx' Используется в GDB. 'old-archive' '-o' в 'tar'. 'old-file' '-o' в Make. 'one-file-system' '-l' в 'tar', 'cp', и 'du'. 'only-file' '-o' в 'ptx'. 'only-prof' '-f' в 'gprof'. 'only-time' '-F' в 'gprof'. 'output' В различных программах определяет имя выходного файла. 'override' '-o' в 'rm'. 'owner' '-o' в 'install'. 'paginate' '-l' в 'diff'. 'paragraph-indent' Используется в Makeinfo. 'parents' '-p' в 'mkdir' и 'rmdir'. 'pass-all' '-p' в 'ul'. 'pass-through' '-p' в 'cpio'. 'port' '-P' в 'finger'. 'portability' '-c' в 'cpio' и 'tar'. 'prefix-builtins' '-P' в 'm4'. 'prefix' '-f' в 'csplit'. 'preserve' Используется в 'tar' и 'cp'. 'preserve-environment' '-p' в 'su'. 'preserve-modification-time' '-m' в 'cpio'. 'preserve-order' '-s' в 'tar'. 'preserve-permissions' '-p' в 'tar'. 'print' '-l' в 'diff'. 'print-chars' '-L' в 'cmp'. 'print-data-base' '-p' в Make. 'print-directory' '-w' в Make. 'print-file-name' '-o' в 'nm'. 'print-symdefs' '-s' в 'nm'. 'question' '-q' в Make. 'quiet' Используется во многих программах для отключения вывода необязательных сообщений. Замечание: каждая программа, принимающая '--quiet', должна также принимать '--silent' как синоним. 'quote-name' '-Q' в 'ls'. 'rcs' '-n' в 'diff'. 'read-full-blocks' '-B' в 'tar'. 'readnow' Используется в GDB. 'recon' '-n' в Make. 'record-number' '-R' в 'tar'. 'recursive' Используется в 'chgrp', 'chown', 'cp', 'ls', 'diff', и 'rm'. 'reference-limit' Используется в Makeinfo. 'references' '-r' в 'ptx'. 'regex' '-r' в 'tac'. 'release' '-r' в 'uname'. 'relocation' '-r' в 'objdump'. 'rename' '-r' в 'cpio'. 'replace' '-i' в 'xargs'. 'report-identical-files' '-s' в 'diff'. 'reset-access-time' '-a' в 'cpio'. 'reverse' '-r' в 'ls' и 'nm'. 'reversed-ed' '-f' в 'diff'. 'right-side-defs' '-R' в 'ptx'. 'same-order' '-s' в 'tar'. 'same-permissions' '-p' в 'tar'. 'save' '-g' в 'stty'. 'se' Используется в GDB. 'sentence-regexp' '-S' в 'ptx'. 'separate-dirs' '-S' в 'du'. 'separator' '-s' в 'tac'. 'sequence' Используется 'recode' для выбора файлов или потоков для последовательной обработки. 'shell' '-s' в 'su'. 'show-all' '-A' в 'cat'. 'show-c-function' '-p' в 'diff'. 'show-ends' '-E' в 'cat'. 'show-function-line' '-F' в 'diff'. 'show-tabs' '-T' в 'cat'. 'silent' Используется во многих программах для отключения вывода необязательных сообщений. Замечание: каждая программа, принимающая '--silent', должна также принимать '--quiet' как синоним. 'size' '-s' в 'ls'. 'sort' Используется в 'ls'. 'sparse' '-S' в 'tar'. 'speed-large-files' '-H' в 'diff'. 'squeeze-blank' '-s' в 'cat'. 'starting-file' Используется в 'tar' и 'diff' для указания, с какого файла в каком каталоге следует начать обработку. 'stop' '-S' в Make. 'strict' '-s' в 'recode'. 'strip' '-s' в 'install'. 'strip-all' '-s' в 'strip'. 'strip-debug' '-S' в 'strip'. 'suffix' '-S' в 'cp', 'ln', 'mv'. 'suffix-format' '-b' в 'csplit'. 'sum' '-s' в 'gprof'. 'summarize' '-s' в 'du'. 'symbolic' '-s' в 'ln'. 'symbols' Используется в GDB и 'objdump'. 'synclines' '-s' в 'm4'. 'sysname' '-s' в 'uname'. 'tabs' '-t' в 'expand' и 'unexpand'. 'tabsize' '-T' в 'ls'. 'terminal' '-T' в 'tput' и 'ul'. 'text' '-a' в 'diff'. 'time' Используется в 'ls' и 'touch'. 'to-stdout' '-O' в 'tar'. 'total' '-c' в 'du'. 'touch' '-t' в Make, 'ranlib', и 'recode'. 'trace' '-t' в 'm4'. 'traditional' '-t' в 'hello'; '-G' в 'm4' и 'ptx'. 'tty' Используется в GDB. 'typedefs' '-t' в 'etags'. 'typedefs-and-c++' '-T' в 'etags'. 'typeset-mode' '-t' в 'ptx'. 'uncompress' '-z' в 'tar'. 'unconditional' '-u' в 'cpio'. 'undefine' '-U' в 'm4'. 'undefined-only' '-u' в 'nm'. 'update' '-u' в 'cp', 'etags', 'mv', 'tar'. 'verbose' Печатать дополнительную информацию о работе программы. Многие программы поддерживают эту опцию. 'verify' '-W' в 'tar'. 'version' Печать номера версии программы. 'version-control' '-V' в 'cp', 'ln', 'mv'. 'vgrind' '-v' в 'etags'. 'volume' '-V' в 'tar'. 'what-if' '-W' в Make. 'width' '-w' в 'ls' и 'ptx'. 'word-regexp' '-W' в 'ptx'. 'writable' '-T' в 'who'. 'zeros' '-z' в 'gprof'. 19. Документирование программ Для документирования программ следует использовать Texinfo. Посмотрите руководство по пакету Texinfo - напечатанное, или версию в info-подсистеме в среде GNU Emacs (C-h i). Посмотрите в качестве примера на существующие Texinfo-файлы (например, в каталоге man/ в поставке GNU Emacs). Титульный лист руководства должен содержать версию программы, для которой это руководство написано. Эта информация должна содержаться и в Top-узле руководства. Если руководство изменяется более часто или независимо от программы, укажите так же номер версии руководства и в том, и в другом месте. Руководство должно описывать все аргументы командной строки и все команды. Должны приводиться примеры их использования. Но не следует организовывать руководство как перечень возможностей. Лучше предварительно поместить концепции, которые пользователь должен постичь, прежде чем он дойдет до такого места в руководстве. Поставте перед пользователем цель, что он должен иметь в голове, и объясните, как он может достичь этого. Не стоит использовать man-страницы Unix как образец при написании GNU-документации; они являют собой плохой пример для подражания. Руководство должно иметь узел с именем 'program Invocation' or 'Invoking program', где program означает название описываемой программы, такое же, как вы набираете в shell'е, чтобы ее запустить. Этот узел (вместе со всеми подузлами, если они будут) должет описывать аргументы командной строки программы и как она должна запускаться (информация типа той, что обычно люди видят на man-страницах). Начните с '@example', который содержит шаблоны для всех опций и аргументов, которые программа использует. Можно также поместить элементы в некоторое меню для каждого такого шаблона. Если одно руководство описывает несколько программ, такой узел должен присутствовать для каждой описываемой программы. Кроме руководства, пакет должен содержать файл с именем 'NEWS', который содержит список видимых для пользователя и заслуживающих внимания изменений. Для каждой новой версии надо добавлять пункты в начало этого файла и указывать номер версии, к которой они принадлежат. Не уничтожайте старые записи, оставляйте их в файле следом за новыми. Это нужно для того, чтобы пользователь мог узнать, что нового он получил при переходе к более новой версии продукта. Если файл 'NEWS' становится слишком длинным, переместите некоторые старые записи в файл с именем ONEWS и поместите примечение в конце, которое ссылается на этот файл. Не надо использовать термин "pathname", часто встречающийся в Unix-документации; вместо этого используйте словосочетание "file name" Слово "path" мы используем только для путей поиска, которые из себя представляют списки из имен файлов. При желании, Вы можете приложить man-страницу к программе. Но имейте ввиду, что поддержка man-страницы требует непрерывных усилий при каждом изменении программы. То время, которое Вы проводите над man-страницей, можно использовать более полезно. Так что, даже если доброволец-пользователь подарит Вам man-страницу, Вы можете посчитать слишком дорогим принять ее. Если у Вас нет времени, может быть лучше отказаться от нее, если конечно этот доброволец не согласится принять на себя всю ответственность за ее поддержание - тогда Вы сможете полностью умыть руки от этого дела. Если доброволец перестанет выполнять эту работу, то не чуствуйте себя обязанным делать это самому; может оказаться лучшим изъять эту man-страницу до той поры, пока не найдется другой доброволец. Если же Вы считаете, что различия между man-страницей и документом будут незначительными, и она будет все равно оставаться полезной, поместите хорошо заметное примечание где-то в начале страницы, объясняющее, что Вы не поддерживаете ее, что Texinfo-руководство более полно, и поясняющее, как воспользоваться Texinfo-документацией. 20. Издание версии Упакуйте поставку пакета Foo версии 69.96 в сжатый с помощью gzip tar-файл с именем foo-69.96.tar.gz. Он должен распаковываться в подкаталог с именем foo-69.96. Процедура построения и установки программы никогда не должна изменять файлы, входящие в состав поставки. Это значит, что все файлы, образующие часть программы, должны быть разделены на исходные файлы и неисходные файлы. Исходные файлы пишутся человеком и никогда не изменяются автоматически; неисходные файлы получаются из исходных с помощью других программ под управлением Makefile. Естественно, все исходные файлы должны входить в поставку. Допустимо включать также и неисходные файлы, предполагая, что они актуальны и машинно-независимы, так что построение поставки никогда не будет изменять их. Мы обычно включаем все файлы, произведенные Bison, Lex, TeX и Makeinfo; это помогает избежать нежелательных зависимостей между нашими поставками, так что пользователи смогут установить почти любой пакет, который они захотят. В случае, если Вы распространяете неисходные файлы, необходимо перед созданием поставки убедиться в их актуальности, чтобы они не изменялись при построении и установке программы. Убедитесь, что каталог, в который распаковывается поставка (как и все его подкаталоги) доступен на запись для всей (восьмеричный режим доступа 777). Это нужно, чтобы с помощью старых версий tar, которые хранят хозяина файлов и права доступа в tar-архиве, непривилегированный пользователь мог извлечь все файлы. Убедитесь, что все файлы в поставке доступны на чтение для любого пользователя. Убедитесь, что в поставку не входят файлы с именами длиннее 14 символов. Точно так же, никакой файл, создаваемый при построении программы, не должен иметь длину имени более 14 символов. Причина состоит в том, что некоторые системы придерживаются "глупой" интерпретации стандарта POSIX, и не позволяют открывать файлы с длинными именами, не пытаясь даже просто обрезать их. Не включайте никаких символических ссылок в поставку. Если tar-файл содержит символические ссылки, то люди не смогут распаковать его на системе, которая не поддерживает символические ссылки. Не надо использовать несколько имен для одного файла в разных каталогах, поскольку некоторые файловые системы не могут обработать этого, что препятствует распаковке поставки. Постарайтесь добиться также, чтобы все имена файлов будут различны в MS-DOS. Имя в MS-DOS состоит из 8 символов, за которыми возможно следует символ '.' и расширение до 3 символов. MS-DOS урезает лишние символы, появляющиеся как до, так и после точки. Так, 'foobarhacker.c' и 'foobarhacker.o' непротиворечивы, поскольку они урезаются до 'foobarha.c' и 'foobarha.o' соответственно, а эти имена различны. Включайте в Вашу поставку копию 'texinfo.tex', которую Вы использовали для тестовой печатю любого '*.texinfo' файла. Аналогично, если Ваша программа использует небольшой программный пакет GNU, такой, как regex, getopt, obstack или termcap, включите его в файл поставки. Если Вы их не включите, то Вы сэкономите конечно немного места, зато включив их в поставку многим пользователям не придется задумываться, где раздобыть отсутствующие файлы. С О Д Е Р Ж А Н И Е 1. Общие положения относительно Ваших программ 2 2. Принятие "вкладов" 3 3. Журналы изменений 4 4. Совместимость с другими реализациями 6 5. Соглашения, касающиеся Make-файлов (Makefile) 7 5.1. Общие соглашения для Makefile 7 5.2. Использование утилит в Makefile. 8 5.3. Стандартные цели в Make 9 5.4. Переменные для указания команд. 14 5.5. Переменные для каталогов 15 6. Как должно выполняться конфигурирование. 19 7. Использование языков, отличных от C. 24 8. Форматирование Вашего исходного кода. 24 9. Комментарии 27 10. Ясность использования конструкция C. 29 11. Именование переменных и функций. 31 12. Использование нестандартных возможностей. 32 13. Вызов системных функций. 33 14. Ожидаемое поведение для произвольных программ. 36 15. Формат сообщений об ошибках. 38 16. Поведение библиотек. 39 17. Переносимость, как она понимается в GNU 40 18. Стандарты для командной строки 42 19. Документирование программ 67 20. Издание версии 69


Партнёры и спонсоры проекта:

Все материалы сайта распространяются по лицензии GNU/GPL
© ProUNIX 2003-2009, UnixLib 2005-2009, SoftLib 2006-2009.