gawk-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gawk-diffs] [SCM] gawk branch, master, updated. 759f2234c9bfa689151277f


From: Arnold Robbins
Subject: [gawk-diffs] [SCM] gawk branch, master, updated. 759f2234c9bfa689151277fd2215bc0927cfc9c3
Date: Fri, 24 Aug 2012 10:40:50 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "gawk".

The branch, master has been updated
       via  759f2234c9bfa689151277fd2215bc0927cfc9c3 (commit)
       via  8970970f3f3bc3d757fe491e90e608366fb7e604 (commit)
       via  cca8e6fca6c40de9c67f17ed74fc80291fd969e1 (commit)
      from  21df49a92356710bfa6551939041438af55d8c84 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://git.sv.gnu.org/cgit/gawk.git/commit/?id=759f2234c9bfa689151277fd2215bc0927cfc9c3

commit 759f2234c9bfa689151277fd2215bc0927cfc9c3
Author: Arnold D. Robbins <address@hidden>
Date:   Fri Aug 24 13:40:22 2012 +0300

    Add facility to get vesion info from extensions.

diff --git a/ChangeLog b/ChangeLog
index 489ffa8..4297866 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2012-08-24         Arnold D. Robbins     <address@hidden>
+
+       Add version facility to API. Thanks to Manuel Collado
+       for the idea.
+
+       * awk.h (print_ext_versions): Declare.
+       Rearrange includes and decls to make more sense.
+       * gawkapi.h (register_ext_version): New API.
+       (dl_load_func): Add code for ext_version.
+       * gawkapi.c (api_register_ext_version, print_ext_versions):
+       New functions.
+       * main.c (do_version): New variable.
+       (optab): Set it for -v / --version.
+       (main): Set it in arg parsing switch. Call version() after the
+       extensions have been loaded.
+
 2012-08-22         Arnold D. Robbins     <address@hidden>
 
        Add output wrapper and two-way processor to extension API.
