vendor/awk: upgrade from 20121220 to 20200612
authorDaniel Fojt <df@neosystem.org>
Wed, 24 Jun 2020 05:45:41 +0000 (07:45 +0200)
committerDaniel Fojt <df@neosystem.org>
Thu, 25 Jun 2020 04:11:49 +0000 (06:11 +0200)
Summary of notable changes:

- fixed buffer overflow in error reporting
- OFS value used to rebuild the record was incorrect
- add POSIX standard %a and %A to supported formats
- decrementing NF did not change $0
- unary plus on a string constant returned the string
- avoid undefined behaviour when using ctype(3) functions in relex()
- make getline handle numeric strings
- added support for POSIX-standard interval expressions in regexps
- use of random(3) as the random number generator
- support POSIX-specified C-style escape sequences "\a" and "\v" in
  command line arguments and regular expressions
- fix various array / memory overruns
- import code from NetBSD awk that implements RS as a regular expression
- add an optimization for string concatenation
- fix a bug whereby a{0,3} could match four a's
- input/output errors on closing files are now fatal instead of mere
  warnings
- if POSIXLY_CORRECT is set in the environment, then sub and gsub use
  POSIX rules for multiple backslashes
- it's no longer necessary to use the -y flag to bison
- backslash continuation inside strings removes the newline
- avoid any spurious errors left over from previous calls

For details, see FIXES, or commit history at
https://github.com/onetrueawk/awk/commits/master

15 files changed:
contrib/awk/FIXES
contrib/awk/README [deleted file]
contrib/awk/README.md [new file with mode: 0644]
contrib/awk/awk.1
contrib/awk/awk.h
contrib/awk/awkgram.y
contrib/awk/b.c
contrib/awk/lex.c
contrib/awk/lib.c
contrib/awk/main.c
contrib/awk/maketab.c
contrib/awk/parse.c
contrib/awk/proto.h
contrib/awk/run.c
contrib/awk/tran.c

index c78aabc..4cbcca2 100644 (file)
@@ -25,6 +25,272 @@ THIS SOFTWARE.
 This file lists all bug fixes, changes, etc., made since the AWK book
 was sent to the printers in August, 1987.
 
