standards: Semantics
1
1 4.2 Writing Robust Programs
1 ===========================
1
1 Avoid arbitrary limits on the length or number of _any_ data structure,
1 including file names, lines, files, and symbols, by allocating all data
1 structures dynamically. In most Unix utilities, "long lines are
1 silently truncated". This is not acceptable in a GNU utility.
1
1 Utilities reading files should not drop NUL characters, or any other
1 nonprinting characters. Programs should work properly with multibyte
1 character encodings, such as UTF-8. You can use libiconv to deal with a
1 range of encodings.
1
1 Check every system call for an error return, unless you know you wish
1 to ignore errors. Include the system error text (from 'strerror', or
1 equivalent) in _every_ error message resulting from a failing system
1 call, as well as the name of the file if any and the name of the
1 utility. Just "cannot open foo.c" or "stat failed" is not sufficient.
1
1 Check every call to 'malloc' or 'realloc' to see if it returned
1 'NULL'. Check 'realloc' even if you are making the block smaller; in a
1 system that rounds block sizes to a power of 2, 'realloc' may get a
1 different block if you ask for less space.
1
1 You must expect 'free' to alter the contents of the block that was
1 freed. Anything you want to fetch from the block, you must fetch before
1 calling 'free'.
1
1 If 'malloc' fails in a noninteractive program, make that a fatal
1 error. In an interactive program (one that reads commands from the
1 user), it is better to abort the command and return to the command
1 reader loop. This allows the user to kill other processes to free up
1 virtual memory, and then try the command again.
1
1 Use 'getopt_long' to decode arguments, unless the argument syntax
1 makes this unreasonable.
1
1 When static storage is to be written in during program execution, use
1 explicit C code to initialize it. This way, restarting the program
1 (without reloading it), or part of it, will reinitialize those
1 variables. Reserve C initialized declarations for data that will not be
1 changed.
1
1 Try to avoid low-level interfaces to obscure Unix data structures
1 (such as file directories, utmp, or the layout of kernel memory), since
1 these are less likely to work compatibly. If you need to find all the
1 files in a directory, use 'readdir' or some other high-level interface.
1 These are supported compatibly by GNU.
1
1 The preferred signal handling facilities are the BSD variant of
1 'signal', and the POSIX 'sigaction' function; the alternative USG
1 'signal' interface is an inferior design.
1
1 Nowadays, using the POSIX signal functions may be the easiest way to
1 make a program portable. If you use 'signal', then on GNU/Linux systems
1 running GNU libc version 1, you should include 'bsd/signal.h' instead of
1 'signal.h', so as to get BSD behavior. It is up to you whether to
1 support systems where 'signal' has only the USG behavior, or give up on
1 them.
1
1 In error checks that detect "impossible" conditions, just abort.
1 There is usually no point in printing any message. These checks
1 indicate the existence of bugs. Whoever wants to fix the bugs will have
1 to read the source code and run a debugger. So explain the problem with
1 comments in the source. The relevant data will be in variables, which
1 are easy to examine with the debugger, so there is no point moving them
1 elsewhere.
1
1 Do not use a count of errors as the exit status for a program. _That
1 does not work_, because exit status values are limited to 8 bits (0
1 through 255). A single run of the program might have 256 errors; if you
1 try to return 256 as the exit status, the parent process will see 0 as
1 the status, and it will appear that the program succeeded.
1
1 If you make temporary files, check the 'TMPDIR' environment variable;
1 if that variable is defined, use the specified directory instead of
1 '/tmp'.
1
1 In addition, be aware that there is a possible security problem when
1 creating temporary files in world-writable directories. In C, you can
1 avoid this problem by creating temporary files in this manner:
1
1 fd = open (filename, O_WRONLY | O_CREAT | O_EXCL, 0600);
1
11 or by using the 'mkstemps' function from Gnulib (⇒
(gnulib)mkstemps).
1
1 In bash, use 'set -C' (long name 'noclobber') to avoid this problem.
1 In addition, the 'mktemp' utility is a more general solution for
11 creating temporary files from shell scripts (⇒(coreutils)mktemp
invocation).
1