xref: /openbsd/bin/csh/exec.c (revision db3296cf)
1 /*	$OpenBSD: exec.c,v 1.13 2003/06/11 21:09:50 deraadt 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 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)exec.c	8.3 (Berkeley) 5/23/95";
36 #else
37 static char rcsid[] = "$OpenBSD: exec.c,v 1.13 2003/06/11 21:09:50 deraadt Exp $";
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <dirent.h>
44 #include <fcntl.h>
45 #include <sys/stat.h>
46 #include <errno.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <stdarg.h>
51 
52 #include "csh.h"
53 #include "extern.h"
54 
55 /*
56  * System level search and execute of a command.  We look in each directory
57  * for the specified command name.  If the name contains a '/' then we
58  * execute only the full path name.  If there is no search path then we
59  * execute only full path names.
60  */
61 extern char **environ;
62 
63 /*
64  * As we search for the command we note the first non-trivial error
65  * message for presentation to the user.  This allows us often
66  * to show that a file has the wrong mode/no access when the file
67  * is not in the last component of the search path, so we must
68  * go on after first detecting the error.
69  */
70 static char *exerr;		/* Execution error message */
71 static Char *expath;		/* Path for exerr */
72 
73 /*
74  * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
75  * to hash execs.  If it is allocated (havhash true), then to tell
76  * whether ``name'' is (possibly) present in the i'th component
77  * of the variable path, you look at the bit in xhash indexed by
78  * hash(hashname("name"), i).  This is setup automatically
79  * after .login is executed, and recomputed whenever ``path'' is
80  * changed.
81  * The two part hash function is designed to let texec() call the
82  * more expensive hashname() only once and the simple hash() several
83  * times (once for each path component checked).
84  * Byte size is assumed to be 8.
85  */
86 #define	HSHSIZ		8192	/* 1k bytes */
87 #define HSHMASK		(HSHSIZ - 1)
88 #define HSHMUL		243
89 static char xhash[HSHSIZ / 8];
90 
91 #define hash(a, b)	(((a) * HSHMUL + (b)) & HSHMASK)
92 #define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
93 #define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
94 static int hits, misses;
95 
96 /* Dummy search path for just absolute search when no path */
97 static Char *justabs[] = {STRNULL, 0};
98 
99 static void	pexerr(void);
100 static void	texec(Char *, Char **);
101 static int	hashname(Char *);
102 static int 	tellmewhat(struct wordent *, Char *, int len);
103 static int	executable(Char *, Char *, bool);
104 static int	iscommand(Char *);
105 
106 
107 void
108 /*ARGSUSED*/
109 doexec(Char **v, struct command *t)
110 {
111     Char *dp, **pv, **av, *sav;
112     struct varent *pathv;
113     bool slash;
114     int hashval = 0, hashval1, i;
115     Char   *blk[2];
116     sigset_t sigset;
117 
118     /*
119      * Glob the command name. We will search $path even if this does something,
120      * as in sh but not in csh.  One special case: if there is no PATH, then we
121      * execute only commands which start with '/'.
122      */
123     blk[0] = t->t_dcom[0];
124     blk[1] = 0;
125     gflag = 0, tglob(blk);
126     if (gflag) {
127 	pv = globall(blk);
128 	if (pv == 0) {
129 	    setname(vis_str(blk[0]));
130 	    stderror(ERR_NAME | ERR_NOMATCH);
131 	}
132 	gargv = 0;
133     }
134     else
135 	pv = saveblk(blk);
136 
137     trim(pv);
138 
139     exerr = 0;
140     expath = Strsave(pv[0]);
141     Vexpath = expath;
142 
143     pathv = adrof(STRpath);
144     if (pathv == 0 && expath[0] != '/') {
145 	blkfree(pv);
146 	pexerr();
147     }
148     slash = any(short2str(expath), '/');
149 
150     /*
151      * Glob the argument list, if necessary. Otherwise trim off the quote bits.
152      */
153     gflag = 0;
154     av = &t->t_dcom[1];
155     tglob(av);
156     if (gflag) {
157 	av = globall(av);
158 	if (av == 0) {
159 	    blkfree(pv);
160 	    setname(vis_str(expath));
161 	    stderror(ERR_NAME | ERR_NOMATCH);
162 	}
163 	gargv = 0;
164     }
165     else
166 	av = saveblk(av);
167 
168     blkfree(t->t_dcom);
169     t->t_dcom = blkspl(pv, av);
170     xfree((ptr_t) pv);
171     xfree((ptr_t) av);
172     av = t->t_dcom;
173     trim(av);
174 
175     if (*av == NULL || **av == '\0')
176 	pexerr();
177 
178     xechoit(av);		/* Echo command if -x */
179     /*
180      * Since all internal file descriptors are set to close on exec, we don't
181      * need to close them explicitly here.  Just reorient ourselves for error
182      * messages.
183      */
184     SHIN = 0;
185     SHOUT = 1;
186     SHERR = 2;
187     OLDSTD = 0;
188     /*
189      * We must do this AFTER any possible forking (like `foo` in glob) so that
190      * this shell can still do subprocesses.
191      */
192     sigemptyset(&sigset);
193     sigprocmask(SIG_SETMASK, &sigset, NULL);
194     /*
195      * If no path, no words in path, or a / in the filename then restrict the
196      * command search.
197      */
198     if (pathv == 0 || pathv->vec[0] == 0 || slash)
199 	pv = justabs;
200     else
201 	pv = pathv->vec;
202     sav = Strspl(STRslash, *av);/* / command name for postpending */
203     Vsav = sav;
204     if (havhash)
205 	hashval = hashname(*av);
206     i = 0;
207     hits++;
208     do {
209 	/*
210 	 * Try to save time by looking at the hash table for where this command
211 	 * could be.  If we are doing delayed hashing, then we put the names in
212 	 * one at a time, as the user enters them.  This is kinda like Korn
213 	 * Shell's "tracked aliases".
214 	 */
215 	if (!slash && pv[0][0] == '/' && havhash) {
216 	    hashval1 = hash(hashval, i);
217 	    if (!bit(xhash, hashval1))
218 		goto cont;
219 	}
220 	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
221 	    texec(*av, av);
222 	else {
223 	    dp = Strspl(*pv, sav);
224 	    Vdp = dp;
225 	    texec(dp, av);
226 	    Vdp = 0;
227 	    xfree((ptr_t) dp);
228 	}
229 	misses++;
230 cont:
231 	pv++;
232 	i++;
233     } while (*pv);
234     hits--;
235     Vsav = 0;
236     xfree((ptr_t) sav);
237     pexerr();
238 }
239 
240 static void
241 pexerr(void)
242 {
243     /* Couldn't find the damn thing */
244     if (expath) {
245 	setname(vis_str(expath));
246 	Vexpath = 0;
247 	xfree((ptr_t) expath);
248 	expath = 0;
249     }
250     else
251 	setname("");
252     if (exerr)
253 	stderror(ERR_NAME | ERR_STRING, exerr);
254     stderror(ERR_NAME | ERR_COMMAND);
255 }
256 
257 /*
258  * Execute command f, arg list t.
259  * Record error message if not found.
260  * Also do shell scripts here.
261  */
262 static void
263 texec(Char *sf, Char **st)
264 {
265     char **t;
266     char *f;
267     struct varent *v;
268     Char **vp;
269     Char   *lastsh[2];
270     int     fd;
271     unsigned char c;
272     Char   *st0, **ost;
273 
274     /* The order for the conversions is significant */
275     t = short2blk(st);
276     f = short2str(sf);
277     Vt = t;
278     errno = 0;			/* don't use a previous error */
279     (void) execve(f, t, environ);
280     Vt = 0;
281     blkfree((Char **) t);
282     switch (errno) {
283 
284     case ENOEXEC:
285 	/*
286 	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
287 	 * it, don't feed it to the shell if it looks like a binary!
288 	 */
289 	if ((fd = open(f, O_RDONLY)) != -1) {
290 	    if (read(fd, (char *) &c, 1) == 1) {
291 		if (!Isprint(c) && (c != '\n' && c != '\t')) {
292 		    (void) close(fd);
293 		    /*
294 		     * We *know* what ENOEXEC means.
295 		     */
296 		    stderror(ERR_ARCH, f, strerror(errno));
297 		}
298 	    }
299 #ifdef _PATH_BSHELL
300 	    else
301 		c = '#';
302 #endif
303 	    (void) close(fd);
304 	}
305 	/*
306 	 * If there is an alias for shell, then put the words of the alias in
307 	 * front of the argument list replacing the command name. Note no
308 	 * interpretation of the words at this point.
309 	 */
310 	v = adrof1(STRshell, &aliases);
311 	if (v == 0) {
312 	    vp = lastsh;
313 	    vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH;
314 	    vp[1] = NULL;
315 #ifdef _PATH_BSHELL
316 	    if (fd != -1 && c != '#')
317 		vp[0] = STR_BSHELL;
318 #endif
319 	}
320 	else
321 	    vp = v->vec;
322 	st0 = st[0];
323 	st[0] = sf;
324 	ost = st;
325 	st = blkspl(vp, st);	/* Splice up the new arglst */
326 	ost[0] = st0;
327 	sf = *st;
328 	/* The order for the conversions is significant */
329 	t = short2blk(st);
330 	f = short2str(sf);
331 	xfree((ptr_t) st);
332 	Vt = t;
333 	(void) execve(f, t, environ);
334 	Vt = 0;
335 	blkfree((Char **) t);
336 	/* The sky is falling, the sky is falling! */
337 
338     case ENOMEM:
339 	stderror(ERR_SYSTEM, f, strerror(errno));
340 
341     case ENOENT:
342 	break;
343 
344     default:
345 	if (exerr == 0) {
346 	    exerr = strerror(errno);
347 	    if (expath)
348 		xfree((ptr_t) expath);
349 	    expath = Strsave(sf);
350 	    Vexpath = expath;
351 	}
352     }
353 }
354 
355 /*ARGSUSED*/
356 void
357 execash(Char **t, struct command *kp)
358 {
359     int     saveIN, saveOUT, saveDIAG, saveSTD;
360     int     oSHIN;
361     int     oSHOUT;
362     int     oSHERR;
363     int     oOLDSTD;
364     jmp_buf osetexit;
365     int	    my_reenter;
366     int     odidfds;
367     sig_t   osigint, osigquit, osigterm;
368 
369     if (chkstop == 0 && setintr)
370 	panystop(0);
371     /*
372      * Hmm, we don't really want to do that now because we might
373      * fail, but what is the choice
374      */
375     rechist();
376 
377     osigint  = signal(SIGINT, parintr);
378     osigquit = signal(SIGQUIT, parintr);
379     osigterm = signal(SIGTERM, parterm);
380 
381     odidfds = didfds;
382     oSHIN = SHIN;
383     oSHOUT = SHOUT;
384     oSHERR = SHERR;
385     oOLDSTD = OLDSTD;
386 
387     saveIN = dcopy(SHIN, -1);
388     saveOUT = dcopy(SHOUT, -1);
389     saveDIAG = dcopy(SHERR, -1);
390     saveSTD = dcopy(OLDSTD, -1);
391 
392     lshift(kp->t_dcom, 1);
393 
394     getexit(osetexit);
395 
396     if ((my_reenter = setexit()) == 0) {
397 	SHIN = dcopy(0, -1);
398 	SHOUT = dcopy(1, -1);
399 	SHERR = dcopy(2, -1);
400 	didfds = 0;
401 	doexec(t, kp);
402     }
403 
404     (void) signal(SIGINT, osigint);
405     (void) signal(SIGQUIT, osigquit);
406     (void) signal(SIGTERM, osigterm);
407 
408     doneinp = 0;
409     didfds = odidfds;
410     (void) close(SHIN);
411     (void) close(SHOUT);
412     (void) close(SHERR);
413     (void) close(OLDSTD);
414     SHIN = dmove(saveIN, oSHIN);
415     SHOUT = dmove(saveOUT, oSHOUT);
416     SHERR = dmove(saveDIAG, oSHERR);
417     OLDSTD = dmove(saveSTD, oOLDSTD);
418 
419     resexit(osetexit);
420     if (my_reenter)
421 	stderror(ERR_SILENT);
422 }
423 
424 void
425 xechoit(Char **t)
426 {
427     if (adrof(STRecho)) {
428 	(void) fflush(csherr);
429 	blkpr(csherr, t);
430 	(void) fputc('\n', csherr);
431     }
432 }
433 
434 void
435 /*ARGSUSED*/
436 dohash(Char **v, struct command *t)
437 {
438     DIR    *dirp;
439     struct dirent *dp;
440     int cnt;
441     int     i = 0;
442     struct varent *pathv = adrof(STRpath);
443     Char  **pv;
444     int     hashval;
445 
446     havhash = 1;
447     for (cnt = 0; cnt < sizeof xhash; cnt++)
448 	xhash[cnt] = 0;
449     if (pathv == 0)
450 	return;
451     for (pv = pathv->vec; *pv; pv++, i++) {
452 	if (pv[0][0] != '/')
453 	    continue;
454 	dirp = opendir(short2str(*pv));
455 	if (dirp == NULL)
456 	    continue;
457 	while ((dp = readdir(dirp)) != NULL) {
458 	    if (dp->d_ino == 0)
459 		continue;
460 	    if (dp->d_name[0] == '.' &&
461 		(dp->d_name[1] == '\0' ||
462 		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
463 		continue;
464 	    hashval = hash(hashname(str2short(dp->d_name)), i);
465 	    bis(xhash, hashval);
466 	    /* tw_add_comm_name (dp->d_name); */
467 	}
468 	(void) closedir(dirp);
469     }
470 }
471 
472 void
473 /*ARGSUSED*/
474 dounhash(Char **v, struct command *t)
475 {
476     havhash = 0;
477 }
478 
479 void
480 /*ARGSUSED*/
481 hashstat(Char **v, struct command *t)
482 {
483     if (hits + misses)
484 	(void) fprintf(cshout, "%d hits, %d misses, %d%%\n",
485 		       hits, misses, 100 * hits / (hits + misses));
486 }
487 
488 /*
489  * Hash a command name.
490  */
491 static int
492 hashname(Char *cp)
493 {
494     long h = 0;
495 
496     while (*cp)
497 	h = hash(h, *cp++);
498     return ((int) h);
499 }
500 
501 static int
502 iscommand(Char *name)
503 {
504     Char **pv;
505     Char *sav;
506     struct varent *v;
507     bool slash = any(short2str(name), '/');
508     int hashval = 0, hashval1, i;
509 
510     v = adrof(STRpath);
511     if (v == 0 || v->vec[0] == 0 || slash)
512 	pv = justabs;
513     else
514 	pv = v->vec;
515     sav = Strspl(STRslash, name);	/* / command name for postpending */
516     if (havhash)
517 	hashval = hashname(name);
518     i = 0;
519     do {
520 	if (!slash && pv[0][0] == '/' && havhash) {
521 	    hashval1 = hash(hashval, i);
522 	    if (!bit(xhash, hashval1))
523 		goto cont;
524 	}
525 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
526 	    if (executable(NULL, name, 0)) {
527 		xfree((ptr_t) sav);
528 		return i + 1;
529 	    }
530 	}
531 	else {
532 	    if (executable(*pv, sav, 0)) {
533 		xfree((ptr_t) sav);
534 		return i + 1;
535 	    }
536 	}
537 cont:
538 	pv++;
539 	i++;
540     } while (*pv);
541     xfree((ptr_t) sav);
542     return 0;
543 }
544 
545 /* Also by:
546  *  Andreas Luik <luik@isaak.isa.de>
547  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
548  *  Azenberstr. 35
549  *  D-7000 Stuttgart 1
550  *  West-Germany
551  * is the executable() routine below and changes to iscommand().
552  * Thanks again!!
553  */
554 
555 /*
556  * executable() examines the pathname obtained by concatenating dir and name
557  * (dir may be NULL), and returns 1 either if it is executable by us, or
558  * if dir_ok is set and the pathname refers to a directory.
559  * This is a bit kludgy, but in the name of optimization...
560  */
561 static int
562 executable(Char *dir, Char *name, bool dir_ok)
563 {
564     struct stat stbuf;
565     Char    path[MAXPATHLEN], *dp, *sp;
566     char   *strname;
567 
568     if (dir && *dir) {
569 	for (dp = path, sp = dir; *sp; *dp++ = *sp++)
570 	    if (dp == &path[MAXPATHLEN]) {
571 		*--dp = '\0';
572 		break;
573 	    }
574 	for (sp = name; *sp; *dp++ = *sp++)
575 	    if (dp == &path[MAXPATHLEN]) {
576 		*--dp = '\0';
577 		break;
578 	    }
579 	*dp = '\0';
580 	strname = short2str(path);
581     }
582     else
583 	strname = short2str(name);
584     return (stat(strname, &stbuf) != -1 &&
585 	    ((S_ISREG(stbuf.st_mode) &&
586     /* save time by not calling access() in the hopeless case */
587 	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
588 	      access(strname, X_OK) == 0) ||
589 	     (dir_ok && S_ISDIR(stbuf.st_mode))));
590 }
591 
592 /* The dowhich() is by:
593  *  Andreas Luik <luik@isaak.isa.de>
594  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
595  *  Azenberstr. 35
596  *  D-7000 Stuttgart 1
597  *  West-Germany
598  * Thanks!!
599  */
600 /*ARGSUSED*/
601 void
602 dowhich(Char **v, struct command *c)
603 {
604     struct wordent lex[3];
605     struct varent *vp;
606 
607     lex[0].next = &lex[1];
608     lex[1].next = &lex[2];
609     lex[2].next = &lex[0];
610 
611     lex[0].prev = &lex[2];
612     lex[1].prev = &lex[0];
613     lex[2].prev = &lex[1];
614 
615     lex[0].word = STRNULL;
616     lex[2].word = STRret;
617 
618     while (*++v) {
619 	if ((vp = adrof1(*v, &aliases)) != NULL) {
620 	    (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v));
621 	    blkpr(cshout, vp->vec);
622 	    (void) fputc('\n', cshout);
623 	    set(STRstatus, Strsave(STR0));
624 	}
625 	else {
626 	    lex[1].word = *v;
627 	    set(STRstatus, Strsave(tellmewhat(lex, NULL, 0) ? STR0 : STR1));
628 	}
629     }
630 }
631 
632 static int
633 tellmewhat(struct wordent *lexp, Char *str, int len)
634 {
635     int i;
636     struct biltins *bptr;
637     struct wordent *sp = lexp->next;
638     bool    aliased = 0, found;
639     Char   *s0, *s1, *s2, *cmd;
640     Char    qc;
641 
642     if (adrof1(sp->word, &aliases)) {
643 	alias(lexp);
644 	sp = lexp->next;
645 	aliased = 1;
646     }
647 
648     s0 = sp->word;		/* to get the memory freeing right... */
649 
650     /* handle quoted alias hack */
651     if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
652 	(sp->word)++;
653 
654     /* do quoting, if it hasn't been done */
655     s1 = s2 = sp->word;
656     while (*s2)
657 	switch (*s2) {
658 	case '\'':
659 	case '"':
660 	    qc = *s2++;
661 	    while (*s2 && *s2 != qc)
662 		*s1++ = *s2++ | QUOTE;
663 	    if (*s2)
664 		s2++;
665 	    break;
666 	case '\\':
667 	    if (*++s2)
668 		*s1++ = *s2++ | QUOTE;
669 	    break;
670 	default:
671 	    *s1++ = *s2++;
672 	}
673     *s1 = '\0';
674 
675     for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
676 	if (eq(sp->word, str2short(bptr->bname))) {
677 	    if (str == NULL) {
678 	        if (aliased)
679 		        prlex(cshout, lexp);
680 	        (void) fprintf(cshout, "%s: shell built-in command.\n",
681 			   vis_str(sp->word));
682 	    }
683 	    else
684 		(void) Strlcpy(str, sp->word, len/sizeof(Char));
685 	    sp->word = s0;	/* we save and then restore this */
686 	    return 1;
687 	}
688     }
689 
690     sp->word = cmd = globone(sp->word, G_IGNORE);
691 
692     if ((i = iscommand(sp->word)) != 0) {
693 	Char **pv;
694 	struct varent *v;
695 	bool    slash = any(short2str(sp->word), '/');
696 
697 	v = adrof(STRpath);
698 	if (v == 0 || v->vec[0] == 0 || slash)
699 	    pv = justabs;
700 	else
701 	    pv = v->vec;
702 
703 	while (--i)
704 	    pv++;
705 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
706 	    if (!slash) {
707 		sp->word = Strspl(STRdotsl, sp->word);
708 		prlex(cshout, lexp);
709 		xfree((ptr_t) sp->word);
710 	    }
711 	    else
712 		prlex(cshout, lexp);
713 	}
714 	else {
715 	    s1 = Strspl(*pv, STRslash);
716 	    sp->word = Strspl(s1, sp->word);
717 	    xfree((ptr_t) s1);
718 	    if (str == NULL)
719 		prlex(cshout, lexp);
720 	    else
721 		(void) Strlcpy(str, sp->word, len/sizeof(Char));
722 	    xfree((ptr_t) sp->word);
723         }
724 	found = 1;
725     }
726     else {
727  	if (str == NULL) {
728 	    if (aliased)
729 		prlex(cshout, lexp);
730 	    (void) fprintf(csherr,
731 			   "%s: Command not found.\n", vis_str(sp->word));
732 	}
733 	else
734 	    (void) Strlcpy(str, sp->word, len/sizeof(Char));
735 	found = 0;
736     }
737     sp->word = s0;		/* we save and then restore this */
738     xfree((ptr_t) cmd);
739     return found;
740 }
741