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

Diff for /src/usr.bin/make/compat.c between version 1.59 and 1.60

version 1.59, 2007/09/17 09:28:36 version 1.60, 2007/09/17 12:42:09
Line 66 
Line 66 
 #include "lst.h"  #include "lst.h"
 #include "pathnames.h"  #include "pathnames.h"
   
 /* The following array is used to make a fast determination of which  
  * characters are interpreted specially by the shell.  If a command  
  * contains any of these characters, it is executed by the shell, not  
  * directly by us.  */  
   
 static char meta[256];  
   
 static GNode *ENDNode;  
 static void CompatInterrupt(int);  static void CompatInterrupt(int);
 static int CompatRunCommand(LstNode, void *);  static int CompatRunCommand(LstNode, void *);
 static void CompatMake(void *, void *);  static void CompatMake(void *, void *);
 static int shellneed(char **);  static int run_gnode(GNode *);
   static void setup_meta(void);
   static char **recheck_command_for_shell(char **);
   static void run_command(const char *, bool);
   
 static volatile sig_atomic_t interrupted;  static volatile sig_atomic_t interrupted;
   
Line 88 
Line 83 
                 interrupted = signo;                  interrupted = signo;
 }  }
   
 /*-  /* The following array is used to make a fast determination of which
  *-----------------------------------------------------------------------   * characters are interpreted specially by the shell.  If a command
  * shellneed --   * contains any of these characters, it is executed by the shell, not
  *   * directly by us.  */
  * Results:  static char         meta[256];
  *      Returns 1 if a specified set of arguments  
  *      must be executed by the shell,  static void
  *      0 if it can be run via execve, and -1 if the command can be  setup_meta(void)
  *      handled internally  
  *  
  * Side Effects:  
  *      May modify the process umask  
  *-----------------------------------------------------------------------  
  */  
 static int  
 shellneed(char **av)  
 {  {
           char *p;
   
           for (p = "#=|^(){};&<>*?[]:$`\\\n"; *p != '\0'; p++)
                   meta[(unsigned char) *p] = 1;
           /* The null character serves as a sentinel in the string.  */
           meta[0] = 1;
   }
   
   static char **
   recheck_command_for_shell(char **av)
   {
         char *runsh[] = {          char *runsh[] = {
                 "alias", "cd", "eval", "exec", "exit", "read", "set", "ulimit",                  "alias", "cd", "eval", "exit", "read", "set", "ulimit",
                 "unalias", "unset", "wait",                  "unalias", "unset", "wait", "umask", NULL
                 NULL  
         };          };
   
         char **p;          char **p;
   
         /* FIXME most of these ARE actual no-ops */          /* optimization: if exec cmd, we avoid the intermediate shell */
           if (strcmp(av[0], "exec") == 0)
                   av++;
   
         for (p = runsh; *p; p++)          for (p = runsh; *p; p++)
                 if (strcmp(av[0], *p) == 0)                  if (strcmp(av[0], *p) == 0)
                         return 1;                          return NULL;
   
         if (strcmp(av[0], "umask") == 0) {          return av;
                 long umi;  }
                 char *ep = NULL;  
                 mode_t um;  
   
                 if (av[1] != NULL) {  static void
                         umi = strtol(av[1], &ep, 8);  run_command(const char *cmd, bool errCheck)
                         if (ep == NULL)  {
                                 return 1;          const char *p;
                         um = umi;          char *shargv[4];
                 }          char **todo;
                 else {  
                         um = umask(0);          shargv[0] = _PATH_BSHELL;
                         printf("%o\n", um);  
                 }          shargv[1] = errCheck ? "-ec" : "-c";
                 (void)umask(um);          shargv[2] = (char *)cmd;
                 return -1;          shargv[3] = NULL;
   
           todo = shargv;
   
   
           /* Search for meta characters in the command. If there are no meta
            * characters, there's no need to execute a shell to execute the
            * command.  */
           for (p = cmd; !meta[(unsigned char)*p]; p++)
                   continue;
           if (*p == '\0') {
                   char *bp;
                   char **av;
                   int argc;
                   /* No meta-characters, so probably no need to exec a shell.
                    * Break the command into words to form an argument vector
                    * we can execute.  */
                   av = brk_string(cmd, &argc, &bp);
                   av = recheck_command_for_shell(av);
                   if (av != NULL)
                           todo = av;
         }          }
           execvp(todo[0], todo);
   
         return 0;          if (errno == ENOENT)
                   fprintf(stderr, "%s: not found\n", todo[0]);
           else
                   perror(todo[0]);
           _exit(1);
 }  }
   
 /*-  /*-
Line 154 
Line 177 
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static int  static int
 CompatRunCommand(LstNode cmdNode,/* Command to execute */  CompatRunCommand(LstNode cmdNode,       /* Command to execute */
     void *gnp)                  /* Node from which the command came */      void *gnp)                          /* Node from which the command came */
 {  {
         char *cmdStart;         /* Start of expanded command */          char *cmdStart; /* Start of expanded command */
         char *cp, *bp = NULL;          bool silent;    /* Don't print command */
         bool silent;            /* Don't print command */          bool doExecute; /* Execute the command */
         bool doExecute;         /* Execute the command */          bool errCheck;  /* Check errors */
         volatile bool errCheck; /* Check errors */          int reason;     /* Reason for child's death */
         int reason;             /* Reason for child's death */          int status;     /* Description of child's death */
         int status;             /* Description of child's death */          pid_t cpid;     /* Child actually found */
         pid_t cpid;             /* Child actually found */          pid_t stat;     /* Status of fork */
         pid_t stat;             /* Status of fork */  
         char **volatile av;     /* Argument vector for thing to exec */  
         int argc;               /* Number of arguments in av or 0 if not  
                                  * dynamically allocated */  
         char *cmd = (char *)Lst_Datum(cmdNode);          char *cmd = (char *)Lst_Datum(cmdNode);
         GNode *gn = (GNode *)gnp;          GNode *gn = (GNode *)gnp;
         static char *shargv[4] = { _PATH_BSHELL };  
   
         silent = gn->type & OP_SILENT;          silent = gn->type & OP_SILENT;
         errCheck = !(gn->type & OP_IGNORE);          errCheck = !(gn->type & OP_IGNORE);
Line 179 
Line 197 
   
         cmdStart = Var_Subst(cmd, &gn->context, false);          cmdStart = Var_Subst(cmd, &gn->context, false);
   
         /* brk_string will return an argv with a NULL in av[0], thus causing          /* How can we execute a null command ? we warn the user that the
          * execvp to choke and die horribly. Besides, how can we execute a null           * command expanded to nothing (is this the right thing to do?).  */
          * command? In any case, we warn the user that the command expanded to  
          * nothing (is this the right thing to do?).  */  
   
         if (*cmdStart == '\0') {          if (*cmdStart == '\0') {
                 free(cmdStart);                  free(cmdStart);
                 Error("%s expands to empty string", cmd);                  Error("%s expands to empty string", cmd);
Line 192 
Line 207 
                 cmd = cmdStart;                  cmd = cmdStart;
         Lst_Replace(cmdNode, cmdStart);          Lst_Replace(cmdNode, cmdStart);
   
         if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {          if ((gn->type & OP_SAVE_CMDS) && (gn != end_node)) {
                 Lst_AtEnd(&ENDNode->commands, cmdStart);                  Lst_AtEnd(&end_node->commands, cmdStart);
                   end_node->type &= ~OP_DUMMY;
                 return 1;                  return 1;
         } else if (strcmp(cmdStart, "...") == 0) {          } else if (strcmp(cmdStart, "...") == 0) {
                 gn->type |= OP_SAVE_CMDS;                  gn->type |= OP_SAVE_CMDS;
                 return 1;                  return 1;
         }          }
   
         for (;; cmd++) {          for (;; cmd++) {
                 if (*cmd == '@')                  if (*cmd == '@')
                         silent = DEBUG(LOUD) ? false : true;                          silent = DEBUG(LOUD) ? false : true;
Line 210 
Line 225 
                 else                  else
                         break;                          break;
         }          }
   
         while (isspace(*cmd))          while (isspace(*cmd))
                 cmd++;                  cmd++;
   
         /* Search for meta characters in the command. If there are no meta  
          * characters, there's no need to execute a shell to execute the  
          * command.  */  
         for (cp = cmd; !meta[(unsigned char)*cp]; cp++) {  
                 continue;  
         }  
   
         /* Print the command before echoing if we're not supposed to be quiet          /* Print the command before echoing if we're not supposed to be quiet
          * for this one. We also print the command if -n given.  */           * for this one. We also print the command if -n given.  */
         if (!silent || noExecute) {          if (!silent || noExecute) {
                 printf("%s\n", cmd);                  printf("%s\n", cmd);
                 fflush(stdout);                  fflush(stdout);
         }          }
   
         /* If we're not supposed to execute any commands, this is as far as          /* If we're not supposed to execute any commands, this is as far as
          * we go...  */           * we go...  */
         if (!doExecute)          if (!doExecute)
                 return 1;                  return 1;
   
         if (*cp != '\0') {  
                 /* If *cp isn't the null character, we hit a "meta" character  
                  * and need to pass the command off to the shell. We give the  
                  * shell the -e flag as well as -c if it's supposed to exit  
                  * when it hits an error.  */  
   
                 shargv[1] = errCheck ? "-ec" : "-c";  
                 shargv[2] = cmd;  
                 shargv[3] = NULL;  
                 av = shargv;  
                 argc = 0;  
         } else {  
                 /* No meta-characters, so probably no need to exec a shell.  
                  * Break the command into words to form an argument vector  
                  * we can execute.  */  
                 av = brk_string(cmd, &argc, &bp);  
                 switch (shellneed(av)) {  
                 case -1: /* handled internally */  
                         free(bp);  
                         free(av);  
                         return 1;  
                 case 1:  
                         shargv[1] = errCheck ? "-ec" : "-c";  
                         shargv[2] = cmd;  
                         shargv[3] = NULL;  
                         free(av);  
                         free(bp);  
                         bp = NULL;  
                         av = shargv;  
                         argc = 0;  
                         break;  
                 default: /* nothing needed */  
                         break;  
                 }  
         }  
   
         /* Fork and execute the single command. If the fork fails, we abort.  */          /* Fork and execute the single command. If the fork fails, we abort.  */
         cpid = fork();          switch (cpid = fork()) {
         if (cpid == -1)          case -1:
                 Fatal("Could not fork");                  Fatal("Could not fork");
         if (cpid == 0) {                  /*NOTREACHED*/
                 execvp(av[0], av);          case 0:
                 if (errno == ENOENT)                  run_command(cmd, errCheck);
                         fprintf(stderr, "%s: not found\n", av[0]);                  /*NOTREACHED*/
                 else          default:
                         perror(av[0]);                  break;
                 _exit(1);  
         }          }
         if (bp) {  
                 free(av);  
                 free(bp);  
         }  
         free(cmdStart);          free(cmdStart);
         Lst_Replace(cmdNode, NULL);          Lst_Replace(cmdNode, NULL);
   
Line 305 
Line 269 
                         else if (WIFEXITED(reason)) {                          else if (WIFEXITED(reason)) {
                                 status = WEXITSTATUS(reason);   /* exited */                                  status = WEXITSTATUS(reason);   /* exited */
                                 if (status != 0)                                  if (status != 0)
                                         printf("*** Error code %d", status);                                      printf("*** Error code %d", status);
                         } else {                          } else {
                                 status = WTERMSIG(reason);      /* signaled */                                  status = WTERMSIG(reason);      /* signaled */
                                 printf("*** Signal %d", status);                                  printf("*** Signal %d", status);
Line 320 
Line 284 
                                                  * but let others continue.  */                                                   * but let others continue.  */
                                                 printf(" (continuing)\n");                                                  printf(" (continuing)\n");
                                 } else {                                  } else {
                                         /* Continue executing commands for this                                          /* Continue executing commands for
                                          * target.  If we return 0, this will                                           * this target.  If we return 0,
                                          * happen...  */                                           * this will happen...  */
                                         printf(" (ignored)\n");                                          printf(" (ignored)\n");
                                         status = 0;                                          status = 0;
                                 }                                  }
Line 335 
Line 299 
   
         /* This is reached only if interrupted */          /* This is reached only if interrupted */
         if (!Targ_Precious(gn)) {          if (!Targ_Precious(gn)) {
                 char *file = Varq_Value(TARGET_INDEX, gn);                  char      *file = Varq_Value(TARGET_INDEX, gn);
   
                 if (!noExecute && eunlink(file) != -1)                  if (!noExecute && eunlink(file) != -1)
                         Error("*** %s removed\n", file);                          Error("*** %s removed\n", file);
         }          }
         if (interrupted == SIGINT) {          if (interrupted == SIGINT) {
                 GNode *i = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);  
                 signal(SIGINT, SIG_IGN);                  signal(SIGINT, SIG_IGN);
                 signal(SIGTERM, SIG_IGN);                  signal(SIGTERM, SIG_IGN);
                 signal(SIGHUP, SIG_IGN);                  signal(SIGHUP, SIG_IGN);
                 signal(SIGQUIT, SIG_IGN);                  signal(SIGQUIT, SIG_IGN);
                 interrupted = 0;                  interrupted = 0;
                 if (i != NULL)                  run_gnode(interrupt_node);
                         Lst_ForEachNodeWhile(&i->commands, CompatRunCommand, i);  
                 exit(SIGINT);                  exit(SIGINT);
         }          }
         exit(interrupted);          exit(interrupted);
 }  }
   
   static int
   run_gnode(GNode *gn)
   {
           if (gn != NULL && (gn->type & OP_DUMMY) == 0) {
                   Lst_ForEachNodeWhile(&gn->commands, CompatRunCommand, gn);
                   return gn->made;
           } else
                   return NOSUCHNODE;
   }
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * CompatMake --   * CompatMake --
Line 379 
Line 351 
                 Make_HandleUse(gn, pgn);                  Make_HandleUse(gn, pgn);
         } else if (gn->made == UNMADE) {          } else if (gn->made == UNMADE) {
                 /* First mark ourselves to be made, then apply whatever                  /* First mark ourselves to be made, then apply whatever
                  * transformations the suffix module thinks are necessary. Once                   * transformations the suffix module thinks are necessary.
                  * that's done, we can descend and make all our children. If                   * Once that's done, we can descend and make all our children.
                  * any of them has an error but the -k flag was given, our                   * If any of them has an error but the -k flag was given,
                  * 'make' field will be set false again.  This is our signal to                   * our 'make' field will be set false again.  This is our
                  * not attempt to do anything but abort our parent as well.  */                   * signal to not attempt to do anything but abort our
                    * parent as well.  */
                 gn->make = true;                  gn->make = true;
                 gn->made = BEINGMADE;                  gn->made = BEINGMADE;
                 Suff_FindDeps(gn);                  Suff_FindDeps(gn);
Line 401 
Line 374 
   
                 /* All the children were made ok. Now cmtime contains the                  /* All the children were made ok. Now cmtime contains the
                  * modification time of the newest child, we need to find out                   * modification time of the newest child, we need to find out
                  * if we exist and when we were modified last. The criteria for                   * if we exist and when we were modified last. The criteria
                  * datedness are defined by the Make_OODate function.  */                   * for datedness are defined by the Make_OODate function.  */
                 if (DEBUG(MAKE))                  if (DEBUG(MAKE))
                         printf("Examining %s...", gn->name);                          printf("Examining %s...", gn->name);
                 if (! Make_OODate(gn)) {                  if (!Make_OODate(gn)) {
                         gn->made = UPTODATE;                          gn->made = UPTODATE;
                         if (DEBUG(MAKE))                          if (DEBUG(MAKE))
                                 printf("up-to-date.\n");                                  printf("up-to-date.\n");
Line 413 
Line 386 
                 } else if (DEBUG(MAKE))                  } else if (DEBUG(MAKE))
                         printf("out-of-date.\n");                          printf("out-of-date.\n");
   
                 /* If the user is just seeing if something is out-of-date, exit                  /* If the user is just seeing if something is out-of-date,
                  * now to tell him/her "yes".  */                   * exit now to tell him/her "yes".  */
                 if (queryFlag)                  if (queryFlag)
                         exit(-1);                          exit(-1);
   
                 /* We need to be re-made. We also have to make sure we've got a                  /* We need to be re-made. We also have to make sure we've
                  * $?  variable. To be nice, we also define the $> variable                   * got a $?  variable. To be nice, we also define the $>
                  * using Make_DoAllVar().  */                   * variable using Make_DoAllVar().  */
                 Make_DoAllVar(gn);                  Make_DoAllVar(gn);
   
                 /* Alter our type to tell if errors should be ignored or things                  /* Alter our type to tell if errors should be ignored or things
                  * should not be printed so CompatRunCommand knows what to do.                   * should not be printed so CompatRunCommand knows what to do.
                  * */                   */
                 if (Targ_Ignore(gn))                  if (Targ_Ignore(gn))
                         gn->type |= OP_IGNORE;                          gn->type |= OP_IGNORE;
                 if (Targ_Silent(gn))                  if (Targ_Silent(gn))
                         gn->type |= OP_SILENT;                          gn->type |= OP_SILENT;
   
                 if (Job_CheckCommands(gn, Fatal)) {                  if (Job_CheckCommands(gn, Fatal)) {
                         /* Our commands are ok, but we still have to worry                      /* Our commands are ok, but we still have to worry
                          * about the -t flag... */                       * about the -t flag...     */
                         if (!touchFlag)                      if (!touchFlag)
                                 Lst_ForEachNodeWhile(&gn->commands,                              run_gnode(gn);
                                     CompatRunCommand, gn);                      else
                         else                              Job_Touch(gn, gn->type & OP_SILENT);
                                 Job_Touch(gn, gn->type & OP_SILENT);  
                 } else                  } else
                         gn->made = ERROR;                          gn->made = ERROR;
   
                 if (gn->made != ERROR) {                  if (gn->made != ERROR) {
                         /* If the node was made successfully, mark it so,                          /* If the node was made successfully, mark it so,
                          * update its modification time and timestamp all its                           * update its modification time and timestamp all
                          * parents. Note that for .ZEROTIME targets, the                           * its parents.
                          * timestamping isn't done.  This is to keep its state                           * This is to keep its state from affecting that of
                          * from affecting that of its parent.  */                           * its parent.  */
                         gn->made = MADE;                          gn->made = MADE;
                         /* This is what Make does and it's actually a good                          /* This is what Make does and it's actually a good
                          * thing, as it allows rules like                           * thing, as it allows rules like
Line 455 
Line 427 
                          *      cmp -s y.tab.h parse.h || cp y.tab.h parse.h                           *      cmp -s y.tab.h parse.h || cp y.tab.h parse.h
                          *                           *
                          * to function as intended. Unfortunately, thanks to                           * to function as intended. Unfortunately, thanks to
                          * the stateless nature of NFS (and the speed of this                           * the stateless nature of NFS (and the speed of
                          * program), there are times when the modification time                           * this program), there are times when the
                          * of a file created on a remote machine will not be                           * modification time of a file created on a remote
                          * modified before the stat() implied by the Dir_MTime                           * machine will not be modified before the stat()
                          * occurs, thus leading us to believe that the file is                           * implied by the Dir_MTime occurs, thus leading us
                          * unchanged, wreaking havoc with files that depend on                           * to believe that the file is unchanged, wreaking
                          * this one.                           * havoc with files that depend on this one.
                          *  
                          * I have decided it is better to make too much than to  
                          * make too little, so this stuff is commented out  
                          * unless you're sure it's ok.  -- ardeb 1/12/88  
                          */                           */
                         if (noExecute || is_out_of_date(Dir_MTime(gn)))                          if (noExecute || is_out_of_date(Dir_MTime(gn)))
                                 gn->mtime = now;                                  gn->mtime = now;
Line 493 
Line 461 
                         exit(1);                          exit(1);
                 }                  }
         } else if (gn->made == ERROR)          } else if (gn->made == ERROR)
                 /* Already had an error when making this beastie. Tell the                  /* Already had an error when making this beastie. Tell the parent
                  * parent to abort.  */                   * to abort.  */
                 pgn->make = false;                  pgn->make = false;
         else {          else {
                 if (Lst_Member(&gn->iParents, pgn) != NULL) {                  if (Lst_Member(&gn->iParents, pgn) != NULL) {
Line 526 
Line 494 
 void  void
 Compat_Run(Lst targs)           /* List of target nodes to re-create */  Compat_Run(Lst targs)           /* List of target nodes to re-create */
 {  {
         char *cp;               /* Pointer to string of shell meta-characters */          GNode     *gn = NULL;   /* Current root target */
         GNode *gn = NULL;       /* Current root target */          int       errors;       /* Number of targets not remade due to errors */
         int errors;             /* Number of targets not remade due to errors */  
   
         signal(SIGINT, CompatInterrupt);          signal(SIGINT, CompatInterrupt);
         signal(SIGTERM, CompatInterrupt);          signal(SIGTERM, CompatInterrupt);
         signal(SIGHUP, CompatInterrupt);          signal(SIGHUP, CompatInterrupt);
         signal(SIGQUIT, CompatInterrupt);          signal(SIGQUIT, CompatInterrupt);
   
         for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++)          setup_meta();
                 meta[(unsigned char) *cp] = 1;  
         /* The null character serves as a sentinel in the string.  */  
         meta[0] = 1;  
   
         ENDNode = Targ_FindNode(".END", TARG_CREATE);  
         /* If the user has defined a .BEGIN target, execute the commands          /* If the user has defined a .BEGIN target, execute the commands
          * attached to it.  */           * attached to it.  */
         if (!queryFlag) {          if (!queryFlag) {
                 gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);                  if (run_gnode(begin_node) == ERROR) {
                 if (gn != NULL) {                          printf("\n\nStop.\n");
                         Lst_ForEachNodeWhile(&gn->commands, CompatRunCommand,                          exit(1);
                             gn);  
                         if (gn->made == ERROR) {  
                                 printf("\n\nStop.\n");  
                                 exit(1);  
                         }  
                 }                  }
         }          }
   
Line 560 
Line 517 
          * in one of several states:           * in one of several states:
          *          UPTODATE        gn was already up-to-date           *          UPTODATE        gn was already up-to-date
          *          MADE            gn was recreated successfully           *          MADE            gn was recreated successfully
          *          ERROR           An error occurred while gn was being created           *          ERROR           An error occurred while gn was being
            *                          created
          *          ABORTED         gn was not remade because one of its           *          ABORTED         gn was not remade because one of its
          *                          inferiors could not be made due to errors.           *                          inferiors could not be made due to errors.
          */           */
         errors = 0;          errors = 0;
         while ((gn = (GNode *)Lst_DeQueue(targs)) != NULL) {          while ((gn = (GNode *)Lst_DeQueue(targs)) != NULL) {
Line 579 
Line 537 
   
         /* If the user has defined a .END target, run its commands.  */          /* If the user has defined a .END target, run its commands.  */
         if (errors == 0)          if (errors == 0)
                 Lst_ForEachNodeWhile(&ENDNode->commands, CompatRunCommand,                  run_gnode(end_node);
                     ENDNode);  
 }  }

Legend:
Removed from v.1.59  
changed lines
  Added in v.1.60