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