diff options
-rw-r--r-- | .gitignore | 18 | ||||
-rw-r--r-- | ChangeLog | 186 | ||||
-rw-r--r-- | FUTURES | 4 | ||||
-rw-r--r-- | Makefile.am | 28 | ||||
-rw-r--r-- | Makefile.in | 72 | ||||
-rw-r--r-- | NEWS | 12 | ||||
-rw-r--r-- | README.git | 218 | ||||
-rw-r--r-- | array.c | 1403 | ||||
-rw-r--r-- | awk.h | 568 | ||||
-rw-r--r-- | awkgram.c | 2774 | ||||
-rw-r--r-- | awkgram.y | 1233 | ||||
-rw-r--r-- | awklib/.gitignore | 8 | ||||
-rw-r--r-- | builtin.c | 63 | ||||
-rw-r--r-- | cint_array.c | 1237 | ||||
-rw-r--r-- | cmd.h | 3 | ||||
-rw-r--r-- | command.c | 74 | ||||
-rw-r--r-- | command.y | 74 | ||||
-rwxr-xr-x | configure | 24 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | debug.c | 342 | ||||
-rw-r--r-- | doc/ChangeLog | 16 | ||||
-rw-r--r-- | doc/awkcard.in | 37 | ||||
-rw-r--r-- | doc/gawk.1 | 130 | ||||
-rw-r--r-- | doc/gawk.info | 1905 | ||||
-rw-r--r-- | doc/gawk.texi | 473 | ||||
-rw-r--r-- | eval.c | 1471 | ||||
-rw-r--r-- | eval_d.c | 27 | ||||
-rw-r--r-- | eval_p.c | 27 | ||||
-rw-r--r-- | ext.c | 234 | ||||
-rw-r--r-- | extension/ChangeLog | 4 | ||||
-rw-r--r-- | extension/arrayparm.c | 8 | ||||
-rw-r--r-- | extension/filefuncs.c | 44 | ||||
-rw-r--r-- | extension/fork.c | 8 | ||||
-rw-r--r-- | extension/ordchr.c | 4 | ||||
-rw-r--r-- | extension/readfile.c | 2 | ||||
-rw-r--r-- | extension/rwarray.c | 6 | ||||
-rw-r--r-- | extension/testarg.c | 8 | ||||
-rw-r--r-- | field.c | 28 | ||||
-rw-r--r-- | int_array.c | 828 | ||||
-rw-r--r-- | interpret.h | 1187 | ||||
-rw-r--r-- | io.c | 79 | ||||
-rw-r--r-- | main.c | 209 | ||||
-rw-r--r-- | msg.c | 2 | ||||
-rw-r--r-- | node.c | 168 | ||||
-rw-r--r-- | pc/ChangeLog | 4 | ||||
-rw-r--r-- | pc/Makefile.tst | 43 | ||||
-rw-r--r-- | po/.gitignore | 4 | ||||
-rw-r--r-- | po/ast.gmo | bin | 37068 -> 0 bytes | |||
-rw-r--r-- | po/ca.gmo | bin | 25787 -> 0 bytes | |||
-rw-r--r-- | po/ga.gmo | bin | 33929 -> 0 bytes | |||
-rw-r--r-- | po/he.gmo | bin | 24651 -> 0 bytes | |||
-rw-r--r-- | po/id.gmo | bin | 35809 -> 0 bytes | |||
-rw-r--r-- | po/pt_BR.gmo | bin | 29120 -> 0 bytes | |||
-rw-r--r-- | po/ro.gmo | bin | 25383 -> 0 bytes | |||
-rw-r--r-- | po/rw.gmo | bin | 487 -> 0 bytes | |||
-rw-r--r-- | po/tr.gmo | bin | 33826 -> 0 bytes | |||
-rw-r--r-- | po/vi.gmo | bin | 40445 -> 0 bytes | |||
-rw-r--r-- | po/zh_CN.gmo | bin | 33717 -> 0 bytes | |||
-rw-r--r-- | profile.c | 69 | ||||
-rw-r--r-- | profile_p.c | 27 | ||||
-rw-r--r-- | str_array.c | 762 | ||||
-rw-r--r-- | symbol.c | 718 | ||||
-rw-r--r-- | test/ChangeLog | 4 | ||||
-rw-r--r-- | test/Makefile.am | 7 | ||||
-rw-r--r-- | test/Makefile.in | 9 | ||||
-rw-r--r-- | test/Maketests | 2 | ||||
-rw-r--r-- | test/badargs.ok | 3 | ||||
-rw-r--r-- | test/delfunc.ok | 5 | ||||
-rw-r--r-- | test/fnamedat.ok | 5 | ||||
-rw-r--r-- | test/fnarray.ok | 2 | ||||
-rw-r--r-- | test/fnarray2.in | 1 | ||||
-rw-r--r-- | test/fnarray2.ok | 4 | ||||
-rw-r--r-- | test/fnarydel.ok | 10 | ||||
-rw-r--r-- | test/fnasgnm.ok | 5 | ||||
-rw-r--r-- | test/fnparydl.ok | 6 | ||||
-rw-r--r-- | test/funsmnam.ok | 2 | ||||
-rw-r--r-- | test/gsubasgn.ok | 8 | ||||
-rw-r--r-- | test/match2.ok | 5 | ||||
-rw-r--r-- | version.c | 2 |
79 files changed, 9777 insertions, 7182 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e2ae74d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Ignore files that are created by a build. +# For example objects and archives. +*.[oa] + +# Directories +autom4te.cache +.deps + +# Single files. +Makefile +config.h +config.log +config.status +dgawk +gawk +pgawk +stamp-h1 + @@ -1,9 +1,69 @@ 2011-12-31 Arnold D. Robbins <arnold@skeeve.com> + * profile_p.c: Remove the file. + * msg.c (err): Remove check for name being dgawk. + +2011-12-31 Arnold D. Robbins <arnold@skeeve.com> + * awk.h [STREQ, STREQN]: Remove macros. * awkgram.y, builtin.c, command.y, debug.c, eval.c, io.c, msg.c: Change all uses to call strcmp, strncmp. +2011-12-28 Arnold D. Robbins <arnold@skeeve.com> + + * int_array.c, str_array.c: Fix some compiler warnings 32/64 + bit system differences. + +2011-12-26 John Haque <j.eh@mchsi.com> + + Merge gawk, pgawk and dgawk into a single executable gawk. + + * awk.h (DO_PRETTY_PRINT, DO_PROFILE, DO_DEBUG, + do_pretty_print, do_debug): New defines. + (interpret): New variable, a pointer to an interpreter routine. + (enum exe_mode): Nuked. + * main.c (opttab): New options --pretty-print and --debug; + Remove option --command. + (usage): Update usage messages. + * interpret.h: New file. + * eval.c (r_interpret): Move to the new file. + (debug_interpret): New interpreter routine when debugging. + (init_interpret): New routine to initialize interpreter related + variables. + * eval_d.c, eval_p.c: Delete files. + * debug.c (interpret): Renamed to debug_prog. + (DEFAULT_PROMPT, DEFAULT_HISTFILE, DEFAULT_OPTFILE): Remove prefix 'd'. + * profile.c (init_profiling): Nuked. + * Makefile.am: Adjusted. + + Add command line option --load for loading extensions. + + * awk.h (srctype): Add new source type SRC_EXTLIB. + * ext.c(load_ext): New routine to load extension. + (do_ext): Adjust to use load_ext(). + * main.c (opttab): Add new option --load. + (main): Call load_ext() to load extensions. + (usage): Add usage message for the new option. + * io.c (get_cwd): New routine. + (do_find_source): Use the new routine. + (find_source): Handle new type SRC_EXTLIB. + * awkgram.y (parse_program, next_sourcefile): Skip type SRC_EXTLIB. + (add_srcfile): Adjust call to find_source. + * debug.c (source_find): Same. + + Unrelated: + + * ext.c (get_argument): Fixed argument parsing. + * array.c (null_array_func): Reworked array routines for an empty array. + * str_array.c, int_array.c: Make GCC happy, use %u instead of %lu + printf formats. + * eval.c (node_Boolean): New array for TRUE and FALSE nodes. + (init_interpret): Create the new nodes. + (eval_condition): Add test for the new nodes. + (setup_frame): Disable tail-recursion optimization when profiling. + * interpret.h (r_interpret): Use the boolean nodes instead of making + new ones when needed. + 2011-12-26 Arnold D. Robbins <arnold@skeeve.com> Finish Rational Range Interpretation (!) @@ -63,6 +123,14 @@ 2011-10-25 Arnold D. Robbins <arnold@skeeve.com> + Merge with gawk_performance branch done. Additionally: + + * cint_array.c, int_array.c, str_array.c: Fix compiler complaints + about printf formats (signed / unsigned vs. %d / %u). + * eval.c (setup_frame): Add a missing return value. + +2011-10-25 Arnold D. Robbins <arnold@skeeve.com> + * Makefile.am (dist-hook): Use `cd $(srcdir)/pc' so that `make distcheck' works completely. * builtin.c (do_strftime): Add cast to long int in check @@ -134,10 +202,25 @@ (remap_std_file): Per Eli's suggestion, removed the leading close of oldfd and will let dup2 do the close for us. +2011-10-11 John Haque <j.eh@mchsi.com> + + * symbol.c: Add licence notice. + * array.c (PREC_NUM, PREC_STR): Define as macros. + 2011-10-09 Arnold D. Robbins <arnold@skeeve.com> * dfa.c: Sync with GNU grep. +2011-10-07 John Haque <j.eh@mchsi.com> + + Tail recursion optimization. + * awkgram.y (grammar, mk_function): Recognize tail-recursive + calls. + * awk.h (tail_call, num_tail_calls): New defines. + * eval.c (setup_frame): Reuse function call stack for + tail-recursive calls. + (dump_fcall_stack): Reworked. + 2011-10-04 Arnold D. Robbins <arnold@skeeve.com> * awk.h, main.c (gawk_mb_cur_max): Make it a constant 1 when @@ -174,10 +257,113 @@ * dfa.c: Sync with GNU grep. +2011-09-08 John Haque <j.eh@mchsi.com> + + Optimization for compound assignment, increment and + decrement operators; Avoid unref and make_number calls + when there is no extra references to the value NODE. + 2011-09-03 Arnold D. Robbins <arnold@skeeve.com> * dfa.c: Sync with GNU grep. +2011-08-31 John Haque <j.eh@mchsi.com> + + Grammar related changes: Simplify grammar for user-defined + functions and general cleanups. + + * symbol.c: New file. + * awkgram.y: Move symbol table related routines to the + new file. + (rule, func_name, function_prologue, param_list): Reworked. + (install_function, check_params): Do all error checkings + for the function name and parameters before installing in + the symbol table. + (mk_function): Finalize function definition. + (func_install, append_param, dup_params): Nuked. + * symbol.c (make_params): allocate function parameter nodes + for the symbol table. Use the hash node as Node_param_list; + Saves a NODE for each parameter. + (install_params): Install function parameters into the symbol + table. + (remove_params): Remove parameters out of the symbol table. + * awk.h (parmlist, FUNC): Nuked. + (fparms): New define. + + + Dynamically loaded function parameters are now handled like + those for a builtin. + + * awk.h (Node_ext_func, Op_ext_builtin): New types. + (Op_ext_func): Nuked. + * ext.c (make_builtin): Simplified. + (get_curfunc_arg_count): Nuked; Use the argument 'nargs' of + the extension function instead. + (get_argument, get_actual_argument): Adjust. + * eval.c (r_interpret): Update case Op_func_call for a dynamic + extension function. Handle the new opcode Op_ext_builtin. + * pprint (profile.c): Adjust. + + + Use a single variable to process gawk options. + + * awk.h (do_flags): New variable. + (DO_LINT_INVALID, DO_LINT_ALL, DO_LINT_OLD, DO_TRADITIONAL, + DO_POSIX, DO_INTL, DO_NON_DEC_DATA, DO_INTERVALS, + DO_PROFILING, DO_DUMP_VARS, DO_TIDY_MEM, + DO_SANDBOX): New defines. + (do_traditional, do_posix, do_intervals, do_intl, + do_non_decimal_data, do_profiling, do_dump_vars, + do_tidy_mem, do_sandbox, do_lint, + do_lint_old): Defined as macros. + * main.c: Remove definitions of the do_XX variables. Add + do_flags definition. + * debug.c (execute_code, do_eval, parse_condition): Save + do_flags before executing/parsing and restore afterwards. + + + Nuke PERM flag. Always increment/decrement the reference + count for a Node_val. Simplifies macros and avoids + occassional memory leaks, specially in the debugger. + + * awk.h (UPREF, DEREF, dupnode, unref): Simplified. + (mk_number): Nuked. + * (*.c): Increment the reference count of Nnull_string before + assigning as a value. + + + Revamped array handling mechanism for more speed and + less memory consumption. + + * awk.h (union bucket_item, BUCKET): New definitions. Used as + bucket elements for the hash table implementations of arrays; + 40% space saving in 32 bit x86. + (buckets, nodes, array_funcs, array_base, array_capacity, + xarray, alookup, aexists, aclear, aremove, alist, + acopy, adump, NUM_AFUNCS): New defines. + (array_empty): New macro to test for an empty array. + (assoc_lookup, in_array): Defined as macros. + (enum assoc_list_flags): New declaration. + (Node_ahash, NUMIND): Nuked. + * eval.c (r_interpret): Adjust cases Op_subscript, + Op_subscript_lhs, Op_store_var and Op_arrayfor_incr. + * node.c (dupnode, unref): Removed code related to Node_ahash. + * str_array.c: New file to handle array with string indices. + * int_array.c: New file to handle array with integer indices. + * cint_array.c: New file. Special handling of arrays with + (mostly) consecutive integer indices. + + + Memory pool management reworked to handle NODE and BUCKET. + + * awk.h (struct block_item, BLOCK, block_id): New definitions. + (getblock, freeblock): New macros. + (getbucket, freebucket): New macros to allocate and deallocate + a BUCKET. + (getnode, freenode): Adjusted. + * node.c (more_nodes): Nuked. + (more_blocks): New routine to allocate blocks of memory. + 2011-08-24 Arnold D. Robbins <arnold@skeeve.com> Fix pty co-process communication on Ubuntu GNU/Linux. @@ -11,14 +11,14 @@ don't bug us too much about schedules or what all this really means. For 4.1 ======= - Merge gawk/pgawk/dgawk into one executable + DONE: Merge gawk/pgawk/dgawk into one executable Consider removing use of and/or need for the protos.h file. Consider moving var_value info into Node_var itself to reduce memory usage. - Merge xmlgawk -l feature + DONE: Merge xmlgawk -l feature Merge xmlgawk XML extensions diff --git a/Makefile.am b/Makefile.am index aeff42f5..b9470617 100644 --- a/Makefile.am +++ b/Makefile.am @@ -79,17 +79,22 @@ SUBDIRS = \ test # what to make and install -bin_PROGRAMS = gawk pgawk dgawk +bin_PROGRAMS = gawk -# sources for both gawk and pgawk +# sources for both gawk and dgawk base_sources = \ array.c \ awk.h \ awkgram.y \ builtin.c \ + cint_array.c \ + cmd.h \ + command.y \ custom.h \ + debug.c \ dfa.c \ dfa.h \ + eval.c \ ext.c \ field.c \ floatcomp.c \ @@ -100,11 +105,14 @@ base_sources = \ getopt1.c \ getopt_int.h \ gettext.h \ + int_array.c \ + interpret.h \ io.c \ mbsupport.h \ main.c \ msg.c \ node.c \ + profile.c \ protos.h \ random.c \ random.h \ @@ -112,16 +120,15 @@ base_sources = \ regex.c \ regex.h \ replace.c \ + str_array.c \ + symbol.c \ version.c \ xalloc.h -gawk_SOURCES = $(base_sources) eval.c profile.c -pgawk_SOURCES = $(base_sources) eval_p.c profile_p.c -dgawk_SOURCES = $(base_sources) eval_d.c profile.c cmd.h command.y debug.c +gawk_SOURCES = $(base_sources) # Get extra libs as needed, Automake will supply LIBINTL and SOCKET_LIBS. -LDADD = $(LIBSIGSEGV) $(LIBINTL) $(SOCKET_LIBS) -dgawk_LDADD = $(LDADD) @LIBREADLINE@ +LDADD = $(LIBSIGSEGV) $(LIBINTL) $(SOCKET_LIBS) @LIBREADLINE@ # Directory for gawk's data files. Automake supplies datadir. pkgdatadir = $(datadir)/awk @@ -140,7 +147,6 @@ MAINTAINERCLEANFILES = version.c LN= ln # First, add a link from gawk to gawk-X.Y.Z. -# Same for pgawk. # # For GNU systems where gawk is awk, add a link to awk. # (This is done universally, which may not always be right, but @@ -148,7 +154,6 @@ LN= ln install-exec-hook: (cd $(DESTDIR)$(bindir); \ $(LN) gawk$(EXEEXT) gawk-$(VERSION)$(EXEEXT) 2>/dev/null ; \ - $(LN) pgawk$(EXEEXT) pgawk-$(VERSION)$(EXEEXT) 2>/dev/null ; \ if [ ! -f awk ]; \ then $(LN_S) gawk$(EXEEXT) awk; \ fi; exit 0) @@ -157,12 +162,12 @@ install-exec-hook: uninstall-links: (cd $(DESTDIR)$(bindir); \ if [ -f awk ] && cmp awk gawk$(EXEEXT) > /dev/null; then rm -f awk; fi ; \ - rm -f gawk-$(VERSION)$(EXEEXT) pgawk-$(VERSION)$(EXEEXT); exit 0) + rm -f gawk-$(VERSION)$(EXEEXT); exit 0) uninstall-recursive: uninstall-links # force there to be a gawk executable before running tests -check-local: gawk$(EXEEXT) pgawk$(EXEEXT) +check-local: gawk$(EXEEXT) # A little extra clean up when making distributions. # And additional set up for the pc directory. @@ -177,6 +182,7 @@ dist-hook: # Special rules for individual files # Use of awk instead of $(AWK) is deliberate, in case gawk doesn't build # or work correctly. + awkgram.c: awkgram.y $(YACC) $(AM_YFLAGS) $(YFLAGS) $< sed 's/parse error/syntax error/g' < y.tab.c | awk -f $(srcdir)/bisonfix.awk awkgram > $*.c && rm y.tab.c diff --git a/Makefile.in b/Makefile.in index c8208d94..3083c4c3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -56,7 +56,7 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -bin_PROGRAMS = gawk$(EXEEXT) pgawk$(EXEEXT) dgawk$(EXEEXT) +bin_PROGRAMS = gawk$(EXEEXT) subdir = . DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/configh.in \ @@ -88,29 +88,20 @@ CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) am__objects_1 = array.$(OBJEXT) awkgram.$(OBJEXT) builtin.$(OBJEXT) \ - dfa.$(OBJEXT) ext.$(OBJEXT) field.$(OBJEXT) \ + cint_array.$(OBJEXT) command.$(OBJEXT) debug.$(OBJEXT) \ + dfa.$(OBJEXT) eval.$(OBJEXT) ext.$(OBJEXT) field.$(OBJEXT) \ floatcomp.$(OBJEXT) gawkmisc.$(OBJEXT) getopt.$(OBJEXT) \ - getopt1.$(OBJEXT) io.$(OBJEXT) main.$(OBJEXT) msg.$(OBJEXT) \ - node.$(OBJEXT) random.$(OBJEXT) re.$(OBJEXT) regex.$(OBJEXT) \ - replace.$(OBJEXT) version.$(OBJEXT) -am_dgawk_OBJECTS = $(am__objects_1) eval_d.$(OBJEXT) profile.$(OBJEXT) \ - command.$(OBJEXT) debug.$(OBJEXT) -dgawk_OBJECTS = $(am_dgawk_OBJECTS) -am__DEPENDENCIES_1 = -am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) -dgawk_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_gawk_OBJECTS = $(am__objects_1) eval.$(OBJEXT) profile.$(OBJEXT) + getopt1.$(OBJEXT) int_array.$(OBJEXT) io.$(OBJEXT) \ + main.$(OBJEXT) msg.$(OBJEXT) node.$(OBJEXT) profile.$(OBJEXT) \ + random.$(OBJEXT) re.$(OBJEXT) regex.$(OBJEXT) \ + replace.$(OBJEXT) str_array.$(OBJEXT) symbol.$(OBJEXT) \ + version.$(OBJEXT) +am_gawk_OBJECTS = $(am__objects_1) gawk_OBJECTS = $(am_gawk_OBJECTS) gawk_LDADD = $(LDADD) +am__DEPENDENCIES_1 = gawk_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) -am_pgawk_OBJECTS = $(am__objects_1) eval_p.$(OBJEXT) \ - profile_p.$(OBJEXT) -pgawk_OBJECTS = $(am_pgawk_OBJECTS) -pgawk_LDADD = $(LDADD) -pgawk_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -121,8 +112,8 @@ CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS) YLWRAP = $(top_srcdir)/ylwrap -SOURCES = $(dgawk_SOURCES) $(gawk_SOURCES) $(pgawk_SOURCES) -DIST_SOURCES = $(dgawk_SOURCES) $(gawk_SOURCES) $(pgawk_SOURCES) +SOURCES = $(gawk_SOURCES) +DIST_SOURCES = $(gawk_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-dvi-recursive install-exec-recursive \ @@ -355,15 +346,20 @@ SUBDIRS = \ test -# sources for both gawk and pgawk +# sources for both gawk and dgawk base_sources = \ array.c \ awk.h \ awkgram.y \ builtin.c \ + cint_array.c \ + cmd.h \ + command.y \ custom.h \ + debug.c \ dfa.c \ dfa.h \ + eval.c \ ext.c \ field.c \ floatcomp.c \ @@ -374,11 +370,14 @@ base_sources = \ getopt1.c \ getopt_int.h \ gettext.h \ + int_array.c \ + interpret.h \ io.c \ mbsupport.h \ main.c \ msg.c \ node.c \ + profile.c \ protos.h \ random.c \ random.h \ @@ -386,16 +385,15 @@ base_sources = \ regex.c \ regex.h \ replace.c \ + str_array.c \ + symbol.c \ version.c \ xalloc.h -gawk_SOURCES = $(base_sources) eval.c profile.c -pgawk_SOURCES = $(base_sources) eval_p.c profile_p.c -dgawk_SOURCES = $(base_sources) eval_d.c profile.c cmd.h command.y debug.c +gawk_SOURCES = $(base_sources) # Get extra libs as needed, Automake will supply LIBINTL and SOCKET_LIBS. -LDADD = $(LIBSIGSEGV) $(LIBINTL) $(SOCKET_LIBS) -dgawk_LDADD = $(LDADD) @LIBREADLINE@ +LDADD = $(LIBSIGSEGV) $(LIBINTL) $(SOCKET_LIBS) @LIBREADLINE@ # stuff for compiling gawk/pgawk DEFPATH = '".$(PATH_SEPARATOR)$(pkgdatadir)"' @@ -499,15 +497,9 @@ uninstall-binPROGRAMS: clean-binPROGRAMS: -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) -dgawk$(EXEEXT): $(dgawk_OBJECTS) $(dgawk_DEPENDENCIES) - @rm -f dgawk$(EXEEXT) - $(LINK) $(dgawk_OBJECTS) $(dgawk_LDADD) $(LIBS) gawk$(EXEEXT): $(gawk_OBJECTS) $(gawk_DEPENDENCIES) @rm -f gawk$(EXEEXT) $(LINK) $(gawk_OBJECTS) $(gawk_LDADD) $(LIBS) -pgawk$(EXEEXT): $(pgawk_OBJECTS) $(pgawk_DEPENDENCIES) - @rm -f pgawk$(EXEEXT) - $(LINK) $(pgawk_OBJECTS) $(pgawk_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -518,28 +510,29 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/awkgram.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/builtin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cint_array.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dfa.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval_d.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval_p.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/field.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/floatcomp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gawkmisc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/int_array.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/io.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/msg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile_p.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/re.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/replace.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/str_array.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symbol.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@ .c.o: @@ -1007,7 +1000,6 @@ uninstall-am: uninstall-binPROGRAMS # First, add a link from gawk to gawk-X.Y.Z. -# Same for pgawk. # # For GNU systems where gawk is awk, add a link to awk. # (This is done universally, which may not always be right, but @@ -1015,7 +1007,6 @@ uninstall-am: uninstall-binPROGRAMS install-exec-hook: (cd $(DESTDIR)$(bindir); \ $(LN) gawk$(EXEEXT) gawk-$(VERSION)$(EXEEXT) 2>/dev/null ; \ - $(LN) pgawk$(EXEEXT) pgawk-$(VERSION)$(EXEEXT) 2>/dev/null ; \ if [ ! -f awk ]; \ then $(LN_S) gawk$(EXEEXT) awk; \ fi; exit 0) @@ -1024,12 +1015,12 @@ install-exec-hook: uninstall-links: (cd $(DESTDIR)$(bindir); \ if [ -f awk ] && cmp awk gawk$(EXEEXT) > /dev/null; then rm -f awk; fi ; \ - rm -f gawk-$(VERSION)$(EXEEXT) pgawk-$(VERSION)$(EXEEXT); exit 0) + rm -f gawk-$(VERSION)$(EXEEXT); exit 0) uninstall-recursive: uninstall-links # force there to be a gawk executable before running tests -check-local: gawk$(EXEEXT) pgawk$(EXEEXT) +check-local: gawk$(EXEEXT) # A little extra clean up when making distributions. # And additional set up for the pc directory. @@ -1044,6 +1035,7 @@ dist-hook: # Special rules for individual files # Use of awk instead of $(AWK) is deliberate, in case gawk doesn't build # or work correctly. + awkgram.c: awkgram.y $(YACC) $(AM_YFLAGS) $(YFLAGS) $< sed 's/parse error/syntax error/g' < y.tab.c | awk -f $(srcdir)/bisonfix.awk awkgram > $*.c && rm y.tab.c @@ -3,6 +3,18 @@ Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. + +Changes from 4.0.1 to 4.1 +------------------------- +1. The three executables gawk, pgawk, and dgawk, have been merged into + one, named just gawk. As a result: + * The -R option is gone + * Use -D to run the debugger. An optional file argument is a + list of commands to run first. + * Use -o to do pretty-printing only. + * Use -p to do profiling. + +2. The new -l option is used for loading dynamic extensions. Changes from 4.0.0 to 4.0.1 --------------------------- @@ -18,6 +18,224 @@ comp.lang.awk is generally a bad idea, no matter what the purpose, but especially if you wish to report a gawk bug. Use the above email address. Really. +You can find gawk's GIT repository at Savannah +https://savannah.gnu.org/git/?group=gawk +Detailed instructions on using and contributing to gawk can also be +found there +http://savannah.gnu.org/maintenance/UsingGit + + +- How can I check out the GIT repository ? + +Depending upon your working habits, there are several options. +1. On the Linux command line use the git command (details see below) +2. With Microsoft Windows, use TortoiseGIT +3. On both platforms Eclipse with its EGIT plugin is an excellent choice + +On the Linux command line use git to check out the repository. +With Microsoft Windows, use TortoiseGIT. + + +- Where is TortoiseGIT and how do I install it ? + +Follow these instructions for installation: + https://github.com/multitheftauto/multitheftauto/wiki/how-to-use-tortoisegit + +Begin with installing Putty, then msysgit and finally TortoiseGIT. +Find Putty at http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html + + +- What about the SSH keys needed when Windows is my primary environment ? + +On Windows you may use Puttygen for generating keys. +Save both keys (public and private) in a local .ppk file. +Notice that this file is highly confidential and even other +team members are not supposed to see your keys. +Finally you need to convert Putty's keys (.ppk file) so that they +can also be used on Linux. + http://www.laszlomolnar.name/open-source/tips-and-tutorials/how-to-convert-puttys-private-key-ppk-into-opensshs-private-key-format-in-linux.html + + puttygen my_keys.ppk -O private-openssh -o .ssh/id_rsa + puttygen my_keys.ppk -O public-openssh -o .ssh/id_rsa.pub + + +- What about the SSH keys needed when Linux is my primary environment ? + +On Linux follow these instructions to generate keys: +http://www.guyrutenberg.com/2007/10/05/ssh-keygen-tutorial-generating-rsa-and-dsa-keys/ +If you ever need these keys inside a Windows environment, use Puttygen +to import the already existing keys. + + +- I know Subversion, now what's different with git ? + +Read the "Git - SVN Crash Course". It lists the Subversion commands that +are roughly equivalent to certain git commands: + http://www.pronego.com/helpdesk/knowledgebase.php?article=49 +This document is only one of many copies of the document on the Internet. +You should read the original (which is currently offline and unreachable): + https://git.wiki.kernel.org/index.php/GitSvnCrashCourse + + +- How can I check out this repository inside a clean subdirectory ? + + mkdir -p workspace/git + cd workspace/git + git clone git://git.sv.gnu.org/project.git + + git remote -v + origin ssh://jkahrs@git.sv.gnu.org/srv/git/gawk.git (fetch) + origin ssh://jkahrs@git.sv.gnu.org/srv/git/gawk.git (push) + + +- How can I check out this repository with Eclipse ? + +Use the most recent version of Eclipse, it already comes with the +EGIT plugin installed. + Select File -> Import -> Git -> Git Repository. + Press clone and maintain the git repository "ssh://jkahrs@git.sv.gnu.org/srv/git/gawk.git". + You only have to paste the URL to the first line of the dialog, + the rest will be filled out automatically. + +You can find details in the EGIT tutorial. + http://www.vogella.de/articles/EGit/article.html#respository_checkoutproject + + +- Can I start adding new files to the repository right now ? +Yes, you can, but you should not do so. +Convention with branches. + +But first you should make sure some global settings identifying +you are set. The global settings will be used every time you commit +something to the repository. + + git config --global user.name "First-Name Last-Name" + git config --global user.email email@address.site + git config --global color.ui auto + + +- How can I inspect my settings ? + + + git config --list + giggle.main-window-maximized=false + giggle.main-window-geometry=1369x753+183+81 + giggle.main-window-view=HistoryView + giggle.history-view-vpane-position=389 + giggle.file-view-vpane-position=293 + user.name=First-Name Last-Name + user.email=email@address.site + color.diff=auto + color.status=auto + color.branch=auto + gui.spellingdictionary=en_US + core.repositoryformatversion=0 + core.filemode=true + core.bare=false + core.logallrefupdates=true + remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* + remote.origin.url=ssh://jkahrs@git.sv.gnu.org/srv/git/gawk.git + branch.master.remote=origin + branch.master.merge=refs/heads/master + branch.xgawk_load.remote=origin + branch.xgawk_load.merge=refs/heads/xgawk_load + + +- How can I get or set a specific variable of the settings ? + + git config --get color.ui + git config --set color.ui auto + + +- How can I create new files or directories to the repository ? + + touch README + git add README + git commit -m "first commit" + + +- What did I change since the last commit ? + + git diff README + git diff + + +- I have committed everything to my local repository, now how can I + "push" these changes up to gawk.git ? + + git push -u origin master # push up to master branch + git push -u origin my_feature_branch # push up to my own branch + + +- How can I inspect the list of branches of my repository ? + + git branch # shows all local branches + git branch -r # shows all remote branches + git branch -a # shows all local and all remote branches + + +- How can I change to a different branch ? + + git checkout my_stuff # change to branch my_stuff + git checkout -b my_stuff # create new branch my_stuff and change to it + +- How can I create a branch ? + +For each new feature to be considered for inclusion into future +releasses, a new branch shall be created. Upon creation, this new +branch shall be based on the master branch. + + # make master branch the base + git checkout master + git branch my_new_feature_branch + touch my_new_file.c + git add my_new_file.c + git status + git commit -m "Created new feature branch." + git push -u origin my_new_feature_branch + git checkout my_new_feature_branch + +- How can I throw away an obsolete branch ? + + git push origin :newfeature # remove remote branch + git checkout -f master # switch back from newfeature to master, ignoring changes + git branch -D newfeature # remove local branch + + +- I have made stupid changes to a file and want the original back, how ? + + svn checkout file_name.ext + + This will only work if the file was not yet committed. + If you have already committed the change and want back the + last pushed version, use "git reset" instead. + + +- Who changed a specific line in my file ? + +Sometimes you need to find out whom to blame for a certain line of a change. +git can tell you for each line who did the most recent change of the line. + + git blame README + + +- Who else has ever changed my file, when and why ? + +You can inspect the log history file-wise but also directory-wise. + + git log README + git log + + +- How to fix a broken repository ? + + git fsck + +- How to clean up my repository (garbage collection) ? + + git gc + + Thanks, Arnold Robbins @@ -1,5 +1,5 @@ /* - * array.c - routines for associative arrays. + * array.c - routines for awk arrays. */ /* @@ -25,69 +25,195 @@ #include "awk.h" +extern FILE *output_fp; +extern NODE **fmt_list; /* declared in eval.c */ +extern array_ptr str_array_func[]; +extern array_ptr cint_array_func[]; +extern array_ptr int_array_func[]; + +static size_t SUBSEPlen; +static char *SUBSEP; +static char indent_char[] = " "; + +static NODE **null_lookup(NODE *symbol, NODE *subs); +static NODE **null_afunc(NODE *symbol, NODE *subs); +static NODE **null_dump(NODE *symbol, NODE *subs); +static array_ptr null_array_func[] = { + (array_ptr) 0, + (array_ptr) 0, + null_lookup, + null_afunc, + null_afunc, + null_afunc, + null_afunc, + null_afunc, + null_dump, +#ifdef ARRAYDEBUG + null_afunc +#endif +}; + +#define MAX_ATYPE 10 + +static array_ptr *atypes[MAX_ATYPE]; +static int num_atypes = 0; + /* - * Tree walks (``for (iggy in foo)'') and array deletions use expensive - * linear searching. So what we do is start out with small arrays and - * grow them as needed, so that our arrays are hopefully small enough, - * most of the time, that they're pretty full and we're not looking at - * wasted space. - * - * The decision is made to grow the array if the average chain length is - * ``too big''. This is defined as the total number of entries in the table - * divided by the size of the array being greater than some constant. + * register_array_func --- add routines to handle arrays. * - * We make the constant a variable, so that it can be tweaked - * via environment variable. + * index 0 : initialization. + * index 1 : check if index is compatible. + * index 8 : array dump, memory and other statistics (do_adump). */ + + +int +register_array_func(array_ptr *afunc) +{ + if (afunc && num_atypes < MAX_ATYPE) { + if (afunc != str_array_func && ! afunc[1]) + return FALSE; + atypes[num_atypes++] = afunc; + if (afunc[0]) /* execute init routine if any */ + (void) (*afunc[0])(NULL, NULL); + return TRUE; + } + return FALSE; +} -static size_t AVG_CHAIN_MAX = 2; /* Modern machines are bigger, reduce this from 10. */ -static size_t SUBSEPlen; -static char *SUBSEP; +/* array_init --- register all builtin array types */ + +void +array_init() +{ + (void) register_array_func(str_array_func); /* the default */ + (void) register_array_func(int_array_func); + (void) register_array_func(cint_array_func); +} -static NODE *assoc_find(NODE *symbol, NODE *subs, unsigned long hash1, NODE **last); -static void grow_table(NODE *symbol); -static unsigned long gst_hash_string(const char *str, size_t len, unsigned long hsize, size_t *code); -static unsigned long scramble(unsigned long x); -static unsigned long awk_hash(const char *s, size_t len, unsigned long hsize, size_t *code); +/* make_array --- create an array node */ -unsigned long (*hash)(const char *s, size_t len, unsigned long hsize, size_t *code) = awk_hash; +NODE * +make_array() +{ + NODE *array; + getnode(array); + memset(array, '\0', sizeof(NODE)); + array->type = Node_var_array; + array->array_funcs = null_array_func; + /* vname, flags, and parent_array not set here */ -/* qsort comparison function */ -static int sort_up_index_string(const void *, const void *); -static int sort_down_index_string(const void *, const void *); -static int sort_up_index_number(const void *, const void *); -static int sort_down_index_number(const void *, const void *); -static int sort_up_value_string(const void *, const void *); -static int sort_down_value_string(const void *, const void *); -static int sort_up_value_number(const void *, const void *); -static int sort_down_value_number(const void *, const void *); -static int sort_up_value_type(const void *, const void *); -static int sort_down_value_type(const void *, const void *); + return array; +} -/* array_init --- check relevant environment variables */ + +/* init_array --- (re)initialize an array node */ void -array_init() +init_array(NODE *symbol) { - const char *val; - char *endptr; - size_t newval; - - if ((val = getenv("AVG_CHAIN_MAX")) != NULL && isdigit((unsigned char) *val)) { - newval = strtoul(val, & endptr, 10); - if (endptr != val && newval > 0) - AVG_CHAIN_MAX = newval; + symbol->type = Node_var_array; + symbol->array_funcs = null_array_func; + symbol->buckets = NULL; + symbol->table_size = symbol->array_size = 0; + symbol->array_capacity = 0; + + assert(symbol->xarray == NULL); + /* symbol->xarray = NULL; */ + + /* flags, vname, parent_array not (re)initialized */ +} + + +/* null_lookup: assign type to an empty array. */ + +static NODE ** +null_lookup(NODE *symbol, NODE *subs) +{ + int i; + array_ptr *afunc = NULL; + + assert(symbol->table_size == 0); + + /* Check which array type wants to accept this sub; traverse + * array type list in reverse order. + */ + for (i = num_atypes - 1; i >= 1; i--) { + afunc = atypes[i]; + if (afunc[1](symbol, subs) != NULL) + break; } + if (i == 0 || afunc == NULL) + afunc = atypes[0]; /* default is str_array_func */ + symbol->array_funcs = afunc; + + /* We have the right type of array; install the subscript */ + return symbol->alookup(symbol, subs); +} - if ((val = getenv("AWK_HASH")) != NULL && strcmp(val, "gst") == 0) - hash = gst_hash_string; + +/* null_afunc --- dummy function for an empty array */ + +static NODE ** +null_afunc(NODE *symbol ATTRIBUTE_UNUSED, NODE *subs ATTRIBUTE_UNUSED) +{ + return NULL; } +/* null_dump --- dump function for an empty array */ + +static NODE ** +null_dump(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED) +{ + fprintf(output_fp, "array `%s' is empty\n", array_vname(symbol)); + return NULL; +} + + +/* r_in_array --- test whether the array element symbol[subs] exists or not, + * return pointer to value if it does. + */ + +NODE * +r_in_array(NODE *symbol, NODE *subs) +{ + NODE **ret; + + ret = symbol->aexists(symbol, subs); + return (ret ? *ret : NULL); +} + + +/* assoc_copy --- duplicate input array "symbol" */ + +NODE * +assoc_copy(NODE *symbol, NODE *newsymb) +{ + assert(newsymb->vname != NULL); + + assoc_clear(newsymb); + (void) symbol->acopy(symbol, newsymb); + newsymb->array_funcs = symbol->array_funcs; + newsymb->flags = symbol->flags; + return newsymb; +} + + +/* assoc_dump --- dump array */ + +void +assoc_dump(NODE *symbol, NODE *ndump) +{ + if (symbol->adump) + (void) symbol->adump(symbol, ndump); +} + + /* make_aname --- construct a 'vname' for a (sub)array */ -static char * +const char * make_aname(const NODE *symbol) { static char *aname = NULL; @@ -115,10 +241,11 @@ make_aname(const NODE *symbol) erealloc(aname, char *, (max_alen + 1) * sizeof(char *), "make_aname"); } memcpy(aname, symbol->vname, alen + 1); - } + } return aname; -#undef SLEN } +#undef SLEN + /* * array_vname --- print the name of the array @@ -128,7 +255,7 @@ make_aname(const NODE *symbol) * to save it, they have to make a copy. */ -char * +const char * array_vname(const NODE *symbol) { static char *message = NULL; @@ -164,7 +291,6 @@ array_vname(const NODE *symbol) else aname = make_aname(symbol); len += strlen(aname); - /* * Each node contributes by strlen(from) minus the length * of "%s" in the translation (which is at least 2) @@ -218,7 +344,7 @@ get_array(NODE *symbol, int canfatal) NODE *save_symbol = symbol; int isparam = FALSE; - if (symbol->type == Node_param_list && (symbol->flags & FUNC) == 0) { + if (symbol->type == Node_param_list) { save_symbol = symbol = GET_PARAM(symbol->param_cnt); isparam = TRUE; if (symbol->type == Node_array_ref) @@ -227,35 +353,24 @@ get_array(NODE *symbol, int canfatal) switch (symbol->type) { case Node_var_new: - symbol->type = Node_var_array; - symbol->var_array = NULL; + init_array(symbol); symbol->parent_array = NULL; /* main array has no parent */ /* fall through */ case Node_var_array: break; case Node_array_ref: - case Node_param_list: - if ((symbol->flags & FUNC) == 0) - cant_happen(); - /* else - fall through */ - default: - /* notably Node_var but catches also e.g. FS[1] = "x" */ + /* notably Node_var but catches also e.g. a[1] = "x"; a[1][1] = "y" */ if (canfatal) { if (symbol->type == Node_val) fatal(_("attempt to use a scalar value as array")); - - if ((symbol->flags & FUNC) != 0) - fatal(_("attempt to use function `%s' as an array"), - save_symbol->vname); - else if (isparam) + if (isparam) fatal(_("attempt to use scalar parameter `%s' as an array"), - save_symbol->vname); + save_symbol->vname); else fatal(_("attempt to use scalar `%s' as an array"), - save_symbol->vname); + save_symbol->vname); } else break; } @@ -269,16 +384,18 @@ get_array(NODE *symbol, int canfatal) void set_SUBSEP() { - SUBSEP = force_string(SUBSEP_node->var_value)->stptr; + SUBSEP_node->var_value = force_string(SUBSEP_node->var_value); + SUBSEP = SUBSEP_node->var_value->stptr; SUBSEPlen = SUBSEP_node->var_value->stlen; -} +} + /* concat_exp --- concatenate expression list into a single string */ NODE * concat_exp(int nargs, int do_subsep) { - /* do_subsep is false for Node-concat */ + /* do_subsep is FALSE for Op_concat */ NODE *r; char *str; char *s; @@ -295,13 +412,14 @@ concat_exp(int nargs, int do_subsep) len = 0; for (i = 1; i <= nargs; i++) { - r = POP(); + r = TOP(); if (r->type == Node_var_array) { while (--i > 0) DEREF(args_array[i]); /* avoid memory leak */ fatal(_("attempt to use array `%s' in a scalar context"), array_vname(r)); - } - args_array[i] = force_string(r); + } + r = POP_STRING(); + args_array[i] = r; len += r->stlen; } len += (nargs - 1) * subseplen; @@ -329,246 +447,6 @@ concat_exp(int nargs, int do_subsep) } -/* assoc_clear --- flush all the values in symbol[] */ - -void -assoc_clear(NODE *symbol) -{ - long i; - NODE *bucket, *next; - - if (symbol->var_array == NULL) - return; - - for (i = 0; i < symbol->array_size; i++) { - for (bucket = symbol->var_array[i]; bucket != NULL; bucket = next) { - next = bucket->ahnext; - if (bucket->ahvalue->type == Node_var_array) { - NODE *r = bucket->ahvalue; - assoc_clear(r); /* recursively clear all sub-arrays */ - efree(r->vname); - freenode(r); - } else - unref(bucket->ahvalue); - - unref(bucket); /* unref() will free the ahname_str */ - } - symbol->var_array[i] = NULL; - } - efree(symbol->var_array); - symbol->var_array = NULL; - symbol->array_size = symbol->table_size = 0; - symbol->flags &= ~ARRAYMAXED; -} - -/* awk_hash --- calculate the hash function of the string in subs */ - -static unsigned long -awk_hash(const char *s, size_t len, unsigned long hsize, size_t *code) -{ - unsigned long h = 0; - unsigned long htmp; - - /* - * Ozan Yigit's original sdbm hash, copied from Margo Seltzers - * db package. - * - * This is INCREDIBLY ugly, but fast. We break the string up into - * 8 byte units. On the first time through the loop we get the - * "leftover bytes" (strlen % 8). On every other iteration, we - * perform 8 HASHC's so we handle all 8 bytes. Essentially, this - * saves us 7 cmp & branch instructions. If this routine is - * heavily used enough, it's worth the ugly coding. - */ - - /* - * Even more speed: - * #define HASHC h = *s++ + 65599 * h - * Because 65599 = pow(2, 6) + pow(2, 16) - 1 we multiply by shifts - * - * 4/2011: Force the results to 32 bits, to get the same - * result on both 32- and 64-bit systems. This may be a - * bad idea. - */ -#define HASHC htmp = (h << 6); \ - h = *s++ + htmp + (htmp << 10) - h ; \ - htmp &= 0xFFFFFFFF; \ - h &= 0xFFFFFFFF - - h = 0; - - /* "Duff's Device" */ - if (len > 0) { - size_t loop = (len + 8 - 1) >> 3; - - switch (len & (8 - 1)) { - case 0: - do { /* All fall throughs */ - HASHC; - case 7: HASHC; - case 6: HASHC; - case 5: HASHC; - case 4: HASHC; - case 3: HASHC; - case 2: HASHC; - case 1: HASHC; - } while (--loop); - } - } - - if (code != NULL) - *code = h; - - if (h >= hsize) - h %= hsize; - return h; -} - -/* assoc_find --- locate symbol[subs] */ - -static NODE * /* NULL if not found */ -assoc_find(NODE *symbol, NODE *subs, unsigned long hash1, NODE **last) -{ - NODE *bucket, *prev; - const char *s1_str; - size_t s1_len; - NODE *s2; - - for (prev = NULL, bucket = symbol->var_array[hash1]; bucket != NULL; - prev = bucket, bucket = bucket->ahnext) { - /* - * This used to use cmp_nodes() here. That's wrong. - * Array indices are strings; compare as such, always! - */ - s1_str = bucket->ahname_str; - s1_len = bucket->ahname_len; - s2 = subs; - - if (s1_len == s2->stlen) { - if (s1_len == 0 /* "" is a valid index */ - || memcmp(s1_str, s2->stptr, s1_len) == 0) - break; - } - } - if (last != NULL) - *last = prev; - return bucket; -} - -/* in_array --- test whether the array element symbol[subs] exists or not, - * return pointer to value if it does. - */ - -NODE * -in_array(NODE *symbol, NODE *subs) -{ - unsigned long hash1; - NODE *ret; - - assert(symbol->type == Node_var_array); - - if (symbol->var_array == NULL) - return NULL; - - hash1 = hash(subs->stptr, subs->stlen, (unsigned long) symbol->array_size, NULL); - ret = assoc_find(symbol, subs, hash1, NULL); - return (ret ? ret->ahvalue : NULL); -} - -/* - * assoc_lookup: - * Find SYMBOL[SUBS] in the assoc array. Install it with value "" if it - * isn't there. Returns a pointer ala get_lhs to where its value is stored. - * - * SYMBOL is the address of the node (or other pointer) being dereferenced. - * SUBS is a number or string used as the subscript. - */ - -NODE ** -assoc_lookup(NODE *symbol, NODE *subs, int reference) -{ - unsigned long hash1; - NODE *bucket; - size_t code; - - assert(symbol->type == Node_var_array); - - (void) force_string(subs); - - if (symbol->var_array == NULL) { - symbol->array_size = symbol->table_size = 0; /* sanity */ - symbol->flags &= ~ARRAYMAXED; - grow_table(symbol); - hash1 = hash(subs->stptr, subs->stlen, - (unsigned long) symbol->array_size, & code); - } else { - hash1 = hash(subs->stptr, subs->stlen, - (unsigned long) symbol->array_size, & code); - bucket = assoc_find(symbol, subs, hash1, NULL); - if (bucket != NULL) - return &(bucket->ahvalue); - } - - if (do_lint && reference) { - lintwarn(_("reference to uninitialized element `%s[\"%.*s\"]'"), - array_vname(symbol), (int)subs->stlen, subs->stptr); - } - - /* It's not there, install it. */ - if (do_lint && subs->stlen == 0) - lintwarn(_("subscript of array `%s' is null string"), - array_vname(symbol)); - - /* first see if we would need to grow the array, before installing */ - symbol->table_size++; - if ((symbol->flags & ARRAYMAXED) == 0 - && (symbol->table_size / symbol->array_size) > AVG_CHAIN_MAX) { - grow_table(symbol); - /* have to recompute hash value for new size */ - hash1 = code % (unsigned long) symbol->array_size; - } - - getnode(bucket); - bucket->type = Node_ahash; - - /* - * Freeze this string value --- it must never - * change, no matter what happens to the value - * that created it or to CONVFMT, etc. - * - * One day: Use an atom table to track array indices, - * and avoid the extra memory overhead. - */ - bucket->flags |= MALLOC; - bucket->ahname_ref = 1; - - emalloc(bucket->ahname_str, char *, subs->stlen + 2, "assoc_lookup"); - bucket->ahname_len = subs->stlen; - memcpy(bucket->ahname_str, subs->stptr, subs->stlen); - bucket->ahname_str[bucket->ahname_len] = '\0'; - bucket->ahvalue = Nnull_string; - - bucket->ahnext = symbol->var_array[hash1]; - bucket->ahcode = code; - - /* - * Set the numeric value for the index if it's available. Useful - * for numeric sorting by index. Do this only if the numeric - * value is available, instead of all the time, since doing it - * all the time is a big performance hit for something that may - * never be used. - */ - if ((subs->flags & NUMCUR) != 0) { - bucket->ahname_num = subs->numbr; - bucket->flags |= NUMIND; - } - - /* hook it into the symbol table */ - symbol->var_array[hash1] = bucket; - return &(bucket->ahvalue); -} - - /* adjust_fcall_stack: remove subarray(s) of symbol[] from * function call stack. */ @@ -602,7 +480,7 @@ adjust_fcall_stack(NODE *symbol, int nsubs) func = frame_ptr->func_node; if (func == NULL) /* in main */ return; - pcount = func->lnode->param_cnt; + pcount = func->param_cnt; sp = frame_ptr->stack; for (; pcount > 0; pcount--) { @@ -627,12 +505,10 @@ adjust_fcall_stack(NODE *symbol, int nsubs) * function f(c, d) { delete c; ..} * BEGIN { a[0][0] = 1; f(a[0], a[0]); ...} */ - char *save; -local_array: - save = r->vname; - memset(r, '\0', sizeof(NODE)); - r->vname = save; - r->type = Node_var_array; + + init_array(r); + r->parent_array = NULL; + r->flags = 0; continue; } @@ -648,8 +524,10 @@ local_array: * BEGIN { a[0][0][0][0] = 1; f(a[0], a[0][0][0]); .. } * */ - - goto local_array; + init_array(r); + r->parent_array = NULL; + r->flags = 0; + break; } } } @@ -660,18 +538,17 @@ local_array: /* * `symbol' is array - * `nsubs' is number of subscripts + * `nsubs' is no of subscripts */ void do_delete(NODE *symbol, int nsubs) { - unsigned long hash1 = 0; - NODE *subs, *bucket, *last, *r; + NODE *val, *subs; int i; assert(symbol->type == Node_var_array); - subs = bucket = last = r = NULL; /* silence the compiler */ + subs = val = NULL; /* silence the compiler */ /* * The force_string() call is needed to make sure that @@ -683,16 +560,17 @@ do_delete(NODE *symbol, int nsubs) * Without it, the code does not fail. */ -#define free_subs(n) \ -do { \ +#define free_subs(n) do { \ NODE *s = PEEK(n - 1); \ if (s->type == Node_val) { \ - (void) force_string(s); /* may have side effects ? */ \ + (void) force_string(s); /* may have side effects. */ \ DEREF(s); \ } \ } while (--n > 0) - if (nsubs == 0) { /* delete array */ + if (nsubs == 0) { + /* delete array */ + adjust_fcall_stack(symbol, 0); /* fix function call stack; See above. */ assoc_clear(symbol); return; @@ -706,66 +584,46 @@ do { \ free_subs(i); fatal(_("attempt to use array `%s' in a scalar context"), array_vname(subs)); } - (void) force_string(subs); - - last = NULL; /* shut up gcc -Wall */ - hash1 = 0; /* ditto */ - bucket = NULL; /* array may be empty */ - if (symbol->var_array != NULL) { - hash1 = hash(subs->stptr, subs->stlen, - (unsigned long) symbol->array_size, NULL); - bucket = assoc_find(symbol, subs, hash1, &last); - } - - if (bucket == NULL) { - if (do_lint) + val = in_array(symbol, subs); + if (val == NULL) { + if (do_lint) { + subs = force_string(subs); lintwarn(_("delete: index `%s' not in array `%s'"), subs->stptr, array_vname(symbol)); + } /* avoid memory leak, free all subs */ free_subs(i); return; } if (i > 1) { - if (bucket->ahvalue->type != Node_var_array) { + if (val->type != Node_var_array) { /* e.g.: a[1] = 1; delete a[1][1] */ + free_subs(i); + subs = force_string(subs); fatal(_("attempt to use scalar `%s[\"%.*s\"]' as an array"), array_vname(symbol), - (int) bucket->ahname_len, - bucket->ahname_str); + (int) subs->stlen, + subs->stptr); } - symbol = bucket->ahvalue; + symbol = val; + DEREF(subs); } - DEREF(subs); } - r = bucket->ahvalue; - if (r->type == Node_var_array) { - adjust_fcall_stack(r, nsubs); /* fix function call stack; See above. */ - assoc_clear(r); + if (val->type == Node_var_array) { + adjust_fcall_stack(val, nsubs); /* fix function call stack; See above. */ + assoc_clear(val); /* cleared a sub-array, free Node_var_array */ - efree(r->vname); - freenode(r); + efree(val->vname); + freenode(val); } else - unref(r); + unref(val); - if (last != NULL) - last->ahnext = bucket->ahnext; - else - symbol->var_array[hash1] = bucket->ahnext; - - unref(bucket); /* unref() will free the ahname_str */ - symbol->table_size--; - if (symbol->table_size <= 0) { - symbol->table_size = symbol->array_size = 0; - symbol->flags &= ~ARRAYMAXED; - if (symbol->var_array != NULL) { - efree(symbol->var_array); - symbol->var_array = NULL; - } - } + (void) assoc_remove(symbol, subs); + DEREF(subs); #undef free_subs } @@ -782,272 +640,164 @@ do { \ void do_delete_loop(NODE *symbol, NODE **lhs) { - long i; - - assert(symbol->type == Node_var_array); + NODE **list; + NODE fl; - if (symbol->var_array == NULL) + if (array_empty(symbol)) return; - /* get first index value */ - for (i = 0; i < symbol->array_size; i++) { - if (symbol->var_array[i] != NULL) { - unref(*lhs); - *lhs = make_string(symbol->var_array[i]->ahname_str, - symbol->var_array[i]->ahname_len); - break; - } - } + fl.flags = AINDEX|ADELETE; /* need a single index */ + list = symbol->alist(symbol, & fl); + assert(list != NULL); + + unref(*lhs); + *lhs = list[0]; + efree(list); /* blast the array in one shot */ - adjust_fcall_stack(symbol, 0); + adjust_fcall_stack(symbol, 0); assoc_clear(symbol); } -/* grow_table --- grow a hash table */ + +/* value_info --- print scalar node info */ + static void -grow_table(NODE *symbol) +value_info(NODE *n) { - NODE **old, **new, *chain, *next; - int i, j; - unsigned long hash1; - unsigned long oldsize, newsize, k; - /* - * This is an array of primes. We grow the table by an order of - * magnitude each time (not just doubling) so that growing is a - * rare operation. We expect, on average, that it won't happen - * more than twice. When things are very large (> 8K), we just - * double more or less, instead of just jumping from 8K to 64K. - */ - static const long sizes[] = { - 13, 127, 1021, 8191, 16381, 32749, 65497, 131101, 262147, - 524309, 1048583, 2097169, 4194319, 8388617, 16777259, 33554467, - 67108879, 134217757, 268435459, 536870923, 1073741827 - }; - - /* find next biggest hash size */ - newsize = oldsize = symbol->array_size; - for (i = 0, j = sizeof(sizes)/sizeof(sizes[0]); i < j; i++) { - if (oldsize < sizes[i]) { - newsize = sizes[i]; - break; - } - } - if (newsize == oldsize) { /* table already at max (!) */ - symbol->flags |= ARRAYMAXED; +#define PREC_NUM -1 +#define PREC_STR -1 + + if (n == Nnull_string || n == Null_field) { + fprintf(output_fp, "<(null)>"); return; } - /* allocate new table */ - emalloc(new, NODE **, newsize * sizeof(NODE *), "grow_table"); - memset(new, '\0', newsize * sizeof(NODE *)); - - /* brand new hash table, set things up and return */ - if (symbol->var_array == NULL) { - symbol->table_size = 0; - goto done; - } + if ((n->flags & (STRING|STRCUR)) != 0) { + fprintf(output_fp, "<"); + fprintf(output_fp, "\"%.*s\"", PREC_STR, n->stptr); + if ((n->flags & (NUMBER|NUMCUR)) != 0) + fprintf(output_fp, ":%.*g", PREC_NUM, n->numbr); + fprintf(output_fp, ">"); + } else + fprintf(output_fp, "<%.*g>", PREC_NUM, n->numbr); - /* old hash table there, move stuff to new, free old */ - old = symbol->var_array; - for (k = 0; k < oldsize; k++) { - if (old[k] == NULL) - continue; + fprintf(output_fp, ":%s", flags2str(n->flags)); - for (chain = old[k]; chain != NULL; chain = next) { - next = chain->ahnext; - hash1 = chain->ahcode % newsize; + if ((n->flags & FIELD) == 0) + fprintf(output_fp, ":%ld", n->valref); + else + fprintf(output_fp, ":"); - /* remove from old list, add to new */ - chain->ahnext = new[hash1]; - new[hash1] = chain; - } + if ((n->flags & (STRING|STRCUR)) == STRCUR) { + fprintf(output_fp, "]["); + fprintf(output_fp, "stfmt=%d, ", n->stfmt); + fprintf(output_fp, "CONVFMT=\"%s\"", n->stfmt <= -1 ? "%ld" + : fmt_list[n->stfmt]->stptr); } - efree(old); -done: - /* - * note that symbol->table_size does not change if an old array, - * and is explicitly set to 0 if a new one. - */ - symbol->var_array = new; - symbol->array_size = newsize; +#undef PREC_NUM +#undef PREC_STR } -/* pr_node --- print simple node info */ -static void -pr_node(NODE *n) +#ifdef ARRAYDEBUG + +NODE * +do_aoption(int nargs) { - if ((n->flags & NUMBER) != 0) - printf("%s %g p: %p", flags2str(n->flags), n->numbr, n); - else - printf("%s %.*s p: %p", flags2str(n->flags), - (int) n->stlen, n->stptr, n); + int ret = -1; + NODE *opt, *val; + int i; + array_ptr *afunc; + + val = POP_SCALAR(); + opt = POP_SCALAR(); + for (i = 0; i < num_atypes; i++) { + afunc = atypes[i]; + if (afunc[NUM_AFUNCS] && (*afunc[NUM_AFUNCS])(opt, val) != NULL) { + ret = 0; + break; + } + } + DEREF(opt); + DEREF(val); + return make_number((AWKNUM) ret); } +#endif -static void +void indent(int indent_level) { - int k; - for (k = 0; k < indent_level; k++) - putchar('\t'); + int i; + for (i = 0; i < indent_level; i++) + fprintf(output_fp, "%s", indent_char); } -/* assoc_dump --- dump the contents of an array */ +/* assoc_info --- print index, value info */ -NODE * -assoc_dump(NODE *symbol, int indent_level) +void +assoc_info(NODE *subs, NODE *val, NODE *ndump, const char *aname) { - long i; - NODE *bucket; + int indent_level = ndump->alevel; + indent_level++; indent(indent_level); - if (symbol->var_array == NULL) { - printf(_("%s: empty (null)\n"), symbol->vname); - return make_number((AWKNUM) 0); - } - - if (symbol->table_size == 0) { - printf(_("%s: empty (zero)\n"), symbol->vname); - return make_number((AWKNUM) 0); - } + fprintf(output_fp, "I: [%s:", aname); + if ((subs->flags & INTIND) != 0) + fprintf(output_fp, "<%ld>", (long) subs->numbr); + else + value_info(subs); + fprintf(output_fp, "]\n"); - printf(_("%s: table_size = %d, array_size = %d\n"), symbol->vname, - (int) symbol->table_size, (int) symbol->array_size); - - for (i = 0; i < symbol->array_size; i++) { - for (bucket = symbol->var_array[i]; bucket != NULL; - bucket = bucket->ahnext) { - indent(indent_level); - printf("%s: I: [len %d <%.*s> p: %p] V: [", - symbol->vname, - (int) bucket->ahname_len, - (int) bucket->ahname_len, - bucket->ahname_str, - bucket->ahname_str); - if (bucket->ahvalue->type == Node_var_array) { - printf("\n"); - assoc_dump(bucket->ahvalue, indent_level + 1); - indent(indent_level); - } else - pr_node(bucket->ahvalue); - printf("]\n"); - } + indent(indent_level); + if (val->type == Node_val) { + fprintf(output_fp, "V: [scalar: "); + value_info(val); + } else { + fprintf(output_fp, "V: ["); + ndump->alevel++; + ndump->adepth--; + assoc_dump(val, ndump); + ndump->adepth++; + ndump->alevel--; + indent(indent_level); } - - return make_number((AWKNUM) 0); + fprintf(output_fp, "]\n"); } + /* do_adump --- dump an array: interface to assoc_dump */ NODE * do_adump(int nargs) { - NODE *r, *a; + NODE *symbol, *tmp; + static NODE ndump; + long depth = 0; - a = POP(); - if (a->type == Node_param_list) { - printf(_("%s: is parameter\n"), a->vname); - a = GET_PARAM(a->param_cnt); - } - if (a->type == Node_array_ref) { - printf(_("%s: array_ref to %s\n"), a->vname, - a->orig_array->vname); - a = a->orig_array; - } - if (a->type != Node_var_array) - fatal(_("adump: argument not an array")); - r = assoc_dump(a, 0); - return r; -} - -/* - * The following functions implement the builtin - * asort function. Initial work by Alan J. Broder, - * ajb@woti.com. - */ - -/* dup_table --- recursively duplicate input array "symbol" */ + /* depth < 0, no index and value info. + * = 0, main array index and value info; does not descend into sub-arrays. + * > 0, descends into 'depth' sub-arrays, and prints index and value info. + */ -static NODE * -dup_table(NODE *symbol, NODE *newsymb) -{ - NODE **old, **new, *chain, *bucket; - long i; - unsigned long cursize; - - /* find the current hash size */ - cursize = symbol->array_size; - - new = NULL; - - /* input is a brand new hash table, so there's nothing to copy */ - if (symbol->var_array == NULL) - newsymb->table_size = 0; - else { - /* old hash table there, dupnode stuff into a new table */ - - /* allocate new table */ - emalloc(new, NODE **, cursize * sizeof(NODE *), "dup_table"); - memset(new, '\0', cursize * sizeof(NODE *)); - - /* do the copying/dupnode'ing */ - old = symbol->var_array; - for (i = 0; i < cursize; i++) { - if (old[i] != NULL) { - for (chain = old[i]; chain != NULL; - chain = chain->ahnext) { - /* get a node for the linked list */ - getnode(bucket); - bucket->type = Node_ahash; - bucket->flags |= MALLOC; - bucket->ahname_ref = 1; - bucket->ahcode = chain->ahcode; - if ((chain->flags & NUMIND) != 0) { - bucket->ahname_num = chain->ahname_num; - bucket->flags |= NUMIND; - } - - /* - * copy the corresponding name and - * value from the original input list - */ - emalloc(bucket->ahname_str, char *, chain->ahname_len + 2, "dup_table"); - bucket->ahname_len = chain->ahname_len; - - memcpy(bucket->ahname_str, chain->ahname_str, chain->ahname_len); - bucket->ahname_str[bucket->ahname_len] = '\0'; - - if (chain->ahvalue->type == Node_var_array) { - NODE *r; - getnode(r); - r->type = Node_var_array; - r->vname = estrdup(chain->ahname_str, chain->ahname_len); - r->parent_array = newsymb; - bucket->ahvalue = dup_table(chain->ahvalue, r); - } else - bucket->ahvalue = dupnode(chain->ahvalue); - - /* - * put the node on the corresponding - * linked list in the new table - */ - bucket->ahnext = new[i]; - new[i] = bucket; - } - } - } - newsymb->table_size = symbol->table_size; + if (nargs == 2) { + tmp = POP_SCALAR(); + depth = (long) force_number(tmp); + DEREF(tmp); } - - newsymb->var_array = new; - newsymb->array_size = cursize; - newsymb->flags = symbol->flags; /* ARRAYMAXED */ - return newsymb; + symbol = POP_PARAM(); + if (symbol->type != Node_var_array) + fatal(_("adump: first argument not an array")); + + ndump.type = Node_dump_array; + ndump.adepth = depth; + ndump.alevel = 0; + assoc_dump(symbol, & ndump); + return make_number((AWKNUM) 0); } @@ -1058,16 +808,14 @@ asort_actual(int nargs, SORT_CTXT ctxt) { NODE *array, *dest = NULL, *result; NODE *r, *subs, *s; - NODE **list, **ptr; -#define TSIZE 100 /* an arbitrary amount */ - static char buf[TSIZE+2]; + NODE **list = NULL, **ptr; unsigned long num_elems, i; const char *sort_str; if (nargs == 3) /* 3rd optional arg */ s = POP_STRING(); else - s = Nnull_string; /* "" => default sorting */ + s = dupnode(Nnull_string); /* "" => default sorting */ s = force_string(s); sort_str = s->stptr; @@ -1078,7 +826,6 @@ asort_actual(int nargs, SORT_CTXT ctxt) sort_str = "@ind_str_asc"; } - if (nargs >= 2) { /* 2nd optional arg */ dest = POP_PARAM(); if (dest->type != Node_var_array) { @@ -1107,20 +854,21 @@ asort_actual(int nargs, SORT_CTXT ctxt) fatal(ctxt == ASORT ? _("asort: cannot use a subarray of second arg for first arg") : _("asorti: cannot use a subarray of second arg for first arg")); - } + } } num_elems = array->table_size; - if (num_elems == 0 || array->var_array == NULL) { /* source array is empty */ - if (dest != NULL && dest != array) - assoc_clear(dest); - return make_number((AWKNUM) 0); - } - - /* sorting happens inside assoc_list */ - list = assoc_list(array, sort_str, ctxt); + if (num_elems > 0) /* sorting happens inside assoc_list */ + list = assoc_list(array, sort_str, ctxt); DEREF(s); + if (num_elems == 0 || list == NULL) { + /* source array is empty */ + if (dest != NULL && dest != array) + assoc_clear(dest); + return make_number((AWKNUM) 0); + } + /* * Must not assoc_clear() the source array before constructing * the output array. assoc_list() does not duplicate array values @@ -1132,70 +880,52 @@ asort_actual(int nargs, SORT_CTXT ctxt) result = dest; } else { /* use 'result' as a temporary destination array */ - getnode(result); - memset(result, '\0', sizeof(NODE)); - result->type = Node_var_array; + result = make_array(); result->vname = array->vname; result->parent_array = array->parent_array; } - subs = make_str_node(buf, TSIZE, ALREADY_MALLOCED); /* fake it */ - subs->flags &= ~MALLOC; /* safety */ - for (i = 1, ptr = list; i <= num_elems; i++) { - sprintf(buf, "%lu", i); - subs->stlen = strlen(buf); - /* make number valid in case this array gets sorted later */ - subs->numbr = i; - subs->flags |= NUMCUR; - r = *ptr++; - if (ctxt == ASORTI) { - /* - * We want the indices of the source array as values - * of the 'result' array. - */ - *assoc_lookup(result, subs, FALSE) = - make_string(r->ahname_str, r->ahname_len); - } else { - NODE *val; - - /* We want the values of the source array. */ - - val = r->ahvalue; - if (result != dest) { - /* optimization for dest = NULL or dest = array */ - - if (val->type == Node_var_array) { - /* update subarray index in parent array */ - efree(val->vname); - val->vname = estrdup(subs->stptr, subs->stlen); - } - *assoc_lookup(result, subs, FALSE) = val; - r->ahvalue = Nnull_string; - } else { - if (val->type == Node_val) - *assoc_lookup(result, subs, FALSE) = dupnode(val); - else { - NODE *arr; - - /* - * There isn't any reference counting for - * subarrays, so recursively copy subarrays - * using dup_table(). - */ - getnode(arr); - arr->type = Node_var_array; - arr->var_array = NULL; - arr->vname = estrdup(subs->stptr, subs->stlen); - arr->parent_array = array; /* actual parent, not the temporary one. */ - *assoc_lookup(result, subs, FALSE) = dup_table(val, arr); - } + subs = make_number((AWKNUM) 0.0); + + if (ctxt == ASORTI) { + /* We want the indices of the source array. */ + + for (i = 1, ptr = list; i <= num_elems; i++, ptr += 2) { + subs->numbr = (AWKNUM) i; + r = *ptr; + *assoc_lookup(result, subs) = r; + } + } else { + /* We want the values of the source array. */ + + for (i = 1, ptr = list; i <= num_elems; i++) { + subs->numbr = (AWKNUM) i; + + /* free index node */ + r = *ptr++; + unref(r); + + /* value node */ + r = *ptr++; + + /* FIXME: asort(a) optimization */ + + if (r->type == Node_val) + *assoc_lookup(result, subs) = dupnode(r); + else { + NODE *arr; + arr = make_array(); + subs = force_string(subs); + arr->vname = subs->stptr; + subs->stptr = NULL; + subs->flags &= ~STRCUR; + arr->parent_array = array; /* actual parent, not the temporary one. */ + *assoc_lookup(result, subs) = assoc_copy(r, arr); } } + } - unref(r); - } - - freenode(subs); /* stptr(buf) not malloc-ed */ + unref(subs); efree(list); if (result != dest) { @@ -1209,7 +939,6 @@ asort_actual(int nargs, SORT_CTXT ctxt) return make_number((AWKNUM) num_elems); } -#undef TSIZE /* do_asort --- sort array by value */ @@ -1227,6 +956,7 @@ do_asorti(int nargs) return asort_actual(nargs, ASORTI); } + /* * cmp_string --- compare two strings; logic similar to cmp_nodes() in eval.c * except the extra case-sensitive comparison when the case-insensitive @@ -1241,18 +971,10 @@ cmp_string(const NODE *n1, const NODE *n2) int ret; size_t lmin; - assert(n1->type == n2->type); - if (n1->type == Node_ahash) { - s1 = n1->ahname_str; - len1 = n1->ahname_len; - s2 = n2->ahname_str; - len2 = n2->ahname_len; - } else { - s1 = n1->stptr; - len1 = n1->stlen; - s2 = n2->stptr; - len2 = n2->stlen; - } + s1 = n1->stptr; + len1 = n1->stlen; + s2 = n2->stptr; + len2 = n2->stlen; if (len1 == 0) return len2 == 0 ? 0 : -1; @@ -1303,7 +1025,7 @@ sort_up_index_string(const void *p1, const void *p2) } -/* sort_down_index_string --- descending index strings */ +/* sort_down_index_str --- qsort comparison function; descending index strings. */ static int sort_down_index_string(const void *p1, const void *p2) @@ -1326,24 +1048,23 @@ sort_down_index_string(const void *p1, const void *p2) static int sort_up_index_number(const void *p1, const void *p2) { - const NODE *n1, *n2; + const NODE *t1, *t2; int ret; - n1 = *((const NODE *const *) p1); - n2 = *((const NODE *const *) p2); + t1 = *((const NODE *const *) p1); + t2 = *((const NODE *const *) p2); - if (n1->ahname_num < n2->ahname_num) + if (t1->numbr < t2->numbr) ret = -1; else - ret = (n1->ahname_num > n2->ahname_num); + ret = (t1->numbr > t2->numbr); /* break a tie with the index string itself */ if (ret == 0) - return cmp_string(n1, n2); + return cmp_string(t1, t2); return ret; } - /* sort_down_index_number --- qsort comparison function; descending index numbers */ static int @@ -1359,29 +1080,23 @@ static int sort_up_value_string(const void *p1, const void *p2) { const NODE *t1, *t2; - NODE *n1, *n2; - /* we're passed a pair of index (array subscript) nodes */ - t1 = *(const NODE *const *) p1; - t2 = *(const NODE *const *) p2; + t1 = *((const NODE *const *) p1 + 1); + t2 = *((const NODE *const *) p2 + 1); - /* and we want to compare the element values they refer to */ - n1 = t1->ahvalue; - n2 = t2->ahvalue; - - if (n1->type == Node_var_array) { - /* return 0 if n2 is a sub-array too, else return 1 */ - return (n2->type != Node_var_array); + if (t1->type == Node_var_array) { + /* return 0 if t2 is a sub-array too, else return 1 */ + return (t2->type != Node_var_array); } - if (n2->type == Node_var_array) - return -1; /* n1 (scalar) < n2 (sub-array) */ + if (t2->type == Node_var_array) + return -1; /* t1 (scalar) < t2 (sub-array) */ - /* n1 and n2 both have string values; See sort_force_value_string(). */ - return cmp_string(n1, n2); + /* t1 and t2 both have string values */ + return cmp_string(t1, t2); } -/* sort_down_value_string --- descending value string */ +/* sort_down_value_string --- qsort comparison function; descending value string */ static int sort_down_value_string(const void *p1, const void *p2) @@ -1389,50 +1104,46 @@ sort_down_value_string(const void *p1, const void *p2) return -sort_up_value_string(p1, p2); } + /* sort_up_value_number --- qsort comparison function; ascending value number */ static int sort_up_value_number(const void *p1, const void *p2) { - const NODE *t1, *t2; - NODE *n1, *n2; + NODE *t1, *t2; int ret; - /* we're passed a pair of index (array subscript) nodes */ - t1 = *(const NODE *const *) p1; - t2 = *(const NODE *const *) p2; - - /* and we want to compare the element values they refer to */ - n1 = t1->ahvalue; - n2 = t2->ahvalue; + t1 = *((NODE *const *) p1 + 1); + t2 = *((NODE *const *) p2 + 1); - if (n1->type == Node_var_array) { - /* return 0 if n2 is a sub-array too, else return 1 */ - return (n2->type != Node_var_array); + if (t1->type == Node_var_array) { + /* return 0 if t2 is a sub-array too, else return 1 */ + return (t2->type != Node_var_array); } - if (n2->type == Node_var_array) - return -1; /* n1 (scalar) < n2 (sub-array) */ + if (t2->type == Node_var_array) + return -1; /* t1 (scalar) < t2 (sub-array) */ - /* n1 and n2 both Node_val, and force_number'ed */ - if (n1->numbr < n2->numbr) + /* t1 and t2 both Node_val, and force_number'ed */ + if (t1->numbr < t2->numbr) ret = -1; else - ret = (n1->numbr > n2->numbr); + ret = (t1->numbr > t2->numbr); if (ret == 0) { /* * Use string value to guarantee same sort order on all * versions of qsort(). */ - n1 = force_string(n1); - n2 = force_string(n2); - ret = cmp_string(n1, n2); + t1 = force_string(t1); + t2 = force_string(t2); + ret = cmp_string(t1, t2); } return ret; } -/* sort_down_value_number --- descending value number */ + +/* sort_down_value_number --- qsort comparison function; descending value number */ static int sort_down_value_number(const void *p1, const void *p2) @@ -1440,21 +1151,17 @@ sort_down_value_number(const void *p1, const void *p2) return -sort_up_value_number(p1, p2); } + /* sort_up_value_type --- qsort comparison function; ascending value type */ static int sort_up_value_type(const void *p1, const void *p2) { - const NODE *t1, *t2; NODE *n1, *n2; - /* we're passed a pair of index (array subscript) nodes */ - t1 = *(const NODE *const *) p1; - t2 = *(const NODE *const *) p2; - - /* and we want to compare the element values they refer to */ - n1 = t1->ahvalue; - n2 = t2->ahvalue; + /* we want to compare the element values */ + n1 = *((NODE *const *) p1 + 1); + n2 = *((NODE *const *) p2 + 1); /* 1. Arrays vs. scalar, scalar is less than array */ if (n1->type == Node_var_array) { @@ -1472,6 +1179,12 @@ sort_up_value_type(const void *p1, const void *p2) if ((n2->flags & MAYBE_NUM) != 0) (void) force_number(n2); + /* 2.5. Resolve INTIND, so that is STRING, and not NUMBER */ + if ((n1->flags & INTIND) != 0) + (void) force_string(n1); + if ((n2->flags & INTIND) != 0) + (void) force_string(n2); + if ((n1->flags & NUMBER) != 0 && (n2->flags & NUMBER) != 0) { if (n1->numbr < n2->numbr) return -1; @@ -1492,7 +1205,7 @@ sort_up_value_type(const void *p1, const void *p2) return cmp_string(n1, n2); } -/* sort_down_value_type --- descending value type */ +/* sort_down_value_type --- qsort comparison function; descending value type */ static int sort_down_value_type(const void *p1, const void *p2) @@ -1505,33 +1218,32 @@ sort_down_value_type(const void *p1, const void *p2) static int sort_user_func(const void *p1, const void *p2) { - const NODE *t1, *t2; NODE *idx1, *idx2, *val1, *val2; AWKNUM ret; INSTRUCTION *code; - t1 = *((const NODE *const *) p1); - t2 = *((const NODE *const *) p2); - - idx1 = make_string(t1->ahname_str, t1->ahname_len); - idx2 = make_string(t2->ahname_str, t2->ahname_len); - val1 = t1->ahvalue; - val2 = t2->ahvalue; + idx1 = *((NODE *const *) p1); + idx2 = *((NODE *const *) p2); + val1 = *((NODE *const *) p1 + 1); + val2 = *((NODE *const *) p2 + 1); code = TOP()->code_ptr; /* comparison function call instructions */ /* setup 4 arguments to comp_func() */ + UPREF(idx1); PUSH(idx1); if (val1->type == Node_val) UPREF(val1); PUSH(val1); + + UPREF(idx2); PUSH(idx2); if (val2->type == Node_val) UPREF(val2); PUSH(val2); /* execute the comparison function */ - (void) interpret(code); + (void) (*interpret)(code); /* return value of the comparison function */ POP_NUMBER(ret); @@ -1539,113 +1251,75 @@ sort_user_func(const void *p1, const void *p2) return (ret < 0.0) ? -1 : (ret > 0.0); } -/* sort_force_index_number -- pre-process list items for sorting indices as numbers */ - -static void -sort_force_index_number(NODE **list, size_t num_elems) -{ - size_t i; - NODE *r; - static NODE temp_node; - - for (i = 0; i < num_elems; i++) { - r = list[i]; - - if ((r->flags & NUMIND) != 0) /* once in a lifetime is plenty */ - continue; - temp_node.type = Node_val; - temp_node.stptr = r->ahname_str; - temp_node.stlen = r->ahname_len; - temp_node.flags = 0; /* only interested in the return value of r_force_number */ - r->ahname_num = r_force_number(& temp_node); - r->flags |= NUMIND; - } -} - -/* sort_force_value_number -- pre-process list items for sorting values as numbers */ - -static void -sort_force_value_number(NODE **list, size_t num_elems) -{ - size_t i; - NODE *r, *val; - - for (i = 0; i < num_elems; i++) { - r = list[i]; - val = r->ahvalue; - if (val->type == Node_val) - (void) force_number(val); - } -} - -/* sort_force_value_string -- pre-process list items for sorting values as strings */ - -static void -sort_force_value_string(NODE **list, size_t num_elems) -{ - size_t i; - NODE *r, *val; - - for (i = 0; i < num_elems; i++) { - r = list[i]; - val = r->ahvalue; - if (val->type == Node_val) - r->ahvalue = force_string(val); - } -} /* assoc_list -- construct, and optionally sort, a list of array elements */ NODE ** -assoc_list(NODE *array, const char *sort_str, SORT_CTXT sort_ctxt) +assoc_list(NODE *symbol, const char *sort_str, SORT_CTXT sort_ctxt) { - typedef void (*qsort_prefunc)(NODE **, size_t); typedef int (*qsort_compfunc)(const void *, const void *); static const struct qsort_funcs { const char *name; qsort_compfunc comp_func; - qsort_prefunc pre_func; /* pre-processing of list items */ + enum assoc_list_flags flags; } sort_funcs[] = { - { "@ind_str_asc", sort_up_index_string, 0 }, - { "@ind_num_asc", sort_up_index_number, sort_force_index_number }, - { "@val_str_asc", sort_up_value_string, sort_force_value_string }, - { "@val_num_asc", sort_up_value_number, sort_force_value_number }, - { "@ind_str_desc", sort_down_index_string, 0 }, - { "@ind_num_desc", sort_down_index_number, sort_force_index_number }, - { "@val_str_desc", sort_down_value_string, sort_force_value_string }, - { "@val_num_desc", sort_down_value_number, sort_force_value_number }, - { "@val_type_asc", sort_up_value_type, 0 }, - { "@val_type_desc", sort_down_value_type, 0 }, - { "@unsorted", 0, 0 }, - }; +{ "@ind_str_asc", sort_up_index_string, AINDEX|AISTR|AASC }, +{ "@ind_num_asc", sort_up_index_number, AINDEX|AINUM|AASC }, +{ "@val_str_asc", sort_up_value_string, AVALUE|AVSTR|AASC }, +{ "@val_num_asc", sort_up_value_number, AVALUE|AVNUM|AASC }, +{ "@ind_str_desc", sort_down_index_string, AINDEX|AISTR|ADESC }, +{ "@ind_num_desc", sort_down_index_number, AINDEX|AINUM|ADESC }, +{ "@val_str_desc", sort_down_value_string, AVALUE|AVSTR|ADESC }, +{ "@val_num_desc", sort_down_value_number, AVALUE|AVNUM|ADESC }, +{ "@val_type_asc", sort_up_value_type, AVALUE|AASC }, +{ "@val_type_desc", sort_down_value_type, AVALUE|ADESC }, +{ "@unsorted", 0, AINDEX }, +}; + + /* N.B.: AASC and ADESC are hints to the specific array types. + * See cint_list() in cint_array.c. + */ + NODE **list; - NODE *r; - size_t num_elems, i, j; + NODE fl; + unsigned long num_elems, j; + int elem_size, qi; qsort_compfunc cmp_func = 0; - qsort_prefunc pre_func = 0; INSTRUCTION *code = NULL; - int qi; extern int currule; + int save_rule = 0; - num_elems = array->table_size; - assert(num_elems > 0); + num_elems = symbol->table_size; + if (num_elems == 0) + return NULL; + + elem_size = 1; + fl.flags = 0; for (qi = 0, j = sizeof(sort_funcs)/sizeof(sort_funcs[0]); qi < j; qi++) { if (strcmp(sort_funcs[qi].name, sort_str) == 0) break; } - if (qi >= 0 && qi < j) { + if (qi < j) { cmp_func = sort_funcs[qi].comp_func; - pre_func = sort_funcs[qi].pre_func; + fl.flags = sort_funcs[qi].flags; + + if (symbol->array_funcs != cint_array_func) + fl.flags &= ~(AASC|ADESC); - } else { /* unrecognized */ + if (sort_ctxt != SORTED_IN || (fl.flags & AVALUE) != 0) { + /* need index and value pair in the list */ + + fl.flags |= (AINDEX|AVALUE); + elem_size = 2; + } + + } else { /* unrecognized */ NODE *f; const char *sp; - assert(sort_str != NULL); - for (sp = sort_str; *sp != '\0' && ! isspace((unsigned char) *sp); sp++) continue; @@ -1659,7 +1333,10 @@ assoc_list(NODE *array, const char *sort_str, SORT_CTXT sort_ctxt) fatal(_("sort comparison function `%s' is not defined"), sort_str); cmp_func = sort_user_func; - /* pre_func is still NULL */ + + /* need index and value pair in the list */ + fl.flags |= (AVALUE|AINDEX); + elem_size = 2; /* make function call instructions */ code = bcalloc(Op_func_call, 2, 0); @@ -1673,103 +1350,35 @@ assoc_list(NODE *array, const char *sort_str, SORT_CTXT sort_ctxt) * to undefined (0). */ - (code + 1)->inrule = currule; /* save current rule */ + save_rule = currule; /* save current rule */ currule = 0; PUSH_CODE(code); } - /* allocate space for array; the extra space is used in for(i in a) opcode (eval.c) */ - emalloc(list, NODE **, (num_elems + 1) * sizeof(NODE *), "assoc_list"); + list = symbol->alist(symbol, & fl); - /* populate it */ - for (i = j = 0; i < array->array_size; i++) - for (r = array->var_array[i]; r != NULL; r = r->ahnext) - list[j++] = dupnode(r); - list[num_elems] = NULL; + if (list == NULL || ! cmp_func || (fl.flags & (AASC|ADESC)) != 0) + return list; /* empty list or unsorted, or list already sorted */ - if (! cmp_func) /* unsorted */ - return list; - - /* special pre-processing of list items */ - if (pre_func) - pre_func(list, num_elems); - - qsort(list, num_elems, sizeof(NODE *), cmp_func); /* shazzam! */ + qsort(list, num_elems, elem_size * sizeof(NODE *), cmp_func); /* shazzam! */ if (cmp_func == sort_user_func) { code = POP_CODE(); - currule = (code + 1)->inrule; /* restore current rule */ + currule = save_rule; /* restore current rule */ bcfree(code->nexti); /* Op_stop */ bcfree(code); /* Op_func_call */ } - return list; -} - - -/* -From bonzini@gnu.org Mon Oct 28 16:05:26 2002 -Date: Mon, 28 Oct 2002 13:33:03 +0100 -From: Paolo Bonzini <bonzini@gnu.org> -To: arnold@skeeve.com -Subject: Hash function -Message-ID: <20021028123303.GA6832@biancaneve> - -Here is the hash function I'm using in GNU Smalltalk. The scrambling is -needed if you use powers of two as the table sizes. If you use primes it -is not needed. - -To use double-hashing with power-of-two size, you should use the -_gst_hash_string(str, len) as the primary hash and -scramble(_gst_hash_string (str, len)) | 1 as the secondary hash. - -Paolo - -*/ -/* - * ADR: Slightly modified to work w/in the context of gawk. - */ - -static unsigned long -gst_hash_string(const char *str, size_t len, unsigned long hsize, size_t *code) -{ - unsigned long hashVal = 1497032417; /* arbitrary value */ - unsigned long ret; - - while (len--) { - hashVal += *str++; - hashVal += (hashVal << 10); - hashVal ^= (hashVal >> 6); - } - - ret = scramble(hashVal); - - if (code != NULL) - *code = ret; - - if (ret >= hsize) - ret %= hsize; - - return ret; -} + if (sort_ctxt == SORTED_IN && (fl.flags & (AINDEX|AVALUE)) == (AINDEX|AVALUE)) { + /* relocate all index nodes to the first half of the list. */ + for (j = 1; j < num_elems; j++) + list[j] = list[2 * j]; -static unsigned long -scramble(unsigned long x) -{ - if (sizeof(long) == 4) { - int y = ~x; + /* give back extra memory */ - x += (y << 10) | (y >> 22); - x += (x << 6) | (x >> 26); - x -= (x << 16) | (x >> 16); - } else { - x ^= (~x) >> 31; - x += (x << 21) | (x >> 11); - x += (x << 5) | (x >> 27); - x += (x << 27) | (x >> 5); - x += (x << 31); + erealloc(list, NODE **, num_elems * sizeof(NODE *), "assoc_list"); } - return x; + return list; } @@ -256,10 +256,9 @@ extern double gawk_strtod(); #define FALSE 0 #endif -#define LINT_INVALID 1 /* only warn about invalid */ -#define LINT_ALL 2 /* warn about all things */ +#define INT32_BIT 32 -enum defrule {BEGIN = 1, Rule, END, BEGINFILE, ENDFILE, +enum defrule { BEGIN = 1, Rule, END, BEGINFILE, ENDFILE, MAXRULE /* sentinel, not legal */ }; extern const char *const ruletab[]; @@ -278,10 +277,13 @@ typedef enum nodevals { Node_var_new, /* newly created variable, may become an array */ Node_param_list, /* lnode is a variable, rnode is more list */ Node_func, /* lnode is param. list, rnode is body */ + Node_ext_func, /* extension function, code_ptr is builtin code */ Node_hashnode, /* an identifier in the symbol table */ - Node_ahash, /* an array element */ Node_array_ref, /* array passed by ref as parameter */ + Node_array_tree, /* Hashed array tree (HAT) */ + Node_array_leaf, /* Linear 1-D array */ + Node_dump_array, /* array info */ /* program execution -- stack item types */ Node_arrayfor, @@ -291,9 +293,44 @@ typedef enum nodevals { Node_final /* sentry value, not legal */ } NODETYPE; +struct exp_node; + +typedef union bucket_item { + struct { + union bucket_item *next; + char *str; + size_t len; + size_t code; + struct exp_node *name; + struct exp_node *val; + } hs; + struct { + union bucket_item *next; + long li[2]; + struct exp_node *val[2]; + size_t cnt; + } hi; +} BUCKET; + +/* string hash table */ +#define ahnext hs.next +#define ahname hs.name /* a string index node */ +#define ahname_str hs.str /* shallow copy; = ahname->stptr */ +#define ahname_len hs.len /* = ahname->stlen */ +#define ahvalue hs.val +#define ahcode hs.code + +/* integer hash table */ +#define ainext hi.next +#define ainum hi.li /* integer indices */ +#define aivalue hi.val +#define aicount hi.cnt struct exp_instruction; +typedef int (*Func_print)(FILE *, const char *, ...); +typedef struct exp_node **(*array_ptr)(struct exp_node *, struct exp_node *); + /* * NOTE - this struct is a rather kludgey -- it is packed to minimize * space usage, at the expense of cleanliness. Alter at own risk. @@ -305,13 +342,15 @@ typedef struct exp_node { struct exp_node *lptr; struct exp_instruction *li; long ll; + array_ptr *lp; } l; union { struct exp_node *rptr; Regexp *preg; struct exp_node **av; + BUCKET **bv; void (*uptr)(void); - struct exp_instruction *ri; + struct exp_instruction *iptr; } r; union { struct exp_node *extra; @@ -320,16 +359,19 @@ typedef struct exp_node { char **param_list; } x; char *name; + size_t reserved; struct exp_node *rn; + unsigned long cnt; unsigned long reflags; # define CASE 1 # define CONSTANT 2 # define FS_DFLT 4 } nodep; + struct { AWKNUM fltnum; /* this is here for optimal packing of - * the structure on many machines - */ + * the structure on many machines + */ char *sp; size_t slen; long sref; @@ -339,97 +381,122 @@ typedef struct exp_node { size_t wslen; #endif } val; - struct { - AWKNUM num; - struct exp_node *next; - char *name; - size_t length; - struct exp_node *value; - long ref; - size_t code; - } hash; -#define hnext sub.hash.next -#define hname sub.hash.name -#define hlength sub.hash.length -#define hvalue sub.hash.value - -#define ahnext sub.hash.next -#define ahname_str sub.hash.name -#define ahname_len sub.hash.length -#define ahname_num sub.hash.num -#define ahvalue sub.hash.value -#define ahname_ref sub.hash.ref -#define ahcode sub.hash.code } sub; NODETYPE type; - unsigned short flags; -# define MALLOC 1 /* can be free'd */ -# define PERM 2 /* can't be free'd */ -# define STRING 4 /* assigned as string */ -# define STRCUR 8 /* string value is current */ -# define NUMCUR 16 /* numeric value is current */ -# define NUMBER 32 /* assigned as number */ -# define MAYBE_NUM 64 /* user input: if NUMERIC then - * a NUMBER */ -# define ARRAYMAXED 128 /* array is at max size */ -# define FUNC 256 /* this parameter is really a - * function name; see awkgram.y */ -# define FIELD 512 /* this is a field */ -# define INTLSTR 1024 /* use localized version */ -# define NUMIND 2048 /* numeric val of index is current */ -# define WSTRCUR 4096 /* wide str value is current */ + unsigned int flags; + +/* any type */ +# define MALLOC 0x0001 /* can be free'd */ + +/* type = Node_val */ +# define STRING 0x0002 /* assigned as string */ +# define STRCUR 0x0004 /* string value is current */ +# define NUMCUR 0x0008 /* numeric value is current */ +# define NUMBER 0x0010 /* assigned as number */ +# define MAYBE_NUM 0x0020 /* user input: if NUMERIC then + * a NUMBER */ +# define FIELD 0x0040 /* this is a field */ +# define INTLSTR 0x0080 /* use localized version */ +# define NUMINT 0x0100 /* numeric value is an integer */ +# define INTIND 0x0200 /* integral value is array index; + * lazy conversion to string. + */ +# define WSTRCUR 0x0400 /* wide str value is current */ + +/* type = Node_var_array */ +# define ARRAYMAXED 0x0800 /* array is at max size */ +# define HALFHAT 0x1000 /* half-capacity Hashed Array Tree; + * See cint_array.c */ +# define XARRAY 0x2000 } NODE; - #define vname sub.nodep.name #define lnode sub.nodep.l.lptr #define nextp sub.nodep.l.lptr #define rnode sub.nodep.r.rptr -#define param_cnt sub.nodep.l.ll -#define param vname +/* Node_hashnode, Node_param_list */ +#define hnext sub.nodep.r.rptr +#define hname vname +#define hlength sub.nodep.reserved +#define hcode sub.nodep.cnt +#define hvalue sub.nodep.x.extra + +/* Node_param_list, Node_func */ +#define param_cnt sub.nodep.l.ll +/* Node_param_list */ +#define param vname -#define parmlist sub.nodep.x.param_list -#define code_ptr sub.nodep.r.ri +/* Node_func */ +#define fparms sub.nodep.rn +#define code_ptr sub.nodep.r.iptr +/* Node_regex, Node_dynregex */ #define re_reg sub.nodep.r.preg #define re_flags sub.nodep.reflags #define re_text lnode #define re_exp sub.nodep.x.extra -#define re_cnt flags +#define re_cnt flags +/* Node_val */ #define stptr sub.val.sp #define stlen sub.val.slen #define valref sub.val.sref -#define stfmt sub.val.idx - +#define stfmt sub.val.idx #define wstptr sub.val.wsp #define wstlen sub.val.wslen - #define numbr sub.val.fltnum +/* Node_arrayfor */ +#define for_list sub.nodep.r.av +#define for_list_size sub.nodep.reflags +#define cur_idx sub.nodep.l.ll +#define for_array sub.nodep.rn + /* Node_frame: */ #define stack sub.nodep.r.av #define func_node sub.nodep.x.extra #define prev_frame_size sub.nodep.reflags #define reti sub.nodep.l.li +#define num_tail_calls sub.nodep.cnt /* Node_var: */ -#define var_value lnode +#define var_value lnode #define var_update sub.nodep.r.uptr #define var_assign sub.nodep.x.aptr /* Node_var_array: */ -#define var_array sub.nodep.r.av -#define array_size sub.nodep.l.ll -#define table_size sub.nodep.x.xl -#define parent_array sub.nodep.rn +#define buckets sub.nodep.r.bv +#define nodes sub.nodep.r.av +#define array_funcs sub.nodep.l.lp +#define array_base sub.nodep.l.ll +#define table_size sub.nodep.reflags +#define array_size sub.nodep.cnt +#define array_capacity sub.nodep.reserved +#define xarray sub.nodep.rn +#define parent_array sub.nodep.x.extra + +/* array_funcs[0] is the array initialization function and + * array_funcs[1] is the index type checking function + */ +#define alookup array_funcs[2] +#define aexists array_funcs[3] +#define aclear array_funcs[4] +#define aremove array_funcs[5] +#define alist array_funcs[6] +#define acopy array_funcs[7] +#define adump array_funcs[8] +#define NUM_AFUNCS 9 /* # of entries in array_funcs */ /* Node_array_ref: */ #define orig_array lnode #define prev_array rnode +/* Node_array_print */ +#define adepth sub.nodep.l.ll +#define alevel sub.nodep.x.xl + /* --------------------------------lint warning types----------------------------*/ typedef enum lintvals { LINT_illegal, @@ -527,6 +594,7 @@ typedef enum opcodeval { Op_builtin, Op_sub_builtin, /* sub, gsub and gensub */ + Op_ext_builtin, Op_in_array, /* boolean test of membership in array */ /* function call instruction */ @@ -559,7 +627,6 @@ typedef enum opcodeval { Op_after_beginfile, Op_after_endfile, - Op_ext_func, Op_func, Op_exec_count, @@ -660,6 +727,7 @@ typedef struct exp_instruction { /* Op_token */ #define lextok d.name +#define param_count x.xl /* Op_rule */ #define in_rule x.xl @@ -668,11 +736,11 @@ typedef struct exp_instruction { /* Op_K_case, Op_K_default */ #define case_stmt x.xi #define case_exp d.di -#define stmt_start case_exp -#define stmt_end case_stmt -#define match_exp x.xl +#define stmt_start case_exp +#define stmt_end case_stmt +#define match_exp x.xl -#define target_stmt x.xi +#define target_stmt x.xi /* Op_K_switch */ #define switch_end x.xi @@ -697,7 +765,7 @@ typedef struct exp_instruction { #define func_body x.xn /* Op_func_call */ -#define inrule d.dl +#define tail_call d.dl /* Op_subscript */ #define sub_count d.dl @@ -732,9 +800,9 @@ typedef struct exp_instruction { #define assign_ctxt d.dl /* Op_concat */ -#define concat_flag d.dl -#define CSUBSEP 1 -#define CSVAR 2 +#define concat_flag d.dl +#define CSUBSEP 1 +#define CSVAR 2 /* Op_breakpoint */ #define break_pt x.bpt @@ -764,6 +832,10 @@ typedef struct exp_instruction { #define condpair_left d.di #define condpair_right x.xi +/* Op_store_var */ +#define initval x.xn + + typedef struct iobuf { const char *name; /* filename */ int fd; /* file descriptor */ @@ -829,8 +901,8 @@ typedef struct srcfile { struct srcfile *next; struct srcfile *prev; - enum srctype { SRC_CMDLINE = 1, SRC_STDIN, SRC_FILE, SRC_INC } stype; - char *src; /* name on command line or inclde statement */ + enum srctype { SRC_CMDLINE = 1, SRC_STDIN, SRC_FILE, SRC_INC, SRC_EXTLIB } stype; + char *src; /* name on command line or include statement */ char *fullpath; /* full path after AWKPATH search */ time_t mtime; struct stat sbuf; @@ -856,7 +928,7 @@ typedef struct context { SRCFILE srcfiles; int sourceline; char *source; - void (*install_func)(char *); + void (*install_func)(NODE *); struct context *prev; } AWK_CONTEXT; @@ -866,6 +938,20 @@ struct flagtab { const char *name; }; + +typedef struct block_item { + size_t size; + struct block_item *freep; +} BLOCK; + +enum block_id { + BLOCK_INVALID = 0, /* not legal */ + BLOCK_NODE, + BLOCK_BUCKET, + BLOCK_MAX /* count */ +}; + + #ifndef LONG_MAX #define LONG_MAX ((long)(~(1L << (sizeof (long) * 8 - 1)))) #endif @@ -904,23 +990,62 @@ extern NODE *Null_field; extern NODE **fields_arr; extern int sourceline; extern char *source; +extern int (*interpret)(INSTRUCTION *); /* interpreter routine */ + #if __GNUC__ < 2 -extern NODE *_t; /* used as temporary in tree_eval */ +extern NODE *_t; /* used as temporary in macros */ #endif -extern NODE *_r; /* used as temporary in stack macros */ +extern NODE *_r; /* used as temporary in macros */ -extern NODE *nextfree; +extern BLOCK nextfree[]; extern int field0_valid; -extern int do_traditional; -extern int do_posix; -extern int do_intervals; -extern int do_intl; -extern int do_non_decimal_data; -extern int do_profiling; -extern int do_dump_vars; -extern int do_tidy_mem; -extern int do_sandbox; + +extern int do_flags; + +/* only warn about invalid */ +#define DO_LINT_INVALID 0x0001 +/* warn about all things */ +#define DO_LINT_ALL 0x0002 +/* warn about stuff not in V7 awk */ +#define DO_LINT_OLD 0x0004 +/* no gnu extensions, add traditional weirdnesses */ +#define DO_TRADITIONAL 0x0008 +/* turn off gnu and unix extensions */ +#define DO_POSIX 0x0010 +/* dump locale-izable strings to stdout */ +#define DO_INTL 0x0020 +/* allow octal/hex C style DATA. Use with caution! */ +#define DO_NON_DEC_DATA 0x0040 +/* allow {...,...} in regexps, see resetup() */ +#define DO_INTERVALS 0x0080 +/* pretty print the program */ +#define DO_PRETTY_PRINT 0x0100 +/* dump all global variables at end */ +#define DO_DUMP_VARS 0x0200 +/* release vars when done */ +#define DO_TIDY_MEM 0x0400 +/* sandbox mode - disable 'system' function & redirections */ +#define DO_SANDBOX 0x0800 +/* profile the program */ +#define DO_PROFILE 0x1000 +/* debug the program */ +#define DO_DEBUG 0x2000 + + +#define do_traditional (do_flags & DO_TRADITIONAL) +#define do_posix (do_flags & DO_POSIX) +#define do_intl (do_flags & DO_INTL) +#define do_non_decimal_data (do_flags & DO_NON_DEC_DATA) +#define do_intervals (do_flags & DO_INTERVALS) +#define do_pretty_print (do_flags & DO_PRETTY_PRINT) +#define do_profile (do_flags & DO_PROFILE) +#define do_dump_vars (do_flags & DO_DUMP_VARS) +#define do_tidy_mem (do_flags & DO_TIDY_MEM) +#define do_sandbox (do_flags & DO_SANDBOX) +#define do_debug (do_flags & DO_DEBUG) + + extern int do_optimize; extern int use_lc_numeric; extern int exit_val; @@ -929,8 +1054,8 @@ extern int exit_val; #define do_lint 0 #define do_lint_old 0 #else -extern int do_lint; -extern int do_lint_old; +#define do_lint (do_flags & (DO_LINT_INVALID|DO_LINT_ALL)) +#define do_lint_old (do_flags & DO_LINT_OLD) #endif #if MBS_SUPPORT extern int gawk_mb_cur_max; @@ -956,18 +1081,11 @@ extern char envsep; extern char casetable[]; /* for case-independent regexp matching */ -/* - * Provide a way for code to know which program is executing: - * gawk vs dgawk vs pgawk. - */ -enum exe_mode { exe_normal = 1, exe_debugging, exe_profiling }; -extern enum exe_mode which_gawk; /* (defined in eval.c) */ - /* ------------------------- Runtime stack -------------------------------- */ typedef union stack_item { - NODE *rptr; /* variable etc. */ - NODE **lptr; /* address of a variable etc. */ + NODE *rptr; /* variable etc. */ + NODE **lptr; /* address of a variable etc. */ } STACK_ITEM; extern STACK_ITEM *stack_ptr; @@ -975,84 +1093,75 @@ extern NODE *frame_ptr; extern STACK_ITEM *stack_bottom; extern STACK_ITEM *stack_top; -#define decr_sp() (stack_ptr--) +#define decr_sp() (stack_ptr--) #define incr_sp() ((stack_ptr < stack_top) ? ++stack_ptr : grow_stack()) -#define stack_adj(n) (stack_ptr += (n)) -#define stack_empty() (stack_ptr < stack_bottom) - -#define POP() decr_sp()->rptr -#define POP_ADDRESS() decr_sp()->lptr -#define PEEK(n) (stack_ptr - (n))->rptr -#define TOP() stack_ptr->rptr /* same as PEEK(0) */ -#define TOP_ADDRESS() stack_ptr->lptr -#define PUSH(r) (void) (incr_sp()->rptr = (r)) -#define PUSH_ADDRESS(l) (void) (incr_sp()->lptr = (l)) -#define REPLACE(r) (void) (stack_ptr->rptr = (r)) -#define REPLACE_ADDRESS(l) (void) (stack_ptr->lptr = (l)) - +#define stack_adj(n) (stack_ptr += (n)) +#define stack_empty() (stack_ptr < stack_bottom) + +#define POP() decr_sp()->rptr +#define POP_ADDRESS() decr_sp()->lptr +#define PEEK(n) (stack_ptr - (n))->rptr +#define TOP() stack_ptr->rptr /* same as PEEK(0) */ +#define TOP_ADDRESS() stack_ptr->lptr +#define PUSH(r) (void) (incr_sp()->rptr = (r)) +#define PUSH_ADDRESS(l) (void) (incr_sp()->lptr = (l)) +#define REPLACE(r) (void) (stack_ptr->rptr = (r)) +#define REPLACE_ADDRESS(l) (void) (stack_ptr->lptr = (l)) /* function param */ -#define GET_PARAM(n) frame_ptr->stack[n] +#define GET_PARAM(n) frame_ptr->stack[n] /* - * UPREF and DEREF --- simplified versions of dupnode and unref - * UPREF does not handle FIELD node. Most appropriate use is - * for elements on the runtime stack. When in doubt, use dupnode. - */ + * UPREF --- simplified versions of dupnode, does not handle FIELD node. + * Most appropriate use is for elements on the runtime stack. + * When in doubt, use dupnode. + */ -#define DEREF(r) ( _r = (r), (!(_r->flags & PERM) && (--_r->valref == 0)) ? unref(_r) : (void)0 ) +#define UPREF(r) (void) ((r)->valref++) + +#define DEREF(r) ( _r = (r), (--_r->valref == 0) ? r_unref(_r) : (void)0 ) #if __GNUC__ >= 2 -#define UPREF(r) ({ NODE *_t = (r); !(_t->flags & PERM) && _t->valref++;}) #define POP_ARRAY() ({ NODE *_t = POP(); \ - _t->type == Node_var_array ? \ - _t : get_array(_t, TRUE); }) + _t->type == Node_var_array ? _t : get_array(_t, TRUE); }) #define POP_PARAM() ({ NODE *_t = POP(); \ - _t->type == Node_var_array ? \ - _t : get_array(_t, FALSE); }) + _t->type == Node_var_array ? _t : get_array(_t, FALSE); }) -#define POP_NUMBER(x) ({ NODE *_t = POP_SCALAR(); \ - x = force_number(_t); DEREF(_t); }) -#define TOP_NUMBER(x) ({ NODE *_t = TOP_SCALAR(); \ - x = force_number(_t); DEREF(_t); }) +#define POP_NUMBER(x) ({ NODE *_t = POP_SCALAR(); x = force_number(_t); DEREF(_t); }) +#define TOP_NUMBER(x) ({ NODE *_t = TOP_SCALAR(); x = force_number(_t); DEREF(_t); }) -#define POP_SCALAR() ({ NODE *_t = POP(); _t->type != Node_var_array ? _t \ - : (fatal(_("attempt to use array `%s' in a scalar context"), array_vname(_t)), _t);}) -#define TOP_SCALAR() ({ NODE *_t = TOP(); _t->type != Node_var_array ? _t \ - : (fatal(_("attempt to use array `%s' in a scalar context"), array_vname(_t)), _t);}) +#define POP_SCALAR() ({ NODE *_t = POP(); _t->type != Node_var_array ? _t \ + : (fatal(_("attempt to use array `%s' in a scalar context"), array_vname(_t)), _t);}) +#define TOP_SCALAR() ({ NODE *_t = TOP(); _t->type != Node_var_array ? _t \ + : (fatal(_("attempt to use array `%s' in a scalar context"), array_vname(_t)), _t);}) -#else /* not __GNUC__ */ +#define POP_STRING() force_string(POP_SCALAR()) +#define TOP_STRING() force_string(TOP_SCALAR()) -#define UPREF(r) (_t = (r), !(_t->flags & PERM) && _t->valref++) +#else /* not __GNUC__ */ #define POP_ARRAY() (_t = POP(), \ - _t->type == Node_var_array ? \ - _t : get_array(_t, TRUE)) + _t->type == Node_var_array ? _t : get_array(_t, TRUE)) #define POP_PARAM() (_t = POP(), \ - _t->type == Node_var_array ? \ - _t : get_array(_t, FALSE)) + _t->type == Node_var_array ? _t : get_array(_t, FALSE)) -#define POP_NUMBER(x) (_t = POP_SCALAR(), \ - x = force_number(_t), DEREF(_t)) -#define TOP_NUMBER(x) (_t = TOP_SCALAR(), \ - x = force_number(_t), DEREF(_t)) +#define POP_NUMBER(x) (_t = POP_SCALAR(), x = force_number(_t), DEREF(_t)) +#define TOP_NUMBER(x) (_t = TOP_SCALAR(), x = force_number(_t), DEREF(_t)) #define POP_SCALAR() (_t = POP(), _t->type != Node_var_array ? _t \ - : (fatal(_("attempt to use array `%s' in a scalar context"), array_vname(_t)), _t)) + : (fatal(_("attempt to use array `%s' in a scalar context"), array_vname(_t)), _t)) #define TOP_SCALAR() (_t = TOP(), _t->type != Node_var_array ? _t \ - : (fatal(_("attempt to use array `%s' in a scalar context"), array_vname(_t)), _t)) - -#endif /* __GNUC__ */ + : (fatal(_("attempt to use array `%s' in a scalar context"), array_vname(_t)), _t)) +#define POP_STRING() (_r = POP_SCALAR(), m_force_string(_r)) +#define TOP_STRING() (_r = TOP_SCALAR(), m_force_string(_r)) -#define POP_STRING() force_string(POP_SCALAR()) -#define TOP_STRING() force_string(TOP_SCALAR()) +#endif /* __GNUC__ */ /* ------------------------- Pseudo-functions ------------------------- */ - #define is_identchar(c) (isalnum(c) || (c) == '_') #define var_uninitialized(n) ((n)->var_value == Nnull_string) @@ -1060,21 +1169,20 @@ extern STACK_ITEM *stack_top; #define get_lhs(n, r) (n)->type == Node_var && ! var_uninitialized(n) ? \ &((n)->var_value) : r_get_lhs((n), (r)) -#ifdef MPROF -#define getnode(n) emalloc((n), NODE *, sizeof(NODE), "getnode"), \ - (n)->flags = 0 -#define freenode(n) efree(n) -#else /* not MPROF */ -#define getnode(n) (void) (nextfree ? \ - (n = nextfree, nextfree = nextfree->nextp) \ - : (n = more_nodes())) -#define freenode(n) ((n)->flags = 0, (n)->nextp = nextfree, nextfree = (n)) -#endif /* not MPROF */ +#define getblock(p, id, ty) (void) ((p = (ty) nextfree[id].freep) ? \ + (ty) (nextfree[id].freep = ((BLOCK *) p)->freep) \ + : (p = (ty) more_blocks(id))) +#define freeblock(p, id) (void) (((BLOCK *) p)->freep = nextfree[id].freep, \ + nextfree[id].freep = (BLOCK *) p) -#define make_number(x) mk_number((x), (unsigned int)(MALLOC|NUMCUR|NUMBER)) +#define getnode(n) getblock(n, BLOCK_NODE, NODE *) +#define freenode(n) freeblock(n, BLOCK_NODE) -#define make_string(s, l) r_make_str_node((s), (size_t) (l), 0) -#define make_str_node(s, l, f) r_make_str_node((s), (size_t) (l), (f)) +#define getbucket(b) getblock(b, BLOCK_BUCKET, BUCKET *) +#define freebucket(b) freeblock(b, BLOCK_BUCKET) + +#define make_string(s, l) r_make_str_node((s), (l), 0) +#define make_str_node(s, l, f) r_make_str_node((s), (l), (f)) #define SCAN 1 #define ALREADY_MALLOCED 2 @@ -1094,19 +1202,34 @@ extern STACK_ITEM *stack_top; #ifdef GAWKDEBUG #define force_number r_force_number -#define force_string r_force_string +#define dupnode r_dupnode +#define unref r_unref +#define m_force_string r_force_string +extern NODE *r_force_string(NODE *s); #else /* not GAWKDEBUG */ + +#define unref(r) ( _r = (r), (_r == NULL || --_r->valref > 0) ? \ + (void)0 : r_unref(_r) ) + +#define m_force_string(_ts) (((_ts->flags & STRCUR) && \ + (_ts->stfmt == -1 || _ts->stfmt == CONVFMTidx)) ? \ + _ts : format_val(CONVFMT, CONVFMTidx, _ts)) + #if __GNUC__ >= 2 -#define force_number(n) __extension__ ({NODE *_tn = (n);\ - (_tn->flags & NUMCUR) ? _tn->numbr : r_force_number(_tn);}) +#define dupnode(n) __extension__ ({ NODE *_tn = (n); \ + (_tn->flags & MALLOC) ? (_tn->valref++, _tn) : r_dupnode(_tn); }) + +#define force_number(n) __extension__ ({ NODE *_tn = (n);\ + (_tn->flags & NUMCUR) ? _tn->numbr : r_force_number(_tn); }) + +#define force_string(s) __extension__ ({ NODE *_ts = (s); m_force_string(_ts); }) -#define force_string(s) __extension__ ({NODE *_ts = (s);\ - ((_ts->flags & STRCUR) && \ - (_ts->stfmt == -1 || _ts->stfmt == CONVFMTidx)) ?\ - _ts : format_val(CONVFMT, CONVFMTidx, _ts);}) #else /* not __GNUC__ */ +#define dupnode(n) (_t = (n), \ + (_t->flags & MALLOC) ? (_t->valref++, _t) : r_dupnode(_t)) + #define force_number r_force_number -#define force_string r_force_string +#define force_string(s) (_t = (s), m_force_string(_t)) #endif /* __GNUC__ */ #endif /* GAWKDEBUG */ @@ -1119,60 +1242,74 @@ extern int fatal_tag_valid; if (val++) \ memcpy((char *) (stack), (const char *) tag, sizeof(jmp_buf)) #define POP_BINDING(stack, tag, val) \ -if (--val) \ +if (--val) \ memcpy((char *) tag, (const char *) (stack), sizeof(jmp_buf)) -/* ------------- Function prototypes or defs (as appropriate) ------------- */ -typedef int (*Func_print)(FILE *, const char *, ...); +#define array_empty(a) ((a)->table_size == 0) +#define assoc_lookup(a, s) (a)->alookup(a, s) + +/* assoc_clear --- flush all the values in symbol[] */ +#define assoc_clear(a) (void) ((a)->aclear(a, NULL)) + +/* assoc_remove --- remove an index from symbol[] */ +#define assoc_remove(a, s) ((a)->aremove(a, s) != NULL) + + +#if __GNUC__ >= 2 +#define in_array(a, s) ({ NODE **_l; _l = (a)->aexists(a, s); _l ? *_l : NULL; }) +#else /* not __GNUC__ */ +#define in_array(a, s) r_in_array(a, s) +#endif /* __GNUC__ */ + +/* ------------- Function prototypes or defs (as appropriate) ------------- */ /* array.c */ typedef enum sort_context { SORTED_IN = 1, ASORT, ASORTI } SORT_CTXT; -extern NODE **assoc_list(NODE *array, const char *sort_str, SORT_CTXT sort_ctxt); +enum assoc_list_flags { +AINDEX = 0x01, /* list of indices */ +AVALUE = 0x02, /* list of values */ +AINUM = 0x04, /* numeric index */ +AISTR = 0x08, /* string index */ +AVNUM = 0x10, /* numeric scalar value */ +AVSTR = 0x20, /* string scalar value */ +AASC = 0x40, /* ascending order */ +ADESC = 0x80, /* descending order */ +ADELETE = 0x100, /* need a single index; for use in do_delete_loop */ +}; + +extern NODE *make_array(void); +extern void init_array(NODE *symbol); extern NODE *get_array(NODE *symbol, int canfatal); -extern char *array_vname(const NODE *symbol); +extern const char *make_aname(const NODE *symbol); +extern const char *array_vname(const NODE *symbol); extern void array_init(void); +extern int register_array_func(array_ptr *afunc); extern void set_SUBSEP(void); extern NODE *concat_exp(int nargs, int do_subsep); -extern void ahash_unref(NODE *tmp); -extern void assoc_clear(NODE *symbol); -extern NODE *in_array(NODE *symbol, NODE *subs); -extern NODE **assoc_lookup(NODE *symbol, NODE *subs, int reference); +extern NODE *r_in_array(NODE *symbol, NODE *subs); +extern NODE *assoc_copy(NODE *symbol, NODE *newsymb); +extern void assoc_dump(NODE *symbol, NODE *p); +extern NODE **assoc_list(NODE *symbol, const char *sort_str, SORT_CTXT sort_ctxt); +extern void assoc_info(NODE *subs, NODE *val, NODE *p, const char *aname); extern void do_delete(NODE *symbol, int nsubs); extern void do_delete_loop(NODE *symbol, NODE **lhs); -extern NODE *assoc_dump(NODE *symbol, int indent_level); extern NODE *do_adump(int nargs); +extern NODE *do_aoption(int nargs); extern NODE *do_asort(int nargs); extern NODE *do_asorti(int nargs); extern unsigned long (*hash)(const char *s, size_t len, unsigned long hsize, size_t *code); /* awkgram.c */ -extern NODE *mk_symbol(NODETYPE type, NODE *value); -extern NODE *install_symbol(char *name, NODE *value); -extern NODE *remove_symbol(char *name); -extern NODE *lookup(const char *name); -extern NODE *variable(char *name, NODETYPE type); +extern NODE *variable(int location, char *name, NODETYPE type); extern int parse_program(INSTRUCTION **pcode); extern void dump_funcs(void); extern void dump_vars(const char *fname); -extern void release_all_vars(void); extern const char *getfname(NODE *(*)(int)); -extern NODE *stopme(int nargs); extern void shadow_funcs(void); extern int check_special(const char *name); -extern int foreach_func(int (*)(INSTRUCTION *, void *), int, void *); -extern INSTRUCTION *bcalloc(OPCODE op, int size, int srcline); -extern void bcfree(INSTRUCTION *); extern SRCFILE *add_srcfile(int stype, char *src, SRCFILE *curr, int *already_included, int *errcode); extern void register_deferred_variable(const char *name, NODE *(*load_func)(void)); extern int files_are_same(char *path, SRCFILE *src); extern void valinfo(NODE *n, Func_print print_func, FILE *fp); -extern void print_vars(Func_print print_func, FILE *fp); -extern AWK_CONTEXT *new_context(void); -extern void push_context(AWK_CONTEXT *ctxt); -extern void pop_context(); -extern int in_main_context(); -extern void free_context(AWK_CONTEXT *ctxt, int ); -extern void append_symbol(char *name); - /* builtin.c */ extern double double_to_int(double d); extern NODE *do_exp(int nargs); @@ -1221,8 +1358,10 @@ extern int strncasecmpmbs(const unsigned char *, /* eval.c */ extern void PUSH_CODE(INSTRUCTION *cp); extern INSTRUCTION *POP_CODE(void); -extern int interpret(INSTRUCTION *); -extern int cmp_nodes(NODE *, NODE *); +extern void init_interpret(void); +extern int r_interpret(INSTRUCTION *); +extern int debug_interpret(INSTRUCTION *); +extern int cmp_nodes(NODE *p1, NODE *p2); extern void set_IGNORECASE(void); extern void set_OFS(void); extern void set_ORS(void); @@ -1247,14 +1386,12 @@ extern const char *opcode2str(OPCODE type); extern const char *op2str(OPCODE type); extern NODE **r_get_lhs(NODE *n, int reference); extern STACK_ITEM *grow_stack(void); -#ifdef PROFILING extern void dump_fcall_stack(FILE *fp); -#endif /* ext.c */ NODE *do_ext(int nargs); +NODE *load_ext(const char *lib_name, const char *init_func, NODE *obj); #ifdef DYNAMIC void make_builtin(const char *, NODE *(*)(int), int); -size_t get_curfunc_arg_count(void); NODE *get_argument(int); NODE *get_actual_argument(int, int, int); #define get_scalar_argument(i, opt) get_actual_argument((i), (opt), FALSE) @@ -1307,7 +1444,7 @@ extern int flush_io(void); extern int close_io(int *stdio_problem); extern int devopen(const char *name, const char *mode); extern int srcopen(SRCFILE *s); -extern char *find_source(const char *src, struct stat *stb, int *errcode); +extern char *find_source(const char *src, struct stat *stb, int *errcode, int is_extlib); extern NODE *do_getline_redir(int intovar, int redirtype); extern NODE *do_getline(int intovar, IOBUF *iop); extern struct redirect *getredirect(const char *str, int len); @@ -1318,6 +1455,7 @@ extern int arg_assign(char *arg, int initing); extern int is_std_var(const char *var); extern char *estrdup(const char *str, size_t len); extern void update_global_values(); +extern long getenv_long(const char *name); /* msg.c */ extern void gawk_exit(int status); extern void err(const char *s, const char *emsg, va_list argp) ATTRIBUTE_PRINTF(2, 0); @@ -1345,12 +1483,11 @@ extern void pp_string_fp(Func_print print_func, FILE *fp, const char *str, /* node.c */ extern AWKNUM r_force_number(NODE *n); extern NODE *format_val(const char *format, int index, NODE *s); -extern NODE *r_force_string(NODE *s); -extern NODE *dupnode(NODE *n); -extern NODE *mk_number(AWKNUM x, unsigned int flags); -extern NODE *r_make_str_node(const char *s, unsigned long len, int scan); -extern NODE *more_nodes(void); -extern void unref(NODE *tmp); +extern NODE *r_dupnode(NODE *n); +extern NODE *make_number(AWKNUM x); +extern NODE *r_make_str_node(const char *s, size_t len, int flags); +extern void *more_blocks(int id); +extern void r_unref(NODE *tmp); extern int parse_escape(const char **string_ptr); #if MBS_SUPPORT extern NODE *str2wstr(NODE *n, size_t **ptr); @@ -1381,6 +1518,29 @@ extern int reisstring(const char *text, size_t len, Regexp *re, const char *buf) extern int remaybelong(const char *text, size_t len); extern int isnondecimal(const char *str, int use_locale); +/* symbol.c */ +extern NODE *install_symbol(char *name, NODETYPE type); +extern NODE *remove_symbol(NODE *r); +extern void destroy_symbol(NODE *r); +extern void release_symbols(NODE *symlist, int keep_globals); +extern void append_symbol(NODE *r); +extern NODE *lookup(const char *name); +extern NODE *make_params(char **pnames, int pcount); +extern void install_params(NODE *func); +extern void remove_params(NODE *func); +extern void release_all_vars(void); +extern int foreach_func(NODE **table, int (*)(INSTRUCTION *, void *), void *); +extern INSTRUCTION *bcalloc(OPCODE op, int size, int srcline); +extern void bcfree(INSTRUCTION *); +extern AWK_CONTEXT *new_context(void); +extern void push_context(AWK_CONTEXT *ctxt); +extern void pop_context(); +extern int in_main_context(); +extern void free_context(AWK_CONTEXT *ctxt, int ); +extern NODE **variable_list(); +extern NODE **function_list(int sort); +extern void print_vars(NODE **table, Func_print print_func, FILE *fp); + /* floatcomp.c */ #ifdef VMS /* VMS linker weirdness? */ #define Ceil gawk_ceil @@ -86,19 +86,15 @@ static char *get_src_buf(void); static int yylex(void); int yyparse(void); static INSTRUCTION *snode(INSTRUCTION *subn, INSTRUCTION *op); -static int func_install(INSTRUCTION *fp, INSTRUCTION *def); -static void pop_params(NODE *params); -static NODE *make_param(char *pname); +static char **check_params(char *fname, int pcount, INSTRUCTION *list); +static int install_function(char *fname, INSTRUCTION *fi, INSTRUCTION *plist); static NODE *mk_rexp(INSTRUCTION *exp); -static void append_param(char *pname); -static int dup_parms(INSTRUCTION *fp, NODE *func); static void param_sanity(INSTRUCTION *arglist); static int parms_shadow(INSTRUCTION *pc, int *shadow); static int isnoeffect(OPCODE type); static INSTRUCTION *make_assignable(INSTRUCTION *ip); static void dumpintlstr(const char *str, size_t len); static void dumpintlstr2(const char *str1, size_t len1, const char *str2, size_t len2); -static int isarray(NODE *n); static int include_source(INSTRUCTION *file); static void next_sourcefile(void); static char *tokexpand(void); @@ -107,6 +103,7 @@ static char *tokexpand(void); static INSTRUCTION *mk_program(void); static INSTRUCTION *append_rule(INSTRUCTION *pattern, INSTRUCTION *action); +static INSTRUCTION *mk_function(INSTRUCTION *fi, INSTRUCTION *def); static INSTRUCTION *mk_condition(INSTRUCTION *cond, INSTRUCTION *ifp, INSTRUCTION *true_branch, INSTRUCTION *elsep, INSTRUCTION *false_branch); static INSTRUCTION *mk_expression_list(INSTRUCTION *list, INSTRUCTION *s1); @@ -125,16 +122,13 @@ static void add_lint(INSTRUCTION *list, LINTTYPE linttype); enum defref { FUNC_DEFINE, FUNC_USE }; static void func_use(const char *name, enum defref how); static void check_funcs(void); -static void free_bcpool(INSTRUCTION *pl); static ssize_t read_one_line(int fd, void *buffer, size_t count); static int one_line_close(int fd); -static void (*install_func)(char *) = NULL; - static int want_source = FALSE; static int want_regexp; /* lexical scanning kludge */ -static int can_return; /* parsing kludge */ +static char *in_function; /* parsing kludge */ static int rule = 0; const char *const ruletab[] = { @@ -169,27 +163,17 @@ static int continue_allowed; /* kludge for continue */ #define END_SRC -2000 #define YYDEBUG_LEXER_TEXT (lexeme) -static int param_counter; -static NODE *func_params; /* list of parameters for the current function */ static char *tokstart = NULL; static char *tok = NULL; static char *tokend; static int errcount = 0; -static NODE *symbol_list; -extern void destroy_symbol(char *name); - -static long func_count; /* total number of functions */ - -#define HASHSIZE 1021 /* this constant only used here */ -NODE *variables[HASHSIZE]; -static int var_count; /* total number of global variables */ - extern char *source; extern int sourceline; extern SRCFILE *srcfiles; extern INSTRUCTION *rule_list; extern int max_args; +extern NODE **args_array; static INSTRUCTION *rule_block[sizeof(ruletab)]; @@ -206,22 +190,12 @@ static inline INSTRUCTION *list_prepend(INSTRUCTION *l, INSTRUCTION *x); static inline INSTRUCTION *list_merge(INSTRUCTION *l1, INSTRUCTION *l2); extern double fmod(double x, double y); -/* - * This string cannot occur as a real awk identifier. - * Use it as a special token to make function parsing - * uniform, but if it's seen, don't install the function. - * e.g. - * function split(x) { return x } - * function x(a) { return a } - * should only produce one error message, and not core dump. - */ -static char builtin_func[] = "@builtin"; #define YYSTYPE INSTRUCTION * /* Line 268 of yacc.c */ -#line 225 "awkgram.c" +#line 199 "awkgram.c" /* Enabling traces. */ #ifndef YYDEBUG @@ -367,7 +341,7 @@ typedef int YYSTYPE; /* Line 343 of yacc.c */ -#line 371 "awkgram.c" +#line 345 "awkgram.c" #ifdef short # undef short @@ -583,16 +557,16 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 1157 +#define YYLAST 1150 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 74 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 65 +#define YYNNTS 64 /* YYNRULES -- Number of rules. */ -#define YYNRULES 185 +#define YYNRULES 184 /* YYNRULES -- Number of states. */ -#define YYNSTATES 330 +#define YYNSTATES 329 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 @@ -644,110 +618,110 @@ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 4, 7, 10, 13, 16, 19, 22, 25, 30, 32, 35, 37, 38, 40, 45, 47, 49, - 51, 53, 59, 61, 63, 65, 68, 70, 72, 73, - 81, 82, 86, 88, 90, 91, 94, 97, 99, 102, - 105, 109, 111, 121, 128, 137, 146, 159, 171, 173, - 176, 179, 182, 185, 189, 190, 195, 198, 199, 204, - 205, 210, 215, 217, 218, 220, 221, 224, 227, 233, - 238, 240, 243, 246, 248, 250, 252, 254, 256, 260, - 261, 262, 266, 273, 283, 285, 288, 289, 291, 292, - 295, 296, 298, 300, 304, 306, 309, 313, 314, 316, - 317, 319, 321, 325, 327, 330, 334, 338, 342, 346, - 350, 354, 358, 362, 368, 370, 372, 374, 377, 379, - 381, 383, 385, 387, 389, 392, 394, 398, 402, 406, - 410, 414, 418, 422, 425, 428, 434, 439, 443, 447, - 451, 455, 459, 463, 465, 468, 472, 477, 482, 484, - 486, 488, 491, 494, 496, 498, 501, 504, 506, 509, - 514, 515, 517, 518, 521, 523, 526, 528, 532, 534, - 537, 540, 542, 545, 547, 551, 553, 555, 556, 559, - 562, 564, 565, 567, 569, 571 + 51, 53, 59, 61, 63, 65, 68, 70, 72, 79, + 80, 84, 86, 88, 89, 92, 95, 97, 100, 103, + 107, 109, 119, 126, 135, 144, 157, 169, 171, 174, + 177, 180, 183, 187, 188, 193, 196, 197, 202, 203, + 208, 213, 215, 216, 218, 219, 222, 225, 231, 236, + 238, 241, 244, 246, 248, 250, 252, 254, 258, 259, + 260, 264, 271, 281, 283, 286, 287, 289, 290, 293, + 294, 296, 298, 302, 304, 307, 311, 312, 314, 315, + 317, 319, 323, 325, 328, 332, 336, 340, 344, 348, + 352, 356, 360, 366, 368, 370, 372, 375, 377, 379, + 381, 383, 385, 387, 390, 392, 396, 400, 404, 408, + 412, 416, 420, 423, 426, 432, 437, 441, 445, 449, + 453, 457, 461, 463, 466, 470, 475, 480, 482, 484, + 486, 489, 492, 494, 496, 499, 502, 504, 507, 512, + 513, 515, 516, 519, 521, 524, 526, 530, 532, 535, + 538, 540, 543, 545, 549, 551, 553, 554, 557, 560, + 562, 563, 565, 567, 569 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int16 yyrhs[] = { - 75, 0, -1, -1, 75, 76, -1, 75, 104, -1, + 75, 0, -1, -1, 75, 76, -1, 75, 103, -1, 75, 47, -1, 75, 1, -1, 78, 79, -1, 78, - 88, -1, 82, 79, -1, 68, 48, 77, 88, -1, - 6, -1, 6, 1, -1, 1, -1, -1, 112, -1, - 112, 54, 105, 112, -1, 17, -1, 18, -1, 36, - -1, 37, -1, 132, 87, 133, 135, 105, -1, 4, + 87, -1, 82, 79, -1, 68, 48, 77, 87, -1, + 6, -1, 6, 1, -1, 1, -1, -1, 111, -1, + 111, 54, 104, 111, -1, 17, -1, 18, -1, 36, + -1, 37, -1, 131, 86, 132, 134, 104, -1, 4, -1, 3, -1, 81, -1, 68, 49, -1, 45, -1, - 46, -1, -1, 35, 83, 80, 66, 107, 134, 105, - -1, -1, 86, 85, 5, -1, 60, -1, 51, -1, - -1, 87, 89, -1, 87, 1, -1, 104, -1, 136, - 105, -1, 136, 105, -1, 132, 87, 133, -1, 103, - -1, 23, 66, 112, 134, 105, 132, 96, 105, 133, - -1, 26, 66, 112, 134, 105, 89, -1, 27, 105, - 89, 26, 66, 112, 134, 105, -1, 28, 66, 4, - 40, 129, 134, 105, 89, -1, 28, 66, 95, 136, - 105, 112, 136, 105, 95, 134, 105, 89, -1, 28, - 66, 95, 136, 105, 136, 105, 95, 134, 105, 89, - -1, 90, -1, 29, 88, -1, 30, 88, -1, 33, - 88, -1, 39, 88, -1, 34, 109, 88, -1, -1, - 21, 91, 109, 88, -1, 92, 88, -1, -1, 99, - 93, 100, 101, -1, -1, 22, 4, 94, 123, -1, - 22, 66, 4, 67, -1, 112, -1, -1, 92, -1, - -1, 96, 97, -1, 96, 1, -1, 24, 98, 137, - 105, 87, -1, 25, 137, 105, 87, -1, 7, -1, - 58, 7, -1, 57, 7, -1, 8, -1, 84, -1, - 31, -1, 32, -1, 110, -1, 66, 111, 134, -1, - -1, -1, 10, 102, 116, -1, 19, 66, 112, 134, - 105, 89, -1, 19, 66, 112, 134, 105, 89, 20, - 105, 89, -1, 50, -1, 104, 50, -1, -1, 104, - -1, -1, 55, 117, -1, -1, 108, -1, 4, -1, - 108, 138, 4, -1, 1, -1, 108, 1, -1, 108, - 138, 1, -1, -1, 112, -1, -1, 111, -1, 112, - -1, 111, 138, 112, -1, 1, -1, 111, 1, -1, - 111, 1, 112, -1, 111, 138, 1, -1, 130, 113, - 112, -1, 112, 41, 112, -1, 112, 42, 112, -1, - 112, 14, 112, -1, 112, 40, 129, -1, 112, 115, - 112, -1, 112, 52, 112, 53, 112, -1, 116, -1, - 13, -1, 12, -1, 51, 13, -1, 9, -1, 55, - -1, 114, -1, 56, -1, 117, -1, 118, -1, 116, - 117, -1, 119, -1, 117, 64, 117, -1, 117, 59, - 117, -1, 117, 60, 117, -1, 117, 61, 117, -1, - 117, 57, 117, -1, 117, 58, 117, -1, 38, 122, - 106, -1, 130, 43, -1, 130, 44, -1, 66, 111, - 134, 40, 129, -1, 116, 11, 38, 122, -1, 118, - 64, 117, -1, 118, 59, 117, -1, 118, 60, 117, - -1, 118, 61, 117, -1, 118, 57, 117, -1, 118, - 58, 117, -1, 84, -1, 62, 117, -1, 66, 112, - 134, -1, 45, 66, 110, 134, -1, 46, 66, 110, - 134, -1, 46, -1, 120, -1, 130, -1, 43, 130, - -1, 44, 130, -1, 7, -1, 8, -1, 58, 117, - -1, 57, 117, -1, 121, -1, 68, 121, -1, 3, - 66, 110, 134, -1, -1, 130, -1, -1, 124, 16, - -1, 125, -1, 124, 125, -1, 126, -1, 69, 111, - 70, -1, 126, -1, 127, 126, -1, 127, 16, -1, - 4, -1, 4, 128, -1, 129, -1, 65, 119, 131, - -1, 43, -1, 44, -1, -1, 71, 105, -1, 72, - 105, -1, 67, -1, -1, 136, -1, 73, -1, 53, - -1, 54, 105, -1 + 46, -1, 35, 80, 66, 106, 133, 104, -1, -1, + 85, 84, 5, -1, 60, -1, 51, -1, -1, 86, + 88, -1, 86, 1, -1, 103, -1, 135, 104, -1, + 135, 104, -1, 131, 86, 132, -1, 102, -1, 23, + 66, 111, 133, 104, 131, 95, 104, 132, -1, 26, + 66, 111, 133, 104, 88, -1, 27, 104, 88, 26, + 66, 111, 133, 104, -1, 28, 66, 4, 40, 128, + 133, 104, 88, -1, 28, 66, 94, 135, 104, 111, + 135, 104, 94, 133, 104, 88, -1, 28, 66, 94, + 135, 104, 135, 104, 94, 133, 104, 88, -1, 89, + -1, 29, 87, -1, 30, 87, -1, 33, 87, -1, + 39, 87, -1, 34, 108, 87, -1, -1, 21, 90, + 108, 87, -1, 91, 87, -1, -1, 98, 92, 99, + 100, -1, -1, 22, 4, 93, 122, -1, 22, 66, + 4, 67, -1, 111, -1, -1, 91, -1, -1, 95, + 96, -1, 95, 1, -1, 24, 97, 136, 104, 86, + -1, 25, 136, 104, 86, -1, 7, -1, 58, 7, + -1, 57, 7, -1, 8, -1, 83, -1, 31, -1, + 32, -1, 109, -1, 66, 110, 133, -1, -1, -1, + 10, 101, 115, -1, 19, 66, 111, 133, 104, 88, + -1, 19, 66, 111, 133, 104, 88, 20, 104, 88, + -1, 50, -1, 103, 50, -1, -1, 103, -1, -1, + 55, 116, -1, -1, 107, -1, 4, -1, 107, 137, + 4, -1, 1, -1, 107, 1, -1, 107, 137, 1, + -1, -1, 111, -1, -1, 110, -1, 111, -1, 110, + 137, 111, -1, 1, -1, 110, 1, -1, 110, 1, + 111, -1, 110, 137, 1, -1, 129, 112, 111, -1, + 111, 41, 111, -1, 111, 42, 111, -1, 111, 14, + 111, -1, 111, 40, 128, -1, 111, 114, 111, -1, + 111, 52, 111, 53, 111, -1, 115, -1, 13, -1, + 12, -1, 51, 13, -1, 9, -1, 55, -1, 113, + -1, 56, -1, 116, -1, 117, -1, 115, 116, -1, + 118, -1, 116, 64, 116, -1, 116, 59, 116, -1, + 116, 60, 116, -1, 116, 61, 116, -1, 116, 57, + 116, -1, 116, 58, 116, -1, 38, 121, 105, -1, + 129, 43, -1, 129, 44, -1, 66, 110, 133, 40, + 128, -1, 115, 11, 38, 121, -1, 117, 64, 116, + -1, 117, 59, 116, -1, 117, 60, 116, -1, 117, + 61, 116, -1, 117, 57, 116, -1, 117, 58, 116, + -1, 83, -1, 62, 116, -1, 66, 111, 133, -1, + 45, 66, 109, 133, -1, 46, 66, 109, 133, -1, + 46, -1, 119, -1, 129, -1, 43, 129, -1, 44, + 129, -1, 7, -1, 8, -1, 58, 116, -1, 57, + 116, -1, 120, -1, 68, 120, -1, 3, 66, 109, + 133, -1, -1, 129, -1, -1, 123, 16, -1, 124, + -1, 123, 124, -1, 125, -1, 69, 110, 70, -1, + 125, -1, 126, 125, -1, 126, 16, -1, 4, -1, + 4, 127, -1, 128, -1, 65, 118, 130, -1, 43, + -1, 44, -1, -1, 71, 104, -1, 72, 104, -1, + 67, -1, -1, 135, -1, 73, -1, 53, -1, 54, + 104, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 218, 218, 220, 225, 226, 230, 242, 246, 257, - 265, 273, 281, 283, 289, 290, 292, 318, 329, 340, - 346, 355, 365, 367, 369, 380, 385, 386, 391, 390, - 420, 419, 452, 454, 459, 460, 473, 478, 479, 483, - 485, 487, 494, 584, 626, 668, 781, 788, 795, 805, - 814, 823, 832, 847, 863, 862, 874, 886, 886, 982, - 982, 1007, 1030, 1036, 1037, 1043, 1044, 1051, 1056, 1068, - 1082, 1084, 1090, 1095, 1097, 1105, 1107, 1116, 1117, 1125, - 1130, 1130, 1141, 1145, 1153, 1154, 1157, 1159, 1164, 1165, - 1172, 1174, 1178, 1184, 1191, 1193, 1195, 1202, 1203, 1209, - 1210, 1215, 1217, 1222, 1224, 1226, 1228, 1234, 1241, 1243, - 1245, 1261, 1271, 1278, 1280, 1285, 1287, 1289, 1297, 1299, - 1304, 1306, 1311, 1313, 1315, 1368, 1370, 1372, 1374, 1376, - 1378, 1380, 1382, 1405, 1410, 1415, 1440, 1446, 1448, 1450, - 1452, 1454, 1456, 1461, 1465, 1496, 1498, 1504, 1510, 1523, - 1524, 1525, 1530, 1535, 1539, 1543, 1555, 1568, 1573, 1609, - 1627, 1628, 1634, 1635, 1640, 1642, 1649, 1666, 1683, 1685, - 1692, 1697, 1705, 1719, 1731, 1740, 1744, 1748, 1752, 1756, - 1760, 1763, 1765, 1769, 1773, 1777 + 0, 192, 192, 194, 199, 200, 204, 216, 220, 231, + 237, 245, 253, 255, 261, 262, 264, 290, 301, 312, + 318, 327, 337, 339, 341, 347, 352, 353, 357, 376, + 375, 409, 411, 416, 417, 430, 435, 436, 440, 442, + 444, 451, 541, 583, 625, 738, 745, 752, 762, 771, + 780, 789, 804, 820, 819, 843, 855, 855, 949, 949, + 974, 997, 1003, 1004, 1010, 1011, 1018, 1023, 1035, 1049, + 1051, 1057, 1062, 1064, 1072, 1074, 1083, 1084, 1092, 1097, + 1097, 1108, 1112, 1120, 1121, 1124, 1126, 1131, 1132, 1141, + 1142, 1147, 1152, 1158, 1160, 1162, 1169, 1170, 1176, 1177, + 1182, 1184, 1189, 1191, 1193, 1195, 1201, 1208, 1210, 1212, + 1228, 1238, 1245, 1247, 1252, 1254, 1256, 1264, 1266, 1271, + 1273, 1278, 1280, 1282, 1332, 1334, 1336, 1338, 1340, 1342, + 1344, 1346, 1369, 1374, 1379, 1404, 1410, 1412, 1414, 1416, + 1418, 1420, 1425, 1429, 1460, 1462, 1468, 1474, 1487, 1488, + 1489, 1494, 1499, 1503, 1507, 1520, 1533, 1538, 1574, 1592, + 1593, 1599, 1600, 1605, 1607, 1614, 1631, 1648, 1650, 1657, + 1662, 1670, 1680, 1692, 1701, 1705, 1709, 1713, 1717, 1721, + 1724, 1726, 1730, 1734, 1738 }; #endif @@ -769,10 +743,10 @@ static const char *const yytname[] = "'+'", "'-'", "'*'", "'/'", "'%'", "'!'", "UNARY", "'^'", "'$'", "'('", "')'", "'@'", "'['", "']'", "'{'", "'}'", "';'", "$accept", "program", "rule", "source", "pattern", "action", "func_name", "lex_builtin", - "function_prologue", "$@1", "regexp", "$@2", "a_slash", "statements", - "statement_term", "statement", "non_compound_stmt", "$@3", "simple_stmt", - "$@4", "$@5", "opt_simple_stmt", "case_statements", "case_statement", - "case_value", "print", "print_expression_list", "output_redir", "$@6", + "function_prologue", "regexp", "$@1", "a_slash", "statements", + "statement_term", "statement", "non_compound_stmt", "$@2", "simple_stmt", + "$@3", "$@4", "opt_simple_stmt", "case_statements", "case_statement", + "case_value", "print", "print_expression_list", "output_redir", "$@5", "if_statement", "nls", "opt_nls", "input_redir", "opt_param_list", "param_list", "opt_exp", "opt_expression_list", "expression_list", "exp", "assign_operator", "relop_or_less", "a_relop", "common_exp", "simp_exp", @@ -805,23 +779,23 @@ static const yytype_uint8 yyr1[] = { 0, 74, 75, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 78, 78, 78, 78, 78, 78, - 78, 79, 80, 80, 80, 80, 81, 81, 83, 82, - 85, 84, 86, 86, 87, 87, 87, 88, 88, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, 90, - 90, 90, 90, 90, 91, 90, 90, 93, 92, 94, - 92, 92, 92, 95, 95, 96, 96, 96, 97, 97, - 98, 98, 98, 98, 98, 99, 99, 100, 100, 101, - 102, 101, 103, 103, 104, 104, 105, 105, 106, 106, - 107, 107, 108, 108, 108, 108, 108, 109, 109, 110, - 110, 111, 111, 111, 111, 111, 111, 112, 112, 112, - 112, 112, 112, 112, 112, 113, 113, 113, 114, 114, - 115, 115, 116, 116, 116, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 118, 118, 118, 118, - 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 120, 120, 121, - 122, 122, 123, 123, 124, 124, 125, 126, 127, 127, - 128, 129, 129, 130, 130, 131, 131, 131, 132, 133, - 134, 135, 135, 136, 137, 138 + 78, 79, 80, 80, 80, 80, 81, 81, 82, 84, + 83, 85, 85, 86, 86, 86, 87, 87, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 89, 89, + 89, 89, 89, 90, 89, 89, 92, 91, 93, 91, + 91, 91, 94, 94, 95, 95, 95, 96, 96, 97, + 97, 97, 97, 97, 98, 98, 99, 99, 100, 101, + 100, 102, 102, 103, 103, 104, 104, 105, 105, 106, + 106, 107, 107, 107, 107, 107, 108, 108, 109, 109, + 110, 110, 110, 110, 110, 110, 111, 111, 111, 111, + 111, 111, 111, 111, 112, 112, 112, 113, 113, 114, + 114, 115, 115, 115, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 117, 117, 117, 117, 117, + 117, 117, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 119, 119, 120, 121, + 121, 122, 122, 123, 123, 124, 125, 126, 126, 127, + 128, 128, 129, 129, 130, 130, 130, 131, 132, 133, + 134, 134, 135, 136, 137 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -829,23 +803,23 @@ static const yytype_uint8 yyr2[] = { 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, 4, 1, 2, 1, 0, 1, 4, 1, 1, 1, - 1, 5, 1, 1, 1, 2, 1, 1, 0, 7, - 0, 3, 1, 1, 0, 2, 2, 1, 2, 2, - 3, 1, 9, 6, 8, 8, 12, 11, 1, 2, - 2, 2, 2, 3, 0, 4, 2, 0, 4, 0, - 4, 4, 1, 0, 1, 0, 2, 2, 5, 4, - 1, 2, 2, 1, 1, 1, 1, 1, 3, 0, - 0, 3, 6, 9, 1, 2, 0, 1, 0, 2, - 0, 1, 1, 3, 1, 2, 3, 0, 1, 0, - 1, 1, 3, 1, 2, 3, 3, 3, 3, 3, - 3, 3, 3, 5, 1, 1, 1, 2, 1, 1, - 1, 1, 1, 1, 2, 1, 3, 3, 3, 3, - 3, 3, 3, 2, 2, 5, 4, 3, 3, 3, - 3, 3, 3, 1, 2, 3, 4, 4, 1, 1, - 1, 2, 2, 1, 1, 2, 2, 1, 2, 4, - 0, 1, 0, 2, 1, 2, 1, 3, 1, 2, - 2, 1, 2, 1, 3, 1, 1, 0, 2, 2, - 1, 0, 1, 1, 1, 2 + 1, 5, 1, 1, 1, 2, 1, 1, 6, 0, + 3, 1, 1, 0, 2, 2, 1, 2, 2, 3, + 1, 9, 6, 8, 8, 12, 11, 1, 2, 2, + 2, 2, 3, 0, 4, 2, 0, 4, 0, 4, + 4, 1, 0, 1, 0, 2, 2, 5, 4, 1, + 2, 2, 1, 1, 1, 1, 1, 3, 0, 0, + 3, 6, 9, 1, 2, 0, 1, 0, 2, 0, + 1, 1, 3, 1, 2, 3, 0, 1, 0, 1, + 1, 3, 1, 2, 3, 3, 3, 3, 3, 3, + 3, 3, 5, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 2, 1, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 5, 4, 3, 3, 3, 3, + 3, 3, 1, 2, 3, 4, 4, 1, 1, 1, + 2, 2, 1, 1, 2, 2, 1, 2, 4, 0, + 1, 0, 2, 1, 2, 1, 3, 1, 2, 2, + 1, 2, 1, 3, 1, 1, 0, 2, 2, 1, + 0, 1, 1, 1, 2 }; /* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. @@ -853,353 +827,353 @@ static const yytype_uint8 yyr2[] = means the default is an error. */ static const yytype_uint8 yydefact[] = { - 2, 0, 1, 6, 0, 171, 153, 154, 17, 18, - 28, 19, 20, 160, 0, 0, 0, 148, 5, 84, - 33, 0, 0, 32, 0, 0, 0, 0, 3, 0, - 0, 143, 30, 4, 15, 114, 122, 123, 125, 149, - 157, 173, 150, 0, 0, 168, 0, 172, 0, 88, - 161, 151, 152, 0, 0, 0, 156, 150, 155, 144, - 0, 177, 150, 103, 0, 101, 0, 158, 86, 183, - 7, 8, 37, 34, 86, 9, 0, 85, 118, 0, - 0, 0, 0, 0, 86, 119, 121, 120, 0, 0, - 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 116, 115, 133, 134, 0, 0, 0, - 0, 101, 0, 170, 169, 23, 22, 26, 27, 0, - 0, 24, 0, 132, 0, 0, 0, 175, 176, 174, - 104, 86, 180, 0, 0, 145, 13, 0, 0, 87, - 178, 0, 38, 31, 110, 111, 108, 109, 0, 0, - 112, 160, 130, 131, 127, 128, 129, 126, 141, 142, - 138, 139, 140, 137, 117, 107, 159, 167, 25, 0, - 89, 146, 147, 105, 185, 0, 106, 102, 12, 10, - 36, 0, 54, 0, 0, 0, 86, 0, 0, 0, - 75, 76, 0, 97, 0, 86, 35, 48, 0, 57, - 41, 62, 34, 181, 86, 0, 16, 136, 94, 92, - 0, 0, 135, 0, 97, 59, 0, 0, 0, 0, - 63, 49, 50, 51, 0, 98, 52, 179, 56, 0, - 0, 86, 182, 39, 113, 86, 95, 0, 0, 0, - 162, 0, 0, 0, 0, 171, 64, 0, 53, 0, - 79, 77, 40, 21, 29, 96, 93, 86, 55, 60, - 0, 164, 166, 61, 86, 86, 0, 0, 86, 0, - 80, 58, 0, 163, 165, 0, 0, 0, 0, 0, - 78, 0, 82, 65, 43, 0, 86, 0, 86, 81, - 86, 0, 86, 0, 86, 63, 0, 67, 0, 0, - 66, 0, 44, 45, 63, 0, 83, 70, 73, 0, - 0, 74, 0, 184, 86, 42, 0, 86, 72, 71, - 86, 34, 86, 0, 34, 0, 0, 47, 0, 46 + 2, 0, 1, 6, 0, 170, 152, 153, 17, 18, + 0, 19, 20, 159, 0, 0, 0, 147, 5, 83, + 32, 0, 0, 31, 0, 0, 0, 0, 3, 0, + 0, 142, 29, 4, 15, 113, 121, 122, 124, 148, + 156, 172, 149, 0, 0, 167, 0, 171, 23, 22, + 26, 27, 0, 0, 24, 87, 160, 150, 151, 0, + 0, 0, 155, 149, 154, 143, 0, 176, 149, 102, + 0, 100, 0, 157, 85, 182, 7, 8, 36, 33, + 85, 9, 0, 84, 117, 0, 0, 0, 0, 0, + 85, 118, 120, 119, 0, 0, 123, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, + 114, 132, 133, 0, 0, 0, 0, 100, 0, 169, + 168, 25, 0, 0, 131, 0, 0, 0, 174, 175, + 173, 103, 85, 179, 0, 0, 144, 13, 0, 0, + 86, 177, 0, 37, 30, 109, 110, 107, 108, 0, + 0, 111, 159, 129, 130, 126, 127, 128, 125, 140, + 141, 137, 138, 139, 136, 116, 106, 158, 166, 93, + 91, 0, 0, 88, 145, 146, 104, 184, 0, 105, + 101, 12, 10, 35, 0, 53, 0, 0, 0, 85, + 0, 0, 0, 74, 75, 0, 96, 0, 85, 34, + 47, 0, 56, 40, 61, 33, 180, 85, 0, 16, + 135, 85, 94, 0, 134, 0, 96, 58, 0, 0, + 0, 0, 62, 48, 49, 50, 0, 97, 51, 178, + 55, 0, 0, 85, 181, 38, 112, 28, 95, 92, + 0, 0, 161, 0, 0, 0, 0, 170, 63, 0, + 52, 0, 78, 76, 39, 21, 85, 54, 59, 0, + 163, 165, 60, 85, 85, 0, 0, 85, 0, 79, + 57, 0, 162, 164, 0, 0, 0, 0, 0, 77, + 0, 81, 64, 42, 0, 85, 0, 85, 80, 85, + 0, 85, 0, 85, 62, 0, 66, 0, 0, 65, + 0, 43, 44, 62, 0, 82, 69, 72, 0, 0, + 73, 0, 183, 85, 41, 0, 85, 71, 70, 85, + 33, 85, 0, 33, 0, 0, 46, 0, 45 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 28, 138, 29, 70, 120, 121, 30, 48, - 31, 76, 32, 141, 71, 196, 197, 214, 198, 229, - 240, 247, 291, 300, 312, 199, 250, 271, 281, 200, - 139, 140, 123, 210, 211, 224, 109, 110, 201, 108, - 87, 88, 35, 36, 37, 38, 39, 40, 49, 259, - 260, 261, 45, 46, 47, 41, 42, 129, 202, 203, - 135, 231, 204, 314, 134 + -1, 1, 28, 139, 29, 76, 53, 54, 30, 31, + 82, 32, 142, 77, 199, 200, 216, 201, 231, 242, + 249, 290, 299, 311, 202, 252, 270, 280, 203, 140, + 141, 124, 171, 172, 226, 115, 116, 204, 114, 93, + 94, 35, 36, 37, 38, 39, 40, 55, 258, 259, + 260, 45, 46, 47, 41, 42, 130, 205, 206, 136, + 233, 207, 313, 135 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -269 +#define YYPACT_NINF -264 static const yytype_int16 yypact[] = { - -269, 335, -269, -269, -31, -24, -269, -269, -269, -269, - -269, -269, -269, 12, 12, 12, -19, -12, -269, -269, - -269, 978, 978, -269, 978, 1023, 804, 21, -269, 115, - -21, -269, -269, 8, 1062, 952, -20, 330, -269, -269, - -269, -269, 246, 736, 804, -269, 2, -269, 205, 15, - -269, -269, -269, 736, 736, 70, 52, 80, 52, 52, - 978, 147, -269, -269, 50, 308, 174, -269, 64, -269, - -269, -269, 8, -269, 64, -269, 129, -269, -269, 978, - 143, 978, 978, 978, 64, -269, -269, -269, 978, 112, - -20, 978, 978, 978, 978, 978, 978, 978, 978, 978, - 978, 978, 978, -269, -269, -269, -269, 141, 978, 90, - 152, 1101, 48, -269, -269, -269, -269, -269, -269, 111, - 105, -269, 978, -269, 90, 90, 308, -269, -269, -269, - 978, 64, -269, 134, 830, -269, -269, 13, -16, 8, - -269, 552, -269, -269, 53, -269, 142, 300, 1081, 978, - 103, 12, 185, 185, 52, 52, 52, 52, 185, 185, - 52, 52, 52, 52, -269, 1101, -269, -269, -269, 63, - -20, -269, -269, 1101, -269, 143, -269, 1101, -269, -269, - -269, 121, -269, 6, 130, 137, 64, 139, -16, -16, - -269, -269, -16, 978, -16, 64, -269, -269, -16, -269, - -269, 1101, -269, 127, 64, 978, 1101, -269, -269, -269, - 90, 118, -269, 978, 978, -269, 180, 978, 978, 665, - 875, -269, -269, -269, -16, 1101, -269, -269, -269, 598, - 552, 64, -269, -269, 1101, 64, -269, 28, 308, -16, - -24, 140, 308, 308, 189, -14, -269, 127, -269, 804, - 201, -269, -269, -269, -269, -269, -269, 64, -269, -269, - 14, -269, -269, -269, 64, 64, 158, 143, 64, 50, - -269, -269, 665, -269, -269, -21, 665, 978, 90, 710, - 134, 978, 198, -269, -269, 308, 64, 1056, 64, 952, - 64, 60, 64, 665, 64, 907, 665, -269, 119, 177, - -269, 155, -269, -269, 907, 90, -269, -269, -269, 224, - 228, -269, 177, -269, 64, -269, 90, 64, -269, -269, - 64, -269, 64, 665, -269, 406, 665, -269, 479, -269 + -264, 367, -264, -264, -31, -42, -264, -264, -264, -264, + 165, -264, -264, 46, 46, 46, -29, -27, -264, -264, + -264, 1010, 1010, -264, 1010, 1055, 836, 27, -264, -35, + -7, -264, -264, 17, 1088, 984, 288, 362, -264, -264, + -264, -264, 146, 768, 836, -264, 1, -264, -264, -264, + -264, -264, 60, -18, -264, 11, -264, -264, -264, 768, + 768, 74, 52, 9, 52, 52, 1010, 13, -264, -264, + 53, 341, 28, -264, 79, -264, -264, -264, 17, -264, + 79, -264, 119, -264, -264, 1010, 148, 1010, 1010, 1010, + 79, -264, -264, -264, 1010, 122, 288, 1010, 1010, 1010, + 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, -264, + -264, -264, -264, 151, 1010, 94, 81, 1094, 40, -264, + -264, -264, 45, 1010, -264, 94, 94, 341, -264, -264, + -264, 1010, 79, -264, 125, 862, -264, -264, 82, -22, + 17, -264, 584, -264, -264, 62, -264, 212, 267, 301, + 1010, 118, 46, 127, 127, 52, 52, 52, 52, 127, + 127, 52, 52, 52, 52, -264, 1094, -264, -264, -264, + -264, 94, 61, 288, -264, -264, 1094, -264, 148, -264, + 1094, -264, -264, -264, 105, -264, 10, 109, 112, 79, + 113, -22, -22, -264, -264, -22, 1010, -22, 79, -264, + -264, -22, -264, -264, 1094, -264, 107, 79, 1010, 1094, + -264, 79, -264, 43, -264, 1010, 1010, -264, 180, 1010, + 1010, 697, 907, -264, -264, -264, -22, 1094, -264, -264, + -264, 630, 584, 79, -264, -264, 1094, -264, -264, -264, + 341, -22, -42, 126, 341, 341, 166, -14, -264, 107, + -264, 836, 190, -264, -264, -264, 79, -264, -264, 16, + -264, -264, -264, 79, 79, 136, 148, 79, 53, -264, + -264, 697, -264, -264, -7, 697, 1010, 94, 742, 125, + 1010, 186, -264, -264, 341, 79, 278, 79, 984, 79, + 132, 79, 697, 79, 939, 697, -264, 240, 155, -264, + 137, -264, -264, 939, 94, -264, -264, -264, 205, 206, + -264, 155, -264, 79, -264, 94, 79, -264, -264, 79, + -264, 79, 697, -264, 438, 697, -264, 511, -264 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -269, -269, -269, -269, -269, 208, -269, -269, -269, -269, - -58, -269, -269, -193, 72, -171, -269, -269, -189, -269, - -269, -268, -269, -269, -269, -269, -269, -269, -269, -269, - 45, 37, -269, -269, -269, 38, -48, -23, -1, -269, - -269, -269, -26, 44, -269, 217, -269, 1, 102, -269, - -269, -3, -39, -269, -269, -72, -2, -269, -28, -213, - -49, -269, -25, -47, 66 + -264, -264, -264, -264, -264, 187, -264, -264, -264, -74, + -264, -264, -197, 98, -203, -264, -264, -213, -264, -264, + -263, -264, -264, -264, -264, -264, -264, -264, -264, 44, + 73, -264, -264, -264, 18, -54, -23, -1, -264, -264, + -264, -55, 39, -264, 202, -264, 124, 77, -264, -264, + -19, -39, -264, -264, -70, -2, -264, -28, -222, -46, + -264, -25, -79, 70 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -101 +#define YYTABLE_NINF -100 static const yytype_int16 yytable[] = { - 34, 73, 73, 64, 74, 124, 125, 114, 145, 230, - 215, 50, 51, 52, 178, 133, 5, 252, 113, 57, - 57, 112, 57, 62, 4, 65, 267, 305, 67, 255, - 273, 246, 256, 57, 19, 43, 316, 91, 92, 93, - 94, 95, 111, 111, 96, 44, 33, 53, 244, 130, - 68, 130, 111, 111, 54, 44, 67, 69, 77, 126, - 166, 297, 78, -11, 208, 56, 58, 209, 59, 66, - 122, 44, 216, 4, 72, 171, 172, 25, 144, 90, - 146, 147, 148, 44, 298, 299, -11, 150, 315, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 282, 131, 212, 131, 284, 246, 165, 85, 86, - 19, 142, -101, 74, 19, 246, 96, 132, 167, 236, - 57, 149, 303, 105, 106, 306, 307, 308, 325, 173, - -90, 328, -86, 177, 143, 152, 153, 154, 155, 156, - 157, 158, 159, 160, 161, 162, 163, 5, 206, 50, - 151, 78, 327, 130, 164, 329, 79, 132, -101, -101, - 168, 235, -100, 74, 74, 19, 170, 74, 174, 74, - 20, 169, 131, 74, 175, 136, 309, 310, 232, 23, - 137, 251, 80, 72, 241, -91, 68, 213, 69, 257, - 127, 128, 225, 264, 265, 278, 217, 85, 86, 74, - 69, 262, -100, 218, 234, 220, 131, 263, 115, 116, - 179, 270, 238, 225, 74, 266, 242, 243, 290, -100, - 280, 262, 268, 219, 277, -100, 269, 195, 111, 286, - 313, 318, 227, 72, 72, 319, 292, 72, 75, 72, - 311, 233, 61, 72, 93, 94, 95, 283, 65, 96, - 117, 118, 239, 207, 288, 289, 317, 274, 103, 104, - 221, 222, 294, 0, 223, 320, 226, 322, 253, 72, - 228, 0, 254, 119, 0, 0, 285, 237, 287, 57, - 0, 0, 0, 0, 72, 0, 0, 57, 0, 105, - 106, 0, 0, 0, 272, 0, 248, 107, 0, 0, - 0, 275, 276, 0, 0, 279, 0, 0, 0, 78, - 0, 258, 0, 0, 79, 0, 0, 78, 0, 0, - 0, 0, 79, 293, 0, 295, 0, 296, 301, 302, - 0, 304, 0, 90, 0, 2, 3, 0, 4, 5, - 80, 81, 6, 7, 0, 0, 0, 0, 80, 81, - 82, 321, 8, 9, 323, 85, 86, 324, 0, 326, - 83, 0, 0, 85, 86, 0, 0, 0, 0, 0, - 10, 11, 12, 13, 0, 132, 0, 0, 14, 15, - 16, 17, 18, 0, 0, 19, 20, 97, 98, 99, - 100, 101, 21, 22, 102, 23, 0, 24, 0, 0, - 25, 26, 0, 27, 0, 0, -14, 180, -14, 4, - 5, 0, 0, 6, 7, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 181, 0, 182, 183, 184, - -69, -69, 185, 186, 187, 188, 189, 190, 191, 192, - 193, 0, 0, 0, 13, 194, 0, 0, 0, 14, - 15, 16, 17, 0, 0, 0, -69, 20, 0, 0, - 0, 0, 0, 21, 22, 0, 23, 0, 24, 0, - 0, 25, 26, 0, 55, 0, 0, 68, -69, 69, - 180, 0, 4, 5, 0, 0, 6, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 181, 0, - 182, 183, 184, -68, -68, 185, 186, 187, 188, 189, - 190, 191, 192, 193, 0, 0, 0, 13, 194, 0, - 0, 0, 14, 15, 16, 17, 0, 0, 0, -68, - 20, 0, 0, 0, 0, 0, 21, 22, 0, 23, - 0, 24, 0, 0, 25, 26, 0, 55, 0, 0, - 68, -68, 69, 180, 0, 4, 5, 0, 0, 6, - 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 181, 0, 182, 183, 184, 0, 0, 185, 186, - 187, 188, 189, 190, 191, 192, 193, 0, 0, 0, - 13, 194, 0, 0, 0, 14, 15, 16, 17, 63, - 0, 4, 5, 20, 0, 6, 7, 0, -99, 21, - 22, 0, 23, 0, 24, 0, 0, 25, 26, 0, - 55, 0, 0, 68, 195, 69, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, - 0, 14, 15, 16, 17, 0, 0, 0, -99, 20, + 34, 79, 79, 70, 80, 125, 126, 120, 232, 248, + 254, 56, 57, 58, 217, 19, 146, 119, 246, 63, + 63, 118, 63, 68, 134, 71, 266, 44, 19, 137, + 4, 304, 272, 63, 138, 43, 74, 59, 75, 60, + 315, 131, 117, 117, 238, 33, 169, 239, 122, 170, + 5, 75, 111, 112, 131, 44, 128, 129, 117, 117, + 62, 64, 212, 65, 74, 127, 123, 83, 281, 167, + 44, 84, 283, 78, 96, 72, 218, 4, 314, 174, + 175, 248, 131, 181, 145, 44, 147, 148, 149, 302, + 248, -99, 305, 151, 132, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 132, 214, 121, + 168, 25, -89, 166, 80, 132, 102, 91, 92, 326, + 133, 63, 328, 324, 144, 211, 327, -100, -90, 19, + 176, -99, -11, 296, 180, 132, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, -99, 209, + 56, 73, 5, 143, -99, -11, 297, 298, 109, 110, + 152, 133, 173, 150, 165, 178, 80, 80, 48, 49, + 80, 215, 80, -100, -100, 219, 80, 253, 220, 222, + 75, 234, 19, 78, 243, 73, 99, 100, 101, 111, + 112, 102, 265, 262, 256, 227, 277, 113, 263, 264, + 269, 80, 276, 261, -85, 177, 289, 236, 312, 198, + 50, 51, 317, 318, 240, 227, 80, 81, 244, 245, + 261, 84, 279, 310, 267, 288, 85, 67, 268, 210, + 117, 285, 319, 52, 241, 78, 78, 182, 291, 78, + 273, 78, 213, 0, 0, 78, 282, 306, 307, 0, + 71, 0, 86, 287, 0, 0, 0, 0, 316, 0, + 0, 293, 221, 0, 0, 0, 0, 91, 92, 321, + 78, 229, 0, 0, 0, 284, 84, 286, 63, 0, + 235, 85, 0, 0, 237, 78, 63, 84, 0, 223, + 224, 20, 85, 225, 0, 228, 0, 308, 309, 230, + 23, 0, 0, 0, 0, 0, 255, 86, 87, 0, + 84, 0, 0, 0, 0, 85, 0, 0, 86, 87, + 88, 0, 91, 92, 250, 0, 0, 96, 0, 271, + 89, 0, 0, 91, 92, 0, 274, 275, 0, 257, + 278, 86, 87, 88, 0, 97, 98, 99, 100, 101, + 84, 75, 102, 89, 208, 85, 91, 92, 292, 0, + 294, 0, 295, 300, 301, 0, 303, 2, 3, 0, + 4, 5, 0, 0, 6, 7, 0, 0, 0, 0, + 0, 86, 87, 88, 8, 9, 320, 0, 0, 322, + 0, 0, 323, 89, 325, 0, 91, 92, 0, 0, + 0, 0, 10, 11, 12, 13, 0, 0, 133, 0, + 14, 15, 16, 17, 18, 0, 0, 19, 20, 103, + 104, 105, 106, 107, 21, 22, 108, 23, 0, 24, + 0, 0, 25, 26, 0, 27, 0, 0, -14, 183, + -14, 4, 5, 0, 0, 6, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 184, 0, 185, + 186, 187, -68, -68, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 0, 0, 0, 13, 197, 0, 0, + 0, 14, 15, 16, 17, 0, 0, 0, -68, 20, 0, 0, 0, 0, 0, 21, 22, 0, 23, 0, - 24, 0, 0, 25, 249, -99, 55, 0, 4, 5, - 0, -99, 6, 7, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 181, 0, 182, 183, 184, 0, - 0, 185, 186, 187, 188, 189, 190, 191, 192, 193, - 0, 0, 0, 13, 194, 0, 0, 0, 14, 15, - 16, 17, 0, 4, 5, 0, 20, 6, 7, 0, - 0, 0, 21, 22, 0, 23, 0, 24, 0, 0, - 25, 26, 0, 55, 0, 0, 68, 63, 69, 4, - 5, 0, 0, 6, 7, 0, 0, 0, 13, 0, - 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, - 0, 20, 0, 0, 0, 0, 0, 21, 22, 0, - 23, 0, 24, 0, 13, 25, 26, 0, 55, 14, - 15, 16, 17, 69, 0, 0, 0, 20, 0, 0, - 0, 0, 0, 21, 22, 0, 23, 0, 24, 0, - 0, 25, 26, -99, 55, 63, 0, 4, 5, 0, + 24, 0, 0, 25, 26, 0, 61, 0, 0, 74, + -68, 75, 183, 0, 4, 5, 0, 0, 6, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 184, 0, 185, 186, 187, -67, -67, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 0, 0, 0, 13, + 197, 0, 0, 0, 14, 15, 16, 17, 0, 0, + 0, -67, 20, 0, 0, 0, 0, 0, 21, 22, + 0, 23, 0, 24, 0, 0, 25, 26, 0, 61, + 0, 0, 74, -67, 75, 183, 0, 4, 5, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 184, 0, 185, 186, 187, 0, 0, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 0, + 0, 0, 13, 197, 0, 0, 0, 14, 15, 16, + 17, 69, 0, 4, 5, 20, 0, 6, 7, 0, + -98, 21, 22, 0, 23, 0, 24, 0, 0, 25, + 26, 0, 61, 0, 0, 74, 198, 75, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, + 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, + -98, 20, 0, 0, 0, 0, 0, 21, 22, 0, + 23, 0, 24, 0, 0, 25, 251, -98, 61, 0, + 4, 5, 0, -98, 6, 7, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 184, 0, 185, 186, + 187, 0, 0, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 0, 0, 0, 13, 197, 0, 0, 0, + 14, 15, 16, 17, 0, 4, 5, 0, 20, 6, + 7, 0, 0, 0, 21, 22, 0, 23, 0, 24, + 0, 0, 25, 26, 0, 61, 0, 0, 74, 69, + 75, 4, 5, 0, 0, 6, 7, 0, 0, 0, + 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, + 0, 0, 0, 20, 0, 0, 0, 0, 0, 21, + 22, 0, 23, 0, 24, 0, 13, 25, 26, 0, + 61, 14, 15, 16, 17, 75, 0, 0, 0, 20, + 0, 0, 0, 0, 0, 21, 22, 0, 23, 0, + 24, 0, 0, 25, 26, -98, 61, 69, 0, 4, + 5, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 176, 0, 4, 5, 0, 0, 6, 7, 0, + 0, 0, 0, 179, 0, 4, 5, 0, 0, 6, + 7, 0, 0, 0, 13, 0, 0, 0, 0, 14, + 15, 16, 17, 0, 0, 0, 0, 20, 0, 0, + 0, 0, 0, 21, 22, 0, 23, 0, 24, 0, + 13, 25, 26, 0, 61, 14, 15, 16, 17, 0, + 4, 247, 0, 20, 6, 7, 0, 0, 0, 21, + 22, 0, 23, 0, 24, 0, 0, 25, 26, 186, + 61, 0, 0, 0, 0, 0, 0, 0, 193, 194, + 0, 0, 4, 5, 0, 13, 6, 7, 0, 0, + 14, 15, 16, 17, 0, 0, 0, 0, 20, 0, + 0, 186, 0, 0, 21, 22, 0, 23, 0, 24, + 193, 194, 25, 26, 0, 61, 0, 13, 0, 0, + 0, 0, 14, 15, 16, 17, 0, 4, 5, 0, + 20, 6, 7, 0, 0, 95, 21, 22, 0, 23, + 0, 24, 0, 0, 25, 26, 0, 61, 0, 0, + 0, 0, 0, 4, 5, 0, 0, 6, 7, 0, 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 21, 22, 0, 23, 0, 24, 0, 13, 25, - 26, 0, 55, 14, 15, 16, 17, 0, 4, 245, + 26, 0, 61, 14, 15, 16, 17, 0, 4, 5, 0, 20, 6, 7, 0, 0, 0, 21, 22, 0, - 23, 0, 24, 0, 0, 25, 26, 183, 55, 0, - 0, 0, 0, 0, 0, 0, 190, 191, 0, 0, - 4, 5, 0, 13, 6, 7, 0, 0, 14, 15, - 16, 17, 0, 0, 0, 0, 20, 0, 0, 183, - 0, 0, 21, 22, 0, 23, 0, 24, 190, 191, - 25, 26, 0, 55, 0, 13, 0, 0, 0, 0, - 14, 15, 16, 17, 0, 4, 5, 0, 20, 6, - 7, 0, 0, 89, 21, 22, 0, 23, 0, 24, - 0, 0, 25, 26, 0, 55, 0, 0, 0, 0, - 0, 4, 5, 0, 0, 6, 7, 0, 0, 0, - 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, - 0, 0, 0, 20, 0, 0, 0, 0, 0, 21, - 22, 0, 23, 0, 24, 0, 13, 25, 26, 0, - 55, 14, 15, 16, 17, 0, 4, 5, 0, 20, - 6, 7, 0, 0, 0, 21, 22, 0, 23, 0, - 24, 0, 0, 25, 26, 0, 55, 0, 0, 0, + 23, 0, 24, 0, 0, 25, 26, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 78, 14, 15, 16, 17, - 79, 78, 0, 0, 20, 0, 79, 0, 0, 0, - 21, 22, 0, 23, 0, 24, 0, 0, 25, 60, - 78, 55, 0, 0, 0, 79, 80, 81, 82, 0, - 0, 0, 80, 81, 82, 0, 0, 0, 83, 0, - 78, 85, 86, 0, 83, 79, 84, 85, 86, 0, - 0, 80, 81, 82, 0, 0, 0, 0, 0, 69, - 0, 0, 0, 83, 205, 0, 85, 86, 0, 0, - 0, 80, 81, 82, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 83, 0, 0, 85, 86 + 0, 0, 0, 0, 0, 0, 0, 84, 14, 15, + 16, 17, 85, 84, 0, 0, 20, 0, 85, 0, + 0, 0, 21, 22, 0, 23, 0, 24, 0, 0, + 25, 66, 0, 61, 0, 0, 0, 0, 86, 87, + 88, 0, 0, 0, 86, 87, 88, 0, 0, 0, + 89, 0, 90, 91, 92, 0, 89, 0, 0, 91, + 92 }; #define yypact_value_is_default(yystate) \ - ((yystate) == (-269)) + ((yystate) == (-264)) #define yytable_value_is_error(yytable_value) \ - ((yytable_value) == (-101)) + ((yytable_value) == (-100)) static const yytype_int16 yycheck[] = { - 1, 29, 30, 26, 29, 53, 54, 46, 80, 202, - 4, 13, 14, 15, 1, 64, 4, 230, 16, 21, - 22, 44, 24, 25, 3, 26, 40, 295, 27, 1, - 16, 220, 4, 35, 50, 66, 304, 57, 58, 59, - 60, 61, 43, 44, 64, 69, 1, 66, 219, 1, - 71, 1, 53, 54, 66, 69, 55, 73, 50, 60, - 109, 1, 9, 50, 1, 21, 22, 4, 24, 48, - 55, 69, 66, 3, 29, 124, 125, 65, 79, 35, - 81, 82, 83, 69, 24, 25, 73, 88, 301, 91, - 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - 102, 272, 54, 175, 54, 276, 295, 108, 55, 56, - 50, 74, 9, 138, 50, 304, 64, 67, 70, 1, - 122, 84, 293, 43, 44, 296, 7, 8, 321, 130, - 67, 324, 72, 134, 5, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 4, 149, 151, - 38, 9, 323, 1, 13, 326, 14, 67, 55, 56, - 49, 210, 10, 188, 189, 50, 122, 192, 131, 194, - 51, 66, 54, 198, 40, 1, 57, 58, 203, 60, - 6, 229, 40, 138, 4, 67, 71, 66, 73, 238, - 43, 44, 193, 242, 243, 267, 66, 55, 56, 224, - 73, 240, 50, 66, 205, 66, 54, 67, 3, 4, - 138, 10, 213, 214, 239, 26, 217, 218, 20, 67, - 269, 260, 247, 186, 66, 73, 249, 72, 229, 278, - 53, 7, 195, 188, 189, 7, 285, 192, 30, 194, - 298, 204, 25, 198, 59, 60, 61, 275, 249, 64, - 45, 46, 214, 151, 279, 281, 305, 260, 12, 13, - 188, 189, 287, -1, 192, 312, 194, 316, 231, 224, - 198, -1, 235, 68, -1, -1, 277, 211, 279, 281, - -1, -1, -1, -1, 239, -1, -1, 289, -1, 43, - 44, -1, -1, -1, 257, -1, 224, 51, -1, -1, - -1, 264, 265, -1, -1, 268, -1, -1, -1, 9, - -1, 239, -1, -1, 14, -1, -1, 9, -1, -1, - -1, -1, 14, 286, -1, 288, -1, 290, 291, 292, - -1, 294, -1, 289, -1, 0, 1, -1, 3, 4, - 40, 41, 7, 8, -1, -1, -1, -1, 40, 41, - 42, 314, 17, 18, 317, 55, 56, 320, -1, 322, - 52, -1, -1, 55, 56, -1, -1, -1, -1, -1, - 35, 36, 37, 38, -1, 67, -1, -1, 43, 44, - 45, 46, 47, -1, -1, 50, 51, 57, 58, 59, - 60, 61, 57, 58, 64, 60, -1, 62, -1, -1, - 65, 66, -1, 68, -1, -1, 71, 1, 73, 3, - 4, -1, -1, 7, 8, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 19, -1, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, - 34, -1, -1, -1, 38, 39, -1, -1, -1, 43, - 44, 45, 46, -1, -1, -1, 50, 51, -1, -1, - -1, -1, -1, 57, 58, -1, 60, -1, 62, -1, - -1, 65, 66, -1, 68, -1, -1, 71, 72, 73, - 1, -1, 3, 4, -1, -1, 7, 8, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 19, -1, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, -1, -1, -1, 38, 39, -1, - -1, -1, 43, 44, 45, 46, -1, -1, -1, 50, - 51, -1, -1, -1, -1, -1, 57, 58, -1, 60, - -1, 62, -1, -1, 65, 66, -1, 68, -1, -1, - 71, 72, 73, 1, -1, 3, 4, -1, -1, 7, - 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 19, -1, 21, 22, 23, -1, -1, 26, 27, - 28, 29, 30, 31, 32, 33, 34, -1, -1, -1, - 38, 39, -1, -1, -1, 43, 44, 45, 46, 1, - -1, 3, 4, 51, -1, 7, 8, -1, 10, 57, - 58, -1, 60, -1, 62, -1, -1, 65, 66, -1, - 68, -1, -1, 71, 72, 73, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 38, -1, -1, -1, + 1, 29, 30, 26, 29, 59, 60, 46, 205, 222, + 232, 13, 14, 15, 4, 50, 86, 16, 221, 21, + 22, 44, 24, 25, 70, 26, 40, 69, 50, 1, + 3, 294, 16, 35, 6, 66, 71, 66, 73, 66, + 303, 1, 43, 44, 1, 1, 1, 4, 66, 4, + 4, 73, 43, 44, 1, 69, 43, 44, 59, 60, + 21, 22, 1, 24, 71, 66, 55, 50, 271, 115, + 69, 9, 275, 29, 35, 48, 66, 3, 300, 125, + 126, 294, 1, 1, 85, 69, 87, 88, 89, 292, + 303, 10, 295, 94, 54, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 54, 178, 49, + 70, 65, 67, 114, 139, 54, 64, 55, 56, 322, + 67, 123, 325, 320, 5, 171, 323, 9, 67, 50, + 131, 50, 50, 1, 135, 54, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 67, 150, + 152, 27, 4, 80, 73, 73, 24, 25, 12, 13, + 38, 67, 123, 90, 13, 40, 191, 192, 3, 4, + 195, 66, 197, 55, 56, 66, 201, 231, 66, 66, + 73, 206, 50, 139, 4, 61, 59, 60, 61, 43, + 44, 64, 26, 67, 240, 196, 266, 51, 244, 245, + 10, 226, 66, 242, 72, 132, 20, 208, 53, 72, + 45, 46, 7, 7, 215, 216, 241, 30, 219, 220, + 259, 9, 268, 297, 249, 280, 14, 25, 251, 152, + 231, 277, 311, 68, 216, 191, 192, 139, 284, 195, + 259, 197, 172, -1, -1, 201, 274, 7, 8, -1, + 251, -1, 40, 278, -1, -1, -1, -1, 304, -1, + -1, 286, 189, -1, -1, -1, -1, 55, 56, 315, + 226, 198, -1, -1, -1, 276, 9, 278, 280, -1, + 207, 14, -1, -1, 211, 241, 288, 9, -1, 191, + 192, 51, 14, 195, -1, 197, -1, 57, 58, 201, + 60, -1, -1, -1, -1, -1, 233, 40, 41, -1, + 9, -1, -1, -1, -1, 14, -1, -1, 40, 41, + 42, -1, 55, 56, 226, -1, -1, 288, -1, 256, + 52, -1, -1, 55, 56, -1, 263, 264, -1, 241, + 267, 40, 41, 42, -1, 57, 58, 59, 60, 61, + 9, 73, 64, 52, 53, 14, 55, 56, 285, -1, + 287, -1, 289, 290, 291, -1, 293, 0, 1, -1, + 3, 4, -1, -1, 7, 8, -1, -1, -1, -1, + -1, 40, 41, 42, 17, 18, 313, -1, -1, 316, + -1, -1, 319, 52, 321, -1, 55, 56, -1, -1, + -1, -1, 35, 36, 37, 38, -1, -1, 67, -1, + 43, 44, 45, 46, 47, -1, -1, 50, 51, 57, + 58, 59, 60, 61, 57, 58, 64, 60, -1, 62, + -1, -1, 65, 66, -1, 68, -1, -1, 71, 1, + 73, 3, 4, -1, -1, 7, 8, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 19, -1, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, -1, -1, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, -1, 50, 51, -1, -1, -1, -1, -1, 57, 58, -1, 60, -1, - 62, -1, -1, 65, 66, 67, 68, -1, 3, 4, - -1, 73, 7, 8, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 19, -1, 21, 22, 23, -1, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, - -1, -1, -1, 38, 39, -1, -1, -1, 43, 44, - 45, 46, -1, 3, 4, -1, 51, 7, 8, -1, - -1, -1, 57, 58, -1, 60, -1, 62, -1, -1, - 65, 66, -1, 68, -1, -1, 71, 1, 73, 3, - 4, -1, -1, 7, 8, -1, -1, -1, 38, -1, - -1, -1, -1, 43, 44, 45, 46, -1, -1, -1, - -1, 51, -1, -1, -1, -1, -1, 57, 58, -1, - 60, -1, 62, -1, 38, 65, 66, -1, 68, 43, - 44, 45, 46, 73, -1, -1, -1, 51, -1, -1, - -1, -1, -1, 57, 58, -1, 60, -1, 62, -1, - -1, 65, 66, 67, 68, 1, -1, 3, 4, -1, + 62, -1, -1, 65, 66, -1, 68, -1, -1, 71, + 72, 73, 1, -1, 3, 4, -1, -1, 7, 8, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, -1, -1, -1, 38, + 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, + -1, 50, 51, -1, -1, -1, -1, -1, 57, 58, + -1, 60, -1, 62, -1, -1, 65, 66, -1, 68, + -1, -1, 71, 72, 73, 1, -1, 3, 4, -1, -1, 7, 8, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 19, -1, 21, 22, 23, -1, -1, + 26, 27, 28, 29, 30, 31, 32, 33, 34, -1, + -1, -1, 38, 39, -1, -1, -1, 43, 44, 45, + 46, 1, -1, 3, 4, 51, -1, 7, 8, -1, + 10, 57, 58, -1, 60, -1, 62, -1, -1, 65, + 66, -1, 68, -1, -1, 71, 72, 73, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 38, -1, + -1, -1, -1, 43, 44, 45, 46, -1, -1, -1, + 50, 51, -1, -1, -1, -1, -1, 57, 58, -1, + 60, -1, 62, -1, -1, 65, 66, 67, 68, -1, + 3, 4, -1, 73, 7, 8, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 19, -1, 21, 22, + 23, -1, -1, 26, 27, 28, 29, 30, 31, 32, + 33, 34, -1, -1, -1, 38, 39, -1, -1, -1, + 43, 44, 45, 46, -1, 3, 4, -1, 51, 7, + 8, -1, -1, -1, 57, 58, -1, 60, -1, 62, + -1, -1, 65, 66, -1, 68, -1, -1, 71, 1, + 73, 3, 4, -1, -1, 7, 8, -1, -1, -1, + 38, -1, -1, -1, -1, 43, 44, 45, 46, -1, + -1, -1, -1, 51, -1, -1, -1, -1, -1, 57, + 58, -1, 60, -1, 62, -1, 38, 65, 66, -1, + 68, 43, 44, 45, 46, 73, -1, -1, -1, 51, + -1, -1, -1, -1, -1, 57, 58, -1, 60, -1, + 62, -1, -1, 65, 66, 67, 68, 1, -1, 3, + 4, -1, -1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 1, -1, 3, 4, -1, -1, 7, 8, -1, + -1, -1, -1, 1, -1, 3, 4, -1, -1, 7, + 8, -1, -1, -1, 38, -1, -1, -1, -1, 43, + 44, 45, 46, -1, -1, -1, -1, 51, -1, -1, + -1, -1, -1, 57, 58, -1, 60, -1, 62, -1, + 38, 65, 66, -1, 68, 43, 44, 45, 46, -1, + 3, 4, -1, 51, 7, 8, -1, -1, -1, 57, + 58, -1, 60, -1, 62, -1, -1, 65, 66, 22, + 68, -1, -1, -1, -1, -1, -1, -1, 31, 32, + -1, -1, 3, 4, -1, 38, 7, 8, -1, -1, + 43, 44, 45, 46, -1, -1, -1, -1, 51, -1, + -1, 22, -1, -1, 57, 58, -1, 60, -1, 62, + 31, 32, 65, 66, -1, 68, -1, 38, -1, -1, + -1, -1, 43, 44, 45, 46, -1, 3, 4, -1, + 51, 7, 8, -1, -1, 11, 57, 58, -1, 60, + -1, 62, -1, -1, 65, 66, -1, 68, -1, -1, + -1, -1, -1, 3, 4, -1, -1, 7, 8, -1, -1, -1, 38, -1, -1, -1, -1, 43, 44, 45, 46, -1, -1, -1, -1, 51, -1, -1, -1, -1, -1, 57, 58, -1, 60, -1, 62, -1, 38, 65, 66, -1, 68, 43, 44, 45, 46, -1, 3, 4, -1, 51, 7, 8, -1, -1, -1, 57, 58, -1, - 60, -1, 62, -1, -1, 65, 66, 22, 68, -1, - -1, -1, -1, -1, -1, -1, 31, 32, -1, -1, - 3, 4, -1, 38, 7, 8, -1, -1, 43, 44, - 45, 46, -1, -1, -1, -1, 51, -1, -1, 22, - -1, -1, 57, 58, -1, 60, -1, 62, 31, 32, - 65, 66, -1, 68, -1, 38, -1, -1, -1, -1, - 43, 44, 45, 46, -1, 3, 4, -1, 51, 7, - 8, -1, -1, 11, 57, 58, -1, 60, -1, 62, - -1, -1, 65, 66, -1, 68, -1, -1, -1, -1, - -1, 3, 4, -1, -1, 7, 8, -1, -1, -1, - 38, -1, -1, -1, -1, 43, 44, 45, 46, -1, - -1, -1, -1, 51, -1, -1, -1, -1, -1, 57, - 58, -1, 60, -1, 62, -1, 38, 65, 66, -1, - 68, 43, 44, 45, 46, -1, 3, 4, -1, 51, - 7, 8, -1, -1, -1, 57, 58, -1, 60, -1, - 62, -1, -1, 65, 66, -1, 68, -1, -1, -1, + 60, -1, 62, -1, -1, 65, 66, -1, 68, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 9, 43, 44, 45, 46, - 14, 9, -1, -1, 51, -1, 14, -1, -1, -1, - 57, 58, -1, 60, -1, 62, -1, -1, 65, 66, - 9, 68, -1, -1, -1, 14, 40, 41, 42, -1, - -1, -1, 40, 41, 42, -1, -1, -1, 52, -1, - 9, 55, 56, -1, 52, 14, 54, 55, 56, -1, - -1, 40, 41, 42, -1, -1, -1, -1, -1, 73, - -1, -1, -1, 52, 53, -1, 55, 56, -1, -1, - -1, 40, 41, 42, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 52, -1, -1, 55, 56 + -1, -1, -1, -1, -1, -1, -1, 9, 43, 44, + 45, 46, 14, 9, -1, -1, 51, -1, 14, -1, + -1, -1, 57, 58, -1, 60, -1, 62, -1, -1, + 65, 66, -1, 68, -1, -1, -1, -1, 40, 41, + 42, -1, -1, -1, 40, 41, 42, -1, -1, -1, + 52, -1, 54, 55, 56, -1, 52, -1, -1, 55, + 56 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing @@ -1209,36 +1183,36 @@ static const yytype_uint8 yystos[] = 0, 75, 0, 1, 3, 4, 7, 8, 17, 18, 35, 36, 37, 38, 43, 44, 45, 46, 47, 50, 51, 57, 58, 60, 62, 65, 66, 68, 76, 78, - 82, 84, 86, 104, 112, 116, 117, 118, 119, 120, - 121, 129, 130, 66, 69, 126, 127, 128, 83, 122, - 130, 130, 130, 66, 66, 68, 117, 130, 117, 117, - 66, 119, 130, 1, 111, 112, 48, 121, 71, 73, - 79, 88, 104, 132, 136, 79, 85, 50, 9, 14, - 40, 41, 42, 52, 54, 55, 56, 114, 115, 11, - 117, 57, 58, 59, 60, 61, 64, 57, 58, 59, - 60, 61, 64, 12, 13, 43, 44, 51, 113, 110, - 111, 112, 111, 16, 126, 3, 4, 45, 46, 68, - 80, 81, 55, 106, 110, 110, 112, 43, 44, 131, - 1, 54, 67, 134, 138, 134, 1, 6, 77, 104, - 105, 87, 105, 5, 112, 129, 112, 112, 112, 105, - 112, 38, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 13, 112, 134, 70, 49, 66, - 117, 134, 134, 112, 105, 40, 1, 112, 1, 88, - 1, 19, 21, 22, 23, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 39, 72, 89, 90, 92, 99, - 103, 112, 132, 133, 136, 53, 112, 122, 1, 4, - 107, 108, 129, 66, 91, 4, 66, 66, 66, 105, - 66, 88, 88, 88, 109, 112, 88, 105, 88, 93, - 87, 135, 136, 105, 112, 134, 1, 138, 112, 109, - 94, 4, 112, 112, 89, 4, 92, 95, 88, 66, - 100, 110, 133, 105, 105, 1, 4, 134, 88, 123, - 124, 125, 126, 67, 134, 134, 26, 40, 136, 111, - 10, 101, 105, 16, 125, 105, 105, 66, 129, 105, - 134, 102, 89, 132, 89, 112, 134, 112, 136, 116, - 20, 96, 134, 105, 136, 105, 105, 1, 24, 25, - 97, 105, 105, 89, 105, 95, 89, 7, 8, 57, - 58, 84, 98, 53, 137, 133, 95, 134, 7, 7, - 137, 105, 134, 105, 105, 87, 105, 89, 87, 89 + 82, 83, 85, 103, 111, 115, 116, 117, 118, 119, + 120, 128, 129, 66, 69, 125, 126, 127, 3, 4, + 45, 46, 68, 80, 81, 121, 129, 129, 129, 66, + 66, 68, 116, 129, 116, 116, 66, 118, 129, 1, + 110, 111, 48, 120, 71, 73, 79, 87, 103, 131, + 135, 79, 84, 50, 9, 14, 40, 41, 42, 52, + 54, 55, 56, 113, 114, 11, 116, 57, 58, 59, + 60, 61, 64, 57, 58, 59, 60, 61, 64, 12, + 13, 43, 44, 51, 112, 109, 110, 111, 110, 16, + 125, 49, 66, 55, 105, 109, 109, 111, 43, 44, + 130, 1, 54, 67, 133, 137, 133, 1, 6, 77, + 103, 104, 86, 104, 5, 111, 128, 111, 111, 111, + 104, 111, 38, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 13, 111, 133, 70, 1, + 4, 106, 107, 116, 133, 133, 111, 104, 40, 1, + 111, 1, 87, 1, 19, 21, 22, 23, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 39, 72, 88, + 89, 91, 98, 102, 111, 131, 132, 135, 53, 111, + 121, 133, 1, 137, 128, 66, 90, 4, 66, 66, + 66, 104, 66, 87, 87, 87, 108, 111, 87, 104, + 87, 92, 86, 134, 135, 104, 111, 104, 1, 4, + 111, 108, 93, 4, 111, 111, 88, 4, 91, 94, + 87, 66, 99, 109, 132, 104, 133, 87, 122, 123, + 124, 125, 67, 133, 133, 26, 40, 135, 110, 10, + 100, 104, 16, 124, 104, 104, 66, 128, 104, 133, + 101, 88, 131, 88, 111, 133, 111, 135, 115, 20, + 95, 133, 104, 135, 104, 104, 1, 24, 25, 96, + 104, 104, 88, 104, 94, 88, 7, 8, 57, 58, + 83, 97, 53, 136, 132, 94, 133, 7, 7, 136, + 104, 133, 104, 104, 86, 104, 88, 86, 88 }; #define yyerrok (yyerrstatus = 0) @@ -2066,7 +2040,7 @@ yyreduce: case 3: /* Line 1806 of yacc.c */ -#line 221 "awkgram.y" +#line 195 "awkgram.y" { rule = 0; yyerrok; @@ -2076,7 +2050,7 @@ yyreduce: case 5: /* Line 1806 of yacc.c */ -#line 227 "awkgram.y" +#line 201 "awkgram.y" { next_sourcefile(); } @@ -2085,7 +2059,7 @@ yyreduce: case 6: /* Line 1806 of yacc.c */ -#line 231 "awkgram.y" +#line 205 "awkgram.y" { rule = 0; /* @@ -2099,7 +2073,7 @@ yyreduce: case 7: /* Line 1806 of yacc.c */ -#line 243 "awkgram.y" +#line 217 "awkgram.y" { (void) append_rule((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); } @@ -2108,7 +2082,7 @@ yyreduce: case 8: /* Line 1806 of yacc.c */ -#line 247 "awkgram.y" +#line 221 "awkgram.y" { if (rule != Rule) { msg(_("%s blocks must have an action part"), ruletab[rule]); @@ -2124,12 +2098,10 @@ yyreduce: case 9: /* Line 1806 of yacc.c */ -#line 258 "awkgram.y" +#line 232 "awkgram.y" { - can_return = FALSE; - if ((yyvsp[(1) - (2)]) && func_install((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])) < 0) - YYABORT; - func_params = NULL; + in_function = NULL; + (void) mk_function((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); yyerrok; } break; @@ -2137,7 +2109,7 @@ yyreduce: case 10: /* Line 1806 of yacc.c */ -#line 266 "awkgram.y" +#line 238 "awkgram.y" { want_source = FALSE; yyerrok; @@ -2147,7 +2119,7 @@ yyreduce: case 11: /* Line 1806 of yacc.c */ -#line 274 "awkgram.y" +#line 246 "awkgram.y" { if (include_source((yyvsp[(1) - (1)])) < 0) YYABORT; @@ -2160,35 +2132,35 @@ yyreduce: case 12: /* Line 1806 of yacc.c */ -#line 282 "awkgram.y" +#line 254 "awkgram.y" { (yyval) = NULL; } break; case 13: /* Line 1806 of yacc.c */ -#line 284 "awkgram.y" +#line 256 "awkgram.y" { (yyval) = NULL; } break; case 14: /* Line 1806 of yacc.c */ -#line 289 "awkgram.y" +#line 261 "awkgram.y" { (yyval) = NULL; rule = Rule; } break; case 15: /* Line 1806 of yacc.c */ -#line 291 "awkgram.y" +#line 263 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); rule = Rule; } break; case 16: /* Line 1806 of yacc.c */ -#line 293 "awkgram.y" +#line 265 "awkgram.y" { INSTRUCTION *tp; @@ -2196,7 +2168,7 @@ yyreduce: add_lint((yyvsp[(4) - (4)]), LINT_assign_in_cond); tp = instruction(Op_no_op); - list_prepend((yyvsp[(1) - (4)]), bcalloc(Op_line_range, !!do_profiling + 1, 0)); + list_prepend((yyvsp[(1) - (4)]), bcalloc(Op_line_range, !!do_pretty_print + 1, 0)); (yyvsp[(1) - (4)])->nexti->triggered = FALSE; (yyvsp[(1) - (4)])->nexti->target_jmp = (yyvsp[(4) - (4)])->nexti; @@ -2207,7 +2179,7 @@ yyreduce: list_append((yyvsp[(4) - (4)]), instruction(Op_cond_pair)); (yyvsp[(4) - (4)])->lasti->line_range = (yyvsp[(1) - (4)])->nexti; (yyvsp[(4) - (4)])->lasti->target_jmp = tp; - if (do_profiling) { + if (do_pretty_print) { ((yyvsp[(1) - (4)])->nexti + 1)->condpair_left = (yyvsp[(1) - (4)])->lasti; ((yyvsp[(1) - (4)])->nexti + 1)->condpair_right = (yyvsp[(4) - (4)])->lasti; } @@ -2219,7 +2191,7 @@ yyreduce: case 17: /* Line 1806 of yacc.c */ -#line 319 "awkgram.y" +#line 291 "awkgram.y" { static int begin_seen = 0; if (do_lint_old && ++begin_seen == 2) @@ -2235,7 +2207,7 @@ yyreduce: case 18: /* Line 1806 of yacc.c */ -#line 330 "awkgram.y" +#line 302 "awkgram.y" { static int end_seen = 0; if (do_lint_old && ++end_seen == 2) @@ -2251,7 +2223,7 @@ yyreduce: case 19: /* Line 1806 of yacc.c */ -#line 341 "awkgram.y" +#line 313 "awkgram.y" { (yyvsp[(1) - (1)])->in_rule = rule = BEGINFILE; (yyvsp[(1) - (1)])->source_file = source; @@ -2262,7 +2234,7 @@ yyreduce: case 20: /* Line 1806 of yacc.c */ -#line 347 "awkgram.y" +#line 319 "awkgram.y" { (yyvsp[(1) - (1)])->in_rule = rule = ENDFILE; (yyvsp[(1) - (1)])->source_file = source; @@ -2273,7 +2245,7 @@ yyreduce: case 21: /* Line 1806 of yacc.c */ -#line 356 "awkgram.y" +#line 328 "awkgram.y" { if ((yyvsp[(2) - (5)]) == NULL) (yyval) = list_create(instruction(Op_no_op)); @@ -2285,89 +2257,69 @@ yyreduce: case 22: /* Line 1806 of yacc.c */ -#line 366 "awkgram.y" +#line 338 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; case 23: /* Line 1806 of yacc.c */ -#line 368 "awkgram.y" +#line 340 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; case 24: /* Line 1806 of yacc.c */ -#line 370 "awkgram.y" +#line 342 "awkgram.y" { yyerror(_("`%s' is a built-in function, it cannot be redefined"), - tokstart); - (yyvsp[(1) - (1)])->opcode = Op_symbol; /* Op_symbol instead of Op_token so that - * free_bc_internal does not try to free it - */ - (yyvsp[(1) - (1)])->lextok = builtin_func; - (yyval) = (yyvsp[(1) - (1)]); - /* yyerrok; */ + tokstart); + YYABORT; } break; case 25: /* Line 1806 of yacc.c */ -#line 381 "awkgram.y" +#line 348 "awkgram.y" { (yyval) = (yyvsp[(2) - (2)]); } break; case 28: /* Line 1806 of yacc.c */ -#line 391 "awkgram.y" +#line 358 "awkgram.y" { - param_counter = 0; - func_params = NULL; + (yyvsp[(1) - (6)])->source_file = source; + if (install_function((yyvsp[(2) - (6)])->lextok, (yyvsp[(1) - (6)]), (yyvsp[(4) - (6)])) < 0) + YYABORT; + in_function = (yyvsp[(2) - (6)])->lextok; + (yyvsp[(2) - (6)])->lextok = NULL; + bcfree((yyvsp[(2) - (6)])); + /* $4 already free'd in install_function */ + (yyval) = (yyvsp[(1) - (6)]); } break; case 29: /* Line 1806 of yacc.c */ -#line 396 "awkgram.y" - { - NODE *t; - - (yyvsp[(1) - (7)])->source_file = source; - t = make_param((yyvsp[(3) - (7)])->lextok); - (yyvsp[(3) - (7)])->lextok = NULL; - bcfree((yyvsp[(3) - (7)])); - t->flags |= FUNC; - t->rnode = func_params; - func_params = t; - (yyval) = (yyvsp[(1) - (7)]); - can_return = TRUE; - /* check for duplicate parameter names */ - if (dup_parms((yyvsp[(1) - (7)]), t)) - errcount++; - } - break; - - case 30: - -/* Line 1806 of yacc.c */ -#line 420 "awkgram.y" +#line 376 "awkgram.y" { ++want_regexp; } break; - case 31: + case 30: /* Line 1806 of yacc.c */ -#line 422 "awkgram.y" +#line 378 "awkgram.y" { NODE *n, *exp; char *re; size_t len; re = (yyvsp[(3) - (3)])->lextok; + (yyvsp[(3) - (3)])->lextok = NULL; len = strlen(re); if (do_lint) { if (len == 0) @@ -2391,24 +2343,24 @@ yyreduce: } break; - case 32: + case 31: /* Line 1806 of yacc.c */ -#line 453 "awkgram.y" +#line 410 "awkgram.y" { bcfree((yyvsp[(1) - (1)])); } break; - case 34: + case 33: /* Line 1806 of yacc.c */ -#line 459 "awkgram.y" +#line 416 "awkgram.y" { (yyval) = NULL; } break; - case 35: + case 34: /* Line 1806 of yacc.c */ -#line 461 "awkgram.y" +#line 418 "awkgram.y" { if ((yyvsp[(2) - (2)]) == NULL) (yyval) = (yyvsp[(1) - (2)]); @@ -2423,43 +2375,43 @@ yyreduce: } break; - case 36: + case 35: /* Line 1806 of yacc.c */ -#line 474 "awkgram.y" +#line 431 "awkgram.y" { (yyval) = NULL; } break; - case 39: + case 38: /* Line 1806 of yacc.c */ -#line 484 "awkgram.y" +#line 441 "awkgram.y" { (yyval) = NULL; } break; - case 40: + case 39: /* Line 1806 of yacc.c */ -#line 486 "awkgram.y" +#line 443 "awkgram.y" { (yyval) = (yyvsp[(2) - (3)]); } break; - case 41: + case 40: /* Line 1806 of yacc.c */ -#line 488 "awkgram.y" +#line 445 "awkgram.y" { - if (do_profiling) + if (do_pretty_print) (yyval) = list_prepend((yyvsp[(1) - (1)]), instruction(Op_exec_count)); else (yyval) = (yyvsp[(1) - (1)]); } break; - case 42: + case 41: /* Line 1806 of yacc.c */ -#line 495 "awkgram.y" +#line 452 "awkgram.y" { INSTRUCTION *dflt, *curr = NULL, *cexp, *cstmt; INSTRUCTION *ip, *nextc, *tbreak; @@ -2518,7 +2470,7 @@ yyreduce: else dflt->target_jmp = casestmt->nexti; - if (do_profiling) { + if (do_pretty_print) { curr->stmt_start = casestmt->nexti; curr->stmt_end = casestmt->lasti; (void) list_prepend(cexp, curr); @@ -2533,7 +2485,7 @@ yyreduce: efree(case_values); ip = (yyvsp[(3) - (9)]); - if (do_profiling) { + if (do_pretty_print) { (void) list_prepend(ip, (yyvsp[(1) - (9)])); (void) list_prepend(ip, instruction(Op_exec_count)); (yyvsp[(1) - (9)])->target_break = tbreak; @@ -2551,10 +2503,10 @@ yyreduce: } break; - case 43: + case 42: /* Line 1806 of yacc.c */ -#line 585 "awkgram.y" +#line 542 "awkgram.y" { /* * ----------------- @@ -2577,7 +2529,7 @@ yyreduce: ip = list_append((yyvsp[(3) - (6)]), instruction(Op_jmp_false)); ip->lasti->target_jmp = tbreak; - if (do_profiling) { + if (do_pretty_print) { (void) list_append(ip, instruction(Op_exec_count)); (yyvsp[(1) - (6)])->target_break = tbreak; (yyvsp[(1) - (6)])->target_continue = tcont; @@ -2598,10 +2550,10 @@ yyreduce: } break; - case 44: + case 43: /* Line 1806 of yacc.c */ -#line 627 "awkgram.y" +#line 584 "awkgram.y" { /* * ----------------- @@ -2624,7 +2576,7 @@ yyreduce: ip = list_merge((yyvsp[(3) - (8)]), (yyvsp[(6) - (8)])); else ip = list_prepend((yyvsp[(6) - (8)]), instruction(Op_no_op)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(ip, instruction(Op_exec_count)); (void) list_append(ip, instruction(Op_jmp_true)); ip->lasti->target_jmp = ip->nexti; @@ -2634,7 +2586,7 @@ yyreduce: continue_allowed--; fix_break_continue(ip, tbreak, tcont); - if (do_profiling) { + if (do_pretty_print) { (yyvsp[(1) - (8)])->target_break = tbreak; (yyvsp[(1) - (8)])->target_continue = tcont; ((yyvsp[(1) - (8)]) + 1)->doloop_cond = tcont; @@ -2645,10 +2597,10 @@ yyreduce: } break; - case 45: + case 44: /* Line 1806 of yacc.c */ -#line 669 "awkgram.y" +#line 626 "awkgram.y" { INSTRUCTION *ip; char *var_name = (yyvsp[(3) - (8)])->lextok; @@ -2715,14 +2667,14 @@ regular_loop: tbreak = instruction(Op_arrayfor_final); (yyvsp[(4) - (8)])->opcode = Op_arrayfor_incr; - (yyvsp[(4) - (8)])->array_var = variable(var_name, Node_var); + (yyvsp[(4) - (8)])->array_var = variable((yyvsp[(3) - (8)])->source_line, var_name, Node_var); (yyvsp[(4) - (8)])->target_jmp = tbreak; tcont = (yyvsp[(4) - (8)]); (yyvsp[(3) - (8)])->opcode = Op_arrayfor_init; (yyvsp[(3) - (8)])->target_jmp = tbreak; (void) list_append(ip, (yyvsp[(3) - (8)])); - if (do_profiling) { + if (do_pretty_print) { (yyvsp[(1) - (8)])->opcode = Op_K_arrayfor; (yyvsp[(1) - (8)])->target_continue = tcont; (yyvsp[(1) - (8)])->target_break = tbreak; @@ -2743,7 +2695,7 @@ regular_loop: ip->lasti->assign_var = (yyvsp[(4) - (8)])->array_var->var_assign; } - if (do_profiling) { + if (do_pretty_print) { (void) list_append(ip, instruction(Op_exec_count)); ((yyvsp[(1) - (8)]) + 1)->forloop_cond = (yyvsp[(4) - (8)]); ((yyvsp[(1) - (8)]) + 1)->forloop_body = ip->lasti; @@ -2763,10 +2715,10 @@ regular_loop: } break; - case 46: + case 45: /* Line 1806 of yacc.c */ -#line 782 "awkgram.y" +#line 739 "awkgram.y" { (yyval) = mk_for_loop((yyvsp[(1) - (12)]), (yyvsp[(3) - (12)]), (yyvsp[(6) - (12)]), (yyvsp[(9) - (12)]), (yyvsp[(12) - (12)])); @@ -2775,10 +2727,10 @@ regular_loop: } break; - case 47: + case 46: /* Line 1806 of yacc.c */ -#line 789 "awkgram.y" +#line 746 "awkgram.y" { (yyval) = mk_for_loop((yyvsp[(1) - (11)]), (yyvsp[(3) - (11)]), (INSTRUCTION *) NULL, (yyvsp[(8) - (11)]), (yyvsp[(11) - (11)])); @@ -2787,22 +2739,22 @@ regular_loop: } break; - case 48: + case 47: /* Line 1806 of yacc.c */ -#line 796 "awkgram.y" +#line 753 "awkgram.y" { - if (do_profiling) + if (do_pretty_print) (yyval) = list_prepend((yyvsp[(1) - (1)]), instruction(Op_exec_count)); else (yyval) = (yyvsp[(1) - (1)]); } break; - case 49: + case 48: /* Line 1806 of yacc.c */ -#line 806 "awkgram.y" +#line 763 "awkgram.y" { if (! break_allowed) error_ln((yyvsp[(1) - (2)])->source_line, @@ -2813,10 +2765,10 @@ regular_loop: } break; - case 50: + case 49: /* Line 1806 of yacc.c */ -#line 815 "awkgram.y" +#line 772 "awkgram.y" { if (! continue_allowed) error_ln((yyvsp[(1) - (2)])->source_line, @@ -2827,10 +2779,10 @@ regular_loop: } break; - case 51: + case 50: /* Line 1806 of yacc.c */ -#line 824 "awkgram.y" +#line 781 "awkgram.y" { /* if inside function (rule = 0), resolve context at run-time */ if (rule && rule != Rule) @@ -2841,10 +2793,10 @@ regular_loop: } break; - case 52: + case 51: /* Line 1806 of yacc.c */ -#line 833 "awkgram.y" +#line 790 "awkgram.y" { if (do_traditional) error_ln((yyvsp[(1) - (2)])->source_line, @@ -2861,10 +2813,10 @@ regular_loop: } break; - case 53: + case 52: /* Line 1806 of yacc.c */ -#line 848 "awkgram.y" +#line 805 "awkgram.y" { /* Initialize the two possible jump targets, the actual target * is resolved at run-time. @@ -2875,47 +2827,59 @@ regular_loop: if ((yyvsp[(2) - (3)]) == NULL) { (yyval) = list_create((yyvsp[(1) - (3)])); (void) list_prepend((yyval), instruction(Op_push_i)); - (yyval)->nexti->memory = Nnull_string; + (yyval)->nexti->memory = dupnode(Nnull_string); } else (yyval) = list_append((yyvsp[(2) - (3)]), (yyvsp[(1) - (3)])); } break; - case 54: + case 53: /* Line 1806 of yacc.c */ -#line 863 "awkgram.y" +#line 820 "awkgram.y" { - if (! can_return) + if (! in_function) yyerror(_("`return' used outside function context")); } break; - case 55: + case 54: /* Line 1806 of yacc.c */ -#line 866 "awkgram.y" +#line 823 "awkgram.y" { if ((yyvsp[(3) - (4)]) == NULL) { (yyval) = list_create((yyvsp[(1) - (4)])); (void) list_prepend((yyval), instruction(Op_push_i)); - (yyval)->nexti->memory = Nnull_string; - } else + (yyval)->nexti->memory = dupnode(Nnull_string); + } else { + if (do_optimize > 1 + && (yyvsp[(3) - (4)])->lasti->opcode == Op_func_call + && strcmp((yyvsp[(3) - (4)])->lasti->func_name, in_function) == 0 + ) { + /* Do tail recursion optimization. Tail + * call without a return value is recognized + * in mk_function(). + */ + ((yyvsp[(3) - (4)])->lasti + 1)->tail_call = TRUE; + } + (yyval) = list_append((yyvsp[(3) - (4)]), (yyvsp[(1) - (4)])); + } } break; - case 57: + case 56: /* Line 1806 of yacc.c */ -#line 886 "awkgram.y" +#line 855 "awkgram.y" { in_print = TRUE; in_parens = 0; } break; - case 58: + case 57: /* Line 1806 of yacc.c */ -#line 887 "awkgram.y" +#line 856 "awkgram.y" { /* * Optimization: plain `print' has no expression list, so $3 is null. @@ -2924,13 +2888,13 @@ regular_loop: */ if ((yyvsp[(1) - (4)])->opcode == Op_K_print && - ((yyvsp[(3) - (4)]) == NULL - || ((yyvsp[(3) - (4)])->lasti->opcode == Op_field_spec - && (yyvsp[(3) - (4)])->nexti->nexti->nexti == (yyvsp[(3) - (4)])->lasti - && (yyvsp[(3) - (4)])->nexti->nexti->opcode == Op_push_i - && (yyvsp[(3) - (4)])->nexti->nexti->memory->type == Node_val - && (yyvsp[(3) - (4)])->nexti->nexti->memory->numbr == 0.0) - ) + ((yyvsp[(3) - (4)]) == NULL + || ((yyvsp[(3) - (4)])->lasti->opcode == Op_field_spec + && (yyvsp[(3) - (4)])->nexti->nexti->nexti == (yyvsp[(3) - (4)])->lasti + && (yyvsp[(3) - (4)])->nexti->nexti->opcode == Op_push_i + && (yyvsp[(3) - (4)])->nexti->nexti->memory->type == Node_val + && (yyvsp[(3) - (4)])->nexti->nexti->memory->numbr == 0.0) + ) ) { static short warned = FALSE; /* ----------------- @@ -2944,8 +2908,6 @@ regular_loop: if ((yyvsp[(3) - (4)]) != NULL) { bcfree((yyvsp[(3) - (4)])->lasti); /* Op_field_spec */ - (yyvsp[(3) - (4)])->nexti->nexti->memory->flags &= ~PERM; - (yyvsp[(3) - (4)])->nexti->nexti->memory->flags |= MALLOC; unref((yyvsp[(3) - (4)])->nexti->nexti->memory); /* Node_val */ bcfree((yyvsp[(3) - (4)])->nexti->nexti); /* Op_push_i */ bcfree((yyvsp[(3) - (4)])->nexti); /* Op_list */ @@ -3012,22 +2974,22 @@ regular_loop: } break; - case 59: + case 58: /* Line 1806 of yacc.c */ -#line 982 "awkgram.y" +#line 949 "awkgram.y" { sub_counter = 0; } break; - case 60: + case 59: /* Line 1806 of yacc.c */ -#line 983 "awkgram.y" +#line 950 "awkgram.y" { char *arr = (yyvsp[(2) - (4)])->lextok; (yyvsp[(2) - (4)])->opcode = Op_push_array; - (yyvsp[(2) - (4)])->memory = variable(arr, Node_var_new); + (yyvsp[(2) - (4)])->memory = variable((yyvsp[(2) - (4)])->source_line, arr, Node_var_new); if ((yyvsp[(4) - (4)]) == NULL) { static short warned = FALSE; @@ -3049,10 +3011,10 @@ regular_loop: } break; - case 61: + case 60: /* Line 1806 of yacc.c */ -#line 1012 "awkgram.y" +#line 979 "awkgram.y" { static short warned = FALSE; char *arr = (yyvsp[(3) - (4)])->lextok; @@ -3066,45 +3028,45 @@ regular_loop: error_ln((yyvsp[(1) - (4)])->source_line, _("`delete array' is a gawk extension")); } - (yyvsp[(3) - (4)])->memory = variable(arr, Node_var_new); + (yyvsp[(3) - (4)])->memory = variable((yyvsp[(3) - (4)])->source_line, arr, Node_var_new); (yyvsp[(3) - (4)])->opcode = Op_push_array; (yyvsp[(1) - (4)])->expr_count = 0; (yyval) = list_append(list_create((yyvsp[(3) - (4)])), (yyvsp[(1) - (4)])); } break; - case 62: + case 61: /* Line 1806 of yacc.c */ -#line 1031 "awkgram.y" +#line 998 "awkgram.y" { (yyval) = optimize_assignment((yyvsp[(1) - (1)])); } break; - case 63: + case 62: /* Line 1806 of yacc.c */ -#line 1036 "awkgram.y" +#line 1003 "awkgram.y" { (yyval) = NULL; } break; - case 64: + case 63: /* Line 1806 of yacc.c */ -#line 1038 "awkgram.y" +#line 1005 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 65: + case 64: /* Line 1806 of yacc.c */ -#line 1043 "awkgram.y" +#line 1010 "awkgram.y" { (yyval) = NULL; } break; - case 66: + case 65: /* Line 1806 of yacc.c */ -#line 1045 "awkgram.y" +#line 1012 "awkgram.y" { if ((yyvsp[(1) - (2)]) == NULL) (yyval) = list_create((yyvsp[(2) - (2)])); @@ -3113,22 +3075,22 @@ regular_loop: } break; - case 67: + case 66: /* Line 1806 of yacc.c */ -#line 1052 "awkgram.y" +#line 1019 "awkgram.y" { (yyval) = NULL; } break; - case 68: + case 67: /* Line 1806 of yacc.c */ -#line 1057 "awkgram.y" +#line 1024 "awkgram.y" { INSTRUCTION *casestmt = (yyvsp[(5) - (5)]); if ((yyvsp[(5) - (5)]) == NULL) casestmt = list_create(instruction(Op_no_op)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(casestmt, instruction(Op_exec_count)); (yyvsp[(1) - (5)])->case_exp = (yyvsp[(2) - (5)]); (yyvsp[(1) - (5)])->case_stmt = casestmt; @@ -3137,15 +3099,15 @@ regular_loop: } break; - case 69: + case 68: /* Line 1806 of yacc.c */ -#line 1069 "awkgram.y" +#line 1036 "awkgram.y" { INSTRUCTION *casestmt = (yyvsp[(4) - (4)]); if ((yyvsp[(4) - (4)]) == NULL) casestmt = list_create(instruction(Op_no_op)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(casestmt, instruction(Op_exec_count)); bcfree((yyvsp[(2) - (4)])); (yyvsp[(1) - (4)])->case_stmt = casestmt; @@ -3153,17 +3115,17 @@ regular_loop: } break; - case 70: + case 69: /* Line 1806 of yacc.c */ -#line 1083 "awkgram.y" +#line 1050 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 71: + case 70: /* Line 1806 of yacc.c */ -#line 1085 "awkgram.y" +#line 1052 "awkgram.y" { (yyvsp[(2) - (2)])->memory->numbr = -(force_number((yyvsp[(2) - (2)])->memory)); bcfree((yyvsp[(1) - (2)])); @@ -3171,60 +3133,60 @@ regular_loop: } break; - case 72: + case 71: /* Line 1806 of yacc.c */ -#line 1091 "awkgram.y" +#line 1058 "awkgram.y" { bcfree((yyvsp[(1) - (2)])); (yyval) = (yyvsp[(2) - (2)]); } break; - case 73: + case 72: /* Line 1806 of yacc.c */ -#line 1096 "awkgram.y" +#line 1063 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 74: + case 73: /* Line 1806 of yacc.c */ -#line 1098 "awkgram.y" +#line 1065 "awkgram.y" { (yyvsp[(1) - (1)])->opcode = Op_push_re; (yyval) = (yyvsp[(1) - (1)]); } break; - case 75: + case 74: /* Line 1806 of yacc.c */ -#line 1106 "awkgram.y" +#line 1073 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 76: + case 75: /* Line 1806 of yacc.c */ -#line 1108 "awkgram.y" +#line 1075 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 78: + case 77: /* Line 1806 of yacc.c */ -#line 1118 "awkgram.y" +#line 1085 "awkgram.y" { (yyval) = (yyvsp[(2) - (3)]); } break; - case 79: + case 78: /* Line 1806 of yacc.c */ -#line 1125 "awkgram.y" +#line 1092 "awkgram.y" { in_print = FALSE; in_parens = 0; @@ -3232,17 +3194,17 @@ regular_loop: } break; - case 80: + case 79: /* Line 1806 of yacc.c */ -#line 1130 "awkgram.y" +#line 1097 "awkgram.y" { in_print = FALSE; in_parens = 0; } break; - case 81: + case 80: /* Line 1806 of yacc.c */ -#line 1131 "awkgram.y" +#line 1098 "awkgram.y" { if ((yyvsp[(1) - (3)])->redir_type == redirect_twoway && (yyvsp[(3) - (3)])->lasti->opcode == Op_K_getline_redir @@ -3252,162 +3214,174 @@ regular_loop: } break; - case 82: + case 81: /* Line 1806 of yacc.c */ -#line 1142 "awkgram.y" +#line 1109 "awkgram.y" { (yyval) = mk_condition((yyvsp[(3) - (6)]), (yyvsp[(1) - (6)]), (yyvsp[(6) - (6)]), NULL, NULL); } break; - case 83: + case 82: /* Line 1806 of yacc.c */ -#line 1147 "awkgram.y" +#line 1114 "awkgram.y" { (yyval) = mk_condition((yyvsp[(3) - (9)]), (yyvsp[(1) - (9)]), (yyvsp[(6) - (9)]), (yyvsp[(7) - (9)]), (yyvsp[(9) - (9)])); } break; - case 88: + case 87: /* Line 1806 of yacc.c */ -#line 1164 "awkgram.y" +#line 1131 "awkgram.y" { (yyval) = NULL; } break; - case 89: + case 88: /* Line 1806 of yacc.c */ -#line 1166 "awkgram.y" +#line 1133 "awkgram.y" { bcfree((yyvsp[(1) - (2)])); (yyval) = (yyvsp[(2) - (2)]); } break; - case 92: + case 89: + +/* Line 1806 of yacc.c */ +#line 1141 "awkgram.y" + { (yyval) = NULL; } + break; + + case 90: /* Line 1806 of yacc.c */ -#line 1179 "awkgram.y" +#line 1143 "awkgram.y" + { (yyval) = (yyvsp[(1) - (1)]) ; } + break; + + case 91: + +/* Line 1806 of yacc.c */ +#line 1148 "awkgram.y" { - append_param((yyvsp[(1) - (1)])->lextok); - (yyvsp[(1) - (1)])->lextok = NULL; - bcfree((yyvsp[(1) - (1)])); + (yyvsp[(1) - (1)])->param_count = 0; + (yyval) = list_create((yyvsp[(1) - (1)])); } break; - case 93: + case 92: /* Line 1806 of yacc.c */ -#line 1185 "awkgram.y" +#line 1153 "awkgram.y" { - append_param((yyvsp[(3) - (3)])->lextok); - (yyvsp[(3) - (3)])->lextok = NULL; - bcfree((yyvsp[(3) - (3)])); + (yyvsp[(3) - (3)])->param_count = (yyvsp[(1) - (3)])->lasti->param_count + 1; + (yyval) = list_append((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); yyerrok; } break; - case 94: + case 93: /* Line 1806 of yacc.c */ -#line 1192 "awkgram.y" - { /* func_params = NULL; */ } +#line 1159 "awkgram.y" + { (yyval) = NULL; } break; - case 95: + case 94: /* Line 1806 of yacc.c */ -#line 1194 "awkgram.y" - { /* func_params = NULL; */ } +#line 1161 "awkgram.y" + { (yyval) = (yyvsp[(1) - (2)]); } break; - case 96: + case 95: /* Line 1806 of yacc.c */ -#line 1196 "awkgram.y" - { /* func_params = NULL; */ } +#line 1163 "awkgram.y" + { (yyval) = (yyvsp[(1) - (3)]); } break; - case 97: + case 96: /* Line 1806 of yacc.c */ -#line 1202 "awkgram.y" +#line 1169 "awkgram.y" { (yyval) = NULL; } break; - case 98: + case 97: /* Line 1806 of yacc.c */ -#line 1204 "awkgram.y" +#line 1171 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 99: + case 98: /* Line 1806 of yacc.c */ -#line 1209 "awkgram.y" +#line 1176 "awkgram.y" { (yyval) = NULL; } break; - case 100: + case 99: /* Line 1806 of yacc.c */ -#line 1211 "awkgram.y" +#line 1178 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 101: + case 100: /* Line 1806 of yacc.c */ -#line 1216 "awkgram.y" +#line 1183 "awkgram.y" { (yyval) = mk_expression_list(NULL, (yyvsp[(1) - (1)])); } break; - case 102: + case 101: /* Line 1806 of yacc.c */ -#line 1218 "awkgram.y" +#line 1185 "awkgram.y" { (yyval) = mk_expression_list((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); yyerrok; } break; - case 103: + case 102: /* Line 1806 of yacc.c */ -#line 1223 "awkgram.y" +#line 1190 "awkgram.y" { (yyval) = NULL; } break; - case 104: + case 103: /* Line 1806 of yacc.c */ -#line 1225 "awkgram.y" +#line 1192 "awkgram.y" { (yyval) = NULL; } break; - case 105: + case 104: /* Line 1806 of yacc.c */ -#line 1227 "awkgram.y" +#line 1194 "awkgram.y" { (yyval) = NULL; } break; - case 106: + case 105: /* Line 1806 of yacc.c */ -#line 1229 "awkgram.y" +#line 1196 "awkgram.y" { (yyval) = NULL; } break; - case 107: + case 106: /* Line 1806 of yacc.c */ -#line 1235 "awkgram.y" +#line 1202 "awkgram.y" { if (do_lint && (yyvsp[(3) - (3)])->lasti->opcode == Op_match_rec) lintwarn_ln((yyvsp[(2) - (3)])->source_line, @@ -3416,24 +3390,24 @@ regular_loop: } break; - case 108: + case 107: /* Line 1806 of yacc.c */ -#line 1242 "awkgram.y" +#line 1209 "awkgram.y" { (yyval) = mk_boolean((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; - case 109: + case 108: /* Line 1806 of yacc.c */ -#line 1244 "awkgram.y" +#line 1211 "awkgram.y" { (yyval) = mk_boolean((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; - case 110: + case 109: /* Line 1806 of yacc.c */ -#line 1246 "awkgram.y" +#line 1213 "awkgram.y" { if ((yyvsp[(1) - (3)])->lasti->opcode == Op_match_rec) warning_ln((yyvsp[(2) - (3)])->source_line, @@ -3451,13 +3425,13 @@ regular_loop: } break; - case 111: + case 110: /* Line 1806 of yacc.c */ -#line 1262 "awkgram.y" +#line 1229 "awkgram.y" { if (do_lint_old) - warning_ln((yyvsp[(2) - (3)])->source_line, + warning_ln((yyvsp[(2) - (3)])->source_line, _("old awk does not support the keyword `in' except after `for'")); (yyvsp[(3) - (3)])->nexti->opcode = Op_push_array; (yyvsp[(2) - (3)])->opcode = Op_in_array; @@ -3466,10 +3440,10 @@ regular_loop: } break; - case 112: + case 111: /* Line 1806 of yacc.c */ -#line 1272 "awkgram.y" +#line 1239 "awkgram.y" { if (do_lint && (yyvsp[(3) - (3)])->lasti->opcode == Op_match_rec) lintwarn_ln((yyvsp[(2) - (3)])->source_line, @@ -3478,90 +3452,90 @@ regular_loop: } break; - case 113: + case 112: /* Line 1806 of yacc.c */ -#line 1279 "awkgram.y" +#line 1246 "awkgram.y" { (yyval) = mk_condition((yyvsp[(1) - (5)]), (yyvsp[(2) - (5)]), (yyvsp[(3) - (5)]), (yyvsp[(4) - (5)]), (yyvsp[(5) - (5)])); } break; - case 114: + case 113: /* Line 1806 of yacc.c */ -#line 1281 "awkgram.y" +#line 1248 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 115: + case 114: /* Line 1806 of yacc.c */ -#line 1286 "awkgram.y" +#line 1253 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 116: + case 115: /* Line 1806 of yacc.c */ -#line 1288 "awkgram.y" +#line 1255 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 117: + case 116: /* Line 1806 of yacc.c */ -#line 1290 "awkgram.y" +#line 1257 "awkgram.y" { (yyvsp[(2) - (2)])->opcode = Op_assign_quotient; (yyval) = (yyvsp[(2) - (2)]); } break; + case 117: + +/* Line 1806 of yacc.c */ +#line 1265 "awkgram.y" + { (yyval) = (yyvsp[(1) - (1)]); } + break; + case 118: /* Line 1806 of yacc.c */ -#line 1298 "awkgram.y" +#line 1267 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; case 119: /* Line 1806 of yacc.c */ -#line 1300 "awkgram.y" +#line 1272 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; case 120: /* Line 1806 of yacc.c */ -#line 1305 "awkgram.y" +#line 1274 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; case 121: /* Line 1806 of yacc.c */ -#line 1307 "awkgram.y" +#line 1279 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; case 122: /* Line 1806 of yacc.c */ -#line 1312 "awkgram.y" +#line 1281 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; case 123: /* Line 1806 of yacc.c */ -#line 1314 "awkgram.y" - { (yyval) = (yyvsp[(1) - (1)]); } - break; - - case 124: - -/* Line 1806 of yacc.c */ -#line 1316 "awkgram.y" +#line 1283 "awkgram.y" { int count = 2; int is_simple_var = FALSE; @@ -3573,32 +3547,29 @@ regular_loop: (yyvsp[(1) - (2)])->lasti->opcode = Op_no_op; } else { is_simple_var = ((yyvsp[(1) - (2)])->nexti->opcode == Op_push - && (yyvsp[(1) - (2)])->lasti == (yyvsp[(1) - (2)])->nexti); /* first exp. is a simple - * variable?; kludge for use - * in Op_assign_concat. - */ + && (yyvsp[(1) - (2)])->lasti == (yyvsp[(1) - (2)])->nexti); /* first exp. is a simple + * variable?; kludge for use + * in Op_assign_concat. + */ } if (do_optimize > 1 - && (yyvsp[(1) - (2)])->nexti == (yyvsp[(1) - (2)])->lasti && (yyvsp[(1) - (2)])->nexti->opcode == Op_push_i - && (yyvsp[(2) - (2)])->nexti == (yyvsp[(2) - (2)])->lasti && (yyvsp[(2) - (2)])->nexti->opcode == Op_push_i + && (yyvsp[(1) - (2)])->nexti == (yyvsp[(1) - (2)])->lasti && (yyvsp[(1) - (2)])->nexti->opcode == Op_push_i + && (yyvsp[(2) - (2)])->nexti == (yyvsp[(2) - (2)])->lasti && (yyvsp[(2) - (2)])->nexti->opcode == Op_push_i ) { NODE *n1 = (yyvsp[(1) - (2)])->nexti->memory; NODE *n2 = (yyvsp[(2) - (2)])->nexti->memory; size_t nlen; - (void) force_string(n1); - (void) force_string(n2); + n1 = force_string(n1); + n2 = force_string(n2); nlen = n1->stlen + n2->stlen; erealloc(n1->stptr, char *, nlen + 2, "constant fold"); memcpy(n1->stptr + n1->stlen, n2->stptr, n2->stlen); n1->stlen = nlen; n1->stptr[nlen] = '\0'; - n1->flags &= ~(NUMCUR|NUMBER); + n1->flags &= ~(NUMCUR|NUMBER|NUMINT); n1->flags |= (STRING|STRCUR); - - n2->flags &= ~PERM; - n2->flags |= MALLOC; unref(n2); bcfree((yyvsp[(2) - (2)])->nexti); bcfree((yyvsp[(2) - (2)])); @@ -3613,52 +3584,52 @@ regular_loop: } break; + case 125: + +/* Line 1806 of yacc.c */ +#line 1335 "awkgram.y" + { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } + break; + case 126: /* Line 1806 of yacc.c */ -#line 1371 "awkgram.y" +#line 1337 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 127: /* Line 1806 of yacc.c */ -#line 1373 "awkgram.y" +#line 1339 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 128: /* Line 1806 of yacc.c */ -#line 1375 "awkgram.y" +#line 1341 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 129: /* Line 1806 of yacc.c */ -#line 1377 "awkgram.y" +#line 1343 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 130: /* Line 1806 of yacc.c */ -#line 1379 "awkgram.y" +#line 1345 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 131: /* Line 1806 of yacc.c */ -#line 1381 "awkgram.y" - { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } - break; - - case 132: - -/* Line 1806 of yacc.c */ -#line 1383 "awkgram.y" +#line 1347 "awkgram.y" { /* * In BEGINFILE/ENDFILE, allow `getline var < file' @@ -3683,30 +3654,30 @@ regular_loop: } break; - case 133: + case 132: /* Line 1806 of yacc.c */ -#line 1406 "awkgram.y" +#line 1370 "awkgram.y" { (yyvsp[(2) - (2)])->opcode = Op_postincrement; (yyval) = mk_assignment((yyvsp[(1) - (2)]), NULL, (yyvsp[(2) - (2)])); } break; - case 134: + case 133: /* Line 1806 of yacc.c */ -#line 1411 "awkgram.y" +#line 1375 "awkgram.y" { (yyvsp[(2) - (2)])->opcode = Op_postdecrement; (yyval) = mk_assignment((yyvsp[(1) - (2)]), NULL, (yyvsp[(2) - (2)])); } break; - case 135: + case 134: /* Line 1806 of yacc.c */ -#line 1416 "awkgram.y" +#line 1380 "awkgram.y" { if (do_lint_old) { warning_ln((yyvsp[(4) - (5)])->source_line, @@ -3728,81 +3699,81 @@ regular_loop: } break; - case 136: + case 135: /* Line 1806 of yacc.c */ -#line 1441 "awkgram.y" +#line 1405 "awkgram.y" { (yyval) = mk_getline((yyvsp[(3) - (4)]), (yyvsp[(4) - (4)]), (yyvsp[(1) - (4)]), (yyvsp[(2) - (4)])->redir_type); bcfree((yyvsp[(2) - (4)])); } break; + case 136: + +/* Line 1806 of yacc.c */ +#line 1411 "awkgram.y" + { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } + break; + case 137: /* Line 1806 of yacc.c */ -#line 1447 "awkgram.y" +#line 1413 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 138: /* Line 1806 of yacc.c */ -#line 1449 "awkgram.y" +#line 1415 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 139: /* Line 1806 of yacc.c */ -#line 1451 "awkgram.y" +#line 1417 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 140: /* Line 1806 of yacc.c */ -#line 1453 "awkgram.y" +#line 1419 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 141: /* Line 1806 of yacc.c */ -#line 1455 "awkgram.y" +#line 1421 "awkgram.y" { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } break; case 142: /* Line 1806 of yacc.c */ -#line 1457 "awkgram.y" - { (yyval) = mk_binary((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), (yyvsp[(2) - (3)])); } - break; - - case 143: - -/* Line 1806 of yacc.c */ -#line 1462 "awkgram.y" +#line 1426 "awkgram.y" { (yyval) = list_create((yyvsp[(1) - (1)])); } break; - case 144: + case 143: /* Line 1806 of yacc.c */ -#line 1466 "awkgram.y" +#line 1430 "awkgram.y" { if ((yyvsp[(2) - (2)])->opcode == Op_match_rec) { (yyvsp[(2) - (2)])->opcode = Op_nomatch; (yyvsp[(1) - (2)])->opcode = Op_push_i; - (yyvsp[(1) - (2)])->memory = mk_number(0.0, (PERM|NUMCUR|NUMBER)); + (yyvsp[(1) - (2)])->memory = make_number(0.0); (yyval) = list_append(list_append(list_create((yyvsp[(1) - (2)])), - instruction(Op_field_spec)), (yyvsp[(2) - (2)])); + instruction(Op_field_spec)), (yyvsp[(2) - (2)])); } else { if (do_optimize > 1 && (yyvsp[(2) - (2)])->nexti == (yyvsp[(2) - (2)])->lasti - && (yyvsp[(2) - (2)])->nexti->opcode == Op_push_i + && (yyvsp[(2) - (2)])->nexti->opcode == Op_push_i ) { NODE *n = (yyvsp[(2) - (2)])->nexti->memory; if ((n->flags & (STRCUR|STRING)) != 0) { @@ -3825,17 +3796,17 @@ regular_loop: } break; - case 145: + case 144: /* Line 1806 of yacc.c */ -#line 1497 "awkgram.y" +#line 1461 "awkgram.y" { (yyval) = (yyvsp[(2) - (3)]); } break; - case 146: + case 145: /* Line 1806 of yacc.c */ -#line 1499 "awkgram.y" +#line 1463 "awkgram.y" { (yyval) = snode((yyvsp[(3) - (4)]), (yyvsp[(1) - (4)])); if ((yyval) == NULL) @@ -3843,10 +3814,10 @@ regular_loop: } break; - case 147: + case 146: /* Line 1806 of yacc.c */ -#line 1505 "awkgram.y" +#line 1469 "awkgram.y" { (yyval) = snode((yyvsp[(3) - (4)]), (yyvsp[(1) - (4)])); if ((yyval) == NULL) @@ -3854,10 +3825,10 @@ regular_loop: } break; - case 148: + case 147: /* Line 1806 of yacc.c */ -#line 1511 "awkgram.y" +#line 1475 "awkgram.y" { static short warned1 = FALSE; @@ -3872,51 +3843,52 @@ regular_loop: } break; - case 151: + case 150: /* Line 1806 of yacc.c */ -#line 1526 "awkgram.y" +#line 1490 "awkgram.y" { (yyvsp[(1) - (2)])->opcode = Op_preincrement; (yyval) = mk_assignment((yyvsp[(2) - (2)]), NULL, (yyvsp[(1) - (2)])); } break; - case 152: + case 151: /* Line 1806 of yacc.c */ -#line 1531 "awkgram.y" +#line 1495 "awkgram.y" { (yyvsp[(1) - (2)])->opcode = Op_predecrement; (yyval) = mk_assignment((yyvsp[(2) - (2)]), NULL, (yyvsp[(1) - (2)])); } break; - case 153: + case 152: /* Line 1806 of yacc.c */ -#line 1536 "awkgram.y" +#line 1500 "awkgram.y" { (yyval) = list_create((yyvsp[(1) - (1)])); } break; - case 154: + case 153: /* Line 1806 of yacc.c */ -#line 1540 "awkgram.y" +#line 1504 "awkgram.y" { (yyval) = list_create((yyvsp[(1) - (1)])); } break; - case 155: + case 154: /* Line 1806 of yacc.c */ -#line 1544 "awkgram.y" +#line 1508 "awkgram.y" { if ((yyvsp[(2) - (2)])->lasti->opcode == Op_push_i - && ((yyvsp[(2) - (2)])->lasti->memory->flags & (STRCUR|STRING)) == 0) { + && ((yyvsp[(2) - (2)])->lasti->memory->flags & (STRCUR|STRING)) == 0 + ) { (yyvsp[(2) - (2)])->lasti->memory->numbr = -(force_number((yyvsp[(2) - (2)])->lasti->memory)); (yyval) = (yyvsp[(2) - (2)]); bcfree((yyvsp[(1) - (2)])); @@ -3927,35 +3899,35 @@ regular_loop: } break; - case 156: + case 155: /* Line 1806 of yacc.c */ -#line 1556 "awkgram.y" +#line 1521 "awkgram.y" { /* * was: $$ = $2 * POSIX semantics: force a conversion to numeric type */ (yyvsp[(1) - (2)])->opcode = Op_plus_i; - (yyvsp[(1) - (2)])->memory = mk_number((AWKNUM) 0.0, (PERM|NUMCUR|NUMBER)); + (yyvsp[(1) - (2)])->memory = make_number(0.0); (yyval) = list_append((yyvsp[(2) - (2)]), (yyvsp[(1) - (2)])); } break; - case 157: + case 156: /* Line 1806 of yacc.c */ -#line 1569 "awkgram.y" +#line 1534 "awkgram.y" { func_use((yyvsp[(1) - (1)])->lasti->func_name, FUNC_USE); (yyval) = (yyvsp[(1) - (1)]); } break; - case 158: + case 157: /* Line 1806 of yacc.c */ -#line 1574 "awkgram.y" +#line 1539 "awkgram.y" { /* indirect function call */ INSTRUCTION *f, *t; @@ -3976,7 +3948,7 @@ regular_loop: name = estrdup(f->func_name, strlen(f->func_name)); if (is_std_var(name)) yyerror(_("can not use special variable `%s' for indirect function call"), name); - indirect_var = variable(name, Node_var_new); + indirect_var = variable(f->source_line, name, Node_var_new); t = instruction(Op_push); t->memory = indirect_var; @@ -3990,10 +3962,10 @@ regular_loop: } break; - case 159: + case 158: /* Line 1806 of yacc.c */ -#line 1610 "awkgram.y" +#line 1575 "awkgram.y" { param_sanity((yyvsp[(3) - (4)])); (yyvsp[(1) - (4)])->opcode = Op_func_call; @@ -4009,54 +3981,54 @@ regular_loop: } break; - case 160: + case 159: /* Line 1806 of yacc.c */ -#line 1627 "awkgram.y" +#line 1592 "awkgram.y" { (yyval) = NULL; } break; - case 161: + case 160: /* Line 1806 of yacc.c */ -#line 1629 "awkgram.y" +#line 1594 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 162: + case 161: /* Line 1806 of yacc.c */ -#line 1634 "awkgram.y" +#line 1599 "awkgram.y" { (yyval) = NULL; } break; - case 163: + case 162: /* Line 1806 of yacc.c */ -#line 1636 "awkgram.y" +#line 1601 "awkgram.y" { (yyval) = (yyvsp[(1) - (2)]); } break; - case 164: + case 163: /* Line 1806 of yacc.c */ -#line 1641 "awkgram.y" +#line 1606 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 165: + case 164: /* Line 1806 of yacc.c */ -#line 1643 "awkgram.y" +#line 1608 "awkgram.y" { (yyval) = list_merge((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); } break; - case 166: + case 165: /* Line 1806 of yacc.c */ -#line 1650 "awkgram.y" +#line 1615 "awkgram.y" { INSTRUCTION *ip = (yyvsp[(1) - (1)])->lasti; int count = ip->sub_count; /* # of SUBSEP-seperated expressions */ @@ -4072,10 +4044,10 @@ regular_loop: } break; - case 167: + case 166: /* Line 1806 of yacc.c */ -#line 1667 "awkgram.y" +#line 1632 "awkgram.y" { INSTRUCTION *t = (yyvsp[(2) - (3)]); if ((yyvsp[(2) - (3)]) == NULL) { @@ -4083,7 +4055,7 @@ regular_loop: _("invalid subscript expression")); /* install Null string as subscript. */ t = list_create(instruction(Op_push_i)); - t->nexti->memory = Nnull_string; + t->nexti->memory = dupnode(Nnull_string); (yyvsp[(3) - (3)])->sub_count = 1; } else (yyvsp[(3) - (3)])->sub_count = count_expressions(&t, FALSE); @@ -4091,67 +4063,63 @@ regular_loop: } break; - case 168: + case 167: /* Line 1806 of yacc.c */ -#line 1684 "awkgram.y" +#line 1649 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); } break; - case 169: + case 168: /* Line 1806 of yacc.c */ -#line 1686 "awkgram.y" +#line 1651 "awkgram.y" { (yyval) = list_merge((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); } break; - case 170: + case 169: /* Line 1806 of yacc.c */ -#line 1693 "awkgram.y" +#line 1658 "awkgram.y" { (yyval) = (yyvsp[(1) - (2)]); } break; - case 171: + case 170: /* Line 1806 of yacc.c */ -#line 1698 "awkgram.y" +#line 1663 "awkgram.y" { char *var_name = (yyvsp[(1) - (1)])->lextok; (yyvsp[(1) - (1)])->opcode = Op_push; - (yyvsp[(1) - (1)])->memory = variable(var_name, Node_var_new); + (yyvsp[(1) - (1)])->memory = variable((yyvsp[(1) - (1)])->source_line, var_name, Node_var_new); (yyval) = list_create((yyvsp[(1) - (1)])); } break; - case 172: + case 171: /* Line 1806 of yacc.c */ -#line 1706 "awkgram.y" +#line 1671 "awkgram.y" { - NODE *n; - char *arr = (yyvsp[(1) - (2)])->lextok; - if ((n = lookup(arr)) != NULL && ! isarray(n)) - yyerror(_("use of non-array as array")); - (yyvsp[(1) - (2)])->memory = variable(arr, Node_var_new); + (yyvsp[(1) - (2)])->memory = variable((yyvsp[(1) - (2)])->source_line, arr, Node_var_new); (yyvsp[(1) - (2)])->opcode = Op_push_array; (yyval) = list_prepend((yyvsp[(2) - (2)]), (yyvsp[(1) - (2)])); } break; - case 173: + case 172: /* Line 1806 of yacc.c */ -#line 1720 "awkgram.y" +#line 1681 "awkgram.y" { INSTRUCTION *ip = (yyvsp[(1) - (1)])->nexti; if (ip->opcode == Op_push - && ip->memory->type == Node_var - && ip->memory->var_update + && ip->memory->type == Node_var + && ip->memory->var_update ) { (yyval) = list_prepend((yyvsp[(1) - (1)]), instruction(Op_var_update)); (yyval)->nexti->update_var = ip->memory->var_update; @@ -4160,81 +4128,81 @@ regular_loop: } break; - case 174: + case 173: /* Line 1806 of yacc.c */ -#line 1732 "awkgram.y" +#line 1693 "awkgram.y" { (yyval) = list_append((yyvsp[(2) - (3)]), (yyvsp[(1) - (3)])); if ((yyvsp[(3) - (3)]) != NULL) - mk_assignment((yyvsp[(2) - (3)]), NULL, (yyvsp[(3) - (3)])); + mk_assignment((yyvsp[(2) - (3)]), NULL, (yyvsp[(3) - (3)])); } break; - case 175: + case 174: /* Line 1806 of yacc.c */ -#line 1741 "awkgram.y" +#line 1702 "awkgram.y" { (yyvsp[(1) - (1)])->opcode = Op_postincrement; } break; - case 176: + case 175: /* Line 1806 of yacc.c */ -#line 1745 "awkgram.y" +#line 1706 "awkgram.y" { (yyvsp[(1) - (1)])->opcode = Op_postdecrement; } break; - case 177: + case 176: /* Line 1806 of yacc.c */ -#line 1748 "awkgram.y" +#line 1709 "awkgram.y" { (yyval) = NULL; } break; - case 179: + case 178: /* Line 1806 of yacc.c */ -#line 1756 "awkgram.y" +#line 1717 "awkgram.y" { yyerrok; } break; - case 180: + case 179: /* Line 1806 of yacc.c */ -#line 1760 "awkgram.y" +#line 1721 "awkgram.y" { yyerrok; } break; - case 183: + case 182: /* Line 1806 of yacc.c */ -#line 1769 "awkgram.y" +#line 1730 "awkgram.y" { yyerrok; } break; - case 184: + case 183: /* Line 1806 of yacc.c */ -#line 1773 "awkgram.y" +#line 1734 "awkgram.y" { (yyval) = (yyvsp[(1) - (1)]); yyerrok; } break; - case 185: + case 184: /* Line 1806 of yacc.c */ -#line 1777 "awkgram.y" +#line 1738 "awkgram.y" { yyerrok; } break; /* Line 1806 of yacc.c */ -#line 4250 "awkgram.c" +#line 4218 "awkgram.c" default: break; } /* User semantic actions sometimes alter yychar, and that requires @@ -4465,7 +4433,7 @@ yyreturn: /* Line 2067 of yacc.c */ -#line 1779 "awkgram.y" +#line 1740 "awkgram.y" struct token { @@ -4513,9 +4481,12 @@ static const struct token tokentab[] = { {"END", Op_rule, LEX_END, 0, 0}, {"ENDFILE", Op_rule, LEX_ENDFILE, GAWKX, 0}, #ifdef ARRAYDEBUG -{"adump", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_adump}, +{"adump", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_adump}, #endif {"and", Op_builtin, LEX_BUILTIN, GAWKX|A(2), do_and}, +#ifdef ARRAYDEBUG +{"aoption", Op_builtin, LEX_BUILTIN, GAWKX|A(2), do_aoption}, +#endif {"asort", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asort}, {"asorti", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asorti}, {"atan2", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2), do_atan2}, @@ -4568,9 +4539,6 @@ static const struct token tokentab[] = { {"sprintf", Op_builtin, LEX_BUILTIN, 0, do_sprintf}, {"sqrt", Op_builtin, LEX_BUILTIN, A(1), do_sqrt}, {"srand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0)|A(1), do_srand}, -#if defined(GAWKDEBUG) || defined(ARRAYDEBUG) /* || ... */ -{"stopme", Op_builtin, LEX_BUILTIN, GAWKX|A(0), stopme}, -#endif {"strftime", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|A(1)|A(2)|A(3), do_strftime}, {"strtonum", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_strtonum}, {"sub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0}, @@ -4640,7 +4608,7 @@ print_included_from() line--; msg("%s %s:%d%c", s->prev == sourcefile ? "In file included from" - : " from", + : " from", (s->stype == SRC_INC || s->stype == SRC_FILE) ? s->src : "cmd. line", line, @@ -4922,7 +4890,10 @@ parse_program(INSTRUCTION **pcode) ip_atexit = instruction(Op_atexit); /* target for `exit' in END block */ } - sourcefile = srcfiles->next; + for (sourcefile = srcfiles->next; sourcefile->stype == SRC_EXTLIB; + sourcefile = sourcefile->next) + ; + lexeof = FALSE; lexptr = NULL; lasttok = 0; @@ -4939,6 +4910,11 @@ parse_program(INSTRUCTION **pcode) if (ret == 0) /* avoid spurious warning if parser aborted with YYABORT */ check_funcs(); + if (args_array == NULL) + emalloc(args_array, NODE **, (max_args + 2) * sizeof(NODE *), "parse_program"); + else + erealloc(args_array, NODE **, (max_args + 2) * sizeof(NODE *), "parse_program"); + return (ret || errcount); } @@ -4981,7 +4957,7 @@ add_srcfile(int stype, char *src, SRCFILE *thisfile, int *already_included, int if (stype == SRC_CMDLINE || stype == SRC_STDIN) return do_add_srcfile(stype, src, NULL, thisfile); - path = find_source(src, &sbuf, &errno_val); + path = find_source(src, & sbuf, &errno_val, stype == SRC_EXTLIB); if (path == NULL) { if (errcode) { *errcode = errno_val; @@ -4992,7 +4968,7 @@ add_srcfile(int stype, char *src, SRCFILE *thisfile, int *already_included, int } for (s = srcfiles->next; s != srcfiles; s = s->next) { - if ((s->stype == SRC_FILE || s->stype == SRC_INC) + if ((s->stype == SRC_FILE || s->stype == SRC_INC || s->stype == SRC_EXTLIB) && files_are_same(path, s) ) { if (do_lint) { @@ -5094,6 +5070,7 @@ next_sourcefile() * * assert(lexeof == TRUE); */ + lexeof = FALSE; eof_warned = FALSE; sourcefile->srclines = sourceline; /* total no of lines in current file */ @@ -5108,9 +5085,12 @@ next_sourcefile() sourcefile->lexptr_begin = NULL; } - sourcefile = sourcefile->next; - if (sourcefile == srcfiles) - return; + while ((sourcefile = sourcefile->next) != NULL) { + if (sourcefile == srcfiles) + return; + if (sourcefile->stype != SRC_EXTLIB) + break; + } if (sourcefile->lexptr_begin != NULL) { /* resume reading from already opened file (postponed to process '@include') */ @@ -5484,6 +5464,7 @@ yylex(void) char *tokkey; int inhex = FALSE; int intlstr = FALSE; + AWKNUM d; #define GET_INSTRUCTION(op) bcalloc(op, 1, sourceline) @@ -5922,9 +5903,7 @@ retry: yylval->opcode = Op_push_i; yylval->memory = make_str_node(tokstart, - tok - tokstart, esc_seen ? SCAN : 0); - yylval->memory->flags &= ~MALLOC; - yylval->memory->flags |= PERM; + tok - tokstart, esc_seen ? SCAN : 0); if (intlstr) { yylval->memory->flags |= INTLSTR; intlstr = FALSE; @@ -6066,10 +6045,12 @@ retry: lintwarn("numeric constant `%.*s' treated as hexadecimal", (int) strlen(tokstart)-1, tokstart); } - yylval->memory = mk_number(nondec2awknum(tokstart, strlen(tokstart)), - PERM|NUMCUR|NUMBER); + d = nondec2awknum(tokstart, strlen(tokstart)); } else - yylval->memory = mk_number(atof(tokstart), PERM|NUMCUR|NUMBER); + d = atof(tokstart); + yylval->memory = make_number(d); + if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d) + yylval->memory->flags |= NUMINT; return lasttok = YNUMBER; case '&': @@ -6206,7 +6187,7 @@ retry: case LEX_WHILE: case LEX_DO: case LEX_SWITCH: - if (! do_profiling) + if (! do_pretty_print) return lasttok = class; /* fall through */ case LEX_CASE: @@ -6246,23 +6227,6 @@ out: #undef NEWLINE_EOF } -/* mk_symbol --- allocates a symbol for the symbol table. */ - -NODE * -mk_symbol(NODETYPE type, NODE *value) -{ - NODE *r; - - getnode(r); - r->type = type; - r->flags = MALLOC; - r->lnode = value; - r->rnode = NULL; - r->parent_array = NULL; - r->var_assign = (Func_ptr) 0; - return r; -} - /* snode --- instructions for builtin functions. Checks for arg. count and supplies defaults where possible. */ @@ -6314,7 +6278,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) INSTRUCTION *expr; expr = list_create(instruction(Op_push_i)); - expr->nexti->memory = mk_number((AWKNUM) 0.0, (PERM|NUMCUR|NUMBER)); + expr->nexti->memory = make_number(0.0); (void) mk_expression_list(subn, list_append(expr, instruction(Op_field_spec))); } @@ -6358,7 +6322,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) r->sub_flags |= GENSUB; if (nexp == 3) { ip = instruction(Op_push_i); - ip->memory = mk_number((AWKNUM) 0.0, (PERM|NUMCUR|NUMBER)); + ip->memory = make_number(0.0); (void) mk_expression_list(subn, list_append(list_create(ip), instruction(Op_field_spec))); } @@ -6381,7 +6345,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) list = list_create(r); (void) list_prepend(list, instruction(Op_field_spec)); (void) list_prepend(list, instruction(Op_push_i)); - list->nexti->memory = mk_number((AWKNUM) 0.0, (PERM|NUMCUR|NUMBER)); + list->nexti->memory = make_number(0.0); return list; } else { arg = subn->nexti; @@ -6509,7 +6473,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) if (ip->opcode == Op_push) ip->opcode = Op_push_array; } -#endif +#endif if (subn != NULL) { r->expr_count = count_expressions(&subn, FALSE); @@ -6520,75 +6484,6 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) return list_create(r); } -/* append_param --- append PNAME to the list of parameters - * for the current function. - */ - -static void -append_param(char *pname) -{ - static NODE *savetail = NULL; - NODE *p; - - p = make_param(pname); - if (func_params == NULL) { - func_params = p; - savetail = p; - } else if (savetail != NULL) { - savetail->rnode = p; - savetail = p; - } -} - -/* dup_parms --- return TRUE if there are duplicate parameters */ - -static int -dup_parms(INSTRUCTION *fp, NODE *func) -{ - NODE *np; - const char *fname, **names; - int count, i, j, dups; - NODE *params; - - if (func == NULL) /* error earlier */ - return TRUE; - - fname = func->param; - count = func->param_cnt; - params = func->rnode; - - if (count == 0) /* no args, no problem */ - return FALSE; - - if (params == NULL) /* error earlier */ - return TRUE; - - emalloc(names, const char **, count * sizeof(char *), "dup_parms"); - - i = 0; - for (np = params; np != NULL; np = np->rnode) { - if (np->param == NULL) { /* error earlier, give up, go home */ - efree(names); - return TRUE; - } - names[i++] = np->param; - } - - dups = 0; - for (i = 1; i < count; i++) { - for (j = 0; j < i; j++) { - if (strcmp(names[i], names[j]) == 0) { - dups++; - error_ln(fp->source_line, - _("function `%s': parameter #%d, `%s', duplicates parameter #%d"), - fname, i + 1, names[j], j+1); - } - } - } - - efree(names); - return (dups > 0 ? TRUE : FALSE); -} /* parms_shadow --- check if parameters shadow globals */ @@ -6597,18 +6492,19 @@ parms_shadow(INSTRUCTION *pc, int *shadow) { int pcount, i; int ret = FALSE; - NODE *func; + NODE *func, *fp; char *fname; func = pc->func_body; - fname = func->lnode->param; - + fname = func->vname; + fp = func->fparms; + #if 0 /* can't happen, already exited if error ? */ if (fname == NULL || func == NULL) /* error earlier */ return FALSE; #endif - pcount = func->lnode->param_cnt; + pcount = func->param_cnt; if (pcount == 0) /* no args, no problem */ return 0; @@ -6620,10 +6516,10 @@ parms_shadow(INSTRUCTION *pc, int *shadow) * about all shadowed parameters. */ for (i = 0; i < pcount; i++) { - if (lookup(func->parmlist[i]) != NULL) { + if (lookup(fp[i].param) != NULL) { warning( _("function `%s': parameter `%s' shadows global variable"), - fname, func->parmlist[i]); + fname, fp[i].param); ret = TRUE; } } @@ -6632,80 +6528,10 @@ parms_shadow(INSTRUCTION *pc, int *shadow) return 0; } - -/* - * install_symbol: - * Install a name in the symbol table, even if it is already there. - * Caller must check against redefinition if that is desired. - */ - - -NODE * -install_symbol(char *name, NODE *value) -{ - NODE *hp; - size_t len; - int bucket; - - if (install_func) - (*install_func)(name); - - var_count++; - len = strlen(name); - bucket = hash(name, len, (unsigned long) HASHSIZE, NULL); - getnode(hp); - hp->type = Node_hashnode; - hp->hnext = variables[bucket]; - variables[bucket] = hp; - hp->hlength = len; - hp->hvalue = value; - hp->hname = name; - hp->hvalue->vname = name; - return hp->hvalue; -} - -/* lookup --- find the most recent hash node for name installed by install_symbol */ - -NODE * -lookup(const char *name) -{ - NODE *bucket; - size_t len; - - len = strlen(name); - for (bucket = variables[hash(name, len, (unsigned long) HASHSIZE, NULL)]; - bucket != NULL; bucket = bucket->hnext) - if (bucket->hlength == len && strncmp(bucket->hname, name, len) == 0) - return bucket->hvalue; - return NULL; -} - -/* sym_comp --- compare two symbol (variable or function) names */ - -static int -sym_comp(const void *v1, const void *v2) -{ - const NODE *const *npp1, *const *npp2; - const NODE *n1, *n2; - int minlen; - - npp1 = (const NODE *const *) v1; - npp2 = (const NODE *const *) v2; - n1 = *npp1; - n2 = *npp2; - - if (n1->hlength > n2->hlength) - minlen = n1->hlength; - else - minlen = n2->hlength; - - return strncmp(n1->hname, n2->hname, minlen); -} - /* valinfo --- dump var info */ void -valinfo(NODE *n, int (*print_func)(FILE *, const char *, ...), FILE *fp) +valinfo(NODE *n, Func_print print_func, FILE *fp) { if (n == Nnull_string) print_func(fp, "uninitialized scalar\n"); @@ -6723,52 +6549,6 @@ valinfo(NODE *n, int (*print_func)(FILE *, const char *, ...), FILE *fp) print_func(fp, "?? flags %s\n", flags2str(n->flags)); } -/* get_varlist --- list of global variables */ - -NODE ** -get_varlist() -{ - int i, j; - NODE **table; - NODE *p; - - emalloc(table, NODE **, (var_count + 1) * sizeof(NODE *), "get_varlist"); - update_global_values(); - for (i = j = 0; i < HASHSIZE; i++) - for (p = variables[i]; p != NULL; p = p->hnext) - table[j++] = p; - assert(j == var_count); - - /* Shazzam! */ - qsort(table, j, sizeof(NODE *), sym_comp); - - table[j] = NULL; - return table; -} - -/* print_vars --- print names and values of global variables */ - -void -print_vars(int (*print_func)(FILE *, const char *, ...), FILE *fp) -{ - int i; - NODE **table; - NODE *p; - - table = get_varlist(); - for (i = 0; (p = table[i]) != NULL; i++) { - if (p->hvalue->type == Node_func) - continue; - print_func(fp, "%.*s: ", (int) p->hlength, p->hname); - if (p->hvalue->type == Node_var_array) - print_func(fp, "array, %ld elements\n", p->hvalue->table_size); - else if (p->hvalue->type == Node_var_new) - print_func(fp, "untyped variable\n"); - else if (p->hvalue->type == Node_var) - valinfo(p->hvalue->var_value, print_func, fp); - } - efree(table); -} /* dump_vars --- dump the symbol table */ @@ -6776,6 +6556,7 @@ void dump_vars(const char *fname) { FILE *fp; + NODE **vars; if (fname == NULL) fp = stderr; @@ -6785,48 +6566,25 @@ dump_vars(const char *fname) fp = stderr; } - print_vars(fprintf, fp); + vars = variable_list(); + print_vars(vars, fprintf, fp); + efree(vars); if (fp != stderr && fclose(fp) != 0) warning(_("%s: close failed (%s)"), fname, strerror(errno)); } -/* release_all_vars --- free all variable memory */ - -void -release_all_vars() -{ - int i; - NODE *p, *next; - - for (i = 0; i < HASHSIZE; i++) { - for (p = variables[i]; p != NULL; p = next) { - next = p->hnext; - - if (p->hvalue->type == Node_func) - continue; - else if (p->hvalue->type == Node_var_array) - assoc_clear(p->hvalue); - else if (p->hvalue->type != Node_var_new) - unref(p->hvalue->var_value); - - efree(p->hname); - freenode(p->hvalue); - freenode(p); - } - } -} - /* dump_funcs --- print all functions */ void dump_funcs() { - if (func_count <= 0) - return; - - (void) foreach_func((int (*)(INSTRUCTION *, void *)) pp_func, TRUE, (void *) 0); + NODE **funcs; + funcs = function_list(TRUE); + (void) foreach_func(funcs, (int (*)(INSTRUCTION *, void *)) pp_func, (void *) 0); + efree(funcs); } + /* shadow_funcs --- check all functions for parameters that shadow globals */ void @@ -6834,175 +6592,164 @@ shadow_funcs() { static int calls = 0; int shadow = FALSE; - - if (func_count <= 0) - return; + NODE **funcs; if (calls++ != 0) fatal(_("shadow_funcs() called twice!")); - (void) foreach_func((int (*)(INSTRUCTION *, void *)) parms_shadow, TRUE, &shadow); + funcs = function_list(TRUE); + (void) foreach_func(funcs, (int (*)(INSTRUCTION *, void *)) parms_shadow, & shadow); + efree(funcs); /* End with fatal if the user requested it. */ if (shadow && lintfunc != warning) lintwarn(_("there were shadowed variables.")); } -/* - * func_install: - * check if name is already installed; if so, it had better have Null value, - * in which case def is added as the value. Otherwise, install name with def - * as value. - * - * Extra work, build up and save a list of the parameter names in a table - * and hang it off params->parmlist. This is used to set the `vname' field - * of each function parameter during a function call. See eval.c. + +/* mk_function --- finalize function definition node; remove parameters + * out of the symbol table. */ -static int -func_install(INSTRUCTION *func, INSTRUCTION *def) +static INSTRUCTION * +mk_function(INSTRUCTION *fi, INSTRUCTION *def) { - NODE *params; - NODE *r, *n, *thisfunc, *hp; - char **pnames = NULL; - char *fname; - int pcount = 0; - int i; + NODE *thisfunc; - params = func_params; + thisfunc = fi->func_body; + assert(thisfunc != NULL); - /* check for function foo(foo) { ... }. bleah. */ - for (n = params->rnode; n != NULL; n = n->rnode) { - if (strcmp(n->param, params->param) == 0) { - error_ln(func->source_line, - _("function `%s': can't use function name as parameter name"), params->param); - return -1; - } else if (is_std_var(n->param)) { - error_ln(func->source_line, - _("function `%s': can't use special variable `%s' as a function parameter"), - params->param, n->param); - return -1; - } - } + if (do_optimize > 1 && def->lasti->opcode == Op_pop) { + /* tail call which does not return any value. */ - thisfunc = NULL; /* turn off warnings */ + INSTRUCTION *t; - fname = params->param; - /* symbol table management */ - hp = remove_symbol(params->param); /* remove function name out of symbol table */ - if (hp != NULL) - freenode(hp); - r = lookup(fname); - if (r != NULL) { - error_ln(func->source_line, - _("function name `%s' previously defined"), fname); - return -1; - } else if (fname == builtin_func) /* not a valid function name */ - goto remove_params; + for (t = def->nexti; t->nexti != def->lasti; t = t->nexti) + ; + if (t->opcode == Op_func_call + && strcmp(t->func_name, thisfunc->vname) == 0) + (t + 1)->tail_call = TRUE; + } /* add an implicit return at end; * also used by 'return' command in debugger */ - + (void) list_append(def, instruction(Op_push_i)); - def->lasti->memory = Nnull_string; + def->lasti->memory = dupnode(Nnull_string); (void) list_append(def, instruction(Op_K_return)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(def, instruction(Op_exec_count)); - /* func->opcode is Op_func */ - (func + 1)->firsti = def->nexti; - (func + 1)->lasti = def->lasti; - (func + 2)->first_line = func->source_line; - (func + 2)->last_line = lastline; - - func->nexti = def->nexti; + /* fi->opcode = Op_func */ + (fi + 1)->firsti = def->nexti; + (fi + 1)->lasti = def->lasti; + (fi + 2)->first_line = fi->source_line; + (fi + 2)->last_line = lastline; + fi->nexti = def->nexti; bcfree(def); - (void) list_append(rule_list, func + 1); /* debugging */ - - /* install the function */ - thisfunc = mk_symbol(Node_func, params); - (void) install_symbol(fname, thisfunc); - thisfunc->code_ptr = func; - func->func_body = thisfunc; - - for (n = params->rnode; n != NULL; n = n->rnode) - pcount++; - - if (pcount != 0) { - emalloc(pnames, char **, (pcount + 1) * sizeof(char *), "func_install"); - for (i = 0, n = params->rnode; i < pcount; i++, n = n->rnode) - pnames[i] = n->param; - pnames[pcount] = NULL; - } - thisfunc->parmlist = pnames; + (void) list_append(rule_list, fi + 1); /* debugging */ /* update lint table info */ - func_use(fname, FUNC_DEFINE); - - func_count++; /* used in profiler / pretty printer */ + func_use(thisfunc->vname, FUNC_DEFINE); -remove_params: /* remove params from symbol table */ - pop_params(params->rnode); - return 0; + remove_params(thisfunc); + return fi; } -/* remove_symbol --- remove a variable from the symbol table */ +/* + * install_function: + * install function name in the symbol table. + * Extra work, build up and install a list of the parameter names. + */ -NODE * -remove_symbol(char *name) +static int +install_function(char *fname, INSTRUCTION *fi, INSTRUCTION *plist) { - NODE *bucket, **save; - size_t len; + NODE *r, *f; + int pcount = 0; - len = strlen(name); - save = &(variables[hash(name, len, (unsigned long) HASHSIZE, NULL)]); - for (bucket = *save; bucket != NULL; bucket = bucket->hnext) { - if (len == bucket->hlength && strncmp(bucket->hname, name, len) == 0) { - var_count--; - *save = bucket->hnext; - return bucket; - } - save = &(bucket->hnext); + r = lookup(fname); + if (r != NULL) { + error_ln(fi->source_line, _("function name `%s' previously defined"), fname); + return -1; } - return NULL; + + if (plist != NULL) + pcount = plist->lasti->param_count + 1; + f = install_symbol(fname, Node_func); + fi->func_body = f; + f->param_cnt = pcount; + f->code_ptr = fi; + f->fparms = NULL; + if (pcount > 0) { + char **pnames; + pnames = check_params(fname, pcount, plist); /* frees plist */ + f->fparms = make_params(pnames, pcount); + efree(pnames); + install_params(f); + } + return 0; } -/* pop_params --- remove list of function parameters from symbol table */ -/* - * pop parameters out of the symbol table. do this in reverse order to - * avoid reading freed memory if there were duplicated parameters. +/* check_params --- build a list of function parameter names after + * making sure that the names are valid and there are no duplicates. */ -static void -pop_params(NODE *params) + +static char ** +check_params(char *fname, int pcount, INSTRUCTION *list) { - NODE *hp; - if (params == NULL) - return; - pop_params(params->rnode); - hp = remove_symbol(params->param); - if (hp != NULL) - freenode(hp); -} + INSTRUCTION *p, *np; + int i, j; + char *name; + char **pnames; -/* make_param --- make NAME into a function parameter */ + assert(pcount > 0); -static NODE * -make_param(char *name) -{ - NODE *r; + emalloc(pnames, char **, pcount * sizeof(char *), "check_params"); + + for (i = 0, p = list->nexti; p != NULL; i++, p = np) { + np = p->nexti; + name = p->lextok; + p->lextok = NULL; + + if (strcmp(name, fname) == 0) { + /* check for function foo(foo) { ... }. bleah. */ + error_ln(p->source_line, + _("function `%s': can't use function name as parameter name"), fname); + } else if (is_std_var(name)) { + error_ln(p->source_line, + _("function `%s': can't use special variable `%s' as a function parameter"), + fname, name); + } + + /* check for duplicate parameters */ + for (j = 0; j < i; j++) { + if (strcmp(name, pnames[j]) == 0) { + error_ln(p->source_line, + _("function `%s': parameter #%d, `%s', duplicates parameter #%d"), + fname, i + 1, name, j + 1); + } + } + + pnames[i] = name; + bcfree(p); + } + bcfree(list); - getnode(r); - r->type = Node_param_list; - r->rnode = NULL; - r->param_cnt = param_counter++; - return (install_symbol(name, r)); + return pnames; } + +#ifdef HASHSIZE +undef HASHSIZE +#endif +#define HASHSIZE 1021 + static struct fdesc { char *name; short used; @@ -7110,69 +6857,6 @@ param_sanity(INSTRUCTION *arglist) } } -/* foreach_func --- execute given function for each awk function in symbol table. */ - -int -foreach_func(int (*pfunc)(INSTRUCTION *, void *), int sort, void *data) -{ - int i, j; - NODE *p; - int ret = 0; - - if (sort) { - NODE **tab; - - /* - * Walk through symbol table counting functions. - * Could be more than func_count if there are - * extension functions. - */ - for (i = j = 0; i < HASHSIZE; i++) { - for (p = variables[i]; p != NULL; p = p->hnext) { - if (p->hvalue->type == Node_func) { - j++; - } - } - } - - if (j == 0) - return 0; - - emalloc(tab, NODE **, j * sizeof(NODE *), "foreach_func"); - - /* now walk again, copying info */ - for (i = j = 0; i < HASHSIZE; i++) { - for (p = variables[i]; p != NULL; p = p->hnext) { - if (p->hvalue->type == Node_func) { - tab[j] = p; - j++; - } - } - } - - /* Shazzam! */ - qsort(tab, j, sizeof(NODE *), sym_comp); - - for (i = 0; i < j; i++) { - if ((ret = pfunc(tab[i]->hvalue->code_ptr, data)) != 0) - break; - } - - efree(tab); - return ret; - } - - /* unsorted */ - for (i = 0; i < HASHSIZE; i++) { - for (p = variables[i]; p != NULL; p = p->hnext) { - if (p->hvalue->type == Node_func - && (ret = pfunc(p->hvalue->code_ptr, data)) != 0) - return ret; - } - } - return 0; -} - /* deferred variables --- those that are only defined if needed. */ /* @@ -7207,17 +6891,14 @@ register_deferred_variable(const char *name, NODE *(*load_func)(void)) /* variable --- make sure NAME is in the symbol table */ NODE * -variable(char *name, NODETYPE type) +variable(int location, char *name, NODETYPE type) { NODE *r; if ((r = lookup(name)) != NULL) { - if (r->type == Node_func) { - error(_("function `%s' called with space between name and `(',\nor used as a variable or an array"), + if (r->type == Node_func || r->type == Node_ext_func ) + error_ln(location, _("function `%s' called with space between name and `(',\nor used as a variable or an array"), r->vname); - errcount++; - r->type = Node_var_new; /* continue parsing instead of exiting */ - } } else { /* not found */ struct deferred_variable *dv; @@ -7227,11 +6908,7 @@ variable(char *name, NODETYPE type) /* * This is the only case in which we may not free the string. */ - if (type == Node_var) - r = mk_symbol(type, Nnull_string); - else - r = mk_symbol(type, (NODE *) NULL); - return install_symbol(name, r); + return install_symbol(name, type); } if (strcmp(name, dv->name) == 0) { r = (*dv->load_func)(); @@ -7338,9 +7015,6 @@ make_assignable(INSTRUCTION *ip) { switch (ip->opcode) { case Op_push: - if (ip->memory->type == Node_param_list - && (ip->memory->flags & FUNC) != 0) - return NULL; ip->opcode = Op_push_lhs; return ip; case Op_field_spec: @@ -7355,14 +7029,6 @@ make_assignable(INSTRUCTION *ip) return NULL; } -/* stopme --- for debugging */ - -NODE * -stopme(int nargs ATTRIBUTE_UNUSED) -{ - return (NODE *) 0; -} - /* dumpintlstr --- write out an initial .po file entry for the string */ static void @@ -7412,27 +7078,6 @@ dumpintlstr2(const char *str1, size_t len1, const char *str2, size_t len2) fflush(stdout); } -/* isarray --- can this type be subscripted? */ - -static int -isarray(NODE *n) -{ - switch (n->type) { - case Node_var_new: - case Node_var_array: - return TRUE; - case Node_param_list: - return (n->flags & FUNC) == 0; - case Node_array_ref: - cant_happen(); - break; - default: - break; /* keeps gcc -Wall happy */ - } - - return FALSE; -} - /* mk_binary --- instructions for binary operators */ static INSTRUCTION * @@ -7493,11 +7138,7 @@ mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op) } op->opcode = Op_push_i; - op->memory = mk_number(res, (PERM|NUMCUR|NUMBER)); - n1->flags &= ~PERM; - n1->flags |= MALLOC; - n2->flags &= ~PERM; - n2->flags |= MALLOC; + op->memory = make_number(res); unref(n1); unref(n2); bcfree(ip1); @@ -7623,7 +7264,7 @@ mk_condition(INSTRUCTION *cond, INSTRUCTION *ifp, INSTRUCTION *true_branch, if (false_branch == NULL) { false_branch = list_create(instruction(Op_no_op)); if (elsep != NULL) { /* else { } */ - if (do_profiling) + if (do_pretty_print) (void) list_prepend(false_branch, elsep); else bcfree(elsep); @@ -7634,7 +7275,7 @@ mk_condition(INSTRUCTION *cond, INSTRUCTION *ifp, INSTRUCTION *true_branch, /* avoid a series of no_op's: if .. else if .. else if .. */ if (false_branch->lasti->opcode != Op_no_op) (void) list_append(false_branch, instruction(Op_no_op)); - if (do_profiling) { + if (do_pretty_print) { (void) list_prepend(false_branch, elsep); false_branch->nexti->branch_end = false_branch->lasti; (void) list_prepend(false_branch, instruction(Op_exec_count)); @@ -7649,7 +7290,7 @@ mk_condition(INSTRUCTION *cond, INSTRUCTION *ifp, INSTRUCTION *true_branch, ip = list_append(cond, instruction(Op_jmp_false)); ip->lasti->target_jmp = false_branch->nexti->nexti; - if (do_profiling) { + if (do_pretty_print) { (void) list_prepend(ip, ifp); (void) list_append(ip, instruction(Op_exec_count)); ip->nexti->branch_if = ip->lasti; @@ -7711,7 +7352,7 @@ append_rule(INSTRUCTION *pattern, INSTRUCTION *action) if (rule != Rule) { rp = pattern; - if (do_profiling) + if (do_pretty_print) (void) list_append(action, instruction(Op_no_op)); (rp + 1)->firsti = action->nexti; (rp + 1)->lasti = action->lasti; @@ -7727,7 +7368,7 @@ append_rule(INSTRUCTION *pattern, INSTRUCTION *action) if (pattern == NULL) { /* assert(action != NULL); */ - if (do_profiling) + if (do_pretty_print) (void) list_prepend(action, instruction(Op_exec_count)); (rp + 1)->firsti = action->nexti; (rp + 1)->lasti = tp; @@ -7743,12 +7384,12 @@ append_rule(INSTRUCTION *pattern, INSTRUCTION *action) if (action == NULL) { (rp + 2)->last_line = find_line(pattern, LAST_LINE); action = list_create(instruction(Op_K_print_rec)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(action, instruction(Op_exec_count)); } else (rp + 2)->last_line = lastline; - if (do_profiling) { + if (do_pretty_print) { (void) list_prepend(pattern, instruction(Op_exec_count)); (void) list_prepend(action, instruction(Op_exec_count)); } @@ -7828,9 +7469,7 @@ mk_assignment(INSTRUCTION *lhs, INSTRUCTION *rhs, INSTRUCTION *op) static INSTRUCTION * optimize_assignment(INSTRUCTION *exp) { - INSTRUCTION *i1; - INSTRUCTION *i2; - INSTRUCTION *i3; + INSTRUCTION *i1, *i2, *i3; /* * Optimize assignment statements array[subs] = x; var = x; $n = x; @@ -7866,7 +7505,7 @@ optimize_assignment(INSTRUCTION *exp) if ( ! do_optimize || ( i1->opcode != Op_assign && i1->opcode != Op_field_assign) - ) + ) return list_append(exp, instruction(Op_pop)); for (i2 = exp->nexti; i2 != i1; i2 = i2->nexti) { @@ -7951,13 +7590,26 @@ optimize_assignment(INSTRUCTION *exp) case Op_push_lhs: if (i2->nexti == i1 - && i1->opcode == Op_assign + && i1->opcode == Op_assign ) { /* var = .. */ i2->opcode = Op_store_var; i2->nexti = NULL; bcfree(i1); /* Op_assign */ exp->lasti = i2; /* update Op_list */ + + i3 = exp->nexti; + if (i3->opcode == Op_push_i + && (i3->memory->flags & INTLSTR) == 0 + && i3->nexti == i2 + ) { + /* constant initializer */ + i2->initval = i3->memory; + bcfree(i3); + exp->nexti = i2; + } else + i2->initval = NULL; + return exp; } break; @@ -8078,7 +7730,7 @@ mk_for_loop(INSTRUCTION *forp, INSTRUCTION *init, INSTRUCTION *cond, if (init != NULL) ip = list_merge(init, ip); - if (do_profiling) { + if (do_pretty_print) { (void) list_append(ip, instruction(Op_exec_count)); (forp + 1)->forloop_cond = pp_cond; (forp + 1)->forloop_body = ip->lasti; @@ -8100,7 +7752,7 @@ mk_for_loop(INSTRUCTION *forp, INSTRUCTION *init, INSTRUCTION *cond, ret = list_append(ip, tbreak); fix_break_continue(ret, tbreak, tcont); - if (do_profiling) { + if (do_pretty_print) { forp->target_break = tbreak; forp->target_continue = tcont; ret = list_prepend(ret, forp); @@ -8259,320 +7911,6 @@ fix_break_continue(INSTRUCTION *list, INSTRUCTION *b_target, INSTRUCTION *c_targ } } - -/* append_symbol --- append symbol to the list of symbols - * installed in the symbol table. - */ - -void -append_symbol(char *name) -{ - NODE *hp; - - /* N.B.: func_install removes func name and reinstalls it; - * and we get two entries for it here!. destroy_symbol() - * will find and destroy the Node_func which is what we want. - */ - - getnode(hp); - hp->hname = name; /* shallow copy */ - hp->hnext = symbol_list->hnext; - symbol_list->hnext = hp; -} - -/* release_symbol --- free symbol list and optionally remove symbol from symbol table */ - -void -release_symbols(NODE *symlist, int keep_globals) -{ - NODE *hp, *n; - - for (hp = symlist->hnext; hp != NULL; hp = n) { - if (! keep_globals) { - /* destroys globals, function, and params - * if still in symbol table and not removed by func_install - * due to syntax error. - */ - destroy_symbol(hp->hname); - } - n = hp->hnext; - freenode(hp); - } - symlist->hnext = NULL; -} - -/* destroy_symbol --- remove a symbol from symbol table -* and free all associated memory. -*/ - -void -destroy_symbol(char *name) -{ - NODE *symbol, *hp; - - symbol = lookup(name); - if (symbol == NULL) - return; - - if (symbol->type == Node_func) { - char **varnames; - NODE *func, *n; - - func = symbol; - varnames = func->parmlist; - if (varnames != NULL) - efree(varnames); - - /* function parameters of type Node_param_list */ - for (n = func->lnode->rnode; n != NULL; ) { - NODE *np; - np = n->rnode; - efree(n->param); - freenode(n); - n = np; - } - freenode(func->lnode); - func_count--; - - } else if (symbol->type == Node_var_array) - assoc_clear(symbol); - else if (symbol->type == Node_var) - unref(symbol->var_value); - - /* remove from symbol table */ - hp = remove_symbol(name); - efree(hp->hname); - freenode(hp->hvalue); - freenode(hp); -} - -#define pool_size d.dl -#define freei x.xi -static INSTRUCTION *pool_list; -static AWK_CONTEXT *curr_ctxt = NULL; - -/* new_context --- create a new execution context. */ - -AWK_CONTEXT * -new_context() -{ - AWK_CONTEXT *ctxt; - - emalloc(ctxt, AWK_CONTEXT *, sizeof(AWK_CONTEXT), "new_context"); - memset(ctxt, 0, sizeof(AWK_CONTEXT)); - ctxt->srcfiles.next = ctxt->srcfiles.prev = &ctxt->srcfiles; - ctxt->rule_list.opcode = Op_list; - ctxt->rule_list.lasti = &ctxt->rule_list; - return ctxt; -} - -/* set_context --- change current execution context. */ - -static void -set_context(AWK_CONTEXT *ctxt) -{ - pool_list = &ctxt->pools; - symbol_list = &ctxt->symbols; - srcfiles = &ctxt->srcfiles; - rule_list = &ctxt->rule_list; - install_func = ctxt->install_func; - curr_ctxt = ctxt; -} - -/* - * push_context: - * - * Switch to the given context after saving the current one. The set - * of active execution contexts forms a stack; the global or main context - * is at the bottom of the stack. - */ - -void -push_context(AWK_CONTEXT *ctxt) -{ - ctxt->prev = curr_ctxt; - /* save current source and sourceline */ - if (curr_ctxt != NULL) { - curr_ctxt->sourceline = sourceline; - curr_ctxt->source = source; - } - sourceline = 0; - source = NULL; - set_context(ctxt); -} - -/* pop_context --- switch to previous execution context. */ - -void -pop_context() -{ - AWK_CONTEXT *ctxt; - - assert(curr_ctxt != NULL); - ctxt = curr_ctxt->prev; - /* restore source and sourceline */ - sourceline = ctxt->sourceline; - source = ctxt->source; - set_context(ctxt); -} - -/* in_main_context --- are we in the main context ? */ - -int -in_main_context() -{ - assert(curr_ctxt != NULL); - return (curr_ctxt->prev == NULL); -} - -/* free_context --- free context structure and related data. */ - -void -free_context(AWK_CONTEXT *ctxt, int keep_globals) -{ - SRCFILE *s, *sn; - - if (ctxt == NULL) - return; - - assert(curr_ctxt != ctxt); - - /* free all code including function codes */ - free_bcpool(&ctxt->pools); - /* free symbols */ - release_symbols(&ctxt->symbols, keep_globals); - /* free srcfiles */ - for (s = &ctxt->srcfiles; s != &ctxt->srcfiles; s = sn) { - sn = s->next; - if (s->stype != SRC_CMDLINE && s->stype != SRC_STDIN) - efree(s->fullpath); - efree(s->src); - efree(s); - } - efree(ctxt); -} - -/* free_bc_internal --- free internal memory of an instruction. */ - -static void -free_bc_internal(INSTRUCTION *cp) -{ - NODE *m; - - switch(cp->opcode) { - case Op_func_call: - if (cp->func_name != NULL - && cp->func_name != builtin_func - ) - efree(cp->func_name); - break; - case Op_push_re: - case Op_match_rec: - case Op_match: - case Op_nomatch: - m = cp->memory; - if (m->re_reg != NULL) - refree(m->re_reg); - if (m->re_exp != NULL) - unref(m->re_exp); - if (m->re_text != NULL) - unref(m->re_text); - freenode(m); - break; - case Op_token: /* token lost during error recovery in yyparse */ - if (cp->lextok != NULL) - efree(cp->lextok); - break; - case Op_illegal: - cant_happen(); - default: - break; - } -} - - -/* INSTR_CHUNK must be > largest code size (3) */ -#define INSTR_CHUNK 127 - -/* bcfree --- deallocate instruction */ - -void -bcfree(INSTRUCTION *cp) -{ - cp->opcode = 0; - cp->nexti = pool_list->freei; - pool_list->freei = cp; -} - -/* bcalloc --- allocate a new instruction */ - -INSTRUCTION * -bcalloc(OPCODE op, int size, int srcline) -{ - INSTRUCTION *cp; - - if (size > 1) { - /* wide instructions Op_rule, Op_func_call .. */ - emalloc(cp, INSTRUCTION *, (size + 1) * sizeof(INSTRUCTION), "bcalloc"); - cp->pool_size = size; - cp->nexti = pool_list->nexti; - pool_list->nexti = cp++; - } else { - INSTRUCTION *pool; - - pool = pool_list->freei; - if (pool == NULL) { - INSTRUCTION *last; - emalloc(cp, INSTRUCTION *, (INSTR_CHUNK + 1) * sizeof(INSTRUCTION), "bcalloc"); - - cp->pool_size = INSTR_CHUNK; - cp->nexti = pool_list->nexti; - pool_list->nexti = cp; - pool = ++cp; - last = &pool[INSTR_CHUNK - 1]; - for (; cp <= last; cp++) { - cp->opcode = 0; - cp->nexti = cp + 1; - } - --cp; - cp->nexti = NULL; - } - cp = pool; - pool_list->freei = cp->nexti; - } - - memset(cp, 0, size * sizeof(INSTRUCTION)); - cp->opcode = op; - cp->source_line = srcline; - return cp; -} - -/* free_bcpool --- free list of instruction memory pools */ - -static void -free_bcpool(INSTRUCTION *pl) -{ - INSTRUCTION *pool, *tmp; - - for (pool = pl->nexti; pool != NULL; pool = tmp) { - INSTRUCTION *cp, *last; - long psiz; - psiz = pool->pool_size; - if (psiz == INSTR_CHUNK) - last = pool + psiz; - else - last = pool + 1; - for (cp = pool + 1; cp <= last ; cp++) { - if (cp->opcode != 0) - free_bc_internal(cp); - } - tmp = pool->nexti; - efree(pool); - } - memset(pl, 0, sizeof(INSTRUCTION)); -} - - static inline INSTRUCTION * list_create(INSTRUCTION *x) { @@ -42,19 +42,15 @@ static char *get_src_buf(void); static int yylex(void); int yyparse(void); static INSTRUCTION *snode(INSTRUCTION *subn, INSTRUCTION *op); -static int func_install(INSTRUCTION *fp, INSTRUCTION *def); -static void pop_params(NODE *params); -static NODE *make_param(char *pname); +static char **check_params(char *fname, int pcount, INSTRUCTION *list); +static int install_function(char *fname, INSTRUCTION *fi, INSTRUCTION *plist); static NODE *mk_rexp(INSTRUCTION *exp); -static void append_param(char *pname); -static int dup_parms(INSTRUCTION *fp, NODE *func); static void param_sanity(INSTRUCTION *arglist); static int parms_shadow(INSTRUCTION *pc, int *shadow); static int isnoeffect(OPCODE type); static INSTRUCTION *make_assignable(INSTRUCTION *ip); static void dumpintlstr(const char *str, size_t len); static void dumpintlstr2(const char *str1, size_t len1, const char *str2, size_t len2); -static int isarray(NODE *n); static int include_source(INSTRUCTION *file); static void next_sourcefile(void); static char *tokexpand(void); @@ -63,6 +59,7 @@ static char *tokexpand(void); static INSTRUCTION *mk_program(void); static INSTRUCTION *append_rule(INSTRUCTION *pattern, INSTRUCTION *action); +static INSTRUCTION *mk_function(INSTRUCTION *fi, INSTRUCTION *def); static INSTRUCTION *mk_condition(INSTRUCTION *cond, INSTRUCTION *ifp, INSTRUCTION *true_branch, INSTRUCTION *elsep, INSTRUCTION *false_branch); static INSTRUCTION *mk_expression_list(INSTRUCTION *list, INSTRUCTION *s1); @@ -81,16 +78,13 @@ static void add_lint(INSTRUCTION *list, LINTTYPE linttype); enum defref { FUNC_DEFINE, FUNC_USE }; static void func_use(const char *name, enum defref how); static void check_funcs(void); -static void free_bcpool(INSTRUCTION *pl); static ssize_t read_one_line(int fd, void *buffer, size_t count); static int one_line_close(int fd); -static void (*install_func)(char *) = NULL; - static int want_source = FALSE; static int want_regexp; /* lexical scanning kludge */ -static int can_return; /* parsing kludge */ +static char *in_function; /* parsing kludge */ static int rule = 0; const char *const ruletab[] = { @@ -125,27 +119,17 @@ static int continue_allowed; /* kludge for continue */ #define END_SRC -2000 #define YYDEBUG_LEXER_TEXT (lexeme) -static int param_counter; -static NODE *func_params; /* list of parameters for the current function */ static char *tokstart = NULL; static char *tok = NULL; static char *tokend; static int errcount = 0; -static NODE *symbol_list; -extern void destroy_symbol(char *name); - -static long func_count; /* total number of functions */ - -#define HASHSIZE 1021 /* this constant only used here */ -NODE *variables[HASHSIZE]; -static int var_count; /* total number of global variables */ - extern char *source; extern int sourceline; extern SRCFILE *srcfiles; extern INSTRUCTION *rule_list; extern int max_args; +extern NODE **args_array; static INSTRUCTION *rule_block[sizeof(ruletab)]; @@ -162,16 +146,6 @@ static inline INSTRUCTION *list_prepend(INSTRUCTION *l, INSTRUCTION *x); static inline INSTRUCTION *list_merge(INSTRUCTION *l1, INSTRUCTION *l2); extern double fmod(double x, double y); -/* - * This string cannot occur as a real awk identifier. - * Use it as a special token to make function parsing - * uniform, but if it's seen, don't install the function. - * e.g. - * function split(x) { return x } - * function x(a) { return a } - * should only produce one error message, and not core dump. - */ -static char builtin_func[] = "@builtin"; #define YYSTYPE INSTRUCTION * %} @@ -256,10 +230,8 @@ rule } | function_prologue action { - can_return = FALSE; - if ($1 && func_install($1, $2) < 0) - YYABORT; - func_params = NULL; + in_function = NULL; + (void) mk_function($1, $2); yyerrok; } | '@' LEX_INCLUDE source statement_term @@ -297,7 +269,7 @@ pattern add_lint($4, LINT_assign_in_cond); tp = instruction(Op_no_op); - list_prepend($1, bcalloc(Op_line_range, !!do_profiling + 1, 0)); + list_prepend($1, bcalloc(Op_line_range, !!do_pretty_print + 1, 0)); $1->nexti->triggered = FALSE; $1->nexti->target_jmp = $4->nexti; @@ -308,7 +280,7 @@ pattern list_append($4, instruction(Op_cond_pair)); $4->lasti->line_range = $1->nexti; $4->lasti->target_jmp = tp; - if (do_profiling) { + if (do_pretty_print) { ($1->nexti + 1)->condpair_left = $1->lasti; ($1->nexti + 1)->condpair_right = $4->lasti; } @@ -369,13 +341,8 @@ func_name | lex_builtin { yyerror(_("`%s' is a built-in function, it cannot be redefined"), - tokstart); - $1->opcode = Op_symbol; /* Op_symbol instead of Op_token so that - * free_bc_internal does not try to free it - */ - $1->lextok = builtin_func; - $$ = $1; - /* yyerrok; */ + tokstart); + YYABORT; } | '@' LEX_EVAL { $$ = $2; } @@ -387,28 +354,17 @@ lex_builtin ; function_prologue - : LEX_FUNCTION + : LEX_FUNCTION func_name '(' opt_param_list r_paren opt_nls { - param_counter = 0; - func_params = NULL; + $1->source_file = source; + if (install_function($2->lextok, $1, $4) < 0) + YYABORT; + in_function = $2->lextok; + $2->lextok = NULL; + bcfree($2); + /* $4 already free'd in install_function */ + $$ = $1; } - func_name '(' opt_param_list r_paren opt_nls - { - NODE *t; - - $1->source_file = source; - t = make_param($3->lextok); - $3->lextok = NULL; - bcfree($3); - t->flags |= FUNC; - t->rnode = func_params; - func_params = t; - $$ = $1; - can_return = TRUE; - /* check for duplicate parameter names */ - if (dup_parms($1, t)) - errcount++; - } ; regexp @@ -425,6 +381,7 @@ regexp size_t len; re = $3->lextok; + $3->lextok = NULL; len = strlen(re); if (do_lint) { if (len == 0) @@ -486,7 +443,7 @@ statement { $$ = $2; } | if_statement { - if (do_profiling) + if (do_pretty_print) $$ = list_prepend($1, instruction(Op_exec_count)); else $$ = $1; @@ -550,7 +507,7 @@ statement else dflt->target_jmp = casestmt->nexti; - if (do_profiling) { + if (do_pretty_print) { curr->stmt_start = casestmt->nexti; curr->stmt_end = casestmt->lasti; (void) list_prepend(cexp, curr); @@ -565,7 +522,7 @@ statement efree(case_values); ip = $3; - if (do_profiling) { + if (do_pretty_print) { (void) list_prepend(ip, $1); (void) list_prepend(ip, instruction(Op_exec_count)); $1->target_break = tbreak; @@ -604,7 +561,7 @@ statement ip = list_append($3, instruction(Op_jmp_false)); ip->lasti->target_jmp = tbreak; - if (do_profiling) { + if (do_pretty_print) { (void) list_append(ip, instruction(Op_exec_count)); $1->target_break = tbreak; $1->target_continue = tcont; @@ -646,7 +603,7 @@ statement ip = list_merge($3, $6); else ip = list_prepend($6, instruction(Op_no_op)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(ip, instruction(Op_exec_count)); (void) list_append(ip, instruction(Op_jmp_true)); ip->lasti->target_jmp = ip->nexti; @@ -656,7 +613,7 @@ statement continue_allowed--; fix_break_continue(ip, tbreak, tcont); - if (do_profiling) { + if (do_pretty_print) { $1->target_break = tbreak; $1->target_continue = tcont; ($1 + 1)->doloop_cond = tcont; @@ -732,14 +689,14 @@ regular_loop: tbreak = instruction(Op_arrayfor_final); $4->opcode = Op_arrayfor_incr; - $4->array_var = variable(var_name, Node_var); + $4->array_var = variable($3->source_line, var_name, Node_var); $4->target_jmp = tbreak; tcont = $4; $3->opcode = Op_arrayfor_init; $3->target_jmp = tbreak; (void) list_append(ip, $3); - if (do_profiling) { + if (do_pretty_print) { $1->opcode = Op_K_arrayfor; $1->target_continue = tcont; $1->target_break = tbreak; @@ -760,7 +717,7 @@ regular_loop: ip->lasti->assign_var = $4->array_var->var_assign; } - if (do_profiling) { + if (do_pretty_print) { (void) list_append(ip, instruction(Op_exec_count)); ($1 + 1)->forloop_cond = $4; ($1 + 1)->forloop_body = ip->lasti; @@ -794,7 +751,7 @@ regular_loop: } | non_compound_stmt { - if (do_profiling) + if (do_pretty_print) $$ = list_prepend($1, instruction(Op_exec_count)); else $$ = $1; @@ -855,21 +812,33 @@ non_compound_stmt if ($2 == NULL) { $$ = list_create($1); (void) list_prepend($$, instruction(Op_push_i)); - $$->nexti->memory = Nnull_string; + $$->nexti->memory = dupnode(Nnull_string); } else $$ = list_append($2, $1); } | LEX_RETURN { - if (! can_return) + if (! in_function) yyerror(_("`return' used outside function context")); } opt_exp statement_term { if ($3 == NULL) { $$ = list_create($1); (void) list_prepend($$, instruction(Op_push_i)); - $$->nexti->memory = Nnull_string; - } else + $$->nexti->memory = dupnode(Nnull_string); + } else { + if (do_optimize > 1 + && $3->lasti->opcode == Op_func_call + && strcmp($3->lasti->func_name, in_function) == 0 + ) { + /* Do tail recursion optimization. Tail + * call without a return value is recognized + * in mk_function(). + */ + ($3->lasti + 1)->tail_call = TRUE; + } + $$ = list_append($3, $1); + } } | simple_stmt statement_term ; @@ -892,13 +861,13 @@ simple_stmt */ if ($1->opcode == Op_K_print && - ($3 == NULL - || ($3->lasti->opcode == Op_field_spec - && $3->nexti->nexti->nexti == $3->lasti - && $3->nexti->nexti->opcode == Op_push_i - && $3->nexti->nexti->memory->type == Node_val - && $3->nexti->nexti->memory->numbr == 0.0) - ) + ($3 == NULL + || ($3->lasti->opcode == Op_field_spec + && $3->nexti->nexti->nexti == $3->lasti + && $3->nexti->nexti->opcode == Op_push_i + && $3->nexti->nexti->memory->type == Node_val + && $3->nexti->nexti->memory->numbr == 0.0) + ) ) { static short warned = FALSE; /* ----------------- @@ -912,8 +881,6 @@ simple_stmt if ($3 != NULL) { bcfree($3->lasti); /* Op_field_spec */ - $3->nexti->nexti->memory->flags &= ~PERM; - $3->nexti->nexti->memory->flags |= MALLOC; unref($3->nexti->nexti->memory); /* Node_val */ bcfree($3->nexti->nexti); /* Op_push_i */ bcfree($3->nexti); /* Op_list */ @@ -984,7 +951,7 @@ simple_stmt char *arr = $2->lextok; $2->opcode = Op_push_array; - $2->memory = variable(arr, Node_var_new); + $2->memory = variable($2->source_line, arr, Node_var_new); if ($4 == NULL) { static short warned = FALSE; @@ -1022,7 +989,7 @@ simple_stmt error_ln($1->source_line, _("`delete array' is a gawk extension")); } - $3->memory = variable(arr, Node_var_new); + $3->memory = variable($3->source_line, arr, Node_var_new); $3->opcode = Op_push_array; $1->expr_count = 0; $$ = list_append(list_create($3), $1); @@ -1058,7 +1025,7 @@ case_statement INSTRUCTION *casestmt = $5; if ($5 == NULL) casestmt = list_create(instruction(Op_no_op)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(casestmt, instruction(Op_exec_count)); $1->case_exp = $2; $1->case_stmt = casestmt; @@ -1070,7 +1037,7 @@ case_statement INSTRUCTION *casestmt = $4; if ($4 == NULL) casestmt = list_create(instruction(Op_no_op)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(casestmt, instruction(Op_exec_count)); bcfree($2); $1->case_stmt = casestmt; @@ -1171,29 +1138,29 @@ input_redir opt_param_list : /* empty */ + { $$ = NULL; } | param_list + { $$ = $1 ; } ; param_list : NAME { - append_param($1->lextok); - $1->lextok = NULL; - bcfree($1); + $1->param_count = 0; + $$ = list_create($1); } | param_list comma NAME { - append_param($3->lextok); - $3->lextok = NULL; - bcfree($3); + $3->param_count = $1->lasti->param_count + 1; + $$ = list_append($1, $3); yyerrok; } | error - { /* func_params = NULL; */ } + { $$ = NULL; } | param_list error - { /* func_params = NULL; */ } + { $$ = $1; } | param_list comma error - { /* func_params = NULL; */ } + { $$ = $1; } ; /* optional expression, as in for loop */ @@ -1261,7 +1228,7 @@ exp | exp LEX_IN simple_variable { if (do_lint_old) - warning_ln($2->source_line, + warning_ln($2->source_line, _("old awk does not support the keyword `in' except after `for'")); $3->nexti->opcode = Op_push_array; $2->opcode = Op_in_array; @@ -1324,32 +1291,29 @@ common_exp $1->lasti->opcode = Op_no_op; } else { is_simple_var = ($1->nexti->opcode == Op_push - && $1->lasti == $1->nexti); /* first exp. is a simple - * variable?; kludge for use - * in Op_assign_concat. - */ + && $1->lasti == $1->nexti); /* first exp. is a simple + * variable?; kludge for use + * in Op_assign_concat. + */ } if (do_optimize > 1 - && $1->nexti == $1->lasti && $1->nexti->opcode == Op_push_i - && $2->nexti == $2->lasti && $2->nexti->opcode == Op_push_i + && $1->nexti == $1->lasti && $1->nexti->opcode == Op_push_i + && $2->nexti == $2->lasti && $2->nexti->opcode == Op_push_i ) { NODE *n1 = $1->nexti->memory; NODE *n2 = $2->nexti->memory; size_t nlen; - (void) force_string(n1); - (void) force_string(n2); + n1 = force_string(n1); + n2 = force_string(n2); nlen = n1->stlen + n2->stlen; erealloc(n1->stptr, char *, nlen + 2, "constant fold"); memcpy(n1->stptr + n1->stlen, n2->stptr, n2->stlen); n1->stlen = nlen; n1->stptr[nlen] = '\0'; - n1->flags &= ~(NUMCUR|NUMBER); + n1->flags &= ~(NUMCUR|NUMBER|NUMINT); n1->flags |= (STRING|STRCUR); - - n2->flags &= ~PERM; - n2->flags |= MALLOC; unref(n2); bcfree($2->nexti); bcfree($2); @@ -1467,12 +1431,12 @@ non_post_simp_exp if ($2->opcode == Op_match_rec) { $2->opcode = Op_nomatch; $1->opcode = Op_push_i; - $1->memory = mk_number(0.0, (PERM|NUMCUR|NUMBER)); + $1->memory = make_number(0.0); $$ = list_append(list_append(list_create($1), - instruction(Op_field_spec)), $2); + instruction(Op_field_spec)), $2); } else { if (do_optimize > 1 && $2->nexti == $2->lasti - && $2->nexti->opcode == Op_push_i + && $2->nexti->opcode == Op_push_i ) { NODE *n = $2->nexti->memory; if ((n->flags & (STRCUR|STRING)) != 0) { @@ -1543,7 +1507,8 @@ non_post_simp_exp | '-' simp_exp %prec UNARY { if ($2->lasti->opcode == Op_push_i - && ($2->lasti->memory->flags & (STRCUR|STRING)) == 0) { + && ($2->lasti->memory->flags & (STRCUR|STRING)) == 0 + ) { $2->lasti->memory->numbr = -(force_number($2->lasti->memory)); $$ = $2; bcfree($1); @@ -1559,7 +1524,7 @@ non_post_simp_exp * POSIX semantics: force a conversion to numeric type */ $1->opcode = Op_plus_i; - $1->memory = mk_number((AWKNUM) 0.0, (PERM|NUMCUR|NUMBER)); + $1->memory = make_number(0.0); $$ = list_append($2, $1); } ; @@ -1591,7 +1556,7 @@ func_call name = estrdup(f->func_name, strlen(f->func_name)); if (is_std_var(name)) yyerror(_("can not use special variable `%s' for indirect function call"), name); - indirect_var = variable(name, Node_var_new); + indirect_var = variable(f->source_line, name, Node_var_new); t = instruction(Op_push); t->memory = indirect_var; @@ -1671,7 +1636,7 @@ bracketed_exp_list _("invalid subscript expression")); /* install Null string as subscript. */ t = list_create(instruction(Op_push_i)); - t->nexti->memory = Nnull_string; + t->nexti->memory = dupnode(Nnull_string); $3->sub_count = 1; } else $3->sub_count = count_expressions(&t, FALSE); @@ -1699,17 +1664,13 @@ simple_variable char *var_name = $1->lextok; $1->opcode = Op_push; - $1->memory = variable(var_name, Node_var_new); + $1->memory = variable($1->source_line, var_name, Node_var_new); $$ = list_create($1); } | NAME subscript_list { - NODE *n; - char *arr = $1->lextok; - if ((n = lookup(arr)) != NULL && ! isarray(n)) - yyerror(_("use of non-array as array")); - $1->memory = variable(arr, Node_var_new); + $1->memory = variable($1->source_line, arr, Node_var_new); $1->opcode = Op_push_array; $$ = list_prepend($2, $1); } @@ -1720,8 +1681,8 @@ variable { INSTRUCTION *ip = $1->nexti; if (ip->opcode == Op_push - && ip->memory->type == Node_var - && ip->memory->var_update + && ip->memory->type == Node_var + && ip->memory->var_update ) { $$ = list_prepend($1, instruction(Op_var_update)); $$->nexti->update_var = ip->memory->var_update; @@ -1732,7 +1693,7 @@ variable { $$ = list_append($2, $1); if ($3 != NULL) - mk_assignment($2, NULL, $3); + mk_assignment($2, NULL, $3); } ; @@ -1823,9 +1784,12 @@ static const struct token tokentab[] = { {"END", Op_rule, LEX_END, 0, 0}, {"ENDFILE", Op_rule, LEX_ENDFILE, GAWKX, 0}, #ifdef ARRAYDEBUG -{"adump", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_adump}, +{"adump", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_adump}, #endif {"and", Op_builtin, LEX_BUILTIN, GAWKX|A(2), do_and}, +#ifdef ARRAYDEBUG +{"aoption", Op_builtin, LEX_BUILTIN, GAWKX|A(2), do_aoption}, +#endif {"asort", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asort}, {"asorti", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asorti}, {"atan2", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2), do_atan2}, @@ -1878,9 +1842,6 @@ static const struct token tokentab[] = { {"sprintf", Op_builtin, LEX_BUILTIN, 0, do_sprintf}, {"sqrt", Op_builtin, LEX_BUILTIN, A(1), do_sqrt}, {"srand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0)|A(1), do_srand}, -#if defined(GAWKDEBUG) || defined(ARRAYDEBUG) /* || ... */ -{"stopme", Op_builtin, LEX_BUILTIN, GAWKX|A(0), stopme}, -#endif {"strftime", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|A(1)|A(2)|A(3), do_strftime}, {"strtonum", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_strtonum}, {"sub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0}, @@ -1950,7 +1911,7 @@ print_included_from() line--; msg("%s %s:%d%c", s->prev == sourcefile ? "In file included from" - : " from", + : " from", (s->stype == SRC_INC || s->stype == SRC_FILE) ? s->src : "cmd. line", line, @@ -2232,7 +2193,10 @@ parse_program(INSTRUCTION **pcode) ip_atexit = instruction(Op_atexit); /* target for `exit' in END block */ } - sourcefile = srcfiles->next; + for (sourcefile = srcfiles->next; sourcefile->stype == SRC_EXTLIB; + sourcefile = sourcefile->next) + ; + lexeof = FALSE; lexptr = NULL; lasttok = 0; @@ -2249,6 +2213,11 @@ parse_program(INSTRUCTION **pcode) if (ret == 0) /* avoid spurious warning if parser aborted with YYABORT */ check_funcs(); + if (args_array == NULL) + emalloc(args_array, NODE **, (max_args + 2) * sizeof(NODE *), "parse_program"); + else + erealloc(args_array, NODE **, (max_args + 2) * sizeof(NODE *), "parse_program"); + return (ret || errcount); } @@ -2291,7 +2260,7 @@ add_srcfile(int stype, char *src, SRCFILE *thisfile, int *already_included, int if (stype == SRC_CMDLINE || stype == SRC_STDIN) return do_add_srcfile(stype, src, NULL, thisfile); - path = find_source(src, &sbuf, &errno_val); + path = find_source(src, & sbuf, &errno_val, stype == SRC_EXTLIB); if (path == NULL) { if (errcode) { *errcode = errno_val; @@ -2302,7 +2271,7 @@ add_srcfile(int stype, char *src, SRCFILE *thisfile, int *already_included, int } for (s = srcfiles->next; s != srcfiles; s = s->next) { - if ((s->stype == SRC_FILE || s->stype == SRC_INC) + if ((s->stype == SRC_FILE || s->stype == SRC_INC || s->stype == SRC_EXTLIB) && files_are_same(path, s) ) { if (do_lint) { @@ -2404,6 +2373,7 @@ next_sourcefile() * * assert(lexeof == TRUE); */ + lexeof = FALSE; eof_warned = FALSE; sourcefile->srclines = sourceline; /* total no of lines in current file */ @@ -2418,9 +2388,12 @@ next_sourcefile() sourcefile->lexptr_begin = NULL; } - sourcefile = sourcefile->next; - if (sourcefile == srcfiles) - return; + while ((sourcefile = sourcefile->next) != NULL) { + if (sourcefile == srcfiles) + return; + if (sourcefile->stype != SRC_EXTLIB) + break; + } if (sourcefile->lexptr_begin != NULL) { /* resume reading from already opened file (postponed to process '@include') */ @@ -2794,6 +2767,7 @@ yylex(void) char *tokkey; int inhex = FALSE; int intlstr = FALSE; + AWKNUM d; #define GET_INSTRUCTION(op) bcalloc(op, 1, sourceline) @@ -3232,9 +3206,7 @@ retry: yylval->opcode = Op_push_i; yylval->memory = make_str_node(tokstart, - tok - tokstart, esc_seen ? SCAN : 0); - yylval->memory->flags &= ~MALLOC; - yylval->memory->flags |= PERM; + tok - tokstart, esc_seen ? SCAN : 0); if (intlstr) { yylval->memory->flags |= INTLSTR; intlstr = FALSE; @@ -3376,10 +3348,12 @@ retry: lintwarn("numeric constant `%.*s' treated as hexadecimal", (int) strlen(tokstart)-1, tokstart); } - yylval->memory = mk_number(nondec2awknum(tokstart, strlen(tokstart)), - PERM|NUMCUR|NUMBER); + d = nondec2awknum(tokstart, strlen(tokstart)); } else - yylval->memory = mk_number(atof(tokstart), PERM|NUMCUR|NUMBER); + d = atof(tokstart); + yylval->memory = make_number(d); + if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d) + yylval->memory->flags |= NUMINT; return lasttok = YNUMBER; case '&': @@ -3516,7 +3490,7 @@ retry: case LEX_WHILE: case LEX_DO: case LEX_SWITCH: - if (! do_profiling) + if (! do_pretty_print) return lasttok = class; /* fall through */ case LEX_CASE: @@ -3556,23 +3530,6 @@ out: #undef NEWLINE_EOF } -/* mk_symbol --- allocates a symbol for the symbol table. */ - -NODE * -mk_symbol(NODETYPE type, NODE *value) -{ - NODE *r; - - getnode(r); - r->type = type; - r->flags = MALLOC; - r->lnode = value; - r->rnode = NULL; - r->parent_array = NULL; - r->var_assign = (Func_ptr) 0; - return r; -} - /* snode --- instructions for builtin functions. Checks for arg. count and supplies defaults where possible. */ @@ -3624,7 +3581,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) INSTRUCTION *expr; expr = list_create(instruction(Op_push_i)); - expr->nexti->memory = mk_number((AWKNUM) 0.0, (PERM|NUMCUR|NUMBER)); + expr->nexti->memory = make_number(0.0); (void) mk_expression_list(subn, list_append(expr, instruction(Op_field_spec))); } @@ -3668,7 +3625,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) r->sub_flags |= GENSUB; if (nexp == 3) { ip = instruction(Op_push_i); - ip->memory = mk_number((AWKNUM) 0.0, (PERM|NUMCUR|NUMBER)); + ip->memory = make_number(0.0); (void) mk_expression_list(subn, list_append(list_create(ip), instruction(Op_field_spec))); } @@ -3691,7 +3648,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) list = list_create(r); (void) list_prepend(list, instruction(Op_field_spec)); (void) list_prepend(list, instruction(Op_push_i)); - list->nexti->memory = mk_number((AWKNUM) 0.0, (PERM|NUMCUR|NUMBER)); + list->nexti->memory = make_number(0.0); return list; } else { arg = subn->nexti; @@ -3819,7 +3776,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) if (ip->opcode == Op_push) ip->opcode = Op_push_array; } -#endif +#endif if (subn != NULL) { r->expr_count = count_expressions(&subn, FALSE); @@ -3830,75 +3787,6 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) return list_create(r); } -/* append_param --- append PNAME to the list of parameters - * for the current function. - */ - -static void -append_param(char *pname) -{ - static NODE *savetail = NULL; - NODE *p; - - p = make_param(pname); - if (func_params == NULL) { - func_params = p; - savetail = p; - } else if (savetail != NULL) { - savetail->rnode = p; - savetail = p; - } -} - -/* dup_parms --- return TRUE if there are duplicate parameters */ - -static int -dup_parms(INSTRUCTION *fp, NODE *func) -{ - NODE *np; - const char *fname, **names; - int count, i, j, dups; - NODE *params; - - if (func == NULL) /* error earlier */ - return TRUE; - - fname = func->param; - count = func->param_cnt; - params = func->rnode; - - if (count == 0) /* no args, no problem */ - return FALSE; - - if (params == NULL) /* error earlier */ - return TRUE; - - emalloc(names, const char **, count * sizeof(char *), "dup_parms"); - - i = 0; - for (np = params; np != NULL; np = np->rnode) { - if (np->param == NULL) { /* error earlier, give up, go home */ - efree(names); - return TRUE; - } - names[i++] = np->param; - } - - dups = 0; - for (i = 1; i < count; i++) { - for (j = 0; j < i; j++) { - if (strcmp(names[i], names[j]) == 0) { - dups++; - error_ln(fp->source_line, - _("function `%s': parameter #%d, `%s', duplicates parameter #%d"), - fname, i + 1, names[j], j+1); - } - } - } - - efree(names); - return (dups > 0 ? TRUE : FALSE); -} /* parms_shadow --- check if parameters shadow globals */ @@ -3907,18 +3795,19 @@ parms_shadow(INSTRUCTION *pc, int *shadow) { int pcount, i; int ret = FALSE; - NODE *func; + NODE *func, *fp; char *fname; func = pc->func_body; - fname = func->lnode->param; - + fname = func->vname; + fp = func->fparms; + #if 0 /* can't happen, already exited if error ? */ if (fname == NULL || func == NULL) /* error earlier */ return FALSE; #endif - pcount = func->lnode->param_cnt; + pcount = func->param_cnt; if (pcount == 0) /* no args, no problem */ return 0; @@ -3930,10 +3819,10 @@ parms_shadow(INSTRUCTION *pc, int *shadow) * about all shadowed parameters. */ for (i = 0; i < pcount; i++) { - if (lookup(func->parmlist[i]) != NULL) { + if (lookup(fp[i].param) != NULL) { warning( _("function `%s': parameter `%s' shadows global variable"), - fname, func->parmlist[i]); + fname, fp[i].param); ret = TRUE; } } @@ -3942,80 +3831,10 @@ parms_shadow(INSTRUCTION *pc, int *shadow) return 0; } - -/* - * install_symbol: - * Install a name in the symbol table, even if it is already there. - * Caller must check against redefinition if that is desired. - */ - - -NODE * -install_symbol(char *name, NODE *value) -{ - NODE *hp; - size_t len; - int bucket; - - if (install_func) - (*install_func)(name); - - var_count++; - len = strlen(name); - bucket = hash(name, len, (unsigned long) HASHSIZE, NULL); - getnode(hp); - hp->type = Node_hashnode; - hp->hnext = variables[bucket]; - variables[bucket] = hp; - hp->hlength = len; - hp->hvalue = value; - hp->hname = name; - hp->hvalue->vname = name; - return hp->hvalue; -} - -/* lookup --- find the most recent hash node for name installed by install_symbol */ - -NODE * -lookup(const char *name) -{ - NODE *bucket; - size_t len; - - len = strlen(name); - for (bucket = variables[hash(name, len, (unsigned long) HASHSIZE, NULL)]; - bucket != NULL; bucket = bucket->hnext) - if (bucket->hlength == len && strncmp(bucket->hname, name, len) == 0) - return bucket->hvalue; - return NULL; -} - -/* sym_comp --- compare two symbol (variable or function) names */ - -static int -sym_comp(const void *v1, const void *v2) -{ - const NODE *const *npp1, *const *npp2; - const NODE *n1, *n2; - int minlen; - - npp1 = (const NODE *const *) v1; - npp2 = (const NODE *const *) v2; - n1 = *npp1; - n2 = *npp2; - - if (n1->hlength > n2->hlength) - minlen = n1->hlength; - else - minlen = n2->hlength; - - return strncmp(n1->hname, n2->hname, minlen); -} - /* valinfo --- dump var info */ void -valinfo(NODE *n, int (*print_func)(FILE *, const char *, ...), FILE *fp) +valinfo(NODE *n, Func_print print_func, FILE *fp) { if (n == Nnull_string) print_func(fp, "uninitialized scalar\n"); @@ -4033,52 +3852,6 @@ valinfo(NODE *n, int (*print_func)(FILE *, const char *, ...), FILE *fp) print_func(fp, "?? flags %s\n", flags2str(n->flags)); } -/* get_varlist --- list of global variables */ - -NODE ** -get_varlist() -{ - int i, j; - NODE **table; - NODE *p; - - emalloc(table, NODE **, (var_count + 1) * sizeof(NODE *), "get_varlist"); - update_global_values(); - for (i = j = 0; i < HASHSIZE; i++) - for (p = variables[i]; p != NULL; p = p->hnext) - table[j++] = p; - assert(j == var_count); - - /* Shazzam! */ - qsort(table, j, sizeof(NODE *), sym_comp); - - table[j] = NULL; - return table; -} - -/* print_vars --- print names and values of global variables */ - -void -print_vars(int (*print_func)(FILE *, const char *, ...), FILE *fp) -{ - int i; - NODE **table; - NODE *p; - - table = get_varlist(); - for (i = 0; (p = table[i]) != NULL; i++) { - if (p->hvalue->type == Node_func) - continue; - print_func(fp, "%.*s: ", (int) p->hlength, p->hname); - if (p->hvalue->type == Node_var_array) - print_func(fp, "array, %ld elements\n", p->hvalue->table_size); - else if (p->hvalue->type == Node_var_new) - print_func(fp, "untyped variable\n"); - else if (p->hvalue->type == Node_var) - valinfo(p->hvalue->var_value, print_func, fp); - } - efree(table); -} /* dump_vars --- dump the symbol table */ @@ -4086,6 +3859,7 @@ void dump_vars(const char *fname) { FILE *fp; + NODE **vars; if (fname == NULL) fp = stderr; @@ -4095,48 +3869,25 @@ dump_vars(const char *fname) fp = stderr; } - print_vars(fprintf, fp); + vars = variable_list(); + print_vars(vars, fprintf, fp); + efree(vars); if (fp != stderr && fclose(fp) != 0) warning(_("%s: close failed (%s)"), fname, strerror(errno)); } -/* release_all_vars --- free all variable memory */ - -void -release_all_vars() -{ - int i; - NODE *p, *next; - - for (i = 0; i < HASHSIZE; i++) { - for (p = variables[i]; p != NULL; p = next) { - next = p->hnext; - - if (p->hvalue->type == Node_func) - continue; - else if (p->hvalue->type == Node_var_array) - assoc_clear(p->hvalue); - else if (p->hvalue->type != Node_var_new) - unref(p->hvalue->var_value); - - efree(p->hname); - freenode(p->hvalue); - freenode(p); - } - } -} - /* dump_funcs --- print all functions */ void dump_funcs() { - if (func_count <= 0) - return; - - (void) foreach_func((int (*)(INSTRUCTION *, void *)) pp_func, TRUE, (void *) 0); + NODE **funcs; + funcs = function_list(TRUE); + (void) foreach_func(funcs, (int (*)(INSTRUCTION *, void *)) pp_func, (void *) 0); + efree(funcs); } + /* shadow_funcs --- check all functions for parameters that shadow globals */ void @@ -4144,175 +3895,164 @@ shadow_funcs() { static int calls = 0; int shadow = FALSE; - - if (func_count <= 0) - return; + NODE **funcs; if (calls++ != 0) fatal(_("shadow_funcs() called twice!")); - (void) foreach_func((int (*)(INSTRUCTION *, void *)) parms_shadow, TRUE, &shadow); + funcs = function_list(TRUE); + (void) foreach_func(funcs, (int (*)(INSTRUCTION *, void *)) parms_shadow, & shadow); + efree(funcs); /* End with fatal if the user requested it. */ if (shadow && lintfunc != warning) lintwarn(_("there were shadowed variables.")); } -/* - * func_install: - * check if name is already installed; if so, it had better have Null value, - * in which case def is added as the value. Otherwise, install name with def - * as value. - * - * Extra work, build up and save a list of the parameter names in a table - * and hang it off params->parmlist. This is used to set the `vname' field - * of each function parameter during a function call. See eval.c. + +/* mk_function --- finalize function definition node; remove parameters + * out of the symbol table. */ -static int -func_install(INSTRUCTION *func, INSTRUCTION *def) +static INSTRUCTION * +mk_function(INSTRUCTION *fi, INSTRUCTION *def) { - NODE *params; - NODE *r, *n, *thisfunc, *hp; - char **pnames = NULL; - char *fname; - int pcount = 0; - int i; + NODE *thisfunc; - params = func_params; + thisfunc = fi->func_body; + assert(thisfunc != NULL); - /* check for function foo(foo) { ... }. bleah. */ - for (n = params->rnode; n != NULL; n = n->rnode) { - if (strcmp(n->param, params->param) == 0) { - error_ln(func->source_line, - _("function `%s': can't use function name as parameter name"), params->param); - return -1; - } else if (is_std_var(n->param)) { - error_ln(func->source_line, - _("function `%s': can't use special variable `%s' as a function parameter"), - params->param, n->param); - return -1; - } - } + if (do_optimize > 1 && def->lasti->opcode == Op_pop) { + /* tail call which does not return any value. */ - thisfunc = NULL; /* turn off warnings */ + INSTRUCTION *t; - fname = params->param; - /* symbol table management */ - hp = remove_symbol(params->param); /* remove function name out of symbol table */ - if (hp != NULL) - freenode(hp); - r = lookup(fname); - if (r != NULL) { - error_ln(func->source_line, - _("function name `%s' previously defined"), fname); - return -1; - } else if (fname == builtin_func) /* not a valid function name */ - goto remove_params; + for (t = def->nexti; t->nexti != def->lasti; t = t->nexti) + ; + if (t->opcode == Op_func_call + && strcmp(t->func_name, thisfunc->vname) == 0) + (t + 1)->tail_call = TRUE; + } /* add an implicit return at end; * also used by 'return' command in debugger */ - + (void) list_append(def, instruction(Op_push_i)); - def->lasti->memory = Nnull_string; + def->lasti->memory = dupnode(Nnull_string); (void) list_append(def, instruction(Op_K_return)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(def, instruction(Op_exec_count)); - /* func->opcode is Op_func */ - (func + 1)->firsti = def->nexti; - (func + 1)->lasti = def->lasti; - (func + 2)->first_line = func->source_line; - (func + 2)->last_line = lastline; - - func->nexti = def->nexti; + /* fi->opcode = Op_func */ + (fi + 1)->firsti = def->nexti; + (fi + 1)->lasti = def->lasti; + (fi + 2)->first_line = fi->source_line; + (fi + 2)->last_line = lastline; + fi->nexti = def->nexti; bcfree(def); - (void) list_append(rule_list, func + 1); /* debugging */ - - /* install the function */ - thisfunc = mk_symbol(Node_func, params); - (void) install_symbol(fname, thisfunc); - thisfunc->code_ptr = func; - func->func_body = thisfunc; - - for (n = params->rnode; n != NULL; n = n->rnode) - pcount++; - - if (pcount != 0) { - emalloc(pnames, char **, (pcount + 1) * sizeof(char *), "func_install"); - for (i = 0, n = params->rnode; i < pcount; i++, n = n->rnode) - pnames[i] = n->param; - pnames[pcount] = NULL; - } - thisfunc->parmlist = pnames; + (void) list_append(rule_list, fi + 1); /* debugging */ /* update lint table info */ - func_use(fname, FUNC_DEFINE); - - func_count++; /* used in profiler / pretty printer */ + func_use(thisfunc->vname, FUNC_DEFINE); -remove_params: /* remove params from symbol table */ - pop_params(params->rnode); - return 0; + remove_params(thisfunc); + return fi; } -/* remove_symbol --- remove a variable from the symbol table */ +/* + * install_function: + * install function name in the symbol table. + * Extra work, build up and install a list of the parameter names. + */ -NODE * -remove_symbol(char *name) +static int +install_function(char *fname, INSTRUCTION *fi, INSTRUCTION *plist) { - NODE *bucket, **save; - size_t len; + NODE *r, *f; + int pcount = 0; - len = strlen(name); - save = &(variables[hash(name, len, (unsigned long) HASHSIZE, NULL)]); - for (bucket = *save; bucket != NULL; bucket = bucket->hnext) { - if (len == bucket->hlength && strncmp(bucket->hname, name, len) == 0) { - var_count--; - *save = bucket->hnext; - return bucket; - } - save = &(bucket->hnext); + r = lookup(fname); + if (r != NULL) { + error_ln(fi->source_line, _("function name `%s' previously defined"), fname); + return -1; } - return NULL; + + if (plist != NULL) + pcount = plist->lasti->param_count + 1; + f = install_symbol(fname, Node_func); + fi->func_body = f; + f->param_cnt = pcount; + f->code_ptr = fi; + f->fparms = NULL; + if (pcount > 0) { + char **pnames; + pnames = check_params(fname, pcount, plist); /* frees plist */ + f->fparms = make_params(pnames, pcount); + efree(pnames); + install_params(f); + } + return 0; } -/* pop_params --- remove list of function parameters from symbol table */ -/* - * pop parameters out of the symbol table. do this in reverse order to - * avoid reading freed memory if there were duplicated parameters. +/* check_params --- build a list of function parameter names after + * making sure that the names are valid and there are no duplicates. */ -static void -pop_params(NODE *params) + +static char ** +check_params(char *fname, int pcount, INSTRUCTION *list) { - NODE *hp; - if (params == NULL) - return; - pop_params(params->rnode); - hp = remove_symbol(params->param); - if (hp != NULL) - freenode(hp); -} + INSTRUCTION *p, *np; + int i, j; + char *name; + char **pnames; -/* make_param --- make NAME into a function parameter */ + assert(pcount > 0); -static NODE * -make_param(char *name) -{ - NODE *r; + emalloc(pnames, char **, pcount * sizeof(char *), "check_params"); + + for (i = 0, p = list->nexti; p != NULL; i++, p = np) { + np = p->nexti; + name = p->lextok; + p->lextok = NULL; + + if (strcmp(name, fname) == 0) { + /* check for function foo(foo) { ... }. bleah. */ + error_ln(p->source_line, + _("function `%s': can't use function name as parameter name"), fname); + } else if (is_std_var(name)) { + error_ln(p->source_line, + _("function `%s': can't use special variable `%s' as a function parameter"), + fname, name); + } - getnode(r); - r->type = Node_param_list; - r->rnode = NULL; - r->param_cnt = param_counter++; - return (install_symbol(name, r)); + /* check for duplicate parameters */ + for (j = 0; j < i; j++) { + if (strcmp(name, pnames[j]) == 0) { + error_ln(p->source_line, + _("function `%s': parameter #%d, `%s', duplicates parameter #%d"), + fname, i + 1, name, j + 1); + } + } + + pnames[i] = name; + bcfree(p); + } + bcfree(list); + + return pnames; } + +#ifdef HASHSIZE +undef HASHSIZE +#endif +#define HASHSIZE 1021 + static struct fdesc { char *name; short used; @@ -4420,69 +4160,6 @@ param_sanity(INSTRUCTION *arglist) } } -/* foreach_func --- execute given function for each awk function in symbol table. */ - -int -foreach_func(int (*pfunc)(INSTRUCTION *, void *), int sort, void *data) -{ - int i, j; - NODE *p; - int ret = 0; - - if (sort) { - NODE **tab; - - /* - * Walk through symbol table counting functions. - * Could be more than func_count if there are - * extension functions. - */ - for (i = j = 0; i < HASHSIZE; i++) { - for (p = variables[i]; p != NULL; p = p->hnext) { - if (p->hvalue->type == Node_func) { - j++; - } - } - } - - if (j == 0) - return 0; - - emalloc(tab, NODE **, j * sizeof(NODE *), "foreach_func"); - - /* now walk again, copying info */ - for (i = j = 0; i < HASHSIZE; i++) { - for (p = variables[i]; p != NULL; p = p->hnext) { - if (p->hvalue->type == Node_func) { - tab[j] = p; - j++; - } - } - } - - /* Shazzam! */ - qsort(tab, j, sizeof(NODE *), sym_comp); - - for (i = 0; i < j; i++) { - if ((ret = pfunc(tab[i]->hvalue->code_ptr, data)) != 0) - break; - } - - efree(tab); - return ret; - } - - /* unsorted */ - for (i = 0; i < HASHSIZE; i++) { - for (p = variables[i]; p != NULL; p = p->hnext) { - if (p->hvalue->type == Node_func - && (ret = pfunc(p->hvalue->code_ptr, data)) != 0) - return ret; - } - } - return 0; -} - /* deferred variables --- those that are only defined if needed. */ /* @@ -4517,17 +4194,14 @@ register_deferred_variable(const char *name, NODE *(*load_func)(void)) /* variable --- make sure NAME is in the symbol table */ NODE * -variable(char *name, NODETYPE type) +variable(int location, char *name, NODETYPE type) { NODE *r; if ((r = lookup(name)) != NULL) { - if (r->type == Node_func) { - error(_("function `%s' called with space between name and `(',\nor used as a variable or an array"), + if (r->type == Node_func || r->type == Node_ext_func ) + error_ln(location, _("function `%s' called with space between name and `(',\nor used as a variable or an array"), r->vname); - errcount++; - r->type = Node_var_new; /* continue parsing instead of exiting */ - } } else { /* not found */ struct deferred_variable *dv; @@ -4537,11 +4211,7 @@ variable(char *name, NODETYPE type) /* * This is the only case in which we may not free the string. */ - if (type == Node_var) - r = mk_symbol(type, Nnull_string); - else - r = mk_symbol(type, (NODE *) NULL); - return install_symbol(name, r); + return install_symbol(name, type); } if (strcmp(name, dv->name) == 0) { r = (*dv->load_func)(); @@ -4648,9 +4318,6 @@ make_assignable(INSTRUCTION *ip) { switch (ip->opcode) { case Op_push: - if (ip->memory->type == Node_param_list - && (ip->memory->flags & FUNC) != 0) - return NULL; ip->opcode = Op_push_lhs; return ip; case Op_field_spec: @@ -4665,14 +4332,6 @@ make_assignable(INSTRUCTION *ip) return NULL; } -/* stopme --- for debugging */ - -NODE * -stopme(int nargs ATTRIBUTE_UNUSED) -{ - return (NODE *) 0; -} - /* dumpintlstr --- write out an initial .po file entry for the string */ static void @@ -4722,27 +4381,6 @@ dumpintlstr2(const char *str1, size_t len1, const char *str2, size_t len2) fflush(stdout); } -/* isarray --- can this type be subscripted? */ - -static int -isarray(NODE *n) -{ - switch (n->type) { - case Node_var_new: - case Node_var_array: - return TRUE; - case Node_param_list: - return (n->flags & FUNC) == 0; - case Node_array_ref: - cant_happen(); - break; - default: - break; /* keeps gcc -Wall happy */ - } - - return FALSE; -} - /* mk_binary --- instructions for binary operators */ static INSTRUCTION * @@ -4803,11 +4441,7 @@ mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op) } op->opcode = Op_push_i; - op->memory = mk_number(res, (PERM|NUMCUR|NUMBER)); - n1->flags &= ~PERM; - n1->flags |= MALLOC; - n2->flags &= ~PERM; - n2->flags |= MALLOC; + op->memory = make_number(res); unref(n1); unref(n2); bcfree(ip1); @@ -4933,7 +4567,7 @@ mk_condition(INSTRUCTION *cond, INSTRUCTION *ifp, INSTRUCTION *true_branch, if (false_branch == NULL) { false_branch = list_create(instruction(Op_no_op)); if (elsep != NULL) { /* else { } */ - if (do_profiling) + if (do_pretty_print) (void) list_prepend(false_branch, elsep); else bcfree(elsep); @@ -4944,7 +4578,7 @@ mk_condition(INSTRUCTION *cond, INSTRUCTION *ifp, INSTRUCTION *true_branch, /* avoid a series of no_op's: if .. else if .. else if .. */ if (false_branch->lasti->opcode != Op_no_op) (void) list_append(false_branch, instruction(Op_no_op)); - if (do_profiling) { + if (do_pretty_print) { (void) list_prepend(false_branch, elsep); false_branch->nexti->branch_end = false_branch->lasti; (void) list_prepend(false_branch, instruction(Op_exec_count)); @@ -4959,7 +4593,7 @@ mk_condition(INSTRUCTION *cond, INSTRUCTION *ifp, INSTRUCTION *true_branch, ip = list_append(cond, instruction(Op_jmp_false)); ip->lasti->target_jmp = false_branch->nexti->nexti; - if (do_profiling) { + if (do_pretty_print) { (void) list_prepend(ip, ifp); (void) list_append(ip, instruction(Op_exec_count)); ip->nexti->branch_if = ip->lasti; @@ -5021,7 +4655,7 @@ append_rule(INSTRUCTION *pattern, INSTRUCTION *action) if (rule != Rule) { rp = pattern; - if (do_profiling) + if (do_pretty_print) (void) list_append(action, instruction(Op_no_op)); (rp + 1)->firsti = action->nexti; (rp + 1)->lasti = action->lasti; @@ -5037,7 +4671,7 @@ append_rule(INSTRUCTION *pattern, INSTRUCTION *action) if (pattern == NULL) { /* assert(action != NULL); */ - if (do_profiling) + if (do_pretty_print) (void) list_prepend(action, instruction(Op_exec_count)); (rp + 1)->firsti = action->nexti; (rp + 1)->lasti = tp; @@ -5053,12 +4687,12 @@ append_rule(INSTRUCTION *pattern, INSTRUCTION *action) if (action == NULL) { (rp + 2)->last_line = find_line(pattern, LAST_LINE); action = list_create(instruction(Op_K_print_rec)); - if (do_profiling) + if (do_pretty_print) (void) list_prepend(action, instruction(Op_exec_count)); } else (rp + 2)->last_line = lastline; - if (do_profiling) { + if (do_pretty_print) { (void) list_prepend(pattern, instruction(Op_exec_count)); (void) list_prepend(action, instruction(Op_exec_count)); } @@ -5138,9 +4772,7 @@ mk_assignment(INSTRUCTION *lhs, INSTRUCTION *rhs, INSTRUCTION *op) static INSTRUCTION * optimize_assignment(INSTRUCTION *exp) { - INSTRUCTION *i1; - INSTRUCTION *i2; - INSTRUCTION *i3; + INSTRUCTION *i1, *i2, *i3; /* * Optimize assignment statements array[subs] = x; var = x; $n = x; @@ -5176,7 +4808,7 @@ optimize_assignment(INSTRUCTION *exp) if ( ! do_optimize || ( i1->opcode != Op_assign && i1->opcode != Op_field_assign) - ) + ) return list_append(exp, instruction(Op_pop)); for (i2 = exp->nexti; i2 != i1; i2 = i2->nexti) { @@ -5261,13 +4893,26 @@ optimize_assignment(INSTRUCTION *exp) case Op_push_lhs: if (i2->nexti == i1 - && i1->opcode == Op_assign + && i1->opcode == Op_assign ) { /* var = .. */ i2->opcode = Op_store_var; i2->nexti = NULL; bcfree(i1); /* Op_assign */ exp->lasti = i2; /* update Op_list */ + + i3 = exp->nexti; + if (i3->opcode == Op_push_i + && (i3->memory->flags & INTLSTR) == 0 + && i3->nexti == i2 + ) { + /* constant initializer */ + i2->initval = i3->memory; + bcfree(i3); + exp->nexti = i2; + } else + i2->initval = NULL; + return exp; } break; @@ -5388,7 +5033,7 @@ mk_for_loop(INSTRUCTION *forp, INSTRUCTION *init, INSTRUCTION *cond, if (init != NULL) ip = list_merge(init, ip); - if (do_profiling) { + if (do_pretty_print) { (void) list_append(ip, instruction(Op_exec_count)); (forp + 1)->forloop_cond = pp_cond; (forp + 1)->forloop_body = ip->lasti; @@ -5410,7 +5055,7 @@ mk_for_loop(INSTRUCTION *forp, INSTRUCTION *init, INSTRUCTION *cond, ret = list_append(ip, tbreak); fix_break_continue(ret, tbreak, tcont); - if (do_profiling) { + if (do_pretty_print) { forp->target_break = tbreak; forp->target_continue = tcont; ret = list_prepend(ret, forp); @@ -5569,320 +5214,6 @@ fix_break_continue(INSTRUCTION *list, INSTRUCTION *b_target, INSTRUCTION *c_targ } } - -/* append_symbol --- append symbol to the list of symbols - * installed in the symbol table. - */ - -void -append_symbol(char *name) -{ - NODE *hp; - - /* N.B.: func_install removes func name and reinstalls it; - * and we get two entries for it here!. destroy_symbol() - * will find and destroy the Node_func which is what we want. - */ - - getnode(hp); - hp->hname = name; /* shallow copy */ - hp->hnext = symbol_list->hnext; - symbol_list->hnext = hp; -} - -/* release_symbol --- free symbol list and optionally remove symbol from symbol table */ - -void -release_symbols(NODE *symlist, int keep_globals) -{ - NODE *hp, *n; - - for (hp = symlist->hnext; hp != NULL; hp = n) { - if (! keep_globals) { - /* destroys globals, function, and params - * if still in symbol table and not removed by func_install - * due to parse error. - */ - destroy_symbol(hp->hname); - } - n = hp->hnext; - freenode(hp); - } - symlist->hnext = NULL; -} - -/* destroy_symbol --- remove a symbol from symbol table -* and free all associated memory. -*/ - -void -destroy_symbol(char *name) -{ - NODE *symbol, *hp; - - symbol = lookup(name); - if (symbol == NULL) - return; - - if (symbol->type == Node_func) { - char **varnames; - NODE *func, *n; - - func = symbol; - varnames = func->parmlist; - if (varnames != NULL) - efree(varnames); - - /* function parameters of type Node_param_list */ - for (n = func->lnode->rnode; n != NULL; ) { - NODE *np; - np = n->rnode; - efree(n->param); - freenode(n); - n = np; - } - freenode(func->lnode); - func_count--; - - } else if (symbol->type == Node_var_array) - assoc_clear(symbol); - else if (symbol->type == Node_var) - unref(symbol->var_value); - - /* remove from symbol table */ - hp = remove_symbol(name); - efree(hp->hname); - freenode(hp->hvalue); - freenode(hp); -} - -#define pool_size d.dl -#define freei x.xi -static INSTRUCTION *pool_list; -static AWK_CONTEXT *curr_ctxt = NULL; - -/* new_context --- create a new execution context. */ - -AWK_CONTEXT * -new_context() -{ - AWK_CONTEXT *ctxt; - - emalloc(ctxt, AWK_CONTEXT *, sizeof(AWK_CONTEXT), "new_context"); - memset(ctxt, 0, sizeof(AWK_CONTEXT)); - ctxt->srcfiles.next = ctxt->srcfiles.prev = &ctxt->srcfiles; - ctxt->rule_list.opcode = Op_list; - ctxt->rule_list.lasti = &ctxt->rule_list; - return ctxt; -} - -/* set_context --- change current execution context. */ - -static void -set_context(AWK_CONTEXT *ctxt) -{ - pool_list = &ctxt->pools; - symbol_list = &ctxt->symbols; - srcfiles = &ctxt->srcfiles; - rule_list = &ctxt->rule_list; - install_func = ctxt->install_func; - curr_ctxt = ctxt; -} - -/* - * push_context: - * - * Switch to the given context after saving the current one. The set - * of active execution contexts forms a stack; the global or main context - * is at the bottom of the stack. - */ - -void -push_context(AWK_CONTEXT *ctxt) -{ - ctxt->prev = curr_ctxt; - /* save current source and sourceline */ - if (curr_ctxt != NULL) { - curr_ctxt->sourceline = sourceline; - curr_ctxt->source = source; - } - sourceline = 0; - source = NULL; - set_context(ctxt); -} - -/* pop_context --- switch to previous execution context. */ - -void -pop_context() -{ - AWK_CONTEXT *ctxt; - - assert(curr_ctxt != NULL); - ctxt = curr_ctxt->prev; - /* restore source and sourceline */ - sourceline = ctxt->sourceline; - source = ctxt->source; - set_context(ctxt); -} - -/* in_main_context --- are we in the main context ? */ - -int -in_main_context() -{ - assert(curr_ctxt != NULL); - return (curr_ctxt->prev == NULL); -} - -/* free_context --- free context structure and related data. */ - -void -free_context(AWK_CONTEXT *ctxt, int keep_globals) -{ - SRCFILE *s, *sn; - - if (ctxt == NULL) - return; - - assert(curr_ctxt != ctxt); - - /* free all code including function codes */ - free_bcpool(&ctxt->pools); - /* free symbols */ - release_symbols(&ctxt->symbols, keep_globals); - /* free srcfiles */ - for (s = &ctxt->srcfiles; s != &ctxt->srcfiles; s = sn) { - sn = s->next; - if (s->stype != SRC_CMDLINE && s->stype != SRC_STDIN) - efree(s->fullpath); - efree(s->src); - efree(s); - } - efree(ctxt); -} - -/* free_bc_internal --- free internal memory of an instruction. */ - -static void -free_bc_internal(INSTRUCTION *cp) -{ - NODE *m; - - switch(cp->opcode) { - case Op_func_call: - if (cp->func_name != NULL - && cp->func_name != builtin_func - ) - efree(cp->func_name); - break; - case Op_push_re: - case Op_match_rec: - case Op_match: - case Op_nomatch: - m = cp->memory; - if (m->re_reg != NULL) - refree(m->re_reg); - if (m->re_exp != NULL) - unref(m->re_exp); - if (m->re_text != NULL) - unref(m->re_text); - freenode(m); - break; - case Op_token: /* token lost during error recovery in yyparse */ - if (cp->lextok != NULL) - efree(cp->lextok); - break; - case Op_illegal: - cant_happen(); - default: - break; - } -} - - -/* INSTR_CHUNK must be > largest code size (3) */ -#define INSTR_CHUNK 127 - -/* bcfree --- deallocate instruction */ - -void -bcfree(INSTRUCTION *cp) -{ - cp->opcode = 0; - cp->nexti = pool_list->freei; - pool_list->freei = cp; -} - -/* bcalloc --- allocate a new instruction */ - -INSTRUCTION * -bcalloc(OPCODE op, int size, int srcline) -{ - INSTRUCTION *cp; - - if (size > 1) { - /* wide instructions Op_rule, Op_func_call .. */ - emalloc(cp, INSTRUCTION *, (size + 1) * sizeof(INSTRUCTION), "bcalloc"); - cp->pool_size = size; - cp->nexti = pool_list->nexti; - pool_list->nexti = cp++; - } else { - INSTRUCTION *pool; - - pool = pool_list->freei; - if (pool == NULL) { - INSTRUCTION *last; - emalloc(cp, INSTRUCTION *, (INSTR_CHUNK + 1) * sizeof(INSTRUCTION), "bcalloc"); - - cp->pool_size = INSTR_CHUNK; - cp->nexti = pool_list->nexti; - pool_list->nexti = cp; - pool = ++cp; - last = &pool[INSTR_CHUNK - 1]; - for (; cp <= last; cp++) { - cp->opcode = 0; - cp->nexti = cp + 1; - } - --cp; - cp->nexti = NULL; - } - cp = pool; - pool_list->freei = cp->nexti; - } - - memset(cp, 0, size * sizeof(INSTRUCTION)); - cp->opcode = op; - cp->source_line = srcline; - return cp; -} - -/* free_bcpool --- free list of instruction memory pools */ - -static void -free_bcpool(INSTRUCTION *pl) -{ - INSTRUCTION *pool, *tmp; - - for (pool = pl->nexti; pool != NULL; pool = tmp) { - INSTRUCTION *cp, *last; - long psiz; - psiz = pool->pool_size; - if (psiz == INSTR_CHUNK) - last = pool + psiz; - else - last = pool + 1; - for (cp = pool + 1; cp <= last ; cp++) { - if (cp->opcode != 0) - free_bc_internal(cp); - } - tmp = pool->nexti; - efree(pool); - } - memset(pl, 0, sizeof(INSTRUCTION)); -} - - static inline INSTRUCTION * list_create(INSTRUCTION *x) { diff --git a/awklib/.gitignore b/awklib/.gitignore new file mode 100644 index 00000000..b46084d4 --- /dev/null +++ b/awklib/.gitignore @@ -0,0 +1,8 @@ +# Ignore files that are created by a build. +# For example objects and archives. +grcat +group.awk +igawk +passwd.awk +pwcat + @@ -80,8 +80,10 @@ extern FILE *output_fp; #define POP_TWO_SCALARS(s1, s2) \ s2 = POP_SCALAR(); \ s1 = POP(); \ -if ((s1)->type == Node_var_array) \ - DEREF(s2), fatal(_("attempt to use array `%s' in a scalar context"), array_vname(s1)), 0 +do { if (s1->type == Node_var_array) { \ +DEREF(s2); \ +fatal(_("attempt to use array `%s' in a scalar context"), array_vname(s1)); \ +}} while (FALSE) /* @@ -334,8 +336,10 @@ do_index(int nargs) if ((s2->flags & (STRING|STRCUR)) == 0) lintwarn(_("index: received non-string second argument")); } - force_string(s1); - force_string(s2); + + s1 = force_string(s1); + s2 = force_string(s2); + p1 = s1->stptr; p2 = s2->stptr; l1 = s1->stlen; @@ -502,7 +506,7 @@ do_length(int nargs) if (do_lint && (tmp->flags & (STRING|STRCUR)) == 0) lintwarn(_("length: received non-string argument")); - (void) force_string(tmp); + tmp = force_string(tmp); #if MBS_SUPPORT if (gawk_mb_cur_max > 1) { @@ -1385,7 +1389,7 @@ printf_common(int nargs) } } - force_string(args_array[0]); + args_array[0] = force_string(args_array[0]); r = format_tree(args_array[0]->stptr, args_array[0]->stlen, args_array, nargs); for (i = 0; i < nargs; i++) DEREF(args_array[i]); @@ -1498,12 +1502,12 @@ do_substr(int nargs) if (nargs == 3) { if (! (d_length >= 1)) { - if (do_lint == LINT_ALL) + if (do_lint == DO_LINT_ALL) lintwarn(_("substr: length %g is not >= 1"), d_length); - else if (do_lint == LINT_INVALID && ! (d_length >= 0)) + else if (do_lint == DO_LINT_INVALID && ! (d_length >= 0)) lintwarn(_("substr: length %g is not >= 0"), d_length); DEREF(t1); - return Nnull_string; + return dupnode(Nnull_string); } if (do_lint) { if (double_to_int(d_length) != d_length) @@ -1554,10 +1558,10 @@ do_substr(int nargs) if (t1->stlen == 0) { /* substr("", 1, 0) produces a warning only if LINT_ALL */ - if (do_lint && (do_lint == LINT_ALL || ((indx | length) != 0))) + if (do_lint && (do_lint == DO_LINT_ALL || ((indx | length) != 0))) lintwarn(_("substr: source string is zero length")); DEREF(t1); - return Nnull_string; + return dupnode(Nnull_string); } /* get total len of input string, for following checks */ @@ -1574,7 +1578,7 @@ do_substr(int nargs) lintwarn(_("substr: start index %g is past end of string"), d_index); DEREF(t1); - return Nnull_string; + return dupnode(Nnull_string); } if (length > src_len - indx) { if (do_lint) @@ -1672,7 +1676,7 @@ do_strftime(int nargs) do_gmt = (t3->stlen > 0); DEREF(t3); } - + if (nargs >= 2) { t2 = POP_SCALAR(); if (do_lint && (t2->flags & (NUMCUR|NUMBER)) == 0) @@ -1687,6 +1691,7 @@ do_strftime(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (STRING|STRCUR)) == 0) lintwarn(_("strftime: received non-string first argument")); + t1 = force_string(tmp); format = t1->stptr; formatlen = t1->stlen; @@ -1769,13 +1774,13 @@ do_mktime(int nargs) & hour, & minute, & second, & dst); - if (do_lint /* Ready? Set! Go: */ - && ( (second < 0 || second > 60) - || (minute < 0 || minute > 60) - || (hour < 0 || hour > 23) - || (day < 1 || day > 31) - || (month < 1 || month > 12) )) - lintwarn(_("mktime: at least one of the values is out of the default range")); + if (do_lint /* Ready? Set! Go: */ + && ( (second < 0 || second > 60) + || (minute < 0 || minute > 60) + || (hour < 0 || hour > 23) + || (day < 1 || day > 31) + || (month < 1 || month > 12) )) + lintwarn(_("mktime: at least one of the values is out of the default range")); t1->stptr[t1->stlen] = save; DEREF(t1); @@ -1836,11 +1841,9 @@ do_system(int nargs) return make_number((AWKNUM) ret); } -extern NODE **fmt_list; /* declared in eval.c */ - /* do_print --- print items, separated by OFS, terminated with ORS */ -void +void do_print(int nargs, int redirtype) { struct redirect *rp = NULL; @@ -1848,7 +1851,7 @@ do_print(int nargs, int redirtype) FILE *fp = NULL; int i; NODE *redir_exp = NULL; - NODE *tmp; + NODE *tmp = NULL; assert(nargs <= max_args); @@ -1869,12 +1872,10 @@ do_print(int nargs, int redirtype) DEREF(args_array[i]); fatal(_("attempt to use array `%s' in a scalar context"), array_vname(tmp)); } - if (do_lint && tmp->type == Node_var_new) - lintwarn(_("reference to uninitialized variable `%s'"), - tmp->vname); + if ((tmp->flags & (NUMBER|STRING)) == NUMBER) { if (OFMTidx == CONVFMTidx) - (void) force_string(tmp); + args_array[i] = force_string(tmp); else args_array[i] = format_val(OFMT, OFMTidx, tmp); } @@ -2266,7 +2267,7 @@ do_match(int nargs) it->flags |= MAYBE_NUM; /* user input */ sub = make_number((AWKNUM) (ii)); - lhs = assoc_lookup(dest, sub, FALSE); + lhs = assoc_lookup(dest, sub); unref(*lhs); *lhs = it; unref(sub); @@ -2289,7 +2290,7 @@ do_match(int nargs) it = make_number((AWKNUM) subpat_start + 1); sub = make_string(buf, slen); - lhs = assoc_lookup(dest, sub, FALSE); + lhs = assoc_lookup(dest, sub); unref(*lhs); *lhs = it; unref(sub); @@ -2302,7 +2303,7 @@ do_match(int nargs) it = make_number((AWKNUM) subpat_len); sub = make_string(buf, slen); - lhs = assoc_lookup(dest, sub, FALSE); + lhs = assoc_lookup(dest, sub); unref(*lhs); *lhs = it; unref(sub); diff --git a/cint_array.c b/cint_array.c new file mode 100644 index 00000000..8ec09239 --- /dev/null +++ b/cint_array.c @@ -0,0 +1,1237 @@ +/* + * cint_array.c - routines for arrays of (mostly) consecutive positive integer indices. + */ + +/* + * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Programming Language. + * + * GAWK 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 3 of the License, or + * (at your option) any later version. + * + * GAWK 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "awk.h" + +extern FILE *output_fp; +extern void indent(int indent_level); +extern NODE **is_integer(NODE *symbol, NODE *subs); + +/* + * NHAT --- maximum size of a leaf array (2^NHAT). + * THRESHOLD --- Maximum capacity waste; THRESHOLD >= 2^(NHAT + 1). + */ + +static int NHAT = 10; +static long THRESHOLD; + +/* What is the optimium NHAT ? timing results suggest that 10 is a good choice, + * although differences aren't that significant for > 10. + */ + + +static NODE **cint_array_init(NODE *symbol, NODE *subs); +static NODE **is_uinteger(NODE *symbol, NODE *subs); +static NODE **cint_lookup(NODE *symbol, NODE *subs); +static NODE **cint_exists(NODE *symbol, NODE *subs); +static NODE **cint_clear(NODE *symbol, NODE *subs); +static NODE **cint_remove(NODE *symbol, NODE *subs); +static NODE **cint_list(NODE *symbol, NODE *t); +static NODE **cint_copy(NODE *symbol, NODE *newsymb); +static NODE **cint_dump(NODE *symbol, NODE *ndump); +#ifdef ARRAYDEBUG +static NODE **cint_option(NODE *opt, NODE *val); +static void cint_print(NODE *symbol); +#endif + +array_ptr cint_array_func[] = { + cint_array_init, + is_uinteger, + cint_lookup, + cint_exists, + cint_clear, + cint_remove, + cint_list, + cint_copy, + cint_dump, +#ifdef ARRAYDEBUG + cint_option, +#endif +}; + +static inline int cint_hash(long k); +static inline NODE **cint_find(NODE *symbol, long k, int h1); + +static inline NODE *make_node(NODETYPE type); + +static NODE **tree_lookup(NODE *symbol, NODE *tree, long k, int m, long base); +static NODE **tree_exists(NODE *tree, long k); +static void tree_clear(NODE *tree); +static int tree_remove(NODE *symbol, NODE *tree, long k); +static void tree_copy(NODE *newsymb, NODE *tree, NODE *newtree); +static long tree_list(NODE *tree, NODE **list, unsigned int flags); +static inline NODE **tree_find(NODE *tree, long k, int i); +static void tree_info(NODE *tree, NODE *ndump, const char *aname); +static size_t tree_kilobytes(NODE *tree); +#ifdef ARRAYDEBUG +static void tree_print(NODE *tree, size_t bi, int indent_level); +#endif + +static inline NODE **leaf_lookup(NODE *symbol, NODE *array, long k, long size, long base); +static inline NODE **leaf_exists(NODE *array, long k); +static void leaf_clear(NODE *array); +static int leaf_remove(NODE *symbol, NODE *array, long k); +static void leaf_copy(NODE *newsymb, NODE *array, NODE *newarray); +static long leaf_list(NODE *array, NODE **list, unsigned int flags); +static void leaf_info(NODE *array, NODE *ndump, const char *aname); +#ifdef ARRAYDEBUG +static void leaf_print(NODE *array, size_t bi, int indent_level); +#endif + +/* powers of 2 table upto 2^30 */ +static const long power_two_table[] = { + 1, 2, 4, 8, 16, 32, 64, + 128, 256, 512, 1024, 2048, 4096, + 8192, 16384, 32768, 65536, 131072, 262144, + 524288, 1048576, 2097152, 4194304, 8388608, 16777216, + 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824 +}; + + +#define ISUINT(a, s) ((((s)->flags & NUMINT) != 0 || is_integer(a, s) != NULL) \ + && (s)->numbr >= 0) + +/* + * To store 2^n integers, allocate top-level array of size n, elements + * of which are 1-Dimensional (leaf-array) of geometrically increasing + * size (power of 2). + * + * [0] --> [ 0 ] + * [1] --> [ 1 ] + * |2| --> [ 2 | 3 ] + * |3| --> [ 4 | 5 | 6 | 7 ] + * |.| + * |k| --> [ 2^(k - 1)| ... | 2^k - 1 ] + * ... + * + * For a given integer n (> 0), the leaf-array is at 1 + floor(log2(n)). + * + * The idea for the geometrically increasing array sizes is from: + * Fast Functional Lists, Hash-Lists, Deques and Variable Length Arrays. + * Bagwell, Phil (2002). + * http://infoscience.epfl.ch/record/64410/files/techlists.pdf + * + * Disadvantage: + * Worst case memory waste > 99% and will happen when each of the + * leaf arrays contains only a single element. Even with consecutive + * integers, memory waste can be as high as 50%. + * + * Solution: Hashed Array Trees (HATs). + * + */ + +/* cint_array_init --- check relevant environment variables */ + +static NODE ** +cint_array_init(NODE *symbol ATTRIBUTE_UNUSED, NODE *subs ATTRIBUTE_UNUSED) +{ + long newval; + + if ((newval = getenv_long("NHAT")) > 1 && newval < INT32_BIT) + NHAT = newval; + THRESHOLD = power_two_table[NHAT + 1]; + return (NODE **) ! NULL; +} + + +/* is_uinteger --- test if the subscript is an integer >= 0 */ + +NODE ** +is_uinteger(NODE *symbol, NODE *subs) +{ + if (is_integer(symbol, subs) != NULL && subs->numbr >= 0) + return (NODE **) ! NULL; + return NULL; +} + + +/* cint_lookup --- Find the subscript in the array; Install it if it isn't there. */ + +static NODE ** +cint_lookup(NODE *symbol, NODE *subs) +{ + NODE **lhs; + long k; + int h1 = -1, m, li; + NODE *tn, *xn; + long cint_size, capacity; + + k = -1; + if (ISUINT(symbol, subs)) { + k = subs->numbr; /* k >= 0 */ + h1 = cint_hash(k); /* h1 >= NHAT */ + if ((lhs = cint_find(symbol, k, h1)) != NULL) + return lhs; + } + xn = symbol->xarray; + if (xn != NULL && (lhs = xn->aexists(xn, subs)) != NULL) + return lhs; + + /* It's not there, install it */ + + if (k < 0) + goto xinstall; + + m = h1 - 1; /* m >= (NHAT- 1) */ + + /* Estimate capacity upper bound. + * capacity upper bound = current capacity + leaf array size. + */ + li = m > NHAT ? m : NHAT; + while (li >= NHAT) { + /* leaf-array of a HAT */ + li = (li + 1) / 2; + } + capacity = symbol->array_capacity + power_two_table[li]; + + cint_size = (xn == NULL) ? symbol->table_size + : (symbol->table_size - xn->table_size); + assert(cint_size >= 0); + if ((capacity - cint_size) > THRESHOLD) + goto xinstall; + + if (symbol->nodes == NULL) { + symbol->array_capacity = 0; + assert(symbol->table_size == 0); + + /* nodes[0] .. nodes[NHAT- 1] not used */ + emalloc(symbol->nodes, NODE **, INT32_BIT * sizeof(NODE *), "cint_lookup"); + memset(symbol->nodes, '\0', INT32_BIT * sizeof(NODE *)); + } + + symbol->table_size++; /* one more element in array */ + + tn = symbol->nodes[h1]; + if (tn == NULL) { + tn = make_node(Node_array_tree); + symbol->nodes[h1] = tn; + } + + if (m < NHAT) + return tree_lookup(symbol, tn, k, NHAT, 0); + return tree_lookup(symbol, tn, k, m, power_two_table[m]); + +xinstall: + + symbol->table_size++; + if (xn == NULL) { + extern array_ptr int_array_func[]; + extern array_ptr str_array_func[]; + + xn = symbol->xarray = make_array(); + xn->vname = symbol->vname; /* shallow copy */ + + /* Avoid using assoc_lookup(xn, subs) which may lead + * to infinite recursion. + */ + + if (is_integer(xn, subs)) + xn->array_funcs = int_array_func; + else + xn->array_funcs = str_array_func; + xn->flags |= XARRAY; + } + return xn->alookup(xn, subs); +} + + +/* cint_exists --- test whether an index is in the array or not. */ + +static NODE ** +cint_exists(NODE *symbol, NODE *subs) +{ + NODE *xn; + + if (ISUINT(symbol, subs)) { + long k = subs->numbr; + NODE **lhs; + if ((lhs = cint_find(symbol, k, cint_hash(k))) != NULL) + return lhs; + } + if ((xn = symbol->xarray) == NULL) + return NULL; + return xn->aexists(xn, subs); +} + + +/* cint_clear --- flush all the values in symbol[] */ + +static NODE ** +cint_clear(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED) +{ + size_t i; + NODE *tn; + + assert(symbol->nodes != NULL); + + if (symbol->xarray != NULL) { + NODE *xn = symbol->xarray; + assoc_clear(xn); + freenode(xn); + symbol->xarray = NULL; + } + + for (i = NHAT; i < INT32_BIT; i++) { + tn = symbol->nodes[i]; + if (tn != NULL) { + tree_clear(tn); + freenode(tn); + } + } + + efree(symbol->nodes); + init_array(symbol); /* re-initialize symbol */ + return NULL; +} + + +/* cint_remove --- remove an index from the array */ + +static NODE ** +cint_remove(NODE *symbol, NODE *subs) +{ + long k; + int h1; + NODE *tn, *xn = symbol->xarray; + + if (symbol->table_size == 0) + return NULL; + + if (! ISUINT(symbol, subs)) + goto xremove; + + assert(symbol->nodes != NULL); + + k = subs->numbr; + h1 = cint_hash(k); + tn = symbol->nodes[h1]; + if (tn == NULL || ! tree_remove(symbol, tn, k)) + goto xremove; + + if (tn->table_size == 0) { + freenode(tn); + symbol->nodes[h1] = NULL; + } + + symbol->table_size--; + + if (xn == NULL && symbol->table_size == 0) { + efree(symbol->nodes); + init_array(symbol); /* re-initialize array 'symbol' */ + } else if(xn != NULL && symbol->table_size == xn->table_size) { + /* promote xn to symbol */ + + xn->flags &= ~XARRAY; + xn->parent_array = symbol->parent_array; + efree(symbol->nodes); + *symbol = *xn; + freenode(xn); + } + + return (NODE **) ! NULL; + +xremove: + xn = symbol->xarray; + if (xn == NULL || xn->aremove(xn, subs) == NULL) + return NULL; + if (xn->table_size == 0) { + freenode(xn); + symbol->xarray = NULL; + } + symbol->table_size--; + assert(symbol->table_size > 0); + + return (NODE **) ! NULL; +} + + +/* cint_copy --- duplicate input array "symbol" */ + +static NODE ** +cint_copy(NODE *symbol, NODE *newsymb) +{ + NODE **old, **new; + size_t i; + + assert(symbol->nodes != NULL); + + /* allocate new table */ + emalloc(new, NODE **, INT32_BIT * sizeof(NODE *), "cint_copy"); + memset(new, '\0', INT32_BIT * sizeof(NODE *)); + + old = symbol->nodes; + for (i = NHAT; i < INT32_BIT; i++) { + if (old[i] == NULL) + continue; + new[i] = make_node(Node_array_tree); + tree_copy(newsymb, old[i], new[i]); + } + + if (symbol->xarray != NULL) { + NODE *xn, *n; + xn = symbol->xarray; + n = make_array(); + n->vname = newsymb->vname; + (void) xn->acopy(xn, n); + newsymb->xarray = n; + } else + newsymb->xarray = NULL; + + newsymb->nodes = new; + newsymb->table_size = symbol->table_size; + newsymb->array_capacity = symbol->array_capacity; + newsymb->flags = symbol->flags; + + return NULL; +} + + +/* cint_list --- return a list of items */ + +static NODE** +cint_list(NODE *symbol, NODE *t) +{ + NODE **list = NULL; + NODE *tn, *xn; + unsigned long k = 0, num_elems, list_size; + size_t j, ja, jd; + int elem_size = 1; + + num_elems = symbol->table_size; + if (num_elems == 0) + return NULL; + + if ((t->flags & (AINDEX|AVALUE|ADELETE)) == (AINDEX|ADELETE)) + num_elems = 1; + + if ((t->flags & (AINDEX|AVALUE)) == (AINDEX|AVALUE)) + elem_size = 2; + list_size = num_elems * elem_size; + + if (symbol->xarray != NULL) { + xn = symbol->xarray; + list = xn->alist(xn, t); + assert(list != NULL); + t->flags &= ~(AASC|ADESC); + if (num_elems == 1 || num_elems == xn->table_size) + return list; + erealloc(list, NODE **, list_size * sizeof(NODE *), "cint_list"); + k = elem_size * xn->table_size; + } else + emalloc(list, NODE **, list_size * sizeof(NODE *), "cint_list"); + + + if ((t->flags & AINUM) == 0) /* not sorting by "index num" */ + t->flags &= ~(AASC|ADESC); + + /* populate it with index in ascending or descending order */ + + for (ja = NHAT, jd = INT32_BIT - 1; ja < INT32_BIT && jd >= NHAT; ) { + j = (t->flags & ADESC) ? jd-- : ja++; + tn = symbol->nodes[j]; + if (tn == NULL) + continue; + k += tree_list(tn, list + k, t->flags); + if (k >= list_size) + return list; + } + return list; +} + + +/* cint_dump --- dump array info */ + +static NODE ** +cint_dump(NODE *symbol, NODE *ndump) +{ + NODE *tn, *xn = NULL; + int indent_level; + size_t i; + long cint_size = 0, xsize = 0; + AWKNUM kb = 0; + extern AWKNUM int_kilobytes(NODE *symbol); + extern AWKNUM str_kilobytes(NODE *symbol); + extern array_ptr int_array_func[]; + + indent_level = ndump->alevel; + + if (symbol->xarray != NULL) { + xn = symbol->xarray; + xsize = xn->table_size; + } + cint_size = symbol->table_size - xsize; + + if ((symbol->flags & XARRAY) == 0) + fprintf(output_fp, "%s `%s'\n", + (symbol->parent_array == NULL) ? "array" : "sub-array", + array_vname(symbol)); + indent_level++; + indent(indent_level); + fprintf(output_fp, "array_func: cint_array_func\n"); + if (symbol->flags != 0) { + indent(indent_level); + fprintf(output_fp, "flags: %s\n", flags2str(symbol->flags)); + } + indent(indent_level); + fprintf(output_fp, "NHAT: %d\n", NHAT); + indent(indent_level); + fprintf(output_fp, "THRESHOLD: %ld\n", THRESHOLD); + indent(indent_level); + fprintf(output_fp, "table_size: %ld (total), %ld (cint), %ld (int + str)\n", + symbol->table_size, cint_size, xsize); + indent(indent_level); + fprintf(output_fp, "array_capacity: %lu\n", (unsigned long) symbol->array_capacity); + indent(indent_level); + fprintf(output_fp, "Load Factor: %.2g\n", (AWKNUM) cint_size / symbol->array_capacity); + + for (i = NHAT; i < INT32_BIT; i++) { + tn = symbol->nodes[i]; + if (tn == NULL) + continue; + /* Node_array_tree + HAT */ + kb += (sizeof(NODE) + tree_kilobytes(tn)) / 1024.0; + } + kb += (INT32_BIT * sizeof(NODE *)) / 1024.0; /* symbol->nodes */ + kb += (symbol->array_capacity * sizeof(NODE *)) / 1024.0; /* value nodes in Node_array_leaf(s) */ + if (xn != NULL) { + if (xn->array_funcs == int_array_func) + kb += int_kilobytes(xn); + else + kb += str_kilobytes(xn); + } + + indent(indent_level); + fprintf(output_fp, "memory: %.2g kB (total)\n", kb); + + /* dump elements */ + + if (ndump->adepth >= 0) { + const char *aname; + + fprintf(output_fp, "\n"); + aname = make_aname(symbol); + for (i = NHAT; i < INT32_BIT; i++) { + tn = symbol->nodes[i]; + if (tn != NULL) + tree_info(tn, ndump, aname); + } + } + + if (xn != NULL) { + fprintf(output_fp, "\n"); + xn->adump(xn, ndump); + } + +#ifdef ARRAYDEBUG + if (ndump->adepth < -999) + cint_print(symbol); +#endif + + return NULL; +} + + +/* cint_hash --- locate the HAT for a given number 'k' */ + +static inline int +cint_hash(long k) +{ + uint32_t num, r, shift; + + assert(k >= 0); + if (k == 0) + return NHAT; + num = k; + + /* Find the Floor(log base 2 of 32-bit integer) */ + + /* Warren Jr., Henry S. (2002). Hacker's Delight. + * Addison Wesley. pp. pp. 215. ISBN 978-0201914658. + * + * r = 0; + * if (num >= 1<<16) { num >>= 16; r += 16; } + * if (num >= 1<< 8) { num >>= 8; r += 8; } + * if (num >= 1<< 4) { num >>= 4; r += 4; } + * if (num >= 1<< 2) { num >>= 2; r += 2; } + * if (num >= 1<< 1) { r += 1; } + */ + + + /* Slightly different code copied from: + * + * http://www-graphics.stanford.edu/~seander/bithacks.html + * Bit Twiddling Hacks + * By Sean Eron Anderson + * seander@cs.stanford.edu + * Individually, the code snippets here are in the public domain + * (unless otherwise noted) — feel free to use them however you please. + * The aggregate collection and descriptions are © 1997-2005 + * Sean Eron Anderson. The code and descriptions are distributed in the + * hope that they will be useful, but WITHOUT ANY WARRANTY and without + * even the implied warranty of merchantability or fitness for a particular + * purpose. + * + */ + + r = (num > 0xFFFF) << 4; num >>= r; + shift = (num > 0xFF) << 3; num >>= shift; r |= shift; + shift = (num > 0x0F) << 2; num >>= shift; r |= shift; + shift = (num > 0x03) << 1; num >>= shift; r |= shift; + r |= (num >> 1); + + /* We use a single HAT for 0 <= num < 2^NHAT */ + if (r < NHAT) + return NHAT; + + return (1 + r); +} + + +/* cint_find --- locate the integer subscript */ + +static inline NODE ** +cint_find(NODE *symbol, long k, int h1) +{ + NODE *tn; + + if (symbol->nodes == NULL || (tn = symbol->nodes[h1]) == NULL) + return NULL; + return tree_exists(tn, k); +} + + +#ifdef ARRAYDEBUG + +static NODE ** +cint_option(NODE *opt, NODE *val) +{ + NODE *tmp; + NODE **ret = (NODE **) ! NULL; + + tmp = force_string(opt); + (void) force_number(val); + if (strcmp(tmp->stptr, "NHAT") == 0) + NHAT = (int) val->numbr; + else + ret = NULL; + return ret; +} + + +/* cint_print --- print structural info */ + +static void +cint_print(NODE *symbol) +{ + NODE *tn; + size_t i; + + fprintf(output_fp, "I[%4lu:%-4lu]\n", (unsigned long) INT32_BIT, + (unsigned long) symbol->table_size); + for (i = NHAT; i < INT32_BIT; i++) { + tn = symbol->nodes[i]; + if (tn == NULL) + continue; + tree_print(tn, i, 1); + } +} + +#endif + + +/*------------------------ Hashed Array Trees -----------------------------*/ + +/* + * HATs: Hashed Array Trees + * Fast variable-length arrays + * Edward Sitarski + * http://www.drdobbs.com/architecture-and-design/184409965 + * + * HAT has a top-level array containing a power of two + * number of leaf arrays. All leaf arrays are the same size as the + * top-level array. A full HAT can hold n^2 elements, + * where n (some power of 2) is the size of each leaf array. + * [i/n][i & (n - 1)] locates the `i th' element in a HAT. + * + */ + +/* + * A half HAT is defined here as a HAT with a top-level array of size n^2/2 + * and holds the first n^2/2 elements. + * + * 1. 2^8 elements can be stored in a full HAT of size 2^4. + * 2. 2^9 elements can be stored in a half HAT of size 2^5. + * 3. When the number of elements is some power of 2, it + * can be stored in a full or a half HAT. + * 4. When the number of elements is some power of 2, it + * can be stored in a HAT (full or half) with HATs as leaf elements + * (full or half), and so on (e.g. 2^8 elements in a HAT of size 2^4 (top-level + * array dimension) with each leaf array being a HAT of size 2^2). + * + * IMPLEMENTATION DETAILS: + * 1. A HAT of 2^12 elements needs 2^6 house-keeping NODEs + * of Node_array_leaf. + * + * 2. A HAT of HATS of 2^12 elements needs + * 2^6 * (1 Node_array_tree + 2^3 Node_array_leaf) + * ~ 2^9 house-keeping NODEs. + * + * 3. When a leaf array (or leaf HAT) becomes empty, the memory + * is deallocated, and when there is no leaf array (or leaf HAT) left, + * the HAT is deleted. + * + * 4. A HAT stores the base (first) element, and locates the leaf array/HAT + * for the `i th' element using integer division + * (i - base)/n where n is the size of the top-level array. + * + */ + +/* make_node --- initialize a NODE */ + +static inline NODE * +make_node(NODETYPE type) +{ + NODE *n; + getnode(n); + memset(n, '\0', sizeof(NODE)); + n->type = type; + return n; +} + + +/* tree_lookup --- Find an integer subscript in a HAT; Install it if it isn't there */ + +static NODE ** +tree_lookup(NODE *symbol, NODE *tree, long k, int m, long base) +{ + NODE **lhs; + NODE *tn; + int i, n; + size_t size; + long num = k; + + /* + * HAT size (size of Top & Leaf array) = 2^n + * where n = Floor ((m + 1)/2). For an odd value of m, + * only the first half of the HAT is needed. + */ + + n = (m + 1) / 2; + + if (tree->table_size == 0) { + size_t actual_size; + NODE **table; + + assert(tree->nodes == NULL); + + /* initialize top-level array */ + size = actual_size = power_two_table[n]; + tree->array_base = base; + tree->array_size = size; + tree->table_size = 0; /* # of elements in the array */ + if (n > m/2) { + /* only first half of the array used */ + actual_size /= 2; + tree->flags |= HALFHAT; + } + emalloc(table, NODE **, actual_size * sizeof(NODE *), "tree_lookup"); + memset(table, '\0', actual_size * sizeof(NODE *)); + tree->nodes = table; + } else + size = tree->array_size; + + num -= tree->array_base; + i = num / size; /* top-level array index */ + assert(i >= 0); + + if ((lhs = tree_find(tree, k, i)) != NULL) + return lhs; + + /* It's not there, install it */ + + tree->table_size++; + base += (size * i); + tn = tree->nodes[i]; + if (n > NHAT) { + if (tn == NULL) + tn = tree->nodes[i] = make_node(Node_array_tree); + return tree_lookup(symbol, tn, k, n, base); + } else { + if (tn == NULL) + tn = tree->nodes[i] = make_node(Node_array_leaf); + return leaf_lookup(symbol, tn, k, size, base); + } +} + + +/* tree_exists --- test whether integer subscript `k' exists or not */ + +static NODE ** +tree_exists(NODE *tree, long k) +{ + int i; + NODE *tn; + + i = (k - tree->array_base) / tree->array_size; + assert(i >= 0); + tn = tree->nodes[i]; + if (tn == NULL) + return NULL; + if (tn->type == Node_array_tree) + return tree_exists(tn, k); + return leaf_exists(tn, k); +} + +/* tree_clear --- flush all the values */ + +static void +tree_clear(NODE *tree) +{ + NODE *tn; + size_t j, hsize; + + hsize = tree->array_size; + if ((tree->flags & HALFHAT) != 0) + hsize /= 2; + + for (j = 0; j < hsize; j++) { + tn = tree->nodes[j]; + if (tn == NULL) + continue; + if (tn->type == Node_array_tree) + tree_clear(tn); + else + leaf_clear(tn); + freenode(tn); + } + + efree(tree->nodes); + memset(tree, '\0', sizeof(NODE)); + tree->type = Node_array_tree; +} + + +/* tree_remove --- If the integer subscript is in the HAT, remove it */ + +static int +tree_remove(NODE *symbol, NODE *tree, long k) +{ + int i; + NODE *tn; + + i = (k - tree->array_base) / tree->array_size; + assert(i >= 0); + tn = tree->nodes[i]; + if (tn == NULL) + return FALSE; + + if (tn->type == Node_array_tree + && ! tree_remove(symbol, tn, k)) + return FALSE; + else if (tn->type == Node_array_leaf + && ! leaf_remove(symbol, tn, k)) + return FALSE; + + if (tn->table_size == 0) { + freenode(tn); + tree->nodes[i] = NULL; + } + + /* one less item in array */ + if (--tree->table_size == 0) { + efree(tree->nodes); + memset(tree, '\0', sizeof(NODE)); + tree->type = Node_array_tree; + } + return TRUE; +} + + +/* tree_find --- locate an interger subscript in the HAT */ + +static inline NODE ** +tree_find(NODE *tree, long k, int i) +{ + NODE *tn; + + assert(tree->nodes != NULL); + tn = tree->nodes[i]; + if (tn != NULL) { + if (tn->type == Node_array_tree) + return tree_exists(tn, k); + return leaf_exists(tn, k); + } + return NULL; +} + + +/* tree_list --- return a list of items in the HAT */ + +static long +tree_list(NODE *tree, NODE **list, unsigned int flags) +{ + NODE *tn; + size_t j, cj, hsize; + long k = 0; + + assert(list != NULL); + + hsize = tree->array_size; + if ((tree->flags & HALFHAT) != 0) + hsize /= 2; + + for (j = 0; j < hsize; j++) { + cj = (flags & ADESC) ? (hsize - 1 - j) : j; + tn = tree->nodes[cj]; + if (tn == NULL) + continue; + if (tn->type == Node_array_tree) + k += tree_list(tn, list + k, flags); + else + k += leaf_list(tn, list + k, flags); + if ((flags & ADELETE) != 0 && k >= 1) + return k; + } + return k; +} + + +/* tree_copy --- duplicate a HAT */ + +static void +tree_copy(NODE *newsymb, NODE *tree, NODE *newtree) +{ + NODE **old, **new; + size_t j, hsize; + + hsize = tree->array_size; + if ((tree->flags & HALFHAT) != 0) + hsize /= 2; + + emalloc(new, NODE **, hsize * sizeof(NODE *), "tree_copy"); + memset(new, '\0', hsize * sizeof(NODE *)); + newtree->nodes = new; + newtree->array_base = tree->array_base; + newtree->array_size = tree->array_size; + newtree->table_size = tree->table_size; + newtree->flags = tree->flags; + + old = tree->nodes; + for (j = 0; j < hsize; j++) { + if (old[j] == NULL) + continue; + if (old[j]->type == Node_array_tree) { + new[j] = make_node(Node_array_tree); + tree_copy(newsymb, old[j], new[j]); + } else { + new[j] = make_node(Node_array_leaf); + leaf_copy(newsymb, old[j], new[j]); + } + } +} + + +/* tree_info --- print index, value info */ + +static void +tree_info(NODE *tree, NODE *ndump, const char *aname) +{ + NODE *tn; + size_t j, hsize; + + hsize = tree->array_size; + if ((tree->flags & HALFHAT) != 0) + hsize /= 2; + + for (j = 0; j < hsize; j++) { + tn = tree->nodes[j]; + if (tn == NULL) + continue; + if (tn->type == Node_array_tree) + tree_info(tn, ndump, aname); + else + leaf_info(tn, ndump, aname); + } +} + + +/* tree_kilobytes --- calculate memory consumption of a HAT */ + +static size_t +tree_kilobytes(NODE *tree) +{ + NODE *tn; + size_t j, hsize; + size_t sz = 0; + + hsize = tree->array_size; + if ((tree->flags & HALFHAT) != 0) + hsize /= 2; + for (j = 0; j < hsize; j++) { + tn = tree->nodes[j]; + if (tn == NULL) + continue; + sz += sizeof(NODE); /* Node_array_tree or Node_array_leaf */ + if (tn->type == Node_array_tree) + sz += tree_kilobytes(tn); + } + sz += hsize * sizeof(NODE *); /* tree->nodes */ + return sz; +} + +#ifdef ARRAYDEBUG + +/* tree_print --- print the HAT structures */ + +static void +tree_print(NODE *tree, size_t bi, int indent_level) +{ + NODE *tn; + size_t j, hsize; + + indent(indent_level); + + hsize = tree->array_size; + if ((tree->flags & HALFHAT) != 0) + hsize /= 2; + fprintf(output_fp, "%4lu:%s[%4lu:%-4lu]\n", bi, + (tree->flags & HALFHAT) ? "HH" : "H", + (unsigned long) hsize, (unsigned long) tree->table_size); + + for (j = 0; j < hsize; j++) { + tn = tree->nodes[j]; + if (tn == NULL) + continue; + if (tn->type == Node_array_tree) + tree_print(tn, j, indent_level + 1); + else + leaf_print(tn, j, indent_level + 1); + } +} +#endif + +/*--------------------- leaf (linear 1-D) array --------------------*/ + +/* leaf_lookup --- find an integer subscript in the array; Install it if + it isn't there. +*/ + +static inline NODE ** +leaf_lookup(NODE *symbol, NODE *array, long k, long size, long base) +{ + NODE **lhs; + + if (array->nodes == NULL) { + array->table_size = 0; /* sanity */ + array->array_size = size; + array->array_base = base; + emalloc(array->nodes, NODE **, size * sizeof(NODE *), "leaf_lookup"); + memset(array->nodes, '\0', size * sizeof(NODE *)); + symbol->array_capacity += size; + } + + lhs = array->nodes + (k - base); /* leaf element */ + if (*lhs == NULL) { + array->table_size++; /* one more element in leaf array */ + *lhs = dupnode(Nnull_string); + } + return lhs; +} + + +/* leaf_exists --- check if the array contains an integer subscript */ + +static inline NODE ** +leaf_exists(NODE *array, long k) +{ + NODE **lhs; + lhs = array->nodes + (k - array->array_base); + return (*lhs != NULL) ? lhs : NULL; +} + + +/* leaf_clear --- flush all values in the array */ + +static void +leaf_clear(NODE *array) +{ + long i, size = array->array_size; + NODE *r; + + for (i = 0; i < size; i++) { + r = array->nodes[i]; + if (r == NULL) + continue; + if (r->type == Node_var_array) { + assoc_clear(r); /* recursively clear all sub-arrays */ + efree(r->vname); + freenode(r); + } else + unref(r); + } + efree(array->nodes); + array->nodes = NULL; + array->array_size = array->table_size = 0; +} + + +/* leaf_remove --- remove an integer subscript from the array */ + +static int +leaf_remove(NODE *symbol, NODE *array, long k) +{ + NODE **lhs; + + lhs = array->nodes + (k - array->array_base); + if (*lhs == NULL) + return FALSE; + *lhs = NULL; + if (--array->table_size == 0) { + efree(array->nodes); + array->nodes = NULL; + symbol->array_capacity -= array->array_size; + array->array_size = 0; /* sanity */ + } + return TRUE; +} + + +/* leaf_copy --- duplicate a leaf array */ + +static void +leaf_copy(NODE *newsymb, NODE *array, NODE *newarray) +{ + NODE **old, **new; + long size, i; + + size = array->array_size; + emalloc(new, NODE **, size * sizeof(NODE *), "leaf_copy"); + memset(new, '\0', size * sizeof(NODE *)); + newarray->nodes = new; + newarray->array_size = size; + newarray->array_base = array->array_base; + newarray->flags = array->flags; + newarray->table_size = array->table_size; + + old = array->nodes; + for (i = 0; i < size; i++) { + if (old[i] == NULL) + continue; + if (old[i]->type == Node_val) + new[i] = dupnode(old[i]); + else { + NODE *r; + r = make_array(); + r->vname = estrdup(old[i]->vname, strlen(old[i]->vname)); + r->parent_array = newsymb; + new[i] = assoc_copy(old[i], r); + } + } +} + + +/* leaf_list --- return a list of items */ + +static long +leaf_list(NODE *array, NODE **list, unsigned int flags) +{ + NODE *r, *subs; + long num, i, ci, k = 0; + long size = array->array_size; + static char buf[100]; + + for (i = 0; i < size; i++) { + ci = (flags & ADESC) ? (size - 1 - i) : i; + r = array->nodes[ci]; + if (r == NULL) + continue; + + /* index */ + num = array->array_base + ci; + if (flags & AISTR) { + sprintf(buf, "%ld", num); + subs = make_string(buf, strlen(buf)); + subs->numbr = num; + subs->flags |= (NUMCUR|NUMINT); + } else { + subs = make_number((AWKNUM) num); + subs->flags |= (INTIND|NUMINT); + } + list[k++] = subs; + + /* value */ + if (flags & AVALUE) { + if (r->type == Node_val) { + if ((flags & AVNUM) != 0) + (void) force_number(r); + else if ((flags & AVSTR) != 0) + r = force_string(r); + } + list[k++] = r; + } + if ((flags & ADELETE) != 0 && k >= 1) + return k; + } + + return k; +} + + +/* leaf_info --- print index, value info */ + +static void +leaf_info(NODE *array, NODE *ndump, const char *aname) +{ + NODE *subs, *val; + size_t i, size; + + size = array->array_size; + + subs = make_number((AWKNUM) 0.0); + subs->flags |= (INTIND|NUMINT); + for (i = 0; i < size; i++) { + val = array->nodes[i]; + if (val == NULL) + continue; + subs->numbr = array->array_base + i; + assoc_info(subs, val, ndump, aname); + } + unref(subs); +} + +#ifdef ARRAYDEBUG + +/* leaf_print --- print the leaf-array structure */ + + +static void +leaf_print(NODE *array, size_t bi, int indent_level) +{ + indent(indent_level); + fprintf(output_fp, "%4lu:L[%4lu:%-4lu]\n", bi, + (unsigned long) array->array_size, + (unsigned long) array->table_size); +} +#endif @@ -28,8 +28,7 @@ #include <readline/history.h> extern char **command_completion(const char *text, int start, int end); extern void initialize_pager(FILE *fp); /* debug.c */ -extern NODE **get_varlist(void); -extern char **get_parmlist(void); +extern NODE *get_function(void); #else #define initialize_pager(x) /* nothing */ #define add_history(x) /* nothing */ @@ -102,7 +102,7 @@ static int num_dim; static int in_eval = FALSE; static const char start_EVAL[] = "function @eval(){"; static const char end_EVAL[] = "}"; -static CMDARG *append_statement(CMDARG *alist, char *stmt); +static CMDARG *append_statement(CMDARG *stmt_list, char *stmt); static char *next_word(char *p, int len, char **endp); static NODE *concat_args(CMDARG *a, int count); @@ -2763,7 +2763,7 @@ yyreturn: /* append_statement --- append 'stmt' to the list of eval awk statements */ static CMDARG * -append_statement(CMDARG *alist, char *stmt) +append_statement(CMDARG *stmt_list, char *stmt) { CMDARG *a, *arg; char *s; @@ -2773,7 +2773,7 @@ append_statement(CMDARG *alist, char *stmt) if (stmt == start_EVAL) { len = sizeof(start_EVAL); - for (a = alist; a != NULL; a = a->next) + for (a = stmt_list; a != NULL; a = a->next) len += strlen(a->a_string) + 1; /* 1 for ',' */ len += EVALSIZE; @@ -2785,7 +2785,7 @@ append_statement(CMDARG *alist, char *stmt) slen = sizeof("function @eval(") - 1; memcpy(s, start_EVAL, slen); - for (a = alist; a != NULL; a = a->next) { + for (a = stmt_list; a != NULL; a = a->next) { len = strlen(a->a_string); memcpy(s + slen, a->a_string, len); slen += len; @@ -2799,14 +2799,14 @@ append_statement(CMDARG *alist, char *stmt) } len = strlen(stmt) + 1; /* 1 for newline */ - s = alist->a_string; + s = stmt_list->a_string; slen = strlen(s); - ssize = alist->a_count; + ssize = stmt_list->a_count; if (len > ssize - slen) { ssize = slen + len + EVALSIZE; erealloc(s, char *, (ssize + 2) * sizeof(char), "append_statement"); - alist->a_string = s; - alist->a_count = ssize; + stmt_list->a_string = s; + stmt_list->a_count = ssize; } memcpy(s + slen, stmt, len); slen += len; @@ -2816,8 +2816,8 @@ append_statement(CMDARG *alist, char *stmt) } if (stmt == end_EVAL) - erealloc(alist->a_string, char *, slen + 2, "append_statement"); - return alist; + erealloc(stmt_list->a_string, char *, slen + 2, "append_statement"); + return stmt_list; #undef EVALSIZE } @@ -3206,7 +3206,7 @@ err: if (! want_nodeval) { yylval = mk_cmdarg(D_string); - yylval->a_string = estrdup(str, p - str); + yylval->a_string = str; append_cmdarg(yylval); return D_STRING; } else { /* awk string */ @@ -3397,8 +3397,10 @@ find_command(const char *token, size_t toklen) && strncmp(name, token, toklen) == 0 ) return i; - if (*name > *token) + + if (*name > *token || i == (k - 1)) try_exact = FALSE; + if (abrv_match < 0) { abrv = cmdtab[i].abbrvn; if (abrv[0] == token[0]) { @@ -3538,6 +3540,7 @@ command_completion(const char *text, int start, int end) return NULL; } } + if (this_cmd == D_print || this_cmd == D_printf) return rl_completion_matches(text, variable_generator); return NULL; @@ -3598,7 +3601,7 @@ argument_generator(const char *text, int state) { static size_t textlen; static int idx; - char *name; + const char *name; if (! state) { /* first time */ textlen = strlen(text); @@ -3606,12 +3609,12 @@ argument_generator(const char *text, int state) } if (this_cmd == D_help) { - while ((name = (char *) cmdtab[idx++].name) != NULL) { + while ((name = cmdtab[idx++].name) != NULL) { if (strncmp(name, text, textlen) == 0) return estrdup(name, strlen(name)); } } else { - while ((name = (char *) argtab[idx].name) != NULL) { + while ((name = argtab[idx].name) != NULL) { if (this_cmd != argtab[idx++].cmd) continue; if (strncmp(name, text, textlen) == 0) @@ -3628,45 +3631,39 @@ variable_generator(const char *text, int state) { static size_t textlen; static int idx = 0; - static char **pnames = NULL; - static NODE **var_table = NULL; - char *name; - NODE *hp; + static NODE *func = NULL; + static NODE **vars = NULL; + const char *name; + NODE *r; if (! state) { /* first time */ textlen = strlen(text); - if (var_table != NULL) - efree(var_table); - var_table = get_varlist(); + if (vars != NULL) + efree(vars); + vars = variable_list(); idx = 0; - pnames = get_parmlist(); /* names of function params in - * current context; the array - * is NULL terminated in - * awkgram.y (func_install). - */ + func = get_function(); /* function in current context */ } /* function params */ - while (pnames != NULL) { - name = pnames[idx]; - if (name == NULL) { - pnames = NULL; /* don't try to match params again */ + while (func != NULL) { + if (idx >= func->param_cnt) { + func = NULL; /* don't try to match params again */ idx = 0; break; } - idx++; + name = func->fparms[idx++].param; if (strncmp(name, text, textlen) == 0) return estrdup(name, strlen(name)); } /* globals */ - while ((hp = var_table[idx]) != NULL) { - idx++; - if (hp->hvalue->type == Node_func) - continue; - if (strncmp(hp->hname, text, textlen) == 0) - return estrdup(hp->hname, hp->hlength); + while ((r = vars[idx++]) != NULL) { + name = r->vname; + if (strncmp(name, text, textlen) == 0) + return estrdup(name, strlen(name)); } + return NULL; } @@ -3692,4 +3689,3 @@ history_expand_line(char **line) #endif - @@ -50,7 +50,7 @@ static int num_dim; static int in_eval = FALSE; static const char start_EVAL[] = "function @eval(){"; static const char end_EVAL[] = "}"; -static CMDARG *append_statement(CMDARG *alist, char *stmt); +static CMDARG *append_statement(CMDARG *stmt_list, char *stmt); static char *next_word(char *p, int len, char **endp); static NODE *concat_args(CMDARG *a, int count); @@ -749,7 +749,7 @@ nls /* append_statement --- append 'stmt' to the list of eval awk statements */ static CMDARG * -append_statement(CMDARG *alist, char *stmt) +append_statement(CMDARG *stmt_list, char *stmt) { CMDARG *a, *arg; char *s; @@ -759,7 +759,7 @@ append_statement(CMDARG *alist, char *stmt) if (stmt == start_EVAL) { len = sizeof(start_EVAL); - for (a = alist; a != NULL; a = a->next) + for (a = stmt_list; a != NULL; a = a->next) len += strlen(a->a_string) + 1; /* 1 for ',' */ len += EVALSIZE; @@ -771,7 +771,7 @@ append_statement(CMDARG *alist, char *stmt) slen = sizeof("function @eval(") - 1; memcpy(s, start_EVAL, slen); - for (a = alist; a != NULL; a = a->next) { + for (a = stmt_list; a != NULL; a = a->next) { len = strlen(a->a_string); memcpy(s + slen, a->a_string, len); slen += len; @@ -785,14 +785,14 @@ append_statement(CMDARG *alist, char *stmt) } len = strlen(stmt) + 1; /* 1 for newline */ - s = alist->a_string; + s = stmt_list->a_string; slen = strlen(s); - ssize = alist->a_count; + ssize = stmt_list->a_count; if (len > ssize - slen) { ssize = slen + len + EVALSIZE; erealloc(s, char *, (ssize + 2) * sizeof(char), "append_statement"); - alist->a_string = s; - alist->a_count = ssize; + stmt_list->a_string = s; + stmt_list->a_count = ssize; } memcpy(s + slen, stmt, len); slen += len; @@ -802,8 +802,8 @@ append_statement(CMDARG *alist, char *stmt) } if (stmt == end_EVAL) - erealloc(alist->a_string, char *, slen + 2, "append_statement"); - return alist; + erealloc(stmt_list->a_string, char *, slen + 2, "append_statement"); + return stmt_list; #undef EVALSIZE } @@ -1192,7 +1192,7 @@ err: if (! want_nodeval) { yylval = mk_cmdarg(D_string); - yylval->a_string = estrdup(str, p - str); + yylval->a_string = str; append_cmdarg(yylval); return D_STRING; } else { /* awk string */ @@ -1383,8 +1383,10 @@ find_command(const char *token, size_t toklen) && strncmp(name, token, toklen) == 0 ) return i; - if (*name > *token) + + if (*name > *token || i == (k - 1)) try_exact = FALSE; + if (abrv_match < 0) { abrv = cmdtab[i].abbrvn; if (abrv[0] == token[0]) { @@ -1524,6 +1526,7 @@ command_completion(const char *text, int start, int end) return NULL; } } + if (this_cmd == D_print || this_cmd == D_printf) return rl_completion_matches(text, variable_generator); return NULL; @@ -1584,7 +1587,7 @@ argument_generator(const char *text, int state) { static size_t textlen; static int idx; - char *name; + const char *name; if (! state) { /* first time */ textlen = strlen(text); @@ -1592,12 +1595,12 @@ argument_generator(const char *text, int state) } if (this_cmd == D_help) { - while ((name = (char *) cmdtab[idx++].name) != NULL) { + while ((name = cmdtab[idx++].name) != NULL) { if (strncmp(name, text, textlen) == 0) return estrdup(name, strlen(name)); } } else { - while ((name = (char *) argtab[idx].name) != NULL) { + while ((name = argtab[idx].name) != NULL) { if (this_cmd != argtab[idx++].cmd) continue; if (strncmp(name, text, textlen) == 0) @@ -1614,45 +1617,39 @@ variable_generator(const char *text, int state) { static size_t textlen; static int idx = 0; - static char **pnames = NULL; - static NODE **var_table = NULL; - char *name; - NODE *hp; + static NODE *func = NULL; + static NODE **vars = NULL; + const char *name; + NODE *r; if (! state) { /* first time */ textlen = strlen(text); - if (var_table != NULL) - efree(var_table); - var_table = get_varlist(); + if (vars != NULL) + efree(vars); + vars = variable_list(); idx = 0; - pnames = get_parmlist(); /* names of function params in - * current context; the array - * is NULL terminated in - * awkgram.y (func_install). - */ + func = get_function(); /* function in current context */ } /* function params */ - while (pnames != NULL) { - name = pnames[idx]; - if (name == NULL) { - pnames = NULL; /* don't try to match params again */ + while (func != NULL) { + if (idx >= func->param_cnt) { + func = NULL; /* don't try to match params again */ idx = 0; break; } - idx++; + name = func->fparms[idx++].param; if (strncmp(name, text, textlen) == 0) return estrdup(name, strlen(name)); } /* globals */ - while ((hp = var_table[idx]) != NULL) { - idx++; - if (hp->hvalue->type == Node_func) - continue; - if (strncmp(hp->hname, text, textlen) == 0) - return estrdup(hp->hname, hp->hlength); + while ((r = vars[idx++]) != NULL) { + name = r->vname; + if (strncmp(name, text, textlen) == 0) + return estrdup(name, strlen(name)); } + return NULL; } @@ -1677,4 +1674,3 @@ history_expand_line(char **line) } #endif - @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for GNU Awk 4.0.0g. +# Generated by GNU Autoconf 2.68 for GNU Awk 4.0.70. # # Report bugs to <bug-gawk@gnu.org>. # @@ -560,8 +560,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='GNU Awk' PACKAGE_TARNAME='gawk' -PACKAGE_VERSION='4.0.0g' -PACKAGE_STRING='GNU Awk 4.0.0g' +PACKAGE_VERSION='4.0.70' +PACKAGE_STRING='GNU Awk 4.0.70' PACKAGE_BUGREPORT='bug-gawk@gnu.org' PACKAGE_URL='http://www.gnu.org/software/gawk/' @@ -1290,7 +1290,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures GNU Awk 4.0.0g to adapt to many kinds of systems. +\`configure' configures GNU Awk 4.0.70 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1360,7 +1360,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of GNU Awk 4.0.0g:";; + short | recursive ) echo "Configuration of GNU Awk 4.0.70:";; esac cat <<\_ACEOF @@ -1472,7 +1472,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -GNU Awk configure 4.0.0g +GNU Awk configure 4.0.70 generated by GNU Autoconf 2.68 Copyright (C) 2010 Free Software Foundation, Inc. @@ -2176,7 +2176,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by GNU Awk $as_me 4.0.0g, which was +It was created by GNU Awk $as_me 4.0.70, which was generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -3011,7 +3011,7 @@ fi # Define the identity of the package. PACKAGE='gawk' - VERSION='4.0.0g' + VERSION='4.0.70' cat >>confdefs.h <<_ACEOF @@ -5486,7 +5486,7 @@ $as_echo_n "checking for special development options... " >&6; } if test -f $srcdir/.developing then # add other debug flags as appropriate, save GAWKDEBUG for emergencies - CFLAGS="$CFLAGS -DARRAYDEBUG -DYYDEBUG" + CFLAGS="$CFLAGS -DARRAYDEBUG" if grep dbug $srcdir/.developing then CFLAGS="$CFLAGS -DDBUG" @@ -5496,7 +5496,7 @@ then # enable debugging using macros also if test "$GCC" = yes then - CFLAGS="$CFLAGS -Wall -fno-builtin -g3 -gdwarf-2" + CFLAGS="$CFLAGS -Wall" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } @@ -11221,7 +11221,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by GNU Awk $as_me 4.0.0g, which was +This file was extended by GNU Awk $as_me 4.0.70, which was generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -11289,7 +11289,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -GNU Awk config.status 4.0.0g +GNU Awk config.status 4.0.70 configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 545ad10b..3b0ba330 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ dnl dnl Process this file with autoconf to produce a configure script. -AC_INIT([GNU Awk], 4.0.0g, bug-gawk@gnu.org, gawk) +AC_INIT([GNU Awk], 4.0.70, bug-gawk@gnu.org, gawk) # This is a hack. Different versions of install on different systems # are just too different. Chuck it and use install-sh. @@ -80,7 +80,7 @@ AC_MSG_CHECKING([for special development options]) if test -f $srcdir/.developing then # add other debug flags as appropriate, save GAWKDEBUG for emergencies - CFLAGS="$CFLAGS -DARRAYDEBUG -DYYDEBUG" + CFLAGS="$CFLAGS -DARRAYDEBUG" if grep dbug $srcdir/.developing then CFLAGS="$CFLAGS -DDBUG" @@ -90,7 +90,7 @@ then # enable debugging using macros also if test "$GCC" = yes then - CFLAGS="$CFLAGS -Wall -fno-builtin -g3 -gdwarf-2" + CFLAGS="$CFLAGS -Wall" fi AC_MSG_RESULT([yes]) else @@ -40,12 +40,9 @@ extern FILE *output_fp; extern IOBUF *curfile; extern const char *command_file; extern const char *get_spec_varname(Func_ptr fptr); -extern int r_interpret(INSTRUCTION *); extern int zzparse(void); #define read_command() (void) zzparse() -extern int free_instruction(INSTRUCTION *, int *); -extern void destroy_symbol(char *name); extern const char *redir2str(int redirtype); static char *linebuf = NULL; /* used to print a single line of source */ @@ -216,9 +213,9 @@ struct dbg_option { const char *help_txt; }; -#define DEFAULT_HISTFILE "./.dgawk_history" -#define DEFAULT_OPTFILE "./.dgawkrc" -#define DEFAULT_PROMPT "dgawk> " +#define DEFAULT_HISTFILE "./.gawk_history" +#define DEFAULT_OPTFILE "./.gawkrc" +#define DEFAULT_PROMPT "gawk> " #define DEFAULT_LISTSIZE 15 #define DEFAULT_HISTSIZE 100 @@ -237,7 +234,7 @@ static const char *history_file = DEFAULT_HISTFILE; /* keep all option variables in one place */ static char *output_file = "/dev/stdout"; /* gawk output redirection */ -char *dgawk_Prompt = NULL; /* initialized in interpret */ +char *dgawk_Prompt = NULL; /* initialized in do_debug */ static int list_size = DEFAULT_LISTSIZE; /* # of lines that 'list' prints */ static int do_trace = FALSE; static int do_save_history = TRUE; @@ -267,7 +264,7 @@ static void save_options(const char *file); /* pager */ jmp_buf pager_quit_tag; -int pager_quit_tag_valid; +int pager_quit_tag_valid = FALSE; static int screen_width = INT_MAX; /* no of columns */ static int screen_height = INT_MAX; /* no of rows */ static int pager_lines_printed = 0; /* no of lines printed so far */ @@ -307,8 +304,8 @@ static struct list_item *add_item(struct list_item *list, int type, NODE *symbol static void delete_item(struct list_item *d); static int breakpoint_triggered(BREAKPOINT *b); static int watchpoint_triggered(struct list_item *w); - static void print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump); +static int print_code(INSTRUCTION *pc, void *x); static void next_command(); static char *g_readline(const char *prompt); static int prompt_yes_no(const char *, char , int , FILE *); @@ -335,9 +332,6 @@ struct command_source static struct command_source *cmd_src = NULL; -#define get_param_count(f) (f)->lnode->param_cnt -#define get_params(f) (f)->parmlist - #define CHECK_PROG_RUNNING() \ do { \ @@ -490,11 +484,11 @@ source_find(char *src) return s; } - path = find_source(src, &sbuf, &errno_val); + path = find_source(src, & sbuf, & errno_val, FALSE); if (path != NULL) { for (s = srcfiles->next; s != srcfiles; s = s->next) { if ((s->stype == SRC_FILE || s->stype == SRC_INC) - && files_are_same(path, s)) { + && files_are_same(path, s)) { efree(path); return s; } @@ -719,6 +713,8 @@ list: int do_info(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) { + NODE **table; + if (arg == NULL || arg->type != D_argument) return FALSE; @@ -804,7 +800,6 @@ do_info(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) INSTRUCTION *pc; int arg_count, pcount; int i, from, to; - char **pnames; CHECK_PROG_RUNNING(); f = find_frame(cur_frame); @@ -815,8 +810,7 @@ do_info(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) return FALSE; } - pcount = get_param_count(func); /* # of defined params */ - pnames = get_params(func); /* param names */ + pcount = func->param_cnt; /* # of defined params */ pc = (INSTRUCTION *) f->reti; /* Op_func_call instruction */ arg_count = (pc + 1)->expr_count; /* # of arguments supplied */ @@ -836,7 +830,7 @@ do_info(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) r = f->stack[i]; if (r->type == Node_array_ref) r = r->orig_array; - fprintf(out_fp, "%s = ", pnames[i]); + fprintf(out_fp, "%s = ", func->fparms[i].param); print_symbol(r, TRUE); } if (to < from) @@ -848,25 +842,28 @@ do_info(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) break; case A_VARIABLES: + table = variable_list(); initialize_pager(out_fp); if (setjmp(pager_quit_tag) == 0) { gprintf(out_fp, _("All defined variables:\n\n")); - print_vars(gprintf, out_fp); + print_vars(table, gprintf, out_fp); } + efree(table); break; case A_FUNCTIONS: + table = function_list(TRUE); initialize_pager(out_fp); if (setjmp(pager_quit_tag) == 0) { gprintf(out_fp, _("All defined functions:\n\n")); pf_data.print_func = gprintf; pf_data.fp = out_fp; pf_data.defn = TRUE; - (void) foreach_func((int (*)(INSTRUCTION *, void *)) print_function, - FALSE, /* sort */ - &pf_data /* data */ - ); + (void) foreach_func(table, + (int (*)(INSTRUCTION *, void *)) print_function, + &pf_data); } + efree(table); break; case A_DISPLAY: @@ -977,6 +974,7 @@ find_param(const char *name, long num, char **pname) { NODE *r = NULL; NODE *f; + char *fparam; if (pname) *pname = NULL; @@ -986,20 +984,18 @@ find_param(const char *name, long num, char **pname) f = find_frame(num); if (f->func_node != NULL) { /* in function */ NODE *func; - char **pnames; int i, pcount; func = f->func_node; - pnames = get_params(func); - pcount = get_param_count(func); - + pcount = func->param_cnt; for (i = 0; i < pcount; i++) { - if (strcmp(name, pnames[i]) == 0) { + fparam = func->fparms[i].param; + if (strcmp(name, fparam) == 0) { r = f->stack[i]; if (r->type == Node_array_ref) r = r->orig_array; if (pname) - *pname = pnames[i]; + *pname = fparam; break; } } @@ -1059,7 +1055,7 @@ print_field(long field_num) static int print_array(volatile NODE *arr, char *arr_name) { - NODE *bucket; + NODE *subs; NODE **list; int i; size_t num_elems = 0; @@ -1067,7 +1063,7 @@ print_array(volatile NODE *arr, char *arr_name) volatile int ret = 0; volatile jmp_buf pager_quit_tag_stack; - if (arr->var_array == NULL || arr->table_size == 0) { + if (array_empty(arr)) { gprintf(out_fp, _("array `%s' is empty\n"), arr_name); return 0; } @@ -1080,12 +1076,12 @@ print_array(volatile NODE *arr, char *arr_name) PUSH_BINDING(pager_quit_tag_stack, pager_quit_tag, pager_quit_tag_valid); if (setjmp(pager_quit_tag) == 0) { for (i = 0; ret == 0 && i < num_elems; i++) { - bucket = list[i]; - r = bucket->ahvalue; + subs = list[i]; + r = *assoc_lookup((NODE *) arr, subs); if (r->type == Node_var_array) ret = print_array(r, r->vname); else { - gprintf(out_fp, "%s[\"%s\"] = ", arr_name, bucket->ahname_str); + gprintf(out_fp, "%s[\"%s\"] = ", arr_name, subs->stptr); valinfo((NODE *) r, gprintf, out_fp); } } @@ -1215,7 +1211,7 @@ do_set_var(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) switch (r->type) { case Node_var_new: r->type = Node_var; - r->var_value = Nnull_string; + r->var_value = dupnode(Nnull_string); /* fall through */ case Node_var: lhs = &r->var_value; @@ -1255,7 +1251,7 @@ do_set_var(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) else { arg = arg->next; val = arg->a_node; - lhs = assoc_lookup(r, subs, FALSE); + lhs = assoc_lookup(r, subs); unref(*lhs); *lhs = dupnode(val); fprintf(out_fp, "%s[\"%s\"] = ", name, subs->stptr); @@ -1264,12 +1260,10 @@ do_set_var(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) } else { if (value == NULL) { NODE *array; - - getnode(array); - array->type = Node_var_array; - array->var_array = NULL; + array = make_array(); array->vname = estrdup(subs->stptr, subs->stlen); - *assoc_lookup(r, subs, FALSE) = array; + array->parent_array = r; + *assoc_lookup(r, subs) = array; r = array; } else if (value->type != Node_var_array) { d_error(_("attempt to use scalar `%s[\"%s\"]' as array"), @@ -1306,7 +1300,7 @@ do_set_var(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) break; } return FALSE; -} +} /* find_item --- find an item in the watch/display list */ @@ -1378,14 +1372,18 @@ add_item(struct list_item *list, int type, NODE *symbol, char *pname) d->fcall_count = fcall_count - cur_frame; } - if (type == D_field) { /* field number */ + if (type == D_field) { + /* field number */ d->symbol = symbol; d->flags |= FIELD_NUM; - } else if (type == D_subscript) { /* subscript */ + } else if (type == D_subscript) { + /* subscript */ d->symbol = symbol; d->flags |= SUBSCRIPT; - } else /* array or variable */ + } else { + /* array or variable */ d->symbol = symbol; + } /* add to list */ d->next = list->next; @@ -1430,7 +1428,7 @@ do_add_item(struct list_item *list, CMDARG *arg) for (i = 0; i < count; i++) { arg = arg->next; subs[i] = dupnode(arg->a_node); - (void) force_string(subs[i]); + subs[i] = force_string(subs[i]); } item->subs = subs; item->num_subs = count; @@ -1598,7 +1596,6 @@ condition_triggered(struct condition *cndn) } - static int find_subscript(struct list_item *item, NODE **ptr) { @@ -1617,7 +1614,8 @@ find_subscript(struct list_item *item, NODE **ptr) else if (i < count - 1) return -1; } - *ptr = r; + if (r != NULL) + *ptr = r; return 0; } @@ -1647,8 +1645,7 @@ cmp_val(struct list_item *w, NODE *old, NODE *new) if (new->type == Node_val) /* 7 */ return TRUE; /* new->type == Node_var_array */ /* 8 */ - if (new->var_array != NULL) - size = new->table_size; + size = new->table_size; if (w->cur_size == size) return FALSE; return TRUE; @@ -1722,7 +1719,7 @@ watchpoint_triggered(struct list_item *w) w->flags &= ~CUR_IS_ARRAY; w->cur_value = dupnode(t2); } else - w->cur_size = (t2->var_array != NULL) ? t2->table_size : 0; + w->cur_size = (t2->type == Node_var_array) ? t2->table_size : 0; } else if (! t1) { /* 1, 2 */ w->old_value = 0; /* new != NULL */ @@ -1730,7 +1727,7 @@ watchpoint_triggered(struct list_item *w) w->cur_value = dupnode(t2); else { w->flags |= CUR_IS_ARRAY; - w->cur_size = (t2->var_array != NULL) ? t2->table_size : 0; + w->cur_size = (t2->type == Node_var_array) ? t2->table_size : 0; } } else /* if (t1->type == Node_val) */ { /* 4, 5, 6 */ w->old_value = w->cur_value; @@ -1738,7 +1735,7 @@ watchpoint_triggered(struct list_item *w) w->cur_value = 0; else if (t2->type == Node_var_array) { w->flags |= CUR_IS_ARRAY; - w->cur_size = (t2->var_array != NULL) ? t2->table_size : 0; + w->cur_size = t2->table_size; } else w->cur_value = dupnode(t2); } @@ -1764,7 +1761,7 @@ initialize_watch_item(struct list_item *w) w->cur_value = (NODE *) 0; else if (r->type == Node_var_array) { /* it's a sub-array */ w->flags |= CUR_IS_ARRAY; - w->cur_size = (r->var_array != NULL) ? r->table_size : 0; + w->cur_size = r->table_size; } else w->cur_value = dupnode(r); } else if (IS_FIELD(w)) { @@ -1781,7 +1778,7 @@ initialize_watch_item(struct list_item *w) w->cur_value = dupnode(r); } else if (symbol->type == Node_var_array) { w->flags |= CUR_IS_ARRAY; - w->cur_size = (symbol->var_array != NULL) ? symbol->table_size : 0; + w->cur_size = symbol->table_size; } /* else can't happen */ } @@ -1873,19 +1870,17 @@ print_function(INSTRUCTION *pc, void *x) { NODE *func; int i, pcount; - char **pnames; struct pf_data *data = (struct pf_data *) x; int defn = data->defn; Func_print print_func = data->print_func; FILE *fp = data->fp; func = pc->func_body; - pcount = get_param_count(func); - pnames = get_params(func); + pcount = func->param_cnt; - print_func(fp, "%s(", func->lnode->param); + print_func(fp, "%s(", func->vname); for (i = 0; i < pcount; i++) { - print_func(fp, "%s", pnames[i]); + print_func(fp, "%s", func->fparms[i].param); if (i < pcount - 1) print_func(fp, ", "); } @@ -2368,7 +2363,7 @@ func: rp = func->code_ptr; if ((b = set_breakpoint_at(rp, rp->source_line, FALSE)) == NULL) fprintf(out_fp, _("Can't set breakpoint in function `%s'\n"), - func->lnode->param); + func->vname); else if (temporary) b->flags |= BP_TEMP; lineno = b->bpi->source_line; @@ -2495,7 +2490,7 @@ func: } if (! bp_found) fprintf(out_fp, _("No breakpoint(s) at entry to function `%s'\n"), - func->lnode->param); + func->vname); else fprintf(out_fp, "\n"); /* fall through */ @@ -2690,19 +2685,17 @@ do_disable_breakpoint(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) #ifdef HAVE_LIBREADLINE -/* get_parmlist --- list of function params in current context */ +/* get_function --- function definition in current context */ -char ** -get_parmlist() +NODE * +get_function() { NODE *func; if (! prog_running) return NULL; func = find_frame(cur_frame)->func_node; - if (func == NULL) /* in main */ - return NULL; - return func->parmlist; + return func; } /* initialize_readline --- initialize readline */ @@ -2728,10 +2721,10 @@ initialize_readline() #endif -/* interpret --- debugger entry point */ +/* debug_prog --- debugger entry point */ int -interpret(INSTRUCTION *pc) +debug_prog(INSTRUCTION *pc) { char *run; @@ -2781,7 +2774,7 @@ interpret(INSTRUCTION *pc) (void) do_run(NULL, 0); } else if (command_file != NULL) { - /* run commands from a file (--command=file or -R file) */ + /* run commands from a file (--debug=file or -D file) */ int fd; fd = open_readfd(command_file); if (fd == INVALID_HANDLE) { @@ -2928,14 +2921,14 @@ do_run(CMDARG *arg ATTRIBUTE_UNUSED, int cmd ATTRIBUTE_UNUSED) prog_running = TRUE; fatal_tag_valid = TRUE; if (setjmp(fatal_tag) == 0) - (void) r_interpret(code_block); + (void) interpret(code_block); fatal_tag_valid = FALSE; prog_running = FALSE; fprintf(out_fp, _("Program exited %s with exit value: %d\n"), - (! exiting && exit_val != EXIT_SUCCESS) ? "abnormally" - : "normally", - exit_val); + (! exiting && exit_val != EXIT_SUCCESS) ? "abnormally" + : "normally", + exit_val); need_restart = TRUE; return FALSE; } @@ -3135,7 +3128,6 @@ do_next(CMDARG *arg, int cmd) static int check_nexti(INSTRUCTION **pi) { - /* make sure not to step inside function calls */ if (fcall_count < stop.fcall_count) { @@ -3224,7 +3216,7 @@ check_return(INSTRUCTION **pi) int do_return(CMDARG *arg, int cmd) { - NODE *func; + NODE *func, *n; CHECK_PROG_RUNNING(); func = find_frame(cur_frame)->func_node; @@ -3241,12 +3233,11 @@ do_return(CMDARG *arg, int cmd) stop.check_func = check_return; - if (arg != NULL && arg->type == D_node) { /* optional return value */ - NODE *n; + if (arg != NULL && arg->type == D_node) /* optional return value */ n = dupnode(arg->a_node); - PUSH(n); - } else - PUSH(Nnull_string); + else + n = dupnode(Nnull_string); + PUSH(n); return TRUE; } @@ -3315,7 +3306,7 @@ do_until(CMDARG *arg, int cmd) s = source_find(arg->a_string); arg = arg->next; if (s == NULL || arg == NULL - || (arg->type != D_int && arg->type != D_func)) + || (arg->type != D_int && arg->type != D_func)) return FALSE; src = s->src; if (arg->type == D_func) @@ -3345,7 +3336,7 @@ func: } } fprintf(out_fp, _("Can't find specified location in function `%s'\n"), - func->lnode->param); + func->vname); /* fall through */ default: return FALSE; @@ -3614,7 +3605,6 @@ pre_execute(INSTRUCTION **pi) return TRUE; case Op_func: - case Op_ext_func: case Op_var_update: return TRUE; @@ -3649,7 +3639,7 @@ pre_execute(INSTRUCTION **pi) /* print_memory --- print a scalar value */ static void -print_memory(NODE *m, char **fparms, Func_print print_func, FILE *fp) +print_memory(NODE *m, NODE *func, Func_print print_func, FILE *fp) { switch (m->type) { case Node_val: @@ -3676,8 +3666,8 @@ print_memory(NODE *m, char **fparms, Func_print print_func, FILE *fp) break; case Node_param_list: - assert(fparms != NULL); - print_func(fp, "%s", fparms[m->param_cnt]); + assert(func != NULL); + print_func(fp, "%s", func->fparms[m->param_cnt].param); break; case Node_var: @@ -3696,9 +3686,8 @@ print_memory(NODE *m, char **fparms, Func_print print_func, FILE *fp) static void print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) { - static char **fparms = NULL; int pcount = 0; - NODE *func = NULL; + static NODE *func = NULL; static int noffset = 0; if (noffset == 0) { @@ -3709,25 +3698,17 @@ print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) if (pc->opcode == Op_func) { func = pc->func_body; - fparms = get_params(func); - pcount = get_param_count(func); + pcount = func->param_cnt; if (in_dump) { int j; - print_func(fp, "\n\t# Function: %s (", func->lnode->param); + print_func(fp, "\n\t# Function: %s (", func->vname); for (j = 0; j < pcount; j++) { - print_func(fp, "%s", fparms[j]); + print_func(fp, "%s", func->fparms[j].param); if (j < pcount - 1) print_func(fp, ", "); } print_func(fp, ")\n\n"); } - } else if (pc->opcode == Op_ext_func) { - func = pc->func_body; - fparms = get_params(func); - pcount = get_param_count(func); - if (in_dump) - print_func(fp, "\n\t# Extension function: %s (... %d params ...)\n\n", - func->lnode->param, pcount); } else if (pc->opcode == Op_rule) { if (in_dump) print_func(fp, "\n\t# %s\n\n", ruletab[pc->in_rule]); @@ -3743,12 +3724,8 @@ print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) pc->source_line, pc, opcode2str(pc->opcode)); if (prog_running && ! in_dump) { - /* find params in the current frame */ + /* find Node_func if in function */ func = find_frame(0)->func_node; - if (func != NULL) - fparms = get_params(func); - /* else - fparms = NULL; */ } @@ -3774,10 +3751,6 @@ print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) pc->target_assign, pc->do_reference ? "TRUE" : "FALSE"); break; - case Op_ext_func: - print_func(fp, "[param_cnt = %d]\n", pcount); - break; - case Op_func: print_func(fp, "[param_cnt = %d] [source_file = %s]\n", pcount, pc->source_file ? pc->source_file : "cmd. line"); @@ -3852,7 +3825,7 @@ print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) case Op_arrayfor_incr: print_func(fp, "[array_var = %s] [target_jmp = %p]\n", pc->array_var->type == Node_param_list ? - fparms[pc->array_var->param_cnt] : pc->array_var->vname, + func->fparms[pc->array_var->param_cnt].param : pc->array_var->vname, pc->target_jmp); break; @@ -3887,13 +3860,13 @@ print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) break; case Op_builtin: - { - const char *fname = getfname(pc->builtin); - if (fname == NULL) - print_func(fp, "(extension func) [arg_count = %ld]\n", pc->expr_count); - else - print_func(fp, "%s [arg_count = %ld]\n", fname, pc->expr_count); - } + print_func(fp, "%s [arg_count = %ld]\n", getfname(pc->builtin), + pc->expr_count); + break; + + case Op_ext_builtin: + print_func(fp, "%s [arg_count = %ld]\n", (pc + 1)->func_name, + pc->expr_count); break; case Op_subscript: @@ -3902,7 +3875,7 @@ print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) break; case Op_store_sub: - print_memory(pc->memory, fparms, print_func, fp); + print_memory(pc->memory, func, print_func, fp); print_func(fp, " [sub_count = %ld]\n", pc->expr_count); break; @@ -3945,9 +3918,17 @@ print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) print_func(fp, "[exec_count = %ld]\n", pc->exec_count); break; - case Op_store_var: + case Op_store_var: + print_memory(pc->memory, func, print_func, fp); + if (pc->initval != NULL) { + print_func(fp, " = "); + print_memory(pc->initval, func, print_func, fp); + } + print_func(fp, "\n"); + break; + case Op_push_lhs: - print_memory(pc->memory, fparms, print_func, fp); + print_memory(pc->memory, func, print_func, fp); print_func(fp, " [do_reference = %s]\n", pc->do_reference ? "TRUE" : "FALSE"); break; @@ -3968,7 +3949,7 @@ print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) case Op_quotient_i: case Op_mod_i: case Op_assign_concat: - print_memory(pc->memory, fparms, print_func, fp); + print_memory(pc->memory, func, print_func, fp); /* fall through */ default: print_func(fp, "\n"); @@ -4006,7 +3987,8 @@ int do_dump_instructions(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) { FILE *fp; - + NODE **funcs; + if (arg != NULL && arg->type == D_string) { /* dump to a file */ if ((fp = fopen(arg->a_string, "w")) == NULL) { @@ -4018,25 +4000,27 @@ do_dump_instructions(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) pf_data.fp = fp; pf_data.defn = TRUE; /* in_dump = TRUE */ (void) print_code(code_block, &pf_data); - (void) foreach_func((int (*)(INSTRUCTION *, void *)) print_code, - FALSE, /* sort */ - &pf_data /* data */ - ); + funcs = function_list(TRUE); + (void) foreach_func(funcs, + (int (*)(INSTRUCTION *, void *)) print_code, + &pf_data); + efree(funcs); fclose(fp); return FALSE; } + funcs = function_list(TRUE); initialize_pager(out_fp); if (setjmp(pager_quit_tag) == 0) { pf_data.print_func = gprintf; pf_data.fp = out_fp; pf_data.defn = TRUE; /* in_dump = TRUE */ (void) print_code(code_block, &pf_data); - (void) foreach_func((int (*)(INSTRUCTION *, void *)) print_code, - FALSE, /* sort */ - &pf_data /* data */ - ); + (void) foreach_func(funcs, + (int (*)(INSTRUCTION *, void *)) print_code, + &pf_data); } + efree(funcs); return FALSE; } @@ -4958,7 +4942,7 @@ do_print_f(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) goto done; for (; cnt > 0; cnt--) { - NODE *value, *subs; + NODE *value, *subs; a = a->next; subs = a->a_node; value = in_array(r, subs); @@ -4996,7 +4980,7 @@ do_print_f(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) } } - force_string(tmp[0]); + tmp[0] = force_string(tmp[0]); PUSH_BINDING(fatal_tag_stack, fatal_tag, fatal_tag_valid); if (setjmp(fatal_tag) == 0) @@ -5339,35 +5323,6 @@ close_all() set_gawk_output(NULL); /* closes output_fp if not stdout */ } -/* install_params --- install function parameters into the symbol table */ - -static void -install_params(NODE *func) -{ - NODE *np; - - if (func == NULL) - return; - /* function parameters of type Node_param_list */ - np = func->lnode; - for (np = np->rnode; np != NULL; np = np->rnode) - install_symbol(np->param, np); -} - -/* remove_params --- remove function parameters out of the symbol table */ - -static void -remove_params(NODE *func) -{ - NODE *np; - - if (func == NULL) - return; - np = func->lnode; - for (np = np->rnode; np != NULL; np = np->rnode) - remove_symbol(np->param); -} - /* pre_execute_code --- pre_hook for execute_code, called by pre_execute */ static int @@ -5410,22 +5365,25 @@ execute_code(volatile INSTRUCTION *code) volatile NODE *r = NULL; volatile jmp_buf fatal_tag_stack; long save_stack_size; + int save_flags = do_flags; /* We use one global stack for all contexts. * Save # of items in stack; in case of * a fatal error, pop stack until it has that many items. */ + save_stack_size = (stack_ptr - stack_bottom) + 1; + do_flags = FALSE; PUSH_BINDING(fatal_tag_stack, fatal_tag, fatal_tag_valid); if (setjmp(fatal_tag) == 0) { - (void) r_interpret((INSTRUCTION *) code); + (void) interpret((INSTRUCTION *) code); r = POP_SCALAR(); } else /* fatal error */ (void) unwind_stack(save_stack_size); POP_BINDING(fatal_tag_stack, fatal_tag, fatal_tag_valid); - + do_flags = save_flags; if (exit_val != EXIT_SUCCESS) { /* must be EXIT_FATAL? */ exit_val = EXIT_SUCCESS; return NULL; @@ -5444,9 +5402,9 @@ do_eval(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) NODE **sp; INSTRUCTION *eval, *code = NULL; AWK_CONTEXT *ctxt; - char **save_parmlist = NULL; int ecount = 0, pcount = 0; int ret; + int save_flags = do_flags; if (prog_running) { this_frame = find_frame(0); @@ -5458,7 +5416,9 @@ do_eval(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) ctxt->install_func = append_symbol; /* keep track of newly installed globals */ push_context(ctxt); (void) add_srcfile(SRC_CMDLINE, arg->a_string, srcfiles, NULL, NULL); + do_flags = FALSE; ret = parse_program(&code); + do_flags = save_flags; remove_params(this_func); if (ret != 0) { pop_context(); /* switch to prev context */ @@ -5480,9 +5440,7 @@ do_eval(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) } else { /* execute as a part of the current function */ int i; - char **varnames; INSTRUCTION *t; - NODE *np; eval = f->code_ptr; /* Op_func */ eval->source_file = cur_srcfile->src; @@ -5491,9 +5449,8 @@ do_eval(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) t->opcode = Op_stop; /* add or append eval locals to the current frame stack */ - ecount = f->lnode->param_cnt; /* eval local count */ - pcount = this_func->lnode->param_cnt; - save_parmlist = this_func->parmlist; + ecount = f->param_cnt; /* eval local count */ + pcount = this_func->param_cnt; if (ecount > 0) { if (pcount == 0) @@ -5501,26 +5458,22 @@ do_eval(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) else erealloc(this_frame->stack, NODE **, (pcount + ecount) * sizeof(NODE *), "do_eval"); - emalloc(varnames, char **, (pcount + ecount + 1) * sizeof(char *), "do_eval"); - if (pcount > 0) - memcpy(varnames, save_parmlist, pcount * sizeof(char *)); - for (np = f->lnode->rnode, i = 0; np != NULL; np = np->rnode, i++) { - varnames[pcount + i] = np->param; - np->param_cnt += pcount; /* appending eval locals: fixup param_cnt */ - } - varnames[pcount + ecount] = NULL; sp = this_frame->stack + pcount; for (i = 0; i < ecount; i++) { + NODE *np; + + np = f->fparms + i; + np->param_cnt += pcount; /* appending eval locals: fixup param_cnt */ + getnode(r); memset(r, 0, sizeof(NODE)); *sp++ = r; /* local variable */ r->type = Node_var_new; - r->vname = varnames[pcount + i]; + r->vname = np->param; } - this_func->parmlist = varnames; - this_func->lnode->param_cnt += ecount; + this_func->param_cnt += ecount; } } @@ -5560,19 +5513,17 @@ do_eval(CMDARG *arg, int cmd ATTRIBUTE_UNUSED) } /* else restore_frame() will free it */ - efree(this_func->parmlist); - this_func->parmlist = save_parmlist; - this_func->lnode->param_cnt -= ecount; + this_func->param_cnt -= ecount; } /* always destroy symbol "@eval", however destroy all newly installed - * globals only if fatal error in r_interpret (r == NULL). + * globals only if fatal error (execute_code() returing NULL). */ pop_context(); /* switch to prev context */ free_context(ctxt, (ret_val != NULL)); /* free all instructions and optionally symbols */ if (ret_val != NULL) - destroy_symbol("@eval"); /* destroy "@eval" */ + destroy_symbol(f); /* destroy "@eval" */ return FALSE; } @@ -5589,13 +5540,13 @@ an error message: static int invalid_symbol = 0; -void -check_symbol(char *name) +static void +check_symbol(NODE *r) { invalid_symbol++; - d_error(_("No symbol `%s' in current context"), name); + d_error(_("No symbol `%s' in current context"), r->vname); /* install anyway, but keep track of it */ - append_symbol(name); + append_symbol(r); } /* parse_condition --- compile a condition expression */ @@ -5611,6 +5562,7 @@ parse_condition(int type, int num, char *expr) NODE *this_func = NULL; INSTRUCTION *it, *stop, *rule; struct condition *cndn = NULL; + int save_flags = do_flags; if (type == D_break && (b = find_breakpoint(num)) != NULL) { INSTRUCTION *rp; @@ -5634,7 +5586,9 @@ parse_condition(int type, int num, char *expr) ctxt->install_func = check_symbol; push_context(ctxt); (void) add_srcfile(SRC_CMDLINE, expr, srcfiles, NULL, NULL); + do_flags = FALSE; ret = parse_program(&code); + do_flags = save_flags; remove_params(this_func); pop_context(); @@ -5654,8 +5608,8 @@ parse_condition(int type, int num, char *expr) it = rule->firsti; /* Op_K_print_rec */ assert(it->opcode == Op_K_print_rec); - it->opcode = Op_push_i; - it->memory = mk_number((AWKNUM) 1.0, PERM|NUMBER|NUMCUR); + it->opcode = Op_push_i; + it->memory = make_number(1.0); it->nexti = bcalloc(Op_jmp, 1, 0); it->nexti->target_jmp = stop; it->nexti->nexti = rule->lasti; @@ -5663,7 +5617,7 @@ parse_condition(int type, int num, char *expr) it = rule->lasti; /* Op_no_op, target for Op_jmp_false */ assert(it->opcode == Op_no_op); it->opcode = Op_push_i; - it->memory = mk_number((AWKNUM) 0.0, PERM|NUMBER|NUMCUR); + it->memory = make_number(0.0); it->nexti = stop; out: @@ -5743,7 +5697,7 @@ push_cmd_src( cs->str = NULL; cs->next = cmd_src; cmd_src = cs; - + input_fd = fd; input_from_tty = istty; read_a_line = readfunc; diff --git a/doc/ChangeLog b/doc/ChangeLog index 2921a919..eb9bbf45 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,15 @@ +2011-12-28 Arnold D. Robbins <arnold@skeeve.com> + + * awkcard.in, gawk.1: Minor edits after merge of executables. + +2011-12-21 John Haque <j.eh@mchsi.com> + + * gawk.texi: Updated sections on profiling and debugging + after merging the exes. Document new options --debug and + --load, and add a sub-section on loading extension library. + * gawk.1: Same. + * awkcard.in: Same. + 2011-12-06 Arnold D. Robbins <arnold@skeeve.com> * gawk.texi: Various typo fixes from mailing list. @@ -22,6 +34,10 @@ Jeroen Schot <schot@A-Eskwadraat.nl>. * gawk.texi: Some minor fixes. +2011-08-31 John Haque <j.eh@mchsi.com> + + * gawk.texi: Updated gawk dynamic extension doc. + 2011-07-28 Arnold D. Robbins <arnold@skeeve.com> * gawk.texi (Gory Details): Restore text on historical behavior diff --git a/doc/awkcard.in b/doc/awkcard.in index 1c4d1910..c8d41833 100644 --- a/doc/awkcard.in +++ b/doc/awkcard.in @@ -25,7 +25,6 @@ .\" Strings to save typing .ds AK \*(FCawk\*(FR .ds GK \*(FCgawk\*(FR -.ds PK \*(FCpgawk\*(FR .ds NK Bell Labs \*(FCawk\*(FR .ds MK \*(FCmawk\*(FR .\" @@ -76,7 +75,7 @@ Pattern Elements 8 Printf Formats 14 Records 10 Regular Expressions 11 -Signals (\*(PK) 4 +Signals (\*(GK \*(FC\-\^\-profile\*(FR) 4 Special Filenames 12 String Functions 16 Time Functions (\*(GK) 17 @@ -253,6 +252,9 @@ their types and final values to If no \*(FIfile\*(FR is provided, \*(FCgawk\*(FR uses \*(FCawkvars.out\*(FR. +.TI "\*(FC\-D\*(FR[\*(FC\*(FIfile\*(FR], \*(FC\-\^\-debug\*(FR[\*(FC=\*(FIfile\*(FR] +Enable debugging of program. Optionally read stored commands +from \*(FIfile\*(FR. .TI "\*(FC-e '\*(FItext\*(FC'\*(FR, \*(FC\-\^\-source '\*(FItext\*(FC'\*(FR Use \*(FItext\*(FR as AWK program source code. .TI "\*(FC\-E \*(FIfile\*(FR, \*(FC\-\^\-exec \*(FIfile\*(FR @@ -268,6 +270,8 @@ for localization. .TI "\*(FC\-h\*(FR, \*(FC\-\^\-help\*(FR Print a short summary of the available options on \*(FCstdout\*(FR, then exit zero. +.TI "\*(FC\-l \*(FIlib\*(FR, \*(FC\-\^\-load \*(FIlib\*(FR +Load dynamic extension \*(FIlib\fP. .TI "\*(FC\-L \*(FR[\*(FC\*(FIvalue\*(FR], \*(FC\-\^\-lint\*(FR[\*(FC=\*(FIvalue\*(FR] Warn about dubious or non-portable constructs. If \*(FIvalue\*(FR is @@ -282,31 +286,23 @@ Recognize octal and hexadecimal values in input data. \*(FIUse this option with great caution!\*(FR .TI "\*(FC\-N\*(FR, \*(FC\-\^\-use\-lc\-numeric\*(FR Force use of the locale's decimal point character when parsing input data. +.TI "\*(FC\-o\*(FR[\*(FC\*(FIfile\*(FR], \*(FC\-\^\-pretty-print\*(FR[\*(FC=\*(FIfile\*(FR] +Output a pretty printed version of the program to \*(FIfile\*(FR +(default: \*(FCawkprof.out\*(FR). .TI "\*(FC\-O\*(FR, \*(FC\-\^\-optimize\*(FR Enable some internal optimizations. .TI "\*(FC\-p\*(FR[\*(FC\*(FIfile\*(FR], \*(FC\-\^\-profile\*(FR[\*(FC=\*(FIfile\*(FR] Send profiling data to \*(FIfile\*(FR (default: \*(FCawkprof.out\*(FR). -With \*(GK, -the profile is just a ``pretty printed'' version of the program. -With \*(PK, -the profile contains execution counts in the left margin +The profile contains execution counts in the left margin of each statement in the program. .TI "\*(FC\-P\*(FR, \*(FC\-\^\-posix\*(FR Disable common and GNU extensions. -.TI "\*(FC\-R \*(FIfile\*(FR, \*(FC\-\^\-command \*(FIfile\*(FR" -\*(FCdgawk\*(FR only. -Read stored debugger commands from \*(FIfile\*(FR. .TI "\*(FC\-r\*(FR, \*(FC\-\^\-re\-interval\*(FR Enable \*(FIinterval expressions\*(FR in regular expression matching (see \fHRegular Expressions\fP below). Useful if -\*(FC\-\^\-traditional\*(FR is specified. -.TI "\*(FC\-S\*(FR, \*(FC\-\^\-sandbox\*(FR -Disable the \*(FCsystem()\*(FR function, -input redirection with \*(FCgetline\*(FR, -output redirection with \*(FCprint\*(FR and \*(FCprintf\*(FR, -and dynamic extensions loading.\*(CB +\*(FC\-\^\-traditional\*(FR is specified.\*(CB .in -4n .EB "\s+2\f(HBCOMMAND LINE ARGUMENTS (\*(GK\f(HB)\*(FR\s0" @@ -318,6 +314,11 @@ and dynamic extensions loading.\*(CB .ES .fi .in +4n +.TI "\*(FC\-S\*(FR, \*(FC\-\^\-sandbox\*(FR +Disable the \*(FCsystem()\*(FR function, +input redirection with \*(FCgetline\*(FR, +output redirection with \*(FCprint\*(FR and \*(FCprintf\*(FR, +and dynamic extensions loading. .TI "\*(FC-t\*(FR, \*(FC\-\^\-lint\-old\*(FR Warn about constructs that are not portable to the original version of @@ -382,14 +383,14 @@ The options may be abbreviated using just the first letter, e.g., and so on.\*(CB .EB "\s+2\f(HBCOMMAND LINE ARGUMENTS (\*(MK\f(HB)\*(FR\s0" .sp .7 -.\" --- Signals (pgawk) +.\" --- Signals (gawk --profile) .ES .fi -\*(CD\*(PK accepts two signals. +\*(CD\*(GK accepts two signals while profiling. \*(FCSIGUSR1\fP dumps a profile and function call stack to the profile file. It then continues to run. \*(FCSIGHUP\fP is similar, but exits.\*(CB -.EB "\s+2\f(HBSIGNALS (\*(PK\f(HB)\*(FR\s0" +.EB "\s+2\f(HBSIGNALS (\*(GK \f(HB\-\^\-profile)\*(FR\s0" .\" --- Lines And Statements .ES @@ -33,30 +33,6 @@ gawk \- pattern scanning and processing language ] .I program-text file .\|.\|. -.sp -.B pgawk -[ \*(PX or \*(GN style options ] -.B \-f -.I program-file -[ -.B \-\^\- -] file .\|.\|. -.br -.B pgawk -[ \*(PX or \*(GN style options ] -[ -.B \-\^\- -] -.I program-text -file .\|.\|. -.sp -.B dgawk -[ \*(PX or \*(GN style options ] -.B \-f -.I program-file -[ -.B \-\^\- -] file .\|.\|. .SH DESCRIPTION .I Gawk is the \*(GN Project's implementation of the \*(AK programming language. @@ -84,27 +60,31 @@ and .B ARGV pre-defined \*(AK variables. .PP -.I Pgawk -is the profiling version of -.IR gawk . -It is identical in every way to -.IR gawk , -except that programs run more slowly, -and it automatically produces an execution profile in the file +When +.I gawk +is invoked with the +.B \-\^\-profile +option, it starts gathering profiling statistics +from the execution of the program. +.I Gawk +runs more slowly in this mode, and automatically produces an execution +profile in the file .B awkprof.out when done. See the .B \-\^\-profile option, below. .PP -.I Dgawk -is an -.I awk -debugger. Instead of running the program directly, it loads the +.I Gawk +also has an integrated debugger. An interactive debugging session can +be started by supplying the +.B \-\^\-debug +option to the command line. In this mode of execution, +.I gawk +loads the AWK source code and then prompts for debugging commands. -Unlike -.IR gawk " and " pgawk ", " dgawk -only processes AWK program source provided with the +.I Gawk +can only debug AWK program source provided with the .B \-f option. The debugger is documented in \*(EP. @@ -117,8 +97,8 @@ while long options start with \*(lq\-\^\-\*(rq. Long options are provided for both \*(GN-specific features and for \*(PX-mandated features. .PP -.IR Gawk - -specific options are typically used in long-option form. +.IR Gawk -specific +options are typically used in long-option form. Arguments to long options are either joined with the option by an .B = @@ -273,6 +253,18 @@ names like and so on.) .TP .PD 0 +\fB\-D\fR[\fIfile\fR] +.TP +.PD +\fB\-\^\-debug\fR[\fB=\fIfile\fR] +Enable debugging of \*(AK programs. +By default, the debugger reads commands interactively from the terminal. +The optional +.IR file +argument can be used to specify a file with a list +of commands for the debugger to execute non-interactively. +.TP +.PD 0 .BI "\-e " program-text .TP .PD @@ -331,6 +323,21 @@ the standard output. these options cause an immediate, successful exit.) .TP .PD 0 +.BI "\-l " lib +.TP +.PD +.BI \-\^\-load " lib" +Load a shared library +.IR lib . +This searches for the library using the +.B AWKPATH +environment variable. The suffix +.I .so +in the library name is optional, and +the library initialization routine is expected to be named +.BR dlload() . +.TP +.PD 0 .BR "\-L " [ \fIvalue\fR ] .TP .PD @@ -387,12 +394,28 @@ users. .. .TP .PD 0 +\fB\-o\fR[\fIfile\fR] +.TP +.PD +\fB\-\^\-pretty-print\fR[\fB=\fIfile\fR] +Output a pretty printed version of the program to +.IR file . +If no +.I file +is provided, +.I gawk +uses a file named +.B awkprof.out +in the current directory. +.TP +.PD 0 .B \-O .TP .PD .B \-\^\-optimize Enable optimizations upon the internal representation of the program. -Currently, this includes just simple constant-folding. The +Currently, this includes simple constant-folding, and tail call +elimination for recursive functions. The .I gawk maintainer hopes to add additional optimizations over time. .TP @@ -401,16 +424,11 @@ maintainer hopes to add additional optimizations over time. .TP .PD \fB\-\^\-profile\fR[\fB=\fIprof_file\fR] -Send profiling data to +Start a profiling session, and send the profiling data to .IR prof_file . The default is .BR awkprof.out . -When run with -.IR gawk , -the profile is just a \*(lqpretty printed\*(rq version of the program. -When run with -.IR pgawk , -the profile contains execution counts of each statement in the program +The profile contains execution counts of each statement in the program in the left margin and function call counts for each user-defined function. .TP .PD 0 @@ -482,15 +500,6 @@ They are enabled by default, but this option remains for use with .BR \-\^-traditional . .TP .PD 0 -.B \-R -.TP -.PD -.BI \-\^\-command " file" -.I Dgawk -only. Read stored debugger commands from -.IR file . -.TP -.PD 0 .BI \-S .TP .PD @@ -3122,8 +3131,9 @@ Using this feature at the C level is not pretty, but it is unlikely to go away. Additional mechanisms may be added at some point. .SH SIGNALS -.I pgawk -accepts two signals. +The +.I gawk +profiler accepts two signals. .B SIGUSR1 causes it to dump a profile and function call stack to the profile file, which is either @@ -3133,7 +3143,7 @@ or whatever file was named with the option. It then continues to run. .B SIGHUP causes -.I pgawk +.I gawk to dump the profile and function call stack and then exit. .SH INTERNATIONALIZATION .PP diff --git a/doc/gawk.info b/doc/gawk.info index 8d9422f2..06b8e119 100644 --- a/doc/gawk.info +++ b/doc/gawk.info @@ -94,7 +94,7 @@ texts being (a) (see below), and with the Back-Cover Texts being (b) * Library Functions:: A Library of `awk' Functions. * Sample Programs:: Many `awk' programs with complete explanations. -* Debugger:: The `dgawk' debugger. +* Debugger:: The `gawk' debugger. * Language History:: The evolution of the `awk' language. * Installation:: Installing `gawk' under various @@ -417,23 +417,23 @@ texts being (a) (see below), and with the Back-Cover Texts being (b) * Anagram Program:: Finding anagrams from a dictionary. * Signature Program:: People do amazing things with too much time on their hands. -* Debugging:: Introduction to `dgawk'. -* Debugging Concepts:: Debugging In General. +* Debugging:: Introduction to `gawk' Debugger. +* Debugging Concepts:: Debugging in General. * Debugging Terms:: Additional Debugging Concepts. * Awk Debugging:: Awk Debugging. -* Sample dgawk session:: Sample `dgawk' session. -* dgawk invocation:: `dgawk' Invocation. -* Finding The Bug:: Finding The Bug. -* List of Debugger Commands:: Main `dgawk' Commands. -* Breakpoint Control:: Control of breakpoints. -* Dgawk Execution Control:: Control of execution. -* Viewing And Changing Data:: Viewing and changing data. -* Dgawk Stack:: Dealing with the stack. -* Dgawk Info:: Obtaining information about the program and - the debugger state. -* Miscellaneous Dgawk Commands:: Miscellaneous Commands. +* Sample Debugging Session:: Sample Debugging Session. +* Debugger Invocation:: How to Start the Debugger. +* Finding The Bug:: Finding the Bug. +* List of Debugger Commands:: Main Commands. +* Breakpoint Control:: Control of Breakpoints. +* Debugger Execution Control:: Control of Execution. +* Viewing And Changing Data:: Viewing and Changing Data. +* Execution Stack:: Dealing with the Stack. +* Debugger Info:: Obtaining Information about the Program and + the Debugger State. +* Miscellaneous Debugger Commands:: Miscellaneous Commands. * Readline Support:: Readline Support. -* Dgawk Limitations:: Limitations and future plans. +* Limitations:: Limitations and Future Plans. * V7/SVR3.1:: The major changes between V7 and System V Release 3.1. * SVR4:: Minor changes between System V Releases 3.1 @@ -489,6 +489,7 @@ texts being (a) (see below), and with the Back-Cover Texts being (b) * Internals:: A brief look at some `gawk' internals. * Plugin License:: A note about licensing. +* Loading Extensions:: How to load dynamic extensions. * Sample Library:: A example of new functions. * Internal File Description:: What the new functions will do. * Internal File Ops:: The code for internal file operations. @@ -849,7 +850,7 @@ and profile your `awk' programs. sample `awk' programs. Reading them allows you to see `awk' solving real problems. - *note Debugger::, describes the `awk' debugger, `dgawk'. + *note Debugger::, describes the `awk' debugger. *note Language History::, describes how the `awk' language has evolved since its first release to present. It also describes how @@ -2215,6 +2216,15 @@ The following list describes options mandated by the POSIX standard: particularly easy mistake to make with simple variable names like `i', `j', etc.) +`-D[FILE]' +`--debug=[FILE]' + Enable debugging of `awk' programs (*note Debugging::). By + default, the debugger reads commands interactively from the + terminal. The optional FILE argument allows you to specify a file + with a list of commands for the debugger to execute + non-interactively. No space is allowed between the `-D' and FILE, + if FILE is supplied. + `-e PROGRAM-TEXT' `--source PROGRAM-TEXT' Provide program source code in the PROGRAM-TEXT. This option @@ -2257,6 +2267,13 @@ The following list describes options mandated by the POSIX standard: Print a "usage" message summarizing the short and long style options that `gawk' accepts and then exit. +`-l LIB' +`--load LIB' + Load a shared library LIB. This searches for the library using the + `AWKPATH' environment variable. The suffix `.so' in the library + name is optional. The library initialization routine should be + named `dlload()'. + `-L [value]' `--lint[=value]' Warn about constructs that are dubious or nonportable to other @@ -2289,6 +2306,14 @@ The following list describes options mandated by the POSIX standard: Force the use of the locale's decimal point character when parsing numeric input data (*note Locales::). +`-o[FILE]' +`--pretty-print[=FILE]' + Enable pretty-printing of `awk' programs. By default, output + program is created in a file named `awkprof.out'. The optional + FILE argument allows you to specify a different file name for the + output. No space is allowed between the `-o' and FILE, if FILE is + supplied. + `-O' `--optimize' Enable some optimizations on the internal representation of the @@ -2304,10 +2329,9 @@ The following list describes options mandated by the POSIX standard: for the profile file. No space is allowed between the `-p' and FILE, if FILE is supplied. - When run with `gawk', the profile is just a "pretty printed" - version of the program. When run with `pgawk', the profile - contains execution counts for each statement in the program in the - left margin, and function call counts for each function. + The profile contains execution counts for each statement in the + program in the left margin, and function call counts for each + function. `-P' `--posix' @@ -2341,11 +2365,6 @@ The following list describes options mandated by the POSIX standard: remains both for backward compatibility, and for use in combination with the `--traditional' option. -`-R FILE' -`--command=FILE' - `dgawk' only. Read `dgawk' debugger options and commands from - FILE. *Note Dgawk Info::, for more information. - `-S' `--sandbox' Disable the `system()' function, input redirections with `getline', @@ -14115,25 +14134,22 @@ File: gawk.info, Node: Profiling, Prev: TCP/IP Networking, Up: Advanced Featu ================================== You may produce execution traces of your `awk' programs. This is done -with a specially compiled version of `gawk', called `pgawk' ("profiling -`gawk'"). - - `pgawk' is identical in every way to `gawk', except that when it has -finished running, it creates a profile of your program in a file named -`awkprof.out'. Because it is profiling, it also executes up to 45% +by passing the option `--profile' to `gawk'. When `gawk' has finished +running, it creates a profile of your program in a file named +`awkprof.out'. Because it is profiling, it also executes up to 45% slower than `gawk' normally does. As shown in the following example, the `--profile' option can be -used to change the name of the file where `pgawk' will write the -profile: +used to change the name of the file where `gawk' will write the profile: - pgawk --profile=myprog.prof -f myprog.awk data1 data2 + gawk --profile=myprog.prof -f myprog.awk data1 data2 -In the above example, `pgawk' places the profile in `myprog.prof' +In the above example, `gawk' places the profile in `myprog.prof' instead of in `awkprof.out'. Here is a sample session showing a simple `awk' program, its input -data, and the results from running `pgawk'. First, the `awk' program: +data, and the results from running `gawk' with the `--profile' option. +First, the `awk' program: BEGIN { print "First BEGIN rule" } @@ -14169,9 +14185,9 @@ data, and the results from running `pgawk'. First, the `awk' program: foo junk - Here is the `awkprof.out' that results from running `pgawk' on this -program and data (this example also illustrates that `awk' programmers -sometimes have to work late): + Here is the `awkprof.out' that results from running the `gawk' +profiler on this program and data (this example also illustrates that +`awk' programmers sometimes have to work late): # gawk profile, created Sun Aug 13 00:00:15 2000 @@ -14260,14 +14276,14 @@ output. They are as follows: redirection. Similarly, if the target of a redirection isn't a scalar, it gets parenthesized. - * `pgawk' supplies leading comments in front of the `BEGIN' and - `END' rules, the pattern/action rules, and the functions. + * `gawk' supplies leading comments in front of the `BEGIN' and `END' + rules, the pattern/action rules, and the functions. The profiled version of your program may not look exactly like what -you typed when you wrote it. This is because `pgawk' creates the +you typed when you wrote it. This is because `gawk' creates the profiled version by "pretty printing" its internal representation of -the program. The advantage to this is that `pgawk' can produce a +the program. The advantage to this is that `gawk' can produce a standard representation. The disadvantage is that all source-code comments are lost, as are the distinctions among multiple `BEGIN', `END', `BEGINFILE', and `ENDFILE' rules. Also, things such as: @@ -14282,21 +14298,23 @@ come out as: which is correct, but possibly surprising. - Besides creating profiles when a program has completed, `pgawk' can + Besides creating profiles when a program has completed, `gawk' can produce a profile while it is running. This is useful if your `awk' program goes into an infinite loop and you want to see what has been -executed. To use this feature, run `pgawk' in the background: +executed. To use this feature, run `gawk' with the `--profile' option +in the background: - $ pgawk -f myprog & + $ gawk --profile -f myprog & [1] 13992 The shell prints a job number and process ID number; in this case, -13992. Use the `kill' command to send the `USR1' signal to `pgawk': +13992. Use the `kill' command to send the `USR1' signal to `gawk': $ kill -USR1 13992 As usual, the profiled version of the program is written to -`awkprof.out', or to a different file if you use the `--profile' option. +`awkprof.out', or to a different file if one specified with the +`--profile' option. Along with the regular profile, as shown earlier, the profile includes a trace of any active functions: @@ -14308,22 +14326,22 @@ includes a trace of any active functions: # 1. foo # -- main -- - You may send `pgawk' the `USR1' signal as many times as you like. + You may send `gawk' the `USR1' signal as many times as you like. Each time, the profile and function call trace are appended to the output profile file. - If you use the `HUP' signal instead of the `USR1' signal, `pgawk' + If you use the `HUP' signal instead of the `USR1' signal, `gawk' produces the profile and the function call trace and then exits. - When `pgawk' runs on MS-Windows systems, it uses the `INT' and -`QUIT' signals for producing the profile and, in the case of the `INT' -signal, `pgawk' exits. This is because these systems don't support the -`kill' command, so the only signals you can deliver to a program are -those generated by the keyboard. The `INT' signal is generated by the + When `gawk' runs on MS-Windows systems, it uses the `INT' and `QUIT' +signals for producing the profile and, in the case of the `INT' signal, +`gawk' exits. This is because these systems don't support the `kill' +command, so the only signals you can deliver to a program are those +generated by the keyboard. The `INT' signal is generated by the `Ctrl-<C>' or `Ctrl-<BREAK>' key, while the `QUIT' signal is generated by the `Ctrl-<\>' key. - Finally, regular `gawk' also accepts the `--profile' option. When + Finally, `gawk' also accepts another option `--pretty-print'. When called this way, `gawk' "pretty prints" the program into `awkprof.out', without any execution counts. @@ -18702,45 +18720,46 @@ supplies the following copyright terms: File: gawk.info, Node: Debugger, Next: Language History, Prev: Sample Programs, Up: Top -14 `dgawk': The `awk' Debugger -****************************** +14 Debugging `awk' Programs +*************************** It would be nice if computer programs worked perfectly the first time they were run, but in real life, this rarely happens for programs of any complexity. Thus, most programming languages have facilities available for "debugging" programs, and now `awk' is no exception. - The `dgawk' debugger is purposely modeled after the GNU Debugger + The `gawk' debugger is purposely modeled after the GNU Debugger (GDB) (http://www.gnu.org/software/gdb/) command-line debugger. If you -are familiar with GDB, learning `dgawk' is easy. +are familiar with GDB, learning how to use `gawk' for debugging your +program is easy. * Menu: -* Debugging:: Introduction to `dgawk'. -* Sample dgawk session:: Sample `dgawk' session. -* List of Debugger Commands:: Main `dgawk' Commands. -* Readline Support:: Readline Support. -* Dgawk Limitations:: Limitations and future plans. +* Debugging:: Introduction to `gawk' debugger. +* Sample Debugging Session:: Sample debugging session. +* List of Debugger Commands:: Main debugger commands. +* Readline Support:: Readline support. +* Limitations:: Limitations and future plans. -File: gawk.info, Node: Debugging, Next: Sample dgawk session, Up: Debugger +File: gawk.info, Node: Debugging, Next: Sample Debugging Session, Up: Debugger -14.1 Introduction to `dgawk' -============================ +14.1 Introduction to `gawk' Debugger +==================================== This minor node introduces debugging in general and begins the discussion of debugging in `gawk'. * Menu: -* Debugging Concepts:: Debugging In General. +* Debugging Concepts:: Debugging in General. * Debugging Terms:: Additional Debugging Concepts. * Awk Debugging:: Awk Debugging. File: gawk.info, Node: Debugging Concepts, Next: Debugging Terms, Up: Debugging -14.1.1 Debugging In General +14.1.1 Debugging in General --------------------------- (If you have used debuggers in other languages, you may want to skip @@ -18784,9 +18803,8 @@ File: gawk.info, Node: Debugging Terms, Next: Awk Debugging, Prev: Debugging ------------------------------------ Before diving in to the details, we need to introduce several important -concepts that apply to just about all debuggers, including `dgawk'. -The following list defines terms used throughout the rest of this -major node. +concepts that apply to just about all debuggers. The following list +defines terms used throughout the rest of this major node. "Stack Frame" Programs generally call functions during the course of their @@ -18804,11 +18822,11 @@ major node. needed to manage the call stack. This data area is termed a "stack frame". - `gawk' also follows this model, and `dgawk' gives you access to - the call stack and to each stack frame. You can see the call - stack, as well as from where each function on the stack was - invoked. Commands that print the call stack print information about - each stack frame (as detailed later on). + `gawk' also follows this model, and gives you access to the call + stack and to each stack frame. You can see the call stack, as well + as from where each function on the stack was invoked. Commands + that print the call stack print information about each stack frame + (as detailed later on). "Breakpoint" During debugging, you often wish to let the program run until it @@ -18853,54 +18871,57 @@ individual primitive instructions carried out by the higher-level `awk' commands. -File: gawk.info, Node: Sample dgawk session, Next: List of Debugger Commands, Prev: Debugging, Up: Debugger +File: gawk.info, Node: Sample Debugging Session, Next: List of Debugger Commands, Prev: Debugging, Up: Debugger -14.2 Sample `dgawk' session -=========================== +14.2 Sample Debugging Session +============================= -In order to illustrate the use of `dgawk', let's look at a sample -debugging session. We will use the `awk' implementation of the POSIX -`uniq' command described earlier (*note Uniq Program::) as our example. +In order to illustrate the use of `gawk' as a debugger, let's look at a +sample debugging session. We will use the `awk' implementation of the +POSIX `uniq' command described earlier (*note Uniq Program::) as our +example. * Menu: -* dgawk invocation:: `dgawk' Invocation. -* Finding The Bug:: Finding The Bug. +* Debugger Invocation:: How to Start the Debugger. +* Finding The Bug:: Finding the Bug. -File: gawk.info, Node: dgawk invocation, Next: Finding The Bug, Up: Sample dgawk session +File: gawk.info, Node: Debugger Invocation, Next: Finding The Bug, Up: Sample Debugging Session -14.2.1 `dgawk' Invocation -------------------------- +14.2.1 How to Start the Debugger +-------------------------------- -Starting `dgawk' is exactly like running `awk'. The file(s) containing -the program and any supporting code are given on the command line as -arguments to one or more `-f' options. (`dgawk' is not designed to -debug command-line programs, only programs contained in files.) In our -case, we call `dgawk' like this: +Starting the debugger is almost exactly like running `awk', except you +have to pass an additional option `--debug' or the corresponding short +option `-D'. The file(s) containing the program and any supporting +code are given on the command line as arguments to one or more `-f' +options. (`gawk' is not designed to debug command-line programs, only +programs contained in files.) In our case, we invoke the debugger like +this: - $ dgawk -f getopt.awk -f join.awk -f uniq.awk inputfile + $ gawk -D -f getopt.awk -f join.awk -f uniq.awk inputfile where both `getopt.awk' and `uniq.awk' are in `$AWKPATH'. (Experienced users of GDB or similar debuggers should note that this syntax is -slightly different from what they are used to. With `dgawk', the -arguments for running the program are given in the command line to the -debugger rather than as part of the `run' command at the debugger +slightly different from what they are used to. With `gawk' debugger, +the arguments for running the program are given in the command line to +the debugger rather than as part of the `run' command at the debugger prompt.) Instead of immediately running the program on `inputfile', as `gawk' -would ordinarily do, `dgawk' merely loads all the program source files, -compiles them internally, and then gives us a prompt: +would ordinarily do, the debugger merely loads all the program source +files, compiles them internally, and then gives us a prompt: - dgawk> + gawk> from which we can issue commands to the debugger. At this point, no code has been executed. -File: gawk.info, Node: Finding The Bug, Prev: dgawk invocation, Up: Sample dgawk session +File: gawk.info, Node: Finding The Bug, Prev: Debugger Invocation, Up: Sample Debugging Session -14.2.2 Finding The Bug +14.2.2 Finding the Bug ---------------------- Let's say that we are having a problem using (a faulty version of) @@ -18930,27 +18951,27 @@ for a breakpoint in `uniq.awk' is at the beginning of the function `are_equal()', which compares the current line with the previous one. To set the breakpoint, use the `b' (breakpoint) command: - dgawk> b are_equal + gawk> b are_equal -| Breakpoint 1 set at file `awklib/eg/prog/uniq.awk', line 64 The debugger tells us the file and line number where the breakpoint is. Now type `r' or `run' and the program runs until it hits the breakpoint for the first time: - dgawk> r + gawk> r -| Starting program: -| Stopping in Rule ... -| Breakpoint 1, are_equal(n, m, clast, cline, alast, aline) at `awklib/eg/prog/uniq.awk':64 -| 64 if (fcount == 0 && charcount == 0) - dgawk> + gawk> Now we can look at what's going on inside our program. First of all, let's see how we got to where we are. At the prompt, we type `bt' -(short for "backtrace"), and `dgawk' responds with a listing of the -current stack frames: +(short for "backtrace"), and the debugger responds with a listing of +the current stack frames: - dgawk> bt + gawk> bt -| #0 are_equal(n, m, clast, cline, alast, aline) at `awklib/eg/prog/uniq.awk':69 -| #1 in main() at `awklib/eg/prog/uniq.awk':89 @@ -18964,9 +18985,9 @@ the key to finding the source of the problem.) Now that we're in `are_equal()', we can start looking at the values of some variables. Let's say we type `p n' (`p' is short for "print"). We would expect to see the value of `n', a parameter to `are_equal()'. -Actually, `dgawk' gives us: +Actually, the debugger gives us: - dgawk> p n + gawk> p n -| n = untyped variable In this case, `n' is an uninitialized local variable, since the @@ -18974,13 +18995,13 @@ function was called without arguments (*note Function Calls::). A more useful variable to display might be the current record: - dgawk> p $0 + gawk> p $0 -| $0 = string ("gawk is a wonderful program!") This might be a bit puzzling at first since this is the second line of our test input above. Let's look at `NR': - dgawk> p NR + gawk> p NR -| NR = number (2) So we can see that `are_equal()' was only called for the second record @@ -18994,7 +19015,7 @@ for `NR == 1': OK, let's just check that that rule worked correctly: - dgawk> p last + gawk> p last -| last = string ("awk is a wonderful program!") Everything we have done so far has verified that the program has @@ -19003,7 +19024,7 @@ the problem must be inside this function. To investigate further, we must begin "stepping through" the lines of `are_equal()'. We start by typing `n' (for "next"): - dgawk> n + gawk> n -| 67 if (fcount > 0) { This tells us that `gawk' is now ready to execute line 67, which @@ -19019,15 +19040,15 @@ was false.) Continuing to step, we now get to the splitting of the current and last records: - dgawk> n + gawk> n -| 68 n = split(last, alast) - dgawk> n + gawk> n -| 69 m = split($0, aline) At this point, we should be curious to see what our records were split into, so we try to look: - dgawk> p n m alast aline + gawk> p n m alast aline -| n = number (5) -| m = number (5) -| alast = array, 5 elements @@ -19043,19 +19064,19 @@ want to see inside the array? The first choice would be to use subscripts: - dgawk> p alast[0] + gawk> p alast[0] -| "0" not in array `alast' Oops! - dgawk> p alast[1] + gawk> p alast[1] -| alast["1"] = string ("awk") - This would be kind of slow for a 100-member array, though, so -`dgawk' provides a shortcut (reminiscent of another language not to be + This would be kind of slow for a 100-member array, though, so `gawk' +provides a shortcut (reminiscent of another language not to be mentioned): - dgawk> p @alast + gawk> p @alast -| alast["1"] = string ("awk") -| alast["2"] = string ("is") -| alast["3"] = string ("a") @@ -19064,9 +19085,9 @@ mentioned): It looks like we got this far OK. Let's take another step or two: - dgawk> n + gawk> n -| 70 clast = join(alast, fcount, n) - dgawk> n + gawk> n -| 71 cline = join(aline, fcount, m) Well, here we are at our error (sorry to spoil the suspense). What @@ -19074,7 +19095,7 @@ we had in mind was to join the fields starting from the second one to make the virtual record to compare, and if the first field was numbered zero, this would work. Let's look at what we've got: - dgawk> p cline clast + gawk> p cline clast -| cline = string ("gawk is a wonderful program!") -| clast = string ("awk is a wonderful program!") @@ -19082,9 +19103,9 @@ zero, this would work. Let's look at what we've got: unaltered, input records. A little thinking (the human brain is still the best debugging tool), and we realize that we were off by one! - We get out of `dgawk': + We get out of the debugger: - dgawk> q + gawk> q -| The program is running. Exit anyway (y/n)? y Then we get into an editor: @@ -19095,12 +19116,13 @@ Then we get into an editor: and problem solved! -File: gawk.info, Node: List of Debugger Commands, Next: Readline Support, Prev: Sample dgawk session, Up: Debugger +File: gawk.info, Node: List of Debugger Commands, Next: Readline Support, Prev: Sample Debugging Session, Up: Debugger -14.3 Main `dgawk' Commands -========================== +14.3 Main Debugger Commands +=========================== -The `dgawk' command set can be divided into the following categories: +The `gawk' debugger command set can be divided into the following +categories: * Breakpoint control @@ -19116,26 +19138,26 @@ The `dgawk' command set can be divided into the following categories: Each of these are discussed in the following subsections. In the following descriptions, commands which may be abbreviated show the -abbreviation on a second description line. A `dgawk' command name may -also be truncated if that partial name is unambiguous. `dgawk' has the -built-in capability to automatically repeat the previous command when -just hitting <Enter>. This works for the commands `list', `next', +abbreviation on a second description line. A debugger command name may +also be truncated if that partial name is unambiguous. The debugger has +the built-in capability to automatically repeat the previous command +when just hitting <Enter>. This works for the commands `list', `next', `nexti', `step', `stepi' and `continue' executed without any argument. * Menu: -* Breakpoint Control:: Control of breakpoints. -* Dgawk Execution Control:: Control of execution. -* Viewing And Changing Data:: Viewing and changing data. -* Dgawk Stack:: Dealing with the stack. -* Dgawk Info:: Obtaining information about the program and - the debugger state. -* Miscellaneous Dgawk Commands:: Miscellaneous Commands. +* Breakpoint Control:: Control of Breakpoints. +* Debugger Execution Control:: Control of Execution. +* Viewing And Changing Data:: Viewing and Changing Data. +* Execution Stack:: Dealing with the Stack. +* Debugger Info:: Obtaining Information about the Program and + the Debugger State. +* Miscellaneous Debugger Commands:: Miscellaneous Commands. -File: gawk.info, Node: Breakpoint Control, Next: Dgawk Execution Control, Up: List of Debugger Commands +File: gawk.info, Node: Breakpoint Control, Next: Debugger Execution Control, Up: List of Debugger Commands -14.3.1 Control Of Breakpoints +14.3.1 Control of Breakpoints ----------------------------- As we saw above, the first thing you probably want to do in a debugging @@ -19163,10 +19185,10 @@ controlling breakpoints are: it from the breakpoint list using the `delete' command. With a breakpoint, you may also supply a condition. This is an - `awk' expression (enclosed in double quotes) that `dgawk' + `awk' expression (enclosed in double quotes) that the debugger evaluates whenever the breakpoint is reached. If the condition is - true, then `dgawk' stops execution and prompts for a command. - Otherwise, `dgawk' continues executing the program. + true, then the debugger stops execution and prompts for a command. + Otherwise, it continues executing the program. `clear' [[FILENAME`:']N | FUNCTION] Without any argument, delete any breakpoint at the next instruction @@ -19188,12 +19210,13 @@ controlling breakpoints are: `condition' N `"EXPRESSION"' Add a condition to existing breakpoint or watchpoint N. The - condition is an `awk' expression that `dgawk' evaluates whenever - the breakpoint or watchpoint is reached. If the condition is true, - then `dgawk' stops execution and prompts for a command. Otherwise, - `dgawk' continues executing the program. If the condition - expression is not specified, any existing condition is removed; - i.e., the breakpoint or watchpoint is made unconditional. + condition is an `awk' expression that the debugger evaluates + whenever the breakpoint or watchpoint is reached. If the condition + is true, then the debugger stops execution and prompts for a + command. Otherwise, the debugger continues executing the program. + If the condition expression is not specified, any existing + condition is removed; i.e., the breakpoint or watchpoint is made + unconditional. `delete' [N1 N2 ...] [N-M] `d' [N1 N2 ...] [N-M] @@ -19227,7 +19250,7 @@ controlling breakpoints are: arguments are the same as for `break'. -File: gawk.info, Node: Dgawk Execution Control, Next: Viewing And Changing Data, Prev: Breakpoint Control, Up: List of Debugger Commands +File: gawk.info, Node: Debugger Execution Control, Next: Viewing And Changing Data, Prev: Breakpoint Control, Up: List of Debugger Commands 14.3.2 Control of Execution --------------------------- @@ -19250,14 +19273,14 @@ execution of the program than we saw in our earlier example: `continue') terminates the list (an implicit `end'), and subsequent commands are ignored. For example: - dgawk> commands + gawk> commands > silent > printf "A silent breakpoint; i = %d\n", i > info locals > set i = 10 > continue > end - dgawk> + gawk> `continue' [COUNT] `c' [COUNT] @@ -19290,9 +19313,9 @@ execution of the program than we saw in our earlier example: `run' `r' - Start/restart execution of the program. When restarting, `dgawk' - retains the current breakpoints, watchpoints, command history, - automatic display variables, and debugger options. + Start/restart execution of the program. When restarting, the + debugger retains the current breakpoints, watchpoints, command + history, automatic display variables, and debugger options. `step' [COUNT] `s' [COUNT] @@ -19307,7 +19330,7 @@ execution of the program than we saw in our earlier example: Execute one (or COUNT) instruction(s), stepping inside function calls. (For illustration of what is meant by an "instruction" in `gawk', see the output shown under `dump' in *note Miscellaneous - Dgawk Commands::.) + Debugger Commands::.) `until' [[FILENAME`:']N | FUNCTION] `u' [[FILENAME`:']N | FUNCTION] @@ -19317,7 +19340,7 @@ execution of the program than we saw in our earlier example: current stack frame returns. -File: gawk.info, Node: Viewing And Changing Data, Next: Dgawk Stack, Prev: Dgawk Execution Control, Up: List of Debugger Commands +File: gawk.info, Node: Viewing And Changing Data, Next: Execution Stack, Prev: Debugger Execution Control, Up: List of Debugger Commands 14.3.3 Viewing and Changing Data -------------------------------- @@ -19329,7 +19352,7 @@ The commands for viewing and changing variables inside of `gawk' are: of the variable or field is displayed each time the program stops. Each variable added to the list is identified by a unique number: - dgawk> display x + gawk> display x -| 10: x = 1 displays the assigned item number, the variable name and its @@ -19357,7 +19380,7 @@ AWK STATEMENTS Print the value of a `gawk' variable or field. Fields must be referenced by constants: - dgawk> print $3 + gawk> print $3 This prints the third field in the input record (if the specified field does not exist, it prints `Null field'). A variable can be @@ -19385,16 +19408,16 @@ AWK STATEMENTS `watch' VAR | `$'N [`"EXPRESSION"'] `w' VAR | `$'N [`"EXPRESSION"'] - Add variable VAR (or field `$N') to the watch list. `dgawk' then - stops whenever the value of the variable or field changes. Each - watched item is assigned a number which can be used to delete it - from the watch list using the `unwatch' command. + Add variable VAR (or field `$N') to the watch list. The debugger + then stops whenever the value of the variable or field changes. + Each watched item is assigned a number which can be used to delete + it from the watch list using the `unwatch' command. With a watchpoint, you may also supply a condition. This is an - `awk' expression (enclosed in double quotes) that `dgawk' + `awk' expression (enclosed in double quotes) that the debugger evaluates whenever the watchpoint is reached. If the condition is - true, then `dgawk' stops execution and prompts for a command. - Otherwise, `dgawk' continues executing the program. + true, then the debugger stops execution and prompts for a command. + Otherwise, `gawk' continues executing the program. `undisplay' [N] Remove item number N (or all items, if no argument) from the @@ -19406,9 +19429,9 @@ AWK STATEMENTS -File: gawk.info, Node: Dgawk Stack, Next: Dgawk Info, Prev: Viewing And Changing Data, Up: List of Debugger Commands +File: gawk.info, Node: Execution Stack, Next: Debugger Info, Prev: Viewing And Changing Data, Up: List of Debugger Commands -14.3.4 Dealing With The Stack +14.3.4 Dealing with the Stack ----------------------------- Whenever you run a program which contains any function calls, `gawk' @@ -19443,16 +19466,17 @@ are: frame. Then select and print the frame. -File: gawk.info, Node: Dgawk Info, Next: Miscellaneous Dgawk Commands, Prev: Dgawk Stack, Up: List of Debugger Commands +File: gawk.info, Node: Debugger Info, Next: Miscellaneous Debugger Commands, Prev: Execution Stack, Up: List of Debugger Commands -14.3.5 Obtaining Information About The Program and The Debugger State +14.3.5 Obtaining Information about the Program and the Debugger State --------------------------------------------------------------------- Besides looking at the values of variables, there is often a need to get other sorts of information about the state of your program and of the -debugging environment itself. `dgawk' has one command which provides -this information, appropriately called `info'. `info' is used with one -of a number of arguments that tell it exactly what you want to know: +debugging environment itself. The `gawk' debugger has one command which +provides this information, appropriately called `info'. `info' is used +with one of a number of arguments that tell it exactly what you want to +know: `info' WHAT `i' WHAT @@ -19480,10 +19504,10 @@ of a number of arguments that tell it exactly what you want to know: `source' The name of the current source file. Each time the program stops, the current source file is the file containing the - current instruction. When `dgawk' first starts, the current - source file is the first file included via the `-f' option. - The `list FILENAME:LINENO' command can be used at any time to - change the current source. + current instruction. When the debugger first starts, the + current source file is the first file included via the `-f' + option. The `list FILENAME:LINENO' command can be used at any + time to change the current source. `sources' List all program sources. @@ -19507,7 +19531,7 @@ from a file. The commands are: `history_size' The maximum number of lines to keep in the history file - `./.dgawk_history'. The default is 100. + `./.gawk_history'. The default is 100. `listsize' The number of lines that `list' prints. The default is 15. @@ -19518,14 +19542,14 @@ from a file. The commands are: standard output. `prompt' - The debugger prompt. The default is `dgawk> '. + The debugger prompt. The default is `gawk> '. `save_history [on | off]' - Save command history to file `./.dgawk_history'. The default + Save command history to file `./.gawk_history'. The default is `on'. `save_options [on | off]' - Save current options to file `./.dgawkrc' upon exit. The + Save current options to file `./.gawkrc' upon exit. The default is `on'. Options are read back in to the next session upon startup. @@ -19543,15 +19567,15 @@ from a file. The commands are: ignored; they do _not_ repeat the last command. You can't restart the program by having more than one `run' command in the file. Also, the list of commands may include additional `source' - commands; however, `dgawk' will not source the same file more than - once in order to avoid infinite recursion. + commands; however, the `gawk' debugger will not source the same + file more than once in order to avoid infinite recursion. In addition to, or instead of the `source' command, you can use - the `-R FILE' or `--command=FILE' command-line options to execute + the `-D FILE' or `--debug=FILE' command-line options to execute commands from a file non-interactively (*note Options::. -File: gawk.info, Node: Miscellaneous Dgawk Commands, Prev: Dgawk Info, Up: List of Debugger Commands +File: gawk.info, Node: Miscellaneous Debugger Commands, Prev: Debugger Info, Up: List of Debugger Commands 14.3.6 Miscellaneous Commands ----------------------------- @@ -19567,7 +19591,7 @@ categories, as follows: partial dump of Davide Brini's obfuscated code (*note Signature Program::) demonstrates: - dgawk> dump + gawk> dump -| # BEGIN -| -| [ 2:0x89faef4] Op_rule : [in_rule = BEGIN] [source_file = brini.awk] @@ -19616,13 +19640,13 @@ categories, as follows: -| [ :0x89fa3b0] Op_after_beginfile : -| [ :0x89fa388] Op_no_op : -| [ :0x89fa3c4] Op_after_endfile : - dgawk> + gawk> `help' `h' - Print a list of all of the `dgawk' commands with a short summary - of their usage. `help COMMAND' prints the information about the - command COMMAND. + Print a list of all of the `gawk' debugger commands with a short + summary of their usage. `help COMMAND' prints the information + about the command COMMAND. `list' [`-' | `+' | N | FILENAME`:'N | N-M | FUNCTION] `l' [`-' | `+' | N | FILENAME`:'N | N-M | FUNCTION] @@ -19656,7 +19680,7 @@ categories, as follows: Exit the debugger. Debugging is great fun, but sometimes we all have to tend to other obligations in life, and sometimes we find the bug, and are free to go on to the next one! As we saw above, - if you are running a program, `dgawk' warns you if you + if you are running a program, the debugger warns you if you accidentally type `q' or `quit', to make sure you really want to quit. @@ -19671,12 +19695,12 @@ categories, as follows: -File: gawk.info, Node: Readline Support, Next: Dgawk Limitations, Prev: List of Debugger Commands, Up: Debugger +File: gawk.info, Node: Readline Support, Next: Limitations, Prev: List of Debugger Commands, Up: Debugger 14.4 Readline Support ===================== -If `dgawk' is compiled with the `readline' library, you can take +If `gawk' is compiled with the `readline' library, you can take advantage of that library's command completion and history expansion features. The following types of completion are available: @@ -19698,27 +19722,27 @@ Variable name completion -File: gawk.info, Node: Dgawk Limitations, Prev: Readline Support, Up: Debugger +File: gawk.info, Node: Limitations, Prev: Readline Support, Up: Debugger 14.5 Limitations and Future Plans ================================= -We hope you find `dgawk' useful and enjoyable to work with, but as with -any program, especially in its early releases, it still has some -limitations. A few which are worth being aware of are: +We hope you find the `gawk' debugger useful and enjoyable to work with, +but as with any program, especially in its early releases, it still has +some limitations. A few which are worth being aware of are: - * At this point, `dgawk' does not give a detailed explanation of + * At this point, the debugger does not give a detailed explanation of what you did wrong when you type in something it doesn't like. Rather, it just responds `syntax error'. When you do figure out what your mistake was, though, you'll feel like a real guru. - * If you perused the dump of opcodes in *note Miscellaneous Dgawk + * If you perused the dump of opcodes in *note Miscellaneous Debugger Commands::, (or if you are already familiar with `gawk' internals), you will realize that much of the internal manipulation of data in `gawk', as in many interpreters, is done on a stack. `Op_push', `Op_pop', etc., are the "bread and butter" of most `gawk' code. - Unfortunately, as of now, `dgawk' does not allow you to examine - the stack's contents. + Unfortunately, as of now, the `gawk' debugger does not allow you + to examine the stack's contents. That is, the intermediate results of expression evaluation are on the stack, but cannot be printed. Rather, only variables which @@ -19731,13 +19755,15 @@ limitations. A few which are worth being aware of are: expressions to see if you got it right. As an `awk' programmer, you are expected to know what `/[^[:alnum:][:blank:]]/' means. - * `dgawk' is designed to be used by running a program (with all its - parameters) on the command line, as described in *note dgawk - invocation::. There is no way (as of now) to attach or "break in" - to a running program. This seems reasonable for a language which - is used mainly for quickly executing, short programs. + * The `gawk' debugger is designed to be used by running a program + (with all its parameters) on the command line, as described in + *note Debugger Invocation::. There is no way (as of now) to + attach or "break in" to a running program. This seems reasonable + for a language which is used mainly for quickly executing, short + programs. - * `dgawk' only accepts source supplied with the `-f' option. + * The `gawk' debugger only accepts source supplied with the `-f' + option. Look forward to a future release when these and other missing features may be added, and of course feel free to try to add them @@ -20313,7 +20339,7 @@ Info file, in approximate chronological order: Windows32 environments. (This is no longer supported) * John Haque reworked the `gawk' internals to use a byte-code engine, - providing the `dgawk' debugger for `awk' programs. + providing the `gawk' debugger for `awk' programs. * Efraim Yawitz contributed the original text for *note Debugger::. @@ -21330,9 +21356,10 @@ Unix `awk' `pawk' Nelson H.F. Beebe at the University of Utah has modified Brian Kernighan's `awk' to provide timing and profiling information. It - is different from `pgawk' (*note Profiling::), in that it uses - CPU-based profiling, not line-count profiling. You may find it at - either `ftp://ftp.math.utah.edu/pub/pawk/pawk-20030606.tar.gz' or + is different from `gawk' with the `--profile' option. (*note + Profiling::), in that it uses CPU-based profiling, not line-count + profiling. You may find it at either + `ftp://ftp.math.utah.edu/pub/pawk/pawk-20030606.tar.gz' or `http://www.math.utah.edu/pub/pawk/pawk-20030606.tar.gz'. Busybox Awk @@ -21729,6 +21756,7 @@ is necessary when reading this minor node. * Internals:: A brief look at some `gawk' internals. * Plugin License:: A note about licensing. +* Loading Extensions:: How to load dynamic extensions. * Sample Library:: A example of new functions. @@ -21773,16 +21801,9 @@ when writing extensions. The next minor node shows how they are used: is current. It may end up calling an internal `gawk' function. It also guarantees that the wide string is zero-terminated. -`size_t get_curfunc_arg_count(void)' - This function returns the actual number of parameters passed to - the current function. Inside the code of an extension this can be - used to determine the maximum index which is safe to use with - `get_actual_argument'. If this value is greater than `nargs', the - function was called incorrectly from the `awk' program. - `nargs' - Inside an extension function, this is the maximum number of - expected parameters, as set by the `make_builtin()' function. + Inside an extension function, this is the actual number of + parameters passed to the current function. `n->stptr' `n->stlen' @@ -21810,12 +21831,10 @@ when writing extensions. The next minor node shows how they are used: Clears the associative array pointed to by `n'. Make sure that `n->type == Node_var_array' first. -`NODE **assoc_lookup(NODE *symbol, NODE *subs, int reference)' +`NODE **assoc_lookup(NODE *symbol, NODE *subs)' Finds, and installs if necessary, array elements. `symbol' is the array, `subs' is the subscript. This is usually a value created - with `make_string()' (see below). `reference' should be `TRUE' if - it is an error to use the value before it is created. Typically, - `FALSE' is the correct value to use from extension functions. + with `make_string()' (see below). `NODE *make_string(char *s, size_t len)' Take a C string and turn it into a pointer to a `NODE' that can be @@ -21937,7 +21956,7 @@ function parameter. just blindly copy this code. -File: gawk.info, Node: Plugin License, Next: Sample Library, Prev: Internals, Up: Dynamic Extensions +File: gawk.info, Node: Plugin License, Next: Loading Extensions, Prev: Internals, Up: Dynamic Extensions C.3.2 Extension Licensing ------------------------- @@ -21954,9 +21973,54 @@ the symbol exists in the global scope. Something like this is enough: int plugin_is_GPL_compatible; -File: gawk.info, Node: Sample Library, Prev: Plugin License, Up: Dynamic Extensions +File: gawk.info, Node: Loading Extensions, Next: Sample Library, Prev: Plugin License, Up: Dynamic Extensions + +C.3.3 Loading a Dynamic Extension +--------------------------------- + +There are two ways to load a dynamically linked library. The first is +to use the builtin `extension()': + + extension(libname, init_func) + + where `libname' is the library to load, and `init_func' is the name +of the initialization or bootstrap routine to run once loaded. + + The second method for dynamic loading of a library is to use the +command line option `-l': + + $ gawk -l libname -f myprog + + This will work only if the initialization routine is named +`dlload()'. + + If you use `extension()', the library will be loaded at run time. +This means that the functions are available only to the rest of your +script. If you use the command line option `-l' instead, the library +will be loaded before `gawk' starts compiling the actual program. The +net effect is that you can use those functions anywhere in the program. + + `gawk' has a list of directories where it searches for libraries. +By default, the list includes directories that depend upon how gawk was +built and installed (*note AWKPATH Variable::). If you want `gawk' to +look for libraries in your private directory, you have to tell it. The +way to do it is to set the `AWKPATH' environment variable (*note +AWKPATH Variable::). `gawk' supplies the default suffix `.so' if it is +not present in the name of the library. If the name of your library is +`mylib.so', you can simply type + + $ gawk -l mylib -f myprog + + and `gawk' will do everything necessary to load in your library, and +then call your `dlload()' routine. + + You can always specify the library using an absolute pathname, in +which case `gawk' will not use `AWKPATH' to search for it. + + +File: gawk.info, Node: Sample Library, Prev: Loading Extensions, Up: Dynamic Extensions -C.3.3 Example: Directory and File Operation Built-ins +C.3.4 Example: Directory and File Operation Built-ins ----------------------------------------------------- Two useful functions that are not in `awk' are `chdir()' (so that an @@ -21973,7 +22037,7 @@ implements these functions for `gawk' in an external extension library. File: gawk.info, Node: Internal File Description, Next: Internal File Ops, Up: Sample Library -C.3.3.1 Using `chdir()' and `stat()' +C.3.4.1 Using `chdir()' and `stat()' .................................... This minor node shows how to use the new functions at the `awk' level @@ -22096,7 +22160,7 @@ Elements::): File: gawk.info, Node: Internal File Ops, Next: Using Internal File Ops, Prev: Internal File Description, Up: Sample Library -C.3.3.2 C Code for `chdir()' and `stat()' +C.3.4.2 C Code for `chdir()' and `stat()' ......................................... Here is the C code for these extensions. They were written for @@ -22117,7 +22181,7 @@ other POSIX-compliant systems:(1) NODE *newdir; int ret = -1; - if (do_lint && get_curfunc_arg_count() != 1) + if (do_lint && nargs != 1) lintwarn("chdir: called with incorrect number of arguments"); newdir = get_scalar_argument(0, FALSE); @@ -22174,7 +22238,7 @@ declarations and argument checking: char *pmode; /* printable mode */ char *type = "unknown"; - if (do_lint && get_curfunc_arg_count() > 2) + if (do_lint && nargs > 2) lintwarn("stat: called with too many arguments"); Then comes the actual work. First, the function gets the arguments. @@ -22201,15 +22265,15 @@ link. If there's an error, it sets `ERRNO' and returns: calls are shown here, since they all follow the same pattern: /* fill in the array */ - aptr = assoc_lookup(array, tmp = make_string("name", 4), FALSE); + aptr = assoc_lookup(array, tmp = make_string("name", 4)); *aptr = dupnode(file); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("mode", 4), FALSE); + aptr = assoc_lookup(array, tmp = make_string("mode", 4)); *aptr = make_number((AWKNUM) sbuf.st_mode); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("pmode", 5), FALSE); + aptr = assoc_lookup(array, tmp = make_string("pmode", 5)); pmode = format_mode(sbuf.st_mode); *aptr = make_string(pmode, strlen(pmode)); unref(tmp); @@ -22246,7 +22310,7 @@ version. File: gawk.info, Node: Using Internal File Ops, Prev: Internal File Ops, Up: Sample Library -C.3.3.3 Integrating the Extensions +C.3.4.3 Integrating the Extensions .................................. Now that the code is written, it must be possible to add it at runtime @@ -24684,8 +24748,8 @@ Index * ' (single quote): One-shot. (line 15) * ' (single quote), vs. apostrophe: Comments. (line 27) * ' (single quote), with double quotes: Quoting. (line 53) +* () (parentheses) <1>: Profiling. (line 138) * () (parentheses): Regexp Operators. (line 79) -* () (parentheses), pgawk program: Profiling. (line 141) * * (asterisk), * operator, as multiplication operator: Precedence. (line 55) * * (asterisk), * operator, as regexp operator: Regexp Operators. @@ -24717,71 +24781,74 @@ Index * --assign option: Options. (line 32) * --c option: Options. (line 78) * --characters-as-bytes option: Options. (line 68) -* --command option: Options. (line 231) * --copyright option: Options. (line 85) +* --debug option: Options. (line 105) * --disable-lint configuration option: Additional Configuration Options. (line 9) * --disable-nls configuration option: Additional Configuration Options. (line 24) * --dump-variables option <1>: Library Names. (line 45) * --dump-variables option: Options. (line 90) -* --exec option: Options. (line 113) +* --exec option: Options. (line 122) * --field-separator option: Options. (line 21) * --file option: Options. (line 25) * --gen-pot option <1>: String Extraction. (line 6) -* --gen-pot option: Options. (line 135) -* --help option: Options. (line 142) -* --L option: Options. (line 245) -* --lint option <1>: Options. (line 147) +* --gen-pot option: Options. (line 144) +* --help option: Options. (line 151) +* --L option: Options. (line 263) +* --lint option <1>: Options. (line 163) * --lint option: Command Line. (line 20) -* --lint-old option: Options. (line 245) +* --lint-old option: Options. (line 263) +* --load option: Options. (line 156) * --non-decimal-data option <1>: Nondecimal Data. (line 6) -* --non-decimal-data option: Options. (line 166) +* --non-decimal-data option: Options. (line 182) * --non-decimal-data option, strtonum() function and: Nondecimal Data. (line 36) -* --optimize option: Options. (line 179) -* --posix option: Options. (line 199) -* --posix option, --traditional option and: Options. (line 218) -* --profile option <1>: Profiling. (line 15) -* --profile option: Options. (line 186) -* --re-interval option: Options. (line 224) -* --sandbox option: Options. (line 236) +* --optimize option: Options. (line 203) +* --posix option: Options. (line 222) +* --posix option, --traditional option and: Options. (line 241) +* --pretty-print option: Options. (line 195) +* --profile option <1>: Profiling. (line 12) +* --profile option: Options. (line 210) +* --re-interval option: Options. (line 247) +* --sandbox option: Options. (line 254) * --sandbox option, disabling system() function: I/O Functions. (line 85) * --sandbox option, input redirection with getline: Getline. (line 19) * --sandbox option, output redirection with print, printf: Redirection. (line 6) -* --source option: Options. (line 105) +* --source option: Options. (line 114) * --traditional option: Options. (line 78) -* --traditional option, --posix option and: Options. (line 218) -* --use-lc-numeric option: Options. (line 174) -* --version option: Options. (line 250) +* --traditional option, --posix option and: Options. (line 241) +* --use-lc-numeric option: Options. (line 190) +* --version option: Options. (line 268) * --with-whiny-user-strftime configuration option: Additional Configuration Options. (line 29) * -b option: Options. (line 68) * -C option: Options. (line 85) +* -D option: Options. (line 105) * -d option: Options. (line 90) -* -E option: Options. (line 113) -* -e option: Options. (line 105) +* -E option: Options. (line 122) +* -e option: Options. (line 114) * -F option: Command Line Field Separator. (line 6) * -f option: Options. (line 25) * -F option: Options. (line 21) * -f option: Long. (line 12) -* -F option, -Ft sets FS to TAB: Options. (line 258) -* -f option, on command line: Options. (line 263) -* -g option: Options. (line 135) -* -h option: Options. (line 142) -* -l option: Options. (line 147) -* -N option: Options. (line 174) -* -n option: Options. (line 166) -* -O option: Options. (line 179) -* -P option: Options. (line 199) -* -p option: Options. (line 186) -* -R option: Options. (line 231) -* -r option: Options. (line 224) -* -S option: Options. (line 236) -* -V option: Options. (line 250) +* -F option, -Ft sets FS to TAB: Options. (line 276) +* -f option, on command line: Options. (line 281) +* -g option: Options. (line 144) +* -h option: Options. (line 151) +* -l option: Options. (line 156) +* -N option: Options. (line 190) +* -n option: Options. (line 182) +* -O option: Options. (line 203) +* -o option: Options. (line 195) +* -P option: Options. (line 222) +* -p option: Options. (line 210) +* -r option: Options. (line 247) +* -S option: Options. (line 254) +* -V option: Options. (line 268) * -v option: Options. (line 32) * -v option, variables, assigning: Assignment Options. (line 12) * -W option: Options. (line 46) @@ -24970,18 +25037,18 @@ Index * arguments, command-line, invoking awk: Command Line. (line 6) * arguments, in function calls: Function Calls. (line 16) * arguments, processing: Getopt Function. (line 6) -* arguments, retrieving: Internals. (line 120) +* arguments, retrieving: Internals. (line 111) * arithmetic operators: Arithmetic Ops. (line 6) * arrays: Arrays. (line 6) * arrays, as parameters to functions: Pass By Value/Reference. (line 47) * arrays, associative: Array Intro. (line 50) -* arrays, associative, clearing: Internals. (line 75) +* arrays, associative, clearing: Internals. (line 68) * arrays, associative, library functions and: Library Names. (line 57) * arrays, deleting entire contents: Delete. (line 39) * arrays, elements, assigning: Assigning Elements. (line 6) * arrays, elements, deleting: Delete. (line 6) -* arrays, elements, installing: Internals. (line 79) +* arrays, elements, installing: Internals. (line 72) * arrays, elements, order of: Scanning an Array. (line 48) * arrays, elements, referencing: Reference to Elements. (line 6) @@ -25020,8 +25087,8 @@ Index * assignment operators, evaluation order: Assignment Ops. (line 111) * assignment operators, lvalues/rvalues: Assignment Ops. (line 32) * assignments as filenames: Ignoring Assigns. (line 6) -* assoc_clear() internal function: Internals. (line 75) -* assoc_lookup() internal function: Internals. (line 79) +* assoc_clear() internal function: Internals. (line 68) +* assoc_lookup() internal function: Internals. (line 72) * associative arrays: Array Intro. (line 50) * asterisk (*), * operator, as multiplication operator: Precedence. (line 55) @@ -25037,7 +25104,10 @@ Index * asterisk (*), *= operator: Assignment Ops. (line 129) * atan2() function: Numeric Functions. (line 11) * awf (amazingly workable formatter) program: Glossary. (line 25) +* awk debugging, enabling: Options. (line 105) +* awk enabling: Options. (line 195) * awk language, POSIX version: Assignment Ops. (line 136) +* awk profiling, enabling: Options. (line 210) * awk programs <1>: Two Rules. (line 6) * awk programs <2>: Executable Scripts. (line 6) * awk programs: Getting Started. (line 12) @@ -25053,7 +25123,6 @@ Index * awk programs, location of: Options. (line 25) * awk programs, one-line examples: Very Simple. (line 45) * awk programs, profiling: Profiling. (line 6) -* awk programs, profiling, enabling: Options. (line 186) * awk programs, running <1>: Long. (line 6) * awk programs, running: Running gawk. (line 6) * awk programs, running, from shell scripts: One-shot. (line 22) @@ -25093,7 +25162,7 @@ Index * AWKNUM internal type: Internals. (line 19) * AWKPATH environment variable <1>: PC Using. (line 11) * AWKPATH environment variable: AWKPATH Variable. (line 6) -* awkprof.out file: Profiling. (line 10) +* awkprof.out file: Profiling. (line 6) * awksed.awk program: Simple Sed. (line 25) * awkvars.out file: Options. (line 90) * b debugger command (alias for break): Breakpoint Control. (line 11) @@ -25147,12 +25216,13 @@ Index * backslash (\), in escape sequences, POSIX and: Escape Sequences. (line 113) * backslash (\), regexp constants: Computed Regexps. (line 28) -* backtrace debugger command: Dgawk Stack. (line 13) +* backtrace debugger command: Execution Stack. (line 13) * BBS-list file: Sample Data Files. (line 6) * Beebe, Nelson <1>: Other Versions. (line 69) * Beebe, Nelson: Acknowledgments. (line 60) -* BEGIN pattern <1>: BEGIN/END. (line 6) -* BEGIN pattern <2>: Field Separators. (line 44) +* BEGIN pattern <1>: Profiling. (line 62) +* BEGIN pattern <2>: BEGIN/END. (line 6) +* BEGIN pattern <3>: Field Separators. (line 44) * BEGIN pattern: Records. (line 29) * BEGIN pattern, assert() user-defined function and: Assert Function. (line 83) @@ -25167,7 +25237,6 @@ Index * BEGIN pattern, OFS/ORS variables, assigning values to: Output Separators. (line 20) * BEGIN pattern, operators and: Using BEGIN/END. (line 17) -* BEGIN pattern, pgawk program: Profiling. (line 65) * BEGIN pattern, print statement and: I/O And BEGIN/END. (line 16) * BEGIN pattern, pwcat program: Passwd Functions. (line 143) * BEGIN pattern, running awk programs and: Cut Program. (line 68) @@ -25196,8 +25265,8 @@ Index * Boolean expressions, as patterns: Expression Patterns. (line 41) * Boolean operators, See Boolean expressions: Boolean Ops. (line 6) * Bourne shell, quoting rules for: Quoting. (line 18) +* braces ({}): Profiling. (line 134) * braces ({}), actions and: Action Overview. (line 19) -* braces ({}), pgawk program: Profiling. (line 137) * braces ({}), statements, grouping: Statements. (line 10) * bracket expressions <1>: Bracket Expressions. (line 6) * bracket expressions: Regexp Operators. (line 55) @@ -25224,7 +25293,7 @@ Index * Broder, Alan J.: Contributors. (line 88) * Brown, Martin: Contributors. (line 82) * BSD-based operating systems: Glossary. (line 611) -* bt debugger command (alias for backtrace): Dgawk Stack. (line 13) +* bt debugger command (alias for backtrace): Execution Stack. (line 13) * Buening, Andreas <1>: Bugs. (line 71) * Buening, Andreas <2>: Contributors. (line 92) * Buening, Andreas: Acknowledgments. (line 60) @@ -25242,7 +25311,7 @@ Index * built-in variables, -v option, setting with: Options. (line 40) * built-in variables, conveying information: Auto-set. (line 6) * built-in variables, user-modifiable: User-modified. (line 6) -* Busybox Awk: Other Versions. (line 78) +* Busybox Awk: Other Versions. (line 79) * call by reference: Pass By Value/Reference. (line 47) * call by value: Pass By Value/Reference. @@ -25262,7 +25331,7 @@ Index * case sensitivity, regexps and <1>: User-modified. (line 82) * case sensitivity, regexps and: Case-sensitivity. (line 6) * case sensitivity, string comparisons and: User-modified. (line 82) -* CGI, awk scripts for: Options. (line 113) +* CGI, awk scripts for: Options. (line 122) * character lists, See bracket expressions: Regexp Operators. (line 55) * character sets (machine character encodings) <1>: Glossary. (line 141) * character sets (machine character encodings): Ordinal Functions. @@ -25292,7 +25361,7 @@ Index * close() function, two-way pipes and: Two-way I/O. (line 77) * Close, Diane <1>: Contributors. (line 21) * Close, Diane: Manual History. (line 41) -* close_func() input method: Internals. (line 160) +* close_func() input method: Internals. (line 151) * collating elements: Bracket Expressions. (line 69) * collating symbols: Bracket Expressions. (line 76) * Colombo, Antonio: Acknowledgments. (line 60) @@ -25316,7 +25385,7 @@ Index * command line, variables, assigning on: Assignment Options. (line 6) * command-line options, processing: Getopt Function. (line 6) * command-line options, string extraction: String Extraction. (line 6) -* commands debugger command: Dgawk Execution Control. +* commands debugger command: Debugger Execution Control. (line 10) * commenting: Comments. (line 6) * commenting, backslash continuation and: Statements/Lines. (line 76) @@ -25393,7 +25462,7 @@ Index * cos() function: Numeric Functions. (line 14) * counting: Wc Program. (line 6) * csh utility: Statements/Lines. (line 44) -* csh utility, POSIXLY_CORRECT environment variable: Options. (line 305) +* csh utility, POSIXLY_CORRECT environment variable: Options. (line 323) * csh utility, |& operator, comparison with: Two-way I/O. (line 44) * ctime() user-defined function: Function Example. (line 72) * currency symbols, localization: Explaining gettext. (line 103) @@ -25401,7 +25470,7 @@ Index (line 30) * cut utility: Cut Program. (line 6) * cut.awk program: Cut Program. (line 45) -* d debugger command (alias for delete): Breakpoint Control. (line 63) +* d debugger command (alias for delete): Breakpoint Control. (line 64) * d.c., See dark corner: Conventions. (line 38) * dark corner <1>: Glossary. (line 193) * dark corner <2>: Truth Values. (line 24) @@ -25465,113 +25534,113 @@ Index (line 33) * deadlocks: Two-way I/O. (line 70) * debugger commands, b (break): Breakpoint Control. (line 11) -* debugger commands, backtrace: Dgawk Stack. (line 13) +* debugger commands, backtrace: Execution Stack. (line 13) * debugger commands, break: Breakpoint Control. (line 11) -* debugger commands, bt (backtrace): Dgawk Stack. (line 13) -* debugger commands, c (continue): Dgawk Execution Control. +* debugger commands, bt (backtrace): Execution Stack. (line 13) +* debugger commands, c (continue): Debugger Execution Control. (line 33) * debugger commands, clear: Breakpoint Control. (line 36) -* debugger commands, commands: Dgawk Execution Control. +* debugger commands, commands: Debugger Execution Control. (line 10) * debugger commands, condition: Breakpoint Control. (line 54) -* debugger commands, continue: Dgawk Execution Control. +* debugger commands, continue: Debugger Execution Control. (line 33) -* debugger commands, d (delete): Breakpoint Control. (line 63) -* debugger commands, delete: Breakpoint Control. (line 63) -* debugger commands, disable: Breakpoint Control. (line 68) +* debugger commands, d (delete): Breakpoint Control. (line 64) +* debugger commands, delete: Breakpoint Control. (line 64) +* debugger commands, disable: Breakpoint Control. (line 69) * debugger commands, display: Viewing And Changing Data. (line 8) -* debugger commands, down: Dgawk Stack. (line 21) -* debugger commands, dump: Miscellaneous Dgawk Commands. +* debugger commands, down: Execution Stack. (line 21) +* debugger commands, dump: Miscellaneous Debugger Commands. (line 9) -* debugger commands, e (enable): Breakpoint Control. (line 72) -* debugger commands, enable: Breakpoint Control. (line 72) -* debugger commands, end: Dgawk Execution Control. +* debugger commands, e (enable): Breakpoint Control. (line 73) +* debugger commands, enable: Breakpoint Control. (line 73) +* debugger commands, end: Debugger Execution Control. (line 10) * debugger commands, eval: Viewing And Changing Data. (line 23) -* debugger commands, f (frame): Dgawk Stack. (line 25) -* debugger commands, finish: Dgawk Execution Control. +* debugger commands, f (frame): Execution Stack. (line 25) +* debugger commands, finish: Debugger Execution Control. (line 39) -* debugger commands, frame: Dgawk Stack. (line 25) -* debugger commands, h (help): Miscellaneous Dgawk Commands. +* debugger commands, frame: Execution Stack. (line 25) +* debugger commands, h (help): Miscellaneous Debugger Commands. (line 68) -* debugger commands, help: Miscellaneous Dgawk Commands. +* debugger commands, help: Miscellaneous Debugger Commands. (line 68) -* debugger commands, i (info): Dgawk Info. (line 12) -* debugger commands, ignore: Breakpoint Control. (line 86) -* debugger commands, info: Dgawk Info. (line 12) -* debugger commands, l (list): Miscellaneous Dgawk Commands. +* debugger commands, i (info): Debugger Info. (line 13) +* debugger commands, ignore: Breakpoint Control. (line 87) +* debugger commands, info: Debugger Info. (line 13) +* debugger commands, l (list): Miscellaneous Debugger Commands. (line 74) -* debugger commands, list: Miscellaneous Dgawk Commands. +* debugger commands, list: Miscellaneous Debugger Commands. (line 74) -* debugger commands, n (next): Dgawk Execution Control. +* debugger commands, n (next): Debugger Execution Control. (line 43) -* debugger commands, next: Dgawk Execution Control. +* debugger commands, next: Debugger Execution Control. (line 43) -* debugger commands, nexti: Dgawk Execution Control. +* debugger commands, nexti: Debugger Execution Control. (line 49) -* debugger commands, ni (nexti): Dgawk Execution Control. +* debugger commands, ni (nexti): Debugger Execution Control. (line 49) -* debugger commands, o (option): Dgawk Info. (line 56) -* debugger commands, option: Dgawk Info. (line 56) +* debugger commands, o (option): Debugger Info. (line 57) +* debugger commands, option: Debugger Info. (line 57) * debugger commands, p (print): Viewing And Changing Data. (line 36) * debugger commands, print: Viewing And Changing Data. (line 36) * debugger commands, printf: Viewing And Changing Data. (line 54) -* debugger commands, q (quit): Miscellaneous Dgawk Commands. +* debugger commands, q (quit): Miscellaneous Debugger Commands. (line 101) -* debugger commands, quit: Miscellaneous Dgawk Commands. +* debugger commands, quit: Miscellaneous Debugger Commands. (line 101) -* debugger commands, r (run): Dgawk Execution Control. +* debugger commands, r (run): Debugger Execution Control. (line 62) -* debugger commands, return: Dgawk Execution Control. +* debugger commands, return: Debugger Execution Control. (line 54) -* debugger commands, run: Dgawk Execution Control. +* debugger commands, run: Debugger Execution Control. (line 62) -* debugger commands, s (step): Dgawk Execution Control. +* debugger commands, s (step): Debugger Execution Control. (line 68) * debugger commands, set: Viewing And Changing Data. (line 59) -* debugger commands, si (stepi): Dgawk Execution Control. +* debugger commands, si (stepi): Debugger Execution Control. (line 76) -* debugger commands, silent: Dgawk Execution Control. +* debugger commands, silent: Debugger Execution Control. (line 10) -* debugger commands, step: Dgawk Execution Control. +* debugger commands, step: Debugger Execution Control. (line 68) -* debugger commands, stepi: Dgawk Execution Control. +* debugger commands, stepi: Debugger Execution Control. (line 76) -* debugger commands, t (tbreak): Breakpoint Control. (line 89) -* debugger commands, tbreak: Breakpoint Control. (line 89) -* debugger commands, trace: Miscellaneous Dgawk Commands. +* debugger commands, t (tbreak): Breakpoint Control. (line 90) +* debugger commands, tbreak: Breakpoint Control. (line 90) +* debugger commands, trace: Miscellaneous Debugger Commands. (line 110) -* debugger commands, u (until): Dgawk Execution Control. +* debugger commands, u (until): Debugger Execution Control. (line 83) * debugger commands, undisplay: Viewing And Changing Data. (line 80) -* debugger commands, until: Dgawk Execution Control. +* debugger commands, until: Debugger Execution Control. (line 83) * debugger commands, unwatch: Viewing And Changing Data. (line 84) -* debugger commands, up: Dgawk Stack. (line 33) +* debugger commands, up: Execution Stack. (line 33) * debugger commands, w (watch): Viewing And Changing Data. (line 67) * debugger commands, watch: Viewing And Changing Data. (line 67) +* debugging awk programs: Debugger. (line 6) * debugging gawk, bug reports: Bugs. (line 9) -* decimal point character, locale specific: Options. (line 215) +* decimal point character, locale specific: Options. (line 238) * decrement operators: Increment Ops. (line 35) * default keyword: Switch Statement. (line 6) * Deifik, Scott <1>: Bugs. (line 70) * Deifik, Scott <2>: Contributors. (line 54) * Deifik, Scott: Acknowledgments. (line 60) -* delete debugger command: Breakpoint Control. (line 63) +* delete debugger command: Breakpoint Control. (line 64) * delete statement: Delete. (line 6) * deleting elements in arrays: Delete. (line 6) * deleting entire arrays: Delete. (line 39) -* dgawk: Debugger. (line 6) * differences between gawk and awk: String Functions. (line 196) * differences in awk and gawk, ARGC/ARGV variables: ARGC and ARGV. (line 88) @@ -25642,7 +25711,7 @@ Index (line 6) * directories, searching <1>: Igawk Program. (line 368) * directories, searching: AWKPATH Variable. (line 6) -* disable debugger command: Breakpoint Control. (line 68) +* disable debugger command: Breakpoint Control. (line 69) * display debugger command: Viewing And Changing Data. (line 8) * division: Arithmetic Ops. (line 44) @@ -25660,14 +25729,14 @@ Index * double quote (") <1>: Quoting. (line 37) * double quote ("): Read Terminal. (line 25) * double quote ("), regexp constants: Computed Regexps. (line 28) -* down debugger command: Dgawk Stack. (line 21) +* down debugger command: Execution Stack. (line 21) * Drepper, Ulrich: Acknowledgments. (line 52) * DuBois, John: Acknowledgments. (line 60) -* dump debugger command: Miscellaneous Dgawk Commands. +* dump debugger command: Miscellaneous Debugger Commands. (line 9) -* dupnode() internal function: Internals. (line 96) +* dupnode() internal function: Internals. (line 87) * dupword.awk program: Dupword Program. (line 31) -* e debugger command (alias for enable): Breakpoint Control. (line 72) +* e debugger command (alias for enable): Breakpoint Control. (line 73) * EBCDIC: Ordinal Functions. (line 45) * egrep utility <1>: Egrep Program. (line 6) * egrep utility: Bracket Expressions. (line 24) @@ -25683,9 +25752,10 @@ Index * empty pattern: Empty. (line 6) * empty strings, See null strings: Regexp Field Splitting. (line 43) -* enable debugger command: Breakpoint Control. (line 72) -* end debugger command: Dgawk Execution Control. +* enable debugger command: Breakpoint Control. (line 73) +* end debugger command: Debugger Execution Control. (line 10) +* END pattern <1>: Profiling. (line 62) * END pattern: BEGIN/END. (line 6) * END pattern, assert() user-defined function and: Assert Function. (line 75) @@ -25697,7 +25767,6 @@ Index * END pattern, next/nextfile statements and: I/O And BEGIN/END. (line 37) * END pattern, operators and: Using BEGIN/END. (line 17) -* END pattern, pgawk program: Profiling. (line 65) * END pattern, print statement and: I/O And BEGIN/END. (line 16) * ENDFILE pattern: BEGINFILE/ENDFILE. (line 6) * ENDFILE pattern, Boolean patterns and: Expression Patterns. (line 73) @@ -25706,7 +25775,7 @@ Index * endgrent() user-defined function: Group Functions. (line 218) * endpwent() function (C library): Passwd Functions. (line 210) * endpwent() user-defined function: Passwd Functions. (line 213) -* ENVIRON array <1>: Internals. (line 149) +* ENVIRON array <1>: Internals. (line 140) * ENVIRON array: Auto-set. (line 60) * environment variables: Auto-set. (line 60) * epoch, definition of: Glossary. (line 239) @@ -25715,7 +25784,7 @@ Index * equals sign (=), == operator: Comparison Operators. (line 11) * EREs (Extended Regular Expressions): Bracket Expressions. (line 24) -* ERRNO variable <1>: Internals. (line 139) +* ERRNO variable <1>: Internals. (line 130) * ERRNO variable <2>: TCP/IP Networking. (line 54) * ERRNO variable <3>: Auto-set. (line 72) * ERRNO variable <4>: BEGINFILE/ENDFILE. (line 26) @@ -25764,7 +25833,7 @@ Index (line 9) * expressions, selecting: Conditional Exp. (line 6) * Extended Regular Expressions (EREs): Bracket Expressions. (line 24) -* eXtensible Markup Language (XML): Internals. (line 160) +* eXtensible Markup Language (XML): Internals. (line 151) * extension() function (gawk): Using Internal File Ops. (line 15) * extensions, Brian Kernighan's awk <1>: Other Versions. (line 13) @@ -25789,7 +25858,7 @@ Index * extract.awk program: Extract Program. (line 78) * extraction, of marked strings (internationalization): String Extraction. (line 6) -* f debugger command (alias for frame): Dgawk Stack. (line 25) +* f debugger command (alias for frame): Execution Stack. (line 25) * false, logical: Truth Values. (line 6) * FDL (Free Documentation License): GNU Free Documentation License. (line 6) @@ -25856,7 +25925,7 @@ Index * files, /inet6/... (gawk): TCP/IP Networking. (line 6) * files, as single records: Records. (line 196) * files, awk programs in: Long. (line 6) -* files, awkprof.out: Profiling. (line 10) +* files, awkprof.out: Profiling. (line 6) * files, awkvars.out: Options. (line 90) * files, closing: I/O Functions. (line 10) * files, descriptors, See file descriptors: Special FD. (line 6) @@ -25885,7 +25954,7 @@ Index * files, portable object template: Explaining gettext. (line 30) * files, portable object, converting to message object files: I18N Example. (line 62) -* files, portable object, generating: Options. (line 135) +* files, portable object, generating: Options. (line 144) * files, processing, ARGIND variable and: Auto-set. (line 47) * files, reading: Rewind Function. (line 6) * files, reading, multiline records: Multiple Line. (line 6) @@ -25894,7 +25963,7 @@ Index * files, source, search path for: Igawk Program. (line 368) * files, splitting: Split Program. (line 6) * files, Texinfo, extracting programs from: Extract Program. (line 6) -* finish debugger command: Dgawk Execution Control. +* finish debugger command: Debugger Execution Control. (line 39) * Fish, Fred: Contributors. (line 51) * fixed-width data: Constant Size. (line 9) @@ -25929,7 +25998,7 @@ Index * FPAT variable <1>: User-modified. (line 45) * FPAT variable: Splitting By Content. (line 26) -* frame debugger command: Dgawk Stack. (line 25) +* frame debugger command: Execution Stack. (line 25) * Free Documentation License (FDL): GNU Free Documentation License. (line 6) * Free Software Foundation (FSF) <1>: Glossary. (line 301) @@ -25941,7 +26010,7 @@ Index * FS variable, --field-separator option and: Options. (line 21) * FS variable, as null string: Single Character Fields. (line 20) -* FS variable, as TAB character: Options. (line 211) +* FS variable, as TAB character: Options. (line 234) * FS variable, changing value of: Field Separators. (line 34) * FS variable, running awk programs and: Cut Program. (line 68) * FS variable, setting from command line: Command Line Field Separator. @@ -25986,13 +26055,13 @@ Index * functions, names of <1>: Definition Syntax. (line 20) * functions, names of: Arrays. (line 18) * functions, recursive: Definition Syntax. (line 73) -* functions, return values, setting: Internals. (line 139) +* functions, return values, setting: Internals. (line 130) * functions, string-translation: I18N Functions. (line 6) * functions, undefined: Pass By Value/Reference. (line 71) * functions, user-defined: User-defined. (line 6) * functions, user-defined, calling: Calling A Function. (line 6) -* functions, user-defined, counts: Profiling. (line 132) +* functions, user-defined, counts: Profiling. (line 129) * functions, user-defined, library of: Library Functions. (line 6) * functions, user-defined, next/nextfile statements and <1>: Nextfile Statement. (line 44) @@ -26000,6 +26069,7 @@ Index (line 45) * G-d: Acknowledgments. (line 81) * Garfinkle, Scott: Contributors. (line 35) +* gawk program, dynamic profiling: Profiling. (line 171) * gawk, ARGIND variable in: Other Arguments. (line 12) * gawk, awk and <1>: This Manual. (line 14) * gawk, awk and: Preface. (line 23) @@ -26026,7 +26096,7 @@ Index (line 139) * gawk, ERRNO variable in: Getline. (line 19) * gawk, escape sequences: Escape Sequences. (line 125) -* gawk, extensions, disabling: Options. (line 199) +* gawk, extensions, disabling: Options. (line 222) * gawk, features, adding: Adding Code. (line 6) * gawk, features, advanced: Advanced Features. (line 6) * gawk, fflush() function in: I/O Functions. (line 44) @@ -26040,6 +26110,7 @@ Index (line 26) * gawk, function arguments and: Calling Built-in. (line 16) * gawk, functions, adding: Dynamic Extensions. (line 10) +* gawk, functions, loading: Loading Extensions. (line 6) * gawk, hexadecimal numbers and: Nondecimal-numbers. (line 42) * gawk, IGNORECASE variable in <1>: Array Sorting Functions. (line 81) @@ -26090,7 +26161,7 @@ Index * gawk, TEXTDOMAIN variable in: User-modified. (line 153) * gawk, timestamps: Time Functions. (line 6) * gawk, uses for: Preface. (line 36) -* gawk, versions of, information about, printing: Options. (line 250) +* gawk, versions of, information about, printing: Options. (line 268) * gawk, VMS version of: VMS Installation. (line 6) * gawk, word-boundary operator: GNU Regexp Operators. (line 63) @@ -26100,12 +26171,11 @@ Index * gensub() function (gawk): Using Constant Regexps. (line 43) * gensub() function (gawk), escape processing: Gory Details. (line 6) -* get_actual_argument() internal function: Internals. (line 125) -* get_argument() internal function: Internals. (line 120) -* get_array_argument() internal macro: Internals. (line 136) -* get_curfunc_arg_count() internal function: Internals. (line 42) -* get_record() input method: Internals. (line 160) -* get_scalar_argument() internal macro: Internals. (line 133) +* get_actual_argument() internal function: Internals. (line 116) +* get_argument() internal function: Internals. (line 111) +* get_array_argument() internal macro: Internals. (line 127) +* get_record() input method: Internals. (line 151) +* get_scalar_argument() internal macro: Internals. (line 124) * getaddrinfo() function (C library): TCP/IP Networking. (line 38) * getgrent() function (C library): Group Functions. (line 6) * getgrent() user-defined function: Group Functions. (line 6) @@ -26151,7 +26221,7 @@ Index * GNU Lesser General Public License: Glossary. (line 397) * GNU long options <1>: Options. (line 6) * GNU long options: Command Line. (line 13) -* GNU long options, printing list of: Options. (line 142) +* GNU long options, printing list of: Options. (line 151) * GNU Project <1>: Glossary. (line 319) * GNU Project: Manual History. (line 11) * GNU/Linux <1>: Glossary. (line 611) @@ -26170,7 +26240,7 @@ Index (line 43) * gsub() function, arguments of: String Functions. (line 462) * gsub() function, escape processing: Gory Details. (line 6) -* h debugger command (alias for help): Miscellaneous Dgawk Commands. +* h debugger command (alias for help): Miscellaneous Debugger Commands. (line 68) * Hankerson, Darrel <1>: Contributors. (line 61) * Hankerson, Darrel: Acknowledgments. (line 60) @@ -26179,13 +26249,13 @@ Index * Hartholz, Elaine: Acknowledgments. (line 38) * Hartholz, Marshall: Acknowledgments. (line 38) * Hasegawa, Isamu: Contributors. (line 94) -* help debugger command: Miscellaneous Dgawk Commands. +* help debugger command: Miscellaneous Debugger Commands. (line 68) * hexadecimal numbers: Nondecimal-numbers. (line 6) -* hexadecimal values, enabling interpretation of: Options. (line 166) +* hexadecimal values, enabling interpretation of: Options. (line 182) * histsort.awk program: History Sorting. (line 25) * Hughes, Phil: Acknowledgments. (line 43) -* HUP signal: Profiling. (line 204) +* HUP signal: Profiling. (line 203) * hyphen (-), - operator: Precedence. (line 52) * hyphen (-), -- (decrement/increment) operators: Precedence. (line 46) * hyphen (-), -- operator: Increment Ops. (line 48) @@ -26193,14 +26263,14 @@ Index * hyphen (-), -= operator: Assignment Ops. (line 129) * hyphen (-), filenames beginning with: Options. (line 59) * hyphen (-), in bracket expressions: Bracket Expressions. (line 17) -* i debugger command (alias for info): Dgawk Info. (line 12) +* i debugger command (alias for info): Debugger Info. (line 13) * id utility: Id Program. (line 6) * id.awk program: Id Program. (line 30) * if statement <1>: If Statement. (line 6) * if statement: Regexp Usage. (line 19) * if statement, actions, changing: Ranges. (line 25) * igawk.sh program: Igawk Program. (line 124) -* ignore debugger command: Breakpoint Control. (line 86) +* ignore debugger command: Breakpoint Control. (line 87) * IGNORECASE variable <1>: Array Sorting Functions. (line 81) * IGNORECASE variable <2>: String Functions. (line 29) @@ -26228,7 +26298,7 @@ Index * index() function: String Functions. (line 155) * indexing arrays: Array Intro. (line 50) * indirect function calls: Indirect Calls. (line 6) -* info debugger command: Dgawk Info. (line 12) +* info debugger command: Debugger Info. (line 13) * initialization, automatic: More Complex. (line 38) * input files: Reading Files. (line 6) * input files, closing: Close Files And Pipes. @@ -26253,42 +26323,41 @@ Index * insomnia, cure for: Alarm Program. (line 6) * installation, VMS: VMS Installation. (line 6) * installing gawk: Installation. (line 6) -* INT signal (MS-Windows): Profiling. (line 207) +* INT signal (MS-Windows): Profiling. (line 206) * int() function: Numeric Functions. (line 22) * integers: Basic Data Typing. (line 21) * integers, unsigned: Basic Data Typing. (line 30) * interacting with other programs: I/O Functions. (line 63) -* internal constant, INVALID_HANDLE: Internals. (line 160) -* internal function, assoc_clear(): Internals. (line 75) -* internal function, assoc_lookup(): Internals. (line 79) -* internal function, dupnode(): Internals. (line 96) +* internal constant, INVALID_HANDLE: Internals. (line 151) +* internal function, assoc_clear(): Internals. (line 68) +* internal function, assoc_lookup(): Internals. (line 72) +* internal function, dupnode(): Internals. (line 87) * internal function, force_number(): Internals. (line 27) * internal function, force_string(): Internals. (line 32) * internal function, force_wstring(): Internals. (line 37) -* internal function, get_actual_argument(): Internals. (line 125) -* internal function, get_argument(): Internals. (line 120) -* internal function, get_curfunc_arg_count(): Internals. (line 42) -* internal function, iop_alloc(): Internals. (line 160) -* internal function, make_builtin(): Internals. (line 106) -* internal function, make_number(): Internals. (line 91) -* internal function, make_string(): Internals. (line 86) -* internal function, register_deferred_variable(): Internals. (line 149) -* internal function, register_open_hook(): Internals. (line 160) -* internal function, unref(): Internals. (line 101) -* internal function, update_ERRNO(): Internals. (line 139) -* internal function, update_ERRNO_saved(): Internals. (line 144) -* internal macro, get_array_argument(): Internals. (line 136) -* internal macro, get_scalar_argument(): Internals. (line 133) -* internal structure, IOBUF: Internals. (line 160) +* internal function, get_actual_argument(): Internals. (line 116) +* internal function, get_argument(): Internals. (line 111) +* internal function, iop_alloc(): Internals. (line 151) +* internal function, make_builtin(): Internals. (line 97) +* internal function, make_number(): Internals. (line 82) +* internal function, make_string(): Internals. (line 77) +* internal function, register_deferred_variable(): Internals. (line 140) +* internal function, register_open_hook(): Internals. (line 151) +* internal function, unref(): Internals. (line 92) +* internal function, update_ERRNO(): Internals. (line 130) +* internal function, update_ERRNO_saved(): Internals. (line 135) +* internal macro, get_array_argument(): Internals. (line 127) +* internal macro, get_scalar_argument(): Internals. (line 124) +* internal structure, IOBUF: Internals. (line 151) * internal type, AWKNUM: Internals. (line 19) * internal type, NODE: Internals. (line 23) -* internal variable, nargs: Internals. (line 49) -* internal variable, stlen: Internals. (line 53) -* internal variable, stptr: Internals. (line 53) -* internal variable, type: Internals. (line 66) -* internal variable, vname: Internals. (line 71) -* internal variable, wstlen: Internals. (line 61) -* internal variable, wstptr: Internals. (line 61) +* internal variable, nargs: Internals. (line 42) +* internal variable, stlen: Internals. (line 46) +* internal variable, stptr: Internals. (line 46) +* internal variable, type: Internals. (line 59) +* internal variable, vname: Internals. (line 64) +* internal variable, wstlen: Internals. (line 54) +* internal variable, wstptr: Internals. (line 54) * internationalization <1>: I18N and L10N. (line 6) * internationalization: I18N Functions. (line 6) * internationalization, localization <1>: Internationalization. @@ -26308,10 +26377,10 @@ Index * interpreted programs <1>: Glossary. (line 361) * interpreted programs: Basic High Level. (line 14) * interval expressions: Regexp Operators. (line 116) -* INVALID_HANDLE internal constant: Internals. (line 160) +* INVALID_HANDLE internal constant: Internals. (line 151) * inventory-shipped file: Sample Data Files. (line 32) -* IOBUF internal structure: Internals. (line 160) -* iop_alloc() internal function: Internals. (line 160) +* IOBUF internal structure: Internals. (line 151) +* iop_alloc() internal function: Internals. (line 151) * isarray() function (gawk): Type Functions. (line 11) * ISO: Glossary. (line 372) * ISO 8859-1: Glossary. (line 141) @@ -26319,9 +26388,9 @@ Index * Jacobs, Andrew: Passwd Functions. (line 90) * Jaegermann, Michal <1>: Contributors. (line 46) * Jaegermann, Michal: Acknowledgments. (line 60) -* Java implementation of awk: Other Versions. (line 96) +* Java implementation of awk: Other Versions. (line 97) * Java programming language: Glossary. (line 380) -* jawk: Other Versions. (line 96) +* jawk: Other Versions. (line 97) * Jedi knights: Undocumented. (line 6) * join() user-defined function: Join Function. (line 18) * Kahrs, Ju"rgen <1>: Contributors. (line 70) @@ -26336,10 +26405,10 @@ Index * Kernighan, Brian <6>: Acknowledgments. (line 75) * Kernighan, Brian <7>: Conventions. (line 34) * Kernighan, Brian: History. (line 17) -* kill command, dynamic profiling: Profiling. (line 182) +* kill command, dynamic profiling: Profiling. (line 180) * Knights, jedi: Undocumented. (line 6) * Kwok, Conrad: Contributors. (line 35) -* l debugger command (alias for list): Miscellaneous Dgawk Commands. +* l debugger command (alias for list): Miscellaneous Debugger Commands. (line 74) * labels.awk program: Labels Program. (line 51) * languages, data-driven: Basic High Level. (line 83) @@ -26365,7 +26434,7 @@ Index * length() function: String Functions. (line 166) * Lesser General Public License (LGPL): Glossary. (line 397) * LGPL (Lesser General Public License): Glossary. (line 397) -* libmawk: Other Versions. (line 104) +* libmawk: Other Versions. (line 105) * libraries of awk functions: Library Functions. (line 6) * libraries of awk functions, assertions: Assert Function. (line 6) * libraries of awk functions, associative arrays and: Library Names. @@ -26403,20 +26472,22 @@ Index * lint checking, array subscripts: Uninitialized Subscripts. (line 43) * lint checking, empty programs: Command Line. (line 16) -* lint checking, issuing warnings: Options. (line 147) +* lint checking, issuing warnings: Options. (line 163) * lint checking, POSIXLY_CORRECT environment variable: Options. - (line 289) + (line 307) * lint checking, undefined functions: Pass By Value/Reference. (line 88) * LINT variable: User-modified. (line 98) * Linux <1>: Glossary. (line 611) * Linux <2>: I18N Example. (line 55) * Linux: Manual History. (line 28) -* list debugger command: Miscellaneous Dgawk Commands. +* list debugger command: Miscellaneous Debugger Commands. (line 74) +* loading extension: Loading Extensions. (line 6) +* loading, library: Options. (line 156) * local variables: Variable Scope. (line 6) * locale categories: Explaining gettext. (line 80) -* locale decimal point character: Options. (line 215) +* locale decimal point character: Options. (line 238) * locale, definition of: Locales. (line 6) * localization: I18N and L10N. (line 6) * localization, See internationalization, localization: I18N and L10N. @@ -26429,7 +26500,7 @@ Index * long options: Command Line. (line 13) * loops: While Statement. (line 6) * loops, continue statements and: For Statement. (line 64) -* loops, count for header: Profiling. (line 126) +* loops, count for header: Profiling. (line 123) * loops, exiting: Break Statement. (line 6) * loops, See Also while statement: While Statement. (line 6) * Lost In Space: Dynamic Extensions. (line 6) @@ -26438,9 +26509,9 @@ Index * lvalues/rvalues: Assignment Ops. (line 32) * mailing labels, printing: Labels Program. (line 6) * mailing list, GNITS: Acknowledgments. (line 52) -* make_builtin() internal function: Internals. (line 106) -* make_number() internal function: Internals. (line 91) -* make_string() internal function: Internals. (line 86) +* make_builtin() internal function: Internals. (line 97) +* make_number() internal function: Internals. (line 82) +* make_string() internal function: Internals. (line 77) * mark parity: Ordinal Functions. (line 45) * marked string extraction (internationalization): String Extraction. (line 6) @@ -26455,7 +26526,7 @@ Index * matching, null strings: Gory Details. (line 164) * mawk program: Other Versions. (line 35) * McPhee, Patrick: Contributors. (line 100) -* memory, releasing: Internals. (line 101) +* memory, releasing: Internals. (line 92) * message object files: Explaining gettext. (line 41) * message object files, converting from portable object files: I18N Example. (line 62) @@ -26468,7 +26539,7 @@ Index * modifiers, in format specifiers: Format Modifiers. (line 6) * monetary information, localization: Explaining gettext. (line 103) * msgfmt utility: I18N Example. (line 62) -* n debugger command (alias for next): Dgawk Execution Control. +* n debugger command (alias for next): Debugger Execution Control. (line 43) * names, arrays/variables <1>: Library Names. (line 6) * names, arrays/variables: Arrays. (line 18) @@ -26477,14 +26548,14 @@ Index * namespace issues <1>: Library Names. (line 6) * namespace issues: Arrays. (line 18) * namespace issues, functions: Definition Syntax. (line 20) -* nargs internal variable: Internals. (line 49) +* nargs internal variable: Internals. (line 42) * nawk utility: Names. (line 17) * negative zero: Unexpected Results. (line 28) * NetBSD: Glossary. (line 611) * networks, programming: TCP/IP Networking. (line 6) * networks, support for: Special Network. (line 6) * newlines <1>: Boolean Ops. (line 67) -* newlines <2>: Options. (line 205) +* newlines <2>: Options. (line 228) * newlines: Statements/Lines. (line 6) * newlines, as field separators: Default Field Splitting. (line 6) @@ -26495,7 +26566,7 @@ Index * newlines, separating statements in actions <1>: Statements. (line 10) * newlines, separating statements in actions: Action Overview. (line 19) -* next debugger command: Dgawk Execution Control. +* next debugger command: Debugger Execution Control. (line 43) * next statement <1>: Next Statement. (line 6) * next statement: Boolean Ops. (line 85) @@ -26510,16 +26581,16 @@ Index (line 26) * nextfile statement, user-defined functions and: Nextfile Statement. (line 44) -* nexti debugger command: Dgawk Execution Control. +* nexti debugger command: Debugger Execution Control. (line 49) * NF variable <1>: Auto-set. (line 107) * NF variable: Fields. (line 33) * NF variable, decrementing: Changing Fields. (line 107) -* ni debugger command (alias for nexti): Dgawk Execution Control. +* ni debugger command (alias for nexti): Debugger Execution Control. (line 49) * noassign.awk program: Ignoring Assigns. (line 15) * NODE internal type: Internals. (line 23) -* nodes, duplicating: Internals. (line 96) +* nodes, duplicating: Internals. (line 87) * not Boolean-logic operator: Boolean Ops. (line 6) * NR variable <1>: Auto-set. (line 118) * NR variable: Records. (line 6) @@ -26540,7 +26611,7 @@ Index * number sign (#), #! (executable scripts), portability issues with: Executable Scripts. (line 6) * number sign (#), commenting: Comments. (line 6) -* numbers: Internals. (line 91) +* numbers: Internals. (line 82) * numbers, as array subscripts: Numeric Array Subscripts. (line 6) * numbers, as values of characters: Ordinal Functions. (line 6) @@ -26560,11 +26631,11 @@ Index * numeric, output format: OFMT. (line 6) * numeric, strings: Variable Typing. (line 6) * numeric, values: Internals. (line 27) -* o debugger command (alias for option): Dgawk Info. (line 56) +* o debugger command (alias for option): Debugger Info. (line 57) * oawk utility: Names. (line 17) * obsolete features: Obsolete. (line 6) * octal numbers: Nondecimal-numbers. (line 6) -* octal values, enabling interpretation of: Options. (line 166) +* octal values, enabling interpretation of: Options. (line 182) * OFMT variable <1>: User-modified. (line 115) * OFMT variable <2>: Conversion. (line 55) * OFMT variable: OFMT. (line 15) @@ -26573,7 +26644,7 @@ Index * OFS variable <2>: Output Separators. (line 6) * OFS variable: Changing Fields. (line 64) * OpenBSD: Glossary. (line 611) -* OpenSolaris: Other Versions. (line 86) +* OpenSolaris: Other Versions. (line 87) * operating systems, BSD-based: Manual History. (line 28) * operating systems, PC, gawk on: PC Using. (line 6) * operating systems, PC, gawk on, installing: PC Installation. @@ -26606,7 +26677,7 @@ Index (line 48) * operators, word-boundary (gawk): GNU Regexp Operators. (line 63) -* option debugger command: Dgawk Info. (line 56) +* option debugger command: Debugger Info. (line 57) * options, command-line <1>: Command Line Field Separator. (line 6) * options, command-line <2>: Options. (line 6) @@ -26617,7 +26688,7 @@ Index * options, deprecated: Obsolete. (line 6) * options, long <1>: Options. (line 6) * options, long: Command Line. (line 13) -* options, printing list of: Options. (line 142) +* options, printing list of: Options. (line 151) * OR bitwise operation: Bitwise Functions. (line 6) * or Boolean-logic operator: Boolean Ops. (line 6) * or() function (gawk): Bitwise Functions. (line 48) @@ -26643,15 +26714,15 @@ Index (line 36) * P1003.1 POSIX standard: Glossary. (line 454) * P1003.2 POSIX standard: Glossary. (line 454) -* parameters, number of: Internals. (line 49) +* parameters, number of: Internals. (line 42) +* parentheses () <1>: Profiling. (line 138) * parentheses (): Regexp Operators. (line 79) -* parentheses (), pgawk program: Profiling. (line 141) * password file: Passwd Functions. (line 16) * patsplit() function: String Functions. (line 293) * patterns: Patterns and Actions. (line 6) * patterns, comparison expressions as: Expression Patterns. (line 14) -* patterns, counts: Profiling. (line 113) +* patterns, counts: Profiling. (line 110) * patterns, default: Very Simple. (line 34) * patterns, empty: Empty. (line 6) * patterns, expressions as: Regexp Patterns. (line 6) @@ -26669,9 +26740,6 @@ Index * Perl: Future Extensions. (line 6) * Peters, Arno: Contributors. (line 85) * Peterson, Hal: Contributors. (line 40) -* pgawk program: Profiling. (line 6) -* pgawk program, awkprof.out file: Profiling. (line 10) -* pgawk program, dynamic profiling: Profiling. (line 174) * pipes, closing: Close Files And Pipes. (line 6) * pipes, input: Getline/Pipe. (line 6) @@ -26712,13 +26780,13 @@ Index * portability, NF variable, decrementing: Changing Fields. (line 115) * portability, operators: Increment Ops. (line 61) * portability, operators, not in POSIX awk: Precedence. (line 98) -* portability, POSIXLY_CORRECT environment variable: Options. (line 310) +* portability, POSIXLY_CORRECT environment variable: Options. (line 328) * portability, substr() function: String Functions. (line 512) * portable object files <1>: Translator i18n. (line 6) * portable object files: Explaining gettext. (line 36) * portable object files, converting to message object files: I18N Example. (line 62) -* portable object files, generating: Options. (line 135) +* portable object files, generating: Options. (line 144) * portable object template files: Explaining gettext. (line 30) * porting gawk: New Ports. (line 6) * positional specifiers, printf statement <1>: Printf Ordering. @@ -26762,11 +26830,11 @@ Index * POSIX awk, regular expressions and: Regexp Operators. (line 157) * POSIX awk, timestamps and: Time Functions. (line 6) * POSIX awk, | I/O operator and: Getline/Pipe. (line 52) -* POSIX mode: Options. (line 199) +* POSIX mode: Options. (line 222) * POSIX, awk and: Preface. (line 23) * POSIX, gawk extensions not included in: POSIX/GNU. (line 6) * POSIX, programs, implementing in awk: Clones. (line 6) -* POSIXLY_CORRECT environment variable: Options. (line 289) +* POSIXLY_CORRECT environment variable: Options. (line 307) * precedence <1>: Precedence. (line 6) * precedence: Increment Ops. (line 61) * precedence, regexp operators: Regexp Operators. (line 152) @@ -26800,14 +26868,14 @@ Index * printf statement, sprintf() function and: Round Function. (line 6) * printf statement, syntax of: Basic Printf. (line 6) * printing: Printing. (line 6) -* printing, list of options: Options. (line 142) +* printing, list of options: Options. (line 151) * printing, mailing labels: Labels Program. (line 6) * printing, unduplicated lines of text: Uniq Program. (line 6) * printing, user information: Id Program. (line 6) * private variables: Library Names. (line 11) * processes, two-way communications with: Two-way I/O. (line 23) * processing data: Basic High Level. (line 6) -* PROCINFO array <1>: Internals. (line 149) +* PROCINFO array <1>: Internals. (line 140) * PROCINFO array <2>: Id Program. (line 15) * PROCINFO array <3>: Group Functions. (line 6) * PROCINFO array <4>: Passwd Functions. (line 6) @@ -26816,8 +26884,8 @@ Index * PROCINFO array <7>: Auto-set. (line 123) * PROCINFO array: Obsolete. (line 11) * profiling awk programs: Profiling. (line 6) -* profiling awk programs, dynamically: Profiling. (line 174) -* profiling gawk, See pgawk program: Profiling. (line 6) +* profiling awk programs, dynamically: Profiling. (line 171) +* profiling gawk: Profiling. (line 6) * program, definition of: Getting Started. (line 21) * programmers, attractiveness of: Two-way I/O. (line 6) * programming conventions, --non-decimal-data option: Nondecimal Data. @@ -26841,23 +26909,23 @@ Index * programming, basic steps: Basic High Level. (line 19) * programming, concepts: Basic Concepts. (line 6) * pwcat program: Passwd Functions. (line 23) -* q debugger command (alias for quit): Miscellaneous Dgawk Commands. +* q debugger command (alias for quit): Miscellaneous Debugger Commands. (line 101) -* QSE Awk: Other Versions. (line 108) +* QSE Awk: Other Versions. (line 109) * question mark (?) regexp operator <1>: GNU Regexp Operators. (line 59) * question mark (?) regexp operator: Regexp Operators. (line 111) * question mark (?), ?: operator: Precedence. (line 92) -* QuikTrim Awk: Other Versions. (line 112) -* quit debugger command: Miscellaneous Dgawk Commands. +* QuikTrim Awk: Other Versions. (line 113) +* quit debugger command: Miscellaneous Debugger Commands. (line 101) -* QUIT signal (MS-Windows): Profiling. (line 207) +* QUIT signal (MS-Windows): Profiling. (line 206) * quoting <1>: Comments. (line 27) * quoting <2>: Long. (line 26) * quoting: Read Terminal. (line 25) * quoting, rules for: Quoting. (line 6) * quoting, tricks for: Quoting. (line 71) -* r debugger command (alias for run): Dgawk Execution Control. +* r debugger command (alias for run): Debugger Execution Control. (line 62) * Rakitzis, Byron: History Sorting. (line 25) * rand() function: Numeric Functions. (line 33) @@ -26903,8 +26971,8 @@ Index * regexp constants, slashes vs. quotes: Computed Regexps. (line 28) * regexp constants, vs. string constants: Computed Regexps. (line 38) * regexp, See regular expressions: Regexp. (line 6) -* register_deferred_variable() internal function: Internals. (line 149) -* register_open_hook() internal function: Internals. (line 160) +* register_deferred_variable() internal function: Internals. (line 140) +* register_open_hook() internal function: Internals. (line 151) * regular expressions: Regexp. (line 6) * regular expressions as field separators: Field Separators. (line 50) * regular expressions, anchors in: Regexp Operators. (line 22) @@ -26923,7 +26991,7 @@ Index (line 59) * regular expressions, gawk, command-line options: GNU Regexp Operators. (line 70) -* regular expressions, interval expressions and: Options. (line 224) +* regular expressions, interval expressions and: Options. (line 247) * regular expressions, leftmost longest match: Leftmost Longest. (line 6) * regular expressions, operators <1>: Regexp Operators. (line 6) @@ -26939,7 +27007,7 @@ Index * regular expressions, searching for: Egrep Program. (line 6) * relational operators, See comparison operators: Typing and Comparison. (line 9) -* return debugger command: Dgawk Execution Control. +* return debugger command: Debugger Execution Control. (line 54) * return statement, user-defined functions: Return Statement. (line 6) * return values, close() function: Close Files And Pipes. @@ -26992,12 +27060,12 @@ Index * Rubin, Paul <1>: Contributors. (line 16) * Rubin, Paul: History. (line 30) * rule, definition of: Getting Started. (line 21) -* run debugger command: Dgawk Execution Control. +* run debugger command: Debugger Execution Control. (line 62) * rvalues/lvalues: Assignment Ops. (line 32) -* s debugger command (alias for step): Dgawk Execution Control. +* s debugger command (alias for step): Debugger Execution Control. (line 68) -* sandbox mode: Options. (line 236) +* sandbox mode: Options. (line 254) * scalar values: Basic Data Typing. (line 13) * Schorr, Andrew: Acknowledgments. (line 60) * Schreiber, Bert: Acknowledgments. (line 38) @@ -27043,7 +27111,7 @@ Index (line 6) * shift, bitwise: Bitwise Functions. (line 32) * short-circuit operators: Boolean Ops. (line 57) -* si debugger command (alias for stepi): Dgawk Execution Control. +* si debugger command (alias for stepi): Debugger Execution Control. (line 76) * side effects <1>: Increment Ops. (line 11) * side effects: Concatenation. (line 42) @@ -27058,15 +27126,15 @@ Index * side effects, FILENAME variable: Getline Notes. (line 19) * side effects, function calls: Function Calls. (line 54) * side effects, statements: Action Overview. (line 32) -* SIGHUP signal: Profiling. (line 204) -* SIGINT signal (MS-Windows): Profiling. (line 207) -* signals, HUP/SIGHUP: Profiling. (line 204) -* signals, INT/SIGINT (MS-Windows): Profiling. (line 207) -* signals, QUIT/SIGQUIT (MS-Windows): Profiling. (line 207) -* signals, USR1/SIGUSR1: Profiling. (line 182) -* SIGQUIT signal (MS-Windows): Profiling. (line 207) -* SIGUSR1 signal: Profiling. (line 182) -* silent debugger command: Dgawk Execution Control. +* SIGHUP signal: Profiling. (line 203) +* SIGINT signal (MS-Windows): Profiling. (line 206) +* signals, HUP/SIGHUP: Profiling. (line 203) +* signals, INT/SIGINT (MS-Windows): Profiling. (line 206) +* signals, QUIT/SIGQUIT (MS-Windows): Profiling. (line 206) +* signals, USR1/SIGUSR1: Profiling. (line 180) +* SIGQUIT signal (MS-Windows): Profiling. (line 206) +* SIGUSR1 signal: Profiling. (line 180) +* silent debugger command: Debugger Execution Control. (line 10) * sin() function: Numeric Functions. (line 74) * single precision floating-point: Basic Data Typing. (line 36) @@ -27079,7 +27147,7 @@ Index (line 6) * Skywalker, Luke: Undocumented. (line 6) * sleep utility: Alarm Program. (line 109) -* Solaris, POSIX-compliant awk: Other Versions. (line 86) +* Solaris, POSIX-compliant awk: Other Versions. (line 87) * sort function, arrays, sorting: Array Sorting Functions. (line 6) * sort utility: Word Sorting. (line 50) @@ -27088,17 +27156,17 @@ Index (line 93) * source code, awka: Other Versions. (line 55) * source code, Brian Kernighan's awk: Other Versions. (line 13) -* source code, Busybox Awk: Other Versions. (line 78) +* source code, Busybox Awk: Other Versions. (line 79) * source code, gawk: Gawk Distribution. (line 6) -* source code, jawk: Other Versions. (line 96) -* source code, libmawk: Other Versions. (line 104) +* source code, jawk: Other Versions. (line 97) +* source code, libmawk: Other Versions. (line 105) * source code, mawk: Other Versions. (line 35) -* source code, mixing: Options. (line 105) +* source code, mixing: Options. (line 114) * source code, pawk: Other Versions. (line 69) -* source code, QSE Awk: Other Versions. (line 108) -* source code, QuikTrim Awk: Other Versions. (line 112) -* source code, Solaris awk: Other Versions. (line 86) -* source code, xgawk: Other Versions. (line 119) +* source code, QSE Awk: Other Versions. (line 109) +* source code, QuikTrim Awk: Other Versions. (line 113) +* source code, Solaris awk: Other Versions. (line 87) +* source code, xgawk: Other Versions. (line 120) * source files, search path for: Igawk Program. (line 368) * sparse arrays: Array Intro. (line 71) * Spencer, Henry: Glossary. (line 12) @@ -27126,12 +27194,12 @@ Index * statements, compound, control statements and: Statements. (line 10) * statements, control, in actions: Statements. (line 6) * statements, multiple: Statements/Lines. (line 91) -* step debugger command: Dgawk Execution Control. +* step debugger command: Debugger Execution Control. (line 68) -* stepi debugger command: Dgawk Execution Control. +* stepi debugger command: Debugger Execution Control. (line 76) -* stlen internal variable: Internals. (line 53) -* stptr internal variable: Internals. (line 53) +* stlen internal variable: Internals. (line 46) +* stptr internal variable: Internals. (line 46) * stream editors <1>: Simple Sed. (line 6) * stream editors: Field Splitting Summary. (line 47) @@ -27142,7 +27210,7 @@ Index (line 6) * string operators: Concatenation. (line 9) * string-matching operators: Regexp Usage. (line 19) -* strings: Internals. (line 86) +* strings: Internals. (line 77) * strings, converting <1>: Bitwise Functions. (line 107) * strings, converting: Conversion. (line 6) * strings, converting, numbers to: User-modified. (line 28) @@ -27182,8 +27250,8 @@ Index (line 148) * system() function: I/O Functions. (line 63) * systime() function (gawk): Time Functions. (line 64) -* t debugger command (alias for tbreak): Breakpoint Control. (line 89) -* tbreak debugger command: Breakpoint Control. (line 89) +* t debugger command (alias for tbreak): Breakpoint Control. (line 90) +* tbreak debugger command: Breakpoint Control. (line 90) * Tcl: Library Names. (line 57) * TCP/IP: TCP/IP Networking. (line 6) * TCP/IP, support for: Special Network. (line 6) @@ -27229,10 +27297,10 @@ Index * tolower() function: String Functions. (line 523) * toupper() function: String Functions. (line 529) * tr utility: Translate Program. (line 6) -* trace debugger command: Miscellaneous Dgawk Commands. +* trace debugger command: Miscellaneous Debugger Commands. (line 110) * translate.awk program: Translate Program. (line 55) -* troubleshooting, --non-decimal-data option: Options. (line 166) +* troubleshooting, --non-decimal-data option: Options. (line 182) * troubleshooting, == operator: Comparison Operators. (line 37) * troubleshooting, awk uses FS not IFS: Field Separators. (line 29) @@ -27272,8 +27340,8 @@ Index * trunc-mod operation: Arithmetic Ops. (line 66) * truth values: Truth Values. (line 6) * type conversion: Conversion. (line 21) -* type internal variable: Internals. (line 66) -* u debugger command (alias for until): Dgawk Execution Control. +* type internal variable: Internals. (line 59) +* u debugger command (alias for until): Debugger Execution Control. (line 83) * undefined functions: Pass By Value/Reference. (line 71) @@ -27298,23 +27366,23 @@ Index (line 72) * Unix, awk scripts and: Executable Scripts. (line 6) * UNIXROOT variable, on OS/2 systems: PC Using. (line 17) -* unref() internal function: Internals. (line 101) +* unref() internal function: Internals. (line 92) * unsigned integers: Basic Data Typing. (line 30) -* until debugger command: Dgawk Execution Control. +* until debugger command: Debugger Execution Control. (line 83) * unwatch debugger command: Viewing And Changing Data. (line 84) -* up debugger command: Dgawk Stack. (line 33) -* update_ERRNO() internal function: Internals. (line 139) -* update_ERRNO_saved() internal function: Internals. (line 144) +* up debugger command: Execution Stack. (line 33) +* update_ERRNO() internal function: Internals. (line 130) +* update_ERRNO_saved() internal function: Internals. (line 135) * user database, reading: Passwd Functions. (line 6) * user-defined, functions: User-defined. (line 6) -* user-defined, functions, counts: Profiling. (line 132) +* user-defined, functions, counts: Profiling. (line 129) * user-defined, variables: Variables. (line 6) * user-modifiable variables: User-modified. (line 6) * users, information about, printing: Id Program. (line 6) * users, information about, retrieving: Passwd Functions. (line 16) -* USR1 signal: Profiling. (line 182) +* USR1 signal: Profiling. (line 180) * values, numeric: Basic Data Typing. (line 13) * values, string: Basic Data Typing. (line 13) * variable typing: Typing and Comparison. @@ -27357,7 +27425,7 @@ Index * vertical bar (|), || operator <1>: Precedence. (line 89) * vertical bar (|), || operator: Boolean Ops. (line 57) * Vinschen, Corinna: Acknowledgments. (line 60) -* vname internal variable: Internals. (line 71) +* vname internal variable: Internals. (line 64) * w debugger command (alias for watch): Viewing And Changing Data. (line 67) * w utility: Constant Size. (line 22) @@ -27365,7 +27433,7 @@ Index * Wall, Larry <1>: Future Extensions. (line 6) * Wall, Larry: Array Intro. (line 6) * Wallin, Anders: Acknowledgments. (line 60) -* warnings, issuing: Options. (line 147) +* warnings, issuing: Options. (line 163) * watch debugger command: Viewing And Changing Data. (line 67) * wc utility: Wc Program. (line 6) @@ -27377,7 +27445,7 @@ Index * whitespace, as field separators: Default Field Splitting. (line 6) * whitespace, functions, calling: Calling Built-in. (line 10) -* whitespace, newlines as: Options. (line 205) +* whitespace, newlines as: Options. (line 228) * Williams, Kent: Contributors. (line 35) * Woehlke, Matthew: Contributors. (line 79) * Woods, John: Contributors. (line 28) @@ -27391,11 +27459,11 @@ Index * words, counting: Wc Program. (line 6) * words, duplicate, searching for: Dupword Program. (line 6) * words, usage counts, generating: Word Sorting. (line 6) -* wstlen internal variable: Internals. (line 61) -* wstptr internal variable: Internals. (line 61) -* xgawk: Other Versions. (line 119) +* wstlen internal variable: Internals. (line 54) +* wstptr internal variable: Internals. (line 54) +* xgawk: Other Versions. (line 120) * xgettext utility: String Extraction. (line 13) -* XML (eXtensible Markup Language): Internals. (line 160) +* XML (eXtensible Markup Language): Internals. (line 151) * XOR bitwise operation: Bitwise Functions. (line 6) * xor() function (gawk): Bitwise Functions. (line 54) * Yawitz, Efraim: Contributors. (line 106) @@ -27405,8 +27473,8 @@ Index * zero, negative vs. positive: Unexpected Results. (line 28) * zerofile.awk program: Empty Files. (line 21) * Zoulas, Christos: Contributors. (line 67) +* {} (braces): Profiling. (line 134) * {} (braces), actions and: Action Overview. (line 19) -* {} (braces), pgawk program: Profiling. (line 137) * {} (braces), statements, grouping: Statements. (line 10) * | (vertical bar): Regexp Operators. (line 69) * | (vertical bar), | operator (I/O) <1>: Precedence. (line 65) @@ -27433,416 +27501,417 @@ Index Tag Table: Node: Top1346 -Node: Foreword30270 -Node: Preface34615 -Ref: Preface-Footnote-137668 -Ref: Preface-Footnote-237774 -Node: History38006 -Node: Names40397 -Ref: Names-Footnote-141874 -Node: This Manual41946 -Ref: This Manual-Footnote-146893 -Node: Conventions46993 -Node: Manual History49127 -Ref: Manual History-Footnote-152397 -Ref: Manual History-Footnote-252438 -Node: How To Contribute52512 -Node: Acknowledgments53656 -Node: Getting Started57987 -Node: Running gawk60366 -Node: One-shot61552 -Node: Read Terminal62777 -Ref: Read Terminal-Footnote-164427 -Ref: Read Terminal-Footnote-264703 -Node: Long64874 -Node: Executable Scripts66250 -Ref: Executable Scripts-Footnote-168119 -Ref: Executable Scripts-Footnote-268221 -Node: Comments68672 -Node: Quoting71139 -Node: DOS Quoting75762 -Node: Sample Data Files76437 -Node: Very Simple79469 -Node: Two Rules84068 -Node: More Complex86215 -Ref: More Complex-Footnote-189145 -Node: Statements/Lines89230 -Ref: Statements/Lines-Footnote-193692 -Node: Other Features93957 -Node: When94885 -Node: Invoking Gawk97032 -Node: Command Line98417 -Node: Options99200 -Ref: Options-Footnote-1112637 -Node: Other Arguments112662 -Node: Naming Standard Input115320 -Node: Environment Variables116414 -Node: AWKPATH Variable116858 -Ref: AWKPATH Variable-Footnote-1119455 -Node: Other Environment Variables119715 -Node: Exit Status122055 -Node: Include Files122730 -Node: Obsolete126215 -Node: Undocumented126901 -Node: Regexp127142 -Node: Regexp Usage128531 -Node: Escape Sequences130557 -Node: Regexp Operators136320 -Ref: Regexp Operators-Footnote-1143517 -Ref: Regexp Operators-Footnote-2143664 -Node: Bracket Expressions143762 -Ref: table-char-classes145652 -Node: GNU Regexp Operators148175 -Node: Case-sensitivity151898 -Ref: Case-sensitivity-Footnote-1154866 -Ref: Case-sensitivity-Footnote-2155101 -Node: Leftmost Longest155209 -Node: Computed Regexps156410 -Node: Reading Files159820 -Node: Records161761 -Ref: Records-Footnote-1170435 -Node: Fields170472 -Ref: Fields-Footnote-1173505 -Node: Nonconstant Fields173591 -Node: Changing Fields175793 -Node: Field Separators181774 -Node: Default Field Splitting184403 -Node: Regexp Field Splitting185520 -Node: Single Character Fields188862 -Node: Command Line Field Separator189921 -Node: Field Splitting Summary193362 -Ref: Field Splitting Summary-Footnote-1196554 -Node: Constant Size196655 -Node: Splitting By Content201239 -Ref: Splitting By Content-Footnote-1204965 -Node: Multiple Line205005 -Ref: Multiple Line-Footnote-1210852 -Node: Getline211031 -Node: Plain Getline213259 -Node: Getline/Variable215348 -Node: Getline/File216489 -Node: Getline/Variable/File217811 -Ref: Getline/Variable/File-Footnote-1219410 -Node: Getline/Pipe219497 -Node: Getline/Variable/Pipe222057 -Node: Getline/Coprocess223164 -Node: Getline/Variable/Coprocess224407 -Node: Getline Notes225121 -Node: Getline Summary227063 -Ref: table-getline-variants227406 -Node: Command line directories228262 -Node: Printing228887 -Node: Print230518 -Node: Print Examples231855 -Node: Output Separators234639 -Node: OFMT236399 -Node: Printf237757 -Node: Basic Printf238663 -Node: Control Letters240202 -Node: Format Modifiers244014 -Node: Printf Examples250023 -Node: Redirection252738 -Node: Special Files259722 -Node: Special FD260255 -Ref: Special FD-Footnote-1263880 -Node: Special Network263954 -Node: Special Caveats264804 -Node: Close Files And Pipes265600 -Ref: Close Files And Pipes-Footnote-1272623 -Ref: Close Files And Pipes-Footnote-2272771 -Node: Expressions272921 -Node: Values274053 -Node: Constants274729 -Node: Scalar Constants275409 -Ref: Scalar Constants-Footnote-1276268 -Node: Nondecimal-numbers276450 -Node: Regexp Constants279509 -Node: Using Constant Regexps279984 -Node: Variables283039 -Node: Using Variables283694 -Node: Assignment Options285418 -Node: Conversion287290 -Ref: table-locale-affects292666 -Ref: Conversion-Footnote-1293290 -Node: All Operators293399 -Node: Arithmetic Ops294029 -Node: Concatenation296534 -Ref: Concatenation-Footnote-1299327 -Node: Assignment Ops299447 -Ref: table-assign-ops304435 -Node: Increment Ops305843 -Node: Truth Values and Conditions309313 -Node: Truth Values310396 -Node: Typing and Comparison311445 -Node: Variable Typing312234 -Ref: Variable Typing-Footnote-1316131 -Node: Comparison Operators316253 -Ref: table-relational-ops316663 -Node: POSIX String Comparison320212 -Ref: POSIX String Comparison-Footnote-1321168 -Node: Boolean Ops321306 -Ref: Boolean Ops-Footnote-1325384 -Node: Conditional Exp325475 -Node: Function Calls327207 -Node: Precedence330801 -Node: Locales334470 -Node: Patterns and Actions335559 -Node: Pattern Overview336613 -Node: Regexp Patterns338282 -Node: Expression Patterns338825 -Node: Ranges342510 -Node: BEGIN/END345476 -Node: Using BEGIN/END346238 -Ref: Using BEGIN/END-Footnote-1348969 -Node: I/O And BEGIN/END349075 -Node: BEGINFILE/ENDFILE351357 -Node: Empty354250 -Node: Using Shell Variables354566 -Node: Action Overview356851 -Node: Statements359208 -Node: If Statement361062 -Node: While Statement362561 -Node: Do Statement364605 -Node: For Statement365761 -Node: Switch Statement368913 -Node: Break Statement371010 -Node: Continue Statement373000 -Node: Next Statement374793 -Node: Nextfile Statement377183 -Node: Exit Statement379728 -Node: Built-in Variables382144 -Node: User-modified383239 -Ref: User-modified-Footnote-1391265 -Node: Auto-set391327 -Ref: Auto-set-Footnote-1400618 -Node: ARGC and ARGV400823 -Node: Arrays404674 -Node: Array Basics406179 -Node: Array Intro407005 -Node: Reference to Elements411323 -Node: Assigning Elements413593 -Node: Array Example414084 -Node: Scanning an Array415816 -Node: Controlling Scanning418130 -Ref: Controlling Scanning-Footnote-1423063 -Node: Delete423379 -Ref: Delete-Footnote-1425814 -Node: Numeric Array Subscripts425871 -Node: Uninitialized Subscripts428054 -Node: Multi-dimensional429682 -Node: Multi-scanning432776 -Node: Arrays of Arrays434367 -Node: Functions439012 -Node: Built-in439834 -Node: Calling Built-in440912 -Node: Numeric Functions442900 -Ref: Numeric Functions-Footnote-1446665 -Ref: Numeric Functions-Footnote-2447022 -Ref: Numeric Functions-Footnote-3447070 -Node: String Functions447339 -Ref: String Functions-Footnote-1470836 -Ref: String Functions-Footnote-2470965 -Ref: String Functions-Footnote-3471213 -Node: Gory Details471300 -Ref: table-sub-escapes472979 -Ref: table-sub-posix-92474333 -Ref: table-sub-proposed475676 -Ref: table-posix-sub477026 -Ref: table-gensub-escapes478572 -Ref: Gory Details-Footnote-1479779 -Ref: Gory Details-Footnote-2479830 -Node: I/O Functions479981 -Ref: I/O Functions-Footnote-1486636 -Node: Time Functions486783 -Ref: Time Functions-Footnote-1497675 -Ref: Time Functions-Footnote-2497743 -Ref: Time Functions-Footnote-3497901 -Ref: Time Functions-Footnote-4498012 -Ref: Time Functions-Footnote-5498124 -Ref: Time Functions-Footnote-6498351 -Node: Bitwise Functions498617 -Ref: table-bitwise-ops499175 -Ref: Bitwise Functions-Footnote-1503335 -Node: Type Functions503519 -Node: I18N Functions503989 -Node: User-defined505616 -Node: Definition Syntax506420 -Ref: Definition Syntax-Footnote-1511330 -Node: Function Example511399 -Node: Function Caveats513993 -Node: Calling A Function514414 -Node: Variable Scope515529 -Node: Pass By Value/Reference517504 -Node: Return Statement520944 -Node: Dynamic Typing523925 -Node: Indirect Calls524660 -Node: Internationalization534345 -Node: I18N and L10N535771 -Node: Explaining gettext536457 -Ref: Explaining gettext-Footnote-1541523 -Ref: Explaining gettext-Footnote-2541707 -Node: Programmer i18n541872 -Node: Translator i18n546072 -Node: String Extraction546865 -Ref: String Extraction-Footnote-1547826 -Node: Printf Ordering547912 -Ref: Printf Ordering-Footnote-1550696 -Node: I18N Portability550760 -Ref: I18N Portability-Footnote-1553209 -Node: I18N Example553272 -Ref: I18N Example-Footnote-1555907 -Node: Gawk I18N555979 -Node: Advanced Features556596 -Node: Nondecimal Data558109 -Node: Array Sorting559692 -Node: Controlling Array Traversal560389 -Node: Array Sorting Functions568626 -Ref: Array Sorting Functions-Footnote-1572300 -Ref: Array Sorting Functions-Footnote-2572393 -Node: Two-way I/O572587 -Ref: Two-way I/O-Footnote-1578019 -Node: TCP/IP Networking578089 -Node: Profiling580933 -Node: Library Functions588407 -Ref: Library Functions-Footnote-1591414 -Node: Library Names591585 -Ref: Library Names-Footnote-1595056 -Ref: Library Names-Footnote-2595276 -Node: General Functions595362 -Node: Strtonum Function596315 -Node: Assert Function599245 -Node: Round Function602571 -Node: Cliff Random Function604114 -Node: Ordinal Functions605130 -Ref: Ordinal Functions-Footnote-1608200 -Ref: Ordinal Functions-Footnote-2608452 -Node: Join Function608661 -Ref: Join Function-Footnote-1610432 -Node: Gettimeofday Function610632 -Node: Data File Management614347 -Node: Filetrans Function614979 -Node: Rewind Function619118 -Node: File Checking620505 -Node: Empty Files621599 -Node: Ignoring Assigns623829 -Node: Getopt Function625382 -Ref: Getopt Function-Footnote-1636686 -Node: Passwd Functions636889 -Ref: Passwd Functions-Footnote-1645864 -Node: Group Functions645952 -Node: Walking Arrays654036 -Node: Sample Programs655605 -Node: Running Examples656270 -Node: Clones656998 -Node: Cut Program658222 -Node: Egrep Program668067 -Ref: Egrep Program-Footnote-1675840 -Node: Id Program675950 -Node: Split Program679566 -Ref: Split Program-Footnote-1683085 -Node: Tee Program683213 -Node: Uniq Program686016 -Node: Wc Program693445 -Ref: Wc Program-Footnote-1697711 -Ref: Wc Program-Footnote-2697911 -Node: Miscellaneous Programs698003 -Node: Dupword Program699191 -Node: Alarm Program701222 -Node: Translate Program705971 -Ref: Translate Program-Footnote-1710358 -Ref: Translate Program-Footnote-2710586 -Node: Labels Program710720 -Ref: Labels Program-Footnote-1714091 -Node: Word Sorting714175 -Node: History Sorting718059 -Node: Extract Program719898 -Ref: Extract Program-Footnote-1727381 -Node: Simple Sed727509 -Node: Igawk Program730571 -Ref: Igawk Program-Footnote-1745728 -Ref: Igawk Program-Footnote-2745929 -Node: Anagram Program746067 -Node: Signature Program749135 -Node: Debugger750235 -Node: Debugging751146 -Node: Debugging Concepts751559 -Node: Debugging Terms753415 -Node: Awk Debugging756038 -Node: Sample dgawk session756930 -Node: dgawk invocation757422 -Node: Finding The Bug758604 -Node: List of Debugger Commands765090 -Node: Breakpoint Control766401 -Node: Dgawk Execution Control770037 -Node: Viewing And Changing Data773388 -Node: Dgawk Stack776725 -Node: Dgawk Info778185 -Node: Miscellaneous Dgawk Commands782133 -Node: Readline Support787561 -Node: Dgawk Limitations788399 -Node: Language History790588 -Node: V7/SVR3.1792100 -Node: SVR4794421 -Node: POSIX795863 -Node: BTL796871 -Node: POSIX/GNU797605 -Node: Common Extensions802756 -Node: Ranges and Locales803863 -Ref: Ranges and Locales-Footnote-1808467 -Node: Contributors808688 -Node: Installation812950 -Node: Gawk Distribution813844 -Node: Getting814328 -Node: Extracting815154 -Node: Distribution contents816846 -Node: Unix Installation822068 -Node: Quick Installation822685 -Node: Additional Configuration Options824647 -Node: Configuration Philosophy826124 -Node: Non-Unix Installation828466 -Node: PC Installation828924 -Node: PC Binary Installation830223 -Node: PC Compiling832071 -Node: PC Testing835015 -Node: PC Using836191 -Node: Cygwin840376 -Node: MSYS841376 -Node: VMS Installation841890 -Node: VMS Compilation842493 -Ref: VMS Compilation-Footnote-1843500 -Node: VMS Installation Details843558 -Node: VMS Running845193 -Node: VMS Old Gawk846800 -Node: Bugs847274 -Node: Other Versions851126 -Node: Notes856407 -Node: Compatibility Mode857099 -Node: Additions857882 -Node: Accessing The Source858694 -Node: Adding Code860119 -Node: New Ports866086 -Node: Dynamic Extensions870199 -Node: Internals871575 -Node: Plugin License880678 -Node: Sample Library881312 -Node: Internal File Description881998 -Node: Internal File Ops885713 -Ref: Internal File Ops-Footnote-1890494 -Node: Using Internal File Ops890634 -Node: Future Extensions893011 -Node: Basic Concepts895515 -Node: Basic High Level896272 -Ref: Basic High Level-Footnote-1900307 -Node: Basic Data Typing900492 -Node: Floating Point Issues905017 -Node: String Conversion Precision906100 -Ref: String Conversion Precision-Footnote-1907800 -Node: Unexpected Results907909 -Node: POSIX Floating Point Problems909735 -Ref: POSIX Floating Point Problems-Footnote-1913440 -Node: Glossary913478 -Node: Copying938454 -Node: GNU Free Documentation License976011 -Node: Index1001148 +Node: Foreword30346 +Node: Preface34691 +Ref: Preface-Footnote-137744 +Ref: Preface-Footnote-237850 +Node: History38082 +Node: Names40473 +Ref: Names-Footnote-141950 +Node: This Manual42022 +Ref: This Manual-Footnote-146960 +Node: Conventions47060 +Node: Manual History49194 +Ref: Manual History-Footnote-152464 +Ref: Manual History-Footnote-252505 +Node: How To Contribute52579 +Node: Acknowledgments53723 +Node: Getting Started58054 +Node: Running gawk60433 +Node: One-shot61619 +Node: Read Terminal62844 +Ref: Read Terminal-Footnote-164494 +Ref: Read Terminal-Footnote-264770 +Node: Long64941 +Node: Executable Scripts66317 +Ref: Executable Scripts-Footnote-168186 +Ref: Executable Scripts-Footnote-268288 +Node: Comments68739 +Node: Quoting71206 +Node: DOS Quoting75829 +Node: Sample Data Files76504 +Node: Very Simple79536 +Node: Two Rules84135 +Node: More Complex86282 +Ref: More Complex-Footnote-189212 +Node: Statements/Lines89297 +Ref: Statements/Lines-Footnote-193759 +Node: Other Features94024 +Node: When94952 +Node: Invoking Gawk97099 +Node: Command Line98484 +Node: Options99267 +Ref: Options-Footnote-1113412 +Node: Other Arguments113437 +Node: Naming Standard Input116095 +Node: Environment Variables117189 +Node: AWKPATH Variable117633 +Ref: AWKPATH Variable-Footnote-1120230 +Node: Other Environment Variables120490 +Node: Exit Status122830 +Node: Include Files123505 +Node: Obsolete126990 +Node: Undocumented127676 +Node: Regexp127917 +Node: Regexp Usage129306 +Node: Escape Sequences131332 +Node: Regexp Operators137095 +Ref: Regexp Operators-Footnote-1144292 +Ref: Regexp Operators-Footnote-2144439 +Node: Bracket Expressions144537 +Ref: table-char-classes146427 +Node: GNU Regexp Operators148950 +Node: Case-sensitivity152673 +Ref: Case-sensitivity-Footnote-1155641 +Ref: Case-sensitivity-Footnote-2155876 +Node: Leftmost Longest155984 +Node: Computed Regexps157185 +Node: Reading Files160595 +Node: Records162536 +Ref: Records-Footnote-1171210 +Node: Fields171247 +Ref: Fields-Footnote-1174280 +Node: Nonconstant Fields174366 +Node: Changing Fields176568 +Node: Field Separators182549 +Node: Default Field Splitting185178 +Node: Regexp Field Splitting186295 +Node: Single Character Fields189637 +Node: Command Line Field Separator190696 +Node: Field Splitting Summary194137 +Ref: Field Splitting Summary-Footnote-1197329 +Node: Constant Size197430 +Node: Splitting By Content202014 +Ref: Splitting By Content-Footnote-1205740 +Node: Multiple Line205780 +Ref: Multiple Line-Footnote-1211627 +Node: Getline211806 +Node: Plain Getline214034 +Node: Getline/Variable216123 +Node: Getline/File217264 +Node: Getline/Variable/File218586 +Ref: Getline/Variable/File-Footnote-1220185 +Node: Getline/Pipe220272 +Node: Getline/Variable/Pipe222832 +Node: Getline/Coprocess223939 +Node: Getline/Variable/Coprocess225182 +Node: Getline Notes225896 +Node: Getline Summary227838 +Ref: table-getline-variants228181 +Node: Command line directories229037 +Node: Printing229662 +Node: Print231293 +Node: Print Examples232630 +Node: Output Separators235414 +Node: OFMT237174 +Node: Printf238532 +Node: Basic Printf239438 +Node: Control Letters240977 +Node: Format Modifiers244789 +Node: Printf Examples250798 +Node: Redirection253513 +Node: Special Files260497 +Node: Special FD261030 +Ref: Special FD-Footnote-1264655 +Node: Special Network264729 +Node: Special Caveats265579 +Node: Close Files And Pipes266375 +Ref: Close Files And Pipes-Footnote-1273398 +Ref: Close Files And Pipes-Footnote-2273546 +Node: Expressions273696 +Node: Values274828 +Node: Constants275504 +Node: Scalar Constants276184 +Ref: Scalar Constants-Footnote-1277043 +Node: Nondecimal-numbers277225 +Node: Regexp Constants280284 +Node: Using Constant Regexps280759 +Node: Variables283814 +Node: Using Variables284469 +Node: Assignment Options286193 +Node: Conversion288065 +Ref: table-locale-affects293441 +Ref: Conversion-Footnote-1294065 +Node: All Operators294174 +Node: Arithmetic Ops294804 +Node: Concatenation297309 +Ref: Concatenation-Footnote-1300102 +Node: Assignment Ops300222 +Ref: table-assign-ops305210 +Node: Increment Ops306618 +Node: Truth Values and Conditions310088 +Node: Truth Values311171 +Node: Typing and Comparison312220 +Node: Variable Typing313009 +Ref: Variable Typing-Footnote-1316906 +Node: Comparison Operators317028 +Ref: table-relational-ops317438 +Node: POSIX String Comparison320987 +Ref: POSIX String Comparison-Footnote-1321943 +Node: Boolean Ops322081 +Ref: Boolean Ops-Footnote-1326159 +Node: Conditional Exp326250 +Node: Function Calls327982 +Node: Precedence331576 +Node: Locales335245 +Node: Patterns and Actions336334 +Node: Pattern Overview337388 +Node: Regexp Patterns339057 +Node: Expression Patterns339600 +Node: Ranges343285 +Node: BEGIN/END346251 +Node: Using BEGIN/END347013 +Ref: Using BEGIN/END-Footnote-1349744 +Node: I/O And BEGIN/END349850 +Node: BEGINFILE/ENDFILE352132 +Node: Empty355025 +Node: Using Shell Variables355341 +Node: Action Overview357626 +Node: Statements359983 +Node: If Statement361837 +Node: While Statement363336 +Node: Do Statement365380 +Node: For Statement366536 +Node: Switch Statement369688 +Node: Break Statement371785 +Node: Continue Statement373775 +Node: Next Statement375568 +Node: Nextfile Statement377958 +Node: Exit Statement380503 +Node: Built-in Variables382919 +Node: User-modified384014 +Ref: User-modified-Footnote-1392040 +Node: Auto-set392102 +Ref: Auto-set-Footnote-1401393 +Node: ARGC and ARGV401598 +Node: Arrays405449 +Node: Array Basics406954 +Node: Array Intro407780 +Node: Reference to Elements412098 +Node: Assigning Elements414368 +Node: Array Example414859 +Node: Scanning an Array416591 +Node: Controlling Scanning418905 +Ref: Controlling Scanning-Footnote-1423838 +Node: Delete424154 +Ref: Delete-Footnote-1426589 +Node: Numeric Array Subscripts426646 +Node: Uninitialized Subscripts428829 +Node: Multi-dimensional430457 +Node: Multi-scanning433551 +Node: Arrays of Arrays435142 +Node: Functions439787 +Node: Built-in440609 +Node: Calling Built-in441687 +Node: Numeric Functions443675 +Ref: Numeric Functions-Footnote-1447440 +Ref: Numeric Functions-Footnote-2447797 +Ref: Numeric Functions-Footnote-3447845 +Node: String Functions448114 +Ref: String Functions-Footnote-1471611 +Ref: String Functions-Footnote-2471740 +Ref: String Functions-Footnote-3471988 +Node: Gory Details472075 +Ref: table-sub-escapes473754 +Ref: table-sub-posix-92475108 +Ref: table-sub-proposed476451 +Ref: table-posix-sub477801 +Ref: table-gensub-escapes479347 +Ref: Gory Details-Footnote-1480554 +Ref: Gory Details-Footnote-2480605 +Node: I/O Functions480756 +Ref: I/O Functions-Footnote-1487411 +Node: Time Functions487558 +Ref: Time Functions-Footnote-1498450 +Ref: Time Functions-Footnote-2498518 +Ref: Time Functions-Footnote-3498676 +Ref: Time Functions-Footnote-4498787 +Ref: Time Functions-Footnote-5498899 +Ref: Time Functions-Footnote-6499126 +Node: Bitwise Functions499392 +Ref: table-bitwise-ops499950 +Ref: Bitwise Functions-Footnote-1504110 +Node: Type Functions504294 +Node: I18N Functions504764 +Node: User-defined506391 +Node: Definition Syntax507195 +Ref: Definition Syntax-Footnote-1512105 +Node: Function Example512174 +Node: Function Caveats514768 +Node: Calling A Function515189 +Node: Variable Scope516304 +Node: Pass By Value/Reference518279 +Node: Return Statement521719 +Node: Dynamic Typing524700 +Node: Indirect Calls525435 +Node: Internationalization535120 +Node: I18N and L10N536546 +Node: Explaining gettext537232 +Ref: Explaining gettext-Footnote-1542298 +Ref: Explaining gettext-Footnote-2542482 +Node: Programmer i18n542647 +Node: Translator i18n546847 +Node: String Extraction547640 +Ref: String Extraction-Footnote-1548601 +Node: Printf Ordering548687 +Ref: Printf Ordering-Footnote-1551471 +Node: I18N Portability551535 +Ref: I18N Portability-Footnote-1553984 +Node: I18N Example554047 +Ref: I18N Example-Footnote-1556682 +Node: Gawk I18N556754 +Node: Advanced Features557371 +Node: Nondecimal Data558884 +Node: Array Sorting560467 +Node: Controlling Array Traversal561164 +Node: Array Sorting Functions569401 +Ref: Array Sorting Functions-Footnote-1573075 +Ref: Array Sorting Functions-Footnote-2573168 +Node: Two-way I/O573362 +Ref: Two-way I/O-Footnote-1578794 +Node: TCP/IP Networking578864 +Node: Profiling581708 +Node: Library Functions589162 +Ref: Library Functions-Footnote-1592169 +Node: Library Names592340 +Ref: Library Names-Footnote-1595811 +Ref: Library Names-Footnote-2596031 +Node: General Functions596117 +Node: Strtonum Function597070 +Node: Assert Function600000 +Node: Round Function603326 +Node: Cliff Random Function604869 +Node: Ordinal Functions605885 +Ref: Ordinal Functions-Footnote-1608955 +Ref: Ordinal Functions-Footnote-2609207 +Node: Join Function609416 +Ref: Join Function-Footnote-1611187 +Node: Gettimeofday Function611387 +Node: Data File Management615102 +Node: Filetrans Function615734 +Node: Rewind Function619873 +Node: File Checking621260 +Node: Empty Files622354 +Node: Ignoring Assigns624584 +Node: Getopt Function626137 +Ref: Getopt Function-Footnote-1637441 +Node: Passwd Functions637644 +Ref: Passwd Functions-Footnote-1646619 +Node: Group Functions646707 +Node: Walking Arrays654791 +Node: Sample Programs656360 +Node: Running Examples657025 +Node: Clones657753 +Node: Cut Program658977 +Node: Egrep Program668822 +Ref: Egrep Program-Footnote-1676595 +Node: Id Program676705 +Node: Split Program680321 +Ref: Split Program-Footnote-1683840 +Node: Tee Program683968 +Node: Uniq Program686771 +Node: Wc Program694200 +Ref: Wc Program-Footnote-1698466 +Ref: Wc Program-Footnote-2698666 +Node: Miscellaneous Programs698758 +Node: Dupword Program699946 +Node: Alarm Program701977 +Node: Translate Program706726 +Ref: Translate Program-Footnote-1711113 +Ref: Translate Program-Footnote-2711341 +Node: Labels Program711475 +Ref: Labels Program-Footnote-1714846 +Node: Word Sorting714930 +Node: History Sorting718814 +Node: Extract Program720653 +Ref: Extract Program-Footnote-1728136 +Node: Simple Sed728264 +Node: Igawk Program731326 +Ref: Igawk Program-Footnote-1746483 +Ref: Igawk Program-Footnote-2746684 +Node: Anagram Program746822 +Node: Signature Program749890 +Node: Debugger750990 +Node: Debugging751942 +Node: Debugging Concepts752375 +Node: Debugging Terms754231 +Node: Awk Debugging756828 +Node: Sample Debugging Session757720 +Node: Debugger Invocation758240 +Node: Finding The Bug759569 +Node: List of Debugger Commands766057 +Node: Breakpoint Control767391 +Node: Debugger Execution Control771055 +Node: Viewing And Changing Data774415 +Node: Execution Stack777771 +Node: Debugger Info779238 +Node: Miscellaneous Debugger Commands783219 +Node: Readline Support788664 +Node: Limitations789495 +Node: Language History791747 +Node: V7/SVR3.1793259 +Node: SVR4795580 +Node: POSIX797022 +Node: BTL798030 +Node: POSIX/GNU798764 +Node: Common Extensions803915 +Node: Ranges and Locales805022 +Ref: Ranges and Locales-Footnote-1809626 +Node: Contributors809847 +Node: Installation814108 +Node: Gawk Distribution815002 +Node: Getting815486 +Node: Extracting816312 +Node: Distribution contents818004 +Node: Unix Installation823226 +Node: Quick Installation823843 +Node: Additional Configuration Options825805 +Node: Configuration Philosophy827282 +Node: Non-Unix Installation829624 +Node: PC Installation830082 +Node: PC Binary Installation831381 +Node: PC Compiling833229 +Node: PC Testing836173 +Node: PC Using837349 +Node: Cygwin841534 +Node: MSYS842534 +Node: VMS Installation843048 +Node: VMS Compilation843651 +Ref: VMS Compilation-Footnote-1844658 +Node: VMS Installation Details844716 +Node: VMS Running846351 +Node: VMS Old Gawk847958 +Node: Bugs848432 +Node: Other Versions852284 +Node: Notes857599 +Node: Compatibility Mode858291 +Node: Additions859074 +Node: Accessing The Source859886 +Node: Adding Code861311 +Node: New Ports867278 +Node: Dynamic Extensions871391 +Node: Internals872831 +Node: Plugin License881350 +Node: Loading Extensions881988 +Node: Sample Library883798 +Node: Internal File Description884488 +Node: Internal File Ops888203 +Ref: Internal File Ops-Footnote-1892927 +Node: Using Internal File Ops893067 +Node: Future Extensions895444 +Node: Basic Concepts897948 +Node: Basic High Level898705 +Ref: Basic High Level-Footnote-1902740 +Node: Basic Data Typing902925 +Node: Floating Point Issues907450 +Node: String Conversion Precision908533 +Ref: String Conversion Precision-Footnote-1910233 +Node: Unexpected Results910342 +Node: POSIX Floating Point Problems912168 +Ref: POSIX Floating Point Problems-Footnote-1915873 +Node: Glossary915911 +Node: Copying940887 +Node: GNU Free Documentation License978444 +Node: Index1003581 End Tag Table diff --git a/doc/gawk.texi b/doc/gawk.texi index 33e66d70..13681850 100644 --- a/doc/gawk.texi +++ b/doc/gawk.texi @@ -290,7 +290,7 @@ particular records in a file and perform operations upon them. * Library Functions:: A Library of @command{awk} Functions. * Sample Programs:: Many @command{awk} programs with complete explanations. -* Debugger:: The @code{dgawk} debugger. +* Debugger:: The @code{gawk} debugger. * Language History:: The evolution of the @command{awk} language. * Installation:: Installing @command{gawk} under various @@ -614,23 +614,23 @@ particular records in a file and perform operations upon them. * Anagram Program:: Finding anagrams from a dictionary. * Signature Program:: People do amazing things with too much time on their hands. -* Debugging:: Introduction to @command{dgawk}. -* Debugging Concepts:: Debugging In General. +* Debugging:: Introduction to @command{gawk} Debugger. +* Debugging Concepts:: Debugging in General. * Debugging Terms:: Additional Debugging Concepts. * Awk Debugging:: Awk Debugging. -* Sample dgawk session:: Sample @command{dgawk} session. -* dgawk invocation:: @command{dgawk} Invocation. -* Finding The Bug:: Finding The Bug. -* List of Debugger Commands:: Main @command{dgawk} Commands. -* Breakpoint Control:: Control of breakpoints. -* Dgawk Execution Control:: Control of execution. -* Viewing And Changing Data:: Viewing and changing data. -* Dgawk Stack:: Dealing with the stack. -* Dgawk Info:: Obtaining information about the program and - the debugger state. -* Miscellaneous Dgawk Commands:: Miscellaneous Commands. +* Sample Debugging Session:: Sample Debugging Session. +* Debugger Invocation:: How to Start the Debugger. +* Finding The Bug:: Finding the Bug. +* List of Debugger Commands:: Main Commands. +* Breakpoint Control:: Control of Breakpoints. +* Debugger Execution Control:: Control of Execution. +* Viewing And Changing Data:: Viewing and Changing Data. +* Execution Stack:: Dealing with the Stack. +* Debugger Info:: Obtaining Information about the Program and + the Debugger State. +* Miscellaneous Debugger Commands:: Miscellaneous Commands. * Readline Support:: Readline Support. -* Dgawk Limitations:: Limitations and future plans. +* Limitations:: Limitations and Future Plans. * V7/SVR3.1:: The major changes between V7 and System V Release 3.1. * SVR4:: Minor changes between System V Releases 3.1 @@ -686,6 +686,7 @@ particular records in a file and perform operations upon them. * Internals:: A brief look at some @command{gawk} internals. * Plugin License:: A note about licensing. +* Loading Extensions:: How to load dynamic extensions. * Sample Library:: A example of new functions. * Internal File Description:: What the new functions will do. * Internal File Ops:: The code for internal file operations. @@ -1164,8 +1165,7 @@ provide many sample @command{awk} programs. Reading them allows you to see @command{awk} solving real problems. -@ref{Debugger}, describes the @command{awk} debugger, -@command{dgawk}. +@ref{Debugger}, describes the @command{awk} debugger. @ref{Language History}, describes how the @command{awk} language has evolved since @@ -3101,6 +3101,19 @@ inadvertently use global variables that you meant to be local. (This is a particularly easy mistake to make with simple variable names like @code{i}, @code{j}, etc.) +@item -D@r{[}@var{file}@r{]} +@itemx --debug=@r{[}@var{file}@r{]} +@cindex @code{-D} option +@cindex @code{--debug} option +@cindex @command{awk} debugging, enabling +Enable debugging of @command{awk} programs +(@pxref{Debugging}). +By default, the debugger reads commands interactively from the terminal. +The optional @var{file} argument allows you to specify a file with a list +of commands for the debugger to execute non-interactively. +No space is allowed between the @option{-D} and @var{file}, if +@var{file} is supplied. + @item -e @var{program-text} @itemx --source @var{program-text} @cindex @code{-e} option @@ -3166,6 +3179,15 @@ for information about this option. Print a ``usage'' message summarizing the short and long style options that @command{gawk} accepts and then exit. +@item -l @var{lib} +@itemx --load @var{lib} +@cindex @code{-l} option +@cindex @code{--load} option +@cindex loading, library +Load a shared library @var{lib}. This searches for the library using the @env{AWKPATH} +environment variable. The suffix @samp{.so} in the library name is optional. +The library initialization routine should be named @code{dlload()}. + @item -L @r{[}value@r{]} @itemx --lint@r{[}=value@r{]} @cindex @code{-l} option @@ -3212,6 +3234,18 @@ Use with care. Force the use of the locale's decimal point character when parsing numeric input data (@pxref{Locales}). +@item -o@r{[}@var{file}@r{]} +@itemx --pretty-print@r{[}=@var{file}@r{]} +@cindex @code{-o} option +@cindex @code{--pretty-print} option +@cindex @command{awk} enabling +Enable pretty-printing of @command{awk} programs. +By default, output program is created in a file named @file{awkprof.out}. +The optional @var{file} argument allows you to specify a different +@value{FN} for the output. +No space is allowed between the @option{-o} and @var{file}, if +@var{file} is supplied. + @item -O @itemx --optimize @cindex @code{--optimize} option @@ -3224,7 +3258,7 @@ maintainer hopes to add more optimizations over time. @itemx --profile@r{[}=@var{file}@r{]} @cindex @code{-p} option @cindex @code{--profile} option -@cindex @command{awk} programs, profiling, enabling +@cindex @command{awk} profiling, enabling Enable profiling of @command{awk} programs (@pxref{Profiling}). By default, profiles are created in a file named @file{awkprof.out}. @@ -3233,10 +3267,8 @@ The optional @var{file} argument allows you to specify a different No space is allowed between the @option{-p} and @var{file}, if @var{file} is supplied. -When run with @command{gawk}, the profile is just a ``pretty printed'' version -of the program. When run with @command{pgawk}, the profile contains execution -counts for each statement in the program in the left margin, and function -call counts for each function. +The profile contains execution counts for each statement in the program +in the left margin, and function call counts for each function. @item -P @itemx --posix @@ -3300,14 +3332,6 @@ This is now @command{gawk}'s default behavior. Nevertheless, this option remains both for backward compatibility, and for use in combination with the @option{--traditional} option. -@item -R @var{file} -@itemx --command=@var{file} -@cindex @code{-R} option -@cindex @code{--command} option -@command{dgawk} only. -Read @command{dgawk} debugger options and commands from @var{file}. -@xref{Dgawk Info}, for more information. - @item -S @itemx --sandbox @cindex @code{-S} option @@ -3704,7 +3728,7 @@ into smaller, more manageable pieces, and also lets you reuse common @command{aw code from various @command{awk} scripts. In other words, you can group together @command{awk} functions, used to carry out specific tasks, into external files. These files can be used just like function libraries, -using the @samp{@@include} keyword in conjunction with the @code{AWKPATH} +using the @samp{@@include} keyword in conjunction with the @env{AWKPATH} environment variable. Let's see an example. @@ -18954,40 +18978,32 @@ extensive examples. @cindex @command{awk} programs, profiling @c STARTOFRANGE proawk @cindex profiling @command{awk} programs -@c STARTOFRANGE pgawk -@cindex @command{pgawk} program -@cindex profiling @command{gawk}, See @command{pgawk} program - -You may produce execution -traces of your @command{awk} programs. -This is done with a specially compiled version of @command{gawk}, -called @command{pgawk} (``profiling @command{gawk}''). - +@cindex profiling @command{gawk} @cindex @code{awkprof.out} file @cindex files, @code{awkprof.out} -@cindex @command{pgawk} program, @code{awkprof.out} file -@command{pgawk} is identical in every way to @command{gawk}, except that when -it has finished running, it creates a profile of your program in a file -named @file{awkprof.out}. -Because it is profiling, it also executes up to 45% slower than + +You may produce execution traces of your @command{awk} programs. +This is done by passing the option @option{--profile} to @command{gawk}. +When @command{gawk} has finished running, it creates a profile of your program in a file +named @file{awkprof.out}. Because it is profiling, it also executes up to 45% slower than @command{gawk} normally does. @cindex @code{--profile} option As shown in the following example, the @option{--profile} option can be used to change the name of the file -where @command{pgawk} will write the profile: +where @command{gawk} will write the profile: @example -pgawk --profile=myprog.prof -f myprog.awk data1 data2 +gawk --profile=myprog.prof -f myprog.awk data1 data2 @end example @noindent -In the above example, @command{pgawk} places the profile in +In the above example, @command{gawk} places the profile in @file{myprog.prof} instead of in @file{awkprof.out}. -Here is a sample -session showing a simple @command{awk} program, its input data, and the -results from running @command{pgawk}. First, the @command{awk} program: +Here is a sample session showing a simple @command{awk} program, its input data, and the +results from running @command{gawk} with the @option{--profile} option. +First, the @command{awk} program: @example BEGIN @{ print "First BEGIN rule" @} @@ -19027,12 +19043,12 @@ foo junk @end example -Here is the @file{awkprof.out} that results from running @command{pgawk} -on this program and data (this example also illustrates that @command{awk} +Here is the @file{awkprof.out} that results from running the @command{gawk} +profiler on this program and data (this example also illustrates that @command{awk} programmers sometimes have to work late): -@cindex @code{BEGIN} pattern, @command{pgawk} program -@cindex @code{END} pattern, @command{pgawk} program +@cindex @code{BEGIN} pattern +@cindex @code{END} pattern @example # gawk profile, created Sun Aug 13 00:00:15 2000 @@ -19124,15 +19140,15 @@ keyword indicates how many times the function was called. The counts next to the statements in the body show how many times those statements were executed. -@cindex @code{@{@}} (braces), @command{pgawk} program -@cindex braces (@code{@{@}}), @command{pgawk} program +@cindex @code{@{@}} (braces) +@cindex braces (@code{@{@}}) @item The layout uses ``K&R'' style with TABs. Braces are used everywhere, even when the body of an @code{if}, @code{else}, or loop is only a single statement. -@cindex @code{()} (parentheses), @command{pgawk} program -@cindex parentheses @code{()}, @command{pgawk} program +@cindex @code{()} (parentheses) +@cindex parentheses @code{()} @item Parentheses are used only where needed, as indicated by the structure of the program and the precedence rules. @@ -19155,16 +19171,16 @@ Similarly, if the target of a redirection isn't a scalar, it gets parenthesized. @item -@command{pgawk} supplies leading comments in +@command{gawk} supplies leading comments in front of the @code{BEGIN} and @code{END} rules, the pattern/action rules, and the functions. @end itemize The profiled version of your program may not look exactly like what you -typed when you wrote it. This is because @command{pgawk} creates the +typed when you wrote it. This is because @command{gawk} creates the profiled version by ``pretty printing'' its internal representation of -the program. The advantage to this is that @command{pgawk} can produce +the program. The advantage to this is that @command{gawk} can produce a standard representation. The disadvantage is that all source-code comments are lost, as are the distinctions among multiple @code{BEGIN}, @code{END}, @code{BEGINFILE}, and @code{ENDFILE} rules. Also, things such as: @@ -19186,15 +19202,16 @@ come out as: which is correct, but possibly surprising. @cindex profiling @command{awk} programs, dynamically -@cindex @command{pgawk} program, dynamic profiling +@cindex @command{gawk} program, dynamic profiling Besides creating profiles when a program has completed, -@command{pgawk} can produce a profile while it is running. +@command{gawk} can produce a profile while it is running. This is useful if your @command{awk} program goes into an infinite loop and you want to see what has been executed. -To use this feature, run @command{pgawk} in the background: +To use this feature, run @command{gawk} with the @option{--profile} +option in the background: @example -$ @kbd{pgawk -f myprog &} +$ @kbd{gawk --profile -f myprog &} [1] 13992 @end example @@ -19205,7 +19222,7 @@ $ @kbd{pgawk -f myprog &} @noindent The shell prints a job number and process ID number; in this case, 13992. Use the @command{kill} command to send the @code{USR1} signal -to @command{pgawk}: +to @command{gawk}: @example $ @kbd{kill -USR1 13992} @@ -19213,8 +19230,8 @@ $ @kbd{kill -USR1 13992} @noindent As usual, the profiled version of the program is written to -@file{awkprof.out}, or to a different file if you use the @option{--profile} -option. +@file{awkprof.out}, or to a different file if one specified with +the @option{--profile} option. Along with the regular profile, as shown earlier, the profile includes a trace of any active functions: @@ -19228,7 +19245,7 @@ includes a trace of any active functions: # -- main -- @end example -You may send @command{pgawk} the @code{USR1} signal as many times as you like. +You may send @command{gawk} the @code{USR1} signal as many times as you like. Each time, the profile and function call trace are appended to the output profile file. @@ -19236,7 +19253,7 @@ profile file. @cindex @code{SIGHUP} signal @cindex signals, @code{HUP}/@code{SIGHUP} If you use the @code{HUP} signal instead of the @code{USR1} signal, -@command{pgawk} produces the profile and the function call trace and then exits. +@command{gawk} produces the profile and the function call trace and then exits. @cindex @code{INT} signal (MS-Windows) @cindex @code{SIGINT} signal (MS-Windows) @@ -19244,21 +19261,20 @@ If you use the @code{HUP} signal instead of the @code{USR1} signal, @cindex @code{QUIT} signal (MS-Windows) @cindex @code{SIGQUIT} signal (MS-Windows) @cindex signals, @code{QUIT}/@code{SIGQUIT} (MS-Windows) -When @command{pgawk} runs on MS-Windows systems, it uses the +When @command{gawk} runs on MS-Windows systems, it uses the @code{INT} and @code{QUIT} signals for producing the profile and, in -the case of the @code{INT} signal, @command{pgawk} exits. This is +the case of the @code{INT} signal, @command{gawk} exits. This is because these systems don't support the @command{kill} command, so the only signals you can deliver to a program are those generated by the keyboard. The @code{INT} signal is generated by the @kbd{@value{CTL}-@key{C}} or @kbd{@value{CTL}-@key{BREAK}} key, while the @code{QUIT} signal is generated by the @kbd{@value{CTL}-@key{\}} key. -Finally, regular @command{gawk} also accepts the @option{--profile} option. +Finally, @command{gawk} also accepts another option @option{--pretty-print}. When called this way, @command{gawk} ``pretty prints'' the program into @file{awkprof.out}, without any execution counts. @c ENDOFRANGE advgaw @c ENDOFRANGE gawadv -@c ENDOFRANGE pgawk @c ENDOFRANGE awkp @c ENDOFRANGE proawk @@ -25140,41 +25156,41 @@ BEGIN { @c FIXME: Add more indexing. @node Debugger -@chapter @command{dgawk}: The @command{awk} Debugger -@cindex @command{dgawk} +@chapter Debugging @command{awk} Programs +@cindex debugging @command{awk} programs It would be nice if computer programs worked perfectly the first time they were run, but in real life, this rarely happens for programs of any complexity. Thus, most programming languages have facilities available for ``debugging'' programs, and now @command{awk} is no exception. -The @command{dgawk} debugger is purposely modeled after +The @command{gawk} debugger is purposely modeled after @uref{http://www.gnu.org/software/gdb/, the GNU Debugger (GDB)} command-line debugger. If you are familiar with GDB, learning -@command{dgawk} is easy. +how to use @command{gawk} for debugging your program is easy. @menu -* Debugging:: Introduction to @command{dgawk}. -* Sample dgawk session:: Sample @command{dgawk} session. -* List of Debugger Commands:: Main @command{dgawk} Commands. -* Readline Support:: Readline Support. -* Dgawk Limitations:: Limitations and future plans. +* Debugging:: Introduction to @command{gawk} debugger. +* Sample Debugging Session:: Sample debugging session. +* List of Debugger Commands:: Main debugger commands. +* Readline Support:: Readline support. +* Limitations:: Limitations and future plans. @end menu @node Debugging -@section Introduction to @command{dgawk} +@section Introduction to @command{gawk} Debugger This @value{SECTION} introduces debugging in general and begins the discussion of debugging in @command{gawk}. @menu -* Debugging Concepts:: Debugging In General. +* Debugging Concepts:: Debugging in General. * Debugging Terms:: Additional Debugging Concepts. * Awk Debugging:: Awk Debugging. @end menu @node Debugging Concepts -@subsection Debugging In General +@subsection Debugging in General (If you have used debuggers in other languages, you may want to skip ahead to the next section on the specific features of the @command{awk} @@ -25220,8 +25236,7 @@ functional program that you or someone else wrote). @subsection Additional Debugging Concepts Before diving in to the details, we need to introduce several -important concepts that apply to just about all debuggers, including -@command{dgawk}. +important concepts that apply to just about all debuggers. The following list defines terms used throughout the rest of this @value{CHAPTER}. @@ -25240,7 +25255,7 @@ that contains the function's parameters, local variables, and return value, as well as any other ``bookkeeping'' information needed to manage the call stack. This data area is termed a @dfn{stack frame}. -@command{gawk} also follows this model, and @command{dgawk} gives you +@command{gawk} also follows this model, and gives you access to the call stack and to each stack frame. You can see the call stack, as well as from where each function on the stack was invoked. Commands that print the call stack print information about @@ -25285,48 +25300,48 @@ each line of @command{awk} code. The debugger provides the opportunity to look at the individual primitive instructions carried out by the higher-level @command{awk} commands. -@node Sample dgawk session -@section Sample @command{dgawk} session +@node Sample Debugging Session +@section Sample Debugging Session -In order to illustrate the use of @command{dgawk}, let's look at a sample +In order to illustrate the use of @command{gawk} as a debugger, let's look at a sample debugging session. We will use the @command{awk} implementation of the POSIX @command{uniq} command described earlier (@pxref{Uniq Program}) as our example. @menu -* dgawk invocation:: @command{dgawk} Invocation. -* Finding The Bug:: Finding The Bug. +* Debugger Invocation:: How to Start the Debugger. +* Finding The Bug:: Finding the Bug. @end menu -@node dgawk invocation -@subsection @command{dgawk} Invocation +@node Debugger Invocation +@subsection How to Start the Debugger -Starting @command{dgawk} is exactly like running @command{awk}. The -file(s) containing the program and any supporting code are given on the -command line as arguments to one or more @option{-f} options. -(@command{dgawk} is not designed to debug command-line -programs, only programs contained in files.) In our case, -we call @command{dgawk} like this: +Starting the debugger is almost exactly like running @command{awk}, except you have to +pass an additional option @option{--debug} or the corresponding short option @option{-D}. +The file(s) containing the program and any supporting code are given on the command +line as arguments to one or more @option{-f} options. (@command{gawk} is not designed +to debug command-line programs, only programs contained in files.) In our case, +we invoke the debugger like this: @example -$ @kbd{dgawk -f getopt.awk -f join.awk -f uniq.awk inputfile} +$ @kbd{gawk -D -f getopt.awk -f join.awk -f uniq.awk inputfile} @end example @noindent where both @file{getopt.awk} and @file{uniq.awk} are in @env{$AWKPATH}. (Experienced users of GDB or similar debuggers should note that this syntax is slightly different from what they are used to. -With @command{dgawk}, the arguments for running the program are given +With @command{gawk} debugger, the arguments for running the program are given in the command line to the debugger rather than as part of the @code{run} command at the debugger prompt.) Instead of immediately running the program on @file{inputfile}, as -@command{gawk} would ordinarily do, @command{dgawk} merely loads all +@command{gawk} would ordinarily do, the debugger merely loads all the program source files, compiles them internally, and then gives us a prompt: @example -dgawk> +gawk> @end example @noindent @@ -25334,7 +25349,7 @@ from which we can issue commands to the debugger. At this point, no code has been executed. @node Finding The Bug -@subsection Finding The Bug +@subsection Finding the Bug Let's say that we are having a problem using (a faulty version of) @file{uniq.awk} in the ``field-skipping'' mode, and it doesn't seem to be @@ -25370,7 +25385,7 @@ a breakpoint in @file{uniq.awk} is at the beginning of the function the breakpoint, use the @code{b} (breakpoint) command: @example -dgawk> @kbd{b are_equal} +gawk> @kbd{b are_equal} @print{} Breakpoint 1 set at file `awklib/eg/prog/uniq.awk', line 64 @end example @@ -25379,22 +25394,22 @@ Now type @samp{r} or @samp{run} and the program runs until it hits the breakpoint for the first time: @example -dgawk> @kbd{r} +gawk> @kbd{r} @print{} Starting program: @print{} Stopping in Rule ... @print{} Breakpoint 1, are_equal(n, m, clast, cline, alast, aline) at `awklib/eg/prog/uniq.awk':64 @print{} 64 if (fcount == 0 && charcount == 0) -dgawk> +gawk> @end example Now we can look at what's going on inside our program. First of all, let's see how we got to where we are. At the prompt, we type @samp{bt} -(short for ``backtrace''), and @command{dgawk} responds with a +(short for ``backtrace''), and the debugger responds with a listing of the current stack frames: @example -dgawk> @kbd{bt} +gawk> @kbd{bt} @print{} #0 are_equal(n, m, clast, cline, alast, aline) at `awklib/eg/prog/uniq.awk':69 @print{} #1 in main() at `awklib/eg/prog/uniq.awk':89 @@ -25409,11 +25424,11 @@ the key to finding the source of the problem.) Now that we're in @code{are_equal()}, we can start looking at the values of some variables. Let's say we type @samp{p n} (@code{p} is short for ``print''). We would expect to see the value of -@code{n}, a parameter to @code{are_equal()}. Actually, @command{dgawk} +@code{n}, a parameter to @code{are_equal()}. Actually, the debugger gives us: @example -dgawk> @kbd{p n} +gawk> @kbd{p n} @print{} n = untyped variable @end example @@ -25424,7 +25439,7 @@ function was called without arguments (@pxref{Function Calls}). A more useful variable to display might be the current record: @example -dgawk> @kbd{p $0} +gawk> @kbd{p $0} @print{} $0 = string ("gawk is a wonderful program!") @end example @@ -25433,7 +25448,7 @@ This might be a bit puzzling at first since this is the second line of our test input above. Let's look at @code{NR}: @example -dgawk> @kbd{p NR} +gawk> @kbd{p NR} @print{} NR = number (2) @end example @@ -25452,7 +25467,7 @@ NR == 1 @{ OK, let's just check that that rule worked correctly: @example -dgawk> @kbd{p last} +gawk> @kbd{p last} @print{} last = string ("awk is a wonderful program!") @end example @@ -25463,7 +25478,7 @@ be inside this function. To investigate further, we must begin @samp{n} (for ``next''): @example -dgawk> @kbd{n} +gawk> @kbd{n} @print{} 67 if (fcount > 0) @{ @end example @@ -25483,9 +25498,9 @@ Continuing to step, we now get to the splitting of the current and last records: @example -dgawk> @kbd{n} +gawk> @kbd{n} @print{} 68 n = split(last, alast) -dgawk> @kbd{n} +gawk> @kbd{n} @print{} 69 m = split($0, aline) @end example @@ -25493,7 +25508,7 @@ At this point, we should be curious to see what our records were split into, so we try to look: @example -dgawk> @kbd{p n m alast aline} +gawk> @kbd{p n m alast aline} @print{} n = number (5) @print{} m = number (5) @print{} alast = array, 5 elements @@ -25512,7 +25527,7 @@ inside the array? The first choice would be to use subscripts: @example -dgawk> @kbd{p alast[0]} +gawk> @kbd{p alast[0]} @print{} "0" not in array `alast' @end example @@ -25520,16 +25535,16 @@ dgawk> @kbd{p alast[0]} Oops! @example -dgawk> @kbd{p alast[1]} +gawk> @kbd{p alast[1]} @print{} alast["1"] = string ("awk") @end example This would be kind of slow for a 100-member array, though, so -@command{dgawk} provides a shortcut (reminiscent of another language +@command{gawk} provides a shortcut (reminiscent of another language not to be mentioned): @example -dgawk> @kbd{p @@alast} +gawk> @kbd{p @@alast} @print{} alast["1"] = string ("awk") @print{} alast["2"] = string ("is") @print{} alast["3"] = string ("a") @@ -25541,9 +25556,9 @@ It looks like we got this far OK. Let's take another step or two: @example -dgawk> @kbd{n} +gawk> @kbd{n} @print{} 70 clast = join(alast, fcount, n) -dgawk> @kbd{n} +gawk> @kbd{n} @print{} 71 cline = join(aline, fcount, m) @end example @@ -25553,7 +25568,7 @@ the virtual record to compare, and if the first field was numbered zero, this would work. Let's look at what we've got: @example -dgawk> @kbd{p cline clast} +gawk> @kbd{p cline clast} @print{} cline = string ("gawk is a wonderful program!") @print{} clast = string ("awk is a wonderful program!") @end example @@ -25562,10 +25577,10 @@ Hey, those look pretty familiar! They're just our original, unaltered, input records. A little thinking (the human brain is still the best debugging tool), and we realize that we were off by one! -We get out of @command{dgawk}: +We get out of the debugger: @example -dgawk> @kbd{q} +gawk> @kbd{q} @print{} The program is running. Exit anyway (y/n)? @kbd{y} @end example @@ -25581,9 +25596,9 @@ cline = join(aline, fcount+1, m) and problem solved! @node List of Debugger Commands -@section Main @command{dgawk} Commands +@section Main Debugger Commands -The @command{dgawk} command set can be divided into the +The @command{gawk} debugger command set can be divided into the following categories: @itemize @bullet{} @@ -25610,24 +25625,24 @@ Miscellaneous Each of these are discussed in the following subsections. In the following descriptions, commands which may be abbreviated show the abbreviation on a second description line. -A @command{dgawk} command name may also be truncated if that partial -name is unambiguous. @command{dgawk} has the built-in capability to +A debugger command name may also be truncated if that partial +name is unambiguous. The debugger has the built-in capability to automatically repeat the previous command when just hitting @key{Enter}. This works for the commands @code{list}, @code{next}, @code{nexti}, @code{step}, @code{stepi} and @code{continue} executed without any argument. @menu -* Breakpoint Control:: Control of breakpoints. -* Dgawk Execution Control:: Control of execution. -* Viewing And Changing Data:: Viewing and changing data. -* Dgawk Stack:: Dealing with the stack. -* Dgawk Info:: Obtaining information about the program and - the debugger state. -* Miscellaneous Dgawk Commands:: Miscellaneous Commands. +* Breakpoint Control:: Control of Breakpoints. +* Debugger Execution Control:: Control of Execution. +* Viewing And Changing Data:: Viewing and Changing Data. +* Execution Stack:: Dealing with the Stack. +* Debugger Info:: Obtaining Information about the Program and + the Debugger State. +* Miscellaneous Debugger Commands:: Miscellaneous Commands. @end menu @node Breakpoint Control -@subsection Control Of Breakpoints +@subsection Control of Breakpoints As we saw above, the first thing you probably want to do in a debugging session is to get your breakpoints set up, since otherwise your program @@ -25662,10 +25677,10 @@ Each breakpoint is assigned a number which can be used to delete it from the breakpoint list using the @code{delete} command. With a breakpoint, you may also supply a condition. This is an -@command{awk} expression (enclosed in double quotes) that @command{dgawk} +@command{awk} expression (enclosed in double quotes) that the debugger evaluates whenever the breakpoint is reached. If the condition is true, -then @command{dgawk} stops execution and prompts for a command. Otherwise, -@command{dgawk} continues executing the program. +then the debugger stops execution and prompts for a command. Otherwise, +it continues executing the program. @cindex debugger commands, @code{clear} @cindex @code{clear} debugger command @@ -25691,10 +25706,10 @@ Delete breakpoint(s) set at entry to function @var{function}. @cindex @code{condition} debugger command @item @code{condition} @var{n} @code{"@var{expression}"} Add a condition to existing breakpoint or watchpoint @var{n}. The -condition is an @command{awk} expression that @command{dgawk} evaluates +condition is an @command{awk} expression that the debugger evaluates whenever the breakpoint or watchpoint is reached. If the condition is true, then -@command{dgawk} stops execution and prompts for a command. Otherwise, -@command{dgawk} continues executing the program. If the condition expression is +the debugger stops execution and prompts for a command. Otherwise, +the debugger continues executing the program. If the condition expression is not specified, any existing condition is removed; i.e., the breakpoint or watchpoint is made unconditional. @@ -25750,7 +25765,7 @@ Set a temporary breakpoint (enabled for only one stop). The arguments are the same as for @code{break}. @end table -@node Dgawk Execution Control +@node Debugger Execution Control @subsection Control of Execution Now that your breakpoints are ready, you can start running the program @@ -25779,14 +25794,14 @@ in the list that resumes execution (e.g., @code{continue}) terminates the list For example: @example -dgawk> @kbd{commands} +gawk> @kbd{commands} > @kbd{silent} > @kbd{printf "A silent breakpoint; i = %d\n", i} > @kbd{info locals} > @kbd{set i = 10} > @kbd{continue} > @kbd{end} -dgawk> +gawk> @end example @cindex debugger commands, @code{c} (@code{continue}) @@ -25836,7 +25851,7 @@ and the caller of that frame becomes the innermost frame. @cindex @code{r} debugger command (alias for @code{run}) @item @code{run} @itemx @code{r} -Start/restart execution of the program. When restarting, @command{dgawk} +Start/restart execution of the program. When restarting, the debugger retains the current breakpoints, watchpoints, command history, automatic display variables, and debugger options. @@ -25859,7 +25874,7 @@ stopping, unless it encounters a breakpoint or watchpoint. @itemx @code{si} [@var{count}] Execute one (or @var{count}) instruction(s), stepping inside function calls. (For illustration of what is meant by an ``instruction'' in @command{gawk}, -see the output shown under @code{dump} in @ref{Miscellaneous Dgawk Commands}.) +see the output shown under @code{dump} in @ref{Miscellaneous Debugger Commands}.) @cindex debugger commands, @code{u} (@code{until}) @cindex debugger commands, @code{until} @@ -25887,7 +25902,7 @@ The value of the variable or field is displayed each time the program stops. Each variable added to the list is identified by a unique number: @example -dgawk> @kbd{display x} +gawk> @kbd{display x} @print{} 10: x = 1 @end example @@ -25924,7 +25939,7 @@ Print the value of a @command{gawk} variable or field. Fields must be referenced by constants: @example -dgawk> @kbd{print $3} +gawk> @kbd{print $3} @end example @noindent @@ -25966,16 +25981,16 @@ You can also set special @command{awk} variables, such as @code{FS}, @item @code{watch} @var{var} | @code{$}@var{n} [@code{"@var{expression}"}] @itemx @code{w} @var{var} | @code{$}@var{n} [@code{"@var{expression}"}] Add variable @var{var} (or field @code{$@var{n}}) to the watch list. -@command{dgawk} then stops whenever +The debugger then stops whenever the value of the variable or field changes. Each watched item is assigned a number which can be used to delete it from the watch list using the @code{unwatch} command. With a watchpoint, you may also supply a condition. This is an -@command{awk} expression (enclosed in double quotes) that @command{dgawk} +@command{awk} expression (enclosed in double quotes) that the debugger evaluates whenever the watchpoint is reached. If the condition is true, -then @command{dgawk} stops execution and prompts for a command. Otherwise, -@command{dgawk} continues executing the program. +then the debugger stops execution and prompts for a command. Otherwise, +@command{gawk} continues executing the program. @cindex debugger commands, @code{undisplay} @cindex @code{undisplay} debugger command @@ -25991,8 +26006,8 @@ watch list. @end table -@node Dgawk Stack -@subsection Dealing With The Stack +@node Execution Stack +@subsection Dealing with the Stack Whenever you run a program which contains any function calls, @command{gawk} maintains a stack of all of the function calls leading up @@ -26036,12 +26051,12 @@ Move @var{count} (default 1) frames up the stack toward the outermost frame. Then select and print the frame. @end table -@node Dgawk Info -@subsection Obtaining Information About The Program and The Debugger State +@node Debugger Info +@subsection Obtaining Information about the Program and the Debugger State Besides looking at the values of variables, there is often a need to get other sorts of information about the state of your program and of the -debugging environment itself. @command{dgawk} has one command which +debugging environment itself. The @command{gawk} debugger has one command which provides this information, appropriately called @code{info}. @code{info} is used with one of a number of arguments that tell it exactly what you want to know: @@ -26079,7 +26094,7 @@ Local variables of the selected frame. @item source The name of the current source file. Each time the program stops, the current source file is the file containing the current instruction. -When @command{dgawk} first starts, the current source file is the first file +When the debugger first starts, the current source file is the first file included via the @option{-f} option. The @samp{list @var{filename}:@var{lineno}} command can be used at any time to change the current source. @@ -26115,7 +26130,7 @@ The available options are: @c nested table @table @code @item history_size -The maximum number of lines to keep in the history file @file{./.dgawk_history}. +The maximum number of lines to keep in the history file @file{./.gawk_history}. The default is 100. @item listsize @@ -26127,14 +26142,14 @@ to standard output. An empty string (@code{""}) resets output to standard output. @item prompt -The debugger prompt. The default is @samp{@w{dgawk> }}. +The debugger prompt. The default is @samp{@w{gawk> }}. @item save_history @r{[}on @r{|} off@r{]} -Save command history to file @file{./.dgawk_history}. +Save command history to file @file{./.gawk_history}. The default is @code{on}. @item save_options @r{[}on @r{|} off@r{]} -Save current options to file @file{./.dgawkrc} upon exit. +Save current options to file @file{./.gawkrc} upon exit. The default is @code{on}. Options are read back in to the next session upon startup. @@ -26154,16 +26169,16 @@ Empty lines are ignored; they do @emph{not} repeat the last command. You can't restart the program by having more than one @code{run} command in the file. Also, the list of commands may include additional -@code{source} commands; however, @command{dgawk} will not source the +@code{source} commands; however, the @command{gawk} debugger will not source the same file more than once in order to avoid infinite recursion. In addition to, or instead of the @code{source} command, you can use -the @option{-R @var{file}} or @option{--command=@var{file}} command-line +the @option{-D @var{file}} or @option{--debug=@var{file}} command-line options to execute commands from a file non-interactively (@pxref{Options}. @end table -@node Miscellaneous Dgawk Commands +@node Miscellaneous Debugger Commands @subsection Miscellaneous Commands There are a few more commands which do not fit into the @@ -26181,7 +26196,7 @@ partial dump of Davide Brini's obfuscated code (@pxref{Signature Program}) demonstrates: @smallexample -dgawk> @kbd{dump} +gawk> @kbd{dump} @print{} # BEGIN @print{} @print{} [ 2:0x89faef4] Op_rule : [in_rule = BEGIN] [source_file = brini.awk] @@ -26230,7 +26245,7 @@ dgawk> @kbd{dump} @print{} [ :0x89fa3b0] Op_after_beginfile : @print{} [ :0x89fa388] Op_no_op : @print{} [ :0x89fa3c4] Op_after_endfile : -dgawk> +gawk> @end smallexample @cindex debugger commands, @code{h} (@code{help}) @@ -26239,7 +26254,7 @@ dgawk> @cindex @code{h} debugger command (alias for @code{help}) @item @code{help} @itemx @code{h} -Print a list of all of the @command{dgawk} commands with a short +Print a list of all of the @command{gawk} debugger commands with a short summary of their usage. @samp{help @var{command}} prints the information about the command @var{command}. @@ -26286,7 +26301,7 @@ function @var{function}. This command may change the current source file. Exit the debugger. Debugging is great fun, but sometimes we all have to tend to other obligations in life, and sometimes we find the bug, and are free to go on to the next one! As we saw above, if you are -running a program, @command{dgawk} warns you if you accidentally type +running a program, the debugger warns you if you accidentally type @samp{q} or @samp{quit}, to make sure you really want to quit. @cindex debugger commands, @code{trace} @@ -26305,7 +26320,7 @@ fairly self-explanatory, and using @code{stepi} and @code{nexti} while @node Readline Support @section Readline Support -If @command{dgawk} is compiled with the @code{readline} library, you +If @command{gawk} is compiled with the @code{readline} library, you can take advantage of that library's command completion and history expansion features. The following types of completion are available: @@ -26337,28 +26352,28 @@ and @end table -@node Dgawk Limitations +@node Limitations @section Limitations and Future Plans -We hope you find @command{dgawk} useful and enjoyable to work with, +We hope you find the @command{gawk} debugger useful and enjoyable to work with, but as with any program, especially in its early releases, it still has some limitations. A few which are worth being aware of are: @itemize @bullet{} @item -At this point, @command{dgawk} does not give a detailed explanation of +At this point, the debugger does not give a detailed explanation of what you did wrong when you type in something it doesn't like. Rather, it just responds @samp{syntax error}. When you do figure out what your mistake was, though, you'll feel like a real guru. @item -If you perused the dump of opcodes in @ref{Miscellaneous Dgawk Commands}, +If you perused the dump of opcodes in @ref{Miscellaneous Debugger Commands}, (or if you are already familiar with @command{gawk} internals), you will realize that much of the internal manipulation of data in @command{gawk}, as in many interpreters, is done on a stack. @code{Op_push}, @code{Op_pop}, etc., are the ``bread and butter'' of -most @command{gawk} code. Unfortunately, as of now, @command{dgawk} -does not allow you to examine the stack's contents. +most @command{gawk} code. Unfortunately, as of now, the @command{gawk} +debugger does not allow you to examine the stack's contents. That is, the intermediate results of expression evaluation are on the stack, but cannot be printed. Rather, only variables which are defined @@ -26373,14 +26388,14 @@ programmer, you are expected to know what @code{/[^[:alnum:][:blank:]]/} means. @item -@command{dgawk} is designed to be used by running a program (with all its -parameters) on the command line, as described in @ref{dgawk invocation}. +The @command{gawk} debugger is designed to be used by running a program (with all its +parameters) on the command line, as described in @ref{Debugger Invocation}. There is no way (as of now) to attach or ``break in'' to a running program. This seems reasonable for a language which is used mainly for quickly executing, short programs. @item -@command{dgawk} only accepts source supplied with the @option{-f} option. +The @command{gawk} debugger only accepts source supplied with the @option{-f} option. @end itemize Look forward to a future release when these and other missing features may @@ -27289,7 +27304,7 @@ environments. @cindex Haque, John John Haque reworked the @command{gawk} internals to use a byte-code engine, -providing the @command{dgawk} debugger for @command{awk} programs. +providing the @command{gawk} debugger for @command{awk} programs. @item @cindex Yawitz, Efraim @@ -28559,7 +28574,7 @@ since approximately 2003. @item @command{pawk} Nelson H.F.@: Beebe at the University of Utah has modified Brian Kernighan's @command{awk} to provide timing and profiling information. -It is different from @command{pgawk} +It is different from @command{gawk} with the @option{--profile} option. (@pxref{Profiling}), in that it uses CPU-based profiling, not line-count profiling. You may find it at either @@ -29064,6 +29079,7 @@ When @option{--sandbox} is specified, extensions are disabled @menu * Internals:: A brief look at some @command{gawk} internals. * Plugin License:: A note about licensing. +* Loading Extensions:: How to load dynamic extensions. * Sample Library:: A example of new functions. @end menu @@ -29128,22 +29144,12 @@ macro guarantees that a @code{NODE}'s wide-string value is current. It may end up calling an internal @command{gawk} function. It also guarantees that the wide string is zero-terminated. -@cindex @code{get_curfunc_arg_count()} internal function -@cindex internal function, @code{get_curfunc_arg_count()} -@item size_t get_curfunc_arg_count(void) -This function returns the actual number of parameters passed -to the current function. Inside the code of an extension -this can be used to determine the maximum index which is -safe to use with @code{get_actual_argument}. If this value is -greater than @code{nargs}, the function was -called incorrectly from the @command{awk} program. - @cindex parameters@comma{} number of @cindex @code{nargs} internal variable @cindex internal variable, @code{nargs} @item nargs -Inside an extension function, this is the maximum number of -expected parameters, as set by the @code{make_builtin()} function. +Inside an extension function, this is the actual number of +parameters passed to the current function. @cindex @code{stptr} internal variable @cindex internal variable, @code{stptr} @@ -29189,13 +29195,10 @@ Make sure that @samp{n->type == Node_var_array} first. @cindex arrays, elements, installing @cindex @code{assoc_lookup()} internal function @cindex internal function, @code{assoc_lookup()} -@item NODE **assoc_lookup(NODE *symbol, NODE *subs, int reference) +@item NODE **assoc_lookup(NODE *symbol, NODE *subs) Finds, and installs if necessary, array elements. @code{symbol} is the array, @code{subs} is the subscript. This is usually a value created with @code{make_string()} (see below). -@code{reference} should be @code{TRUE} if it is an error to use the -value before it is created. Typically, @code{FALSE} is the -correct value to use from extension functions. @cindex strings @cindex @code{make_string()} internal function @@ -29387,6 +29390,56 @@ the symbol exists in the global scope. Something like this is enough: int plugin_is_GPL_compatible; @end example +@node Loading Extensions +@appendixsubsec Loading a Dynamic Extension +@cindex loading extension +@cindex @command{gawk}, functions, loading +There are two ways to load a dynamically linked library. The first is to use the +builtin @code{extension()}: + +@example +extension(libname, init_func) +@end example + +where @file{libname} is the library to load, and @samp{init_func} is the +name of the initialization or bootstrap routine to run once loaded. + +The second method for dynamic loading of a library is to use the +command line option @option{-l}: + +@example +$ @kbd{gawk -l libname -f myprog} +@end example + +This will work only if the initialization routine is named @code{dlload()}. + +If you use @code{extension()}, the library will be loaded +at run time. This means that the functions are available only to the rest of +your script. If you use the command line option @option{-l} instead, +the library will be loaded before @command{gawk} starts compiling the +actual program. The net effect is that you can use those functions +anywhere in the program. + +@command{gawk} has a list of directories where it searches for libraries. +By default, the list includes directories that depend upon how gawk was built +and installed (@pxref{AWKPATH Variable}). If you want @command{gawk} +to look for libraries in your private directory, you have to tell it. +The way to do it is to set the @env{AWKPATH} environment variable +(@pxref{AWKPATH Variable}). +@command{gawk} supplies the default suffix @samp{.so} if it is not +present in the name of the library. +If the name of your library is @file{mylib.so}, you can simply type + +@example +$ @kbd{gawk -l mylib -f myprog} +@end example + +and @command{gawk} will do everything necessary to load in your library, +and then call your @code{dlload()} routine. + +You can always specify the library using an absolute pathname, in which +case @command{gawk} will not use @env{AWKPATH} to search for it. + @node Sample Library @appendixsubsec Example: Directory and File Operation Built-ins @c STARTOFRANGE chdirg @@ -29579,7 +29632,7 @@ do_chdir(int nargs) NODE *newdir; int ret = -1; - if (do_lint && get_curfunc_arg_count() != 1) + if (do_lint && nargs != 1) lintwarn("chdir: called with incorrect number of arguments"); newdir = get_scalar_argument(0, FALSE); @@ -29652,7 +29705,7 @@ do_stat(int nargs) char *pmode; /* printable mode */ char *type = "unknown"; - if (do_lint && get_curfunc_arg_count() > 2) + if (do_lint && nargs > 2) lintwarn("stat: called with too many arguments"); @end example @@ -29686,15 +29739,15 @@ calls are shown here, since they all follow the same pattern: @example /* fill in the array */ - aptr = assoc_lookup(array, tmp = make_string("name", 4), FALSE); + aptr = assoc_lookup(array, tmp = make_string("name", 4)); *aptr = dupnode(file); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("mode", 4), FALSE); + aptr = assoc_lookup(array, tmp = make_string("mode", 4)); *aptr = make_number((AWKNUM) sbuf.st_mode); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("pmode", 5), FALSE); + aptr = assoc_lookup(array, tmp = make_string("pmode", 5)); pmode = format_mode(sbuf.st_mode); *aptr = make_string(pmode, strlen(pmode)); unref(tmp); @@ -29,34 +29,17 @@ extern void after_beginfile(IOBUF **curfile); extern double pow(double x, double y); extern double modf(double x, double *yp); extern double fmod(double x, double y); -NODE **fcall_list; +NODE **fcall_list = NULL; long fcall_count = 0; int currule = 0; IOBUF *curfile = NULL; /* current data file */ int exiting = FALSE; -#ifdef DEBUGGING +int (*interpret)(INSTRUCTION *); + extern int pre_execute(INSTRUCTION **); extern void post_execute(INSTRUCTION *); -#else -#define r_interpret interpret -#endif - -/* - * Flag which executable this is; done here because eval.c is compiled - * differently for each of them. - */ -enum exe_mode which_gawk = -#ifdef PROFILING - exe_profiling /* pgawk */ -#else -# ifdef DEBUGGING - exe_debugging /* dgawk */ -# else - exe_normal /* normal gawk */ -# endif -#endif - ; /* which_gawk */ +extern void frame_popped(); #if __GNUC__ < 2 NODE *_t; /* used as a temporary in macros */ @@ -66,12 +49,7 @@ int ORSlen; int OFMTidx; int CONVFMTidx; -/* Profiling stuff */ -#ifdef PROFILING -#define INCREMENT(n) n++ -#else -#define INCREMENT(n) /* nothing */ -#endif +static NODE *node_Boolean[2]; /* This rather ugly macro is for VMS C */ #ifdef C @@ -262,9 +240,12 @@ static const char *const nodetypes[] = { "Node_var_new", "Node_param_list", "Node_func", + "Node_ext_func", "Node_hashnode", - "Node_ahash", "Node_array_ref", + "Node_array_tree", + "Node_array_leaf", + "Node_dump_array", "Node_arrayfor", "Node_frame", "Node_instruction", @@ -349,6 +330,7 @@ static struct optypetab { { "Op_K_nextfile", "nextfile" }, { "Op_builtin", NULL }, { "Op_sub_builtin", NULL }, + { "Op_ext_builtin", NULL }, { "Op_in_array", " in " }, { "Op_func_call", NULL }, { "Op_indirect_func_call", NULL }, @@ -376,7 +358,6 @@ static struct optypetab { { "Op_field_assign", NULL }, { "Op_after_beginfile", NULL }, { "Op_after_endfile", NULL }, - { "Op_ext_func", NULL }, { "Op_func", NULL }, { "Op_exec_count", NULL }, { "Op_breakpoint", NULL }, @@ -446,20 +427,19 @@ flags2str(int flagval) { static const struct flagtab values[] = { { MALLOC, "MALLOC" }, - { PERM, "PERM" }, { STRING, "STRING" }, { STRCUR, "STRCUR" }, { NUMCUR, "NUMCUR" }, { NUMBER, "NUMBER" }, { MAYBE_NUM, "MAYBE_NUM" }, - { ARRAYMAXED, "ARRAYMAXED" }, - { FUNC, "FUNC" }, { FIELD, "FIELD" }, { INTLSTR, "INTLSTR" }, - { NUMIND, "NUMIND" }, -#ifdef WSTRCUR + { NUMINT, "NUMINT" }, + { INTIND, "INTIND" }, { WSTRCUR, "WSTRCUR" }, -#endif + { ARRAYMAXED, "ARRAYMAXED" }, + { HALFHAT, "HALFHAT" }, + { XARRAY, "XARRAY" }, { 0, NULL }, }; @@ -484,7 +464,7 @@ genflags2str(int flagval, const struct flagtab *tab) * the '|' character. */ space_needed = (strlen(tab[i].name) + (sp != buffer)); - if (space_left < space_needed) + if (space_left <= space_needed) fatal(_("buffer overflow in genflags2str")); if (sp != buffer) { @@ -498,6 +478,7 @@ genflags2str(int flagval, const struct flagtab *tab) } } + *sp = '\0'; return buffer; } @@ -582,7 +563,6 @@ posix_compare(NODE *s1, NODE *s2) return ret; } - /* cmp_nodes --- compare two nodes, returning negative, 0, positive */ int @@ -599,6 +579,11 @@ cmp_nodes(NODE *t1, NODE *t2) (void) force_number(t1); if (t2->flags & MAYBE_NUM) (void) force_number(t2); + if (t1->flags & INTIND) + t1 = force_string(t1); + if (t2->flags & INTIND) + t2 = force_string(t2); + if ((t1->flags & NUMBER) && (t2->flags & NUMBER)) { if (t1->numbr == t2->numbr) ret = 0; @@ -610,8 +595,8 @@ cmp_nodes(NODE *t1, NODE *t2) return ret; } - (void) force_string(t1); - (void) force_string(t2); + t1 = force_string(t1); + t2 = force_string(t2); len1 = t1->stlen; len2 = t2->stlen; ldiff = len1 - len2; @@ -637,11 +622,13 @@ cmp_nodes(NODE *t1, NODE *t2) ret = casetable[*cp1] - casetable[*cp2]; } else ret = memcmp(t1->stptr, t2->stptr, l); - return (ret == 0 ? ldiff : ret); + + ret = ret == 0 ? ldiff : ret; + return ret; } +/* push_frame --- push a frame NODE onto stack */ -#if defined(PROFILING) || defined(DEBUGGING) static void push_frame(NODE *f) { @@ -663,27 +650,20 @@ push_frame(NODE *f) fcall_list[1] = f; } + +/* pop_frame --- pop off a frame NODE*/ + static void pop_frame() { -#ifdef DEBUGGING - extern void frame_popped(); -#endif if (fcall_count > 1) memmove(fcall_list + 1, fcall_list + 2, (fcall_count - 1) * sizeof(NODE *)); fcall_count--; assert(fcall_count >= 0); -#ifdef DEBUGGING - frame_popped(); -#endif + if (do_debug) + frame_popped(); } -#else /* not PROFILING or DEBUGGING */ -#define push_frame(p) /* nothing */ -#define pop_frame() /* nothing */ -#endif - -#ifdef PROFILING /* dump_fcall_stack --- print a backtrace of the awk function calls */ @@ -691,7 +671,7 @@ void dump_fcall_stack(FILE *fp) { NODE *f, *func; - long i = 0; + long i = 0, j, k = 0; if (fcall_count == 0) return; @@ -699,19 +679,20 @@ dump_fcall_stack(FILE *fp) /* current frame */ func = frame_ptr->func_node; - fprintf(fp, "\t# %3ld. %s\n", i, func->lnode->param); + for (j = 0; j <= frame_ptr->num_tail_calls; j++) + fprintf(fp, "\t# %3ld. %s\n", k++, func->vname); /* outer frames except main */ for (i = 1; i < fcall_count; i++) { f = fcall_list[i]; func = f->func_node; - fprintf(fp, "\t# %3ld. %s\n", i, func->lnode->param); + for (j = 0; j <= f->num_tail_calls; j++) + fprintf(fp, "\t# %3ld. %s\n", k++, func->vname); } - fprintf(fp, "\t# %3ld. -- main --\n", fcall_count); + fprintf(fp, "\t# %3ld. -- main --\n", k); } -#endif /* PROFILING */ /* set_IGNORECASE --- update IGNORECASE as appropriate */ @@ -728,9 +709,10 @@ set_IGNORECASE() if (do_traditional) IGNORECASE = FALSE; else if ((IGNORECASE_node->var_value->flags & (STRING|STRCUR)) != 0) { - if ((IGNORECASE_node->var_value->flags & MAYBE_NUM) == 0) - IGNORECASE = (force_string(IGNORECASE_node->var_value)->stlen > 0); - else + if ((IGNORECASE_node->var_value->flags & MAYBE_NUM) == 0) { + IGNORECASE_node->var_value = force_string(IGNORECASE_node->var_value); + IGNORECASE = (IGNORECASE_node->var_value->stlen > 0); + } else IGNORECASE = (force_number(IGNORECASE_node->var_value) != 0.0); } else if ((IGNORECASE_node->var_value->flags & (NUMCUR|NUMBER)) != 0) IGNORECASE = (force_number(IGNORECASE_node->var_value) != 0.0); @@ -823,7 +805,8 @@ set_BINMODE() void set_OFS() { - OFS = force_string(OFS_node->var_value)->stptr; + OFS_node->var_value = force_string(OFS_node->var_value); + OFS = OFS_node->var_value->stptr; OFSlen = OFS_node->var_value->stlen; OFS[OFSlen] = '\0'; } @@ -833,7 +816,8 @@ set_OFS() void set_ORS() { - ORS = force_string(ORS_node->var_value)->stptr; + ORS_node->var_value = force_string(ORS_node->var_value); + ORS = ORS_node->var_value->stptr; ORSlen = ORS_node->var_value->stlen; ORS[ORSlen] = '\0'; } @@ -849,6 +833,7 @@ fmt_ok(NODE *n) { NODE *tmp = force_string(n); const char *p = tmp->stptr; + #if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 static const char float_formats[] = "efgEG"; #else @@ -890,7 +875,7 @@ fmt_index(NODE *n) if (fmt_list == NULL) emalloc(fmt_list, NODE **, fmt_num*sizeof(*fmt_list), "fmt_index"); - (void) force_string(n); + n = force_string(n); while (ix < fmt_hiwater) { if (cmp_nodes(fmt_list[ix], n) == 0) return ix; @@ -942,41 +927,45 @@ set_LINT() if ((LINT_node->var_value->flags & MAYBE_NUM) == 0) { const char *lintval; size_t lintlen; + NODE *tmp; - do_lint = (force_string(LINT_node->var_value)->stlen > 0); - lintval = LINT_node->var_value->stptr; - lintlen = LINT_node->var_value->stlen; - if (do_lint) { - do_lint = LINT_ALL; + tmp = LINT_node->var_value = force_string(LINT_node->var_value); + lintval = tmp->stptr; + lintlen = tmp->stlen; + if (lintlen > 0) { + do_flags |= DO_LINT_ALL; if (lintlen == 5 && strncmp(lintval, "fatal", 5) == 0) lintfunc = r_fatal; - else if (lintlen == 7 && strncmp(lintval, "invalid", 7) == 0) - do_lint = LINT_INVALID; - else + else if (lintlen == 7 && strncmp(lintval, "invalid", 7) == 0) { + do_flags &= ~ DO_LINT_ALL; + do_flags |= DO_LINT_INVALID; + } else lintfunc = warning; - } else + } else { + do_flags &= ~(DO_LINT_ALL|DO_LINT_INVALID); lintfunc = warning; + } } else { if (force_number(LINT_node->var_value) != 0.0) - do_lint = LINT_ALL; + do_flags |= DO_LINT_ALL; else - do_lint = FALSE; + do_flags &= ~(DO_LINT_ALL|DO_LINT_INVALID); lintfunc = warning; } } else if ((LINT_node->var_value->flags & (NUMCUR|NUMBER)) != 0) { if (force_number(LINT_node->var_value) != 0.0) - do_lint = LINT_ALL; + do_flags |= DO_LINT_ALL; else - do_lint = FALSE; + do_flags &= ~(DO_LINT_ALL|DO_LINT_INVALID); lintfunc = warning; } else - do_lint = FALSE; /* shouldn't happen */ + do_flags &= ~(DO_LINT_ALL|DO_LINT_INVALID); /* shouldn't happen */ if (! do_lint) lintfunc = warning; /* explicitly use warning() here, in case lintfunc == r_fatal */ - if (old_lint != do_lint && old_lint && do_lint == FALSE) + if (old_lint != do_lint && old_lint && ! do_lint) warning(_("turning off `--lint' due to assignment to `LINT'")); #endif /* ! NO_LINT */ } @@ -987,9 +976,11 @@ void set_TEXTDOMAIN() { int len; + NODE *tmp; - TEXTDOMAIN = force_string(TEXTDOMAIN_node->var_value)->stptr; - len = TEXTDOMAIN_node->var_value->stlen; + tmp = TEXTDOMAIN_node->var_value = force_string(TEXTDOMAIN_node->var_value); + TEXTDOMAIN = tmp->stptr; + len = tmp->stlen; TEXTDOMAIN[len] = '\0'; /* * Note: don't call textdomain(); this value is for @@ -1057,7 +1048,6 @@ update_FNR() } - NODE *frame_ptr; /* current frame */ STACK_ITEM *stack_ptr = NULL; STACK_ITEM *stack_bottom; @@ -1078,32 +1068,6 @@ NODE **args_array = NULL; STACK_ITEM * grow_stack() { - if (stack_ptr == NULL) { - char *val; - - if ((val = getenv("GAWK_STACKSIZE")) != NULL) { - if (isdigit((unsigned char) *val)) { - unsigned long n = 0; - for (; *val && isdigit((unsigned char) *val); val++) - n = (n * 10) + *val - '0'; - if (n >= 1) - STACK_SIZE = n; - } - } - - emalloc(stack_bottom, STACK_ITEM *, STACK_SIZE * sizeof(STACK_ITEM), "grow_stack"); - stack_ptr = stack_bottom - 1; - stack_top = stack_bottom + STACK_SIZE - 1; - - /* initialize frame pointer */ - getnode(frame_ptr); - frame_ptr->type = Node_frame; - frame_ptr->stack = NULL; - frame_ptr->func_node = NULL; /* in main */ - frame_ptr->vname = NULL; - return stack_ptr; - } - STACK_SIZE *= 2; erealloc(stack_bottom, STACK_ITEM *, STACK_SIZE * sizeof(STACK_ITEM), "grow_stack"); stack_top = stack_bottom + STACK_SIZE - 1; @@ -1123,9 +1087,6 @@ r_get_lhs(NODE *n, int reference) int isparam = FALSE; if (n->type == Node_param_list) { - if ((n->flags & FUNC) != 0) - fatal(_("can't use function name `%s' as variable or array"), - n->vname); isparam = TRUE; n = GET_PARAM(n->param_cnt); } @@ -1139,11 +1100,11 @@ r_get_lhs(NODE *n, int reference) fatal(_("attempt to use array `%s' in a scalar context"), array_vname(n)); n->orig_array->type = Node_var; - n->orig_array->var_value = Nnull_string; + n->orig_array->var_value = dupnode(Nnull_string); /* fall through */ case Node_var_new: n->type = Node_var; - n->var_value = Nnull_string; + n->var_value = dupnode(Nnull_string); break; case Node_var: @@ -1158,7 +1119,7 @@ r_get_lhs(NODE *n, int reference) _("reference to uninitialized argument `%s'") : _("reference to uninitialized variable `%s'")), n->vname); - return &n->var_value; + return & n->var_value; } @@ -1239,17 +1200,42 @@ static INSTRUCTION * setup_frame(INSTRUCTION *pc) { NODE *r = NULL; - NODE *m; - NODE *f; + NODE *m, *f, *fp; NODE **sp = NULL; - char **varnames; - int pcount, arg_count, i; + int pcount, arg_count, i, j; + int tail_optimize = FALSE; f = pc->func_body; - pcount = f->lnode->param_cnt; - varnames = f->parmlist; + pcount = f->param_cnt; + fp = f->fparms; arg_count = (pc + 1)->expr_count; + /* tail recursion optimization */ + tail_optimize = ((pc + 1)->tail_call && do_optimize > 1 + && ! do_debug && ! do_profile); + + if (tail_optimize) { + /* free local vars of calling frame */ + + NODE *func; + int n; + + func = frame_ptr->func_node; + for (n = func->param_cnt, sp = frame_ptr->stack; n > 0; n--) { + r = *sp++; + if (r->type == Node_var) /* local variable */ + DEREF(r->var_value); + else if (r->type == Node_var_array) /* local array */ + assoc_clear(r); + } + sp = frame_ptr->stack; + + } else if (pcount > 0) { + emalloc(sp, NODE **, pcount * sizeof(NODE *), "setup_frame"); + memset(sp, 0, pcount * sizeof(NODE *)); + } + + /* check for extra args */ if (arg_count > pcount) { warning( @@ -1262,23 +1248,23 @@ setup_frame(INSTRUCTION *pc) } while (--arg_count > pcount); } - if (pcount > 0) { - emalloc(sp, NODE **, pcount * sizeof(NODE *), "setup_frame"); - memset(sp, 0, pcount * sizeof(NODE *)); - } + for (i = 0, j = arg_count - 1; i < pcount; i++, j--) { + if (tail_optimize) + r = sp[i]; + else { + getnode(r); + memset(r, 0, sizeof(NODE)); + sp[i] = r; + } - for (i = 0; i < pcount; i++) { - getnode(r); - memset(r, 0, sizeof(NODE)); - sp[i] = r; if (i >= arg_count) { /* local variable */ r->type = Node_var_new; - r->vname = varnames[i]; + r->vname = fp[i].param; continue; } - m = PEEK(arg_count - i - 1); /* arguments in reverse order on runtime stack */ + m = PEEK(j); /* arguments in reverse order on runtime stack */ if (m->type == Node_param_list) m = GET_PARAM(m->param_cnt); @@ -1302,7 +1288,7 @@ setup_frame(INSTRUCTION *pc) * subsequent param. */ r->type = Node_var; - r->var_value = Nnull_string; + r->var_value = dupnode(Nnull_string); break; case Node_val: @@ -1313,10 +1299,16 @@ setup_frame(INSTRUCTION *pc) default: cant_happen(); } - r->vname = varnames[i]; + r->vname = fp[i].param; } + stack_adj(-arg_count); /* adjust stack pointer */ + if (tail_optimize) { + frame_ptr->num_tail_calls++; + return f->code_ptr; + } + if (pc->opcode == Op_indirect_func_call) { r = POP(); /* indirect var */ DEREF(r); @@ -1324,7 +1316,8 @@ setup_frame(INSTRUCTION *pc) frame_ptr->vname = source; /* save current source */ - push_frame(frame_ptr); + if (do_profile || do_debug) + push_frame(frame_ptr); /* save current frame in stack */ PUSH(frame_ptr); @@ -1335,6 +1328,7 @@ setup_frame(INSTRUCTION *pc) frame_ptr->stack = sp; frame_ptr->prev_frame_size = (stack_ptr - stack_bottom); /* size of the previous stack frame */ frame_ptr->func_node = f; + frame_ptr->num_tail_calls = 0; frame_ptr->vname = NULL; frame_ptr->reti = pc; /* on return execute pc->nexti */ @@ -1354,7 +1348,7 @@ restore_frame(NODE *fp) INSTRUCTION *ri; func = frame_ptr->func_node; - n = func->lnode->param_cnt; + n = func->param_cnt; sp = frame_ptr->stack; for (; n > 0; n--) { @@ -1365,13 +1359,15 @@ restore_frame(NODE *fp) assoc_clear(r); freenode(r); } + if (frame_ptr->stack != NULL) efree(frame_ptr->stack); ri = frame_ptr->reti; /* execution in calling frame * resumes from ri->nexti. */ freenode(frame_ptr); - pop_frame(); + if (do_profile || do_debug) + pop_frame(); /* restore frame */ frame_ptr = fp; @@ -1388,11 +1384,14 @@ restore_frame(NODE *fp) static inline void free_arrayfor(NODE *r) { - if (r->var_array != NULL) { - size_t num_elems = r->table_size; - NODE **list = r->var_array; - while (num_elems > 0) - unref(list[--num_elems]); + if (r->for_list != NULL) { + NODE *n; + size_t num_elems = r->for_list_size; + NODE **list = r->for_list; + while (num_elems > 0) { + n = list[--num_elems]; + unref(n); + } efree(list); } freenode(r); @@ -1471,6 +1470,12 @@ unwind_stack(long n) static inline int eval_condition(NODE *t) { + if (t == node_Boolean[FALSE]) + return FALSE; + + if (t == node_Boolean[TRUE]) + return TRUE; + if ((t->flags & MAYBE_NUM) != 0) force_number(t); @@ -1500,38 +1505,37 @@ cmp_scalar() return di; } + /* op_assign --- assignment operators excluding = */ static void op_assign(OPCODE op) { NODE **lhs; - NODE *r = NULL; - AWKNUM x1, x2; -#ifndef HAVE_FMOD - AWKNUM x; -#endif + NODE *t1; + AWKNUM x = 0.0, x1, x2; lhs = POP_ADDRESS(); - x1 = force_number(*lhs); + t1 = *lhs; + x1 = force_number(t1); TOP_NUMBER(x2); - unref(*lhs); + switch (op) { case Op_assign_plus: - r = *lhs = make_number(x1 + x2); + x = x1 + x2; break; case Op_assign_minus: - r = *lhs = make_number(x1 - x2); + x = x1 - x2; break; case Op_assign_times: - r = *lhs = make_number(x1 * x2); + x = x1 * x2; break; case Op_assign_quotient: if (x2 == (AWKNUM) 0) { decr_sp(); fatal(_("division by zero attempted in `/='")); } - r = *lhs = make_number(x1 / x2); + x = x1 / x2; break; case Op_assign_mod: if (x2 == (AWKNUM) 0) { @@ -1539,22 +1543,29 @@ op_assign(OPCODE op) fatal(_("division by zero attempted in `%%='")); } #ifdef HAVE_FMOD - r = *lhs = make_number(fmod(x1, x2)); + x = fmod(x1, x2); #else /* ! HAVE_FMOD */ (void) modf(x1 / x2, &x); x = x1 - x2 * x; - r = *lhs = make_number(x); #endif /* ! HAVE_FMOD */ break; case Op_assign_exp: - r = *lhs = make_number((AWKNUM) calc_exp((double) x1, (double) x2)); + x = calc_exp((double) x1, (double) x2); break; default: break; } - UPREF(r); - REPLACE(r); + if (t1->valref == 1 && t1->flags == (MALLOC|NUMCUR|NUMBER)) { + /* optimization */ + t1->numbr = x; + } else { + unref(t1); + t1 = *lhs = make_number(x); + } + + UPREF(t1); + REPLACE(t1); } @@ -1652,1127 +1663,47 @@ pop_exec_state(int *rule, char **src, long *sz) return cp; } - -/* - * r_interpret: - * code is a list of instructions to run. returns the exit value - * from the awk code. - */ - - /* N.B.: - * 1) reference counting done for both number and string values. - * 2) TEMP flag no longer needed (consequence of the above; valref = 0 - * is the replacement). - * 3) Stack operations: - * Use REPLACE[_XX] if last stack operation was TOP[_XX], - * PUSH[_XX] if last operation was POP[_XX] instead. - * 4) UPREF and DREF -- see awk.h - */ - - -int -r_interpret(INSTRUCTION *code) +void +init_interpret() { - INSTRUCTION *pc; /* current instruction */ - NODE *r = NULL; - NODE *m; - INSTRUCTION *ni; - NODE *t1, *t2; - NODE *f; /* function definition */ - NODE **lhs; - AWKNUM x, x1, x2; - int di, pre = FALSE; - Regexp *rp; -#if defined(GAWKDEBUG) || defined(ARRAYDEBUG) - int last_was_stopme = FALSE; /* builtin stopme() called ? */ -#endif - int stdio_problem = FALSE; - - - if (args_array == NULL) - emalloc(args_array, NODE **, (max_args + 2)*sizeof(NODE *), "r_interpret"); - else - erealloc(args_array, NODE **, (max_args + 2)*sizeof(NODE *), "r_interpret"); - -/* array subscript */ -#define mk_sub(n) (n == 1 ? POP_STRING() : concat_exp(n, TRUE)) - -#ifdef DEBUGGING -#define JUMPTO(x) do { post_execute(pc); pc = (x); goto top; } while(FALSE) -#else -#define JUMPTO(x) do { pc = (x); goto top; } while(FALSE) -#endif - - pc = code; - - /* N.B.: always use JUMPTO for next instruction, otherwise bad things - * may happen. DO NOT add a real loop (for/while) below to - * replace ' forever {'; this catches failure to use JUMPTO to execute - * next instruction (e.g. continue statement). - */ - - /* loop until hit Op_stop instruction */ - - /* forever { */ -top: - if (pc->source_line > 0) - sourceline = pc->source_line; - -#ifdef DEBUGGING - if (! pre_execute(&pc)) - goto top; -#endif - - switch (pc->opcode) { - case Op_rule: - currule = pc->in_rule; /* for sole use in Op_K_next, Op_K_nextfile, Op_K_getline* */ - /* fall through */ - case Op_func: - case Op_ext_func: - source = pc->source_file; - break; - - case Op_atexit: - /* avoid false source indications */ - source = NULL; - sourceline = 0; - (void) nextfile(&curfile, TRUE); /* close input data file */ - /* - * This used to be: - * - * if (close_io() != 0 && ! exiting && exit_val == 0) - * exit_val = 1; - * - * Other awks don't care about problems closing open files - * and pipes, in that it doesn't affect their exit status. - * So we no longer do either. - */ - (void) close_io(& stdio_problem); - /* - * However, we do want to exit non-zero if there was a problem - * with stdout/stderr, so we reinstate a slightly different - * version of the above: - */ - if (stdio_problem && ! exiting && exit_val == 0) - exit_val = 1; - break; - - case Op_stop: - return 0; - - case Op_push_i: - m = pc->memory; - PUSH((m->flags & INTLSTR) != 0 ? format_val(CONVFMT, CONVFMTidx, m): m); - break; - - case Op_push: - case Op_push_arg: - { - NODE *save_symbol; - int isparam = FALSE; - - save_symbol = m = pc->memory; - if (m->type == Node_param_list) { - if ((m->flags & FUNC) != 0) - fatal(_("can't use function name `%s' as variable or array"), - m->vname); - isparam = TRUE; - save_symbol = m = GET_PARAM(m->param_cnt); - if (m->type == Node_array_ref) - m = m->orig_array; - } - - switch (m->type) { - case Node_var: - if (do_lint && var_uninitialized(m)) - lintwarn(isparam ? - _("reference to uninitialized argument `%s'") : - _("reference to uninitialized variable `%s'"), - save_symbol->vname); - m = m->var_value; - UPREF(m); - PUSH(m); - break; - - case Node_var_new: - m->type = Node_var; - m->var_value = Nnull_string; - if (do_lint) - lintwarn(isparam ? - _("reference to uninitialized argument `%s'") : - _("reference to uninitialized variable `%s'"), - save_symbol->vname); - PUSH(Nnull_string); - break; - - case Node_var_array: - if (pc->opcode == Op_push_arg) - PUSH(m); - else - fatal(_("attempt to use array `%s' in a scalar context"), - array_vname(save_symbol)); - break; - - default: - cant_happen(); - } - } - break; - - case Op_push_param: /* function argument */ - m = pc->memory; - if (m->type == Node_param_list) - m = GET_PARAM(m->param_cnt); - if (m->type == Node_var) { - m = m->var_value; - UPREF(m); - PUSH(m); - break; - } - /* else - fall through */ - case Op_push_array: - PUSH(pc->memory); - break; + long newval; - case Op_push_lhs: - lhs = get_lhs(pc->memory, pc->do_reference); - PUSH_ADDRESS(lhs); - break; - - case Op_subscript: - t2 = mk_sub(pc->sub_count); - t1 = POP_ARRAY(); - r = *assoc_lookup(t1, t2, TRUE); - DEREF(t2); - if (r->type == Node_val) - UPREF(r); - PUSH(r); - break; - - case Op_sub_array: - t2 = mk_sub(pc->sub_count); - t1 = POP_ARRAY(); - r = in_array(t1, t2); - if (r == NULL) { - getnode(r); - r->type = Node_var_array; - r->var_array = NULL; - r->vname = estrdup(t2->stptr, t2->stlen); /* the subscript in parent array */ - r->parent_array = t1; - *assoc_lookup(t1, t2, FALSE) = r; - } else if (r->type != Node_var_array) - fatal(_("attempt to use scalar `%s[\"%.*s\"]' as an array"), - array_vname(t1), (int) t2->stlen, t2->stptr); - DEREF(t2); - PUSH(r); - break; - - case Op_subscript_lhs: - t2 = mk_sub(pc->sub_count); - t1 = POP_ARRAY(); - lhs = assoc_lookup(t1, t2, pc->do_reference); - if ((*lhs)->type == Node_var_array) - fatal(_("attempt to use array `%s[\"%.*s\"]' in a scalar context"), - array_vname(t1), (int) t2->stlen, t2->stptr); - DEREF(t2); - PUSH_ADDRESS(lhs); - break; - - case Op_field_spec: - t1 = TOP_SCALAR(); - lhs = r_get_field(t1, (Func_ptr *) 0, TRUE); - decr_sp(); - DEREF(t1); - /* This used to look like this: - PUSH(dupnode(*lhs)); - but was changed to bypass an apparent bug in the z/OS C compiler. - Please do not remerge. */ - r = dupnode(*lhs); /* can't use UPREF here */ - PUSH(r); - break; - - case Op_field_spec_lhs: - t1 = TOP_SCALAR(); - lhs = r_get_field(t1, &pc->target_assign->field_assign, pc->do_reference); - decr_sp(); - DEREF(t1); - PUSH_ADDRESS(lhs); - break; - - case Op_lint: - if (do_lint) { - switch (pc->lint_type) { - case LINT_assign_in_cond: - lintwarn(_("assignment used in conditional context")); - break; - - case LINT_no_effect: - lintwarn(_("statement has no effect")); - break; - - default: - cant_happen(); - } - } - break; - - case Op_K_break: - case Op_K_continue: - case Op_jmp: - JUMPTO(pc->target_jmp); - - case Op_jmp_false: - r = POP_SCALAR(); - di = eval_condition(r); - DEREF(r); - if (! di) - JUMPTO(pc->target_jmp); - break; + if ((newval = getenv_long("GAWK_STACKSIZE")) > 0) + STACK_SIZE = newval; - case Op_jmp_true: - r = POP_SCALAR(); - di = eval_condition(r); - DEREF(r); - if (di) - JUMPTO(pc->target_jmp); - break; - - case Op_and: - case Op_or: - t1 = POP_SCALAR(); - di = eval_condition(t1); - DEREF(t1); - if ((pc->opcode == Op_and && di) - || (pc->opcode == Op_or && ! di)) - break; - r = make_number((AWKNUM) di); - PUSH(r); - ni = pc->target_jmp; - JUMPTO(ni->nexti); - - case Op_and_final: - case Op_or_final: - t1 = TOP_SCALAR(); - r = make_number((AWKNUM) eval_condition(t1)); - DEREF(t1); - REPLACE(r); - break; - - case Op_not: - t1 = TOP_SCALAR(); - r = make_number((AWKNUM) ! eval_condition(t1)); - DEREF(t1); - REPLACE(r); - break; - - case Op_equal: - r = make_number((AWKNUM) (cmp_scalar() == 0)); - REPLACE(r); - break; - - case Op_notequal: - r = make_number((AWKNUM) (cmp_scalar() != 0)); - REPLACE(r); - break; - - case Op_less: - r = make_number((AWKNUM) (cmp_scalar() < 0)); - REPLACE(r); - break; - - case Op_greater: - r = make_number((AWKNUM) (cmp_scalar() > 0)); - REPLACE(r); - break; - - case Op_leq: - r = make_number((AWKNUM) (cmp_scalar() <= 0)); - REPLACE(r); - break; - - case Op_geq: - r = make_number((AWKNUM) (cmp_scalar() >= 0)); - REPLACE(r); - break; - - case Op_plus_i: - x2 = force_number(pc->memory); - goto plus; - - case Op_plus: - POP_NUMBER(x2); -plus: - TOP_NUMBER(x1); - r = make_number(x1 + x2); - REPLACE(r); - break; - - case Op_minus_i: - x2 = force_number(pc->memory); - goto minus; - - case Op_minus: - POP_NUMBER(x2); -minus: - TOP_NUMBER(x1); - r = make_number(x1 - x2); - REPLACE(r); - break; - - case Op_times_i: - x2 = force_number(pc->memory); - goto times; - - case Op_times: - POP_NUMBER(x2); -times: - TOP_NUMBER(x1); - r = make_number(x1 * x2); - REPLACE(r); - break; - - case Op_exp_i: - x2 = force_number(pc->memory); - goto exponent; - - case Op_exp: - POP_NUMBER(x2); -exponent: - TOP_NUMBER(x1); - x = calc_exp(x1, x2); - r = make_number(x); - REPLACE(r); - break; - - case Op_quotient_i: - x2 = force_number(pc->memory); - goto quotient; - - case Op_quotient: - POP_NUMBER(x2); -quotient: - if (x2 == 0) - fatal(_("division by zero attempted")); - - TOP_NUMBER(x1); - x = x1 / x2; - r = make_number(x); - REPLACE(r); - break; - - case Op_mod_i: - x2 = force_number(pc->memory); - goto mod; - - case Op_mod: - POP_NUMBER(x2); -mod: - if (x2 == 0) - fatal(_("division by zero attempted in `%%'")); - - TOP_NUMBER(x1); -#ifdef HAVE_FMOD - x = fmod(x1, x2); -#else /* ! HAVE_FMOD */ - (void) modf(x1 / x2, &x); - x = x1 - x * x2; -#endif /* ! HAVE_FMOD */ - r = make_number(x); - REPLACE(r); - break; - - case Op_preincrement: - pre = TRUE; - case Op_postincrement: - x2 = 1.0; -post: - lhs = TOP_ADDRESS(); - x1 = force_number(*lhs); - unref(*lhs); - r = *lhs = make_number(x1 + x2); - if (pre) - UPREF(r); - else - r = make_number(x1); - REPLACE(r); - pre = FALSE; - break; - - case Op_predecrement: - pre = TRUE; - case Op_postdecrement: - x2 = -1.0; - goto post; - - case Op_unary_minus: - TOP_NUMBER(x1); - r = make_number(-x1); - REPLACE(r); - break; - - case Op_store_sub: - /* array[sub] assignment optimization, - * see awkgram.y (optimize_assignment) - */ - t1 = get_array(pc->memory, TRUE); /* array */ - t2 = mk_sub(pc->expr_count); /* subscript */ - lhs = assoc_lookup(t1, t2, FALSE); - if ((*lhs)->type == Node_var_array) - fatal(_("attempt to use array `%s[\"%.*s\"]' in a scalar context"), - array_vname(t1), (int) t2->stlen, t2->stptr); - DEREF(t2); - unref(*lhs); - *lhs = POP_SCALAR(); - break; - - case Op_store_var: - /* simple variable assignment optimization, - * see awkgram.y (optimize_assignment) - */ - - lhs = get_lhs(pc->memory, FALSE); - unref(*lhs); - *lhs = POP_SCALAR(); - break; - - case Op_store_field: - { - /* field assignment optimization, - * see awkgram.y (optimize_assignment) - */ - - Func_ptr assign; - t1 = TOP_SCALAR(); - lhs = r_get_field(t1, &assign, FALSE); - decr_sp(); - DEREF(t1); - unref(*lhs); - *lhs = POP_SCALAR(); - assert(assign != NULL); - assign(); - } - break; - - case Op_assign_concat: - /* x = x ... string concatenation optimization */ - lhs = get_lhs(pc->memory, FALSE); - t1 = force_string(*lhs); - t2 = POP_STRING(); - - free_wstr(*lhs); - - if (t1 != t2 && t1->valref == 1 && (t1->flags & PERM) == 0) { - size_t nlen = t1->stlen + t2->stlen; - erealloc(t1->stptr, char *, nlen + 2, "r_interpret"); - memcpy(t1->stptr + t1->stlen, t2->stptr, t2->stlen); - t1->stlen = nlen; - t1->stptr[nlen] = '\0'; - } else { - size_t nlen = t1->stlen + t2->stlen; - char *p; - - emalloc(p, char *, nlen + 2, "r_interpret"); - memcpy(p, t1->stptr, t1->stlen); - memcpy(p + t1->stlen, t2->stptr, t2->stlen); - unref(*lhs); - t1 = *lhs = make_str_node(p, nlen, ALREADY_MALLOCED); - } - t1->flags &= ~(NUMCUR|NUMBER); - DEREF(t2); - break; - - case Op_assign: - lhs = POP_ADDRESS(); - r = TOP_SCALAR(); - unref(*lhs); - *lhs = r; - UPREF(r); - REPLACE(r); - break; - - /* numeric assignments */ - case Op_assign_plus: - case Op_assign_minus: - case Op_assign_times: - case Op_assign_quotient: - case Op_assign_mod: - case Op_assign_exp: - op_assign(pc->opcode); - break; - - case Op_var_update: /* update value of NR, FNR or NF */ - pc->update_var(); - break; - - case Op_var_assign: - case Op_field_assign: - if (pc->assign_ctxt == Op_sub_builtin - && TOP()->numbr == 0.0 /* top of stack has a number == 0 */ - ) { - /* There wasn't any substitutions. If the target is a FIELD, - * this means no field re-splitting or $0 reconstruction. - * Skip the set_FOO routine if the target is a special variable. - */ - - break; - } else if ((pc->assign_ctxt == Op_K_getline - || pc->assign_ctxt == Op_K_getline_redir) - && TOP()->numbr <= 0.0 /* top of stack has a number <= 0 */ - ) { - /* getline returned EOF or error */ - - break; - } - - if (pc->opcode == Op_var_assign) - pc->assign_var(); - else - pc->field_assign(); - break; - - case Op_concat: - r = concat_exp(pc->expr_count, pc->concat_flag & CSUBSEP); - PUSH(r); - break; - - case Op_K_case: - if ((pc + 1)->match_exp) { - /* match a constant regex against switch expression instead of $0. */ - m = POP(); /* regex */ - t2 = TOP_SCALAR(); /* switch expression */ - (void) force_string(t2); - rp = re_update(m); - di = (research(rp, t2->stptr, 0, t2->stlen, - avoid_dfa(m, t2->stptr, t2->stlen)) >= 0); - } else { - t1 = POP_SCALAR(); /* case value */ - t2 = TOP_SCALAR(); /* switch expression */ - di = (cmp_nodes(t2, t1) == 0); - DEREF(t1); - } - - if (di) { /* match found */ - decr_sp(); - DEREF(t2); - JUMPTO(pc->target_jmp); - } - break; - - case Op_K_delete: - t1 = POP_ARRAY(); - do_delete(t1, pc->expr_count); - stack_adj(-pc->expr_count); - break; - - case Op_K_delete_loop: - t1 = POP_ARRAY(); - lhs = POP_ADDRESS(); /* item */ - do_delete_loop(t1, lhs); - break; - - case Op_in_array: - t1 = POP_ARRAY(); - t2 = mk_sub(pc->expr_count); - di = (in_array(t1, t2) != NULL); - DEREF(t2); - PUSH(make_number((AWKNUM) di)); - break; - - case Op_arrayfor_init: - { - NODE **list = NULL; - NODE *array, *sort_str; - size_t num_elems = 0; - static NODE *sorted_in = NULL; - const char *how_to_sort = "@unsorted"; - - /* get the array */ - array = POP_ARRAY(); - - /* sanity: check if empty */ - if (array->var_array == NULL || array->table_size == 0) - goto arrayfor; - - num_elems = array->table_size; - - if (sorted_in == NULL) /* do this once */ - sorted_in = make_string("sorted_in", 9); - - sort_str = NULL; - /* - * If posix, or if there's no PROCINFO[], - * there's no ["sorted_in"], so no sorting - */ - if (! do_posix && PROCINFO_node != NULL) - sort_str = in_array(PROCINFO_node, sorted_in); - - if (sort_str != NULL) { - sort_str = force_string(sort_str); - if (sort_str->stlen > 0) - how_to_sort = sort_str->stptr; - } - - list = assoc_list(array, how_to_sort, SORTED_IN); - - /* - * Actual array for use in lint warning - * in Op_arrayfor_incr - */ - list[num_elems] = array; - -arrayfor: - getnode(r); - r->type = Node_arrayfor; - r->var_array = list; - r->table_size = num_elems; /* # of elements in list */ - r->array_size = -1; /* current index */ - PUSH(r); - - if (num_elems == 0) - JUMPTO(pc->target_jmp); /* Op_arrayfor_final */ - } - break; - - case Op_arrayfor_incr: - r = TOP(); /* Node_arrayfor */ - if (++r->array_size == r->table_size) { - NODE *array; - array = r->var_array[r->table_size]; /* actual array */ - if (do_lint && array->table_size != r->table_size) - lintwarn(_("for loop: array `%s' changed size from %ld to %ld during loop execution"), - array_vname(array), (long) r->table_size, (long) array->table_size); - JUMPTO(pc->target_jmp); /* Op_arrayfor_final */ - } - - t1 = r->var_array[r->array_size]; - lhs = get_lhs(pc->array_var, FALSE); - unref(*lhs); - *lhs = make_string(t1->ahname_str, t1->ahname_len); - break; - - case Op_arrayfor_final: - r = POP(); - assert(r->type == Node_arrayfor); - free_arrayfor(r); - break; - - case Op_builtin: - r = pc->builtin(pc->expr_count); -#if defined(GAWKDEBUG) || defined(ARRAYDEBUG) - if (! r) - last_was_stopme = TRUE; - else -#endif - PUSH(r); - break; - - case Op_sub_builtin: /* sub, gsub and gensub */ - r = do_sub(pc->expr_count, pc->sub_flags); - PUSH(r); - break; - - case Op_K_print: - do_print(pc->expr_count, pc->redir_type); - break; - - case Op_K_printf: - do_printf(pc->expr_count, pc->redir_type); - break; - - case Op_K_print_rec: - do_print_rec(pc->expr_count, pc->redir_type); - break; - - case Op_push_re: - m = pc->memory; - if (m->type == Node_dynregex) { - r = POP_STRING(); - unref(m->re_exp); - m->re_exp = r; - } - PUSH(m); - break; - - case Op_match_rec: - m = pc->memory; - t1 = *get_field(0, (Func_ptr *) 0); -match_re: - rp = re_update(m); - /* - * Any place where research() is called with a last parameter of - * zero, we need to use the avoid_dfa test. This appears here and - * in the code for Op_K_case. - * - * A new or improved dfa that distinguishes beginning/end of - * string from beginning/end of line will allow us to get rid of - * this hack. - * - * The avoid_dfa() function is in re.c; it is not very smart. - */ - - di = research(rp, t1->stptr, 0, t1->stlen, - avoid_dfa(m, t1->stptr, t1->stlen)); - di = (di == -1) ^ (pc->opcode != Op_nomatch); - if(pc->opcode != Op_match_rec) { - decr_sp(); - DEREF(t1); - } - r = make_number((AWKNUM) di); - PUSH(r); - break; - - case Op_nomatch: - /* fall through */ - case Op_match: - m = pc->memory; - t1 = TOP_STRING(); - if (m->type == Node_dynregex) { - unref(m->re_exp); - m->re_exp = t1; - decr_sp(); - t1 = TOP_STRING(); - } - goto match_re; - break; - - case Op_indirect_func_call: - { - int arg_count; - - f = NULL; - arg_count = (pc + 1)->expr_count; - t1 = PEEK(arg_count); /* indirect var */ - assert(t1->type == Node_val); /* @a[1](p) not allowed in grammar */ - (void) force_string(t1); - if (t1->stlen > 0) { - /* retrieve function definition node */ - f = pc->func_body; - if (f != NULL && strcmp(f->vname, t1->stptr) == 0) - /* indirect var hasn't been reassigned */ - goto func_call; - f = lookup(t1->stptr); - } - - if (f == NULL || f->type != Node_func) - fatal(_("function called indirectly through `%s' does not exist"), pc->func_name); - pc->func_body = f; /* save for next call */ - - goto func_call; - } - - case Op_func_call: - /* retrieve function definition node */ - f = pc->func_body; - if (f == NULL) { - f = lookup(pc->func_name); - if (f == NULL || f->type != Node_func) - fatal(_("function `%s' not defined"), pc->func_name); - pc->func_body = f; /* save for next call */ - } - - /* save current frame along with source */ - -func_call: - ni = setup_frame(pc); - - if (ni->opcode == Op_ext_func) { - /* dynamically set source and line numbers for an extension builtin. */ - ni->source_file = source; - ni->source_line = sourceline; - ni->nexti->source_line = sourceline; /* Op_builtin */ - ni->nexti->nexti->source_line = sourceline; /* Op_K_return */ - } - - /* run the function instructions */ - JUMPTO(ni); /* Op_func or Op_ext_func */ - - case Op_K_return: - m = POP_SCALAR(); /* return value */ - - ni = pop_fcall(); - - /* put the return value back on stack */ - PUSH(m); - - JUMPTO(ni); - - case Op_K_getline_redir: - if ((currule == BEGINFILE || currule == ENDFILE) - && pc->into_var == FALSE - && pc->redir_type == redirect_input) - fatal(_("`getline' invalid inside `%s' rule"), ruletab[currule]); - r = do_getline_redir(pc->into_var, pc->redir_type); - PUSH(r); - break; - - case Op_K_getline: /* no redirection */ - if (! currule || currule == BEGINFILE || currule == ENDFILE) - fatal(_("non-redirected `getline' invalid inside `%s' rule"), - ruletab[currule]); - - do { - int ret; - ret = nextfile(& curfile, FALSE); - if (ret <= 0) - r = do_getline(pc->into_var, curfile); - else { - - /* Save execution state so that we can return to it - * from Op_after_beginfile or Op_after_endfile. - */ - - push_exec_state(pc, currule, source, stack_ptr); - - if (curfile == NULL) - JUMPTO((pc + 1)->target_endfile); - else - JUMPTO((pc + 1)->target_beginfile); - } - } while (r == NULL); /* EOF */ - - PUSH(r); - break; - - case Op_after_endfile: - /* Find the execution state to return to */ - ni = pop_exec_state(& currule, & source, NULL); - - assert(ni->opcode == Op_newfile || ni->opcode == Op_K_getline); - JUMPTO(ni); - - case Op_after_beginfile: - after_beginfile(& curfile); - - /* Find the execution state to return to */ - ni = pop_exec_state(& currule, & source, NULL); - - assert(ni->opcode == Op_newfile || ni->opcode == Op_K_getline); - if (ni->opcode == Op_K_getline - || curfile == NULL /* skipping directory argument */ - ) - JUMPTO(ni); - - break; /* read a record, Op_get_record */ - - case Op_newfile: - { - int ret; - - ret = nextfile(& curfile, FALSE); - - if (ret < 0) /* end of input */ - JUMPTO(pc->target_jmp); /* end block or Op_atexit */ - - if (ret == 0) /* read a record */ - JUMPTO((pc + 1)->target_get_record); - - /* ret > 0 */ - /* Save execution state for use in Op_after_beginfile or Op_after_endfile. */ - - push_exec_state(pc, currule, source, stack_ptr); - - if (curfile == NULL) /* EOF */ - JUMPTO(pc->target_endfile); - /* else - execute beginfile block */ - } - break; - - case Op_get_record: - { - int errcode = 0; - - ni = pc->target_newfile; - if (curfile == NULL) { - /* from non-redirected getline, e.g.: - * { - * while (getline > 0) ; - * } - */ - - ni = ni->target_jmp; /* end_block or Op_atexit */ - JUMPTO(ni); - } - - if (inrec(curfile, & errcode) != 0) { - if (errcode > 0 && (do_traditional || ! pc->has_endfile)) - fatal(_("error reading input file `%s': %s"), - curfile->name, strerror(errcode)); - - JUMPTO(ni); - } /* else - prog (rule) block */ - } - break; - - case Op_K_nextfile: - { - int ret; - - if (currule != Rule && currule != BEGINFILE) - fatal(_("`nextfile' cannot be called from a `%s' rule"), - ruletab[currule]); - - ret = nextfile(& curfile, TRUE); /* skip current file */ - - if (currule == BEGINFILE) { - long stack_size; - - ni = pop_exec_state(& currule, & source, & stack_size); - - assert(ni->opcode == Op_K_getline || ni->opcode == Op_newfile); - - /* pop stack returning to the state of Op_K_getline or Op_newfile. */ - unwind_stack(stack_size); - - if (ret == 0) { - /* There was an error opening the file; - * don't run ENDFILE block(s). - */ - - JUMPTO(ni); - } else { - /* do run ENDFILE block(s) first. */ - - /* Execution state to return to in Op_after_endfile. */ - push_exec_state(ni, currule, source, stack_ptr); - - JUMPTO(pc->target_endfile); - } - } /* else - Start over with the first rule. */ - - /* empty the run-time stack to avoid memory leak */ - pop_stack(); - - /* Push an execution state for Op_after_endfile to return to */ - push_exec_state(pc->target_newfile, currule, source, stack_ptr); - - JUMPTO(pc->target_endfile); - } - break; - - case Op_K_exit: - /* exit not allowed in user-defined comparison functions for "sorted_in"; - * This is done so that END blocks aren't executed more than once. - */ - if (! currule) - fatal(_("`exit' cannot be called in the current context")); - - exiting = TRUE; - POP_NUMBER(x1); - exit_val = (int) x1; -#ifdef VMS - if (exit_val == 0) - exit_val = EXIT_SUCCESS; - else if (exit_val == 1) - exit_val = EXIT_FAILURE; - /* else - just pass anything else on through */ -#endif - - if (currule == BEGINFILE || currule == ENDFILE) { - - /* Find the rule of the saved execution state (Op_K_getline/Op_newfile). - * This is needed to prevent multiple execution of any END rules: - * gawk 'BEGINFILE { exit(1) } \ - * END { while (getline > 0); }' in1 in2 - */ - - (void) pop_exec_state(& currule, & source, NULL); - } - - pop_stack(); /* empty stack, don't leak memory */ - - /* Jump to either the first END block instruction - * or to Op_atexit. - */ - - if (currule == END) - ni = pc->target_atexit; - else - ni = pc->target_end; - JUMPTO(ni); - - case Op_K_next: - if (currule != Rule) - fatal(_("`next' cannot be called from a `%s' rule"), ruletab[currule]); - - pop_stack(); - JUMPTO(pc->target_jmp); /* Op_get_record, read next record */ - - case Op_pop: -#if defined(GAWKDEBUG) || defined(ARRAYDEBUG) - if (last_was_stopme) - last_was_stopme = FALSE; - else -#endif - { - r = POP_SCALAR(); - DEREF(r); - } - break; - - case Op_line_range: - if (pc->triggered) /* evaluate right expression */ - JUMPTO(pc->target_jmp); - /* else - evaluate left expression */ - break; - - case Op_cond_pair: - { - int result; - INSTRUCTION *ip; - - t1 = TOP_SCALAR(); /* from right hand side expression */ - di = (eval_condition(t1) != 0); - DEREF(t1); - - ip = pc->line_range; /* Op_line_range */ - - if (! ip->triggered && di) { - /* not already triggered and left expression is TRUE */ - decr_sp(); - ip->triggered = TRUE; - JUMPTO(ip->target_jmp); /* evaluate right expression */ - } - - result = ip->triggered || di; - ip->triggered ^= di; /* update triggered flag */ - r = make_number((AWKNUM) result); /* final value of condition pair */ - REPLACE(r); - JUMPTO(pc->target_jmp); - } + emalloc(stack_bottom, STACK_ITEM *, STACK_SIZE * sizeof(STACK_ITEM), "grow_stack"); + stack_ptr = stack_bottom - 1; + stack_top = stack_bottom + STACK_SIZE - 1; - case Op_exec_count: - INCREMENT(pc->exec_count); - break; + /* initialize frame pointer */ + getnode(frame_ptr); + frame_ptr->type = Node_frame; + frame_ptr->stack = NULL; + frame_ptr->func_node = NULL; /* in main */ + frame_ptr->num_tail_calls = 0; + frame_ptr->vname = NULL; - case Op_no_op: - case Op_K_do: - case Op_K_while: - case Op_K_for: - case Op_K_arrayfor: - case Op_K_switch: - case Op_K_default: - case Op_K_if: - case Op_K_else: - case Op_cond_exp: - break; + /* initialize TRUE and FALSE nodes */ + node_Boolean[FALSE] = make_number(0); + node_Boolean[FALSE]->flags |= NUMINT; + node_Boolean[TRUE] = make_number(1.0); + node_Boolean[TRUE]->flags |= NUMINT; - default: - fatal(_("Sorry, don't know how to interpret `%s'"), opcode2str(pc->opcode)); - } + /* select the interpreter routine */ + if (do_debug) + interpret = debug_interpret; + else + interpret = r_interpret; +} - JUMPTO(pc->nexti); -/* } forever */ +/* interpreter routine when not debugging */ +#include "interpret.h" - /* not reached */ - return 0; +/* interpreter routine when deubugging with gawk --debug */ +#define r_interpret debug_interpret +#define DEBUGGING 1 +#include "interpret.h" +#undef DEBUGGING +#undef r_interpret -#undef mk_sub -#undef JUMPTO -} diff --git a/eval_d.c b/eval_d.c deleted file mode 100644 index 64a8e55a..00000000 --- a/eval_d.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * eval_p.c - compile eval.c with debugging turned on. - */ - -/* - * Copyright (C) 2001 the Free Software Foundation, Inc. - * - * This file is part of GAWK, the GNU implementation of the - * AWK Programming Language. - * - * GAWK 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. - * - * GAWK 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 - */ - -#define DEBUGGING 1 -#include "eval.c" diff --git a/eval_p.c b/eval_p.c deleted file mode 100644 index c8afe666..00000000 --- a/eval_p.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * eval_p.c - compile eval.c with profiling turned on. - */ - -/* - * Copyright (C) 2001 the Free Software Foundation, Inc. - * - * This file is part of GAWK, the GNU implementation of the - * AWK Programming Language. - * - * GAWK 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 3 of the License, or - * (at your option) any later version. - * - * GAWK 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define PROFILING 1 -#include "eval.c" @@ -37,22 +37,38 @@ static unsigned long long dummy; /* fake out gcc for dynamic loading? */ #endif -/* do_ext --- load an extension */ - +/* do_ext --- load an extension at run-time: interface to load_ext */ + NODE * do_ext(int nargs) { - NODE *obj; - NODE *fun; + NODE *obj, *fun, *ret = NULL; + SRCFILE *s; + extern SRCFILE *srcfiles; + + fun = POP_STRING(); + obj = POP_STRING(); + + s = add_srcfile(SRC_EXTLIB, obj->stptr, srcfiles, NULL, NULL); + if (s != NULL) + ret = load_ext(s->fullpath, fun->stptr, obj); + DEREF(obj); + DEREF(fun); + if (ret == NULL) + ret = dupnode(Nnull_string); + return ret; +} + +/* load_ext --- load an external library */ + +NODE * +load_ext(const char *lib_name, const char *init_func, NODE *obj) +{ NODE *tmp = NULL; NODE *(*func)(NODE *, void *); void *dl; int flags = RTLD_LAZY; - int fatal_error = FALSE; int *gpl_compat; -#if 0 - static short warned = FALSE; -#endif #ifdef __GNUC__ AWKNUM junk; @@ -63,57 +79,37 @@ do_ext(int nargs) if (do_sandbox) fatal(_("extensions are not allowed in sandbox mode")); -#if 0 - /* already done in parser */ - if (do_lint && ! warned) { - warned = TRUE; - lintwarn(_("`extension' is a gawk extension")); - } -#endif - if (do_traditional || do_posix) - error(_("`extension' is a gawk extension")); - - fun = POP_STRING(); - obj = POP_STRING(); + fatal(_("`extension' is a gawk extension")); #ifdef RTLD_GLOBAL flags |= RTLD_GLOBAL; #endif - if ((dl = dlopen(obj->stptr, flags)) == NULL) { - /* fatal needs `obj', and we need to deallocate it! */ - msg(_("fatal: extension: cannot open `%s' (%s)\n"), obj->stptr, + + if ((dl = dlopen(lib_name, flags)) == NULL) + fatal(_("extension: cannot open library `%s' (%s)\n"), lib_name, dlerror()); - fatal_error = TRUE; - goto done; - } /* Per the GNU Coding standards */ gpl_compat = (int *) dlsym(dl, "plugin_is_GPL_compatible"); - if (gpl_compat == NULL) { - msg(_("fatal: extension: library `%s': does not define `plugin_is_GPL_compatible' (%s)\n"), - obj->stptr, dlerror()); - fatal_error = TRUE; - goto done; - } - - - func = (NODE *(*)(NODE *, void *)) dlsym(dl, fun->stptr); - if (func == NULL) { - msg(_("fatal: extension: library `%s': cannot call function `%s' (%s)\n"), - obj->stptr, fun->stptr, dlerror()); - fatal_error = TRUE; - goto done; + if (gpl_compat == NULL) + fatal(_("extension: library `%s': does not define `plugin_is_GPL_compatible' (%s)\n"), + lib_name, dlerror()); + + func = (NODE *(*)(NODE *, void *)) dlsym(dl, init_func); + if (func == NULL) + fatal(_("extension: library `%s': cannot call function `%s' (%s)\n"), + lib_name, init_func, dlerror()); + + if (obj == NULL) { + obj = make_string(lib_name, strlen(lib_name)); + tmp = (*func)(obj, dl); + unref(tmp); + unref(obj); + return NULL; } tmp = (*func)(obj, dl); - if (tmp == NULL) - tmp = Nnull_string; -done: - DEREF(obj); - DEREF(fun); - if (fatal_error) - gawk_exit(EXIT_FATAL); return tmp; } @@ -123,14 +119,10 @@ done: void make_builtin(const char *name, NODE *(*func)(int), int count) { - NODE *p, *symbol, *f; - INSTRUCTION *b, *r; + NODE *symbol, *f; + INSTRUCTION *b; const char *sp; - char *pname; - char **vnames = NULL; - char c, buf[200]; - size_t space_needed; - int i; + char c; sp = name; if (sp == NULL || *sp == '\0') @@ -146,126 +138,82 @@ make_builtin(const char *name, NODE *(*func)(int), int count) if (f != NULL) { if (f->type == Node_func) { - INSTRUCTION *pc = f->code_ptr; - if (pc->opcode != Op_ext_func) /* user-defined function */ - fatal(_("extension: can't redefine function `%s'"), name); - else { - /* multiple extension() calls etc. */ - if (do_lint) - lintwarn(_("extension: function `%s' already defined"), name); - return; - } + /* user-defined function */ + fatal(_("extension: can't redefine function `%s'"), name); + } else if (f->type == Node_ext_func) { + /* multiple extension() calls etc. */ + if (do_lint) + lintwarn(_("extension: function `%s' already defined"), name); + return; } else /* variable name etc. */ fatal(_("extension: function name `%s' previously defined"), name); } else if (check_special(name) >= 0) fatal(_("extension: can't use gawk built-in `%s' as function name"), name); - /* count parameters, create artificial list of param names */ if (count < 0) fatal(_("make_builtin: negative argument count for function `%s'"), - name); - - if (count > 0) { - sprintf(buf, "p%d", count); - space_needed = strlen(buf) + 1; - emalloc(vnames, char **, count * sizeof(char *), "make_builtin"); - for (i = 0; i < count; i++) { - emalloc(pname, char *, space_needed, "make_builtin"); - sprintf(pname, "p%d", i); - vnames[i] = pname; - } - } - - - getnode(p); - p->type = Node_param_list; - p->flags |= FUNC; - /* get our own copy for name */ - p->param = estrdup(name, strlen(name)); - p->param_cnt = count; + name); - /* actual source and line numbers set at runtime for these instructions */ - b = bcalloc(Op_builtin, 1, __LINE__); + b = bcalloc(Op_symbol, 1, 0); b->builtin = func; b->expr_count = count; - b->nexti = bcalloc(Op_K_return, 1, __LINE__); - r = bcalloc(Op_ext_func, 1, __LINE__); - r->source_file = __FILE__; - r->nexti = b; /* NB: extension sub must return something */ - symbol = mk_symbol(Node_func, p); - symbol->parmlist = vnames; - symbol->code_ptr = r; - r->func_body = symbol; - (void) install_symbol(p->param, symbol); + symbol = install_symbol(estrdup(name, strlen(name)), Node_ext_func); + symbol->code_ptr = b; } -/* get_curfunc_arg_count --- return number actual parameters */ - -size_t -get_curfunc_arg_count() -{ - size_t argc; - INSTRUCTION *pc; - - pc = (INSTRUCTION *) frame_ptr->reti; /* Op_func_call instruction */ - argc = (pc + 1)->expr_count; /* # of arguments supplied */ - return argc; -} - - -/* get_argument --- get the n'th argument of a dynamically linked function */ +/* get_argument --- get the i'th argument of a dynamically linked function */ NODE * get_argument(int i) { - int pcount; - NODE *t, *f; - int actual_args; + NODE *t; + int arg_count, pcount; INSTRUCTION *pc; - f = frame_ptr->func_node; - pcount = f->lnode->param_cnt; + pc = TOP()->code_ptr; /* Op_ext_builtin instruction */ + pcount = (pc + 1)->expr_count; /* max # of arguments */ + arg_count = pc->expr_count; /* # of arguments supplied */ - pc = (INSTRUCTION *) frame_ptr->reti; /* Op_func_call instruction */ - actual_args = (pc + 1)->expr_count; /* # of arguments supplied */ - - if (i < 0 || i >= pcount || i >= actual_args) + if (i < 0 || i >= pcount || i >= arg_count) return NULL; - t = GET_PARAM(i); + t = PEEK(arg_count - i); + if (t->type == Node_param_list) + t = GET_PARAM(t->param_cnt); if (t->type == Node_array_ref) - return t->orig_array; /* Node_var_new or Node_var_array */ - if (t->type == Node_var_new || t->type == Node_var_array) - return t; - return t->var_value; + t = t->orig_array; + if (t->type == Node_var) /* See Case Node_var in setup_frame(), eval.c */ + return Nnull_string; + /* Node_var_new, Node_var_array or Node_val */ + return t; } -/* get_actual_argument --- get a scalar or array, allowed to be optional */ +/* get_actual_argument --- get the i'th scalar or array argument of a + dynamically linked function, allowed to be optional. +*/ NODE * get_actual_argument(int i, int optional, int want_array) { - /* optional : if TRUE and i th argument not present return NULL, else fatal. */ - - NODE *t, *f; - int pcount; + NODE *t; char *fname; - + int pcount; + INSTRUCTION *pc; + + pc = TOP()->code_ptr; /* Op_ext_builtin instruction */ + fname = (pc + 1)->func_name; + pcount = (pc + 1)->expr_count; + t = get_argument(i); - - f = frame_ptr->func_node; - pcount = f->lnode->param_cnt; - fname = f->lnode->param; - if (t == NULL) { - if (i >= pcount) /* must be fatal */ + if (i >= pcount) /* must be fatal */ fatal(_("function `%s' defined to take no more than %d argument(s)"), fname, pcount); if (! optional) @@ -279,8 +227,8 @@ get_actual_argument(int i, int optional, int want_array) return get_array(t, FALSE); else { t->type = Node_var; - t->var_value = Nnull_string; - return Nnull_string; + t->var_value = dupnode(Nnull_string); + return t->var_value; } } @@ -293,6 +241,7 @@ get_actual_argument(int i, int optional, int want_array) fatal(_("function `%s': argument #%d: attempt to use array as a scalar"), fname, i + 1); } + assert(t->type == Node_var_array || t->type == Node_val); return t; } @@ -309,4 +258,13 @@ do_ext(int nargs) ERRNO_node->var_value = make_string(emsg, strlen(emsg)); return make_number((AWKNUM) -1); } + +/* load_ext --- dummy version if extensions not available */ + +NODE * +load_ext(const char *lib_name, const char *init_func, NODE *obj) +{ + fatal(_("dynamic loading of library not supported")); + return NULL; +} #endif diff --git a/extension/ChangeLog b/extension/ChangeLog index 8aaeb418..dff4cf67 100644 --- a/extension/ChangeLog +++ b/extension/ChangeLog @@ -1,3 +1,7 @@ +2011-08-31 John Haque <j.eh@mchsi.com> + * arrayparm.c, filefuncs.c, fork.c, ordchr.c, readfile.c, + rwarray.c, testarg.c: Updated. + 2011-06-23 Arnold D. Robbins <arnold@skeeve.com> * ChangeLog.0: Rotated ChangeLog into this file. diff --git a/extension/arrayparm.c b/extension/arrayparm.c index 8a550ace..b0aee33d 100644 --- a/extension/arrayparm.c +++ b/extension/arrayparm.c @@ -43,13 +43,13 @@ int plugin_is_GPL_compatible; */ static NODE * -do_mkarray(int args) +do_mkarray(int nargs) { int ret = -1; NODE *var, *sub, *val; NODE **elemval; - if (do_lint && get_curfunc_arg_count() > 3) + if (do_lint && nargs > 3) lintwarn("mkarray: called with too many arguments"); var = get_array_argument(0, FALSE); @@ -60,9 +60,9 @@ do_mkarray(int args) printf("sub->type = %s\n", nodetype2str(sub->type)); printf("val->type = %s\n", nodetype2str(val->type)); - assoc_clear(var); + assoc_clear(var, NULL); - elemval = assoc_lookup(var, sub, 0); + elemval = assoc_lookup(var, sub); *elemval = dupnode(val); ret = 0; diff --git a/extension/filefuncs.c b/extension/filefuncs.c index ad7828f3..1a0a86ef 100644 --- a/extension/filefuncs.c +++ b/extension/filefuncs.c @@ -41,7 +41,7 @@ do_chdir(int nargs) NODE *newdir; int ret = -1; - if (do_lint && get_curfunc_arg_count() != 1) + if (do_lint && nargs != 1) lintwarn("chdir: called with incorrect number of arguments"); newdir = get_scalar_argument(0, FALSE); @@ -169,7 +169,7 @@ do_stat(int nargs) char *pmode; /* printable mode */ char *type = "unknown"; - if (do_lint && get_curfunc_arg_count() > 2) + if (do_lint && nargs > 2) lintwarn("stat: called with too many arguments"); /* file is first arg, array to hold results is second */ @@ -177,7 +177,7 @@ do_stat(int nargs) array = get_array_argument(1, FALSE); /* empty out the array */ - assoc_clear(array); + assoc_clear(array, NULL); /* lstat the file, if error, set ERRNO and return */ (void) force_string(file); @@ -188,76 +188,76 @@ do_stat(int nargs) } /* fill in the array */ - aptr = assoc_lookup(array, tmp = make_string("name", 4), FALSE); + aptr = assoc_lookup(array, tmp = make_string("name", 4)); *aptr = dupnode(file); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("dev", 3), FALSE); + aptr = assoc_lookup(array, tmp = make_string("dev", 3)); *aptr = make_number((AWKNUM) sbuf.st_dev); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("ino", 3), FALSE); + aptr = assoc_lookup(array, tmp = make_string("ino", 3)); *aptr = make_number((AWKNUM) sbuf.st_ino); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("mode", 4), FALSE); + aptr = assoc_lookup(array, tmp = make_string("mode", 4)); *aptr = make_number((AWKNUM) sbuf.st_mode); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("nlink", 5), FALSE); + aptr = assoc_lookup(array, tmp = make_string("nlink", 5)); *aptr = make_number((AWKNUM) sbuf.st_nlink); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("uid", 3), FALSE); + aptr = assoc_lookup(array, tmp = make_string("uid", 3)); *aptr = make_number((AWKNUM) sbuf.st_uid); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("gid", 3), FALSE); + aptr = assoc_lookup(array, tmp = make_string("gid", 3)); *aptr = make_number((AWKNUM) sbuf.st_gid); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("size", 4), FALSE); + aptr = assoc_lookup(array, tmp = make_string("size", 4)); *aptr = make_number((AWKNUM) sbuf.st_size); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("blocks", 6), FALSE); + aptr = assoc_lookup(array, tmp = make_string("blocks", 6)); *aptr = make_number((AWKNUM) sbuf.st_blocks); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("atime", 5), FALSE); + aptr = assoc_lookup(array, tmp = make_string("atime", 5)); *aptr = make_number((AWKNUM) sbuf.st_atime); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("mtime", 5), FALSE); + aptr = assoc_lookup(array, tmp = make_string("mtime", 5)); *aptr = make_number((AWKNUM) sbuf.st_mtime); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("ctime", 5), FALSE); + aptr = assoc_lookup(array, tmp = make_string("ctime", 5)); *aptr = make_number((AWKNUM) sbuf.st_ctime); unref(tmp); /* for block and character devices, add rdev, major and minor numbers */ if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode)) { - aptr = assoc_lookup(array, tmp = make_string("rdev", 4), FALSE); + aptr = assoc_lookup(array, tmp = make_string("rdev", 4)); *aptr = make_number((AWKNUM) sbuf.st_rdev); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("major", 5), FALSE); + aptr = assoc_lookup(array, tmp = make_string("major", 5)); *aptr = make_number((AWKNUM) major(sbuf.st_rdev)); unref(tmp); - aptr = assoc_lookup(array, tmp = make_string("minor", 5), FALSE); + aptr = assoc_lookup(array, tmp = make_string("minor", 5)); *aptr = make_number((AWKNUM) minor(sbuf.st_rdev)); unref(tmp); } #ifdef HAVE_ST_BLKSIZE - aptr = assoc_lookup(array, tmp = make_string("blksize", 7), FALSE); + aptr = assoc_lookup(array, tmp = make_string("blksize", 7)); *aptr = make_number((AWKNUM) sbuf.st_blksize); unref(tmp); #endif /* HAVE_ST_BLKSIZE */ - aptr = assoc_lookup(array, tmp = make_string("pmode", 5), FALSE); + aptr = assoc_lookup(array, tmp = make_string("pmode", 5)); pmode = format_mode(sbuf.st_mode); *aptr = make_string(pmode, strlen(pmode)); unref(tmp); @@ -277,7 +277,7 @@ do_stat(int nargs) */ buf[linksize] = '\0'; - aptr = assoc_lookup(array, tmp = make_string("linkval", 7), FALSE); + aptr = assoc_lookup(array, tmp = make_string("linkval", 7)); *aptr = make_str_node(buf, linksize, ALREADY_MALLOCED); unref(tmp); } @@ -319,7 +319,7 @@ do_stat(int nargs) #endif } - aptr = assoc_lookup(array, tmp = make_string("type", 4), FALSE); + aptr = assoc_lookup(array, tmp = make_string("type", 4)); *aptr = make_string(type, strlen(type)); unref(tmp); diff --git a/extension/fork.c b/extension/fork.c index aff9b568..88353879 100644 --- a/extension/fork.c +++ b/extension/fork.c @@ -38,7 +38,7 @@ do_fork(int nargs) NODE **aptr; NODE *tmp; - if (do_lint && get_curfunc_arg_count() > 0) + if (do_lint && nargs > 0) lintwarn("fork: called with too many arguments"); ret = fork(); @@ -48,11 +48,11 @@ do_fork(int nargs) else if (ret == 0) { /* update PROCINFO in the child */ - aptr = assoc_lookup(PROCINFO_node, tmp = make_string("pid", 3), FALSE); + aptr = assoc_lookup(PROCINFO_node, tmp = make_string("pid", 3)); (*aptr)->numbr = (AWKNUM) getpid(); unref(tmp); - aptr = assoc_lookup(PROCINFO_node, tmp = make_string("ppid", 4), FALSE); + aptr = assoc_lookup(PROCINFO_node, tmp = make_string("ppid", 4)); (*aptr)->numbr = (AWKNUM) getppid(); unref(tmp); } @@ -73,7 +73,7 @@ do_waitpid(int nargs) pid_t pid; int options = 0; - if (do_lint && get_curfunc_arg_count() > 1) + if (do_lint && nargs > 1) lintwarn("waitpid: called with too many arguments"); pidnode = get_scalar_argument(0, FALSE); diff --git a/extension/ordchr.c b/extension/ordchr.c index efbc6d56..8926a949 100644 --- a/extension/ordchr.c +++ b/extension/ordchr.c @@ -40,7 +40,7 @@ do_ord(int nargs) NODE *str; int ret = -1; - if (do_lint && get_curfunc_arg_count() > 1) + if (do_lint && nargs > 1) lintwarn("ord: called with too many arguments"); str = get_scalar_argument(0, FALSE); @@ -67,7 +67,7 @@ do_chr(int nargs) str[0] = str[1] = '\0'; - if (do_lint && get_curfunc_arg_count() > 1) + if (do_lint && nargs > 1) lintwarn("chr: called with too many arguments"); num = get_scalar_argument(0, FALSE); diff --git a/extension/readfile.c b/extension/readfile.c index e6ee0f22..c9b1efc3 100644 --- a/extension/readfile.c +++ b/extension/readfile.c @@ -50,7 +50,7 @@ do_readfile(int nargs) char *text; int fd; - if (do_lint && get_curfunc_arg_count() > 1) + if (do_lint && nargs > 1) lintwarn("readfile: called with too many arguments"); filename = get_scalar_argument(0, FALSE); diff --git a/extension/rwarray.c b/extension/rwarray.c index 3c629579..8175c7c0 100644 --- a/extension/rwarray.c +++ b/extension/rwarray.c @@ -82,7 +82,7 @@ do_writea(int nargs) uint32_t major = MAJOR; uint32_t minor = MINOR; - if (do_lint && get_curfunc_arg_count() > 2) + if (do_lint && nargs > 2) lintwarn("writea: called with too many arguments"); /* directory is first arg, array to dump is second */ @@ -250,7 +250,7 @@ do_reada(int nargs) uint32_t minor; char magic_buf[30]; - if (do_lint && get_curfunc_arg_count() > 2) + if (do_lint && nargs > 2) lintwarn("reada: called with too many arguments"); /* directory is first arg, array to dump is second */ @@ -289,7 +289,7 @@ do_reada(int nargs) goto done1; } - assoc_clear(array); + assoc_clear(array, NULL); ret = read_array(fd, array); if (ret == 0) diff --git a/extension/testarg.c b/extension/testarg.c index ba4d56ff..4d012db5 100644 --- a/extension/testarg.c +++ b/extension/testarg.c @@ -5,17 +5,15 @@ int plugin_is_GPL_compatible; static NODE * do_check_arg(int nargs) { - int ret = 0, argc; + int ret = 0; NODE *arg1, *arg2, *arg3; - argc = get_curfunc_arg_count(); - printf("arg count: defined = %d, supplied = %d\n", - nargs, argc); + printf("arg count: defined = 3, supplied = %d\n", nargs); arg1 = get_scalar_argument(0, FALSE); arg2 = get_array_argument(1, FALSE); arg3 = get_scalar_argument(2, TRUE); /* optional */ - if (argc > 3) { + if (nargs > 3) { /* try to use an extra arg */ NODE *arg4; arg4 = get_array_argument(3, TRUE); @@ -85,13 +85,13 @@ void init_fields() { emalloc(fields_arr, NODE **, sizeof(NODE *), "init_fields"); - fields_arr[0] = Nnull_string; + fields_arr[0] = dupnode(Nnull_string); parse_extent = fields_arr[0]->stptr; save_FS = dupnode(FS_node->var_value); getnode(Null_field); *Null_field = *Nnull_string; - Null_field->flags |= FIELD; - Null_field->flags &= ~(NUMCUR|NUMBER|MAYBE_NUM|PERM|MALLOC); + Null_field->valref = 1; + Null_field->flags = (FIELD|STRCUR|STRING); field0_valid = TRUE; } @@ -207,7 +207,7 @@ rebuild_record() } } else { *n = *(fields_arr[i]); - n->flags &= ~(MALLOC|PERM|STRING); + n->flags &= ~(MALLOC|STRING); } n->stptr = cops; @@ -289,7 +289,7 @@ reset_record() int i; NODE *n; - (void) force_string(fields_arr[0]); + fields_arr[0] = force_string(fields_arr[0]); NF = -1; for (i = 1; i <= parse_high_water; i++) { @@ -927,7 +927,7 @@ set_element(long num, char *s, long len, NODE *n) it = make_string(s, len); it->flags |= MAYBE_NUM; sub = make_number((AWKNUM) (num)); - lhs = assoc_lookup(n, sub, FALSE); + lhs = assoc_lookup(n, sub); unref(sub); unref(*lhs); *lhs = it; @@ -988,8 +988,8 @@ do_split(int nargs) /* * Skip the work if first arg is the null string. */ - decr_sp(); - DEREF(src); + tmp = POP_SCALAR(); + DEREF(tmp); return make_number((AWKNUM) 0); } @@ -1027,7 +1027,7 @@ do_split(int nargs) tmp = make_number((AWKNUM) (*parseit)(UNLIMITED, &s, (int) src->stlen, fs, rp, set_element, arr, sep_arr, FALSE)); - decr_sp(); + src = POP_SCALAR(); /* really pop off stack */ DEREF(src); return tmp; } @@ -1088,7 +1088,7 @@ do_patsplit(int nargs) set_element, arr, sep_arr, FALSE)); } - decr_sp(); /* 1st argument not POP-ed */ + src = POP_SCALAR(); /* really pop off stack */ DEREF(src); return tmp; } @@ -1104,6 +1104,7 @@ set_FIELDWIDTHS() static int fw_alloc = 4; static short warned = FALSE; int fatal_error = FALSE; + NODE *tmp; if (do_lint && ! warned) { warned = TRUE; @@ -1120,7 +1121,8 @@ set_FIELDWIDTHS() (void) get_field(UNLIMITED - 1, 0); parse_field = fw_parse_field; - scan = force_string(FIELDWIDTHS_node->var_value)->stptr; + tmp = force_string(FIELDWIDTHS_node->var_value); + scan = tmp->stptr; if (FIELDWIDTHS == NULL) emalloc(FIELDWIDTHS, int *, fw_alloc * sizeof(int), "set_FIELDWIDTHS"); @@ -1331,7 +1333,7 @@ update_PROCINFO_str(const char *subscript, const char *str) if (PROCINFO_node == NULL) return; tmp = make_string(subscript, strlen(subscript)); - aptr = assoc_lookup(PROCINFO_node, tmp, FALSE); + aptr = assoc_lookup(PROCINFO_node, tmp); unref(tmp); unref(*aptr); *aptr = make_string(str, strlen(str)); @@ -1348,7 +1350,7 @@ update_PROCINFO_num(const char *subscript, AWKNUM val) if (PROCINFO_node == NULL) return; tmp = make_string(subscript, strlen(subscript)); - aptr = assoc_lookup(PROCINFO_node, tmp, FALSE); + aptr = assoc_lookup(PROCINFO_node, tmp); unref(tmp); unref(*aptr); *aptr = make_number(val); diff --git a/int_array.c b/int_array.c new file mode 100644 index 00000000..9dd20bea --- /dev/null +++ b/int_array.c @@ -0,0 +1,828 @@ +/* + * int_array.c - routines for arrays of integer indices. + */ + +/* + * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Programming Language. + * + * GAWK 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 3 of the License, or + * (at your option) any later version. + * + * GAWK 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "awk.h" + +extern FILE *output_fp; +extern void indent(int indent_level); +extern NODE **is_integer(NODE *symbol, NODE *subs); + +static size_t INT_CHAIN_MAX = 2; + +static NODE **int_array_init(NODE *symbol, NODE *subs); +static NODE **int_lookup(NODE *symbol, NODE *subs); +static NODE **int_exists(NODE *symbol, NODE *subs); +static NODE **int_clear(NODE *symbol, NODE *subs); +static NODE **int_remove(NODE *symbol, NODE *subs); +static NODE **int_list(NODE *symbol, NODE *t); +static NODE **int_copy(NODE *symbol, NODE *newsymb); +static NODE **int_dump(NODE *symbol, NODE *ndump); + +#ifdef ARRAYDEBUG +static NODE **int_option(NODE *opt, NODE *val); +#endif + +static uint32_t int_hash(uint32_t k, uint32_t hsize); +static inline NODE **int_find(NODE *symbol, long k, uint32_t hash1); +static NODE **int_insert(NODE *symbol, long k, uint32_t hash1); +static void grow_int_table(NODE *symbol); + +array_ptr int_array_func[] = { + int_array_init, + is_integer, + int_lookup, + int_exists, + int_clear, + int_remove, + int_list, + int_copy, + int_dump, +#ifdef ARRAYDEBUG + int_option, +#endif +}; + + +/* int_array_init --- check relevant environment variables */ + +static NODE ** +int_array_init(NODE *symbol ATTRIBUTE_UNUSED, NODE *subs ATTRIBUTE_UNUSED) +{ + long newval; + + if ((newval = getenv_long("INT_CHAIN_MAX")) > 0) + INT_CHAIN_MAX = newval; + return (NODE **) ! NULL; +} + + +/* is_integer --- check if subscript is an integer */ + +NODE ** +is_integer(NODE *symbol, NODE *subs) +{ + long l; + AWKNUM d; + + if (subs == Nnull_string) + return NULL; + + if ((subs->flags & NUMINT) != 0) + return (NODE **) ! NULL; + + if ((subs->flags & NUMBER) != 0) { + d = subs->numbr; + if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d) { + subs->flags |= NUMINT; + return (NODE **) ! NULL; + } + return NULL; + } + + /* a[3]=1; print "3" in a -- TRUE + * a[3]=1; print "+3" in a -- FALSE + * a[3]=1; print "03" in a -- FALSE + * a[-3]=1; print "-3" in a -- TRUE + */ + + if ((subs->flags & (STRING|STRCUR)) != 0) { + char *cp = subs->stptr, *cpend, *ptr; + char save; + size_t len = subs->stlen; + + if (len == 0 || (! isdigit((unsigned char) *cp) && *cp != '-')) + return NULL; + if (len > 1 && + ((*cp == '0') /* "00", "011" .. */ + || (*cp == '-' && *(cp + 1) == '0') /* "-0", "-011" .. */ + ) + ) + return NULL; + if (len == 1 && *cp != '-') { /* single digit */ + subs->numbr = (long) (*cp - '0'); + if ((subs->flags & MAYBE_NUM) != 0) { + subs->flags &= ~MAYBE_NUM; + subs->flags |= NUMBER; + } + subs->flags |= (NUMCUR|NUMINT); + return (NODE **) ! NULL; + } + + cpend = cp + len; + save = *cpend; + *cpend = '\0'; + + errno = 0; + l = strtol(cp, & ptr, 10); + *cpend = save; + if (errno != 0 || ptr != cpend) + return NULL; + subs->numbr = l; + if ((subs->flags & MAYBE_NUM) != 0) { + subs->flags &= ~MAYBE_NUM; + subs->flags |= NUMBER; + } + subs->flags |= NUMCUR; + if (l <= INT32_MAX && l >= INT32_MIN) { + subs->flags |= NUMINT; + return (NODE **) ! NULL; + } + } + return NULL; +} + + +/* int_lookup --- Find SYMBOL[SUBS] in the assoc array. Install it with value "" + * if it isn't there. Returns a pointer ala get_lhs to where its value is stored. + */ + +static NODE ** +int_lookup(NODE *symbol, NODE *subs) +{ + uint32_t hash1; + long k; + unsigned long size; + NODE **lhs; + NODE *xn; + + /* N.B: symbol->table_size is the total # of non-integers (symbol->xarray) + * and integer elements. Also, symbol->xarray must have at least one + * item in it, and can not exist if there are no integer elements. + * In that case, symbol->xarray is promoted to 'symbol' (See int_remove). + */ + + + if (! is_integer(symbol, subs)) { + xn = symbol->xarray; + if (xn == NULL) { + xn = symbol->xarray = make_array(); + xn->vname = symbol->vname; /* shallow copy */ + xn->flags |= XARRAY; + } else if ((lhs = xn->aexists(xn, subs)) != NULL) + return lhs; + symbol->table_size++; + return assoc_lookup(xn, subs); + } + + k = subs->numbr; + if (symbol->buckets == NULL) + grow_int_table(symbol); + + hash1 = int_hash(k, symbol->array_size); + if ((lhs = int_find(symbol, k, hash1)) != NULL) + return lhs; + + /* It's not there, install it */ + + symbol->table_size++; + + /* first see if we would need to grow the array, before installing */ + size = symbol->table_size; + if ((xn = symbol->xarray) != NULL) + size -= xn->table_size; + + if ((symbol->flags & ARRAYMAXED) == 0 + && (size / symbol->array_size) > INT_CHAIN_MAX) { + grow_int_table(symbol); + /* have to recompute hash value for new size */ + hash1 = int_hash(k, symbol->array_size); + } + + return int_insert(symbol, k, hash1); +} + + +/* int_exists --- test whether the array element symbol[subs] exists or not, + * return pointer to value if it does. + */ + +static NODE ** +int_exists(NODE *symbol, NODE *subs) +{ + long k; + uint32_t hash1; + + if (! is_integer(symbol, subs)) { + NODE *xn = symbol->xarray; + if (xn == NULL) + return NULL; + return xn->aexists(xn, subs); + } + if (symbol->buckets == NULL) + return NULL; + + k = subs->numbr; + hash1 = int_hash(k, symbol->array_size); + return int_find(symbol, k, hash1); +} + +/* int_clear --- flush all the values in symbol[] */ + +static NODE ** +int_clear(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED) +{ + unsigned long i; + int j; + BUCKET *b, *next; + NODE *r; + + if (symbol->xarray != NULL) { + NODE *xn = symbol->xarray; + assoc_clear(xn); + freenode(xn); + symbol->xarray = NULL; + } + + for (i = 0; i < symbol->array_size; i++) { + for (b = symbol->buckets[i]; b != NULL; b = next) { + next = b->ainext; + for (j = 0; j < b->aicount; j++) { + r = b->aivalue[j]; + if (r->type == Node_var_array) { + assoc_clear(r); /* recursively clear all sub-arrays */ + efree(r->vname); + freenode(r); + } else + unref(r); + } + freebucket(b); + } + symbol->buckets[i] = NULL; + } + if (symbol->buckets != NULL) + efree(symbol->buckets); + init_array(symbol); /* re-initialize symbol */ + symbol->flags &= ~ARRAYMAXED; + return NULL; +} + + +/* int_remove --- If SUBS is already in the table, remove it. */ + +static NODE ** +int_remove(NODE *symbol, NODE *subs) +{ + uint32_t hash1; + BUCKET *b, *prev = NULL; + long k; + int i; + NODE *xn = symbol->xarray; + + if (symbol->table_size == 0 || symbol->buckets == NULL) + return NULL; + + if (! is_integer(symbol, subs)) { + if (xn == NULL || xn->aremove(xn, subs) == NULL) + return NULL; + if (xn->table_size == 0) { + freenode(xn); + symbol->xarray = NULL; + } + symbol->table_size--; + assert(symbol->table_size > 0); + return (NODE **) ! NULL; + } + + k = subs->numbr; + hash1 = int_hash(k, symbol->array_size); + + for (b = symbol->buckets[hash1]; b != NULL; prev = b, b = b->ainext) { + for (i = 0; i < b->aicount; i++) { + if (k != b->ainum[i]) + continue; + + /* item found */ + if (i == 0 && b->aicount == 2) { + /* removing the 1st item; move 2nd item from position 1 to 0 */ + + b->ainum[0] = b->ainum[1]; + b->aivalue[0] = b->aivalue[1]; + } /* else + removing the only item or the 2nd item */ + + goto removed; + } + } + + if (b == NULL) /* item not in array */ + return NULL; + +removed: + b->aicount--; + + if (b->aicount == 0) { + /* detach bucket */ + if (prev != NULL) + prev->ainext = b->ainext; + else + symbol->buckets[hash1] = b->ainext; + + /* delete bucket */ + freebucket(b); + } else if (b != symbol->buckets[hash1]) { + BUCKET *head = symbol->buckets[hash1]; + + assert(b->aicount == 1); + /* move the last element from head + * to bucket to make it full. + */ + i = --head->aicount; /* head has one less element */ + b->ainum[1] = head->ainum[i]; + b->aivalue[1] = head->aivalue[i]; + b->aicount++; /* bucket has one more element */ + if (i == 0) { + /* head is now empty; delete head */ + symbol->buckets[hash1] = head->ainext; + freebucket(head); + } + } /* else + do nothing */ + + symbol->table_size--; + if (xn == NULL && symbol->table_size == 0) { + efree(symbol->buckets); + init_array(symbol); /* re-initialize array 'symbol' */ + symbol->flags &= ~ARRAYMAXED; + } else if (xn != NULL && symbol->table_size == xn->table_size) { + /* promote xn (str_array) to symbol */ + xn->flags &= ~XARRAY; + xn->parent_array = symbol->parent_array; + efree(symbol->buckets); + *symbol = *xn; + freenode(xn); + } + + return (NODE **) ! NULL; /* return success */ +} + + +/* int_copy --- duplicate input array "symbol" */ + +static NODE ** +int_copy(NODE *symbol, NODE *newsymb) +{ + BUCKET **old, **new, **pnew; + BUCKET *chain, *newchain; + int j; + unsigned long i, cursize; + + assert(symbol->buckets != NULL); + + /* find the current hash size */ + cursize = symbol->array_size; + + /* allocate new table */ + emalloc(new, BUCKET **, cursize * sizeof(BUCKET *), "int_copy"); + memset(new, '\0', cursize * sizeof(BUCKET *)); + + old = symbol->buckets; + + for (i = 0; i < cursize; i++) { + for (chain = old[i], pnew = & new[i]; chain != NULL; + chain = chain->ainext + ) { + getbucket(newchain); + newchain->aicount = chain->aicount; + for (j = 0; j < chain->aicount; j++) { + NODE *oldval; + + /* + * copy the corresponding key and + * value from the original input list + */ + newchain->ainum[j] = chain->ainum[j]; + + oldval = chain->aivalue[j]; + if (oldval->type == Node_val) + newchain->aivalue[j] = dupnode(oldval); + else { + NODE *r; + r = make_array(); + r->vname = estrdup(oldval->vname, strlen(oldval->vname)); + r->parent_array = newsymb; + newchain->aivalue[j] = assoc_copy(oldval, r); + } + } + + *pnew = newchain; + pnew = & newchain->ainext; + } + } + + if (symbol->xarray != NULL) { + NODE *xn, *n; + xn = symbol->xarray; + n = make_array(); + n->vname = newsymb->vname; /* shallow copy */ + (void) xn->acopy(xn, n); + newsymb->xarray = n; + } else + newsymb->xarray = NULL; + + newsymb->table_size = symbol->table_size; + newsymb->buckets = new; + newsymb->array_size = cursize; + newsymb->flags = symbol->flags; + + return NULL; +} + + +/* int_list --- return a list of array items */ + +static NODE** +int_list(NODE *symbol, NODE *t) +{ + NODE **list = NULL; + unsigned long num_elems, list_size, i, k = 0; + BUCKET *b; + NODE *r, *subs, *xn; + int j, elem_size = 1; + long num; + static char buf[100]; + + if (symbol->table_size == 0) + return NULL; + + num_elems = symbol->table_size; + if ((t->flags & (AINDEX|AVALUE|ADELETE)) == (AINDEX|ADELETE)) + num_elems = 1; + + if ((t->flags & (AINDEX|AVALUE)) == (AINDEX|AVALUE)) + elem_size = 2; + list_size = elem_size * num_elems; + + if (symbol->xarray != NULL) { + xn = symbol->xarray; + list = xn->alist(xn, t); + assert(list != NULL); + if (num_elems == 1 || num_elems == xn->table_size) + return list; + erealloc(list, NODE **, list_size * sizeof(NODE *), "int_list"); + k = elem_size * xn->table_size; + } else + emalloc(list, NODE **, list_size * sizeof(NODE *), "int_list"); + + /* populate it */ + + for (i = 0; i < symbol->array_size; i++) { + for (b = symbol->buckets[i]; b != NULL; b = b->ainext) { + for (j = 0; j < b->aicount; j++) { + /* index */ + num = b->ainum[j]; + if (t->flags & AISTR) { + sprintf(buf, "%ld", num); + subs = make_string(buf, strlen(buf)); + subs->numbr = num; + subs->flags |= (NUMCUR|NUMINT); + } else { + subs = make_number((AWKNUM) num); + subs->flags |= (INTIND|NUMINT); + } + list[k++] = subs; + + /* value */ + if (t->flags & AVALUE) { + r = b->aivalue[j]; + if (r->type == Node_val) { + if ((t->flags & AVNUM) != 0) + (void) force_number(r); + else if ((t->flags & AVSTR) != 0) + r = force_string(r); + } + list[k++] = r; + } + + if (k >= list_size) + return list; + } + } + } + return list; +} + + +/* int_kilobytes --- calculate memory consumption of the assoc array */ + +AWKNUM +int_kilobytes(NODE *symbol) +{ + unsigned long i, bucket_cnt = 0; + BUCKET *b; + AWKNUM kb; + extern AWKNUM str_kilobytes(NODE *symbol); + + for (i = 0; i < symbol->array_size; i++) { + for (b = symbol->buckets[i]; b != NULL; b = b->ainext) + bucket_cnt++; + } + kb = (((AWKNUM) bucket_cnt) * sizeof (BUCKET) + + ((AWKNUM) symbol->array_size) * sizeof (BUCKET *)) / 1024.0; + + if (symbol->xarray != NULL) + kb += str_kilobytes(symbol->xarray); + + return kb; +} + + +/* int_dump --- dump array info */ + +static NODE ** +int_dump(NODE *symbol, NODE *ndump) +{ +#define HCNT 31 + + int indent_level; + BUCKET *b; + NODE *xn = NULL; + unsigned long str_size = 0, int_size = 0; + unsigned long i; + size_t j, bucket_cnt; + static size_t hash_dist[HCNT + 1]; + + indent_level = ndump->alevel; + + if (symbol->xarray != NULL) { + xn = symbol->xarray; + str_size = xn->table_size; + } + int_size = symbol->table_size - str_size; + + if ((symbol->flags & XARRAY) == 0) + fprintf(output_fp, "%s `%s'\n", + (symbol->parent_array == NULL) ? "array" : "sub-array", + array_vname(symbol)); + + indent_level++; + indent(indent_level); + fprintf(output_fp, "array_func: int_array_func\n"); + if (symbol->flags != 0) { + indent(indent_level); + fprintf(output_fp, "flags: %s\n", flags2str(symbol->flags)); + } + indent(indent_level); + fprintf(output_fp, "INT_CHAIN_MAX: %lu\n", (unsigned long) INT_CHAIN_MAX); + indent(indent_level); + fprintf(output_fp, "array_size: %lu (int)\n", (unsigned long) symbol->array_size); + indent(indent_level); + fprintf(output_fp, "table_size: %lu (total), %lu (int), %lu (str)\n", + (unsigned long) symbol->table_size, int_size, str_size); + indent(indent_level); + fprintf(output_fp, "Avg # of items per chain (int): %.2g\n", + ((AWKNUM) int_size) / symbol->array_size); + + indent(indent_level); + fprintf(output_fp, "memory: %.2g kB (total)\n", int_kilobytes(symbol)); + + /* hash value distribution */ + + memset(hash_dist, '\0', (HCNT + 1) * sizeof(size_t)); + for (i = 0; i < symbol->array_size; i++) { + bucket_cnt = 0; + for (b = symbol->buckets[i]; b != NULL; b = b->ainext) + bucket_cnt += b->aicount; + if (bucket_cnt >= HCNT) + bucket_cnt = HCNT; + hash_dist[bucket_cnt]++; + } + + indent(indent_level); + fprintf(output_fp, "Hash distribution:\n"); + indent_level++; + for (j = 0; j <= HCNT; j++) { + if (hash_dist[j] > 0) { + indent(indent_level); + if (j == HCNT) + fprintf(output_fp, "[>=%lu]:%lu\n", + (unsigned long) HCNT, (unsigned long) hash_dist[j]); + else + fprintf(output_fp, "[%lu]:%lu\n", + (unsigned long) j, (unsigned long) hash_dist[j]); + } + } + indent_level--; + + /* dump elements */ + + if (ndump->adepth >= 0) { + NODE *subs; + const char *aname; + + fprintf(output_fp, "\n"); + + aname = make_aname(symbol); + subs = make_number((AWKNUM) 0); + subs->flags |= (INTIND|NUMINT); + + for (i = 0; i < symbol->array_size; i++) { + for (b = symbol->buckets[i]; b != NULL; b = b->ainext) { + for (j = 0; j < b->aicount; j++) { + subs->numbr = b->ainum[j]; + assoc_info(subs, b->aivalue[j], ndump, aname); + } + } + } + unref(subs); + } + + if (xn != NULL) { + fprintf(output_fp, "\n"); + xn->adump(xn, ndump); + } + + return NULL; + +#undef HCNT +} + + +/* int_hash --- calculate the hash function of the integer subs */ + +static uint32_t +int_hash(uint32_t k, uint32_t hsize) +{ + +/* Code snippet copied from: + * Hash functions (http://www.azillionmonkeys.com/qed/hash.html). + * Copyright 2004-2008 by Paul Hsieh. Licenced under LGPL 2.1. + */ + + /* This is the final mixing function used by Paul Hsieh + * in SuperFastHash. + */ + + k ^= k << 3; + k += k >> 5; + k ^= k << 4; + k += k >> 17; + k ^= k << 25; + k += k >> 6; + + if (k >= hsize) + k %= hsize; + return k; +} + +/* int_find --- locate symbol[subs] */ + +static inline NODE ** +int_find(NODE *symbol, long k, uint32_t hash1) +{ + BUCKET *b; + int i; + + assert(symbol->buckets != NULL); + for (b = symbol->buckets[hash1]; b != NULL; b = b->ainext) { + for (i = 0; i < b->aicount; i++) { + if (b->ainum[i] == k) + return (b->aivalue + i); + } + } + return NULL; +} + + +/* int_insert --- install subs in the assoc array */ + +static NODE ** +int_insert(NODE *symbol, long k, uint32_t hash1) +{ + BUCKET *b; + int i; + + b = symbol->buckets[hash1]; + + /* Only the first bucket in the chain can be partially full, + * but is never empty. + */ + + if (b == NULL || (i = b->aicount) == 2) { + getbucket(b); + b->aicount = 0; + b->ainext = symbol->buckets[hash1]; + symbol->buckets[hash1] = b; + i = 0; + } + + b->ainum[i] = k; + b->aivalue[i] = dupnode(Nnull_string); + b->aicount++; + return & b->aivalue[i]; +} + + +/* grow_int_table --- grow the hash table */ + +static void +grow_int_table(NODE *symbol) +{ + BUCKET **old, **new; + BUCKET *chain, *next; + int i, j; + unsigned long oldsize, newsize, k; + + /* + * This is an array of primes. We grow the table by an order of + * magnitude each time (not just doubling) so that growing is a + * rare operation. We expect, on average, that it won't happen + * more than twice. The final size is also chosen to be small + * enough so that MS-DOG mallocs can handle it. When things are + * very large (> 8K), we just double more or less, instead of + * just jumping from 8K to 64K. + */ + + static const unsigned long sizes[] = { + 13, 127, 1021, 8191, 16381, 32749, 65497, + 131101, 262147, 524309, 1048583, 2097169, + 4194319, 8388617, 16777259, 33554467, + 67108879, 134217757, 268435459, 536870923, + 1073741827 + }; + + /* find next biggest hash size */ + newsize = oldsize = symbol->array_size; + + for (i = 0, j = sizeof(sizes)/sizeof(sizes[0]); i < j; i++) { + if (oldsize < sizes[i]) { + newsize = sizes[i]; + break; + } + } + if (newsize == oldsize) { /* table already at max (!) */ + symbol->flags |= ARRAYMAXED; + return; + } + + /* allocate new table */ + emalloc(new, BUCKET **, newsize * sizeof(BUCKET *), "grow_int_table"); + memset(new, '\0', newsize * sizeof(BUCKET *)); + + old = symbol->buckets; + symbol->buckets = new; + symbol->array_size = newsize; + + /* brand new hash table */ + if (old == NULL) + return; /* DO NOT initialize symbol->table_size */ + + /* old hash table there, move stuff to new, free old */ + /* note that symbol->table_size does not change if an old array. */ + + for (k = 0; k < oldsize; k++) { + long num; + for (chain = old[k]; chain != NULL; chain = next) { + for (i = 0; i < chain->aicount; i++) { + num = chain->ainum[i]; + *int_insert(symbol, num, int_hash(num, newsize)) = chain->aivalue[i]; + } + next = chain->ainext; + freebucket(chain); + } + } + efree(old); +} + + +#ifdef ARRAYDEBUG + +static NODE ** +int_option(NODE *opt, NODE *val) +{ + int newval; + NODE *tmp; + NODE **ret = (NODE **) ! NULL; + + tmp = force_string(opt); + (void) force_number(val); + if (strcmp(tmp->stptr, "INT_CHAIN_MAX") == 0) { + newval = (int) val->numbr; + if (newval > 0) + INT_CHAIN_MAX = newval; + } else + ret = NULL; + return ret; +} +#endif diff --git a/interpret.h b/interpret.h new file mode 100644 index 00000000..67a702e3 --- /dev/null +++ b/interpret.h @@ -0,0 +1,1187 @@ +/* + * interpret: + * code is a list of instructions to run. returns the exit value + * from the awk code. + */ + + /* N.B.: + * 1) reference counting done for both number and string values. + * 2) Stack operations: + * Use REPLACE[_XX] if last stack operation was TOP[_XX], + * PUSH[_XX] if last operation was POP[_XX] instead. + * 3) UPREF and DREF -- see awk.h + */ + +int +r_interpret(INSTRUCTION *code) +{ + INSTRUCTION *pc; /* current instruction */ + NODE *r = NULL; + NODE *m; + INSTRUCTION *ni; + NODE *t1, *t2; + NODE *f; /* function definition */ + NODE **lhs; + AWKNUM x, x1, x2; + int di; + Regexp *rp; + int stdio_problem = FALSE; + +/* array subscript */ +#define mk_sub(n) (n == 1 ? POP_SCALAR() : concat_exp(n, TRUE)) + +#ifdef DEBUGGING +#define JUMPTO(x) do { post_execute(pc); pc = (x); goto top; } while (FALSE) +#else +#define JUMPTO(x) do { pc = (x); goto top; } while (FALSE) +#endif + + pc = code; + + /* N.B.: always use JUMPTO for next instruction, otherwise bad things + * may happen. DO NOT add a real loop (for/while) below to + * replace ' forever {'; this catches failure to use JUMPTO to execute + * next instruction (e.g. continue statement). + */ + + /* loop until hit Op_stop instruction */ + + /* forever { */ +top: + if (pc->source_line > 0) + sourceline = pc->source_line; + +#ifdef DEBUGGING + if (! pre_execute(&pc)) + goto top; +#endif + + switch (pc->opcode) { + case Op_rule: + currule = pc->in_rule; /* for sole use in Op_K_next, Op_K_nextfile, Op_K_getline */ + /* fall through */ + case Op_func: + source = pc->source_file; + break; + + case Op_atexit: + /* avoid false source indications */ + source = NULL; + sourceline = 0; + (void) nextfile(& curfile, TRUE); /* close input data file */ + /* + * This used to be: + * + * if (close_io() != 0 && ! exiting && exit_val == 0) + * exit_val = 1; + * + * Other awks don't care about problems closing open files + * and pipes, in that it doesn't affect their exit status. + * So we no longer do either. + */ + (void) close_io(& stdio_problem); + /* + * However, we do want to exit non-zero if there was a problem + * with stdout/stderr, so we reinstate a slightly different + * version of the above: + */ + if (stdio_problem && ! exiting && exit_val == 0) + exit_val = 1; + break; + + case Op_stop: + return 0; + + case Op_push_i: + m = pc->memory; + if (! do_traditional && (m->flags & INTLSTR) != 0) { + char *orig, *trans, save; + + save = m->stptr[m->stlen]; + m->stptr[m->stlen] = '\0'; + orig = m->stptr; + trans = dgettext(TEXTDOMAIN, orig); + m->stptr[m->stlen] = save; + m = make_string(trans, strlen(trans)); + } else + UPREF(m); + PUSH(m); + break; + + case Op_push: + case Op_push_arg: + { + NODE *save_symbol; + int isparam = FALSE; + + save_symbol = m = pc->memory; + if (m->type == Node_param_list) { + isparam = TRUE; + save_symbol = m = GET_PARAM(m->param_cnt); + if (m->type == Node_array_ref) + m = m->orig_array; + } + + switch (m->type) { + case Node_var: + if (do_lint && var_uninitialized(m)) + lintwarn(isparam ? + _("reference to uninitialized argument `%s'") : + _("reference to uninitialized variable `%s'"), + save_symbol->vname); + m = m->var_value; + UPREF(m); + PUSH(m); + break; + + case Node_var_new: + m->type = Node_var; + m->var_value = dupnode(Nnull_string); + if (do_lint) + lintwarn(isparam ? + _("reference to uninitialized argument `%s'") : + _("reference to uninitialized variable `%s'"), + save_symbol->vname); + m = dupnode(Nnull_string); + PUSH(m); + break; + + case Node_var_array: + if (pc->opcode == Op_push_arg) + PUSH(m); + else + fatal(_("attempt to use array `%s' in a scalar context"), + array_vname(save_symbol)); + break; + + default: + cant_happen(); + } + } + break; + + case Op_push_param: /* function argument */ + m = pc->memory; + if (m->type == Node_param_list) + m = GET_PARAM(m->param_cnt); + if (m->type == Node_var) { + m = m->var_value; + UPREF(m); + PUSH(m); + break; + } + /* else + fall through */ + case Op_push_array: + PUSH(pc->memory); + break; + + case Op_push_lhs: + lhs = get_lhs(pc->memory, pc->do_reference); + PUSH_ADDRESS(lhs); + break; + + case Op_subscript: + t2 = mk_sub(pc->sub_count); + t1 = POP_ARRAY(); + + if (do_lint && in_array(t1, t2) == NULL) { + t2 = force_string(t2); + lintwarn(_("reference to uninitialized element `%s[\"%.*s\"]'"), + array_vname(t1), (int) t2->stlen, t2->stptr); + if (t2->stlen == 0) + lintwarn(_("subscript of array `%s' is null string"), array_vname(t1)); + } + + r = *assoc_lookup(t1, t2); + DEREF(t2); + if (r->type == Node_val) + UPREF(r); + PUSH(r); + break; + + case Op_sub_array: + t2 = mk_sub(pc->sub_count); + t1 = POP_ARRAY(); + r = in_array(t1, t2); + if (r == NULL) { + r = make_array(); + r->parent_array = t1; + *assoc_lookup(t1, t2) = r; + t2 = force_string(t2); + r->vname = estrdup(t2->stptr, t2->stlen); /* the subscript in parent array */ + } else if (r->type != Node_var_array) { + t2 = force_string(t2); + fatal(_("attempt to use scalar `%s[\"%.*s\"]' as an array"), + array_vname(t1), (int) t2->stlen, t2->stptr); + } + + DEREF(t2); + PUSH(r); + break; + + case Op_subscript_lhs: + t2 = mk_sub(pc->sub_count); + t1 = POP_ARRAY(); + if (do_lint && in_array(t1, t2) == NULL) { + t2 = force_string(t2); + if (pc->do_reference) + lintwarn(_("reference to uninitialized element `%s[\"%.*s\"]'"), + array_vname(t1), (int) t2->stlen, t2->stptr); + if (t2->stlen == 0) + lintwarn(_("subscript of array `%s' is null string"), array_vname(t1)); + } + + lhs = assoc_lookup(t1, t2); + if ((*lhs)->type == Node_var_array) { + t2 = force_string(t2); + fatal(_("attempt to use array `%s[\"%.*s\"]' in a scalar context"), + array_vname(t1), (int) t2->stlen, t2->stptr); + } + + DEREF(t2); + PUSH_ADDRESS(lhs); + break; + + case Op_field_spec: + t1 = TOP_SCALAR(); + lhs = r_get_field(t1, (Func_ptr *) 0, TRUE); + decr_sp(); + DEREF(t1); + r = dupnode(*lhs); /* can't use UPREF here */ + PUSH(r); + break; + + case Op_field_spec_lhs: + t1 = TOP_SCALAR(); + lhs = r_get_field(t1, &pc->target_assign->field_assign, pc->do_reference); + decr_sp(); + DEREF(t1); + PUSH_ADDRESS(lhs); + break; + + case Op_lint: + if (do_lint) { + switch (pc->lint_type) { + case LINT_assign_in_cond: + lintwarn(_("assignment used in conditional context")); + break; + + case LINT_no_effect: + lintwarn(_("statement has no effect")); + break; + + default: + cant_happen(); + } + } + break; + + case Op_K_break: + case Op_K_continue: + case Op_jmp: + JUMPTO(pc->target_jmp); + + case Op_jmp_false: + r = POP_SCALAR(); + di = eval_condition(r); + DEREF(r); + if (! di) + JUMPTO(pc->target_jmp); + break; + + case Op_jmp_true: + r = POP_SCALAR(); + di = eval_condition(r); + DEREF(r); + if (di) + JUMPTO(pc->target_jmp); + break; + + case Op_and: + case Op_or: + t1 = POP_SCALAR(); + di = eval_condition(t1); + DEREF(t1); + if ((pc->opcode == Op_and && di) + || (pc->opcode == Op_or && ! di)) + break; + r = node_Boolean[di]; + UPREF(r); + PUSH(r); + ni = pc->target_jmp; + JUMPTO(ni->nexti); + + case Op_and_final: + case Op_or_final: + t1 = TOP_SCALAR(); + r = node_Boolean[eval_condition(t1)]; + DEREF(t1); + UPREF(r); + REPLACE(r); + break; + + case Op_not: + t1 = TOP_SCALAR(); + r = node_Boolean[! eval_condition(t1)]; + DEREF(t1); + UPREF(r); + REPLACE(r); + break; + + case Op_equal: + r = node_Boolean[cmp_scalar() == 0]; + UPREF(r); + REPLACE(r); + break; + + case Op_notequal: + r = node_Boolean[cmp_scalar() != 0]; + UPREF(r); + REPLACE(r); + break; + + case Op_less: + r = node_Boolean[cmp_scalar() < 0]; + UPREF(r); + REPLACE(r); + break; + + case Op_greater: + r = node_Boolean[cmp_scalar() > 0]; + UPREF(r); + REPLACE(r); + break; + + case Op_leq: + r = node_Boolean[cmp_scalar() <= 0]; + UPREF(r); + REPLACE(r); + break; + + case Op_geq: + r = node_Boolean[cmp_scalar() >= 0]; + UPREF(r); + REPLACE(r); + break; + + case Op_plus_i: + x2 = force_number(pc->memory); + goto plus; + + case Op_plus: + POP_NUMBER(x2); +plus: + TOP_NUMBER(x1); + r = make_number(x1 + x2); + REPLACE(r); + break; + + case Op_minus_i: + x2 = force_number(pc->memory); + goto minus; + + case Op_minus: + POP_NUMBER(x2); +minus: + TOP_NUMBER(x1); + r = make_number(x1 - x2); + REPLACE(r); + break; + + case Op_times_i: + x2 = force_number(pc->memory); + goto times; + + case Op_times: + POP_NUMBER(x2); +times: + TOP_NUMBER(x1); + r = make_number(x1 * x2); + REPLACE(r); + break; + + case Op_exp_i: + x2 = force_number(pc->memory); + goto exponent; + + case Op_exp: + POP_NUMBER(x2); +exponent: + TOP_NUMBER(x1); + x = calc_exp(x1, x2); + r = make_number(x); + REPLACE(r); + break; + + case Op_quotient_i: + x2 = force_number(pc->memory); + goto quotient; + + case Op_quotient: + POP_NUMBER(x2); +quotient: + if (x2 == 0) + fatal(_("division by zero attempted")); + + TOP_NUMBER(x1); + x = x1 / x2; + r = make_number(x); + REPLACE(r); + break; + + case Op_mod_i: + x2 = force_number(pc->memory); + goto mod; + + case Op_mod: + POP_NUMBER(x2); +mod: + if (x2 == 0) + fatal(_("division by zero attempted in `%%'")); + + TOP_NUMBER(x1); +#ifdef HAVE_FMOD + x = fmod(x1, x2); +#else /* ! HAVE_FMOD */ + (void) modf(x1 / x2, &x); + x = x1 - x * x2; +#endif /* ! HAVE_FMOD */ + r = make_number(x); + REPLACE(r); + break; + + case Op_preincrement: + case Op_predecrement: + x2 = pc->opcode == Op_preincrement ? 1.0 : -1.0; + lhs = TOP_ADDRESS(); + t1 = *lhs; + x1 = force_number(t1); + if (t1->valref == 1 && t1->flags == (MALLOC|NUMCUR|NUMBER)) { + /* optimization */ + t1->numbr = x1 + x2; + } else { + unref(t1); + t1 = *lhs = make_number(x1 + x2); + } + UPREF(t1); + REPLACE(t1); + break; + + case Op_postincrement: + case Op_postdecrement: + x2 = pc->opcode == Op_postincrement ? 1.0 : -1.0; + lhs = TOP_ADDRESS(); + t1 = *lhs; + x1 = force_number(t1); + if (t1->valref == 1 && t1->flags == (MALLOC|NUMCUR|NUMBER)) { + /* optimization */ + t1->numbr = x1 + x2; + } else { + unref(t1); + *lhs = make_number(x1 + x2); + } + r = make_number(x1); + REPLACE(r); + break; + + case Op_unary_minus: + TOP_NUMBER(x1); + r = make_number(-x1); + REPLACE(r); + break; + + case Op_store_sub: + /* array[sub] assignment optimization, + * see awkgram.y (optimize_assignment) + */ + t1 = get_array(pc->memory, TRUE); /* array */ + t2 = mk_sub(pc->expr_count); /* subscript */ + lhs = assoc_lookup(t1, t2); + if ((*lhs)->type == Node_var_array) { + t2 = force_string(t2); + fatal(_("attempt to use array `%s[\"%.*s\"]' in a scalar context"), + array_vname(t1), (int) t2->stlen, t2->stptr); + } + DEREF(t2); + unref(*lhs); + *lhs = POP_SCALAR(); + break; + + case Op_store_var: + /* simple variable assignment optimization, + * see awkgram.y (optimize_assignment) + */ + + lhs = get_lhs(pc->memory, FALSE); + unref(*lhs); + r = pc->initval; /* constant initializer */ + if (r == NULL) + *lhs = POP_SCALAR(); + else { + UPREF(r); + *lhs = r; + } + break; + + case Op_store_field: + { + /* field assignment optimization, + * see awkgram.y (optimize_assignment) + */ + + Func_ptr assign; + t1 = TOP_SCALAR(); + lhs = r_get_field(t1, &assign, FALSE); + decr_sp(); + DEREF(t1); + unref(*lhs); + *lhs = POP_SCALAR(); + assert(assign != NULL); + assign(); + } + break; + + case Op_assign_concat: + /* x = x ... string concatenation optimization */ + lhs = get_lhs(pc->memory, FALSE); + t1 = force_string(*lhs); + t2 = POP_STRING(); + + free_wstr(*lhs); + + if (t1 != *lhs) { + unref(*lhs); + *lhs = dupnode(t1); + } + + if (t1 != t2 && t1->valref == 1) { + size_t nlen = t1->stlen + t2->stlen; + + erealloc(t1->stptr, char *, nlen + 2, "r_interpret"); + memcpy(t1->stptr + t1->stlen, t2->stptr, t2->stlen); + t1->stlen = nlen; + t1->stptr[nlen] = '\0'; + t1->flags &= ~(NUMCUR|NUMBER|NUMINT); + } else { + size_t nlen = t1->stlen + t2->stlen; + char *p; + + emalloc(p, char *, nlen + 2, "r_interpret"); + memcpy(p, t1->stptr, t1->stlen); + memcpy(p + t1->stlen, t2->stptr, t2->stlen); + unref(*lhs); + t1 = *lhs = make_str_node(p, nlen, ALREADY_MALLOCED); + } + DEREF(t2); + break; + + case Op_assign: + lhs = POP_ADDRESS(); + r = TOP_SCALAR(); + unref(*lhs); + *lhs = r; + UPREF(r); + REPLACE(r); + break; + + /* numeric assignments */ + case Op_assign_plus: + case Op_assign_minus: + case Op_assign_times: + case Op_assign_quotient: + case Op_assign_mod: + case Op_assign_exp: + op_assign(pc->opcode); + break; + + case Op_var_update: /* update value of NR, FNR or NF */ + pc->update_var(); + break; + + case Op_var_assign: + case Op_field_assign: + if (pc->assign_ctxt == Op_sub_builtin + && TOP()->numbr == 0.0 /* top of stack has a number == 0 */ + ) { + /* There wasn't any substitutions. If the target is a FIELD, + * this means no field re-splitting or $0 reconstruction. + * Skip the set_FOO routine if the target is a special variable. + */ + + break; + } else if ((pc->assign_ctxt == Op_K_getline + || pc->assign_ctxt == Op_K_getline_redir) + && TOP()->numbr <= 0.0 /* top of stack has a number <= 0 */ + ) { + /* getline returned EOF or error */ + + break; + } + + if (pc->opcode == Op_var_assign) + pc->assign_var(); + else + pc->field_assign(); + break; + + case Op_concat: + r = concat_exp(pc->expr_count, pc->concat_flag & CSUBSEP); + PUSH(r); + break; + + case Op_K_case: + if ((pc + 1)->match_exp) { + /* match a constant regex against switch expression instead of $0. */ + + m = POP(); /* regex */ + t2 = TOP_SCALAR(); /* switch expression */ + t2 = force_string(t2); + rp = re_update(m); + di = (research(rp, t2->stptr, 0, t2->stlen, + avoid_dfa(m, t2->stptr, t2->stlen)) >= 0); + } else { + t1 = POP_SCALAR(); /* case value */ + t2 = TOP_SCALAR(); /* switch expression */ + di = (cmp_nodes(t2, t1) == 0); + DEREF(t1); + } + + if (di) { + /* match found */ + + t2 = POP_SCALAR(); + DEREF(t2); + JUMPTO(pc->target_jmp); + } + break; + + case Op_K_delete: + t1 = POP_ARRAY(); + do_delete(t1, pc->expr_count); + stack_adj(-pc->expr_count); + break; + + case Op_K_delete_loop: + t1 = POP_ARRAY(); + lhs = POP_ADDRESS(); /* item */ + do_delete_loop(t1, lhs); + break; + + case Op_in_array: + t1 = POP_ARRAY(); + t2 = mk_sub(pc->expr_count); + di = (in_array(t1, t2) != NULL); + DEREF(t2); + PUSH(make_number((AWKNUM) di)); + break; + + case Op_arrayfor_init: + { + NODE **list = NULL; + NODE *array, *sort_str; + size_t num_elems = 0; + static NODE *sorted_in = NULL; + const char *how_to_sort = "@unsorted"; + + /* get the array */ + array = POP_ARRAY(); + + /* sanity: check if empty */ + if (array_empty(array)) + goto arrayfor; + + num_elems = array->table_size; + + if (sorted_in == NULL) /* do this once */ + sorted_in = make_string("sorted_in", 9); + + sort_str = NULL; + /* + * If posix, or if there's no PROCINFO[], + * there's no ["sorted_in"], so no sorting + */ + if (! do_posix && PROCINFO_node != NULL) + sort_str = in_array(PROCINFO_node, sorted_in); + + if (sort_str != NULL) { + sort_str = force_string(sort_str); + if (sort_str->stlen > 0) + how_to_sort = sort_str->stptr; + } + + list = assoc_list(array, how_to_sort, SORTED_IN); + +arrayfor: + getnode(r); + r->type = Node_arrayfor; + r->for_list = list; + r->for_list_size = num_elems; /* # of elements in list */ + r->cur_idx = -1; /* current index */ + r->for_array = array; /* array */ + PUSH(r); + + if (num_elems == 0) + JUMPTO(pc->target_jmp); /* Op_arrayfor_final */ + } + break; + + case Op_arrayfor_incr: + r = TOP(); /* Node_arrayfor */ + if (++r->cur_idx == r->for_list_size) { + NODE *array; + array = r->for_array; /* actual array */ + if (do_lint && array->table_size != r->for_list_size) + lintwarn(_("for loop: array `%s' changed size from %ld to %ld during loop execution"), + array_vname(array), (long) r->for_list_size, (long) array->table_size); + JUMPTO(pc->target_jmp); /* Op_arrayfor_final */ + } + + t1 = r->for_list[r->cur_idx]; + lhs = get_lhs(pc->array_var, FALSE); + unref(*lhs); + *lhs = dupnode(t1); + break; + + case Op_arrayfor_final: + r = POP(); + assert(r->type == Node_arrayfor); + free_arrayfor(r); + break; + + case Op_builtin: + r = pc->builtin(pc->expr_count); + PUSH(r); + break; + + case Op_ext_builtin: + { + int arg_count = pc->expr_count; + + PUSH_CODE(pc); + r = pc->builtin(arg_count); + (void) POP_CODE(); + while (arg_count-- > 0) { + t1 = POP(); + if (t1->type == Node_val) + DEREF(t1); + } + PUSH(r); + } + break; + + case Op_sub_builtin: /* sub, gsub and gensub */ + r = do_sub(pc->expr_count, pc->sub_flags); + PUSH(r); + break; + + case Op_K_print: + do_print(pc->expr_count, pc->redir_type); + break; + + case Op_K_printf: + do_printf(pc->expr_count, pc->redir_type); + break; + + case Op_K_print_rec: + do_print_rec(pc->expr_count, pc->redir_type); + break; + + case Op_push_re: + m = pc->memory; + if (m->type == Node_dynregex) { + r = POP_STRING(); + unref(m->re_exp); + m->re_exp = r; + } + PUSH(m); + break; + + case Op_match_rec: + m = pc->memory; + t1 = *get_field(0, (Func_ptr *) 0); +match_re: + rp = re_update(m); + /* + * Any place where research() is called with a last parameter of + * zero, we need to use the avoid_dfa test. This appears here and + * in the code for Op_K_case. + * + * A new or improved dfa that distinguishes beginning/end of + * string from beginning/end of line will allow us to get rid of + * this hack. + * + * The avoid_dfa() function is in re.c; it is not very smart. + */ + + di = research(rp, t1->stptr, 0, t1->stlen, + avoid_dfa(m, t1->stptr, t1->stlen)); + di = (di == -1) ^ (pc->opcode != Op_nomatch); + if(pc->opcode != Op_match_rec) { + decr_sp(); + DEREF(t1); + } + r = node_Boolean[di]; + UPREF(r); + PUSH(r); + break; + + case Op_nomatch: + /* fall through */ + case Op_match: + m = pc->memory; + t1 = TOP_STRING(); + if (m->type == Node_dynregex) { + unref(m->re_exp); + m->re_exp = t1; + decr_sp(); + t1 = TOP_STRING(); + } + goto match_re; + break; + + case Op_indirect_func_call: + { + int arg_count; + + f = NULL; + arg_count = (pc + 1)->expr_count; + t1 = PEEK(arg_count); /* indirect var */ + assert(t1->type == Node_val); /* @a[1](p) not allowed in grammar */ + t1 = force_string(t1); + if (t1->stlen > 0) { + /* retrieve function definition node */ + f = pc->func_body; + if (f != NULL && strcmp(f->vname, t1->stptr) == 0) { + /* indirect var hasn't been reassigned */ + + goto func_call; + } + f = lookup(t1->stptr); + } + + if (f == NULL || f->type != Node_func) + fatal(_("function called indirectly through `%s' does not exist"), + pc->func_name); + pc->func_body = f; /* save for next call */ + + goto func_call; + } + + case Op_func_call: + /* retrieve function definition node */ + f = pc->func_body; + if (f == NULL) { + f = lookup(pc->func_name); + if (f == NULL || (f->type != Node_func && f->type != Node_ext_func)) + fatal(_("function `%s' not defined"), pc->func_name); + pc->func_body = f; /* save for next call */ + } + + if (f->type == Node_ext_func) { + INSTRUCTION *bc; + char *fname = pc->func_name; + int arg_count = (pc + 1)->expr_count; + + bc = f->code_ptr; + assert(bc->opcode == Op_symbol); + pc->opcode = Op_ext_builtin; /* self modifying code */ + pc->builtin = bc->builtin; + pc->expr_count = arg_count; /* actual argument count */ + (pc + 1)->func_name = fname; /* name of the builtin */ + (pc + 1)->expr_count = bc->expr_count; /* defined max # of arguments */ + ni = pc; + JUMPTO(ni); + } + +func_call: + ni = setup_frame(pc); + + /* run the function instructions */ + JUMPTO(ni); /* Op_func */ + + case Op_K_return: + m = POP_SCALAR(); /* return value */ + + ni = pop_fcall(); + + /* put the return value back on stack */ + PUSH(m); + + JUMPTO(ni); + + case Op_K_getline_redir: + if ((currule == BEGINFILE || currule == ENDFILE) + && pc->into_var == FALSE + && pc->redir_type == redirect_input) + fatal(_("`getline' invalid inside `%s' rule"), ruletab[currule]); + r = do_getline_redir(pc->into_var, pc->redir_type); + PUSH(r); + break; + + case Op_K_getline: /* no redirection */ + if (! currule || currule == BEGINFILE || currule == ENDFILE) + fatal(_("non-redirected `getline' invalid inside `%s' rule"), + ruletab[currule]); + + do { + int ret; + ret = nextfile(& curfile, FALSE); + if (ret <= 0) + r = do_getline(pc->into_var, curfile); + else { + + /* Save execution state so that we can return to it + * from Op_after_beginfile or Op_after_endfile. + */ + + push_exec_state(pc, currule, source, stack_ptr); + + if (curfile == NULL) + JUMPTO((pc + 1)->target_endfile); + else + JUMPTO((pc + 1)->target_beginfile); + } + } while (r == NULL); /* EOF */ + + PUSH(r); + break; + + case Op_after_endfile: + /* Find the execution state to return to */ + ni = pop_exec_state(& currule, & source, NULL); + + assert(ni->opcode == Op_newfile || ni->opcode == Op_K_getline); + JUMPTO(ni); + + case Op_after_beginfile: + after_beginfile(& curfile); + + /* Find the execution state to return to */ + ni = pop_exec_state(& currule, & source, NULL); + + assert(ni->opcode == Op_newfile || ni->opcode == Op_K_getline); + if (ni->opcode == Op_K_getline + || curfile == NULL /* skipping directory argument */ + ) + JUMPTO(ni); + + break; /* read a record, Op_get_record */ + + case Op_newfile: + { + int ret; + + ret = nextfile(& curfile, FALSE); + + if (ret < 0) /* end of input */ + JUMPTO(pc->target_jmp); /* end block or Op_atexit */ + + if (ret == 0) /* read a record */ + JUMPTO((pc + 1)->target_get_record); + + /* ret > 0 */ + /* Save execution state for use in Op_after_beginfile or Op_after_endfile. */ + + push_exec_state(pc, currule, source, stack_ptr); + + if (curfile == NULL) /* EOF */ + JUMPTO(pc->target_endfile); + /* else + execute beginfile block */ + } + break; + + case Op_get_record: + { + int errcode = 0; + + ni = pc->target_newfile; + if (curfile == NULL) { + /* from non-redirected getline, e.g.: + * { + * while (getline > 0) ; + * } + */ + + ni = ni->target_jmp; /* end_block or Op_atexit */ + JUMPTO(ni); + } + + if (inrec(curfile, & errcode) != 0) { + if (errcode > 0 && (do_traditional || ! pc->has_endfile)) + fatal(_("error reading input file `%s': %s"), + curfile->name, strerror(errcode)); + + JUMPTO(ni); + } /* else + prog (rule) block */ + } + break; + + case Op_K_nextfile: + { + int ret; + + if (currule != Rule && currule != BEGINFILE) + fatal(_("`nextfile' cannot be called from a `%s' rule"), + ruletab[currule]); + + ret = nextfile(& curfile, TRUE); /* skip current file */ + + if (currule == BEGINFILE) { + long stack_size; + + ni = pop_exec_state(& currule, & source, & stack_size); + + assert(ni->opcode == Op_K_getline || ni->opcode == Op_newfile); + + /* pop stack returning to the state of Op_K_getline or Op_newfile. */ + unwind_stack(stack_size); + + if (ret == 0) { + /* There was an error opening the file; + * don't run ENDFILE block(s). + */ + + JUMPTO(ni); + } else { + /* do run ENDFILE block(s) first. */ + + /* Execution state to return to in Op_after_endfile. */ + push_exec_state(ni, currule, source, stack_ptr); + + JUMPTO(pc->target_endfile); + } + } /* else + Start over with the first rule. */ + + /* empty the run-time stack to avoid memory leak */ + pop_stack(); + + /* Push an execution state for Op_after_endfile to return to */ + push_exec_state(pc->target_newfile, currule, source, stack_ptr); + + JUMPTO(pc->target_endfile); + } + break; + + case Op_K_exit: + /* exit not allowed in user-defined comparison functions for "sorted_in"; + * This is done so that END blocks aren't executed more than once. + */ + if (! currule) + fatal(_("`exit' cannot be called in the current context")); + + exiting = TRUE; + POP_NUMBER(x1); + exit_val = (int) x1; +#ifdef VMS + if (exit_val == 0) + exit_val = EXIT_SUCCESS; + else if (exit_val == 1) + exit_val = EXIT_FAILURE; + /* else + just pass anything else on through */ +#endif + + if (currule == BEGINFILE || currule == ENDFILE) { + + /* Find the rule of the saved execution state (Op_K_getline/Op_newfile). + * This is needed to prevent multiple execution of any END rules: + * gawk 'BEGINFILE { exit(1) } \ + * END { while (getline > 0); }' in1 in2 + */ + + (void) pop_exec_state(& currule, & source, NULL); + } + + pop_stack(); /* empty stack, don't leak memory */ + + /* Jump to either the first END block instruction + * or to Op_atexit. + */ + + if (currule == END) + ni = pc->target_atexit; + else + ni = pc->target_end; + JUMPTO(ni); + + case Op_K_next: + if (currule != Rule) + fatal(_("`next' cannot be called from a `%s' rule"), ruletab[currule]); + + pop_stack(); + JUMPTO(pc->target_jmp); /* Op_get_record, read next record */ + + case Op_pop: + r = POP_SCALAR(); + DEREF(r); + break; + + case Op_line_range: + if (pc->triggered) /* evaluate right expression */ + JUMPTO(pc->target_jmp); + /* else + evaluate left expression */ + break; + + case Op_cond_pair: + { + int result; + INSTRUCTION *ip; + + t1 = TOP_SCALAR(); /* from right hand side expression */ + di = (eval_condition(t1) != 0); + DEREF(t1); + + ip = pc->line_range; /* Op_line_range */ + + if (! ip->triggered && di) { + /* not already triggered and left expression is TRUE */ + decr_sp(); + ip->triggered = TRUE; + JUMPTO(ip->target_jmp); /* evaluate right expression */ + } + + result = ip->triggered || di; + ip->triggered ^= di; /* update triggered flag */ + r = node_Boolean[result]; /* final value of condition pair */ + UPREF(r); + REPLACE(r); + JUMPTO(pc->target_jmp); + } + + case Op_exec_count: + if (do_profile) + pc->exec_count++; + break; + + case Op_no_op: + case Op_K_do: + case Op_K_while: + case Op_K_for: + case Op_K_arrayfor: + case Op_K_switch: + case Op_K_default: + case Op_K_if: + case Op_K_else: + case Op_cond_exp: + break; + + default: + fatal(_("Sorry, don't know how to interpret `%s'"), opcode2str(pc->opcode)); + } + + JUMPTO(pc->nexti); + +/* } forever */ + + /* not reached */ + return 0; + +#undef mk_sub +#undef JUMPTO +} + @@ -211,7 +211,7 @@ static int inetfile(const char *str, int *length, int *family); #endif static struct redirect *red_head = NULL; -static NODE *RS; +static NODE *RS = NULL; static Regexp *RS_re_yes_case; static Regexp *RS_re_no_case; static Regexp *RS_regexp; @@ -611,7 +611,7 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) if (do_lint && (redir_exp->flags & STRCUR) == 0) lintwarn(_("expression in `%s' redirection only has numeric value"), what); - redir_exp = force_string(redir_exp); + redir_exp= force_string(redir_exp); str = redir_exp->stptr; if (str == NULL || *str == '\0') @@ -2340,6 +2340,30 @@ init_awkpath(char *path) #undef INC_PATH } +/* get_cwd -- get current working directory */ + +static char * +get_cwd () +{ +#define BSIZE 100 + char *buf; + size_t bsize = BSIZE; + + emalloc(buf, char *, bsize * sizeof(char), "get_cwd"); + while (TRUE) { + if (getcwd(buf, bsize) == buf) + return buf; + if (errno != ERANGE) { + efree(buf); + return NULL; + } + bsize *= 2; + erealloc(buf, char *, bsize * sizeof(char), "get_cwd"); + } +#undef BSIZE +} + + /* do_find_source --- search $AWKPATH for file, return NULL if not found */ static char * @@ -2361,10 +2385,16 @@ do_find_source(const char *src, struct stat *stb, int *errcode) return NULL; } - /* try current directory before path search */ + /* try current directory before $AWKPATH search */ if (stat(src, stb) == 0) { - emalloc(path, char *, strlen(src) + 1, "do_find_source"); - strcpy(path, src); + path = get_cwd(); + if (path == NULL) { + *errcode = errno; + return NULL; + } + erealloc(path, char *, strlen(path) + strlen(src) + 2, "do_find_source"); + strcat(path, "/"); + strcat(path, src); return path; } @@ -2374,6 +2404,7 @@ do_find_source(const char *src, struct stat *stb, int *errcode) emalloc(path, char *, max_pathlen + strlen(src) + 1, "do_find_source"); for (i = 0; awkpath[i] != NULL; i++) { if (strcmp(awkpath[i], "./") == 0 || strcmp(awkpath[i], ".") == 0) { + /* FIXME: already tried CWD above; Why do it again ? */ *path = '\0'; } else strcpy(path, awkpath[i]); @@ -2391,7 +2422,7 @@ do_find_source(const char *src, struct stat *stb, int *errcode) /* find_source --- find source file with default file extension handling */ char * -find_source(const char *src, struct stat *stb, int *errcode) +find_source(const char *src, struct stat *stb, int *errcode, int is_extlib) { char *path; @@ -2400,10 +2431,36 @@ find_source(const char *src, struct stat *stb, int *errcode) return NULL; path = do_find_source(src, stb, errcode); + if (path == NULL && is_extlib) { + char *file_ext; + int save_errno; + size_t src_len; + size_t suffix_len; + +#define EXTLIB_SUFFIX ".so" + src_len = strlen(src); + suffix_len = strlen(EXTLIB_SUFFIX); + + /* check if already has the SUFFIX */ + if (src_len >= suffix_len && strcmp(& src[src_len - suffix_len], EXTLIB_SUFFIX) == 0) + return NULL; + + /* append EXTLIB_SUFFIX and try again */ + save_errno = errno; + emalloc(file_ext, char *, src_len + suffix_len + 1, "find_source"); + sprintf(file_ext, "%s%s", src, EXTLIB_SUFFIX); + path = do_find_source(file_ext, stb, errcode); + efree(file_ext); + if (path == NULL) + errno = save_errno; + return path; +#undef EXTLIB_SUFFIX + } + #ifdef DEFAULT_FILETYPE if (! do_traditional && path == NULL) { char *file_awk; - int save = errno; + int save_errno = errno; #ifdef VMS int vms_save = vaxc$errno; #endif @@ -2415,7 +2472,7 @@ find_source(const char *src, struct stat *stb, int *errcode) path = do_find_source(file_awk, stb, errcode); efree(file_awk); if (path == NULL) { - errno = save; + errno = save_errno; #ifdef VMS vaxc$errno = vms_save; #endif @@ -2426,6 +2483,7 @@ find_source(const char *src, struct stat *stb, int *errcode) return path; } + /* srcopen --- open source file */ int @@ -2530,7 +2588,7 @@ iop_alloc(int fd, const char *name, IOBUF *iop, int do_openhooks) #define set_RT_to_null() \ (void)(! do_traditional && (unref(RT_node->var_value), \ - RT_node->var_value = Nnull_string)) + RT_node->var_value = dupnode(Nnull_string))) #define set_RT(str, len) \ (void)(! do_traditional && (unref(RT_node->var_value), \ @@ -3146,8 +3204,7 @@ pty_vs_pipe(const char *command) #ifdef HAVE_TERMIOS_H char *full_index; size_t full_len; - NODE *val; - NODE *sub; + NODE *val, *sub; if (PROCINFO_node == NULL) return FALSE; @@ -58,6 +58,9 @@ static void init_groupset(void); static void save_argv(int, char **); +extern int debug_prog(INSTRUCTION *pc); /* debug.c */ + + /* These nodes store all the special variables AWK uses */ NODE *ARGC_node, *ARGIND_node, *ARGV_node, *BINMODE_node, *CONVFMT_node; NODE *ENVIRON_node, *ERRNO_node, *FIELDWIDTHS_node, *FILENAME_node; @@ -129,30 +132,18 @@ static int disallow_var_assigns = FALSE; /* true for --exec */ static void add_preassign(enum assign_type type, char *val); -#undef do_lint -#undef do_lint_old - -int do_traditional = FALSE; /* no gnu extensions, add traditional weirdnesses */ -int do_posix = FALSE; /* turn off gnu and unix extensions */ -int do_lint = FALSE; /* provide warnings about questionable stuff */ -int do_lint_old = FALSE; /* warn about stuff not in V7 awk */ -int do_intl = FALSE; /* dump locale-izable strings to stdout */ -int do_non_decimal_data = FALSE; /* allow octal/hex C style DATA. Use with caution! */ -int do_nostalgia = FALSE; /* provide a blast from the past */ -int do_intervals = FALSE; /* allow {...,...} in regexps, see resetup() */ -int do_profiling = FALSE; /* profile and pretty print the program */ -int do_dump_vars = FALSE; /* dump all global variables at end */ -int do_tidy_mem = FALSE; /* release vars when done */ -int do_optimize = TRUE; /* apply default optimizations */ -int do_binary = FALSE; /* hands off my data! */ -int do_sandbox = FALSE; /* sandbox mode - disable 'system' function & redirections */ +int do_flags = FALSE; +int do_optimize = TRUE; /* apply default optimizations */ +static int do_nostalgia = FALSE; /* provide a blast from the past */ +static int do_binary = FALSE; /* hands off my data! */ + int use_lc_numeric = FALSE; /* obey locale for decimal point */ #if MBS_SUPPORT int gawk_mb_cur_max; /* MB_CUR_MAX value, see comment in main() */ #endif -FILE *output_fp; /* default output for debugger */ +FILE *output_fp; /* default gawk output, can be redirected in the debugger */ int output_is_tty = FALSE; /* control flushing of output */ /* default format for strftime(), available via PROCINFO */ @@ -167,26 +158,24 @@ int ngroups; /* size of said set */ void (*lintfunc)(const char *mesg, ...) = warning; -/* - * Note: reserve -D for future use, to merge dgawk into gawk. - * Note: reserve -l for future use, for xgawk's -l option. - */ static const struct option optab[] = { - { "traditional", no_argument, & do_traditional, 1 }, + { "traditional", no_argument, NULL, 'c' }, { "lint", optional_argument, NULL, 'L' }, - { "lint-old", no_argument, & do_lint_old, 1 }, - { "optimize", no_argument, & do_optimize, 'O' }, - { "posix", no_argument, & do_posix, 1 }, - { "command", required_argument, NULL, 'R' }, + { "lint-old", no_argument, NULL, 't' }, + { "optimize", no_argument, NULL, 'O' }, + { "posix", no_argument, NULL, 'P' }, { "nostalgia", no_argument, & do_nostalgia, 1 }, - { "gen-pot", no_argument, & do_intl, 1 }, - { "non-decimal-data", no_argument, & do_non_decimal_data, 1 }, + { "gen-pot", no_argument, NULL, 'g' }, + { "non-decimal-data", no_argument, NULL, 'n' }, + { "pretty-print", optional_argument, NULL, 'o' }, { "profile", optional_argument, NULL, 'p' }, + { "debug", optional_argument, NULL, 'D' }, { "copyright", no_argument, NULL, 'C' }, { "field-separator", required_argument, NULL, 'F' }, { "file", required_argument, NULL, 'f' }, - { "re-interval", no_argument, & do_intervals, 1 }, + { "re-interval", no_argument, NULL, 'r' }, { "source", required_argument, NULL, 'e' }, + { "load", required_argument, NULL, 'l' }, { "dump-variables", optional_argument, NULL, 'd' }, { "assign", required_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, @@ -194,17 +183,13 @@ static const struct option optab[] = { { "exec", required_argument, NULL, 'E' }, { "use-lc-numeric", no_argument, & use_lc_numeric, 1 }, { "characters-as-bytes", no_argument, & do_binary, 'b' }, - { "sandbox", no_argument, & do_sandbox, 1 }, + { "sandbox", no_argument, NULL, 'S' }, #if defined(YYDEBUG) || defined(GAWKDEBUG) { "parsedebug", no_argument, NULL, 'Y' }, #endif { NULL, 0, NULL, '\0' } }; -#ifdef NO_LINT -#define do_lint 0 -#define do_lint_old 0 -#endif /* main --- process args, parse program, run it, clean up */ @@ -213,19 +198,21 @@ main(int argc, char **argv) { /* * The + on the front tells GNU getopt not to rearrange argv. - * Note: reserve -D for future use, to merge dgawk into gawk. * Note: reserve -l for future use, for xgawk's -l option. */ - const char *optlist = "+F:f:v:W;m:bcCd::e:E:gh:L:nNOp::PrR:StVY"; + const char *optlist = "+F:f:v:W;m:bcCd::D::e:E:gh:l:L:nNo::Op::PrStVY"; int stopped_early = FALSE; int old_optind; int i; int c; char *scan, *src; char *extra_stack; + int have_srcfile = FALSE; + SRCFILE *s; /* do these checks early */ - do_tidy_mem = (getenv("TIDYMEM") != NULL); + if (getenv("TIDYMEM") != NULL) + do_flags |= DO_TIDY_MEM; #ifdef HAVE_MCHECK_H #ifdef HAVE_MTRACE @@ -371,7 +358,7 @@ main(int argc, char **argv) break; case 'c': - do_traditional = TRUE; + do_flags |= DO_TRADITIONAL; break; case 'C': @@ -379,11 +366,17 @@ main(int argc, char **argv) break; case 'd': - do_dump_vars = TRUE; + do_flags |= DO_DUMP_VARS; if (optarg != NULL && optarg[0] != '\0') varfile = optarg; break; + case 'D': + do_flags |= DO_DEBUG; + if (optarg != NULL && optarg[0] != '\0') + command_file = optarg; + break; + case 'e': if (optarg[0] == '\0') warning(_("empty argument to `-e/--source' ignored")); @@ -392,7 +385,7 @@ main(int argc, char **argv) break; case 'g': - do_intl = TRUE; + do_flags |= DO_INTL; break; case 'h': @@ -400,19 +393,25 @@ main(int argc, char **argv) usage(EXIT_SUCCESS, stdout); break; -#ifndef NO_LINT + case 'l': + (void) add_srcfile(SRC_EXTLIB, optarg, srcfiles, NULL, NULL); + break; + case 'L': - do_lint = LINT_ALL; +#ifndef NO_LINT + do_flags |= DO_LINT_ALL; if (optarg != NULL) { if (strcmp(optarg, "fatal") == 0) lintfunc = r_fatal; - else if (strcmp(optarg, "invalid") == 0) - do_lint = LINT_INVALID; + else if (strcmp(optarg, "invalid") == 0) { + do_flags &= ~DO_LINT_ALL; + do_flags |= DO_LINT_INVALID; + } } break; case 't': - do_lint_old = TRUE; + do_flags |= DO_LINT_OLD; break; #else case 'L': @@ -421,7 +420,7 @@ main(int argc, char **argv) #endif case 'n': - do_non_decimal_data = TRUE; + do_flags |= DO_NON_DEC_DATA; break; case 'N': @@ -433,7 +432,10 @@ main(int argc, char **argv) break; case 'p': - do_profiling = TRUE; + do_flags |= DO_PROFILE; + /* fall through */ + case 'o': + do_flags |= DO_PRETTY_PRINT; if (optarg != NULL) set_prof_file(optarg); else @@ -441,15 +443,15 @@ main(int argc, char **argv) break; case 'P': - do_posix = TRUE; + do_flags |= DO_POSIX; break; case 'r': - do_intervals = TRUE; + do_flags |= DO_INTERVALS; break; case 'S': - do_sandbox = TRUE; + do_flags |= DO_SANDBOX; break; case 'V': @@ -470,20 +472,13 @@ main(int argc, char **argv) break; case 'Y': - case 'R': #if defined(YYDEBUG) || defined(GAWKDEBUG) if (c == 'Y') { yydebug = 2; break; } #endif - if (c == 'R' && which_gawk == exe_debugging) { - if (optarg[0] != '\0') - command_file = optarg; - break; - } - /* if not debugging or dgawk, fall through */ - + /* if not debugging, fall through */ case '?': default: /* @@ -528,7 +523,7 @@ out: /* check for POSIXLY_CORRECT environment variable */ if (! do_posix && getenv("POSIXLY_CORRECT") != NULL) { - do_posix = TRUE; + do_flags |= DO_POSIX; if (do_lint) lintwarn( _("environment variable `POSIXLY_CORRECT' set: turning on `--posix'")); @@ -539,7 +534,7 @@ out: if (do_traditional) /* both on command line */ warning(_("`--posix' overrides `--traditional'")); else - do_traditional = TRUE; + do_flags |= DO_TRADITIONAL; /* * POSIX compliance also implies * no GNU extensions either. @@ -547,7 +542,7 @@ out: } if (do_traditional && do_non_decimal_data) { - do_non_decimal_data = FALSE; + do_flags &= ~DO_NON_DEC_DATA; warning(_("`--posix'/`--traditional' overrides `--non-decimal-data'")); } @@ -563,21 +558,13 @@ out: } #endif - /* - * Force profiling if this is pgawk. - * Don't bother if the command line already set profiling up. - */ - if (! do_profiling) - init_profiling(& do_profiling, DEFAULT_PROFILE); - /* load group set */ init_groupset(); /* initialize the null string */ Nnull_string = make_string("", 0); Nnull_string->numbr = 0.0; - Nnull_string->type = Node_val; - Nnull_string->flags = (PERM|STRCUR|STRING|NUMCUR|NUMBER); + Nnull_string->flags = (MALLOC|STRCUR|STRING|NUMCUR|NUMBER); /* * Tell the regex routines how they should work. @@ -586,7 +573,7 @@ out: */ resetup(); - (void) grow_stack(); + init_interpret(); /* Set up the special variables */ init_vars(); @@ -621,8 +608,17 @@ out: #endif if (os_isatty(fileno(stdout))) output_is_tty = TRUE; + + /* load extension libs */ + for (s = srcfiles->next; s != srcfiles; s = s->next) { + if (s->stype == SRC_EXTLIB) + (void) load_ext(s->fullpath, "dlload", NULL); + else + have_srcfile++; + } + /* No -f or --source options, use next arg */ - if (srcfiles->next == srcfiles) { + if (! have_srcfile) { if (optind > argc - 1 || stopped_early) /* no args left or no program */ usage(EXIT_FAILURE, stderr); (void) add_srcfile(SRC_CMDLINE, argv[optind], srcfiles, NULL, NULL); @@ -642,7 +638,7 @@ out: setlocale(LC_NUMERIC, "C"); #endif /* Read in the program */ - if (parse_program(&code_block) != 0) + if (parse_program(& code_block) != 0) exit(EXIT_FAILURE); if (do_intl) @@ -654,7 +650,8 @@ out: if (do_lint && code_block->nexti->opcode == Op_atexit) lintwarn(_("no program text at all!")); - init_profiling_signals(); + if (do_profile) + init_profiling_signals(); #if defined(LC_NUMERIC) /* @@ -677,9 +674,12 @@ out: #endif output_fp = stdout; - interpret(code_block); + if (do_debug) + debug_prog(code_block); + else + interpret(code_block); - if (do_profiling) { + if (do_pretty_print) { dump_prog(code_block); dump_funcs(); } @@ -733,7 +733,7 @@ usage(int exitval, FILE *fp) fprintf(fp, _("Usage: %s [POSIX or GNU style options] -f progfile [--] file ...\n"), myname); fprintf(fp, _("Usage: %s [POSIX or GNU style options] [--] %cprogram%c file ...\n"), - myname, quote, quote); + myname, quote, quote); /* GNU long options info. This is too many options. */ @@ -746,19 +746,20 @@ usage(int exitval, FILE *fp) fputs(_("\t-c\t\t\t--traditional\n"), fp); fputs(_("\t-C\t\t\t--copyright\n"), fp); fputs(_("\t-d[file]\t\t--dump-variables[=file]\n"), fp); + fputs(_("\t-D[file]\t\t--debug[=file]\n"), fp); fputs(_("\t-e 'program-text'\t--source='program-text'\n"), fp); fputs(_("\t-E file\t\t\t--exec=file\n"), fp); fputs(_("\t-g\t\t\t--gen-pot\n"), fp); fputs(_("\t-h\t\t\t--help\n"), fp); + fputs(_("\t-l library\t\t--load=library\n"), fp); fputs(_("\t-L [fatal]\t\t--lint[=fatal]\n"), fp); fputs(_("\t-n\t\t\t--non-decimal-data\n"), fp); fputs(_("\t-N\t\t\t--use-lc-numeric\n"), fp); + fputs(_("\t-o[file]\t\t--pretty-print[=file]\n"), fp); fputs(_("\t-O\t\t\t--optimize\n"), fp); fputs(_("\t-p[file]\t\t--profile[=file]\n"), fp); fputs(_("\t-P\t\t\t--posix\n"), fp); fputs(_("\t-r\t\t\t--re-interval\n"), fp); - if (which_gawk == exe_debugging) - fputs(_("\t-R file\t\t\t--command=file\n"), fp); fputs(_("\t-S\t\t\t--sandbox\n"), fp); fputs(_("\t-t\t\t\t--lint-old\n"), fp); fputs(_("\t-V\t\t\t--version\n"), fp); @@ -856,6 +857,7 @@ cmdline_fs(char *str) if (do_traditional && ! do_posix) str[0] = '\t'; } + *tmp = make_str_node(str, strlen(str), SCAN); /* do process escapes */ set_FS(); } @@ -869,26 +871,27 @@ init_args(int argc0, int argc, const char *argv0, char **argv) NODE **aptr; NODE *tmp; - ARGV_node = install_symbol(estrdup("ARGV", 4), mk_symbol(Node_var_array, (NODE *) NULL)); + ARGV_node = install_symbol(estrdup("ARGV", 4), Node_var_array); tmp = make_number(0.0); - aptr = assoc_lookup(ARGV_node, tmp, FALSE); + aptr = assoc_lookup(ARGV_node, tmp); unref(tmp); unref(*aptr); *aptr = make_string(argv0, strlen(argv0)); (*aptr)->flags |= MAYBE_NUM; for (i = argc0, j = 1; i < argc; i++, j++) { tmp = make_number((AWKNUM) j); - aptr = assoc_lookup(ARGV_node, tmp, FALSE); + aptr = assoc_lookup(ARGV_node, tmp); unref(tmp); unref(*aptr); *aptr = make_string(argv[i], strlen(argv[i])); (*aptr)->flags |= MAYBE_NUM; } - ARGC_node = install_symbol(estrdup("ARGC", 4), - mk_symbol(Node_var, make_number((AWKNUM) j))); + ARGC_node = install_symbol(estrdup("ARGC", 4), Node_var); + ARGC_node->var_value = make_number((AWKNUM) j); } + /* * Set all the special variables to their initial values. * Note that some of the variables that have set_FOO routines should @@ -951,13 +954,11 @@ init_vars() for (vp = varinit; vp->name != NULL; vp++) { if ((vp->flags & NO_INSTALL) != 0) continue; - n = mk_symbol(Node_var, vp->strval == NULL - ? make_number(vp->numval) - : make_string(vp->strval, strlen(vp->strval))); + n = *(vp->spec) = install_symbol(estrdup(vp->name, strlen(vp->name)), Node_var); + n->var_value = vp->strval == NULL ? make_number(vp->numval) + : make_string(vp->strval, strlen(vp->strval)); n->var_assign = (Func_ptr) vp->assign; n->var_update = (Func_ptr) vp->update; - - *(vp->spec) = install_symbol(estrdup(vp->name, strlen(vp->name)), n); if (vp->do_assign) (*(vp->assign))(); } @@ -981,9 +982,7 @@ load_environ() int i; NODE *tmp; - ENVIRON_node = install_symbol(estrdup("ENVIRON", 7), - mk_symbol(Node_var_array, (NODE *) NULL)); - + ENVIRON_node = install_symbol(estrdup("ENVIRON", 7), Node_var_array); for (i = 0; environ[i] != NULL; i++) { static char nullstr[] = ""; @@ -994,7 +993,7 @@ load_environ() else val = nullstr; tmp = make_string(var, strlen(var)); - aptr = assoc_lookup(ENVIRON_node, tmp, FALSE); + aptr = assoc_lookup(ENVIRON_node, tmp); unref(tmp); unref(*aptr); *aptr = make_string(val, strlen(val)); @@ -1017,7 +1016,7 @@ load_environ() val = getenv("AWKPATH"); if (val == NULL) val = defpath; - aptr = assoc_lookup(ENVIRON_node, tmp, FALSE); + aptr = assoc_lookup(ENVIRON_node, tmp); unref(*aptr); *aptr = make_string(val, strlen(val)); } @@ -1036,8 +1035,7 @@ load_procinfo() #endif AWKNUM value; - PROCINFO_node = install_symbol(estrdup("PROCINFO", 8), - mk_symbol(Node_var_array, (NODE *) NULL)); + PROCINFO_node = install_symbol(estrdup("PROCINFO", 8), Node_var_array); update_PROCINFO_str("version", VERSION); update_PROCINFO_str("strftime", def_strftime_format); @@ -1233,7 +1231,7 @@ arg_assign(char *arg, int initing) cp2 = estrdup(arg, cp - arg); /* var name */ - var = variable(cp2, Node_var); + var = variable(0, cp2, Node_var); if (var == NULL) /* error */ exit(EXIT_FATAL); if (var->type == Node_var && var->var_update) @@ -1457,3 +1455,18 @@ update_global_values() vp->update(); } } + +/* getenv_long --- read a long value (>= 0) from an environment var. */ + +long +getenv_long(const char *name) +{ + const char *val; + long newval; + if ((val = getenv(name)) != NULL && isdigit((unsigned char) *val)) { + for (newval = 0; *val && isdigit((unsigned char) *val); val++) + newval = (newval * 10) + *val - '0'; + return newval; + } + return -1; +} @@ -46,8 +46,6 @@ err(const char *s, const char *emsg, va_list argp) (void) fflush(output_fp); me = myname; - if (strncmp(me, "dgawk", 5) == 0) - me = &myname[1]; (void) fprintf(stderr, "%s: ", me); #ifdef GAWKDEBUG if (srcfile != NULL) { @@ -29,6 +29,8 @@ static int is_ieee_magic_val(const char *val); static AWKNUM get_ieee_magic_val(const char *val); +extern NODE **fmt_list; /* declared in eval.c */ + /* force_number --- force a value to be numeric */ @@ -104,6 +106,8 @@ r_force_number(NODE *n) n->numbr = (AWKNUM)(*cp - '0'); n->flags |= newflags; n->flags |= NUMCUR; + if (cp == n->stptr) /* no leading spaces */ + n->flags |= NUMINT; } return n->numbr; } @@ -166,18 +170,6 @@ format_val(const char *format, int index, NODE *s) char buf[BUFSIZ]; char *sp = buf; double val; - char *orig, *trans, save; - - if (! do_traditional && (s->flags & INTLSTR) != 0) { - save = s->stptr[s->stlen]; - s->stptr[s->stlen] = '\0'; - - orig = s->stptr; - trans = dgettext(TEXTDOMAIN, orig); - - s->stptr[s->stlen] = save; - return make_string(trans, strlen(trans)); - } /* * 2/2007: Simplify our lives here. Instead of worrying about @@ -210,7 +202,6 @@ format_val(const char *format, int index, NODE *s) NODE *dummy[2], *r; unsigned short oflags; - extern NODE **fmt_list; /* declared in eval.c */ /* create dummy node for a sole use of format_tree */ dummy[1] = s; @@ -234,8 +225,7 @@ format_val(const char *format, int index, NODE *s) goto no_malloc; } else { /* - * integral value - * force conversion to long only once + * integral value; force conversion to long only once. */ long num = (long) val; @@ -247,19 +237,25 @@ format_val(const char *format, int index, NODE *s) s->stlen = strlen(sp); } s->stfmt = -1; + if (s->flags & INTIND) { + s->flags &= ~(INTIND|NUMBER); + s->flags |= STRING; + } } if (s->stptr != NULL) efree(s->stptr); emalloc(s->stptr, char *, s->stlen + 2, "format_val"); - memcpy(s->stptr, sp, s->stlen+1); + memcpy(s->stptr, sp, s->stlen + 1); no_malloc: s->flags |= STRCUR; free_wstr(s); return s; } -/* force_string --- force a value to be a string */ +/* r_force_string --- force a value to be a string */ + +#ifdef GAWKDEBUG NODE * r_force_string(NODE *s) { @@ -269,28 +265,23 @@ r_force_string(NODE *s) return s; return format_val(CONVFMT, CONVFMTidx, s); } +#endif -/* dupnode --- duplicate a node */ +/* r_dupnode --- duplicate a node */ NODE * -dupnode(NODE *n) +r_dupnode(NODE *n) { NODE *r; - if (n->type == Node_ahash) { - n->ahname_ref++; - return n; - } - assert(n->type == Node_val); - if ((n->flags & PERM) != 0) - return n; - +#ifdef GAWKDEBUG if ((n->flags & MALLOC) != 0) { n->valref++; return n; } +#endif getnode(r); *r = *n; @@ -308,13 +299,13 @@ dupnode(NODE *n) #endif /* MBS_SUPPORT */ if ((n->flags & STRCUR) != 0) { - emalloc(r->stptr, char *, n->stlen + 2, "dupnode"); + emalloc(r->stptr, char *, n->stlen + 2, "r_dupnode"); memcpy(r->stptr, n->stptr, n->stlen); r->stptr[n->stlen] = '\0'; #if MBS_SUPPORT if ((n->flags & WSTRCUR) != 0) { r->wstlen = n->wstlen; - emalloc(r->wstptr, wchar_t *, sizeof(wchar_t) * (n->wstlen + 2), "dupnode"); + emalloc(r->wstptr, wchar_t *, sizeof(wchar_t) * (n->wstlen + 2), "r_dupnode"); memcpy(r->wstptr, n->wstptr, n->wstlen * sizeof(wchar_t)); r->wstptr[n->wstlen] = L'\0'; r->flags |= WSTRCUR; @@ -325,35 +316,40 @@ dupnode(NODE *n) return r; } -/* mk_number --- allocate a node with defined number */ +/* make_number --- allocate a node with defined number */ NODE * -mk_number(AWKNUM x, unsigned int flags) +make_number(AWKNUM x) { NODE *r; - getnode(r); r->type = Node_val; r->numbr = x; r->valref = 1; - r->flags = flags; + r->flags = MALLOC|NUMBER|NUMCUR; r->stptr = NULL; r->stlen = 0; - free_wstr(r); +#if MBS_SUPPORT + r->wstptr = NULL; + r->wstlen = 0; +#endif /* defined MBS_SUPPORT */ return r; } -/* make_str_node --- make a string node */ + +/* r_make_str_node --- make a string node */ NODE * -r_make_str_node(const char *s, unsigned long len, int flags) +r_make_str_node(const char *s, size_t len, int flags) { NODE *r; - getnode(r); r->type = Node_val; r->numbr = 0; - r->flags = (STRING|STRCUR|MALLOC); + r->flags = (MALLOC|STRING|STRCUR); + r->valref = 1; + r->stfmt = -1; + #if MBS_SUPPORT r->wstptr = NULL; r->wstlen = 0; @@ -362,11 +358,11 @@ r_make_str_node(const char *s, unsigned long len, int flags) if (flags & ALREADY_MALLOCED) r->stptr = (char *) s; else { - emalloc(r->stptr, char *, len + 2, "make_str_node"); + emalloc(r->stptr, char *, len + 2, "r_make_str_node"); memcpy(r->stptr, s, len); } r->stptr[len] = '\0'; - + if ((flags & SCAN) != 0) { /* scan for escape sequences */ const char *pf; char *ptm; @@ -411,70 +407,36 @@ r_make_str_node(const char *s, unsigned long len, int flags) *ptm++ = c; } len = ptm - r->stptr; - erealloc(r->stptr, char *, len + 1, "make_str_node"); + erealloc(r->stptr, char *, len + 1, "r_make_str_node"); r->stptr[len] = '\0'; - r->flags &= ~MALLOC; - r->flags |= PERM; } r->stlen = len; - r->valref = 1; - r->stfmt = -1; return r; } -/* more_nodes --- allocate more nodes */ - -#define NODECHUNK 100 - -NODE *nextfree = NULL; - -NODE * -more_nodes() -{ - NODE *np; - - /* get more nodes and initialize list */ - emalloc(nextfree, NODE *, NODECHUNK * sizeof(NODE), "more_nodes"); - memset(nextfree, 0, NODECHUNK * sizeof(NODE)); - for (np = nextfree; np <= &nextfree[NODECHUNK - 1]; np++) { - np->nextp = np + 1; - } - --np; - np->nextp = NULL; - np = nextfree; - nextfree = nextfree->nextp; - return np; -} /* unref --- remove reference to a particular node */ void -unref(NODE *tmp) +r_unref(NODE *tmp) { +#ifdef GAWKDEBUG if (tmp == NULL) return; - if ((tmp->flags & PERM) != 0) - return; - - if (tmp->type == Node_ahash) { - if (tmp->ahname_ref > 1) - tmp->ahname_ref--; - else { - efree(tmp->ahname_str); - freenode(tmp); - } - return; - } - if ((tmp->flags & MALLOC) != 0) { if (tmp->valref > 1) { - tmp->valref--; + tmp->valref--; return; - } + } if (tmp->flags & STRCUR) efree(tmp->stptr); } +#else + if ((tmp->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR)) + efree(tmp->stptr); +#endif + free_wstr(tmp); freenode(tmp); } @@ -963,3 +925,41 @@ void init_btowc_cache() } } #endif + +#define BLOCKCHUNK 100 + +BLOCK nextfree[BLOCK_MAX] = { + { 0, NULL}, /* invalid */ + { sizeof(NODE), NULL }, + { sizeof(BUCKET), NULL }, +}; + + +/* more_blocks --- get more blocks of memory and add to the free list; + size of a block must be >= sizeof(BLOCK) + */ + +void * +more_blocks(int id) +{ + BLOCK *freep, *np, *next; + char *p, *endp; + size_t size; + + size = nextfree[id].size; + + emalloc(freep, BLOCK *, BLOCKCHUNK * size, "more_blocks"); + p = (char *) freep; + endp = p + BLOCKCHUNK * size; + + for (np = freep; ; np = next) { + next = (BLOCK *) (p += size); + if (p >= endp) { + np->freep = NULL; + break; + } + np->freep = next; + } + nextfree[id].freep = freep->freep; + return freep; +} diff --git a/pc/ChangeLog b/pc/ChangeLog index 429a88ed..5f287dd8 100644 --- a/pc/ChangeLog +++ b/pc/ChangeLog @@ -1,3 +1,7 @@ +2011-12-12 Scott Deifik <scottd.mail@sbcglobal.net> + + * Makefile.tst: Sync with mainline version. + 2011-12-06 Scott Deifik <scottd.mail@sbcglobal.net> * Makefile.tst: Sync with mainline version. diff --git a/pc/Makefile.tst b/pc/Makefile.tst index db214348..f14341c8 100644 --- a/pc/Makefile.tst +++ b/pc/Makefile.tst @@ -306,7 +306,7 @@ argarray:: .) : ;; \ *) rm -f ./argarray.in ;; \ esac - @-$(CMP) $(srcdir)/argarray.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ regtest:: @echo 'Some of the output from regtest is very system specific, do not' @@ -321,44 +321,45 @@ manyfiles:: @$(AWK) 'BEGIN { for (i = 1; i <= 1030; i++) print i, i}' >_$@ @$(AWK) -f $(srcdir)/manyfiles.awk _$@ _$@ @wc -l junk/* | $(AWK) '$$1 != 2' | wc -l | sed "s/ *//g" > _$@ - @rm -rf junk ; $(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ + @rm -rf junk + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ compare:: @echo $@ @$(AWK) -f $(srcdir)/compare.awk 0 1 $(srcdir)/compare.in >_$@ - @-$(CMP) $(srcdir)/compare.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ inftest:: @echo $@ @echo This test is very machine specific... @echo Expect inftest to fail with DJGPP. @$(AWK) -f $(srcdir)/inftest.awk | sed "s/inf/Inf/g" >_$@ - @-$(CMP) $(srcdir)/inftest.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ getline2:: @echo $@ @$(AWK) -f $(srcdir)/getline2.awk $(srcdir)/getline2.awk $(srcdir)/getline2.awk >_$@ - @-$(CMP) $(srcdir)/getline2.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ awkpath:: @echo $@ @AWKPATH="$(srcdir)$(PATH_SEPARATOR)$(srcdir)/lib" $(AWK) -f awkpath.awk >_$@ - @-$(CMP) $(srcdir)/awkpath.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ argtest:: @echo $@ @$(AWK) -f $(srcdir)/argtest.awk -x -y abc >_$@ - @-$(CMP) $(srcdir)/argtest.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ badargs:: @echo $@ @-$(AWK) -f 2>&1 | grep -v patchlevel >_$@ - @-$(CMP) $(srcdir)/badargs.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ nonl:: @echo $@ @-AWKPATH=$(srcdir) $(AWK) --lint -f nonl.awk /dev/null >_$@ 2>&1 - @-$(CMP) $(srcdir)/nonl.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ strftime:: @echo This test could fail on slow machines or on a minute boundary, @@ -375,7 +376,7 @@ strftime:: litoct:: @echo $@ @echo ab | $(AWK) --traditional -f $(srcdir)/litoct.awk >_$@ - @-$(CMP) $(srcdir)/litoct.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ devfd:: @echo $@ @@ -386,13 +387,13 @@ devfd:: fflush:: @echo $@ @$(srcdir)/fflush.sh >_$@ - @-$(CMP) $(srcdir)/fflush.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ tweakfld:: @echo $@ @$(AWK) -f $(srcdir)/tweakfld.awk $(srcdir)/tweakfld.in >_$@ @rm -f errors.cleanup - @-$(CMP) $(srcdir)/tweakfld.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ mmap8k:: @echo $@ @@ -402,7 +403,7 @@ mmap8k:: tradanch:: @echo $@ @$(AWK) --traditional -f $(srcdir)/tradanch.awk $(srcdir)/tradanch.in >_$@ - @-$(CMP) $(srcdir)/tradanch.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ # AIX /bin/sh exec's the last command in a list, therefore issue a ":" # command so that pid.sh is fork'ed as a child before being exec'ed. @@ -418,12 +419,12 @@ strftlng:: @if $(CMP) $(srcdir)/strftlng.ok _$@ >/dev/null 2>&1 ; then : ; else \ TZ=UTC0; export TZ; $(AWK) -f $(srcdir)/strftlng.awk >_$@ ; \ fi - @-$(CMP) $(srcdir)/strftlng.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ nors:: @echo $@ @echo A B C D E | tr -d '\12\15' | $(AWK) '{ print $$NF }' - $(srcdir)/nors.in > _$@ - @-$(CMP) $(srcdir)/nors.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ fmtspcl.ok: fmtspcl.tok @$(AWK) -v "sd=$(srcdir)" 'BEGIN {pnan = sprintf("%g",sqrt(-1)); nnan = sprintf("%g",-sqrt(-1)); pinf = sprintf("%g",-log(0)); ninf = sprintf("%g",log(0))} {sub(/positive_nan/,pnan); sub(/negative_nan/,nnan); sub(/positive_infinity/,pinf); sub(/negative_infinity/,ninf); sub(/fmtspcl/,(sd"/fmtspcl")); print}' < $(srcdir)/fmtspcl.tok > $@ 2>/dev/null @@ -437,18 +438,18 @@ fmtspcl: fmtspcl.ok reint:: @echo $@ @$(AWK) --re-interval -f $(srcdir)/reint.awk $(srcdir)/reint.in >_$@ - @-$(CMP) $(srcdir)/reint.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ pipeio1:: @echo $@ @$(AWK) -f $(srcdir)/pipeio1.awk >_$@ @rm -f test1 test2 - @-$(CMP) $(srcdir)/pipeio1.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ pipeio2:: @echo $@ @$(AWK) -v SRCDIR=$(srcdir) -f $(srcdir)/pipeio2.awk >_$@ - @-$(CMP) $(srcdir)/pipeio2.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ clobber:: @echo $@ @@ -459,7 +460,7 @@ clobber:: arynocls:: @echo $@ @-AWKPATH=$(srcdir) $(AWK) -v INPUT=$(srcdir)/arynocls.in -f arynocls.awk >_$@ - @-$(CMP) $(srcdir)/arynocls.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ getlnbuf:: @echo $@ @@ -503,12 +504,12 @@ inetdayt:: redfilnm:: @echo $@ @$(AWK) -f $(srcdir)/redfilnm.awk srcdir=$(srcdir) $(srcdir)/redfilnm.in >_$@ - @-$(CMP) $(srcdir)/redfilnm.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ leaddig:: @echo $@ @$(AWK) -v x=2E -f $(srcdir)/leaddig.awk >_$@ - @-$(CMP) $(srcdir)/leaddig.ok _$@ && rm -f _$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ gsubtst3:: @echo $@ diff --git a/po/.gitignore b/po/.gitignore new file mode 100644 index 00000000..2a1c0abe --- /dev/null +++ b/po/.gitignore @@ -0,0 +1,4 @@ +# Ignore files that are created by a build. +Makefile.in +POTFILES + diff --git a/po/ast.gmo b/po/ast.gmo Binary files differdeleted file mode 100644 index 7c2ac4e2..00000000 --- a/po/ast.gmo +++ /dev/null diff --git a/po/ca.gmo b/po/ca.gmo Binary files differdeleted file mode 100644 index 426541dc..00000000 --- a/po/ca.gmo +++ /dev/null diff --git a/po/ga.gmo b/po/ga.gmo Binary files differdeleted file mode 100644 index 21141c51..00000000 --- a/po/ga.gmo +++ /dev/null diff --git a/po/he.gmo b/po/he.gmo Binary files differdeleted file mode 100644 index b9cb6d3c..00000000 --- a/po/he.gmo +++ /dev/null diff --git a/po/id.gmo b/po/id.gmo Binary files differdeleted file mode 100644 index 272d5f8c..00000000 --- a/po/id.gmo +++ /dev/null diff --git a/po/pt_BR.gmo b/po/pt_BR.gmo Binary files differdeleted file mode 100644 index 4cbc6792..00000000 --- a/po/pt_BR.gmo +++ /dev/null diff --git a/po/ro.gmo b/po/ro.gmo Binary files differdeleted file mode 100644 index e3534b89..00000000 --- a/po/ro.gmo +++ /dev/null diff --git a/po/rw.gmo b/po/rw.gmo Binary files differdeleted file mode 100644 index a08f9183..00000000 --- a/po/rw.gmo +++ /dev/null diff --git a/po/tr.gmo b/po/tr.gmo Binary files differdeleted file mode 100644 index 146da41d..00000000 --- a/po/tr.gmo +++ /dev/null diff --git a/po/vi.gmo b/po/vi.gmo Binary files differdeleted file mode 100644 index e3a5e619..00000000 --- a/po/vi.gmo +++ /dev/null diff --git a/po/zh_CN.gmo b/po/zh_CN.gmo Binary files differdeleted file mode 100644 index 7ed096c3..00000000 --- a/po/zh_CN.gmo +++ /dev/null @@ -43,15 +43,14 @@ const char *redir2str(int redirtype); #define DONT_FREE 1 #define CAN_FREE 2 -#ifdef PROFILING + static RETSIGTYPE dump_and_exit(int signum) ATTRIBUTE_NORETURN; static RETSIGTYPE just_dump(int signum); -#endif /* pretty printing related functions and variables */ static NODE *pp_stack = NULL; -static char **fparms; /* function parameter names */ +static NODE *func_params; /* function parameters */ static FILE *prof_fp; /* where to send the profile */ static long indent_level = 0; @@ -59,20 +58,7 @@ static long indent_level = 0; #define SPACEOVER 0 -/* init_profiling --- do needed initializations, see also main.c */ - -void -init_profiling(int *flag ATTRIBUTE_UNUSED, const char *def_file ATTRIBUTE_UNUSED) -{ -#ifdef PROFILING - if (*flag == FALSE) { - *flag = TRUE; - set_prof_file(def_file); - } -#endif -} - -/* set_prof_file --- set the output file for profiling */ +/* set_prof_file --- set the output file for profiling or pretty-printing */ void set_prof_file(const char *file) @@ -87,12 +73,11 @@ set_prof_file(const char *file) } } -/* init_profiling_signals --- set up signal handling for pgawk */ +/* init_profiling_signals --- set up signal handling for gawk --profile */ void init_profiling_signals() { -#ifdef PROFILING #ifdef __DJGPP__ signal(SIGINT, dump_and_exit); signal(SIGQUIT, just_dump); @@ -104,7 +89,6 @@ init_profiling_signals() signal(SIGUSR1, just_dump); #endif #endif /* !__DJGPP__ */ -#endif /* PROFILING */ } /* indent --- print out enough tabs */ @@ -214,10 +198,10 @@ pprint(INSTRUCTION *startp, INSTRUCTION *endp, int in_for_header) fprintf(prof_fp, "%s {", t1->pp_str); pp_free(t1); ip = (pc + 1)->firsti; -#ifdef PROFILING - if (ip->exec_count > 0) + + if (do_profile && ip->exec_count > 0) fprintf(prof_fp, " # %ld", ip->exec_count); -#endif + fprintf(prof_fp, "\n"); } else { fprintf(prof_fp, "{\n"); @@ -257,6 +241,9 @@ pprint(INSTRUCTION *startp, INSTRUCTION *endp, int in_for_header) break; case Op_store_var: + if (pc->initval != NULL) + pp_push(Op_push_i, pp_node(pc->initval), CAN_FREE); + /* fall through */ case Op_store_sub: case Op_assign_concat: case Op_push_lhs: @@ -267,7 +254,7 @@ pprint(INSTRUCTION *startp, INSTRUCTION *endp, int in_for_header) m = pc->memory; switch (m->type) { case Node_param_list: - pp_push(pc->opcode, fparms[m->param_cnt], DONT_FREE); + pp_push(pc->opcode, func_params[m->param_cnt].param, DONT_FREE); break; case Node_var: @@ -522,9 +509,13 @@ cleanup: break; case Op_builtin: + case Op_ext_builtin: { - static char *ext_func = "extension_function()"; - const char *fname = getfname(pc->builtin); + const char *fname; + if (pc->opcode == Op_builtin) + fname = getfname(pc->builtin); + else + fname = (pc + 1)->func_name; if (fname != NULL) { if (pc->expr_count > 0) { tmp = pp_list(pc->expr_count, "()", ", "); @@ -534,10 +525,10 @@ cleanup: str = pp_concat(fname, "()", ""); pp_push(Op_builtin, str, CAN_FREE); } else - pp_push(Op_builtin, ext_func, DONT_FREE); + fatal(_("internal error: builtin with null fname")); } break; - + case Op_K_print: case Op_K_printf: case Op_K_print_rec: @@ -756,14 +747,15 @@ cleanup: case Op_K_arrayfor: { - char *array, *item; + char *array; + const char *item; ip = pc + 1; t1 = pp_pop(); array = t1->pp_str; m = ip->forloop_cond->array_var; if (m->type == Node_param_list) - item = fparms[m->param_cnt]; + item = func_params[m->param_cnt].param; else item = m->vname; indent(ip->forloop_body->exec_count); @@ -909,7 +901,7 @@ pp_string_fp(Func_print print_func, FILE *fp, const char *in_str, efree(s); } -#ifdef PROFILING + /* just_dump --- dump the profile and function stack and keep going */ static RETSIGTYPE @@ -933,7 +925,6 @@ dump_and_exit(int signum) exit(EXIT_FAILURE); } -#endif /* dump_prog --- dump the program */ @@ -1321,9 +1312,8 @@ int pp_func(INSTRUCTION *pc, void *data ATTRIBUTE_UNUSED) { int j; - char **pnames; - NODE *f; static int first = TRUE; + NODE *func; int pcount; if (first) { @@ -1331,15 +1321,14 @@ pp_func(INSTRUCTION *pc, void *data ATTRIBUTE_UNUSED) fprintf(prof_fp, _("\n\t# Functions, listed alphabetically\n")); } - f = pc->func_body; + func = pc->func_body; fprintf(prof_fp, "\n"); indent(pc->nexti->exec_count); - fprintf(prof_fp, "%s %s(", op2str(Op_K_function), f->lnode->param); - pnames = f->parmlist; - fparms = pnames; - pcount = f->lnode->param_cnt; + fprintf(prof_fp, "%s %s(", op2str(Op_K_function), func->vname); + pcount = func->param_cnt; + func_params = func->fparms; for (j = 0; j < pcount; j++) { - fprintf(prof_fp, "%s", pnames[j]); + fprintf(prof_fp, "%s", func_params[j].param); if (j < pcount - 1) fprintf(prof_fp, ", "); } diff --git a/profile_p.c b/profile_p.c deleted file mode 100644 index 97edd367..00000000 --- a/profile_p.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * profile_p.c - compile profile.c with profiling turned on. - */ - -/* - * Copyright (C) 2001 the Free Software Foundation, Inc. - * - * This file is part of GAWK, the GNU implementation of the - * AWK Programming Language. - * - * GAWK 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 3 of the License, or - * (at your option) any later version. - * - * GAWK 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#define PROFILING 1 -#include "profile.c" diff --git a/str_array.c b/str_array.c new file mode 100644 index 00000000..7ce617ed --- /dev/null +++ b/str_array.c @@ -0,0 +1,762 @@ +/* + * str_array.c - routines for associative arrays of string indices. + */ + +/* + * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Programming Language. + * + * GAWK 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 3 of the License, or + * (at your option) any later version. + * + * GAWK 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "awk.h" + +/* + * Tree walks (``for (iggy in foo)'') and array deletions use expensive + * linear searching. So what we do is start out with small arrays and + * grow them as needed, so that our arrays are hopefully small enough, + * most of the time, that they're pretty full and we're not looking at + * wasted space. + * + * The decision is made to grow the array if the average chain length is + * ``too big''. This is defined as the total number of entries in the table + * divided by the size of the array being greater than some constant. + * + * 11/2002: We make the constant a variable, so that it can be tweaked + * via environment variable. + * 11/2002: Modern machines are bigger, cut this down from 10. + */ + +static size_t STR_CHAIN_MAX = 2; + +extern FILE *output_fp; +extern void indent(int indent_level); + +static NODE **str_array_init(NODE *symbol, NODE *subs); +static NODE **str_lookup(NODE *symbol, NODE *subs); +static NODE **str_exists(NODE *symbol, NODE *subs); +static NODE **str_clear(NODE *symbol, NODE *subs); +static NODE **str_remove(NODE *symbol, NODE *subs); +static NODE **str_list(NODE *symbol, NODE *subs); +static NODE **str_copy(NODE *symbol, NODE *newsymb); +static NODE **str_dump(NODE *symbol, NODE *ndump); + +#ifdef ARRAYDEBUG +static NODE **str_option(NODE *opt, NODE *val); +#endif + + +array_ptr str_array_func[] = { + str_array_init, + (array_ptr) 0, + str_lookup, + str_exists, + str_clear, + str_remove, + str_list, + str_copy, + str_dump, +#ifdef ARRAYDEBUG + str_option +#endif +}; + +static inline NODE **str_find(NODE *symbol, NODE *s1, size_t code1, unsigned long hash1); +static void grow_table(NODE *symbol); + +static unsigned long gst_hash_string(const char *str, size_t len, unsigned long hsize, size_t *code); +static unsigned long scramble(unsigned long x); +static unsigned long awk_hash(const char *s, size_t len, unsigned long hsize, size_t *code); + +unsigned long (*hash)(const char *s, size_t len, unsigned long hsize, size_t *code) = awk_hash; + + +/* str_array_init --- check relevant environment variables */ + +static NODE ** +str_array_init(NODE *symbol ATTRIBUTE_UNUSED, NODE *subs ATTRIBUTE_UNUSED) +{ + long newval; + const char *val; + + if ((newval = getenv_long("STR_CHAIN_MAX")) > 0) + STR_CHAIN_MAX = newval; + if ((val = getenv("AWK_HASH")) != NULL && strcmp(val, "gst") == 0) + hash = gst_hash_string; + return (NODE **) ! NULL; +} + + +/* + * assoc_lookup: + * Find SYMBOL[SUBS] in the assoc array. Install it with value "" if it + * isn't there. Returns a pointer ala get_lhs to where its value is stored. + * + * SYMBOL is the address of the node (or other pointer) being dereferenced. + * SUBS is a number or string used as the subscript. + */ + +static NODE ** +str_lookup(NODE *symbol, NODE *subs) +{ + unsigned long hash1; + NODE **lhs; + BUCKET *b; + size_t code1; + + subs = force_string(subs); + + if (symbol->buckets == NULL) + grow_table(symbol); + hash1 = hash(subs->stptr, subs->stlen, + (unsigned long) symbol->array_size, & code1); + if ((lhs = str_find(symbol, subs, code1, hash1)) != NULL) + return lhs; + + /* It's not there, install it. */ + /* first see if we would need to grow the array, before installing */ + + symbol->table_size++; + if ((symbol->flags & ARRAYMAXED) == 0 + && (symbol->table_size / symbol->array_size) > STR_CHAIN_MAX) { + grow_table(symbol); + /* have to recompute hash value for new size */ + hash1 = code1 % (unsigned long) symbol->array_size; + } + + if (subs->stfmt != -1) { + NODE *tmp; + + /* + * Need to freeze this string value --- it must never + * change, no matter what happens to the value + * that created it or to CONVFMT, etc.; So, get + * a private copy. + */ + + tmp = make_string(subs->stptr, subs->stlen); + + /* + * Set the numeric value for the index if it's available. Useful + * for numeric sorting by index. Do this only if the numeric + * value is available, instead of all the time, since doing it + * all the time is a big performance hit for something that may + * never be used. + */ + + if (subs->flags & NUMCUR) { + tmp->numbr = subs->numbr; + tmp->flags |= NUMCUR; + } + subs = tmp; + } else { + /* string value already "frozen" */ + + subs = dupnode(subs); + } + + getbucket(b); + b->ahnext = symbol->buckets[hash1]; + symbol->buckets[hash1] = b; + b->ahname = subs; + b->ahname_str = subs->stptr; + b->ahname_len = subs->stlen; + b->ahvalue = dupnode(Nnull_string); + b->ahcode = code1; + return & (b->ahvalue); +} + +/* str_exists --- test whether the array element symbol[subs] exists or not, + * return pointer to value if it does. + */ + +static NODE ** +str_exists(NODE *symbol, NODE *subs) +{ + NODE **lhs; + unsigned long hash1; + size_t code1; + + if (symbol->table_size == 0) + return NULL; + + subs = force_string(subs); + hash1 = hash(subs->stptr, subs->stlen, (unsigned long) symbol->array_size, & code1); + lhs = str_find(symbol, subs, code1, hash1); + return lhs; +} + +/* str_clear --- flush all the values in symbol[] */ + +static NODE ** +str_clear(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED) +{ + unsigned long i; + BUCKET *b, *next; + NODE *r; + + for (i = 0; i < symbol->array_size; i++) { + for (b = symbol->buckets[i]; b != NULL; b = next) { + next = b->ahnext; + r = b->ahvalue; + if (r->type == Node_var_array) { + assoc_clear(r); /* recursively clear all sub-arrays */ + efree(r->vname); + freenode(r); + } else + unref(r); + unref(b->ahname); + freebucket(b); + } + symbol->buckets[i] = NULL; + } + + if (symbol->buckets != NULL) + efree(symbol->buckets); + init_array(symbol); /* re-initialize symbol */ + symbol->flags &= ~ARRAYMAXED; + return NULL; +} + + +/* str_remove --- If SUBS is already in the table, remove it. */ + +static NODE ** +str_remove(NODE *symbol, NODE *subs) +{ + unsigned long hash1; + BUCKET *b, *prev; + NODE *s2; + size_t s1_len; + + if (symbol->table_size == 0) + return NULL; + + s2 = force_string(subs); + hash1 = hash(s2->stptr, s2->stlen, (unsigned long) symbol->array_size, NULL); + + for (b = symbol->buckets[hash1], prev = NULL; b != NULL; + prev = b, b = b->ahnext) { + + /* Array indexes are strings; compare as such, always! */ + s1_len = b->ahname_len; + + if (s1_len != s2->stlen) + continue; + if (s1_len == 0 /* "" is a valid index */ + || memcmp(b->ahname_str, s2->stptr, s1_len) == 0) { + /* item found */ + + unref(b->ahname); + if (prev != NULL) + prev->ahnext = b->ahnext; + else + symbol->buckets[hash1] = b->ahnext; + + /* delete bucket */ + freebucket(b); + + /* one less element in array */ + if (--symbol->table_size == 0) { + if (symbol->buckets != NULL) + efree(symbol->buckets); + init_array(symbol); /* re-initialize symbol */ + symbol->flags &= ~ARRAYMAXED; + } + + return (NODE **) ! NULL; /* return success */ + } + } + + return NULL; +} + + +/* str_copy --- duplicate input array "symbol" */ + +static NODE ** +str_copy(NODE *symbol, NODE *newsymb) +{ + BUCKET **old, **new, **pnew; + BUCKET *chain, *newchain; + unsigned long cursize, i; + + assert(symbol->table_size > 0); + + /* find the current hash size */ + cursize = symbol->array_size; + + /* allocate new table */ + emalloc(new, BUCKET **, cursize * sizeof(BUCKET *), "str_copy"); + memset(new, '\0', cursize * sizeof(BUCKET *)); + + old = symbol->buckets; + + for (i = 0; i < cursize; i++) { + for (chain = old[i], pnew = & new[i]; chain != NULL; + chain = chain->ahnext + ) { + NODE *oldval, *newsubs; + + getbucket(newchain); + + /* + * copy the corresponding name and + * value from the original input list + */ + + newsubs = newchain->ahname = dupnode(chain->ahname); + newchain->ahname_str = newsubs->stptr; + newchain->ahname_len = newsubs->stlen; + + oldval = chain->ahvalue; + if (oldval->type == Node_val) + newchain->ahvalue = dupnode(oldval); + else { + NODE *r; + + r = make_array(); + r->vname = estrdup(oldval->vname, strlen(oldval->vname)); + r->parent_array = newsymb; + newchain->ahvalue = assoc_copy(oldval, r); + } + newchain->ahcode = chain->ahcode; + + *pnew = newchain; + pnew = & newchain->ahnext; + } + } + + newsymb->table_size = symbol->table_size; + newsymb->buckets = new; + newsymb->array_size = cursize; + newsymb->flags = symbol->flags; + return NULL; +} + + +/* str_list --- return a list of array items */ + +static NODE** +str_list(NODE *symbol, NODE *t) +{ + NODE **list; + NODE *subs, *val; + BUCKET *b; + unsigned long num_elems, list_size, i, k = 0; + int elem_size = 1; + + if (symbol->table_size == 0) + return NULL; + + if ((t->flags & (AINDEX|AVALUE)) == (AINDEX|AVALUE)) + elem_size = 2; + + /* allocate space for array */ + num_elems = symbol->table_size; + if ((t->flags & (AINDEX|AVALUE|ADELETE)) == (AINDEX|ADELETE)) + num_elems = 1; + list_size = elem_size * num_elems; + + emalloc(list, NODE **, list_size * sizeof(NODE *), "str_list"); + + /* populate it */ + + for (i = 0; i < symbol->array_size; i++) { + for (b = symbol->buckets[i]; b != NULL; b = b->ahnext) { + /* index */ + subs = b->ahname; + if (t->flags & AINUM) + (void) force_number(subs); + list[k++] = dupnode(subs); + + /* value */ + if (t->flags & AVALUE) { + val = b->ahvalue; + if (val->type == Node_val) { + if ((t->flags & AVNUM) != 0) + (void) force_number(val); + else if ((t->flags & AVSTR) != 0) + val = force_string(val); + } + list[k++] = val; + } + if (k >= list_size) + return list; + } + } + return list; +} + + +/* str_kilobytes --- calculate memory consumption of the assoc array */ + +AWKNUM +str_kilobytes(NODE *symbol) +{ + unsigned long bucket_cnt; + AWKNUM kb; + + bucket_cnt = symbol->table_size; + + /* This does not include extra memory for indices with stfmt != -1 */ + kb = (((AWKNUM) bucket_cnt) * sizeof (BUCKET) + + ((AWKNUM) symbol->array_size) * sizeof (BUCKET *)) / 1024.0; + return kb; +} + + +/* str_dump --- dump array info */ + +static NODE ** +str_dump(NODE *symbol, NODE *ndump) +{ +#define HCNT 31 + + int indent_level; + unsigned long i, bucket_cnt; + BUCKET *b; + static size_t hash_dist[HCNT + 1]; + + indent_level = ndump->alevel; + + if ((symbol->flags & XARRAY) == 0) + fprintf(output_fp, "%s `%s'\n", + (symbol->parent_array == NULL) ? "array" : "sub-array", + array_vname(symbol)); + indent_level++; + indent(indent_level); + fprintf(output_fp, "array_func: str_array_func\n"); + if (symbol->flags != 0) { + indent(indent_level); + fprintf(output_fp, "flags: %s\n", flags2str(symbol->flags)); + } + indent(indent_level); + fprintf(output_fp, "STR_CHAIN_MAX: %lu\n", (unsigned long) STR_CHAIN_MAX); + indent(indent_level); + fprintf(output_fp, "array_size: %lu\n", (unsigned long) symbol->array_size); + indent(indent_level); + fprintf(output_fp, "table_size: %lu\n", (unsigned long) symbol->table_size); + indent(indent_level); + fprintf(output_fp, "Avg # of items per chain: %.2g\n", + ((AWKNUM) symbol->table_size) / symbol->array_size); + + indent(indent_level); + fprintf(output_fp, "memory: %.2g kB\n", str_kilobytes(symbol)); + + /* hash value distribution */ + + memset(hash_dist, '\0', (HCNT + 1) * sizeof(size_t)); + for (i = 0; i < symbol->array_size; i++) { + bucket_cnt = 0; + for (b = symbol->buckets[i]; b != NULL; b = b->ahnext) + bucket_cnt++; + if (bucket_cnt >= HCNT) + bucket_cnt = HCNT; + hash_dist[bucket_cnt]++; + } + + indent(indent_level); + fprintf(output_fp, "Hash distribution:\n"); + indent_level++; + for (i = 0; i <= HCNT; i++) { + if (hash_dist[i] > 0) { + indent(indent_level); + if (i == HCNT) + fprintf(output_fp, "[>=%lu]:%lu\n", + (unsigned long) HCNT, (unsigned long) hash_dist[i]); + else + fprintf(output_fp, "[%lu]:%lu\n", + (unsigned long) i, (unsigned long) hash_dist[i]); + } + } + indent_level--; + + /* dump elements */ + + if (ndump->adepth >= 0) { + const char *aname; + + fprintf(output_fp, "\n"); + aname = make_aname(symbol); + for (i = 0; i < symbol->array_size; i++) { + for (b = symbol->buckets[i]; b != NULL; b = b->ahnext) + assoc_info(b->ahname, b->ahvalue, ndump, aname); + } + } + + return NULL; + +#undef HCNT +} + + +/* awk_hash --- calculate the hash function of the string in subs */ + +static unsigned long +awk_hash(const char *s, size_t len, unsigned long hsize, size_t *code) +{ + unsigned long h = 0; + unsigned long htmp; + + /* + * Ozan Yigit's original sdbm hash, copied from Margo Seltzers + * db package. + * + * This is INCREDIBLY ugly, but fast. We break the string up into + * 8 byte units. On the first time through the loop we get the + * "leftover bytes" (strlen % 8). On every other iteration, we + * perform 8 HASHC's so we handle all 8 bytes. Essentially, this + * saves us 7 cmp & branch instructions. If this routine is + * heavily used enough, it's worth the ugly coding. + */ + + /* + * Even more speed: + * #define HASHC h = *s++ + 65599 * h + * Because 65599 = pow(2, 6) + pow(2, 16) - 1 we multiply by shifts + * + * 4/2011: Force the results to 32 bits, to get the same + * result on both 32- and 64-bit systems. This may be a + * bad idea. + */ +#define HASHC htmp = (h << 6); \ + h = *s++ + htmp + (htmp << 10) - h ; \ + htmp &= 0xFFFFFFFF; \ + h &= 0xFFFFFFFF + + h = 0; + + /* "Duff's Device" */ + if (len > 0) { + size_t loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) { + case 0: + do { /* All fall throughs */ + HASHC; + case 7: HASHC; + case 6: HASHC; + case 5: HASHC; + case 4: HASHC; + case 3: HASHC; + case 2: HASHC; + case 1: HASHC; + } while (--loop); + } + } + + if (code != NULL) + *code = h; + + if (h >= hsize) + h %= hsize; + return h; +} + + +/* str_find --- locate symbol[subs] */ + +static inline NODE ** +str_find(NODE *symbol, NODE *s1, size_t code1, unsigned long hash1) +{ + BUCKET *b; + size_t s2_len; + + for (b = symbol->buckets[hash1]; b != NULL; b = b->ahnext) { + /* + * This used to use cmp_nodes() here. That's wrong. + * Array indexes are strings; compare as such, always! + */ + s2_len = b->ahname_len; + + if (code1 == b->ahcode + && s1->stlen == s2_len + && (s2_len == 0 /* "" is a valid index */ + || memcmp(s1->stptr, b->ahname_str, s2_len) == 0) + ) + return & (b->ahvalue); + } + return NULL; +} + + +/* grow_table --- grow a hash table */ + +static void +grow_table(NODE *symbol) +{ + BUCKET **old, **new; + BUCKET *chain, *next; + int i, j; + unsigned long oldsize, newsize, k; + unsigned long hash1; + + /* + * This is an array of primes. We grow the table by an order of + * magnitude each time (not just doubling) so that growing is a + * rare operation. We expect, on average, that it won't happen + * more than twice. The final size is also chosen to be small + * enough so that MS-DOG mallocs can handle it. When things are + * very large (> 8K), we just double more or less, instead of + * just jumping from 8K to 64K. + */ + + static const unsigned long sizes[] = { + 13, 127, 1021, 8191, 16381, 32749, 65497, + 131101, 262147, 524309, 1048583, 2097169, + 4194319, 8388617, 16777259, 33554467, + 67108879, 134217757, 268435459, 536870923, + 1073741827 + }; + + /* find next biggest hash size */ + newsize = oldsize = symbol->array_size; + + for (i = 0, j = sizeof(sizes)/sizeof(sizes[0]); i < j; i++) { + if (oldsize < sizes[i]) { + newsize = sizes[i]; + break; + } + } + if (newsize == oldsize) { /* table already at max (!) */ + symbol->flags |= ARRAYMAXED; + return; + } + + /* allocate new table */ + emalloc(new, BUCKET **, newsize * sizeof(BUCKET *), "grow_table"); + memset(new, '\0', newsize * sizeof(BUCKET *)); + + old = symbol->buckets; + symbol->buckets = new; + symbol->array_size = newsize; + + /* brand new hash table, set things up and return */ + if (old == NULL) { + symbol->table_size = 0; + return; + } + + /* old hash table there, move stuff to new, free old */ + + /* + * note that symbol->table_size does not change if an old array, + * and is explicitly set to 0 if a new one. + */ + + for (k = 0; k < oldsize; k++) { + for (chain = old[k]; chain != NULL; chain = next) { + next = chain->ahnext; + hash1 = chain->ahcode % newsize; + + /* remove from old list, add to new */ + chain->ahnext = new[hash1]; + new[hash1] = chain; + } + } + efree(old); +} + + +#ifdef ARRAYDEBUG + +static NODE ** +str_option(NODE *opt, NODE *val) +{ + int newval; + NODE *tmp; + NODE **ret = (NODE **) ! NULL; + + tmp = force_string(opt); + (void) force_number(val); + if (strcmp(tmp->stptr, "STR_CHAIN_MAX") == 0) { + newval = (int) val->numbr; + if (newval > 0) + STR_CHAIN_MAX = newval; + } else + ret = NULL; + return ret; +} +#endif + + +/* +From bonzini@gnu.org Mon Oct 28 16:05:26 2002 +Date: Mon, 28 Oct 2002 13:33:03 +0100 +From: Paolo Bonzini <bonzini@gnu.org> +To: arnold@skeeve.com +Subject: Hash function +Message-ID: <20021028123303.GA6832@biancaneve> + +Here is the hash function I'm using in GNU Smalltalk. The scrambling is +needed if you use powers of two as the table sizes. If you use primes it +is not needed. + +To use double-hashing with power-of-two size, you should use the +_gst_hash_string(str, len) as the primary hash and +scramble(_gst_hash_string (str, len)) | 1 as the secondary hash. + +Paolo + +*/ +/* + * ADR: Slightly modified to work w/in the context of gawk. + */ + +static unsigned long +gst_hash_string(const char *str, size_t len, unsigned long hsize, size_t *code) +{ + unsigned long hashVal = 1497032417; /* arbitrary value */ + unsigned long ret; + + while (len--) { + hashVal += *str++; + hashVal += (hashVal << 10); + hashVal ^= (hashVal >> 6); + } + + ret = scramble(hashVal); + + if (code != NULL) + *code = ret; + + if (ret >= hsize) + ret %= hsize; + + return ret; +} + +static unsigned long +scramble(unsigned long x) +{ + if (sizeof(long) == 4) { + int y = ~x; + + x += (y << 10) | (y >> 22); + x += (x << 6) | (x >> 26); + x -= (x << 16) | (x >> 16); + } else { + x ^= (~x) >> 31; + x += (x << 21) | (x >> 11); + x += (x << 5) | (x >> 27); + x += (x << 27) | (x >> 5); + x += (x << 31); + } + + return x; +} diff --git a/symbol.c b/symbol.c new file mode 100644 index 00000000..57ca7be0 --- /dev/null +++ b/symbol.c @@ -0,0 +1,718 @@ +/* + * symbol.c - routines for symbol table management and code allocation + */ + +/* + * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Programming Language. + * + * GAWK 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 3 of the License, or + * (at your option) any later version. + * + * GAWK 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "awk.h" + +extern SRCFILE *srcfiles; +extern INSTRUCTION *rule_list; + +#define HASHSIZE 1021 + +static NODE *variables[HASHSIZE]; +static int func_count; /* total number of functions */ +static int var_count; /* total number of global variables and functions */ + +static NODE *symbol_list; +static void (*install_func)(NODE *) = NULL; +static NODE *make_symbol(char *name, NODETYPE type); +static NODE *install(char *name, NODE *hp, NODETYPE type); +static void free_bcpool(INSTRUCTION *pl); + +static AWK_CONTEXT *curr_ctxt = NULL; +static int ctxt_level; + + +/* + * install_symbol: + * Install a global name in the symbol table, even if it is already there. + * Caller must check against redefinition if that is desired. + */ + +NODE * +install_symbol(char *name, NODETYPE type) +{ + return install(name, NULL, type); +} + + +/* lookup --- find the most recent global or param node for name + * installed by install_symbol + */ + +NODE * +lookup(const char *name) +{ + NODE *hp; + size_t len; + int hash1; + + len = strlen(name); + hash1 = hash(name, len, (unsigned long) HASHSIZE, NULL); + for (hp = variables[hash1]; hp != NULL; hp = hp->hnext) { + if (hp->hlength == len && strncmp(hp->hname, name, len) == 0) + return hp->hvalue; + } + return NULL; +} + +/* make_params --- allocate function parameters for the symbol table */ + +NODE * +make_params(char **pnames, int pcount) +{ + NODE *hp, *parms; + int i; + + if (pcount <= 0 || pnames == NULL) + return NULL; + + emalloc(parms, NODE *, pcount * sizeof(NODE), "make_params"); + memset(parms, '\0', pcount * sizeof(NODE)); + + for (i = 0, hp = parms; i < pcount; i++, hp++) { + hp->type = Node_param_list; + hp->hname = pnames[i]; /* shadows pname and vname */ + hp->hlength = strlen(pnames[i]); + hp->param_cnt = i; + hp->hvalue = hp; /* points to itself */ + } + + return parms; +} + +/* install_params --- install function parameters into the symbol table */ + +void +install_params(NODE *func) +{ + int i, pcount; + NODE *parms; + + if (func == NULL) + return; + assert(func->type == Node_func); + if ((pcount = func->param_cnt) <= 0 + || (parms = func->fparms) == NULL + ) + return; + for (i = 0; i < pcount; i++) + (void) install(NULL, parms + i, Node_param_list); +} + + +/* + * remove_params --- remove function parameters out of the symbol table. + */ + +void +remove_params(NODE *func) +{ + NODE *parms, *p, *prev, *n; + int i, pcount, hash1; + + if (func == NULL) + return; + assert(func->type == Node_func); + if ((pcount = func->param_cnt) <= 0 + || (parms = func->fparms) == NULL + ) + return; + + for (i = pcount - 1; i >= 0; i--) { + p = parms + i; + hash1 = p->hcode; + if (hash1 < 0 || hash1 >= HASHSIZE) + continue; + for (prev = NULL, n = variables[hash1]; n != NULL; + prev = n, n = n->hnext) { + if (n == p) + break; + } + if (n == NULL) + continue; + if (prev == NULL) + variables[hash1] = n->hnext; /* param at the head of the chain */ + else + prev->hnext = n->hnext; /* param not at the head */ + } +} + + +/* remove_symbol --- remove a symbol from the symbol table */ + +NODE * +remove_symbol(NODE *r) +{ + NODE *prev, *hp; + int hash1; + + hash1 = hash(r->vname, strlen(r->vname), (unsigned long) HASHSIZE, NULL); + for (prev = NULL, hp = variables[hash1]; hp != NULL; + prev = hp, hp = hp->hnext) { + if (hp->hvalue == r) + break; + } + + if (hp == NULL) + return NULL; + assert(hp->hcode == hash1); + + if (prev == NULL) + variables[hash1] = hp->hnext; /* symbol at the head of chain */ + else + prev->hnext = hp->hnext; /* symbol not at the head */ + + if (r->type == Node_param_list) + return r; /* r == hp */ + if (r->type == Node_func) + func_count--; + if (r->type != Node_ext_func) + var_count--; + freenode(hp); + return r; +} + + +/* destroy_symbol --- remove a symbol from symbol table +* and free all associated memory. +*/ + +void +destroy_symbol(NODE *r) +{ + r = remove_symbol(r); + if (r == NULL) + return; + + switch (r->type) { + case Node_func: + if (r->param_cnt > 0) { + NODE *n; + int i; + int pcount = r->param_cnt; + + /* function parameters of type Node_param_list */ + for (i = 0; i < pcount; i++) { + n = r->fparms + i; + efree(n->param); + } + efree(r->fparms); + } + break; + + case Node_ext_func: + bcfree(r->code_ptr); + break; + + case Node_var_array: + assoc_clear(r); + break; + + case Node_var: + unref(r->var_value); + break; + + default: + /* Node_param_list -- YYABORT */ + return; + } + + efree(r->vname); + freenode(r); +} + + +/* make_symbol --- allocates a global symbol for the symbol table. */ + +static NODE * +make_symbol(char *name, NODETYPE type) +{ + NODE *hp, *r; + + getnode(hp); + hp->type = Node_hashnode; + hp->hlength = strlen(name); + hp->hname = name; + getnode(r); + memset(r, '\0', sizeof(NODE)); + hp->hvalue = r; + if (type == Node_var_array) + init_array(r); + else if (type == Node_var) + r->var_value = dupnode(Nnull_string); + r->vname = name; + r->type = type; + return hp; +} + +/* install --- install a global name or function parameter in the symbol table */ + +static NODE * +install(char *name, NODE *hp, NODETYPE type) +{ + int hash1; + NODE *r; + + if (hp == NULL) { + /* global symbol */ + hp = make_symbol(name, type); + if (type == Node_func) + func_count++; + if (type != Node_ext_func) + var_count++; /* total, includes Node_func */ + } + + r = hp->hvalue; + hash1 = hash(hp->hname, hp->hlength, (unsigned long) HASHSIZE, NULL); + hp->hcode = hash1; + hp->hnext = variables[hash1]; + variables[hash1] = hp; + + if (install_func) + (*install_func)(r); + + return r; +} + + +/* comp_symbol --- compare two (variable or function) names */ + +static int +comp_symbol(const void *v1, const void *v2) +{ + const NODE *const *npp1, *const *npp2; + const NODE *n1, *n2; + + npp1 = (const NODE *const *) v1; + npp2 = (const NODE *const *) v2; + n1 = *npp1; + n2 = *npp2; + + return strcmp(n1->vname, n2->vname); +} + + +typedef enum { FUNCTION = 1, VARIABLE } SYMBOL_TYPE; + +/* get_symbols --- return a list of optionally sorted symbols */ + +static NODE ** +get_symbols(SYMBOL_TYPE what, int sort) +{ + int i; + NODE **table; + NODE *hp, *r; + long j, count = 0; + + if (what == FUNCTION) + count = func_count; + else /* if (what == VARIABLE) */ + count = var_count; + + emalloc(table, NODE **, (count + 1) * sizeof(NODE *), "symbol_list"); + if (what == VARIABLE) + update_global_values(); + + for (i = j = 0; i < HASHSIZE; i++) + for (hp = variables[i]; hp != NULL; hp = hp->hnext) { + if (hp->type != Node_hashnode) + continue; + r = hp->hvalue; + if (r->type == Node_ext_func) + continue; + if (what == FUNCTION && r->type == Node_func) + table[j++] = r; + else if (what == VARIABLE) + table[j++] = r; + } + + if (sort && count > 1) + qsort(table, count, sizeof(NODE *), comp_symbol); /* Shazzam! */ + table[count] = NULL; /* null terminate the list */ + return table; +} + + +/* variable_list --- list of global variables */ + +NODE ** +variable_list() +{ + return get_symbols(VARIABLE, TRUE); +} + +/* function_list --- list of functions */ + +NODE ** +function_list(int sort) +{ + return get_symbols(FUNCTION, sort); +} + +/* print_vars --- print names and values of global variables */ + +void +print_vars(NODE **table, int (*print_func)(FILE *, const char *, ...), FILE *fp) +{ + int i; + NODE *r; + + assert(table != NULL); + + for (i = 0; (r = table[i]) != NULL; i++) { + if (r->type == Node_func || r->type == Node_ext_func) + continue; + print_func(fp, "%s: ", r->vname); + if (r->type == Node_var_array) + print_func(fp, "array, %ld elements\n", r->table_size); + else if (r->type == Node_var_new) + print_func(fp, "untyped variable\n"); + else if (r->type == Node_var) + valinfo(r->var_value, print_func, fp); + } +} + + +/* foreach_func --- execute given function for each awk function in table. */ + +int +foreach_func(NODE **table, int (*pfunc)(INSTRUCTION *, void *), void *data) +{ + int i; + NODE *r; + int ret = 0; + + assert(table != NULL); + + for (i = 0; (r = table[i]) != NULL; i++) { + if ((ret = pfunc(r->code_ptr, data)) != 0) + break; + } + return ret; +} + +/* release_all_vars --- free all variable memory */ + +void +release_all_vars() +{ + int i; + NODE *hp, *r, *next; + + for (i = 0; i < HASHSIZE; i++) + for (hp = variables[i]; hp != NULL; hp = next) { + next = hp->hnext; + if (hp->type != Node_hashnode) + continue; + r = hp->hvalue; + if (r->type == Node_func || r->type == Node_ext_func) + continue; + if (r->type == Node_var_array) + assoc_clear(r); + else if (r->type == Node_var) + unref(r->var_value); + efree(r->vname); + freenode(r); + freenode(hp); + } +} + + +/* append_symbol --- append symbol to the list of symbols + * installed in the symbol table. + */ + +void +append_symbol(NODE *r) +{ + NODE *hp; + + getnode(hp); + hp->lnode = r; + hp->rnode = symbol_list->rnode; + symbol_list->rnode = hp; +} + +/* release_symbol --- free symbol list and optionally remove symbol from symbol table */ + +void +release_symbols(NODE *symlist, int keep_globals) +{ + NODE *hp, *next; + + for (hp = symlist->rnode; hp != NULL; hp = next) { + if (! keep_globals) { + /* destroys globals, function, and params + * if still in symbol table + */ + destroy_symbol(hp->lnode); + } + next = hp->rnode; + freenode(hp); + } + symlist->rnode = NULL; +} + +#define pool_size d.dl +#define freei x.xi +static INSTRUCTION *pool_list; + +/* INSTR_CHUNK must be > largest code size (3) */ +#define INSTR_CHUNK 127 + +/* bcfree --- deallocate instruction */ + +void +bcfree(INSTRUCTION *cp) +{ + cp->opcode = 0; + cp->nexti = pool_list->freei; + pool_list->freei = cp; +} + +/* bcalloc --- allocate a new instruction */ + +INSTRUCTION * +bcalloc(OPCODE op, int size, int srcline) +{ + INSTRUCTION *cp; + + if (size > 1) { + /* wide instructions Op_rule, Op_func_call .. */ + emalloc(cp, INSTRUCTION *, (size + 1) * sizeof(INSTRUCTION), "bcalloc"); + cp->pool_size = size; + cp->nexti = pool_list->nexti; + pool_list->nexti = cp++; + } else { + INSTRUCTION *pool; + + pool = pool_list->freei; + if (pool == NULL) { + INSTRUCTION *last; + emalloc(cp, INSTRUCTION *, (INSTR_CHUNK + 1) * sizeof(INSTRUCTION), "bcalloc"); + + cp->pool_size = INSTR_CHUNK; + cp->nexti = pool_list->nexti; + pool_list->nexti = cp; + pool = ++cp; + last = &pool[INSTR_CHUNK - 1]; + for (; cp <= last; cp++) { + cp->opcode = 0; + cp->nexti = cp + 1; + } + --cp; + cp->nexti = NULL; + } + cp = pool; + pool_list->freei = cp->nexti; + } + + memset(cp, 0, size * sizeof(INSTRUCTION)); + cp->opcode = op; + cp->source_line = srcline; + return cp; +} + +/* new_context --- create a new execution context. */ + +AWK_CONTEXT * +new_context() +{ + AWK_CONTEXT *ctxt; + + emalloc(ctxt, AWK_CONTEXT *, sizeof(AWK_CONTEXT), "new_context"); + memset(ctxt, 0, sizeof(AWK_CONTEXT)); + ctxt->srcfiles.next = ctxt->srcfiles.prev = & ctxt->srcfiles; + ctxt->rule_list.opcode = Op_list; + ctxt->rule_list.lasti = & ctxt->rule_list; + return ctxt; +} + +/* set_context --- change current execution context. */ + +static void +set_context(AWK_CONTEXT *ctxt) +{ + pool_list = & ctxt->pools; + symbol_list = & ctxt->symbols; + srcfiles = & ctxt->srcfiles; + rule_list = & ctxt->rule_list; + install_func = ctxt->install_func; + curr_ctxt = ctxt; +} + +/* + * push_context: + * + * Switch to the given context after saving the current one. The set + * of active execution contexts forms a stack; the global or main context + * is at the bottom of the stack. + */ + +void +push_context(AWK_CONTEXT *ctxt) +{ + ctxt->prev = curr_ctxt; + /* save current source and sourceline */ + if (curr_ctxt != NULL) { + curr_ctxt->sourceline = sourceline; + curr_ctxt->source = source; + } + sourceline = 0; + source = NULL; + set_context(ctxt); + ctxt_level++; +} + +/* pop_context --- switch to previous execution context. */ + +void +pop_context() +{ + AWK_CONTEXT *ctxt; + + assert(curr_ctxt != NULL); + if (curr_ctxt->prev == NULL) + fatal(_("can not pop main context")); + ctxt = curr_ctxt->prev; + /* restore source and sourceline */ + sourceline = ctxt->sourceline; + source = ctxt->source; + set_context(ctxt); + ctxt_level--; +} + +/* in_main_context --- are we in the main context ? */ + +int +in_main_context() +{ + assert(ctxt_level > 0); + return (ctxt_level == 1); +} + +/* free_context --- free context structure and related data. */ + +void +free_context(AWK_CONTEXT *ctxt, int keep_globals) +{ + SRCFILE *s, *sn; + + if (ctxt == NULL) + return; + + assert(curr_ctxt != ctxt); + + /* free all code including function codes */ + + free_bcpool(& ctxt->pools); + + /* free symbols */ + + release_symbols(& ctxt->symbols, keep_globals); + + /* free srcfiles */ + + for (s = & ctxt->srcfiles; s != & ctxt->srcfiles; s = sn) { + sn = s->next; + if (s->stype != SRC_CMDLINE && s->stype != SRC_STDIN) + efree(s->fullpath); + efree(s->src); + efree(s); + } + + efree(ctxt); +} + +/* free_bc_internal --- free internal memory of an instruction. */ + +static void +free_bc_internal(INSTRUCTION *cp) +{ + NODE *m; + + switch(cp->opcode) { + case Op_func_call: + if (cp->func_name != NULL) + efree(cp->func_name); + break; + case Op_push_re: + case Op_match_rec: + case Op_match: + case Op_nomatch: + m = cp->memory; + if (m->re_reg != NULL) + refree(m->re_reg); + if (m->re_exp != NULL) + unref(m->re_exp); + if (m->re_text != NULL) + unref(m->re_text); + freenode(m); + break; + case Op_token: + /* token lost during error recovery in yyparse */ + if (cp->lextok != NULL) + efree(cp->lextok); + break; + case Op_push_i: + m = cp->memory; + unref(m); + break; + case Op_store_var: + m = cp->initval; + if (m != NULL) + unref(m); + break; + case Op_illegal: + cant_happen(); + default: + break; + } +} + +/* free_bcpool --- free list of instruction memory pools */ + +static void +free_bcpool(INSTRUCTION *pl) +{ + INSTRUCTION *pool, *tmp; + + for (pool = pl->nexti; pool != NULL; pool = tmp) { + INSTRUCTION *cp, *last; + long psiz; + psiz = pool->pool_size; + if (psiz == INSTR_CHUNK) + last = pool + psiz; + else + last = pool + 1; + for (cp = pool + 1; cp <= last ; cp++) { + if (cp->opcode != 0) + free_bc_internal(cp); + } + tmp = pool->nexti; + efree(pool); + } + memset(pl, 0, sizeof(INSTRUCTION)); +} diff --git a/test/ChangeLog b/test/ChangeLog index 711d8b20..c16e5ecf 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,7 @@ +2011-12-26 John Haque <j.eh@mchsi.com> + + * badargs.ok: Adjust for new and changed command line options. + 2011-12-26 Arnold D. Robbins <arnold@skeeve.com> * Makefile.am (rri1): New test. diff --git a/test/Makefile.am b/test/Makefile.am index f7d50b28..943dbd9f 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -217,6 +217,7 @@ EXTRA_DIST = \ fnarray.awk \ fnarray.ok \ fnarray2.awk \ + fnarray2.in \ fnarray2.ok \ fnarydel.awk \ fnarydel.ok \ @@ -1406,19 +1407,19 @@ dumpvars:: profile1: @echo $@ - @$(AWK) --profile=ap-$@.out -f $(srcdir)/xref.awk $(srcdir)/dtdgport.awk > _$@.out1 + @$(AWK) --pretty-print=ap-$@.out -f $(srcdir)/xref.awk $(srcdir)/dtdgport.awk > _$@.out1 @$(AWK) -f ap-$@.out $(srcdir)/dtdgport.awk > _$@.out2 ; rm ap-$@.out @cmp _$@.out1 _$@.out2 && rm _$@.out[12] || echo EXIT CODE: $$? >>_$@ profile2: @echo $@ - @$(PGAWK) --profile=ap-$@.out -v sortcmd=sort -f $(srcdir)/xref.awk $(srcdir)/dtdgport.awk > /dev/null + @$(AWK) --profile=ap-$@.out -v sortcmd=sort -f $(srcdir)/xref.awk $(srcdir)/dtdgport.awk > /dev/null @sed 1,2d < ap-$@.out > _$@; rm ap-$@.out @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ profile3: @echo $@ - @$(PGAWK) --profile=ap-$@.out -f $(srcdir)/$@.awk > /dev/null + @$(AWK) --profile=ap-$@.out -f $(srcdir)/$@.awk > /dev/null @sed 1,2d < ap-$@.out > _$@; rm ap-$@.out @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ diff --git a/test/Makefile.in b/test/Makefile.in index 05946f32..a389929a 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -401,6 +401,7 @@ EXTRA_DIST = \ fnarray.awk \ fnarray.ok \ fnarray2.awk \ + fnarray2.in \ fnarray2.ok \ fnarydel.awk \ fnarydel.ok \ @@ -1756,19 +1757,19 @@ dumpvars:: profile1: @echo $@ - @$(AWK) --profile=ap-$@.out -f $(srcdir)/xref.awk $(srcdir)/dtdgport.awk > _$@.out1 + @$(AWK) --pretty-print=ap-$@.out -f $(srcdir)/xref.awk $(srcdir)/dtdgport.awk > _$@.out1 @$(AWK) -f ap-$@.out $(srcdir)/dtdgport.awk > _$@.out2 ; rm ap-$@.out @cmp _$@.out1 _$@.out2 && rm _$@.out[12] || echo EXIT CODE: $$? >>_$@ profile2: @echo $@ - @$(PGAWK) --profile=ap-$@.out -v sortcmd=sort -f $(srcdir)/xref.awk $(srcdir)/dtdgport.awk > /dev/null + @$(AWK) --profile=ap-$@.out -v sortcmd=sort -f $(srcdir)/xref.awk $(srcdir)/dtdgport.awk > /dev/null @sed 1,2d < ap-$@.out > _$@; rm ap-$@.out @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ profile3: @echo $@ - @$(PGAWK) --profile=ap-$@.out -f $(srcdir)/$@.awk > /dev/null + @$(AWK) --profile=ap-$@.out -f $(srcdir)/$@.awk > /dev/null @sed 1,2d < ap-$@.out > _$@; rm ap-$@.out @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ @@ -2036,7 +2037,7 @@ fnarray: fnarray2: @echo fnarray2 - @AWKPATH=$(srcdir) $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @AWKPATH=$(srcdir) $(AWK) -f $@.awk < $(srcdir)/$@.in >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ fnaryscl: diff --git a/test/Maketests b/test/Maketests index c76769f4..5c1a6b38 100644 --- a/test/Maketests +++ b/test/Maketests @@ -242,7 +242,7 @@ fnarray: fnarray2: @echo fnarray2 - @AWKPATH=$(srcdir) $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @AWKPATH=$(srcdir) $(AWK) -f $@.awk < $(srcdir)/$@.in >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ fnaryscl: diff --git a/test/badargs.ok b/test/badargs.ok index 66e67b03..cb140161 100644 --- a/test/badargs.ok +++ b/test/badargs.ok @@ -10,13 +10,16 @@ Short options: GNU long options: (extensions) -c --traditional -C --copyright -d[file] --dump-variables[=file] + -D[file] --debug[=file] -e 'program-text' --source='program-text' -E file --exec=file -g --gen-pot -h --help + -l library --load=library -L [fatal] --lint[=fatal] -n --non-decimal-data -N --use-lc-numeric + -o[file] --pretty-print[=file] -O --optimize -p[file] --profile[=file] -P --posix diff --git a/test/delfunc.ok b/test/delfunc.ok index d12f0bc9..29a7450d 100644 --- a/test/delfunc.ok +++ b/test/delfunc.ok @@ -1,2 +1,3 @@ -gawk: delfunc.awk:4: fatal: attempt to use function `f' as an array -EXIT CODE: 2 +gawk: delfunc.awk:4: error: function `f' called with space between name and `(', +or used as a variable or an array +EXIT CODE: 1 diff --git a/test/fnamedat.ok b/test/fnamedat.ok index d32acff4..d7b71c41 100644 --- a/test/fnamedat.ok +++ b/test/fnamedat.ok @@ -1,2 +1,3 @@ -gawk: fnamedat.awk:1: (FILENAME=- FNR=1) fatal: can't use function name `foo' as variable or array -EXIT CODE: 2 +gawk: fnamedat.awk:1: error: function `foo' called with space between name and `(', +or used as a variable or an array +EXIT CODE: 1 diff --git a/test/fnarray.ok b/test/fnarray.ok index 04260b0f..6cab9134 100644 --- a/test/fnarray.ok +++ b/test/fnarray.ok @@ -1,5 +1,3 @@ -gawk: fnarray.awk:5: Num = foo[c] -gawk: fnarray.awk:5: ^ use of non-array as array gawk: fnarray.awk:5: error: function `foo' called with space between name and `(', or used as a variable or an array EXIT CODE: 1 diff --git a/test/fnarray2.in b/test/fnarray2.in new file mode 100644 index 00000000..587be6b4 --- /dev/null +++ b/test/fnarray2.in @@ -0,0 +1 @@ +x diff --git a/test/fnarray2.ok b/test/fnarray2.ok index 243e4cc3..82815055 100644 --- a/test/fnarray2.ok +++ b/test/fnarray2.ok @@ -1,3 +1,3 @@ -gawk: fnarray2.awk:3: r = ++pile[c] -gawk: fnarray2.awk:3: ^ use of non-array as array +gawk: fnarray2.awk:3: error: function `pile' called with space between name and `(', +or used as a variable or an array EXIT CODE: 1 diff --git a/test/fnarydel.ok b/test/fnarydel.ok index 7f3e4531..7078c015 100644 --- a/test/fnarydel.ok +++ b/test/fnarydel.ok @@ -1,24 +1,24 @@ first loop +1 +2 +3 4 5 6 7 8 9 +second loop +third loop 1 2 3 -second loop -third loop 4 5 6 7 8 9 -1 -2 -3 call func fourth loop You should just see: 4 4 diff --git a/test/fnasgnm.ok b/test/fnasgnm.ok index 0db5c6d8..5cacff27 100644 --- a/test/fnasgnm.ok +++ b/test/fnasgnm.ok @@ -1,2 +1,3 @@ -gawk: fnasgnm.awk:14: (FILENAME=- FNR=1) fatal: can't use function name `ShowMe' as variable or array -EXIT CODE: 2 +gawk: fnasgnm.awk:14: error: function `ShowMe' called with space between name and `(', +or used as a variable or an array +EXIT CODE: 1 diff --git a/test/fnparydl.ok b/test/fnparydl.ok index 26a5c390..9f798224 100644 --- a/test/fnparydl.ok +++ b/test/fnparydl.ok @@ -1,10 +1,10 @@ BEFORE LOOP +DELETING KEY 1 +DELETING KEY 2 +DELETING KEY 3 DELETING KEY 4 DELETING KEY 5 DELETING KEY 6 DELETING KEY 7 -DELETING KEY 1 -DELETING KEY 2 -DELETING KEY 3 AFTER LOOP 0 elements still in q[] diff --git a/test/funsmnam.ok b/test/funsmnam.ok index e4f2174a..cce0d275 100644 --- a/test/funsmnam.ok +++ b/test/funsmnam.ok @@ -1,2 +1,2 @@ -gawk: funsmnam.awk:1: error: function `foo': can't use function name as parameter name +gawk: funsmnam.awk:2: error: function `foo': can't use function name as parameter name EXIT CODE: 1 diff --git a/test/gsubasgn.ok b/test/gsubasgn.ok index 8817c36d..8a309c7c 100644 --- a/test/gsubasgn.ok +++ b/test/gsubasgn.ok @@ -1,5 +1,5 @@ -gawk: gsubasgn.awk:4: function test1 (r) { gsub(r, "x", test1) } -gawk: gsubasgn.awk:4: ^ gsub third parameter is not a changeable object -gawk: gsubasgn.awk:8: function test2 () { gsub(/a/, "x", test2) } -gawk: gsubasgn.awk:8: ^ gsub third parameter is not a changeable object +gawk: gsubasgn.awk:4: error: function `test1' called with space between name and `(', +or used as a variable or an array +gawk: gsubasgn.awk:8: error: function `test2' called with space between name and `(', +or used as a variable or an array EXIT CODE: 1 diff --git a/test/match2.ok b/test/match2.ok index a4a91e85..ad2e324c 100644 --- a/test/match2.ok +++ b/test/match2.ok @@ -1,2 +1,3 @@ -gawk: match2.awk:3: fatal: match: third argument is not an array -EXIT CODE: 2 +gawk: match2.awk:3: error: function `f' called with space between name and `(', +or used as a variable or an array +EXIT CODE: 1 @@ -1,3 +1,3 @@ #include "config.h" -const char *version_string = "GNU Awk 4.0.0g"; +const char *version_string = "GNU Awk 4.0.70"; |