home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / tools / make / pdmake / parse.c < prev    next >
C/C++ Source or Header  |  1990-07-06  |  17KB  |  823 lines

  1. /*
  2.  * parse.c
  3.  *
  4.  * 88-10-01 v1.0    created by greg yachuk, placed in the public domain
  5.  * 88-10-06 v1.1    changed prerequisite list handling
  6.  * 88-11-11 v1.2    fixed some bugs and added environment variables
  7.  * 89-07-12 v1.3    stop appending shell commands, and flush output
  8.  * 89-08-01 v1.4 AB    lots of new options and code
  9.  * 89-10-30 v1.5    -f -S -q options, took some changes from v1.4
  10.  * 90-04-18 v1.6    -b -- -W options, emulate <<, non-BSD cleanup
  11.  */
  12. #include <stdio.h>
  13. #include <ctype.h>
  14. #include <string.h>
  15. #ifdef    MSDOS
  16. #include <stdlib.h>
  17. #endif
  18.  
  19. #include "make.h"
  20. #include "tstring.h"
  21. #include "decl.h"
  22.  
  23. /*
  24.  * parse    - read (text) makefile, and parse
  25.  *        - close file before returing
  26.  *
  27.  * lines have the following format:
  28.  *    # with or without preceeding spaces/tabs    (comment line)
  29.  *    <TAB> commands                    (shell line)
  30.  *    name = stuff                    (macro)
  31.  *    name += stuff                    (macro)
  32.  *    targ [targ...] : [pre-req...] [; shell cmd ]    (target line)
  33.  */
  34. parse(fd)
  35. FILE   *fd;
  36. {
  37.     char   *input;
  38.     char   *ip;
  39.     char   *colonp;
  40.     char    schar;
  41.     int     ntargs, npreqs, nshell;
  42.     int     tmax, pmax, smax;
  43.     targptr *targs;
  44.     fileptr *preqs;
  45.     shellptr *shells;
  46.  
  47.     if (fd == NULL)
  48.         return (0);
  49.  
  50.     /* start off with a short list of targets */
  51.     targs = (targptr *) grow_list(NULL, &tmax);
  52.     preqs = (fileptr *) grow_list(NULL, &pmax);
  53.     shells = (shellptr *) grow_list(NULL, &smax);
  54.  
  55.     ntargs = npreqs = nshell = 0;
  56.  
  57.     /* maximize buffering */
  58.     setvbuf(fd, NULL, _IOFBF, 2048);
  59.  
  60.     while ((input = tgets(fd)) != NULL)
  61.     {
  62.         /* punt on comments and blank lines */
  63.         for (ip = input; isspace(*ip); ++ip);
  64.         if (*ip == '#' || *ip == '\0')
  65.             continue;
  66.  
  67.         /* process include files */
  68.         if (!strncmp(ip, "include", 7))
  69.         {
  70.             /* skip spaces AFTER "include" */
  71.             for (ip += 7; isspace(*ip); ++ip);
  72.  
  73.             /* process macros in the filename */
  74.             ip = breakout(ip);
  75.  
  76.             /* parse the makefile */
  77.             if (!parse(fopen(ip, "r")))
  78.                 terror(1, tstrcat("cannot open ", ip));
  79.  
  80.             /* free up the broken-out string */
  81.             tfree(ip);
  82.             continue;    /* get next input line */
  83.         }
  84.  
  85.         /* display the makefile line ? */
  86.         if (opts.display)
  87.             puts(input);
  88.  
  89.         /* get rid of comments and preceeding spaces */
  90.         for (colonp = ip; *colonp && *colonp != '#'; ++colonp)
  91.         {
  92.             if (*colonp == '\'' || *colonp == '"')
  93.                 colonp = tstrspan(colonp);
  94.         }
  95.  
  96.         for (--colonp; colonp >= ip && isspace(*colonp); --colonp);
  97.  
  98.         /* we *know* that some non-space is on this line, from above */
  99.         if (colonp >= ip)
  100.             *++colonp = '\0';
  101.  
  102.         /* see if we have a shell command */
  103.         if (isspace(*input))
  104.         {
  105.             if (ntargs == 0)
  106.                 terror(1, "rules must be after target");
  107.     got_shell:
  108.             if (nshell == smax)
  109.             {
  110.                 shells = (shellptr *)
  111.                     grow_list((char **) shells, &smax);
  112.             }
  113.             shells[nshell++] = add_shell(ip);
  114.             continue;
  115.         }
  116.  
  117.         /* not a shell line, so must be a target or a macro */
  118.         if (ntargs != 0)
  119.         {
  120.             /* link previous preq's and shell's */
  121.             targs[ntargs] = NULL;
  122.             preqs[npreqs] = NULL;
  123.             shells[nshell] = NULL;
  124.             link_targs(targs, preqs, shells);
  125.             ntargs = npreqs = nshell = 0;
  126.         }
  127.  
  128.         /* don't break out symbols until macro is invoked */
  129.         if (add_macro(ip, 0))
  130.             continue;
  131.  
  132.         /* okay, we have a target line; break out macro symbols */
  133.         input = breakout(ip);
  134.  
  135.         /* just look for tokens with standard isspace() separators */
  136.         ip = token(input, NULL, &schar);
  137.         while (ip)
  138.         {
  139.             colonp = strchr(ip, ':');
  140. #ifdef    MSDOS
  141.             /* need to allow c:/bin/make.exe as a target */
  142.             if (colonp && colonp - ip == 1)
  143.                 colonp = strchr(colonp + 1, ':');
  144. #endif
  145.             if (colonp)
  146.             {
  147.                 /* got a separator */
  148.                 *colonp = '\0';
  149.  
  150.                 /* if at front of token, target is done */
  151.                 if (colonp == ip)
  152.                     break;
  153.             }
  154.  
  155.             if (ntargs == tmax)
  156.                 targs = (targptr *) grow_list((char **) targs,
  157.                                   &tmax);
  158.             targs[ntargs] = add_target(ip);
  159.  
  160.             /* make sure we don't save .INIT as our 1st target */
  161.             if (first_targ == NULL && *ip != '.')
  162.                 first_targ = targs[ntargs];
  163.             ++ntargs;
  164.  
  165.             if (colonp)
  166.                 break;
  167.             ip = token(NULL, NULL, &schar);
  168.         }
  169.  
  170.         /* a target line without a colon?  naughty, naughty! */
  171.         if (!colonp)
  172.             terror(-1, "Unexpected end of line seen");
  173.  
  174. /*
  175.  *         taking care of four possible cases:
  176.  *            1)    object : source
  177.  *            2)    object: source
  178.  *            3)    object :source
  179.  *            4)    object:source
  180.  */
  181.  
  182.         if (colonp && *++colonp)
  183.             ip = colonp;
  184.         else
  185.             ip = token(NULL, NULL, &schar);
  186.  
  187.         /* link the pre-req's */
  188.         while (ip)
  189.         {
  190.             if ((colonp = strchr(ip, ';')) != NULL)
  191.             {
  192.                 ip[strlen(ip)] = schar;
  193.                 *colonp = '\0';
  194.             }
  195.  
  196.             if (*ip)
  197.             {
  198.                 if (npreqs == pmax)
  199.                 {
  200.                     preqs = (fileptr *)
  201.                         grow_list((char **) preqs,
  202.                               &pmax);
  203.                 }
  204.  
  205.                 preqs[npreqs++] = add_file(ip);
  206.             }
  207.  
  208.             if (colonp)
  209.             {
  210.                 ip = colonp + 1;
  211.                 goto got_shell;
  212.             }
  213.  
  214.             ip = token(NULL, NULL, &schar);
  215.         }
  216.  
  217.         /* gotta free the line allocated by breakout() */
  218.         tfree(input);
  219.     }
  220.  
  221.     /* link up any dangling dependants */
  222.     if (ntargs != 0)
  223.     {
  224.         targs[ntargs] = NULL;
  225.         preqs[npreqs] = NULL;
  226.         shells[nshell] = NULL;
  227.         link_targs(targs, preqs, shells);
  228.     }
  229.  
  230.     /* clean up our mallocs */
  231.     tfree(targs);
  232.     tfree(preqs);
  233.     tfree(shells);
  234.  
  235.     fclose(fd);
  236.     return (1);
  237. }
  238.  
  239.  
  240. /*
  241.  * link_targs    - force a list of targs to point to same preq's and shell's
  242.  */
  243. link_targs(targs, preqs, shells)
  244. targptr *targs;
  245. fileptr *preqs;
  246. shellptr *shells;
  247. {
  248.     while (targs && *targs)
  249.     {
  250.         /* process some special targets */
  251.         if ((*targs)->tfile->fname[0] == '.')
  252.         {
  253.             if (equal((*targs)->tfile->fname, ".SILENT"))
  254.                 opts.silent = 1;
  255.             else
  256.             if (equal((*targs)->tfile->fname, ".IGNORE"))
  257.                 opts.ignore = 1;
  258.             else
  259.             if (equal((*targs)->tfile->fname, ".SUFFIXES"))
  260.                 /*
  261.                  * set `suffix_targ' to speed up
  262.                  * `default_rule' 
  263.                  */
  264.                 suffix_targ = *targs;
  265.  
  266.             /* special rule has preq's reset */
  267.             /* normally, preq's are merely appended */
  268.             if (*preqs == NULL && (*targs)->tpreq != NULL)
  269.             {
  270.                 tfree((*targs)->tpreq);
  271.                 (*targs)->tpreq = NULL;
  272.             }
  273.  
  274.             /* special rules have their shell commands replaced */
  275.             if ((*targs)->tshell != NULL && *shells != NULL)
  276.             {
  277.                 shellptr *sp;
  278.  
  279.                 for (sp = (*targs)->tshell; *sp; ++sp)
  280.                     tfree(*sp);
  281.                 tfree((*targs)->tshell);
  282.                 (*targs)->tshell = NULL;
  283.             }
  284.         }
  285.  
  286.         /* each target in the list points to the preq's and shell's */
  287.         (*targs)->tpreq = append_preq((*targs)->tpreq, preqs);
  288.  
  289.         /* we cannot expand the list of shell commands */
  290.         if ((*targs)->tshell != NULL && *shells != NULL)
  291.         {
  292.             terror(1, tstrcat("Too many rules defined for target ",
  293.                       (*targs)->tfile->fname));
  294.         }
  295.         (*targs)->tshell = append_shell((*targs)->tshell, shells);
  296.         ++targs;
  297.     }
  298. }
  299.  
  300.  
  301. /* macros must have the format: WORD = more stuff
  302.  *                     WORD= more stuff
  303.  *                WORD =more stuff
  304.  *                WORD=more stuff
  305.  *            or:    WORD += more stuff
  306.  *                WORD +=more stuff
  307.  *
  308.  * it is assumed that there is no leading whitespace in `input'
  309.  */
  310. add_macro(input, scmd)
  311. char   *input;
  312. int     scmd;
  313. {
  314.     char   *eqsign;
  315.     char   *value;
  316.     symptr  symp;
  317.  
  318.     /* gotta have an '=' to be a macro */
  319.     eqsign = strchr(input, '=');
  320.     if (eqsign == NULL)
  321.         return (0);
  322.  
  323.     /* make sure we catch imbedded '='s (e.g. MACRO=STUFF) */
  324.     for (value = input; *value && !isspace(*value); ++value);
  325.     if (value > eqsign)
  326.         value = eqsign;
  327.  
  328.     /* terminate the macro name */
  329.     *value = '\0';
  330.  
  331.     /* find start of value */
  332.     for (value = eqsign + 1; isspace(*value); ++value);
  333.  
  334.     /* look for concat character */
  335.     --eqsign;
  336.  
  337.     if (eqsign < input || (eqsign == input && *eqsign == '+'))
  338.         terror(1, "Badly formed macro");
  339.  
  340.     if (*eqsign == '+')
  341.     {
  342.         /* append to the current macro definition */
  343.         *eqsign = '\0';
  344.         symp = get_symbol(input, scmd);
  345.         if (symp->scmd && !scmd)
  346.             return (1);
  347.         if (symp->slevel < make_level)
  348.             symp = dup_symbol(symp, symp->svalue);
  349.         if (symp->svalue)
  350.         {
  351.             eqsign = tstrcat(symp->svalue, " ");
  352.             value = tstrcat(eqsign, value);
  353.             tfree(eqsign);
  354.             tfree(symp->svalue);
  355.             symp->svalue = value;
  356.             return (1);
  357.         }
  358.     }
  359.  
  360.     add_symbol(input, value, scmd);
  361.     return (1);
  362. }
  363.  
  364.  
  365. /*
  366.  * add_symbol    - add a <name,value> pair to the symbol table
  367.  *        - override existing symbol value
  368.  *        - mark as either command-line macro or not
  369.  */
  370. add_symbol(name, value, scmd)
  371. char   *name;
  372. char   *value;
  373. int     scmd;
  374. {
  375.     symptr  symp;
  376.  
  377.     symp = get_symbol(name, scmd);
  378.     if (symp->scmd & !scmd)
  379.         return;
  380.     if (symp->slevel < make_level)
  381.         symp = dup_symbol(symp, NULL);    /* don't dup the value */
  382.     if (symp->svalue)
  383.         tfree(symp->svalue);
  384.     symp->svalue = tstrcpy(value);
  385.     symp->scmd = scmd;
  386. }
  387.  
  388.  
  389. /*
  390.  * get_symbol    - find a symbol in the symbol table
  391.  *        - if non-extant, create <name,NULL>
  392.  *        - return created or found symbol node
  393.  */
  394. symptr  get_symbol(name, scmd)
  395. char   *name;
  396. int     scmd;
  397. {
  398.     symptr  symp;
  399.     t_mask  mask;
  400.     char   *np;
  401.  
  402.     /* use `mask' to screen out most string comparisons */
  403.     mask = 0;
  404.     np = name;
  405.     while (*np)
  406.         mask += *np++;
  407.  
  408.     /* linear search through symbol list */
  409.     for (symp = symbol_list; symp != NULL; symp = symp->snext)
  410.     {
  411.         if (mask != symp->smask)
  412.             continue;
  413.  
  414.         if (equal(name, symp->sname))
  415.             return (symp);
  416.     }
  417.  
  418.     symp = tnew(symnode);    /* allocate symbol node */
  419.     symp->smask = mask;    /* record mask for later */
  420.     symp->sname = tstrcpy(name);    /* allocate string and copy name */
  421.     symp->scmd = scmd;    /* command line macro? */
  422.     symp->slevel = make_level;    /* current new_make() level */
  423.  
  424.     /* get the value from the environment, if it is there */
  425.  
  426.     if ((symp->svalue = getenv(name)) != NULL)
  427.     {
  428.         symp->svalue = tstrcpy(symp->svalue);
  429.  
  430.         /*
  431.          * if `-e', let command line macros override, but not macro
  432.          * assignments in the makefile. 
  433.          */
  434.         if (opts.envirn)
  435.             symp->scmd = 1;
  436.     }
  437.  
  438.     symp->snext = symbol_list;    /* link to head of symbol list */
  439.     symbol_list = symp;
  440.  
  441.     return (symp);
  442. }
  443.  
  444.  
  445. /*
  446.  * dup_sym    - duplicate a symbol node, but at current new_make() level
  447.  */
  448. symptr  dup_symbol(symp, value)
  449. symptr  symp;
  450. char   *value;
  451. {
  452.     symptr  nsp;
  453.  
  454.     nsp = tnew(symnode);    /* allocate symbol node */
  455.     nsp->smask = symp->smask;    /* record mask for later */
  456.     nsp->sname = tstrcpy(symp->sname);    /* allocate string and copy
  457.                          * name */
  458.     nsp->svalue = (value == NULL) ? NULL : tstrcpy(value);
  459.     nsp->scmd = symp->scmd;    /* command line macro? */
  460.     nsp->slevel = make_level;    /* current new_make() level */
  461.  
  462.     nsp->snext = symbol_list;    /* link to head of symbol list */
  463.     symbol_list = nsp;
  464.  
  465.     return (nsp);
  466. }
  467.  
  468.  
  469. /*
  470.  * add_target    - return extant target node, or create new one
  471.  */
  472. targptr add_target(name)
  473. char   *name;
  474. {
  475.     t_mask  mask;
  476.     targptr targp;
  477.     fileptr filep;
  478.  
  479.     /* each target must have a file node */
  480.     filep = add_file(name);
  481.  
  482.     /* see if target already exists */
  483.     targp = hash_target(name, &mask);
  484.     if (targp)
  485.         return (targp);
  486.  
  487.     /* oh well, gotta create one */
  488.     targp = tnew(targnode);    /* allocate a target node */
  489.     targp->tmask = mask;    /* save mask for later */
  490.     targp->tfile = filep;    /* save pointer to file node */
  491.     targp->tpreq = NULL;    /* no pre-req's yet */
  492.     targp->tshell = NULL;    /* no shell lines yet */
  493.  
  494.     targp->tnext = target_list;    /* link to front of target list */
  495.     target_list = targp;
  496.  
  497.     return (targp);
  498. }
  499.  
  500.  
  501. /*
  502.  * hash_target    - look up target (by name) in target list
  503.  *        - return target node or NULL
  504.  *        - if requested, also return the mask
  505.  */
  506. targptr hash_target(name, maskp)
  507. char   *name;
  508. t_mask *maskp;
  509. {
  510.     targptr targp;
  511.     t_mask  mask;
  512.     char   *np;
  513.  
  514.     /* use `mask' to screen out most string comparisons */
  515.     mask = 0;
  516.     np = name;
  517.     while (*np)
  518.         mask += *np++;
  519.  
  520.     /* see if we gotta return it */
  521.     if (maskp != NULL)
  522.         *maskp = mask;
  523.  
  524.     /* linear search through target list */
  525.     for (targp = target_list; targp != NULL; targp = targp->tnext)
  526.     {
  527.         if (mask != targp->tmask)
  528.             continue;
  529.  
  530.         /* target name is ONLY stored in the file node */
  531.         if (equal(name, targp->tfile->fname))
  532.             return (targp);
  533.     }
  534.  
  535.     /* nope, no target here */
  536.     return (NULL);
  537. }
  538.  
  539.  
  540. /*
  541.  * add_file    - return a found or created file node
  542.  */
  543. fileptr add_file(name)
  544. char   *name;
  545. {
  546.     t_mask  mask;
  547.     fileptr filep;
  548.  
  549.     /* see if file node already exists */
  550.     filep = hash_file(name, &mask);
  551.     if (filep)
  552.         return (filep);
  553.  
  554.     filep = tnew(filenode);    /* allocate new file node */
  555.     filep->fmask = mask;    /* save mask for later */
  556.     filep->fname = tstrcpy(name);    /* allocate string and copy name */
  557.     filep->ftime = MAXNEGTIME;    /* init MODIFY time to long time ago */
  558.  
  559.     filep->fnext = file_list;    /* link to head of file list */
  560.     file_list = filep;
  561.  
  562.     return (filep);
  563. }
  564.  
  565.  
  566. /*
  567.  * hash_file    - look up file (by name) in file list
  568.  *        - return file node or NULL
  569.  *        - if requested, also return the mask
  570.  */
  571. fileptr hash_file(name, maskp)
  572. char   *name;
  573. t_mask *maskp;
  574. {
  575.     fileptr filep;
  576.     t_mask  mask;
  577.     char   *np;
  578.  
  579.     /* use `mask' to screen out most string comparisons */
  580.     mask = 0;
  581.     np = name;
  582.     while (*np)
  583.         mask += *np++;
  584.  
  585.     /* see if we gotta return it */
  586.     if (maskp != NULL)
  587.         *maskp = mask;
  588.  
  589.     /* linear search through file list */
  590.     for (filep = file_list; filep != NULL; filep = filep->fnext)
  591.     {
  592.         if (filep->fmask != mask)
  593.             continue;
  594.  
  595.         if (equal(filep->fname, name))
  596.             return (filep);
  597.     }
  598.  
  599.     /* nope, no file here */
  600.     return (NULL);
  601. }
  602.  
  603.  
  604. /*
  605.  * append_node    - add a node to the end of an array of nodes
  606.  */
  607. char  **append_node(node, adds, size)
  608. char  **node;
  609. char  **adds;
  610. int     size;
  611. {
  612.     int     addlen, len;
  613.  
  614.     for (addlen = 0; adds[addlen] != NULL; ++addlen);
  615.     if (addlen++ == 0)
  616.         return (node);
  617.  
  618.     len = 0;
  619.  
  620.     if (node != NULL)
  621.     {
  622.         for (; node[len] != NULL; ++len);
  623.         node = (char **) trealloc((char *) node, (len + addlen) * size);
  624.     }
  625.     else
  626.         node = (char **) talloc(addlen * size);
  627.  
  628.     memcpy(node + len, adds, addlen * size);
  629.     return (node);
  630. }
  631.  
  632. /*
  633.  * add_shell    - create a new shell node, and add to end of given list
  634.  */
  635. shellptr add_shell(input)
  636. char   *input;
  637. {
  638.     shellptr snode;
  639.  
  640.     snode = tnew(shellnode);/* allocate a new shell node */
  641.     snode->s_shell = snode->s_ignore = snode->s_silent = 0;
  642.  
  643.     for (; isspace(*input); ++input);    /* skip over leading spaces */
  644.     for (;; ++input)
  645.     {
  646.         if (*input == '+')
  647.             snode->s_shell = 1;    /* must use command.com */
  648.         else
  649.         if (*input == '-')
  650.             snode->s_ignore = 1;    /* ignore return value */
  651.         else
  652.         if (*input == '@')
  653.             snode->s_silent = 1;    /* don't echo command */
  654.         else
  655.             break;
  656.     }
  657.  
  658.     snode->scmd = tstrcpy(input);    /* allocate string and copy command */
  659.  
  660.     snode->slink = shell_list;    /* attach to global list */
  661.     shell_list = snode;
  662.  
  663.     return (snode);
  664. }
  665.  
  666.  
  667. /*
  668.  * breakout    - replace macro names with values
  669.  *        - apply recursively
  670.  * note: allocates (and returns) a string which must be freed
  671.  */
  672. char   *breakout(input)
  673. char   *input;
  674. {
  675.     char   *dest, *dend;
  676.     char   *dp;
  677.     int     dlen;
  678.     int     tlen;
  679.     int     state;
  680.     char    symname[100];
  681.     char   *sp;
  682.     symptr  symp;
  683.     int     slen;
  684.     char    endch;
  685.  
  686.     /* allocate a string twice as long as input string */
  687.  
  688.     dlen = strlen(input) * 2;
  689.     dest = dp = talloc(dlen);
  690.     dend = dest + dlen;
  691.  
  692. /*
  693.  *     state machine with 4 states
  694.  *        0)    normal text    -- just copy
  695.  *        1)    starting macro    -- define end char (e.g. ')', '}')
  696.  *        2)    macro name    -- copy to a buffer
  697.  *        3)    end of macro    -- look up value, and copy
  698.  */
  699.     state = 0;
  700.  
  701.     while (*input || state == 3)
  702.     {
  703.         /* if we don't have enough room, double size of string */
  704.         if (dp == dend)
  705.         {
  706.             dlen *= 2;
  707.             tlen = dp - dest;
  708.             dest = trealloc(dest, dlen);
  709.             dp = dest + tlen;
  710.             dend = dest + dlen;
  711.         }
  712.  
  713.         switch (state)
  714.         {
  715.         case 0:
  716.             if (*input == '$')
  717.                 state = 1;    /* found a macro */
  718.             else
  719.                 *dp++ = *input++;
  720.             break;
  721.  
  722.         case 1:
  723.             state = 2;    /* only in this state for 1 char */
  724.             sp = symname;
  725.             switch (*++input)
  726.             {
  727.             case '$':
  728.                 *dp++ = '$';
  729.                 state = 0;
  730.                 break;
  731.             case '(':
  732.                 endch = ')';
  733.                 break;
  734.             case '{':
  735.                 endch = '}';
  736.                 break;
  737.             default:
  738.                 /* single char; go to state 3 immediately */
  739.                 *sp++ = *input;
  740.                 state = 3;
  741.                 break;
  742.             }
  743.             ++input;/* skip bracket (or character) */
  744.             break;
  745.  
  746.         case 2:
  747.             if (*input == endch)
  748.                 state = 3;
  749.             else
  750.                 *sp++ = *input;
  751.  
  752.             if ((sp - symname) >= (sizeof symname / sizeof symname[0]))
  753.             {
  754.                 sp[-1] = '\0';
  755.                 terror(1,
  756.                 tstrcat("Macro too long (limit 100 chars): ",
  757.                     symname));
  758.             }
  759.  
  760.             ++input;/* make sure we skip end char */
  761.             break;
  762.  
  763.         case 3:
  764.             *sp = '\0';
  765.             symp = get_symbol(symname, 0);
  766.             sp = symp->svalue;
  767.             slen = -1;
  768.             while (sp && *sp)
  769.             {
  770.                 /*
  771.                  * if value has a macro in it, we must
  772.                  * process recursively 
  773.                  */
  774.                 if (*sp == '$')
  775.                 {
  776.                     sp = breakout(symp->svalue);
  777.                     /* now guaranteed not to have a '$' */
  778.                     slen = strlen(sp);
  779.                     break;
  780.                 }
  781.                 ++sp;
  782.             }
  783.  
  784.             if (slen == -1)
  785.             {
  786.                 /* value did NOT have a macro */
  787.                 slen = (sp - symp->svalue);
  788.                 sp = symp->svalue;
  789.             }
  790.  
  791.             /* if we have not enough room, expand */
  792.             if (slen >= (dend - dp))
  793.             {
  794.                 /* use slen to make sure that we can fit */
  795.                 dlen = dlen * 2 + slen;
  796.                 tlen = dp - dest;
  797.                 dest = trealloc(dest, dlen);
  798.                 dp = dest + tlen;
  799.                 dend = dest + dlen;
  800.             }
  801.  
  802.             /* if length is zero, don't bother to copy */
  803.             if (slen)
  804.             {
  805.                 strcpy(dp, sp);
  806.                 dp += slen;
  807.             }
  808.  
  809.             if (sp != symp->svalue)
  810.                 tfree(sp);    /* must've called `breakout' */
  811.  
  812.             state = 0;    /* and we are back to text */
  813.             break;
  814.         }
  815.     }
  816.  
  817.     if (state != 0)
  818.         terror(1, tstrcat("Improper macro.\n", dest));
  819.  
  820.     *dp = '\0';        /* terminate the string */
  821.     return (dest);        /* and return it */
  822. }
  823.