18980: new zsh/system module

This commit is contained in:
Peter Stephenson 2003-08-30 19:06:06 +00:00
parent c016b2dcdc
commit c9c5f9da93
11 changed files with 680 additions and 3 deletions

View file

@ -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.

View file

@ -15,3 +15,4 @@ zsh.tp zsh.tps
zsh_*.ps
infodir
*.swp
zsh.pdf zsh_a4.pdf zsh_us.pdf

View file

@ -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 \

128
Doc/Zsh/mod_system.yo Normal file
View file

@ -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()

View file

@ -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>)(

View file

@ -13,3 +13,4 @@ so_locations
*.mdhs
*.mdh.tmp
*.swp
ernames.c errcount.h

18
Src/Modules/errnames1.awk Normal file
View file

@ -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)
}

42
Src/Modules/errnames2.awk Normal file
View file

@ -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 "};"
}

418
Src/Modules/system.c Normal file
View file

@ -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;
}

27
Src/Modules/system.mdd Normal file
View file

@ -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

View file

@ -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 -----------------------------------------------------