/* print variable/alias value using necessary quotes * (POSIX says they should be suitable for re-entry...) * No trailing newline is printed. */ void print_value_quoted(const char *s) { const char *p; int inquote = 0; /* Test if any quotes are needed */ for (p = s; *p; p++) if (ctype(*p, C_QUOTE)) break; if (!*p) { shprintf("%s", s); return; } for (p = s; *p; p++) { if (*p == '\'') { shprintf("'\\'" + 1 - inquote); inquote = 0; } else { if (!inquote) { shprintf("'"); inquote = 1; } shf_putc(*p, shl_stdout); } } if (inquote) shprintf("'"); }
static void printoptions(int verbose) { int i; if (verbose) { struct options_info oi; int n, len; /* verbose version */ shprintf("Current option settings\n"); for (i = n = oi.opt_width = 0; i < NELEM(options); i++) if (options[i].name) { len = strlen(options[i].name); oi.opts[n].name = options[i].name; oi.opts[n++].flag = i; if (len > oi.opt_width) oi.opt_width = len; } print_columns(shl_stdout, n, options_fmt_entry, &oi, oi.opt_width + 5, 1); } else { /* short version ala ksh93 */ shprintf("set"); for (i = 0; i < NELEM(options); i++) if (Flag(i) && options[i].name) shprintf(" -o %s", options[i].name); shprintf(newline); } }
int c_times(char **wp) { struct tms all; (void) ksh_times(&all); shprintf("Shell: %8ss user ", clocktos(all.tms_utime)); shprintf("%8ss system\n", clocktos(all.tms_stime)); shprintf("Kids: %8ss user ", clocktos(all.tms_cutime)); shprintf("%8ss system\n", clocktos(all.tms_cstime)); return 0; }
static void print_ulimit(const struct limits *l, int how) { rlim_t val = 0; struct rlimit limit; getrlimit(l->resource, &limit); if (how & SOFT) val = limit.rlim_cur; else if (how & HARD) val = limit.rlim_max; if (val == RLIM_INFINITY) shprintf("unlimited\n"); else { val /= l->factor; shprintf("%ld\n", (long) val); } }
int c_trap(char **wp) { int i; char *s; Trap *p; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; wp += builtin_opt.optind; if (*wp == NULL) { for (p = sigtraps, i = NSIG+1; --i >= 0; p++) { if (p->trap != NULL) { shprintf("trap -- "); print_value_quoted(p->trap); shprintf(" %s\n", p->name); } } return 0; } /* * Use case sensitive lookup for first arg so the * command 'exit' isn't confused with the pseudo-signal * 'EXIT'. */ s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL; /* get command */ if (s != NULL && s[0] == '-' && s[1] == '\0') s = NULL; /* set/clear traps */ while (*wp != NULL) { p = gettrap(*wp++, true); if (p == NULL) { bi_errorf("bad signal %s", wp[-1]); return 1; } settrap(p, s); } return 0; }
static void printoptions(int verbose) { unsigned int ele; if (verbose) { struct options_info oi; unsigned int n; int len; /* verbose version */ shprintf("Current option settings\n"); for (ele = n = oi.opt_width = 0; ele < NELEM(sh_options); ele++) { if (sh_options[ele].name) { len = strlen(sh_options[ele].name); oi.opts[n].name = sh_options[ele].name; oi.opts[n++].flag = ele; if (len > oi.opt_width) oi.opt_width = len; } } print_columns(shl_stdout, n, options_fmt_entry, &oi, oi.opt_width + 5, 1); } else { /* short version ala ksh93 */ shprintf("set"); for (ele = 0; ele < NELEM(sh_options); ele++) { if (sh_options[ele].name) shprintf(" %co %s", Flag(ele) ? '-' : '+', sh_options[ele].name); } shprintf("\n"); } }
static void printoptions(bool verbose) { int i = 0; if (verbose) { int n = 0, len, octs = 0; struct options_info oi; /* verbose version */ shf_puts("Current option settings\n", shl_stdout); oi.opt_width = 0; while (i < (int)NELEM(options)) { if (options[i].name) { oi.opts[n++] = i; len = strlen(options[i].name); if (len > octs) octs = len; len = utf_mbswidth(options[i].name); if (len > oi.opt_width) oi.opt_width = len; } ++i; } print_columns(shl_stdout, n, options_fmt_entry, &oi, octs + 4, oi.opt_width + 4, true); } else { /* short version รก la AT&T ksh93 */ shf_puts("set", shl_stdout); while (i < (int)NELEM(options)) { if (Flag(i) && options[i].name) shprintf(" -o %s", options[i].name); ++i; } shf_putc('\n', shl_stdout); } }
static void printoptions(bool verbose) { size_t i = 0; if (verbose) { size_t n = 0, len, octs = 0; struct options_info oi; /* verbose version */ shf_puts("Current option settings\n", shl_stdout); oi.opt_width = 0; while (i < NELEM(options)) { if ((len = strlen(OFN(i)))) { oi.opts[n++] = i; if (len > octs) octs = len; len = utf_mbswidth(OFN(i)); if ((int)len > oi.opt_width) oi.opt_width = (int)len; } ++i; } print_columns(shl_stdout, n, options_fmt_entry, &oi, octs + 4, oi.opt_width + 4, true); } else { /* short version like AT&T ksh93 */ shf_puts(Tset, shl_stdout); while (i < NELEM(options)) { if (Flag(i) && OFN(i)[0]) shprintf(" -o %s", OFN(i)); ++i; } shf_putc('\n', shl_stdout); } }
int c_ulimit(char **wp) { static const struct limits limits[] = { /* Do not use options -H, -S or -a or change the order. */ { "time(cpu-seconds)", RLIMIT_CPU, 1, 't' }, { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, { "lockedmem(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, { "processes", RLIMIT_NPROC, 1, 'p' }, #ifdef RLIMIT_VMEM { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' }, #endif /* RLIMIT_VMEM */ { NULL, 0, 0, 0 }, }; static char _options[4 + NELEM(limits) * 2]; int how = SOFT | HARD; const struct limits *l; int optc, all = 0; if (!_options[0]) { /* build options string on first call - yuck */ char *p = _options; *p++ = 'H'; *p++ = 'S'; *p++ = 'a'; for (l = limits; l->name; l++) { *p++ = l->option; *p++ = '#'; } *p = '\0'; } /* First check for -a, -H and -S. */ while ((optc = ksh_getopt(wp, &builtin_opt, _options)) != -1) switch (optc) { case 'H': how = HARD; break; case 'S': how = SOFT; break; case 'a': all = 1; break; case '?': return (1); default: break; } if (wp[builtin_opt.optind] != NULL) { bi_errorf("usage: ulimit [-acdfHlmnpSst] [value]"); return (1); } /* Then parse and act on the actual limits, one at a time */ ksh_getopt_reset(&builtin_opt, GF_ERROR); while ((optc = ksh_getopt(wp, &builtin_opt, _options)) != -1) switch (optc) { case 'a': case 'H': case 'S': break; case '?': return (1); default: for (l = limits; l->name && l->option != optc; l++) ; if (!l->name) { internal_errorf(0, "ulimit: %c", optc); return (1); } if (builtin_opt.optarg) { if (set_ulimit(l, builtin_opt.optarg, how)) return (1); } else print_ulimit(l, how); break; } wp += builtin_opt.optind; if (all) { for (l = limits; l->name; l++) { shprintf("%-20s ", l->name); print_ulimit(l, how); } } else if (builtin_opt.optind == 1) { /* No limit specified, use file size */ l = &limits[1]; if (wp[0] != NULL) { if (set_ulimit(l, wp[0], how)) return (1); wp++; } else { print_ulimit(l, how); } } return (0); }
int c_umask(char **wp) { int i; char *cp; int symbolic = 0; mode_t old_umask; int optc; while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != -1) switch (optc) { case 'S': symbolic = 1; break; case '?': return 1; } cp = wp[builtin_opt.optind]; if (cp == NULL) { old_umask = umask(0); umask(old_umask); if (symbolic) { char buf[18]; int j; old_umask = ~old_umask; cp = buf; for (i = 0; i < 3; i++) { *cp++ = "ugo"[i]; *cp++ = '='; for (j = 0; j < 3; j++) if (old_umask & (1 << (8 - (3*i + j)))) *cp++ = "rwx"[j]; *cp++ = ','; } cp[-1] = '\0'; shprintf("%s\n", buf); } else shprintf("%#3.3o\n", old_umask); } else { mode_t new_umask; if (digit(*cp)) { for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++) new_umask = new_umask * 8 + (*cp - '0'); if (*cp) { bi_errorf("bad number"); return 1; } } else { /* symbolic format */ int positions, new_val; char op; old_umask = umask(0); umask(old_umask); /* in case of error */ old_umask = ~old_umask; new_umask = old_umask; positions = 0; while (*cp) { while (*cp && strchr("augo", *cp)) switch (*cp++) { case 'a': positions |= 0111; break; case 'u': positions |= 0100; break; case 'g': positions |= 0010; break; case 'o': positions |= 0001; break; } if (!positions) positions = 0111; /* default is a */ if (!strchr("=+-", op = *cp)) break; cp++; new_val = 0; while (*cp && strchr("rwxugoXs", *cp)) switch (*cp++) { case 'r': new_val |= 04; break; case 'w': new_val |= 02; break; case 'x': new_val |= 01; break; case 'u': new_val |= old_umask >> 6; break; case 'g': new_val |= old_umask >> 3; break; case 'o': new_val |= old_umask >> 0; break; case 'X': if (old_umask & 0111) new_val |= 01; break; case 's': /* ignored */ break; } new_val = (new_val & 07) * positions; switch (op) { case '-': new_umask &= ~new_val; break; case '=': new_umask = new_val | (new_umask & ~(positions * 07)); break; case '+': new_umask |= new_val; } if (*cp == ',') { positions = 0; cp++; } else if (!strchr("=+-", *cp)) break; } if (*cp) { bi_errorf("bad mask"); return 1; } new_umask = ~new_umask; } umask(new_umask); } return 0; }
/* fg and bg built-ins: called only if Flag(FMONITOR) set */ int j_resume(const char *cp, int bg) { Job *j; Proc *p; int ecode; int running; int rv = 0; sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); if ((j = j_lookup(cp, &ecode)) == (Job *) 0) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return 1; } if (j->pgrp == 0) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("job not job-controlled"); return 1; } if (bg) shprintf("[%d] ", j->job); running = 0; for (p = j->proc_list; p != (Proc *) 0; p = p->next) { if (p->state == PSTOPPED) { p->state = PRUNNING; p->status = 0; running = 1; } shprintf("%s%s", p->command, p->next ? "| " : null); } shprintf("%s", newline); shf_flush(shl_stdout); if (running) j->state = PRUNNING; put_job(j, PJ_PAST_STOPPED); if (bg) j_set_async(j); else { # ifdef JOBS /* attach tty to job */ if (j->state == PRUNNING) { if (ttypgrp_ok && (j->flags & JF_SAVEDTTY)) tcsetattr(tty_fd, TCSADRAIN, &j->ttystate); /* See comment in j_waitj regarding saved_ttypgrp. */ if (ttypgrp_ok && tcsetpgrp(tty_fd, (j->flags & JF_SAVEDTTYPGRP) ? j->saved_ttypgrp : j->pgrp) < 0) { if (j->flags & JF_SAVEDTTY) tcsetattr(tty_fd, TCSADRAIN, &tty_state); sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("1st tcsetpgrp(%d, %d) failed: %s", tty_fd, (int) ((j->flags & JF_SAVEDTTYPGRP) ? j->saved_ttypgrp : j->pgrp), strerror(errno)); return 1; } } # endif /* JOBS */ j->flags |= JF_FG; j->flags &= ~JF_KNOWN; if (j == async_job) async_job = (Job *) 0; } if (j->state == PRUNNING && killpg(j->pgrp, SIGCONT) < 0) { int err = errno; if (!bg) { j->flags &= ~JF_FG; # ifdef JOBS if (ttypgrp_ok && (j->flags & JF_SAVEDTTY)) tcsetattr(tty_fd, TCSADRAIN, &tty_state); if (ttypgrp_ok && tcsetpgrp(tty_fd, our_pgrp) < 0) { warningf(true, "fg: 2nd tcsetpgrp(%d, %d) failed: %s", tty_fd, (int) our_pgrp, strerror(errno)); } # endif /* JOBS */ } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); bi_errorf("cannot continue job %s: %s", cp, strerror(err)); return 1; } if (!bg) { # ifdef JOBS if (ttypgrp_ok) { j->flags &= ~(JF_SAVEDTTY | JF_SAVEDTTYPGRP); } # endif /* JOBS */ rv = j_waitj(j, JW_NONE, "jw:resume"); } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return rv; }
int c_cd(const char **wp) { int optc, rv, phys_path; bool physical = tobool(Flag(FPHYSICAL)); /* was a node from cdpath added in? */ int cdnode; /* show where we went?, error for $PWD */ bool printpath = false, eflag = false; struct tbl *pwd_s, *oldpwd_s; XString xs; char *dir, *allocd = NULL, *tryp, *pwd, *cdpath; while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1) switch (optc) { case 'e': eflag = true; break; case 'L': physical = false; break; case 'P': physical = true; break; case '?': return (2); } wp += builtin_opt.optind; if (Flag(FRESTRICTED)) { bi_errorf("restricted shell - can't cd"); return (2); } pwd_s = global("PWD"); oldpwd_s = global("OLDPWD"); if (!wp[0]) { /* No arguments - go home */ if ((dir = str_val(global("HOME"))) == null) { bi_errorf("no home directory (HOME not set)"); return (2); } } else if (!wp[1]) { /* One argument: - or dir */ strdupx(allocd, wp[0], ATEMP); if (ksh_isdash((dir = allocd))) { afree(allocd, ATEMP); allocd = NULL; dir = str_val(oldpwd_s); if (dir == null) { bi_errorf("no OLDPWD"); return (2); } printpath = true; } } else if (!wp[2]) { /* Two arguments - substitute arg1 in PWD for arg2 */ size_t ilen, olen, nlen, elen; char *cp; if (!current_wd[0]) { bi_errorf("can't determine current directory"); return (2); } /* * substitute arg1 for arg2 in current path. * if the first substitution fails because the cd fails * we could try to find another substitution. For now * we don't */ if ((cp = strstr(current_wd, wp[0])) == NULL) { bi_errorf("bad substitution"); return (2); } /*- * ilen = part of current_wd before wp[0] * elen = part of current_wd after wp[0] * because current_wd and wp[1] need to be in memory at the * same time beforehand the addition can stay unchecked */ ilen = cp - current_wd; olen = strlen(wp[0]); nlen = strlen(wp[1]); elen = strlen(current_wd + ilen + olen) + 1; dir = allocd = alloc(ilen + nlen + elen, ATEMP); memcpy(dir, current_wd, ilen); memcpy(dir + ilen, wp[1], nlen); memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen); printpath = true; } else { bi_errorf("too many arguments"); return (2); } #ifdef MKSH__NO_PATH_MAX /* only a first guess; make_path will enlarge xs if necessary */ XinitN(xs, 1024, ATEMP); #else XinitN(xs, PATH_MAX, ATEMP); #endif cdpath = str_val(global("CDPATH")); do { cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path); if (physical) rv = chdir(tryp = Xstring(xs, xp) + phys_path); else { simplify_path(Xstring(xs, xp)); rv = chdir(tryp = Xstring(xs, xp)); } } while (rv < 0 && cdpath != NULL); if (rv < 0) { if (cdnode) bi_errorf("%s: %s", dir, "bad directory"); else bi_errorf("%s: %s", tryp, cstrerror(errno)); afree(allocd, ATEMP); Xfree(xs, xp); return (2); } rv = 0; /* allocd (above) => dir, which is no longer used */ afree(allocd, ATEMP); allocd = NULL; /* Clear out tracked aliases with relative paths */ flushcom(false); /* * Set OLDPWD (note: unsetting OLDPWD does not disable this * setting in AT&T ksh) */ if (current_wd[0]) /* Ignore failure (happens if readonly or integer) */ setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR); if (!mksh_abspath(Xstring(xs, xp))) { pwd = NULL; } else if (!physical) { goto norealpath_PWD; } else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) { if (eflag) rv = 1; norealpath_PWD: pwd = Xstring(xs, xp); } /* Set PWD */ if (pwd) { char *ptmp = pwd; set_current_wd(ptmp); /* Ignore failure (happens if readonly or integer) */ setstr(pwd_s, ptmp, KSH_RETURN_ERROR); } else { set_current_wd(null); pwd = Xstring(xs, xp); /* XXX unset $PWD? */ if (eflag) rv = 1; } if (printpath || cdnode) shprintf("%s\n", pwd); afree(allocd, ATEMP); Xfree(xs, xp); return (rv); }
int c_trap(char **wp) { int i; char *s; Trap *p; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; wp += builtin_opt.optind; if (*wp == NULL) { #if 0/*defined but not used*/ int anydfl = 0; #endif for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) { if (p->trap == NULL) { #if 0/*defined but not used*/ anydfl = 1; #endif } else { shprintf("trap -- "); print_value_quoted(p->trap); shprintf(" %s\n", p->name); } } #if 0 /* this is ugly and not clear POSIX needs it */ /* POSIX may need this so output of trap can be saved and * used to restore trap conditions */ if (anydfl) { shprintf("trap -- -"); for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) if (p->trap == NULL && p->name) shprintf(" %s", p->name); shprintf(newline); } #endif return 0; } /* * Use case sensitive lookup for first arg so the * command 'exit' isn't confused with the pseudo-signal * 'EXIT'. */ s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL; /* get command */ if (s != NULL && s[0] == '-' && s[1] == '\0') s = NULL; /* set/clear traps */ while (*wp != NULL) { p = gettrap(*wp++, true); if (p == NULL) { bi_errorf("bad signal %s", wp[-1]); return 1; } settrap(p, s); } return 0; }
int c_ulimit(char **wp) { static const struct limits limits[] = { /* Do not use options -H, -S or -a or change the order. */ { "time(cpu-seconds)", RLIMIT_CPU, 1, 't' }, { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, { "lockedmem(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, { "processes", RLIMIT_NPROC, 1, 'p' }, { NULL } }; const char *options = "HSat#f#c#d#s#l#m#n#p#"; int how = SOFT | HARD; const struct limits *l; int optc, all = 0; /* First check for -a, -H and -S. */ while ((optc = ksh_getopt(wp, &builtin_opt, options)) != -1) switch (optc) { case 'H': how = HARD; break; case 'S': how = SOFT; break; case 'a': all = 1; break; case '?': return 1; default: break; } if (wp[builtin_opt.optind] != NULL) { bi_errorf("usage: ulimit [-acdfHlmnpSst] [value]"); return 1; } /* Then parse and act on the actual limits, one at a time */ ksh_getopt_reset(&builtin_opt, GF_ERROR); while ((optc = ksh_getopt(wp, &builtin_opt, options)) != -1) switch (optc) { case 'a': case 'H': case 'S': break; case '?': return 1; default: for (l = limits; l->name && l->option != optc; l++) ; if (!l->name) { internal_warningf("%s: %c", __func__, optc); return 1; } if (builtin_opt.optarg) { if (set_ulimit(l, builtin_opt.optarg, how)) return 1; } else print_ulimit(l, how); break; } wp += builtin_opt.optind; if (all) { for (l = limits; l->name; l++) { shprintf("%-20s ", l->name); print_ulimit(l, how); } } else if (builtin_opt.optind == 1) { /* No limit specified, use file size */ l = &limits[1]; if (wp[0] != NULL) { if (set_ulimit(l, wp[0], how)) return 1; wp++; } else { print_ulimit(l, how); } } return 0; }