sh: Make 'hash' return 1 if at least one utility is not found.
[dragonfly.git] / bin / sh / exec.c
CommitLineData
984263bc
MD
1/*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
1de703da
MD
35 *
36 * @(#)exec.c 8.4 (Berkeley) 6/8/95
8bb2400d 37 * $FreeBSD: src/bin/sh/exec.c,v 1.55 2012/02/11 21:06:45 jilles Exp $
984263bc
MD
38 */
39
984263bc
MD
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <unistd.h>
43#include <fcntl.h>
44#include <errno.h>
99512ac4 45#include <paths.h>
984263bc
MD
46#include <stdlib.h>
47
48/*
49 * When commands are first encountered, they are entered in a hash table.
50 * This ensures that a full path search will not have to be done for them
51 * on each invocation.
52 *
53 * We should investigate converting to a linear search, even though that
54 * would make the command name "hash" a misnomer.
55 */
56
57#include "shell.h"
58#include "main.h"
59#include "nodes.h"
60#include "parser.h"
61#include "redir.h"
62#include "eval.h"
63#include "exec.h"
64#include "builtins.h"
65#include "var.h"
66#include "options.h"
67#include "input.h"
68#include "output.h"
69#include "syntax.h"
70#include "memalloc.h"
71#include "error.h"
72#include "init.h"
73#include "mystring.h"
74#include "show.h"
75#include "jobs.h"
76#include "alias.h"
77
78
79#define CMDTABLESIZE 31 /* should be prime */
80#define ARB 1 /* actual size determined at run time */
81
82
83
84struct tblentry {
85 struct tblentry *next; /* next entry in hash chain */
86 union param param; /* definition of builtin function */
0d5aaed6 87 int special; /* flag for special builtin commands */
984263bc
MD
88 short cmdtype; /* index identifying command */
89 char rehash; /* if set, cd done since entry created */
90 char cmdname[ARB]; /* name of command */
91};
92
93
99512ac4 94static struct tblentry *cmdtable[CMDTABLESIZE];
984263bc
MD
95int exerrno = 0; /* Last exec error */
96
97
99512ac4
PA
98static void tryexec(char *, char **, char **);
99static void printentry(struct tblentry *, int);
100static struct tblentry *cmdlookup(const char *, int);
101static void delete_cmd_entry(void);
b6bd99f6 102static void addcmdentry(const char *, struct cmdentry *);
984263bc
MD
103
104/*
105 * Exec a program. Never returns. If you change this routine, you may
106 * have to change the find_command routine as well.
99512ac4
PA
107 *
108 * The argv array may be changed and element argv[-1] should be writable.
984263bc
MD
109 */
110
111void
934863d7 112shellexec(char **argv, char **envp, const char *path, int idx)
984263bc
MD
113{
114 char *cmdname;
115 int e;
116
117 if (strchr(argv[0], '/') != NULL) {
118 tryexec(argv[0], argv, envp);
119 e = errno;
120 } else {
121 e = ENOENT;
122 while ((cmdname = padvance(&path, argv[0])) != NULL) {
934863d7 123 if (--idx < 0 && pathopt == NULL) {
984263bc
MD
124 tryexec(cmdname, argv, envp);
125 if (errno != ENOENT && errno != ENOTDIR)
126 e = errno;
99512ac4
PA
127 if (e == ENOEXEC)
128 break;
984263bc
MD
129 }
130 stunalloc(cmdname);
131 }
132 }
133
134 /* Map to POSIX errors */
99512ac4 135 if (e == ENOENT || e == ENOTDIR) {
984263bc 136 exerrno = 127;
010f7689 137 exerror(EXEXEC, "%s: not found", argv[0]);
99512ac4
PA
138 } else {
139 exerrno = 126;
140 exerror(EXEXEC, "%s: %s", argv[0], strerror(e));
141 }
984263bc
MD
142}
143
144
99512ac4 145static void
984263bc
MD
146tryexec(char *cmd, char **argv, char **envp)
147{
99512ac4
PA
148 int e, in;
149 ssize_t n;
150 char buf[256];
984263bc
MD
151
152 execve(cmd, argv, envp);
153 e = errno;
154 if (e == ENOEXEC) {
99512ac4
PA
155 INTOFF;
156 in = open(cmd, O_RDONLY | O_NONBLOCK);
157 if (in != -1) {
158 n = pread(in, buf, sizeof buf, 0);
159 close(in);
160 if (n > 0 && memchr(buf, '\0', n) != NULL) {
161 errno = ENOEXEC;
162 return;
163 }
164 }
165 *argv = cmd;
166 *--argv = __DECONST(char *, _PATH_BSHELL);
167 execve(_PATH_BSHELL, argv, envp);
984263bc
MD
168 }
169 errno = e;
170}
171
172/*
173 * Do a path search. The variable path (passed by reference) should be
174 * set to the start of the path before the first call; padvance will update
175 * this value as it proceeds. Successive calls to padvance will return
176 * the possible path expansions in sequence. If an option (indicated by
177 * a percent sign) appears in the path entry then the global variable
178 * pathopt will be set to point to it; otherwise pathopt will be set to
179 * NULL.
180 */
181
492efe05 182const char *pathopt;
984263bc
MD
183
184char *
492efe05 185padvance(const char **path, const char *name)
984263bc 186{
99512ac4 187 const char *p, *start;
492efe05 188 char *q;
984263bc
MD
189 int len;
190
191 if (*path == NULL)
192 return NULL;
193 start = *path;
99512ac4
PA
194 for (p = start; *p && *p != ':' && *p != '%'; p++)
195 ; /* nothing */
984263bc 196 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
99512ac4
PA
197 STARTSTACKSTR(q);
198 CHECKSTRSPACE(len, q);
984263bc
MD
199 if (p != start) {
200 memcpy(q, start, p - start);
201 q += p - start;
202 *q++ = '/';
203 }
204 strcpy(q, name);
205 pathopt = NULL;
206 if (*p == '%') {
207 pathopt = ++p;
208 while (*p && *p != ':') p++;
209 }
210 if (*p == ':')
211 *path = p + 1;
212 else
213 *path = NULL;
214 return stalloc(len);
215}
216
217
218
219/*** Command hashing code ***/
220
221
222int
223hashcmd(int argc __unused, char **argv __unused)
224{
225 struct tblentry **pp;
226 struct tblentry *cmdp;
227 int c;
228 int verbose;
229 struct cmdentry entry;
230 char *name;
8bb2400d 231 int errors;
984263bc 232
8bb2400d 233 errors = 0;
984263bc
MD
234 verbose = 0;
235 while ((c = nextopt("rv")) != '\0') {
236 if (c == 'r') {
99512ac4 237 clearcmdentry();
984263bc
MD
238 } else if (c == 'v') {
239 verbose++;
240 }
241 }
242 if (*argptr == NULL) {
243 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
244 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
245 if (cmdp->cmdtype == CMDNORMAL)
246 printentry(cmdp, verbose);
247 }
248 }
249 return 0;
250 }
251 while ((name = *argptr) != NULL) {
252 if ((cmdp = cmdlookup(name, 0)) != NULL
99512ac4 253 && cmdp->cmdtype == CMDNORMAL)
984263bc 254 delete_cmd_entry();
99512ac4 255 find_command(name, &entry, DO_ERR, pathval());
8bb2400d
PA
256 if (entry.cmdtype == CMDUNKNOWN)
257 errors = 1;
258 else if (verbose) {
259 cmdp = cmdlookup(name, 0);
260 if (cmdp != NULL)
261 printentry(cmdp, verbose);
262 else {
263 outfmt(out2, "%s: not found\n", name);
264 errors = 1;
984263bc
MD
265 }
266 flushall();
267 }
268 argptr++;
269 }
8bb2400d 270 return errors;
984263bc
MD
271}
272
273
99512ac4 274static void
984263bc
MD
275printentry(struct tblentry *cmdp, int verbose)
276{
934863d7 277 int idx;
492efe05 278 const char *path;
984263bc
MD
279 char *name;
280
281 if (cmdp->cmdtype == CMDNORMAL) {
934863d7 282 idx = cmdp->param.index;
984263bc
MD
283 path = pathval();
284 do {
285 name = padvance(&path, cmdp->cmdname);
286 stunalloc(name);
934863d7 287 } while (--idx >= 0);
984263bc
MD
288 out1str(name);
289 } else if (cmdp->cmdtype == CMDBUILTIN) {
290 out1fmt("builtin %s", cmdp->cmdname);
291 } else if (cmdp->cmdtype == CMDFUNCTION) {
292 out1fmt("function %s", cmdp->cmdname);
293 if (verbose) {
294 INTOFF;
99512ac4 295 name = commandtext(getfuncnode(cmdp->param.func));
984263bc
MD
296 out1c(' ');
297 out1str(name);
298 ckfree(name);
299 INTON;
300 }
301#ifdef DEBUG
302 } else {
303 error("internal error: cmdtype %d", cmdp->cmdtype);
304#endif
305 }
306 if (cmdp->rehash)
307 out1c('*');
308 out1c('\n');
309}
310
311
312
313/*
314 * Resolve a command name. If you change this routine, you may have to
315 * change the shellexec routine as well.
316 */
317
318void
99512ac4
PA
319find_command(const char *name, struct cmdentry *entry, int act,
320 const char *path)
984263bc 321{
99512ac4 322 struct tblentry *cmdp, loc_cmd;
934863d7 323 int idx;
984263bc
MD
324 int prev;
325 char *fullname;
326 struct stat statb;
327 int e;
328 int i;
0d5aaed6 329 int spec;
984263bc
MD
330
331 /* If name contains a slash, don't use the hash table */
332 if (strchr(name, '/') != NULL) {
333 entry->cmdtype = CMDNORMAL;
334 entry->u.index = 0;
335 return;
336 }
337
338 /* If name is in the table, and not invalidated by cd, we're done */
99512ac4
PA
339 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
340 if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
341 cmdp = NULL;
342 else
343 goto success;
344 }
984263bc 345
99512ac4
PA
346 /* Check for builtin next */
347 if ((i = find_builtin(name, &spec)) >= 0) {
984263bc
MD
348 INTOFF;
349 cmdp = cmdlookup(name, 1);
99512ac4
PA
350 if (cmdp->cmdtype == CMDFUNCTION)
351 cmdp = &loc_cmd;
984263bc
MD
352 cmdp->cmdtype = CMDBUILTIN;
353 cmdp->param.index = i;
0d5aaed6 354 cmdp->special = spec;
984263bc
MD
355 INTON;
356 goto success;
357 }
358
359 /* We have to search path. */
360 prev = -1; /* where to start */
361 if (cmdp) { /* doing a rehash */
362 if (cmdp->cmdtype == CMDBUILTIN)
99512ac4 363 prev = -1;
984263bc
MD
364 else
365 prev = cmdp->param.index;
366 }
367
368 e = ENOENT;
934863d7 369 idx = -1;
984263bc
MD
370loop:
371 while ((fullname = padvance(&path, name)) != NULL) {
372 stunalloc(fullname);
934863d7 373 idx++;
984263bc 374 if (pathopt) {
99512ac4 375 if (prefix("func", pathopt)) {
984263bc
MD
376 /* handled below */
377 } else {
378 goto loop; /* ignore unimplemented options */
379 }
380 }
381 /* if rehash, don't redo absolute path names */
934863d7
PA
382 if (fullname[0] == '/' && idx <= prev) {
383 if (idx < prev)
984263bc
MD
384 goto loop;
385 TRACE(("searchexec \"%s\": no change\n", name));
386 goto success;
387 }
388 if (stat(fullname, &statb) < 0) {
389 if (errno != ENOENT && errno != ENOTDIR)
390 e = errno;
391 goto loop;
392 }
393 e = EACCES; /* if we fail, this will be the error */
394 if (!S_ISREG(statb.st_mode))
395 goto loop;
396 if (pathopt) { /* this is a %func directory */
397 stalloc(strlen(fullname) + 1);
398 readcmdfile(fullname);
399 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
400 error("%s not defined in %s", name, fullname);
401 stunalloc(fullname);
402 goto success;
403 }
404#ifdef notdef
405 if (statb.st_uid == geteuid()) {
406 if ((statb.st_mode & 0100) == 0)
407 goto loop;
408 } else if (statb.st_gid == getegid()) {
409 if ((statb.st_mode & 010) == 0)
410 goto loop;
411 } else {
412 if ((statb.st_mode & 01) == 0)
413 goto loop;
414 }
415#endif
416 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
417 INTOFF;
418 cmdp = cmdlookup(name, 1);
99512ac4
PA
419 if (cmdp->cmdtype == CMDFUNCTION)
420 cmdp = &loc_cmd;
984263bc 421 cmdp->cmdtype = CMDNORMAL;
934863d7 422 cmdp->param.index = idx;
984263bc
MD
423 INTON;
424 goto success;
425 }
426
427 /* We failed. If there was an entry for this command, delete it */
99512ac4 428 if (cmdp && cmdp->cmdtype != CMDFUNCTION)
984263bc 429 delete_cmd_entry();
99512ac4 430 if (act & DO_ERR) {
010f7689
PA
431 if (e == ENOENT || e == ENOTDIR)
432 outfmt(out2, "%s: not found\n", name);
433 else
434 outfmt(out2, "%s: %s\n", name, strerror(e));
435 }
984263bc 436 entry->cmdtype = CMDUNKNOWN;
99512ac4 437 entry->u.index = 0;
984263bc
MD
438 return;
439
440success:
4b1ea71d
SS
441 if (cmdp) {
442 cmdp->rehash = 0;
443 entry->cmdtype = cmdp->cmdtype;
444 entry->u = cmdp->param;
0d5aaed6 445 entry->special = cmdp->special;
4b1ea71d
SS
446 } else
447 entry->cmdtype = CMDUNKNOWN;
984263bc
MD
448}
449
450
451
452/*
453 * Search the table of builtin commands.
454 */
455
456int
99512ac4 457find_builtin(const char *name, int *special)
984263bc
MD
458{
459 const struct builtincmd *bp;
460
461 for (bp = builtincmd ; bp->name ; bp++) {
0d5aaed6
PA
462 if (*bp->name == *name && equal(bp->name, name)) {
463 *special = bp->special;
984263bc 464 return bp->code;
0d5aaed6 465 }
984263bc
MD
466 }
467 return -1;
468}
469
470
471
472/*
473 * Called when a cd is done. Marks all commands so the next time they
474 * are executed they will be rehashed.
475 */
476
477void
478hashcd(void)
479{
480 struct tblentry **pp;
481 struct tblentry *cmdp;
482
483 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
484 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
99512ac4 485 if (cmdp->cmdtype == CMDNORMAL)
984263bc
MD
486 cmdp->rehash = 1;
487 }
488 }
489}
490
491
492
493/*
494 * Called before PATH is changed. The argument is the new value of PATH;
495 * pathval() still returns the old value at this point. Called with
496 * interrupts off.
497 */
498
499void
99512ac4 500changepath(const char *newval __unused)
984263bc 501{
99512ac4 502 clearcmdentry();
984263bc
MD
503}
504
505
506/*
507 * Clear out command entries. The argument specifies the first entry in
508 * PATH which has changed.
509 */
510
511void
99512ac4 512clearcmdentry(void)
984263bc
MD
513{
514 struct tblentry **tblp;
515 struct tblentry **pp;
516 struct tblentry *cmdp;
517
518 INTOFF;
519 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
520 pp = tblp;
521 while ((cmdp = *pp) != NULL) {
99512ac4 522 if (cmdp->cmdtype == CMDNORMAL) {
984263bc 523 *pp = cmdp->next;
984263bc
MD
524 ckfree(cmdp);
525 } else {
526 pp = &cmdp->next;
527 }
528 }
529 }
530 INTON;
531}
532
533
984263bc
MD
534/*
535 * Locate a command in the command hash table. If "add" is nonzero,
536 * add the command to the table if it is not already present. The
537 * variable "lastcmdentry" is set to point to the address of the link
538 * pointing to the entry, so that delete_cmd_entry can delete the
539 * entry.
540 */
541
99512ac4 542static struct tblentry **lastcmdentry;
984263bc
MD
543
544
99512ac4
PA
545static struct tblentry *
546cmdlookup(const char *name, int add)
984263bc
MD
547{
548 int hashval;
99512ac4 549 const char *p;
984263bc
MD
550 struct tblentry *cmdp;
551 struct tblentry **pp;
552
553 p = name;
554 hashval = *p << 4;
555 while (*p)
556 hashval += *p++;
557 hashval &= 0x7FFF;
558 pp = &cmdtable[hashval % CMDTABLESIZE];
559 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
560 if (equal(cmdp->cmdname, name))
561 break;
562 pp = &cmdp->next;
563 }
564 if (add && cmdp == NULL) {
565 INTOFF;
566 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
567 + strlen(name) + 1);
568 cmdp->next = NULL;
569 cmdp->cmdtype = CMDUNKNOWN;
570 cmdp->rehash = 0;
571 strcpy(cmdp->cmdname, name);
572 INTON;
573 }
574 lastcmdentry = pp;
575 return cmdp;
576}
577
578/*
579 * Delete the command entry returned on the last lookup.
580 */
581
99512ac4 582static void
984263bc
MD
583delete_cmd_entry(void)
584{
585 struct tblentry *cmdp;
586
587 INTOFF;
588 cmdp = *lastcmdentry;
589 *lastcmdentry = cmdp->next;
590 ckfree(cmdp);
591 INTON;
592}
593
594
595
596/*
597 * Add a new command entry, replacing any existing command entry for
598 * the same name.
599 */
600
b6bd99f6 601static void
99512ac4 602addcmdentry(const char *name, struct cmdentry *entry)
984263bc
MD
603{
604 struct tblentry *cmdp;
605
606 INTOFF;
607 cmdp = cmdlookup(name, 1);
608 if (cmdp->cmdtype == CMDFUNCTION) {
99512ac4 609 unreffunc(cmdp->param.func);
984263bc
MD
610 }
611 cmdp->cmdtype = entry->cmdtype;
612 cmdp->param = entry->u;
613 INTON;
614}
615
616
617/*
618 * Define a shell function.
619 */
620
621void
99512ac4 622defun(const char *name, union node *func)
984263bc
MD
623{
624 struct cmdentry entry;
625
626 INTOFF;
627 entry.cmdtype = CMDFUNCTION;
628 entry.u.func = copyfunc(func);
629 addcmdentry(name, &entry);
630 INTON;
631}
632
633
634/*
635 * Delete a function if it exists.
636 */
637
638int
99512ac4 639unsetfunc(const char *name)
984263bc
MD
640{
641 struct tblentry *cmdp;
642
643 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
99512ac4 644 unreffunc(cmdp->param.func);
984263bc 645 delete_cmd_entry();
984263bc 646 }
31b6347c 647 return (0);
984263bc
MD
648}
649
650/*
0d5aaed6
PA
651 * Shared code for the following builtin commands:
652 * type, command -v, command -V
984263bc
MD
653 */
654
655int
99512ac4 656typecmd_impl(int argc, char **argv, int cmd, const char *path)
984263bc
MD
657{
658 struct cmdentry entry;
659 struct tblentry *cmdp;
492efe05 660 const char * const *pp;
984263bc
MD
661 struct alias *ap;
662 int i;
934863d7 663 int err = 0;
984263bc 664
99512ac4
PA
665 if (path != pathval())
666 clearcmdentry();
0d5aaed6 667
99512ac4 668 for (i = 1; i < argc; i++) {
984263bc 669 /* First look at the keywords */
492efe05 670 for (pp = parsekwd; *pp; pp++)
984263bc
MD
671 if (**pp == *argv[i] && equal(*pp, argv[i]))
672 break;
673
674 if (*pp) {
0d5aaed6
PA
675 if (cmd == TYPECMD_SMALLV)
676 out1fmt("%s\n", argv[i]);
677 else
99512ac4 678 out1fmt("%s is a shell keyword\n", argv[i]);
984263bc
MD
679 continue;
680 }
681
682 /* Then look at the aliases */
683 if ((ap = lookupalias(argv[i], 1)) != NULL) {
0d5aaed6
PA
684 if (cmd == TYPECMD_SMALLV)
685 out1fmt("alias %s='%s'\n", argv[i], ap->val);
686 else
99512ac4
PA
687 out1fmt("%s is an alias for %s\n", argv[i],
688 ap->val);
984263bc
MD
689 continue;
690 }
691
692 /* Then check if it is a tracked alias */
693 if ((cmdp = cmdlookup(argv[i], 0)) != NULL) {
694 entry.cmdtype = cmdp->cmdtype;
695 entry.u = cmdp->param;
99512ac4 696 entry.special = cmdp->special;
984263bc
MD
697 }
698 else {
699 /* Finally use brute force */
99512ac4 700 find_command(argv[i], &entry, 0, path);
984263bc
MD
701 }
702
703 switch (entry.cmdtype) {
704 case CMDNORMAL: {
705 if (strchr(argv[i], '/') == NULL) {
99512ac4 706 const char *path2 = path;
492efe05 707 char *name;
984263bc
MD
708 int j = entry.u.index;
709 do {
99512ac4 710 name = padvance(&path2, argv[i]);
984263bc
MD
711 stunalloc(name);
712 } while (--j >= 0);
0d5aaed6
PA
713 if (cmd == TYPECMD_SMALLV)
714 out1fmt("%s\n", name);
715 else
99512ac4 716 out1fmt("%s is%s %s\n", argv[i],
0d5aaed6
PA
717 (cmdp && cmd == TYPECMD_TYPE) ?
718 " a tracked alias for" : "",
719 name);
984263bc 720 } else {
b25bde5a 721 if (access(argv[i], X_OK) == 0) {
0d5aaed6
PA
722 if (cmd == TYPECMD_SMALLV)
723 out1fmt("%s\n", argv[i]);
724 else
99512ac4
PA
725 out1fmt("%s is %s\n", argv[i],
726 argv[i]);
ee44a32d
PA
727 } else {
728 if (cmd != TYPECMD_SMALLV)
99512ac4
PA
729 outfmt(out2, "%s: %s\n",
730 argv[i], strerror(errno));
934863d7 731 err |= 127;
b25bde5a 732 }
984263bc
MD
733 }
734 break;
735 }
736 case CMDFUNCTION:
0d5aaed6
PA
737 if (cmd == TYPECMD_SMALLV)
738 out1fmt("%s\n", argv[i]);
739 else
99512ac4 740 out1fmt("%s is a shell function\n", argv[i]);
984263bc
MD
741 break;
742
743 case CMDBUILTIN:
0d5aaed6
PA
744 if (cmd == TYPECMD_SMALLV)
745 out1fmt("%s\n", argv[i]);
99512ac4
PA
746 else if (entry.special)
747 out1fmt("%s is a special shell builtin\n",
748 argv[i]);
0d5aaed6 749 else
99512ac4 750 out1fmt("%s is a shell builtin\n", argv[i]);
984263bc
MD
751 break;
752
753 default:
0d5aaed6 754 if (cmd != TYPECMD_SMALLV)
99512ac4 755 outfmt(out2, "%s: not found\n", argv[i]);
934863d7 756 err |= 127;
984263bc
MD
757 break;
758 }
759 }
99512ac4
PA
760
761 if (path != pathval())
762 clearcmdentry();
763
934863d7 764 return err;
984263bc 765}
0d5aaed6
PA
766
767/*
768 * Locate and print what a word is...
769 */
770
771int
772typecmd(int argc, char **argv)
773{
99512ac4 774 return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1));
0d5aaed6 775}