386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Tue, 16 Apr 1991 23:50:28 +0000 (15:50 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Tue, 16 Apr 1991 23:50:28 +0000 (15:50 -0800)
Work on file usr/src/usr.bin/tsort/tsort.c
Work on file usr/src/usr.bin/unifdef/unifdef.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/usr.bin/tsort/tsort.c [new file with mode: 0644]
usr/src/usr.bin/unifdef/unifdef.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/tsort/tsort.c b/usr/src/usr.bin/tsort/tsort.c
new file mode 100644 (file)
index 0000000..a484d9b
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Rendell of Memorial University of Newfoundland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tsort.c    5.3 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+/*
+ *  Topological sort.  Input is a list of pairs of strings seperated by
+ *  white space (spaces, tabs, and/or newlines); strings are written to
+ *  standard output in sorted order, one per line.
+ *
+ *  usage:
+ *     tsort [inputfile]
+ *  If no input file is specified, standard input is read.
+ *
+ *  Should be compatable with AT&T tsort HOWEVER the output is not identical
+ *  (i.e. for most graphs there is more than one sorted order, and this tsort
+ *  usually generates a different one then the AT&T tsort).  Also, cycle
+ *  reporting seems to be more accurate in this version (the AT&T tsort
+ *  sometimes says a node is in a cycle when it isn't).
+ *
+ *  Michael Rendell, michael@stretch.cs.mun.ca - Feb 26, '90
+ */
+#define        HASHSIZE        53              /* doesn't need to be big */
+#define        NF_MARK         0x1             /* marker for cycle detection */
+#define        NF_ACYCLIC      0x2             /* this node is cycle free */
+
+typedef struct node_str NODE;
+
+struct node_str {
+       char *n_name;                   /* name of this node */
+       NODE **n_prevp;                 /* pointer to previous node's n_next */
+       NODE *n_next;                   /* next node in graph */
+       NODE *n_hash;                   /* next node in hash table */
+       int n_narcs;                    /* number of arcs in n_arcs[] */
+       int n_arcsize;                  /* size of n_arcs[] array */
+       NODE **n_arcs;                  /* array of arcs to other nodes */
+       int n_refcnt;                   /* # of arcs pointing to this node */
+       int n_flags;                    /* NF_* */
+};
+
+typedef struct _buf {
+       char *b_buf;
+       int b_bsize;
+} BUF;
+
+NODE *add_node(), *find_node();
+void add_arc(), no_memory(), remove_node(), tsort();
+char *grow_buf(), *malloc();
+
+extern int errno;
+NODE *graph;
+NODE *hashtable[HASHSIZE];
+NODE **cycle_buf;
+NODE **longest_cycle;
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       register BUF *b;
+       register int c, n;
+       FILE *fp;
+       int bsize, nused;
+       BUF bufs[2];
+
+       if (argc < 2)
+               fp = stdin;
+       else if (argc == 2) {
+               (void)fprintf(stderr, "usage: tsort [ inputfile ]\n");
+               exit(1);
+       } else if (!(fp = fopen(argv[1], "r"))) {
+               (void)fprintf(stderr, "tsort: %s.\n", strerror(errno));
+               exit(1);
+       }
+
+       for (b = bufs, n = 2; --n >= 0; b++)
+               b->b_buf = grow_buf((char *)NULL, b->b_bsize = 1024);
+
+       /* parse input and build the graph */
+       for (n = 0, c = getc(fp);;) {
+               while (c != EOF && isspace(c))
+                       c = getc(fp);
+               if (c == EOF)
+                       break;
+
+               nused = 0;
+               b = &bufs[n];
+               bsize = b->b_bsize;
+               do {
+                       b->b_buf[nused++] = c;
+                       if (nused == bsize) {
+                               bsize *= 2;
+                               b->b_buf = grow_buf(b->b_buf, bsize);
+                       }
+                       c = getc(fp);
+               } while (c != EOF && !isspace(c));
+
+               b->b_buf[nused] = '\0';
+               b->b_bsize = bsize;
+               if (n)
+                       add_arc(bufs[0].b_buf, bufs[1].b_buf);
+               n = !n;
+       }
+       (void)fclose(fp);
+       if (n) {
+               (void)fprintf(stderr, "tsort: odd data count.\n");
+               exit(1);
+       }
+
+       /* do the sort */
+       tsort();
+       exit(0);
+}
+
+/* double the size of oldbuf and return a pointer to the new buffer. */
+char *
+grow_buf(bp, size)
+       char *bp;
+       int size;
+{
+       char *realloc();
+
+       if (!(bp = realloc(bp, (u_int)size)))
+               no_memory();
+       return(bp);
+}
+
+/*
+ * add an arc from node s1 to node s2 in the graph.  If s1 or s2 are not in
+ * the graph, then add them.
+ */
+void
+add_arc(s1, s2)
+       char *s1, *s2;
+{
+       register NODE *n1;
+       NODE *n2;
+       int bsize;
+
+       n1 = find_node(s1);
+       if (!n1)
+               n1 = add_node(s1);
+
+       if (!strcmp(s1, s2))
+               return;
+
+       n2 = find_node(s2);
+       if (!n2)
+               n2 = add_node(s2);
+
+       /*
+        * could check to see if this arc is here already, but it isn't
+        * worth the bother -- there usually isn't and it doesn't hurt if
+        * there is (I think :-).
+        */
+       if (n1->n_narcs == n1->n_arcsize) {
+               if (!n1->n_arcsize)
+                       n1->n_arcsize = 10;
+               bsize = n1->n_arcsize * sizeof(*n1->n_arcs) * 2;
+               n1->n_arcs = (NODE **)grow_buf((char *)n1->n_arcs, bsize);
+               n1->n_arcsize = bsize / sizeof(*n1->n_arcs);
+       }
+       n1->n_arcs[n1->n_narcs++] = n2;
+       ++n2->n_refcnt;
+}
+
+hash_string(s)
+       char *s;
+{
+       register int hash, i;
+
+       for (hash = 0, i = 1; *s; s++, i++)
+               hash += *s * i;
+       return(hash % HASHSIZE);
+}
+
+/*
+ * find a node in the graph and return a pointer to it - returns null if not
+ * found.
+ */
+NODE *
+find_node(name)
+       char *name;
+{
+       register NODE *n;
+
+       for (n = hashtable[hash_string(name)]; n; n = n->n_hash)
+               if (!strcmp(n->n_name, name))
+                       return(n);
+       return((NODE *)NULL);
+}
+
+/* Add a node to the graph and return a pointer to it. */
+NODE *
+add_node(name)
+       char *name;
+{
+       register NODE *n;
+       int hash;
+
+       if (!(n = (NODE *)malloc(sizeof(NODE))) || !(n->n_name = strdup(name)))
+               no_memory();
+
+       n->n_narcs = 0;
+       n->n_arcsize = 0;
+       n->n_arcs = (NODE **)NULL;
+       n->n_refcnt = 0;
+       n->n_flags = 0;
+
+       /* add to linked list */
+       if (n->n_next = graph)
+               graph->n_prevp = &n->n_next;
+       n->n_prevp = &graph;
+       graph = n;
+
+       /* add to hash table */
+       hash = hash_string(name);
+       n->n_hash = hashtable[hash];
+       hashtable[hash] = n;
+       return(n);
+}
+
+/* do topological sort on graph */
+void
+tsort()
+{
+       register NODE *n, *next;
+       register int cnt;
+
+       while (graph) {
+               /*
+                * keep getting rid of simple cases until there are none left,
+                * if there are any nodes still in the graph, then there is
+                * a cycle in it.
+                */
+               do {
+                       for (cnt = 0, n = graph; n; n = next) {
+                               next = n->n_next;
+                               if (n->n_refcnt == 0) {
+                                       remove_node(n);
+                                       ++cnt;
+                               }
+                       }
+               } while (graph && cnt);
+
+               if (!graph)
+                       break;
+
+               if (!cycle_buf) {
+                       /*
+                        * allocate space for two cycle logs - one to be used
+                        * as scratch space, the other to save the longest
+                        * cycle.
+                        */
+                       for (cnt = 0, n = graph; n; n = n->n_next)
+                               ++cnt;
+                       cycle_buf =
+                           (NODE **)malloc((u_int)sizeof(NODE *) * cnt);
+                       longest_cycle =
+                           (NODE **)malloc((u_int)sizeof(NODE *) * cnt);
+                       if (!cycle_buf || !longest_cycle)
+                               no_memory();
+               }
+               for (n = graph; n; n = n->n_next)
+                       if (!(n->n_flags & NF_ACYCLIC)) {
+                               if (cnt = find_cycle(n, n, 0, 0)) {
+                                       register int i;
+
+                                       (void)fprintf(stderr,
+                                           "tsort: cycle in data.\n");
+                                       for (i = 0; i < cnt; i++)
+                                               (void)fprintf(stderr,
+                               "tsort: %s.\n", longest_cycle[i]->n_name);
+                                       remove_node(n);
+                                       break;
+                               } else
+                                       /* to avoid further checks */
+                                       n->n_flags  = NF_ACYCLIC;
+                       }
+
+               if (!n) {
+                       (void)fprintf(stderr,
+                           "tsort: internal error -- could not find cycle.\n");
+                       exit(1);
+               }
+       }
+}
+
+/* print node and remove from graph (does not actually free node) */
+void
+remove_node(n)
+       register NODE *n;
+{
+       register NODE **np;
+       register int i;
+
+       (void)printf("%s\n", n->n_name);
+       for (np = n->n_arcs, i = n->n_narcs; --i >= 0; np++)
+               --(*np)->n_refcnt;
+       n->n_narcs = 0;
+       *n->n_prevp = n->n_next;
+       if (n->n_next)
+               n->n_next->n_prevp = n->n_prevp;
+}
+
+/* look for the longest cycle from node from to node to. */
+find_cycle(from, to, longest_len, depth)
+       NODE *from, *to;
+       int depth, longest_len;
+{
+       register NODE **np;
+       register int i, len;
+
+       /*
+        * avoid infinite loops and ignore portions of the graph known
+        * to be acyclic
+        */
+       if (from->n_flags & (NF_MARK|NF_ACYCLIC))
+               return(0);
+       from->n_flags = NF_MARK;
+
+       for (np = from->n_arcs, i = from->n_narcs; --i >= 0; np++) {
+               cycle_buf[depth] = *np;
+               if (*np == to) {
+                       if (depth + 1 > longest_len) {
+                               longest_len = depth + 1;
+                               (void)memcpy((char *)longest_cycle,
+                                   (char *)cycle_buf,
+                                   longest_len * sizeof(NODE *));
+                       }
+               } else {
+                       len = find_cycle(*np, to, longest_len, depth + 1);
+                       if (len > longest_len)
+                               longest_len = len;
+               }
+       }
+       from->n_flags &= ~NF_MARK;
+       return(longest_len);
+}
+
+void
+no_memory()
+{
+       (void)fprintf(stderr, "tsort: %s.\n", strerror(ENOMEM));
+       exit(1);
+}
diff --git a/usr/src/usr.bin/unifdef/unifdef.c b/usr/src/usr.bin/unifdef/unifdef.c
new file mode 100644 (file)
index 0000000..399825e
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) 1985 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Dave Yost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1985 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)unifdef.c  4.7 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+/*
+ * unifdef - remove ifdef'ed lines
+ *
+ *  Warning: will not work correctly if input contains null characters.
+ *
+ *  Wishlist:
+ *      provide an option which will append the name of the
+ *        appropriate symbol after #else's and #endif's
+ *      provide an option which will check symbols after
+ *        #else's and #endif's to see that they match their
+ *        corresponding #ifdef or #ifndef
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#define BSS
+FILE *input;
+#ifndef YES
+#define YES 1
+#define NO  0
+#endif/*YES */
+typedef int Bool;
+
+char *progname BSS;
+char *filename BSS;
+char text BSS;          /* -t option in effect: this is a text file */
+char lnblank BSS;       /* -l option in effect: blank deleted lines */
+char complement BSS;    /* -c option in effect: complement the operation */
+
+#define MAXSYMS 100
+char *symname[MAXSYMS] BSS; /* symbol name */
+char true[MAXSYMS] BSS;     /* -Dsym */
+char ignore[MAXSYMS] BSS;   /* -iDsym or -iUsym */
+char insym[MAXSYMS] BSS;    /* state: false, inactive, true */
+#define SYM_INACTIVE 0      /* symbol is currently inactive */
+#define SYM_FALSE    1      /* symbol is currently false */
+#define SYM_TRUE     2      /* symbol is currently true  */
+
+char nsyms BSS;
+char incomment BSS;         /* inside C comment */
+
+#define QUOTE_NONE   0
+#define QUOTE_SINGLE 1
+#define QUOTE_DOUBLE 2
+char inquote BSS;           /* inside single or double quotes */
+
+int exitstat BSS;
+char *skipcomment ();
+char *skipquote ();
+
+main (argc, argv)
+int argc;
+char **argv;
+{
+    char **curarg;
+    register char *cp;
+    register char *cp1;
+    char ignorethis;
+
+    progname = argv[0][0] ? argv[0] : "unifdef";
+
+    for (curarg = &argv[1]; --argc > 0; curarg++) {
+       if (*(cp1 = cp = *curarg) != '-')
+           break;
+       if (*++cp1 == 'i') {
+           ignorethis = YES;
+           cp1++;
+       } else
+           ignorethis = NO;
+       if (   (   *cp1 == 'D'
+               || *cp1 == 'U'
+              )
+           && cp1[1] != '\0'
+          ) {
+           register int symind;
+
+           if ((symind = findsym (&cp1[1])) < 0) {
+               if (nsyms >= MAXSYMS) {
+                   prname ();
+                   fprintf (stderr, "too many symbols.\n");
+                   exit (2);
+               }
+               symind = nsyms++;
+               symname[symind] = &cp1[1];
+               insym[symind] = SYM_INACTIVE;
+           }
+           ignore[symind] = ignorethis;
+           true[symind] = *cp1 == 'D' ? YES : NO;
+       } else if (ignorethis)
+           goto unrec;
+       else if (strcmp (&cp[1], "t") == 0)
+           text = YES;
+       else if (strcmp (&cp[1], "l") == 0)
+           lnblank = YES;
+       else if (strcmp (&cp[1], "c") == 0)
+           complement = YES;
+       else {
+ unrec:
+           prname ();
+           fprintf (stderr, "unrecognized option: %s\n", cp);
+           goto usage;
+       }
+    }
+    if (nsyms == 0) {
+ usage:
+       fprintf (stderr, "\
+Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n\
+    At least one arg from [-D -U -iD -iU] is required\n", progname);
+       exit (2);
+    }
+
+    if (argc > 1) {
+       prname ();
+       fprintf (stderr, "can only do one file.\n");
+    } else if (argc == 1) {
+       filename = *curarg;
+       if ((input = fopen (filename, "r")) != NULL) {
+           pfile();
+           (void) fclose (input);
+       } else {
+           prname ();
+           fprintf (stderr, "can't open ");
+           perror(*curarg);
+       }
+    } else {
+       filename = "[stdin]";
+       input = stdin;
+       pfile();
+    }
+
+    (void) fflush (stdout);
+    exit (exitstat);
+}
+
+/* types of input lines: */
+typedef int Linetype;
+#define LT_PLAIN       0   /* ordinary line */
+#define LT_TRUE        1   /* a true  #ifdef of a symbol known to us */
+#define LT_FALSE       2   /* a false #ifdef of a symbol known to us */
+#define LT_OTHER       3   /* an #ifdef of a symbol not known to us */
+#define LT_IF          4   /* an #ifdef of a symbol not known to us */
+#define LT_ELSE        5   /* #else */
+#define LT_ENDIF       6   /* #endif */
+#define LT_LEOF        7   /* end of file */
+extern Linetype checkline ();
+
+typedef int Reject_level;
+Reject_level reject BSS;    /* 0 or 1: pass thru; 1 or 2: ignore comments */
+#define REJ_NO          0
+#define REJ_IGNORE      1
+#define REJ_YES         2
+
+int linenum BSS;    /* current line number */
+int stqcline BSS;   /* start of current coment or quote */
+char *errs[] = {
+#define NO_ERR      0
+                       "",
+#define END_ERR     1
+                       "",
+#define ELSE_ERR    2
+                       "Inappropriate else",
+#define ENDIF_ERR   3
+                       "Inappropriate endif",
+#define IEOF_ERR    4
+                       "Premature EOF in ifdef",
+#define CEOF_ERR    5
+                       "Premature EOF in comment",
+#define Q1EOF_ERR   6
+                       "Premature EOF in quoted character",
+#define Q2EOF_ERR   7
+                       "Premature EOF in quoted string"
+};
+
+/* States for inif arg to doif */
+#define IN_NONE 0
+#define IN_IF   1
+#define IN_ELSE 2
+
+pfile ()
+{
+    reject = REJ_NO;
+    (void) doif (-1, IN_NONE, reject, 0);
+    return;
+}
+
+int
+doif (thissym, inif, prevreject, depth)
+register int thissym;   /* index of the symbol who was last ifdef'ed */
+int inif;               /* YES or NO we are inside an ifdef */
+Reject_level prevreject;/* previous value of reject */
+int depth;              /* depth of ifdef's */
+{
+    register Linetype lineval;
+    register Reject_level thisreject;
+    int doret;          /* tmp return value of doif */
+    int cursym;         /* index of the symbol returned by checkline */
+    int stline;         /* line number when called this time */
+
+    stline = linenum;
+    for (;;) {
+       switch (lineval = checkline (&cursym)) {
+       case LT_PLAIN:
+           flushline (YES);
+           break;
+
+       case LT_TRUE:
+       case LT_FALSE:
+           thisreject = reject;
+           if (lineval == LT_TRUE)
+               insym[cursym] = SYM_TRUE;
+           else {
+               if (reject != REJ_YES)
+                   reject = ignore[cursym] ? REJ_IGNORE : REJ_YES;
+               insym[cursym] = SYM_FALSE;
+           }
+           if (ignore[cursym])
+               flushline (YES);
+           else {
+               exitstat = 1;
+               flushline (NO);
+           }
+           if ((doret = doif (cursym, IN_IF, thisreject, depth + 1)) != NO_ERR)
+               return error (doret, stline, depth);
+           break;
+
+       case LT_IF:
+       case LT_OTHER:
+           flushline (YES);
+           if ((doret = doif (-1, IN_IF, reject, depth + 1)) != NO_ERR)
+               return error (doret, stline, depth);
+           break;
+
+       case LT_ELSE:
+           if (inif != IN_IF)
+               return error (ELSE_ERR, linenum, depth);
+           inif = IN_ELSE;
+           if (thissym >= 0) {
+               if (insym[thissym] == SYM_TRUE) {
+                   reject = ignore[thissym] ? REJ_IGNORE : REJ_YES;
+                   insym[thissym] = SYM_FALSE;
+               } else { /* (insym[thissym] == SYM_FALSE) */
+                   reject = prevreject;
+                   insym[thissym] = SYM_TRUE;
+               }
+               if (!ignore[thissym]) {
+                   flushline (NO);
+                   break;
+               }
+           }
+           flushline (YES);
+           break;
+
+       case LT_ENDIF:
+           if (inif == IN_NONE)
+               return error (ENDIF_ERR, linenum, depth);
+           if (thissym >= 0) {
+               insym[thissym] = SYM_INACTIVE;
+               reject = prevreject;
+               if (!ignore[thissym]) {
+                   flushline (NO);
+                   return NO_ERR;
+               }
+           }
+           flushline (YES);
+           return NO_ERR;
+
+       case LT_LEOF: {
+           int err;
+           err =   incomment
+                 ? CEOF_ERR
+                 : inquote == QUOTE_SINGLE
+                 ? Q1EOF_ERR
+                 : inquote == QUOTE_DOUBLE
+                 ? Q2EOF_ERR
+                 : NO_ERR;
+           if (inif != IN_NONE) {
+               if (err != NO_ERR)
+                   (void) error (err, stqcline, depth);
+               return error (IEOF_ERR, stline, depth);
+           } else if (err != NO_ERR)
+               return error (err, stqcline, depth);
+           else
+               return NO_ERR;
+           }
+       }
+    }
+}
+
+#define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_')
+
+#define MAXLINE 256
+char tline[MAXLINE] BSS;
+
+Linetype
+checkline (cursym)
+int *cursym;    /* if LT_TRUE or LT_FALSE returned, set this to sym index */
+{
+    register char *cp;
+    register char *symp;
+    char *scp;
+    Linetype retval;
+#   define KWSIZE 8
+    char keyword[KWSIZE];
+
+    linenum++;
+    if (getlin (tline, sizeof tline, input, NO) == EOF)
+       return LT_LEOF;
+
+    retval = LT_PLAIN;
+    if (   *(cp = tline) != '#'
+       || incomment
+       || inquote == QUOTE_SINGLE
+       || inquote == QUOTE_DOUBLE
+       )
+       goto eol;
+
+    cp = skipcomment (++cp);
+    symp = keyword;
+    while (!endsym (*cp)) {
+       *symp = *cp++;
+       if (++symp >= &keyword[KWSIZE])
+           goto eol;
+    }
+    *symp = '\0';
+
+    if (strcmp (keyword, "ifdef") == 0) {
+       retval = YES;
+       goto ifdef;
+    } else if (strcmp (keyword, "ifndef") == 0) {
+       retval = NO;
+ ifdef:
+       scp = cp = skipcomment (++cp);
+       if (incomment) {
+           retval = LT_PLAIN;
+           goto eol;
+       }
+       {
+           int symind;
+
+           if ((symind = findsym (scp)) >= 0)
+               retval = (retval ^ true[*cursym = symind])
+                        ? LT_FALSE : LT_TRUE;
+           else
+               retval = LT_OTHER;
+       }
+    } else if (strcmp (keyword, "if") == 0)
+       retval = LT_IF;
+    else if (strcmp (keyword, "else") == 0)
+       retval = LT_ELSE;
+    else if (strcmp (keyword, "endif") == 0)
+       retval = LT_ENDIF;
+
+ eol:
+    if (!text && reject != REJ_IGNORE)
+       for (; *cp; ) {
+           if (incomment)
+               cp = skipcomment (cp);
+           else if (inquote == QUOTE_SINGLE)
+               cp = skipquote (cp, QUOTE_SINGLE);
+           else if (inquote == QUOTE_DOUBLE)
+               cp = skipquote (cp, QUOTE_DOUBLE);
+           else if (*cp == '/' && cp[1] == '*')
+               cp = skipcomment (cp);
+           else if (*cp == '\'')
+               cp = skipquote (cp, QUOTE_SINGLE);
+           else if (*cp == '"')
+               cp = skipquote (cp, QUOTE_DOUBLE);
+           else
+               cp++;
+       }
+    return retval;
+}
+
+/*
+ *  Skip over comments and stop at the next charaacter
+ *  position that is not whitespace.
+ */
+char *
+skipcomment (cp)
+register char *cp;
+{
+    if (incomment)
+       goto inside;
+    for (;; cp++) {
+       while (*cp == ' ' || *cp == '\t')
+           cp++;
+       if (text)
+           return cp;
+       if (   cp[0] != '/'
+           || cp[1] != '*'
+          )
+           return cp;
+       cp += 2;
+       if (!incomment) {
+           incomment = YES;
+           stqcline = linenum;
+       }
+ inside:
+       for (;;) {
+           for (; *cp != '*'; cp++)
+               if (*cp == '\0')
+                   return cp;
+           if (*++cp == '/') {
+               incomment = NO;
+               break;
+           }
+       }
+    }
+}
+
+/*
+ *  Skip over a quoted string or character and stop at the next charaacter
+ *  position that is not whitespace.
+ */
+char *
+skipquote (cp, type)
+register char *cp;
+register int type;
+{
+    register char qchar;
+
+    qchar = type == QUOTE_SINGLE ? '\'' : '"';
+
+    if (inquote == type)
+       goto inside;
+    for (;; cp++) {
+       if (*cp != qchar)
+           return cp;
+       cp++;
+       inquote = type;
+       stqcline = linenum;
+ inside:
+       for (; ; cp++) {
+           if (*cp == qchar)
+               break;
+           if (   *cp == '\0'
+               || *cp == '\\' && *++cp == '\0'
+              )
+               return cp;
+       }
+       inquote = QUOTE_NONE;
+    }
+}
+
+/*
+ *  findsym - look for the symbol in the symbol table.
+ *            if found, return symbol table index,
+ *            else return -1.
+ */
+int
+findsym (str)
+char *str;
+{
+    register char *cp;
+    register char *symp;
+    register int symind;
+    register char chr;
+
+    for (symind = 0; symind < nsyms; ++symind) {
+       if (insym[symind] == SYM_INACTIVE) {
+           for ( symp = symname[symind], cp = str
+               ; *symp && *cp == *symp
+               ; cp++, symp++
+               )
+               continue;
+           chr = *cp;
+           if (*symp == '\0' && endsym (chr))
+               return symind;
+       }
+    }
+    return -1;
+}
+
+/*
+ *   getlin - expands tabs if asked for
+ *            and (if compiled in) treats form-feed as an end-of-line
+ */
+int
+getlin (line, maxline, inp, expandtabs)
+register char *line;
+int maxline;
+FILE *inp;
+int expandtabs;
+{
+    int tmp;
+    register int num;
+    register int chr;
+#ifdef  FFSPECIAL
+    static char havechar = NO;  /* have leftover char from last time */
+    static char svchar BSS;
+#endif/*FFSPECIAL */
+
+    num = 0;
+#ifdef  FFSPECIAL
+    if (havechar) {
+       havechar = NO;
+       chr = svchar;
+       goto ent;
+    }
+#endif/*FFSPECIAL */
+    while (num + 8 < maxline) {   /* leave room for tab */
+       chr = getc (inp);
+       if (isprint (chr)) {
+#ifdef  FFSPECIAL
+ ent:
+#endif/*FFSPECIAL */
+           *line++ = chr;
+           num++;
+       } else
+           switch (chr) {
+           case EOF:
+               return EOF;
+
+           case '\t':
+               if (expandtabs) {
+                   num += tmp = 8 - (num & 7);
+                   do
+                       *line++ = ' ';
+                   while (--tmp);
+                   break;
+               }
+           default:
+               *line++ = chr;
+               num++;
+               break;
+
+           case '\n':
+               *line = '\n';
+               num++;
+               goto end;
+
+#ifdef  FFSPECIAL
+           case '\f':
+               if (++num == 1)
+                   *line = '\f';
+               else {
+                   *line = '\n';
+                   havechar = YES;
+                   svchar = chr;
+               }
+               goto end;
+#endif/*FFSPECIAL */
+           }
+    }
+ end:
+    *++line = '\0';
+    return num;
+}
+
+flushline (keep)
+Bool keep;
+{
+    if ((keep && reject != REJ_YES) ^ complement) {
+       register char *line = tline;
+       register FILE *out = stdout;
+       register char chr;
+
+       while (chr = *line++)
+           putc (chr, out);
+    } else if (lnblank)
+       putc ('\n', stdout);
+    return;
+}
+
+prname ()
+{
+    fprintf (stderr, "%s: ", progname);
+    return;
+}
+
+int
+error (err, line, depth)
+int err;        /* type of error & index into error string array */
+int line;       /* line number */
+int depth;      /* how many ifdefs we are inside */
+{
+    if (err == END_ERR)
+       return err;
+
+    prname ();
+
+#ifndef TESTING
+    fprintf (stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]);
+#else/* TESTING */
+    fprintf (stderr, "Error in %s line %d: %s. ", filename, line, errs[err]);
+    fprintf (stderr, "ifdef depth: %d\n", depth);
+#endif/*TESTING */
+
+    exitstat = 2;
+    return depth > 1 ? IEOF_ERR : END_ERR;
+}