BSD 4_4_Lite1 development
[unix-history] / usr / src / contrib / rc-1.4 / builtins.c
CommitLineData
489be711
C
1/* builtins.c: the collection of rc's builtin commands */
2
3/*
4 NOTE: rc's builtins do not call "rc_error" because they are
5 commands, and rc errors usually arise from syntax errors. e.g.,
6 you probably don't want interpretation of a shell script to stop
7 because of a bad umask.
8*/
9
10#include <sys/ioctl.h>
11#include <setjmp.h>
12#include <errno.h>
13#include "rc.h"
14#include "jbwrap.h"
15#include <sys/time.h>
16#include <sys/resource.h>
17
18extern int umask(int);
19
20static void b_break(char **), b_cd(char **), b_eval(char **), b_exit(char **),
21 b_newpgrp(char **), b_return(char **), b_shift(char **), b_umask(char **),
22 b_wait(char **), b_whatis(char **);
23
24static void b_limit(char **);
25static void b_echo(char **);
26
27static struct {
28 builtin_t *p;
29 char *name;
30} builtins[] = {
31 { b_break, "break" },
32 { b_builtin, "builtin" },
33 { b_cd, "cd" },
34 { b_echo, "echo" },
35 { b_eval, "eval" },
36 { b_exec, "exec" },
37 { b_exit, "exit" },
38 { b_limit, "limit" },
39 { b_newpgrp, "newpgrp" },
40 { b_return, "return" },
41 { b_shift, "shift" },
42 { b_umask, "umask" },
43 { b_wait, "wait" },
44 { b_whatis, "whatis" },
45 { b_dot, "." },
46};
47
48extern builtin_t *isbuiltin(char *s) {
49 int i;
50 for (i = 0; i < arraysize(builtins); i++)
51 if (streq(builtins[i].name, s))
52 return builtins[i].p;
53 return NULL;
54}
55
56/* funcall() is the wrapper used to invoke shell functions. pushes $*, and "return" returns here. */
57
58extern void funcall(char **av) {
59 Jbwrap j;
60 Estack e1, e2;
61 Edata jreturn, star;
62 if (setjmp(j.j))
63 return;
64 starassign(*av, av+1, TRUE);
65 jreturn.jb = &j;
66 star.name = "*";
67 except(eReturn, jreturn, &e1);
68 except(eVarstack, star, &e2);
69 walk(treecpy(fnlookup(*av), nalloc), TRUE);
70 varrm("*", TRUE);
71 unexcept(); /* eVarstack */
72 unexcept(); /* eReturn */
73}
74
75static void arg_count(char *name) {
76 fprint(2, "too many arguments to %s\n", name);
77 set(FALSE);
78}
79
80static void badnum(char *num) {
81 fprint(2, "%s is a bad number\n", num);
82 set(FALSE);
83}
84
85/* a dummy command. (exec() performs "exec" simply by not forking) */
86
87extern void b_exec(char **av) {
88}
89
90/* echo -n omits a newline. echo -- -n echos '-n' */
91
92static void b_echo(char **av) {
93 char *format = "%A\n";
94 if (*++av != NULL) {
95 if (streq(*av, "-n"))
96 format = "%A", av++;
97 else if (streq(*av, "--"))
98 av++;
99 }
100 fprint(1, format, av);
101 set(TRUE);
102}
103
104/* cd. traverse $cdpath if the directory given is not an absolute pathname */
105
106static void b_cd(char **av) {
107 List *s, nil;
108 char *path = NULL;
109 size_t t, pathlen = 0;
110 if (*++av == NULL) {
111 s = varlookup("home");
112 *av = (s == NULL) ? "/" : s->w;
113 } else if (av[1] != NULL) {
114 arg_count("cd");
115 return;
116 }
117 if (isabsolute(*av) || streq(*av, ".") || streq(*av, "..")) { /* absolute pathname? */
118 if (chdir(*av) < 0) {
119 set(FALSE);
120 uerror(*av);
121 } else
122 set(TRUE);
123 } else {
124 s = varlookup("cdpath");
125 if (s == NULL) {
126 s = &nil;
127 nil.w = "";
128 nil.n = NULL;
129 }
130 do {
131 if (s != &nil && *s->w != '\0') {
132 t = strlen(*av) + strlen(s->w) + 2;
133 if (t > pathlen)
134 path = nalloc(pathlen = t);
135 strcpy(path, s->w);
136 if (!streq(s->w, "/")) /* "//" is special to POSIX */
137 strcat(path, "/");
138 strcat(path, *av);
139 } else {
140 pathlen = 0;
141 path = *av;
142 }
143 if (chdir(path) >= 0) {
144 set(TRUE);
145 if (interactive && *s->w != '\0' && !streq(s->w, "."))
146 fprint(1, "%s\n", path);
147 return;
148 }
149 s = s->n;
150 } while (s != NULL);
151 fprint(2, "couldn't cd to %s\n", *av);
152 set(FALSE);
153 }
154}
155
156static void b_umask(char **av) {
157 int i;
158 if (*++av == NULL) {
159 set(TRUE);
160 i = umask(0);
161 umask(i);
162 fprint(1, "0%o\n", i);
163 } else if (av[1] == NULL) {
164 i = o2u(*av);
165 if ((unsigned int) i > 0777) {
166 fprint(2, "bad umask\n");
167 set(FALSE);
168 } else {
169 umask(i);
170 set(TRUE);
171 }
172 } else {
173 arg_count("umask");
174 return;
175 }
176}
177
178static void b_exit(char **av) {
179 if (*++av != NULL)
180 ssetstatus(av);
181 rc_exit(getstatus());
182}
183
184/* raise a "return" exception, i.e., return from a function. if an integer argument is present, set $status to it */
185
186static void b_return(char **av) {
187 if (*++av != NULL)
188 ssetstatus(av);
189 rc_raise(eReturn);
190}
191
192/* raise a "break" exception for breaking out of for and while loops */
193
194static void b_break(char **av) {
195 if (av[1] != NULL) {
196 arg_count("break");
197 return;
198 }
199 rc_raise(eBreak);
200}
201
202/* shift $* n places (default 1) */
203
204static void b_shift(char **av) {
205 int shift = (av[1] == NULL ? 1 : a2u(av[1]));
206 List *s, *dollarzero;
207 if (av[1] != NULL && av[2] != NULL) {
208 arg_count("shift");
209 return;
210 }
211 if (shift < 0) {
212 badnum(av[1]);
213 return;
214 }
215 s = varlookup("*")->n;
216 dollarzero = varlookup("0");
217 while (s != NULL && shift != 0) {
218 s = s->n;
219 --shift;
220 }
221 if (s == NULL && shift != 0) {
222 fprint(2, "cannot shift\n");
223 set(FALSE);
224 } else {
225 varassign("*", append(dollarzero, s), FALSE);
226 set(TRUE);
227 }
228}
229
230/* dud function */
231
232extern void b_builtin(char **av) {
233}
234
235/* wait for a given process, or all outstanding processes */
236
237static void b_wait(char **av) {
238 int stat, pid;
239 if (av[1] == NULL) {
240 waitforall();
241 return;
242 }
243 if (av[2] != NULL) {
244 arg_count("wait");
245 return;
246 }
247 if ((pid = a2u(av[1])) < 0) {
248 badnum(av[1]);
249 return;
250 }
251 if (rc_wait4(pid, &stat, FALSE) > 0)
252 setstatus(pid, stat);
253 else
254 set(FALSE);
255 SIGCHK;
256}
257
258/*
259 whatis without arguments prints all variables and functions. Otherwise, check to see if a name
260 is defined as a variable, function or pathname.
261*/
262
263static void b_whatis(char **av) {
264 bool f, found;
265 bool ess = FALSE;
266 int i, ac, c;
267 List *s;
268 Node *n;
269 char *e;
270 for (rc_optind = ac = 0; av[ac] != NULL; ac++)
271 ; /* count the arguments for getopt */
272 while ((c = rc_getopt(ac, av, "s")) == 's')
273 ess = TRUE;
274 if (c != -1) {
275 set(FALSE);
276 return;
277 }
278 av += rc_optind;
279 if (*av == NULL && !ess) {
280 whatare_all_vars();
281 set(TRUE);
282 return;
283 }
284 if (ess)
285 whatare_all_signals();
286 found = TRUE;
287 for (i = 0; av[i] != NULL; i++) {
288 f = FALSE;
289 errno = ENOENT;
290 if ((s = varlookup(av[i])) != NULL) {
291 f = TRUE;
292 prettyprint_var(1, av[i], s);
293 }
294 if ((n = fnlookup(av[i])) != NULL) {
295 f = TRUE;
296 prettyprint_fn(1, av[i], n);
297 } else if (isbuiltin(av[i]) != NULL) {
298 f = TRUE;
299 fprint(1, "builtin %s\n", av[i]);
300 } else if ((e = which(av[i], FALSE)) != NULL) {
301 f = TRUE;
302 fprint(1, "%s\n", e);
303 }
304 if (!f) {
305 found = FALSE;
306 if (errno != ENOENT)
307 uerror(av[i]);
308 else
309 fprint(2, "%s not found\n", av[i]);
310 }
311 }
312 set(found);
313}
314
315/* push a string to be eval'ed onto the input stack. evaluate it */
316
317static void b_eval(char **av) {
318 bool i = interactive;
319 if (av[1] == NULL)
320 return;
321 interactive = FALSE;
322 pushstring(av + 1, i); /* don't reset line numbers on noninteractive eval */
323 doit(TRUE);
324 interactive = i;
325}
326
327/*
328 push a file to be interpreted onto the input stack. with "-i" treat this as an interactive
329 input source.
330*/
331
332extern void b_dot(char **av) {
333 int fd;
334 bool old_i = interactive, i = FALSE;
335 Estack e;
336 Edata star;
337 av++;
338 if (*av == NULL)
339 return;
340 if (streq(*av, "-i")) {
341 av++;
342 i = TRUE;
343 }
344 if (dasheye) { /* rc -i file has to do the right thing. reset the dasheye state to FALSE, though. */
345 dasheye = FALSE;
346 i = TRUE;
347 }
348 if (*av == NULL)
349 return;
350 fd = rc_open(*av, rFrom);
351 if (fd < 0) {
352 if (rcrc) /* on rc -l, don't flag nonexistence of .rcrc */
353 rcrc = FALSE;
354 else {
355 uerror(*av);
356 set(FALSE);
357 }
358 return;
359 }
360 rcrc = FALSE;
361 starassign(*av, av+1, TRUE);
362 pushfd(fd);
363 interactive = i;
364 star.name = "*";
365 except(eVarstack, star, &e);
366 doit(TRUE);
367 varrm("*", TRUE);
368 unexcept(); /* eVarstack */
369 interactive = old_i;
370}
371
372/* put rc into a new pgrp. Used on the NeXT where the Terminal program is broken (sigh) */
373
374static void b_newpgrp(char **av) {
375 if (av[1] != NULL) {
376 arg_count("newpgrp");
377 return;
378 }
379 setpgrp(rc_pid, rc_pid);
380 ioctl(2, TIOCSPGRP, &rc_pid);
381}
382
383/* Berkeley limit support was cleaned up by Paul Haahr. */
384
385typedef struct Suffix Suffix;
386struct Suffix {
387 const Suffix *next;
388 long amount;
389 char *name;
390};
391
392static const Suffix
393 kbsuf = { NULL, 1024, "k" },
394 mbsuf = { &kbsuf, 1024*1024, "m" },
395 gbsuf = { &mbsuf, 1024*1024*1024, "g" },
396 stsuf = { NULL, 1, "s" },
397 mtsuf = { &stsuf, 60, "m" },
398 htsuf = { &mtsuf, 60*60, "h" };
399#define SIZESUF &gbsuf
400#define TIMESUF &htsuf
401#define NOSUF ((Suffix *) NULL) /* for RLIMIT_NOFILE on SunOS 4.1 */
402
403typedef struct {
404 char *name;
405 int flag;
406 const Suffix *suffix;
407} Limit;
408static const Limit limits[] = {
409 { "cputime", RLIMIT_CPU, TIMESUF },
410 { "filesize", RLIMIT_FSIZE, SIZESUF },
411 { "datasize", RLIMIT_DATA, SIZESUF },
412 { "stacksize", RLIMIT_STACK, SIZESUF },
413 { "coredumpsize", RLIMIT_CORE, SIZESUF },
414 { "memoryuse", RLIMIT_RSS, SIZESUF },
415 { "descriptors", RLIMIT_NOFILE, NOSUF },
416 { NULL, 0, NULL }
417};
418
419static void printlimit(const Limit *limit, bool hard) {
420 struct rlimit rlim;
421 long lim;
422 getrlimit(limit->flag, &rlim);
423 if (hard)
424 lim = rlim.rlim_max;
425 else
426 lim = rlim.rlim_cur;
427 if ((unsigned) lim == (unsigned) RLIM_INFINITY)
428 fprint(1, "%s \tunlimited\n", limit->name);
429 else {
430 const Suffix *suf;
431 for (suf = limit->suffix; suf != NULL; suf = suf->next)
432 if (lim % suf->amount == 0 && (lim != 0 || suf->amount > 1)) {
433 lim /= suf->amount;
434 break;
435 }
436 fprint(1, "%s \t%d%s\n", limit->name, lim, (suf == NULL || lim == 0) ? "" : suf->name);
437 }
438}
439
440static long parselimit(const Limit *limit, char *s) {
441 char *t;
442 int len = strlen(s);
443 long lim = 1;
444 const Suffix *suf = limit->suffix;
445 if (streq(s, "unlimited"))
446 return RLIM_INFINITY;
447 if (suf == TIMESUF && (t = strchr(s, ':')) != NULL) {
448 *t++ = '\0';
449 lim = 60 * a2u(s) + a2u(t);
450 } else {
451 for (; suf != NULL; suf = suf->next)
452 if (streq(suf->name, s + len - strlen(suf->name))) {
453 s[len - strlen(suf->name)] = '\0';
454 lim *= suf->amount;
455 break;
456 }
457 lim *= a2u(s);
458 }
459 return lim;
460}
461
462static void b_limit(char **av) {
463 const Limit *lp = limits;
464 bool hard = FALSE;
465 if (*++av != NULL && streq(*av, "-h")) {
466 av++;
467 hard = TRUE;
468 }
469 if (*av == NULL) {
470 for (; lp->name != NULL; lp++)
471 printlimit(lp, hard);
472 return;
473 }
474 for (;; lp++) {
475 if (lp->name == NULL) {
476 fprint(2, "no such limit\n");
477 set(FALSE);
478 return;
479 }
480 if (streq(*av, lp->name))
481 break;
482 }
483 if (*++av == NULL)
484 printlimit(lp, hard);
485 else {
486 struct rlimit rlim;
487 long pl;
488 getrlimit(lp->flag, &rlim);
489 if ((pl = parselimit(lp, *av)) < 0) {
490 fprint(2, "bad limit\n");
491 set(FALSE);
492 return;
493 }
494 if (hard)
495 rlim.rlim_max = pl;
496 else
497 rlim.rlim_cur = pl;
498 if (setrlimit(lp->flag, &rlim) == -1) {
499 uerror("setrlimit");
500 set(FALSE);
501 } else
502 set(TRUE);
503 }
504}