diff options
Diffstat (limited to 'Src/Modules/system.c')
-rw-r--r-- | Src/Modules/system.c | 418 |
1 files changed, 418 insertions, 0 deletions
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; +} |