- use bb_error_msg
[oweals/busybox.git] / editors / awk.c
index 74f9d8e5445ee1952276d03327eb6d3c26288036..a18025ef0ab42373668ca4abab2680ff13f2fae8 100644 (file)
@@ -7,49 +7,39 @@
  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <strings.h>
-#include <time.h>
-#include <math.h>
-#include <ctype.h>
-#include <getopt.h>
-
-#include "xregex.h"
 #include "busybox.h"
+#include "xregex.h"
+#include <math.h>
 
 
-#define        MAXVARFMT       240
-#define        MINNVBLOCK      64
+#define        MAXVARFMT       240
+#define        MINNVBLOCK      64
 
 /* variable flags */
-#define        VF_NUMBER       0x0001  /* 1 = primary type is number */
-#define        VF_ARRAY        0x0002  /* 1 = it's an array */
+#define        VF_NUMBER       0x0001  /* 1 = primary type is number */
+#define        VF_ARRAY        0x0002  /* 1 = it's an array */
 
-#define        VF_CACHED       0x0100  /* 1 = num/str value has cached str/num eq */
-#define        VF_USER         0x0200  /* 1 = user input (may be numeric string) */
-#define        VF_SPECIAL      0x0400  /* 1 = requires extra handling when changed */
-#define        VF_WALK         0x0800  /* 1 = variable has alloc'd x.walker list */
-#define        VF_FSTR         0x1000  /* 1 = string points to fstring buffer */
-#define        VF_CHILD        0x2000  /* 1 = function arg; x.parent points to source */
-#define        VF_DIRTY        0x4000  /* 1 = variable was set explicitly */
+#define        VF_CACHED       0x0100  /* 1 = num/str value has cached str/num eq */
+#define        VF_USER         0x0200  /* 1 = user input (may be numeric string) */
+#define        VF_SPECIAL      0x0400  /* 1 = requires extra handling when changed */
+#define        VF_WALK         0x0800  /* 1 = variable has alloc'd x.walker list */
+#define        VF_FSTR         0x1000  /* 1 = var::string points to fstring buffer */
+#define        VF_CHILD        0x2000  /* 1 = function arg; x.parent points to source */
+#define        VF_DIRTY        0x4000  /* 1 = variable was set explicitly */
 
 /* these flags are static, don't change them when value is changed */
-#define        VF_DONTTOUCH (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY)
+#define        VF_DONTTOUCH    (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY)
 
 /* Variable */
 typedef struct var_s {
-       unsigned short type;            /* flags */
+       unsigned short type;            /* flags */
        double number;
        char *string;
        union {
-               int aidx;                               /* func arg idx (for compilation stage) */
-               struct xhash_s *array;  /* array ptr */
-               struct var_s *parent;   /* for func args, ptr to actual parameter */
-               char **walker;                  /* list of array elements (for..in) */
+               int aidx;               /* func arg idx (for compilation stage) */
+               struct xhash_s *array;  /* array ptr */
+               struct var_s *parent;   /* for func args, ptr to actual parameter */
+               char **walker;          /* list of array elements (for..in) */
        } x;
 } var;
 
@@ -57,7 +47,7 @@ typedef struct var_s {
 typedef struct chain_s {
        struct node_s *first;
        struct node_s *last;
-       char *programname;
+       const char *programname;
 } chain;
 
 /* Function */
@@ -87,10 +77,10 @@ typedef struct hash_item_s {
 } hash_item;
 
 typedef struct xhash_s {
-       unsigned int nel;                                       /* num of elements */
-       unsigned int csize;                                     /* current hash size */
-       unsigned int nprime;                            /* next hash size in PRIMES[] */
-       unsigned int glen;                                      /* summary length of item names */
+       unsigned nel;                                   /* num of elements */
+       unsigned csize;                                 /* current hash size */
+       unsigned nprime;                                /* next hash size in PRIMES[] */
+       unsigned glen;                                  /* summary length of item names */
        struct hash_item_s **items;
 } xhash;
 
@@ -259,7 +249,8 @@ enum {
 /* builtins */
 enum {
        B_a2=0, B_ix,   B_ma,   B_sp,   B_ss,   B_ti,   B_lo,   B_up,
-       B_ge,   B_gs,   B_su
+       B_ge,   B_gs,   B_su,
+       B_an,   B_co,   B_ls,   B_or,   B_rs,   B_xo,
 };
 
 /* tokens and their corresponding info values */
@@ -269,89 +260,92 @@ enum {
 
 #define        OC_B    OC_BUILTIN
 
-static char * const tokenlist =
-       "\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
-
-       "\5close"       "\6system"      "\6fflush"      "\5atan2"       /* BUILTIN */
-       "\3cos"         "\3exp"         "\3int"         "\3log"
-       "\4rand"        "\3sin"         "\4sqrt"        "\5srand"
-       "\6gensub"      "\4gsub"        "\5index"       "\6length"
-       "\5match"       "\5split"       "\7sprintf"     "\3sub"
-       "\6substr"      "\7systime"     "\10strftime"
-       "\7tolower"     "\7toupper"     NTC
-       "\7getline"     NTC
-       "\4func"        "\10function"   NTC
-       "\5BEGIN"       NTC
-       "\3END"         "\0"
+static const char tokenlist[] =
+       "\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"
+       "\7tolower" "\7toupper" NTC
+       "\7getline" NTC
+       "\4func"    "\10function"   NTC
+       "\5BEGIN"   NTC
+       "\3END"     "\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)|':',
+       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),
        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,
-       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_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,
 
+       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),
        OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83),
        OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg,
        OC_FBLTIN|F_rn,    OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr,
