home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 2: PC / frozenfish_august_1995.bin / bbs / d01xx / d0160.lha / M4 / Src / main.c < prev    next >
C/C++ Source or Header  |  1988-10-02  |  11KB  |  458 lines

  1. /*
  2.  * main.c
  3.  * Facility: m4 macro processor
  4.  * by: oz
  5.  */
  6.  
  7. #include "mdef.h"
  8.  
  9. /*
  10.  * m4 - macro processor
  11.  *
  12.  * PD m4 is based on the macro tool distributed with the software 
  13.  * tools (VOS) package, and described in the "SOFTWARE TOOLS" and 
  14.  * "SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include 
  15.  * most of the command set of SysV m4, the standard UN*X macro processor.
  16.  *
  17.  * Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro,
  18.  * there may be certain implementation similarities between
  19.  * the two. The PD m4 was produced without ANY references to m4
  20.  * sources.
  21.  *
  22.  * References:
  23.  *
  24.  *    Software Tools distribution: macro
  25.  *
  26.  *    Kernighan, Brian W. and P. J. Plauger, SOFTWARE
  27.  *    TOOLS IN PASCAL, Addison-Wesley, Mass. 1981
  28.  *
  29.  *    Kernighan, Brian W. and P. J. Plauger, SOFTWARE
  30.  *    TOOLS, Addison-Wesley, Mass. 1976
  31.  *
  32.  *    Kernighan, Brian W. and Dennis M. Ritchie,
  33.  *    THE M4 MACRO PROCESSOR, Unix Programmer's Manual,
  34.  *    Seventh Edition, Vol. 2, Bell Telephone Labs, 1979
  35.  *
  36.  *    System V man page for M4
  37.  *
  38.  * Modification History:
  39.  *
  40.  * Jan 28 1986 Oz    Break the whole thing into little
  41.  *            pieces, for easier (?) maintenance.
  42.  *
  43.  * Dec 12 1985 Oz    Optimize the code, try to squeeze
  44.  *            few microseconds out..
  45.  *
  46.  * Dec 05 1985 Oz    Add getopt interface, define (-D),
  47.  *            undefine (-U) options.
  48.  *
  49.  * Oct 21 1985 Oz    Clean up various bugs, add comment handling.
  50.  *
  51.  * June 7 1985 Oz    Add some of SysV m4 stuff (m4wrap, pushdef,
  52.  *            popdef, decr, shift etc.).
  53.  *
  54.  * June 5 1985 Oz    Initial cut.
  55.  *
  56.  * Implementation Notes:
  57.  *
  58.  * [1]    PD m4 uses a different (and simpler) stack mechanism than the one 
  59.  *    described in Software Tools and Software Tools in Pascal books. 
  60.  *    The triple stack nonsense is replaced with a single stack containing 
  61.  *    the call frames and the arguments. Each frame is back-linked to a 
  62.  *     previous stack frame, which enables us to rewind the stack after 
  63.  *     each nested call is completed. Each argument is a character pointer 
  64.  *    to the beginning of the argument string within the string space.
  65.  *    The only exceptions to this are (*) arg 0 and arg 1, which are
  66.  *     the macro definition and macro name strings, stored dynamically
  67.  *    for the hash table.
  68.  *
  69.  *        .                       .
  70.  *    |   .    |  <-- sp            |  .  |
  71.  *    +-------+                +-----+
  72.  *    | arg 3 ------------------------------->| str |
  73.  *    +-------+                |  .  |
  74.  *    | arg 2 --------------+            .
  75.  *    +-------+          |
  76.  *        *              |            |     |
  77.  *    +-------+          |         +-----+
  78.  *    | plev    |  <-- fp     +---------------->| str |
  79.  *    +-------+                |  .  |
  80.  *    | type    |                   .
  81.  *    +-------+
  82.  *    | prcf    -----------+        plev: paren level
  83.  *    +-------+         |        type: call type
  84.  *    |   .    |        |        prcf: prev. call frame
  85.  *        .              |
  86.  *    +-------+       |
  87.  *    |    <----------+
  88.  *    +-------+
  89.  *
  90.  * [2]    We have three types of null values:
  91.  *
  92.  *        nil  - nodeblock pointer type 0
  93.  *        null - null string ("")
  94.  *        NULL - Stdio-defined NULL
  95.  *
  96.  */
  97.  
  98. #ifdef MYMKTMP
  99. int mytmpnum=000000;        /* used in mktemp()           */
  100. #endif
  101.  
  102. ndptr hashtab[HASHSIZE];    /* hash table for macros etc.  */
  103. char buf[BUFSIZE];        /* push-back buffer           */
  104. char *bp = buf;         /* first available character   */
  105. char *endpbb = buf+BUFSIZE;    /* end of push-back buffer     */
  106. stae mstack[STACKMAX+1];     /* stack of m4 machine         */
  107. char strspace[STRSPMAX+1];    /* string space for evaluation */
  108. char *ep = strspace;        /* first free char in strspace */
  109. char *endest= strspace+STRSPMAX;/* end of string space           */
  110. int sp;             /* current m4  stack pointer   */
  111. int fp;             /* m4 call frame pointer       */
  112. FILE *infile[MAXINP];        /* input file stack (0=stdin)  */
  113. FILE *outfile[MAXOUT];        /* diversion array(0=bitbucket)*/
  114. FILE *active;            /* active output file pointer  */
  115. char *m4temp;            /* filename for diversions     */
  116. int ilevel = 0;         /* input file stack pointer    */
  117. int oindex = 0;         /* diversion index..           */
  118. char *null = "";                /* as it says.. just a null..  */
  119. char *m4wraps = "";             /* m4wrap string default..     */
  120. char lquote = LQUOTE;        /* left quote character  (`)   */
  121. char rquote = RQUOTE;        /* right quote character (')   */
  122. char scommt = SCOMMT;        /* start character for comment */
  123. char ecommt = ECOMMT;        /* end character for comment   */
  124. struct keyblk keywrds[] = {    /* m4 keywords to be installed */
  125.     "include",      INCLTYPE,
  126.     "sinclude",     SINCTYPE,
  127.     "define",       DEFITYPE,
  128.     "defn",         DEFNTYPE,
  129.     "divert",       DIVRTYPE,
  130.     "expr",         EXPRTYPE,
  131.     "eval",         EXPRTYPE,
  132.     "substr",       SUBSTYPE,
  133.     "ifelse",       IFELTYPE,
  134.     "ifdef",        IFDFTYPE,
  135.     "len",          LENGTYPE,
  136.     "incr",         INCRTYPE,
  137.     "decr",         DECRTYPE,
  138.     "dnl",          DNLNTYPE,
  139.     "changequote",  CHNQTYPE,
  140.     "changecom",    CHNCTYPE,
  141.     "index",        INDXTYPE,
  142. #ifdef EXTENDED
  143.     "paste",        PASTTYPE,
  144.     "spaste",       SPASTYPE,
  145. #endif
  146.     "popdef",       POPDTYPE,
  147.     "pushdef",      PUSDTYPE,
  148.     "dumpdef",      DUMPTYPE,
  149.     "shift",        SHIFTYPE,
  150.     "translit",     TRNLTYPE,
  151.     "undefine",     UNDFTYPE,
  152.     "undivert",     UNDVTYPE,
  153.     "divnum",       DIVNTYPE,
  154.     "maketemp",     MKTMTYPE,
  155.     "errprint",     ERRPTYPE,
  156.     "m4wrap",       M4WRTYPE,
  157.     "m4exit",       EXITTYPE,
  158. #if unix || vms
  159.     "syscmd",       SYSCTYPE,
  160.     "sysval",       SYSVTYPE,
  161. #endif
  162. #if unix
  163.     "unix",         MACRTYPE,
  164. #else
  165. #if vms
  166.     "vms",          MACRTYPE,
  167. #endif
  168. #endif
  169. };
  170.  
  171. #define MAXKEYS    (sizeof(keywrds)/sizeof(struct keyblk))
  172.  
  173. extern ndptr lookup();
  174. extern ndptr addent();
  175. extern int onintr();
  176.  
  177. extern char *malloc();
  178. extern char *mktemp();
  179.  
  180. extern int optind;
  181. extern char *optarg;
  182.  
  183. void main(argc,argv)
  184. char *argv[];
  185. {
  186.     register int c;
  187.     register int n;
  188.     char *p;
  189.  
  190.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  191.         signal(SIGINT, onintr);
  192. #ifdef NONZEROPAGES
  193.     initm4();
  194. #endif
  195.     initkwds();
  196.  
  197.     while ((c = getopt(argc, argv, "tD:U:o:")) != EOF)
  198.         switch(c) {
  199.  
  200.         case 'D':               /* define something..*/
  201.             for (p = optarg; *p; p++)
  202.                 if (*p == '=')
  203.                     break;
  204.             if (*p)
  205.                 *p++ = EOS;
  206.             dodefine(optarg, p);
  207.             break;
  208.         case 'U':               /* undefine...       */
  209.             remhash(optarg, TOP);
  210.             break;
  211.         case 'o':        /* specific output   */
  212.         case '?':
  213.         default:
  214.             usage();
  215.         }
  216.  
  217.     infile[0] = stdin;        /* default input (naturally) */
  218.     active = stdout;        /* default active output     */
  219.      m4temp = mktemp(DIVNAM);    /* filename for diversions   */
  220.  
  221.     sp = -1;            /* stack pointer initialized */
  222.     fp = 0;             /* frame pointer initialized */
  223.  
  224.     macro();            /* get some work done here   */
  225.  
  226.     if (*m4wraps) {         /* anything for rundown ??   */
  227.         ilevel = 0;        /* in case m4wrap includes.. */
  228.         putback(EOF);        /* eof is a must !!         */
  229.         pbstr(m4wraps);     /* user-defined wrapup act   */
  230.         macro();        /* last will and testament   */
  231.     }
  232.     else                /* default wrap-up: undivert */
  233.         for (n = 1; n < MAXOUT; n++)
  234.             if (outfile[n] != NULL)
  235.                 getdiv(n);
  236.  
  237.                     /* remove bitbucket if used  */
  238.     if (outfile[0] != NULL) {
  239.         (void) fclose(outfile[0]);
  240.         m4temp[UNIQUE] = '0';
  241. #if vms
  242.         (void) remove(m4temp);
  243. #else
  244.         (void) unlink(m4temp);
  245. #endif
  246.     }
  247.  
  248.     exit(0);
  249. }
  250.  
  251. ndptr inspect();    /* forward ... */
  252.  
  253. /*
  254.  * macro - the work horse..
  255.  *
  256.  */
  257. int macro() {
  258.     char token[MAXTOK];
  259.     register char *s;
  260.     register int t, l;
  261.     register ndptr p;
  262.     register int  nlpar;
  263.  
  264.     cycle {
  265.         if ((t = gpbc()) == '_' || isalpha(t)) {
  266.             putback(t);
  267.             if ((p = inspect(s = token)) == nil) {
  268.                 if (sp < 0)
  269.                     while (*s)
  270.                         putc(*s++, active);
  271.                 else
  272.                     while (*s)
  273.                         chrsave(*s++);
  274.             }
  275.             else {
  276.         /*
  277.          * real thing.. First build a call frame:
  278.          *
  279.          */
  280.                 pushf(fp);    /* previous call frm */
  281.                 pushf(p->type); /* type of the call  */
  282.                 pushf(0);    /* parenthesis level */
  283.                 fp = sp;    /* new frame pointer */
  284.         /*
  285.          * now push the string arguments:
  286.          *
  287.          */
  288.                 pushs(p->defn);          /* defn string */
  289.                 pushs(p->name);          /* macro name  */
  290.                 pushs(ep);          /* start next..*/
  291.  
  292.                 putback(l = gpbc());
  293.                 if (l != LPAREN)  {   /* add bracks  */
  294.                     putback(RPAREN);
  295.                     putback(LPAREN);
  296.                 }
  297.             }
  298.         }
  299.         else if (t == EOF) {
  300.             if (sp > -1)
  301.                 error("m4: unexpected end of input");
  302.             if (--ilevel < 0)
  303.                 break;            /* all done thanks.. */
  304.             (void) fclose(infile[ilevel+1]);
  305.             continue;
  306.         }
  307.     /*
  308.      * non-alpha single-char token seen..
  309.      * [the order of else if .. stmts is
  310.      * important.]
  311.      *
  312.      */
  313.         else if (t == lquote) {         /* strip quotes */
  314.             nlpar = 1;
  315.             do {
  316.                 if ((l = gpbc()) == rquote)
  317.                     nlpar--;
  318.                 else if (l == lquote)
  319.                     nlpar++;
  320.                 else if (l == EOF)
  321.                     error("m4: missing right quote");
  322.                 if (nlpar > 0) {
  323.                     if (sp < 0)
  324.                         putc(l, active);
  325.                     else
  326.                         chrsave(l);
  327.                 }
  328.             }
  329.             while (nlpar != 0);
  330.         }
  331.  
  332.         else if (sp < 0) {        /* not in a macro at all */
  333.             if (t == scommt) {    /* comment handling here */
  334.                 putc(t, active);
  335.                 while ((t = gpbc()) != ecommt)
  336.                     putc(t, active);
  337.             }
  338.             putc(t, active);    /* output directly..     */
  339.         }
  340.  
  341.         else switch(t) {
  342.  
  343.         case LPAREN:
  344.             if (PARLEV > 0)
  345.                 chrsave(t);
  346.             while (isspace(l = gpbc()))
  347.                 ;        /* skip blank, tab, nl.. */
  348.             putback(l);
  349.             PARLEV++;
  350.             break;
  351.  
  352.         case RPAREN:
  353.             if (--PARLEV > 0)
  354.                 chrsave(t);
  355.             else {            /* end of argument list */
  356.                 chrsave(EOS);
  357.  
  358.                 if (sp == STACKMAX)
  359.                     error("m4: internal stack overflow");
  360.  
  361.                 if (CALTYP == MACRTYPE)
  362.                     expand(mstack+fp+1, sp-fp);
  363.                 else
  364.                     eval(mstack+fp+1, sp-fp, CALTYP);
  365.  
  366.                 ep = PREVEP;    /* flush strspace */
  367.                 sp = PREVSP;    /* previous sp..  */
  368.                 fp = PREVFP;    /* rewind stack...*/
  369.             }
  370.             break;
  371.  
  372.         case COMMA:
  373.             if (PARLEV == 1)    {
  374.                 chrsave(EOS);        /* new argument   */
  375.                 while (isspace(l = gpbc()))
  376.                     ;
  377.                 putback(l);
  378.                 pushs(ep);
  379.             }
  380.             break;
  381.         default:
  382.             chrsave(t);            /* stack the char */
  383.             break;
  384.         }
  385.     }
  386. }
  387.  
  388.  
  389. /*
  390.  * build an input token..
  391.  * consider only those starting with _ or A-Za-z. This is a
  392.  * combo with lookup to speed things up.
  393.  */
  394. ndptr
  395. inspect(tp) 
  396. register char *tp;
  397. {
  398.     register int h = 0;
  399.     register char c;
  400.     register char *name = tp;
  401.     register char *etp = tp+MAXTOK;
  402.     register ndptr p;
  403.  
  404.     while (tp < etp && (isalnum(c = gpbc()) || c == '_'))
  405.         h += (*tp++ = c);
  406.     putback(c);
  407.     if (tp == etp)
  408.         error("m4: token too long");
  409.     *tp = EOS;
  410.     for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
  411.         if (strcmp(name, p->name) == 0)
  412.             break;
  413.     return(p);
  414. }
  415.  
  416. #ifdef NONZEROPAGES
  417. /*
  418.  * initm4 - initialize various tables. Useful only if your system 
  419.  * does not know anything about demand-zero pages.
  420.  *
  421.  */
  422. initm4()
  423. {
  424.     register int i;
  425.  
  426.     for (i = 0; i < HASHSIZE; i++)
  427.         hashtab[i] = nil;
  428.     for (i = 0; i < MAXOUT; i++)
  429.         outfile[i] = NULL;
  430. }
  431. #endif
  432.  
  433. /*
  434.  * initkwds - initialise m4 keywords as fast as possible. 
  435.  * This very similar to install, but without certain overheads,
  436.  * such as calling lookup. Malloc is not used for storing the 
  437.  * keyword strings, since we simply use the static  pointers
  438.  * within keywrds block. We also assume that there is enough memory 
  439.  * to at least install the keywords (i.e. malloc won't fail).
  440.  *
  441.  */
  442.  
  443. int initkwds() {
  444.     register int i;
  445.     register int h;
  446.     register ndptr p;
  447.  
  448.     for (i = 0; i < MAXKEYS; i++) {
  449.         h = hash(keywrds[i].knam);
  450.         p = (ndptr) malloc(sizeof(struct ndblock));
  451.         p->nxtptr = hashtab[h];
  452.         hashtab[h] = p;
  453.         p->name = keywrds[i].knam;
  454.         p->defn = null;
  455.         p->type = keywrds[i].ktyp | STATIC;
  456.     }
  457. }
  458.