]> git.ipfire.org Git - thirdparty/make.git/commitdiff
Fix Savannah bug #1454: skip over semicolons (and comments) inside variable
authorPaul Smith <psmith@gnu.org>
Sat, 25 Jun 2005 21:30:13 +0000 (21:30 +0000)
committerPaul Smith <psmith@gnu.org>
Sat, 25 Jun 2005 21:30:13 +0000 (21:30 +0000)
references in target definition lines.

ChangeLog
make.h
misc.c
read.c
tests/ChangeLog
tests/scripts/misc/general3

index 8e53a647119a43a96bd340c251517e6e7fa2599e..c76458023bf9902aebe48e06f08bc33774707055 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2005-06-25  Paul D. Smith  <psmith@gnu.org>
 
+       Fix Savannah bug #1454.
+
+       * read.c (find_char_unquote): Accept a new argument IGNOREVARS.
+       If it's set, then don't stop on STOPCHARs or BLANKs if they're
+       inside a variable reference.  Make this function static as it's
+       only used here.
+       (eval): Call find_char_unquote() with IGNOREVARS set when we're
+       parsing an unexpanded line looking for semicolons.
+       * misc.c (remove_comments): Move this to read.c and make it static
+       as it's only used there.  Call find_char_unquote() with new arg.
+       * make.h: Remove prototypes for find_char_unquote() and
+       remove_comments() since they're static now.
+
        Implement the MAKE_RESTARTS variable, and disable -B if it's >0.
        Fixes Savannah bug #7566.
 
diff --git a/make.h b/make.h
index 699a6e4096f547d6c4152ef073839afd8268f0af..20ee56953555d156fcf846179d0a9642ab861536 100644 (file)
--- a/make.h
+++ b/make.h
@@ -421,11 +421,9 @@ extern char *find_next_token PARAMS ((char **, unsigned int *));
 extern char *next_token PARAMS ((const char *));
 extern char *end_of_token PARAMS ((const char *));
 extern void collapse_continuations PARAMS ((char *));
-extern void remove_comments PARAMS((char *));
 extern char *lindex PARAMS ((const char *, const char *, int));
 extern int alpha_compare PARAMS ((const void *, const void *));
 extern void print_spaces PARAMS ((unsigned int));
-extern char *find_char_unquote PARAMS ((char *, int, int, int));
 extern char *find_percent PARAMS ((char *));
 extern FILE *open_tmpfile PARAMS ((char **, const char *));
 
diff --git a/misc.c b/misc.c
index 995fd20232d59d218a572d2ced350575dee6db14..ac50270910bc0ec3038b4083fc3f8b2f1720c45c 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -149,22 +149,6 @@ collapse_continuations (char *line)
 
   *out = '\0';
 }
-
-
-/* Remove comments from LINE.
-   This is done by copying the text at LINE onto itself.  */
-
-void
-remove_comments (char *line)
-{
-  char *comment;
-
-  comment = find_char_unquote (line, '#', 0, 0);
-
-  if (comment != 0)
-    /* Cut off the line at the #.  */
-    *comment = '\0';
-}
 \f
 /* Print N spaces (used in debug for target-depth).  */
 
diff --git a/read.c b/read.c
index 40ff986bbba8961f79c12b0d8f5b551c40606c3a..2e979956531bcb6b94debf087b3b4ac9e7905abb 100644 (file)
--- a/read.c
+++ b/read.c
@@ -143,6 +143,9 @@ static void record_target_var PARAMS ((struct nameseq *filenames, char *defn,
                                        const struct floc *flocp));
 static enum make_word_type get_next_mword PARAMS ((char *buffer, char *delim,
                         char **startp, unsigned int *length));
+static void remove_comments PARAMS ((char *line));
+static char *find_char_unquote PARAMS ((char *string, int stop1,
+                                        int stop2, int blank, int ignorevars));
 \f
 /* Read in all the makefiles and return the chain of their names.  */
 
@@ -882,7 +885,7 @@ eval (struct ebuffer *ebuf, int set_default)
 
         /* Search the line for an unquoted ; that is not after an
            unquoted #.  */
