version 1.18, 2020/02/27 21:43:46 |
version 1.19, 2020/06/10 21:00:01 |
|
|
|
|
extern Cell **fldtab; |
extern Cell **fldtab; |
|
|
|
static void |
|
setfree(Cell *vp) |
|
{ |
|
if (&vp->sval == FS || &vp->sval == RS || |
|
&vp->sval == OFS || &vp->sval == ORS || |
|
&vp->sval == OFMT || &vp->sval == CONVFMT || |
|
&vp->sval == FILENAME || &vp->sval == SUBSEP) |
|
vp->tval |= DONTFREE; |
|
else |
|
vp->tval &= ~DONTFREE; |
|
} |
|
|
void syminit(void) /* initialize symbol table with builtin vars */ |
void syminit(void) /* initialize symbol table with builtin vars */ |
{ |
{ |
literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab); |
literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab); |
|
|
{ |
{ |
int fldno; |
int fldno; |
|
|
|
f += 0.0; /* normalise negative zero to positive zero */ |
if ((vp->tval & (NUM | STR)) == 0) |
if ((vp->tval & (NUM | STR)) == 0) |
funnyvar(vp, "assign to"); |
funnyvar(vp, "assign to"); |
if (isfld(vp)) { |
if (isfld(vp)) { |
|
|
if (fldno > *NF) |
if (fldno > *NF) |
newfld(fldno); |
newfld(fldno); |
DPRINTF( ("setting field %d to %g\n", fldno, f) ); |
DPRINTF( ("setting field %d to %g\n", fldno, f) ); |
|
} else if (&vp->fval == NF) { |
|
donerec = 0; /* mark $0 invalid */ |
|
setlastfld(f); |
|
DPRINTF( ("setting NF to %g\n", f) ); |
} else if (isrec(vp)) { |
} else if (isrec(vp)) { |
donefld = 0; /* mark $1... invalid */ |
donefld = 0; /* mark $1... invalid */ |
donerec = 1; |
donerec = 1; |
} |
} |
if (freeable(vp)) |
if (freeable(vp)) |
xfree(vp->sval); /* free any previous string */ |
xfree(vp->sval); /* free any previous string */ |
vp->tval &= ~STR; /* mark string invalid */ |
vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */ |
|
vp->fmt = NULL; |
vp->tval |= NUM; /* mark number ok */ |
vp->tval |= NUM; /* mark number ok */ |
if (f == -0) /* who would have thought this possible? */ |
if (f == -0) /* who would have thought this possible? */ |
f = 0; |
f = 0; |
|
|
{ |
{ |
char *t; |
char *t; |
int fldno; |
int fldno; |
|
Awkfloat f; |
|
|
DPRINTF( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n", |
DPRINTF( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n", |
(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) ); |
(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) ); |
|
|
} else if (isrec(vp)) { |
} else if (isrec(vp)) { |
donefld = 0; /* mark $1... invalid */ |
donefld = 0; /* mark $1... invalid */ |
donerec = 1; |
donerec = 1; |
|
} else if (&vp->sval == OFS) { |
|
if (donerec == 0) |
|
recbld(); |
} |
} |
t = tostring(s); /* in case it's self-assign */ |
t = s ? tostring(s) : tostring(""); /* in case it's self-assign */ |
if (freeable(vp)) |
if (freeable(vp)) |
xfree(vp->sval); |
xfree(vp->sval); |
vp->tval &= ~NUM; |
vp->tval &= ~(NUM|CONVC|CONVO); |
vp->tval |= STR; |
vp->tval |= STR; |
vp->tval &= ~DONTFREE; |
vp->fmt = NULL; |
|
setfree(vp); |
DPRINTF( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n", |
DPRINTF( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n", |
(void*)vp, NN(vp->nval), t,t, vp->tval, donerec, donefld) ); |
(void*)vp, NN(vp->nval), t,t, vp->tval, donerec, donefld) ); |
return(vp->sval = t); |
vp->sval = t; |
|
if (&vp->fval == NF) { |
|
donerec = 0; /* mark $0 invalid */ |
|
f = getfval(vp); |
|
setlastfld(f); |
|
DPRINTF( ("setting NF to %g\n", f) ); |
|
} |
|
|
|
return(vp->sval); |
} |
} |
|
|
Awkfloat getfval(Cell *vp) /* get float val of a Cell */ |
Awkfloat getfval(Cell *vp) /* get float val of a Cell */ |
|
|
fldbld(); |
fldbld(); |
else if (isrec(vp) && donerec == 0) |
else if (isrec(vp) && donerec == 0) |
recbld(); |
recbld(); |
|
|
|
/* |
|
* ADR: This is complicated and more fragile than is desirable. |
|
* Retrieving a string value for a number associates the string |
|
* value with the scalar. Previously, the string value was |
|
* sticky, meaning if converted via OFMT that became the value |
|
* (even though POSIX wants it to be via CONVFMT). Or if CONVFMT |
|
* changed after a string value was retrieved, the original value |
|
* was maintained and used. Also not per POSIX. |
|
* |
|
* We work around this design by adding two additional flags, |
|
* CONVC and CONVO, indicating how the string value was |
|
* obtained (via CONVFMT or OFMT) and _also_ maintaining a copy |
|
* of the pointer to the xFMT format string used for the |
|
* conversion. This pointer is only read, **never** dereferenced. |
|
* The next time we do a conversion, if it's coming from the same |
|
* xFMT as last time, and the pointer value is different, we |
|
* know that the xFMT format string changed, and we need to |
|
* redo the conversion. If it's the same, we don't have to. |
|
* |
|
* There are also several cases where we don't do a conversion, |
|
* such as for a field (see the checks below). |
|
*/ |
|
|
|
/* Don't duplicate the code for actually updating the value */ |
|
#define update_str_val(vp) \ |
|
{ \ |
|
if (freeable(vp)) \ |
|
xfree(vp->sval); \ |
|
if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \ |
|
n = asprintf(&vp->sval, "%.30g", vp->fval); \ |
|
else \ |
|
n = asprintf(&vp->sval, *fmt, vp->fval); \ |
|
if (n == -1) \ |
|
FATAL("out of space in get_str_val"); \ |
|
vp->tval &= ~DONTFREE; \ |
|
vp->tval |= STR; \ |
|
} |
|
|
if (isstr(vp) == 0) { |
if (isstr(vp) == 0) { |
if (freeable(vp)) |
update_str_val(vp); |
xfree(vp->sval); |
if (fmt == OFMT) { |
if (modf(vp->fval, &dtemp) == 0) /* it's integral */ |
vp->tval &= ~CONVC; |
n = asprintf(&vp->sval, "%.30g", vp->fval); |
vp->tval |= CONVO; |
else |
} else { |
n = asprintf(&vp->sval, *fmt, vp->fval); |
/* CONVFMT */ |
if (n == -1) |
vp->tval &= ~CONVO; |
FATAL("out of space in get_str_val"); |
vp->tval |= CONVC; |
vp->tval &= ~DONTFREE; |
} |
vp->tval |= STR; |
vp->fmt = *fmt; |
|
} else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) { |
|
goto done; |
|
} else if (isstr(vp)) { |
|
if (fmt == OFMT) { |
|
if ((vp->tval & CONVC) != 0 |
|
|| ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) { |
|
update_str_val(vp); |
|
vp->tval &= ~CONVC; |
|
vp->tval |= CONVO; |
|
vp->fmt = *fmt; |
|
} |
|
} else { |
|
/* CONVFMT */ |
|
if ((vp->tval & CONVO) != 0 |
|
|| ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) { |
|
update_str_val(vp); |
|
vp->tval &= ~CONVO; |
|
vp->tval |= CONVC; |
|
vp->fmt = *fmt; |
|
} |
|
} |
} |
} |
|
done: |
DPRINTF( ("getsval %p: %s = \"%s (%p)\", t=%o\n", |
DPRINTF( ("getsval %p: %s = \"%s (%p)\", t=%o\n", |
(void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) ); |
(void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) ); |
return(vp->sval); |
return(vp->sval); |
|
|
} |
} |
*bp++ = 0; |
*bp++ = 0; |
return (char *) buf; |
return (char *) buf; |
|
} |
|
|
|
const char *flags2str(int flags) |
|
{ |
|
static const struct ftab { |
|
const char *name; |
|
int value; |
|
} flagtab[] = { |
|
{ "NUM", NUM }, |
|
{ "STR", STR }, |
|
{ "DONTFREE", DONTFREE }, |
|
{ "CON", CON }, |
|
{ "ARR", ARR }, |
|
{ "FCN", FCN }, |
|
{ "FLD", FLD }, |
|
{ "REC", REC }, |
|
{ "CONVC", CONVC }, |
|
{ "CONVO", CONVO }, |
|
{ NULL, 0 } |
|
}; |
|
static char buf[100]; |
|
int i, len; |
|
char *cp = buf; |
|
|
|
for (i = 0; flagtab[i].name != NULL; i++) { |
|
if ((flags & flagtab[i].value) != 0) { |
|
len = snprintf(cp, sizeof(buf) - (cp - buf), |
|
"%s%s", cp > buf ? "|" : "", flagtab[i].name); |
|
if (len < 0 || len >= sizeof(buf) - (cp - buf)) |
|
FATAL("out of space in flags2str"); |
|
cp += len; |
|
} |
|
} |
|
|
|
return buf; |
} |
} |