#include "libbb.h"
#include "xregex.h"
#include <math.h>
-extern char **environ;
/* This is a NOEXEC applet. Be very careful! */
#define OC_B OC_BUILTIN
-static const char tokenlist[] =
+static const char tokenlist[] ALIGN1 =
"\1(" NTC
"\1)" NTC
"\1/" NTC /* REGEXP */
enum {
CONVFMT, OFMT, FS, OFS,
ORS, RS, RT, FILENAME,
- SUBSEP, ARGIND, ARGC, ARGV,
- ERRNO, FNR,
- NR, NF, IGNORECASE,
- ENVIRON, F0, NUM_INTERNAL_VARS
+ SUBSEP, F0, ARGIND, ARGC,
+ ARGV, ERRNO, FNR, NR,
+ NF, IGNORECASE, ENVIRON, NUM_INTERNAL_VARS
};
-static const char vNames[] =
+static const char vNames[] ALIGN1 =
"CONVFMT\0" "OFMT\0" "FS\0*" "OFS\0"
"ORS\0" "RS\0*" "RT\0" "FILENAME\0"
- "SUBSEP\0" "ARGIND\0" "ARGC\0" "ARGV\0"
- "ERRNO\0" "FNR\0"
- "NR\0" "NF\0*" "IGNORECASE\0*"
- "ENVIRON\0" "$\0*" "\0";
+ "SUBSEP\0" "$\0*" "ARGIND\0" "ARGC\0"
+ "ARGV\0" "ERRNO\0" "FNR\0" "NR\0"
+ "NF\0*" "IGNORECASE\0*" "ENVIRON\0" "\0";
-static const char vValues[] =
+static const char vValues[] ALIGN1 =
"%.6g\0" "%.6g\0" " \0" " \0"
"\n\0" "\n\0" "\0" "\0"
- "\034\0"
- "\377";
+ "\034\0" "\0" "\377";
/* hash size may grow to these values */
-#define FIRST_PRIME 61;
-static const unsigned PRIMES[] = { 251, 1021, 4093, 16381, 65521 };
-
+#define FIRST_PRIME 61
+static const uint16_t PRIMES[] ALIGN2 = { 251, 1021, 4093, 16381, 65521 };
/* Globals. Split in two parts so that first one is addressed
- * with (mostly short) negative offsets */
+ * with (mostly short) negative offsets.
+ * NB: it's unsafe to put members of type "double"
+ * into globals2 (gcc may fail to align them).
+ */
struct globals {
- chain beginseq, mainseq, endseq, *seq;
+ double t_double;
+ chain beginseq, mainseq, endseq;
+ chain *seq;
node *break_ptr, *continue_ptr;
rstream *iF;
xhash *vhash, *ahash, *fdhash, *fnhash;
tsplitter exec_builtin__tspl;
/* biggest and least used members go last */
- double t_double;
tsplitter fsplitter, rsplitter;
};
#define G1 (ptr_to_globals[-1])
-#define G (*(struct globals2 *const)ptr_to_globals)
+#define G (*(struct globals2 *)ptr_to_globals)
/* For debug. nm --size-sort awk.o | grep -vi ' [tr] ' */
-/* char G1size[sizeof(G1)]; - 0x6c */
-/* char Gsize[sizeof(G)]; - 0x1cc */
+/*char G1size[sizeof(G1)]; - 0x74 */
+/*char Gsize[sizeof(G)]; - 0x1c4 */
/* Trying to keep most of members accessible with short offsets: */
-/* char Gofs_seed[offsetof(struct globals2, evaluate__seed)]; - 0x90 */
+/*char Gofs_seed[offsetof(struct globals2, evaluate__seed)]; - 0x90 */
+#define t_double (G1.t_double )
#define beginseq (G1.beginseq )
#define mainseq (G1.mainseq )
#define endseq (G1.endseq )
#define t_info (G.t_info )
#define t_tclass (G.t_tclass )
#define t_string (G.t_string )
-#define t_double (G.t_double )
#define t_lineno (G.t_lineno )
#define t_rollback (G.t_rollback )
#define intvar (G.intvar )
#define fsplitter (G.fsplitter )
#define rsplitter (G.rsplitter )
#define INIT_G() do { \
- PTR_TO_GLOBALS = xzalloc(sizeof(G1) + sizeof(G)) + sizeof(G1); \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G1) + sizeof(G)) + sizeof(G1)); \
G.next_token__ltclass = TC_OPTERM; \
G.evaluate__seed = 1; \
} while (0)
static var *evaluate(node *, var *);
static rstream *next_input_file(void);
static int fmt_num(char *, int, const char *, double, int);
-static int awk_exit(int) ATTRIBUTE_NORETURN;
+static int awk_exit(int) NORETURN;
/* ---- error handling ---- */
-static const char EMSG_INTERNAL_ERROR[] = "Internal error";
-static const char EMSG_UNEXP_EOS[] = "Unexpected end of string";
-static const char EMSG_UNEXP_TOKEN[] = "Unexpected token";
-static const char EMSG_DIV_BY_ZERO[] = "Division by zero";
-static const char EMSG_INV_FMT[] = "Invalid format specifier";
-static const char EMSG_TOO_FEW_ARGS[] = "Too few arguments for builtin";
-static const char EMSG_NOT_ARRAY[] = "Not an array";
-static const char EMSG_POSSIBLE_ERROR[] = "Possible syntax error";
-static const char EMSG_UNDEF_FUNC[] = "Call to undefined function";
-#if !ENABLE_FEATURE_AWK_MATH
-static const char EMSG_NO_MATH[] = "Math support is not compiled in";
+static const char EMSG_INTERNAL_ERROR[] ALIGN1 = "Internal error";
+static const char EMSG_UNEXP_EOS[] ALIGN1 = "Unexpected end of string";
+static const char EMSG_UNEXP_TOKEN[] ALIGN1 = "Unexpected token";
+static const char EMSG_DIV_BY_ZERO[] ALIGN1 = "Division by zero";
+static const char EMSG_INV_FMT[] ALIGN1 = "Invalid format specifier";
+static const char EMSG_TOO_FEW_ARGS[] ALIGN1 = "Too few arguments for builtin";
+static const char EMSG_NOT_ARRAY[] ALIGN1 = "Not an array";
+static const char EMSG_POSSIBLE_ERROR[] ALIGN1 = "Possible syntax error";
+static const char EMSG_UNDEF_FUNC[] ALIGN1 = "Call to undefined function";
+#if !ENABLE_FEATURE_AWK_LIBM
+static const char EMSG_NO_MATH[] ALIGN1 = "Math support is not compiled in";
#endif
static void zero_out_var(var * vp)
memset(vp, 0, sizeof(*vp));
}
-static void syntax_error(const char * const message) ATTRIBUTE_NORETURN;
-static void syntax_error(const char * const message)
+static void syntax_error(const char *message) NORETURN;
+static void syntax_error(const char *message)
{
bb_error_msg_and_die("%s:%i: %s", g_progname, g_lineno, message);
}
hash_rebuild(hash);
l = strlen(name) + 1;
- hi = xzalloc(sizeof(hash_item) + l);
- memcpy(hi->name, name, l);
+ hi = xzalloc(sizeof(*hi) + l);
+ strcpy(hi->name, name);
idx = hashidx(name) % hash->csize;
hi->next = hash->items[idx];
return c;
}
-static int ALWAYS_INLINE isalnum_(int c)
+static ALWAYS_INLINE int isalnum_(int c)
{
return (isalnum(c) || c == '_');
}
-static FILE *afopen(const char *path, const char *mode)
+static double my_strtod(char **pp)
{
- return (*path == '-' && *(path+1) == '\0') ? stdin : xfopen(path, mode);
+#if ENABLE_DESKTOP
+ if ((*pp)[0] == '0'
+ && ((((*pp)[1] | 0x20) == 'x') || isdigit((*pp)[1]))
+ ) {
+ return strtoull(*pp, pp, 0);
+ }
+#endif
+ return strtod(*pp, pp);
}
/* -------- working with variables (set/get/copy/etc) -------- */
v->number = 0;
s = v->string;
if (s && *s) {
- v->number = strtod(s, &s);
+ v->number = my_strtod(&s);
if (v->type & VF_USER) {
skip_spaces(&s);
if (*s != '\0')
return v->number;
}
+/* Used for operands of bitwise ops */
+static unsigned long getvar_i_int(var *v)
+{
+ double d = getvar_i(v);
+
+ /* Casting doubles to longs is undefined for values outside
+ * of target type range. Try to widen it as much as possible */
+ if (d >= 0)
+ return (unsigned long)d;
+ /* Why? Think about d == -4294967295.0 (assuming 32bit longs) */
+ return - (long) (unsigned long) (-d);
+}
+
static var *copyvar(var *dest, const var *src)
{
if (dest != src) {
static var *incvar(var *v)
{
- return setvar_i(v, getvar_i(v)+1.);
+ return setvar_i(v, getvar_i(v) + 1.);
}
/* return true if v is number or numeric string */
if (!g_cb) {
size = (n <= MINNVBLOCK) ? MINNVBLOCK : n;
- g_cb = xmalloc(sizeof(nvblock) + size * sizeof(var));
+ g_cb = xzalloc(sizeof(nvblock) + size * sizeof(var));
g_cb->size = size;
g_cb->pos = g_cb->nv;
g_cb->prev = pb;
- g_cb->next = NULL;
+ /*g_cb->next = NULL; - xzalloc did it */
if (pb) pb->next = g_cb;
}
} else if (*p == '.' || isdigit(*p)) {
/* it's a number */
- t_double = strtod(p, &p);
+ t_double = my_strtod(&p);
if (*p == '.')
syntax_error(EMSG_UNEXP_TOKEN);
tc = TC_NUMBER;
*/
static regex_t *as_regex(node *op, regex_t *preg)
{
+ int cflags;
var *v;
const char *s;
}
v = nvalloc(1);
s = getvar_s(evaluate(op, v));
- xregcomp(preg, s, icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED);
+
+ cflags = icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED;
+ /* Testcase where REG_EXTENDED fails (unpaired '{'):
+ * echo Hi | awk 'gsub("@(samp|code|file)\{","");'
+ * gawk 3.1.5 eats this. We revert to ~REG_EXTENDED
+ * (maybe gsub is not supposed to use REG_EXTENDED?).
+ */
+ if (regcomp(preg, s, cflags)) {
+ cflags &= ~REG_EXTENDED;
+ xregcomp(preg, s, cflags);
+ }
nvfree(v);
return preg;
}
/* gradually increasing buffer */
static void qrealloc(char **b, int n, int *size)
{
- if (!*b || n >= *size)
- *b = xrealloc(*b, *size = n + (n>>1) + 80);
+ if (!*b || n >= *size) {
+ *size = n + (n>>1) + 80;
+ *b = xrealloc(*b, *size);
+ }
}
/* resize field storage space */
if (*getvar_s(intvar[RS]) == '\0')
c[2] = '\n';
- if ((spl->info & OPCLSMASK) == OC_REGEXP) { /* regex split */
- while (*s) {
- l = strcspn(s, c+2);
+ if ((spl->info & OPCLSMASK) == OC_REGEXP) { /* regex split */
+ if (!*s)
+ return n; /* "": zero fields */
+ n++; /* at least one field will be there */
+ do {
+ l = strcspn(s, c+2); /* len till next NUL or \n */
if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0
&& pmatch[0].rm_so <= l
) {
l++;
pmatch[0].rm_eo++;
}
+ n++; /* we saw yet another delimiter */
} else {
pmatch[0].rm_eo = l;
- if (s[l]) pmatch[0].rm_eo++;
+ if (s[l])
+ pmatch[0].rm_eo++;
}
-
memcpy(s1, s, l);
- s1[l] = '\0';
+ /* make sure we remove *all* of the separator chars */
+ do {
+ s1[l] = '\0';
+ } while (++l < pmatch[0].rm_eo);
nextword(&s1);
s += pmatch[0].rm_eo;
- n++;
- }
- } else if (c[0] == '\0') { /* null split */
+ } while (*s);
+ return n;
+ }
+ if (c[0] == '\0') { /* null split */
while (*s) {
*s1++ = *s++;
*s1++ = '\0';
n++;
}
- } else if (c[0] != ' ') { /* single-character split */
+ return n;
+ }
+ if (c[0] != ' ') { /* single-character split */
if (icase) {
c[0] = toupper(c[0]);
c[1] = tolower(c[1]);
*s1++ = '\0';
n++;
}
- } else { /* space split */
- while (*s) {
- s = skip_whitespace(s);
- if (!*s) break;
- n++;
- while (*s && !isspace(*s))
- *s1++ = *s++;
- *s1++ = '\0';
- }
+ return n;
+ }
+ /* space split */
+ while (*s) {
+ s = skip_whitespace(s);
+ if (!*s) break;
+ n++;
+ while (*s && !isspace(*s))
+ *s1++ = *s++;
+ *s1++ = '\0';
}
return n;
}
static void split_f0(void)
{
+/* static char *fstrings; */
#define fstrings (G.split_f0__fstrings)
int i, n;
{
char **w;
hash_item *hi;
- int i;
+ unsigned i;
if (v->type & VF_WALK)
free(v->x.walker);
return r;
}
-
/* formatted output into an allocated buffer, return ptr to buffer */
static char *awk_printf(node *n)
{
}
nargs = i;
- if (nargs < (info >> 30))
+ if ((uint32_t)nargs < (info >> 30))
syntax_error(EMSG_TOO_FEW_ARGS);
switch (info & OPNMASK) {
case B_a2:
-#if ENABLE_FEATURE_AWK_MATH
- setvar_i(res, atan2(getvar_i(av[i]), getvar_i(av[1])));
+#if ENABLE_FEATURE_AWK_LIBM
+ setvar_i(res, atan2(getvar_i(av[0]), getvar_i(av[1])));
#else
syntax_error(EMSG_NO_MATH);
#endif
n = awk_split(as[0], spl, &s);
s1 = s;
clear_array(iamarray(av[1]));
- for (i=1; i<=n; i++)
+ for (i = 1; i <= n; i++)
setari_u(av[1], i, nextword(&s1));
free(s);
setvar_i(res, n);
if (i < 0) i = 0;
n = (nargs > 2) ? getvar_i(av[2]) : l-i;
if (n < 0) n = 0;
- s = xmalloc(n+1);
- strncpy(s, as[0]+i, n);
- s[n] = '\0';
+ s = xstrndup(as[0]+i, n);
setvar_p(res, s);
break;
+ /* Bitwise ops must assume that operands are unsigned. GNU Awk 3.1.5:
+ * awk '{ print or(-1,1) }' gives "4.29497e+09", not "-2.xxxe+09" */
case B_an:
- setvar_i(res, (long)getvar_i(av[0]) & (long)getvar_i(av[1]));
+ setvar_i(res, getvar_i_int(av[0]) & getvar_i_int(av[1]));
break;
case B_co:
- setvar_i(res, ~(long)getvar_i(av[0]));
+ setvar_i(res, ~getvar_i_int(av[0]));
break;
case B_ls:
- setvar_i(res, (long)getvar_i(av[0]) << (long)getvar_i(av[1]));
+ setvar_i(res, getvar_i_int(av[0]) << getvar_i_int(av[1]));
break;
case B_or:
- setvar_i(res, (long)getvar_i(av[0]) | (long)getvar_i(av[1]));
+ setvar_i(res, getvar_i_int(av[0]) | getvar_i_int(av[1]));
break;
case B_rs:
- setvar_i(res, (long)((unsigned long)getvar_i(av[0]) >> (unsigned long)getvar_i(av[1])));
+ setvar_i(res, getvar_i_int(av[0]) >> getvar_i_int(av[1]));
break;
case B_xo:
- setvar_i(res, (long)getvar_i(av[0]) ^ (long)getvar_i(av[1]));
+ setvar_i(res, getvar_i_int(av[0]) ^ getvar_i_int(av[1]));
break;
case B_lo:
X.rsm->F = popen(L.s, "r");
X.rsm->is_pipe = TRUE;
} else {
- X.rsm->F = fopen(L.s, "r"); /* not xfopen! */
+ X.rsm->F = fopen_for_read(L.s); /* not xfopen! */
}
}
} else {
case F_rn:
R.d = (double)rand() / (double)RAND_MAX;
break;
-#if ENABLE_FEATURE_AWK_MATH
+#if ENABLE_FEATURE_AWK_LIBM
case F_co:
R.d = cos(L.d);
break;
L.d /= R.d;
break;
case '&':
-#if ENABLE_FEATURE_AWK_MATH
+#if ENABLE_FEATURE_AWK_LIBM
L.d = pow(L.d, R.d);
#else
syntax_error(EMSG_NO_MATH);
ind = getvar_s(incvar(intvar[ARGIND]));
fname = getvar_s(findvar(iamarray(intvar[ARGV]), ind));
if (fname && *fname && !is_assignment(fname))
- F = afopen(fname, "r");
+ F = xfopen_stdin(fname);
}
} while (!F);
#undef files_happen
}
-int awk_main(int argc, char **argv);
+int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int awk_main(int argc, char **argv)
{
unsigned opt;
char *opt_F, *opt_W;
- llist_t *opt_v = NULL;
- int i, j, flen;
+ llist_t *list_v = NULL;
+ llist_t *list_f = NULL;
+ int i, j;
var *v;
var tv;
char **envp;
*s1 = '=';
}
}
- opt_complementary = "v::";
- opt = getopt32(argc, argv, "F:v:f:W:", &opt_F, &opt_v, &g_progname, &opt_W);
+ opt_complementary = "v::f::"; /* -v and -f can occur multiple times */
+ opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, &opt_W);
argv += optind;
argc -= optind;
if (opt & 0x1)
setvar_s(intvar[FS], opt_F); // -F
- while (opt_v) { /* -v */
- if (!is_assignment(llist_pop(&opt_v)))
+ while (list_v) { /* -v */
+ if (!is_assignment(llist_pop(&list_v)))
bb_show_usage();
}
- if (opt & 0x4) { // -f
- char *s = s; /* die, gcc, die */
- FILE *from_file = afopen(g_progname, "r");
- /* one byte is reserved for some trick in next_token */
- if (fseek(from_file, 0, SEEK_END) == 0) {
- flen = ftell(from_file);
- s = xmalloc(flen + 4);
- fseek(from_file, 0, SEEK_SET);
- i = 1 + fread(s + 1, 1, flen, from_file);
- } else {
+ if (list_f) { /* -f */
+ do {
+ char *s = NULL;
+ FILE *from_file;
+
+ g_progname = llist_pop(&list_f);
+ from_file = xfopen_stdin(g_progname);
+ /* one byte is reserved for some trick in next_token */
for (i = j = 1; j > 0; i += j) {
s = xrealloc(s, i + 4096);
j = fread(s + i, 1, 4094, from_file);
}
- }
- s[i] = '\0';
- fclose(from_file);
- parse_program(s + 1);
- free(s);
+ s[i] = '\0';
+ fclose(from_file);
+ parse_program(s + 1);
+ free(s);
+ } while (list_f);
+ argc++;
} else { // no -f: take program from 1st parameter
if (!argc)
bb_show_usage();
g_progname = "cmd. line";
parse_program(*argv++);
- argc--;
}
if (opt & 0x8) // -W
bb_error_msg("warning: unrecognized option '-W %s' ignored", opt_W);
/* fill in ARGV array */
- setvar_i(intvar[ARGC], argc + 1);
+ setvar_i(intvar[ARGC], argc);
setari_u(intvar[ARGV], 0, "awk");
i = 0;
while (*argv)