summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog81
-rw-r--r--awk.h19
-rw-r--r--awkgram.c4
-rw-r--r--awkgram.y4
-rw-r--r--builtin.c72
-rw-r--r--interpret.h15
-rw-r--r--io.c60
-rw-r--r--mpfr.c6
-rw-r--r--node.c20
-rw-r--r--str_array.c6
-rw-r--r--test/ChangeLog5
-rw-r--r--test/Makefile.am5
-rw-r--r--test/Makefile.in10
-rw-r--r--test/Maketests5
-rw-r--r--test/strftfld.awk3
-rw-r--r--test/strftfld.in1
-rw-r--r--test/strftfld.ok1
17 files changed, 248 insertions, 69 deletions
diff --git a/ChangeLog b/ChangeLog
index e655ebc5..6d5300b2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -114,6 +114,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
@@ -128,6 +157,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.
diff --git a/awk.h b/awk.h
index a0af5793..b56649af 100644
--- a/awk.h
+++ b/awk.h
@@ -1683,7 +1683,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 */
@@ -1963,6 +1963,23 @@ 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
+
#ifdef SIGPIPE
#define ignore_sigpipe() signal(SIGPIPE, SIG_IGN)
#define set_sigpipe_to_default() signal(SIGPIPE, SIG_DFL)
diff --git a/awkgram.c b/awkgram.c
index 006f32c9..3ad04500 100644
--- a/awkgram.c
+++ b/awkgram.c
@@ -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);
diff --git a/awkgram.y b/awkgram.y
index 5b81d8ac..46970eee 100644
--- a/awkgram.y
+++ b/awkgram.y
@@ -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);
diff --git a/builtin.c b/builtin.c
index e2143b36..a0db2e4d 100644
--- a/builtin.c
+++ b/builtin.c
@@ -1917,6 +1917,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);
@@ -1990,6 +1991,7 @@ do_strftime(int nargs)
DEREF(t1);
return make_string("", 0);
}
+ str_terminate(t1, save);
}
if (do_gmt)
@@ -1997,8 +1999,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);
@@ -2024,8 +2028,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;
}
@@ -3592,7 +3599,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;
@@ -3617,7 +3624,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.
@@ -3667,7 +3674,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)
@@ -3785,8 +3792,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();
@@ -3798,9 +3805,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
@@ -3818,16 +3823,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);
}
@@ -3839,11 +3848,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 */
@@ -3885,17 +3895,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 */
@@ -3920,29 +3942,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 */
diff --git a/io.c b/io.c
index f854ec5a..84e608ae 100644
--- a/io.c
+++ b/io.c
@@ -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 */
@@ -1784,7 +1784,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;
@@ -1793,13 +1793,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;
@@ -1845,6 +1847,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 */
@@ -1867,7 +1870,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;
}
@@ -1889,7 +1892,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");
@@ -4000,21 +4003,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;
@@ -4035,9 +4041,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;
@@ -4045,37 +4053,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
diff --git a/mpfr.c b/mpfr.c
index c0f1ff0c..ec8d5561 100644
--- a/mpfr.c
+++ b/mpfr.c
@@ -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;
}
diff --git a/node.c b/node.c
index d7ed98ea..962a650d 100644
--- a/node.c
+++ b/node.c
@@ -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 df0ed8fa..6aea1662 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -21,6 +21,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 a356d63b..e29d5d9a 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 8719840b..84b8a8f7 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 \
@@ -4257,6 +4260,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