awk: fix segfault on closing non-opened file
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 23 Oct 2010 19:02:15 +0000 (21:02 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 23 Oct 2010 19:02:15 +0000 (21:02 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
editors/awk.c
testsuite/awk.tests

index 2245cad03161cff4f6eb40a8141e0280e280b829..fb3bf6b47582e4477b53adc29a2568f83e899ef6 100644 (file)
@@ -283,88 +283,80 @@ enum {
 #define        OC_B    OC_BUILTIN
 
 static const char tokenlist[] ALIGN1 =
-       "\1("       NTC
-       "\1)"       NTC
-       "\1/"       NTC                                 /* REGEXP */
-       "\2>>"      "\1>"       "\1|"       NTC         /* OUTRDR */
-       "\2++"      "\2--"      NTC                     /* UOPPOST */
-       "\2++"      "\2--"      "\1$"       NTC         /* UOPPRE1 */
-       "\2=="      "\1="       "\2+="      "\2-="      /* BINOPX */
-       "\2*="      "\2/="      "\2%="      "\2^="
-       "\1+"       "\1-"       "\3**="     "\2**"
-       "\1/"       "\1%"       "\1^"       "\1*"
-       "\2!="      "\2>="      "\2<="      "\1>"
-       "\1<"       "\2!~"      "\1~"       "\2&&"
-       "\2||"      "\1?"       "\1:"       NTC
-       "\2in"      NTC
-       "\1,"       NTC
-       "\1|"       NTC
-       "\1+"       "\1-"       "\1!"       NTC         /* UOPPRE2 */
-       "\1]"       NTC
-       "\1{"       NTC
-       "\1}"       NTC
-       "\1;"       NTC
-       "\1\n"      NTC
-       "\2if"      "\2do"      "\3for"     "\5break"   /* STATX */
-       "\10continue"           "\6delete"  "\5print"
-       "\6printf"  "\4next"    "\10nextfile"
-       "\6return"  "\4exit"    NTC
-       "\5while"   NTC
-       "\4else"    NTC
-
-       "\3and"     "\5compl"   "\6lshift"  "\2or"
-       "\6rshift"  "\3xor"
-       "\5close"   "\6system"  "\6fflush"  "\5atan2"   /* BUILTIN */
-       "\3cos"     "\3exp"     "\3int"     "\3log"
-       "\4rand"    "\3sin"     "\4sqrt"    "\5srand"
-       "\6gensub"  "\4gsub"    "\5index"   "\6length"
-       "\5match"   "\5split"   "\7sprintf" "\3sub"
-       "\6substr"  "\7systime" "\10strftime" "\6mktime"
-       "\7tolower" "\7toupper" NTC
-       "\7getline" NTC
-       "\4func"    "\10function"   NTC
-       "\5BEGIN"   NTC
-       "\3END"     "\0"
+       "\1("         NTC
+       "\1)"         NTC
+       "\1/"         NTC                                   /* REGEXP */
+       "\2>>"        "\1>"         "\1|"       NTC         /* OUTRDR */
+       "\2++"        "\2--"        NTC                     /* UOPPOST */
+       "\2++"        "\2--"        "\1$"       NTC         /* UOPPRE1 */
+       "\2=="        "\1="         "\2+="      "\2-="      /* BINOPX */
+       "\2*="        "\2/="        "\2%="      "\2^="
+       "\1+"         "\1-"         "\3**="     "\2**"
+       "\1/"         "\1%"         "\1^"       "\1*"
+       "\2!="        "\2>="        "\2<="      "\1>"
+       "\1<"         "\2!~"        "\1~"       "\2&&"
+       "\2||"        "\1?"         "\1:"       NTC
+       "\2in"        NTC
+       "\1,"         NTC
+       "\1|"         NTC
+       "\1+"         "\1-"         "\1!"       NTC         /* UOPPRE2 */
+       "\1]"         NTC
+       "\1{"         NTC
+       "\1}"         NTC
+       "\1;"         NTC
+       "\1\n"        NTC
+       "\2if"        "\2do"        "\3for"     "\5break"   /* STATX */
+       "\10continue" "\6delete"    "\5print"
+       "\6printf"    "\4next"      "\10nextfile"
+       "\6return"    "\4exit"      NTC
+       "\5while"     NTC
+       "\4else"      NTC
+
+       "\3and"       "\5compl"     "\6lshift"  "\2or"
+       "\6rshift"    "\3xor"
+       "\5close"     "\6system"    "\6fflush"  "\5atan2"   /* BUILTIN */
+       "\3cos"       "\3exp"       "\3int"     "\3log"
+       "\4rand"      "\3sin"       "\4sqrt"    "\5srand"
+       "\6gensub"    "\4gsub"      "\5index"   "\6length"
+       "\5match"     "\5split"     "\7sprintf" "\3sub"
+       "\6substr"    "\7systime"   "\10strftime" "\6mktime"
+       "\7tolower"   "\7toupper"   NTC
+       "\7getline"   NTC
+       "\4func"      "\10function" NTC
+       "\5BEGIN"     NTC
+       "\3END"
+       /* compiler adds trailing "\0" */
        ;
 
 static const uint32_t tokeninfo[] = {
        0,
        0,
        OC_REGEXP,
-       xS|'a',     xS|'w',     xS|'|',
-       OC_UNARY|xV|P(9)|'p',       OC_UNARY|xV|P(9)|'m',
-       OC_UNARY|xV|P(9)|'P',       OC_UNARY|xV|P(9)|'M',
-           OC_FIELD|xV|P(5),
-       OC_COMPARE|VV|P(39)|5,      OC_MOVE|VV|P(74),
-           OC_REPLACE|NV|P(74)|'+',    OC_REPLACE|NV|P(74)|'-',
-       OC_REPLACE|NV|P(74)|'*',    OC_REPLACE|NV|P(74)|'/',
-           OC_REPLACE|NV|P(74)|'%',    OC_REPLACE|NV|P(74)|'&',
-       OC_BINARY|NV|P(29)|'+',     OC_BINARY|NV|P(29)|'-',
-           OC_REPLACE|NV|P(74)|'&',    OC_BINARY|NV|P(15)|'&',
-       OC_BINARY|NV|P(25)|'/',     OC_BINARY|NV|P(25)|'%',
-           OC_BINARY|NV|P(15)|'&',     OC_BINARY|NV|P(25)|'*',
-       OC_COMPARE|VV|P(39)|4,      OC_COMPARE|VV|P(39)|3,
-           OC_COMPARE|VV|P(39)|0,      OC_COMPARE|VV|P(39)|1,
-       OC_COMPARE|VV|P(39)|2,      OC_MATCH|Sx|P(45)|'!',
-           OC_MATCH|Sx|P(45)|'~',      OC_LAND|Vx|P(55),
-       OC_LOR|Vx|P(59),            OC_TERNARY|Vx|P(64)|'?',
-           OC_COLON|xx|P(67)|':',
-       OC_IN|SV|P(49),
+       xS|'a',                  xS|'w',                  xS|'|',
+       OC_UNARY|xV|P(9)|'p',    OC_UNARY|xV|P(9)|'m',
+       OC_UNARY|xV|P(9)|'P',    OC_UNARY|xV|P(9)|'M',    OC_FIELD|xV|P(5),
+       OC_COMPARE|VV|P(39)|5,   OC_MOVE|VV|P(74),        OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-',
+       OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&',
+       OC_BINARY|NV|P(29)|'+',  OC_BINARY|NV|P(29)|'-',  OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&',
+       OC_BINARY|NV|P(25)|'/',  OC_BINARY|NV|P(25)|'%',  OC_BINARY|NV|P(15)|'&',  OC_BINARY|NV|P(25)|'*',
+       OC_COMPARE|VV|P(39)|4,   OC_COMPARE|VV|P(39)|3,   OC_COMPARE|VV|P(39)|0,   OC_COMPARE|VV|P(39)|1,
+       OC_COMPARE|VV|P(39)|2,   OC_MATCH|Sx|P(45)|'!',   OC_MATCH|Sx|P(45)|'~',   OC_LAND|Vx|P(55),
+       OC_LOR|Vx|P(59),         OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':',
+       OC_IN|SV|P(49), /* in */
        OC_COMMA|SS|P(80),
        OC_PGETLINE|SV|P(37),
-       OC_UNARY|xV|P(19)|'+',      OC_UNARY|xV|P(19)|'-',
-           OC_UNARY|xV|P(19)|'!',
+       OC_UNARY|xV|P(19)|'+',   OC_UNARY|xV|P(19)|'-',   OC_UNARY|xV|P(19)|'!',
+       0, /* ] */
        0,
        0,
        0,
-       0,
-       0,
-       ST_IF,          ST_DO,          ST_FOR,         OC_BREAK,
-       OC_CONTINUE,                    OC_DELETE|Vx,   OC_PRINT,
-       OC_PRINTF,      OC_NEXT,        OC_NEXTFILE,
-       OC_RETURN|Vx,   OC_EXIT|Nx,
+       0, /* \n */
+       ST_IF,        ST_DO,        ST_FOR,      OC_BREAK,
+       OC_CONTINUE,  OC_DELETE|Vx, OC_PRINT,
+       OC_PRINTF,    OC_NEXT,      OC_NEXTFILE,
+       OC_RETURN|Vx, OC_EXIT|Nx,
        ST_WHILE,
-       0,
+       0, /* else */
 
        OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83),
        OC_B|B_rs|P(0x83), OC_B|B_xo|P(0x83),
@@ -376,9 +368,9 @@ static const uint32_t tokeninfo[] = {
        OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti,    OC_B|B_ti|P(0x0b), OC_B|B_mt|P(0x0b),
        OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49),
        OC_GETLINE|SV|P(0),
-       0,      0,
+       0,                 0,
        0,
-       0
+       0 /* END */
 };
 
 /* internal variable names and their initial values       */
@@ -1836,6 +1828,8 @@ static int awk_getline(rstream *rsm, var *v)
        int fd, so, eo, r, rp;
        char c, *m, *s;
 
+       debug_printf_eval("entered %s()\n", __func__);
+
        /* we're using our own buffer since we need access to accumulating
         * characters
         */
@@ -1922,6 +1916,8 @@ static int awk_getline(rstream *rsm, var *v)
        rsm->pos = p - eo;
        rsm->size = size;
 
+       debug_printf_eval("returning from %s(): %d\n", __func__, r);
+
        return r;
 }
 
@@ -2347,6 +2343,8 @@ static var *evaluate(node *op, var *res)
        if (!op)
                return setvar_s(res, NULL);
 
+       debug_printf_eval("entered %s()\n", __func__);
+
        v1 = nvalloc(2);
 
        while (op) {
@@ -2367,7 +2365,7 @@ static var *evaluate(node *op, var *res)
                opn = (opinfo & OPNMASK);
                g_lineno = op->lineno;
                op1 = op->l.n;
-               debug_printf_eval("opinfo:%08x opn:%08x XC:%x\n", opinfo, opn, XC(opinfo & OPCLSMASK));
+               debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn);
 
                /* execute inevitable things */
                if (opinfo & OF_RES1)
@@ -2387,6 +2385,7 @@ static var *evaluate(node *op, var *res)
                        debug_printf_eval("L_d:%f\n", L_d);
                }
 
