BSD 4 release
[unix-history] / usr / src / cmd / oldcsh / sh.exec.c
CommitLineData
b898b9e0
BJ
1/* Copyright (c) 1979 Regents of the University of California */
2#include "sh.h"
3
4/*
5 * C shell
6 */
7
8/*
9 * System level search and execute of a command.
10 * We look in each directory for the specified command name.
11 * If the name contains a '/' then we execute only the full path name.
12 * If there is no search path then we execute only full path names.
13 */
14
15/*
16 * As we search for the command we note the first non-trivial error
17 * message for presentation to the user. This allows us often
18 * to show that a file has the wrong mode/no access when the file
19 * is not in the last component of the search path, so we must
20 * go on after first detecting the error.
21 */
22char *exerr; /* Execution error message */
23char *expath; /* Path for exerr */
24
25/*
26 * Xhash is an array of HSHSIZ chars, which are used to hash execs.
27 * If it is allocated, then to tell whether ``name'' is (possibly)
28 * present in the i'th component of the variable path, you look at
29 * the i'th bit of xhash[hash("name")]. This is setup automatically
30 * after .login is executed, and recomputed whenever ``path'' is
31 * changed.
32 */
33int havhash;
34#define HSHSIZ 511
35char xhash[HSHSIZ];
36int hits, misses;
37
38/* Dummy search path for just absolute search when no path */
39char *justabs[] = { "", 0 };
40
41doexec(t)
42 register struct command *t;
43{
44 char *sav;
45 register char *dp, **pv, **av;
46 register struct varent *v;
47 bool slash = any('/', t->t_dcom[0]);
48 int hashval, i;
49 char *blk[2];
50
51 /*
52 * Glob the command name. If this does anything, then we
53 * will execute the command only relative to ".". One special
54 * case: if there is no PATH, then we execute only commands
55 * which start with '/'.
56 */
57 dp = globone(t->t_dcom[0]);
58 sav = t->t_dcom[0];
59 exerr = 0; expath = t->t_dcom[0] = dp;
60 xfree(sav);
61 v = adrof("path");
62 if (v == 0 && expath[0] != '/')
63 pexerr();
64 slash |= gflag;
65
66 /*
67 * Glob the argument list, if necessary.
68 * Otherwise trim off the quote bits.
69 */
70 gflag = 0; av = &t->t_dcom[1];
71 rscan(av, tglob);
72 if (gflag) {
73 av = glob(av);
74 if (av == 0)
75 error("No match");
76 }
77 blk[0] = t->t_dcom[0];
78 blk[1] = 0;
79 av = blkspl(blk, av);
80#ifdef VFORK
81 Vav = av;
82#endif
83 scan(av, trim);
84
85 xechoit(av); /* Echo command if -x */
86 closech(); /* Close random fd's */
87
88 /*
89 * If no path, no words in path, or a / in the filename
90 * then restrict the command search.
91 */
92 if (v == 0 || v->vec[0] == 0 || slash)
93 pv = justabs;
94 else
95 pv = v->vec;
96 sav = strspl("/", *av); /* / command name for postpending */
97#ifdef VFORK
98 Vsav = sav;
99#endif
100 if (havhash)
101 hashval = xhash[hash(*av)];
102 i = 0;
103 hits++;
104 do {
105 if (!slash && pv[0][0] == '/' && havhash && (hashval & (1 << (i % 8))) == 0)
106 goto cont;
107 if (pv[0][0] == 0 || eq(pv[0], ".")) /* don't make ./xxx */
108 texec(*av, av);
109 else {
110 dp = strspl(*pv, sav);
111#ifdef VFORK
112 Vdp = dp;
113#endif
114 texec(dp, av);
115#ifdef VFORK
116 Vdp = 0;
117#endif
118 xfree(dp);
119 }
120 misses++;
121cont:
122 pv++;
123 i++;
124 } while (*pv);
125 hits--;
126#ifdef VFORK
127 Vsav = 0;
128 Vav = 0;
129#endif
130 xfree(sav);
131 xfree(av);
132 pexerr();
133}
134
135pexerr()
136{
137
138 /* Couldn't find the damn thing */
139 setname(expath);
140 /* xfree(expath); */
141 if (exerr)
142 bferr(exerr);
143 bferr("Command not found");
144}
145
146/* Last resort shell */
147char *lastsh[] = { SHELLPATH, 0 };
148
149/*
150 * Execute command f, arg list t.
151 * Record error message if not found.
152 * Also do shell scripts here.
153 */
154texec(f, t)
155 char *f;
156 register char **t;
157{
158 register struct varent *v;
159 register char **vp;
160 extern char *sys_errlist[];
161
162 execv(f, t);
163 switch (errno) {
164
165 case ENOEXEC:
166 /*
167 * If there is an alias for shell, then
168 * put the words of the alias in front of the
169 * argument list replacing the command name.
170 * Note no interpretation of the words at this point.
171 */
172 v = adrof1("shell", &aliases);
173 if (v == 0) {
174#ifdef OTHERSH
175 register int ff = open(f, 0);
176 char ch;
177#endif
178
179 vp = lastsh;
180 vp[0] = adrof("shell") ? value("shell") : SHELLPATH;
181#ifdef OTHERSH
182 if (ff != -1 && read(ff, &ch, 1) == 1 && ch != '#')
183 vp[0] = OTHERSH;
184 close(ff);
185#endif
186 } else
187 vp = v->vec;
188 t[0] = f;
189 t = blkspl(vp, t); /* Splice up the new arglst */
190 f = *t;
191 execv(f, t);
192 xfree(t);
193 /* The sky is falling, the sky is falling! */
194
195 case ENOMEM:
196 Perror(f);
197
198 case ENOENT:
199 break;
200
201 default:
202 if (exerr == 0) {
203 exerr = sys_errlist[errno];
204 expath = savestr(f);
205 }
206 }
207}
208
209execash(t, kp)
210 register struct command *kp;
211{
212
213 didcch++;
214 lshift(kp->t_dcom, 1);
215 doexec(kp);
216 /*NOTREACHED*/
217}
218
219xechoit(t)
220 char **t;
221{
222
223 if (adrof("echo")) {
224 flush();
225 haderr = 1;
226 blkpr(t), printf("\n");
227 haderr = 0;
228 }
229}
230
231dohash()
232{
233 struct stat stb;
234 struct direct dirbuf[BUFSIZ / sizeof (struct direct)];
235 char d_name[DIRSIZ + 1];
236 register int dirf, cnt;
237 int i = 0;
238 struct varent *v = adrof("path");
239 char **pv;
240
241 havhash = 1;
242 for (cnt = 0; cnt < HSHSIZ; cnt++)
243 xhash[cnt] = 0;
244 if (v == 0)
245 return;
246 for (pv = v->vec; *pv; pv++, i = (i + 1) % 8) {
247 if (pv[0][0] != '/')
248 continue;
249 dirf = open(*pv, 0);
250 if (dirf < 0)
251 continue;
252 if (fstat(dirf, &stb) < 0 || !isdir(stb)) {
253 close(dirf);
254 continue;
255 }
256 while ((cnt = read(dirf, (char *) dirbuf, sizeof dirbuf)) >= sizeof dirbuf[0]) {
257 register struct direct *ep = dirbuf;
258
259 for (cnt /= sizeof(struct direct); cnt > 0; cnt--, ep++) {
260 if (ep->d_ino == 0)
261 continue;
262 copdent(d_name, ep->d_name);
263 xhash[hash(d_name)] |= (1 << i);
264 }
265 }
266 close(dirf);
267 }
268}
269
270dounhash()
271{
272
273 havhash = 0;
274}
275
276hashstat()
277{
278
279 if (hits+misses)
280 printf("%d hits, %d misses, %2d%%\n", hits, misses, 100 * hits / (hits + misses));
281}
282
283hash(cp)
284 register char *cp;
285{
286 register int hash = 0;
287
288 while (*cp)
289 hash += hash + *cp++;
290 return (hash % HSHSIZ);
291}