[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.58 and 1.59

version 1.58, 2000/11/24 14:27:20 version 1.59, 2001/05/03 13:41:08
Line 1 
Line 1 
   /*      $OpenPackages$ */
 /*      $OpenBSD$       */  /*      $OpenBSD$       */
 /*      $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $      */  /*      $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $      */
   
 /*  /*
    * Copyright (c) 1999 Marc Espie.
    *
    * Extensive code changes for the OpenBSD project.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions
    * are met:
    * 1. Redistributions of source code must retain the above copyright
    *    notice, this list of conditions and the following disclaimer.
    * 2. Redistributions in binary form must reproduce the above copyright
    *    notice, this list of conditions and the following disclaimer in the
    *    documentation and/or other materials provided with the distribution.
    *
    * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
    * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
    * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    */
   /*
  * Copyright (c) 1988, 1989, 1990, 1993   * Copyright (c) 1988, 1989, 1990, 1993
  *      The Regents of the University of California.  All rights reserved.   *      The Regents of the University of California.  All rights reserved.
  * Copyright (c) 1989 by Berkeley Softworks   * Copyright (c) 1989 by Berkeley Softworks
Line 58 
Line 85 
  *      messages can be more meaningful.   *      messages can be more meaningful.
  *   *
  * Interface:   * Interface:
  *      Parse_Init                  Initialization function which must be   *      Parse_Init                  Initialization function which must be
  *                                  called before anything else in this module   *                                  called before anything else in this module
  *                                  is used.   *                                  is used.
  *   *
  *      Parse_End                   Cleanup the module   *      Parse_End                   Cleanup the module
  *   *
  *      Parse_File                  Function used to parse a makefile. It must   *      Parse_File                  Function used to parse a makefile. It must
  *                                  be given the name of the file, which should   *                                  be given the name of the file, which should
  *                                  already have been opened, and a function   *                                  already have been opened, and a function
  *                                  to call to read a character from the file.   *                                  to call to read a character from the file.
  *   *
  *      Parse_IsVar                 Returns TRUE if the given line is a   *      Parse_IsVar                 Returns TRUE if the given line is a
  *                                  variable assignment. Used by MainParseArgs   *                                  variable assignment. Used by MainParseArgs
  *                                  to determine if an argument is a target   *                                  to determine if an argument is a target
  *                                  or a variable assignment. Used internally   *                                  or a variable assignment. Used internally
  *                                  for pretty much the same thing...   *                                  for pretty much the same thing...
  *   *
  *      Parse_Error                 Function called when an error occurs in   *      Parse_Error                 Function called when an error occurs in
  *                                  parsing. Used by the variable and   *                                  parsing. Used by the variable and
  *                                  conditional modules.   *                                  conditional modules.
  *      Parse_MainName              Returns a Lst of the main target to create.   *      Parse_MainName              Returns a Lst of the main target to create.
  */   */
   
 #ifdef __STDC__  #ifdef __STDC__
Line 86 
Line 113 
 #else  #else
 #include <varargs.h>  #include <varargs.h>
 #endif  #endif
   #include <assert.h>
 #include <stddef.h>  #include <stddef.h>
 #include <stdio.h>  #include <stdio.h>
 #include <ctype.h>  #include <ctype.h>
Line 107 
Line 135 
 #endif  #endif
 #endif /* not lint */  #endif /* not lint */
   
 static LIST             targets;        /* targets we're working on */  LIST            parseIncPath;   /* list of directories for "..." includes */
   LIST            sysIncPath;     /* list of directories for <...> includes */
   
   static LIST         targets;    /* targets we're working on */
 #ifdef CLEANUP  #ifdef CLEANUP
 static LIST             targCmds;       /* command lines for targets */  static LIST         targCmds;   /* command lines for targets */
   static LIST         fileNames;
 #endif  #endif
 static Boolean          inLine;         /* true if currently in a dependency  
                                          * line or its commands */  
 static GNode        *mainNode;  /* The main target to create. This is the  static GNode        *mainNode;  /* The main target to create. This is the
                                  * first target on the first dependency                                   * first target on the first dependency
                                  * line in the first makefile */                                   * line in the first makefile */
 LIST            parseIncPath;   /* list of directories for "..." includes */  
 LIST            sysIncPath;     /* list of directories for <...> includes */  
   
 /*-  /*-
  * 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   * Not if the target is unspecial. If it *is* special, however, the children
Line 126 
Line 154 
  * set in ParseDoDependency   * set in ParseDoDependency
  */   */
 typedef enum {  typedef enum {
     Begin,          /* .BEGIN */      Begin,          /* .BEGIN */
     Default,        /* .DEFAULT */      Default,        /* .DEFAULT */
     End,            /* .END */      End,            /* .END */
     Ignore,         /* .IGNORE */      Ignore,         /* .IGNORE */
     Includes,       /* .INCLUDES */      Includes,       /* .INCLUDES */
     Interrupt,      /* .INTERRUPT */      Interrupt,      /* .INTERRUPT */
Line 140 
Line 168 
     NoPath,         /* .NOPATH */      NoPath,         /* .NOPATH */
     Not,            /* Not special */      Not,            /* Not special */
     NotParallel,    /* .NOTPARALELL */      NotParallel,    /* .NOTPARALELL */
     Null,           /* .NULL */      Null,           /* .NULL */
     Order,          /* .ORDER */      Order,          /* .ORDER */
     Parallel,       /* .PARALLEL */      Parallel,       /* .PARALLEL */
     ExPath,         /* .PATH */      ExPath,         /* .PATH */
     Phony,          /* .PHONY */      Phony,          /* .PHONY */
Line 171 
Line 199 
  * keyword is used as a source ("0" if the keyword isn't special as a source)   * keyword is used as a source ("0" if the keyword isn't special as a source)
  */   */
 static struct {  static struct {
     char          *name;        /* Name of keyword */      char          *name;        /* Name of keyword */
     ParseSpecial  spec;         /* Type when used as a target */      ParseSpecial  spec;         /* Type when used as a target */
     int           op;           /* Operator when used as a source */      int           op;           /* Operator when used as a source */
 } parseKeywords[] = {  } parseKeywords[] = {
 { ".BEGIN",       Begin,        0 },  { ".BEGIN",       Begin,        0 },
 { ".DEFAULT",     Default,      0 },  { ".DEFAULT",     Default,      0 },
 { ".END",         End,          0 },  { ".END",         End,          0 },
 { ".EXEC",        Attribute,    OP_EXEC },  { ".EXEC",        Attribute,    OP_EXEC },
 { ".IGNORE",      Ignore,       OP_IGNORE },  { ".IGNORE",      Ignore,       OP_IGNORE },
 { ".INCLUDES",    Includes,     0 },  { ".INCLUDES",    Includes,     0 },
 { ".INTERRUPT",   Interrupt,    0 },  { ".INTERRUPT",   Interrupt,    0 },
 { ".INVISIBLE",   Attribute,    OP_INVISIBLE },  { ".INVISIBLE",   Attribute,    OP_INVISIBLE },
 { ".JOIN",        Attribute,    OP_JOIN },  { ".JOIN",        Attribute,    OP_JOIN },
 { ".LIBS",        Libs,         0 },  { ".LIBS",        Libs,         0 },
 { ".MADE",        Attribute,    OP_MADE },  { ".MADE",        Attribute,    OP_MADE },
 { ".MAIN",        Main,         0 },  { ".MAIN",        Main,         0 },
 { ".MAKE",        Attribute,    OP_MAKE },  { ".MAKE",        Attribute,    OP_MAKE },
 { ".MAKEFLAGS",   MFlags,       0 },  { ".MAKEFLAGS",   MFlags,       0 },
 { ".MFLAGS",      MFlags,       0 },  { ".MFLAGS",      MFlags,       0 },
 #if 0   /* basic scaffolding for NOPATH, not working yet */  #if 0   /* basic scaffolding for NOPATH, not working yet */
 { ".NOPATH",      NoPath,       OP_NOPATH },  { ".NOPATH",      NoPath,       OP_NOPATH },
 #endif  #endif
 { ".NOTMAIN",     Attribute,    OP_NOTMAIN },  { ".NOTMAIN",     Attribute,    OP_NOTMAIN },
 { ".NOTPARALLEL", NotParallel,  0 },  { ".NOTPARALLEL", NotParallel,  0 },
 { ".NO_PARALLEL", NotParallel,  0 },  { ".NO_PARALLEL", NotParallel,  0 },
 { ".NULL",        Null,         0 },  { ".NULL",        Null,         0 },
 { ".OPTIONAL",    Attribute,    OP_OPTIONAL },  { ".OPTIONAL",    Attribute,    OP_OPTIONAL },
 { ".ORDER",       Order,        0 },  { ".ORDER",       Order,        0 },
 { ".PARALLEL",    Parallel,     0 },  { ".PARALLEL",    Parallel,     0 },
 { ".PATH",        ExPath,       0 },  { ".PATH",        ExPath,       0 },
 { ".PHONY",       Phony,        OP_PHONY },  { ".PHONY",       Phony,        OP_PHONY },
 { ".PRECIOUS",    Precious,     OP_PRECIOUS },  { ".PRECIOUS",    Precious,     OP_PRECIOUS },
 { ".RECURSIVE",   Attribute,    OP_MAKE },  { ".RECURSIVE",   Attribute,    OP_MAKE },
 { ".SHELL",       ExShell,      0 },  { ".SHELL",       ExShell,      0 },
 { ".SILENT",      Silent,       OP_SILENT },  { ".SILENT",      Silent,       OP_SILENT },
 { ".SINGLESHELL", SingleShell,  0 },  { ".SINGLESHELL", SingleShell,  0 },
 { ".SUFFIXES",    Suffixes,     0 },  { ".SUFFIXES",    Suffixes,     0 },
 { ".USE",         Attribute,    OP_USE },  { ".USE",         Attribute,    OP_USE },
 { ".WAIT",        Wait,         0 },  { ".WAIT",        Wait,         0 },
 };  };
   
 static int ParseFindKeyword __P((char *));  static int ParseFindKeyword(const char *);
 static void ParseLinkSrc __P((void *, void *));  static void ParseLinkSrc(GNode *, GNode *);
 static int ParseDoOp __P((void *, void *));  static int ParseDoOp(void *, void *);
 static int ParseAddDep __P((void *, void *));  static int ParseAddDep(void *, void *);
 static void ParseDoSrc __P((int, char *, Lst));  static void ParseDoSrc(int, const char *, Lst);
 static int ParseFindMain __P((void *, void *));  static int ParseFindMain(void *, void *);
 static void ParseAddDir __P((void *, void *));  static void ParseAddDir(void *, void *);
 static void ParseClearPath __P((void *));  static void ParseClearPath(void *);
 static void ParseDoDependency __P((char *));  static void ParseDoDependency(char *);
 static void ParseAddCmd __P((void *, void *));  static void ParseAddCmd(void *, void *);
 static void ParseHasCommands __P((void *));  static void ParseHasCommands(void *);
 static void ParseDoInclude __P((char *));  static void ParseDoInclude(char *);
 #ifdef SYSVINCLUDE  static void ParseTraditionalInclude(char *);
 static void ParseTraditionalInclude __P((char *));  static void ParseConditionalInclude(char *);
 #endif  static void ParseLookupIncludeFile(char *, char *, Boolean, Boolean);
 static void ParseLookupIncludeFile __P((char *, char *, Boolean));  #define ParseGetLoopLine(linebuf)       ParseGetLine(linebuf, "for loop")
 static void ParseFinishLine __P((void));  static void ParseFinishDependency(void);
   static Boolean ParseIsCond(Buffer, Buffer, char *);
   static char *strip_comments(Buffer, const char *);
   
   static void ParseDoCommands(const char *);
   static const char *find_op1(const char *);
   static const char *find_op2(const char *);
   
 /*-  /*-
  *----------------------------------------------------------------------   *----------------------------------------------------------------------
  * ParseFindKeyword --   * ParseFindKeyword --
Line 237 
Line 271 
  *   *
  * Results:   * Results:
  *      The index of the keyword, or -1 if it isn't there.   *      The index of the keyword, or -1 if it isn't there.
  *  
  * Side Effects:  
  *      None  
  *----------------------------------------------------------------------   *----------------------------------------------------------------------
  */   */
 static int  static int
 ParseFindKeyword (str)  ParseFindKeyword(str)
     char            *str;               /* String to find */      const char      *str;               /* String to find */
 {  {
     register int    start,      int             start,
                     end,                      end,
                     cur;                      cur;
     register int    diff;      int             diff;
   
     start = 0;      start = 0;
     end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1;      end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1;
   
     do {      do {
         cur = start + ((end - start) / 2);          cur = start + (end - start) / 2;
         diff = strcmp (str, parseKeywords[cur].name);          diff = strcmp(str, parseKeywords[cur].name);
   
         if (diff == 0) {          if (diff == 0) {
             return (cur);              return cur;
         } else if (diff < 0) {          } else if (diff < 0) {
             end = cur - 1;              end = cur - 1;
         } else {          } else {
             start = cur + 1;              start = cur + 1;
         }          }
     } while (start <= end);      } while (start <= end);
     return (-1);      return -1;
 }  }
   
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * ParseLinkSrc  --   * ParseLinkSrc  --
  *      Link the parent node to its new child. Used in a Lst_ForEach by   *      Link the parent node to its new child. Used by
  *      ParseDoDependency. If the specType isn't 'Not', the parent   *      ParseDoDependency. If the specType isn't 'Not', the parent
  *      isn't linked as a parent of the child.   *      isn't linked as a parent of the child.
  *   *
Line 283 
Line 313 
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  */   */
 static void  static void
 ParseLinkSrc(pgnp, cgnp)  ParseLinkSrc(pgn, cgn)
     void *pgnp; /* The parent node */      GNode               *pgn;   /* The parent node */
     void *cgnp; /* The child node */      GNode               *cgn;   /* The child node */
 {  {
     GNode          *pgn = (GNode *)pgnp;      if (Lst_AddNew(&pgn->children, cgn) == SUCCESS) {
     GNode          *cgn = (GNode *)cgnp;  
     if (Lst_Member(&pgn->children, cgn) == NULL) {  
         Lst_AtEnd(&pgn->children, cgn);  
         if (specType == Not)          if (specType == Not)
             Lst_AtEnd(&cgn->parents, pgn);              Lst_AtEnd(&cgn->parents, pgn);
         pgn->unmade += 1;          pgn->unmade++;
     }      }
 }  }
   
Line 305 
Line 332 
  *      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.
  *   *
  * Results:  
  *      0 if a problem, 1 if ok.  
  *  
  * Side Effects:   * Side Effects:
  *      The type field of the node is altered to reflect any new bits in   *      The type field of the node is altered to reflect any new bits in
  *      the op.   *      the op.
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  */   */
 static int  static int
 ParseDoOp (gnp, opp)  ParseDoOp(gnp, opp)
     void *gnp;          /* The node to which the operator is to be      void           *gnp;        /* The node to which the operator is to be
                          * applied */                                   * applied */
     void *opp;          /* The operator to apply */      void           *opp;        /* The operator to apply */
 {  {
     GNode          *gn = (GNode *) gnp;      GNode          *gn = (GNode *)gnp;
     int             op = *(int *) opp;      int             op = *(int *)opp;
     /*      /*
      * If the dependency mask of the operator and the node don't match and       * 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       * the node has actually had an operator applied to it before, and
Line 332 
Line 356 
         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 create a
          * If the node was the object of a :: operator, we need to create a  
          * new instance of it for the children and commands on this dependency           * new instance of it for the children and commands on this dependency
          * line. The new instance is placed on the 'cohorts' list of the           * line. The new instance is placed on the 'cohorts' list of the
          * initial one (note the initial one is not on its own cohorts list)           * initial one (note the initial one is not on its own cohorts list)
          * and the new instance is linked to all parents of the initial           * and the new instance is linked to all parents of the initial
          * instance.           * instance.  */
          */          GNode           *cohort;
         register GNode  *cohort;          LstNode         ln;
         LstNode         ln;  
   
         cohort = Targ_NewGN(gn->name, NULL);          cohort = Targ_NewGN(gn->name, NULL);
         /*          /* Duplicate links to parents so graph traversal is simple. Perhaps
          * Duplicate links to parents so graph traversal is simple. Perhaps  
          * some type bits should be duplicated?           * some type bits should be duplicated?
          *           *
          * Make the cohort invisible as well to avoid duplicating it into           * Make the cohort invisible as well to avoid duplicating it into
          * other variables. True, parents of this target won't tend to do           * other variables. True, parents of this target won't tend to do
          * anything with their local variables, but better safe than           * anything with their local variables, but better safe than
          * sorry.           * sorry.  */
          */          for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln))
         Lst_ForEach(&gn->parents, ParseLinkSrc, 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  
          */  
         ln = Lst_Member(&targets, gn);          ln = Lst_Member(&targets, gn);
         Lst_Replace(ln, cohort);          Lst_Replace(ln, cohort);
         gn = cohort;          gn = cohort;
     }      }
     /*      /* We don't want to nuke any previous flags (whatever they were) so we
      * We don't want to nuke any previous flags (whatever they were) so we       * just OR the new operator into the old.  */
      * just OR the new operator into the old  
      */  
     gn->type |= op;      gn->type |= op;
   
     return 1;      return 1;
 }  }
   
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * ParseAddDep  --   * ParseAddDep  --
  *      Check if the pair of GNodes given needs to be synchronized.   *      Check if the pair of GNodes given needs to be synchronized.
  *      This has to be when two nodes are on different sides of a   *      This has to be when two nodes are on different sides of a
  *      .WAIT directive.   *      .WAIT directive.
  *   *
  * Results:   * Results:
  *      Returns 0 if the two targets need to be ordered, 1 otherwise.   *      Returns 0 if the two targets need to be ordered, 1 otherwise.
  *      If it returns 0, the search can stop   *      If it returns 0, the search can stop.
  *   *
  * Side Effects:   * Side Effects:
  *      A dependency can be added between the two nodes.   *      A dependency can be added between the two nodes.
