home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 138.lha / M4 / Sources / main.c < prev    next >
C/C++ Source or Header  |  1986-11-20  |  16KB  |  421 lines

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