hbase

heirloom base
git clone git://git.2f30.org/hbase
Log | Files | Refs | README

commit 52c4f9942a6e7e32caedd5753870f5305512be7c
parent 519d61827edafdfdb74aa070c3e7bb47cca843f1
Author: Daniel Bainton <dpb@driftaway.org>
Date:   Fri, 11 Apr 2014 13:58:11 +0300

Add awk from obase and remove heirloom nawk

Diffstat:
Aawk/FIXES | 1011+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/README | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/awk.1 | 800+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/awk.h | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/awkgram.y | 487+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/b.c | 958+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/gen/maketab.c | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/lex.c | 597+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/lib.c | 738+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/main.c | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/mkfile | 27+++++++++++++++++++++++++++
Aawk/parse.c | 277+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/proto.h | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/run.c | 2017+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/tran.c | 458+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmkfile | 2+-
Dnawk/COPYING | 340-------------------------------------------------------------------------------
Dnawk/NOTES | 20--------------------
Dnawk/awk.g.y | 468-------------------------------------------------------------------------------
Dnawk/awk.h | 387-------------------------------------------------------------------------------
Dnawk/awk.lx.l | 383-------------------------------------------------------------------------------
Dnawk/b.c | 174-------------------------------------------------------------------------------
Dnawk/lib.c | 852-------------------------------------------------------------------------------
Dnawk/main.c | 215-------------------------------------------------------------------------------
Dnawk/maketab.c | 177-------------------------------------------------------------------------------
Dnawk/mkfile | 53-----------------------------------------------------
Dnawk/nawk.1 | 585-------------------------------------------------------------------------------
Dnawk/parse.c | 248-------------------------------------------------------------------------------
Dnawk/run.c | 1962-------------------------------------------------------------------------------
Dnawk/tran.c | 483-------------------------------------------------------------------------------
Dnawk/version.c | 25-------------------------
31 files changed, 8292 insertions(+), 6373 deletions(-)

diff --git a/awk/FIXES b/awk/FIXES @@ -0,0 +1,1011 @@ +/* $OpenBSD: FIXES,v 1.16 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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 file lists all bug fixes, changes, etc., made since the AWK book +was sent to the printers in August, 1987. + +Aug 10, 2011: + another fix to avoid core dump with delete(ARGV); again, many thanks + to ruslan ermilov. + +Aug 7, 2011: + split(s, a, //) now behaves the same as split(s, a, "") + +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. + + removed fixed limit on number of open files; thanks to aleksey + cheusov and christos zoulos. + + fixed day 1 bug that resurrected deleted elements of ARGV when + used as filenames (in lib.c). + + minor type fiddles to make gcc -Wall -pedantic happier (but not + totally so); turned on -fno-strict-aliasing in makefile. + +May 6, 2011: + added #ifdef for isblank. + now allows -ffoo as well as -f foo arguments. + (thanks, ruslan) + +May 1, 2011: + after advice from todd miller, kevin lo, ruslan ermilov, + 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. + + fixed a subtle (and i hope low-probability) overflow error + 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 + longer have anything like a current windows environment, so + i can't test any of it. + +May 23, 2010: + fixed long-standing overflow bug in run.c; many thanks to + nelson beebe for spotting it and providing the fix. + + fixed bug that didn't parse -vd=1 properly; thanks to santiago + vila for spotting it. + +Feb 8, 2010: + i give up. replaced isblank with isspace in b.c; there are + no consistent header files. + +Nov 26, 2009: + fixed a long-standing issue with when FS takes effect. a + change to FS is now noticed immediately for subsequent splits. + + changed the name getline() to awkgetline() to avoid yet another + name conflict somewhere. + +Feb 11, 2009: + temporarily for now defined HAS_ISBLANK, since that seems to + be the best way through the thicket. isblank arrived in C99, + but seems to be arriving at different systems at different + times. + +Oct 8, 2008: + fixed typo in b.c that set tmpvec wrongly. no one had ever + run into the problem, apparently. thanks to alistair crooks. + +Oct 23, 2007: + minor fix in lib.c: increase inputFS to 100, change malloc + for fields to n+1. + + fixed memory fault caused by out of order test in setsval. + + thanks to david o'brien, freebsd, for both fixes. + +May 1, 2007: + fiddle in makefile to fix for BSD make; thanks to igor sobrado. + +Mar 31, 2007: + fixed some null pointer refs calling adjbuf. + +Feb 21, 2007: + fixed a bug in matching the null RE in sub and gsub. thanks to al aho + who actually did the fix (in b.c), and to wolfgang seeberg for finding + it and providing a very compact test case. + + fixed quotation in b.c; thanks to Hal Pratt and the Princeton Dante + Project. + + removed some no-effect asserts in run.c. + + fiddled maketab.c to not complain about bison-generated values. + + removed the obsolete -V argument; fixed --version to print the + version and exit. + + fixed wording and an outright error in the usage message; thanks to igor + sobrado and jason mcintyre. + + fixed a bug in -d that caused core dump if no program followed. + +Jan 1, 2007: + dropped mac.code from makefile; there are few non-MacOSX + mac's these days. + +Jan 17, 2006: + system() not flagged as unsafe in the unadvertised -safe option. + found it while enhancing tests before shipping the ;login: article. + practice what you preach. + + removed the 9-years-obsolete -mr and -mf flags. + + added -version and --version options. + + core dump on linux with BEGIN {nextfile}, now fixed. + + removed some #ifdef's in run.c and lex.c that appear to no + longer be necessary. + +Apr 24, 2005: + modified lib.c so that values of $0 et al are preserved in the END + block, apparently as required by posix. thanks to havard eidnes + for the report and code. + +Jan 14, 2005: + fixed infinite loop in parsing, originally found by brian tsang. + thanks to arnold robbins for a suggestion that started me + 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 + todd miller. + +Dec 22, 2004: + cranked up size of NCHARS; coverity thinks it can be overrun with + smaller size, and i think that's right. added some assertions to b.c + to catch places where it might overrun. the RE code is still fragile. + +Dec 5, 2004: + fixed a couple of overflow problems with ridiculous field numbers: + e.g., print $(2^32-1). thanks to ruslan ermilov, giorgos keramidas + and david o'brien at freebsd.org for patches. this really should + be re-done from scratch. + +Nov 21, 2004: + fixed another 25-year-old RE bug, in split. it's another failure + to (re-)initialize. thanks to steve fisher for spotting this and + providing a good test case. + +Nov 22, 2003: + fixed a bug in regular expressions that dates (so help me) from 1977; + it's been there from the beginning. an anchored longest match that + was longer than the number of states triggered a failure to initialize + the machine properly. many thanks to moinak ghosh for not only finding + this one but for providing a fix, in some of the most mysterious + 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 + spotting this very subtle one. + +Jul 31, 2003: + fixed, thanks to andrey chernov and ruslan ermilov, a bug in lex.c + that mis-handled the character 255 in input. (it was being compared + to EOF with a signed comparison.) + +Jul 29, 2003: + fixed (i think) the long-standing botch that included the beginning of + line state ^ for RE's in the set of valid characters; this led to a + variety of odd problems, including failure to properly match certain + regular expressions in non-US locales. thanks to ruslan for keeping + at this one. + +Jul 28, 2003: + n-th try at getting internationalization right, with thanks to volker + kiefel, arnold robbins and ruslan ermilov for advice, though they + should not be blamed for the outcome. according to posix, "." is the + 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 + in locale fr_CA. i can see how this might arise by implementation + but i cannot explain it to a human user. (this behavior can be seen + in gawk as well; we're leaning on the same library.) + + the issue appears to be that strcoll is meant for sorting, where + merging upper and lower case may make sense (though note that unix + sort does not do this by default either). it is not appropriate + for regular expressions, where the goal is to match specific + patterns of characters. in any case, the notations [:lower:], etc., + are available in awk, and they are more likely to work correctly in + most locales. + + a moratorium is hereby declared on internationalization changes. + i apologize to friends and colleagues in other parts of the world. + i would truly like to get this "right", but i don't know what + that is, and i do not want to keep making changes until it's clear. + +Jul 4, 2003: + fixed bug that permitted non-terminated RE, as in "awk /x". + +Jun 1, 2003: + subtle change to split: if source is empty, number of elems + is always 0 and the array is not set. + +Mar 21, 2003: + added some parens to isblank, in another attempt to make things + internationally portable. + +Mar 14, 2003: + the internationalization changes, somewhat modified, are now + reinstated. in theory awk will now do character comparisons + and case conversions in national language, but "." will always + be the decimal point separator on input and output regardless + of national language. isblank(){} has an #ifndef. + + this no longer compiles on windows: LC_MESSAGES isn't defined + in vc6++. + + fixed subtle behavior in field and record splitting: if FS is + a single character and RS is not empty, \n is NOT a separator. + this tortuous reading is found in the awk book; behavior now + matches gawk and mawk. + +Dec 13, 2002: + for the moment, the internationalization changes of nov 29 are + rolled back -- programs like x = 1.2 don't work in some locales, + because the parser is expecting x = 1,2. until i understand this + better, this will have to wait. + +Nov 29, 2002: + modified b.c (with tiny changes in main and run) to support + locales, using strcoll and iswhatever tests for posix character + classes. thanks to ruslan ermilov (ru@freebsd.org) for code. + the function isblank doesn't seem to have propagated to any + header file near me, so it's there explicitly. not properly + tested on non-ascii character sets by me. + +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 + 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 + code and examples. + + fixed subtle bug in format that could get core dump. thanks to + Jaromir Dolecek <jdolecek@NetBSD.org> for finding and fixing. + minor cleanup in run.c / format() at the same time. + + added some tests for null pointers to debugging printf's, which + were never intended for external consumption. thanks to dave + kerns (dkerns@lucent.com) for pointing this out. + + GNU compatibility: an empty regexp matches anything (thanks to + dag-erling smorgrav, des@ofug.org). subject to reversion if + this does more harm than good. + + pervasive small changes to make things more const-correct, as + reported by gcc's -Wwrite-strings. as it says in the gcc manual, + this may be more nuisance than useful. provoked by a suggestion + and code from arnaud desitter, arnaud@nimbus.geog.ox.ac.uk + + minor documentation changes to note that this now compiles out + of the box on Mac OS X. + +Feb 10, 2002: + changed types in posix chars structure to quiet solaris cc. + +Jan 1, 2002: + fflush() or fflush("") flushes all files and pipes. + + length(arrayname) returns number of elements; thanks to + arnold robbins for suggestion. + + added a makefile.win to make it easier to build on windows. + based on dan allen's buildwin.bat. + +Nov 16, 2001: + added support for posix character class names like [:digit:], + which are not exactly shorter than [0-9] and perhaps no more + portable. thanks to dag-erling smorgrav for code. + +Feb 16, 2001: + removed -m option; no longer needed, and it was actually + broken (noted thanks to volker kiefel). + +Feb 10, 2001: + fixed an appalling bug in gettok: any sequence of digits, +,-, E, e, + and period was accepted as a valid number if it started with a period. + this would never have happened with the lex version. + + other 1-character botches, now fixed, include a bare $ and a + bare " at the end of the input. + +Feb 7, 2001: + more (const char *) casts in b.c and tran.c to silence warnings. + +Nov 15, 2000: + fixed a bug introduced in august 1997 that caused expressions + like $f[1] to be syntax errors. thanks to arnold robbins for + noticing this and providing a fix. + +Oct 30, 2000: + fixed some nextfile bugs: not handling all cases. thanks to + arnold robbins for pointing this out. new regressions added. + + close() is now a function. it returns whatever the library + fclose returns, and -1 for closing a file or pipe that wasn't + opened. + +Sep 24, 2000: + permit \n explicitly in character classes; won't work right + if comes in as "[\n]" but ok as /[\n]/, because of multiple + processing of \'s. thanks to arnold robbins. + +July 5, 2000: + minor fiddles in tran.c to keep compilers happy about uschar. + thanks to norman wilson. + +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 + off potential errors in subscripts (like isdigit). also + changed HAT to NCHARS-2. thanks again to santiago vila. + + changed maketab.c to ignore apparently out of range definitions + instead of halting; new freeBSD generates one. thanks to + jon snader <jsnader@ix.netcom.com> for pointing out the problem. + +May 2, 2000: + fixed an 8-bit problem in b.c by making several char*'s into + unsigned char*'s. not clear i have them all yet. thanks to + Santiago Vila <sanvila@unex.es> for the bug report. + +Apr 21, 2000: + finally found and fixed a memory leak in function call; it's + been there since functions were added ~1983. thanks to + jon bentley for the test case that found it. + + added test in envinit to catch environment "variables" with + names beginning with '='; thanks to Berend Hasselman. + +Jul 28, 1999: + added test in defn() to catch function foo(foo), which + otherwise recurses until core dump. thanks to arnold + robbins for noticing this. + +Jun 20, 1999: + added *bp in gettok in lex.c; appears possible to exit function + without terminating the string. thanks to russ cox. + +Jun 2, 1999: + added function stdinit() to run to initialize files[] array, + in case stdin, etc., are not constants; some compilers care. + +May 10, 1999: + replaced the ERROR ... FATAL, etc., macros with functions + based on vprintf, to avoid problems caused by overrunning + fixed-size errbuf array. thanks to ralph corderoy for the + impetus, and for pointing out a string termination bug in + qstring as well. + +Apr 21, 1999: + fixed bug that caused occasional core dumps with commandline + variable with value ending in \. (thanks to nelson beebe for + the test case.) + +Apr 16, 1999: + 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. + +Apr 5, 1999: + changed true/false to True/False in run.c to make it + easier to compile with C++. Added some casts on malloc + and realloc to be honest about casts; ditto. changed + ltype int to long in struct rrow to reduce some 64-bit + complaints; other changes scattered throughout for the + same purpose. thanks to Nelson Beebe for these portability + improvements. + + removed some horrible pointer-int casting in b.c and elsewhere + by adding ptoi and itonp to localize the casts, which are + all benign. fixed one incipient bug that showed up on sgi + in 64-bit mode. + + reset lineno for new source file; include filename in error + message. also fixed line number error in continuation lines. + (thanks to Nelson Beebe for both of these.) + +Mar 24, 1999: + Nelson Beebe notes that irix 5.3 yacc dies with a bogus + error; use a newer version or switch to bison, since sgi + is unlikely to fix it. + +Mar 5, 1999: + changed isnumber to is_number to avoid the problem caused by + versions of ctype.h that include the name isnumber. + + distribution now includes a script for building on a Mac, + thanks to Dan Allen. + +Feb 20, 1999: + fixed memory leaks in run.c (call) and tran.c (setfval). + thanks to Stephen Nutt for finding these and providing the fixes. + +Jan 13, 1999: + replaced srand argument by (unsigned int) in run.c; + avoids problem on Mac and potentially on Unix & Windows. + thanks to Dan Allen. + + added a few (int) casts to silence useless compiler warnings. + e.g., errorflag= in run.c jump(). + + added proctab.c to the bundle outout; one less thing + to have to compile out of the box. + + added calls to _popen and _pclose to the win95 stub for + pipes (thanks to Steve Adams for this helpful suggestion). + seems to work, though properties are not well understood + by me, and it appears that under some circumstances the + pipe output is truncated. Be careful. + +Oct 19, 1998: + fixed a couple of bugs in getrec: could fail to update $0 + 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 + least often used. + + thanks to ross ridge for finding these, and for providing + great bug reports. + +May 12, 1998: + fixed potential bug in readrec: might fail to update record + pointer after growing. thanks to dan levy for spotting this + and suggesting the fix. + +Mar 12, 1998: + added -V to print version number and die. + +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 + parsed again because token buffer wasn't terminated right. + example: awk 'length($0) > 10'. blush. at least i found it + myself. + +Aug 31, 1997: + s/adelete/awkdelete/: SGI uses this in malloc.h. + thanks to nelson beebe for pointing this one out. + +Aug 21, 1997: + fixed some bugs in sub and gsub when replacement includes \\. + this is a dark, horrible corner, but at least now i believe that + the behavior is the same as gawk and the intended posix standard. + thanks to arnold robbins for advice here. + +Aug 9, 1997: + somewhat regretfully, replaced the ancient lex-based lexical + analyzer with one written in C. it's longer, generates less code, + and more portable; the old one depended too much on mysterious + properties of lex that were not preserved in other environments. + in theory these recognize the same language. + + now using strtod to test whether a string is a number, instead of + the convoluted original function. should be more portable and + reliable if strtod is implemented right. + + removed now-pointless optimization in makefile that tries to avoid + recompilation when awkgram.y is changed but symbols are not. + + removed most fixed-size arrays, though a handful remain, some + of which are unchecked. you have been warned. + +Aug 4, 1997: + with some trepidation, replaced the ancient code that managed + fields and $0 in fixed-size arrays with arrays that grow on + demand. there is still some tension between trying to make this + run fast and making it clean; not sure it's right yet. + + the ill-conceived -mr and -mf arguments are now useful only + for debugging. previous dynamic string code removed. + + numerous other minor cleanups along the way. + +Jul 30, 1997: + using code provided by dan levy (to whom profuse thanks), replaced + fixed-size arrays and awkward kludges by a fairly uniform mechanism + to grow arrays as needed for printf, sub, gsub, etc. + +Jul 23, 1997: + falling off the end of a function returns "" and 0, not 0. + thanks to arnold robbins. + +Jun 17, 1997: + replaced several fixed-size arrays by dynamically-created ones + in run.c; added overflow tests to some previously unchecked cases. + getline, toupper, tolower. + + getline code is still broken in that recursive calls may wind + up using the same space. [fixed later] + + increased RECSIZE to 8192 to push problems further over the horizon. + + added \r to \n as input line separator for programs, not data. + damn CRLFs. + + modified format() to permit explicit printf("%c", 0) to include + a null byte in output. thanks to ken stailey for the fix. + + added a "-safe" argument that disables file output (print >, + print >>), process creation (cmd|getline, print |, system), and + access to the environment (ENVIRON). this is a first approximation + to a "safe" version of awk, but don't rely on it too much. thanks + to joan feigenbaum and matt blaze for the inspiration long ago. + +Jul 8, 1996: + fixed long-standing bug in sub, gsub(/a/, "\\\\&"); thanks to + ralph corderoy. + +Jun 29, 1996: + fixed awful bug in new field splitting; didn't get all the places + where input was done. + +Jun 28, 1996: + changed field-splitting to conform to posix definition: fields are + split using the value of FS at the time of input; it used to be + the value when the field or NF was first referred to, a much less + predictable definition. thanks to arnold robbins for encouragement + to do the right thing. + +May 28, 1996: + fixed appalling but apparently unimportant bug in parsing octal + numbers in reg exprs. + + explicit hex in reg exprs now limited to 2 chars: \xa, \xaa. + +May 27, 1996: + cleaned up some declarations so gcc -Wall is now almost silent. + + makefile now includes backup copies of ytab.c and lexyy.c in case + one makes before looking; it also avoids recreating lexyy.c unless + really needed. + + s/aprintf/awkprint, s/asprintf/awksprintf/ to avoid some name clashes + with unwisely-written header files. + + thanks to jeffrey friedl for several of these. + +May 26, 1996: + an attempt to rationalize the (unsigned) char issue. almost all + instances of unsigned char have been removed; the handful of places + in b.c where chars are used as table indices have been hand-crafted. + added some latin-1 tests to the regression, but i'm not confident; + none of my compilers seem to care much. thanks to nelson beebe for + pointing out some others that do care. + +May 2, 1996: + removed all register declarations. + + enhanced split(), as in gawk, etc: split(s, a, "") splits s into + a[1]...a[length(s)] with each character a single element. + + made the same changes for field-splitting if FS is "". + + added nextfile, as in gawk: causes immediate advance to next + 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 + everything; [z-a] is now empty, not z. far from complete + or correct, however. (thanks to jeffrey friedl for pointing out + some awful behaviors.) + +Apr 29, 1996: + replaced uchar by uschar everywhere; apparently some compilers + usurp this name and this causes conflicts. + + fixed call to time in run.c (bltin); arg is time_t *. + + replaced horrible pointer/long punning in b.c by a legitimate + union. should be safer on 64-bit machines and cleaner everywhere. + (thanks to nelson beebe for pointing out some of these problems.) + + replaced nested comments by #if 0...#endif in run.c, lib.c. + + removed getsval, setsval, execute macros from run.c and lib.c. + machines are 100x faster than they were when these macros were + first used. + + revised filenames: awk.g.y => awkgram.y, awk.lx.l => awklex.l, + y.tab.[ch] => ytab.[ch], lex.yy.c => lexyy.c, all in the aid of + portability to nameless systems. + + "make bundle" now includes yacc and lex output files for recipients + who don't have yacc or lex. + +Aug 15, 1995: + initialized Cells in setsymtab more carefully; some fields + were not set. (thanks to purify, all of whose complaints i + think i now understand.) + + fixed at least one error in gsub that looked at -1-th element + of an array when substituting for a null match (e.g., $). + + delete arrayname is now legal; it clears the elements but leaves + the array, which may not be the right behavior. + + modified makefile: my current make can't cope with the test used + to avoid unnecessary yacc invocations. + +Jul 17, 1995: + added dynamically growing strings to awk.lx.l and b.c + to permit regular expressions to be much bigger. + the state arrays can still overflow. + +Aug 24, 1994: + detect duplicate arguments in function definitions (mdm). + +May 11, 1994: + trivial fix to printf to limit string size in sub(). + +Apr 22, 1994: + fixed yet another subtle self-assignment problem: + $1 = $2; $1 = $1 clobbered $1. + + Regression tests now use private echo, to avoid quoting problems. + +Feb 2, 1994: + changed error() to print line number as %d, not %g. + +Jul 23, 1993: + cosmetic changes: increased sizes of some arrays, + reworded some error messages. + + added CONVFMT as in posix (just replaced OFMT in getsval) + + FILENAME is now "" until the first thing that causes a file + to be opened. + +Nov 28, 1992: + deleted yyunput and yyoutput from proto.h; + different versions of lex give these different declarations. + +May 31, 1992: + added -mr N and -mf N options: more record and fields. + these really ought to adjust automatically. + + cleaned up some error messages; "out of space" now means + malloc returned NULL in all cases. + + changed rehash so that if it runs out, it just returns; + things will continue to run slow, but maybe a bit longer. + +Apr 24, 1992: + remove redundant close of stdin when using -f -. + + got rid of core dump with -d; awk -d just prints date. + +Apr 12, 1992: + added explicit check for /dev/std(in,out,err) in redirection. + unlike gawk, no /dev/fd/n yet. + + added (file/pipe) builtin. hard to test satisfactorily. + not posix. + +Feb 20, 1992: + recompile after abortive changes; should be unchanged. + +Dec 2, 1991: + die-casting time: converted to ansi C, installed that. + +Nov 30, 1991: + fixed storage leak in freefa, failing to recover [N]CCL. + thanks to Bill Jones (jones@cs.usask.ca) + +Nov 19, 1991: + use RAND_MAX instead of literal in builtin(). + +Nov 12, 1991: + cranked up some fixed-size arrays in b.c, and added a test for + overflow in penter. thanks to mark larsen. + +Sep 24, 1991: + increased buffer in gsub. a very crude fix to a general problem. + and again on Sep 26. + +Aug 18, 1991: + enforce variable name syntax for commandline variables: has to + start with letter or _. + +Jul 27, 1991: + allow newline after ; in for statements. + +Jul 21, 1991: + fixed so that in self-assignment like $1=$1, side effects + like recomputing $0 take place. (this is getting subtle.) + +Jun 30, 1991: + better test for detecting too-long output record. + +Jun 2, 1991: + better defense against very long printf strings. + made break and continue illegal outside of loops. + +May 13, 1991: + removed extra arg on gettemp, tempfree. minor error message rewording. + +May 6, 1991: + fixed silly bug in hex parsing in hexstr(). + removed an apparently unnecessary test in isnumber(). + warn about weird printf conversions. + fixed unchecked array overwrite in relex(). + + changed for (i in array) to access elements in sorted order. + then unchanged it -- it really does run slower in too many cases. + left the code in place, commented out. + +Feb 10, 1991: + check error status on all writes, to avoid banging on full disks. + +Jan 28, 1991: + awk -f - reads the program from stdin. + +Jan 11, 1991: + failed to set numeric state on $0 in cmd|getline context in run.c. + +Nov 2, 1990: + fixed sleazy test for integrality in getsval; use modf. + +Oct 29, 1990: + fixed sleazy buggy code in lib.c that looked (incorrectly) for + too long input lines. + +Oct 14, 1990: + fixed the bug on p. 198 in which it couldn't deduce that an + argument was an array in some contexts. replaced the error + message in intest() by code that damn well makes it an array. + +Oct 8, 1990: + fixed horrible bug: types and values were not preserved in + some kinds of self-assignment. (in assign().) + +Aug 24, 1990: + changed NCHARS to 256 to handle 8-bit characters in strings + presented to match(), etc. + +Jun 26, 1990: + changed struct rrow (awk.h) to use long instead of int for lval, + since cfoll() stores a pointer in it. now works better when int's + are smaller than pointers! + +May 6, 1990: + AVA fixed the grammar so that ! is uniformly of the same precedence as + unary + and -. This renders illegal some constructs like !x=y, which + now has to be parenthesized as !(x=y), and makes others work properly: + !x+y is (!x)+y, and x!y is x !y, not two pattern-action statements. + (These problems were pointed out by Bob Lenk of Posix.) + + Added \x to regular expressions (already in strings). + Limited octal to octal digits; \8 and \9 are not octal. + Centralized the code for parsing escapes in regular expressions. + Added a bunch of tests to T.re and T.sub to verify some of this. + +Feb 9, 1990: + fixed null pointer dereference bug in main.c: -F[nothing]. sigh. + + restored srand behavior: it returns the current seed. + +Jan 18, 1990: + srand now returns previous seed value (0 to start). + +Jan 5, 1990: + fix potential problem in tran.c -- something was freed, + then used in freesymtab. + +Oct 18, 1989: + another try to get the max number of open files set with + relatively machine-independent code. + + small fix to input() in case of multiple reads after EOF. + +Oct 11, 1989: + FILENAME is now defined in the BEGIN block -- too many old + programs broke. + + "-" means stdin in getline as well as on the commandline. + + added a bunch of casts to the code to tell the truth about + char * vs. unsigned char *, a right royal pain. added a + setlocale call to the front of main, though probably no one + has it usefully implemented yet. + +Aug 24, 1989: + removed redundant relational tests against nullnode if parse + tree already had a relational at that point. + +Aug 11, 1989: + fixed bug: commandline variable assignment has to look like + var=something. (consider the man page for =, in file =.1) + + changed number of arguments to functions to static arrays + to avoid repeated malloc calls. + +Aug 2, 1989: + restored -F (space) separator + +Jul 30, 1989: + added -v x=1 y=2 ... for immediate commandline variable assignment; + done before the BEGIN block for sure. they have to precede the + program if the program is on the commandline. + Modified Aug 2 to require a separate -v for each assignment. + +Jul 10, 1989: + fixed ref-thru-zero bug in environment code in tran.c + +Jun 23, 1989: + add newline to usage message. + +Jun 14, 1989: + added some missing ansi printf conversion letters: %i %X %E %G. + no sensible meaning for h or L, so they may not do what one expects. + + made %* conversions work. + + changed x^y so that if n is a positive integer, it's done + by explicit multiplication, thus achieving maximum accuracy. + (this should be done by pow() but it seems not to be locally.) + done to x ^= y as well. + +Jun 4, 1989: + ENVIRON array contains environment: if shell variable V=thing, + ENVIRON["V"] is "thing" + + multiple -f arguments permitted. error reporting is naive. + (they were permitted before, but only the last was used.) + + fixed a really stupid botch in the debugging macro dprintf + + fixed order of evaluation of commandline assignments to match + what the book claims: an argument of the form x=e is evaluated + at the time it would have been opened if it were a filename (p 63). + this invalidates the suggested answer to ex 4-1 (p 195). + + removed some code that permitted -F (space) fieldseparator, + since it didn't quite work right anyway. (restored aug 2) + +Apr 27, 1989: + Line number now accumulated correctly for comment lines. + +Apr 26, 1989: + Debugging output now includes a version date, + if one compiles it into the source each time. + +Apr 9, 1989: + Changed grammar to prohibit constants as 3rd arg of sub and gsub; + prevents class of overwriting-a-constant errors. (Last one?) + This invalidates the "banana" example on page 43 of the book. + + Added \a ("alert"), \v (vertical tab), \xhhh (hexadecimal), + as in ANSI, for strings. Rescinded the sloppiness that permitted + non-octal digits in \ooo. Warning: not all compilers and libraries + will be able to deal with \x correctly. + +Jan 9, 1989: + Fixed bug that caused tempcell list to contain a duplicate. + The fix is kludgy. + +Dec 17, 1988: + Catches some more commandline errors in main. + Removed redundant decl of modf in run.c (confuses some compilers). + Warning: there's no single declaration of malloc, etc., in awk.h + that seems to satisfy all compilers. + +Dec 7, 1988: + Added a bit of code to error printing to avoid printing nulls. + (Not clear that it actually would.) + +Nov 27, 1988: + With fear and trembling, modified the grammar to permit + multiple pattern-action statements on one line without + an explicit separator. By definition, this capitulation + to the ghost of ancient implementations remains undefined + and thus subject to change without notice or apology. + DO NOT COUNT ON IT. + +Oct 30, 1988: + Fixed bug in call() that failed to recover storage. + + A warning is now generated if there are more arguments + in the call than in the definition (in lieu of fixing + another storage leak). + +Oct 20, 1988: + Fixed %c: if expr is numeric, use numeric value; + otherwise print 1st char of string value. still + doesn't work if the value is 0 -- won't print \0. + + Added a few more checks for running out of malloc. + +Oct 12, 1988: + Fixed bug in call() that freed local arrays twice. + + Fixed to handle deletion of non-existent array right; + complains about attempt to delete non-array element. + +Sep 30, 1988: + Now guarantees to evaluate all arguments of built-in + functions, as in C; the appearance is that arguments + are evaluated before the function is called. Places + affected are sub (gsub was ok), substr, printf, and + all the built-in arithmetic functions in bltin(). + A warning is generated if a bltin() is called with + the wrong number of arguments. + + This requires changing makeprof on p167 of the book. + +Aug 23, 1988: + setting FILENAME in BEGIN caused core dump, apparently + because it was freeing space not allocated by malloc. + +July 24, 1988: + fixed egregious error in toupper/tolower functions. + still subject to rescinding, however. + +July 2, 1988: + flush stdout before opening file or pipe + +July 2, 1988: + performance bug in b.c/cgoto(): not freeing some sets of states. + partial fix only right now, and the number of states increased + to make it less obvious. + +June 1, 1988: + check error status on close + +May 28, 1988: + srand returns seed value it's using. + see 1/18/90 + +May 22, 1988: + Removed limit on depth of function calls. + +May 10, 1988: + Fixed lib.c to permit _ in commandline variable names. + +Mar 25, 1988: + main.c fixed to recognize -- as terminator of command- + line options. Illegal options flagged. + Error reporting slightly cleaned up. + +Dec 2, 1987: + Newer C compilers apply a strict scope rule to extern + declarations within functions. Two extern declarations in + lib.c and tran.c have been moved to obviate this problem. + +Oct xx, 1987: + Reluctantly added toupper and tolower functions. + Subject to rescinding without notice. + +Sep 17, 1987: + Error-message printer had printf(s) instead of + printf("%s",s); got core dumps when the message + included a %. + +Sep 12, 1987: + Very long printf strings caused core dump; + fixed aprintf, asprintf, format to catch them. + Can still get a core dump in printf itself. + + diff --git a/awk/README b/awk/README @@ -0,0 +1,95 @@ +/* $OpenBSD: README,v 1.7 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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/awk/awk.1 b/awk/awk.1 @@ -0,0 +1,800 @@ +.\" $OpenBSD: awk.1,v 1.40 2011/05/02 11:14:11 jmc Exp $ +.\" +.\" 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. +.\" +.Dd $Mdocdate: May 2 2011 $ +.Dt AWK 1 +.Os +.Sh NAME +.Nm awk +.Nd pattern-directed scanning and processing language +.Sh SYNOPSIS +.Nm awk +.Op Fl safe +.Op Fl V +.Op Fl d Ns Op Ar n +.Op Fl F Ar fs +.Op Fl v Ar var Ns = Ns Ar value +.Op Ar prog | Fl f Ar progfile +.Ar +.Sh DESCRIPTION +.Nm +scans each input +.Ar file +for lines that match any of a set of patterns specified literally in +.Ar prog +or in one or more files specified as +.Fl f Ar progfile . +With each pattern there can be an associated action that will be performed +when a line of a +.Ar file +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 +.Sq - +means the standard input. +Any +.Ar file +of the form +.Ar var Ns = Ns Ar value +is treated as an assignment, not a filename, +and is executed at the time it would have been opened if it were a filename. +.Pp +The options are as follows: +.Bl -tag -width "-safe " +.It Fl d Ns Op Ar n +Debug mode. +Set debug level to +.Ar n , +or 1 if +.Ar n +is not specified. +A value greater than 1 causes +.Nm +to dump core on fatal errors. +.It Fl F Ar fs +Define the input field separator to be the regular expression +.Ar fs . +.It Fl f Ar progfile +Read program code from the specified file +.Ar progfile +instead of from the command line. +.It Fl safe +Disable file output +.Pf ( Ic print No > , +.Ic print No >> ) , +process creation +.Po +.Ar cmd | Ic getline , +.Ic print | , +.Ic system +.Pc +and access to the environment +.Pf ( Va ENVIRON ; +see the section on variables below). +This is a first +.Pq and not very reliable +approximation to a +.Dq safe +version of +.Nm . +.It Fl V +Print the version number of +.Nm +to standard output and exit. +.It Fl v Ar var Ns = Ns Ar value +Assign +.Ar value +to variable +.Ar var +before +.Ar prog +is executed; +any number of +.Fl v +options may be present. +.El +.Pp +The input is normally made up of input lines +.Pq records +separated by newlines, or by the value of +.Va RS . +If +.Va RS +is null, then any number of blank lines are used as the record separator, +and newlines are used as field separators +(in addition to the value of +.Va FS ) . +This is convenient when working with multi-line records. +.Pp +An input line is normally made up of fields separated by whitespace, +or by the regular expression +.Va FS . +The fields are denoted +.Va $1 , $2 , ... , +while +.Va $0 +refers to the entire line. +If +.Va FS +is null, the input line is split into one field per character. +.Pp +Normally, any number of blanks separate fields. +In order to set the field separator to a single blank, use the +.Fl F +option with a value of +.Sq [\ \&] . +If a field separator of +.Sq t +is specified, +.Nm +treats it as if +.Sq \et +had been specified and uses +.Aq TAB +as the field separator. +In order to use a literal +.Sq t +as the field separator, use the +.Fl F +option with a value of +.Sq [t] . +.Pp +A pattern-action statement has the form +.Pp +.D1 Ar pattern Ic \&{ Ar action Ic \&} +.Pp +A missing +.Ic \&{ Ar action Ic \&} +means print the line; +a missing pattern always matches. +Pattern-action statements are separated by newlines or semicolons. +.Pp +Newlines are permitted after a terminating statement or following a comma +.Pq Sq ,\& , +an open brace +.Pq Sq { , +a logical AND +.Pq Sq && , +a logical OR +.Pq Sq || , +after the +.Sq do +or +.Sq else +keywords, +or after the closing parenthesis of an +.Sq if , +.Sq for , +or +.Sq while +statement. +Additionally, a backslash +.Pq Sq \e +can be used to escape a newline between tokens. +.Pp +An action is a sequence of statements. +A statement can be one of the following: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Xo Ic if ( Ar expression ) Ar statement +.Op Ic else Ar statement +.Xc +.It Ic while ( Ar expression ) Ar statement +.It Xo Ic for +.No ( Ar expression ; expression ; expression ) statement +.Xc +.It Xo Ic for +.No ( Ar var Ic in Ar array ) statement +.Xc +.It Xo Ic do +.Ar statement Ic while ( Ar expression ) +.Xc +.It Ic break +.It Ic continue +.It Xo Ic { +.Op Ar statement ... +.Ic } +.Xc +.It Xo Ar expression +.No # commonly +.Ar var No = Ar expression +.Xc +.It Xo Ic print +.Op Ar expression-list +.Op > Ns Ar expression +.Xc +.It Xo Ic printf Ar format +.Op Ar ... , expression-list +.Op > Ns Ar expression +.Xc +.It Ic return Op Ar expression +.It Xo Ic next +.No # skip remaining patterns on this input line +.Xc +.It Xo Ic nextfile +.No # skip rest of this file, open next, start at top +.Xc +.It Xo Ic delete +.Sm off +.Ar array Ic \&[ Ar expression Ic \&] +.Sm on +.No # delete an array element +.Xc +.It Xo Ic delete Ar array +.No # delete all elements of array +.Xc +.It Xo Ic exit +.Op Ar expression +.No # exit immediately; status is Ar expression +.Xc +.El +.Pp +Statements are terminated by +semicolons, newlines or right braces. +An empty +.Ar expression-list +stands for +.Ar $0 . +String constants are quoted +.Li \&"" , +with the usual C escapes recognized within +(see +.Xr printf 1 +for a complete list of these). +Expressions take on string or numeric values as appropriate, +and are built using the operators +.Ic + \- * / % ^ +.Pq exponentiation , +and concatenation +.Pq indicated by whitespace . +The operators +.Ic \&! ++ \-\- += \-= *= /= %= ^= +.Ic > >= < <= == != ?: +are also available in expressions. +Variables may be scalars, array elements +(denoted +.Li x[i] ) +or fields. +Variables are initialized to the null string. +Array subscripts may be any string, +not necessarily numeric; +this allows for a form of associative memory. +Multiple subscripts such as +.Li [i,j,k] +are permitted; the constituents are concatenated, +separated by the value of +.Va SUBSEP +.Pq see the section on variables below . +.Pp +The +.Ic print +statement prints its arguments on the standard output +(or on a file if +.Pf > Ns Ar file +or +.Pf >> Ns Ar file +is present or on a pipe if +.Pf |\ \& Ar cmd +is present), separated by the current output field separator, +and terminated by the output record separator. +.Ar file +and +.Ar cmd +may be literal names or parenthesized expressions; +identical string values in different statements denote +the same open file. +The +.Ic printf +statement formats its expression list according to the format +(see +.Xr printf 1 ) . +.Pp +Patterns are arbitrary Boolean combinations +(with +.Ic "\&! || &&" ) +of regular expressions and +relational expressions. +.Nm +supports extended regular expressions +.Pq EREs . +See +.Xr re_format 7 +for more information on regular expressions. +Isolated regular expressions +in a pattern apply to the entire line. +Regular expressions may also occur in +relational expressions, using the operators +.Ic ~ +and +.Ic !~ . +.Pf / Ns Ar re Ns / +is a constant regular expression; +any string (constant or variable) may be used +as a regular expression, except in the position of an isolated regular expression +in a pattern. +.Pp +A pattern may consist of two patterns separated by a comma; +in this case, the action is performed for all lines +from an occurrence of the first pattern +through an occurrence of the second. +.Pp +A relational expression is one of the following: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Ar expression matchop regular-expression +.It Ar expression relop expression +.It Ar expression Ic in Ar array-name +.It Xo Ic \&( Ns +.Ar expr , expr , \&... Ns Ic \&) in +.Ar array-name +.Xc +.El +.Pp +where a +.Ar relop +is any of the six relational operators in C, and a +.Ar matchop +is either +.Ic ~ +(matches) +or +.Ic !~ +(does not match). +A conditional is an arithmetic expression, +a relational expression, +or a Boolean combination +of these. +.Pp +The special patterns +.Ic BEGIN +and +.Ic END +may be used to capture control before the first input line is read +and after the last. +.Ic BEGIN +and +.Ic END +do not combine with other patterns. +.Pp +Variable names with special meanings: +.Pp +.Bl -tag -width "FILENAME " -compact +.It Va ARGC +Argument count, assignable. +.It Va ARGV +Argument array, assignable; +non-null members are taken as filenames. +.It Va CONVFMT +Conversion format when converting numbers +(default +.Qq Li %.6g ) . +.It Va ENVIRON +Array of environment variables; subscripts are names. +.It Va FILENAME +The name of the current input file. +.It Va FNR +Ordinal number of the current record in the current file. +.It Va FS +Regular expression used to separate fields; also settable +by option +.Fl F Ar fs . +.It Va NF +Number of fields in the current record. +.Va $NF +can be used to obtain the value of the last field in the current record. +.It Va NR +Ordinal number of the current record. +.It Va OFMT +Output format for numbers (default +.Qq Li %.6g ) . +.It Va OFS +Output field separator (default blank). +.It Va ORS +Output record separator (default newline). +.It Va RLENGTH +The length of the string matched by the +.Fn match +function. +.It Va RS +Input record separator (default newline). +.It Va RSTART +The starting position of the string matched by the +.Fn match +function. +.It Va SUBSEP +Separates multiple subscripts (default 034). +.El +.Sh FUNCTIONS +The awk language has a variety of built-in functions: +arithmetic, string, input/output, general, and bit-operation. +.Pp +Functions may be defined (at the position of a pattern-action statement) +thusly: +.Pp +.Dl function foo(a, b, c) { ...; return x } +.Pp +Parameters are passed by value if scalar, and by reference if array name; +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. +.Ss Arithmetic Functions +.Bl -tag -width "atan2(y, x)" +.It Fn atan2 y x +Return the arctangent of +.Fa y Ns / Ns Fa x +in radians. +.It Fn cos x +Return the cosine of +.Fa x , +where +.Fa x +is in radians. +.It Fn exp x +Return the exponential of +.Fa x . +.It Fn int x +Return +.Fa x +truncated to an integer value. +.It Fn log x +Return the natural logarithm of +.Fa x . +.It Fn rand +Return a random number, +.Fa n , +such that +.Sm off +.Pf 0 \*(Le Fa n No \*(Lt 1 . +.Sm on +.It Fn sin x +Return the sine of +.Fa x , +where +.Fa x +is in radians. +.It Fn sqrt x +Return the square root of +.Fa x . +.It Fn srand expr +Sets seed for +.Fn rand +to +.Fa expr +and returns the previous seed. +If +.Fa expr +is omitted, the time of day is used instead. +.El +.Ss String Functions +.Bl -tag -width "split(s, a, fs)" +.It Fn gsub r t s +The same as +.Fn sub +except that all occurrences of the regular expression are replaced. +.Fn gsub +returns the number of replacements. +.It Fn index s t +The position in +.Fa s +where the string +.Fa t +occurs, or 0 if it does not. +.It Fn length s +The length of +.Fa s +taken as a string, +or of +.Va $0 +if no argument is given. +.It Fn match s r +The position in +.Fa s +where the regular expression +.Fa r +occurs, or 0 if it does not. +The variable +.Va RSTART +is set to the starting position of the matched string +.Pq which is the same as the returned value +or zero if no match is found. +The variable +.Va RLENGTH +is set to the length of the matched string, +or \-1 if no match is found. +.It Fn split s a fs +Splits the string +.Fa s +into array elements +.Va a[1] , a[2] , ... , a[n] +and returns +.Va n . +The separation is done with the regular expression +.Ar fs +or with the field separator +.Va FS +if +.Ar fs +is not given. +An empty string as field separator splits the string +into one array element per character. +.It Fn sprintf fmt expr ... +The string resulting from formatting +.Fa expr , ... +according to the +.Xr printf 1 +format +.Fa fmt . +.It Fn sub r t s +Substitutes +.Fa t +for the first occurrence of the regular expression +.Fa r +in the string +.Fa s . +If +.Fa s +is not given, +.Va $0 +is used. +An ampersand +.Pq Sq & +in +.Fa t +is replaced in string +.Fa s +with regular expression +.Fa r . +A literal ampersand can be specified by preceding it with two backslashes +.Pq Sq \e\e . +A literal backslash can be specified by preceding it with another backslash +.Pq Sq \e\e . +.Fn sub +returns the number of replacements. +.It Fn substr s m n +Return at most the +.Fa n Ns -character +substring of +.Fa s +that begins at position +.Fa m +counted from 1. +If +.Fa n +is omitted, or if +.Fa n +specifies more characters than are left in the string, +the length of the substring is limited by the length of +.Fa s . +.It Fn tolower str +Returns a copy of +.Fa str +with all upper-case characters translated to their +corresponding lower-case equivalents. +.It Fn toupper str +Returns a copy of +.Fa str +with all lower-case characters translated to their +corresponding upper-case equivalents. +.El +.Ss Input/Output and General Functions +.Bl -tag -width "getline [var] < file" +.It Fn close expr +Closes the file or pipe +.Fa expr . +.Fa expr +should match the string that was used to open the file or pipe. +.It Ar cmd | Ic getline Op Va var +Read a record of input from a stream piped from the output of +.Ar cmd . +If +.Va var +is omitted, the variables +.Va $0 +and +.Va NF +are set. +Otherwise +.Va var +is set. +If the stream is not open, it is opened. +As long as the stream remains open, subsequent calls +will read subsequent records from the stream. +The stream remains open until explicitly closed with a call to +.Fn close . +.Ic getline +returns 1 for a successful input, 0 for end of file, and \-1 for an error. +.It Fn fflush [expr] +Flushes any buffered output for the file or pipe +.Fa expr , +or all open files or pipes if +.Fa expr +is omitted. +.Fa expr +should match the string that was used to open the file or pipe. +.It Ic getline +Sets +.Va $0 +to the next input record from the current input file. +This form of +.Ic getline +sets the variables +.Va NF , +.Va NR , +and +.Va FNR . +.Ic getline +returns 1 for a successful input, 0 for end of file, and \-1 for an error. +.It Ic getline Va var +Sets +.Va $0 +to variable +.Va var . +This form of +.Ic getline +sets the variables +.Va NR +and +.Va FNR . +.Ic getline +returns 1 for a successful input, 0 for end of file, and \-1 for an error. +.It Xo +.Ic getline Op Va var +.Pf \ \&< Ar file +.Xc +Sets +.Va $0 +to the next record from +.Ar file . +If +.Va var +is omitted, the variables +.Va $0 +and +.Va NF +are set. +Otherwise +.Va var +is set. +If +.Ar file +is not open, it is opened. +As long as the stream remains open, subsequent calls will read subsequent +records from +.Ar file . +.Ar file +remains open until explicitly closed with a call to +.Fn close . +.It Fn system cmd +Executes +.Fa cmd +and returns its exit status. +.El +.Ss Bit-Operation Functions +.Bl -tag -width "lshift(a, b)" +.It Fn compl x +Returns the bitwise complement of integer argument x. +.It Fn and x y +Performs a bitwise AND on integer arguments x and y. +.It Fn or x y +Performs a bitwise OR on integer arguments x and y. +.It Fn xor x y +Performs a bitwise Exclusive-OR on integer arguments x and y. +.It Fn lshift x n +Returns integer argument x shifted by n bits to the left. +.It Fn rshift x n +Returns integer argument x shifted by n bits to the right. +.El +.Sh EXIT STATUS +.Ex -std awk +.Pp +But note that the +.Ic exit +expression can modify the exit status. +.Sh EXAMPLES +Print lines longer than 72 characters: +.Pp +.Dl length($0) > 72 +.Pp +Print first two fields in opposite order: +.Pp +.Dl { print $2, $1 } +.Pp +Same, with input fields separated by comma and/or blanks and tabs: +.Bd -literal -offset indent +BEGIN { FS = ",[ \et]*|[ \et]+" } + { print $2, $1 } +.Ed +.Pp +Add up first column, print sum and average: +.Bd -literal -offset indent +{ s += $1 } +END { print "sum is", s, " average is", s/NR } +.Ed +.Pp +Print all lines between start/stop pairs: +.Pp +.Dl /start/, /stop/ +.Pp +Simulate echo(1): +.Bd -literal -offset indent +BEGIN { # Simulate echo(1) + for (i = 1; i < ARGC; i++) printf "%s ", ARGV[i] + printf "\en" + exit } +.Ed +.Pp +Print an error message to standard error: +.Bd -literal -offset indent +{ print "error!" > "/dev/stderr" } +.Ed +.Sh SEE ALSO +.Xr lex 1 , +.Xr printf 1 , +.Xr sed 1 , +.Xr re_format 7 , +.Xr script 7 +.Rs +.%A A. V. Aho +.%A B. W. Kernighan +.%A P. J. Weinberger +.%T The AWK Programming Language +.%I Addison-Wesley +.%D 1988 +.%O ISBN 0-201-07981-X +.Re +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl \&dV +and +.Op Fl safe , +as well as the commands +.Cm fflush , compl , and , or , +.Cm xor , lshift , rshift , +are extensions to that specification. +.Pp +.Nm +does not support {n,m} pattern matching. +.Sh HISTORY +An +.Nm +utility appeared in +.At v7 . +.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 +.Li \&"" +to it. +.Pp +The scope rules for variables in functions are a botch; +the syntax is worse. diff --git a/awk/awk.h b/awk/awk.h @@ -0,0 +1,240 @@ +/* $OpenBSD: awk.h,v 1.13 2008/10/06 20:38:33 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +#include <assert.h> + +typedef double Awkfloat; + +/* unsigned char is more trouble than it's worth */ + +typedef unsigned char uschar; + +#define xfree(a) { if ((a) != NULL) { free((void *) (a)); (a) = NULL; } } + +#define NN(p) ((p) ? (p) : "(null)") /* guaranteed non-null for dprintf +*/ +#define DEBUG +#ifdef DEBUG + /* uses have to be doubly parenthesized */ +# define dprintf(x) if (dbg) printf x +#else +# define dprintf(x) +#endif + +extern int compile_time; /* 1 if compiling, 0 if running */ +extern int safe; /* 0 => unsafe, 1 => safe */ + +#define RECSIZE (8 * 1024) /* sets limit on records, fields, etc., etc. */ +extern int recsize; /* size of current record, orig RECSIZE */ + +extern char **FS; +extern char **RS; +extern char **ORS; +extern char **OFS; +extern char **OFMT; +extern Awkfloat *NR; +extern Awkfloat *FNR; +extern Awkfloat *NF; +extern char **FILENAME; +extern char **SUBSEP; +extern Awkfloat *RSTART; +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 int dbg; + +extern 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 */ + +typedef struct Cell { + uschar ctype; /* OCELL, OBOOL, OJUMP, etc. */ + uschar csub; /* CCON, CTEMP, CFLD, etc. */ + 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 */ + struct Cell *cnext; /* ptr to next if chained */ +} Cell; + +typedef struct Array { /* symbol table array */ + int nelem; /* elements in table right now */ + int size; /* size of tab */ + Cell **tab; /* hash table pointers */ +} Array; + +#define NSYMTAB 50 /* initial size of a symbol table */ +extern Array *symtab; + +extern Cell *nrloc; /* NR */ +extern Cell *fnrloc; /* FNR */ +extern Cell *nfloc; /* NF */ +extern Cell *rstartloc; /* RSTART */ +extern Cell *rlengthloc; /* RLENGTH */ + +/* Cell.tval values: */ +#define NUM 01 /* number value is valid */ +#define STR 02 /* string value is valid */ +#define DONTFREE 04 /* string space is not freeable */ +#define CON 010 /* this is a constant */ +#define ARR 020 /* this is an array */ +#define FCN 040 /* this is a function name */ +#define FLD 0100 /* this is a field $1, $2, ... */ +#define REC 0200 /* this is $0 */ + + +/* function types */ +#define FLENGTH 1 +#define FSQRT 2 +#define FEXP 3 +#define FLOG 4 +#define FINT 5 +#define FSYSTEM 6 +#define FRAND 7 +#define FSRAND 8 +#define FSIN 9 +#define FCOS 10 +#define FATAN 11 +#define FTOUPPER 12 +#define FTOLOWER 13 +#define FFLUSH 14 +#define FAND 15 +#define FFOR 16 +#define FXOR 17 +#define FCOMPL 18 +#define FLSHIFT 19 +#define FRSHIFT 20 + +/* Node: parse tree is made of nodes, with Cell's at bottom */ + +typedef struct Node { + int ntype; + struct Node *nnext; + int lineno; + int nobj; + struct Node *narg[1]; /* variable: actual size set by calling malloc */ +} Node; + +#define NIL ((Node *) 0) + +extern Node *winner; +extern Node *nullstat; +extern Node *nullnode; + +/* ctypes */ +#define OCELL 1 +#define OBOOL 2 +#define OJUMP 3 + +/* Cell subtypes: csub */ +#define CFREE 7 +#define CCOPY 6 +#define CCON 5 +#define CTEMP 4 +#define CNAME 3 +#define CVAR 2 +#define CFLD 1 +#define CUNK 0 + +/* bool subtypes */ +#define BTRUE 11 +#define BFALSE 12 + +/* jump subtypes */ +#define JEXIT 21 +#define JNEXT 22 +#define JBREAK 23 +#define JCONT 24 +#define JRET 25 +#define JNEXTFILE 26 + +/* node types */ +#define NVALUE 1 +#define NSTAT 2 +#define NEXPR 3 + + +extern int pairstack[], paircnt; + +#define notlegal(n) (n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc) +#define isvalue(n) ((n)->ntype == NVALUE) +#define isexpr(n) ((n)->ntype == NEXPR) +#define isjump(n) ((n)->ctype == OJUMP) +#define isexit(n) ((n)->csub == JEXIT) +#define isbreak(n) ((n)->csub == JBREAK) +#define iscont(n) ((n)->csub == JCONT) +#define isnext(n) ((n)->csub == JNEXT || (n)->csub == JNEXTFILE) +#define isret(n) ((n)->csub == JRET) +#define isrec(n) ((n)->tval & REC) +#define isfld(n) ((n)->tval & FLD) +#define isstr(n) ((n)->tval & STR) +#define isnum(n) ((n)->tval & NUM) +#define isarr(n) ((n)->tval & ARR) +#define isfcn(n) ((n)->tval & FCN) +#define istrue(n) ((n)->csub == BTRUE) +#define istemp(n) ((n)->csub == CTEMP) +#define isargument(n) ((n)->nobj == ARG) +/* #define freeable(p) (!((p)->tval & DONTFREE)) */ +#define freeable(p) ( ((p)->tval & (STR|DONTFREE)) == STR ) + +/* structures used by regular expression matching machinery, mostly b.c: */ + +#define NCHARS (256+3) /* 256 handles 8-bit chars; 128 does 7-bit */ + /* watch out in match(), etc. */ +#define NSTATES 32 + +typedef struct rrow { + long ltype; /* long avoids pointer warnings on 64-bit */ + union { + int i; + Node *np; + uschar *up; + } lval; /* because Al stores a pointer in it! */ + int *lfollow; +} rrow; + +typedef struct fa { + uschar gototab[NSTATES][NCHARS]; + uschar out[NSTATES]; + uschar *restr; + int *posns[NSTATES]; + int anchor; + int use; + int initstat; + int curstat; + int accept; + int reset; + struct rrow re[1]; /* variable: actual size set by calling malloc */ +} fa; + + +#include "proto.h" diff --git a/awk/awkgram.y b/awk/awkgram.y @@ -0,0 +1,487 @@ +/* $OpenBSD: awkgram.y,v 1.9 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +%{ +#include <stdio.h> +#include <string.h> +#include "awk.h" + +void checkdup(Node *list, Cell *item); +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 */ +char *curfname = 0; /* current function name */ +Node *arglist = 0; /* list of args for current function */ +%} + +%union { + Node *p; + Cell *cp; + int i; + char *s; +} + +%token <i> FIRSTTOKEN /* must be first */ +%token <p> PROGRAM PASTAT PASTAT2 XBEGIN XEND +%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> AND BOR APPEND EQ GE GT LE LT NE IN +%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 +%token <i> PRINT PRINTF SPRINTF +%token <p> ELSE INTEST CONDEXPR +%token <i> POSTINCR PREINCR POSTDECR PREDECR +%token <cp> VAR IVAR VARNF CALL NUMBER STRING +%token <s> REGEXPR + +%type <p> pas pattern ppattern plist pplist patlist prarg term re +%type <p> pa_pat pa_stat pa_stats +%type <s> reg_expr +%type <p> simple_stmt opt_simple_stmt stmt stmtlist +%type <p> var varname funcname varlist +%type <p> for if else while +%type <i> do st +%type <i> pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor +%type <i> subop print + +%right ASGNOP +%right '?' +%right ':' +%left BOR +%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 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 +%right POWER +%right DECR INCR +%left INDIRECT +%token LASTTOKEN /* must be last */ + +%% + +program: + pas { if (errorflag==0) + winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); } + | error { yyclearin; bracecheck(); SYNTAX("bailing out"); } + ; + +and: + AND | and NL + ; + +bor: + BOR | bor NL + ; + +comma: + ',' | comma NL + ; + +do: + DO | do NL + ; + +else: + ELSE | else NL + ; + +for: + FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt + { --inloop; $$ = stat4(FOR, $3, notnull($6), $9, $12); } + | FOR '(' opt_simple_stmt ';' ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt + { --inloop; $$ = stat4(FOR, $3, NIL, $7, $10); } + | FOR '(' varname IN varname rparen {inloop++;} stmt + { --inloop; $$ = stat3(IN, $3, makearr($5), $8); } + ; + +funcname: + VAR { setfname($1); } + | CALL { setfname($1); } + ; + +if: + IF '(' pattern rparen { $$ = notnull($3); } + ; + +lbrace: + '{' | lbrace NL + ; + +nl: + NL | nl NL + ; + +opt_nl: + /* empty */ { $$ = 0; } + | nl + ; + +opt_pst: + /* empty */ { $$ = 0; } + | pst + ; + + +opt_simple_stmt: + /* empty */ { $$ = 0; } + | simple_stmt + ; + +pas: + opt_pst { $$ = 0; } + | opt_pst pa_stats opt_pst { $$ = $2; } + ; + +pa_pat: + pattern { $$ = notnull($1); } + ; + +pa_stat: + pa_pat { $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); } + | pa_pat lbrace stmtlist '}' { $$ = stat2(PASTAT, $1, $3); } + | pa_pat ',' opt_nl pa_pat { $$ = pa2stat($1, $4, stat2(PRINT, rectonode(), NIL)); } + | pa_pat ',' opt_nl pa_pat lbrace stmtlist '}' { $$ = pa2stat($1, $4, $6); } + | lbrace stmtlist '}' { $$ = stat2(PASTAT, NIL, $2); } + | XBEGIN lbrace stmtlist '}' + { 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; } + ; + +pa_stats: + pa_stat + | pa_stats opt_pst pa_stat { $$ = linkum($1, $3); } + ; + +patlist: + pattern + | patlist comma pattern { $$ = linkum($1, $3); } + ; + +ppattern: + var ASGNOP ppattern { $$ = op2($2, $1, $3); } + | ppattern '?' ppattern ':' ppattern %prec '?' + { $$ = op3(CONDEXPR, notnull($1), $3, $5); } + | ppattern bor ppattern %prec BOR + { $$ = op2(BOR, notnull($1), notnull($3)); } + | ppattern and ppattern %prec AND + { $$ = op2(AND, notnull($1), notnull($3)); } + | ppattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); } + | ppattern MATCHOP ppattern + { if (constnode($3)) + $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0)); + else + $$ = op3($2, (Node *)1, $1, $3); } + | ppattern IN varname { $$ = op2(INTEST, $1, makearr($3)); } + | '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); } + | ppattern term %prec CAT { $$ = op2(CAT, $1, $2); } + | re + | term + ; + +pattern: + var ASGNOP pattern { $$ = op2($2, $1, $3); } + | pattern '?' pattern ':' pattern %prec '?' + { $$ = op3(CONDEXPR, notnull($1), $3, $5); } + | pattern bor pattern %prec BOR + { $$ = op2(BOR, notnull($1), notnull($3)); } + | pattern and pattern %prec AND + { $$ = op2(AND, notnull($1), notnull($3)); } + | pattern EQ pattern { $$ = op2($2, $1, $3); } + | pattern GE pattern { $$ = op2($2, $1, $3); } + | pattern GT pattern { $$ = op2($2, $1, $3); } + | pattern LE pattern { $$ = op2($2, $1, $3); } + | pattern LT pattern { $$ = op2($2, $1, $3); } + | pattern NE pattern { $$ = op2($2, $1, $3); } + | pattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); } + | pattern MATCHOP pattern + { if (constnode($3)) + $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0)); + else + $$ = 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 { + if (safe) SYNTAX("cmd | getline is unsafe"); + else $$ = op3(GETLINE, $4, itonp($2), $1); } + | 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); } + | re + | term + ; + +plist: + pattern comma pattern { $$ = linkum($1, $3); } + | plist comma pattern { $$ = linkum($1, $3); } + ; + +pplist: + ppattern + | pplist comma ppattern { $$ = linkum($1, $3); } + ; + +prarg: + /* empty */ { $$ = rectonode(); } + | pplist + | '(' plist ')' { $$ = $2; } + ; + +print: + PRINT | PRINTF + ; + +pst: + NL | ';' | pst NL | pst ';' + ; + +rbrace: + '}' | rbrace NL + ; + +re: + reg_expr + { $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1, 0)); } + | NOT re { $$ = op1(NOT, notnull($2)); } + ; + +reg_expr: + '/' {startreg();} REGEXPR '/' { $$ = $3; } + ; + +rparen: + ')' | rparen NL + ; + +simple_stmt: + print prarg '|' term { + if (safe) SYNTAX("print | is unsafe"); + else $$ = stat3($1, $2, itonp($3), $4); } + | print prarg APPEND term { + if (safe) SYNTAX("print >> is unsafe"); + else $$ = stat3($1, $2, itonp($3), $4); } + | print prarg GT term { + if (safe) SYNTAX("print > is unsafe"); + else $$ = stat3($1, $2, itonp($3), $4); } + | print prarg { $$ = stat3($1, $2, NIL, NIL); } + | DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); } + | DELETE varname { $$ = stat2(DELETE, makearr($2), 0); } + | pattern { $$ = exptostat($1); } + | error { yyclearin; SYNTAX("illegal statement"); } + ; + +st: + nl + | ';' opt_nl + ; + +stmt: + BREAK st { if (!inloop) SYNTAX("break illegal outside of loops"); + $$ = stat1(BREAK, NIL); } + | CONTINUE st { if (!inloop) SYNTAX("continue illegal outside of loops"); + $$ = stat1(CONTINUE, NIL); } + | do {inloop++;} stmt {--inloop;} WHILE '(' pattern ')' st + { $$ = stat2(DO, $3, notnull($7)); } + | EXIT pattern st { $$ = stat1(EXIT, $2); } + | EXIT st { $$ = stat1(EXIT, NIL); } + | for + | if stmt else stmt { $$ = stat3(IF, $1, $2, $4); } + | if stmt { $$ = stat3(IF, $1, $2, NIL); } + | lbrace stmtlist rbrace { $$ = $2; } + | NEXT st { if (infunc) + SYNTAX("next is illegal inside a function"); + $$ = stat1(NEXT, NIL); } + | NEXTFILE st { if (infunc) + SYNTAX("nextfile is illegal inside a function"); + $$ = stat1(NEXTFILE, NIL); } + | RETURN pattern st { $$ = stat1(RETURN, $2); } + | RETURN st { $$ = stat1(RETURN, NIL); } + | simple_stmt st + | while {inloop++;} stmt { --inloop; $$ = stat2(WHILE, $1, $3); } + | ';' opt_nl { $$ = 0; } + ; + +stmtlist: + stmt + | stmtlist stmt { $$ = linkum($1, $2); } + ; + +subop: + SUB | GSUB + ; + +term: + term '/' ASGNOP term { $$ = op2(DIVEQ, $1, $4); } + | term '+' term { $$ = op2(ADD, $1, $3); } + | term '-' term { $$ = op2(MINUS, $1, $3); } + | term '*' term { $$ = op2(MULT, $1, $3); } + | term '/' term { $$ = op2(DIVIDE, $1, $3); } + | term '%' term { $$ = op2(MOD, $1, $3); } + | term POWER term { $$ = op2(POWER, $1, $3); } + | '-' term %prec UMINUS { $$ = op1(UMINUS, $2); } + | '+' term %prec UMINUS { $$ = $2; } + | NOT term %prec UMINUS { $$ = op1(NOT, notnull($2)); } + | BLTIN '(' ')' { $$ = op2(BLTIN, itonp($1), rectonode()); } + | BLTIN '(' patlist ')' { $$ = op2(BLTIN, itonp($1), $3); } + | BLTIN { $$ = op2(BLTIN, itonp($1), rectonode()); } + | CALL '(' ')' { $$ = op2(CALL, celltonode($1,CVAR), NIL); } + | CALL '(' patlist ')' { $$ = op2(CALL, celltonode($1,CVAR), $3); } + | CLOSE term { $$ = op1(CLOSE, $2); } + | DECR var { $$ = op1(PREDECR, $2); } + | INCR var { $$ = op1(PREINCR, $2); } + | var DECR { $$ = op1(POSTDECR, $1); } + | var INCR { $$ = op1(POSTINCR, $1); } + | GETLINE var LT term { $$ = op3(GETLINE, $2, itonp($3), $4); } + | GETLINE LT term { $$ = op3(GETLINE, NIL, itonp($2), $3); } + | GETLINE var { $$ = op3(GETLINE, $2, NIL, NIL); } + | GETLINE { $$ = op3(GETLINE, NIL, NIL, NIL); } + | INDEX '(' pattern comma pattern ')' + { $$ = op2(INDEX, $3, $5); } + | INDEX '(' pattern comma reg_expr ')' + { SYNTAX("index() doesn't permit regular expressions"); + $$ = op2(INDEX, $3, (Node*)$5); } + | '(' pattern ')' { $$ = $2; } + | MATCHFCN '(' pattern comma reg_expr ')' + { $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); } + | MATCHFCN '(' pattern comma pattern ')' + { if (constnode($5)) + $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1)); + else + $$ = op3(MATCHFCN, (Node *)1, $3, $5); } + | NUMBER { $$ = celltonode($1, CCON); } + | SPLIT '(' pattern comma varname comma pattern ')' /* string */ + { $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); } + | SPLIT '(' pattern comma varname comma reg_expr ')' /* const /regexp/ */ + { $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); } + | SPLIT '(' pattern comma varname ')' + { $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); } /* default */ + | SPRINTF '(' patlist ')' { $$ = op1($1, $3); } + | STRING { $$ = celltonode($1, CCON); } + | subop '(' reg_expr comma pattern ')' + { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); } + | subop '(' pattern comma pattern ')' + { if (constnode($3)) + $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode()); + else + $$ = op4($1, (Node *)1, $3, $5, rectonode()); } + | subop '(' reg_expr comma pattern comma var ')' + { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); } + | subop '(' pattern comma pattern comma var ')' + { if (constnode($3)) + $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7); + else + $$ = op4($1, (Node *)1, $3, $5, $7); } + | SUBSTR '(' pattern comma pattern comma pattern ')' + { $$ = op3(SUBSTR, $3, $5, $7); } + | SUBSTR '(' pattern comma pattern ')' + { $$ = op3(SUBSTR, $3, $5, NIL); } + | var + ; + +var: + varname + | varname '[' patlist ']' { $$ = op2(ARRAY, makearr($1), $3); } + | IVAR { $$ = op1(INDIRECT, celltonode($1, CVAR)); } + | INDIRECT term { $$ = op1(INDIRECT, $2); } + ; + +varlist: + /* nothing */ { arglist = $$ = 0; } + | VAR { arglist = $$ = celltonode($1,CVAR); } + | varlist comma VAR { + checkdup($1, $3); + arglist = $$ = linkum($1,celltonode($3,CVAR)); } + ; + +varname: + VAR { $$ = celltonode($1, CVAR); } + | ARG { $$ = op1(ARG, itonp($1)); } + | VARNF { $$ = op1(VARNF, (Node *) $1); } + ; + + +while: + WHILE '(' pattern rparen { $$ = notnull($3); } + ; + +%% + +void setfname(Cell *p) +{ + if (isarr(p)) + SYNTAX("%s is an array, not a function", p->nval); + else if (isfcn(p)) + SYNTAX("you can't define function %s more than once", p->nval); + curfname = p->nval; +} + +int constnode(Node *p) +{ + return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON; +} + +char *strnode(Node *p) +{ + return ((Cell *)(p->narg[0]))->sval; +} + +Node *notnull(Node *n) +{ + switch (n->nobj) { + case LE: case LT: case EQ: case NE: case GT: case GE: + case BOR: case AND: case NOT: + return n; + default: + return op2(NE, n, nullnode); + } +} + +void checkdup(Node *vl, Cell *cp) /* check if name already in list */ +{ + char *s = cp->nval; + for ( ; vl; vl = vl->nnext) { + if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) { + SYNTAX("duplicate argument %s", s); + break; + } + } +} diff --git a/awk/b.c b/awk/b.c @@ -0,0 +1,958 @@ +/* $OpenBSD: b.c,v 1.17 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +/* lasciate ogne speranza, voi ch'intrate. */ + +#define DEBUG + +#include <ctype.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 */ +#define info(v) (v)->ntype /* badly overloaded here */ +#define left(v) (v)->narg[0] +#define right(v) (v)->narg[1] +#define parent(v) (v)->nnext + +#define LEAF case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL: +#define ELEAF case EMPTYRE: /* empty string in regexp */ +#define UNARY case STAR: case PLUS: case QUEST: + +/* encoding in tree Nodes: + leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE): + left is index, right contains value or pointer to value + unary (STAR, PLUS, QUEST): left is child, right is null + binary (CAT, OR): left and right are children + parent contains pointer to parent +*/ + + +int *setvec; +int *tmpset; +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 int setcnt; +static int poscnt; + +char *patbeg; +int patlen; + +#define NFA 20 /* 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 */ +{ + int i, use, nuse; + fa *pfa; + static int now = 1; + + if (setvec == 0) { /* first time through any RE */ + maxsetvec = MAXLIN; + setvec = (int *) calloc(maxsetvec, sizeof(int)); + tmpset = (int *) calloc(maxsetvec, sizeof(int)); + if (setvec == 0 || tmpset == 0) + overflo("out of space initializing makedfa"); + } + + if (compile_time) /* a constant for sure */ + return mkdfa(s, anchor); + for (i = 0; i < nfatab; i++) /* is it there already? */ + if (fatab[i]->anchor == anchor + && strcmp((const char *) fatab[i]->restr, s) == 0) { + fatab[i]->use = now++; + return fatab[i]; + } + pfa = mkdfa(s, anchor); + if (nfatab < NFA) { /* room for another */ + fatab[nfatab] = pfa; + fatab[nfatab]->use = now++; + nfatab++; + return pfa; + } + use = fatab[0]->use; /* replace least-recently used */ + nuse = 0; + for (i = 1; i < nfatab; i++) + if (fatab[i]->use < use) { + use = fatab[i]->use; + nuse = i; + } + freefa(fatab[nuse]); + fatab[nuse] = pfa; + pfa->use = now++; + return pfa; +} + +fa *mkdfa(const char *s, int anchor) /* does the real work of making a dfa */ + /* anchor = 1 for anchored matches, else 0 */ +{ + Node *p, *p1; + fa *f; + + p = reparse(s); + p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p); + /* put ALL STAR in front of reg. exp. */ + p1 = op2(CAT, p1, op2(FINAL, NIL, NIL)); + /* put FINAL after reg. exp. */ + + 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"); + 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(*(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"); + *f->posns[1] = 0; + f->initstat = makeinit(f, anchor); + f->anchor = anchor; + f->restr = (uschar *) tostring(s); + return f; +} + +int makeinit(fa *f, int 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(k+1, sizeof(int))) == NULL) + overflo("out of space in makeinit"); + 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++) + 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++) { + (f->posns[0])[i] = (f->posns[2])[i]; + } + + f->out[0] = f->out[2]; + if (f->curstat != 2) + --(*f->posns[f->curstat]); + } + return f->curstat; +} + +void penter(Node *p) /* set up parent pointers and leaf indices */ +{ + switch (type(p)) { + ELEAF + LEAF + info(p) = poscnt; + poscnt++; + break; + UNARY + penter(left(p)); + parent(left(p)) = p; + break; + case CAT: + case OR: + penter(left(p)); + penter(right(p)); + parent(left(p)) = p; + parent(right(p)) = p; + break; + default: /* can't happen */ + FATAL("can't happen: unknown type %d in penter", type(p)); + break; + } +} + +void freetr(Node *p) /* free parse tree */ +{ + switch (type(p)) { + ELEAF + LEAF + xfree(p); + break; + UNARY + freetr(left(p)); + xfree(p); + break; + case CAT: + case OR: + freetr(left(p)); + freetr(right(p)); + xfree(p); + break; + default: /* can't happen */ + FATAL("can't happen: unknown type %d in freetr", type(p)); + break; + } +} + +/* 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 */ +{ /* only pick up one 8-bit byte (2 chars) */ + uschar *p; + int n = 0; + int i; + + for (i = 0, p = (uschar *) *pp; i < 2 && isxdigit(*p); i++, p++) { + if (isdigit(*p)) + n = 16 * n + *p - '0'; + else if (*p >= 'a' && *p <= 'f') + n = 16 * n + *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + n = 16 * n + *p - 'A' + 10; + } + *pp = (uschar *) p; + return n; +} + +#define isoctdigit(c) ((c) >= '0' && (c) <= '7') /* multiple use of arg */ + +int quoted(uschar **pp) /* pick up next thing after a \\ */ + /* and increment *pp */ +{ + uschar *p = *pp; + int c; + + if ((c = *p++) == 't') + c = '\t'; + else if (c == 'n') + c = '\n'; + else if (c == 'f') + c = '\f'; + else if (c == 'r') + c = '\r'; + else if (c == 'b') + c = '\b'; + else if (c == '\\') + c = '\\'; + else if (c == 'x') { /* hexadecimal goo follows */ + c = hexstr(&p); /* this adds a null if number is invalid */ + } else if (isoctdigit(c)) { /* \d \dd \ddd */ + int n = c - '0'; + if (isoctdigit(*p)) { + n = 8 * n + *p++ - '0'; + if (isoctdigit(*p)) + n = 8 * n + *p++ - '0'; + } + c = n; + } /* else */ + /* c = c; */ + *pp = p; + return c; +} + +char *cclenter(const char *argp) /* add a character class */ +{ + int i, c, c2; + uschar *p = (uschar *) argp; + uschar *op, *bp; + static uschar *buf = 0; + static int bufsz = 100; + + op = p; + if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL) + FATAL("out of space for character class [%.10s...] 1", p); + bp = buf; + for (i = 0; (c = *p++) != 0; ) { + if (c == '\\') { + c = quoted(&p); + } else if (c == '-' && i > 0 && bp[-1] != 0) { + if (*p != 0) { + c = bp[-1]; + c2 = *p++; + if (c2 == '\\') + c2 = quoted(&p); + if (c > c2) { /* empty; ignore */ + bp--; + i--; + continue; + } + while (c < c2) { + if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter1")) + FATAL("out of space for character class [%.10s...] 2", p); + *bp++ = ++c; + i++; + } + continue; + } + } + if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter2")) + FATAL("out of space for character class [%.10s...] 3", p); + *bp++ = c; + i++; + } + *bp = 0; + dprintf( ("cclenter: in = |%s|, out = |%s|\n", op, buf) ); + xfree(op); + return (char *) tostring((char *) buf); +} + +void overflo(const char *s) +{ + FATAL("regular expression too big: %.30s...", s); +} + +void cfoll(fa *f, Node *v) /* enter follow set of each leaf of vertex v into lfollow[leaf] */ +{ + int i; + int *p; + + switch (type(v)) { + ELEAF + LEAF + 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()"); + } + for (i = 0; i <= f->accept; i++) + setvec[i] = 0; + setcnt = 0; + follow(v); /* computes setvec and setcnt */ + if ((p = (int *) calloc(setcnt+1, sizeof(int))) == NULL) + overflo("out of space building follow set"); + f->re[info(v)].lfollow = p; + *p = setcnt; + for (i = f->accept; i >= 0; i--) + if (setvec[i] == 1) + *++p = i; + break; + UNARY + cfoll(f,left(v)); + break; + case CAT: + case OR: + cfoll(f,left(v)); + cfoll(f,right(v)); + break; + default: /* can't happen */ + FATAL("can't happen: unknown type %d in cfoll", type(v)); + } +} + +int first(Node *p) /* collects initially active leaves of p into setvec */ + /* returns 0 if p matches empty string */ +{ + int b, lp; + + switch (type(p)) { + ELEAF + 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()"); + } + if (type(p) == EMPTYRE) { + setvec[lp] = 0; + return(0); + } + if (setvec[lp] != 1) { + setvec[lp] = 1; + setcnt++; + } + if (type(p) == CCL && (*(char *) right(p)) == '\0') + return(0); /* empty CCL */ + else return(1); + case PLUS: + if (first(left(p)) == 0) return(0); + return(1); + case STAR: + case QUEST: + first(left(p)); + return(0); + case CAT: + if (first(left(p)) == 0 && first(right(p)) == 0) return(0); + return(1); + case OR: + b = first(right(p)); + if (first(left(p)) == 0 || b == 0) return(0); + return(1); + } + FATAL("can't happen: unknown type %d in first", type(p)); /* can't happen */ + return(-1); +} + +void follow(Node *v) /* collects leaves that can follow v into setvec */ +{ + Node *p; + + if (type(v) == FINAL) + return; + p = parent(v); + switch (type(p)) { + case STAR: + case PLUS: + first(v); + follow(p); + return; + + case OR: + case QUEST: + follow(p); + return; + + case CAT: + if (v == left(p)) { /* v is left child of p */ + if (first(right(p)) == 0) { + follow(p); + return; + } + } else /* v is right child */ + follow(p); + return; + } +} + +int member(int c, const char *sarg) /* is c in s? */ +{ + uschar *s = (uschar *) sarg; + + while (*s) + if (c == *s++) + return(1); + return(0); +} + +int match(fa *f, const char *p0) /* shortest match ? */ +{ + int s, ns; + uschar *p = (uschar *) p0; + + s = f->reset ? makeinit(f,0) : f->initstat; + if (f->out[s]) + return(1); + do { + /* assert(*p < NCHARS); */ + if ((ns = f->gototab[s][*p]) != 0) + s = ns; + else + s = cgoto(f, s, *p); + if (f->out[s]) + return(1); + } while (*p++ != 0); + return(0); +} + +int pmatch(fa *f, const char *p0) /* longest match, for sub */ +{ + int s, ns; + uschar *p = (uschar *) p0; + uschar *q; + int i, k; + + /* s = f->reset ? makeinit(f,1) : f->initstat; */ + if (f->reset) { + f->initstat = s = makeinit(f,1); + } else { + s = f->initstat; + } + patbeg = (char *) p; + patlen = -1; + do { + q = p; + do { + if (f->out[s]) /* final state */ + patlen = q-p; + /* assert(*q < NCHARS); */ + if ((ns = f->gototab[s][*q]) != 0) + s = ns; + else + s = cgoto(f, s, *q); + if (s == 1) { /* no transition */ + if (patlen >= 0) { + patbeg = (char *) p; + return(1); + } + else + goto nextin; /* no match */ + } + } while (*q++ != 0); + if (f->out[s]) + patlen = q-p-1; /* don't count $ */ + if (patlen >= 0) { + patbeg = (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(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); + 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; + + /* s = f->reset ? makeinit(f,1) : f->initstat; */ + if (f->reset) { + f->initstat = s = makeinit(f,1); + } else { + s = f->initstat; + } + patlen = -1; + while (*p) { + q = p; + do { + if (f->out[s]) /* final state */ + patlen = q-p; + /* assert(*q < NCHARS); */ + if ((ns = f->gototab[s][*q]) != 0) + s = ns; + else + s = cgoto(f, s, *q); + if (s == 1) { /* no transition */ + if (patlen > 0) { + patbeg = (char *) p; + return(1); + } else + goto nnextin; /* no nonempty match */ + } + } while (*q++ != 0); + if (f->out[s]) + patlen = q-p-1; /* don't count $ */ + if (patlen > 0 ) { + patbeg = (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(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); +} + +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 */ + rtok = relex(); + /* GNU compatibility: an empty regexp matches anything */ + if (rtok == '\0') { + /* FATAL("empty regular expression"); previous */ + return(op2(EMPTYRE, NIL, NIL)); + } + np = regexp(); + if (rtok != '\0') + FATAL("syntax error in regular expression %s at %s", lastre, prestr); + return(np); +} + +Node *regexp(void) /* top-level parse of reg expr */ +{ + return (alt(concat(primary()))); +} + +Node *primary(void) +{ + Node *np; + + switch (rtok) { + case CHAR: + np = op2(CHAR, NIL, itonp(rlxval)); + rtok = relex(); + return (unary(np)); + case ALL: + rtok = relex(); + return (unary(op2(ALL, NIL, NIL))); + case EMPTYRE: + rtok = relex(); + return (unary(op2(ALL, NIL, NIL))); + case DOT: + rtok = relex(); + return (unary(op2(DOT, NIL, NIL))); + case CCL: + np = op2(CCL, NIL, (Node*) cclenter((char *) rlxstr)); + rtok = relex(); + return (unary(np)); + case NCCL: + np = op2(NCCL, NIL, (Node *) cclenter((char *) rlxstr)); + rtok = relex(); + return (unary(np)); + case '^': + rtok = relex(); + return (unary(op2(CHAR, NIL, itonp(HAT)))); + case '$': + rtok = relex(); + return (unary(op2(CHAR, NIL, NIL))); + case '(': + rtok = relex(); + if (rtok == ')') { /* special pleading for () */ + rtok = relex(); + return unary(op2(CCL, NIL, (Node *) tostring(""))); + } + np = regexp(); + if (rtok == ')') { + rtok = relex(); + return (unary(np)); + } + else + FATAL("syntax error in regular expression %s at %s", lastre, prestr); + default: + FATAL("illegal primary in regular expression %s at %s", lastre, prestr); + } + return 0; /*NOTREACHED*/ +} + +Node *concat(Node *np) +{ + switch (rtok) { + case CHAR: case DOT: case ALL: case EMPTYRE: case CCL: case NCCL: case '$': case '(': + return (concat(op2(CAT, np, primary()))); + } + return (np); +} + +Node *alt(Node *np) +{ + if (rtok == OR) { + rtok = relex(); + return (alt(op2(OR, np, concat(primary())))); + } + return (np); +} + +Node *unary(Node *np) +{ + switch (rtok) { + case STAR: + rtok = relex(); + return (unary(op2(STAR, np, NIL))); + case PLUS: + rtok = relex(); + return (unary(op2(PLUS, np, NIL))); + case QUEST: + rtok = relex(); + return (unary(op2(QUEST, np, NIL))); + default: + return (np); + } +} + +/* + * Character class definitions conformant to the POSIX locale as + * defined in IEEE P1003.1 draft 7 of June 2001, assuming the source + * and operating character sets are both ASCII (ISO646) or supersets + * thereof. + * + * Note that to avoid overflowing the temporary buffer used in + * relex(), the expanded character class (prior to range expansion) + * must be less than twice the size of their full name. + */ + +/* Because isblank doesn't show up in any of the header files on any + * system i use, it's defined here. if some other locale has a richer + * definition of "blank", define HAS_ISBLANK and provide your own + * version. + * the parentheses here are an attempt to find a path through the maze + * of macro definition and/or function and/or version provided. thanks + * to nelson beebe for the suggestion; let's see if it works everywhere. + */ + +#ifndef HAS_ISBLANK + +int (xisblank)(int c) +{ + return c==' ' || c=='\t'; +} + +#endif + +struct charclass { + const char *cc_name; + int cc_namelen; + int (*cc_func)(int); +} charclasses[] = { + { "alnum", 5, isalnum }, + { "alpha", 5, isalpha }, +#ifndef HAS_ISBLANK + { "blank", 5, isspace }, /* was isblank */ +#else + { "blank", 5, isblank }, +#endif + { "cntrl", 5, iscntrl }, + { "digit", 5, isdigit }, + { "graph", 5, isgraph }, + { "lower", 5, islower }, + { "print", 5, isprint }, + { "punct", 5, ispunct }, + { "space", 5, isspace }, + { "upper", 5, isupper }, + { "xdigit", 6, isxdigit }, + { NULL, 0, NULL }, +}; + + +int relex(void) /* lexical analyzer for reparse */ +{ + int c, n; + int cflag; + static uschar *buf = 0; + static int bufsz = 100; + uschar *bp; + struct charclass *cc; + int i; + + switch (c = *prestr++) { + case '|': return OR; + case '*': return STAR; + case '+': return PLUS; + case '?': return QUEST; + case '.': return DOT; + case '\0': prestr--; return '\0'; + case '^': + case '$': + case '(': + case ')': + return c; + case '\\': + rlxval = quoted(&prestr); + return CHAR; + default: + rlxval = c; + return CHAR; + case '[': + if (buf == 0 && (buf = (uschar *) malloc(bufsz)) == NULL) + FATAL("out of space in reg expr %.10s..", lastre); + bp = buf; + if (*prestr == '^') { + cflag = 1; + prestr++; + } + else + cflag = 0; + n = 2 * strlen((const char *) prestr)+1; + if (!adjbuf((char **) &buf, &bufsz, n, n, (char **) &bp, "relex1")) + FATAL("out of space for reg expr %.10s...", lastre); + for (; ; ) { + if ((c = *prestr++) == '\\') { + *bp++ = '\\'; + if ((c = *prestr++) == '\0') + FATAL("nonterminated character class %.20s...", lastre); + *bp++ = c; + /* } else if (c == '\n') { */ + /* FATAL("newline in character class %.20s...", lastre); */ + } else if (c == '[' && *prestr == ':') { + /* POSIX char class names, Dag-Erling Smorgrav, des@ofug.org */ + for (cc = charclasses; cc->cc_name; cc++) + if (strncmp((const char *) prestr + 1, (const char *) cc->cc_name, cc->cc_namelen) == 0) + break; + 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++) { + 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)) { + *bp++ = i; + n++; + } + } + } else + *bp++ = c; + } else if (c == '\0') { + FATAL("nonterminated character class %.20s", lastre); + } else if (bp == buf) { /* 1st char is special */ + *bp++ = c; + } else if (c == ']') { + *bp++ = 0; + rlxstr = (uschar *) tostring((char *) buf); + if (cflag == 0) + return CCL; + else + return NCCL; + } else + *bp++ = c; + } + } +} + +int cgoto(fa *f, int s, int c) +{ + int i, j, k; + int *p, *q; + + 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()"); + } + for (i = 0; i <= f->accept; i++) + setvec[i] = 0; + setcnt = 0; + /* compute positions of gototab[s,c] into setvec */ + p = f->posns[s]; + for (i = 1; i <= *p; i++) { + if ((k = f->re[p[i]].ltype) != FINAL) { + if ((k == CHAR && c == ptoi(f->re[p[i]].lval.np)) + || (k == DOT && c != 0 && c != HAT) + || (k == ALL && c != 0) + || (k == EMPTYRE && c != 0) + || (k == CCL && member(c, (char *) f->re[p[i]].lval.up)) + || (k == NCCL && !member(c, (char *) f->re[p[i]].lval.up) && c != 0 && c != HAT)) { + 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"); + } + if (setvec[q[j]] == 0) { + setcnt++; + setvec[q[j]] = 1; + } + } + } + } + } + /* determine if setvec is a previous state */ + tmpset[0] = setcnt; + j = 1; + for (i = f->accept; i >= 0; i--) + if (setvec[i]) { + tmpset[j++] = i; + } + /* tmpset == previous state? */ + for (i = 1; i <= f->curstat; i++) { + p = f->posns[i]; + if ((k = tmpset[0]) != p[0]) + goto different; + for (j = 1; j <= k; j++) + if (tmpset[j] != p[j]) + goto different; + /* setvec is state i */ + 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); + for (i = 0; i < NCHARS; i++) + f->gototab[f->curstat][i] = 0; + xfree(f->posns[f->curstat]); + if ((p = (int *) calloc(setcnt+1, sizeof(int))) == NULL) + overflo("out of space in cgoto"); + + f->posns[f->curstat] = p; + f->gototab[s][c] = f->curstat; + for (i = 0; i <= setcnt; i++) + p[i] = tmpset[i]; + if (setvec[f->accept]) + f->out[f->curstat] = 1; + else + f->out[f->curstat] = 0; + return f->curstat; +} + + +void freefa(fa *f) /* free a finite automaton */ +{ + int i; + + if (f == NULL) + return; + 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->restr); + xfree(f); +} diff --git a/awk/gen/maketab.c b/awk/gen/maketab.c @@ -0,0 +1,178 @@ +/* $OpenBSD: maketab.c,v 1.11 2010/06/13 17:58:19 millert Exp $ */ +/**************************************************************** +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 program makes the table to link function names + * and type indices that is used by execute() in run.c. + * it finds the indices in ytab.h, produced by yacc. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "../awk.h" +#include "../ytab.h" + +struct xx +{ int token; + const char *name; + const char *pname; +} proc[] = { + { PROGRAM, "program", NULL }, + { BOR, "boolop", " || " }, + { AND, "boolop", " && " }, + { NOT, "boolop", " !" }, + { NE, "relop", " != " }, + { EQ, "relop", " == " }, + { LE, "relop", " <= " }, + { LT, "relop", " < " }, + { GE, "relop", " >= " }, + { GT, "relop", " > " }, + { ARRAY, "array", NULL }, + { INDIRECT, "indirect", "$(" }, + { SUBSTR, "substr", "substr" }, + { SUB, "sub", "sub" }, + { GSUB, "gsub", "gsub" }, + { INDEX, "sindex", "sindex" }, + { SPRINTF, "awksprintf", "sprintf " }, + { ADD, "arith", " + " }, + { MINUS, "arith", " - " }, + { MULT, "arith", " * " }, + { DIVIDE, "arith", " / " }, + { MOD, "arith", " % " }, + { UMINUS, "arith", " -" }, + { POWER, "arith", " **" }, + { PREINCR, "incrdecr", "++" }, + { POSTINCR, "incrdecr", "++" }, + { PREDECR, "incrdecr", "--" }, + { POSTDECR, "incrdecr", "--" }, + { CAT, "cat", " " }, + { PASTAT, "pastat", NULL }, + { PASTAT2, "dopa2", NULL }, + { MATCH, "matchop", " ~ " }, + { NOTMATCH, "matchop", " !~ " }, + { MATCHFCN, "matchop", "matchop" }, + { INTEST, "intest", "intest" }, + { PRINTF, "awkprintf", "printf" }, + { PRINT, "printstat", "print" }, + { CLOSE, "closefile", "closefile" }, + { DELETE, "awkdelete", "awkdelete" }, + { SPLIT, "split", "split" }, + { ASSIGN, "assign", " = " }, + { ADDEQ, "assign", " += " }, + { SUBEQ, "assign", " -= " }, + { MULTEQ, "assign", " *= " }, + { DIVEQ, "assign", " /= " }, + { MODEQ, "assign", " %= " }, + { POWEQ, "assign", " ^= " }, + { CONDEXPR, "condexpr", " ?: " }, + { IF, "ifstat", "if(" }, + { WHILE, "whilestat", "while(" }, + { FOR, "forstat", "for(" }, + { DO, "dostat", "do" }, + { IN, "instat", "instat" }, + { NEXT, "jump", "next" }, + { NEXTFILE, "jump", "nextfile" }, + { EXIT, "jump", "exit" }, + { BREAK, "jump", "break" }, + { CONTINUE, "jump", "continue" }, + { RETURN, "jump", "ret" }, + { BLTIN, "bltin", "bltin" }, + { CALL, "call", "call" }, + { ARG, "arg", "arg" }, + { VARNF, "getnf", "NF" }, + { GETLINE, "awkgetline", "getline" }, + { 0, "", "" }, +}; + +#define SIZE (LASTTOKEN - FIRSTTOKEN + 1) +const char *table[SIZE]; +char *names[SIZE]; + +int main(int argc, char *argv[]) +{ + const struct xx *p; + int i, n, tok; + char c; + FILE *fp; + char buf[200], name[200], def[200]; + + printf("#include <stdio.h>\n"); + printf("#include \"awk.h\"\n"); + printf("#include \"ytab.h\"\n\n"); + for (i = SIZE; --i >= 0; ) + names[i] = ""; + + if(argc != 2) { + fprintf(stderr, "maketab: expecting filename of ytab.h!\n"); + exit(1); + } + + if ((fp = fopen(argv[1], "r")) == NULL) { + fprintf(stderr, "maketab: can't open ytab.h!\n"); + exit(1); + } + + printf("static char *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 (n != 4 || c != '#' || strcmp(def, "define") != 0) + continue; /* not a valid #define */ + if (tok < FIRSTTOKEN || tok > LASTTOKEN) { + /* fprintf(stderr, "maketab: funny token %d %s ignored\n", tok, buf); */ + continue; + } + names[tok-FIRSTTOKEN] = (char *) strdup(name); + if (names[tok-FIRSTTOKEN] == NULL) { + fprintf(stderr, "maketab: out of memory\n"); + exit(1); + } + printf("\t(char *) \"%s\",\t/* %d */\n", name, tok); + i++; + } + printf("};\n\n"); + + for (p=proc; p->token!=0; p++) + 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("};\n\n"); + + printf("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(" snprintf(buf, sizeof buf, \"token %%d\", n);\n"); + printf(" return buf;\n"); + printf(" }\n"); + printf(" return printname[n-FIRSTTOKEN];\n"); + printf("}\n"); + return 0; +} diff --git a/awk/lex.c b/awk/lex.c @@ -0,0 +1,597 @@ +/* $OpenBSD: lex.c,v 1.12 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "awk.h" +#include "ytab.h" + +extern YYSTYPE yylval; +extern int infunc; + +int lineno = 1; +int bracecnt = 0; +int brackcnt = 0; +int parencnt = 0; + +typedef struct Keyword { + const char *word; + int sub; + int type; +} Keyword; + +Keyword keywords[] ={ /* keep sorted: binary searched */ + { "BEGIN", XBEGIN, XBEGIN }, + { "END", XEND, XEND }, + { "NF", VARNF, VARNF }, + { "and", FAND, BLTIN }, + { "atan2", FATAN, BLTIN }, + { "break", BREAK, BREAK }, + { "close", CLOSE, CLOSE }, + { "compl", FCOMPL, BLTIN }, + { "continue", CONTINUE, CONTINUE }, + { "cos", FCOS, BLTIN }, + { "delete", DELETE, DELETE }, + { "do", DO, DO }, + { "else", ELSE, ELSE }, + { "exit", EXIT, EXIT }, + { "exp", FEXP, BLTIN }, + { "fflush", FFLUSH, BLTIN }, + { "for", FOR, FOR }, + { "func", FUNC, FUNC }, + { "function", FUNC, FUNC }, + { "getline", GETLINE, GETLINE }, + { "gsub", GSUB, GSUB }, + { "if", IF, IF }, + { "in", IN, IN }, + { "index", INDEX, INDEX }, + { "int", FINT, BLTIN }, + { "length", FLENGTH, BLTIN }, + { "log", FLOG, BLTIN }, + { "lshift", FLSHIFT, BLTIN }, + { "match", MATCHFCN, MATCHFCN }, + { "next", NEXT, NEXT }, + { "nextfile", NEXTFILE, NEXTFILE }, + { "or", FFOR, BLTIN }, + { "print", PRINT, PRINT }, + { "printf", PRINTF, PRINTF }, + { "rand", FRAND, BLTIN }, + { "return", RETURN, RETURN }, + { "rshift", FRSHIFT, BLTIN }, + { "sin", FSIN, BLTIN }, + { "split", SPLIT, SPLIT }, + { "sprintf", SPRINTF, SPRINTF }, + { "sqrt", FSQRT, BLTIN }, + { "srand", FSRAND, BLTIN }, + { "sub", SUB, SUB }, + { "substr", SUBSTR, SUBSTR }, + { "system", FSYSTEM, BLTIN }, + { "tolower", FTOLOWER, BLTIN }, + { "toupper", FTOUPPER, BLTIN }, + { "while", WHILE, WHILE }, + { "xor", FXOR, BLTIN }, +}; + +#define RET(x) { if(dbg)printf("lex %s\n", tokname(x)); return(x); } + +int peek(void); +int gettok(char **, int *); +int binsearch(char *, Keyword *, int); + +int peek(void) +{ + int c = input(); + unput(c); + return c; +} + +int gettok(char **pbuf, int *psz) /* get next input token */ +{ + int c, retc; + char *buf = *pbuf; + int sz = *psz; + char *bp = buf; + + c = input(); + if (c == 0) + return 0; + buf[0] = c; + buf[1] = 0; + if (!isalnum(c) && c != '.' && c != '_') + return c; + + *bp++ = c; + if (isalpha(c) || c == '_') { /* it's a varname */ + for ( ; (c = input()) != 0; ) { + if (bp-buf >= sz) + if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok")) + FATAL( "out of space for name %.10s...", buf ); + if (isalnum(c) || c == '_') + *bp++ = c; + else { + *bp = 0; + unput(c); + break; + } + } + *bp = 0; + retc = 'a'; /* alphanumeric */ + } else { /* maybe it's a number, but could be . */ + char *rem; + /* read input until can't be a number */ + for ( ; (c = input()) != 0; ) { + 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' + || c == '.' || c == '+' || c == '-') + *bp++ = c; + else { + unput(c); + break; + } + } + *bp = 0; + strtod(buf, &rem); /* parse the number */ + if (rem == buf) { /* it wasn't a valid number at all */ + buf[1] = 0; /* return one character as token */ + retc = buf[0]; /* character is its own type */ + unputstr(rem+1); /* put rest back for later */ + } else { /* some prefix was a number */ + unputstr(rem); /* put rest back for later */ + rem[0] = 0; /* truncate buf after number part */ + retc = '0'; /* type is number */ + } + } + *pbuf = buf; + *psz = sz; + return retc; +} + +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 */ + +int yylex(void) +{ + int c; + static char *buf = 0; + static int bufsize = 5; /* BUG: setting this small causes core dump! */ + + if (buf == 0 && (buf = (char *) malloc(bufsize)) == NULL) + FATAL( "out of space in yylex" ); + if (sc) { + sc = 0; + RET('}'); + } + if (reg) { + reg = 0; + return regexpr(); + } + for (;;) { + c = gettok(&buf, &bufsize); + if (c == 0) + return 0; + if (isalpha(c) || c == '_') + return word(buf); + if (isdigit(c)) { + yylval.cp = setsymtab(buf, tostring(buf), atof(buf), CON|NUM, symtab); + /* should this also have STR set? */ + RET(NUMBER); + } + + yylval.i = c; + switch (c) { + case '\n': /* {EOL} */ + RET(NL); + case '\r': /* assume \n is coming */ + case ' ': /* {WS}+ */ + case '\t': + break; + case '#': /* #.* strip comments */ + while ((c = input()) != '\n' && c != 0) + ; + unput(c); + break; + case ';': + RET(';'); + case '\\': + if (peek() == '\n') { + input(); + } else if (peek() == '\r') { + input(); input(); /* \n */ + lineno++; + } else { + RET(c); + } + break; + case '&': + if (peek() == '&') { + input(); RET(AND); + } else + RET('&'); + case '|': + if (peek() == '|') { + input(); RET(BOR); + } else + RET('|'); + case '!': + if (peek() == '=') { + input(); yylval.i = NE; RET(NE); + } else if (peek() == '~') { + input(); yylval.i = NOTMATCH; RET(MATCHOP); + } else + RET(NOT); + case '~': + yylval.i = MATCH; + RET(MATCHOP); + case '<': + if (peek() == '=') { + input(); yylval.i = LE; RET(LE); + } else { + yylval.i = LT; RET(LT); + } + case '=': + if (peek() == '=') { + input(); yylval.i = EQ; RET(EQ); + } else { + yylval.i = ASSIGN; RET(ASGNOP); + } + case '>': + if (peek() == '=') { + input(); yylval.i = GE; RET(GE); + } else if (peek() == '>') { + input(); yylval.i = APPEND; RET(APPEND); + } else { + yylval.i = GT; RET(GT); + } + case '+': + if (peek() == '+') { + input(); yylval.i = INCR; RET(INCR); + } else if (peek() == '=') { + input(); yylval.i = ADDEQ; RET(ASGNOP); + } else + RET('+'); + case '-': + if (peek() == '-') { + input(); yylval.i = DECR; RET(DECR); + } else if (peek() == '=') { + input(); yylval.i = SUBEQ; RET(ASGNOP); + } else + RET('-'); + case '*': + if (peek() == '=') { /* *= */ + input(); yylval.i = MULTEQ; RET(ASGNOP); + } else if (peek() == '*') { /* ** or **= */ + input(); /* eat 2nd * */ + if (peek() == '=') { + input(); yylval.i = POWEQ; RET(ASGNOP); + } else { + RET(POWER); + } + } else + RET('*'); + case '/': + RET('/'); + case '%': + if (peek() == '=') { + input(); yylval.i = MODEQ; RET(ASGNOP); + } else + RET('%'); + case '^': + if (peek() == '=') { + input(); yylval.i = POWEQ; RET(ASGNOP); + } else + RET(POWER); + + case '$': + /* BUG: awkward, if not wrong */ + c = gettok(&buf, &bufsize); + if (isalpha(c)) { + if (strcmp(buf, "NF") == 0) { /* very special */ + unputstr("(NF)"); + RET(INDIRECT); + } + c = peek(); + if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) { + unputstr(buf); + RET(INDIRECT); + } + yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab); + RET(IVAR); + } else if (c == 0) { /* */ + SYNTAX( "unexpected end of input after $" ); + RET(';'); + } else { + unputstr(buf); + RET(INDIRECT); + } + + case '}': + if (--bracecnt < 0) + SYNTAX( "extra }" ); + sc = 1; + RET(';'); + case ']': + if (--brackcnt < 0) + SYNTAX( "extra ]" ); + RET(']'); + case ')': + if (--parencnt < 0) + SYNTAX( "extra )" ); + RET(')'); + case '{': + bracecnt++; + RET('{'); + case '[': + brackcnt++; + RET('['); + case '(': + parencnt++; + RET('('); + + case '"': + return string(); /* BUG: should be like tran.c ? */ + + default: + RET(c); + } + } +} + +int string(void) +{ + int c, n; + char *s, *bp; + static char *buf = 0; + static int bufsz = 500; + + if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of space for strings"); + for (bp = buf; (c = input()) != '"'; ) { + if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, "string")) + FATAL("out of space for string %.10s...", buf); + switch (c) { + case '\n': + case '\r': + case 0: + SYNTAX( "non-terminated string %.10s...", buf ); + lineno++; + if (c == 0) /* hopeless */ + FATAL( "giving up" ); + break; + case '\\': + c = input(); + switch (c) { + case '"': *bp++ = '"'; 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 '\\': *bp++ = '\\'; break; + + case '0': case '1': case '2': /* octal: \d \dd \ddd */ + case '3': case '4': case '5': case '6': case '7': + n = c - '0'; + if ((c = peek()) >= '0' && c < '8') { + n = 8 * n + input() - '0'; + if ((c = peek()) >= '0' && c < '8') + n = 8 * n + input() - '0'; + } + *bp++ = n; + break; + + case 'x': /* hex \x0-9a-fA-F + */ + { char xbuf[100], *px; + for (px = xbuf; (c = input()) != 0 && px-xbuf < 100-2; ) { + if (isdigit(c) + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')) + *px++ = c; + else + break; + } + *px = 0; + unput(c); + sscanf(xbuf, "%x", (unsigned int *) &n); + *bp++ = n; + break; + } + + default: + *bp++ = c; + break; + } + break; + default: + *bp++ = c; + break; + } + } + *bp = 0; + s = tostring(buf); + *bp++ = ' '; *bp++ = 0; + yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab); + RET(STRING); +} + + +int binsearch(char *w, Keyword *kp, int n) +{ + int cond, low, mid, high; + + low = 0; + high = n - 1; + while (low <= high) { + mid = (low + high) / 2; + if ((cond = strcmp(w, kp[mid].word)) < 0) + high = mid - 1; + else if (cond > 0) + low = mid + 1; + else + return mid; + } + return -1; +} + +int word(char *w) +{ + 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 */ + yylval.i = kp->sub; + switch (kp->type) { /* special handling */ + case BLTIN: + if (kp->sub == FSYSTEM && safe) + SYNTAX( "system is unsafe" ); + RET(kp->type); + case FUNC: + if (infunc) + SYNTAX( "illegal nested function" ); + RET(kp->type); + case RETURN: + if (!infunc) + SYNTAX( "return not in function" ); + RET(kp->type); + case VARNF: + yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab); + RET(VARNF); + default: + RET(kp->type); + } + } + c = peek(); /* look for '(' */ + if (c != '(' && infunc && (n=isarg(w)) >= 0) { + yylval.i = n; + RET(ARG); + } else { + yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab); + if (c == '(') { + RET(CALL); + } else { + RET(VAR); + } + } +} + +void startreg(void) /* next call to yylex will return a regular expression */ +{ + reg = 1; +} + +int regexpr(void) +{ + int c, openclass = 0; + static char *buf = 0; + static int bufsz = 500; + char *bp; + + if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of space for rex expr"); + bp = buf; + for ( ; ((c = input()) != '/' || openclass == 1) && 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 ); + unput('\n'); + break; + } else if (c == '\\') { + *bp++ = '\\'; + *bp++ = input(); + } else { + if (c == '[') + openclass = 1; + else if (c == ']') + openclass = 0; + *bp++ = c; + } + } + *bp = 0; + if (c == 0) + SYNTAX("non-terminated regular expression %.10s...", buf); + yylval.s = tostring(buf); + unput('/'); + RET(REGEXPR); +} + +/* low-level lexical stuff, sort of inherited from lex */ + +char ebuf[300]; +char *ep = ebuf; +char yysbuf[100]; /* pushback buffer */ +char *yysptr = yysbuf; +FILE *yyin = 0; + +int input(void) /* get next lexical input character */ +{ + int c; + extern char *lexprog; + + if (yysptr > yysbuf) + c = (uschar)*--yysptr; + else if (lexprog != NULL) { /* awk '...' */ + if ((c = (uschar)*lexprog) != 0) + lexprog++; + } else /* awk -f ... */ + c = pgetc(); + if (c == '\n') + lineno++; + else if (c == EOF) + c = 0; + if (ep >= ebuf + sizeof ebuf) + ep = ebuf; + return *ep++ = c; +} + +void unput(int c) /* put lexical character back on input */ +{ + if (c == '\n') + lineno--; + if (yysptr >= yysbuf + sizeof(yysbuf)) + FATAL("pushed back too much: %.20s...", yysbuf); + *yysptr++ = c; + if (--ep < ebuf) + ep = ebuf + sizeof(ebuf) - 1; +} + +void unputstr(const char *s) /* put a string back on input */ +{ + int i; + + for (i = strlen(s)-1; i >= 0; i--) + unput(s[i]); +} diff --git a/awk/lib.c b/awk/lib.c @@ -0,0 +1,738 @@ +/* $OpenBSD: lib.c,v 1.20 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +#define DEBUG +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include "awk.h" +#include "ytab.h" + +FILE *infile = NULL; +char *file = ""; +char *record; +int recsize = RECSIZE; +char *fields; +int fieldssize = RECSIZE; + +Cell **fldtab; /* pointers to Cells */ +char inputFS[100] = " "; + +#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) */ + +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 }; + +void recinit(unsigned int n) +{ + if ( (record = (char *) malloc(n)) == NULL + || (fields = (char *) malloc(n+1)) == NULL + || (fldtab = (Cell **) calloc(nfields+1, sizeof(Cell *))) == NULL + || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL ) + FATAL("out of space for $0 and fields"); + *fldtab[0] = dollar0; + fldtab[0]->sval = record; + fldtab[0]->nval = tostring("0"); + makefields(1, nfields); +} + +void makefields(int n1, int n2) /* create $n1..$n2 inclusive */ +{ + char temp[50]; + int i; + + for (i = n1; i <= n2; i++) { + fldtab[i] = (Cell *) malloc(sizeof (struct Cell)); + if (fldtab[i] == NULL) + FATAL("out of space in makefields %d", i); + *fldtab[i] = dollar1; + snprintf(temp, sizeof temp, "%d", i); + fldtab[i]->nval = tostring(temp); + } +} + +void initgetrec(void) +{ + int i; + char *p; + + for (i = 1; i < *ARGC; i++) { + p = getargv(i); /* find 1st real filename */ + if (p == NULL || *p == '\0') { /* deleted or zapped */ + argno++; + continue; + } + if (!isclvar(p)) { + setsval(lookup("FILENAME", symtab), p); + return; + } + setclvar(p); /* a commandline assignment before filename */ + argno++; + } + infile = stdin; /* no filenames, so use stdin */ +} + +static int firsttime = 1; + +int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */ +{ /* note: cares whether buf == record */ + int c; + char *buf = *pbuf; + uschar saveb0; + int bufsize = *pbufsize, savebufsize = bufsize; + + if (firsttime) { + firsttime = 0; + initgetrec(); + } + dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n", + *RS, *FS, *ARGC, *FILENAME) ); + if (isrecord) { + donefld = 0; + donerec = 1; + } + saveb0 = buf[0]; + buf[0] = 0; + while (argno < *ARGC || infile == stdin) { + dprintf( ("argno=%d, file=|%s|\n", argno, file) ); + if (infile == NULL) { /* have to open a new file */ + file = getargv(argno); + if (file == NULL || *file == '\0') { /* deleted or zapped */ + argno++; + continue; + } + if (isclvar(file)) { /* a var=value arg */ + setclvar(file); + argno++; + continue; + } + *FILENAME = file; + dprintf( ("opening file %s\n", file) ); + if (*file == '-' && *(file+1) == '\0') + infile = stdin; + else if ((infile = fopen(file, "r")) == NULL) + FATAL("can't open file %s", file); + setfval(fnrloc, 0.0); + } + c = readrec(&buf, &bufsize, infile); + if (c != 0 || buf[0] != '\0') { /* normal record */ + if (isrecord) { + if (freeable(fldtab[0])) + xfree(fldtab[0]->sval); + fldtab[0]->sval = buf; /* buf == record */ + fldtab[0]->tval = REC | STR | DONTFREE; + if (is_number(fldtab[0]->sval)) { + fldtab[0]->fval = atof(fldtab[0]->sval); + fldtab[0]->tval |= NUM; + } + } + setfval(nrloc, nrloc->fval+1); + setfval(fnrloc, fnrloc->fval+1); + *pbuf = buf; + *pbufsize = bufsize; + return 1; + } + /* EOF arrived on this file; set up next */ + if (infile != stdin) + fclose(infile); + infile = NULL; + argno++; + } + buf[0] = saveb0; + *pbuf = buf; + *pbufsize = savebufsize; + return 0; /* true end of file */ +} + +void nextfile(void) +{ + if (infile != NULL && infile != stdin) + fclose(infile); + infile = NULL; + argno++; +} + +int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */ +{ + int sep, c; + 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*/ + strlcpy(inputFS, *FS, sizeof inputFS); /* 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); + *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 (!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; +} + +char *getargv(int n) /* get ARGV[n] */ +{ + Cell *x; + char *s, temp[50]; + extern Array *ARGVtab; + + snprintf(temp, sizeof temp, "%d", n); + if (lookup(temp, ARGVtab) == NULL) + return NULL; + x = setsymtab(temp, "", 0.0, STR, ARGVtab); + s = getsval(x); + dprintf( ("getargv(%d) returns |%s|\n", n, s) ); + return s; +} + +void setclvar(char *s) /* set var=value from s */ +{ + char *p; + Cell *q; + + for (p=s; *p != '='; p++) + ; + *p++ = 0; + p = qstring(p, '\0'); + q = setsymtab(s, p, 0.0, STR, symtab); + setsval(q, p); + if (is_number(q->sval)) { + q->fval = atof(q->sval); + q->tval |= NUM; + } + dprintf( ("command line set %s to |%s|\n", s, p) ); +} + + +void fldbld(void) /* create fields from current record */ +{ + /* this relies on having fields[] the same length as $0 */ + /* the fields are all stored in this one array with \0's */ + /* possibly with a final trailing \0 not associated with any field */ + char *r, *fr, sep; + Cell *p; + int i, j, n; + + if (donefld) + return; + if (!isstr(fldtab[0])) + getsval(fldtab[0]); + r = fldtab[0]->sval; + n = strlen(r); + if (n > fieldssize) { + xfree(fields); + if ((fields = (char *) 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 */ + strlcpy(inputFS, *FS, sizeof(inputFS)); + if (strlen(inputFS) > 1) { /* it's a regular expression */ + i = refldbld(r, inputFS); + } else if ((sep = *inputFS) == ' ') { /* default whitespace */ + for (i = 0; ; ) { + while (*r == ' ' || *r == '\t' || *r == '\n') + r++; + if (*r == 0) + break; + i++; + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + fldtab[i]->sval = fr; + fldtab[i]->tval = FLD | STR | DONTFREE; + do + *fr++ = *r++; + while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); + *fr++ = 0; + } + *fr = 0; + } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ + for (i = 0; *r != 0; r++) { + char buf[2]; + i++; + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + buf[0] = *r; + buf[1] = 0; + fldtab[i]->sval = tostring(buf); + fldtab[i]->tval = FLD | STR; + } + *fr = 0; + } else if (*r != 0) { /* if 0, it's a null field */ + /* subtlecase : if length(FS) == 1 && length(RS > 0) + * \n is NOT a field separator (cf awk book 61,84). + * this variable is tested in the inner while loop. + */ + int rtest = '\n'; /* normal case */ + if (strlen(*RS) > 0) + rtest = '\0'; + for (;;) { + i++; + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + fldtab[i]->sval = fr; + fldtab[i]->tval = FLD | STR | DONTFREE; + while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */ + *fr++ = *r++; + *fr++ = 0; + if (*r++ == 0) + break; + } + *fr = 0; + } + if (i > nfields) + 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; + for (j = 1; j <= lastfld; j++) { + p = fldtab[j]; + if(is_number(p->sval)) { + p->fval = atof(p->sval); + p->tval |= NUM; + } + } + setfval(nfloc, (Awkfloat) lastfld); + if (dbg) { + for (j = 0; j <= lastfld; j++) { + p = fldtab[j]; + printf("field %d (%s): |%s|\n", j, p->nval, p->sval); + } + } +} + +void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ +{ /* nvals remain intact */ + Cell *p; + int i; + + for (i = n1; i <= n2; i++) { + p = fldtab[i]; + if (freeable(p)) + xfree(p->sval); + p->sval = ""; + p->tval = FLD | STR | DONTFREE; + } +} + +void newfld(int n) /* add field n after end of existing lastfld */ +{ + if (n > nfields) + growfldtab(n); + cleanfld(lastfld+1, n); + lastfld = n; + setfval(nfloc, (Awkfloat) n); +} + +Cell *fieldadr(int n) /* get nth field */ +{ + if (n < 0) + FATAL("trying to access out of range field %d", n); + if (n > nfields) /* fields after NF are empty */ + growfldtab(n); /* but does not increase NF */ + return(fldtab[n]); +} + +void growfldtab(int n) /* make new fields up to at least $n */ +{ + int nf = 2 * nfields; + size_t s; + + 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); + else /* overflow sizeof int */ + xfree(fldtab); /* make it null */ + if (fldtab == NULL) + FATAL("out of space creating %d fields", nf); + makefields(nfields+1, nf); + nfields = nf; +} + +int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */ +{ + /* this relies on having fields[] the same length as $0 */ + /* the fields are all stored in this one array with \0's */ + char *fr; + int i, tempstat, n; + fa *pfa; + + n = strlen(rec); + if (n > fieldssize) { + xfree(fields); + if ((fields = (char *) malloc(n+1)) == NULL) + FATAL("out of space for fields in refldbld %d", n); + fieldssize = n; + } + fr = fields; + *fr = '\0'; + if (*rec == '\0') + return 0; + pfa = makedfa(fs, 1); + dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) ); + tempstat = pfa->initstat; + for (i = 1; ; i++) { + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + fldtab[i]->tval = FLD | STR | DONTFREE; + fldtab[i]->sval = fr; + dprintf( ("refldbld: i=%d\n", i) ); + if (nematch(pfa, rec)) { + pfa->initstat = 2; /* horrible coupling to b.c */ + dprintf( ("match %s (%d chars)\n", patbeg, patlen) ); + strncpy(fr, rec, patbeg-rec); + fr += patbeg - rec + 1; + *(fr-1) = '\0'; + rec = patbeg + patlen; + } else { + dprintf( ("no match %s\n", rec) ); + strlcpy(fr, rec, fields + fieldssize - fr); + pfa->initstat = tempstat; + break; + } + } + return i; +} + +void recbld(void) /* create $0 from $1..$NF if necessary */ +{ + int i; + char *r, *p; + + if (donerec == 1) + return; + r = record; + for (i = 1; i <= *NF; i++) { + p = getsval(fldtab[i]); + if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) + FATAL("created $0 `%.30s...' too long", record); + while ((*r = *p++) != 0) + r++; + if (i < *NF) { + if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2")) + FATAL("created $0 `%.30s...' too long", record); + for (p = *OFS; (*r = *p++) != 0; ) + r++; + } + } + if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) + FATAL("built giant record `%.30s...'", record); + *r = '\0'; + dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) ); + + if (freeable(fldtab[0])) + xfree(fldtab[0]->sval); + fldtab[0]->tval = REC | STR | DONTFREE; + fldtab[0]->sval = record; + + dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) ); + dprintf( ("recbld = |%s|\n", record) ); + donerec = 1; +} + +int errorflag = 0; + +void yyerror(const char *s) +{ + SYNTAX("%s", s); +} + +void SYNTAX(const char *fmt, ...) +{ + extern char *cmdname, *curfname; + static int been_here = 0; + va_list varg; + + if (been_here++ > 2) + return; + fprintf(stderr, "%s: ", cmdname); + va_start(varg, fmt); + vfprintf(stderr, fmt, varg); + va_end(varg); + fprintf(stderr, " at source line %d", lineno); + if (curfname != NULL) + fprintf(stderr, " in function %s", curfname); + if (compile_time == 1 && cursource() != NULL) + fprintf(stderr, " source file %s", cursource()); + fprintf(stderr, "\n"); + errorflag = 2; + eprint(); +} + +void fpecatch(int sig) +{ + extern Node *curnode; + char buf[1024]; + + snprintf(buf, sizeof buf, "floating point exception\n"); + write(STDERR_FILENO, buf, strlen(buf)); + + if (compile_time != 2 && NR && *NR > 0) { + snprintf(buf, sizeof buf, " input record number %d", (int) (*FNR)); + write(STDERR_FILENO, buf, strlen(buf)); + + if (strcmp(*FILENAME, "-") != 0) { + snprintf(buf, sizeof buf, ", file %s", *FILENAME); + write(STDERR_FILENO, buf, strlen(buf)); + } + write(STDERR_FILENO, "\n", 1); + } + if (compile_time != 2 && curnode) { + snprintf(buf, sizeof buf, " source line number %d", curnode->lineno); + write(STDERR_FILENO, buf, strlen(buf)); + } else if (compile_time != 2 && lineno) { + snprintf(buf, sizeof buf, " source line number %d", lineno); + write(STDERR_FILENO, buf, strlen(buf)); + } + if (compile_time == 1 && cursource() != NULL) { + snprintf(buf, sizeof buf, " source file %s", cursource()); + write(STDERR_FILENO, buf, strlen(buf)); + } + write(STDERR_FILENO, "\n", 1); + if (dbg > 1) /* core dump if serious debugging on */ + abort(); + _exit(1); +} + +extern int bracecnt, brackcnt, parencnt; + +void bracecheck(void) +{ + int c; + static int beenhere = 0; + + if (beenhere++) + return; + while ((c = input()) != EOF && c != '\0') + bclass(c); + bcheck2(bracecnt, '{', '}'); + bcheck2(brackcnt, '[', ']'); + bcheck2(parencnt, '(', ')'); +} + +void bcheck2(int n, int c1, int c2) +{ + if (n == 1) + fprintf(stderr, "\tmissing %c\n", c2); + else if (n > 1) + fprintf(stderr, "\t%d missing %c's\n", n, c2); + else if (n == -1) + fprintf(stderr, "\textra %c\n", c2); + else if (n < -1) + fprintf(stderr, "\t%d extra %c's\n", -n, c2); +} + +void FATAL(const char *fmt, ...) +{ + extern char *cmdname; + va_list varg; + + fflush(stdout); + fprintf(stderr, "%s: ", cmdname); + va_start(varg, fmt); + vfprintf(stderr, fmt, varg); + va_end(varg); + error(); + if (dbg > 1) /* core dump if serious debugging on */ + abort(); + exit(2); +} + +void WARNING(const char *fmt, ...) +{ + extern char *cmdname; + va_list varg; + + fflush(stdout); + fprintf(stderr, "%s: ", cmdname); + va_start(varg, fmt); + vfprintf(stderr, fmt, varg); + va_end(varg); + error(); +} + +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 != 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) + fprintf(stderr, " source file %s", cursource()); + fprintf(stderr, "\n"); + eprint(); +} + +void eprint(void) /* try to print context around error */ +{ + char *p, *q; + int c; + static int been_here = 0; + extern char ebuf[], *ep; + + if (compile_time == 2 || compile_time == 0 || been_here++ > 0) + return; + p = ep - 1; + if (p > ebuf && *p == '\n') + p--; + for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) + ; + while (*p == '\n') + p++; + fprintf(stderr, " context is\n\t"); + for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) + ; + for ( ; p < q; p++) + if (*p) + putc(*p, stderr); + fprintf(stderr, " >>> "); + for ( ; p < ep; p++) + if (*p) + putc(*p, stderr); + fprintf(stderr, " <<< "); + if (*ep) + while ((c = input()) != '\n' && c != '\0' && c != EOF) { + putc(c, stderr); + bclass(c); + } + putc('\n', stderr); + ep = ebuf; +} + +void bclass(int c) +{ + switch (c) { + case '{': bracecnt++; break; + case '}': bracecnt--; break; + case '[': brackcnt++; break; + case ']': brackcnt--; break; + case '(': parencnt++; break; + case ')': parencnt--; break; + } +} + +double errcheck(double x, const char *s) +{ + + if (errno == EDOM) { + errno = 0; + WARNING("%s argument out of domain", s); + x = 1; + } else if (errno == ERANGE) { + errno = 0; + WARNING("%s result out of range", s); + x = 1; + } + return x; +} + +int isclvar(const char *s) /* is s of form var=something ? */ +{ + const char *os = s; + + if (!isalpha((uschar) *s) && *s != '_') + return 0; + for ( ; *s; s++) + if (!(isalnum((uschar) *s) || *s == '_')) + break; + return *s == '=' && s > os && *(s+1) != '='; +} + +/* 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 */ + +#include <math.h> +int is_number(const char *s) +{ + double r; + char *ep; + errno = 0; + r = strtod(s, &ep); + if (ep == s || r == HUGE_VAL || errno == ERANGE) + return 0; + while (*ep == ' ' || *ep == '\t' || *ep == '\n') + ep++; + if (*ep == '\0') + return 1; + else + return 0; +} diff --git a/awk/main.c b/awk/main.c @@ -0,0 +1,212 @@ +/* $OpenBSD: main.c,v 1.17 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +const char *version = "version 20110810"; + +#define DEBUG +#include <stdio.h> +#include <ctype.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include "awk.h" +#include "ytab.h" + +extern char **environ; +extern int nfields; +extern char *__progname; + +int dbg = 0; +Awkfloat srand_seed = 1; +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 */ + +#define MAX_PFILE 20 /* max number of -f's */ + +char *pfile[MAX_PFILE]; /* program filenames from -f's */ +int npfile = 0; /* number of filenames */ +int curpfile = 0; /* current filename */ + +int safe = 0; /* 1 => "safe" mode */ + +int main(int argc, char *argv[]) +{ + const char *fs = NULL; + + setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */ + cmdname = __progname; + if (argc == 1) { + fprintf(stderr, "usage: %s [-safe] [-V] [-d[n]] [-F fs] " + "[-v var=value] [prog | -f progfile]\n\tfile ...\n", + cmdname); + exit(1); + } + signal(SIGFPE, fpecatch); + + yyin = NULL; + symtab = makesymtab(NSYMTAB); + while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') { + if (strcmp(argv[1], "--") == 0) { /* explicit end of args */ + argc--; + argv++; + break; + } + switch (argv[1][1]) { + case 's': + if (strcmp(argv[1], "-safe") == 0) + safe = 1; + 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; + 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') + 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]); + } + break; + case 'd': + dbg = atoi(&argv[1][2]); + if (dbg == 0) + dbg = 1; + printf("awk %s\n", version); + break; + case 'V': /* added for exptools "standard" */ + printf("awk %s\n", version); + exit(0); + break; + default: + WARNING("unknown option %s ignored", argv[1]); + break; + } + argc--; + argv++; + } + /* argv[1] is now the first argument */ + if (npfile == 0) { /* no -f; first argument is program */ + if (argc <= 1) { + if (dbg) + exit(0); + FATAL("no program given"); + } + dprintf( ("program = |%s|\n", argv[1]) ); + lexprog = argv[1]; + argc--; + argv++; + } + recinit(recsize); + syminit(); + compile_time = 1; + 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(); + setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */ + if (fs) + *FS = qstring(fs, '\0'); + dprintf( ("errorflag=%d\n", errorflag) ); + if (errorflag == 0) { + compile_time = 0; + run(winner); + } else + bracecheck(); + return(errorflag); +} + +int pgetc(void) /* get 1 character from awk program */ +{ + int c; + + for (;;) { + if (yyin == NULL) { + if (curpfile >= npfile) + return EOF; + if (strcmp(pfile[curpfile], "-") == 0) + yyin = stdin; + else if ((yyin = fopen(pfile[curpfile], "r")) == NULL) + FATAL("can't open file %s", pfile[curpfile]); + lineno = 1; + } + if ((c = getc(yyin)) != EOF) + return c; + if (yyin != stdin) + fclose(yyin); + yyin = NULL; + curpfile++; + } +} + +char *cursource(void) /* current source file name */ +{ + if (npfile > 0) + return pfile[curpfile]; + else + return NULL; +} diff --git a/awk/mkfile b/awk/mkfile @@ -0,0 +1,27 @@ +BIN = awk +OBJ = ytab.o lex.o b.o main.o parse.o proctab.o tran.o lib.o run.o +INSTALL_BIN = awk +INSTALL_MAN1 = awk.1 +LOCAL_LDFLAGS = -lm +CLEAN_FILES = ytab.c ytab.h proctab.c gen/maketab +DEPS = yacc + +<$mkbuild/mk.default + +ytab:VQ: awkgram.y + echo YACC -d $prereq + $YACC -d $prereq + mv y.tab.c ytab.c + mv y.tab.h ytab.h + +ytab.c: ytab + +ytab.h: ytab + +proctab.c:VQ: gen/maketab + echo MAKETAB proctab.c + gen/maketab ytab.h >proctab.c + +gen/maketab:VQ: ytab.h gen/maketab.c + echo HOSTCC $target + $HOSTCC gen/maketab.c -o $target diff --git a/awk/parse.c b/awk/parse.c @@ -0,0 +1,277 @@ +/* $OpenBSD: parse.c,v 1.6 2002/12/19 21:24:28 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +#define DEBUG +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "awk.h" +#include "ytab.h" + +Node *nodealloc(int n) +{ + Node *x; + + x = (Node *) malloc(sizeof(Node) + (n-1)*sizeof(Node *)); + if (x == NULL) + FATAL("out of space in nodealloc"); + x->nnext = NULL; + x->lineno = lineno; + return(x); +} + +Node *exptostat(Node *a) +{ + a->ntype = NSTAT; + return(a); +} + +Node *node1(int a, Node *b) +{ + Node *x; + + x = nodealloc(1); + x->nobj = a; + x->narg[0]=b; + return(x); +} + +Node *node2(int a, Node *b, Node *c) +{ + Node *x; + + x = nodealloc(2); + x->nobj = a; + x->narg[0] = b; + x->narg[1] = c; + return(x); +} + +Node *node3(int a, Node *b, Node *c, Node *d) +{ + Node *x; + + x = nodealloc(3); + x->nobj = a; + x->narg[0] = b; + x->narg[1] = c; + x->narg[2] = d; + return(x); +} + +Node *node4(int a, Node *b, Node *c, Node *d, Node *e) +{ + Node *x; + + x = nodealloc(4); + x->nobj = a; + x->narg[0] = b; + x->narg[1] = c; + x->narg[2] = d; + x->narg[3] = e; + return(x); +} + +Node *stat1(int a, Node *b) +{ + Node *x; + + x = node1(a,b); + x->ntype = NSTAT; + return(x); +} + +Node *stat2(int a, Node *b, Node *c) +{ + Node *x; + + x = node2(a,b,c); + x->ntype = NSTAT; + return(x); +} + +Node *stat3(int a, Node *b, Node *c, Node *d) +{ + Node *x; + + x = node3(a,b,c,d); + x->ntype = NSTAT; + return(x); +} + +Node *stat4(int a, Node *b, Node *c, Node *d, Node *e) +{ + Node *x; + + x = node4(a,b,c,d,e); + x->ntype = NSTAT; + return(x); +} + +Node *op1(int a, Node *b) +{ + Node *x; + + x = node1(a,b); + x->ntype = NEXPR; + return(x); +} + +Node *op2(int a, Node *b, Node *c) +{ + Node *x; + + x = node2(a,b,c); + x->ntype = NEXPR; + return(x); +} + +Node *op3(int a, Node *b, Node *c, Node *d) +{ + Node *x; + + x = node3(a,b,c,d); + x->ntype = NEXPR; + return(x); +} + +Node *op4(int a, Node *b, Node *c, Node *d, Node *e) +{ + Node *x; + + x = node4(a,b,c,d,e); + x->ntype = NEXPR; + return(x); +} + +Node *celltonode(Cell *a, int b) +{ + Node *x; + + a->ctype = OCELL; + a->csub = b; + x = node1(0, (Node *) a); + x->ntype = NVALUE; + return(x); +} + +Node *rectonode(void) /* make $0 into a Node */ +{ + extern Cell *literal0; + return op1(INDIRECT, celltonode(literal0, CUNK)); +} + +Node *makearr(Node *p) +{ + Cell *cp; + + if (isvalue(p)) { + cp = (Cell *) (p->narg[0]); + if (isfcn(cp)) + SYNTAX( "%s is a function, not an array", cp->nval ); + else if (!isarr(cp)) { + xfree(cp->sval); + cp->sval = (char *) makesymtab(NSYMTAB); + cp->tval = ARR; + } + } + return p; +} + +#define PA2NUM 50 /* max number of pat,pat patterns allowed */ +int paircnt; /* number of them in use */ +int pairstack[PA2NUM]; /* state of each pat,pat */ + +Node *pa2stat(Node *a, Node *b, Node *c) /* pat, pat {...} */ +{ + Node *x; + + x = node4(PASTAT2, a, b, c, itonp(paircnt)); + if (paircnt++ >= PA2NUM) + SYNTAX( "limited to %d pat,pat statements", PA2NUM ); + x->ntype = NSTAT; + return(x); +} + +Node *linkum(Node *a, Node *b) +{ + Node *c; + + if (errorflag) /* don't link things that are wrong */ + return a; + if (a == NULL) + return(b); + else if (b == NULL) + return(a); + for (c = a; c->nnext != NULL; c = c->nnext) + ; + c->nnext = b; + return(a); +} + +void defn(Cell *v, Node *vl, Node *st) /* turn on FCN bit in definition, */ +{ /* body of function, arglist */ + Node *p; + int n; + + if (isarr(v)) { + SYNTAX( "`%s' is an array name and a function name", v->nval ); + return; + } + if (isarg(v->nval) != -1) { + SYNTAX( "`%s' is both function name and argument name", v->nval ); + return; + } + + v->tval = FCN; + v->sval = (char *) st; + n = 0; /* count arguments */ + for (p = vl; p; p = p->nnext) + n++; + v->fval = n; + dprintf( ("defining func %s (%d args)\n", v->nval, n) ); +} + +int isarg(const char *s) /* is s in argument list for current function? */ +{ /* return -1 if not, otherwise arg # */ + extern Node *arglist; + Node *p = arglist; + int n; + + for (n = 0; p != 0; p = p->nnext, n++) + if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0) + return n; + return -1; +} + +int ptoi(void *p) /* convert pointer to integer */ +{ + return (int) (long) p; /* swearing that p fits, of course */ +} + +Node *itonp(int i) /* and vice versa */ +{ + return (Node *) (long) i; +} diff --git a/awk/proto.h b/awk/proto.h @@ -0,0 +1,196 @@ +/* $OpenBSD: proto.h,v 1.9 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +extern int yywrap(void); +extern void setfname(Cell *); +extern int constnode(Node *); +extern char *strnode(Node *); +extern Node *notnull(Node *); +extern int yyparse(void); + +extern int yylex(void); +extern void startreg(void); +extern int input(void); +extern void unput(int); +extern void unputstr(const char *); +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 void penter(Node *); +extern void freetr(Node *); +extern int hexstr(uschar **); +extern int quoted(uschar **); +extern char *cclenter(const char *); +extern void overflo(const char *); +extern void cfoll(fa *, Node *); +extern int first(Node *); +extern void follow(Node *); +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 Node *reparse(const char *); +extern Node *regexp(void); +extern Node *primary(void); +extern Node *concat(Node *); +extern Node *alt(Node *); +extern Node *unary(Node *); +extern int relex(void); +extern int cgoto(fa *, int, int); +extern void freefa(fa *); + +extern int pgetc(void); +extern char *cursource(void); + +extern Node *nodealloc(int); +extern Node *exptostat(Node *); +extern Node *node1(int, Node *); +extern Node *node2(int, Node *, Node *); +extern Node *node3(int, Node *, Node *, Node *); +extern Node *node4(int, Node *, Node *, Node *, Node *); +extern Node *stat3(int, Node *, Node *, Node *); +extern Node *op2(int, Node *, Node *); +extern Node *op1(int, Node *); +extern Node *stat1(int, Node *); +extern Node *op3(int, Node *, Node *, Node *); +extern Node *op4(int, Node *, Node *, Node *, Node *); +extern Node *stat2(int, Node *, Node *); +extern Node *stat4(int, Node *, Node *, Node *, Node *); +extern Node *celltonode(Cell *, int); +extern Node *rectonode(void); +extern Node *makearr(Node *); +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 Cell *(*proctab[])(Node **, int); +extern int ptoi(void *); +extern Node *itonp(int); + +extern void syminit(void); +extern void arginit(int, char **); +extern void envinit(char **); +extern Array *makesymtab(int); +extern void freesymtab(Cell *); +extern void freeelem(Cell *, const char *); +extern Cell *setsymtab(const char *, const char *, double, unsigned int, Array *); +extern int hash(const char *, int); +extern void rehash(Array *); +extern Cell *lookup(const char *, Array *); +extern double setfval(Cell *, double); +extern void funnyvar(Cell *, const char *); +extern char *setsval(Cell *, const char *); +extern double getfval(Cell *); +extern char *getsval(Cell *); +extern char *getpssval(Cell *); /* for print */ +extern char *tostring(const char *); +extern char *qstring(const char *, int); + +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 nextfile(void); +extern int readrec(char **buf, int *bufsize, FILE *inf); +extern char *getargv(int); +extern void setclvar(char *); +extern void fldbld(void); +extern void cleanfld(int, int); +extern void newfld(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 *, ...); +extern void WARNING(const char *, ...); +extern void error(void); +extern void eprint(void); +extern void bclass(int); +extern double errcheck(double, const char *); +extern int isclvar(const char *); +extern int is_number(const char *); + +extern int adjbuf(char **pb, int *sz, int min, int q, char **pbp, const char *what); +extern void run(Node *); +extern Cell *execute(Node *); +extern Cell *program(Node **, int); +extern Cell *call(Node **, int); +extern Cell *copycell(Cell *); +extern Cell *arg(Node **, int); +extern Cell *jump(Node **, int); +extern Cell *awkgetline(Node **, int); +extern Cell *getnf(Node **, int); +extern Cell *array(Node **, int); +extern Cell *awkdelete(Node **, int); +extern Cell *intest(Node **, int); +extern Cell *matchop(Node **, int); +extern Cell *boolop(Node **, int); +extern Cell *relop(Node **, int); +extern void tfree(Cell *); +extern Cell *gettemp(void); +extern Cell *field(Node **, int); +extern Cell *indirect(Node **, int); +extern Cell *substr(Node **, int); +extern Cell *sindex(Node **, int); +extern int format(char **, int *, const char *, Node *); +extern Cell *awksprintf(Node **, int); +extern Cell *awkprintf(Node **, int); +extern Cell *arith(Node **, int); +extern double ipow(double, int); +extern Cell *incrdecr(Node **, int); +extern Cell *assign(Node **, int); +extern Cell *cat(Node **, int); +extern Cell *pastat(Node **, int); +extern Cell *dopa2(Node **, int); +extern Cell *split(Node **, int); +extern Cell *condexpr(Node **, int); +extern Cell *ifstat(Node **, int); +extern Cell *whilestat(Node **, int); +extern Cell *dostat(Node **, int); +extern Cell *forstat(Node **, int); +extern Cell *instat(Node **, int); +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 const char *filename(FILE *); +extern Cell *closefile(Node **, int); +extern void closeall(void); +extern Cell *sub(Node **, int); +extern Cell *gsub(Node **, int); + +extern FILE *popen(const char *, const char *); +extern int pclose(FILE *); diff --git a/awk/run.c b/awk/run.c @@ -0,0 +1,2017 @@ +/* $OpenBSD: run.c,v 1.33 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +#define DEBUG +#include <stdio.h> +#include <ctype.h> +#include <setjmp.h> +#include <limits.h> +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include "awk.h" +#include "ytab.h" + +#define tempfree(x) if (istemp(x)) tfree(x); else + +/* +#undef tempfree + +void tempfree(Cell *p) { + if (p->ctype == OCELL && (p->csub < CUNK || p->csub > CFREE)) { + WARNING("bad csub %d in Cell %d %s", + p->csub, p->ctype, p->sval); + } + if (istemp(p)) + tfree(p); +} +*/ + +/* do we really need these? */ +/* #ifdef _NFILE */ +/* #ifndef FOPEN_MAX */ +/* #define FOPEN_MAX _NFILE */ +/* #endif */ +/* #endif */ +/* */ +/* #ifndef FOPEN_MAX */ +/* #define FOPEN_MAX 40 */ /* max number of open files */ +/* #endif */ +/* */ +/* #ifndef RAND_MAX */ +/* #define RAND_MAX 32767 */ /* all that ansi guarantees */ +/* #endif */ + +jmp_buf env; +extern int pairstack[]; +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 }; +Cell *True = &truecell; +static Cell falsecell ={ OBOOL, BFALSE, 0, 0, 0.0, NUM }; +Cell *False = &falsecell; +static Cell breakcell ={ OJUMP, JBREAK, 0, 0, 0.0, NUM }; +Cell *jbreak = &breakcell; +static Cell contcell ={ OJUMP, JCONT, 0, 0, 0.0, NUM }; +Cell *jcont = &contcell; +static Cell nextcell ={ OJUMP, JNEXT, 0, 0, 0.0, NUM }; +Cell *jnext = &nextcell; +static Cell nextfilecell ={ OJUMP, JNEXTFILE, 0, 0, 0.0, NUM }; +Cell *jnextfile = &nextfilecell; +static Cell exitcell ={ OJUMP, JEXIT, 0, 0, 0.0, NUM }; +Cell *jexit = &exitcell; +static Cell retcell ={ OJUMP, JRET, 0, 0, 0.0, NUM }; +Cell *jret = &retcell; +static Cell tempcell ={ OCELL, CTEMP, 0, "", 0.0, NUM|STR|DONTFREE }; + +Node *curnode = NULL; /* the node being executed, for debugging */ + +void stdinit(void); +void flush_all(void); + +/* buffer memory management */ +int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr, + const char *whatrtn) +/* pbuf: address of pointer to buffer being managed + * psiz: address of buffer size variable + * minlen: minimum length of buffer needed + * quantum: buffer size quantum + * pbptr: address of movable pointer into buffer, or 0 if none + * whatrtn: name of the calling routine if failure should cause fatal error + * + * return 0 for realloc failure, !=0 for success + */ +{ + if (minlen > *psiz) { + char *tbuf; + int rminlen = quantum ? minlen % quantum : 0; + int boff = pbptr ? *pbptr - *pbuf : 0; + /* round up to next multiple of quantum */ + if (rminlen) + minlen += quantum - rminlen; + tbuf = (char *) realloc(*pbuf, minlen); + dprintf( ("adjbuf %s: %d %d (pbuf=%p, tbuf=%p)\n", whatrtn, *psiz, minlen, *pbuf, tbuf) ); + if (tbuf == NULL) { + if (whatrtn) + FATAL("out of memory in %s", whatrtn); + return 0; + } + *pbuf = tbuf; + *psiz = minlen; + if (pbptr) + *pbptr = tbuf + boff; + } + return 1; +} + +void run(Node *a) /* execution of parse tree starts here */ +{ + stdinit(); + execute(a); + closeall(); +} + +Cell *execute(Node *u) /* execute a node of the parse tree */ +{ + Cell *(*proc)(Node **, int); + Cell *x; + Node *a; + + if (u == NULL) + return(True); + for (a = u; ; a = a->nnext) { + curnode = a; + if (isvalue(a)) { + x = (Cell *) (a->narg[0]); + if (isfld(x) && !donefld) + fldbld(); + else if (isrec(x) && !donerec) + recbld(); + return(x); + } + if (notlegal(a->nobj)) /* probably a Cell* but too risky to print */ + FATAL("illegal statement"); + proc = proctab[a->nobj-FIRSTTOKEN]; + x = (*proc)(a->narg, a->nobj); + if (isfld(x) && !donefld) + fldbld(); + else if (isrec(x) && !donerec) + recbld(); + if (isexpr(a)) + return(x); + if (isjump(x)) + return(x); + if (a->nnext == NULL) + return(x); + tempfree(x); + } +} + + +Cell *program(Node **a, int n) /* execute an awk program */ +{ /* a[0] = BEGIN, a[1] = body, a[2] = END */ + Cell *x; + + if (setjmp(env) != 0) + goto ex; + if (a[0]) { /* BEGIN */ + x = execute(a[0]); + if (isexit(x)) + return(True); + if (isjump(x)) + FATAL("illegal break, continue, next or nextfile from BEGIN"); + tempfree(x); + } + if (a[1] || a[2]) + while (getrec(&record, &recsize, 1) > 0) { + x = execute(a[1]); + if (isexit(x)) + break; + tempfree(x); + } + ex: + if (setjmp(env) != 0) /* handles exit within END */ + goto ex1; + if (a[2]) { /* END */ + x = execute(a[2]); + if (isbreak(x) || isnext(x) || iscont(x)) + FATAL("illegal break, continue, next or nextfile from END"); + tempfree(x); + } + ex1: + return(True); +} + +struct Frame { /* stack frame for awk function calls */ + int nargs; /* number of arguments in this call */ + Cell *fcncell; /* pointer to Cell for function */ + Cell **args; /* pointer to array of arguments after execute */ + Cell *retval; /* return value */ +}; + +#define NARGS 50 /* max args in a call */ + +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 */ + +Cell *call(Node **a, int n) /* function call. very kludgy and fragile */ +{ + static Cell newcopycell = { OCELL, CCOPY, 0, "", 0.0, NUM|STR|DONTFREE }; + int i, ncall, ndef; + int freed = 0; /* handles potential double freeing when fcn & param share a tempcell */ + Node *x; + Cell *args[NARGS], *oargs[NARGS]; /* BUG: fixed size arrays */ + Cell *y, *z, *fcn; + char *s; + + fcn = execute(a[0]); /* the function itself */ + s = fcn->nval; + if (!isfcn(fcn)) + FATAL("calling undefined function %s", s); + if (frame == NULL) { + fp = frame = (struct Frame *) calloc(nframe += 100, sizeof(struct 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)) ); + 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)) ); + y = execute(x); + oargs[i] = y; + dprintf( ("args[%d]: %s %f <%s>, t=%o\n", + i, NN(y->nval), y->fval, isarr(y) ? "(array)" : NN(y->sval), y->tval) ); + if (isfcn(y)) + FATAL("can't use function %s as argument in %s", y->nval, s); + if (isarr(y)) + args[i] = y; /* arrays by ref */ + else + args[i] = copycell(y); + tempfree(y); + } + for ( ; i < ndef; i++) { /* add null args for ones not provided */ + 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)); + if (frame == NULL) + FATAL("out of space for stack frames in %s", s); + fp = frame + dfp; + } + fp->fcncell = fcn; + fp->args = args; + fp->nargs = ndef; /* number defined with (excess are locals) */ + fp->retval = gettemp(); + + dprintf( ("start exec of %s, fp=%d\n", s, (int) (fp-frame)) ); + y = execute((Node *)(fcn->sval)); /* execute body */ + dprintf( ("finished exec of %s, fp=%d\n", s, (int) (fp-frame)) ); + + for (i = 0; i < ndef; i++) { + Cell *t = fp->args[i]; + if (isarr(t)) { + if (t->csub == CCOPY) { + if (i >= ncall) { + freesymtab(t); + t->csub = CTEMP; + tempfree(t); + } else { + oargs[i]->tval = t->tval; + oargs[i]->tval &= ~(STR|NUM|DONTFREE); + oargs[i]->sval = t->sval; + tempfree(t); + } + } + } else if (t != y) { /* kludge to prevent freeing twice */ + t->csub = CTEMP; + tempfree(t); + } else if (t == y && t->csub == CCOPY) { + t->csub = CTEMP; + tempfree(t); + freed = 1; + } + } + tempfree(fcn); + if (isexit(y) || isnext(y)) + return y; + if (freed == 0) { + tempfree(y); /* don't free twice! */ + } + z = fp->retval; /* return value */ + dprintf( ("%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval) ); + fp--; + return(z); +} + +Cell *copycell(Cell *x) /* make a copy of a cell in a temp */ +{ + Cell *y; + + y = gettemp(); + y->csub = CCOPY; /* prevents freeing until call is over */ + y->nval = x->nval; /* BUG? */ + if (isstr(x)) + y->sval = tostring(x->sval); + y->fval = x->fval; + y->tval = x->tval & ~(CON|FLD|REC|DONTFREE); /* copy is not constant or field */ + /* is DONTFREE right? */ + return y; +} + +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) + FATAL("argument #%d of function %s was not supplied", + n+1, fp->fcncell->nval); + return fp->args[n]; +} + +Cell *jump(Node **a, int n) /* break, continue, next, nextfile, return */ +{ + Cell *y; + + switch (n) { + case EXIT: + if (a[0] != NULL) { + y = execute(a[0]); + errorflag = (int) getfval(y); + tempfree(y); + } + longjmp(env, 1); + case RETURN: + if (a[0] != NULL) { + y = execute(a[0]); + if ((y->tval & (STR|NUM)) == (STR|NUM)) { + setsval(fp->retval, getsval(y)); + fp->retval->fval = getfval(y); + fp->retval->tval |= NUM; + } + else if (y->tval & STR) + setsval(fp->retval, getsval(y)); + else if (y->tval & NUM) + setfval(fp->retval, getfval(y)); + else /* can't happen */ + FATAL("bad type variable %d", y->tval); + tempfree(y); + } + return(jret); + case NEXT: + return(jnext); + case NEXTFILE: + nextfile(); + return(jnextfile); + case BREAK: + return(jbreak); + case CONTINUE: + return(jcont); + default: /* can't happen */ + FATAL("illegal jump type %d", n); + } + return 0; /* not reached */ +} + +Cell *awkgetline(Node **a, int n) /* get next line from specific input */ +{ /* a[0] is variable, a[1] is operator, a[2] is filename */ + Cell *r, *x; + extern Cell **fldtab; + FILE *fp; + char *buf; + int bufsize = recsize; + int mode; + + if ((buf = (char *) malloc(bufsize)) == NULL) + FATAL("out of memory in getline"); + + fflush(stdout); /* in case someone is waiting for a prompt */ + r = gettemp(); + if (a[1] != NULL) { /* getline < file */ + x = execute(a[2]); /* filename */ + mode = ptoi(a[1]); + if (mode == '|') /* input pipe */ + mode = LE; /* arbitrary flag */ + fp = openfile(mode, getsval(x)); + tempfree(x); + if (fp == NULL) + n = -1; + else + n = readrec(&buf, &bufsize, fp); + if (n <= 0) { + ; + } else if (a[0] != NULL) { /* getline var <file */ + x = execute(a[0]); + setsval(x, buf); + tempfree(x); + } else { /* getline <file */ + setsval(fldtab[0], buf); + if (is_number(fldtab[0]->sval)) { + fldtab[0]->fval = atof(fldtab[0]->sval); + fldtab[0]->tval |= NUM; + } + } + } else { /* bare getline; use current input */ + if (a[0] == NULL) /* getline */ + n = getrec(&record, &recsize, 1); + else { /* getline var */ + n = getrec(&buf, &bufsize, 0); + x = execute(a[0]); + setsval(x, buf); + tempfree(x); + } + } + setfval(r, (Awkfloat) n); + free(buf); + return r; +} + +Cell *getnf(Node **a, int n) /* get NF */ +{ + if (donefld == 0) + fldbld(); + return (Cell *) a[0]; +} + +Cell *array(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */ +{ + Cell *x, *y, *z; + char *s; + Node *np; + char *buf; + int bufsz = recsize; + int nsub = strlen(*SUBSEP); + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in array"); + + 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); + strlcat(buf, s, bufsz); + if (np->nnext) + strlcat(buf, *SUBSEP, bufsz); + tempfree(y); + } + if (!isarr(x)) { + dprintf( ("making %s into an array\n", NN(x->nval)) ); + if (freeable(x)) + xfree(x->sval); + x->tval &= ~(STR|NUM|DONTFREE); + x->tval |= ARR; + x->sval = (char *) makesymtab(NSYMTAB); + } + z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval); + z->ctype = OCELL; + z->csub = CVAR; + tempfree(x); + free(buf); + return(z); +} + +Cell *awkdelete(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */ +{ + Cell *x, *y; + Node *np; + char *s; + int nsub = strlen(*SUBSEP); + + x = execute(a[0]); /* Cell* for symbol table */ + if (!isarr(x)) + return True; + if (a[1] == 0) { /* 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); + strlcat(buf, s, bufsz); + if (np->nnext) + strlcat(buf, *SUBSEP, bufsz); + tempfree(y); + } + freeelem(x, buf); + free(buf); + } + tempfree(x); + return True; +} + +Cell *intest(Node **a, int n) /* a[0] is index (list), a[1] is symtab */ +{ + Cell *x, *ap, *k; + Node *p; + char *buf; + char *s; + int bufsz = recsize; + int nsub = strlen(*SUBSEP); + + ap = execute(a[1]); /* array name */ + if (!isarr(ap)) { + dprintf( ("making %s into an array\n", ap->nval) ); + if (freeable(ap)) + xfree(ap->sval); + ap->tval &= ~(STR|NUM|DONTFREE); + ap->tval |= ARR; + ap->sval = (char *) makesymtab(NSYMTAB); + } + 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); + strlcat(buf, s, bufsz); + tempfree(x); + if (p->nnext) + strlcat(buf, *SUBSEP, bufsz); + } + k = lookup(buf, (Array *) ap->sval); + tempfree(ap); + free(buf); + if (k == NULL) + return(False); + else + return(True); +} + + +Cell *matchop(Node **a, int n) /* ~ and match() */ +{ + Cell *x, *y; + char *s, *t; + int i; + fa *pfa; + int (*mf)(fa *, const char *) = match, mode = 0; + + if (n == MATCHFCN) { + mf = pmatch; + mode = 1; + } + x = execute(a[1]); /* a[1] = target text */ + s = getsval(x); + if (a[0] == 0) /* a[1] == 0: already-compiled reg expr */ + i = (*mf)((fa *) a[2], s); + else { + y = execute(a[2]); /* a[2] = regular expr */ + t = getsval(y); + pfa = makedfa(t, mode); + i = (*mf)(pfa, s); + tempfree(y); + } + tempfree(x); + if (n == MATCHFCN) { + int start = patbeg - s + 1; + if (patlen < 0) + start = 0; + setfval(rstartloc, (Awkfloat) start); + setfval(rlengthloc, (Awkfloat) patlen); + x = gettemp(); + x->tval = NUM; + x->fval = start; + return x; + } else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0)) + return(True); + else + return(False); +} + + +Cell *boolop(Node **a, int n) /* a[0] || a[1], a[0] && a[1], !a[0] */ +{ + Cell *x, *y; + int i; + + x = execute(a[0]); + i = istrue(x); + tempfree(x); + switch (n) { + case BOR: + if (i) return(True); + y = execute(a[1]); + i = istrue(y); + tempfree(y); + if (i) return(True); + else return(False); + case AND: + if ( !i ) return(False); + y = execute(a[1]); + i = istrue(y); + tempfree(y); + if (i) return(True); + else return(False); + case NOT: + if (i) return(False); + else return(True); + default: /* can't happen */ + FATAL("unknown boolean operator %d", n); + } + return 0; /*NOTREACHED*/ +} + +Cell *relop(Node **a, int n) /* a[0 < a[1], etc. */ +{ + int i; + Cell *x, *y; + Awkfloat j; + + x = execute(a[0]); + y = execute(a[1]); + if (x->tval&NUM && y->tval&NUM) { + j = x->fval - y->fval; + i = j<0? -1: (j>0? 1: 0); + } else { + i = strcmp(getsval(x), getsval(y)); + } + tempfree(x); + tempfree(y); + switch (n) { + case LT: if (i<0) return(True); + else return(False); + case LE: if (i<=0) return(True); + else return(False); + case NE: if (i!=0) return(True); + else return(False); + case EQ: if (i == 0) return(True); + else return(False); + case GE: if (i>=0) return(True); + else return(False); + case GT: if (i>0) return(True); + else return(False); + default: /* can't happen */ + FATAL("unknown relational operator %d", n); + } + return 0; /*NOTREACHED*/ +} + +void tfree(Cell *a) /* free a tempcell */ +{ + if (freeable(a)) { + dprintf( ("freeing %s %s %o\n", NN(a->nval), NN(a->sval), a->tval) ); + xfree(a->sval); + } + if (a == tmps) + FATAL("tempcell list is curdled"); + a->cnext = tmps; + tmps = a; +} + +Cell *gettemp(void) /* get a tempcell */ +{ int i; + Cell *x; + + if (!tmps) { + tmps = (Cell *) calloc(100, sizeof(Cell)); + if (!tmps) + FATAL("out of space for temporaries"); + for(i = 1; i < 100; i++) + tmps[i-1].cnext = &tmps[i]; + tmps[i-1].cnext = 0; + } + x = tmps; + tmps = x->cnext; + *x = tempcell; + return(x); +} + +Cell *indirect(Node **a, int n) /* $( a[0] ) */ +{ + Awkfloat val; + Cell *x; + int m; + char *s; + + x = execute(a[0]); + val = getfval(x); /* freebsd: defend against super large field numbers */ + if ((Awkfloat)INT_MAX < val) + FATAL("trying to access out of range field %s", x->nval); + m = (int) val; + if (m == 0 && !is_number(s = getsval(x))) /* suspicion! */ + FATAL("illegal field $(%s), name \"%s\"", s, x->nval); + /* BUG: can x->nval ever be null??? */ + tempfree(x); + x = fieldadr(m); + x->ctype = OCELL; /* BUG? why are these needed? */ + x->csub = CFLD; + return(x); +} + +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; + + x = execute(a[0]); + y = execute(a[1]); + if (a[2] != 0) + z = execute(a[2]); + s = getsval(x); + k = strlen(s) + 1; + if (k <= 1) { + tempfree(x); + tempfree(y); + if (a[2] != 0) { + tempfree(z); + } + x = gettemp(); + setsval(x, ""); + return(x); + } + m = (int) getfval(y); + if (m <= 0) + m = 1; + else if (m > k) + m = k; + tempfree(y); + if (a[2] != 0) { + n = (int) getfval(z); + tempfree(z); + } else + n = k - 1; + if (n < 0) + n = 0; + else if (n > k - m) + n = k - m; + dprintf( ("substr: m=%d, n=%d, s=%s\n", m, n, s) ); + y = gettemp(); + temp = s[n+m-1]; /* with thanks to John Linderman */ + s[n+m-1] = '\0'; + setsval(y, s + m - 1); + s[n+m-1] = temp; + tempfree(x); + return(y); +} + +Cell *sindex(Node **a, int nnn) /* index(a[0], a[1]) */ +{ + Cell *x, *y, *z; + char *s1, *s2, *p1, *p2, *q; + Awkfloat v = 0.0; + + x = execute(a[0]); + s1 = getsval(x); + y = execute(a[1]); + s2 = getsval(y); + + z = gettemp(); + for (p1 = s1; *p1 != '\0'; p1++) { + for (q=p1, p2=s2; *p2 != '\0' && *q == *p2; q++, p2++) + ; + if (*p2 == '\0') { + v = (Awkfloat) (p1 - s1 + 1); /* origin 1 */ + break; + } + } + tempfree(x); + tempfree(y); + setfval(z, v); + return(z); +} + +#define MAXNUMSIZE 50 + +int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like conversions */ +{ + char *fmt; + char *p, *t; + const char *os; + Cell *x; + int flag = 0, n; + int fmtwd; /* format width */ + int fmtsz = recsize; + char *buf = *pbuf; + int bufsize = *pbufsize; + + os = s; + p = buf; + if ((fmt = (char *) malloc(fmtsz)) == NULL) + FATAL("out of memory in format()"); + while (*s) { + adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format1"); + if (*s != '%') { + *p++ = *s++; + continue; + } + if (*(s+1) == '%') { + *p++ = '%'; + s += 2; + continue; + } + /* have to be real careful in case this is a huge number, eg, %100000d */ + fmtwd = atoi(s+1); + if (fmtwd < 0) + fmtwd = -fmtwd; + adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format2"); + for (t = fmt; (*t++ = *s) != '\0'; s++) { + if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, "format3")) + FATAL("format item %.30s... ran format() out of memory", os); + if (isalpha((uschar)*s) && *s != 'l' && *s != 'h' && *s != 'L') + break; /* the ansi panoply */ + if (*s == '*') { + if (a == NULL) + FATAL("not enough args in printf(%s)", os); + x = execute(a); + a = a->nnext; + snprintf(t-1, fmt + fmtsz - (t-1), "%d", fmtwd=(int) getfval(x)); + if (fmtwd < 0) + fmtwd = -fmtwd; + adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format"); + t = fmt + strlen(fmt); + tempfree(x); + } + } + *t = '\0'; + if (fmtwd < 0) + fmtwd = -fmtwd; + adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format4"); + + switch (*s) { + case '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'; + *++t = '\0'; + break; + case 'o': case 'x': case 'X': case 'u': + flag = *(s-1) == 'l' ? 'd' : 'u'; + break; + case 's': + flag = 's'; + break; + case 'c': + flag = 'c'; + break; + default: + WARNING("weird printf conversion %s", fmt); + flag = '?'; + break; + } + if (a == NULL) + FATAL("not enough args in printf(%s)", os); + x = execute(a); + a = a->nnext; + n = MAXNUMSIZE; + if (fmtwd > n) + n = fmtwd; + adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format5"); + switch (flag) { + case '?': /* unknown, so dump it too */ + snprintf(p, buf + bufsize - p, "%s", fmt); + t = getsval(x); + n = strlen(t); + if (fmtwd > n) + n = fmtwd; + adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format6"); + p += strlen(p); + snprintf(p, buf + bufsize - p, "%s", t); + break; + case 'f': snprintf(p, buf + bufsize - p, fmt, getfval(x)); break; + case 'd': snprintf(p, buf + bufsize - p, fmt, (long) getfval(x)); break; + case 'u': snprintf(p, buf + bufsize - p, fmt, (int) getfval(x)); break; + case 's': + t = getsval(x); + n = strlen(t); + if (fmtwd > n) + n = fmtwd; + if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format7")) + FATAL("huge string/format (%d chars) in printf %.30s... ran format() out of memory", n, t); + snprintf(p, buf + bufsize - p, fmt, t); + break; + case 'c': + if (isnum(x)) { + if (getfval(x)) + snprintf(p, buf + bufsize - p, fmt, (int) getfval(x)); + else { + *p++ = '\0'; /* explicit null byte */ + *p = '\0'; /* next output will start here */ + } + } else + snprintf(p, buf + bufsize - p, fmt, getsval(x)[0]); + break; + default: + FATAL("can't happen: bad conversion %c in format()", flag); + } + tempfree(x); + p += strlen(p); + s++; + } + *p = '\0'; + free(fmt); + for ( ; a; a = a->nnext) /* evaluate any remaining args */ + execute(a); + *pbuf = buf; + *pbufsize = bufsize; + return p - buf; +} + +Cell *awksprintf(Node **a, int n) /* sprintf(a[0]) */ +{ + Cell *x; + Node *y; + char *buf; + int bufsz=3*recsize; + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in awksprintf"); + y = a[0]->nnext; + x = execute(a[0]); + if (format(&buf, &bufsz, getsval(x), y) == -1) + FATAL("sprintf string %.30s... too long. can't happen.", buf); + tempfree(x); + x = gettemp(); + x->sval = buf; + x->tval = STR; + return(x); +} + +Cell *awkprintf(Node **a, int n) /* printf */ +{ /* a[0] is list of args, starting with format string */ + /* a[1] is redirection operator, a[2] is redirection file */ + FILE *fp; + Cell *x; + Node *y; + char *buf; + int len; + int bufsz=3*recsize; + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in awkprintf"); + y = a[0]->nnext; + x = execute(a[0]); + if ((len = format(&buf, &bufsz, getsval(x), y)) == -1) + FATAL("printf string %.30s... too long. can't happen.", buf); + tempfree(x); + if (a[1] == NULL) { + /* fputs(buf, stdout); */ + fwrite(buf, len, 1, stdout); + if (ferror(stdout)) + FATAL("write error on stdout"); + } else { + fp = redirect(ptoi(a[1]), a[2]); + /* fputs(buf, fp); */ + fwrite(buf, len, 1, fp); + fflush(fp); + if (ferror(fp)) + FATAL("write error on %s", filename(fp)); + } + free(buf); + return(True); +} + +Cell *arith(Node **a, int n) /* a[0] + a[1], etc. also -a[0] */ +{ + Awkfloat i, j = 0; + double v; + Cell *x, *y, *z; + + x = execute(a[0]); + i = getfval(x); + tempfree(x); + if (n != UMINUS) { + y = execute(a[1]); + j = getfval(y); + tempfree(y); + } + z = gettemp(); + switch (n) { + case ADD: + i += j; + break; + case MINUS: + i -= j; + break; + case MULT: + i *= j; + break; + case DIVIDE: + if (j == 0) + FATAL("division by zero"); + i /= j; + break; + case MOD: + if (j == 0) + FATAL("division by zero in mod"); + modf(i/j, &v); + i = i - j * v; + break; + case UMINUS: + i = -i; + break; + case POWER: + if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */ + i = ipow(i, (int) j); + else + i = errcheck(pow(i, j), "pow"); + break; + default: /* can't happen */ + FATAL("illegal arithmetic operator %d", n); + } + setfval(z, i); + return(z); +} + +double ipow(double x, int n) /* x**n. ought to be done by pow, but isn't always */ +{ + double v; + + if (n <= 0) + return 1; + v = ipow(x, n/2); + if (n % 2 == 0) + return v * v; + else + return x * v * v; +} + +Cell *incrdecr(Node **a, int n) /* a[0]++, etc. */ +{ + Cell *x, *z; + int k; + Awkfloat xf; + + x = execute(a[0]); + xf = getfval(x); + k = (n == PREINCR || n == POSTINCR) ? 1 : -1; + if (n == PREINCR || n == PREDECR) { + setfval(x, xf + k); + return(x); + } + z = gettemp(); + setfval(z, xf); + setfval(x, xf + k); + tempfree(x); + return(z); +} + +Cell *assign(Node **a, int n) /* a[0] = a[1], a[0] += a[1], etc. */ +{ /* this is subtle; don't muck with it. */ + Cell *x, *y; + Awkfloat xf, yf; + double v; + + 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 */ + else if ((y->tval & (STR|NUM)) == (STR|NUM)) { + setsval(x, getsval(y)); + x->fval = getfval(y); + x->tval |= NUM; + } + else if (isstr(y)) + setsval(x, getsval(y)); + else if (isnum(y)) + setfval(x, getfval(y)); + else + funnyvar(y, "read value of"); + tempfree(y); + return(x); + } + xf = getfval(x); + yf = getfval(y); + switch (n) { + case ADDEQ: + xf += yf; + break; + case SUBEQ: + xf -= yf; + break; + case MULTEQ: + xf *= yf; + break; + case DIVEQ: + if (yf == 0) + FATAL("division by zero in /="); + xf /= yf; + break; + case MODEQ: + if (yf == 0) + FATAL("division by zero in %%="); + modf(xf/yf, &v); + xf = xf - yf * v; + break; + case POWEQ: + if (yf >= 0 && modf(yf, &v) == 0.0) /* pos integer exponent */ + xf = ipow(xf, (int) yf); + else + xf = errcheck(pow(xf, yf), "pow"); + break; + default: + FATAL("illegal assignment operator %d", n); + break; + } + tempfree(y); + setfval(x, xf); + return(x); +} + +Cell *cat(Node **a, int q) /* a[0] cat a[1] */ +{ + Cell *x, *y, *z; + int n1, n2; + char *s; + size_t len; + + x = execute(a[0]); + y = execute(a[1]); + getsval(x); + getsval(y); + n1 = strlen(x->sval); + n2 = strlen(y->sval); + len = n1 + n2 + 1; + s = (char *) malloc(len); + if (s == NULL) + FATAL("out of space concatenating %.15s... and %.15s...", + x->sval, y->sval); + strlcpy(s, x->sval, len); + strlcpy(s+n1, y->sval, len - n1); + tempfree(x); + tempfree(y); + z = gettemp(); + z->sval = s; + z->tval = STR; + return(z); +} + +Cell *pastat(Node **a, int n) /* a[0] { a[1] } */ +{ + Cell *x; + + if (a[0] == 0) + x = execute(a[1]); + else { + x = execute(a[0]); + if (istrue(x)) { + tempfree(x); + x = execute(a[1]); + } + } + return x; +} + +Cell *dopa2(Node **a, int n) /* a[0], a[1] { a[2] } */ +{ + Cell *x; + int pair; + + pair = ptoi(a[3]); + if (pairstack[pair] == 0) { + x = execute(a[0]); + if (istrue(x)) + pairstack[pair] = 1; + tempfree(x); + } + if (pairstack[pair] == 1) { + x = execute(a[1]); + if (istrue(x)) + pairstack[pair] = 0; + tempfree(x); + x = execute(a[2]); + return(x); + } + return(False); +} + +Cell *split(Node **a, int nnn) /* split(a[0], a[1], a[2]); a[3] is type */ +{ + Cell *x = 0, *y, *ap; + char *s; + int sep; + char *t, temp, num[50], *fs = 0; + int n, tempstat, arg3type; + + y = execute(a[0]); /* source string */ + s = getsval(y); + arg3type = ptoi(a[3]); + if (a[2] == 0) /* fs string */ + fs = *FS; + else if (arg3type == STRING) { /* split(str,arr,"string") */ + x = execute(a[2]); + fs = getsval(x); + } else if (arg3type == REGEXPR) + fs = "(regexpr)"; /* split(str,arr,/regexpr/) */ + else + FATAL("illegal type of split"); + sep = *fs; + ap = execute(a[1]); /* array name */ + freesymtab(ap); + dprintf( ("split: s=|%s|, a=%s, sep=|%s|\n", s, NN(ap->nval), fs) ); + ap->tval &= ~STR; + ap->tval |= ARR; + ap->sval = (char *) makesymtab(NSYMTAB); + + n = 0; + if (arg3type == REGEXPR && strlen((char*)((fa*)a[2])->restr) == 0) { + /* split(s, a, //); have to arrange that it looks like empty sep */ + arg3type = 0; + fs = ""; + sep = 0; + } + if (*s != '\0' && (strlen(fs) > 1 || arg3type == REGEXPR)) { /* reg expr */ + fa *pfa; + if (arg3type == REGEXPR) { /* it's ready already */ + pfa = (fa *) a[2]; + } else { + pfa = makedfa(fs, 1); + } + if (nematch(pfa,s)) { + tempstat = pfa->initstat; + pfa->initstat = 2; + do { + n++; + snprintf(num, sizeof num, "%d", n); + temp = *patbeg; + *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; + s = patbeg + patlen; + if (*(patbeg+patlen-1) == 0 || *s == 0) { + n++; + snprintf(num, sizeof num, "%d", n); + setsymtab(num, "", 0.0, STR, (Array *) ap->sval); + pfa->initstat = tempstat; + goto spdone; + } + } while (nematch(pfa,s)); + pfa->initstat = tempstat; /* bwk: has to be here to reset */ + /* cf gsub and refldbld */ + } + n++; + snprintf(num, sizeof num, "%d", n); + if (is_number(s)) + setsymtab(num, s, atof(s), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, s, 0.0, STR, (Array *) ap->sval); + spdone: + pfa = NULL; + } else if (sep == ' ') { + for (n = 0; ; ) { + while (*s == ' ' || *s == '\t' || *s == '\n') + s++; + if (*s == 0) + break; + n++; + t = s; + do + s++; + while (*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0'); + temp = *s; + *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) + s++; + } + } else if (sep == 0) { /* new: split(s, a, "") => 1 char/elem */ + for (n = 0; *s != 0; s++) { + char buf[2]; + n++; + snprintf(num, sizeof num, "%d", n); + buf[0] = *s; + 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) { + for (;;) { + n++; + t = s; + while (*s != sep && *s != '\n' && *s != '\0') + s++; + temp = *s; + *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) + break; + } + } + tempfree(ap); + tempfree(y); + if (a[2] != 0 && arg3type == STRING) { + tempfree(x); + } + x = gettemp(); + x->tval = NUM; + x->fval = n; + return(x); +} + +Cell *condexpr(Node **a, int n) /* a[0] ? a[1] : a[2] */ +{ + Cell *x; + + x = execute(a[0]); + if (istrue(x)) { + tempfree(x); + x = execute(a[1]); + } else { + tempfree(x); + x = execute(a[2]); + } + return(x); +} + +Cell *ifstat(Node **a, int n) /* if (a[0]) a[1]; else a[2] */ +{ + Cell *x; + + x = execute(a[0]); + if (istrue(x)) { + tempfree(x); + x = execute(a[1]); + } else if (a[2] != 0) { + tempfree(x); + x = execute(a[2]); + } + return(x); +} + +Cell *whilestat(Node **a, int n) /* while (a[0]) a[1] */ +{ + Cell *x; + + for (;;) { + x = execute(a[0]); + if (!istrue(x)) + return(x); + tempfree(x); + x = execute(a[1]); + if (isbreak(x)) { + x = True; + return(x); + } + if (isnext(x) || isexit(x) || isret(x)) + return(x); + tempfree(x); + } +} + +Cell *dostat(Node **a, int n) /* do a[0]; while(a[1]) */ +{ + Cell *x; + + for (;;) { + x = execute(a[0]); + if (isbreak(x)) + return True; + if (isnext(x) || isexit(x) || isret(x)) + return(x); + tempfree(x); + x = execute(a[1]); + if (!istrue(x)) + return(x); + tempfree(x); + } +} + +Cell *forstat(Node **a, int n) /* for (a[0]; a[1]; a[2]) a[3] */ +{ + Cell *x; + + x = execute(a[0]); + tempfree(x); + for (;;) { + if (a[1]!=0) { + x = execute(a[1]); + if (!istrue(x)) return(x); + else tempfree(x); + } + x = execute(a[3]); + if (isbreak(x)) /* turn off break */ + return True; + if (isnext(x) || isexit(x) || isret(x)) + return(x); + tempfree(x); + x = execute(a[2]); + tempfree(x); + } +} + +Cell *instat(Node **a, int n) /* for (a[0] in a[1]) a[2] */ +{ + Cell *x, *vp, *arrayp, *cp, *ncp; + Array *tp; + int i; + + vp = execute(a[0]); + arrayp = execute(a[1]); + if (!isarr(arrayp)) { + return True; + } + tp = (Array *) arrayp->sval; + tempfree(arrayp); + for (i = 0; i < tp->size; i++) { /* this routine knows too much */ + for (cp = tp->tab[i]; cp != NULL; cp = ncp) { + setsval(vp, cp->nval); + ncp = cp->cnext; + x = execute(a[2]); + if (isbreak(x)) { + tempfree(vp); + return True; + } + if (isnext(x) || isexit(x) || isret(x)) { + tempfree(vp); + return(x); + } + tempfree(x); + } + } + return True; +} + +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; + Node *nextarg; + FILE *fp; + + t = ptoi(a[0]); + x = execute(a[1]); + nextarg = a[1]->nnext; + switch (t) { + case FLENGTH: + if (isarr(x)) + u = ((Array *) x->sval)->nelem; /* GROT. should be function*/ + else + u = strlen(getsval(x)); + break; + case FLOG: + u = errcheck(log(getfval(x)), "log"); break; + case FINT: + modf(getfval(x), &u); break; + case FEXP: + u = errcheck(exp(getfval(x)), "exp"); break; + case FSQRT: + 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) { + WARNING("atan2 requires two arguments; returning 1.0"); + u = 1.0; + } else { + y = execute(a[1]->nnext); + u = atan2(getfval(x), getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + } + break; + case FCOMPL: + u = ~((int)getfval(x)); + break; + case FAND: + if (nextarg == 0) { + WARNING("and requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) & ((int)getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + break; + case FFOR: + if (nextarg == 0) { + WARNING("or requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) | ((int)getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + break; + case FXOR: + if (nextarg == 0) { + WARNING("or requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) ^ ((int)getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + break; + case FLSHIFT: + if (nextarg == 0) { + WARNING("or requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) << ((int)getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + break; + case FRSHIFT: + if (nextarg == 0) { + WARNING("or requires two arguments; returning 0"); + u = 0; + break; + } + y = execute(a[1]->nnext); + u = ((int)getfval(x)) >> ((int)getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + break; + case FSYSTEM: + fflush(stdout); /* in case something is buffered already */ + u = (Awkfloat) system(getsval(x)) / 256; /* 256 is unix-dep */ + break; + case FRAND: + u = (Awkfloat) (random() % RAND_MAX) / RAND_MAX; + break; + case FSRAND: + u = getfval(x); + tmp = u; + srandom((unsigned int) 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); + } + tempfree(x); + x = gettemp(); + setsval(x, buf); + free(buf); + return x; + case FFLUSH: + if (isrec(x) || strlen(getsval(x)) == 0) { + flush_all(); /* fflush() or fflush("") -> all */ + u = 0; + } else if ((fp = openfile(FFLUSH, getsval(x))) == NULL) + u = EOF; + else + u = fflush(fp); + break; + default: /* can't happen */ + FATAL("illegal function type %d", t); + break; + } + tempfree(x); + x = gettemp(); + setfval(x, u); + if (nextarg != 0) { + WARNING("warning: function has too many arguments"); + for ( ; nextarg; nextarg = nextarg->nnext) + execute(nextarg); + } + return(x); +} + +Cell *printstat(Node **a, int n) /* print a[0] */ +{ + Node *x; + Cell *y; + FILE *fp; + + if (a[1] == 0) /* a[1] is redirection operator, a[2] is file */ + fp = stdout; + else + fp = redirect(ptoi(a[1]), a[2]); + for (x = a[0]; x != NULL; x = x->nnext) { + y = execute(x); + fputs(getpssval(y), fp); + tempfree(y); + if (x->nnext == NULL) + fputs(*ORS, fp); + else + fputs(*OFS, fp); + } + if (a[1] != 0) + fflush(fp); + if (ferror(fp)) + FATAL("write error on %s", filename(fp)); + return(True); +} + +Cell *nullproc(Node **a, int n) +{ + n = n; + a = a; + return 0; +} + + +FILE *redirect(int a, Node *b) /* set up all i/o redirections */ +{ + FILE *fp; + Cell *x; + char *fname; + + x = execute(b); + fname = getsval(x); + fp = openfile(a, fname); + if (fp == NULL) + FATAL("can't open file %s", fname); + tempfree(x); + return fp; +} + +struct files { + FILE *fp; + const char *fname; + int mode; /* '|', 'a', 'w' => LE/LT, GT */ +} *files; + +int nfiles; + +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); + files[0].fp = stdin; + files[0].fname = "/dev/stdin"; + files[0].mode = LT; + files[1].fp = stdout; + files[1].fname = "/dev/stdout"; + files[1].mode = GT; + files[2].fp = stderr; + files[2].fname = "/dev/stderr"; + files[2].mode = GT; +} + +FILE *openfile(int a, const char *us) +{ + const char *s = us; + int i, m; + FILE *fp = 0; + + 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; + } + 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) + break; + if (i >= nfiles) { + struct files *nf; + int nnf = nfiles + FOPEN_MAX; + nf = realloc(files, nnf * sizeof(*nf)); + if (nf == NULL) + FATAL("cannot grow files for %s and %d files", s, nnf); + memset(&nf[nfiles], 0, FOPEN_MAX * sizeof(*nf)); + nfiles = nnf; + files = nf; + } + fflush(stdout); /* force a semblance of order */ + m = a; + if (a == GT) { + fp = fopen(s, "w"); + } else if (a == APPEND) { + fp = fopen(s, "a"); + m = GT; /* so can mix > and >> */ + } else if (a == '|') { /* output pipe */ + fp = popen(s, "w"); + } else if (a == LE) { /* input pipe */ + fp = popen(s, "r"); + } else if (a == LT) { /* getline <file */ + fp = strcmp(s, "-") == 0 ? stdin : fopen(s, "r"); /* "-" is stdin */ + } else /* can't happen */ + FATAL("illegal redirection %d", a); + if (fp != NULL) { + files[i].fname = tostring(s); + files[i].fp = fp; + files[i].mode = m; + } + return fp; +} + +const char *filename(FILE *fp) +{ + int i; + + for (i = 0; i < nfiles; i++) + if (fp == files[i].fp) + return files[i].fname; + return "???"; +} + +Cell *closefile(Node **a, int n) +{ + Cell *x; + 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); +} + +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 ); + } + } +} + +void flush_all(void) +{ + int i; + + for (i = 0; i < nfiles; i++) + if (files[i].fp) + fflush(files[i].fp); +} + +void backsub(char **pb_ptr, char **sptr_ptr); + +Cell *sub(Node **a, int nnn) /* substitute command */ +{ + char *sptr, *pb, *q; + Cell *x, *y, *result; + char *t, *buf; + fa *pfa; + int bufsz = recsize; + + if ((buf = (char *) 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 */ + pfa = (fa *) a[1]; /* regular expression */ + else { + y = execute(a[1]); + pfa = makedfa(getsval(y), 1); + tempfree(y); + } + y = execute(a[2]); /* replacement string */ + result = False; + if (pmatch(pfa, t)) { + sptr = t; + adjbuf(&buf, &bufsz, 1+patbeg-sptr, recsize, 0, "sub"); + pb = buf; + while (sptr < patbeg) + *pb++ = *sptr++; + sptr = getsval(y); + while (*sptr != 0) { + adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "sub"); + if (*sptr == '\\') { + backsub(&pb, &sptr); + } else if (*sptr == '&') { + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "sub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + *pb = '\0'; + if (pb > buf + bufsz) + FATAL("sub result1 %.30s too big; can't happen", buf); + 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) + ; + } + 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; + } + tempfree(x); + tempfree(y); + free(buf); + return result; +} + +Cell *gsub(Node **a, int nnn) /* global substitute */ +{ + Cell *x, *y; + char *rptr, *sptr, *t, *pb, *q; + char *buf; + fa *pfa; + int mflag, tempstat, num; + int bufsz = recsize; + + if ((buf = (char *) 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 */ + pfa = (fa *) a[1]; /* regular expression */ + else { + y = execute(a[1]); + pfa = makedfa(getsval(y), 1); + tempfree(y); + } + y = execute(a[2]); /* replacement string */ + if (pmatch(pfa, t)) { + tempstat = pfa->initstat; + pfa->initstat = 2; + pb = buf; + rptr = getsval(y); + do { + if (patlen == 0 && *patbeg != 0) { /* matched empty string */ + if (mflag == 0) { /* can replace empty */ + num++; + sptr = rptr; + while (*sptr != 0) { + adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub"); + if (*sptr == '\\') { + backsub(&pb, &sptr); + } else if (*sptr == '&') { + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + } + if (*t == 0) /* at end */ + goto done; + adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gsub"); + *pb++ = *t++; + if (pb > buf + bufsz) /* BUG: not sure of this test */ + FATAL("gsub result0 %.30s too big; can't happen", buf); + mflag = 0; + } + else { /* matched nonempty string */ + num++; + sptr = t; + adjbuf(&buf, &bufsz, 1+(patbeg-sptr)+pb-buf, recsize, &pb, "gsub"); + while (sptr < patbeg) + *pb++ = *sptr++; + sptr = rptr; + while (*sptr != 0) { + adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub"); + if (*sptr == '\\') { + backsub(&pb, &sptr); + } else if (*sptr == '&') { + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + t = patbeg + patlen; + if (patlen == 0 || *t == 0 || *(t-1) == 0) + goto done; + if (pb > buf + bufsz) + FATAL("gsub result1 %.30s too big; can't happen", buf); + mflag = 1; + } + } while (pmatch(pfa,t)); + sptr = t; + adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gsub"); + while ((*pb++ = *sptr++) != 0) + ; + done: if (pb < buf + bufsz) + *pb = '\0'; + else if (*(pb-1) != '\0') + FATAL("gsub result2 %.30s truncated; can't happen", buf); + setsval(x, buf); /* BUG: should be able to avoid copy + free */ + pfa->initstat = tempstat; + } + tempfree(x); + tempfree(y); + x = gettemp(); + x->tval = NUM; + x->fval = num; + free(buf); + return(x); +} + +void backsub(char **pb_ptr, char **sptr_ptr) /* handle \\& variations */ +{ /* sptr[0] == '\\' */ + char *pb = *pb_ptr, *sptr = *sptr_ptr; + + if (sptr[1] == '\\') { + if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */ + *pb++ = '\\'; + *pb++ = '&'; + sptr += 4; + } else if (sptr[2] == '&') { /* \\& -> \ + matched */ + *pb++ = '\\'; + sptr += 2; + } else { /* \\x -> \\x */ + *pb++ = *sptr++; + *pb++ = *sptr++; + } + } else if (sptr[1] == '&') { /* literal & */ + sptr++; + *pb++ = *sptr++; + } else /* literal \ */ + *pb++ = *sptr++; + + *pb_ptr = pb; + *sptr_ptr = sptr; +} diff --git a/awk/tran.c b/awk/tran.c @@ -0,0 +1,458 @@ +/* $OpenBSD: tran.c,v 1.15 2011/09/28 19:27:18 millert Exp $ */ +/**************************************************************** +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. +****************************************************************/ + +#define DEBUG +#include <stdio.h> +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "awk.h" +#include "ytab.h" + +#define FULLTAB 2 /* rehash when table gets this x full */ +#define GROWTAB 4 /* grow table by this factor */ + +Array *symtab; /* main symbol table */ + +char **FS; /* initial field sep */ +char **RS; /* initial record sep */ +char **OFS; /* output field sep */ +char **ORS; /* output record sep */ +char **OFMT; /* output format for numbers */ +char **CONVFMT; /* format for conversions in getsval */ +Awkfloat *NF; /* number of fields in current record */ +Awkfloat *NR; /* number of current record */ +Awkfloat *FNR; /* number of current record in current file */ +char **FILENAME; /* current filename argument */ +Awkfloat *ARGC; /* number of arguments from command line */ +char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */ +Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */ +Awkfloat *RLENGTH; /* length of same */ + +Cell *fsloc; /* FS */ +Cell *nrloc; /* NR */ +Cell *nfloc; /* NF */ +Cell *fnrloc; /* FNR */ +Array *ARGVtab; /* symbol table containing ARGV[...] */ +Array *ENVtab; /* symbol table containing ENVIRON[...] */ +Cell *rstartloc; /* RSTART */ +Cell *rlengthloc; /* RLENGTH */ +Cell *symtabloc; /* SYMTAB */ + +Cell *nullloc; /* a guaranteed empty cell */ +Node *nullnode; /* zero&null, converted into a node for comparisons */ +Cell *literal0; + +extern Cell **fldtab; + +void syminit(void) /* initialize symbol table with builtin vars */ +{ + literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab); + /* this is used for if(x)... tests: */ + nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab); + nullnode = celltonode(nullloc, CCON); + + 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; + 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; + nfloc = setsymtab("NF", "", 0.0, NUM, symtab); + NF = &nfloc->fval; + nrloc = setsymtab("NR", "", 0.0, NUM, symtab); + NR = &nrloc->fval; + fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab); + FNR = &fnrloc->fval; + SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->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); + symtabloc->sval = (char *) symtab; +} + +void arginit(int ac, char **av) /* set up ARGV and ARGC */ +{ + Cell *cp; + int i; + char temp[50]; + + ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval; + cp = setsymtab("ARGV", "", 0.0, ARR, symtab); + ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */ + cp->sval = (char *) ARGVtab; + for (i = 0; i < ac; i++) { + snprintf(temp, sizeof temp, "%d", i); + if (is_number(*av)) + setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab); + else + setsymtab(temp, *av, 0.0, STR, ARGVtab); + av++; + } +} + +void envinit(char **envp) /* set up ENVIRON variable */ +{ + Cell *cp; + char *p; + + cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab); + ENVtab = makesymtab(NSYMTAB); + cp->sval = (char *) ENVtab; + for ( ; *envp; envp++) { + if ((p = strchr(*envp, '=')) == NULL) + continue; + if( p == *envp ) /* no left hand side name in env string */ + continue; + *p++ = 0; /* split into two strings at = */ + if (is_number(p)) + setsymtab(*envp, p, atof(p), STR|NUM, ENVtab); + else + setsymtab(*envp, p, 0.0, STR, ENVtab); + p[-1] = '='; /* restore in case env is passed down to a shell */ + } +} + +Array *makesymtab(int n) /* make a new symbol table */ +{ + Array *ap; + Cell **tp; + + ap = (Array *) malloc(sizeof(Array)); + tp = (Cell **) calloc(n, sizeof(Cell *)); + if (ap == NULL || tp == NULL) + FATAL("out of space in makesymtab"); + ap->nelem = 0; + ap->size = n; + ap->tab = tp; + return(ap); +} + +void freesymtab(Cell *ap) /* free a symbol table */ +{ + Cell *cp, *temp; + Array *tp; + int i; + + if (!isarr(ap)) + return; + tp = (Array *) ap->sval; + if (tp == NULL) + return; + for (i = 0; i < tp->size; i++) { + for (cp = tp->tab[i]; cp != NULL; cp = temp) { + xfree(cp->nval); + if (freeable(cp)) + xfree(cp->sval); + temp = cp->cnext; /* avoids freeing then using */ + free(cp); + tp->nelem--; + } + tp->tab[i] = 0; + } + if (tp->nelem != 0) + WARNING("can't happen: inconsistent element count freeing %s", ap->nval); + free(tp->tab); + free(tp); +} + +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) + if (strcmp(s, p->nval) == 0) { + if (prev == NULL) /* 1st one */ + tp->tab[h] = p->cnext; + else /* middle somewhere */ + prev->cnext = p->cnext; + if (freeable(p)) + xfree(p->sval); + free(p->nval); + free(p); + tp->nelem--; + return; + } +} + +Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp) +{ + int h; + Cell *p; + + if (n != NULL && (p = lookup(n, tp)) != NULL) { + dprintf( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n", + (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval) ); + return(p); + } + p = (Cell *) malloc(sizeof(Cell)); + if (p == NULL) + FATAL("out of space for symbol table at %s", n); + p->nval = tostring(n); + p->sval = s ? tostring(s) : tostring(""); + p->fval = f; + p->tval = t; + p->csub = CUNK; + p->ctype = OCELL; + tp->nelem++; + if (tp->nelem > FULLTAB * tp->size) + rehash(tp); + h = hash(n, tp->size); + p->cnext = tp->tab[h]; + tp->tab[h] = p; + dprintf( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n", + (void*)p, p->nval, p->sval, p->fval, p->tval) ); + return(p); +} + +int hash(const char *s, int n) /* form hash value for string s */ +{ + unsigned hashval; + + for (hashval = 0; *s != '\0'; s++) + hashval = (*s + 31 * hashval); + return hashval % n; +} + +void rehash(Array *tp) /* rehash items in small table into big one */ +{ + int i, nh, nsz; + Cell *cp, *op, **np; + + nsz = GROWTAB * tp->size; + np = (Cell **) calloc(nsz, sizeof(Cell *)); + 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++) { + for (cp = tp->tab[i]; cp; cp = op) { + op = cp->cnext; + nh = hash(cp->nval, nsz); + cp->cnext = np[nh]; + np[nh] = cp; + } + } + free(tp->tab); + tp->tab = np; + tp->size = nsz; +} + +Cell *lookup(const char *s, Array *tp) /* look for s in tp */ +{ + Cell *p; + int h; + + h = hash(s, tp->size); + for (p = tp->tab[h]; p != NULL; p = p->cnext) + if (strcmp(s, p->nval) == 0) + return(p); /* found it */ + return(NULL); /* not found */ +} + +Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */ +{ + int fldno; + + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "assign to"); + if (isfld(vp)) { + donerec = 0; /* mark $0 invalid */ + fldno = atoi(vp->nval); + if (fldno > *NF) + newfld(fldno); + dprintf( ("setting field %d to %g\n", fldno, f) ); + } else if (isrec(vp)) { + donefld = 0; /* mark $1... invalid */ + donerec = 1; + } + if (freeable(vp)) + xfree(vp->sval); /* free any previous string */ + vp->tval &= ~STR; /* mark string invalid */ + vp->tval |= NUM; /* mark number ok */ + dprintf( ("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval) ); + return vp->fval = f; +} + +void funnyvar(Cell *vp, const char *rw) +{ + if (isarr(vp)) + FATAL("can't %s %s; it's an array name.", rw, vp->nval); + 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); +} + +char *setsval(Cell *vp, const char *s) /* set string val of a Cell */ +{ + char *t; + int fldno; + + 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 */ + 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; + } + t = tostring(s); /* in case it's self-assign */ + if (freeable(vp)) + xfree(vp->sval); + vp->tval &= ~NUM; + 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); +} + +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) + fldbld(); + else if (isrec(vp) && donerec == 0) + recbld(); + if (!isnum(vp)) { /* not a number */ + vp->fval = atof(vp->sval); /* best guess */ + if (is_number(vp->sval) && !(vp->tval&CON)) + vp->tval |= NUM; /* make NUM only sparingly */ + } + dprintf( ("getfval %p: %s = %g, t=%o\n", + (void*)vp, NN(vp->nval), vp->fval, vp->tval) ); + return(vp->fval); +} + +static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */ +{ + int n; + double dtemp; + + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "read value of"); + if (isfld(vp) && donefld == 0) + fldbld(); + else if (isrec(vp) && donerec == 0) + recbld(); + if (isstr(vp) == 0) { + if (freeable(vp)) + xfree(vp->sval); + if (modf(vp->fval, &dtemp) == 0) /* it's integral */ + n = asprintf(&vp->sval, "%.30g", vp->fval); + else + n = asprintf(&vp->sval, *fmt, vp->fval); + if (n == -1) + FATAL("out of space in get_str_val"); + vp->tval &= ~DONTFREE; + vp->tval |= STR; + } + dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n", + (void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) ); + return(vp->sval); +} + +char *getsval(Cell *vp) /* get string val of a Cell */ +{ + return get_str_val(vp, CONVFMT); +} + +char *getpssval(Cell *vp) /* get string val of a Cell for print */ +{ + return get_str_val(vp, OFMT); +} + + +char *tostring(const char *s) /* make a copy of string s */ +{ + char *p; + + p = strdup(s); + if (p == NULL) + FATAL("out of space in tostring on %s", s); + return p; +} + +char *qstring(const char *is, int delim) /* collect string up to next delim */ +{ + const char *os = is; + int c, n; + uschar *s = (uschar *) is; + uschar *buf, *bp; + + if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL) + FATAL( "out of space in qstring(%s)", s); + for (bp = buf; (c = *s) != delim; s++) { + if (c == '\n') + SYNTAX( "newline in string %.20s...", os ); + else if (c != '\\') + *bp++ = c; + else { /* \something */ + c = *++s; + if (c == 0) { /* \ at end */ + *bp++ = '\\'; + break; /* for loop */ + } + switch (c) { + case '\\': *bp++ = '\\'; break; + case 'n': *bp++ = '\n'; break; + case 't': *bp++ = '\t'; break; + case 'b': *bp++ = '\b'; break; + case 'f': *bp++ = '\f'; break; + case 'r': *bp++ = '\r'; break; + default: + if (!isdigit(c)) { + *bp++ = c; + break; + } + n = c - '0'; + if (isdigit(s[1])) { + n = 8 * n + *++s - '0'; + if (isdigit(s[1])) + n = 8 * n + *++s - '0'; + } + *bp++ = n; + break; + } + } + } + *bp++ = 0; + return (char *) buf; +} diff --git a/mkfile b/mkfile @@ -1,4 +1,4 @@ -TARG = _install find sed ed grep expr od stty nawk \ +TARG = _install find sed ed grep expr od stty awk \ patch diff printf dc dd fmt hd bc ps pgrep tar cp mk \ libcommon libuxre lex yacc diff --git a/nawk/COPYING b/nawk/COPYING @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/nawk/NOTES b/nawk/NOTES @@ -1,20 +0,0 @@ -Notes for the 'nawk' utility -============================ - -Changes since the version published by Caldera in OS Utilities 0.1a -(<http://unixtools.sourceforge.net/>) include: - -- The lex part of the code can be built with both Unix lex and flex. -- Support for multibyte characters. -- Proper support for LC_TIME (locale-specifix radix character recognized - in input data, but not in scripts). -- No line length limitations on input and output data. -- No limit on the number of fields per record. - -4.4BSD old awk and Brian W. Kernighan's 'One True awk' (available at -<http://cm.bell-labs.com/cm/cs/who/bwk/index.html>) have been used as -reference for some of the changes; in addition, the manual page -enclosed here has been derived from 'One True awk' since Caldera -did not enclose one. - - Gunnar Ritter 7/30/05 diff --git a/nawk/awk.g.y b/nawk/awk.g.y @@ -1,468 +0,0 @@ -/* - Changes by Gunnar Ritter, Freiburg i. Br., Germany, December 2002. - - Sccsid @(#)awk.g.y 1.9 (gritter) 5/14/06> - */ -/* UNIX(R) Regular Expression Tools - - Copyright (C) 2001 Caldera International, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* copyright "%c%" */ - -/* from RCS Header: awk.g.y 1.2 91/06/25 */ - -%{ -#include "awk.h" -#include <unistd.h> -#include <inttypes.h> -#include <pfmt.h> -int yywrap(void) { return(1); } -#ifndef DEBUG -# define PUTS(x) -#endif -Node *beginloc = 0, *endloc = 0; -int infunc = 0; /* = 1 if in arglist or body of func */ -unsigned char *curfname = 0; -Node *arglist = 0; /* list of args for current function */ -static void setfname(Cell *); -static int constnode(Node *); -static unsigned char *strnode(Node *); -static Node *notnull(Node *); -extern const char illstat[]; - -extern int yylex(void); -%} - -%union { - Node *p; - Cell *cp; - intptr_t i; - unsigned char *s; -} - -%token <i> FIRSTTOKEN /* must be first */ -%token <p> PROGRAM PASTAT PASTAT2 XBEGIN XEND -%token <i> NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']' -%token <i> ARRAY -%token <i> MATCH NOTMATCH MATCHOP -%token <i> FINAL DOT ALL CCL NCCL CHAR MCHAR OR STAR QUEST PLUS -%token <i> AND BOR APPEND EQ GE GT LE LT NE IN -%token <i> ARG BLTIN BREAK CONTINUE DELETE DO EXIT FOR FUNC -%token <i> SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT -%token <i> ADD MINUS MULT DIVIDE MOD -%token <i> ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ -%token <i> PRINT PRINTF SPRINTF -%token <p> ELSE INTEST CONDEXPR -%token <i> POSTINCR PREINCR POSTDECR PREDECR -%token <cp> VAR IVAR VARNF CALL NUMBER STRING FIELD -%token <s> REGEXPR - -%type <p> pas pattern ppattern plist pplist patlist prarg term re -%type <p> pa_pat pa_stat pa_stats -%type <s> reg_expr -%type <p> simple_stmt opt_simple_stmt stmt stmtlist -%type <p> var varname funcname varlist -%type <p> for if while -%type <i> pst opt_pst lbrace rparen comma nl opt_nl and bor -%type <i> subop print - -%right ASGNOP -%right '?' -%right ':' -%left BOR -%left AND -%left GETLINE -%nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|' -%left ARG BLTIN BREAK CALL CONTINUE DELETE DO EXIT FOR FIELD 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 -%right POWER -%right DECR INCR -%left INDIRECT -%token LASTTOKEN /* must be last */ - -%% - -program: - pas { if (errorflag==0) - winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); } - | error { yyclearin; bracecheck(); vyyerror(":95:Bailing out"); } - ; - -and: - AND | and NL - ; - -bor: - BOR | bor NL - ; - -comma: - ',' | comma NL - ; - -do: - DO { } | do NL - ; - -else: - ELSE { } | else NL - ; - -for: - FOR '(' opt_simple_stmt ';' pattern ';' opt_simple_stmt rparen stmt - { $$ = stat4(FOR, $3, notnull($5), $7, $9); } - | FOR '(' opt_simple_stmt ';' ';' opt_simple_stmt rparen stmt - { $$ = stat4(FOR, $3, NIL, $6, $8); } - | FOR '(' varname IN varname rparen stmt - { $$ = stat3(IN, $3, makearr($5), $7); } - ; - -funcname: - VAR { setfname($1); } - | CALL { setfname($1); } - ; - -if: - IF '(' pattern rparen { $$ = notnull($3); } - ; - -lbrace: - '{' | lbrace NL - ; - -nl: - NL | nl NL - ; - -opt_nl: - /* empty */ { $$ = 0; } - | nl - ; - -opt_pst: - /* empty */ { $$ = 0; } - | pst - ; - - -opt_simple_stmt: - /* empty */ { $$ = 0; } - | simple_stmt - ; - -pas: - opt_pst { $$ = 0; } - | opt_pst pa_stats opt_pst { $$ = $2; } - ; - -pa_pat: - pattern { $$ = notnull($1); } - ; - -pa_stat: - pa_pat { $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); } - | pa_pat lbrace stmtlist '}' { $$ = stat2(PASTAT, $1, $3); } - | pa_pat ',' pa_pat { $$ = pa2stat($1, $3, stat2(PRINT, rectonode(), NIL)); } - | pa_pat ',' pa_pat lbrace stmtlist '}' { $$ = pa2stat($1, $3, $5); } - | lbrace stmtlist '}' { $$ = stat2(PASTAT, NIL, $2); } - | XBEGIN lbrace stmtlist '}' - { 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; } - ; - -pa_stats: - pa_stat - | pa_stats opt_pst pa_stat { $$ = linkum($1, $3); } - ; - -patlist: - pattern - | patlist comma pattern { $$ = linkum($1, $3); } - ; - -ppattern: - var ASGNOP ppattern { $$ = op2($2, $1, $3); } - | ppattern '?' ppattern ':' ppattern %prec '?' - { $$ = op3(CONDEXPR, notnull($1), $3, $5); } - | ppattern bor ppattern %prec BOR - { $$ = op2(BOR, notnull($1), notnull($3)); } - | ppattern and ppattern %prec AND - { $$ = op2(AND, notnull($1), notnull($3)); } - | ppattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); } - | ppattern MATCHOP ppattern - { if (constnode($3)) - $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0)); - else - $$ = op3($2, (Node *)1, $1, $3); } - | ppattern IN varname { $$ = op2(INTEST, $1, makearr($3)); } - | '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); } - | ppattern term %prec CAT { $$ = op2(CAT, $1, $2); } - | term - ; - -pattern: - var ASGNOP pattern { $$ = op2($2, $1, $3); } - | pattern '?' pattern ':' pattern %prec '?' - { $$ = op3(CONDEXPR, notnull($1), $3, $5); } - | pattern bor pattern %prec BOR - { $$ = op2(BOR, notnull($1), notnull($3)); } - | pattern and pattern %prec AND - { $$ = op2(AND, notnull($1), notnull($3)); } - | NOT pattern - { $$ = op1(NOT, op2(NE,$2,valtonode(lookup("$zero&null",symtab),CCON))); } - | pattern EQ pattern { $$ = op2($2, $1, $3); } - | pattern GE pattern { $$ = op2($2, $1, $3); } - | pattern GT pattern { $$ = op2($2, $1, $3); } - | pattern LE pattern { $$ = op2($2, $1, $3); } - | pattern LT pattern { $$ = op2($2, $1, $3); } - | pattern NE pattern { $$ = op2($2, $1, $3); } - | pattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); } - | pattern MATCHOP pattern - { if (constnode($3)) - $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0)); - else - $$ = 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 { $$ = op3(GETLINE, $4, (Node*)$2, $1); } - | pattern '|' GETLINE { $$ = op3(GETLINE, (Node*)0, (Node*)$2, $1); } - | pattern term %prec CAT { $$ = op2(CAT, $1, $2); } - | term - ; - -plist: - pattern comma pattern { $$ = linkum($1, $3); } - | plist comma pattern { $$ = linkum($1, $3); } - ; - -pplist: - ppattern - | pplist comma ppattern { $$ = linkum($1, $3); } - ; - -prarg: - /* empty */ { $$ = rectonode(); } - | pplist - | '(' plist ')' { $$ = $2; } - ; - -print: - PRINT | PRINTF - ; - -pst: - NL | ';' | pst NL | pst ';' - ; - -rbrace: - '}' { } | rbrace NL - ; - -re: - reg_expr - { $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1,0)); } - | NOT re {$$ = op1(NOT, notnull($2)); } - ; - -reg_expr: - '/' {startreg();} REGEXPR '/' { $$ = $3; } - ; - -rparen: - ')' | rparen NL - ; - -simple_stmt: - print prarg '|' term { $$ = stat3($1, $2, (Node *) $3, $4); } - | print prarg APPEND term { $$ = stat3($1, $2, (Node *) $3, $4); } - | print prarg GT term { $$ = stat3($1, $2, (Node *) $3, $4); } - | print prarg { $$ = stat3($1, $2, NIL, NIL); } - | DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); } - | DELETE varname { yyclearin; vyyerror(":96:You can only delete array[element]"); $$ = stat1(DELETE, $2); } - | pattern { $$ = exptostat($1); } - | error { yyclearin; vyyerror(illstat); } - ; - -st: - nl { } | ';' opt_nl { } - ; - -stmt: - BREAK st { $$ = stat1(BREAK, NIL); } - | CONTINUE st { $$ = stat1(CONTINUE, NIL); } - | do stmt WHILE '(' pattern ')' st - { $$ = stat2(DO, $2, notnull($5)); } - | EXIT pattern st { $$ = stat1(EXIT, $2); } - | EXIT st { $$ = stat1(EXIT, NIL); } - | for - | if stmt else stmt { $$ = stat3(IF, $1, $2, $4); } - | if stmt { $$ = stat3(IF, $1, $2, NIL); } - | lbrace stmtlist rbrace { $$ = $2; } - | NEXT st { if (infunc) - vyyerror(":97:Next is illegal inside a function"); - $$ = stat1(NEXT, NIL); } - | RETURN pattern st { $$ = stat1(RETURN, $2); } - | RETURN st { $$ = stat1(RETURN, NIL); } - | simple_stmt st - | while stmt { $$ = stat2(WHILE, $1, $2); } - | ';' opt_nl { $$ = 0; } - ; - -stmtlist: - stmt - | stmtlist stmt { $$ = linkum($1, $2); } - ; - -subop: - SUB | GSUB - ; - -term: - term '+' term { $$ = op2(ADD, $1, $3); } - | term '-' term { $$ = op2(MINUS, $1, $3); } - | term '*' term { $$ = op2(MULT, $1, $3); } - | term '/' term { $$ = op2(DIVIDE, $1, $3); } - | term '%' term { $$ = op2(MOD, $1, $3); } - | term POWER term { $$ = op2(POWER, $1, $3); } - | '-' term %prec UMINUS { $$ = op1(UMINUS, $2); } - | '+' term %prec UMINUS { $$ = $2; } - | NOT term %prec UMINUS { $$ = op1(NOT, notnull($2)); } - | BLTIN '(' ')' { $$ = op2(BLTIN, (Node *) $1, rectonode()); } - | BLTIN '(' patlist ')' { $$ = op2(BLTIN, (Node *) $1, $3); } - | BLTIN { $$ = op2(BLTIN, (Node *) $1, rectonode()); } - | CALL '(' ')' { $$ = op2(CALL, valtonode($1,CVAR), NIL); } - | CALL '(' patlist ')' { $$ = op2(CALL, valtonode($1,CVAR), $3); } - | DECR var { $$ = op1(PREDECR, $2); } - | INCR var { $$ = op1(PREINCR, $2); } - | var DECR { $$ = op1(POSTDECR, $1); } - | var INCR { $$ = op1(POSTINCR, $1); } - | GETLINE var LT term { $$ = op3(GETLINE, $2, (Node *)$3, $4); } - | GETLINE LT term { $$ = op3(GETLINE, NIL, (Node *)$2, $3); } - | GETLINE var { $$ = op3(GETLINE, $2, NIL, NIL); } - | GETLINE { $$ = op3(GETLINE, NIL, NIL, NIL); } - | INDEX '(' pattern comma pattern ')' - { $$ = op2(INDEX, $3, $5); } - | INDEX '(' pattern comma reg_expr ')' - { vyyerror(":98:Index() doesn't permit regular expressions"); - $$ = op2(INDEX, $3, (Node*)$5); } - | '(' pattern ')' { $$ = $2; } - | MATCHFCN '(' pattern comma reg_expr ')' - { $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); } - | MATCHFCN '(' pattern comma pattern ')' - { if (constnode($5)) - $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1)); - else - $$ = op3(MATCHFCN, (Node *)1, $3, $5); } - | NUMBER { $$ = valtonode($1, CCON); } - | SPLIT '(' pattern comma varname comma pattern ')' /* string */ - { $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); } - | SPLIT '(' pattern comma varname comma reg_expr ')' /* const /regexp/ */ - { $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); } - | SPLIT '(' pattern comma varname ')' - { $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); } /* default */ - | SPRINTF '(' patlist ')' { $$ = op1($1, $3); } - | STRING { $$ = valtonode($1, CCON); } - | subop '(' reg_expr comma pattern ')' - { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); } - | subop '(' pattern comma pattern ')' - { if (constnode($3)) - $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode()); - else - $$ = op4($1, (Node *)1, $3, $5, rectonode()); } - | subop '(' reg_expr comma pattern comma var ')' - { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); } - | subop '(' pattern comma pattern comma var ')' - { if (constnode($3)) - $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7); - else - $$ = op4($1, (Node *)1, $3, $5, $7); } - | SUBSTR '(' pattern comma pattern comma pattern ')' - { $$ = op3(SUBSTR, $3, $5, $7); } - | SUBSTR '(' pattern comma pattern ')' - { $$ = op3(SUBSTR, $3, $5, NIL); } - | var - | re - ; - -var: - varname - | varname '[' patlist ']' { $$ = op2(ARRAY, makearr($1), $3); } - | FIELD { $$ = valtonode($1, CFLD); } - | IVAR { $$ = op1(INDIRECT, valtonode($1, CVAR)); } - | INDIRECT term { $$ = op1(INDIRECT, $2); } - ; - -varlist: - /* nothing */ { arglist = $$ = 0; } - | VAR { arglist = $$ = valtonode($1,CVAR); } - | varlist comma VAR { arglist = $$ = linkum($1,valtonode($3,CVAR)); } - ; - -varname: - VAR { $$ = valtonode($1, CVAR); } - | ARG { $$ = op1(ARG, (Node *) $1); } - | VARNF { $$ = op1(VARNF, (Node *) $1); } - ; - - -while: - WHILE '(' pattern rparen { $$ = notnull($3); } - ; - -%% - -static void -setfname(Cell *p) -{ - if (isarr(p)) - vyyerror(":99:%s is an array, not a function", p->nval); - else if (isfunc(p)) - vyyerror(":100:You cannot define function %s more than once", p->nval); - curfname = p->nval; -} - -static int -constnode(Node *p) -{ - return p->ntype == NVALUE && ((Cell *) (p->narg[0]))->csub == CCON; -} - -static unsigned char *strnode(Node *p) -{ - return ((Cell *)(p->narg[0]))->sval; -} - -static Node *notnull(Node *n) -{ - switch (n->nobj) { - case LE: case LT: case EQ: case NE: case GT: case GE: - case BOR: case AND: case NOT: - return n; - default: - return op2(NE, n, nullnode); - } -} diff --git a/nawk/awk.h b/nawk/awk.h @@ -1,387 +0,0 @@ -/* - Changes by Gunnar Ritter, Freiburg i. Br., Germany, December 2002. - - Sccsid @(#)awk.h 1.23 (gritter) 12/25/04> - */ -/* UNIX(R) Regular Expression Tools - - Copyright (C) 2001 Caldera International, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* copyright "%c%" */ - -/* from unixsrc:usr/src/common/cmd/awk/awk.h /main/uw7_nj/1 */ -/* from RCS Header: awk.h 1.2 91/06/25 */ - -typedef double Awkfloat; - -#define xfree(a) { if ((a) != NULL) { free(a); a = NULL; } } -#define MAXLABEL 25 - -extern const char version[]; - -extern char errbuf[200]; -#define ERROR snprintf(errbuf, sizeof errbuf, -#define FATAL ), error(1, errbuf) -#define WARNING ), error(0, errbuf) -#define SYNTAX ), yyerror(errbuf) - -extern int compile_time; /* 1 if compiling, 0 if running */ - -extern int posix; /* if POSIX behavior is desired */ - -/* - * This is done to prevent redefinition of our own definitions for FS with - * those defined in the system's header files. Same of RS (on HP-UX/PA-RISC). - */ -#undef FS -#undef RS - -extern unsigned char **FS; -extern unsigned char **RS; -extern unsigned char **ORS; -extern unsigned char **OFS; -extern unsigned char **OFMT; -extern unsigned char **CONVFMT; -extern Awkfloat *NR; -extern Awkfloat *FNR; -extern Awkfloat *NF; -extern unsigned char **FILENAME; -extern unsigned char **SUBSEP; -extern Awkfloat *RSTART; -extern Awkfloat *RLENGTH; - -#define CHUNK 512 /* record and string increment */ - -extern unsigned char *record; -extern int recsize; -extern int dbg; -extern int lineno; -extern int errorflag; -extern int donefld; /* 1 if record broken into fields */ -extern int donerec; /* 1 if record is valid (no fld has changed */ - -#define CBUFLEN 5120 -extern unsigned char cbuf[CBUFLEN]; /* miscellaneous character collection */ - -extern unsigned char *patbeg; /* beginning of pattern matched */ -extern int patlen; /* length. set in b.c */ - -extern int mb_cur_max; /* MB_CUR_MAX, for acceleration purposes */ - -extern const char outofspace[]; /* message */ - -/* Cell: all information about a variable or constant */ - -typedef struct Cell { - unsigned char ctype; /* OCELL, OBOOL, OJUMP, etc. */ - unsigned char csub; /* CCON, CTEMP, CFLD, etc. */ - unsigned char *nval; /* name, for variables only */ - unsigned char *sval; /* string value */ - Awkfloat fval; /* value as number */ - unsigned tval; /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE */ - struct Cell *cnext; /* ptr to next if chained */ -} Cell; - -typedef struct { /* symbol table array */ - int nelem; /* elements in table right now */ - int size; /* size of tab */ - Cell **tab; /* hash table pointers */ -} Array; - -#define NSYMTAB 50 /* initial size of a symbol table */ -extern Array *symtab, *makesymtab(int); -#define setsymtab(n, s, f, t, tp) ssetsymtab((unsigned char *)n, \ - (unsigned char *)s, \ - f, t, tp) -extern Cell *ssetsymtab(unsigned char *, unsigned char *, Awkfloat, - unsigned, Array *); -#define lookup(s, tp) slookup((unsigned char *)s, tp) -extern Cell *slookup(unsigned char *, Array *); - -extern Cell *recloc; /* location of input record */ -extern Cell *nrloc; /* NR */ -extern Cell *fnrloc; /* FNR */ -extern Cell *fsloc; /* FS */ -extern Cell *nfloc; /* NF */ -extern Cell *rstartloc; /* RSTART */ -extern Cell *rlengthloc; /* RLENGTH */ - -/* Cell.tval values: */ -#define NUM 01 /* number value is valid */ -#define STR 02 /* string value is valid */ -#define DONTFREE 04 /* string space is not freeable */ -#define CON 010 /* this is a constant */ -#define ARR 020 /* this is an array */ -#define FCN 040 /* this is a function name */ -#define FLD 0100 /* this is a field $1, $2, ... */ -#define REC 0200 /* this is $0 */ -#define CANBENUM 0400 /* tells setsymtab() to try for NUM, too */ - -#define freeable(p) (!((p)->tval & DONTFREE)) - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#ifdef __GLIBC__ -#ifdef _IO_getc_unlocked -#undef getc -#define getc(c) _IO_getc_unlocked(c) -#endif /* _IO_getc_unlocked */ -#endif /* __GLIBC__ */ - -#define getline xxgetline /* avoid glibc _GNU_SOURCE collision */ - -#define DEBUG -#ifdef DEBUG - /* uses have to be doubly parenthesized */ -# define dprintf(x) if (dbg) printf x -#else -# define dprintf(x) -#endif - -#ifndef IN_MAKETAB -#include <wchar.h> - -/* - * Get next character from string s and store it in wc; n is set to - * the length of the corresponding byte sequence. - */ -#define next(wc, s, n) (mb_cur_max > 1 && *(s) & 0200 ? \ - ((n) = mbtowc(&(wc), (char *)(s), mb_cur_max), \ - (n) = ((n) > 0 ? (n) : (n) < 0 ? (wc=WEOF, 1) : 1)) :\ - ((wc) = *(s), (n) = 1)) -#endif /* !IN_MAKETAB */ - -/* function types */ -#define FLENGTH 1 -#define FSQRT 2 -#define FEXP 3 -#define FLOG 4 -#define FINT 5 -#define FSYSTEM 6 -#define FRAND 7 -#define FSRAND 8 -#define FSIN 9 -#define FCOS 10 -#define FATAN 11 -#define FTOUPPER 12 -#define FTOLOWER 13 -#define FCLOSE 14 - -/* Node: parse tree is made of nodes, with Cell's at bottom */ - -typedef struct Node { - int ntype; - struct Node *nnext; - int lineno; - int nobj; - struct Node *narg[1]; /* variable: actual size set by calling malloc */ -} Node; - -#define NIL ((Node *) 0) - -extern Node *winner; -extern Node *nullstat; -extern Node *nullnode; - -/* ctypes */ -#define OCELL 1 -#define OBOOL 2 -#define OJUMP 3 - -/* Cell subtypes: csub */ -#define CFREE 7 -#define CCOPY 6 -#define CCON 5 -#define CTEMP 4 -#define CNAME 3 -#define CVAR 2 -#define CFLD 1 - -/* bool subtypes */ -#define BTRUE 11 -#define BFALSE 12 - -/* jump subtypes */ -#define JEXIT 21 -#define JNEXT 22 -#define JBREAK 23 -#define JCONT 24 -#define JRET 25 - -/* node types */ -#define NVALUE 1 -#define NSTAT 2 -#define NEXPR 3 -#define NFIELD 4 - -extern Cell *(*proctab[])(Node **, int); -extern int pairstack[]; -extern long paircnt; - -#define notlegal(n) (n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc) -#define isvalue(n) ((n)->ntype == NVALUE) -#define isexpr(n) ((n)->ntype == NEXPR) -#define isjump(n) ((n)->ctype == OJUMP) -#define isexit(n) ((n)->csub == JEXIT) -#define isbreak(n) ((n)->csub == JBREAK) -#define iscont(n) ((n)->csub == JCONT) -#define isnext(n) ((n)->csub == JNEXT) -#define isret(n) ((n)->csub == JRET) -#define isstr(n) ((n)->tval & STR) -#define isnum(n) ((n)->tval & NUM) -#define isarr(n) ((n)->tval & ARR) -#define isfunc(n) ((n)->tval & FCN) -#define istrue(n) ((n)->csub == BTRUE) -#define istemp(n) ((n)->csub == CTEMP) - -#include <regex.h> - -typedef struct fa { - unsigned char *restr; - int use; - int notbol; - regex_t re; -} fa; - -/* awk.g.c */ -extern int yywrap(void); -extern int yyparse(void); -/* awk.lx.c */ -extern int yylex(void); -extern void startreg(void); -extern int awk_input(void); -/* b.c */ -extern fa *makedfa(unsigned char *, int); -extern int match(void *, unsigned char *); -extern int pmatch(void *, unsigned char *); -extern int nematch(void *, unsigned char *); -/* lib.c */ -extern void fldinit(void); -extern void initgetrec(void); -extern int getrec(unsigned char **, int *); -extern int readrec(unsigned char **, int *, FILE *); -extern unsigned char *getargv(int); -extern void setclvar(unsigned char *); -extern void fldbld(void); -extern void newfld(int); -extern void recbld(void); -extern Cell *fieldadr(int); -extern void vyyerror(const char *, ...); -extern void yyerror(char *); -extern void fpecatch(int); -extern void bracecheck(void); -extern void error(int, const char *, ...); -extern void bclass(int); -extern double errcheck(double, unsigned char *); -extern void PUTS(unsigned char *); -extern int isclvar(unsigned char *); -extern int is2number(unsigned char *, Cell *); -extern double awk_atof(const char *); -extern unsigned char *makerec(const unsigned char *, int); -/* main.c */ -extern int pgetc(void); -/* parse.c */ -extern Node *nodealloc(int); -extern Node *exptostat(Node *); -extern Node *node1(int, Node *); -extern Node *node2(int, Node *, Node *); -extern Node *node3(int, Node *, Node *, Node *); -extern Node *node4(int, Node *, Node *, Node *, Node *); -extern Node *stat3(int, Node *, Node *, Node *); -extern Node *op2(int, Node *, Node *); -extern Node *op1(int, Node *); -extern Node *stat1(int, Node *); -extern Node *op3(int, Node *, Node *, Node *); -extern Node *op4(int, Node *, Node *, Node *, Node *); -extern Node *stat2(int, Node *, Node *); -extern Node *stat4(int, Node *, Node *, Node *, Node *); -extern Node *valtonode(Cell *, int); -extern Node *rectonode(void); -extern Node *makearr(Node *); -extern Node *pa2stat(Node *, Node *, Node *); -extern Node *linkum(Node *, Node *); -extern void defn(Cell *, Node *, Node *); -extern int isarg(const char *); -/* proctab.c */ -extern unsigned char *tokname(int); -/* run.c */ -extern int run(Node *); -extern Cell *r_execute(Node *); -extern Cell *program(Node **, int); -extern Cell *call(Node **, int); -extern Cell *copycell(Cell *); -extern Cell *arg(Node **, int); -extern Cell *jump(Node **, int); -extern Cell *getline(Node **, int); -extern Cell *getnf(Node **, int); -extern Cell *array(Node **, int); -extern Cell *delete(Node **, int); -extern Cell *intest(Node **, int); -extern Cell *matchop(Node **, int); -extern Cell *boolop(Node **, int); -extern Cell *relop(Node **, int); -extern Cell *gettemp(const char *); -extern Cell *indirect(Node **, int); -extern Cell *substr(Node **, int); -extern Cell *sindex(Node **, int); -extern int format(unsigned char **, int *, const unsigned char *, Node *); -extern Cell *awsprintf(Node **, int); -extern Cell *aprintf(Node **, int); -extern Cell *arith(Node **, int); -extern double ipow(double, int); -extern Cell *incrdecr(Node **, int); -extern Cell *assign(Node **, int); -extern Cell *cat(Node **, int); -extern Cell *pastat(Node **, int); -extern Cell *dopa2(Node **, int); -extern Cell *split(Node **, int); -extern Cell *condexpr(Node **, int); -extern Cell *ifstat(Node **, int); -extern Cell *whilestat(Node **, int); -extern Cell *dostat(Node **, int); -extern Cell *forstat(Node **, int); -extern Cell *instat(Node **, int); -extern Cell *bltin(Node **, int); -extern Cell *print(Node **, int); -extern Cell *nullproc(Node **, int); -extern FILE *redirect(int, Node *); -extern FILE *openfile(int, unsigned char *); -extern Cell *sub(Node **, int); -extern Cell *gsub(Node **, int); -extern int chrlen(const unsigned char *); -extern int chrdist(const unsigned char *, const unsigned char *); -/* tran.c */ -extern void syminit(void); -extern void arginit(int, unsigned char **); -extern void envinit(unsigned char **); -extern Array *makesymtab(int); -extern void freesymtab(Cell *); -extern void freeelem(Cell *, unsigned char *); -extern Cell *ssetsymtab(unsigned char *, unsigned char *, - Awkfloat, unsigned, Array *); -extern Cell *slookup(unsigned char *, Array *); -extern Awkfloat setfval(Cell *, Awkfloat); -extern void funnyvar(Cell *, char *); -extern unsigned char *setsval(Cell *, unsigned char *); -extern Awkfloat r_getfval(Cell *); -extern unsigned char *r_getsval(Cell *); -#define tostring(s) stostring((unsigned char *)s) -extern unsigned char *stostring(const unsigned char *); -extern unsigned char *qstring(unsigned char *, int); diff --git a/nawk/awk.lx.l b/nawk/awk.lx.l @@ -1,383 +0,0 @@ -%{ -/* - Changes by Gunnar Ritter, Freiburg i. Br., Germany, December 2002. - - Sccsid @(#)awk.lx.l 1.13 (gritter) 11/22/05> - */ -/* UNIX(R) Regular Expression Tools - - Copyright (C) 2001 Caldera International, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* - * flex port partially taken from 4.4BSD awk, - * - * Copyright (c) 1991 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* copyright "%c%" */ - -/* from unixsrc:usr/src/common/cmd/awk/awk.lx.l /main/uw7_nj/1 */ -/* from RCS Header: awk.lx.l 1.2 91/06/25 */ - -/*%Start A str sc reg comment*/ -/*%X A str sc reg comment*/ -%} -%X A str reg - -%{ - -#include "awk.h" -#include "y.tab.h" -#include <pfmt.h> -#include <unistd.h> - -static void awk_unputstr(const char *s); - -#ifdef FLEX_SCANNER -static int awk_yytchar; -int awk_input(void); -static void awk_unput(int c); -#undef YY_INPUT -#define YY_INPUT(buf, result, max_size) { \ - int c = awk_input(); \ - result = (c == EOF || c == '\0') ? YY_NULL : (buf[0] = c, 1); \ -} -#else /* !FLEX_SCANNER */ -#undef input /* defeat lex */ -#undef unput -int input(void); -void unput(int c); -#define awk_unput(c) unput(c) -#define awk_yytchar yytchar -#endif /* !FLEX_SCANNER */ - - -extern YYSTYPE yylval; -extern int infunc; - -int lineno = 1; -int bracecnt = 0; -int brackcnt = 0; -int parencnt = 0; -#define DEBUG -#ifdef DEBUG -# define RET(x) {if(dbg)printf("lex %s [%s]\n", tokname(x), yytext); return(x); } -#else -# define RET(x) return(x) -#endif - -#define CADD cbuf[clen++] = yytext[0]; \ - if (clen >= CBUFLEN-1) { \ - vyyerror(":90:String/reg expr %.10s ... too long", cbuf); \ - BEGIN INITIAL; \ - } - -static const char extra[] = ":91:Extra %c"; -extern const char nlstring[]; - -unsigned char cbuf[CBUFLEN]; -unsigned char *s; -int clen, cflag; -%} - -A [a-zA-Z_] -B [a-zA-Z0-9_] -D [0-9] -O [0-7] -H [0-9a-fA-F] -WS [ \t] - -%% - static int sc_flag = 0; - - if (sc_flag) { - BEGIN INITIAL; - sc_flag = 0; - RET('}'); - } - -\n { lineno++; RET(NL); } -#.* { ; } /* strip comments */ -{WS}+ { ; } -<INITIAL,reg>"\\"\n lineno++; -; { RET(';'); } - -BEGIN { RET(XBEGIN); } -END { RET(XEND); } -func(tion)? { if (infunc) vyyerror(":92:Illegal nested function"); RET(FUNC); } -return { if (!infunc) vyyerror(":93:Return not in function"); RET(RETURN); } -"&&" { RET(AND); } -"||" { RET(BOR); } -"!" { RET(NOT); } -"!=" { yylval.i = NE; RET(NE); } -"~" { yylval.i = MATCH; RET(MATCHOP); } -"!~" { yylval.i = NOTMATCH; RET(MATCHOP); } -"<" { yylval.i = LT; RET(LT); } -"<=" { yylval.i = LE; RET(LE); } -"==" { yylval.i = EQ; RET(EQ); } -">=" { yylval.i = GE; RET(GE); } -">" { yylval.i = GT; RET(GT); } -">>" { yylval.i = APPEND; RET(APPEND); } -"++" { yylval.i = INCR; RET(INCR); } -"--" { yylval.i = DECR; RET(DECR); } -"+=" { yylval.i = ADDEQ; RET(ASGNOP); } -"-=" { yylval.i = SUBEQ; RET(ASGNOP); } -"*=" { yylval.i = MULTEQ; RET(ASGNOP); } -"/=" { yylval.i = DIVEQ; RET(ASGNOP); } -"%=" { yylval.i = MODEQ; RET(ASGNOP); } -"^=" { yylval.i = POWEQ; RET(ASGNOP); } -"**=" { yylval.i = POWEQ; RET(ASGNOP); } -"=" { yylval.i = ASSIGN; RET(ASGNOP); } -"**" { RET(POWER); } -"^" { RET(POWER); } - -"$"{D}+ { yylval.cp = fieldadr(atoi(yytext+1)); RET(FIELD); } -"$NF" { awk_unputstr("(NF)"); return(INDIRECT); } -"$"{A}{B}* { int c, n; - c = awk_yytchar; - if (c == '(' || c == '[' || infunc && (n=isarg(yytext+1)) >= 0) { - awk_unputstr(yytext+1); - return(INDIRECT); - } else { - yylval.cp = setsymtab((unsigned char *)yytext+1,"",0.0,STR|NUM,symtab); - RET(IVAR); - } - } -"$" { RET(INDIRECT); } -NF { yylval.cp = setsymtab((unsigned char *)yytext, "", 0.0, NUM, symtab); RET(VARNF); } - -({D}+("."?){D}*|"."{D}+)((e|E)("+"|-)?{D}+)? { - yylval.cp = setsymtab((unsigned char *)yytext, tostring((unsigned char *)yytext), awk_atof(yytext), CON|NUM, symtab); - RET(NUMBER); } - -while { RET(WHILE); } -for { RET(FOR); } -do { RET(DO); } -if { RET(IF); } -else { RET(ELSE); } -next { RET(NEXT); } -exit { RET(EXIT); } -break { RET(BREAK); } -continue { RET(CONTINUE); } -print { yylval.i = PRINT; RET(PRINT); } -printf { yylval.i = PRINTF; RET(PRINTF); } -sprintf { yylval.i = SPRINTF; RET(SPRINTF); } -split { yylval.i = SPLIT; RET(SPLIT); } -substr { RET(SUBSTR); } -sub { yylval.i = SUB; RET(SUB); } -gsub { yylval.i = GSUB; RET(GSUB); } -index { RET(INDEX); } -match { RET(MATCHFCN); } -in { RET(IN); } -getline { RET(GETLINE); } -delete { RET(DELETE); } -length { yylval.i = FLENGTH; RET(BLTIN); } -log { yylval.i = FLOG; RET(BLTIN); } -int { yylval.i = FINT; RET(BLTIN); } -exp { yylval.i = FEXP; RET(BLTIN); } -sqrt { yylval.i = FSQRT; RET(BLTIN); } -sin { yylval.i = FSIN; RET(BLTIN); } -cos { yylval.i = FCOS; RET(BLTIN); } -atan2 { yylval.i = FATAN; RET(BLTIN); } -system { yylval.i = FSYSTEM; RET(BLTIN); } -rand { yylval.i = FRAND; RET(BLTIN); } -srand { yylval.i = FSRAND; RET(BLTIN); } -toupper { yylval.i = FTOUPPER; RET(BLTIN); } -tolower { yylval.i = FTOLOWER; RET(BLTIN); } -close { yylval.i = FCLOSE; RET(BLTIN); } - -{A}{B}* { int n, c; - c = awk_yytchar; /* look for '(' */ - if (c != '(' && infunc && (n=isarg(yytext)) >= 0) { - yylval.i = n; - RET(ARG); - } else { - yylval.cp = setsymtab((unsigned char *)yytext,"",0.0,STR|NUM,symtab); - if (c == '(') { - RET(CALL); - } else { - RET(VAR); - } - } - } -\" { BEGIN str; clen = 0; } - -"}" { if (--bracecnt < 0) vyyerror(extra, '}'); sc_flag = 1; RET(';'); } -"]" { if (--brackcnt < 0) vyyerror(extra, ']'); RET(']'); } -")" { if (--parencnt < 0) vyyerror(extra, ')'); RET(')'); } - -. { if (yytext[0] == '{') bracecnt++; - else if (yytext[0] == '[') brackcnt++; - else if (yytext[0] == '(') parencnt++; - RET(yylval.i = yytext[0]); /* everything else */ } - -<reg>\\. { cbuf[clen++] = '\\'; cbuf[clen++] = yytext[1]; } -<reg>\n { vyyerror(":94:Newline in regular expression %.10s ...", cbuf); lineno++; BEGIN INITIAL; } -<reg>"/" { BEGIN INITIAL; - cbuf[clen] = 0; - yylval.s = tostring(cbuf); - awk_unput('/'); - RET(REGEXPR); } -<reg>. { CADD; } - -<str>\" { BEGIN INITIAL; - cbuf[clen] = 0; s = tostring(cbuf); - cbuf[clen] = ' '; cbuf[++clen] = 0; - yylval.cp = setsymtab(cbuf, s, 0.0, CON|STR, symtab); - RET(STRING); } -<str>\n { vyyerror(nlstring, cbuf); lineno++; BEGIN INITIAL; } -<str>"\\\"" { cbuf[clen++] = '"'; } -<str>"\\"n { cbuf[clen++] = '\n'; } -<str>"\\"t { cbuf[clen++] = '\t'; } -<str>"\\"f { cbuf[clen++] = '\f'; } -<str>"\\"r { cbuf[clen++] = '\r'; } -<str>"\\"b { cbuf[clen++] = '\b'; } -<str>"\\"v { cbuf[clen++] = '\v'; } /* these ANSIisms may not be known by */ -<str>"\\"a { cbuf[clen++] = '\007'; } /* your compiler. hence 007 for bell */ -<str>"\\\\" { cbuf[clen++] = '\\'; } -<str>"\\"({O}{O}{O}|{O}{O}|{O}) { int n; - sscanf(yytext+1, "%o", &n); cbuf[clen++] = n; } -<str>"\\"x({H}+) { int n; /* ANSI permits any number! */ - sscanf(yytext+2, "%x", &n); cbuf[clen++] = n; } -<str>"\\". { cbuf[clen++] = yytext[1]; } -<str>. { CADD; } - -%% - -void -startreg(void) -{ - BEGIN reg; - clen = 0; -} - -/* input() and unput() were transcriptions of the standard lex - macros for input and output with additions for error message - printing. God help us all if someone changes how lex works. - - Luckily, the BSD people did most of the flex porting already - for oawk. -*/ - -unsigned char ebuf[300]; -unsigned char *ep = ebuf; - -#ifdef FLEX_SCANNER -int -awk_input(void) -{ - register int c; - extern unsigned char *lexprog; - - if (lexprog != NULL) { /* awk '...' */ - if (c = *lexprog & 0377) - lexprog++; - } else /* awk -f ... */ - c = pgetc(); - if (c == EOF) - c = 0; - if (ep >= ebuf + sizeof ebuf) - ep = ebuf; - awk_yytchar = c; - return *ep++ = c; -} - -static void -awk_unput(int c) -{ - awk_yytchar = c; - unput(c); -} - - -#else /* !FLEX_SCANNER */ -int -input(void) -{ - register int c; - extern unsigned char *lexprog; - - if (yysptr > yysbuf) - c = U(*--yysptr) & 0377; - else if (lexprog != NULL) { /* awk '...' */ - if (c = *lexprog & 0377) - lexprog++; - } else /* awk -f ... */ - c = pgetc(); - if (c == '\n') - yylineno++; - else if (c == EOF) - c = 0; - if (ep >= ebuf + sizeof ebuf) - ep = ebuf; - return *ep++ = c; -} - -int -awk_input(void) -{ - return input(); -} - -void -unput(int c) -{ - yytchar = c; - if (yytchar == '\n') - yylineno--; - *yysptr++ = yytchar; - if (--ep < ebuf) - ep = ebuf + sizeof(ebuf) - 1; -} -#endif /* !FLEX_SCANNER */ - -static void -awk_unputstr(const char *s) -{ - int i; - - for (i = strlen(s)-1; i >= 0; i--) - awk_unput(s[i]); -} diff --git a/nawk/b.c b/nawk/b.c @@ -1,174 +0,0 @@ -/* - Changes by Gunnar Ritter, Freiburg i. Br., Germany, December 2002. - - Sccsid @(#)b.c 1.6 (gritter) 5/15/04> - */ -/* UNIX(R) Regular Expression Tools - - Copyright (C) 2001 Caldera International, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* copyright "%c%" */ - -/* from unixsrc:usr/src/common/cmd/awk/b.c /main/uw7_nj/1 */ - -#include <stdio.h> -#include "awk.h" -#include <ctype.h> -#include "y.tab.h" -#include <pfmt.h> - -unsigned char *patbeg; -int patlen; - -static void -reprob(fa *f, int e) -{ - char msg[BUFSIZ]; - - regerror(e, &f->re, msg, sizeof(msg)); - error(MM_ERROR, ":104:Error in RE `%s': %s", f->restr, msg); -} - -static fa * -mkdfa(unsigned char *s) /* build DFA from s */ -{ - fa *pfa; - int i; - int flags; - - if ((pfa = (fa *)malloc(sizeof(fa))) == 0) - { - error(MM_ERROR, - "5:Regular expression too big: out of space in %s", s); - } - flags = posix ? REG_EXTENDED : REG_OLDERE | REG_OLDESC | REG_NOI18N; - flags |= REG_ONESUB | REG_BKTEMPTY | REG_BKTESCAPE | REG_ESCSEQ; - if ((i = regcomp(&pfa->re, (char *)s, flags)) != 0) - { - pfa->restr = s; - reprob(pfa, i); - } - pfa->restr = tostring(s); - pfa->use = 1; - pfa->notbol = 0; - return pfa; -} - -fa * -makedfa(unsigned char *s, int leftmost) /* build and cache DFA from s */ -{ - static fa *fatab[20]; - static int nfatab; - int i, n, u; - fa *pfa; - - if (compile_time) - return mkdfa(s); - /* - * Search for a match to those cached. - * If not found, save it, tossing least used one when full. - */ - for (i = 0; i < nfatab; i++) - { - if (strcmp((char *)fatab[i]->restr, (char *)s) == 0) - { - fatab[i]->use++; - return fatab[i]; - } - } - pfa = mkdfa(s); - if ((n = nfatab) < sizeof(fatab) / sizeof(fa *)) - nfatab++; - else - { - n = 0; - u = fatab[0]->use; - for (i = 1; i < sizeof(fatab) / sizeof(fa *); i++) - { - if (fatab[i]->use < u) - { - n = i; - u = fatab[n]->use; - } - } - free((void *)fatab[n]->restr); - regfree(&fatab[n]->re); - free((void *)fatab[n]); - } - fatab[n] = pfa; - return pfa; -} - -int -match(void *v, unsigned char *p) /* does p match f anywhere? */ -{ - int err; - fa *f = v; - - if ((err = regexec(&f->re, (char *)p, (size_t)0, (regmatch_t *)0, 0)) == 0) - return 1; - if (err != REG_NOMATCH) - reprob(f, err); - return 0; -} - -int -pmatch(void *v, unsigned char *p) /* find leftmost longest (maybe empty) match */ -{ - regmatch_t m; - int err; - fa *f = v; - - if ((err = regexec(&f->re, (char *)p, (size_t)1, &m, f->notbol)) == 0) - { - patbeg = &p[m.rm_so]; - patlen = m.rm_eo - m.rm_so; - return 1; - } - if (err != REG_NOMATCH) - reprob(f, err); - patlen = -1; - return 0; -} - -int -nematch(void *v, unsigned char *p) /* find leftmost longest nonempty match */ -{ - regmatch_t m; - int err; - fa *f = v; - - for (;;) - { - if ((err = regexec(&f->re, (char *)p, (size_t)1, &m, - f->notbol | REG_NONEMPTY)) == 0) - { - if ((patlen = m.rm_eo - m.rm_so) == 0) - { - p += m.rm_eo; - continue; - } - patbeg = &p[m.rm_so]; - return 1; - } - if (err != REG_NOMATCH) - reprob(f, err); - patlen = -1; - return 0; - } -} diff --git a/nawk/lib.c b/nawk/lib.c @@ -1,852 +0,0 @@ -/* - Changes by Gunnar Ritter, Freiburg i. Br., Germany, December 2002. - - Sccsid @(#)lib.c 1.27 (gritter) 12/25/06> - */ -/* UNIX(R) Regular Expression Tools - - Copyright (C) 2001 Caldera International, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* copyright "%c%" */ - -/* from unixsrc:usr/src/common/cmd/awk/lib.c /main/uw7_nj/1 */ -/* from RCS Header: lib.c 1.2 91/06/25 */ - -#define DEBUG -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include <string.h> -#include <strings.h> -#include "awk.h" -#include "y.tab.h" -#include <pfmt.h> -#include <stdarg.h> -#include <wctype.h> -#include "asciitype.h" - -#undef RS - -static void eprint(void); - -#define getfval(p) (((p)->tval & (ARR|FLD|REC|NUM)) == NUM ? (p)->fval : r_getfval(p)) -#define getsval(p) (((p)->tval & (ARR|FLD|REC|STR)) == STR ? (p)->sval : r_getsval(p)) - -FILE *infile = NULL; -unsigned char *file = (unsigned char*) ""; -unsigned char *record; -unsigned char *recdata; -int recsize; -unsigned char *fields; - -int donefld; /* 1 = implies rec broken into fields */ -int donerec; /* 1 = record is valid (no flds have changed) */ - -Cell **fldtab; /* room for fields */ - -static Cell dollar0 = { - OCELL, CFLD, (unsigned char*) "$0", (unsigned char *)"", 0.0, REC|STR|DONTFREE -}; -static Cell FINIT = { - OCELL, CFLD, NULL, (unsigned char*) "", 0.0, FLD|STR|DONTFREE -}; - - -static int MAXFLD; /* number of allocated fields */ -int maxfld = 0; /* last used field */ -int argno = 1; /* current input argument number */ -extern Awkfloat *ARGC; - -static void growrec(unsigned char **, int *, int, unsigned char **, int); - -char badopen[] = ":11:Cannot open %s: %s"; - -/* Dynamic field and record allocation inspired by Bell Labs awk. */ -static void morefields(void) -{ - int i; - const int n = 32; - - fldtab = realloc(fldtab, (MAXFLD + n + 1) * sizeof *fldtab); - if (fldtab == NULL) - error(MM_ERROR, ":13:Record `%.20s...' has too many fields", - record); - recloc = fldtab[0]; - for (i = MAXFLD; i < MAXFLD + n; i++) { - fldtab[i] = malloc(sizeof **fldtab); - if (fldtab[i] == NULL) - error(MM_ERROR, - ":13:Record `%.20s...' has too many fields", - record); - *fldtab[i] = FINIT; - } - MAXFLD += n; -} - -void fldinit(void) -{ - record = recdata = malloc(recsize = CHUNK); - fields = malloc(recsize); - if (record == NULL || fields == NULL) - error(MM_ERROR, outofspace, "fldinit"); - *record = '\0'; - morefields(); - *fldtab[0] = dollar0; -} - -void initgetrec(void) -{ - extern unsigned char **start_delayed, **after_delayed; - unsigned char **pp; - int i; - unsigned char *p; - - /* first handle delayed name=val arguments */ - for (pp = start_delayed; pp != after_delayed; pp++) - setclvar(*pp); - for (i = 1; i < *ARGC; i++) { - if (!isclvar(p = getargv(i))) /* find 1st real filename */ - return; - setclvar(p); /* a commandline assignment before filename */ - argno++; - } - infile = stdin; /* no filenames, so use stdin */ - /* *FILENAME = file = (unsigned char*) "-"; */ -} - -int getrec(unsigned char **buf, int *bufsize) -{ - int c, saved; - static int firsttime = 1; - - if (firsttime) { - firsttime = 0; - initgetrec(); - } - dprintf( ("RS=<%s>, FS=<%s>, ARGC=%d, FILENAME=%s\n", - *RS ? *RS : tostring(""), - *FS ? *FS : tostring(""), - (int) *ARGC, - *FILENAME ? *FILENAME : tostring("")) ); - donefld = 0; - donerec = 1; - if (*bufsize == 0) { - if ((*buf = malloc(*bufsize = CHUNK)) == NULL) - error(MM_ERROR, outofspace, "getrec"); - **buf = '\0'; - } - saved = (*buf)[0]; - (*buf)[0] = 0; - while (argno < *ARGC || infile == stdin) { - dprintf( ("argno=%d, file=|%s|\n", argno, file) ) - ; - if (infile == NULL) { /* have to open a new file */ - file = getargv(argno); - if (*file == '\0') { /* it's been zapped */ - argno++; - continue; - } - if (isclvar(file)) { /* a var=value arg */ - setclvar(file); - argno++; - continue; - } - *FILENAME = file; - dprintf( ("opening file %s\n", file) ); - if (*file == '-' && *(file+1) == '\0') - infile = stdin; - else if ((infile = fopen((char *)file, "r")) == NULL) - error(MM_ERROR, badopen, file, strerror(errno)); - setfval(fnrloc, 0.0); - } - c = readrec(buf, bufsize, infile); - if (c != 0 || (*buf)[0] != '\0') { /* normal record */ - if (*buf == record) { - if (!(recloc->tval & DONTFREE)) - xfree(recloc->sval); - recloc->sval = record; - recloc->tval = REC | STR | DONTFREE; - (void)is2number(0, recloc); - } - setfval(nrloc, nrloc->fval+1); - setfval(fnrloc, fnrloc->fval+1); - return 1; - } - /* EOF arrived on this file; set up next */ - if (infile != stdin) - fclose(infile); - infile = NULL; - argno++; - } - /* - * POSIX.2 requires that NF stick with its last value - * at the start of the END code. The most straightforward - * way to do this is to restore the contents of record - * [==buf when called from program()] so that getnf() will - * recompute the same NF value unless something strange - * occurs. This has the side effect of $0...$NF *also* - * having sticky values into END, but that seems to match - * the spirit of POSIX.2's rule for NF. - */ - if (posix) - (*buf)[0] = saved; - return 0; /* true end of file */ -} - -int readrec(unsigned char **buf, int *bufsize, FILE *inf) - /* read one record into buf */ -{ - register int sep, c, k, m, n; - unsigned char *rr; - register int nrr; - wchar_t wc; - - next(wc, *RS, n); - if ((sep = **RS) == 0) { - sep = '\n'; - while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ - ; - if (c != EOF) - ungetc(c, inf); - } - if (*bufsize == 0) - growrec(buf, bufsize, CHUNK, NULL, 0); - for (rr = *buf, nrr = *bufsize; ; ) { - cont: for (; (c=getc(inf)) != sep && c != EOF; *rr++ = c) - if (--nrr < n + 3) { - growrec(buf, bufsize, *bufsize + CHUNK, &rr, 0); - nrr += CHUNK; - } - if (c != EOF) { - /* - * Note: This code does not restrict occurences of - * the multibyte sequence in RS to the start of an - * input character. - */ - for (m = 1; m < n; m++) { - if ((c = getc(inf)) == EOF || c != (*RS)[m]) { - for (k = 0; k < m; k++) - *rr++ = (*RS)[k]; - nrr -= k; - if (c == EOF) - break; - *rr++ = c; - nrr--; - goto cont; - } - } - } - if (**RS == sep || c == EOF) - break; - if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ - break; - *rr++ = '\n'; - *rr++ = c; - } - /*if (rr > *buf + *bufsize) - error(MM_ERROR, ":12:Input record `%.20s...' too long", *buf);*/ - *rr = 0; - dprintf( ("readrec saw <%s>, returns %d\n", *buf, c == EOF - && rr == *buf ? 0 : 1) ); - return c == EOF && rr == *buf ? 0 : 1; -} - -unsigned char *getargv(int n) /* get ARGV[n] */ -{ - Cell *x; - unsigned char *s, temp[25]; - extern Array *ARGVtab; - - snprintf((char *)temp, sizeof temp, "%d", n); - x = setsymtab(temp, "", 0.0, STR, ARGVtab); - s = getsval(x); - dprintf( ("getargv(%d) returns |%s|\n", n, s) ); - return s; -} - -void setclvar(unsigned char *s) /* set var=value from s */ -{ - unsigned char *p; - Cell *q; - - for (p=s; *p != '='; p++) - ; - *p++ = 0; - p = qstring(p, '\0'); - q = setsymtab(s, p, 0.0, STR, symtab); - setsval(q, p); - (void)is2number(0, q); - dprintf( ("command line set %s to |%s|\n", s, p) ); -} - -static void cleanfld(int n1, int n2); -static int refldbld(unsigned char *rec, unsigned char *fs); - -void -fldbld(void) -{ - register unsigned char *r, *fr; - Cell **p; - wchar_t wc, sep; - int i, n; - - if (donefld) - return; - if (!(recloc->tval & STR)) - getsval(recloc); - r = recloc->sval; /* was record! */ - fr = fields; - i = 0; /* number of fields accumulated here */ - if ((sep = **FS) != '\0' && (next(sep, *FS, n), (*FS)[n] != '\0')) { - /* it's a regular expression */ - i = refldbld(r, *FS); - } else if (sep == ' ') { - for (i = 0; ; ) { - while (*r == ' ' || *r == '\t' || *r == '\n') - r++; - if (*r == 0) - break; - i++; - if (i >= MAXFLD) - morefields(); - if (!(fldtab[i]->tval & DONTFREE)) - xfree(fldtab[i]->sval); - fldtab[i]->sval = fr; - fldtab[i]->tval = FLD | STR | DONTFREE; - next(wc, r, n); - do { - do - *fr++ = *r++; - while (--n); - next(wc, r, n); - } while (wc != ' ' && wc != '\t' && wc != '\n' && - wc != '\0'); - *fr++ = 0; - } - *fr = 0; - } else if (*r != 0) { /* if 0, it's a null field */ - for (;;) { - i++; - if (i >= MAXFLD) - morefields(); - if (!(fldtab[i]->tval & DONTFREE)) - xfree(fldtab[i]->sval); - fldtab[i]->sval = fr; - fldtab[i]->tval = FLD | STR | DONTFREE; - while (next(wc, r, n), - wc != sep && wc != '\n' && wc != '\0') { - /* \n always a separator */ - do - *fr++ = *r++; - while (--n); - } - *fr++ = '\0'; - if (wc == '\0') - break; - r += n; - } - *fr = 0; - } - /*if (i >= MAXFLD) - error(MM_ERROR, ":13:Record `%.20s...' has too many fields", - record);*/ - /* clean out junk from previous record */ - cleanfld(i, maxfld); - maxfld = i; - donefld = 1; - for (p = &fldtab[1]; p <= &fldtab[0]+maxfld; p++) - (void)is2number(0, *p); - setfval(nfloc, (Awkfloat) maxfld); - if (dbg) - for (p = &fldtab[0]; p <= &fldtab[0]+maxfld; p++) - pfmt(stdout, MM_INFO, ":14:field %d: |%s|\n", p-&fldtab[0], - (*p)->sval); -} - -static void cleanfld(int n1, int n2) /* clean out fields n1..n2 inclusive */ -{ - static unsigned char *nullstat = (unsigned char *) ""; - register Cell **p, **q; - - for (p = &fldtab[n2], q = &fldtab[n1]; p > q; p--) { - if (!((*p)->tval & DONTFREE)) - xfree((*p)->sval); - (*p)->tval = FLD | STR | DONTFREE; - (*p)->sval = nullstat; - } -} - -void newfld(int n) /* add field n (after end) */ -{ - /*if (n >= MAXFLD) - error(MM_ERROR, ":15:Creating too many fields", record);*/ - while (n >= MAXFLD) - morefields(); - cleanfld(maxfld, n); - maxfld = n; - setfval(nfloc, (Awkfloat) n); -} - -static int refldbld(unsigned char *rec, - unsigned char *fs) /* build fields from reg expr in FS */ -{ - unsigned char *fr; - int i; - fa *pfa; - - fr = fields; - *fr = '\0'; - if (*rec == '\0') - return 0; - pfa = makedfa(fs, 1); - dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, - fs) ); - pfa->notbol = 0; - for (i = 1; ; i++) { - if (i >= MAXFLD) - morefields(); - if (!(fldtab[i]->tval & DONTFREE)) - xfree(fldtab[i]->sval); - fldtab[i]->tval = FLD | STR | DONTFREE; - fldtab[i]->sval = fr; - dprintf( ("refldbld: i=%d\n", i) ); - if (nematch(pfa, rec)) { - pfa->notbol = REG_NOTBOL; - dprintf( ("match %s (%d chars\n", - patbeg, patlen) ); - strncpy((char*) fr, (char*) rec, patbeg-rec); - fr += patbeg - rec + 1; - *(fr-1) = '\0'; - rec = patbeg + patlen; - } else { - dprintf( ("no match %s\n", rec) ); - strcpy((char*) fr, (char*) rec); - pfa->notbol = 0; - break; - } - } - return i; -} - -void recbld(void) -{ - int i; - unsigned char *r, *p; - - if (donerec == 1) - return; - r = recdata; - for (i = 1; i <= *NF; i++) { - p = getsval(fldtab[i]); - while ((*r = *p++)) { - if (++r >= &recdata[recsize]) { - recsize += CHUNK; - growrec(&recdata, &recsize, recsize, &r, 1); - } - } - if (i < *NF) - for ((p = *OFS); (*r = *p++); ) { - if (++r >= &recdata[recsize]) { - recsize += CHUNK; - growrec(&recdata, &recsize, - recsize, &r, 1); - } - } - } - *r = '\0'; - dprintf( ("in recbld FS=%o, recloc=%lo\n", **FS, - (long)recloc) ); - recloc->tval = REC | STR | DONTFREE; - recloc->sval = record = recdata; - dprintf( ("in recbld FS=%o, recloc=%lo\n", **FS, - (long)recloc) ); - dprintf( ("recbld = |%s|\n", record) ); - donerec = 1; -} - -Cell *fieldadr(int n) -{ - if (n < 0) - error(MM_ERROR, ":17:Trying to access field %d", n); - while (n >= MAXFLD) - morefields(); - return(fldtab[n]); -} - -int errorflag = 0; -char errbuf[200]; - -static int been_here = 0; -static char - atline[] = ":18: at source line %d", - infunc[] = ":19: in function %s"; - -void -vyyerror(const char *msg, ...) -{ - extern unsigned char *curfname; - va_list args; - - if (been_here++ > 2) - return; - va_start(args, msg); - vpfmt(stderr, MM_ERROR, msg, args); - pfmt(stderr, MM_NOSTD, atline, lineno); - if (curfname != NULL) - pfmt(stderr, MM_NOSTD, infunc, curfname); - fprintf(stderr, "\n"); - errorflag = 2; - eprint(); - - va_end(args); -} - -void -yyerror(char *s) -{ - extern unsigned char /**cmdname,*/ *curfname; - static int been_here = 0; - - if (been_here++ > 2) - return; - pfmt(stderr, (MM_ERROR | MM_NOGET), "%s", s); - pfmt(stderr, MM_NOSTD, atline, lineno); - if (curfname != NULL) - pfmt(stderr, MM_NOSTD, infunc, curfname); - fprintf(stderr, "\n"); - errorflag = 2; - eprint(); -} - -/*ARGSUSED*/ -void fpecatch(int signo) -{ - error(MM_ERROR, ":20:Floating point exception"); -} - -extern int bracecnt, brackcnt, parencnt; -static void bcheck2(int n, int c1, int c2); - -void bracecheck(void) -{ - int c; - static int beenhere = 0; - - if (beenhere++) - return; - while ((c = awk_input()) != EOF && c != '\0') - bclass(c); - bcheck2(bracecnt, '{', '}'); - bcheck2(brackcnt, '[', ']'); - bcheck2(parencnt, '(', ')'); -} - -static void bcheck2(int n, int c1, int c2) -{ - if (n == 1) - pfmt(stderr, MM_ERROR, ":21:Missing %c\n", c2); - else if (n > 1) - pfmt(stderr, MM_ERROR, ":22:%d missing %c's\n", n, c2); - else if (n == -1) - pfmt(stderr, MM_ERROR, ":23:Extra %c\n", c2); - else if (n < -1) - pfmt(stderr, MM_ERROR, ":24:%d extra %c's\n", -n, c2); -} - -void -error(int flag, const char *msg, ...) -{ - int errline; - extern Node *curnode; - /*extern unsigned char *cmdname;*/ - va_list args; - - fflush(stdout); - va_start(args, msg); - vpfmt(stderr, flag, msg, args); - putc('\n', stderr); - - if (compile_time != 2 && NR && *NR > 0) { - pfmt(stderr, MM_INFO, - ":25:Input record number %g", *FNR); - if (strcmp((char*) *FILENAME, "-") != 0) - pfmt(stderr, MM_NOSTD, - ":26:, file %s", *FILENAME); - fprintf(stderr, "\n"); - } - errline = 0; - if (compile_time != 2 && curnode) - errline = curnode->lineno; - else if (compile_time != 2 && lineno) - errline = lineno; - if (errline) - pfmt(stderr, MM_INFO, ":27:Source line number %d\n", errline); - eprint(); - if (flag == MM_ERROR) { - if (dbg) - abort(); - exit(2); - } - va_end(args); -} - -static void eprint(void) /* try to print context around error */ -{ - unsigned char *p, *q, *r; - int c, episnul; - static int been_here = 0; - extern unsigned char ebuf[300], *ep; - - if (compile_time == 2 || compile_time == 0 || been_here++ > 0) - return; - episnul = ep > ebuf && ep[-1] == '\0'; - p = ep - 1 - episnul; - if (p > ebuf && *p == '\n') - p--; - for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) - ; - while (*p == '\n') - p++; - if (0 /* posix */) - pfmt(stderr, MM_INFO, ":28:Context is\n\t"); - else - pfmt(stderr, MM_INFO|MM_NOSTD, ":2228: context is\n\t"); - for (q=ep-1-episnul; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) - ; - for (r = q; r < ep; r++) { - if (*r != ' ' && *r != '\t' && *r != '\n') { - for ( ; p < q; p++) - if (*p) - putc(*p, stderr); - break; - } - } - fprintf(stderr, " >>> "); - for ( ; p < ep; p++) - if (*p) - putc(*p, stderr); - fprintf(stderr, " <<< "); - if (*ep) - while ((c = awk_input()) != '\n' && c != '\0' && c != EOF) { - putc(c, stderr); - bclass(c); - } - putc('\n', stderr); - ep = ebuf; -} - -void bclass(int c) -{ - switch (c) { - case '{': bracecnt++; break; - case '}': bracecnt--; break; - case '[': brackcnt++; break; - case ']': brackcnt--; break; - case '(': parencnt++; break; - case ')': parencnt--; break; - } -} - -double errcheck(double x, unsigned char *s) -{ - if (errno == EDOM) { - errno = 0; - error(MM_WARNING, ":29:%s argument out of domain", s); - x = 1; - } else if (errno == ERANGE) { - errno = 0; - error(MM_WARNING, ":30:%s result out of range", s); - x = 1; - } - return x; -} - -void PUTS(unsigned char *s) { - dprintf( ("%s\n", s) ); -} - -int isclvar(unsigned char *s) /* is s of form var=something? */ -{ - unsigned char *os = s; - - for ( ; *s; s++) - if (!(alnumchar(*s) || *s == '_')) - break; - return *s == '=' && s > os && *(s+1) != '=' && !digitchar(*os); -} - -int is2number(register unsigned char *s, Cell *p) -{ - unsigned char *after; - Awkfloat val; - - /* - * POSIX.2 says leading <blank>s are skipped and that - * <blank> is at least ' ' and '\t' and can include other - * characters, but not in the "POSIX" (aka "C") locale. - * - * The historic code skipped those two and newline. So, - * unless it's noticed by some test suite, we choose to - * keep things compatible. To be safe, reject the string - * if it starts with other white space characters since - * strtod() skips any form of white space. - * - * Permit similarly spelled trailing white space for - * compatibility. - */ - if (p != 0) - s = p->sval; - while (*s == ' ' || *s == '\t' || *s == '\n') - s++; - if (isspace(*s)) - return 0; - /* - * Reject hexadecimal numbers, infinity and NaN strings which - * are recognized by C99 strtod() implementations. - */ - switch (*s) { - case '0': - if (s[1] == 'x' || s[1] == 'X') - return 0; - break; - case 'i': - case 'I': - if (strncasecmp((char *)s, "inf", 3) == 0) - return 0; - break; - case 'n': - case 'N': - if (strncasecmp((char *)s, "NaN", 3) == 0) - return 0; - break; - } - val = strtod((char *)s, (char **)&after); - for (s = after; *s == ' ' || *s == '\t' || *s == '\n'; s++) - ; - if (*s != '\0') - return 0; - if (p != 0) { - p->fval = val; - p->tval |= NUM; - } - return 1; -} - -double -awk_atof(const char *s) -{ - wchar_t wc; - int n; - - while (*s) { - next(wc, s, n); - if (!(mb_cur_max > 1 ? iswspace(wc) : isspace(wc))) - break; - s += n; - } - /* - * Return 0 for hexadecimal numbers, infinity and NaN strings which - * are recognized by C99 atof() implementations. - */ - switch (*s) { - case '0': - if (s[1] == 'x' || s[1] == 'X') - return 0; - break; - case 'i': - case 'I': - if (strncasecmp(s, "inf", 3) == 0) - return 0; - break; - case 'n': - case 'N': - if (strncasecmp(s, "NaN", 3) == 0) - return 0; - break; - } - return atof(s); -} - -unsigned char *makerec(const unsigned char *data, int size) -{ - if (!(recloc->tval & DONTFREE)) - xfree(recloc->sval); - if (recsize < size) - growrec(&recdata, &recsize, size, NULL, 0); - record = recdata; - strcpy((char*)record, (char*)data); - recloc->sval = record; - recloc->tval = REC | STR | DONTFREE; - donerec = 1; donefld = 0; - return record; -} - -static void growrec(unsigned char **buf, int *bufsize, int newsize, - unsigned char **ptr, int bld) -{ - unsigned char *np, *op; - - op = *buf; - if ((np = realloc(op, *bufsize = newsize)) == 0) { - oflo: if (bld) - error(MM_ERROR, - ":16:Built giant record `%.20s...'", - *buf); - else - error(MM_ERROR, - ":12:Input record `%.20s...' too long", - *buf); - } - if (ptr && *ptr) - *ptr = &np[*ptr - op]; - if (record == op) - record = np; - if (recdata == op) { - recdata = np; - recsize = *bufsize; - if ((fields = realloc(fields, recsize)) == NULL) - goto oflo; - } - if (fldtab[0]->sval == op) - fldtab[0]->sval = np; - if (recloc->sval == op) - recloc->sval = np; - *buf = np; -} - -int -vpfmt(FILE *stream, long flags, const char *fmt, va_list ap) -{ - extern char *pfmt_label__; - int n = 0; - - if ((flags & MM_NOGET) == 0) { - if (*fmt == ':') { - do - fmt++; - while (*fmt != ':'); - fmt++; - } - } - if ((flags & MM_NOSTD) == 0) - n += fprintf(stream, "%s: ", pfmt_label__); - if ((flags & MM_ACTION) == 0 && isupper(*fmt&0377)) - n += fprintf(stream, "%c", tolower(*fmt++&0377)); - n += vfprintf(stream, fmt, ap); - return n; -} diff --git a/nawk/main.c b/nawk/main.c @@ -1,215 +0,0 @@ -/* - Changes by Gunnar Ritter, Freiburg i. Br., Germany, December 2002. - - Sccsid @(#)main.c 1.14 (gritter) 12/19/04> - */ -/* UNIX(R) Regular Expression Tools - - Copyright (C) 2001 Caldera International, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* copyright "%c%" */ - -/* from unixsrc:usr/src/common/cmd/awk/main.c /main/uw7_nj/2 */ -/* from RCS Header: main.c 1.3 91/08/12 */ - -#define DEBUG -#include <stdio.h> -#include <ctype.h> -#include <signal.h> -#include <pfmt.h> -#include <errno.h> -#include <string.h> -#include <locale.h> -#include <langinfo.h> -#include <libgen.h> - -#define CMDCLASS ""/*"UX:"*/ /* Command classification */ - -#include <locale.h> - -#include "awk.h" -#include "y.tab.h" - -int dbg = 0; -unsigned char *cmdname; /* gets argv[0] for error messages */ -extern FILE *yyin; /* lex input file */ -static FILE *awk_yyin; -extern FILE *yyout; -unsigned char *lexprog; /* points to program argument if it exists */ -unsigned char **start_delayed; /* first name=val argument delayed for BEGIN code */ -unsigned char **after_delayed; /* first argument after the delayed name=val's */ -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 */ - -#define MAXPFILE 100 -unsigned char *pfile[MAXPFILE]; /* program filenames from -f's */ -int npfile = 0; /* number of filenames */ -int curpfile = 0; /* current filename */ - -int mb_cur_max; /* MB_CUR_MAX, for acceleration */ - -extern const char badopen[]; - -int main(int argc, unsigned char *argv[], unsigned char *envp[]) -{ - unsigned char *fs = NULL; - char label[MAXLABEL+1]; /* Space for the catalogue label */ - - (void)setlocale(LC_COLLATE, ""); - (void)setlocale(LC_CTYPE, ""); - /*(void)setlocale(LC_MESSAGES, "");*/ - (void)setlocale(LC_NUMERIC, "POSIX"); /* redundant */ - mb_cur_max = MB_CUR_MAX; - cmdname = (unsigned char *)basename((char *)argv[0]); - (void)strcpy(label, CMDCLASS); - (void)strncat(label, (char*) cmdname, (MAXLABEL - sizeof(CMDCLASS) - 1)); - (void)setcat("uxawk"); - (void)setlabel(label); - /*version = (char*) gettxt(":31", "version Oct 11, 1989");*/ - if (argc == 1) { - if (0 /* posix */) - pfmt(stderr, MM_ERROR, ":32:Incorrect usage\n"); - pfmt(stderr, MM_ACTION | (0 /* posix */ ? 0 : MM_NOSTD), - ":210107:Usage: %s [-f programfile | 'program'] [-Ffieldsep] [-v var=value] [files]\n", - cmdname); - exit(1); - } - signal(SIGFPE, fpecatch); - awk_yyin = NULL; - yyout = stdout; - fldinit(); - syminit(); - while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') { - if (strcmp((char*) argv[1], "--") == 0) { /* explicit end of args */ - argc--; - argv++; - break; - } - switch (argv[1][1]) { - case 'f': /* next argument is program filename */ - if (npfile >= MAXPFILE) - error(MM_ERROR, ":106:Too many program filenames"); - if (argv[1][2] != '\0') { /* arg is -fname */ - pfile[npfile++] = &argv[1][2]; - } else { - argc--; - argv++; - if (argc <= 1) - error(MM_ERROR, ":34:No program filename"); - pfile[npfile++] = argv[1]; - } - 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 = (unsigned char *) "\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 = (unsigned char *) "\t"; - else if (argc > 1 && argv[1][0] != 0) - fs = &argv[1][0]; - } - if (fs == NULL || *fs == '\0') - error(MM_WARNING, ":35: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 -va=1 */ - if (!isclvar(&argv[1][2])) - error(MM_ERROR, ":105:malformed -v assignment"); - setclvar(&argv[1][2]); - } else if (--argc > 1 && isclvar((++argv)[1])) { - setclvar(argv[1]); - } else { - error(MM_ERROR, ":105:malformed -v assignment"); - } - break; - case 'd': - dbg = atoi((char *)&argv[1][2]); - if (dbg == 0) - dbg = 1; - pfmt(stdout, (MM_INFO | MM_NOGET), "%s %s\n", - cmdname, version); - break; - default: - pfmt(stderr, MM_WARNING, - ":36:Unknown option %s ignored\n", argv[1]); - break; - } - argc--; - argv++; - } - /* argv[1] is now the first argument */ - if (npfile == 0) { /* no -f; first argument is program */ - if (argc <= 1) - error(MM_ERROR, ":37:No program given"); - dprintf( ("program = |%s|\n", argv[1]) ); - lexprog = argv[1]; - argc--; - argv++; - } - /* hold leading name=val arguments until just after BEGIN */ - if (posix && argc > 1 && isclvar(argv[1])) { - start_delayed = &argv[0]; - do { - argv[0] = argv[1]; - argv++; - } while (--argc > 1 && isclvar(argv[1])); - after_delayed = &argv[0]; - } - compile_time = 1; - argv[0] = cmdname; /* put prog name at front of arglist */ - dprintf( ("argc=%d, argv[0]=%s\n", argc, argv[0]) ); - arginit(argc, argv); - envinit(envp); - yyparse(); - if (fs) - *FS = tostring(qstring(fs, '\0')); - dprintf( ("errorflag=%d\n", errorflag) ); - if (errorflag == 0) { - compile_time = 0; - (void)setlocale(LC_NUMERIC, ""); - run(winner); - } else - bracecheck(); - exit(errorflag); -} - -int pgetc(void) /* get program character */ -{ - int c; - - for (;;) { - if (awk_yyin == NULL) { - if (curpfile >= npfile) - return EOF; - if (!strcmp((char *)pfile[curpfile], "-")) - awk_yyin = stdin; - else if ((awk_yyin = fopen((char *) pfile[curpfile], "r")) == NULL) - error(MM_ERROR, badopen, - pfile[curpfile], strerror(errno)); - } - if ((c = getc(awk_yyin)) != EOF) - return c; - awk_yyin = NULL; - curpfile++; - } -} diff --git a/nawk/maketab.c b/nawk/maketab.c @@ -1,177 +0,0 @@ -/* - Changes by Gunnar Ritter, Freiburg i. Br., Germany, December 2002. - - Sccsid @(#)maketab.c 1.11 (gritter) 12/4/04> - */ -/* UNIX(R) Regular Expression Tools - - Copyright (C) 2001 Caldera International, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* copyright "%c%" */ - -/* from unixsrc:usr/src/common/cmd/awk/maketab.c /main/uw7_nj/1 */ -/* from RCS Header: maketab.c 1.2 91/06/25 */ -static const char sccsid[] = "@(#)maketab.c 1.11 (gritter) 12/4/04"; - -#include <stdio.h> -#include <string.h> -#include "awk.h" -#include "y.tab.h" - -struct xx -{ int token; - char *name; - char *pname; -} proc[] = { - { PROGRAM, "program", NULL }, - { BOR, "boolop", " || " }, - { AND, "boolop", " && " }, - { NOT, "boolop", " !" }, - { NE, "relop", " != " }, - { EQ, "relop", " == " }, - { LE, "relop", " <= " }, - { LT, "relop", " < " }, - { GE, "relop", " >= " }, - { GT, "relop", " > " }, - { ARRAY, "array", NULL }, - { INDIRECT, "indirect", "$(" }, - { SUBSTR, "substr", "substr" }, - { SUB, "sub", "sub" }, - { GSUB, "gsub", "gsub" }, - { INDEX, "sindex", "sindex" }, - { SPRINTF, "awsprintf", "sprintf " }, - { ADD, "arith", " + " }, - { MINUS, "arith", " - " }, - { MULT, "arith", " * " }, - { DIVIDE, "arith", " / " }, - { MOD, "arith", " % " }, - { UMINUS, "arith", " -" }, - { POWER, "arith", " **" }, - { PREINCR, "incrdecr", "++" }, - { POSTINCR, "incrdecr", "++" }, - { PREDECR, "incrdecr", "--" }, - { POSTDECR, "incrdecr", "--" }, - { CAT, "cat", " " }, - { PASTAT, "pastat", NULL }, - { PASTAT2, "dopa2", NULL }, - { MATCH, "matchop", " ~ " }, - { NOTMATCH, "matchop", " !~ " }, - { MATCHFCN, "matchop", "matchop" }, - { INTEST, "intest", "intest" }, - { PRINTF, "aprintf", "printf" }, - { PRINT, "print", "print" }, - { DELETE, "delete", "delete" }, - { SPLIT, "split", "split" }, - { ASSIGN, "assign", " = " }, - { ADDEQ, "assign", " += " }, - { SUBEQ, "assign", " -= " }, - { MULTEQ, "assign", " *= " }, - { DIVEQ, "assign", " /= " }, - { MODEQ, "assign", " %= " }, - { POWEQ, "assign", " ^= " }, - { CONDEXPR, "condexpr", " ?: " }, - { IF, "ifstat", "if(" }, - { WHILE, "whilestat", "while(" }, - { FOR, "forstat", "for(" }, - { DO, "dostat", "do" }, - { IN, "instat", "instat" }, - { NEXT, "jump", "next" }, - { EXIT, "jump", "exit" }, - { BREAK, "jump", "break" }, - { CONTINUE, "jump", "continue" }, - { RETURN, "jump", "ret" }, - { BLTIN, "bltin", "bltin" }, - { CALL, "call", "call" }, - { ARG, "arg", "arg" }, - { VARNF, "getnf", "NF" }, - { GETLINE, "getline", "getline" }, - { 0, "", "" }, -}; - -#define SIZE LASTTOKEN - FIRSTTOKEN + 1 -char *table[SIZE]; -char *names[SIZE]; - -int main(void) -{ - struct xx *p; - int i, n, tok; - char c; - FILE *fp; - char buf[100], name[100], def[100]; - - printf("#include <stdio.h>\n"); - printf("#include \"awk.h\"\n"); - printf("#include \"y.tab.h\"\n\n"); -/* printf("Cell *nullproc();\n"); - for (i = SIZE; --i >= 0; ) - names[i] = ""; - for (p=proc; p->token!=0; p++) - if (p == proc || strcmp(p->name, (p-1)->name)) - printf("extern Cell *%s();\n", p->name);*/ - - if ((fp = fopen("y.tab.h", "r")) == NULL) { - fprintf(stderr, "maketab can't open y.tab.h!\n"); - exit(1); - } - printf("static unsigned char *printname[%d] = {\n", SIZE); - i = 0; - while (fgets(buf, sizeof buf, fp) != NULL) { - if (*buf == '\n') - continue; - n = sscanf(buf, "%1c %s %s %d", &c, def, name, &tok); - if ((c != '#' || n != 4) && (strcmp(def,"define") != 0)) /* not a valid #define */ - continue; - if (strncmp(name, "YY", 2) == 0 || strncmp(name, "yy", 2) == 0) - continue; - if (tok < FIRSTTOKEN || tok > LASTTOKEN) { - continue; - /* - fprintf(stderr, "maketab funny token %d %s\n", tok, buf); - exit(1); - */ - } - names[tok-FIRSTTOKEN] = (char *) malloc(strlen(name)+1); - strcpy(names[tok-FIRSTTOKEN], name); - printf("\t(unsigned char *) \"%s\",\t/* %d */\n", name, tok); - i++; - } - printf("};\n\n"); - - for (p=proc; p->token!=0; p++) - 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("};\n\n"); - - printf("unsigned char *tokname(int n)\n"); /* print a tokname() function */ - - printf("{\n"); - printf(" static unsigned char buf[100];\n\n"); - printf(" if (n < FIRSTTOKEN || n > LASTTOKEN) {\n"); - printf(" snprintf((char *)buf, sizeof buf, \"token %%d\", n);\n"); - printf(" return buf;\n"); - printf(" }\n"); - printf(" return printname[n-257];\n"); - printf("}\n"); - exit(0); -} diff --git a/nawk/mkfile b/nawk/mkfile @@ -1,53 +0,0 @@ -BIN = nawk -OBJ = awk.lx.o b.o lib.o main.o parse.o proctab.o run.o tran.o \ - awk.g.o version.o -LOCAL_CFLAGS = -DSU3 -LOCAL_LDFLAGS = -lm -CLEAN_FILES = maketab maketab.o awk.g.c y.tab.h awk.lx.c proctab.c awk.1 -DEPS = libcommon libuxre lex yacc ed - -<$mkbuild/mk.common - -INSTALL_BIN = nawk -INSTALL_MAN1 = nawk.1 -INSTALL_SYMLINK = \ - nawk $BINDIR/awk \ - nawk.1 $MANDIR/man1/awk.1 - -nawk: $OBJ - -awk.g.c:Q: awk.g.y - echo YACC $target - $YACC -d awk.g.y - mv -f y.tab.c awk.g.c - -y.tab.h:Q: awk.g.c - (echo '1i'; echo '#include <inttypes.h>'; echo '.'; echo 'w';) | \ - $ED -s y.tab.h - -maketab:Q: maketab.o - echo CC $target - $CC $LDFLAGS maketab.o -o maketab - -proctab.c: maketab - ./maketab > proctab.c - -awk.lx.c:Q: awk.lx.l - echo LEX $target - $LEX -t awk.lx.l > awk.lx.c - -maketab.o:Q: maketab.c - echo CC $target - $CC -DIN_MAKETAB -c maketab.c - -awk.g.o: awk.h y.tab.h -awk.lx.o: awk.h y.tab.h -b.o: awk.h y.tab.h -lib.o: awk.h y.tab.h -main.o: awk.h y.tab.h -maketab.o: awk.h y.tab.h -parse.o: awk.h y.tab.h -proctab.o: awk.h y.tab.h -run.o: awk.h y.tab.h -tran.o: awk.h y.tab.h -version.o: awk.h y.tab.h diff --git a/nawk/nawk.1 b/nawk/nawk.1 @@ -1,585 +0,0 @@ -.\" -.\" Sccsid @(#)nawk.1 1.21 (gritter) 2/6/05 -.\" Derived from awk.1, Bell Labs: -.\" -.\" 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. -.TH NAWK 1 "2/6/05" "Heirloom Toolchest" "User Commands" -.SH NAME -nawk \- pattern scanning and processing language -.SH SYNOPSIS -.HP -.ad l -\fBnawk\fR -[\fB\-f \fIprogfile\fR | \fI'prog'\fR] -[\fB\-F\fIfieldsep\fR] -[\fB\-v \fIvar=value\fR] -[\fIfile . . .\fR] -.br -.ad b -.SH DESCRIPTION -.I Nawk -scans each input -.I file -for lines that match any of a set of patterns specified literally in -.IR prog -or in one or more files -specified as -.B \-f -.IR progfile . -With each pattern -there can be an associated action that will be performed -when a line of a -.I file -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 -.B \- -means the standard input. -Any -.IR file -of the form -.I var=value -is treated as an assignment, not a filename, -and is executed at the time it would have been opened if it were a filename -.RB ( /usr/5bin/s42/awk , -.BR /usr/5bin/posix/awk , -and -.B /usr/5bin/posix2001/awk -only). -The option -.B \-v -followed by -.I var=value -is an assignment to be done before -.I prog -is executed; -any number of -.B \-v -options may be present. -The -.B \-F -.IR fs -option defines the input field separator to be the regular expression -.IR fs. -.PP -An input line is normally made up of fields separated by white space, -or by regular expression -.BR FS . -The fields are denoted -.BR $1 , -.BR $2 , -\&..., while -.B $0 -refers to the entire line. -.PP -A pattern-action statement has the form -.IP -.IB pattern " { " action " }" -.PP -A missing -.BI { " action " } -means print the line; -a missing pattern always matches. -Pattern-action statements are separated by newlines or semicolons. -.PP -An action is a sequence of statements. -A statement can be one of the following: -.PP -.\".ta \w'\f(CWdelete array[subscript]'u -.RS -.nf -\fBif (\fI expression \fB)\fI statement \fR[ \fBelse\fI statement \fR] -\fBwhile (\fI expression \fB)\fI statement\fR -\fBfor (\fI expression \fB;\fI expression \fB;\fI expression \fB)\fI statement\fR -\fBfor (\fI var \fBin\fI array \fB)\fI statement\fR -\fBdo\fI statement \fBwhile (\fI expression \fB)\fR -\fBbreak\fR -\fBcontinue\fR -\fB{\fR [\fIstatement \fR...] \fB}\fR -\fIexpression\fR # commonly \fIvar \fB=\fI expression\fR -\fBprint\fR [\fIexpression-list\fR] [\fB>\fI expression\fR] -\fBprintf\fI format \fR[\fB,\fI expression-list\fR] [\fB>\fI expression\fR] -\fBnext \fR# skip remaining patterns on this input line -\fBdelete\fI array\fB[\fIsubscript\fB]\fR # delete an array element -\fBexit\fR [\fIexpr\fR] # exit immediately; status is \fIexpr\fR -\fBreturn\fR [\fIexpr\fR] -.fi -.RE -.br -.DT -.PP -Statements are terminated by -semicolons, newlines or right braces. -An empty -.I expression-list -stands for -.BR $0 . -String constants are quoted \&\f(CW"\ "\fR, -with the usual C escapes recognized within. -Expressions take on string or numeric values as appropriate, -and are built using the operators -.B + \- * / % ^ -(exponentiation), and concatenation (indicated by white space). -The operators -.B ! ++ \-\- += \-= *= -.B /= %= ^= > >= < -.B <= == != ?: -are also available in expressions. -Variables may be scalars, array elements -(denoted \fIx\fB[\fIi\fB]\fR) -or fields. -Variables are initialized to the null string. -Array subscripts may be any string, -not necessarily numeric; -this allows for a form of associative memory. -Multiple subscripts such as -\fB[\fIi\fB,\fIj\fB,\fIk\fB]\fR -are permitted; the constituents are concatenated, -separated by the value of -.BR SUBSEP . -.PP -The -.B print -statement prints its arguments on the standard output -(or on a file if -.BI > file -or -.BI >> file -is present or on a pipe if -.BI | cmd -is present), separated by the current output field separator, -and terminated by the output record separator. -.I file -and -.I cmd -may be literal names or parenthesized expressions; -identical string values in different statements denote -the same open file. -The -.B printf -statement formats its expression list according to the format -(see -.IR printf (3)) . -The built-in function -.BI close( expr ) -closes the file or pipe -.IR expr . -.PP -The mathematical functions -.BR exp , -.BR log , -.BR sqrt , -.BR sin , -.BR cos , -and -.BR atan2 -are built in. -Other built-in functions: -.\".TF length -.TP -.B gsub -same as -.B sub -except that all occurrences of the regular expression -are replaced; -.B sub -and -.B gsub -return the number of replacements. -.TP -.BI index( s , " t" ) -the position in -.I s -where the string -.I t -occurs, or 0 if it does not. -.TP -.B int -truncates to an integer value -.TP -.B length -the length of its argument -taken as a string, -or of -.B $0 -if no argument. -.TP -.BI match( s , " r" ) -the position in -.I s -where the regular expression -.I r -occurs, or 0 if it does not. -The variables -.B RSTART -and -.B RLENGTH -are set to the position and length of the matched string. -.TP -.B rand -random number on (0,1) -.TP -\fBsplit(\fIs\fB, \fIa\fB, \fIfs\fB)\fR -splits the string -.I s -into array elements -.IB a [1] , -.IB a [2] , -\&..., -.IB a [ n ] , -and returns -.IR n . -The separation is done with the regular expression -.I fs -or with the field separator -.B FS -if -.I fs -is not given. -.TP -\fBsprintf(\fIfmt\fB, \fIexpr\fB, \fI...\fB)\fR -the string resulting from formatting -.I expr ... -according to the -.IR printf (3) -format -.I fmt -.TP -.B srand -sets seed for -.B rand -and returns the previous seed. -.TP -\fBsub(\fIr\fB, \fIt\fB, \fIs\fB)\fR -substitutes -.I t -for the first occurrence of the regular expression -.I r -in the string -.IR s . -If -.I s -is not given, -.B $0 -is used. -.TP -\fBsubstr(\fIs\fB, \fIm\fB, \fIn\fB)\fR -the -.IR n -character -substring of -.I s -that begins at position -.IR m -counted from 1. -.TP -.BI system( cmd ) -executes -.I cmd -and returns its exit status -.TP -.BI tolower( str ) -returns a copy of -.I str -with all upper-case characters translated to their -corresponding lower-case equivalents. -.TP -.BI toupper( str ) -returns a copy of -.I str -with all lower-case characters translated to their -corresponding upper-case equivalents. -.PD -.PP -The ``function'' -.B getline -sets -.B $0 -to the next input record from the current input file; -.B getline -.BI < file -sets -.B $0 -to the next record from -.IR file . -.B getline -.I x -sets variable -.I x -instead. -Finally, -.IB cmd " |getline" -pipes the output of -.I cmd -into -.BR getline ; -each call of -.B getline -returns the next line of output from -.IR cmd . -In all cases, -.B getline -returns 1 for a successful input, -0 for end of file, and \-1 for an error. -.PP -Additional functions may be defined -(at the position of a pattern-action statement) thus: -.IP -\fBfunction \fIfoo\fB(\fIa\fB, \fIb\fB, \fIc\fB) -{ \fI...\fB; return \fIx\fB }\fR -.PP -or: -.IP -\fBfunc \fIfoo\fB(\fIa\fB, \fIb\fB, \fIc\fB) -{ \fI...\fB; return \fIx\fB }\fR -.PP -Parameters are passed by value if scalar and by reference if array name; -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. -.PP -Patterns are arbitrary Boolean combinations -(with -.BR "! || &&" ) -of regular expressions and -relational expressions. -Regular expressions are -full regular expressions with -.B /usr/5bin/nawk -and -extended regular expressions with -.BR /usr/5bin/s42/awk , -.BR /usr/5bin/posix/awk , -and -.BR /usr/5bin/posix2001/awk ; -both are as described in -.IR egrep (1). -Isolated regular expressions -in a pattern apply to the entire line. -Regular expressions may also occur in -relational expressions, using the operators -.BR ~ -and -.BR !~ . -.BI / re / -is a constant regular expression; -any string (constant or variable) may be used -as a regular expression, except in the position of an isolated regular expression -in a pattern. -For -.BR /usr/5bin/posix2001/awk , -regular expressions may be part of arithmetic expressions. -.PP -A pattern may consist of two patterns separated by a comma; -in this case, the action is performed for all lines -from an occurrence of the first pattern -though an occurrence of the second. -.PP -A relational expression is one of the following: -.IP -.I expression matchop regular-expression -.br -.I expression relop expression -.br -.IB expression " in " array-name -.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 -.B ~ -(matches) -or -.B !~ -(does not match). -A conditional is an arithmetic expression, -a relational expression, -or a Boolean combination -of these. -.PP -The special patterns -.B BEGIN -and -.B END -may be used to capture control before the first input line is read -and after the last. -.B BEGIN -and -.B END -do not combine with other patterns. -.PP -Variable names with special meanings: -.\".TF FILENAME -.TP 10 -.B ARGC -argument count, assignable -.TP 10 -.B ARGV -argument array, assignable; -non-null members are taken as filenames -.TP 10 -.B CONVFMT -.RB ( /usr/5bin/s42/awk , -.BR /usr/5bin/posix2001/awk , -and -.B /usr/5bin/posix/awk -only) -conversion format used when converting numbers -(default -.BR "%.6g" ) -.TP 10 -.B ENVIRON -array of environment variables; subscripts are names. -.TP 10 -.B FILENAME -the name of the current input file -.TP 10 -.B FNR -ordinal number of the current record in the current file -.TP 10 -.B FS -regular expression used to separate fields; also settable -by option -.BI \-F fs. -.TP 10 -.BR NF -number of fields in the current record -.TP 10 -.B NR -ordinal number of the current record -.TP 10 -.B OFMT -output format for numbers (default -.BR "%.6g" ) -.TP 10 -.B OFS -output field separator (default blank) -.TP 10 -.B ORS -output record separator (default newline) -.TP 10 -.B RS -input record separator (default newline) -.TP 10 -.B SUBSEP -separates multiple subscripts (default 034) -.PD -.SH EXAMPLES -.TP -.nf -length($0) > 72 -.br -.fi -Print lines longer than 72 characters. -.TP -.nf -{ print $2, $1 } -.br -.fi -Print first two fields in opposite order. -.PP -.nf -BEGIN { FS = ",[ \et]*|[ \et]+" } - { print $2, $1 } -.br -.fi -.ns -.IP -Same, with input fields separated by comma and/or blanks and tabs. -.PP -.nf - { s += $1 } -END { print "sum is", s, " average is", s/NR } -.fi -.br -.ns -.IP -Add up first column, print sum and average. -.TP -.nf -/start/, /stop/ -.br -.fi -Print all lines between start/stop pairs. -.PP -.nf -BEGIN { # Simulate echo(1) - for (i = 1; i < ARGC; i++) printf "%s ", ARGV[i] - printf "\en" - exit } -.fi -.br -.SH "ENVIRONMENT VARIABLES" -.TP -.BR LANG ", " LC_ALL -See -.IR locale (7). -.TP -.B LC_COLLATE -Affects the collation order for range expressions, -equivalence classes, and collation symbols -in regular expressions -as well as string comparison. -.TP -.B LC_CTYPE -Determines the mapping of bytes to characters, -the availability and composition of character classes -in regular expressions, -and the case mapping for the toupper() and tolower() functions. -.TP -.B LC_NUMERIC -Determine the radix character used when interpreting numeric input, -performing conversions between numeric and string values -and formatting numeric output. -Regardless of locale, the period character -(the decimal-point character of the C locale) -is the decimal-point character recognized in processing awk programs. -.SH SEE ALSO -egrep(1), -lex(1), -oawk(1), -sed(1), -printf(3), -locale(7) -.br -A. V. Aho, B. W. Kernighan, P. J. Weinberger, -.I -The AWK Programming Language, -Addison-Wesley, 1988. ISBN 0-201-07981-X -.SH NOTES -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 -\&\fB""\fR to it. -.\".sp -.\"The scope rules for variables in functions are a botch; -.\"the syntax is worse. -.PP -The LC_COLLATE variable has currently no effect in regular expressions. -Ranges in bracket expressions are ordered -as byte values in single-byte locales -and as wide character values in multibyte locales; -equivalence classes match the given character only, -and multi-character collating elements are not available. diff --git a/nawk/parse.c b/nawk/parse.c @@ -1,248 +0,0 @@ -/* - Changes by Gunnar Ritter, Freiburg i. Br., Germany, December 2002. - - Sccsid @(#)parse.c 1.7 (gritter) 12/4/04> - */ -/* UNIX(R) Regular Expression Tools - - Copyright (C) 2001 Caldera International, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* copyright "%c%" */ - -/* from unixsrc:usr/src/common/cmd/awk/parse.c /main/uw7_nj/1 */ -/* from RCS Header: parse.c 1.2 91/06/25 */ - -#define DEBUG -#include <stdio.h> -#include <string.h> -#include <pfmt.h> -#include "awk.h" -#include "y.tab.h" - -Node *nodealloc(int n) -{ - register Node *x; - x = (Node *) malloc(sizeof(Node) + (n-1)*sizeof(Node *)); - if (x == NULL) - error(MM_ERROR, outofspace, "nodealloc"); - x->nnext = NULL; - x->lineno = lineno; - return(x); -} - -Node *exptostat(Node *a) -{ - a->ntype = NSTAT; - return(a); -} - -Node *node1(int a, Node *b) -{ - register Node *x; - x = nodealloc(1); - x->nobj = a; - x->narg[0]=b; - return(x); -} - -Node *node2(int a, Node *b, Node *c) -{ - register Node *x; - x = nodealloc(2); - x->nobj = a; - x->narg[0] = b; - x->narg[1] = c; - return(x); -} - -Node *node3(int a, Node *b, Node *c, Node *d) -{ - register Node *x; - x = nodealloc(3); - x->nobj = a; - x->narg[0] = b; - x->narg[1] = c; - x->narg[2] = d; - return(x); -} - -Node *node4(int a, Node *b, Node *c, Node *d, Node *e) -{ - register Node *x; - x = nodealloc(4); - x->nobj = a; - x->narg[0] = b; - x->narg[1] = c; - x->narg[2] = d; - x->narg[3] = e; - return(x); -} - -Node *stat3(int a, Node *b, Node *c, Node *d) -{ - register Node *x; - x = node3(a,b,c,d); - x->ntype = NSTAT; - return(x); -} - -Node *op2(int a, Node *b, Node *c) -{ - register Node *x; - x = node2(a,b,c); - x->ntype = NEXPR; - return(x); -} - -Node *op1(int a, Node *b) -{ - register Node *x; - x = node1(a,b); - x->ntype = NEXPR; - return(x); -} - -Node *stat1(int a, Node *b) -{ - register Node *x; - x = node1(a,b); - x->ntype = NSTAT; - return(x); -} - -Node *op3(int a, Node *b, Node *c, Node *d) -{ - register Node *x; - x = node3(a,b,c,d); - x->ntype = NEXPR; - return(x); -} - -Node *op4(int a, Node *b, Node *c, Node *d, Node *e) -{ - register Node *x; - x = node4(a,b,c,d,e); - x->ntype = NEXPR; - return(x); -} - -Node *stat2(int a, Node *b, Node *c) -{ - register Node *x; - x = node2(a,b,c); - x->ntype = NSTAT; - return(x); -} - -Node *stat4(int a, Node *b, Node *c, Node *d, Node *e) -{ - register Node *x; - x = node4(a,b,c,d,e); - x->ntype = NSTAT; - return(x); -} - -Node *valtonode(Cell *a, int b) -{ - register Node *x; - - a->ctype = OCELL; - a->csub = b; - x = node1(0, (Node *) a); - x->ntype = NVALUE; - return(x); -} - -Node *rectonode(void) -{ - /* return valtonode(lookup("$0", symtab), CFLD); */ - return valtonode(recloc, CFLD); -} - -Node *makearr(Node *p) -{ - Cell *cp; - - if (isvalue(p)) { - cp = (Cell *) (p->narg[0]); - if (isfunc(cp)) - vyyerror(":38:%s is a function, not an array", - cp->nval); - else if (!isarr(cp)) { - xfree(cp->sval); - cp->sval = (unsigned char *) makesymtab(NSYMTAB); - cp->tval = ARR; - } - } - return p; -} - -Node *pa2stat(Node *a,Node *b,Node *c) -{ - register Node *x; - x = node4(PASTAT2, a, b, c, (Node *) paircnt); - paircnt++; - x->ntype = NSTAT; - return(x); -} - -Node *linkum(Node *a,Node *b) -{ - register Node *c; - - if (errorflag) /* don't link things that are wrong */ - return a; - if (a == NULL) return(b); - else if (b == NULL) return(a); - for (c = a; c->nnext != NULL; c = c->nnext) - ; - c->nnext = b; - return(a); -} - -void defn(Cell *v, /* turn on FCN bit in definition */ - Node *vl, Node *st) /* arglist, body of function */ -{ - Node *p; - int n; - - if (isarr(v)) { - vyyerror(":39:`%s' is an array name and a function name", - v->nval); - return; - } - v->tval = FCN; - v->sval = (unsigned char *) st; - n = 0; /* count arguments */ - for (p = vl; p; p = p->nnext) - n++; - v->fval = n; - dprintf( ("defining func %s (%d args)\n", v->nval, n) ); -} - -int isarg(const char *s) /* is s in argument list for current function? */ -{ - extern Node *arglist; - Node *p = arglist; - int n; - - for (n = 0; p != 0; p = p->nnext, n++) - if (strcmp((char *)((Cell *)(p->narg[0]))->nval, s) == 0) - return n; - return -1; -} diff --git a/nawk/run.c b/nawk/run.c @@ -1,1962 +0,0 @@ -/* - Changes by Gunnar Ritter, Freiburg i. Br., Germany, December 2002. - - Sccsid @(#)run.c 1.33 (gritter) 12/25/06> - */ -/* UNIX(R) Regular Expression Tools - - Copyright (C) 2001 Caldera International, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to: - Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* copyright "%c%" */ - - -/* from unixsrc:usr/src/common/cmd/awk/run.c /main/uw7_nj/1 */ -/* from RCS Header: run.c 1.3 91/08/12 */ - -#define tempfree(x,s) if (istemp(x)) tfree(x,s); else - -/* #define execute(p) (isvalue(p) ? (Cell *)((p)->narg[0]) : r_execute(p)) */ -#define execute(p) r_execute((Node *)p) - -#define DEBUG -#include <math.h> -#include <stdio.h> -#include <ctype.h> -#include <setjmp.h> -#include <pfmt.h> -#include <string.h> -#include <errno.h> -#include <wctype.h> -#include <inttypes.h> -#include <time.h> -#include "awk.h" -#include "y.tab.h" - -jmp_buf env; - -#define getfval(p) (((p)->tval & (ARR|FLD|REC|NUM)) == NUM ? (p)->fval : r_getfval(p)) -#define getsval(p) (((p)->tval & (ARR|FLD|REC|STR)) == STR ? (p)->sval : r_getsval(p)) - -static void tfree(register Cell *a, char *s); - -#define PA2NUM 29 -int pairstack[PA2NUM]; -long paircnt; -Node *winner = NULL; -Cell *tmps; - -static Cell truecell ={ OBOOL, BTRUE, 0, 0, 1.0, NUM }; -Cell *true = &truecell; -static Cell falsecell ={ OBOOL, BFALSE, 0, 0, 0.0, NUM }; -Cell *false = &falsecell; -static Cell breakcell ={ OJUMP, JBREAK, 0, 0, 0.0, NUM }; -Cell *jbreak = &breakcell; -static Cell contcell ={ OJUMP, JCONT, 0, 0, 0.0, NUM }; -Cell *jcont = &contcell; -static Cell nextcell ={ OJUMP, JNEXT, 0, 0, 0.0, NUM }; -Cell *jnext = &nextcell; -static Cell exitcell ={ OJUMP, JEXIT, 0, 0, 0.0, NUM }; -Cell *jexit = &exitcell; -static Cell retcell ={ OJUMP, JRET, 0, 0, 0.0, NUM }; -Cell *jret = &retcell; -static Cell tempcell ={ OCELL, CTEMP, 0, 0, 0.0, NUM }; - -Node *curnode = NULL; /* the node being executed, for debugging */ - -static const char - restoobig[] = ":40:%s() result %.20s too big", - notarray[] = ":41:%s is not an array", - ioerror[] = ":42:I/O error occurred on %s"; -const char - illstat[] = ":43:Illegal statement"; - -extern const char readvofid[], readvof[], badopen[]; - -static int growsprintf(unsigned char **, unsigned char **, - int *, const char *, ...); -static void growbuf(unsigned char **buf, int *bufsize, int incr, - unsigned char **ptr, const char *fn); -static void closeall(void); -static void caseconv(unsigned char *s, wint_t (*conv)(wint_t)); - -int run(Node *a) -{ - execute(a); - closeall(); - return 0; -} - -Cell *r_execute(Node *u) -{ - register Cell *(*proc)(Node **, int); - register Cell *x; - register Node *a; - - if (u == NULL) - return(true); - for (a = u; ; a = a->nnext) { - curnode = a; - if (isvalue(a)) { - x = (Cell *) (a->narg[0]); - if ((x->tval & FLD) && !donefld) - fldbld(); - else if ((x->tval & REC) && !donerec) - recbld(); - return(x); - } - if (notlegal(a->nobj)) /* probably a Cell* but too risky to print */ - error(MM_ERROR, illstat); - proc = proctab[a->nobj-FIRSTTOKEN]; - x = (*proc)(a->narg, a->nobj); - if ((x->tval & FLD) && !donefld) - fldbld(); - else if ((x->tval & REC) && !donerec) - recbld(); - if (isexpr(a)) - return(x); - /* a statement, goto next statement */ - if (isjump(x)) - return(x); - if (a->nnext == (Node *)NULL) - return(x); - tempfree(x, "execute"); - } -} - - -Cell *program(register Node **a, int n) -{ - register Cell *x = 0; - - if (setjmp(env) != 0) - goto ex; - if (a[0]) { /* BEGIN */ - x = execute(a[0]); - if (isexit(x)) - return(true); - if (isjump(x)) - error(MM_ERROR, - ":44:Illegal break, continue or next from BEGIN"); - if(x != 0) { tempfree(x, ""); } - } - loop: - if (a[1] || a[2]) - while (getrec(&record, &recsize) > 0) { - x = execute(a[1]); - if (isexit(x)) - break; - if(x != 0) { tempfree(x, ""); } - } - ex: - if (setjmp(env) != 0) - goto ex1; - if (a[2]) { /* END */ - x = execute(a[2]); - if (iscont(x)) /* read some more */ - goto loop; - if (isbreak(x) || isnext(x)) - error(MM_ERROR, ":45:Illegal break or next from END"); - if(x != 0) { tempfree(x, ""); } - } - ex1: - return(true); -} - -struct Frame { - int nargs; /* number of arguments in this call */ - Cell *fcncell; /* pointer to Cell for function */ - Cell **args; /* pointer to array of arguments after execute */ - Cell *retval; /* return value */ -}; - -#define NARGS 30 - -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 */ - -Cell *call(Node **a, int n) -{ - static Cell newcopycell = { OCELL, CCOPY, 0, (unsigned char *) "", 0.0, NUM|STR|DONTFREE }; - int i, ncall, ndef; - Node *x; - Cell *args[NARGS], *oargs[NARGS], *y, *z, *fcn; - unsigned char *s; - - fcn = execute(a[0]); /* the function itself */ - s = fcn->nval; - if (!isfunc(fcn)) - error(MM_ERROR, ":46:Calling undefined function %s", s); - if (frame == NULL) { - fp = frame = (struct Frame *) calloc(nframe += 100, sizeof(struct Frame)); - if (frame == NULL) - error(MM_ERROR, ":47: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=%ld\n", s, - ncall, ndef, (long)(fp-frame)) ); - if (ncall > ndef) { - if (ncall == 1) - error(MM_WARNING, ":48:Function %s called with 1 arg, uses only %d", - s, ndef); - else - error(MM_WARNING, ":49:Function %s called with %d args, uses only %d", - s, ncall, ndef); - } - if (ncall + ndef > NARGS) - error(MM_ERROR, ":50: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=%ld:\n", i, - (long)(fp-frame)) ); - y = execute(x); - oargs[i] = y; - dprintf( ("args[%d]: %s %f <%s>, t=%o\n", - i, y->nval, y->fval, isarr(y) ? - "(array)" : (char*) y->sval, y->tval) ); - if (isfunc(y)) - error(MM_ERROR, ":51:Cannot use function %s as argument in %s", - y->nval, s); - if (isarr(y)) - args[i] = y; /* arrays by ref */ - else - args[i] = copycell(y); - tempfree(y, "callargs"); - } - for ( ; i < ndef; i++) { /* add null args for ones not provided */ - args[i] = gettemp("nullargs"); - *args[i] = newcopycell; - } - fp++; /* now ok to up frame */ - if (fp >= frame + nframe) { - int dfp = fp - frame; /* old index */ - frame = (struct Frame *) - realloc(frame, (nframe += 100) * sizeof(struct Frame)); - if (frame == NULL) - error(MM_ERROR, ":52:Out of space for stack frames in %s", s); - fp = frame + dfp; - } - fp->fcncell = fcn; - fp->args = args; - fp->nargs = ndef; /* number defined with (excess are locals) */ - fp->retval = gettemp("retval"); - - dprintf( ("start exec of %s, fp=%ld\n", s, (long)(fp-frame)) ); - y = execute((Node *)(fcn->sval)); /* execute body */ - dprintf( ("finished exec of %s, fp=%ld\n", s, (long)(fp-frame)) ); - - for (i = 0; i < ndef; i++) { - Cell *t = fp->args[i]; - if (isarr(t)) { - if (t->csub == CCOPY) { - if (i >= ncall) { - freesymtab(t); - t->csub = CTEMP; - } else { - oargs[i]->tval = t->tval; - oargs[i]->tval &= ~(STR|NUM|DONTFREE); - oargs[i]->sval = t->sval; - tempfree(t, "oargsarr"); - } - } - } else if (t != y) { /* kludge to prevent freeing twice */ - t->csub = CTEMP; - tempfree(t, "fp->args"); - } - } - tempfree(fcn, "call.fcn"); - if (isexit(y) || isnext(y)) - return y; - tempfree(y, "fcn ret"); /* this can free twice! */ - z = fp->retval; /* return value */ - dprintf( ("%s returns %g |%s| %o\n", s, getfval(z), - getsval(z), z->tval) ); - fp--; - return(z); -} - -Cell *copycell(Cell *x) /* make a copy of a cell in a temp */ -{ - Cell *y; - - y = gettemp("copycell"); - y->csub = CCOPY; /* prevents freeing until call is over */ - y->nval = x->nval; - y->sval = x->sval ? tostring(x->sval) : NULL; - y->fval = x->fval; - y->tval = x->tval & ~(CON|FLD|REC|DONTFREE); /* copy is not constant or field */ - /* is DONTFREE right? */ - return y; -} - -/*ARGSUSED2*/ -Cell *arg(Node **a, int nnn) -{ - int n; - - n = (intptr_t) a[0]; /* argument number, counting from 0 */ - dprintf( ("arg(%d), fp->nargs=%d\n", n, fp->nargs) ); - if (n+1 > fp->nargs) - error(MM_ERROR, ":53:Argument #%d of function %s was not supplied", - n+1, fp->fcncell->nval); - return fp->args[n]; -} - -static int in_loop = 0; /* Flag : are we in a [while|do|for] loop ? */ - -Cell *jump(Node **a, int n) -{ - register Cell *y; - - switch (n) { - case EXIT: - if (a[0] != NULL) { - y = execute(a[0]); - errorflag = getfval(y); - tempfree(y, ""); - } - longjmp(env, 1); - case RETURN: - if (a[0] != NULL) { - y = execute(a[0]); - if ((y->tval & (STR|NUM)) == (STR|NUM)) { - setsval(fp->retval, getsval(y)); - fp->retval->fval = getfval(y); - fp->retval->tval |= NUM; - } - else if (y->tval & STR) - setsval(fp->retval, getsval(y)); - else if (y->tval & NUM) - setfval(fp->retval, getfval(y)); - tempfree(y, ""); - } - return(jret); - case NEXT: - return(jnext); - case BREAK: - if (posix && !in_loop) - error(MM_ERROR, ":101:break-statement outside of a loop"); - return(jbreak); - case CONTINUE: - if (posix && !in_loop) - error(MM_ERROR, ":102:continue-statement outside of a loop"); - return(jcont); - default: /* can't happen */ - error(MM_ERROR, ":54:Illegal jump type %d", n); - /*NOTREACHED*/ - return 0; - } -} - -Cell *getline(Node **a, int n) -{ - /* a[0] is variable, a[1] is operator, a[2] is filename */ - register Cell *r, *x; - unsigned char *buf = NULL; - int bufsize = 0; - FILE *fp; - - fflush(stdout); /* in case someone is waiting for a prompt */ - r = gettemp(""); - if (a[1] != NULL) { /* getline < file */ - x = execute(a[2]); /* filename */ - if ((intptr_t) a[1] == '|') /* input pipe */ - a[1] = (Node *) LE; /* arbitrary flag */ - fp = openfile((intptr_t) a[1], getsval(x)); - tempfree(x, ""); - if (fp == NULL) - n = -1; - else - n = readrec(&buf, &bufsize, fp); - if (n <= 0) { - ; - } else if (a[0] != NULL) { /* getline var <file */ - setsval(execute(a[0]), buf); - } else { /* getline <file */ - makerec(buf, bufsize); - } - } else { /* bare getline; use current input */ - if (a[0] == NULL) /* getline */ - n = getrec(&record, &recsize); - else { /* getline var */ - n = getrec(&buf, &bufsize); - setsval(execute(a[0]), buf); - } - } - setfval(r, (Awkfloat) n); - if (bufsize) - free(buf); - return r; -} - -Cell *getnf(register Node **a, int n) -{ - if (donefld == 0) - fldbld(); - return (Cell *) a[0]; -} - -Cell *array(register Node **a, int n) -{ - register Cell *x, *y, *z; - register unsigned char *s; - register Node *np; - unsigned char *buf = NULL; - int bufsz = 0, subseplen, len = 1, l; - - x = execute(a[0]); /* Cell* for symbol table */ - subseplen = strlen((char *)*SUBSEP); - growbuf(&buf, &bufsz, CHUNK, NULL, "array"); - buf[0] = 0; - for (np = a[1]; np; np = np->nnext) { - y = execute(np); /* subscript */ - s = getsval(y); - len += (l = strlen((char *)s) + subseplen); - if (len >= bufsz) - growbuf(&buf, &bufsz, l, NULL, "array"); - strcat((char*)buf, (char*)s); - if (np->nnext) - strcat((char*)buf, (char*)*SUBSEP); - tempfree(y, ""); - } - if (!isarr(x)) { - dprintf( ("making %s into an array\n", x->nval) ); - if (freeable(x)) - xfree(x->sval); - x->tval &= ~(STR|NUM|DONTFREE); - x->tval |= ARR; - x->sval = (unsigned char *) makesymtab(NSYMTAB); - } - z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval); - z->ctype = OCELL; - z->csub = CVAR; - tempfree(x, ""); - free(buf); - return(z); -} - -Cell *delete(Node **a, int n) -{ - Cell *x, *y; - Node *np; - unsigned char *buf = NULL, *s; - int bufsz = 0, subseplen, len = 1, l; - - x = execute(a[0]); /* Cell* for symbol table */ - if (!isarr(x)) - return true; - subseplen = strlen((char *)*SUBSEP); - growbuf(&buf, &bufsz, CHUNK, NULL, "delete"); - buf[0] = 0; - for (np = a[1]; np; np = np->nnext) { - y = execute(np); /* subscript */ - s = getsval(y); - len += (l = strlen((char *)s) + subseplen); - if (len >= bufsz) - growbuf(&buf, &bufsz, l, NULL, "delete"); - strcat((char*)buf, (char*)s); - if (np->nnext) - strcat((char*)buf, (char*)*SUBSEP); - tempfree(y, ""); - } - freeelem(x, buf); - tempfree(x, ""); - free(buf); - return true; -} - -Cell *intest(Node **a, int n) -{ - register Cell *x, *ap, *k; - Node *p; - unsigned char *s; - unsigned char *buf = NULL; - int bufsz = 0, subseplen, len = 1, l; - - ap = execute(a[1]); /* array name */ - if (!isarr(ap)) - error(MM_ERROR, notarray, ap->nval); - subseplen = strlen((char *)*SUBSEP); - growbuf(&buf, &bufsz, CHUNK, NULL, "intest"); - buf[0] = 0; - for (p = a[0]; p; p = p->nnext) { - x = execute(p); /* expr */ - s = getsval(x); - len += (l = strlen((char *)s) + subseplen); - if (len >= bufsz) - growbuf(&buf, &bufsz, l, NULL, "array"); - strcat((char *)buf, (char*)s); - tempfree(x, ""); - if (p->nnext) - strcat((char *)buf, (char*)*SUBSEP); - } - k = lookup(buf, (Array *) ap->sval); - tempfree(ap, ""); - free(buf); - if (k == NULL) - return(false); - else - return(true); -} - - -Cell *matchop(Node **a, int n) -{ - register Cell *x, *y; - register unsigned char *s, *t; - register int i; - fa *pfa; - int (*mf)(void *, unsigned char *) = match, mode = 0; - - if (n == MATCHFCN) { - mf = pmatch; - mode = 1; - } - x = execute(a[1]); - s = getsval(x); - if (a[0] == 0) - i = (*mf)(a[2], s); - else { - y = execute(a[2]); - t = getsval(y); - pfa = makedfa(t, mode); - i = (*mf)(pfa, s); - tempfree(y, ""); - } - tempfree(x, ""); - if (n == MATCHFCN) { - int start, length; - if (patlen < 0) { - start = 0; - length = patlen; - } else { - start = chrdist(s, patbeg); - length = chrdist(patbeg, &patbeg[patlen - 1]); - } - setfval(rstartloc, (Awkfloat) start); - setfval(rlengthloc, (Awkfloat) length); - x = gettemp(""); - x->tval = NUM; - x->fval = start; - return x; - } else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0)) - return(true); - else - return(false); -} - - -Cell *boolop(Node **a, int n) -{ - register Cell *x, *y; - register int i; - - x = execute(a[0]); - i = istrue(x); - tempfree(x, ""); - switch (n) { - case BOR: - if (i) return(true); - y = execute(a[1]); - i = istrue(y); - tempfree(y, ""); - if (i) return(true); - else return(false); - case AND: - if ( !i ) return(false); - y = execute(a[1]); - i = istrue(y); - tempfree(y, ""); - if (i) return(true); - else return(false); - case NOT: - if (i) return(false); - else return(true); - default: /* can't happen */ - error(MM_ERROR, ":55:Unknown boolean operator %d", n); - } - /*NOTREACHED*/ - return 0; -} - -Cell *relop(Node **a, int n) -{ - register int i; - register Cell *x, *y; - Awkfloat j; - - x = execute(a[0]); - y = execute(a[1]); - if (x->tval&NUM && y->tval&NUM) { - j = x->fval - y->fval; - i = j<0? -1: (j>0? 1: 0); - } else { - i = strcoll((char*)getsval(x), (char*)getsval(y)); - } - tempfree(x, ""); - tempfree(y, ""); - switch (n) { - case LT: if (i<0) return(true); - else return(false); - case LE: if (i<=0) return(true); - else return(false); - case NE: if (i!=0) return(true); - else return(false); - case EQ: if (i == 0) return(true); - else return(false); - case GE: if (i>=0) return(true); - else return(false); - case GT: if (i>0) return(true); - else return(false); - default: /* can't happen */ - error(MM_ERROR, ":56:Unknown relational operator %d", n); - } - /*NOTREACHED*/ - return 0; -} - -static void tfree(register Cell *a, char *s) -{ - if (dbg>1) printf("## tfree %.8s %06lo %s\n", - s, (long)a, a->sval ? a->sval : (unsigned char *)""); - if (freeable(a)) - xfree(a->sval); - if (a == tmps) - error(MM_ERROR, ":57:Tempcell list is curdled"); - a->cnext = tmps; - tmps = a; -} - -Cell *gettemp(const char *s) -{ int i; - register Cell *x; - - if (!tmps) { - tmps = (Cell *) calloc(100, sizeof(Cell)); - if (!tmps) - error(MM_ERROR, ":58:No space for temporaries"); - for(i = 1; i < 100; i++) - tmps[i-1].cnext = &tmps[i]; - tmps[i-1].cnext = 0; - } - x = tmps; - tmps = x->cnext; - *x = tempcell; - if (dbg>1) printf("## gtemp %.8s %06lo\n", s, (long)x); - return(x); -} - -Cell *indirect(Node **a, int n) -{ - register Cell *x; - register int m; - register unsigned char *s; - - x = execute(a[0]); - m = getfval(x); - if (m == 0 && !is2number(s = getsval(x), 0)) /* suspicion! */ - error(MM_ERROR, ":59:Illegal field $(%s)", s); - tempfree(x, ""); - x = fieldadr(m); - x->ctype = OCELL; - x->csub = CFLD; - return(x); -} - -Cell *substr(Node **a, int nnn) -{ - register int k, m, n; - wchar_t wc; - register unsigned char *s, *sp, *sq; - int temp; - register Cell *x, *y, *z = 0; - - x = execute(a[0]); - y = execute(a[1]); - if (a[2] != 0) - z = execute(a[2]); - s = getsval(x); - k = strlen((char*)s) + 1; - if (k <= 1) { - tempfree(x, ""); - tempfree(y, ""); - if (a[2] != 0) { - tempfree(z, ""); - } - x = gettemp(""); - setsval(x, (unsigned char *)""); - return(x); - } - m = getfval(y); - if (m <= 0) - m = 1; - else if (m > k) - m = k; - tempfree(y, ""); - if (a[2] != 0) { - n = getfval(z); - tempfree(z, ""); - } else - n = k - 1; - if (n < 0) - n = 0; - else if (n > k - m) - n = k - m; - dprintf( ("substr: m=%d, n=%d, s=%s\n", m, n, s) ); - if (mb_cur_max > 1) { - for (sp = s; m > 1 && *sp; m--) { - next(wc, sp, k); - sp += k; - } - m = sp - s + 1; - for (sq = sp ; n > 0 && *sq; n--) { - next(wc, sq, k); - sq += k; - } - n = sq - sp; - dprintf( ("substr: multibyte: m=%d, n=%d, s=%s\n", m, n, s) ); - } - y = gettemp(""); - temp = s[n+m-1]; /* with thanks to John Linderman */ - s[n+m-1] = '\0'; - setsval(y, s + m - 1); - s[n+m-1] = temp; - tempfree(x, ""); - return(y); -} - -Cell *sindex(Node **a, int nnn) -{ - register Cell *x, *y, *z; - register unsigned char *s1, *s2, *p1, *p2, *q; - int n, nq, n2; - wchar_t wc, wq, w2; - Awkfloat v = 0.0; - - x = execute(a[0]); - s1 = getsval(x); - y = execute(a[1]); - s2 = getsval(y); - - z = gettemp(""); - for (p1 = s1; next(wc, p1, n), wc != '\0'; p1 += n) { - for (q = p1, p2 = s2; - next(wq, q, nq), - next(w2, p2, n2), - w2 != '\0' && wq == w2; - q += nq, p2 += n2) - ; - if (w2 == '\0') { - v = (Awkfloat) chrdist(s1, p1); - break; - } - } - tempfree(x, ""); - tempfree(y, ""); - setfval(z, v); - return(z); -} - - -int format(unsigned char **buf, int *bufsize, const unsigned char *s, Node *a) -{ - unsigned char *fmt = NULL; - int fmtsz = 0; - unsigned char *p, *t; - const unsigned char *os; - register Cell *x; - int flag = 0; - - os = s; - fmt = malloc(fmtsz = CHUNK); - if (*bufsize == 0) - *buf = malloc(*bufsize = CHUNK); - if (fmt == NULL || *buf == NULL) - error(MM_ERROR, outofspace, "format"); - p = *buf; - while (*s) { - if (p >= &(*buf)[*bufsize]) - growbuf(buf, bufsize, CHUNK, &p, "format"); - if (*s != '%') { - *p++ = *s++; - continue; - } - if (*(s+1) == '%') { - *p++ = '%'; - s += 2; - continue; - } - for (t=fmt; (*t++ = *s) != '\0'; s++) { - if (t >= &fmt[fmtsz]) - growbuf(&fmt, &fmtsz, CHUNK, &t, "format"); - if (isalpha(*s) && *s != 'l' && *s != 'h' && *s != 'L') - break; /* the ansi panoply */ - if (*s == '*') { - x = execute(a); - a = a->nnext; - t--; - growsprintf(&fmt, &t, &fmtsz, "%d", (int) getfval(x)); - tempfree(x, ""); - } - } - *t = '\0'; - switch (*s) { - case 'a': case 'A': - case 'e': case 'E': - case 'f': case 'F': - case 'g': case 'G': - flag = 1; - break; - case 'd': case 'i': - flag = 2; - if(*(s-1) == 'l') break; - *(t-1) = 'l'; - *t = 'd'; - *++t = '\0'; - break; - case 'o': case 'x': case 'X': case 'u': - flag = *(s-1) == 'l' ? 12 : 13; - break; - case 's': - /* - * Note: If MB_CUR_MAX > 1, the precision is in - * bytes, not characters. This doesn't make much - * sense in awk context, but it seems to match - * what POSIX demands. - */ - flag = 4; - break; - case 'c': - if (mb_cur_max > 1) { - *(t-1) = 'l'; - *t = 'c'; - *++t = '\0'; - flag = 6; - } else - flag = 5; - break; - default: - flag = 0; - break; - } - if (flag == 0) { - growsprintf(buf, &p, bufsize, "%s", fmt); - continue; - } - if (a == NULL) - error(MM_ERROR, ":61:Not enough args in printf(%s)", - os); - x = execute(a); - a = a->nnext; - switch (flag) { - case 1: growsprintf(buf, &p, bufsize, (char *)fmt, getfval(x)); - break; - case 2: growsprintf(buf, &p, bufsize, (char *)fmt, - (long) getfval(x)); - break; - case 3: growsprintf(buf, &p, bufsize, (char *)fmt, - (int) getfval(x)); - break; - case 12:growsprintf(buf, &p, bufsize, (char *)fmt, - (unsigned long) getfval(x)); - break; - case 13:growsprintf(buf, &p, bufsize, (char *)fmt, - (unsigned int) getfval(x)); - break; - case 4: growsprintf(buf, &p, bufsize, (char *)fmt, getsval(x)); - break; - case 5: isnum(x) ? growsprintf(buf, &p, bufsize, (char *)fmt, - (int) getfval(x)) - : growsprintf(buf, &p, bufsize, (char *)fmt, - getsval(x)[0]); - break; - case 6: isnum(x) ? growsprintf(buf, &p, bufsize, (char *)fmt, - (wint_t) getfval(x)) - : growsprintf(buf, &p, bufsize, (char *)fmt, - (wint_t) getsval(x)[0]); - break; - } - tempfree(x, ""); - s++; - } - *p = '\0'; - for ( ; a; a = a->nnext) /* evaluate any remaining args */ - execute(a); - xfree(fmt); - return 0; -} - -Cell *awsprintf(Node **a, int n) -{ - register Cell *x; - register Node *y; - unsigned char *buf = NULL; - int bufsize = 0; - - y = a[0]->nnext; - x = execute(a[0]); - if (format(&buf, &bufsize, getsval(x), y) == -1) - error(MM_ERROR, ":62:sprintf string %.40s ... too long", buf); - tempfree(x, ""); - x = gettemp(""); - x->sval = /*tostring(buf);*/ buf ? buf : tostring(""); - x->tval = STR; - return(x); -} - -Cell *aprintf(Node **a, int n) -{ - FILE *fp; - register Cell *x; - register Node *y; - unsigned char *buf = NULL; - int bufsize = 0; - - y = a[0]->nnext; - x = execute(a[0]); - if (format(&buf, &bufsize, getsval(x), y) == -1) - error(MM_ERROR, ":63:printf string %.40s ... too long", buf); - tempfree(x, ""); - if (buf) { - if (a[1] == NULL) - fputs((char *)buf, stdout); - else { - fp = redirect((intptr_t)a[1], a[2]); - fputs((char *)buf, fp); - fflush(fp); - } - free(buf); - } - return(true); -} - -Cell *arith(Node **a, int n) -{ - Awkfloat i, j = 0; - double v; - register Cell *x, *y, *z; - - x = execute(a[0]); - i = getfval(x); - tempfree(x, ""); - if (n != UMINUS) { - y = execute(a[1]); - j = getfval(y); - tempfree(y, ""); - } - z = gettemp(""); - switch (n) { - case ADD: - i += j; - break; - case MINUS: - i -= j; - break; - case MULT: - i *= j; - break; - case DIVIDE: - if (j == 0) - error(MM_ERROR, ":64:Division by zero"); - i /= j; - break; - case MOD: - if (j == 0) - error(MM_ERROR, ":65:Division by zero in mod"); - modf(i/j, &v); - i = i - j * v; - break; - case UMINUS: - i = -i; - break; - case POWER: - if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */ - i = ipow(i, (int) j); - else - i = errcheck(pow(i, j), (unsigned char *)"pow"); - break; - default: /* can't happen */ - error(MM_ERROR, ":66:Illegal arithmetic operator %d", n); - } - setfval(z, i); - return(z); -} - -double ipow(double x, int n) -{ - double v; - - if (n <= 0) - return 1; - v = ipow(x, n/2); - if (n % 2 == 0) - return v * v; - else - return x * v * v; -} - -Cell *incrdecr(Node **a, int n) -{ - register Cell *x, *z; - register int k; - Awkfloat xf; - - x = execute(a[0]); - xf = getfval(x); - k = (n == PREINCR || n == POSTINCR) ? 1 : -1; - if (n == PREINCR || n == PREDECR) { - setfval(x, xf + k); - return(x); - } - z = gettemp(""); - setfval(z, xf); - setfval(x, xf + k); - tempfree(x, ""); - return(z);