diff --git a/awk.h b/awk.h
index 9b59507..d106d2e 100644
--- a/awk.h
+++ b/awk.h
@@ -244,6 +244,8 @@ typedef struct Regexp {
 #define RE_NEED_START  1       /* need to know start/end of match */
 #define RE_NO_BOL      2       /* not allowed to match ^ in regexp */
 
+#include "gawkapi.h"
+
 /* Stuff for losing systems. */
 #if !defined(HAVE_STRTOD)
 extern double gawk_strtod();
@@ -705,16 +707,6 @@ enum redirval {
 
 struct break_point;
 
-#if 1
-#include "gawkapi.h"
-/* gawkapi.c: */
-extern gawk_api_t api_impl;
-extern void init_ext_api(void);
-extern void update_ext_api(void);
-extern NODE *awk_value_to_node(const awk_value_t *);
-extern void run_ext_exit_handlers(int exitval);
-#endif
-
 typedef struct exp_instruction {
        struct exp_instruction *nexti;
        union {
@@ -1525,6 +1517,14 @@ typedef enum {
 } field_sep_type;
 extern field_sep_type current_field_sep(void);
 
+/* gawkapi.c: */
+extern gawk_api_t api_impl;
+extern void init_ext_api(void);
+extern void update_ext_api(void);
+extern NODE *awk_value_to_node(const awk_value_t *);
+extern void run_ext_exit_handlers(int exitval);
+extern void print_ext_versions(void);
+
 /* gawkmisc.c */
 extern char *gawk_name(const char *filespec);
 extern void os_arg_fixup(int *argcp, char ***argvp);
diff --git a/extension/ChangeLog b/extension/ChangeLog
index 0a15220..be3cf08 100644
--- a/extension/ChangeLog
+++ b/extension/ChangeLog
@@ -1,3 +1,18 @@
+2012-08-24         Arnold D. Robbins     <address@hidden>
+
+       * filefuncs.c, fnmatch.c, fork.c, ordchr.c, readdir.c, readfile.c,
+       revoutput.c, revtwoway.c, rwarray.c, rwarray0.c, testext.c,
+       time.c: Add ext_version string.
+
+2012-08-23         Arnold D. Robbins     <address@hidden>
+
+       * revoutwoway.c: New testing extension for two way processor.
+       * Makefile.am: Build revtwoway extension.
+       * readdir.c: Fix to fall back to stat if d_type is 'u' and
+       do_ftype is one.
+       * readdir.3am: Revise doc that some GNU/Linux filesystems
+       don't support d_type.
+
 2012-08-22         Arnold D. Robbins     <address@hidden>
 
        * revoutput.c: New testing extension for output wrapper.
diff --git a/extension/filefuncs.c b/extension/filefuncs.c
index e27e51b..9f49870 100644
--- a/extension/filefuncs.c
+++ b/extension/filefuncs.c
@@ -61,6 +61,7 @@ static const gawk_api_t *api; /* for convenience macros to 
work */
 static awk_ext_id_t *ext_id;
 static awk_bool_t init_filefuncs(void);
 static awk_bool_t (*init_func)(void) = init_filefuncs;
+static const char *ext_version = "filefuncs extension: version 1.0";
 
 int plugin_is_GPL_compatible;
 
diff --git a/extension/fnmatch.c b/extension/fnmatch.c
index 3ebae59..6a4d463 100644
--- a/extension/fnmatch.c
+++ b/extension/fnmatch.c
@@ -61,6 +61,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "fnmatch extension: version 1.0";
 
 static awk_bool_t init_fnmatch(void);
 static awk_bool_t (*init_func)(void) = init_fnmatch;
diff --git a/extension/fork.c b/extension/fork.c
index 7bee8ba..9f9fc08 100644
--- a/extension/fork.c
+++ b/extension/fork.c
@@ -45,6 +45,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "fork extension: version 1.0";
 static awk_bool_t (*init_func)(void) = NULL;
 
 int plugin_is_GPL_compatible;
diff --git a/extension/ordchr.c b/extension/ordchr.c
index 01466f1..5abb623 100644
--- a/extension/ordchr.c
+++ b/extension/ordchr.c
@@ -47,6 +47,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "ordchr extension: version 1.0";
 static awk_bool_t (*init_func)(void) = NULL;
 
 int plugin_is_GPL_compatible;
diff --git a/extension/readdir.c b/extension/readdir.c
index 3eae407..6ce7bea 100644
--- a/extension/readdir.c
+++ b/extension/readdir.c
@@ -54,6 +54,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "readdir extension: version 1.0";
 
 static awk_bool_t init_readdir(void);
 static awk_bool_t (*init_func)(void) = init_readdir;
diff --git a/extension/readfile.c b/extension/readfile.c
index 8f68c24..5abb576 100644
--- a/extension/readfile.c
+++ b/extension/readfile.c
@@ -54,6 +54,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "readfile extension: version 1.0";
 static awk_bool_t (*init_func)(void) = NULL;
 
 int plugin_is_GPL_compatible;
diff --git a/extension/revoutput.c b/extension/revoutput.c
index c1ea1dd..bb195a2 100644
--- a/extension/revoutput.c
+++ b/extension/revoutput.c
@@ -45,6 +45,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "revoutput extension: version 1.0";
 
 static awk_bool_t init_revout(void);
 static awk_bool_t (*init_func)(void) = init_revout;
diff --git a/extension/revtwoway.c b/extension/revtwoway.c
index 7ab9366..9ce0c73 100644
--- a/extension/revtwoway.c
+++ b/extension/revtwoway.c
@@ -47,6 +47,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "revtwoway extension: version 1.0";
 
 static awk_bool_t init_revtwoway(void);
 static awk_bool_t (*init_func)(void) = init_revtwoway;
diff --git a/extension/rwarray.c b/extension/rwarray.c
index 0eca977..371078e 100644
--- a/extension/rwarray.c
+++ b/extension/rwarray.c
@@ -51,6 +51,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "rwarray extension: version 1.0";
 static awk_bool_t (*init_func)(void) = NULL;
 
 int plugin_is_GPL_compatible;
diff --git a/extension/rwarray0.c b/extension/rwarray0.c
index 75c735a..c511ce0 100644
--- a/extension/rwarray0.c
+++ b/extension/rwarray0.c
@@ -51,6 +51,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "rwarray0 extension: version 1.0";
 static awk_bool_t (*init_func)(void) = NULL;
 
 int plugin_is_GPL_compatible;
diff --git a/extension/testext.c b/extension/testext.c
index d1ebac1..d0215d9 100644
--- a/extension/testext.c
+++ b/extension/testext.c
@@ -39,6 +39,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "testext extension: version 1.0";
 
 int plugin_is_GPL_compatible;
 
diff --git a/extension/time.c b/extension/time.c
index 7e3fc52..24c7572 100644
--- a/extension/time.c
+++ b/extension/time.c
@@ -43,6 +43,7 @@
 
 static const gawk_api_t *api;  /* for convenience macros to work */
 static awk_ext_id_t *ext_id;
+static const char *ext_version = "time extension: version 1.0";
 static awk_bool_t (*init_func)(void) = NULL;
 
 int plugin_is_GPL_compatible;
diff --git a/gawkapi.c b/gawkapi.c
index 8fd2047..fe6eefc 100644
--- a/gawkapi.c
+++ b/gawkapi.c
@@ -980,6 +980,33 @@ api_release_value(awk_ext_id_t id, awk_value_cookie_t 
value)
        return true;
 }
 
+/*
+ * Register a version string for this extension with gawk.
+ */
+
+static struct version_info {
+       const char *version;
+       struct version_info *next;
+};
+
+static struct version_info *vi_head;
+
+/* api_register_ext_version --- add an extension version string to the list */
+
+static void
+api_register_ext_version(awk_ext_id_t id, const char *version)
+{
+
+       struct version_info *info;
+
+       (void) id;
+
+       emalloc(info, struct version_info *, sizeof(struct version_info), 
"register_ext_version");
+       info->version = version;
+       info->next = vi_head;
+       vi_head = info;
+}
+
 gawk_api_t api_impl = {
        GAWK_API_MAJOR_VERSION, /* major and minor versions */
        GAWK_API_MINOR_VERSION,
@@ -1021,6 +1048,8 @@ gawk_api_t api_impl = {
 
        api_create_value,
        api_release_value,
+
+       api_register_ext_version,
 };
 
 /* init_ext_api --- init the extension API */
@@ -1052,3 +1081,14 @@ set_constant()
 {
        fatal(_("cannot assign to defined constant"));
 }
+
+/* print_ext_versions --- print the list */
+
+extern void
+print_ext_versions(void)
+{
+       struct version_info *p;
+
+       for (p = vi_head; p != NULL; p = p->next)
+               printf("%s\n", p->version);
+}
diff --git a/gawkapi.h b/gawkapi.h
index 3c369a8..95effd9 100644
--- a/gawkapi.h
+++ b/gawkapi.h
@@ -576,6 +576,11 @@ typedef struct gawk_api {
         * Please call this to free memory when the value is no longer needed.
         */
        awk_bool_t (*api_release_value)(awk_ext_id_t id, awk_value_cookie_t vc);
+
+       /*
+        * Register a version string for this extension with gawk.
+        */
+       void (*api_register_ext_version)(awk_ext_id_t id, const char *version);
 } gawk_api_t;
 
 #ifndef GAWK   /* these are not for the gawk code itself! */
@@ -654,6 +659,9 @@ typedef struct gawk_api {
 #define release_value(value) \
        (api->api_release_value(ext_id, value))
 
+#define register_ext_version(version) \
+       (api->api_register_ext_version(ext_id, version))
+
 #define emalloc(pointer, type, size, message) \
        do { \
                if ((pointer = (type) malloc(size)) == 0) \
@@ -748,6 +756,7 @@ static awk_ext_func_t func_table[] = {
        { "name", do_name, 1 },
        /* ... */
 };
+static const char *ext_version = NULL; /* or ... = "some string" */
 
 /* EITHER: */
 
@@ -801,6 +810,9 @@ int dl_load(const gawk_api_t *const api_p, awk_ext_id_t id) 
 \
                } \
        } \
 \
+       if (ext_version != NULL) \
+               register_ext_version(ext_version); \
+\
        return (errors == 0); \
 }
 
diff --git a/main.c b/main.c
index 9c99c86..8ac9493 100644
--- a/main.c
+++ b/main.c
@@ -138,6 +138,7 @@ int do_flags = false;
 bool do_optimize = true;               /* apply default optimizations */
 static int do_nostalgia = false;       /* provide a blast from the past */
 static int do_binary = false;          /* hands off my data! */
+static int do_version = false;         /* print version info */
 
 int use_lc_numeric = false;    /* obey locale for decimal point */
 
@@ -180,7 +181,7 @@ static const struct option optab[] = {
        { "load",               required_argument,      NULL,   'l' },
        { "dump-variables",     optional_argument,      NULL,   'd' },
        { "assign",             required_argument,      NULL,   'v' },
-       { "version",            no_argument,            NULL,   'V' },
+       { "version",            no_argument,            & do_version, 'V' },
        { "help",               no_argument,            NULL,   'h' },
        { "exec",               required_argument,      NULL,   'E' },
        { "use-lc-numeric",     no_argument,            & use_lc_numeric, 1 },
@@ -469,7 +470,7 @@ main(int argc, char **argv)
                        break;
 
                case 'V':
-                       version();
+                       do_version = true;
                        break;
 
                case 'W':       /* gawk specific options - now in getopt_long */
@@ -652,6 +653,10 @@ out:
                        have_srcfile++;
         }
 
+       /* do version check after extensions are loaded to get extension info */
+       if (do_version)
+               version();
+
        /* No -f or --source options, use next arg */
        if (! have_srcfile) {
                if (optind > argc - 1 || stopped_early) /* no args left or no 
program */
@@ -1409,6 +1414,8 @@ version()
        printf(" (GNU MPFR %s, GNU MP %s)", mpfr_get_version(), gmp_version);
 #endif
        printf("\n"); 
+       print_ext_versions();
+
        /*
         * Per GNU coding standards, print copyright info,
         * then exit successfully, do nothing else.

http://git.sv.gnu.org/cgit/gawk.git/commit/?id=8970970f3f3bc3d757fe491e90e608366fb7e604

commit 8970970f3f3bc3d757fe491e90e608366fb7e604
Author: Arnold D. Robbins <address@hidden>
Date:   Fri Aug 24 13:25:52 2012 +0300

    Add output and two-way processors to API. Update Mac config stuff.

diff --git a/ChangeLog b/ChangeLog
index 7063a36..489ffa8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2012-08-22         Arnold D. Robbins     <address@hidden>
+
+       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     <address@hidden>
 
        * Update infrastructure: Automake 1.12.3 and bison 2.6.2.
diff --git a/awk.h b/awk.h
index e799862..9b59507 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 3576372..6a2d9d6 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 17ade95..3f4b95f 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 bc4b060..0a15220 100644
--- a/extension/ChangeLog
+++ b/extension/ChangeLog
@@ -1,3 +1,8 @@
+2012-08-22         Arnold D. Robbins     <address@hidden>
+
+       * revoutput.c: New testing extension for output wrapper.
+       * Makefile.am: Build revoutput extension.
+
 2012-08-08         Arnold D. Robbins     <address@hidden>
 
        Add fts() to filefuncs.
diff --git a/extension/Makefile.am b/extension/Makefile.am
index bf9a715..f7d939a 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 59ead43..bebc006 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@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
 @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@
diff --git a/extension/aclocal.m4 b/extension/aclocal.m4
index db5d9f8..18cab75 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 73ce026..571b7a7 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 732bfc9..7030c8b 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 3300961..4869584 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 9ce5738..c122339 100644
--- a/extension/m4/ChangeLog
+++ b/extension/m4/ChangeLog
@@ -1,3 +1,7 @@
+2012-08-24         Arnold D. Robbins     <address@hidden>
+
+       * intlmacosx.m4: New file.
+
 2012-07-30         Arnold D. Robbins     <address@hidden>
 
        * 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 0000000..dd91025
--- /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 5f6ae06..9cb4862 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 c28764e..3eae407 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 0000000..c1ea1dd
--- /dev/null
+++ b/extension/revoutput.c
@@ -0,0 +1,136 @@
+/*
+ * revoutput.c --- Provide an output wrapper that reverses lines.
+ *
+ * Arnold Robbins
+ * address@hidden
+ * 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 0000000..7ab9366
--- /dev/null
+++ b/extension/revtwoway.c
@@ -0,0 +1,335 @@
+/*
+ * revtwoway.c --- Provide a two-way processor that reverses lines.
+ *
+ * Arnold Robbins
+ * address@hidden
+ * 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 c34b52b..8fd2047 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 9b89e42..3c369a8 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 589abbf..d4f7535 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 a0e3c0c..5414bfc 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,8 @@
+2012-08-23         Arnold D. Robbins     <address@hidden>
+
+       * Makefile.am (revout, revtwoway): New tests.
+       * revout.awk, revout.ok, revtwoway.awk, revtwoway.ok: New files.
+
 2012-08-11         Andrew J. Schorr     <address@hidden>
 
        * Makefile.am (EXTRA_DIST): Add inchello.awk and incdupe[4-7].ok.
diff --git a/test/Makefile.am b/test/Makefile.am
index 8b2f857..ef833ef 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 f5ef08c..c4b1bb6 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 address@hidden  >_$@ 2>&1 || echo EXIT 
CODE: $$? >>_$@
        @-$(CMP) $(srcdir)/address@hidden _$@ && rm -f _$@
 
+revout:
+       @echo $@
+       @AWKPATH=$(srcdir) $(AWK) -f address@hidden  >_$@ 2>&1 || echo EXIT 
CODE: $$? >>_$@
+       @-$(CMP) $(srcdir)/address@hidden _$@ && rm -f _$@
+
+revtwoway:
+       @echo $@
+       @AWKPATH=$(srcdir) $(AWK) -f address@hidden  >_$@ 2>&1 || echo EXIT 
CODE: $$? >>_$@
+       @-$(CMP) $(srcdir)/address@hidden _$@ && rm -f _$@
+
 rwarray:
        @echo $@
        @AWKPATH=$(srcdir) $(AWK) -f address@hidden  < $(srcdir)/address@hidden 
>_$@ 2>&1 || echo EXIT CODE: $$? >>_$@
diff --git a/test/Maketests b/test/Maketests
index 675551e..3951c3f 100644
--- a/test/Maketests
+++ b/test/Maketests
@@ -1239,6 +1239,16 @@ ordchr:
        @AWKPATH=$(srcdir) $(AWK) -f address@hidden  >_$@ 2>&1 || echo EXIT 
CODE: $$? >>_$@
        @-$(CMP) $(srcdir)/address@hidden _$@ && rm -f _$@
 
+revout:
+       @echo $@
+       @AWKPATH=$(srcdir) $(AWK) -f address@hidden  >_$@ 2>&1 || echo EXIT 
CODE: $$? >>_$@
+       @-$(CMP) $(srcdir)/address@hidden _$@ && rm -f _$@
+
+revtwoway:
+       @echo $@
+       @AWKPATH=$(srcdir) $(AWK) -f address@hidden  >_$@ 2>&1 || echo EXIT 
CODE: $$? >>_$@
+       @-$(CMP) $(srcdir)/address@hidden _$@ && rm -f _$@
+
 rwarray:
        @echo $@
        @AWKPATH=$(srcdir) $(AWK) -f address@hidden  < $(srcdir)/address@hidden 
>_$@ 2>&1 || echo EXIT CODE: $$? >>_$@
diff --git a/test/revout.awk b/test/revout.awk
new file mode 100644
index 0000000..25f62fc
--- /dev/null
+++ b/test/revout.awk
@@ -0,0 +1,6 @@
address@hidden "revoutput"
+
+BEGIN {
+       REVOUT = 1
+       print "hello, world" > "/dev/stdout"
+}
diff --git a/test/revout.ok b/test/revout.ok
new file mode 100644
index 0000000..8101cb9
--- /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 0000000..05bded5
--- /dev/null
+++ b/test/revtwoway.awk
@@ -0,0 +1,11 @@
address@hidden "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 0000000..b8a5ff3
--- /dev/null
+++ b/test/revtwoway.ok
@@ -0,0 +1,2 @@
+got back: <dlrow ,olleh>, RT = <
+>

http://git.sv.gnu.org/cgit/gawk.git/commit/?id=cca8e6fca6c40de9c67f17ed74fc80291fd969e1

commit cca8e6fca6c40de9c67f17ed74fc80291fd969e1
Author: Arnold D. Robbins <address@hidden>
Date:   Fri Aug 24 13:18:09 2012 +0300

    Fix it.po.

diff --git a/po/it.gmo b/po/it.gmo
index eb5e49a..6f46f49 100644
Binary files a/po/it.gmo and b/po/it.gmo differ
diff --git a/po/it.po b/po/it.po
index aecb2b8..e5be16f 100644
--- a/po/it.po
+++ b/po/it.po
@@ -516,11 +516,6 @@ msgstr ""
 "funzione `%s': non posso usare la variabile speciale `%s' come parametro di "
 "funzione"
 
-#: awkgram.y:4201
-#, c-format
-msgid "function name `%s' previously defined"
-msgstr "funzione di nome `%s' definita in precedenza"
-
 #: awkgram.y:4295 awkgram.y:4299
 #, c-format
 msgid "function `%s' called but never defined"
@@ -1897,11 +1892,6 @@ msgstr "disabilito `--lint' a causa di assegnamento a 
`LINT'"
 msgid "reference to uninitialized argument `%s'"
 msgstr "riferimento ad argomento non inizializzato `%s'"
 
-#: eval.c:1146
-#, c-format
-msgid "reference to uninitialized variable `%s'"
-msgstr "riferimento a variabile non inizializzata `%s'"
-
 #: eval.c:1164
 msgid "attempt to field reference from non-numeric value"
 msgstr "tentativo di riferimento a un campo da valore non numerico"
@@ -1993,11 +1983,6 @@ msgstr "estensione: non riesco a ridefinire funzione 
`%s'"
 msgid "extension: function `%s' already defined"
 msgstr "estensione: funzione `%s' giĆ  definita"
 
-#: ext.c:114
-#, c-format
-msgid "extension: function name `%s' previously defined"
-msgstr "estensione: funzione di nome `%s' definita in precedenza"
-
 #: ext.c:116
 #, c-format
 msgid "extension: can't use gawk built-in `%s' as function name"

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                          |   41 +++++
 awk.h                              |   24 ++--
 builtin.c                          |   36 +++--
 ext.c                              |   12 +-
 extension/ChangeLog                |   20 ++
 extension/Makefile.am              |   18 ++
 extension/Makefile.in              |   47 ++++-
 extension/aclocal.m4               |    1 +
 extension/configh.in               |   11 ++
 extension/configure                |   79 ++++++++-
 extension/configure.ac             |    2 +-
 extension/filefuncs.c              |    1 +
 extension/fnmatch.c                |    1 +
 extension/fork.c                   |    1 +
 extension/m4/ChangeLog             |    4 +
 {m4 => extension/m4}/intlmacosx.m4 |    0
 extension/ordchr.c                 |    1 +
 extension/readdir.3am              |   17 ++-
 extension/readdir.c                |   25 ++--
 extension/readfile.c               |    1 +
 extension/revoutput.c              |  137 +++++++++++++++
 extension/revtwoway.c              |  336 ++++++++++++++++++++++++++++++++++++
 extension/rwarray.c                |    1 +
 extension/rwarray0.c               |    1 +
 extension/testext.c                |    1 +
 extension/time.c                   |    1 +
 gawkapi.c                          |   82 +++++++++-
 gawkapi.h                          |   84 +++++++++-
 io.c                               |  265 ++++++++++++++++++++++++-----
 main.c                             |   11 +-
 po/it.gmo                          |  Bin 44922 -> 44464 bytes
 po/it.po                           |   15 --
 test/ChangeLog                     |    5 +
 test/Makefile.am                   |    6 +-
 test/Makefile.in                   |   16 ++-
 test/Maketests                     |   10 +
 test/revout.awk                    |    6 +
 test/revout.ok                     |    1 +
 test/revtwoway.awk                 |   11 ++
 test/revtwoway.ok                  |    2 +
 40 files changed, 1212 insertions(+), 121 deletions(-)
 copy {m4 => extension/m4}/intlmacosx.m4 (100%)
 create mode 100644 extension/revoutput.c
 create mode 100644 extension/revtwoway.c
 create mode 100644 test/revout.awk
 create mode 100644 test/revout.ok
 create mode 100644 test/revtwoway.awk
 create mode 100644 test/revtwoway.ok


hooks/post-receive
-- 
gawk



reply via email to

[Prev in Thread] Current Thread [Next in Thread]