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