Created a coding standard to maximize cross platform and multiple compiler compatibility. I probably need to clean up some of the code to meet the standard! The standard was based on the Ethereal coding standard.
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
This software runs on many platforms, and can be compiled with a number of
|
||||
different compilers; here are some rules for writing code that will work
|
||||
on multiple platforms.
|
||||
|
||||
Don't use C++-style comments (comments beginning with "//" and running
|
||||
to the end of the line) for modules that are written in C. The module
|
||||
may run through C rather than C++ compilers, and not all C compilers
|
||||
support C++-style comments (GCC does, but IBM's C compiler for AIX, for
|
||||
example, doesn't do so by default).
|
||||
|
||||
Don't initialize variables in their declaration with non-constant
|
||||
values. Not all compilers support this. E.g. don't use
|
||||
uint32_t i = somearray[2];
|
||||
use
|
||||
uint32_t i;
|
||||
i = somearray[2];
|
||||
instead.
|
||||
|
||||
Don't use zero-length arrays; not all compilers support them. If an
|
||||
array would have no members, just leave it out.
|
||||
|
||||
Don't declare variables in the middle of executable code; not all C
|
||||
compilers support that. Variables should be declared at the beginning
|
||||
of a function or compound statement, or outside a function
|
||||
|
||||
Don't use "inline"; not all compilers support it.
|
||||
|
||||
Use the C99 stdint.h and stdbool.h definitions for declaring variables
|
||||
when needed. If they are not defined for your compiler, put those files
|
||||
into the ports directory for your compiler with the proper definitions.
|
||||
Sometimes scalable code should just use an int or unsigned declaration.
|
||||
8-bit unsigned = uint8_t
|
||||
8-bit signed = int8_t
|
||||
16-bit unsigned = uint16_t
|
||||
16-bit signed = int16_t
|
||||
32-bit unsigned = uint32_t
|
||||
32-bit signed = int32_t
|
||||
boolean = bool
|
||||
|
||||
Don't use "long" to mean "signed 32-bit integer", and don't use
|
||||
"unsigned long" to mean "unsigned 32-bit integer"; "long"s are 64 bits
|
||||
long on many platforms. Use "int32_t" for signed 32-bit integers and use
|
||||
"uint32_t" for unsigned 32-bit integers.
|
||||
|
||||
Don't use "long" to mean "signed 64-bit integer" and don't use "unsigned
|
||||
long" to mean "unsigned 64-bit integer"; "long"s are 32 bits long on
|
||||
many other platforms. Don't use "long long" or "unsigned long long",
|
||||
either, as not all platforms support them; use "int64_t" or "uint64_t",
|
||||
which need to be defined as the appropriate types for 64-bit signed and
|
||||
unsigned integers.
|
||||
|
||||
Don't use a label without a statement following it. For example,
|
||||
something such as
|
||||
|
||||
if (...) {
|
||||
|
||||
...
|
||||
|
||||
done:
|
||||
}
|
||||
|
||||
will not work with all compilers - you have to do
|
||||
|
||||
if (...) {
|
||||
|
||||
...
|
||||
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
with some statements, even if it's a null statement, after the label.
|
||||
|
||||
Don't use "bzero()", "bcopy()", or "bcmp()"; instead, use the ANSI C
|
||||
routines
|
||||
|
||||
"memset()" (with zero as the second argument, so that it sets
|
||||
all the bytes to zero);
|
||||
|
||||
"memcpy()" or "memmove()" (note that the first and second
|
||||
arguments to "memcpy()" are in the reverse order to the
|
||||
arguments to "bcopy()"; note also that "bcopy()" is typically
|
||||
guaranteed to work on overlapping memory regions, while
|
||||
"memcpy()" isn't, so if you may be copying from one region to a
|
||||
region that overlaps it, use "memmove()", not "memcpy()" - but
|
||||
"memcpy()" might be faster as a result of not guaranteeing
|
||||
correct operation on overlapping memory regions);
|
||||
|
||||
and "memcmp()" (note that "memcmp()" returns 0, 1, or -1, doing
|
||||
an ordered comparison, rather than just returning 0 for "equal"
|
||||
and 1 for "not equal", as "bcmp()" does).
|
||||
|
||||
Not all platforms necessarily have "bzero()"/"bcopy()"/"bcmp()", and
|
||||
those that do might not declare them in the header file on which they're
|
||||
declared on your platform.
|
||||
|
||||
Don't use "index()" or "rindex()"; instead, use the ANSI C equivalents,
|
||||
"strchr()" and "strrchr()". Not all platforms necessarily have
|
||||
"index()" or "rindex()", and those that do might not declare them in the
|
||||
header file on which they're declared on your platform.
|
||||
|
||||
Don't fetch data from packets by getting a pointer to data in the
|
||||
packet, casting that pointer to a pointer to a structure,
|
||||
and dereferencing that pointer. That pointer won't necessarily be aligned
|
||||
on the proper boundary, which can cause crashes on some platforms (even
|
||||
if it doesn't crash on an x86-based PC). This means that you cannot
|
||||
safely cast it to any data type other than a pointer to "char",
|
||||
"unsigned char", "uint8_t", or other one-byte data types. You cannot,
|
||||
for example, safely cast it to a pointer to a structure, and then access
|
||||
the structure members directly; on some systems, unaligned accesses to
|
||||
integral data types larger than 1 byte, and floating-point data types,
|
||||
cause a trap, which will, at best, result in the OS slowly performing an
|
||||
unaligned access for you, and will, on at least some platforms, cause
|
||||
the program to be terminated.
|
||||
|
||||
The data in a packet is not necessarily in the byte order of
|
||||
the machine on which this software is running. Make use of
|
||||
big_endian() which returns non-zero on big_endian machines.
|
||||
|
||||
Use "ntohs()", "ntohl()", "htons()", or "htonl()" only in the ports
|
||||
directories since the header files required to define or declare
|
||||
them differ between platforms. There are some common functions in
|
||||
the bacdcode library for converting to and from long and short.
|
||||
|
||||
Don't put a comma after the last element of an enum - some compilers may
|
||||
either warn about it (producing extra noise) or refuse to accept it.
|
||||
|
||||
When opening a file with "fopen()", "freopen()", or "fdopen()", if the
|
||||
file contains ASCII text, use "r", "w", "a", and so on as the open mode
|
||||
- but if it contains binary data, use "rb", "wb", and so on. On
|
||||
Windows, if a file is opened in a text mode, writing a byte with the
|
||||
value of octal 12 (newline) to the file causes two bytes, one with the
|
||||
value octal 15 (carriage return) and one with the value octal 12, to be
|
||||
written to the file, and causes bytes with the value octal 15 to be
|
||||
discarded when reading the file (to translate between C's UNIX-style
|
||||
lines that end with newline and Windows' DEC-style lines that end with
|
||||
carriage return/line feed).
|
||||
|
||||
In addition, that also means that when opening or creating a binary
|
||||
file, you must use "open()" (with O_CREAT and possibly O_TRUNC if the
|
||||
file is to be created if it doesn't exist), and OR in the O_BINARY flag.
|
||||
That flag is not present on most, if not all, UNIX systems, so you must
|
||||
also do
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
to properly define it for UNIX (it's not necessary on UNIX).
|
||||
|
||||
Don't use forward declarations of static arrays without a specified size
|
||||
in a fashion such as this:
|
||||
|
||||
static const value_string foo_vals[];
|
||||
|
||||
...
|
||||
|
||||
static const value_string foo_vals[] = {
|
||||
{ 0, "Red" },
|
||||
{ 1, "Green" },
|
||||
{ 2, "Blue" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
as some compilers will reject the first of those statements. Instead,
|
||||
initialize the array at the point at which it's first declared, so that
|
||||
the size is known.
|
||||
|
||||
Don't put declarations in the middle of a block; put them before all
|
||||
code. Not all compilers support declarations in the middle of code,
|
||||
such as
|
||||
|
||||
int i;
|
||||
|
||||
i = foo();
|
||||
|
||||
int j;
|
||||
|
||||
For #define names and enum member names, prefix the names with a tag so
|
||||
as to avoid collisions with other names - this might be more of an issue
|
||||
on Windows, as it appears to #define names such as DELETE and
|
||||
OPTIONAL.
|
||||
|
||||
Don't use "variadic macros", such as
|
||||
|
||||
#define DBG(format, args...) fprintf(stderr, format, ## args)
|
||||
|
||||
as not all C compilers support them. Use macros that take a fixed
|
||||
number of arguments, such as
|
||||
|
||||
#define DBG0(format) fprintf(stderr, format)
|
||||
#define DBG1(format, arg1) fprintf(stderr, format, arg1)
|
||||
#define DBG2(format, arg1, arg2) fprintf(stderr, format, arg1, arg2)
|
||||
|
||||
...
|
||||
|
||||
or something such as
|
||||
|
||||
#define DBG(args) printf args
|
||||
|
||||
Instead of tmpnam(), use mkstemp(). tmpnam is insecure and should
|
||||
not be used any more. Note: mkstemp does not accept NULL as a parameter.
|
||||
|
||||
Try to write code portably whenever possible, however; note that
|
||||
there are some routines in the software that are platform-dependent
|
||||
implementations. The platform independent API is declared in the
|
||||
header file, and the dependent routine is placed in a ports directory.
|
||||
|
||||
Reference: The cross platform aspect of this coding standard is based
|
||||
on the developer coding standard for Ethereal and has been modified
|
||||
by Steve Karg for this project. Thank you, Ethereal!
|
||||
Reference in New Issue
Block a user