-        cmdleft = find_char_unquote (line, ';', '#', 0);
+        cmdleft = find_char_unquote (line, ';', '#', 0, 1);
         if (cmdleft != 0 && *cmdleft == '#')
           {
             /* We found a comment before a semicolon.  */
@@ -929,7 +932,7 @@ eval (struct ebuffer *ebuf, int set_default)
             if (cmdleft == 0)
               {
                 /* Look for a semicolon in the expanded line.  */
-                cmdleft = find_char_unquote (p2, ';', 0, 0);
+                cmdleft = find_char_unquote (p2, ';', 0, 0, 0);
 
                 if (cmdleft != 0)
                   {
@@ -956,7 +959,7 @@ eval (struct ebuffer *ebuf, int set_default)
                   }
               }
 
-            colonp = find_char_unquote(p2, ':', 0, 0);
+            colonp = find_char_unquote(p2, ':', 0, 0, 0);
 #ifdef HAVE_DOS_PATHS
             /* The drive spec brain-damage strikes again...  */
             /* Note that the only separators of targets in this context
@@ -965,7 +968,7 @@ eval (struct ebuffer *ebuf, int set_default)
             while (colonp && (colonp[1] == '/' || colonp[1] == '\\') &&
                    colonp > p2 && isalpha ((unsigned char)colonp[-1]) &&
                    (colonp == p2 + 1 || strchr (" \t(", colonp[-2]) != 0))
-              colonp = find_char_unquote(colonp + 1, ':', 0, 0);
+              colonp = find_char_unquote(colonp + 1, ':', 0, 0, 0);
 #endif
             if (colonp != 0)
               break;
@@ -1079,7 +1082,7 @@ eval (struct ebuffer *ebuf, int set_default)
 
         /* This is a normal target, _not_ a target-specific variable.
            Unquote any = in the dependency list.  */
-        find_char_unquote (lb_next, '=', 0, 0);
+        find_char_unquote (lb_next, '=', 0, 0, 0);
 
         /* We have some targets, so don't ignore the following commands.  */
         no_targets = 0;
@@ -1094,7 +1097,7 @@ eval (struct ebuffer *ebuf, int set_default)
             /* Look for a semicolon in the expanded line.  */
             if (cmdleft == 0)
               {
-                cmdleft = find_char_unquote (p2, ';', 0, 0);
+                cmdleft = find_char_unquote (p2, ';', 0, 0, 0);
                 if (cmdleft != 0)
                   *(cmdleft++) = '\0';
               }
@@ -1310,8 +1313,23 @@ eval (struct ebuffer *ebuf, int set_default)
 
   return 1;
 }
-
 \f
+
+/* Remove comments from LINE.
+   This is done by copying the text at LINE onto itself.  */
+
+static void
+remove_comments (char *line)
+{
+  char *comment;
+
+  comment = find_char_unquote (line, '#', 0, 0, 0);
+
+  if (comment != 0)
+    /* Cut off the line at the #.  */
+    *comment = '\0';
+}
+
 /* Execute a `define' directive.
    The first line has already been read, and NAME is the name of
    the variable to be defined.  The following lines remain to be read.  */
