home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume3 / scpp / part1 next >
Encoding:
Internet Message Format  |  1986-11-30  |  41.7 KB

  1. From: genrad!masscomp!tektronix!tekig4!bradn
  2. Subject: scpp - a selective C preprocessor (Part 1 of 2)
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 13
  7. Submitted by: decvax!tektronix!tekig4!bradn
  8.  
  9.  
  10. In response to the net.lang.c comments about the misuses of the preprocessor,
  11. I offer this program that interprets selected macros in a file without
  12. disturbing anything else.  I wrote it after trying to read Bourne's
  13. "Algol-like" adb.
  14.  
  15. Scpp is also the most thorough conditional-code remover I've seen -- very
  16. useful for making sense out of heavily "ifdef'ed" code like UUCP.
  17.  
  18. Scpp should run at least under 4.2BSD, SYSIII and SYSV.  Please let me know
  19. if you have any trouble getting it running.
  20.  
  21. Brad Needham
  22. Tektronix, Inc.
  23. ...decvax!tektronix!tekig4!bradn
  24.  
  25. -------------- cut along the dashed line -------
  26. #! /bin/sh
  27. # This is a shell archive, meaning:
  28. # 1. Remove everything above the #! /bin/sh line.
  29. # 2. Save the resulting text in a file.
  30. # 3. Execute the file with /bin/sh (not csh) to create the files:
  31. #    scpp.1
  32. #    Makefile
  33. #    lex.l
  34. #    ctrl.c
  35. #    interp
  36. #    io.c
  37. # This archive created: Thu Sep 19 12:35:36 1985
  38. export PATH; PATH=/bin:$PATH
  39. echo shar: extracting "'scpp.1'" '(2992 characters)'
  40. if test -f 'scpp.1'
  41. then
  42.     echo shar: will not over-write existing file "'scpp.1'"
  43. else
  44. sed 's/^    X//' << \SHAR_EOF > 'scpp.1'
  45.     X.TH SCPP 1 "28 September 1983"
  46.     X.SH NAME
  47.     Xscpp \- selective C preprocessor
  48.     X.SH SYNOPSIS
  49.     X.B scpp
  50.     X[
  51.     X.BI \-M macro
  52.     X] [
  53.     X.BI \-D macro
  54.     X] [
  55.     X.BI \-D macro=def
  56.     X] [
  57.     X.B \-C
  58.     X]
  59.     X.ti +5
  60.     X[
  61.     X.BI \-I incdir
  62.     X] [
  63.     X.I file...
  64.     X]
  65.     X.SH DESCRIPTION
  66.     X.B Scpp
  67.     Xconcatenates the input
  68.     X.I files
  69.     X(or reads standard-in, if no
  70.     X.I file
  71.     Xis given),
  72.     Xinterprets all references to given macros,
  73.     Xleaving the rest of the
  74.     X.IR file "(s)"
  75.     Xunaltered,
  76.     Xthen writes the result to standard-out.
  77.     XIt is helpful in removing conditionally compiled code or misleading
  78.     Xmacros from a file.
  79.     X.PP
  80.     XThe
  81.     X.I file
  82.     Xname "\fB-\fP" refers to standard-in.
  83.     X.PP
  84.     XThe following options are available.
  85.     XEach option can appear as frequently as desired.
  86.     X.RS
  87.     X.TP
  88.     X.SM \-M
  89.     XInterpret all references to the given
  90.     X.I macro.
  91.     X.I Macro
  92.     Xcan be either a single macro name or a whitespace-separated list of
  93.     Xmacro names (e.g. -MMAXINT or -M"MAXINT MININT INTWID").
  94.     XAll occurrences of the macro and all instances of the intrinsic macro
  95.     X\&"defined()" referring to this macro are expanded.
  96.     XAll preprocessor directives referring to this macro (except
  97.     X.BR #if )
  98.     Xperform their usual function and do not appear in the output.
  99.     X.B #if
  100.     Xdirectives are interpreted only if their value is not dependent on macros
  101.     Xwhich are not to be interpreted.
  102.     X.TP
  103.     X.SM \-D
  104.     XDefine the
  105.     X.I macro
  106.     Xto have the value
  107.     X.I def,
  108.     Xor "1" if no
  109.     X.I def
  110.     Xis given.
  111.     XUnlike the C preprocessor,
  112.     X.B scpp
  113.     Xdoes not implicitly define certain macros that describe the environment in
  114.     Xwhich the code will be running (e.g. "vax" or "unix").
  115.     X.B \-D
  116.     Ximplies
  117.     X.B \-M.
  118.     X.TP
  119.     X.SM \-C
  120.     XPreserve comments and whitespace in interpreted macro definitions.
  121.     XNormally, comments and leading and trailing whitespace are stripped from
  122.     Xinterpreted macro definitions.
  123.     X.TP
  124.     X.SM \-I
  125.     XAdd
  126.     X.I incdir
  127.     Xto the list of directories to be searched for include files.
  128.     X.B Scpp
  129.     Xsearches directories in the same order as the C preprocessor:
  130.     Xif the include filename is enclosed in double-quotes ("...")
  131.     Xrather than angle-brackets (<...>),
  132.     Xthe directory containing the current file being processed;
  133.     Xthe directories given by -I, left-to-right;
  134.     Xthe standard directory, /usr/include.
  135.     X.RE
  136.     X.SH AUTHOR
  137.     XBrad Needham, Tektronix, Inc.
  138.     X.SH "SEE ALSO"
  139.     Xcc(1).
  140.     X.SH BUGS
  141.     XVery long identifiers (those over 100 characters long) will crash
  142.     X.B scpp.
  143.     X.PP
  144.     XBecause
  145.     X.B scpp
  146.     Xinterprets only the given macros, the meaning of some code will change.
  147.     XE.g. "scpp -MBOO" of
  148.     X.br
  149.     X    #define BOO hello,there
  150.     X.br
  151.     X    #define twopar(a,b) a b
  152.     X.br
  153.     X    twopar(BOO,folks)
  154.     X.br
  155.     Xwill generate
  156.     X.br
  157.     X    #define twopar(a,b) a b
  158.     X.br
  159.     X    twopar(hello,there,folks)
  160.     X.br
  161.     Xcausing an argument mismatch when the output is compiled.
  162.     X.PP
  163.     XBecause uninterpreted "#if"s, "ifdef"s, and "ifndef"s, have no effect
  164.     Xon the output, the following example, when processed via "scpp -MLEFT",
  165.     Xwill generate an error message complaining about
  166.     Xmultiple definitions of "LEFT".
  167.     X.br
  168.     X    #ifdef ZULU
  169.     X.br
  170.     X    #define LEFT 20
  171.     X.br
  172.     X    #else
  173.     X.br
  174.     X    #define LEFT 347
  175.     X.br
  176.     X    #endif
  177.     X.PP
  178.     XThe C preprocessor macros "\fB__FILE__\fP" and "\fB__LINE__\fP" have no
  179.     Xspecial meaning to
  180.     X.B scpp.
  181. SHAR_EOF
  182. if test 2992 -ne "`wc -c < 'scpp.1'`"
  183. then
  184.     echo shar: error transmitting "'scpp.1'" '(should have been 2992 characters)'
  185. fi
  186. fi # end of overwriting check
  187. echo shar: extracting "'Makefile'" '(1086 characters)'
  188. if test -f 'Makefile'
  189. then
  190.     echo shar: will not over-write existing file "'Makefile'"
  191. else
  192. sed 's/^    X//' << \SHAR_EOF > 'Makefile'
  193.     X# Makefile for the selective C preprocessor, scpp.
  194.     X#
  195.     X# Copyright (c) 1985 by
  196.     X# Tektronix, Incorporated Beaverton, Oregon 97077
  197.     X# All rights reserved.
  198.     X#
  199.     X# Permission is hereby granted for personal, non-commercial
  200.     X# reproduction and use of this program, provided that this
  201.     X# notice and all copyright notices are included in any copy.
  202.     X
  203.     XDEFS=
  204.     XCFLAGS= -O $(DEFS)
  205.     XHDRS= scpp.h
  206.     X
  207.     XSOURCES= ctrl.c io.c lex.l parse.y scpp.c
  208.     XOBJECTS= ctrl.o io.o lex.o parse.o scpp.o
  209.     X
  210.     Xall:    scpp scpp.cat
  211.     Xscpp:    $(OBJECTS)
  212.     X    $(CC) $(CFLAGS) -o scpp $(OBJECTS) -ll
  213.     Xscpp.cat: scpp.1
  214.     X    nroff -man scpp.1 >scpp.cat
  215.     X
  216.     Xscpp.o: scpp.c y.tab.h scpp.h
  217.     Xctrl.o: ctrl.c y.tab.h scpp.h
  218.     Xio.o:  io.c scpp.h
  219.     Xlex.o: lex.c y.tab.h scpp.h
  220.     Xparse.o: parse.c scpp.h
  221.     X
  222.     Xlex.c: lex.l
  223.     X    lex lex.l
  224.     X    sed -e '/yylex/s//xxlex/g' <lex.yy.c >lex.c
  225.     X    rm lex.yy.c
  226.     Xy.tab.h parse.c: parse.y
  227.     X    yacc -d parse.y
  228.     X    mv y.tab.c parse.c
  229.     X
  230.     Xclean:
  231.     X    -rm -f lex.yy.c lex.c y.tab.c y.tab.h y.output parse.c
  232.     X    -rm -f $(OBJECTS)
  233.     X
  234.     Xtags: $(SOURCES)
  235.     X    ctags $(SOURCES)
  236.     Xmail:
  237.     X    shar -a scpp.1 Makefile lex.l ctrl.c interp io.c >scpp.shar1
  238.     X    shar -a parse.y scpp.c scpp.h >scpp.shar2
  239. SHAR_EOF
  240. if test 1086 -ne "`wc -c < 'Makefile'`"
  241. then
  242.     echo shar: error transmitting "'Makefile'" '(should have been 1086 characters)'
  243. fi
  244. fi # end of overwriting check
  245. echo shar: extracting "'lex.l'" '(2334 characters)'
  246. if test -f 'lex.l'
  247. then
  248.     echo shar: will not over-write existing file "'lex.l'"
  249. else
  250. sed 's/^    X//' << \SHAR_EOF > 'lex.l'
  251.     X%{
  252.     X/*
  253.     X * scpp - selective C preprocessor
  254.     X *  Lexical scanner
  255.     X *
  256.     X * Copyright (c) 1985 by
  257.     X * Tektronix, Incorporated Beaverton, Oregon 97077
  258.     X * All rights reserved.
  259.     X *
  260.     X * Permission is hereby granted for personal, non-commercial
  261.     X * reproduction and use of this program, provided that this
  262.     X * notice and all copyright notices are included in any copy.
  263.     X */
  264.     X
  265.     X# include <stdio.h>
  266.     X
  267.     X# undef input
  268.     X# undef unput
  269.     X# define input() (*nxtin == ATTN ? nxtc() : *nxtin++)
  270.     X# define unput(c) unc(c)
  271.     X
  272.     X# include "scpp.h"
  273.     X# include "y.tab.h"
  274.     X
  275.     Xint lasttok = NL;    /* used to detect ^# when lex can't    */
  276.     X# define yield(t) lasttok = t; questr(yytext, yyleng); return(t)
  277.     X
  278.     X/*
  279.     X * All input to higher levels of scpp is provided exclusively by this
  280.     X *  lexical analyzer, xxlex().
  281.     X * This routine is called xxlex() rather than yylex() because the "#if"
  282.     X *  expression parser uses a slightly different lexical analyzer (which
  283.     X *  calls xxlex()).
  284.     X */
  285.     X
  286.     X%}
  287.     X%%
  288.     X
  289.     X"<"    {yield(LT);}
  290.     X"<="    {yield(LE);}
  291.     X">"    {yield(GT);}
  292.     X">="    {yield(GE);}
  293.     X","    {yield(CM);}
  294.     X"/"    {yield(DIV);}
  295.     X"%"    {yield(MOD);}
  296.     X"+"    {yield(PLUS);}
  297.     X"-"    {yield(MINUS);}
  298.     X"<<"    {yield(LS);}
  299.     X">>"    {yield(RS);}
  300.     X"*"    {yield(MUL);}
  301.     X"=="    {yield(EQ);}
  302.     X"!="    {yield(NE);}
  303.     X"&"    {yield(AND);}
  304.     X"|"    {yield(OR);}
  305.     X"^"    {yield(ER);}
  306.     X"&&"    {yield(ANDAND);}
  307.     X"||"    {yield(OROR);}
  308.     X"?"    {yield(QUEST);}
  309.     X":"    {yield(COLON);}
  310.     X"!"    {yield(NOT);}
  311.     X"~"    {yield(COMPL);}
  312.     X"("    {yield(LP);}
  313.     X")"    {yield(RP);}
  314.     X","    {yield(CM);}
  315.     X[ \t]+    {yield(WHITE);    /* whitespace */}
  316.     X\\\n    {/* escaped newline */
  317.     X        if (curfile->af_raw) {
  318.     X            curfile->af_line++;
  319.     X        }
  320.     X        yield(QNL);
  321.     X    }
  322.     X\n    {/* unescaped newline */
  323.     X        if (curfile->af_raw) {
  324.     X            curfile->af_line++;
  325.     X        }
  326.     X        yield(NL);
  327.     X    }
  328.     X0x[0-9a-fA-F]+[Ll]?    {yield(INT); /* hex constant */}
  329.     X[0-9]+[Ll]?        {yield(INT); /* decimal or octal constant */}
  330.     X[0-9]+[Ee]([+-][0-9])?[0-9]*        |
  331.     X\.[0-9]+([Ee]([+-][0-9])?[0-9]*)?    |
  332.     X[0-9]+\.[0-9]*([Ee]([+-][0-9])?[0-9]*)?    {yield(FLOAT); /* floating constant */}
  333.     X[a-zA-Z_][a-zA-Z0-9_]*    {yield(IDENT); /* identifier */}
  334.     X\'    {yield(QUOTE);}
  335.     X\"    {yield(DQUOTE);}
  336.     X\\    {yield(BACKS);}
  337.     X"/*"    {yield(OPENC); /* start (open) comment */}
  338.     X"*/"    {yield(CLOSEC);/* finish (close) comment */}
  339.     X#    {/*
  340.     X      * a control line if preceeded immediately by a newline,
  341.     X      *  even if that newline was the result of macro interpretation.
  342.     X      */
  343.     X        if (lasttok == NL) {
  344.     X            yield(POUNDLINE);
  345.     X        }
  346.     X        yield(OTHER);
  347.     X    }
  348.     X.    {yield(OTHER);}
  349.     X
  350.     X%%
  351. SHAR_EOF
  352. if test 2334 -ne "`wc -c < 'lex.l'`"
  353. then
  354.     echo shar: error transmitting "'lex.l'" '(should have been 2334 characters)'
  355. fi
  356. fi # end of overwriting check
  357. echo shar: extracting "'ctrl.c'" '(19059 characters)'
  358. if test -f 'ctrl.c'
  359. then
  360.     echo shar: will not over-write existing file "'ctrl.c'"
  361. else
  362. sed 's/^    X//' << \SHAR_EOF > 'ctrl.c'
  363.     X/*
  364.     X * ctrl - interpretation of preprocessor control lines (e.g. #define...)
  365.     X *  for the selective C preprocessor, scpp.
  366.     X *
  367.     X * Copyright (c) 1985 by
  368.     X * Tektronix, Incorporated Beaverton, Oregon 97077
  369.     X * All rights reserved.
  370.     X *
  371.     X * Permission is hereby granted for personal, non-commercial
  372.     X * reproduction and use of this program, provided that this
  373.     X * notice and all copyright notices are included in any copy.
  374.     X */
  375.     X
  376.     X# include <stdio.h>
  377.     X# include "scpp.h"
  378.     X# include "y.tab.h"
  379.     X
  380.     X/*
  381.     X * valbuf[] the buffer in which to build the value of a macro which has
  382.     X *  parameters.
  383.     X */
  384.     X
  385.     X# define VALLEN 1000        /* max # of chars in a macro value string  */
  386.     Xchar valbuf[VALLEN];
  387.     Xchar *valend;            /*
  388.     X                 * always points to the null-terminator
  389.     X                 * of the value while the value is being built
  390.     X                 */
  391.     X
  392.     X/*
  393.     X * form[] the array of formal parameters for a macro.
  394.     X *  Each formal argument of a macro acts as a "local variable" during the
  395.     X *  scan for the value of the macro.  The form[] array contains
  396.     X *  pointers to each argument's slot in the symbol table (used to recognize
  397.     X *  the formal argument in the value) and the previous value of that slot
  398.     X *  (so that the symbol table can be restored to normal after the value
  399.     X *  of the macro has been scanned.
  400.     X */
  401.     X
  402.     Xstruct aformal {
  403.     X    struct amacro *fm_sym;    /* symbol table slot for this formal    */
  404.     X    struct amacro  fm_copy;    /* copy of the old contents of that slot */
  405.     X};
  406.     X# define FORMSIZ 40    /* max number of parameters to a macro    */
  407.     Xstruct aformal form[FORMSIZ];
  408.     Xstruct aformal *formtop; /* points to the next empty slot in form[]    */
  409.     X
  410.     X/*
  411.     X * do_xxx() - functions for processing preprocessor control lines.
  412.     X */
  413.     X
  414.     Xint do_line();
  415.     Xint do_include();
  416.     Xint do_define();
  417.     Xint do_undef();
  418.     Xint do_ifdef();
  419.     Xint do_ifndef();
  420.     Xint do_if();
  421.     Xint do_else();
  422.     Xint do_endif();
  423.     X
  424.     X/*
  425.     X * key - the array of preprocessor keywords.
  426.     X */
  427.     X
  428.     Xstruct akeyword key[] = {
  429.     X    {"line", do_line, 0},
  430.     X    {"include", do_include, 0},
  431.     X    {"define", do_define, 0},
  432.     X    {"undef", do_undef, 0},
  433.     X    {"ifdef", do_ifdef, 0},
  434.     X    {"ifndef", do_ifndef, 0},
  435.     X    {"if", do_if, 0},
  436.     X    {"else", do_else, 0},
  437.     X    {"endif", do_endif, 0},
  438.     X    {0,0,0}            /* a zero ak_name marks the end of the list */
  439.     X};
  440.     X
  441.     X/*
  442.     X * ikeywords() - initialize the preprocessor keywords.
  443.     X *  For each keyword, set its ak_sym field and act as if it has been -M'ed.
  444.     X */
  445.     X
  446.     Xikeywords()
  447.     X{
  448.     X    struct akeyword *kp;
  449.     X
  450.     X
  451.     X    for (kp = &key[0]; kp->ak_name; ++kp) {
  452.     X        kp->ak_sym = findmac(kp->ak_name,
  453.     X            kp->ak_name + strlen(kp->ak_name));
  454.     X        if (kp->ak_sym->am_name) {
  455.     X            bomb("INTERNAL: identical keywords in key[]");
  456.     X        }
  457.     X
  458.     X        kp->ak_sym->am_name = kp->ak_name;
  459.     X        kp->ak_sym->am_npar = -1;
  460.     X        /* leave am_val as 0 */
  461.     X    }
  462.     X}
  463.     X
  464.     X/*
  465.     X * findkey() - find the keyword corresponding to the given symbol table entry,
  466.     X *  returning a pointer to that keyword in key[],
  467.     X *  or zero if no match.
  468.     X */
  469.     X
  470.     Xstruct akeyword *
  471.     Xfindkey(mac)
  472.     Xstruct amacro *mac;
  473.     X{
  474.     X    struct akeyword *kp;
  475.     X
  476.     X    for (kp = &key[0]; kp->ak_name && kp->ak_sym != mac; ++kp)
  477.     X        ;
  478.     X    if (!kp->ak_name) {
  479.     X        return((struct akeyword *) 0);
  480.     X    }
  481.     X    return(kp);
  482.     X}
  483.     X
  484.     X/*
  485.     X * doctrl() - process a control line (a line beginning with '#').
  486.     X */
  487.     X
  488.     Xint        /* returned token (NL or 0)    */
  489.     Xdoctrl(f)        /* process control lines */
  490.     Xchar *f;        /* first char of this line (the '#' token) in pend[] */
  491.     X{
  492.     X    int tok;        /* the current token    */
  493.     X    struct amacro *cmd;    /* the preprocessor command (symbol table)  */
  494.     X    struct akeyword *kp;    /* the preprocessor command (keyword table) */
  495.     X
  496.     X
  497.     X    /*
  498.     X     * skip initial whitespace and comments (if any);
  499.     X     * ignore empty command lines;
  500.     X     * print warnings for garbled command lines;
  501.     X     * switch on the command name.
  502.     X     */
  503.     X
  504.     X    if ((tok = nonwhite(gintok)) == 0 || tok == NL) {
  505.     X        return(tok);
  506.     X    }
  507.     X
  508.     X    if (tok != IDENT) {
  509.     X        warnf("undefined control");
  510.     X        tok = endline();
  511.     X        return(tok);
  512.     X    }
  513.     X
  514.     X    cmd = findmac(curtext, nxtout);
  515.     X    if (!cmd->am_name || !(kp = findkey(cmd))) {
  516.     X        /* name is not a preprocessor command */
  517.     X
  518.     X        warnf("undefined control");
  519.     X        tok = endline();
  520.     X        return(tok);
  521.     X    }
  522.     X
  523.     X    /*
  524.     X     * invoke the appropriate handler
  525.     X     */
  526.     X
  527.     X    tok = (*kp->ak_proc)(f);
  528.     X    return(tok);
  529.     X}
  530.     X
  531.     X/*
  532.     X * do_line - parse a #line command.
  533.     X *   #line syntax:
  534.     X *     #[<whitespace>]line[<whitespace>][<int>[<whitespace>]<string>
  535.     X *     where: <int> is the new integer line number for this line,
  536.     X *          <string> is the new double-quote enclosed filename.
  537.     X */
  538.     X
  539.     Xint
  540.     Xdo_line(f)
  541.     Xchar *f;
  542.     X{
  543.     X    int tok;
  544.     X    char *name;        /* the new filename        */
  545.     X    char *src;
  546.     X    char *dst;
  547.     X
  548.     X
  549.     X    if ((tok = nonwhite(gintok)) == 0 || tok == NL) {
  550.     X        return(tok);
  551.     X    }
  552.     X
  553.     X    if (tok == INT) {
  554.     X        if (curfile >= &filestk[0]) {
  555.     X            curfile->af_line = inttok(curtext, nxtout);
  556.     X        }
  557.     X
  558.     X        if ((tok = nonwhite(gintok)) == 0 || tok == NL) {
  559.     X            return(tok);
  560.     X        }
  561.     X    }
  562.     X    if (tok != STRING) {
  563.     X        tok = endline();
  564.     X        return(tok);
  565.     X    }
  566.     X
  567.     X    name = savtok(curtext, nxtout);
  568.     X    for (dst = name, src = name + 1; (*dst = *src) != '\0';
  569.     X      ++dst, ++src)
  570.     X        ;
  571.     X    if (--dst <= name || *dst != '"') {
  572.     X        free(name);
  573.     X        tok = endline();
  574.     X        return(tok);
  575.     X    }
  576.     X    *dst = '\0';
  577.     X
  578.     X    if (curfile >= &filestk[0]) {
  579.     X        free(curfile->af_name);
  580.     X        curfile->af_name = name;
  581.     X    }
  582.     X
  583.     X    tok = endline();
  584.     X    return(tok);
  585.     X}
  586.     X
  587.     Xint
  588.     Xdo_include(f)
  589.     Xchar *f;    /* (unused because #include lines are never deleted)    */
  590.     X{
  591.     X    char *ifile;        /* the (dynamically alloc'ed) filename    */
  592.     X    int looktype;        /* type of directory search to perform    */
  593.     X    int tok;        /* the current token's type        */
  594.     X    char *src, *dst;
  595.     X
  596.     X
  597.     X    /*
  598.     X     * pickup the filename, scan the rest of the command line,
  599.     X     * then include the file.
  600.     X     */
  601.     X
  602.     X    ifile = (char *) 0;
  603.     X    if ((tok = nonwhite(gintok)) == 0) {
  604.     X        goto badinc;
  605.     X    }
  606.     X    if (tok == STRING) {
  607.     X        /*
  608.     X         * the filename is enclosed in double-quotes.
  609.     X         * Set the search type to include the current file's directory;
  610.     X         * Save the filename, then remove the string delimiters from it.
  611.     X         */
  612.     X
  613.     X        looktype = PF_DOT;
  614.     X
  615.     X        ifile = savtok(curtext, nxtout);
  616.     X
  617.     X        for (dst = ifile, src = ifile + 1; (*dst = *src) != '\0';
  618.     X            ++dst, ++src)
  619.     X            ;
  620.     X        if (--dst <= ifile || *dst != '"') {
  621.     X            goto badinc;
  622.     X        }
  623.     X        *dst = '\0';
  624.     X    } else if (tok == LT) {
  625.     X        /*
  626.     X         * The filename is enclosed in angle-brackets.
  627.     X         * Set the directory search to exclude the current file's
  628.     X         *  directory; collect and save the filename.
  629.     X         */
  630.     X
  631.     X        looktype = PF_NODOT;
  632.     X
  633.     X        src = nxtout;
  634.     X        while ((tok = gtok()) != GT && tok != NL && tok != 0)
  635.     X            ;
  636.     X        if (tok != GT) {
  637.     X            goto badinc;
  638.     X        }
  639.     X        ifile = savtok(src, curtext);
  640.     X    } else {
  641.     Xbadinc:
  642.     X        bombf("bad include syntax");
  643.     X        if (ifile) {
  644.     X            free(ifile);
  645.     X        }
  646.     X        tok = endline();
  647.     X        return(tok);
  648.     X    }
  649.     X
  650.     X    tok = endline();
  651.     X    pushfile(ifile, looktype, PF_HIDE);
  652.     X    free(ifile);
  653.     X    return(tok);
  654.     X}
  655.     X
  656.     Xint
  657.     Xdo_define(f)
  658.     Xchar *f;
  659.     X{
  660.     X    int tok;
  661.     X    struct amacro *mac;    /* the macro being defined        */
  662.     X    struct amacro *arg;    /*
  663.     X                 * points to the slot for the current argument
  664.     X                 * from the definition of a macro with
  665.     X                 * parameters.
  666.     X                 */
  667.     X    struct amacro maccopy;    /*
  668.     X                 * a copy of some of the info from the slot
  669.     X                 *  for the macro being defined.  Copying the
  670.     X                 *  slot allows perverse macro definitions
  671.     X                 *  such as:
  672.     X                 *    # define boo(boo)  boo()
  673.     X                 */
  674.     X    struct aformal *formp;    /* a temp pointer            */
  675.     X    int defok = TRUE;    /*
  676.     X                 * 'ok to define the macro.' Used to prevent
  677.     X                 * definition of a macro with parameters if
  678.     X                 * there was a syntax error in the definition.
  679.     X                 */
  680.     X
  681.     X
  682.     X    /*
  683.     X     * scan for the macro name with identifier expansion turned off.
  684.     X     * Find the slot corresponding to the macro name.
  685.     X     */
  686.     X
  687.     X    if ((tok = nonwhite(gtok)) == 0) {
  688.     X        return(0);
  689.     X    }
  690.     X    if (tok != IDENT) {
  691.     X        warnf("illegal macro name");
  692.     X        tok = endline();
  693.     X        return(tok);
  694.     X    }
  695.     X    mac = findmac(curtext, nxtout);
  696.     X
  697.     X    if ((tok = gtok()) != LP) {
  698.     X        /*
  699.     X         * a simple macro.  If it hasn't been -M'ed, ignore it.
  700.     X         * Otherwise, save the replacement text (disposing of
  701.     X         *  any quoted newlines, comments, and appropriate
  702.     X         *  whitespace), define the macro, and dispose of this line.
  703.     X         */
  704.     X
  705.     X        char *valstrt;    /* points to the macro value within pend[] */
  706.     X        char *valstr;    /* the dynamically alloc'ed value string */
  707.     X
  708.     X        if (!mac->am_name) {
  709.     X            tok = endline();
  710.     X            return(tok);
  711.     X        }
  712.     X
  713.     X        valstrt = curtext;
  714.     X
  715.     X        /*
  716.     X         * if the delimiter is whitespace, skip the first character
  717.     X         * of the whitespace (and any preceeding ATTN bytes).
  718.     X         */
  719.     X
  720.     X        if (tok == WHITE) {
  721.     X            while (valstrt < nxtout && *valstrt == ATTN) {
  722.     X                valstrt += 2;
  723.     X            }
  724.     X            if (valstrt < nxtout) {
  725.     X                valstrt++;
  726.     X            }
  727.     X        }
  728.     X        while (tok != NL && tok != 0) {
  729.     X            if (tok == QNL || (!savcom && tok == COMMENT)) {
  730.     X                (void) dispose(curtext);
  731.     X            }
  732.     X            tok = gtok();
  733.     X        }
  734.     X        if (tok == 0) {
  735.     X            warnf("unterminated preprocessor command");
  736.     X        } else {
  737.     X            valstr = savtok(valstrt, curtext);
  738.     X            if (!savcom) {
  739.     X                stripwhite(valstr);
  740.     X            }
  741.     X            defmac(mac->am_name,
  742.     X              mac->am_name + strlen(mac->am_name),
  743.     X              -1 /* npar */, valstr);
  744.     X            free(valstr);
  745.     X        }
  746.     X        (void) dispose(f);
  747.     X        return(tok);
  748.     X    }
  749.     X
  750.     X    /* a macro with parameters.  Copy relevant parts of it. */
  751.     X
  752.     X    maccopy.am_name = mac->am_name;
  753.     X
  754.     X    /*
  755.     X     * Collect the comma-separated formals of the macro.
  756.     X     * Temporarily define each formal of the macro, saving the old
  757.     X     * contents of that formal's slot in the symbol table,
  758.     X     * marking that symbol as -M'ed, but undefined (so that
  759.     X     * scanning an uninterpreted macro definition works in the
  760.     X     * following case:
  761.     X     *    scpp -MMOO
  762.     X     *        #define MOO lose
  763.     X     *        #define goo(MOO) is a MOO.
  764.     X     *    should generate
  765.     X     *        #define goo(MOO) is a MOO.
  766.     X     *    rather than
  767.     X     *        #define goo(MOO) is a lose.
  768.     X     */
  769.     X
  770.     X    formtop = &form[0];
  771.     X    while (formtop < &form[FORMSIZ]) {
  772.     X        if ((tok = nonwhite(gtok)) != IDENT) {
  773.     X            break;
  774.     X        }
  775.     X
  776.     X        /* process the formal argument */
  777.     X
  778.     X        formtop->fm_sym = findmac(curtext, nxtout);
  779.     X        for (formp = &form[0]; formp < formtop &&
  780.     X          formp->fm_sym != formtop->fm_sym; ++formp)
  781.     X            ;
  782.     X        if (formp < formtop) {
  783.     X            warnf("duplicate formal names in macro definition");
  784.     X        }
  785.     X
  786.     X        formtop->fm_copy.am_name = formtop->fm_sym->am_name;
  787.     X        formtop->fm_copy.am_npar = formtop->fm_sym->am_npar;
  788.     X        formtop->fm_copy.am_val = formtop->fm_sym->am_val;
  789.     X
  790.     X        formtop->fm_sym->am_name = savtok(curtext, nxtout);
  791.     X        formtop->fm_sym->am_npar = -1;
  792.     X        formtop->fm_sym->am_val = (char *) 0;
  793.     X
  794.     X        ++formtop;
  795.     X
  796.     X        if ((tok = nonwhite(gtok)) != CM) {
  797.     X            break;
  798.     X        }
  799.     X    }
  800.     X    if (tok != RP) {
  801.     X        if (formtop >= &form[FORMSIZ]) {
  802.     X            warnf("too many formal arguments");
  803.     X        } else {
  804.     X            warnf("syntax error in formal arguments");
  805.     X        }
  806.     X        tok = endline();
  807.     X        defok = FALSE;
  808.     X        goto rollback;
  809.     X    }
  810.     X
  811.     X    if (!maccopy.am_name) {
  812.     X
  813.     X        /*
  814.     X         * This macro is not -M'ed, so don't interpret this #define.
  815.     X         * scan to the end of the line.
  816.     X         */
  817.     X
  818.     X        tok = endline();
  819.     X        defok = FALSE;
  820.     X    } else {
  821.     X
  822.     X        /*
  823.     X         * This macro is -M'ed.  Record the number of parameters,
  824.     X         * then save the value of this macro,
  825.     X         * marking occurrences of the formal arguments.
  826.     X         */
  827.     X
  828.     X        maccopy.am_npar = formtop - &form[0];
  829.     X
  830.     X        valend = &valbuf[0];
  831.     X        *valend = '\0';
  832.     X
  833.     X        while ((tok = gtok()) != 0 && tok != NL) {
  834.     X            if (tok == QNL || (!savcom && tok == COMMENT)) {
  835.     X                /* ignore the token */
  836.     X            } else {
  837.     X                /*
  838.     X                 * if this token is a formal parameter name,
  839.     X                 * add its parameter number & an ATTN byte
  840.     X                 * to the macro value.  Otherwise add the
  841.     X                 * token's value (less ATTN bytes) to the value.
  842.     X                 */
  843.     X    
  844.     X                formp = formtop;
  845.     X                if (tok == IDENT) {
  846.     X                    arg = findmac(curtext, nxtout);
  847.     X                    for (formp = &form[0]; formp < formtop &&
  848.     X                      formp->fm_sym != arg; ++formp)
  849.     X                        ;
  850.     X                }
  851.     X
  852.     X                if (formp < formtop) {
  853.     X                    if (valend + 2 >= &valbuf[VALLEN]) {
  854.     X                    bombf("macro value too long");
  855.     X                    }
  856.     X                    *valend++ = (char) ((formp - &form[0]) + 1);
  857.     X                    *valend++ = ATTN;
  858.     X                    *valend = '\0';
  859.     X                } else {
  860.     X                    if (valend + (nxtout - curtext) >=
  861.     X                      &valbuf[VALLEN]) {
  862.     X                    bombf("macro value too long");
  863.     X                    }
  864.     X                    while (curtext < nxtout) {
  865.     X                    if (*curtext == ATTN) {
  866.     X                        curtext += 2;
  867.     X                    } else {
  868.     X                        *valend++ = *curtext++;
  869.     X                    }
  870.     X                    }
  871.     X                    *valend = '\0';
  872.     X                }
  873.     X            }
  874.     X        }
  875.     X        (void) dispose(f);
  876.     X    }
  877.     X
  878.     Xrollback:
  879.     X
  880.     X    /*
  881.     X     * restore the formal parameter's original values
  882.     X     * (in reverse order to take care of duplicate formal parameters).
  883.     X     */
  884.     X
  885.     X    while (--formtop >= &form[0]) {
  886.     X        free(formtop->fm_sym->am_name);
  887.     X        formtop->fm_sym->am_name = formtop->fm_copy.am_name;
  888.     X        formtop->fm_sym->am_npar = formtop->fm_copy.am_npar;
  889.     X        formtop->fm_sym->am_val = formtop->fm_copy.am_val;
  890.     X    }
  891.     X
  892.     X    /*
  893.     X     * (finally) define the macro (if there was no problem),
  894.     X     * stripping whitespace where appropriate.
  895.     X     */
  896.     X
  897.     X    if (defok) {
  898.     X        if (!savcom) {
  899.     X            stripwhite(&valbuf[0]);
  900.     X        }
  901.     X        defmac(maccopy.am_name,
  902.     X          maccopy.am_name + strlen(maccopy.am_name),
  903.     X          maccopy.am_npar, &valbuf[0]);
  904.     X    }
  905.     X    return(tok);
  906.     X}
  907.     X
  908.     Xint
  909.     Xdo_undef(f)
  910.     Xchar *f;
  911.     X{
  912.     X    int tok;        /* the current token's type    */
  913.     X    struct amacro *mac;    /* the macro to be undefined    */
  914.     X    char *cp;
  915.     X
  916.     X
  917.     X
  918.     X    /*
  919.     X     * find the macro to be undefined (it is legal to undef an undefined
  920.     X     *  macro, a non "-M"ed macro, or a preprocessor keyword);
  921.     X     * Read the rest of the "#undef" line;
  922.     X     * If this macro is one of the magic preprocessor macros
  923.     X     * (e.g. "defined()"), it cannot be undef'ed.
  924.     X     * Otherwise, find the beginning of the value and free it,
  925.     X     *  then zero the value, undefining the macro.
  926.     X     * Destroy the original text of the #undef.
  927.     X     */
  928.     X
  929.     X    if ((tok = nonwhite(gtok)) != IDENT) {
  930.     X        warnf("illegal macro name");
  931.     X        tok = endline();
  932.     X        return(tok);
  933.     X    }
  934.     X    mac = findmac(curtext, nxtout);
  935.     X    if (!mac->am_name) {
  936.     X        tok = endline();
  937.     X        return(tok);
  938.     X    }
  939.     X    if (mac->am_val) {
  940.     X        if (mac->am_val == &magicval) {
  941.     X            warnf("cannot undef implicit macro");
  942.     X            tok = endline();
  943.     X            return(tok);
  944.     X        }
  945.     X        cp = mac->am_val;
  946.     X        while (*--cp != '\0')
  947.     X            ;
  948.     X        free(cp);
  949.     X        mac->am_val = (char *) 0;
  950.     X    }
  951.     X    tok = endline();
  952.     X    (void) dispose(f);
  953.     X    return(tok);
  954.     X}
  955.     X
  956.     Xint
  957.     Xdo_ifdef(f)
  958.     Xchar *f;
  959.     X{
  960.     X    return(ifdorn(f,TRUE));
  961.     X}
  962.     X
  963.     Xint
  964.     Xdo_ifndef(f)
  965.     Xchar *f;
  966.     X{
  967.     X    return(ifdorn(f, FALSE));
  968.     X}
  969.     X
  970.     X/*
  971.     X * ifdorn() - "if defined or not defined" -- this is the common code for
  972.     X *  ifdef and ifndef processing.
  973.     X */
  974.     X
  975.     Xint
  976.     Xifdorn(f, defed)
  977.     Xchar *f;        /* points to the beginning of the command in pend[] */
  978.     Xint defed;        /* "the if is true if the macro is defined"    */
  979.     X{
  980.     X    int tok;        /* the current token's type    */
  981.     X    struct amacro *mac;    /* the macro in question    */
  982.     X
  983.     X
  984.     X    if (++curif >= &ifstk[IFSIZ]) {
  985.     X        bombf("too many nested if's");
  986.     X    }
  987.     X    *curif = IF_INIF;
  988.     X
  989.     X    tok = nonwhite(gtok);
  990.     X    if (tok != IDENT) {
  991.     X        warnf("illegal macro name");
  992.     X        tok = endline();
  993.     X        return(tok);
  994.     X    }
  995.     X    mac = findmac(curtext, nxtout);
  996.     X    tok = endline();
  997.     X
  998.     X    if (!mac->am_name) {
  999.     X        return(tok);
  1000.     X    }
  1001.     X
  1002.     X    if ((mac->am_val && defed) || (!mac->am_val && !defed)) {
  1003.     X        *curif |= IF_TRUE;
  1004.     X    } else {
  1005.     X        *curif |= IF_FALSE;
  1006.     X        ift_f();
  1007.     X    }
  1008.     X
  1009.     X    (void) dispose(f);
  1010.     X    return(tok);
  1011.     X}
  1012.     X
  1013.     Xint
  1014.     Xdo_if(f)
  1015.     Xchar *f;
  1016.     X{
  1017.     X    int tok;
  1018.     X    int oldnint;        /* interp' count prior to parsing the exp */
  1019.     X    int wasraw;        /* the state of interpretation prior to parse */
  1020.     X
  1021.     X
  1022.     X    if (++curif >= &ifstk[IFSIZ]) {
  1023.     X        bombf("too many nested if's");
  1024.     X    }
  1025.     X    *curif = IF_INIF;
  1026.     X
  1027.     X    wasraw = curfile->af_raw;
  1028.     X    oldnint = ninterp;
  1029.     X
  1030.     X    expparse = TRUE;
  1031.     X    if (yyparse() != 0) {
  1032.     X        /* syntax error - don't interpret this 'if'    */
  1033.     X
  1034.     X        *curif &= ~(IF_TRUE | IF_FALSE);
  1035.     X        tok = endline();
  1036.     X        expparse = FALSE;
  1037.     X        return(tok);
  1038.     X    }
  1039.     X    tok = endline();
  1040.     X    expparse = FALSE;
  1041.     X
  1042.     X    if (!(*curif & (IF_TRUE | IF_FALSE)) ||
  1043.     X       (wasraw && oldnint == ninterp)) {
  1044.     X        /*
  1045.     X         * either the truth is not known or
  1046.     X         * no macro interpretation was performed;
  1047.     X         * Don't interpret the #if.
  1048.     X         */
  1049.     X
  1050.     X        *curif &= ~(IF_TRUE | IF_FALSE);
  1051.     X        return(tok);
  1052.     X    }
  1053.     X    if (*curif & IF_FALSE) {
  1054.     X        ift_f();
  1055.     X    }
  1056.     X
  1057.     X    (void) dispose(f);
  1058.     X    return(tok);
  1059.     X}
  1060.     X
  1061.     Xint
  1062.     Xdo_else(f)
  1063.     Xchar *f;
  1064.     X{
  1065.     X    int tok;
  1066.     X
  1067.     X
  1068.     X    if (curif < &ifstk[0] || !(*curif & IF_INIF)) {
  1069.     X        warnf("if-less else");
  1070.     X        tok = endline();
  1071.     X        return(tok);
  1072.     X    }
  1073.     X    tok = endline();
  1074.     X
  1075.     X    *curif &= ~IF_INIF;
  1076.     X
  1077.     X    if (*curif & IF_TRUE) {
  1078.     X        *curif &= ~IF_TRUE;
  1079.     X        *curif |= IF_FALSE;
  1080.     X        ift_f();
  1081.     X        (void) dispose(f);
  1082.     X        return(tok);
  1083.     X    }
  1084.     X    if (*curif & IF_FALSE) {
  1085.     X        *curif &= ~IF_FALSE;
  1086.     X        *curif |= IF_TRUE;
  1087.     X        iff_t();
  1088.     X        (void) dispose(f);
  1089.     X        return(tok);
  1090.     X    }
  1091.     X    /* this is the 'else' of an uninterpreted if    */
  1092.     X    return(tok);
  1093.     X}
  1094.     X
  1095.     Xint
  1096.     Xdo_endif(f)
  1097.     Xchar *f;
  1098.     X{
  1099.     X    int tok;
  1100.     X
  1101.     X
  1102.     X    if (curif < &ifstk[0]) {
  1103.     X        warnf("if-less endif");
  1104.     X        tok = endline();
  1105.     X        return(tok);
  1106.     X    }
  1107.     X
  1108.     X    tok = endline();
  1109.     X
  1110.     X    if (!(*curif & (IF_TRUE | IF_FALSE))) {
  1111.     X        /* this is the 'endif' of an uninterpreted if */
  1112.     X        --curif;
  1113.     X        return(tok);
  1114.     X    }
  1115.     X    if (*curif & IF_FALSE) {
  1116.     X        iff_t();
  1117.     X    }
  1118.     X
  1119.     X    --curif;
  1120.     X    (void) dispose(f);
  1121.     X    return(tok);
  1122.     X}
  1123.     X
  1124.     X/*
  1125.     X * ift_f(), iff_t() - #if statement transitions which may affect output.
  1126.     X *   Ift_f() is called whenever an #if statement makes a transition from
  1127.     X *    from true (or non-existent) to false;
  1128.     X *   Iff_t() is called whenever one goes from false to true (or non-existent).
  1129.     X */
  1130.     X
  1131.     Xift_f()
  1132.     X{
  1133.     X    if (falsecnt++ == 0 && hidecnt == 0) {
  1134.     X        quec(ATTN);
  1135.     X        quec(AT_OUTOFF);
  1136.     X    }
  1137.     X}
  1138.     X
  1139.     Xiff_t()
  1140.     X{
  1141.     X    if (--falsecnt == 0 && hidecnt == 0) {
  1142.     X        quec(ATTN);
  1143.     X        quec(AT_OUTON);
  1144.     X    }
  1145.     X}
  1146.     X
  1147.     X/*
  1148.     X * stripwhite() - given a pointer to a (possibly dynamically allocated)
  1149.     X *  string which is to become the value of a macro, strip the leading
  1150.     X *  and trailing whitespace from the value.
  1151.     X */
  1152.     X
  1153.     Xstripwhite(s)
  1154.     Xchar *s;
  1155.     X{
  1156.     X    char *cp;
  1157.     X    char *nb;    /*
  1158.     X             * points to the char beyond the last non-blank
  1159.     X             * character in the string.
  1160.     X             */
  1161.     X
  1162.     X    /*
  1163.     X     * skip the initial whitespace, but don't count as whitespace a
  1164.     X     *  parameter number which preceeds an ATTN byte.
  1165.     X     */
  1166.     X
  1167.     X    for (cp = s; *cp == ' ' || *cp == '\t'; ++cp)
  1168.     X        ;
  1169.     X    if (*cp == ATTN) {
  1170.     X        if (cp == s) {
  1171.     X            bombf("INTERNAL: ATTN at beginning of string");
  1172.     X        } else {
  1173.     X            --cp;
  1174.     X        }
  1175.     X    }
  1176.     X
  1177.     X    /*
  1178.     X     * slide the string into its new position, noting the position of
  1179.     X     *  the char beyond the final non-white character so that the final
  1180.     X     *  whitespace can be eliminated.
  1181.     X     */
  1182.     X
  1183.     X    for (nb = cp; (*s++ = *cp) != '\0'; ++cp) {
  1184.     X        if (*cp != ' ' && *cp != '\t') {
  1185.     X            nb = s;
  1186.     X        }
  1187.     X    }
  1188.     X    *nb = '\0';
  1189.     X}
  1190.     X
  1191.     X/*
  1192.     X * nonwhite() - read until the next non-white (and non-comment) token,
  1193.     X *  using the scanner provided.
  1194.     X *  This routine is used only to skip whitespace within preprocessor command
  1195.     X *   lines.
  1196.     X */
  1197.     X
  1198.     Xint            /* the non-white, non-comment token        */
  1199.     Xnonwhite(scan)
  1200.     Xint (*scan)();        /* token scanner - either gtok() or gintok()    */
  1201.     X{
  1202.     X    int tok;
  1203.     X
  1204.     X    while ((tok = (*scan)()) == WHITE || tok == COMMENT)
  1205.     X        ;
  1206.     X    if (tok == 0) {
  1207.     X        warnf("unterminated preprocessor command");
  1208.     X    }
  1209.     X    return(tok);
  1210.     X}
  1211.     X
  1212.     X/*
  1213.     X * endline() - if not already at the end of the line, read tokens to get there.
  1214.     X *  return the final token (either NL or 0).
  1215.     X *  Used only to read the ends of preprocessor command lines.
  1216.     X *  For the benefit of uninterpreted command lines, macros are interpreted.
  1217.     X *
  1218.     X *  Endline should be called as late as possible in processing a line
  1219.     X *  so that error messages will be correlated to the offending line rather
  1220.     X *  than the following line.
  1221.     X */
  1222.     X
  1223.     Xint
  1224.     Xendline()
  1225.     X{
  1226.     X    int tok;    /* the current token    */
  1227.     X    char *cp;    /* the current character in a backward search    */
  1228.     X
  1229.     X
  1230.     X    /*
  1231.     X     * if the last character read (less ATTN byte pairs)
  1232.     X     *  was an unescaped newline, return;
  1233.     X     * Otherwise, skip tokens until the end of the line or
  1234.     X     *  the end of the file.
  1235.     X     */
  1236.     X
  1237.     X    for (cp = nxtout - 1; cp >= &pend[1]; cp -= 2) {
  1238.     X        if (*(cp - 1) != ATTN) {
  1239.     X            break;
  1240.     X        }
  1241.     X    }
  1242.     X    if (cp >= &pend[0] && *cp == '\n' &&
  1243.     X        (cp == &pend[0] || *(cp - 1) != '\\')) {
  1244.     X        /* an unescaped newline has already been read */
  1245.     X
  1246.     X        return(NL);
  1247.     X    }
  1248.     X
  1249.     X    while ((tok = gintok()) != NL) {
  1250.     X        if (tok == 0) {
  1251.     X            warnf("unterminated preprocessor command");
  1252.     X            return(tok);
  1253.     X        }
  1254.     X    }
  1255.     X    return(tok);
  1256.     X}
  1257. SHAR_EOF
  1258. if test 19059 -ne "`wc -c < 'ctrl.c'`"
  1259. then
  1260.     echo shar: error transmitting "'ctrl.c'" '(should have been 19059 characters)'
  1261. fi
  1262. fi # end of overwriting check
  1263. echo shar: extracting "'interp'" '(1986 characters)'
  1264. if test -f 'interp'
  1265. then
  1266.     echo shar: will not over-write existing file "'interp'"
  1267. else
  1268. sed 's/^    X//' << \SHAR_EOF > 'interp'
  1269.     XInterpretation rules:
  1270.     XText is, by default, interpreted.  The following chart shows what sections
  1271.     Xof preprocessor directives are not interpreted (i.e. no macro interpretation,
  1272.     Xno preprocessor line interpretation).  Note, however that the lexical analyzer
  1273.     Xis still used -- this prevents the recognition of floating point numbers as
  1274.     Xan instance of a formal parameter named, for example, "e4".
  1275.     X
  1276.     X(\n is an unescaped newline -- escaped newlines are recognized)
  1277.     X
  1278.     X# line n ..............\n
  1279.     X
  1280.     X# line n filename .....\n
  1281.     X              (the filename is treated as garbage)
  1282.     X# line n "filename"....\n
  1283.     X              (the filename is not interp'ed 'cause it's tween "")
  1284.     X              (#line lines are no-ops)
  1285.     X# include "file".......\n
  1286.     X                      (again, "file" is in quotes & is not interpreted)
  1287.     X# include <file>.......\n
  1288.     X       -----
  1289.     X# define macro replacement-text\n
  1290.     X    ------------------------- (if 'macro' is one to interpret)
  1291.     X        ------              (if 'macro' is not one to interpret --
  1292.     X                   also, the line is a no-op in this case)
  1293.     X# define macro( f1 , f2 , f3 ...) replacement-text with formals\n
  1294.     X    ---------------------------------------------------------
  1295.     X    ------------------------- (if 'macro' is not one to be interpreted)
  1296.     X  (the above case is strange in that the formal parameters in the replacement
  1297.     X   text must be protected against interpretation)
  1298.     X   [remember to allow comments in the formals]
  1299.     X# undef macro .........\n  (a no-op if 'macro' is not to be interpreted)
  1300.     X       ------
  1301.     X# ifdef macro .........\n  (also a no-op if 'macro is not -M'ed, except that
  1302.     X       ------            there must be a matching #endif or #else)
  1303.     X# ifndef macro ........\n  (same note as above)
  1304.     X        ------
  1305.     X# else.................\n
  1306.     X
  1307.     X# endif................\n
  1308.     X
  1309.     X# if expression\n  (if the truth of the expression is known, this is
  1310.     X                    interpreted.  Otherwise it is not)
  1311.     X#\n
  1312.     X
  1313.     X
  1314.     XSpecial case: text is not interpreted (but preprocessor lines are interpreted)
  1315.     X during the scan for the actual parameters of a macro with parameters.
  1316. SHAR_EOF
  1317. if test 1986 -ne "`wc -c < 'interp'`"
  1318. then
  1319.     echo shar: error transmitting "'interp'" '(should have been 1986 characters)'
  1320. fi
  1321. fi # end of overwriting check
  1322. echo shar: extracting "'io.c'" '(8707 characters)'
  1323. if test -f 'io.c'
  1324. then
  1325.     echo shar: will not over-write existing file "'io.c'"
  1326. else
  1327. sed 's/^    X//' << \SHAR_EOF > 'io.c'
  1328.     X/*
  1329.     X * io.c - input and output primitives for the selective C preprocessor, scpp.
  1330.     X *
  1331.     X * Copyright (c) 1985 by
  1332.     X * Tektronix, Incorporated Beaverton, Oregon 97077
  1333.     X * All rights reserved.
  1334.     X *
  1335.     X * Permission is hereby granted for personal, non-commercial
  1336.     X * reproduction and use of this program, provided that this
  1337.     X * notice and all copyright notices are included in any copy.
  1338.     X */
  1339.     X
  1340.     X# include <stdio.h>
  1341.     X# include "scpp.h"
  1342.     X
  1343.     X# define STDINPUT 0    /* file descriptor of stdin        */
  1344.     X# define BSIZE    512    /*
  1345.     X             * # of bytes per read -- controls how quickly
  1346.     X             * istk[] is consumed.
  1347.     X             */
  1348.     Xint dooutput = 1;    /* "actually write data" rather than tossing it    */
  1349.     X
  1350.     X/*
  1351.     X * nxtc() - return the next character from the input stream.
  1352.     X *   Nxtc() is used only by lex.
  1353.     X */
  1354.     X
  1355.     Xchar
  1356.     Xnxtc()
  1357.     X{
  1358.     X    char ch;
  1359.     X    int readcnt;
  1360.     X
  1361.     X
  1362.     X    while ((ch = *nxtin++) == ATTN) {
  1363.     X        switch (ch = *nxtin++) {
  1364.     X        case AT_EPUSH:    /* end of pushback (interpreted text)    */
  1365.     X            curfile->af_raw = TRUE;
  1366.     X            break;
  1367.     X        case AT_EBLK:    /* end of block    */
  1368.     X
  1369.     X            /*
  1370.     X             * The current block is exhausted.
  1371.     X             * Mark the end of the new block
  1372.     X             * then read in the new block (if there's space)
  1373.     X             * and adjust the top of stack.
  1374.     X             */
  1375.     X
  1376.     X            unc(AT_EBLK);
  1377.     X            unc(ATTN);
  1378.     X            if (nxtin < &istk[BSIZE]) {
  1379.     X                over();
  1380.     X            }
  1381.     X            nxtin -= BSIZE;
  1382.     X            readcnt = read(curfile->af_fd, nxtin, BSIZE);
  1383.     X            if (readcnt < 0) {
  1384.     X                bombf("read error");
  1385.     X            }
  1386.     X            if (readcnt > 0) {
  1387.     X                if (readcnt < BSIZE) {
  1388.     X                    /* slide the new data into place */
  1389.     X
  1390.     X                    register char *src, *dst;
  1391.     X
  1392.     X                    for (dst = nxtin + BSIZE,
  1393.     X                         src = nxtin + readcnt;
  1394.     X                         src > nxtin; *--dst = *--src)
  1395.     X                        ;
  1396.     X                    nxtin = dst;
  1397.     X                }
  1398.     X                break;
  1399.     X            }
  1400.     X
  1401.     X            /*
  1402.     X             * The current file is exhausted.
  1403.     X             * Pop the nonexistent block and the ATTN bytes
  1404.     X             *  from the input stack;
  1405.     X             * Turn on the output if necessary;
  1406.     X             * Close and pop the file.
  1407.     X             */
  1408.     X
  1409.     X            nxtin += BSIZE + 2;
  1410.     X            if (curfile->af_hide) {
  1411.     X                if (--hidecnt == 0 && falsecnt == 0) {
  1412.     X                    quec(ATTN);
  1413.     X                    quec(AT_OUTON);
  1414.     X                }
  1415.     X            }
  1416.     X            if (curfile->af_fd != STDINPUT) {
  1417.     X                close(curfile->af_fd);
  1418.     X            }
  1419.     X            free(curfile->af_name);
  1420.     X            if (--curfile >= &filestk[0]){
  1421.     X                break;
  1422.     X            }
  1423.     X
  1424.     X            /*
  1425.     X             * no more current files remain - open the next
  1426.     X             * file to be processed (if there is one),
  1427.     X             * or pushback the EOF character and an ATTN
  1428.     X             * so that further nxtc() calls return EOF.
  1429.     X             */
  1430.     X
  1431.     X            if (*nxtfile == (char *) 0) {
  1432.     X                unc(AT_EBLK);
  1433.     X                unc(ATTN);
  1434.     X                unc('\0');
  1435.     X                break;
  1436.     X            }
  1437.     X            pushfile(*nxtfile++, PF_NOLOOK, PF_NOHIDE);
  1438.     X            break;
  1439.     X
  1440.     X        default:
  1441.     X            bombf("illegal character in input: 0x%x", ATTN);
  1442.     X        }
  1443.     X    }
  1444.     X
  1445.     X    return(ch);
  1446.     X}
  1447.     X
  1448.     X/*
  1449.     X * untok() - push back the most recent token (less ATTN bytes)
  1450.     X *  from the output stream into the input stream.
  1451.     X */
  1452.     X
  1453.     Xuntok()
  1454.     X{
  1455.     X    char *cp;
  1456.     X
  1457.     X    for (cp = nxtout - 1; cp >= curtext; --cp) {
  1458.     X        if (cp > curtext && *(cp - 1) == ATTN) {
  1459.     X            --cp;
  1460.     X        } else {
  1461.     X            if (*cp == '\n' && curfile->af_raw) {
  1462.     X                curfile->af_line--;
  1463.     X            }
  1464.     X            unc(*cp);
  1465.     X        }
  1466.     X    }
  1467.     X    nxtout = dispose(curtext);
  1468.     X}
  1469.     X
  1470.     X/*
  1471.     X * pushmac() - push the given macro value back into the input stream.
  1472.     X *  Used to expand a macro.
  1473.     X * pushmac() is passed a pointer to the END of a string to be pushed
  1474.     X *  (some part of a macro's replacement text).  Pushmac() pushes the string
  1475.     X *  backwards onto the input stack until it comes to a null-terminator or
  1476.     X *  an ATTN byte.  It returns a pointer to the terminator.
  1477.     X */
  1478.     X
  1479.     Xchar *
  1480.     Xpushmac(v)
  1481.     Xchar *v;    /* points to a null-terminator or other ignored byte    */
  1482.     X{
  1483.     X    if (curfile->af_raw) {
  1484.     X        unc(AT_EPUSH);
  1485.     X        unc(ATTN);
  1486.     X        curfile->af_raw = FALSE;
  1487.     X    }
  1488.     X    while (*--v != '\0' && *v != ATTN) {
  1489.     X        if (nxtin-- < &istk[0]) {
  1490.     X            over();
  1491.     X        }
  1492.     X        *nxtin = *v;
  1493.     X    }
  1494.     X    return(v);
  1495.     X}
  1496.     X
  1497.     X/*
  1498.     X * pushfile() - effectively push the given file into the input stream.
  1499.     X *  Used to include a file.
  1500.     X */
  1501.     X
  1502.     Xpushfile(name, itype, hide)
  1503.     Xchar *name;
  1504.     Xint itype;
  1505.     X{
  1506.     X#define PNLEN 257
  1507.     X    char pname[PNLEN];
  1508.     X    char *cp;
  1509.     X    char **dp;
  1510.     X    struct afile *ip;
  1511.     X    char *rindex();
  1512.     X    char *malloc();
  1513.     X
  1514.     X
  1515.     X    if (++curfile >= &filestk[FILESIZ]) {
  1516.     X        --curfile;
  1517.     X        warnf("too many nested include files.  skipping `%s'", name);
  1518.     X        return;
  1519.     X    }
  1520.     X
  1521.     X    /*
  1522.     X     * if the name is to be opened with no modification, do that.
  1523.     X     * If the directory of the current file is to be searched, do that.
  1524.     X     * Search each directory in the list for the file.
  1525.     X     */
  1526.     X
  1527.     X    if (name[0] == '/' || itype == PF_NOLOOK) {
  1528.     X        (void) strcpy(pname, name);
  1529.     X        if (strcmp(name, "-") == 0) {
  1530.     X            curfile->af_fd = STDINPUT;
  1531.     X        } else {
  1532.     X            curfile->af_fd = open(pname, 0);
  1533.     X        }
  1534.     X    } else {
  1535.     X        curfile->af_fd = -1;
  1536.     X        if (itype == PF_DOT) {
  1537.     X            (void) strcpy(pname, (curfile - 1)->af_name);
  1538.     X            if ((cp = rindex(pname, '/'))) {
  1539.     X                ++cp;
  1540.     X            } else {
  1541.     X                cp = &pname[0];
  1542.     X            }
  1543.     X            if (cp + strlen(name) >= &pname[PNLEN]) {
  1544.     X                --curfile;
  1545.     X                bombf("name too long `%s%s'", pname, name);
  1546.     X            }
  1547.     X            (void) strcpy(cp, name);
  1548.     X            curfile->af_fd = open(pname, 0);
  1549.     X        }
  1550.     X        for (dp = &dirlist[0]; *dp && curfile->af_fd < 0; dp++) {
  1551.     X            cp = &pname[0] + strlen(*dp);
  1552.     X            if (cp >= &pname[PNLEN]) {
  1553.     X                --curfile;
  1554.     X                bombf("name too long `%s'", *dp);
  1555.     X            }
  1556.     X            (void) strcpy(pname, *dp);
  1557.     X            if (cp > &pname[0] && *(cp - 1) != '/') {
  1558.     X                *cp++ = '/';
  1559.     X                *cp = '\0';
  1560.     X            }
  1561.     X            if (cp + strlen(name) >= &pname[PNLEN]) {
  1562.     X                --curfile;
  1563.     X                bombf("name too long `%s%s'", pname, name);
  1564.     X            }
  1565.     X            (void) strcpy(cp, name);
  1566.     X            curfile->af_fd = open(pname, 0);
  1567.     X        }
  1568.     X    }
  1569.     X    if (curfile->af_fd < 0) {
  1570.     X        --curfile;
  1571.     X        warnf("cannot find%s file `%s'",
  1572.     X          curfile > &filestk[0] ? " include" : "", name);
  1573.     X        return;
  1574.     X    }
  1575.     X
  1576.     X    /*
  1577.     X     * the file is open.
  1578.     X     * See if this is a recursive include.
  1579.     X     */
  1580.     X
  1581.     X    for (ip = &filestk[0]; ip < curfile; ip++) {
  1582.     X        if (strcmp(ip->af_name, pname) == 0) {
  1583.     X            close(curfile->af_fd);
  1584.     X            --curfile;
  1585.     X            warnf("skipping recursive inclusion of `%s'", pname);
  1586.     X            return;
  1587.     X        }
  1588.     X    }
  1589.     X
  1590.     X    /*
  1591.     X     * fill in the rest of the afile structure.
  1592.     X     */
  1593.     X
  1594.     X    if (!(curfile->af_name = malloc((unsigned) strlen(pname) + 1))) {
  1595.     X        --curfile;
  1596.     X        bombf("out of memory");
  1597.     X    }
  1598.     X    (void) strcpy(curfile->af_name, pname);
  1599.     X    curfile->af_line = 1;
  1600.     X    curfile->af_raw = TRUE;
  1601.     X    curfile->af_hide = hide;
  1602.     X    if (hide) {
  1603.     X        if (hidecnt++ == 0 && falsecnt == 0) {
  1604.     X            quec(ATTN);
  1605.     X            quec(AT_OUTOFF);
  1606.     X        }
  1607.     X    }
  1608.     X    unc(AT_EBLK);
  1609.     X    unc(ATTN);
  1610.     X
  1611.     X#undef PNLEN
  1612.     X}
  1613.     X
  1614.     X/*
  1615.     X * quec() - move a character to the output queue, pend[]
  1616.     X */
  1617.     X
  1618.     Xquec(c)
  1619.     Xchar c;
  1620.     X{
  1621.     X    *nxtout = c;
  1622.     X    if (++nxtout >= &pend[PENDSIZ]) {
  1623.     X        bombf("too much forward search");
  1624.     X    }
  1625.     X}
  1626.     X
  1627.     X/*
  1628.     X * questr() - move the null-terminated string to the output queue, pend[]
  1629.     X *  Used only by xxlex().
  1630.     X */
  1631.     X
  1632.     Xquestr(s, len)
  1633.     Xregister char *s;
  1634.     Xint len;        /* length (in bytes) of the string to be moved */
  1635.     X{
  1636.     X    register char *d = nxtout;
  1637.     X
  1638.     X
  1639.     X    if (d + len < &pend[PENDSIZ]) {
  1640.     X        while (*d++ = *s++)
  1641.     X            ;
  1642.     X        nxtout += len;
  1643.     X    } else {
  1644.     X        bombf("too much forward search");
  1645.     X    }
  1646.     X}
  1647.     X
  1648.     X/*
  1649.     X * writepend() - write pending data to the output file, scanning for
  1650.     X *  output control characters.  Called only by the macro outpend().
  1651.     X */
  1652.     X
  1653.     Xwritepend()
  1654.     X{
  1655.     X    char *cp;
  1656.     X
  1657.     X    for (cp = &pend[0]; cp < nxtout; cp++) {
  1658.     X        if (*cp != ATTN) {
  1659.     X            if (dooutput) {
  1660.     X                putchar(*cp);
  1661.     X            }
  1662.     X        } else {
  1663.     X            switch(*++cp) {
  1664.     X            case AT_OUTON:
  1665.     X                dooutput = TRUE;
  1666.     X                break;
  1667.     X            case AT_OUTOFF:
  1668.     X                dooutput = FALSE;
  1669.     X                break;
  1670.     X            default:
  1671.     X                bombf("INTERNAL illegal character in output: 0x%x", ATTN);
  1672.     X            }
  1673.     X        }
  1674.     X    }
  1675.     X    nxtout = &pend[0];
  1676.     X}
  1677.     X
  1678.     X/*
  1679.     X * dispose() - dispose of pending output.
  1680.     X *  output from the given point to nxtout is discarded, output control ATTN's
  1681.     X *  are not discarded.
  1682.     X */
  1683.     X
  1684.     Xchar *        /* returns the new end of the buffer (nxttok)    */
  1685.     Xdispose(f)
  1686.     Xchar *f;
  1687.     X{
  1688.     X    char *cp;
  1689.     X
  1690.     X    for (cp = f; cp < nxtout; cp++) {
  1691.     X        if (*cp == ATTN) {
  1692.     X            /* copy the ATTN byte and the following code    */
  1693.     X
  1694.     X            *f++ = *cp++;
  1695.     X            *f++ = *cp;
  1696.     X        }
  1697.     X    }
  1698.     X    nxtout = f;
  1699.     X    return(f);
  1700.     X}
  1701.     X
  1702.     X/*
  1703.     X * warnf - print a file-specific error and continue;
  1704.     X */
  1705.     X
  1706.     X/*VARARGS1*/
  1707.     Xwarnf(s, x1, x2, x3, x4, x5, x6, x7, x8)
  1708.     Xchar *s;
  1709.     Xint x1, x2, x3, x4, x5, x6, x7, x8;
  1710.     X{
  1711.     X    if (curfile >= &filestk[0]) {
  1712.     X        fprintf(stderr, "\"%s\", line %d: ",
  1713.     X          curfile->af_name, curfile->af_line);
  1714.     X    }
  1715.     X    fprintf(stderr, s, x1, x2, x3, x4, x5, x6, x7, x8);
  1716.     X    fprintf(stderr, "\n");
  1717.     X    sawerror = TRUE;
  1718.     X}
  1719.     X
  1720.     X/*
  1721.     X * bombf - print a file-specific error and exit.
  1722.     X */
  1723.     X
  1724.     X/*VARARGS1*/
  1725.     Xbombf(s, x1, x2, x3, x4, x5, x6, x7, x8)
  1726.     Xchar *s;
  1727.     Xint x1, x2, x3, x4, x5, x6, x7, x8;
  1728.     X{
  1729.     X    warnf(s, x1, x2, x3, x4, x5, x6, x7, x8);
  1730.     X    exit(1);
  1731.     X}
  1732.     X
  1733.     X/*
  1734.     X * warn - print a non-file-specific error and continue.
  1735.     X */
  1736.     X
  1737.     X/*VARARGS1*/
  1738.     Xwarn(s, x1, x2, x3, x4, x5, x6, x7, x8)
  1739.     Xchar *s;
  1740.     Xint x1, x2, x3, x4, x5, x6, x7, x8;
  1741.     X{
  1742.     X    fprintf(stderr, s, x1, x2, x3, x4, x5, x6, x7, x8);
  1743.     X    fprintf(stderr, "\n");
  1744.     X    sawerror = TRUE;
  1745.     X}
  1746.     X
  1747.     X/*
  1748.     X * bomb - print a non-file-specific error and exit.
  1749.     X */
  1750.     X
  1751.     X/*VARARGS1*/
  1752.     Xbomb(s, x1, x2, x3, x4, x5, x6, x7, x8)
  1753.     Xchar *s;
  1754.     Xint x1, x2, x3, x4, x5, x6, x7, x8;
  1755.     X{
  1756.     X    fprintf(stderr, s, x1, x2, x3, x4, x5, x6, x7, x8);
  1757.     X    exit(1);
  1758.     X}
  1759.     X
  1760.     X/*
  1761.     X * over() - input pushback overflow
  1762.     X */
  1763.     X
  1764.     Xover()
  1765.     X{
  1766.     X    bombf("too much pushback");
  1767.     X}
  1768. SHAR_EOF
  1769. if test 8707 -ne "`wc -c < 'io.c'`"
  1770. then
  1771.     echo shar: error transmitting "'io.c'" '(should have been 8707 characters)'
  1772. fi
  1773. fi # end of overwriting check
  1774. #    End of shell archive
  1775. exit 0
  1776.  
  1777.