[BACK]Return to parse.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

Diff for /src/usr.bin/make/parse.c between version 1.91 and 1.92

version 1.91, 2007/09/17 11:32:25 version 1.92, 2007/09/17 12:42:09
Line 91 
Line 91 
 #include "parsevar.h"  #include "parsevar.h"
 #include "stats.h"  #include "stats.h"
 #include "garray.h"  #include "garray.h"
   #include "node_int.h"
   #include "nodehashconsts.h"
   
   
   /* gsources and gtargets should be local to some functions, but they're
    * set as persistent arrays for performance reasons.
    */
 static struct growableArray gsources, gtargets;  static struct growableArray gsources, gtargets;
 #define SOURCES_SIZE    128  #define SOURCES_SIZE    128
 #define TARGETS_SIZE    32  #define TARGETS_SIZE    32
Line 110 
Line 115 
                                  * first target on the first dependency                                   * first target on the first dependency
                                  * line in the first makefile */                                   * line in the first makefile */
 /*-  /*-
  * specType contains the SPECial TYPE of the current target. It is   * specType contains the special TYPE of the current target. It is
  * Not if the target is unspecial. If it *is* special, however, the children   * SPECIAL_NONE if the target is unspecial. If it *is* special, however,
  * are linked as children of the parent but not vice versa. This variable is   * the children are linked as children of the parent but not vice versa.
  * set in ParseDoDependency   * This variable is set in ParseDoDependency
  */   */
 typedef enum {  
     Begin,          /* .BEGIN */  
     Default,        /* .DEFAULT */  
     End,            /* .END */  
     Ignore,         /* .IGNORE */  
     Includes,       /* .INCLUDES */  
     Interrupt,      /* .INTERRUPT */  
     Libs,           /* .LIBS */  
     MFlags,         /* .MFLAGS or .MAKEFLAGS */  
     Main,           /* .MAIN and we don't have anything user-specified to  
                      * make */  
     NoExport,       /* .NOEXPORT */  
     NoPath,         /* .NOPATH */  
     Not,            /* Not special */  
     NotParallel,    /* .NOTPARALELL */  
     Null,           /* .NULL */  
     Order,          /* .ORDER */  
     Parallel,       /* .PARALLEL */  
     ExPath,         /* .PATH */  
     Phony,          /* .PHONY */  
     Precious,       /* .PRECIOUS */  
     Silent,         /* .SILENT */  
     SingleShell,    /* .SINGLESHELL */  
     Suffixes,       /* .SUFFIXES */  
     Wait,           /* .WAIT */  
     Attribute       /* Generic attribute */  
 } ParseSpecial;  
   
 static ParseSpecial specType;  static int specType;
 static int waiting;  static int waiting;
   
 /*  /*
Line 152 
Line 130 
  */   */
 static GNode    *predecessor;  static GNode    *predecessor;
   
 /*  
  * The parseKeywords table is searched using binary search when deciding  
  * if a target or source is special. The 'spec' field is the ParseSpecial  
  * type of the keyword ("Not" if the keyword isn't special as a target) while  
  * the 'op' field is the operator to apply to the list of targets if the  
  * keyword is used as a source ("0" if the keyword isn't special as a source)  
  */  
 static struct {  
     char          *name;        /* Name of keyword */  
     ParseSpecial  spec;         /* Type when used as a target */  
     int           op;           /* Operator when used as a source */  
 } parseKeywords[] = {  
 { ".BEGIN",       Begin,        0 },  
 { ".DEFAULT",     Default,      0 },  
 { ".END",         End,          0 },  
 { ".EXEC",        Attribute,    OP_EXEC },  
 { ".IGNORE",      Ignore,       OP_IGNORE },  
 { ".INCLUDES",    Includes,     0 },  
 { ".INTERRUPT",   Interrupt,    0 },  
 { ".INVISIBLE",   Attribute,    OP_INVISIBLE },  
 { ".JOIN",        Attribute,    OP_JOIN },  
 { ".LIBS",        Libs,         0 },  
 { ".MADE",        Attribute,    OP_MADE },  
 { ".MAIN",        Main,         0 },  
 { ".MAKE",        Attribute,    OP_MAKE },  
 { ".MAKEFLAGS",   MFlags,       0 },  
 { ".MFLAGS",      MFlags,       0 },  
 #if 0   /* basic scaffolding for NOPATH, not working yet */  
 { ".NOPATH",      NoPath,       OP_NOPATH },  
 #endif  
 { ".NOTMAIN",     Attribute,    OP_NOTMAIN },  
 { ".NOTPARALLEL", NotParallel,  0 },  
 { ".NO_PARALLEL", NotParallel,  0 },  
 { ".NULL",        Null,         0 },  
 { ".OPTIONAL",    Attribute,    OP_OPTIONAL },  
 { ".ORDER",       Order,        0 },  
 { ".PARALLEL",    Parallel,     0 },  
 { ".PATH",        ExPath,       0 },  
 { ".PHONY",       Phony,        OP_PHONY },  
 { ".PRECIOUS",    Precious,     OP_PRECIOUS },  
 { ".RECURSIVE",   Attribute,    OP_MAKE },  
 { ".SILENT",      Silent,       OP_SILENT },  
 { ".SINGLESHELL", SingleShell,  0 },  
 { ".SUFFIXES",    Suffixes,     0 },  
 { ".USE",         Attribute,    OP_USE },  
 { ".WAIT",        Wait,         0 },  
 };  
   
 static int ParseFindKeyword(const char *);  
 static void ParseLinkSrc(GNode *, GNode *);  static void ParseLinkSrc(GNode *, GNode *);
 static int ParseDoOp(GNode *, int);  static int ParseDoOp(GNode **, int);
 static int ParseAddDep(GNode *, GNode *);  static int ParseAddDep(GNode *, GNode *);
 static void ParseDoSrc(int, const char *);  static void ParseDoSrc(struct growableArray *, struct growableArray *, int,
       const char *, const char *);
 static int ParseFindMain(void *, void *);  static int ParseFindMain(void *, void *);
 static void ParseAddDir(void *, void *);  
 static void ParseClearPath(void *);  static void ParseClearPath(void *);
   
 static void add_target_node(const char *);  static void add_target_node(const char *, const char *);
 static void add_target_nodes(const char *);  static void add_target_nodes(const char *, const char *);
 static void ParseDoDependency(char *);  static void apply_op(struct growableArray *, int, GNode *);
   static void ParseDoDependency(const char *);
 static void ParseAddCmd(void *, void *);  static void ParseAddCmd(void *, void *);
 static void ParseHasCommands(void *);  static void ParseHasCommands(void *);
 static bool handle_poison(const char *);  static bool handle_poison(const char *);
