You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
623 lines
20 KiB
623 lines
20 KiB
/*
|
|
* $Id: CodingGuidelines,v 1.6 2007/02/16 18:26:41 desrod Exp $
|
|
*
|
|
* CodingGuidelines: pilot-link Coding Guidelines
|
|
*
|
|
* (c) 2002-2007, David A. Desrosiers <desrod@gnu-designs.com>
|
|
*
|
|
*
|
|
* (2002-01-26) dd
|
|
* Initial release of Coding Guidelines
|
|
*
|
|
* (2002-05-04) dd
|
|
* Added some user-submitted suggestions to formatting and content,
|
|
* fixed a pilot-unix reference
|
|
*
|
|
* (2007-02-07) dd
|
|
* Updated some links and verbage here and there, based on user input
|
|
* over the last 5 years.
|
|
*/
|
|
|
|
There are bound to be errors in this document, but this document strives to
|
|
capture the ideas and concepts distributed throughout the pilot-link
|
|
codebase, userland conduits and language bindings.
|
|
|
|
This document should assist anyone who wants to modify the code found here,
|
|
or work on active development of the codebase or submit patches to it.
|
|
|
|
Anyone who wishes to contribute code should adhere to these guidelines. Code
|
|
that doesn't follow these conventions will be modified or (in extreme cases)
|
|
rejected. If you have additional suggestions or a good case for changing
|
|
some of these guidelines then send a message or post a message on one of the
|
|
pilot-link mailing lists[1].
|
|
|
|
First, start off by reading the ANSI C99 Coding Standards document[2]. I may
|
|
have deviated in a few places but just about everything in the document
|
|
applies here as well.
|
|
|
|
If you are new to C programming, and reading the code seems confusing to
|
|
you, you might want to try some of the available FAQs[3][4][5] out there, as
|
|
well as the K&R "The C Programming Language"[6] books.
|
|
|
|
Above all, write code that is easy to read and easy to maintain. Comment
|
|
blocks of code and functions at all times. And get on my case if I deviate
|
|
too much as well!
|
|
|
|
Some Advice to Contributors
|
|
---------------------------
|
|
Document and comment your code while you're writing it, not after
|
|
you have it debugged and working correctly.
|
|
|
|
Everyone who will have to look at your unfinished but
|
|
well-documented program will appreciate the explanations.
|
|
|
|
|
|
File Organization
|
|
-----------------
|
|
A source file consists of various sections that should be separated
|
|
by several blank lines.
|
|
|
|
Although there is no maximum length limit for source files, files
|
|
with more than about 1,000 lines are cumbersome to deal with.. so
|
|
try to break them up into logical source files based on function,
|
|
rather than name.
|
|
|
|
Many rows of asterisks, for example, present little information
|
|
compared to the time it takes to scroll past, and are discouraged.
|
|
|
|
Lines longer than 79 columns are not handled well by all terminals
|
|
and should be avoided if possible. Excessively long lines which
|
|
result from deep indenting are often a symptom of poorly-organized
|
|
code. Avoid it if you can (yes, we fall into this trap in several
|
|
places in pilot-link too).
|
|
|
|
Every source file should start at the top with comments containing a
|
|
copyright notice, the name of the file, and a half-to-one-line
|
|
summary of what the file contains.
|
|
|
|
If you create a file by copying the boilerplate from another file,
|
|
make sure to edit the copyright year and the file name as well as
|
|
add your own name to the top if you are the original author.
|
|
|
|
|
|
File Naming Conventions
|
|
-----------------------
|
|
Some compilers and tools require certain suffix conventions for names of
|
|
files. The following suffixes are required and are lowercase unless
|
|
otherwise noted:
|
|
|
|
* C source file names must end in a .c extension
|
|
|
|
* Header files, otherwise called includes, must end in .h
|
|
|
|
* Headers for C++ code in pilot-link may end in .hxx
|
|
|
|
* Perl source files should always end in .pl, or .plt for "tainted"
|
|
Perl scripts
|
|
|
|
* All Java source files should end in .java
|
|
|
|
* All Python source files should end in .py (or .i for swig source)
|
|
|
|
The following conventions are universally followed:
|
|
|
|
* Relocatable object file names end in .o
|
|
|
|
* Include header file names end in .h
|
|
|
|
An alternate convention that may be preferable in multi-language
|
|
environments like pilot-link is to suffix both the language type and
|
|
.h (e.g. 'foo.cc' or 'foo.hxx') to separate the standard .h headers
|
|
and includes from those that are C++ specific.
|
|
|
|
* Yacc source file names end in .y
|
|
|
|
C++ has compiler-dependent suffix conventions, including .c, ..c,
|
|
.cc, .c.c, and .C. Since much C code is also C++ code, there is no
|
|
clear solution here, just stick with .c for C and .cxx for C++.
|
|
|
|
In addition, it is conventional to use 'Makefile' (not 'makefile',
|
|
note the lowercase 'M' in the second case) for the main control file
|
|
for make (for systems that support it) and 'README' for a summary of
|
|
the contents of the directory or directory tree.
|
|
|
|
We use Makefile.am for automake versions of the Makefile targets in
|
|
pilot-link.
|
|
|
|
|
|
Program Files
|
|
-------------
|
|
The suggested order of sections for a program file is as follows:
|
|
|
|
1. First in the file is a prologue that tells what is in that file.
|
|
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 a list of the object names.
|
|
The prologue may optionally contain author(s), revision control
|
|
information, references, etc.
|
|
|
|
2. Any header file includes should be next. If the include is for a
|
|
non-obvious reason, the reason should be commented. In most
|
|
cases, system include files like stdio.h should be included
|
|
before user include files.
|
|
|
|
In pilot-link, the stacking order looks like this:
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <signal.h>
|
|
#include <utime.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
|
|
#include "pi-source.h"
|
|
#include "pi-socket.h"
|
|
#include "pi-file.h"
|
|
#include "pi-dlp.h"
|
|
#include "pi-version.h"
|
|
#include "pi-header.h"
|
|
|
|
3. Any defines and typedefs that apply to the file as a whole are
|
|
next. One normal order is to have "constant" macros first, then
|
|
"function" macros, followed by typedefs and enums.
|
|
|
|
4. Next come the global (external) data declarations, usually in the
|
|
order: externs, non-static globals, static globals. If a set of
|
|
defines applies to a particular piece of global data (such as a
|
|
flags word), the defines should be immediately after the data
|
|
declaration or embedded in structure declarations, indented to
|
|
put the defines one level deeper than the first keyword of the
|
|
declaration to which they apply.
|
|
|
|
5. The functions come last, and should be in some sort of meaningful
|
|
order. Like functions should appear together. A "breadth-first"
|
|
approach (functions on a similar level of abstraction together)
|
|
is preferred over "depth-first" (functions defined as soon as
|
|
possible before or after their calls).
|
|
|
|
Considerable judgement is called for here. If defining large
|
|
numbers of essentially-independent utility functions, consider
|
|
alphabetical order.
|
|
|
|
You'll notice a convention in pilot-link's source files which
|
|
models the following boilerplate structure, that main() is ALWAYS
|
|
last, and any Help() functions appear directly above main():
|
|
|
|
/*
|
|
* MyConduit.c: Palm boilerplate conduit template
|
|
*
|
|
* Copyright (c) 1996-2002, John Q. Public
|
|
*
|
|
* This is licensed under the Foo License
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <system.h>
|
|
#include "local.h"
|
|
|
|
#define something somevalue
|
|
|
|
/* Declare prototypes */
|
|
int main(int argc, char *argv[])
|
|
void my_function(char foo, FILE *blort);
|
|
static void Help(char *progname)
|
|
|
|
void my_function(foo, FILE *blort) {
|
|
..function data;
|
|
}
|
|
|
|
/* Help() always directly precedes main() */
|
|
static void Help(char *progname) {
|
|
..Help text here;
|
|
..right above main();
|
|
}
|
|
|
|
/* main(); is always last, at the bottom */
|
|
int main(int argc, char *argv[]) {
|
|
..main last function;
|
|
}
|
|
|
|
|
|
Header Files
|
|
------------
|
|
The header file is the first place that a contributor will go to
|
|
find out information about procedures, structures, constants, etc.
|
|
Make sure that every procedure and structure has a comment that says
|
|
what it does. Divide procedures into meaningful groups set off by
|
|
some distinguished form of comment.
|
|
|
|
Avoid private header filenames that are the same as library header
|
|
filenames. Namespace pollution is no fun for anyone. Avoid it if you
|
|
can.
|
|
|
|
Don't use absolute pathnames for header files. Use the construction
|
|
for getting them from a standard place, or define them relative to
|
|
the current directory. Use the -I (include-path) to handle extensive
|
|
private libraries of header files. This allows reorganizing the
|
|
project directory structure without having to alter source files.
|
|
|
|
If you're creating your own header files, remember that header files
|
|
that declare functions or external variables should be included in
|
|
the file that defines the function or variable. That way, the
|
|
compiler can do type checking and the external declaration will
|
|
always agree with the definition.
|
|
|
|
Avoid defining variables in a header file. It is bad design, and is
|
|
a common symptom of poor "partitioning" of code between files.
|
|
|
|
Some objects like typedefs and initialized data definitions cannot
|
|
be seen twice by the compiler in one compilation. On some systems,
|
|
repeating uninitialized declarations without the extern keyword also
|
|
causes problems. Repeated declarations can happen if include files
|
|
are nested and will cause the compilation to fail.
|
|
|
|
Header files should not be nested. In extreme cases, where a large
|
|
number of header files are to be included in several different
|
|
source files, please put all common #includes in one include file,
|
|
such as the legacy include/pi-config.h file in previous pilot-link
|
|
releases.
|
|
|
|
One trick to avoid "double-inclusion" of headers is to use the
|
|
syntax below. It is not widely used in pilot-link, but the project
|
|
is moving in that direction.
|
|
|
|
#ifndef MYHEADER_H define MYHEADER_H
|
|
..body of myheader.h
|
|
#endif /* end MYHEADER_H */
|
|
|
|
|
|
Comments
|
|
--------
|
|
The comments should describe what is happening, how it is being
|
|
done, what parameters mean, which globals are used and which are
|
|
modified, and any restrictions or bugs.
|
|
|
|
C is not assembler; putting a comment at the top of a 3-10 line
|
|
section telling what it does overall is often more useful than a
|
|
comment on each line describing micrologic.
|
|
|
|
Comments should justify offensive code. The justification should be
|
|
that something bad will happen if unoffensive code is used. Just
|
|
making code faster is not enough to rationalize a hack; the
|
|
performance must be shown to be unacceptable without the hack. The
|
|
comment should explain the unacceptable behavior and describe why
|
|
the hack is a "good fix" solution to the problem.
|
|
|
|
Block comments inside a function are appropriate, and they should be
|
|
tabbed over to the same tab setting as the code that they describe.
|
|
One-line comments alone on a line should be indented to the tab
|
|
setting of the code that follows.
|
|
|
|
if (argc > 1) {
|
|
/* Get input file from command line. */
|
|
if (freopen(argv[1], "r", stdin) == NULL) {
|
|
perror(argv[1]);
|
|
}
|
|
}
|
|
|
|
Very short comments may appear on the same line as the code they
|
|
describe, and should be tabbed over 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.
|
|
|
|
if (a == EXCEPTION) {
|
|
b = TRUE; /* special case */
|
|
} else {
|
|
b = isprime(a); /* works only for odd 'a' */
|
|
}
|
|
|
|
|
|
Declarations
|
|
------------
|
|
Global declarations should begin in column one at the top of the
|
|
file. All external data declaration should be preceded by the extern
|
|
keyword.
|
|
|
|
Repeated size declarations are particularly beneficial to someone
|
|
picking up code written by another.
|
|
|
|
The "pointer" qualifier, '*', should be with the variable name
|
|
rather than with the type.
|
|
|
|
char *s,
|
|
*t,
|
|
*u;
|
|
|
|
..instead of
|
|
|
|
char* s,
|
|
t,
|
|
u;
|
|
|
|
which would be incorrect, since 't' and 'u' do not get declared as
|
|
pointers in the second example.
|
|
|
|
You may notice throughout pilot-link's codebase, a very different
|
|
indenting standard on declaring types. This style is called
|
|
"Berkeley Indenting".
|
|
|
|
In the above two examples, you can see that the types are indented
|
|
one level beyond the type name, and each additional declaration of
|
|
the same type is indented underneath it, separated by commas. The
|
|
following two are functionally equivalent:
|
|
|
|
char *s, *t, *u;
|
|
|
|
char *s, /* S is for Santa */
|
|
*t, /* T is for Type */
|
|
*u; /* U know what this is */
|
|
|
|
However, the second type is much more readable, and easier to
|
|
maintain. It also allows type-level commenting, which the first
|
|
example does not. Please adhere to this style when developing code
|
|
against pilot-link, or updating existing pilot-link code.
|
|
|
|
The names, values, and comments are usually tabbed so that they line
|
|
up underneath each other. Use the tab character rather than blanks
|
|
(spaces).
|
|
|
|
Any variable whose initial value is important should be explicitly
|
|
initialized, or at the very least should be commented to indicate
|
|
that C's default initialization to zero is being relied upon.
|
|
|
|
Constants used to initialize longs should be explicitly long. Use
|
|
capital letters; for example two long '2l' looks a lot like '21',
|
|
the number twenty-one. A proper example would be similar to:
|
|
|
|
int sd = -1;
|
|
char *msg = "string";
|
|
|
|
struct bar foo[] = {
|
|
{ 40, BLORT, 60000L }, /* Note the capital L */
|
|
{ 28, QUUX, 0L },
|
|
{ 0 },
|
|
};
|
|
|
|
In any file which is part of a larger whole rather than a
|
|
self-contained program, maximum use should be made of the static
|
|
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 usage should be commented to make it clear that another file's
|
|
variables are being used; the comment should name the other file.
|
|
|
|
|
|
Function Declarations
|
|
---------------------
|
|
Each function should be preceded by a block comment prologue that
|
|
gives a short description of what the function does and (if not
|
|
clear) how to use it. Discussion of non-trivial design decisions and
|
|
side-effects is also appropriate. Avoid duplicating information
|
|
clear from the code.
|
|
|
|
An example from pilot-link's libsock/dlp.c file:
|
|
|
|
/************************************************************
|
|
*
|
|
* Function: dlp_GetSysDateTime
|
|
*
|
|
* Summary: DLP 1.0 GetSysDateTime function to get
|
|
* device date and time
|
|
*
|
|
* Parameters: None
|
|
*
|
|
* Returns: A negative number on error, the number of
|
|
* bytes read otherwise
|
|
*
|
|
************************************************************/
|
|
|
|
The function return type should be alone on a line, and indented one
|
|
stop (if necessary) Do not default to int; if the function does not
|
|
return a value then it should be given return type void
|
|
|
|
static void Help(char *progname)
|
|
{
|
|
printf("This is some Help text that"
|
|
"extends to a second line\n");
|
|
return;
|
|
}
|
|
|
|
Each parameter should be declared (do not default to int). In
|
|
general the role of each variable in the function should be
|
|
described. This may either be done in the function comment or, if
|
|
each declaration is on its own line, in a comment on that line.
|
|
|
|
Loop counters called 'i', 'j', 'k', string pointers called 's', and
|
|
integral types called 'c' when used for characters are typically
|
|
excluded.
|
|
|
|
If a group of functions all have a similar parameter or local
|
|
variable name, it helps to call the repeated variable by the same
|
|
name in all functions (int i; /* for example */).
|
|
|
|
Avoid using the same name for different purposes in related
|
|
functions. Like parameters should also appear in the same place in
|
|
the various argument lists.
|
|
|
|
Comments for parameters and local variables should be tabbed so that
|
|
they line up underneath each other. Local variable declarations
|
|
should be separated from the function's statements by a blank line.
|
|
|
|
Avoid local declarations that override declarations at higher
|
|
levels. In particular, local variables should not be redeclared in
|
|
nested blocks.
|
|
|
|
|
|
Whitespace
|
|
----------
|
|
Use vertical and horizontal whitespace tactfully. Indentation and
|
|
spacing should reflect the block structure of the code; i.e. there
|
|
should be at least two blank lines between the end of one function
|
|
and the comments for the next.
|
|
|
|
A long string of conditional operators should be split onto separate
|
|
lines for easier readability.
|
|
|
|
A conditional expressed as:
|
|
|
|
if (foo->next==NULL && totalcount<needed && needed<=MAX_ALLOT
|
|
&& server_active(current_input)) {
|
|
..stuff
|
|
}
|
|
|
|
Would be more readable as:
|
|
|
|
if (foo->next == NULL
|
|
&& totalcount < needed
|
|
&& needed <= MAX_ALLOT
|
|
&& server_active(current_input)) {
|
|
..stuff
|
|
}
|
|
|
|
|
|
Compound Statements
|
|
-------------------
|
|
A compound statement is a list of statements enclosed by braces.
|
|
There are many common ways of formatting the braces. Be consistent
|
|
with your local standard, if you have one, or pick one and use it
|
|
consistently. When editing someone else's code, always use the style
|
|
used in that code.
|
|
|
|
control {
|
|
statement;
|
|
statement;
|
|
}
|
|
|
|
The style above is called "K&R style", and is preferred if you
|
|
haven't already got a favorite. With K&R style, the else part of an
|
|
if-else statement and the while part of a do-while statement should
|
|
appear on the same line as the close brace. With most other styles,
|
|
the braces are always alone on a line.
|
|
|
|
In pilot-link, you will see a mix of both K&R and Berkeley style
|
|
indenting. Either is appropriate, and they are functionally
|
|
interchangable. If you are unsure which to use, follow similar code
|
|
examples in the pilot-link project as a baseline. Defaulting to K&R
|
|
will always work as well.
|
|
|
|
When a block of code has several labels, the labels are placed on
|
|
separate lines. The fall-through feature of the C switch statement,
|
|
(with no break between a code segment and the next case statement)
|
|
must be commented for future maintenance.
|
|
|
|
A lint-style comment/directive is best.
|
|
|
|
switch (expr) {
|
|
case ABC:
|
|
case DEF:
|
|
statement;
|
|
break;
|
|
case GHI:
|
|
statement;
|
|
/* FALLTHROUGH */
|
|
case XYZ:
|
|
statement;
|
|
break;
|
|
}
|
|
|
|
The last break above is unnecessary, but is required because it
|
|
prevents a fall-through error if another case is added later after
|
|
the last one.
|
|
|
|
The "default" case, if used, should be last and does not require a
|
|
break if it is last.
|
|
|
|
|
|
Whenever an if-else statement has a compound statement for either
|
|
the if or else section, the statements of both the if and else
|
|
sections should both be enclosed in braces (called fully bracketed
|
|
syntax).
|
|
|
|
if (expr) {
|
|
statement;
|
|
} else {
|
|
statement;
|
|
statement;
|
|
}
|
|
|
|
|
|
An if-else with else if statements should be written with the else
|
|
conditions left-justified. There are several examples of this usage
|
|
throughout pilot-link's conduits.
|
|
|
|
if (STREQ(reply, "yes")) {
|
|
statements for yes
|
|
...
|
|
} else if (STREQ(reply, "no")) {
|
|
...
|
|
} else if (STREQ(reply, "maybe")) {
|
|
...
|
|
} else {
|
|
statements for default
|
|
...
|
|
}
|
|
|
|
##################################################################
|
|
#
|
|
# This part is not finished yet, these are just my notes while I
|
|
# work my way down the document
|
|
#
|
|
##################################################################
|
|
|
|
Naming Classes
|
|
--------------
|
|
* Use FirstLetterOfWordIsCaptilized style (Camel Notation)
|
|
|
|
|
|
Naming Files
|
|
------------
|
|
* Use all lower case.
|
|
|
|
* Use - to separate words. eg. pilot-install.c
|
|
|
|
* Use .c file extensions for source files, .h for headers, .cpp or
|
|
.cc for C++, .hxx for C++ private headers, .pl for perl, and so
|
|
on.
|
|
|
|
* Filenames must be less than 32 characters in length. This plays
|
|
nice with older file systems like MacOS.
|
|
|
|
|
|
General Formatting
|
|
------------------
|
|
* Use TABS with a size of 8 to make the code easily readable while
|
|
not wasting too much space
|
|
|
|
|
|
Braces and Parenthesis
|
|
----------------------
|
|
* Paranthesis should be right after a function name. i.e.
|
|
function() not function ()
|
|
|
|
* Paranthesis should have a space right after a keyword (if, while,
|
|
for) eg: for (...)
|
|
|
|
Miscellaneous
|
|
-------------
|
|
* Avoid magic numbers. The only magic numbers in use should be 1 and
|
|
0. There are some cases where this may not apply, such as:
|
|
|
|
#define APPEND_CRLF 1
|
|
my_strcat(str, "Hello World", APPEND_CRLF);
|
|
|
|
Instead of using:
|
|
my_strcat(str, "Hello World", 1);
|
|
|
|
Numbers are only magic if its not readily apparent what they are
|
|
for.
|
|
|
|
|
|
Appendices
|
|
----------
|
|
[1] http://lists.pilot-link.org/
|
|
[2] http://webstore.ansi.org/ansidocstore/product.asp?sku=INCITS%2FISO%2FIEC+9899-1999+(R2005)
|
|
[3] http://www.eskimo.com/~scs/C-faq/top.html
|
|
[4] http://c-faq.com/
|
|
[5] http://www.faqs.org/faqs/C-faq/
|
|
[6] http://isbn.nu/0131103628
|