summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.bin/awk/FIXES39
-rw-r--r--usr.bin/awk/awk.141
-rw-r--r--usr.bin/awk/awk.h7
-rw-r--r--usr.bin/awk/awkgram.y6
-rw-r--r--usr.bin/awk/b.c4
-rw-r--r--usr.bin/awk/lib.c16
-rw-r--r--usr.bin/awk/main.c4
-rw-r--r--usr.bin/awk/maketab.c3
-rw-r--r--usr.bin/awk/proto.h5
-rw-r--r--usr.bin/awk/run.c53
-rw-r--r--usr.bin/awk/tran.c159
11 files changed, 293 insertions, 44 deletions
diff --git a/usr.bin/awk/FIXES b/usr.bin/awk/FIXES
index 3aad2c78334..f355591ac2b 100644
--- a/usr.bin/awk/FIXES
+++ b/usr.bin/awk/FIXES
@@ -1,4 +1,4 @@
-/* $OpenBSD: FIXES,v 1.18 2020/06/10 20:59:06 millert Exp $ */
+/* $OpenBSD: FIXES,v 1.19 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -26,6 +26,41 @@ THIS SOFTWARE.
This file lists all bug fixes, changes, etc., made since the AWK book
was sent to the printers in August, 1987.
+Aug 23, 2018:
+ A long list of fixes courtesy of Arnold Robbins,
+ to whom profound thanks.
+
+ 1. ofs-rebuild: OFS value used to rebuild the record was incorrect.
+ Fixed August 19, 2014. Revised fix August 2018.
+
+ 2. system-status: Instead of a floating-point division by 256, use
+ the wait(2) macros to create a reasonable exit status.
+ Fixed March 12, 2016.
+
+ 3. space: Use provided xisblank() function instead of ispace() for
+ matching [[:blank:]].
+
+ 4. a-format: Add POSIX standard %a and %A to supported formats. Check
+ at runtime that this format is available.
+
+ 5. decr-NF: Decrementing NF did not change $0. This is a decades-old
+ bug. There are interactions with the old and new value of OFS as well.
+ Most of the fix came from the NetBSD awk.
+
+ 6. string-conv: String conversions of scalars were sticky. Once a
+ conversion to string happened, even with OFMT, that value was used until
+ a new numeric value was assigned, even if OFMT differed from CONVFMT,
+ and also if CONVFMT changed.
+
+ 7. unary-plus: Unary plus on a string constant returned the string.
+ Instead, it should convert the value to numeric and give that value.
+
+ Also added Arnold's tests for these to awktest.tar as T.arnold.
+
+Aug 15, 2018:
+ fixed mangled awktest.tar (thanks, Arnold), posted all
+ current (very minor) fixes to github / onetrueawk
+
Jun 7, 2018:
(yes, a long layoff)
Updated some broken tests (beebe.tar, T.lilly)
@@ -511,6 +546,8 @@ May 12, 1998:
Mar 12, 1998:
added -V to print version number and die.
+[notify dave kerns, dkerns@dacsoup.ih.lucent.com]
+
Feb 11, 1998:
subtle silent bug in lex.c: if the program ended with a number
longer than 1 digit, part of the input would be pushed back and
diff --git a/usr.bin/awk/awk.1 b/usr.bin/awk/awk.1
index 8e473f86149..7c0fda130be 100644
--- a/usr.bin/awk/awk.1
+++ b/usr.bin/awk/awk.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: awk.1,v 1.46 2020/01/22 03:47:38 deraadt Exp $
+.\" $OpenBSD: awk.1,v 1.47 2020/06/10 21:00:01 millert Exp $
.\"
.\" Copyright (C) Lucent Technologies 1997
.\" All Rights Reserved
@@ -22,7 +22,7 @@
.\" ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
.\" THIS SOFTWARE.
.\"
-.Dd $Mdocdate: January 22 2020 $
+.Dd $Mdocdate: June 10 2020 $
.Dt AWK 1
.Os
.Sh NAME
@@ -162,7 +162,7 @@ as the field separator, use the
option with a value of
.Sq [t] .
.Pp
-A pattern-action statement has the form
+A pattern-action statement has the form:
.Pp
.D1 Ar pattern Ic \&{ Ar action Ic \&}
.Pp
@@ -288,9 +288,9 @@ The
.Ic print
statement prints its arguments on the standard output
(or on a file if
-.Pf > Ar file
+.Pf >\ \& Ar file
or
-.Pf >> Ar file
+.Pf >>\ \& Ar file
is present or on a pipe if
.Pf |\ \& Ar cmd
is present), separated by the current output field separator,
@@ -303,7 +303,8 @@ identical string values in different statements denote
the same open file.
The
.Ic printf
-statement formats its expression list according to the format
+statement formats its expression list according to the
+.Ar format
(see
.Xr printf 1 ) .
.Pp
@@ -373,6 +374,9 @@ may be used to capture control after processing is finished.
and
.Ic END
do not combine with other patterns.
+They may appear multiple times in a program and execute
+in the order they are read by
+.Nm .
.Pp
Variable names with special meanings:
.Pp
@@ -502,7 +506,8 @@ occurs, or 0 if it does not.
The length of
.Fa s
taken as a string,
-or of
+number of elements in an array for an array argument,
+or length of
.Va $0
if no argument is given.
.It Fn match s r
@@ -658,7 +663,7 @@ and
returns 1 for a successful input, 0 for end of file, and \-1 for an error.
.It Xo
.Ic getline Op Va var
-.Pf \ \&< Ar file
+.Pf <\ \& Ar file
.Xc
Sets
.Va $0
@@ -687,6 +692,19 @@ remains open until explicitly closed with a call to
Executes
.Fa cmd
and returns its exit status.
+This will be \-1 upon error,
+.Ar cmd Ns 's
+exit status upon a normal exit,
+256 +
+.Em sig
+if
+.Fa cmd
+was terminated by a signal, where
+.Em sig
+is the number of the signal,
+or 512 +
+.Em sig
+if there was a core dump.
.El
.Ss Bit-Operation Functions
.Bl -tag -width "lshift(a, b)"
@@ -718,7 +736,7 @@ Print first two fields in opposite order:
.Pp
.Dl { print $2, $1 }
.Pp
-Same, with input fields separated by comma and/or blanks and tabs:
+Same, with input fields separated by comma and/or spaces and tabs:
.Bd -literal -offset indent
BEGIN { FS = ",[ \et]*|[ \et]+" }
{ print $2, $1 }
@@ -749,6 +767,7 @@ Print an error message to standard error:
.Ed
.Sh SEE ALSO
.Xr cut 1 ,
+.Xr grep 1 ,
.Xr lex 1 ,
.Xr printf 1 ,
.Xr sed 1 ,
@@ -795,3 +814,7 @@ to it.
.Pp
The scope rules for variables in functions are a botch;
the syntax is worse.
+.Pp
+POSIX-standard interval expressions in regular expressions are not supported.
+.Pp
+Only eight-bit character sets are handled correctly.
diff --git a/usr.bin/awk/awk.h b/usr.bin/awk/awk.h
index 4f535f64017..2eda23b4fa4 100644
--- a/usr.bin/awk/awk.h
+++ b/usr.bin/awk/awk.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: awk.h,v 1.14 2017/10/09 14:51:31 deraadt Exp $ */
+/* $OpenBSD: awk.h,v 1.15 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -82,7 +82,8 @@ typedef struct Cell {
char *nval; /* name, for variables only */
char *sval; /* string value */
Awkfloat fval; /* value as number */
- int tval; /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE */
+ int tval; /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE|CONVC|CONVO */
+ char *fmt; /* CONVFMT/OFMT value used to convert from number */
struct Cell *cnext; /* ptr to next if chained */
} Cell;
@@ -110,6 +111,8 @@ extern Cell *rlengthloc; /* RLENGTH */
#define FCN 040 /* this is a function name */
#define FLD 0100 /* this is a field $1, $2, ... */
#define REC 0200 /* this is $0 */
+#define CONVC 0400 /* string was converted from number via CONVFMT */
+#define CONVO 01000 /* string was converted from number via OFMT */
/* function types */
diff --git a/usr.bin/awk/awkgram.y b/usr.bin/awk/awkgram.y
index 4eb03106dce..24e110bc9ae 100644
--- a/usr.bin/awk/awkgram.y
+++ b/usr.bin/awk/awkgram.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: awkgram.y,v 1.9 2011/09/28 19:27:18 millert Exp $ */
+/* $OpenBSD: awkgram.y,v 1.10 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -87,7 +87,7 @@ Node *arglist = 0; /* list of args for current function */
%left CAT
%left '+' '-'
%left '*' '/' '%'
-%left NOT UMINUS
+%left NOT UMINUS UPLUS
%right POWER
%right DECR INCR
%left INDIRECT
@@ -358,7 +358,7 @@ term:
| term '%' term { $$ = op2(MOD, $1, $3); }
| term POWER term { $$ = op2(POWER, $1, $3); }
| '-' term %prec UMINUS { $$ = op1(UMINUS, $2); }
- | '+' term %prec UMINUS { $$ = $2; }
+ | '+' term %prec UMINUS { $$ = op1(UPLUS, $2); }
| NOT term %prec UMINUS { $$ = op1(NOT, notnull($2)); }
| BLTIN '(' ')' { $$ = op2(BLTIN, itonp($1), rectonode()); }
| BLTIN '(' patlist ')' { $$ = op2(BLTIN, itonp($1), $3); }
diff --git a/usr.bin/awk/b.c b/usr.bin/awk/b.c
index 5091823bb01..8f9bc16b12b 100644
--- a/usr.bin/awk/b.c
+++ b/usr.bin/awk/b.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: b.c,v 1.20 2018/01/24 16:28:25 millert Exp $ */
+/* $OpenBSD: b.c,v 1.21 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -757,7 +757,7 @@ struct charclass {
{ "alnum", 5, isalnum },
{ "alpha", 5, isalpha },
#ifndef HAS_ISBLANK
- { "blank", 5, isspace }, /* was isblank */
+ { "blank", 5, xisblank },
#else
{ "blank", 5, isblank },
#endif
diff --git a/usr.bin/awk/lib.c b/usr.bin/awk/lib.c
index f1ff64731ca..bd51ca41b62 100644
--- a/usr.bin/awk/lib.c
+++ b/usr.bin/awk/lib.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lib.c,v 1.25 2017/12/08 17:04:15 deraadt Exp $ */
+/* $OpenBSD: lib.c,v 1.26 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -359,6 +359,7 @@ void fldbld(void) /* create fields from current record */
}
}
setfval(nfloc, (Awkfloat) lastfld);
+ donerec = 1; /* restore */
if (dbg) {
for (j = 0; j <= lastfld; j++) {
p = fldtab[j];
@@ -390,6 +391,19 @@ void newfld(int n) /* add field n after end of existing lastfld */
setfval(nfloc, (Awkfloat) n);
}
+void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
+{
+ if (n > nfields)
+ growfldtab(n);
+
+ if (lastfld < n)
+ cleanfld(lastfld+1, n);
+ else
+ cleanfld(n+1, lastfld);
+
+ lastfld = n;
+}
+
Cell *fieldadr(int n) /* get nth field */
{
if (n < 0)
diff --git a/usr.bin/awk/main.c b/usr.bin/awk/main.c
index 8d2db5c309a..4f61c3960f9 100644
--- a/usr.bin/awk/main.c
+++ b/usr.bin/awk/main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: main.c,v 1.23 2020/06/10 20:59:06 millert Exp $ */
+/* $OpenBSD: main.c,v 1.24 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -23,7 +23,7 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
-const char *version = "version 20130105";
+const char *version = "version 20180823";
#define DEBUG
#include <stdio.h>
diff --git a/usr.bin/awk/maketab.c b/usr.bin/awk/maketab.c
index 84aef727031..071458bfd95 100644
--- a/usr.bin/awk/maketab.c
+++ b/usr.bin/awk/maketab.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: maketab.c,v 1.11 2010/06/13 17:58:19 millert Exp $ */
+/* $OpenBSD: maketab.c,v 1.12 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -63,6 +63,7 @@ struct xx
{ DIVIDE, "arith", " / " },
{ MOD, "arith", " % " },
{ UMINUS, "arith", " -" },
+ { UPLUS, "arith", " +" },
{ POWER, "arith", " **" },
{ PREINCR, "incrdecr", "++" },
{ POSTINCR, "incrdecr", "++" },
diff --git a/usr.bin/awk/proto.h b/usr.bin/awk/proto.h
index 7c084922e77..326fddf0c87 100644
--- a/usr.bin/awk/proto.h
+++ b/usr.bin/awk/proto.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: proto.h,v 1.11 2020/02/27 21:43:46 millert Exp $ */
+/* $OpenBSD: proto.h,v 1.12 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -125,6 +125,7 @@ extern void setclvar(char *);
extern void fldbld(void);
extern void cleanfld(int, int);
extern void newfld(int);
+extern void setlastfld(int);
extern int refldbld(const char *, const char *);
extern void recbld(void);
extern Cell *fieldadr(int);
@@ -194,3 +195,5 @@ extern Cell *gsub(Node **, int);
extern FILE *popen(const char *, const char *);
extern int pclose(FILE *);
+
+extern const char *flags2str(int flags);
diff --git a/usr.bin/awk/run.c b/usr.bin/awk/run.c
index 416ca609289..a71dba3e0fd 100644
--- a/usr.bin/awk/run.c
+++ b/usr.bin/awk/run.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: run.c,v 1.46 2020/06/10 20:59:06 millert Exp $ */
+/* $OpenBSD: run.c,v 1.47 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -33,6 +33,8 @@ THIS SOFTWARE.
#include <string.h>
#include <stdlib.h>
#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include "awk.h"
#include "ytab.h"
@@ -326,14 +328,18 @@ Cell *copycell(Cell *x) /* make a copy of a cell in a temp */
{
Cell *y;
+ /* copy is not constant or field */
+
y = gettemp();
+ y->tval = x->tval & ~(CON|FLD|REC);
y->csub = CCOPY; /* prevents freeing until call is over */
y->nval = x->nval; /* BUG? */
- if (isstr(x))
+ if (isstr(x) /* || x->ctype == OCELL */) {
y->sval = tostring(x->sval);
+ y->tval &= ~DONTFREE;
+ } else
+ y->tval |= DONTFREE;
y->fval = x->fval;
- y->tval = x->tval & ~(CON|FLD|REC|DONTFREE); /* copy is not constant or field */
- /* is DONTFREE right? */
return y;
}
@@ -820,6 +826,17 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like co
char *buf = *pbuf;
int bufsize = *pbufsize;
+ static int first = 1;
+ static int have_a_format = 0;
+
+ if (first) {
+ char buf[100];
+
+ snprintf(buf, sizeof(buf), "%a", 42.0);
+ have_a_format = (strcmp(buf, "0x1.5p+5") == 0);
+ first = 0;
+ }
+
os = s;
p = buf;
if ((fmt = (char *) malloc(fmtsz)) == NULL)
@@ -864,6 +881,12 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like co
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;
@@ -907,6 +930,8 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like co
p += strlen(p);
snprintf(p, buf + bufsize - p, "%s", t);
break;
+ case 'a':
+ case 'A':
case 'f': snprintf(p, buf + bufsize - p, fmt, getfval(x)); break;
case 'd': snprintf(p, buf + bufsize - p, fmt, (long) getfval(x)); break;
case 'u': snprintf(p, buf + bufsize - p, fmt, (int) getfval(x)); break;
@@ -1009,7 +1034,7 @@ Cell *arith(Node **a, int n) /* a[0] + a[1], etc. also -a[0] */
x = execute(a[0]);
i = getfval(x);
tempfree(x);
- if (n != UMINUS) {
+ if (n != UMINUS && n != UPLUS) {
y = execute(a[1]);
j = getfval(y);
tempfree(y);
@@ -1039,6 +1064,8 @@ Cell *arith(Node **a, int n) /* a[0] + a[1], etc. also -a[0] */
case UMINUS:
i = -i;
break;
+ case UPLUS: /* handled by getfval(), above */
+ break;
case POWER:
if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */
i = ipow(i, (int) j);
@@ -1492,6 +1519,7 @@ Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg lis
char *p, *buf;
Node *nextarg;
FILE *fp;
+ int status = 0;
t = ptoi(a[0]);
x = execute(a[1]);
@@ -1589,7 +1617,20 @@ Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg lis
break;
case FSYSTEM:
fflush(stdout); /* in case something is buffered already */
- u = (Awkfloat) system(getsval(x)) / 256; /* 256 is unix-dep */
+ status = system(getsval(x));
+ u = status;
+ if (status != -1) {
+ if (WIFEXITED(status)) {
+ u = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ u = WTERMSIG(status) + 256;
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status))
+ u += 256;
+#endif
+ } else /* something else?!? */
+ u = 0;
+ }
break;
case FRAND:
u = (Awkfloat) (random() & RAND_MAX) / ((u_int)RAND_MAX + 1);
diff --git a/usr.bin/awk/tran.c b/usr.bin/awk/tran.c
index b56c9435d9d..c47e3403dae 100644
--- a/usr.bin/awk/tran.c
+++ b/usr.bin/awk/tran.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tran.c,v 1.18 2020/02/27 21:43:46 millert Exp $ */
+/* $OpenBSD: tran.c,v 1.19 2020/06/10 21:00:01 millert Exp $ */
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
@@ -68,6 +68,18 @@ Cell *literal0;
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 */
{
literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
@@ -283,6 +295,7 @@ Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
{
int fldno;
+ f += 0.0; /* normalise negative zero to positive zero */
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "assign to");
if (isfld(vp)) {
@@ -291,13 +304,18 @@ Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
if (fldno > *NF)
newfld(fldno);
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)) {
donefld = 0; /* mark $1... invalid */
donerec = 1;
}
if (freeable(vp))
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 */
if (f == -0) /* who would have thought this possible? */
f = 0;
@@ -319,6 +337,7 @@ char *setsval(Cell *vp, const char *s) /* set string val of a Cell */
{
char *t;
int fldno;
+ Awkfloat f;
DPRINTF( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) );
@@ -333,16 +352,28 @@ char *setsval(Cell *vp, const char *s) /* set string val of a Cell */
} else if (isrec(vp)) {
donefld = 0; /* mark $1... invalid */
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))
xfree(vp->sval);
- vp->tval &= ~NUM;
+ vp->tval &= ~(NUM|CONVC|CONVO);
vp->tval |= STR;
- vp->tval &= ~DONTFREE;
+ vp->fmt = NULL;
+ setfree(vp);
DPRINTF( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
(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 */
@@ -374,18 +405,79 @@ static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cel
fldbld();
else if (isrec(vp) && donerec == 0)
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 (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;
+ update_str_val(vp);
+ if (fmt == OFMT) {
+ vp->tval &= ~CONVC;
+ vp->tval |= CONVO;
+ } else {
+ /* CONVFMT */
+ vp->tval &= ~CONVO;
+ vp->tval |= CONVC;
+ }
+ 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",
(void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) );
return(vp->sval);
@@ -460,3 +552,38 @@ char *qstring(const char *is, int delim) /* collect string up to next delim */
*bp++ = 0;
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;
+}