Bug Summary

File:src/bin/csh/exec.c
Warning:line 131, column 22
Array access (from variable 'pv') results in a null pointer dereference

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name exec.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/bin/csh/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/bin/csh -I . -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/bin/csh/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/bin/csh/exec.c
1/* $OpenBSD: exec.c,v 1.22 2023/03/08 04:43:04 guenther Exp $ */
2/* $NetBSD: exec.c,v 1.9 1996/09/30 20:03:54 christos Exp $ */
3
4/*-
5 * Copyright (c) 1980, 1991, 1993
6 * The Regents of the University of California. All rights reserved.
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. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34#include <dirent.h>
35#include <fcntl.h>
36#include <sys/stat.h>
37#include <errno(*__errno()).h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41#include <limits.h>
42#include <stdarg.h>
43
44#include "csh.h"
45#include "extern.h"
46
47/*
48 * System level search and execute of a command. We look in each directory
49 * for the specified command name. If the name contains a '/' then we
50 * execute only the full path name. If there is no search path then we
51 * execute only full path names.
52 */
53extern char **environ;
54
55/*
56 * As we search for the command we note the first non-trivial error
57 * message for presentation to the user. This allows us often
58 * to show that a file has the wrong mode/no access when the file
59 * is not in the last component of the search path, so we must
60 * go on after first detecting the error.
61 */
62static char *exerr; /* Execution error message */
63static Char *expath; /* Path for exerr */
64
65/*
66 * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
67 * to hash execs. If it is allocated (havhash true), then to tell
68 * whether ``name'' is (possibly) present in the i'th component
69 * of the variable path, you look at the bit in xhash indexed by
70 * hash(hashname("name"), i). This is setup automatically
71 * after .login is executed, and recomputed whenever ``path'' is
72 * changed.
73 * The two part hash function is designed to let texec() call the
74 * more expensive hashname() only once and the simple hash() several
75 * times (once for each path component checked).
76 * Byte size is assumed to be 8.
77 */
78#define HSHSIZ8192 8192 /* 1k bytes */
79#define HSHMASK(8192 - 1) (HSHSIZ8192 - 1)
80#define HSHMUL243 243
81static char xhash[HSHSIZ8192 / 8];
82
83#define hash(a, b)(((a) * 243 + (b)) & (8192 - 1)) (((a) * HSHMUL243 + (b)) & HSHMASK(8192 - 1))
84#define bit(h, b)((h)[(b) >> 3] & 1 << ((b) & 7)) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */
85#define bis(h, b)((h)[(b) >> 3] |= 1 << ((b) & 7)) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */
86static int hits, misses;
87
88/* Dummy search path for just absolute search when no path */
89static Char *justabs[] = {STRNULL, 0};
90
91static void pexerr(void);
92static void texec(Char *, Char **);
93static int hashname(Char *);
94static int tellmewhat(struct wordent *, Char *, int len);
95static int executable(Char *, Char *, bool);
96static int iscommand(Char *);
97
98
99void
100doexec(Char **v, struct command *t)
101{
102 Char *dp, **pv, **av, *sav;
103 struct varent *pathv;
104 bool slash;
105 int hashval = 0, hashval1, i;
106 Char *blk[2];
107 sigset_t sigset;
108
109 /*
110 * Glob the command name. We will search $path even if this does something,
111 * as in sh but not in csh. One special case: if there is no PATH, then we
112 * execute only commands which start with '/'.
113 */
114 blk[0] = t->t_dcom[0];
115 blk[1] = 0;
116 gflag = 0, tglob(blk);
117 if (gflag) {
5
Assuming 'gflag' is not equal to 0
6
Taking true branch
118 pv = globall(blk);
7
Value assigned to 'pv'
119 if (pv == 0) {
8
Assuming 'pv' is equal to null
9
Taking true branch
120 setname(vis_str(blk[0]))(bname = (vis_str(blk[0])));
121 stderror(ERR_NAME0x10000000 | ERR_NOMATCH50);
122 }
123 gargv = 0;
124 }
125 else
126 pv = saveblk(blk);
127
128 trim(pv);
129
130 exerr = 0;
131 expath = Strsave(pv[0]);
10
Array access (from variable 'pv') results in a null pointer dereference
132 Vexpath = expath;
133
134 pathv = adrof(STRpath)adrof1(STRpath, &shvhed);
135 if (pathv == 0 && expath[0] != '/') {
136 blkfree(pv);
137 pexerr();
138 }
139 slash = any(short2str(expath), '/');
140
141 /*
142 * Glob the argument list, if necessary. Otherwise trim off the quote bits.
143 */
144 gflag = 0;
145 av = &t->t_dcom[1];
146 tglob(av);
147 if (gflag) {
148 av = globall(av);
149 if (av == 0) {
150 blkfree(pv);
151 setname(vis_str(expath))(bname = (vis_str(expath)));
152 stderror(ERR_NAME0x10000000 | ERR_NOMATCH50);
153 }
154 gargv = 0;
155 }
156 else
157 av = saveblk(av);
158
159 blkfree(t->t_dcom);
160 t->t_dcom = blkspl(pv, av);
161 free(pv);
162 free(av);
163 av = t->t_dcom;
164 trim(av);
165
166 if (*av == NULL((void *)0) || **av == '\0')
167 pexerr();
168
169 xechoit(av); /* Echo command if -x */
170 /*
171 * Since all internal file descriptors are set to close on exec, we don't
172 * need to close them explicitly here. Just reorient ourselves for error
173 * messages.
174 */
175 SHIN = 0;
176 SHOUT = 1;
177 SHERR = 2;
178 OLDSTD = 0;
179 /*
180 * We must do this AFTER any possible forking (like `foo` in glob) so that
181 * this shell can still do subprocesses.
182 */
183 sigemptyset(&sigset);
184 sigprocmask(SIG_SETMASK3, &sigset, NULL((void *)0));
185 /*
186 * If no path, no words in path, or a / in the filename then restrict the
187 * command search.
188 */
189 if (pathv == 0 || pathv->vec[0] == 0 || slash)
190 pv = justabs;
191 else
192 pv = pathv->vec;
193 sav = Strspl(STRslash, *av);/* / command name for postpending */
194 Vsav = sav;
195 if (havhash)
196 hashval = hashname(*av);
197 i = 0;
198 hits++;
199 do {
200 /*
201 * Try to save time by looking at the hash table for where this command
202 * could be. If we are doing delayed hashing, then we put the names in
203 * one at a time, as the user enters them. This is kinda like Korn
204 * Shell's "tracked aliases".
205 */
206 if (!slash && pv[0][0] == '/' && havhash) {
207 hashval1 = hash(hashval, i)(((hashval) * 243 + (i)) & (8192 - 1));
208 if (!bit(xhash, hashval1)((xhash)[(hashval1) >> 3] & 1 << ((hashval1) &
7))
)
209 goto cont;
210 }
211 if (pv[0][0] == 0 || eq(pv[0], STRdot)(Strcmp(pv[0], STRdot) == 0)) /* don't make ./xxx */
212 texec(*av, av);
213 else {
214 dp = Strspl(*pv, sav);
215 Vdp = dp;
216 texec(dp, av);
217 Vdp = 0;
218 free(dp);
219 }
220 misses++;
221cont:
222 pv++;
223 i++;
224 } while (*pv);
225 hits--;
226 Vsav = 0;
227 free(sav);
228 pexerr();
229}
230
231static void
232pexerr(void)
233{
234 /* Couldn't find the damn thing */
235 if (expath) {
236 setname(vis_str(expath))(bname = (vis_str(expath)));
237 Vexpath = 0;
238 free(expath);
239 expath = 0;
240 }
241 else
242 setname("")(bname = (""));
243 if (exerr)
244 stderror(ERR_NAME0x10000000 | ERR_STRING56, exerr);
245 stderror(ERR_NAME0x10000000 | ERR_COMMAND14);
246}
247
248/*
249 * Execute command f, arg list t.
250 * Record error message if not found.
251 * Also do shell scripts here.
252 */
253static void
254texec(Char *sf, Char **st)
255{
256 char **t;
257 char *f;
258 struct varent *v;
259 Char **vp;
260 Char *lastsh[2];
261 int fd;
262 unsigned char c;
263 Char *st0, **ost;
264
265 /* The order for the conversions is significant */
266 t = short2blk(st);
267 f = short2str(sf);
268 Vt = t;
269 errno(*__errno()) = 0; /* don't use a previous error */
270 (void) execve(f, t, environ);
271 Vt = 0;
272 blkfree((Char **) t);
273 switch (errno(*__errno())) {
274
275 case ENOEXEC8:
276 /*
277 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
278 * it, don't feed it to the shell if it looks like a binary!
279 */
280 if ((fd = open(f, O_RDONLY0x0000)) != -1) {
281 if (read(fd, (char *) &c, 1) == 1) {
282 if (!Isprint(c)(((c) & 0100000U) ? 0 : isprint((unsigned char) (c))) && (c != '\n' && c != '\t')) {
283 (void) close(fd);
284 /*
285 * We *know* what ENOEXEC means.
286 */
287 stderror(ERR_ARCH107, f, strerror(errno(*__errno())));
288 }
289 }
290 else
291 c = '#';
292 (void) close(fd);
293 }
294 /*
295 * If there is an alias for shell, then put the words of the alias in
296 * front of the argument list replacing the command name. Note no
297 * interpretation of the words at this point.
298 */
299 v = adrof1(STRshell, &aliases);
300 if (v == 0) {
301 vp = lastsh;
302 vp[0] = adrof(STRshell)adrof1(STRshell, &shvhed) ? value(STRshell)value1(STRshell, &shvhed) : STR_SHELLPATH;
303 vp[1] = NULL((void *)0);
304 if (fd != -1 && c != '#')
305 vp[0] = STR_BSHELL;
306 }
307 else
308 vp = v->vec;
309 st0 = st[0];
310 st[0] = sf;
311 ost = st;
312 st = blkspl(vp, st); /* Splice up the new arglst */
313 ost[0] = st0;
314 sf = *st;
315 /* The order for the conversions is significant */
316 t = short2blk(st);
317 f = short2str(sf);
318 free(st);
319 Vt = t;
320 (void) execve(f, t, environ);
321 Vt = 0;
322 blkfree((Char **) t);
323 /* The sky is falling, the sky is falling! */
324
325 case ENOMEM12:
326 stderror(ERR_SYSTEM55, f, strerror(errno(*__errno())));
327
328 case ENOENT2:
329 break;
330
331 default:
332 if (exerr == 0) {
333 exerr = strerror(errno(*__errno()));
334 if (expath)
335 free(expath);
336 expath = Strsave(sf);
337 Vexpath = expath;
338 }
339 }
340}
341
342void
343execash(Char **t, struct command *kp)
344{
345 int saveIN, saveOUT, saveDIAG, saveSTD;
346 int oSHIN;
347 int oSHOUT;
348 int oSHERR;
349 int oOLDSTD;
350 jmp_buf osetexit;
351 int my_reenter;
352 int odidfds;
353 sig_t osigint, osigquit, osigterm;
354
355 if (chkstop == 0 && setintr)
1
Assuming 'chkstop' is not equal to 0
356 panystop(0);
357 /*
358 * Hmm, we don't really want to do that now because we might
359 * fail, but what is the choice
360 */
361 rechist();
362
363 osigint = signal(SIGINT2, parintr);
364 osigquit = signal(SIGQUIT3, parintr);
365 osigterm = signal(SIGTERM15, parterm);
366
367 odidfds = didfds;
368 oSHIN = SHIN;
369 oSHOUT = SHOUT;
370 oSHERR = SHERR;
371 oOLDSTD = OLDSTD;
372
373 saveIN = dcopy(SHIN, -1);
374 saveOUT = dcopy(SHOUT, -1);
375 saveDIAG = dcopy(SHERR, -1);
376 saveSTD = dcopy(OLDSTD, -1);
377
378 lshift(kp->t_dcom, 1);
379
380 getexit(osetexit)memcpy((osetexit), reslab, sizeof reslab);
381
382 if ((my_reenter = setexit()(setjmp(reslab))) == 0) {
2
Assuming the condition is true
3
Taking true branch
383 SHIN = dcopy(0, -1);
384 SHOUT = dcopy(1, -1);
385 SHERR = dcopy(2, -1);
386 didfds = 0;
387 doexec(t, kp);
4
Calling 'doexec'
388 }
389
390 (void) signal(SIGINT2, osigint);
391 (void) signal(SIGQUIT3, osigquit);
392 (void) signal(SIGTERM15, osigterm);
393
394 doneinp = 0;
395 didfds = odidfds;
396 (void) close(SHIN);
397 (void) close(SHOUT);
398 (void) close(SHERR);
399 (void) close(OLDSTD);
400 SHIN = dmove(saveIN, oSHIN);
401 SHOUT = dmove(saveOUT, oSHOUT);
402 SHERR = dmove(saveDIAG, oSHERR);
403 OLDSTD = dmove(saveSTD, oOLDSTD);
404
405 resexit(osetexit)memcpy(reslab, (osetexit), sizeof reslab);
406 if (my_reenter)
407 stderror(ERR_SILENT0x20000000);
408}
409
410void
411xechoit(Char **t)
412{
413 if (adrof(STRecho)adrof1(STRecho, &shvhed)) {
414 (void) fflush(csherr);
415 blkpr(csherr, t);
416 (void) fputc('\n', csherr);
417 }
418}
419
420void
421dohash(Char **v, struct command *t)
422{
423 DIR *dirp;
424 struct dirent *dp;
425 int cnt;
426 int i = 0;
427 struct varent *pathv = adrof(STRpath)adrof1(STRpath, &shvhed);
428 Char **pv;
429 int hashval;
430
431 havhash = 1;
432 for (cnt = 0; cnt < sizeof xhash; cnt++)
433 xhash[cnt] = 0;
434 if (pathv == 0)
435 return;
436 for (pv = pathv->vec; *pv; pv++, i++) {
437 if (pv[0][0] != '/')
438 continue;
439 dirp = opendir(short2str(*pv));
440 if (dirp == NULL((void *)0))
441 continue;
442 while ((dp = readdir(dirp)) != NULL((void *)0)) {
443 if (dp->d_inod_fileno == 0)
444 continue;
445 if (dp->d_name[0] == '.' &&
446 (dp->d_name[1] == '\0' ||
447 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
448 continue;
449 hashval = hash(hashname(str2short(dp->d_name)), i)(((hashname(str2short(dp->d_name))) * 243 + (i)) & (8192
- 1))
;
450 bis(xhash, hashval)((xhash)[(hashval) >> 3] |= 1 << ((hashval) &
7))
;
451 /* tw_add_comm_name (dp->d_name); */
452 }
453 (void) closedir(dirp);
454 }
455}
456
457void
458dounhash(Char **v, struct command *t)
459{
460 havhash = 0;
461}
462
463void
464hashstat(Char **v, struct command *t)
465{
466 if (hits + misses)
467 (void) fprintf(cshout, "%d hits, %d misses, %d%%\n",
468 hits, misses, 100 * hits / (hits + misses));
469}
470
471/*
472 * Hash a command name.
473 */
474static int
475hashname(Char *cp)
476{
477 long h = 0;
478
479 while (*cp)
480 h = hash(h, *cp++)(((h) * 243 + (*cp++)) & (8192 - 1));
481 return ((int) h);
482}
483
484static int
485iscommand(Char *name)
486{
487 Char **pv;
488 Char *sav;
489 struct varent *v;
490 bool slash = any(short2str(name), '/');
491 int hashval = 0, hashval1, i;
492
493 v = adrof(STRpath)adrof1(STRpath, &shvhed);
494 if (v == 0 || v->vec[0] == 0 || slash)
495 pv = justabs;
496 else
497 pv = v->vec;
498 sav = Strspl(STRslash, name); /* / command name for postpending */
499 if (havhash)
500 hashval = hashname(name);
501 i = 0;
502 do {
503 if (!slash && pv[0][0] == '/' && havhash) {
504 hashval1 = hash(hashval, i)(((hashval) * 243 + (i)) & (8192 - 1));
505 if (!bit(xhash, hashval1)((xhash)[(hashval1) >> 3] & 1 << ((hashval1) &
7))
)
506 goto cont;
507 }
508 if (pv[0][0] == 0 || eq(pv[0], STRdot)(Strcmp(pv[0], STRdot) == 0)) { /* don't make ./xxx */
509 if (executable(NULL((void *)0), name, 0)) {
510 free(sav);
511 return i + 1;
512 }
513 }
514 else {
515 if (executable(*pv, sav, 0)) {
516 free(sav);
517 return i + 1;
518 }
519 }
520cont:
521 pv++;
522 i++;
523 } while (*pv);
524 free(sav);
525 return 0;
526}
527
528/* Also by:
529 * Andreas Luik <luik@isaak.isa.de>
530 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung
531 * Azenberstr. 35
532 * D-7000 Stuttgart 1
533 * West-Germany
534 * is the executable() routine below and changes to iscommand().
535 * Thanks again!!
536 */
537
538/*
539 * executable() examines the pathname obtained by concatenating dir and name
540 * (dir may be NULL), and returns 1 either if it is executable by us, or
541 * if dir_ok is set and the pathname refers to a directory.
542 * This is a bit kludgy, but in the name of optimization...
543 */
544static int
545executable(Char *dir, Char *name, bool dir_ok)
546{
547 struct stat stbuf;
548 Char path[PATH_MAX1024], *dp, *sp;
549 char *strname;
550
551 if (dir && *dir) {
552 for (dp = path, sp = dir; *sp; *dp++ = *sp++)
553 if (dp == &path[PATH_MAX1024]) {
554 *--dp = '\0';
555 break;
556 }
557 for (sp = name; *sp; *dp++ = *sp++)
558 if (dp == &path[PATH_MAX1024]) {
559 *--dp = '\0';
560 break;
561 }
562 *dp = '\0';
563 strname = short2str(path);
564 }
565 else
566 strname = short2str(name);
567 return (stat(strname, &stbuf) != -1 &&
568 ((S_ISREG(stbuf.st_mode)((stbuf.st_mode & 0170000) == 0100000) &&
569 /* save time by not calling access() in the hopeless case */
570 (stbuf.st_mode & (S_IXOTH0000001 | S_IXGRP0000010 | S_IXUSR0000100)) &&
571 access(strname, X_OK0x01) == 0) ||
572 (dir_ok && S_ISDIR(stbuf.st_mode)((stbuf.st_mode & 0170000) == 0040000))));
573}
574
575/* The dowhich() is by:
576 * Andreas Luik <luik@isaak.isa.de>
577 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung
578 * Azenberstr. 35
579 * D-7000 Stuttgart 1
580 * West-Germany
581 * Thanks!!
582 */
583void
584dowhich(Char **v, struct command *c)
585{
586 struct wordent lex[3];
587 struct varent *vp;
588
589 lex[0].next = &lex[1];
590 lex[1].next = &lex[2];
591 lex[2].next = &lex[0];
592
593 lex[0].prev = &lex[2];
594 lex[1].prev = &lex[0];
595 lex[2].prev = &lex[1];
596
597 lex[0].word = STRNULL;
598 lex[2].word = STRret;
599
600 while (*++v) {
601 if ((vp = adrof1(*v, &aliases)) != NULL((void *)0)) {
602 (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v));
603 blkpr(cshout, vp->vec);
604 (void) fputc('\n', cshout);
605 set(STRstatus, Strsave(STR0));
606 }
607 else {
608 lex[1].word = *v;
609 set(STRstatus, Strsave(tellmewhat(lex, NULL((void *)0), 0) ? STR0 : STR1));
610 }
611 }
612}
613
614static int
615tellmewhat(struct wordent *lexp, Char *str, int len)
616{
617 int i;
618 struct biltins *bptr;
619 struct wordent *sp = lexp->next;
620 bool aliased = 0, found;
621 Char *s0, *s1, *s2, *cmd;
622 Char qc;
623
624 if (adrof1(sp->word, &aliases)) {
625 alias(lexp);
626 sp = lexp->next;
627 aliased = 1;
628 }
629
630 s0 = sp->word; /* to get the memory freeing right... */
631
632 /* handle quoted alias hack */
633 if ((*(sp->word) & (QUOTE0100000U | TRIM0077777)) == QUOTE0100000U)
634 (sp->word)++;
635
636 /* do quoting, if it hasn't been done */
637 s1 = s2 = sp->word;
638 while (*s2)
639 switch (*s2) {
640 case '\'':
641 case '"':
642 qc = *s2++;
643 while (*s2 && *s2 != qc)
644 *s1++ = *s2++ | QUOTE0100000U;
645 if (*s2)
646 s2++;
647 break;
648 case '\\':
649 if (*++s2)
650 *s1++ = *s2++ | QUOTE0100000U;
651 break;
652 default:
653 *s1++ = *s2++;
654 }
655 *s1 = '\0';
656
657 for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
658 if (eq(sp->word, str2short(bptr->bname))(Strcmp(sp->word, str2short(bptr->bname)) == 0)) {
659 if (str == NULL((void *)0)) {
660 if (aliased)
661 prlex(cshout, lexp);
662 (void) fprintf(cshout, "%s: shell built-in command.\n",
663 vis_str(sp->word));
664 }
665 else
666 (void) Strlcpy(str, sp->word, len/sizeof(Char));
667 sp->word = s0; /* we save and then restore this */
668 return 1;
669 }
670 }
671
672 sp->word = cmd = globone(sp->word, G_IGNORE1);
673
674 if ((i = iscommand(sp->word)) != 0) {
675 Char **pv;
676 struct varent *v;
677 bool slash = any(short2str(sp->word), '/');
678
679 v = adrof(STRpath)adrof1(STRpath, &shvhed);
680 if (v == 0 || v->vec[0] == 0 || slash)
681 pv = justabs;
682 else
683 pv = v->vec;
684
685 while (--i)
686 pv++;
687 if (pv[0][0] == 0 || eq(pv[0], STRdot)(Strcmp(pv[0], STRdot) == 0)) {
688 if (!slash) {
689 sp->word = Strspl(STRdotsl, sp->word);
690 prlex(cshout, lexp);
691 free(sp->word);
692 }
693 else
694 prlex(cshout, lexp);
695 }
696 else {
697 s1 = Strspl(*pv, STRslash);
698 sp->word = Strspl(s1, sp->word);
699 free(s1);
700 if (str == NULL((void *)0))
701 prlex(cshout, lexp);
702 else
703 (void) Strlcpy(str, sp->word, len/sizeof(Char));
704 free(sp->word);
705 }
706 found = 1;
707 }
708 else {
709 if (str == NULL((void *)0)) {
710 if (aliased)
711 prlex(cshout, lexp);
712 (void) fprintf(csherr,
713 "%s: Command not found.\n", vis_str(sp->word));
714 }
715 else
716 (void) Strlcpy(str, sp->word, len/sizeof(Char));
717 found = 0;
718 }
719 sp->word = s0; /* we save and then restore this */
720 free(cmd);
721 return found;
722}