home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / dmake40.zip / rulparse.c < prev    next >
C/C++ Source or Header  |  1994-10-23  |  45KB  |  1,479 lines

  1. /* RCS      -- $Header: /u5/dvadura/src/public/dmake/src/RCS/rulparse.c,v 1.1 1994/10/06 17:42:00 dvadura Exp $
  2. -- SYNOPSIS -- perform semantic analysis on input
  3. -- 
  4. -- DESCRIPTION
  5. --    This code performs semantic analysis on the input, and builds
  6. --    the complex internal datastructure that is used to represent
  7. --    the user makefile.
  8. -- 
  9. -- AUTHOR
  10. --      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
  11. --      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
  12. --
  13. -- COPYRIGHT
  14. --      Copyright (c) 1992,1994 by Dennis Vadura.  All rights reserved.
  15. -- 
  16. --      This program is free software; you can redistribute it and/or
  17. --      modify it under the terms of the GNU General Public License
  18. --      (version 1), as published by the Free Software Foundation, and
  19. --      found in the file 'LICENSE' included with this distribution.
  20. -- 
  21. --      This program is distributed in the hope that it will be useful,
  22. --      but WITHOUT ANY WARRANTY; without even the implied warrant of
  23. --      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24. --      GNU General Public License for more details.
  25. -- 
  26. --      You should have received a copy of the GNU General Public License
  27. --      along with this program;  if not, write to the Free Software
  28. --      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  29. --
  30. -- LOG
  31. --     $Log: rulparse.c,v $
  32.  * Revision 1.1  1994/10/06  17:42:00  dvadura
  33.  * dmake Release Version 4.0, Initial revision
  34.  *
  35. */
  36.  
  37. #include "extern.h"
  38.  
  39. /* prototypes for local functions */
  40. static  void    _add_global_prereq ANSI((CELLPTR));
  41. static  int     _add_root ANSI((CELLPTR));
  42. static    CELLPTR    _build_graph ANSI((int, CELLPTR, CELLPTR));
  43. static    char*    _build_meta ANSI((char*));
  44. static    int    _do_magic ANSI((int, char*, CELLPTR, CELLPTR, t_attr, char*));
  45. static    void    _do_special ANSI((int, int, t_attr,char*,CELLPTR,CELLPTR,int*));
  46. static    int    _do_targets ANSI((int, t_attr, char*, CELLPTR, CELLPTR));
  47. static    t_attr    _is_attribute ANSI((char*));
  48. static    int    _is_special ANSI((char*));
  49. static    char*    _is_magic ANSI((char*));
  50. static    int    _is_percent ANSI((char*));
  51. static  CELLPTR _make_multi ANSI((CELLPTR));
  52. static  CELLPTR _replace_cell ANSI((CELLPTR,CELLPTR,CELLPTR));
  53. static    void    _set_attributes ANSI((t_attr, char*, CELLPTR ));
  54. static    void    _stick_at_head ANSI((CELLPTR, CELLPTR));
  55. static    void    _set_global_attr ANSI((t_attr));
  56.  
  57.  
  58. /* static variables that must persist across invocation of Parse_rule_def */
  59. static CELLPTR    _sv_targets = NIL(CELL);
  60. static STRINGPTR  _sv_rules   = NIL(STRING);
  61. static STRINGPTR  _sv_crule   = NIL(STRING);
  62. static CELLPTR    _sv_edgel   = NIL(CELL);
  63. static LINKPTR    _sv_glb_prq = NIL(LINK);
  64. static int      _sp_target  = FALSE;
  65. static t_attr     _sv_attr;
  66. static int        _sv_flag;
  67. static int      _sv_op;
  68. static char      *_sv_setdir;
  69. static char      _sv_globprq_only = 0;
  70.  
  71. /* Define for global attribute mask */
  72. #define A_GLOB     (A_PRECIOUS | A_SILENT | A_IGNORE | A_EPILOG | A_SWAP |\
  73.           A_SHELL | A_PROLOG | A_NOINFER | A_SEQ | A_MKSARGS )
  74.  
  75.  
  76. PUBLIC int
  77. Parse_rule_def( state )/*
  78. =========================
  79.    Parse the rule definition contained in Buffer, and modify the state 
  80.    if appropriate.  The function returns 0, if the definition is found to
  81.    be an illegal rule definition, and it returns 1 if it is a rule definition.
  82.    */
  83. int *state;
  84. {
  85.    TKSTR     input;        /* input string struct for token search      */
  86.    CELLPTR    targets;    /* list of targets if any          */
  87.    CELLPTR    prereq;        /* list of prereq if any          */
  88.    CELLPTR    prereqtail;    /* tail of prerequisite list          */
  89.    CELLPTR    cp;        /* temporary cell pointer for list making */
  90.    char     *result;    /* temporary storage for result            */
  91.    char        *tok;        /* temporary pointer for tokens          */
  92.    char         *set_dir;       /* value of setdir attribute              */
  93.    char        *brk;        /* break char list for Get_token      */
  94.    char         *firstrcp;      /* first recipe line, from ; in rule line */
  95.    t_attr       attr;           /* sum of attribute flags for current tgts*/
  96.    t_attr    at;        /* temp place to keep an attribute code      */
  97.    int        op;        /* rule operator              */
  98.    int        special;    /* indicate special targets in rule      */
  99.    int        percent;    /* indicate percent rule target          */
  100.    int        mixed_glob_prq; /* indicate mixed %-rule prereq possible  */
  101.  
  102.    DB_ENTER( "Parse_rule_def" );
  103.  
  104.    op          = 0;
  105.    attr       = 0;
  106.    special    = 0;
  107.    percent    = 0;
  108.    set_dir    = NIL( char );
  109.    targets    = NIL(CELL);
  110.    prereq     = NIL(CELL);
  111.    prereqtail = NIL(CELL);
  112.    mixed_glob_prq = 0;
  113.  
  114.    /* Check to see if the line is of the form:
  115.     *    targets : prerequisites; first recipe line
  116.     * If so remember the first_recipe part of the line. */
  117.  
  118.    firstrcp = strchr( Buffer, ';' );
  119.    if( firstrcp != NIL( char ) ) {
  120.       *firstrcp++ = 0;
  121.       firstrcp = DmStrSpn( firstrcp, " \t" );
  122.    }
  123.  
  124.    result = Expand( Buffer );
  125.    for( brk=strchr(result,'\\'); brk != NIL(char); brk=strchr(brk,'\\') )
  126.       if( brk[1] == '\n' )
  127.      *brk = ' ';
  128.       else
  129.          brk++;
  130.  
  131.    DB_PRINT( "par", ("Scanning: [%s]", result) );
  132.  
  133.    SET_TOKEN( &input, result );
  134.    brk = ":-^!|";
  135.    Def_targets = TRUE;
  136.    
  137.    /* Scan the input rule line collecting targets, the operator, and any
  138.     * prerequisites.  Stop when we run out of targets and prerequisites. */
  139.  
  140.    while( *(tok = Get_token( &input, brk, TRUE )) != '\0' )
  141.       if( !op ) {
  142.      /* we are scanning targets and attributes
  143.       * check to see if token is an operator.  */
  144.  
  145.      op = Rule_op( tok );
  146.  
  147.      if( !op ) {
  148.         /* define a new cell, or get old cell  */
  149.         cp = Def_cell( tok );
  150.         DB_PRINT( "par", ("tg_cell [%s]", tok) );
  151.         
  152.         if( (at = _is_attribute(tok)) != 0 ) {
  153.            /* Logically OR the attributes specified into one main
  154.             * ATTRIBUTE mask. */
  155.  
  156.            if( at == A_SETDIR )
  157.               if( set_dir != NIL( char ) )
  158.                  Warning( "Multiple .SETDIR attribute ignored" );
  159.               else
  160.                  set_dir = DmStrDup( tok );
  161.  
  162.            attr |= at;
  163.         }
  164.         else {
  165.            int tmp;
  166.            
  167.            tmp = _is_special( tok );
  168.            if( _is_percent( tok ) ) percent++;
  169.  
  170.            if( percent )
  171.           cp->ce_flag |= F_PERCENT;
  172.  
  173.            if( special )
  174.               Fatal( "Special target must appear alone", tok );
  175.            else if( !(cp->ce_flag & F_MARK) ) {
  176.           /* Targets are kept in this list in lexically sorted order.
  177.            * This allows for easy equality comparison of target
  178.            * sets.*/
  179.           CELLPTR prev,cur;
  180.           for(prev=NIL(CELL),cur=targets;cur;prev=cur,cur=cur->ce_link)
  181.              if(strcmp(cur->CE_NAME,cp->CE_NAME) > 0)
  182.             break;
  183.             
  184.           cp->ce_link = cur;
  185.  
  186.           if (!prev)
  187.              targets = cp;
  188.           else
  189.              prev->ce_link = cp;
  190.  
  191.           cp->ce_flag |= F_MARK | F_EXPLICIT;
  192.           special = tmp;
  193.            }
  194.            else if( !(cp->ce_attr & A_LIBRARY) )
  195.           Warning("Duplicate entry [%s] in target list",cp->CE_NAME);
  196.         }
  197.      }
  198.      else {
  199.         /* found an operator so empty out break list
  200.          * and clear mark bits on target list, setting them all to F_USED*/
  201.  
  202.         brk  = "";
  203.         for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) {
  204.            cp->ce_flag ^= F_MARK;
  205.            cp->ce_flag |= F_USED;
  206.         }
  207.  
  208.         Def_targets = FALSE;
  209.      }
  210.       }
  211.       else {
  212.          /* Scanning prerequisites so build the prerequisite list.  We use
  213.           * F_MARK flag to make certain we have only a single copy of the
  214.           * prerequisite in the list */
  215.  
  216.      cp = Def_cell( tok );
  217.  
  218.      if( _is_percent( tok ) ) {
  219.         if( !percent && !attr )
  220.            Fatal( "Syntax error in %% rule, missing %% target");
  221.         mixed_glob_prq = 1;
  222.      }
  223.  
  224.      if( cp->ce_flag & F_USED ) {
  225.         if( cp->ce_attr & A_COMPOSITE )
  226.            continue;
  227.         else
  228.            Fatal( "Detected circular dependency in graph at [%s]",
  229.               cp->CE_NAME );
  230.      }
  231.          else if( !(cp->ce_flag & F_MARK) ) {
  232.         DB_PRINT( "par", ("pq_cell [%s]", tok) );
  233.         cp->ce_flag |= F_MARK;
  234.  
  235.         if( prereqtail == NIL(CELL) )    /* keep prereq's in order */
  236.            prereq = cp;
  237.         else
  238.            prereqtail->ce_link = cp;
  239.  
  240.         prereqtail = cp;
  241.         cp->ce_link = NIL(CELL);
  242.      }
  243.      else if( !(cp->ce_attr & A_LIBRARY) )
  244.         Warning("Duplicate entry [%s] in prerequisite list",cp->CE_NAME);
  245.       }
  246.       
  247.    /* Check to see if we have a percent rule that has only global
  248.     * prerequisites.  If so then set the flag so that later on, we don't issue
  249.     * an error if such targets supply an empty set of rules. */
  250.  
  251.    if( percent && !mixed_glob_prq && (prereq != NIL(CELL)) )
  252.       _sv_globprq_only = 1;
  253.  
  254.    /* It's ok to have targets with attributes, and no prerequisites, but it's
  255.     * not ok to have no targets and no attributes, or no operator */
  256.  
  257.    if( !op ) {
  258.       CLEAR_TOKEN( &input );
  259.       DB_PRINT( "par", ("Not a rule [%s]", Buffer) );
  260.       DB_RETURN( 0 );
  261.    }
  262.  
  263.    if( !attr && targets == NIL(CELL) ) {
  264.       Fatal( "Missing targets or attributes in rule" );
  265.       if( set_dir != NIL( char )) FREE( set_dir );
  266.       DB_RETURN( 0 );
  267.    }
  268.  
  269.    /* We have established we have a legal rules line, so we must process it.
  270.     * In doing so we must handle any special targets.  Special targets must
  271.     * appear alone possibly accompanied by attributes.
  272.     * NOTE:  special != 0  ==> targets != NIL(CELL) */
  273.     
  274.    if( prereqtail != NIL(CELL) ) prereqtail->ce_link = NIL(CELL);
  275.  
  276.    /* Clear out MARK bits used in duplicate checking.  I originally wanted
  277.     * to do this as the lists get processed but that got too error prone
  278.     * so I bit the bullit and added these two loops. */
  279.  
  280.    for( cp=prereq;  cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_MARK;
  281.    for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_USED;
  282.  
  283.    /* Check to see if the previous rule line was bound if, not the call
  284.     * Bind_rules_to_targets to go and bind the line */
  285.  
  286.    if( _sv_rules != NIL(STRING) ) Bind_rules_to_targets( F_DEFAULT );
  287.  
  288.    /* Add the first recipe line to the list */
  289.    if( firstrcp != NIL( char ) )
  290.       Add_recipe_to_list( firstrcp, TRUE, FALSE );
  291.  
  292.    /* Save these prior to calling _do_targets, since _build_graph needs the
  293.     * _sv_setdir value for matching edges. */
  294.    _sv_op     = op;
  295.    _sv_setdir = set_dir;
  296.  
  297.    if( special )
  298.       _do_special( special, op, attr, set_dir, targets, prereq, state );
  299.    else
  300.       *state = _do_targets( op, attr, set_dir, targets, prereq );
  301.  
  302.    DB_RETURN( 1 );
  303. }
  304.  
  305.  
  306. PUBLIC int
  307. Rule_op( op )/*
  308. ================
  309.    Check the passed in op string and map it to one of the rule operators */
  310. char *op;
  311. {
  312.    int ret = 0;
  313.  
  314.    DB_ENTER( "rule_op" );
  315.    
  316.    if( *op == TGT_DEP_SEP ) {
  317.       ret = R_OP_CL;
  318.       op++;
  319.  
  320.       /* All rule operations begin with a :, but may include any one of the
  321.        * four modifiers.  In order for the rule to be properly mapped we must
  322.        * check for each of the modifiers in turn, building up our return bit
  323.        * string. */
  324.  
  325.       while( *op && ret )
  326.          switch( *op ) {
  327.         case ':': ret |= R_OP_DCL; op++; break;
  328.         case '!': ret |= R_OP_BG;  op++; break;
  329.         case '^': ret |= R_OP_UP;  op++; break;
  330.         case '-': ret |= R_OP_MI;  op++; break;
  331.         case '|': ret |= R_OP_OR;  op++; break;
  332.  
  333.         default : ret  = 0;  /* an invalid modifier, chuck whole string */
  334.          }
  335.  
  336.       if( *op != '\0' ) ret = 0;
  337.    }
  338.  
  339.    DB_RETURN( ret );
  340. }
  341.  
  342.  
  343. PUBLIC void
  344. Add_recipe_to_list( rule, white_too, no_check )/*
  345. =================================================
  346.         Take the provided string and add it to the list of recipe lines
  347.     we are saving to be added to the list of targets we have built
  348.     previously.  If white_too == TRUE add the rule EVEN IF it contains only
  349.         whitespace. */
  350. char *rule;
  351. int  white_too;
  352. int  no_check;
  353. {
  354.    DB_ENTER( "Add_recipe_to_list" );
  355.  
  356.    if( rule != NIL( char ) && (*rule != '\0' || white_too) ) {
  357.       DB_PRINT( "par", ("Adding recipe [%s]", rule) );
  358.       _sv_crule = Def_recipe( rule, _sv_crule, white_too, no_check );
  359.  
  360.       if( _sv_rules == NIL(STRING) )
  361.          _sv_rules = _sv_crule;
  362.    }
  363.  
  364.    DB_VOID_RETURN;
  365. }
  366.  
  367.  
  368. PUBLIC void
  369. Bind_rules_to_targets( flag )/*
  370. ===============================
  371.         Take the rules we have defined and bind them with proper attributes
  372.         to the targets that were previously defined in the parse.  The
  373.         attributes that get passed here are merged with those that are were
  374.         previously defined.  (namely F_SINGLE) */
  375. int flag;
  376. {
  377.    CELLPTR tg;             /* pointer to current target in list */
  378.    LINKPTR lp;           /* pointer to link cell        */
  379.    int     magic;          /* TRUE if target is .xxx.yyy form   */
  380.    int     tflag;          /* TRUE if we assigned targets here  */
  381.  
  382.    DB_ENTER( "Bind_rules_to_targets" );
  383.  
  384.    /* This line is needed since Parse may call us twice when the last
  385.     * GROUP rule appears at the end of file.  In this case the rules
  386.     * have already been bound and we want to ignore them. */
  387.  
  388.    if( _sv_targets == NIL(CELL) ) { DB_VOID_RETURN; }
  389.  
  390.    tflag  = FALSE;
  391.    flag  |= (_sv_flag & F_SINGLE);
  392.    flag  |= ((_sv_attr & A_GROUP) ? F_GROUP : 0);
  393.  
  394.    for( tg = _sv_targets; tg != NIL(CELL); tg = tg->ce_link ) {
  395.       DB_PRINT( "par", ("Binding to %s, %04x", tg->CE_NAME, tg->ce_flag) );
  396.       magic = tg->ce_flag & F_PERCENT;
  397.  
  398. #if 0
  399.       /* Check to see if we had a rule of the form '%.o : a.h b.h ; xxx'
  400.        * In which case we must build a NULL prq node to hold the recipe */
  401.       if( _sv_globprq_only && (_sv_rules != NIL(STRING)) )
  402.      _build_graph( _sv_op, tg, NIL(CELL) );
  403. #endif
  404.  
  405.       /* NOTE:  For targets that are magic we ignore any previously defined
  406.        *        rules.  ie. We throw away the old definition and use the new.*/
  407.       if( !(tg->ce_flag & F_MULTI) && !magic && (tg->CE_RECIPE != NIL(STRING))
  408.       && !_sp_target && (_sv_rules != NIL(STRING)) )
  409.          Fatal( "Multiply defined recipe for target %s", tg->CE_NAME );
  410.  
  411.       if( (magic || _sp_target) && (_sv_rules == NIL(STRING)) &&
  412.       !(tg->ce_flag & F_SPECIAL) && !_sv_globprq_only )
  413.          Warning( "Empty recipe for special target %s", tg->CE_NAME );
  414.  
  415.       if( magic ) {
  416.      CELLPTR ep;
  417.  
  418.      for( ep=_sv_edgel; ep != NIL(CELL); ep=ep->ce_link ) {
  419.         _set_attributes( _sv_attr, _sv_setdir, ep );
  420.         ep->ce_flag |= (F_TARGET|flag);
  421.  
  422.         if( _sv_rules != NIL(STRING) ) {
  423.            ep->ce_recipe  = _sv_rules;
  424.            ep->ce_indprq  = _sv_glb_prq;
  425.         }
  426.      }
  427.       }
  428.       else {
  429.      tg->ce_attr |= _sv_attr;
  430.      tg->ce_flag |= flag;
  431.  
  432.      if( _sv_rules != NIL(STRING) ) {
  433.         tg->ce_recipe  = _sv_rules;
  434.         tg->ce_flag   |= F_RULES | F_TARGET;
  435.  
  436.         /* Bind the current set of prerequisites as belonging to the
  437.          * original recipe given for the target */
  438.         for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next )
  439.           if( !(lp->cl_flag & F_USED) ) lp->cl_flag |= F_TARGET;
  440.          }
  441.      else for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next )
  442.         lp->cl_flag |= F_USED;
  443.       }
  444.  
  445.       tflag |= _add_root(tg);
  446.    }
  447.  
  448.    if( tflag ) Target = TRUE;
  449.    if( _sv_setdir ) FREE(_sv_setdir);
  450.    _sv_rules   = NIL(STRING);
  451.    _sv_crule   = NIL(STRING);
  452.    _sv_targets = NIL(CELL);
  453.    _sv_glb_prq = NIL(LINK);
  454.    _sv_edgel   = NIL(CELL);
  455.    _sp_target  = FALSE;
  456.    _sv_globprq_only = 0;
  457.  
  458.    DB_VOID_RETURN;
  459. }
  460.  
  461.  
  462.  
  463. PUBLIC int
  464. Set_group_attributes( list )/*
  465. ==============================
  466.     Scan list looking for the standard @ and - (as in recipe line defs)
  467.     and set the flags accordingly so that they apply when we bind the
  468.     rules to the appropriate targets. */
  469. char *list;
  470. {
  471.    int res = FALSE;
  472.  
  473.    if ( !((_sv_attr|Glob_attr)&A_IGNOREGROUP) ) {
  474.       res = (*DmStrSpn(list,"@-%+ \t") == '[');
  475.       if( res ) _sv_attr |= Rcp_attribute(list);
  476.    }
  477.  
  478.    return(res);
  479. }
  480.  
  481.  
  482. static void
  483. _do_special( special, op, attr, set_dir, target, prereq, state )/*
  484. ==================================================================
  485.    Process a special target.  So far the only special targets we have
  486.    are those recognized by the _is_special function.
  487.  
  488.    target is always only a single special target.
  489.    
  490.    NOTE:  For the cases of .IMPORT, and .INCLUDE, the cells created by the
  491.          parser are never freed.  This is due to the fact that it is too much
  492.       trouble to get them out of the hash table once they are defined, and
  493.       if they are per chance used again it will be ok, anyway, since the
  494.       cell is not really used by the code below.  */
  495.  
  496. int    special;
  497. int    op;
  498. t_attr    attr;
  499. char    *set_dir;
  500. CELLPTR target;
  501. CELLPTR prereq;
  502. int     *state;
  503. {
  504.    HASHPTR    hp;        /* pointer to macro def cell        */
  505.    CELLPTR    cp;        /* temporary pointer into cells list    */
  506.    CELLPTR     dp;        /* pointer to directory dir cell    */
  507.    LINKPTR     lp;        /* pointer at prerequisite list     */
  508.    char        *dir;        /* current dir to prepend        */
  509.    char        *path;        /* resulting path to try to read    */
  510.    char     *name;        /* File name for processing a .INCLUDE    */
  511.    char        *tmp;        /* temporary string pointer        */
  512.    FILE     *fil;        /* File descriptor returned by Openfile    */
  513.  
  514.    DB_ENTER( "_do_special" );
  515.  
  516.    target->ce_flag = F_SPECIAL;    /* mark the target as special */
  517.  
  518.    switch( special ) {
  519.       case ST_EXPORT:
  520.      for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
  521.         DB_PRINT( "par", ("Exporting [%s]", prereq->CE_NAME) );
  522.         hp = GET_MACRO( prereq->CE_NAME );
  523.  
  524.         if( hp != NIL(HASH) ) {
  525.            char *tmpstr = hp->ht_value;
  526.  
  527.            if( tmpstr == NIL(char) ) tmpstr = "";
  528.  
  529.            if( Write_env_string( prereq->CE_NAME, tmpstr ) != 0 )
  530.           Warning( "Could not export %s", prereq->CE_NAME );
  531.         }
  532.      }
  533.      break;
  534.  
  535.       /* Simply cause the parser to fail on the next input read */
  536.       case ST_EXIT:
  537.          Skip_to_eof = TRUE;
  538.          break;
  539.  
  540.       case ST_IMPORT:
  541.      for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
  542.         char *tmpstr;
  543.  
  544.         DB_PRINT( "par", ("Importing [%s]", prereq->CE_NAME) );
  545.  
  546.         if( strcmp(prereq->CE_NAME, ".EVERYTHING") == 0 ) {
  547.            t_attr sattr = Glob_attr;
  548.            Glob_attr |= A_SILENT;
  549.  
  550.            ReadEnvironment();
  551.  
  552.            Glob_attr = sattr;
  553.         }
  554.         else {
  555.            tmpstr = Read_env_string( prereq->CE_NAME );
  556.  
  557.            if( tmpstr != NIL(char) )
  558.           Def_macro( prereq->CE_NAME,tmpstr,M_EXPANDED | M_LITERAL);
  559.            else
  560.           if( !((Glob_attr | attr) & A_IGNORE) )
  561.              Fatal("Imported macro `%s' not found",prereq->CE_NAME);
  562.         }
  563.      }
  564.  
  565.      attr &= ~A_IGNORE;
  566.      break;
  567.  
  568.       case ST_INCLUDE:
  569.       {
  570.      int pushed     = FALSE;
  571.      int first      = (attr & A_FIRST);
  572.      int ignore     = (((Glob_attr | attr) & A_IGNORE) != 0);
  573.      int found      = FALSE;
  574.      LINKPTR prqlnk = NIL(LINK);
  575.      LINKPTR prqlst = NIL(LINK);
  576.  
  577.      if( prereq == NIL(CELL) )  Fatal( "No .INCLUDE file(s) specified" );
  578.  
  579.      dp = Def_cell( ".INCLUDEDIRS" );
  580.  
  581.      if( (attr & A_SETDIR) && *(dir = strchr(set_dir, '=')+1) )
  582.         pushed = Push_dir( dir, ".INCLUDE", ignore );
  583.  
  584.      for( cp=prereq; cp != NIL(CELL); cp = cp->ce_link ) {
  585.         LINKPTR ltmp;
  586.         TALLOC(ltmp, 1, LINK);
  587.         ltmp->cl_prq = cp;
  588.  
  589.         if( prqlnk == NIL(LINK) )
  590.            prqlst = ltmp;
  591.         else
  592.            prqlnk->cl_next = ltmp;
  593.  
  594.         prqlnk = ltmp;
  595.      }
  596.  
  597.      for( ; prqlst != NIL(LINK); FREE(prqlst), prqlst=prqlnk ) {
  598.         prqlnk = prqlst->cl_next;
  599.         cp     = prqlst->cl_prq;
  600.         name   = cp->CE_NAME;
  601.  
  602.         /* Leave this here, it ensures that prqlst gets propely free'd */
  603.         if ( first && found )
  604.            continue;
  605.         
  606.         if( *name == '<' ) {
  607.            /* We have a file name enclosed in <....>
  608.             * so get rid of the <> arround the file name */
  609.  
  610.            name++;
  611.            if( (tmp = strrchr( name, '>' )) != NIL( char ) )
  612.           *tmp = 0;
  613.  
  614.            if( If_root_path( name ) )
  615.               fil = Openfile( name, FALSE, FALSE );
  616.            else
  617.           fil = NIL(FILE);
  618.         }
  619.         else
  620.            fil = Openfile( name, FALSE, FALSE );
  621.            
  622.         if( fil == NIL(FILE) ) { /*if true ==> not found in current dir*/
  623.  
  624.            /* Now we must scan the list of prerequisites for .INCLUDEDIRS
  625.             * looking for the file in each of the specified directories.
  626.         * if we don't find it then we issue an error.  The error
  627.         * message is suppressed if the .IGNORE attribute of attr is
  628.         * set.  If a file is found we call Parse on the file to
  629.         * perform the parse and then continue on from where we left
  630.         * off.  */
  631.  
  632.            for(lp=dp->CE_PRQ; lp && fil == NIL(FILE); lp=lp->cl_next) {
  633.           dir  = lp->cl_prq->CE_NAME;
  634.           if( strchr(dir, '$') ) dir = Expand(dir);
  635.           path = Build_path( dir, name );
  636.  
  637.           DB_PRINT( "par", ("Trying to include [%s]", path) );
  638.  
  639.           fil = Openfile( path, FALSE, FALSE );
  640.           if( dir != lp->cl_prq->CE_NAME ) FREE(dir);
  641.            }
  642.         }
  643.  
  644.         if ( fil == NIL(FILE) ) {
  645.            t_attr glob = Glob_attr;
  646.            t_attr cattr = prqlst->cl_prq->ce_attr;
  647.  
  648.            prqlst->cl_next = NIL(LINK);
  649.            Glob_attr |= ((attr&A_IGNORE)|A_SILENT);
  650.            prqlst->cl_prq->ce_attr &= ~A_FRINGE;
  651.  
  652.            fil = TryFiles(prqlst);
  653.  
  654.            Glob_attr = glob;
  655.            prqlst->cl_prq->ce_attr |= (cattr & A_FRINGE);
  656.         }
  657.  
  658.         if( fil != NIL(FILE) ) {
  659.            Parse( fil );
  660.            found = TRUE;
  661.         }
  662.         else if( !(ignore || first) )
  663.            Fatal( "Include file %s, not found", name );
  664.      }
  665.  
  666.      if ( !ignore && first && !found )
  667.         Fatal( "No include file was found" );
  668.  
  669.      if( pushed ) Pop_dir(FALSE);
  670.      attr &= ~(A_IGNORE|A_SETDIR);
  671.       }
  672.       break;
  673.      
  674.       case ST_SOURCE:
  675.       /* case ST_SUFFIXES: */
  676.            if( prereq != NIL(CELL) )
  677.         _do_targets( op & (R_OP_CL | R_OP_MI | R_OP_UP), attr, set_dir,
  678.              target, prereq );
  679.      else {
  680.         /* The old semantics of .SOURCE were that an empty list of
  681.          * prerequisites clears the .SOURCE list.  So we must implement
  682.          * that here as a clearout prerequisite operation.  Since this is
  683.          * a standard operation with the :- opcode we can simply call the
  684.          * proper routine with the target cell and it should do the trick
  685.          */
  686.  
  687.         if( op == R_OP_CL || (op & R_OP_MI) )
  688.            Clear_prerequisites( target );
  689.      }
  690.  
  691.      op &= ~(R_OP_MI | R_OP_UP);
  692.      break;
  693.  
  694.       case ST_KEEP:
  695.      if( Keep_state != NIL(char) ) break;
  696.      Def_macro( ".KEEP_STATE", "_state.mk", M_EXPANDED );
  697.      break;
  698.  
  699.       case ST_REST:
  700.          /* The rest of the special targets can all take rules, as such they
  701.       * must be able to affect the state of the parser. */
  702.  
  703.      {
  704.         int s_targ = Target;
  705.  
  706.         Target     = TRUE;
  707.         _sp_target = TRUE;
  708.         *state     = _do_targets( op, attr, set_dir, target, prereq );
  709.         Target     = s_targ;
  710.  
  711.         target->ce_flag |= F_TARGET;
  712.  
  713.         attr    = A_DEFAULT;
  714.         op      = R_OP_CL;
  715.      }
  716.      break;
  717.  
  718.       default:break;
  719.    }
  720.       
  721.    if( op   != R_OP_CL   ) Warning( "Modifier(s) for operator ignored" );
  722.    if( attr != A_DEFAULT ) Warning( "Extra attributes ignored" );
  723.  
  724.    DB_VOID_RETURN;
  725. }
  726.  
  727.  
  728.  
  729. static int
  730. _do_targets( op, attr, set_dir, targets, prereq )/*
  731. ================================================= */
  732. int    op;
  733. t_attr    attr;
  734. char    *set_dir;
  735. CELLPTR targets;
  736. CELLPTR prereq;
  737. {
  738.    CELLPTR    tg1;        /* temporary target pointer        */
  739.    CELLPTR    tp1;        /* temporary prerequisite pointer    */
  740.    LINKPTR      prev_cell;    /* pointer for .UPDATEALL processing    */
  741.    char        *p;        /* temporary char pointer        */
  742.    int          tflag = FALSE;    /* set to TRUE if we add target to root */
  743.  
  744.    DB_ENTER( "_do_targets" );
  745.  
  746.    if( attr & A_UPDATEALL ) {
  747.       if( targets == NIL(CELL) )
  748.      Fatal( ".UPDATEALL attribute requires non-empty list of targets" );
  749.  
  750.       if (targets->ce_set == NIL(CELL)) {
  751.      for(
  752.         prev_cell=CeMeToo(targets),tg1=targets->ce_link;
  753.         tg1 != NIL(CELL);
  754.         tg1=tg1->ce_link
  755.      ) {
  756.         if (tg1->ce_set)
  757.            Fatal( "Target [%s] appears on multiple .UPDATEALL lists",
  758.               tg1->CE_NAME);
  759.         tg1->ce_set = targets;
  760.         TALLOC(prev_cell->cl_next, 1, LINK);
  761.         prev_cell = prev_cell->cl_next;
  762.         prev_cell->cl_prq = tg1;
  763.      }
  764.      targets->ce_set = targets;
  765.       }
  766.       else {
  767.      LINKPTR ap;
  768.      CELLPTR tp;
  769.  
  770.      tp = targets;
  771.      ap = CeMeToo(targets);
  772.      while (ap && tp && ap->cl_prq == tp && tp->ce_set == targets) {
  773.         ap = ap->cl_next;
  774.         tp = tp->ce_link;
  775.      }
  776.      if (ap || tp)
  777.         Fatal("Inconsistent .UPDATEALL lists for target [%s]",
  778.           targets->CE_NAME);
  779.       }
  780.       targets->ce_link = NIL(CELL);
  781.    }
  782.  
  783.    for( tg1 = targets; tg1 != NIL(CELL); tg1 = tg1->ce_link ) {
  784.       /* Check each target.  Check for inconsistencies between :: and : rule
  785.        * sets.  :: may follow either : or :: but not the reverse.
  786.        *
  787.        * Any targets that contain :: rules are represented by a prerequisite
  788.        * list hanging off the main target cell where each of the prerequisites
  789.        * is a copy of the target cell but is not entered into the hash table.
  790.        */
  791.       int magic = (tg1->ce_flag & F_PERCENT) && !(tg1->ce_flag & F_MAGIC);
  792.  
  793.       if( !(op & R_OP_DCL ) && (tg1->ce_flag & F_MULTI) && !magic )
  794.      Fatal( "':' vs '::' inconsistency in rules for %s", tg1->CE_NAME );
  795.  
  796.       if( magic ) {
  797.      if (op & R_OP_OR)
  798.         for(tp1=prereq; tp1; tp1=tp1->ce_link) {
  799.            CELLPTR tmpcell = tp1->ce_link;
  800.            tp1->ce_link = NIL(CELL);
  801.            _build_graph(op,tg1,tp1);
  802.            tp1->ce_link = tmpcell;
  803.         }
  804.      else
  805.         prereq = _build_graph(op,tg1,prereq);
  806.       }
  807.       else if( !(tg1->ce_flag & F_SPECIAL) && 
  808.         (prereq == NIL(CELL)) &&
  809.         (p = _is_magic( tg1->CE_NAME )) != NIL(char))
  810.          _do_magic( op, p, tg1, prereq, attr, set_dir );
  811.       else if( op & R_OP_DCL ) {
  812.      CELLPTR tmp_cell = _make_multi(tg1);
  813.      tflag |= _add_root(tg1);
  814.      targets = _replace_cell( targets, tg1, tmp_cell );
  815.      tg1 = tmp_cell;
  816.       }
  817.  
  818.       if( !magic ) _set_attributes( attr, set_dir, tg1 );
  819.  
  820.       /* Build the proper prerequisite list of the target.  If the `-',
  821.        * modifier was used clear the prerequisite list before adding any
  822.        * new prerequisites.  Else add them to the head/tail as appropriate.
  823.        *
  824.        * If the target has F_PERCENT set then no prerequisites are used. */
  825.  
  826.       if( !(tg1->ce_flag & F_PERCENT) ) {
  827.      if( op & R_OP_MI ) Clear_prerequisites( tg1 );
  828.  
  829.      if( (op & R_OP_UP) && (tg1->ce_prq != NIL(LINK)) )
  830.         _stick_at_head( tg1, prereq );
  831.      else for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link )
  832.         Add_prerequisite( tg1, tp1, FALSE, FALSE );
  833.       }
  834.       else if( op & (R_OP_MI | R_OP_UP) )
  835.      Warning( "Modifier(s) `^!' for %-meta target ignored" );
  836.    }
  837.  
  838.    if(tflag)
  839.       Target = TRUE;
  840.  
  841.    /* Check to see if we have NO targets but some attributes.  IF so then
  842.     * apply all of the attributes to the complete list of prerequisites.
  843.     */
  844.  
  845.    if( (targets == NIL(CELL)) && attr )
  846.       if( prereq != NIL(CELL) )
  847.      for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link )
  848.         _set_attributes( attr, set_dir, tp1 );
  849.       else
  850.      _set_global_attr( attr );
  851.  
  852.    /* Now that we have built the lists of targets, the parser must parse the
  853.     * rules if there are any.  However we must start the rule list with the
  854.     * rule specified as via the ; kludge, if there is one */
  855.    _sv_targets = targets;
  856.    _sv_attr    = attr;
  857.    _sv_flag    = ((op & R_OP_BG) ? F_SINGLE : F_DEFAULT);
  858.       
  859.    DB_RETURN( RULE_SCAN );
  860. }
  861.  
  862.  
  863. static int
  864. _do_magic( op, dot, target, prereq, attr, set_dir )/*
  865. =====================================================
  866.    This function takes a magic target of the form .<chars>.<chars> or
  867.    .<chars> and builds the appropriate % rules for that target.
  868.    
  869.    The function builds the % rule, `%.o : %.c'  from .c.o, and
  870.    `%.a :' from .a */
  871.  
  872. int    op;
  873. char    *dot;
  874. CELLPTR target;
  875. CELLPTR prereq;
  876. t_attr  attr;
  877. char    *set_dir;
  878. {
  879.    CELLPTR tg;
  880.    CELLPTR prq;
  881.    char    *tmp, *tmp2;
  882.  
  883.    DB_ENTER( "_do_magic" );
  884.  
  885.    if( prereq != NIL(CELL) )
  886.       Warning( "Ignoring prerequisites of old style meta-target" );
  887.  
  888.    if( dot == target->CE_NAME )    {        /* its of the form .a    */
  889.       tg  = Def_cell( "%" );            /* ==> no prerequisite  */
  890.       tmp = _build_meta( target->CE_NAME );
  891.       prq = Def_cell( tmp );
  892.       FREE( tmp );
  893.  
  894.       _build_graph( op, tg, prq );
  895.    }
  896.    else {
  897.       tmp = _build_meta( dot );
  898.       tg  = Def_cell( tmp );
  899.       FREE( tmp );
  900.  
  901.       tmp = _build_meta( tmp2 = DmSubStr( target->CE_NAME, dot ) );
  902.       prq = Def_cell( tmp );
  903.       FREE( tmp  );
  904.       FREE( tmp2 );
  905.  
  906.       _build_graph( op, tg, prq );
  907.    }
  908.  
  909.    tg->ce_flag      |= F_PERCENT;
  910.    target->ce_flag  |= (F_MAGIC|F_PERCENT);
  911.  
  912.    _set_attributes( attr, set_dir, tg );
  913.  
  914.    DB_RETURN(1);
  915. }
  916.  
  917.  
  918. static CELLPTR
  919. _replace_cell( lst, cell, rep )
  920. CELLPTR lst;
  921. CELLPTR cell;
  922. CELLPTR rep;
  923. {
  924.    register CELLPTR tp;
  925.    
  926.    if( lst == cell ) {
  927.       rep->ce_link = lst->ce_link;
  928.       lst = rep;
  929.    }
  930.    else {
  931.       for( tp=lst; tp->ce_link != cell; tp=tp->ce_link );
  932.       rep->ce_link = tp->ce_link->ce_link;
  933.       tp->ce_link = rep;
  934.    }
  935.  
  936.    return(lst);
  937. }
  938.  
  939.  
  940. static char *
  941. _build_meta( name )/*
  942. =====================
  943.    Check to see if the name is of the form .c~ if so and if Augmake
  944.    translation is enabled then return s.%.c, else return %.suff, where if the
  945.    suffix ends in '~' then leave it be.*/
  946. char *name;
  947. {
  948.    char *tmp;
  949.    int  test = Augmake ? name[strlen(name)-1] == '~' : 0;
  950.  
  951.    tmp = DmStrJoin( test ? "s.%" : "%", name, -1, FALSE);
  952.    if( test ) tmp[ strlen(tmp)-1 ] = '\0';
  953.  
  954.    return(tmp);
  955. }
  956.  
  957.  
  958. static CELLPTR
  959. _build_graph( op, target, prereq )/*
  960. ====================================
  961.    This function is called to build the graph for the % rule given by
  962.    target : prereq cell combination.  This function assumes that target
  963.    is a % target and that prereq is a single % prerequisite.  R_OP_CL
  964.    rules replace existing rules if any, only R_OP_CL works for meta-rules.
  965.    %.o :: %.c is meaningless.   If target has ce_all set then all the cells
  966.    on the list must match in order for the match to work.  If prereq->ce_link
  967.    is not nil then all prerequisites listed by the link set must match also.
  968.    This latter match is more difficult because in general the prerequisite
  969.    sets may not be listed in the same order.
  970.    
  971.    It also assumes that target cell has F_PERCENT set already. */
  972. int op;
  973. CELLPTR target;
  974. CELLPTR prereq;
  975. {
  976.    LINKPTR edl;
  977.    CELLPTR edge;
  978.    CELLPTR tpq,cur;
  979.    int match;
  980.  
  981.    DB_ENTER( "_build_graph" );
  982.    DB_PRINT( "%", ("Building graph for [%s : %s]", target->CE_NAME,
  983.             (prereq == NIL(CELL)) ? "" : prereq->CE_NAME) );
  984.  
  985.    tpq = NIL(CELL);
  986.    for(cur=prereq;cur;cur=cur->ce_link) {
  987.       char *name = cur->CE_NAME;
  988.       int   len  = strlen(name);
  989.  
  990.       if( *name == '\'' && name[len-1]=='\'' ){
  991.      _add_global_prereq( cur );
  992.      name[len-1] = '\0';
  993.      strcpy(name,name+1);
  994.       }
  995.       else {
  996.      if (tpq)
  997.         tpq->ce_link = cur;
  998.      else
  999.         prereq = cur;
  1000.      tpq = cur;
  1001.       }
  1002.    }
  1003.    if(tpq)
  1004.       tpq->ce_link=NIL(CELL);
  1005.    else
  1006.       prereq = NIL(CELL);
  1007.  
  1008.    /* Search the list of prerequisites for the current target and see if
  1009.     * any of them match the current %-meta's : prereq's pair.  NOTE that
  1010.     * %-metas are built as if they were F_MULTI targets. */
  1011.    match = FALSE;
  1012.    for(edl=target->ce_prq; !match && edl != NIL(LINK); edl=edl->cl_next) {
  1013.       LINKPTR l1,l2;
  1014.       edge = edl->cl_prq;
  1015.  
  1016.       DB_PRINT("%", ("Trying to match [%s]",edge?edge->CE_NAME:"(nil)"));
  1017.  
  1018.       /* First we match the target sets, if this fails then we don't have to
  1019.        * bother with the prerequisite sets.  The targets sets are sorted.
  1020.        * this makes life very simple. */
  1021.       
  1022.       l1 = CeMeToo(target);
  1023.       l2 = CeMeToo(edge);
  1024.       while(l1 && l2 && l1->cl_prq == l2->cl_prq) {
  1025.      l1=l1->cl_next;
  1026.      l2=l2->cl_next;
  1027.       }
  1028.  
  1029.       if (l1 || l2)
  1030.      continue;
  1031.  
  1032.       /* target sets match, so check prerequisites. */
  1033.  
  1034.       if(    (!edge->ce_prq && !prereq)
  1035.       || (   edge->ce_prq
  1036.           && (   edge->ce_dir == _sv_setdir
  1037.           || (   edge->ce_dir
  1038.               && _sv_setdir
  1039.               && !strcmp(edge->ce_dir,strchr(_sv_setdir,'=')+1)
  1040.              )
  1041.          )
  1042.          )
  1043.       ) {
  1044.      LINKPTR prql;
  1045.      
  1046.      /* this is a really gross way to compare two sets, it's n^2 but
  1047.       * since the sets are assumed to always be tiny, it should be ok. */
  1048.      for(tpq=prereq; tpq; tpq=tpq->ce_link) {
  1049.         for(prql=edge->ce_prq;prql;prql=prql->cl_next)
  1050.            if (prql->cl_prq == tpq)
  1051.           break;
  1052.  
  1053.         if(prql == NIL(LINK))
  1054.            break;
  1055.         
  1056.         prql->cl_prq->ce_flag |= F_MARK;
  1057.      }
  1058.  
  1059.      if (tpq == NIL(CELL)) {
  1060.         for(prql=edge->ce_prq;prql;prql=prql->cl_next)
  1061.            if(!(prql->cl_prq->ce_flag & F_MARK))
  1062.           break;
  1063.  
  1064.         if(prql == NIL(LINK))
  1065.         match = TRUE;
  1066.      }
  1067.  
  1068.      /* clean up the mark bits. */
  1069.      for(prql=edge->ce_prq;prql;prql=prql->cl_next)
  1070.         prql->cl_prq->ce_flag &= ~F_MARK;
  1071.       }
  1072.    }
  1073.  
  1074.    if( match ) {
  1075.       /* match is TRUE hence, we found an edge joining the target and the
  1076.        * prerequisite so reset the new edge so that new values replace it. */
  1077.       DB_PRINT( "%", ("It's an old edge") );
  1078.  
  1079.       edge->ce_dir    = NIL(char);
  1080.       edge->ce_flag  &= (F_PERCENT|F_MAGIC|F_DFA);
  1081.       edge->ce_attr  &= A_NOINFER;
  1082.    }
  1083.    else {
  1084.       DB_PRINT( "%", ("Adding a new edge") );
  1085.  
  1086.       edge = _make_multi(target);
  1087.  
  1088.       for(edl=CeMeToo(target);edl;edl=edl->cl_next) {
  1089.      if( !((tpq=edl->cl_prq)->ce_flag & F_DFA) ) {
  1090.         Add_nfa( tpq->CE_NAME );
  1091.         tpq->ce_flag |= F_DFA;
  1092.      }
  1093.  
  1094.      edl->cl_prq->ce_set = edge;
  1095.       }
  1096.  
  1097.       edge->ce_all = target->ce_all;
  1098.       target->ce_all.cl_next = NIL(LINK);
  1099.       target->ce_set = NIL(CELL);
  1100.       
  1101.       for(tpq=prereq; tpq; tpq=tpq->ce_link)
  1102.          Add_prerequisite(edge, tpq, FALSE, TRUE);
  1103.    }
  1104.  
  1105.    if( op & R_OP_DCL )
  1106.    Warning("'::' operator for meta-target '%s' ignored, ':' operator assumed.",
  1107.        target->CE_NAME );
  1108.  
  1109.    edge->ce_link = _sv_edgel;
  1110.    _sv_edgel = edge;
  1111.    _sv_globprq_only = 0;
  1112.  
  1113.    DB_RETURN(NIL(CELL));
  1114. }
  1115.  
  1116.  
  1117. static CELLPTR
  1118. _make_multi( tg )
  1119. CELLPTR tg;
  1120. {
  1121.    CELLPTR cp;
  1122.    CELLPTR tcp;
  1123.  
  1124.    /* This first handles the case when a : foo ; exists prior to seeing
  1125.     * a :: fee; */
  1126.    if( !(tg->ce_flag & F_MULTI) && (tg->ce_prq || tg->ce_recipe) ) {
  1127.       TALLOC(cp, 1, CELL);
  1128.       *cp = *tg;
  1129.  
  1130.       tg->ce_prq    = NIL(LINK);
  1131.       tg->ce_flag  |= F_RULES|F_MULTI|F_TARGET;
  1132.       tg->ce_attr  |= A_SEQ;
  1133.       tg->ce_recipe = NIL(STRING);
  1134.       tg->ce_dir    = NIL(char);
  1135.       cp->ce_count  = ++tg->ce_index;
  1136.       cp->ce_cond   = NIL(STRING);
  1137.       cp->ce_set    = NIL(CELL);
  1138.       cp->ce_all.cl_prq = cp;
  1139.       CeNotMe(cp)   = NIL(LINK);
  1140.  
  1141.       Add_prerequisite(tg, cp, FALSE, TRUE);
  1142.    }
  1143.  
  1144.    TALLOC(cp, 1, CELL);
  1145.    *cp = *tg;
  1146.  
  1147.    if( !(tg->ce_flag & F_MULTI) ) {
  1148.       tg->ce_prq    = NIL(LINK);
  1149.       tg->ce_flag  |= F_RULES|F_MULTI|F_TARGET;
  1150.       tg->ce_attr  |= A_SEQ;
  1151.       tg->ce_recipe = NIL(STRING);
  1152.       tg->ce_dir    = NIL(char);
  1153.       cp->ce_cond   = NIL(STRING);
  1154.    }
  1155.    else {
  1156.       cp->ce_flag  &= ~(F_RULES|F_MULTI);
  1157.       cp->ce_attr  &= ~A_SEQ;
  1158.       cp->ce_prq    = NIL(LINK);
  1159.       cp->ce_index  = 0;
  1160.       cp->ce_cond   = NIL(STRING);
  1161.    }
  1162.    cp->ce_count = ++tg->ce_index;
  1163.    cp->ce_flag |= F_TARGET;
  1164.    cp->ce_set   = NIL(CELL);
  1165.    cp->ce_all.cl_prq = cp;
  1166.    CeNotMe(cp)  = NIL(LINK);
  1167.    
  1168.    Add_prerequisite(tg, cp, FALSE, TRUE);
  1169.    return(cp);
  1170. }
  1171.  
  1172.  
  1173. static void
  1174. _add_global_prereq( pq )/*
  1175. ==========================
  1176.     Prerequisite is a non-% prerequisite for a %-rule target, add it to
  1177.     the target's list of global prerequsites to add on match */
  1178. CELLPTR pq;
  1179. {
  1180.    register LINKPTR ln;
  1181.  
  1182.    for(ln=_sv_glb_prq; ln; ln=ln->cl_next)
  1183.       if(strcmp(ln->cl_prq->CE_NAME,pq->CE_NAME) == 0)
  1184.      return;
  1185.  
  1186.    TALLOC( ln, 1, LINK );
  1187.    ln->cl_next = _sv_glb_prq;
  1188.    ln->cl_prq  = pq;
  1189.    _sv_glb_prq = ln;
  1190. }
  1191.  
  1192.  
  1193.  
  1194. static void
  1195. _set_attributes( attr, set_dir, cp )/*
  1196. ======================================
  1197.     Set the appropriate attributes for a cell */
  1198. t_attr    attr;
  1199. char    *set_dir;
  1200. CELLPTR cp;
  1201. {
  1202.    char   *dir;
  1203.  
  1204.    DB_ENTER( "_set_attributes" );
  1205.  
  1206.    /* If .SETDIR attribute is set then we have at least .SETDIR= in the
  1207.     * set_dir string.  So go and fishout what is at the end of the =.
  1208.     * If not set and not NULL then propagate it to the target cell. */
  1209.  
  1210.    if( attr & A_SETDIR ) {
  1211.       char *p;
  1212.       if( (p = strchr( set_dir, '=' )) != NULL )
  1213.          dir = p + 1;
  1214.  
  1215.       if( cp->ce_dir )
  1216.      Warning( "Multiple .SETDIR for %s ignored", cp->CE_NAME );
  1217.       else if( *dir )
  1218.          cp->ce_dir = DmStrDup(dir);
  1219.    }
  1220.    cp->ce_attr |= attr;        /* set rest of attributes for target */
  1221.  
  1222.    DB_VOID_RETURN;
  1223. }
  1224.  
  1225.  
  1226.  
  1227. static void
  1228. _set_global_attr( attr )/*
  1229. ==========================
  1230.     Handle the setting of the global attribute functions based on
  1231.     The attribute flags set in attr. */
  1232. t_attr attr;
  1233. {
  1234.    t_attr flag;
  1235.  
  1236.    /* Some compilers can't handle a switch on a long, and t_attr is now a long
  1237.     * integer on some systems.  foey! */
  1238.    for( flag = MAX_ATTR; flag; flag >>= 1 )
  1239.       if( flag & attr )
  1240.      if( flag == A_PRECIOUS)      Def_macro(".PRECIOUS",  "y", M_EXPANDED);
  1241.      else if( flag == A_SILENT)   Def_macro(".SILENT",    "y", M_EXPANDED);
  1242.      else if( flag == A_IGNORE )  Def_macro(".IGNORE",    "y", M_EXPANDED);
  1243.      else if( flag == A_EPILOG )  Def_macro(".EPILOG",    "y", M_EXPANDED);
  1244.      else if( flag == A_PROLOG )  Def_macro(".PROLOG",    "y", M_EXPANDED);
  1245.      else if( flag == A_NOINFER ) Def_macro(".NOINFER",   "y", M_EXPANDED);
  1246.      else if( flag == A_SEQ )     Def_macro(".SEQUENTIAL","y", M_EXPANDED);
  1247.      else if( flag == A_SHELL )   Def_macro(".USESHELL",  "y", M_EXPANDED);
  1248.      else if( flag == A_MKSARGS ) Def_macro(".MKSARGS",   "y", M_EXPANDED);
  1249.      else if( flag == A_SWAP )    Def_macro(".SWAP",      "y", M_EXPANDED);
  1250.  
  1251.    attr &= ~A_GLOB;
  1252.    if( attr ) Warning( "Non global attribute(s) ignored" );
  1253. }
  1254.  
  1255.  
  1256.  
  1257. static void
  1258. _stick_at_head( cp, pq )/*
  1259. ==========================
  1260.     Add the prerequisite list to the head of the existing prerequisite
  1261.     list */
  1262.  
  1263. CELLPTR cp;          /* cell for target node    */
  1264. CELLPTR pq;        /* list of prerequisites to add */
  1265. {
  1266.    DB_ENTER( "_stick_at_head" );
  1267.  
  1268.    if( pq->ce_link != NIL(CELL) ) _stick_at_head( cp, pq->ce_link );
  1269.    Add_prerequisite( cp, pq, TRUE, FALSE );
  1270.  
  1271.    DB_VOID_RETURN;
  1272. }
  1273.  
  1274.  
  1275.  
  1276. static t_attr
  1277. _is_attribute( name )/*
  1278. =======================
  1279.    Check the passed name against the list of valid attributes and return the
  1280.    attribute index if it is, else return 0, indicating the name is not a valid
  1281.    attribute.  The present attributes are defined in dmake.h as A_xxx #defines,
  1282.    with the corresponding makefile specification:  (note they must be named
  1283.    exactly as defined below)
  1284.    
  1285.    Valid attributes are:  .IGNORE, .SETDIR=, .SILENT, .PRECIOUS, .LIBRARY,
  1286.                           .EPILOG, .PROLOG,  .LIBRARYM, .SYMBOL, .UPDATEALL,
  1287.               .USESHELL, .NOINFER, .PHONY, .SWAP, .SEQUENTIAL
  1288.               .NOSTATE,  .MKSARGS, .IGNOREGROUP, .GROUP, .FIRST
  1289.  
  1290.    NOTE:  The strcmp's are OK since at most three are ever executed for any
  1291.           one attribute check, and that happens only when we can be fairly
  1292.           certain we have an attribute.  */
  1293. char *name;
  1294. {
  1295.    t_attr attr = 0;
  1296.    
  1297.    DB_ENTER( "_is_attribute" );
  1298.    
  1299.    if( *name++ == '.' )
  1300.       switch( *name )
  1301.       {
  1302.          case 'E': attr = (strcmp(name, "EPILOG"))   ? 0 : A_EPILOG;  break;
  1303.  
  1304.      /* A_FIRST implies A_IGNORE, handled in ST_INCLUDE */
  1305.          case 'F':
  1306.         attr = (strcmp(name, "FIRST")) ? 0 : A_FIRST;
  1307.         break;
  1308.  
  1309.      case 'G': attr = (strcmp(name, "GROUP"))    ? 0 : A_GROUP;   break;
  1310.          case 'L': attr = (strcmp(name, "LIBRARY"))  ? 0 : A_LIBRARY; break;
  1311.          case 'M': attr = (strcmp(name, "MKSARGS"))  ? 0 : A_MKSARGS; break;
  1312.  
  1313.          case 'I':
  1314.         if( !strcmp(name, "IGNORE") )           attr = A_IGNORE;
  1315.         else if( !strcmp(name, "IGNOREGROUP"))  attr = A_IGNOREGROUP;
  1316.         else attr = 0;
  1317.         break;
  1318.  
  1319.          case 'N':
  1320.         if( !strcmp(name, "NOINFER") )      attr = A_NOINFER;
  1321.         else if( !strcmp(name, "NOSTATE"))  attr = A_NOSTATE;
  1322.         else attr = 0;
  1323.         break;
  1324.  
  1325.          case 'U':
  1326.         if( !strcmp(name, "UPDATEALL") )    attr = A_UPDATEALL;
  1327.         else if( !strcmp(name, "USESHELL")) attr = A_SHELL;
  1328.         else attr = 0;
  1329.         break;
  1330.  
  1331.          case 'P':
  1332.             if( !strcmp(name, "PRECIOUS") )     attr = A_PRECIOUS;
  1333.             else if( !strcmp(name, "PROLOG") )  attr = A_PROLOG;
  1334.             else if( !strcmp(name, "PHONY") )   attr = A_PHONY;
  1335.             else attr = 0;
  1336.             break;
  1337.  
  1338.          case 'S':
  1339.             if( !strncmp(name, "SETDIR=", 7) )    attr = A_SETDIR;
  1340.             else if( !strcmp(name, "SILENT") )    attr = A_SILENT;
  1341.             else if( !strcmp(name, "SYMBOL") )    attr = A_SYMBOL;
  1342.             else if( !strcmp(name, "SEQUENTIAL")) attr = A_SEQ;
  1343.             else if( !strcmp(name, "SWAP"))       attr = A_SWAP;
  1344.             else attr = 0;
  1345.             break;
  1346.       }
  1347.  
  1348.    DB_RETURN( attr );
  1349. }
  1350.  
  1351.  
  1352.  
  1353. static int
  1354. _is_special( tg )/*
  1355. ===================
  1356.    This function returns TRUE if the name passed in represents a special
  1357.    target, otherwise it returns false.  A special target is one that has
  1358.    a special meaning to dmake, and may require processing at the time that
  1359.    it is parsed.
  1360.    
  1361.    Current Special targets are:
  1362.     .GROUPPROLOG    .GROUPEPILOG    .INCLUDE    .IMPORT
  1363.     .EXPORT        .SOURCE     .SUFFIXES    .ERROR        .EXIT
  1364.     .INCLUDEDIRS    .MAKEFILES    .REMOVE        .KEEP_STATE
  1365. */
  1366. char *tg;
  1367. {
  1368.    DB_ENTER( "_is_special" );
  1369.    
  1370.    if( *tg++ != '.' ) DB_RETURN( 0 );
  1371.    
  1372.    switch( *tg )
  1373.    {
  1374.       case 'E':
  1375.          if( !strcmp( tg, "ERROR" ) )        DB_RETURN( ST_REST     );
  1376.          else if( !strcmp( tg, "EXPORT" ) )    DB_RETURN( ST_EXPORT   );
  1377.          else if( !strcmp( tg, "EXIT" ) )    DB_RETURN( ST_EXIT     );
  1378.      break;
  1379.  
  1380.       case 'G':
  1381.      if( !strcmp( tg, "GROUPPROLOG" ))      DB_RETURN( ST_REST     );
  1382.      else if( !strcmp( tg, "GROUPEPILOG" )) DB_RETURN( ST_REST     );
  1383.      break;
  1384.  
  1385.       case 'I':
  1386.          if( !strcmp( tg, "IMPORT" ) )        DB_RETURN( ST_IMPORT   );
  1387.          else if( !strcmp( tg, "INCLUDE" ) )    DB_RETURN( ST_INCLUDE  );
  1388.      else if( !strcmp( tg, "INCLUDEDIRS" )) DB_RETURN( ST_REST     );
  1389.      break;
  1390.       
  1391.       case 'K':
  1392.          if( !strcmp( tg, "KEEP_STATE" ) )    DB_RETURN( ST_KEEP     );
  1393.      break;
  1394.  
  1395.       case 'M':
  1396.          if( !strcmp( tg, "MAKEFILES" ) )    DB_RETURN( ST_REST     );
  1397.      break;
  1398.  
  1399.       case 'R':
  1400.          if( !strcmp( tg, "REMOVE" ) )        DB_RETURN( ST_REST     );
  1401.      break;
  1402.  
  1403.       case 'S':
  1404.          if( !strncmp( tg, "SOURCE", 6 ) )    DB_RETURN( ST_SOURCE   );
  1405.          else if( !strncmp(tg, "SUFFIXES", 8 )) DB_RETURN( ST_SOURCE   );
  1406.      break;
  1407.    }
  1408.    
  1409.    DB_RETURN( 0 );
  1410. }
  1411.  
  1412.  
  1413.  
  1414. static int
  1415. _is_percent( np )/*
  1416. ===================
  1417.     return TRUE if np points at a string containing a % sign */
  1418. char *np;
  1419. {
  1420.    return( (strchr(np,'%') && (*np != '\'' && np[strlen(np)-1] != '\'')) ?
  1421.        TRUE : FALSE );
  1422. }
  1423.  
  1424.  
  1425. static char *
  1426. _is_magic( np )/*
  1427. =================
  1428.     return TRUE if np points at a string of the form
  1429.           .<chars>.<chars>  or  .<chars>
  1430.     where chars are only alpha characters.
  1431.  
  1432.         NOTE:  reject target if it begins with ./ or ../ */
  1433. char *np;
  1434. {
  1435.    register char *n;
  1436.  
  1437.    n = np;
  1438.    if( *n != '.' ) return( NIL(char) );
  1439.    if (strchr(DirBrkStr, *(n+1))!=NULL || *(n+1) == '.' )
  1440.       return (NIL(char));
  1441.  
  1442.    for( n++; isgraph(*n) && (*n != '.'); n++ );
  1443.  
  1444.    if( *n != '\0' ) {
  1445.       if( *n != '.' )  return( NIL(char) );
  1446.       for( np = n++; isgraph( *n ) && (*n != '.'); n++ );
  1447.       if( *n != '\0' ) return( NIL(char) );
  1448.    }
  1449.    else if( !Augmake )
  1450.       return( NIL(char) );
  1451.  
  1452.    /* np points at the second . of .<chars>.<chars> string.
  1453.     * if the special target is of the form .<chars> then np points at the
  1454.     * first . in the token. */
  1455.  
  1456.    return( np );
  1457. }
  1458.  
  1459.  
  1460. static int
  1461. _add_root(tg)
  1462. CELLPTR tg;
  1463. {
  1464.    int res = FALSE;
  1465.  
  1466.    if(tg == Targets)
  1467.       return(TRUE);
  1468.  
  1469.    if( !Target && !(tg->ce_flag & (F_SPECIAL|F_PERCENT)) ) {
  1470.       Add_prerequisite(Targets, tg, FALSE, TRUE);
  1471.  
  1472.       tg->ce_flag |= F_TARGET;
  1473.       tg->ce_attr |= A_FRINGE;
  1474.       res          = TRUE;
  1475.    }
  1476.  
  1477.    return(res);
  1478. }
  1479.