[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
//======================================================================

Back to main article.