The PennMUSH code style

javelin's picture

The PennMUSH devteam have adopted some coding conventions, which are listed below. If you use these conventions, your hacks are more likely to work on multiple systems, and will be easier for me to integrate into new patchlevels.

  • Use ANSI C style. All functions should be explicitly prototyped, and function definitions should use the ANSI C style. Although PennMUSH does contain some function definitions in the K&R style, they are being replaced by ANSI C definitions as the devteam rewrites sections of the code.
  • All source files should #include "config.h" before any other include file (except perhaps copyright.h) and should #include "confmagic.h" after all other include files. This lets the file take full advantage of the autoconfiguration script. You usually want to #include "conf.h" after "config.h" and any files, but before any other PennMUSH header file.
  • All header files should be idempotent. That's a fancy way of saying that they should be set up like this:
    #ifndef _MYFOO_H
    #define _MYFOO_H
    ...header file here...
    #endif  /* _MYFOO_H */

    This protects you if the file gets included twice by mistake.

  • If you're doing string handling, learn how to properly use the PennMUSH safe_* functions (e.g. safe_str, safe_chr, safe_integer, safe_format) to avoid buffer overflows.
  • Use ISO C 9899 functions when possible. For example, use memcpy in preference to bcopy, and strchr in preference to index
  • Signal handlers return Signal_t, defined through autoconfiguration. If SIGNALS_KEPT is defined, you don't have to reset signals in the handler.
  • malloc returns Malloc_t, and calls to free should have their parameters cast as (Malloc_t). I.e.: free((Malloc_t)buff);
  • Use mush_malloc(size to malloc,"name of mem_check") and mush_free((Malloc_t) ptr to free, "name of mem_check") when possible. These are macros which are preprocessed to plain old malloc/free when MEM_CHECK is not defined, and which call a function in memcheck.c to add/delete a mem_check before doing the malloc/free when MEM_CHECK is defined. You still need to add MEM_CHECK's manually if you get your memory by strdup or safe_uncompress or something.
  • Use the UPCASE() and DOWNCASE() macro to get the uppercase or lowercase versions of a character. The autoconfig checks whether toupper() can accept only lower-case letters, and defines UPCASE to protect toupper. If toupper() is safe, UPCASE() is defined as toupper() to be more efficient.
  • Learn about Penn's typedefs and macros, and use them when you can. This requires reading header files and examples in the source code.
  • The code follows a standard indentation scheme, which is documented in src/Makefile under the 'indent' target. It requires GNU indent 1.9 or later. You can use 'make indent' to reindent your code. Please re-indent before making context diffs for patches.
  • Document your code. As we revise the PennMUSH code, we're moving toward using doxygen ( http://www.doxygen.org ) to help us generate html documentation of the code.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Signals

Some changes since the above item on signals was written:

Signal handler functions have the prototype void foo(int signo); (Typedefed as Sigfunc). There's no Signal_t type anymore.

There are functions in src/sig.c for manipulating signals. You shouldn't be using signal(2)!

  • Sigfunc install_sig_handler(int signo, Sigfunc f): Add a signal handler. It returns any previous handler function, if any, or NULL (On an error, SIG_ERR). Signal handlers installed this way are made restartable if possible.

    The last line of the handler function should be:

  • void reload_sig_handler(ing signo, Sigfunc f): Reloads a signal handler if needed.
  • void ignore_signal(int signo): Ignore the given signal. Same as install_sig_handler(signo, SIG_IGN).
  • void block_a_signal(int signo) and void unblock_a_signal(int signo): Add and remove signals from the blocked list. A blocked signal with a handler function will continue to use the same handler after it's unblocked.
  • void block_all_signals(void): Blocks all signals. On Windows and other OSes without sigprocmask(2), if individual signals are later unblocked, any handler functions assigned to them may not be restored.

In general: Unless you know what you're doing with signals, don't mess with them. They're not very portable between Windows and UNIX, or even between older UNIXes. You've been warned.
For more information on them, see any Unix systems programming text. W. Richard Stevens' Advanced Programming in the UNIX Environment is suggested.

The C Language dialect

The base version of C used for Penn is the ANSI 1989/ISO 1990 standard. The ISO 1999 version is still not supported by Microsoft C compilers for Windows, so it's not used. However, any C99 or compiler-specific features that can easily be tested for and disabled if not supported can be considered for use.

C99 and compiler extensions currently used in Penn:

  • restrict. C99 pointer qualifier. Says that the only way memory pointed to by that pointer can be accessed is through that pointer. A prototype like memcpy(void* restrict dst, const void* restrict src, size_t n) says that dst and src cannot overlap. Used for optimization of memory access.
  • NORETURN after a function prototype. Used by GCC and MS C to indicate that the function will never return. Used for optimization of assembly output.
  • __attribute_malloc__: GCC extension. Used after a function prototype to indicate that the function returns a newly-allocated pointer. Used for optimization of memory access.
  • WIN32_CDECL: Microsoft extension. Put between the return type and the name of a function in both prototype and declaration, if the function will be passed as a pointer or has variable arguments. Used for optimization of assembly output.
  • __attribute__((__format__(__printf__,X,Y))): GCC extension. Used after the prototype of a variable-argument function that takes printf() style format and arguments. X is the position of the argument with the format, Y the position of the first variable argument of format data. See hdrs/log.h for an example. Used for better warnings.

<ctype.h> functions

The isFOO() character classification functions should never be passed a plain char. Characters need to be casted to unsigned char. In some environments, char is a signed type, and an accented or other non-ASCII character might end up as a negative number when stored in a plain char variable. With naive isFOO() implementations, this will result in a negative array index that tries to reference memory that might not be readable, causing a segfault. Don't do it!

Malloc_t

There's no reason to cast arguments to free() or mush_free() to Malloc_t. It's left over from the bad old days before void pointers and not needed for C89 or better compilers. (Penn is ancient!)

type widths

Penn runs on a variety of 32-bit and 64-bit systems. sizeof(int) is not always equal to sizeof(long) is not always equal to sizeof(void*). Avoid writing code that assumes so. If you need an exact width type, use the appropriate <stdint.h> typedef and put a check for it into configure.in (There are examples for uint16_t and uint32_t).

#define versus enum

Prefer enums over #defines for constants, especially those used as an option argument to functions. They're better for debugging and for compile-time warnings.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.