+June 12, 2020:
+       Clear errno before calling errcheck to avoid any spurious errors
+       left over from previous calls that may have set it. Thanks to
+       Todd Miller for the fix, from PR #80.
+
+       Fix Issue #78 by allowing \r to follow floating point numbers in
+       lib.c:is_number. Thanks to GitHub user ajcarr for the report
+       and to Arnold Robbins for the fix.
+
+June 5, 2020:
+       In fldbld(), make sure that inputFS is set before trying to
+       use it. Thanks to  Steffen Nurpmeso <steffen@sdaoden.eu>
+       for the report.
+
+May 5, 2020:
+       Fix checks for compilers that can handle noreturn. Thanks to
+       GitHub user enh-google for pointing it out. Closes Issue #79.
+
+April 16, 2020:
+       Handle old compilers that don't support C11 (for noreturn).
+       Thanks to Arnold Robbins.
+
+April 5, 2020:
+       Use <stdnoreturn.h> and noreturn instead of GCC attributes.
+       Thanks to GitHub user awkfan77. Closes PR #77.
+
+February 28, 2020:
+       More cleanups from Christos Zoulas: notably backslash continuation
+       inside strings removes the newline and a fix for RS = "^a".
+       Fix for address sanitizer-found problem. Thanks to GitHub user
+       enh-google.
+
+February 19, 2020:
+       More small cleanups from Christos Zoulas.
+
+February 18, 2020:
+       Additional cleanups from Christos Zoulas. It's no longer necessary
+       to use the -y flag to bison.
+
+February 6, 2020:
+       Additional small cleanups from Christos Zoulas. awk is now
+       a little more robust about reporting I/O errors upon exit.
+
+January 31, 2020:
+       Merge PR #70, which avoids use of variable length arrays. Thanks
+       to GitHub user michaelforney.  Fix issue #60 ({0} in interval
+       expressions doesn't work).  Also get all tests working again.
+       Thanks to Arnold Robbins.
+
+January 24, 2020:
+       A number of small cleanups from Christos Zoulas.  Add the close
+       on exec flag to files/pipes opened for redirection; courtesy of
+       Arnold Robbins.
+
+January 19, 2020:
+       If POSIXLY_CORRECT is set in the environment, then sub and gsub
+       use POSIX rules for multiple backslashes.  This fixes Issue #66,
+       while maintaining backwards compatibility.
+
+January 9, 2020:
+       Input/output errors on closing files are now fatal instead of
+       mere warnings. Thanks to Martijn Dekker <martijn@inlv.org>.
+
+January 5, 2020:
+       Fix a bug in the concatentation of two string constants into
+       one done in the grammar.  Fixes GitHub issue #61.  Thanks
+       to GitHub user awkfan77 for pointing out the direction for
+       the fix.  New test T.concat added to the test suite.
+       Fix a few memory leaks reported by valgrind, as well.
+
+December 27, 2019:
+       Fix a bug whereby a{0,3} could match four a's.  Thanks to
+       "Anonymous AWK fan" for the report.
+
+December 11, 2019:
+       Further printf-related fixes for 32 bit systems.
+       Thanks again to Christos Zoulas.
+
+December 8, 2019:
+       Fix the return value of sprintf("%d") on 32 bit systems.
+       Thanks to Jim Lowe for the report and to Christos Zoulas
+       for the fix.
+
+November 10, 2019:
+       Convert a number of Boolean integer variables into
+       actual bools. Convert compile_time variable into an
+       enum and simplify some of the related code.  Thanks
+       to Arnold Robbins.
+
+November 8, 2019:
+       Fix from Ori Bernstein to get UTF-8 characters instead of
+       bytes when FS = "".  This is currently the only bit of
+       the One True Awk that understands multibyte characters.
+       From Arnold Robbins, apply some cleanups in the test suite.
+
+October 25, 2019:
+       More fixes and cleanups from NetBSD, courtesy of Christos
+       Zoulas. Merges PRs 54 and 55.
+
+October 24, 2019:
+       Import second round of code cleanups from NetBSD. Much thanks
+       to Christos Zoulas (GitHub user zoulasc). Merges PR 53.
+       Add an optimization for string concatenation, also from
+       Christos.
+
+October 17, 2019:
+       Import code cleanups from NetBSD. Much thanks to Christos
+       Zoulas (GitHub user zoulasc). Merges PR 51.
+
+October 6, 2019:
+       Import code from NetBSD awk that implements RS as a regular
+       expression.
+
+September 10, 2019:
+       Fixes for various array / memory overruns found via gcc's
+       -fsanitize=unknown. Thanks to Alexander Richardson (GitHub
+       user arichardson). Merges PRs 47 and 48.
+
+July 28, 2019:
+       Import grammar optimization from NetBSD: Two string constants
+       concatenated together get turned into a single string.
+
+July 26, 2019:
+       Support POSIX-specified C-style escape sequences "\a" (alarm)
+       and "\v" (vertical tab) in command line arguments and regular
+       expressions, further to the support for them in strings added on
+       Apr 9, 1989. These now no longer match as literal "a" and "v"
+       characters (as they don't on other awk implementations).
+       Thanks to Martijn Dekker.
+
+July 17, 2019:
+       Pull in a number of code cleanups and minor fixes from
+       Warner Losh's bsd-ota branch.  The only user visible change
+       is the use of random(3) as the random number generator.
+       Thanks to Warner Losh for collecting all these fixes in
+       one easy place to get them from.
+
+July 16, 2019:
+       Fix field splitting to use FS value as of the time a record
+       was read or assigned to.  Thanks to GitHub user Cody Mello (melloc)
+       for the fix. (Merged from his branch, via PR #42.) Updated
+       testdir/T.split per said PR as well.
+
+June 24, 2019:
+       Extract awktest.tar into testdir directory. Add some very
+       simple mechanics to the makefile for running the tests and
+       for cleaning up. No changes to awk itself.
+
+June 17, 2019:
+       Disallow deleting SYMTAB and its elements, which creates
+       use-after-free bugs. Thanks to GitHub user Cody Mello (melloc)
+       for the fix. (Merged from PR #43.)
+
+June 5, 2019:
+       Allow unmatched right parenthesis in a regular expression to
+       be treated literally. Fixes Issue #40. Thanks to GitHub user
+       Warner Losh (bsdimp) for the report. Thanks to Arnold Robbins
+       for the fix.
+
+May 29,2019:
+       Fix check for command line arguments to no longer require that
+       first character after '=' not be another '='. Reverts change of
+       August 11, 1989. Thanks to GitHub user Jamie Landeg Jones for
+       pointing out the issue; from Issue #38.
+
+Apr 7, 2019:
+       Update awktest.tar(p.50) to use modern options to sort. Needed
+       for Android development. Thanks to GitHub user mohd-akram (Mohamed
+       Akram).  From Issue #33.
+
+Mar 12, 2019:
+       Added very simplistic support for cross-compiling in the
+       makefile.  We are NOT going to go in the direction of the
+       autotools, though.  Thanks to GitHub user nee-san for
+       the basic change. (Merged from PR #34.)
+
+Mar 5, 2019:
+       Added support for POSIX-standard interval expressions (a.k.a.
+       bounds, a.k.a. repetition expressions) in regular expressions,
+       backported (via NetBSD) from Apple awk-24 (20070501).
+       Thanks to Martijn Dekker <martijn@inlv.org> for the port.
+       (Merged from PR #30.)
+
+Mar 3, 2019:
+       Merge PRs as follows:
+       #12: Avoid undefined behaviour when using ctype(3) functions in
+            relex(). Thanks to GitHub user iamleot.
+       #31: Make getline handle numeric strings, and update FIXES. Thanks
+            to GitHub user arnoldrobbins.
+       #32: maketab: support build systems with read-only source. Thanks
+            to GitHub user enh.
+
+Jan 25, 2019:
+       Make getline handle numeric strings properly in all cases.
+       (Thanks, Arnold.)
+
+Jan 21, 2019:
+       Merged a number of small fixes from GitHub pull requests.
+       Thanks to GitHub users Arnold Robbins (arnoldrobbins),
+       Cody Mello (melloc) and Christoph Junghans (junghans).
+       PR numbers: 13-21, 23, 24, 27.
+
+Oct 25, 2018:
+       Added test in maketab.c to prevent generating a proctab entry
+       for YYSTYPE_IS_DEFINED.  It was harmless but some gcc settings
+       generated a warning message.  Thanks to Nan Xiao for report.
+
+Aug 27, 2018:
+       Disallow '$' in printf formats; arguments evaluated in order
+       and printed in order.
+
+       Added some casts to silence warnings on debugging printfs.
+       (Thanks, Arnold.)
+
+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)
+       [thanks to Arnold Robbins]
+
+Mar 26, 2015:
+       buffer overflow in error reporting; thanks to tobias ulmer
+       and john-mark gurney for spotting it and the fix.
+
+Feb 4, 2013:
+       cleaned up a handful of tests that didn't seem to actually
+       test for correct behavior: T.latin1, T.gawk.
+
+Jan 5, 2013:
+       added ,NULL initializer to static Cells in run.c; not really
+       needed but cleaner.  Thanks to Michael Bombardieri.
+
 Dec 20, 2012:
        fiddled makefile to get correct yacc and bison flags.  pick yacc
        (linux) or bison (mac) as necessary.
@@ -52,10 +318,10 @@ Jun 12, 2011:
        /pat/, \n /pat/ {...} is now legal, though bad style to use.
 
        added checks to new -v code that permits -vnospace; thanks to
-       ruslan ermilov for spotting this and providing the patch. 
+       ruslan ermilov for spotting this and providing the patch.
 
        removed fixed limit on number of open files; thanks to aleksey
-       cheusov and christos zoulos. 
+       cheusov and christos zoulos.
 
        fixed day 1 bug that resurrected deleted elements of ARGV when
        used as filenames (in lib.c).
@@ -73,10 +339,10 @@ May 1, 2011:
        and arnold robbins, changed srand() to return the previous
        seed (which is 1 on the first call of srand).  the seed is
        an Awkfloat internally though converted to unsigned int to
-       pass to the library srand().  thanks, everyone. 
+       pass to the library srand().  thanks, everyone.
 
        fixed a subtle (and i hope low-probability) overflow error
-       in fldbld, by adding space for one extra \0.  thanks to 
+       in fldbld, by adding space for one extra \0.  thanks to
        robert bassett for spotting this one and providing a fix.
 
        removed the files related to compilation on windows.  i no
@@ -113,7 +379,7 @@ Oct 8, 2008:
 
 Oct 23, 2007:
        minor fix in lib.c: increase inputFS to 100, change malloc
-       for fields to n+1.  
+       for fields to n+1.
 
        fixed memory fault caused by out of order test in setsval.
 
@@ -160,7 +426,7 @@ Jan 17, 2006:
 
        core dump on linux with BEGIN {nextfile}, now fixed.
 
-       removed some #ifdef's in run.c and lex.c that appear to no 
+       removed some #ifdef's in run.c and lex.c that appear to no
        longer be necessary.
 
 Apr 24, 2005:
@@ -174,8 +440,8 @@ Jan 14, 2005:
        rethinking it.
 
 Dec 31, 2004:
-       prevent overflow of -f array in main, head off potential error in 
-       call of SYNTAX(), test malloc return in lib.c, all with thanks to 
+       prevent overflow of -f array in main, head off potential error in
+       call of SYNTAX(), test malloc return in lib.c, all with thanks to
        todd miller.
 
 Dec 22, 2004:
@@ -203,8 +469,8 @@ Nov 22, 2003:
        code known to man.
 
        fixed a storage leak in call() that appears to have been there since
-       1983 or so -- a function without an explicit return that assigns a 
-       string to a parameter leaked a Cell.  thanks to moinak ghosh for 
+       1983 or so -- a function without an explicit return that assigns a
+       string to a parameter leaked a Cell.  thanks to moinak ghosh for
        spotting this very subtle one.
 
 Jul 31, 2003:
@@ -226,7 +492,7 @@ Jul 28, 2003:
        radix character in programs and command line arguments regardless of
        the locale; otherwise, the locale should prevail for input and output
        of numbers.  so it's intended to work that way.
-       
+
        i have rescinded the attempt to use strcoll in expanding shorthands in
        regular expressions (cclenter).  its properties are much too
        surprising; for example [a-c] matches aAbBc in locale en_US but abBcC
@@ -290,7 +556,7 @@ Nov 29, 2002:
 Jun 28, 2002:
        modified run/format() and tran/getsval() to do a slightly better
        job on using OFMT for output from print and CONVFMT for other
-       number->string conversions, as promised by posix and done by 
+       number->string conversions, as promised by posix and done by
        gawk and mawk.  there are still places where it doesn't work
        right if CONVFMT is changed; by then the STR attribute of the
        variable has been irrevocably set.  thanks to arnold robbins for
@@ -322,7 +588,7 @@ Feb 10, 2002:
 Jan 1, 2002:
        fflush() or fflush("") flushes all files and pipes.
 
-       length(arrayname) returns number of elements; thanks to 
+       length(arrayname) returns number of elements; thanks to
        arnold robbins for suggestion.
 
        added a makefile.win to make it easier to build on windows.
@@ -372,7 +638,7 @@ July 5, 2000:
 
 May 25, 2000:
        yet another attempt at making 8-bit input work, with another
-       band-aid in b.c (member()), and some (uschar) casts to head 
+       band-aid in b.c (member()), and some (uschar) casts to head
        off potential errors in subscripts (like isdigit).  also
        changed HAT to NCHARS-2.  thanks again to santiago vila.
 
@@ -419,7 +685,7 @@ Apr 21, 1999:
        the test case.)
 
 Apr 16, 1999:
-       with code kindly provided by Bruce Lilly, awk now parses 
+       with code kindly provided by Bruce Lilly, awk now parses
        /=/ and similar constructs more sensibly in more places.
        Bruce also provided some helpful test cases.
 
@@ -476,7 +742,7 @@ Jan 13, 1999:
 
 Oct 19, 1998:
        fixed a couple of bugs in getrec: could fail to update $0
-       after a getline var; because inputFS wasn't initialized, 
+       after a getline var; because inputFS wasn't initialized,
        could split $0 on every character, a misleading diversion.
 
        fixed caching bug in makedfa: LRU was actually removing
@@ -493,6 +759,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
@@ -622,7 +890,7 @@ May 2, 1996:
        input file. (thanks to arnold robbins for inspiration and code).
 
        small fixes to regexpr code:  can now handle []], [[], and
-       variants;  [] is now a syntax error, rather than matching 
+       variants;  [] is now a syntax error, rather than matching
        everything;  [z-a] is now empty, not z.  far from complete
        or correct, however.  (thanks to jeffrey friedl for pointing out
        some awful behaviors.)
diff --git a/contrib/awk/README b/contrib/awk/README
deleted file mode 100644 (file)
index 24aaf90..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************
-Copyright (C) Lucent Technologies 1997
-All Rights Reserved
-
-Permission to use, copy, modify, and distribute this software and
-its documentation for any purpose and without fee is hereby
-granted, provided that the above copyright notice appear in all
-copies and that both that the copyright notice and this
-permission notice and warranty disclaimer appear in supporting
-documentation, and that the name Lucent Technologies or any of
-its entities not be used in advertising or publicity pertaining
-to distribution of the software without specific, written prior
-permission.
-
-LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
-INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
-IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
-SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
-ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-THIS SOFTWARE.
-****************************************************************/
-
-This is the version of awk described in "The AWK Programming Language",
-by Al Aho, Brian Kernighan, and Peter Weinberger
-(Addison-Wesley, 1988, ISBN 0-201-07981-X).
-
-Changes, mostly bug fixes and occasional enhancements, are listed
-in FIXES.  If you distribute this code further, please please please
-distribute FIXES with it.  If you find errors, please report them
-to bwk@cs.princeton.edu.  Thanks.
-
-The program itself is created by
-       make
-which should produce a sequence of messages roughly like this:
-
-       yacc -d awkgram.y
-
-conflicts: 43 shift/reduce, 85 reduce/reduce
-       mv y.tab.c ytab.c
-       mv y.tab.h ytab.h
-       cc -c ytab.c
-       cc -c b.c
-       cc -c main.c
-       cc -c parse.c
-       cc maketab.c -o maketab
-       ./maketab >proctab.c
-       cc -c proctab.c
-       cc -c tran.c
-       cc -c lib.c
-       cc -c run.c
-       cc -c lex.c
-       cc ytab.o b.o main.o parse.o proctab.o tran.o lib.o run.o lex.o -lm
-
-This produces an executable a.out; you will eventually want to
-move this to some place like /usr/bin/awk.
-
-If your system does not have yacc or bison (the GNU
-equivalent), you must compile the pieces manually.  We have
-included yacc output in ytab.c and ytab.h, and backup copies in
-case you overwrite them.  We have also included a copy of
-proctab.c so you do not need to run maketab.
-
-NOTE: This version uses ANSI C, as you should also.  We have
-compiled this without any changes using gcc -Wall and/or local C
-compilers on a variety of systems, but new systems or compilers
-may raise some new complaint; reports of difficulties are
-welcome.
-
-This also compiles with Visual C++ on all flavors of Windows,
-*if* you provide versions of popen and pclose.  The file
-missing95.c contains versions that can be used to get started
-with, though the underlying support has mysterious properties,
-the symptom of which can be truncated pipe output.  Beware.  The
-file makefile.win gives hints on how to proceed; if you run
-vcvars32.bat, it will set up necessary paths and parameters so
-you can subsequently run nmake -f makefile.win.  Beware also that
-when running on Windows under command.com, various quoting
-conventions are different from Unix systems: single quotes won't
-work around arguments, and various characters like % are
-interpreted within double quotes.
-
-This compiles without change on Macintosh OS X using gcc and
-the standard developer tools.
-
-This is also said to compile on Macintosh OS 9 systems, using the
-file "buildmac" provided by Dan Allen (danallen@microsoft.com),
-to whom many thanks.
-
-The version of malloc that comes with some systems is sometimes
-astonishly slow.  If awk seems slow, you might try fixing that.
-More generally, turning on optimization can significantly improve
-awk's speed, perhaps by 1/3 for highest levels.
diff --git a/contrib/awk/README.md b/contrib/awk/README.md
new file mode 100644 (file)
index 0000000..ab6aae1
--- /dev/null
@@ -0,0 +1,108 @@
+# The One True Awk
+
+This is the version of `awk` described in _The AWK Programming Language_,
+by Al Aho, Brian Kernighan, and Peter Weinberger
+(Addison-Wesley, 1988, ISBN 0-201-07981-X).
+
+## Copyright
+
+Copyright (C) Lucent Technologies 1997<br/>
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name Lucent Technologies or any of
+its entities not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+## Distribution and Reporting Problems
+
+Changes, mostly bug fixes and occasional enhancements, are listed
+in `FIXES`.  If you distribute this code further, please please please
+distribute `FIXES` with it.
+
+If you find errors, please report them
+to bwk@cs.princeton.edu.
+Please _also_ open an issue in the GitHub issue tracker, to make
+it easy to track issues.
+Thanks.
+
+## Submitting Pull Requests
+
+Pull requests are welcome. Some guidelines:
+
+* Please do not use functions or facilities that are not standard (e.g.,
+`strlcpy()`, `fpurge()`).
+
+* Please run the test suite and make sure that your changes pass before
+posting the pull request. To do so:
+
+  1. Save the previous version of `awk` somewhere in your path. Call it `nawk` (for example).
+  1. Run `oldawk=nawk make check > check.out 2>&1`.
+  1. Search for `BAD` or `error` in the result. In general, look over it manually to make sure there are no errors.
+
+* Please create the pull request with a request
+to merge into the `staging` branch instead of into the `master` branch.
+This allows us to do testing, and to make any additional edits or changes
+after the merge but before merging to `master`.
+
+## Building
+
+The program itself is created by
+
+       make
+
+which should produce a sequence of messages roughly like this:
+
+       yacc -d awkgram.y
+       conflicts: 43 shift/reduce, 85 reduce/reduce
+       mv y.tab.c ytab.c
+       mv y.tab.h ytab.h
+       cc -c ytab.c
+       cc -c b.c
+       cc -c main.c
+       cc -c parse.c
+       cc maketab.c -o maketab
+       ./maketab >proctab.c
+       cc -c proctab.c
+       cc -c tran.c
+       cc -c lib.c
+       cc -c run.c
+       cc -c lex.c
+       cc ytab.o b.o main.o parse.o proctab.o tran.o lib.o run.o lex.o -lm
+
+This produces an executable `a.out`; you will eventually want to
+move this to some place like `/usr/bin/awk`.
+
+If your system does not have `yacc` or `bison` (the GNU
+equivalent), you need to install one of them first.
+
+NOTE: This version uses ANSI C (C 99), as you should also.  We have
+compiled this without any changes using `gcc -Wall` and/or local C
+compilers on a variety of systems, but new systems or compilers
+may raise some new complaint; reports of difficulties are
+welcome.
+
+This compiles without change on Macintosh OS X using `gcc` and
+the standard developer tools.
+
+The version of `malloc` that comes with some systems is sometimes
+astonishly slow.  If `awk` seems slow, you might try fixing that.
+More generally, turning on optimization can significantly improve
+`awk`'s speed, perhaps by 1/3 for highest levels.
+
+#### Last Updated
+Wed Jan  1 22:44:38 IST 2020
index 6119613..49ab58f 100644 (file)
@@ -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
@@ -49,11 +48,11 @@ matches the pattern.
 Each line is matched against the
 pattern portion of every pattern-action statement;
 the associated action is performed for each matched pattern.
-The file name 
+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,11 +86,11 @@ 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
-A missing 
+A missing
 .BI { " action " }
 means print the line;
 a missing pattern always matches.
@@ -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,12 +203,13 @@ 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
 .B rand
-random number on (0,1)
+random number on [0,1).
 .TP
 .B srand
 sets seed for
@@ -216,16 +217,19 @@ sets seed for
 and returns the previous seed.
 .TP
 .B int
-truncates to an integer value
+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 n ,
+use the rest of the string.
 .TP
 .BI index( s , " t" )
 the position in
@@ -246,14 +250,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 +270,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 +283,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 +293,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 +335,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
@@ -352,14 +366,14 @@ Patterns are arbitrary Boolean combinations
 of regular expressions and
 relational expressions.
 Regular expressions are as in
-.IR egrep ; 
+.IR egrep ;
 see
 .IR grep (1).
 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 +397,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 +423,73 @@ 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
+ordinal number of the current record.
 .TP
-.B FILENAME
-the name of the current input file
-.TP
-.B RS
-input record separator (default newline)
+.B OFMT
+output format for numbers (default
+.BR "%.6g" ).
 .TP
 .B OFS
-output field separator (default blank)
+output field separator (default space).
 .TP
 .B ORS
-output record separator (default newline)
-.TP
-.B OFMT
-output format for numbers (default
-.BR "%.6g" )
+output record separator (default newline).
 .TP
-.B SUBSEP
-separates multiple subscripts (default 034)
+.B RLENGTH
+the length of a string matched by
+.BR match .
 .TP
-.B ARGC
-argument count, assignable
+.B RS
+input record separator (default newline).
+If empty, blank lines separate records.
+If more than one character long,
+.B RS
+is treated as a regular expression, and records are
+separated by text matching the expression.
 .TP
-.B ARGV
-argument array, assignable;
-non-null members are taken as filenames
+.B RSTART
+the start position of a string matched by
+.BR match .
 .TP
-.B ENVIRON
-array of environment variables; subscripts are names.
+.B SUBSEP
+separates multiple subscripts (default 034).
 .PD
 .PP
 Functions may be defined (at the position of a pattern-action statement) thus:
@@ -468,6 +502,16 @@ functions may be called recursively.
 Parameters are local to the function; all other variables are global.
 Thus local variables may be created by providing excess parameters in
 the function definition.
+.SH ENVIRONMENT VARIABLES
+If
+.B POSIXLY_CORRECT
+is set in the environment, then
+.I awk
+follows the POSIX rules for
+.B sub
+and
+.B gsub
+with respect to consecutive backslashes and ampersands.
 .SH EXAMPLES
 .TP
 .EX
@@ -486,7 +530,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,18 +556,20 @@ BEGIN     {       # Simulate echo(1)
 .fi
 .EE
 .SH SEE ALSO
-.IR lex (1), 
+.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;
 to force it to be treated as a string concatenate
 \&\f(CW""\fP to it.
-.br
+.PP
 The scope rules for variables in functions are a botch;
 the syntax is worse.
+.PP
+Only eight-bit characters sets are handled correctly.
index a36cdb1..5a55301 100644 (file)
@@ -23,6 +23,13 @@ THIS SOFTWARE.
 ****************************************************************/
 
 #include <assert.h>
+#include <stdint.h>
+#include <stdbool.h>
+#if __STDC_VERSION__ <= 199901L
+#define noreturn
+#else
+#include <stdnoreturn.h>
+#endif
 
 typedef double Awkfloat;
 
@@ -30,9 +37,14 @@ typedef double       Awkfloat;
 
 typedef        unsigned char uschar;
 
-#define        xfree(a)        { if ((a) != NULL) { free((void *) (a)); (a) = NULL; } }
+#define        xfree(a)        { if ((a) != NULL) { free((void *)(intptr_t)(a)); (a) = NULL; } }
+/*
+ * We sometimes cheat writing read-only pointers to NUL-terminate them
+ * and then put back the original value
+ */
+#define setptr(ptr, a) (*(char *)(intptr_t)(ptr)) = (a)
 
-#define        NN(p)   ((p) ? (p) : "(null)")  /* guaranteed non-null for dprintf 
+#define        NN(p)   ((p) ? (p) : "(null)")  /* guaranteed non-null for dprintf
 */
 #define        DEBUG
 #ifdef DEBUG
@@ -42,12 +54,18 @@ typedef     unsigned char uschar;
 #      define  dprintf(x)
 #endif
 
-extern int     compile_time;   /* 1 if compiling, 0 if running */
-extern int     safe;           /* 0 => unsafe, 1 => safe */
+extern enum compile_states {
+       RUNNING,
+       COMPILING,
+       ERROR_PRINTING
+} compile_time;
+
+extern bool    safe;           /* false => unsafe, true => safe */
 
 #define        RECSIZE (8 * 1024)      /* sets limit on records, fields, etc., etc. */
 extern int     recsize;        /* size of current record, orig RECSIZE */
 
+extern char    EMPTY[];        /* this avoid -Wwritable-strings issues */
 extern char    **FS;
 extern char    **RS;
 extern char    **ORS;
@@ -64,13 +82,11 @@ extern Awkfloat *RLENGTH;
 extern char    *record;        /* points to $0 */
 extern int     lineno;         /* line number in awk program */
 extern int     errorflag;      /* 1 if error has occurred */
-extern int     donefld;        /* 1 if record broken into fields */
-extern int     donerec;        /* 1 if record is valid (no fld has changed */
-extern char    inputFS[];      /* FS at time of input, for field splitting */
-
+extern bool    donefld;        /* true if record broken into fields */
+extern bool    donerec;        /* true if record is valid (no fld has changed */
 extern int     dbg;
 
-extern char    *patbeg;        /* beginning of pattern matched */
+extern const char *patbeg;     /* beginning of pattern matched */
 extern int     patlen;         /* length of pattern matched.  set in b.c */
 
 /* Cell:  all information about a variable or constant */
@@ -81,7 +97,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;
 
@@ -96,9 +113,15 @@ extern Array        *symtab;
 
 extern Cell    *nrloc;         /* NR */
 extern Cell    *fnrloc;        /* FNR */
+extern Cell    *fsloc;         /* FS */
 extern Cell    *nfloc;         /* NF */
+extern Cell    *ofsloc;        /* OFS */
+extern Cell    *orsloc;        /* ORS */
+extern Cell    *rsloc;         /* RS */
 extern Cell    *rstartloc;     /* RSTART */
 extern Cell    *rlengthloc;    /* RLENGTH */
+extern Cell    *subseploc;     /* SUBSEP */
+extern Cell    *symtabloc;     /* SYMTAB */
 
 /* Cell.tval values: */
 #define        NUM     01      /* number value is valid */
@@ -109,6 +132,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 */
@@ -153,7 +178,7 @@ extern Node *nullnode;
 #define CCOPY  6
 #define CCON   5
 #define CTEMP  4
-#define CNAME  3 
+#define CNAME  3
 #define CVAR   2
 #define CFLD   1
 #define        CUNK    0
@@ -203,6 +228,7 @@ extern      int     pairstack[], paircnt;
 
 #define NCHARS (256+3)         /* 256 handles 8-bit chars; 128 does 7-bit */
                                /* watch out in match(), etc. */
+#define        HAT     (NCHARS+2)      /* matches ^ in regular expr */
 #define NSTATES        32
 
 typedef struct rrow {
@@ -216,16 +242,16 @@ typedef struct rrow {
 } rrow;
 
 typedef struct fa {
-       uschar  gototab[NSTATES][NCHARS];
-       uschar  out[NSTATES];
+       unsigned int    **gototab;
+       uschar  *out;
        uschar  *restr;
-       int     *posns[NSTATES];
-       int     anchor;
+       int     **posns;
+       int     state_count;
+       bool    anchor;
        int     use;
        int     initstat;
        int     curstat;
        int     accept;
-       int     reset;
        struct  rrow re[1];     /* variable: actual size set by calling malloc */
 } fa;
 
index 5b5c461..8fc1709 100644 (file)
@@ -32,8 +32,8 @@ int yywrap(void) { return(1); }
 
 Node   *beginloc = 0;
 Node   *endloc = 0;
-int    infunc  = 0;    /* = 1 if in arglist or body of func */
-int    inloop  = 0;    /* = 1 if in while, for, do */
+bool   infunc  = false;        /* = true if in arglist or body of func */
+int    inloop  = 0;    /* >= 1 if in while, for, do; can't be bool, since loops can next */
 char   *curfname = 0;  /* current function name */
 Node   *arglist = 0;   /* list of args for current function */
 %}
@@ -50,9 +50,9 @@ Node  *arglist = 0;   /* list of args for current function */
 %token <i>     NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']'
 %token <i>     ARRAY
 %token <i>     MATCH NOTMATCH MATCHOP
-%token <i>     FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS EMPTYRE
+%token <i>     FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS EMPTYRE ZERO
 %token <i>     AND BOR APPEND EQ GE GT LE LT NE IN
-%token <i>     ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC 
+%token <i>     ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC
 %token <i>     SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT NEXTFILE
 %token <i>     ADD MINUS MULT DIVIDE MOD
 %token <i>     ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ
@@ -71,6 +71,7 @@ Node  *arglist = 0;   /* list of args for current function */
 %type  <i>     do st
 %type  <i>     pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor
 %type  <i>     subop print
+%type  <cp>    string
 
 %right ASGNOP
 %right '?'
@@ -79,14 +80,14 @@ Node        *arglist = 0;   /* list of args for current function */
 %left  AND
 %left  GETLINE
 %nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|'
-%left  ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC 
+%left  ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC
 %left  GSUB IF INDEX LSUBSTR MATCHFCN NEXT NUMBER
 %left  PRINT PRINTF RETURN SPLIT SPRINTF STRING SUB SUBSTR
 %left  REGEXPR VAR VARNF IVAR WHILE '('
 %left  CAT
 %left  '+' '-'
 %left  '*' '/' '%'
-%left  NOT UMINUS
+%left  NOT UMINUS UPLUS
 %right POWER
 %right DECR INCR
 %left  INDIRECT
@@ -181,8 +182,8 @@ pa_stat:
                { beginloc = linkum(beginloc, $3); $$ = 0; }
        | XEND lbrace stmtlist '}'
                { endloc = linkum(endloc, $3); $$ = 0; }
-       | FUNC funcname '(' varlist rparen {infunc++;} lbrace stmtlist '}'
-               { infunc--; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; }
+       | FUNC funcname '(' varlist rparen {infunc = true;} lbrace stmtlist '}'
+               { infunc = false; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; }
        ;
 
 pa_stats:
@@ -238,10 +239,10 @@ pattern:
                        $$ = op3($2, (Node *)1, $1, $3); }
        | pattern IN varname            { $$ = op2(INTEST, $1, makearr($3)); }
        | '(' plist ')' IN varname      { $$ = op2(INTEST, $2, makearr($5)); }
-       | pattern '|' GETLINE var       { 
+       | pattern '|' GETLINE var       {
                        if (safe) SYNTAX("cmd | getline is unsafe");
                        else $$ = op3(GETLINE, $4, itonp($2), $1); }
-       | pattern '|' GETLINE           { 
+       | pattern '|' GETLINE           {
                        if (safe) SYNTAX("cmd | getline is unsafe");
                        else $$ = op3(GETLINE, (Node*)0, itonp($2), $1); }
        | pattern term %prec CAT        { $$ = op2(CAT, $1, $2); }
@@ -292,7 +293,7 @@ rparen:
        ;
 
 simple_stmt:
-         print prarg '|' term          { 
+         print prarg '|' term          {
                        if (safe) SYNTAX("print | is unsafe");
                        else $$ = stat3($1, $2, itonp($3), $4); }
        | print prarg APPEND term       {
@@ -348,6 +349,11 @@ subop:
          SUB | GSUB
        ;
 
+string:
+         STRING
+       | string STRING         { $$ = catstr($1, $2); }
+       ;
+
 term:
          term '/' ASGNOP term          { $$ = op2(DIVEQ, $1, $4); }
        | term '+' term                 { $$ = op2(ADD, $1, $3); }
@@ -357,7 +363,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); }
@@ -394,7 +400,7 @@ term:
        | SPLIT '(' pattern comma varname ')'
                { $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); }  /* default */
        | SPRINTF '(' patlist ')'       { $$ = op1($1, $3); }
-       | STRING                        { $$ = celltonode($1, CCON); }
+       | string                        { $$ = celltonode($1, CCON); }
        | subop '(' reg_expr comma pattern ')'
                { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); }
        | subop '(' pattern comma pattern ')'
@@ -421,7 +427,7 @@ var:
        | varname '[' patlist ']'       { $$ = op2(ARRAY, makearr($1), $3); }
        | IVAR                          { $$ = op1(INDIRECT, celltonode($1, CVAR)); }
        | INDIRECT term                 { $$ = op1(INDIRECT, $2); }
-       ;       
+       ;
 
 varlist:
          /* nothing */         { arglist = $$ = 0; }
index 4f78a60..545fb7d 100644 (file)
@@ -27,14 +27,13 @@ THIS SOFTWARE.
 #define        DEBUG
 
 #include <ctype.h>
+#include <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include "awk.h"
 #include "ytab.h"
 
-#define        HAT     (NCHARS+2)      /* matches ^ in regular expr */
-                               /* NCHARS is 2**n */
 #define MAXLIN 22
 
 #define type(v)                (v)->nobj       /* badly overloaded here */
@@ -62,35 +61,97 @@ int maxsetvec = 0;
 
 int    rtok;           /* next token in current re */
 int    rlxval;
-static uschar  *rlxstr;
-static uschar  *prestr;        /* current position in current re */
-static uschar  *lastre;        /* origin of last re */
+static const uschar    *rlxstr;
+static const uschar    *prestr;        /* current position in current re */
+static const uschar    *lastre;        /* origin of last re */
+static const uschar    *lastatom;      /* origin of last Atom */
+static const uschar    *starttok;
+static const uschar    *basestr;       /* starts with original, replaced during
+                                  repetition processing */
+static const uschar    *firstbasestr;
 
 static int setcnt;
 static int poscnt;
 
-char   *patbeg;
+const char     *patbeg;
 int    patlen;
 
-#define        NFA     20      /* cache this many dynamic fa's */
+#define        NFA     128     /* cache this many dynamic fa's */
 fa     *fatab[NFA];
 int    nfatab  = 0;    /* entries in fatab */
 
-fa *makedfa(const char *s, int anchor) /* returns dfa for reg expr s */
+static int *
+intalloc(size_t n, const char *f)
+{
+       void *p = calloc(n, sizeof(int));
+       if (p == NULL)
+               overflo(f);
+       return p;
+}
+
+static void
+resizesetvec(const char *f)
+{
+       if (maxsetvec == 0)
+               maxsetvec = MAXLIN;
+       else
+               maxsetvec *= 4;
+       setvec = realloc(setvec, maxsetvec * sizeof(*setvec));
+       tmpset = realloc(tmpset, maxsetvec * sizeof(*tmpset));
+       if (setvec == NULL || tmpset == NULL)
+               overflo(f);
+}
+
+static void
+resize_state(fa *f, int state)
+{
+       void *p;
+       int i, new_count;
+
+       if (++state < f->state_count)
+               return;
+
+       new_count = state + 10; /* needs to be tuned */
+
+       p = realloc(f->gototab, new_count * sizeof(f->gototab[0]));
+       if (p == NULL)
+               goto out;
+       f->gototab = p;
+
+       p = realloc(f->out, new_count * sizeof(f->out[0]));
+       if (p == NULL)
+               goto out;
+       f->out = p;
+
+       p = realloc(f->posns, new_count * sizeof(f->posns[0]));
+       if (p == NULL)
+               goto out;
+       f->posns = p;
+
+       for (i = f->state_count; i < new_count; ++i) {
+               f->gototab[i] = calloc(NCHARS, sizeof(**f->gototab));
+               if (f->gototab[i] == NULL)
+                       goto out;
+               f->out[i]  = 0;
+               f->posns[i] = NULL;
+       }
+       f->state_count = new_count;
+       return;
+out:
+       overflo(__func__);
+}
+
+fa *makedfa(const char *s, bool anchor)        /* returns dfa for reg expr s */
 {
        int i, use, nuse;
        fa *pfa;
        static int now = 1;
 
-       if (setvec == 0) {      /* first time through any RE */
-               maxsetvec = MAXLIN;
-               setvec = (int *) malloc(maxsetvec * sizeof(int));
-               tmpset = (int *) malloc(maxsetvec * sizeof(int));
-               if (setvec == 0 || tmpset == 0)
-                       overflo("out of space initializing makedfa");
+       if (setvec == NULL) {   /* first time through any RE */
+               resizesetvec(__func__);
        }
 
-       if (compile_time)       /* a constant for sure */
+       if (compile_time != RUNNING)    /* a constant for sure */
                return mkdfa(s, anchor);
        for (i = 0; i < nfatab; i++)    /* is it there already? */
                if (fatab[i]->anchor == anchor
@@ -118,12 +179,14 @@ fa *makedfa(const char *s, int anchor)    /* returns dfa for reg expr s */
        return pfa;
 }
 
-fa *mkdfa(const char *s, int anchor)   /* does the real work of making a dfa */
-                               /* anchor = 1 for anchored matches, else 0 */
+fa *mkdfa(const char *s, bool anchor)  /* does the real work of making a dfa */
+                               /* anchor = true for anchored matches, else false */
 {
        Node *p, *p1;
        fa *f;
 
+       firstbasestr = (const uschar *) s;
+       basestr = firstbasestr;
        p = reparse(s);
        p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p);
                /* put ALL STAR in front of reg.  exp. */
@@ -132,44 +195,45 @@ fa *mkdfa(const char *s, int anchor)      /* does the real work of making a dfa */
 
        poscnt = 0;
        penter(p1);     /* enter parent pointers and leaf indices */
-       if ((f = (fa *) calloc(1, sizeof(fa) + poscnt*sizeof(rrow))) == NULL)
-               overflo("out of space for fa");
+       if ((f = calloc(1, sizeof(fa) + poscnt * sizeof(rrow))) == NULL)
+               overflo(__func__);
        f->accept = poscnt-1;   /* penter has computed number of positions in re */
        cfoll(f, p1);   /* set up follow sets */
        freetr(p1);
-       if ((f->posns[0] = (int *) calloc(1, *(f->re[0].lfollow)*sizeof(int))) == NULL)
-                       overflo("out of space in makedfa");
-       if ((f->posns[1] = (int *) calloc(1, sizeof(int))) == NULL)
-               overflo("out of space in makedfa");
+       resize_state(f, 1);
+       f->posns[0] = intalloc(*(f->re[0].lfollow), __func__);
+       f->posns[1] = intalloc(1, __func__);
        *f->posns[1] = 0;
        f->initstat = makeinit(f, anchor);
        f->anchor = anchor;
        f->restr = (uschar *) tostring(s);
+       if (firstbasestr != basestr) {
+               if (basestr)
+                       xfree(basestr);
+       }
        return f;
 }
 
-int makeinit(fa *f, int anchor)
+int makeinit(fa *f, bool anchor)
 {
        int i, k;
 
        f->curstat = 2;
        f->out[2] = 0;
-       f->reset = 0;
        k = *(f->re[0].lfollow);
-       xfree(f->posns[2]);                     
-       if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
-               overflo("out of space in makeinit");
-       for (i=0; i <= k; i++) {
+       xfree(f->posns[2]);
+       f->posns[2] = intalloc(k + 1,  __func__);
+       for (i = 0; i <= k; i++) {
                (f->posns[2])[i] = (f->re[0].lfollow)[i];
        }
        if ((f->posns[2])[1] == f->accept)
                f->out[2] = 1;
-       for (i=0; i < NCHARS; i++)
+       for (i = 0; i < NCHARS; i++)
                f->gototab[2][i] = 0;
        f->curstat = cgoto(f, 2, HAT);
        if (anchor) {
                *f->posns[2] = k-1;     /* leave out position 0 */
-               for (i=0; i < k; i++) {
+               for (i = 0; i < k; i++) {
                        (f->posns[0])[i] = (f->posns[2])[i];
                }
 
@@ -199,6 +263,8 @@ void penter(Node *p)        /* set up parent pointers and leaf indices */
                parent(left(p)) = p;
                parent(right(p)) = p;
                break;
+       case ZERO:
+               break;
        default:        /* can't happen */
                FATAL("can't happen: unknown type %d in penter", type(p));
                break;
@@ -213,6 +279,7 @@ void freetr(Node *p)        /* free parse tree */
                xfree(p);
                break;
        UNARY
+       case ZERO:
                freetr(left(p));
                xfree(p);
                break;
@@ -231,13 +298,13 @@ void freetr(Node *p)      /* free parse tree */
 /* in the parsing of regular expressions, metacharacters like . have */
 /* to be seen literally;  \056 is not a metacharacter. */
 
-int hexstr(uschar **pp)        /* find and eval hex string at pp, return new p */
+int hexstr(const uschar **pp)  /* find and eval hex string at pp, return new p */
 {                      /* only pick up one 8-bit byte (2 chars) */
-       uschar *p;
+       const uschar *p;
        int n = 0;
        int i;
 
-       for (i = 0, p = (uschar *) *pp; i < 2 && isxdigit(*p); i++, p++) {
+       for (i = 0, p = *pp; i < 2 && isxdigit(*p); i++, p++) {
                if (isdigit(*p))
                        n = 16 * n + *p - '0';
                else if (*p >= 'a' && *p <= 'f')
@@ -245,16 +312,16 @@ int hexstr(uschar **pp)   /* find and eval hex string at pp, return new p */
                else if (*p >= 'A' && *p <= 'F')
                        n = 16 * n + *p - 'A' + 10;
        }
-       *pp = (uschar *) p;
+       *pp = p;
        return n;
 }
 
 #define isoctdigit(c) ((c) >= '0' && (c) <= '7')       /* multiple use of arg */
 
-int quoted(uschar **pp)        /* pick up next thing after a \\ */
+int quoted(const uschar **pp)  /* pick up next thing after a \\ */
                        /* and increment *pp */
 {
-       uschar *p = *pp;
+       const uschar *p = *pp;
        int c;
 
        if ((c = *p++) == 't')
@@ -267,6 +334,10 @@ int quoted(uschar **pp)    /* pick up next thing after a \\ */
                c = '\r';
        else if (c == 'b')
                c = '\b';
+       else if (c == 'v')
+               c = '\v';
+       else if (c == 'a')
+               c = '\a';
        else if (c == '\\')
                c = '\\';
        else if (c == 'x') {    /* hexadecimal goo follows */
@@ -288,13 +359,13 @@ int quoted(uschar **pp)   /* pick up next thing after a \\ */
 char *cclenter(const char *argp)       /* add a character class */
 {
        int i, c, c2;
-       uschar *p = (uschar *) argp;
-       uschar *op, *bp;
-       static uschar *buf = 0;
+       const uschar *op, *p = (const uschar *) argp;
+       uschar *bp;
+       static uschar *buf = NULL;
        static int bufsz = 100;
 
        op = p;
-       if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL)
+       if (buf == NULL && (buf = malloc(bufsz)) == NULL)
                FATAL("out of space for character class [%.10s...] 1", p);
        bp = buf;
        for (i = 0; (c = *p++) != 0; ) {
@@ -333,7 +404,7 @@ char *cclenter(const char *argp)    /* add a character class */
 
 void overflo(const char *s)
 {
-       FATAL("regular expression too big: %.30s...", s);
+       FATAL("regular expression too big: out of space in %.30s...", s);
 }
 
 void cfoll(fa *f, Node *v)     /* enter follow set of each leaf of vertex v into lfollow[leaf] */
@@ -347,18 +418,13 @@ void cfoll(fa *f, Node *v)        /* enter follow set of each leaf of vertex v into lfo
                f->re[info(v)].ltype = type(v);
                f->re[info(v)].lval.np = right(v);
                while (f->accept >= maxsetvec) {        /* guessing here! */
-                       maxsetvec *= 4;
-                       setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
-                       tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
-                       if (setvec == 0 || tmpset == 0)
-                               overflo("out of space in cfoll()");
+                       resizesetvec(__func__);
                }
                for (i = 0; i <= f->accept; i++)
                        setvec[i] = 0;
                setcnt = 0;
                follow(v);      /* computes setvec and setcnt */
-               if ((p = (int *) calloc(1, (setcnt+1)*sizeof(int))) == NULL)
-                       overflo("out of space building follow set");
+               p = intalloc(setcnt + 1, __func__);
                f->re[info(v)].lfollow = p;
                *p = setcnt;
                for (i = f->accept; i >= 0; i--)
@@ -373,6 +439,8 @@ void cfoll(fa *f, Node *v)  /* enter follow set of each leaf of vertex v into lfo
                cfoll(f,left(v));
                cfoll(f,right(v));
                break;
+       case ZERO:
+               break;
        default:        /* can't happen */
                FATAL("can't happen: unknown type %d in cfoll", type(v));
        }
@@ -388,11 +456,7 @@ int first(Node *p) /* collects initially active leaves of p into setvec */
        LEAF
                lp = info(p);   /* look for high-water mark of subscripts */
                while (setcnt >= maxsetvec || lp >= maxsetvec) {        /* guessing here! */
-                       maxsetvec *= 4;
-                       setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
-                       tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
-                       if (setvec == 0 || tmpset == 0)
-                               overflo("out of space in first()");
+                       resizesetvec(__func__);
                }
                if (type(p) == EMPTYRE) {
                        setvec[lp] = 0;
@@ -404,9 +468,10 @@ int first(Node *p) /* collects initially active leaves of p into setvec */
                }
                if (type(p) == CCL && (*(char *) right(p)) == '\0')
                        return(0);              /* empty CCL */
-               else return(1);
+               return(1);
        case PLUS:
-               if (first(left(p)) == 0) return(0);
+               if (first(left(p)) == 0)
+                       return(0);
                return(1);
        case STAR:
        case QUEST:
@@ -419,6 +484,8 @@ int first(Node *p)  /* collects initially active leaves of p into setvec */
                b = first(right(p));
                if (first(left(p)) == 0 || b == 0) return(0);
                return(1);
+       case ZERO:
+               return 0;
        }
        FATAL("can't happen: unknown type %d in first", type(p));       /* can't happen */
        return(-1);
@@ -457,7 +524,7 @@ void follow(Node *v)        /* collects leaves that can follow v into setvec */
 
 int member(int c, const char *sarg)    /* is c in s? */
 {
-       uschar *s = (uschar *) sarg;
+       const uschar *s = (const uschar *) sarg;
 
        while (*s)
                if (c == *s++)
@@ -468,9 +535,11 @@ int member(int c, const char *sarg)        /* is c in s? */
 int match(fa *f, const char *p0)       /* shortest match ? */
 {
        int s, ns;
-       uschar *p = (uschar *) p0;
+       const uschar *p = (const uschar *) p0;
+
+       s = f->initstat;
+       assert (s < f->state_count);
 
-       s = f->reset ? makeinit(f,0) : f->initstat;
        if (f->out[s])
                return(1);
        do {
@@ -488,17 +557,13 @@ int match(fa *f, const char *p0)  /* shortest match ? */
 int pmatch(fa *f, const char *p0)      /* longest match, for sub */
 {
        int s, ns;
-       uschar *p = (uschar *) p0;
-       uschar *q;
-       int i, k;
+       const uschar *p = (const uschar *) p0;
+       const uschar *q;
 
-       /* s = f->reset ? makeinit(f,1) : f->initstat; */
-       if (f->reset) {
-               f->initstat = s = makeinit(f,1);
-       } else {
-               s = f->initstat;
-       }
-       patbeg = (char *) p;
+       s = f->initstat;
+       assert(s < f->state_count);
+
+       patbeg = (const char *)p;
        patlen = -1;
        do {
                q = p;
@@ -510,9 +575,12 @@ int pmatch(fa *f, const char *p0)  /* longest match, for sub */
                                s = ns;
                        else
                                s = cgoto(f, s, *q);
+
+                       assert(s < f->state_count);
+
                        if (s == 1) {   /* no transition */
                                if (patlen >= 0) {
-                                       patbeg = (char *) p;
+                                       patbeg = (const char *) p;
                                        return(1);
                                }
                                else
@@ -522,41 +590,25 @@ int pmatch(fa *f, const char *p0) /* longest match, for sub */
                if (f->out[s])
                        patlen = q-p-1; /* don't count $ */
                if (patlen >= 0) {
-                       patbeg = (char *) p;
+                       patbeg = (const char *) p;
                        return(1);
                }
        nextin:
                s = 2;
-               if (f->reset) {
-                       for (i = 2; i <= f->curstat; i++)
-                               xfree(f->posns[i]);
-                       k = *f->posns[0];                       
-                       if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
-                               overflo("out of space in pmatch");
-                       for (i = 0; i <= k; i++)
-                               (f->posns[2])[i] = (f->posns[0])[i];
-                       f->initstat = f->curstat = 2;
-                       f->out[2] = f->out[0];
-                       for (i = 0; i < NCHARS; i++)
-                               f->gototab[2][i] = 0;
-               }
-       } while (*p++ != 0);
+       } while (*p++);
        return (0);
 }
 
 int nematch(fa *f, const char *p0)     /* non-empty match, for sub */
 {
        int s, ns;
-       uschar *p = (uschar *) p0;
-       uschar *q;
-       int i, k;
+       const uschar *p = (const uschar *) p0;
+       const uschar *q;
 
-       /* s = f->reset ? makeinit(f,1) : f->initstat; */
-       if (f->reset) {
-               f->initstat = s = makeinit(f,1);
-       } else {
-               s = f->initstat;
-       }
+       s = f->initstat;
+       assert(s < f->state_count);
+
+       patbeg = (const char *)p;
        patlen = -1;
        while (*p) {
                q = p;
@@ -570,7 +622,7 @@ int nematch(fa *f, const char *p0)  /* non-empty match, for sub */
                                s = cgoto(f, s, *q);
                        if (s == 1) {   /* no transition */
                                if (patlen > 0) {
-                                       patbeg = (char *) p;
+                                       patbeg = (const char *) p;
                                        return(1);
                                } else
                                        goto nnextin;   /* no nonempty match */
@@ -579,35 +631,110 @@ int nematch(fa *f, const char *p0)       /* non-empty match, for sub */
                if (f->out[s])
                        patlen = q-p-1; /* don't count $ */
                if (patlen > 0 ) {
-                       patbeg = (char *) p;
+                       patbeg = (const char *) p;
                        return(1);
                }
        nnextin:
                s = 2;
-               if (f->reset) {
-                       for (i = 2; i <= f->curstat; i++)
-                               xfree(f->posns[i]);
-                       k = *f->posns[0];                       
-                       if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
-                               overflo("out of state space");
-                       for (i = 0; i <= k; i++)
-                               (f->posns[2])[i] = (f->posns[0])[i];
-                       f->initstat = f->curstat = 2;
-                       f->out[2] = f->out[0];
-                       for (i = 0; i < NCHARS; i++)
-                               f->gototab[2][i] = 0;
-               }
                p++;
        }
        return (0);
 }
 
+
+/*
+ * NAME
+ *     fnematch
+ *
+ * DESCRIPTION
+ *     A stream-fed version of nematch which transfers characters to a
+ *     null-terminated buffer. All characters up to and including the last
+ *     character of the matching text or EOF are placed in the buffer. If
+ *     a match is found, patbeg and patlen are set appropriately.
+ *
+ * RETURN VALUES
+ *     false    No match found.
+ *     true     Match found.
+ */
+
+bool fnematch(fa *pfa, FILE *f, char **pbuf, int *pbufsize, int quantum)
+{
+       char *buf = *pbuf;
+       int bufsize = *pbufsize;
+       int c, i, j, k, ns, s;
+
+       s = pfa->initstat;
+       patlen = 0;
+
+       /*
+        * All indices relative to buf.
+        * i <= j <= k <= bufsize
+        *
+        * i: origin of active substring
+        * j: current character
+        * k: destination of next getc()
+        */
+       i = -1, k = 0;
+        do {
+               j = i++;
+               do {
+                       if (++j == k) {
+                               if (k == bufsize)
+                                       if (!adjbuf((char **) &buf, &bufsize, bufsize+1, quantum, 0, "fnematch"))
+                                               FATAL("stream '%.30s...' too long", buf);
+                               buf[k++] = (c = getc(f)) != EOF ? c : 0;
+                       }
+                       c = buf[j];
+                       /* assert(c < NCHARS); */
+
+                       if ((ns = pfa->gototab[s][c]) != 0)
+                               s = ns;
+                       else
+                               s = cgoto(pfa, s, c);
+
+                       if (pfa->out[s]) {      /* final state */
+                               patlen = j - i + 1;
+                               if (c == 0)     /* don't count $ */
+                                       patlen--;
+                       }
+               } while (buf[j] && s != 1);
+               s = 2;
+       } while (buf[i] && !patlen);
+
+       /* adjbuf() may have relocated a resized buffer. Inform the world. */
+       *pbuf = buf;
+       *pbufsize = bufsize;
+
+       if (patlen) {
+               patbeg = (char *) buf + i;
+               /*
+                * Under no circumstances is the last character fed to
+                * the automaton part of the match. It is EOF's nullbyte,
+                * or it sent the automaton into a state with no further
+                * transitions available (s==1), or both. Room for a
+                * terminating nullbyte is guaranteed.
+                *
+                * ungetc any chars after the end of matching text
+                * (except for EOF's nullbyte, if present) and null
+                * terminate the buffer.
+                */
+               do
+                       if (buf[--k] && ungetc(buf[k], f) == EOF)
+                               FATAL("unable to ungetc '%c'", buf[k]);
+               while (k > i + patlen);
+               buf[k] = '\0';
+               return true;
+       }
+       else
+               return false;
+}
+
 Node *reparse(const char *p)   /* parses regular expression pointed to by p */
 {                      /* uses relex() to scan regular expression */
        Node *np;
 
        dprintf( ("reparse <%s>\n", p) );
-       lastre = prestr = (uschar *) p; /* prestr points to string to be parsed */
+       lastre = prestr = (const uschar *) p;   /* prestr points to string to be parsed */
        rtok = relex();
        /* GNU compatibility: an empty regexp matches anything */
        if (rtok == '\0') {
@@ -628,9 +755,11 @@ Node *regexp(void) /* top-level parse of reg expr */
 Node *primary(void)
 {
        Node *np;
+       int savelastatom;
 
        switch (rtok) {
        case CHAR:
+               lastatom = starttok;
                np = op2(CHAR, NIL, itonp(rlxval));
                rtok = relex();
                return (unary(np));
@@ -639,16 +768,19 @@ Node *primary(void)
                return (unary(op2(ALL, NIL, NIL)));
        case EMPTYRE:
                rtok = relex();
-               return (unary(op2(ALL, NIL, NIL)));
+               return (unary(op2(EMPTYRE, NIL, NIL)));
        case DOT:
+               lastatom = starttok;
                rtok = relex();
                return (unary(op2(DOT, NIL, NIL)));
        case CCL:
-               np = op2(CCL, NIL, (Node*) cclenter((char *) rlxstr));
+               np = op2(CCL, NIL, (Node*) cclenter((const char *) rlxstr));
+               lastatom = starttok;
                rtok = relex();
                return (unary(np));
        case NCCL:
-               np = op2(NCCL, NIL, (Node *) cclenter((char *) rlxstr));
+               np = op2(NCCL, NIL, (Node *) cclenter((const char *) rlxstr));
+               lastatom = starttok;
                rtok = relex();
                return (unary(np));
        case '^':
@@ -658,6 +790,8 @@ Node *primary(void)
                rtok = relex();
                return (unary(op2(CHAR, NIL, NIL)));
        case '(':
+               lastatom = starttok;
+               savelastatom = starttok - basestr; /* Retain over recursion */
                rtok = relex();
                if (rtok == ')') {      /* special pleading for () */
                        rtok = relex();
@@ -665,6 +799,7 @@ Node *primary(void)
                }
                np = regexp();
                if (rtok == ')') {
+                       lastatom = basestr + savelastatom; /* Restore */
                        rtok = relex();
                        return (unary(np));
                }
@@ -679,8 +814,12 @@ Node *primary(void)
 Node *concat(Node *np)
 {
        switch (rtok) {
-       case CHAR: case DOT: case ALL: case EMPTYRE: case CCL: case NCCL: case '$': case '(':
+       case CHAR: case DOT: case ALL: case CCL: case NCCL: case '$': case '(':
                return (concat(op2(CAT, np, primary())));
+       case EMPTYRE:
+               rtok = relex();
+               return (concat(op2(CAT, op2(CCL, NIL, (Node *) tostring("")),
+                               primary())));
        }
        return (np);
 }
@@ -706,6 +845,9 @@ Node *unary(Node *np)
        case QUEST:
                rtok = relex();
                return (unary(op2(QUEST, np, NIL)));
+       case ZERO:
+               rtok = relex();
+               return (unary(op2(ZERO, np, NIL)));
        default:
                return (np);
        }
@@ -741,7 +883,7 @@ int (xisblank)(int c)
 
 #endif
 
-struct charclass {
+static const struct charclass {
        const char *cc_name;
        int cc_namelen;
        int (*cc_func)(int);
@@ -749,7 +891,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
@@ -765,16 +907,135 @@ struct charclass {
        { NULL,         0,      NULL },
 };
 
+#define REPEAT_SIMPLE          0
+#define REPEAT_PLUS_APPENDED   1
+#define REPEAT_WITH_Q          2
+#define REPEAT_ZERO            3
+
+static int
+replace_repeat(const uschar *reptok, int reptoklen, const uschar *atom,
+              int atomlen, int firstnum, int secondnum, int special_case)
+{
+       int i, j;
+       uschar *buf = 0;
+       int ret = 1;
+       int init_q = (firstnum == 0);           /* first added char will be ? */
+       int n_q_reps = secondnum-firstnum;      /* m>n, so reduce until {1,m-n} left  */
+       int prefix_length = reptok - basestr;   /* prefix includes first rep    */
+       int suffix_length = strlen((const char *) reptok) - reptoklen;  /* string after rep specifier   */
+       int size = prefix_length +  suffix_length;
+
+       if (firstnum > 1) {     /* add room for reps 2 through firstnum */
+               size += atomlen*(firstnum-1);
+       }
+
+       /* Adjust size of buffer for special cases */
+       if (special_case == REPEAT_PLUS_APPENDED) {
+               size++;         /* for the final + */
+       } else if (special_case == REPEAT_WITH_Q) {
+               size += init_q + (atomlen+1)* n_q_reps;
+       } else if (special_case == REPEAT_ZERO) {
+               size += 2;      /* just a null ERE: () */
+       }
+       if ((buf = malloc(size + 1)) == NULL)
+               FATAL("out of space in reg expr %.10s..", lastre);
+       memcpy(buf, basestr, prefix_length);    /* copy prefix  */
+       j = prefix_length;
+       if (special_case == REPEAT_ZERO) {
+               j -= atomlen;
+               buf[j++] = '(';
+               buf[j++] = ')';
+       }
+       for (i = 1; i < firstnum; i++) {        /* copy x reps  */
+               memcpy(&buf[j], atom, atomlen);
+               j += atomlen;
+       }
+       if (special_case == REPEAT_PLUS_APPENDED) {
+               buf[j++] = '+';
+       } else if (special_case == REPEAT_WITH_Q) {
+               if (init_q)
+                       buf[j++] = '?';
+               for (i = init_q; i < n_q_reps; i++) {   /* copy x? reps */
+                       memcpy(&buf[j], atom, atomlen);
+                       j += atomlen;
+                       buf[j++] = '?';
+               }
+       }
+       memcpy(&buf[j], reptok+reptoklen, suffix_length);
+       if (special_case == REPEAT_ZERO) {
+               buf[j+suffix_length] = '\0';
+       } else {
+               buf[size] = '\0';
+       }
+       /* free old basestr */
+       if (firstbasestr != basestr) {
+               if (basestr)
+                       xfree(basestr);
+       }
+       basestr = buf;
+       prestr  = buf + prefix_length;
+       if (special_case == REPEAT_ZERO) {
+               prestr  -= atomlen;
+               ret++;
+       }
+       return ret;
+}
+
+static int repeat(const uschar *reptok, int reptoklen, const uschar *atom,
+                 int atomlen, int firstnum, int secondnum)
+{
+       /*
+          In general, the repetition specifier or "bound" is replaced here
+          by an equivalent ERE string, repeating the immediately previous atom
+          and appending ? and + as needed. Note that the first copy of the
+          atom is left in place, except in the special_case of a zero-repeat
+          (i.e., {0}).
+        */
+       if (secondnum < 0) {    /* means {n,} -> repeat n-1 times followed by PLUS */
+               if (firstnum < 2) {
+                       /* 0 or 1: should be handled before you get here */
+                       FATAL("internal error");
+               } else {
+                       return replace_repeat(reptok, reptoklen, atom, atomlen,
+                               firstnum, secondnum, REPEAT_PLUS_APPENDED);
+               }
+       } else if (firstnum == secondnum) {     /* {n} or {n,n} -> simply repeat n-1 times */
+               if (firstnum == 0) {    /* {0} or {0,0} */
+                       /* This case is unusual because the resulting
+                          replacement string might actually be SMALLER than
+                          the original ERE */
+                       return replace_repeat(reptok, reptoklen, atom, atomlen,
+                                       firstnum, secondnum, REPEAT_ZERO);
+               } else {                /* (firstnum >= 1) */
+                       return replace_repeat(reptok, reptoklen, atom, atomlen,
+                                       firstnum, secondnum, REPEAT_SIMPLE);
+               }
+       } else if (firstnum < secondnum) {      /* {n,m} -> repeat n-1 times then alternate  */
+               /*  x{n,m}  =>  xx...x{1, m-n+1}  =>  xx...x?x?x?..x?   */
+               return replace_repeat(reptok, reptoklen, atom, atomlen,
+                                       firstnum, secondnum, REPEAT_WITH_Q);
+       } else {        /* Error - shouldn't be here (n>m) */
+               FATAL("internal error");
+       }
+       return 0;
+}
 
 int relex(void)                /* lexical analyzer for reparse */
 {
        int c, n;
        int cflag;
-       static uschar *buf = 0;
+       static uschar *buf = NULL;
        static int bufsz = 100;
        uschar *bp;
-       struct charclass *cc;
+       const struct charclass *cc;
        int i;
+       int num, m;
+       bool commafound, digitfound;
+       const uschar *startreptok;
+       static int parens = 0;
+
+rescan:
+       starttok = prestr;
 
        switch (c = *prestr++) {
        case '|': return OR;
@@ -785,17 +1046,26 @@ int relex(void)          /* lexical analyzer for reparse */
        case '\0': prestr--; return '\0';
        case '^':
        case '$':
+               return c;
        case '(':
-       case ')':
+               parens++;
                return c;
+       case ')':
+               if (parens) {
+                       parens--;
+                       return c;
+               }
+               /* unmatched close parenthesis; per POSIX, treat as literal */
+               rlxval = c;
+               return CHAR;
        case '\\':
                rlxval = quoted(&prestr);
                return CHAR;
        default:
                rlxval = c;
                return CHAR;
-       case '[': 
-               if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL)
+       case '[':
+               if (buf == NULL && (buf = malloc(bufsz)) == NULL)
                        FATAL("out of space in reg expr %.10s..", lastre);
                bp = buf;
                if (*prestr == '^') {
@@ -823,7 +1093,15 @@ int relex(void)           /* lexical analyzer for reparse */
                                if (cc->cc_name != NULL && prestr[1 + cc->cc_namelen] == ':' &&
                                    prestr[2 + cc->cc_namelen] == ']') {
                                        prestr += cc->cc_namelen + 3;
-                                       for (i = 0; i < NCHARS; i++) {
+                                       /*
+                                        * BUG: We begin at 1, instead of 0, since we
+                                        * would otherwise prematurely terminate the
+                                        * string for classes like [[:cntrl:]]. This
+                                        * means that we can't match the NUL character,
+                                        * not without first adapting the entire
+                                        * program to track each string's length.
+                                        */
+                                       for (i = 1; i <= UCHAR_MAX; i++) {
                                                if (!adjbuf((char **) &buf, &bufsz, bp-buf+1, 100, (char **) &bp, "relex2"))
                                                    FATAL("out of space for reg expr %.10s...", lastre);
                                                if (cc->cc_func(i)) {
@@ -833,6 +1111,40 @@ int relex(void)           /* lexical analyzer for reparse */
                                        }
                                } else
                                        *bp++ = c;
+                       } else if (c == '[' && *prestr == '.') {
+                               char collate_char;
+                               prestr++;
+                               collate_char = *prestr++;
+                               if (*prestr == '.' && prestr[1] == ']') {
+                                       prestr += 2;
+                                       /* Found it: map via locale TBD: for
+                                          now, simply return this char.  This
+                                          is sufficient to pass conformance
+                                          test awk.ex 156
+                                        */
+                                       if (*prestr == ']') {
+                                               prestr++;
+                                               rlxval = collate_char;
+                                               return CHAR;
+                                       }
+                               }
+                       } else if (c == '[' && *prestr == '=') {
+                               char equiv_char;
+                               prestr++;
+                               equiv_char = *prestr++;
+                               if (*prestr == '=' && prestr[1] == ']') {
+                                       prestr += 2;
+                                       /* Found it: map via locale TBD: for now
+                                          simply return this char. This is
+                                          sufficient to pass conformance test
+                                          awk.ex 156
+                                        */
+                                       if (*prestr == ']') {
+                                               prestr++;
+                                               rlxval = equiv_char;
+                                               return CHAR;
+                                       }
+                               }
                        } else if (c == '\0') {
                                FATAL("nonterminated character class %.20s", lastre);
                        } else if (bp == buf) { /* 1st char is special */
@@ -847,25 +1159,93 @@ int relex(void)          /* lexical analyzer for reparse */
                        } else
                                *bp++ = c;
                }
+               break;
+       case '{':
+               if (isdigit(*(prestr))) {
+                       num = 0;        /* Process as a repetition */
+                       n = -1; m = -1;
+                       commafound = false;
+                       digitfound = false;
+                       startreptok = prestr-1;
+                       /* Remember start of previous atom here ? */
+               } else {                /* just a { char, not a repetition */
+                       rlxval = c;
+                       return CHAR;
+                }
+               for (; ; ) {
+                       if ((c = *prestr++) == '}') {
+                               if (commafound) {
+                                       if (digitfound) { /* {n,m} */
+                                               m = num;
+                                               if (m < n)
+                                                       FATAL("illegal repetition expression: class %.20s",
+                                                               lastre);
+                                               if (n == 0 && m == 1) {
+                                                       return QUEST;
+                                               }
+                                       } else {        /* {n,} */
+                                               if (n == 0)
+                                                       return STAR;
+                                               else if (n == 1)
+                                                       return PLUS;
+                                       }
+                               } else {
+                                       if (digitfound) { /* {n} same as {n,n} */
+                                               n = num;
+                                               m = num;
+                                       } else {        /* {} */
+                                               FATAL("illegal repetition expression: class %.20s",
+                                                       lastre);
+                                       }
+                               }
+                               if (repeat(starttok, prestr-starttok, lastatom,
+                                          startreptok - lastatom, n, m) > 0) {
+                                       if (n == 0 && m == 0) {
+                                               return ZERO;
+                                       }
+                                       /* must rescan input for next token */
+                                       goto rescan;
+                               }
+                               /* Failed to replace: eat up {...} characters
+                                  and treat like just PLUS */
+                               return PLUS;
+                       } else if (c == '\0') {
+                               FATAL("nonterminated character class %.20s",
+                                       lastre);
+                       } else if (isdigit(c)) {
+                               num = 10 * num + c - '0';
+                               digitfound = true;
+                       } else if (c == ',') {
+                               if (commafound)
+                                       FATAL("illegal repetition expression: class %.20s",
+                                               lastre);
+                               /* looking for {n,} or {n,m} */
+                               commafound = true;
+                               n = num;
+                               digitfound = false; /* reset */
+                               num = 0;
+                       } else {
+                               FATAL("illegal repetition expression: class %.20s",
+                                       lastre);
+                       }
+               }
+               break;
        }
 }
 
 int cgoto(fa *f, int s, int c)
 {
-       int i, j, k;
        int *p, *q;
+       int i, j, k;
 
        assert(c == HAT || c < NCHARS);
        while (f->accept >= maxsetvec) {        /* guessing here! */
-               maxsetvec *= 4;
-               setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
-               tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
-               if (setvec == 0 || tmpset == 0)
-                       overflo("out of space in cgoto()");
+               resizesetvec(__func__);
        }
        for (i = 0; i <= f->accept; i++)
                setvec[i] = 0;
        setcnt = 0;
+       resize_state(f, s);
        /* compute positions of gototab[s,c] into setvec */
        p = f->posns[s];
        for (i = 1; i <= *p; i++) {
@@ -879,11 +1259,7 @@ int cgoto(fa *f, int s, int c)
                                q = f->re[p[i]].lfollow;
                                for (j = 1; j <= *q; j++) {
                                        if (q[j] >= maxsetvec) {
-                                               maxsetvec *= 4;
-                                               setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
-                                               tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
-                                               if (setvec == 0 || tmpset == 0)
-                                                       overflo("cgoto overflow");
+                                               resizesetvec(__func__);
                                        }
                                        if (setvec[q[j]] == 0) {
                                                setcnt++;
@@ -900,6 +1276,7 @@ int cgoto(fa *f, int s, int c)
                if (setvec[i]) {
                        tmpset[j++] = i;
                }
+       resize_state(f, f->curstat > s ? f->curstat : s);
        /* tmpset == previous state? */
        for (i = 1; i <= f->curstat; i++) {
                p = f->posns[i];
@@ -909,27 +1286,23 @@ int cgoto(fa *f, int s, int c)
                        if (tmpset[j] != p[j])
                                goto different;
                /* setvec is state i */
-               f->gototab[s][c] = i;
+               if (c != HAT)
+                       f->gototab[s][c] = i;
                return i;
          different:;
        }
 
        /* add tmpset to current set of states */
-       if (f->curstat >= NSTATES-1) {
-               f->curstat = 2;
-               f->reset = 1;
-               for (i = 2; i < NSTATES; i++)
-                       xfree(f->posns[i]);
-       } else
-               ++(f->curstat);
+       ++(f->curstat);
+       resize_state(f, f->curstat);
        for (i = 0; i < NCHARS; i++)
                f->gototab[f->curstat][i] = 0;
        xfree(f->posns[f->curstat]);
-       if ((p = (int *) calloc(1, (setcnt+1)*sizeof(int))) == NULL)
-               overflo("out of space in cgoto");
+       p = intalloc(setcnt + 1, __func__);
 
        f->posns[f->curstat] = p;
-       f->gototab[s][c] = f->curstat;
+       if (c != HAT)
+               f->gototab[s][c] = f->curstat;
        for (i = 0; i <= setcnt; i++)
                p[i] = tmpset[i];
        if (setvec[f->accept])
@@ -946,13 +1319,18 @@ void freefa(fa *f)       /* free a finite automaton */
 
        if (f == NULL)
                return;
+       for (i = 0; i < f->state_count; i++)
+               xfree(f->gototab[i])
        for (i = 0; i <= f->curstat; i++)
                xfree(f->posns[i]);
        for (i = 0; i <= f->accept; i++) {
                xfree(f->re[i].lfollow);
                if (f->re[i].ltype == CCL || f->re[i].ltype == NCCL)
-                       xfree((f->re[i].lval.np));
+                       xfree(f->re[i].lval.np);
        }
        xfree(f->restr);
+       xfree(f->out);
+       xfree(f->posns);
+       xfree(f->gototab);
        xfree(f);
 }
index d09f550..0d2142c 100644 (file)
@@ -30,7 +30,7 @@ THIS SOFTWARE.
 #include "ytab.h"
 
 extern YYSTYPE yylval;
-extern int     infunc;
+extern bool    infunc;
 
 int    lineno  = 1;
 int    bracecnt = 0;
@@ -43,7 +43,7 @@ typedef struct Keyword {
        int     type;
 } Keyword;
 
-Keyword keywords[] ={  /* keep sorted: binary searched */
+const Keyword keywords[] = {   /* keep sorted: binary searched */
        { "BEGIN",      XBEGIN,         XBEGIN },
        { "END",        XEND,           XEND },
        { "NF",         VARNF,          VARNF },
@@ -91,14 +91,14 @@ Keyword keywords[] ={       /* keep sorted: binary searched */
 
 #define        RET(x)  { if(dbg)printf("lex %s\n", tokname(x)); return(x); }
 
-int peek(void)
+static int peek(void)
 {
        int c = input();
        unput(c);
        return c;
 }
 
-int gettok(char **pbuf, int *psz)      /* get next input token */
+static int gettok(char **pbuf, int *psz)       /* get next input token */
 {
        int c, retc;
        char *buf = *pbuf;
@@ -136,7 +136,7 @@ int gettok(char **pbuf, int *psz)   /* get next input token */
                        if (bp-buf >= sz)
                                if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok"))
                                        FATAL( "out of space for number %.10s...", buf );
-                       if (isdigit(c) || c == 'e' || c == 'E' 
+                       if (isdigit(c) || c == 'e' || c == 'E'
                          || c == '.' || c == '+' || c == '-')
                                *bp++ = c;
                        else {
@@ -164,23 +164,23 @@ int gettok(char **pbuf, int *psz) /* get next input token */
 int    word(char *);
 int    string(void);
 int    regexpr(void);
-int    sc      = 0;    /* 1 => return a } right now */
-int    reg     = 0;    /* 1 => return a REGEXPR now */
+bool   sc      = false;        /* true => return a } right now */
+bool   reg     = false;        /* true => return a REGEXPR now */
 
 int yylex(void)
 {
        int c;
-       static char *buf = 0;
+       static char *buf = NULL;
        static int bufsize = 5; /* BUG: setting this small causes core dump! */
 
-       if (buf == 0 && (buf = (char *) malloc(bufsize)) == NULL)
+       if (buf == NULL && (buf = malloc(bufsize)) == NULL)
                FATAL( "out of space in yylex" );
        if (sc) {
-               sc = 0;
+               sc = false;
                RET('}');
        }
        if (reg) {
-               reg = 0;
+               reg = false;
                return regexpr();
        }
        for (;;) {
@@ -190,14 +190,17 @@ int yylex(void)
                if (isalpha(c) || c == '_')
                        return word(buf);
                if (isdigit(c)) {
-                       yylval.cp = setsymtab(buf, tostring(buf), atof(buf), CON|NUM, symtab);
+                       char *cp = tostring(buf);
+                       yylval.cp = setsymtab(buf, cp, atof(buf), CON|NUM, symtab);
+                       free(cp);
                        /* should this also have STR set? */
                        RET(NUMBER);
                }
-       
+
                yylval.i = c;
                switch (c) {
                case '\n':      /* {EOL} */
+                       lineno++;
                        RET(NL);
                case '\r':      /* assume \n is coming */
                case ' ':       /* {WS}+ */
@@ -207,12 +210,18 @@ int yylex(void)
                        while ((c = input()) != '\n' && c != 0)
                                ;
                        unput(c);
+                       /*
+                        * Next line is a hack, itcompensates for
+                        * unput's treatment of \n.
+                        */
+                       lineno++;
                        break;
                case ';':
                        RET(';');
                case '\\':
                        if (peek() == '\n') {
                                input();
+                               lineno++;
                        } else if (peek() == '\r') {
                                input(); input();       /* \n */
                                lineno++;
@@ -223,7 +232,7 @@ int yylex(void)
                case '&':
                        if (peek() == '&') {
                                input(); RET(AND);
-                       } else 
+                       } else
                                RET('&');
                case '|':
                        if (peek() == '|') {
@@ -321,11 +330,11 @@ int yylex(void)
                                unputstr(buf);
                                RET(INDIRECT);
                        }
-       
+
                case '}':
                        if (--bracecnt < 0)
                                SYNTAX( "extra }" );
-                       sc = 1;
+                       sc = true;
                        RET(';');
                case ']':
                        if (--brackcnt < 0)
@@ -344,10 +353,10 @@ int yylex(void)
                case '(':
                        parencnt++;
                        RET('(');
-       
+
                case '"':
                        return string();        /* BUG: should be like tran.c ? */
-       
+
                default:
                        RET(c);
                }
@@ -358,10 +367,10 @@ int string(void)
 {
        int c, n;
        char *s, *bp;
-       static char *buf = 0;
+       static char *buf = NULL;
        static int bufsz = 500;
 
-       if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
+       if (buf == NULL && (buf = malloc(bufsz)) == NULL)
                FATAL("out of space for strings");
        for (bp = buf; (c = input()) != '"'; ) {
                if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, "string"))
@@ -370,22 +379,24 @@ int string(void)
                case '\n':
                case '\r':
                case 0:
+                       *bp = '\0';
                        SYNTAX( "non-terminated string %.10s...", buf );
-                       lineno++;
                        if (c == 0)     /* hopeless */
                                FATAL( "giving up" );
+                       lineno++;
                        break;
                case '\\':
                        c = input();
                        switch (c) {
+                       case '\n': break;
                        case '"': *bp++ = '"'; break;
-                       case 'n': *bp++ = '\n'; break;  
+                       case 'n': *bp++ = '\n'; break;
                        case 't': *bp++ = '\t'; break;
                        case 'f': *bp++ = '\f'; break;
                        case 'r': *bp++ = '\r'; break;
                        case 'b': *bp++ = '\b'; break;
                        case 'v': *bp++ = '\v'; break;
-                       case 'a': *bp++ = '\007'; break;
+                       case 'a': *bp++ = '\a'; break;
                        case '\\': *bp++ = '\\'; break;
 
                        case '0': case '1': case '2': /* octal: \d \dd \ddd */
@@ -416,7 +427,7 @@ int string(void)
                                break;
                            }
 
-                       default: 
+                       default:
                                *bp++ = c;
                                break;
                        }
@@ -426,15 +437,16 @@ int string(void)
                        break;
                }
        }
-       *bp = 0; 
+       *bp = 0;
        s = tostring(buf);
-       *bp++ = ' '; *bp++ = 0;
+       *bp++ = ' '; *bp++ = '\0';
        yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab);
+       free(s);
        RET(STRING);
 }
 
 
-int binsearch(char *w, Keyword *kp, int n)
+static int binsearch(char *w, const Keyword *kp, int n)
 {
        int cond, low, mid, high;
 
@@ -452,15 +464,14 @@ int binsearch(char *w, Keyword *kp, int n)
        return -1;
 }
 
-int word(char *w) 
+int word(char *w)
 {
-       Keyword *kp;
+       const Keyword *kp;
        int c, n;
 
        n = binsearch(w, keywords, sizeof(keywords)/sizeof(keywords[0]));
-/* BUG: this ought to be inside the if; in theory could fault (daniel barrett) */
-       kp = keywords + n;
        if (n != -1) {  /* found in table */
+               kp = keywords + n;
                yylval.i = kp->sub;
                switch (kp->type) {     /* special handling */
                case BLTIN:
@@ -498,28 +509,29 @@ int word(char *w)
 
 void startreg(void)    /* next call to yylex will return a regular expression */
 {
-       reg = 1;
+       reg = true;
 }
 
 int regexpr(void)
 {
        int c;
-       static char *buf = 0;
+       static char *buf = NULL;
        static int bufsz = 500;
        char *bp;
 
-       if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
+       if (buf == NULL && (buf = malloc(bufsz)) == NULL)
                FATAL("out of space for rex expr");
        bp = buf;
        for ( ; (c = input()) != '/' && c != 0; ) {
                if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, "regexpr"))
                        FATAL("out of space for reg expr %.10s...", buf);
                if (c == '\n') {
-                       SYNTAX( "newline in regular expression %.10s...", buf ); 
+                       *bp = '\0';
+                       SYNTAX( "newline in regular expression %.10s...", buf );
                        unput('\n');
                        break;
                } else if (c == '\\') {
-                       *bp++ = '\\'; 
+                       *bp++ = '\\';
                        *bp++ = input();
                } else {
                        *bp++ = c;
@@ -539,7 +551,7 @@ char        ebuf[300];
 char   *ep = ebuf;
 char   yysbuf[100];    /* pushback buffer */
 char   *yysptr = yysbuf;
-FILE   *yyin = 0;
+FILE   *yyin = NULL;
 
 int input(void)        /* get next lexical input character */
 {
@@ -553,18 +565,20 @@ int input(void)   /* get next lexical input character */
                        lexprog++;
        } else                          /* awk -f ... */
                c = pgetc();
-       if (c == '\n')
-               lineno++;
-       else if (c == EOF)
+       if (c == EOF)
                c = 0;
        if (ep >= ebuf + sizeof ebuf)
                ep = ebuf;
-       return *ep++ = c;
+       *ep = c;
+       if (c != 0) {
+               ep++;
+       }
+       return (c);
 }
 
 void unput(int c)      /* put lexical character back on input */
 {
-       if (c == '\n')
+       if (c == '\n')  
                lineno--;
        if (yysptr >= yysbuf + sizeof(yysbuf))
                FATAL("pushed back too much: %.20s...", yysbuf);
index 5eeb53d..27ef30f 100644 (file)
@@ -29,39 +29,44 @@ THIS SOFTWARE.
 #include <errno.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <limits.h>
 #include "awk.h"
 #include "ytab.h"
 
+char   EMPTY[] = { '\0' };
 FILE   *infile = NULL;
-char   *file   = "";
+bool   innew;          /* true = infile has not been read by readrec */
+char   *file   = EMPTY;
 char   *record;
 int    recsize = RECSIZE;
 char   *fields;
 int    fieldssize = RECSIZE;
 
 Cell   **fldtab;       /* pointers to Cells */
-char   inputFS[100] = " ";
+static size_t  len_inputFS = 0;
+static char    *inputFS = NULL; /* FS at time of input, for field splitting */
 
 #define        MAXFLD  2
 int    nfields = MAXFLD;       /* last allocated slot for $i */
 
-int    donefld;        /* 1 = implies rec broken into fields */
-int    donerec;        /* 1 = record is valid (no flds have changed) */
+bool   donefld;        /* true = implies rec broken into fields */
+bool   donerec;        /* true = record is valid (no flds have changed) */
 
 int    lastfld = 0;    /* last used field */
 int    argno   = 1;    /* current input argument number */
 extern Awkfloat *ARGC;
 
-static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
-static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
+static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL };
+static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL };
 
 void recinit(unsigned int n)
 {
-       if ( (record = (char *) malloc(n)) == NULL
-         || (fields = (char *) malloc(n+1)) == NULL
-         || (fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *))) == NULL
-         || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL )
+       if ( (record = malloc(n)) == NULL
+         || (fields = malloc(n+1)) == NULL
+         || (fldtab = calloc(nfields+2, sizeof(*fldtab))) == NULL
+         || (fldtab[0] = malloc(sizeof(**fldtab))) == NULL)
                FATAL("out of space for $0 and fields");
+       *record = '\0';
        *fldtab[0] = dollar0;
        fldtab[0]->sval = record;
        fldtab[0]->nval = tostring("0");
@@ -74,11 +79,11 @@ void makefields(int n1, int n2)             /* create $n1..$n2 inclusive */
        int i;
 
        for (i = n1; i <= n2; i++) {
-               fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
+               fldtab[i] = malloc(sizeof(**fldtab));
                if (fldtab[i] == NULL)
                        FATAL("out of space in makefields %d", i);
                *fldtab[i] = dollar1;
-               sprintf(temp, "%d", i);
+               snprintf(temp, sizeof(temp), "%d", i);
                fldtab[i]->nval = tostring(temp);
        }
 }
@@ -102,11 +107,36 @@ void initgetrec(void)
                argno++;
        }
        infile = stdin;         /* no filenames, so use stdin */
+       innew = true;
 }
 
-static int firsttime = 1;
+/*
+ * POSIX specifies that fields are supposed to be evaluated as if they were
+ * split using the value of FS at the time that the record's value ($0) was
+ * read.
+ *
+ * Since field-splitting is done lazily, we save the current value of FS
+ * whenever a new record is read in (implicitly or via getline), or when
+ * a new value is assigned to $0.
+ */
+void savefs(void)
+{
+       size_t len;
+       if ((len = strlen(getsval(fsloc))) < len_inputFS) {
+               strcpy(inputFS, *FS);   /* for subsequent field splitting */
+               return;
+       }
 
-int getrec(char **pbuf, int *pbufsize, int isrecord)   /* get next input record */
+       len_inputFS = len + 1;
+       inputFS = realloc(inputFS, len_inputFS);
+       if (inputFS == NULL)
+               FATAL("field separator %.10s... is too long", *FS);
+       memcpy(inputFS, *FS, len_inputFS);
+}
+
+static bool firsttime = true;
+
+int getrec(char **pbuf, int *pbufsize, bool isrecord)  /* get next input record */
 {                      /* note: cares whether buf == record */
        int c;
        char *buf = *pbuf;
@@ -114,14 +144,15 @@ int getrec(char **pbuf, int *pbufsize, int isrecord)      /* get next input record */
        int bufsize = *pbufsize, savebufsize = bufsize;
 
        if (firsttime) {
-               firsttime = 0;
+               firsttime = false;
                initgetrec();
        }
           dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
                *RS, *FS, *ARGC, *FILENAME) );
        if (isrecord) {
-               donefld = 0;
-               donerec = 1;
+               donefld = false;
+               donerec = true;
+               savefs();
        }
        saveb0 = buf[0];
        buf[0] = 0;
@@ -146,7 +177,9 @@ int getrec(char **pbuf, int *pbufsize, int isrecord)        /* get next input record */
                                FATAL("can't open file %s", file);
                        setfval(fnrloc, 0.0);
                }
-               c = readrec(&buf, &bufsize, infile);
+               c = readrec(&buf, &bufsize, infile, innew);
+               if (innew)
+                       innew = false;
                if (c != 0 || buf[0] != '\0') { /* normal record */
                        if (isrecord) {
                                if (freeable(fldtab[0]))
@@ -184,46 +217,62 @@ void nextfile(void)
        argno++;
 }
 
-int readrec(char **pbuf, int *pbufsize, FILE *inf)     /* read one record into buf */
+int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag)       /* read one record into buf */
 {
-       int sep, c;
+       int sep, c, isrec;
        char *rr, *buf = *pbuf;
        int bufsize = *pbufsize;
-
-       if (strlen(*FS) >= sizeof(inputFS))
-               FATAL("field separator %.10s... is too long", *FS);
-       /*fflush(stdout); avoids some buffering problem but makes it 25% slower*/
-       strcpy(inputFS, *FS);   /* for subsequent field splitting */
-       if ((sep = **RS) == 0) {
-               sep = '\n';
-               while ((c=getc(inf)) == '\n' && c != EOF)       /* skip leading \n's */
-                       ;
-               if (c != EOF)
-                       ungetc(c, inf);
-       }
-       for (rr = buf; ; ) {
-               for (; (c=getc(inf)) != sep && c != EOF; ) {
-                       if (rr-buf+1 > bufsize)
-                               if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
-                                       FATAL("input record `%.30s...' too long", buf);
+       char *rs = getsval(rsloc);
+
+       if (*rs && rs[1]) {
+               bool found;
+
+               fa *pfa = makedfa(rs, 1);
+               if (newflag)
+                       found = fnematch(pfa, inf, &buf, &bufsize, recsize);
+               else {
+                       int tempstat = pfa->initstat;
+                       pfa->initstat = 2;
+                       found = fnematch(pfa, inf, &buf, &bufsize, recsize);
+                       pfa->initstat = tempstat;
+               }
+               if (found)
+                       setptr(patbeg, '\0');
+       } else {
+               if ((sep = *rs) == 0) {
+                       sep = '\n';
+                       while ((c=getc(inf)) == '\n' && c != EOF)       /* skip leading \n's */
+                               ;
+                       if (c != EOF)
+                               ungetc(c, inf);
+               }
+               for (rr = buf; ; ) {
+                       for (; (c=getc(inf)) != sep && c != EOF; ) {
+                               if (rr-buf+1 > bufsize)
+                                       if (!adjbuf(&buf, &bufsize, 1+rr-buf,
+                                           recsize, &rr, "readrec 1"))
+                                               FATAL("input record `%.30s...' too long", buf);
+                               *rr++ = c;
+                       }
+                       if (*rs == sep || c == EOF)
+                               break;
+                       if ((c = getc(inf)) == '\n' || c == EOF)        /* 2 in a row */
+                               break;
+                       if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
+                           "readrec 2"))
+                               FATAL("input record `%.30s...' too long", buf);
+                       *rr++ = '\n';
                        *rr++ = c;
                }
-               if (**RS == sep || c == EOF)
-                       break;
-               if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
-                       break;
-               if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
+               if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
                        FATAL("input record `%.30s...' too long", buf);
-               *rr++ = '\n';
-               *rr++ = c;
+               *rr = 0;
        }
-       if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
-               FATAL("input record `%.30s...' too long", buf);
-       *rr = 0;
-          dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
        *pbuf = buf;
        *pbufsize = bufsize;
-       return c == EOF && rr == buf ? 0 : 1;
+       isrec = *buf || !feof(inf);
+          dprintf( ("readrec saw <%s>, returns %d\n", buf, isrec) );
+       return isrec;
 }
 
 char *getargv(int n)   /* get ARGV[n] */
@@ -232,7 +281,7 @@ char *getargv(int n)        /* get ARGV[n] */
        char *s, temp[50];
        extern Array *ARGVtab;
 
-       sprintf(temp, "%d", n);
+       snprintf(temp, sizeof(temp), "%d", n);
        if (lookup(temp, ARGVtab) == NULL)
                return NULL;
        x = setsymtab(temp, "", 0.0, STR, ARGVtab);
@@ -277,13 +326,14 @@ void fldbld(void) /* create fields from current record */
        n = strlen(r);
        if (n > fieldssize) {
                xfree(fields);
-               if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
+               if ((fields = malloc(n+2)) == NULL) /* possibly 2 final \0s */
                        FATAL("out of space for fields in fldbld %d", n);
                fieldssize = n;
        }
        fr = fields;
        i = 0;  /* number of fields accumulated here */
-       strcpy(inputFS, *FS);
+       if (inputFS == NULL)    /* make sure we have a copy of FS */
+               savefs();
        if (strlen(inputFS) > 1) {      /* it's a regular expression */
                i = refldbld(r, inputFS);
        } else if ((sep = *inputFS) == ' ') {   /* default whitespace */
@@ -306,15 +356,19 @@ void fldbld(void) /* create fields from current record */
                }
                *fr = 0;
        } else if ((sep = *inputFS) == 0) {             /* new: FS="" => 1 char/field */
-               for (i = 0; *r != 0; r++) {
-                       char buf[2];
+               for (i = 0; *r != '\0'; r += n) {
+                       char buf[MB_LEN_MAX + 1];
+
                        i++;
                        if (i > nfields)
                                growfldtab(i);
                        if (freeable(fldtab[i]))
                                xfree(fldtab[i]->sval);
-                       buf[0] = *r;
-                       buf[1] = 0;
+                       n = mblen(r, MB_LEN_MAX);
+                       if (n < 0)
+                               n = 1;
+                       memcpy(buf, r, n);
+                       buf[n] = '\0';
                        fldtab[i]->sval = tostring(buf);
                        fldtab[i]->tval = FLD | STR;
                }
@@ -347,7 +401,7 @@ void fldbld(void)   /* create fields from current record */
                FATAL("record `%.30s...' has too many fields; can't happen", r);
        cleanfld(i+1, lastfld); /* clean out junk from previous record */
        lastfld = i;
-       donefld = 1;
+       donefld = true;
        for (j = 1; j <= lastfld; j++) {
                p = fldtab[j];
                if(is_number(p->sval)) {
@@ -356,6 +410,7 @@ void fldbld(void)   /* create fields from current record */
                }
        }
        setfval(nfloc, (Awkfloat) lastfld);
+       donerec = true; /* restore */
        if (dbg) {
                for (j = 0; j <= lastfld; j++) {
                        p = fldtab[j];
@@ -373,7 +428,7 @@ void cleanfld(int n1, int n2)       /* clean out fields n1 .. n2 inclusive */
                p = fldtab[i];
                if (freeable(p))
                        xfree(p->sval);
-               p->sval = "";
+               p->sval = EMPTY,
                p->tval = FLD | STR | DONTFREE;
        }
 }
@@ -387,6 +442,21 @@ 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 < 0)
+               FATAL("cannot set NF to a negative value");
+       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)
@@ -404,8 +474,8 @@ void growfldtab(int n)      /* make new fields up to at least $n */
        if (n > nf)
                nf = n;
        s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
-       if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
-               fldtab = (Cell **) realloc(fldtab, s);
+       if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
+               fldtab = realloc(fldtab, s);
        else                                    /* overflow sizeof int */
                xfree(fldtab);  /* make it null */
        if (fldtab == NULL)
@@ -425,7 +495,7 @@ int refldbld(const char *rec, const char *fs)       /* build fields from reg expr in F
        n = strlen(rec);
        if (n > fieldssize) {
                xfree(fields);
-               if ((fields = (char *) malloc(n+1)) == NULL)
+               if ((fields = malloc(n+1)) == NULL)
                        FATAL("out of space for fields in refldbld %d", n);
                fieldssize = n;
        }
@@ -458,15 +528,16 @@ int refldbld(const char *rec, const char *fs)     /* build fields from reg expr in F
                        break;
                }
        }
-       return i;               
+       return i;
 }
 
 void recbld(void)      /* create $0 from $1..$NF if necessary */
 {
        int i;
        char *r, *p;
+       char *sep = getsval(ofsloc);
 
-       if (donerec == 1)
+       if (donerec)
                return;
        r = record;
        for (i = 1; i <= *NF; i++) {
@@ -476,9 +547,9 @@ void recbld(void)   /* create $0 from $1..$NF if necessary */
                while ((*r = *p++) != 0)
                        r++;
                if (i < *NF) {
-                       if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
+                       if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
                                FATAL("created $0 `%.30s...' too long", record);
-                       for (p = *OFS; (*r = *p++) != 0; )
+                       for (p = sep; (*r = *p++) != 0; )
                                r++;
                }
        }
@@ -494,7 +565,7 @@ void recbld(void)   /* create $0 from $1..$NF if necessary */
 
           dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
           dprintf( ("recbld = |%s|\n", record) );
-       donerec = 1;
+       donerec = true;
 }
 
 int    errorflag       = 0;
@@ -519,18 +590,13 @@ void SYNTAX(const char *fmt, ...)
        fprintf(stderr, " at source line %d", lineno);
        if (curfname != NULL)
                fprintf(stderr, " in function %s", curfname);
-       if (compile_time == 1 && cursource() != NULL)
+       if (compile_time == COMPILING && cursource() != NULL)
                fprintf(stderr, " source file %s", cursource());
        fprintf(stderr, "\n");
        errorflag = 2;
        eprint();
 }
 
-void fpecatch(int n)
-{
-       FATAL("floating point exception %d", n);
-}
-
 extern int bracecnt, brackcnt, parencnt;
 
 void bracecheck(void)
@@ -593,17 +659,20 @@ void error()
        extern Node *curnode;
 
        fprintf(stderr, "\n");
-       if (compile_time != 2 && NR && *NR > 0) {
-               fprintf(stderr, " input record number %d", (int) (*FNR));
-               if (strcmp(*FILENAME, "-") != 0)
-                       fprintf(stderr, ", file %s", *FILENAME);
-               fprintf(stderr, "\n");
+       if (compile_time != ERROR_PRINTING) {
+               if (NR && *NR > 0) {
+                       fprintf(stderr, " input record number %d", (int) (*FNR));
+                       if (strcmp(*FILENAME, "-") != 0)
+                               fprintf(stderr, ", file %s", *FILENAME);
+                       fprintf(stderr, "\n");
+               }
+               if (curnode)
+                       fprintf(stderr, " source line number %d", curnode->lineno);
+               else if (lineno)
+                       fprintf(stderr, " source line number %d", lineno);
        }
-       if (compile_time != 2 && curnode)
-               fprintf(stderr, " source line number %d", curnode->lineno);
-       else if (compile_time != 2 && lineno)
-               fprintf(stderr, " source line number %d", lineno);
-       if (compile_time == 1 && cursource() != NULL)
+
+       if (compile_time == COMPILING && cursource() != NULL)
                fprintf(stderr, " source file %s", cursource());
        fprintf(stderr, "\n");
        eprint();
@@ -616,7 +685,9 @@ void eprint(void)   /* try to print context around error */
        static int been_here = 0;
        extern char ebuf[], *ep;
 
-       if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
+       if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
+               return;
+       if (ebuf == ep)
                return;
        p = ep - 1;
        if (p > ebuf && *p == '\n')
@@ -681,12 +752,15 @@ int isclvar(const char *s)        /* is s of form var=something ? */
        for ( ; *s; s++)
                if (!(isalnum((uschar) *s) || *s == '_'))
                        break;
-       return *s == '=' && s > os && *(s+1) != '=';
+       return *s == '=' && s > os;
 }
 
 /* strtod is supposed to be a proper test of what's a valid number */
 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
 /* wrong: violates 4.10.1.4 of ansi C standard */
+/* well, not quite. As of C99, hex floating point is allowed. so this is
+ * a bit of a mess.
+ */
 
 #include <math.h>
 int is_number(const char *s)
@@ -697,7 +771,8 @@ int is_number(const char *s)
        r = strtod(s, &ep);
        if (ep == s || r == HUGE_VAL || errno == ERANGE)
                return 0;
-       while (*ep == ' ' || *ep == '\t' || *ep == '\n')
+       /* allow \r as well. windows files aren't going to go away. */
+       while (*ep == ' ' || *ep == '\t' || *ep == '\n' || *ep == '\r')
                ep++;
        if (*ep == '\0')
                return 1;
index 4b65997..269f0a8 100644 (file)
@@ -22,7 +22,7 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
 THIS SOFTWARE.
 ****************************************************************/
 
-const char     *version = "version 20121220";
+const char     *version = "version 20200612";
 
 #define DEBUG
 #include <stdio.h>
@@ -43,44 +43,112 @@ char       *cmdname;       /* gets argv[0] for error messages */
 extern FILE    *yyin;  /* lex input file */
 char   *lexprog;       /* points to program argument if it exists */
 extern int errorflag;  /* non-zero if any syntax errors; set by yyerror */
-int    compile_time = 2;       /* for error printing: */
-                               /* 2 = cmdline, 1 = compile, 0 = running */
+enum compile_states    compile_time = ERROR_PRINTING;
 
-#define        MAX_PFILE       20      /* max number of -f's */
+static char    **pfile;        /* program filenames from -f's */
+static size_t  maxpfile;       /* max program filename */
+static size_t  npfile;         /* number of filenames */
+static size_t  curpfile;       /* current filename */
 
-char   *pfile[MAX_PFILE];      /* program filenames from -f's */
-int    npfile = 0;     /* number of filenames */
-int    curpfile = 0;   /* current filename */
+bool   safe = false;   /* true => "safe" mode */
 
-int    safe    = 0;    /* 1 => "safe" mode */
+static noreturn void fpecatch(int n
+#ifdef SA_SIGINFO
+       , siginfo_t *si, void *uc
+#endif
+)
+{
+#ifdef SA_SIGINFO
+       static const char *emsg[] = {
+               [0] = "Unknown error",
+               [FPE_INTDIV] = "Integer divide by zero",
+               [FPE_INTOVF] = "Integer overflow",
+               [FPE_FLTDIV] = "Floating point divide by zero",
+               [FPE_FLTOVF] = "Floating point overflow",
+               [FPE_FLTUND] = "Floating point underflow",
+               [FPE_FLTRES] = "Floating point inexact result",
+               [FPE_FLTINV] = "Invalid Floating point operation",
+               [FPE_FLTSUB] = "Subscript out of range",
+       };
+#endif
+       FATAL("floating point exception"
+#ifdef SA_SIGINFO
+               ": %s", (size_t)si->si_code < sizeof(emsg) / sizeof(emsg[0]) &&
+               emsg[si->si_code] ? emsg[si->si_code] : emsg[0]
+#endif
+           );
+}
+
+/* Can this work with recursive calls?  I don't think so.
+void segvcatch(int n)
+{
+       FATAL("segfault.  Do you have an unbounded recursive call?", n);
+}
+*/
+
+static const char *
+setfs(char *p)
+{
+       /* wart: t=>\t */
+       if (p[0] == 't' && p[1] == '\0')
+               return "\t";
+       else if (p[0] != '\0')
+               return p;
+       return NULL;
+}
+
+static char *
+getarg(int *argc, char ***argv, const char *msg)
+{
+       if ((*argv)[1][2] != '\0') {    /* arg is -fsomething */
+               return &(*argv)[1][2];
+       } else {                        /* arg is -f something */
+               (*argc)--; (*argv)++;
+               if (*argc <= 1)
+                       FATAL("%s", msg);
+               return (*argv)[1];
+       }
+}
 
 int main(int argc, char *argv[])
 {
        const char *fs = NULL;
+       char *fn, *vn;
 
        setlocale(LC_CTYPE, "");
        setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */
        cmdname = argv[0];
        if (argc == 1) {
-               fprintf(stderr, 
-                 "usage: %s [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]\n", 
+               fprintf(stderr,
+                 "usage: %s [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]\n",
                  cmdname);
                exit(1);
        }
-       signal(SIGFPE, fpecatch);
+#ifdef SA_SIGINFO
+       {
+               struct sigaction sa;
+               sa.sa_sigaction = fpecatch;
+               sa.sa_flags = SA_SIGINFO;
+               sigemptyset(&sa.sa_mask);
+               (void)sigaction(SIGFPE, &sa, NULL);
+       }
+#else
+       (void)signal(SIGFPE, fpecatch);
+#endif
+       /*signal(SIGSEGV, segvcatch); experiment */
 
+       /* Set and keep track of the random seed */
        srand_seed = 1;
-       srand(srand_seed);
+       srandom((unsigned long) srand_seed);
 
        yyin = NULL;
        symtab = makesymtab(NSYMTAB/NSYMTAB);
        while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
-               if (strcmp(argv[1],"-version") == 0 || strcmp(argv[1],"--version") == 0) {
+               if (strcmp(argv[1], "-version") == 0 || strcmp(argv[1], "--version") == 0) {
                        printf("awk %s\n", version);
-                       exit(0);
-                       break;
+                       return 0;
                }
-               if (strncmp(argv[1], "--", 2) == 0) {   /* explicit end of args */
+               if (strcmp(argv[1], "--") == 0) {       /* explicit end of args */
                        argc--;
                        argv++;
                        break;
@@ -88,53 +156,29 @@ int main(int argc, char *argv[])
                switch (argv[1][1]) {
                case 's':
                        if (strcmp(argv[1], "-safe") == 0)
-                               safe = 1;
+                               safe = true;
                        break;
                case 'f':       /* next argument is program filename */
-                       if (argv[1][2] != 0) {  /* arg is -fsomething */
-                               if (npfile >= MAX_PFILE - 1)
-                                       FATAL("too many -f options"); 
-                               pfile[npfile++] = &argv[1][2];
-                       } else {                /* arg is -f something */
-                               argc--; argv++;
-                               if (argc <= 1)
-                                       FATAL("no program filename");
-                               if (npfile >= MAX_PFILE - 1)
-                                       FATAL("too many -f options"); 
-                               pfile[npfile++] = argv[1];
-                       }
-                       break;
+                       fn = getarg(&argc, &argv, "no program filename");
+                       if (npfile >= maxpfile) {
+                               maxpfile += 20;
+                               pfile = realloc(pfile, maxpfile * sizeof(*pfile));
+                               if (pfile == NULL)
+                                       FATAL("error allocating space for -f options");
+                       }
+                       pfile[npfile++] = fn;
+                       break;
                case 'F':       /* set field separator */
-                       if (argv[1][2] != 0) {  /* arg is -Fsomething */
-                               if (argv[1][2] == 't' && argv[1][3] == 0)       /* wart: t=>\t */
-                                       fs = "\t";
-                               else if (argv[1][2] != 0)
-                                       fs = &argv[1][2];
-                       } else {                /* arg is -F something */
-                               argc--; argv++;
-                               if (argc > 1 && argv[1][0] == 't' && argv[1][1] == 0)   /* wart: t=>\t */
-                                       fs = "\t";
-                               else if (argc > 1 && argv[1][0] != 0)
-                                       fs = &argv[1][0];
-                       }
-                       if (fs == NULL || *fs == '\0')
+                       fs = setfs(getarg(&argc, &argv, "no field separator"));
+                       if (fs == NULL)
                                WARNING("field separator FS is empty");
                        break;
                case 'v':       /* -v a=1 to be done NOW.  one -v for each */
-                       if (argv[1][2] != 0) {  /* arg is -vsomething */
-                               if (isclvar(&argv[1][2]))
-                                       setclvar(&argv[1][2]);
-                               else
-                                       FATAL("invalid -v option argument: %s", &argv[1][2]);
-                       } else {                /* arg is -v something */
-                               argc--; argv++;
-                               if (argc <= 1)
-                                       FATAL("no variable name");
-                               if (isclvar(argv[1]))
-                                       setclvar(argv[1]);
-                               else
-                                       FATAL("invalid -v option argument: %s", argv[1]);
-                       }
+                       vn = getarg(&argc, &argv, "no variable name");
+                       if (isclvar(vn))
+                               setclvar(vn);
+                       else
+                               FATAL("invalid -v option argument: %s", vn);
                        break;
                case 'd':
                        dbg = atoi(&argv[1][2]);
@@ -163,19 +207,23 @@ int main(int argc, char *argv[])
        }
        recinit(recsize);
        syminit();
-       compile_time = 1;
+       compile_time = COMPILING;
        argv[0] = cmdname;      /* put prog name at front of arglist */
           dprintf( ("argc=%d, argv[0]=%s\n", argc, argv[0]) );
        arginit(argc, argv);
        if (!safe)
                envinit(environ);
        yyparse();
+#if 0
+       // Doing this would comply with POSIX, but is not compatible with
+       // other awks and with what most users expect. So comment it out.
        setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */
+#endif
        if (fs)
                *FS = qstring(fs, '\0');
           dprintf( ("errorflag=%d\n", errorflag) );
        if (errorflag == 0) {
-               compile_time = 0;
+               compile_time = RUNNING;
                run(winner);
        } else
                bracecheck();
index 31acd75..2384d51 100644 (file)
@@ -62,6 +62,7 @@ struct xx
        { DIVIDE, "arith", " / " },
        { MOD, "arith", " % " },
        { UMINUS, "arith", " -" },
+       { UPLUS, "arith", " +" },
        { POWER, "arith", " **" },
        { PREINCR, "incrdecr", "++" },
        { POSTINCR, "incrdecr", "++" },
@@ -117,30 +118,55 @@ int main(int argc, char *argv[])
        char c;
        FILE *fp;
        char buf[200], name[200], def[200];
+       enum { TOK_UNKNOWN, TOK_ENUM, TOK_DEFINE } tokentype = TOK_UNKNOWN;
 
        printf("#include <stdio.h>\n");
        printf("#include \"awk.h\"\n");
        printf("#include \"ytab.h\"\n\n");
-       for (i = SIZE; --i >= 0; )
-               names[i] = "";
 
-       if ((fp = fopen("ytab.h", "r")) == NULL) {
-               fprintf(stderr, "maketab can't open ytab.h!\n");
+       if (argc != 2) {
+               fprintf(stderr, "usage: maketab YTAB_H\n");
                exit(1);
        }
-       printf("static char *printname[%d] = {\n", SIZE);
+       if ((fp = fopen(argv[1], "r")) == NULL) {
+               fprintf(stderr, "maketab can't open %s!\n", argv[1]);
+               exit(1);
+       }
+       printf("static const char * const printname[%d] = {\n", SIZE);
        i = 0;
        while (fgets(buf, sizeof buf, fp) != NULL) {
-               n = sscanf(buf, "%1c %s %s %d", &c, def, name, &tok);
-               if (c != '#' || (n != 4 && strcmp(def,"define") != 0))  /* not a valid #define */
+               // 199 is sizeof(def) - 1
+               if (tokentype != TOK_ENUM) {
+                       n = sscanf(buf, "%1c %199s %199s %d", &c, def, name,
+                           &tok);
+                       if (n == 4 && c == '#' && strcmp(def, "define") == 0) {
+                               tokentype = TOK_DEFINE;
+                       } else if (tokentype != TOK_UNKNOWN) {
+                               continue;
+                       }
+               }
+               if (tokentype != TOK_DEFINE) {
+                       /* not a valid #define, bison uses enums now */
+                       n = sscanf(buf, "%199s = %d,\n", name, &tok);
+                       if (n != 2)
+                               continue;
+                       tokentype = TOK_ENUM;
+               }
+               if (strcmp(name, "YYSTYPE_IS_DECLARED") == 0) {
+                       tokentype = TOK_UNKNOWN;
                        continue;
+               }
                if (tok < FIRSTTOKEN || tok > LASTTOKEN) {
+                       tokentype = TOK_UNKNOWN;
                        /* fprintf(stderr, "maketab funny token %d %s ignored\n", tok, buf); */
                        continue;
                }
-               names[tok-FIRSTTOKEN] = (char *) malloc(strlen(name)+1);
-               strcpy(names[tok-FIRSTTOKEN], name);
-               printf("\t(char *) \"%s\",\t/* %d */\n", name, tok);
+               names[tok-FIRSTTOKEN] = strdup(name);
+               if (names[tok-FIRSTTOKEN] == NULL) {
+                       fprintf(stderr, "maketab out of space copying %s", name);
+                       continue;
+               }
+               printf("\t\"%s\",\t/* %d */\n", name, tok);
                i++;
        }
        printf("};\n\n");
@@ -149,20 +175,18 @@ int main(int argc, char *argv[])
                table[p->token-FIRSTTOKEN] = p->name;
        printf("\nCell *(*proctab[%d])(Node **, int) = {\n", SIZE);
        for (i=0; i<SIZE; i++)
-               if (table[i]==0)
-                       printf("\tnullproc,\t/* %s */\n", names[i]);
-               else
-                       printf("\t%s,\t/* %s */\n", table[i], names[i]);
+               printf("\t%s,\t/* %s */\n",
+                   table[i] ? table[i] : "nullproc", names[i] ? names[i] : "");
        printf("};\n\n");
 
-       printf("char *tokname(int n)\n");       /* print a tokname() function */
+       printf("const char *tokname(int n)\n"); /* print a tokname() function */
        printf("{\n");
-       printf("        static char buf[100];\n\n");
-       printf("        if (n < FIRSTTOKEN || n > LASTTOKEN) {\n");
-       printf("                sprintf(buf, \"token %%d\", n);\n");
-       printf("                return buf;\n");
-       printf("        }\n");
-       printf("        return printname[n-FIRSTTOKEN];\n");
+       printf("\tstatic char buf[100];\n\n");
+       printf("\tif (n < FIRSTTOKEN || n > LASTTOKEN) {\n");
+       printf("\t\tsnprintf(buf, sizeof(buf), \"token %%d\", n);\n");
+       printf("\t\treturn buf;\n");
+       printf("\t}\n");
+       printf("\treturn printname[n-FIRSTTOKEN];\n");
        printf("}\n");
        return 0;
 }
index 8304ded..18556e3 100644 (file)
@@ -33,7 +33,7 @@ Node *nodealloc(int n)
 {
        Node *x;
 
-       x = (Node *) malloc(sizeof(Node) + (n-1)*sizeof(Node *));
+       x = malloc(sizeof(*x) + (n-1) * sizeof(x));
        if (x == NULL)
                FATAL("out of space in nodealloc");
        x->nnext = NULL;
@@ -259,7 +259,7 @@ int isarg(const char *s)            /* is s in argument list for current function? */
        Node *p = arglist;
        int n;
 
-       for (n = 0; p != 0; p = p->nnext, n++)
+       for (n = 0; p != NULL; p = p->nnext, n++)
                if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0)
                        return n;
        return -1;
index 9a657ef..71e3cd9 100644 (file)
@@ -38,15 +38,15 @@ extern      int     yylook(void);
 extern int     yyback(int *, int);
 extern int     yyinput(void);
 
-extern fa      *makedfa(const char *, int);
-extern fa      *mkdfa(const char *, int);
-extern int     makeinit(fa *, int);
+extern fa      *makedfa(const char *, bool);
+extern fa      *mkdfa(const char *, bool);
+extern int     makeinit(fa *, bool);
 extern void    penter(Node *);
 extern void    freetr(Node *);
-extern int     hexstr(uschar **);
-extern int     quoted(uschar **);
+extern int     hexstr(const uschar **);
+extern int     quoted(const uschar **);
 extern char    *cclenter(const char *);
-extern void    overflo(const char *) __attribute__((__noreturn__));
+extern noreturn void   overflo(const char *);
 extern void    cfoll(fa *, Node *);
 extern int     first(Node *);
 extern void    follow(Node *);
@@ -54,6 +54,7 @@ extern        int     member(int, const char *);
 extern int     match(fa *, const char *);
 extern int     pmatch(fa *, const char *);
 extern int     nematch(fa *, const char *);
+extern bool    fnematch(fa *, FILE *, char **, int *, int);
 extern Node    *reparse(const char *);
 extern Node    *regexp(void);
 extern Node    *primary(void);
@@ -88,7 +89,7 @@ extern        Node    *pa2stat(Node *, Node *, Node *);
 extern Node    *linkum(Node *, Node *);
 extern void    defn(Cell *, Node *, Node *);
 extern int     isarg(const char *);
-extern char    *tokname(int);
+extern const char *tokname(int);
 extern Cell    *(*proctab[])(Node **, int);
 extern int     ptoi(void *);
 extern Node    *itonp(int);
@@ -110,30 +111,36 @@ extern    double  getfval(Cell *);
 extern char    *getsval(Cell *);
 extern char    *getpssval(Cell *);     /* for print */
 extern char    *tostring(const char *);
+extern char    *tostringN(const char *, size_t);
 extern char    *qstring(const char *, int);
+extern Cell    *catstr(Cell *, Cell *);
 
 extern void    recinit(unsigned int);
 extern void    initgetrec(void);
 extern void    makefields(int, int);
 extern void    growfldtab(int n);
-extern int     getrec(char **, int *, int);
+extern void    savefs(void);
+extern int     getrec(char **, int *, bool);
 extern void    nextfile(void);
-extern int     readrec(char **buf, int *bufsize, FILE *inf);
+extern int     readrec(char **buf, int *bufsize, FILE *inf, bool isnew);
 extern char    *getargv(int);
 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);
 extern void    yyerror(const char *);
-extern void    fpecatch(int);
 extern void    bracecheck(void);
 extern void    bcheck2(int, int, int);
-extern void    SYNTAX(const char *, ...);
-extern void    FATAL(const char *, ...) __attribute__((__noreturn__));
-extern void    WARNING(const char *, ...);
+extern void    SYNTAX(const char *, ...)
+    __attribute__((__format__(__printf__, 1, 2)));
+extern noreturn void   FATAL(const char *, ...)
+    __attribute__((__format__(__printf__, 1, 2)));
+extern void    WARNING(const char *, ...)
+    __attribute__((__format__(__printf__, 1, 2)));
 extern void    error(void);
 extern void    eprint(void);
 extern void    bclass(int);
@@ -184,7 +191,7 @@ extern      Cell    *bltin(Node **, int);
 extern Cell    *printstat(Node **, int);
 extern Cell    *nullproc(Node **, int);
 extern FILE    *redirect(int, Node *);
-extern FILE    *openfile(int, const char *);
+extern FILE    *openfile(int, const char *, bool *);
 extern const char      *filename(FILE *);
 extern Cell    *closefile(Node **, int);
 extern void    closeall(void);
@@ -193,3 +200,5 @@ extern      Cell    *gsub(Node **, int);
 
 extern FILE    *popen(const char *, const char *);
 extern int     pclose(FILE *);
+
+extern  const char     *flags2str(int flags);
index 6c4ce10..41f1bbe 100644 (file)
@@ -25,20 +25,27 @@ THIS SOFTWARE.
 #define DEBUG
 #include <stdio.h>
 #include <ctype.h>
+#include <errno.h>
+#include <wchar.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/wait.h>
 #include "awk.h"
 #include "ytab.h"
 
-#define tempfree(x)    if (istemp(x)) tfree(x); else
-
-/*
-#undef tempfree
+static void stdinit(void);
+static void flush_all(void);
 
+#if 1
+#define tempfree(x)    do { if (istemp(x)) tfree(x); } while (/*CONSTCOND*/0)
+#else
 void tempfree(Cell *p) {
        if (p->ctype == OCELL && (p->csub < CUNK || p->csub > CFREE)) {
                WARNING("bad csub %d in Cell %d %s",
@@ -47,7 +54,7 @@ void tempfree(Cell *p) {
        if (istemp(p))
                tfree(p);
 }
-*/
+#endif
 
 /* do we really need these? */
 /* #ifdef _NFILE */
@@ -71,23 +78,23 @@ extern      Awkfloat        srand_seed;
 Node   *winner = NULL; /* root of parse tree */
 Cell   *tmps;          /* free temporary cells for execution */
 
-static Cell    truecell        ={ OBOOL, BTRUE, 0, 0, 1.0, NUM };
+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 };
+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 };
+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 };
+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 };
+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 };
+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 };
+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 };
+static Cell    retcell         ={ OJUMP, JRET, 0, 0, 0.0, NUM, NULL, NULL };
 Cell   *jret   = &retcell;
-static Cell    tempcell        ={ OCELL, CTEMP, 0, "", 0.0, NUM|STR|DONTFREE };
+static Cell    tempcell        ={ OCELL, CTEMP, 0, EMPTY, 0.0, NUM|STR|DONTFREE, NULL, NULL };
 
 Node   *curnode = NULL;        /* the node being executed, for debugging */
 
@@ -111,7 +118,7 @@ int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr,
                /* round up to next multiple of quantum */
                if (rminlen)
                        minlen += quantum - rminlen;
-               tbuf = (char *) realloc(*pbuf, minlen);
+               tbuf = realloc(*pbuf, minlen);
                dprintf( ("adjbuf %s: %d %d (pbuf=%p, tbuf=%p)\n", whatrtn, *psiz, minlen, *pbuf, tbuf) );
                if (tbuf == NULL) {
                        if (whatrtn)
@@ -128,7 +135,6 @@ int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr,
 
 void run(Node *a)      /* execution of parse tree starts here */
 {
-       extern void stdinit(void);
 
        stdinit();
        execute(a);
@@ -187,7 +193,7 @@ Cell *program(Node **a, int n)      /* execute an awk program */
                tempfree(x);
        }
        if (a[1] || a[2])
-               while (getrec(&record, &recsize, 1) > 0) {
+               while (getrec(&record, &recsize, true) > 0) {
                        x = execute(a[1]);
                        if (isexit(x))
                                break;
@@ -217,11 +223,11 @@ struct Frame {    /* stack frame for awk function calls */
 
 struct Frame *frame = NULL;    /* base of stack frames; dynamically allocated */
 int    nframe = 0;             /* number of frames allocated */
-struct Frame *fp = NULL;       /* frame pointer. bottom level unused */
+struct Frame *frp = NULL;      /* frame pointer. bottom level unused */
 
 Cell *call(Node **a, int n)    /* function call.  very kludgy and fragile */
 {
-       static Cell newcopycell = { OCELL, CCOPY, 0, "", 0.0, NUM|STR|DONTFREE };
+       static const Cell newcopycell = { OCELL, CCOPY, 0, EMPTY, 0.0, NUM|STR|DONTFREE, NULL, NULL };
        int i, ncall, ndef;
        int freed = 0; /* handles potential double freeing when fcn & param share a tempcell */
        Node *x;
@@ -234,21 +240,21 @@ Cell *call(Node **a, int n)       /* function call.  very kludgy and fragile */
        if (!isfcn(fcn))
                FATAL("calling undefined function %s", s);
        if (frame == NULL) {
-               fp = frame = (struct Frame *) calloc(nframe += 100, sizeof(struct Frame));
+               frp = 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)      /* args in call */
                ncall++;
        ndef = (int) fcn->fval;                 /* args in defn */
-          dprintf( ("calling %s, %d args (%d in defn), fp=%d\n", s, ncall, ndef, (int) (fp-frame)) );
+          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) {   /* get call args */
-                  dprintf( ("evaluate args[%d], fp=%d:\n", i, (int) (fp-frame)) );
+                  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",
@@ -265,26 +271,25 @@ Cell *call(Node **a, int n)       /* function call.  very kludgy and fragile */
                args[i] = gettemp();
                *args[i] = newcopycell;
        }
-       fp++;   /* now ok to up frame */
-       if (fp >= frame + nframe) {
-               int dfp = fp - frame;   /* old index */
-               frame = (struct Frame *)
-                       realloc((char *) frame, (nframe += 100) * sizeof(struct Frame));
+       frp++;  /* now ok to up frame */
+       if (frp >= frame + nframe) {
+               int dfp = frp - frame;  /* old index */
+               frame = realloc(frame, (nframe += 100) * sizeof(*frame));
                if (frame == NULL)
                        FATAL("out of space for stack frames in %s", s);
-               fp = frame + dfp;
+               frp = frame + dfp;
        }
-       fp->fcncell = fcn;
-       fp->args = args;
-       fp->nargs = ndef;       /* number defined with (excess are locals) */
-       fp->retval = gettemp();
+       frp->fcncell = fcn;
+       frp->args = args;
+       frp->nargs = ndef;      /* number defined with (excess are locals) */
+       frp->retval = gettemp();
 
-          dprintf( ("start exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+          dprintf( ("start exec of %s, frp=%d\n", s, (int) (frp-frame)) );
        y = execute((Node *)(fcn->sval));       /* execute body */
-          dprintf( ("finished exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+          dprintf( ("finished exec of %s, frp=%d\n", s, (int) (frp-frame)) );
 
        for (i = 0; i < ndef; i++) {
-               Cell *t = fp->args[i];
+               Cell *t = frp->args[i];
                if (isarr(t)) {
                        if (t->csub == CCOPY) {
                                if (i >= ncall) {
@@ -313,9 +318,9 @@ Cell *call(Node **a, int n) /* function call.  very kludgy and fragile */
        if (freed == 0) {
                tempfree(y);    /* don't free twice! */
        }
-       z = fp->retval;                 /* return value */
+       z = frp->retval;                        /* return value */
           dprintf( ("%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval) );
-       fp--;
+       frp--;
        return(z);
 }
 
@@ -323,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;
 }
 
@@ -338,11 +347,11 @@ Cell *arg(Node **a, int n)        /* nth argument of a function */
 {
 
        n = ptoi(a[0]); /* argument number, counting from 0 */
-          dprintf( ("arg(%d), fp->nargs=%d\n", n, fp->nargs) );
-       if (n+1 > fp->nargs)
+          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, fp->fcncell->nval);
-       return fp->args[n];
+                       n+1, frp->fcncell->nval);
+       return frp->args[n];
 }
 
 Cell *jump(Node **a, int n)    /* break, continue, next, nextfile, return */
@@ -361,14 +370,14 @@ Cell *jump(Node **a, int n)       /* break, continue, next, nextfile, return */
                if (a[0] != NULL) {
                        y = execute(a[0]);
                        if ((y->tval & (STR|NUM)) == (STR|NUM)) {
-                               setsval(fp->retval, getsval(y));
-                               fp->retval->fval = getfval(y);
-                               fp->retval->tval |= NUM;
+                               setsval(frp->retval, getsval(y));
+                               frp->retval->fval = getfval(y);
+                               frp->retval->tval |= NUM;
                        }
                        else if (y->tval & STR)
-                               setsval(fp->retval, getsval(y));
+                               setsval(frp->retval, getsval(y));
                        else if (y->tval & NUM)
-                               setfval(fp->retval, getfval(y));
+                               setfval(frp->retval, getfval(y));
                        else            /* can't happen */
                                FATAL("bad type variable %d", y->tval);
                        tempfree(y);
@@ -397,8 +406,9 @@ Cell *awkgetline(Node **a, int n)   /* get next line from specific input */
        char *buf;
        int bufsize = recsize;
        int mode;
+       bool newflag;
 
-       if ((buf = (char *) malloc(bufsize)) == NULL)
+       if ((buf = malloc(bufsize)) == NULL)
                FATAL("out of memory in getline");
 
        fflush(stdout); /* in case someone is waiting for a prompt */
@@ -408,17 +418,21 @@ Cell *awkgetline(Node **a, int n) /* get next line from specific input */
                mode = ptoi(a[1]);
                if (mode == '|')                /* input pipe */
                        mode = LE;      /* arbitrary flag */
-               fp = openfile(mode, getsval(x));
+               fp = openfile(mode, getsval(x), &newflag);
                tempfree(x);
                if (fp == NULL)
                        n = -1;
                else
-                       n = readrec(&buf, &bufsize, fp);
+                       n = readrec(&buf, &bufsize, fp, newflag);
                if (n <= 0) {
                        ;
                } else if (a[0] != NULL) {      /* getline var <file */
                        x = execute(a[0]);
                        setsval(x, buf);
+                       if (is_number(x->sval)) {
+                               x->fval = atof(x->sval);
+                               x->tval |= NUM;
+                       }
                        tempfree(x);
                } else {                        /* getline <file */
                        setsval(fldtab[0], buf);
@@ -429,11 +443,15 @@ Cell *awkgetline(Node **a, int n) /* get next line from specific input */
                }
        } else {                        /* bare getline; use current input */
                if (a[0] == NULL)       /* getline */
-                       n = getrec(&record, &recsize, 1);
+                       n = getrec(&record, &recsize, true);
                else {                  /* getline var */
-                       n = getrec(&buf, &bufsize, 0);
+                       n = getrec(&buf, &bufsize, false);
                        x = execute(a[0]);
                        setsval(x, buf);
+                       if (is_number(x->sval)) {
+                               x->fval = atof(x->sval);
+                               x->tval |= NUM;
+                       }
                        tempfree(x);
                }
        }
@@ -444,35 +462,55 @@ Cell *awkgetline(Node **a, int n) /* get next line from specific input */
 
 Cell *getnf(Node **a, int n)   /* get NF */
 {
-       if (donefld == 0)
+       if (!donefld)
                fldbld();
        return (Cell *) a[0];
 }
 
-Cell *array(Node **a, int n)   /* a[0] is symtab, a[1] is list of subscripts */
+static char *
+makearraystring(Node *p, const char *func)
 {
-       Cell *x, *y, *z;
-       char *s;
-       Node *np;
        char *buf;
        int bufsz = recsize;
-       int nsub = strlen(*SUBSEP);
+       size_t blen, seplen;
 
-       if ((buf = (char *) malloc(bufsz)) == NULL)
-               FATAL("out of memory in array");
+       if ((buf = malloc(bufsz)) == NULL) {
+               FATAL("%s: out of memory", func);
+       }
 
-       x = execute(a[0]);      /* Cell* for symbol table */
-       buf[0] = 0;
-       for (np = a[1]; np; np = np->nnext) {
-               y = execute(np);        /* subscript */
-               s = getsval(y);
-               if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, "array"))
-                       FATAL("out of memory for %s[%s...]", x->nval, buf);
-               strcat(buf, s);
-               if (np->nnext)
-                       strcat(buf, *SUBSEP);
-               tempfree(y);
+       blen = 0;
+       buf[blen] = '\0';
+       seplen = strlen(getsval(subseploc));
+
+       for (; p; p = p->nnext) {
+               Cell *x = execute(p);   /* expr */
+               char *s = getsval(x);
+               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)   /* a[0] is symtab, a[1] is list of subscripts */
+{
+       Cell *x, *z;
+       char *buf;
+
+       x = execute(a[0]);      /* Cell* for symbol table */
+       buf = makearraystring(a[1], __func__);
        if (!isarr(x)) {
                   dprintf( ("making %s into an array\n", NN(x->nval)) );
                if (freeable(x))
@@ -491,35 +529,21 @@ Cell *array(Node **a, int n)      /* a[0] is symtab, a[1] is list of subscripts */
 
 Cell *awkdelete(Node **a, int n)       /* a[0] is symtab, a[1] is list of subscripts */
 {
-       Cell *x, *y;
-       Node *np;
-       char *s;
-       int nsub = strlen(*SUBSEP);
+       Cell *x;
 
        x = execute(a[0]);      /* Cell* for symbol table */
+       if (x == symtabloc) {
+               FATAL("cannot delete SYMTAB or its elements");
+       }
        if (!isarr(x))
                return True;
-       if (a[1] == 0) {        /* delete the elements, not the table */
+       if (a[1] == NULL) {     /* delete the elements, not the table */
                freesymtab(x);
                x->tval &= ~STR;
                x->tval |= ARR;
                x->sval = (char *) makesymtab(NSYMTAB);
        } else {
-               int bufsz = recsize;
-               char *buf;
-               if ((buf = (char *) malloc(bufsz)) == NULL)
-                       FATAL("out of memory in adelete");
-               buf[0] = 0;
-               for (np = a[1]; np; np = np->nnext) {
-                       y = execute(np);        /* subscript */
-                       s = getsval(y);
-                       if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, "awkdelete"))
-                               FATAL("out of memory deleting %s[%s...]", x->nval, buf);
-                       strcat(buf, s); 
-                       if (np->nnext)
-                               strcat(buf, *SUBSEP);
-                       tempfree(y);
-               }
+               char *buf = makearraystring(a[1], __func__);
                freeelem(x, buf);
                free(buf);
        }
@@ -529,12 +553,8 @@ Cell *awkdelete(Node **a, int n)   /* a[0] is symtab, a[1] is list of subscripts *
 
 Cell *intest(Node **a, int n)  /* a[0] is index (list), a[1] is symtab */
 {
-       Cell *x, *ap, *k;
-       Node *p;
+       Cell *ap, *k;
        char *buf;
-       char *s;
-       int bufsz = recsize;
-       int nsub = strlen(*SUBSEP);
 
        ap = execute(a[1]);     /* array name */
        if (!isarr(ap)) {
@@ -545,20 +565,7 @@ Cell *intest(Node **a, int n)      /* a[0] is index (list), a[1] is symtab */
                ap->tval |= ARR;
                ap->sval = (char *) makesymtab(NSYMTAB);
        }
-       if ((buf = (char *) malloc(bufsz)) == NULL) {
-               FATAL("out of memory in intest");
-       }
-       buf[0] = 0;
-       for (p = a[0]; p; p = p->nnext) {
-               x = execute(p); /* expr */
-               s = getsval(x);
-               if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, "intest"))
-                       FATAL("out of memory deleting %s[%s...]", x->nval, buf);
-               strcat(buf, s);
-               tempfree(x);
-               if (p->nnext)
-                       strcat(buf, *SUBSEP);
-       }
+       buf = makearraystring(a[0], __func__);
        k = lookup(buf, (Array *) ap->sval);
        tempfree(ap);
        free(buf);
@@ -583,7 +590,7 @@ Cell *matchop(Node **a, int n)      /* ~ and match() */
        }
        x = execute(a[1]);      /* a[1] = target text */
        s = getsval(x);
-       if (a[0] == 0)          /* a[1] == 0: already-compiled reg expr */
+       if (a[0] == NULL)       /* a[1] == 0: already-compiled reg expr */
                i = (*mf)((fa *) a[2], s);
        else {
                y = execute(a[2]);      /* a[2] = regular expr */
@@ -694,12 +701,12 @@ Cell *gettemp(void)       /* get a tempcell */
        Cell *x;
 
        if (!tmps) {
-               tmps = (Cell *) calloc(100, sizeof(Cell));
+               tmps = calloc(100, sizeof(*tmps));
                if (!tmps)
                        FATAL("out of space for temporaries");
-               for(i = 1; i < 100; i++)
+               for (i = 1; i < 100; i++)
                        tmps[i-1].cnext = &tmps[i];
-               tmps[i-1].cnext = 0;
+               tmps[i-1].cnext = NULL;
        }
        x = tmps;
        tmps = x->cnext;
@@ -734,18 +741,18 @@ Cell *substr(Node **a, int nnn)           /* substr(a[0], a[1], a[2]) */
        int k, m, n;
        char *s;
        int temp;
-       Cell *x, *y, *z = 0;
+       Cell *x, *y, *z = NULL;
 
        x = execute(a[0]);
        y = execute(a[1]);
-       if (a[2] != 0)
+       if (a[2] != NULL)
                z = execute(a[2]);
        s = getsval(x);
        k = strlen(s) + 1;
        if (k <= 1) {
                tempfree(x);
                tempfree(y);
-               if (a[2] != 0) {
+               if (a[2] != NULL) {
                        tempfree(z);
                }
                x = gettemp();
@@ -758,7 +765,7 @@ Cell *substr(Node **a, int nnn)             /* substr(a[0], a[1], a[2]) */
        else if (m > k)
                m = k;
        tempfree(y);
-       if (a[2] != 0) {
+       if (a[2] != NULL) {
                n = (int) getfval(z);
                tempfree(z);
        } else
@@ -790,8 +797,8 @@ Cell *sindex(Node **a, int nnn)             /* index(a[0], a[1]) */
 
        z = gettemp();
        for (p1 = s1; *p1 != '\0'; p1++) {
-               for (q=p1, p2=s2; *p2 != '\0' && *q == *p2; q++, p2++)
-                       ;
+               for (q = p1, p2 = s2; *p2 != '\0' && *q == *p2; q++, p2++)
+                       continue;
                if (*p2 == '\0') {
                        v = (Awkfloat) (p1 - s1 + 1);   /* origin 1 */
                        break;
@@ -816,10 +823,23 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a)    /* printf-like co
        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)
+       if ((fmt = malloc(fmtsz)) == NULL)
                FATAL("out of memory in format()");
        while (*s) {
                adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format1");
@@ -840,12 +860,24 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a)    /* printf-like co
                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 (isalpha((uschar)*s) && *s != 'l' && *s != 'h' && *s != 'L')
-                               break;  /* the ansi panoply */
+                       /* Ignore size specifiers */
+                       if (strchr("hjLlqtz", *s) != NULL) {    /* the ansi panoply */
+                               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;
-                               sprintf(t-1, "%d", fmtwd=(int) getfval(x));
+                               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");
@@ -857,21 +889,22 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a)    /* printf-like co
                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':
-                       flag = 'd';
-                       if(*(s-1) == 'l') break;
-                       *(t-1) = 'l';
-                       *t = 'd';
+               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 'o': case 'x': case 'X': case 'u':
-                       flag = *(s-1) == 'l' ? 'd' : 'u';
-                       break;
                case 's':
                        flag = 's';
                        break;
@@ -892,18 +925,20 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a)    /* printf-like co
                        n = fmtwd;
                adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format5");
                switch (flag) {
-               case '?':       sprintf(p, "%s", fmt);  /* unknown, so dump it too */
+               case '?':       snprintf(p, BUFSZ(p), "%s", fmt);       /* unknown, so dump it too */
                        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);
-                       sprintf(p, "%s", t);
+                       snprintf(p, BUFSZ(p), "%s", t);
                        break;
-               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;
+               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);
@@ -911,18 +946,18 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a)    /* printf-like co
                                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);
-                       sprintf(p, fmt, t);
+                       snprintf(p, BUFSZ(p), fmt, t);
                        break;
                case 'c':
                        if (isnum(x)) {
-                               if (getfval(x))
-                                       sprintf(p, fmt, (int) getfval(x));
+                               if ((int)getfval(x))
+                                       snprintf(p, BUFSZ(p), fmt, (int) getfval(x));
                                else {
                                        *p++ = '\0'; /* explicit null byte */
                                        *p = '\0';   /* next output will start here */
                                }
                        } else
-                               sprintf(p, fmt, getsval(x)[0]);
+                               snprintf(p, BUFSZ(p), fmt, getsval(x)[0]);
                        break;
                default:
                        FATAL("can't happen: bad conversion %c in format()", flag);
@@ -947,7 +982,7 @@ Cell *awksprintf(Node **a, int n)           /* sprintf(a[0]) */
        char *buf;
        int bufsz=3*recsize;
 
-       if ((buf = (char *) malloc(bufsz)) == NULL)
+       if ((buf = malloc(bufsz)) == NULL)
                FATAL("out of memory in awksprintf");
        y = a[0]->nnext;
        x = execute(a[0]);
@@ -970,7 +1005,7 @@ Cell *awkprintf(Node **a, int n)           /* printf */
        int len;
        int bufsz=3*recsize;
 
-       if ((buf = (char *) malloc(bufsz)) == NULL)
+       if ((buf = malloc(bufsz)) == NULL)
                FATAL("out of memory in awkprintf");
        y = a[0]->nnext;
        x = execute(a[0]);
@@ -1003,7 +1038,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,11 +1068,15 @@ 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);
-               else
+               else {
+                       errno = 0;
                        i = errcheck(pow(i, j), "pow");
+               }
                break;
        default:        /* can't happen */
                FATAL("illegal arithmetic operator %d", n);
@@ -1088,8 +1127,8 @@ Cell *assign(Node **a, int n)     /* a[0] = a[1], a[0] += a[1], etc. */
        y = execute(a[1]);
        x = execute(a[0]);
        if (n == ASSIGN) {      /* ordinary assignment */
-               if (x == y && !(x->tval & (FLD|REC)))   /* self-assignment: */
-                       ;               /* leave alone unless it's a field */
+               if (x == y && !(x->tval & (FLD|REC)) && x != nfloc)
+                       ;       /* self-assignment: leave alone unless it's a field or NF */
                else if ((y->tval & (STR|NUM)) == (STR|NUM)) {
                        setsval(x, getsval(y));
                        x->fval = getfval(y);
@@ -1130,8 +1169,10 @@ Cell *assign(Node **a, int n)    /* a[0] = a[1], a[0] += a[1], etc. */
        case POWEQ:
                if (yf >= 0 && modf(yf, &v) == 0.0)     /* pos integer exponent */
                        xf = ipow(xf, (int) yf);
-               else
+               else {
+                       errno = 0;
                        xf = errcheck(pow(xf, yf), "pow");
+               }
                break;
        default:
                FATAL("illegal assignment operator %d", n);
@@ -1146,25 +1187,27 @@ Cell *cat(Node **a, int q)      /* a[0] cat a[1] */
 {
        Cell *x, *y, *z;
        int n1, n2;
-       char *s;
+       char *s = NULL;
+       int ssz = 0;
 
        x = execute(a[0]);
+       n1 = strlen(getsval(x));
+
        y = execute(a[1]);
-       getsval(x);
-       getsval(y);
-       n1 = strlen(x->sval);
-       n2 = strlen(y->sval);
-       s = (char *) malloc(n1 + n2 + 1);
-       if (s == NULL)
-               FATAL("out of space concatenating %.15s... and %.15s...",
-                       x->sval, y->sval);
-       strcpy(s, x->sval);
-       strcpy(s+n1, y->sval);
+       n2 = strlen(getsval(y));
+
+       adjbuf(&s, &ssz, n1 + n2 + 1, recsize, 0, "cat");
+       memcpy(s, x->sval, n1);
+       memcpy(s + n1, y->sval, n2);
+       s[n1 + n2] = '\0';
+
        tempfree(x);
        tempfree(y);
+
        z = gettemp();
        z->sval = s;
        z->tval = STR;
+
        return(z);
 }
 
@@ -1172,7 +1215,7 @@ Cell *pastat(Node **a, int n)     /* a[0] { a[1] } */
 {
        Cell *x;
 
-       if (a[0] == 0)
+       if (a[0] == NULL)
                x = execute(a[1]);
        else {
                x = execute(a[0]);
@@ -1209,20 +1252,23 @@ Cell *dopa2(Node **a, int n)    /* a[0], a[1] { a[2] } */
 
 Cell *split(Node **a, int nnn) /* split(a[0], a[1], a[2]); a[3] is type */
 {
-       Cell *x = 0, *y, *ap;
-       char *s, *origs;
+       Cell *x = NULL, *y, *ap;
+       const char *s, *origs, *t;
+       const char *fs = NULL;
+       char *origfs = NULL;
        int sep;
-       char *t, temp, num[50], *fs = 0;
+       char temp, num[50];
        int n, tempstat, arg3type;
 
        y = execute(a[0]);      /* source string */
        origs = s = strdup(getsval(y));
        arg3type = ptoi(a[3]);
-       if (a[2] == 0)          /* fs string */
-               fs = *FS;
+       if (a[2] == NULL)               /* fs string */
+               fs = getsval(fsloc);
        else if (arg3type == STRING) {  /* split(str,arr,"string") */
                x = execute(a[2]);
-               fs = getsval(x);
+               fs = origfs = strdup(getsval(x));
+               tempfree(x);
        } else if (arg3type == REGEXPR)
                fs = "(regexpr)";       /* split(str,arr,/regexpr/) */
        else
@@ -1254,18 +1300,18 @@ Cell *split(Node **a, int nnn)  /* split(a[0], a[1], a[2]); a[3] is type */
                        pfa->initstat = 2;
                        do {
                                n++;
-                               sprintf(num, "%d", n);
+                               snprintf(num, sizeof(num), "%d", n);
                                temp = *patbeg;
-                               *patbeg = '\0';
+                               setptr(patbeg, '\0');
                                if (is_number(s))
                                        setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval);
                                else
                                        setsymtab(num, s, 0.0, STR, (Array *) ap->sval);
-                               *patbeg = temp;
+                               setptr(patbeg, temp);
                                s = patbeg + patlen;
-                               if (*(patbeg+patlen-1) == 0 || *s == 0) {
+                               if (*(patbeg+patlen-1) == '\0' || *s == '\0') {
                                        n++;
-                                       sprintf(num, "%d", n);
+                                       snprintf(num, sizeof(num), "%d", n);
                                        setsymtab(num, "", 0.0, STR, (Array *) ap->sval);
                                        pfa->initstat = tempstat;
                                        goto spdone;
@@ -1275,7 +1321,7 @@ Cell *split(Node **a, int nnn)    /* split(a[0], a[1], a[2]); a[3] is type */
                                                        /* cf gsub and refldbld */
                }
                n++;
-               sprintf(num, "%d", n);
+               snprintf(num, sizeof(num), "%d", n);
                if (is_number(s))
                        setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval);
                else
@@ -1284,62 +1330,61 @@ Cell *split(Node **a, int nnn)  /* split(a[0], a[1], a[2]); a[3] is type */
                pfa = NULL;
        } else if (sep == ' ') {
                for (n = 0; ; ) {
-                       while (*s == ' ' || *s == '\t' || *s == '\n')
+#define ISWS(c)        ((c) == ' ' || (c) == '\t' || (c) == '\n')
+                       while (ISWS(*s))
                                s++;
-                       if (*s == 0)
+                       if (*s == '\0')
                                break;
                        n++;
                        t = s;
                        do
                                s++;
-                       while (*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0');
+                       while (*s != '\0' && !ISWS(*s));
                        temp = *s;
-                       *s = '\0';
-                       sprintf(num, "%d", n);
+                       setptr(s, '\0');
+                       snprintf(num, sizeof(num), "%d", n);
                        if (is_number(t))
                                setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
                        else
                                setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
-                       *s = temp;
-                       if (*s != 0)
+                       setptr(s, temp);
+                       if (*s != '\0')
                                s++;
                }
        } else if (sep == 0) {  /* new: split(s, a, "") => 1 char/elem */
-               for (n = 0; *s != 0; s++) {
+               for (n = 0; *s != '\0'; s++) {
                        char buf[2];
                        n++;
-                       sprintf(num, "%d", n);
+                       snprintf(num, sizeof(num), "%d", n);
                        buf[0] = *s;
-                       buf[1] = 0;
+                       buf[1] = '\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) {
+       } else if (*s != '\0') {
                for (;;) {
                        n++;
                        t = s;
                        while (*s != sep && *s != '\n' && *s != '\0')
                                s++;
                        temp = *s;
-                       *s = '\0';
-                       sprintf(num, "%d", n);
+                       setptr(s, '\0');
+                       snprintf(num, sizeof(num), "%d", n);
                        if (is_number(t))
                                setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
                        else
                                setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
-                       *s = temp;
-                       if (*s++ == 0)
+                       setptr(s, temp);
+                       if (*s++ == '\0')
                                break;
                }
        }
        tempfree(ap);
        tempfree(y);
-       free(origs);
-       if (a[2] != 0 && arg3type == STRING) {
-               tempfree(x);
-       }
+       xfree(origs);
+       xfree(origfs);
        x = gettemp();
        x->tval = NUM;
        x->fval = n;
@@ -1369,7 +1414,7 @@ Cell *ifstat(Node **a, int n)     /* if (a[0]) a[1]; else a[2] */
        if (istrue(x)) {
                tempfree(x);
                x = execute(a[1]);
-       } else if (a[2] != 0) {
+       } else if (a[2] != NULL) {
                tempfree(x);
                x = execute(a[2]);
        }
@@ -1421,7 +1466,7 @@ Cell *forstat(Node **a, int n)    /* for (a[0]; a[1]; a[2]) a[3] */
        x = execute(a[0]);
        tempfree(x);
        for (;;) {
-               if (a[1]!=0) {
+               if (a[1]!=NULL) {
                        x = execute(a[1]);
                        if (!istrue(x)) return(x);
                        else tempfree(x);
@@ -1469,16 +1514,74 @@ Cell *instat(Node **a, int n)   /* for (a[0] in a[1]) a[2] */
        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;
+       mbstate_t mbs, mbs2;
+       wchar_t wc;
+       size_t sz = MB_CUR_MAX;
+
+       if (sz == 1) {
+               buf = tostring(s);
+
+               for (pbuf = buf; *pbuf; pbuf++)
+                       *pbuf = fun_c((uschar)*pbuf);
+
+               return buf;
+       } else {
+               /* upper/lower character may be shorter/longer */
+               buf = tostringN(s, strlen(s) * sz + 1);
+
+               memset(&mbs,  0, sizeof(mbs));
+               memset(&mbs2, 0, sizeof(mbs2));
+
+               ps   = s;
+               pbuf = buf;
+               while (n = mbrtowc(&wc, ps, sz, &mbs),
+                      n > 0 && n != (size_t)-1 && n != (size_t)-2)
+               {
+                       ps += n;
+
+                       n = wcrtomb(pbuf, fun_wc(wc), &mbs2);
+                       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;
+       }
+}
+
+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)   /* builtin functions. a[0] is type, a[1] is arg list */
 {
        Cell *x, *y;
        Awkfloat u;
        int t;
        Awkfloat tmp;
-       char *p, *buf;
+       char *buf;
        Node *nextarg;
        FILE *fp;
-       void flush_all(void);
+       int status = 0;
 
        t = ptoi(a[0]);
        x = execute(a[1]);
@@ -1491,19 +1594,25 @@ Cell *bltin(Node **a, int n)    /* builtin functions. a[0] is type, a[1] is arg lis
                        u = strlen(getsval(x));
                break;
        case FLOG:
-               u = errcheck(log(getfval(x)), "log"); break;
+               errno = 0;
+               u = errcheck(log(getfval(x)), "log");
+               break;
        case FINT:
                modf(getfval(x), &u); break;
        case FEXP:
-               u = errcheck(exp(getfval(x)), "exp"); break;
+               errno = 0;
+               u = errcheck(exp(getfval(x)), "exp");
+               break;
        case FSQRT:
-               u = errcheck(sqrt(getfval(x)), "sqrt"); break;
+               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 == 0) {
+               if (nextarg == NULL) {
                        WARNING("atan2 requires two arguments; returning 1.0");
                        u = 1.0;
                } else {
@@ -1515,11 +1624,26 @@ 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 */
-               u = (Awkfloat) (rand() % RAND_MAX) / RAND_MAX;
+               /* random() returns numbers in [0..2^31-1]
+                * in order to get a number in [0, 1), divide it by 2^31
+                */
+               u = (Awkfloat) random() / (0x7fffffffL + 0x1UL);
                break;
        case FSRAND:
                if (isrec(x))   /* no argument provided */
@@ -1527,22 +1651,16 @@ Cell *bltin(Node **a, int n)    /* builtin functions. a[0] is type, a[1] is arg lis
                else
                        u = getfval(x);
                tmp = u;
-               srand((unsigned int) u);
+               srandom((unsigned long) u);
                u = srand_seed;
                srand_seed = tmp;
                break;
        case FTOUPPER:
        case FTOLOWER:
-               buf = tostring(getsval(x));
-               if (t == FTOUPPER) {
-                       for (p = buf; *p; p++)
-                               if (islower((uschar) *p))
-                                       *p = toupper((uschar)*p);
-               } else {
-                       for (p = buf; *p; p++)
-                               if (isupper((uschar) *p))
-                                       *p = tolower((uschar)*p);
-               }
+               if (t == FTOUPPER)
+                       buf = nawk_toupper(getsval(x));
+               else
+                       buf = nawk_tolower(getsval(x));
                tempfree(x);
                x = gettemp();
                setsval(x, buf);
@@ -1552,7 +1670,7 @@ Cell *bltin(Node **a, int n)      /* builtin functions. a[0] is type, a[1] is arg lis
                if (isrec(x) || strlen(getsval(x)) == 0) {
                        flush_all();    /* fflush() or fflush("") -> all */
                        u = 0;
-               } else if ((fp = openfile(FFLUSH, getsval(x))) == NULL)
+               } else if ((fp = openfile(FFLUSH, getsval(x), NULL)) == NULL)
                        u = EOF;
                else
                        u = fflush(fp);
@@ -1564,7 +1682,7 @@ Cell *bltin(Node **a, int n)      /* builtin functions. a[0] is type, a[1] is arg lis
        tempfree(x);
        x = gettemp();
        setfval(x, u);
-       if (nextarg != 0) {
+       if (nextarg != NULL) {
                WARNING("warning: function has too many arguments");
                for ( ; nextarg; nextarg = nextarg->nnext)
                        execute(nextarg);
@@ -1578,7 +1696,7 @@ Cell *printstat(Node **a, int n)  /* print a[0] */
        Cell *y;
        FILE *fp;
 
-       if (a[1] == 0)  /* a[1] is redirection operator, a[2] is file */
+       if (a[1] == NULL)       /* a[1] is redirection operator, a[2] is file */
                fp = stdout;
        else
                fp = redirect(ptoi(a[1]), a[2]);
@@ -1587,11 +1705,11 @@ Cell *printstat(Node **a, int n)        /* print a[0] */
                fputs(getpssval(y), fp);
                tempfree(y);
                if (x->nnext == NULL)
-                       fputs(*ORS, fp);
+                       fputs(getsval(orsloc), fp);
                else
-                       fputs(*OFS, fp);
+                       fputs(getsval(ofsloc), fp);
        }
-       if (a[1] != 0)
+       if (a[1] != NULL)
                fflush(fp);
        if (ferror(fp))
                FATAL("write error on %s", filename(fp));
@@ -1600,8 +1718,6 @@ Cell *printstat(Node **a, int n)  /* print a[0] */
 
 Cell *nullproc(Node **a, int n)
 {
-       n = n;
-       a = a;
        return 0;
 }
 
@@ -1614,7 +1730,7 @@ FILE *redirect(int a, Node *b)    /* set up all i/o redirections */
 
        x = execute(b);
        fname = getsval(x);
-       fp = openfile(a, fname);
+       fp = openfile(a, fname, NULL);
        if (fp == NULL)
                FATAL("can't open file %s", fname);
        tempfree(x);
@@ -1627,14 +1743,14 @@ struct files {
        int     mode;   /* '|', 'a', 'w' => LE/LT, GT */
 } *files;
 
-int nfiles;
+size_t nfiles;
 
-void stdinit(void)     /* in case stdin, etc., are not constants */
+static void stdinit(void)      /* in case stdin, etc., are not constants */
 {
        nfiles = FOPEN_MAX;
        files = calloc(nfiles, sizeof(*files));
        if (files == NULL)
-               FATAL("can't allocate file memory for %u files", nfiles);
+               FATAL("can't allocate file memory for %zu files", nfiles);
         files[0].fp = stdin;
        files[0].fname = "/dev/stdin";
        files[0].mode = LT;
@@ -1646,33 +1762,35 @@ void stdinit(void)      /* in case stdin, etc., are not constants */
        files[2].mode = GT;
 }
 
-FILE *openfile(int a, const char *us)
+FILE *openfile(int a, const char *us, bool *pnewflag)
 {
        const char *s = us;
-       int i, m;
-       FILE *fp = 0;
+       size_t i;
+       int m;
+       FILE *fp = NULL;
 
        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) {
-                       if (a == files[i].mode || (a==APPEND && files[i].mode==GT))
-                               return files[i].fp;
-                       if (a == FFLUSH)
-                               return files[i].fp;
+       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)        /* didn't find it, so don't create it! */
                return NULL;
 
-       for (i=0; i < nfiles; i++)
-               if (files[i].fp == 0)
+       for (i = 0; i < nfiles; i++)
+               if (files[i].fp == NULL)
                        break;
        if (i >= nfiles) {
                struct files *nf;
-               int nnf = nfiles + FOPEN_MAX;
+               size_t nnf = nfiles + FOPEN_MAX;
                nf = realloc(files, nnf * sizeof(*nf));
                if (nf == NULL)
-                       FATAL("cannot grow files for %s and %d files", s, nnf);
+                       FATAL("cannot grow files for %s and %zu files", s, nnf);
                memset(&nf[nfiles], 0, FOPEN_MAX * sizeof(*nf));
                nfiles = nnf;
                files = nf;
@@ -1696,13 +1814,17 @@ FILE *openfile(int a, const char *us)
                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)
 {
-       int i;
+       size_t i;
 
        for (i = 0; i < nfiles; i++)
                if (fp == files[i].fp)
@@ -1710,79 +1832,80 @@ const char *filename(FILE *fp)
        return "???";
 }
 
-Cell *closefile(Node **a, int n)
-{
-       Cell *x;
-       int i, stat;
-
-       n = n;
-       x = execute(a[0]);
-       getsval(x);
-       stat = -1;
-       for (i = 0; i < nfiles; i++) {
-               if (files[i].fname && strcmp(x->sval, files[i].fname) == 0) {
-                       if (ferror(files[i].fp))
-                               WARNING( "i/o error occurred on %s", files[i].fname );
-                       if (files[i].mode == '|' || files[i].mode == LE)
-                               stat = pclose(files[i].fp);
-                       else
-                               stat = fclose(files[i].fp);
-                       if (stat == EOF)
-                               WARNING( "i/o error occurred closing %s", files[i].fname );
-                       if (i > 2)      /* don't do /dev/std... */
-                               xfree(files[i].fname);
-                       files[i].fname = NULL;  /* watch out for ref thru this */
-                       files[i].fp = NULL;
-               }
-       }
-       tempfree(x);
-       x = gettemp();
-       setfval(x, (Awkfloat) stat);
-       return(x);
-}
+ 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 (ferror(files[i].fp))
+                       FATAL("i/o error occurred on %s", files[i].fname);
+               if (files[i].mode == '|' || files[i].mode == LE)
+                       stat = pclose(files[i].fp) == -1;
+               else
+                       stat = fclose(files[i].fp) == EOF;
+               if (stat)
+                       FATAL("i/o error occurred closing %s", files[i].fname);
+               if (i > 2)      /* don't do /dev/std... */
+                       xfree(files[i].fname);
+               files[i].fname = NULL;  /* watch out for ref thru this */
+               files[i].fp = NULL;
+       }
+       tempfree(x);
+       x = gettemp();
+       setfval(x, (Awkfloat) (stat ? -1 : 0));
+       return(x);
+ }
 
 void closeall(void)
 {
-       int i, stat;
-
-       for (i = 0; i < FOPEN_MAX; i++) {
-               if (files[i].fp) {
-                       if (ferror(files[i].fp))
-                               WARNING( "i/o error occurred on %s", files[i].fname );
-                       if (files[i].mode == '|' || files[i].mode == LE)
-                               stat = pclose(files[i].fp);
-                       else
-                               stat = fclose(files[i].fp);
-                       if (stat == EOF)
-                               WARNING( "i/o error occurred while closing %s", files[i].fname );
-               }
+       size_t i;
+       bool stat = false;
+
+       for (i = 0; i < nfiles; i++) {
+               if (! files[i].fp)
+                       continue;
+               if (ferror(files[i].fp))
+                       FATAL( "i/o error occurred on %s", files[i].fname );
+               if (files[i].mode == '|' || files[i].mode == LE)
+                       stat = pclose(files[i].fp) == -1;
+               else
+                       stat = fclose(files[i].fp) == EOF;
+               if (stat)
+                       FATAL( "i/o error occurred while closing %s", files[i].fname );
        }
 }
 
-void flush_all(void)
+static void flush_all(void)
 {
-       int i;
+       size_t i;
 
        for (i = 0; i < nfiles; i++)
                if (files[i].fp)
                        fflush(files[i].fp);
 }
 
-void backsub(char **pb_ptr, char **sptr_ptr);
+void backsub(char **pb_ptr, const char **sptr_ptr);
 
 Cell *sub(Node **a, int nnn)   /* substitute command */
 {
-       char *sptr, *pb, *q;
+       const char *sptr, *q;
        Cell *x, *y, *result;
-       char *t, *buf;
+       char *t, *buf, *pb;
        fa *pfa;
        int bufsz = recsize;
 
-       if ((buf = (char *) malloc(bufsz)) == NULL)
+       if ((buf = malloc(bufsz)) == NULL)
                FATAL("out of memory in sub");
        x = execute(a[3]);      /* target string */
        t = getsval(x);
-       if (a[0] == 0)          /* 0 => a[1] is already-compiled regexpr */
+       if (a[0] == NULL)       /* 0 => a[1] is already-compiled regexpr */
                pfa = (fa *) a[1];      /* regular expression */
        else {
                y = execute(a[1]);
@@ -1798,7 +1921,7 @@ Cell *sub(Node **a, int nnn)      /* substitute command */
                while (sptr < patbeg)
                        *pb++ = *sptr++;
                sptr = getsval(y);
-               while (*sptr != 0) {
+               while (*sptr != '\0') {
                        adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "sub");
                        if (*sptr == '\\') {
                                backsub(&pb, &sptr);
@@ -1816,13 +1939,13 @@ Cell *sub(Node **a, int nnn)    /* substitute command */
                sptr = patbeg + patlen;
                if ((patlen == 0 && *patbeg) || (patlen && *(sptr-1))) {
                        adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "sub");
-                       while ((*pb++ = *sptr++) != 0)
-                               ;
+                       while ((*pb++ = *sptr++) != '\0')
+                               continue;
                }
                if (pb > buf + bufsz)
                        FATAL("sub result2 %.30s too big; can't happen", buf);
                setsval(x, buf);        /* BUG: should be able to avoid copy */
-               result = True;;
+               result = True;
        }
        tempfree(x);
        tempfree(y);
@@ -1833,19 +1956,20 @@ Cell *sub(Node **a, int nnn)    /* substitute command */
 Cell *gsub(Node **a, int nnn)  /* global substitute */
 {
        Cell *x, *y;
-       char *rptr, *sptr, *t, *pb, *q;
+       char *rptr, *pb;
+       const char *q, *t, *sptr;
        char *buf;
        fa *pfa;
        int mflag, tempstat, num;
        int bufsz = recsize;
 
-       if ((buf = (char *) malloc(bufsz)) == NULL)
+       if ((buf = malloc(bufsz)) == NULL)
                FATAL("out of memory in gsub");
        mflag = 0;      /* if mflag == 0, can replace empty string */
        num = 0;
        x = execute(a[3]);      /* target string */
        t = getsval(x);
-       if (a[0] == 0)          /* 0 => a[1] is already-compiled regexpr */
+       if (a[0] == NULL)       /* 0 => a[1] is already-compiled regexpr */
                pfa = (fa *) a[1];      /* regular expression */
        else {
                y = execute(a[1]);
@@ -1859,11 +1983,11 @@ Cell *gsub(Node **a, int nnn)   /* global substitute */
                pb = buf;
                rptr = getsval(y);
                do {
-                       if (patlen == 0 && *patbeg != 0) {      /* matched empty string */
+                       if (patlen == 0 && *patbeg != '\0') {   /* matched empty string */
                                if (mflag == 0) {       /* can replace empty */
                                        num++;
                                        sptr = rptr;
-                                       while (*sptr != 0) {
+                                       while (*sptr != '\0') {
                                                adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
                                                if (*sptr == '\\') {
                                                        backsub(&pb, &sptr);
@@ -1876,7 +2000,7 @@ Cell *gsub(Node **a, int nnn)     /* global substitute */
                                                        *pb++ = *sptr++;
                                        }
                                }
-                               if (*t == 0)    /* at end */
+                               if (*t == '\0') /* at end */
                                        goto done;
                                adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gsub");
                                *pb++ = *t++;
@@ -1891,7 +2015,7 @@ Cell *gsub(Node **a, int nnn)     /* global substitute */
                                while (sptr < patbeg)
                                        *pb++ = *sptr++;
                                sptr = rptr;
-                               while (*sptr != 0) {
+                               while (*sptr != '\0') {
                                        adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
                                        if (*sptr == '\\') {
                                                backsub(&pb, &sptr);
@@ -1904,7 +2028,7 @@ Cell *gsub(Node **a, int nnn)     /* global substitute */
                                                *pb++ = *sptr++;
                                }
                                t = patbeg + patlen;
-                               if (patlen == 0 || *t == 0 || *(t-1) == 0)
+                               if (patlen == 0 || *t == '\0' || *(t-1) == '\0')
                                        goto done;
                                if (pb > buf + bufsz)
                                        FATAL("gsub result1 %.30s too big; can't happen", buf);
@@ -1913,8 +2037,8 @@ Cell *gsub(Node **a, int nnn)     /* global substitute */
                } while (pmatch(pfa,t));
                sptr = t;
                adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gsub");
-               while ((*pb++ = *sptr++) != 0)
-                       ;
+               while ((*pb++ = *sptr++) != '\0')
+                       continue;
        done:   if (pb < buf + bufsz)
                        *pb = '\0';
                else if (*(pb-1) != '\0')
@@ -1931,9 +2055,17 @@ Cell *gsub(Node **a, int nnn)    /* global substitute */
        return(x);
 }
 
-void backsub(char **pb_ptr, char **sptr_ptr)   /* handle \\& variations */
+void backsub(char **pb_ptr, const char **sptr_ptr)     /* handle \\& variations */
 {                                              /* sptr[0] == '\\' */
-       char *pb = *pb_ptr, *sptr = *sptr_ptr;
+       char *pb = *pb_ptr;
+       const char *sptr = *sptr_ptr;
+       static bool first = true;
+       static bool do_posix = false;
+
+       if (first) {
+               first = false;
+               do_posix = (getenv("POSIXLY_CORRECT") != NULL);
+       }
 
        if (sptr[1] == '\\') {
                if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */
@@ -1943,6 +2075,9 @@ void backsub(char **pb_ptr, char **sptr_ptr)      /* handle \\& variations */
                } else if (sptr[2] == '&') {    /* \\& -> \ + matched */
                        *pb++ = '\\';
                        sptr += 2;
+               } else if (do_posix) {          /* \\x -> \x */
+                       sptr++;
+                       *pb++ = *sptr++;
                } else {                        /* \\x -> \\x */
                        *pb++ = *sptr++;
                        *pb++ = *sptr++;
index a9fa325..4efaa21 100644 (file)
@@ -55,10 +55,14 @@ Cell        *fsloc;         /* FS */
 Cell   *nrloc;         /* NR */
 Cell   *nfloc;         /* NF */
 Cell   *fnrloc;        /* FNR */
+Cell   *ofsloc;        /* OFS */
+Cell   *orsloc;        /* ORS */
+Cell   *rsloc;         /* RS */
 Array  *ARGVtab;       /* symbol table containing ARGV[...] */
 Array  *ENVtab;        /* symbol table containing ENVIRON[...] */
 Cell   *rstartloc;     /* RSTART */
 Cell   *rlengthloc;    /* RLENGTH */
+Cell   *subseploc;     /* SUBSEP */
 Cell   *symtabloc;     /* SYMTAB */
 
 Cell   *nullloc;       /* a guaranteed empty cell */
@@ -67,6 +71,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);
@@ -76,9 +92,12 @@ void syminit(void)   /* initialize symbol table with builtin vars */
 
        fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
        FS = &fsloc->sval;
-       RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
-       OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval;
-       ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
+       rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
+       RS = &rsloc->sval;
+       ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
+       OFS = &ofsloc->sval;
+       orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
+       ORS = &orsloc->sval;
        OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
        CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
        FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
@@ -88,12 +107,14 @@ void syminit(void) /* initialize symbol table with builtin vars */
        NR = &nrloc->fval;
        fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
        FNR = &fnrloc->fval;
-       SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval;
+       subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
+       SUBSEP = &subseploc->sval;
        rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
        RSTART = &rstartloc->fval;
        rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
        RLENGTH = &rlengthloc->fval;
        symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
+       free(symtabloc->sval);
        symtabloc->sval = (char *) symtab;
 }
 
@@ -106,6 +127,7 @@ void arginit(int ac, char **av)     /* set up ARGV and ARGC */
        ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
        cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
        ARGVtab = makesymtab(NSYMTAB);  /* could be (int) ARGC as well */
+       free(cp->sval);
        cp->sval = (char *) ARGVtab;
        for (i = 0; i < ac; i++) {
                sprintf(temp, "%d", i);
@@ -124,6 +146,7 @@ void envinit(char **envp)   /* set up ENVIRON variable */
 
        cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
        ENVtab = makesymtab(NSYMTAB);
+       free(cp->sval);
        cp->sval = (char *) ENVtab;
        for ( ; *envp; envp++) {
                if ((p = strchr(*envp, '=')) == NULL)
@@ -144,8 +167,8 @@ Array *makesymtab(int n)    /* make a new symbol table */
        Array *ap;
        Cell **tp;
 
-       ap = (Array *) malloc(sizeof(Array));
-       tp = (Cell **) calloc(n, sizeof(Cell *));
+       ap = malloc(sizeof(*ap));
+       tp = calloc(n, sizeof(*tp));
        if (ap == NULL || tp == NULL)
                FATAL("out of space in makesymtab");
        ap->nelem = 0;
@@ -171,10 +194,10 @@ void freesymtab(Cell *ap) /* free a symbol table */
                        if (freeable(cp))
                                xfree(cp->sval);
                        temp = cp->cnext;       /* avoids freeing then using */
-                       free(cp); 
+                       free(cp);
                        tp->nelem--;
                }
-               tp->tab[i] = 0;
+               tp->tab[i] = NULL;
        }
        if (tp->nelem != 0)
                WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
@@ -187,7 +210,7 @@ void freeelem(Cell *ap, const char *s)      /* free elem s from ap (i.e., ap["s"] */
        Array *tp;
        Cell *p, *prev = NULL;
        int h;
-       
+
        tp = (Array *) ap->sval;
        h = hash(s, tp->size);
        for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
@@ -215,7 +238,7 @@ Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
                        (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval) );
                return(p);
        }
-       p = (Cell *) malloc(sizeof(Cell));
+       p = malloc(sizeof(*p));
        if (p == NULL)
                FATAL("out of space for symbol table at %s", n);
        p->nval = tostring(n);
@@ -250,7 +273,7 @@ void rehash(Array *tp)      /* rehash items in small table into big one */
        Cell *cp, *op, **np;
 
        nsz = GROWTAB * tp->size;
-       np = (Cell **) calloc(nsz, sizeof(Cell *));
+       np = calloc(nsz, sizeof(*np));
        if (np == NULL)         /* can't do it, but can keep running. */
                return;         /* someone else will run out later. */
        for (i = 0; i < tp->size; i++) {
@@ -282,21 +305,31 @@ Awkfloat setfval(Cell *vp, Awkfloat f)    /* set float val of a Cell */
 {
        int fldno;
 
-       if ((vp->tval & (NUM | STR)) == 0) 
+       f += 0.0;               /* normalise negative zero to positive zero */
+       if ((vp->tval & (NUM | STR)) == 0)
                funnyvar(vp, "assign to");
        if (isfld(vp)) {
-               donerec = 0;    /* mark $0 invalid */
+               donerec = false;        /* mark $0 invalid */
                fldno = atoi(vp->nval);
                if (fldno > *NF)
                        newfld(fldno);
                   dprintf( ("setting field %d to %g\n", fldno, f) );
+       } else if (&vp->fval == NF) {
+               donerec = false;        /* mark $0 invalid */
+               setlastfld(f);
+               dprintf( ("setting NF to %g\n", f) );
        } else if (isrec(vp)) {
-               donefld = 0;    /* mark $1... invalid */
-               donerec = 1;
+               donefld = false;        /* mark $1... invalid */
+               donerec = true;
+               savefs();
+       } else if (vp == ofsloc) {
+               if (!donerec)
+                       recbld();
        }
        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;
@@ -311,46 +344,60 @@ void funnyvar(Cell *vp, const char *rw)
        if (vp->tval & FCN)
                FATAL("can't %s %s; it's a function.", rw, vp->nval);
        WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
-               vp, vp->nval, vp->sval, vp->fval, vp->tval);
+               (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
 }
 
 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", 
+          dprintf( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
                (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) );
        if ((vp->tval & (NUM | STR)) == 0)
                funnyvar(vp, "assign to");
        if (isfld(vp)) {
-               donerec = 0;    /* mark $0 invalid */
+               donerec = false;        /* mark $0 invalid */
                fldno = atoi(vp->nval);
                if (fldno > *NF)
                        newfld(fldno);
                   dprintf( ("setting field %d to %s (%p)\n", fldno, s, s) );
        } else if (isrec(vp)) {
-               donefld = 0;    /* mark $1... invalid */
-               donerec = 1;
+               donefld = false;        /* mark $1... invalid */
+               donerec = true;
+               savefs();
+       } else if (vp == ofsloc) {
+               if (!donerec)
+                       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;
-          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->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) );
+       vp->sval = t;
+       if (&vp->fval == NF) {
+               donerec = false;        /* 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 */
 {
        if ((vp->tval & (NUM | STR)) == 0)
                funnyvar(vp, "read value of");
-       if (isfld(vp) && donefld == 0)
+       if (isfld(vp) && !donefld)
                fldbld();
-       else if (isrec(vp) && donerec == 0)
+       else if (isrec(vp) && !donerec)
                recbld();
        if (!isnum(vp)) {       /* not a number */
                vp->fval = atof(vp->sval);      /* best guess */
@@ -364,26 +411,87 @@ Awkfloat getfval(Cell *vp)        /* get float val of a Cell */
 
 static char *get_str_val(Cell *vp, char **fmt)        /* get string val of a Cell */
 {
-       char s[100];    /* BUG: unchecked */
+       char s[256];
        double dtemp;
 
        if ((vp->tval & (NUM | STR)) == 0)
                funnyvar(vp, "read value of");
-       if (isfld(vp) && donefld == 0)
+       if (isfld(vp) && ! donefld)
                fldbld();
-       else if (isrec(vp) && donerec == 0)
+       else if (isrec(vp) && ! donerec)
                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 */ \
+                       snprintf(s, sizeof (s), "%.30g", vp->fval); \
+               else \
+                       snprintf(s, sizeof (s), *fmt, vp->fval); \
+               vp->sval = tostring(s); \
+               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 */
-                       sprintf(s, "%.30g", vp->fval);
-               else
-                       sprintf(s, *fmt, vp->fval);
-               vp->sval = tostring(s);
-               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);
@@ -401,24 +509,57 @@ char *getpssval(Cell *vp)     /* get string val of a Cell for print */
 
 
 char *tostring(const char *s)  /* make a copy of string s */
+{
+       char *p = strdup(s);
+       if (p == NULL)
+               FATAL("out of space in tostring on %s", s);
+       return(p);
+}
+
+char *tostringN(const char *s, size_t n)       /* make a copy of string s */
 {
        char *p;
 
-       p = (char *) malloc(strlen(s)+1);
+       p = malloc(n);
        if (p == NULL)
                FATAL("out of space in tostring on %s", s);
        strcpy(p, s);
        return(p);
 }
 
+Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
+{
+       Cell *c;
+       char *p;
+       char *sa = getsval(a);
+       char *sb = getsval(b);
+       size_t l = strlen(sa) + strlen(sb) + 1;
+       p = malloc(l);
+       if (p == NULL)
+               FATAL("out of space concatenating %s and %s", sa, sb);
+       snprintf(p, l, "%s%s", sa, sb);
+
+       l++;    // add room for ' '
+       char *newbuf = malloc(l);
+       if (newbuf == NULL)
+               FATAL("out of space concatenating %s and %s", sa, sb);
+       // See string() in lex.c; a string "xx" is stored in the symbol
+       // table as "xx ".
+       snprintf(newbuf, l, "%s ", p);
+       c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
+       free(p);
+       free(newbuf);
+       return c;
+}
+
 char *qstring(const char *is, int delim)       /* collect string up to next delim */
 {
        const char *os = is;
        int c, n;
-       uschar *s = (uschar *) is;
+       const uschar *s = (const uschar *) is;
        uschar *buf, *bp;
 
-       if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
+       if ((buf = malloc(strlen(is)+3)) == NULL)
                FATAL( "out of space in qstring(%s)", s);
        for (bp = buf; (c = *s) != delim; s++) {
                if (c == '\n')
@@ -430,7 +571,7 @@ char *qstring(const char *is, int delim)    /* collect string up to next delim */
                        if (c == 0) {   /* \ at end */
                                *bp++ = '\\';
                                break;  /* for loop */
-                       }       
+                       }
                        switch (c) {
                        case '\\':      *bp++ = '\\'; break;
                        case 'n':       *bp++ = '\n'; break;
@@ -438,6 +579,8 @@ char *qstring(const char *is, int delim)    /* collect string up to next delim */
                        case 'b':       *bp++ = '\b'; break;
                        case 'f':       *bp++ = '\f'; break;
                        case 'r':       *bp++ = '\r'; break;
+                       case 'v':       *bp++ = '\v'; break;
+                       case 'a':       *bp++ = '\a'; break;
                        default:
                                if (!isdigit(c)) {
                                        *bp++ = c;
@@ -457,3 +600,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;
+}