aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2012-08-24 13:25:52 +0300
committerArnold D. Robbins <arnold@skeeve.com>2012-08-24 13:25:52 +0300
commit8970970f3f3bc3d757fe491e90e608366fb7e604 (patch)
tree5191834bc71c9674180dfa0103ae3d6edce4cabe
parentcca8e6fca6c40de9c67f17ed74fc80291fd969e1 (diff)
downloadegawk-8970970f3f3bc3d757fe491e90e608366fb7e604.tar.gz
egawk-8970970f3f3bc3d757fe491e90e608366fb7e604.tar.bz2
egawk-8970970f3f3bc3d757fe491e90e608366fb7e604.zip
Add output and two-way processors to API. Update Mac config stuff.
-rw-r--r--ChangeLog25
-rw-r--r--awk.h4
-rw-r--r--builtin.c36
-rw-r--r--ext.c12
-rw-r--r--extension/ChangeLog5
-rw-r--r--extension/Makefile.am18
-rw-r--r--extension/Makefile.in47
-rw-r--r--extension/aclocal.m41
-rw-r--r--extension/configh.in11
-rwxr-xr-xextension/configure79
-rw-r--r--extension/configure.ac2
-rw-r--r--extension/m4/ChangeLog4
-rw-r--r--extension/m4/intlmacosx.m451
-rw-r--r--extension/readdir.3am17
-rw-r--r--extension/readdir.c24
-rw-r--r--extension/revoutput.c136
-rw-r--r--extension/revtwoway.c335
-rw-r--r--gawkapi.c42
-rw-r--r--gawkapi.h72
-rw-r--r--io.c265
-rw-r--r--test/ChangeLog5
-rw-r--r--test/Makefile.am6
-rw-r--r--test/Makefile.in16
-rw-r--r--test/Maketests10
-rw-r--r--test/revout.awk6
-rw-r--r--test/revout.ok1
-rw-r--r--test/revtwoway.awk11
-rw-r--r--test/revtwoway.ok2
28 files changed, 1149 insertions, 94 deletions
diff --git a/ChangeLog b/ChangeLog
index 7063a365..489ffa8b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/awk.h b/awk.h
index e7998626..9b595072 100644
--- a/awk.h
+++ b/awk.h
@@ -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);
diff --git a/builtin.c b/builtin.c
index 3576372c..6a2d9d6b 100644
--- a/builtin.c
+++ b/builtin.c
@@ -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
diff --git a/ext.c b/ext.c
index 17ade95a..3f4b95f8 100644
--- a/ext.c
+++ b/ext.c
@@ -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, "")
diff --git a/gawkapi.c b/gawkapi.c
index c34b52bb..8fd20472 100644
--- a/gawkapi.c
+++ b/gawkapi.c
@@ -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,
diff --git a/gawkapi.h b/gawkapi.h
index 9b89e425..3c369a8f 100644
--- a/gawkapi.h
+++ b/gawkapi.h
@@ -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); \
diff --git a/io.c b/io.c
index 589abbf4..d4f7535f 100644
--- a/io.c
+++ b/io.c
@@ -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 = <
+>