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.

457 lines
18 KiB

Coding style guidelines for transcode
-------------------------------------
== Syntax ==
* Lines must not be longer than 79 columns, and should be kept to 75
columns or less when feasible.
* The basic indentation unit is four columns.
* Tab characters (ASCII 0x09) must not be used for indentation or alignment
of source code, and should be avoided in other cases (such as in strings)
where possible. Tabs should not be assumed to have any particular width.
* Comments should use standard C style (/*...*/); however, C++ style (//)
is permissible for inline comments (comments placed on the same line as
code). Comments on their own lines should start at the same indentation
level as the surrounding code; inline comments should, if feasible, line
up with other nearby inline comments. Multi-line comments should begin
with a single "*" on the second and subsequent lines, aligned with the
"*" in the "/*" on the first line; the final "*/" should be placed on the
same line as the last line of comment text, unless doing so would cause
that line to exceed 79 characters. However, for lengthy comments it is
also acceptable to place the initial "/*" and trailing "*/" on their own
lines. Examples:
/*
* This is a lengthy multiline comment, perhaps a function header,
* with the comment delimiters placed on their own lines.
*/
void myfunction()
{
int var1; // Inline comment about var1
double variable2; // Inline comment about var2
/* This is a shorter multiline comment about what the next bit of
* code will do. */
first();
/* This is a single-line comment */
second();
}
* Every function should be preceded by a header in the following format
which describes the purpose of the function.
/**
* function_name: The purpose/behavior of the function, in detail.
* (Replace "function_name" by the function's name.)
*
* Parameters:
* param: Description of `param'.
* param2: Description of `param2'. Note that parameters are
* right-aligned to the colon.
* Return value:
* A description of the value returned by the function. If the
* the function's return type is void, write "None."
* Preconditions:
* A list of everything the function assumes to be true. Use C-style
* expressions (such as "ptr != NULL") where possible. If there are no
* preconditions, write "None." This section may be omitted if there
* are no preconditions.
* Postconditions:
* A list of everything the function guarantees to be true when it
* returns, or "None." This section may be omitted if no
* postconditions are specified.
* Side effects:
* Any effects the function has that are not mentioned in the function
* description (such as the effect of strtok() destroying its source
* string), or "None." This section may be omitted if there are no
* side effects.
*/
* Only one statement is allowed per line. Statements (including the empty
statement ";") may not be written on the same line as a control keyword
(if, for, or while).
* Spaces are placed between binary operators and their operands, except for
the member-reference operators "->" and "."; spaces are not placed
between unary operators and their operands. However, spaces may be
omitted around binary operators when doing so would improve readability.
Examples:
i = j * 2;
structure->field++;
result += i*60 + (j+59)/60;
* Parentheses are required when && and || (or two or more of &, |, and ^)
are used in the same expression to explicitly indicate the order of
evaluation; do not rely on the default order of evaluation.
* Parentheses following the control statements "if", "for", and "while" are
preceded by a space. Parentheses following function or macro calls are
not preceded by a space. Example:
if (flag)
function();
* Spaces are placed after the comma of each parameter to a function or
macro; however, they may be omitted in function calls which are
themselves parameters to a function or macro, or when including them
would make the line exceed 79 columns in length (and removing them does
not harm the readability of the code). Spaces are also placed after the
semicolons in a for statement. Spaces are not placed after the opening
parenthesis or before the closing parenthesis of a function/macro call or
control statement. Examples:
function(param1, param2);
function(param1, strchr(string,'/'), param3);
for (i = 0; i < count; i++) ...
* Opening braces of control statement blocks go on the same line as the
control statement (if, for, etc.) associated with the block; function
blocks and "naked blocks" (those not associated with any control
statement or function) have the opening brace alone on a line, indented
to the same level as the surrounding code. Closing braces are indented
to the same level as the line containing the opening brace. Examples:
if (flag) {
/* indented code goes here */
...
}
int function(int param1, char *param2)
{
/* indented code goes here */
...
/* start of a naked block */
{
int foo;
...
}
}
* If an if statement is longer than one line, it should be broken at
logical operators (&& or ||); the operator should be placed at the
beginning of the next line, and should be indented to show which term the
operator belongs with. The closing parenthesis for the if statement and
the subsequent open brace should be alone on a line, aligned with the
first line of the if statement. (Braces are required in this case.)
Conditions for for and while statements should never span multiple lines,
though a for statement may be broken at the semicolons if necessary.
Examples:
if (ok && (strcmp(option, "option-name-1") == 0
|| strcmp(option, "option-name-2") == 0)
) {
...
}
if (!init_first()
|| !init_second()
|| !init_third()
|| !init_fourth()
) {
/* This example outdents the || by three spaces
* to make the terms line up. */
...
}
for (node = first_node(list, LISTTYPE_MYLIST); node != NULL;
node = next_node(list, LISTTYPE_MYLIST))
) {
...
}
* An else followed by an if is considered to be a single keyword, "else if".
The if part should always be placed on the same line as and immediately
following the else; the else if should never be split up onto two lines,
nor should braces be used around the if block. The exception to this is
when the else and if refer to two conceptually distinct sets of
conditions, in which case the if block should be enclosed by braces and
indented. Example:
res = check_password( /* ... */ );
if (res == 0) {
...
} else if (res == 1) {
/* "else if" on a single line */
...
} else {
if (otherflag) {
/* "if" condition is different from "else"
* condition, thus separate */
...
}
}
* Braces are always placed around the body of a control statement (if, for,
etc.). The one exception, aside from the case of else if mentioned
above, is a statement for which the body, as well as the bodies of any
else if or else statements for an if statement, are all one statement in
length and do not contain any nested control statements; in this case
braces are optional (but recommended if any of the statements span
multiple lines). Examples:
for (i = 0; i < count; i++)
function(i);
while (!done) {
/* Braces required because of the nested "if" */
if (do_stuff())
done = 1;
}
if (state == 0) {
a = b;
} else if (state == 1) {
/* Every if/else body gets braces because this body
* has two statements */
b += a;
a = 0;
} else {
state = 0;
}
* When using multiply-nested control statements, or a single control
statement which has a long block, the closing brace of the block should
be followed by a comment indicating what control statement the block
belongs to. This should usually be the content of the control statement,
but may be abbreviated as long as its meaning is clear. If a long if
block is followed by an else clause, the else should likewise be
documented. Examples:
while (i < count) {
...
} /* while (i < count) */
if (flag) {
...
} else { /* !flag */
...
} /* if (flag) */
for (node = first; node != NULL; node = node->;next) {
...
} /* for all nodes */
* Case labels for a switch should be indented half of a normal indentation
unit (two columns) from the line containing the switch with which they
are associated; statements associated with a case should be indented a
full unit from the line containing the switch (half a unit from the
case). If a case requires its own block, such as when it declares its
own local variables, the opening brace is placed after the colon on the
case line. Example:
switch (variable) {
case 123: {
int foo;
...
break;
} /* case 123 */
default:
...
return -1;
}
* When a case in a switch block does not contain a break (or return)
statement and deliberately "falls through" to the next case, a comment to
this effect should be made at the bottom of the case. Example:
switch (state) {
case 0:
...
/* fall through */
case 1:
...
break;
}
* Structured type initializers should use C99-style named initializers, to
avoid bugs resulting from initialization of incorrect fields if the
structure definition is changed. This is particularly important if the
structure definition is in a different source file, such as a header
file. Example:
struct foo {
int bar;
double quux;
int have_quux;
};
...
struct foo myfoo = {
.bar = 1,
.have_quux = 0,
};
== Semantics ==
* Functions should not be overly long or complex; as a rule of thumb, if a
function has more than four levels of indentation then it should probably
be broken up into separate functions.
* Do not use binary operators to perform arithmetic operations. In
particular, never use bit-shift operators in place of multiplication or
division by powers of two. (Modern compilers are smart enough to convert
arithmetic expressions like "x/2" to a bit-shift operation; moreover,
right shift and division give different results on negative numbers.)
Binary operators should only be used when operating on bitmasks or other
values where there is a need to extract or modify particular bits,
independent of the value of the variable as a whole.
* Always use the increment and decrement operators (++ and --) as postfix
operators, except in expressions that require prefix usage.
* Avoid nesting assignment expressions (including increment and decrement
operations) within other expressions, to the extent that extracting the
assignment operation would not unduly complicate the code. In the
following example, separating the strtok() call and its comparison would
require changing the structure of the loop completely, so the example is
acceptable as is:
while ((s = strtok(NULL, " \t\r\n")) != NULL) {
...
}
* Do not use an assignment expression as the condition in an "if", "while",
or "for" test; explicitly compare the result of the assignment against 0
or NULL, as appropriate.
* Do not use the result of a logical expression (operators &&, ||, ==,
etc.) as an operand in an arithmetic or binary expression.
* Always use NULL, not 0, in pointer comparisons. For character
comparisons, use 0 when checking for the null terminator at the end of a
string; use '\0' only if the intent is to check for the ASCII character
NUL as part of a string. Do not use '\0' with variables declared as type
int8_t or uint8_t, even though these types are usually equivalent to char
and unsigned char.
* The construct "!!expression" should not be used. Use "expression != 0"
(for characters or integers) or "expression != NULL" (for pointers)
instead.
* All exported functions must be prototyped in a header file.
* When declaring a function that takes no parameters, use "(void)" instead
of "()", since the latter does not declare a prototype in C. Example:
int function(void) /* Function that takes no parameters */
{
...
}
* Do not use the "inline" keyword. Most compilers can determine
automatically when inlining a function will be beneficial, and will do
so (if told to optimize) even without the "inline"; older compilers may
not understand "inline" at all.
* Functions should check all parameters for validity. However, in cases
where it is clear that a parameter is valid (for example, because the
function is only called by one other function under specific
circumstances), it is acceptable to assume the parameter is valid if that
assumption is listed as a precondition in the function's header.
* Variables should be declared in the innermost block which completely
encloses all uses of the variable. However, a loop counter in a nested
loop may be declared at a higher level if there are no other loop-local
variables at the appropriate level. Examples:
int function(int limit)
{
int foo = 0; /* used throughout the function */
int i; /* used in a top-level loop */
int j; /* used in a nested loop, no other locals */
for (i = 0; i < limit; i++) {
/* No loop-local variables here, so j is declared above */
for (j = 0; j < 3; j++) {
int x, y; /* only used inside the loop body */
otherfunction(i, j, &x, &y);
foo += x * y;
}
}
return foo;
}
* The goto statement should not be used except in error handling situations
where it will help avoid multiple levels of if nesting or other awkward
code. Labels for goto should be outdented half of an indentation unit
from the surrounding code (i.e., indented two columns less than the
surrounding code).
* assert() must not be used. If a sanity check fails, the program should
either recover as best it can or, if recovery is impossible, print a
meaningful error message and exit gracefully.
== Identifier naming ==
* Global variables (including variables declared static at the file level)
and type names should use mixed upper- and lower-case, with an upper-case
letter at the beginning of each distinct word in the name (including the
first).
* Preprocessor constants and macros should use all upper-case letters, with
an underscore between distinct words in the name.
* Structure names and local variables should use all lower-case letters,
with an underscore between distinct words in the name.
* Structure names for structures with an associated type name (i.e., from
typedef) should be given the same name as the type, except with all
letters in lowercase and followed by an underscore. Example:
typedef struct mytype_ MyType;
struct mytype_ {
MyType *next, *prev;
...
};
* Names should be descriptive. For global variables, preprocessor macros
and constants, type names, and structure names, names should generally
consist of one or more full words. For local variables, short names and
abbreviations are permitted as long as it is clear what the variables are
used for. In general, one-letter local variable names should not be used
other than the following:
* c: character
* f: file pointer (FILE *)
* i, j, k: integers (usually counters)
* n, p, q: integers (p may also be a pointer variable)
* s: string
* t: string or temporary variable
* x, y, z: integers (usually position variables)
== Transcode-specific features ==
* Always include config.h (if HAVE_CONFIG_H is defined) before including
any system includes, since config.h defines symbols such as
_FILE_OFFSET_BITS which can affect system includes. The following files
also include config.h:
transcode.h
aclib/aclib.h
avilib/avilib.h
avilib/wavlib.h
libtc/libtc.h
libtcaudio/tcaudio.h
libtcvideo/tcvideo.h
* All code which may be called from the main transcode executable
(including modules, libraries, the tcdecode/tcextract/etc. subprograms,
and their component source files) should use one of the tc_log_XXX()
macros from libtc for output intended to be seen by the user, including
debugging messages. There are four log levels available, and they
should be used as follows:
* tc_log_error()
For unrecoverable errors which cause the operation to abort.
* tc_log_warn()
For recoverable errors or other situations which may indicate
a problem but do not cause the operation to abort.
* tc_log_info()
For ordinary informational messages.
* tc_log_msg()
For debugging messages and less important informational
messages.
NEVER use printf() or any other functions which write to stdout for
these purposes.
* Do not use "\t" in strings for aligning text; use the proper number of
spaces instead.