[comp.os.minix] [patch] to ifdef - copes with #ifs

wkt@ccadfa.adfa.oz.au (Warren Toomey) (11/14/90)

Before 1.6 hits the beta mailing list, here's a new version of ifdef(1)
that copes with #if's. Whole source, as the diffs were longer. I also
include a manual, as 1.5 didn't have one.

I just noticed a patch for Clam under MacMinix - I'll have to integrate
that into the next version, which is due out soon, if I ever get
re-enthused with it. Also in the pipeline is a new version of dump(8),
which does multi-vol backups.

	Warren Toomey


echo x - ifdef.c
sed '/^X/s///' > ifdef.c << '/'
X/* Ifdef - remove #ifdefs		Author: Warren Toomey */
X
X/* Ifdef: Remove unwanted ifdefs from C code	   */
X/* Written by Warren Toomey - 1989		   */
X/* You may freely copy or give away this source as */
X/* Long as this notice remains intact.		   */
X/* 890916 - Added usage() before getopt() - meb    */
X/* 900517 - Added (limited) support for #if's      */
X
X#include <sys/types.h>
X#include <string.h>
X#include <stdlib.h>
X#include <stdio.h>
X
X/* Definition of structures and constants used in ifdef.c  */
X
X/* Types of symbols */
X#define DEF	  1		/* Symbol is defined    */
X#define UNDEF	  2		/* Symbol isn't defined */
X#define IGN	  3		/* Ignore this symbol unless defined */
X
X/* Redef mode values */
X#define MUTABLE   1		/* Symbol can change defined <-> undefined */
X#define IMMUTABLE 2		/* Symbol can't change as above            */
X
X/* Processing modes */
X#define NO	0		/* Don't process */
X#define YES	1		/* Process */
X
X/* Ignore (IGN), ignore but process */
Xstruct DEFINE {
X  char *symbol;			/* SLL of defined symbols. The redef  */
X  char type;			/* field indicates if this symbol can */
X  char redef;			/* change from defined <-> undefined. */
X  struct DEFINE *next;		/* Type is DEF or UNDEF.	      */
X};
X
X/* Global variables & structures */
XFILE *zin;			/* Input file for processing  */
Xstruct DEFINE *defptr;		/* Defined symbols SLL        */
Xstruct DEFINE *defend;		/* Ptr to last node in defptr */
Xstruct DEFINE *deftemp;		/* Ptr to last found node     */
Xint line = 1;			/* Current line number        */
Xint table = 0;			/* Don't normally want a table */
X
X/* Forward declarations, sigh */
Xvoid printtable();
X
X#ifdef __STDC__
Xchar fgetarg ( FILE *stream , char *cbuf )
X#else
Xchar fgetarg(stream, cbuf)	/* Get next arg from file into cbuf, */
XFILE *stream;			/* returning the character that      */
Xchar *cbuf;			/* terminated it. Cbuf returns 0     */
X#endif
X{				/* if no arg. EOF is returned if no  */
X  int ch;			/* args left in file.                */
X  int i;
X
X  i = 0;
X  cbuf[i] = 0;
X
X  while (((ch = fgetc(stream)) == ' ') || (ch == '\t') || (ch == '\n'))
X	if (ch == '\n') return(ch);	/* Bypass leading */
X					/* Whitespace     */
X  if (feof(stream)) return(EOF);
X
X  cbuf[i++] = ch;
X
X  while (((ch = fgetc(stream)) != ' ') && (ch != '\t') && (ch != '\n'))
X	cbuf[i++] = ch;			/* Get the argument */
X
X  cbuf[i] = 0;
X  return(ch);
X}
X
X
X#ifdef __STDC__
Xint find ( char *sym )
X#else
Xint find(sym)
Xchar *sym;
X#endif
X{				/* Return DEF if defined else UNDEF */
X
X  deftemp = defptr;
X  while (deftemp) {			/* Search for the symbol */
X	if (!strcmp(deftemp->symbol, sym))
X		return(deftemp->type);	/* Setting up the type */
X	deftemp = deftemp->next;
X  }
X  return(0);
X}
X
X
X
X#define Define(x,y)	defit(x,y,DEF)
X#define Undefine(x,y)	defit(x,y,UNDEF)
X#define Ignore(x,y)	defit(x,y,IGN)
X
X#ifdef __STDC__
Xvoid defit ( char *sym , int redef , int type )
X#else
Xvoid defit(sym, redef, type)	/* Add symbol to the define list */
Xchar *sym;
Xchar redef;			/* Mode: MUTABLE etc      */
Xchar type;			/* Type: DEF, UNDEF, IGN  */
X#endif
X{
X  struct DEFINE *temp;
X  char c;
X
X  c = find(sym);		/* First try finding the symbol */
X  if (type == c) return;	/* Return if already declared */
X  if (c) {			/* We have to move if from DEF <-> UNDEF */
X	if (deftemp->redef == IMMUTABLE)
X		return;
X	else {
X		deftemp->type = type;
X		deftemp->redef = redef;
X	}
X  } else {			/* We must create a struct & add it */
X				/* Malloc room for the struct */
X	if ((temp = (struct DEFINE *)malloc((unsigned)sizeof(struct DEFINE))) == NULL) {
X		(void)fprintf(stderr, "ifdef: could not malloc\n");
X		exit(1);
X	}
X
X					/* Malloc room for symbol */
X	if ((temp->symbol = (char *)malloc((unsigned)strlen(sym) + 1)) == NULL) {
X		(void)fprintf(stderr, "ifdef: could not malloc\n");
X		exit(1);
X	}
X	(void)strcpy(temp->symbol, sym); /* Copy symbol into struct      */
X	temp->redef = redef;		/* and set its redef mode too   */
X	temp->type = type;		/* as well as making it defined */
X
X
X					/* Now add to the SLL */
X	if (defptr == NULL)		/* If first node set  */
X		defptr = temp;		/* the pointers to it */
X	else
X		defend->next = temp;	/* else add it to the */
X	defend = temp;			/* end of the list.   */
X  }
X}
X
X
X
X#ifdef __STDC__
Xvoid stop ( void )
X#else
Xvoid stop()
X#endif
X{				/* Stop: Tidy up at EOF */
X  if (table) printtable();
X  (void)fclose(zin);
X  exit(0);
X}
X
X#define Goto	{ line++; if (ch!='\n') gotoeoln(); }
X#define Print	{ line++; if (ch!='\n') prteoln();  }
X
X#ifdef __STDC__
Xvoid gotoeoln ( void )
X#else
Xvoid gotoeoln()			/* Go to the end of the line */
X#endif
X{
X  int ch;
X  while ((ch = fgetc(zin)) != '\n')
X	if (ch == EOF) stop();
X}
X
X
X#ifdef __STDC__
Xvoid prteoln ( void )
X#else
Xvoid prteoln()			/* Print to the end of the line */
X#endif
X{
X  int ch;
X  while ((ch = fgetc(zin)) != '\n')
X	if (ch == EOF)
X		stop();
X	else
X		(void)putchar(ch);
X  (void)putchar('\n');
X}
X
X
X#ifdef __STDC__
Xvoid printtable ( void )
X#else
Xvoid printtable()		/* Print the defines in the SLL */
X#endif
X{
X  struct DEFINE *temp;
X
X  (void)printf("Defined\n\n");
X
X  temp = defptr;
X  while (temp) {
X	if (temp->type == DEF) (void)printf("%s\n", temp->symbol);
X	temp = temp->next;
X  }
X
X  (void)printf("\n\nUndefined\n\n");
X
X  temp = defptr;
X  while (temp) {
X	if (temp->type == UNDEF) (void)printf("%s\n", temp->symbol);
X	temp = temp->next;
X  }
X}
X
X#ifdef __STDC__
Xchar getendif ( void )
X#else
Xchar getendif()
X#endif
X{				/* Find matching endif when ignoring */
X  char word[80];		/* Buffer for symbols */
X  int ch;
X  int skip;			/* Number of skipped #ifdefs */
X
X  skip = 1;
X
X  while (1) {
X			/* Scan through the file looking for starting lines */
X	if ((ch = fgetc(zin)) == EOF)
X		stop();		/* Get first char on the line */
X	if (ch != '#') {	/* If not a # ignore line     */
X		(void)putchar(ch);
X		Print;
X		continue;
X	}
X	ch = fgetarg(zin, word);	/* Get the word after the # */
X
X	if (!strcmp(word, "ifdef") || !strcmp(word, "ifndef")) skip++;
X						/* Keep track of ifdefs & */
X	if (!strcmp(word, "endif")) skip--;	/* endifs		  */
X
X	(void)printf("#%s%c", word, ch);	/* Print the line out 	  */
X	Print;
X	if (!skip) return('\n');	/* If matching endif, return */
X  }
X}
X
X
X#ifdef __STDC__
Xvoid gettable ( void )
X#else
Xvoid gettable()			/* Get & print a table of defines etc.  */
X#endif
X{
X
X  char word[80];		/* Buffer for symbols */
X  int ch;
X
X  while (1) {
X			/* Scan through the file looking for starting lines */
X	if ((ch = fgetc(zin)) == EOF)
X		stop();		/* Get first char on the line */
X	if (ch != '#') {	/* If not a # ignore line     */
X		Goto;
X		continue;
X	}
X	ch = fgetarg(zin, word);	/* Get the word after the # */
X
X	if (!strcmp(word, "define")) {		/* Define: Define the */
X		ch = fgetarg(zin, word);	/* symbol, and goto   */
X		Define(word, MUTABLE);		/* the end of line    */
X		Goto;
X		continue;
X	}
X	if (!strcmp(word, "undef")) {		/* Undef: Undefine the */
X		ch = fgetarg(zin, word);	/* symbol, and goto    */
X		Undefine(word, MUTABLE);	/* the end of line     */
X		Goto;
X		continue;
X	}					/* Ifdef:            */
X	if (!strcmp(word, "ifdef") || !strcmp(word, "ifndef")) {
X		ch = fgetarg(zin, word);	/* Get the symbol */
X		if (find(word) != DEF)
X			Undefine(word, MUTABLE);	/* undefine it */
X		Goto;
X		continue;
X	}
X	Goto;				/* else ignore the line */
X  }
X}
X
X
X
X#ifdef __STDC__
Xvoid parse ( void )
X#else
Xvoid parse()
X#endif
X{				/* Parse & remove ifdefs from C source */
X  char word[80];		/* Buffer for symbols */
X  int ch;
X  int proc;			/* Should we be processing this bit?    */
X  int skip;			/* Number of skipped #ifdefs		 */
X
X  proc = 1;
X  skip = 0;
X
X  while (1) {
X			/* Scan through the file looking for starting lines */
X	if ((ch = fgetc(zin)) == EOF)
X		stop();		/* Get first char on the line */
X	if (ch != '#')
X		if (proc) {	/* If not # and  we're processing */
X			(void)putchar(ch); /* then print the line */
X			Print;
X			continue;
X		} else {
X			Goto;	/* else just skip the line  */
X			continue;
X		}
X
X	ch = fgetarg(zin, word);	/* Get the word after the # */
X
X	if (!strcmp(word, "define") && proc) {	/* Define: Define the */
X		ch = fgetarg(zin, word);	/* symbol, and goto   */
X		Define(word, MUTABLE);		/* the end of line    */
X		(void)printf("#define %s%c", word, ch);
X		Print;
X		continue;
X	}
X	if (!strcmp(word, "undef") && proc) {	/* Undef: Undefine the */
X		ch = fgetarg(zin, word);	/* symbol, and goto    */
X		Undefine(word, MUTABLE);	/* the end of line     */
X		(void)printf("#undef %s%c", word, ch);
X		Print;
X		continue;
X	}
X	if (!strcmp(word, "if")) {	/* If: we cannot handle these */
X		if (!proc)		/* at the moment, so just */
X			skip++;		/* treat them as an ignored */
X		else {			/* definition */
X			(void)printf("#%s%c",word,ch);
X			Print;
X			ch = getendif();	/* Get matching endif */
X			continue;
X		     	}
X	}
X	if (!strcmp(word, "ifdef")) {	/* Ifdef:	     */
X		if (!proc)		/* If not processing */
X			skip++;		/* skip it           */
X		else {
X			ch = fgetarg(zin, word); /* Get the symbol */
X			switch (find(word)) {
X			    case DEF:
X				break;
X			    case IGN:
X				(void)printf("#ifdef %s%c", word, ch);
X				Print;
X				ch = getendif(); /* Get matching endif */
X				break;
X						/* If symbol undefined */
X			    default:
X				Undefine(word, MUTABLE); /* undefine it */
X				proc = 0;	/* & stop processing */
X			}
X		}
X		Goto;
X		continue;
X	}
X	if (!strcmp(word, "ifndef")) {
X		/* Ifndef: */
X		if (!proc)	/* If not processing */
X			skip++;	/* skip the line     */
X		else {
X			ch = fgetarg(zin, word); /* Get the symbol */
X			switch (find(word)) {	/* If defined, stop */
X			    case DEF:
X				proc = 0;	/* processing       */
X				break;
X			    case IGN:
X				(void)printf("#ifdef %s%c", word, ch);
X				Print;
X				ch = getendif(); /* Get matching endif */
X				break;
X			}
X		}
X		Goto;
X		continue;
X	}
X	if (!strcmp(word, "else") && !skip) {	/* Else: Flip processing */
X		proc = !proc;
X		Goto;
X		continue;
X	}
X	if (!strcmp(word, "endif")) {	/* Endif: If no skipped   */
X					/* ifdefs turn processing */
X		if (!skip)		/* on, else decrement the */
X			proc = 1;	/* number of skips        */
X		else
X			skip--;
X		Goto;
X		continue;
X	}
X		/* The word fails all of the above tests, so if we're */
X		/* processing, print the line. */
X	if (proc) {
X		(void)printf("#%s%c", word, ch);
X		Print;
X	} else
X		Goto;
X  }
X}
X
X
X#ifdef __STDC__
Xvoid usage ( void )
X#else
Xvoid usage()
X#endif
X{
X  (void)fprintf(stderr, "Usage: ifdef [-t] [-Dsymbol] [-dsymbol] [-Usymbol] [-Isymbol] <file>\n");
X  exit(0);
X}
X
X
X#ifdef __STDC__
Xint main ( int argc , char *argv [])
X#else
Xint main(argc, argv)
Xint argc;
Xchar *argv[];
X#endif
X{
X  extern int optind;
X  extern char *optarg;
X
X  char sym[80];			/* Temp symbol storage */
X  int c;
X
X  if (argc == 1) usage();	/* Catch the curious user	 */
X  while ((c = getopt(argc, argv, "tD:d:U:I:")) != EOF) {
X	switch (c) {
X	    case 't':
X		table = 1;	/* Get the various options */
X		break;
X
X	    case 'd':
X		(void)strcpy(sym, optarg);
X		Define(sym, MUTABLE);
X		break;
X
X	    case 'D':
X		(void)strcpy(sym, optarg);
X		Define(sym, IMMUTABLE);
X		break;
X
X	    case 'U':
X		(void)strcpy(sym, optarg);
X		Undefine(sym, IMMUTABLE);
X		break;
X
X	    case 'I':
X		(void)strcpy(sym, optarg);
X		Ignore(sym, IMMUTABLE);
X		break;
X
X	    default:	usage();
X	}
X  }
X
X  zin = stdin;		/* If a C file is named */
X			/* Open stdin with it */
X  if (*argv[argc - 1] != '-') {
X	(void)fclose(zin);
X	if ((zin = fopen(argv[argc - 1], "r")) == NULL) {
X		perror("ifdef");
X		exit(1);
X	}
X  }
X  if (table)
X	gettable();		/* Either generate a table or    */
X  else
X	parse();		/* parse & replace with the file */
X}
/
echo x - ifdef.1
sed '/^X/s///' > ifdef.1 << '/'
X.TH ifdef 1L
X.SH NAME
Xifdef \- remove conditional code from C files
X.SH SYNTAX
Xifdef [\-t] [\-dsymbol] [\-Dsymbol] [\-Usymbol] [\-Isymbol] <file>
X.SH OPTIONS
XOptions are processed by
X.B getopts(3).
X.TP 8
X.B \-t   
XTable mode.
X.I Ifdef
Xproduces a table of the defined and undefined symbols in the given file.
X.TP
X.B \-dsymbol
XDefine the symbol before processing takes place. Note that if the symbol
Xis later #undef'd, then it will become undefined.
X.TP
X.B \-Dsymbol
XDefine the symbol, and make it 
X.I immutable.
XThat is, later #undef's will not undefine the symbol.
X.TP
X.B \-Usymbol
XUndefine the symbol, and make it
X.I immutable.
XSuccessive #define's will not define this symbol.
X.TP
X.B \-Isymbol
XIgnore this symbol in processing. #ifdef's using this symbol will be
Xleft in the output code.
X.SH DESCRIPTION
X.I Ifdef
Xis a small program which allows conditional code [ #ifdef ... #endif ]
Xto be selectively removed from C files, but at the same time leaving
Xall other C preprocessor commands intact [ #define, #include etc ].
XThe resulting code can then be viewed or compiled with any unnecessary
Xconditional code missing.
X.PP
XInput to
X.I ifdef
Xis either a
X.B file
Xnamed as the last argument, or standard input if no file is named.
X.I Ifdef
Xtakes the input file, and processes it, removing unwanted conditional
Xcode. The resulting code is placed on the standard output.
X.PP
XSymbols may be
Xdefined with the
X.B \-d
Xand
X.B \-D
Xoptions ( like the C preprocessor ), except that the latter option
Xmakes the symbol
X.I immutable,
Xand hence cannot be #undef'd at a later time. Similarly, the
X.B \-U
Xoption undefines a symbol
X.I immutably,
Xpreventing it from being #define'd at a later time. Note that symbols
Xnot mentioned at runtime are undefined but mutable.
X.PP
X.I Ifdef
Xignores any values given to a symbol; in fact, if given an option of
Xthe form
X.PP
X\-DLEVEL=22
X.PP
Xthen it will assume the defined symbol is 
X.B LEVEL=22.
X.PP
XThe 
X.B \-I
Xoption allows a specific symbol to be
Xignored thoughout the processing of the file. Any #ifdef code
Xusing the symbol will be left intact in the output code.
X.PP
XFinally, the
X.B \-t
Xoption puts
X.I ifdef
Xinto table mode; instead of putting the resulting code on the standard
Xoutput,
X.I ifdef
Xreturns a list of symbols that were defined or not defined in the code.
XThis is useful for finding out what symbols are tested for in a new
Xpiece of code. When this option is used, all #defines will be found, so
Xthat
X.PP
X.B #define FOO
X.PP
X.B #undef FOO
X.PP
Xwill make
X.I ifdef
Xbelieve FOO is undefined, although it may have been defined for parts of the
Xcode.
X.SH BUGS
XNo checking is done to see if the conditional code is semantically
Xcorrect. Missing or extra #ifdef's etc. are not detected.
X.I Ifdef
Xprovides limited support for the preprocessor directives #if and #elif, by
Xtreating them as an ignored symbol, and leaving the enclosed code in the
Xoutput.
X.PP
X.SH SEE ALSO
Xcpp(1), scpp(1L).
/
-- 
   Warren Toomey VK1XWT, rescreened.
Deep in the bowels of ADFA Comp Science.
      `What the hell is SIGTTIN?'