home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 355_02 / slk2.exe / SPP / DEF.C < prev    next >
C/C++ Source or Header  |  1991-06-09  |  30KB  |  1,289 lines

  1. /*
  2.     New Sherlock Preprocessor -- definition and expansion routines.
  3.  
  4.     source:  def.c
  5.     started: October 22, 1985
  6.  
  7.     Version 1.0:    July 20, 1988
  8.     Version 1.0b:    November 3, 1988
  9.             bug fix in pp_def()
  10.     Version 1.1:
  11.         February 15, 1988
  12.             bug fix in pp_def()
  13.             bug fix in substitute()
  14.         February 16, 1989
  15.             outer_expand calls so_disable() if Sherlock macro seen.
  16.             periods removed from error messages.
  17.     Version 1.4
  18.         June 22, 1989
  19.             o backslash-newline processing removed because
  20.               sysnext() now eliminates all such combinations.
  21.         June 27, 1989
  22.             o Add check for macro overflow to substitute.
  23.             o Allocate buffers from the heap, not the stack.
  24.     Version 1.5
  25.         August 3, 1989
  26.             o Added support for single-line comments.
  27.             o Moved run-on check to better place in substitute().
  28.  
  29.  
  30.     PUBLIC DOMAIN SOFTWARE
  31.  
  32.     Sherlock, including the SPP, SDEL and SDIF programs, was placed in
  33.     the public domain on June 15, 1991, by its author,
  34.  
  35.         Edward K. Ream
  36.         166 North Prospect Ave.
  37.         Madison, WI 53705.
  38.         (608) 257-0802
  39.  
  40.     Sherlock may be used for any commercial or non-commercial purpose.
  41.  
  42.  
  43.     DISCLAIMER OF WARRANTIES
  44.  
  45.     Edward K. Ream (Ream) specifically disclaims all warranties,
  46.     expressed or implied, with respect to this computer software,
  47.     including but not limited to implied warranties of merchantability
  48.     and fitness for a particular purpose.  In no event shall Ream be
  49.     liable for any loss of profit or any commercial damage, including
  50.     but not limited to special, incidental consequential or other damages.
  51. */
  52.  
  53. #include "spp.h"
  54.  
  55. /*
  56.     Forward declarations of internal routines.
  57. */
  58.  
  59. #ifndef MICRO_SOFT
  60.  
  61. static void    aexpand        (char *name, int *nargs, char **p1, char *out,
  62.                     char **p2);
  63. static void    aparse        (char *name, int *nargs, char *out, char **p);
  64. static bool    a1parse        (char *name, char *out, int max_out);
  65. static void    disable_name    (char *name);
  66. static void    enable_name    (void);
  67. static bool    is_disabled    (char *name);
  68. static void    expand        (char *name, int nargs, char *rtext, char *out,
  69.                     int max_out);
  70. static void    rescan        (char *name, char *in, char *out, int max_out,
  71.                     bool bounded_flag);
  72. static void    substitute    (char *name, int nargs, char *in, char **args,
  73.                     char *out, int max_out);
  74. #else
  75.  
  76. static void    aexpand        (char *, int *, char **, char *, char **);
  77. static void    aparse        (char *, int *, char *, char **);
  78. static bool    a1parse        (char *, char *, int);
  79. static void    disable_name    (char *);
  80. static void    enable_name    (void);
  81. static bool    is_disabled    (char *);
  82. static void    expand        (char *, int, char *, char *, int);
  83. static void    rescan        (char *, char *, char *, int, bool);
  84. static void    substitute    (char *, int, char *, char **, char *, int);
  85.  
  86. #endif
  87.  
  88. /*
  89.     Define static variables used only by this module.
  90. */
  91. static char *    x_stack [MAX_MDEPTH];    /* Pointers to disabled macro names. */
  92. static int    x_count = 0;        /* Number of disabled macro names.   */
  93.  
  94. /*
  95.     Externally visible routines.
  96. */
  97.  
  98. /*
  99.     Check an identifier seen at the outer level to see whether it
  100.     could be a macro call and expand it and return TRUE if so.
  101. */
  102. bool
  103. outer_expand(name, old_mflag)
  104. char    *name;
  105. bool    old_mflag;
  106. {
  107.     char     *p, *p1, *p2;
  108.     int    i;
  109.     char *    rtext;            /* Replacement text for name.       */
  110.     int    nargs;            /* Required number of args.       */
  111.     char    temp_buf[MAX_RTEXT];    /* Temp spot for macro.           */    
  112.  
  113.     ENTER_TRACE("outer_expand", printf("(%s)\n", name));
  114.  
  115.     if (m_flag || old_mflag || !mst_lookup(name, &rtext, &nargs)) {
  116.         RETURN_BOOL("outer_expand", FALSE);
  117.     }
  118.     else if (nargs != -1) {
  119.         skip_ws(TRUE);
  120.         if (ch != '(') {
  121.             warn3("Function-like macro, ", name,
  122.                 "appears without arguments");
  123.             RETURN_BOOL("outer_expand", FALSE);
  124.         }
  125.     }
  126.  
  127.     /*
  128.         2/16/89
  129.         Disable Sherlock output when a sherlock macro is seen.
  130.     */
  131.     if (is_sherlock(name)) {
  132.         so_disable();
  133.     }
  134.  
  135.     /*
  136.         Expand the macro into temp_buf[].
  137.         This will use ONLY file characters.
  138.     */
  139.     expand(name, nargs, rtext, &temp_buf[0], MAX_RTEXT);
  140.  
  141.     /*
  142.         Expand will have changed ch to point past any args.
  143.         Use sys_fpb so that
  144.         1.  The old ch will appear just AFTER the macro expansion.
  145.         2.  It will not be mistaken added to the rescan buffer.
  146.     */
  147.     sys_fpb(ch);
  148.  
  149.     /*
  150.         Fully rescan temp_buf[] into macro_buf[].
  151.         Use an unbounded rescan, i.e., allow file characters to
  152.         complete a partial list of actual parameters which may
  153.         be started in the rescanned text.
  154.     */
  155.     p_rescan = NULL;
  156.     m_flag   = FALSE;
  157.     rescan(name, &temp_buf[0], ¯o_buf[0], MAX_RTEXT, FALSE);
  158.  
  159.     /* Make sure we save the now current ch. */
  160.     sys_fpb(ch);
  161.  
  162.     /* Delete any internal flags from the output. */
  163.     TRACEP("outer_expand", printf("macro1: %s\n", pr_str(¯o_buf[0])));
  164.  
  165.     p1 = p2 = ¯o_buf[0];
  166.     while (*p2) {
  167.         if (*p2 == EXPAND_OFF || *p2 == EORT) {
  168.             p2++;
  169.         }
  170.         else {
  171.             *p1++ = *p2++;
  172.         }
  173.     }
  174.     *p1 = '\0';
  175.     TRACEP("outer_expand", printf("macro2: %s\n", pr_str(¯o_buf[0])));
  176.  
  177.     /*
  178.         Inhibit any further macro expansion until the end of the
  179.         current local input stream.
  180.     */
  181.  
  182.     if (m_flag) {
  183.         syserr("outer_expand: Can't happen");
  184.     }
  185.  
  186.     /*
  187.         Indicate that the macro buffer has a macro.
  188.     */
  189.     m_flag = TRUE;
  190.     p_rescan = ¯o_buf[0];
  191.  
  192.     /* Reload ch from the global macro buffer. */
  193.     sysnext();
  194.     RETURN_BOOL("outer_expand", TRUE);
  195. }
  196.  
  197. /*
  198.     Handle the #define directive by parsing the statement, and entering
  199.     the macro's name, number of arguments and replacement text into the
  200.     macro table.
  201.  
  202.     Formal arguments are found and replaced by a flag byte followed by the
  203.     number of the formal argument.
  204. */
  205. void
  206. pp_def()
  207. {
  208.     int    i;
  209.  
  210.     char    name[MAX_SYMBOL];    /* Name of macro.        */
  211.  
  212.     char    rtext [MAX_RTEXT];    /* Replacement text.        */
  213.     int    rcount = 0;        /* Number of chars in rtext[].    */
  214.  
  215.     char    atext [MAX_ATEXT];    /* Text of formal parameters.    */
  216.     int    acount = 0;        /* Number of chars in atext[].    */
  217.  
  218.     char *    argps [MAX_NARGS];    /* Pointers into atext[].     */
  219.     int    nargs = 0;        /* Number of entries in argps[]. */
  220.  
  221.     ENTER("pp_def");
  222.  
  223.     /* Make sure the name is present. */
  224.     if (!isid1(ch)) {
  225.         error("#define ignored--no symbol name given");
  226.         skip_1line();
  227.         return;
  228.     }
  229.  
  230.     /* Get the macro name into name. */
  231.     t_id(&name[0], MAX_SYMBOL);
  232.     TRACEPN("pp_def", printf("macro name is %s\n", name));
  233.     
  234.     if (ch != '(') {
  235.         /* Indicate no argument list and continue. */
  236.         nargs = -1;
  237.         goto gettext;
  238.     }
  239.     else {
  240.         sysnext();
  241.     }
  242.  
  243.     if (ch == ')') {
  244.         sysnext();
  245.         nargs = 0;
  246.         goto gettext;
  247.     }
  248.  
  249.     /* 
  250.         Put the formal arguments into dynamic storage.
  251.         Set pointers to the arguments argps[].
  252.     */
  253.     while (nargs < MAX_NARGS) {
  254.  
  255.         skip_ws(TRUE);
  256.  
  257.         if (!isid1(ch)) {
  258.             err2(    "#define ignored--",
  259.                 "formal arg must be an identifier");
  260.             skip_1line();
  261.             RETURN_VOID("pp_def");
  262.         }
  263.  
  264.         /* Copy one formal arg to atext[]. */
  265.         t_id(&t_symbol[0], MAX_SYMBOL);
  266.         argps [nargs++] = &atext[acount];
  267.         str_cpy(atext + acount, t_symbol);
  268.         acount += strlen(t_symbol)+1;
  269.  
  270.         TRACEPN("pp_def", printf("formal [%d] is %s\n",
  271.             nargs-1, pr_str(argps [nargs-1])));
  272.  
  273.         /* Bug fix: 11/3/88 */
  274.         skip_ws(TRUE);
  275.             
  276.         if (ch == ')') {
  277.             sysnext();
  278.             goto gettext;
  279.         }
  280.         else if (ch == ',') {
  281.             sysnext();
  282.         }
  283.     }
  284.     error("#define ignored--too many arguments");
  285.     skip_1line();
  286.     RETURN_VOID("pp_def");
  287.  
  288.  
  289.     /*
  290.         At this point, nargs contains the number of formal arguments,
  291.         or -1 if no argument list was given.
  292.         0 is allowed and means that the argument list was ().
  293.     */
  294.  
  295. gettext:
  296.  
  297.     TRACEPN("pp_def", printf("nargs is %d\n", nargs));
  298.  
  299.     skip_bl();
  300.  
  301.     /*
  302.         Put the replacement text into rtext[].
  303.         The text is tokenized, which means that partial comments
  304.         or strings are not valid.
  305.  
  306.         Replace formal arg n by n FLAG on the fly.
  307.     */
  308.  
  309.     for (rcount = 0;;) {
  310.         switch (ch) {
  311.  
  312.         case END_FILE:
  313.         case '\n':
  314.             goto done;
  315.  
  316.         /* Replace a sequence of white space by a blank. */
  317.         case ' ':
  318.         case '\t':
  319.             sysnext();
  320.             if (rcount > 0 && rtext[rcount-1] != ' ') {
  321.                 rtext[rcount++] = ' ';
  322.             }
  323.             continue;
  324.  
  325. #ifdef BACKSLASH_NEWLINE
  326.         /* Look for continuation of definitions. */
  327.         case '\\':
  328.             sysnext();
  329.             if (ch == '\n') {
  330.                 sysnext();
  331.                 if (rcount > 0 && rtext[rcount-1] != ' ') {
  332.                     rtext[rcount++] = ' ';
  333.                 }
  334.                 bump_line();
  335.             }
  336.             else {
  337.                 warning("Backslash not followed by newline");
  338.                 rtext[rcount++] = '\\';
  339.             }
  340.             continue;
  341. #endif
  342.  
  343.         /* Eliminate standard and single-line comments. */
  344.         case '/':
  345.             sysnext();
  346.             if (ch == '*') {
  347.                 /* Skip a regular comment. */
  348.                 if (rcount > 0 && rtext[rcount-1] != ' ') {
  349.                     rtext[rcount++] = ' ';
  350.                 }
  351.                 sysnext();
  352.                 skip_comment();
  353.             }
  354.             else if (slc_flag && ch == '/') {
  355.                 /* 8/3/89 Skip a single-line comment. */
  356.                 if (rcount > 0 && rtext[rcount-1] != ' ') {
  357.                     rtext[rcount++] = ' ';
  358.                 }
  359.                 while (ch != END_FILE && ch != '\n') {
  360.                     sysnext();
  361.                 }
  362.                 goto done;
  363.             }
  364.             else {
  365.                 rtext[rcount++] = '/';
  366.             }
  367.             continue;
  368.  
  369.         /* Handle complete strings. */
  370.         case '"':
  371.         case '\'':
  372.             t_string(&rtext[rcount], MAX_RTEXT-rcount, TRUE);
  373.             rcount += t_length;
  374.             continue;
  375.  
  376.         /* Handle # formal param and ## */
  377.         case '#':
  378.             sysnext();
  379.             if (ch == '#') {
  380.                 sysnext();
  381.                 rtext[rcount++] = CONCAT_FLAG;
  382.                 continue;
  383.             }
  384.  
  385.             /* We expect an identifier here. */
  386.             /* bug fix: 2/15/89 */
  387.             if (ch == ' ' || ch == '\t') {
  388.                 rtext[rcount++] = ' ';
  389.                 skip_bl();
  390.             }
  391.             if (isid1(ch)) {
  392.                 t_id(&rtext[rcount], MAX_RTEXT-rcount);
  393.                 i = arg_search( rtext + rcount, nargs,
  394.                         &argps[0]);
  395.                 if (i >= 0) {
  396.                     /* Replace id with flags. */
  397.                     rtext[rcount++] = POUND_FLAG;
  398.                     rtext[rcount++] = i + ARG_OFFSET;
  399.                 }
  400.                 else {
  401.                     /* Accept id. */
  402.                     rcount += t_length;
  403.                 }
  404.             }
  405.             else {
  406.                 /* bug fix: 2/15/89 */
  407.                 warning("Id expected after '#'");
  408.             }
  409.             continue;
  410.  
  411.         default:
  412.  
  413.             /* Possible formal parameter. */
  414.             if (isid1(ch)) {
  415.                 t_id(&rtext[rcount], MAX_RTEXT-rcount);
  416.                 i = arg_search(    rtext + rcount, nargs,
  417.                         &argps[0]);
  418.                 if (i >= 0) {
  419.                     rtext[rcount++] = ARG_FLAG;
  420.                     rtext[rcount++] = i+ARG_OFFSET;
  421.                 }
  422.                 else {
  423.                     rcount += t_length;
  424.                 }
  425.             }
  426.             else {
  427.                 rtext[rcount++] = ch;
  428.                 sysnext();
  429.             }
  430.             continue;
  431.         }
  432.     }
  433.  
  434. done:
  435.  
  436.     /* This check is made only here to save a little time. */
  437.     if (rcount >= MAX_RTEXT) {
  438.         fatal("Replacement text of macro is too long");
  439.     }
  440.  
  441.     /* Strip at most one blank off the end of the definition. */
  442.     if (rtext[rcount - 1] == ' ') {
  443.         rcount--;
  444.     }
  445.  
  446.     /* Terminate the replacement text properly. */
  447.     rtext[rcount] = '\0';
  448.  
  449.     TRACEPN("pp_def", printf("rtext: <%s>\n", pr_str(rtext)));
  450.  
  451.     /*
  452.         Enter the symbol, replacement text and number of arguments
  453.         into the macro table.
  454.  
  455.         Note that mst_enter allocates space for copies of name and
  456.         rtext so the fact that they are on the stack doesn't matter.
  457.     */
  458.     mst_enter(&name[0], rtext, nargs);
  459.     skip_pp();
  460.  
  461.     RETURN_VOID("pp_def");
  462. }
  463.  
  464.  
  465. /*
  466.     Internal Routines.
  467. */
  468.  
  469. /*
  470.     Macro expand all arguments in old_argps and leave them in new_atext[].
  471.     Put pointers to them in new_argps[].
  472. */
  473. static void
  474. aexpand(name, nargs, old_argps, new_atext, new_argps)
  475. char *    name;        /* Name of macro whose args are being expanded.    */
  476. int *    nargs;        /* Number of args the macro should have.    */
  477. char *    old_argps[];    /* Pointers to unexpanded tokens for each arg.    */
  478. char     new_atext[];    /* Storage for expanded args.            */
  479. char *    new_argps[];    /* Pointers to expanded args.            */
  480. {
  481.     char    save_ch;
  482.     int    i;
  483.     char *    new;
  484.     int    new_count = 0;
  485.  
  486.     ENTER_TRACE("aexpand", printf("(%s, %d, %p, %p, %p)\n",
  487.         name, *nargs, old_argps, new_atext, new_argps));
  488.  
  489.     /*
  490.         Save the current value of ch!
  491.         rescan will change ch, and it must be restored afterwards.
  492.     */
  493.     save_ch = ch;
  494.  
  495.     /* Rescan all arguments. */
  496.     for (i = 0; i < *nargs; i++) {
  497.         new = &new_atext[new_count];
  498.         new_argps[i] = new;
  499.  
  500.         /* Do a bounded rescan. */
  501.         rescan(    "<NO_NAME>", old_argps[i], new,
  502.             MAX_RTEXT-new_count, TRUE);
  503.  
  504.         new_count += strlen(new)+1;
  505.     }
  506.  
  507.     /* Restore ch */
  508.     ch = save_ch;
  509.  
  510.     RETURN_VOID("aexpand");
  511. }
  512.  
  513. /*
  514.     Parse a list of actual arguments into atext[] and put pointers to
  515.     the arguments in argps[].  
  516.     Set *nargs to -1 on error.
  517.     The size of atext[] is MAX_ATEXT and the size of argps is MAX_ARGPS.
  518. */
  519. static int call_start;        /* Line where arguments started.    */
  520.  
  521. static void
  522. aparse(name, nargs, atext, argps)
  523. char *    name;        /* Macro whose arguments are being parsed.       */
  524. int *    nargs;        /* Pointer to arg count.               */
  525. char     atext[];    /* Buffer into which UNEXPANDED arguments are put. */
  526. char *    argps[];    /* Array of pointers to arguments.           */
  527. {
  528.     int    arg_c = 0;    /* Number of arguments parsed so far.    */
  529.     int    length = 0;    /* Total length of arguemnts parsed.    */
  530.     char    msg [100];
  531.     char    line [10];
  532.     bool    flag;
  533.  
  534.     if (*nargs == -1) {
  535.         return;
  536.     }
  537.  
  538.     ENTER_TRACE("aparse", printf("(%s, %d, %p, %p)\n",
  539.         name, *nargs, atext, argps));
  540.  
  541.     if (ch != '(') {
  542.         syserr("aparse: Can't happen");
  543.     }
  544.     else {
  545.         sysnext();
  546.     }
  547.  
  548.     /* Save starting line number of the macro call. */
  549.     call_start = t_line;
  550.  
  551.     if (ch == ')') {
  552.         sysnext();
  553.         goto check;
  554.     }
  555.  
  556.     for(;;) {
  557.         /* Set pointer to the start of the argument. */
  558.         argps[arg_c] = &atext[length];
  559.  
  560.         /* Parse the argument. */
  561.         flag = a1parse(name, &atext[length], MAX_ATEXT-length);
  562.         if (flag || ch == END_FILE) {
  563.             *nargs = -1;
  564.             RETURN_VOID("aparse");
  565.         }
  566.         else {
  567.             length += strlen(argps[arg_c])+1;
  568.             arg_c++;
  569.         }
  570.  
  571.         if (ch == ')') {
  572.             sysnext();
  573.             break;
  574.         }
  575.         else if (ch == ',') {
  576.             sysnext();
  577.         }
  578.         else {
  579.             /* Error detected in a1parse. */
  580.             break;
  581.         }
  582.     }
  583.  
  584. check:
  585.     if (arg_c != *nargs) {
  586.         if (call_start != t_line) {
  587.             strcpy(msg, "Call to macro ");
  588.             strcat(msg, name);
  589.             strcat(msg, " starting at line ");
  590.             conv2s(call_start, line);
  591.             strcat(msg, line);
  592.         }
  593.         else {
  594.             strcpy(msg, "Call to macro ");
  595.             strcat(msg, name);
  596.         }
  597.         strcat(msg, " requires ");
  598.         conv2s(*nargs, line);
  599.         strcat(msg, line);
  600.         strcat(msg, " arguments");
  601.         error(msg);
  602.         *nargs = -1;
  603.     }
  604.     TRACEP("aparse",
  605.         {int i;
  606.         if (*nargs != 0) printf("\n");
  607.         for(i = 0; i < *nargs; i++) {
  608.             printf("arg[%d]: @%p=%p: <%s>\n",
  609.                 i, &argps[i], argps[i], pr_str(argps[i]));
  610.         }});
  611.     RETURN_VOID("aparse");
  612. }
  613.  
  614. /*
  615.     Parse one actual argument into out[].
  616.     The size of out is max_length.
  617.     Return TRUE if an error was seen.
  618.  
  619.     Formal arguments are simply identifiers, which are handled by t_id(),
  620.     but actual arguments are LISTS of tokens separated by commas.  As an
  621.     added twist, commas inside single or double quotes, or commas which are
  622.     "protected" by additional parentheses do NOT separate actual args.
  623.     Thus, each of the following calls have to M have ONE actual argument:
  624.  
  625.         M(a)
  626.         M(a * N(c,b))
  627.         M(',')
  628.         M("a,b")
  629.         M((a,b))
  630.         M((a,")",b))
  631.  
  632.     This routine changes all comments and white space to a single blank.
  633. */
  634. static bool
  635. a1parse(name, out, max_out)
  636. char *    name;            /* Name of the macro being expanded.    */
  637. char *    out;            /* Output buffer.            */
  638. int    max_out;        /* Size of out[].            */
  639. {
  640.      int count;    /* Number of characters in out[].    */
  641.     int plevel;    /* Parenthesis level.            */
  642.  
  643.     char buffer [100];    /* Buffer for messages.        */
  644.     char linebuf [10];    /* Buffer for messages.        */
  645.  
  646.     ENTER_TRACE("a1parse", printf("(%s, %p, %d)\n",
  647.         name, out, max_out));
  648.  
  649.     /* No parens have been seen yet. */
  650.     plevel = 0;
  651.  
  652.     for(count = 0;;) {
  653.  
  654.         /* Make sure there is room for one more. */
  655.         if (count >= max_out) {
  656.             goto toolong;
  657.         }
  658.  
  659.         switch (ch) {
  660.  
  661.         case END_FILE:
  662.             goto runon;
  663.  
  664.         case '\n':
  665.             sysnext();
  666.             bump_line();
  667.             if (count > 0 && out[count-1] != ' ') {
  668.                 out[count++] = ' ';
  669.             }
  670.             continue;
  671.  
  672.         /* Convert a sequence of white space to one blank. */
  673.         case ' ':
  674.         case '\t':
  675.             sysnext();
  676.             if (count > 0 && out[count-1] != ' ') {
  677.                 out[count++] = ' ';
  678.             }
  679.             continue;
  680.  
  681. #ifdef BACKSLASH_NEWLINE
  682.         case '\\':
  683.             sysnext();
  684.             if (ch == '\n') {
  685.                 sysnext();
  686.                 if (count > 0 && out[count-1] != ' ') {
  687.                     out[count++] = ' ';
  688.                 }
  689.                 bump_line();
  690.             }
  691.             else {
  692.                 out[count++] = '\\';
  693.             }
  694.             continue;
  695. #endif
  696.  
  697.         case ',':
  698.             if (plevel == 0) {
  699.                 goto end_arg;
  700.             }
  701.             else {
  702.                 out[count++] = ch;
  703.                 sysnext();
  704.             }
  705.             continue;
  706.                 
  707.         case ')':
  708.  
  709.             if (plevel == 0) {
  710.                 goto end_arg;
  711.             }
  712.             else {
  713.                 plevel--;
  714.                 out[count++] = ch;
  715.                 sysnext();
  716.                 continue;
  717.             }
  718.  
  719.         case '(':
  720.             plevel++;
  721.             out[count++] = ch;
  722.             sysnext();
  723.             continue;
  724.  
  725.         case '"':
  726.         case '\'':
  727.             t_string(&out[count], max_out-count, TRUE);
  728.             count += t_length;
  729.             if (count >= max_out) {
  730.                 goto toolong;
  731.             }
  732.             continue;
  733.  
  734.         case '/':
  735.             sysnext();
  736.             if (ch == '*') {
  737.                 sysnext();
  738.                 skip_comment();
  739.  
  740.                 /* Change a comment into one blank. */
  741.                 if (count > 0 && out[count] != ' ') {
  742.                     out[count++] = ' ';
  743.                 }
  744.             }
  745.             else {
  746.                 out[count++] = '/';
  747.             }
  748.             continue;
  749.  
  750.         default:
  751.             if (isid1(ch)) {
  752.                 t_id(&out[count], max_out-count);
  753.                 count += t_length;
  754.                 if (count >= max_out) {
  755.                     goto toolong;
  756.                 }
  757.             }
  758.             else {
  759.                 out[count++] = ch;
  760.                 sysnext();
  761.             }
  762.         }
  763.     }
  764.  
  765. end_arg:
  766.     /* Finish off the argument. */
  767.     out[count] = '\0';
  768.     TRACEP("a1parse", printf("out: <%s>\n", pr_str(out)));
  769.     RETURN_BOOL("a1parse", FALSE);
  770.  
  771. runon:
  772.     if (call_start != t_line) {
  773.         conv2s(call_start, linebuf);
  774.         str_cpy(buffer, "Runon macro call at line ");
  775.         str_cat(buffer, linebuf);
  776.     }
  777.     else {
  778.         str_cpy(buffer, "Runon macro call");
  779.     }
  780.     str_cat(buffer, "--last arg set to null");
  781.     error(buffer);
  782.     RETURN_BOOL("a1parse", TRUE);
  783.  
  784. toolong:
  785.     conv2s(call_start, linebuf);
  786.     str_cpy(buffer, "Macro arg starting at line ");
  787.     str_cat(buffer, linebuf);
  788.     str_cat(buffer, " is too long");
  789.     fatal(buffer);
  790.     RETURN_BOOL("a1parse", TRUE);
  791. }
  792.  
  793. /*
  794.     Search an array of formal arguments for a match.
  795. */
  796. static int
  797. arg_search(name, nargs, argps)
  798. char *    name;    /* Name to search for.    */
  799. int    nargs;    /* Number of args.    */
  800. char **    argps;    /* Array of args.    */
  801. {
  802.     int i;
  803.  
  804.     /* See if the id is a formal arg. */
  805.     for (i = 0; i < nargs ; i++) {
  806.         if (str_eq(name, argps[i])) {
  807.             return i;
  808.         }
  809.     }
  810.     return -1;
  811. }
  812.  
  813. /*
  814.     Disable the expansion of one name.
  815. */
  816. static void
  817. disable_name(name)
  818. char *name;
  819. {
  820.     TRACEP("disable_name", printf("(%s)\n", name));
  821.  
  822.     if (x_count < MAX_MDEPTH) {
  823.         x_stack [x_count++] = name;
  824.     }
  825. }
  826.  
  827. /*
  828.     Enable the expansion of the last disabled name.
  829. */
  830. static void
  831. enable_name()
  832. {
  833.     TICK("enable_name");
  834.  
  835.     if (x_count == 0) {
  836.         syserr("enable_name: Can't happen");
  837.     }
  838.     x_count--;
  839. }
  840.  
  841. /*
  842.     Fully expand the macro described by name, nargs, rtext.
  843. */
  844. static void
  845. expand(name, nargs, rtext, out, max_out)
  846. char *    name;        /* Name of macro expanded.    */
  847. int    nargs;        /* Required number of args.    */
  848. char *    rtext;        /* Replacement text for name.    */
  849. char *    out;        /* Output buffer.        */
  850. int    max_out;    /* Size of out[].        */
  851. {
  852.  
  853. #ifdef OLD_WAY
  854.     char    old_atext[MAX_ATEXT];    /* Buffer for actual arguments.       */
  855.     char    new_atext[MAX_ATEXT];    /* Buffer for expansion of args.   */    
  856. #else
  857.     char *    old_atext;    /* Buffer for actual arguments.       */
  858.     char *    new_atext;    /* Buffer for expansion of args.   */    
  859. #endif
  860.  
  861.     char *    old_argps[MAX_NARGS];    /* Pointers into old_atext[].       */
  862.     char *    new_argps[MAX_NARGS];    /* Pointers into new_atext[].       */
  863.  
  864.     ENTER_TRACE("expand", printf("(%s, %d, <%s>, %p, %d) ch: %s\n",
  865.         name, nargs, pr_str(rtext), out, max_out, pr_ch(ch)));
  866.  
  867.     /*
  868.         6/27/89
  869.         Allocate memory here so we don't crash the stack.
  870.     */
  871.     old_atext = m_alloc(MAX_ATEXT);
  872.     new_atext = m_alloc(MAX_ATEXT);
  873.  
  874.     /* Parse all arguments to the macro. */
  875.     aparse(name, &nargs, old_atext, old_argps);
  876.  
  877.     /* Fully macro expand all arguments. */
  878.     aexpand(name, &nargs, &old_argps[0], &new_atext[0], &new_argps[0]);
  879.  
  880.     /* Substitute all expanded arguments into rtext[], giving out[]. */
  881.     substitute(name, nargs, rtext, &new_argps[0], &out[0], max_out);
  882.  
  883.     /* Free all memory. */
  884.     m_free(old_atext);
  885.     m_free(new_atext);
  886.  
  887.     RETURN_VOID("expand");
  888. }
  889.  
  890. /*
  891.     Return TRUE if macro expansion of name has been disabled.
  892. */
  893. static bool
  894. is_disabled(name)
  895. char *name;
  896. {
  897.     int i;
  898.  
  899.     ENTER_TRACE("is_disabled", printf("(%s), x_count: %d\n",
  900.         name, x_count));
  901.  
  902.     for (i = x_count-1; i >= 0; i--) {
  903.         TRACEP("is_disabled_v", printf("compare: <%s> <%s>\n",
  904.             name, x_stack[i]));
  905.         if (str_eq(name, x_stack[i])) {
  906.             RETURN_BOOL("is_disabled", TRUE);
  907.         }
  908.     }
  909.     RETURN_BOOL("is_disabled", FALSE);
  910. }
  911.  
  912. /*
  913.     This is it: the guts of macro expansion.
  914.     Fully rescan in[] to out[], looking for macro calls.
  915.  
  916.     A key distinction:  bounded versus unbounded rescanning.
  917.  
  918.     The CALLER of rescan must save and restore ch as appropriate.
  919.     (Note that rescan is called recursively from within, so it must
  920.     save ch in that case just as any other caller must.
  921.  
  922.     Note that rescan will completely process rescan buffer in all cases.
  923.     For unbounded rescans, the previous rescan buffer is appended to in[].
  924.     For bounded rescans, the previous rescan buffer is kept separate.
  925. */
  926. static void
  927. rescan(name, in, out, max_out, bounded_flag)
  928. char *    name;        /* Name of macro being rescanned.    */
  929. char *    in;        /* Text to be rescanned.        */
  930. char *    out;        /* Output buffer.            */
  931. int    max_out;    /* Size of out[].            */
  932. bool    bounded_flag;    /* TRUE if bounded rescan.        */
  933. {
  934.     int    save_mflag;        /* Saved m_flag.    */
  935.     char *    save_prescan;        /* Saved p_rescan.    */
  936.  
  937. #ifdef OLD_WAY
  938.     char    local_in[MAX_RTEXT];    /* Local rescan buffer.    */
  939.     char    expand_buf[MAX_RTEXT];    /* Expansion buffer.    */
  940.     char    id_buf[MAX_SYMBOL];    /* Saved rescan id.    */
  941. #else
  942.     char *    local_in;    /* Local rescan buffer.    */
  943.     char *    expand_buf;    /* Expansion buffer.    */
  944.     char *    id_buf;        /* Saved rescan id.    */
  945. #endif
  946.  
  947.     int    out_len = 0;        /* Index into out[].    */
  948.     char *    rtext;            /* Ptr to replacement text.    */
  949.     int    nargs;            /* Required number of args.    */
  950.     int    i, level;
  951.  
  952.     ENTER_TRACE("rescan", printf("(%s, <%s>, %p, %d, %s) ch: %s\n",
  953.         name, pr_str(in), out, max_out, pr_bool(bounded_flag),
  954.         pr_ch(ch)));
  955.  
  956.     /*
  957.         6/27/89
  958.         Allocate buffers here so we don't crash the stack.
  959.     */
  960.     local_in   = m_alloc(MAX_RTEXT);
  961.     expand_buf = m_alloc(MAX_RTEXT);
  962.     id_buf     = m_alloc(MAX_SYMBOL);
  963.  
  964.     if (bounded_flag) {
  965.         /* Save old rescan buffer and use in[] as the new buffer. */
  966.         save_mflag   = m_flag;
  967.         save_prescan = p_rescan;
  968.  
  969.         /*
  970.             Indicate the end of the buffer with EORT flag.
  971.             Copy in[] to local_in[] so we have room for it.
  972.         */
  973.         p_rescan = &local_in[0];
  974.         strcpy(&local_in[0], in);
  975.         i = strlen(&local_in[0]);
  976.         local_in[i++] = EORT;
  977.         local_in[i]   = '\0';
  978.     }
  979.     else {
  980.         /*
  981.             The key programming trick in this whole file.
  982.             Set rescan to old rescan appended behind in[].
  983.         */
  984.         if (!m_flag) {
  985.             p_rescan = in;
  986.         }
  987.         else {
  988.             str_cpy(local_in, in);
  989.             str_cat(&local_in[0], p_rescan);
  990.             p_rescan = &local_in[0];
  991.         }
  992.     }
  993.  
  994.     /* Get input from rescan buffer. */
  995.     m_flag = TRUE;
  996.  
  997.     TRACEP("rescan_v", printf("p_rescan: <%s>\n", pr_str(p_rescan)));
  998.  
  999.     /*
  1000.         Note1:    At this point, sysnext() will return the characters
  1001.         that have just been placed in the rescan buffer pointed to by
  1002.         p_rescan.  This means that t_id(), t_string(), etc.  may be
  1003.         used to parse the rescan buffer.
  1004.  
  1005.         Note2:  For bounded rescans, the rescan buffer ends with EORT,
  1006.         so sysnext() will return EORT when the rescan buffer is
  1007.         exhausted.  This means that t_string() and skip_comment() will
  1008.         complain about "unexpected end of input," which is as it
  1009.         should be.
  1010.     */
  1011.  
  1012.     /* Disable further expansions of name for the duration. */
  1013.     disable_name(name);
  1014.  
  1015.     /*
  1016.         Scan the buffer until it is exhausted or a macro call is seen.
  1017.         (The recursive call to rescan() will finish off the buffer.)
  1018.     */
  1019.     out_len = 0;
  1020.     sysnext();
  1021.     while (m_flag) {
  1022.         if (ch == EORT) {
  1023.             break;
  1024.         }
  1025.         else if (ch == EXPAND_OFF) {
  1026.             /* Inhibited identifier. */
  1027.             out[out_len++] = ch;
  1028.             sysnext();
  1029.             t_id(&out[out_len], max_out-out_len);
  1030.             out_len += strlen(&out[out_len]);
  1031.         }
  1032.         else if (ch == '"' || ch == '\'') {
  1033.             /* Handle complete strings. */
  1034.             t_string(&out[out_len], max_out-out_len, TRUE);
  1035.             out_len += t_length;
  1036.         }
  1037.         else if (isid1(ch)) {
  1038.             /* Handle identifiers. */
  1039.             t_id(&id_buf[0], MAX_SYMBOL);
  1040.  
  1041.             /*
  1042.                 Possible inner macro call.
  1043.                 Do not adjust out_len until we know for sure.
  1044.             */
  1045.             if (!mst_lookup(&id_buf[0], &rtext, &nargs)) {
  1046.                 /* Not a macro call. */
  1047.                 str_cpy(&out[out_len], &id_buf[0]);
  1048.                 out_len += t_length;
  1049.                 continue;
  1050.             }
  1051.             else if (is_disabled(&id_buf[0])) {
  1052.                 /*
  1053.                     Disabled macro.
  1054.                     Inhibit ALL further expansion.
  1055.                 */
  1056.                 out[out_len++] = EXPAND_OFF;
  1057.                 str_cpy(&out[out_len], &id_buf[0]);
  1058.                 out_len += t_length;
  1059.                 continue;
  1060.             }
  1061.             else if (nargs > -1) {
  1062.                 skip_ws(TRUE);
  1063.                 /*
  1064.                     Function-like macro name without args
  1065.                     are ok in bounded expansions.
  1066.                 */
  1067.                 if (ch != '(') {
  1068.                     if (ch != EORT && !bounded_flag) {
  1069.                        warn3("Function-like macro ",
  1070.                          &id_buf[0],
  1071.                          " appears without arguments");
  1072.                     }
  1073.                     str_cpy(&out[out_len], &id_buf[0]);
  1074.                     out_len += t_length;
  1075.                     continue;
  1076.                 }
  1077.             }
  1078.  
  1079.             /* Valid macro call. */
  1080.  
  1081.             out[out_len] = '\0';
  1082.  
  1083.             /* Expand the text into expand_buf[]. */
  1084.             expand(    &id_buf[0], nargs, rtext,
  1085.                 &expand_buf[0], MAX_RTEXT);
  1086.  
  1087.             if (m_flag) {
  1088.                 /* Append current ch to expand_buf[]. */
  1089.                 i = strlen(&expand_buf[0]);
  1090.                 expand_buf[i++] = ch;
  1091.                 expand_buf[i] = '\0';
  1092.             }
  1093.             else {
  1094.                 /*
  1095.                     Don't needlessly suck file characters
  1096.                     into the expansion.
  1097.                 */
  1098.                 sys_fpb(ch);
  1099.             }
  1100.  
  1101.             /* Rescan will append the rest of p_rescan. */
  1102.             TRACEP("rescan", printf("recursive call. ch: %s\n",
  1103.                 pr_ch(ch)));
  1104.             rescan(    &id_buf[0], &expand_buf[0],
  1105.                 &out[out_len], MAX_RTEXT-out_len, FALSE);
  1106.  
  1107.             out_len=strlen(&out[0]);
  1108.  
  1109.             /* Use the ch that the recursive call left behind. */
  1110.             continue;
  1111.         }
  1112.         /* Comments might appear here due to concatenation. */
  1113.         else if (ch == '/') {
  1114.             sysnext();
  1115.             if (ch == '*') {
  1116.                 sysnext();
  1117.                 skip_comment();
  1118.             }
  1119.             else {
  1120.                 out[out_len++] = '/';
  1121.             }
  1122.         }
  1123.         else {
  1124.             out[out_len++] = ch;
  1125.             sysnext();
  1126.         }
  1127.     }
  1128.  
  1129.     out[out_len] = '\0';
  1130.     TRACEP("rescan", printf("out: <%s>\n", pr_str(out)));
  1131.  
  1132.     enable_name();
  1133.  
  1134.     /* Restore the b_rescan global. */
  1135.     if (bounded_flag) {
  1136.         /* Restore the old rescan buffer. */
  1137.         m_flag   = save_mflag;
  1138.         p_rescan = save_prescan;
  1139.     }
  1140.     else {
  1141.         /* Everything is complete. */
  1142.         m_flag = FALSE;
  1143.     }
  1144.  
  1145.     /* Free buffers. */
  1146.     m_free(local_in);
  1147.     m_free(expand_buf);
  1148.     m_free(id_buf);
  1149.  
  1150.     RETURN_VOID("rescan");
  1151. }
  1152.  
  1153. /*
  1154.     Substitute expanded arguments from argps[] into in[] leaving out[].
  1155.     6/27/89 bug fix: a check is made to see if out is large enough.
  1156. */
  1157. static void
  1158. substitute(name, nargs, rtext, argps, out, max_out)
  1159. char *    name;    /* The name of the macro being expanded.    */
  1160. int    nargs;    /* Number of args for the macro.        */
  1161. char *    rtext;    /* Replacement text of the macro.        */
  1162. char **    argps;    /* Pointer to fully expanded arguments.        */
  1163. char *    out;    /* Output buffer.                */
  1164. int    max_out; /* Size of output buffer.            */
  1165. {
  1166.     int    limit;
  1167.     int    c, i, count;
  1168.     int    arg_num;
  1169.     char    *p, *p2;
  1170.     char    str_buf[MAX_SYMBOL];
  1171.  
  1172.     ENTER_TRACE("substitute",
  1173.         printf("(%s, %d, <%s>, %p, %p, max_out: %d)\n",
  1174.         name, nargs, pr_str(rtext), argps, out, max_out));
  1175.  
  1176.     /* Make SURE the output buffer is never over-run. */
  1177.     max_out -= 4;
  1178.     if (max_out < 10) {
  1179.         goto run_on;
  1180.     }
  1181.  
  1182.     /*
  1183.         Put the final replacement text into out[].
  1184.         Replace ARG_FLAG n by arg n.
  1185.         Replace POUND_FLAG n by the stringized arg n.
  1186.         Delete CONCAT_FLAG and surrounding whitespace.
  1187.     */
  1188.     limit = strlen(rtext);
  1189.     count = 0;
  1190.     for (i = 0; i < limit;) {
  1191.  
  1192.         /*
  1193.             Check for run-on expansion.
  1194.             This is actually possible.
  1195.         */
  1196.         if (count >= max_out) {
  1197.             goto run_on;
  1198.         }
  1199.  
  1200.         c = rtext[i++];
  1201.         if (c == ARG_FLAG) {
  1202.  
  1203.             /* Copy the argument. */
  1204.             arg_num = rtext[i++] - ARG_OFFSET;
  1205.             if (arg_num >= nargs) {
  1206.                 /* Previous user error. */
  1207.                 out[count++] = ' ';
  1208.                 continue;
  1209.             }
  1210.  
  1211.             /* Bug fix: 6/27/89 */
  1212.             if (count + strlen(argps[arg_num]) >= max_out) {
  1213.                 goto run_on;
  1214.             }
  1215.             str_cpy(out + count, argps [arg_num]);
  1216.             count += strlen(argps[arg_num]);
  1217.         }
  1218.         else if (c == POUND_FLAG) {
  1219.  
  1220.             /* Stringize an actual argument */
  1221.             arg_num = rtext[i++] - ARG_OFFSET;
  1222.             if (arg_num >= nargs) {
  1223.                 /* Previous user error. */
  1224.                 out[count++] = ' ';
  1225.                 continue;
  1226.             }
  1227.             out[count++] = '"';
  1228.             for(p = argps[arg_num]; *p; ) {
  1229.             /* bug fix: 2/15/89 */
  1230.             if (*p == '"' || *p == '\'') {
  1231.                 /* Copy the string. */
  1232.                 p += in_string(p, &str_buf[0], MAX_SYMBOL);
  1233.  
  1234.                 /* Stingize the string. */
  1235.                 for (p2 = &str_buf[0]; *p2; ) {
  1236.                     /* 8/1/89 better here. */
  1237.                     if (count >= max_out-1) {
  1238.                         goto run_on;
  1239.                     }
  1240.                     if (*p2 == '\\' || *p2 == '"') {
  1241.                         out[count++] = '\\';
  1242.                         out[count++] = *p2++;
  1243.                     }
  1244.                     else {
  1245.                         out[count++] = *p2++;
  1246.                     }
  1247.                 }
  1248.             }
  1249.             else {
  1250.                 out[count++] = *p++;
  1251.             }
  1252.             } /* end for */
  1253.             out[count++] = '"';
  1254.         }
  1255.         else if (c == CONCAT_FLAG) {
  1256.  
  1257.             /* Delete preceding whitespace. */
  1258.             while (count-1 >= 0 && out[count-1] == ' ') {
  1259.                 count--;
  1260.             }
  1261.  
  1262.             /* Delete following whitespace. */
  1263.             while (i < limit && rtext[i] == ' ') {
  1264.                 i++;
  1265.             }
  1266.         }
  1267.         else {
  1268.             out[count++] = c;
  1269.         }
  1270.     }
  1271.  
  1272.     /* Append current ch to output. */
  1273.     out[count] = '\0';
  1274.  
  1275.     TRACEP("substitute", printf("out: <%s>\n", pr_str(out)));
  1276.  
  1277.     RETURN_VOID("substitute");
  1278.  
  1279. run_on:
  1280.     strcpy(str_buf, "Macro expansion for ");
  1281.     strcat(str_buf, name);
  1282.     strcat(str_buf, " is too long...\nExpansion set to empty string");
  1283.     error(str_buf);
  1284.  
  1285.     /* Truncate the ENTIRE macro. */
  1286.     out[0] = '\0';
  1287.     RETURN_VOID("substitute");
  1288. }
  1289.