home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / DMAKE38A.ZIP / RULPARSE.C < prev    next >
C/C++ Source or Header  |  1992-01-23  |  40KB  |  1,326 lines

  1. /* RCS      -- $Header: /u2/dvadura/src/generic/dmake/src/RCS/rulparse.c,v 1.1 1992/01/24 03:27:57 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) 1990 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  1992/01/24  03:27:57  dvadura
  33.  * dmake Version 3.8, 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    void    _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 = _strspn( 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 ) ) {
  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 = _strdup( 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.               if( targets != NIL(CELL) )
  172.              Fatal( "Multiple targets are not allowed in %% rules" );
  173.           else
  174.              cp->ce_flag |= F_PERCENT;
  175.  
  176.            if( special )
  177.               Fatal( "Special target must appear alone", tok );
  178.            else if( !(cp->ce_flag & F_MARK) ) {
  179.           cp->ce_link  = targets;  /* targets are stacked in this list*/
  180.           cp->ce_flag |= F_MARK | F_EXPLICIT;
  181.           targets      = cp;
  182.  
  183.           special = tmp;
  184.            }
  185.            else if( !(cp->ce_attr & A_LIBRARY) )
  186.           Warning("Duplicate entry [%s] in target list",cp->CE_NAME);
  187.         }
  188.      }
  189.      else {
  190.         /* found an operator so empty out break list
  191.          * and clear mark bits on target list, setting them all to F_USED */
  192.  
  193.         brk  = "";
  194.         for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) {
  195.            cp->ce_flag ^= F_MARK;
  196.            cp->ce_flag |= F_USED;
  197.         }
  198.  
  199.         Def_targets = FALSE;
  200.      }
  201.       }
  202.       else {
  203.          /* Scanning prerequisites so build the prerequisite list.  We use
  204.           * F_MARK flag to make certain we have only a single copy of the
  205.           * prerequisite in the list */
  206.  
  207.      cp = Def_cell( tok );
  208.  
  209.      if( _is_percent( tok ) ) {
  210.         if( !percent && !attr )
  211.            Fatal( "Syntax error in %% rule, missing %% target");
  212.         mixed_glob_prq = 1;
  213.      }
  214.  
  215.      if( cp->ce_flag & F_USED ) {
  216.         if( cp->ce_attr & A_COMPOSITE )
  217.            continue;
  218.         else
  219.            Fatal( "Detected circular dependency in graph at [%s]",
  220.               cp->CE_NAME );
  221.      }
  222.          else if( !(cp->ce_flag & F_MARK) ) {
  223.         DB_PRINT( "par", ("pq_cell [%s]", tok) );
  224.         cp->ce_flag |= F_MARK;
  225.  
  226.         if( prereqtail == NIL(CELL) )    /* keep prereq's in order */
  227.            prereq = cp;
  228.         else
  229.            prereqtail->ce_link = cp;
  230.  
  231.         prereqtail = cp;
  232.         cp->ce_link = NIL(CELL);
  233.      }
  234.      else if( !(cp->ce_attr & A_LIBRARY) )
  235.         Warning("Duplicate entry [%s] in prerequisite list",cp->CE_NAME);
  236.       }
  237.       
  238.    /* Check to see if we have a percent rule that has only global
  239.     * prerequisites.  If so then set the flag so that later on, we don't issue
  240.     * an error if such targets supply an empty set of rules. */
  241.  
  242.    if( percent && !mixed_glob_prq && (prereq != NIL(CELL)) )
  243.       _sv_globprq_only = 1;
  244.  
  245.    /* It's ok to have targets with attributes, and no prerequisites, but it's
  246.     * not ok to have no targets and no attributes, or no operator */
  247.  
  248.    if( !op ) {
  249.       CLEAR_TOKEN( &input );
  250.       DB_PRINT( "par", ("Not a rule [%s]", Buffer) );
  251.       DB_RETURN( 0 );
  252.    }
  253.  
  254.    if( !attr && targets == NIL(CELL) ) {
  255.       Fatal( "Missing targets or attributes in rule" );
  256.       if( set_dir != NIL( char )) FREE( set_dir );
  257.       DB_RETURN( 0 );
  258.    }
  259.  
  260.    /* We have established we have a legal rules line, so we must process it.
  261.     * In doing so we must handle any special targets.  Special targets must
  262.     * appear alone possibly accompanied by attributes.
  263.     * NOTE:  special != 0  ==> targets != NIL(CELL) */
  264.     
  265.    if( prereqtail != NIL(CELL) ) prereqtail->ce_link = NIL(CELL);
  266.  
  267.    /* Clear out MARK bits used in duplicate checking.  I originally wanted
  268.     * to do this as the lists get processed but that got too error prone
  269.     * so I bit the bullit and added these two loops. */
  270.  
  271.    for( cp=prereq;  cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_MARK;
  272.    for( cp=targets; cp != NIL(CELL); cp=cp->ce_link ) cp->ce_flag &= ~F_USED;
  273.  
  274.    /* Check to see if the previous rule line was bound if, not the call
  275.     * Bind_rules_to_targets to go and bind the line */
  276.  
  277.    if( _sv_rules != NIL(STRING) ) Bind_rules_to_targets( F_DEFAULT );
  278.  
  279.    /* Add the first recipe line to the list */
  280.    if( firstrcp != NIL( char ) )
  281.       Add_recipe_to_list( firstrcp, TRUE, FALSE );
  282.  
  283.    /* Save these prior to calling _do_targets, since _build_graph needs the
  284.     * _sv_setdir value for matching edges. */
  285.    _sv_op     = op;
  286.    _sv_setdir = set_dir;
  287.  
  288.    if( special )
  289.       _do_special( special, op, attr, set_dir, targets, prereq, state );
  290.    else
  291.       *state = _do_targets( op, attr, set_dir, targets, prereq );
  292.  
  293.    DB_RETURN( 1 );
  294. }
  295.  
  296.  
  297. PUBLIC int
  298. Rule_op( op )/*
  299. ================
  300.    Check the passed in op string and map it to one of the rule operators */
  301. char *op;
  302. {
  303.    int ret = 0;
  304.  
  305.    DB_ENTER( "rule_op" );
  306.    
  307.    if( *op == TGT_DEP_SEP ) {
  308.       ret = R_OP_CL;
  309.       op++;
  310.  
  311.       /* All rule operations begin with a :, but may include any one of the
  312.        * four modifiers.  In order for the rule to be properly mapped we must
  313.        * check for each of the modifiers in turn, building up our return bit
  314.        * string. */
  315.  
  316.       while( *op && ret )
  317.          switch( *op ) {
  318.         case ':': ret |= R_OP_DCL; op++; break;
  319.         case '!': ret |= R_OP_BG;  op++; break;
  320.         case '^': ret |= R_OP_UP;  op++; break;
  321.         case '-': ret |= R_OP_MI;  op++; break;
  322.  
  323.         default : ret  = 0;  /* an invalid modifier, chuck whole string */
  324.          }
  325.  
  326.       if( *op != '\0' ) ret = 0;
  327.    }
  328.  
  329.    DB_RETURN( ret );
  330. }
  331.  
  332.  
  333. PUBLIC void
  334. Add_recipe_to_list( rule, white_too, no_check )/*
  335. =================================================
  336.         Take the provided string and add it to the list of recipe lines
  337.     we are saving to be added to the list of targets we have built
  338.     previously.  If white_too == TRUE add the rule EVEN IF it contains only
  339.         whitespace. */
  340. char *rule;
  341. int  white_too;
  342. int  no_check;
  343. {
  344.    DB_ENTER( "Add_recipe_to_list" );
  345.  
  346.    if( rule != NIL( char ) && (*rule != '\0' || white_too) ) {
  347.       DB_PRINT( "par", ("Adding recipe [%s]", rule) );
  348.       _sv_crule = Def_recipe( rule, _sv_crule, white_too, no_check );
  349.  
  350.       if( _sv_rules == NIL(STRING) )
  351.          _sv_rules = _sv_crule;
  352.    }
  353.  
  354.    DB_VOID_RETURN;
  355. }
  356.  
  357.  
  358. PUBLIC void
  359. Bind_rules_to_targets( flag )/*
  360. ===============================
  361.         Take the rules we have defined and bind them with proper attributes
  362.         to the targets that were previously defined in the parse.  The
  363.         attributes that get passed here are merged with those that are were
  364.         previously defined.  (namely F_SINGLE) */
  365. int flag;
  366. {
  367.    CELLPTR tg;             /* pointer to current target in list */
  368.    LINKPTR lp;           /* pointer to link cell        */
  369.    int     magic;          /* TRUE if target is .xxx.yyy form   */
  370.    int     tflag;          /* TRUE if we assigned targets here  */
  371.  
  372.    DB_ENTER( "Bind_rules_to_targets" );
  373.  
  374.    /* This line is needed since Parse may call us twice when the last
  375.     * GROUP rule appears at the end of file.  In this case the rules
  376.     * have already been bound and we want to ignore them. */
  377.  
  378.    if( _sv_targets == NIL(CELL) ) { DB_VOID_RETURN; }
  379.  
  380.    tflag  = FALSE;
  381.    flag  |= (_sv_flag & F_SINGLE);
  382.  
  383.    for( tg = _sv_targets; tg != NIL(CELL); tg = tg->ce_link ) {
  384.       DB_PRINT( "par", ("Binding to %s, %04x", tg->CE_NAME, tg->ce_flag) );
  385.       magic = tg->ce_flag & F_PERCENT;
  386.  
  387.       /* Check to see if we had a rule of the form '%.o : a.h b.h ; xxx'
  388.        * In which case we must build a NULL prq node to hold the recipe */
  389.       if( _sv_globprq_only && (_sv_rules != NIL(STRING)) )
  390.      _build_graph( _sv_op, tg, NIL(CELL) );
  391.  
  392.       /* NOTE:  For targets that are magic we ignore any previously defined
  393.        *        rules.  ie. We throw away the old definition and use the new. */
  394.       if( !(tg->ce_flag & F_MULTI) && !magic && (tg->CE_RECIPE != NIL(STRING))
  395.       && !_sp_target && (_sv_rules != NIL(STRING)) )
  396.          Fatal( "Multiply defined recipe for target %s", tg->CE_NAME );
  397.  
  398.       if( (magic || _sp_target) && (_sv_rules == NIL(STRING)) &&
  399.       !(tg->ce_flag & F_SPECIAL) && !_sv_globprq_only )
  400.          Warning( "Empty recipe for special target %s", tg->CE_NAME );
  401.  
  402.       if( magic ) {
  403.      CELLPTR ep;
  404.  
  405.      for( ep=_sv_edgel; ep != NIL(CELL); ep=ep->ce_link ) {
  406.         _set_attributes( _sv_attr, _sv_setdir, ep );
  407.         ep->ce_flag |= (F_TARGET|flag);
  408.  
  409.         if( _sv_rules != NIL(STRING) ) {
  410.            ep->ce_recipe  = _sv_rules;
  411.            ep->ce_indprq  = _sv_glb_prq;
  412.         }
  413.      }
  414.       }
  415.       else {
  416.      tg->ce_attr |= _sv_attr;
  417.      tg->ce_flag |= flag;
  418.  
  419.      if( _sv_rules != NIL(STRING) ) {
  420.         tg->ce_recipe  = _sv_rules;
  421.         tg->ce_flag   |= F_RULES | F_TARGET;
  422.  
  423.         /* Bind the current set of prerequisites as belonging to the
  424.          * original recipe given for the target */
  425.         for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next )
  426.           if( !(lp->cl_flag & F_USED) ) lp->cl_flag |= F_TARGET;
  427.          }
  428.      else for( lp=tg->ce_prq; lp != NIL(LINK); lp = lp->cl_next )
  429.         lp->cl_flag |= F_USED;
  430.       }
  431.  
  432.       tflag |= _add_root(tg);
  433.    }
  434.  
  435.    if( tflag ) Target = TRUE;
  436.    if( _sv_setdir ) FREE(_sv_setdir);
  437.    _sv_rules   = NIL(STRING);
  438.    _sv_crule   = NIL(STRING);
  439.    _sv_targets = NIL(CELL);
  440.    _sv_glb_prq = NIL(LINK);
  441.    _sv_edgel   = NIL(CELL);
  442.    _sp_target  = FALSE;
  443.    _sv_globprq_only = 0;
  444.  
  445.    DB_VOID_RETURN;
  446. }
  447.  
  448.  
  449.  
  450. PUBLIC int
  451. Set_group_attributes( list )/*
  452. ==============================
  453.     Scan list looking for the standard @ and - (as in recipe line defs)
  454.     and set the flags accordingly so that they apply when we bind the
  455.     rules to the appropriate targets. */
  456. char *list;
  457. {
  458.    int res = (*_strspn(list,"@-%+ \t") == '[');
  459.    if( res ) _sv_attr |= Rcp_attribute(list);
  460.    return(res);
  461. }
  462.  
  463.  
  464. static void
  465. _do_special( special, op, attr, set_dir, target, prereq, state )/*
  466. ==================================================================
  467.    Process a special target.  So far the only special targets we have
  468.    are those recognized by the _is_special function.
  469.  
  470.    target is always only a single special target.
  471.    
  472.    NOTE:  For the cases of .IMPORT, and .INCLUDE, the cells created by the
  473.          parser are never freed.  This is due to the fact that it is too much
  474.       trouble to get them out of the hash table once they are defined, and
  475.       if they are per chance used again it will be ok, anyway, since the
  476.       cell is not really used by the code below.  */
  477.  
  478. int    special;
  479. int    op;
  480. t_attr    attr;
  481. char    *set_dir;
  482. CELLPTR target;
  483. CELLPTR prereq;
  484. int     *state;
  485. {
  486.    HASHPTR    hp;        /* pointer to macro def cell        */
  487.    CELLPTR    cp;        /* temporary pointer into cells list    */
  488.    CELLPTR     dp;        /* pointer to directory dir cell    */
  489.    LINKPTR     lp;        /* pointer at prerequisite list     */
  490.    char        *dir;        /* current dir to prepend        */
  491.    char        *path;        /* resulting path to try to read    */
  492.    char     *name;        /* File name for processing a .INCLUDE    */
  493.    char        *tmp;        /* temporary string pointer        */
  494.    FILE     *fil;        /* File descriptor returned by Openfile    */
  495.  
  496.    DB_ENTER( "_do_special" );
  497.  
  498.    target->ce_flag = F_SPECIAL;    /* mark the target as special */
  499.  
  500.    switch( special ) {
  501.       case ST_EXPORT:
  502.      for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
  503.         DB_PRINT( "par", ("Exporting [%s]", prereq->CE_NAME) );
  504.         hp = GET_MACRO( prereq->CE_NAME );
  505.  
  506.         if( hp != NIL(HASH) ) {
  507.            char *tmpstr = hp->ht_value;
  508.  
  509.            if( tmpstr == NIL(char) ) tmpstr = "";
  510.  
  511.            if( Write_env_string( prereq->CE_NAME, tmpstr ) != 0 )
  512.           Warning( "Could not export %s", prereq->CE_NAME );
  513.         }
  514.      }
  515.      break;
  516.  
  517.       case ST_IMPORT:
  518.      for( ; prereq != NIL(CELL); prereq = prereq->ce_link ) {
  519.         char *tmpstr;
  520.  
  521.         DB_PRINT( "par", ("Importing [%s]", prereq->CE_NAME) );
  522.  
  523.         if( strcmp(prereq->CE_NAME, ".EVERYTHING") == 0 ) {
  524.            t_attr sattr = Glob_attr;
  525.            Glob_attr |= A_SILENT;
  526.  
  527.            ReadEnvironment();
  528.  
  529.            Glob_attr = sattr;
  530.         }
  531.         else {
  532.            tmpstr = Read_env_string( prereq->CE_NAME );
  533.  
  534.            if( tmpstr != NIL(char) )
  535.           Def_macro( prereq->CE_NAME,tmpstr,M_EXPANDED | M_LITERAL);
  536.            else
  537.           if( !((Glob_attr | attr) & A_IGNORE) )
  538.              Fatal("Imported macro `%s' not found",prereq->CE_NAME);
  539.         }
  540.      }
  541.  
  542.      attr &= ~A_IGNORE;
  543.      break;
  544.  
  545.       case ST_INCLUDE:
  546.       {
  547.      int ignore     = (((Glob_attr | attr) & A_IGNORE) != 0);
  548.      int pushed     = FALSE;
  549.      LINKPTR prqlnk = NIL(LINK);
  550.      LINKPTR prqlst = NIL(LINK);
  551.  
  552.      if( prereq == NIL(CELL) )  Fatal( "No .INCLUDE file(s) specified" );
  553.  
  554.      dp = Def_cell( ".INCLUDEDIRS" );
  555.  
  556.      if( (attr & A_SETDIR) && *(dir = strchr(set_dir, '=')+1) )
  557.         pushed = Push_dir( dir, ".INCLUDE", ignore );
  558.  
  559.      for( cp=prereq; cp != NIL(CELL); cp = cp->ce_link ) {
  560.         LINKPTR ltmp;
  561.         TALLOC(ltmp, 1, LINK);
  562.         ltmp->cl_prq = cp;
  563.  
  564.         if( prqlnk == NIL(LINK) )
  565.            prqlst = ltmp;
  566.         else
  567.            prqlnk->cl_next = ltmp;
  568.  
  569.         prqlnk = ltmp;
  570.      }
  571.  
  572.      for( ; prqlst != NIL(LINK); FREE(prqlst), prqlst=prqlnk ) {
  573.         prqlnk = prqlst->cl_next;
  574.         cp     = prqlst->cl_prq;
  575.         name   = cp->CE_NAME;
  576.         
  577.         if( *name == '<' ) {
  578.            /* We have a file name enclosed in <....>
  579.             * so get rid of the <> arround the file name */
  580.  
  581.            name++;
  582.            if( (tmp = strrchr( name, '>' )) != NIL( char ) )
  583.           *tmp = 0;
  584.  
  585.            if( If_root_path( name ) )
  586.               fil = Openfile( name, FALSE, FALSE );
  587.            else
  588.           fil = NIL(FILE);
  589.         }
  590.         else
  591.            fil = Openfile( name, FALSE, FALSE );
  592.            
  593.         if( fil == NIL(FILE) ) {    /*if true ==> not found in current dir*/
  594.            /* Now we must scan the list of prerequisites for .INCLUDEDIRS
  595.             * looking for the file in each of the specified directories.
  596.         * if we don't find it then we issue an error.  The error
  597.         * message is suppressed if the .IGNORE attribute of attr is
  598.         * set.  If a file is found we call Parse on the file to
  599.         * perform the parse and then continue on from where we left
  600.         * off.  */
  601.  
  602.            for(lp=dp->CE_PRQ; lp && fil == NIL(FILE); lp=lp->cl_next) {
  603.           dir  = lp->cl_prq->CE_NAME;
  604.           if( strchr(dir, '$') ) dir = Expand(dir);
  605.           path = Build_path( dir, name );
  606.  
  607.           DB_PRINT( "par", ("Trying to include [%s]", path) );
  608.  
  609.           fil = Openfile( path, FALSE, FALSE );
  610.           if( dir != lp->cl_prq->CE_NAME ) FREE(dir);
  611.            }
  612.         }
  613.  
  614.         if( fil != NIL(FILE) )
  615.            Parse( fil );
  616.         else if( !((Glob_attr | attr) & A_IGNORE) )
  617.            Fatal( "Include file %s, not found", name );
  618.      }
  619.  
  620.      if( pushed ) Pop_dir(FALSE);
  621.      attr &= ~(A_IGNORE|A_SETDIR);
  622.       }
  623.       break;
  624.      
  625.       case ST_SOURCE:
  626.       /* case ST_SUFFIXES: */
  627.            if( prereq != NIL(CELL) )
  628.         _do_targets( op & (R_OP_CL | R_OP_MI | R_OP_UP), attr, set_dir,
  629.              target, prereq );
  630.      else {
  631.         /* The old semantics of .SOURCE were that an empty list of
  632.          * prerequisites clears the .SOURCE list.  So we must implement
  633.          * that here as a clearout prerequisite operation.  Since this is
  634.          * a standard operation with the :- opcode we can simply call the
  635.          * proper routine with the target cell and it should do the trick
  636.          */
  637.  
  638.         if( op == R_OP_CL || (op & R_OP_MI) )
  639.            Clear_prerequisites( target );
  640.      }
  641.  
  642.      op &= ~(R_OP_MI | R_OP_UP);
  643.      break;
  644.  
  645.       case ST_KEEP:
  646.      if( Keep_state != NIL(char) ) break;
  647.      Def_macro( ".KEEP_STATE", "_state.mk", M_EXPANDED );
  648.      break;
  649.  
  650.       case ST_REST:
  651.          /* The rest of the special targets can all take rules, as such they
  652.       * must be able to affect the state of the parser. */
  653.  
  654.      {
  655.         int s_targ = Target;
  656.  
  657.         Target     = TRUE;
  658.         _sp_target = TRUE;
  659.         *state     = _do_targets( op, attr, set_dir, target, prereq );
  660.         Target     = s_targ;
  661.  
  662.         target->ce_flag |= F_TARGET;
  663.  
  664.         attr    = A_DEFAULT;
  665.         op      = R_OP_CL;
  666.      }
  667.      break;
  668.  
  669.       default:break;
  670.    }
  671.       
  672.    if( op   != R_OP_CL   ) Warning( "Modifier(s) for operator ignored" );
  673.    if( attr != A_DEFAULT ) Warning( "Extra attributes ignored" );
  674.  
  675.    DB_VOID_RETURN;
  676. }
  677.  
  678.  
  679.  
  680. static int
  681. _do_targets( op, attr, set_dir, targets, prereq )/*
  682. ================================================= */
  683. int    op;
  684. t_attr    attr;
  685. char    *set_dir;
  686. CELLPTR targets;
  687. CELLPTR prereq;
  688. {
  689.    CELLPTR    tg1;        /* temporary target pointer        */
  690.    CELLPTR    tp1;        /* temporary prerequisite pointer    */
  691.    char        *p;        /* temporary char pointer        */
  692.    CELLPTR      prev_cell;    /* pointer for .UPDATEALL processing    */
  693.    CELLPTR      first_cell;    /* pointer for .UPDATEALL processing    */
  694.    int        update;        /* A_UPDATEALL attribute flag        */
  695.    int        smagic = 0;    /* collective amount of magic :-)    */
  696.    int          tflag = FALSE;    /* set to TRUE if we add target to root */
  697.  
  698.    DB_ENTER( "_do_targets" );
  699.  
  700.    if( update = ((attr & A_UPDATEALL) != 0) )
  701.       if( targets == NIL(CELL) )
  702.      Fatal( ".UPDATEALL attribute requires non-empty list of targets" );
  703.  
  704.    first_cell = prev_cell = NIL(CELL);
  705.    for( tg1 = targets; tg1 != NIL(CELL); tg1 = tg1->ce_link ) {
  706.       /* Check each target.  Check for inconsistencies between :: and : rule
  707.        * sets.  :: may follow either : or :: but not the reverse.
  708.        *
  709.        * Any targets that contain :: rules are represented by a prerequisite
  710.        * list hanging off the main target cell where each of the prerequisites
  711.        * is a copy of the target cell but is not entered into the hash table.
  712.        */
  713.       int magic  = (tg1->ce_flag & F_PERCENT) && !(tg1->ce_flag & F_MAGIC);
  714.       smagic |= magic;
  715.  
  716.       if( !(op & R_OP_DCL ) && (tg1->ce_flag & F_MULTI) && !magic )
  717.      Fatal( "Inconsistency in inference rules for %s", tg1->CE_NAME );
  718.  
  719.       if( magic )
  720.          do {
  721.         _build_graph( op, tg1, prereq );
  722.         if( prereq != NIL(CELL) ) prereq = prereq->ce_link;
  723.      } while( prereq != NIL(CELL) );
  724.       else if( !(tg1->ce_flag & F_SPECIAL) && 
  725.         (prereq == NIL(CELL)) &&
  726.         (p = _is_magic( tg1->CE_NAME )) != NIL(char))
  727.          smagic |= _do_magic( op, p, tg1, prereq, attr, set_dir );
  728.       else if( op & R_OP_DCL ) {
  729.      CELLPTR tmp_cell = _make_multi(tg1);
  730.      tflag |= _add_root(tg1);
  731.      targets = _replace_cell( targets, tg1, tmp_cell );
  732.      tg1 = tmp_cell;
  733.       }
  734.  
  735.       if( !magic ) _set_attributes( attr, set_dir, tg1 );
  736.  
  737.       if( update ) {
  738.      if( smagic ) Fatal( ".UPDATEALL attribute not legal in meta rule" );
  739.  
  740.      /* Check this as it would break another circular .UPATEALL list if
  741.       * we blindly assign it and it is part of another list already. */
  742.      if( tg1->ce_all != NIL(CELL) )
  743.         Fatal( "Target [%s] appears on multiple .UPDATEALL lists" );
  744.  
  745.      tg1->ce_all = prev_cell;
  746.      if( prev_cell == NIL(CELL) ) first_cell = tg1;
  747.      prev_cell = tg1;
  748.       }
  749.  
  750.       /* Build the proper prerequisite list of the target.  If the `-',
  751.        * modifier was used clear the prerequisite list before adding any
  752.        * new prerequisites.  Else add them to the head/tail as appropriate.
  753.        *
  754.        * If the target has F_PERCENT set then no prerequisites are used. */
  755.  
  756.       if( !(tg1->ce_flag & F_PERCENT) ) {
  757.      if( op & R_OP_MI ) Clear_prerequisites( tg1 );
  758.  
  759.      if( (op & R_OP_UP) && (tg1->ce_prq != NIL(LINK)) )
  760.         _stick_at_head( tg1, prereq );
  761.      else for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link )
  762.         Add_prerequisite( tg1, tp1, FALSE, FALSE );
  763.       }
  764.       else if( op & (R_OP_MI | R_OP_UP) )
  765.      Warning( "Modifier(s) `^!' for %-meta target ignored" );
  766.    }
  767.  
  768.    if( tflag ) Target = TRUE;
  769.    if( first_cell != NIL(CELL) ) first_cell->ce_all = prev_cell;
  770.  
  771.  
  772.    /* Check to see if we have NO targets but some attributes.  IF so then
  773.     * apply all of the attributes to the complete list of prerequisites.
  774.     * Cannot happen for F_PERCENT targets. (ie. in that case targets is always
  775.     * not NIL) */
  776.  
  777.    if( (targets == NIL(CELL)) && attr )
  778.       if( prereq != NIL(CELL) )
  779.      for( tp1=prereq; tp1 != NIL(CELL); tp1 = tp1->ce_link )
  780.         _set_attributes( attr, set_dir, tp1 );
  781.       else
  782.      _set_global_attr( attr );
  783.  
  784.    /* Fix up the ce_link pointers so that when we go to attach a recipe in
  785.     * Bind_targets to rules we get the right thing if it's an .UPDATEALL ::
  786.     * recipe */
  787.    if( update ) {
  788.       for( tp1=NIL(CELL),tg1=prev_cell; tg1!=first_cell; tg1=tg1->ce_all ) {
  789.      tg1->ce_link = tp1;
  790.      tp1 = tg1;
  791.       }
  792.       tg1->ce_link = tp1;
  793.       targets = first_cell;
  794.    }
  795.  
  796.    /* Now that we have built the lists of targets, the parser must parse the
  797.     * rules if there are any.  However we must start the rule list with the
  798.     * rule specified as via the ; kludge, if there is one */
  799.    _sv_targets = targets;
  800.    _sv_attr    = attr;
  801.    _sv_flag    = ((op & R_OP_BG) ? F_SINGLE : F_DEFAULT);
  802.       
  803.    DB_RETURN( RULE_SCAN );
  804. }
  805.  
  806.  
  807. static int
  808. _do_magic( op, dot, target, prereq, attr, set_dir )/*
  809. =====================================================
  810.    This function takes a magic target of the form .<chars>.<chars> or
  811.    .<chars> and builds the appropriate % rules for that target.
  812.    
  813.    The function builds the % rule, `%.o : %.c'  from .c.o, and
  814.    `%.a :' from .a */
  815.  
  816. int    op;
  817. char    *dot;
  818. CELLPTR target;
  819. CELLPTR prereq;
  820. t_attr  attr;
  821. char    *set_dir;
  822. {
  823.    CELLPTR tg;
  824.    CELLPTR prq;
  825.    char    *tmp, *tmp2;
  826.  
  827.    DB_ENTER( "_do_magic" );
  828.  
  829.    if( prereq != NIL(CELL) )
  830.       Warning( "Ignoring prerequisites of old style meta-target" );
  831.  
  832.    if( dot == target->CE_NAME )    {        /* its of the form .a    */
  833.       tg  = Def_cell( "%" );            /* ==> no prerequisite  */
  834.       tmp = _build_meta( target->CE_NAME );
  835.       prq = Def_cell( tmp );
  836.       FREE( tmp );
  837.  
  838.       _build_graph( op, tg, prq );
  839.    }
  840.    else {
  841.       tmp = _build_meta( dot );
  842.       tg  = Def_cell( tmp );
  843.       FREE( tmp );
  844.  
  845.       tmp = _build_meta( tmp2 = _substr( target->CE_NAME, dot ) );
  846.       prq = Def_cell( tmp );
  847.       FREE( tmp  );
  848.       FREE( tmp2 );
  849.  
  850.       _build_graph( op, tg, prq );
  851.    }
  852.  
  853.    tg->ce_flag      |= F_PERCENT;
  854.    target->ce_flag  |= (F_MAGIC|F_PERCENT);
  855.  
  856.    _set_attributes( attr, set_dir, tg );
  857.  
  858.    DB_RETURN(1);
  859. }
  860.  
  861.  
  862. static CELLPTR
  863. _replace_cell( lst, cell, rep )
  864. CELLPTR lst;
  865. CELLPTR cell;
  866. CELLPTR rep;
  867. {
  868.    register CELLPTR tp;
  869.    
  870.    if( lst == cell ) {
  871.       rep->ce_link = lst->ce_link;
  872.       lst = rep;
  873.    }
  874.    else {
  875.       for( tp=lst; tp->ce_link != cell; tp=tp->ce_link );
  876.       rep->ce_link = tp->ce_link->ce_link;
  877.       tp->ce_link = rep;
  878.    }
  879.  
  880.    return(lst);
  881. }
  882.  
  883.  
  884. static char *
  885. _build_meta( name )/*
  886. =====================
  887.    Check to see if the name is of the form .c~ if so and if Augmake
  888.    translation is enabled then return s.%.c, else return %.suff, where if the
  889.    suffix ends in '~' then leave it be.*/
  890. char *name;
  891. {
  892.    char *tmp;
  893.    int  test = Augmake ? name[strlen(name)-1] == '~' : 0;
  894.  
  895.    tmp = _strjoin( test ? "s.%" : "%", name, -1, FALSE);
  896.    if( test ) tmp[ strlen(tmp)-1 ] = '\0';
  897.  
  898.    return(tmp);
  899. }
  900.  
  901.  
  902.  
  903. static void
  904. _build_graph( op, target, prereq )/*
  905. ====================================
  906.    This function is called to build the graph for the % rule given by
  907.    target : prereq cell combination.  This function assumes that target
  908.    is a % target and that prereq is a single % prerequisite.  R_OP_CL
  909.    rules replace existing rules if any, only R_OP_CL works for meta-rules.
  910.    %.o :: %.c is meaningless. 
  911.    
  912.    It also assumes that target cell has F_PERCENT set already. */
  913. int op;
  914. CELLPTR target;
  915. CELLPTR prereq;
  916. {
  917.    LINKPTR edl;
  918.    CELLPTR edge;
  919.    int match;
  920.  
  921.    DB_ENTER( "_build_graph" );
  922.    DB_PRINT( "%", ("Building graph for [%s : %s]", target->CE_NAME,
  923.             (prereq == NIL(CELL)) ? "" : prereq->CE_NAME) );
  924.  
  925.    if( prereq != NIL(CELL) ) {
  926.       char *name = prereq->CE_NAME;
  927.       int   len  = strlen(name);
  928.  
  929.       if( *name == '\'' && name[len-1]=='\'' ){
  930.      _add_global_prereq( prereq );
  931.      name[len-1] = '\0';
  932.      strcpy(name, name+1);
  933.      DB_VOID_RETURN;
  934.       }
  935.    }
  936.  
  937.    /* Search the list of prerequisites for the current target and see if
  938.     * any of them match the current %-meta : prereq pair.  NOTE that %-metas
  939.     * are built as if they were F_MULTI targets. */
  940.  
  941.    match = FALSE;
  942.    for( edl=target->ce_prq; edl != NIL(LINK); edl=edl->cl_next ) {
  943.       edge = edl->cl_prq;
  944.  
  945.       DB_PRINT( "%", ("Trying to match [%s]", edge?edge->CE_NAME:"(nil)") );
  946.  
  947.       if(    (!edge->ce_prq && !prereq)
  948.       || (   edge->ce_prq
  949.           && edge->ce_prq->cl_prq == prereq
  950.           && (   edge->ce_dir == _sv_setdir
  951.           || (   edge->ce_dir
  952.               && _sv_setdir
  953.               && !strcmp(edge->ce_dir,strchr(_sv_setdir,'=')+1)
  954.              )
  955.          )
  956.          )
  957.     ) {
  958.      match = TRUE;
  959.      break;
  960.       }
  961.    }
  962.  
  963.    if( match ) {
  964.       /* match is TRUE hence, we found an edge joining the target and the
  965.        * prerequisite so reset the new edge's how values to reflect the new
  966.        * recipe etc. */
  967.       DB_PRINT( "%", ("It's an old edge") );
  968.  
  969.       edge->ce_dir    = NIL(char);
  970.       edge->ce_flag  &= (F_PERCENT|F_MAGIC|F_DFA);
  971.       edge->ce_attr  &= A_NOINFER;
  972.    }
  973.    else {
  974.       DB_PRINT( "%", ("Adding a new edge") );
  975.  
  976.       if( !(target->ce_flag & F_DFA) ) {
  977.      Add_nfa( target->CE_NAME );
  978.      target->ce_flag |= F_DFA;
  979.       }
  980.       edge = _make_multi(target);
  981.       if( prereq ) Add_prerequisite(edge, prereq, FALSE, TRUE);
  982.    }
  983.  
  984.    if( op & R_OP_DCL )
  985.    Warning( "'::' operator for meta-target '%s' ignored, ':' operator assumed.",
  986.            target->CE_NAME );
  987.  
  988.    edge->ce_link = _sv_edgel;
  989.    _sv_edgel = edge;
  990.    _sv_globprq_only = 0;
  991.  
  992.    DB_VOID_RETURN;
  993. }
  994.  
  995.  
  996. static CELLPTR
  997. _make_multi( tg )
  998. CELLPTR tg;
  999. {
  1000.    CELLPTR cp;
  1001.  
  1002.    /* This first handle the case when a : foo ; exists prior to seeing
  1003.     * a :: fee; */
  1004.    if( !(tg->ce_flag & F_MULTI) && (tg->ce_prq || tg->ce_recipe) ) {
  1005.       TALLOC(cp, 1, CELL);
  1006.       *cp = *tg;
  1007.  
  1008.       tg->ce_prq    = NIL(LINK);
  1009.       tg->ce_flag  |= F_RULES|F_MULTI|F_TARGET;
  1010.       tg->ce_attr  |= A_SEQ;
  1011.       tg->ce_recipe = NIL(STRING);
  1012.       tg->ce_dir    = NIL(char);
  1013.       cp->ce_count  = ++tg->ce_index;
  1014.  
  1015.       Add_prerequisite(tg, cp, FALSE, TRUE);
  1016.    }
  1017.  
  1018.    TALLOC(cp, 1, CELL);
  1019.    *cp = *tg;
  1020.  
  1021.    if( !(tg->ce_flag & F_MULTI) ) {
  1022.       tg->ce_prq    = NIL(LINK);
  1023.       tg->ce_flag  |= F_RULES|F_MULTI|F_TARGET;
  1024.       tg->ce_attr  |= A_SEQ;
  1025.       tg->ce_recipe = NIL(STRING);
  1026.       tg->ce_dir    = NIL(char);
  1027.    }
  1028.    else {
  1029.       cp->ce_flag  &= ~(F_RULES|F_MULTI);
  1030.       cp->ce_attr  &= ~A_SEQ;
  1031.       cp->ce_prq    = NIL(LINK);
  1032.       cp->ce_index  = 0;
  1033.    }
  1034.    cp->ce_count = ++tg->ce_index;
  1035.    cp->ce_flag |= F_TARGET;
  1036.  
  1037.    Add_prerequisite(tg, cp, FALSE, TRUE);
  1038.    return(cp);
  1039. }
  1040.  
  1041.  
  1042. static void
  1043. _add_global_prereq( pq )/*
  1044. ==========================
  1045.     Prerequisite is a non-% prerequisite for a %-rule target, add it to
  1046.     the target's list of global prerequsites to add on match */
  1047. CELLPTR pq;
  1048. {
  1049.    register LINKPTR ln;
  1050.  
  1051.    TALLOC( ln, 1, LINK );
  1052.    ln->cl_next = _sv_glb_prq;
  1053.    ln->cl_prq  = pq;
  1054.    _sv_glb_prq = ln;
  1055. }
  1056.  
  1057.  
  1058.  
  1059. static void
  1060. _set_attributes( attr, set_dir, cp )/*
  1061. ======================================
  1062.     Set the appropriate attributes for a cell */
  1063. t_attr    attr;
  1064. char    *set_dir;
  1065. CELLPTR cp;
  1066. {
  1067.    char   *dir;
  1068.  
  1069.    DB_ENTER( "_set_attributes" );
  1070.  
  1071.    /* If .SETDIR attribute is set then we have at least .SETDIR= in the
  1072.     * set_dir string.  So go and fishout what is at the end of the =.
  1073.     * If not set and not NULL then propagate it to the target cell. */
  1074.  
  1075.    if( attr & A_SETDIR ) {
  1076.       dir = strchr( set_dir, '=' ) + 1;
  1077.  
  1078.       if( cp->ce_dir )
  1079.      Warning( "Multiple .SETDIR for %s ignored", cp->CE_NAME );
  1080.       else
  1081.      if( *dir ) cp->ce_dir = _strdup(dir);
  1082.    }
  1083.    cp->ce_attr |= attr;        /* set rest of attributes for target */
  1084.  
  1085.    DB_VOID_RETURN;
  1086. }
  1087.  
  1088.  
  1089.  
  1090. static void
  1091. _set_global_attr( attr )/*
  1092. ==========================
  1093.     Handle the setting of the global attribute functions based on
  1094.     The attribute flags set in attr. */
  1095. t_attr attr;
  1096. {
  1097.    t_attr flag;
  1098.  
  1099.    /* Some compilers can't handle a switch on a long, and t_attr is now a long
  1100.     * integer on some systems.  foey! */
  1101.    for( flag = MAX_ATTR; flag; flag >>= 1 )
  1102.       if( flag & attr )
  1103.      if( flag == A_PRECIOUS)      Def_macro(".PRECIOUS",  "y", M_EXPANDED);
  1104.      else if( flag == A_SILENT)   Def_macro(".SILENT",    "y", M_EXPANDED);
  1105.      else if( flag == A_IGNORE )  Def_macro(".IGNORE",    "y", M_EXPANDED);
  1106.      else if( flag == A_EPILOG )  Def_macro(".EPILOG",    "y", M_EXPANDED);
  1107.      else if( flag == A_PROLOG )  Def_macro(".PROLOG",    "y", M_EXPANDED);
  1108.      else if( flag == A_NOINFER ) Def_macro(".NOINFER",   "y", M_EXPANDED);
  1109.      else if( flag == A_SEQ )     Def_macro(".SEQUENTIAL","y", M_EXPANDED);
  1110.      else if( flag == A_SHELL )   Def_macro(".USESHELL",  "y", M_EXPANDED);
  1111.      else if( flag == A_MKSARGS ) Def_macro(".MKSARGS",   "y", M_EXPANDED);
  1112.      else if( flag == A_SWAP )    Def_macro(".SWAP",      "y", M_EXPANDED);
  1113.  
  1114.    attr &= ~A_GLOB;
  1115.    if( attr ) Warning( "Non global attribute(s) ignored" );
  1116. }
  1117.  
  1118.  
  1119.  
  1120. static void
  1121. _stick_at_head( cp, pq )/*
  1122. ==========================
  1123.     Add the prerequisite list to the head of the existing prerequisite
  1124.     list */
  1125.  
  1126. CELLPTR cp;          /* cell for target node    */
  1127. CELLPTR pq;        /* list of prerequisites to add */
  1128. {
  1129.    DB_ENTER( "_stick_at_head" );
  1130.  
  1131.    if( pq->ce_link != NIL(CELL) ) _stick_at_head( cp, pq->ce_link );
  1132.    Add_prerequisite( cp, pq, TRUE, FALSE );
  1133.  
  1134.    DB_VOID_RETURN;
  1135. }
  1136.  
  1137.  
  1138.  
  1139. static t_attr
  1140. _is_attribute( name )/*
  1141. =======================
  1142.    Check the passed name against the list of valid attributes and return the
  1143.    attribute index if it is, else return 0, indicating the name is not a valid
  1144.    attribute.  The present attributes are defined in dmake.h as A_xxx #defines,
  1145.    with the corresponding makefile specification:  (note they must be named
  1146.    exactly as defined below)
  1147.    
  1148.    Valid attributes are:  .IGNORE, .SETDIR=, .SILENT, .PRECIOUS, .LIBRARY,
  1149.                           .EPILOG, .PROLOG,  .LIBRARYM, .SYMBOL, .UPDATEALL,
  1150.               .USESHELL, .NOINFER, .PHONY, .SWAP, .SEQUENTIAL
  1151.               .NOSTATE,  .MKSARGS
  1152.  
  1153.    NOTE:  The strcmp's are OK since at most three are ever executed for any
  1154.           one attribute check, and that happens only when we can be fairly
  1155.           certain we have an attribute.  */
  1156. char *name;
  1157. {
  1158.    t_attr attr = 0;
  1159.    
  1160.    DB_ENTER( "_is_attribute" );
  1161.    
  1162.    if( *name++ == '.' )
  1163.       switch( *name )
  1164.       {
  1165.          case 'E': attr = (strcmp(name, "EPILOG"))   ? 0 : A_EPILOG;  break;
  1166.          case 'I': attr = (strcmp(name, "IGNORE"))   ? 0 : A_IGNORE;  break;
  1167.          case 'L': attr = (strcmp(name, "LIBRARY"))  ? 0 : A_LIBRARY; break;
  1168.          case 'M': attr = (strcmp(name, "MKSARGS"))  ? 0 : A_MKSARGS; break;
  1169.  
  1170.          case 'N':
  1171.         if( !strcmp(name, "NOINFER") )      attr = A_NOINFER;
  1172.         else if( !strcmp(name, "NOSTATE"))  attr = A_NOSTATE;
  1173.         else attr = 0;
  1174.         break;
  1175.  
  1176.          case 'U':
  1177.         if( !strcmp(name, "UPDATEALL") )    attr = A_UPDATEALL;
  1178.         else if( !strcmp(name, "USESHELL")) attr = A_SHELL;
  1179.         else attr = 0;
  1180.         break;
  1181.  
  1182.          case 'P':
  1183.             if( !strcmp(name, "PRECIOUS") )     attr = A_PRECIOUS;
  1184.             else if( !strcmp(name, "PROLOG") )  attr = A_PROLOG;
  1185.             else if( !strcmp(name, "PHONY") )   attr = A_PHONY;
  1186.             else attr = 0;
  1187.             break;
  1188.  
  1189.          case 'S':
  1190.             if( !strncmp(name, "SETDIR=", 7) )    attr = A_SETDIR;
  1191.             else if( !strcmp(name, "SILENT") )    attr = A_SILENT;
  1192.             else if( !strcmp(name, "SYMBOL") )    attr = A_SYMBOL;
  1193.             else if( !strcmp(name, "SEQUENTIAL")) attr = A_SEQ;
  1194.             else if( !strcmp(name, "SWAP"))       attr = A_SWAP;
  1195.             else attr = 0;
  1196.             break;
  1197.       }
  1198.  
  1199.    DB_RETURN( attr );
  1200. }
  1201.  
  1202.  
  1203.  
  1204. static int
  1205. _is_special( tg )/*
  1206. ===================
  1207.    This function returns TRUE if the name passed in represents a special
  1208.    target, otherwise it returns false.  A special target is one that has
  1209.    a special meaning to dmake, and may require processing at the time that
  1210.    it is parsed.
  1211.    
  1212.    Current Special targets are:
  1213.     .GROUPPROLOG    .GROUPEPILOG    .INCLUDE    .IMPORT
  1214.     .EXPORT        .SOURCE     .SUFFIXES    .ERROR
  1215.     .INCLUDEDIRS    .MAKEFILES    .REMOVE        .KEEP_STATE
  1216. */
  1217. char *tg;
  1218. {
  1219.    DB_ENTER( "_is_special" );
  1220.    
  1221.    if( *tg++ != '.' ) DB_RETURN( 0 );
  1222.    
  1223.    switch( *tg )
  1224.    {
  1225.       case 'I':
  1226.          if( !strcmp( tg, "IMPORT" ) )        DB_RETURN( ST_IMPORT   );
  1227.          else if( !strcmp( tg, "INCLUDE" ) )    DB_RETURN( ST_INCLUDE  );
  1228.      else if( !strcmp( tg, "INCLUDEDIRS" )) DB_RETURN( ST_REST     );
  1229.      break;
  1230.       
  1231.       case 'M':
  1232.          if( !strcmp( tg, "MAKEFILES" ) )    DB_RETURN( ST_REST     );
  1233.      break;
  1234.  
  1235.       case 'E':
  1236.          if( !strcmp( tg, "ERROR" ) )        DB_RETURN( ST_REST     );
  1237.          else if( !strcmp( tg, "EXPORT" ) )    DB_RETURN( ST_EXPORT   );
  1238.      break;
  1239.  
  1240.       case 'G':
  1241.      if( !strcmp( tg, "GROUPPROLOG" ))      DB_RETURN( ST_REST     );
  1242.      else if( !strcmp( tg, "GROUPEPILOG" )) DB_RETURN( ST_REST     );
  1243.      break;
  1244.  
  1245.       case 'K':
  1246.          if( !strcmp( tg, "KEEP_STATE" ) )    DB_RETURN( ST_KEEP     );
  1247.      break;
  1248.  
  1249.       case 'R':
  1250.          if( !strcmp( tg, "REMOVE" ) )        DB_RETURN( ST_REST     );
  1251.      break;
  1252.  
  1253.       case 'S':
  1254.          if( !strncmp( tg, "SOURCE", 6 ) )    DB_RETURN( ST_SOURCE   );
  1255.          else if( !strncmp(tg, "SUFFIXES", 8 )) DB_RETURN( ST_SOURCE   );
  1256.      break;
  1257.    }
  1258.    
  1259.    DB_RETURN( 0 );
  1260. }
  1261.  
  1262.  
  1263.  
  1264. static int
  1265. _is_percent( np )/*
  1266. ===================
  1267.     return TRUE if np points at a string containing a % sign */
  1268. char *np;
  1269. {
  1270.    return( (strchr(np,'%') && (*np != '\'' && np[strlen(np)-1] != '\'')) ?
  1271.        TRUE : FALSE );
  1272. }
  1273.  
  1274.  
  1275. static char *
  1276. _is_magic( np )/*
  1277. =================
  1278.     return TRUE if np points at a string of the form
  1279.           .<chars>.<chars>  or  .<chars>
  1280.     where chars are only alpha characters.
  1281.  
  1282.         NOTE:  reject target if it begins with ./ or ../ */
  1283. char *np;
  1284. {
  1285.    register char *n;
  1286.  
  1287.    n = np;
  1288.    if( *n != '.' ) return( NIL(char) );
  1289.    if (strchr(DirBrkStr, *(n+1))!=NULL || *(n+1) == '.' )
  1290.       return (NIL(char));
  1291.  
  1292.    for( n++; isgraph(*n) && (*n != '.'); n++ );
  1293.  
  1294.    if( *n != '\0' ) {
  1295.       if( *n != '.' )  return( NIL(char) );
  1296.       for( np = n++; isgraph( *n ) && (*n != '.'); n++ );
  1297.       if( *n != '\0' ) return( NIL(char) );
  1298.    }
  1299.    else if( !Augmake )
  1300.       return( NIL(char) );
  1301.  
  1302.    /* np points at the second . of .<chars>.<chars> string.
  1303.     * if the special target is of the form .<chars> then np points at the
  1304.     * first . in the token. */
  1305.  
  1306.    return( np );
  1307. }
  1308.  
  1309.  
  1310. static int
  1311. _add_root(tg)
  1312. CELLPTR tg;
  1313. {
  1314.    int res = FALSE;
  1315.  
  1316.    if( !Target && !(tg->ce_flag & (F_SPECIAL|F_PERCENT)) ) {
  1317.       Add_prerequisite( Root, tg, FALSE, TRUE );
  1318.  
  1319.       tg->ce_flag |= F_TARGET;
  1320.       tg->ce_attr |= A_FRINGE;
  1321.       res          = TRUE;
  1322.    }
  1323.  
  1324.    return(res);
  1325. }
  1326.