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