Contents
Contents
Professional software developers recognize the value and necessity of documenting the procedures that they have developed to backup their memories and to assist in setting up new computers or passing on to others their own expertise.
The intent of this document is not to describe policies in a development group setting, but rather to act as a vehicle for discussion purposes to describe activities as they may already occur and recommended extensions to those activities. The goal is to improve the efficiency and efficacy of the software development group by documenting procedures to supplement individual memories and assist in training new members.
The procedures and guidelines in this document are oriented to software development using industry standard architecture microcomputer hardware and software tools for programming in C. The example tools selected are illustrative only and do not constitute a recommendation.
In 1983, I picked up a copy of The C Programming Language and began to learn how to program in C. My previous experience programming in APL, BASIC, COBOL, FORTRAN, PASCAL, Xerox Sigma assembly, 6502 assembly, and 8080 assembly made it easy to learn the language from this book which is usually considered unsuitable for teaching. The C Programming Language, Second Edition is even better and the ANSI standardization of the language is a welcome addition.
I immediately fell in love with C. The language fascinated me because it was as logical as APL, but far more structured. In some ways C is simple like BASIC, but it is far more powerful. C has more structure than COBOL, and without the verbosity. It handles numbers almost as well as FORTRAN, without the archaic design limitations. It has the elegance of PASCAL, but is more symbolic and less wordy. It is almost as flexible and powerful as assembly language, yet it has more structure and is highly portable.
There were times when I would hate C. Its free-wheeling flexibility which makes it more attractive than PASCAL can sometimes degenerate into anarchy. It is embarrassing to be confronted with the following sample code.
{
int i;
int a[] = 0, 1, 2, 3, 4, 5;
i = 5;
printf ("%d %d %d", a[5], 5[a], i[a]);
}
It is embarrassing to admit that, yes the code works and no, the compiler won't complain. It is difficult to explain to new C programmers the reason why C does not treat arrays the way you might expect. Then you try to explain about using LINT and mouth the old cliche that "Real programmers don't program that way."
I don't like C++. Superficially it is an improvement to the language but it increases the complexity dangerously close to being un-maintainable. The forced rigidity of the class definition only formalizes the methods of encapsulation that the best C programmers have always used with standard C. The implementation is clunky and not truly an improvement.
A set of guidelines for C programming is necessary to bring some consistency and order to the simple definition of the language. Guidelines can emphasize the procedures necessary to improve readability, maintainability, flexibility, robustness, modularity, functional decomposition, information hiding, interfaces, and other programming methods. The ANSI standardization of the language provided some very important and necessary features to improve programming style.
This document illustrates a personal set of C programming guidelines I began developing as soon as I started programming in C. I had previously been exposed to and developed guidelines for other programming languages. I have learned over the years that it is too easy to just do things without keeping track of what was done. Anything that is done that is not written down and recorded for later reference is wasted and lost.
These guidelines are based on a specific philosophy and approach to programming. This philosophy and approach may not exactly match your own goals. This document should NOT be taken as an authoritative and inflexible standard. Earlier, privately distributed, versions of this document used the title C Programming Standards which was confusing and misleading. After I read Plum's C Programming Guidelines in 1990, I agreed with his own reasoning on the subject and changed the title as well as expanded the scope. Plum's book is recommended reading for anyone interested in this subject.
Use this document to provide ideas and material for developing personalized software development procedures and C programming guidelines. Any experienced programmer can probably add their own little bits of wisdom to this collection. Different approaches can readily lead to different guidelines which are just as valid as these. It is just as important to have guidelines as it is to decide the content of those guidelines.
I want to express my appreciation to the many friends and associates who have provided numerous comments and criticisms over the years as the material which went into this document has evolved. Regrettably, their names are too numerous to mention them all. Thank you.
It is important to the efficiency of any software development group to have defined procedures and configuration guidelines for a development computer setup. These guidelines apply only to the minimum files and directories that would appear on all systems. Additional files and directories are at the discretion of the person who primarily uses a particular computer.
These procedures will make it easier to transfer coding assignments and responsibilities, perform long term maintenance, train new people on the system, perform disaster recovery, and set up new or replacement systems.
When implementing department or corporate programming procedures and guidelines, it is important to remember that programmers are individuals, and each has their own style, background, and experience. The use of an automatic formatter for C software code can be invaluable in allowing programmers flexibility with their own programming style and have submitted results reformatted to the guidelines. Brace placement and indentation are invariably the most hotly discussed topics and the easiest to have automatically reformatted. Such a formatter must have the flexibility to change code that is checked out back to the style that is the most comfortable for the individual programmer.
It is also important to note that the programming guidelines and procedures in this document represent only a subset of what is needed for complete departmental procedures. There are many issues of configuration management, metrics, documentation, and validation which have not been addressed in these guidelines. The information in this document is only a small beginning.
Programming guidelines go beyond syntactic accuracy to provide consistency and encourage higher quality code which is easier to develop, read, and maintain. This class of guidelines is based on programming philosophies. Different philosophies result in different guidelines. The reasons for many of these points is presented to encourage understanding in how they were derived.
The most important philosophy is readability. A person who understands C code should be able to call a new code listing up on the display and be able to read through it as easily as others read through a novel. Many times the same organizational concepts of a novel are applied to code.
The next most important philosophy is tutorial. The simple fact that is that while many people know how to program, they do not know how to program well. One of the things that education sometimes fails to do is teach organization and presentation of code. Therefore, I have adopted the practice of making every program an example and an opportunity to teach and train.
Some professors lack a deep experience in quality programming. This is most evident in large programming projects. A small project of a few thousand lines in a couple of files simply does not tax the understanding enough to force development of good organization. Most educational projects are relatively tiny.
Another overall philosophy behind these guidelines is compactness. These guidelines will not produce code that can be nicely printed on standard 11 by 14 computer green bar paper. These guidelines assume that code is viewed on a typical video display approximating the old 25 line by 80 column screen while using an editor with all its search functions to locate and display areas of interest. The use of paper to view code is archaic and defeats the power that the computer can offer. For example, the ideal code review would have each participant looking at the code on their own video display.
Examples are provided where they can contribute to
understanding.
All examples are abbreviated for compactness.
Examples may be incomplete and should be expected to
illustrate only the current point.
Examples and C keywords are shown in a special
monospace type.
Complete sample programs illustrating most points as a whole are shown in the appendices.
It is absolutely vital, as a part of any C programming guidelines, to require some type of LINT pre-processing at the most stringent levels possible. Any diagnostic messages must be resolved. The compiler should also be instructed to perform the most stringent checks possible and all diagnostic messages must be resolved.
The reader is assumed to have an intimate familiarity with the C language and with the book The C Programming Language by Kernighan and Ritchie. The points presented here are in the order of relevance as they would apply in the reading of a source file from top to bottom. This order is also a logical progression from minimal understanding of the program to full understanding with the overview of important points appearing before the details.
The overwhelming prevalence of the rolling mouse often causes it to be selected.
The choice of program editor is at the discretion of the developer. The Microsoft Editor comes with the Microsoft C package. Since this editor has almost all the features of other editors and some that few others have and comes free it is my personal choice.
It is important to remember that all software registration materials must be filled out and mailed in. It is recommended to make a copy of the registration materials before mailing them and file them in the manual.
In a software development group where there is inevitably employee turnover and transfers it is desirable to have all the software registered to the name "software librarian" so that updates and notices don't get lost when people leave.
The original disks in any software package should be immediately write protected and they should never be write enabled. The original disks should only be used for the installation and then stored in a safe place.
The installation and set up for each of the possible software applications is described separately. Each command and each keystroke necessary for installation will be listed and described. Each input is usually ended by typing the ENTER key. It is always important and necessary to read the information and prompts on the screen. Software inevitably goes through periodic revisions and the installation methods described are not expected to be valid for very long.
Backups should be performed on a periodic basis commensurate with need, based on the frequency of revision of the files. On the average system a total backup of all user generated files should be performed once a week. This procedure is made simpler and faster by segregating installed programs from user generated files in separate subdirectory trees. This will allow simpler selection of the appropriate files. The batch file BACKITUP automates the backup process. Tape backups and network backup procedures are more convenient and would be similar.
MS-DOS version 4.01
The procedure to install MS-DOS version 4.01 is as follows:
Repeat the above process using EDLIN to create a file for C:\AUTOEXEC.BAT, C:\UTILITY\MENU.BAT, C:\UTILITY\MENU.DAT, AND C:\UTILITY\BAS.BAT C:\UTILITY\BACKITUP.BAT. Alternatively these files can be copied from a setup disk that can be made generally available.
Remove the floppy disk from the drive and reboot the computer.
The following directory structure will be produced.
Directory C:\ C:\IO.SYS C:\MSDOS.SYS C:\CONFIG.SYS files=30 buffers=20,8 shell=\command.com /p /e:512 install=c:\dos\share.exe install=\utility\mouse.com ser bhigh s10 device=c:\dos\smartdrv.sys 2048 C:\AUTOEXEC.BAT echo off cls rem rem check for a rerun of autoexec.bat rem if not "%boot_drive%"=="" goto restart rem rem minimize the environment for the tsr's rem set path= set comspec= rem rem install the tsr's first rem \utility\history >nul rem rem set all the environment variables rem rem >@@@@@@@@.bat prompt set boot_drive=$n: command /c @@@@@@@@.bat >@@@@@@@@.bat call @@@@@@@@.bat del @@@@@@@@.bat rem rem things redone on a rerun of autoexec.bat rem :restart set comspec=%boot_drive%\command.com path %boot_drive%\dos path %path%;%boot_drive%\utility prompt $d,$t $p$g menu Directory C:\DOS . . . Directory C:\BASIC . . . Directory C:\UTILITY C:\UTILITY\MENU.BAT @echo off cls %boot_drive% cd \ echo: echo: echo Valid applications: echo: echo BAS - Call BASIC echo WP filename echo PRODIGY echo WIN - windows echo: C:\UTILITY\BAS.BAT c: cd \basic gwbasic setup menu C:\UTILITY\BACKITUP.BAT c: cd \ backup c:\*.* a: backup c:\bas\*.* a: /a /s backup c:\utility\*.* a: /a /s
The purpose of the UTILITY directory is to hold all the little programs and batch files that people tend to accumulate to accomplish various tasks. In particular it is important to note that many applications do not have to be added to the PATH when there is a batch file to change directories and call them up. This reduces the size of the PATH, its memory use and processing time. A batch file can also include additional standard startup commands and is preferable to using SET commands where possible.
One megabyte of the 3M available extended memory could be converted to expanded memory to be used by BUFFERS, FASTOPEN, and any application which can use it. This places some overhead on MS-DOS function calls which I have chosen to avoid, particularly since there are few programs that use expanded memory.
The balance of the extended memory is allocated to a disk cache which speeds up disk operations almost as fast as RAMDRIVE but does not have any of the problems of worrying about disk updating and the inevitable overflow of allocated space.
WordPerfect version 5.1
The procedure to install WordPerfect version 5.1 differs slightly whether you are using 5 1/4 inch or 3 1/2 inch floppy disks. The major difference is the prompts for inserting the disks. These prompts are not mentioned in the following description.
The procedure to install WordPerfect version 5.1 is:
There are numerous setup options within WordPerfect that can be customized. It is not necessary to add WP51 to the path because it can be called better with a batch file from the standard utility directory.
The addition of the LaserJet printer requires special files as described under the printer setup procedure.
The new and changed files and subdirectories this process creates on the hard disk are:
Directory C:\ Directory C:\WP51 . . (WordPerfect program files) . Directory C:\WP51\DOC . . (Your WordPerfect document files) . Directory C:\UTILITY C:\UTILITY\WP.BAT @echo off c: cd \wp51\doc \wp51\wp %1 menu C:\UTILITY\MENU.BAT ... echo Applications: echo: echo WP [filename] - prepare a document for editing BAS - call basic ... C:\UTILITY\BACKITUP.BAT c: cd \ backup c:\*.* a: backup c:\bas\*.* a: /a /s backup c:\utility\*.* a: /a /s backup c:\wp51\doc\*.* a: /a /s
Microsoft Macro Assembler 5.1
The procedure to install Microsoft Macro Assembler 5.1 on a development computer is:
The new and changed files and subdirectories this process creates on the hard disk are:
Directory C:\ C:\AUTOEXEC.BAT ... path c:\bin;c:\dos;c:\utility PROMPT $d,$t $p$g menu Directory C:\UTILITY C:\UTILITY\BACKITUP.BAT c: cd \ backup c:\*.* a: backup c:\bas\*.* a: /a /s backup c:\utility\*.* a: /a /s . . . backup c:\masm\*.* a: /a /s Directory C:\BIN ...
The normal command for performing an assemble is:
MASM /W2 /ML filename;
The highest error checking level is requested and symbols are selected to be case sensitive.
Microsoft C 5.1 Optimizing Compiler
The procedure to install Microsoft C 5.1 Optimizing Compiler (and the Microsoft Editor) on a development computer is:
The new and changed files and subdirectories this process creates on the hard disk are:
Directory C:\
C:\AUTOEXEC.BAT
...
rem
rem set up the environment
rem
path=c:\bin;%path%
set include=c:\bin\include
set lib=c:\bin\lib
set cl=/W3 /Zi /AL /J /Za /I \c
set tmp=c:\tmp
set temp=%tmp%
set init=c:\utility\me
...
Directory C:\UTILITY
C:\UTILITY\BACKITUP.BAT
c:
cd \
backup c:\*.* a:
backup c:\bas\*.* a: /a /s
backup c:\utility\*.* a: /a /s
...
backup c:\c\*.* a: /a /s
Directory C:\C
C:\C\STANDARD.H
/******************************************************************************
standard.h
Version modified on Thu Mar 29, 1990 15:37:11 by Frank T. Clark
******************************************************************************/
/* the return value on these functions can be ignored */
/*lint -esym(534,int86,putch) dos.h */
/*lint -esym(534,strtok) string.h */
#define EXIT_SUCCESS 0 /* ANSI standard missing stdlib */
#define EXIT_FAILURE 1 /* ANSI standard missing stdlib */
typedef enum
{
FALSE,
TRUE
} LOGIC;
Directory C:\BIN
...
The normal command for performing a compile is:
CL filename
PC-LINT version 4.00
The procedure to install PC-LINT version 4.00 on a development computer is:
The new and changed files and subdirectories this process creates on the hard disk are:
Directory C:\UTILITY C:\LINT.BAT \lint\lint \lint\co-msc +fcu -A -mL +libclass(all) %1 Directory C:\LINT ...
The command for performing a LINT analysis is:
LINT filename.C
Every source file developed in C must pass LINT error checking without generating any diagnostic messages. LINT commands for message suppression within the source file are not allowed without specific approval. Refer to the STANDARD.H include file for examples of LINT control commands.
C-Clearly version 1.50
The procedure to install C-Clearly version 1.50 on a development computer is:
The new and changed files and subdirectories this process creates on the hard disk are:
Directory C:\UTILITY C:\CCL.BAT \cclearly\ccl %1 %2 %3 %4 %5 %6 %7 %8 Directory C:\CCLEARLY ...
The command for reformatting a C source file is:
CCL filename.C
There are numerous options which should be set to your own personal specifications.
Polytron Version Control System - Network version 2.1c
The procedure to install Polytron Version Control System - Network version 2.1c on a development computer is:
The new and changed files and subdirectories this process creates on the hard disk are:
Directory C:\ C:\AUTOEXEC.BAT ... rem rem set up the environment rem path=%path%;c:\pvcs set vcsid=Your Name set vcscfg=c:\pvcs\vcs.cfg ... Directory C:\PVCS C:\PVCS.CFG # # \pvcs\sr1\ip\vcs.cfg # # configuration options for IP project # accesslist=Administrator noautocreate branchwarn checklock commentprefix .asm = "" commentprefix .bat = "rem " commentprefix .c = "" commentprefix .cfg = "# " commentprefix .h = "" commentprefix .mak = "# " noctrlz deletework noexclusivelock expandkeywords touch noexpandkeywords .exe noexpandkeywords .obj forceunlock ignorepath logsuffix=??v___ logwork pathseparator = / semaphore verbose vcsdir=c:\pvcs\;\ c:\pvcs\sr1\ip\lib;\ c:\pvcs\sr1\ip\comm;\ c:\pvcs\sr1\ip\eprom;\ c:\pvcs\sr1\ip\protocol;\ c:\pvcs\sr1\ip\startup;\ c:\pvcs\sr1\ip\control;\ c:\pvcs\sr1\ip\maintain;\ c:\pvcs\sr1\ip\disks;\ c:\pvcs\sr1\ip\external workdir=c:\tmp writeprotect
All software source code, libraries, data files, make files, and any other file that goes on a distribution disk or is used to generate the disk or anything on the disk must be under version control. Refer to the chapter on version control for specific procedures. Some sample procedures involving the use of this software are described as follows:
Create the initial version control logfile:
VCS -I C:\PVCS(SAMPLE.C)
Put a work file into a version control logfile and delete it:
PUT (SAMPLE.C)
Put all work files in the current directory (if locked and changed) and delete them:
PUT (*.*)
Get a work file for editing:
GET -L (SAMPLE.C)
Get a write protected work file for reference use but only if the file is newer than the one we may already have:
GET -U (SAMPLE.C)
Assign a version label prior to distribution:
VCS -V"SAMPLE 1.11" @SAMPLE.LST
Find differences between the workfile and current checked in revision:
VDIFF SAMPLE.C -R
Find differences between all files in the current directory and current checked in revisions:
FOR %F IN (*.*) DO VDIFF %F -R
Procomm Plus version 1.1B
The procedure to install Procomm Plus version 1.1B on a development computer is:
Place the appropriate Procomm Plus program disk in the A: floppy drive.
The new and changed files and subdirectories this process creates on the hard disk are:
Directory C:\UTILITY C:\UTILITY\PCPLUS.BAT set pcplus=c:\pcplus\ c:\pcplus\pcplus set pcplus= Directory C:\PCPLUS ...
Every attempt has been made and will continue to be made to make the procedures simple and automatic. Software developers act independently in a trusted capacity to perform the suggested version control procedures without requiring excess interaction with other individuals.
Introduction
These procedures are based on a common model that has inputs, a process, and a result. Some results are intermediate steps and become the inputs to other processes. The inputs are programmer entered data and, optionally, some number of PVCS files. The process involves editing, compilation, assembly or other steps. The result is a single PVCS file.
Any result file that is created by a non-trivial process, has a make file checked into PVCS which automates that process as far as possible. An example of a trivial process would be for a data file requires only editing. A make file is a set of instructions for making a result file. The make file has the same name as the result file and an extension of MAK.
Every result file that has other PVCS files as input has a list file checked into PVCS. The list file contains a list of all the file names used to create the result file including the list file itself, the make file, and the result file. The list file has the same name as the result file and an extension of LST.
The name of the primary input file, the list file, the make file, and the result file should be the same.
Overview
Each step of the development process is controlled by locking the result file and any input files that will be changed by the programmer entered data. The changed result file and all the input files are given a version label which is unique to that result file. The description given when any modified files are checked back into PVCS describes the programmer entered data that went into the process.
There are several distinct types of processes which are classified based on the result file. The result file can be an object file, a library file, a data file, or an executable program.
Every file or program that is released for use by any software developer for anyone else to use, including another software developer must pass through version control. Any file or program that is released outside of the software development group must be in the form of an approved release disk with the contents defined by PVCS. Every release disk will contain a release marker file defining the name of the release disk and the version label. The release marker file is used to track the release disk within PVCS. Every file on a release disk must come from PVCS.
The PVCS data should be stored on a central network. The location of the data on the network drive is yet to be determined since we don't have a network yet. The drive and directory N:\PVCS\SR1\IP will be used to illustrate the following examples. As far as possible it is benificial to use unique file names across the entire project. This will allow you to skip naming the subdirectories in the each command line.
Example files
The following simple example illustrates all the files necessary for a small system that includes one program called STARTUP and one release disk called IPDISK.
STARTUP.LST (STARTUP.LST) (STARTUP.MAK) (STARTUP.C) (FUNCTION.C) (PROTOCOL.H) (IPLIB.LIB) (STARTUP.OBJ) (FUNCTION.OBJ) (STARTUP.REL) STARTUP.MAK # # startup.mak # lint=\lint\co-msc +fcu -A -mL +libclass(all) cl= /W3 /J /Za /AL /c startup.obj: startup.c protocol.h \lint\lint $(lint) startup.c chmod -w startup.obj cl $(cl) startup.c function.obj: function.c protocol.h \lint\lint $(lint) function.c chmod -w function.obj cl $(cl) function.c startup.rel: startup.obj function.obj iplib.lib chmod -w startupt.rel link startup.obj,startup.rel,nul,iplib.lib/NOD; IPDISK.LST (IPDISK.LST) (IPDISK.BAT) (IPDISK.DSK) (STARTUP.REL) IPDISK.BAT @echo off rem rem ipdisk.bat rem if "%1" == "a" goto :ok if "%1" == "b" goto :ok echo You must specify a drive letter exit echo Drive %1: must contain a blank formatted disk pause copy ipdisk.dsk %1: copy startup.rel %1: IPDISK.DSK $Logfile$ $Revision$
Basic development procedure
The person who locks the result file becomes the primary developer responsible for that result file until they finish their revisions and unlock the file. The primary developer receives priority on input file locks for that result file. If another developer needs to lock files that are already locked, they follow alternate procedures described below.
The basic development procedure consists of the following steps. The program STARTUP.EXE is shown as an example result file. The steps would be similar irrespective of the result file.
Create working directory
Create a working subdirectory for this process. The name of the subdirectory should be the same as the name of the result file.
MD C:\C\STARTUP
CD C:\C\STARTUP
Version determination
Lock the result file and note the current revision for use in the version label. The version label should contain the result file name, and the next revision number.
VCS -L (STARTUP.EXE)
rev 1.0
SET VERSION="STARTUP.EXE 1.1"
List retrieve
Get the list file.
GET (STARTUP.LST)
Mark inputs
Mark the next version in all current log files. (Note that the %VERSION% described below can only be used in a batch file.)
VCS -V%VERSION% @STARTUP.LST
Get files
Get all the files for this version.
GET -V%VERSION% @STARTUP.LST
Lock files
Get and lock the input files to be changed. A list of all the modified files must be kept or created when finished with VDIFF.
GET -V%VERSION% -L (STARTUP.C)
If PVCS informs you that someone else has the file locked you will need to coordinate with them and what they are doing.
Revise
Perform the revisions and testing. When done it may be necessary to retest with any recent changes to non-locked PVCS input files.
MAKE STARTUP.MAK
Put input files
Put and label the current version of all locked input files. (Note that this changes the version label to point to the new revision.)
PUT -V%VERSION% (STARTUP.C)
Put result file
Put and label the current version of the current result file. (Note that this changes the version label to point to the new revision.)
PUT -V%VERSION% (STARTUP.EXE)
Cleanup
Clear the working directory of all files and delete it.
XRD C:\C\STARTUP Y
Shortcuts
The complete procedure described above is only necessary in a large, mature project composed of several team members. It is possible to shortcut many of the steps if the proper care is taken and you know what you are doing. This is particularly appropriate during the initial development and if you know that you will not be in conflict with anyone using the files at the same time.
The individual locking of files can be skipped and you can just lock all input files at the beginning or end of the current task. The assigning of version labels is not necessary until you begin releasing disks and it becomes critical to track configurations. Some example shortcuts are shown below.
Initial setup
This example illustrates the creation of a brand new program called STARTUP which will go into the VCS subdirectory STARTUP.
MD C:\C\STARTUP
CD C:\C\STARTUP
EDLIN STARTUP.C
...
MAKE STARTUP.MAK
DEL *.BAK
VCS -I -T"Startup program" N:\PVCS\SR1\IP\STARTUP(*.*)
PUT (*.*)
CD ..
RD STARTUP
Modification
This example shows a modification to the STARTUP program.
MD C:\C\STARTUP
CD C:\C\STARTUP
GET (STARTUP.LST)
GET -L -N@STARTUP.LST
CHMOD -W *.*
...
MAKE STARTUP.MAK
VCS -L @STARTUP.LST
PUT -M"Revision description" -N @STARTUP.LST
DEL *.*
CD ..
RD STARTUP
There will be a lot of error messages for attempted changes to files that are not yours but watch them carefully and there will be no problem.
Alternate Procedures
If a file is already locked by a developer and needed by another developer, the second developer follows an alternate procedure which they coordinate with the primary developer. The version label which the secondary developer attaches to the files contains their initials in addition to the normal data.
When the primary developer finishes their work, the secondary developer locks the file, resolves any differences with current code, updates and integrates to current code levels, changes the version labels and becomes the primary developer.
The details of this process will be defined and elaborated later.
Software Development Release Disks
The final development step is creating software development release disks. Software development release disks are 3.5 inch double sided, high density (1.44M) floppy media and contain all the files necessary for system testing and software delivery. Creating the release disks requires a PVCS "GET" of all the files associated with the release marker file and running the batch file to make the disks.
This example illustrates the process of creating a fictional release disk which we shall call PRODUCTION:
Create directory
Create a working directory for the files.
MD \C\IPDISK
CD \C\IPDISK
Get list file
Get the list file for creating the disk.
SET VERSION="IPDISK 1.08"
GET -V%VERSION% N:PVCS(IPDISK.LST)
Get files
Get all the proper version files for creating the disk.
GET -V%VERSION% @IPDISK.LST
Build
Build the release disk set.
IPDISK
Cleanup
Delete the working directory.
CHMOD -W *.*
DEL *.*
CD \
RD \IPDISK
The disk(s) should be clearly marked with the version label of the PRODUCTION PVCS file. An example of a complete disk label would be:
Serono-Baker Diagnostics, Inc.
SR1-B Phase II software
"IPDISK 1.08"
Software development release disk
The ideal comment ("//") was introduced by C++.
Terminating the comment at the end of line was
one of the few good ideas in the language.
It is better to write in completely ANSI standard C.
The one exception is the double slash comment.
Always follow the double slash with a space
before any comment text.
The one exception is the double slash comment.
There is little chance for error or confusion
with a comment that ends automatically at the
end of the line.
Example:
The block double slash comment is a line of all slashes.
Example:
An obvious exception is that comments for PC-LINT processing prohibit a space at the beginning of the
comment inside the comment delimiter.
Example:
A partial-line comment should never appear on a line with a brace.
Example:
Example:
Preferred break points, in order of preference, are after commas or before logical AND (&&), logical OR (||), and leading parentheses.
Example:
Example:
Example:
Example:
ANSI and other standard library includes are enclosed
in "angle brackets" (<>)
and all others in quotation marks.
Include files are grouped acording to a hierarchical order.
Conditional compilation
Comments should never be used for conditional compilation. Conditional compilation is less confusing and less
error-prone when properly handled by the appropriate commands.
#if FALSE
...
#endif
Comment blocks
A multiple-line comment begins and ends with a full line of asterisks and a blank line inside the comment
delimiters. Multiple-line comments are only used: at the beginning of a source file (section 6.3.1), at the beginning
of an include file (section 6.4.2), for major data descriptions (section 6.9.5), and as a function heading (section
6.10.3). Any process big or important enough to require a multiple-line comment should be in its own function.
/*****************************************************************************
Description and definition of data . . .
Legal ranges and values . . .
Used by . . .
Changed by . . .
*****************************************************************************/
int global_major_variable;
Line comment
A single-line comment should begin in column one. This provides maximum space for a single sentence comment.
A single-line comment should have a space inside the comment delimiters to make it more readable. When a
function contains several simple processes, each process section can receive a simple introduction in a standard
format. Any process requiring more than a single line of introduction should appear in its own function with a
heading comment. A single-line comment is also used for a data description.
/* section description */
/*lint ++flb */
Partial line comment
A partial-line comment should never appear alone on a line. A partial-line comment begins in column 40 or at least
two tab stops past the statement. A partial-line comment should have a space inside the comment delimiters. As far
as possible, code should be written in such a clear fashion that this type of comment is unnecessary.
statement; /* comment */
White Space
The effects of white space are as critical as anything else in contributing to readable code.
Blank lines
Blank lines should be used to separate distinct sections.
The guideline describes most of the situations where blank lines are used.
Triple spacing should not to be used, even in comments.
Blank spaces
Blank spaces should be used to match English syntax use for readability.
All identifiers and operators are to be separated by a single space.
for (i = 0; i < MAX_INDEX; ++i)
Tabs
Tabs (except before partial-line comments) should be used only to indent levels of code or data.
Tabs should appear only against the left margin. (Four columns per tab is preferred.)
Line length
Total line length should be limited to 80 characters.
Continuation lines used to achieve the limit are indented to the
next level after the starting line.
Continuation lines are clearest when they
begin with an operator that is obviously not legal except on a
continuation line.
Source Files
Heading comment
Every source file should begin with a heading comment. Refer to section 6.1.4. The heading comment contains the
source filename, author, copyright notice, a brief description, and a version history. This guideline provides an
explicit place for source file documentation at the beginning of the file. Documenting the source filename within
the source makes it easier to keep track of names. A version history makes it easier to know what changes have
been made and how old the code is.
/*****************************************************************************
example.c
Frank T. Clark
Copyright 1989, By Frank T. Clark. All rights reserved.
Copies of this document may be freely distributed but only without any
modification. This document may be freely quoted in any publication if proper
reference is provided.
This program is an example of style
Version 1.0 released June 1984
*****************************************************************************/
File names
The name of all source file names should be limited to
the same length and characters as a "C" symbol
and should end in ".C".
There are three types of source files:
program (section 6.3.3),
subfunction (section 6.3.4 and 6.3.5),
and library (section 6.3.6).
These requirements assist segregation of data and symbols,
which improves reliability and reduces potential side effects.
Program source file
The first function in a program source file must be main.
Note section 6.10.1. The entry point main
must be the only global function. The name of the file is the name of the program.
/*****************************************************************************
program.c
*****************************************************************************/
int main(int argc,char *argv[])
{
...
}
Subfunction source file
The subfunction source file should contain a single global primary function. The primary function should
appear first. Note section 6.10.1. The file also contains any static functions that the primary function
references. The name of the file should match the name of the primary function. A subfunction source file is
usually created to modularize the program and to reduce the size of the program source file. The subfunction source
file should contain a single carefully pruned branch of the function reference tree for the program.
/*****************************************************************************
major.c
*****************************************************************************/
void MajorFunction(void)
{
...
LocalFunction();
...
LocalFunction();
}
static void LocalFunction(void)
{
...
}
Interrupt/task source files
Each interrupt handler and task (in a multitasking environment) should have its own separate subfunction source
file. It should contain one global function, as well as any static functions that the primary function
references. The name of the file matches the name of the primary function.
Library source file
The library type of source file contains a collection of global functions which perform a common purpose
and are referenced by multiple source files. The global functions should appear first. Note section 6.10.1.
The file also contains any static functions referenced by the global functions. These libraries are
often built around a single hidden resource such as a display, a database, a data list or a device. The name of the file
should match the purpose of the functions.
/*****************************************************************************
global.c
*****************************************************************************/
void Global1Function(void)
{
}
void Global2Function(void)
{
}
External references
Source files should not contain any external reference (extern) statements. Include files should be the
only place where external reference statements are used. Any global data or functions must be referenced
through an include file which acts as an interface (section 6.4.3).
File size
Source files should avoid extreme file sizes. Programs should use as few files as possible while adhering to the
preceding recommendations. Numerous small files should be avoided. Extremely large files should also be
avoided. The ideal source file is more than a hundred lines but less than a thousand lines.
Order
All source files should use the following sequence for individual elements. Blank lines should be used between
these various elements:
Include Files
Include files are very important to serve as the interface between source files.
File names
Include file names should be limited to the same length
and characters as a "C" symbol and should end in ".H".
Path names and other system dependencies should be
avoided to improve portability.
| Hierarchical Taxonomy of Include Groups | |
|---|---|
| Group Name | Example File |
| Language | stdio.h |
| Compiler | conio.h |
| Platform | windows.h |
| Manufacturer | Versions.h |
| Product | product.h |
| Component | project.h |
| Module | Main.h |
Example:
#include <stdio.h> /* ANSI standard library */ #include <string.h> #include <dos.h> /* Microsoft C standard library */ #include <graph.h> #include "example.h" /* application includes */
Example:
/***************************************************************************** example.h Frank T. Clark Copyright 1989, By Frank T. Clark. All rights reserved. Version 1.0 released June 1984 This include file is an example *****************************************************************************/
Simple function level automatic variable names are allowed as an exception.
Example:
extern int example_global_variable
int another_global_variable
static int local_variable
void Function(void)
{
static int value;
int i;
}
Example:
#define ROWS 24
typedef struct
{
...
}STRUCT_DEF;
Example:
#define PI (3.14159) #define ABSDIFF(x,y) ((x)>(y)?(x)-(y):(y)-(x))
Example:
typedef struct
{
...
}STRUCT_DEF;
STRUCT_DEF structure_variable;
Example:
typedef enum
{
FALSE,
TRUE
}LOGIC;
LOGIC door_is_open;
Example:
static void LocateString(int row,int col,int attribute,unsigned char *string);
.
.
.
static void LocateString(int row,int col,int attribute,unsigned char *string)
{
}
Example:
void Function(void)
{
static const char prompt_string[]="This is a constant string";
int process_state;
process_state=READY;
}
Example:
/***************************************************************************** Description and definition of data . . . Legal ranges and values . . . Initialized by . . . Used by . . . Changed by . . . *****************************************************************************/ int global_major_variable;
Function Definitions
Example:
Example:
Example:
Example:
There should be only one statement per line. A semicolon should only appear at the end of a line (except in a for
statement). The colon in a case statement should only appear at the end of a line.
The switch statement should be written in a structured form. Dropping through from one case to another is a
potential source of problems and should be avoided. The case statement values should appear in sorted order. The
default case should always be included, and it should always appear last. The unexpected case should always be
planned for and handled. Appearing last contributes to the forward flow of reading the program.
Example:
Example:
Order
The order of function definitions in a source file is explicitly determined by the order of reference within the source
file. The global functions are always defined first. Other functions are always defined after the last function that
references it and in the order that it was referenced.
Separator
The function definition declarator should be preceded by a blank line. This makes it easier to see the beginning of
the function and could be considered a substitute for indenting the function level braces.
Heading
Every function should begin with a heading comment. Refer to section 6.1.4. The heading comment appears after
the function definition declarator but before the compound statement constituting the function body. The comment
contains a description of the arguments and returns, purposes, legal values or ranges, and a process description.
This guideline provides an explicit place and content for function documentation. The heading comment makes it
easier to recognize the start of each function. Any process big or important enough to require a multiple-line
comment should be in its own function.
void LocateString(int row,int col,int attribute,unsigned char *string)
/*****************************************************************************
Arguments:
row (0-24) - the row of the screen to place a string
col (0-79) - the column of the screen to place a string
attribute (0 - 255) - the attribute of the characters
string - the character string to be placed on the screen
Return: none
Description: write the string to the screen.
There is no interpretation of the characters in the string.
*****************************************************************************/
{
Main
The main function in a program source file should have a complete standard definition. The main function always
returns an exit code using the ANSI standard symbolic constants. The command line arguments should always be
checked thoroughly and helpful information provided if they are incorrect.
int main(int argc,char *argv[])
{
return EXIT_SUCCESS;
}
Function Statements
Indentation
Function definitions, function level braces, local data definitions, and first level code should begin in column 1.
This contributes to the compactness of the code.
static int function (void)
{
int i;
while(expression)
{
statement1;
statement2;
}
}
Complexity
Function complexity should be carefully limited. For example, nesting deeper than approximately five levels
indicates that more subfunctions are required or that flow should be restructured. Functions larger than about 50 -
100 lines should be modularized and broken down into smaller pieces.
Structure
Structured code is required. Do not use setjump, longjump, or goto. A break statement is an acceptable alternative as
is a return statement from the middle of a function.
Parentheses
Redundant parentheses should be used wherever there might be confusion about operator precedence. The operator
precedence may be less obvious to the next person reading the code.
Braces
Braces should appear on a line by themselves,
indented by a tab per level,
with opening and closing braces for the
same level aligned in the same column.
Braces may be left out when there is only a single line for
each structure level.
if(expression)
return;
for(expression;expression;expression)
for(expression;expression;expression)
statement;
while(expression)
{
if(expression)
statement;
else
statement;
}
for(expression;expression;expression)
{
for(expression;expression;expression)
{
statement;
statement;
}
}
Statements
Switch
switch(expression)
{
case 1:
{
statement;
statement;
}
break;
case 2:
{
statement;
statement;
}
break;
default:
{
statement;
statement;
}
}
Returns
The error and status returns of every function should be checked. Every error and status return should be either
handled or displayed even if the error should never occur. A standard error printing function should be used for
every check. Some functions do return a purely informational result which may be ignored.
status=puts("Sample string");
if(status<0)
ErrorPrint(__LINE__,status,"puts");
strcpy(buffer,"Sample string");
Program Source File Example
The following sample illustrates a source file using most of the elements of style described above.
/*****************************************************************************
standard.c
Version modified on Mon Oct 02, 1989 09:03:09 by Frank T. Clark
standard revisions and lint revisions
all char are assumed unsigned unless explicitly signed
Version modified on Fri Dec 29, 1989 13:49:54 by Frank T. Clark
standard program skeleton
*****************************************************************************/
/*lint ++flb */
#include <stdio.h> /* ANSI standard library */
#include <stdlib.h>
#include <time.h>
#include <dos.h> /* MSC headers */
#include "datetime.h" /* application headers */
/*lint --flb */
#define EXIT_FAILURE 1 /* ANSI standard missing in STDLIB.H */
#define EXIT_SUCCESS 0 /* ANSI standard missing in STDLIB.H */
typedef enum
{
FALSE,
TRUE
}LOGIC;
int main (int argc, char *argv[]);
static void ErrorExit (int line,int error, char *string);
static void ErrorPrint (int line, int error, char *string);
static void HelpExit (int argc, char *argv[]);
int main (int argc, char *argv[])
/*****************************************************************************
ARGUMENTS: argc - count of program arguments
argv - array of pointers to program arguments
RETURNS: exit code
DESCRIPTION: Check arguments, print greeting, start processing and then
bid them goodbye
*****************************************************************************/
{
char *buffer;
if (1 < argc)
HelpExit (argc, argv);
if (NULL == (buffer = malloc (DATETIME)))
ErrorExit (__LINE__, 0, "malloc");
DateTime (NULL, buffer);
printf("The date and time is %s\n", buffer);
return EXIT_SUCCESS;
}
static void HelpExit (int argc, char *argv[])
/*****************************************************************************
ARGUMENTS: argc - count of program arguments
argv - array of pointers to program arguments
RETURNS: none
DESCRIPTION: provide help if the command line is incorrect then exit
*****************************************************************************/
{
fprintf (stderr, "\n");
fprintf (stderr, "%s - example C program\n", __FILE__);
fprintf (stderr, "\n");
fprintf (stderr, "Command line invalid: ");
while (argc--)
fprintf (stderr, "%s ", *argv++);
fprintf (stderr, "\n");
fprintf (stderr, "\n");
fprintf (stderr, "Command line syntax: No arguments\n");
fprintf (stderr, "\n");
exit (EXIT_FAILURE);
}
static void ErrorExit (int line, int error, char *string)
/*****************************************************************************
ARGUMENTS: line - line number where the error was declared
error - the error number
string - descriptive message
RETURNS: none
DESCRIPTION: These errors should not occur but if they do we must know about
it. What and where. Then exit.
*****************************************************************************/
{
ErrorPrint (line, error, string);
exit (EXIT_FAILURE);
}
static void ErrorPrint (int line, int error, char *string)
/*****************************************************************************
ARGUMENTS: line - line number where the error was declared
error - the error number
string - descriptive message
RETURNS: none
DESCRIPTION: These errors should not occur but if they do we must know about
it. What and where.
*****************************************************************************/
{
fprintf (stderr, "%s %d: Error %d %s\n", __FILE__, line, error, string);
}
Include File Example
The following sample illustrates an include file using most of the elements of style described above.
/*****************************************************************************
datetime.h
version 1.0 released July 1, 1989
This file is a sample include file
*****************************************************************************/
#define DATETIME 38 /* minimum bytes in buffer */
extern void DateTime (time_t value, char *buffer);
Global Source File Example
The following sample illustrates a global source file using most of the elements of style described above.
/*****************************************************************************
datetime.c
version 1.0 released July 1, 1989
This file is a sample global file
*****************************************************************************/
/*lint ++flb */
#include <stdio.h> /* ANSI standard library */
#include <stdlib.h>
#include <time.h>
#include <dos.h> /* MSC headers */
#include "datetime.h" /* application headers */
/*lint --flb */
void DateTime (time_t value, char *buffer)
/*****************************************************************************
ARGUMENTS: value - time value, 0 = current time
buffer - destination buffer of at least 38 characters
RETURNS: buffer
DESCRIPTION: create or convert the time value to a string of the form:
Wednesday September 25, 1989 12:12:12
*****************************************************************************/
{
static const char *day[]={"Sun","Mon","Tues","Wednes","Thurs","Fri","Satur"};
static const char *month[]={
"January"
,"February"
,"March"
,"April"
,"May"
,"June"
,"July"
,"August"
,"September"
,"October"
,"November"
,"December"
};
struct tm tm_struct;
if (NULL == value)
time (&value);
tm_struct = *localtime (&value);
sprintf (buffer, "%sday %s %d, %d %d:%02d:%02d", day[tm_struct.tm_wday],
month[tm_struct.tm_mon], tm_struct.tm_mday,
1900+tm_struct.tm_year, tm_struct.tm_hour, tm_struct.tm_min, tm_struct.tm_sec);
}