From 32093f5bbf567525d88566a449a89c72d2845e7e Mon Sep 17 00:00:00 2001 From: "Arnold D. Robbins" Date: Wed, 22 Aug 2018 20:40:26 +0300 Subject: [PATCH] Fix multiple long-standing bugs, improve test suite. --- ChangeLog | 225 +++++++++++++++++++++++++++++++++++ FIXES | 2 + REGRESS | 17 +++ TODO | 20 ++++ awk.1 | 170 +++++++++++++++----------- awk.h | 5 +- awkgram.y | 4 +- awktest.tar | Bin 7208960 -> 6819840 bytes b.c | 2 +- bugs-fixed/README | 25 ++++ bugs-fixed/a-format.awk | 3 + bugs-fixed/a-format.bad | 3 + bugs-fixed/a-format.ok | 1 + bugs-fixed/decr-NF.awk | 11 ++ bugs-fixed/decr-NF.bad | 5 + bugs-fixed/decr-NF.ok | 5 + bugs-fixed/ofs-rebuild.awk | 17 +++ bugs-fixed/ofs-rebuild.bad | 1 + bugs-fixed/ofs-rebuild.ok | 1 + bugs-fixed/space.awk | 22 ++++ bugs-fixed/space.bad | 16 +++ bugs-fixed/space.ok | 16 +++ bugs-fixed/string-conv.awk | 13 ++ bugs-fixed/string-conv.bad | 4 + bugs-fixed/string-conv.ok | 4 + bugs-fixed/system-status.awk | 19 +++ bugs-fixed/system-status.bad | 3 + bugs-fixed/system-status.ok | 3 + bugs-fixed/unary-plus.awk | 4 + bugs-fixed/unary-plus.bad | 2 + bugs-fixed/unary-plus.ok | 2 + lib.c | 14 +++ main.c | 2 +- makefile | 8 +- maketab.c | 1 + proto.h | 3 + run.c | 51 +++++++- tran.c | 156 +++++++++++++++++++++--- 38 files changed, 763 insertions(+), 97 deletions(-) create mode 100644 ChangeLog create mode 100644 TODO create mode 100644 bugs-fixed/README create mode 100644 bugs-fixed/a-format.awk create mode 100644 bugs-fixed/a-format.bad create mode 100644 bugs-fixed/a-format.ok create mode 100644 bugs-fixed/decr-NF.awk create mode 100644 bugs-fixed/decr-NF.bad create mode 100644 bugs-fixed/decr-NF.ok create mode 100644 bugs-fixed/ofs-rebuild.awk create mode 100644 bugs-fixed/ofs-rebuild.bad create mode 100644 bugs-fixed/ofs-rebuild.ok create mode 100644 bugs-fixed/space.awk create mode 100644 bugs-fixed/space.bad create mode 100644 bugs-fixed/space.ok create mode 100644 bugs-fixed/string-conv.awk create mode 100644 bugs-fixed/string-conv.bad create mode 100644 bugs-fixed/string-conv.ok create mode 100644 bugs-fixed/system-status.awk create mode 100644 bugs-fixed/system-status.bad create mode 100644 bugs-fixed/system-status.ok create mode 100644 bugs-fixed/unary-plus.awk create mode 100644 bugs-fixed/unary-plus.bad create mode 100644 bugs-fixed/unary-plus.ok diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..17715fc --- /dev/null +++ b/ChangeLog @@ -0,0 +1,225 @@ +2018-08-22 Arnold D. Robbins + + * awktest.tar (testdir/T.expr): Fix test for unary plus. + +2018-08-22 Arnold D. Robbins + + * REGRESS: Extract tests if necessary, set PATH to include '.'. + * regdir/beebe.tar (Makefile): Fix longwrds test to prefix + sort with LC_ALL=C. + * awktest.tar: Updated from fixed test suite, directory + it extracts is now called 'testdir' to match what's in top-level + REGRESS script. + * regdir: Removed, as Brian wants to keep the test suite in + the tar file. + +2018-08-22 Arnold D. Robbins + + * FIXES, lib.c, run.c, makefile, main.c: Merge from Brian's tree. + * REGRESS: New file, from Brian. + * awktest.tar: Restored from Brian's tree. + +2018-08-22 Arnold D. Robbins + + * awkgram.y (UPLUS): New token. In the grammar, call op1() + with it. + * maketab.c (proc): Add entry for UPLUS. + * run.c (arith): Handle UPLUS. + * main.c (version): Updated. + * bugs-fixed/unary-plus.awk, bugs-fixed/unary-plus.bad, + bugs-fixed/unary-plus.ok: New files. + +2018-08-10 Arnold D. Robbins + + * TODO: Updated. + * awk.1: Improve use of macros, add some additional explanation + in a few places, alphabetize list of variables. + +2018-08-08 Arnold D. Robbins + + * awk.h (Cell): Add new field `fmt' to track xFMT value used + for a string conversion. + [CONVC, CONVO]: New flag macros. + * bugs-fixed/README: Updated. + * bugs-fixed/string-conv.awk, bugs-fixed/string-conv.bad, + bugs-fixed/string-conv.ok: New files. + * main.c (version): Updated. + * proto.h (flags2str): Add declaration. + * tran.c (setfval): Clear CONVC and CONVO flags and set vp->fmt + to NULL. + (setsval): Ditto. Add large comment and new code to manage + correct conversion of number to string based on various flags + and the value of vp->fmt. The idea is to not convert again + if xFMT is the same as before and we're doing the same conversion. + Otherwise, clear the old flags, set the new, and reconvert. + (flags2str): New function. For debug prints and for use from a debugger. + +2018-08-05 Arnold D. Robbins + + Fix filename conflicts in regdir where the only difference was + in letter case. This caused problems on Windows systems. + + * regdir/Compare.T1: Renamed from regdir/Compare.T. + * regdir/t.delete0: Renamed from regdir/t.delete. + * regdir/t.getline1: Renamed from regdir/t.getline. + * regdir/t.redir1: Renamed from regdir/t.redir. + * regdir/t.split1: Renamed from regdir/t.split. + * regdir/t.sub0: Renamed from regdir/t.sub. + * regdir/REGRESS: Adjusted. + +2018-08-04 Arnold D. Robbins + + With scalpel, tweasers, magnifying glass and bated breath, + borrow code from the NetBSD version of nawk to fix the years-old + bug whereby decrementing the value of NF did not change the + record. + + * lib.c (fldbld): Set donerec to 1 when done. + (setlastfld): New function. + * proto.h (setlastfld): Add declaration. + * run.c (copycell): Make code smarter about flags (from NetBSD code). + * tran.c (setfree): New function. + * tran.c (setfval): Normalize negative zero to positive zero. + If setting NF, clear donerec and call setlastfld(). + (setsval): Remove call to save_old_OFS(). If setting OFS, call + recbld(). If setting NF, clear donerec and call setlastfld(). + + As part of the process, revert OFS-related changes of 2018-05-22: + + * awk.h (saveOFS, saveOFSlen, save_old_OFS): Remove declarations. + * lib.c (recbld): Use *OFS instead of saveOFS. + * run.c (saveOFS, saveOFSlen, save_old_OFS): Remove. + * tran.c (syminit): Remove initialization of saveOFS and saveOFSlen. + + General stuff that goes along with all this: + + * bugs-fixed/README: Updated. + * bugs-fixed/decr-NF.awk, bugs-fixed/decr-NF.bad, + bugs-fixed/decr-NF.ok: New files. + * main.c (version): Updated. + * regdir/README.TESTS: Fix awk book title. + * regdir/T.misc: Revise test to match fixed code. + * run.c (format): Increase size of buffer used for %a test. (Unrelated + to NF or OFS, but fixes a compiler complaint.) + +2018-06-07 Arnold D. Robbins + + * regdir/beebe.tar: Fix longwrds.ok so that the test will pass. + The file was incorrectly sorted. + +2018-06-06 Arnold D. Robbins + + * regdir/T.lilly: Fix the bug again in the second instance + of the code. Thanks to BWK for pointing this out. + +2018-05-31 Arnold D. Robbins + + * regdir/T.lilly: Fix a syntax error and ordering bug + in creating the 'foo' file. + +2018-05-23 Arnold D. Robbins + + * awk.1: Remove standalone 'awk' at the top of file, it messed up + the formatting. Arrange built-in variable list in alphabetical + order. + +2018-05-23 Arnold D. Robbins + + * main.c (version): Add my email address and a date so that + users can tell this isn't straight BWK awk. + * README.md: Minor updates. + * TODO: Updated. + +2018-05-22 Arnold D. Robbins + + Add POSIX-required formats %a and %A. + + * run.c (format): Check for %a support in C library. If there, + allow %a and %A as valid formats. + * TODO: Updated. + * bugs-fixed/README: Updated. + * bugs-fixed/a-format.awk, bugs-fixed/a-format.bad, + bugs-fixed/a-format.ok: New files. + +2018-05-22 Arnold D. Robbins + + * FIXES: Restored a line from a much earlier version that + apparently got lost when the dates were reordered. + * TODO: Updated. + +2018-05-22 Arnold D. Robbins + + * README.md: New file. + +2018-05-22 Arnold D. Robbins + + * regdir/echo.c, regdir/time.c: Minor fixes to compile without + warning on current GCC / Linux. + +2018-05-22 Arnold D. Robbins + + * TODO: New file. + +2018-05-22 Arnold D. Robbins + + * makefile (gitadd, gitpush): Remove these targets. They + should not be automated and were incorrect for things that + would be done regularly. + +2018-05-22 Arnold D. Robbins + + Fix nawk so that [[:blank:]] only matches space and tab instead + of any whitespace character, originally made May 10, 2018. + See bugs-fixed/space.awk. + + This appears to have been a thinko on Brian's part. + + * b.c (charclasses): Use xisblank() function for [[:blank:]]. + * bugs-fixed/README: Updated. + * bugs-fixed/space.awk, bugs-fixed/space.bad, + bugs-fixed/space.ok: New files. + +2018-05-22 Arnold D. Robbins + + * .gitignore: New file. + +2018-05-22 Arnold D. Robbins + + Fix nawk to provide reasonable exit status for system(), + a la gawk, originally made March 12, 2016. See + bugs-fixed/system-status.awk. + + * run.c (bltin): For FSYSTEM, use the macros defined for wait(2) + to produce a reasonable exit value, instead of doing a floating-point + division by 256. + * awk.1: Document the return status values. + * bugs-fixed/README: Updated. + * bugs-fixed/system-status.awk, bugs-fixed/system-status.bad, + bugs-fixed/system-status.ok: New files. + +2018-05-22 Arnold D. Robbins + + Bug fix with respect to rebuilding a record, originally + made August 19, 2014. See bugs-fixed/ofs-rebuild.awk. + + * awk.h (saveOFS, saveOFSlen): Declare new variables. + * lib.c (recbld): Use them when rebuilding the record. + * run.c (saveOFS, saveOFSlen): Define new variables. + (save_old_OFS): New function to save OFS aside. + * tran.c (syminit): Initialize saveOFS and saveOFSlen. + (setsval): If setting a field, call save_old_OFS(). + * bugs-fixed/README, bugs-fixed/ofs-rebuild.awk, + bugs-fixed/ofs-rebuild.bad, bugs-fixed/ofs-rebuild.ok: New files. + +2018-05-22 Arnold D. Robbins + + * makefile (YACC): Use bison. + +2018-05-22 Arnold D. Robbins + + * ChangeLog: Created. + * regdir: Created. Based on contents of awktest.a. + * .gitattributes: Created, to preserve CR LF in regdir/t.crlf. + * awktest.a: Removed. + * regdir/T.gawk, regdir/T.latin1: Updated from awktest.tar. + * awktest.tar: Removed. diff --git a/FIXES b/FIXES index b17d8a3..5c33dd9 100644 --- a/FIXES +++ b/FIXES @@ -514,6 +514,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/REGRESS b/REGRESS index f4372d6..facbd83 100755 --- a/REGRESS +++ b/REGRESS @@ -1,3 +1,20 @@ +#! /bin/sh + +if [ -d testdir ] +then + true # do nothing +elif [ -f awktest.tar ] +then + echo extracting testdir + tar -xpf awktest.tar +else + echo $0: No testdir directory and no awktest.tar to extract it from! >&2 + exit 1 +fi + cd testdir pwd +PATH=.:$PATH +export PATH + REGRESS diff --git a/TODO b/TODO new file mode 100644 index 0000000..5099569 --- /dev/null +++ b/TODO @@ -0,0 +1,20 @@ +Fri Aug 10 11:11:11 IDT 2018 +============================ + +A semi-random list of things to look into. + +1. DONE. Make sure the FIXES file has everything after the date order +was reversed. + +2. DONE. Look into the problem that NF-- doesn't throw away the last field. +This a long-standing issue. + +3. DONE. Add suport for %a and %A printf formats. + +4. DONE. Look into the problem that string conversions are sticky; once +done they never change, even if OFMT or CONVFMT do. Similarly, a +conversion from OFMT becomes the permanent string value, which is +incorrect. + +5. MAYBE: Improve the test suite enough so that it can be cleaned up after +it was run, and old results can be saved for comparison. diff --git a/awk.1 b/awk.1 index 6119613..5830143 100644 --- a/awk.1 +++ b/awk.1 @@ -7,7 +7,6 @@ .fi .ft 1 .. -awk .TH AWK 1 .CT 1 files prog_other .SH NAME @@ -36,7 +35,7 @@ awk \- pattern-directed scanning and processing language scans each input .I file for lines that match any of a set of patterns specified literally in -.IR prog +.I prog or in one or more files specified as .B \-f @@ -53,7 +52,7 @@ The file name .B \- means the standard input. Any -.IR file +.I file of the form .I var=value is treated as an assignment, not a filename, @@ -70,12 +69,12 @@ any number of options may be present. The .B \-F -.IR fs +.I fs option defines the input field separator to be the regular expression -.IR fs. +.IR fs . .PP An input line is normally made up of fields separated by white space, -or by regular expression +or by the regular expression .BR FS . The fields are denoted .BR $1 , @@ -87,7 +86,7 @@ If .BR FS is null, the input line is split into one field per character. .PP -A pattern-action statement has the form +A pattern-action statement has the form: .IP .IB pattern " { " action " } .PP @@ -101,7 +100,7 @@ An action is a sequence of statements. A statement can be one of the following: .PP .EX -.ta \w'\f(CWdelete array[expression]'u +.ta \w'\f(CWdelete array[expression]\fR'u .RS .nf .ft CW @@ -145,7 +144,7 @@ The operators are also available in expressions. Variables may be scalars, array elements (denoted -.IB x [ i ] ) +.IB x [ i ] \fR) or fields. Variables are initialized to the null string. Array subscripts may be any string, @@ -161,11 +160,11 @@ The .B print statement prints its arguments on the standard output (or on a file if -.BI > file +.BI > " file or -.BI >> file +.BI >> " file is present or on a pipe if -.BI | cmd +.BI | " cmd is present), separated by the current output field separator, and terminated by the output record separator. .I file @@ -176,9 +175,10 @@ identical string values in different statements denote the same open file. The .B printf -statement formats its expression list according to the format +statement formats its expression list according to the +.I format (see -.IR printf (3)) . +.IR printf (3)). The built-in function .BI close( expr ) closes the file or pipe @@ -189,13 +189,13 @@ flushes any buffered output for the file or pipe .IR expr . .PP The mathematical functions +.BR atan2 , +.BR cos , .BR exp , .BR log , -.BR sqrt , .BR sin , -.BR cos , and -.BR atan2 +.B sqrt are built in. Other built-in functions: .TF length @@ -203,7 +203,8 @@ Other built-in functions: .B length the length of its argument taken as a string, -or of +number of elements in an array for an array argument, +or length of .B $0 if no argument. .TP @@ -218,14 +219,18 @@ and returns the previous seed. .B int truncates to an integer value .TP -.BI substr( s , " m" , " n\fB) +\fBsubstr(\fIs\fB, \fIm\fR [\fB, \fIn\^\fR]\fB)\fR the .IR n -character substring of .I s that begins at position -.IR m +.I m counted from 1. +If no +.IR m , +use the rest of the string +.I .TP .BI index( s , " t" ) the position in @@ -246,14 +251,14 @@ and .B RLENGTH are set to the position and length of the matched string. .TP -.BI split( s , " a" , " fs\fB) +\fBsplit(\fIs\fB, \fIa \fR[\fB, \fIfs\^\fR]\fB)\fR splits the string .I s into array elements -.IB a [1] , -.IB a [2] , +.IB a [1] \fR, +.IB a [2] \fR, \&..., -.IB a [ n ] , +.IB a [ n ] \fR, and returns .IR n . The separation is done with the regular expression @@ -266,7 +271,7 @@ is not given. An empty string as field separator splits the string into one array element per character. .TP -.BI sub( r , " t" , " s\fB) +\fBsub(\fIr\fB, \fIt \fR[, \fIs\^\fR]\fB) substitutes .I t for the first occurrence of the regular expression @@ -279,7 +284,7 @@ is not given, .B $0 is used. .TP -.B gsub +\fBgsub(\fIr\fB, \fIt \fR[, \fIs\^\fR]\fB) same as .B sub except that all occurrences of the regular expression @@ -289,18 +294,28 @@ and .B gsub return the number of replacements. .TP -.BI sprintf( fmt , " expr" , " ...\fB ) +.BI sprintf( fmt , " expr" , " ...\fB) the string resulting from formatting .I expr ... according to the .IR printf (3) format -.I fmt +.IR fmt . .TP .BI system( cmd ) executes .I cmd -and returns its exit status +and returns its exit status. This will be \-1 upon error, +.IR cmd 's +exit status upon a normal exit, +256 + +.I sig +upon death-by-signal, where +.I sig +is the number of the murdering signal, +or 512 + +.I sig +if there was a core dump. .TP .BI tolower( str ) returns a copy of @@ -321,7 +336,7 @@ sets .B $0 to the next input record from the current input file; .B getline -.BI < file +.BI < " file sets .B $0 to the next record from @@ -359,7 +374,7 @@ Isolated regular expressions in a pattern apply to the entire line. Regular expressions may also occur in relational expressions, using the operators -.BR ~ +.B ~ and .BR !~ . .BI / re / @@ -383,8 +398,12 @@ A relational expression is one of the following: .br .BI ( expr , expr,... ") in " array-name .PP -where a relop is any of the six relational operators in C, -and a matchop is either +where a +.I relop +is any of the six relational operators in C, +and a +.I matchop +is either .B ~ (matches) or @@ -405,57 +424,68 @@ and after the last. and .B END do not combine with other patterns. +They may appear multiple times in a program and execute +in the order they are read by +.IR awk . .PP Variable names with special meanings: .TF FILENAME .TP +.B ARGC +argument count, assignable. +.TP +.B ARGV +argument array, assignable; +non-null members are taken as filenames. +.TP .B CONVFMT conversion format used when converting numbers (default -.BR "%.6g" ) +.BR "%.6g" ). +.TP +.B ENVIRON +array of environment variables; subscripts are names. +.TP +.B FILENAME +the name of the current input file. +.TP +.B FNR +ordinal number of the current record in the current file. .TP .B FS regular expression used to separate fields; also settable by option -.BI \-F fs. +.BI \-F fs\fR. .TP .BR NF -number of fields in the current record +number of fields in the current record. .TP .B NR -ordinal number of the current record -.TP -.B FNR -ordinal number of the current record in the current file -.TP -.B FILENAME -the name of the current input file -.TP -.B RS -input record separator (default newline) -.TP -.B OFS -output field separator (default blank) -.TP -.B ORS -output record separator (default newline) +ordinal number of the current record. .TP .B OFMT output format for numbers (default -.BR "%.6g" ) +.BR "%.6g" ). +.TP +.B OFS +output field separator (default space). +.TP +.B ORS +output record separator (default newline). +.TP +.B RLENGTH +the length of a string matched by +.BR match . +.TP +.B RS +input record separator (default newline). +.TP +.B RSTART +the start position of a string matched by +.BR match . .TP .B SUBSEP -separates multiple subscripts (default 034) -.TP -.B ARGC -argument count, assignable -.TP -.B ARGV -argument array, assignable; -non-null members are taken as filenames -.TP -.B ENVIRON -array of environment variables; subscripts are names. +separates multiple subscripts (default 034). .PD .PP Functions may be defined (at the position of a pattern-action statement) thus: @@ -486,7 +516,7 @@ BEGIN { FS = ",[ \et]*|[ \et]+" } .EE .ns .IP -Same, with input fields separated by comma and/or blanks and tabs. +Same, with input fields separated by comma and/or spaces and tabs. .PP .EX .nf @@ -512,13 +542,13 @@ BEGIN { # Simulate echo(1) .fi .EE .SH SEE ALSO +.IR grep (1), .IR lex (1), .IR sed (1) .br A. V. Aho, B. W. Kernighan, P. J. Weinberger, -.I -The AWK Programming Language, -Addison-Wesley, 1988. ISBN 0-201-07981-X +.IR "The AWK Programming Language" , +Addison-Wesley, 1988. ISBN 0-201-07981-X. .SH BUGS There are no explicit conversions between numbers and strings. To force an expression to be treated as a number add 0 to it; @@ -527,3 +557,7 @@ to force it to be treated as a string concatenate .br The scope rules for variables in functions are a botch; the syntax is worse. +.br +POSIX-standard interval expressions in regular expressions are not supported. +.br +Only eight-bit characters sets are handled correctly. diff --git a/awk.h b/awk.h index a36cdb1..70097b9 100644 --- a/awk.h +++ b/awk.h @@ -81,7 +81,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; @@ -109,6 +110,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/awkgram.y b/awkgram.y index 5b5c461..e4abeed 100644 --- a/awkgram.y +++ b/awkgram.y @@ -86,7 +86,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 @@ -357,7 +357,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/awktest.tar b/awktest.tar index d1cff8cf91fdc2b229a079f8863805c00779a29a..959b452cfa4543af5512417e034f895abcac4d9b 100644 GIT binary patch delta 22930 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 O|K~ruw|-^pZ|u3J^If5iwfECv5CS@Ui`a} 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" because + # it delays rebuilding $0 until it's needed, and then it uses + # the current value of OFS. Oops. + print +} diff --git a/bugs-fixed/ofs-rebuild.bad b/bugs-fixed/ofs-rebuild.bad new file mode 100644 index 0000000..7570811 --- /dev/null +++ b/bugs-fixed/ofs-rebuild.bad @@ -0,0 +1 @@ +a<>b<>3333<>d<>e<>f<>g diff --git a/bugs-fixed/ofs-rebuild.ok b/bugs-fixed/ofs-rebuild.ok new file mode 100644 index 0000000..2689218 --- /dev/null +++ b/bugs-fixed/ofs-rebuild.ok @@ -0,0 +1 @@ +a:b:3333:d:e:f:g diff --git a/bugs-fixed/space.awk b/bugs-fixed/space.awk new file mode 100644 index 0000000..6aa87d2 --- /dev/null +++ b/bugs-fixed/space.awk @@ -0,0 +1,22 @@ +BEGIN { + c[" "] = "\" \"" + c["\a"] = "\\a" + c["\b"] = "\\b" + c["\f"] = "\\f" + c["\n"] = "\\n" + c["\r"] = "\\r" + c["\t"] = "\\t" + c["\v"] = "\\v" + + sort = "LC_ALL=C sort" + + for (i in c) + printf("%s %s [[:space:]]\n", c[i], + i ~ /[[:space:]]/ ? "~" : "!~") | sort + + for (i in c) + printf("%s %s [[:blank:]]\n", c[i], + i ~ /[[:blank:]]/ ? "~" : "!~") | sort + + close(sort) +} diff --git a/bugs-fixed/space.bad b/bugs-fixed/space.bad new file mode 100644 index 0000000..f92055f --- /dev/null +++ b/bugs-fixed/space.bad @@ -0,0 +1,16 @@ +" " ~ [[:blank:]] +" " ~ [[:space:]] +\a !~ [[:blank:]] +\a !~ [[:space:]] +\b !~ [[:blank:]] +\b !~ [[:space:]] +\f ~ [[:blank:]] +\f ~ [[:space:]] +\n ~ [[:blank:]] +\n ~ [[:space:]] +\r ~ [[:blank:]] +\r ~ [[:space:]] +\t ~ [[:blank:]] +\t ~ [[:space:]] +\v ~ [[:blank:]] +\v ~ [[:space:]] diff --git a/bugs-fixed/space.ok b/bugs-fixed/space.ok new file mode 100644 index 0000000..4278c5c --- /dev/null +++ b/bugs-fixed/space.ok @@ -0,0 +1,16 @@ +" " ~ [[:blank:]] +" " ~ [[:space:]] +\a !~ [[:blank:]] +\a !~ [[:space:]] +\b !~ [[:blank:]] +\b !~ [[:space:]] +\f !~ [[:blank:]] +\f ~ [[:space:]] +\n !~ [[:blank:]] +\n ~ [[:space:]] +\r !~ [[:blank:]] +\r ~ [[:space:]] +\t ~ [[:blank:]] +\t ~ [[:space:]] +\v !~ [[:blank:]] +\v ~ [[:space:]] diff --git a/bugs-fixed/string-conv.awk b/bugs-fixed/string-conv.awk new file mode 100644 index 0000000..a1f04ab --- /dev/null +++ b/bugs-fixed/string-conv.awk @@ -0,0 +1,13 @@ +BEGIN { + OFMT = ">>%.6g<<" + a = 12.1234 + print "a =", a + b = a "" + print "1 ->", b + CONVFMT = "%2.2f" + b = a "" + print "2 ->", b + CONVFMT = "%.12g" + b = a "" + print "3 ->", b +} diff --git a/bugs-fixed/string-conv.bad b/bugs-fixed/string-conv.bad new file mode 100644 index 0000000..2ab95e8 --- /dev/null +++ b/bugs-fixed/string-conv.bad @@ -0,0 +1,4 @@ +a = >>12.1234<< +1 -> >>12.1234<< +2 -> >>12.1234<< +3 -> >>12.1234<< diff --git a/bugs-fixed/string-conv.ok b/bugs-fixed/string-conv.ok new file mode 100644 index 0000000..7c09711 --- /dev/null +++ b/bugs-fixed/string-conv.ok @@ -0,0 +1,4 @@ +a = >>12.1234<< +1 -> 12.1234 +2 -> 12.12 +3 -> 12.1234 diff --git a/bugs-fixed/system-status.awk b/bugs-fixed/system-status.awk new file mode 100644 index 0000000..8daf563 --- /dev/null +++ b/bugs-fixed/system-status.awk @@ -0,0 +1,19 @@ +# Unmodified nawk prints the 16 bit exit status divided by 256, but +# does so using floating point arithmetic, yielding strange results. +# +# The fix is to use the various macros defined for wait(2) and to +# use the signal number + 256 for death by signal, or signal number + 512 +# for death by signal with core dump. + +BEGIN { + status = system("exit 42") + print "normal status", status + + status = system("kill -HUP $$") + print "death by signal status", status + + status = system("kill -ABRT $$") + print "death by signal with core dump status", status + + system("rm -f core*") +} diff --git a/bugs-fixed/system-status.bad b/bugs-fixed/system-status.bad new file mode 100644 index 0000000..a1317db --- /dev/null +++ b/bugs-fixed/system-status.bad @@ -0,0 +1,3 @@ +normal status 42 +death by signal status 0.00390625 +death by signal with core dump status 0.523438 diff --git a/bugs-fixed/system-status.ok b/bugs-fixed/system-status.ok new file mode 100644 index 0000000..737828f --- /dev/null +++ b/bugs-fixed/system-status.ok @@ -0,0 +1,3 @@ +normal status 42 +death by signal status 257 +death by signal with core dump status 518 diff --git a/bugs-fixed/unary-plus.awk b/bugs-fixed/unary-plus.awk new file mode 100644 index 0000000..ba6185b --- /dev/null +++ b/bugs-fixed/unary-plus.awk @@ -0,0 +1,4 @@ +BEGIN { + print +"q" + print +"43.12345678912345678" +} diff --git a/bugs-fixed/unary-plus.bad b/bugs-fixed/unary-plus.bad new file mode 100644 index 0000000..76f57d5 --- /dev/null +++ b/bugs-fixed/unary-plus.bad @@ -0,0 +1,2 @@ +q +43.12345678912345678 diff --git a/bugs-fixed/unary-plus.ok b/bugs-fixed/unary-plus.ok new file mode 100644 index 0000000..90f97af --- /dev/null +++ b/bugs-fixed/unary-plus.ok @@ -0,0 +1,2 @@ +0 +43.1235 diff --git a/lib.c b/lib.c index 1b6d86e..ba6ebd4 100644 --- a/lib.c +++ b/lib.c @@ -356,6 +356,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]; @@ -387,6 +388,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/main.c b/main.c index 45b4dbb..d292ad3 100644 --- a/main.c +++ b/main.c @@ -22,7 +22,7 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ****************************************************************/ -const char *version = "version 20130105"; +const char *version = "version 20130105 + fixes by arnold@skeeve.com 20180822"; #define DEBUG #include diff --git a/makefile b/makefile index ff54372..8f5906f 100644 --- a/makefile +++ b/makefile @@ -26,13 +26,13 @@ CFLAGS = -g CFLAGS = -O2 CFLAGS = -CC = gcc -Wall -g -Wwrite-strings +#CC = gcc -Wall -g -Wwrite-strings CC = gcc -g -Wall -pedantic -CC = gcc -O4 -Wall -pedantic -fno-strict-aliasing -CC = gcc -fprofile-arcs -ftest-coverage # then gcov f1.c; cat f1.c.gcov +#CC = gcc -O4 -Wall -pedantic -fno-strict-aliasing +#CC = gcc -fprofile-arcs -ftest-coverage # then gcov f1.c; cat f1.c.gcov YACC = bison -d -y -YACC = yacc -d +# YACC = yacc -d #YFLAGS = -d -S # -S uses sprintf in yacc parser instead of sprint diff --git a/maketab.c b/maketab.c index 31acd75..e23974c 100644 --- a/maketab.c +++ b/maketab.c @@ -62,6 +62,7 @@ struct xx { DIVIDE, "arith", " / " }, { MOD, "arith", " % " }, { UMINUS, "arith", " -" }, + { UPLUS, "arith", " +" }, { POWER, "arith", " **" }, { PREINCR, "incrdecr", "++" }, { POSTINCR, "incrdecr", "++" }, diff --git a/proto.h b/proto.h index 9a657ef..ad6f2e8 100644 --- a/proto.h +++ b/proto.h @@ -124,6 +124,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); @@ -193,3 +194,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/run.c b/run.c index d3f9c68..c818a08 100644 --- a/run.c +++ b/run.c @@ -31,6 +31,8 @@ THIS SOFTWARE. #include #include #include +#include +#include #include "awk.h" #include "ytab.h" @@ -323,14 +325,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; } @@ -817,6 +823,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]; + + sprintf(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) @@ -859,6 +876,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; @@ -901,6 +924,8 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like co p += strlen(p); sprintf(p, "%s", t); break; + case 'a': + case 'A': case 'f': sprintf(p, fmt, getfval(x)); break; case 'd': sprintf(p, fmt, (long) getfval(x)); break; case 'u': sprintf(p, fmt, (int) getfval(x)); break; @@ -1003,7 +1028,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); @@ -1033,6 +1058,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); @@ -1479,6 +1506,7 @@ Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg lis Node *nextarg; FILE *fp; void flush_all(void); + int status = 0; t = ptoi(a[0]); x = execute(a[1]); @@ -1515,7 +1543,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: /* in principle, rand() returns something in 0..RAND_MAX */ diff --git a/tran.c b/tran.c index a9fa325..06f32fc 100644 --- a/tran.c +++ b/tran.c @@ -67,6 +67,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); @@ -282,6 +294,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)) { @@ -290,13 +303,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; @@ -318,6 +336,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) ); @@ -332,16 +351,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 */ @@ -373,17 +404,78 @@ static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cel fldbld(); else if (isrec(vp) && donerec == 0) recbld(); - if (isstr(vp) == 0) { - if (freeable(vp)) - xfree(vp->sval); - if (modf(vp->fval, &dtemp) == 0) /* it's integral */ - sprintf(s, "%.30g", vp->fval); - else - sprintf(s, *fmt, vp->fval); - vp->sval = tostring(s); - vp->tval &= ~DONTFREE; - vp->tval |= STR; + + /* + * 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 */ \ + sprintf(s, "%.30g", vp->fval); \ + else \ + sprintf(s, *fmt, vp->fval); \ + vp->sval = tostring(s); \ + vp->tval &= ~DONTFREE; \ + vp->tval |= STR; \ } + + if (isstr(vp) == 0) { + 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); @@ -457,3 +549,37 @@ 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; + char *cp = buf; + + for (i = 0; flagtab[i].name != NULL; i++) { + if ((flags & flagtab[i].value) != 0) { + if (cp > buf) + *cp++ = '|'; + strcpy(cp, flagtab[i].name); + cp += strlen(cp); + } + } + + return buf; +}