+               debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK));
                switch (XC(opinfo & OPCLSMASK)) {
 
                /* -- iterative node type -- */
@@ -2642,8 +2641,6 @@ static var *evaluate(node *op, var *res)
 
                /* simple builtins */
                case XC( OC_FBLTIN ): {
-                       int i;
-                       rstream *rsm;
                        double R_d = R_d; /* for compiler */
 
                        switch (opn) {
@@ -2709,26 +2706,37 @@ static var *evaluate(node *op, var *res)
                                if (!op1) {
                                        fflush(stdout);
                                } else if (L.s && *L.s) {
-                                       rsm = newfile(L.s);
+                                       rstream *rsm = newfile(L.s);
                                        fflush(rsm->F);
                                } else {
                                        fflush_all();
                                }
                                break;
 
-                       case F_cl:
-                               i = 0;
+                       case F_cl: {
+                               rstream *rsm;
+                               int err = 0;
                                rsm = (rstream *)hash_search(fdhash, L.s);
+                               debug_printf_eval("OC_FBLTIN F_cl rsm:%p\n", rsm);
                                if (rsm) {
-                                       i = rsm->is_pipe ? pclose(rsm->F) : fclose(rsm->F);
+                                       debug_printf_eval("OC_FBLTIN F_cl "
+                                               "rsm->is_pipe:%d, ->F:%p\n",
+                                               rsm->is_pipe, rsm->F);
+                                       /* Can be NULL if open failed. Example:
+                                        * getline line <"doesnt_exist";
+                                        * close("doesnt_exist"); <--- here rsm->F is NULL
+                                        */
+                                       if (rsm->F)
+                                               err = rsm->is_pipe ? pclose(rsm->F) : fclose(rsm->F);
                                        free(rsm->buffer);
                                        hash_remove(fdhash, L.s);
                                }
-                               if (i != 0)
+                               if (err)
                                        setvar_i(intvar[ERRNO], errno);
-                               R_d = (double)i;
+                               R_d = (double)err;
                                break;
                        }
+                       } /* switch */
                        setvar_i(res, R_d);
                        break;
                }
@@ -2877,6 +2885,7 @@ static var *evaluate(node *op, var *res)
        } /* while (op) */
 
        nvfree(v1);
