[comp.os.minix] Ifdef

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