@@ -2158,34 +2176,75 @@ record_files (struct nameseq *filenames, char *pattern, char *pattern_percent,
    Backslashes quote STOPCHAR, blanks if BLANK is nonzero, and backslash.
    Quoting backslashes are removed from STRING by compacting it into
    itself.  Returns a pointer to the first unquoted STOPCHAR if there is
-   one, or nil if there are none.  */
+   one, or nil if there are none.  STOPCHARs inside variable references are
+   ignored if IGNOREVARS is true.
 
-char *
-find_char_unquote (char *string, int stop1, int stop2, int blank)
+   STOPCHAR _cannot_ be '$' if IGNOREVARS is true.  */
+
+static char *
+find_char_unquote (char *string, int stop1, int stop2, int blank,
+                   int ignorevars)
 {
   unsigned int string_len = 0;
   register char *p = string;
+  int pcount = 0;
+  char openparen;
+  char closeparen;
+
+  if (ignorevars)
+    ignorevars = '$';
 
   while (1)
     {
       if (stop2 && blank)
-       while (*p != '\0' && *p != stop1 && *p != stop2
+       while (*p != '\0' && *p != ignorevars && *p != stop1 && *p != stop2
               && ! isblank ((unsigned char) *p))
          ++p;
       else if (stop2)
-       while (*p != '\0' && *p != stop1 && *p != stop2)
+       while (*p != '\0' && *p != ignorevars && *p != stop1 && *p != stop2)
          ++p;
       else if (blank)
-       while (*p != '\0' && *p != stop1
+       while (*p != '\0' && *p != ignorevars && *p != stop1
               && ! isblank ((unsigned char) *p))
          ++p;
       else
-       while (*p != '\0' && *p != stop1)
+       while (*p != '\0' && *p != ignorevars && *p != stop1)
          ++p;
 
       if (*p == '\0')
        break;
 
+      /* If we stopped due to a variable reference, skip over its contents.  */
+      if (*p == ignorevars)
+        {
+          char openparen = p[1];
+
+          p += 2;
+
+          /* Skip the contents of a non-quoted, multi-char variable ref.  */
+          if (openparen == '(' || openparen == '{')
+            {
+              unsigned int pcount = 1;
+              char closeparen = (openparen == '(' ? ')' : '}');
+
+              while (*p)
+                {
+                  if (*p == openparen)
+                    ++pcount;
+                  else if (*p == closeparen)
+                    if (--pcount == 0)
+                      {
+                        ++p;
+                        break;
+                      }
+                  ++p;
+                }
+            }
+
+          /* Skipped the variable reference: look for STOPCHARS again.  */
+          continue;
+        }
+
       if (p > string && p[-1] == '\\')
        {
          /* Search for more backslashes.  */
@@ -2221,7 +2280,7 @@ find_char_unquote (char *string, int stop1, int stop2, int blank)
 char *
 find_percent (char *pattern)
 {
-  return find_char_unquote (pattern, '%', 0, 0);
+  return find_char_unquote (pattern, '%', 0, 0, 0);
 }
 \f
 /* Parse a string into a sequence of filenames represented as a
@@ -2263,7 +2322,7 @@ parse_file_seq (char **stringp, int stopchar, unsigned int size, int strip)
 
       /* Yes, find end of next name.  */
       q = p;
-      p = find_char_unquote (q, stopchar, VMS_COMMA, 1);
+      p = find_char_unquote (q, stopchar, VMS_COMMA, 1, 0);
 #ifdef VMS
        /* convert comma separated list to space separated */
       if (p && *p == ',')
@@ -2274,7 +2333,7 @@ parse_file_seq (char **stringp, int stopchar, unsigned int size, int strip)
           && !(isspace ((unsigned char)p[1]) || !p[1]
                || isspace ((unsigned char)p[-1])))
       {
-       p = find_char_unquote (p+1, stopchar, VMS_COMMA, 1);
+       p = find_char_unquote (p+1, stopchar, VMS_COMMA, 1, 0);
       }
 #endif
 #ifdef HAVE_DOS_PATHS
@@ -2285,7 +2344,7 @@ parse_file_seq (char **stringp, int stopchar, unsigned int size, int strip)
     if (stopchar == ':')
       while (p != 0 && !isspace ((unsigned char)*p) &&
              (p[1] == '\\' || p[1] == '/') && isalpha ((unsigned char)p[-1]))
-        p = find_char_unquote (p + 1, stopchar, VMS_COMMA, 1);
+        p = find_char_unquote (p + 1, stopchar, VMS_COMMA, 1, 0);
 #endif
       if (p == 0)
        p = q + strlen (q);
index be5169b433667ec4dc273816db4a278c84142e7d..fa6420d7987b7b68523b7e11ca43d0da8ecb2f76 100644 (file)
@@ -1,5 +1,8 @@
 2005-06-25  Paul D. Smith  <psmith@gnu.org>
 
+       * scripts/misc/general3: Test semicolons in variable references.
+       Tests fix for Savannah bug #1454.
+
        * scripts/variables/MAKE_RESTARTS: New file: test the
        MAKE_RESTARTS variable.
        * scripts/options/dash-B: Test re-exec doesn't loop infinitely.
index 2421ed4e862f6b217b1b9fb89372c1b71393e801..40b7ed93ac9c437396cac5de6f22ef2e4bbdace1 100644 (file)
@@ -5,13 +5,7 @@ This tests random features of the parser that need to be supported, and
 which have either broken at some point in the past or seem likely to
 break.";
 
-$makefile2 = &get_tmpfile;
-
-open(MAKEFILE,"> $makefile");
-
-# The contents of the Makefile ...
-
-print MAKEFILE <<EOF;
+run_make_test("
 # We want to allow both empty commands _and_ commands that resolve to empty.
 EMPTY =
 
@@ -31,20 +25,15 @@ TAB =   \t  \# A TAB and some spaces
 
 \$(STR)
 
-\$(STR) \$(TAB)
-
-EOF
-
-close(MAKEFILE);
-
-&run_make_with_options($makefile,"",&get_logfile);
-$answer = "$make_name: Nothing to be done for `all'.\n";
-&compare_output($answer,&get_logfile(1));
-
+\$(STR) \$(TAB)",
+              '', "#MAKE#: Nothing to be done for `all'.");
 
 # TEST 2
 
 # Make sure files without trailing newlines are handled properly.
+# Have to use the old style invocation to test this.
+
+$makefile2 = &get_tmpfile;
 
 open(MAKEFILE, "> $makefile2");
 print MAKEFILE "all:;\@echo FOO = \$(FOO)\nFOO = foo";
@@ -54,5 +43,14 @@ close(MAKEFILE);
 $answer = "FOO = foo\n";
 &compare_output($answer,&get_logfile(1));
 
+# TEST 3
+
+# Check semicolons in variable references
+
+run_make_test('
+$(if true,$(info true; true))
+all: ; @:
+',
+              '', 'true; true');
 
 1;