While looking at another problem, I noticed that in several places GNU
make 3.79's source passes a possibly negative char value to ctype
operations like "isblank".  On hosts where characters are signed, this
has undefined behavior; e.g. isblank ('\200') might dump core, though
it more typically just returns a garbage value.

You may recall that I reported a similar set of problems against make
3.78.1 back in September.  Most got fixed, but some remain.  Perhaps
you relied on "gcc -Wchar-subscripts" to find these problems?  That
doesn't work in general, unfortunately.

Here is a patch.

2000-05-17  Paul Eggert  <[EMAIL PROTECTED]>

        * commands.c (chop_commands): Ensure ctype macro args are nonnegative.
        * expand.c (variable_expand_string): Likewise.
        * function.c (subst_expand, lookup_function, msdos_openpipe):
        Likewise.
        * job.c (vms_redirect, start_job_command, new_job, child_execute_job,
        construct_command_argv_internal, construct_command_argv): Likewise.
        * main.c (decode_env_switches, quote_for_env): Likewise.
        * misc.c (collapse_continuations, end_of_token, end_of_token_w32,
        next_token): Likewise.
        * read.c (read_makefile, do_define, conditional_line,
        find_char_unquote,get_next_mword): Likewise.
        * variable.c (try_variable_definition): Likewise.
        * vpath.c (construct_vpath_list): Likewise.
        * w32/pathstuff.c (convert_vpath_to_windows32): Likewise.

===================================================================
RCS file: commands.c,v
retrieving revision 3.79
retrieving revision 3.79.0.1
diff -pu -r3.79 -r3.79.0.1
--- commands.c  2000/02/05 07:37:40     3.79
+++ commands.c  2000/05/18 00:28:13     3.79.0.1
@@ -300,7 +300,7 @@ chop_commands (cmds)
       int flags = 0;
 
       for (p = lines[idx];
-           isblank (*p) || *p == '-' || *p == '@' || *p == '+';
+           isblank ((unsigned char)*p) || *p == '-' || *p == '@' || *p == '+';
            ++p)
         switch (*p)
           {
===================================================================
RCS file: expand.c,v
retrieving revision 3.79
retrieving revision 3.79.0.1
diff -pu -r3.79 -r3.79.0.1
--- expand.c    2000/02/24 18:57:23     3.79
+++ expand.c    2000/05/18 00:28:13     3.79.0.1
@@ -367,7 +367,7 @@ variable_expand_string (line, string, le
          break;
 
        default:
-         if (isblank (p[-1]))
+         if (isblank ((unsigned char)p[-1]))
            break;
 
          /* A $ followed by a random char is a variable reference:
===================================================================
RCS file: function.c,v
retrieving revision 3.79
retrieving revision 3.79.0.1
diff -pu -r3.79 -r3.79.0.1
--- function.c  2000/04/04 19:22:03     3.79
+++ function.c  2000/05/18 00:28:13     3.79.0.1
@@ -92,10 +92,10 @@ subst_expand (o, text, subst, replace, s
       /* If we're substituting only by fully matched words,
         or only at the ends of words, check that this case qualifies.  */
       if ((by_word
-          && ((p > t && !isblank (p[-1]))
-              || (p[slen] != '\0' && !isblank (p[slen]))))
+          && ((p > t && !isblank ((unsigned char)p[-1]))
+              || (p[slen] != '\0' && !isblank ((unsigned char)p[slen]))))
          || (suffix_only
-             && (p[slen] != '\0' && !isblank (p[slen]))))
+             && (p[slen] != '\0' && !isblank ((unsigned char)p[slen]))))
        /* Struck out.  Output the rest of the string that is
           no longer to be replaced.  */
        o = variable_buffer_output (o, subst, slen);
@@ -235,7 +235,7 @@ lookup_function (table, s)
 
   for (; table->name != NULL; ++table)
     if (table->len <= len
-        && (isblank (s[table->len]) || s[table->len] == '\0')
+        && (isblank ((unsigned char)s[table->len]) || s[table->len] == '\0')
         && strneq (s, table->name, table->len))
       return table;
 
