1 /* Yash: yet another shell */
2 /* exec.c: command execution */
3 /* (C) 2007-2020 magicant */
4 
5 /* This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17 
18 
19 #include "common.h"
20 #include "exec.h"
21 #include <assert.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #if HAVE_GETTEXT
25 # include <libintl.h>
26 #endif
27 #include <limits.h>
28 #include <math.h>
29 #if HAVE_PATHS_H
30 # include <paths.h>
31 #endif
32 #include <signal.h>
33 #include <stdbool.h>
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/times.h>
39 #include <unistd.h>
40 #include <wchar.h>
41 #include "alias.h"
42 #include "builtin.h"
43 #include "expand.h"
44 #if YASH_ENABLE_HISTORY
45 # include "history.h"
46 #endif
47 #include "input.h"
48 #include "job.h"
49 #include "option.h"
50 #include "parser.h"
51 #include "path.h"
52 #include "plist.h"
53 #include "redir.h"
54 #include "sig.h"
55 #include "strbuf.h"
56 #include "util.h"
57 #include "variable.h"
58 #include "xfnmatch.h"
59 #include "yash.h"
60 #if YASH_ENABLE_DOUBLE_BRACKET
61 # include "builtins/test.h"
62 #endif
63 #if YASH_ENABLE_LINEEDIT
64 # include "lineedit/complete.h"
65 # include "lineedit/lineedit.h"
66 #endif
67 
68 
69 /* type of command execution */
70 typedef enum {
71     E_NORMAL,  /* normal execution */
72     E_ASYNC,   /* asynchronous execution */
73     E_SELF,    /* execution in the shell's own process */
74 } exec_T;
75 
76 /* info about file descriptors of pipes */
77 typedef struct pipeinfo_T {
78     int pi_fromprevfd;   /* reading end of the pipe from the previous process */
79     int pi_tonextfds[2]; /* both ends of the pipe to the next process */
80     /* -1 is assigned to unused members. */
81 } pipeinfo_T;
82 #define PIPEINFO_INIT { -1, { -1, -1 }, }
83 
84 /* values used to specify the behavior of command search. */
85 typedef enum srchcmdtype_T {
86     SCT_EXTERNAL = 1 << 0,  /* search for an external command */
87     SCT_BUILTIN  = 1 << 1,  /* search for a built-in */
88     SCT_FUNCTION = 1 << 2,  /* search for a function */
89     SCT_ALL      = 1 << 3,  /* search all */
90     SCT_STDPATH  = 1 << 4,  /* search the standard PATH */
91     SCT_CHECK    = 1 << 5,  /* check command existence */
92 } srchcmdtype_T;
93 
94 typedef enum cmdtype_T {
95     CT_NONE,
96     CT_EXTERNALPROGRAM,
97     CT_SPECIALBUILTIN,
98     CT_SEMISPECIALBUILTIN,
99     CT_REGULARBUILTIN,
100     CT_FUNCTION,
101 } cmdtype_T;
102 
103 /* info about a simple command to execute */
104 typedef struct commandinfo_T {
105     cmdtype_T type;           /* type of command */
106     union {
107 	const char *path;     /* command path (for external program) */
108 	main_T *builtin;      /* body of built-in */
109 	command_T *function;  /* body of function */
110     } value;
111 } commandinfo_T;
112 #define ci_path     value.path
113 #define ci_builtin  value.builtin
114 #define ci_function value.function
115 
116 /* result of `fork_and_wait' */
117 typedef struct fork_and_wait_T {
118     pid_t cpid;       /* child process ID */
119     wchar_t **namep;  /* where to place the job name */
120 } fork_and_wait_T;
121 
122 typedef enum exception_T {
123     E_NONE,
124     E_CONTINUE,
125     E_RETURN,
126     E_BREAK_ITERATION,
127     E_CONTINUE_ITERATION,
128 } exception_T;
129 
130 /* state of currently executed loop */
131 typedef struct execstate_T {
132     unsigned loopnest;      /* level of nested loops */
133     unsigned breakloopnest; /* target level of "break" */
134     bool noreturn;          /* true when the "return" built-in is not allowed */
135     bool iterating;         /* true when iterative execution is ongoing */
136 } execstate_T;
137 
138 static void exec_pipelines(const pipeline_T *p, bool finally_exit);
139 static void exec_pipelines_async(const pipeline_T *p)
140     __attribute__((nonnull));
141 
142 static void exec_commands(command_T *cs, exec_T type)
143     __attribute__((nonnull));
144 static inline size_t number_of_commands_in_pipeline(const command_T *c)
145     __attribute__((nonnull,pure,warn_unused_result));
146 static void apply_errexit_errreturn(const command_T *c);
147 static bool is_errexit_condition(void)
148     __attribute__((pure));
149 static bool is_errreturn_condition(void)
150     __attribute__((pure));
151 static bool is_err_condition_for(const command_T *c)
152     __attribute__((pure));
153 static inline void next_pipe(pipeinfo_T *pi, bool next)
154     __attribute__((nonnull));
155 static inline void connect_pipes(pipeinfo_T *pi)
156     __attribute__((nonnull));
157 
158 static void exec_one_command(command_T *c, bool finally_exit)
159     __attribute__((nonnull));
160 static void exec_simple_command(const command_T *c, bool finally_exit)
161     __attribute__((nonnull));
162 static bool exec_simple_command_without_words(const command_T *c)
163     __attribute__((nonnull,warn_unused_result));
164 static bool exec_simple_command_with_words(
165 	const command_T *c, int argc, void **argv, bool finally_exit)
166     __attribute__((nonnull,warn_unused_result));
167 static void print_xtrace(void *const *argv);
168 static void search_command(
169 	const char *restrict name, const wchar_t *restrict wname,
170 	commandinfo_T *restrict ci, enum srchcmdtype_T type)
171     __attribute__((nonnull));
172 static inline bool is_special_builtin(const char *cmdname)
173     __attribute__((nonnull,pure));
174 static bool command_not_found_handler(void *const *argv)
175     __attribute__((nonnull));
176 static wchar_t **invoke_simple_command(const commandinfo_T *ci,
177 	int argc, char *argv0, void **argv, bool finally_exit)
178     __attribute__((nonnull,warn_unused_result));
179 static void exec_external_program(
180 	const char *path, int argc, char *argv0, void **argv, char **envs)
181     __attribute__((nonnull));
182 static inline int xexecve(
183 	const char *path, char *const *argv, char *const *envp)
184     __attribute__((nonnull(1)));
185 static void exec_fall_back_on_sh(
186 	int argc, char *const *argv, char *const *env, const char *path)
187     __attribute__((nonnull(2,3,4)));
188 static void exec_function_body(
189 	command_T *body, void *const *args, bool finally_exit, bool complete)
190     __attribute__((nonnull));
191 
192 static void exec_nonsimple_command(command_T *c, bool finally_exit)
193     __attribute__((nonnull));
194 static void exec_if(const command_T *c, bool finally_exit)
195     __attribute__((nonnull));
196 static inline bool exec_condition(const and_or_T *c);
197 static void exec_for(const command_T *c, bool finally_exit)
198     __attribute__((nonnull));
199 static void exec_while(const command_T *c, bool finally_exit)
200     __attribute__((nonnull));
201 static void exec_case(const command_T *c, bool finally_exit)
202     __attribute__((nonnull));
203 static void exec_funcdef(const command_T *c, bool finally_exit)
204     __attribute__((nonnull));
205 
206 static fork_and_wait_T fork_and_wait(sigtype_T sigtype)
207     __attribute__((warn_unused_result));
208 static void become_child(sigtype_T sigtype);
209 
210 static int exec_iteration(void *const *commands, const char *codename)
211     __attribute__((nonnull));
212 
213 
214 /* exit status of the last command */
215 int laststatus = Exit_SUCCESS;
216 /* exit status of the command preceding the currently executed trap action */
217 int savelaststatus = -1;  // -1 if not in a trap handler
218 /* exit status of the last command substitution */
219 static int lastcmdsubstatus;
220 /* exit status of the command that immediately preceded the EXIT trap. */
221 int exitstatus = -1;  // -1 if not executing the EXIT trap
222 /* the process ID of the last asynchronous list */
223 pid_t lastasyncpid;
224 
225 /* This flag is set to true while the shell is executing the condition of an if-
226  * statement, an and-or list, etc. to suppress the effect of the "errexit" and
227  * "errreturn" options. */
228 static bool suppresserrexit = false, suppresserrreturn = false;
229 
230 /* state of currently executed loop */
231 static execstate_T execstate;
232 /* exceptional jump to be done (other than "break") */
233 static exception_T exception;
234 
235 /* This flag is set when a special built-in is executed as such. */
236 bool special_builtin_executed;
237 
238 /* This flag is set while the "exec" built-in is executed. */
239 static bool exec_builtin_executed = false;
240 
241 /* True while executing auxiliary commands such as $PROMPT_COMMAND and
242  * $COMMAND_NOT_FOUND_HANDLER. */
243 bool is_executing_auxiliary = false;
244 
245 /* the last assignment. */
246 static const assign_T *last_assign;
247 
248 /* a buffer for xtrace.
249  * When assignments are performed while executing a simple command, the trace
250  * is appended to this buffer. Each trace of an assignment must be prefixed
251  * with a space to separate it with the previous one. The first space will be
252  * trimmed when the buffer is flushed to the standard error. */
253 static xwcsbuf_T xtrace_buffer = { .contents = NULL };
254 
255 
256 /* Resets `execstate' to the initial state. */
reset_execstate(bool reset_iteration)257 void reset_execstate(bool reset_iteration)
258 {
259     execstate.loopnest = 0;
260     execstate.breakloopnest = 0;
261     execstate.noreturn = false;
262     if (reset_iteration)
263 	execstate.iterating = false;
264 }
265 
266 /* Saves the current `execstate' and returns it.
267  * You typically call `reset_execstate' after calling this function. */
save_execstate(void)268 execstate_T *save_execstate(void)
269 {
270     execstate_T *save = xmalloc(sizeof execstate);
271     *save = execstate;
272     return save;
273 }
274 
275 /* Restores `execstate' to `save' and frees `save'. */
restore_execstate(execstate_T * save)276 void restore_execstate(execstate_T *save)
277 {
278     execstate = *save;
279     free(save);
280 }
281 
282 /* Disables the "return" built-in in the current `execstate'. */
disable_return(void)283 void disable_return(void)
284 {
285     execstate.noreturn = true;
286 }
287 
288 /* If we're returning, clear the flag. */
cancel_return(void)289 void cancel_return(void)
290 {
291     if (exception == E_RETURN)
292 	exception = E_NONE;
293 }
294 
295 /* Returns true iff we're breaking/continuing/returning now. */
need_break(void)296 bool need_break(void)
297 {
298     return execstate.breakloopnest < execstate.loopnest
299 	|| exception != E_NONE
300 	|| is_interrupted();
301 }
302 
303 
304 /* Executes the and-or lists.
305  * If `finally_exit' is true, the shell exits after execution. */
exec_and_or_lists(const and_or_T * a,bool finally_exit)306 void exec_and_or_lists(const and_or_T *a, bool finally_exit)
307 {
308     while (a != NULL && !need_break()) {
309 	if (!a->ao_async)
310 	    exec_pipelines(a->ao_pipelines, finally_exit && !a->next);
311 	else
312 	    exec_pipelines_async(a->ao_pipelines);
313 
314 	a = a->next;
315     }
316     if (finally_exit)
317 	exit_shell();
318 }
319 
320 /* Executes the pipelines. */
exec_pipelines(const pipeline_T * p,bool finally_exit)321 void exec_pipelines(const pipeline_T *p, bool finally_exit)
322 {
323     for (bool first = true;
324 	    p != NULL && !need_break();
325 	    p = p->next, first = false) {
326 	if (!first && p->pl_cond == (laststatus != Exit_SUCCESS))
327 	    continue;
328 
329 	bool savesee = suppresserrexit, saveser = suppresserrreturn;
330 	bool suppress = p->pl_neg || p->next != NULL;
331 	suppresserrexit |= suppress;
332 	suppresserrreturn |= suppress;
333 
334 	bool self = finally_exit && !p->next && !p->pl_neg;
335 	exec_commands(p->pl_commands, self ? E_SELF : E_NORMAL);
336 	if (p->pl_neg) {
337 	    if (laststatus == Exit_SUCCESS)
338 		laststatus = Exit_FAILURE;
339 	    else
340 		laststatus = Exit_SUCCESS;
341 	}
342 
343 	suppresserrexit = savesee, suppresserrreturn = saveser;
344     }
345     if (finally_exit)
346 	exit_shell();
347 }
348 
349 /* Executes the pipelines asynchronously. */
exec_pipelines_async(const pipeline_T * p)350 void exec_pipelines_async(const pipeline_T *p)
351 {
352     if (p->next == NULL && !p->pl_neg) {
353 	exec_commands(p->pl_commands, E_ASYNC);
354 	return;
355     }
356 
357     pid_t cpid = fork_and_reset(0, false, t_quitint);
358 
359     if (cpid > 0) {
360 	/* parent process: add a new job */
361 	job_T *job = xmalloc(add(sizeof *job, sizeof *job->j_procs));
362 	process_T *ps = job->j_procs;
363 
364 	ps->pr_pid = cpid;
365 	ps->pr_status = JS_RUNNING;
366 	ps->pr_statuscode = 0;
367 	ps->pr_name = pipelines_to_wcs(p);
368 
369 	job->j_pgid = doing_job_control_now ? cpid : 0;
370 	job->j_status = JS_RUNNING;
371 	job->j_statuschanged = true;
372 	job->j_legacy = false;
373 	job->j_nonotify = false;
374 	job->j_pcount = 1;
375 
376 	set_active_job(job);
377 	add_job(shopt_curasync);
378 	laststatus = Exit_SUCCESS;
379 	lastasyncpid = cpid;
380     } else if (cpid == 0) {
381 	/* child process: execute the commands and then exit */
382 	maybe_redirect_stdin_to_devnull();
383 	exec_pipelines(p, true);
384 	assert(false);
385     } else {
386 	/* fork failure */
387 	laststatus = Exit_NOEXEC;
388     }
389 }
390 
391 /* Executes the commands in a pipeline. */
exec_commands(command_T * const cs,exec_T type)392 void exec_commands(command_T *const cs, exec_T type)
393 {
394     size_t count = number_of_commands_in_pipeline(cs);
395     assert(count > 0);
396 
397     bool short_circuit =
398 	type == E_SELF && !doing_job_control_now && !any_trap_set &&
399 	(count == 1 || !shopt_pipefail);
400 
401     if (count == 1 && type != E_ASYNC) {
402 	exec_one_command(cs, /* finally_exit = */ short_circuit);
403 	goto done;
404     }
405 
406     /* fork a child process for each command in the pipeline */
407     pid_t pgid = 0;
408     pipeinfo_T pipe = PIPEINFO_INIT;
409     job_T *job = xmallocs(sizeof *job, count, sizeof *job->j_procs);
410     command_T *c;
411     process_T *p;
412     int forkstatus = Exit_SUCCESS;
413     for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++) {
414 	bool is_last = c->next == NULL;
415 	next_pipe(&pipe, !is_last);
416 
417 	if (is_last && short_circuit)
418 	    goto exec_one_command; /* skip forking */
419 
420 	sigtype_T sigtype = (type == E_ASYNC) ? t_quitint : 0;
421 	pid_t pid = fork_and_reset(pgid, type == E_NORMAL, sigtype);
422 	if (pid == 0) {
423 exec_one_command: /* child process */
424 	    free(job);
425 	    connect_pipes(&pipe);
426 	    if (type == E_ASYNC && pipe.pi_fromprevfd < 0)
427 		maybe_redirect_stdin_to_devnull();
428 	    exec_one_command(c, true);
429 	    assert(false);
430 	} else if (pid >= 0) {
431 	    /* parent process: fork succeeded */
432 	    if (pgid == 0)
433 		pgid = pid;
434 	    p->pr_pid = pid;
435 	    p->pr_status = JS_RUNNING;
436 	    // p->pr_statuscode = ?; // The process is still running.
437 	    p->pr_name = NULL; // The actual name is given later.
438 	} else {
439 	    /* parent process: fork failed */
440 	    p->pr_pid = 0;
441 	    p->pr_status = JS_DONE;
442 	    p->pr_statuscode = forkstatus = Exit_NOEXEC;
443 	    p->pr_name = NULL;
444 	}
445     }
446 
447     assert(pipe.pi_tonextfds[PIPE_IN] < 0);
448     assert(pipe.pi_tonextfds[PIPE_OUT] < 0);
449     if (pipe.pi_fromprevfd >= 0)
450 	xclose(pipe.pi_fromprevfd); /* close the leftover pipe */
451 
452     /* establish the job and wait for it */
453     job->j_pgid = doing_job_control_now ? pgid : 0;
454     job->j_status = JS_RUNNING;
455     job->j_statuschanged = true;
456     job->j_legacy = false;
457     job->j_nonotify = false;
458     job->j_pcount = count;
459     set_active_job(job);
460     if (type != E_ASYNC) {
461 	wait_for_job(ACTIVE_JOBNO, doing_job_control_now, false, false);
462 	if (doing_job_control_now)
463 	    put_foreground(shell_pgid);
464 	laststatus = calc_status_of_job(job);
465     } else {
466 	laststatus = forkstatus;
467 	lastasyncpid = job->j_procs[count - 1].pr_pid;
468     }
469 
470     if (job->j_status == JS_DONE) {
471 	notify_signaled_job(ACTIVE_JOBNO);
472 	remove_job(ACTIVE_JOBNO);
473     } else {
474 	/* name the job processes */
475 	for (c = cs, p = job->j_procs; c != NULL; c = c->next, p++)
476 	    p->pr_name = command_to_wcs(c, false);
477 
478 	/* remember the suspended job */
479 	add_job(type == E_NORMAL || shopt_curasync);
480     }
481 
482 done:
483     handle_signals();
484 
485     apply_errexit_errreturn(cs);
486 
487     if (type == E_SELF)
488 	exit_shell();
489 }
490 
number_of_commands_in_pipeline(const command_T * c)491 size_t number_of_commands_in_pipeline(const command_T *c)
492 {
493     size_t count = 1;
494     while ((c = c->next) != NULL)
495 	count++;
496     return count;
497 }
498 
499 /* Tests the current condition for "errexit" and "errreturn" and then performs
500  * exit or return if applicable. */
apply_errexit_errreturn(const command_T * c)501 void apply_errexit_errreturn(const command_T *c)
502 {
503     if (is_errexit_condition() && is_err_condition_for(c))
504 	exit_shell_with_status(laststatus);
505     if (is_errreturn_condition() && is_err_condition_for(c))
506 	exception = E_RETURN;
507 }
508 
509 /* Returns true if the shell should exit because of the `errexit' option. */
is_errexit_condition(void)510 bool is_errexit_condition(void)
511 {
512     if (!shopt_errexit || suppresserrexit)
513 	return false;
514     if (laststatus == Exit_SUCCESS)
515 	return false;
516 
517 #if YASH_ENABLE_LINEEDIT
518     if (le_state & LE_STATE_COMPLETING)
519 	return false;
520 #endif
521 
522     return true;
523 }
524 
525 /* Returns true if the shell should return because of the `errreturn' option. */
is_errreturn_condition(void)526 bool is_errreturn_condition(void)
527 {
528     if (!shopt_errreturn || suppresserrreturn || execstate.noreturn)
529 	return false;
530     return laststatus != Exit_SUCCESS;
531 }
532 
533 /* Returns true if "errexit" and "errreturn" should be applied to the given
534  * command. */
is_err_condition_for(const command_T * c)535 bool is_err_condition_for(const command_T *c)
536 {
537     if (c == NULL)
538 	return true;
539 
540     /* If this is a multi-command pipeline, the commands are executed in
541      * subshells. Otherwise, we need to check the type of the command. */
542     if (c->next != NULL)
543 	return true;
544 
545     switch (c->c_type) {
546 	case CT_SIMPLE:
547 	case CT_SUBSHELL:
548 #if YASH_ENABLE_DOUBLE_BRACKET
549 	case CT_BRACKET:
550 #endif
551 	case CT_FUNCDEF:
552 	    return true;
553 	case CT_GROUP:
554 	case CT_IF:
555 	case CT_FOR:
556 	case CT_WHILE:
557 	case CT_CASE:
558 	    return false;
559     }
560 
561     assert(false);
562 }
563 
564 /* Updates the contents of the `pipeinfo_T' to proceed to the next process
565  * execution. `pi->pi_fromprevfd' and `pi->pi_tonextfds[PIPE_OUT]' are closed,
566  * `pi->pi_tonextfds[PIPE_IN]' is moved to `pi->pi_fromprevfd', and,
567  * if `next' is true, a new pipe is opened in `pi->pi_tonextfds'; otherwise,
568  * `pi->pi_tonextfds' is assigned -1.
569  * Returns true iff successful. */
next_pipe(pipeinfo_T * pi,bool next)570 void next_pipe(pipeinfo_T *pi, bool next)
571 {
572     if (pi->pi_fromprevfd >= 0)
573 	xclose(pi->pi_fromprevfd);
574     if (pi->pi_tonextfds[PIPE_OUT] >= 0)
575 	xclose(pi->pi_tonextfds[PIPE_OUT]);
576     pi->pi_fromprevfd = pi->pi_tonextfds[PIPE_IN];
577     if (next) {
578 	if (pipe(pi->pi_tonextfds) < 0)
579 	    goto fail;
580 
581 	/* The pipe's FDs must not be 0 or 1, or they may be overridden by each
582 	 * other when we move the pipe to the standard input/output later. So,
583 	 * if they are 0 or 1, we move them to bigger numbers. */
584 	int origin  = pi->pi_tonextfds[PIPE_IN];
585 	int origout = pi->pi_tonextfds[PIPE_OUT];
586 	if (origin < 2 || origout < 2) {
587 	    if (origin < 2)
588 		pi->pi_tonextfds[PIPE_IN] = dup(origin);
589 	    if (origout < 2)
590 		pi->pi_tonextfds[PIPE_OUT] = dup(origout);
591 	    if (origin < 2)
592 		xclose(origin);
593 	    if (origout < 2)
594 		xclose(origout);
595 	    if (pi->pi_tonextfds[PIPE_IN] < 0) {
596 		xclose(pi->pi_tonextfds[PIPE_OUT]);
597 		goto fail;
598 	    }
599 	    if (pi->pi_tonextfds[PIPE_OUT] < 0) {
600 		xclose(pi->pi_tonextfds[PIPE_IN]);
601 		goto fail;
602 	    }
603 	}
604     } else {
605 	pi->pi_tonextfds[PIPE_IN] = pi->pi_tonextfds[PIPE_OUT] = -1;
606     }
607     return;
608 
609 fail:
610     pi->pi_tonextfds[PIPE_IN] = pi->pi_tonextfds[PIPE_OUT] = -1;
611     xerror(errno, Ngt("cannot open a pipe"));
612 }
613 
614 /* Connects the pipe(s) and closes the pipes left. */
connect_pipes(pipeinfo_T * pi)615 void connect_pipes(pipeinfo_T *pi)
616 {
617     if (pi->pi_fromprevfd >= 0) {
618 	xdup2(pi->pi_fromprevfd, STDIN_FILENO);
619 	xclose(pi->pi_fromprevfd);
620     }
621     if (pi->pi_tonextfds[PIPE_OUT] >= 0) {
622 	xdup2(pi->pi_tonextfds[PIPE_OUT], STDOUT_FILENO);
623 	xclose(pi->pi_tonextfds[PIPE_OUT]);
624     }
625     if (pi->pi_tonextfds[PIPE_IN] >= 0)
626 	xclose(pi->pi_tonextfds[PIPE_IN]);
627 }
628 
629 /* Executes the command. */
exec_one_command(command_T * c,bool finally_exit)630 void exec_one_command(command_T *c, bool finally_exit)
631 {
632     /* prevent the command data from being freed in case the command is part of
633      * a function that is unset during execution. */
634     c = comsdup(c);
635 
636     update_lineno(c->c_lineno);
637 
638     if (c->c_type == CT_SIMPLE) {
639 	exec_simple_command(c, finally_exit);
640     } else {
641 	savefd_T *savefd;
642 	if (open_redirections(c->c_redirs, &savefd)) {
643 	    exec_nonsimple_command(c, finally_exit && savefd == NULL);
644 	    undo_redirections(savefd);
645 	} else {
646 	    undo_redirections(savefd);
647 	    laststatus = Exit_REDIRERR;
648 	    apply_errexit_errreturn(NULL);
649 	}
650     }
651 
652     comsfree(c);
653 
654     if (finally_exit)
655 	exit_shell();
656 }
657 
658 /* Executes the simple command. */
exec_simple_command(const command_T * c,bool finally_exit)659 void exec_simple_command(const command_T *c, bool finally_exit)
660 {
661     lastcmdsubstatus = Exit_SUCCESS;
662 
663     /* expand the command words */
664     int argc;
665     void **argv;
666     if (!expand_line(c->c_words, &argc, &argv)) {
667 	laststatus = Exit_EXPERROR;
668 	goto done;
669     }
670     if (is_interrupted())
671 	goto done1;
672 
673     /* execute the remaining part */
674     if (argc == 0)
675 	finally_exit |= exec_simple_command_without_words(c);
676     else
677 	finally_exit |=
678 	    exec_simple_command_with_words(c, argc, argv, finally_exit);
679 
680     /* cleanup */
681 done1:
682     plfree(argv, free);
683 done:
684     if (finally_exit)
685 	exit_shell();
686 }
687 
688 /* Executes the simple command that has no expanded words.
689  * Returns true if the shell should exit. */
exec_simple_command_without_words(const command_T * c)690 bool exec_simple_command_without_words(const command_T *c)
691 {
692     /* perform assignments */
693     bool ok = do_assignments(c->c_assigns, false, shopt_allexport);
694     print_xtrace(NULL);
695     last_assign = c->c_assigns;
696     if (!ok) {
697 	laststatus = Exit_ASSGNERR;
698 	return !is_interactive_now;
699     }
700 
701     /* done? */
702     if (c->c_redirs == NULL) {
703 	laststatus = lastcmdsubstatus;
704 	return false;
705     }
706 
707     /* create a subshell to perform redirections in */
708     fork_and_wait_T faw = fork_and_wait(0);
709     if (faw.cpid != 0) {
710 	/* parent process */
711 	if (faw.namep != NULL)
712 	    *faw.namep = command_to_wcs(c, false);
713 	return false;
714     }
715 
716     /* open redirections in subshell */
717     savefd_T *savefd;
718     ok = open_redirections(c->c_redirs, &savefd);
719     undo_redirections(savefd);
720     exit_shell_with_status(ok ? lastcmdsubstatus : Exit_REDIRERR);
721 }
722 
723 /* Executes the simple command that has one or more expanded words.
724  * `argv' must be a NULL-terminated array of pointers to wide strings that are
725  * the results of the word expansion on the simple command being executed.
726  * `argc' must be the number of words in `argv', which must be at least 1.
727  * If `finally_exit' is true, the shell process may be replaced by the command
728  * process. However, this function still may return in some cases.
729  * Returns true if the shell should exit. */
exec_simple_command_with_words(const command_T * c,int argc,void ** argv,bool finally_exit)730 bool exec_simple_command_with_words(
731 	const command_T *c, int argc, void **argv, bool finally_exit)
732 {
733     assert(argc > 0);
734 
735     char *argv0 = malloc_wcstombs(argv[0]);
736     if (argv0 == NULL)
737 	argv0 = xstrdup("");
738 
739     /* open redirections */
740     savefd_T *savefd;
741     if (!open_redirections(c->c_redirs, &savefd)) {
742 	/* On redirection error, the command is not executed. */
743 	laststatus = Exit_REDIRERR;
744 	apply_errexit_errreturn(NULL);
745 	if (posixly_correct && !is_interactive_now && is_special_builtin(argv0))
746 	    finally_exit = true;
747 	goto done;
748     }
749 
750     last_assign = c->c_assigns;
751 
752     /* check if the command is a special built-in or function */
753     commandinfo_T cmdinfo;
754     search_command(argv0, argv[0], &cmdinfo, SCT_BUILTIN | SCT_FUNCTION);
755     special_builtin_executed = (cmdinfo.type == CT_SPECIALBUILTIN);
756 
757     /* open a temporary variable environment */
758     bool temp = c->c_assigns != NULL && !special_builtin_executed;
759     if (temp)
760 	open_new_environment(true);
761 
762     /* perform the assignments */
763     if (!do_assignments(c->c_assigns, temp, true)) {
764 	/* On assignment error, the command is not executed. */
765 	print_xtrace(NULL);
766 	laststatus = Exit_ASSGNERR;
767 	if (!is_interactive_now)
768 	    finally_exit = true;
769 	goto done1;
770     }
771     print_xtrace(argv);
772 
773     /* find command path */
774     if (cmdinfo.type == CT_NONE) {
775 	search_command(argv0, argv[0], &cmdinfo,
776 		SCT_EXTERNAL | SCT_BUILTIN | SCT_CHECK);
777 	if (cmdinfo.type == CT_NONE) {
778 	    if (!posixly_correct && command_not_found_handler(argv))
779 		goto done1;
780 	    if (wcschr(argv[0], L'/') != NULL) {
781 		cmdinfo.type = CT_EXTERNALPROGRAM;
782 		cmdinfo.ci_path = argv0;
783 	    }
784 	}
785     }
786 
787     /* execute! */
788     wchar_t **namep = invoke_simple_command(&cmdinfo, argc, argv0, argv,
789 	    finally_exit && /* !temp && */ savefd == NULL);
790     if (namep != NULL)
791 	*namep = command_to_wcs(c, false);
792 
793     /* Redirections are not undone after a successful "exec" command:
794      * remove the saved data of file descriptors. */
795     if (exec_builtin_executed && laststatus == Exit_SUCCESS) {
796 	clear_savefd(savefd);
797 	savefd = NULL;
798     }
799     exec_builtin_executed = false;
800 
801     /* cleanup */
802 done1:
803     if (temp)
804 	close_current_environment();
805 done:
806     undo_redirections(savefd);
807     free(argv0);
808 
809     return finally_exit;
810 }
811 
812 /* Returns a pointer to the xtrace buffer.
813  * The buffer is initialized if not. */
get_xtrace_buffer(void)814 xwcsbuf_T *get_xtrace_buffer(void)
815 {
816     if (xtrace_buffer.contents == NULL)
817 	wb_init(&xtrace_buffer);
818     return &xtrace_buffer;
819 }
820 
821 /* Prints a trace if the "xtrace" option is on. */
print_xtrace(void * const * argv)822 void print_xtrace(void *const *argv)
823 {
824     bool tracevars = xtrace_buffer.contents != NULL
825 		  && xtrace_buffer.length > 0;
826 
827     if (shopt_xtrace
828 	    && (!is_executing_auxiliary || shopt_traceall)
829 	    && (tracevars || argv != NULL)
830 #if YASH_ENABLE_LINEEDIT
831 	    && !(le_state & LE_STATE_ACTIVE)
832 #endif
833 	    ) {
834 	bool first = true;
835 
836 	struct promptset_T prompt = get_prompt(4);
837 	print_prompt(prompt.main);
838 	print_prompt(prompt.styler);
839 
840 	if (tracevars) {
841 	    fprintf(stderr, "%ls", xtrace_buffer.contents + 1);
842 	    first = false;
843 	}
844 	if (argv != NULL) {
845 	    for (void *const *a = argv; *a != NULL; a++) {
846 		if (!first)
847 		    fputc(' ', stderr);
848 		first = false;
849 
850 		wchar_t *quoted = quote_as_word(*a);
851 		fprintf(stderr, "%ls", quoted);
852 		free(quoted);
853 	    }
854 	}
855 	fputc('\n', stderr);
856 
857 	print_prompt(PROMPT_RESET);
858 	free_prompt(prompt);
859     }
860     if (xtrace_buffer.contents != NULL) {
861 	wb_destroy(&xtrace_buffer);
862 	xtrace_buffer.contents = NULL;
863     }
864 }
865 
866 /* Searches for a command.
867  * The result is assigned to `*ci'.
868  * `name' and `wname' must contain the same string value.
869  * If the SCT_ALL flag is not set:
870  *   *  a function whose name contains a slash cannot be found
871  *   *  a regular built-in cannot be found in the POSIXly correct mode if the
872  *      SCT_EXTERNAL flag is not set either.
873  * If the SCT_EXTERNAL flag is set, the SCT_CHECK flag is not set, and `name'
874  * contains a slash, the external command of the given `name' is always found.
875  */
search_command(const char * restrict name,const wchar_t * restrict wname,commandinfo_T * restrict ci,enum srchcmdtype_T type)876 void search_command(
877 	const char *restrict name, const wchar_t *restrict wname,
878 	commandinfo_T *restrict ci, enum srchcmdtype_T type)
879 {
880     bool slash = wcschr(wname, L'/') != NULL;
881 
882     const builtin_T *bi;
883     if (!slash && (type & SCT_BUILTIN))
884 	bi = get_builtin(name);
885     else
886 	bi = NULL;
887     if (bi != NULL && bi->type == BI_SPECIAL) {
888 	ci->type = CT_SPECIALBUILTIN;
889 	ci->ci_builtin = bi->body;
890 	return;
891     }
892 
893     if ((type & SCT_FUNCTION) && (!slash || (type & SCT_ALL))) {
894 	command_T *funcbody = get_function(wname);
895 	if (funcbody != NULL) {
896 	    ci->type = CT_FUNCTION;
897 	    ci->ci_function = funcbody;
898 	    return;
899 	}
900     }
901 
902     if (bi != NULL) {
903 	if (bi->type == BI_SEMISPECIAL) {
904 	    ci->type = CT_SEMISPECIALBUILTIN;
905 	    ci->ci_builtin = bi->body;
906 	    return;
907 	} else if (!posixly_correct) {
908 	    goto regular_builtin;
909 	}
910     }
911 
912     if (slash) {
913 	if (type & SCT_EXTERNAL) {
914 	    if (!(type & SCT_CHECK) || is_executable_regular(name)) {
915 		ci->type = CT_EXTERNALPROGRAM;
916 		ci->ci_path = name;
917 		return;
918 	    }
919 	}
920     } else {
921 	if ((type & SCT_EXTERNAL) || (bi != NULL && (type & SCT_ALL))) {
922 	    const char *cmdpath;
923 	    if (type & SCT_STDPATH)
924 		cmdpath = get_command_path_default(name);
925 	    else
926 		cmdpath = get_command_path(name, false);
927 	    if (cmdpath != NULL) {
928 		if (bi != NULL) {
929 regular_builtin:
930 		    assert(bi->type == BI_REGULAR);
931 		    ci->type = CT_REGULARBUILTIN;
932 		    ci->ci_builtin = bi->body;
933 		} else {
934 		    ci->type = CT_EXTERNALPROGRAM;
935 		    ci->ci_path = cmdpath;
936 		}
937 		return;
938 	    }
939 	}
940     }
941 
942     /* command not found... */
943     ci->type = CT_NONE;
944     ci->ci_path = NULL;
945     return;
946 }
947 
948 /* Returns true iff the specified command is a special built-in. */
is_special_builtin(const char * cmdname)949 bool is_special_builtin(const char *cmdname)
950 {
951     const builtin_T *bi = get_builtin(cmdname);
952     return bi != NULL && bi->type == BI_SPECIAL;
953 }
954 
955 /* Executes $COMMAND_NOT_FOUND_HANDLER if any.
956  * `argv' is set to the positional parameters of the environment in which the
957  * handler is executed.
958  * Returns true iff the hander was executed and $HANDLED was set non-empty. */
command_not_found_handler(void * const * argv)959 bool command_not_found_handler(void *const *argv)
960 {
961     static bool handling = false;
962     if (handling)
963 	return false;  /* don't allow reentrance */
964     handling = true;
965 
966     bool handled;
967     int result;
968 
969     open_new_environment(false);
970     set_positional_parameters(argv);
971     set_variable(L VAR_HANDLED, xwcsdup(L""), SCOPE_LOCAL, false);
972 
973     result = exec_variable_as_auxiliary_(VAR_COMMAND_NOT_FOUND_HANDLER);
974     if (result >= 0) {
975 	const wchar_t *handledv = getvar(L VAR_HANDLED);
976 	handled = (handledv != NULL && handledv[0] != L'\0');
977     } else {
978 	handled = false;
979     }
980 
981     close_current_environment();
982 
983     handling = false;
984     if (handled) {
985 	laststatus = result;
986 	return true;
987     } else {
988 	return false;
989     }
990 }
991 
992 /* Invokes the simple command. */
993 /* `argv0' is the multibyte version of `argv[0]' */
invoke_simple_command(const commandinfo_T * ci,int argc,char * argv0,void ** argv,bool finally_exit)994 wchar_t **invoke_simple_command(
995 	const commandinfo_T *ci, int argc, char *argv0, void **argv,
996 	bool finally_exit)
997 {
998     assert(plcount(argv) == (size_t) argc);
999 
1000     fork_and_wait_T faw = { 0, NULL };
1001 
1002     switch (ci->type) {
1003     case CT_NONE:
1004 	xerror(0, Ngt("no such command `%s'"), argv0);
1005 	laststatus = Exit_NOTFOUND;
1006 	break;
1007     case CT_EXTERNALPROGRAM:
1008 	if (!finally_exit) {
1009 	    faw = fork_and_wait(t_leave);
1010 	    if (faw.cpid != 0)
1011 		break;
1012 	    finally_exit = true;
1013 	}
1014 	exec_external_program(ci->ci_path, argc, argv0, argv, environ);
1015 	break;
1016     case CT_SPECIALBUILTIN:
1017     case CT_SEMISPECIALBUILTIN:
1018     case CT_REGULARBUILTIN:
1019 	yash_error_message_count = 0;
1020 
1021 	const wchar_t *savecbn = current_builtin_name;
1022 	current_builtin_name = argv[0];
1023 
1024 	laststatus = ci->ci_builtin(argc, argv);
1025 
1026 	current_builtin_name = savecbn;
1027 	break;
1028     case CT_FUNCTION:
1029 	exec_function_body(ci->ci_function, &argv[1], finally_exit, false);
1030 	break;
1031     }
1032     if (finally_exit)
1033 	exit_shell();
1034     return faw.namep;
1035 }
1036 
1037 /* Executes the external program.
1038  *  path:  path to the program to be executed
1039  *  argc:  number of strings in `argv'
1040  *  argv0: multibyte version of `argv[0]'
1041  *  argv:  pointer to an array of pointers to wide strings that are passed to
1042  *         the program */
exec_external_program(const char * path,int argc,char * argv0,void ** argv,char ** envs)1043 void exec_external_program(
1044 	const char *path, int argc, char *argv0, void **argv, char **envs)
1045 {
1046     char *mbsargv[argc + 1];
1047     mbsargv[0] = argv0;
1048     for (int i = 1; i < argc; i++) {
1049 	mbsargv[i] = malloc_wcstombs(argv[i]);
1050 	if (mbsargv[i] == NULL)
1051 	    mbsargv[i] = xstrdup("");
1052     }
1053     mbsargv[argc] = NULL;
1054 
1055     restore_signals(true);
1056 
1057     xexecve(path, mbsargv, envs);
1058     int saveerrno = errno;
1059     if (saveerrno != ENOEXEC) {
1060 	if (saveerrno == EACCES && is_directory(path))
1061 	    saveerrno = EISDIR;
1062 	xerror(saveerrno,
1063 		strcmp(mbsargv[0], path) == 0
1064 		    ? Ngt("cannot execute command `%s'")
1065 		    : Ngt("cannot execute command `%s' (%s)"),
1066 		argv0, path);
1067     } else if (saveerrno != ENOENT) {
1068 	exec_fall_back_on_sh(argc, mbsargv, envs, path);
1069     }
1070     laststatus = (saveerrno == ENOENT) ? Exit_NOTFOUND : Exit_NOEXEC;
1071 
1072     set_signals();
1073 
1074     for (int i = 1; i < argc; i++)
1075 	free(mbsargv[i]);
1076 }
1077 
1078 /* Calls `execve' until it doesn't return EINTR. */
xexecve(const char * path,char * const * argv,char * const * envp)1079 int xexecve(const char *path, char *const *argv, char *const *envp)
1080 {
1081     do
1082 	execve(path, argv, envp);
1083     while (errno == EINTR);
1084     return -1;
1085 }
1086 
1087 /* Executes the specified command as a shell script by `exec'ing a shell.
1088  * `path' is the full path to the script file.
1089  * Returns iff failed to `exec'. */
exec_fall_back_on_sh(int argc,char * const * argv,char * const * envp,const char * path)1090 void exec_fall_back_on_sh(
1091 	int argc, char *const *argv, char *const *envp, const char *path)
1092 {
1093     assert(argv[argc] == NULL);
1094 
1095     char *args[argc + 3];
1096     size_t index = 0;
1097     args[index++] = "sh";
1098     args[index++] = (char *) "-";
1099     if (strcmp(path, "--") == 0)
1100 	args[index++] = "./--";
1101     else
1102 	args[index++] = (char *) path;
1103     for (int i = 1; i < argc; i++)
1104 	args[index++] = argv[i];
1105     args[index] = NULL;
1106 #if HAVE_PROC_SELF_EXE
1107     xexecve("/proc/self/exe", args, envp);
1108 #elif HAVE_PROC_CURPROC_FILE
1109     xexecve("/proc/curproc/file", args, envp);
1110 #elif HAVE_PROC_OBJECT_AOUT
1111     char *objpath = malloc_printf(
1112 	    "/proc/%jd/object/a.out", (intmax_t) getpid());
1113     xexecve(objpath, args, envp);
1114     free(objpath);
1115 #elif HAVE_PATHS_H && defined _PATH_BSHELL
1116     xexecve(_PATH_BSHELL, args, envp);
1117 #endif
1118     const char *shpath = get_command_path("sh", false);
1119     if (shpath != NULL)
1120 	xexecve(shpath, args, envp);
1121     else
1122 	errno = ENOENT;
1123     xerror(errno, Ngt("cannot invoke a new shell to execute script `%s'"),
1124 	    argv[0]);
1125 }
1126 
1127 /* Executes the specified command as a function.
1128  * `args' are the arguments to the function, which are wide strings cast to
1129  * (void *).
1130  * If `complete' is true, `set_completion_variables' will be called after a new
1131  * variable environment was opened before the function body is executed. */
exec_function_body(command_T * body,void * const * args,bool finally_exit,bool complete)1132 void exec_function_body(
1133 	command_T *body, void *const *args, bool finally_exit, bool complete)
1134 {
1135     execstate_T *saveexecstate = save_execstate();
1136     reset_execstate(false);
1137 
1138     bool saveser = suppresserrreturn;
1139     suppresserrreturn = false;
1140 
1141     open_new_environment(false);
1142     set_positional_parameters(args);
1143 #if YASH_ENABLE_LINEEDIT
1144     if (complete)
1145 	set_completion_variables();
1146 #else
1147     (void) complete;
1148 #endif
1149     exec_commands(body, finally_exit ? E_SELF : E_NORMAL);
1150     close_current_environment();
1151 
1152     cancel_return();
1153     suppresserrreturn = saveser;
1154     restore_execstate(saveexecstate);
1155 }
1156 
1157 /* Executes the specified command whose type is not `CT_SIMPLE'.
1158  * The redirections for the command is not performed in this function. */
exec_nonsimple_command(command_T * c,bool finally_exit)1159 void exec_nonsimple_command(command_T *c, bool finally_exit)
1160 {
1161     switch (c->c_type) {
1162     case CT_SIMPLE:
1163 	assert(false);
1164     case CT_SUBSHELL:
1165 	if (finally_exit) {
1166 	    /* This is the last command to execute in the current shell, hence
1167 	     * no need to make a new child. */
1168 	    become_child(0);
1169 	} else {
1170 	    /* make a child process to execute the command */
1171 	    fork_and_wait_T faw = fork_and_wait(0);
1172 	    if (faw.cpid != 0) {
1173 		if (faw.namep != NULL)
1174 		    *faw.namep = command_to_wcs(c, false);
1175 		break;
1176 	    }
1177 	    finally_exit = true;
1178 	}
1179 	// falls thru!
1180     case CT_GROUP:
1181 	exec_and_or_lists(c->c_subcmds, finally_exit);
1182 	break;
1183     case CT_IF:
1184 	exec_if(c, finally_exit);
1185 	break;
1186     case CT_FOR:
1187 	exec_for(c, finally_exit);
1188 	break;
1189     case CT_WHILE:
1190 	exec_while(c, finally_exit);
1191 	break;
1192     case CT_CASE:
1193 	exec_case(c, finally_exit);
1194 	break;
1195 #if YASH_ENABLE_DOUBLE_BRACKET
1196     case CT_BRACKET:
1197 	laststatus = exec_double_bracket(c);
1198 	if (finally_exit)
1199 	    exit_shell();
1200 	break;
1201 #endif /* YASH_ENABLE_DOUBLE_BRACKET */
1202     case CT_FUNCDEF:
1203 	exec_funcdef(c, finally_exit);
1204 	break;
1205     }
1206 }
1207 
1208 /* Executes the if command */
exec_if(const command_T * c,bool finally_exit)1209 void exec_if(const command_T *c, bool finally_exit)
1210 {
1211     assert(c->c_type == CT_IF);
1212 
1213     for (const ifcommand_T *cmds = c->c_ifcmds;
1214 	    cmds != NULL;
1215 	    cmds = cmds->next) {
1216 	if (need_break())
1217 	    goto done;
1218 	if (exec_condition(cmds->ic_condition)) {
1219 	    exec_and_or_lists(cmds->ic_commands, finally_exit);
1220 	    assert(!finally_exit);
1221 	    return;
1222 	}
1223     }
1224     laststatus = Exit_SUCCESS;
1225 done:
1226     if (finally_exit)
1227 	exit_shell();
1228 }
1229 
1230 /* Executes the condition of an if/while/until command. */
exec_condition(const and_or_T * c)1231 bool exec_condition(const and_or_T *c)
1232 {
1233     if (c == NULL)
1234 	return true;
1235 
1236     bool savesee = suppresserrexit, saveser = suppresserrreturn;
1237     suppresserrexit = suppresserrreturn = true;
1238     exec_and_or_lists(c, false);
1239     suppresserrexit = savesee, suppresserrreturn = saveser;
1240     return laststatus == Exit_SUCCESS;
1241 }
1242 
1243 /* Executes the for command. */
exec_for(const command_T * c,bool finally_exit)1244 void exec_for(const command_T *c, bool finally_exit)
1245 {
1246     assert(c->c_type == CT_FOR);
1247     execstate.loopnest++;
1248     execstate.breakloopnest = execstate.loopnest;
1249 
1250     int count;
1251     void **words;
1252 
1253     if (c->c_forwords != NULL) {
1254 	/* expand the words between "in" and "do" of the for command. */
1255 	if (!expand_line(c->c_forwords, &count, &words)) {
1256 	    laststatus = Exit_EXPERROR;
1257 	    apply_errexit_errreturn(NULL);
1258 	    goto finish;
1259 	}
1260     } else {
1261 	/* no "in" keyword in the for command: use the positional parameters */
1262 	struct get_variable_T v = get_variable(L"@");
1263 	assert(v.type == GV_ARRAY && v.values != NULL);
1264 	save_get_variable_values(&v);
1265 	count = (int) v.count;
1266 	words = v.values;
1267     }
1268 
1269 #define CHECK_LOOP                                      \
1270     if (execstate.breakloopnest < execstate.loopnest) { \
1271 	goto done;                                      \
1272     } else if (exception == E_CONTINUE) {               \
1273 	exception = E_NONE;                             \
1274 	continue;                                       \
1275     } else if (exception != E_NONE) {                   \
1276 	goto done;                                      \
1277     } else if (is_interrupted()) {                      \
1278 	goto done;                                      \
1279     } else (void) 0
1280 
1281     int i;
1282     for (i = 0; i < count; i++) {
1283 	if (!set_variable(c->c_forname, words[i],
1284 		    shopt_forlocal && !posixly_correct ?
1285 			SCOPE_LOCAL : SCOPE_GLOBAL,
1286 		    false)) {
1287 	    laststatus = Exit_ASSGNERR;
1288 	    goto done;
1289 	}
1290 	exec_and_or_lists(c->c_forcmds, finally_exit && i + 1 == count);
1291 
1292 	if (c->c_forcmds == NULL)
1293 	    handle_signals();
1294 	CHECK_LOOP;
1295     }
1296 
1297 done:
1298     while (++i < count)  /* free unused words */
1299 	free(words[i]);
1300     free(words);
1301     if (count == 0 && c->c_forcmds != NULL)
1302 	laststatus = Exit_SUCCESS;
1303 finish:
1304     execstate.loopnest--;
1305     if (finally_exit)
1306 	exit_shell();
1307 }
1308 
1309 /* Executes the while/until command. */
1310 /* The exit status of a while/until command is that of `c_whlcmds' executed
1311  * last.  If `c_whlcmds' is not executed at all, the status is 0 regardless of
1312  * `c_whlcond'. */
exec_while(const command_T * c,bool finally_exit)1313 void exec_while(const command_T *c, bool finally_exit)
1314 {
1315     assert(c->c_type == CT_WHILE);
1316     execstate.loopnest++;
1317     execstate.breakloopnest = execstate.loopnest;
1318 
1319     int status = Exit_SUCCESS;
1320     for (;;) {
1321 	bool cond = exec_condition(c->c_whlcond);
1322 
1323 	if (c->c_whlcond == NULL)
1324 	    handle_signals();
1325 	CHECK_LOOP;
1326 
1327 	if (cond != c->c_whltype)
1328 	    break;
1329 	if (c->c_whlcmds != NULL) {
1330 	    exec_and_or_lists(c->c_whlcmds, false);
1331 	    status = laststatus;
1332 	}
1333 
1334 	if (c->c_whlcmds == NULL)
1335 	    handle_signals();
1336 	CHECK_LOOP;
1337     }
1338 
1339     laststatus = status;
1340 done:
1341     execstate.loopnest--;
1342     if (finally_exit)
1343 	exit_shell();
1344 }
1345 #undef CHECK_LOOP
1346 
1347 /* Executes the case command. */
exec_case(const command_T * c,bool finally_exit)1348 void exec_case(const command_T *c, bool finally_exit)
1349 {
1350     assert(c->c_type == CT_CASE);
1351 
1352     wchar_t *word = expand_single(c->c_casword, TT_SINGLE, Q_WORD, ES_NONE);
1353     if (word == NULL)
1354 	goto fail;
1355 
1356     for (const caseitem_T *ci = c->c_casitems; ci != NULL; ci = ci->next) {
1357 	for (void **pats = ci->ci_patterns; *pats != NULL; pats++) {
1358 	    wchar_t *pattern =
1359 		expand_single(*pats, TT_SINGLE, Q_WORD, ES_QUOTED);
1360 	    if (pattern == NULL)
1361 		goto fail;
1362 
1363 	    bool match = match_pattern(word, pattern);
1364 	    free(pattern);
1365 	    if (match) {
1366 		if (ci->ci_commands != NULL) {
1367 		    exec_and_or_lists(ci->ci_commands, finally_exit);
1368 		    goto done;
1369 		} else {
1370 		    goto success;
1371 		}
1372 	    }
1373 	}
1374     }
1375 success:
1376     laststatus = Exit_SUCCESS;
1377 done:
1378     free(word);
1379     if (finally_exit)
1380 	exit_shell();
1381     return;
1382 
1383 fail:
1384     laststatus = Exit_EXPERROR;
1385     apply_errexit_errreturn(NULL);
1386     goto done;
1387 }
1388 
1389 /* Executes the function definition. */
exec_funcdef(const command_T * c,bool finally_exit)1390 void exec_funcdef(const command_T *c, bool finally_exit)
1391 {
1392     assert(c->c_type == CT_FUNCDEF);
1393 
1394     wchar_t *funcname =
1395 	expand_single(c->c_funcname, TT_SINGLE, Q_WORD, ES_NONE);
1396     if (funcname != NULL) {
1397 	if (define_function(funcname, c->c_funcbody))
1398 	    laststatus = Exit_SUCCESS;
1399 	else
1400 	    laststatus = Exit_ASSGNERR;
1401 	free(funcname);
1402     } else {
1403 	laststatus = Exit_EXPERROR;
1404     }
1405 
1406     if (finally_exit)
1407 	exit_shell();
1408 }
1409 
1410 /* Forks a new child process and wait for it to finish.
1411  * `sigtype' is passed to `fork_and_reset'.
1412  * In the parent process, this function updates `laststatus' to the exit status
1413  * of the child.
1414  * In the child process, this function does not wait for anything.
1415  * Returns a pair of the return value of `fork' and a pointer to a pointer to
1416  * a wide string. If the pointer is non-null, the caller must assign it a
1417  * pointer to a newly malloced wide string that is the job name of the child. */
fork_and_wait(sigtype_T sigtype)1418 fork_and_wait_T fork_and_wait(sigtype_T sigtype)
1419 {
1420     fork_and_wait_T result;
1421     result.cpid = fork_and_reset(0, true, sigtype);
1422     if (result.cpid < 0) {
1423 	/* Fork failed. */
1424 	laststatus = Exit_NOEXEC;
1425 	result.namep = NULL;
1426     } if (result.cpid > 0) {
1427 	/* parent process */
1428 	result.namep = wait_for_child(
1429 		result.cpid,
1430 		doing_job_control_now ? result.cpid : 0,
1431 		doing_job_control_now);
1432     } else {
1433 	/* child process */
1434 	result.namep = NULL;
1435     }
1436     return result;
1437 }
1438 
1439 /* Forks a subshell and does some settings.
1440  * If job control is active, the child process's process group ID is set to
1441  * `pgid'. If `pgid' is 0, the child process's process ID is used as the process
1442  * group ID..
1443  * If `pgid' is negative or job control is inactive, the process group ID is not
1444  * changed.
1445  * If job control is active and `fg' is true, the child process becomes a
1446  * foreground process.
1447  * `sigtype' specifies the settings of signals in the child.
1448  * `sigtype' is a bitwise OR of the followings:
1449  *   t_quitint: SIGQUIT & SIGINT are ignored if the parent's job control is off
1450  *   t_tstp: SIGTSTP is ignored if the parent is job-controlling
1451  *   t_leave: Don't clear traps and shell FDs. Restore the signal mask for
1452  *          SIGCHLD. Don't reset `execstate'. This option must be used iff the
1453  *          shell is going to `exec' to an external program.
1454  * Returns the return value of `fork'. */
fork_and_reset(pid_t pgid,bool fg,sigtype_T sigtype)1455 pid_t fork_and_reset(pid_t pgid, bool fg, sigtype_T sigtype)
1456 {
1457     sigset_t savemask;
1458     if (sigtype & (t_quitint | t_tstp)) {
1459 	/* block all signals to prevent the race condition */
1460 	sigset_t all;
1461 	sigfillset(&all);
1462 	sigemptyset(&savemask);
1463 	sigprocmask(SIG_BLOCK, &all, &savemask);
1464     }
1465 
1466     pid_t cpid = fork();
1467 
1468     if (cpid != 0) {
1469 	if (cpid < 0) {
1470 	    /* fork failure */
1471 	    xerror(errno, Ngt("cannot make a child process"));
1472 	} else {
1473 	    /* parent process */
1474 	    if (doing_job_control_now && pgid >= 0)
1475 		setpgid(cpid, pgid);
1476 	}
1477 	if (sigtype & (t_quitint | t_tstp))
1478 	    sigprocmask(SIG_SETMASK, &savemask, NULL);
1479     } else {
1480 	/* child process */
1481 	if (doing_job_control_now && pgid >= 0) {
1482 	    setpgid(0, pgid);
1483 	    if (pgid == 0)
1484 		pgid = getpgrp();
1485 	    shell_pgid = pgid;
1486 	    if (fg)
1487 		put_foreground(pgid);
1488 	}
1489 	become_child(sigtype);  /* signal mask is restored here */
1490     }
1491     return cpid;
1492 }
1493 
1494 /* Resets traps, signal handlers, etc. for the current process to become a
1495  * subshell. See `fork_and_reset' for the meaning of the `sigtype' parameter. */
become_child(sigtype_T sigtype)1496 void become_child(sigtype_T sigtype)
1497 {
1498     if (sigtype & t_leave) {
1499 	clear_exit_trap();
1500     } else {
1501 	phantomize_traps();
1502 	neglect_all_jobs();
1503 #if YASH_ENABLE_HISTORY
1504 	close_history_file();
1505 #endif
1506 	reset_execstate(true);
1507     }
1508 
1509     if (sigtype & t_quitint)
1510 	if (!doing_job_control_now)
1511 	    ignore_sigquit_and_sigint();
1512     if (sigtype & t_tstp)
1513 	if (doing_job_control_now)
1514 	    ignore_sigtstp();
1515 
1516     restore_signals(sigtype & t_leave);  /* signal mask is restored here */
1517     clear_shellfds(sigtype & t_leave);
1518     is_interactive_now = false;
1519     suppresserrreturn = false;
1520     exitstatus = -1;
1521 }
1522 
1523 /* Executes the command substitution and returns the string to substitute with.
1524  * This function blocks until the command finishes.
1525  * The return value is a newly-malloced string without a trailing newline.
1526  * NULL is returned on error. */
exec_command_substitution(const embedcmd_T * cmdsub)1527 wchar_t *exec_command_substitution(const embedcmd_T *cmdsub)
1528 {
1529     int pipefd[2];
1530     pid_t cpid;
1531 
1532     if (cmdsub->is_preparsed
1533 	    ? cmdsub->value.preparsed == NULL
1534 	    : cmdsub->value.unparsed[0] == L'\0')  /* empty command */
1535 	return xwcsdup(L"");
1536 
1537     /* open a pipe to receive output from the command */
1538     if (pipe(pipefd) < 0) {
1539 	xerror(errno, Ngt("cannot open a pipe for the command substitution"));
1540 	return NULL;
1541     }
1542 
1543     /* If the child is stopped by SIGTSTP, it can never be resumed and
1544      * the shell will be stuck. So we specify the `t_tstp' flag to prevent the
1545      * child from being stopped by SIGTSTP. */
1546     cpid = fork_and_reset(-1, false, t_tstp);
1547     if (cpid < 0) {
1548 	/* fork failure */
1549 	xclose(pipefd[PIPE_IN]);
1550 	xclose(pipefd[PIPE_OUT]);
1551 	lastcmdsubstatus = Exit_NOEXEC;
1552 	return NULL;
1553     } else if (cpid > 0) {
1554 	/* parent process */
1555 	FILE *f;
1556 
1557 	xclose(pipefd[PIPE_OUT]);
1558 	f = fdopen(pipefd[PIPE_IN], "r");
1559 	if (f == NULL) {
1560 	    xerror(errno,
1561 		    Ngt("cannot open a pipe for the command substitution"));
1562 	    xclose(pipefd[PIPE_IN]);
1563 	    lastcmdsubstatus = Exit_NOEXEC;
1564 	    return NULL;
1565 	}
1566 
1567 	/* read output from the command */
1568 	xwcsbuf_T buf;
1569 	wint_t c;
1570 	wb_init(&buf);
1571 	while ((c = fgetwc(f)) != WEOF)
1572 	    wb_wccat(&buf, c);
1573 	fclose(f);
1574 
1575 	/* wait for the child to finish */
1576 	int savelaststatus = laststatus;
1577 	wait_for_child(cpid, 0, false);
1578 	lastcmdsubstatus = laststatus;
1579 	laststatus = savelaststatus;
1580 
1581 	/* trim trailing newlines and return */
1582 	size_t len = buf.length;
1583 	while (len > 0 && buf.contents[len - 1] == L'\n')
1584 	    len--;
1585 	return wb_towcs(wb_truncate(&buf, len));
1586     } else {
1587 	/* child process */
1588 	xclose(pipefd[PIPE_IN]);
1589 	if (pipefd[PIPE_OUT] != STDOUT_FILENO) {  /* connect the pipe */
1590 	    if (xdup2(pipefd[PIPE_OUT], STDOUT_FILENO) < 0)
1591 		exit(Exit_NOEXEC);
1592 	    xclose(pipefd[PIPE_OUT]);
1593 	}
1594 
1595 	if (cmdsub->is_preparsed)
1596 	    exec_and_or_lists(cmdsub->value.preparsed, true);
1597 	else
1598 	    exec_wcs(cmdsub->value.unparsed, gt("command substitution"), true);
1599 	assert(false);
1600     }
1601 }
1602 
1603 /* Executes the value of the specified variable.
1604  * The variable value is parsed as commands.
1605  * If the `varname' names an array, every element of the array is executed (but
1606  * if the iteration is interrupted by the "break -i" command, the remaining
1607  * elements are not executed).
1608  * `codename' is passed to `exec_wcs' as the command name.
1609  * Returns the exit status of the executed command (or zero if none executed, or
1610  * -1 if the variable is unset).
1611  * When this function returns, `laststatus' is restored to the original value.*/
exec_variable_as_commands(const wchar_t * varname,const char * codename)1612 int exec_variable_as_commands(const wchar_t *varname, const char *codename)
1613 {
1614     struct get_variable_T gv = get_variable(varname);
1615 
1616     switch (gv.type) {
1617 	case GV_NOTFOUND:
1618 	    return -1;
1619 	case GV_SCALAR:
1620 	case GV_ARRAY:
1621 	    break;
1622 	case GV_ARRAY_CONCAT:
1623 	    /* should execute the concatenated value, but is not supported now*/
1624 	    return -1;
1625     }
1626 
1627     /* copy the array values in case they are unset during execution */
1628     save_get_variable_values(&gv);
1629 
1630     /* prevent "break" and "continue" */
1631     execstate_T *saveexecstate = save_execstate();
1632     // reset_execstate(false); /* don't reset `execstate.noreturn' */
1633     execstate.loopnest = 0;
1634 
1635     int result = exec_iteration(gv.values, codename);
1636 
1637     restore_execstate(saveexecstate);
1638 
1639     plfree(gv.values, free);
1640     return result;
1641 }
1642 
1643 /* Executes commands in the given array of wide strings (iterative execution).
1644  * The strings are parsed and executed one by one.
1645  * If the iteration is interrupted by the "break -i" command, the remaining
1646  * elements are not executed.
1647  * `codename' is passed to `exec_wcs' as the command name.
1648  * Returns the exit status of the executed command (or zero if none executed).
1649  * When this function returns, `laststatus' is restored to the original value.*/
exec_iteration(void * const * commands,const char * codename)1650 int exec_iteration(void *const *commands, const char *codename)
1651 {
1652     int savelaststatus = laststatus, commandstatus = Exit_SUCCESS;
1653     bool saveiterating = execstate.iterating;
1654     execstate.iterating = true;
1655 
1656     for (void *const *command = commands; *command != NULL; command++) {
1657 	exec_wcs(*command, codename, false);
1658 	commandstatus = laststatus;
1659 	laststatus = savelaststatus;
1660 	switch (exception) {
1661 	    case E_BREAK_ITERATION:
1662 		exception = E_NONE;
1663 		/* falls thru! */
1664 	    default:
1665 		goto done;
1666 	    case E_CONTINUE_ITERATION:
1667 		exception = E_NONE;
1668 		/* falls thru! */
1669 	    case E_NONE:
1670 		break;
1671 	}
1672 	if (execstate.breakloopnest < execstate.loopnest || is_interrupted())
1673 	    goto done;
1674     }
1675 done:
1676     execstate.iterating = saveiterating;
1677     return commandstatus;
1678 }
1679 
1680 /* Calls `exec_variable_as_commands' with `is_executing_auxiliary' set to true.
1681  */
exec_variable_as_auxiliary(const wchar_t * varname,const char * codename)1682 int exec_variable_as_auxiliary(const wchar_t *varname, const char *codename)
1683 {
1684     bool save_executing_auxiliary = is_executing_auxiliary;
1685     is_executing_auxiliary = true;
1686     int result = exec_variable_as_commands(varname, codename);
1687     is_executing_auxiliary = save_executing_auxiliary;
1688     return result;
1689 }
1690 
1691 #if YASH_ENABLE_LINEEDIT
1692 
1693 /* Autoloads the specified file to load a completion function definition.
1694  * String `cmdname', which may be NULL, is used as the only positional parameter
1695  * during script execution.
1696  * Returns true iff a file was autoloaded. */
autoload_completion_function_file(const wchar_t * filename,const wchar_t * cmdname)1697 bool autoload_completion_function_file(
1698 	const wchar_t *filename, const wchar_t *cmdname)
1699 {
1700     char *mbsfilename = malloc_wcstombs(filename);
1701     if (mbsfilename == NULL)
1702 	return false;
1703 
1704     char *path = which(mbsfilename, get_path_array(PA_LOADPATH),
1705 	    is_readable_regular);
1706     if (path == NULL) {
1707 	le_compdebug("file \"%s\" was not found in $YASH_LOADPATH",
1708 		mbsfilename);
1709 	free(mbsfilename);
1710 	return false;
1711     }
1712 
1713     int fd = move_to_shellfd(open(path, O_RDONLY));
1714     if (fd < 0) {
1715 	le_compdebug("cannot open file \"%s\"", path);
1716 	free(path);
1717 	return false;
1718     }
1719 
1720     execstate_T *saveexecstate = save_execstate();
1721     int savelaststatus = laststatus;
1722     bool saveposix = posixly_correct;
1723     reset_execstate(true);
1724     posixly_correct = false;
1725     open_new_environment(false);
1726     set_positional_parameters((void *[]) { (void *) cmdname, NULL });
1727 
1728     le_compdebug("executing file \"%s\" (autoload)", path);
1729     exec_input(fd, mbsfilename, 0);
1730     le_compdebug("finished executing file \"%s\"", path);
1731 
1732     close_current_environment();
1733     posixly_correct = saveposix;
1734     laststatus = savelaststatus;
1735     cancel_return();
1736     restore_execstate(saveexecstate);
1737     remove_shellfd(fd);
1738     xclose(fd);
1739     free(path);
1740     free(mbsfilename);
1741     return true;
1742 }
1743 
1744 /* Calls the function whose name is `funcname' as a completion function.
1745  * Returns false if no such function has been defined. */
call_completion_function(const wchar_t * funcname)1746 bool call_completion_function(const wchar_t *funcname)
1747 {
1748     command_T *func = get_function(funcname);
1749     if (func == NULL) {
1750 	le_compdebug("completion function \"%ls\" is not defined", funcname);
1751 	return false;
1752     }
1753 
1754     execstate_T *saveexecstate = save_execstate();
1755     int savelaststatus = laststatus;
1756     bool saveposix = posixly_correct, saveerrreturn = shopt_errreturn;
1757     reset_execstate(true);
1758     posixly_correct = false, shopt_errreturn = false;
1759 
1760     le_compdebug("executing completion function \"%ls\"", funcname);
1761 
1762     exec_function_body(func, (void *[]) { NULL }, false, true);
1763 
1764     le_compdebug("finished executing completion function \"%ls\"", funcname);
1765     le_compdebug("  with the exit status of %d", laststatus);
1766 
1767     posixly_correct = saveposix, shopt_errreturn = saveerrreturn;
1768     laststatus = savelaststatus;
1769     cancel_return();
1770     restore_execstate(saveexecstate);
1771 
1772     return true;
1773 }
1774 
1775 #endif /* YASH_ENABLE_LINEEDIT */
1776 
1777 
1778 /********** Built-ins **********/
1779 
1780 static int exec_builtin_2(int argc, void **argv, const wchar_t *as, bool clear)
1781     __attribute__((nonnull(2)));
1782 static int command_builtin_execute(
1783 	int argc, void **argv, enum srchcmdtype_T type)
1784     __attribute__((nonnull));
1785 static bool print_command_info(const wchar_t *commandname,
1786 	enum srchcmdtype_T type, bool alias, bool keyword, bool humanfriendly)
1787     __attribute__((nonnull));
1788 static void print_command_absolute_path(
1789 	const char *name, const char *path, bool humanfriendly)
1790     __attribute__((nonnull));
1791 static void print_command_path(
1792 	const char *name, const char *path, bool humanfriendly)
1793     __attribute__((nonnull));
1794 
1795 /* Options for the "break", "continue" and "eval" built-ins. */
1796 const struct xgetopt_T iter_options[] = {
1797     { L'i', L"iteration", OPTARG_NONE, false, NULL, },
1798 #if YASH_ENABLE_HELP
1799     { L'-', L"help",      OPTARG_NONE, false, NULL, },
1800 #endif
1801     { L'\0', NULL, 0, false, NULL, },
1802 };
1803 
1804 /* Options for the "return" built-in. */
1805 const struct xgetopt_T return_options[] = {
1806     { L'n', L"no-return", OPTARG_NONE, false, NULL, },
1807 #if YASH_ENABLE_HELP
1808     { L'-', L"help",      OPTARG_NONE, false, NULL, },
1809 #endif
1810     { L'\0', NULL, 0, false, NULL, },
1811 };
1812 
1813 /* The "return" built-in, which accepts the following option:
1814  *  -n: don't return from a function. */
return_builtin(int argc,void ** argv)1815 int return_builtin(int argc, void **argv)
1816 {
1817     bool noreturn = false;
1818 
1819     const struct xgetopt_T *opt;
1820     xoptind = 0;
1821     while ((opt = xgetopt(argv, return_options, 0)) != NULL) {
1822 	switch (opt->shortopt) {
1823 	    case L'n':
1824 		noreturn = true;
1825 		break;
1826 #if YASH_ENABLE_HELP
1827 	    case L'-':
1828 		return print_builtin_help(ARGV(0));
1829 #endif
1830 	    default:
1831 		return special_builtin_error(Exit_ERROR);
1832 	}
1833     }
1834 
1835     if (!validate_operand_count(argc - xoptind, 0, 1))
1836 	return special_builtin_error(Exit_ERROR);
1837 
1838     int status;
1839     const wchar_t *statusstr = ARGV(xoptind);
1840     if (statusstr != NULL) {
1841 	if (!xwcstoi(statusstr, 10, &status) || status < 0) {
1842 	    xerror(0, Ngt("`%ls' is not a valid integer"), statusstr);
1843 	    status = Exit_ERROR;
1844 	    special_builtin_error(status);
1845 	    /* return anyway */
1846 	}
1847     } else {
1848 	status = (savelaststatus >= 0) ? savelaststatus : laststatus;
1849     }
1850     if (!noreturn) {
1851 	if (execstate.noreturn && is_interactive_now) {
1852 	    xerror(0, Ngt("cannot be used in the interactive mode"));
1853 	    return Exit_FAILURE;
1854 	}
1855 	exception = E_RETURN;
1856     }
1857     return status;
1858 }
1859 
1860 #if YASH_ENABLE_HELP
1861 const char return_help[] = Ngt(
1862 "return from a function or script"
1863 );
1864 const char return_syntax[] = Ngt(
1865 "\treturn [-n] [exit_status]\n"
1866 );
1867 #endif
1868 
1869 /* The "break"/"continue" built-in, which accepts the following option:
1870  *  -i: iterative execution */
break_builtin(int argc,void ** argv)1871 int break_builtin(int argc, void **argv)
1872 {
1873     bool iter = false;
1874 
1875     const struct xgetopt_T *opt;
1876     xoptind = 0;
1877     while ((opt = xgetopt(argv, iter_options, 0)) != NULL) {
1878 	switch (opt->shortopt) {
1879 	    case L'i':
1880 		iter = true;
1881 		break;
1882 #if YASH_ENABLE_HELP
1883 	    case L'-':
1884 		return print_builtin_help(ARGV(0));
1885 #endif
1886 	    default:
1887 		return special_builtin_error(Exit_ERROR);
1888 	}
1889     }
1890 
1891     if (!validate_operand_count(argc - xoptind, 0, iter ? 0 : 1))
1892 	return special_builtin_error(Exit_ERROR);
1893 
1894     if (iter) {
1895 	/* break/continue iteration */
1896 	if (!execstate.iterating) {
1897 	    xerror(0, Ngt("not in an iteration"));
1898 	    return Exit_ERROR;
1899 	}
1900 	if (wcscmp(ARGV(0), L"break") == 0) {
1901 	    exception = E_BREAK_ITERATION;
1902 	} else {
1903 	    assert(wcscmp(ARGV(0), L"continue") == 0);
1904 	    exception = E_CONTINUE_ITERATION;
1905 	}
1906 	return laststatus;
1907     } else {
1908 	unsigned count;
1909 	const wchar_t *countstr = ARGV(xoptind);
1910 	if (countstr == NULL) {
1911 	    count = 1;
1912 	} else {
1913 	    unsigned long countl;
1914 	    if (!xwcstoul(countstr, 0, &countl)) {
1915 		xerror(0, Ngt("`%ls' is not a valid integer"), countstr);
1916 		return special_builtin_error(Exit_ERROR);
1917 	    } else if (countl == 0) {
1918 		xerror(0, Ngt("%u is not a positive integer"), 0u);
1919 		return special_builtin_error(Exit_ERROR);
1920 	    } else if (countl > UINT_MAX) {
1921 		count = UINT_MAX;
1922 	    } else {
1923 		count = (unsigned) countl;
1924 	    }
1925 	}
1926 	assert(count > 0);
1927 	if (execstate.loopnest == 0) {
1928 	    xerror(0, Ngt("not in a loop"));
1929 	    return special_builtin_error(Exit_ERROR);
1930 	}
1931 	if (count > execstate.loopnest)
1932 	    count = execstate.loopnest;
1933 	if (wcscmp(ARGV(0), L"break") == 0) {
1934 	    execstate.breakloopnest = execstate.loopnest - count;
1935 	} else {
1936 	    assert(wcscmp(ARGV(0), L"continue") == 0);
1937 	    execstate.breakloopnest = execstate.loopnest - count + 1;
1938 	    exception = E_CONTINUE;
1939 	}
1940 	return Exit_SUCCESS;
1941     }
1942 }
1943 
1944 #if YASH_ENABLE_HELP
1945 
1946 const char break_help[] = Ngt(
1947 "exit a loop"
1948 );
1949 const char break_syntax[] = Ngt(
1950 "\tbreak [count]\n"
1951 "\tbreak -i\n"
1952 );
1953 
1954 const char continue_help[] = Ngt(
1955 "continue a loop"
1956 );
1957 const char continue_syntax[] = Ngt(
1958 "\tcontinue [count]\n"
1959 "\tcontinue -i\n"
1960 );
1961 
1962 #endif
1963 
1964 /* The "eval" built-in, which accepts the following option:
1965  *  -i: iterative execution */
eval_builtin(int argc,void ** argv)1966 int eval_builtin(int argc __attribute__((unused)), void **argv)
1967 {
1968     bool iter = false;
1969 
1970     const struct xgetopt_T *opt;
1971     xoptind = 0;
1972     while ((opt = xgetopt(argv, iter_options, XGETOPT_POSIX)) != NULL) {
1973 	switch (opt->shortopt) {
1974 	    case L'i':
1975 		iter = true;
1976 		break;
1977 #if YASH_ENABLE_HELP
1978 	    case L'-':
1979 		return print_builtin_help(ARGV(0));
1980 #endif
1981 	    default:
1982 		return special_builtin_error(Exit_ERROR);
1983 	}
1984     }
1985 
1986     if (iter) {
1987 	return exec_iteration(&argv[xoptind], "eval");
1988     } else {
1989 	wchar_t *args = joinwcsarray(&argv[xoptind], L" ");
1990 	exec_wcs(args, "eval", false);
1991 	free(args);
1992 	return laststatus;
1993     }
1994 }
1995 
1996 #if YASH_ENABLE_HELP
1997 const char eval_help[] = Ngt(
1998 "evaluate arguments as a command"
1999 );
2000 const char eval_syntax[] = Ngt(
2001 "\teval [-i] [argument...]\n"
2002 );
2003 #endif
2004 
2005 /* Options for the "." built-in. */
2006 const struct xgetopt_T dot_options[] = {
2007     { L'A', L"no-alias", OPTARG_NONE, false, NULL, },
2008     { L'L', L"autoload", OPTARG_NONE, false, NULL, },
2009 #if YASH_ENABLE_HELP
2010     { L'-', L"help",     OPTARG_NONE, false, NULL, },
2011 #endif
2012     { L'\0', NULL, 0, false, NULL, },
2013 };
2014 
2015 /* The "." built-in, which accepts the following option:
2016  *  -A: disable aliases
2017  *  -L: autoload */
dot_builtin(int argc,void ** argv)2018 int dot_builtin(int argc, void **argv)
2019 {
2020     bool enable_alias = true, autoload = false;
2021 
2022     const struct xgetopt_T *opt;
2023     xoptind = 0;
2024     while ((opt = xgetopt(argv, dot_options, XGETOPT_POSIX)) != NULL) {
2025 	switch (opt->shortopt) {
2026 	    case L'A':
2027 		enable_alias = false;
2028 		break;
2029 	    case L'L':
2030 		autoload = true;
2031 		break;
2032 #if YASH_ENABLE_HELP
2033 	    case L'-':
2034 		return print_builtin_help(ARGV(0));
2035 #endif
2036 	    default:
2037 		return special_builtin_error(Exit_ERROR);
2038 	}
2039     }
2040 
2041     const wchar_t *filename = ARGV(xoptind++);
2042     if (filename == NULL)
2043 	return special_builtin_error(insufficient_operands_error(1));
2044 
2045     bool has_args = xoptind < argc;
2046     if (has_args && posixly_correct)
2047 	return special_builtin_error(too_many_operands_error(1));
2048 
2049     char *mbsfilename = malloc_wcstombs(filename);
2050     if (mbsfilename == NULL) {
2051 	xerror(EILSEQ, Ngt("unexpected error"));
2052 	return Exit_ERROR;
2053     }
2054 
2055     char *path;
2056     if (autoload) {
2057 	path = which(mbsfilename, get_path_array(PA_LOADPATH),
2058 		is_readable_regular);
2059 	if (path == NULL) {
2060 	    xerror(0, Ngt("file `%s' was not found in $YASH_LOADPATH"),
2061 		    mbsfilename);
2062 	    goto error;
2063 	}
2064     } else if (wcschr(filename, L'/') == NULL) {
2065 	path = which(mbsfilename, get_path_array(PA_PATH), is_readable_regular);
2066 	if (path == NULL) {
2067 	    if (!posixly_correct) {
2068 		path = mbsfilename;
2069 	    } else {
2070 		xerror(0, Ngt("file `%s' was not found in $PATH"), mbsfilename);
2071 		goto error;
2072 	    }
2073 	}
2074     } else {
2075 	path = mbsfilename;
2076     }
2077 
2078     int fd = move_to_shellfd(open(path, O_RDONLY));
2079     if (path != mbsfilename)
2080 	free(path);
2081     if (fd < 0) {
2082 	xerror(errno, Ngt("cannot open file `%s'"), mbsfilename);
2083 	goto error;
2084     }
2085 
2086     if (has_args) {
2087 	open_new_environment(false);
2088 	set_positional_parameters(&argv[xoptind]);
2089     }
2090 
2091     execstate_T *saveexecstate = save_execstate();
2092     reset_execstate(false);
2093 
2094     bool saveser = suppresserrreturn;
2095     suppresserrreturn = false;
2096 
2097     exec_input(fd, mbsfilename, enable_alias ? XIO_SUBST_ALIAS : 0);
2098 
2099     cancel_return();
2100     suppresserrreturn = saveser;
2101     restore_execstate(saveexecstate);
2102     remove_shellfd(fd);
2103     xclose(fd);
2104     free(mbsfilename);
2105 
2106     if (has_args) {
2107 	close_current_environment();
2108     }
2109 
2110     return laststatus;
2111 
2112 error:
2113     free(mbsfilename);
2114     if (special_builtin_executed && !is_interactive_now)
2115 	exit_shell_with_status(Exit_FAILURE);
2116     return Exit_FAILURE;
2117 }
2118 
2119 #if YASH_ENABLE_HELP
2120 const char dot_help[] = Ngt(
2121 "read a file and execute commands"
2122 );
2123 const char dot_syntax[] = Ngt(
2124 "\t. [-AL] file [argument...]\n"
2125 );
2126 #endif
2127 
2128 /* Options for the "exec" built-in. */
2129 const struct xgetopt_T exec_options[] = {
2130     { L'a', L"as",    OPTARG_REQUIRED, false, NULL, },
2131     { L'c', L"clear", OPTARG_NONE,     false, NULL, },
2132     { L'f', L"force", OPTARG_NONE,     false, NULL, },
2133 #if YASH_ENABLE_HELP
2134     { L'-', L"help",  OPTARG_NONE,     false, NULL, },
2135 #endif
2136     { L'\0', NULL, 0, false, NULL, },
2137 };
2138 
2139 /* The "exec" built-in, which accepts the following options:
2140  *  -a name: give <name> as argv[0] to the command
2141  *  -c: don't pass environment variables to the command
2142  *  -f: suppress error when we have stopped jobs */
exec_builtin(int argc,void ** argv)2143 int exec_builtin(int argc, void **argv)
2144 {
2145     const wchar_t *as = NULL;
2146     bool clear = false, force = false;
2147 
2148     const struct xgetopt_T *opt;
2149     xoptind = 0;
2150     while ((opt = xgetopt(argv, exec_options, XGETOPT_POSIX)) != NULL) {
2151 	switch (opt->shortopt) {
2152 	    case L'a':  as = xoptarg;  break;
2153 	    case L'c':  clear = true;  break;
2154 	    case L'f':  force = true;  break;
2155 #if YASH_ENABLE_HELP
2156 	    case L'-':
2157 		return print_builtin_help(ARGV(0));
2158 #endif
2159 	    default:
2160 		return special_builtin_error(Exit_ERROR);
2161 	}
2162     }
2163 
2164     exec_builtin_executed = true;
2165 
2166     if (xoptind == argc)
2167 	return Exit_SUCCESS;
2168     if (!posixly_correct && is_interactive_now && !force) {
2169 	size_t sjc = stopped_job_count();
2170 	if (sjc > 0) {
2171 	    fprintf(stderr,
2172 		    ngt("You have a stopped job!",
2173 			"You have %zu stopped jobs!",
2174 			sjc),
2175 		    sjc);
2176 	    fprintf(stderr, gt("  Use the -f option to exec anyway.\n"));
2177 	    return Exit_FAILURE;
2178 	}
2179     }
2180 
2181     return exec_builtin_2(argc - xoptind, &argv[xoptind], as, clear);
2182 }
2183 
2184 /* The main part of the "exec" built-in.
2185  * argc, argv: the operands (not arguments) of the "exec" built-in.
2186  * as: value of the -a option or NULL
2187  * clear: true iff the -c option is specified */
exec_builtin_2(int argc,void ** argv,const wchar_t * as,bool clear)2188 int exec_builtin_2(int argc, void **argv, const wchar_t *as, bool clear)
2189 {
2190     int err;
2191 
2192     const wchar_t *saveargv0 = ARGV(0);
2193     char *mbssaveargv0 = malloc_wcstombs(saveargv0);
2194     if (mbssaveargv0 == NULL) {
2195 	xerror(EILSEQ, Ngt("unexpected error"));
2196 	err = Exit_NOEXEC;
2197 	goto error1;
2198     }
2199 
2200     char *mbsargv0;
2201     if (as != NULL) {
2202 	mbsargv0 = malloc_wcstombs(as);
2203 	if (mbsargv0 == NULL) {
2204 	    xerror(EILSEQ, Ngt("unexpected error"));
2205 	    err = Exit_NOEXEC;
2206 	    goto error2;
2207 	}
2208 	argv[0] = (void *) as;
2209     } else {
2210 	mbsargv0 = mbssaveargv0;
2211     }
2212 
2213     const char *commandpath;
2214     if (wcschr(saveargv0, L'/') != NULL) {
2215 	commandpath = mbssaveargv0;
2216     } else {
2217 	commandpath = get_command_path(mbssaveargv0, false);
2218 	if (commandpath == NULL) {
2219 	    xerror(0, Ngt("no such command `%s'"), mbssaveargv0);
2220 	    err = Exit_NOTFOUND;
2221 	    goto error3;
2222 	}
2223     }
2224 
2225     char **envs;
2226     if (clear) {
2227 	/* use the environment that contains only the variables assigned by the
2228 	 * assignment for this `exec' built-in. */
2229 	plist_T list;
2230 
2231 	pl_init(&list);
2232 	for (const assign_T *assign = last_assign;
2233 		assign != NULL;
2234 		assign = assign->next) {
2235 	    char *value = get_exported_value(assign->a_name);
2236 	    if (value != NULL) {
2237 		pl_add(&list, malloc_printf("%ls=%s", assign->a_name, value));
2238 		free(value);
2239 	    }
2240 	}
2241 	envs = (char **) pl_toary(&list);
2242     } else {
2243 	envs = environ;
2244     }
2245 
2246     exec_external_program(commandpath, argc, mbsargv0, argv, envs);
2247     err = laststatus;
2248 
2249     if (clear)
2250 	plfree((void **) envs, free);
2251 error3:
2252     if (as != NULL) {
2253 	free(mbsargv0);
2254 	argv[0] = (void *) saveargv0;
2255     }
2256 error2:
2257     free(mbssaveargv0);
2258 error1:
2259     if (posixly_correct || !is_interactive_now)
2260 	exit(err);
2261     return err;
2262 }
2263 
2264 #if YASH_ENABLE_HELP
2265 const char exec_help[] = Ngt(
2266 "replace the shell process with an external command"
2267 );
2268 const char exec_syntax[] = Ngt(
2269 "\texec [-cf] [-a name] [command [argument...]]\n"
2270 );
2271 #endif
2272 
2273 /* Options for the "command" built-in. */
2274 const struct xgetopt_T command_options[] = {
2275     { L'a', L"alias",            OPTARG_NONE, false, NULL, },
2276     { L'b', L"builtin-command",  OPTARG_NONE, false, NULL, },
2277     { L'e', L"external-command", OPTARG_NONE, false, NULL, },
2278     { L'f', L"function",         OPTARG_NONE, false, NULL, },
2279     { L'k', L"keyword",          OPTARG_NONE, false, NULL, },
2280     { L'p', L"standard-path",    OPTARG_NONE, true,  NULL, },
2281     { L'v', L"identify",         OPTARG_NONE, true,  NULL, },
2282     { L'V', L"verbose-identify", OPTARG_NONE, true,  NULL, },
2283 #if YASH_ENABLE_HELP
2284     { L'-', L"help",             OPTARG_NONE, false, NULL, },
2285 #endif
2286     { L'\0', NULL, 0, false, NULL, },
2287 };
2288 
2289 /* The "command"/"type" built-in, which accepts the following options:
2290  *  -a: search aliases
2291  *  -b: search built-ins
2292  *  -e: search external commands
2293  *  -f: search functions
2294  *  -k: search keywords
2295  *  -p: use the default path to find the command
2296  *  -v: print info about the command
2297  *  -V: print info about the command in a human-friendly format */
command_builtin(int argc,void ** argv)2298 int command_builtin(int argc, void **argv)
2299 {
2300     bool argv0istype = wcscmp(ARGV(0), L"type") == 0;
2301     bool printinfo = argv0istype, humanfriendly = argv0istype;
2302     enum srchcmdtype_T type = 0;
2303     bool aliases = false, keywords = false, defpath = false;
2304 
2305     const struct xgetopt_T *opt;
2306     xoptind = 0;
2307     while ((opt = xgetopt(argv, command_options, XGETOPT_POSIX)) != NULL) {
2308 	switch (opt->shortopt) {
2309 	    case L'a':  aliases = true;        break;
2310 	    case L'b':  type |= SCT_BUILTIN;   break;
2311 	    case L'e':  type |= SCT_EXTERNAL;  break;
2312 	    case L'f':  type |= SCT_FUNCTION;  break;
2313 	    case L'k':  keywords = true;       break;
2314 	    case L'p':  defpath = true;        break;
2315 	    case L'v':  printinfo = true;  humanfriendly = false;  break;
2316 	    case L'V':  printinfo = true;  humanfriendly = true;   break;
2317 #if YASH_ENABLE_HELP
2318 	    case L'-':
2319 		return print_builtin_help(ARGV(0));
2320 #endif
2321 	    default:
2322 		return Exit_ERROR;
2323 	}
2324     }
2325 
2326     if (!printinfo) {
2327 	if (aliases || keywords) {
2328 	    xerror(0,
2329 		Ngt("the -a or -k option must be used with the -v option"));
2330 	    return Exit_ERROR;
2331 	}
2332 
2333 	if (xoptind == argc) {
2334 	    if (posixly_correct)
2335 		return insufficient_operands_error(1);
2336 	    else
2337 		return Exit_SUCCESS;
2338 	}
2339 
2340 	if (type == 0)
2341 	    type = SCT_EXTERNAL | SCT_BUILTIN;
2342 	else
2343 	    type |= SCT_ALL;
2344 	if (defpath)
2345 	    type |= SCT_STDPATH;
2346 	return command_builtin_execute(
2347 		argc - xoptind, &argv[xoptind], type);
2348     } else {
2349 	if (posixly_correct && !validate_operand_count(argc - xoptind, 1,
2350 		    argv0istype ? SIZE_MAX : 1))
2351 	    return Exit_ERROR;
2352 
2353 	if (type == 0 && !aliases && !keywords) {
2354 	    type = SCT_EXTERNAL | SCT_BUILTIN | SCT_FUNCTION;
2355 	    aliases = keywords = true;
2356 	} else {
2357 	    type |= SCT_ALL;
2358 	}
2359 	if (defpath)
2360 	    type |= SCT_STDPATH;
2361 	type |= SCT_CHECK;
2362 
2363 	bool ok = true;
2364 	clearerr(stdout);
2365 	for (int i = xoptind; i < argc; i++) {
2366 	    ok &= print_command_info(
2367 		    ARGV(i), type, aliases, keywords, humanfriendly);
2368 	    if (ferror(stdout))
2369 		break;
2370 	}
2371 	return ok && yash_error_message_count == 0
2372 	    ? Exit_SUCCESS : Exit_FAILURE;
2373     }
2374 }
2375 
2376 /* Executes the specified simple command.
2377  * `argc' must be positive.
2378  * Returns the exit status of the command. */
command_builtin_execute(int argc,void ** argv,enum srchcmdtype_T type)2379 int command_builtin_execute(int argc, void **argv, enum srchcmdtype_T type)
2380 {
2381     char *argv0 = malloc_wcstombs(argv[0]);
2382     commandinfo_T ci;
2383 
2384     if (argv0 == NULL) {
2385 	xerror(EILSEQ, NULL);
2386 	return Exit_NOTFOUND;
2387     }
2388 
2389     search_command(argv0, argv[0], &ci, type);
2390 
2391     wchar_t **namep = invoke_simple_command(&ci, argc, argv0, argv, false);
2392     if (namep != NULL)
2393 	*namep = joinwcsarray(argv, L" ");
2394 
2395     free(argv0);
2396     return laststatus;
2397 }
2398 
2399 /* Prints info about the specified command.
2400  * If the command is not found, returns false. */
print_command_info(const wchar_t * commandname,enum srchcmdtype_T type,bool aliases,bool keywords,bool humanfriendly)2401 bool print_command_info(
2402 	const wchar_t *commandname, enum srchcmdtype_T type,
2403 	bool aliases, bool keywords, bool humanfriendly)
2404 {
2405     const char *msgfmt;
2406     char *name = NULL;
2407 
2408     if (keywords && is_keyword(commandname)) {
2409 	msgfmt = humanfriendly ? gt("%ls: a shell keyword\n") : "%ls\n";
2410 	xprintf(msgfmt, commandname);
2411 	return true;
2412     }
2413 
2414     if (aliases) {
2415 	if (print_alias_if_defined(commandname, humanfriendly)) {
2416 	    return true;
2417 	} else {
2418 	    if (ferror(stdout))
2419 		return false;
2420 	}
2421     }
2422 
2423     name = malloc_wcstombs(commandname);
2424     if (name == NULL)
2425 	return false;
2426 
2427     commandinfo_T ci;
2428     search_command(name, commandname, &ci, type);
2429     switch (ci.type) {
2430 	case CT_NONE:
2431 	    if (humanfriendly)
2432 		xerror(0, Ngt("no such command `%s'"), name);
2433 	    break;
2434 	case CT_EXTERNALPROGRAM:
2435 	    print_command_absolute_path(name, ci.ci_path, humanfriendly);
2436 	    break;
2437 	case CT_SPECIALBUILTIN:
2438 	    msgfmt = humanfriendly ? gt("%s: a special built-in\n") : "%s\n";
2439 	    xprintf(msgfmt, name);
2440 	    break;
2441 	case CT_SEMISPECIALBUILTIN:
2442 	    msgfmt = humanfriendly ? gt("%s: a semi-special built-in\n")
2443 		                   : "%s\n";
2444 	    xprintf(msgfmt, name);
2445 	    break;
2446 	case CT_REGULARBUILTIN:;
2447 	    const char *cmdpath;
2448 	    if (type & SCT_STDPATH)
2449 		cmdpath = get_command_path_default(name);
2450 	    else
2451 		cmdpath = get_command_path(name, false);
2452 	    if (humanfriendly) {
2453 		msgfmt = (cmdpath == NULL)
2454 		    ? Ngt("%s: a regular built-in (not found in $PATH)\n")
2455 		    : Ngt("%s: a regular built-in at %s\n");
2456 		xprintf(gt(msgfmt), name, cmdpath);
2457 	    } else {
2458 		xprintf("%s\n", (cmdpath == NULL) ? name : cmdpath);
2459 	    }
2460 	    break;
2461 	case CT_FUNCTION:
2462 	    msgfmt = humanfriendly ? gt("%s: a function\n") : "%s\n";
2463 	    xprintf(msgfmt, name);
2464 	    break;
2465     }
2466 
2467     free(name);
2468     return ci.type != CT_NONE;
2469 }
2470 
2471 /* Prints the absolute path of the specified command. */
print_command_absolute_path(const char * name,const char * path,bool humanfriendly)2472 void print_command_absolute_path(
2473 	const char *name, const char *path, bool humanfriendly)
2474 {
2475     if (path[0] == '/') {
2476 	/* the path is already absolute */
2477 	print_command_path(name, path, humanfriendly);
2478 	return;
2479     }
2480 
2481     xstrbuf_T abspath;
2482     sb_init(&abspath);
2483 
2484     const wchar_t *wpwd = getvar(L VAR_PWD);
2485     if (wpwd != NULL) {
2486 	int r = sb_printf(&abspath, "%ls", wpwd);
2487 	if (r < 0 || !is_same_file(abspath.contents, ".")) {
2488 	    sb_truncate(&abspath, 0);
2489 	}
2490     }
2491     if (abspath.length == 0) {
2492 	char *pwd = xgetcwd();
2493 	if (pwd != NULL)
2494 	    sb_catfree(&abspath, pwd);
2495 	else
2496 	    sb_ccat(&abspath, '.');  /* last resort */
2497     }
2498 
2499     if (abspath.length == 0 || abspath.contents[abspath.length - 1] != '/')
2500 	sb_ccat(&abspath, '/');
2501 
2502     sb_cat(&abspath, path);
2503 
2504     print_command_path(name, abspath.contents, humanfriendly);
2505     sb_destroy(&abspath);
2506 }
2507 
print_command_path(const char * name,const char * path,bool humanfriendly)2508 void print_command_path(
2509 	const char *name, const char *path, bool humanfriendly)
2510 {
2511     if (humanfriendly)
2512 	xprintf(gt("%s: an external command at %s\n"), name, path);
2513     else
2514 	xprintf("%s\n", path);
2515 }
2516 
2517 #if YASH_ENABLE_HELP
2518 
2519 const char command_help[] = Ngt(
2520 "execute or identify a command"
2521 );
2522 const char command_syntax[] = Ngt(
2523 "\tcommand [-befp] command [argument...]\n"
2524 "\tcommand -v|-V [-abefkp] command...\n"
2525 );
2526 
2527 const char type_help[] = Ngt(
2528 "identify a command"
2529 );
2530 const char type_syntax[] = Ngt(
2531 "\ttype command...\n"
2532 );
2533 
2534 #endif
2535 
2536 /* The "times" built-in. */
times_builtin(int argc,void ** argv)2537 int times_builtin(int argc __attribute__((unused)), void **argv)
2538 {
2539     const struct xgetopt_T *opt;
2540     xoptind = 0;
2541     while ((opt = xgetopt(argv, help_option, 0)) != NULL) {
2542 	switch (opt->shortopt) {
2543 #if YASH_ENABLE_HELP
2544 	    case L'-':
2545 		return print_builtin_help(ARGV(0));
2546 #endif
2547 	    default:
2548 		return special_builtin_error(Exit_ERROR);
2549 	}
2550     }
2551 
2552     if (xoptind < argc)
2553 	return special_builtin_error(too_many_operands_error(0));
2554 
2555     double clock;
2556     struct tms tms;
2557     intmax_t sum, ssm, cum, csm;
2558     double sus, sss, cus, css;
2559 #define format_time(time, min, sec) \
2560     do {                                   \
2561 	double tsec = (time) / clock;      \
2562 	double m = trunc(tsec / 60.0);     \
2563 	(min) = (intmax_t) m;              \
2564 	(sec) = tsec - m * 60.0;           \
2565     } while (0)
2566 
2567     clock = sysconf(_SC_CLK_TCK);
2568     if (times(&tms) == (clock_t) -1) {
2569 	xerror(errno, Ngt("cannot get the time data"));
2570 	return special_builtin_error(Exit_FAILURE);
2571     }
2572     format_time(tms.tms_utime, sum, sus);
2573     format_time(tms.tms_stime, ssm, sss);
2574     format_time(tms.tms_cutime, cum, cus);
2575     format_time(tms.tms_cstime, csm, css);
2576 #undef format_time
2577 
2578     xprintf("%jdm%fs %jdm%fs\n%jdm%fs %jdm%fs\n",
2579 	    sum, sus, ssm, sss, cum, cus, csm, css);
2580     return (yash_error_message_count == 0) ?
2581 	    Exit_SUCCESS : special_builtin_error(Exit_FAILURE);
2582 }
2583 
2584 #if YASH_ENABLE_HELP
2585 const char times_help[] = Ngt(
2586 "print CPU time usage"
2587 );
2588 const char times_syntax[] = Ngt(
2589 "\ttimes\n"
2590 );
2591 #endif
2592 
2593 
2594 /* vim: set ts=8 sts=4 sw=4 noet tw=80: */
2595