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