Line 227 
Line 157 
 static void lookup_sysv_include(const char *, const char *);  static void lookup_sysv_include(const char *, const char *);
 static void lookup_conditional_include(const char *, const char *);  static void lookup_conditional_include(const char *, const char *);
 static bool parse_as_special_line(Buffer, Buffer, const char *);  static bool parse_as_special_line(Buffer, Buffer, const char *);
   
   static const char *parse_do_targets(Lst, int *, const char *);
 static void parse_target_line(struct growableArray *, const char *,  static void parse_target_line(struct growableArray *, const char *,
     const char *);      const char *);
   
 /*-  static void finish_commands(struct growableArray *);
  *----------------------------------------------------------------------  static void parse_commands(struct growableArray *, const char *);
  * ParseFindKeyword --  static void create_special_nodes(void);
  *      Look in the table of keywords for one matching the given string.  static bool found_delimiter(const char *);
  *  static int handle_special_targets(Lst);
  * Results:  
  *      The index of the keyword, or -1 if it isn't there.  
  *----------------------------------------------------------------------  
  */  
 static int  
 ParseFindKeyword(const char *str)       /* keyword to look up */  
 {  
     int             start,  
                     end,  
                     cur;  
     int             diff;  
   
     start = 0;  #define SPECIAL_EXEC            4
     end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1;  #define SPECIAL_IGNORE          5
   #define SPECIAL_INCLUDES        6
   #define SPECIAL_INVISIBLE       8
   #define SPECIAL_JOIN            9
   #define SPECIAL_LIBS            10
   #define SPECIAL_MADE            11
   #define SPECIAL_MAIN            12
   #define SPECIAL_MAKE            13
   #define SPECIAL_MFLAGS          14
   #define SPECIAL_NOTMAIN         15
   #define SPECIAL_NOTPARALLEL     16
   #define SPECIAL_NULL            17
   #define SPECIAL_OPTIONAL        18
   #define SPECIAL_ORDER           19
   #define SPECIAL_PARALLEL        20
   #define SPECIAL_PHONY           22
   #define SPECIAL_PRECIOUS        23
   #define SPECIAL_SILENT          25
   #define SPECIAL_SINGLESHELL     26
   #define SPECIAL_SUFFIXES        27
   #define SPECIAL_USE             28
   #define SPECIAL_WAIT            29
   #define SPECIAL_NOPATH          30
   #define SPECIAL_ERROR           31
   
     do {  
         cur = start + (end - start) / 2;  
         diff = strcmp(str, parseKeywords[cur].name);  
   
         if (diff == 0) {  #define P(k) k, sizeof(k), K_##k
             return cur;  
         } else if (diff < 0) {  static struct {
             end = cur - 1;          const char *keyword;
         } else {          size_t sz;
             start = cur + 1;          uint32_t hv;
           int type;
           int special_op;
   } specials[] = {
       { P(NODE_EXEC),     SPECIAL_EXEC | SPECIAL_TARGETSOURCE,    OP_EXEC, },
       { P(NODE_IGNORE),   SPECIAL_IGNORE | SPECIAL_TARGETSOURCE,  OP_IGNORE, },
       { P(NODE_INCLUDES), SPECIAL_INCLUDES | SPECIAL_TARGET,      0, },
       { P(NODE_INVISIBLE),SPECIAL_INVISIBLE | SPECIAL_TARGETSOURCE,OP_INVISIBLE, },
       { P(NODE_JOIN),     SPECIAL_JOIN | SPECIAL_TARGETSOURCE,    OP_JOIN, },
       { P(NODE_LIBS),     SPECIAL_LIBS | SPECIAL_TARGET,          0, },
       { P(NODE_MADE),     SPECIAL_MADE | SPECIAL_TARGETSOURCE,    OP_MADE, },
       { P(NODE_MAIN),     SPECIAL_MAIN | SPECIAL_TARGET,          0, },
       { P(NODE_MAKE),     SPECIAL_MAKE | SPECIAL_TARGETSOURCE,    OP_MAKE, },
       { P(NODE_MAKEFLAGS),        SPECIAL_MFLAGS | SPECIAL_TARGET,        0, },
       { P(NODE_MFLAGS),   SPECIAL_MFLAGS | SPECIAL_TARGET,        0, },
       { P(NODE_NOTMAIN),  SPECIAL_NOTMAIN | SPECIAL_TARGETSOURCE, OP_NOTMAIN, },
       { P(NODE_NOTPARALLEL),SPECIAL_NOTPARALLEL | SPECIAL_TARGET, 0, },
       { P(NODE_NO_PARALLEL),SPECIAL_NOTPARALLEL | SPECIAL_TARGET, 0, },
       { P(NODE_NULL),     SPECIAL_NULL | SPECIAL_TARGET,          0, },
       { P(NODE_OPTIONAL), SPECIAL_OPTIONAL | SPECIAL_TARGETSOURCE,OP_OPTIONAL, },
       { P(NODE_ORDER),    SPECIAL_ORDER | SPECIAL_TARGET,         0, },
       { P(NODE_PARALLEL), SPECIAL_PARALLEL | SPECIAL_TARGET,      0, },
       { P(NODE_PATH),     SPECIAL_PATH | SPECIAL_TARGET,          0, },
       { P(NODE_PHONY),    SPECIAL_PHONY | SPECIAL_TARGETSOURCE,   OP_PHONY, },
       { P(NODE_PRECIOUS), SPECIAL_PRECIOUS | SPECIAL_TARGETSOURCE,OP_PRECIOUS, },
       { P(NODE_RECURSIVE),SPECIAL_MAKE | SPECIAL_TARGETSOURCE,    OP_MAKE, },
       { P(NODE_SILENT),   SPECIAL_SILENT | SPECIAL_TARGETSOURCE,  OP_SILENT, },
       { P(NODE_SINGLESHELL),SPECIAL_SINGLESHELL | SPECIAL_TARGET, 0, },
       { P(NODE_SUFFIXES), SPECIAL_SUFFIXES | SPECIAL_TARGET,      0, },
       { P(NODE_USE),      SPECIAL_USE | SPECIAL_TARGETSOURCE,     OP_USE, },
       { P(NODE_WAIT),     SPECIAL_WAIT | SPECIAL_TARGETSOURCE,    0 },
   #if 0
           { P(NODE_NOPATH),       SPECIAL_NOPATH, },
   #endif
   };
   
   #undef P
   
   static void
   create_special_nodes()
   {
           unsigned int i;
   
           for (i = 0; i < sizeof(specials)/sizeof(specials[0]); i++) {
                   GNode *gn = Targ_FindNodeh(specials[i].keyword,
                       specials[i].sz, specials[i].hv, TARG_CREATE);
                   gn->special = specials[i].type;
                   gn->special_op = specials[i].special_op;
         }          }
     } while (start <= end);  
     return -1;  
 }  }
   
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * ParseLinkSrc  --   * ParseLinkSrc  --
Line 282 
Line 269 
     GNode               *pgn,   /* The parent node */      GNode               *pgn,   /* The parent node */
     GNode               *cgn)   /* The child node */      GNode               *cgn)   /* The child node */
 {  {
         if (Lst_AddNew(&pgn->children, cgn)) {      if (Lst_AddNew(&pgn->children, cgn)) {
                 if (specType == Not)          if (specType == SPECIAL_NONE)
                         Lst_AtEnd(&cgn->parents, pgn);              Lst_AtEnd(&cgn->parents, pgn);
                 pgn->unmade++;          pgn->unmade++;
         }      }
 }  }
   
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * ParseDoOp  --   * ParseDoOp  --
  *      Apply the parsed operator to the given target node. Used in a   *      Apply the parsed operator to the given target node. Used in a
  *      Lst_Find call by ParseDoDependency once all targets have   *      Array_Find call by ParseDoDependency once all targets have
  *      been found and their operator parsed. If the previous and new   *      been found and their operator parsed. If the previous and new
  *      operators are incompatible, a major error is taken.   *      operators are incompatible, a major error is taken.
  *   *
