1 /*
2  * sysread.c - interface to system read/write
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1998-2003 Peter Stephenson
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Peter Stephenson or the Zsh Development
16  * Group be liable to any party for direct, indirect, special, incidental,
17  * or consequential damages arising out of the use of this software and
18  * its documentation, even if Peter Stephenson, and the Zsh
19  * Development Group have been advised of the possibility of such damage.
20  *
21  * Peter Stephenson and the Zsh Development Group specifically
22  * disclaim any warranties, including, but not limited to, the implied
23  * warranties of merchantability and fitness for a particular purpose.  The
24  * software provided hereunder is on an "as is" basis, and Peter Stephenson
25  * and the Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 #include "system.mdh"
31 #include "system.pro"
32 
33 #ifdef HAVE_POLL_H
34 # include <poll.h>
35 #endif
36 #if defined(HAVE_POLL) && !defined(POLLIN)
37 # undef HAVE_POLL
38 #endif
39 
40 #define SYSREAD_BUFSIZE	8192
41 
42 /**/
43 static int
getposint(char * instr,char * nam)44 getposint(char *instr, char *nam)
45 {
46     char *eptr;
47     int ret;
48 
49     ret = (int)zstrtol(instr, &eptr, 10);
50     if (*eptr || ret < 0) {
51 	zwarnnam(nam, "integer expected: %s", instr);
52 	return -1;
53     }
54 
55     return ret;
56 }
57 
58 
59 /*
60  * Return values of bin_sysread:
61  *	0	Successfully read (and written if appropriate)
62  *	1	Error in parameters to command
63  *	2	Error on read, or polling read fd ) ERRNO set by
64  *      3	Error on write			  ) system
65  *	4	Timeout on read
66  *	5       Zero bytes read, end of file
67  */
68 
69 /**/
70 static int
bin_sysread(char * nam,char ** args,Options ops,UNUSED (int func))71 bin_sysread(char *nam, char **args, Options ops, UNUSED(int func))
72 {
73     int infd = 0, outfd = -1, bufsize = SYSREAD_BUFSIZE, count;
74     char *outvar = NULL, *countvar = NULL, *inbuf;
75 
76     /* -i: input file descriptor if not stdin */
77     if (OPT_ISSET(ops, 'i')) {
78 	infd = getposint(OPT_ARG(ops, 'i'), nam);
79 	if (infd < 0)
80 	    return 1;
81     }
82 
83     /* -o: output file descriptor, else store in REPLY */
84     if (OPT_ISSET(ops, 'o')) {
85 	if (*args) {
86 	    zwarnnam(nam, "no argument allowed with -o");
87 	    return 1;
88 	}
89 	outfd = getposint(OPT_ARG(ops, 'o'), nam);
90 	if (outfd < 0)
91 	    return 1;
92     }
93 
94     /* -s: buffer size if not default SYSREAD_BUFSIZE */
95     if (OPT_ISSET(ops, 's')) {
96 	bufsize = getposint(OPT_ARG(ops, 's'), nam);
97 	if (bufsize < 0)
98 	    return 1;
99     }
100 
101     /* -c: name of variable to store count of transferred bytes */
102     if (OPT_ISSET(ops, 'c')) {
103 	countvar = OPT_ARG(ops, 'c');
104 	if (!isident(countvar)) {
105 	    zwarnnam(nam, "not an identifier: %s", countvar);
106 	    return 1;
107 	}
108     }
109 
110     if (*args) {
111 	/*
112 	 * Variable in which to store result if doing a plain read.
113 	 * Default variable if not specified is REPLY.
114 	 * If writing, only stuff we couldn't write is stored here,
115 	 * no default in that case (we just discard it if no variable).
116 	 */
117 	outvar = *args;
118 	if (!isident(outvar)) {
119 	    zwarnnam(nam, "not an identifier: %s", outvar);
120 	    return 1;
121 	}
122     }
123 
124     inbuf = zhalloc(bufsize);
125 
126 #if defined(HAVE_POLL) || defined(HAVE_SELECT)
127     /* -t: timeout */
128     if (OPT_ISSET(ops, 't'))
129     {
130 # ifdef HAVE_POLL
131 	struct pollfd poll_fd;
132 	mnumber to_mn;
133 	int to_int, ret;
134 
135 	poll_fd.fd = infd;
136 	poll_fd.events = POLLIN;
137 
138 	to_mn = matheval(OPT_ARG(ops, 't'));
139 	if (errflag)
140 	    return 1;
141 	if (to_mn.type == MN_FLOAT)
142 	    to_int = (int) (1000 * to_mn.u.d);
143 	else
144 	    to_int = 1000 * (int)to_mn.u.l;
145 
146 	while ((ret = poll(&poll_fd, 1, to_int)) < 0) {
147 	    if (errno != EINTR || errflag || retflag || breaks || contflag)
148 		break;
149 	}
150 	if (ret <= 0) {
151 	    /* treat non-timeout error as error on read */
152 	    return ret ? 2 : 4;
153 	}
154 # else
155 	/* using select */
156 	struct timeval select_tv;
157 	fd_set fds;
158 	mnumber to_mn;
159 	int ret;
160 
161 	FD_ZERO(&fds);
162 	FD_SET(infd, &fds);
163 	to_mn = matheval(OPT_ARG(ops, 't'));
164 	if (errflag)
165 	    return 1;
166 
167 	if (to_mn.type == MN_FLOAT) {
168 	    select_tv.tv_sec = (int) to_mn.u.d;
169 	    select_tv.tv_usec =
170 		(int) ((to_mn.u.d - select_tv.tv_sec) * 1e6);
171 	} else {
172 	    select_tv.tv_sec = (int) to_mn.u.l;
173 	    select_tv.tv_usec = 0;
174 	}
175 
176 	while ((ret = select(infd+1, (SELECT_ARG_2_T) &fds,
177 			     NULL, NULL,&select_tv)) < 0) {
178 	    if (errno != EINTR || errflag || retflag || breaks || contflag)
179 		break;
180 	}
181 	if (ret <= 0) {
182 	    /* treat non-timeout error as error on read */
183 	    return ret ? 2 : 4;
184 	}
185 # endif
186     }
187 #endif
188 
189     while ((count = read(infd, inbuf, bufsize)) < 0) {
190 	if (errno != EINTR || errflag || retflag || breaks || contflag)
191 	    break;
192     }
193     if (countvar)
194 	setiparam(countvar, count);
195     if (count < 0)
196 	return 2;
197 
198     if (outfd >= 0) {
199 	if (!count)
200 	    return 5;
201 	while (count > 0) {
202 	    int ret;
203 
204 	    ret = write(outfd, inbuf, count);
205 	    if (ret < 0) {
206 		if (errno == EINTR && !errflag &&
207 		    !retflag && !breaks && !contflag)
208 		    continue;
209 		if (outvar)
210 		    setsparam(outvar, metafy(inbuf, count, META_DUP));
211 		if (countvar)
212 		    setiparam(countvar, count);
213 		return 3;
214 	    }
215 	    inbuf += ret;
216 	    count -= ret;
217 	}
218 	return 0;
219     }
220 
221     if (!outvar)
222 	    outvar = "REPLY";
223     /* do this even if we read zero bytes */
224     setsparam(outvar, metafy(inbuf, count, META_DUP));
225 
226     return count ? 0 : 5;
227 }
228 
229 
230 /*
231  * Return values of bin_syswrite:
232  *	0	Successfully written
233  *	1	Error in parameters to command
234  *	2	Error on write, ERRNO set by system
235  */
236 
237 /**/
238 static int
bin_syswrite(char * nam,char ** args,Options ops,UNUSED (int func))239 bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func))
240 {
241     int outfd = 1, len, count, totcount;
242     char *countvar = NULL;
243 
244     /* -o: output file descriptor if not stdout */
245     if (OPT_ISSET(ops, 'o')) {
246 	outfd = getposint(OPT_ARG(ops, 'o'), nam);
247 	if (outfd < 0)
248 	    return 1;
249     }
250 
251     /* -c: variable in which to store count of bytes written */
252     if (OPT_ISSET(ops, 'c')) {
253 	countvar = OPT_ARG(ops, 'c');
254 	if (!isident(countvar)) {
255 	    zwarnnam(nam, "not an identifier: %s", countvar);
256 	    return 1;
257 	}
258     }
259 
260     totcount = 0;
261     unmetafy(*args, &len);
262     while (len) {
263 	while ((count = write(outfd, *args, len)) < 0) {
264 	    if (errno != EINTR || errflag || retflag || breaks || contflag)
265 	    {
266 		if (countvar)
267 		    setiparam(countvar, totcount);
268 		return 2;
269 	    }
270 	}
271 	*args += count;
272 	totcount += count;
273 	len -= count;
274     }
275     if (countvar)
276 	setiparam(countvar, totcount);
277 
278     return 0;
279 }
280 
281 
282 static struct { char *name; int oflag; } openopts[] = {
283 #ifdef O_CLOEXEC
284     { "cloexec", O_CLOEXEC },
285 #else
286 # ifdef FD_CLOEXEC
287     { "cloexec", 0  }, /* this needs to be first in the table */
288 # endif
289 #endif
290 #ifdef O_NOFOLLOW
291     { "nofollow", O_NOFOLLOW },
292 #endif
293 #ifdef O_SYNC
294     { "sync", O_SYNC },
295 #endif
296 #ifdef O_NOATIME
297     { "noatime", O_NOATIME },
298 #endif
299     { "excl", O_EXCL | O_CREAT },
300     { "creat", O_CREAT },
301     { "create", O_CREAT },
302     { "truncate", O_TRUNC },
303     { "trunc", O_TRUNC }
304 };
305 
306 /**/
307 static int
bin_sysopen(char * nam,char ** args,Options ops,UNUSED (int func))308 bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func))
309 {
310     int read = OPT_ISSET(ops, 'r');
311     int write = OPT_ISSET(ops, 'w');
312     int append = OPT_ISSET(ops, 'a') ? O_APPEND : 0;
313     int flags = O_NOCTTY | append | ((append || write) ?
314 	(read ? O_RDWR : O_WRONLY) : O_RDONLY);
315     char *opt, *ptr, *nextopt, *fdvar;
316     int o, fd, moved_fd, explicit = -1;
317     mode_t perms = 0666;
318 #if defined(FD_CLOEXEC) && !defined(O_CLOEXEC)
319     int fdflags = 0;
320 #endif
321 
322     if (!OPT_ISSET(ops, 'u')) {
323 	zwarnnam(nam, "file descriptor not specified");
324 	return 1;
325     }
326 
327     /* file descriptor, either 0-9 or a variable name */
328     fdvar = OPT_ARG(ops, 'u');
329     if (idigit(*fdvar) && !fdvar[1]) {
330 	explicit = atoi(fdvar);
331     } else if (!isident(fdvar)) {
332 	zwarnnam(nam, "not an identifier: %s", fdvar);
333 	return 1;
334     }
335 
336     /* open options */
337     if (OPT_ISSET(ops, 'o')) {
338 	opt = OPT_ARG(ops, 'o');
339 	while (opt) {
340 	    if (!strncasecmp(opt, "O_", 2)) /* ignore initial O_ */
341 		opt += 2;
342 	    if ((nextopt = strchr(opt, ',')))
343 		*nextopt++ = '\0';
344 	    for (o = sizeof(openopts)/sizeof(*openopts) - 1; o >= 0 &&
345 		strcasecmp(openopts[o].name, opt); o--) {}
346 	    if (o < 0) {
347 		zwarnnam(nam, "unsupported option: %s\n", opt);
348 		return 1;
349 	    }
350 #if defined(FD_CLOEXEC) && !defined(O_CLOEXEC)
351 	    if (!openopts[o].oflag)
352 		fdflags = FD_CLOEXEC;
353 #endif
354 	    flags |= openopts[o].oflag;
355 	    opt = nextopt;
356 	}
357     }
358 
359     /* -m: permissions or mode for created files */
360     if (OPT_ISSET(ops, 'm')) {
361 	ptr = opt = OPT_ARG(ops, 'm');
362 	while (*ptr >= '0' && *ptr <= '7') ptr++;
363 	if (*ptr || ptr - opt < 3) {
364 	    zwarnnam(nam, "invalid mode %s", opt);
365 	    return 1;
366 	}
367 	perms = zstrtol(opt, 0, 8); /* octal number */
368     }
369 
370     if (flags & O_CREAT)
371 	fd = open(*args, flags, perms);
372     else
373 	fd = open(*args, flags);
374 
375     if (fd == -1) {
376 	zwarnnam(nam, "can't open file %s: %e", *args, errno);
377 	return 1;
378     }
379     moved_fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd);
380     if (moved_fd == -1) {
381 	zwarnnam(nam, "can't open file %s", *args);
382 	return 1;
383     }
384 
385 #ifdef FD_CLOEXEC
386 #ifdef O_CLOEXEC
387     /*
388      * the O_CLOEXEC is a flag attached to the *file descriptor*, not the
389      * *open file description* so it doesn't survive a dup(). If that flag was
390      * requested and the fd was moved, we need to reapply it to the moved fd
391      * even if the original one was open with O_CLOEXEC
392      */
393     if ((flags & O_CLOEXEC) && fd != moved_fd)
394 #else
395     if (fdflags)
396 #endif /* O_CLOEXEC */
397 	fcntl(moved_fd, F_SETFD, FD_CLOEXEC);
398 #endif /* FD_CLOEXEC */
399     fdtable[moved_fd] = FDT_EXTERNAL;
400     if (explicit == -1) {
401 	setiparam(fdvar, moved_fd);
402 	/* if setting the variable failed, close moved_fd to avoid leak */
403 	if (errflag)
404 	    zclose(moved_fd);
405     }
406 
407     return 0;
408 }
409 
410 
411 /*
412  * Return values of bin_sysseek:
413  *	0	Success
414  *	1	Error in parameters to command
415  *	2	Error on seek, ERRNO set by system
416  */
417 
418 /**/
419 static int
bin_sysseek(char * nam,char ** args,Options ops,UNUSED (int func))420 bin_sysseek(char *nam, char **args, Options ops, UNUSED(int func))
421 {
422     int w = SEEK_SET, fd = 0;
423     char *whence;
424     off_t pos;
425 
426     /* -u:  file descriptor if not stdin */
427     if (OPT_ISSET(ops, 'u')) {
428 	fd = getposint(OPT_ARG(ops, 'u'), nam);
429 	if (fd < 0)
430 	    return 1;
431     }
432 
433     /* -w:  whence - starting point of seek */
434     if (OPT_ISSET(ops, 'w')) {
435 	whence = OPT_ARG(ops, 'w');
436         if (!(strcasecmp(whence, "current") && strcmp(whence, "1")))
437 	    w = SEEK_CUR;
438         else if (!(strcasecmp(whence, "end") && strcmp(whence, "2")))
439 	    w = SEEK_END;
440 	else if (strcasecmp(whence, "start") && strcmp(whence, "0")) {
441 	    zwarnnam(nam, "unknown argument to -w: %s", whence);
442 	    return 1;
443 	}
444     }
445 
446     pos = (off_t)mathevali(*args);
447     return (lseek(fd, pos, w) == -1) ? 2 : 0;
448 }
449 
450 /**/
451 static mnumber
math_systell(UNUSED (char * name),UNUSED (int argc),mnumber * argv,UNUSED (int id))452 math_systell(UNUSED(char *name), UNUSED(int argc), mnumber *argv, UNUSED(int id))
453 {
454     int fd = (argv->type == MN_INTEGER) ? argv->u.l : (int)argv->u.d;
455     mnumber ret;
456     ret.type = MN_INTEGER;
457     ret.u.l = 0;
458 
459     if (fd < 0) {
460 	zerr("file descriptor out of range");
461 	return ret;
462     }
463     ret.u.l = lseek(fd, 0, SEEK_CUR);
464     return ret;
465 }
466 
467 
468 /*
469  * Return values of bin_syserror:
470  *	0	Successfully processed error
471  *		(although if the number was invalid the string
472  *		may not be useful)
473  *	1	Error in parameters
474  *	2	Name of error not recognised.
475  */
476 
477 /**/
478 static int
bin_syserror(char * nam,char ** args,Options ops,UNUSED (int func))479 bin_syserror(char *nam, char **args, Options ops, UNUSED(int func))
480 {
481     int num = 0;
482     char *errvar = NULL, *msg, *pfx = "", *str;
483 
484     /* variable in which to write error message */
485     if (OPT_ISSET(ops, 'e')) {
486 	errvar = OPT_ARG(ops, 'e');
487 	if (!isident(errvar)) {
488 	    zwarnnam(nam, "not an identifier: %s", errvar);
489 	    return 1;
490 	}
491     }
492     /* prefix for error message */
493     if (OPT_ISSET(ops, 'p'))
494 	pfx = OPT_ARG(ops, 'p');
495 
496     if (!*args)
497 	num = errno;
498     else {
499 	char *ptr = *args;
500 	while (*ptr && idigit(*ptr))
501 	    ptr++;
502 	if (!*ptr && ptr > *args)
503 	    num = atoi(*args);
504 	else {
505 	    const char **eptr;
506 	    for (eptr = sys_errnames; *eptr; eptr++) {
507 		if (!strcmp(*eptr, *args)) {
508 		    num = (eptr - sys_errnames) + 1;
509 		    break;
510 		}
511 	    }
512 	    if (!*eptr)
513 		return 2;
514 	}
515     }
516 
517     msg = strerror(num);
518     if (errvar) {
519 	str = (char *)zalloc(strlen(msg) + strlen(pfx) + 1);
520 	sprintf(str, "%s%s", pfx, msg);
521 	setsparam(errvar, str);
522     } else {
523 	fprintf(stderr, "%s%s\n", pfx, msg);
524     }
525 
526     return 0;
527 }
528 
529 /**/
530 static int
bin_zsystem_flock(char * nam,char ** args,UNUSED (Options ops),UNUSED (int func))531 bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
532 {
533     int cloexec = 1, unlock = 0, readlock = 0;
534     zlong timeout = -1;
535     char *fdvar = NULL;
536 #ifdef HAVE_FCNTL_H
537     struct flock lck;
538     int flock_fd, flags;
539 #endif
540 
541     while (*args && **args == '-') {
542 	int opt;
543 	char *optptr = *args + 1, *optarg;
544 	args++;
545 	if (!*optptr || !strcmp(optptr, "-"))
546 	    break;
547 	while ((opt = *optptr)) {
548 	    switch (opt) {
549 	    case 'e':
550 		/* keep lock on "exec" */
551 		cloexec = 0;
552 		break;
553 
554 	    case 'f':
555 		/* variable for fd */
556 		if (optptr[1]) {
557 		    fdvar = optptr + 1;
558 		    optptr += strlen(fdvar) - 1;
559 		} else if (*args) {
560 		    fdvar = *args++;
561 		}
562 		if (fdvar == NULL || !isident(fdvar)) {
563 		    zwarnnam(nam, "flock: option %c requires a variable name",
564 			     opt);
565 		    return 1;
566 		}
567 		break;
568 
569 	    case 'r':
570 		/* read lock rather than read-write lock */
571 		readlock = 1;
572 		break;
573 
574 	    case 't':
575 		/* timeout in seconds */
576 		if (optptr[1]) {
577 		    optarg = optptr + 1;
578 		    optptr += strlen(optarg) - 1;
579 		} else if (!*args) {
580 		    zwarnnam(nam, "flock: option %c requires a numeric timeout",
581 			     opt);
582 		    return 1;
583 		} else {
584 		    optarg = *args++;
585 		}
586 		timeout = mathevali(optarg);
587 		break;
588 
589 	    case 'u':
590 		/* unlock: argument is fd */
591 		unlock = 1;
592 		break;
593 
594 	    default:
595 		zwarnnam(nam, "flock: unknown option: %c", *optptr);
596 		return 1;
597 	    }
598 	    optptr++;
599 	}
600     }
601 
602 
603     if (!args[0]) {
604 	zwarnnam(nam, "flock: not enough arguments");
605 	return 1;
606     }
607     if (args[1]) {
608 	zwarnnam(nam, "flock: too many arguments");
609 	return 1;
610     }
611 
612 #ifdef HAVE_FCNTL_H
613     if (unlock) {
614 	flock_fd = (int)mathevali(args[0]);
615 	if (zcloselockfd(flock_fd) < 0) {
616 	    zwarnnam(nam, "flock: file descriptor %d not in use for locking",
617 		     flock_fd);
618 	    return 1;
619 	}
620 	return 0;
621     }
622 
623     if (readlock)
624 	flags = O_RDONLY | O_NOCTTY;
625     else
626 	flags = O_RDWR | O_NOCTTY;
627     if ((flock_fd = open(unmeta(args[0]), flags)) < 0) {
628 	zwarnnam(nam, "failed to open %s for writing: %e", args[0], errno);
629 	return 1;
630     }
631     flock_fd = movefd(flock_fd);
632     if (flock_fd == -1)
633 	return 1;
634 #ifdef FD_CLOEXEC
635     if (cloexec)
636     {
637 	long fdflags = fcntl(flock_fd, F_GETFD, 0);
638 	if (fdflags != (long)-1)
639 	    fcntl(flock_fd, F_SETFD, fdflags | FD_CLOEXEC);
640     }
641 #endif
642     addlockfd(flock_fd, cloexec);
643 
644     lck.l_type = readlock ? F_RDLCK : F_WRLCK;
645     lck.l_whence = SEEK_SET;
646     lck.l_start = 0;
647     lck.l_len = 0;  /* lock the whole file */
648 
649     if (timeout > 0) {
650 	time_t end = time(NULL) + (time_t)timeout;
651 	while (fcntl(flock_fd, F_SETLK, &lck) < 0) {
652 	    if (errflag) {
653                 zclose(flock_fd);
654 		return 1;
655             }
656 	    if (errno != EINTR && errno != EACCES && errno != EAGAIN) {
657                 zclose(flock_fd);
658 		zwarnnam(nam, "failed to lock file %s: %e", args[0], errno);
659 		return 1;
660 	    }
661 	    if (time(NULL) >= end) {
662                 zclose(flock_fd);
663 		return 2;
664             }
665 	    sleep(1);
666 	}
667     } else {
668 	while (fcntl(flock_fd, timeout == 0 ? F_SETLK : F_SETLKW, &lck) < 0) {
669 	    if (errflag) {
670                 zclose(flock_fd);
671 		return 1;
672             }
673 	    if (errno == EINTR)
674 		continue;
675             zclose(flock_fd);
676 	    zwarnnam(nam, "failed to lock file %s: %e", args[0], errno);
677 	    return 1;
678 	}
679     }
680 
681     if (fdvar)
682 	setiparam(fdvar, flock_fd);
683 
684     return 0;
685 #else /* HAVE_FCNTL_H */
686     zwarnnam(nam, "flock: not implemented on this system");
687     return 255;
688 #endif /* HAVE_FCNTL_H */
689 }
690 
691 
692 /*
693  * Return status zero if the zsystem feature is supported, else 1.
694  * Operates silently for future-proofing.
695  */
696 /**/
697 static int
bin_zsystem_supports(char * nam,char ** args,UNUSED (Options ops),UNUSED (int func))698 bin_zsystem_supports(char *nam, char **args,
699 		     UNUSED(Options ops), UNUSED(int func))
700 {
701     if (!args[0]) {
702 	zwarnnam(nam, "supports: not enough arguments");
703 	return 255;
704     }
705     if (args[1]) {
706 	zwarnnam(nam, "supports: too many arguments");
707 	return 255;
708     }
709 
710     /* stupid but logically this should work... */
711     if (!strcmp(*args, "supports"))
712 	return 0;
713 #ifdef HAVE_FCNTL_H
714     if (!strcmp(*args, "flock"))
715 	return 0;
716 #endif
717     return 1;
718 }
719 
720 
721 /**/
722 static int
bin_zsystem(char * nam,char ** args,Options ops,int func)723 bin_zsystem(char *nam, char **args, Options ops, int func)
724 {
725     /* If more commands are implemented, this can be more sophisticated */
726     if (!strcmp(*args, "flock")) {
727 	return bin_zsystem_flock(nam, args+1, ops, func);
728     } else if (!strcmp(*args, "supports")) {
729 	return bin_zsystem_supports(nam, args+1, ops, func);
730     }
731     zwarnnam(nam, "unknown subcommand: %s", *args);
732     return 1;
733 }
734 
735 static struct builtin bintab[] = {
736     BUILTIN("syserror", 0, bin_syserror, 0, 1, 0, "e:p:", NULL),
737     BUILTIN("sysread", 0, bin_sysread, 0, 1, 0, "c:i:o:s:t:", NULL),
738     BUILTIN("syswrite", 0, bin_syswrite, 1, 1, 0, "c:o:", NULL),
739     BUILTIN("sysopen", 0, bin_sysopen, 1, 1, 0, "rwau:o:m:", NULL),
740     BUILTIN("sysseek", 0, bin_sysseek, 1, 1, 0, "u:w:", NULL),
741     BUILTIN("zsystem", 0, bin_zsystem, 1, -1, 0, NULL, NULL)
742 };
743 
744 
745 /* Functions for the errnos special parameter. */
746 
747 /**/
748 static char **
errnosgetfn(UNUSED (Param pm))749 errnosgetfn(UNUSED(Param pm))
750 {
751     /* arrdup etc. should really take const pointers as arguments */
752     return arrdup((char **)sys_errnames);
753 }
754 
755 static const struct gsu_array errnos_gsu =
756 { errnosgetfn, arrsetfn, stdunsetfn };
757 
758 
759 /* Functions for the sysparams special parameter. */
760 
761 /**/
762 static void
fillpmsysparams(Param pm,const char * name)763 fillpmsysparams(Param pm, const char *name)
764 {
765     char buf[DIGBUFSIZE];
766     int num;
767 
768     pm->node.nam = dupstring(name);
769     pm->node.flags = PM_SCALAR | PM_READONLY;
770     pm->gsu.s = &nullsetscalar_gsu;
771     if (!strcmp(name, "pid")) {
772 	num = (int)getpid();
773     } else if (!strcmp(name, "ppid")) {
774 	num = (int)getppid();
775     } else if (!strcmp(name, "procsubstpid")) {
776 	num = (int)procsubstpid;
777     } else {
778 	pm->u.str = dupstring("");
779 	pm->node.flags |= PM_UNSET;
780 	return;
781     }
782 
783     sprintf(buf, "%d", num);
784     pm->u.str = dupstring(buf);
785 }
786 
787 
788 /**/
789 static HashNode
getpmsysparams(UNUSED (HashTable ht),const char * name)790 getpmsysparams(UNUSED(HashTable ht), const char *name)
791 {
792     Param pm;
793 
794     pm = (Param) hcalloc(sizeof(struct param));
795     fillpmsysparams(pm, name);
796     return &pm->node;
797 }
798 
799 
800 /**/
801 static void
scanpmsysparams(UNUSED (HashTable ht),ScanFunc func,int flags)802 scanpmsysparams(UNUSED(HashTable ht), ScanFunc func, int flags)
803 {
804     struct param spm;
805 
806     fillpmsysparams(&spm, "pid");
807     func(&spm.node, flags);
808     fillpmsysparams(&spm, "ppid");
809     func(&spm.node, flags);
810     fillpmsysparams(&spm, "procsubstpid");
811     func(&spm.node, flags);
812 }
813 
814 static struct mathfunc mftab[] = {
815     NUMMATHFUNC("systell", math_systell, 1, 1, 0)
816 };
817 
818 static struct paramdef partab[] = {
819     SPECIALPMDEF("errnos", PM_ARRAY|PM_READONLY,
820 		 &errnos_gsu, NULL, NULL),
821     SPECIALPMDEF("sysparams", PM_READONLY,
822 		 NULL, getpmsysparams, scanpmsysparams)
823 };
824 
825 static struct features module_features = {
826     bintab, sizeof(bintab)/sizeof(*bintab),
827     NULL, 0,
828     mftab, sizeof(mftab)/sizeof(*mftab),
829     partab, sizeof(partab)/sizeof(*partab),
830     0
831 };
832 
833 /* The load/unload routines required by the zsh library interface */
834 
835 /**/
836 int
setup_(UNUSED (Module m))837 setup_(UNUSED(Module m))
838 {
839     return 0;
840 }
841 
842 /**/
843 int
features_(Module m,char *** features)844 features_(Module m, char ***features)
845 {
846     *features = featuresarray(m, &module_features);
847     return 0;
848 }
849 
850 /**/
851 int
enables_(Module m,int ** enables)852 enables_(Module m, int **enables)
853 {
854     return handlefeatures(m, &module_features, enables);
855 }
856 
857 /**/
858 int
boot_(UNUSED (Module m))859 boot_(UNUSED(Module m))
860 {
861     return 0;
862 }
863 
864 
865 /**/
866 int
cleanup_(Module m)867 cleanup_(Module m)
868 {
869     return setfeatureenables(m, &module_features, NULL);
870 }
871 
872 /**/
873 int
finish_(UNUSED (Module m))874 finish_(UNUSED(Module m))
875 {
876     return 0;
877 }
878