awk: fix the case where nested "for" loops with the same variable misbehave
authorDenys Vlasenko <vda.linux@googlemail.com>
Wed, 10 Mar 2010 18:20:32 +0000 (19:20 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 10 Mar 2010 18:20:32 +0000 (19:20 +0100)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
editors/awk.c
testsuite/awk.tests

index b9bc01f1653dd6ec8b6e1b22465c69ae1b5ff315..5cc4adce77b47178ffefff5ca0d99f684f057524 100644 (file)
@@ -912,8 +912,10 @@ static void nvfree(var *v)
                        free(p->x.array->items);
                        free(p->x.array);
                }
-               if (p->type & VF_WALK)
+               if (p->type & VF_WALK) {
+                       //bb_error_msg("free(walker@%p:%p) #1", &p->x.walker, p->x.walker);
                        free(p->x.walker);
+               }
 
                clrvar(p);
        }
@@ -1724,18 +1726,20 @@ static void hashwalk_init(var *v, xhash *array)
        char **w;
        hash_item *hi;
        unsigned i;
-
-       if (v->type & VF_WALK)
-               free(v->x.walker);
+       char **prev_walker = (v->type & VF_WALK) ? v->x.walker : NULL;
 
        v->type |= VF_WALK;
-       w = v->x.walker = xzalloc(2 + 2*sizeof(char *) + array->glen);
-       w[0] = w[1] = (char *)(w + 2);
+
+       /* walker structure is: "[ptr2end][ptr2start][prev]<word1>NUL<word2>NUL" */
+       w = v->x.walker = xzalloc(2 + 3*sizeof(char *) + array->glen);
+       //bb_error_msg("walker@%p=%p", &v->x.walker, v->x.walker);
+       w[0] = w[1] = (char *)(w + 3);
+       w[2] = (char *)prev_walker;
        for (i = 0; i < array->csize; i++) {
                hi = array->items[i];
                while (hi) {
-                       strcpy(*w, hi->name);
-                       nextword(w);
+                       strcpy(w[0], hi->name);
+                       nextword(&w[0]);
                        hi = hi->next;
                }
        }
@@ -1746,10 +1750,16 @@ static int hashwalk_next(var *v)
        char **w;
 
        w = v->x.walker;
-       if (w[1] == w[0])
+       if (w[1] == w[0]) {
+               char **prev_walker = (char**)w[2];
+
+               //bb_error_msg("free(walker@%p:%p) #3, restoring to %p", &v->x.walker, v->x.walker, prev_walker);
+               free(v->x.walker);
+               v->x.walker = prev_walker;
                return FALSE;
+       }
 
-       setvar_s(v, nextword(w+1));
+       setvar_s(v, nextword(&w[1]));
        return TRUE;
 }
 
index 03d4649699386de24bd8900bae96dcfbbaefe855..78f9f0b986677c2dff672f10a4143e17c69bd5ca 100755 (executable)
@@ -67,4 +67,42 @@ testing "awk string cast (bug 725)" \
 testing "awk handles whitespace before array subscript" \
        "awk 'BEGIN { arr [3] = 1; print arr [3] }'" "1\n" "" ""
 
+prg='
+BEGIN {
+  v["q"]=1
+  v["w"]=1
+  v["e"]=1
+  for (l in v) {
+    print "outer1", l;
+    for (l in v) {
+      print " inner", l;
+    }
+    print "outer2", l;
+  }
+  print "end", l;
+  l="a"
+  exit;
+}'
+testing "awk nested loops with the same variable" \
+       "awk '$prg'" \
+       "\
+outer1 e
+ inner e
+ inner q
+ inner w
+outer2 w
+outer1 q
+ inner e
+ inner q
+ inner w
+outer2 w
+outer1 w
+ inner e
+ inner q
+ inner w
+outer2 w
+end w
+" \
+       "" ""
+
 exit $FAILCOUNT