Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: zsh/system library



Clearing out some patches which have advanced as far as they're likely
to for the time being.

Here is a provisional interface to the read and write system calls via
sysread and syswrite builtins in a new library zsh/system.

An interface to system errors is also provided.  The $errnos array
contains error macro names (EINTR, etc.) and is as I originally proposed
for zsh/params but by popular vote (1 to 0) it has been moved here.
There is also an interface to output (or write to an array) the full
error message.  This is the command syserror; there are several reasons
this is a command rather than another array (1) the messages are much
longer than the names and so waste space in the shell (2) they can
change dynamically due to internationalization support (3) the standard
C interface is a function (4) unlike the ENAMES, you are unlikely to
need them internally in the code.

The other change is that the existing ERRNO parameter is now writable.
This allows setting it to 0 consistent with using the system interface
from C.

I sneaked/snuck in some extra .cvsignore lines which I noticed when I
was adding the documentation.

Yet another internal poll/select call.  Sigh.

Possible enhancements include interfaces to fcntl and lseek.

Further comments welcome.

Index: zshconfig.ac
===================================================================
RCS file: /cvsroot/zsh/zsh/zshconfig.ac,v
retrieving revision 1.37
diff -u -r1.37 zshconfig.ac
--- zshconfig.ac	25 Apr 2003 11:18:51 -0000	1.37
+++ zshconfig.ac	30 Aug 2003 17:43:10 -0000
@@ -1117,6 +1117,39 @@
 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 -----------------------------------------------------
Index: Doc/.cvsignore
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/.cvsignore,v
retrieving revision 1.3
diff -u -r1.3 .cvsignore
--- Doc/.cvsignore	5 May 2003 06:14:51 -0000	1.3
+++ Doc/.cvsignore	30 Aug 2003 17:43:12 -0000
@@ -15,3 +15,4 @@
 zsh_*.ps
 infodir
 *.swp
+zsh.pdf zsh_a4.pdf zsh_us.pdf
Index: Doc/Makefile.in
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Makefile.in,v
retrieving revision 1.17
diff -u -r1.17 Makefile.in
--- Doc/Makefile.in	6 Feb 2003 12:21:51 -0000	1.17
+++ Doc/Makefile.in	30 Aug 2003 17:43:13 -0000
@@ -60,10 +60,11 @@
 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 \
Index: Doc/Zsh/mod_system.yo
===================================================================
RCS file: Doc/Zsh/mod_system.yo
diff -N Doc/Zsh/mod_system.yo
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Doc/Zsh/mod_system.yo	30 Aug 2003 17:43:13 -0000
@@ -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()
Index: Doc/Zsh/params.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/params.yo,v
retrieving revision 1.18
diff -u -r1.18 params.yo
--- Doc/Zsh/params.yo	4 Jul 2003 16:05:20 -0000	1.18
+++ Doc/Zsh/params.yo	30 Aug 2003 17:43:19 -0000
@@ -520,7 +520,8 @@
 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>)(
Index: Src/params.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/params.c,v
retrieving revision 1.71
diff -u -r1.71 params.c
--- Src/params.c	4 Apr 2003 16:47:07 -0000	1.71
+++ Src/params.c	30 Aug 2003 17:43:38 -0000
@@ -137,7 +137,7 @@
 #define GFN(X) BR(((char *(*)_((Param)))(X)))
 #define IPDEF1(A,B,C,D) {NULL,A,PM_INTEGER|PM_SPECIAL|D,BR(NULL),SFN(C),GFN(B),stdunsetfn,10,NULL,NULL,NULL,0}
 IPDEF1("#", poundgetfn, nullintsetfn, PM_READONLY),
-IPDEF1("ERRNO", errnogetfn, nullintsetfn, PM_READONLY),
+IPDEF1("ERRNO", errnogetfn, errnosetfn, 0),
 IPDEF1("GID", gidgetfn, gidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("EGID", egidgetfn, egidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("HISTSIZE", histsizegetfn, histsizesetfn, PM_RESTRICTED),
@@ -3012,6 +3012,15 @@
 {
     if ((savehistsiz = v) < 0)
 	savehistsiz = 0;
+}
+
+/* Function to set value for special parameter `ERRNO' */
+
+/**/
+void
+errnosetfn(Param pm, zlong x)
+{
+    errno = (int)x;
 }
 
 /* Function to get value for special parameter `ERRNO' */
Index: Src/Modules/.cvsignore
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/.cvsignore,v
retrieving revision 1.2
diff -u -r1.2 .cvsignore
--- Src/Modules/.cvsignore	5 May 2003 06:14:51 -0000	1.2
+++ Src/Modules/.cvsignore	30 Aug 2003 17:43:47 -0000
@@ -13,3 +13,4 @@
 *.mdhs
 *.mdh.tmp
 *.swp
+ernames.c errcount.h
Index: Src/Modules/errnames1.awk
===================================================================
RCS file: Src/Modules/errnames1.awk
diff -N Src/Modules/errnames1.awk
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Src/Modules/errnames1.awk	30 Aug 2003 17:43:47 -0000
@@ -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)
+}
Index: Src/Modules/errnames2.awk
===================================================================
RCS file: Src/Modules/errnames2.awk
diff -N Src/Modules/errnames2.awk
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Src/Modules/errnames2.awk	30 Aug 2003 17:43:47 -0000
@@ -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 "};"
+}
Index: Src/Modules/system.c
===================================================================
RCS file: Src/Modules/system.c
diff -N Src/Modules/system.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Src/Modules/system.c	30 Aug 2003 17:43:49 -0000
@@ -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;
+}
Index: Src/Modules/system.mdd
===================================================================
RCS file: Src/Modules/system.mdd
diff -N Src/Modules/system.mdd
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Src/Modules/system.mdd	30 Aug 2003 17:43:49 -0000
@@ -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
+

-- 
Peter Stephenson <pws@xxxxxxxxxxxxxxxxxxxxxxxx>
Work: pws@xxxxxxx
Web: http://www.pwstephenson.fsnet.co.uk



Messages sorted by: Reverse Date, Date, Thread, Author