[The following is a code listing from the article "Error Messages and Documentation", by S. L. Sanders]
//============================== parse2.h ==============================
// General parsing constants and macros to support exstrng.c, a program
// that illustrates string extraction from C source files.
//
// Copyright (c) Sanders-Indev, Inc, 1997:
// Commercial use of any kind is prohibited without written consent of
// the owner. Personal and teaching use is permitted.
//======================================================================
//----------------------- Duplication Prevention -----------------------
#if !defined(PARSE2h) //If parse2.h has not already been processed,
#define PARSE2h //define "already processed" flag, then go ahead
//-------------------------- Required Headers --------------------------
#include <stdio.h
#include <stddef.h
#include <string.h
//--------------------------- Global Constants -------------------------
#define LINESZ 256 //Max length of line from file (with NUL appended)
//Note: This constant is intended as the length of
//lines read by fgets(). It should be at least 2
//greater than the length of the longest line
//expected. (See fgets() in run-time library
//manual, and NEXTLINE below.)
#define FNSZ 8 //Max length of filename (without NUL terminator)
#define FFNSZ 66 //Max length of full-path filename (with NUL appended)
#define IDSZ 33 //Max length of identifier (with NUL appended)
//------------------------------- Macros -------------------------------
/*--NEXTLINE--*/
//Using fgets(), read the next line from the file represented by filptr.
//Characters from the file are put into the location to which sbuf
//points, until a '\n' is transferred, sbufsz-1 chars have been
//transferred, or end-of-file, whichever happens first. A '\0' is
//appended in every case.
//If ferror() detects an error, execute failstmt; otherwise, set
//character scan index csi to 0, and if EOF not reached, add 1 to
//lcounter. If EOF is reached, put EOF in sbuf[0].
//SYNTAX NOTE: The actual argument corresponding to failstmt must be a
//valid C statement. If it is a compound statement, each statement must
//be ended with a semicolon, and they all must be enclosed with braces.
// Example: {puts("Can't read file.\a\n");err=1;}
//If it is a single statement, it need not be ended with a semicolon
//nor enclosed in braces.
// Example: err=1
#define NEXTLINE(sbuf,sbufsz,filptr,lcounter,csi,failstmt){\
fgets(sbuf,sbufsz,filptr);\
if(ferror(filptr))\
{failstmt;}\
else{\
csi=0;\
if(feof(filptr))\
sbuf[0]=EOF;\
else\
lcounter++;\
}\
}
/*--NOTMEMLN--*/
//Increment the character-scan index csi until it indexes the next char
//in the string buffer sbuf that does not match any of the chars in the
//string to which critstr points. In no case will csi be incremented
//beyond the kNUL that must mark the end of active sbuf data. kNUL is
//used as end-of-string, so it can't be one of the critstr chars.
#define NOTMEMLN(csi,sbuf,critstr){\
short i;\
char crit;\
short csisav;\
do{csisav=csi;\
for(i=0;(crit=critstr[i])!=kNUL;i++)\
for(;sbuf[csi]==crit;csi++);\
}while(csi!=csisav);\
}
/*--NOTMEMFL--*/
//Increment csi (character-scan index) until it indexes the next
//character in the file represented by filptr that does not match any of
//the chars in the string to which critstr points, reading additional
//lines from the file into sbuf as necessary. In no case will csi be
//advanced beyond EOF. The char kNUL is used as end-of-string, so it
//can't be one of the critstr chars. See NEXTLINE for descriptions of
//the other arguments.
#define NOTMEMFL(filptr,csi,critstr,sbuf,sbufsz,lcounter,failstmt){\
short lctsav;\
do{lctsav=lcounter;\
NOTMEMLN(csi,sbuf,critstr)\
if(sbuf[csi]=='\n'||sbuf[csi]==kNUL)\
NEXTLINE(sbuf,sbufsz,filptr,lcounter,csi,failstmt)\
}while(!feof(filptr)&&!ferror(filptr)&&lctsav!=lcounter);\
}
/*--ISMEMLN--*/
//Increment the character-scan index csi until it indexes the next char
//in the string buffer sbuf that matches any of the chars in in the
//string to which critstr points. In no case will csi be incremented
//beyond the kNUL that must mark the end of active sbuf data. kNUL is
//used as end-of-string, so it can't be one of the critstr chars.
#define ISMEMLN(csi,sbuf,critstr){\
short csisav;\
char const *critptr;\
do{csisav=csi;\
critptr=critstr;\
for(;sbuf[csi]!=*critptr&&*critptr!=kNUL;critptr++);\
if(*critptr==kNUL&&sbuf[csi]!=kNUL)\
csi++;\
}while(csi!=csisav);\
}
/*--ISMEMFL--*/
//Increment csi (character-scan index) until it indexes the next
//character in the file represented by filptr that matches any of the
//chars in the string to which critstr points, reading additional lines
//from the file into sbuf as necessary. In no case will csi be advanced
//beyond EOF. kNUL marks the end of the string, so it can't be one of
//the critstr chars. See NEXTLINE for descriptions of the other
//arguments.
#define ISMEMFL(filptr,csi,critstr,sbuf,sbufsz,lcounter,failstmt){\
short lctsav;\
do{lctsav=lcounter;\
ISMEMLN(csi,sbuf,critstr)\
if(sbuf[csi]==kNUL)\
NEXTLINE(sbuf,sbufsz,filptr,lcounter,csi,failstmt)\
}while(!feof(filptr)&&!ferror(filptr)&&lctsav!=lcounter);\
}
/*--NOBLANKR--*/
//After saving the current file position in pos, read the next line from
//the file represented by filptr. Store the line in a string buffer
//sbuf, which is sbufsz bytes long. Set the character-scan index csi to
//0, then increment it past leading blanks and empty lines, reading
//additional lines as needed. A blank is defined as a space (0x20) or
//tab (0x09). To permit recognition of a variety of end-of-line
//conditions, the actual argument for eol is supposed to be a macro that
//is TRUE (nonzero) if and only if sbuf[csi] is an end-of-line signal.
//Add 1 to lcounter for each line read.
//If fgets() fails, execute failstmt.
//SYNTAX NOTE: The actual argument corresponding to failstmt must be a
//valid C statement. If it is a compound statement, each statement must
//be ended with a semicolon, and they all must be enclosed with braces.
// Example: {puts("Can't read file."\a\n");err=1;}
//If it is a single statement, it need not be ended with a semicolon
//nor enclosed in braces.
// Example: err=1
#define NOBLANKR(pos,filptr,sbuf,sbufsz,csi,eol,lcounter,failstmt){\
fgetpos(filptr,&pos);\
/*Ensure NEXTLINE execution*/\
sbuf[csi]='\n';\
do{NOTMEMFL(filptr,csi," \x09",sbuf,LINESZ,lcounter,failstmt)\
if(eol)\
sbuf[csi]='\n';\
}while(sbuf[csi]=='\n'&&!ferror(filptr));\
}
/*--SKIPBLNK--*/
//Increment csi (character-scan index) until it indexes the next
//character in sbuf that is not an ASCII space or HT char.
#define SKIPBLNK(sbuf,csi){\
for(;BLANK(sbuf,csi);csi++);\
}
/*--BLANK--*/
//Expression TRUE iff sbuf[csi] is an ASCII space or HT char.
#define BLANK(sbuf,csi) (sbuf[csi]==' '||sbuf[csi]==kHT)
/*--CIDCHR--*/
//Expression TRUE iff sbuf[csi] is a valid C identifier char.
#define CIDCHR(sbuf,csi)\
(((isalnum(sbuf[csi]))||sbuf[csi]=='_')&&sbuf[csi]<123)
/*--LINECONT--*/
//Expression TRUE iff sbuf[csi] is the \ of a line-continuation marker.
#define LINECONT(sbuf,csi) (sbuf[csi]=='\\'&&sbuf[csi+1]=='\n')
/*--ENDLINE--*/
//Expression TRUE iff csi indexes the end of the line in sbuf; either
//the \ of a line-continuation marker, or a newline char.
#define ENDLINE(sbuf,csi) (sbuf[csi]=='\n'||LINECONT(sbuf,csi))
/*--CLOSEQUO--*/
//Expression TRUE iff csi indexes the closing quote mark of a string.
#define CLOSEQUO(sbuf,csi) (sbuf[csi]=='"'&&sbuf[csi-1]!='\\')
//----------------------------------------------------------------------
#endif //PARSE2.h has not already been processed
//======================================================================