diff options
-rw-r--r-- | ChangeLog | 81 | ||||
-rw-r--r-- | awk.h | 18 | ||||
-rw-r--r-- | awkgram.c | 4 | ||||
-rw-r--r-- | awkgram.y | 4 | ||||
-rw-r--r-- | builtin.c | 72 | ||||
-rw-r--r-- | interpret.h | 15 | ||||
-rw-r--r-- | io.c | 60 | ||||
-rw-r--r-- | mpfr.c | 6 | ||||
-rw-r--r-- | node.c | 20 | ||||
-rw-r--r-- | str_array.c | 6 | ||||
-rw-r--r-- | test/ChangeLog | 5 | ||||
-rw-r--r-- | test/Makefile.am | 5 | ||||
-rw-r--r-- | test/Makefile.in | 10 | ||||
-rw-r--r-- | test/Maketests | 5 | ||||
-rw-r--r-- | test/strftfld.awk | 3 | ||||
-rw-r--r-- | test/strftfld.in | 1 | ||||
-rw-r--r-- | test/strftfld.ok | 1 |
17 files changed, 247 insertions, 69 deletions
@@ -49,6 +49,35 @@ (set_profile_next): New function. All calls adjusted. Also improved use at MPFR number case. +2017-01-28 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * io.c (inetfile): Replace strncmp with memcmp in a few places, now + that we are checking string length beforehand. + +2017-01-27 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * io.c (redirect_string): Check explen positive before accessing *str. + In lintwarn message, use explen string length. Pass length to inetfile. + (devopen): Pass name length to inetfile. + Stop assuming that remoteport is NUL-terminated. + (two_way_open): Pass name length to inetfile. + (inetfile): Stop assuming NUL string termination; add checks to avoid + string overrun. + +2017-01-27 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * awk.h (str_terminate_f): New helper function for terminating a string + NODE. + (str_terminate): Macro wrapper to call str_terminate_f. + (str_restore): New macro to restore the string. + * builtin.c (do_strftime): Use str_terminate and str_restore. + (do_dcgettext): Ditto, and remove saved_end flag since equivalent + to testing (t2 != NULL). Fix overrun bug in calculating result + length when !ENABLE_NLS. + (do_dcngettext, do_bindtextdomain): Use str_terminate and str_restore. + * interpret.h (Op_arrayfor_init, Op_indirect_func_call): Ditto. + * str_array.c (env_remove): Ditto. + 2017-01-27 Andrew J. Schorr <aschorr@telemetry-investments.com> * interpret.h [UNFIELD]: Fix condition for assignment from @@ -63,6 +92,58 @@ 2017-01-26 Andrew J. Schorr <aschorr@telemetry-investments.com> + * builtin.c (do_dcgettext): First argument also needs protection + from string overrun. + (do_dcngettext): Need to terminate string1 and string2 also, + and replace strlen(the_result), which could overrun. + (do_bindtextdomain): Terminate both string args, and eliminate + saved_end boolean which is redundant with (t2 != NULL). + +2017-01-26 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * interpret.h (Op_arrayfor_init): Protect against string overrun + on sorting method. + (Op_indirect_func_call): Terminate function name. + +2017-01-26 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * str_array.c (env_remove): Terminate string before calling unsetenv. + +2017-01-26 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * node.c (is_hex): Add a new argument pointing to the end of the string + so we can check for string overrun. + (r_force_number): Pass string end to is_hex. + +2017-01-26 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * awk.h (get_numbase): Add string length argument so we can operate + on unterminated strings. + * awkgram.y: Call get_numbase with string length, and fix off-by-one + error in length passed to nondec2awknum: should be strlen(tokstart)-1 + based on surrounding code. + * builtin.c (do_strtonum): Pass string length to get_numbase. + (nondec2awknum): Check string length before accessing characters. + * mpfr.c (force_mpnum): Pass string length to get_numbase. + * node.c (r_force_number): Pass string length to get_numbase. + (get_numbase): Add string length argument and honor it. + +2017-01-26 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * builtin.c (do_strftime): If format argument is passed, we need + to terminate it in case it's a field variable. + +2017-01-26 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * node.c (r_format_val): Before we free s->stptr, make sure that it + was malloced. + (wstr2str): Add comment explaining why it's safe to free n->stptr + without doing any checks. + * mpfr.c (mpg_format_val): Ditto. And no need to reset the STRCUR flag + that we just checked. + +2017-01-26 Andrew J. Schorr <aschorr@telemetry-investments.com> + * awk.h (enum block_id): Remove BLOCK_INVALID, since it serves no useful purpose and seems to slow things down a bit. * node.c (nextfree): Remove first invalid entry. @@ -1679,7 +1679,7 @@ extern Regexp *re_update(NODE *t); extern void resyntax(int syntax); extern void resetup(void); extern int reisstring(const char *text, size_t len, Regexp *re, const char *buf); -extern int get_numbase(const char *str, bool use_locale); +extern int get_numbase(const char *str, size_t len, bool use_locale); extern bool using_utf8(void); /* symbol.c */ @@ -1958,3 +1958,19 @@ erealloc_real(void *ptr, size_t count, const char *where, const char *var, const return ret; } + +/* + * str_terminate_f, str_terminate, str_restore: function and macros to + * reduce chances of typos when terminating and restoring strings. + * This also helps to enforce that the NODE must be in scope when we restore. + */ + +static inline void +str_terminate_f(NODE *n, char *savep) +{ + *savep = n->stptr[n->stlen]; + n->stptr[n->stlen] = '\0'; +} + +#define str_terminate(n, save) str_terminate_f((n), &save) +#define str_restore(n, save) (n)->stptr[(n)->stlen] = save @@ -6414,7 +6414,7 @@ retry: base = 10; if (! do_traditional) { - base = get_numbase(tokstart, false); + base = get_numbase(tokstart, strlen(tokstart)-1, false); if (do_lint) { if (base == 8) lintwarn("numeric constant `%.*s' treated as octal", @@ -6445,7 +6445,7 @@ retry: } #endif if (base != 10) - d = nondec2awknum(tokstart, strlen(tokstart), NULL); + d = nondec2awknum(tokstart, strlen(tokstart)-1, NULL); else d = atof(tokstart); yylval->memory = set_profile_text(make_number(d), tokstart, strlen(tokstart) - 1); @@ -3994,7 +3994,7 @@ retry: base = 10; if (! do_traditional) { - base = get_numbase(tokstart, false); + base = get_numbase(tokstart, strlen(tokstart)-1, false); if (do_lint) { if (base == 8) lintwarn("numeric constant `%.*s' treated as octal", @@ -4025,7 +4025,7 @@ retry: } #endif if (base != 10) - d = nondec2awknum(tokstart, strlen(tokstart), NULL); + d = nondec2awknum(tokstart, strlen(tokstart)-1, NULL); else d = atof(tokstart); yylval->memory = set_profile_text(make_number(d), tokstart, strlen(tokstart) - 1); @@ -1907,6 +1907,7 @@ do_strftime(int nargs) bool do_gmt; NODE *val = NULL; NODE *sub = NULL; + char save; static const time_t time_t_min = TYPE_MINIMUM(time_t); static const time_t time_t_max = TYPE_MAXIMUM(time_t); @@ -1980,6 +1981,7 @@ do_strftime(int nargs) DEREF(t1); return make_string("", 0); } + str_terminate(t1, save); } if (do_gmt) @@ -1987,8 +1989,10 @@ do_strftime(int nargs) else tm = localtime(& fclock); - if (tm == NULL) - return make_string("", 0); + if (tm == NULL) { + ret = make_string("", 0); + goto done; + } bufp = buf; bufsize = sizeof(buf); @@ -2014,8 +2018,11 @@ do_strftime(int nargs) ret = make_string(bufp, buflen); if (bufp != buf) efree(bufp); - if (t1) +done: + if (t1) { + str_restore(t1, save); DEREF(t1); + } return ret; } @@ -3586,7 +3593,7 @@ do_strtonum(int nargs) tmp = fixtype(POP_SCALAR()); if ((tmp->flags & NUMBER) != 0) d = (AWKNUM) tmp->numbr; - else if (get_numbase(tmp->stptr, use_lc_numeric) != 10) + else if (get_numbase(tmp->stptr, tmp->stlen, use_lc_numeric) != 10) d = nondec2awknum(tmp->stptr, tmp->stlen, NULL); else d = (AWKNUM) force_number(tmp)->numbr; @@ -3611,7 +3618,7 @@ nondec2awknum(char *str, size_t len, char **endptr) short val; char *start = str; - if (*str == '0' && (str[1] == 'x' || str[1] == 'X')) { + if (len >= 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) { /* * User called strtonum("0x") or some such, * so just quit early. @@ -3661,7 +3668,7 @@ nondec2awknum(char *str, size_t len, char **endptr) } if (endptr) *endptr = str; - } else if (*str == '0') { + } else if (len >= 1 && *str == '0') { for (; len > 0; len--) { if (! isdigit((unsigned char) *str)) { if (endptr) @@ -3779,8 +3786,8 @@ do_dcgettext(int nargs) #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT int lc_cat; char *domain; - char save; - bool saved_end = false; + char save1, save2; + size_t reslen; if (nargs == 3) { /* third argument */ tmp = POP_STRING(); @@ -3792,9 +3799,7 @@ do_dcgettext(int nargs) if (nargs >= 2) { /* second argument */ t2 = POP_STRING(); domain = t2->stptr; - save = domain[t2->stlen]; - domain[t2->stlen] = '\0'; - saved_end = true; + str_terminate(t2, save2); } else domain = TEXTDOMAIN; #else @@ -3812,16 +3817,20 @@ do_dcgettext(int nargs) string = t1->stptr; #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT + str_terminate(t1, save1); the_result = dcgettext(domain, string, lc_cat); - if (saved_end) - domain[t2->stlen] = save; - if (t2 != NULL) + str_restore(t1, save1); + if (t2 != NULL) { + str_restore(t2, save2); DEREF(t2); + } + reslen = strlen(the_result); #else the_result = string; + reslen = t1->stlen; #endif DEREF(t1); - return make_string(the_result, strlen(the_result)); + return make_string(the_result, reslen); } @@ -3833,11 +3842,12 @@ do_dcngettext(int nargs) unsigned long number; AWKNUM d; char *the_result; + size_t reslen; #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT int lc_cat; char *domain; - char save; + char save, save1, save2; bool saved_end = false; if (nargs == 5) { /* fifth argument */ @@ -3879,17 +3889,29 @@ do_dcngettext(int nargs) #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT + str_terminate(t1, save1); + str_terminate(t2, save2); the_result = dcngettext(domain, string1, string2, number, lc_cat); + reslen = strlen(the_result); + str_restore(t1, save1); + str_restore(t2, save2); if (saved_end) domain[t3->stlen] = save; if (t3 != NULL) DEREF(t3); #else - the_result = (number == 1 ? string1 : string2); + if (number == 1) { + the_result = string1; + reslen = t1->stlen; + } + else { + the_result = string2; + reslen = t2->stlen; + } #endif DEREF(t1); DEREF(t2); - return make_string(the_result, strlen(the_result)); + return make_string(the_result, reslen); } /* do_bindtextdomain --- set the directory for a text domain */ @@ -3914,29 +3936,31 @@ do_bindtextdomain(int nargs) /* set defaults */ directory = NULL; domain = TEXTDOMAIN; - char save; - bool saved_end = false; + char save, save1; if (nargs == 2) { /* second argument */ t2 = POP_STRING(); domain = (const char *) t2->stptr; save = t2->stptr[t2->stlen]; t2->stptr[t2->stlen] = '\0'; - saved_end = true; } /* first argument */ t1 = POP_STRING(); - if (t1->stlen > 0) + if (t1->stlen > 0) { directory = (const char *) t1->stptr; + str_terminate(t1, save1); + } the_result = bindtextdomain(domain, directory); + if (directory) + str_restore(t1, save1); DEREF(t1); - if (saved_end) + if (t2 != NULL) { t2->stptr[t2->stlen] = save; - if (t2 != NULL) DEREF(t2); + } return make_string(the_result, strlen(the_result)); } diff --git a/interpret.h b/interpret.h index 8c9675bb..13394e22 100644 --- a/interpret.h +++ b/interpret.h @@ -886,6 +886,8 @@ mod: size_t num_elems = 0; static NODE *sorted_in = NULL; const char *how_to_sort = "@unsorted"; + char save; + bool saved_end = false; /* get the array */ array = POP_ARRAY(); @@ -908,11 +910,16 @@ mod: if (sort_str != NULL) { sort_str = force_string(sort_str); - if (sort_str->stlen > 0) + if (sort_str->stlen > 0) { how_to_sort = sort_str->stptr; + str_terminate(sort_str, save); + saved_end = true; + } } list = assoc_list(array, how_to_sort, SORTED_IN); + if (saved_end) + str_restore(sort_str, save); arrayfor: getnode(r); @@ -1049,6 +1056,7 @@ match_re: { NODE *f = NULL; int arg_count; + char save; arg_count = (pc + 1)->expr_count; t1 = PEEK(arg_count); /* indirect var */ @@ -1057,12 +1065,14 @@ match_re: fatal(_("indirect function call requires a simple scalar value")); t1 = force_string(t1); + str_terminate(t1, save); if (t1->stlen > 0) { /* retrieve function definition node */ f = pc->func_body; if (f != NULL && strcmp(f->vname, t1->stptr) == 0) { /* indirect var hasn't been reassigned */ + str_restore(t1, save); ni = setup_frame(pc); JUMPTO(ni); /* Op_func */ } @@ -1087,10 +1097,12 @@ match_re: r = call_split_func(t1->stptr, arg_count); else r = the_func(arg_count); + str_restore(t1, save); PUSH(r); break; } else if (f->type != Node_func) { + str_restore(t1, save); if (f->type == Node_ext_func) { /* code copied from below, keep in sync */ INSTRUCTION *bc; @@ -1115,6 +1127,7 @@ match_re: pc->func_name); } pc->func_body = f; /* save for next call */ + str_restore(t1, save); ni = setup_frame(pc); JUMPTO(ni); /* Op_func */ @@ -306,7 +306,7 @@ struct inet_socket_info { } localport, remotehost, remoteport; }; -static bool inetfile(const char *str, struct inet_socket_info *isn); +static bool inetfile(const char *str, size_t len, struct inet_socket_info *isn); static NODE *in_PROCINFO(const char *pidx1, const char *pidx2, NODE **full_idx); static long get_read_timeout(IOBUF *iop); @@ -786,21 +786,21 @@ redirect_string(const char *str, size_t explen, bool not_string, lintwarn(_("expression in `%s' redirection is a number"), what); - if (str == NULL || *str == '\0') + if (explen < 1 || str == NULL || *str == '\0') fatal(_("expression for `%s' redirection has null string value"), what); if (do_lint && (strncmp(str, "0", explen) == 0 || strncmp(str, "1", explen) == 0)) - lintwarn(_("filename `%s' for `%s' redirection may be result of logical expression"), - str, what); + lintwarn(_("filename `%.*s' for `%s' redirection may be result of logical expression"), + (int) explen, str, what); #ifdef HAVE_SOCKETS /* * Use /inet4 to force IPv4, /inet6 to force IPv6, and plain * /inet will be whatever we get back from the system. */ - if (inetfile(str, & isi)) { + if (inetfile(str, explen, & isi)) { tflag |= RED_SOCKET; if (isi.protocol == SOCK_STREAM) tflag |= RED_TCP; /* use shutdown when closing */ @@ -1740,7 +1740,7 @@ devopen(const char *name, const char *mode) if (do_traditional) { goto strictopen; - } else if (inetfile(name, & isi)) { + } else if (inetfile(name, strlen(name), & isi)) { #ifdef HAVE_SOCKETS #define DEFAULT_RETRIES 20 static unsigned long def_retries = DEFAULT_RETRIES; @@ -1749,13 +1749,15 @@ devopen(const char *name, const char *mode) static long msleep = 1000; bool hard_error = false; bool non_fatal = is_non_fatal_redirect(name, strlen(name)); + char save; cp = (char *) name; /* socketopen requires NUL-terminated strings */ cp[isi.localport.offset+isi.localport.len] = '\0'; cp[isi.remotehost.offset+isi.remotehost.len] = '\0'; - /* remoteport comes last, so already NUL-terminated */ + save = cp[isi.remoteport.offset+isi.remoteport.len]; + cp[isi.remoteport.offset+isi.remoteport.len] = '\0'; if (first_time) { char *cp, *end; @@ -1801,6 +1803,7 @@ devopen(const char *name, const char *mode) /* restore original name string */ cp[isi.localport.offset+isi.localport.len] = '/'; cp[isi.remotehost.offset+isi.remotehost.len] = '/'; + cp[isi.remoteport.offset+isi.remoteport.len] = save; #else /* ! HAVE_SOCKETS */ fatal(_("TCP/IP communications are not supported")); #endif /* HAVE_SOCKETS */ @@ -1823,7 +1826,7 @@ strictopen: not permitted. */ struct stat buf; - if (! inetfile(name, NULL) + if (! inetfile(name, strlen(name), NULL) && stat(name, & buf) == 0 && S_ISDIR(buf.st_mode)) errno = EISDIR; } @@ -1845,7 +1848,7 @@ two_way_open(const char *str, struct redirect *rp, int extfd) #ifdef HAVE_SOCKETS /* case 1: socket */ - if (extfd >= 0 || inetfile(str, NULL)) { + if (extfd >= 0 || inetfile(str, strlen(str), NULL)) { int fd, newfd; fd = (extfd >= 0) ? extfd : devopen(str, "rw"); @@ -3960,21 +3963,24 @@ free_rp(struct redirect *rp) /* inetfile --- return true for a /inet special file, set other values */ static bool -inetfile(const char *str, struct inet_socket_info *isi) +inetfile(const char *str, size_t len, struct inet_socket_info *isi) { #ifndef HAVE_SOCKETS return false; #else const char *cp = str; + const char *cpend = str + len; struct inet_socket_info buf; /* syntax: /inet/protocol/localport/hostname/remoteport */ - if (strncmp(cp, "/inet", 5) != 0) + if (len < 5 || memcmp(cp, "/inet", 5) != 0) /* quick exit */ return false; if (! isi) isi = & buf; cp += 5; + if (cpend - cp < 2) + return false; switch (*cp) { case '/': isi->family = AF_UNSPEC; @@ -3995,9 +4001,11 @@ inetfile(const char *str, struct inet_socket_info *isi) cp++; /* skip past '/' */ /* which protocol? */ - if (strncmp(cp, "tcp/", 4) == 0) + if (cpend - cp < 5) + return false; + if (memcmp(cp, "tcp/", 4) == 0) isi->protocol = SOCK_STREAM; - else if (strncmp(cp, "udp/", 4) == 0) + else if (memcmp(cp, "udp/", 4) == 0) isi->protocol = SOCK_DGRAM; else return false; @@ -4005,37 +4013,43 @@ inetfile(const char *str, struct inet_socket_info *isi) /* which localport? */ isi->localport.offset = cp-str; - while (*cp != '/' && *cp != '\0') - cp++; + while (*cp != '/') { + if (++cp >= cpend) + return false; + } /* * Require a port, let them explicitly put 0 if * they don't care. */ - if (*cp != '/' || ((isi->localport.len = (cp-str)-isi->localport.offset) == 0)) + if ((isi->localport.len = (cp-str)-isi->localport.offset) == 0) return false; /* which hostname? */ + if (cpend - cp < 2) + return false; cp++; isi->remotehost.offset = cp-str; - while (*cp != '/' && *cp != '\0') - cp++; - if (*cp != '/' || ((isi->remotehost.len = (cp-str)-isi->remotehost.offset) == 0)) + while (*cp != '/') { + if (++cp >= cpend) + return false; + } + if ((isi->remotehost.len = (cp-str)-isi->remotehost.offset) == 0) return false; /* which remoteport? */ + if (cpend - cp < 2) + return false; cp++; /* * The remote port ends the special file name. - * This means there already is a '\0' at the end of the string. - * Therefore no need to patch any string ending. * * Here too, require a port, let them explicitly put 0 if * they don't care. */ isi->remoteport.offset = cp-str; - while (*cp != '/' && *cp != '\0') + while (*cp != '/' && cp < cpend) cp++; - if (*cp != '\0' || ((isi->remoteport.len = (cp-str)-isi->remoteport.offset) == 0)) + if (cp != cpend || ((isi->remoteport.len = (cp-str)-isi->remoteport.offset) == 0)) return false; #ifndef HAVE_GETADDRINFO @@ -303,7 +303,7 @@ force_mpnum(NODE *n, int do_nondec, int use_locale) cp1 = cp; if (do_nondec) - base = get_numbase(cp1, use_locale); + base = get_numbase(cp1, cpend - cp1, use_locale); if (! mpg_maybe_float(cp1, use_locale)) { mpg_zero(n); @@ -381,12 +381,10 @@ mpg_format_val(const char *format, int index, NODE *s) } s->flags = oflags; s->stlen = r->stlen; - if ((s->flags & STRCUR) != 0) + if ((s->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR)) efree(s->stptr); s->stptr = r->stptr; freenode(r); /* Do not unref(r)! We want to keep s->stptr == r->stpr. */ - - s->flags |= STRCUR; free_wstr(s); return s; } @@ -41,12 +41,13 @@ int (*cmp_numbers)(const NODE *, const NODE *) = cmp_awknums; /* is_hex --- return true if a string looks like a hex value */ static bool -is_hex(const char *str) +is_hex(const char *str, const char *cpend) { + /* on entry, we know the string length is >= 1 */ if (*str == '-' || *str == '+') str++; - if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + if (str + 1 < cpend && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) return true; return false; @@ -113,7 +114,7 @@ r_force_number(NODE *n) if ( (! do_posix /* not POSIXLY paranoid and */ && (is_alpha((unsigned char) *cp) /* letter, or */ /* CANNOT do non-decimal and saw 0x */ - || (! do_non_decimal_data && is_hex(cp))))) { + || (! do_non_decimal_data && is_hex(cp, cpend))))) { goto badnum; } @@ -129,7 +130,7 @@ r_force_number(NODE *n) errno = 0; if (do_non_decimal_data /* main.c assures false if do_posix */ - && ! do_traditional && get_numbase(cp, true) != 10) { + && ! do_traditional && get_numbase(cp, cpend - cp, true) != 10) { /* nondec2awknum() saves and restores the byte after the string itself */ n->numbr = nondec2awknum(cp, cpend - cp, &ptr); } else { @@ -248,7 +249,7 @@ r_format_val(const char *format, int index, NODE *s) } s->flags = oflags; s->stlen = r->stlen; - if ((s->flags & STRCUR) != 0) + if ((s->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR)) efree(s->stptr); s->stptr = r->stptr; freenode(r); /* Do not unref(r)! We want to keep s->stptr == r->stpr. */ @@ -273,7 +274,7 @@ r_format_val(const char *format, int index, NODE *s) s->flags |= STRING; } } - if ((s->flags & STRCUR) != 0) + if ((s->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR)) efree(s->stptr); emalloc(s->stptr, char *, s->stlen + 1, "format_val"); memcpy(s->stptr, sp, s->stlen + 1); @@ -631,7 +632,7 @@ parse_escape(const char **string_ptr) /* get_numbase --- return the base to use for the number in 's' */ int -get_numbase(const char *s, bool use_locale) +get_numbase(const char *s, size_t len, bool use_locale) { int dec_point = '.'; const char *str = s; @@ -645,7 +646,7 @@ get_numbase(const char *s, bool use_locale) dec_point = loc.decimal_point[0]; /* XXX --- assumes one char */ #endif - if (str[0] != '0') + if (len < 2 || str[0] != '0') return 10; /* leading 0x or 0X */ @@ -658,7 +659,7 @@ get_numbase(const char *s, bool use_locale) * * These beasts can have trailing whitespace. Deal with that too. */ - for (; *str != '\0'; str++) { + for (; len > 0; len--, str++) { if (*str == 'e' || *str == 'E' || *str == dec_point) return 10; else if (! isdigit((unsigned char) *str)) @@ -844,6 +845,7 @@ wstr2str(NODE *n) } *cp = '\0'; + /* N.B. caller just created n with make_string, so this free is safe */ efree(n->stptr); n->stptr = newval; n->stlen = cp - newval; diff --git a/str_array.c b/str_array.c index d832380d..fe07ce4b 100644 --- a/str_array.c +++ b/str_array.c @@ -773,9 +773,13 @@ static NODE ** env_remove(NODE *symbol, NODE *subs) { NODE **val = str_remove(symbol, subs); + char save; - if (val != NULL) + if (val != NULL) { + str_terminate(subs, save); (void) unsetenv(subs->stptr); + str_restore(subs, save); + } return val; } diff --git a/test/ChangeLog b/test/ChangeLog index b0ca5a97..5f3c349c 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -14,6 +14,11 @@ * Makefile.am (gensub3): New test. * gensub3.awk, gensub3.in, gensub3.ok: New files. +2017-01-26 Andrew J. Schorr <aschorr@telemetry-investments.com> + + * Makefile.am (strftfld): New test. + * strftfld.awk, strftfld.in, strftfld.ok: New files. + 2017-01-15 Andrew J. Schorr <aschorr@telemetry-investments.com> * Makefile.am (concat5): New test. diff --git a/test/Makefile.am b/test/Makefile.am index 65336b6c..1f482122 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1029,6 +1029,9 @@ EXTRA_DIST = \ strftime.awk \ strftlng.awk \ strftlng.ok \ + strftfld.awk \ + strftfld.in \ + strftfld.ok \ strnum1.awk \ strnum1.ok \ strnum2.awk \ @@ -1237,7 +1240,7 @@ GAWK_EXT_TESTS = \ rebuf regnul1 regnul2 regx8bit reginttrad reint reint2 rsgetline rsglstdin rsstart1 \ rsstart2 rsstart3 rstest6 shadow shadowbuiltin \ sortfor sortfor2 sortu split_after_fpat \ - splitarg4 strftime \ + splitarg4 strftime strftfld \ strtonum strtonum1 switch2 symtab1 symtab2 symtab3 symtab4 symtab5 symtab6 \ symtab7 symtab8 symtab9 symtab10 \ typedregex1 typedregex2 typedregex3 \ diff --git a/test/Makefile.in b/test/Makefile.in index 1a61996f..7059b73d 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -1287,6 +1287,9 @@ EXTRA_DIST = \ strftime.awk \ strftlng.awk \ strftlng.ok \ + strftfld.awk \ + strftfld.in \ + strftfld.ok \ strnum1.awk \ strnum1.ok \ strnum2.awk \ @@ -1494,7 +1497,7 @@ GAWK_EXT_TESTS = \ rebuf regnul1 regnul2 regx8bit reginttrad reint reint2 rsgetline rsglstdin rsstart1 \ rsstart2 rsstart3 rstest6 shadow shadowbuiltin \ sortfor sortfor2 sortu split_after_fpat \ - splitarg4 strftime \ + splitarg4 strftime strftfld \ strtonum strtonum1 switch2 symtab1 symtab2 symtab3 symtab4 symtab5 symtab6 \ symtab7 symtab8 symtab9 symtab10 \ typedregex1 typedregex2 typedregex3 \ @@ -4263,6 +4266,11 @@ splitarg4: @AWKPATH="$(srcdir)" $(AWK) -f $@.awk < "$(srcdir)"/$@.in >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ @-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@ +strftfld: + @echo $@ + @AWKPATH="$(srcdir)" $(AWK) -f $@.awk < "$(srcdir)"/$@.in >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@ + strtonum: @echo $@ @AWKPATH="$(srcdir)" $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ diff --git a/test/Maketests b/test/Maketests index d9183c0a..95321a57 100644 --- a/test/Maketests +++ b/test/Maketests @@ -1452,6 +1452,11 @@ splitarg4: @AWKPATH="$(srcdir)" $(AWK) -f $@.awk < "$(srcdir)"/$@.in >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ @-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@ +strftfld: + @echo $@ + @AWKPATH="$(srcdir)" $(AWK) -f $@.awk < "$(srcdir)"/$@.in >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@ + strtonum: @echo $@ @AWKPATH="$(srcdir)" $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ diff --git a/test/strftfld.awk b/test/strftfld.awk new file mode 100644 index 00000000..26f75a5a --- /dev/null +++ b/test/strftfld.awk @@ -0,0 +1,3 @@ +{ + print split(strftime($1), f) +} diff --git a/test/strftfld.in b/test/strftfld.in new file mode 100644 index 00000000..c1175143 --- /dev/null +++ b/test/strftfld.in @@ -0,0 +1 @@ +%F %T diff --git a/test/strftfld.ok b/test/strftfld.ok new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/test/strftfld.ok @@ -0,0 +1 @@ +1 |