@@ -1274,7 +1274,7 @@ msdos_openpipe (int* pipedes, int *pidp,
   extern int dos_command_running, dos_status;
 
   /* Make sure not to bother processing an empty line.  */
-  while (isblank (*text))
+  while (isblank ((unsigned char)*text))
     ++text;
   if (*text == '\0')
     return 0;
===================================================================
RCS file: job.c,v
retrieving revision 3.79
retrieving revision 3.79.0.1
diff -pu -r3.79 -r3.79.0.1
--- job.c       2000/02/24 20:26:25     3.79
+++ job.c       2000/05/18 00:28:13     3.79.0.1
@@ -295,10 +295,10 @@ vms_redirect (desc, fname, ibuf)
   extern char *vmsify ();
 
   ibuf++;
-  while (isspace (*ibuf))
+  while (isspace ((unsigned char)*ibuf))
     ibuf++;
   fptr = ibuf;
-  while (*ibuf && !isspace (*ibuf))
+  while (*ibuf && !isspace ((unsigned char)*ibuf))
     ibuf++;
   *ibuf = 0;
   if (strcmp (fptr, "/dev/null") != 0)
@@ -890,7 +890,7 @@ start_job_command (child)
        flags |= COMMANDS_RECURSE;
       else if (*p == '-')
        child->noerror = 1;
-      else if (!isblank (*p))
+      else if (!isblank ((unsigned char)*p))
        break;
       ++p;
     }
@@ -1411,7 +1411,8 @@ new_job (file)
 
                          /* Discard any preceding whitespace that has
                             already been written to the output.  */
-                         while (out > ref && isblank (out[-1]))
+                         while (out > ref
+                                && isblank ((unsigned char)out[-1]))
                            --out;
 
                          /* Replace it all with a single space.  */
@@ -1806,7 +1807,7 @@ child_execute_job (argv, child)
 
   DB (DB_JOBS, ("child_execute_job (%s)\n", argv));
 
-  while (isspace (*argv))
+  while (isspace ((unsigned char)*argv))
     argv++;
 
   if (*argv == 0)
@@ -1831,9 +1832,9 @@ child_execute_job (argv, child)
            p++;
            if (*p == '\n')
              p++;
-           if (isspace (*p))
+           if (isspace ((unsigned char)*p))
              {
-               do { p++; } while (isspace (*p));
+               do { p++; } while (isspace ((unsigned char)*p));
                p--;
              }
            *q = *p;
@@ -1993,11 +1994,11 @@ child_execute_job (argv, child)
             case '\n':
               /* At a newline, skip any whitespace around a leading $
                  from the command and issue exactly one $ into the DCL. */
-              while (isspace (*p))
+              while (isspace ((unsigned char)*p))
                 p++;
               if (*p == '$')
                 p++;
-              while (isspace (*p))
+              while (isspace ((unsigned char)*p))
                 p++;
               fwrite (p, 1, q - p, outfile);
               fputc ('$', outfile);
@@ -2444,7 +2445,7 @@ construct_command_argv_internal (line, r
     *restp = NULL;
 
   /* Make sure not to bother processing an empty line.  */
-  while (isblank (*line))
+  while (isblank ((unsigned char)*line))
     ++line;
   if (*line == '\0')
     return 0;
@@ -2962,12 +2963,12 @@ construct_command_argv (line, restp, fil
   for (;;)
     {
       while ((*cptr != 0)
-            && (isspace (*cptr)))
+            && (isspace ((unsigned char)*cptr)))
        cptr++;
       if (*cptr == 0)
        break;
       while ((*cptr != 0)
-            && (!isspace(*cptr)))
+            && (!isspace((unsigned char)*cptr)))
        cptr++;
       argc++;
     }
@@ -2981,14 +2982,14 @@ construct_command_argv (line, restp, fil
   for (;;)
     {
       while ((*cptr != 0)
-            && (isspace (*cptr)))
+            && (isspace ((unsigned char)*cptr)))
        cptr++;
       if (*cptr == 0)
        break;
       DB (DB_JOBS, ("argv[%d] = [%s]\n", argc, cptr));
       argv[argc++] = cptr;
       while ((*cptr != 0)
-            && (!isspace(*cptr)))
+            && (!isspace((unsigned char)*cptr)))
        cptr++;
       if (*cptr != 0)
        *cptr++ = 0;
