home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / m4 / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-20  |  12.8 KB  |  486 lines

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