Frank T. Clark

Software Systems Design Engineer

/Home /Professional /Papers /Guidelines

www.Frank-T-Clark.com

Software Development

Procedures and Guidelines

(Undergoing revision)


Contents

  1. Preface
  2. Introduction
  3. Hardware
  4. Software Installation and Use
  5. Version control procedures
  6. Programming style
    1. Comments
      1. Tutorial
      2. Nesting
      3. Conditional compilation
      4. Comment blocks
      5. Line comment
      6. Partial line comment
    2. White Space
      1. Blank lines
      2. Blank spaces
      3. Tabs
      4. Line length
    3. Source Files
      1. Heading comment
      2. File names
      3. Program source file
      4. Subfunction source file
      5. Interrupt/task source files
      6. Library source file
      7. External references
      8. File size
      9. Order
    4. Include Files
      1. File names
      2. Heading comment
      3. File contents
      4. Nested files
      5. Unnecessary files
      6. File name linkage
      7. File count
      8. Order
    5. Symbol Naming Conventions
      1. Name recognition
      2. Symbolic Constants
      3. Data names
      4. Function names
      5. Differentiation
    6. Macro Definition and Text Substitution
      1. Text substitution
      2. Constants
      3. Macros
    7. Type Definition Declarations
      1. Tags
      2. Symbolic constants
      3. Arrays
    8. Function Declarations
      1. Prototypes
      2. Variable names
    9. Data Definitions
      1. Data types
      2. Scope
      3. Unused
      4. Constants
      5. Descriptions
    10. Function Definitions
      1. Order
      2. Separator
      3. Heading
      4. Main
    11. Function Statements
      1. Indentation
      2. Complexity
      3. Structure
      4. Parentheses
      5. Braces
      6. Statements
      7. Switch
      8. Returns
    12. C Program Source File Example
    13. C Include File Example
    14. C Global Source File Example

Contents

Preface

I resurrected this document from an earlier project. It was originally designed many years ago to be a book that I was writing. I have been using these software development procedures and guidelines on every software project over the years. This document is only a general treatment of the subject of procedures and guidelines for software development. This information is useful for individual software developers as well as software development groups or departments.

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.


Contents

Introduction

Some sections of this document are very old and need to be updated.

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.


Contents

Hardware

A typical development system hardware configuration would include specific details which need to be outlined.

Computer setup

The set up procedure for the computer hardware is usely straightforward and not described in detail unless needed.

Stationary mouse setup

The selection of a stationary mouse (trackball) saves on desk space and the clutter that occurs with the normal mouse which requires reserving some of the precious blank space on your desktop. The stationary mouse also overcomes the problem of running to the edge of your desk and having to pick up the mouse and reposition it. The stationary mouse accumulates less dust and dirt from the desktop, gets bumped less, and can be used from one corner of your desktop computer if necessary rather than being placed on the desk.

The overwhelming prevalence of the rolling mouse often causes it to be selected.


Contents

Software Installation and Use

The software applications which may be used on the development system are:

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
...


Contents

Version control procedures

All non-temporary files used in software development and release will be stored in the Polytron Version Control System (PVCS). PVCS archives every revision of every file. PVCS assigns revision numbers and records the revision description, the revision date, and the author. PVCS provides version labels which are used to match which revisions of files are used for every version. PVCS has a file checkout system which tracks who is in the process of changing files and is used to alert a developer to potential parallel revision conflicts.

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


Contents

Programming style

The following elements of programming style are presented for consideration. The organization of these elements is in a top down order.

Comments

It may surprise some to see comments as the first topic in a discussion of programming guidelines. Comments are not only the first thing you should see at the beginning of the code but they are also the most critical part of the code and yet they are so often misused and abused. One of the goals of the following guidelines is to keep the comments neat and orderly to improve readability. Comments do not replace a properly written Software Requirements Specification or the Software Design Description.

Tutorial

Comments should be used liberally for tutorial purposes. The most important tutorial aspect is to indicate code organization. Comments should be used wherever necessary to describe data, processes, and provide information. Comments should assume that the reader understands the code and should not redundantly state what the code already shows. This requirement assumes that the code is indeed clearly written. The lack of clearly written code cannot be overcome by adding more comments. Comments are needed to explain the why and how of what the code is doing.

Nesting

Comments should not be nested. Nested comments are confusing and error-prone. Comments are not allowed to be nested in the ANSI C standard.

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.

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.

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:

 
#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.

The block double slash comment is a line of all slashes.

Example:

 
/*****************************************************************************

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.

An obvious exception is that comments for PC-LINT processing prohibit a space at the beginning of the comment inside the comment delimiter.

Example:

 
/* 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.

A partial-line comment should never appear on a line with a brace.

Example:

 
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.

Example:

 
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.

Preferred break points, in order of preference, are after commas or before logical AND (&&), logical OR (||), and leading parentheses.

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:

 
/*****************************************************************************
 
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.

Example:

 
/*****************************************************************************

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.

Example:

 
/*****************************************************************************

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.

Example:

 
/*****************************************************************************
 
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.

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.

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 */

Heading comment

Every include file should begin with a heading comment. Refer to section 6.1.4. The heading comment contains the filename, author, copyright notice, version history, and a brief description.

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

*****************************************************************************/

File contents