Line 395 
Line 411 
     void *pp;      void *pp;
     void *sp;      void *sp;
 {  {
     GNode *p = (GNode *) pp;      GNode *p = (GNode *)pp;
     GNode *s = (GNode *) sp;      GNode *s = (GNode *)sp;
   
     if (p->order < s->order) {      if (p->order < s->order) {
         /*          /* XXX: This can cause loops, and loops can cause unmade targets,
          * XXX: This can cause loops, and loops can cause unmade targets,  
          * but checking is tedious, and the debugging output can show the           * but checking is tedious, and the debugging output 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;
Line 422 
Line 436 
  *      of some special target and apply it if so. Otherwise, make the   *      of some special target and apply it if so. Otherwise, make the
  *      source be a child of the targets in the list 'targets'   *      source be a child of the targets in the list 'targets'
  *   *
  * Results:  
  *      None  
  *  
  * Side Effects:   * Side Effects:
  *      Operator bits may be added to the list of targets or to the source.   *      Operator bits may be added to the list of targets or to the source.
  *      The targets may have a new source added to their lists of children.   *      The targets may have a new source added to their lists of children.
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  */   */
 static void  static void
 ParseDoSrc (tOp, src, allsrc)  ParseDoSrc(tOp, src, allsrc)
     int         tOp;    /* operator (if any) from special targets */      int         tOp;    /* operator (if any) from special targets */
     char        *src;   /* name of the source to handle */      const char  *src;   /* name of the source to handle */
     Lst         allsrc; /* List of all sources to wait for */      Lst         allsrc; /* List of all sources to wait for */
   
 {  {
     GNode       *gn = NULL;      GNode       *gn = NULL;
   
     if (*src == '.' && isupper (src[1])) {      if (*src == '.' && isupper(src[1])) {
         int keywd = ParseFindKeyword(src);          int keywd = ParseFindKeyword(src);
         if (keywd != -1) {          if (keywd != -1) {
             int op = parseKeywords[keywd].op;              int op = parseKeywords[keywd].op;
Line 477 
Line 488 
          * Create proper predecessor/successor links between the previous           * Create proper predecessor/successor links between the previous
          * source and the current one.           * source and the current one.
          */           */
         gn = Targ_FindNode(src, TARG_CREATE);          gn = Targ_FindNode(src, NULL, 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);
Line 500 
Line 511 
          * 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);          gn = Targ_FindNode(src, NULL, TARG_CREATE);
         if (tOp) {          if (tOp) {
             gn->type |= tOp;              gn->type |= tOp;
         } else {          } else {
             Lst_ForEach(&targets, ParseLinkSrc, gn);              LstNode     ln;
   
               for (ln = Lst_First(&targets); ln != NULL; ln = Lst_Adv(ln))
                   ParseLinkSrc((GNode *)Lst_Datum(ln), gn);
         }          }
         if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) {          if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
             register GNode      *cohort;              GNode       *cohort;
             register LstNode    ln;              LstNode     ln;
   
             for (ln=Lst_First(&gn->cohorts); ln != NULL; ln = Lst_Adv(ln)){              for (ln=Lst_First(&gn->cohorts); ln != NULL; ln = Lst_Adv(ln)){
                 cohort = (GNode *)Lst_Datum(ln);                  cohort = (GNode *)Lst_Datum(ln);
                 if (tOp) {                  if (tOp) {
                     cohort->type |= tOp;                      cohort->type |= tOp;
                 } else {                  } else {
                     Lst_ForEach(&targets, ParseLinkSrc, cohort);                      LstNode     ln;
   
                       for (ln = Lst_First(&targets); ln != NULL; ln = Lst_Adv(ln))
                           ParseLinkSrc((GNode *)Lst_Datum(ln), cohort);
                 }                  }
             }              }
         }          }
Line 524 
Line 541 
   
     gn->order = waiting;      gn->order = waiting;
     Lst_AtEnd(allsrc, gn);      Lst_AtEnd(allsrc, gn);
     if (waiting)      if (waiting) {
         Lst_Find(allsrc, ParseAddDep, gn);          Lst_Find(allsrc, ParseAddDep, gn);
       }
 }  }
   
 /*-  /*-
Line 540 
Line 558 
  *   *
  * Side Effects:   * Side Effects:
  *      mainNode is changed and Targ_SetMain is called.   *      mainNode is changed and Targ_SetMain is called.
  *  
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static int  static int
 ParseFindMain(gnp, dummy)  ParseFindMain(gnp, dummy)
     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) {
         mainNode = gn;          mainNode = gn;
         Targ_SetMain(gn);          Targ_SetMain(gn);
         return 0;          return 0;
     } else      } else {
         return 1;          return 1;
       }
 }  }
   
 /*-  /*-
Line 564 
Line 582 
  *   *
  * Side Effects:   * Side Effects:
  *      See Dir_AddDir.   *      See Dir_AddDir.
  *  
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static void  static void
Line 578 
Line 595 
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * ParseClearPath --   * ParseClearPath --
  *      Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going   *      Reinit path to an empty path
  *  
  * Side Effects:  
  *      See Dir_ClearPath  
  *  
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static void  static void
 ParseClearPath(path)  ParseClearPath(p)
     void *path;      void        *p;
 {  {
     Dir_ClearPath((Lst)path);      Lst         path = (Lst)p;
   
       Lst_Destroy(path, Dir_Destroy);
       Lst_Init(path);
 }  }
   
 /*-  /*-
Line 597 
Line 613 
  * ParseDoDependency  --   * ParseDoDependency  --
  *      Parse the dependency line in line.   *      Parse the dependency line in line.
  *   *
  * Results:  
  *      None  
  *  
  * Side Effects:   * Side Effects:
  *      The nodes of the sources are linked as children to the nodes of the   *      The nodes of the sources are linked as children to the nodes of the
  *      targets. Some nodes may be created.   *      targets. Some nodes may be created.
Line 609 
Line 622 
  * until a character is encountered which is an operator character. Currently   * until a character is encountered which is an operator character. Currently
  * these are only ! and :. At this point the operator is parsed and the   * these are only ! and :. At this point the operator is parsed and the
  * pointer into the line advanced until the first source is encountered.   * pointer into the line advanced until the first source is encountered.
  *      The parsed operator is applied to each node in the 'targets' list,   *      The parsed operator is applied to each node in the 'targets' list,
  * which is where the nodes found for the targets are kept, by means of   * which is where the nodes found for the targets are kept, by means of
  * the ParseDoOp function.   * the ParseDoOp function.
  *      The sources are read in much the same way as the targets were except   *      The sources are read in much the same way as the targets were except
Line 627 
Line 640 
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  */   */
 static void  static void
 ParseDoDependency (line)  ParseDoDependency(line)
     char           *line;       /* the line to parse */      char           *line;       /* the line to parse */
 {  {
     char           *cp;         /* our current position */      char           *cp;         /* our current position */
     GNode          *gn;         /* a general purpose temporary node */      GNode          *gn;         /* a general purpose temporary node */
     int             op;         /* the operator on the line */      int             op;         /* the operator on the line */
     char            savec;      /* a place to save a character */      char            savec;      /* a place to save a character */
     LIST            paths;      /* List of search paths to alter when parsing      LIST            paths;      /* List of search paths to alter when parsing
                                  * a list of .PATH targets */                                   * a list of .PATH targets */
     int             tOp;        /* operator from special target */      int             tOp;        /* operator from special target */
     LIST            curTargs;   /* list of target names to be found and added      LIST            curTargs;   /* list of target names to be found and added
                                  * to the targets list */                                   * to the targets list */
     LIST            curSrcs;    /* list of sources in order */      LIST            curSrcs;    /* list of sources in order */
   
Line 651 
Line 664 
     Lst_Init(&curSrcs);      Lst_Init(&curSrcs);
   
     do {      do {
         for (cp = line;          for (cp = line; *cp && !isspace(*cp) && *cp != '(';)
              *cp && !isspace (*cp) && (*cp != '(');              if (*cp == '$')
              cp++)                  /* Must be a dynamic source (would have been expanded
         {  
             /*  
              * 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 (*p == '\0')  
                     break;                      /* no chance, not enough room */  
                 /*  
                  * Only end the word on ':' or '!' if there is not  
                  * a match later on followed by whitespace.  
                  */  
                 while ((p = strchr(p + 1, *cp)) && !isspace(*(p + 1)))  
                     ;  
                 if (!p || !isspace(*(p + 1)))  
                     break;  
             } else if (*cp == '$') {  
                 /*  
                  * Must be a dynamic source (would have been expanded  
                  * otherwise), so call the Var module to parse the puppy                   * otherwise), so call the Var module to parse the puppy
                  * so we can safely advance beyond it...There should be                   * so we can safely advance beyond it...There should be
                  * no errors in this, as they would have been discovered                   * no errors in this, as they would have been discovered
                  * in the initial Var_Subst and we wouldn't be here.                   * in the initial Var_Subst and we wouldn't be here.  */
                  */                  cp += Var_ParseSkip(cp, NULL, NULL);
                 size_t  length;              else {
                 Boolean freeIt;                  /* We don't want to end a word on ':' or '!' if there is a
                 char    *result;                   * 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;
   
                 result=Var_Parse(cp, NULL, TRUE, &length, &freeIt);                      if (*cp == ':' && *p == ':')
                           p++;
   
                 if (freeIt) {                      /* Found the best match already. */
                     free(result);                      if (isspace(*p) || *p == '\0')
                           break;
   
                       do {
                           p += strcspn(p, "!:");
                           if (*p == '\0')
                               break;
                           p++;
                       } while (!isspace(*p));
   
                       /* No better match later on... */
                       if (*p == '\0')
                           break;
   
                 }                  }
                 cp += length-1;                  cp++;
             }              }
             continue;  
         }  
         if (*cp == '(') {          if (*cp == '(') {
             /*              /* Archives must be handled specially to make sure the OP_ARCHV
              * Archives must be handled specially to make sure the OP_ARCHV  
              * flag is set in their 'type' field, for one thing, and because               * flag is set in their 'type' field, for one thing, and because
              * things like "archive(file1.o file2.o file3.o)" are permissible.               * things like "archive(file1.o file2.o file3.o)" are permissible.
              * Arch_ParseArchive will set 'line' to be the first non-blank               * Arch_ParseArchive will set 'line' to be the first non-blank
              * after the archive-spec. It creates/finds nodes for the members               * after the archive-spec. It creates/finds nodes for the members
              * and places them on the given list, returning SUCCESS if all               * and places them on the given list, returning SUCCESS if all
              * went well and FAILURE if there was an error in the               * went well and FAILURE if there was an error in the
              * specification. On error, line should remain untouched.               * specification. On error, line should remain untouched.  */
              */  
             if (Arch_ParseArchive(&line, &targets, NULL) != SUCCESS) {              if (Arch_ParseArchive(&line, &targets, NULL) != SUCCESS) {
                 Parse_Error (PARSE_FATAL,                  Parse_Error(PARSE_FATAL,
                              "Error in archive specification: \"%s\"", line);                               "Error in archive specification: \"%s\"", line);
                 return;                  return;
             } else {              } else {
Line 721 
Line 725 
         }          }
         savec = *cp;          savec = *cp;
   
         if (!*cp) {          if (*cp == '\0') {
             /*              /* Ending a dependency line without an operator is a Bozo no-no */
              * Ending a dependency line without an operator is a Bozo              Parse_Error(PARSE_FATAL, "Need an operator");
              * no-no  
              */  
             Parse_Error (PARSE_FATAL, "Need an operator");  
             return;              return;
         }          }
         *cp = '\0';          *cp = '\0';
         /*          /* Have a word in line. See if it's a special target and set
          * Have a word in line. See if it's a special target and set           * specType to match it.  */
          * specType to match it.          if (*line == '.' && isupper(line[1])) {
          */              /* See if the target is a special target that must have it
         if (*line == '.' && isupper (line[1])) {               * or its sources handled specially.  */
             /*  
              * See if the target is a special target that must have it  
              * or its sources handled specially.  
              */  
             int keywd = ParseFindKeyword(line);              int keywd = ParseFindKeyword(line);
             if (keywd != -1) {              if (keywd != -1) {
                 if (specType == ExPath && parseKeywords[keywd].spec != ExPath) {                  if (specType == ExPath && parseKeywords[keywd].spec != ExPath) {
Line 756 
Line 753 
                  *      .MAIN           Its sources are only used if                   *      .MAIN           Its sources are only used if
                  *                      nothing has been specified to                   *                      nothing has been specified to
                  *                      create.                   *                      create.
                  *      .DEFAULT        Need to create a node to hang                   *      .DEFAULT        Need to create a node to hang
                  *                      commands on, but we don't want                   *                      commands on, but we don't want
                  *                      it in the graph, nor do we want                   *                      it in the graph, nor do we want
                  *                      it to be the Main Target, so we                   *                      it to be the Main Target, so we
Line 764 
Line 761 
                  *                      add it to the list, setting                   *                      add it to the list, setting
                  *                      DEFAULT to the new node for                   *                      DEFAULT to the new node for
                  *                      later use. We claim the node is                   *                      later use. We claim the node is
                  *                      A transformation rule to make                   *                      A transformation rule to make
                  *                      life easier later, when we'll                   *                      life easier later, when we'll
                  *                      use Make_HandleUse to actually                   *                      use Make_HandleUse to actually
                  *                      apply the .DEFAULT commands.                   *                      apply the .DEFAULT commands.
                  *      .PHONY          The list of targets                   *      .PHONY          The list of targets
                  *      .NOPATH         Don't search for file in the path                   *      .NOPATH         Don't search for file in the path
                  *      .BEGIN                   *      .BEGIN
                  *      .END                   *      .END
                  *      .INTERRUPT      Are not to be considered the                   *      .INTERRUPT      Are not to be considered the
                  *                      main target.                   *                      main target.
                  *      .NOTPARALLEL    Make only one target at a time.                   *      .NOTPARALLEL    Make only one target at a time.
                  *      .SINGLESHELL    Create a shell for each command.                   *      .SINGLESHELL    Create a shell for each command.
                  *      .ORDER          Must set initial predecessor to NULL                   *      .ORDER          Must set initial predecessor to NULL
                  */                   */
                 switch (specType) {                  switch (specType) {
                     case ExPath:                      case ExPath:
Line 790 
Line 787 
                     case Begin:                      case Begin:
                     case End:                      case End:
                     case Interrupt:                      case Interrupt:
                         gn = Targ_FindNode(line, TARG_CREATE);                          gn = Targ_FindNode(line, NULL, TARG_CREATE);
                         gn->type |= OP_NOTMAIN;                          gn->type |= OP_NOTMAIN;
                         Lst_AtEnd(&targets, gn);                          Lst_AtEnd(&targets, gn);
                         break;                          break;
                     case Default:                      case Default:
                         gn = Targ_NewGN(".DEFAULT", NULL);                          gn = Targ_NewGN(".DEFAULT", NULL);
                         gn->type |= (OP_NOTMAIN|OP_TRANSFORM);                          gn->type |= OP_NOTMAIN|OP_TRANSFORM;
                         Lst_AtEnd(&targets, gn);                          Lst_AtEnd(&targets, gn);
                         DEFAULT = gn;                          DEFAULT = gn;
                         break;                          break;
Line 816 
Line 813 
                     default:                      default:
                         break;                          break;
                 }                  }
             } else if (strncmp (line, ".PATH", 5) == 0) {              } else if (strncmp(line, ".PATH", 5) == 0) {
                 /*                  /*
                  * .PATH<suffix> has to be handled specially.                   * .PATH<suffix> has to be handled specially.
                  * Call on the suffix module to give us a path to                   * Call on the suffix module to give us a path to
                  * modify.                   * modify.
                  */                   */
                 Lst     path;                  Lst     path;
   
                 specType = ExPath;                  specType = ExPath;
                 path = Suff_GetPath(&line[5]);                  path = Suff_GetPath(&line[5]);
Line 831 
Line 828 
                                  "Suffix '%s' not defined (yet)",                                   "Suffix '%s' not defined (yet)",
                                  &line[5]);                                   &line[5]);
                     return;                      return;
                 } else                  } else {
                     Lst_AtEnd(&paths, path);                      Lst_AtEnd(&paths, path);
                   }
             }              }
         }          }
   
Line 840 
Line 838 
          * Have word in line. Get or create its node and stick it at           * Have word in line. Get or create its node and stick it at
          * the end of the targets list           * the end of the targets list
          */           */
         if ((specType == Not) && (*line != '\0')) {          if (specType == Not && *line != '\0') {
             char *targName;              char *targName;
   
             if (Dir_HasWildcards(line)) {              if (Dir_HasWildcards(line)) {
Line 851 
Line 849 
                  * Dir module could have added a directory to the path...                   * Dir module could have added a directory to the path...
                  */                   */
                 LIST        emptyPath;                  LIST        emptyPath;
   
                 Lst_Init(&emptyPath);  
   
                   Lst_Init(&emptyPath);
                 Dir_Expand(line, &emptyPath, &curTargs);                  Dir_Expand(line, &emptyPath, &curTargs);
   
                 Lst_Destroy(&emptyPath, Dir_Destroy);                  Lst_Destroy(&emptyPath, Dir_Destroy);
             } else {              } else {
                 /*                  /*
Line 865 
Line 861 
                 Lst_AtEnd(&curTargs, line);                  Lst_AtEnd(&curTargs, line);
             }              }
   
             while((targName = (char *)Lst_DeQueue(&curTargs)) != NULL) {              while ((targName = (char *)Lst_DeQueue(&curTargs)) != NULL) {
                 if (!Suff_IsTransform (targName)) {                  if (!Suff_IsTransform(targName)) {
                     gn = Targ_FindNode (targName, TARG_CREATE);                      gn = Targ_FindNode(targName, NULL, TARG_CREATE);
                 } else {                  } else {
                     gn = Suff_AddTransform (targName);                      gn = Suff_AddTransform(targName);
                 }                  }
   
                 if (gn != NULL)                  if (gn != NULL)
Line 887 
Line 883 
         if (specType != Not && specType != ExPath) {          if (specType != Not && specType != ExPath) {
             Boolean warn = FALSE;              Boolean warn = FALSE;
   
             while ((*cp != '!') && (*cp != ':') && *cp) {              while (*cp != '!' && *cp != ':' && *cp) {
                 if (*cp != ' ' && *cp != '\t') {                  if (*cp != ' ' && *cp != '\t') {
                     warn = TRUE;                      warn = TRUE;
                 }                  }
Line 897 
Line 893 
                 Parse_Error(PARSE_WARNING, "Extra target ignored");                  Parse_Error(PARSE_WARNING, "Extra target ignored");
             }              }
         } else {          } else {
             while (*cp && isspace (*cp)) {              while (*cp && isspace(*cp)) {
                 cp++;                  cp++;
             }              }
         }          }
         line = cp;          line = cp;
     } while ((*line != '!') && (*line != ':') && *line);      } while (*line != '!' && *line != ':' && *line);
   
     /* Don't need the list of target names any more */      /*
        * Don't need the list of target names anymore...
        */
     Lst_Destroy(&curTargs, NOFREE);      Lst_Destroy(&curTargs, NOFREE);
   
     if (!Lst_IsEmpty(&targets)) {      if (!Lst_IsEmpty(&targets)) {
         switch(specType) {          switch (specType) {
             default:              default:
                 Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored");                  Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored");
                 break;                  break;
Line 916 
Line 914 
             case Begin:              case Begin:
             case End:              case End:
             case Interrupt:              case Interrupt:
                 /*                  /* These four create nodes on which to hang commands, so
                  * These four create nodes on which to hang commands, so                   * targets shouldn't be empty...  */
                  * targets shouldn't be empty...  
                  */  
             case Not:              case Not:
                 /*                  /* Nothing special here -- targets can be empty if it wants.  */
                  * Nothing special here -- targets can be empty if it wants.  
                  */  
                 break;                  break;
         }          }
     }      }
   
     /*      /* Have now parsed all the target names. Must parse the operator next. The
      * Have now parsed all the target names. Must parse the operator next. The       * result is left in op .  */
      * result is left in  op .  
      */  
     if (*cp == '!') {      if (*cp == '!') {
         op = OP_FORCE;          op = OP_FORCE;
     } else if (*cp == ':') {      } else if (*cp == ':') {
Line 942 
Line 934 
             op = OP_DEPENDS;              op = OP_DEPENDS;
         }          }
     } else {      } else {
         Parse_Error (PARSE_FATAL, "Missing dependency operator");          Parse_Error(PARSE_FATAL, "Missing dependency operator");
         return;          return;
     }      }
   
Line 953 
Line 945 
     /*      /*
      * Get to the first source       * Get to the first source
      */       */
     while (*cp && isspace (*cp)) {      while (*cp && isspace(*cp)) {
         cp++;          cp++;
     }      }
     line = cp;      line = cp;
Line 970 
Line 962 
     if (!*line) {      if (!*line) {
         switch (specType) {          switch (specType) {
             case Suffixes:              case Suffixes:
                 Suff_ClearSuffixes ();                  Suff_ClearSuffixes();
                 break;                  break;
             case Precious:              case Precious:
                 allPrecious = TRUE;                  allPrecious = TRUE;
Line 993 
Line 985 
          * set the initial character to a null-character so the loop to           * set the initial character to a null-character so the loop to
          * get sources won't get anything           * get sources won't get anything
          */           */
         Main_ParseArgLine (line);          Main_ParseArgLine(line);
         *line = '\0';          *line = '\0';
     } else if (specType == ExShell) {      } else if (specType == ExShell) {
         if (Job_ParseShell (line) != SUCCESS) {          if (Job_ParseShell(line) != SUCCESS) {
             Parse_Error (PARSE_FATAL, "improper shell specification");              Parse_Error(PARSE_FATAL, "improper shell specification");
             return;              return;
         }          }
         *line = '\0';          *line = '\0';
     } else if ((specType == NotParallel) || (specType == SingleShell)) {      } else if (specType == NotParallel || specType == SingleShell) {
         *line = '\0';          *line = '\0';
     }      }
   
     /*      /*
      * NOW GO FOR THE SOURCES       * NOW GO FOR THE SOURCES
      */       */
     if ((specType == Suffixes) || (specType == ExPath) ||      if (specType == Suffixes || specType == ExPath ||
         (specType == Includes) || (specType == Libs) ||          specType == Includes || specType == Libs ||
         (specType == Null))          specType == Null) {
     {  
         while (*line) {          while (*line) {
             /*              /*
              * If the target was one that doesn't take files as its sources               * If the target was one that doesn't take files as its sources
Line 1038 
Line 1029 
              * has no valid suffix.               * has no valid suffix.
              */               */
             char  savec;              char  savec;
             while (*cp && !isspace (*cp)) {              while (*cp && !isspace(*cp)) {
                 cp++;                  cp++;
             }              }
             savec = *cp;              savec = *cp;
             *cp = '\0';              *cp = '\0';
             switch (specType) {              switch (specType) {
                 case Suffixes:                  case Suffixes:
                     Suff_AddSuffix (line);                      Suff_AddSuffix(line);
                     break;                      break;
                 case ExPath:                  case ExPath:
                     Lst_ForEach(&paths, ParseAddDir, line);                      Lst_ForEach(&paths, ParseAddDir, line);
                     break;                      break;
                 case Includes:                  case Includes:
                     Suff_AddInclude (line);                      Suff_AddInclude(line);
                     break;                      break;
                 case Libs:                  case Libs:
                     Suff_AddLib (line);                      Suff_AddLib(line);
                     break;                      break;
                 case Null:                  case Null:
                     Suff_SetNull (line);                      Suff_SetNull(line);
                     break;                      break;
                 default:                  default:
                     break;                      break;
Line 1066 
Line 1057 
             if (savec != '\0') {              if (savec != '\0') {
                 cp++;                  cp++;
             }              }
             while (*cp && isspace (*cp)) {              while (*cp && isspace(*cp)) {
                 cp++;                  cp++;
             }              }
             line = cp;              line = cp;
Line 1079 
Line 1070 
              * specifications (i.e. things with left parentheses in them)               * specifications (i.e. things with left parentheses in them)
              * and handle them accordingly.               * and handle them accordingly.
              */               */
             while (*cp && !isspace (*cp)) {              while (*cp && !isspace(*cp)) {
                 if ((*cp == '(') && (cp > line) && (cp[-1] != '$')) {                  if (*cp == '(' && cp > line && cp[-1] != '$') {
                     /*                      /*
                      * Only stop for a left parenthesis if it isn't at the                       * Only stop for a left parenthesis if it isn't at the
                      * start of a word (that'll be for variable changes                       * start of a word (that'll be for variable changes
Line 1095 
Line 1086 
   
             if (*cp == '(') {              if (*cp == '(') {
                 GNode     *gn;                  GNode     *gn;
                 LIST      sources;      /* list of archive source names after                  LIST      sources; /* list of archive source names after
                                          * expansion */                                      * expansion */
   
                 Lst_Init(&sources);                  Lst_Init(&sources);
                 if (Arch_ParseArchive(&line, &sources, NULL) != SUCCESS) {                  if (Arch_ParseArchive(&line, &sources, NULL) != SUCCESS) {
                     Parse_Error (PARSE_FATAL,                      Parse_Error(PARSE_FATAL,
                                  "Error in source archive spec \"%s\"", line);                                   "Error in source archive spec \"%s\"", line);
                     return;                      return;
                 }                  }
   
                 while ((gn = (GNode *)Lst_DeQueue(&sources)) != NULL)                  while ((gn = (GNode *)Lst_DeQueue(&sources)) != NULL)
                     ParseDoSrc(tOp, gn->name, &curSrcs);                      ParseDoSrc(tOp, gn->name, &curSrcs);
                 Lst_Destroy(&sources, NOFREE);  
                 cp = line;                  cp = line;
             } else {              } else {
                 if (*cp) {                  if (*cp) {
Line 1117 
Line 1107 
   
                 ParseDoSrc(tOp, line, &curSrcs);                  ParseDoSrc(tOp, line, &curSrcs);
             }              }
             while (*cp && isspace (*cp)) {              while (*cp && isspace(*cp)) {
                 cp++;                  cp++;
             }              }
             line = cp;              line = cp;
Line 1125 
Line 1115 
     }      }
   
     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.  */
          */  
         Lst_Find(&targets, ParseFindMain, NULL);          Lst_Find(&targets, ParseFindMain, NULL);
     }      }
   
Line 1140 
Line 1128 
   
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * Parse_IsVar  --   * Parse_IsVar  --
  *      Return TRUE if the passed line is a variable assignment. A variable   *      Return TRUE if the passed line is a variable assignment. A variable
  *      assignment consists of a single word followed by optional whitespace   *      assignment consists of a single word followed by optional whitespace
  *      followed by either a += or an = operator.   *      followed by either a += or an = operator.
Line 1149 
Line 1137 
  *   *
  * Results:   * Results:
  *      TRUE if it is. FALSE if it ain't   *      TRUE if it is. FALSE if it ain't
  *  
  * Side Effects:  
  *      none  
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  */   */
 Boolean  Boolean
 Parse_IsVar (line)  Parse_IsVar(line)
     register char  *line;       /* the line to check */      char  *line;        /* the line to check */
 {  {
     register Boolean wasSpace = FALSE;  /* set TRUE if found a space */      Boolean wasSpace = FALSE;   /* set TRUE if found a space */
     register Boolean haveName = FALSE;  /* Set TRUE if have a variable name */      Boolean haveName = FALSE;   /* Set TRUE if have a variable name */
     int level = 0;      int level = 0;
 #define ISEQOPERATOR(c) \  #define ISEQOPERATOR(c) \
         (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!'))          ((c) == '+' || (c) == ':' || (c) == '?' || (c) == '!')
   
     /*  
      * Skip to variable name  
      */  
     for (;(*line == ' ') || (*line == '\t'); line++)  
         continue;  
   
     for (; *line != '=' || level != 0; line++)      for (; *line != '=' || level != 0; line++)
         switch (*line) {          switch (*line) {
         case '\0':          case '\0':
             /*              /* end-of-line -- can't be a variable assignment.  */
              * end-of-line -- can't be a variable assignment.  
              */  
             return FALSE;              return FALSE;
   
         case ' ':          case ' ':
Line 1200 
Line 1177 
         default:          default:
             if (wasSpace && haveName) {              if (wasSpace && haveName) {
                     if (ISEQOPERATOR(*line)) {                      if (ISEQOPERATOR(*line)) {
                         /*                          /* We must have a finished word.  */
                          * We must have a finished word  
                          */  
                         if (level != 0)                          if (level != 0)
                             return FALSE;                              return FALSE;
   
                         /*                          /* When an = operator [+?!:] is found, the next
                          * When an = operator [+?!:] is found, the next  
                          * character must be an = or it ain't a valid                           * character must be an = or it ain't a valid
                          * assignment.                           * assignment.  */
                          */  
                         if (line[1] == '=')                          if (line[1] == '=')
                             return haveName;                              return haveName;
 #ifdef SUNSHCMD                          /* This is a shell command.  */
                         /*                          if (FEATURES(FEATURE_SUNSHCMD) &&
                          * This is a shell command                              strncmp(line, ":sh", 3) == 0)
                          */  
                         if (strncmp(line, ":sh", 3) == 0)  
                             return haveName;                              return haveName;
 #endif  
                     }                      }
                     /*                      /* This is the start of another word, so not assignment.  */
                      * This is the start of another word, so not assignment.  
                      */  
                     return FALSE;                      return FALSE;
             }              }
             else {              else {
Line 1236 
Line 1204 
     return haveName;      return haveName;
 }  }
   
   static const char *
   find_op1(p)
       const char *p;
   {
       for(;; p++) {
           if (*p == '=' || isspace(*p) || *p == '$')
               break;
           if (p[1] == '=' && (*p == '?' || *p == ':' || *p == '!' || *p == '+'))
               break;
           if (p[0] == ':' && p[1] == 's' && p[2] == 'h')
               break;
       }
       return p;
   }
   
   static const char *
   find_op2(p)
       const char *p;
   {
       for(;; p++) {
           if (*p == '=' || isspace(*p) || *p == '$')
               break;
           if (p[1] == '=' && (*p == '?' || *p == ':' || *p == '!' || *p == '+'))
               break;
       }
       return p;
   }
   
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * Parse_DoVar  --   * Parse_DoVar  --
  *      Take the variable assignment in the passed line and do it in the   *      Take the variable assignment in the passed line and do it in the
  *      global context.   *      global context.
  *   *
Line 1248 
Line 1244 
  *          C++=/usr/bin/CC   *          C++=/usr/bin/CC
  *      is interpreted as "C+ +=" instead of "C++ =".   *      is interpreted as "C+ +=" instead of "C++ =".
  *   *
  * Results:  
  *      none  
  *  
  * Side Effects:   * Side Effects:
  *      the variable structure of the given variable name is altered in the   *      the variable structure of the given variable name is altered in the
  *      global context.   *      global context.
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  */   */
 void  void
 Parse_DoVar (line, ctxt)  Parse_DoVar(line, ctxt)
     char            *line;      /* a line guaranteed to be a variable      const char      *line;      /* a line guaranteed to be a variable
                                  * assignment. This reduces error checks */                                   * assignment. This reduces error checks */
     GSymT           *ctxt;      /* Context in which to do the assignment */      GSymT           *ctxt;    /* Context in which to do the assignment */
 {  {
     char           *cp; /* pointer into line */      const char      *end;
       const char      *arg;
     enum {      enum {
         VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL          VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL
     }               type;       /* Type of assignment */      }               type;       /* Type of assignment */
     char            *opc;       /* ptr to operator character to      struct Name     name;
                                  * null-terminate the variable name */  
     /*  
      * Avoid clobbered variable warnings by forcing the compiler  
      * to ``unregister'' variables  
      */  
 #if __GNUC__  
     (void) &cp;  
     (void) &line;  
 #endif  
   
     /*      end = Var_Name_Get(line, &name, (SymTable *)ctxt, TRUE,
      * Skip to variable name          FEATURES(FEATURE_SUNSHCMD) ? find_op1 : find_op2);
      */  
     while ((*line == ' ') || (*line == '\t')) {  
         line++;  
     }  
   
     /*      while (isspace(*end))
      * Skip to operator character, nulling out whitespace as we go          end++;
      */  
     for (cp = line + 1; *cp != '='; cp++) {  
         if (isspace (*cp)) {  
             *cp = '\0';  
         }  
     }  
     opc = cp-1;         /* operator is the previous character */  
     *cp++ = '\0';       /* nuke the = */  
   
     /*      /* Check operator type.  */
      * Check operator type      switch (*end) {
      */  
     switch (*opc) {  
         case '+':          case '+':
             type = VAR_APPEND;              type = VAR_APPEND;
             *opc = '\0';  
             break;              break;
   
         case '?':          case '?':
             /*              /* If the variable already has a value, we don't do anything.  */
              * If the variable already has a value, we don't do anything.              if (Var_Value_interval(name.s, name.e) != NULL) {
              */                  Var_Name_Free(&name);
             *opc = '\0';  
             if (Var_Exists(line, ctxt)) {  
                 return;                  return;
             } else {  
                 type = VAR_NORMAL;  
             }              }
               type = VAR_NORMAL;
             break;              break;
   
         case ':':          case ':':
             type = VAR_SUBST;              type = VAR_SUBST;
             *opc = '\0';  
             break;              break;
   
         case '!':          case '!':
             type = VAR_SHELL;              type = VAR_SHELL;
             *opc = '\0';  
             break;              break;
   
         default:          default:
 #ifdef SUNSHCMD              if (FEATURES(FEATURE_SUNSHCMD) && strncmp(end, ":sh", 3) == 0)
             while (*opc != ':')  
                 if (--opc < line)  
                     break;  
   
             if (strncmp(opc, ":sh", 3) == 0) {  
                 type = VAR_SHELL;                  type = VAR_SHELL;
                 *opc = '\0';              else
                 break;                  type = VAR_NORMAL;
             }  
 #endif  
             type = VAR_NORMAL;  
             break;              break;
     }      }
   
     while (isspace (*cp)) {      /* Find operator itself and go over it.  */
         cp++;      arg = end;
     }      while (*arg != '=')
           arg++;
       arg++;
       while (isspace(*arg))
           arg++;
   
     if (type == VAR_APPEND) {      if (type == VAR_APPEND)
         Var_Append (line, cp, ctxt);          Var_Append_interval(name.s, name.e, arg, ctxt);
     } else if (type == VAR_SUBST) {      else if (type == VAR_SUBST) {
           char *sub;
         /*          /*
          * Allow variables in the old value to be undefined, but leave their           * Allow variables in the old value to be undefined, but leave their
          * invocation alone -- this is done by forcing oldVars to be false.           * invocation alone -- this is done by forcing oldVars to be false.
Line 1360 
Line 1322 
          *           *
          * And not get an error.           * And not get an error.
          */           */
         Boolean   oldOldVars = oldVars;          Boolean   oldOldVars = oldVars;
   
         oldVars = FALSE;          oldVars = FALSE;
         cp = Var_Subst(cp, (SymTable *)ctxt, FALSE);          /* ensure the variable is set to something to avoid `variable
            * is recursive' errors.  */
           if (Var_Value_interval(name.s, name.e) == NULL)
               Var_Set_interval(name.s, name.e, "", ctxt);
   
           sub = Var_Subst(arg, (SymTable *)ctxt, FALSE);
         oldVars = oldOldVars;          oldVars = oldOldVars;
   
         Var_Set(line, cp, ctxt);          Var_Set_interval(name.s, name.e, sub, ctxt);
         free(cp);          free(sub);
     } else if (type == VAR_SHELL) {      } else if (type == VAR_SHELL) {
         Boolean freeCmd = FALSE; /* TRUE if the command needs to be freed, i.e.  
                                   * if any variable expansion was performed */  
         char *res, *err;          char *res, *err;
   
         if (strchr(cp, '$') != NULL) {          if (strchr(arg, '$') != NULL) {
             /*              char *sub;
              * There's a dollar sign in the command, so perform variable              /* There's a dollar sign in the command, so perform variable
              * expansion on the whole thing. The resulting string will need               * expansion on the whole thing. */
              * freeing when we're done, so set freeCmd to TRUE.              sub = Var_Subst(arg, NULL, TRUE);
              */              res = Cmd_Exec(sub, &err);
             cp = Var_Subst(cp, NULL, TRUE);              free(sub);
             freeCmd = TRUE;          } else
         }              res = Cmd_Exec(arg, &err);
   
         res = Cmd_Exec(cp, &err);          Var_Set_interval(name.s, name.e, res, ctxt);
         Var_Set(line, res, ctxt);  
         free(res);          free(res);
   
         if (err)          if (err)
             Parse_Error(PARSE_WARNING, err, cp);              Parse_Error(PARSE_WARNING, err, arg);
   
         if (freeCmd)      } else
             free(cp);          /* Normal assignment -- just do it.  */
     } else {          Var_Set_interval(name.s, name.e, arg, ctxt);
         /*      Var_Name_Free(&name);
          * Normal assignment -- just do it.  
          */  
         Var_Set(line, cp, ctxt);  
     }  
 }  }
   
   
 /*-  /*-
  * ParseAddCmd  --   * ParseAddCmd  --
  *      Lst_ForEach function to add a command line to all targets   *      Lst_ForEach function to add a command line to all targets
  *   *
  * Side Effects:   * Side Effects:
Line 1432 
Line 1392 
  *      having commands if it does, to keep from having shell commands   *      having commands if it does, to keep from having shell commands
  *      on multiple dependency lines.   *      on multiple dependency lines.
  *   *
  * Results:  
  *      None  
  *  
  * Side Effects:   * Side Effects:
  *      OP_HAS_COMMANDS may be set for the target.   *      OP_HAS_COMMANDS may be set for the target.
  *  
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static void  static void
 ParseHasCommands(gnp)  ParseHasCommands(gnp)
     void *gnp;      /* Node to examine */      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;
     }      }
Line 1455 
Line 1411 
  * Parse_AddIncludeDir --   * Parse_AddIncludeDir --
  *      Add a directory to the path searched for included makefiles   *      Add a directory to the path searched for included makefiles
  *      bracketed by double-quotes. Used by functions in main.c   *      bracketed by double-quotes. Used by functions in main.c
  *  
  * Results:  
  *      None.  
  *  
  * Side Effects:  
  *      The directory is appended to the list.  
  *  
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 void  void
 Parse_AddIncludeDir(dir)  Parse_AddIncludeDir(dir)
     char          *dir;     /* The name of the directory to add */      const char  *dir;   /* The name of the directory to add */
 {  {
     Dir_AddDir(&parseIncPath, dir, NULL);      Dir_AddDir(&parseIncPath, dir, NULL);
 }  }
Line 1482 
Line 1431 
  *      options   *      options
  *   *
  * Side Effects:   * Side Effects:
  *      old parse context is pushed on the stack, new file becomes   *      old parse context is pushed on the stack, new file becomes
  *      current context.   *      current context.
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  */   */
 static void  static void
 ParseDoInclude(file)  ParseDoInclude(file)
     char          *file;        /* file specification */      char          *file;        /* file specification */
 {  {
     char          endc;         /* the character which ends the file spec */      char          endc;         /* the character which ends the file spec */
     char          *cp;          /* current position in file spec */      char          *cp;          /* current position in file spec */
     Boolean       isSystem;     /* TRUE if makefile is a system makefile */      Boolean       isSystem;     /* TRUE if makefile is a system makefile */
   
     /* Skip to delimiter character so we know where to look */      /* Skip to delimiter character so we know where to look.  */
     while (*file == ' ' || *file == '\t')      while (*file == ' ' || *file == '\t')
         file++;          file++;
   
     if (*file != '"' && *file != '<') {      if (*file != '"' && *file != '<') {
         Parse_Error (PARSE_FATAL,          Parse_Error(PARSE_FATAL,
             ".include filename must be delimited by '\"' or '<'");              ".include filename must be delimited by '\"' or '<'");
         return;          return;
     }      }
Line 1515 
Line 1464 
         endc = '"';          endc = '"';
     }      }
   
     /* Skip to matching delimiter */      /* Skip to matching delimiter.  */
     for (cp = ++file; *cp && *cp != endc; cp++) {      for (cp = ++file; *cp != endc; cp++) {
         if (*cp == '\0') {          if (*cp == '\0') {
             Parse_Error(PARSE_FATAL,              Parse_Error(PARSE_FATAL,
                      "Unclosed %cinclude filename. '%c' expected",                       "Unclosed %cinclude filename. '%c' expected",
                      '.', endc);                       '.', endc);
             return;              return;
         }          }
     }      }
     ParseLookupIncludeFile(file, cp, isSystem);      ParseLookupIncludeFile(file, cp, isSystem, TRUE);
 }  }
   
 #ifdef SYSVINCLUDE  
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * ParseTraditionalInclude  --   * ParseTraditionalInclude  --
Line 1537 
Line 1485 
  *      the string following the "include".   *      the string following the "include".
  *   *
  * Side Effects:   * Side Effects:
  *      old parse context is pushed on the stack, new file becomes   *      old parse context is pushed on the stack, new file becomes
  *      current context.   *      current context.
    *
    *      XXX May wish to support multiple files and wildcards ?
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  */   */
 static void  static void
 ParseTraditionalInclude(file)  ParseTraditionalInclude(file)
     char          *file;        /* file specification */      char          *file;        /* file specification */
 {  {
     char          *cp;          /* current position in file spec */      char          *cp;          /* current position in file spec */
   
     /* Skip over whitespace */      /* Skip over whitespace.  */
     while (*file == ' ' || *file == '\t')      while (isspace(*file))
         file++;          file++;
   
     if (*file == '\0') {      if (*file == '\0') {
         Parse_Error (PARSE_FATAL,          Parse_Error(PARSE_FATAL,
                      "Filename missing from \"include\"");                       "Filename missing from \"include\"");
         return;          return;
     }      }
       /* Skip to end of line or next whitespace.  */
       for (cp = file; *cp != '\0' && !isspace(*cp);)
           cp++;
   
     /* Skip to end of line or next whitespace */      ParseLookupIncludeFile(file, cp, TRUE, TRUE);
   }
   
   /*-
    *---------------------------------------------------------------------
    * ParseConditionalInclude  --
    *      May push to another file.
    *
    *      No error if the file does not exist.
    *      See ParseTraditionalInclude otherwise.
    *---------------------------------------------------------------------
    */
   static void
   ParseConditionalInclude(file)
       char          *file;        /* file specification */
   {
       char          *cp;          /* current position in file spec */
   
       /* Skip over whitespace.  */
       while (isspace(*file))
           file++;
       if (*file == '\0') {
           Parse_Error(PARSE_FATAL,
                        "Filename missing from \"include\"");
           return;
       }
       /* Skip to end of line or next whitespace.  */
     for (cp = file; *cp != '\0' && !isspace(*cp);)      for (cp = file; *cp != '\0' && !isspace(*cp);)
         cp++;          cp++;
   
     ParseLookupIncludeFile(file, cp, TRUE);      ParseLookupIncludeFile(file, cp, TRUE, FALSE);
 }  }
 #endif  
   
 /* Common part to lookup and read an include file.  */  /* Common part to lookup and read an include file.  */
 static void  static void
 ParseLookupIncludeFile(spec, endSpec, isSystem)  ParseLookupIncludeFile(spec, endSpec, isSystem, errIfNotFound)
     char *spec;      char *spec;
     char *endSpec;      char *endSpec;
     Boolean isSystem;      Boolean isSystem;
       Boolean errIfNotFound;
 {  {
     char *file;      char *file;
     char *fullname;      char *fullname;
     char endc;      char endc;
   
     /* Substitute for any variables in the file name before trying to      /* Substitute for any variables in the file name before trying to
      * find the thing.  */       * find the thing.  */
     endc = *endSpec;      endc = *endSpec;
     *endSpec = '\0';      *endSpec = '\0';
     file = Var_Subst(spec, NULL, FALSE);      file = Var_Subst(spec, NULL, FALSE);
Line 1590 
Line 1568 
   
     /* Handle non-system non-absolute files... */      /* Handle non-system non-absolute files... */
     if (!isSystem && file[0] != '/') {      if (!isSystem && file[0] != '/') {
         /* ... by first searching relative to the including file's          /* ... by first searching relative to the including file's
          * location. We don't want to cd there, of course, so we           * location. We don't want to cd there, of course, so we
          * just tack on the old file's leading path components           * just tack on the old file's leading path components
          * and call Dir_FindFile to see if we can locate the beast.  */           * and call Dir_FindFile to see if we can locate the beast.  */
         char      *slash;          char      *slash;
   
         slash = strrchr(Parse_Getfilename(), '/');          slash = strrchr(Parse_Getfilename(), '/');
         if (slash != NULL) {          if (slash != NULL) {
Line 1610 
Line 1588 
         }          }
     }      }
   
     /* Now look first on the -I search path, then on the .PATH      /* Now look first on the -I search path, then on the .PATH
      * search path, if not found in a -I directory.       * search path, if not found in a -I directory.
      * XXX: Suffix specific?  */       * XXX: Suffix specific?  */
     if (fullname == NULL)      if (fullname == NULL)
         fullname = Dir_FindFile(file, &parseIncPath);          fullname = Dir_FindFile(file, &parseIncPath);
     if (fullname == NULL)      if (fullname == NULL)
         fullname = Dir_FindFile(file, &dirSearchPath);          fullname = Dir_FindFile(file, &dirSearchPath);
Line 1623 
Line 1601 
     if (fullname == NULL)      if (fullname == NULL)
         fullname = Dir_FindFile(file, &sysIncPath);          fullname = Dir_FindFile(file, &sysIncPath);
   
     if (fullname == NULL)      if (fullname == NULL && errIfNotFound)
         Parse_Error(PARSE_FATAL, "Could not find %s", file);              Parse_Error(PARSE_FATAL, "Could not find %s", file);
   
   
     free(file);      free(file);
   
     if (fullname != NULL) {      if (fullname != NULL) {
         FILE *f;          FILE *f;
   
         f = fopen(fullname, "r");          f = fopen(fullname, "r");
         if (f == NULL) {          if (f == NULL && errIfNotFound) {
             Parse_Error(PARSE_FATAL, "Cannot open %s", fullname);              Parse_Error(PARSE_FATAL, "Cannot open %s", fullname);
         } else {          } else {
             /* Once we find the absolute path to the file, we push the current              /* Once we find the absolute path to the file, we push the current
              * stream to the includes stack, and start reading from the new               * stream to the includes stack, and start reading from the new
              * file.  We set up the file name to be its absolute name so that               * file.  We set up the file name to be its absolute name so that
              * error messages are informative.  */               * error messages are informative.  */
             Parse_FromFile(fullname, f);              Parse_FromFile(fullname, f);
         }          }
     }      }
 }  }
   
   
   
   
   /* Strip comments from the line. May return either a copy of the line, or
    * the line itself.  */
   static char *
   strip_comments(copy, line)
       Buffer copy;
       const char *line;
   {
       const char *comment;
       const char *p;
   
       comment = strchr(line, '#');
       assert(comment != line);
       if (comment == NULL)
           return (char *)line;
       else {
           Buf_Reset(copy);
   
           for (p = line; *p != '\0'; p++) {
               if (*p == '\\') {
                   if (p[1] == '#') {
                       Buf_AddInterval(copy, line, p);
                       Buf_AddChar(copy, '#');
                       line = p+2;
                   }
                   if (p[1] != '\0')
                       p++;
               } else if (*p == '#')
                   break;
           }
           Buf_AddInterval(copy, line, p);
           Buf_KillTrailingSpaces(copy);
           return Buf_Retrieve(copy);
       }
   }
   
   static Boolean
   ParseIsCond(linebuf, copy, line)
       Buffer      linebuf;
       Buffer      copy;
       char        *line;
   {
   
       char        *stripped;
   
       while (*line != '\0' && isspace(*line))
           line++;
   
       /* The line might be a conditional. Ask the conditional module
        * about it and act accordingly.  */
       switch (Cond_Eval(line)) {
       case COND_SKIP:
           /* Skip to next conditional that evaluates to COND_PARSE.  */
           do {
               line = ParseSkipGetLine(linebuf);
               if (line != NULL) {
                   while (*line != '\0' && isspace(*line))
                       line++;
                       stripped = strip_comments(copy, line);
               }
           } while (line != NULL && Cond_Eval(stripped) != COND_PARSE);
           /* FALLTHROUGH */
       case COND_PARSE:
           return TRUE;
       default:
           break;
       }
   
       {
       For *loop;
   
       loop = For_Eval(line);
       if (loop != NULL) {
           Boolean ok;
           do {
               /* Find the matching endfor.  */
               line = ParseGetLoopLine(linebuf);
               if (line == NULL) {
                   Parse_Error(PARSE_FATAL,
                            "Unexpected end of file in for loop.\n");
                   return FALSE;
               }
               ok = For_Accumulate(loop, line);
           } while (ok);
           For_Run(loop);
           return TRUE;
       }
       }
   
       if (strncmp(line, "include", 7) == 0) {
           ParseDoInclude(line + 7);
           return TRUE;
       } else if (strncmp(line, "undef", 5) == 0) {
           char *cp;
   
           line+=5;
           while (*line != '\0' && isspace(*line))
               line++;
           for (cp = line; !isspace(*cp) && *cp != '\0';)
               cp++;
           *cp = '\0';
           Var_Delete(line);
           return TRUE;
       }
       return FALSE;
   }
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * ParseFinishLine --   * ParseFinishDependency --
  *      Handle the end of a dependency group.   *      Handle the end of a dependency group.
  *   *
  * Side Effects:   * Side Effects:
  *      inLine set FALSE. 'targets' list destroyed.   *      'targets' list destroyed.
  *   *
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static void  static void
 ParseFinishLine()  ParseFinishDependency()
 {  {
     if (inLine) {      Lst_Every(&targets, Suff_EndTransform);
         Lst_Every(&targets, Suff_EndTransform);      Lst_Destroy(&targets, ParseHasCommands);
         Lst_Destroy(&targets, ParseHasCommands);  
         Lst_Init(&targets);  
         inLine = FALSE;  
     }  
 }  }
   
   static void
   ParseDoCommands(line)
       const char *line;
   {
       /* add the command to the list of
        * commands of all targets in the dependency spec */
       char *cmd = estrdup(line);
   
       Lst_ForEach(&targets, ParseAddCmd, cmd);
   #ifdef CLEANUP
       Lst_AtEnd(&targCmds, cmd);
   #endif
   }
   
 /*-  /*-
  *---------------------------------------------------------------------   *---------------------------------------------------------------------
  * Parse_File --   * Parse_File --
Line 1673 
Line 1770 
  *      current dependency graph. This is the main function and controls   *      current dependency graph. This is the main function and controls
  *      almost every other function in this module   *      almost every other function in this module
  *   *
  * Results:  
  *      None  
  *  
  * Side Effects:   * Side Effects:
  *      Loads. Nodes are added to the list of all targets, nodes and links   *      Loads. Nodes are added to the list of all targets, nodes and links
  *      are added to the dependency graph. etc. etc. etc.   *      are added to the dependency graph. etc. etc. etc.
Line 1683 
Line 1777 
  */   */
 void  void
 Parse_File(name, stream)  Parse_File(name, stream)
     char          *name;        /* the name of the file being read */      char          *name;        /* the name of the file being read */
     FILE *        stream;       /* Stream open to makefile to parse */      FILE          *stream;      /* Stream open to makefile to parse */
 {  {
     register char *cp,          /* pointer into the line */      char          *cp,          /* pointer into the line */
                   *line;        /* the line we're working on */                    *line;        /* the line we're working on */
       Boolean       inDependency; /* true if currently in a dependency
                                    * line or its commands */
   
     inLine = FALSE;      BUFFER        buf;
       BUFFER        copy;
   
       Buf_Init(&buf, MAKE_BSIZE);
       Buf_Init(&copy, MAKE_BSIZE);
       inDependency = FALSE;
     Parse_FromFile(name, stream);      Parse_FromFile(name, stream);
     do {  
         while ((line = ParseReadLine ()) != NULL) {  
             if (*line == '.') {  
                 /*  
                  * Lines that begin with the special character are either  
                  * include or undef directives.  
                  */  
                 for (cp = line + 1; isspace (*cp); cp++) {  
                     continue;  
                 }  
                 if (strncmp (cp, "include", 7) == 0) {  
                     ParseDoInclude (cp + 7);  
                     goto nextLine;  
                 } else if (strncmp(cp, "undef", 5) == 0) {  
                     char *cp2;  
                     for (cp += 5; isspace((unsigned char) *cp); cp++) {  
                         continue;  
                     }  
   
                     for (cp2 = cp; !isspace((unsigned char) *cp2) &&      do {
                                    (*cp2 != '\0'); cp2++) {          while ((line = ParseReadLine(&buf)) != NULL) {
                         continue;  
                     }  
   
                     *cp2 = '\0';  
   
                     Var_Delete(cp, VAR_GLOBAL);  
                     goto nextLine;  
                 }  
             }  
             if (*line == '#') {  
                 /* If we're this far, the line must be a comment. */  
                 goto nextLine;  
             }  
   
             if (*line == '\t') {              if (*line == '\t') {
                 /*                  if (inDependency)
                  * If a line starts with a tab, it can only hope to be                      ParseDoCommands(line+1);
                  * a creation command.                  else
                  */                      Parse_Error(PARSE_FATAL,
 #ifndef POSIX                          "Unassociated shell command \"%s\"",
             shellCommand:                           line);
 #endif  
                 for (cp = line + 1; isspace (*cp); cp++) {  
                     continue;  
                 }  
                 if (*cp) {  
                     if (inLine) {  
                         /*  
                          * So long as it's not a blank line and we're actually  
                          * in a dependency spec, add the command to the list of  
                          * commands of all targets in the dependency spec  
                          */  
                         Lst_ForEach(&targets, ParseAddCmd, cp);  
 #ifdef CLEANUP  
                         Lst_AtEnd(&targCmds, line);  
 #endif  
                         continue;  
                     } else {  
                         Parse_Error (PARSE_FATAL,  
                                      "Unassociated shell command \"%s\"",  
                                      cp);  
                     }  
                 }  
 #ifdef SYSVINCLUDE  
             } else if (strncmp (line, "include", 7) == 0 &&  
                        isspace((unsigned char) line[7]) &&  
                        strchr(line, ':') == NULL) {  
                 /*  
                  * It's an S3/S5-style "include".  
                  */  
                 ParseTraditionalInclude (line + 7);  
                 goto nextLine;  
 #endif  
             } else if (Parse_IsVar (line)) {  
                 ParseFinishLine();  
                 Parse_DoVar (line, VAR_GLOBAL);  
             } else {              } else {
                 /*                  char *stripped;
                  * We now know it's a dependency line so it needs to have all                  stripped = strip_comments(&copy, line);
                  * variables expanded before being parsed. Tell the variable                  if (*stripped == '.' && ParseIsCond(&buf, &copy, stripped+1))
                  * module to complain if some variable is undefined...                      ;
                  * To make life easier on novices, if the line is indented we                  else if (FEATURES(FEATURE_SYSVINCLUDE) &&
                  * first make sure the line has a dependency operator in it.                      strncmp(stripped, "include", 7) == 0 &&
                  * If it doesn't have an operator and we're in a dependency                      isspace(stripped[7]) &&
                  * line's script, we assume it's actually a shell command                      strchr(stripped, ':') == NULL) {
                  * and add it to the current list of targets.                      /* It's an S3/S5-style "include".  */
                  */                          ParseTraditionalInclude(stripped + 7);
 #ifndef POSIX                  } else if (FEATURES(FEATURE_CONDINCLUDE) &&
                 Boolean nonSpace = FALSE;                      (*stripped == '-' || *stripped == 's') &&
 #endif                      strncmp(stripped+1, "include", 7) == 0 &&
                       isspace(stripped[8]) &&
                 cp = line;                      strchr(stripped, ':') == NULL) {
                 if (isspace((unsigned char) line[0])) {                          ParseConditionalInclude(stripped+8);
                     while ((*cp != '\0') && isspace((unsigned char) *cp)) {  
                         cp++;  
                     }  
                     if (*cp == '\0') {  
                         goto nextLine;  
                     }  
 #ifndef POSIX  
                     while ((*cp != ':') && (*cp != '!') && (*cp != '\0')) {  
                         nonSpace = TRUE;  
                         cp++;  
                     }  
 #endif  
                 }  
   
 #ifndef POSIX  
                 if (*cp == '\0') {  
                     if (inLine) {  
                         Parse_Error (PARSE_WARNING,  
                                      "Shell command needs a leading tab");  
                         goto shellCommand;  
                     } else if (nonSpace) {  
                         Parse_Error (PARSE_FATAL, "Missing operator");  
                     }  
                 } else {                  } else {
 #endif                      char *dep;
                     ParseFinishLine();  
   
                     cp = Var_Subst(line, NULL, TRUE);                      if (inDependency)
                     free (line);                          ParseFinishDependency();
                     line = cp;                      if (Parse_IsVar(stripped)) {
                           inDependency = FALSE;
                           Parse_DoVar(stripped, VAR_GLOBAL);
                       } else {
                           size_t pos;
                           char *end;
   
                     /* Need a new list for the target nodes */                          /* Need a new list for the target nodes.  */
                     Lst_Destroy(&targets, NOFREE);                          Lst_Init(&targets);
                     Lst_Init(&targets);                          inDependency = TRUE;
                     inLine = TRUE;  
   
                     ParseDoDependency (line);                          dep = NULL;
 #ifndef POSIX                          /* First we need to find eventual dependencies */
                           pos = strcspn(stripped, ":!");
                           /* go over :!, and find ;  */
                           if (stripped[pos] != '\0' &&
                               (end = strchr(stripped+pos+1, ';')) != NULL) {
                                   if (line != stripped)
                                       /* find matching ; in original... The
                                        * original might be slightly longer.  */
                                       dep = strchr(line+(end-stripped), ';');
                                   else
                                       dep = end;
                                   /* kill end of line. */
                                   *end = '\0';
                           }
                           /* We now know it's a dependency line so it needs to
                            * have all variables expanded before being parsed.
                            * Tell the variable module to complain if some
                            * variable is undefined... */
                           cp = Var_Subst(stripped, NULL, TRUE);
                           ParseDoDependency(cp);
                           free(cp);
   
                           /* Parse dependency if it's not empty. */
                           if (dep != NULL) {
                               do {
                                   dep++;
                               } while (isspace(*dep));
                               if (*dep != '\0')
                                   ParseDoCommands(dep);
                           }
                       }
                 }                  }
 #endif  
             }              }
   
             nextLine:  
   
             free (line);  
         }          }
     } while (Parse_NextFile());      } while (Parse_NextFile());
   
     /* Make sure conditionals are clean */      if (inDependency)
           ParseFinishDependency();
       /* Make sure conditionals are clean.  */
     Cond_End();      Cond_End();
   
     Finish_Errors();      Finish_Errors();
       Buf_Destroy(&buf);
       Buf_Destroy(&copy);
 }  }
   
 /*-  /*-
Line 1868 
Line 1911 
 #ifdef CLEANUP  #ifdef CLEANUP
     Lst_Destroy(&targCmds, (SimpleProc)free);      Lst_Destroy(&targCmds, (SimpleProc)free);
     Lst_Destroy(&fileNames, (SimpleProc)free);      Lst_Destroy(&fileNames, (SimpleProc)free);
     Lst_Delete(&targets, NOFREE);      Lst_Destroy(&targets, NOFREE);
     Lst_Destroy(&sysIncPath, Dir_Destroy);      Lst_Destroy(&sysIncPath, Dir_Destroy);
     Lst_Destroy(&parseIncPath, Dir_Destroy);      Lst_Destroy(&parseIncPath, Dir_Destroy);
     LowParse_End();      LowParse_End();
Line 1882 
Line 1925 
  *      Return a Lst of the main target to create for main()'s sake. If   *      Return a Lst of the main target to create for main()'s sake. If
  *      no such target exists, we Punt with an obnoxious error message.   *      no such target exists, we Punt with an obnoxious error message.
  *   *
  * Side Effects:   * Side effect:
  *      Add the node to create to the list.   *      Add the node to create to the list.
  *  
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 void  void
 Parse_MainName(listmain)  Parse_MainName(listmain)
     Lst           listmain;     /* result list */      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.58  
changed lines
  Added in v.1.59