1 /* $NetBSD: eval.c,v 1.110 2015/01/02 19:56:20 christos Exp $ */
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/cdefs.h>
38 static char sccsid
[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95";
40 __RCSID("$NetBSD: eval.c,v 1.110 2015/01/02 19:56:20 christos Exp $");
51 #include <sys/fcntl.h>
52 #include <sys/times.h>
53 #include <sys/param.h>
54 #include <sys/types.h>
56 #include <sys/sysctl.h>
83 #include "myhistedit.h"
87 /* flags in argument to evaltree */
88 #define EV_EXIT 01 /* exit after evaluating tree */
89 #define EV_TESTED 02 /* exit status is checked; ignore -e flag */
90 #define EV_BACKCMD 04 /* command executing within back quotes */
92 STATIC
enum skipstate evalskip
; /* != SKIPNONE if we are skipping commands */
93 STATIC
int skipcount
; /* number of levels to skip */
94 STATIC
int loopnest
; /* current loop nesting level */
95 STATIC
int funcnest
; /* depth of function calls */
96 STATIC
int builtin_flags
; /* evalcommand flags for builtins */
98 * Base function nesting level inside a dot command. Set to 0 initially
99 * and to (funcnest + 1) before every dot command to enable
100 * 1) detection of being in a file sourced by a dot command and
101 * 2) counting of function nesting in that file for the implementation
102 * of the return command.
103 * The value is reset to its previous value after the dot command.
105 STATIC
int dot_funcnest
;
108 const char *commandname
;
109 struct strlist
*cmdenviron
;
110 int exitstatus
; /* exit status of last command */
111 int back_exitstatus
; /* exit status of backquoted command */
114 STATIC
void evalloop(union node
*, int);
115 STATIC
void evalfor(union node
*, int);
116 STATIC
void evalcase(union node
*, int);
117 STATIC
void evalsubshell(union node
*, int);
118 STATIC
void expredir(union node
*);
119 STATIC
void evalpipe(union node
*);
120 STATIC
void evalcommand(union node
*, int, struct backcmd
*);
121 STATIC
void prehash(union node
*);
123 STATIC
char *find_dot_file(char *);
126 * Called to reset things after an exception.
159 nfd
= fcntl(fds
[0], F_DUPFD
, 3);
167 nfd
= fcntl(fds
[1], F_DUPFD
, 3);
182 evalcmd(int argc
, char **argv
)
191 STARTSTACKSTR(concat
);
195 STPUTC(*p
++, concat
);
196 if ((p
= *ap
++) == NULL
)
200 STPUTC('\0', concat
);
201 p
= grabstackstr(concat
);
203 evalstring(p
, builtin_flags
& EV_TESTED
);
210 * Execute a command or commands contained in a string.
214 evalstring(char *s
, int flag
)
217 struct stackmark smark
;
219 setstackmark(&smark
);
220 setinputstring(s
, 1);
222 while ((n
= parsecmd(0)) != NEOF
) {
224 popstackmark(&smark
);
227 popstackmark(&smark
);
233 * Evaluate a parse tree. The value is left in the global variable
238 evaltree(union node
*n
, int flags
)
244 TRACE(("evaltree(NULL) called\n"));
249 displayhist
= 1; /* show history substitutions done with fc */
251 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
252 getpid(), n
, n
->type
, flags
));
255 evaltree(n
->nbinary
.ch1
, flags
& EV_TESTED
);
258 evaltree(n
->nbinary
.ch2
, flags
);
261 evaltree(n
->nbinary
.ch1
, EV_TESTED
);
262 if (evalskip
|| exitstatus
!= 0)
264 evaltree(n
->nbinary
.ch2
, flags
);
267 evaltree(n
->nbinary
.ch1
, EV_TESTED
);
268 if (evalskip
|| exitstatus
== 0)
270 evaltree(n
->nbinary
.ch2
, flags
);
273 expredir(n
->nredir
.redirect
);
274 redirect(n
->nredir
.redirect
, REDIR_PUSH
);
275 evaltree(n
->nredir
.n
, flags
);
279 evalsubshell(n
, flags
);
280 do_etest
= !(flags
& EV_TESTED
);
283 evalsubshell(n
, flags
);
286 evaltree(n
->nif
.test
, EV_TESTED
);
290 evaltree(n
->nif
.ifpart
, flags
);
291 else if (n
->nif
.elsepart
)
292 evaltree(n
->nif
.elsepart
, flags
);
308 defun(n
->narg
.text
, n
->narg
.next
);
312 evaltree(n
->nnot
.com
, EV_TESTED
);
313 exitstatus
= !exitstatus
;
317 do_etest
= !(flags
& EV_TESTED
);
320 evalcommand(n
, flags
, NULL
);
321 do_etest
= !(flags
& EV_TESTED
);
324 out1fmt("Node type = %d\n", n
->type
);
331 if ((flags
& EV_EXIT
) != 0 || (eflag
&& exitstatus
!= 0 && do_etest
))
332 exitshell(exitstatus
);
337 evalloop(union node
*n
, int flags
)
344 evaltree(n
->nbinary
.ch1
, EV_TESTED
);
346 skipping
: if (evalskip
== SKIPCONT
&& --skipcount
<= 0) {
350 if (evalskip
== SKIPBREAK
&& --skipcount
<= 0)
354 if (n
->type
== NWHILE
) {
361 evaltree(n
->nbinary
.ch2
, flags
& EV_TESTED
);
373 evalfor(union node
*n
, int flags
)
375 struct arglist arglist
;
378 struct stackmark smark
;
381 setstackmark(&smark
);
382 arglist
.lastp
= &arglist
.list
;
383 for (argp
= n
->nfor
.args
; argp
; argp
= argp
->narg
.next
) {
384 expandarg(argp
, &arglist
, EXP_FULL
| EXP_TILDE
);
388 *arglist
.lastp
= NULL
;
391 for (sp
= arglist
.list
; sp
; sp
= sp
->next
) {
392 setvar(n
->nfor
.var
, sp
->text
, 0);
393 evaltree(n
->nfor
.body
, flags
& EV_TESTED
);
396 if (evalskip
== SKIPCONT
&& --skipcount
<= 0) {
400 if (evalskip
== SKIPBREAK
&& --skipcount
<= 0)
408 popstackmark(&smark
);
414 evalcase(union node
*n
, int flags
)
418 struct arglist arglist
;
419 struct stackmark smark
;
422 setstackmark(&smark
);
423 arglist
.lastp
= &arglist
.list
;
424 expandarg(n
->ncase
.expr
, &arglist
, EXP_TILDE
);
425 for (cp
= n
->ncase
.cases
; cp
&& evalskip
== 0 ; cp
= cp
->nclist
.next
) {
426 for (patp
= cp
->nclist
.pattern
; patp
; patp
= patp
->narg
.next
) {
427 if (casematch(patp
, arglist
.list
->text
)) {
429 evaltree(cp
->nclist
.body
, flags
);
438 popstackmark(&smark
);
444 * Kick off a subshell to evaluate a tree.
448 evalsubshell(union node
*n
, int flags
)
451 int backgnd
= (n
->type
== NBACKGND
);
453 expredir(n
->nredir
.redirect
);
456 if (forkshell(jp
, n
, backgnd
? FORK_BG
: FORK_FG
) == 0) {
460 redirect(n
->nredir
.redirect
, 0);
462 evaltree(n
->nredir
.n
, flags
| EV_EXIT
);
465 exitstatus
= waitforjob(jp
);
472 * Compute the names of the files in a redirection list.
476 expredir(union node
*n
)
480 for (redir
= n
; redir
; redir
= redir
->nfile
.next
) {
483 switch (redir
->type
) {
489 expandarg(redir
->nfile
.fname
, &fn
, EXP_TILDE
| EXP_REDIR
);
490 redir
->nfile
.expfname
= fn
.list
->text
;
494 if (redir
->ndup
.vname
) {
495 expandarg(redir
->ndup
.vname
, &fn
, EXP_FULL
| EXP_TILDE
);
496 fixredir(redir
, fn
.list
->text
, 1);
506 * Evaluate a pipeline. All the processes in the pipeline are children
507 * of the process creating the pipeline. (This differs from some versions
508 * of the shell, which make the last process in a pipeline the parent
513 evalpipe(union node
*n
)
521 TRACE(("evalpipe(0x%lx) called\n", (long)n
));
523 for (lp
= n
->npipe
.cmdlist
; lp
; lp
= lp
->next
)
526 jp
= makejob(n
, pipelen
);
528 for (lp
= n
->npipe
.cmdlist
; lp
; lp
= lp
->next
) {
532 if (sh_pipe(pip
) < 0) {
535 error("Pipe call failed");
538 if (forkshell(jp
, lp
->n
, n
->npipe
.backgnd
? FORK_BG
: FORK_FG
) == 0) {
542 copyfd(prevfd
, 0, 1);
549 copyfd(pip
[1], 1, 1);
553 evaltree(lp
->n
, EV_EXIT
);
560 if (n
->npipe
.backgnd
== 0) {
561 exitstatus
= waitforjob(jp
);
562 TRACE(("evalpipe: job done exit status %d\n", exitstatus
));
570 * Execute a command inside back quotes. If it's a builtin command, we
571 * want to save its output in a block obtained from malloc. Otherwise
572 * we fork off a subprocess and get the output of the command via a pipe.
573 * Should be called with interrupts off.
577 evalbackcmd(union node
*n
, struct backcmd
*result
)
581 struct stackmark smark
; /* unnecessary */
583 setstackmark(&smark
);
593 * For now we disable executing builtins in the same
594 * context as the shell, because we are not keeping
595 * enough state to recover from changes that are
596 * supposed only to affect subshells. eg. echo "`cd /`"
598 if (n
->type
== NCMD
) {
599 exitstatus
= oexitstatus
;
600 evalcommand(n
, EV_BACKCMD
, result
);
605 if (sh_pipe(pip
) < 0)
606 error("Pipe call failed");
608 if (forkshell(jp
, n
, FORK_NOJOB
) == 0) {
613 copyfd(pip
[1], 1, 1);
617 evaltree(n
, EV_EXIT
);
626 popstackmark(&smark
);
627 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
628 result
->fd
, result
->buf
, result
->nleft
, result
->jp
));
634 static char *sys_path
= NULL
;
635 static int mib
[] = {CTL_USER
, USER_CS_PATH
};
636 static char def_path
[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
639 if (sys_path
== NULL
) {
640 if (sysctl(mib
, 2, 0, &len
, 0, 0) != -1 &&
641 (sys_path
= ckmalloc(len
+ 5)) != NULL
&&
642 sysctl(mib
, 2, sys_path
+ 5, &len
, 0, 0) != -1) {
643 memcpy(sys_path
, "PATH=", 5);
646 /* something to keep things happy */
654 parse_command_args(int argc
, char **argv
, int *use_syspath
)
668 if (*cp
== '-' && cp
[1] == 0) {
673 while ((c
= *cp
++)) {
679 /* run 'typecmd' for other options */
684 return sv_argc
- argc
;
691 * Execute a simple command.
695 evalcommand(union node
*cmd
, int flgs
, struct backcmd
*backcmd
)
697 struct stackmark smark
;
699 struct arglist arglist
;
700 struct arglist varlist
;
701 volatile int flags
= flgs
;
702 char ** volatile argv
;
709 struct cmdentry cmdentry
;
710 struct job
* volatile jp
;
711 struct jmploc jmploc
;
712 struct jmploc
*volatile savehandler
= NULL
;
713 const char *volatile savecmdname
;
714 volatile struct shparam saveparam
;
715 struct localvar
*volatile savelocalvars
;
717 char * volatile lastarg
;
718 const char * volatile path
= pathval();
719 volatile int temp_path
;
722 /* First expand the arguments. */
723 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd
, flags
));
724 setstackmark(&smark
);
727 arglist
.lastp
= &arglist
.list
;
729 /* Expand arguments, ignoring the initial 'name=value' ones */
730 for (argp
= cmd
->ncmd
.args
; argp
; argp
= argp
->narg
.next
) {
731 char *p
= argp
->narg
.text
;
732 if (varflag
&& is_name(*p
)) {
735 } while (is_in_name(*p
));
739 expandarg(argp
, &arglist
, EXP_FULL
| EXP_TILDE
);
742 *arglist
.lastp
= NULL
;
744 expredir(cmd
->ncmd
.redirect
);
746 /* Now do the initial 'name=value' ones we skipped above */
747 varlist
.lastp
= &varlist
.list
;
748 for (argp
= cmd
->ncmd
.args
; argp
; argp
= argp
->narg
.next
) {
749 char *p
= argp
->narg
.text
;
754 while (is_in_name(*p
));
757 expandarg(argp
, &varlist
, EXP_VARTILDE
);
759 *varlist
.lastp
= NULL
;
762 for (sp
= arglist
.list
; sp
; sp
= sp
->next
)
764 argv
= stalloc(sizeof (char *) * (argc
+ 1));
766 for (sp
= arglist
.list
; sp
; sp
= sp
->next
) {
767 TRACE(("evalcommand arg: %s\n", sp
->text
));
772 if (iflag
&& funcnest
== 0 && argc
> 0)
776 /* Print the command if xflag is set. */
780 for (sp
= varlist
.list
; sp
; sp
= sp
->next
) {
786 for (sp
= arglist
.list
; sp
; sp
= sp
->next
) {
796 /* Now locate the command. */
798 cmdentry
.cmdtype
= CMDSPLBLTIN
;
799 cmdentry
.u
.bltin
= bltincmd
;
801 static const char PATH
[] = "PATH=";
802 int cmd_flags
= DO_ERR
;
805 * Modify the command lookup path, if a PATH= assignment
808 for (sp
= varlist
.list
; sp
; sp
= sp
->next
)
809 if (strncmp(sp
->text
, PATH
, sizeof(PATH
) - 1) == 0)
810 path
= sp
->text
+ sizeof(PATH
) - 1;
813 int argsused
, use_syspath
;
814 find_command(argv
[0], &cmdentry
, cmd_flags
, path
);
815 if (cmdentry
.cmdtype
== CMDUNKNOWN
) {
821 /* implement the 'command' builtin here */
822 if (cmdentry
.cmdtype
!= CMDBUILTIN
||
823 cmdentry
.u
.bltin
!= bltincmd
)
825 cmd_flags
|= DO_NOFUNC
;
826 argsused
= parse_command_args(argc
, argv
, &use_syspath
);
828 /* use 'type' builting to display info */
829 cmdentry
.u
.bltin
= typecmd
;
835 path
= syspath() + 5;
837 if (cmdentry
.cmdtype
== CMDSPLBLTIN
&& cmd_flags
& DO_NOFUNC
)
838 /* posix mandates that 'command <splbltin>' act as if
839 <splbltin> was a normal builtin */
840 cmdentry
.cmdtype
= CMDBUILTIN
;
843 /* Fork off a child process if necessary. */
844 if (cmd
->ncmd
.backgnd
|| (trap
[0] && (flags
& EV_EXIT
) != 0)
845 || (cmdentry
.cmdtype
== CMDNORMAL
&& (flags
& EV_EXIT
) == 0)
846 || ((flags
& EV_BACKCMD
) != 0
847 && ((cmdentry
.cmdtype
!= CMDBUILTIN
&& cmdentry
.cmdtype
!= CMDSPLBLTIN
)
848 || cmdentry
.u
.bltin
== dotcmd
849 || cmdentry
.u
.bltin
== evalcmd
))) {
851 jp
= makejob(cmd
, 1);
852 mode
= cmd
->ncmd
.backgnd
;
853 if (flags
& EV_BACKCMD
) {
855 if (sh_pipe(pip
) < 0)
856 error("Pipe call failed");
858 #ifdef DO_SHAREDVFORK
859 /* It is essential that if DO_SHAREDVFORK is defined that the
860 * child's address space is actually shared with the parent as
863 if (usefork
== 0 && cmdentry
.cmdtype
== CMDNORMAL
) {
867 savelocalvars
= localvars
;
870 switch (pid
= vfork()) {
873 TRACE(("Vfork failed, errno=%d\n", serrno
));
875 error("Cannot vfork (%s)", strerror(serrno
));
878 /* Make sure that exceptions only unwind to
881 if (setjmp(jmploc
.loc
)) {
882 if (exception
== EXSHELLPROC
) {
883 /* We can't progress with the vfork,
884 * so, set vforked = 2 so the parent
885 * knows, and _exit();
893 savehandler
= handler
;
895 listmklocal(varlist
.list
, VEXPORT
| VNOFUNC
);
896 forkchild(jp
, cmd
, mode
, vforked
);
899 handler
= savehandler
; /* restore from vfork(2) */
901 localvars
= savelocalvars
;
905 (void)waitpid(pid
, NULL
, 0);
906 /* We need to progress in a normal fork fashion */
910 forkparent(jp
, cmd
, mode
, pid
);
916 if (forkshell(jp
, cmd
, mode
) != 0)
917 goto parent
; /* at end of routine */
919 #ifdef DO_SHAREDVFORK
922 if (flags
& EV_BACKCMD
) {
929 copyfd(pip
[1], 1, 1);
936 /* This is the child process if a fork occurred. */
937 /* Execute the command. */
938 switch (cmdentry
.cmdtype
) {
941 trputs("Shell function: "); trargs(argv
);
943 redirect(cmd
->ncmd
.redirect
, REDIR_PUSH
);
944 saveparam
= shellparam
;
945 shellparam
.malloc
= 0;
946 shellparam
.reset
= 1;
947 shellparam
.nparam
= argc
- 1;
948 shellparam
.p
= argv
+ 1;
949 shellparam
.optnext
= NULL
;
951 savelocalvars
= localvars
;
954 if (setjmp(jmploc
.loc
)) {
955 if (exception
== EXSHELLPROC
) {
956 freeparam((volatile struct shparam
*)
959 freeparam(&shellparam
);
960 shellparam
= saveparam
;
963 localvars
= savelocalvars
;
964 handler
= savehandler
;
965 longjmp(handler
->loc
, 1);
967 savehandler
= handler
;
969 listmklocal(varlist
.list
, VEXPORT
);
970 /* stop shell blowing its stack */
971 if (++funcnest
> 1000)
972 error("too many nested function calls");
973 evaltree(cmdentry
.u
.func
, flags
& EV_TESTED
);
977 localvars
= savelocalvars
;
978 freeparam(&shellparam
);
979 shellparam
= saveparam
;
980 handler
= savehandler
;
983 if (evalskip
== SKIPFUNC
) {
988 exitshell(exitstatus
);
994 trputs("builtin command: "); trargs(argv
);
996 mode
= (cmdentry
.u
.bltin
== execcmd
) ? 0 : REDIR_PUSH
;
997 if (flags
== EV_BACKCMD
) {
999 memout
.nextc
= memout
.buf
;
1000 memout
.bufsize
= 64;
1001 mode
|= REDIR_BACKQ
;
1004 savehandler
= handler
;
1005 savecmdname
= commandname
;
1008 if (!setjmp(jmploc
.loc
)) {
1009 /* We need to ensure the command hash table isn't
1010 * corruped by temporary PATH assignments.
1011 * However we must ensure the 'local' command works!
1013 if (path
!= pathval() && (cmdentry
.u
.bltin
== hashcmd
||
1014 cmdentry
.u
.bltin
== typecmd
)) {
1015 savelocalvars
= localvars
;
1018 mklocal(path
- 5 /* PATH= */, 0);
1020 redirect(cmd
->ncmd
.redirect
, mode
);
1022 /* exec is a special builtin, but needs this list... */
1023 cmdenviron
= varlist
.list
;
1024 /* we must check 'readonly' flag for all builtins */
1025 listsetvar(varlist
.list
,
1026 cmdentry
.cmdtype
== CMDSPLBLTIN
? 0 : VNOSET
);
1027 commandname
= argv
[0];
1028 /* initialize nextopt */
1034 builtin_flags
= flags
;
1035 exitstatus
= cmdentry
.u
.bltin(argc
, argv
);
1038 exitstatus
= e
== EXINT
? SIGINT
+ 128 :
1039 e
== EXEXEC
? exerrno
: 2;
1041 handler
= savehandler
;
1048 localvars
= savelocalvars
;
1051 if (e
!= EXSHELLPROC
) {
1052 commandname
= savecmdname
;
1053 if (flags
& EV_EXIT
)
1054 exitshell(exitstatus
);
1057 if ((e
!= EXERROR
&& e
!= EXEXEC
)
1058 || cmdentry
.cmdtype
== CMDSPLBLTIN
)
1062 if (cmdentry
.u
.bltin
!= execcmd
)
1064 if (flags
== EV_BACKCMD
) {
1065 backcmd
->buf
= memout
.buf
;
1066 backcmd
->nleft
= memout
.nextc
- memout
.buf
;
1073 trputs("normal command: "); trargs(argv
);
1075 redirect(cmd
->ncmd
.redirect
, vforked
? REDIR_VFORK
: 0);
1077 for (sp
= varlist
.list
; sp
; sp
= sp
->next
)
1078 setvareq(sp
->text
, VEXPORT
|VSTACK
);
1079 envp
= environment();
1080 shellexec(argv
, envp
, path
, cmdentry
.u
.index
, vforked
);
1085 parent
: /* parent process gets here (if we forked) */
1086 if (mode
== FORK_FG
) { /* argument to fork */
1087 exitstatus
= waitforjob(jp
);
1088 } else if (mode
== FORK_NOJOB
) {
1089 backcmd
->fd
= pip
[0];
1097 /* dsl: I think this is intended to be used to support
1098 * '_' in 'vi' command mode during line editing...
1099 * However I implemented that within libedit itself.
1101 setvar("_", lastarg
, 0);
1102 popstackmark(&smark
);
1107 * Search for a command. This is called before we fork so that the
1108 * location of the command will be available in the parent as well as
1109 * the child. The check for "goodname" is an overly conservative
1110 * check that the name will not be subject to expansion.
1114 prehash(union node
*n
)
1116 struct cmdentry entry
;
1118 if (n
&& n
->type
== NCMD
&& n
->ncmd
.args
)
1119 if (goodname(n
->ncmd
.args
->narg
.text
))
1120 find_command(n
->ncmd
.args
->narg
.text
, &entry
, 0,
1130 STATIC
enum skipstate
1131 current_skipstate(void)
1139 evalskip
= SKIPNONE
;
1144 * Builtin commands. Builtin commands whose functions are closely
1145 * tied to evaluation are implemented here.
1153 bltincmd(int argc
, char **argv
)
1156 * Preserve exitstatus of a previous possible redirection
1159 return back_exitstatus
;
1164 * Handle break and continue commands. Break, continue, and return are
1165 * all handled by setting the evalskip flag. The evaluation routines
1166 * above all check this flag, and if it is set they start skipping
1167 * commands rather than executing them. The variable skipcount is
1168 * the number of loops to break/continue, or the number of function
1169 * levels to return. (The latter is always 1.) It should probably
1170 * be an error to break out of more loops than exist, but it isn't
1171 * in the standard shell so we don't make it one here.
1175 breakcmd(int argc
, char **argv
)
1177 int n
= argc
> 1 ? number(argv
[1]) : 1;
1182 evalskip
= (**argv
== 'c')? SKIPCONT
: SKIPBREAK
;
1189 dotcmd(int argc
, char **argv
)
1193 if (argc
>= 2) { /* That's what SVR2 does */
1196 * dot_funcnest needs to be 0 when not in a dotcmd, so it
1197 * cannot be restored with (funcnest + 1).
1199 int dot_funcnest_old
;
1200 struct stackmark smark
;
1202 setstackmark(&smark
);
1203 fullname
= find_dot_file(argv
[1]);
1204 setinputfile(fullname
, 1);
1205 commandname
= fullname
;
1206 dot_funcnest_old
= dot_funcnest
;
1207 dot_funcnest
= funcnest
+ 1;
1209 dot_funcnest
= dot_funcnest_old
;
1211 popstackmark(&smark
);
1217 * Take commands from a file. To be compatible we should do a path
1218 * search for the file, which is necessary to find sub-commands.
1222 find_dot_file(char *basename
)
1225 const char *path
= pathval();
1228 /* don't try this for absolute or relative paths */
1229 if (strchr(basename
, '/'))
1232 while ((fullname
= padvance(&path
, basename
)) != NULL
) {
1233 if ((stat(fullname
, &statb
) == 0) && S_ISREG(statb
.st_mode
)) {
1235 * Don't bother freeing here, since it will
1236 * be freed by the caller.
1240 stunalloc(fullname
);
1243 /* not found in the PATH */
1244 error("%s: not found", basename
);
1251 * The return command.
1253 * Quoth the POSIX standard:
1254 * The return utility shall cause the shell to stop executing the current
1255 * function or dot script. If the shell is not currently executing
1256 * a function or dot script, the results are unspecified.
1258 * As for the unspecified part, there seems to be no de-facto standard: bash
1259 * ignores the return with a warning, zsh ignores the return in interactive
1260 * mode but seems to liken it to exit in a script. (checked May 2014)
1262 * We choose to silently ignore the return. Older versions of this shell
1263 * set evalskip to SKIPFILE causing the shell to (indirectly) exit. This
1264 * had at least the problem of circumventing the check for stopped jobs,
1265 * which would occur for exit or ^D.
1269 returncmd(int argc
, char **argv
)
1271 int ret
= argc
> 1 ? number(argv
[1]) : exitstatus
;
1273 if ((dot_funcnest
== 0 && funcnest
)
1274 || (dot_funcnest
> 0 && funcnest
- (dot_funcnest
- 1) > 0)) {
1275 evalskip
= SKIPFUNC
;
1277 } else if (dot_funcnest
> 0) {
1278 evalskip
= SKIPFILE
;
1281 /* XXX: should a warning be issued? */
1290 falsecmd(int argc
, char **argv
)
1297 truecmd(int argc
, char **argv
)
1304 execcmd(int argc
, char **argv
)
1309 iflag
= 0; /* exit on error */
1312 for (sp
= cmdenviron
; sp
; sp
= sp
->next
)
1313 setvareq(sp
->text
, VEXPORT
|VSTACK
);
1314 shellexec(argv
+ 1, environment(), pathval(), 0, 0);
1320 conv_time(clock_t ticks
, char *seconds
, size_t l
)
1322 static clock_t tpm
= 0;
1327 tpm
= sysconf(_SC_CLK_TCK
) * 60;
1330 snprintf(seconds
, l
, "%.4f", (ticks
- mins
* tpm
) * 60.0 / tpm
);
1332 if (seconds
[0] == '6' && seconds
[1] == '0') {
1333 /* 59.99995 got rounded up... */
1335 strlcpy(seconds
, "0.0", l
);
1339 /* suppress trailing zeros */
1340 i
= strlen(seconds
) - 1;
1341 for (; seconds
[i
] == '0' && seconds
[i
- 1] != '.'; i
--)
1347 timescmd(int argc
, char **argv
)
1351 char us
[8], ss
[8], cus
[8], css
[8];
1357 u
= conv_time(tms
.tms_utime
, us
, sizeof(us
));
1358 s
= conv_time(tms
.tms_stime
, ss
, sizeof(ss
));
1359 cu
= conv_time(tms
.tms_cutime
, cus
, sizeof(cus
));
1360 cs
= conv_time(tms
.tms_cstime
, css
, sizeof(css
));
1362 outfmt(out1
, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
1363 u
, us
, s
, ss
, cu
, cus
, cs
, css
);