386BSD 0.1 development
[unix-history] / usr / othersrc / public / zsh-2.2 / src / builtin.c
CommitLineData
dbf02a84
WJ
1/*
2 *
3 * builtin.c - builtin commands
4 *
5 * This file is part of zsh, the Z shell.
6 *
7 * This software is Copyright 1992 by Paul Falstad
8 *
9 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
10 * use this software as long as: there is no monetary profit gained
11 * specifically from the use or reproduction of this software, it is not
12 * sold, rented, traded or otherwise marketed, and this copyright notice is
13 * included prominently in any copy made.
14 *
15 * The author make no claims as to the fitness or correctness of this software
16 * for any use whatsoever, and it is provided as is. Any use of this software
17 * is at the user's own risk.
18 *
19 */
20
21#include "zsh.h"
22#include <sys/errno.h>
23
24#define makecond() allocnode(N_COND)
25
26/* builtin flags */
27
28#define BINF_PLUSOPTS 1 /* +xyz legal */
29#define BINF_R 2 /* this is r (fc -e -) */
30#define BINF_PRINTOPTS 4
31#define BINF_SETOPTS 8
32#define BINF_FCOPTS 16
33#define BINF_TYPEOPT 32
34#define BINF_TYPEOPTS (BINF_TYPEOPT|BINF_PLUSOPTS)
35#define BINF_ECHOPTS 64
36
37/* builtin funcs */
38
39#define BIN_TYPESET 0
40#define BIN_BG 1
41#define BIN_FG 2
42#define BIN_JOBS 3
43#define BIN_WAIT 4
44#define BIN_DISOWN 5
45#define BIN_BREAK 6
46#define BIN_CONTINUE 7
47#define BIN_EXIT 8
48#define BIN_RETURN 9
49#define BIN_SHIFT 10
50#define BIN_CD 11
51#define BIN_POPD 12
52#define BIN_PUSHD 13
53#define BIN_PRINT 14
54#define BIN_EVAL 15
55#define BIN_SCHED 16
56#define BIN_FC 17
57#define BIN_PUSHLINE 18
58#define BIN_LOGOUT 19
59#define BIN_BUILTIN 20
60#define BIN_TEST 21
61#define BIN_BRACKET 22
62
63struct bincmd {
64 char *name;
65 int (*handlerfunc) DCLPROTO((char *,char **,char *,int));
66 int minargs; /* min # of args */
67 int maxargs; /* max # of args, or -1 for no limit */
68 int flags; /* BINF_flags (see above) */
69 int funcid; /* xbins (see above) for overloaded handlerfuncs */
70 char *optstr; /* string of legal options */
71 char *defopts; /* options set by default for overloaded handlerfuncs */
72 };
73
74/* structure for foo=bar assignments */
75
76struct asgment {
77 struct asgment *next;
78 char *name,*value;
79 };
80
81static char *auxdata;
82static int auxlen;
83static int showflag = 0,showflag2 = 0;
84
85struct bincmd builtins[] = {
86 "[",bin_test,0,-1,0,BIN_BRACKET,NULL,NULL,
87 ".",bin_dot,1,-1,0,0,NULL,NULL,
88 ":",bin_colon,0,-1,0,0,NULL,NULL,
89 "alias",bin_alias,0,-1,0,0,"ga",NULL,
90 "autoload",bin_typeset,0,-1,BINF_TYPEOPTS,0,"tx","fu",
91 "bg",bin_fg,0,-1,0,BIN_BG,NULL,NULL,
92 "bindkey",bin_bindkey,0,-1,0,0,"asvemdrl",NULL,
93 "break",bin_break,0,1,0,BIN_BREAK,NULL,NULL,
94 "builtin",NULL,0,0,0,BIN_BUILTIN,NULL,NULL,
95 "bye",bin_break,0,1,0,BIN_EXIT,NULL,NULL,
96 "cd",bin_cd,0,2,0,BIN_CD,NULL,NULL,
97 "chdir",bin_cd,0,2,0,BIN_CD,NULL,NULL,
98 "compctl",bin_compctl,0,-1,0,0,NULL,NULL,
99 "continue",bin_break,0,1,0,BIN_CONTINUE,NULL,NULL,
100 "declare",bin_typeset,0,-1,BINF_TYPEOPTS,0,"LRZfilrtux",NULL,
101 "dirs",bin_dirs,0,-1,0,0,"v",NULL,
102 "disable",bin_disable,1,-1,0,0,NULL,NULL,
103 "disown",bin_fg,1,-1,0,BIN_DISOWN,NULL,NULL,
104 "echo",bin_print,0,-1,BINF_PRINTOPTS|BINF_ECHOPTS,BIN_PRINT,"n","-",
105 "echotc",bin_echotc,1,-1,0,0,NULL,NULL,
106 "enable",bin_enable,1,-1,0,0,NULL,NULL,
107 "eval",bin_eval,0,-1,0,BIN_EVAL,NULL,NULL,
108 "exit",bin_break,0,1,0,BIN_EXIT,NULL,NULL,
109 "export",bin_typeset,0,-1,BINF_TYPEOPTS,0,"LRZfilrtu","x",
110 "false",bin_let,0,0,0,0,NULL,NULL,
111 "fc",bin_fc,0,-1,BINF_FCOPTS,BIN_FC,"nlreRWAdD",NULL,
112 "fg",bin_fg,0,-1,0,BIN_FG,NULL,NULL,
113 "functions",bin_typeset,0,-1,BINF_TYPEOPTS,0,"tu","f",
114 "getln",bin_read,0,-1,0,0,NULL,"zr",
115 "getopts",bin_getopts,2,-1,0,0,NULL,NULL,
116 "hash",bin_hash,2,2,0,0,"r",NULL,
117 "history",bin_fc,0,-1,0,BIN_FC,"nrdD","l",
118 "integer",bin_typeset,0,-1,BINF_TYPEOPTS,0,"LRZlrtux","i",
119 "jobs",bin_fg,0,-1,0,BIN_JOBS,"lpZ",NULL,
120 "kill",bin_kill,0,-1,0,0,NULL,NULL,
121 "let",bin_let,1,-1,0,0,NULL,NULL,
122 "limit",bin_limit,0,-1,0,0,"sh",NULL,
123 "local",bin_typeset,0,-1,BINF_TYPEOPTS,0,"LRZfilrtux",NULL,
124 "log",bin_log,0,0,0,0,NULL,NULL,
125 "logout",bin_break,0,1,0,BIN_LOGOUT,NULL,NULL,
126 "popd",bin_cd,0,2,0,BIN_POPD,NULL,NULL,
127 "print",bin_print,0,-1,BINF_PRINTOPTS,BIN_PRINT,"RDPnrslzNu0123456789p-",NULL,
128 "pushd",bin_cd,0,2,0,BIN_PUSHD,NULL,NULL,
129 "pushln",bin_print,0,-1,BINF_PRINTOPTS,BIN_PRINT,NULL,"-nz",
130 "pwd",bin_pwd,0,0,0,0,NULL,NULL,
131 "r",bin_fc,0,-1,BINF_R,BIN_FC,"nrl",NULL,
132 "read",bin_read,0,-1,0,0,"rzu0123456789p",NULL,
133 "readonly",bin_typeset,0,-1,BINF_TYPEOPTS,0,"LRZfiltux","r",
134 "rehash",bin_rehash,0,0,0,0,"f",NULL,
135 "return",bin_break,0,1,0,BIN_RETURN,NULL,NULL,
136 "sched",bin_sched,0,-1,0,0,NULL,NULL,
137 "set",bin_set,0,-1,BINF_SETOPTS|BINF_PLUSOPTS,0,"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZaefghijklnosuvwxy",NULL,
138 "setopt",bin_setopt,0,-1,BINF_PLUSOPTS,0,"0123456789BCDEFGHIJKLMNOPQRSTUVWXYZaefghijklmnosuvwxy",NULL,
139 "shift",bin_break,0,1,0,BIN_SHIFT,NULL,NULL,
140 "source",bin_dot,1,-1,0,0,NULL,NULL,
141 "suspend",bin_suspend,0,0,0,0,"f",NULL,
142 "test",bin_test,0,-1,0,BIN_TEST,NULL,NULL,
143 "ttyctl",bin_ttyctl,0,0,0,0,"fu",NULL,
144 "times",bin_times,0,0,0,0,NULL,NULL,
145 "trap",bin_trap,0,-1,0,0,NULL,NULL,
146 "true",bin_colon,0,0,0,0,NULL,NULL,
147 "type",bin_whence,0,-1,0,0,"pfa","v",
148 "typeset",bin_typeset,0,-1,BINF_TYPEOPTS,0,"LRZfilrtux",NULL,
149 "ulimit",bin_ulimit,0,1,0,0,"HSacdfmnt",NULL,
150 "umask",bin_umask,0,1,0,0,NULL,NULL,
151 "unalias",bin_unalias,1,-1,0,0,NULL,NULL,
152 "unfunction",bin_unhash,1,-1,0,0,NULL,NULL,
153 "unhash",bin_unhash,1,-1,0,0,NULL,NULL,
154 "unlimit",bin_unlimit,0,-1,0,0,"h",NULL,
155 "unset",bin_unset,1,-1,0,0,NULL,NULL,
156 "unsetopt",bin_setopt,0,-1,BINF_PLUSOPTS,1,"0123456789BCDEFGHIJKLMNOPQRSTUWXYZabefghijklmnosuvwxy",NULL,
157 "vared",bin_vared,1,1,0,0,NULL,NULL,
158 "wait",bin_fg,0,-1,0,BIN_WAIT,NULL,NULL,
159 "whence",bin_whence,0,-1,0,0,"pvcfa",NULL,
160 "which",bin_whence,0,-1,0,0,"pa","c",
161 NULL,NULL,0,0,0,0,NULL,NULL
162 };
163
164/* print options */
165
166static void prtopt()
167{
168struct option *opp;
169
170 if (isset(KSHOPTIONPRINT)) {
171 printf("Current option settings\n");
172 for (opp = optns; opp->name; opp++)
173 printf("%-20s%s\n", opp->name,
174 (opts[opp->id] == OPT_SET) ? "on" : "off");
175 } else
176 for (opp = optns; opp->name; opp++)
177 if (opts[opp->id] == OPT_SET)
178 puts(opp->name);
179}
180
181/* add builtins to the command hash table */
182
183void addbuiltins() /**/
184{
185struct cmdnam *c;
186struct bincmd *b;
187int t0;
188
189 for (t0 = 0, b = builtins; b->name; b++,t0++)
190 {
191 c = (Cmdnam) zcalloc(sizeof *c);
192 c->type = BUILTIN;
193 c->u.binnum = t0;
194 addhperm(b->name,c,cmdnamtab,freecmdnam);
195 }
196}
197
198/* enable */
199
200int bin_enable(name,argv,ops,whocares) /**/
201char *name;char **argv;char *ops;int whocares;
202{
203struct cmdnam *c;
204struct bincmd *b;
205int t0,ret = 0;
206
207 for (; *argv; argv++)
208 {
209 for (t0 = 0, b = builtins; b->name; b++,t0++)
210 if (!strcmp(*argv,b->name))
211 break;
212 if (!b->name)
213 {
214 zerrnam(name,"no such builtin: %s",*argv,0);
215 ret = 1;
216 }
217 else
218 {
219 c = (Cmdnam) zcalloc(sizeof *c);
220 c->type = BUILTIN;
221 c->u.binnum = t0;
222 addhperm(b->name,c,cmdnamtab,freecmdnam);
223 }
224 }
225 return ret;
226}
227
228/* :, true */
229
230int bin_colon(name,argv,ops,whocares) /**/
231char *name;char **argv;char *ops;int whocares;
232{
233 return 0;
234}
235
236/* break, bye, continue, exit, logout, return, shift */
237
238int bin_break(name,argv,ops,func) /**/
239char *name;char **argv;char *ops;int func;
240{
241int num = -1;
242
243 if (*argv)
244 num = matheval(*argv);
245 if ((func == BIN_BREAK || func == BIN_CONTINUE) && !loops)
246 {
247 if (func == BIN_CONTINUE)
248 zerrnam(name,"not in loop",NULL,0);
249 return 1;
250 }
251 switch (func)
252 {
253 case BIN_CONTINUE:
254 contflag = 1;
255 case BIN_BREAK:
256 breaks = (num == -1) ? 1 : num;
257 if (breaks > loops) breaks = loops;
258 break;
259 case BIN_LOGOUT:
260 if (!islogin)
261 {
262 zerrnam(name,"not login shell",NULL,0);
263 return 1;
264 }
265 case BIN_EXIT:
266 zexit((num == -1) ? lastval : num);
267 break;
268 case BIN_RETURN:
269 retflag = 1;
270 return lastval = (num == -1) ? lastval : num;
271 case BIN_SHIFT:
272 {
273 char **s;
274
275 if (num == -1)
276 num = 1;
277 if (num > arrlen(pparams))
278 num = arrlen(pparams);
279 permalloc();
280 s = arrdup(pparams+num);
281 heapalloc();
282 freearray(pparams);
283 pparams = s;
284 break;
285 }
286 }
287 return 0;
288}
289
290/* bg, disown, fg, jobs, wait */
291
292int bin_fg(name,argv,ops,func) /**/
293char *name;char **argv;char *ops;int func;
294{
295int job,lng,firstjob = -1,retval = 0;
296
297 if (ops['Z']) { if (*argv) strcpy(hackzero,*argv); return 0; }
298 lng = (ops['l']) ? 1 : (ops['p']) ? 2 : 0;
299 if ((func == BIN_FG || func == BIN_BG) && !jobbing)
300 {
301 zerrnam(name,"no job control in this shell.",NULL,0);
302 return 1;
303 }
304 if (unset(NOTIFY)) scanjobs();
305 if (!(jobtab[curjob].stat & STAT_INUSE))
306 {
307 curjob = prevjob; setprevjob();
308 if (!(jobtab[curjob].stat & STAT_INUSE))
309 curjob = prevjob; setprevjob();
310 }
311 if (func == BIN_JOBS)
312 stopmsg = 2;
313 if (!*argv)
314 if (func == BIN_FG || func == BIN_BG)
315 {
316 if (curjob == -1 || curjob == thisjob)
317 {
318 zerrnam(name,"no current job",NULL,0);
319 return 1;
320 }
321 firstjob = curjob;
322 }
323 else if (func == BIN_JOBS)
324 {
325 for (job = 0; job != MAXJOB; job++)
326 if (job != thisjob && jobtab[job].stat)
327 printjob(job+jobtab,lng);
328 return 0;
329 }
330 else
331 {
332 for (job = 0; job != MAXJOB; job++)
333 if (job != thisjob && jobtab[job].stat)
334 waitjob(job);
335 return lastval;
336 }
337 for (; (firstjob != -1) || *argv; ( void ) (*argv && argv++))
338 {
339 int stopped,ocj = thisjob;
340
341 if (func == BIN_WAIT && isanum(*argv)) {
342 waitforpid((long) atoi(*argv));
343 retval = lastval;
344 thisjob = ocj;
345 continue;
346 }
347 job = (*argv) ? getjob(*argv,name) : firstjob;
348 firstjob = -1;
349 if (job == -1)
350 break;
351 if (!(jobtab[job].stat & STAT_INUSE))
352 {
353 zerrnam(name,"no such job: %d",0,job);
354 return 1;
355 }
356 switch (func)
357 {
358 case BIN_FG:
359 case BIN_BG:
360 if (stopped = (jobtab[job].stat & STAT_STOPPED))
361 makerunning(jobtab+job);
362 else if (func == BIN_BG)
363 {
364 zerrnam(name,"job already in background",NULL,0);
365 thisjob = ocj;
366 return 1;
367 }
368 if (curjob == job)
369 {
370 curjob = prevjob;
371 prevjob = (func == BIN_BG) ? -1 : job;
372 }
373 if (prevjob == job)
374 prevjob = -1;
375 if (prevjob == -1)
376 setprevjob();
377 if (curjob == -1)
378 {
379 curjob = prevjob;
380 setprevjob();
381 }
382 printjob(jobtab+job,(stopped) ? -1 : 0);
383 if (func == BIN_FG)
384 {
385 thisjob = job;
386 if (strcmp(jobtab[job].pwd,pwd))
387 {
388 printf("(pwd : ");
389 printdir(jobtab[job].pwd);
390 printf(")\n");
391 }
392 fflush(stdout);
393 attachtty(jobtab[job].gleader);
394 }
395 if (stopped)
396 killpg(jobtab[job].gleader,SIGCONT);
397 if (func == BIN_FG)
398 waitjobs();
399 break;
400 case BIN_JOBS:
401 printjob(job+jobtab,lng);
402 break;
403 case BIN_WAIT:
404 waitjob(job);
405 retval = lastval;
406 break;
407 case BIN_DISOWN:
408 {
409 static struct job zero;
410 jobtab[job] = zero;
411 break;
412 }
413 }
414 thisjob = ocj;
415 }
416 return retval;
417}
418
419/* false, let */
420
421int bin_let(name,argv,ops,func) /**/
422char *name;char **argv;char *ops;int func;
423{
424long val = 0;
425
426 while (*argv)
427 val = matheval(*argv++);
428 return !val;
429}
430
431/* print the directory stack */
432
433static void pdstack()
434{
435Lknode node;
436
437 printdir(pwd);
438 for (node = firstnode(dirstack); node; incnode(node))
439 {
440 putchar(' ');
441 printdir(getdata(node));
442 }
443 putchar('\n');
444}
445
446/* exit the shell */
447
448int zexit(val) /**/
449int val;
450{
451 if (isset(MONITOR))
452 if (!stopmsg) {
453 checkjobs();
454 if (stopmsg) {
455 stopmsg = 2;
456 return 1;
457 }
458 } else killrunjobs();
459 savehistfile(getsparam("HISTFILE"),0,0);
460 if (islogin && unset(NORCS))
461 sourcehome(".zlogout");
462 if (sigtrapped[SIGEXIT])
463 dotrap(SIGEXIT);
464 exit(val); return 0;
465}
466
467/* identify an option name */
468
469int optlookup(s) /**/
470char *s;
471{
472char *t;
473struct option *o;
474
475 t = s = strdup(s);
476 while (*t)
477 if (*t == '_')
478 chuck(t);
479 else {
480 *t = tulower(*t);
481 t++;
482 }
483 for (o = optns; o->name; o++)
484 if (!strcmp(o->name,s))
485 return o->id;
486 return -1;
487}
488
489/* setopt, unsetopt */
490
491int bin_setopt(nam,args,ops,isun) /**/
492char *nam;char **args;char *ops;int isun;
493{
494struct option *opp;
495int c;
496
497 if (!ops['@'] && !*args) {
498 if (!isun)
499 prtopt();
500 return 0;
501 }
502 for (opp = optns; opp->name; opp++)
503 if (ops[opp->id] == 1+isun)
504 opts[opp->id] = OPT_SET;
505 else if (ops[opp->id] == 2-isun)
506 opts[opp->id] = OPT_UNSET;
507 while (*args) {
508 c = optlookup(*args++);
509 if (c != -1) {
510 if (c == INTERACTIVE || c == MONITOR)
511 zerrnam(nam,"can't change that option",NULL,0);
512 else
513 opts[c] = (isun) ? OPT_UNSET : OPT_SET;
514 } else {
515 zerrnam(nam,"no such option: %s",args[-1],0);
516 return 1;
517 }
518 }
519 return 0;
520}
521
522/* execute func on each member of the hash table ht */
523
524void listhtable(ht,func) /**/
525Hashtab ht;HFunc func;
526{
527int t0;
528struct hashnode *hn;
529
530 for (t0 = ht->hsize-1; t0 >= 0; t0--)
531 for (hn = ht->nodes[t0]; hn; hn = hn->next)
532 func(hn->nam,(char *) hn);
533}
534
535/* print a shell function (used with listhtable) */
536
537void pshfunc(s,cc) /**/
538char *s;Cmdnam cc;
539{
540char *t;
541
542 if (cc->type != SHFUNC)
543 return;
544 if (showflag && (cc->flags & showflag2) != showflag2)
545 return;
546 if (cc->flags & PMFLAG_u)
547 printf("undefined ");
548 if (cc->flags & PMFLAG_t)
549 printf("traced ");
550 if (!cc->u.list || !showflag) {
551 printf("%s ()\n",s);
552 return;
553 }
554 t = getpermtext((vptr) (cc->u.list));
555 printf("%s () {\n\t%s\n}\n",s,t);
556 free(t);
557}
558
559void niceprint(s) /**/
560char *s;
561{
562 niceprintf(s,stdout);
563}
564
565void niceprintf(s,f) /**/
566char *s;FILE *f;
567{
568 for (; *s; s++)
569 {
570 if (isprint(*s))
571 fputc(*s,f);
572 else if (*s == '\n')
573 {
574 putc('\\',f);
575 putc('n',f);
576 }
577 else
578 {
579 putc('^',f);
580 fputc(*s | 0x40,f);
581 }
582 }
583}
584
585int bin_umask(nam,args,ops,func) /**/
586char *nam;char **args;char *ops;int func;
587{
588int um;
589char *s = *args;
590
591 um = umask(0);
592 umask(um);
593 if (!s)
594 {
595 printf("%03o\n",um);
596 return 0;
597 }
598 if (idigit(*s))
599 {
600 um = zstrtol(s,&s,8);
601 if (*s)
602 {
603 zerrnam(nam,"bad umask",NULL,0);
604 return 1;
605 }
606 }
607 else
608 {
609 int whomask,op,mask;
610
611 for (;;)
612 {
613 if (*s == 'u')
614 s++, whomask = 0100;
615 else if (*s == 'g')
616 s++, whomask = 0010;
617 else if (*s == 'o')
618 s++, whomask = 0001;
619 else
620 whomask = 0111;
621 op = *s++;
622 if (!(op == '+' || op == '-' || op == '='))
623 {
624 zerrnam(nam,"bad symbolic mode operator: %c",NULL,op);
625 return 1;
626 }
627 mask = whomask;
628 if (*s == 'r')
629 mask *= 04;
630 else if (*s == 'w')
631 mask *= 02;
632 else if (*s != 'x')
633 {
634 zerrnam(nam,"bad symbolic mode permission: %c",NULL,*s);
635 return 1;
636 }
637 if (op == '+')
638 um |= mask;
639 else if (op == '-')
640 um &= ~mask;
641 else /* op == '=' */
642 um = (um & ~(whomask*07)) | mask;
643 if (*++s == ',')
644 s++;
645 else
646 break;
647 }
648 if (*s)
649 {
650 zerrnam(nam,"bad character in symbolic mode: %c",NULL,*s);
651 return 1;
652 }
653 }
654 umask(um);
655 return 0;
656}
657
658/* type, whence, which */
659
660int bin_whence(nam,argv,ops,func) /**/
661char *nam;char **argv;char *ops;int func;
662{
663struct cmdnam *chn;
664struct alias *a;
665int retval = 0;
666int csh = ops['c'],all = ops['a'];
667int v = ops['v'] || csh;
668char *cnam;
669
670 for (; *argv; argv++) {
671 if (!ops['p'] && (a = (Alias) gethnode(*argv,aliastab))) {
672 if (a->cmd < 0)
673 printf((csh) ? "%s: shell reserved word\n" :
674 (v) ? "%s is a reserved word\n" : "%s\n",*argv);
675 else if (!v)
676 puts(a->text);
677 else if (a->cmd)
678 printf((csh) ? "%s: aliased to %s\n" :
679 "%s is an alias for %s\n",*argv,a->text);
680 else
681 printf((csh) ? "%s: globally aliased to %s\n" :
682 "%s is a global alias for %s\n",*argv,a->text);
683 retval = 0;
684 if (!all) continue;
685 }
686 if (!ops['p'] && (chn = (Cmdnam) gethnode(*argv,cmdnamtab)) &&
687 (chn->type == SHFUNC || chn->type == BUILTIN)) {
688 if (chn->type == SHFUNC) {
689 if (csh || ops['f']) {
690 showflag = 1; showflag2 = 0;
691 pshfunc(*argv,chn);
692 } else {
693 printf((v) ? "%s is a function\n" : "%s\n",*argv);
694 }
695 } else
696 printf((csh) ? "%s: shell built-in command\n" :
697 (v) ? "%s is a shell builtin\n" : "%s\n",*argv);
698 retval = 0;
699 if (!all) continue;
700 }
701 if (all) {
702 char **pp,buf[MAXPATHLEN],*z;
703 for (pp = path; *pp; pp++) {
704 z = buf;
705 strucpy(&z,*pp);
706 *z++ = '/';
707 strcpy(z,*argv);
708 if (iscom(buf)) {
709 if (v && !csh) printf("%s is %s\n",*argv,buf);
710 else puts(buf);
711 retval = 0;
712 }
713 }
714 } else if (!(cnam = findcmd(*argv))) {
715 if (v) printf("%s not found\n",*argv);
716 retval = 1;
717 break;
718 } else {
719 if (v && !csh) printf("%s is %s\n",*argv,cnam);
720 else puts(cnam);
721 retval = 0;
722 }
723 }
724 return retval;
725}
726
727/* cd, chdir, pushd, popd */
728
729int bin_cd(nam,argv,ops,func) /**/
730char *nam;char **argv;char *ops;int func;
731{
732char *dest;
733
734 if (func == BIN_CD && isset(AUTOPUSHD))
735 func = BIN_PUSHD;
736 dest = cd_get_dest(nam,argv,ops,func);
737 if (!dest) return 1;
738 dest = cd_do_chdir(nam,dest);
739 if (!dest) return 1;
740 cd_new_pwd(func,dest);
741 return 0;
742}
743
744char *cd_get_dest(nam,argv,ops,func) /**/
745char *nam;char **argv;char *ops;int func;
746{
747char *dest;
748
749 if (!argv[0])
750 if (func == BIN_CD || (func == BIN_PUSHD && isset(PUSHDTOHOME)
751 || !full(dirstack)))
752 dest = home;
753 else
754 dest = getnode(dirstack);
755 else if (!argv[1]) {
756 Lknode n;
757 int dd;
758
759 if (argv[0][1] && argv[0][0] == (isset(PUSHDMINUS) ? '-' : '+')) {
760 dd = atoi(argv[0]+1)-1;
761 if (dd < 0) {
762 zerrnam(nam,"bad directory specification",NULL,0);
763 return NULL;
764 }
765 for (n = firstnode(dirstack); n && dd; dd--, incnode(n));
766 if (!n) {
767 zerrnam(nam,"no such entry in dir stack",NULL,0);
768 return NULL;
769 }
770 dest = remnode(dirstack,n);
771 } else if (argv[0][1] && argv[0][0] == (isset(PUSHDMINUS) ? '+' : '-')) {
772 dd = atoi(argv[0]+1);
773 for (n = lastnode(dirstack); n != (Lknode) dirstack && dd;
774 dd--, n = prevnode(n));
775 if (n == (Lknode) dirstack) {
776 zerrnam(nam,"no such entry in dir stack",NULL,0);
777 return NULL;
778 }
779 dest = remnode(dirstack,n);
780 } else {
781 if (!strcmp(argv[0],"-")) printdircr(dest = oldpwd);
782 else dest = argv[0];
783 }
784 } else {
785 char *u;
786 int len1,len2,len3;
787
788 if (!(u = ztrstr(pwd,argv[0]))) {
789 zerrnam(nam,"string not in pwd: %s",argv[0],0);
790 return NULL;
791 }
792 len1 = strlen(argv[0]);
793 len2 = strlen(argv[1]);
794 len3 = u-pwd;
795 dest = alloc(len3+len2+strlen(u+len1)+1);
796 strncpy(dest,pwd,len3);
797 strcpy(dest+len3,argv[1]);
798 strcat(dest,u+len1);
799 printdircr(dest);
800 }
801 return dest;
802}
803
804char *cd_do_chdir(cnam,dest) /**/
805char *cnam; char *dest;
806{
807int hasdot = 0, eno = ENOENT;
808char **pp,*ret;
809
810 if (*dest == '/') {
811 if (ret = cd_try_chdir(NULL,dest)) return ret;
812 zerrnam(cnam,"%e: %s",dest,errno);
813 return NULL;
814 }
815 for (pp = cdpath; *pp; pp++)
816 if ((*pp)[0] == '.' && (*pp)[1] == '\0') hasdot = 1;
817 if (!hasdot) {
818 if (ret = cd_try_chdir(NULL,dest)) return ret;
819 if (errno != ENOENT) eno = errno;
820 }
821 for (pp = cdpath; *pp; pp++) {
822 if (ret = cd_try_chdir(*pp,dest)) {
823 if (strcmp(*pp,".")) {
824 printdircr(ret);
825 }
826 return ret;
827 }
828 if (errno != ENOENT) eno = errno;
829 }
830 if (isset(CDABLEVARS)) {
831 char *s = getsparam(dest);
832 if (s && *s == '/' && chdir(s) != -1) {
833 printdircr(s);
834 return s;
835 }
836 if (errno != ENOENT) eno = errno;
837 }
838 zerrnam(cnam,"%e: %s",dest,eno);
839 return NULL;
840}
841
842char *cd_try_chdir(pfix,dest) /**/
843char *pfix; char *dest;
844{
845static char buf[MAXPATHLEN], buf2[MAXPATHLEN];
846char *s;
847int dotsct;
848
849 if (pfix) sprintf(buf,"%s/%s",(!strcmp("/",pfix)) ? "" : pfix,dest);
850 else strcpy(buf,dest);
851 dotsct = fixdir(buf2,buf);
852 if (buf2[0] == '/') return (chdir(buf2) == -1) ? NULL : buf2;
853 if (!dotsct) {
854 if (chdir((*buf2) ? buf2 : ".") == -1) return NULL;
855 if (*buf2) sprintf(buf,"%s/%s",(!strcmp("/",pwd)) ? "" : pwd,buf2);
856 else strcpy(buf,pwd);
857 return buf;
858 }
859 strcpy(buf,pwd);
860 s = buf+strlen(buf)-1;
861 while (dotsct--) while (s != buf) if (*--s == '/') break;
862 if (s == buf || *buf2) s++;
863 strcpy(s,buf2);
864 if (chdir(buf) != -1 || chdir(dest) != -1) return buf;
865 return NULL;
866}
867
868int fixdir(d,s) /**/
869char *d; char *s;
870{
871int ct = 0;
872char *d0 = d;
873
874#ifdef HAS_RFS
875 if (*s == '/' && s[1] == '.' && s[2] == '.') {
876 *d++ = '/'; *d++ = '.'; *d++ = '.';
877 s += 3;
878 }
879#endif
880 for (;;) {
881 if (*s == '/') {
882 *d++ = *s++;
883 while (*s == '/') s++;
884 }
885 if (!*s) {
886 while (d > d0+1 && d[-1] == '/') d--;
887 *d = '\0';
888 return ct;
889 }
890 if (s[0] == '.' && s[1] == '.' && (s[2] == '\0' || s[2] == '/')) {
891 if (d > d0+1) {
892 for (d--; d > d0+1 && d[-1] != '/'; d--);
893 } else ct++;
894 s += 2; if (*s) s++;
895 } else if (s[0] == '.' && (s[1] == '/' || s[1] == '\0')) {
896 s++; if (*s) s++;
897 } else {
898 while (*s != '/' && *s != '\0') *d++ = *s++;
899 }
900 }
901}
902
903void cd_new_pwd(func,s) /**/
904int func; char *s;
905{
906Param pm;
907List l;
908
909 oldpwd = pwd;
910 if (isset(CHASELINKS))
911 pwd = findpwd(s);
912 else
913 pwd = ztrdup(s);
914 if ((pm = gethnode("PWD", paramtab)) &&
915 (pm->flags & PMFLAG_x) && pm->env)
916 pm->env = replenv(pm->env,pwd);
917 if ((pm = gethnode("OLDPWD", paramtab)) &&
918 (pm->flags & PMFLAG_x) && pm->env)
919 pm->env = replenv(pm->env,oldpwd);
920 if (func == BIN_PUSHD) {
921 permalloc();
922 if (isset(PUSHDIGNOREDUPS)) {
923 Lknode n;
924 for (n = firstnode(dirstack); n; incnode(n))
925 if (!strcmp(oldpwd,getdata(n))) {
926 free(remnode(dirstack,n)); break;
927 }
928 }
929 pushnode(dirstack,oldpwd);
930 heapalloc();
931 }
932 if (unset(PUSHDSILENT) && func != BIN_CD && isset(INTERACTIVE))
933 pdstack();
934 if (l = getshfunc("chpwd")) {
935 fflush(stdout); fflush(stderr);
936 doshfuncnoval(dupstruct(l),NULL,0);
937 }
938 if (dirstacksize != -1 && countnodes(dirstack) >= dirstacksize) {
939 if (dirstacksize < 2)
940 dirstacksize = 2;
941 else
942 free(remnode(dirstack,lastnode(dirstack)));
943 }
944}
945
946void convertwd(s,t,off) /**/
947char *s; char *t; int off;
948{
949char *u,*start;
950
951 *t++ = '/';
952 start = t;
953 while (off--) *t++ = *s++;
954 for (;;) {
955 while (*s == '/') s++;
956 for (u = s; *u && *u != '/'; u++);
957 if (!strncmp(s,".",u-s)) {
958 ;
959 } else if (!strncmp(s,"..",u-s)) {
960 while (t != start && *--t != '/');
961 } else {
962 if (t != start) *t++ = '/';
963 struncpy(&t,s,u-s);
964 }
965 if (!*u) break;
966 s = u;
967 }
968 *t = '\0';
969}
970
971int bin_rehash(name,argv,ops,func) /**/
972char *name;char **argv;char *ops;int func;
973{
974 newcmdnamtab();
975 if (ops['f']) fullhash();
976 return 0;
977}
978
979int bin_hash(name,argv,ops,func) /**/
980char *name;char **argv;char *ops;int func;
981{
982struct cmdnam *chn;
983
984 chn = (Cmdnam) zcalloc(sizeof *chn);
985 chn->type = EXCMD;
986 chn->pcomp = NULL; /* this is probably a bug ! */
987 chn->u.nam = ztrdup(argv[1]);
988 addhnode(ztrdup(argv[0]),chn,cmdnamtab,freecmdnam);
989 return 0;
990}
991
992/* != 0 if s is a prefix of t */
993
994int prefix(s,t) /**/
995char *s;char *t;
996{
997 while (*s && *t && *s == *t) s++,t++;
998 return (!*s);
999}
1000
1001/* convert %%, %1, %foo, %?bar? to a job number */
1002
1003int getjob(s,prog) /**/
1004char *s;char *prog;
1005{
1006int t0,retval;
1007
1008 if (*s != '%')
1009 goto jump;
1010 s++;
1011 if (*s == '%' || *s == '+' || !*s)
1012 {
1013 if (curjob == -1)
1014 {
1015 zerrnam(prog,"no current job",NULL,0);
1016 retval = -1; goto done;
1017 }
1018 retval = curjob; goto done;
1019 }
1020 if (*s == '-')
1021 {
1022 if (prevjob == -1)
1023 {
1024 zerrnam(prog,"no previous job",NULL,0);
1025 retval = -1; goto done;
1026 }
1027 retval = prevjob; goto done;
1028 }
1029 if (idigit(*s))
1030 {
1031 t0 = atoi(s);
1032 if (t0 && t0 < MAXJOB && jobtab[t0].stat && t0 != thisjob)
1033 { retval = t0; goto done; }
1034 zerrnam(prog,"no such job",NULL,0);
1035 retval = -1; goto done;
1036 }
1037 if (*s == '?')
1038 {
1039 struct process *pn;
1040
1041 for (t0 = MAXJOB-1; t0 >= 0; t0--)
1042 if (jobtab[t0].stat && t0 != thisjob)
1043 for (pn = jobtab[t0].procs; pn; pn = pn->next)
1044 if (ztrstr(pn->text,s+1))
1045 { retval = t0; goto done; }
1046 zerrnam(prog,"job not found: %s",s,0);
1047 retval = -1; goto done;
1048 }
1049jump:
1050 if ((t0 = findjobnam(s)) != -1)
1051 { retval = t0; goto done; }
1052 zerrnam(prog,"job not found: %s",s,0);
1053 retval = -1;
1054done:
1055 return retval;
1056}
1057
1058/* find a job named s */
1059
1060int findjobnam(s) /**/
1061char *s;
1062{
1063int t0;
1064
1065 for (t0 = MAXJOB-1; t0 >= 0; t0--)
1066 if (jobtab[t0].stat && jobtab[t0].procs && t0 != thisjob &&
1067 jobtab[t0].procs->text && prefix(s,jobtab[t0].procs->text))
1068 return t0;
1069 return -1;
1070}
1071
1072int isanum(s) /**/
1073char *s;
1074{
1075 while (*s == '-' || idigit(*s)) s++;
1076 return *s == '\0';
1077}
1078
1079int bin_kill(nam,argv,ops,func) /**/
1080char *nam;char **argv;char *ops;int func;
1081{
1082int sig = SIGTERM;
1083int retval = 0;
1084
1085 if (*argv && **argv == '-') {
1086 if (idigit((*argv)[1]))
1087 sig = atoi(*argv+1);
1088 else {
1089 if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
1090 printf("%s",sigs[1]);
1091 for (sig = 2; sig != SIGCOUNT; sig++)
1092 printf(" %s",sigs[sig]);
1093 putchar('\n');
1094 return 0;
1095 }
1096 for (sig = 0; sig != SIGCOUNT; sig++)
1097 if (!strcmp(sigs[sig],*argv+1)) break;
1098 if (sig == SIGCOUNT) {
1099 zerrnam(nam,"unknown signal: SIG%s",*argv+1,0);
1100 zerrnam(nam,"type kill -l for a List of signals",NULL,0);
1101 return 1;
1102 }
1103 }
1104 argv++;
1105 }
1106 for (; *argv; argv++) {
1107 if (**argv == '%') {
1108 int p = getjob(*argv,"kill");
1109
1110 if (p == -1) {
1111 retval = 1;
1112 continue;
1113 }
1114 if (killjb(jobtab+p,sig) == -1) {
1115 zerrnam("kill","kill failed: %e",NULL,errno);
1116 retval = 1;
1117 continue;
1118 }
1119 if (jobtab[p].stat & STAT_STOPPED) {
1120 if (sig == SIGCONT)
1121 jobtab[p].stat &= ~STAT_STOPPED;
1122 if (sig != SIGKILL && sig != SIGCONT && sig != SIGTSTP
1123 && sig != SIGTTOU && sig != SIGTTIN && sig != SIGSTOP)
1124 killjb(jobtab+p,SIGCONT);
1125 }
1126 } else if (!isanum(*argv)) {
1127 zerrnam("kill","illegal pid: %s",*argv,0);
1128 } else if (kill(atoi(*argv),sig) == -1) {
1129 zerrnam("kill","kill failed: %e",NULL,errno);
1130 retval = 1;
1131 }
1132 }
1133 return 0;
1134}
1135
1136static char *recs[] = {
1137 "cputime","filesize","datasize","stacksize","coredumpsize",
1138 "resident","descriptors"
1139 };
1140
1141int bin_limit(nam,argv,ops,func) /**/
1142char *nam;char **argv;char *ops;int func;
1143{
1144#ifndef RLIM_INFINITY
1145 zerrnam(nam,"not available on this system",NULL,0);
1146 return 1;
1147#else
1148char *s;
1149int hard = ops['h'],t0,lim;
1150long val;
1151
1152 if (ops['s'])
1153 {
1154 if (*argv)
1155 zerrnam(nam,"arguments after -s ignored",NULL,0);
1156 for (t0 = 0; t0 != RLIM_NLIMITS; t0++)
1157 if (setrlimit(t0,limits+t0) < 0)
1158 zerrnam(nam,"setrlimit failed: %e",NULL,errno);
1159 return 0;
1160 }
1161 if (!*argv)
1162 {
1163 showlimits(hard,-1);
1164 return 0;
1165 }
1166 while (s = *argv++)
1167 {
1168 for (lim = -1, t0 = 0; t0 != RLIM_NLIMITS; t0++)
1169 if (!strncmp(recs[t0],s,strlen(s)))
1170 {
1171 if (lim != -1)
1172 lim = -2;
1173 else
1174 lim = t0;
1175 }
1176 if (lim < 0)
1177 {
1178 zerrnam("limit",
1179 (lim == -2) ? "ambiguous resource specification: %s"
1180 : "no such resource: %s",s,0);
1181 return 1;
1182 }
1183 if (!(s = *argv++))
1184 {
1185 showlimits(hard,lim);
1186 return 0;
1187 }
1188 if (!lim)
1189 {
1190 val = zstrtol(s,&s,10);
1191 if (*s)
1192 if ((*s == 'h' || *s == 'H') && !s[1])
1193 val *= 3600L;
1194 else if ((*s == 'm' || *s == 'M') && !s[1])
1195 val *= 60L;
1196 else if (*s == ':')
1197 val = val*60+zstrtol(s+1,&s,10);
1198 else
1199 {
1200 zerrnam("limit","unknown scaling factor: %s",s,0);
1201 return 1;
1202 }
1203 }
1204#ifdef RLIMIT_NOFILE
1205 else if (lim == RLIMIT_NOFILE)
1206 val = zstrtol(s,&s,10);
1207#endif
1208 else
1209 {
1210 val = zstrtol(s,&s,10);
1211 if (!*s || ((*s == 'k' || *s == 'K') && !s[1]))
1212 val *= 1024L;
1213 else if ((*s == 'M' || *s == 'm') && !s[1])
1214 val *= 1024L*1024;
1215 else
1216 {
1217 zerrnam("limit","unknown scaling factor: %s",s,0);
1218 return 1;
1219 }
1220 }
1221 if (hard)
1222 if (val > limits[lim].rlim_max && geteuid())
1223 {
1224 zerrnam("limit","can't raise hard limits",NULL,0);
1225 return 1;
1226 }
1227 else
1228 {
1229 limits[lim].rlim_max = val;
1230 if (limits[lim].rlim_max < limits[lim].rlim_cur)
1231 limits[lim].rlim_cur = limits[lim].rlim_max;
1232 }
1233 else
1234 if (val > limits[lim].rlim_max)
1235 {
1236 zerrnam("limit","limit exceeds hard limit",NULL,0);
1237 return 1;
1238 }
1239 else
1240 limits[lim].rlim_cur = val;
1241 }
1242 return 0;
1243#endif
1244}
1245
1246int bin_unlimit(nam,argv,ops,func) /**/
1247char *nam;char **argv;char *ops;int func;
1248{
1249#ifndef RLIM_INFINITY
1250 zerrnam(nam,"not available on this system",NULL,0);
1251 return 1;
1252#else
1253int hard = ops['h'],t0,lim;
1254
1255 if (hard && geteuid())
1256 {
1257 zerrnam(nam,"can't remove hard limits",NULL,0);
1258 return 1;
1259 }
1260 if (!*argv)
1261 {
1262 for (t0 = 0; t0 != RLIM_NLIMITS; t0++)
1263 {
1264 if (hard)
1265 limits[t0].rlim_max = RLIM_INFINITY;
1266 else
1267 limits[t0].rlim_cur = limits[t0].rlim_max;
1268 }
1269 return 0;
1270 }
1271 for (; *argv; argv++)
1272 {
1273 for (lim = -1, t0 = 0; t0 != RLIM_NLIMITS; t0++)
1274 if (!strncmp(recs[t0],*argv,strlen(*argv)))
1275 {
1276 if (lim != -1)
1277 lim = -2;
1278 else
1279 lim = t0;
1280 }
1281 if (lim < 0)
1282 {
1283 zerrnam(nam,
1284 (lim == -2) ? "ambiguous resource specification: %s"
1285 : "no such resource: %s",*argv,0);
1286 return 1;
1287 }
1288 if (hard)
1289 limits[lim].rlim_max = RLIM_INFINITY;
1290 else
1291 limits[lim].rlim_cur = limits[lim].rlim_max;
1292 }
1293 return 0;
1294#endif
1295}
1296
1297void showlimits(hard,lim) /**/
1298int hard;int lim;
1299{
1300int t0;
1301long val;
1302
1303#ifdef RLIM_INFINITY
1304 for (t0 = 0; t0 != RLIM_NLIMITS; t0++)
1305 if (t0 == lim || lim == -1)
1306 {
1307 printf("%-16s",recs[t0]);
1308 val = (hard) ? limits[t0].rlim_max : limits[t0].rlim_cur;
1309 if (val == RLIM_INFINITY)
1310 printf("unlimited\n");
1311 else if (!t0)
1312 printf("%d:%02d:%02d\n",(int) (val/3600),
1313 (int) (val/60) % 60,(int) (val % 60));
1314#ifdef RLIMIT_NOFILE
1315 else if (t0 == RLIMIT_NOFILE)
1316 printf("%d\n",(int) val);
1317#endif
1318 else if (val >= 1024L*1024L)
1319 printf("%ldMb\n",val/(1024L*1024L));
1320 else
1321 printf("%ldKb\n",val/1024L);
1322 }
1323#endif
1324}
1325
1326int bin_sched(nam,argv,ops,func) /**/
1327char *nam;char **argv;char *ops;int func;
1328{
1329char *s = *argv++;
1330time_t t;
1331long h,m;
1332struct tm *tm;
1333struct schedcmd *sch,*sch2,*schl;
1334int t0;
1335
1336 if (s && *s == '-')
1337 {
1338 t0 = atoi(s+1);
1339
1340 if (!t0)
1341 {
1342 zerrnam("sched","usage for delete: sched -<item#>.",NULL,0);
1343 return 1;
1344 }
1345 for (schl = (struct schedcmd *) &schedcmds, sch = schedcmds, t0--;
1346 sch && t0; sch = (schl = sch)->next, t0--);
1347 if (!sch)
1348 {
1349 zerrnam("sched","not that many entries",NULL,0);
1350 return 1;
1351 }
1352 schl->next = sch->next;
1353 free(sch->cmd);
1354 free(sch);
1355 return 0;
1356 }
1357 if (!s)
1358 {
1359 char tbuf[40];
1360
1361 for (t0 = 1, sch = schedcmds; sch; sch = sch->next,t0++)
1362 {
1363 t = sch->time;
1364 tm = localtime(&t);
1365 ztrftime(tbuf,20,"%a %b %e %k:%M:%S",tm);
1366 printf("%3d %s %s\n",t0,tbuf,sch->cmd);
1367 }
1368 return 0;
1369 }
1370 else if (!*argv)
1371 {
1372 zerrnam("sched","not enough arguments",NULL,0);
1373 return 1;
1374 }
1375 if (*s == '+')
1376 {
1377 h = zstrtol(s+1,&s,10);
1378 if (*s != ':')
1379 {
1380 zerrnam("sched","bad time specifier",NULL,0);
1381 return 1;
1382 }
1383 m = zstrtol(s+1,&s,10);
1384 if (*s)
1385 {
1386 zerrnam("sched","bad time specifier",NULL,0);
1387 return 1;
1388 }
1389 t = time(NULL)+h*3600+m*60;
1390 }
1391 else
1392 {
1393 h = zstrtol(s,&s,10);
1394 if (*s != ':')
1395 {
1396 zerrnam("sched","bad time specifier",NULL,0);
1397 return 1;
1398 }
1399 m = zstrtol(s+1,&s,10);
1400 if (*s && *s != 'a' && *s != 'p')
1401 {
1402 zerrnam("sched","bad time specifier",NULL,0);
1403 return 1;
1404 }
1405 t = time(NULL);
1406 tm = localtime(&t);
1407 t -= tm->tm_sec+tm->tm_min*60+tm->tm_hour*3600;
1408 if (*s == 'p')
1409 h += 12;
1410 t += h*3600+m*60;
1411 if (t < time(NULL))
1412 t += 3600*24;
1413 }
1414 sch = zcalloc(sizeof *sch);
1415 sch->time = t;
1416 sch->cmd = ztrdup(spacejoin(argv));
1417 sch->next = NULL;
1418 for (sch2 = (struct schedcmd *) &schedcmds; sch2->next; sch2 = sch2->next);
1419 sch2->next = sch;
1420 return 0;
1421}
1422
1423int bin_eval(nam,argv,ops,func) /**/
1424char *nam;char **argv;char *ops;int func;
1425{
1426char *s = ztrdup(spacejoin(argv));
1427List list;
1428
1429 hungets(s);
1430 free(s);
1431 strinbeg();
1432 if (!(list = parse_list()))
1433 {
1434 hflush();
1435 strinend();
1436 return 1;
1437 }
1438 strinend();
1439 runlist(list);
1440 return lastval;
1441}
1442
1443/* get the history event associated with s */
1444
1445int fcgetcomm(s) /**/
1446char *s;
1447{
1448int cmd;
1449
1450 if (cmd = atoi(s))
1451 {
1452 if (cmd < 0)
1453 cmd = curhist+cmd+1;
1454 return cmd;
1455 }
1456 cmd = hcomsearch(s);
1457 if (cmd == -1)
1458 zerrnam("fc","event not found: %s",s,0);
1459 return cmd;
1460}
1461
1462/* perform old=new substituion */
1463
1464int fcsubs(sp,sub) /**/
1465char **sp;struct asgment *sub;
1466{
1467char *s1,*s2,*s3,*s4,*s = *sp,*s5;
1468int subbed = 0;
1469
1470 while (sub)
1471 {
1472 s1 = sub->name;
1473 s2 = sub->value;
1474 sub = sub->next;
1475 s5 = s;
1476 while (s3 = (char *) ztrstr(s5,s1))
1477 {
1478 s4 = alloc(1+(s3-s)+strlen(s2)+strlen(s3+strlen(s1)));
1479 ztrncpy(s4,s,s3-s);
1480 strcat(s4,s2);
1481 s5 = s4+strlen(s4);
1482 strcat(s4,s3+strlen(s1));
1483 s = s4;
1484 subbed = 1;
1485 }
1486 }
1487 *sp = s;
1488 return subbed;
1489}
1490
1491/* print a series of history events to a file */
1492
1493int fclist(f,n,r,D,d,first,last,subs) /**/
1494FILE *f;int n;int r;int D;int d;int first;int last;struct asgment *subs;
1495{
1496int done = 0;
1497char *s,*hs;
1498Histent ent;
1499
1500 if (!subs) done = 1;
1501 for (;;) {
1502 hs = quietgetevent(first);
1503 if (!hs) {
1504 zerrnam("fc","no such event: %d",NULL,first);
1505 return 1;
1506 }
1507 s = makehstr(hs);
1508 done |= fcsubs(&s,subs);
1509 if (n) fprintf(f,"%5d ",first);
1510 ent = NULL;
1511 if (d) {
1512 struct tm *ltm;
1513
1514 if (!ent) ent = gethistent(first);
1515 ltm = localtime(&ent->stim);
1516 fprintf(f,"%2d:%02d ",ltm->tm_hour,ltm->tm_min);
1517 }
1518 if (D) {
1519 long diff;
1520
1521 if (!ent) ent = gethistent(first);
1522 diff = (ent->ftim) ? ent->ftim-ent->stim : 0;
1523 fprintf(f,"%d:%02d ",diff/60,diff%60);
1524 }
1525 if (f == stdout) {
1526 niceprintf(s,f);
1527 putc('\n',f);
1528 } else fprintf(f,"%s\n",s);
1529 if (first == last) break;
1530 (r) ? first-- : first++;
1531 }
1532 if (f != stdout) fclose(f);
1533 if (!done) {
1534 zerrnam("fc","no substitutions performed",NULL,0);
1535 return 1;
1536 }
1537 return 0;
1538}
1539
1540int fcedit(ename,fn) /**/
1541char *ename;char *fn;
1542{
1543 if (!strcmp(ename,"-"))
1544 return 1;
1545 return !zyztem(ename,fn);
1546}
1547
1548/* fc, history, r */
1549
1550int bin_fc(nam,argv,ops,func) /**/
1551char *nam;char **argv;char *ops;int func;
1552{
1553int first = -1,last = -1,retval,minflag = 0;
1554char *s;
1555struct asgment *asgf = NULL,*asgl = NULL;
1556
1557 if (!interact) {
1558 zerrnam(nam,"not interactive shell",NULL,0);
1559 return 1;
1560 }
1561 if (!(ops['l'] && unset(HISTNOSTORE))) remhist();
1562 if (ops['R']) {
1563 readhistfile(*argv ? *argv : getsparam("HISTFILE"),1);
1564 return 0;
1565 }
1566 if (ops['W']) {
1567 savehistfile(*argv ? *argv : getsparam("HISTFILE"),1,0);
1568 return 0;
1569 }
1570 if (ops['A']) {
1571 savehistfile(*argv ? *argv : getsparam("HISTFILE"),1,1);
1572 return 0;
1573 }
1574 while (*argv && equalsplit(*argv,&s)) {
1575 struct asgment *a = (struct asgment *) alloc(sizeof *a);
1576
1577 if (!asgf) asgf = asgl = a;
1578 else {
1579 asgl->next = a;
1580 asgl = a;
1581 }
1582 a->name = *argv;
1583 a->value = s;
1584 argv++;
1585 }
1586 if (*argv) {
1587 minflag = **argv == '-';
1588 first = fcgetcomm(*argv);
1589 if (first == -1) return 1;
1590 argv++;
1591 }
1592 if (*argv) {
1593 last = fcgetcomm(*argv);
1594 if (last == -1) return 1;
1595 argv++;
1596 }
1597 if (*argv) {
1598 zerrnam("fc","too many arguments",NULL,0);
1599 return 1;
1600 }
1601 if (first == -1) first = (ops['l']) ? curhist-16 : curhist;
1602 if (last == -1) last = (ops['l']) ? curhist : first;
1603 if (first < firsthist()) first = firsthist();
1604 if (last == -1) last = (minflag) ? curhist : first;
1605 if (ops['l'])
1606 retval = fclist(stdout,!ops['n'],ops['r'],ops['D'],ops['d'],
1607 first,last,asgf);
1608 else {
1609 FILE *out;
1610 char *fil = gettemp();
1611
1612 out = fopen(fil,"w");
1613 if (!out)
1614 zerrnam("fc","can't open temp file: %e",NULL,errno);
1615 else {
1616 retval = 1;
1617 if (!fclist(out,0,ops['r'],0,0,first,last,asgf))
1618 if (fcedit(auxdata ? auxdata : fceditparam,fil))
1619 if (stuff(fil))
1620 zerrnam("fc","%e: %s",s,errno);
1621 else
1622 retval = 0;
1623 }
1624 unlink(fil);
1625 }
1626 return retval;
1627}
1628
1629int bin_suspend(name,argv,ops,func) /**/
1630char *name;char **argv;char *ops;int func;
1631{
1632 if (islogin && !ops['f']) {
1633 zerrnam(name,"can't suspend login shell",NULL,0);
1634 return 1;
1635 }
1636 if (jobbing) {
1637 signal(SIGPIPE,SIG_DFL);
1638 signal(SIGTTIN,SIG_DFL);
1639 signal(SIGTSTP,SIG_DFL);
1640 signal(SIGTTOU,SIG_DFL);
1641 }
1642 kill(0,SIGTSTP);
1643 if (jobbing) {
1644 while (gettygrp() != mypgrp) {
1645 sleep(1);
1646 if (gettygrp() != mypgrp) kill(0,SIGTTIN);
1647 }
1648 signal(SIGTTOU,SIG_IGN);
1649 signal(SIGTSTP,SIG_IGN);
1650 signal(SIGTTIN,SIG_IGN);
1651 signal(SIGPIPE,SIG_IGN);
1652 }
1653 return 0;
1654}
1655
1656int bin_alias(name,argv,ops,func) /**/
1657char *name;char **argv;char *ops;int func;
1658{
1659struct alias *an;
1660struct asgment *asg;
1661int incm = !(ops['a'] || ops['g']),ret = 0;
1662
1663 showflag = !incm;
1664 if (!*argv)
1665 listhtable(aliastab,(HFunc) printalias);
1666 else while (asg = getasg(*argv++))
1667 {
1668 if (asg->value)
1669 addhnode(ztrdup(asg->name),mkanode(ztrdup(asg->value),incm),
1670 aliastab,freeanode);
1671 else if (an = (Alias) gethnode(asg->name,aliastab))
1672 printalias(asg->name,an);
1673 else
1674 ret = 1;
1675 }
1676 return ret;
1677}
1678
1679/* print an alias; used with listhtable */
1680
1681void printalias(s,a) /**/
1682char *s;struct alias *a;
1683{
1684 if (a->cmd >= 0 && !(showflag && a->cmd))
1685 printf("%s=%s\n",s,a->text);
1686}
1687
1688/* print a param; used with listhtable */
1689
1690void printparam(s,p) /**/
1691char *s;Param p;
1692{
1693 if (showflag > 0 && !(p->flags & showflag))
1694 return;
1695 if (!showflag)
1696 {
1697 int fgs = p->flags;
1698
1699 if (fgs & PMFLAG_i) printf("integer ");
1700 if (fgs & PMFLAG_A) printf("array ");
1701 if (fgs & PMFLAG_L) printf("left justified %d ",p->ct);
1702 if (fgs & PMFLAG_R) printf("right justified %d ",p->ct);
1703 if (fgs & PMFLAG_Z) printf("zero filled %d ",p->ct);
1704 if (fgs & PMFLAG_l) printf("lowercase ");
1705 if (fgs & PMFLAG_u) printf("uppercase ");
1706 if (fgs & PMFLAG_r) printf("readonly ");
1707 if (fgs & PMFLAG_t) printf("tagged ");
1708 if (fgs & PMFLAG_x) printf("exported ");
1709 }
1710 if (showflag2)
1711 printf("%s\n",s);
1712 else
1713 {
1714 char *t,**u;
1715
1716 printf("%s=",s);
1717 switch (p->flags & PMTYPE)
1718 {
1719 case PMFLAG_s:
1720 if (p->gets.cfn && (t = p->gets.cfn(p)))
1721 puts(t);
1722 else
1723 putchar('\n');
1724 break;
1725 case PMFLAG_i: printf("%ld\n",p->gets.ifn(p)); break;
1726 case PMFLAG_A:
1727 putchar('(');
1728 u = p->gets.afn(p);
1729 if (!*u)
1730 printf(")\n");
1731 else
1732 {
1733 while (u[1])
1734 printf("%s ",*u++);
1735 printf("%s)\n",*u);
1736 }
1737 break;
1738 }
1739 }
1740}
1741
1742/* autoload, declare, export, functions, integer, local, readonly, typeset */
1743
1744int bin_typeset(name,argv,ops,func) /**/
1745char *name;char **argv;char *ops;int func;
1746{
1747int on = 0,off = 0,roff,bit = 1,retcode = 0;
1748char *optstr = "LRZilurtx";
1749struct param *pm;
1750struct asgment *asg;
1751
1752 for (; *optstr; optstr++,bit <<= 1)
1753 if (ops[*optstr] == 1)
1754 on |= bit;
1755 else if (ops[*optstr] == 2)
1756 off |= bit;
1757 roff = off;
1758 if (ops['f']) {
1759 on &= PMFLAG_t|PMFLAG_u;
1760 off &= PMFLAG_t|PMFLAG_u;
1761 showflag = (ops['f'] == 1);
1762 if (ops['@'] && ((off & ~PMFLAG_t) || (on & ~(PMFLAG_u|PMFLAG_t)))) {
1763 zerrnam(name,"invalid option(s)",NULL,0);
1764 return 1;
1765 }
1766 showflag2 = 0;
1767 if (!*argv) {
1768 showflag2 = off|on;
1769 listhtable(cmdnamtab,(HFunc) pshfunc);
1770 } else for (; *argv; argv++) {
1771 Cmdnam cc;
1772
1773 if ((cc = (Cmdnam) gethnode(*argv,cmdnamtab)) && cc->type == SHFUNC)
1774 if (on|off) cc->flags = (cc->flags | on) & (~off);
1775 else pshfunc(*argv,cc);
1776 else if (on & PMFLAG_u) {
1777 cc = (Cmdnam) zcalloc(sizeof *cc);
1778 cc->type = SHFUNC;
1779 cc->flags = on;
1780 addhnode(ztrdup(*argv),cc,cmdnamtab,freecmdnam);
1781 } else
1782 retcode = 1;
1783 }
1784 return retcode;
1785 }
1786 if (on & PMFLAG_L)
1787 off |= PMFLAG_R;
1788 if (on & PMFLAG_R)
1789 off |= PMFLAG_L;
1790 if (on & PMFLAG_u)
1791 off |= PMFLAG_l;
1792 if (on & PMFLAG_l)
1793 off |= PMFLAG_u;
1794 on &= ~off;
1795 showflag = showflag2 = 0;
1796 if (!*argv) {
1797 showflag = on|off;
1798 showflag2 = roff;
1799 listhtable(paramtab,(HFunc) printparam);
1800 } else while (asg = getasg(*argv++)) {
1801 if (asg->value && *asg->value == '~') {
1802 *asg->value = Tilde;
1803 singsub(&asg->value);
1804 }
1805 pm = (Param) gethnode(asg->name,paramtab);
1806 if (pm) {
1807 if (!on && !roff && !asg->value) {
1808 printparam(asg->name,pm);
1809 continue;
1810 }
1811 pm->flags = (pm->flags | on) & ~off;
1812 if ((on & (PMFLAG_L | PMFLAG_R | PMFLAG_Z | PMFLAG_i))
1813 && (pmtype(pm) != PMFLAG_A))
1814 pm->ct = auxlen;
1815 if (pmtype(pm) != PMFLAG_A) {
1816 if (pm->flags & PMFLAG_x) {
1817 if (!pm->env)
1818 pm->env = addenv(asg->name,
1819 (asg->value) ? asg->value : getsparam(asg->name));
1820 } else if (pm->env) {
1821 delenv(pm->env);
1822 free(pm->env);
1823 pm->env = NULL;
1824 }
1825 if (asg->value)
1826 setsparam(asg->name,ztrdup(asg->value));
1827 }
1828 } else {
1829 if (locallist && !(on & PMFLAG_x)) {
1830 permalloc();
1831 addnode(locallist,ztrdup(asg->name));
1832 heapalloc();
1833 }
1834 createparam(ztrdup(asg->name),
1835 ztrdup((asg->value) ? asg->value : ""),on);
1836 pm = (Param) gethnode(asg->name,paramtab);
1837 pm->ct = auxlen;
1838 }
1839 }
1840 return 0;
1841}
1842
1843/* convert s with escape sequences */
1844
1845char *escsubst(s,nnl) /**/
1846char *s; int *nnl;
1847{
1848char *t = alloc(strlen(s)+1),*ret = t;
1849
1850 for (; *s; s++)
1851 if (*s == '\\' && s[1])
1852 switch (*++s) {
1853 case 'b': *t++ = '\b'; break;
1854 case 'c': *nnl |= 1; break;
1855 case 'e': *t++ = '\033'; break;
1856 case 'f': *t++ = '\f'; break;
1857 case 'n': *t++ = '\n'; break;
1858 case 'r': *t++ = '\r'; break;
1859 case 't': *t++ = '\t'; break;
1860 case 'v': *t++ = '\v'; break;
1861 case '\\': *t++ = '\\'; break;
1862 case '0': *t++ = zstrtol(s,&s,8); s--; break;
1863 default: *t++ = '\\'; *t++ = *s; break;
1864 }
1865 else *t++ = *s;
1866 *t = '\0';
1867 return ret;
1868}
1869
1870/* echo, print, pushln */
1871
1872int bin_print(name,args,ops,func) /**/
1873char *name;char **args;char *ops;int func;
1874{
1875int nnl = 0, fd;
1876Histent ent;
1877FILE *fout = stdout;
1878
1879 if (ops['z']) {
1880 permalloc();
1881 pushnode(bufstack,ztrdup(spacejoin(args)));
1882 heapalloc();
1883 return 0;
1884 }
1885 if (ops['s']) {
1886 permalloc();
1887 ent = gethistent(++curhist);
1888 ent->lex = ztrdup(join(args,HISTSPACE));
1889 ent->lit = ztrdup(join(args,' '));
1890 ent->stim = ent->ftim = time(NULL);
1891 heapalloc();
1892 return 0;
1893 }
1894 if (ops['R'])
1895 ops['r'] = 1;
1896 if (ops['u'] || ops['p']) {
1897 if (ops['u']) {
1898 for (fd = 0; fd < 10; fd++) if (ops[fd+'0']) break;
1899 if (fd == 10) fd = 0;
1900 } else fd = coprocout;
1901 if ((fd = dup(fd)) < 0) {
1902 zerrnam(name,"bad file number",NULL,0);
1903 return 1;
1904 }
1905 if ((fout = fdopen(fd,"w")) == 0) {
1906 zerrnam(name,"bad mode on fd",NULL,0);
1907 return 1;
1908 }
1909 }
1910 for (; *args; args++) {
1911 if (!ops['r']) *args = escsubst(*args,&nnl);
1912 if (ops['D']) fprintdir(*args,fout);
1913 else if (ops['P']) {
1914 int junk;
1915 fputs(putprompt(*args,&junk),fout);
1916 } else fputs(*args,fout);
1917 if (args[1]) fputc(ops['l'] ? '\n' : ops['0'] ? '\0' : ' ',fout);
1918 }
1919 if (!(ops['n'] || nnl)) fputc(ops['N'] ? '\0' : '\n',fout);
1920 if (fout != stdout) fclose(fout);
1921 return 0;
1922}
1923
1924int bin_dirs(name,argv,ops,func) /**/
1925char *name;char **argv;char *ops;int func;
1926{
1927Lklist l;
1928
1929 if (ops['v'])
1930 {
1931 Lknode node;
1932 int t0 = 1;
1933
1934 printf("0\t");
1935 printdir(pwd);
1936 for (node = firstnode(dirstack); node; incnode(node))
1937 {
1938 printf("\n%d\t",t0++);
1939 printdir(getdata(node));
1940 }
1941 putchar('\n');
1942 return 0;
1943 }
1944 if (!*argv)
1945 {
1946 pdstack();
1947 return 0;
1948 }
1949 permalloc();
1950 l = newlist();
1951 if (!*argv)
1952 {
1953 heapalloc();
1954 return 0;
1955 }
1956 while (*argv)
1957 addnode(l,ztrdup(*argv++));
1958 freetable(dirstack,freestr);
1959 dirstack = l;
1960 heapalloc();
1961 return 0;
1962}
1963
1964int bin_unalias(name,argv,ops,func) /**/
1965char *name;char **argv;char *ops;int func;
1966{
1967int ret = 0;
1968vptr dat;
1969
1970 while (*argv)
1971 {
1972 if (dat = remhnode(*argv++,aliastab))
1973 freeanode(dat);
1974 else
1975 ret = 1;
1976 }
1977 return ret;
1978}
1979
1980int bin_disable(name,argv,ops,func) /**/
1981char *name;char **argv;char *ops;int func;
1982{
1983Cmdnam chn;
1984
1985 while (*argv) {
1986 if (!strncmp(*argv,"TRAP",4))
1987 unsettrap(getsignum(*argv+4));
1988 chn = zalloc(sizeof *chn);
1989 chn->type = DISABLED;
1990 addhnode(ztrdup(*argv++),chn,cmdnamtab,freecmdnam);
1991 }
1992 return 0;
1993}
1994
1995int bin_unhash(name,argv,ops,func) /**/
1996char *name;char **argv;char *ops;int func;
1997{
1998vptr dat;
1999
2000 while (*argv) {
2001 if (!strncmp(*argv,"TRAP",4)) unsettrap(getsignum(*argv+4));
2002 if (dat = remhnode(*argv++,cmdnamtab)) freecmdnam(dat);
2003 }
2004 return 0;
2005}
2006
2007int bin_unset(name,argv,ops,func) /**/
2008char *name;char **argv;char *ops;int func;
2009{
2010int retval = 0;
2011char *s;
2012
2013 while (s = *argv++)
2014 if (gethnode(s,paramtab))
2015 unsetparam(s);
2016 else
2017 retval = 1;
2018 return retval;
2019}
2020
2021static char *zbuf;
2022static int readfd;
2023
2024int zread() /**/
2025{
2026char cc;
2027
2028 if (zbuf)
2029 return (*zbuf) ? *zbuf++ : EOF;
2030 if (read(readfd,&cc,1) != 1)
2031 return EOF;
2032 return cc;
2033}
2034
2035int bin_read(name,args,ops,func) /**/
2036char *name;char **args;char *ops;int func;
2037{
2038char *reply,*pmpt;
2039int bsiz,c,gotnl = 0;
2040char *buf,*bptr;
2041
2042 reply = (*args) ? *args++ : "REPLY";
2043 if (ops['u'] && !ops['p']) {
2044 for (readfd = 0; readfd < 10; ++readfd) if (ops[readfd+'0']) break;
2045 if (readfd == 10) readfd = 0;
2046 } else if (ops['p']) readfd = coprocin;
2047 else {
2048 attachtty((jobtab[thisjob].gleader) ? jobtab[thisjob].gleader : mypgrp);
2049 readfd = 0;
2050 if (isatty(0)) {
2051 for (pmpt = reply; *pmpt && *pmpt != '?'; pmpt++);
2052 if (*pmpt++) {
2053 write(2,pmpt,strlen(pmpt));
2054 pmpt[-1] = '\0';
2055 }
2056 }
2057#if 0
2058 else if (isset(SHINSTDIN) && unset(INTERACTIVE)) {
2059 if (isatty(1)) readfd = 1;
2060 else if (isatty(2)) readfd = 2;
2061 }
2062#endif
2063 }
2064 zbuf = (!ops['z']) ? NULL :
2065 (full(bufstack)) ? (char *) getnode(bufstack) : NULL;
2066 while (*args) {
2067 buf = bptr = zalloc(bsiz = 64);
2068redo:
2069 for(;;) {
2070 if (gotnl) break;
2071 c = zread();
2072 if (!ops['r'] && c == '\n' && bptr != buf && bptr[-1] == '\\') {
2073 bptr--;
2074 continue;
2075 }
2076 if (c == EOF || isep(c) || c == '\n') break;
2077 *bptr++ = c;
2078 if (bptr == buf+bsiz) {
2079 buf = realloc(buf,bsiz *= 2);
2080 bptr = buf+(bsiz/2);
2081 }
2082 }
2083 if (c == EOF) {
2084 if (readfd == coprocin) {
2085 close(coprocin);
2086 close(coprocout);
2087 coprocin = coprocout = -1;
2088 }
2089 return 1;
2090 }
2091 if (c == '\n') gotnl = 1;
2092 if (bptr == buf) goto redo;
2093 *bptr = '\0';
2094 setsparam(reply,buf);
2095 reply = *args++;
2096 }
2097 buf = bptr = zalloc(bsiz = 64);
2098 if (!gotnl)
2099 for (;;) {
2100 c = zread();
2101 if (!ops['r'] && c == '\n' && bptr != buf && bptr[-1] == '\\') {
2102 bptr--;
2103 continue;
2104 }
2105 if (c == EOF || (c == '\n' && !zbuf)) break;
2106 *bptr++ = c;
2107 if (bptr == buf+bsiz) {
2108 buf = realloc(buf,bsiz *= 2);
2109 bptr = buf+(bsiz/2);
2110 }
2111 }
2112 *bptr = '\0';
2113 setsparam(reply,buf);
2114 if (c == EOF) {
2115 if (readfd == coprocin) {
2116 close(coprocin);
2117 close(coprocout);
2118 coprocin = coprocout = -1;
2119 }
2120 return 1;
2121 }
2122 return 0;
2123}
2124
2125int bin_vared(name,args,ops,func) /**/
2126char *name;char **args;char *ops;int func;
2127{
2128char *s;char *t;
2129struct param *pm;
2130
2131 if (!(s = getsparam(args[0]))) {
2132 zerrnam(name,"no such variable: %s",args[0],0);
2133 return 1;
2134 }
2135 permalloc();
2136 pushnode(bufstack,ztrdup(s));
2137 heapalloc();
2138 t = (char *) zleread((unsigned char *)"> ",NULL,2);
2139 if (!t || errflag)
2140 return 1;
2141 if (t[strlen(t)-1] == '\n')
2142 t[strlen(t)-1] = '\0';
2143 pm = gethnode(args[0],paramtab);
2144 if (pmtype(pm) == PMFLAG_A)
2145 setaparam(args[0],spacesplit(t));
2146 else
2147 setsparam(args[0],t);
2148 return 0;
2149}
2150
2151#define fset(X) (flags & X)
2152
2153/* execute a builtin handler function after parsing the arguments */
2154
2155int execbin(args,cnode) /**/
2156Lklist args;Cmdnam cnode;
2157{
2158struct bincmd *b;
2159char ops[128],*arg,*pp,*name,**argv,**oargv,*optstr;
2160int t0,flags,sense,argc = 0,op;
2161Lknode n;
2162
2163 auxdata = NULL;
2164 auxlen = 0;
2165 for (t0 = 0; t0 != 128; t0++)
2166 ops[t0] = 0;
2167 name = ugetnode(args);
2168 b = builtins+cnode->u.binnum;
2169
2170/* the 'builtin' builtin is handled specially */
2171
2172 if (b->funcid == BIN_BUILTIN)
2173 {
2174 if (!(name = ugetnode(args)))
2175 {
2176 zerrnam("builtin","command name expected",NULL,0);
2177 return 1;
2178 }
2179 for (t0 = 0, b = builtins; b->name; b++,t0++)
2180 if (!strcmp(name,b->name))
2181 break;
2182 if (!b->name)
2183 {
2184 zerrnam("builtin","no such builtin: %s",name,0);
2185 return 1;
2186 }
2187 }
2188 flags = b->flags;
2189 arg = ugetnode(args);
2190 optstr = b->optstr;
2191 if (flags & BINF_ECHOPTS && arg && strcmp(arg,"-n"))
2192 optstr = NULL;
2193 if (optstr)
2194 while (arg &&
2195 ((sense = *arg == '-') || fset(BINF_PLUSOPTS) && *arg == '+') &&
2196 (fset(BINF_PLUSOPTS) || !atoi(arg)))
2197 {
2198 pp = arg;
2199 if (arg[1] == '-')
2200 arg++;
2201 if (!arg[1])
2202 {
2203 ops['-'] = 1;
2204 if (!sense)
2205 ops['+'] = 1;
2206 }
2207 else
2208 ops['@'] = 1;
2209 op = -1;
2210 while (*++arg)
2211 if (strchr(b->optstr,op = *arg))
2212 ops[*arg] = (sense) ? 1 : 2;
2213 else
2214 break;
2215 if (*arg)
2216 {
2217 zerr("bad option: %c",NULL,*arg);
2218 return 1;
2219 }
2220 arg = ugetnode(args);
2221 if (fset(BINF_SETOPTS) && op == 'o')
2222 {
2223 int c;
2224
2225 if (!arg)
2226 prtopt();
2227 else
2228 {
2229 c = optlookup(arg);
2230 if (c == -1)
2231 {
2232 zerr("bad option: %s",arg,0);
2233 return 1;
2234 }
2235 else
2236 {
2237 ops[c] = ops['o'];
2238 arg = ugetnode(args);
2239 }
2240 }
2241 }
2242 if ((fset(BINF_PRINTOPTS) && ops['R']) || ops['-'])
2243 break;
2244 if (fset(BINF_SETOPTS) && ops['A'])
2245 {
2246 auxdata = arg;
2247 arg = ugetnode(args);
2248 break;
2249 }
2250 if (fset(BINF_FCOPTS) && op == 'e')
2251 {
2252 auxdata = arg;
2253 arg = ugetnode(args);
2254 }
2255 if (fset(BINF_TYPEOPT) && (op == 'L' || op == 'R' ||
2256 op == 'Z' || op == 'i') && arg && idigit(*arg))
2257 {
2258 auxlen = atoi(arg);
2259 arg = ugetnode(args);
2260 }
2261 }
2262 if (fset(BINF_R))
2263 auxdata = "-";
2264 if (pp = b->defopts)
2265 while (*pp)
2266 ops[*pp++] = 1;
2267 if (arg)
2268 {
2269 argc = 1;
2270 n = firstnode(args);
2271 while (n)
2272 argc++,incnode(n);
2273 }
2274 oargv = argv = (char **) ncalloc(sizeof(char **) * (argc+1));
2275 if (*argv++ = arg)
2276 while (*argv++ = ugetnode(args));
2277 argv = oargv;
2278 if (errflag)
2279 return 1;
2280 if (argc < b->minargs || (argc > b->maxargs && b->maxargs != -1)) {
2281 zerrnam(name,(argc < b->minargs)
2282 ? "not enough arguments" : "too many arguments",NULL,0);
2283 return 1;
2284 }
2285 if (isset(XTRACE)) {
2286 char **pp = argv;
2287 fprintf(stderr,"%s%s",(prompt4) ? prompt4 : "",name);
2288 while (*pp) fprintf(stderr," %s",*pp++);
2289 fputc('\n',stderr);
2290 fflush(stderr);
2291 }
2292 return (*(b->handlerfunc))(name,argv,ops,b->funcid);
2293}
2294
2295struct asgment *getasg(s) /**/
2296char *s;
2297{
2298static struct asgment asg;
2299
2300 if (!s)
2301 return NULL;
2302 if (*s == '=')
2303 {
2304 zerr("bad assignment",NULL,0);
2305 return NULL;
2306 }
2307 asg.name = s;
2308 for (; *s && *s != '='; s++);
2309 if (*s)
2310 {
2311 *s = '\0';
2312 asg.value = s+1;
2313 }
2314 else
2315 asg.value = NULL;
2316 return &asg;
2317}
2318
2319/* ., source */
2320
2321int bin_dot(name,argv,ops,func) /**/
2322char *name;char **argv;char *ops;int func;
2323{
2324char **old,*old0;
2325int ret;
2326char buf[MAXPATHLEN];
2327char *s,**t,*enam;
2328
2329 if (!*argv)
2330 return 0;
2331 old = pparams;
2332 old0 = argzero;
2333 if (argv[1]) {
2334 permalloc();
2335 pparams = arrdup(argv+1);
2336 heapalloc();
2337 }
2338 enam = argzero = ztrdup(*argv);
2339 errno = ENOENT;
2340 ret = 1;
2341 for (s = argzero; *s; s++)
2342 if (*s == '/') {
2343 ret = source(argzero);
2344 break;
2345 }
2346 if (!*s) {
2347 for (t = path; *t; t++)
2348 if ((*t)[0] == '.' && !(*t)[1]) {
2349 ret = source(argzero);
2350 break;
2351 } else {
2352 sprintf(buf,"%s/%s",*t,argzero);
2353 if (access(buf,F_OK) == 0) {
2354 ret = source(enam = buf);
2355 break;
2356 }
2357 }
2358 if (!*t && access(argzero,F_OK) == 0)
2359 ret = source(enam = argzero);
2360 }
2361 if (argv[1]) {
2362 freearray(pparams);
2363 pparams = old;
2364 }
2365 if (ret) zerrnam(name,"%e: %s",enam,errno);
2366 free(argzero);
2367 argzero = old0;
2368 return ret;
2369}
2370
2371int bin_set(name,argv,ops,func) /**/
2372char *name;char **argv;char *ops;int func;
2373{
2374struct option *opp;
2375char **x;
2376
2377 if (((ops['+'] && ops['-']) || !ops['-']) && !ops['@'] && !*argv)
2378 {
2379 showflag = ~0;
2380 showflag2 = ops['+'];
2381 listhtable(paramtab,(HFunc) printparam);
2382 }
2383 for (opp = optns; opp->name; opp++)
2384 if (ops[opp->id] == 1)
2385 opts[opp->id] = OPT_SET;
2386 else if (ops[opp->id] == 2)
2387 opts[opp->id] = OPT_UNSET;
2388 if (!*argv && !ops['-'])
2389 return 0;
2390 permalloc();
2391 x = arrdup(argv);
2392 heapalloc();
2393 if (ops['A'])
2394 setaparam(auxdata,x);
2395 else {
2396 freearray(pparams);
2397 pparams = x;
2398 }
2399 return 0;
2400}
2401
2402#define pttime(X) printf("%dm%ds",(X)/3600,(X)/60%60)
2403
2404int bin_times(name,argv,ops,func) /**/
2405char *name;char **argv;char *ops;int func;
2406{
2407struct tms buf;
2408
2409 if (times(&buf) == -1)
2410 return 1;
2411 pttime(buf.tms_utime);
2412 putchar(' ');
2413 pttime(buf.tms_stime);
2414 putchar('\n');
2415 pttime(buf.tms_cutime);
2416 putchar(' ');
2417 pttime(buf.tms_cstime);
2418 putchar('\n');
2419 return 0;
2420}
2421
2422int bin_getopts(name,argv,ops,func) /**/
2423char *name;char **argv;char *ops;int func;
2424{
2425char *optstr = *argv++,*var = *argv++;
2426char **args = (*argv) ? argv : pparams;
2427static int optcind = 1,quiet;
2428char *str,optbuf[3],*opch = optbuf+1;
2429
2430 if (zoptind < 1) zoptind = 1;
2431 optbuf[0] = '+'; optbuf[1] = optbuf[2] = '\0';
2432 if (optarg) free(optarg);
2433 optarg = ztrdup("");
2434 setsparam(var,ztrdup(""));
2435 if (*optstr == ':') {
2436 quiet = 1;
2437 optstr++;
2438 }
2439 if (zoptind > arrlen(args)) return 1;
2440 str = args[zoptind-1];
2441 if (*str != '+' && *str != '-' || optcind >= strlen(str) ||
2442 !strcmp("--",str)) {
2443 if (*str == '+' || *str == '-')
2444 zoptind++;
2445 optcind = 0;
2446 return 1;
2447 }
2448 if (!optcind)
2449 optcind = 1;
2450 *opch = str[optcind++];
2451 if (!args[zoptind-1][optcind]) {
2452 zoptind++;
2453 optcind = 0;
2454 }
2455 for (; *optstr; optstr++)
2456 if (*opch == *optstr)
2457 break;
2458 if (!*optstr) {
2459 setsparam(var,ztrdup("?"));
2460 if (quiet) {
2461 free(optarg); optarg = ztrdup(opch);
2462 return 0;
2463 }
2464 zerr("bad option: %c",NULL,*opch); errflag = 0;
2465 return 0;
2466 }
2467 setsparam(var,ztrdup(opch-(*str == '+')));
2468 if (optstr[1] == ':') {
2469 if (!args[zoptind-1]) {
2470 if (quiet) {
2471 free(optarg); optarg = ztrdup(opch);
2472 setsparam(var,ztrdup(":"));
2473 return 0;
2474 }
2475 setsparam(var,ztrdup("?"));
2476 zerr("argument expected after %c option",NULL,*opch); errflag = 0;
2477 return 0;
2478 }
2479 free(optarg);
2480 optarg = ztrdup(args[zoptind-1]+optcind);
2481 zoptind++;
2482 optcind = 0;
2483 }
2484 return 0;
2485}
2486
2487/* get a signal number from a string */
2488
2489int getsignum(s) /**/
2490char *s;
2491{
2492int x = atoi(s),t0;
2493
2494 if (idigit(*s) && x >= 0 && x < VSIGCOUNT)
2495 return x;
2496 for (t0 = 0; t0 != VSIGCOUNT; t0++)
2497 if (!strcmp(s,sigs[t0]))
2498 return t0;
2499 return -1;
2500}
2501
2502int bin_trap(name,argv,ops,func) /**/
2503char *name;char **argv;char *ops;int func;
2504{
2505List l;
2506char *arg;
2507
2508 if (!*argv) {
2509 int t0;
2510
2511 for (t0 = 0; t0 != VSIGCOUNT; t0++)
2512 if (sigtrapped[t0])
2513 if (!sigfuncs[t0])
2514 printf("TRAP%s () {}\n",sigs[t0]);
2515 else {
2516 char *s = getpermtext((vptr) sigfuncs[t0]);
2517 printf("TRAP%s () {\n\t%s\n}\n",sigs[t0],s);
2518 free(s);
2519 }
2520 return 0;
2521 }
2522 if (!strcmp(*argv,"-")) {
2523 int t0;
2524
2525 argv++;
2526 if (!*argv)
2527 for (t0 = 0; t0 != VSIGCOUNT; t0++) unsettrap(t0);
2528 else
2529 while (*argv) unsettrap(getsignum(*argv++));
2530 return 0;
2531 }
2532 arg = *argv++;
2533 if (!*arg) l = NULL;
2534 else if (!(l = parselstring(arg))) {
2535 zerrnam(name,"couldn't parse trap command",NULL,0);
2536 popheap();
2537 return 1;
2538 }
2539 for (; *argv; argv++) {
2540 int sg = getsignum(*argv);
2541 if (sg == -1) {
2542 zerrnam(name,"undefined signal: %s",*argv,0);
2543 break;
2544 }
2545 settrap(sg,l);
2546 }
2547 if (l) popheap();
2548 return errflag;
2549}
2550
2551void printulimit(lim,hard) /**/
2552int lim;int hard;
2553{
2554long t0;
2555
2556#ifdef RLIM_INFINITY
2557 t0 = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur;
2558 switch (lim)
2559 {
2560 case RLIMIT_CPU: printf("cpu time (seconds) "); break;
2561 case RLIMIT_FSIZE: printf("file size (blocks) "); t0 /= 512; break;
2562 case RLIMIT_DATA: printf("data seg size (kbytes) "); t0 /= 1024; break;
2563 case RLIMIT_STACK: printf("stack size (kbytes) "); t0 /= 1024; break;
2564 case RLIMIT_CORE: printf("core file size (blocks) "); t0 /= 512; break;
2565#ifdef RLIMIT_RSS
2566 case RLIMIT_RSS: printf("resident set size (kbytes) "); t0 /= 1024; break;
2567#endif
2568#ifdef RLIMIT_NOFILE
2569 case RLIMIT_NOFILE: printf("file descriptors "); break;
2570#endif
2571 }
2572 printf("%ld\n",t0);
2573#endif
2574}
2575
2576int bin_ulimit(name,argv,ops,func) /**/
2577char *name;char **argv;char *ops;int func;
2578{
2579int res,hard;
2580
2581#ifndef RLIM_INFINITY
2582 zerrnam(name,"not available on this system",NULL,0);
2583 return 1;
2584#else
2585 hard = ops['H'];
2586 if (ops['a'] || !ops['@'])
2587 res = -1;
2588 else if (ops['t'])
2589 res = RLIMIT_CPU;
2590 else if (ops['f'])
2591 res = RLIMIT_FSIZE;
2592 else if (ops['d'])
2593 res = RLIMIT_DATA;
2594 else if (ops['s'])
2595 res = RLIMIT_STACK;
2596 else if (ops['c'])
2597 res = RLIMIT_CORE;
2598#ifdef RLIMIT_RSS
2599 else if (ops['m'])
2600 res = RLIMIT_RSS;
2601#endif
2602#ifdef RLIMIT_NOFILE
2603 else if (ops['n'])
2604 res = RLIMIT_NOFILE;
2605#endif
2606 else
2607 {
2608 zerrnam(name,"no such limit",NULL,0);
2609 return 1;
2610 }
2611 if (res == -1)
2612 if (*argv)
2613 {
2614 zerrnam(name,"no arguments required after -a",NULL,0);
2615 return 1;
2616 }
2617 else
2618 {
2619 int t0;
2620
2621 for (t0 = 0; t0 != RLIM_NLIMITS; t0++)
2622 printulimit(t0,hard);
2623 return 0;
2624 }
2625 if (!*argv)
2626 printulimit(res,hard);
2627 else if (strcmp(*argv,"unlimited"))
2628 {
2629 long t0;
2630
2631 t0 = atol(*argv);
2632 switch(res)
2633 {
2634 case RLIMIT_FSIZE: case RLIMIT_CORE: t0 *= 512; break;
2635 case RLIMIT_DATA: case RLIMIT_STACK:
2636#ifdef RLIMIT_RSS
2637 case RLIMIT_RSS:
2638#endif
2639 t0 *= 1024; break;
2640 }
2641 if (hard)
2642 {
2643 if (t0 > limits[res].rlim_max && geteuid())
2644 {
2645 zerrnam(name,"can't raise hard limits",NULL,0);
2646 return 1;
2647 }
2648 limits[res].rlim_max = t0;
2649 }
2650 else
2651 {
2652 if (t0 > limits[res].rlim_max)
2653 {
2654 if (geteuid())
2655 {
2656 zerrnam(name,"value exceeds hard limit",NULL,0);
2657 return 1;
2658 }
2659 limits[res].rlim_max = limits[res].rlim_cur = t0;
2660 }
2661 else
2662 limits[res].rlim_cur = t0;
2663 }
2664 }
2665 else
2666 {
2667 if (hard)
2668 {
2669 if (geteuid())
2670 {
2671 zerrnam(name,"can't remove hard limits",NULL,0);
2672 return 1;
2673 }
2674 limits[res].rlim_max = RLIM_INFINITY;
2675 }
2676 else
2677 limits[res].rlim_cur = limits[res].rlim_max;
2678 }
2679 return 0;
2680#endif
2681}
2682
2683int putraw(c) /**/
2684int c;
2685{
2686 putchar(c);
2687 return 0;
2688}
2689
2690int bin_echotc(name,argv,ops,func) /**/
2691char *name;char **argv;char *ops;int func;
2692{
2693char *s,buf[2048],*t,*u;
2694int num,argct,t0;
2695
2696 s = *argv++;
2697 if (!termok)
2698 return 1;
2699 if ((num = tgetnum(s)) != -1)
2700 {
2701 printf("%d\n",num);
2702 return 0;
2703 }
2704 u = buf;
2705 t = tgetstr(s,&u);
2706 if (!t || !*t)
2707 {
2708 zerrnam(name,"no such capability: %s",s,0);
2709 return 1;
2710 }
2711 for (argct = 0, u = t; *u; u++)
2712 if (*u == '%')
2713 {
2714 if (u++, (*u == 'd' || *u == '2' || *u == '3' || *u == '.' ||
2715 *u == '+'))
2716 argct++;
2717 }
2718 if (arrlen(argv) != argct)
2719 {
2720 zerrnam(name,(arrlen(argv) < argct) ? "not enough arguments" :
2721 "too many arguments",NULL,0);
2722 return 1;
2723 }
2724 if (!argct)
2725 tputs(t,1,putraw);
2726 else
2727 {
2728 t0 = (argv[1]) ? atoi(argv[1]) : atoi(*argv);
2729 tputs(tgoto(t,atoi(*argv),t0),t0,putraw);
2730 }
2731 return 0;
2732}
2733
2734int bin_pwd(name,argv,ops,func) /**/
2735char *name;char **argv;char *ops;int func;
2736{
2737 printf("%s\n",pwd);
2738 return 0;
2739}
2740
2741#define TEST_END 0
2742#define TEST_INPAR 1
2743#define TEST_OUTPAR 2
2744#define TEST_STR 3
2745#define TEST_AND 4
2746#define TEST_OR 5
2747#define TEST_NOT 6
2748
2749static char **tsp;
2750static int *tip;
2751
2752int bin_test(name,argv,ops,func) /**/
2753char *name;char **argv;char *ops;int func;
2754{
2755char **s;
2756int cnt,*arr,*ap;
2757Cond c;
2758
2759 if (func == BIN_BRACKET)
2760 {
2761 for (s = argv; *s; s++);
2762 if (s == argv || strcmp(s[-1],"]"))
2763 {
2764 zerrnam(name,"']' expected",NULL,0);
2765 return 1;
2766 }
2767 s[-1] = NULL;
2768 }
2769 for (s = argv, cnt = 0; *s; s++,cnt++);
2770 ap = arr = alloc((cnt+1)*sizeof *arr);
2771 for (s = argv; *s; s++,ap++)
2772 if (!strcmp(*s,"("))
2773 *ap = TEST_INPAR;
2774 else if (!strcmp(*s,")"))
2775 *ap = TEST_OUTPAR;
2776 else if (!strcmp(*s,"-a"))
2777 *ap = TEST_AND;
2778 else if (!strcmp(*s,"-o"))
2779 *ap = TEST_OR;
2780 else if (!strcmp(*s,"!"))
2781 *ap = TEST_NOT;
2782 else
2783 *ap = TEST_STR;
2784 *ap = TEST_END;
2785 tsp = argv;
2786 tip = arr;
2787 c = partest(0);
2788 if (*tip != TEST_END || errflag)
2789 {
2790 zerrnam(name,"parse error",NULL,0);
2791 return 1;
2792 }
2793 return (c) ? !evalcond(c) : 1;
2794}
2795
2796Cond partest(level) /**/
2797int level;
2798{
2799Cond a,b;
2800
2801 switch (level)
2802 {
2803 case 0:
2804 a = partest(1);
2805 if (*tip == TEST_OR)
2806 {
2807 tip++,tsp++;
2808 b = makecond();
2809 b->left = a;
2810 b->right = partest(0);
2811 b->type = COND_OR;
2812 return b;
2813 }
2814 return a;
2815 case 1:
2816 a = partest(2);
2817 if (*tip == TEST_AND)
2818 {
2819 tip++,tsp++;
2820 b = makecond();
2821 b->left = a;
2822 b->right = partest(1);
2823 b->type = COND_AND;
2824 return b;
2825 }
2826 return a;
2827 case 2:
2828 if (*tip == TEST_NOT)
2829 {
2830 tip++,tsp++;
2831 b = makecond();
2832 b->left = partest(2);
2833 b->type = COND_NOT;
2834 return b;
2835 }
2836 case 3:
2837 if (*tip == TEST_INPAR)
2838 {
2839 tip++,tsp++;
2840 b = partest(0);
2841 if (*tip != TEST_OUTPAR)
2842 {
2843 zerrnam("test","parse error",NULL,0);
2844 return NULL;
2845 }
2846 tip++,tsp++;
2847 return b;
2848 }
2849 if (tip[0] != TEST_STR)
2850 {
2851 zerrnam("test","parse error",NULL,0);
2852 return NULL;
2853 }
2854 else if (tip[1] != TEST_STR)
2855 {
2856 b = makecond();
2857 if (!strcmp(*tsp,"-t"))
2858 {
2859 b->left = strdup("1");
2860 b->type = 't';
2861 }
2862 else
2863 {
2864 b->left = tsp[0];
2865 b->type = 'n';
2866 }
2867 tip++,tsp++;
2868 return b;
2869 }
2870 else if (tip[2] != TEST_STR)
2871 {
2872 b = par_cond_double(tsp[0],tsp[1]);
2873 tip += 2,tsp += 2;
2874 return b;
2875 }
2876 else
2877 {
2878 b = par_cond_triple(tsp[0],tsp[1],tsp[2]);
2879 tip += 3,tsp += 3;
2880 return b;
2881 }
2882 }
2883 return NULL;
2884}
2885
2886int bin_compctl(name,argv,ops,func) /**/
2887char *name;char **argv;char *ops;int func;
2888{
2889int flags = 0;
2890Compctl cc = NULL;
2891char *usrkeys = NULL;
2892
2893 for (; *argv && **argv == '-'; argv++)
2894 while (*++(*argv)) switch(**argv) {
2895 case 'c': flags |= CC_COMMPATH; break;
2896 case 'f': flags |= CC_FILES; break;
2897 case 'h': flags |= CC_HOSTS; break;
2898 case 'o': flags |= CC_OPTIONS; break;
2899 case 'v': flags |= CC_VARS; break;
2900 case 'b': flags |= CC_BINDINGS; break;
2901 case 'k':
2902 flags |= CC_USRKEYS;
2903 if ((*argv)[1]) { usrkeys = (*argv)+1; *argv = ""-1; }
2904 else if (!argv[1]) {
2905 zerrnam(name,"variable name expected after -k",NULL,0);
2906 return 1;
2907 } else { usrkeys = *++argv; *argv = ""-1; }
2908 break;
2909 case 'C': cc = &cc_compos; break;
2910 case 'D': cc = &cc_default; break;
2911 default: zerrnam(name,"bad option: %c",NULL,**argv); return 1;
2912 }
2913 if (cc) {
2914 cc->mask = flags;
2915 if (cc->keyvar) free(cc->keyvar);
2916 cc->keyvar = ztrdup(usrkeys);
2917 }
2918 if (!*argv) {
2919 if (!cc) {
2920 showflag = flags;
2921 listhtable(compctltab,(HFunc) printcompctl);
2922 printcompctl("COMMAND",&cc_compos);
2923 printcompctl("DEFAULT",&cc_default);
2924 }
2925 return 0;
2926 }
2927 compctl_process(argv,flags,usrkeys);
2928 return 0;
2929}
2930
2931void printcompctl(s,cc) /**/
2932char *s;Compctl cc;
2933{
2934char *css = "fchovb";
2935
2936 if (cc->mask & showflag) {
2937 puts(s);
2938 } else if (!showflag) {
2939 int flags = cc->mask;
2940 printf("%s -",s);
2941 while (*css) {
2942 if (flags & 1) putchar(*css);
2943 css++; flags >>= 1;
2944 }
2945 if (flags & 1) printf("k %s",cc->keyvar);
2946 putchar('\n');
2947 }
2948}
2949
2950void compctl_process(s,mask,uk) /**/
2951char **s;int mask;char *uk;
2952{
2953Compctl cc;
2954
2955 for (;*s;s++) {
2956 cc = zalloc(sizeof *cc);
2957 cc->mask = mask; cc->keyvar = ztrdup(uk);
2958 addhnode(ztrdup(*s),cc,compctltab,freecompctl);
2959 }
2960}
2961
2962int bin_ttyctl(name,argv,ops,func) /**/
2963char *name;char **argv;char *ops;int func;
2964{
2965 if (ops['f'] || !ops['@']) ttyfrozen = 1;
2966 else if (ops['u']) ttyfrozen = 0;
2967 return 0;
2968}
2969