.\" @(#)cstyle.ms 1.8 96/08/19 SMI | |
.ND 96/08/19 | |
.RP | |
.TL | |
C Style and Coding Standards for SunOS | |
.br | |
.AU | |
Bill Shannon | |
.AI | |
Copyright \(co 1993 by Sun Microsystems, Inc. | |
.br | |
All rights reserved. | |
.AU | |
Version 1.8 of 96/08/19. | |
.\"-------------------- | |
.\" Footnote numbering | |
.ds f \\u\\n+f\\d | |
.nr f 0 1 | |
.ds F \\n+F. | |
.nr F 0 1 | |
.\"-------------------- | |
.AB | |
This document describes a set of coding standards and recommendations | |
that are local standards for programs written in C for the SunOS product. | |
The purpose of these standards is to facilitate sharing of each other's code, | |
as well as to | |
enable construction of tools (e.g., editors, formatters) | |
that, by incorporating knowledge of these standards, | |
can help the programmer in the preparation of programs. | |
.PP | |
This document is based on a similar document | |
written by L.W. Cannon, R.A. Elliott, L.W. Kirchhoff, J.H. Miller, | |
J.M. Milner, R.W. Mitze, E.P. Schan, N.O. Whittington at Bell Labs. | |
It also incorporates several items from a similar paper written by S. Shah. | |
The current version was derived from an earlier version written by the | |
author and Warren Teitelman. | |
.AE | |
.NH | |
Introduction | |
.LP | |
The scope of this document is the coding style used in writing | |
C programs for the SunOS product. | |
.ig xx | |
The purpose of the document is \fBnot\fP to establish a rigid style | |
to be imposed on everyone. | |
We fully expect many to disagree with, and possibly deviate from, some of | |
the standards set forth here. | |
The goal in writing down these standards is to allow | |
those who do not feel strongly about a particular stylistic issue | |
to adopt the style | |
most used by others, rather than simply adopting some random style of their | |
own. | |
.xx | |
To the extent that we do adhere to a common style, | |
it will be easier for several people to cooperate in the development of | |
the same program. | |
It also will facilitate understanding and maintaining | |
code developed by someone else. | |
.ig xx | |
Finally, it will enable the construction of tools that incorporate knowledge | |
of these standards | |
to help the programmer in the preparation of programs. | |
.xx | |
.LP | |
.ig xx | |
For certain style issues, such as number of spaces used for indentation and the | |
format of variable declarations, no clear consensus exists at Sun. | |
In these cases, we have documented the various styles that are most | |
frequently used. | |
However, we strongly recommend that within a particular project, and | |
certainly within a package or module, only one style be employed. | |
.xx | |
More important than the particular coding style used is \fIconsistency\fP | |
of coding style. | |
Within a particular module, package, or project, a consistent coding | |
style should be used throughout. | |
This is particularly important when modifying an existing program; | |
the modifications should be coded in the same style as the program | |
being modified, not in the programmer's personal style, nor necessarily | |
in the style advocated by this document. | |
.LP | |
This document discusses ANSI C only briefly, and C++ is hardly mentioned. | |
A future version of this document will discuss ANSI C more fully, | |
describing when to use such new features as \fLconst\fP. | |
In addition, rules for writing C code that must interact with C++ code, | |
and vice versa, will be covered. | |
Style rules for pure C++ programs will be left to a separate document. | |
.ig xx | |
.LP | |
To facilitate sharing each other's code in the presence of several | |
competing styles, the \fIindent\fP program, a C formatter, | |
has been extended to allow the user to specify which of the accepted styles | |
to use. | |
This allows an individual to take a C program written in a different style, | |
and convert it to a style he prefers. | |
.xx | |
.LP | |
Of necessity, these standards cannot cover all situations. | |
Experience and informed judgment count for much. | |
Inexperienced programmers who encounter unusual situations should | |
consult 1) code written by experienced C programmers following | |
these rules, or 2) experienced C programmers. | |
.NH | |
File Naming Conventions | |
.LP | |
.UX | |
requires certain suffix conventions for names of files to be processed | |
by the \fLcc\fP command\*f. | |
Other suffixes are simply conventions that we require. | |
The following suffixes are required: | |
.FS | |
.IP \*F | |
In addition to the suffix conventions given here, | |
it is conventional to use `Makefile' (not `makefile') for the | |
control file for \fImake\fP | |
and | |
`README' for a summary of the contents of a directory or directory | |
tree. | |
.FE | |
.IP \(bu | |
C source file names end in \fI.c\fP | |
.IP \(bu | |
Assembler source file names end in \fI.s\fP | |
.IP \(bu | |
Relocatable object file names end in \fI.o\fP | |
.IP \(bu | |
Include header file names end in \fI.h\fP | |
.IP \(bu | |
Archived library files end in \fI.a\fP | |
.IP \(bu | |
Fortran source file names end in \fI.f\fP | |
.IP \(bu | |
Pascal source file names end in \fI.p\fP | |
.IP \(bu | |
C-shell source file names end in \fI.csh\fP | |
.IP \(bu | |
Bourne shell source file names end in \fI.sh\fP | |
.IP \(bu | |
Yacc source file names end in \fI.y\fP | |
.IP \(bu | |
Lex source file names end in \fI.l\fP | |
.NH | |
Program Organization | |
.LP | |
The choices made in organizing a program often affect its understandability and ease of maintenance. | |
Many modern programming languages provide mechanisms | |
for defining the modules of a program. | |
Although C does not have these | |
language features, they can be approximated using the techniques and conventions described below. | |
.LP | |
A module is a group of procedures and data that together implement some abstraction, for example, a symbol table. | |
Those procedures and data that can be accessed by other modules are called \fIpublic\fP. | |
Procedures and data known only inside the module are called \fIprivate\fP. | |
Modules are defined using two files: an \fIinterface\fP file and an \fIimplementation\fP file. | |
The interface file contains the public definitions of the module. | |
The implementation file contains the private definitions and the code for the | |
procedures in the module. | |
.LP | |
In C, the role of an interface file is provided by a header file. As mentioned above, the name of a header file | |
ends in \fI.h\fP. The implementation file for a particular header file often has | |
the same root name, but ends in \fI.c\fP instead of \fI.h\fP. | |
Private definitions in the implementation file are declared to be \fLstatic\fP. | |
.LP | |
As an example, here are the interface and implementation files for a symbol | |
table package: | |
.sp | |
\fIsymtab.h\fP | |
.LS | |
.sp | |
/* | |
* Symbol table interface. | |
*/ | |
typedef struct symbol Symbol; | |
struct symbol { | |
char *name | |
int value; | |
symbol chain; | |
}; | |
Symbol *insert(); | |
Symbol *lookup(); | |
.LE | |
.sp 2 | |
\fIsymtab.c\fP | |
.LS | |
.sp | |
/* | |
* Symbol table implementation. | |
*/ | |
#include "symtab.h" | |
#define HASHTABLESIZE 101 | |
static Symbol hashtable[HASHTABLESIZE]; | |
static unsigned hash(); | |
.LE | |
.LS | |
/* | |
* insert(name) inserts "name" into the symbol table. | |
* It returns a pointer to the symbol table entry. | |
*/ | |
Symbol * | |
insert(name) | |
char *name; | |
{ | |
\&... | |
} | |
.LE | |
.LS | |
/* | |
* lookup(name) checks to see if name is in the symbol table. | |
* It returns a pointer to the symbol table entry if it | |
* exists or NULL if not. | |
*/ | |
Symbol * | |
lookup(name) | |
char *name; | |
{ | |
\&... | |
} | |
.LE | |
.LS | |
/* | |
* hash(name) is the hash function used by insert and lookup. | |
*/ | |
static unsigned | |
hash(name) | |
char *name; | |
{ | |
\&... | |
} | |
.LE | |
.LP | |
When the implementation of an abstraction is too large for a single | |
file, a similar technique is used. | |
There is an interface file, or in some cases, several | |
interface files, which contain the public definitions that clients of the | |
package will see, and a collection of implementation files. | |
If it is necessary for several implementation files to share definitions or | |
data that should not be seen by clients of the package, | |
these definitions are placed in a private header file shared by those files. | |
A convention for naming such header files is to append '_impl' | |
to the root name of | |
the public interface file, e.g., \fIsymtab_impl.h\fP, although this | |
convention isn't widely followed. | |
Generally speaking, such implementation header files should \fBnot\fP be | |
shipped to customers\*f. | |
.FS | |
.IP \*F | |
Traditionally many kernel implementation header files have been shipped, | |
for use by programs that read \fL/dev/kmem\fP. | |
Following this tradition for kernel header files is allowed but not required. | |
In other areas of the system, shipping implementation header files is strongly | |
discouraged. | |
.FE | |
.NH | |
File Organization | |
.LP | |
A file consists of various sections that should be separated by blank lines. | |
Although there is no maximum length requirement for source files, | |
files with more than about 3000 lines are cumbersome to deal with. | |
Lines longer than 80 columns are not handled well by all terminals | |
and should be avoided if possible\*f. | |
.FS | |
.IP \*F | |
Excessively long lines which result from deep indenting are often | |
a symptom of poorly organized code. | |
.FE | |
.sp | |
.LP | |
The suggested order of sections for a \fIheader\fP file is as follows | |
(see the Appendix for a detailed example): | |
.sp | |
.IP 1. | |
The first thing in the file should be a comment including the | |
copyright notice. | |
This comment might also describe the purpose of this file. | |
.IP 2. | |
The second thing in the file should be an \fL#ifndef\fP that checks | |
whether the header file has been previously included, and | |
if it has, ignores the rest of the file (see section 5). | |
.IP 3. | |
Next should be a \fL#pragma ident\fP line\*f. | |
.FS | |
.IP \*F | |
The form \fL#pragma ident\fP is preferred over the obsolete and less | |
portable form \fL#ident\fP. | |
.FE | |
.IP 4. | |
If this header file needs to include any other header files, the | |
\fL#include\fP statements should be next. | |
.IP 5. | |
Next should be a guard to allow the header file to be used by C++ programs. | |
.IP 6. | |
If it did not appear at the beginning of the file, next there | |
should be a block comment describing the contents of the file\*f. | |
.FS | |
.IP \*F | |
This comment sometimes appears between items 3 and 4. | |
Any of these locations is acceptable. | |
.FE | |
A description of the purpose of the objects in the files (whether | |
they be functions, external data declarations or definitions, or | |
something else) is more useful than just a list of the object names. | |
Keep the description short and to the point. | |
A list of authors and modification history is \fBnot\fP appropriate here. | |
.IP 7. | |
Any \fL#define\fPs that apply to the file as a whole are next. | |
.IP 8. | |
Any \fLtypedef\fPs are next. | |
.IP 9. | |
Next come the structure declarations. | |
If a set of \fL#define\fPs applies to a particular piece of global data | |
(such as a flags word), the \fL#define\fPs should be immediately after | |
the data declaration. | |
.IP 10. | |
Next come the global variable declarations\*f. | |
Declarations of global variables should use the \fLextern\fP keyword. | |
(Never declare static variables in a header file.) | |
.FS | |
.IP \*F | |
It should be noted that declaring variables in a header file | |
is often a poor idea. | |
Frequently it is a symptom of poor partitioning of code between files. | |
.FE | |
.IP 11. | |
Finally come the declarations of functions. | |
All external functions should be declared, even those that return \fLint\fP, | |
or do not return a value (declare them to return \fLvoid\fP). | |
.IP 12. | |
The end of the header should close with the match to the C++ guard and | |
the match to the multiple inclusion guard. | |
.sp | |
.LP | |
The suggested order of sections for a \fI.c\fP file (implementation file) is | |
roughly the same as a header file, eliminating unneeded constructs (such as | |
the multiple inclusion and C++ guards). | |
Don't forget the copyright notice and \fL#pragma ident\fP. | |
.LP | |
After all the declarations come the definitions of the procedures themselves, | |
each preceded by a block comment. | |
They should be in some sort of meaningful order. | |
Top-down is generally better than bottom-up\*f, | |
.FS | |
.IP \*F | |
Declaring all functions before any are defined allows the implementor | |
to use the top-down ordering without running afoul of the single pass C compilers. | |
.FE | |
and a breadth-first | |
approach (functions on a similar level of abstraction together) is | |
preferred over depth-first (functions defined as soon as possible | |
after their calls). | |
Considerable judgement is called for here. | |
If defining large numbers of essentially independent utility | |
functions, consider alphabetical order. | |
.NH | |
Header Files | |
.LP | |
Header files are files that are included in other files prior to compilation | |
by the C preprocessor. | |
Some are defined at the system level like <\fIstdio.h\fP> which must be included | |
by any program using the standard I/O library. | |
Header files are also used to contain data declarations and \fL#define\fPs | |
that are needed by more than one program\*f. | |
.FS | |
.IP \*F | |
Don't use absolute pathnames when including header files. | |
Use the \fI<name>\fP construction for getting them from a standard | |
place, or define them relative to the current directory. | |
The \-I option of the C compiler is the best way to handle | |
extensive private libraries of header files; it permits reorganizing | |
the directory structure without altering source files. | |
.FE | |
Header files should be functionally organized, | |
i.e., declarations for separate subsystems should be in separate header files. | |
Also, if a set of declarations is likely to change when code is | |
ported from one machine to another, those declarations should be | |
in a separate header file. | |
.LP | |
It is often convenient to be able to nest header files, for example, to | |
provide the user with a single header file which includes, in the right order, | |
a number of other header files needed for a particular application. | |
However, some objects like typedefs and initialized data | |
definitions cannot be | |
seen twice by the compiler in one compilation. | |
Therefore, to provide for the possibility | |
that two master header files will both include the same | |
header file, each header file should contain a check for whether it has | |
previously been included. The standard way of doing this is to define a | |
variable whose name consists of the name of the header file, including | |
any leading directories, but with characters that are otherwise illegal | |
in a variable name replaced with underscores, and with a leading underscore | |
added. | |
The entire header file is then bracketed in an \fL#ifndef\fP | |
statement which checks whether that variable has been defined. | |
For example, here | |
is a header file that would be referenced with | |
\fL#include <bool.h>\fP which defines the enumerated type \fLBool\fP: | |
.sp | |
\fIbool.h\fP | |
.LS | |
/* | |
* Copyright (c) 1993 by Sun Microsystems, Inc. | |
* All rights reserved. | |
*/ | |
#ifndef _BOOL_H | |
#define _BOOL_H | |
#pragma ident "%\&Z%%\&M% %\&I% %\&E% SMI" | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
typedef enum { FALSE = 0, TRUE = 1 } Bool; | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif /* _BOOL_H */ | |
.LE | |
.LP | |
It is a requirement that exported system header files be acceptable | |
to non-ANSI (K&R) C compilers, ANSI C compilers, and C++ compilers. | |
ANSI C defines the new keywords \fLconst\fP, \fLsigned\fP, and \fLvolatile\fP. | |
These keywords should only be used within ANSI C compatible parts of the | |
header file. | |
The biggest difference between ANSI C and K&R C is the ability to | |
declare the types of function parameters. | |
To handle both forms of declarations for external functions, the | |
following style should be used. | |
.LS | |
#if defined(__STDC__) | |
extern bool_t bool_from_string(char *); | |
extern char *bool_to_string(bool_t); | |
#else | |
extern bool_t bool_from_string(); | |
extern char *bool_to_string(); | |
#endif | |
.LE | |
.LP | |
Some people find it helpful to include the parameter types in comments | |
in the K&R form of the declaration. | |
.LS | |
extern bool_t bool_from_string(/* char * */); | |
extern char *bool_to_string(/* bool_t */); | |
.LE | |
While ANSI C allows you to use parameter names (as well as types) in | |
function declarations, and many people find that the names provide | |
useful documentation of the function parameters, the names must be chosen | |
extremely carefully. | |
A user's \fL#define\fP using the same name can render the function | |
declaration syntactically invalid. | |
.LP | |
The \fLextern "C"\fP guard is required in the header file for C++ compatibility. | |
In addition, the following C++ keywords should not be used in headers. | |
.LS | |
.TS | |
center; | |
l l l l l. | |
asm friend overload public throw | |
catch inline private template try | |
class new protected this virtual | |
delete operator | |
.TE | |
.LE | |
.LP | |
Note that there are many additional rules for header files that are | |
specified by various standards, such as ANSI C, POSIX, and XPG. | |
.NH | |
Indentation | |
.LP | |
.ig xx | |
Although both four-space and eight-space indentation are used at Sun, | |
eight-space indentation is generally preferred\*f. | |
.FS | |
.IP \*F | |
Four-space indentation may work better in the presence of long | |
identifier names. In any event, one style should be chosen | |
and used consistently throughout a program. | |
.FE | |
.xx | |
Only eight-space indentation should be used, and a tab should be used rather than | |
eight spaces. | |
If eight-space indentation causes the code to be too wide to fit in 80 | |
columns, it is often the case that the nesting structure is too complex | |
and the code would be clearer if it were rewritten. | |
The rules for how to indent particular C constructs such as | |
\fLif\fP statements, \fLfor\fP statements, \fLswitch\fP statements, etc., are described below in the | |
section on compound statements. | |
.ig xx | |
Note that the \fIindent\fP program can be instructed to use either | |
four- or eight-space indentation, and thus provides a way of converting between these formats. | |
.xx | |
.NH | |
Comments in C Programs | |
.LP | |
Comments should be used to give overviews of code and provide | |
additional information that isn't readily available in the code itself. | |
Comments should only contain information that is germane to reading | |
and understanding the program. | |
For example, information about how | |
the corresponding package is built or in what directory it should | |
reside should not be included as a comment in a source file. | |
Nor should comments include a list of authors or a modification history | |
for the file; this information belongs in the SCCS history. | |
Discussion of nontrivial design decisions | |
is appropriate, but avoid duplicating information that is present in | |
(and clear from) the code. | |
It's too easy for such redundant information to get out-of-date. | |
In general, avoid including in comments information that is likely | |
to become out-of-date. | |
.LP | |
Comments should \fBnot\fP be enclosed in large boxes drawn with asterisks | |
or other characters. | |
Comments should never include special characters, such as form-feed | |
and backspace. | |
.LP | |
There are three styles of comments: | |
block, single-line, and trailing. These are discussed next. | |
.NH 2 | |
Block Comments | |
.LP | |
The opening /* of a block comment that appears outside of any function | |
should be in column one. | |
There should be a * in column | |
2 before each line of text in the block comment, and the closing */ should be in columns 2-3 (so that the *'s line up). | |
This enables \fIgrep ^.\e*\fP to catch all of the block comments in a file. | |
There is never any text on the first or last lines of the block comment. | |
The initial text line is separated from the * by a single space, although later | |
text lines may be further indented, as appropriate. | |
.LS | |
/* | |
* Here is a block comment. | |
* The comment text should be spaced or tabbed over | |
* and the opening slash-star and closing star-slash | |
* should be alone on a line. | |
*/ | |
.LE | |
.LP | |
Block comments are used to provide English descriptions | |
of the contents of files, the functions of procedures, and to describe data structures and algorithms. | |
Block comments should be used at the beginning of each file and before each procedure. | |
The comment at the beginning of the file containing \fLmain()\fP should include a description of what the program does and | |
its command line syntax. | |
The comments at the beginning of other files should describe the contents | |
of those files. | |
.LP | |
The block comment that precedes each procedure should document its function, | |
input parameters, algorithm, and returned value. For example, | |
.LS | |
/* | |
* index(c, str) returns a pointer to the first occurrence of | |
* character c in string str, or NULL if c doesn't occur | |
* in the string. | |
*/ | |
.LE | |
.LP | |
In many cases, block comments inside a function are appropriate, and | |
they should be indented to the same indentation level as the code that | |
they describe. | |
.LP | |
Block comments should generally contain complete English sentences | |
and should follow the English rules for punctuation and capitalization. | |
The other types of comments described below will more often contain | |
sentence fragments, phrases, etc. | |
.NH 2 | |
Single-Line Comments | |
.LP | |
Short comments may appear on a single line indented over to the indentation level of the code that follows. | |
.LS | |
if (argc > 1) { | |
/* get input file from command line */ | |
if (freopen(argv[1], "r", stdin) == NULL) | |
error("can't open %s\en", argv[1]); | |
} | |
.LE | |
.LP | |
Two single-line comments can appear in a row if one line isn't enough, but | |
this is strongly discouraged. | |
The comment text should be separated from the opening /* and closing */ | |
by a space\*f. | |
.FS | |
.IP \*F | |
Except for the special lint comments \fL/*ARGSUSED*/\fP and | |
\fL/*VARARGS\fP\fIn\fP\fL*/\fP | |
(which should appear alone on a line | |
immediately preceding the function to which they apply), | |
and \fL/*NOTREACHED*/\fP, in which the spaces are not required. | |
.FE | |
The closing */'s of several adjacent single-line comments should \fBnot\fP | |
be forced to be aligned vertically. | |
In general, a block comment should be used when a single line is insufficient. | |
.ig xx | |
Note that \fIindent\fP will automatically convert a single-line | |
comment that can no longer fit in the available space on a line into a block comment, i.e., | |
it handles wrapping of comments. | |
.xx | |
.NH 2 | |
Trailing Comments | |
.LP | |
Very short comments may appear on the same line as the code they describe, | |
but should be tabbed over far enough to separate them from the statements. | |
If more than one short comment appears in a block of code, they should all | |
be tabbed to the same tab setting. | |
.ig xx | |
\fIIndent\fP allows the user to specify this | |
tab setting and will automatically position comments at the indicated place. | |
.xx | |
.LS | |
if (a == 2) | |
return (TRUE); /* special case */ | |
else | |
return (isprime(a)); /* works only for odd a */ | |
.LE | |
Trailing comments are most useful for documenting declarations. | |
Avoid the assembly language style of commenting every line of | |
executable code with a trailing comment. | |
.LP | |
Trailing comments are often also used on preprocessor \fL#else\fP and | |
\fL#endif\fP statements if they are far away from the corresponding test. | |
Occasionally, trailing comments will be used to match right braces with | |
the corresponding C statement, but this style is discouraged except in cases | |
where the corresponding statement is many pages away\*f. | |
.FS | |
.IP \*F | |
Many editors include a command to find a matching brace, making it easy | |
to navigate from the right brace back to the corresponding statement. | |
.FE | |
.NH | |
Declarations | |
.LP | |
There is considerable variation at Sun in the formatting of | |
declarations with regard to | |
the number of declarations per line, | |
and whether using tabs within declarations makes them more readable. | |
.NH 2 | |
How Many Declarations Per Line? | |
.LP | |
There is a weak consensus that one declaration per line is to be preferred, | |
because it encourages commenting. | |
In other words, | |
.LS | |
int level; /* indentation level */ | |
int size; /* size of symbol table */ | |
int lines; /* lines read from input */ | |
.LE | |
is preferred over: | |
.LS | |
int level, size, lines; | |
.LE | |
.LP | |
However, the latter style is frequently used, especially for declaration of several temporary variables of primitive types such as \fLint\fP or \fLchar\fP. | |
In no case should variables and functions be declared on the same line, e.g., | |
.LS | |
long dbaddr, get_dbaddr(); /* WRONG */ | |
.LE | |
.NH 2 | |
Indentation Within Declarations | |
.LP | |
Many programmers at Sun like to insert tabs in their variable declarations to | |
align the variables. They feel this makes their code more readable. | |
For example: | |
.LS | |
int x; | |
extern int y; | |
register int count; | |
char **pointer_to_string; | |
.LE | |
.LP | |
.ig xx | |
\fIIndent\fP allows the user to specify the number of columns between the | |
beginning of a declaration and the variable name. In the above example, | |
this number is 16. If the key words in the declaration require more than | |
this number of characters, then \fIindent\fP simply inserts a | |
space between the last key word and the variable name, e.g., | |
.xx | |
If variables with long type names are declared along with variables | |
with short type names, it may be best to indent the variables one | |
or two tab stops, and only use a single space after the long type names, e.g., | |
.LS | |
int x, y, z; | |
extern int i; | |
register int count; | |
struct very_long_name *p; | |
.LE | |
.LP | |
.ig xx | |
Note that setting this parameter to 0 produces the following format, which | |
is also acceptable: | |
.xx | |
It is also acceptable to use only a single space between the type name | |
and the variable name: | |
.LS | |
int x; | |
char c; | |
enum rainbow y; | |
.LE | |
.LP | |
There is no Sun standard regarding how far over to indent variable names. | |
The user should choose a value that looks pleasing to him, taking | |
into consideration how frequently he employs lengthy declarations, | |
but note that declarations such as the following probably make the code | |
\fBharder\fP to read: | |
.LS | |
struct very_long_structure_name *p; | |
struct another_very_long_structure_name *q; | |
char *s; | |
int i; | |
short r; | |
.LE | |
.LP | |
Note that the use of \fL#define\fP to declare constants and macros | |
follows indentation rules similar to those for other declarations. | |
In particular, the \fL#define\fP, the macro name, and the macro text should | |
all be separated from each other by tabs and properly aligned. | |
.NH 2 | |
Local Declarations | |
.LP | |
Do not declare the same variable name in an inner block\*f. | |
.FS | |
.IP \*F | |
In fact, avoid any local declarations that override declarations | |
at higher levels. | |
.FE | |
For example, | |
.LS | |
func() | |
{ | |
int cnt; | |
\&... | |
if (condition) { | |
register int cnt; /* WRONG */ | |
\&... | |
} | |
\&... | |
} | |
.LE | |
.LP | |
Even though this is valid C, the potential confusion is enough | |
that | |
\fIlint\fP will complain about it when given the \fB\-h\fP option. | |
.NH 2 | |
External Declarations | |
.LP | |
External declarations should begin in column 1. | |
Each declaration should be on a separate line. | |
A comment describing the role of the object being declared should be | |
included, with the exception that a list of defined constants does not | |
need comments if the constant names are sufficient documentation. | |
The comments should be tabbed so that they line up underneath each | |
other\*f. | |
.FS | |
.IP \*F | |
So should the constant names and their defined values. | |
.FE | |
Use the tab character rather than blanks. | |
For structure and union template declarations, each element should be alone | |
on a line with a comment describing it. | |
The opening brace (\ {\ ) should be on the same line as the structure | |
tag, and the closing brace should be alone on a line in column 1, e.g., | |
.LS | |
struct boat { | |
int wllength; /* water line length in feet */ | |
int type; /* see below */ | |
long sarea; /* sail area in square feet */ | |
}; | |
/* | |
* defines for boat.type | |
*/ | |
#define KETCH 1 | |
#define YAWL 2 | |
#define SLOOP 3 | |
#define SQRIG 4 | |
#define MOTOR 5 | |
.LE | |
.LP | |
In any file which is part of a larger whole rather than a self-contained | |
program, maximum use should be made of the \fLstatic\fP keyword to make | |
functions and variables local to single files. | |
Variables in particular should be accessible from other files | |
only when there is a clear | |
need that cannot be filled in another way. | |
Such usages should be commented to make it clear that another file's | |
variables are being used. | |
.NH 2 | |
Function Definitions | |
.LP | |
Each function should be preceded by a block comment prologue that gives | |
the name and a short description of what the function does. | |
The type of the value returned should | |
be alone on a line in column 1 (\fLint\fP should be specified explicitly). | |
If the function does not return a value then it should be given | |
the return type \fLvoid\fP. | |
If the value returned requires a long explanation, it should be given | |
in the prologue. | |
Functions that are not used outside of the file in which they are | |
declared should be declared as \fLstatic\fP. | |
This lets the reader know explicitly that a function is private, | |
and also eliminates the possibility of name conflicts with variables | |
and procedures in other files. | |
.LP | |
When defining functions using the old K&R syntax, | |
the function name and formal parameters should be alone on a line | |
beginning in column 1. | |
This line is followed by the declarations of the formal parameters. | |
Each parameter should be declared (do not default to \fLint\fP), | |
and tabbed over one indentation level. | |
The opening brace of the function body should also be alone on a line | |
beginning in column 1. | |
.LP | |
For functions defined using the ANSI C syntax, the style is much the same. | |
The examples below illustrate the acceptable forms of ANSI C declarations. | |
.LP | |
All local declarations and code within the function body should be | |
tabbed over at least one tab. | |
(Labels may appear in column 1.) | |
If the function uses any external variables or functions that are not | |
otherwise declared \fLextern\fP, these should have their | |
own declarations in the function body using the \fLextern\fP keyword. | |
If the external variable is an array, the array bounds must be repeated | |
in the \fLextern\fP declaration. | |
.LP | |
If an external variable or a parameter of type pointer is | |
changed by the function, that fact should be noted in the comment. | |
.LP | |
All comments about parameters and local variables should be | |
tabbed so that they line up underneath each other. | |
The declarations should be separated from the function's statements | |
by a blank line. | |
.LP | |
The following examples illustrate many of the rules for function definitions. | |
.LP | |
.LS | |
/* | |
* sky_is_blue() | |
* | |
* Return true if the sky is blue, else false. | |
*/ | |
int | |
sky_is_blue() | |
{ | |
extern int hour; | |
if (hour < MORNING || hour > EVENING) | |
return (0); /* black */ | |
else | |
return (1); /* blue */ | |
} | |
.LE | |
.LS | |
/* | |
* tail(nodep) | |
* | |
* Find the last element in the linked list | |
* pointed to by nodep and return a pointer to it. | |
*/ | |
Node * | |
tail(nodep) | |
Node *nodep; | |
{ | |
register Node *np; /* current pointer advances to NULL */ | |
register Node *lp; /* last pointer follows np */ | |
np = lp = nodep; | |
while ((np = np->next) != NULL) | |
lp = np; | |
return (lp); | |
} | |
.LE | |
.LS | |
/* | |
* ANSI C Form 1. | |
* Use this form when the arguments easily fit on one line, | |
* and no per-argument comments are needed. | |
*/ | |
int | |
foo(int alpha, char *beta, struct bar gamma) | |
{ | |
\&... | |
} | |
.LE | |
.LS | |
/* | |
* ANSI C Form 2. | |
* This is a variation on form 1, using the standard continuation | |
* line technique (indent by 4 spaces). Use this form when no | |
* per-argument comments are needed, but all argument declarations | |
* won't fit on one line. This form is generally frowned upon, | |
* but acceptable. | |
*/ | |
int | |
foo(int alpha, char *beta, | |
struct bar gamma) | |
{ | |
\&... | |
} | |
.LE | |
.LS | |
/* | |
* ANSI C Form 3. | |
* Use this form when per-argument comments are needed. | |
* Note that each line of arguments is indented by a full | |
* tab stop. Note carefully the placement of the left | |
* and right parentheses. | |
*/ | |
int | |
foo( | |
int alpha, /* first arg */ | |
char *beta, /* arg with a vert long comment needed */ | |
/* to describe its purpose */ | |
struct bar gamma) /* big arg */ | |
{ | |
\&... | |
} | |
.LE | |
.NH 2 | |
Type Declarations | |
.LP | |
Many programmers at Sun use named types, i.e., \fLtypedef\fPs, liberally. | |
They feel that the use of \fLtypedef\fPs simplifies declaration lists and | |
can make program modification easier when types must change. | |
Other programmers feel that the use of | |
a \fLtypedef\fP hides the underlying type when they want to know what the type is. | |
This is particularly | |
true for programmers who need to be concerned with efficiency, e.g., kernel programmers, | |
and therefore need to be aware of the implementation details. | |
The choice of whether or not to use \fLtypedef\fPs is left to the implementor. | |
.LP | |
It should be noted, however, that \fLtypedef\fPs should be used to isolate | |
implementation details, rather than to save keystrokes\*f. | |
.FS | |
.IP \*F | |
An exception is made in the cases of \fLu_char\fP, \fLu_short\fP, etc. | |
that are defined in <\fIsys/types.h\fP>. | |
.FE | |
For instance, the following example demonstrates two inappropriate uses | |
of \fLtypedef\fP. In both cases, the code would pass \fIlint\fP\*f, | |
.FS | |
.IP \*F | |
Some people claim that this is a bug in \fIlint\fP. | |
.FE | |
but nevertheless depends on the underlying types | |
and would break if these were to change: | |
.LS | |
typedef char *Mything1; /* These typedefs are inappropriately */ | |
typedef int Mything2; /* used in the code that follows. */ | |
int | |
can_read(t1) | |
Mything1 t1; | |
{ | |
Mything2 t2; | |
t2 = access(t1, R_OK); /* access() expects a (char *) */ | |
if (t2 == -1) /* and returns an (int) */ | |
takeaction(); | |
return (t2); /* can_read() returns an (int) */ | |
} | |
.LE | |
.LP | |
If one elects to use a \fLtypedef\fP in conjunction with a pointer type, | |
the underlying type should be \fLtypedef\fP-ed, | |
rather than \fLtypedef\fP-ing a pointer to underlying type, because it is often necessary and | |
usually helpful to be able to tell if a type is a pointer. | |
Thus, in the example in section 3, \fLSymbol\fP is defined to be a \fLstruct symbol\fP, | |
rather than \fLstruct symbol *\fP, and \fLinsert()\fP defined to return \fLSymbol *\fP. | |
.NH | |
Statements | |
.LP | |
Each line should contain at most one statement. | |
In particular, do not use the comma operator to group multiple | |
statements on one line, or to avoid using braces. | |
For example, | |
.LS | |
argv++; argc--; /* WRONG */ | |
if (err) | |
fprintf(stderr, "error"), exit(1); /* VERY WRONG */ | |
.LE | |
.LP | |
Do not nest the ternary conditional operator (?:). | |
For example: | |
.LS | |
num = cnt < tcnt ? (cnt < fcnt ? fcnt : cnt) : | |
tcnt < bcnt ? tcnt : bcnt > fcnt ? fcnt : bcnt; /* WRONG */ | |
.LE | |
.LP | |
If the \fLreturn\fP statement is used to return a value, the expression | |
should always be enclosed in parentheses. | |
.LP | |
Functions that return no value should \fBnot\fP include a \fLreturn\fP | |
statement as the last statement in the function. | |
.NH 2 | |
Compound Statements | |
.LP | |
Compound statements are statements that contain lists of statements | |
enclosed in {} braces. | |
The enclosed list should be indented one more level than the compound statement itself. | |
The opening left brace should be at the end of the line beginning the | |
compound statement and the closing right brace should be alone on a | |
line, positioned under the beginning of the compound statement. (See examples below.) | |
Note that the left brace that begins a function body is the only occurrence | |
of a left brace which should be alone on a line. | |
.LP | |
Braces are also used around a single statement when it is part of a control structure, such as an \fLif-else\fP or \fLfor\fP statement, as in: | |
.LS | |
if (condition) { | |
if (other_condition) | |
statement; | |
} | |
.LE | |
.LP | |
Some programmers feel that braces should be used to surround \fBall\fP | |
statements that are part of control structures, even singletons, | |
because this makes it easier to add or delete statements without thinking about | |
whether braces should be added or removed\*f. | |
.FS | |
.IP \*F | |
Some programmers reason that, since some apparent function calls might | |
actually be macros that expand into multiple statements, always using | |
braces allows such macros to always work safely. | |
Instead, we strongly discourage the use of such macros. | |
If such macros must be used, they should be all upper case so as to clearly | |
distinguish them as macros; see the Naming Conventions section. | |
.FE | |
Thus, they would write: | |
.LS | |
if (condition) { | |
return (0); | |
} | |
.LE | |
.LP | |
In either case, if one arm of an \fLif-else\fP statement contains | |
braces, all arms should contain braces. | |
Also, if the body of a \fLfor\fP or \fLwhile\fP loop is empty, | |
no braces are needed: | |
.LS | |
while (*p++ != c) | |
; | |
.LE | |
.NH 2 | |
Examples | |
.LP | |
\fBif, if-else, if-else if-else statements\fP | |
.LS | |
if (condition) { | |
statements; | |
} | |
.LE | |
.LS | |
if (condition) { | |
statements; | |
} else { | |
statements; | |
} | |
.LE | |
.LS | |
if (condition) { | |
statements; | |
} else if (condition) { | |
statements; | |
} | |
.LE | |
.LP | |
Note that the right brace before the \fLelse\fP and the right brace | |
before the \fLwhile\fP of a \fLdo-while\fP statement (see below) are the | |
only places where a right brace appears that is not alone on a line. | |
.KS | |
.LP | |
\fBfor statements\fP | |
.LS no | |
for (initialization; condition; update) { | |
statements; | |
} | |
.LE | |
.LP | |
When using the comma operator in the initialization or update clauses | |
of a \fLfor\fP statement, it is suggested that no more than three variables | |
should be updated. | |
More than this tends to make the expression too complex. | |
In this case it is generally better to use separate statements outside | |
the \fLfor\fP loop (for the initialization clause), or at the end of | |
the loop (for the update clause). | |
.LP | |
The infinite loop is written using a \fLfor\fP loop. | |
.LS no | |
for (;;) { | |
statements; | |
} | |
.LE | |
.KE | |
.KS | |
.LP | |
\fBwhile statements\fP | |
.LS no | |
while (condition) { | |
statements; | |
} | |
.LE | |
.KE | |
.KS | |
.LP | |
\fBdo-while statements\fP | |
.LS no | |
do { | |
statements; | |
} while (condition); | |
.LE | |
.KE | |
.KS | |
.LP | |
\fBswitch statements\fP | |
.LS no | |
switch (condition) { | |
case ABC: | |
case DEF: | |
statements; | |
break; | |
case XYZ: | |
statements; | |
break; | |
default: | |
statements; | |
break; | |
} | |
.LE | |
.KE | |
.LP | |
The last \fLbreak\fP\*f is, strictly speaking, redundant, but it is recommended form | |
.FS | |
.IP \*F | |
A \fLreturn\fP statement is sometimes substituted for the \fLbreak\fP | |
statement, especially in the \fLdefault\fP case. | |
.FE | |
nonetheless because it prevents a fall-through error if another \fLcase\fP | |
is added later after the last one. | |
In general, the fall-through feature of the C \fLswitch\fP statement should | |
rarely, if ever, be used (except for multiple case labels as shown in the example). | |
If it is, it should be commented for future maintenance. | |
.LP | |
All \fLswitch\fP statements should include a default case\*f. | |
.FS | |
.IP \*F | |
With the possible exception of a switch on an \fLenum\fP variable for | |
which all possible values of the \fLenum\fP are listed. | |
.FE | |
Don't assume that the list of cases covers all possible cases. | |
New, unanticipated, cases may be added later, or bugs elsewhere in the | |
program may cause variables to take on unexpected values. | |
.ig xx | |
.LP | |
Another style is to indent the case labels one half tab setting from the switch statement, i.e., four spaces if eight spaces are used for indentation, | |
two spaces if four space indentation is used. | |
.LS | |
switch (condition) { | |
case ABC: | |
case DEF: | |
statements; | |
break; | |
case XYZ: | |
statements; | |
break; | |
default: | |
statements; | |
break; | |
} | |
.LE | |
.LP | |
Either style is acceptable, and both are supported by \fIindent\fP. | |
.xx | |
.LP | |
The \fLcase\fP statement should be indented to the same level as the | |
\fLswitch\fP statement. | |
The \fLcase\fP statement should be on a line separate from the statements | |
within the case. | |
.LP | |
The next example shows the format that should be used for a switch | |
whenever the blocks of statements contain more than a couple of lines. | |
Note the use of blank lines to set off the individual arms of the switch. | |
Note also the use of a block local to one case to declare variables. | |
.LS | |
switch (condition) { | |
case ABC: | |
case DEF: | |
statement1; | |
. | |
. | |
statementn; | |
break; | |
case XYZ: { | |
int var1; | |
statement1; | |
. | |
. | |
statementm; | |
break; | |
} | |
default: | |
statements; | |
break; | |
} | |
.LE | |
.NH | |
Using White Space | |
.NH 2 | |
Vertical White Space | |
.LP | |
The previous example illustrates the use of blank lines to improve the | |
readability of a complicated switch statement. | |
Blank lines improve readability by setting off sections of code that are | |
logically related. | |
Generally, the more white space in code (within reasonable limits), | |
the more readable it is. | |
.LP | |
A blank line should always be used in the following circumstances: | |
.IP \(bu | |
After the \fL#include\fP section. | |
.IP \(bu | |
After blocks of \fL#define\fPs of constants, and before and after \fL#define\fPs of macros. | |
.IP \(bu | |
Between structure declarations. | |
.IP \(bu | |
Between procedures. | |
.IP \(bu | |
After local variable declarations. | |
.ig xx | |
and between the opening brace of | |
a function and the first statement of the function if there are no | |
local variable declarations. | |
.xx | |
.sp | |
.LP | |
Form-feeds should never be used to separate functions. | |
Instead, separate functions into separate files, if desired. | |
.NH 2 | |
Horizontal White Space | |
.LP | |
Here are the guidelines for blank spaces: | |
.IP \(bu | |
A blank should follow a keyword\*f whenever a parenthesis follows the keyword. | |
.FS | |
.IP \*F | |
Note that \fLsizeof\fP and \fLreturn\fP are keywords, whereas \fLstrlen\fP | |
and \fLexit\fP are not. | |
.FE | |
Blanks should not be used between procedure names (or macro calls) and their argument list. | |
This helps to distinguish keywords from procedure calls. | |
.LS 5 | |
if (strcmp(x, "done") == 0) /* no space between strcmp and '(' */ | |
return (0); /* space between return and '(' */ | |
.LE | |
.IP \(bu | |
Blanks should appear after the commas in argument lists. | |
.IP \(bu | |
Blanks should \fBnot\fP appear immediately after a left parenthesis or | |
immediately before a right parenthesis. | |
.IP \(bu | |
All binary operators except \fL.\fP and \fL\->\fP should be separated from their | |
operands by blanks\*f. | |
In other words, blanks should appear around assignment, arithmetic, relational, | |
and logical operators. | |
.FS | |
.IP \*F | |
Some judgment is called for in the case of complex expressions, | |
which may be clearer if the ``inner'' operators are not surrounded | |
by spaces and the ``outer'' ones are. | |
.FE | |
Blanks should never separate unary operators such as unary minus, | |
address (`\fL&\fP'), indirection (`\fL*\fP'), increment (`\fL++\fP'), | |
and decrement (`\fL--\fP') from their operands. | |
Note that this includes the unary \fL*\fP that is a part of pointer | |
declarations. | |
.LP | |
Examples: | |
.LS | |
a += c + d; | |
a = (a + b) / (c * d); | |
strp\->field = str.fl - ((x & MASK) >> DISP); | |
while (*d++ = *s++) | |
n++; | |
.LE | |
.IP \(bu | |
The expressions in a \fLfor\fP statement should be separated by blanks, e.g., | |
.LS | |
for (expr1; expr2; expr3) | |
.LE | |
.IP \(bu | |
Casts should not be followed by a blank, with the exception of function | |
calls whose return values are ignored, e.g., | |
.LS | |
(void) myfunc((unsigned)ptr, (char *)x); | |
.LE | |
.NH 2 | |
Hidden White Space | |
.LP | |
There are many uses of blanks that will not be visible when viewed | |
on a terminal, and it is often difficult to distinguish blanks from tabs. | |
However, inconsistent use of blanks and tabs may produce unexpected results | |
when the code is printed with a pretty-printer, and may make simple regular | |
expression searches fail unexpectedly. | |
The following guidelines are helpful: | |
.IP \(bu | |
Avoid spaces and tabs at the end of a line. | |
.IP \(bu | |
Avoid spaces between tabs and tabs between spaces. | |
.IP \(bu | |
Use tabs to line things up in columns (e.g., for indenting code, and to line | |
up elements within a series of declarations) and spaces to | |
separate items within a line. | |
.IP \(bu | |
Use tabs to separate single line comments from the corresponding code. | |
.NH | |
Parenthesization | |
.LP | |
Since C has some unexpected precedence rules, | |
it is generally a good idea to use parentheses liberally in expressions involving mixed operators. | |
It is also important to remember that complex expressions can be used as parameters to macros, | |
and operator-precedence problems can arise unless \fBall\fP occurrences of | |
parameters in the body of a macro definition have parentheses around them. | |
.NH | |
Naming Conventions | |
.LP | |
Identifier conventions can make programs more understandable by making them | |
easier to read. | |
They can also give information about the | |
function of the identifier, e.g., constant, named type, that can be | |
helpful in understanding code. | |
Individual projects will no doubt have their own naming conventions. | |
However, each programmer should be consistent about his use of naming | |
conventions. | |
.LP | |
Here are some general rules about naming. | |
.IP \(bu | |
Variable and function names should be short yet meaningful. | |
One character variable names should be avoided except for temporary | |
``throwaway'' variables. | |
Use variables \fLi\fP, \fLj\fP, \fLk\fP, \fLm\fP, \fLn\fP | |
for integers, \fLc\fP, \fLd\fP, \fLe\fP for characters, | |
\fLp\fP, \fLq\fP for pointers, and \fLs\fP, \fLt\fP for | |
character pointers. | |
Avoid variable \fLl\fP because it is hard to distinguish \fLl\fP | |
from \fL1\fP on some printers and displays. | |
.IP \(bu | |
Pointer variables should have a ``p'' appended to their names for | |
each level of indirection. | |
For example, a pointer to the variable \fLdbaddr\fP (which contains | |
disk block addresses) can be named \fLdbaddrp\fP (or perhaps simply | |
\fLdp\fP). | |
Similarly, \fLdbaddrpp\fP would be a pointer to a pointer to the | |
variable \fLdbaddr\fP. | |
.IP \(bu | |
Separate "words" in a long variable name with underscores, e.g., | |
\fLcreate_panel_item\f\P\*f. | |
.FS | |
.IP \*F | |
Mixed case names, e.g., \fLCreatePanelItem\fP, are strongly discouraged. | |
.FE | |
An initial underscore should never be used in any user-program names\*f. Trailing underscores should be avoided too. | |
.FS | |
.IP \*F | |
Initial underscores are reserved for global names that are internal to | |
software library packages. | |
.FE | |
.IP \(bu | |
\fL#define\fP names for constants should be in all CAPS. | |
.IP \(bu | |
Two conventions are used for named types, i.e., \fLtypedef\fPs. | |
Within the kernel named types are given a name ending in \fL_t\fP, e.g., | |
.LS | |
typedef enum { FALSE, TRUE } bool_t; | |
typedef struct node node_t; | |
.LE | |
In many user programs named types have their first letter capitalized, e.g., | |
.LS | |
typedef enum { FALSE, TRUE } Bool; | |
typedef struct node Node; | |
.LE | |
.IP \(bu | |
Macro names may be all CAPS or all lower case. | |
Some macros (such as \fIgetchar\fP and \fIputchar\fP) are in lower case | |
since they may also exist as functions. | |
There is a slight preference for all upper case macro names. | |
.IP \(bu | |
Variable names, structure tag names, and function names should be in | |
lower case. | |
.LP | |
Note: in general, with the exception of named types, it is best to avoid names that differ only in case, like \fLfoo\fP | |
and \fLFOO\fP. | |
The potential for confusion is considerable. | |
However, it is acceptable to use as a typedef a name which differs only | |
in capitalization from its base type, e.g., | |
.LS | |
typedef struct node Node; | |
.LE | |
It is also acceptable to give a variable of this type a name that is the | |
all lower case version of the type name, e.g., | |
.LS | |
Node node; | |
.LE | |
.IP \(bu | |
The individual items of enums should be guaranteed unique | |
names by prefixing them with a tag identifying the package to which they belong. For example, | |
.LS | |
enum rainbow { RB_red, RB_orange, RB_yellow, RB_green, RB_blue }; | |
.LE | |
The \fIdbx\fP debugger supports enums in that it can print out the value | |
of an enum, and can also perform assignment statements using | |
an item in the range of an enum. | |
Thus, the use of enums over equivalent \fL#define\fPs may make program debugging | |
easier. | |
For example, rather than writing: | |
.LS | |
#define SUNDAY 0 | |
#define MONDAY 1 | |
\.\. | |
.LE | |
write: | |
.LS | |
enum day_of_week { dw_sunday, dw_monday, \fI...\fP }; | |
.LE | |
.IP \(bu | |
Implementors of libraries should take care to hide the | |
names of any variables and functions that have been declared \fLextern\fP because | |
they are shared by several modules in the library, but nevertheless are private to the library. | |
One technique for doing this is to prefix the name with an underscore and | |
a tag that is | |
unique to the package, e.g., \fL_panel_caret_mpr\fP. | |
.NH | |
Continuation Lines | |
.LP | |
Occasionally, an expression will not fit in the available space in a line, | |
for example, a procedure call with many arguments, | |
or a conjunction or disjunction | |
with many arms. | |
Such occurrences are especially likely when blocks are nested deeply or long | |
identifiers are used. | |
If this happens, | |
the expression should be broken after the last comma in the case of a function call | |
(never in the middle of a parameter expression), | |
or after the last operator that fits on the line | |
(the continuation line should never start with a binary operator). | |
The next line should be further indented by half a tab stop. | |
If they are needed, subsequent continuation lines should be broken in the same manner, and aligned | |
with each other. | |
For example, | |
.LS | |
if (long_logical_test_1 || long_logical_test_2 || | |
long_logical_test_3) { | |
statements; | |
} | |
.LE | |
.LS | |
a = (long_identifier_term1 - long_identifier_term2) * | |
long_identifier_term3; | |
.LE | |
.LS | |
function(long_complicated_expression1, long_complicated_expression2, | |
long_complicated_expression3, long_complicated_expression4, | |
long_complicated_expression5, long_complicated_expression6) | |
.LE | |
.ig xx | |
Some programmers prefer to align function arguments with the first | |
character to the right of the left parenthesis in the previous line, e.g., | |
.LS | |
function(long_complicated_expression1, long_complicated_expression2, | |
long_complicated_expression3, long_complicated_expression4, | |
long_complicated_expression5, long_complicated_expression6) | |
.LE | |
.LP | |
\fIIndent\fP will automatically format continuation lines. | |
Note however that \fIindent\fP will never break a line, it will simply properly align | |
lines that have been broken by the user. | |
.xx | |
.NH | |
Constants | |
.LP | |
Numerical constants should not be coded directly. | |
The \fL#define\fP feature of the C preprocessor should be used to | |
assign a meaningful name. | |
This will also make it easier to administer large programs since the | |
constant value can be changed uniformly by changing only the \fL#define\fP. | |
The enum data type is the preferred way to handle situations where | |
a variable takes on only a discrete set of values, since additional type | |
checking is available through \fIlint\fP. | |
As mentioned above, the \fIdbx\fP debugger also provides support for enums. | |
.LP | |
There are some cases where the constants 0 and 1 may appear as themselves | |
instead of as \fL#define\fPs. | |
For example if a \fLfor\fP loop indexes through an array, then | |
.LS | |
for (i = 0; i < ARYBOUND; i++) | |
.LE | |
is reasonable while the code | |
.LS | |
fptr = fopen(filename, "r"); | |
if (fptr == 0) | |
error("can't open %s\en", filename); | |
.LE | |
is not. | |
In the last example, the defined constant \fLNULL\fP is available as | |
part of the standard I/O library's header file <\fIstdio.h\fP>, and | |
should be used in place of the 0. | |
.LP | |
In rare cases, other constants may appear as themselves. | |
Some judgement is required to determine whether the semantic meaning of the | |
constant is obvious from its value, or whether the code would be easier | |
to understand if a symbolic name were used for the value. | |
.NH | |
Goto | |
.LP | |
While not completely avoidable, use of \fLgoto\fP is discouraged. In many cases, | |
breaking a procedure into smaller pieces, or using a different language | |
construct will enable elimination of \fLgoto\fPs. For example, | |
instead of: | |
.LS 0 | |
again: | |
if (s = proc(args)) | |
if (s == -1 && errno == EINTR) | |
goto again; | |
.LE | |
write: | |
.LS 0 | |
do { | |
s = proc(args); | |
} while (s == -1 && errno == EINTR); /* note\*f */ | |
.LE | |
.FS | |
.IP \*F | |
These two expressions are equivalent unless \fLs\fP is asynchronously modified, | |
e.g., if \fLs\fP is an I/O register. | |
.FE | |
The main place where \fLgoto\fPs can be usefully employed is to break out | |
of several levels of \fLswitch\fP, \fLfor\fP, and \fLwhile\fP | |
nesting\*f, e.g., | |
.FS | |
.IP \*F | |
The need to do such a thing may indicate | |
that the inner constructs should be broken out into | |
a separate function, with a success/failure return code. | |
.FE | |
.LS 0 | |
for (...) | |
for (...) { | |
... | |
if (disaster) | |
goto error; | |
} | |
\&... | |
error: | |
\fIclean up the mess\fP | |
.LE | |
Never use a \fLgoto\fP outside of a given block to branch to a label | |
within a block: | |
.LS | |
goto label; /* WRONG */ | |
\&... | |
for (...) { | |
\&... | |
label: | |
statement; | |
\&... | |
} | |
.LE | |
When a \fLgoto\fP is necessary, the accompanying label should be alone | |
on a line and positioned one indentation level to the left of the code that follows. | |
If a label is not followed by a program statement (e.g., if the next token | |
is a closing brace (\ }\ )) a NULL statement (\ ;\ ) must follow the label. | |
.NH | |
Variable Initialization | |
.LP | |
C permits initializing a variable where it is declared. Programmers at Sun are | |
equally divided about whether or not this is a good idea: | |
.QP | |
"I like to think of declarations and executable code as separate units. Intermixing them only confuses the issue. If only a scattered few declarations are initialized, it is easy not to see them." | |
.QP | |
"The major purpose of code style is clarity. I think the less hunting around for the connections between different places in the code, the better. I don't think | |
variables should be initialized for no reason, however. If the variable doesn't | |
need to be initialized, don't waste the reader's time by making him/her think that it does." | |
.LP | |
A convention used by some programmers is to only initialize automatic variables | |
in declarations if the value of the variable is constant throughout the block. | |
.LP | |
The decision about whether or not to initialize a variable in a declaration is | |
therefore left to the implementor. Use good taste. For example, don't bury a variable initialization in the middle of a long declaration, e.g., | |
.LS | |
int a, b, c, d = 4, e, f; /* This is NOT good style */ | |
.LE | |
.NH | |
Multiple Assignments | |
.LP | |
C also permits assigning several variables to the same value in a single statement, e.g., | |
.LS | |
x = y = z = 0; | |
.LE | |
Good taste is required here also. For example, assigning several variables that are used the same way in the program in a single statement clarifies the relationship between the variables by making it more explicit, e.g., | |
.LS | |
x = y = z = 0; | |
vx = vy = vz = 1; | |
count = 0; | |
scale = 1; | |
.LE | |
is good, whereas: | |
.LS | |
x = y = z = count = 0; | |
vx = vy = vz = scale = 1; | |
.LE | |
sacrifices clarity for brevity. | |
In any case, the variables that are so | |
assigned should all be of the same type (or all pointers being initialized | |
to \fLNULL\fP). | |
It is not a good idea (because it is hard to read) to use multiple assignments for complex expressions, e.g., | |
.LS | |
foo_bar.fb_name.firstchar = bar_foo.fb_name.lastchar = 'c'; /* Yecch */ | |
.LE | |
.NH | |
Preprocessor | |
.LP | |
Do not rename members of a structure using \fL#define\fP within a | |
subsystem; instead, use a \fIunion\fP. | |
However, \fL#define\fP can be used to define shorthand notations | |
for referencing members of a union. | |
For example, instead of | |
.LS | |
struct proc { | |
\&... | |
int p_lock; | |
\&... | |
}; | |
\&... | |
\fIin a subsystem:\fP | |
#define p_label p_lock | |
.LE | |
use | |
.LS | |
struct proc { | |
\&... | |
union { | |
int p_Lock; | |
int p_Label; | |
} p_un; | |
\&... | |
}; | |
#define p_lock p_un.p_Lock | |
#define p_label p_un.p_Label | |
.LE | |
.LP | |
Be \fIextremely\fP careful when choosing names for \fL#define\fPs. | |
For example, never use something like | |
.LS | |
#define size 10 | |
.LE | |
especially in a header file, since it is not unlikely that the user | |
might want to declare a variable named \fLsize\fP. | |
.LP | |
Remember that names used in \fL#define\fP statements come out of a global | |
preprocessor name space and can conflict with names in any other namespace. | |
For this reason, this use of \fL#define\fP is discouraged. | |
.LP | |
Note that \fL#define\fP follows indentation rules similar to other | |
declarations; see the section on indentation for details. | |
.LP | |
Care is needed when defining macros that replace functions since functions | |
pass their parameters by value whereas macros pass their arguments by | |
name substitution. | |
.LP | |
At the end of an \fL#ifdef\fP construct used to select among a required | |
set of options (such as machine types), include a final \fL#else\fP | |
clause containing a useful but illegal statement so that the compiler | |
will generate an error message if none of the options has been defined: | |
.LS | |
#ifdef vax | |
\&... | |
#elif sun | |
\&... | |
#elif u3b2 | |
\&... | |
#else | |
#error unknown machine type; | |
#endif /* machine type */ | |
.LE | |
.LP | |
Note that \fL#elif\fP is an ANSI C construct and should not be used in | |
header files that must be able to be processed be older K&R C compilers. | |
Note also the use of the ANSI C \fL#error\fP statement, which also has | |
the desired effect when using a K&R C compiler. | |
.LP | |
Don't change C syntax via macro substitution, e.g., | |
.LS | |
#define BEGIN { | |
.LE | |
It makes the program unintelligible to all but the perpetrator. | |
.NH | |
Miscellaneous Comments on Good Taste | |
.LP | |
Try to make the structure of your program match the intent. For example, | |
replace: | |
.LS | |
if (boolean_expression) | |
return (TRUE); | |
else | |
return (FALSE); | |
.LE | |
with: | |
.LS | |
return (boolean_expression); | |
.LE | |
Similarly, | |
.LS | |
if (condition) | |
return (x); | |
return (y); | |
.LE | |
is usually clearer when written as: | |
.LS | |
if (condition) | |
return (x); | |
else | |
return (y); | |
.LE | |
or even better, if the condition and return expressions are short; | |
.LS | |
return (condition ? x : y); | |
.LE | |
Do not default the boolean test for nonzero, i.e. | |
.LS | |
if (f() != FAIL) | |
.LE | |
is better than | |
.LS | |
if (f()) | |
.LE | |
even though \fLFAIL\fP may have the value 0 which is considered to mean | |
false by C\*f. | |
This will help you out later when somebody decides that a failure return | |
should be \-1 instead of 0. | |
An exception is commonly made for predicates, which are functions | |
which meet the following restrictions: | |
.IP \(bu | |
Has no other purpose than to return true or false. | |
.IP \(bu | |
Returns 0 for false, non-zero for true. | |
.IP \(bu | |
Is named so that the meaning of (say) a `true' return | |
is absolutely obvious. | |
Call a predicate \fLis_valid\fP or \fLvalid\fP, not \fLcheck_valid\fP. | |
.FS | |
.IP \*F | |
A particularly notorious case is using \fLstrcmp\fP | |
to test for string equality, where the result should never be defaulted. | |
.FE | |
.LP | |
Never use the boolean negation operator (\fL!\fP) with non-boolean | |
expressions. | |
In particular, never use it to test for a NULL pointer or to test for | |
success of the \fLstrcmp\fP function, e.g., | |
.LS | |
char *p; | |
\&... | |
if (!p) /* WRONG */ | |
return; | |
if (!strcmp(*argv, "-a")) /* WRONG */ | |
aflag++; | |
.LE | |
.LP | |
Do not use the assignment operator in a place where it could be easily | |
confused with the equality operator | |
For instance, in the simple expression | |
.LS | |
if (x = y) | |
statement; | |
.LE | |
it is hard to tell whether the programmer really meant assignment or | |
the equality test. | |
Instead, use | |
.LS | |
if ((x = y) != 0) | |
statement; | |
.LE | |
or something similar if the assignment is needed within the \fLif\fP statement. | |
.LP | |
There is a time and a place for embedded assignments\*f. | |
.FS | |
.IP \*F | |
The \fB++\fP and \fB\-\-\fP operators count as assignments. | |
So, for many purposes, do functions with side effects. | |
.FE | |
In some constructs there is no better way to accomplish the results | |
without making the code bulkier and less readable. | |
For example: | |
.LS | |
while ((c = getchar()) != EOF) { | |
\fIprocess the character\fP | |
} | |
.LE | |
Using embedded assignments to improve run-time performance | |
is also possible. | |
However, one should consider the tradeoff between increased speed and | |
decreased maintainability that results when embedded assignments are | |
used in artificial places. | |
For example, the code: | |
.LS | |
a = b + c; | |
d = a + r; | |
.LE | |
should not be replaced by | |
.LS | |
d = (a = b + c) + r; | |
.LE | |
even though the latter may save one cycle. | |
Note that in the long run the time difference between the two will | |
decrease as the optimizer gains maturity, while the difference in | |
ease of maintenance will increase as the human memory of what's | |
going on in the latter piece of code begins to fade\*f. | |
.FS | |
.IP \*F | |
Note also that side effects within expressions can result in code | |
whose semantics are compiler-dependent, since C's order of evaluation | |
is explicitly undefined in most places. | |
Compilers do differ. | |
.FE | |
.LP | |
There is also a time and place for the ternary \fL?\ :\fP operator | |
and the binary comma operator. | |
If an expression containing a binary operator appears before the \fL?\fP, | |
it should be | |
parenthesized: | |
.LS | |
(x >= 0) ? x : \-x | |
.LE | |
Nested \fL?\ :\fP operators can be confusing and should be avoided | |
if possible. | |
There are some macros like \fIgetchar\fP where they can be useful. | |
The comma operator can also be useful in \fLfor\fP statements to | |
provide multiple initializations or incrementations. | |
.NH | |
Portability | |
.LP | |
The advantages of portable code are well known. | |
This section gives some guidelines for writing portable code, | |
where the definition of portable is a source file | |
can be compiled and executed on different | |
machines with the only source change being the inclusion of (possibly | |
different) header files. | |
The header files will contain \fL#define\fPs and \fLtypedef\fPs | |
that may vary from machine to machine. | |
.LP | |
There are two aspects of portability that must be considered: | |
hardware compatibility and interface compatibility. The former | |
category encompasses problems arising from differing machine types, | |
e.g., byte-ordering differences. The second category deals with | |
the multiplicity of | |
.UX | |
operating system interfaces, e.g., BSD4.3 and System V. | |
As the POSIX standard (IEEE P1003.1 Portable Operating System Interface) | |
becomes widely used, it will be preferable to write programs and | |
software packages that use POSIX semantics exclusively. Where this | |
is not possible (for example, POSIX does not define the BSD \fIsocket\fP | |
interface), the required extensions should be documented in a comment | |
near the top of the source file and/or in accompanying manuals\*f. | |
.FS | |
.IP \*F | |
Stringent documentation requirements are defined for programs that | |
claim POSIX conformance. | |
.FE | |
.LP | |
The POSIX standards provide for portability across a wide range of systems. | |
The X/Open XPG standards are emerging as important standards for | |
portability across a wide range of | |
.UN | |
systems. | |
Generally it is best to avoid vendor-specific extensions and use the | |
standard interface that best matches your requirements for portability, | |
performance, functionality, etc. | |
.LP | |
The following is a list of pitfalls to be avoided and recommendations | |
to be considered when designing portable code: | |
.IP \(bu | |
First, one must recognize that some things are inherently non-portable. | |
Examples are code to deal with particular hardware registers such as | |
the program status word, | |
and code that is designed to support a particular piece of hardware | |
such as an assembler or I/O driver. | |
Even in these cases there are many routines and data organizations | |
that can be made machine-independent. | |
Source files should be organized so that the machine-independent | |
code and the machine-dependent code are in separate files. | |
Then if the program is to be moved to a new machine, | |
it is a much easier task to determine what needs to be changed\*f. | |
.FS | |
.IP \*F | |
If you \fL#ifdef\fP dependencies, | |
make sure that if no machine is specified, | |
the result is a syntax error, \fBnot\fP a default machine! | |
.FE | |
It is also possible that code in the machine-independent files | |
may have uses in other programs as well. | |
.IP \(bu | |
Pay attention to word sizes. | |
The following sizes apply to basic types in C for some common machines: | |
.br | |
.ne 2i | |
.TS | |
center; | |
l c c c c c c c | |
l r r r r r r r. | |
type PDP11 VAX Mac PC/DOS 680x0 SPARC PC/UNIX | |
_ | |
char 8 8 8 8 8 8 8 | |
short 16 16 16 16 16 16 16 | |
int 16 32 16 16 32 32 32 | |
long 32 32 32 32 32 32 32 | |
pointer 16 32 32 16 32 32 32 | |
.TE | |
In general, if the word size is important, \fLshort\fP or \fLlong\fP | |
should be used to get 16- or 32-bit items on any of the above machines\*f. | |
.FS | |
.IP \*F | |
Any unsigned type other than plain \fLunsigned int\fP should be | |
\fLtypedef\fPed, as such types are highly compiler-dependent. | |
This is also true of long and short types other than \fLlong int\fP | |
and \fLshort int\fP. | |
Large programs should have a central header file which supplies | |
\fLtypedef\fPs for commonly used width-sensitive types, to make | |
it easier to change them and to aid in finding width-sensitive code. | |
.FE | |
If a simple loop counter is being used where either 16 or 32 bits will | |
do, then use \fLint\fP, since it will get the most efficient (natural) | |
unit for the current machine. | |
.KS | |
.IP \(bu | |
Word size also affects shifts and masks. | |
The code | |
.LS | |
x &= 0177770 | |
.LE | |
.KE | |
will clear only the three rightmost bits of an \fIint\fP on a DOS PC. | |
On a SPARC it will also clear the entire upper halfword. | |
Use | |
.LS | |
x &= ~07 | |
.LE | |
instead which works properly on all machines\*f. | |
.FS | |
.IP \*F | |
The or operator (\ |\ ) does not have these problems, nor do bitfields. | |
.FE | |
.IP \(bu | |
Beware of making assumptions about the size of pointers. | |
They are not always the same size as \fLint\fP\*f. | |
.FS | |
.IP \*F | |
Nor are all pointers always the same size, or freely interchangeable. | |
Pointer-to-character is a particular trouble spot on machines that | |
do not address to the byte. | |
.FE | |
Also, be aware of potential pointer alignment problems. | |
On machines that do not support a uniform address space (unlike, e.g., SPARC), | |
the conversion of a | |
pointer-to-character to a pointer-to-int may result in an invalid address. | |
.IP \(bu | |
Watch out for signed characters. | |
On the SPARC, characters are sign extended when used in expressions, | |
which is not the case on some other machines. | |
In particular, \fIgetchar\fP is an integer-valued function (or macro) | |
since the value of \fLEOF\fP for the standard I/O library is \-1, | |
which is not possible for a character on the IBM 370\*f. | |
If the code depends on the character being signed rather than unsigned, | |
it's probably best to use the ANSI C \fLsigned\fP keyword. | |
.FS | |
.IP \*F | |
Actually, this is not quite the real reason why \fIgetchar\fP returns | |
\fLint\fP, but the comment is valid: code that assumes either | |
that characters are signed or that they are unsigned is unportable. | |
It is best to completely avoid using \fLchar\fP to hold numbers. | |
Manipulation of characters as if they were numbers is also | |
often unportable. | |
.FE | |
.IP \(bu | |
On some processors on which C exists the | |
bits (or bytes) are numbered from right to left within a word. | |
Other machines number the bits from left to right. | |
Hence any code that depends on the left-right orientation of bits | |
in a word deserves special scrutiny. | |
Bit fields within structure members will only be portable so long as | |
two separate fields are never concatenated and treated as a unit. | |
The same applies to variables in general. | |
Alignment considerations and loader peculiarities make it very rash | |
to assume that two consecutively declared variables are together | |
in memory, or that a variable of one type is aligned appropriately | |
to be used as another type. | |
.IP \(bu | |
Become familiar with existing library functions and \fL#define\fPs\*f. | |
.FS | |
.IP \*F | |
But not \fBtoo\fP familiar. | |
The internal details of library facilities, as opposed to their | |
external interfaces, are subject to change without warning. | |
They are also often quite unportable. | |
.FE | |
You should not be writing your own string compare routine, or making | |
your own \fL#define\fPs for system structures\*f. | |
.FS | |
.IP \*F | |
Or, especially, writing your own code to control terminals. | |
Use the \fItermcap\fP, \fIterminfo\fP, or \fIcurses\fP packages. | |
.FE | |
Not only does this waste your time, but it prevents your program | |
from taking advantage of any microcode assists or other | |
means of improving performance of system routines\*f. | |
.FS | |
.IP \*F | |
It also makes your code less readable, because the reader has to | |
figure out whether you're doing something special in the reimplemented | |
stuff to justify its existence. | |
Furthermore, it's a fruitful source of bugs. | |
.FE | |
.IP \(bu | |
Use \fIlint\fP and \fImake\fP (see next sections). | |
.NH | |
Lint | |
.LP | |
\fILint\fP is a C program checker that examines C source files to | |
detect and report type incompatibilities, inconsistencies between | |
function definitions and calls, | |
potential program bugs, etc. | |
It is a good idea to use \fIlint\fP | |
on programs that are being released to a wide audience. | |
.LP | |
It should be noted that the best way to use \fIlint\fP is not as a barrier | |
that must be overcome before official acceptance of a program, but | |
rather as a tool to use whenever major changes or additions to the | |
code have been made. | |
\fILint\fP | |
can find obscure bugs and insure portability before problems occur. | |
.NH | |
Make | |
.LP | |
\fIMake\fP is a program that interprets a description file (\fLMakefile\fP) | |
in order to produce \fIshell\fP commands that generate target files | |
from their sources. All projects should have a \fLMakefile\fP in the | |
top-level source directory that contains rules to build | |
all of the relevant targets. In general, | |
the top-level \fLMakefile\fP will be very simple, containing rules | |
that invoke recursive \fImake\fPs in the sub-directories. | |
.LP | |
In addition to project-dependent targets, the \fLMakefile\fP should contain | |
rules to build the following targets: | |
.IP \fLall\fP 10 | |
builds all targets | |
.IP \fLinstall\fP 10 | |
installs all targets and header files in the appropriate directories | |
.IP \fLclean\fP 10 | |
removes all intermediate files | |
.IP \fLclobber\fP 10 | |
removes all targets and intermediate files | |
.IP \fLlint\fP 10 | |
executes \fIlint\fP on all targets | |
.LP | |
For SunOS, more detailed Makefile guidelines are specified elsewhere. | |
.NH | |
Project-Dependent Standards | |
.LP | |
Individual projects may wish to establish additional standards beyond | |
those given here. | |
The following issues are some of those that should be addressed by | |
projects. | |
.IP \(bu | |
What additional naming conventions should be followed? | |
In particular, systematic prefix conventions for functional grouping | |
of global data and also for structure or union member names can be useful. | |
.IP \(bu | |
What kind of include file organization is appropriate for the | |
project's particular data hierarchy? | |
.IP \(bu | |
What procedures should be established for reviewing \fIlint\fP | |
complaints? | |
A tolerance level needs to be established in concert with the \fIlint\fP | |
options to prevent unimportant complaints from hiding complaints about | |
real bugs or inconsistencies. | |
.IP \(bu | |
If a project establishes its own archive libraries, it should plan on | |
supplying a \fIlint\fP library file to the system administrators. | |
This will allow \fIlint\fP to check for compatible use of library | |
functions. | |
.NH | |
Conclusion | |
.LP | |
A set of standards has been presented for C programming style. | |
One of the most important points is the proper use of white space | |
and comments so that the structure of the program is evident from | |
the layout of the code. | |
Another good idea to keep in mind when writing code is that it is | |
likely that you or someone else will be asked to modify it or make | |
it run on a different machine some time in the future. | |
.bp | |
.ce 1 | |
\fBBibliography\fP | |
.sp 2 | |
.IP [1] | |
S. Shah, | |
\fIC Coding Guidelines for | |
.UX | |
System Development Issue 3\fP, | |
AT&T (internal document) 1987. | |
.IP [2] | |
B.W. Kernighan and D.M. Ritchie, | |
\fIThe C Programming Language\fP, | |
Prentice-Hall 1978. | |
.IP [3] | |
S.P. Harbison and G.L. Steele, | |
\fIA C Reference Manual\fP, | |
Prentice-Hall 1984. | |
.IP [4] | |
Evan Adams, Dave Goldberg, et al, | |
\fINaming Conventions for Software Packages\fP, | |
Sun Microsystems (internal document) 1987. | |
.IP [5] | |
ANSI/X3.159-198x, | |
\fIProgramming Language C Standard\fP, | |
(Draft) 1986. | |
.IP [6] | |
IEEE/P1003.1, | |
\fIPortable Operating System for Computer Environments\fP, | |
(Draft) 1987. | |
.bp | |
.NH | |
Appendix 1: SCCS ident strings and copyrights | |
.LP | |
The following are SCCS ident strings and copyrights for various kinds of files. | |
(Omit the Copyright comment if not relevant.) | |
Note that \fL%\&W%\fP is an acceptable substitute for \fL%\&Z%%\&M% %\&I%\fP. | |
Note also that there are tabs between the \fL%\&M%\fP, \fL%\&I%\fP, | |
\fL%\&E%\fP, and \fL%\&W%\fP keywords, to make the output of the \fBwhat(1)\fP | |
command more readable. | |
.sp | |
\fBHeader Files\fP | |
.sp | |
.LS 0 | |
/* | |
* Copyright (c) 1993 by Sun Microsystems, Inc. | |
* All rights reserved. | |
*/ | |
#ifndef \fIguard\fP | |
#define \fIguard\fP | |
#pragma ident "%\&Z%%\&M% %\&I% %\&E% SMI" | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
\fIbody of header\fP | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif \fIguard\fP | |
.LE | |
.sp | |
\fBC or Assembler Files\fP | |
.sp | |
.LS 0 | |
/* | |
* Copyright (c) 1993 by Sun Microsystems, Inc. | |
* All rights reserved. | |
*/ | |
#pragma ident "%\&Z%%\&M% %\&I% %\&E% SMI" | |
.LE | |
.sp | |
\fBMakefiles\fP | |
.sp | |
.LS 0 | |
# | |
# %\&Z%%\&M% %\&I% %\&E% SMI | |
# | |
.LE | |
.sp | |
\fBShell Files\fP | |
.sp | |
.LS 0 | |
#!/bin/sh (\fRor\fP /bin/ksh, \fRor\fP /bin/csh) | |
# | |
# %\&Z%%\&M% %\&I% %\&E% SMI | |
# | |
.LE | |
.sp | |
.LS 0 | |
\fBManual Pages\fP | |
.sp | |
\0.\\\\" %\&Z%%\&M% %\&I% %\&E% SMI | |
.LE |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment