source: mainline/uspace/app/ash/exec.c@ 8ccd2ea

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8ccd2ea was c28a023, checked in by Josef Cejka <malyzelenyhnus@…>, 17 years ago

Initial commit of ash shell.
It cannot be compiled yet.

  • Property mode set to 100644
File size: 24.1 KB
Line 
1/* $NetBSD: exec.c,v 1.31 2000/11/01 19:21:41 christos Exp $ */
2
3/*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39#include <sys/cdefs.h>
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
43#else
44__RCSID("$NetBSD: exec.c,v 1.31 2000/11/01 19:21:41 christos Exp $");
45#endif
46#endif /* not lint */
47
48#include <sys/types.h>
49#include <sys/stat.h>
50#include <unistd.h>
51#include <fcntl.h>
52#include <errno.h>
53#include <stdlib.h>
54#include <sysexits.h>
55
56/*
57 * When commands are first encountered, they are entered in a hash table.
58 * This ensures that a full path search will not have to be done for them
59 * on each invocation.
60 *
61 * We should investigate converting to a linear search, even though that
62 * would make the command name "hash" a misnomer.
63 */
64
65#include "shell.h"
66#include "main.h"
67#include "nodes.h"
68#include "parser.h"
69#include "redir.h"
70#include "eval.h"
71#include "exec.h"
72#include "builtins.h"
73#include "var.h"
74#include "options.h"
75#include "input.h"
76#include "output.h"
77#include "syntax.h"
78#include "memalloc.h"
79#include "error.h"
80#include "init.h"
81#include "mystring.h"
82#include "show.h"
83#include "jobs.h"
84#include "alias.h"
85
86
87#define CMDTABLESIZE 31 /* should be prime */
88#define ARB 1 /* actual size determined at run time */
89
90
91
92struct tblentry {
93 struct tblentry *next; /* next entry in hash chain */
94 union param param; /* definition of builtin function */
95 short cmdtype; /* index identifying command */
96 char rehash; /* if set, cd done since entry created */
97 char cmdname[ARB]; /* name of command */
98};
99
100
101STATIC struct tblentry *cmdtable[CMDTABLESIZE];
102STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */
103int exerrno = 0; /* Last exec error */
104
105
106STATIC void tryexec (char *, char **, char **);
107STATIC void execinterp (char **, char **);
108STATIC void printentry (struct tblentry *, int);
109STATIC void clearcmdentry (int);
110STATIC struct tblentry *cmdlookup (char *, int);
111STATIC void delete_cmd_entry (void);
112STATIC int describe_command (char *, int);
113STATIC int path_change (const char *, int *);
114STATIC int is_regular_builtin (const char *);
115
116
117
118/*
119 * Exec a program. Never returns. If you change this routine, you may
120 * have to change the find_command routine as well.
121 */
122
123void
124shellexec(argv, envp, path, idx)
125 char **argv, **envp;
126 const char *path;
127 int idx;
128{
129 char *cmdname;
130 int e;
131
132 if (fd2 >= 0 && fd2 != 2) {
133 close(fd2);
134 }
135
136 if (strchr(argv[0], '/') != NULL) {
137 tryexec(argv[0], argv, envp);
138 e = errno;
139 } else {
140 e = ENOENT;
141 while ((cmdname = padvance(&path, argv[0])) != NULL) {
142 if (--idx < 0 && pathopt == NULL) {
143 tryexec(cmdname, argv, envp);
144 if (errno != ENOENT && errno != ENOTDIR)
145 e = errno;
146 }
147 stunalloc(cmdname);
148 }
149 }
150
151 /* Map to POSIX errors */
152 switch (e) {
153 case EACCES:
154 exerrno = 126;
155 break;
156 case ENOENT:
157 exerrno = 127;
158 break;
159 default:
160 exerrno = 2;
161 break;
162 }
163 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
164 /* NOTREACHED */
165}
166
167
168STATIC void
169tryexec(cmd, argv, envp)
170 char *cmd;
171 char **argv;
172 char **envp;
173 {
174 int e;
175#if !defined(BSD) && !defined(linux)
176 char *p;
177#endif
178
179#ifdef SYSV
180 do {
181 execve(cmd, argv, envp);
182 } while (errno == EINTR);
183#else
184 execve(cmd, argv, envp);
185#endif
186 e = errno;
187 if (e == ENOEXEC) {
188 initshellproc();
189 setinputfile(cmd, 0);
190 commandname = arg0 = savestr(argv[0]);
191#if !defined(BSD) && !defined(linux)
192 pgetc(); pungetc(); /* fill up input buffer */
193 p = parsenextc;
194 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
195 argv[0] = cmd;
196 execinterp(argv, envp);
197 }
198#endif
199 setparam(argv + 1);
200 exraise(EXSHELLPROC);
201 }
202 errno = e;
203}
204
205
206#if !defined(BSD) && !defined(linux)
207/*
208 * Execute an interpreter introduced by "#!", for systems where this
209 * feature has not been built into the kernel. If the interpreter is
210 * the shell, return (effectively ignoring the "#!"). If the execution
211 * of the interpreter fails, exit.
212 *
213 * This code peeks inside the input buffer in order to avoid actually
214 * reading any input. It would benefit from a rewrite.
215 */
216
217#define NEWARGS 5
218
219STATIC void
220execinterp(argv, envp)
221 char **argv, **envp;
222 {
223 int n;
224 char *inp;
225 char *outp;
226 char c;
227 char *p;
228 char **ap;
229 char *newargs[NEWARGS];
230 int i;
231 char **ap2;
232 char **new;
233
234 n = parsenleft - 2;
235 inp = parsenextc + 2;
236 ap = newargs;
237 for (;;) {
238 while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
239 inp++;
240 if (n < 0)
241 goto bad;
242 if ((c = *inp++) == '\n')
243 break;
244 if (ap == &newargs[NEWARGS])
245bad: error("Bad #! line");
246 STARTSTACKSTR(outp);
247 do {
248 STPUTC(c, outp);
249 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
250 STPUTC('\0', outp);
251 n++, inp--;
252 *ap++ = grabstackstr(outp);
253 }
254 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
255 p = newargs[0];
256 for (;;) {
257 if (equal(p, "sh") || equal(p, "ash")) {
258 return;
259 }
260 while (*p != '/') {
261 if (*p == '\0')
262 goto break2;
263 p++;
264 }
265 p++;
266 }
267break2:;
268 }
269 i = (char *)ap - (char *)newargs; /* size in bytes */
270 if (i == 0)
271 error("Bad #! line");
272 for (ap2 = argv ; *ap2++ != NULL ; );
273 new = ckmalloc(i + ((char *)ap2 - (char *)argv));
274 ap = newargs, ap2 = new;
275 while ((i -= sizeof (char **)) >= 0)
276 *ap2++ = *ap++;
277 ap = argv;
278 while (*ap2++ = *ap++);
279 shellexec(new, envp, pathval(), 0);
280 /* NOTREACHED */
281}
282#endif
283
284
285
286/*
287 * Do a path search. The variable path (passed by reference) should be
288 * set to the start of the path before the first call; padvance will update
289 * this value as it proceeds. Successive calls to padvance will return
290 * the possible path expansions in sequence. If an option (indicated by
291 * a percent sign) appears in the path entry then the global variable
292 * pathopt will be set to point to it; otherwise pathopt will be set to
293 * NULL.
294 */
295
296const char *pathopt;
297
298char *
299padvance(path, name)
300 const char **path;
301 const char *name;
302 {
303 const char *p;
304 char *q;
305 const char *start;
306 int len;
307
308 if (*path == NULL)
309 return NULL;
310 start = *path;
311 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
312 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
313 while (stackblocksize() < len)
314 growstackblock();
315 q = stackblock();
316 if (p != start) {
317 memcpy(q, start, p - start);
318 q += p - start;
319 *q++ = '/';
320 }
321 strcpy(q, name);
322 pathopt = NULL;
323 if (*p == '%') {
324 pathopt = ++p;
325 while (*p && *p != ':') p++;
326 }
327 if (*p == ':')
328 *path = p + 1;
329 else
330 *path = NULL;
331 return stalloc(len);
332}
333
334
335
336/*** Command hashing code ***/
337
338
339int
340hashcmd(argc, argv)
341 int argc;
342 char **argv;
343{
344 struct tblentry **pp;
345 struct tblentry *cmdp;
346 int c;
347 int verbose;
348 struct cmdentry entry;
349 char *name;
350
351 verbose = 0;
352 while ((c = nextopt("rv")) != '\0') {
353 if (c == 'r') {
354 clearcmdentry(0);
355 } else if (c == 'v') {
356 verbose++;
357 }
358 }
359 if (*argptr == NULL) {
360 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
361 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
362 if (cmdp->cmdtype != CMDBUILTIN) {
363 printentry(cmdp, verbose);
364 }
365 }
366 }
367 return 0;
368 }
369 c = 0;
370 while ((name = *argptr) != NULL) {
371 if ((cmdp = cmdlookup(name, 0)) != NULL
372 && (cmdp->cmdtype == CMDNORMAL
373 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
374 delete_cmd_entry();
375 find_command(name, &entry, DO_ERR, pathval());
376 if (entry.cmdtype == CMDUNKNOWN) c = 1;
377 else if (verbose) {
378 cmdp = cmdlookup(name, 0);
379 if (cmdp) printentry(cmdp, verbose);
380 flushall();
381 }
382 argptr++;
383 }
384 return c;
385}
386
387
388STATIC void
389printentry(cmdp, verbose)
390 struct tblentry *cmdp;
391 int verbose;
392 {
393 int idx;
394 const char *path;
395 char *name;
396
397 if (cmdp->cmdtype == CMDNORMAL) {
398 idx = cmdp->param.index;
399 path = pathval();
400 do {
401 name = padvance(&path, cmdp->cmdname);
402 stunalloc(name);
403 } while (--idx >= 0);
404 out1str(name);
405 } else if (cmdp->cmdtype == CMDBUILTIN) {
406 out1fmt("builtin %s", cmdp->cmdname);
407 } else if (cmdp->cmdtype == CMDFUNCTION) {
408 out1fmt("function %s", cmdp->cmdname);
409 if (verbose) {
410 INTOFF;
411 name = commandtext(cmdp->param.func);
412 out1c(' ');
413 out1str(name);
414 ckfree(name);
415 INTON;
416 }
417#ifdef DEBUG
418 } else {
419 error("internal error: cmdtype %d", cmdp->cmdtype);
420#endif
421 }
422 if (cmdp->rehash)
423 out1c('*');
424 out1c('\n');
425}
426
427
428
429/*
430 * Resolve a command name. If you change this routine, you may have to
431 * change the shellexec routine as well.
432 */
433
434void
435find_command(name, entry, act, path)
436 char *name;
437 struct cmdentry *entry;
438 int act;
439 const char *path;
440{
441 struct tblentry *cmdp;
442 int idx;
443 int prev;
444 char *fullname;
445 struct stat statb;
446 int e;
447 int i;
448 int bltin;
449 int firstchange;
450 int updatetbl;
451 int regular;
452
453 /* If name contains a slash, don't use the hash table */
454 if (strchr(name, '/') != NULL) {
455 if (act & DO_ABS) {
456 while (stat(name, &statb) < 0) {
457 #ifdef SYSV
458 if (errno == EINTR)
459 continue;
460 #endif
461 if (errno != ENOENT && errno != ENOTDIR)
462 e = errno;
463 entry->cmdtype = CMDUNKNOWN;
464 entry->u.index = -1;
465 return;
466 }
467 entry->cmdtype = CMDNORMAL;
468 entry->u.index = -1;
469 return;
470 }
471 entry->cmdtype = CMDNORMAL;
472 entry->u.index = 0;
473 return;
474 }
475
476 updatetbl = 1;
477 if (act & DO_BRUTE) {
478 firstchange = path_change(path, &bltin);
479 } else {
480 bltin = builtinloc;
481 firstchange = 9999;
482 }
483
484 /* If name is in the table, and not invalidated by cd, we're done */
485 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
486 if (cmdp->cmdtype == CMDFUNCTION) {
487 if (act & DO_NOFUN) {
488 updatetbl = 0;
489 } else {
490 goto success;
491 }
492 } else if (act & DO_BRUTE) {
493 if ((cmdp->cmdtype == CMDNORMAL &&
494 cmdp->param.index >= firstchange) ||
495 (cmdp->cmdtype == CMDBUILTIN &&
496 ((builtinloc < 0 && bltin >= 0) ?
497 bltin : builtinloc) >= firstchange)) {
498 /* need to recompute the entry */
499 } else {
500 goto success;
501 }
502 } else {
503 goto success;
504 }
505 }
506
507 if ((regular = is_regular_builtin(name))) {
508 if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
509 goto success;
510 }
511 } else if (act & DO_BRUTE) {
512 if (firstchange == 0) {
513 updatetbl = 0;
514 }
515 }
516
517 /* If %builtin not in path, check for builtin next */
518 if ((bltin < 0 || regular) && (i = find_builtin(name)) >= 0) {
519 if (!updatetbl) {
520 entry->cmdtype = CMDBUILTIN;
521 entry->u.index = i;
522 return;
523 }
524 INTOFF;
525 cmdp = cmdlookup(name, 1);
526 cmdp->cmdtype = CMDBUILTIN;
527 cmdp->param.index = i;
528 INTON;
529 goto success;
530 }
531
532 /* We have to search path. */
533 prev = -1; /* where to start */
534 if (cmdp && cmdp->rehash) { /* doing a rehash */
535 if (cmdp->cmdtype == CMDBUILTIN)
536 prev = builtinloc;
537 else
538 prev = cmdp->param.index;
539 }
540
541 e = ENOENT;
542 idx = -1;
543loop:
544 while ((fullname = padvance(&path, name)) != NULL) {
545 stunalloc(fullname);
546 idx++;
547 if (idx >= firstchange) {
548 updatetbl = 0;
549 }
550 if (pathopt) {
551 if (prefix("builtin", pathopt)) {
552 if ((i = find_builtin(name)) >= 0) {
553 if (!updatetbl) {
554 entry->cmdtype = CMDBUILTIN;
555 entry->u.index = i;
556 return;
557 }
558 INTOFF;
559 cmdp = cmdlookup(name, 1);
560 cmdp->cmdtype = CMDBUILTIN;
561 cmdp->param.index = i;
562 INTON;
563 goto success;
564 } else {
565 continue;
566 }
567 } else if (!(act & DO_NOFUN) &&
568 prefix("func", pathopt)) {
569 /* handled below */
570 } else {
571 continue; /* ignore unimplemented options */
572 }
573 }
574 /* if rehash, don't redo absolute path names */
575 if (fullname[0] == '/' && idx <= prev &&
576 idx < firstchange) {
577 if (idx < prev)
578 continue;
579 TRACE(("searchexec \"%s\": no change\n", name));
580 goto success;
581 }
582 while (stat(fullname, &statb) < 0) {
583#ifdef SYSV
584 if (errno == EINTR)
585 continue;
586#endif
587 if (errno != ENOENT && errno != ENOTDIR)
588 e = errno;
589 goto loop;
590 }
591 e = EACCES; /* if we fail, this will be the error */
592 if (!S_ISREG(statb.st_mode))
593 continue;
594 if (pathopt) { /* this is a %func directory */
595 stalloc(strlen(fullname) + 1);
596 readcmdfile(fullname);
597 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
598 error("%s not defined in %s", name, fullname);
599 stunalloc(fullname);
600 goto success;
601 }
602#ifdef notdef
603 if (statb.st_uid == geteuid()) {
604 if ((statb.st_mode & 0100) == 0)
605 goto loop;
606 } else if (statb.st_gid == getegid()) {
607 if ((statb.st_mode & 010) == 0)
608 goto loop;
609 } else {
610 if ((statb.st_mode & 01) == 0)
611 goto loop;
612 }
613#endif
614 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
615 /* If we aren't called with DO_BRUTE and cmdp is set, it must
616 be a function and we're being called with DO_NOFUN */
617 if (!updatetbl) {
618 entry->cmdtype = CMDNORMAL;
619 entry->u.index = idx;
620 return;
621 }
622 INTOFF;
623 cmdp = cmdlookup(name, 1);
624 cmdp->cmdtype = CMDNORMAL;
625 cmdp->param.index = idx;
626 INTON;
627 goto success;
628 }
629
630 /* We failed. If there was an entry for this command, delete it */
631 if (cmdp && updatetbl)
632 delete_cmd_entry();
633 if (act & DO_ERR)
634 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
635 entry->cmdtype = CMDUNKNOWN;
636 return;
637
638success:
639 cmdp->rehash = 0;
640 entry->cmdtype = cmdp->cmdtype;
641 entry->u = cmdp->param;
642}
643
644
645
646/*
647 * Search the table of builtin commands.
648 */
649
650int
651find_builtin(name)
652 char *name;
653{
654 const struct builtincmd *bp;
655
656 for (bp = builtincmd ; bp->name ; bp++) {
657 if (*bp->name == *name && equal(bp->name, name))
658 return bp->code;
659 }
660 return -1;
661}
662
663
664
665/*
666 * Called when a cd is done. Marks all commands so the next time they
667 * are executed they will be rehashed.
668 */
669
670void
671hashcd() {
672 struct tblentry **pp;
673 struct tblentry *cmdp;
674
675 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
676 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
677 if (cmdp->cmdtype == CMDNORMAL
678 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
679 cmdp->rehash = 1;
680 }
681 }
682}
683
684
685
686/*
687 * Called before PATH is changed. The argument is the new value of PATH;
688 * pathval() still returns the old value at this point. Called with
689 * interrupts off.
690 */
691
692void
693changepath(newval)
694 const char *newval;
695{
696 int firstchange;
697 int bltin;
698
699 firstchange = path_change(newval, &bltin);
700 if (builtinloc < 0 && bltin >= 0)
701 builtinloc = bltin; /* zap builtins */
702 clearcmdentry(firstchange);
703 builtinloc = bltin;
704}
705
706
707/*
708 * Clear out command entries. The argument specifies the first entry in
709 * PATH which has changed.
710 */
711
712STATIC void
713clearcmdentry(firstchange)
714 int firstchange;
715{
716 struct tblentry **tblp;
717 struct tblentry **pp;
718 struct tblentry *cmdp;
719
720 INTOFF;
721 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
722 pp = tblp;
723 while ((cmdp = *pp) != NULL) {
724 if ((cmdp->cmdtype == CMDNORMAL &&
725 cmdp->param.index >= firstchange)
726 || (cmdp->cmdtype == CMDBUILTIN &&
727 builtinloc >= firstchange)) {
728 *pp = cmdp->next;
729 ckfree(cmdp);
730 } else {
731 pp = &cmdp->next;
732 }
733 }
734 }
735 INTON;
736}
737
738
739/*
740 * Delete all functions.
741 */
742
743#ifdef mkinit
744MKINIT void deletefuncs (void);
745
746SHELLPROC {
747 deletefuncs();
748}
749#endif
750
751void
752deletefuncs() {
753 struct tblentry **tblp;
754 struct tblentry **pp;
755 struct tblentry *cmdp;
756
757 INTOFF;
758 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
759 pp = tblp;
760 while ((cmdp = *pp) != NULL) {
761 if (cmdp->cmdtype == CMDFUNCTION) {
762 *pp = cmdp->next;
763 freefunc(cmdp->param.func);
764 ckfree(cmdp);
765 } else {
766 pp = &cmdp->next;
767 }
768 }
769 }
770 INTON;
771}
772
773
774
775/*
776 * Locate a command in the command hash table. If "add" is nonzero,
777 * add the command to the table if it is not already present. The
778 * variable "lastcmdentry" is set to point to the address of the link
779 * pointing to the entry, so that delete_cmd_entry can delete the
780 * entry.
781 */
782
783struct tblentry **lastcmdentry;
784
785
786STATIC struct tblentry *
787cmdlookup(name, add)
788 char *name;
789 int add;
790{
791 int hashval;
792 char *p;
793 struct tblentry *cmdp;
794 struct tblentry **pp;
795
796 p = name;
797 hashval = *p << 4;
798 while (*p)
799 hashval += *p++;
800 hashval &= 0x7FFF;
801 pp = &cmdtable[hashval % CMDTABLESIZE];
802 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
803 if (equal(cmdp->cmdname, name))
804 break;
805 pp = &cmdp->next;
806 }
807 if (add && cmdp == NULL) {
808 INTOFF;
809 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
810 + strlen(name) + 1);
811 cmdp->next = NULL;
812 cmdp->cmdtype = CMDUNKNOWN;
813 cmdp->rehash = 0;
814 strcpy(cmdp->cmdname, name);
815 INTON;
816 }
817 lastcmdentry = pp;
818 return cmdp;
819}
820
821/*
822 * Delete the command entry returned on the last lookup.
823 */
824
825STATIC void
826delete_cmd_entry() {
827 struct tblentry *cmdp;
828
829 INTOFF;
830 cmdp = *lastcmdentry;
831 *lastcmdentry = cmdp->next;
832 ckfree(cmdp);
833 INTON;
834}
835
836
837
838#ifdef notdef
839void
840getcmdentry(name, entry)
841 char *name;
842 struct cmdentry *entry;
843 {
844 struct tblentry *cmdp = cmdlookup(name, 0);
845
846 if (cmdp) {
847 entry->u = cmdp->param;
848 entry->cmdtype = cmdp->cmdtype;
849 } else {
850 entry->cmdtype = CMDUNKNOWN;
851 entry->u.index = 0;
852 }
853}
854#endif
855
856
857/*
858 * Add a new command entry, replacing any existing command entry for
859 * the same name.
860 */
861
862void
863addcmdentry(name, entry)
864 char *name;
865 struct cmdentry *entry;
866 {
867 struct tblentry *cmdp;
868
869 INTOFF;
870 cmdp = cmdlookup(name, 1);
871 if (cmdp->cmdtype == CMDFUNCTION) {
872 freefunc(cmdp->param.func);
873 }
874 cmdp->cmdtype = entry->cmdtype;
875 cmdp->param = entry->u;
876 INTON;
877}
878
879
880/*
881 * Define a shell function.
882 */
883
884void
885defun(name, func)
886 char *name;
887 union node *func;
888 {
889 struct cmdentry entry;
890
891 entry.cmdtype = CMDFUNCTION;
892 entry.u.func = copyfunc(func);
893 addcmdentry(name, &entry);
894}
895
896
897/*
898 * Delete a function if it exists.
899 */
900
901int
902unsetfunc(name)
903 char *name;
904 {
905 struct tblentry *cmdp;
906
907 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
908 freefunc(cmdp->param.func);
909 delete_cmd_entry();
910 return (0);
911 }
912 return (1);
913}
914
915/*
916 * Locate and print what a word is...
917 */
918
919int
920typecmd(argc, argv)
921 int argc;
922 char **argv;
923{
924 struct cmdentry entry;
925 struct tblentry *cmdp;
926 char * const *pp;
927 struct alias *ap;
928 int i;
929 int err = 0;
930 extern char *const parsekwd[];
931
932 for (i = 1; i < argc; i++) {
933 out1str(argv[i]);
934 /* First look at the keywords */
935 for (pp = parsekwd; *pp; pp++)
936 if (**pp == *argv[i] && equal(*pp, argv[i]))
937 break;
938
939 if (*pp) {
940 out1str(" is a shell keyword\n");
941 continue;
942 }
943
944 /* Then look at the aliases */
945 if ((ap = lookupalias(argv[i], 1)) != NULL) {
946 out1fmt(" is an alias for %s\n", ap->val);
947 continue;
948 }
949
950 /* Then check if it is a tracked alias */
951 if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
952 entry.cmdtype = cmdp->cmdtype;
953 entry.u = cmdp->param;
954 }
955 else {
956 /* Finally use brute force */
957 find_command(argv[i], &entry, DO_ABS, pathval());
958 }
959
960 switch (entry.cmdtype) {
961 case CMDNORMAL: {
962 if (strchr(argv[i], '/') == NULL) {
963 const char *path = pathval();
964 char *name;
965 int j = entry.u.index;
966 do {
967 name = padvance(&path, argv[i]);
968 stunalloc(name);
969 } while (--j >= 0);
970 out1fmt(" is%s %s\n",
971 cmdp ? " a tracked alias for" : "", name);
972 } else {
973 if (access(argv[i], X_OK) == 0)
974 out1fmt(" is %s\n", argv[i]);
975 else
976 out1fmt(": %s\n", strerror(errno));
977 }
978 break;
979 }
980 case CMDFUNCTION:
981 out1str(" is a shell function\n");
982 break;
983
984 case CMDBUILTIN:
985 out1str(" is a shell builtin\n");
986 break;
987
988 default:
989 out1str(": not found\n");
990 err |= 127;
991 break;
992 }
993 }
994 return err;
995}
996
997STATIC int
998describe_command(command, verbose)
999 char *command;
1000 int verbose;
1001{
1002 struct cmdentry entry;
1003 struct tblentry *cmdp;
1004 char **pp;
1005 struct alias *ap;
1006 extern char *const parsekwd[];
1007
1008 for (pp = (char **)parsekwd; *pp; pp++)
1009 if (**pp == *command && equal(*pp, command))
1010 break;
1011
1012 if (*pp) {
1013 if (verbose) {
1014 out1fmt("%s is a reserved word\n", command);
1015 } else {
1016 out1fmt("%s\n", command);
1017 }
1018 return 0;
1019 }
1020
1021 /* Then look at the aliases */
1022 if ((ap = lookupalias(command, 1)) != NULL) {
1023 if (verbose) {
1024 out1fmt("%s is aliased to `%s'\n", command, ap->val);
1025 } else {
1026 out1fmt("alias %s='%s'\n", command, ap->val);
1027 }
1028 return 0;
1029 }
1030
1031 /* Then check if it is a tracked alias */
1032 if ((cmdp = cmdlookup(command, 0)) != NULL) {
1033 entry.cmdtype = cmdp->cmdtype;
1034 entry.u = cmdp->param;
1035 }
1036 else {
1037 /* Finally use brute force */
1038 find_command(command, &entry, DO_ABS, pathval());
1039 }
1040
1041 switch (entry.cmdtype) {
1042 case CMDNORMAL: {
1043 int j = entry.u.index;
1044 const char *path = pathval();
1045 char *name;
1046 if (j == -1)
1047 name = command;
1048 else {
1049 do {
1050 name = padvance(&path, command);
1051 stunalloc(name);
1052 } while (--j >= 0);
1053 }
1054 if (verbose) {
1055 out1fmt("%s is %s\n", command, name);
1056 } else {
1057 out1fmt("%s\n", name);
1058 }
1059 break;
1060 }
1061 case CMDFUNCTION:
1062 if (verbose) {
1063 out1fmt("%s is a function\n", command);
1064 } else {
1065 out1fmt("%s\n", command);
1066 }
1067 break;
1068 case CMDBUILTIN:
1069 if (verbose) {
1070 if (is_special_builtin(command)) {
1071 out1fmt("%s is a special built-in utility\n", command);
1072 } else {
1073 out1fmt("%s is a built-in utility\n", command);
1074 }
1075 } else {
1076 out1fmt("%s\n", command);
1077 }
1078 break;
1079 default:
1080 outfmt(out2, "%s not found\n", command);
1081 return 127;
1082 }
1083
1084 return 0;
1085}
1086
1087int
1088commandcmd(argc, argv)
1089 int argc;
1090 char **argv;
1091{
1092 int c;
1093 int default_path = 0;
1094 int verify_only = 0;
1095 int verbose_verify_only = 0;
1096
1097 while ((c = nextopt("pvV")) != '\0')
1098 switch (c) {
1099 case 'p':
1100 default_path = 1;
1101 break;
1102 case 'v':
1103 verify_only = 1;
1104 break;
1105 case 'V':
1106 verbose_verify_only = 1;
1107 break;
1108 default:
1109 outfmt(out2,
1110"command: nextopt returned character code 0%o\n", c);
1111 return EX_SOFTWARE;
1112 }
1113
1114 if (default_path + verify_only + verbose_verify_only > 1 ||
1115 !*argptr) {
1116 outfmt(out2,
1117"command [-p] command [arg ...]\n");
1118 outfmt(out2,
1119"command {-v|-V} command\n");
1120 return EX_USAGE;
1121 }
1122
1123 if (verify_only || verbose_verify_only) {
1124 return describe_command(*argptr, verbose_verify_only);
1125 }
1126
1127 return 0;
1128}
1129
1130STATIC int
1131path_change(newval, bltin)
1132 const char *newval;
1133 int *bltin;
1134{
1135 const char *old, *new;
1136 int idx;
1137 int firstchange;
1138
1139 old = pathval();
1140 new = newval;
1141 firstchange = 9999; /* assume no change */
1142 idx = 0;
1143 *bltin = -1;
1144 for (;;) {
1145 if (*old != *new) {
1146 firstchange = idx;
1147 if ((*old == '\0' && *new == ':')
1148 || (*old == ':' && *new == '\0'))
1149 firstchange++;
1150 old = new; /* ignore subsequent differences */
1151 }
1152 if (*new == '\0')
1153 break;
1154 if (*new == '%' && *bltin < 0 && prefix("builtin", new + 1))
1155 *bltin = idx;
1156 if (*new == ':') {
1157 idx++;
1158 }
1159 new++, old++;
1160 }
1161 if (builtinloc >= 0 && *bltin < 0)
1162 firstchange = 0;
1163 return firstchange;
1164}
1165
1166STATIC int
1167is_regular_builtin(name)
1168 const char *name;
1169{
1170 static const char *regular_builtins[] = {
1171 "alias", "bg", "cd", "command", "false", "fc", "fg",
1172 "getopts", "jobs", "kill", "newgrp", "read", "true",
1173 "umask", "unalias", "wait", (char *)NULL
1174 };
1175 int i;
1176
1177 if (!name) return 0;
1178 for (i = 0; regular_builtins[i]; i++)
1179 if (equal(name, regular_builtins[i])) return 1;
1180 return 0;
1181}
Note: See TracBrowser for help on using the repository browser.