Line 304 
Line 291 
  */   */
 static int  static int
 ParseDoOp(  ParseDoOp(
     GNode          *gn, /* The node to which the operator is to be applied */      GNode          **gnp,
     int            op)  /* The operator to apply */      int            op)  /* The operator to apply */
 {  {
         /*      GNode *gn = *gnp;
          * If the dependency mask of the operator and the node don't match and      /*
          * the node has actually had an operator applied to it before, and       * If the dependency mask of the operator and the node don't match and
          * the operator actually has some dependency information in it, complain.       * the node has actually had an operator applied to it before, and
          */       * the operator actually has some dependency information in it, complain.
         if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) &&       */
                 !OP_NOP(gn->type) && !OP_NOP(op)) {      if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) &&
                 Parse_Error(PARSE_FATAL,          !OP_NOP(gn->type) && !OP_NOP(op)) {
                     "Inconsistent operator for %s", gn->name);          Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name);
                 return 0;          return 0;
         }      }
   
         if (op == OP_DOUBLEDEP && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) {      if (op == OP_DOUBLEDEP && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) {
                 /* If the node was the object of a :: operator, we need to          /* If the node was the object of a :: operator, we need to create a
                  * create a new instance of it for the children and commands on           * new instance of it for the children and commands on this dependency
                  * this dependency line. The new instance is placed on the           * line. The new instance is placed on the 'cohorts' list of the
                  * 'cohorts' list of the initial one (note the initial one is           * initial one (note the initial one is not on its own cohorts list)
                  * not on its own cohorts list) and the new instance is linked           * and the new instance is linked to all parents of the initial
                  * to all parents of the initial instance.  */           * instance.  */
                 GNode           *cohort;          GNode           *cohort;
                 LstNode         ln;          LstNode         ln;
                 unsigned int i;  
   
                 cohort = Targ_NewGN(gn->name);          cohort = Targ_NewGN(gn->name);
                 /* Duplicate links to parents so graph traversal is simple.          /* Duplicate links to parents so graph traversal is simple. Perhaps
                  * Perhaps some type bits should be duplicated?           * some type bits should be duplicated?
                  *           *
                  * Make the cohort invisible as well to avoid duplicating it           * Make the cohort invisible as well to avoid duplicating it into
                  * into other variables. True, parents of this target won't           * other variables. True, parents of this target won't tend to do
                  * tend to do anything with their local variables, but better           * anything with their local variables, but better safe than
                  * safe than sorry.  */           * sorry.  */
                 for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln))          for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln))
                         ParseLinkSrc((GNode *)Lst_Datum(ln), cohort);              ParseLinkSrc((GNode *)Lst_Datum(ln), cohort);
                 cohort->type = OP_DOUBLEDEP|OP_INVISIBLE;          cohort->type = OP_DOUBLEDEP|OP_INVISIBLE;
                 Lst_AtEnd(&gn->cohorts, cohort);          Lst_AtEnd(&gn->cohorts, cohort);
   
                 /* Replace the node in the targets list with the new copy */          /* Replace the node in the targets list with the new copy */
                 for (i = 0; i < gtargets.n; i++)          *gnp = cohort;
                         if (gtargets.a[i] == gn)          gn = cohort;
                                 break;      }
                 gtargets.a[i] = cohort;      /* We don't want to nuke any previous flags (whatever they were) so we
                 gn = cohort;       * just OR the new operator into the old.  */
         }      gn->type |= op;
         /* We don't want to nuke any previous flags (whatever they were) so we      return 1;
          * just OR the new operator into the old.  */  
         gn->type |= op;  
         return 1;  
 }  }
   
 /*-  /*-
Line 375 
Line 358 
 static int  static int
 ParseAddDep(GNode *p, GNode *s)  ParseAddDep(GNode *p, GNode *s)
 {  {
         if (p->order < s->order) {      if (p->order < s->order) {
                 /* XXX: This can cause loops, and loops can cause unmade          /* XXX: This can cause loops, and loops can cause unmade targets,
                  * targets, but checking is tedious, and the debugging output           * but checking is tedious, and the debugging output can show the
                  * can show the problem.  */           * problem.  */
                 Lst_AtEnd(&p->successors, s);          Lst_AtEnd(&p->successors, s);
                 Lst_AtEnd(&s->preds, p);          Lst_AtEnd(&s->preds, p);
                 return 1;          return 1;
         }      }
       else
           return 0;
   }
   
   static void
   apply_op(struct growableArray *targets, int op, GNode *gn)
   {
           if (op)
                   gn->type |= op;
         else          else
                 return 0;                  Array_ForEach(targets, ParseLinkSrc, gn);
 }  }
   
   
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * ParseDoSrc  --   * ParseDoSrc  --
Line 404 
Line 395 
  */   */
 static void  static void
 ParseDoSrc(  ParseDoSrc(
       struct growableArray *targets,
       struct growableArray *sources,
     int         tOp,    /* operator (if any) from special targets */      int         tOp,    /* operator (if any) from special targets */
     const char  *src)   /* name of the source to handle */      const char  *src,   /* name of the source to handle */
       const char *esrc)
 {  {
         GNode   *gn = NULL;          GNode *gn = Targ_FindNodei(src, esrc, TARG_CREATE);
           if ((gn->special & SPECIAL_SOURCE) != 0) {
         if (*src == '.' && isupper(src[1])) {                  if (gn->special_op) {
                 int keywd = ParseFindKeyword(src);                          Array_FindP(targets, ParseDoOp, gn->special_op);
                 if (keywd != -1) {                          return;
                         int op = parseKeywords[keywd].op;                  } else {
                         if (op != 0) {                          assert((gn->special & SPECIAL_MASK) == SPECIAL_WAIT);
                                 Array_Find(&gtargets, ParseDoOp, op);                          waiting++;
                                 return;                          return;
                         }  
                         if (parseKeywords[keywd].spec == Wait) {  
                                 waiting++;  
                                 return;  
                         }  
                 }                  }
         }          }
   
         switch (specType) {          switch (specType) {
         case Main:          case SPECIAL_MAIN:
                 /*                  /*
                  * If we have noted the existence of a .MAIN, it means we need                   * If we have noted the existence of a .MAIN, it means we need
                  * to add the sources of said target to the list of things                   * to add the sources of said target to the list of things
                  * to create. The string 'src' is likely to be freed, so we                   * to create.  Note that this will only be invoked if the user
                  * must make a new copy of it. Note that this will only be                   * didn't specify a target on the command line. This is to
                  * invoked if the user didn't specify a target on the command                   * allow #ifmake's to succeed, or something...
                  * line. This is to allow #ifmake's to succeed, or something...  
                  */                   */
                 Lst_AtEnd(create, estrdup(src));                  Lst_AtEnd(create, gn->name);
                 /*                  /*
                  * Add the name to the .TARGETS variable as well, so the user                   * Add the name to the .TARGETS variable as well, so the user
                  * can employ that, if desired.                   * can employ that, if desired.
                  */                   */
                 Var_Append(".TARGETS", src);                  Var_Append(".TARGETS", gn->name);
                 return;                  return;
   
         case Order:          case SPECIAL_ORDER:
                 /*                  /*
                  * Create proper predecessor/successor links between the                   * Create proper predecessor/successor links between the
                  * previous source and the current one.                   * previous source and the current one.
                  */                   */
                 gn = Targ_FindNode(src, TARG_CREATE);  
                 if (predecessor != NULL) {                  if (predecessor != NULL) {
                         Lst_AtEnd(&predecessor->successors, gn);                          Lst_AtEnd(&predecessor->successors, gn);
                         Lst_AtEnd(&gn->preds, predecessor);                          Lst_AtEnd(&gn->preds, predecessor);
                 }                  }
                 /*  
                  * The current source now becomes the predecessor for the next  
                  * one.  
                  */  
                 predecessor = gn;                  predecessor = gn;
                 break;                  break;
   
         default:          default:
                 /*                  /*
                  * If the source is not an attribute, we need to find/create  
                  * a node for it. After that we can apply any operator to it  
                  * from a special target or link it to its parents, as  
                  * appropriate.  
                  *  
                  * In the case of a source that was the object of a :: operator,                   * In the case of a source that was the object of a :: operator,
                  * the attribute is applied to all of its instances (as kept in                   * the attribute is applied to all of its instances (as kept in
                  * the 'cohorts' list of the node) or all the cohorts are linked                   * the 'cohorts' list of the node) or all the cohorts are linked
                  * to all the targets.                   * to all the targets.
                  */                   */
                 gn = Targ_FindNode(src, TARG_CREATE);                  apply_op(targets, tOp, gn);
                 if (tOp) {  
                         gn->type |= tOp;  
                 } else {  
                         Array_ForEach(&gtargets, ParseLinkSrc, gn);  
                 }  
                 if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) {                  if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
                         GNode   *cohort;  
                         LstNode ln;                          LstNode ln;
   
                         for (ln=Lst_First(&gn->cohorts); ln != NULL;                          for (ln=Lst_First(&gn->cohorts); ln != NULL;
                             ln = Lst_Adv(ln)){                              ln = Lst_Adv(ln)){
                                 cohort = (GNode *)Lst_Datum(ln);                                  apply_op(targets, tOp,
                                 if (tOp) {                                      (GNode *)Lst_Datum(ln));
                                         cohort->type |= tOp;  
                                 } else {  
                                         Array_ForEach(&gtargets, ParseLinkSrc,  
                                             cohort);  
                                 }  
                         }                          }
                 }                  }
                 break;                  break;
         }          }
   
         gn->order = waiting;          gn->order = waiting;
         Array_AtEnd(&gsources, gn);          Array_AtEnd(sources, gn);
         if (waiting) {          if (waiting)
                 Array_Find(&gsources, ParseAddDep, gn);                  Array_Find(sources, ParseAddDep, gn);
         }  
 }  }
   
 /*-  /*-
Line 513 
Line 479 
  *      1 if main not found yet, 0 if it is.   *      1 if main not found yet, 0 if it is.
  *   *
  * Side Effects:   * Side Effects:
  *      mainNode is changed and Targ_SetMain is called.   *      mainNode is changed and.
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static int  static int
Line 521 
Line 487 
     void *gnp,      /* Node to examine */      void *gnp,      /* Node to examine */
     void *dummy         UNUSED)      void *dummy         UNUSED)
 {  {
         GNode     *gn = (GNode *)gnp;      GNode         *gn = (GNode *)gnp;
         if ((gn->type & OP_NOTARGET) == 0) {      if ((gn->type & OP_NOTARGET) == 0 && gn->special == SPECIAL_NONE) {
                 mainNode = gn;          mainNode = gn;
                 Targ_SetMain(gn);          return 0;
                 return 0;      } else {
         } else {          return 1;
                 return 1;      }
         }  
 }  }
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * ParseAddDir --  
  *      Front-end for Dir_AddDir to make sure Lst_ForEach keeps going  
  *  
  * Side Effects:  
  *      See Dir_AddDir.  
  *-----------------------------------------------------------------------  
  */  
 static void  
 ParseAddDir(void *path, void *name)  
 {  
         Dir_AddDir((Lst)path, (char *)name);  
 }  
   
 /*-  
  *-----------------------------------------------------------------------  
  * ParseClearPath --   * ParseClearPath --
  *      Reinit path to an empty path   *      Reinit path to an empty path
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
Line 555 
Line 505 
 static void  static void
 ParseClearPath(void *p)  ParseClearPath(void *p)
 {  {
         Lst     path = (Lst)p;      Lst         path = (Lst)p;
   
         Lst_Destroy(path, Dir_Destroy);      Lst_Destroy(path, Dir_Destroy);
         Lst_Init(path);      Lst_Init(path);
 }  }
   
 static void  static void
 add_target_node(const char *line)  add_target_node(const char *line, const char *end)
 {  {
         GNode *gn;          GNode *gn;
   
         if (!Suff_IsTransform(line))          gn = Suff_ParseAsTransform(line, end);
                 gn = Targ_FindNode(line, TARG_CREATE);  
         else  
                 gn = Suff_AddTransform(line);  
   
           if (gn == NULL) {
                   gn = Targ_FindNodei(line, end, TARG_CREATE);
                   gn->type &= ~OP_DUMMY;
           }
   
         if (gn != NULL)          if (gn != NULL)
                 Array_AtEnd(&gtargets, gn);                  Array_AtEnd(&gtargets, gn);
 }  }
   
 static void  static void
 add_target_nodes(const char *line)  add_target_nodes(const char *line, const char *end)
 {  {
   
         if (Dir_HasWildcards(line)) {          if (Dir_HasWildcardsi(line, end)) {
                 /*                  /*
                  * Targets are to be sought only in the current directory,                   * Targets are to be sought only in the current directory,
                  * so create an empty path for the thing. Note we need to                   * so create an empty path for the thing. Note we need to
Line 592 
Line 544 
   
                 Lst_Init(&emptyPath);                  Lst_Init(&emptyPath);
                 Lst_Init(&curTargs);                  Lst_Init(&curTargs);
                 Dir_Expand(line, &emptyPath, &curTargs);                  Dir_Expandi(line, end, &emptyPath, &curTargs);
                 Lst_Destroy(&emptyPath, Dir_Destroy);                  Lst_Destroy(&emptyPath, Dir_Destroy);
                 while ((targName = (char *)Lst_DeQueue(&curTargs)) != NULL) {                  while ((targName = (char *)Lst_DeQueue(&curTargs)) != NULL) {
                         add_target_node(targName);                          add_target_node(targName, targName + strlen(targName));
                 }                  }
                 Lst_Destroy(&curTargs, NOFREE);                  Lst_Destroy(&curTargs, NOFREE);
         } else {          } else {
                 add_target_node(line);                  add_target_node(line, end);
         }          }
 }  }
   
   /* special target line check: a proper delimiter is a ':' or '!', but
    * we don't want to end a target on such a character if there is a better
    * match later on.
    * By "better" I mean one that is followed by whitespace. This allows the
    * user to have targets like:
    *    fie::fi:fo: fum
    * where "fie::fi:fo" is the target.  In real life this is used for perl5
    * library man pages where "::" separates an object from its class.  Ie:
    * "File::Spec::Unix".
    * This behaviour is also consistent with other versions of make.
    */
   static bool
   found_delimiter(const char *s)
   {
           if (*s == '!' || *s == ':') {
                   const char *p = s + 1;
   
                   if (*s == ':' && *p == ':')
                           p++;
   
                   /* Found the best match already. */
                   if (isspace(*p) || *p == '\0')
                           return true;
   
                   do {
                           p += strcspn(p, "!:");
                           if (*p == '\0')
                               break;
                           p++;
                   } while (!isspace(*p));
   
                   /* No better match later on... */
                   if (*p == '\0')
                           return true;
           }
           return false;
   }
   
   static const char *
   parse_do_targets(Lst paths, int *op, const char *line)
   {
           const char *cp;
   
           do {
                   for (cp = line; *cp && !isspace(*cp) && *cp != '(';) {
                           if (*cp == '$')
                                   /* Must be a dynamic source (would have been
                                    * expanded otherwise), so call the Var module
                                    * to parse the puppy so we can safely advance
                                    * beyond it...There should be no errors in
                                    * this, as they would have been discovered in
                                    * the initial Var_Subst and we wouldn't be
                                    * here.  */
                                   Var_ParseSkip(&cp, NULL);
                           else {
                                   if (found_delimiter(cp))
                                           break;
                                   cp++;
                           }
                   }
   
                   if (*cp == '(') {
                           LIST temp;
                           Lst_Init(&temp);
                           /* Archives must be handled specially to make sure the
                            * OP_ARCHV flag is set in their 'type' field, for one
                            * thing, and because things like "archive(file1.o
                            * file2.o file3.o)" are permissible.
                            * Arch_ParseArchive will set 'line' to be the first
                            * non-blank after the archive-spec. It creates/finds
                            * nodes for the members and places them on the given
                            * list, returning true if all went well and false if
                            * there was an error in the specification. On error,
                            * line should remain untouched.  */
                           if (!Arch_ParseArchive(&line, &temp, NULL)) {
                                   Parse_Error(PARSE_FATAL,
                                        "Error in archive specification: \"%s\"",
                                        line);
                                   return NULL;
                           } else {
                                   AppendList2Array(&temp, &gtargets);
                                   Lst_Destroy(&temp, NOFREE);
                                   cp = line;
                                   continue;
                           }
                   }
                   if (*cp == '\0') {
                           /* Ending a dependency line without an operator is a
                            * Bozo no-no */
                           Parse_Error(PARSE_FATAL, "Need an operator");
                           return NULL;
                   }
                   /*
                    * Have word in line. Get or create its nodes and stick it at
                    * the end of the targets list
                    */
                   if (*line != '\0')
                           add_target_nodes(line, cp);
   
                   while (isspace(*cp))
                           cp++;
                   line = cp;
           } while (*line != '!' && *line != ':' && *line);
           *op = handle_special_targets(paths);
           return cp;
   }
   
   static void
   dump_targets()
   {
           size_t i;
           for (i = 0; i < gtargets.n; i++)
                   fprintf(stderr, "%s", gtargets.a[i]->name);
           fprintf(stderr, "\n");
   }
   
   static int
   handle_special_targets(Lst paths)
   {
           size_t i;
           int seen_path = 0;
           int seen_special = 0;
           int seen_normal = 0;
           int type;
   
           for (i = 0; i < gtargets.n; i++) {
                   type = gtargets.a[i]->special;
                   if ((type & SPECIAL_MASK) == SPECIAL_PATH) {
                           seen_path++;
                           Lst_AtEnd(paths, find_suffix_path(gtargets.a[i]));
                   } else if ((type & SPECIAL_TARGET) != 0)
                           seen_special++;
                   else
                           seen_normal++;
           }
           if ((seen_path != 0) + (seen_special != 0) + (seen_normal != 0) > 1) {
                   Parse_Error(PARSE_FATAL, "Wrong mix of special targets");
                   dump_targets();
                   specType = SPECIAL_ERROR;
                   return 0;
           }
           if (seen_normal != 0) {
                   specType = SPECIAL_NONE;
                   return 0;
           }
           else if (seen_path != 0) {
                   specType = SPECIAL_PATH;
                   return 0;
           } else if (seen_special == 0) {
                   specType = SPECIAL_NONE;
                   return 0;
           } else if (seen_special != 1) {
                   Parse_Error(PARSE_FATAL, "Mixing special targets is not allowed");
                   dump_targets();
                   return 0;
           } else if (seen_special == 1) {
                   specType = gtargets.a[0]->special & SPECIAL_MASK;
                   switch (specType) {
                   case SPECIAL_MAIN:
                           if (!Lst_IsEmpty(create)) {
                                   specType = SPECIAL_NONE;
                           }
                           break;
                   case SPECIAL_NOTPARALLEL:
                   {
                           extern int  maxJobs;
   
                           maxJobs = 1;
                           break;
                   }
                   case SPECIAL_SINGLESHELL:
                           compatMake = 1;
                           break;
                   case SPECIAL_ORDER:
                           predecessor = NULL;
                           break;
                   default:
                           break;
                   }
                   return gtargets.a[0]->special_op;
           } else {
                   /* we're allowed to have 0 target */
                   specType = SPECIAL_NONE;
                   return 0;
           }
   }
   
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * ParseDoDependency  --   * ParseDoDependency  --
Line 635 
Line 774 
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  */   */
 static void  static void
 ParseDoDependency(char *line)   /* the line to parse */  ParseDoDependency(const char *line)     /* the line to parse */
 {  {
     char           *cp;         /* our current position */          const char *cp;         /* our current position */
     GNode          *gn;         /* a general purpose temporary node */          int op;                 /* the operator on the line */
     int             op;         /* the operator on the line */          LIST paths;             /* List of search paths to alter when parsing
     char            savec;      /* a place to save a character */                                  * a list of .PATH targets */
     LIST            paths;      /* List of search paths to alter when parsing          int tOp;                /* operator from special target */
                                  * a list of .PATH targets */  
     int             tOp;        /* operator from special target */  
     tOp = 0;  
   
     specType = Not;  
     waiting = 0;  
     Lst_Init(&paths);  
   
     Array_Reset(&gsources);          waiting = 0;
           Lst_Init(&paths);
   
     do {          Array_Reset(&gsources);
         for (cp = line; *cp && !isspace(*cp) && *cp != '(';)  
             if (*cp == '$')  
                 /* Must be a dynamic source (would have been expanded  
                  * otherwise), so call the Var module to parse the puppy  
                  * so we can safely advance beyond it...There should be  
                  * no errors in this, as they would have been discovered  
                  * in the initial Var_Subst and we wouldn't be here.  */  
                 Var_ParseSkip(&cp, NULL);  
             else {  
                 /* We don't want to end a word on ':' or '!' if there is a  
                  * better match later on in the string.  By "better" I mean  
                  * one that is followed by whitespace.  This allows the user  
                  * to have targets like:  
                  *    fie::fi:fo: fum  
                  * where "fie::fi:fo" is the target.  In real life this is used  
                  * for perl5 library man pages where "::" separates an object  
                  * from its class.  Ie: "File::Spec::Unix".  This behaviour  
                  * is also consistent with other versions of make.  */  
                 if (*cp == '!' || *cp == ':') {  
                     char *p = cp + 1;  
   
                     if (*cp == ':' && *p == ':')          cp = parse_do_targets(&paths, &tOp, line);
                         p++;          if (cp == NULL || specType == SPECIAL_ERROR)
                   return;
   
                     /* Found the best match already. */          /* Have now parsed all the target names. Must parse the operator next.
                     if (isspace(*p) || *p == '\0')           * The result is left in op .  */
                         break;          if (*cp == '!') {
                   op = OP_FORCE;
                     do {          } else if (*cp == ':') {
                         p += strcspn(p, "!:");                  if (cp[1] == ':') {
                         if (*p == '\0')                          op = OP_DOUBLEDEP;
                             break;                          cp++;
                         p++;                  } else {
                     } while (!isspace(*p));                          op = OP_DEPENDS;
   
                     /* No better match later on... */  
                     if (*p == '\0')  
                         break;  
   
                 }                  }
                 cp++;          } else {
             }                  Parse_Error(PARSE_FATAL, "Missing dependency operator");
         if (*cp == '(') {  
             LIST temp;  
             Lst_Init(&temp);  
             /* Archives must be handled specially to make sure the OP_ARCHV  
              * flag is set in their 'type' field, for one thing, and because  
              * things like "archive(file1.o file2.o file3.o)" are permissible.  
              * Arch_ParseArchive will set 'line' to be the first non-blank  
              * after the archive-spec. It creates/finds nodes for the members  
              * and places them on the given list, returning true if all  
              * went well and false if there was an error in the  
              * specification. On error, line should remain untouched.  */  
             if (!Arch_ParseArchive(&line, &temp, NULL)) {  
                 Parse_Error(PARSE_FATAL,  
                              "Error in archive specification: \"%s\"", line);  
                 return;                  return;
             } else {  
                 AppendList2Array(&temp, &gtargets);  
                 Lst_Destroy(&temp, NOFREE);  
                 continue;  
             }  
         }          }
         savec = *cp;  
   
         if (*cp == '\0') {          cp++;                   /* Advance beyond operator */
             /* Ending a dependency line without an operator is a Bozo no-no */  
             Parse_Error(PARSE_FATAL, "Need an operator");  
             return;  
         }  
         *cp = '\0';  
         /* Have a word in line. See if it's a special target and set  
          * specType to match it.  */  
         if (*line == '.' && isupper(line[1])) {  
             /* See if the target is a special target that must have it  
              * or its sources handled specially.  */  
             int keywd = ParseFindKeyword(line);  
             if (keywd != -1) {  
                 if (specType == ExPath && parseKeywords[keywd].spec != ExPath) {  
                     Parse_Error(PARSE_FATAL, "Mismatched special targets");  
                     return;  
                 }  
   
                 specType = parseKeywords[keywd].spec;          Array_FindP(&gtargets, ParseDoOp, op);
                 tOp = parseKeywords[keywd].op;  
   
                 /*          /*
                  * Certain special targets have special semantics:           * Get to the first source
                  *      .PATH           Have to set the defaultPath           */
                  *                      variable too          while (isspace(*cp))
                  *      .MAIN           Its sources are only used if                  cp++;
                  *                      nothing has been specified to          line = cp;
                  *                      create.  
                  *      .DEFAULT        Need to create a node to hang          /*
                  *                      commands on, but we don't want           * Several special targets take different actions if present with no
                  *                      it in the graph, nor do we want           * sources:
                  *                      it to be the Main Target, so we           *      a .SUFFIXES line with no sources clears out all old suffixes
                  *                      create it, set OP_NOTMAIN and           *      a .PRECIOUS line makes all targets precious
                  *                      add it to the list, setting           *      a .IGNORE line ignores errors for all targets
                  *                      DEFAULT to the new node for           *      a .SILENT line creates silence when making all targets
                  *                      later use. We claim the node is           *      a .PATH removes all directories from the search path(s).
                  *                      A transformation rule to make           */
                  *                      life easier later, when we'll          if (!*line) {
                  *                      use Make_HandleUse to actually  
                  *                      apply the .DEFAULT commands.  
                  *      .PHONY          The list of targets  
                  *      .NOPATH         Don't search for file in the path  
                  *      .BEGIN  
                  *      .END  
                  *      .INTERRUPT      Are not to be considered the  
                  *                      main target.  
                  *      .NOTPARALLEL    Make only one target at a time.  
                  *      .SINGLESHELL    Create a shell for each command.  
                  *      .ORDER          Must set initial predecessor to NULL  
                  */  
                 switch (specType) {                  switch (specType) {
                     case ExPath:                  case SPECIAL_SUFFIXES:
                         Lst_AtEnd(&paths, defaultPath);                          Suff_ClearSuffixes();
                         break;                          break;
                     case Main:                  case SPECIAL_PRECIOUS:
                         if (!Lst_IsEmpty(create)) {                          allPrecious = true;
                             specType = Not;  
                         }  
                         break;                          break;
                     case Begin:                  case SPECIAL_IGNORE:
                     case End:                          ignoreErrors = true;
                     case Interrupt:  
                         gn = Targ_FindNode(line, TARG_CREATE);  
                         gn->type |= OP_NOTMAIN;  
                         Array_AtEnd(&gtargets, gn);  
                         break;                          break;
                     case Default:                  case SPECIAL_SILENT:
                         gn = Targ_NewGN(".DEFAULT");                          beSilent = true;
                         gn->type |= OP_NOTMAIN|OP_TRANSFORM;  
                         Array_AtEnd(&gtargets, gn);  
                         DEFAULT = gn;  
                         break;                          break;
                     case NotParallel:                  case SPECIAL_PATH:
                     {                          Lst_Every(&paths, ParseClearPath);
                         extern int  maxJobs;  
   
                         maxJobs = 1;  
                         break;                          break;
                     }                  default:
                     case SingleShell:  
                         compatMake = 1;  
                         break;                          break;
                     case Order:  
                         predecessor = NULL;  
                         break;  
                     default:  
                         break;  
                 }                  }
             } else if (strncmp(line, ".PATH", 5) == 0) {          } else if (specType == SPECIAL_MFLAGS) {
                 /*                  /*Call on functions in main.c to deal with these arguments */
                  * .PATH<suffix> has to be handled specially.                  Main_ParseArgLine(line);
                  * Call on the suffix module to give us a path to                  return;
                  * modify.          } else if (specType == SPECIAL_NOTPARALLEL ||
                  */              specType == SPECIAL_SINGLESHELL) {
                 Lst     path;                  return;
   
                 specType = ExPath;  
                 path = Suff_GetPath(&line[5]);  
                 if (path == NULL) {  
                     Parse_Error(PARSE_FATAL,  
                                  "Suffix '%s' not defined (yet)",  
                                  &line[5]);  
                     return;  
                 } else {  
                     Lst_AtEnd(&paths, path);  
                 }  
             }  
         }          }
   
         /*          /*
          * Have word in line. Get or create its node and stick it at           * NOW GO FOR THE SOURCES
          * the end of the targets list  
          */           */
         if (specType == Not && *line != '\0') {          if (specType == SPECIAL_SUFFIXES || specType == SPECIAL_PATH ||
             add_target_nodes(line);              specType == SPECIAL_INCLUDES || specType == SPECIAL_LIBS ||
         } else if (specType == ExPath && *line != '.' && *line != '\0')              specType == SPECIAL_NULL) {
             Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line);                  while (*line) {
                       /*
                        * If the target was one that doesn't take files as its
                        * sources but takes something like suffixes, we take each
                        * space-separated word on the line as a something and deal
                        * with it accordingly.
                        *
                        * If the target was .SUFFIXES, we take each source as a
                        * suffix and add it to the list of suffixes maintained by
                        * the Suff module.
                        *
                        * If the target was a .PATH, we add the source as a
                        * directory to search on the search path.
                        *
                        * If it was .INCLUDES, the source is taken to be the
                        * suffix of files which will be #included and whose search
                        * path should be present in the .INCLUDES variable.
                        *
                        * If it was .LIBS, the source is taken to be the suffix of
                        * files which are considered libraries and whose search
                        * path should be present in the .LIBS variable.
                        *
                        * If it was .NULL, the source is the suffix to use when a
                        * file has no valid suffix.
                        */
                       while (*cp && !isspace(*cp))
                               cp++;
                       switch (specType) {
                       case SPECIAL_SUFFIXES:
                               Suff_AddSuffixi(line, cp);
                               break;
                       case SPECIAL_PATH:
                               {
                               LstNode ln;
   
         *cp = savec;                              for (ln = Lst_First(&paths); ln != NULL;
         /*                                  ln = Lst_Adv(ln))
          * If it is a special type and not .PATH, it's the only target we                                      Dir_AddDiri((Lst)Lst_Datum(ln), line, cp);
          * allow on this line...                              break;
          */                              }
         if (specType != Not && specType != ExPath) {                      case SPECIAL_INCLUDES:
             bool warn = false;                              Suff_AddIncludei(line, cp);
                               break;
             while (*cp != '!' && *cp != ':' && *cp) {                      case SPECIAL_LIBS:
                 if (*cp != ' ' && *cp != '\t') {                              Suff_AddLibi(line, cp);
                     warn = true;                              break;
                       case SPECIAL_NULL:
                               Suff_SetNulli(line, cp);
                               break;
                       default:
                               break;
                       }
                       if (*cp != '\0')
                           cp++;
                       while (isspace(*cp))
                           cp++;
                       line = cp;
                 }                  }
                 cp++;                  Lst_Destroy(&paths, NOFREE);
             }  
             if (warn) {  
                 Parse_Error(PARSE_WARNING, "Extra target ignored");  
             }  
         } else {          } else {
             while (isspace(*cp)) {                  while (*line) {
                 cp++;                          /*
             }                           * The targets take real sources, so we must beware of
         }                           * archive specifications (i.e. things with left
         line = cp;                           * parentheses in them) and handle them accordingly.
     } while (*line != '!' && *line != ':' && *line);                           */
                           while (*cp && !isspace(*cp)) {
                                   if (*cp == '(' && cp > line && cp[-1] != '$') {
                                           /*
                                            * Only stop for a left parenthesis if
                                            * it isn't at the start of a word
                                            * (that'll be for variable changes
                                            * later) and isn't preceded by a
                                            * dollar sign (a dynamic source).
                                            */
                                           break;
                                   } else {
                                           cp++;
                                   }
                           }
   
     if (!Array_IsEmpty(&gtargets)) {                          if (*cp == '(') {
         switch (specType) {                                  GNode *gn;
             default:                                  LIST sources;   /* list of archive source
                 Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored");                                                   * names after expansion */
                 break;  
             case Default:  
             case Begin:  
             case End:  
             case Interrupt:  
                 /* These four create nodes on which to hang commands, so  
                  * targets shouldn't be empty...  */  
             case Not:  
                 /* Nothing special here -- targets can be empty if it wants.  */  
                 break;  
         }  
     }  
   
     /* Have now parsed all the target names. Must parse the operator next. The                                  Lst_Init(&sources);
      * result is left in op .  */                                  if (!Arch_ParseArchive(&line, &sources, NULL)) {
     if (*cp == '!') {                                          Parse_Error(PARSE_FATAL,
         op = OP_FORCE;                                              "Error in source archive spec \"%s\"",
     } else if (*cp == ':') {                                              line);
         if (cp[1] == ':') {                                          return;
             op = OP_DOUBLEDEP;                                  }
             cp++;  
         } else {  
             op = OP_DEPENDS;  
         }  
     } else {  
         Parse_Error(PARSE_FATAL, "Missing dependency operator");  
         return;  
     }  
   
     cp++;                       /* Advance beyond operator */                                  while ((gn = (GNode *)Lst_DeQueue(&sources)) !=
                                       NULL)
                                           ParseDoSrc(&gtargets, &gsources, tOp,
                                               gn->name, NULL);
                                   cp = line;
                           } else {
                                   const char *endSrc = cp;
   
     Array_Find(&gtargets, ParseDoOp, op);                                  ParseDoSrc(&gtargets, &gsources, tOp, line,
                                       endSrc);
     /*                                  if (*cp)
      * Get to the first source                                          cp++;
      */                          }
     while (isspace(*cp)) {                          while (isspace(*cp))
         cp++;                                  cp++;
     }                          line = cp;
     line = cp;  
   
     /*  
      * Several special targets take different actions if present with no  
      * sources:  
      *  a .SUFFIXES line with no sources clears out all old suffixes  
      *  a .PRECIOUS line makes all targets precious  
      *  a .IGNORE line ignores errors for all targets  
      *  a .SILENT line creates silence when making all targets  
      *  a .PATH removes all directories from the search path(s).  
      */  
     if (!*line) {  
         switch (specType) {  
             case Suffixes:  
                 Suff_ClearSuffixes();  
                 break;  
             case Precious:  
                 allPrecious = true;  
                 break;  
             case Ignore:  
                 ignoreErrors = true;  
                 break;  
             case Silent:  
                 beSilent = true;  
                 break;  
             case ExPath:  
                 Lst_Every(&paths, ParseClearPath);  
                 break;  
             default:  
                 break;  
         }  
     } else if (specType == MFlags) {  
         /*  
          * Call on functions in main.c to deal with these arguments and  
          * set the initial character to a null-character so the loop to  
          * get sources won't get anything  
          */  
         Main_ParseArgLine(line);  
         *line = '\0';  
     } else if (specType == NotParallel || specType == SingleShell) {  
         *line = '\0';  
     }  
   
     /*  
      * NOW GO FOR THE SOURCES  
      */  
     if (specType == Suffixes || specType == ExPath ||  
         specType == Includes || specType == Libs ||  
         specType == Null) {  
         while (*line) {  
             /*  
              * If the target was one that doesn't take files as its sources  
              * but takes something like suffixes, we take each  
              * space-separated word on the line as a something and deal  
              * with it accordingly.  
              *  
              * If the target was .SUFFIXES, we take each source as a  
              * suffix and add it to the list of suffixes maintained by the  
              * Suff module.  
              *  
              * If the target was a .PATH, we add the source as a directory  
              * to search on the search path.  
              *  
              * If it was .INCLUDES, the source is taken to be the suffix of  
              * files which will be #included and whose search path should  
              * be present in the .INCLUDES variable.  
              *  
              * If it was .LIBS, the source is taken to be the suffix of  
              * files which are considered libraries and whose search path  
              * should be present in the .LIBS variable.  
              *  
              * If it was .NULL, the source is the suffix to use when a file  
              * has no valid suffix.  
              */  
             char  savec;  
             while (*cp && !isspace(*cp)) {  
                 cp++;  
             }  
             savec = *cp;  
             *cp = '\0';  
             switch (specType) {  
                 case Suffixes:  
                     Suff_AddSuffix(line);  
                     break;  
                 case ExPath:  
                     Lst_ForEach(&paths, ParseAddDir, line);  
                     break;  
                 case Includes:  
                     Suff_AddInclude(line);  
                     break;  
                 case Libs:  
                     Suff_AddLib(line);  
                     break;  
                 case Null:  
                     Suff_SetNull(line);  
                     break;  
                 default:  
                     break;  
             }  
             *cp = savec;  
             if (savec != '\0') {  
                 cp++;  
             }  
             while (isspace(*cp)) {  
                 cp++;  
             }  
             line = cp;  
         }  
         Lst_Destroy(&paths, NOFREE);  
     } else {  
         while (*line) {  
             /*  
              * The targets take real sources, so we must beware of archive  
              * specifications (i.e. things with left parentheses in them)  
              * and handle them accordingly.  
              */  
             while (*cp && !isspace(*cp)) {  
                 if (*cp == '(' && cp > line && cp[-1] != '$') {  
                     /*  
                      * Only stop for a left parenthesis if it isn't at the  
                      * start of a word (that'll be for variable changes  
                      * later) and isn't preceded by a dollar sign (a dynamic  
                      * source).  
                      */  
                     break;  
                 } else {  
                     cp++;  
                 }                  }
             }  
   
             if (*cp == '(') {  
                 GNode     *gn;  
                 LIST      sources; /* list of archive source names after  
                                     * expansion */  
   
                 Lst_Init(&sources);  
                 if (!Arch_ParseArchive(&line, &sources, NULL)) {  
                     Parse_Error(PARSE_FATAL,  
                                  "Error in source archive spec \"%s\"", line);  
                     return;  
                 }  
   
                 while ((gn = (GNode *)Lst_DeQueue(&sources)) != NULL)  
                     ParseDoSrc(tOp, gn->name);  
                 cp = line;  
             } else {  
                 if (*cp) {  
                     *cp = '\0';  
                     cp++;  
                 }  
   
                 ParseDoSrc(tOp, line);  
             }  
             while (isspace(*cp)) {  
                 cp++;  
             }  
             line = cp;  
         }          }
     }  
   
     if (mainNode == NULL) {          if (mainNode == NULL) {
         /* If we have yet to decide on a main target to make, in the                  /* If we have yet to decide on a main target to make, in the
          * absence of any user input, we want the first target on                   * absence of any user input, we want the first target on
          * the first dependency line that is actually a real target                   * the first dependency line that is actually a real target
          * (i.e. isn't a .USE or .EXEC rule) to be made.  */                   * (i.e. isn't a .USE or .EXEC rule) to be made.  */
         Array_Find(&gtargets, ParseFindMain, NULL);                  Array_Find(&gtargets, ParseFindMain, NULL);
     }          }
   
     /* Finally, destroy the list of sources.  */  
 }  }
   
 /*-  /*-
Line 1114 
Line 1024 
 ParseHasCommands(void *gnp)         /* Node to examine */  ParseHasCommands(void *gnp)         /* Node to examine */
 {  {
         GNode *gn = (GNode *)gnp;          GNode *gn = (GNode *)gnp;
         if (!Lst_IsEmpty(&gn->commands)) {          if (!Lst_IsEmpty(&gn->commands))
                 gn->type |= OP_HAS_COMMANDS;                  gn->type |= OP_HAS_COMMANDS;
         }  
 }  }
   
   
   
 /* Strip comments from line. Build a copy in buffer if necessary, */  /* Strip comments from line. Build a copy in buffer if necessary, */
 static char *  static char *
 strip_comments(Buffer copy, const char *line)  strip_comments(Buffer copy, const char *line)
Line 1399 
Line 1307 
         }          }
 }  }
   
   
 static bool  static bool
 handle_for_loop(Buffer linebuf, const char *line)  handle_for_loop(Buffer linebuf, const char *line)
 {  {
Line 1436 
Line 1345 
         return true;          return true;
 }  }
   
   /* global hub for the construct */
 static bool  static bool
 handle_bsd_command(Buffer linebuf, Buffer copy, const char *line)  handle_bsd_command(Buffer linebuf, Buffer copy, const char *line)
 {  {
Line 1481 
Line 1391 
 static void  static void
 finish_commands(struct growableArray *targets)  finish_commands(struct growableArray *targets)
 {  {
         Array_Every(targets, Suff_EndTransform);  
         Array_Every(targets, ParseHasCommands);          Array_Every(targets, ParseHasCommands);
         Array_Reset(targets);          Array_Reset(targets);
 }  }
Line 1632 
Line 1541 
         mainNode = NULL;          mainNode = NULL;
         Static_Lst_Init(userIncludePath);          Static_Lst_Init(userIncludePath);
         Static_Lst_Init(systemIncludePath);          Static_Lst_Init(systemIncludePath);
         Array_Init(&gsources, SOURCES_SIZE);  
         Array_Init(&gtargets, TARGETS_SIZE);          Array_Init(&gtargets, TARGETS_SIZE);
           Array_Init(&gsources, SOURCES_SIZE);
           create_special_nodes();
   
         LowParse_Init();          LowParse_Init();
 #ifdef CLEANUP  #ifdef CLEANUP
Line 1657 
Line 1567 
 Parse_MainName(Lst listmain)    /* result list */  Parse_MainName(Lst listmain)    /* result list */
 {  {
   
         if (mainNode == NULL) {      if (mainNode == NULL) {
                 Punt("no target to make.");          Punt("no target to make.");
                 /*NOTREACHED*/          /*NOTREACHED*/
         } else if (mainNode->type & OP_DOUBLEDEP) {      } else if (mainNode->type & OP_DOUBLEDEP) {
                 Lst_AtEnd(listmain, mainNode);          Lst_AtEnd(listmain, mainNode);
                 Lst_Concat(listmain, &mainNode->cohorts);          Lst_Concat(listmain, &mainNode->cohorts);
         }      }
         else      else
                 Lst_AtEnd(listmain, mainNode);          Lst_AtEnd(listmain, mainNode);
 }  }
   

Legend:
Removed from v.1.91  
changed lines
  Added in v.1.92