/* * ksh special - the select command processing section * print the args in column form - assuming that we can */ static const char * do_selectargs(const char **ap, bool print_menu) { static const char *read_args[] = { "read", "-r", "REPLY", NULL }; char *s; int i, argct; for (argct = 0; ap[argct]; argct++) ; while (/* CONSTCOND */ 1) { /*- * Menu is printed if * - this is the first time around the select loop * - the user enters a blank line * - the REPLY parameter is empty */ if (print_menu || !*str_val(global("REPLY"))) pr_menu(ap); shellf("%s", str_val(global("PS3"))); if (call_builtin(findcom("read", FC_BI), read_args, Tselect)) return (NULL); s = str_val(global("REPLY")); if (*s) { getn(s, &i); return ((i >= 1 && i <= argct) ? ap[i - 1] : null); } print_menu = true; } }
/* are there any running or stopped jobs ? */ int j_stopped_running(void) { Job *j; int which = 0; for (j = job_list; j != (Job *) 0; j = j->next) { #ifdef JOBS if (j->ppid == procpid && j->state == PSTOPPED) which |= 1; #endif /* JOBS */ if (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid && j->ppid == procpid && j->state == PRUNNING) which |= 2; } if (which) { shellf("You have %s%s%s jobs\n", which & 1 ? "stopped" : "", which == 3 ? " and " : "", which & 2 ? "running" : ""); return 1; } return 0; }
void tprintinfo(struct table *tp) { struct tbl *te; char *n; unsigned int h; int ncmp; int totncmp = 0, maxncmp = 0; int nentries = 0; struct tstate ts; shellf("table size %d, nfree %d\n", tp->size, tp->nfree); shellf(" Ncmp name\n"); ktwalk(&ts, tp); while ((te = ktnext(&ts))) { struct tbl **pp, *p; h = hash(n = te->name); ncmp = 0; /* taken from ktsearch() and added counter */ for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp); pp--) { ncmp++; if (*p->name == *n && strcmp(p->name, n) == 0 && (p->flag&DEFINED)) break; /* return p; */ if (pp == tp->tbls) /* wrap */ pp += tp->size; } shellf(" %4d %s\n", ncmp, n); totncmp += ncmp; nentries++; if (ncmp > maxncmp) maxncmp = ncmp; } if (nentries) shellf(" %d entries, worst ncmp %d, avg ncmp %d.%02d\n", nentries, maxncmp, totncmp / nentries, (totncmp % nentries) * 100 / nentries); }
/* are there any running or stopped jobs ? */ int j_stopped_running(void) { Job *j; int which = 0; for (j = job_list; j != (Job *) 0; j = j->next) { } if (which) { shellf("You have %s%s%s jobs\n", which & 1 ? "stopped" : "", which == 3 ? " and " : "", which & 2 ? "running" : ""); return 1; } return 0; }
static void mprintit(mbox_t *mbp) { struct tbl *vp; #if 0 /* * I doubt this $_ overloading is bad in /bin/sh mode. Anyhow, we * crash as the code looks now if we do not set vp. Now, this is * easy to fix too, but I'd like to see what POSIX says before doing * a change like that. */ if (!Flag(FSH)) #endif /* Ignore setstr errors here (arbitrary) */ setstr((vp = local("_", false)), mbp->mb_path, KSH_RETURN_ERROR); shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0)); unset(vp, 0); }
/* * set up redirection, saving old fds in e->savefd */ static int iosetup(struct ioword *iop, struct tbl *tp) { int u = -1; char *cp = iop->name; int iotype = iop->flag & IOTYPE; bool do_open = true, do_close = false; int flags = 0; struct ioword iotmp; struct stat statb; if (iotype != IOHERE) cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0)); /* Used for tracing and error messages to print expanded cp */ iotmp = *iop; iotmp.name = (iotype == IOHERE) ? NULL : cp; iotmp.flag |= IONAMEXP; if (Flag(FXTRACE)) shellf("%s%s\n", substitute(str_val(global("PS4")), 0), snptreef(NULL, 32, "%R", &iotmp)); switch (iotype) { case IOREAD: flags = O_RDONLY; break; case IOCAT: flags = O_WRONLY | O_APPEND | O_CREAT; break; case IOWRITE: flags = O_WRONLY | O_CREAT | O_TRUNC; /* * The stat() is here to allow redirections to * things like /dev/null without error. */ if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) && (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode))) flags |= O_EXCL; break; case IORDWR: flags = O_RDWR | O_CREAT; break; case IOHERE: do_open = false; /* herein() returns -2 if error has been printed */ u = herein(iop, NULL); /* cp may have wrong name */ break; case IODUP: { const char *emsg; do_open = false; if (*cp == '-' && !cp[1]) { /* prevent error return below */ u = 1009; do_close = true; } else if ((u = check_fd(cp, X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK), &emsg)) < 0) { warningf(true, "%s: %s", snptreef(NULL, 32, "%R", &iotmp), emsg); return (-1); } if (u == iop->unit) /* "dup from" == "dup to" */ return (0); break; } } if (do_open) { if (Flag(FRESTRICTED) && (flags & O_CREAT)) { warningf(true, "%s: %s", cp, "restricted"); return (-1); } u = open(cp, flags, 0666); } if (u < 0) { /* herein() may already have printed message */ if (u == -1) { u = errno; warningf(true, "can't %s %s: %s", iotype == IODUP ? "dup" : (iotype == IOREAD || iotype == IOHERE) ? "open" : "create", cp, cstrerror(u)); } return (-1); } /* Do not save if it has already been redirected (i.e. "cat >x >y"). */ if (e->savefd[iop->unit] == 0) { /* If these are the same, it means unit was previously closed */ if (u == iop->unit) e->savefd[iop->unit] = -1; else /* * c_exec() assumes e->savefd[fd] set for any * redirections. Ask savefd() not to close iop->unit; * this allows error messages to be seen if iop->unit * is 2; also means we can't lose the fd (eg, both * dup2 below and dup2 in restfd() failing). */ e->savefd[iop->unit] = savefd(iop->unit); } if (do_close) close(iop->unit); else if (u != iop->unit) { if (ksh_dup2(u, iop->unit, true) < 0) { int eno; eno = errno; warningf(true, "%s %s %s", "can't finish (dup) redirection", snptreef(NULL, 32, "%R", &iotmp), cstrerror(eno)); if (iotype != IODUP) close(u); return (-1); } if (iotype != IODUP) close(u); /* * Touching any co-process fd in an empty exec * causes the shell to close its copies */ else if (tp && tp->type == CSHELL && tp->val.f == c_exec) { if (iop->flag & IORDUP) /* possible exec <&p */ coproc_read_close(u); else /* possible exec >&p */ coproc_write_close(u); } } if (u == 2) /* Clear any write errors */ shf_reopen(2, SHF_WR, shl_out); return (0); }
int c_read(char **wp) { int c = 0; int expand = 1, history = 0; int expanding; int ecode = 0; char *cp; int fd = 0; struct shf *shf; int optc; const char *emsg; XString cs, xs; struct tbl *vp; char *xp = NULL; while ((optc = ksh_getopt(wp, &builtin_opt, "prsu,")) != -1) switch (optc) { case 'p': if ((fd = coproc_getfd(R_OK, &emsg)) < 0) { bi_errorf("-p: %s", emsg); return 1; } break; case 'r': expand = 0; break; case 's': history = 1; break; case 'u': if (!*(cp = builtin_opt.optarg)) fd = 0; else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) { bi_errorf("-u: %s: %s", cp, emsg); return 1; } break; case '?': return 1; } wp += builtin_opt.optind; if (*wp == NULL) *--wp = "REPLY"; /* Since we can't necessarily seek backwards on non-regular files, * don't buffer them so we can't read too much. */ shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare); if ((cp = strchr(*wp, '?')) != NULL) { *cp = 0; if (isatty(fd)) { /* at&t ksh says it prints prompt on fd if it's open * for writing and is a tty, but it doesn't do it * (it also doesn't check the interactive flag, * as is indicated in the Kornshell book). */ shellf("%s", cp+1); } } /* If we are reading from the co-process for the first time, * make sure the other side of the pipe is closed first. This allows * the detection of eof. * * This is not compatible with at&t ksh... the fd is kept so another * coproc can be started with same output, however, this means eof * can't be detected... This is why it is closed here. * If this call is removed, remove the eof check below, too. * coproc_readw_close(fd); */ if (history) Xinit(xs, xp, 128, ATEMP); expanding = 0; Xinit(cs, cp, 128, ATEMP); for (; *wp != NULL; wp++) { for (cp = Xstring(cs, cp); ; ) { if (c == '\n' || c == EOF) break; while (1) { c = shf_getc(shf); if (c == '\0') continue; if (c == EOF && shf_error(shf) && shf_errno(shf) == EINTR) { /* Was the offending signal one that * would normally kill a process? * If so, pretend the read was killed. */ ecode = fatal_trap_check(); /* non fatal (eg, CHLD), carry on */ if (!ecode) { shf_clearerr(shf); continue; } } break; } if (history) { Xcheck(xs, xp); Xput(xs, xp, c); } Xcheck(cs, cp); if (expanding) { expanding = 0; if (c == '\n') { c = 0; if (Flag(FTALKING_I) && isatty(fd)) { /* set prompt in case this is * called from .profile or $ENV */ set_prompt(PS2, NULL); pprompt(prompt, 0); } } else if (c != EOF) Xput(cs, cp, c); continue; } if (expand && c == '\\') { expanding = 1; continue; } if (c == '\n' || c == EOF) break; if (ctype(c, C_IFS)) { if (Xlength(cs, cp) == 0 && ctype(c, C_IFSWS)) continue; if (wp[1]) break; } Xput(cs, cp, c); } /* strip trailing IFS white space from last variable */ if (!wp[1]) while (Xlength(cs, cp) && ctype(cp[-1], C_IFS) && ctype(cp[-1], C_IFSWS)) cp--; Xput(cs, cp, '\0'); vp = global(*wp); /* Must be done before setting export. */ if (vp->flag & RDONLY) { shf_flush(shf); bi_errorf("%s is read only", *wp); return 1; } if (Flag(FEXPORT)) typeset(*wp, EXPORT, 0, 0, 0); if (!setstr(vp, Xstring(cs, cp), KSH_RETURN_ERROR)) { shf_flush(shf); return 1; } } shf_flush(shf); if (history) { Xput(xs, xp, '\0'); source->line++; histsave(source->line, Xstring(xs, xp), 1); Xfree(xs, xp); } /* if this is the co-process fd, close the file descriptor * (can get eof if and only if all processes are have died, ie, * coproc.njobs is 0 and the pipe is closed). */ if (c == EOF && !ecode) coproc_read_close(fd); return ecode ? ecode : c == EOF; }
/* * run the commands from the input source, returning status. */ int shell(Source * volatile s, volatile int level) { struct op *t; volatile bool wastty = tobool(s->flags & SF_TTY); volatile uint8_t attempts = 13; volatile bool interactive = (level == 0) && Flag(FTALKING); volatile bool sfirst = true; Source *volatile old_source = source; int i; newenv(level == 2 ? E_EVAL : E_PARSE); if (interactive) really_exit = false; switch ((i = kshsetjmp(e->jbuf))) { case 0: break; case LBREAK: case LCONTIN: if (level != 2) { source = old_source; quitenv(NULL); internal_errorf(Tf_cant_s, Tshell, i == LBREAK ? Tbreak : Tcontinue); /* NOTREACHED */ } /* assert: interactive == false */ /* FALLTHROUGH */ case LINTR: /* we get here if SIGINT not caught or ignored */ case LERROR: case LSHELL: if (interactive) { if (i == LINTR) shellf("\n"); /* * Reset any eof that was read as part of a * multiline command. */ if (Flag(FIGNOREEOF) && s->type == SEOF && wastty) s->type = SSTDIN; /* * Used by exit command to get back to * top level shell. Kind of strange since * interactive is set if we are reading from * a tty, but to have stopped jobs, one only * needs FMONITOR set (not FTALKING/SF_TTY)... */ /* toss any input we have so far */ yyrecursive_pop(true); s->start = s->str = null; retrace_info = NULL; herep = heres; break; } /* FALLTHROUGH */ case LEXIT: case LLEAVE: case LRETURN: source = old_source; quitenv(NULL); /* keep on going */ unwind(i); /* NOTREACHED */ default: source = old_source; quitenv(NULL); internal_errorf(Tunexpected_type, Tunwind, Tshell, i); /* NOTREACHED */ } while (/* CONSTCOND */ 1) { if (trap) runtraps(0); if (s->next == NULL) { if (Flag(FVERBOSE)) s->flags |= SF_ECHO; else s->flags &= ~SF_ECHO; } if (interactive) { j_notify(); set_prompt(PS1, s); } t = compile(s, sfirst, true); if (interactive) histsave(&s->line, NULL, HIST_FLUSH, true); sfirst = false; if (!t) goto source_no_tree; if (t->type == TEOF) { if (wastty && Flag(FIGNOREEOF) && --attempts > 0) { shellf("Use 'exit' to leave mksh\n"); s->type = SSTDIN; } else if (wastty && !really_exit && j_stopped_running()) { really_exit = true; s->type = SSTDIN; } else { /* * this for POSIX which says EXIT traps * shall be taken in the environment * immediately after the last command * executed. */ if (level == 0) unwind(LEXIT); break; } } else if ((s->flags & SF_MAYEXEC) && t->type == TCOM) t->u.evalflags |= DOTCOMEXEC; if (!Flag(FNOEXEC) || (s->flags & SF_TTY)) exstat = execute(t, 0, NULL) & 0xFF; if (t->type != TEOF && interactive && really_exit) really_exit = false; source_no_tree: reclaim(); } quitenv(NULL); source = old_source; return (exstat & 0xFF); }