wtoomey@gara.une.oz (Warren Toomey) (07/29/89)
Here's a useful little program that I wrote to help people wade through heavily #ifdef'd C code. It's great for following the logic of code that's been made opaque with #ifdef's, and you can also use it to release/produce programs customised for certain environments, without unneeded code left in. I have tested it reasonably thoroughly, by giving it Clam to chew on. Unfortunately, Clam would have to be one of the worst #ifdef offenders, but after passing through this program it becomes readable, and even compiles faster :-) Note: Small bug - it doeasn't understand #if's Hopefully this will satiate the people complaining about #ifdefs. P.S Expect a set of patches to Clam in the next few weeks. Warren Toomey. ------------ cut, tear, mangle, shred, dessicate, rip here ---------- # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # ifdef.1 Makefile Minix.make ifdef.h ifdef.c echo x - ifdef.1 cat > "ifdef.1" << '//E*O*F ifdef.1//' .T1 ifdef 1L .SH NAME ifdef \- remove conditional code from C files .SH SYNTAX ifdef [\-t] [\-dsymbol] [\-Dsymbol] [\-Usymbol] [\-Isymbol] <file> .SH OPTIONS Options are processed by .B getopts(3). .TP 8 .B \-t Table mode. .I Ifdef produces a table of the defined and undefined symbols in the given file. .TP .B \-dsymbol Define the symbol before processing takes place. Note that if the symbol is later #undef'd, then it will become undefined. .TP .B \-Dsymbol Define the symbol, and make it .I immutable. That is, later #undef's will not undefine the symbol. .TP .B \-Usymbol Undefine the symbol, and make it .I immutable. Successive #define's will not define this symbol. .TP .B \-Isymbol Ignore this symbol in processing. #ifdef's using this symbol will be left in the output code. .SH DESCRIPTION .I Ifdef is a small program which allows conditional code [ #ifdef ... #endif ] to be selectively removed from C files, but at the same time leaving all other C preprocessor commands intact [ #define, #include etc ]. The resulting code can then be viewed or compiled with any unnecessary conditional code missing. .PP Input to .I ifdef is either a .B file named as the last argument, or standard input if no file is named. .I Ifdef takes the input file, and processes it, removing unwanted conditional code. The resulting code is placed on the standard output. .PP Symbols may be defined with the .B \-d and .B \-D options ( like the C preprocessor ), except that the latter option makes the symbol .I immutable, and hence cannot be #undef'd at a later time. Similarly, the .B \-U option undefines a symbol .I immutably, preventing it from being #define'd at a later time. Note that symbols not mentioned at runtime are undefined but mutable. .PP .I Ifdef ignores any values given to a symbol; in fact, if given an option of the form .PP \-DLEVEL=22 .PP then it will assume the defined symbol is .B LEVEL=22. .PP The .B \-I option allows a specific symbol to be ignored thoughout the processing of the file. Any #ifdef code using the symbol will be left intact in the output code. .PP Finally, the .B \-t option puts .I ifdef into table mode; instead of putting the resulting code on the standard output, .I ifdef returns a list of symbols that were defined or not defined in the code. This is useful for finding out what symbols are tested for in a new piece of code. Parsing of the source is done as normal, so not all tests will be found. This means that in the following code .PP .B #ifdef FOO .br .B #define BAR 3 .br .B #endif .PP .B BAR will not be found if .B FOO is not defined in the code or at runtime. .PP .SH BUGS No checking is done to see if the conditional code is semantically corecct. Missing or extra #ifdef's etc. are not detected. .PP .SH SEE ALSO cpp(1), scpp(1L). //E*O*F ifdef.1// echo x - Makefile cat > "Makefile" << '//E*O*F Makefile//' CFLAGS= -gx #DEFINES= -DDEBUG ifdef: ifdef.o cc -o ifdef $(CFLAGS) ifdef.o ifdef.o: ifdef.c ifdef.h Makefile cc -c $(CFLAGS) ifdef.c $(DEFINES) //E*O*F Makefile// echo x - Minix.make cat > "Minix.make" << '//E*O*F Minix.make//' CFLAGS= #DEFINES= -DDEBUG ifdef: ifdef.s getopt.s cc -o ifdef $(CFLAGS) ifdef.s getopt.s ifdef.s: ifdef.c ifdef.h Makefile cc -c $(CFLAGS) ifdef.c $(DEFINES) getopt.s: getopt.c cc -c $(CFLAGS) getopt.c $(DEFINES) //E*O*F Minix.make// echo x - ifdef.h cat > "ifdef.h" << '//E*O*F ifdef.h//' /* Definition of structures and constants used in ifdef.c */ /* Types of symbols */ #define DEF 1 /* Symbol is defined */ #define UNDEF 2 /* Symbol isn't defined */ #define IGN 3 /* Ignore this symbol unless defined */ /* Redef mode values */ #define MUTABLE 1 /* Symbol can change defined <-> undefined */ #define IMMUTABLE 2 /* Symbol can't change as above */ /* Processing modes */ #define NO 0 /* Don't process */ #define YES 1 /* Process */ /* Ignore (IGN), ignore but process */ struct DEFINE { char *symbol; /* SLL of defined symbols. The redef */ char type; /* field indicates if this symbol can */ char redef; /* change from defined <-> undefined. */ struct DEFINE *next; /* Type is DEF or UNDEF. */ }; //E*O*F ifdef.h// echo x - ifdef.c cat > "ifdef.c" << '//E*O*F ifdef.c//' #include <stdio.h> #ifdef BSD # include <strings.h> #else # include <string.h> #endif #include "ifdef.h" /* Ifdef: Remove unwanted ifdefs from C code */ /* Written by Warren Toomey - 1989 */ /* You may freely copy or give away this source as */ /* long as this notice remains intact. */ /* Global variables & structures */ FILE *zin; /* Input file for processing */ struct DEFINE *defptr; /* Defined symbols SLL */ struct DEFINE *defend; /* Ptr to last node in defptr */ struct DEFINE *deftemp; /* Ptr to last found node */ int line=1; /* Current line number */ int table=0; /* Don't normally want a table */ char fgetarg(stream,cbuf) /* Get next arg from file into cbuf, */ FILE *stream; /* returning the character that */ char *cbuf; /* terminated it. Cbuf returns NULL */ { /* if no arg. EOF is returned if no */ char ch; /* args left in file. */ int i; i=0; cbuf[i]=NULL; while (((ch=fgetc(stream))==' ') || (ch=='\t') || (ch=='\n')) if (ch=='\n') return(ch); /* Bypass leading */ /* whitespace */ if (feof(stream)) return(EOF); cbuf[i++]=ch; while (((ch=fgetc(stream))!=' ') && (ch!='\t') && (ch!='\n')) cbuf[i++]=ch; /* Get the argument */ cbuf[i]=NULL; return(ch); } find(sym) /* Return DEF if symbol is defined */ { /* UNDEF if symbol undefined */ char found=0; /* IGN if symbol is ignored */ /* 0 if not in the list */ deftemp=defptr; while (deftemp) /* Search for the symbol */ { if (!strcmp(deftemp->symbol,sym)) return(deftemp->type); /* Setting up the type */ deftemp=deftemp->next; } return(0); } #define Define(x,y) defit(x,y,DEF) #define Undefine(x,y) defit(x,y,UNDEF) #define Ignore(x,y) defit(x,y,IGN) defit(sym,redef,type) /* Add symbol to the define list */ char *sym; char redef; /* Mode: MUTABLE etc */ char type; /* Type: DEF, UNDEF, IGN */ { struct DEFINE *temp; char c; c=find(sym); /* First try finding the symbol */ if (type==c) return; /* Return if already declared */ if (c) /* We have to move if from DEF <-> UNDEF */ if (deftemp->redef==IMMUTABLE) return; else { deftemp->type=type; deftemp->redef=redef; } else /* We must create a struct & add it */ { /* Malloc room for the struct */ if ((temp=(struct DEFINE *)malloc(sizeof(struct DEFINE)))==NULL) { fprintf(stderr,"ifdef: could not malloc\n"); exit(1); } /* Malloc room for symbol */ if ((temp->symbol=(char *)malloc(strlen(sym)+1))==NULL) { fprintf(stderr,"ifdef: could not malloc\n"); exit(1); } strcpy(temp->symbol,sym); /* Copy symbol into struct */ temp->redef=redef; /* and set its redef mode too */ temp->type=type; /* as well as making it defined */ /* Now add to the SLL */ if (defptr==NULL) /* If first node set */ defptr=temp; /* the pointers to it */ else defend->next=temp; /* else add it to the */ defend=temp; /* end of the list. */ } } stop() /* Stop: Tidy up when end of input */ { /* file is reached. */ if (table) printtable(); fclose(zin); exit(0); } #define Goto { line++; if (ch!='\n') gotoeoln(zin); } #define Print { line++; if (ch!='\n') prteoln(zin); } gotoeoln(file) /* Go to the end of the line */ FILE *file; { int ch; while ((ch=fgetc(zin))!='\n') if (ch==EOF) stop(); } prteoln(file) /* Print to the end of the line */ FILE *file; { int ch; while ((ch=fgetc(zin))!='\n') if (ch==EOF) stop(); else putchar(ch); putchar('\n'); } printtable() /* Print the defines in the SLL */ { struct DEFINE *temp; printf("Defined\n\n"); temp=defptr; while (temp) { if (temp->type==DEF) printf("%s\n",temp->symbol); temp=temp->next; } printf("\n\nUndefined\n\n"); temp=defptr; while (temp) { if (temp->type==UNDEF) printf("%s\n",temp->symbol); temp=temp->next; } } getendif() /* Find matching endif when ignoring */ { char word[80]; /* Buffer for symbols */ int ch; int skip; /* Number of skipped #ifdefs */ skip=1; while(1) /* Scan through the file */ { /* looking for # starting lines */ if ((ch=fgetc(zin))==EOF) stop(); /* Get first char on the line */ if (ch!='#') /* If not a # ignore line */ { putchar(ch); Print; continue; } ch=fgetarg(zin,word); /* Get the word after the # */ if (!strcmp(word,"ifdef")) skip++; /* Keep track of ifdefs & */ if (!strcmp(word,"endif")) skip--; /* endifs */ printf("#%s%c",word,ch); /* Print the line out */ Print; if (!skip) return('\n'); /* If matching endif, return */ } } /* Get & print a table of defines etc. */ gettable() { char word[80]; /* Buffer for symbols */ int ch; int proc; /* Should we be processing this bit? */ int skip; /* Number of skipped #ifdefs */ proc=1; skip=0; while(1) /* Scan through the file */ { /* looking for # starting lines */ #ifdef DEBUG printf("%d Skip:%d Proc:%d",line,skip,proc); #endif if ((ch=fgetc(zin))==EOF) stop(); /* Get first char on the line */ if (ch!='#') /* If not a # ignore line */ { Goto; continue; } ch=fgetarg(zin,word); /* Get the word after the # */ #ifdef DEBUG printf("%s\n",word); #endif if (!strcmp(word,"define") && proc) /* Define: Define the */ { ch=fgetarg(zin,word); /* symbol, and goto */ Define(word,MUTABLE); /* the end of line */ Goto; continue; } if (!strcmp(word,"undef") && proc) /* Undef: Undefine the */ { ch=fgetarg(zin,word); /* symbol, and goto */ Undefine(word,MUTABLE); /* the end of line */ Goto; continue; } if (!strcmp(word,"ifdef")) /* Ifdef: */ { if (!proc) /* If not processing */ skip++; /* skip it */ else { ch=fgetarg(zin,word); /* Get the symbol */ #ifdef DEBUG printf(" %s\n",word); #endif if (find(word)!=DEF) /* If symbol undefined */ { Undefine(word,MUTABLE); /* undefine it */ proc=0; /* & stop processing */ } } Goto; continue; } if (!strcmp(word,"ifndef")) /* Ifndef: */ { if (!proc) /* If not processing */ skip++; /* skip the line */ else { ch=fgetarg(zin,word); /* Get the symbol */ #ifdef DEBUG printf(" %s\n",word); #endif if (find(word)==DEF) /* If defined, stop */ proc=0; /* processing */ } Goto; continue; } if (!strcmp(word,"else") && !skip) /* Else: Flip processing */ { proc=!proc; Goto; continue; } if (!strcmp(word,"endif")) /* Endif: If no skipped */ { /* ifdefs turn processing */ if (!skip) /* on, else decrement the */ proc=1; /* number of skips */ else skip--; Goto; continue; } Goto; /* else ignore the line */ } } parse() /* Parse & remove ifdefs from C source */ { char word[80]; /* Buffer for symbols */ int ch; int proc; /* Should we be processing this bit? */ int skip; /* Number of skipped #ifdefs */ proc=1; skip=0; while(1) /* Scan through the file */ { /* looking for # starting lines */ #ifdef DEBUG printf("%d Skip:%d Proc:%d",line,skip,proc); #endif if ((ch=fgetc(zin))==EOF) stop(); /* Get first char on the line */ if (ch!='#') if (proc) /* If not # and we're processing */ { putchar(ch); /* then print the line */ Print; continue; } else { Goto; /* else just skip the line */ continue; } ch=fgetarg(zin,word); /* Get the word after the # */ #ifdef DEBUG printf("%s\n",word); #endif if (!strcmp(word,"define") && proc) /* Define: Define the */ { ch=fgetarg(zin,word); /* symbol, and goto */ Define(word,MUTABLE); /* the end of line */ printf("#define %s%c",word,ch); Print; continue; } if (!strcmp(word,"undef") && proc) /* Undef: Undefine the */ { ch=fgetarg(zin,word); /* symbol, and goto */ Undefine(word,MUTABLE); /* the end of line */ printf("#undef %s%c",word,ch); Print; continue; } if (!strcmp(word,"ifdef")) /* Ifdef: */ { if (!proc) /* If not processing */ skip++; /* skip it */ else { ch=fgetarg(zin,word); /* Get the symbol */ #ifdef DEBUG printf(" %s\n",word); #endif switch(find(word)) { case DEF : break; case IGN : printf ("#ifdef %s%c",word,ch); Print; ch=getendif(); /* Get matching endif */ break; /* If symbol undefined */ default : Undefine(word,MUTABLE); /* undefine it */ proc=0; /* & stop processing */ } } Goto; continue; } if (!strcmp(word,"ifndef")) /* Ifndef: */ { if (!proc) /* If not processing */ skip++; /* skip the line */ else { ch=fgetarg(zin,word); /* Get the symbol */ #ifdef DEBUG printf(" %s\n",word); #endif switch(find(word)) /* If defined, stop */ { case DEF : proc=0; /* processing */ break; case IGN : printf ("#ifdef %s%c",word,ch); Print; ch=getendif(); /* Get matching endif */ break; } } Goto; continue; } if (!strcmp(word,"else") && !skip) /* Else: Flip processing */ { proc=!proc; Goto; continue; } if (!strcmp(word,"endif")) /* Endif: If no skipped */ { /* ifdefs turn processing */ if (!skip) /* on, else decrement the */ proc=1; /* number of skips */ else skip--; Goto; continue; } if (proc) /* The word fails all of the */ { /* above tests, so if we're */ printf("#%s%c",word,ch); /* processing, print out the */ Print; /* line */ } else Goto; } } usage() { fprintf(stderr,"Usage: ifdef [-t] [-Dsymbol] [-dsymbol] [-Usymbol] [-Isymbol] <file>\n"); exit(0); } main(argc,argv) int argc; char *argv[]; { extern int optind; extern char *optarg; char sym[80]; /* Temp symbol storage */ char c; while ((c=getopt(argc,argv,"tD:d:U:I:"))!=EOF) switch(c) { case 't' : table=1; /* Get the various options */ break; case 'd' : strcpy(sym,optarg); Define(sym,MUTABLE); break; case 'D' : strcpy(sym,optarg); Define(sym,IMMUTABLE); break; case 'U' : strcpy(sym,optarg); Undefine(sym,IMMUTABLE); break; case 'I' : strcpy(sym,optarg); Ignore(sym,IMMUTABLE); break; default : usage(); } zin=stdin; if (*argv[argc-1]!='-') /* If a C file is named */ { /* Open stdin with it */ if ((zin=fopen(argv[argc-1],"r"))==NULL) { perror("ifdef"); exit(1); } } if (table) gettable(); /* Either generate a table or */ else parse(); /* parse & replace with the file */ } //E*O*F ifdef.c// exit 0