#define DEBUG
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <wctype.h>
#include <fcntl.h>
#include <setjmp.h>
#include <limits.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "awk.h"
#include "awkgram.tab.h"
static void stdinit(void);
static void flush_all(void);
static char *wide_char_to_byte_str(int rune, size_t *outlen);
#if 1
#define tempfree(x) do { if (istemp(x)) tfree(x); } while (0)
#else
void tempfree(Cell *p) {
if (p->ctype == OCELL && (p->csub < CUNK || p->csub > CFREE)) {
WARNING("bad csub %d in Cell %d %s",
p->csub, p->ctype, p->sval);
}
if (istemp(p))
tfree(p);
}
#endif
jmp_buf env;
extern int pairstack[];
extern Awkfloat srand_seed;
Node *winner = NULL;
Cell *tmps;
static Cell truecell ={ OBOOL, BTRUE, 0, 0, 1.0, NUM, NULL, NULL };
Cell *True = &truecell;
static Cell falsecell ={ OBOOL, BFALSE, 0, 0, 0.0, NUM, NULL, NULL };
Cell *False = &falsecell;
static Cell breakcell ={ OJUMP, JBREAK, 0, 0, 0.0, NUM, NULL, NULL };
Cell *jbreak = &breakcell;
static Cell contcell ={ OJUMP, JCONT, 0, 0, 0.0, NUM, NULL, NULL };
Cell *jcont = &contcell;
static Cell nextcell ={ OJUMP, JNEXT, 0, 0, 0.0, NUM, NULL, NULL };
Cell *jnext = &nextcell;
static Cell nextfilecell ={ OJUMP, JNEXTFILE, 0, 0, 0.0, NUM, NULL, NULL };
Cell *jnextfile = &nextfilecell;
static Cell exitcell ={ OJUMP, JEXIT, 0, 0, 0.0, NUM, NULL, NULL };
Cell *jexit = &exitcell;
static Cell retcell ={ OJUMP, JRET, 0, 0, 0.0, NUM, NULL, NULL };
Cell *jret = &retcell;
static Cell tempcell ={ OCELL, CTEMP, 0, EMPTY, 0.0, NUM|STR|DONTFREE, NULL, NULL };
Node *curnode = NULL;
int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr,
const char *whatrtn)
{
if (minlen > *psiz) {
char *tbuf;
int rminlen = quantum ? minlen % quantum : 0;
int boff = pbptr ? *pbptr - *pbuf : 0;
if (rminlen)
minlen += quantum - rminlen;
tbuf = (char *) realloc(*pbuf, minlen);
DPRINTF("adjbuf %s: %d %d (pbuf=%p, tbuf=%p)\n", whatrtn, *psiz, minlen, (void*)*pbuf, (void*)tbuf);
if (tbuf == NULL) {
if (whatrtn)
FATAL("out of memory in %s", whatrtn);
return 0;
}
*pbuf = tbuf;
*psiz = minlen;
if (pbptr)
*pbptr = tbuf + boff;
}
return 1;
}
void run(Node *a)
{
stdinit();
execute(a);
closeall();
}
Cell *execute(Node *u)
{
Cell *(*proc)(Node **, int);
Cell *x;
Node *a;
if (u == NULL)
return(True);
for (a = u; ; a = a->nnext) {
curnode = a;
if (isvalue(a)) {
x = (Cell *) (a->narg[0]);
if (isfld(x) && !donefld)
fldbld();
else if (isrec(x) && !donerec)
recbld();
return(x);
}
if (notlegal(a->nobj))
FATAL("illegal statement");
proc = proctab[a->nobj-FIRSTTOKEN];
x = (*proc)(a->narg, a->nobj);
if (isfld(x) && !donefld)
fldbld();
else if (isrec(x) && !donerec)
recbld();
if (isexpr(a))
return(x);
if (isjump(x))
return(x);
if (a->nnext == NULL)
return(x);
tempfree(x);
}
}
Cell *program(Node **a, int n)
{
Cell *x;
if (setjmp(env) != 0)
goto ex;
if (a[0]) {
x = execute(a[0]);
if (isexit(x))
return(True);
if (isjump(x))
FATAL("illegal break, continue, next or nextfile from BEGIN");
tempfree(x);
}
if (a[1] || a[2])
while (getrec(&record, &recsize, true) > 0) {
x = execute(a[1]);
if (isexit(x))
break;
tempfree(x);
}
ex:
if (setjmp(env) != 0)
goto ex1;
if (a[2]) {
x = execute(a[2]);
if (isbreak(x) || isnext(x) || iscont(x))
FATAL("illegal break, continue, next or nextfile from END");
tempfree(x);
}
ex1:
return(True);
}
struct Frame {
int nargs;
Cell *fcncell;
Cell **args;
Cell *retval;
};
#define NARGS 50
struct Frame *frame = NULL;
int nframe = 0;
struct Frame *frp = NULL;
Cell *call(Node **a, int n)
{
static const Cell newcopycell = { OCELL, CCOPY, 0, EMPTY, 0.0, NUM|STR|DONTFREE, NULL, NULL };
int i, ncall, ndef;
int freed = 0;
Node *x;
Cell *args[NARGS], *oargs[NARGS];
Cell *y, *z, *fcn;
char *s;
fcn = execute(a[0]);
s = fcn->nval;
if (!isfcn(fcn))
FATAL("calling undefined function %s", s);
if (frame == NULL) {
frp = frame = (struct Frame *) calloc(nframe += 100, sizeof(*frame));
if (frame == NULL)
FATAL("out of space for stack frames calling %s", s);
}
for (ncall = 0, x = a[1]; x != NULL; x = x->nnext)
ncall++;
ndef = (int) fcn->fval;
DPRINTF("calling %s, %d args (%d in defn), frp=%d\n", s, ncall, ndef, (int) (frp-frame));
if (ncall > ndef)
WARNING("function %s called with %d args, uses only %d",
s, ncall, ndef);
if (ncall + ndef > NARGS)
FATAL("function %s has %d arguments, limit %d", s, ncall+ndef, NARGS);
for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) {
DPRINTF("evaluate args[%d], frp=%d:\n", i, (int) (frp-frame));
y = execute(x);
oargs[i] = y;
DPRINTF("args[%d]: %s %f <%s>, t=%o\n",
i, NN(y->nval), y->fval, isarr(y) ? "(array)" : NN(y->sval), y->tval);
if (isfcn(y))
FATAL("can't use function %s as argument in %s", y->nval, s);
if (isarr(y))
args[i] = y;
else
args[i] = copycell(y);
tempfree(y);
}
for ( ; i < ndef; i++) {
args[i] = gettemp();
*args[i] = newcopycell;
}
frp++;
if (frp >= frame + nframe) {
int dfp = frp - frame;
frame = (struct Frame *) reallocarray(frame, (nframe += 100), sizeof(*frame));
if (frame == NULL)
FATAL("out of space for stack frames in %s", s);
frp = frame + dfp;
}
frp->fcncell = fcn;
frp->args = args;
frp->nargs = ndef;
frp->retval = gettemp();
DPRINTF("start exec of %s, frp=%d\n", s, (int) (frp-frame));
y = execute((Node *)(fcn->sval));
DPRINTF("finished exec of %s, frp=%d\n", s, (int) (frp-frame));
for (i = 0; i < ndef; i++) {
Cell *t = frp->args[i];
if (isarr(t)) {
if (t->csub == CCOPY) {
if (i >= ncall) {
freesymtab(t);
t->csub = CTEMP;
tempfree(t);
} else {
oargs[i]->tval = t->tval;
oargs[i]->tval &= ~(STR|NUM|DONTFREE);
oargs[i]->sval = t->sval;
tempfree(t);
}
}
} else if (t != y) {
t->csub = CTEMP;
tempfree(t);
} else if (t == y && t->csub == CCOPY) {
t->csub = CTEMP;
tempfree(t);
freed = 1;
}
}
tempfree(fcn);
if (isexit(y) || isnext(y))
return y;
if (freed == 0) {
tempfree(y);
}
z = frp->retval;
DPRINTF("%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval);
frp--;
return(z);
}
Cell *copycell(Cell *x)
{
Cell *y;
y = gettemp();
y->tval = x->tval & ~(CON|FLD|REC);
y->csub = CCOPY;
y->nval = x->nval;
if (isstr(x) ) {
y->sval = tostring(x->sval);
y->tval &= ~DONTFREE;
} else
y->tval |= DONTFREE;
y->fval = x->fval;
return y;
}
Cell *arg(Node **a, int n)
{
n = ptoi(a[0]);
DPRINTF("arg(%d), frp->nargs=%d\n", n, frp->nargs);
if (n+1 > frp->nargs)
FATAL("argument #%d of function %s was not supplied",
n+1, frp->fcncell->nval);
return frp->args[n];
}
Cell *jump(Node **a, int n)
{
Cell *y;
switch (n) {
case EXIT:
if (a[0] != NULL) {
y = execute(a[0]);
errorflag = (int) getfval(y);
tempfree(y);
}
longjmp(env, 1);
case RETURN:
if (a[0] != NULL) {
y = execute(a[0]);
if ((y->tval & (STR|NUM)) == (STR|NUM)) {
setsval(frp->retval, getsval(y));
frp->retval->fval = getfval(y);
frp->retval->tval |= NUM;
}
else if (y->tval & STR)
setsval(frp->retval, getsval(y));
else if (y->tval & NUM)
setfval(frp->retval, getfval(y));
else
FATAL("bad type variable %d", y->tval);
tempfree(y);
}
return(jret);
case NEXT:
return(jnext);
case NEXTFILE:
nextfile();
return(jnextfile);
case BREAK:
return(jbreak);
case CONTINUE:
return(jcont);
default:
FATAL("illegal jump type %d", n);
}
return 0;
}
Cell *awkgetline(Node **a, int n)
{
Cell *r, *x;
extern Cell **fldtab;
FILE *fp;
char *buf;
int bufsize = recsize;
int mode;
bool newflag;
double result;
if ((buf = (char *) malloc(bufsize)) == NULL)
FATAL("out of memory in getline");
fflush(stdout);
r = gettemp();
if (a[1] != NULL) {
x = execute(a[2]);
mode = ptoi(a[1]);
if (mode == '|')
mode = LE;
fp = openfile(mode, getsval(x), &newflag);
tempfree(x);
if (fp == NULL)
n = -1;
else
n = readrec(&buf, &bufsize, fp, newflag);
if (n <= 0) {
;
} else if (a[0] != NULL) {
x = execute(a[0]);
setsval(x, buf);
if (is_number(x->sval, & result)) {
x->fval = result;
x->tval |= NUM;
}
tempfree(x);
} else {
setsval(fldtab[0], buf);
if (is_number(fldtab[0]->sval, & result)) {
fldtab[0]->fval = result;
fldtab[0]->tval |= NUM;
}
}
} else {
if (a[0] == NULL)
n = getrec(&record, &recsize, true);
else {
n = getrec(&buf, &bufsize, false);
if (n > 0) {
x = execute(a[0]);
setsval(x, buf);
if (is_number(x->sval, & result)) {
x->fval = result;
x->tval |= NUM;
}
tempfree(x);
}
}
}
setfval(r, (Awkfloat) n);
free(buf);
return r;
}
Cell *getnf(Node **a, int n)
{
if (!donefld)
fldbld();
return (Cell *) a[0];
}
static char *
makearraystring(Node *p, const char *func)
{
char *buf;
int bufsz = recsize;
size_t blen;
if ((buf = (char *) malloc(bufsz)) == NULL) {
FATAL("%s: out of memory", func);
}
blen = 0;
buf[blen] = '\0';
for (; p; p = p->nnext) {
Cell *x = execute(p);
char *s = getsval(x);
size_t seplen = strlen(getsval(subseploc));
size_t nsub = p->nnext ? seplen : 0;
size_t slen = strlen(s);
size_t tlen = blen + slen + nsub;
if (!adjbuf(&buf, &bufsz, tlen + 1, recsize, 0, func)) {
FATAL("%s: out of memory %s[%s...]",
func, x->nval, buf);
}
memcpy(buf + blen, s, slen);
if (nsub) {
memcpy(buf + blen + slen, *SUBSEP, nsub);
}
buf[tlen] = '\0';
blen = tlen;
tempfree(x);
}
return buf;
}
Cell *array(Node **a, int n)
{
Cell *x, *z;
char *buf;
x = execute(a[0]);
buf = makearraystring(a[1], __func__);
if (!isarr(x)) {
DPRINTF("making %s into an array\n", NN(x->nval));
if (freeable(x))
xfree(x->sval);
x->tval &= ~(STR|NUM|DONTFREE);
x->tval |= ARR;
x->sval = (char *) makesymtab(NSYMTAB);
}
z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval);
z->ctype = OCELL;
z->csub = CVAR;
tempfree(x);
free(buf);
return(z);
}
Cell *awkdelete(Node **a, int n)
{
Cell *x;
x = execute(a[0]);
if (x == symtabloc) {
FATAL("cannot delete SYMTAB or its elements");
}
if (!isarr(x))
return True;
if (a[1] == NULL) {
freesymtab(x);
x->tval &= ~STR;
x->tval |= ARR;
x->sval = (char *) makesymtab(NSYMTAB);
} else {
char *buf = makearraystring(a[1], __func__);
freeelem(x, buf);
free(buf);
}
tempfree(x);
return True;
}
Cell *intest(Node **a, int n)
{
Cell *ap, *k;
char *buf;
ap = execute(a[1]);
if (!isarr(ap)) {
DPRINTF("making %s into an array\n", ap->nval);
if (freeable(ap))
xfree(ap->sval);
ap->tval &= ~(STR|NUM|DONTFREE);
ap->tval |= ARR;
ap->sval = (char *) makesymtab(NSYMTAB);
}
buf = makearraystring(a[0], __func__);
k = lookup(buf, (Array *) ap->sval);
tempfree(ap);
free(buf);
if (k == NULL)
return(False);
else
return(True);
}
static int u8_isutf(const char *s)
{
int ret;
unsigned char c;
c = s[0];
if (c < 128 || awk_mb_cur_max == 1) {
ret = 1;
} else if (((c>>5) & 0x7) == 0x6 && (s[1] & 0xC0) == 0x80) {
ret = 2;
} else if (((c>>4) & 0xF) == 0xE && (s[1] & 0xC0) == 0x80
&& (s[2] & 0xC0) == 0x80) {
ret = 3;
} else if (((c>>3) & 0x1F) == 0x1E && (s[1] & 0xC0) == 0x80
&& (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80) {
ret = 4;
} else {
ret = 0;
}
return ret;
}
int u8_rune(int *rune, const char *s)
{
int n, ret;
unsigned char c;
c = s[0];
if (c < 128 || awk_mb_cur_max == 1) {
*rune = c;
return 1;
}
n = strlen(s);
if (n >= 2 && ((c>>5) & 0x7) == 0x6 && (s[1] & 0xC0) == 0x80) {
*rune = ((c & 0x1F) << 6) | (s[1] & 0x3F);
ret = 2;
} else if (n >= 3 && ((c>>4) & 0xF) == 0xE && (s[1] & 0xC0) == 0x80
&& (s[2] & 0xC0) == 0x80) {
*rune = ((c & 0xF) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
ret = 3;
} else if (n >= 4 && ((c>>3) & 0x1F) == 0x1E && (s[1] & 0xC0) == 0x80
&& (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80) {
*rune = ((c & 0x7) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
ret = 4;
} else {
*rune = c;
ret = 1;
}
return ret;
}
int u8_nextlen(const char *s)
{
int len;
len = u8_isutf(s);
if (len == 0)
len = 1;
return len;
}
static int u8_strlen(const char *s)
{
int i, len, n, totlen;
unsigned char c;
n = strlen(s);
totlen = 0;
for (i = 0; i < n; i += len) {
c = s[i];
if (c < 128 || awk_mb_cur_max == 1) {
len = 1;
} else {
len = u8_nextlen(&s[i]);
}
totlen++;
if (i > n)
FATAL("bad utf count [%s] n=%d i=%d\n", s, n, i);
}
return totlen;
}
static int u8_char2byte(const char *s, int charnum)
{
int n;
int bytenum = 0;
while (charnum > 0) {
n = u8_nextlen(s);
s += n;
bytenum += n;
charnum--;
}
return bytenum;
}
static int u8_byte2char(const char *s, int bytenum)
{
int i, len, b;
int charnum = 0;
b = strlen(s);
if (bytenum > b) {
return -1;
}
for (i = 0; i <= bytenum; i += len) {
len = u8_nextlen(s+i);
charnum++;
}
return charnum;
}
enum
{
Runeerror = 128,
Runemax = 0x10FFFF,
Bit1 = 7,
Bitx = 6,
Bit2 = 5,
Bit3 = 4,
Bit4 = 3,
Bit5 = 2,
T1 = ((1<<(Bit1+1))-1) ^ 0xFF,
Tx = ((1<<(Bitx+1))-1) ^ 0xFF,
T2 = ((1<<(Bit2+1))-1) ^ 0xFF,
T3 = ((1<<(Bit3+1))-1) ^ 0xFF,
T4 = ((1<<(Bit4+1))-1) ^ 0xFF,
T5 = ((1<<(Bit5+1))-1) ^ 0xFF,
Rune1 = (1<<(Bit1+0*Bitx))-1,
Rune2 = (1<<(Bit2+1*Bitx))-1,
Rune3 = (1<<(Bit3+2*Bitx))-1,
Rune4 = (1<<(Bit4+3*Bitx))-1,
Maskx = (1<<Bitx)-1,
Testx = Maskx ^ 0xFF,
};
int runetochar(char *str, int c)
{
if (c <= Rune1) {
str[0] = c;
return 1;
}
if (c <= Rune2) {
str[0] = T2 | (c >> 1*Bitx);
str[1] = Tx | (c & Maskx);
return 2;
}
if (c > Runemax)
c = Runeerror;
if (c <= Rune3) {
str[0] = T3 | (c >> 2*Bitx);
str[1] = Tx | ((c >> 1*Bitx) & Maskx);
str[2] = Tx | (c & Maskx);
return 3;
}
str[0] = T4 | (c >> 3*Bitx);
str[1] = Tx | ((c >> 2*Bitx) & Maskx);
str[2] = Tx | ((c >> 1*Bitx) & Maskx);
str[3] = Tx | (c & Maskx);
return 4;
}
Cell *matchop(Node **a, int n)
{
Cell *x, *y, *z;
char *s, *t;
int i;
int cstart, cpatlen, len;
fa *pfa;
int (*mf)(fa *, const char *) = match, mode = 0;
if (n == MATCHFCN) {
mf = pmatch;
mode = 1;
}
x = execute(a[1]);
s = getsval(x);
if (a[0] == NULL)
i = (*mf)((fa *) a[2], s);
else {
y = execute(a[2]);
t = getsval(y);
pfa = makedfa(t, mode);
i = (*mf)(pfa, s);
tempfree(y);
}
z = x;
if (n == MATCHFCN) {
int start = patbeg - s + 1;
if (patlen < 0) {
start = 0;
} else {
cstart = u8_byte2char(s, start-1);
cpatlen = 0;
for (i = 0; i < patlen; i += len) {
len = u8_nextlen(patbeg+i);
cpatlen++;
}
start = cstart;
patlen = cpatlen;
}
setfval(rstartloc, (Awkfloat) start);
setfval(rlengthloc, (Awkfloat) patlen);
x = gettemp();
x->tval = NUM;
x->fval = start;
} else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0))
x = True;
else
x = False;
tempfree(z);
return x;
}
Cell *boolop(Node **a, int n)
{
Cell *x, *y;
int i;
x = execute(a[0]);
i = istrue(x);
tempfree(x);
switch (n) {
case BOR:
if (i) return(True);
y = execute(a[1]);
i = istrue(y);
tempfree(y);
if (i) return(True);
else return(False);
case AND:
if ( !i ) return(False);
y = execute(a[1]);
i = istrue(y);
tempfree(y);
if (i) return(True);
else return(False);
case NOT:
if (i) return(False);
else return(True);
default:
FATAL("unknown boolean operator %d", n);
}
return 0;
}
Cell *relop(Node **a, int n)
{
int i;
Cell *x, *y;
Awkfloat j;
bool x_is_nan, y_is_nan;
x = execute(a[0]);
y = execute(a[1]);
x_is_nan = isnan(x->fval);
y_is_nan = isnan(y->fval);
if (x->tval&NUM && y->tval&NUM) {
if ((x_is_nan || y_is_nan) && n != NE)
return(False);
j = x->fval - y->fval;
i = j<0? -1: (j>0? 1: 0);
} else {
i = strcmp(getsval(x), getsval(y));
}
tempfree(x);
tempfree(y);
switch (n) {
case LT: if (i<0) return(True);
else return(False);
case LE: if (i<=0) return(True);
else return(False);
case NE: if (x_is_nan && y_is_nan) return(True);
else if (i!=0) return(True);
else return(False);
case EQ: if (i == 0) return(True);
else return(False);
case GE: if (i>=0) return(True);
else return(False);
case GT: if (i>0) return(True);
else return(False);
default:
FATAL("unknown relational operator %d", n);
}
return 0;
}
void tfree(Cell *a)
{
if (freeable(a)) {
DPRINTF("freeing %s %s %o\n", NN(a->nval), NN(a->sval), a->tval);
xfree(a->sval);
}
if (a == tmps)
FATAL("tempcell list is curdled");
a->cnext = tmps;
tmps = a;
}
Cell *gettemp(void)
{ int i;
Cell *x;
if (!tmps) {
tmps = (Cell *) calloc(100, sizeof(*tmps));
if (!tmps)
FATAL("out of space for temporaries");
for (i = 1; i < 100; i++)
tmps[i-1].cnext = &tmps[i];
tmps[i-1].cnext = NULL;
}
x = tmps;
tmps = x->cnext;
*x = tempcell;
return(x);
}
Cell *indirect(Node **a, int n)
{
Awkfloat val;
Cell *x;
int m;
x = execute(a[0]);
val = getfval(x);
if ((Awkfloat)INT_MAX < val)
FATAL("trying to access out of range field %s", x->nval);
m = (int) val;
tempfree(x);
x = fieldadr(m);
x->ctype = OCELL;
x->csub = CFLD;
return(x);
}
Cell *substr(Node **a, int nnn)
{
int k, m, n;
int mb, nb;
char *s;
int temp;
Cell *x, *y, *z = NULL;
x = execute(a[0]);
y = execute(a[1]);
if (a[2] != NULL)
z = execute(a[2]);
s = getsval(x);
k = u8_strlen(s) + 1;
if (k <= 1) {
tempfree(x);
tempfree(y);
if (a[2] != NULL) {
tempfree(z);
}
x = gettemp();
setsval(x, "");
return(x);
}
m = (int) getfval(y);
if (m <= 0)
m = 1;
else if (m > k)
m = k;
tempfree(y);
if (a[2] != NULL) {
n = (int) getfval(z);
tempfree(z);
} else
n = k - 1;
if (n < 0)
n = 0;
else if (n > k - m)
n = k - m;
DPRINTF("substr: m=%d, n=%d, s=%s\n", m, n, s);
y = gettemp();
mb = u8_char2byte(s, m-1);
nb = mb + u8_char2byte(&s[mb], n);
temp = s[nb];
s[nb] = '\0';
setsval(y, s + mb);
s[nb] = temp;
tempfree(x);
return(y);
}
Cell *sindex(Node **a, int nnn)
{
Cell *x, *y, *z;
char *s1, *s2, *p1, *p2, *q;
Awkfloat v = 0.0;
x = execute(a[0]);
s1 = getsval(x);
y = execute(a[1]);
s2 = getsval(y);
z = gettemp();
for (p1 = s1; *p1 != '\0'; p1++) {
for (q = p1, p2 = s2; *p2 != '\0' && *q == *p2; q++, p2++)
continue;
if (*p2 == '\0') {
int i, len;
v = 0;
for (i = 0; i < p1-s1+1; i += len) {
len = u8_nextlen(s1+i);
v++;
}
break;
}
}
tempfree(x);
tempfree(y);
setfval(z, v);
return(z);
}
static int has_utf8(char *s)
{
int n;
for (n = 0; *s != 0; s += n) {
n = u8_nextlen(s);
if (n > 1)
return 1;
}
return 0;
}
#define MAXNUMSIZE 50
int format(char **pbuf, int *pbufsize, const char *s, Node *a)
{
char *fmt;
char *p, *t;
const char *os;
Cell *x;
int flag = 0, n;
int fmtwd;
int fmtsz = recsize;
char *buf = *pbuf;
int bufsize = *pbufsize;
#define FMTSZ(a) (fmtsz - ((a) - fmt))
#define BUFSZ(a) (bufsize - ((a) - buf))
static bool first = true;
static bool have_a_format = false;
if (first) {
char xbuf[100];
snprintf(xbuf, sizeof(xbuf), "%a", 42.0);
have_a_format = (strcmp(xbuf, "0x1.5p+5") == 0);
first = false;
}
os = s;
p = buf;
if ((fmt = (char *) malloc(fmtsz)) == NULL)
FATAL("out of memory in format()");
while (*s) {
adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format1");
if (*s != '%') {
*p++ = *s++;
continue;
}
if (*(s+1) == '%') {
*p++ = '%';
s += 2;
continue;
}
fmtwd = atoi(s+1);
if (fmtwd < 0)
fmtwd = -fmtwd;
adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format2");
for (t = fmt; (*t++ = *s) != '\0'; s++) {
if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, "format3"))
FATAL("format item %.30s... ran format() out of memory", os);
if (strchr("hjLlqtz", *s) != NULL) {
t--;
continue;
}
if (isalpha((uschar)*s))
break;
if (*s == '$') {
FATAL("'$' not permitted in awk formats");
}
if (*s == '*') {
if (a == NULL) {
FATAL("not enough args in printf(%s)", os);
}
x = execute(a);
a = a->nnext;
snprintf(t - 1, FMTSZ(t - 1),
"%d", fmtwd=(int) getfval(x));
if (fmtwd < 0)
fmtwd = -fmtwd;
adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
t = fmt + strlen(fmt);
tempfree(x);
}
}
*t = '\0';
if (fmtwd < 0)
fmtwd = -fmtwd;
adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format4");
switch (*s) {
case 'a': case 'A':
if (have_a_format)
flag = *s;
else
flag = 'f';
break;
case 'f': case 'e': case 'g': case 'E': case 'G':
flag = 'f';
break;
case 'd': case 'i': case 'o': case 'x': case 'X': case 'u':
flag = (*s == 'd' || *s == 'i') ? 'd' : 'u';
*(t-1) = 'j';
*t = *s;
*++t = '\0';
break;
case 's':
flag = 's';
break;
case 'c':
flag = 'c';
break;
default:
WARNING("weird printf conversion %s", fmt);
flag = '?';
break;
}
if (a == NULL)
FATAL("not enough args in printf(%s)", os);
x = execute(a);
a = a->nnext;
n = MAXNUMSIZE;
if (fmtwd > n)
n = fmtwd;
adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format5");
switch (flag) {
case '?':
snprintf(p, BUFSZ(p), "%s", fmt);
t = getsval(x);
n = strlen(t);
if (fmtwd > n)
n = fmtwd;
adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format6");
p += strlen(p);
snprintf(p, BUFSZ(p), "%s", t);
break;
case 'a':
case 'A':
case 'f': snprintf(p, BUFSZ(p), fmt, getfval(x)); break;
case 'd': snprintf(p, BUFSZ(p), fmt, (intmax_t) getfval(x)); break;
case 'u': snprintf(p, BUFSZ(p), fmt, (uintmax_t) getfval(x)); break;
case 's': {
t = getsval(x);
n = strlen(t);
if (!has_utf8(t) || strcmp(fmt,"%s") == 0) {
if (fmtwd > n)
n = fmtwd;
if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format7"))
FATAL("huge string/format (%d chars) in printf %.30s..." \
" ran format() out of memory", n, t);
snprintf(p, BUFSZ(p), fmt, t);
break;
}
int ljust = 0, wid = 0, prec = n, pad = 0;
char *f = fmt+1;
if (f[0] == '-') {
ljust = 1;
f++;
}
if (f[0] == '0') {
f++;
if (f[0] == '+')
f++;
}
if (f[0] == '+') {
f++;
if (f[0] == '0')
f++;
}
if (isdigit((uschar)f[0])) {
wid = strtol(f, &f, 10);
}
if (f[0] == '.') {
prec = strtol(++f, &f, 10);
}
if (prec > u8_strlen(t))
prec = u8_strlen(t);
pad = wid>prec ? wid - prec : 0;
int i, precb;
if (ljust) {
precb = u8_char2byte(t, prec);
for (i = 0; i < precb; i++) {
*p++ = t[i];
}
for (i = 0; i < pad; i++) {
*p++ = ' ';
}
} else {
for (i = 0; i < pad; i++) {
*p++ = ' ';
}
precb = u8_char2byte(t, prec);
for (i = 0; i < precb; i++) {
*p++ = t[i];
}
}
*p = 0;
break;
}
case 'c': {
if (isnum(x)) {
int charval = (int) getfval(x);
if (charval != 0) {
if (charval < 128 || awk_mb_cur_max == 1)
snprintf(p, BUFSZ(p), fmt, charval);
else {
size_t count;
char *bs = wide_char_to_byte_str(charval, &count);
if (bs == NULL) {
static char invalid_char[] = "\357\277\275";
bs = invalid_char;
count = 3;
}
t = bs;
n = count;
goto format_percent_c;
}
} else {
*p++ = '\0';
*p = '\0';
}
break;
}
t = getsval(x);
n = u8_nextlen(t);
format_percent_c:
if (n < 2) {
snprintf(p, BUFSZ(p), fmt, getsval(x)[0]);
break;
}
int ljust = 0, wid = 0, prec = n, pad = 0;
char *f = fmt+1;
if (f[0] == '-') {
ljust = 1;
f++;
}
if (f[0] == '0') {
f++;
if (f[0] == '+')
f++;
}
if (f[0] == '+') {
f++;
if (f[0] == '0')
f++;
}
if (isdigit((uschar)f[0])) {
wid = strtol(f, &f, 10);
}
if (f[0] == '.') {
prec = strtol(++f, &f, 10);
}
if (prec > 1)
prec = 1;
pad = wid>prec ? wid - prec : 0;
int i;
if (ljust) {
for (i = 0; i < n; i++)
*p++ = t[i];
for (i = 0; i < pad; i++) {
*p++ = ' ';
}
} else {
for (i = 0; i < pad; i++) {
*p++ = ' ';
}
for (i = 0; i < n; i++)
*p++ = t[i];
}
*p = 0;
break;
}
default:
FATAL("can't happen: bad conversion %c in format()", flag);
}
tempfree(x);
p += strlen(p);
s++;
}
*p = '\0';
free(fmt);
for ( ; a; a = a->nnext) {
x = execute(a);
tempfree(x);
}
*pbuf = buf;
*pbufsize = bufsize;
return p - buf;
}
Cell *awksprintf(Node **a, int n)
{
Cell *x;
Node *y;
char *buf;
int bufsz=3*recsize;
if ((buf = (char *) malloc(bufsz)) == NULL)
FATAL("out of memory in awksprintf");
y = a[0]->nnext;
x = execute(a[0]);
if (format(&buf, &bufsz, getsval(x), y) == -1)
FATAL("sprintf string %.30s... too long. can't happen.", buf);
tempfree(x);
x = gettemp();
x->sval = buf;
x->tval = STR;
return(x);
}
Cell *awkprintf(Node **a, int n)
{
FILE *fp;
Cell *x;
Node *y;
char *buf;
int len;
int bufsz=3*recsize;
if ((buf = (char *) malloc(bufsz)) == NULL)
FATAL("out of memory in awkprintf");
y = a[0]->nnext;
x = execute(a[0]);
if ((len = format(&buf, &bufsz, getsval(x), y)) == -1)
FATAL("printf string %.30s... too long. can't happen.", buf);
tempfree(x);
if (a[1] == NULL) {
fwrite(buf, len, 1, stdout);
if (ferror(stdout))
FATAL("write error on stdout");
} else {
fp = redirect(ptoi(a[1]), a[2]);
fwrite(buf, len, 1, fp);
fflush(fp);
if (ferror(fp))
FATAL("write error on %s", filename(fp));
}
free(buf);
return(True);
}
Cell *arith(Node **a, int n)
{
Awkfloat i, j = 0;
double v;
Cell *x, *y, *z;
x = execute(a[0]);
i = getfval(x);
tempfree(x);
if (n != UMINUS && n != UPLUS) {
y = execute(a[1]);
j = getfval(y);
tempfree(y);
}
z = gettemp();
switch (n) {
case ADD:
i += j;
break;
case MINUS:
i -= j;
break;
case MULT:
i *= j;
break;
case DIVIDE:
if (j == 0)
FATAL("division by zero");
i /= j;
break;
case MOD:
if (j == 0)
FATAL("division by zero in mod");
modf(i/j, &v);
i = i - j * v;
break;
case UMINUS:
i = -i;
break;
case UPLUS:
break;
case POWER:
if (j >= 0 && modf(j, &v) == 0.0)
i = ipow(i, (int) j);
else {
errno = 0;
i = errcheck(pow(i, j), "pow");
}
break;
default:
FATAL("illegal arithmetic operator %d", n);
}
setfval(z, i);
return(z);
}
double ipow(double x, int n)
{
double v;
if (n <= 0)
return 1;
v = ipow(x, n/2);
if (n % 2 == 0)
return v * v;
else
return x * v * v;
}
Cell *incrdecr(Node **a, int n)
{
Cell *x, *z;
int k;
Awkfloat xf;
x = execute(a[0]);
xf = getfval(x);
k = (n == PREINCR || n == POSTINCR) ? 1 : -1;
if (n == PREINCR || n == PREDECR) {
setfval(x, xf + k);
return(x);
}
z = gettemp();
setfval(z, xf);
setfval(x, xf + k);
tempfree(x);
return(z);
}
Cell *assign(Node **a, int n)
{
Cell *x, *y;
Awkfloat xf, yf;
double v;
y = execute(a[1]);
x = execute(a[0]);
if (n == ASSIGN) {
if (x == y && !(x->tval & (FLD|REC)) && x != nfloc)
;
else if ((y->tval & (STR|NUM)) == (STR|NUM)) {
yf = getfval(y);
setsval(x, getsval(y));
x->fval = yf;
x->tval |= NUM;
}
else if (isstr(y))
setsval(x, getsval(y));
else if (isnum(y))
setfval(x, getfval(y));
else
funnyvar(y, "read value of");
tempfree(y);
return(x);
}
xf = getfval(x);
yf = getfval(y);
switch (n) {
case ADDEQ:
xf += yf;
break;
case SUBEQ:
xf -= yf;
break;
case MULTEQ:
xf *= yf;
break;
case DIVEQ:
if (yf == 0)
FATAL("division by zero in /=");
xf /= yf;
break;
case MODEQ:
if (yf == 0)
FATAL("division by zero in %%=");
modf(xf/yf, &v);
xf = xf - yf * v;
break;
case POWEQ:
if (yf >= 0 && modf(yf, &v) == 0.0)
xf = ipow(xf, (int) yf);
else {
errno = 0;
xf = errcheck(pow(xf, yf), "pow");
}
break;
default:
FATAL("illegal assignment operator %d", n);
break;
}
tempfree(y);
setfval(x, xf);
return(x);
}
Cell *cat(Node **a, int q)
{
Cell *x, *y, *z;
int n1, n2;
char *s = NULL;
int ssz = 0;
x = execute(a[0]);
n1 = strlen(getsval(x));
adjbuf(&s, &ssz, n1 + 1, recsize, 0, "cat1");
memcpy(s, x->sval, n1);
tempfree(x);
y = execute(a[1]);
n2 = strlen(getsval(y));
adjbuf(&s, &ssz, n1 + n2 + 1, recsize, 0, "cat2");
memcpy(s + n1, y->sval, n2);
s[n1 + n2] = '\0';
tempfree(y);
z = gettemp();
z->sval = s;
z->tval = STR;
return(z);
}
Cell *pastat(Node **a, int n)
{
Cell *x;
if (a[0] == NULL)
x = execute(a[1]);
else {
x = execute(a[0]);
if (istrue(x)) {
tempfree(x);
x = execute(a[1]);
}
}
return x;
}
Cell *dopa2(Node **a, int n)
{
Cell *x;
int pair;
pair = ptoi(a[3]);
if (pairstack[pair] == 0) {
x = execute(a[0]);
if (istrue(x))
pairstack[pair] = 1;
tempfree(x);
}
if (pairstack[pair] == 1) {
x = execute(a[1]);
if (istrue(x))
pairstack[pair] = 0;
tempfree(x);
x = execute(a[2]);
return(x);
}
return(False);
}
Cell *split(Node **a, int nnn)
{
Cell *x = NULL, *y, *ap;
const char *s, *origs, *t;
const char *fs = NULL;
char *origfs = NULL;
int sep;
char temp, num[50];
int j, n, tempstat, arg3type;
double result;
y = execute(a[0]);
origs = s = strdup(getsval(y));
if (s == NULL)
FATAL("out of space in split");
tempfree(y);
arg3type = ptoi(a[3]);
if (a[2] == NULL) {
fs = getsval(fsloc);
} else if (arg3type == STRING) {
x = execute(a[2]);
fs = origfs = strdup(getsval(x));
if (fs == NULL)
FATAL("out of space in split");
tempfree(x);
} else if (arg3type == REGEXPR) {
fs = "(regexpr)";
} else {
FATAL("illegal type of split");
}
sep = *fs;
ap = execute(a[1]);
freesymtab(ap);
DPRINTF("split: s=|%s|, a=%s, sep=|%s|\n", s, NN(ap->nval), fs);
ap->tval &= ~STR;
ap->tval |= ARR;
ap->sval = (char *) makesymtab(NSYMTAB);
n = 0;
if (arg3type == REGEXPR && strlen((char*)((fa*)a[2])->restr) == 0) {
arg3type = 0;
fs = "";
sep = 0;
}
if (*s != '\0' && (strlen(fs) > 1 || arg3type == REGEXPR)) {
fa *pfa;
if (arg3type == REGEXPR) {
pfa = (fa *) a[2];
} else {
pfa = makedfa(fs, 1);
}
if (nematch(pfa,s)) {
tempstat = pfa->initstat;
pfa->initstat = 2;
do {
n++;
snprintf(num, sizeof(num), "%d", n);
temp = *patbeg;
setptr(patbeg, '\0');
if (is_number(s, & result))
setsymtab(num, s, result, STR|NUM, (Array *) ap->sval);
else
setsymtab(num, s, 0.0, STR, (Array *) ap->sval);
setptr(patbeg, temp);
s = patbeg + patlen;
if (*(patbeg+patlen-1) == '\0' || *s == '\0') {
n++;
snprintf(num, sizeof(num), "%d", n);
setsymtab(num, "", 0.0, STR, (Array *) ap->sval);
pfa->initstat = tempstat;
goto spdone;
}
} while (nematch(pfa,s));
pfa->initstat = tempstat;
}
n++;
snprintf(num, sizeof(num), "%d", n);
if (is_number(s, & result))
setsymtab(num, s, result, STR|NUM, (Array *) ap->sval);
else
setsymtab(num, s, 0.0, STR, (Array *) ap->sval);
spdone:
pfa = NULL;
} else if (a[2] == NULL && CSV) {
char *newt = (char *) malloc(strlen(s));
for (;;) {
char *fr = newt;
n++;
if (*s == '"' ) {
for (s++ ; *s != '\0'; ) {
if (*s == '"' && s[1] != '\0' && s[1] == '"') {
s += 2;
*fr++ = '"';
} else if (*s == '"' && (s[1] == '\0' || s[1] == ',')) {
s++;
break;
} else {
*fr++ = *s++;
}
}
*fr++ = 0;
} else {
while (*s != ',' && *s != '\0')
*fr++ = *s++;
*fr++ = 0;
}
snprintf(num, sizeof(num), "%d", n);
if (is_number(newt, &result))
setsymtab(num, newt, result, STR|NUM, (Array *) ap->sval);
else
setsymtab(num, newt, 0.0, STR, (Array *) ap->sval);
if (*s++ == '\0')
break;
}
free(newt);
} else if (!CSV && sep == ' ') {
for (n = 0; ; ) {
#define ISWS(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
while (ISWS(*s))
s++;
if (*s == '\0')
break;
n++;
t = s;
do
s++;
while (*s != '\0' && !ISWS(*s));
temp = *s;
setptr(s, '\0');
snprintf(num, sizeof(num), "%d", n);
if (is_number(t, & result))
setsymtab(num, t, result, STR|NUM, (Array *) ap->sval);
else
setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
setptr(s, temp);
if (*s != '\0')
s++;
}
} else if (sep == 0) {
for (n = 0; *s != '\0'; s += u8_nextlen(s)) {
char buf[10];
n++;
snprintf(num, sizeof(num), "%d", n);
for (j = 0; j < u8_nextlen(s); j++) {
buf[j] = s[j];
}
buf[j] = '\0';
if (isdigit((uschar)buf[0]))
setsymtab(num, buf, atof(buf), STR|NUM, (Array *) ap->sval);
else
setsymtab(num, buf, 0.0, STR, (Array *) ap->sval);
}
} else if (*s != '\0') {
for (;;) {
n++;
t = s;
while (*s != sep && *s != '\0')
s++;
temp = *s;
setptr(s, '\0');
snprintf(num, sizeof(num), "%d", n);
if (is_number(t, & result))
setsymtab(num, t, result, STR|NUM, (Array *) ap->sval);
else
setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
setptr(s, temp);
if (*s++ == '\0')
break;
}
}
tempfree(ap);
xfree(origs);
xfree(origfs);
x = gettemp();
x->tval = NUM;
x->fval = n;
return(x);
}
Cell *condexpr(Node **a, int n)
{
Cell *x;
x = execute(a[0]);
if (istrue(x)) {
tempfree(x);
x = execute(a[1]);
} else {
tempfree(x);
x = execute(a[2]);
}
return(x);
}
Cell *ifstat(Node **a, int n)
{
Cell *x;
x = execute(a[0]);
if (istrue(x)) {
tempfree(x);
x = execute(a[1]);
} else if (a[2] != NULL) {
tempfree(x);
x = execute(a[2]);
}
return(x);
}
Cell *whilestat(Node **a, int n)
{
Cell *x;
for (;;) {
x = execute(a[0]);
if (!istrue(x))
return(x);
tempfree(x);
x = execute(a[1]);
if (isbreak(x)) {
x = True;
return(x);
}
if (isnext(x) || isexit(x) || isret(x))
return(x);
tempfree(x);
}
}
Cell *dostat(Node **a, int n)
{
Cell *x;
for (;;) {
x = execute(a[0]);
if (isbreak(x))
return True;
if (isnext(x) || isexit(x) || isret(x))
return(x);
tempfree(x);
x = execute(a[1]);
if (!istrue(x))
return(x);
tempfree(x);
}
}
Cell *forstat(Node **a, int n)
{
Cell *x;
x = execute(a[0]);
tempfree(x);
for (;;) {
if (a[1]!=NULL) {
x = execute(a[1]);
if (!istrue(x)) return(x);
else tempfree(x);
}
x = execute(a[3]);
if (isbreak(x))
return True;
if (isnext(x) || isexit(x) || isret(x))
return(x);
tempfree(x);
x = execute(a[2]);
tempfree(x);
}
}
Cell *instat(Node **a, int n)
{
Cell *x, *vp, *arrayp, *cp, *ncp;
Array *tp;
int i;
vp = execute(a[0]);
arrayp = execute(a[1]);
if (!isarr(arrayp)) {
return True;
}
tp = (Array *) arrayp->sval;
tempfree(arrayp);
for (i = 0; i < tp->size; i++) {
for (cp = tp->tab[i]; cp != NULL; cp = ncp) {
setsval(vp, cp->nval);
ncp = cp->cnext;
x = execute(a[2]);
if (isbreak(x)) {
tempfree(vp);
return True;
}
if (isnext(x) || isexit(x) || isret(x)) {
tempfree(vp);
return(x);
}
tempfree(x);
}
}
return True;
}
static char *nawk_convert(const char *s, int (*fun_c)(int),
wint_t (*fun_wc)(wint_t))
{
char *buf = NULL;
char *pbuf = NULL;
const char *ps = NULL;
size_t n = 0;
wchar_t wc;
const size_t sz = awk_mb_cur_max;
if (sz == 1) {
buf = tostring(s);
for (pbuf = buf; *pbuf; pbuf++)
*pbuf = fun_c((uschar)*pbuf);
return buf;
} else {
buf = tostringN(s, strlen(s) * sz + 1);
if (mbtowc(NULL, NULL, 0) == -1 || wctomb(NULL, L'\0') == -1)
FATAL("unable to reset character conversion state");
ps = s;
pbuf = buf;
while (n = mbtowc(&wc, ps, sz),
n > 0 && n != (size_t)-1 && n != (size_t)-2)
{
ps += n;
n = wctomb(pbuf, fun_wc(wc));
if (n == (size_t)-1)
FATAL("illegal wide character %s", s);
pbuf += n;
}
*pbuf = '\0';
if (n)
FATAL("illegal byte sequence %s", s);
return buf;
}
}
#ifdef __DJGPP__
static wint_t towupper(wint_t wc)
{
if (wc >= 0 && wc < 256)
return toupper(wc & 0xFF);
return wc;
}
static wint_t towlower(wint_t wc)
{
if (wc >= 0 && wc < 256)
return tolower(wc & 0xFF);
return wc;
}
#endif
static char *nawk_toupper(const char *s)
{
return nawk_convert(s, toupper, towupper);
}
static char *nawk_tolower(const char *s)
{
return nawk_convert(s, tolower, towlower);
}
Cell *bltin(Node **a, int n)
{
Cell *x, *y;
Awkfloat u = 0;
int t, sz;
Awkfloat tmp;
char *buf, *fmt;
Node *nextarg;
FILE *fp;
int status = 0;
time_t tv;
struct tm *tm, tmbuf;
int estatus = 0;
t = ptoi(a[0]);
x = execute(a[1]);
nextarg = a[1]->nnext;
switch (t) {
case FLENGTH:
if (isarr(x))
u = ((Array *) x->sval)->nelem;
else
u = u8_strlen(getsval(x));
break;
case FLOG:
errno = 0;
u = errcheck(log(getfval(x)), "log");
break;
case FINT:
modf(getfval(x), &u); break;
case FEXP:
errno = 0;
u = errcheck(exp(getfval(x)), "exp");
break;
case FSQRT:
errno = 0;
u = errcheck(sqrt(getfval(x)), "sqrt");
break;
case FSIN:
u = sin(getfval(x)); break;
case FCOS:
u = cos(getfval(x)); break;
case FATAN:
if (nextarg == NULL) {
WARNING("atan2 requires two arguments; returning 1.0");
u = 1.0;
} else {
y = execute(a[1]->nnext);
u = atan2(getfval(x), getfval(y));
tempfree(y);
nextarg = nextarg->nnext;
}
break;
case FCOMPL:
u = ~((int)getfval(x));
break;
case FAND:
if (nextarg == 0) {
WARNING("and requires two arguments; returning 0");
u = 0;
break;
}
y = execute(a[1]->nnext);
u = ((int)getfval(x)) & ((int)getfval(y));
tempfree(y);
nextarg = nextarg->nnext;
break;
case FFOR:
if (nextarg == 0) {
WARNING("or requires two arguments; returning 0");
u = 0;
break;
}
y = execute(a[1]->nnext);
u = ((int)getfval(x)) | ((int)getfval(y));
tempfree(y);
nextarg = nextarg->nnext;
break;
case FXOR:
if (nextarg == 0) {
WARNING("xor requires two arguments; returning 0");
u = 0;
break;
}
y = execute(a[1]->nnext);
u = ((int)getfval(x)) ^ ((int)getfval(y));
tempfree(y);
nextarg = nextarg->nnext;
break;
case FLSHIFT:
if (nextarg == 0) {
WARNING("lshift requires two arguments; returning 0");
u = 0;
break;
}
y = execute(a[1]->nnext);
u = ((int)getfval(x)) << ((int)getfval(y));
tempfree(y);
nextarg = nextarg->nnext;
break;
case FRSHIFT:
if (nextarg == 0) {
WARNING("rshift requires two arguments; returning 0");
u = 0;
break;
}
y = execute(a[1]->nnext);
u = ((int)getfval(x)) >> ((int)getfval(y));
tempfree(y);
nextarg = nextarg->nnext;
break;
case FSYSTEM:
fflush(stdout);
estatus = status = system(getsval(x));
if (status != -1) {
if (WIFEXITED(status)) {
estatus = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
estatus = WTERMSIG(status) + 256;
#ifdef WCOREDUMP
if (WCOREDUMP(status))
estatus += 256;
#endif
} else
estatus = 0;
}
u = estatus;
break;
case FRAND:
u = (Awkfloat) random() / (0x7fffffffL + 0x1UL);
break;
case FSRAND:
if (isrec(x)) {
u = time(NULL);
tmp = u;
srandom((unsigned int) u);
} else {
u = getfval(x);
tmp = u;
srandom_deterministic((unsigned int) u);
}
u = srand_seed;
srand_seed = tmp;
break;
case FTOUPPER:
case FTOLOWER:
if (t == FTOUPPER)
buf = nawk_toupper(getsval(x));
else
buf = nawk_tolower(getsval(x));
tempfree(x);
x = gettemp();
setsval(x, buf);
free(buf);
return x;
case FFLUSH:
if (isrec(x) || strlen(getsval(x)) == 0) {
flush_all();
u = 0;
} else if ((fp = openfile(FFLUSH, getsval(x), NULL)) == NULL)
u = EOF;
else
u = fflush(fp);
break;
case FMKTIME:
memset(&tmbuf, 0, sizeof(tmbuf));
tm = &tmbuf;
t = sscanf(getsval(x), "%d %d %d %d %d %d %d",
&tm->tm_year, &tm->tm_mon, &tm->tm_mday, &tm->tm_hour,
&tm->tm_min, &tm->tm_sec, &tm->tm_isdst);
switch (t) {
case 6:
tm->tm_isdst = -1;
case 7:
tm->tm_year -= 1900;
tm->tm_mon--;
u = mktime(tm);
break;
default:
u = -1;
break;
}
break;
case FSYSTIME:
u = time((time_t *) 0);
break;
case FSTRFTIME:
if (nextarg) {
y = execute(nextarg);
nextarg = nextarg->nnext;
tv = (time_t) getfval(y);
tempfree(y);
} else
tv = time((time_t *) 0);
tm = localtime(&tv);
if (tm == NULL)
FATAL("bad time %ld", (long)tv);
if (isrec(x)) {
fmt = tostring("%a %b %d %H:%M:%S %Z %Y");
} else
fmt = tostring(getsval(x));
sz = 32;
buf = NULL;
do {
if ((buf = (char *) reallocarray(buf, 2, sz)) == NULL)
FATAL("out of memory in strftime");
sz *= 2;
} while (strftime(buf, sz, fmt, tm) == 0 && fmt[0] != '\0');
y = gettemp();
setsval(y, buf);
free(fmt);
free(buf);
return y;
default:
FATAL("illegal function type %d", t);
break;
}
tempfree(x);
x = gettemp();
setfval(x, u);
if (nextarg != NULL) {
WARNING("warning: function has too many arguments");
for ( ; nextarg; nextarg = nextarg->nnext) {
y = execute(nextarg);
tempfree(y);
}
}
return(x);
}
Cell *printstat(Node **a, int n)
{
Node *x;
Cell *y;
FILE *fp;
if (a[1] == NULL)
fp = stdout;
else
fp = redirect(ptoi(a[1]), a[2]);
for (x = a[0]; x != NULL; x = x->nnext) {
y = execute(x);
fputs(getpssval(y), fp);
tempfree(y);
if (x->nnext == NULL)
fputs(getsval(orsloc), fp);
else
fputs(getsval(ofsloc), fp);
}
if (a[1] != NULL)
fflush(fp);
if (ferror(fp))
FATAL("write error on %s", filename(fp));
return(True);
}
Cell *nullproc(Node **a, int n)
{
return 0;
}
FILE *redirect(int a, Node *b)
{
FILE *fp;
Cell *x;
char *fname;
x = execute(b);
fname = getsval(x);
fp = openfile(a, fname, NULL);
if (fp == NULL)
FATAL("can't open file %s", fname);
tempfree(x);
return fp;
}
struct files {
FILE *fp;
const char *fname;
int mode;
} *files;
size_t nfiles;
static void stdinit(void)
{
nfiles = FOPEN_MAX;
files = (struct files *) calloc(nfiles, sizeof(*files));
if (files == NULL)
FATAL("can't allocate file memory for %zu files", nfiles);
files[0].fp = stdin;
files[0].fname = tostring("/dev/stdin");
files[0].mode = LT;
files[1].fp = stdout;
files[1].fname = tostring("/dev/stdout");
files[1].mode = GT;
files[2].fp = stderr;
files[2].fname = tostring("/dev/stderr");
files[2].mode = GT;
}
FILE *openfile(int a, const char *us, bool *pnewflag)
{
const char *s = us;
size_t i;
int m;
FILE *fp = NULL;
struct stat sbuf;
if (*s == '\0')
FATAL("null file name in print or getline");
for (i = 0; i < nfiles; i++)
if (files[i].fname && strcmp(s, files[i].fname) == 0 &&
(a == files[i].mode || (a==APPEND && files[i].mode==GT) ||
a == FFLUSH)) {
if (pnewflag)
*pnewflag = false;
return files[i].fp;
}
if (a == FFLUSH)
return NULL;
for (i = 0; i < nfiles; i++)
if (files[i].fp == NULL)
break;
if (i >= nfiles) {
struct files *nf;
size_t nnf = nfiles + FOPEN_MAX;
nf = (struct files *) reallocarray(files, nnf, sizeof(*nf));
if (nf == NULL)
FATAL("cannot grow files for %s and %zu files", s, nnf);
memset(&nf[nfiles], 0, FOPEN_MAX * sizeof(*nf));
nfiles = nnf;
files = nf;
}
fflush(stdout);
if (a == LT || a == GT || a == APPEND)
if (stat(s, &sbuf) == 0 && S_ISDIR(sbuf.st_mode))
return NULL;
m = a;
if (a == GT) {
fp = fopen(s, "w");
} else if (a == APPEND) {
fp = fopen(s, "a");
m = GT;
} else if (a == '|') {
fp = popen(s, "w");
} else if (a == LE) {
fp = popen(s, "r");
} else if (a == LT) {
fp = strcmp(s, "-") == 0 ? stdin : fopen(s, "r");
} else
FATAL("illegal redirection %d", a);
if (fp != NULL) {
files[i].fname = tostring(s);
files[i].fp = fp;
files[i].mode = m;
if (pnewflag)
*pnewflag = true;
if (fp != stdin && fp != stdout && fp != stderr)
(void) fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
}
return fp;
}
const char *filename(FILE *fp)
{
size_t i;
for (i = 0; i < nfiles; i++)
if (fp == files[i].fp)
return files[i].fname;
return "???";
}
Cell *closefile(Node **a, int n)
{
Cell *x;
size_t i;
bool stat;
x = execute(a[0]);
getsval(x);
stat = true;
for (i = 0; i < nfiles; i++) {
if (!files[i].fname || strcmp(x->sval, files[i].fname) != 0)
continue;
if (files[i].mode == GT || files[i].mode == '|')
fflush(files[i].fp);
if (ferror(files[i].fp)) {
if ((files[i].mode == GT && files[i].fp != stderr)
|| files[i].mode == '|')
FATAL("write error on %s", files[i].fname);
else
WARNING("i/o error occurred on %s", files[i].fname);
}
if (files[i].fp == stdin || files[i].fp == stdout ||
files[i].fp == stderr)
stat = freopen("/dev/null", "r+", files[i].fp) == NULL;
else if (files[i].mode == '|' || files[i].mode == LE)
stat = pclose(files[i].fp) == -1;
else
stat = fclose(files[i].fp) == EOF;
if (stat)
WARNING("i/o error occurred closing %s", files[i].fname);
xfree(files[i].fname);
files[i].fname = NULL;
files[i].fp = NULL;
break;
}
tempfree(x);
x = gettemp();
setfval(x, (Awkfloat) (stat ? -1 : 0));
return(x);
}
void closeall(void)
{
size_t i;
bool stat = false;
for (i = 0; i < nfiles; i++) {
if (! files[i].fp)
continue;
if (files[i].mode == GT || files[i].mode == '|')
fflush(files[i].fp);
if (ferror(files[i].fp)) {
if ((files[i].mode == GT && files[i].fp != stderr)
|| files[i].mode == '|')
FATAL("write error on %s", files[i].fname);
else
WARNING("i/o error occurred on %s", files[i].fname);
}
if (files[i].fp == stdin || files[i].fp == stdout ||
files[i].fp == stderr)
continue;
if (files[i].mode == '|' || files[i].mode == LE)
stat = pclose(files[i].fp) == -1;
else
stat = fclose(files[i].fp) == EOF;
if (stat)
WARNING("i/o error occurred while closing %s", files[i].fname);
}
}
static void flush_all(void)
{
size_t i;
for (i = 0; i < nfiles; i++)
if (files[i].fp)
fflush(files[i].fp);
}
void backsub(char **pb_ptr, const char **sptr_ptr);
Cell *dosub(Node **a, int subop)
{
fa *pfa;
int tempstat = 0;
char *repl;
Cell *x;
char *buf = NULL;
char *pb = NULL;
int bufsz = recsize;
const char *r, *s;
const char *start;
const char *noempty = NULL;
size_t m = 0;
size_t whichm = 0;
int mtype;
if (a[0] == NULL) {
pfa = (fa *) a[1];
} else {
x = execute(a[1]);
pfa = makedfa(getsval(x), 1);
tempfree(x);
}
x = execute(a[2]);
repl = tostring(getsval(x));
tempfree(x);
switch (subop) {
case SUB:
whichm = 1;
x = execute(a[3]);
break;
case GSUB:
whichm = 0;
x = execute(a[3]);
break;
default:
FATAL("dosub: unrecognized subop: %d", subop);
}
start = getsval(x);
while (pmatch(pfa, start)) {
if (buf == NULL) {
if ((pb = buf = (char *) malloc(bufsz)) == NULL)
FATAL("out of memory in dosub");
tempstat = pfa->initstat;
pfa->initstat = 2;
}
#define MT_IGNORE 0
#define MT_INSERT 1
#define MT_REPLACE 2
if (patbeg == noempty && patlen == 0) {
mtype = MT_IGNORE;
} else if (whichm == ++m || whichm == 0) {
mtype = patlen ? MT_REPLACE : MT_INSERT;
} else {
mtype = MT_IGNORE;
}
if (patbeg > start) {
adjbuf(&buf, &bufsz, (pb - buf) + (patbeg - start),
recsize, &pb, "dosub");
s = start;
while (s < patbeg)
*pb++ = *s++;
}
if (mtype == MT_IGNORE)
goto matching_text;
r = repl;
while (*r != 0) {
adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "dosub");
if (*r == '\\') {
backsub(&pb, &r);
} else if (*r == '&') {
r++;
adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize,
&pb, "dosub");
for (s = patbeg; s < patbeg+patlen; )
*pb++ = *s++;
} else {
*pb++ = *r++;
}
}
matching_text:
if (mtype == MT_REPLACE || *patbeg == '\0')
goto next_search;
if (patlen == 0)
patlen = u8_nextlen(patbeg);
adjbuf(&buf, &bufsz, (pb-buf) + patlen, recsize, &pb, "dosub");
s = patbeg;
while (s < patbeg + patlen)
*pb++ = *s++;
next_search:
start = patbeg + patlen;
if (m == whichm || *patbeg == '\0')
break;
if (mtype == MT_REPLACE)
noempty = start;
#undef MT_IGNORE
#undef MT_INSERT
#undef MT_REPLACE
}
xfree(repl);
if (buf != NULL) {
pfa->initstat = tempstat;
adjbuf(&buf, &bufsz, 1+strlen(start)+pb-buf, 0, &pb, "dosub");
while ((*pb++ = *start++) != '\0')
;
setsval(x, buf);
free(buf);
}
tempfree(x);
x = gettemp();
x->tval = NUM;
x->fval = m;
return x;
}
Cell *gensub(Node **a, int nnn)
{
Cell *x, *y, *res, *h;
char *rptr;
const char *sptr;
char *buf, *pb;
const char *t, *q;
fa *pfa;
int mflag, tempstat, num, whichm;
int bufsz = recsize;
if ((buf = (char *) malloc(bufsz)) == NULL)
FATAL("out of memory in gensub");
mflag = 0;
num = 0;
x = execute(a[4]);
t = getsval(x);
res = copycell(x);
res->csub = CTEMP;
if (a[0] == 0)
pfa = (fa *) a[1];
else {
y = execute(a[1]);
pfa = makedfa(getsval(y), 1);
tempfree(y);
}
y = execute(a[2]);
h = execute(a[3]);
sptr = getsval(h);
if (sptr[0] == 'g' || sptr[0] == 'G')
whichm = -1;
else {
whichm = (int) getfval(h) - 1;
if (whichm < 0)
whichm = 0;
}
tempfree(h);
if (pmatch(pfa, t)) {
char *sl;
tempstat = pfa->initstat;
pfa->initstat = 2;
pb = buf;
rptr = getsval(y);
for (sl = rptr; (sl = strchr(sl, '\\')) && sl[1]; sl++) {
if (strchr("0123456789", sl[1])) {
FATAL("gensub doesn't support backreferences (subst \"%s\")", rptr);
}
}
do {
if (whichm >= 0 && whichm != num) {
num++;
adjbuf(&buf, &bufsz, (pb - buf) + (patbeg - t) + patlen, recsize, &pb, "gensub");
while (t < patbeg + patlen)
*pb++ = *t++;
continue;
}
if (patlen == 0 && *patbeg != 0) {
if (mflag == 0) {
num++;
sptr = rptr;
while (*sptr != 0) {
adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gensub");
if (*sptr == '\\') {
backsub(&pb, &sptr);
} else if (*sptr == '&') {
sptr++;
adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gensub");
for (q = patbeg; q < patbeg+patlen; )
*pb++ = *q++;
} else
*pb++ = *sptr++;
}
}
if (*t == 0)
goto done;
adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gensub");
*pb++ = *t++;
if (pb > buf + bufsz)
FATAL("gensub result0 %.30s too big; can't happen", buf);
mflag = 0;
}
else {
num++;
sptr = t;
adjbuf(&buf, &bufsz, 1+(patbeg-sptr)+pb-buf, recsize, &pb, "gensub");
while (sptr < patbeg)
*pb++ = *sptr++;
sptr = rptr;
while (*sptr != 0) {
adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gensub");
if (*sptr == '\\') {
backsub(&pb, &sptr);
} else if (*sptr == '&') {
sptr++;
adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gensub");
for (q = patbeg; q < patbeg+patlen; )
*pb++ = *q++;
} else
*pb++ = *sptr++;
}
t = patbeg + patlen;
if (patlen == 0 || *t == 0 || *(t-1) == 0)
goto done;
if (pb > buf + bufsz)
FATAL("gensub result1 %.30s too big; can't happen", buf);
mflag = 1;
}
} while (pmatch(pfa,t));
sptr = t;
adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gensub");
while ((*pb++ = *sptr++) != 0)
;
done: if (pb > buf + bufsz)
FATAL("gensub result2 %.30s too big; can't happen", buf);
*pb = '\0';
setsval(res, buf);
pfa->initstat = tempstat;
}
tempfree(x);
tempfree(y);
free(buf);
return(res);
}
void backsub(char **pb_ptr, const char **sptr_ptr)
{
char *pb = *pb_ptr;
const char *sptr = *sptr_ptr;
if (sptr[1] == '\\') {
if (sptr[2] == '\\' && sptr[3] == '&') {
*pb++ = '\\';
*pb++ = '&';
sptr += 4;
} else if (sptr[2] == '&') {
*pb++ = '\\';
sptr += 2;
} else if (do_posix) {
sptr++;
*pb++ = *sptr++;
} else {
*pb++ = *sptr++;
*pb++ = *sptr++;
}
} else if (sptr[1] == '&') {
sptr++;
*pb++ = *sptr++;
} else
*pb++ = *sptr++;
*pb_ptr = pb;
*sptr_ptr = sptr;
}
static char *wide_char_to_byte_str(int rune, size_t *outlen)
{
static char buf[5];
int len;
if (rune < 0 || rune > 0x10FFFF)
return NULL;
memset(buf, 0, sizeof(buf));
len = 0;
if (rune <= 0x0000007F) {
buf[len++] = rune;
} else if (rune <= 0x000007FF) {
buf[len++] = 0xC0 | (rune >> 6);
buf[len++] = 0x80 | (rune & 0x3F);
} else if (rune <= 0x0000FFFF) {
buf[len++] = 0xE0 | (rune >> 12);
buf[len++] = 0x80 | ((rune >> 6) & 0x3F);
buf[len++] = 0x80 | (rune & 0x3F);
} else {
buf[len++] = 0xF0 | (rune >> 18);
buf[len++] = 0x80 | ((rune >> 12) & 0x3F);
buf[len++] = 0x80 | ((rune >> 6) & 0x3F);
buf[len++] = 0x80 | (rune & 0x3F);
}
*outlen = len;
buf[len++] = '\0';
return buf;
}