Some notes on defensive programming
              Mark Moraes, moraes@cs.toronto.edu

Program in modules -- declare everything in a module  static  un-
less  you  want  it  exported.  Don't export variables, only pro-
cedural interfaces.  Use header files to declare (and  document?)
exported  functions.  Always include a header file in a procedure
when using functions it exports.  Use  Posix  unistd.h  for  Unix
functions;  use  a  dummy  version on systems that don't have one
(eg. the one provided with Jove).

Consider declaring prototypes (with a proto() macro?) and using a
picky  ANSI  compiler  to  take advantage of them.  gcc -W -O and
lint can be good friends.  Other compilers with  excellent  warn-
ings  include  hc  on  the IBM PC/RT and Turbo C++ on the IBM PC.
Use printfck to check printfs -- SGI Irix cc has this  capability
and prototypes.

Check system call error returns.  Use error() or  strerror()  for
good error reporting.

Don't #ifdef DEBUG.  Use if (debug)  instead,  it  may  be  worth
planning  for  multiple debugging levels from the start.  Letting
each module have a debugging variable, like C news relaynews,  is
good practice.

Use a debugging malloc when developing.   If  using  CSRI_MALLOC,
insert  mal_verify(1) at suitable checkpoints if you can't afford
mal_debug(3).

When using routines that take pointers, check who is  responsible
for  storage  allocation.   What is the lifetime of that storage?
The use of automatics where you should be using  static  or  mal-
loced storage can cause hard-to-reproduce bugs.

Document a test procedure, attempt to collect a test  suite.   Is
it possible to run a test instance of your program in addition to
the installed version?  (i.e don't rely on a  config  file  being
installed  in  /etc/foobar -- allow runtime reconfiguration via a
few environment variable or command line options) For daemons  or
complex  multiprocess applications, plan for testing in captured,
controlled environments.  Think about taps for collection of real
world test data for regression.

Handle long lines gracefully.  In general,  dynamically  allocate
objects  and  avoid  static  limits.   If you have static limits,
check that you don't overrun them  and  recover  gracefully  from
them.  strsave(), str3save() are useful.

Avoid #ifdefs in C code.  They're a symptom of portability  prob-
lems,  not  a solution.  Instead, define a system dependent layer
if your application is very system dependent, so it's easy to re-
target it.  If you have to #ifdef, use feature-based ifdefs rath-
er than system #ifdefs.  eg. use #ifdef  HAS_DIRENT  rather  than
#ifdef  SYSV.   The world now consists of hybrid systems and it's
going to get worse.  Write system dependent modules so that  they
can  be  re-used; it greatly eases pain when moving to a new sys-
tem.

Keep a Change Log for the code, indicating *WHY* you changed  the
things you changed in addition to what you changed.

When working  on  code  that  exec's  other  programs,  use  Hugh
Redelmeier's  forktest (mod.sources/comp.sources.unix, Vol 7, Is-
sue 90) to check the uids, open file descriptors,  signal  states
etc.



	Mark.