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