source: trunk/doc/standards @ 18491

Revision 18491, 10.7 KB checked in by ghudson, 20 years ago (diff)
Fix inconsistency in shell script standards (case statements should use two-space indent, not a tab).
1This document describes guidelines for code which is to be
2incorporated into the Athena source tree.  Here are the areas covered:
4        Conventions for C code
5        Conventions for shell scripts
6        Software engineering practices
7        Symbol namespaces
8        Portability considerations for code we maintain
9        Portability considerations for code we release
10        Portability considerations for code we get from third parties
12Conventions for C code
15If you are working on an existing module which does not follow these
16conventions, be consistent with the existent source code rather than
17these conventions.
19You should follow the section on "Writing C" in the Gnu coding
20standards (available in emacs info under "standards"), with the
21following changes:
23        * When making a function call, do not put a space before the
24          open parenthesis.  Do put spaces before the open parenthesis
25          after "while", "if", or "for", though.
27        * When defining a function, do not drop to the next line for
28          the name of the function; put the function name on the same
29          line as the return type.
31        * Don't insert form feed characters into source files to
32          divide programs into pages.
34        * Ignore the sections on "Portability between System Types"
35          and "Portability between CPUs"; see the sections on
36          portability in this file instead.  Also ignore the section
37          on "Internationalization."
39        * In the section "Calling System Functions," ignore everything
40          about string functions.  These days you can assume that
41          using "#include <string.h>", strchr(), and strrchr() will
42          work on just about all platforms.
44You can use "M-x set-c-style" "GNU" in emacs to have emacs take care
45of the gnu-style indentation for you.  (This is the default on Athena
46if you haven't set it to something else in your .emacs.)  Remember to
47drop to the next line before each open brace.
49When writing return statements, do not put parentheses around the
50return value; just write "return foo;".  (This convention is not
51specified in the Gnu coding standards, but is consistent with the
52examples in the standards.)
54Put braces around the bodies of control constructs (if, while, for,
55do, switch) if they are more than one line long.
57Each C source file should begin with an MIT copyright statement, a
58comment describing what the file does, and a line:
60        static char rcsid[] = "$Id$";
62Each C header file should begin with:
64        /* $Id$ */
66followed by an MIT copyright statement and a comment describing what
67the file does.  MIT copyright statements may be omitted for source
68code which is not distributed outside of MIT.
70The preferred convention for comments is
72        /* comment text */
74for one line comments, and
76        /* comment text line 1
77         * comment text line 2
78         */
80for multi-line comments.  (To make "M-q" in Emacs leave the closing
81"*/" on a line by itself, set the variable c-hanging-comment-ender-p
82to nil.) As recommended in the Gnu coding standards, your comments
83should normally be complete, capitalized sentences ending with a
86Write prototypes for all of your functions.  Include argument names in
87your prototypes, to help the reader understand what they mean.
89Conventions for shell scripts
92Use /bin/sh to write shell scripts, not csh.
94Begin each shell script with:
96        #!/bin/sh
97        # $Id$
99followed by an MIT copyright if the shell script will be distributed
100outside of MIT.
102Use two spaces for indentation.  Avoid putting the result or
103alternative parts of an if statement on the same line as the "if" or
104"else".  Avoid creating lines longer than 79 characters.  Write case
105statements according to the format:
107        case expression in
108        pattern)
109          statements
110          ;;
111        esac
113Environment variable names should be in all capitals; shell variable
114names should be in all lowercase.  Avoid writing ${varname} for
115variable expansion except when necessary; just write $varname.  In
116most cases, if you intend to expand a variable as a single word, you
117should write "$varname" with double quotes.  This is not necessary in
118the switch expression of a case statement or in variable assignments,
121Test expressions in shell scripts can be a mine field of ungraceful
122failures if the variables being tested are empty, contain spaces or
123glob characters, or have a value of "!".  To handle all such corner
124cases gracefully without adding unnecessary hair, use the following
125test expressions:
127        To test if a variable is...     Write
128        ---------------------------     -----
129        empty                           -z "$varname"
130        non-empty                       -n "$varname"
131        equal to a literal              literal = "$varname"
132        equal to another variable       "x$varname" = "x$othervarname"
133        set                             "${varname+set}" = set
134        not set                         "${varname+set}" != set
136Software engineering practices
139The following software engineering practices are strongly encouraged:
141        * Restricting the operations which can access a given type of
142          data object, and grouping them together.
144        * Documenting data invariants (i.e. conditions on the
145          representations of data objects which are assumed to always
146          be true) and the meaning of data representations.
148        * Documenting non-obvious requirements and effects of
149          procedures.
151        * Use of prototypes for all functions.
153        * Automated testing of both program components ("unit
154          testing") and whole programs ("integration testing").
156The following software engineering practices are discouraged:
158        * Use of global variables.  Remember that the complexity of
159          any function's interface is increased by the global
160          variables it uses, even if that doesn't show up in the
161          function prototype.  Global variables are marginally
162          acceptable to represent configuration parameters determined
163          by command-line arguments or a configuration file, but even
164          then it is preferable to find an alternative.
166        * Use of global process state.  You should avoid using
167          alarm(), and you should avoid using getuid() or getenv()
168          inside libraries or other "deep" interfaces.  Code that uses
169          global process state tends to interact poorly with other
170          code in large programs.
172Symbol namespaces
175If you are writing a library, you should pick a prefix for your
176library.  You should ensure that all symbols which interact with the
177application's namespace (both at link time and on inclusion of the
178header file) begin with your prefix (or the all-caps form of your
179prefix, in the case of preprocessor symbols and enumeration constants)
180followed by an underscore.  Symbols which are not intended for use by
181the application should begin with your prefix followed by two
184For instance, if your prefix is "hes", then functions exported to the
185user should begin with "hes_" (e.g. "hes_resolve").  Functions used
186internally by the library should begin with "hes__"
187(e.g. "hes__resolve" for the internal resolver function).
188Preprocessor symbols intended for use by the application should begin
189with "HES_" (e.g. "HES_ER_OK"); preprocessor symbols not intended for
190use by the application should begin with "HES__" (e.g.
191"HES__HESIOD_H", used to protect hesiod.h from multiple
194Names of structures should begin with your prefix, but structure
195fields don't need to.  Strictly speaking, structure fields interact
196with the user's namespace because the user might have "#define"'d them
197to something before including your header file, but applications
198generally shouldn't be "#define"ing lots of lowercase symbols, so this
199is not a real worry.
201Portability considerations for code we maintain
204In general, your code should assume a POSIX-compliant and ANSI
205C-compliant development environment, except to work around specific
206bugs in platforms we care about.
208You should use Autoconf to handle portability concerns; see the file
209"build-system" in this directory for how a package in the athena
210hierarchy should use autoconf.  If you must perform an operating
211system test (because you are using a plain Makefile, typically for
212something in the packs hierarchy), do it in two steps; for instance:
214        #define HAVE_STRERROR
215        #if defined(sun) && !defined(SOLARIS)
216        /* SunOS 4.1.3_U1 doesn't have strerror().  Use sys_errlist
217         * instead. */
218        #undef HAVE_STRERROR
219        #endif
221        #ifndef HAVE_STRERROR
222        extern const char *const sys_errlist[];
223        #define strerror(x) (sys_errlist[x])
224        #endif
226This way, if the source tree is ever converted to use feature tests,
227the person porting the code will know exactly what needs to be tested
228for.  If you can anticipate the preprocessor symbol which would be
229used with Autoconf (as in this example), that's even better.  Note
230particularly the comment instead the operating system test; it should
233        * What special consideration is needed for that operating
234          system.
236        * The version number of the operating system for which this
237          approach was determined to be necessary.
239This will help future maintainers determine if one can eliminate the
240special consideration altogether when an OS upgrade has happened.
242Following is a list of appropriate preprocessor symbols to use to
243detect platforms we care about, when using plain Makefiles:
245        SunOS:          #if defined(sun) && !defined(SOLARIS)
246        Solaris:        #ifdef SOLARIS
247        IRIX            #ifdef sgi
248        Linux:          #ifdef linux
249        NetBSD:         #ifdef __NetBSD__
250                        or #include <sys/param.h> and #ifdef BSD4_4
251                        if applicable to all BSD 4.4 systems.
253SOLARIS is not automatically defined by the compiler on Solaris
254systems; we make sure it's defined when we build code not using
255autoconf.  There are no reliable automatically defined constants for
256Solaris systems.
258For some highly non-portable operations you may need to do a platform
259test even when using Autoconf.  In that case, use AC_CANONICAL_HOST
260rather than the above ifdefs.
262Portability considerations for code we release
265All of the standards in the previous section apply; however, we
266generally care about more platforms for code we release to the outside
267works.  It is discouraged, but acceptable, to care about platforms
268which are not POSIX-compliant.  Code that cares about such platforms
269should determine whether the platform supports POSIX interfaces by
270using AC_CHECK_HEADERS(unistd.h) to determine whether it can #include
271<unistd.h>, and then checking whether _POSIX_VERSION is defined.  Such
272code should always use POSIX interfaces rather than less portable
273interfaces when they are available.
275Portability considerations for code we get from third parties
278The overriding principle for code obtained from third parties is to
279make as few changes as possible.  A lot of third-party code has a very
280bad approach to portability, or cares about a lot of platforms we
281don't care about.  You should attempt to follow the portability
282approach used by the rest of the program, such as it may be.  Ideally,
283any changes you make should be made in such a manner that they can be
284incorporated into the source tree maintained by the third party.
Note: See TracBrowser for help on using the repository browser.