mirror of
				https://github.com/stargieg/bacnet-stack
				synced 2025-10-26 23:35:52 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			236 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| 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.
 | |
| 
 | |
| Regarding tabs, indenting, and code style, we run:
 | |
| $ indent -kr -nut -nlp -ip4 -cli4 -bfda -nbc -nbbo -c0 -cd0 -cp0 -di0 -l79 filename.c
 | |
| on the code prior to releasing it.  This ensures a standard look and feel
 | |
| to the code regardless of the authors preferred style.  You may certainly
 | |
| adjust the code to your preferred style using an indent tool.  We use the
 | |
| script indent.sh to adjust all the .c and .h files.
 | |
| 
 | |
| For variable names, separate words within the variables by underscores.
 | |
| Do not use capital letters as separators. Consider how much harder
 | |
| IcantReadThis is on the eyes versus I_can_read_this.
 | |
| 
 | |
| Variable and function names are defined with the first words being
 | |
| descriptive of broad ideas, and later words narrowing down to specifics.
 | |
| For instance: Universe_Galaxy_System_Planet. Consider the following names:
 | |
| Timer_0_Data, Timer_0_Overflow, and Timer_0_Capture. This convention
 | |
| quickly narrows variables to particular segments of the program.
 | |
| Never assume that a verb must be first, as often seen when naming functions.
 | |
| Open_Serial_Port and Close_Serial_Port do a much poorer job of grouping
 | |
| than the better alternative of Serial_Port_Open and Serial_Port_Close.
 | |
| 
 | |
| 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).  Note: there is an application
 | |
| called usr/bin/ccmtcnvt in the liwc package that converts the C++
 | |
| comments to C comments.  There is a script utilizing ccmtcnvt called
 | |
| comment.sh created for this project that searches all the c and h files
 | |
| for C++ headers and converts them.
 | |
| 
 | |
| 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/Wireshark and has been
 | |
| modified by Steve Karg for this project.  Thank you, Ethereal/Wireshark!
 | 
