Commit | Line | Data |
---|---|---|
979aed98 KB |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Kenneth Almquist. | |
7 | * | |
8 | * %sccs.include.redist.c% | |
9 | */ | |
10 | ||
11 | #ifndef lint | |
12 | static char sccsid[] = "@(#)exec.c 5.1 (Berkeley) %G%"; | |
13 | #endif /* not lint */ | |
14 | ||
15 | /* | |
16 | * When commands are first encountered, they are entered in a hash table. | |
17 | * This ensures that a full path search will not have to be done for them | |
18 | * on each invocation. | |
19 | * | |
20 | * We should investigate converting to a linear search, even though that | |
21 | * would make the command name "hash" a misnomer. | |
22 | */ | |
23 | ||
24 | #include "shell.h" | |
25 | #include "main.h" | |
26 | #include "nodes.h" | |
27 | #include "parser.h" | |
28 | #include "redir.h" | |
29 | #include "eval.h" | |
30 | #include "exec.h" | |
31 | #include "builtins.h" | |
32 | #include "var.h" | |
33 | #include "options.h" | |
34 | #include "input.h" | |
35 | #include "output.h" | |
36 | #include "syntax.h" | |
37 | #include "memalloc.h" | |
38 | #include "error.h" | |
39 | #include "init.h" | |
40 | #include "mystring.h" | |
41 | #include <sys/types.h> | |
42 | #include <sys/stat.h> | |
43 | #include <fcntl.h> | |
44 | #include <errno.h> | |
45 | ||
46 | ||
47 | #define CMDTABLESIZE 31 /* should be prime */ | |
48 | #define ARB 1 /* actual size determined at run time */ | |
49 | ||
50 | ||
51 | ||
52 | struct tblentry { | |
53 | struct tblentry *next; /* next entry in hash chain */ | |
54 | union param param; /* definition of builtin function */ | |
55 | short cmdtype; /* index identifying command */ | |
56 | char rehash; /* if set, cd done since entry created */ | |
57 | char cmdname[ARB]; /* name of command */ | |
58 | }; | |
59 | ||
60 | ||
61 | STATIC struct tblentry *cmdtable[CMDTABLESIZE]; | |
62 | STATIC int builtinloc; /* index in path of %builtin, or -1 */ | |
63 | ||
64 | ||
65 | #ifdef __STDC__ | |
66 | STATIC void tryexec(char *, char **, char **); | |
67 | STATIC void execinterp(char **, char **); | |
68 | STATIC void printentry(struct tblentry *); | |
69 | STATIC void clearcmdentry(int); | |
70 | STATIC struct tblentry *cmdlookup(char *, int); | |
71 | STATIC void delete_cmd_entry(void); | |
72 | #else | |
73 | STATIC void tryexec(); | |
74 | STATIC void execinterp(); | |
75 | STATIC void printentry(); | |
76 | STATIC void clearcmdentry(); | |
77 | STATIC struct tblentry *cmdlookup(); | |
78 | STATIC void delete_cmd_entry(); | |
79 | #endif | |
80 | ||
81 | ||
82 | ||
83 | /* | |
84 | * Exec a program. Never returns. If you change this routine, you may | |
85 | * have to change the find_command routine as well. | |
86 | */ | |
87 | ||
88 | void | |
89 | shellexec(argv, envp, path, index) | |
90 | char **argv, **envp; | |
91 | char *path; | |
92 | { | |
93 | char *cmdname; | |
94 | int e; | |
95 | ||
96 | if (strchr(argv[0], '/') != NULL) { | |
97 | tryexec(argv[0], argv, envp); | |
98 | e = errno; | |
99 | } else { | |
100 | e = ENOENT; | |
101 | while ((cmdname = padvance(&path, argv[0])) != NULL) { | |
102 | if (--index < 0 && pathopt == NULL) { | |
103 | tryexec(cmdname, argv, envp); | |
104 | if (errno != ENOENT && errno != ENOTDIR) | |
105 | e = errno; | |
106 | } | |
107 | stunalloc(cmdname); | |
108 | } | |
109 | } | |
110 | error2(argv[0], errmsg(e, E_EXEC)); | |
111 | } | |
112 | ||
113 | ||
114 | STATIC void | |
115 | tryexec(cmd, argv, envp) | |
116 | char *cmd; | |
117 | char **argv; | |
118 | char **envp; | |
119 | { | |
120 | int e; | |
121 | char *p; | |
122 | ||
123 | #ifdef SYSV | |
124 | do { | |
125 | execve(cmd, argv, envp); | |
126 | } while (errno == EINTR); | |
127 | #else | |
128 | execve(cmd, argv, envp); | |
129 | #endif | |
130 | e = errno; | |
131 | if (e == ENOEXEC) { | |
132 | initshellproc(); | |
133 | setinputfile(cmd, 0); | |
134 | commandname = arg0 = savestr(argv[0]); | |
135 | #ifndef BSD | |
136 | pgetc(); pungetc(); /* fill up input buffer */ | |
137 | p = parsenextc; | |
138 | if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { | |
139 | argv[0] = cmd; | |
140 | execinterp(argv, envp); | |
141 | } | |
142 | #endif | |
143 | setparam(argv + 1); | |
144 | exraise(EXSHELLPROC); | |
145 | /*NOTREACHED*/ | |
146 | } | |
147 | errno = e; | |
148 | } | |
149 | ||
150 | ||
151 | #ifndef BSD | |
152 | /* | |
153 | * Execute an interpreter introduced by "#!", for systems where this | |
154 | * feature has not been built into the kernel. If the interpreter is | |
155 | * the shell, return (effectively ignoring the "#!"). If the execution | |
156 | * of the interpreter fails, exit. | |
157 | * | |
158 | * This code peeks inside the input buffer in order to avoid actually | |
159 | * reading any input. It would benefit from a rewrite. | |
160 | */ | |
161 | ||
162 | #define NEWARGS 5 | |
163 | ||
164 | STATIC void | |
165 | execinterp(argv, envp) | |
166 | char **argv, **envp; | |
167 | { | |
168 | int n; | |
169 | char *inp; | |
170 | char *outp; | |
171 | char c; | |
172 | char *p; | |
173 | char **ap; | |
174 | char *newargs[NEWARGS]; | |
175 | int i; | |
176 | char **ap2; | |
177 | char **new; | |
178 | ||
179 | n = parsenleft - 2; | |
180 | inp = parsenextc + 2; | |
181 | ap = newargs; | |
182 | for (;;) { | |
183 | while (--n >= 0 && (*inp == ' ' || *inp == '\t')) | |
184 | inp++; | |
185 | if (n < 0) | |
186 | goto bad; | |
187 | if ((c = *inp++) == '\n') | |
188 | break; | |
189 | if (ap == &newargs[NEWARGS]) | |
190 | bad: error("Bad #! line"); | |
191 | STARTSTACKSTR(outp); | |
192 | do { | |
193 | STPUTC(c, outp); | |
194 | } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); | |
195 | STPUTC('\0', outp); | |
196 | n++, inp--; | |
197 | *ap++ = grabstackstr(outp); | |
198 | } | |
199 | if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ | |
200 | p = newargs[0]; | |
201 | for (;;) { | |
202 | if (equal(p, "sh") || equal(p, "ash")) { | |
203 | return; | |
204 | } | |
205 | while (*p != '/') { | |
206 | if (*p == '\0') | |
207 | goto break2; | |
208 | p++; | |
209 | } | |
210 | p++; | |
211 | } | |
212 | break2:; | |
213 | } | |
214 | i = (char *)ap - (char *)newargs; /* size in bytes */ | |
215 | if (i == 0) | |
216 | error("Bad #! line"); | |
217 | for (ap2 = argv ; *ap2++ != NULL ; ); | |
218 | new = ckmalloc(i + ((char *)ap2 - (char *)argv)); | |
219 | ap = newargs, ap2 = new; | |
220 | while ((i -= sizeof (char **)) >= 0) | |
221 | *ap2++ = *ap++; | |
222 | ap = argv; | |
223 | while (*ap2++ = *ap++); | |
224 | shellexec(new, envp, pathval(), 0); | |
225 | } | |
226 | #endif | |
227 | ||
228 | ||
229 | ||
230 | /* | |
231 | * Do a path search. The variable path (passed by reference) should be | |
232 | * set to the start of the path before the first call; padvance will update | |
233 | * this value as it proceeds. Successive calls to padvance will return | |
234 | * the possible path expansions in sequence. If an option (indicated by | |
235 | * a percent sign) appears in the path entry then the global variable | |
236 | * pathopt will be set to point to it; otherwise pathopt will be set to | |
237 | * NULL. | |
238 | */ | |
239 | ||
240 | char *pathopt; | |
241 | ||
242 | char * | |
243 | padvance(path, name) | |
244 | char **path; | |
245 | char *name; | |
246 | { | |
247 | register char *p, *q; | |
248 | char *start; | |
249 | int len; | |
250 | ||
251 | if (*path == NULL) | |
252 | return NULL; | |
253 | start = *path; | |
254 | for (p = start ; *p && *p != ':' && *p != '%' ; p++); | |
255 | len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ | |
256 | while (stackblocksize() < len) | |
257 | growstackblock(); | |
258 | q = stackblock(); | |
259 | if (p != start) { | |
260 | bcopy(start, q, p - start); | |
261 | q += p - start; | |
262 | *q++ = '/'; | |
263 | } | |
264 | strcpy(q, name); | |
265 | pathopt = NULL; | |
266 | if (*p == '%') { | |
267 | pathopt = ++p; | |
268 | while (*p && *p != ':') p++; | |
269 | } | |
270 | if (*p == ':') | |
271 | *path = p + 1; | |
272 | else | |
273 | *path = NULL; | |
274 | return stalloc(len); | |
275 | } | |
276 | ||
277 | ||
278 | ||
279 | /*** Command hashing code ***/ | |
280 | ||
281 | ||
282 | hashcmd(argc, argv) char **argv; { | |
283 | struct tblentry **pp; | |
284 | struct tblentry *cmdp; | |
285 | int c; | |
286 | int verbose; | |
287 | struct cmdentry entry; | |
288 | char *name; | |
289 | ||
290 | if (argc <= 1) { | |
291 | for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { | |
292 | for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { | |
293 | printentry(cmdp); | |
294 | } | |
295 | } | |
296 | return 0; | |
297 | } | |
298 | verbose = 0; | |
299 | while ((c = nextopt("rv")) >= 0) { | |
300 | if (c == 'r') { | |
301 | clearcmdentry(0); | |
302 | } else if (c == 'v') { | |
303 | verbose++; | |
304 | } | |
305 | } | |
306 | while ((name = *argptr) != NULL) { | |
307 | if ((cmdp = cmdlookup(name, 0)) != NULL | |
308 | && (cmdp->cmdtype == CMDNORMAL | |
309 | || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) | |
310 | delete_cmd_entry(); | |
311 | find_command(name, &entry, 1); | |
312 | if (verbose) { | |
313 | if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ | |
314 | cmdp = cmdlookup(name, 0); | |
315 | printentry(cmdp); | |
316 | } | |
317 | flushall(); | |
318 | } | |
319 | argptr++; | |
320 | } | |
321 | return 0; | |
322 | } | |
323 | ||
324 | ||
325 | STATIC void | |
326 | printentry(cmdp) | |
327 | struct tblentry *cmdp; | |
328 | { | |
329 | int index; | |
330 | char *path; | |
331 | char *name; | |
332 | ||
333 | if (cmdp->cmdtype == CMDNORMAL) { | |
334 | index = cmdp->param.index; | |
335 | path = pathval(); | |
336 | do { | |
337 | name = padvance(&path, cmdp->cmdname); | |
338 | stunalloc(name); | |
339 | } while (--index >= 0); | |
340 | out1str(name); | |
341 | } else if (cmdp->cmdtype == CMDBUILTIN) { | |
342 | out1fmt("builtin %s", cmdp->cmdname); | |
343 | } else if (cmdp->cmdtype == CMDFUNCTION) { | |
344 | out1fmt("function %s", cmdp->cmdname); | |
345 | #ifdef DEBUG | |
346 | } else { | |
347 | error("internal error: cmdtype %d", cmdp->cmdtype); | |
348 | #endif | |
349 | } | |
350 | if (cmdp->rehash) | |
351 | out1c('*'); | |
352 | out1c('\n'); | |
353 | } | |
354 | ||
355 | ||
356 | ||
357 | /* | |
358 | * Resolve a command name. If you change this routine, you may have to | |
359 | * change the shellexec routine as well. | |
360 | */ | |
361 | ||
362 | void | |
363 | find_command(name, entry, printerr) | |
364 | char *name; | |
365 | struct cmdentry *entry; | |
366 | { | |
367 | struct tblentry *cmdp; | |
368 | int index; | |
369 | int prev; | |
370 | char *path; | |
371 | char *fullname; | |
372 | struct stat statb; | |
373 | int e; | |
374 | int i; | |
375 | ||
376 | /* If name contains a slash, don't use the hash table */ | |
377 | if (strchr(name, '/') != NULL) { | |
378 | entry->cmdtype = CMDNORMAL; | |
379 | entry->u.index = 0; | |
380 | return; | |
381 | } | |
382 | ||
383 | /* If name is in the table, and not invalidated by cd, we're done */ | |
384 | if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) | |
385 | goto success; | |
386 | ||
387 | /* If %builtin not in path, check for builtin next */ | |
388 | if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { | |
389 | INTOFF; | |
390 | cmdp = cmdlookup(name, 1); | |
391 | cmdp->cmdtype = CMDBUILTIN; | |
392 | cmdp->param.index = i; | |
393 | INTON; | |
394 | goto success; | |
395 | } | |
396 | ||
397 | /* We have to search path. */ | |
398 | prev = -1; /* where to start */ | |
399 | if (cmdp) { /* doing a rehash */ | |
400 | if (cmdp->cmdtype == CMDBUILTIN) | |
401 | prev = builtinloc; | |
402 | else | |
403 | prev = cmdp->param.index; | |
404 | } | |
405 | ||
406 | path = pathval(); | |
407 | e = ENOENT; | |
408 | index = -1; | |
409 | loop: | |
410 | while ((fullname = padvance(&path, name)) != NULL) { | |
411 | stunalloc(fullname); | |
412 | index++; | |
413 | if (pathopt) { | |
414 | if (prefix("builtin", pathopt)) { | |
415 | if ((i = find_builtin(name)) < 0) | |
416 | goto loop; | |
417 | INTOFF; | |
418 | cmdp = cmdlookup(name, 1); | |
419 | cmdp->cmdtype = CMDBUILTIN; | |
420 | cmdp->param.index = i; | |
421 | INTON; | |
422 | goto success; | |
423 | } else if (prefix("func", pathopt)) { | |
424 | /* handled below */ | |
425 | } else { | |
426 | goto loop; /* ignore unimplemented options */ | |
427 | } | |
428 | } | |
429 | /* if rehash, don't redo absolute path names */ | |
430 | if (fullname[0] == '/' && index <= prev) { | |
431 | if (index < prev) | |
432 | goto loop; | |
433 | TRACE(("searchexec \"%s\": no change\n", name)); | |
434 | goto success; | |
435 | } | |
436 | while (stat(fullname, &statb) < 0) { | |
437 | #ifdef SYSV | |
438 | if (errno == EINTR) | |
439 | continue; | |
440 | #endif | |
441 | if (errno != ENOENT && errno != ENOTDIR) | |
442 | e = errno; | |
443 | goto loop; | |
444 | } | |
445 | e = EACCES; /* if we fail, this will be the error */ | |
446 | if ((statb.st_mode & S_IFMT) != S_IFREG) | |
447 | goto loop; | |
448 | if (pathopt) { /* this is a %func directory */ | |
449 | stalloc(strlen(fullname) + 1); | |
450 | readcmdfile(fullname); | |
451 | if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) | |
452 | error("%s not defined in %s", name, fullname); | |
453 | stunalloc(fullname); | |
454 | goto success; | |
455 | } | |
456 | if (statb.st_uid == geteuid()) { | |
457 | if ((statb.st_mode & 0100) == 0) | |
458 | goto loop; | |
459 | } else if (statb.st_gid == getegid()) { | |
460 | if ((statb.st_mode & 010) == 0) | |
461 | goto loop; | |
462 | } else { | |
463 | if ((statb.st_mode & 01) == 0) | |
464 | goto loop; | |
465 | } | |
466 | TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); | |
467 | INTOFF; | |
468 | cmdp = cmdlookup(name, 1); | |
469 | cmdp->cmdtype = CMDNORMAL; | |
470 | cmdp->param.index = index; | |
471 | INTON; | |
472 | goto success; | |
473 | } | |
474 | ||
475 | /* We failed. If there was an entry for this command, delete it */ | |
476 | if (cmdp) | |
477 | delete_cmd_entry(); | |
478 | if (printerr) | |
479 | outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); | |
480 | entry->cmdtype = CMDUNKNOWN; | |
481 | return; | |
482 | ||
483 | success: | |
484 | cmdp->rehash = 0; | |
485 | entry->cmdtype = cmdp->cmdtype; | |
486 | entry->u = cmdp->param; | |
487 | } | |
488 | ||
489 | ||
490 | ||
491 | /* | |
492 | * Search the table of builtin commands. | |
493 | */ | |
494 | ||
495 | int | |
496 | find_builtin(name) | |
497 | char *name; | |
498 | { | |
499 | const register struct builtincmd *bp; | |
500 | ||
501 | for (bp = builtincmd ; bp->name ; bp++) { | |
502 | if (*bp->name == *name && equal(bp->name, name)) | |
503 | return bp->code; | |
504 | } | |
505 | return -1; | |
506 | } | |
507 | ||
508 | ||
509 | ||
510 | /* | |
511 | * Called when a cd is done. Marks all commands so the next time they | |
512 | * are executed they will be rehashed. | |
513 | */ | |
514 | ||
515 | void | |
516 | hashcd() { | |
517 | struct tblentry **pp; | |
518 | struct tblentry *cmdp; | |
519 | ||
520 | for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { | |
521 | for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { | |
522 | if (cmdp->cmdtype == CMDNORMAL | |
523 | || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0) | |
524 | cmdp->rehash = 1; | |
525 | } | |
526 | } | |
527 | } | |
528 | ||
529 | ||
530 | ||
531 | /* | |
532 | * Called before PATH is changed. The argument is the new value of PATH; | |
533 | * pathval() still returns the old value at this point. Called with | |
534 | * interrupts off. | |
535 | */ | |
536 | ||
537 | void | |
538 | changepath(newval) | |
539 | char *newval; | |
540 | { | |
541 | char *old, *new; | |
542 | int index; | |
543 | int firstchange; | |
544 | int bltin; | |
545 | ||
546 | old = pathval(); | |
547 | new = newval; | |
548 | firstchange = 9999; /* assume no change */ | |
549 | index = 0; | |
550 | bltin = -1; | |
551 | for (;;) { | |
552 | if (*old != *new) { | |
553 | firstchange = index; | |
554 | if (*old == '\0' && *new == ':' | |
555 | || *old == ':' && *new == '\0') | |
556 | firstchange++; | |
557 | old = new; /* ignore subsequent differences */ | |
558 | } | |
559 | if (*new == '\0') | |
560 | break; | |
561 | if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) | |
562 | bltin = index; | |
563 | if (*new == ':') { | |
564 | index++; | |
565 | } | |
566 | new++, old++; | |
567 | } | |
568 | if (builtinloc < 0 && bltin >= 0) | |
569 | builtinloc = bltin; /* zap builtins */ | |
570 | if (builtinloc >= 0 && bltin < 0) | |
571 | firstchange = 0; | |
572 | clearcmdentry(firstchange); | |
573 | builtinloc = bltin; | |
574 | } | |
575 | ||
576 | ||
577 | /* | |
578 | * Clear out command entries. The argument specifies the first entry in | |
579 | * PATH which has changed. | |
580 | */ | |
581 | ||
582 | STATIC void | |
583 | clearcmdentry(firstchange) { | |
584 | struct tblentry **tblp; | |
585 | struct tblentry **pp; | |
586 | struct tblentry *cmdp; | |
587 | ||
588 | INTOFF; | |
589 | for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { | |
590 | pp = tblp; | |
591 | while ((cmdp = *pp) != NULL) { | |
592 | if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange | |
593 | || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) { | |
594 | *pp = cmdp->next; | |
595 | ckfree(cmdp); | |
596 | } else { | |
597 | pp = &cmdp->next; | |
598 | } | |
599 | } | |
600 | } | |
601 | INTON; | |
602 | } | |
603 | ||
604 | ||
605 | /* | |
606 | * Delete all functions. | |
607 | */ | |
608 | ||
609 | #ifdef mkinit | |
610 | MKINIT void deletefuncs(); | |
611 | ||
612 | SHELLPROC { | |
613 | deletefuncs(); | |
614 | } | |
615 | #endif | |
616 | ||
617 | void | |
618 | deletefuncs() { | |
619 | struct tblentry **tblp; | |
620 | struct tblentry **pp; | |
621 | struct tblentry *cmdp; | |
622 | ||
623 | INTOFF; | |
624 | for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { | |
625 | pp = tblp; | |
626 | while ((cmdp = *pp) != NULL) { | |
627 | if (cmdp->cmdtype == CMDFUNCTION) { | |
628 | *pp = cmdp->next; | |
629 | freefunc(cmdp->param.func); | |
630 | ckfree(cmdp); | |
631 | } else { | |
632 | pp = &cmdp->next; | |
633 | } | |
634 | } | |
635 | } | |
636 | INTON; | |
637 | } | |
638 | ||
639 | ||
640 | ||
641 | /* | |
642 | * Locate a command in the command hash table. If "add" is nonzero, | |
643 | * add the command to the table if it is not already present. The | |
644 | * variable "lastcmdentry" is set to point to the address of the link | |
645 | * pointing to the entry, so that delete_cmd_entry can delete the | |
646 | * entry. | |
647 | */ | |
648 | ||
649 | struct tblentry **lastcmdentry; | |
650 | ||
651 | ||
652 | STATIC struct tblentry * | |
653 | cmdlookup(name, add) | |
654 | char *name; | |
655 | { | |
656 | int hashval; | |
657 | register char *p; | |
658 | struct tblentry *cmdp; | |
659 | struct tblentry **pp; | |
660 | ||
661 | p = name; | |
662 | hashval = *p << 4; | |
663 | while (*p) | |
664 | hashval += *p++; | |
665 | hashval &= 0x7FFF; | |
666 | pp = &cmdtable[hashval % CMDTABLESIZE]; | |
667 | for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { | |
668 | if (equal(cmdp->cmdname, name)) | |
669 | break; | |
670 | pp = &cmdp->next; | |
671 | } | |
672 | if (add && cmdp == NULL) { | |
673 | INTOFF; | |
674 | cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB | |
675 | + strlen(name) + 1); | |
676 | cmdp->next = NULL; | |
677 | cmdp->cmdtype = CMDUNKNOWN; | |
678 | cmdp->rehash = 0; | |
679 | strcpy(cmdp->cmdname, name); | |
680 | INTON; | |
681 | } | |
682 | lastcmdentry = pp; | |
683 | return cmdp; | |
684 | } | |
685 | ||
686 | ||
687 | /* | |
688 | * Delete the command entry returned on the last lookup. | |
689 | */ | |
690 | ||
691 | STATIC void | |
692 | delete_cmd_entry() { | |
693 | struct tblentry *cmdp; | |
694 | ||
695 | INTOFF; | |
696 | cmdp = *lastcmdentry; | |
697 | *lastcmdentry = cmdp->next; | |
698 | ckfree(cmdp); | |
699 | INTON; | |
700 | } | |
701 | ||
702 | ||
703 | ||
704 | #ifdef notdef | |
705 | void | |
706 | getcmdentry(name, entry) | |
707 | char *name; | |
708 | struct cmdentry *entry; | |
709 | { | |
710 | struct tblentry *cmdp = cmdlookup(name, 0); | |
711 | ||
712 | if (cmdp) { | |
713 | entry->u = cmdp->param; | |
714 | entry->cmdtype = cmdp->cmdtype; | |
715 | } else { | |
716 | entry->cmdtype = CMDUNKNOWN; | |
717 | entry->u.index = 0; | |
718 | } | |
719 | } | |
720 | #endif | |
721 | ||
722 | ||
723 | /* | |
724 | * Add a new command entry, replacing any existing command entry for | |
725 | * the same name. | |
726 | */ | |
727 | ||
728 | void | |
729 | addcmdentry(name, entry) | |
730 | char *name; | |
731 | struct cmdentry *entry; | |
732 | { | |
733 | struct tblentry *cmdp; | |
734 | ||
735 | INTOFF; | |
736 | cmdp = cmdlookup(name, 1); | |
737 | if (cmdp->cmdtype == CMDFUNCTION) { | |
738 | freefunc(cmdp->param.func); | |
739 | } | |
740 | cmdp->cmdtype = entry->cmdtype; | |
741 | cmdp->param = entry->u; | |
742 | INTON; | |
743 | } | |
744 | ||
745 | ||
746 | /* | |
747 | * Define a shell function. | |
748 | */ | |
749 | ||
750 | void | |
751 | defun(name, func) | |
752 | char *name; | |
753 | union node *func; | |
754 | { | |
755 | struct cmdentry entry; | |
756 | ||
757 | INTOFF; | |
758 | entry.cmdtype = CMDFUNCTION; | |
759 | entry.u.func = copyfunc(func); | |
760 | addcmdentry(name, &entry); | |
761 | INTON; | |
762 | } | |
763 | ||
764 | ||
765 | /* | |
766 | * Delete a function if it exists. | |
767 | */ | |
768 | ||
769 | void | |
770 | unsetfunc(name) | |
771 | char *name; | |
772 | { | |
773 | struct tblentry *cmdp; | |
774 | ||
775 | if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { | |
776 | freefunc(cmdp->param.func); | |
777 | delete_cmd_entry(); | |
778 | } | |
779 | } |