Include files should only contain #defines, typedefs, extern data references and extern function prototype declarations. These should only be part of an include file if they are referenced by more than one source file. Include files should not be catch-alls for unrelated information. Include files should never define code or data. Information should be in an include file if it will be needed by more than one source file; not to reduce clutter in the source file.

Nested files

Include files should not be nested. Nesting include files obscures their relationships to the primary source file, and may lead to unexpected results. The exception might be include files that are required by the file itself.

Unnecessary files

Include files should not be specified unless they are needed. Unnecessary include files confuse the reader of the source code and may lead to unexpected results.

File name linkage

Include files should be named the same as the source file they support. When a function in file "A" calls global function "B" in a subfunction source file, the source file is called "B.C" and the interfacing include file is called "B.H". All the data and functions referenced in an include fie should be in the source file of the same name.

File count

The total number of include files should be minimized. There should be no more than one include file for every source file in the program, other than the standard library includes.

Order

All include files should use the following sequence for individual elements. Blank lines should be used between these various elements:

Symbol Naming Conventions

Name recognition

Names composed of complete words should be used to improve readability and understandability. The name should be phonetically readable, do not omit all the vowels. The name should be meaningful and try to describe the content or purpose. The greater the scope of a name, the bigger you should try to make the name. For example, source level global variables could have two words, and program level global variables could have three words. Portability concerns should be the only reason to restrict length of names, but beware of excessive prior restraint!

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;

 }

Symbolic Constants

Symbolic constants and macros, specified by #define and typedef names should be in upper case. Underscores are used to separate words. This classification serves to separate symbols which are constants, macros, or types from symbols which are data or code.

Example:

#define ROWS 24

typedef struct
 {
 ...
 }STRUCT_DEF;

Data names

Names for structures, unions, and variables should be in lower case. Underscores are used to separate words.

Function names

Names for functions should be in lower case with the first letter of each word capitalized. Underscores are not used to separate words.

Differentiation

Avoid using the same spelling, with different cases for different names. You should also avoid using names that are spelled similarly enough to cause confusion.

Macro Definition and Text Substitution

Text substitution

Only macros and symbolic constants should be created with #defines. The gratuitous replacement of statements, keywords, symbols or syntax garbles the code and confuses the reader.

Example:

#define PI (3.14159)
#define ABSDIFF(x,y) ((x)>(y)?(x)-(y):(y)-(x))

Constants

Symbolic constants should be used to replace hard coded constants which do not have an obvious meaning. A symbolic constant provides a name to describe a constant value. Symbolic constants are not used for character constants unless they are in hexadecimal or octal representation.

Macros

The use of macro definitions should be avoided. The purpose and use of macro definitions can be easily confused and abused.

Type Definition Declarations

Tags

Type definitions should be used instead of tags for enumerations, structures and unions. This method is more consistent and provides greater readability.

Example:

typedef struct
 {
 ...
 }STRUCT_DEF;

STRUCT_DEF structure_variable;

Symbolic constants

Type defined enumerations (instead of #defines) should be used for most symbolic constants. This allows for greater error checking on appropriate use. A clue to symbolic constants which should be an enumeration is when more than one is used for the same purpose (such as a variable assignment or a function return) and only symbolic constants are used.

Example:

typedef enum
 {
 FALSE,
 TRUE
 }LOGIC;

LOGIC door_is_open;

Arrays

Type defined structures (instead of arrays) should be used to group most types of related data items. Arrays should only be used for array processing of identical data items. A possible clue to an array that should be a structure is one that has a constant in the index.

Function Declarations

Prototypes

All static functions should be declared in prototypes. All static functions in the source file must have a prototype in the source file. External and global functions should have their prototypes in an include file. Formal arguments are always named descriptively and identically to the function definition. The function declarations appear in the same order as the function definitions (section 6.10.1).

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)
 {

 }

Variable names

Consistent argument and variable names should be used in all functions. As an example, a local variable in several functions which is used to represent a vertical screen position would always be called row. A local integer variable used to hold keyboard input would be called chr.

Data Definitions

Data types

All variables and function results should be typed, even if the compiler allows a default. Character variables should default to unsigned char to avoid problems with sign extension. The type int is used for most normal variables when the range of values will not exceed the smallest int in common use (16 bits). The type long is explicitly specified for the largest range of values in common use (32 bits). The type long int or short int is specified in structures and other areas where specific byte count is important, such as in data files, to enhance portability.

Scope

The scope of all global variables and functions should be as limited as possible. Static should be declared unless default global scope is required. Global variables should never be used as a substitute for passing arguments.

Unused

Unused variables should not be defined. Unused variables clutter the code and confuse the reader.

Constants

Variables which are constants should be specifically labeled as such and must be initialized in their definition. Automatic variables should not be initialized in their definition.

Example:

 
void Function(void)
 {
 static const char prompt_string[]="This is a constant string";
 int process_state;
 
 process_state=READY;
 }
Descriptions Important global data definitions in include files should be clearly described in a multiple-line comment. Refer to section 6.1.4. This comment should include a full description and definition of the data item, the legal ranges and values appropriate for the data, the name of the source file where the data is initialized, the names of the source files where it is used, and where it is changed.

Example:

/*****************************************************************************
 
Description and definition of data . . .

Legal ranges and values . . .

Initialized by . . .

Used by . . .

Changed by . . .
 
*****************************************************************************/

int global_major_variable;

Function Definitions

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.

Example:

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.

Example:

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.

Example:

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.

Example:

 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

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.

Switch

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:

 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.

Example:

 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);
}