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.