===================================================================
RCS file: main.c,v
retrieving revision 3.79
retrieving revision 3.79.0.1
diff -pu -r3.79 -r3.79.0.1
--- main.c      2000/02/24 21:00:20     3.79
+++ main.c      2000/05/18 00:28:13     3.79.0.1
@@ -2325,14 +2325,14 @@ decode_env_switches (envar, len)
     {
       if (*value == '\\' && value[1] != '\0')
        ++value;                /* Skip the backslash.  */
-      else if (isblank (*value))
+      else if (isblank ((unsigned char)*value))
        {
          /* End of the word.  */
          *p++ = '\0';
          argv[++argc] = p;
          do
            ++value;
-         while (isblank (*value));
+         while (isblank ((unsigned char)*value));
          continue;
        }
       *p++ = *value++;
@@ -2365,7 +2365,7 @@ quote_for_env (out, in)
     {
       if (*in == '$')
        *out++ = '$';
-      else if (isblank (*in) || *in == '\\')
+      else if (isblank ((unsigned char)*in) || *in == '\\')
         *out++ = '\\';
       *out++ = *in++;
     }
===================================================================
RCS file: misc.c,v
retrieving revision 3.79
retrieving revision 3.79.0.1
diff -pu -r3.79 -r3.79.0.1
--- misc.c      1999/11/22 06:15:51     3.79
+++ misc.c      2000/05/18 00:28:13     3.79.0.1
@@ -120,7 +120,7 @@ collapse_continuations (line)
       if (backslash)
        {
          in = next_token (in);
-         while (out > line && isblank (out[-1]))
+         while (out > line && isblank ((unsigned char)out[-1]))
            --out;
          *out++ = ' ';
        }
@@ -478,7 +478,7 @@ char *
 end_of_token (s)
      char *s;
 {
-  while (*s != '\0' && !isblank (*s))
+  while (*s != '\0' && !isblank ((unsigned char)*s))
     ++s;
   return s;
 }
@@ -495,7 +495,8 @@ end_of_token_w32 (s, stopchar)
   register char *p = s;
   register int backslash = 0;
 
-  while (*p != '\0' && *p != stopchar && (backslash || !isblank (*p)))
+  while (*p != '\0' && *p != stopchar
+        && (backslash || !isblank ((unsigned char)*p)))
     {
       if (*p++ == '\\')
         {
@@ -522,7 +523,7 @@ next_token (s)
 {
   register char *p = s;
 
-  while (isblank (*p))
+  while (isblank ((unsigned char)*p))
     ++p;
   return p;
 }
===================================================================
RCS file: read.c,v
retrieving revision 3.79
retrieving revision 3.79.0.1
diff -pu -r3.79 -r3.79.0.1
--- read.c      2000/04/04 23:33:15     3.79
+++ read.c      2000/05/18 00:28:13     3.79.0.1
@@ -550,7 +550,7 @@ read_makefile (filename, flags)
                 removed), so it could be a complex variable/function
                 reference that might contain blanks.  */
              p = strchr (p2, '\0');
-             while (isblank (p[-1]))
+             while (isblank ((unsigned char)p[-1]))
                --p;
              do_define (p2, p - p2, o_file, infile, &fileinfo);
            }
@@ -562,7 +562,8 @@ read_makefile (filename, flags)
          p2 = next_token (p + 8);
          if (*p2 == '\0')
            error (&fileinfo, _("empty `override' directive"));
-         if (strneq (p2, "define", 6) && (isblank (p2[6]) || p2[6] == '\0'))
+         if (strneq (p2, "define", 6)
+             && (isblank ((unsigned char)p2[6]) || p2[6] == '\0'))
            {
              if (ignoring)
                in_ignored_define = 1;
@@ -577,7 +578,7 @@ read_makefile (filename, flags)
                     removed), so it could be a complex variable/function
                     reference that might contain blanks.  */
                  p = strchr (p2, '\0');
-                 while (isblank (p[-1]))
+                 while (isblank ((unsigned char)p[-1]))
                    --p;
                  do_define (p2, p - p2, o_override, infile, &fileinfo);
                }