@@ -368,32 +362,32 @@ static const uint32_t tokeninfo[] = {
 /* internal variable names and their initial values       */
 /* asterisk marks SPECIAL vars; $ is just no-named Field0 */
 enum {
-       CONVFMT=0,      OFMT,           FS,                     OFS,
-       ORS,            RS,                     RT,                     FILENAME,
-       SUBSEP,         ARGIND,         ARGC,           ARGV,
-       ERRNO,          FNR,
-       NR,                     NF,                     IGNORECASE,
-       ENVIRON,        F0,                     _intvarcount_
+       CONVFMT=0,  OFMT,       FS,         OFS,
+       ORS,        RS,         RT,         FILENAME,
+       SUBSEP,     ARGIND,     ARGC,       ARGV,
+       ERRNO,      FNR,
+       NR,         NF,         IGNORECASE,
+       ENVIRON,    F0,         _intvarcount_
 };
 
-static char * vNames =
-       "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";
-
-static char * vValues =
-       "%.6g\0"        "%.6g\0"        " \0"           " \0"
-       "\n\0"          "\n\0"          "\0"            "\0"
+static const char vNames[] =
+       "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";
+
+static const char vValues[] =
+       "%.6g\0"    "%.6g\0"    " \0"       " \0"
+       "\n\0"      "\n\0"      "\0"        "\0"
        "\034\0"
        "\377";
 
 /* hash size may grow to these values */
 #define FIRST_PRIME 61;
-static const unsigned int PRIMES[] = { 251, 1021, 4093, 16381, 65521 };
-enum { NPRIMES = sizeof(PRIMES) / sizeof(unsigned int) };
+static const unsigned PRIMES[] = { 251, 1021, 4093, 16381, 65521 };
+enum { NPRIMES = sizeof(PRIMES) / sizeof(unsigned) };
 
 /* globals */
 
@@ -405,7 +399,7 @@ static int nextrec, nextfile;
 static node *break_ptr, *continue_ptr;
 static rstream *iF;
 static xhash *vhash, *ahash, *fdhash, *fnhash;
-static char *programname;
+static const char *programname;
 static short lineno;
 static int is_f0_split;
 static int nfields;
@@ -446,10 +440,15 @@ 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";
-#ifndef CONFIG_FEATURE_AWK_MATH
+#if !ENABLE_FEATURE_AWK_MATH
 static const char EMSG_NO_MATH[] = "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)
 {
@@ -461,11 +460,11 @@ static void syntax_error(const char * const message)
 
 /* ---- hash stuff ---- */
 
-static unsigned int hashidx(const char *name)
+static unsigned hashidx(const char *name)
 {
-       register unsigned int idx=0;
+       unsigned idx = 0;
 
-       while (*name)  idx = *name++ + (idx << 6) - idx;
+       while (*name) idx = *name++ + (idx << 6) - idx;
        return idx;
 }
 
@@ -474,9 +473,9 @@ static xhash *hash_init(void)
 {
        xhash *newhash;
 
-       newhash = (xhash *)xzalloc(sizeof(xhash));
+       newhash = xzalloc(sizeof(xhash));
        newhash->csize = FIRST_PRIME;
-       newhash->items = (hash_item **)xzalloc(newhash->csize * sizeof(hash_item *));
+       newhash->items = xzalloc(newhash->csize * sizeof(hash_item *));
 
        return newhash;
 }
@@ -498,14 +497,14 @@ static void *hash_search(xhash *hash, const char *name)
 /* grow hash if it becomes too big */
 static void hash_rebuild(xhash *hash)
 {
-       unsigned int newsize, i, idx;
+       unsigned newsize, i, idx;
        hash_item **newitems, *hi, *thi;
 
        if (hash->nprime == NPRIMES)
                return;
 
        newsize = PRIMES[hash->nprime++];
-       newitems = (hash_item **)xzalloc(newsize * sizeof(hash_item *));
+       newitems = xzalloc(newsize * sizeof(hash_item *));
 
        for (i=0; i<hash->csize; i++) {
                hi = hash->items[i];
@@ -527,7 +526,7 @@ static void hash_rebuild(xhash *hash)
 static void *hash_find(xhash *hash, const char *name)
 {
        hash_item *hi;
-       unsigned int idx;
+       unsigned idx;
        int l;
 
        hi = hash_search(hash, name);
@@ -547,10 +546,10 @@ static void *hash_find(xhash *hash, const char *name)
        return &(hi->data);
 }
 
-#define findvar(hash, name) (var *) hash_find ( (hash) , (name) )
-#define newvar(name) (var *) hash_find ( vhash , (name) )
-#define newfile(name) (rstream *) hash_find ( fdhash , (name) )
-#define newfunc(name) (func *) hash_find ( fnhash , (name) )
+#define findvar(hash, name) ((var*)    hash_find((hash) , (name)))
+#define newvar(name)        ((var*)    hash_find(vhash , (name)))
+#define newfile(name)       ((rstream*)hash_find(fdhash ,(name)))
+#define newfunc(name)       ((func*)   hash_find(fnhash , (name)))
 
 static void hash_remove(xhash *hash, const char *name)
 {
@@ -574,9 +573,9 @@ static void hash_remove(xhash *hash, const char *name)
 
 static void skip_spaces(char **s)
 {
-       register char *p = *s;
+       char *p = *s;
 
-       while(*p == ' ' || *p == '\t' ||
+       while (*p == ' ' || *p == '\t' ||
                        (*p == '\\' && *(p+1) == '\n' && (++p, ++t.lineno))) {
                p++;
        }
@@ -585,16 +584,16 @@ static void skip_spaces(char **s)
 
 static char *nextword(char **s)
 {
-       register char *p = *s;
+       char *p = *s;
 
-       while (*(*s)++) ;
+       while (*(*s)++) /* */;
 
        return p;
 }
 
 static char nextchar(char **s)
 {
-       register char c, *pps;
+       char c, *pps;
 
        c = *((*s)++);
        pps = *s;
@@ -603,14 +602,14 @@ static char nextchar(char **s)
        return c;
 }
 
-static inline int isalnum_(int c)
+static int ATTRIBUTE_ALWAYS_INLINE isalnum_(int c)
 {
        return (isalnum(c) || c == '_');
 }
 
 static FILE *afopen(const char *path, const char *mode)
 {
-       return (*path == '-' && *(path+1) == '\0') ? stdin : bb_xfopen(path, mode);
+       return (*path == '-' && *(path+1) == '\0') ? stdin : xfopen(path, mode);
 }
 
 /* -------- working with variables (set/get/copy/etc) -------- */
@@ -631,7 +630,7 @@ static xhash *iamarray(var *v)
 
 static void clear_array(xhash *array)
 {
-       unsigned int i;
+       unsigned i;
        hash_item *hi, *thi;
 
        for (i=0; i<array->csize; i++) {
@@ -672,7 +671,7 @@ static var *setvar_p(var *v, char *value)
 /* same as setvar_p but make a copy of string */
 static var *setvar_s(var *v, const char *value)
 {
-       return setvar_p(v, (value && *value) ? bb_xstrdup(value) : NULL);
+       return setvar_p(v, (value && *value) ? xstrdup(value) : NULL);
 }
 
 /* same as setvar_s but set USER flag */
@@ -686,7 +685,7 @@ static var *setvar_u(var *v, const char *value)
 /* set array element to user string */
 static void setari_u(var *a, int idx, const char *s)
 {
-       register var *v;
+       var *v;
        static char sidx[12];
 
        sprintf(sidx, "%d", idx);
@@ -704,12 +703,12 @@ static var *setvar_i(var *v, double value)
        return v;
 }
 
-static char *getvar_s(var *v)
+static const char *getvar_s(var *v)
 {
        /* if v is numeric and has no cached string, convert it to string */
        if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) {
                fmt_num(buf, MAXVARFMT, getvar_s(V[CONVFMT]), v->number, TRUE);
-               v->string = bb_xstrdup(buf);
+               v->string = xstrdup(buf);
                v->type |= VF_CACHED;
        }
        return (v->string == NULL) ? "" : v->string;
@@ -741,10 +740,10 @@ static var *copyvar(var *dest, const var *src)
 {
        if (dest != src) {
                clrvar(dest);
-               dest->type |= (src->type & ~VF_DONTTOUCH);
+               dest->type |= (src->type & ~(VF_DONTTOUCH | VF_FSTR));
                dest->number = src->number;
                if (src->string)
-                       dest->string = bb_xstrdup(src->string);
+                       dest->string = xstrdup(src->string);
        }
        handle_special(dest);
        return dest;
@@ -786,7 +785,7 @@ static var *nvalloc(int n)
 
        if (! cb) {
                size = (n <= MINNVBLOCK) ? MINNVBLOCK : n;
-               cb = (nvblock *)xmalloc(sizeof(nvblock) + size * sizeof(var));
+               cb = xmalloc(sizeof(nvblock) + size * sizeof(var));
                cb->size = size;
                cb->pos = cb->nv;
                cb->prev = pb;
@@ -838,30 +837,27 @@ static void nvfree(var *v)
  */
 static uint32_t next_token(uint32_t expected)
 {
+       static int concat_inserted;
+       static uint32_t save_tclass, save_info;
+       static uint32_t ltclass = TC_OPTERM;
+
        char *p, *pp, *s;
-       char *tl;
+       const char *tl;
        uint32_t tc;
        const uint32_t *ti;
        int l;
-       static int concat_inserted;
-       static uint32_t save_tclass, save_info;
-       static uint32_t ltclass = TC_OPTERM;
 
        if (t.rollback) {
-
                t.rollback = FALSE;
 
        } else if (concat_inserted) {
-
                concat_inserted = FALSE;
                t.tclass = save_tclass;
                t.info = save_info;
 
        } else {
-
                p = pos;
-
-       readnext:
+ readnext:
                skip_spaces(&p);
                lineno = t.lineno;
                if (*p == '#')
@@ -935,21 +931,21 @@ static uint32_t next_token(uint32_t expected)
                                tl += l;
                        }
 
-                       if (! *tl) {
+                       if (!*tl) {
                                /* it's a name (var/array/function),
                                 * otherwise it's something wrong
                                 */
-                               if (! isalnum_(*p))
+                               if (!isalnum_(*p))
                                        syntax_error(EMSG_UNEXP_TOKEN);
 
                                t.string = --p;
-                               while(isalnum_(*(++p))) {
+                               while (isalnum_(*(++p))) {
                                        *(p-1) = *p;
                                }
                                *(p-1) = '\0';
                                tc = TC_VARIABLE;
                                /* also consume whitespace between functionname and bracket */
-                               if (! (expected & TC_VARIABLE)) skip_spaces(&p);
+                               if (!(expected & TC_VARIABLE)) skip_spaces(&p);
                                if (*p == '(') {
                                        tc = TC_FUNCTION;
                                } else {
@@ -991,15 +987,15 @@ static void rollback_token(void) { t.rollback = TRUE; }
 
 static node *new_node(uint32_t info)
 {
-       register node *n;
+       node *n;
 
-       n = (node *)xzalloc(sizeof(node));
+       n = xzalloc(sizeof(node));
        n->info = info;
        n->lineno = lineno;
        return n;
 }
 
-static node *mk_re_node(char *s, node *n, regex_t *re)
+static node *mk_re_node(const char *s, node *n, regex_t *re)
 {
        n->info = OC_REGEXP;
        n->l.re = re;
@@ -1077,8 +1073,8 @@ static node *parse_expr(uint32_t iexp)
                                /* one should be very careful with switch on tclass -
                                 * only simple tclasses should be used! */
                                switch (tc) {
-                                 case TC_VARIABLE:
-                                 case TC_ARRAY:
+                               case TC_VARIABLE:
+                               case TC_ARRAY:
                                        cn->info = OC_VAR;
                                        if ((v = hash_search(ahash, t.string)) != NULL) {
                                                cn->info = OC_FNARG;
@@ -1092,8 +1088,8 @@ static node *parse_expr(uint32_t iexp)
                                        }
                                        break;
 
-                                 case TC_NUMBER:
-                                 case TC_STRING:
+                               case TC_NUMBER:
+                               case TC_STRING:
                                        cn->info = OC_VAR;
                                        v = cn->l.v = xzalloc(sizeof(var));
                                        if (tc & TC_NUMBER)
@@ -1102,28 +1098,27 @@ static node *parse_expr(uint32_t iexp)
                                                setvar_s(v, t.string);
                                        break;
 
-                                 case TC_REGEXP:
-                                       mk_re_node(t.string, cn,
-                                                                       (regex_t *)xzalloc(sizeof(regex_t)*2));
+                               case TC_REGEXP:
+                                       mk_re_node(t.string, cn, xzalloc(sizeof(regex_t)*2));
                                        break;
 
-                                 case TC_FUNCTION:
+                               case TC_FUNCTION:
                                        cn->info = OC_FUNC;
                                        cn->r.f = newfunc(t.string);
                                        cn->l.n = condition();
                                        break;
 
-                                 case TC_SEQSTART:
+                               case TC_SEQSTART:
                                        cn = vn->r.n = parse_expr(TC_SEQTERM);
                                        cn->a.n = vn;
                                        break;
 
-                                 case TC_GETLINE:
+                               case TC_GETLINE:
                                        glptr = cn;
                                        xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
                                        break;
 
-                                 case TC_BUILTIN:
+                               case TC_BUILTIN:
                                        cn->l.n = condition();
                                        break;
                                }
@@ -1136,7 +1131,7 @@ static node *parse_expr(uint32_t iexp)
 /* add node to chain. Return ptr to alloc'd node */
 static node *chain_node(uint32_t info)
 {
-       register node *n;
+       node *n;
 
        if (! seq->first)
                seq->first = seq->last = new_node(0);
@@ -1144,7 +1139,7 @@ static node *chain_node(uint32_t info)
        if (seq->programname != programname) {
                seq->programname = programname;
                n = chain_node(OC_NEWSOURCE);
-               n->l.s = bb_xstrdup(programname);
+               n->l.s = xstrdup(programname);
        }
 
        n = seq->last;
@@ -1198,7 +1193,7 @@ static void chain_group(void)
        } while (c & TC_NEWLINE);
 
        if (c & TC_GRPSTART) {
-               while(next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) {
+               while (next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) {
                        if (t.tclass & TC_NEWLINE) continue;
                        rollback_token();
                        chain_group();
@@ -1239,7 +1234,7 @@ static void chain_group(void)
                        case ST_FOR:
                                next_token(TC_SEQSTART);
                                n2 = parse_expr(TC_SEMICOL | TC_SEQTERM);
-                               if (t.tclass & TC_SEQTERM) {                            /* for-in */
+                               if (t.tclass & TC_SEQTERM) {    /* for-in */
                                        if ((n2->info & OPCLSMASK) != OC_IN)
                                                syntax_error(EMSG_UNEXP_TOKEN);
                                        n = chain_node(OC_WALKINIT | VV);
@@ -1248,7 +1243,7 @@ static void chain_group(void)
                                        n = chain_loop(NULL);
                                        n->info = OC_WALKNEXT | Vx;
                                        n->l.n = n2->l.n;
-                               } else {                                                                        /* for(;;) */
+                               } else {                        /* for (;;) */
                                        n = chain_node(OC_EXEC | Vx);
                                        n->l.n = n2;
                                        n2 = parse_expr(TC_SEMICOL);
@@ -1285,7 +1280,6 @@ static void chain_group(void)
                        /* delete, next, nextfile, return, exit */
                        default:
                                chain_expr(t.info);
-
                }
        }
 }
@@ -1299,7 +1293,7 @@ static void parse_program(char *p)
 
        pos = p;
        t.lineno = 1;
-       while((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
+       while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
                                TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) {
 
                if (tclass & TC_OPTERM)
@@ -1320,7 +1314,7 @@ static void parse_program(char *p)
                        f = newfunc(t.string);
                        f->body.first = NULL;
                        f->nargs = 0;
-                       while(next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
+                       while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
                                v = findvar(ahash, t.string);
                                v->x.aidx = (f->nargs)++;
 
@@ -1353,15 +1347,15 @@ static void parse_program(char *p)
 
 /* -------- program execution part -------- */
 
-static node *mk_splitter(char *s, tsplitter *spl)
+static node *mk_splitter(const char *s, tsplitter *spl)
 {
-       register regex_t *re, *ire;
+       regex_t *re, *ire;
        node *n;
 
        re = &spl->re[0];
        ire = &spl->re[1];
        n = &spl->n;
-       if ((n->info && OPCLSMASK) == OC_REGEXP) {
+       if ((n->info & OPCLSMASK) == OC_REGEXP) {
                regfree(re);
                regfree(ire);
        }
@@ -1381,7 +1375,7 @@ static node *mk_splitter(char *s, tsplitter *spl)
 static regex_t *as_regex(node *op, regex_t *preg)
 {
        var *v;
-       char *s;
+       const char *s;
 
        if ((op->info & OPCLSMASK) == OC_REGEXP) {
                return icase ? op->r.ire : op->l.re;
@@ -1397,43 +1391,44 @@ static regex_t *as_regex(node *op, regex_t *preg)
 /* gradually increasing buffer */
 static void qrealloc(char **b, int n, int *size)
 {
-       if (! *b || n >= *size)
+       if (!*b || n >= *size)
                *b = xrealloc(*b, *size = n + (n>>1) + 80);
 }
 
 /* resize field storage space */
 static void fsrealloc(int size)
 {
-       static int maxfields = 0;
+       static int maxfields; /* = 0;*/
        int i;
 
        if (size >= maxfields) {
                i = maxfields;
                maxfields = size + 16;
-               Fields = (var *)xrealloc(Fields, maxfields * sizeof(var));
-               for (; i<maxfields; i++) {
+               Fields = xrealloc(Fields, maxfields * sizeof(var));
+               for (; i < maxfields; i++) {
                        Fields[i].type = VF_SPECIAL;
                        Fields[i].string = NULL;
                }
        }
 
        if (size < nfields) {
-               for (i=size; i<nfields; i++) {
-                       clrvar(Fields+i);
+               for (i = size; i < nfields; i++) {
+                       clrvar(Fields + i);
                }
        }
        nfields = size;
 }
 
-static int awk_split(char *s, node *spl, char **slist)
+static int awk_split(const char *s, node *spl, char **slist)
 {
-       int l, n=0;
+       int l, n = 0;
        char c[4];
        char *s1;
        regmatch_t pmatch[2];
 
        /* in worst case, each char would be a separate field */
-       *slist = s1 = bb_xstrndup(s, strlen(s) * 2 + 3);
+       *slist = s1 = xzalloc(strlen(s) * 2 + 3);
+       strcpy(s1, s);
 
        c[0] = c[1] = (char)spl->info;
        c[2] = c[3] = '\0';
@@ -1442,25 +1437,26 @@ static int awk_split(char *s, node *spl, char **slist)
        if ((spl->info & OPCLSMASK) == OC_REGEXP) {             /* regex split */
                while (*s) {
                        l = strcspn(s, c+2);
-                       if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0 &&
-                       pmatch[0].rm_so <= l) {
+                       if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0
+                        && pmatch[0].rm_so <= l
+                       ) {
                                l = pmatch[0].rm_so;
                                if (pmatch[0].rm_eo == 0) { l++; pmatch[0].rm_eo++; }
                        } 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';
+                       s1[l] = '\0';
                        nextword(&s1);
                        s += pmatch[0].rm_eo;
                        n++;
                }
        } else if (c[0] == '\0') {              /* null split */
-               while(*s) {
-                       *(s1++) = *(s++);
-                       *(s1++) = '\0';
+               while (*s) {
+                       *s1++ = *s++;
+                       *s1++ = '\0';
                        n++;
                }
        } else if (c[0] != ' ') {               /* single-character split */
@@ -1470,17 +1466,17 @@ static int awk_split(char *s, node *spl, char **slist)
                }
                if (*s1) n++;
                while ((s1 = strpbrk(s1, c))) {
-                       *(s1++) = '\0';
+                       *s1++ = '\0';
                        n++;
                }
        } else {                                /* space split */
                while (*s) {
-                       while (isspace(*s)) s++;
-                       if (! *s) break;
+                       s = skip_whitespace(s);
+                       if (!*s) break;
                        n++;
                        while (*s && !isspace(*s))
-                               *(s1++) = *(s++);
-                       *(s1++) = '\0';
+                               *s1++ = *s++;
+                       *s1++ = '\0';
                }
        }
        return n;
@@ -1501,7 +1497,7 @@ static void split_f0(void)
        n = awk_split(getvar_s(V[F0]), &fsplitter.n, &fstrings);
        fsrealloc(n);
        s = fstrings;
-       for (i=0; i<n; i++) {
+       for (i = 0; i < n; i++) {
                Fields[i].string = nextword(&s);
                Fields[i].type |= (VF_FSTR | VF_USER | VF_DIRTY);
        }
@@ -1516,10 +1512,11 @@ static void split_f0(void)
 static void handle_special(var *v)
 {
        int n;
-       char *b, *sep, *s;
+       char *b;
+       const char *sep, *s;
        int sl, l, len, i, bsize;
 
-       if (! (v->type & VF_SPECIAL))
+       if (!(v->type & VF_SPECIAL))
                return;
 
        if (v == V[NF]) {
@@ -1542,7 +1539,8 @@ static void handle_special(var *v)
                        memcpy(b+len, s, l);
                        len += l;
                }
-               if (b) b[len] = '\0';
+               if (b)
+                       b[len] = '\0';
                setvar_p(V[F0], b);
                is_f0_split = TRUE;
 
@@ -1558,7 +1556,7 @@ static void handle_special(var *v)
        } else if (v == V[IGNORECASE]) {
                icase = istrue(v);
 
-       } else {                                                /* $n */
+       } else {                                /* $n */
                n = getvar_i(V[NF]);
                setvar_i(V[NF], n > v-Fields ? n : v-Fields+1);
                /* right here v is invalid. Just to note... */
@@ -1590,11 +1588,11 @@ static void hashwalk_init(var *v, xhash *array)
                free(v->x.walker);
 
        v->type |= VF_WALK;
-       w = v->x.walker = (char **)xzalloc(2 + 2*sizeof(char *) + array->glen);
+       w = v->x.walker = xzalloc(2 + 2*sizeof(char *) + array->glen);
        *w = *(w+1) = (char *)(w + 2);
        for (i=0; i<array->csize; i++) {
                hi = array->items[i];
-               while(hi) {
+               while (hi) {
                        strcpy(*w, hi->name);
                        nextword(w);
                        hi = hi->next;
@@ -1617,7 +1615,8 @@ static int hashwalk_next(var *v)
 /* evaluate node, return 1 when result is true, 0 otherwise */
 static int ptest(node *pattern)
 {
-       static var v;
+       static var v; /* static: to save stack space? */
+
        return istrue(evaluate(pattern, &v));
 }
 
@@ -1717,14 +1716,14 @@ static int awk_getline(rstream *rsm, var *v)
 
 static int fmt_num(char *b, int size, const char *format, double n, int int_as_int)
 {
-       int r=0;
+       int r = 0;
        char c;
-       const char *s=format;
+       const char *s = format;
 
        if (int_as_int && n == (int)n) {
                r = snprintf(b, size, "%d", (int)n);
        } else {
-               do { c = *s; } while (*s && *++s);
+               do { c = *s; } while (c && *++s);
                if (strchr("diouxX", c)) {
                        r = snprintf(b, size, format, (int)n);
                } else if (strchr("eEfgG", c)) {
@@ -1741,13 +1740,14 @@ static int fmt_num(char *b, int size, const char *format, double n, int int_as_i
 static char *awk_printf(node *n)
 {
        char *b = NULL;
-       char *fmt, *s, *s1, *f;
+       char *fmt, *s, *f;
+       const char *s1;
        int i, j, incr, bsize;
        char c, c1;
        var *v, *arg;
 
        v = nvalloc(1);
-       fmt = f = bb_xstrdup(getvar_s(evaluate(nextarg(&n), v)));
+       fmt = f = xstrdup(getvar_s(evaluate(nextarg(&n), v)));
 
        i = 0;
        while (*f) {
@@ -1758,18 +1758,20 @@ static char *awk_printf(node *n)
                        f++;
 
                incr = (f - s) + MAXVARFMT;
-               qrealloc(&b, incr+i, &bsize);
-               c = *f; if (c != '\0') f++;
-               c1 = *f ; *f = '\0';
+               qrealloc(&b, incr + i, &bsize);
+               c = *f;
+               if (c != '\0') f++;
+               c1 = *f;
+               *f = '\0';
                arg = evaluate(nextarg(&n), v);
 
                j = i;
                if (c == 'c' || !c) {
-                       i += sprintf(b+i, s,
-                                       is_numeric(arg) ? (char)getvar_i(arg) : *getvar_s(arg));
+                       i += sprintf(b+i, s, is_numeric(arg) ?
+                                       (char)getvar_i(arg) : *getvar_s(arg));
 
                } else if (c == 's') {
-                   s1 = getvar_s(arg);
+                       s1 = getvar_s(arg);
                        qrealloc(&b, incr+i+strlen(s1), &bsize);
                        i += sprintf(b+i, s, s1);
 
@@ -1780,10 +1782,9 @@ static char *awk_printf(node *n)
 
                /* if there was an error while sprintf, return value is negative */
                if (i < j) i = j;
-
        }
 
-       b = xrealloc(b, i+1);
+       b = xrealloc(b, i + 1);
        free(fmt);
        nvfree(v);
        b[i] = '\0';
@@ -1796,10 +1797,11 @@ static char *awk_printf(node *n)
  * all matches. If src or dst is NULL, use $0. If ex=TRUE, enable
  * subexpression matching (\1-\9)
  */
-static int awk_sub(node *rn, char *repl, int nm, var *src, var *dest, int ex)
+static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int ex)
 {
        char *ds = NULL;
-       char *sp, *s;
+       const char *s;
+       const char *sp;
        int c, i, j, di, rl, so, eo, nbs, n, dssize;
        regmatch_t pmatch[10];
        regex_t sreg, *re;
@@ -1868,7 +1870,7 @@ static var *exec_builtin(node *op, var *res)
        var *tv;
        node *an[4];
        var  *av[4];
-       char *as[4];
+       const char *as[4];
        regmatch_t pmatch[2];
        regex_t sreg, *re;
        static tsplitter tspl;
@@ -1897,15 +1899,15 @@ static var *exec_builtin(node *op, var *res)
 
        switch (info & OPNMASK) {
 
-         case B_a2:
-#ifdef CONFIG_FEATURE_AWK_MATH
+       case B_a2:
+#if ENABLE_FEATURE_AWK_MATH
                setvar_i(res, atan2(getvar_i(av[i]), getvar_i(av[1])));
 #else
                runtime_error(EMSG_NO_MATH);
 #endif
                break;
 
-         case B_sp:
+       case B_sp:
                if (nargs > 2) {
                        spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ?
                                an[2] : mk_splitter(getvar_s(evaluate(an[2], &tv[2])), &tspl);
@@ -1922,7 +1924,7 @@ static var *exec_builtin(node *op, var *res)
                setvar_i(res, n);
                break;
 
-         case B_ss:
+       case B_ss:
                l = strlen(as[0]);
                i = getvar_i(av[1]) - 1;
                if (i>l) i=l; if (i<0) i=0;
@@ -1934,14 +1936,38 @@ static var *exec_builtin(node *op, var *res)
                setvar_p(res, s);
                break;
 
-         case B_lo:
+       case B_an:
+               setvar_i(res, (long)getvar_i(av[0]) & (long)getvar_i(av[1]));
+               break;
+
+       case B_co:
+               setvar_i(res, ~(long)getvar_i(av[0]));
+               break;
+
+       case B_ls:
+               setvar_i(res, (long)getvar_i(av[0]) << (long)getvar_i(av[1]));
+               break;
+
+       case B_or:
+               setvar_i(res, (long)getvar_i(av[0]) | (long)getvar_i(av[1]));
+               break;
+
+       case B_rs:
+               setvar_i(res, (long)((unsigned long)getvar_i(av[0]) >> (unsigned long)getvar_i(av[1])));
+               break;
+
+       case B_xo:
+               setvar_i(res, (long)getvar_i(av[0]) ^ (long)getvar_i(av[1]));
+               break;
+
+       case B_lo:
                to_xxx = tolower;
                goto lo_cont;
 
-         case B_up:
+       case B_up:
                to_xxx = toupper;
-lo_cont:
-               s1 = s = bb_xstrdup(as[0]);
+ lo_cont:
+               s1 = s = xstrdup(as[0]);
                while (*s1) {
                        *s1 = (*to_xxx)(*s1);
                        s1++;
@@ -1949,7 +1975,7 @@ lo_cont:
                setvar_p(res, s);
                break;
 
-         case B_ix:
+       case B_ix:
                n = 0;
                ll = strlen(as[1]);
                l = strlen(as[0]) - ll;
@@ -1972,18 +1998,20 @@ lo_cont:
                setvar_i(res, n);
                break;
 
-         case B_ti:
+       case B_ti:
                if (nargs > 1)
                        tt = getvar_i(av[1]);
                else
                        time(&tt);
-               s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y";
-               i = strftime(buf, MAXVARFMT, s, localtime(&tt));
+               //s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y";
+               i = strftime(buf, MAXVARFMT,
+                       ((nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"),
+                       localtime(&tt));
                buf[i] = '\0';
                setvar_s(res, buf);
                break;
 
-         case B_ma:
+       case B_ma:
                re = as_regex(an[1], &sreg);
                n = regexec(re, as[0], 1, pmatch, 0);
                if (n == 0) {
@@ -1999,15 +2027,15 @@ lo_cont:
                if (re == &sreg) regfree(re);
                break;
 
-         case B_ge:
+       case B_ge:
                awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE);
                break;
 
-         case B_gs:
+       case B_gs:
                setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE));
                break;
 
-         case B_su:
+       case B_su:
                setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE));
                break;
        }
@@ -2026,13 +2054,14 @@ static var *evaluate(node *op, var *res)
 {
        /* This procedure is recursive so we should count every byte */
        static var *fnargs = NULL;
-       static unsigned int seed = 1;
+       static unsigned seed = 1;
        static regex_t sreg;
+
        node *op1;
        var *v1;
        union {
                var *v;
-               char *s;
+               const char *s;
                double d;
                int i;
        } L, R;
@@ -2047,7 +2076,7 @@ static var *evaluate(node *op, var *res)
                uint32_t info;
        } X;
 
-       if (! op)
+       if (!op)
                return setvar_s(res, NULL);
 
        v1 = nvalloc(2);
@@ -2071,7 +2100,7 @@ static var *evaluate(node *op, var *res)
                  /* -- iterative node type -- */
 
                  /* test pattern */
-                 case XC( OC_TEST ):
+               case XC( OC_TEST ):
                        if ((op1->info & OPCLSMASK) == OC_COMMA) {
                                /* it's range pattern */
                                if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
@@ -2089,26 +2118,26 @@ static var *evaluate(node *op, var *res)
                        break;
 
                  /* just evaluate an expression, also used as unconditional jump */
-                 case XC( OC_EXEC ):
+               case XC( OC_EXEC ):
                        break;
 
                  /* branch, used in if-else and various loops */
-                 case XC( OC_BR ):
+               case XC( OC_BR ):
                        op = istrue(L.v) ? op->a.n : op->r.n;
                        break;
 
                  /* initialize for-in loop */
-                 case XC( OC_WALKINIT ):
+               case XC( OC_WALKINIT ):
                        hashwalk_init(L.v, iamarray(R.v));
                        break;
 
                  /* get next array item */
-                 case XC( OC_WALKNEXT ):
+               case XC( OC_WALKNEXT ):
                        op = hashwalk_next(L.v) ? op->a.n : op->r.n;
                        break;
 
-                 case XC( OC_PRINT ):
-                 case XC( OC_PRINTF ):
+               case XC( OC_PRINT ):
+               case XC( OC_PRINTF ):
                        X.F = stdout;
                        if (op->r.n) {
                                X.rsm = newfile(R.s);
@@ -2118,7 +2147,7 @@ static var *evaluate(node *op, var *res)
                                                        bb_perror_msg_and_die("popen");
                                                X.rsm->is_pipe = 1;
                                        } else {
-                                               X.rsm->F = bb_xfopen(R.s, opn=='w' ? "w" : "a");
+                                               X.rsm->F = xfopen(R.s, opn=='w' ? "w" : "a");
                                        }
                                }
                                X.F = X.rsm->F;
@@ -2132,7 +2161,7 @@ static var *evaluate(node *op, var *res)
                                                L.v = evaluate(nextarg(&op1), v1);
                                                if (L.v->type & VF_NUMBER) {
                                                        fmt_num(buf, MAXVARFMT, getvar_s(V[OFMT]),
-                                                                                                               getvar_i(L.v), TRUE);
+                                                                       getvar_i(L.v), TRUE);
                                                        fputs(buf, X.F);
                                                } else {
                                                        fputs(getvar_s(L.v), X.F);
@@ -2146,12 +2175,12 @@ static var *evaluate(node *op, var *res)
                        } else {        /* OC_PRINTF */
                                L.s = awk_printf(op1);
                                fputs(L.s, X.F);
-                               free(L.s);
+                               free((char*)L.s);
                        }
                        fflush(X.F);
                        break;
 
-                 case XC( OC_DELETE ):
+               case XC( OC_DELETE ):
                        X.info = op1->info & OPCLSMASK;
                        if (X.info == OC_VAR) {
                                R.v = op1->l.v;
@@ -2170,59 +2199,58 @@ static var *evaluate(node *op, var *res)
                        }
                        break;
 
-                 case XC( OC_NEWSOURCE ):
+               case XC( OC_NEWSOURCE ):
                        programname = op->l.s;
                        break;
 
-                 case XC( OC_RETURN ):
+               case XC( OC_RETURN ):
                        copyvar(res, L.v);
                        break;
 
-                 case XC( OC_NEXTFILE ):
+               case XC( OC_NEXTFILE ):
                        nextfile = TRUE;
-                 case XC( OC_NEXT ):
+               case XC( OC_NEXT ):
                        nextrec = TRUE;
-                 case XC( OC_DONE ):
+               case XC( OC_DONE ):
                        clrvar(res);
                        break;
 
-                 case XC( OC_EXIT ):
+               case XC( OC_EXIT ):
                        awk_exit(L.d);
 
                  /* -- recursive node type -- */
 
-                 case XC( OC_VAR ):
+               case XC( OC_VAR ):
                        L.v = op->l.v;
                        if (L.v == V[NF])
                                split_f0();
                        goto v_cont;
 
-                 case XC( OC_FNARG ):
+               case XC( OC_FNARG ):
                        L.v = &fnargs[op->l.i];
-
-v_cont:
-                       res = (op->r.n) ? findvar(iamarray(L.v), R.s) : L.v;
+ v_cont:
+                       res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v;
                        break;
 
-                 case XC( OC_IN ):
+               case XC( OC_IN ):
                        setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
                        break;
 
-                 case XC( OC_REGEXP ):
+               case XC( OC_REGEXP ):
                        op1 = op;
                        L.s = getvar_s(V[F0]);
                        goto re_cont;
 
-                 case XC( OC_MATCH ):
+               case XC( OC_MATCH ):
                        op1 = op->r.n;
-re_cont:
+ re_cont:
                        X.re = as_regex(op1, &sreg);
                        R.i = regexec(X.re, L.s, 0, NULL, 0);
                        if (X.re == &sreg) regfree(X.re);
                        setvar_i(res, (R.i == 0 ? 1 : 0) ^ (opn == '!' ? 1 : 0));
                        break;
 
-                 case XC( OC_MOVE ):
+               case XC( OC_MOVE ):
                        /* if source is a temporary string, jusk relink it to dest */
                        if (R.v == v1+1 && R.v->string) {
                                res = setvar_p(L.v, R.v->string);
@@ -2232,13 +2260,13 @@ re_cont:
                        }
                        break;
 
-                 case XC( OC_TERNARY ):
+               case XC( OC_TERNARY ):
                        if ((op->r.n->info & OPCLSMASK) != OC_COLON)
                                runtime_error(EMSG_POSSIBLE_ERROR);
                        res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
                        break;
 
-                 case XC( OC_FUNC ):
+               case XC( OC_FUNC ):
                        if (! op->r.f->body.first)
                                runtime_error(EMSG_UNDEF_FUNC);
 
@@ -2263,8 +2291,8 @@ re_cont:
                        fnargs = R.v;
                        break;
 
-                 case XC( OC_GETLINE ):
-                 case XC( OC_PGETLINE ):
+               case XC( OC_GETLINE ):
+               case XC( OC_PGETLINE ):
                        if (op1) {
                                X.rsm = newfile(L.s);
                                if (! X.rsm->F) {
@@ -2272,7 +2300,7 @@ re_cont:
                                                X.rsm->F = popen(L.s, "r");
                                                X.rsm->is_pipe = TRUE;
                                        } else {
-                                               X.rsm->F = fopen(L.s, "r");             /* not bb_xfopen! */
+                                               X.rsm->F = fopen(L.s, "r");             /* not xfopen! */
                                        }
                                }
                        } else {
@@ -2300,69 +2328,70 @@ re_cont:
                        break;
 
                  /* simple builtins */
-                 case XC( OC_FBLTIN ):
+               case XC( OC_FBLTIN ):
                        switch (opn) {
 
-                         case F_in:
+                       case F_in:
                                R.d = (int)L.d;
                                break;
 
-                         case F_rn:
-                               R.d =  (double)rand() / (double)RAND_MAX;
+                       case F_rn:
+                               R.d = (double)rand() / (double)RAND_MAX;
                                break;
 
-#ifdef CONFIG_FEATURE_AWK_MATH
-                         case F_co:
+#if ENABLE_FEATURE_AWK_MATH
+                       case F_co:
                                R.d = cos(L.d);
                                break;
 
-                         case F_ex:
+                       case F_ex:
                                R.d = exp(L.d);
                                break;
 
-                         case F_lg:
+                       case F_lg:
                                R.d = log(L.d);
                                break;
 
-                         case F_si:
+                       case F_si:
                                R.d = sin(L.d);
                                break;
 
-                         case F_sq:
+                       case F_sq:
                                R.d = sqrt(L.d);
                                break;
 #else
-                         case F_co:
-                         case F_ex:
-                         case F_lg:
-                         case F_si:
-                         case F_sq:
+                       case F_co:
+                       case F_ex:
+                       case F_lg:
+                       case F_si:
+                       case F_sq:
                                runtime_error(EMSG_NO_MATH);
                                break;
 #endif
 
-                         case F_sr:
+                       case F_sr:
                                R.d = (double)seed;
-                               seed = op1 ? (unsigned int)L.d : (unsigned int)time(NULL);
+                               seed = op1 ? (unsigned)L.d : (unsigned)time(NULL);
                                srand(seed);
                                break;
 
-                         case F_ti:
+                       case F_ti:
                                R.d = time(NULL);
                                break;
 
-                         case F_le:
+                       case F_le:
                                if (! op1)
                                        L.s = getvar_s(V[F0]);
                                R.d = strlen(L.s);
                                break;
 
-                         case F_sy:
+                       case F_sy:
                                fflush(NULL);
-                               R.d = (L.s && *L.s) ? (system(L.s) >> 8) : 0;
+                               R.d = (ENABLE_FEATURE_ALLOW_EXEC && L.s && *L.s)
+                                               ? (system(L.s) >> 8) : 0;
                                break;
 
-                         case F_ff:
+                       case F_ff:
                                if (! op1)
                                        fflush(stdout);
                                else {
@@ -2375,7 +2404,7 @@ re_cont:
                                }
                                break;
 
-                         case F_cl:
+                       case F_cl:
                                X.rsm = (rstream *)hash_search(fdhash, L.s);
                                if (X.rsm) {
                                        R.i = X.rsm->is_pipe ? pclose(X.rsm->F) : fclose(X.rsm->F);
@@ -2390,43 +2419,43 @@ re_cont:
                        setvar_i(res, R.d);
                        break;
 
-                 case XC( OC_BUILTIN ):
+               case XC( OC_BUILTIN ):
                        res = exec_builtin(op, res);
                        break;
 
-                 case XC( OC_SPRINTF ):
+               case XC( OC_SPRINTF ):
                        setvar_p(res, awk_printf(op1));
                        break;
 
-                 case XC( OC_UNARY ):
+               case XC( OC_UNARY ):
                        X.v = R.v;
                        L.d = R.d = getvar_i(R.v);
                        switch (opn) {
-                         case 'P':
+                       case 'P':
                                L.d = ++R.d;
                                goto r_op_change;
-                         case 'p':
+                       case 'p':
                                R.d++;
                                goto r_op_change;
-                         case 'M':
+                       case 'M':
                                L.d = --R.d;
                                goto r_op_change;
-                         case 'm':
+                       case 'm':
                                R.d--;
                                goto r_op_change;
-                         case '!':
-                           L.d = istrue(X.v) ? 0 : 1;
+                       case '!':
+                               L.d = istrue(X.v) ? 0 : 1;
                                break;
-                         case '-':
+                       case '-':
                                L.d = -R.d;
                                break;
                      r_op_change:
+ r_op_change:
                                setvar_i(X.v, R.d);
                        }
                        setvar_i(res, L.d);
                        break;
 
-                 case XC( OC_FIELD ):
+               case XC( OC_FIELD ):
                        R.i = (int)getvar_i(R.v);
                        if (R.i == 0) {
                                res = V[F0];
@@ -2440,53 +2469,53 @@ re_cont:
                        break;
 
                  /* concatenation (" ") and index joining (",") */
-                 case XC( OC_CONCAT ):
-                 case XC( OC_COMMA ):
+               case XC( OC_CONCAT ):
+               case XC( OC_COMMA ):
                        opn = strlen(L.s) + strlen(R.s) + 2;
-                       X.s = (char *)xmalloc(opn);
+                       X.s = xmalloc(opn);
                        strcpy(X.s, L.s);
                        if ((opinfo & OPCLSMASK) == OC_COMMA) {
                                L.s = getvar_s(V[SUBSEP]);
-                               X.s = (char *)xrealloc(X.s, opn + strlen(L.s));
+                               X.s = xrealloc(X.s, opn + strlen(L.s));
                                strcat(X.s, L.s);
                        }
                        strcat(X.s, R.s);
                        setvar_p(res, X.s);
                        break;
 
-                 case XC( OC_LAND ):
+               case XC( OC_LAND ):
                        setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
                        break;
 
-                 case XC( OC_LOR ):
+               case XC( OC_LOR ):
                        setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
                        break;
 
-                 case XC( OC_BINARY ):
-                 case XC( OC_REPLACE ):
+               case XC( OC_BINARY ):
+               case XC( OC_REPLACE ):
                        R.d = getvar_i(R.v);
                        switch (opn) {
-                         case '+':
+                       case '+':
                                L.d += R.d;
                                break;
-                         case '-':
+                       case '-':
                                L.d -= R.d;
                                break;
-                         case '*':
+                       case '*':
                                L.d *= R.d;
                                break;
-                         case '/':
+                       case '/':
                                if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
                                L.d /= R.d;
                                break;
-                         case '&':
-#ifdef CONFIG_FEATURE_AWK_MATH
+                       case '&':
+#if ENABLE_FEATURE_AWK_MATH
                                L.d = pow(L.d, R.d);
 #else
                                runtime_error(EMSG_NO_MATH);
 #endif
                                break;
-                         case '%':
+                       case '%':
                                if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
                                L.d -= (int)(L.d / R.d) * R.d;
                                break;
@@ -2494,7 +2523,7 @@ re_cont:
                        res = setvar_i(((opinfo&OPCLSMASK) == OC_BINARY) ? res : X.v, L.d);
                        break;
 
-                 case XC( OC_COMPARE ):
+               case XC( OC_COMPARE ):
                        if (is_numeric(L.v) && is_numeric(R.v)) {
                                L.d = getvar_i(L.v) - getvar_i(R.v);
                        } else {
@@ -2503,20 +2532,20 @@ re_cont:
                                L.d = icase ? strcasecmp(L.s, R.s) : strcmp(L.s, R.s);
                        }
                        switch (opn & 0xfe) {
-                         case 0:
+                       case 0:
                                R.i = (L.d > 0);
                                break;
-                         case 2:
+                       case 2:
                                R.i = (L.d >= 0);
                                break;
-                         case 4:
+                       case 4:
                                R.i = (L.d == 0);
                                break;
                        }
                        setvar_i(res, (opn & 0x1 ? R.i : !R.i) ? 1 : 0);
                        break;
 
-                 default:
+               default:
                        runtime_error(EMSG_POSSIBLE_ERROR);
                }
                if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS)
@@ -2535,20 +2564,22 @@ re_cont:
 
 static int awk_exit(int r)
 {
-       unsigned int i;
+       var tv;
+       unsigned i;
        hash_item *hi;
-       static var tv;
 
-       if (! exiting) {
+       zero_out_var(&tv);
+
+       if (!exiting) {
                exiting = TRUE;
                nextrec = FALSE;
                evaluate(endseq.first, &tv);
        }
 
        /* waiting for children */
-       for (i=0; i<fdhash->csize; i++) {
+       for (i = 0; i < fdhash->csize; i++) {
                hi = fdhash->items[i];
-               while(hi) {
+               while (hi) {
                        if (hi->data.rs.F && hi->data.rs.is_pipe)
                                pclose(hi->data.rs.F);
                        hi = hi->next;
@@ -2564,7 +2595,7 @@ static int is_assignment(const char *expr)
 {
        char *exprc, *s, *s0, *s1;
 
-       exprc = bb_xstrdup(expr);
+       exprc = xstrdup(expr);
        if (!isalnum_(*exprc) || (s = strchr(exprc, '=')) == NULL) {
                free(exprc);
                return FALSE;
@@ -2586,7 +2617,7 @@ static rstream *next_input_file(void)
 {
        static rstream rsm;
        FILE *F = NULL;
-       char *fname, *ind;
+       const char *fname, *ind;
        static int files_happen = FALSE;
 
        if (rsm.F) fclose(rsm.F);
@@ -2613,20 +2644,28 @@ static rstream *next_input_file(void)
        return &rsm;
 }
 
+int awk_main(int argc, char **argv);
 int awk_main(int argc, char **argv)
 {
-       char *s, *s1;
-       int i, j, c, flen;
+       unsigned opt;
+       char *opt_F, *opt_W;
+       llist_t *opt_v = NULL;
+       int i, j, flen;
        var *v;
-       static var tv;
+       var tv;
        char **envp;
-       static int from_file = FALSE;
-       rstream *rsm;
-       FILE *F, *stdfiles[3];
-       static char * stdnames = "/dev/stdin\0/dev/stdout\0/dev/stderr";
+       char *vnames = (char *)vNames; /* cheat */
+       char *vvalues = (char *)vValues;
+
+       /* Undo busybox.c, or else strtod may eat ','! This breaks parsing:
+        * $1,$2 == '$1,' '$2', NOT '$1' ',' '$2' */
+       if (ENABLE_LOCALE_SUPPORT)
+               setlocale(LC_NUMERIC, "C");
+
+       zero_out_var(&tv);
 
        /* allocate global buffer */
-       buf = xmalloc(MAXVARFMT+1);
+       buf = xmalloc(MAXVARFMT + 1);
 
        vhash = hash_init();
        ahash = hash_init();
@@ -2634,110 +2673,94 @@ int awk_main(int argc, char **argv)
        fnhash = hash_init();
 
        /* initialize variables */
-       for (i=0;  *vNames;  i++) {
-               V[i] = v = newvar(nextword(&vNames));
-               if (*vValues != '\377')
-                       setvar_s(v, nextword(&vValues));
+       for (i = 0; *vnames; i++) {
+               V[i] = v = newvar(nextword(&vnames));
+               if (*vvalues != '\377')
+                       setvar_s(v, nextword(&vvalues));
                else
                        setvar_i(v, 0);
 
-               if (*vNames == '*') {
+               if (*vnames == '*') {
                        v->type |= VF_SPECIAL;
-                       vNames++;
+                       vnames++;
                }
        }
 
        handle_special(V[FS]);
        handle_special(V[RS]);
 
-       stdfiles[0] = stdin;
-       stdfiles[1] = stdout;
-       stdfiles[2] = stderr;
-       for (i=0; i<3; i++) {
-               rsm = newfile(nextword(&stdnames));
-               rsm->F = stdfiles[i];
-       }
+       newfile("/dev/stdin")->F = stdin;
+       newfile("/dev/stdout")->F = stdout;
+       newfile("/dev/stderr")->F = stderr;
 
-       for (envp=environ; *envp; envp++) {
-               s = bb_xstrdup(*envp);
-               s1 = strchr(s, '=');
-               if (!s1) {
-                       goto keep_going;
+       for (envp = environ; *envp; envp++) {
+               char *s = xstrdup(*envp);
+               char *s1 = strchr(s, '=');
+               if (s1) {
+                       *s1++ = '\0';
+                       setvar_u(findvar(iamarray(V[ENVIRON]), s), s1);
                }
-               *(s1++) = '\0';
-               setvar_u(findvar(iamarray(V[ENVIRON]), s), s1);
-keep_going:
                free(s);
        }
-
-       while((c = getopt(argc, argv, "F:v:f:W:")) != EOF) {
-               switch (c) {
-                       case 'F':
-                               setvar_s(V[FS], optarg);
-                               break;
-                       case 'v':
-                               if (! is_assignment(optarg))
-                                       bb_show_usage();
-                               break;
-                       case 'f':
-                               from_file = TRUE;
-                               F = afopen(programname = optarg, "r");
-                               s = NULL;
-                               /* one byte is reserved for some trick in next_token */
-                               if (fseek(F, 0, SEEK_END) == 0) {
-                                       flen = ftell(F);
-                                       s = (char *)xmalloc(flen+4);
-                                       fseek(F, 0, SEEK_SET);
-                                       i = 1 + fread(s+1, 1, flen, F);
-                               } else {
-                                       for (i=j=1; j>0; i+=j) {
-                                               s = (char *)xrealloc(s, i+4096);
-                                               j = fread(s+i, 1, 4094, F);
-                                       }
-                               }
-                               s[i] = '\0';
-                               fclose(F);
-                               parse_program(s+1);
-                               free(s);
-                               break;
-                       case 'W':
-                               bb_error_msg("Warning: unrecognized option '-W %s' ignored\n", optarg);
-                               break;
-
-                       default:
-                               bb_show_usage();
-               }
+       opt_complementary = "v::";
+       opt = getopt32(argc, argv, "F:v:f:W:", &opt_F, &opt_v, &programname, &opt_W);
+       argv += optind;
+       argc -= optind;
+       if (opt & 0x1) setvar_s(V[FS], opt_F); // -F
+       opt_v = llist_rev(opt_v);
+       while (opt_v) { /* -v */
+               if (!is_assignment(llist_pop(&opt_v)))
+                       bb_show_usage();
        }
-
-       if (!from_file) {
-               if (argc == optind)
+       if (opt & 0x4) { // -f
+               char *s = s; /* die, gcc, die */
+               FILE *from_file = afopen(programname, "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 {
+                       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);
+       } else { // no -f: take program from 1st parameter
+               if (!argc)
                        bb_show_usage();
-               programname="cmd. line";
-               parse_program(argv[optind++]);
-
+               programname = "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(V[ARGC], argc - optind + 1);
+       setvar_i(V[ARGC], argc + 1);
        setari_u(V[ARGV], 0, "awk");
-       for(i=optind; i < argc; i++)
-               setari_u(V[ARGV], i+1-optind, argv[i]);
+       i = 0;
+       while (*argv)
+               setari_u(V[ARGV], ++i, *argv++);
 
        evaluate(beginseq.first, &tv);
-       if (! mainseq.first && ! endseq.first)
+       if (!mainseq.first && !endseq.first)
                awk_exit(EXIT_SUCCESS);
 
        /* input file could already be opened in BEGIN block */
-       if (! iF) iF = next_input_file();
+       if (!iF) iF = next_input_file();
 
        /* passing through input files */
        while (iF) {
-
                nextfile = FALSE;
                setvar_i(V[FNR], 0);
 
-               while ((c = awk_getline(iF, V[F0])) > 0) {
-
+               while ((i = awk_getline(iF, V[F0])) > 0) {
                        nextrec = FALSE;
                        incvar(V[NR]);
                        incvar(V[FNR]);
@@ -2747,15 +2770,12 @@ keep_going:
                                break;
                }
 
-               if (c < 0)
+               if (i < 0)
                        runtime_error(strerror(errno));
 
                iF = next_input_file();
-
        }
 
        awk_exit(EXIT_SUCCESS);
-
-       return 0;
+       /*return 0;*/
 }
-