check for keywords in more places for compat with original shell. also, ansi Cdecls
[unix-history] / usr / src / bin / sh / eval.c
CommitLineData
b3d7414a
KB
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * %sccs.include.redist.c%
9 */
10
11#ifndef lint
ddba57cd 12static char sccsid[] = "@(#)eval.c 5.2 (Berkeley) %G%";
b3d7414a
KB
13#endif /* not lint */
14
15/*
16 * Evaluate a command.
17 */
18
19#include "shell.h"
20#include "nodes.h"
21#include "syntax.h"
22#include "expand.h"
23#include "parser.h"
24#include "jobs.h"
25#include "eval.h"
26#include "builtins.h"
27#include "options.h"
28#include "exec.h"
29#include "redir.h"
30#include "input.h"
31#include "output.h"
32#include "trap.h"
33#include "var.h"
34#include "memalloc.h"
35#include "error.h"
36#include "mystring.h"
37#include <signal.h>
38
39
40/* flags in argument to evaltree */
41#define EV_EXIT 01 /* exit after evaluating tree */
42#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
43#define EV_BACKCMD 04 /* command executing within back quotes */
44
45
46/* reasons for skipping commands (see comment on breakcmd routine) */
47#define SKIPBREAK 1
48#define SKIPCONT 2
49#define SKIPFUNC 3
50
51MKINIT int evalskip; /* set if we are skipping commands */
52STATIC int skipcount; /* number of levels to skip */
53MKINIT int loopnest; /* current loop nesting level */
54int funcnest; /* depth of function calls */
55
56
57char *commandname;
58struct strlist *cmdenviron;
59int exitstatus; /* exit status of last command */
60
61
62#ifdef __STDC__
63STATIC void evalloop(union node *);
64STATIC void evalfor(union node *);
65STATIC void evalcase(union node *, int);
66STATIC void evalsubshell(union node *, int);
67STATIC void expredir(union node *);
68STATIC void evalpipe(union node *);
69STATIC void evalcommand(union node *, int, struct backcmd *);
70STATIC void prehash(union node *);
71#else
72STATIC void evalloop();
73STATIC void evalfor();
74STATIC void evalcase();
75STATIC void evalsubshell();
76STATIC void expredir();
77STATIC void evalpipe();
78STATIC void evalcommand();
79STATIC void prehash();
80#endif
81
82
83
84/*
85 * Called to reset things after an exception.
86 */
87
88#ifdef mkinit
89INCLUDE "eval.h"
90
91RESET {
92 evalskip = 0;
93 loopnest = 0;
94 funcnest = 0;
95}
96
97SHELLPROC {
98 exitstatus = 0;
99}
100#endif
101
102
103
104/*
ddba57cd 105 * The eval commmand.
b3d7414a
KB
106 */
107
ddba57cd
MT
108evalcmd(argc, argv)
109 char **argv;
110{
111 char *p;
112 char *concat;
113 char **ap;
114
115 if (argc > 1) {
116 p = argv[1];
117 if (argc > 2) {
118 STARTSTACKSTR(concat);
119 ap = argv + 2;
120 for (;;) {
121 while (*p)
122 STPUTC(*p++, concat);
123 if ((p = *ap++) == NULL)
124 break;
125 STPUTC(' ', concat);
126 }
127 STPUTC('\0', concat);
128 p = grabstackstr(concat);
129 }
130 evalstring(p);
131 }
132 return exitstatus;
b3d7414a 133}
b3d7414a
KB
134
135
136/*
137 * Execute a command or commands contained in a string.
138 */
139
140void
141evalstring(s)
142 char *s;
143 {
144 union node *n;
145 struct stackmark smark;
146
147 setstackmark(&smark);
148 setinputstring(s, 1);
149 while ((n = parsecmd(0)) != NEOF) {
150 evaltree(n, 0);
151 popstackmark(&smark);
152 }
153 popfile();
154 popstackmark(&smark);
155}
156
157
158
159/*
160 * Evaluate a parse tree. The value is left in the global variable
161 * exitstatus.
162 */
163
164void
165evaltree(n, flags)
166 union node *n;
167 {
168 if (n == NULL) {
169 TRACE(("evaltree(NULL) called\n"));
170 return;
171 }
172 TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type));
173 switch (n->type) {
174 case NSEMI:
175 evaltree(n->nbinary.ch1, 0);
176 if (evalskip)
177 goto out;
178 evaltree(n->nbinary.ch2, flags);
179 break;
180 case NAND:
181 evaltree(n->nbinary.ch1, EV_TESTED);
182 if (evalskip || exitstatus != 0)
183 goto out;
184 evaltree(n->nbinary.ch2, flags);
185 break;
186 case NOR:
187 evaltree(n->nbinary.ch1, EV_TESTED);
188 if (evalskip || exitstatus == 0)
189 goto out;
190 evaltree(n->nbinary.ch2, flags);
191 break;
192 case NREDIR:
193 expredir(n->nredir.redirect);
194 redirect(n->nredir.redirect, REDIR_PUSH);
195 evaltree(n->nredir.n, flags);
196 popredir();
197 break;
198 case NSUBSHELL:
199 evalsubshell(n, flags);
200 break;
201 case NBACKGND:
202 evalsubshell(n, flags);
203 break;
204 case NIF:
205 evaltree(n->nif.test, EV_TESTED);
206 if (evalskip)
207 goto out;
208 if (exitstatus == 0) {
209 evaltree(n->nif.ifpart, flags);
210 } else if (n->nif.elsepart) {
211 evaltree(n->nif.elsepart, flags);
212 }
213 break;
214 case NWHILE:
215 case NUNTIL:
216 evalloop(n);
217 break;
218 case NFOR:
219 evalfor(n);
220 break;
221 case NCASE:
222 evalcase(n, flags);
223 break;
224 case NDEFUN:
225 defun(n->narg.text, n->narg.next);
226 exitstatus = 0;
227 break;
228 case NPIPE:
229 evalpipe(n);
230 break;
231 case NCMD:
232 evalcommand(n, flags, (struct backcmd *)NULL);
233 break;
234 default:
235 out1fmt("Node type = %d\n", n->type);
236 flushout(&output);
237 break;
238 }
239out:
240 if (pendingsigs)
241 dotrap();
242 if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
243 exitshell(exitstatus);
244}
245
246
247STATIC void
248evalloop(n)
249 union node *n;
250 {
251 int status;
252
253 loopnest++;
254 status = 0;
255 for (;;) {
256 evaltree(n->nbinary.ch1, EV_TESTED);
257 if (evalskip) {
258skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
259 evalskip = 0;
260 continue;
261 }
262 if (evalskip == SKIPBREAK && --skipcount <= 0)
263 evalskip = 0;
264 break;
265 }
266 if (n->type == NWHILE) {
267 if (exitstatus != 0)
268 break;
269 } else {
270 if (exitstatus == 0)
271 break;
272 }
273 evaltree(n->nbinary.ch2, 0);
274 status = exitstatus;
275 if (evalskip)
276 goto skipping;
277 }
278 loopnest--;
279 exitstatus = status;
280}
281
282
283
284STATIC void
285evalfor(n)
286 union node *n;
287 {
288 struct arglist arglist;
289 union node *argp;
290 struct strlist *sp;
291 struct stackmark smark;
292
293 setstackmark(&smark);
294 arglist.lastp = &arglist.list;
295 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
296 expandarg(argp, &arglist, 1);
297 if (evalskip)
298 goto out;
299 }
300 *arglist.lastp = NULL;
301
302 exitstatus = 0;
303 loopnest++;
304 for (sp = arglist.list ; sp ; sp = sp->next) {
305 setvar(n->nfor.var, sp->text, 0);
306 evaltree(n->nfor.body, 0);
307 if (evalskip) {
308 if (evalskip == SKIPCONT && --skipcount <= 0) {
309 evalskip = 0;
310 continue;
311 }
312 if (evalskip == SKIPBREAK && --skipcount <= 0)
313 evalskip = 0;
314 break;
315 }
316 }
317 loopnest--;
318out:
319 popstackmark(&smark);
320}
321
322
323
324STATIC void
325evalcase(n, flags)
326 union node *n;
327 {
328 union node *cp;
329 union node *patp;
330 struct arglist arglist;
331 struct stackmark smark;
332
333 setstackmark(&smark);
334 arglist.lastp = &arglist.list;
335 expandarg(n->ncase.expr, &arglist, 0);
336 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
337 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
338 if (casematch(patp, arglist.list->text)) {
339 if (evalskip == 0) {
340 evaltree(cp->nclist.body, flags);
341 }
342 goto out;
343 }
344 }
345 }
346out:
347 popstackmark(&smark);
348}
349
350
351
352/*
353 * Kick off a subshell to evaluate a tree.
354 */
355
356STATIC void
357evalsubshell(n, flags)
358 union node *n;
359 {
360 struct job *jp;
361 int backgnd = (n->type == NBACKGND);
362
363 expredir(n->nredir.redirect);
364 jp = makejob(n, 1);
365 if (forkshell(jp, n, backgnd) == 0) {
366 if (backgnd)
367 flags &=~ EV_TESTED;
368 redirect(n->nredir.redirect, 0);
369 evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
370 }
371 if (! backgnd) {
372 INTOFF;
373 exitstatus = waitforjob(jp);
374 INTON;
375 }
376}
377
378
379
380/*
381 * Compute the names of the files in a redirection list.
382 */
383
384STATIC void
385expredir(n)
386 union node *n;
387 {
388 register union node *redir;
389
390 for (redir = n ; redir ; redir = redir->nfile.next) {
391 if (redir->type == NFROM
392 || redir->type == NTO
393 || redir->type == NAPPEND) {
394 struct arglist fn;
395 fn.lastp = &fn.list;
396 expandarg(redir->nfile.fname, &fn, 0);
397 redir->nfile.expfname = fn.list->text;
398 }
399 }
400}
401
402
403
404/*
405 * Evaluate a pipeline. All the processes in the pipeline are children
406 * of the process creating the pipeline. (This differs from some versions
407 * of the shell, which make the last process in a pipeline the parent
408 * of all the rest.)
409 */
410
411STATIC void
412evalpipe(n)
413 union node *n;
414 {
415 struct job *jp;
416 struct nodelist *lp;
417 int pipelen;
418 int prevfd;
419 int pip[2];
420
421 TRACE(("evalpipe(0x%x) called\n", (int)n));
422 pipelen = 0;
423 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
424 pipelen++;
425 INTOFF;
426 jp = makejob(n, pipelen);
427 prevfd = -1;
428 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
429 prehash(lp->n);
430 pip[1] = -1;
431 if (lp->next) {
432 if (pipe(pip) < 0) {
433 close(prevfd);
434 error("Pipe call failed");
435 }
436 }
437 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
438 INTON;
439 if (prevfd > 0) {
440 close(0);
441 copyfd(prevfd, 0);
442 close(prevfd);
443 }
444 if (pip[1] >= 0) {
445 close(pip[0]);
446 if (pip[1] != 1) {
447 close(1);
448 copyfd(pip[1], 1);
449 close(pip[1]);
450 }
451 }
452 evaltree(lp->n, EV_EXIT);
453 }
454 if (prevfd >= 0)
455 close(prevfd);
456 prevfd = pip[0];
457 close(pip[1]);
458 }
459 INTON;
460 if (n->npipe.backgnd == 0) {
461 INTOFF;
462 exitstatus = waitforjob(jp);
463 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
464 INTON;
465 }
466}
467
468
469
470/*
471 * Execute a command inside back quotes. If it's a builtin command, we
472 * want to save its output in a block obtained from malloc. Otherwise
473 * we fork off a subprocess and get the output of the command via a pipe.
474 * Should be called with interrupts off.
475 */
476
477void
478evalbackcmd(n, result)
479 union node *n;
480 struct backcmd *result;
481 {
482 int pip[2];
483 struct job *jp;
484 struct stackmark smark; /* unnecessary */
485
486 setstackmark(&smark);
487 result->fd = -1;
488 result->buf = NULL;
489 result->nleft = 0;
490 result->jp = NULL;
491 if (n->type == NCMD) {
492 evalcommand(n, EV_BACKCMD, result);
493 } else {
494 if (pipe(pip) < 0)
495 error("Pipe call failed");
496 jp = makejob(n, 1);
497 if (forkshell(jp, n, FORK_NOJOB) == 0) {
498 FORCEINTON;
499 close(pip[0]);
500 if (pip[1] != 1) {
501 close(1);
502 copyfd(pip[1], 1);
503 close(pip[1]);
504 }
505 evaltree(n, EV_EXIT);
506 }
507 close(pip[1]);
508 result->fd = pip[0];
509 result->jp = jp;
510 }
511 popstackmark(&smark);
512 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
513 result->fd, result->buf, result->nleft, result->jp));
514}
515
516
517
518/*
519 * Execute a simple command.
520 */
521
522STATIC void
523evalcommand(cmd, flags, backcmd)
524 union node *cmd;
525 struct backcmd *backcmd;
526 {
527 struct stackmark smark;
528 union node *argp;
529 struct arglist arglist;
530 struct arglist varlist;
531 char **argv;
532 int argc;
533 char **envp;
534 int varflag;
535 struct strlist *sp;
536 register char *p;
537 int mode;
538 int pip[2];
539 struct cmdentry cmdentry;
540 struct job *jp;
541 struct jmploc jmploc;
542 struct jmploc *volatile savehandler;
543 char *volatile savecmdname;
544 volatile struct shparam saveparam;
545 struct localvar *volatile savelocalvars;
546 volatile int e;
547 char *lastarg;
548
549 /* First expand the arguments. */
550 TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags));
551 setstackmark(&smark);
552 arglist.lastp = &arglist.list;
553 varlist.lastp = &varlist.list;
554 varflag = 1;
555 for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
556 p = argp->narg.text;
557 if (varflag && is_name(*p)) {
558 do {
559 p++;
560 } while (is_in_name(*p));
561 if (*p == '=') {
562 expandarg(argp, &varlist, 0);
563 continue;
564 }
565 }
566 expandarg(argp, &arglist, 1);
567 varflag = 0;
568 }
569 *arglist.lastp = NULL;
570 *varlist.lastp = NULL;
571 expredir(cmd->ncmd.redirect);
572 argc = 0;
573 for (sp = arglist.list ; sp ; sp = sp->next)
574 argc++;
575 argv = stalloc(sizeof (char *) * (argc + 1));
576 for (sp = arglist.list ; sp ; sp = sp->next)
577 *argv++ = sp->text;
578 *argv = NULL;
579 lastarg = NULL;
580 if (iflag && funcnest == 0 && argc > 0)
581 lastarg = argv[-1];
582 argv -= argc;
583
584 /* Print the command if xflag is set. */
585 if (xflag) {
586 outc('+', &errout);
587 for (sp = varlist.list ; sp ; sp = sp->next) {
588 outc(' ', &errout);
589 out2str(sp->text);
590 }
591 for (sp = arglist.list ; sp ; sp = sp->next) {
592 outc(' ', &errout);
593 out2str(sp->text);
594 }
595 outc('\n', &errout);
596 flushout(&errout);
597 }
598
599 /* Now locate the command. */
600 if (argc == 0) {
601 cmdentry.cmdtype = CMDBUILTIN;
602 cmdentry.u.index = BLTINCMD;
603 } else {
604 find_command(argv[0], &cmdentry, 1);
605 if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
606 exitstatus = 2;
607 flushout(&errout);
608 return;
609 }
610 /* implement the bltin builtin here */
611 if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
612 for (;;) {
613 argv++;
614 if (--argc == 0)
615 break;
616 if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
617 outfmt(&errout, "%s: not found\n", *argv);
618 exitstatus = 2;
619 flushout(&errout);
620 return;
621 }
622 if (cmdentry.u.index != BLTINCMD)
623 break;
624 }
625 }
626 }
627
628 /* Fork off a child process if necessary. */
629 if (cmd->ncmd.backgnd
630 || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0
631 || (flags & EV_BACKCMD) != 0
632 && (cmdentry.cmdtype != CMDBUILTIN
633 || cmdentry.u.index == DOTCMD
634 || cmdentry.u.index == EVALCMD)) {
635 jp = makejob(cmd, 1);
636 mode = cmd->ncmd.backgnd;
637 if (flags & EV_BACKCMD) {
638 mode = FORK_NOJOB;
639 if (pipe(pip) < 0)
640 error("Pipe call failed");
641 }
642 if (forkshell(jp, cmd, mode) != 0)
643 goto parent; /* at end of routine */
644 if (flags & EV_BACKCMD) {
645 FORCEINTON;
646 close(pip[0]);
647 if (pip[1] != 1) {
648 close(1);
649 copyfd(pip[1], 1);
650 close(pip[1]);
651 }
652 }
653 flags |= EV_EXIT;
654 }
655
656 /* This is the child process if a fork occurred. */
657 /* Execute the command. */
658 if (cmdentry.cmdtype == CMDFUNCTION) {
659 trputs("Shell function: "); trargs(argv);
660 redirect(cmd->ncmd.redirect, REDIR_PUSH);
661 saveparam = shellparam;
662 shellparam.malloc = 0;
663 shellparam.nparam = argc - 1;
664 shellparam.p = argv + 1;
665 shellparam.optnext = NULL;
666 INTOFF;
667 savelocalvars = localvars;
668 localvars = NULL;
669 INTON;
670 if (setjmp(jmploc.loc)) {
671 if (exception == EXSHELLPROC)
672 freeparam((struct shparam *)&saveparam);
673 else {
674 freeparam(&shellparam);
675 shellparam = saveparam;
676 }
677 poplocalvars();
678 localvars = savelocalvars;
679 handler = savehandler;
680 longjmp(handler->loc, 1);
681 }
682 savehandler = handler;
683 handler = &jmploc;
684 for (sp = varlist.list ; sp ; sp = sp->next)
685 mklocal(sp->text);
686 funcnest++;
687 evaltree(cmdentry.u.func, 0);
688 funcnest--;
689 INTOFF;
690 poplocalvars();
691 localvars = savelocalvars;
692 freeparam(&shellparam);
693 shellparam = saveparam;
694 handler = savehandler;
695 popredir();
696 INTON;
697 if (evalskip == SKIPFUNC) {
698 evalskip = 0;
699 skipcount = 0;
700 }
701 if (flags & EV_EXIT)
702 exitshell(exitstatus);
703 } else if (cmdentry.cmdtype == CMDBUILTIN) {
704 trputs("builtin command: "); trargs(argv);
705 mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
706 if (flags == EV_BACKCMD) {
707 memout.nleft = 0;
708 memout.nextc = memout.buf;
709 memout.bufsize = 64;
710 mode |= REDIR_BACKQ;
711 }
712 redirect(cmd->ncmd.redirect, mode);
713 savecmdname = commandname;
714 cmdenviron = varlist.list;
715 e = -1;
716 if (setjmp(jmploc.loc)) {
717 e = exception;
718 exitstatus = (e == EXINT)? SIGINT+128 : 2;
719 goto cmddone;
720 }
721 savehandler = handler;
722 handler = &jmploc;
723 commandname = argv[0];
724 argptr = argv + 1;
725 optptr = NULL; /* initialize nextopt */
726 exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
727 flushall();
728cmddone:
729 out1 = &output;
730 out2 = &errout;
731 freestdout();
732 if (e != EXSHELLPROC) {
733 commandname = savecmdname;
734 if (flags & EV_EXIT) {
735 exitshell(exitstatus);
736 }
737 }
738 handler = savehandler;
739 if (e != -1) {
740 if (e != EXERROR || cmdentry.u.index == BLTINCMD
741 || cmdentry.u.index == DOTCMD
742 || cmdentry.u.index == EVALCMD
743 || cmdentry.u.index == EXECCMD)
744 exraise(e);
745 FORCEINTON;
746 }
747 if (cmdentry.u.index != EXECCMD)
748 popredir();
749 if (flags == EV_BACKCMD) {
750 backcmd->buf = memout.buf;
751 backcmd->nleft = memout.nextc - memout.buf;
752 memout.buf = NULL;
753 }
754 } else {
755 trputs("normal command: "); trargs(argv);
756 clearredir();
757 redirect(cmd->ncmd.redirect, 0);
758 if (varlist.list) {
759 p = stalloc(strlen(pathval()) + 1);
760 scopy(pathval(), p);
761 } else {
762 p = pathval();
763 }
764 for (sp = varlist.list ; sp ; sp = sp->next)
765 setvareq(sp->text, VEXPORT|VSTACK);
766 envp = environment();
767 shellexec(argv, envp, p, cmdentry.u.index);
768 /*NOTREACHED*/
769 }
770 goto out;
771
772parent: /* parent process gets here (if we forked) */
773 if (mode == 0) { /* argument to fork */
774 INTOFF;
775 exitstatus = waitforjob(jp);
776 INTON;
777 } else if (mode == 2) {
778 backcmd->fd = pip[0];
779 close(pip[1]);
780 backcmd->jp = jp;
781 }
782
783out:
784 if (lastarg)
785 setvar("_", lastarg, 0);
786 popstackmark(&smark);
787}
788
789
790
791/*
792 * Search for a command. This is called before we fork so that the
793 * location of the command will be available in the parent as well as
794 * the child. The check for "goodname" is an overly conservative
795 * check that the name will not be subject to expansion.
796 */
797
798STATIC void
799prehash(n)
800 union node *n;
801 {
802 struct cmdentry entry;
803
804 if (n->type == NCMD && goodname(n->ncmd.args->narg.text))
805 find_command(n->ncmd.args->narg.text, &entry, 0);
806}
807
808
809
810/*
811 * Builtin commands. Builtin commands whose functions are closely
812 * tied to evaluation are implemented here.
813 */
814
815/*
816 * No command given, or a bltin command with no arguments. Set the
817 * specified variables.
818 */
819
820bltincmd(argc, argv) char **argv; {
821 listsetvar(cmdenviron);
822 return exitstatus;
823}
824
825
826/*
827 * Handle break and continue commands. Break, continue, and return are
828 * all handled by setting the evalskip flag. The evaluation routines
829 * above all check this flag, and if it is set they start skipping
830 * commands rather than executing them. The variable skipcount is
831 * the number of loops to break/continue, or the number of function
832 * levels to return. (The latter is always 1.) It should probably
833 * be an error to break out of more loops than exist, but it isn't
834 * in the standard shell so we don't make it one here.
835 */
836
837breakcmd(argc, argv) char **argv; {
838 int n;
839
840 n = 1;
841 if (argc > 1)
842 n = number(argv[1]);
843 if (n > loopnest)
844 n = loopnest;
845 if (n > 0) {
846 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
847 skipcount = n;
848 }
849 return 0;
850}
851
852
853/*
854 * The return command.
855 */
856
857returncmd(argc, argv) char **argv; {
858 int ret;
859
860 ret = exitstatus;
861 if (argc > 1)
862 ret = number(argv[1]);
863 if (funcnest) {
864 evalskip = SKIPFUNC;
865 skipcount = 1;
866 }
867 return ret;
868}
869
870
871truecmd(argc, argv) char **argv; {
872 return 0;
873}
874
875
876execcmd(argc, argv) char **argv; {
877 if (argc > 1) {
878 iflag = 0; /* exit on error */
879 setinteractive(0);
880#if JOBS
881 jflag = 0;
882 setjobctl(0);
883#endif
884 shellexec(argv + 1, environment(), pathval(), 0);
885
886 }
887 return 0;
888}