VirtualBox

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

Last change on this file since 3451 was 3438, checked in by bird, 5 years ago

kash: Hammering on threaded mode.

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

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