VirtualBox

source: kBuild/trunk/src/kash/jobs.c@ 3483

Last change on this file since 3483 was 3483, checked in by bird, 4 years ago

kash: Don't use cmdlist to dump nfile lists in cmdtxt, need a dedicated function there.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 35.4 KB
Line 
1/* $NetBSD: jobs.c,v 1.63 2005/06/01 15:41:19 lukem Exp $ */
2
3/*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
21 *
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
32 * SUCH DAMAGE.
33 */
34
35#if 0
36#ifndef lint
37static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95";
38#else
39__RCSID("$NetBSD: jobs.c,v 1.63 2005/06/01 15:41:19 lukem Exp $");
40#endif /* not lint */
41#endif
42
43#include <fcntl.h>
44#include <errno.h>
45#include <stdlib.h>
46#include <sys/types.h>
47
48#include "shell.h"
49#if JOBS && !defined(_MSC_VER)
50# include <termios.h>
51#endif
52#include "redir.h"
53#include "show.h"
54#include "main.h"
55#include "parser.h"
56#include "nodes.h"
57#include "jobs.h"
58#include "options.h"
59#include "trap.h"
60#include "syntax.h"
61#include "input.h"
62#include "output.h"
63#include "memalloc.h"
64#include "error.h"
65#include "mystring.h"
66#include "init.h"
67#include "shinstance.h"
68
69//static struct job *jobtab; /* array of jobs */
70//static int njobs; /* size of array */
71//static int jobs_invalid; /* set in child */
72//MKINIT pid_t backgndpid = -1; /* pid of last background process */
73#if JOBS
74//int initialpgrp; /* pgrp of shell on invocation */
75//static int curjob = -1; /* current job */
76#endif
77//static int ttyfd = -1;
78
79STATIC void restartjob(shinstance *, struct job *);
80STATIC void freejob(shinstance *, struct job *);
81STATIC struct job *getjob(shinstance *, const char *, int);
82STATIC shpid dowait(shinstance *, int, struct job *);
83STATIC shpid waitproc(shinstance *, int, struct job *, int *);
84STATIC void cmdtxt(shinstance *, union node *);
85STATIC void cmdlist(shinstance *, union node *, int);
86STATIC void cmdredirlist(shinstance *, union node *, int);
87STATIC void cmdputs(shinstance *, const char *);
88STATIC shpid forkparent(shinstance *psh, struct job *jp, union node *n, int mode, shpid pid);
89STATIC void forkchild(shinstance *psh, shpid pgrp, union node *n, int mode);
90#ifdef KASH_USE_FORKSHELL2
91# ifndef SH_FORKED_MODE
92struct forkshell2args
93{
94 shinstance *psh;
95 int mode;
96 shpid pgrp; /**< The forkchild() pgrp argument (-1 if not in group). */
97 union node *n;
98 void *argp; /**< Points to child callback data following this structure. */
99 int (* child)(shinstance *, union node *, void *);
100 struct stackmark smark; /* do we need this? */
101};
102static int forkshell2_thread(shinstance *psh, void *argp);
103# endif
104#endif
105
106
107/*
108 * Turn job control on and off.
109 *
110 * Note: This code assumes that the third arg to ioctl is a character
111 * pointer, which is true on Berkeley systems but not System V. Since
112 * System V doesn't have job control yet, this isn't a problem now.
113 */
114
115//MKINIT int jobctl;
116
117void
118setjobctl(shinstance *psh, int on)
119{
120 if (on == psh->jobctl || psh->rootshell == 0)
121 return;
122 if (on) {
123 int err;
124 int i;
125 if (psh->ttyfd != -1)
126 shfile_close(&psh->fdtab, psh->ttyfd);
127 if ((psh->ttyfd = shfile_open(&psh->fdtab, "/dev/tty", O_RDWR, 0)) == -1) {
128 for (i = 0; i < 3; i++) {
129 if (shfile_isatty(&psh->fdtab, i)
130 && (psh->ttyfd = shfile_dup(&psh->fdtab, i)) != -1)
131 break;
132 }
133 if (i == 3)
134 goto out;
135 }
136 /* Move to a high fd */
137 for (i = 10; i > 2; i--) {
138 if ((err = shfile_fcntl(&psh->fdtab, psh->ttyfd, F_DUPFD, (1 << i) - 1)) != -1)
139 break;
140 }
141 if (err != -1) {
142 shfile_close(&psh->fdtab, psh->ttyfd);
143 psh->ttyfd = err;
144 }
145 err = shfile_cloexec(&psh->fdtab, psh->ttyfd, 1);
146 if (err == -1) {
147 shfile_close(&psh->fdtab, psh->ttyfd);
148 psh->ttyfd = -1;
149 goto out;
150 }
151 do { /* while we are in the background */
152 if ((psh->initialpgrp = sh_tcgetpgrp(psh, psh->ttyfd)) < 0) {
153out:
154 out2str(psh, "sh: can't access tty; job control turned off\n");
155 mflag(psh) = 0;
156 return;
157 }
158 if (psh->initialpgrp == -1)
159 psh->initialpgrp = sh_getpgrp(psh);
160 else if (psh->initialpgrp != sh_getpgrp(psh)) {
161 sh_killpg(psh, 0, SIGTTIN);
162 continue;
163 }
164 } while (0);
165
166 setsignal(psh, SIGTSTP);
167 setsignal(psh, SIGTTOU);
168 setsignal(psh, SIGTTIN);
169 if (sh_getpgid(psh, 0) != psh->rootpid && sh_setpgid(psh, 0, psh->rootpid) == -1)
170 error(psh, "Cannot set process group (%s) at %d",
171 sh_strerror(psh, errno), __LINE__);
172 if (sh_tcsetpgrp(psh, psh->ttyfd, psh->rootpid) == -1)
173 error(psh, "Cannot set tty process group (%s) at %d",
174 sh_strerror(psh, errno), __LINE__);
175 } else { /* turning job control off */
176 if (sh_getpgid(psh, 0) != psh->initialpgrp && sh_setpgid(psh, 0, psh->initialpgrp) == -1)
177 error(psh, "Cannot set process group (%s) at %d",
178 sh_strerror(psh, errno), __LINE__);
179 if (sh_tcsetpgrp(psh, psh->ttyfd, psh->initialpgrp) == -1)
180 error(psh, "Cannot set tty process group (%s) at %d",
181 sh_strerror(psh, errno), __LINE__);
182 shfile_close(&psh->fdtab, psh->ttyfd);
183 psh->ttyfd = -1;
184 setsignal(psh, SIGTSTP);
185 setsignal(psh, SIGTTOU);
186 setsignal(psh, SIGTTIN);
187 }
188 psh->jobctl = on;
189}
190
191
192#ifdef mkinit
193INCLUDE <stdlib.h>
194
195SHELLPROC {
196 psh->backgndpid = -1;
197#if JOBS
198 psh->jobctl = 0;
199#endif
200}
201
202#endif
203
204
205
206#if JOBS
207int
208fgcmd(shinstance *psh, int argc, char **argv)
209{
210 struct job *jp;
211 int i;
212 int status;
213
214 nextopt(psh, "");
215 jp = getjob(psh, *psh->argptr, 0);
216 if (jp->jobctl == 0)
217 error(psh, "job not created under job control");
218 out1fmt(psh, "%s", jp->ps[0].cmd);
219 for (i = 1; i < jp->nprocs; i++)
220 out1fmt(psh, " | %s", jp->ps[i].cmd );
221 out1c(psh, '\n');
222 output_flushall(psh);
223
224 for (i = 0; i < jp->nprocs; i++)
225 if (sh_tcsetpgrp(psh, psh->ttyfd, jp->ps[i].pid) != -1)
226 break;
227
228 if (i >= jp->nprocs) {
229 error(psh, "Cannot set tty process group (%s) at %d",
230 sh_strerror(psh, errno), __LINE__);
231 }
232 restartjob(psh, jp);
233 INTOFF;
234 status = waitforjob(psh, jp);
235 INTON;
236 return status;
237}
238
239static void
240set_curjob(shinstance *psh, struct job *jp, int mode)
241{
242 struct job *jp1, *jp2;
243 int i, ji;
244
245 ji = (int)(jp - psh->jobtab);
246
247 /* first remove from list */
248 if (ji == psh->curjob)
249 psh->curjob = jp->prev_job;
250 else {
251 for (i = 0; i < psh->njobs; i++) {
252 if (psh->jobtab[i].prev_job != ji)
253 continue;
254 psh->jobtab[i].prev_job = jp->prev_job;
255 break;
256 }
257 }
258
259 /* Then re-insert in correct position */
260 switch (mode) {
261 case 0: /* job being deleted */
262 jp->prev_job = -1;
263 break;
264 case 1: /* newly created job or backgrounded job,
265 put after all stopped jobs. */
266 if (psh->curjob != -1 && psh->jobtab[psh->curjob].state == JOBSTOPPED) {
267 for (jp1 = psh->jobtab + psh->curjob; ; jp1 = jp2) {
268 if (jp1->prev_job == -1)
269 break;
270 jp2 = psh->jobtab + jp1->prev_job;
271 if (jp2->state != JOBSTOPPED)
272 break;
273 }
274 jp->prev_job = jp1->prev_job;
275 jp1->prev_job = ji;
276 break;
277 }
278 /* FALLTHROUGH */
279 case 2: /* newly stopped job - becomes psh->curjob */
280 jp->prev_job = psh->curjob;
281 psh->curjob = ji;
282 break;
283 }
284}
285
286int
287bgcmd(shinstance *psh, int argc, char **argv)
288{
289 struct job *jp;
290 int i;
291
292 nextopt(psh, "");
293 do {
294 jp = getjob(psh, *psh->argptr, 0);
295 if (jp->jobctl == 0)
296 error(psh, "job not created under job control");
297 set_curjob(psh, jp, 1);
298 out1fmt(psh, "[%ld] %s", (long)(jp - psh->jobtab + 1), jp->ps[0].cmd);
299 for (i = 1; i < jp->nprocs; i++)
300 out1fmt(psh, " | %s", jp->ps[i].cmd );
301 out1c(psh, '\n');
302 output_flushall(psh);
303 restartjob(psh, jp);
304 } while (*psh->argptr && *++psh->argptr);
305 return 0;
306}
307
308
309STATIC void
310restartjob(shinstance *psh, struct job *jp)
311{
312 struct procstat *ps;
313 int i;
314
315 if (jp->state == JOBDONE)
316 return;
317 INTOFF;
318 for (i = 0; i < jp->nprocs; i++)
319 if (sh_killpg(psh, jp->ps[i].pid, SIGCONT) != -1)
320 break;
321 if (i >= jp->nprocs)
322 error(psh, "Cannot continue job (%s)", sh_strerror(psh, errno));
323 for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
324 if (WIFSTOPPED(ps->status)) {
325 ps->status = -1;
326 jp->state = JOBRUNNING;
327 }
328 }
329 INTON;
330}
331#endif
332
333static void
334showjob(shinstance *psh, struct output *out, struct job *jp, int mode)
335{
336 int procno;
337 int st;
338 struct procstat *ps;
339 size_t col;
340 char s[64];
341
342#if JOBS
343 if (mode & SHOW_PGID) {
344 /* just output process (group) id of pipeline */
345 outfmt(out, "%" SHPID_PRI "\n", jp->ps->pid);
346 return;
347 }
348#endif
349
350 procno = jp->nprocs;
351 if (!procno)
352 return;
353
354 if (mode & SHOW_PID)
355 mode |= SHOW_MULTILINE;
356
357 if ((procno > 1 && !(mode & SHOW_MULTILINE))
358 || (mode & SHOW_SIGNALLED)) {
359 /* See if we have more than one status to report */
360 ps = jp->ps;
361 st = ps->status;
362 do {
363 int st1 = ps->status;
364 if (st1 != st)
365 /* yes - need multi-line output */
366 mode |= SHOW_MULTILINE;
367 if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1))
368 continue;
369 if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f)
370 && st1 != SIGINT && st1 != SIGPIPE))
371 mode |= SHOW_ISSIG;
372
373 } while (ps++, --procno);
374 procno = jp->nprocs;
375 }
376
377 if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) {
378 if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) {
379 TRACE((psh, "showjob: freeing job %d\n", jp - psh->jobtab + 1));
380 freejob(psh, jp);
381 }
382 return;
383 }
384
385 for (ps = jp->ps; --procno >= 0; ps++) { /* for each process */
386 if (ps == jp->ps)
387 fmtstr(s, 16, "[%ld] %c ",
388 (long)(jp - psh->jobtab + 1),
389#if JOBS
390 jp == psh->jobtab + psh->curjob ? '+' :
391 psh->curjob != -1 && jp == psh->jobtab +
392 psh->jobtab[psh->curjob].prev_job ? '-' :
393#endif
394 ' ');
395 else
396 fmtstr(s, 16, " " );
397 col = strlen(s);
398 if (mode & SHOW_PID) {
399 fmtstr(s + col, 16, "%" SHPID_PRI " ", ps->pid);
400 col += strlen(s + col);
401 }
402 if (ps->status == -1) {
403 scopy("Running", s + col);
404 } else if (WIFEXITED(ps->status)) {
405 st = WEXITSTATUS(ps->status);
406 if (st)
407 fmtstr(s + col, 16, "Done(%d)", st);
408 else
409 fmtstr(s + col, 16, "Done");
410 } else {
411 const char *pszSigNm;
412#if JOBS
413 if (WIFSTOPPED(ps->status))
414 st = WSTOPSIG(ps->status);
415 else /* WIFSIGNALED(ps->status) */
416#endif
417 st = WTERMSIG(ps->status);
418 st &= 0x7f;
419 pszSigNm = st < NSIG ? strsignal(st) : NULL;
420 if (pszSigNm)
421 scopyn(pszSigNm, s + col, 32);
422 else
423 fmtstr(s + col, 16, "Signal %d", st);
424 if (WCOREDUMP(ps->status)) {
425 col += strlen(s + col);
426 scopyn(" (core dumped)", s + col, 64 - col);
427 }
428 }
429 col += strlen(s + col);
430 outstr(s, out);
431 do {
432 outc(' ', out);
433 col++;
434 } while (col < 30);
435 outstr(ps->cmd, out);
436 if (mode & SHOW_MULTILINE) {
437 if (procno > 0) {
438 outc(' ', out);
439 outc('|', out);
440 }
441 } else {
442 while (--procno >= 0)
443 outfmt(out, " | %s", (++ps)->cmd );
444 }
445 outc('\n', out);
446 }
447 flushout(out);
448 jp->changed = 0;
449 if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE))
450 freejob(psh, jp);
451}
452
453
454int
455jobscmd(shinstance *psh, int argc, char **argv)
456{
457 int mode, m;
458 int sv = psh->jobs_invalid;
459
460 psh->jobs_invalid = 0;
461 mode = 0;
462 while ((m = nextopt(psh, "lp")))
463 if (m == 'l')
464 mode = SHOW_PID;
465 else
466 mode = SHOW_PGID;
467 if (*psh->argptr)
468 do
469 showjob(psh, psh->out1, getjob(psh, *psh->argptr,0), mode);
470 while (*++psh->argptr);
471 else
472 showjobs(psh, psh->out1, mode);
473 psh->jobs_invalid = sv;
474 return 0;
475}
476
477
478/*
479 * Print a list of jobs. If "change" is nonzero, only print jobs whose
480 * statuses have changed since the last call to showjobs.
481 *
482 * If the shell is interrupted in the process of creating a job, the
483 * result may be a job structure containing zero processes. Such structures
484 * will be freed here.
485 */
486
487void
488showjobs(shinstance *psh, struct output *out, int mode)
489{
490 int jobno;
491 struct job *jp;
492 int silent = 0;
493 shpid gotpid;
494
495 TRACE((psh, "showjobs(%x) called\n", mode));
496
497 /* If not even one one job changed, there is nothing to do */
498 gotpid = dowait(psh, 0, NULL);
499 while (dowait(psh, 0, NULL) > 0)
500 continue;
501#ifdef JOBS
502 /*
503 * Check if we are not in our foreground group, and if not
504 * put us in it.
505 */
506 if (mflag(psh) && gotpid != -1 && sh_tcgetpgrp(psh, psh->ttyfd) != sh_getpid(psh)) {
507 if (sh_tcsetpgrp(psh, psh->ttyfd, sh_getpid(psh)) == -1)
508 error(psh, "Cannot set tty process group (%s) at %d",
509 sh_strerror(psh, errno), __LINE__);
510 TRACE((psh, "repaired tty process group\n"));
511 silent = 1;
512 }
513#endif
514 if (psh->jobs_invalid)
515 return;
516
517 for (jobno = 1, jp = psh->jobtab ; jobno <= psh->njobs ; jobno++, jp++) {
518 if (!jp->used)
519 continue;
520 if (jp->nprocs == 0) {
521 freejob(psh, jp);
522 continue;
523 }
524 if ((mode & SHOW_CHANGED) && !jp->changed)
525 continue;
526 if (silent && jp->changed) {
527 jp->changed = 0;
528 continue;
529 }
530 showjob(psh, out, jp, mode);
531 }
532}
533
534/*
535 * Mark a job structure as unused.
536 */
537
538STATIC void
539freejob(shinstance *psh, struct job *jp)
540{
541 INTOFF;
542 if (jp->ps != &jp->ps0) {
543 ckfree(psh, jp->ps);
544 jp->ps = &jp->ps0;
545 }
546 jp->nprocs = 0;
547 jp->used = 0;
548#if JOBS
549 set_curjob(psh, jp, 0);
550#endif
551 INTON;
552}
553
554
555
556int
557waitcmd(shinstance *psh, int argc, char **argv)
558{
559 struct job *job;
560 int status, retval;
561 struct job *jp;
562
563 nextopt(psh, "");
564
565 if (!*psh->argptr) {
566 /* wait for all jobs */
567 jp = psh->jobtab;
568 if (psh->jobs_invalid)
569 return 0;
570 for (;;) {
571 if (jp >= psh->jobtab + psh->njobs) {
572 /* no running procs */
573 return 0;
574 }
575 if (!jp->used || jp->state != JOBRUNNING) {
576 jp++;
577 continue;
578 }
579 if (dowait(psh, 1, (struct job *)NULL) == -1)
580 return 128 + SIGINT;
581 jp = psh->jobtab;
582 }
583 }
584
585 retval = 127; /* XXXGCC: -Wuninitialized */
586 for (; *psh->argptr; psh->argptr++) {
587 job = getjob(psh, *psh->argptr, 1);
588 if (!job) {
589 retval = 127;
590 continue;
591 }
592 /* loop until process terminated or stopped */
593 while (job->state == JOBRUNNING) {
594 if (dowait(psh, 1, (struct job *)NULL) == -1)
595 return 128 + SIGINT;
596 }
597 status = job->ps[job->nprocs].status;
598 if (WIFEXITED(status))
599 retval = WEXITSTATUS(status);
600#if JOBS
601 else if (WIFSTOPPED(status))
602 retval = WSTOPSIG(status) + 128;
603#endif
604 else {
605 /* XXX: limits number of signals */
606 retval = WTERMSIG(status) + 128;
607 }
608 if (!iflag(psh))
609 freejob(psh, job);
610 }
611 return retval;
612}
613
614
615
616int
617jobidcmd(shinstance *psh, int argc, char **argv)
618{
619 struct job *jp;
620 int i;
621
622 nextopt(psh, "");
623 jp = getjob(psh, *psh->argptr, 0);
624 for (i = 0 ; i < jp->nprocs ; ) {
625 out1fmt(psh, "%" SHPID_PRI, jp->ps[i].pid);
626 out1c(psh, ++i < jp->nprocs ? ' ' : '\n');
627 }
628 return 0;
629}
630
631shpid
632getjobpgrp(shinstance *psh, const char *name)
633{
634 struct job *jp;
635
636 jp = getjob(psh, name, 1);
637 if (jp == 0)
638 return 0;
639 return -jp->ps[0].pid;
640}
641
642/*
643 * Convert a job name to a job structure.
644 */
645
646STATIC struct job *
647getjob(shinstance *psh, const char *name, int noerror)
648{
649 int jobno = -1;
650 struct job *jp;
651 int pid;
652 int i;
653 const char *err_msg = "No such job: %s";
654
655 if (name == NULL) {
656#if JOBS
657 jobno = psh->curjob;
658#endif
659 err_msg = "No current job";
660 } else if (name[0] == '%') {
661 if (is_number(name + 1)) {
662 jobno = number(psh, name + 1) - 1;
663 } else if (!name[2]) {
664 switch (name[1]) {
665#if JOBS
666 case 0:
667 case '+':
668 case '%':
669 jobno = psh->curjob;
670 err_msg = "No current job";
671 break;
672 case '-':
673 jobno = psh->curjob;
674 if (jobno != -1)
675 jobno = psh->jobtab[jobno].prev_job;
676 err_msg = "No previous job";
677 break;
678#endif
679 default:
680 goto check_pattern;
681 }
682 } else {
683 struct job *found;
684 check_pattern:
685 found = NULL;
686 for (jp = psh->jobtab, i = psh->njobs ; --i >= 0 ; jp++) {
687 if (!jp->used || jp->nprocs <= 0)
688 continue;
689 if ((name[1] == '?'
690 && strstr(jp->ps[0].cmd, name + 2))
691 || prefix(name + 1, jp->ps[0].cmd)) {
692 if (found) {
693 err_msg = "%s: ambiguous";
694 found = 0;
695 break;
696 }
697 found = jp;
698 }
699 }
700 if (found)
701 return found;
702 }
703
704 } else if (is_number(name)) {
705 pid = number(psh, name);
706 for (jp = psh->jobtab, i = psh->njobs ; --i >= 0 ; jp++) {
707 if (jp->used && jp->nprocs > 0
708 && jp->ps[jp->nprocs - 1].pid == pid)
709 return jp;
710 }
711 }
712
713 if (!psh->jobs_invalid && jobno >= 0 && jobno < psh->njobs) {
714 jp = psh->jobtab + jobno;
715 if (jp->used)
716 return jp;
717 }
718 if (!noerror)
719 error(psh, err_msg, name);
720 return 0;
721}
722
723
724
725/*
726 * Return a new job structure,
727 */
728
729struct job *
730makejob(shinstance *psh, union node *node, int nprocs)
731{
732 int i;
733 struct job *jp;
734
735 if (psh->jobs_invalid) {
736 for (i = psh->njobs, jp = psh->jobtab ; --i >= 0 ; jp++) {
737 if (jp->used)
738 freejob(psh, jp);
739 }
740 psh->jobs_invalid = 0;
741 }
742
743 for (i = psh->njobs, jp = psh->jobtab ; ; jp++) {
744 if (--i < 0) {
745 INTOFF;
746 if (psh->njobs == 0) {
747 psh->jobtab = ckmalloc(psh, 4 * sizeof psh->jobtab[0]);
748 } else {
749 jp = ckmalloc(psh, (psh->njobs + 4) * sizeof psh->jobtab[0]);
750 memcpy(jp, psh->jobtab, psh->njobs * sizeof jp[0]);
751 /* Relocate `ps' pointers */
752 for (i = 0; i < psh->njobs; i++)
753 if (jp[i].ps == &psh->jobtab[i].ps0)
754 jp[i].ps = &jp[i].ps0;
755 ckfree(psh, psh->jobtab);
756 psh->jobtab = jp;
757 }
758 jp = psh->jobtab + psh->njobs;
759 for (i = 4 ; --i >= 0 ; psh->jobtab[psh->njobs++].used = 0) { /*empty*/ }
760 INTON;
761 break;
762 }
763 if (jp->used == 0)
764 break;
765 }
766 INTOFF;
767 jp->state = JOBRUNNING;
768 jp->used = 1;
769 jp->changed = 0;
770 jp->nprocs = 0;
771#if JOBS
772 jp->jobctl = psh->jobctl;
773 set_curjob(psh, jp, 1);
774#endif
775 if (nprocs > 1) {
776 jp->ps = ckmalloc(psh, nprocs * sizeof (struct procstat));
777 } else {
778 jp->ps = &jp->ps0;
779 }
780 INTON;
781 TRACE((psh, "makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
782 jp - psh->jobtab + 1));
783 return jp;
784}
785
786
787/*
788 * Fork off a subshell. If we are doing job control, give the subshell its
789 * own process group. Jp is a job structure that the job is to be added to.
790 * N is the command that will be evaluated by the child. Both jp and n may
791 * be NULL. The mode parameter can be one of the following:
792 * FORK_FG - Fork off a foreground process.
793 * FORK_BG - Fork off a background process.
794 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
795 * process group even if job control is on.
796 *
797 * When job control is turned off, background processes have their standard
798 * input redirected to /dev/null (except for the second and later processes
799 * in a pipeline).
800 */
801
802#ifndef KASH_USE_FORKSHELL2
803shpid
804forkshell(shinstance *psh, struct job *jp, union node *n, int mode)
805{
806 int pid;
807
808 TRACE((psh, "forkshell(%%%d, %p, %d) called\n", jp - psh->jobtab, n, mode));
809 switch ((pid = sh_fork(psh))) {
810 case -1:
811 TRACE((psh, "Fork failed, errno=%d\n", errno));
812 INTON;
813 error(psh, "Cannot fork");
814 return -1; /* won't get here */
815 case 0:
816 forkchild(psh, jp == NULL || jp->nprocs == 0 ? -1 : jp->ps[0].pid, n, mode);
817 return 0;
818 default:
819 return forkparent(psh, jp, n, mode, pid);
820 }
821}
822#else /* KASH_USE_FORKSHELL2 */
823shpid
824forkshell2(shinstance *psh, struct job *jp, union node *n, int mode,
825 int (*child)(struct shinstance *, void *, union node *),
826 union node *nchild, void *argp, size_t arglen,
827 void (*setupchild)(struct shinstance *, struct shinstance *, void *))
828{
829 shpid pid;
830
831# ifdef SH_FORKED_MODE
832 /*
833 * fork variant.
834 */
835 pid = sh_fork(psh);
836 if (pid == 0)
837 {
838 /* child */
839 forkchild(psh, jp == NULL || jp->nprocs == 0 ? -1 : jp->ps[0].pid, n, mode);
840 sh__exit(psh, child(psh, nchild, argp));
841 return 0;
842 }
843
844 /* parent */
845 if (pid != -1)
846 return forkparent(psh, jp, n, mode, pid);
847 TRACE((psh, "Fork failed, errno=%d\n", errno));
848 INTON;
849 (void)arglen;
850 (void)setupchild;
851 error(psh, "Cannot fork");
852 return -1; /* won't get here */
853
854# else
855 /*
856 * Clone the shell and start a thread to service the subshell.
857 */
858 struct shinstance *pshchild;
859
860 TRACE((psh, "forkshell2(%%%d, %p, %d, %p, %p, %p, %d) called\n",
861 jp - psh->jobtab, n, mode, child, nchild, argp, (int)arglen));
862
863 pshchild = sh_create_child_shell(psh);
864 if (pshchild) {
865 /* pack arguments */
866 struct forkshell2args *args = (struct forkshell2args *)sh_calloc(pshchild, sizeof(*args) + arglen, 1);
867 args->psh = pshchild;
868 args->argp = memcpy(args + 1, argp, arglen);
869 args->child = child;
870 args->mode = mode;
871 args->pgrp = jp == NULL || jp->nprocs == 0 ? -1 : jp->ps[0].pid;
872 setstackmark(pshchild, &args->smark);
873 args->n = copyparsetree(pshchild, n);
874 if (setupchild)
875 setupchild(pshchild, psh, args->argp);
876
877 /* start the thread */
878 pid = sh_thread_start(psh, pshchild, forkshell2_thread, args);
879 if (pid >= 0)
880 return forkparent(psh, jp, n, mode, pid);
881 error(psh, "sh_start_child_thread failed (%d)!", (int)pid);
882 }
883 else
884 error(psh, "sh_create_child_shell failed!");
885 return -1;
886# endif
887}
888#endif
889
890STATIC shpid
891forkparent(shinstance *psh, struct job *jp, union node *n, int mode, shpid pid)
892{
893 shpid pgrp;
894
895 if (psh->rootshell && mode != FORK_NOJOB && mflag(psh)) {
896 if (jp == NULL || jp->nprocs == 0)
897 pgrp = pid;
898 else
899 pgrp = jp->ps[0].pid;
900 /* This can fail because we are doing it in the child also */
901 (void)sh_setpgid(psh, pid, pgrp);
902 }
903 if (mode == FORK_BG)
904 psh->backgndpid = pid; /* set $! */
905 if (jp) {
906 struct procstat *ps = &jp->ps[jp->nprocs++];
907 ps->pid = pid;
908 ps->status = -1;
909 ps->cmd[0] = 0;
910 if (/* iflag && rootshell && */ n)
911 commandtext(psh, ps, n);
912 }
913 TRACE((psh, "In parent shell: child = %" SHPID_PRI "\n", pid));
914 return pid;
915}
916
917STATIC void
918forkchild(shinstance *psh, shpid pgrp, union node *n, int mode)
919{
920 int wasroot;
921 const char *devnull = _PATH_DEVNULL;
922 const char *nullerr = "Can't open %s";
923
924 wasroot = psh->rootshell;
925 TRACE((psh, "Child shell %" SHPID_PRI "\n", sh_getpid(psh)));
926 psh->rootshell = 0;
927
928 closescript(psh);
929 clear_traps(psh);
930#if JOBS
931 psh->jobctl = 0; /* do job control only in root shell */
932 if (wasroot && mode != FORK_NOJOB && mflag(psh)) {
933 if (pgrp == -1)
934 pgrp = sh_getpid(psh);
935 /* This can fail because we are doing it in the parent also.
936 And we must ignore SIGTTOU at this point or we'll be stopped! */
937 (void)sh_setpgid(psh, 0, pgrp);
938 if (mode == FORK_FG) {
939 if (sh_tcsetpgrp(psh, psh->ttyfd, pgrp) == -1)
940 error(psh, "Cannot set tty process group (%s) at %d",
941 sh_strerror(psh, errno), __LINE__);
942 }
943 setsignal(psh, SIGTSTP);
944 setsignal(psh, SIGTTOU);
945 } else
946#endif
947 if (mode == FORK_BG) {
948 ignoresig(psh, SIGINT);
949 ignoresig(psh, SIGQUIT);
950 if (pgrp == -1 && ! fd0_redirected_p(psh)) {
951 shfile_close(&psh->fdtab, 0);
952 if (shfile_open(&psh->fdtab, devnull, O_RDONLY, 0) != 0)
953 error(psh, nullerr, devnull);
954 }
955 }
956 if (wasroot && iflag(psh)) {
957 setsignal(psh, SIGINT);
958 setsignal(psh, SIGQUIT);
959 setsignal(psh, SIGTERM);
960 }
961
962 psh->jobs_invalid = 1;
963}
964
965#if defined(KASH_USE_FORKSHELL2) && !defined(SH_FORKED_MODE)
966/** thread procedure */
967static int forkshell2_thread(shinstance *psh, void *argp)
968{
969 struct forkshell2args * volatile volargs = (struct forkshell2args *)argp;
970 struct jmploc jmp;
971 TRACE2((psh, "forkshell2_thread:\n"));
972
973 if (setjmp(jmp.loc) == 0) {
974 struct forkshell2args * const args = volargs;
975
976 forkchild(psh, args->pgrp, args->n, args->mode);
977
978 psh->handler = &jmp;
979 return args->child(psh, args->n, args->argp);
980 }
981
982 /*
983 * (We longjmp'ed here.)
984 *
985 * This is copied from main() and simplified:
986 */
987 for (;;) {
988 psh = volargs->psh; /* longjmp paranoia */
989
990 if (psh->exception != EXSHELLPROC) {
991 if (psh->exception == EXEXEC)
992 psh->exitstatus = psh->exerrno;
993 else if (psh->exception == EXERROR)
994 psh->exitstatus = 2;
995 TRACE2((psh, "forkshell2_thread: exception=%d -> exitshell2(,%d)\n", psh->exception, psh->exitstatus));
996 return exitshell2(psh, psh->exitstatus);
997 }
998
999 /* EXSHELLPROC - tryexec gets us here and it wants to run a program
1000 hoping (?) it's a shell script. We must reset the shell state and
1001 turn ourselves into a root shell before doing so. */
1002 TRACE2((psh, "forkshell2_thread: exception=EXSHELLPROC\n"));
1003 psh->rootpid = /*getpid()*/ psh->pid;
1004 psh->rootshell = 1;
1005 psh->minusc = NULL;
1006
1007 reset(psh);
1008 popstackmark(psh, &volargs->smark);
1009
1010 FORCEINTON; /* enable interrupts */
1011
1012 /* state3: */
1013 if (sflag(psh) == 0) {
1014# ifdef SIGTSTP
1015 static int sigs[] = { SIGINT, SIGQUIT, SIGHUP, SIGPIPE, SIGTSTP };
1016# else
1017 static int sigs[] = { SIGINT, SIGQUIT, SIGHUP, SIGPIPE };
1018# endif
1019 unsigned i;
1020 for (i = 0; i < K_ELEMENTS(sigs); i++)
1021 setsignal(psh, sigs[i]);
1022 }
1023
1024 if (setjmp(jmp.loc) == 0) {
1025 psh->handler = &jmp;
1026 cmdloop(psh, 1);
1027 TRACE2((psh, "forkshell2_thread: cmdloop returned -> exitshell2(,%d)\n", psh->exitstatus));
1028 return exitshell2(psh, psh->exitstatus);
1029 }
1030 }
1031}
1032#endif
1033
1034
1035/*
1036 * Wait for job to finish.
1037 *
1038 * Under job control we have the problem that while a child process is
1039 * running interrupts generated by the user are sent to the child but not
1040 * to the shell. This means that an infinite loop started by an inter-
1041 * active user may be hard to kill. With job control turned off, an
1042 * interactive user may place an interactive program inside a loop. If
1043 * the interactive program catches interrupts, the user doesn't want
1044 * these interrupts to also abort the loop. The approach we take here
1045 * is to have the shell ignore interrupt signals while waiting for a
1046 * forground process to terminate, and then send itself an interrupt
1047 * signal if the child process was terminated by an interrupt signal.
1048 * Unfortunately, some programs want to do a bit of cleanup and then
1049 * exit on interrupt; unless these processes terminate themselves by
1050 * sending a signal to themselves (instead of calling exit) they will
1051 * confuse this approach.
1052 */
1053
1054int
1055waitforjob(shinstance *psh, struct job *jp)
1056{
1057#if JOBS
1058 shpid mypgrp = sh_getpgrp(psh);
1059#endif
1060 int status;
1061 int st;
1062
1063 INTOFF;
1064 TRACE((psh, "waitforjob(%%%d) called\n", jp - psh->jobtab + 1));
1065 while (jp->state == JOBRUNNING) {
1066 dowait(psh, 1, jp);
1067 }
1068#if JOBS
1069 if (jp->jobctl) {
1070 if (sh_tcsetpgrp(psh, psh->ttyfd, mypgrp) == -1)
1071 error(psh, "Cannot set tty process group (%s) at %d",
1072 sh_strerror(psh, errno), __LINE__);
1073 }
1074 if (jp->state == JOBSTOPPED && psh->curjob != jp - psh->jobtab)
1075 set_curjob(psh, jp, 2);
1076#endif
1077 status = jp->ps[jp->nprocs - 1].status;
1078 /* convert to 8 bits */
1079 if (WIFEXITED(status))
1080 st = WEXITSTATUS(status);
1081#if JOBS
1082 else if (WIFSTOPPED(status))
1083 st = WSTOPSIG(status) + 128;
1084#endif
1085 else
1086 st = WTERMSIG(status) + 128;
1087 TRACE((psh, "waitforjob: job %d, nproc %d, status %x, st %x\n",
1088 jp - psh->jobtab + 1, jp->nprocs, status, st ));
1089#if JOBS
1090 if (jp->jobctl) {
1091 /*
1092 * This is truly gross.
1093 * If we're doing job control, then we did a TIOCSPGRP which
1094 * caused us (the shell) to no longer be in the controlling
1095 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
1096 * intuit from the subprocess exit status whether a SIGINT
1097 * occurred, and if so interrupt ourselves. Yuck. - mycroft
1098 */
1099 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
1100 sh_raise_sigint(psh);/*raise(SIGINT);*/
1101 }
1102#endif
1103 if (! JOBS || jp->state == JOBDONE)
1104 freejob(psh, jp);
1105 INTON;
1106 return st;
1107}
1108
1109
1110
1111/*
1112 * Wait for a process to terminate.
1113 */
1114
1115STATIC shpid
1116dowait(shinstance *psh, int block, struct job *job)
1117{
1118 shpid pid;
1119 int status;
1120 struct procstat *sp;
1121 struct job *jp;
1122 struct job *thisjob;
1123 int done;
1124 int stopped;
1125
1126 TRACE((psh, "dowait(%d) called\n", block));
1127 do {
1128 pid = waitproc(psh, block, job, &status);
1129 TRACE((psh, "wait returns pid %" SHPID_PRI ", status %d\n", pid, status));
1130 } while (pid == -1 && errno == EINTR && psh->gotsig[SIGINT - 1] == 0);
1131 if (pid <= 0)
1132 return pid;
1133 INTOFF;
1134 thisjob = NULL;
1135 for (jp = psh->jobtab ; jp < psh->jobtab + psh->njobs ; jp++) {
1136 if (jp->used) {
1137 done = 1;
1138 stopped = 1;
1139 for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
1140 if (sp->pid == -1)
1141 continue;
1142 if (sp->pid == pid) {
1143 TRACE((psh, "Job %d: changing status of proc %" SHPID_PRI " from 0x%x to 0x%x\n",
1144 jp - psh->jobtab + 1, pid, sp->status, status));
1145 sp->status = status;
1146 thisjob = jp;
1147 }
1148 if (sp->status == -1)
1149 stopped = 0;
1150 else if (WIFSTOPPED(sp->status))
1151 done = 0;
1152 }
1153 if (stopped) { /* stopped or done */
1154 int state = done ? JOBDONE : JOBSTOPPED;
1155 if (jp->state != state) {
1156 TRACE((psh, "Job %d: changing state from %d to %d\n", jp - psh->jobtab + 1, jp->state, state));
1157 jp->state = state;
1158#if JOBS
1159 if (done)
1160 set_curjob(psh, jp, 0);
1161#endif
1162 }
1163 }
1164 }
1165 }
1166
1167 if (thisjob && thisjob->state != JOBRUNNING) {
1168 int mode = 0;
1169 if (!psh->rootshell || !iflag(psh))
1170 mode = SHOW_SIGNALLED;
1171 if (job == thisjob)
1172 mode = SHOW_SIGNALLED | SHOW_NO_FREE;
1173 if (mode)
1174 showjob(psh, psh->out2, thisjob, mode);
1175 else {
1176 TRACE((psh, "Not printing status, rootshell=%d, job=%p\n",
1177 psh->rootshell, job));
1178 thisjob->changed = 1;
1179 }
1180 }
1181
1182 INTON;
1183 return pid;
1184}
1185
1186
1187
1188/*
1189 * Do a wait system call. If job control is compiled in, we accept
1190 * stopped processes. If block is zero, we return a value of zero
1191 * rather than blocking.
1192 */
1193STATIC shpid
1194waitproc(shinstance *psh, int block, struct job *jp, int *status)
1195{
1196 int flags = 0;
1197
1198#if JOBS
1199 if (jp != NULL && jp->jobctl)
1200 flags |= WUNTRACED;
1201#endif
1202 if (block == 0)
1203 flags |= WNOHANG;
1204 return sh_waitpid(psh, -1, status, flags);
1205}
1206
1207/*
1208 * return 1 if there are stopped jobs, otherwise 0
1209 */
1210//int job_warning = 0;
1211int
1212stoppedjobs(shinstance *psh)
1213{
1214 int jobno;
1215 struct job *jp;
1216
1217 if (psh->job_warning || psh->jobs_invalid)
1218 return (0);
1219 for (jobno = 1, jp = psh->jobtab; jobno <= psh->njobs; jobno++, jp++) {
1220 if (jp->used == 0)
1221 continue;
1222 if (jp->state == JOBSTOPPED) {
1223 out2str(psh, "You have stopped jobs.\n");
1224 psh->job_warning = 2;
1225 return (1);
1226 }
1227 }
1228
1229 return (0);
1230}
1231
1232/*
1233 * Return a string identifying a command (to be printed by the
1234 * jobs command).
1235 */
1236
1237//STATIC char *cmdnextc;
1238//STATIC int cmdnleft;
1239
1240void
1241commandtext(shinstance *psh, struct procstat *ps, union node *n)
1242{
1243 int len;
1244
1245 psh->cmdnextc = ps->cmd;
1246 if (iflag(psh) || mflag(psh) || sizeof(ps->cmd) < 100)
1247 len = sizeof(ps->cmd);
1248 else
1249 len = sizeof(ps->cmd) / 10;
1250 psh->cmdnleft = len;
1251 cmdtxt(psh, n);
1252 if (psh->cmdnleft <= 0) {
1253 char *p = ps->cmd + len - 4;
1254 p[0] = '.';
1255 p[1] = '.';
1256 p[2] = '.';
1257 p[3] = 0;
1258 } else
1259 *psh->cmdnextc = '\0';
1260 TRACE((psh, "commandtext: ps->cmd %x, end %x, left %d\n\t\"%s\"\n",
1261 ps->cmd, psh->cmdnextc, psh->cmdnleft, ps->cmd));
1262}
1263
1264
1265STATIC void
1266cmdtxt(shinstance *psh, union node *n)
1267{
1268 union node *np;
1269 struct nodelist *lp;
1270 const char *p;
1271 int i;
1272 char s[2];
1273
1274 if (n == NULL || psh->cmdnleft <= 0)
1275 return;
1276 switch (n->type) {
1277 case NSEMI:
1278 cmdtxt(psh, n->nbinary.ch1);
1279 cmdputs(psh, "; ");
1280 cmdtxt(psh, n->nbinary.ch2);
1281 break;
1282 case NAND:
1283 cmdtxt(psh, n->nbinary.ch1);
1284 cmdputs(psh, " && ");
1285 cmdtxt(psh, n->nbinary.ch2);
1286 break;
1287 case NOR:
1288 cmdtxt(psh, n->nbinary.ch1);
1289 cmdputs(psh, " || ");
1290 cmdtxt(psh, n->nbinary.ch2);
1291 break;
1292 case NPIPE:
1293 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
1294 cmdtxt(psh, lp->n);
1295 if (lp->next)
1296 cmdputs(psh, " | ");
1297 }
1298 break;
1299 case NSUBSHELL:
1300 cmdputs(psh, "(");
1301 cmdtxt(psh, n->nredir.n);
1302 cmdputs(psh, ")");
1303 break;
1304 case NREDIR:
1305 case NBACKGND:
1306 cmdtxt(psh, n->nredir.n);
1307 break;
1308 case NIF:
1309 cmdputs(psh, "if ");
1310 cmdtxt(psh, n->nif.test);
1311 cmdputs(psh, "; then ");
1312 cmdtxt(psh, n->nif.ifpart);
1313 if (n->nif.elsepart) {
1314 cmdputs(psh, "; else ");
1315 cmdtxt(psh, n->nif.elsepart);
1316 }
1317 cmdputs(psh, "; fi");
1318 break;
1319 case NWHILE:
1320 cmdputs(psh, "while ");
1321 goto until;
1322 case NUNTIL:
1323 cmdputs(psh, "until ");
1324until:
1325 cmdtxt(psh, n->nbinary.ch1);
1326 cmdputs(psh, "; do ");
1327 cmdtxt(psh, n->nbinary.ch2);
1328 cmdputs(psh, "; done");
1329 break;
1330 case NFOR:
1331 cmdputs(psh, "for ");
1332 cmdputs(psh, n->nfor.var);
1333 cmdputs(psh, " in ");
1334 cmdlist(psh, n->nfor.args, 1);
1335 cmdputs(psh, "; do ");
1336 cmdtxt(psh, n->nfor.body);
1337 cmdputs(psh, "; done");
1338 break;
1339 case NCASE:
1340 cmdputs(psh, "case ");
1341 cmdputs(psh, n->ncase.expr->narg.text);
1342 cmdputs(psh, " in ");
1343 for (np = n->ncase.cases; np; np = np->nclist.next) {
1344 cmdtxt(psh, np->nclist.pattern);
1345 cmdputs(psh, ") ");
1346 cmdtxt(psh, np->nclist.body);
1347 cmdputs(psh, ";; ");
1348 }
1349 cmdputs(psh, "esac");
1350 break;
1351 case NDEFUN:
1352 cmdputs(psh, n->narg.text);
1353 cmdputs(psh, "() { ... }");
1354 break;
1355 case NCMD:
1356 cmdlist(psh, n->ncmd.args, 1);
1357 cmdredirlist(psh, n->ncmd.redirect, 0);
1358 break;
1359 case NARG:
1360 cmdputs(psh, n->narg.text);
1361 break;
1362 case NTO:
1363 p = ">"; i = 1; goto redir;
1364 case NCLOBBER:
1365 p = ">|"; i = 1; goto redir;
1366 case NAPPEND:
1367 p = ">>"; i = 1; goto redir;
1368 case NTOFD:
1369 p = ">&"; i = 1; goto redir;
1370 case NFROM:
1371 p = "<"; i = 0; goto redir;
1372 case NFROMFD:
1373 p = "<&"; i = 0; goto redir;
1374 case NFROMTO:
1375 p = "<>"; i = 0; goto redir;
1376redir:
1377 if (n->nfile.fd != i) {
1378 s[0] = n->nfile.fd + '0';
1379 s[1] = '\0';
1380 cmdputs(psh, s);
1381 }
1382 cmdputs(psh, p);
1383 if (n->type == NTOFD || n->type == NFROMFD) {
1384 s[0] = n->ndup.dupfd + '0';
1385 s[1] = '\0';
1386 cmdputs(psh, s);
1387 } else {
1388 cmdtxt(psh, n->nfile.fname);
1389 }
1390 break;
1391 case NHERE:
1392 case NXHERE:
1393 cmdputs(psh, "<<...");
1394 break;
1395 default:
1396 cmdputs(psh, "???");
1397 break;
1398 }
1399}
1400
1401STATIC void
1402cmdlist(shinstance *psh, union node *np, int sep)
1403{
1404 for (; np; np = np->narg.next) {
1405 if (!sep)
1406 cmdputs(psh, " ");
1407 cmdtxt(psh, np);
1408 if (sep && np->narg.next)
1409 cmdputs(psh, " ");
1410 }
1411}
1412
1413STATIC void
1414cmdredirlist(shinstance *psh, union node *np, int sep)
1415{
1416 for (; np; np = np->nfile.next) {
1417 if (!sep)
1418 cmdputs(psh, " ");
1419 cmdtxt(psh, np);
1420 if (sep && np->nfile.next)
1421 cmdputs(psh, " ");
1422 }
1423}
1424
1425
1426STATIC void
1427cmdputs(shinstance *psh, const char *s)
1428{
1429 const char *p, *str = 0;
1430 char c, cc[2] = " ";
1431 char *nextc;
1432 int nleft;
1433 int subtype = 0;
1434 int quoted = 0;
1435 static char vstype[16][4] = { "", "}", "-", "+", "?", "=",
1436 "#", "##", "%", "%%" };
1437
1438 p = s;
1439 nextc = psh->cmdnextc;
1440 nleft = psh->cmdnleft;
1441 while (nleft > 0 && (c = *p++) != 0) {
1442 switch (c) {
1443 case CTLESC:
1444 c = *p++;
1445 break;
1446 case CTLVAR:
1447 subtype = *p++;
1448 if ((subtype & VSTYPE) == VSLENGTH)
1449 str = "${#";
1450 else
1451 str = "${";
1452 if (!(subtype & VSQUOTE) != !(quoted & 1)) {
1453 quoted ^= 1;
1454 c = '"';
1455 } else
1456 c = *str++;
1457 break;
1458 case CTLENDVAR:
1459 if (quoted & 1) {
1460 c = '"';
1461 str = "}";
1462 } else
1463 c = '}';
1464 quoted >>= 1;
1465 subtype = 0;
1466 break;
1467 case CTLBACKQ:
1468 c = '$';
1469 str = "(...)";
1470 break;
1471 case CTLBACKQ+CTLQUOTE:
1472 c = '"';
1473 str = "$(...)\"";
1474 break;
1475 case CTLARI:
1476 c = '$';
1477 str = "((";
1478 break;
1479 case CTLENDARI:
1480 c = ')';
1481 str = ")";
1482 break;
1483 case CTLQUOTEMARK:
1484 quoted ^= 1;
1485 c = '"';
1486 break;
1487 case '=':
1488 if (subtype == 0)
1489 break;
1490 str = vstype[subtype & VSTYPE];
1491 if (subtype & VSNUL)
1492 c = ':';
1493 else
1494 c = *str++;
1495 if (c != '}')
1496 quoted <<= 1;
1497 break;
1498 case '\'':
1499 case '\\':
1500 case '"':
1501 case '$':
1502 /* These can only happen inside quotes */
1503 cc[0] = c;
1504 str = cc;
1505 c = '\\';
1506 break;
1507 default:
1508 break;
1509 }
1510 do {
1511 *nextc++ = c;
1512 } while (--nleft > 0 && str && (c = *str++));
1513 str = 0;
1514 }
1515 if ((quoted & 1) && nleft) {
1516 *nextc++ = '"';
1517 nleft--;
1518 }
1519 psh->cmdnleft = nleft;
1520 psh->cmdnextc = nextc;
1521}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette