summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2003-08-30 19:06:06 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2003-08-30 19:06:06 +0000
commitc9c5f9da9366d1587c588507433cec35ef243579 (patch)
tree99d69de0866c36adf40dc516c8ab3ab64ad480ce
parentc016b2dcdc7faade5b279c279b2d506b9ea22bf7 (diff)
downloadzsh-c9c5f9da9366d1587c588507433cec35ef243579.tar.gz
zsh-c9c5f9da9366d1587c588507433cec35ef243579.tar.xz
zsh-c9c5f9da9366d1587c588507433cec35ef243579.zip
18980: new zsh/system module
-rw-r--r--ChangeLog7
-rw-r--r--Doc/.cvsignore1
-rw-r--r--Doc/Makefile.in5
-rw-r--r--Doc/Zsh/mod_system.yo128
-rw-r--r--Doc/Zsh/params.yo3
-rw-r--r--Src/Modules/.cvsignore1
-rw-r--r--Src/Modules/errnames1.awk18
-rw-r--r--Src/Modules/errnames2.awk42
-rw-r--r--Src/Modules/system.c418
-rw-r--r--Src/Modules/system.mdd27
-rw-r--r--zshconfig.ac33
11 files changed, 680 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index ee08cb553..20ea2b6d6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2003-08-30  Peter Stephenson  <pws@pwstephenson.fsnet.co.uk>
 
+	* 18980: zshconfig.ac, Doc/.cvsignore, Doc/Makefile.in,
+	Doc/Zsh/mod_system.yo, Doc/Zsh/params.yo, Src/params.c,
+	Src/Modules/.cvsignore, Src/Modules/errnames1.awk,
+	Src/Modules/errnames2.awk, Src/Modules/system.c,
+	Src/Modules/system.mdd: zsh/system module provides interface
+	to read, write and system errors.
+
 	* 18982: Src/subst.c:  Comments/rant for paramsubst().
 
 	* 18981: Doc/Zsh/tcpsys.yo: General tidy up.
diff --git a/Doc/.cvsignore b/Doc/.cvsignore
index 9d1374c2f..23228b27f 100644
--- a/Doc/.cvsignore
+++ b/Doc/.cvsignore
@@ -15,3 +15,4 @@ zsh.tp zsh.tps
 zsh_*.ps
 infodir
 *.swp
+zsh.pdf zsh_a4.pdf zsh_us.pdf
diff --git a/Doc/Makefile.in b/Doc/Makefile.in
index e5f691f08..692b7653b 100644
--- a/Doc/Makefile.in
+++ b/Doc/Makefile.in
@@ -60,10 +60,11 @@ Zsh/mod_computil.yo \
 Zsh/mod_deltochar.yo Zsh/mod_example.yo Zsh/mod_files.yo \
 Zsh/mod_mapfile.yo Zsh/mod_mathfunc.yo Zsh/mod_parameter.yo Zsh/mod_pcre.yo \
 Zsh/mod_sched.yo Zsh/mod_socket.yo \
-Zsh/mod_stat.yo Zsh/mod_termcap.yo Zsh/mod_terminfo.yo \
+Zsh/mod_stat.yo  Zsh/mod_system.yo Zsh/mod_tcp.yo \
+Zsh/mod_termcap.yo Zsh/mod_terminfo.yo \
 Zsh/mod_zftp.yo Zsh/mod_zle.yo Zsh/mod_zleparameter.yo \
 Zsh/mod_zprof.yo Zsh/mod_zpty.yo Zsh/mod_zselect.yo \
-Zsh/mod_zutil.yo Zsh/mod_tcp.yo
+Zsh/mod_zutil.yo
 
 YODLSRC = zmacros.yo zman.yo ztexi.yo Zsh/arith.yo Zsh/builtins.yo \
 Zsh/compat.yo Zsh/compctl.yo Zsh/compsys.yo Zsh/compwid.yo Zsh/cond.yo \
diff --git a/Doc/Zsh/mod_system.yo b/Doc/Zsh/mod_system.yo
new file mode 100644
index 000000000..1dc581bed
--- /dev/null
+++ b/Doc/Zsh/mod_system.yo
@@ -0,0 +1,128 @@
+COMMENT(!MOD!zsh/system
+A builtin interface to various low-level system features.
+!MOD!)
+The tt(zsh/system) module makes available three builtin commands and
+a parameter.
+
+sect(Builtins)
+
+startitem()
+findex(syserror)
+item(tt(syserror) tt([ -e) var(errvar) tt(] [ -p) var(prefix) tt(] [) var(errno) tt(|) var(errname ]))(
+This command prints out the error message associated with var(errno), a
+system error number, followed by a newline to standard error.
+
+Instead of the error number, a name var(errname), for example
+tt(ENOENT), may be used.  The set of names is the same as the contents
+of the array tt(errnos), see below.
+
+If the string var(prefix) is given, it is printed in front of the error
+message, with no intervening space.
+
+If var(errvar) is supplied, the entire message, without a newline, is
+assigned to the parameter names var(errvar) and nothing is output.
+
+A return value of 0 indicates the message was successfully printed
+(although it may not be useful if the error number was out of the
+system's range), a return value of 1 indicates an error in the
+parameters, and a return value of 2 indicates the error name was
+not recognised (no message is printed for this).
+)
+findex(sysread)
+xitem(tt(sysread [ -c) var(countvar) tt(] [ -i) var(infd) tt(] [ -o) var(outfd) tt(]))
+item(  tt([ -s) var(bufsize) tt(] [ -t) var(timeout) tt(] [) var(param) tt(]))(
+Perform a single system read from file descriptor var(infd), or zero if
+that is not given.  The result of the read is stored in var(param) or
+var(REPLY) if that is not given.  If var(countvar) is given, the number
+of bytes read is assigned to the parameter named by var(countvar).
+
+The maximum number of bytes read is var(bufsize) or 8192 if that is not
+given, however the command returns as soon as any number of bytes was
+successfully read.
+
+If var(timeout) is given, it specifies a timeout in seconds, which may
+be zero to poll the file descriptor.  This is handled by the tt(poll)
+system call if available, otherwise the tt(select) system call if
+available.
+
+If var(outfd) is given, an attempt is made to write all the bytes just
+read to the file descriptor var(outfd).  If this fails, because of a
+system error other than tt(EINTR) or because of an internal zsh error
+during an interrupt, the bytes read but not written are stored in the
+parameter named by var(param) if supplied (no default is used in this
+case), and the number of bytes read but not written is stored in the
+parameter named by var(countvar) if that is supplied.  If it was
+successful, var(countvar) contains the full number of bytes transferred,
+as usual, and var(param) is not set.
+
+The error tt(EINTR) (interrupted system call) is handled internally so
+that shell interrupts are transparent to the caller.  Any other error
+causes a return.
+
+The possible return values are
+startitem()
+item(0)(
+At least one byte of data was successfully read and, if appropriate,
+written.
+)
+item(1)(
+There was an error in the parameters to the command.  This is the only
+error for which a message is printed to standard error.
+)
+item(2)(
+There was an error on the read, or on polling the input file descriptor
+for a timeout.  The parameter tt(ERRNO) gives the error.
+)
+item(3)(
+Data were successfully read, but there was an error writing them
+to var(outfd).  The parameter tt(ERRNO) gives the error.
+)
+item(4)(
+The attempt to read timed out.  Note this does not set tt(ERRNO) as this
+is not a system error.
+)
+item(5)(
+No system error occurred, but zero bytes were read.  This usually
+indicates end of file.  The parameters are set according to the
+usual rules; no write to var(outfd) is attempted.
+)
+enditem()
+)
+item(tt(syswrite [ -c) var(countvar) tt(] [ -o) var(outfd) tt(]) var(data))(
+The data (a single string of bytes) are written to the file descriptor
+var(outfd), or 1 if that is not given, using the tt(write) system call.
+Multiple write operations may be used if the first does not write all
+the data.
+
+If var(countvar) is given, the number of byte written is stored in the
+parameter named by var(countvar); this may not be the full length of
+var(data) if an error occurred.
+
+The error tt(EINTR) (interrupted system call) is handled internally by
+retrying; otherwise an error causes the command to return.  For example,
+if the file descriptor is set to non-blocking output, an error
+tt(EAGAIN) (on some systems, tt(EWOULDBLOCK)) may result in the command
+returning early.
+
+The return status may be 0 for success, 1 for an error in the parameters
+to the command, or 2 for an error on the write; no error message is
+printed in the last case, but the parameter tt(ERRNO) will reflect
+the error that occurred.
+)
+enditem()
+
+sect(Parameters)
+
+startitem()
+item(tt(errnos))(
+A readonly array of the names of errors defined on the system.  These
+are typically macros defined in C by including the system header file
+tt(errno.h).  The index of each name (assuming the option tt(KSH_ARRAYS)
+is unset) corresponds to the error number.  Error numbers var(num)
+before the last known error which have no name are given the name
+tt(E)var(num) in the array.
+
+Note that aliases for errors are not handled; only the canonical name is
+used.
+)
+enditem()
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 675a75991..f2f64c93c 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -520,7 +520,8 @@ item(tt(ERRNO) <S>)(
 The value of errno (see manref(errno)(3))
 as set by the most recently failed system call.
 This value is system dependent and is intended for debugging
-purposes.
+purposes.  It is also useful with the tt(zsh/system) module which
+allows the number to be turned into a name or message.
 )
 vindex(GID)
 item(tt(GID) <S>)(
diff --git a/Src/Modules/.cvsignore b/Src/Modules/.cvsignore
index d0ea7d755..acafc7ad3 100644
--- a/Src/Modules/.cvsignore
+++ b/Src/Modules/.cvsignore
@@ -13,3 +13,4 @@ so_locations
 *.mdhs
 *.mdh.tmp
 *.swp
+ernames.c errcount.h
diff --git a/Src/Modules/errnames1.awk b/Src/Modules/errnames1.awk
new file mode 100644
index 000000000..5c41197cb
--- /dev/null
+++ b/Src/Modules/errnames1.awk
@@ -0,0 +1,18 @@
+# Edited version of Src/signames1.awk.
+#
+# This is an awk script which finds out what the possibilities for
+# the error names are, and dumps them out so that cpp can turn them
+# into numbers.  Since we don't need to decide here what the
+# real signals are, we can afford to be generous about definitions,
+# in case the definitions are in terms of other definitions.
+# However, we need to avoid definitions with parentheses, which will
+# mess up the syntax.
+BEGIN { printf "#include <errno.h>\n\n" }
+
+/^[\t ]*#[\t ]*define[\t ]*E[A-Z0-9]*[\t ][\t ]*[^(\t ]/ { 
+    eindex = index($0, "E")
+    etail = substr($0, eindex, 80)
+    split(etail, tmp)
+    enam = substr(tmp[1], 2, 20)
+    printf("XXNAMES XXE%s E%s\n", enam, enam)
+}
diff --git a/Src/Modules/errnames2.awk b/Src/Modules/errnames2.awk
new file mode 100644
index 000000000..60969b423
--- /dev/null
+++ b/Src/Modules/errnames2.awk
@@ -0,0 +1,42 @@
+# Edited version of Src/signames2.awk.
+#
+# {g,n}awk script to generate errnames.c
+# This version relies on the previous output of the preprocessor
+# on sigtmp.c, sigtmp.out, which is in turn generated by errnames1.awk.
+#
+# NB: On SunOS 4.1.3 - user-functions don\'t work properly, also \" problems
+# Without 0 + hacks some nawks compare numbers as strings
+#
+/^XXNAMES XXE[A-Z0-9]*[\t ][\t ]*[1-9][0-9]*/ {
+    eindex = index($0, "E")
+    etail = substr($0, 11, 80)
+    split(etail, tmp)
+    enam = tmp[1]
+    enum = tmp[2]
+    if (errname[enum] == "") {
+	errname[enum] = enam
+	if (0 + max < 0 + enum && enum < 1024)
+	    max = enum
+    }
+}
+
+END {
+    ps = "%s"
+    printf "/** errnames.c                                 **/\n"
+    printf "/** architecture-customized errnames.c for zsh **/\n"
+    printf "\n"
+    printf "#define ERRCOUNT\t%d\n", max
+    printf "\n"
+    printf "#include %csystem.mdh%c\n", 34, 34
+    printf "\n"
+    printf "/**/\n"
+    printf "const char *sys_errnames[ERRCOUNT+1] = {\n"
+
+    for (i = 1; i <= 0 + max; i++)
+	if (errname[i] == "")
+	    printf("\t%cE%d%c,\n", 34, i, 34)
+	else
+	    printf("\t%c%s%c,\n", 34, errname[i], 34)
+    print "\tNULL"
+    print "};"
+}
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
new file mode 100644
index 000000000..174bf133d
--- /dev/null
+++ b/Src/Modules/system.c
@@ -0,0 +1,418 @@
+/*
+ * sysread.c - interface to system read/write
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1998-2003 Peter Stephenson
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Peter Stephenson or the Zsh Development
+ * Group be liable to any party for direct, indirect, special, incidental,
+ * or consequential damages arising out of the use of this software and
+ * its documentation, even if Peter Stephenson, and the Zsh
+ * Development Group have been advised of the possibility of such damage.
+ *
+ * Peter Stephenson and the Zsh Development Group specifically
+ * disclaim any warranties, including, but not limited to, the implied
+ * warranties of merchantability and fitness for a particular purpose.  The
+ * software provided hereunder is on an "as is" basis, and Peter Stephenson
+ * and the Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "system.mdh"
+#include "system.pro"
+
+#ifdef HAVE_POLL_H
+# include <poll.h>
+#endif
+#if defined(HAVE_POLL) && !defined(POLLIN)
+# undef HAVE_POLL
+#endif
+
+#define SYSREAD_BUFSIZE	8192
+
+/**/
+static int
+getposint(char *instr, char *nam)
+{
+    char *eptr;
+    int ret;
+
+    ret = (int)zstrtol(instr, &eptr, 10);
+    if (*eptr || ret < 0) {
+	zwarnnam(nam, "integer expected: %s", instr, 0);
+	return -1;
+    }
+
+    return ret;
+}
+
+
+/*
+ * Return values of bin_sysread:
+ *	0	Successfully read (and written if appropriate)
+ *	1	Error in parameters to command
+ *	2	Error on read, or polling read fd ) ERRNO set by
+ *      3	Error on write			  ) system
+ *	4	Timeout on read
+ *	5       Zero bytes read, end of file
+ */
+
+/**/
+static int
+bin_sysread(char *nam, char **args, Options ops, int func)
+{
+    int infd = 0, outfd = -1, bufsize = SYSREAD_BUFSIZE, count;
+    char *outvar = NULL, *countvar = NULL, *inbuf;
+
+    /* -i: input file descriptor if not stdin */
+    if (OPT_ISSET(ops, 'i')) {
+	infd = getposint(OPT_ARG(ops, 'i'), nam);
+	if (infd < 0)
+	    return 1;
+    }
+
+    /* -o: output file descriptor, else store in REPLY */
+    if (OPT_ISSET(ops, 'o')) {
+	if (*args) {
+	    zwarnnam(nam, "no argument allowed with -o", NULL, 0);
+	    return 1;
+	}
+	outfd = getposint(OPT_ARG(ops, 'o'), nam);
+	if (outfd < 0)
+	    return 1;
+    }
+
+    /* -s: buffer size if not default SYSREAD_BUFSIZE */
+    if (OPT_ISSET(ops, 's')) {
+	bufsize = getposint(OPT_ARG(ops, 's'), nam);
+	if (bufsize < 0)
+	    return 1;
+    }
+
+    /* -c: name of variable to store count of transferred bytes */
+    if (OPT_ISSET(ops, 'c')) {
+	countvar = OPT_ARG(ops, 'c');
+	if (!isident(countvar)) {
+	    zwarnnam(nam, "not an identifier: %s", countvar, 0);
+	    return 1;
+	}
+    }
+
+    if (*args) {
+	/*
+	 * Variable in which to store result if doing a plain read.
+	 * Default variable if not specified is REPLY.
+	 * If writing, only stuff we couldn't write is stored here,
+	 * no default in that case (we just discard it if no variable).
+	 */
+	outvar = *args;
+	if (!isident(outvar)) {
+	    zwarnnam(nam, "not an identifier: %s", outvar, 0);
+	    return 1;
+	}
+    }
+
+    inbuf = zhalloc(bufsize);
+
+#if defined(HAVE_POLL) || defined(HAVE_SELECT)
+    /* -t: timeout */
+    if (OPT_ISSET(ops, 't'))
+    {
+# ifdef HAVE_POLL
+	struct pollfd poll_fd;
+	mnumber to_mn;
+	int to_int, ret;
+
+	poll_fd.fd = infd;
+	poll_fd.events = POLLIN;
+
+	to_mn = matheval(OPT_ARG(ops, 't'));
+	if (errflag)
+	    return 1;
+	if (to_mn.type == MN_FLOAT)
+	    to_int = (int) (1000 * to_mn.u.d);
+	else
+	    to_int = 1000 * (int)to_mn.u.l;
+
+	while ((ret = poll(&poll_fd, 1, to_int)) < 0) {
+	    if (errno != EINTR || errflag || retflag || breaks || contflag)
+		break;
+	}
+	if (ret <= 0) {
+	    /* treat non-timeout error as error on read */
+	    return ret ? 2 : 4;
+	}
+# else
+	/* using select */
+	struct timeval select_tv;
+	fd_set fds;
+	mnumber to_mn;
+	int ret;
+
+	FD_ZERO(&fds);
+	FD_SET(infd, &fds);
+	to_mn = matheval(OPT_ARG(ops, 't'));
+	if (errflag)
+	    return 1;
+
+	if (to_mn.type == MN_FLOAT) {
+	    select_tv.tv_sec = (int) to_mn.u.d;
+	    select_tv.tv_usec =
+		(int) ((to_mn.u.d - select_tv.tv_sec) * 1e6);
+	} else {
+	    select_tv.tv_sec = (int) to_mn.u.l;
+	    select_tv.tv_usec = 0;
+	}
+
+	while ((ret = select(infd+1, (SELECT_ARG_2_T) &fds, 
+			     NULL, NULL,&select_tv)) < 1) {
+	    if (errno != EINTR || errflag || retflag || breaks || contflag)
+		break;
+	}
+	if (ret <= 0) {
+	    /* treat non-timeout error as error on read */
+	    return ret ? 2 : 4;
+	}
+# endif
+    }
+#endif
+
+    while ((count = read(infd, inbuf, bufsize)) < 0) {
+	if (errno != EINTR || errflag || retflag || breaks || contflag)
+	    break;
+    }
+    if (countvar)
+	setiparam(countvar, count);
+    if (count < 0)
+	return 2;
+
+    if (outfd >= 0) {
+	if (!count)
+	    return 5;
+	while (count > 0) {
+	    int ret;
+
+	    ret = write(outfd, inbuf, count);
+	    if (ret < 0) {
+		if (errno == EINTR && !errflag &&
+		    !retflag && !breaks && !contflag)
+		    continue;
+		if (outvar)
+		    setsparam(outvar, metafy(inbuf, count, META_DUP));
+		if (countvar)
+		    setiparam(countvar, count);
+		return 3;
+	    }
+	    inbuf += ret;
+	    count -= ret;
+	}
+	return 0;
+    }
+
+    if (!outvar)
+	    outvar = "REPLY";
+    /* do this even if we read zero bytes */
+    setsparam(outvar, metafy(inbuf, count, META_DUP));
+
+    return count ? 0 : 5;
+}
+
+
+/*
+ * Return values of bin_syswrite:
+ *	0	Successfully written
+ *	1	Error in parameters to command
+ *	2	Error on write, ERRNO set by system
+ */
+
+/**/
+static int
+bin_syswrite(char *nam, char **args, Options ops, int func)
+{
+    int outfd = 1, len, count, totcount;
+    char *countvar = NULL;
+
+    /* -o: output file descriptor if not stdout */
+    if (OPT_ISSET(ops, 'o')) {
+	outfd = getposint(OPT_ARG(ops, 'o'), nam);
+	if (outfd < 0)
+	    return 1;
+    }
+
+    /* -c: variable in which to store count of bytes written */
+    if (OPT_ISSET(ops, 'c')) {
+	countvar = OPT_ARG(ops, 'c');
+	if (!isident(countvar)) {
+	    zwarnnam(nam, "not an identifier: %s", countvar, 0);
+	    return 1;
+	}
+    }
+
+    totcount = 0;
+    unmetafy(*args, &len);
+    while (len) {
+	while ((count = write(outfd, *args, len)) < 0) {
+	    if (errno != EINTR || errflag || retflag || breaks || contflag)
+	    {
+		if (countvar)
+		    setiparam(countvar, totcount);
+		return 2;
+	    }
+	}
+	*args += count;
+	totcount += count;
+	len -= count;
+    }
+    if (countvar)
+	setiparam(countvar, totcount);
+
+    return 0;
+}
+
+
+/*
+ * Return values of bin_syserror:
+ *	0	Successfully processed error
+ *		(although if the number was invalid the string
+ *		may not be useful)
+ *	1	Error in parameters
+ *	2	Name of error not recognised.
+ */
+
+/**/
+static int
+bin_syserror(char *nam, char **args, Options ops, int func)
+{
+    int num = 0;
+    char *errvar = NULL, *msg, *pfx = "", *str;
+
+    /* variable in which to write error message */
+    if (OPT_ISSET(ops, 'e')) {
+	errvar = OPT_ARG(ops, 'e');
+	if (!isident(errvar)) {
+	    zwarnnam(nam, "not an identifier: %s", errvar, 0);
+	    return 1;
+	}
+    }
+    /* prefix for error message */
+    if (OPT_ISSET(ops, 'p'))
+	pfx = OPT_ARG(ops, 'p');
+
+    if (!*args)
+	num = errno;
+    else {
+	char *ptr = *args;
+	while (*ptr && idigit(*ptr))
+	    ptr++;
+	if (!*ptr && ptr > *args)
+	    num = atoi(*args);
+	else {
+	    const char **eptr;
+	    for (eptr = sys_errnames; *eptr; eptr++) {
+		if (!strcmp(*eptr, *args)) {
+		    num = (eptr - sys_errnames) + 1;
+		    break;
+		}
+	    }
+	    if (!*eptr)
+		return 2;
+	}
+    }
+
+    msg = strerror(num);
+    if (errvar) {
+	str = (char *)zalloc(strlen(msg) + strlen(pfx) + 1);
+	sprintf(str, "%s%s", pfx, msg);
+	setsparam(errvar, str);
+    } else {
+	fprintf(stderr, "%s%s\n", pfx, msg);
+    }
+
+    return 0;
+}
+
+
+/* Functions for the errnos special parameter. */
+
+/**/
+static char **
+errnosgetfn(Param pm)
+{
+    /* arrdup etc. should really take const pointers as arguments */
+    return arrdup((char **)sys_errnames);
+}
+
+
+static struct builtin bintab[] = {
+    BUILTIN("syserror", 0, bin_syserror, 0, 1, 0, "e:p:", NULL),
+    BUILTIN("sysread", 0, bin_sysread, 0, 1, 0, "c:i:o:s:t:", NULL),
+    BUILTIN("syswrite", 0, bin_syswrite, 1, 1, 0, "c:o:", NULL),
+};
+
+
+/* The load/unload routines required by the zsh library interface */
+
+/**/
+int
+setup_(Module m)
+{
+    return 0;
+}
+
+/**/
+static void
+tidyparam(Param pm)
+{
+    if (!pm)
+	return;
+    pm->flags &= ~PM_READONLY;
+    unsetparam_pm(pm, 0, 1);
+}
+
+
+/**/
+int
+boot_(Module m)
+{
+    Param pm_nos;
+
+    /* this takes care of an autoload on errnos */
+    unsetparam("errnos");
+    if (!(pm_nos = createparam("errnos", PM_ARRAY|PM_SPECIAL|PM_READONLY|
+			       PM_HIDE|PM_HIDEVAL|PM_REMOVABLE)))
+	return 1;
+    pm_nos->gets.afn = errnosgetfn;
+
+    if (!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab))) {
+	tidyparam(pm_nos);
+	return 1;
+    }
+    return 0;
+}
+
+
+/**/
+int
+cleanup_(Module m)
+{
+    tidyparam((Param)paramtab->getnode(paramtab, "errnos"));
+
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
+}
+
+/**/
+int
+finish_(Module m)
+{
+    return 0;
+}
diff --git a/Src/Modules/system.mdd b/Src/Modules/system.mdd
new file mode 100644
index 000000000..61950994a
--- /dev/null
+++ b/Src/Modules/system.mdd
@@ -0,0 +1,27 @@
+name=zsh/system
+link=dynamic
+load=no
+
+autobins="sysread syswrite syserror"
+
+autoparams="errnos"
+
+objects="system.o errnames.o"
+
+headers="errcount.h"
+
+:<<\Make
+errnames.c: errnames1.awk errnames2.awk $(dir_top)/config.h @ERRNO_H@
+	   if [ x@ERRNO_H@ = x ]; then \
+		touch errtmp.out; \
+	   else \
+		$(AWK) -f $(sdir)/errnames1.awk @ERRNO_H@ >errtmp.c; \
+		$(CPP) errtmp.c >errtmp.out; \
+	   fi
+	   $(AWK) -f $(sdir)/errnames2.awk errtmp.out > $@
+	   rm -f errtmp.c errtmp.out
+
+errcount.h: errnames.c
+	grep 'define.*ERRCOUNT' errnames.c > $@
+Make
+
diff --git a/zshconfig.ac b/zshconfig.ac
index 01cc9a086..a9992c603 100644
--- a/zshconfig.ac
+++ b/zshconfig.ac
@@ -1117,6 +1117,39 @@ zsh_cv_path_signal_h=$SIGNAL_H
 SIGNAL_H=$zsh_cv_path_signal_h
 AC_SUBST(SIGNAL_H)dnl
 
+dnl Where are error names located?  Needed as input for errnames1.awk
+AC_CACHE_CHECK(where error names are located, zsh_cv_path_errno_h,
+[dnl Look at the output from the preprocessor.
+dnl We should get lines of the form `# 1 "/usr/include/errno.h"'
+dnl The following assumes the real definitions are in a file which
+dnl contains the name `err'; we could relax this if necessary,
+dnl but then you can get a rather long list of files to test.
+dnl The backslash substitution is to persuade cygwin to cough up
+dnl slashes rather than doubled backslashes in the path.
+echo "#include <errno.h>" > nametmp.c
+errfile_list="`$CPP nametmp.c |
+sed -n 's/^#[ 	].*\"\(.*\)\"/\1/p' |
+sed 's/\\\\\\\\/\//g' |
+$AWK '{ if (\$1 ~ \"err\") files[[\$1]] = \$1 }
+  END { for (var in files) print var }'`"
+rm -f nametmp.c
+for ERRNO_H in $errfile_list /dev/null
+do
+  dnl Try to make sure it doesn't get confused by files that don't
+  dnl have real error definitions in.  Count definitions to make sure.
+  nerrs=`test -f $ERRNO_H && \
+  grep '#[ 	]*define[ 	][ 	]*E[0-9A-Z]*[ 	]*[0-9][0-9]*' $ERRNO_H | \
+  wc -l | sed 's/[ 	]//g'`
+  test "x$nerrs" != x && test "$nerrs" -ge 7 && break
+done
+if test $ERRNO_H = "/dev/null"; then
+  AC_MSG_ERROR(ERROR MACROS NOT FOUND:  please report to developers)
+fi
+zsh_cv_path_errno_h=$ERRNO_H
+])
+ERRNO_H=$zsh_cv_path_errno_h
+AC_SUBST(ERRNO_H)dnl
+
 dnl -----------------------------------------------------
 dnl Look for the file containing the RLIMIT_* definitions
 dnl -----------------------------------------------------