@@ -726,7 +727,7 @@ read_makefile (filename, flags)
       else if (lb.buffer[0] == '\t')
        {
          p = collapsed;        /* Ignore comments.  */
-         while (isblank (*p))
+         while (isblank ((unsigned char)*p))
            ++p;
          if (*p == '\0')
            /* The line is completely blank; that is harmless.  */
@@ -1108,7 +1109,7 @@ do_define (name, namelen, origin, infile
 
       p = next_token (lb.buffer);
       len = strlen (p);
-      if ((len == 5 || (len > 5 && isblank (p[5])))
+      if ((len == 5 || (len > 5 && isblank ((unsigned char)p[5])))
           && strneq (p, "endef", 5))
        {
          p += 5;
@@ -1300,7 +1301,7 @@ conditional_line (line, flocp)
        {
          /* Strip blanks after the first string.  */
          char *p = line++;
-         while (isblank (p[-1]))
+         while (isblank ((unsigned char)p[-1]))
            --p;
          *p = '\0';
        }
@@ -1824,7 +1825,7 @@ find_char_unquote (string, stopchars, bl
   while (1)
     {
       while (*p != '\0' && strchr (stopchars, *p) == 0
-            && (!blank || !isblank (*p)))
+            && (!blank || !isblank ((unsigned char)*p)))
        ++p;
       if (*p == '\0')
        break;
@@ -2257,7 +2258,7 @@ get_next_mword (buffer, delim, startp, l
   char c;
 
   /* Skip any leading whitespace.  */
-  while (isblank(*p))
+  while (isblank ((unsigned char)*p))
     ++p;
 
   beg = p;
===================================================================
RCS file: variable.c,v
retrieving revision 3.79
retrieving revision 3.79.0.1
diff -pu -r3.79 -r3.79.0.1
--- variable.c  2000/02/24 18:57:25     3.79
+++ variable.c  2000/05/18 00:28:13     3.79.0.1
@@ -859,7 +859,7 @@ try_variable_definition (flocp, line, or
     }
 
   beg = next_token (line);
-  while (end > beg && isblank (end[-1]))
+  while (end > beg && isblank ((unsigned char)end[-1]))
     --end;
   p = next_token (p);
 
===================================================================
RCS file: vpath.c,v
retrieving revision 3.79
retrieving revision 3.79.0.1
diff -pu -r3.79 -r3.79.0.1
--- vpath.c     1999/10/15 07:01:11     3.79
+++ vpath.c     2000/05/18 00:28:13     3.79.0.1
@@ -222,7 +222,7 @@ construct_vpath_list (pattern, dirpath)
   maxelem = 2;
   p = dirpath;
   while (*p != '\0')
-    if (*p++ == PATH_SEPARATOR_CHAR || isblank (*p))
+    if (*p++ == PATH_SEPARATOR_CHAR || isblank ((unsigned char)*p))
       ++maxelem;
 
   vpath = (char **) xmalloc (maxelem * sizeof (char *));
@@ -230,7 +230,7 @@ construct_vpath_list (pattern, dirpath)
 
   /* Skip over any initial separators and blanks.  */
   p = dirpath;
-  while (*p == PATH_SEPARATOR_CHAR || isblank (*p))
+  while (*p == PATH_SEPARATOR_CHAR || isblank ((unsigned char)*p))
     ++p;
 
   elem = 0;
@@ -241,7 +241,8 @@ construct_vpath_list (pattern, dirpath)
 
       /* Find the end of this entry.  */
       v = p;
-      while (*p != '\0' && *p != PATH_SEPARATOR_CHAR && !isblank (*p))
+      while (*p != '\0' && *p != PATH_SEPARATOR_CHAR
+            && !isblank ((unsigned char)*p))
        ++p;
 
       len = p - v;
@@ -274,7 +275,7 @@ construct_vpath_list (pattern, dirpath)
        }
 
       /* Skip over separators and blanks between entries.  */
-      while (*p == PATH_SEPARATOR_CHAR || isblank (*p))
+      while (*p == PATH_SEPARATOR_CHAR || isblank ((unsigned char)*p))
        ++p;
     }
 
===================================================================
RCS file: w32/pathstuff.c,v
retrieving revision 3.78.1.0
retrieving revision 3.78.1.1
diff -pu -r3.78.1.0 -r3.78.1.1
--- w32/pathstuff.c     1998/07/31 20:39:54     3.78.1.0
+++ w32/pathstuff.c     1999/09/29 18:34:12     3.78.1.1
@@ -16,7 +16,7 @@ convert_vpath_to_windows32(char *Path, c
         * contain blanks get trounced here. Use 8.3 format as a workaround.
         */
        for (etok = Path; etok && *etok; etok++)
-               if (isblank(*etok))
+               if (isblank ((unsigned char) *etok))
                        *etok = to_delim;
 
        return (convert_Path_to_windows32(Path, to_delim));
@@ -42,7 +42,7 @@ convert_Path_to_windows32(char *Path, ch
                 etok[0] = to_delim;
                 p = ++etok;
                 continue;    /* ignore empty bucket */
-            } else if (!isalpha(*p)) {
+            } else if (!isalpha ((unsigned char) *p)) {
                 /* found one to count, handle things like '.' */
                 *etok = to_delim;
                 p = ++etok;

Reply via email to