+       debug_printf_eval("returning from %s(): %p\n", __func__, res);
        return res;
 #undef fnargs
 #undef seed
@@ -2919,18 +2928,19 @@ static int is_assignment(const char *expr)
 {
        char *exprc, *s, *s0, *s1;
 
-       exprc = xstrdup(expr);
-       if (!isalnum_(*exprc) || (s = strchr(exprc, '=')) == NULL) {
-               free(exprc);
+       if (!isalnum_(*expr) || (s0 = strchr(expr, '=')) == NULL) {
                return FALSE;
        }
 
+       exprc = xstrdup(expr);
+       s0 = exprc + (s0 - expr);
        *s++ = '\0';
-       s0 = s1 = s;
+
+       s = s1 = s0;
        while (*s)
                *s1++ = nextchar(&s);
-
        *s1 = '\0';
+
        setvar_u(newvar(exprc), s0);
        free(exprc);
        return TRUE;
index 56b11ca46d3b63e0727a20557ce80d7c68d568ee..0afe9b9e73e7ef295fffbd500d56fdc5066aa89a 100755 (executable)
@@ -77,6 +77,12 @@ testing "awk string cast (bug 725)" \
 testing "awk handles whitespace before array subscript" \
        "awk 'BEGIN { arr [3] = 1; print arr [3] }'" "1\n" "" ""
 
+# GNU awk 3.1.5's "print ERRNO" prints "No such file or directory" instead of "2",
+# do we need to emulate that as well?
+testing "awk handles non-existing file correctly" \
+       "awk 'BEGIN { getline line <\"doesnt_exist\"; print ERRNO; ERRNO=0; close(\"doesnt_exist\"); print ERRNO; print \"Ok\" }'" \
+       "2\n0\nOk\n" "" ""
+
 prg='
 BEGIN {
   u["a"]=1