diff options
-rw-r--r-- | ChangeLog | 25 | ||||
-rw-r--r-- | awk.h | 4 | ||||
-rw-r--r-- | builtin.c | 36 | ||||
-rw-r--r-- | ext.c | 12 | ||||
-rw-r--r-- | extension/ChangeLog | 5 | ||||
-rw-r--r-- | extension/Makefile.am | 18 | ||||
-rw-r--r-- | extension/Makefile.in | 47 | ||||
-rw-r--r-- | extension/aclocal.m4 | 1 | ||||
-rw-r--r-- | extension/configh.in | 11 | ||||
-rwxr-xr-x | extension/configure | 79 | ||||
-rw-r--r-- | extension/configure.ac | 2 | ||||
-rw-r--r-- | extension/m4/ChangeLog | 4 | ||||
-rw-r--r-- | extension/m4/intlmacosx.m4 | 51 | ||||
-rw-r--r-- | extension/readdir.3am | 17 | ||||
-rw-r--r-- | extension/readdir.c | 24 | ||||
-rw-r--r-- | extension/revoutput.c | 136 | ||||
-rw-r--r-- | extension/revtwoway.c | 335 | ||||
-rw-r--r-- | gawkapi.c | 42 | ||||
-rw-r--r-- | gawkapi.h | 72 | ||||
-rw-r--r-- | io.c | 265 | ||||
-rw-r--r-- | test/ChangeLog | 5 | ||||
-rw-r--r-- | test/Makefile.am | 6 | ||||
-rw-r--r-- | test/Makefile.in | 16 | ||||
-rw-r--r-- | test/Maketests | 10 | ||||
-rw-r--r-- | test/revout.awk | 6 | ||||
-rw-r--r-- | test/revout.ok | 1 | ||||
-rw-r--r-- | test/revtwoway.awk | 11 | ||||
-rw-r--r-- | test/revtwoway.ok | 2 |
28 files changed, 1149 insertions, 94 deletions
@@ -1,3 +1,28 @@ +2012-08-22 Arnold D. Robbins <arnold@skeeve.com> + + Add output wrapper and two-way processor to extension API. + + * awk.h (struct redirect): Replace output FILE * with awk_output_buf_t. + (register_output_wrapper, register_two_way_processor): Declare. + * builtin.c (efwrite): Adjust logic to use rp->output data and + functions if rp is not NULL. Remove redundant declaration of function. + (do_fflush, do_printf, do_print, do_print_rec): Same adjustment. + * ext.c (make_builtin): Adjust error messages. + * gawkapi.c (api_register_output_wrapper, + api_register_two_way_processor): New functions. + (sym_update_real): Adjust code formatting. + * gawkapi.h (awk_input_parser_t): Make next pointer awk_const. + (awk_output_buf_t, awk_two_way_processor_t): New structs. + (api_register_output_wrapper, api_register_two_way_processor): New APIs. + (dl_load_func): Allow for empty function table (NULL elements). + * io.c (find_output_wrapper, init_output_wrapper, find_two_processor, + gawk_fwrite, gawk_ferror, gawk_fflush, gawk_fclose): New functions. + (redirect): Call init_output_wrapper, find_output_wrapper as needed. + Adjust use of rp->fp to rp->output.fp and also function calls. + (close_rp, close_redir, flush_io): Same adjustment. + (two_way_open): Same adjustment. Call find_two_way_processor, and + find_output_wrapper, as needed. + 2012-08-17 Arnold D. Robbins <arnold@skeeve.com> * Update infrastructure: Automake 1.12.3 and bison 2.6.2. @@ -933,7 +933,6 @@ struct redirect { # define RED_SOCKET 1024 # define RED_TCP 2048 char *value; - FILE *fp; FILE *ifp; /* input fp, needed for PIPES_SIMULATED */ IOBUF *iop; int pid; @@ -941,6 +940,7 @@ struct redirect { struct redirect *prev; struct redirect *next; const char *mode; + awk_output_buf_t output; }; /* @@ -1543,6 +1543,8 @@ extern int isdirpunct(int c); /* io.c */ extern void init_io(void); extern void register_input_parser(awk_input_parser_t *input_parser); +extern void register_output_wrapper(awk_output_wrapper_t *wrapper); +extern void register_two_way_processor(awk_two_way_processor_t *processor); extern void set_FNR(void); extern void set_NR(void); @@ -92,9 +92,6 @@ fatal(_("attempt to use array `%s' in a scalar context"), array_vname(s1)); \ */ #define GAWK_RANDOM_MAX 0x7fffffffL -static void efwrite(const void *ptr, size_t size, size_t count, FILE *fp, - const char *from, struct redirect *rp, bool flush); - /* efwrite --- like fwrite, but with error checking */ static void @@ -107,14 +104,23 @@ efwrite(const void *ptr, bool flush) { errno = 0; - if (fwrite(ptr, size, count, fp) != count) + if (rp != NULL) { + if (rp->output.gawk_fwrite(ptr, size, count, fp, rp->output.opaque) != count) + goto wrerror; + } else if (fwrite(ptr, size, count, fp) != count) goto wrerror; if (flush && ((fp == stdout && output_is_tty) || (rp != NULL && (rp->flag & RED_NOBUF)))) { - fflush(fp); - if (ferror(fp)) - goto wrerror; + if (rp != NULL) { + rp->output.gawk_fflush(fp, rp->output.opaque); + if (rp->output.gawk_ferror(fp, rp->output.opaque)) + goto wrerror; + } else { + fflush(fp); + if (ferror(fp)) + goto wrerror; + } } return; @@ -207,9 +213,9 @@ do_fflush(int nargs) DEREF(tmp); return make_number((AWKNUM) status); } - fp = rp->fp; + fp = rp->output.fp; if (fp != NULL) - status = fflush(fp); + status = rp->output.gawk_fflush(fp, rp->output.opaque); } else if ((fp = stdfile(tmp->stptr, tmp->stlen)) != NULL) { status = fflush(fp); } else { @@ -1615,7 +1621,7 @@ do_printf(int nargs, int redirtype) fatal(_("attempt to use array `%s' in a scalar context"), array_vname(redir_exp)); rp = redirect(redir_exp, redirtype, & errflg); if (rp != NULL) - fp = rp->fp; + fp = rp->output.fp; } else fp = output_fp; @@ -1631,7 +1637,7 @@ do_printf(int nargs, int redirtype) } efwrite(tmp->stptr, sizeof(char), tmp->stlen, fp, "printf", rp, true); if (rp != NULL && (rp->flag & RED_TWOWAY) != 0) - fflush(rp->fp); + rp->output.gawk_fflush(rp->output.fp, rp->output.opaque); DEREF(tmp); } else gawk_exit(EXIT_FATAL); @@ -2041,7 +2047,7 @@ do_print(int nargs, int redirtype) fatal(_("attempt to use array `%s' in a scalar context"), array_vname(redir_exp)); rp = redirect(redir_exp, redirtype, & errflg); if (rp != NULL) - fp = rp->fp; + fp = rp->output.fp; } else fp = output_fp; @@ -2084,7 +2090,7 @@ do_print(int nargs, int redirtype) efwrite(ORS, sizeof(char), (size_t) ORSlen, fp, "print", rp, true); if (rp != NULL && (rp->flag & RED_TWOWAY) != 0) - fflush(rp->fp); + rp->output.gawk_fflush(rp->output.fp, rp->output.opaque); } /* do_print_rec --- special case printing of $0, for speed */ @@ -2103,7 +2109,7 @@ do_print_rec(int nargs, int redirtype) redir_exp = TOP(); rp = redirect(redir_exp, redirtype, & errflg); if (rp != NULL) - fp = rp->fp; + fp = rp->output.fp; DEREF(redir_exp); decr_sp(); } else @@ -2126,7 +2132,7 @@ do_print_rec(int nargs, int redirtype) efwrite(ORS, sizeof(char), (size_t) ORSlen, fp, "print", rp, true); if (rp != NULL && (rp->flag & RED_TWOWAY) != 0) - fflush(rp->fp); + rp->output.gawk_fflush(rp->output.fp, rp->output.opaque); } #if MBS_SUPPORT @@ -90,12 +90,12 @@ make_builtin(const awk_ext_func_t *funcinfo) sp = name; if (sp == NULL || *sp == '\0') - fatal(_("extension: missing function name")); + fatal(_("make_builtin: missing function name")); while ((c = *sp++) != '\0') { if ((sp == &name[1] && c != '_' && ! isalpha((unsigned char) c)) || (sp > &name[1] && ! is_identchar((unsigned char) c))) - fatal(_("extension: illegal character `%c' in function name `%s'"), c, name); + fatal(_("make_builtin: illegal character `%c' in function name `%s'"), c, name); } f = lookup(name); @@ -103,17 +103,17 @@ make_builtin(const awk_ext_func_t *funcinfo) if (f != NULL) { if (f->type == Node_func) { /* user-defined function */ - fatal(_("extension: can't redefine function `%s'"), name); + fatal(_("make_builtin: 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); + lintwarn(_("make_builtin: function `%s' already defined"), name); return false; } else /* variable name etc. */ - fatal(_("extension: function name `%s' previously defined"), name); + fatal(_("make_builtin: 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); + fatal(_("make_builtin: can't use gawk built-in `%s' as function name"), name); if (count < 0) fatal(_("make_builtin: negative argument count for function `%s'"), diff --git a/extension/ChangeLog b/extension/ChangeLog index bc4b060b..0a15220a 100644 --- a/extension/ChangeLog +++ b/extension/ChangeLog @@ -1,3 +1,8 @@ +2012-08-22 Arnold D. Robbins <arnold@skeeve.com> + + * revoutput.c: New testing extension for output wrapper. + * Makefile.am: Build revoutput extension. + 2012-08-08 Arnold D. Robbins <arnold@skeeve.com> Add fts() to filefuncs. diff --git a/extension/Makefile.am b/extension/Makefile.am index bf9a715f..f7d939a4 100644 --- a/extension/Makefile.am +++ b/extension/Makefile.am @@ -38,6 +38,8 @@ pkgextension_LTLIBRARIES = \ ordchr.la \ readdir.la \ readfile.la \ + revoutput.la \ + revtwoway.la \ rwarray.la \ testext.la \ time.la @@ -49,27 +51,43 @@ MY_LIBS = $(LIBINTL) filefuncs_la_SOURCES = filefuncs.c stack.h stack.c filefuncs_la_LDFLAGS = $(MY_MODULE_FLAGS) filefuncs_la_LIBADD = $(MY_LIBS) + fnmatch_la_SOURCES = fnmatch.c fnmatch_la_LDFLAGS = $(MY_MODULE_FLAGS) fnmatch_la_LIBADD = $(MY_LIBS) + fork_la_SOURCES = fork.c fork_la_LDFLAGS = $(MY_MODULE_FLAGS) fork_la_LIBADD = $(MY_LIBS) + ordchr_la_SOURCES = ordchr.c ordchr_la_LDFLAGS = $(MY_MODULE_FLAGS) ordchr_la_LIBADD = $(MY_LIBS) + readdir_la_SOURCES = readdir.c readdir_la_LDFLAGS = $(MY_MODULE_FLAGS) readdir_la_LIBADD = $(MY_LIBS) + readfile_la_SOURCES = readfile.c readfile_la_LDFLAGS = $(MY_MODULE_FLAGS) readfile_la_LIBADD = $(MY_LIBS) + +revoutput_la_SOURCES = revoutput.c +revoutput_la_LDFLAGS = $(MY_MODULE_FLAGS) +revoutput_la_LIBADD = $(MY_LIBS) + +revtwoway_la_SOURCES = revtwoway.c +revtwoway_la_LDFLAGS = $(MY_MODULE_FLAGS) +revtwoway_la_LIBADD = $(MY_LIBS) + rwarray_la_SOURCES = rwarray.c rwarray_la_LDFLAGS = $(MY_MODULE_FLAGS) rwarray_la_LIBADD = $(MY_LIBS) + time_la_SOURCES = time.c time_la_LDFLAGS = $(MY_MODULE_FLAGS) time_la_LIBADD = $(MY_LIBS) + testext_la_SOURCES = testext.c testext_la_LDFLAGS = $(MY_MODULE_FLAGS) testext_la_LIBADD = $(MY_LIBS) diff --git a/extension/Makefile.in b/extension/Makefile.in index 59ead43b..bebc006b 100644 --- a/extension/Makefile.in +++ b/extension/Makefile.in @@ -90,13 +90,13 @@ DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ build-aux/install-sh build-aux/ltmain.sh build-aux/missing ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ - $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \ - $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ - $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ - $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ - $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ @@ -173,6 +173,18 @@ readfile_la_OBJECTS = $(am_readfile_la_OBJECTS) readfile_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(readfile_la_LDFLAGS) $(LDFLAGS) -o $@ +revoutput_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_revoutput_la_OBJECTS = revoutput.lo +revoutput_la_OBJECTS = $(am_revoutput_la_OBJECTS) +revoutput_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(revoutput_la_LDFLAGS) $(LDFLAGS) -o $@ +revtwoway_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_revtwoway_la_OBJECTS = revtwoway.lo +revtwoway_la_OBJECTS = $(am_revtwoway_la_OBJECTS) +revtwoway_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(revtwoway_la_LDFLAGS) $(LDFLAGS) -o $@ rwarray_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_rwarray_la_OBJECTS = rwarray.lo rwarray_la_OBJECTS = $(am_rwarray_la_OBJECTS) @@ -206,11 +218,13 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ $(LDFLAGS) -o $@ SOURCES = $(filefuncs_la_SOURCES) $(fnmatch_la_SOURCES) \ $(fork_la_SOURCES) $(ordchr_la_SOURCES) $(readdir_la_SOURCES) \ - $(readfile_la_SOURCES) $(rwarray_la_SOURCES) \ + $(readfile_la_SOURCES) $(revoutput_la_SOURCES) \ + $(revtwoway_la_SOURCES) $(rwarray_la_SOURCES) \ $(testext_la_SOURCES) $(time_la_SOURCES) DIST_SOURCES = $(filefuncs_la_SOURCES) $(fnmatch_la_SOURCES) \ $(fork_la_SOURCES) $(ordchr_la_SOURCES) $(readdir_la_SOURCES) \ - $(readfile_la_SOURCES) $(rwarray_la_SOURCES) \ + $(readfile_la_SOURCES) $(revoutput_la_SOURCES) \ + $(revtwoway_la_SOURCES) $(rwarray_la_SOURCES) \ $(testext_la_SOURCES) $(time_la_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ @@ -312,6 +326,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ @@ -422,6 +437,8 @@ pkgextension_LTLIBRARIES = \ ordchr.la \ readdir.la \ readfile.la \ + revoutput.la \ + revtwoway.la \ rwarray.la \ testext.la \ time.la @@ -447,6 +464,12 @@ readdir_la_LIBADD = $(MY_LIBS) readfile_la_SOURCES = readfile.c readfile_la_LDFLAGS = $(MY_MODULE_FLAGS) readfile_la_LIBADD = $(MY_LIBS) +revoutput_la_SOURCES = revoutput.c +revoutput_la_LDFLAGS = $(MY_MODULE_FLAGS) +revoutput_la_LIBADD = $(MY_LIBS) +revtwoway_la_SOURCES = revtwoway.c +revtwoway_la_LDFLAGS = $(MY_MODULE_FLAGS) +revtwoway_la_LIBADD = $(MY_LIBS) rwarray_la_SOURCES = rwarray.c rwarray_la_LDFLAGS = $(MY_MODULE_FLAGS) rwarray_la_LIBADD = $(MY_LIBS) @@ -567,6 +590,10 @@ readdir.la: $(readdir_la_OBJECTS) $(readdir_la_DEPENDENCIES) $(EXTRA_readdir_la_ $(readdir_la_LINK) -rpath $(pkgextensiondir) $(readdir_la_OBJECTS) $(readdir_la_LIBADD) $(LIBS) readfile.la: $(readfile_la_OBJECTS) $(readfile_la_DEPENDENCIES) $(EXTRA_readfile_la_DEPENDENCIES) $(readfile_la_LINK) -rpath $(pkgextensiondir) $(readfile_la_OBJECTS) $(readfile_la_LIBADD) $(LIBS) +revoutput.la: $(revoutput_la_OBJECTS) $(revoutput_la_DEPENDENCIES) $(EXTRA_revoutput_la_DEPENDENCIES) + $(revoutput_la_LINK) -rpath $(pkgextensiondir) $(revoutput_la_OBJECTS) $(revoutput_la_LIBADD) $(LIBS) +revtwoway.la: $(revtwoway_la_OBJECTS) $(revtwoway_la_DEPENDENCIES) $(EXTRA_revtwoway_la_DEPENDENCIES) + $(revtwoway_la_LINK) -rpath $(pkgextensiondir) $(revtwoway_la_OBJECTS) $(revtwoway_la_LIBADD) $(LIBS) rwarray.la: $(rwarray_la_OBJECTS) $(rwarray_la_DEPENDENCIES) $(EXTRA_rwarray_la_DEPENDENCIES) $(rwarray_la_LINK) -rpath $(pkgextensiondir) $(rwarray_la_OBJECTS) $(rwarray_la_LIBADD) $(LIBS) testext.la: $(testext_la_OBJECTS) $(testext_la_DEPENDENCIES) $(EXTRA_testext_la_DEPENDENCIES) @@ -586,6 +613,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ordchr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readdir.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readfile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/revoutput.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/revtwoway.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rwarray.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testext.Plo@am__quote@ diff --git a/extension/aclocal.m4 b/extension/aclocal.m4 index db5d9f86..18cab755 100644 --- a/extension/aclocal.m4 +++ b/extension/aclocal.m4 @@ -1003,6 +1003,7 @@ AC_SUBST([am__untar]) m4_include([m4/gettext.m4]) m4_include([m4/iconv.m4]) +m4_include([m4/intlmacosx.m4]) m4_include([m4/lib-ld.m4]) m4_include([m4/lib-link.m4]) m4_include([m4/lib-prefix.m4]) diff --git a/extension/configh.in b/extension/configh.in index 73ce026c..571b7a7e 100644 --- a/extension/configh.in +++ b/extension/configh.in @@ -4,6 +4,14 @@ language is requested. */ #undef ENABLE_NLS +/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the + CoreFoundation framework. */ +#undef HAVE_CFLOCALECOPYCURRENT + +/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in + the CoreFoundation framework. */ +#undef HAVE_CFPREFERENCESCOPYAPPVALUE + /* Define if the GNU dcgettext() function is already present or preinstalled. */ #undef HAVE_DCGETTEXT @@ -32,6 +40,9 @@ /* Define to 1 if you have the `fts_read' function. */ #undef HAVE_FTS_READ +/* Define to 1 if you have the `getdtablesize' function. */ +#undef HAVE_GETDTABLESIZE + /* Define to 1 if you have the `GetSystemTimeAsFileTime' function. */ #undef HAVE_GETSYSTEMTIMEASFILETIME diff --git a/extension/configure b/extension/configure index 732bfc93..7030c8b1 100755 --- a/extension/configure +++ b/extension/configure @@ -662,6 +662,7 @@ LIBINTL INTLLIBS LTLIBICONV LIBICONV +INTL_MACOSX_LIBS EGREP GREP CPP @@ -5090,7 +5091,81 @@ fi - gt_INTL_MACOSX + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFPreferencesCopyAppValue" >&5 +$as_echo_n "checking for CFPreferencesCopyAppValue... " >&6; } +if ${gt_cv_func_CFPreferencesCopyAppValue+:} false; then : + $as_echo_n "(cached) " >&6 +else + gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <CoreFoundation/CFPreferences.h> +int +main () +{ +CFPreferencesCopyAppValue(NULL, NULL) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + gt_cv_func_CFPreferencesCopyAppValue=yes +else + gt_cv_func_CFPreferencesCopyAppValue=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$gt_save_LIBS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFPreferencesCopyAppValue" >&5 +$as_echo "$gt_cv_func_CFPreferencesCopyAppValue" >&6; } + if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then + +$as_echo "#define HAVE_CFPREFERENCESCOPYAPPVALUE 1" >>confdefs.h + + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFLocaleCopyCurrent" >&5 +$as_echo_n "checking for CFLocaleCopyCurrent... " >&6; } +if ${gt_cv_func_CFLocaleCopyCurrent+:} false; then : + $as_echo_n "(cached) " >&6 +else + gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <CoreFoundation/CFLocale.h> +int +main () +{ +CFLocaleCopyCurrent(); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + gt_cv_func_CFLocaleCopyCurrent=yes +else + gt_cv_func_CFLocaleCopyCurrent=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$gt_save_LIBS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFLocaleCopyCurrent" >&5 +$as_echo "$gt_cv_func_CFLocaleCopyCurrent" >&6; } + if test $gt_cv_func_CFLocaleCopyCurrent = yes; then + +$as_echo "#define HAVE_CFLOCALECOPYCURRENT 1" >>confdefs.h + + fi + INTL_MACOSX_LIBS= + if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then + INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation" + fi + + @@ -13453,7 +13528,7 @@ done for ac_func in fdopendir fnmatch fts_open fts_read gettimeofday \ - nanosleep select GetSystemTimeAsFileTime + getdtablesize nanosleep select GetSystemTimeAsFileTime do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff --git a/extension/configure.ac b/extension/configure.ac index 33009611..4869584b 100644 --- a/extension/configure.ac +++ b/extension/configure.ac @@ -51,7 +51,7 @@ fi AC_CHECK_HEADERS(dirent.h fnmatch.h fts.h time.h sys/time.h sys/select.h) AC_CHECK_FUNCS(fdopendir fnmatch fts_open fts_read gettimeofday \ - nanosleep select GetSystemTimeAsFileTime) + getdtablesize nanosleep select GetSystemTimeAsFileTime) dnl checks for compiler characteristics AC_C_INLINE diff --git a/extension/m4/ChangeLog b/extension/m4/ChangeLog index 9ce57384..c1223391 100644 --- a/extension/m4/ChangeLog +++ b/extension/m4/ChangeLog @@ -1,3 +1,7 @@ +2012-08-24 Arnold D. Robbins <arnold@skeeve.com> + + * intlmacosx.m4: New file. + 2012-07-30 Arnold D. Robbins <arnold@skeeve.com> * gettext.m4, iconv.m4, lib-ld.m4, lib-link.m4, lib-prefix.m4, diff --git a/extension/m4/intlmacosx.m4 b/extension/m4/intlmacosx.m4 new file mode 100644 index 00000000..dd910259 --- /dev/null +++ b/extension/m4/intlmacosx.m4 @@ -0,0 +1,51 @@ +# intlmacosx.m4 serial 3 (gettext-0.18) +dnl Copyright (C) 2004-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl This file can can be used in projects which are not available under +dnl the GNU General Public License or the GNU Library General Public +dnl License but which still want to provide support for the GNU gettext +dnl functionality. +dnl Please note that the actual code of the GNU gettext library is covered +dnl by the GNU Library General Public License, and the rest of the GNU +dnl gettext package package is covered by the GNU General Public License. +dnl They are *not* in the public domain. + +dnl Checks for special options needed on MacOS X. +dnl Defines INTL_MACOSX_LIBS. +AC_DEFUN([gt_INTL_MACOSX], +[ + dnl Check for API introduced in MacOS X 10.2. + AC_CACHE_CHECK([for CFPreferencesCopyAppValue], + [gt_cv_func_CFPreferencesCopyAppValue], + [gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + AC_TRY_LINK([#include <CoreFoundation/CFPreferences.h>], + [CFPreferencesCopyAppValue(NULL, NULL)], + [gt_cv_func_CFPreferencesCopyAppValue=yes], + [gt_cv_func_CFPreferencesCopyAppValue=no]) + LIBS="$gt_save_LIBS"]) + if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then + AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], [1], + [Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in the CoreFoundation framework.]) + fi + dnl Check for API introduced in MacOS X 10.3. + AC_CACHE_CHECK([for CFLocaleCopyCurrent], [gt_cv_func_CFLocaleCopyCurrent], + [gt_save_LIBS="$LIBS" + LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation" + AC_TRY_LINK([#include <CoreFoundation/CFLocale.h>], [CFLocaleCopyCurrent();], + [gt_cv_func_CFLocaleCopyCurrent=yes], + [gt_cv_func_CFLocaleCopyCurrent=no]) + LIBS="$gt_save_LIBS"]) + if test $gt_cv_func_CFLocaleCopyCurrent = yes; then + AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], [1], + [Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the CoreFoundation framework.]) + fi + INTL_MACOSX_LIBS= + if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then + INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation" + fi + AC_SUBST([INTL_MACOSX_LIBS]) +]) diff --git a/extension/readdir.3am b/extension/readdir.3am index 5f6ae06f..9cb4862e 100644 --- a/extension/readdir.3am +++ b/extension/readdir.3am @@ -1,4 +1,4 @@ -.TH READDIR 3am "Aug 08 2012" "Free Software Foundation" "GNU Awk Extension Modules" +.TH READDIR 3am "Aug 23 2012" "Free Software Foundation" "GNU Awk Extension Modules" .SH NAME readdir \- directory input parser for gawk .SH SYNOPSIS @@ -49,7 +49,20 @@ causes the extension to use to retrieve the appropriate information. This is not the default, since .IR stat (2) is a potentially expensive operation. -... .SH NOTES +.SH NOTES +On GNU/Linux systems, there are filesystems that don't support the +.B d_type +entry (see +.IR readdir (3)), +and so the file type is always +.BR u . +Therefore, using +.B readdir_do_ftype(1) +is advisable even on GNU/Linux systems. In this case, the +.I readdir +extension will fall back to using +.IR stat (2) +when it encounters an unknown file type. ... .SH BUGS .SH EXAMPLE .ft CW diff --git a/extension/readdir.c b/extension/readdir.c index c28764e8..3eae4079 100644 --- a/extension/readdir.c +++ b/extension/readdir.c @@ -60,17 +60,15 @@ static awk_bool_t (*init_func)(void) = init_readdir; int plugin_is_GPL_compatible; -#ifdef DT_BLK -static const int do_ftype = 1; -#else static int do_ftype; -#endif /* ftype --- return type of file as a single character string */ static const char * ftype(struct dirent *entry) { + struct stat sbuf; + #ifdef DT_BLK switch (entry->d_type) { case DT_BLK: return "b"; @@ -81,12 +79,17 @@ ftype(struct dirent *entry) case DT_REG: return "f"; case DT_SOCK: return "s"; default: - case DT_UNKNOWN: return "u"; + case DT_UNKNOWN: + /* + * Could be that filesystem doesn't support d_type, + * even if the OS does. (E.g., XFS on GNU/Linux). + * So let lstat() do it. + */ + break; } -#else - struct stat sbuf; +#endif - if (lstat(entry->d_name, & sbuf) < 0) + if (! do_ftype || lstat(entry->d_name, & sbuf) < 0) return "u"; switch (sbuf.st_mode & S_IFMT) { @@ -108,9 +111,9 @@ ftype(struct dirent *entry) #endif /* S_IFDOOR */ } return "u"; -#endif } +/* data type for the opaque pointer: */ typedef struct open_directory { DIR *dp; @@ -138,6 +141,7 @@ dir_get_record(char **out, struct iobuf_public *iobuf, int *errcode, the_dir = (open_directory_t *) iobuf->opaque; dp = the_dir->dp; + /* * Initialize errno, since readdir does not set it to zero on EOF. */ @@ -280,9 +284,7 @@ do_readdir_do_ftype(int nargs, awk_value_t *result) goto out; } -#ifndef DT_BLK do_ftype = (flag.num_value != 0.0); -#endif out: return result; diff --git a/extension/revoutput.c b/extension/revoutput.c new file mode 100644 index 00000000..c1ea1ddc --- /dev/null +++ b/extension/revoutput.c @@ -0,0 +1,136 @@ +/* + * revoutput.c --- Provide an output wrapper that reverses lines. + * + * Arnold Robbins + * arnold@skeeve.com + * Written 8/2012 + */ + +/* + * Copyright (C) 2012 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "config.h" + +#include "gawkapi.h" + +#include "gettext.h" +#define _(msgid) gettext(msgid) +#define N_(msgid) msgid + +static const gawk_api_t *api; /* for convenience macros to work */ +static awk_ext_id_t *ext_id; + +static awk_bool_t init_revout(void); +static awk_bool_t (*init_func)(void) = init_revout; + +int plugin_is_GPL_compatible; + +/* rev_fwrite --- write out characters in reverse order */ + +static size_t +rev_fwrite(const void *buf, size_t size, size_t count, FILE *fp, void *opaque) +{ + const char *cp = buf; + int nbytes = size * count; + + (void) opaque; + + for (; nbytes >= 1; nbytes--) + putc(cp[nbytes-1], fp); + + return (size * count); +} + + +/* revout_can_take_file --- return true if we want the file */ + +static int +revout_can_take_file(const awk_output_buf_t *outbuf) +{ + awk_value_t value; + + if (outbuf == NULL) + return 0; + + if (! sym_lookup("REVOUT", AWK_NUMBER, & value)) + return 0; + + return (value.num_value != 0); +} + +/* + * revout_take_control_of --- set up output wrapper. + * We can assume that revout_can_take_file just returned true, + * and no state has changed since then. + */ + +static int +revout_take_control_of(awk_output_buf_t *outbuf) +{ + if (outbuf == NULL) + return 0; + + outbuf->gawk_fwrite = rev_fwrite; + outbuf->redirected = 1; + return 1; +} + +static awk_output_wrapper_t output_wrapper = { + "revout", + revout_can_take_file, + revout_take_control_of, + NULL +}; + +/* init_revout --- set things ups */ + +static awk_bool_t +init_revout() +{ + awk_value_t value; + + register_output_wrapper(& output_wrapper); + + make_number(0.0, & value); /* init to false */ + if (! sym_update("REVOUT", & value)) { + warning(ext_id, _("revout: could not initialize REVOUT variable")); + + return 0; + } + + return 1; +} + +static awk_ext_func_t func_table[] = { + { NULL, NULL, 0 } +}; + +/* define the dl_load function using the boilerplate macro */ + +dl_load_func(func_table, revout, "") diff --git a/extension/revtwoway.c b/extension/revtwoway.c new file mode 100644 index 00000000..7ab9366e --- /dev/null +++ b/extension/revtwoway.c @@ -0,0 +1,335 @@ +/* + * revtwoway.c --- Provide a two-way processor that reverses lines. + * + * Arnold Robbins + * arnold@skeeve.com + * Written 8/2012 + */ + +/* + * Copyright (C) 2012 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 <stdio.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include "config.h" + +#include "gawkapi.h" + +#include "gettext.h" +#define _(msgid) gettext(msgid) +#define N_(msgid) msgid + +static const gawk_api_t *api; /* for convenience macros to work */ +static awk_ext_id_t *ext_id; + +static awk_bool_t init_revtwoway(void); +static awk_bool_t (*init_func)(void) = init_revtwoway; + +int plugin_is_GPL_compatible; + +/* + * Use this variable to provide a value != INVALID_HANDLE in the IOBUF_PUBLIC + * and != NULL in the awk_output_buf_t. The idea is to have a value that + * is greater than the largest allowable file descriptor. + */ +static size_t max_fds; + +#ifndef HAVE_GETDTABLESIZE +/* getdtablesize --- replacement version that should be good enough */ + +static inline int +getdtablesize() +{ + /* + * Algorithm for the GNULIB folks: + * + * Set up a bitmap of 2048 elements. + * Initialize it to zero. + * In a loop, do + * fd = open("/dev/null", O_RDONLY) + * set the bit corresponding to fd in the bit map + * until it fails. + * Get the highest value that succeeded and increment it by one + * --> that is how many descriptors we have. + * Loop over the bitmap to close all the file descriptors we opened. + * + * Do all this upon the first call and return static values upon + * subsequent calls. + */ + + /* In the meantime, this is good enough for us: */ + return 1024; +} +#endif + +/* + * IMPORTANT NOTE: This is a NOT a true general purpose + * extension. It is intended to demonstrate how to set up + * all the "plumbing" and to work one record at a time, ONLY. + * + * While it would be possible to set up buffering and manage it, + * that would duplicate a large chunk of the code in gawk's + * get_a_record() function, and there's no real point in doing that. + */ + +/* the data in the opaque pointer */ +typedef struct two_way_proc_data { + size_t size; /* size of allocated buffer */ + size_t len; /* how much is actually in use */ + char *data; + size_t in_use; /* use count, must hit zero to be freed */ +} two_way_proc_data_t; + +/* close_two_proc_data --- release the data */ + +static void +close_two_proc_data(two_way_proc_data_t *proc_data) +{ + if (proc_data->in_use > 1) { + proc_data->in_use--; + return; + } + + free(proc_data->data); + free(proc_data); +} + +/* + * Input side of the two-way processor (input TO gawk) + */ + +/* rev2way_get_record --- get one record at a time out of a directory */ + +static int +rev2way_get_record(char **out, struct iobuf_public *iobuf, int *errcode, + char **rt_start, size_t *rt_len) +{ + int len = 0; /* for now */ + two_way_proc_data_t *proc_data; + + /* + * The caller sets *errcode to 0, so we should set it only if an + * error occurs. + */ + + if (out == NULL || iobuf == NULL || iobuf->opaque == NULL) + return EOF; + + proc_data = (two_way_proc_data_t *) iobuf->opaque; + if (proc_data->len == 0) + return 0; + + *out = proc_data->data; + + len = proc_data->len; + proc_data->len = 0; + + *rt_len = 0; /* default: set RT to "" */ + if (proc_data->data[len-1] == '\n') { + while (proc_data->data[len-1] == '\n') { + len--; + (*rt_len)++; + } + *rt_start = proc_data->data + len; + } + + return len; +} + +/* rev2way_close --- close up input side when done */ + +static void +rev2way_close(struct iobuf_public *iobuf) +{ + two_way_proc_data_t *proc_data; + + if (iobuf == NULL || iobuf->opaque == NULL) + return; + + proc_data = (two_way_proc_data_t *) iobuf->opaque; + close_two_proc_data(proc_data); + + iobuf->fd = INVALID_HANDLE; +} + + +/* + * Output side of the two-way processor (output FROM gawk) + */ + +/* rev2way_fwrite --- write out characters in reverse order */ + +static size_t +rev2way_fwrite(const void *buf, size_t size, size_t count, FILE *fp, void *opaque) +{ + two_way_proc_data_t *proc_data; + size_t amount, char_count; + char *src, *dest; + + if (opaque == NULL) + return 0; /* error */ + + proc_data = (two_way_proc_data_t *) opaque; + amount = size * count; + + /* do the dance */ + if (amount > proc_data->size || proc_data->len > 0) { + if (proc_data->data == NULL) + emalloc(proc_data->data, char *, amount, "rev2way_fwrite"); + else + erealloc(proc_data->data, char *, proc_data->size + amount, "rev2way_fwrite"); + proc_data->size += amount; + } + + src = (char *) buf + amount -1; + dest = proc_data->data + proc_data->len; + for (char_count = amount; char_count > 0; char_count--) { + /* copy in backwards */ + *dest++ = *src--; + } + proc_data->len += amount; + + return amount; +} + +/* rev2way_fflush --- do nothing hook for fflush */ + +static int +rev2way_fflush(FILE *fp, void *opaque) +{ + (void) fp; + (void) opaque; + + return 0; +} + +/* rev2way_ferror --- do nothing hook for ferror */ + +static int +rev2way_ferror(FILE *fp, void *opaque) +{ + (void) fp; + (void) opaque; + + return 0; +} + +/* rev2way_fclose --- close output side of two-way processor */ + +static int +rev2way_fclose(FILE *fp, void *opaque) +{ + two_way_proc_data_t *proc_data; + + if (opaque == NULL) + return EOF; /* error */ + + (void) fp; + + proc_data = (two_way_proc_data_t *) opaque; + close_two_proc_data(proc_data); + + return 0; +} + + +/* revtwoway_can_two_way --- return true if we want the file */ + +static int +revtwoway_can_take_two_way(const char *name) +{ + return (name != NULL && strcmp(name, "/magic/mirror") == 0); +} + +/* + * revtwoway_take_control_of --- set up two way processor + * We can assume that revtwoway_can_take_two_way just returned true, + * and no state has changed since then. + */ + +static int +revtwoway_take_control_of(const char *name, IOBUF_PUBLIC *inbuf, awk_output_buf_t *outbuf) +{ + two_way_proc_data_t *proc_data; + + if (inbuf == NULL || outbuf == NULL) + return 0; + + emalloc(proc_data, two_way_proc_data_t *, sizeof(two_way_proc_data_t), "revtwoway_take_control_of"); + proc_data->in_use = 2; + proc_data->size = 0; + proc_data->len = 0; + proc_data->data = NULL; + + if (max_fds + 1 == 0) /* wrapped. ha! */ + max_fds = getdtablesize(); + + /* input side: */ + inbuf->get_record = rev2way_get_record; + inbuf->close_func = rev2way_close; + inbuf->fd = max_fds; + inbuf->opaque = proc_data; + + /* output side: */ + outbuf->fp = (FILE *) max_fds++; + outbuf->opaque = proc_data; + outbuf->gawk_fwrite = rev2way_fwrite; + outbuf->gawk_fflush = rev2way_fflush; + outbuf->gawk_ferror = rev2way_ferror; + outbuf->gawk_fclose = rev2way_fclose; + outbuf->redirected = 1; + return 1; +} + +static awk_two_way_processor_t two_way_processor = { + "revtwoway", + revtwoway_can_take_two_way, + revtwoway_take_control_of, + NULL +}; + +/* init_revtwoway --- set things ups */ + +static awk_bool_t +init_revtwoway() +{ + register_two_way_processor(& two_way_processor); + + max_fds = getdtablesize(); + + return 1; +} + +static awk_ext_func_t func_table[] = { + { NULL, NULL, 0 } +}; + +/* define the dl_load function using the boilerplate macro */ + +dl_load_func(func_table, revtwoway, "") @@ -228,6 +228,33 @@ api_register_input_parser(awk_ext_id_t id, awk_input_parser_t *input_parser) register_input_parser(input_parser); } +/* api_register_output_wrapper --- egister an output wrapper, for writing files / two-way pipes */ + +static void api_register_output_wrapper(awk_ext_id_t id, + awk_output_wrapper_t *output_wrapper) +{ + (void) id; + + if (output_wrapper == NULL) + return; + + register_output_wrapper(output_wrapper); +} + +/* api_register_two_way_processor --- register a processor for two way I/O */ + +static void +api_register_two_way_processor(awk_ext_id_t id, + awk_two_way_processor_t *two_way_processor) +{ + (void) id; + + if (two_way_processor == NULL) + return; + + register_two_way_processor(two_way_processor); +} + /* Functions to update ERRNO */ /* api_update_ERRNO_int --- update ERRNO with an integer value */ @@ -526,20 +553,25 @@ sym_update_real(awk_ext_id_t id, if (is_const) node->var_assign = set_constant; } + return true; } - /* If we get here, then it exists already. Any valid type is - * OK except for AWK_ARRAY. */ - if ((value->val_type != AWK_ARRAY) && - (node->type == Node_var || node->type == Node_var_new)) { + /* + * If we get here, then it exists already. Any valid type is + * OK except for AWK_ARRAY. + */ + if ( value->val_type != AWK_ARRAY + && (node->type == Node_var || node->type == Node_var_new)) { unref(node->var_value); node->var_value = awk_value_to_node(value); /* let the extension change its own variable */ if (is_const) node->var_assign = set_constant; + return true; } + return false; } @@ -961,6 +993,8 @@ gawk_api_t api_impl = { api_lintwarn, api_register_input_parser, + api_register_output_wrapper, + api_register_two_way_processor, api_update_ERRNO_int, api_update_ERRNO_string, @@ -150,9 +150,68 @@ typedef struct input_parser { * It should return non-zero if successful. */ int (*take_control_of)(IOBUF_PUBLIC *iobuf); - struct input_parser *awk_const next; /* for use by gawk */ + awk_const struct input_parser *awk_const next; /* for use by gawk */ } awk_input_parser_t; +/* + * Similar for output wrapper. + */ + +typedef struct { + const char *name; + const char *mode; /* mode argument to fopen */ + FILE *fp; + int redirected; /* true if a wrapper is active */ + void *opaque; /* for use by output wrapper */ + + /* + * Replacement functions for I/O. Just like the regular + * versions but also take the opaque pointer argument. + */ + size_t (*gawk_fwrite)(const void *buf, size_t size, size_t count, FILE *fp, void *opaque); + int (*gawk_fflush)(FILE *fp, void *opaque); + int (*gawk_ferror)(FILE *fp, void *opaque); + int (*gawk_fclose)(FILE *fp, void *opaque); +} awk_output_buf_t; + +typedef struct output_wrapper { + const char *name; + /* + * The can_take_file function should return non-zero if the wrapper + * would like to process this file. It should not change any gawk + * state! + */ + int (*can_take_file)(const awk_output_buf_t *outbuf); + /* + * If this wrapper is selected, then take_control_of will be called. + * It can assume that a previous call to can_take_file was successful, + * and no gawk state has changed since that call. It should populate + * the awk_output_buf_t function pointers and opaque pointer as needed. + * It should return non-zero if successful. + */ + int (*take_control_of)(awk_output_buf_t *outbuf); + awk_const struct output_wrapper *awk_const next; /* for use by gawk */ +} awk_output_wrapper_t; + +typedef struct two_way_processor { + const char *name; + /* + * The can_take_file function should return non-zero if the two-way processor + * would like to parse this file. It should not change any gawk + * state! + */ + int (*can_take_two_way)(const char *name); + /* + * If this processor is selected, then take_control_of will be called. + * It can assume that a previous call to can_take_file was successful, + * and no gawk state has changed since that call. It should populate + * the IOBUF_PUBLIC and awk_otuput_buf_t structures as needed. + * It should return non-zero if successful. + */ + int (*take_control_of)(const char *name, IOBUF_PUBLIC *inbuf, awk_output_buf_t *outbuf); + awk_const struct two_way_processor *awk_const next; /* for use by gawk */ +} awk_two_way_processor_t; + enum { GAWK_API_MAJOR_VERSION = 0, @@ -355,6 +414,12 @@ typedef struct gawk_api { /* Register an input parser; for opening files read-only */ void (*api_register_input_parser)(awk_ext_id_t id, awk_input_parser_t *input_parser); + /* Register an output wrapper, for writing files / two-way pipes */ + void (*api_register_output_wrapper)(awk_ext_id_t id, awk_output_wrapper_t *output_wrapper); + + /* Register a processor for two way I/O */ + void (*api_register_two_way_processor)(awk_ext_id_t id, awk_two_way_processor_t *two_way_processor); + /* Functions to update ERRNO */ void (*api_update_ERRNO_int)(awk_ext_id_t id, int errno_val); void (*api_update_ERRNO_string)(awk_ext_id_t id, const char *string); @@ -535,6 +600,9 @@ typedef struct gawk_api { #define lintwarn api->api_lintwarn #define register_input_parser(parser) (api->api_register_input_parser(ext_id, parser)) +#define register_output_wrapper(wrapper) (api->api_register_output_wrapper(ext_id, wrapper)) +#define register_two_way_processor(processor) \ + (api->api_register_two_way_processor(ext_id, processor)) #define update_ERRNO_int(e) (api->api_update_ERRNO_int(ext_id, e)) #define update_ERRNO_string(str) \ @@ -717,6 +785,8 @@ int dl_load(const gawk_api_t *const api_p, awk_ext_id_t id) \ \ /* load functions */ \ for (i = 0, j = sizeof(func_table) / sizeof(func_table[0]); i < j; i++) { \ + if (func_table[i].name == NULL) \ + break; \ if (! add_ext_func(& func_table[i], name_space)) { \ warning(ext_id, #module ": could not add %s\n", \ func_table[i].name); \ @@ -210,6 +210,9 @@ static int str2mode(const char *mode); static int two_way_open(const char *str, struct redirect *rp); static int pty_vs_pipe(const char *command); static void find_input_parser(IOBUF *iop); +static bool find_output_wrapper(awk_output_buf_t *outbuf); +static void init_output_wrapper(awk_output_buf_t *outbuf); +static bool find_two_way_processor(const char *name, struct redirect *rp); static RECVALUE rs1scan(IOBUF *iop, struct recmatch *recm, SCANSTATE *state); static RECVALUE rsnullscan(IOBUF *iop, struct recmatch *recm, SCANSTATE *state); @@ -688,7 +691,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') @@ -759,7 +762,7 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) str[redir_exp->stlen] = '\0'; rp->value = str; rp->flag = tflag; - rp->fp = NULL; + init_output_wrapper(& rp->output); rp->iop = NULL; rp->pid = -1; rp->status = 0; @@ -767,7 +770,7 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) str = rp->value; /* get \0 terminated string */ save_rp = rp; - while (rp->fp == NULL && rp->iop == NULL) { + while (rp->output.fp == NULL && rp->iop == NULL) { if (! new_rp && rp->flag & RED_EOF) { /* * Encountered EOF on file or pipe -- must be cleared @@ -792,12 +795,12 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) (void) flush_io(); os_restore_mode(fileno(stdin)); - if ((rp->fp = popen(str, binmode("w"))) == NULL) + if ((rp->output.fp = popen(str, binmode("w"))) == NULL) fatal(_("can't open pipe `%s' for output (%s)"), str, strerror(errno)); /* set close-on-exec */ - os_close_on_exec(fileno(rp->fp), str, "pipe", "to"); + os_close_on_exec(fileno(rp->output.fp), str, "pipe", "to"); rp->flag |= RED_NOBUF; break; case redirect_pipein: @@ -844,15 +847,16 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) if (mode != NULL) { errno = 0; + rp->output.mode = mode; fd = devopen(str, mode); if (fd > INVALID_HANDLE) { if (fd == fileno(stdin)) - rp->fp = stdin; + rp->output.fp = stdin; else if (fd == fileno(stdout)) - rp->fp = stdout; + rp->output.fp = stdout; else if (fd == fileno(stderr)) - rp->fp = stderr; + rp->output.fp = stderr; else { const char *omode = mode; #if defined(F_GETFL) && defined(O_APPEND) @@ -863,13 +867,13 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) omode = binmode("a"); #endif os_close_on_exec(fd, str, "file", ""); - rp->fp = fdopen(fd, (const char *) omode); + rp->output.fp = fdopen(fd, (const char *) omode); rp->mode = (const char *) mode; /* don't leak file descriptors */ - if (rp->fp == NULL) + if (rp->output.fp == NULL) close(fd); } - if (rp->fp != NULL && os_isatty(fd)) + if (rp->output.fp != NULL && os_isatty(fd)) rp->flag |= RED_NOBUF; /* Move rp to the head of the list. */ @@ -882,9 +886,10 @@ redirect(NODE *redir_exp, int redirtype, int *errflg) red_head = rp; } } + find_output_wrapper(& rp->output); } - if (rp->fp == NULL && rp->iop == NULL) { + if (rp->output.fp == NULL && rp->iop == NULL) { /* too many files open -- close one and try again */ if (errno == EMFILE || errno == ENFILE) close_one(); @@ -978,16 +983,16 @@ close_one() /* now work back up through the list */ for (rp = rplast; rp != NULL; rp = rp->prev) { /* don't close standard files! */ - if (rp->fp == NULL || rp->fp == stderr || rp->fp == stdout) + if (rp->output.fp == NULL || rp->output.fp == stderr || rp->output.fp == stdout) continue; if ((rp->flag & (RED_FILE|RED_WRITE)) == (RED_FILE|RED_WRITE)) { rp->flag |= RED_USED; errno = 0; - if (fclose(rp->fp) != 0) + if (rp->output.gawk_fclose(rp->output.fp, rp->output.opaque) != 0) warning(_("close of `%s' failed (%s)."), rp->value, strerror(errno)); - rp->fp = NULL; + rp->output.fp = NULL; break; } } @@ -1071,18 +1076,18 @@ close_rp(struct redirect *rp, two_way_close_type how) errno = 0; if ((rp->flag & RED_TWOWAY) != 0) { /* two-way pipe */ /* write end: */ - if ((how == CLOSE_ALL || how == CLOSE_TO) && rp->fp != NULL) { + if ((how == CLOSE_ALL || how == CLOSE_TO) && rp->output.fp != NULL) { #ifdef HAVE_SOCKETS if ((rp->flag & RED_TCP) != 0) - (void) shutdown(fileno(rp->fp), SHUT_WR); + (void) shutdown(fileno(rp->output.fp), SHUT_WR); #endif /* HAVE_SOCKETS */ if ((rp->flag & RED_PTY) != 0) { - fwrite("\004\n", sizeof("\004\n") - 1, 1, rp->fp); - fflush(rp->fp); + rp->output.gawk_fwrite("\004\n", sizeof("\004\n") - 1, 1, rp->output.fp, rp->output.opaque); + rp->output.gawk_fflush(rp->output.fp, rp->output.opaque); } - status = fclose(rp->fp); - rp->fp = NULL; + status = rp->output.gawk_fclose(rp->output.fp, rp->output.opaque); + rp->output.fp = NULL; } /* read end: */ @@ -1100,14 +1105,14 @@ close_rp(struct redirect *rp, two_way_close_type how) } } else if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE)) { /* write to pipe */ - status = pclose(rp->fp); + status = pclose(rp->output.fp); if ((BINMODE & 1) != 0) os_setbinmode(fileno(stdin), O_BINARY); - rp->fp = NULL; - } else if (rp->fp != NULL) { /* write to file */ - status = fclose(rp->fp); - rp->fp = NULL; + rp->output.fp = NULL; + } else if (rp->output.fp != NULL) { /* write to file */ + status = rp->output.gawk_fclose(rp->output.fp, rp->output.opaque); + rp->output.fp = NULL; } else if (rp->iop != NULL) { /* read from pipe/file */ if ((rp->flag & RED_PIPE) != 0) /* read from pipe */ status = gawk_pclose(rp); @@ -1130,7 +1135,7 @@ close_redir(struct redirect *rp, bool exitwarn, two_way_close_type how) if (rp == NULL) return 0; - if (rp->fp == stdout || rp->fp == stderr) + if (rp->output.fp == stdout || rp->output.fp == stderr) goto checkwarn; /* bypass closing, remove from list */ if (do_lint && (rp->flag & RED_TWOWAY) == 0 && how != CLOSE_ALL) @@ -1188,7 +1193,7 @@ checkwarn: } /* remove it from the list if closing both or both ends have been closed */ - if (how == CLOSE_ALL || (rp->iop == NULL && rp->fp == NULL)) { + if (how == CLOSE_ALL || (rp->iop == NULL && rp->output.fp == NULL)) { if (rp->next != NULL) rp->next->prev = rp->prev; if (rp->prev != NULL) @@ -1220,8 +1225,8 @@ flush_io() } for (rp = red_head; rp != NULL; rp = rp->next) /* flush both files and pipes, what the heck */ - if ((rp->flag & RED_WRITE) && rp->fp != NULL) { - if (fflush(rp->fp)) { + if ((rp->flag & RED_WRITE) && rp->output.fp != NULL) { + if (rp->output.gawk_fflush(rp->output.fp, rp->output.opaque)) { if (rp->flag & RED_PIPE) warning(_("pipe flush of `%s' failed (%s)."), rp->value, strerror(errno)); @@ -1647,14 +1652,14 @@ two_way_open(const char *str, struct redirect *rp) fd = devopen(str, "rw"); if (fd == INVALID_HANDLE) return false; - rp->fp = fdopen(fd, "w"); - if (rp->fp == NULL) { + rp->output.fp = fdopen(fd, "w"); + if (rp->output.fp == NULL) { close(fd); return false; } newfd = dup(fd); if (newfd < 0) { - fclose(rp->fp); + rp->output.gawk_fclose(rp->output.fp, rp->output.opaque); return false; } os_close_on_exec(fd, str, "socket", "to/from"); @@ -1667,7 +1672,7 @@ two_way_open(const char *str, struct redirect *rp) update_ERRNO_int(rp->iop->errcode); iop_close(rp->iop); rp->iop = NULL; - fclose(rp->fp); + rp->output.gawk_fclose(rp->output.fp, rp->output.opaque); return false; } rp->flag |= RED_SOCKET; @@ -1675,8 +1680,12 @@ two_way_open(const char *str, struct redirect *rp) } #endif /* HAVE_SOCKETS */ + /* case 2: see if an extension wants it */ + if (find_two_way_processor(str, rp)) + return true; + #if defined(HAVE_TERMIOS_H) && ! defined(ZOS_USS) - /* case 2: use ptys for two-way communications to child */ + /* case 3: use ptys for two-way communications to child */ if (! no_ptys && pty_vs_pipe(str)) { static bool initialized = false; static char first_pty_letter; @@ -1876,8 +1885,9 @@ two_way_open(const char *str, struct redirect *rp) * Force read and write ends of two-way connection to * be different fd's so they can be closed independently. */ + rp->output.mode = "w"; if ((dup_master = dup(master)) < 0 - || (rp->fp = fdopen(dup_master, "w")) == NULL) { + || (rp->output.fp = fdopen(dup_master, "w")) == NULL) { iop_close(rp->iop); rp->iop = NULL; (void) close(master); @@ -1885,7 +1895,8 @@ two_way_open(const char *str, struct redirect *rp) if (dup_master > 0) (void) close(dup_master); return false; - } + } else + find_output_wrapper(& rp->output); rp->flag |= RED_PTY; os_close_on_exec(master, str, "pipe", "from"); os_close_on_exec(dup_master, str, "pipe", "to"); @@ -1896,7 +1907,7 @@ two_way_open(const char *str, struct redirect *rp) use_pipes: #ifndef PIPES_SIMULATED /* real pipes */ - /* case 3: two way pipe to a child process */ + /* case 4: two way pipe to a child process */ { int ptoc[2], ctop[2]; int pid; @@ -2033,8 +2044,9 @@ use_pipes: return false; } - rp->fp = fdopen(ptoc[1], "w"); - if (rp->fp == NULL) { + rp->output.fp = fdopen(ptoc[1], "w"); + rp->output.mode = "w"; + if (rp->output.fp == NULL) { iop_close(rp->iop); rp->iop = NULL; (void) close(ctop[0]); @@ -2045,6 +2057,8 @@ use_pipes: return false; } + else + find_output_wrapper(& rp->output); #ifndef __EMX__ os_close_on_exec(ctop[0], str, "pipe", "from"); @@ -2677,6 +2691,124 @@ find_input_parser(IOBUF *iop) } } +/* output wrappers --- for use by extensions */ + +static awk_output_wrapper_t *op_head, *op_tail; + +/* + * register_output_wrapper --- add an output wrapper to the list. + * Same stuff here as for input parsers. + */ + +void +register_output_wrapper(awk_output_wrapper_t *wrapper) +{ + if (wrapper == NULL) + fatal(_("register_output_wrapper: received NULL pointer")); + + wrapper->next = NULL; /* force it */ + if (op_head == NULL) { + op_head = op_tail = wrapper; + } else { + op_tail->next = wrapper; + op_tail = op_tail->next; + } +} + +/* find_output_wrapper --- search the list of output wrappers */ + +static bool +find_output_wrapper(awk_output_buf_t *outbuf) +{ + awk_output_wrapper_t *op, *op2; + + /* if already associated with an output wrapper, bail out early */ + if (outbuf->redirected) + return false; + + op = op2 = NULL; + for (op2 = op_head; op2 != NULL; op2 = op2->next) { + if (op2->can_take_file(outbuf)) { + if (op == NULL) + op = op2; /* found first one */ + else + fatal(_("output wrapper `%s' conflicts with previously installed output wrapper `%s'"), + op2->name, op->name); + } + } + + if (op != NULL) { + if (! op->take_control_of(outbuf)) { + warning(_("output wrapper `%s' failed to open `%s'"), + op->name, outbuf->name); + return false; + } + return true; + } + + return false; +} + + +/* two way processors --- for use by extensions */ + +static awk_two_way_processor_t *tw_head, *tw_tail; + +/* register_two_way_processor --- register a two-way I/O processor, for extensions */ + +void +register_two_way_processor(awk_two_way_processor_t *processor) +{ + if (processor == NULL) + fatal(_("register_output_processor: received NULL pointer")); + + processor->next = NULL; /* force it */ + if (tw_head == NULL) { + tw_head = tw_tail = processor; + } else { + tw_tail->next = processor; + tw_tail = tw_tail->next; + } +} + +/* find_two_way_processor --- search the list of two way processors */ + +static bool +find_two_way_processor(const char *name, struct redirect *rp) +{ + awk_two_way_processor_t *tw, *tw2; + + /* if already associated with i/o, bail out early */ + if ( (rp->iop != NULL && rp->iop->public.fd != INVALID_HANDLE) + || rp->output.fp != NULL) + return false; + + tw = tw2 = NULL; + for (tw2 = tw_head; tw2 != NULL; tw2 = tw2->next) { + if (tw2->can_take_two_way(name)) { + if (tw == NULL) + tw = tw2; /* found first one */ + else + fatal(_("two-way processor `%s' conflicts with previously installed two-way processor `%s'"), + tw2->name, tw->name); + } + } + + if (tw != NULL) { + if (rp->iop == NULL) + rp->iop = iop_alloc(INVALID_HANDLE, name, 0); + if (! tw->take_control_of(name, & rp->iop->public, & rp->output)) { + warning(_("two way processor `%s' failed to open `%s'"), + tw->name, name); + return false; + } + iop_finish(rp->iop); + return true; + } + + return false; +} + /* * IOBUF management is somewhat complicated. In particular, * it is possible and OK for an IOBUF to be allocated with @@ -3601,4 +3733,57 @@ read_with_timeout(int fd, char *buf, size_t size) #endif /* __MINGW32__ */ } +/* + * Dummy pass through functions for default output. + */ + +/* gawk_fwrite --- like fwrite */ + +static size_t +gawk_fwrite(const void *buf, size_t size, size_t count, FILE *fp, void *opaque) +{ + (void) opaque; + return fwrite(buf, size, count, fp); +} + +static int +gawk_fflush(FILE *fp, void *opaque) +{ + (void) opaque; + + return fflush(fp); +} + +static int +gawk_ferror(FILE *fp, void *opaque) +{ + (void) opaque; + + return ferror(fp); +} + +static int +gawk_fclose(FILE *fp, void *opaque) +{ + (void) opaque; + + return fclose(fp); +} + + +/* init_output_wrapper --- initialize the output wrapper */ + +static void +init_output_wrapper(awk_output_buf_t *outbuf) +{ + outbuf->name = NULL; + outbuf->mode = NULL; + outbuf->fp = NULL; + outbuf->opaque = NULL; + outbuf->redirected = false; + outbuf->gawk_fwrite = gawk_fwrite; + outbuf->gawk_fflush = gawk_fflush; + outbuf->gawk_ferror = gawk_ferror; + outbuf->gawk_fclose = gawk_fclose; +} diff --git a/test/ChangeLog b/test/ChangeLog index a0e3c0c4..5414bfce 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,8 @@ +2012-08-23 Arnold D. Robbins <arnold@skeeve.com> + + * Makefile.am (revout, revtwoway): New tests. + * revout.awk, revout.ok, revtwoway.awk, revtwoway.ok: New files. + 2012-08-11 Andrew J. Schorr <aschorr@telemetry-investments.com> * Makefile.am (EXTRA_DIST): Add inchello.awk and incdupe[4-7].ok. diff --git a/test/Makefile.am b/test/Makefile.am index 8b2f8577..ef833ef4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -674,6 +674,10 @@ EXTRA_DIST = \ resplit.awk \ resplit.in \ resplit.ok \ + revout.awk \ + revout.ok \ + revtwoway.awk \ + revtwoway.ok \ rri1.awk \ rri1.in \ rri1.ok \ @@ -913,7 +917,7 @@ LOCALE_CHARSET_TESTS = \ SHLIB_TESTS = \ assignconst fnmatch filefuncs fork fork2 fts ordchr ordchr2 \ - readdir readfile rwarray testext time + readdir readfile revout revtwoway rwarray testext time # List of the tests which should be run with --lint option: NEED_LINT = \ diff --git a/test/Makefile.in b/test/Makefile.in index f5ef08cc..c4b1bb60 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -886,6 +886,10 @@ EXTRA_DIST = \ resplit.awk \ resplit.in \ resplit.ok \ + revout.awk \ + revout.ok \ + revtwoway.awk \ + revtwoway.ok \ rri1.awk \ rri1.in \ rri1.ok \ @@ -1121,7 +1125,7 @@ LOCALE_CHARSET_TESTS = \ SHLIB_TESTS = \ assignconst fnmatch filefuncs fork fork2 fts ordchr ordchr2 \ - readdir readfile rwarray testext time + readdir readfile revout revtwoway rwarray testext time # List of the tests which should be run with --lint option: @@ -3282,6 +3286,16 @@ ordchr: @AWKPATH=$(srcdir) $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ +revout: + @echo $@ + @AWKPATH=$(srcdir) $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ + +revtwoway: + @echo $@ + @AWKPATH=$(srcdir) $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ + rwarray: @echo $@ @AWKPATH=$(srcdir) $(AWK) -f $@.awk < $(srcdir)/$@.in >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ diff --git a/test/Maketests b/test/Maketests index 675551e3..3951c3ff 100644 --- a/test/Maketests +++ b/test/Maketests @@ -1239,6 +1239,16 @@ ordchr: @AWKPATH=$(srcdir) $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ +revout: + @echo $@ + @AWKPATH=$(srcdir) $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ + +revtwoway: + @echo $@ + @AWKPATH=$(srcdir) $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) $(srcdir)/$@.ok _$@ && rm -f _$@ + rwarray: @echo $@ @AWKPATH=$(srcdir) $(AWK) -f $@.awk < $(srcdir)/$@.in >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ diff --git a/test/revout.awk b/test/revout.awk new file mode 100644 index 00000000..25f62fc7 --- /dev/null +++ b/test/revout.awk @@ -0,0 +1,6 @@ +@load "revoutput" + +BEGIN { + REVOUT = 1 + print "hello, world" > "/dev/stdout" +} diff --git a/test/revout.ok b/test/revout.ok new file mode 100644 index 00000000..8101cb93 --- /dev/null +++ b/test/revout.ok @@ -0,0 +1 @@ +dlrow ,olleh diff --git a/test/revtwoway.awk b/test/revtwoway.awk new file mode 100644 index 00000000..05bded5a --- /dev/null +++ b/test/revtwoway.awk @@ -0,0 +1,11 @@ +@load "revtwoway" + +BEGIN { + cmd = "/magic/mirror" + + print "hello, world" |& cmd + cmd |& getline line + + printf("got back: <%s>, RT = <%s>\n", line, RT) + close(cmd) +} diff --git a/test/revtwoway.ok b/test/revtwoway.ok new file mode 100644 index 00000000..b8a5ff31 --- /dev/null +++ b/test/revtwoway.ok @@ -0,0 +1,2 @@ +got back: <dlrow ,olleh>, RT = < +> |