int c_shift(char **wp) { struct block *l = e->loc; int n; long val; char *arg; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; arg = wp[builtin_opt.optind]; if (arg) { evaluate(arg, &val, KSH_UNWIND_ERROR, false); n = val; } else n = 1; if (n < 0) { bi_errorf("%s: bad number", arg); return (1); } if (l->argc < n) { bi_errorf("nothing to shift"); return (1); } l->argv[n] = l->argv[0]; l->argv += n; l->argc -= n; return 0; }
/* kill (built-in) a job */ int j_kill(const char *cp, int sig) { Job *j; int rv = 0; int ecode; 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) { if (kill_job(j, sig) < 0) { bi_errorf("%s: %s", cp, strerror(errno)); rv = 1; } } else { if (killpg(j->pgrp, sig) < 0) { bi_errorf("%s: %s", cp, strerror(errno)); rv = 1; } } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return rv; }
static void ptest_error(Test_env *te, int offset, const char *msg) { const char *op = te->pos.wp + offset >= te->wp_end ? (const char *) 0 : te->pos.wp[offset]; te->flags |= TEF_ERROR; if (op) bi_errorf("%s: %s", op, msg); else bi_errorf("%s", msg); }
static int c_mknod(char **wp) { int argc, optc, ismkfifo = 0, ret; char **argv; void *set = NULL; mode_t mode = 0, oldmode = 0; while ((optc = ksh_getopt(wp, &builtin_opt, "m:")) != -1) { switch (optc) { case 'm': set = setmode(builtin_opt.optarg); if (set == NULL) { bi_errorf("invalid file mode"); return 1; } mode = getmode(set, DEFFILEMODE); free(set); break; default: goto usage; } } argv = &wp[builtin_opt.optind]; if (argv[0] == NULL) goto usage; for (argc = 0; argv[argc]; argc++) ; if (argc == 2 && argv[1][0] == 'p') { ismkfifo = 1; argc--; } else if (argc != 4) goto usage; if (set) oldmode = umask(0); else mode = DEFFILEMODE; if (ismkfifo) ret = domkfifo(argc, argv, mode); else ret = domknod(argc, argv, mode); if (set) umask(oldmode); return ret; usage: builtin_argv0 = NULL; bi_errorf("usage: mknod [-m mode] name b|c major minor"); bi_errorf("usage: mknod [-m mode] name p"); return 1; }
int c_unset(char **wp) { char *id; int optc, unset_var = 1; while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != -1) switch (optc) { case 'f': unset_var = 0; break; case 'v': unset_var = 1; break; case '?': return 1; } wp += builtin_opt.optind; for (; (id = *wp) != NULL; wp++) if (unset_var) { /* unset variable */ struct tbl *vp = global(id); if ((vp->flag&RDONLY)) { bi_errorf("%s is read only", vp->name); return 1; } unset(vp, strchr(id, '[') ? 1 : 0); } else { /* unset function */ define(id, (struct op *) NULL); } return 0; }
/* getn() that prints error */ int bi_getn(const char *as, int *ai) { int rv = getn(as, ai); if (!rv) bi_errorf("%s: bad number", as); return rv; }
static int c_suspend(char **wp) { if (wp[1] != NULL) { bi_errorf("too many arguments"); return 1; } if (Flag(FLOGIN)) { /* Can't suspend an orphaned process group. */ pid_t parent = getppid(); if (getpgid(parent) == getpgid(0) || getsid(parent) != getsid(0)) { bi_errorf("can't suspend a login shell"); return 1; } } j_suspend(); return 0; }
int domkfifo(int argc, char **argv, mode_t mode) { int rv = 0; if (mkfifo(argv[0], mode) < 0) { bi_errorf("%s: %s", argv[0], strerror(errno)); rv = 1; } return (rv); }
static int set_ulimit(const struct limits *l, const char *v, int how) { rlim_t val = 0; struct rlimit limit; if (strcmp(v, "unlimited") == 0) val = RLIM_INFINITY; else { long rval; if (!evaluate(v, &rval, KSH_RETURN_ERROR, false)) return (1); /* * Avoid problems caused by typos that evaluate misses due * to evaluating unset parameters to 0... * If this causes problems, will have to add parameter to * evaluate() to control if unset params are 0 or an error. */ if (!rval && !digit(v[0])) { bi_errorf("invalid limit: %s", v); return (1); } val = (rlim_t)rval * l->factor; } getrlimit(l->resource, &limit); if (how & SOFT) limit.rlim_cur = val; if (how & HARD) limit.rlim_max = val; if (setrlimit(l->resource, &limit) < 0) { if (errno == EPERM) bi_errorf("-%c exceeds allowable limit", l->option); else bi_errorf("bad -%c limit: %s", l->option, strerror(errno)); return (1); } return (0); }
int c_brkcont(char **wp) { int n, quit; struct env *ep, *last_ep = NULL; char *arg; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; arg = wp[builtin_opt.optind]; if (!arg) n = 1; else if (!bi_getn(arg, &n)) return 1; quit = n; if (quit <= 0) { /* at&t ksh does this for non-interactive shells only - weird */ bi_errorf("%s: bad value", arg); return 1; } /* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */ for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv) if (ep->type == E_LOOP) { if (--quit == 0) break; ep->flags |= EF_BRKCONT_PASS; last_ep = ep; } if (quit) { /* at&t ksh doesn't print a message - just does what it * can. We print a message 'cause it helps in debugging * scripts, but don't generate an error (ie, keep going). */ if (n == quit) { warningf(true, "%s: cannot %s", wp[0], wp[0]); return 0; } /* POSIX says if n is too big, the last enclosing loop * shall be used. Doesn't say to print an error but we * do anyway 'cause the user messed up. */ if (last_ep) last_ep->flags &= ~EF_BRKCONT_PASS; warningf(true, "%s: can only %s %d level(s)", wp[0], wp[0], n - quit); } unwind(*wp[0] == 'b' ? LBREAK : LCONTIN); /* NOTREACHED */ }
int c_dot(char **wp) { char *file, *cp; char **argv; int argc; int i; int err; if (ksh_getopt(wp, &builtin_opt, null) == '?') return 1; if ((cp = wp[builtin_opt.optind]) == NULL) { bi_errorf("missing argument"); return 1; } file = search(cp, path, R_OK, &err); if (file == NULL) { bi_errorf("%s: %s", cp, err ? strerror(err) : "not found"); return 1; } /* Set positional parameters? */ if (wp[builtin_opt.optind + 1]) { argv = wp + builtin_opt.optind; argv[0] = e->loc->argv[0]; /* preserve $0 */ for (argc = 0; argv[argc + 1]; argc++) ; } else { argc = 0; argv = (char **) 0; } i = include(file, argc, argv, 0); if (i < 0) { /* should not happen */ bi_errorf("%s: %s", cp, strerror(errno)); return 1; } return i; }
int domknod(int argc, char **argv, mode_t mode) { dev_t dev; char *endp; u_int major, minor; if (argv[1][0] == 'c') mode |= S_IFCHR; else if (argv[1][0] == 'b') mode |= S_IFBLK; else { bi_errorf("node must be type 'b' or 'c'."); return (1); } major = (long)strtoul(argv[2], &endp, 0); if (endp == argv[2] || *endp != '\0') { bi_errorf("non-numeric major number."); return (1); } minor = (long)strtoul(argv[3], &endp, 0); if (endp == argv[3] || *endp != '\0') { bi_errorf("non-numeric minor number."); return (1); } dev = makedev(major, minor); if (major(dev) != major || minor(dev) != minor) { bi_errorf("major or minor number too large"); return (1); } if (mknod(argv[0], mode, dev) < 0) { bi_errorf("%s: %s", argv[0], strerror(errno)); return (1); } return (0); }
/* wait for child, interruptable. */ int waitfor(const char *cp, int *sigp) { int rv; Job *j; int ecode; int flags = JW_INTERRUPT|JW_ASYNCNOTIFY; sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); *sigp = 0; if (cp == (char *) 0) { /* wait for an unspecified job - always returns 0, so * don't have to worry about exited/signaled jobs */ for (j = job_list; j; j = j->next) /* at&t ksh will wait for stopped jobs - we don't */ if (j->ppid == procpid && j->state == PRUNNING) break; if (!j) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return -1; } } else if ((j = j_lookup(cp, &ecode))) { /* don't report normal job completion */ flags &= ~JW_ASYNCNOTIFY; if (j->ppid != procpid) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return -1; } } else { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); if (ecode != JL_NOSUCH) bi_errorf("%s: %s", cp, lookup_msgs[ecode]); return -1; } /* at&t ksh will wait for stopped jobs - we don't */ rv = j_waitj(j, flags, "jw:waitfor"); sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); if (rv < 0) /* we were interrupted */ *sigp = 128 + -rv; return rv; }
/* kill (built-in) a job */ int j_kill(const char *cp, int sig) { Job *j; int rv = 0; int ecode; 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) { /* started when !Flag(FMONITOR) */ if (kill_job(j, sig) < 0) { bi_errorf("%s: %s", cp, strerror(errno)); rv = 1; } } else { #ifdef JOBS if (j->state == PSTOPPED && (sig == SIGTERM || sig == SIGHUP)) (void) killpg(j->pgrp, SIGCONT); #endif /* JOBS */ if (killpg(j->pgrp, sig) < 0) { bi_errorf("%s: %s", cp, strerror(errno)); rv = 1; } } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return rv; }
/* list jobs for jobs built-in */ int j_jobs(const char *cp, int slp, int nflag) /* 0: short, 1: long, 2: pgrp */ { Job *j, *tmp; int how; int zflag = 0; sigset_t omask; sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); if (nflag < 0) { /* kludge: print zombies */ nflag = 0; zflag = 1; } if (cp) { int ecode; 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; } } else j = job_list; how = slp == 0 ? JP_MEDIUM : (slp == 1 ? JP_LONG : JP_PGRP); for (; j; j = j->next) { if ((!(j->flags & JF_ZOMBIE) || zflag) && (!nflag || (j->flags & JF_CHANGED))) { j_print(j, how, shl_stdout); if (j->state == PEXITED || j->state == PSIGNALLED) j->flags |= JF_REMOVE; } if (cp) break; } /* Remove jobs after printing so there won't be multiple + or - jobs */ for (j = job_list; j; j = tmp) { tmp = j->next; if (j->flags & JF_REMOVE) remove_job(j, "jobs"); } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return 0; }
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; }
/* parse command line & set command arguments. returns the index of * non-option arguments, -1 if there is an error. */ int parse_args(char **argv, int what, /* OF_CMDLINE or OF_SET */ int *setargsp) { static char cmd_opts[NELEM(options) + 3]; /* o:\0 */ static char set_opts[NELEM(options) + 5]; /* Ao;s\0 */ char *opts; char *array = (char *) 0; Getopt go; int i, optc, set, sortargs = 0, arrayset = 0; /* First call? Build option strings... */ if (cmd_opts[0] == '\0') { char *p, *q; /* see cmd_opts[] declaration */ strlcpy(cmd_opts, "o:", sizeof cmd_opts); p = cmd_opts + strlen(cmd_opts); /* see set_opts[] declaration */ strlcpy(set_opts, "A:o;s", sizeof set_opts); q = set_opts + strlen(set_opts); for (i = 0; i < NELEM(options); i++) { if (options[i].c) { if (options[i].flags & OF_CMDLINE) *p++ = options[i].c; if (options[i].flags & OF_SET) *q++ = options[i].c; } } *p = '\0'; *q = '\0'; } if (what == OF_CMDLINE) { opts = cmd_opts; } else opts = set_opts; ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); while ((optc = ksh_getopt(argv, &go, opts)) != -1) { set = (go.info & GI_PLUS) ? 0 : 1; switch (optc) { case 'A': arrayset = set ? 1 : -1; array = go.optarg; break; case 'o': if (go.optarg == (char *) 0) { /* lone -o: print options * * Note that on the command line, -o requires * an option (ie, can't get here if what is * OF_CMDLINE). */ printoptions(set); break; } i = option(go.optarg); if (i >= 0 && set == Flag(i)) /* Don't check the context if the flag * isn't changing - makes "set -o interactive" * work if you're already interactive. Needed * if the output of "set +o" is to be used. */ ; else if (i >= 0 && (options[i].flags & what)) change_flag((enum sh_flag) i, what, set); else { bi_errorf("%s: bad option", go.optarg); return -1; } break; case '?': return -1; default: /* -s: sort positional params (at&t ksh stupidity) */ if (what == OF_SET && optc == 's') { sortargs = 1; break; } for (i = 0; i < NELEM(options); i++) if (optc == options[i].c && (what & options[i].flags)) { change_flag((enum sh_flag) i, what, set); break; } if (i == NELEM(options)) { internal_errorf(1, "parse_args: `%c'", optc); return -1; /* not reached */ } } } if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && argv[go.optind][1] == '\0') { /* lone - clears -v and -x flags */ if (argv[go.optind][0] == '-' && !Flag(FPOSIX)) Flag(FVERBOSE) = Flag(FXTRACE) = 0; /* set skips lone - or + option */ go.optind++; } if (setargsp) /* -- means set $#/$* even if there are no arguments */ *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || argv[go.optind]); if (arrayset && (!*array || *skip_varname(array, false))) { bi_errorf("%s: is not an identifier", array); return -1; } if (sortargs) { for (i = go.optind; argv[i]; i++) ; qsortp((void **) &argv[go.optind], (size_t) (i - go.optind), xstrcmp); } if (arrayset) { set_array(array, arrayset, argv + go.optind); for (; argv[go.optind]; go.optind++) ; } return go.optind; }
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_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); }
/* * Parse command line and set command arguments. Returns the index of * non-option arguments, -1 if there is an error. */ int parse_args(const char **argv, /* OF_CMDLINE or OF_SET */ int what, bool *setargsp) { static const char cmd_opts[] = #define SHFLAGS_NOT_SET #define SHFLAGS_OPTCS #include "sh_flags.gen" #undef SHFLAGS_NOT_SET ; static const char set_opts[] = #define SHFLAGS_NOT_CMD #define SHFLAGS_OPTCS #include "sh_flags.gen" #undef SHFLAGS_NOT_CMD ; bool set; const char *opts; const char *array = NULL; Getopt go; size_t i; int optc, arrayset = 0; bool sortargs = false; bool fcompatseen = false; if (what == OF_CMDLINE) { const char *p = argv[0], *q; /* * Set FLOGIN before parsing options so user can clear * flag using +l. */ if (*p != '-') for (q = p; *q; ) if (*q++ == '/') p = q; Flag(FLOGIN) = (*p == '-'); opts = cmd_opts; } else if (what == OF_FIRSTTIME) { opts = cmd_opts; } else opts = set_opts; ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); while ((optc = ksh_getopt(argv, &go, opts)) != -1) { set = tobool(!(go.info & GI_PLUS)); switch (optc) { case 'A': if (what == OF_FIRSTTIME) break; arrayset = set ? 1 : -1; array = go.optarg; break; case 'o': if (what == OF_FIRSTTIME) break; if (go.optarg == NULL) { /* * lone -o: print options * * Note that on the command line, -o requires * an option (ie, can't get here if what is * OF_CMDLINE). */ printoptions(set); break; } i = option(go.optarg); if ((i == FPOSIX || i == FSH) && set && !fcompatseen) { /* * If running 'set -o posix' or * 'set -o sh', turn off the other; * if running 'set -o posix -o sh' * allow both to be set though. */ Flag(FPOSIX) = 0; Flag(FSH) = 0; fcompatseen = true; } if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i)) /* * Don't check the context if the flag * isn't changing - makes "set -o interactive" * work if you're already interactive. Needed * if the output of "set +o" is to be used. */ ; else if ((i != (size_t)-1) && (OFF(i) & what)) change_flag((enum sh_flag)i, what, set); else { bi_errorf("%s: %s", go.optarg, "bad option"); return (-1); } break; #ifdef KSH_CHVT_FLAG case 'T': if (what != OF_FIRSTTIME) break; #ifndef KSH_CHVT_CODE errorf("no TIOCSCTTY ioctl"); #else change_flag(FTALKING, OF_CMDLINE, true); chvt(&go); break; #endif #endif case '?': return (-1); default: if (what == OF_FIRSTTIME) break; /* -s: sort positional params (AT&T ksh stupidity) */ if (what == OF_SET && optc == 's') { sortargs = true; break; } for (i = 0; i < NELEM(options); i++) if (optc == OFC(i) && (what & OFF(i))) { change_flag((enum sh_flag)i, what, set); break; } if (i == NELEM(options)) internal_errorf("parse_args: '%c'", optc); } } if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && argv[go.optind][1] == '\0') { /* lone - clears -v and -x flags */ if (argv[go.optind][0] == '-') { Flag(FVERBOSE) = 0; change_xtrace(0, false); } /* set skips lone - or + option */ go.optind++; } if (setargsp) /* -- means set $#/$* even if there are no arguments */ *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || argv[go.optind]); if (arrayset) { const char *ccp = NULL; if (*array) ccp = skip_varname(array, false); if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) { bi_errorf("%s: %s", array, "is not an identifier"); return (-1); } } if (sortargs) { for (i = go.optind; argv[i]; i++) ; qsort(&argv[go.optind], i - go.optind, sizeof(void *), xstrcmp); } if (arrayset) go.optind += set_array(array, tobool(arrayset > 0), argv + go.optind); return (go.optind); }
/* Parse command line & set command arguments. Returns the index of * non-option arguments, -1 if there is an error. */ int parse_args(const char **argv, int what, /* OF_CMDLINE or OF_SET */ bool *setargsp) { static char cmd_opts[NELEM(options) + 5]; /* o:T:\0 */ static char set_opts[NELEM(options) + 6]; /* A:o;s\0 */ char set, *opts; const char *array = NULL; Getopt go; size_t i; int optc, sortargs = 0, arrayset = 0; /* First call? Build option strings... */ if (cmd_opts[0] == '\0') { char *p = cmd_opts, *q = set_opts; /* see cmd_opts[] declaration */ *p++ = 'o'; *p++ = ':'; #if !defined(MKSH_SMALL) || defined(TIOCSCTTY) *p++ = 'T'; *p++ = ':'; #endif /* see set_opts[] declaration */ *q++ = 'A'; *q++ = ':'; *q++ = 'o'; *q++ = ';'; *q++ = 's'; for (i = 0; i < NELEM(options); i++) { if (options[i].c) { if (options[i].flags & OF_CMDLINE) *p++ = options[i].c; if (options[i].flags & OF_SET) *q++ = options[i].c; } } *p = '\0'; *q = '\0'; } if (what == OF_CMDLINE) { const char *p = argv[0], *q; /* Set FLOGIN before parsing options so user can clear * flag using +l. */ if (*p != '-') for (q = p; *q; ) if (*q++ == '/') p = q; Flag(FLOGIN) = (*p == '-'); opts = cmd_opts; } else if (what == OF_FIRSTTIME) { opts = cmd_opts; } else opts = set_opts; ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); while ((optc = ksh_getopt(argv, &go, opts)) != -1) { set = (go.info & GI_PLUS) ? 0 : 1; switch (optc) { case 'A': if (what == OF_FIRSTTIME) break; arrayset = set ? 1 : -1; array = go.optarg; break; case 'o': if (what == OF_FIRSTTIME) break; if (go.optarg == NULL) { /* lone -o: print options * * Note that on the command line, -o requires * an option (ie, can't get here if what is * OF_CMDLINE). */ printoptions(set); break; } i = option(go.optarg); if ((enum sh_flag)i == FARC4RANDOM) { warningf(true, "Do not use set ±o arc4random," " it will be removed in the next version" " of mksh!"); return (0); } if ((i != (size_t)-1) && set == Flag(i)) /* Don't check the context if the flag * isn't changing - makes "set -o interactive" * work if you're already interactive. Needed * if the output of "set +o" is to be used. */ ; else if ((i != (size_t)-1) && (options[i].flags & what)) change_flag((enum sh_flag)i, what, set); else { bi_errorf("%s: bad option", go.optarg); return (-1); } break; #if !defined(MKSH_SMALL) || defined(TIOCSCTTY) case 'T': if (what != OF_FIRSTTIME) break; #ifndef TIOCSCTTY errorf("no TIOCSCTTY ioctl"); #else change_flag(FTALKING, OF_CMDLINE, 1); chvt(go.optarg); break; #endif #endif case '?': return (-1); default: if (what == OF_FIRSTTIME) break; /* -s: sort positional params (AT&T ksh stupidity) */ if (what == OF_SET && optc == 's') { sortargs = 1; break; } for (i = 0; i < NELEM(options); i++) if (optc == options[i].c && (what & options[i].flags)) { change_flag((enum sh_flag)i, what, set); break; } if (i == NELEM(options)) internal_errorf("parse_args: '%c'", optc); } } if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && argv[go.optind][1] == '\0') { /* lone - clears -v and -x flags */ if (argv[go.optind][0] == '-') Flag(FVERBOSE) = Flag(FXTRACE) = 0; /* set skips lone - or + option */ go.optind++; } if (setargsp) /* -- means set $#/$* even if there are no arguments */ *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || argv[go.optind]); if (arrayset && (!*array || *skip_varname(array, false))) { bi_errorf("%s: is not an identifier", array); return (-1); } if (sortargs) { for (i = go.optind; argv[i]; i++) ; qsort(&argv[go.optind], i - go.optind, sizeof(void *), xstrcmp); } if (arrayset) go.optind += set_array(array, arrayset > 0 ? true : false, argv + go.optind); return (go.optind); }
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; }
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; }
int c_test(char **wp) { int argc; int res; Test_env te; te.flags = 0; te.isa = ptest_isa; te.getopnd = ptest_getopnd; te.eval = ptest_eval; te.error = ptest_error; for (argc = 0; wp[argc]; argc++) ; if (strcmp(wp[0], "[") == 0) { if (strcmp(wp[--argc], "]") != 0) { bi_errorf("missing ]"); return T_ERR_EXIT; } } te.pos.wp = wp + 1; te.wp_end = wp + argc; /* * Handle the special cases from POSIX.2, section 4.62.4. * Implementation of all the rules isn't necessary since * our parser does the right thing for the omitted steps. */ if (argc <= 5) { char **owp = wp; int invert = 0; Test_op op; const char *opnd1, *opnd2; while (--argc >= 0) { if ((*te.isa)(&te, TM_END)) return !0; if (argc == 3) { opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); if ((op = (Test_op) (*te.isa)(&te, TM_BINOP))) { opnd2 = (*te.getopnd)(&te, op, 1); res = (*te.eval)(&te, op, opnd1, opnd2, 1); if (te.flags & TEF_ERROR) return T_ERR_EXIT; if (invert & 1) res = !res; return !res; } /* back up to opnd1 */ te.pos.wp--; } if (argc == 1) { opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); /* Historically, -t by itself test if fd 1 * is a file descriptor, but POSIX says its * a string test... */ if (!Flag(FPOSIX) && strcmp(opnd1, "-t") == 0) break; res = (*te.eval)(&te, TO_STNZE, opnd1, (char *) 0, 1); if (invert & 1) res = !res; return !res; } if ((*te.isa)(&te, TM_NOT)) { invert++; } else break; } te.pos.wp = owp + 1; } return test_parse(&te); }
/* getopt() used for shell built-in commands, the getopts command, and * command line options. * A leading ':' in options means don't print errors, instead return '?' * or ':' and set go->optarg to the offending option character. * If GF_ERROR is set (and option doesn't start with :), errors result in * a call to bi_errorf(). * * Non-standard features: * - ';' is like ':' in options, except the argument is optional * (if it isn't present, optarg is set to 0). * Used for 'set -o'. * - ',' is like ':' in options, except the argument always immediately * follows the option character (optarg is set to the null string if * the option is missing). * Used for 'read -u2', 'print -u2' and fc -40. * - '#' is like ':' in options, expect that the argument is optional * and must start with a digit. If the argument doesn't start with a * digit, it is assumed to be missing and normal option processing * continues (optarg is set to 0 if the option is missing). * Used for 'typeset -LZ4'. * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an * option starting with + is accepted, the GI_PLUS flag will be set * in go->info. */ int ksh_getopt(char **argv, Getopt *go, const char *options) { char c; char *o; if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') { char *arg = argv[go->optind], flag = arg ? *arg : '\0'; go->p = 1; if (flag == '-' && arg[1] == '-' && arg[2] == '\0') { go->optind++; go->p = 0; go->info |= GI_MINUSMINUS; return -1; } if (arg == (char *) 0 || ((flag != '-' ) && /* neither a - nor a + (if + allowed) */ (!(go->flags & GF_PLUSOPT) || flag != '+')) || (c = arg[1]) == '\0') { go->p = 0; return -1; } go->optind++; go->info &= ~(GI_MINUS|GI_PLUS); go->info |= flag == '-' ? GI_MINUS : GI_PLUS; } go->p++; if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' || !(o = strchr(options, c))) { if (options[0] == ':') { go->buf[0] = c; go->optarg = go->buf; } else { warningf(true, "%s%s-%c: unknown option", (go->flags & GF_NONAME) ? "" : argv[0], (go->flags & GF_NONAME) ? "" : ": ", c); if (go->flags & GF_ERROR) bi_errorf(null); } return '?'; } /* : means argument must be present, may be part of option argument * or the next argument * ; same as : but argument may be missing * , means argument is part of option argument, and may be null. */ if (*++o == ':' || *o == ';') { if (argv[go->optind - 1][go->p]) go->optarg = argv[go->optind - 1] + go->p; else if (argv[go->optind]) go->optarg = argv[go->optind++]; else if (*o == ';') go->optarg = (char *) 0; else { if (options[0] == ':') { go->buf[0] = c; go->optarg = go->buf; return ':'; } warningf(true, "%s%s-`%c' requires argument", (go->flags & GF_NONAME) ? "" : argv[0], (go->flags & GF_NONAME) ? "" : ": ", c); if (go->flags & GF_ERROR) bi_errorf(null); return '?'; } go->p = 0; } else if (*o == ',') { /* argument is attatched to option character, even if null */ go->optarg = argv[go->optind - 1] + go->p; go->p = 0; } else if (*o == '#') { /* argument is optional and may be attatched or unattatched * but must start with a digit. optarg is set to 0 if the * argument is missing. */ if (argv[go->optind - 1][go->p]) { if (digit(argv[go->optind - 1][go->p])) { go->optarg = argv[go->optind - 1] + go->p; go->p = 0; } else go->optarg = (char *) 0;; } else { if (argv[go->optind] && digit(argv[go->optind][0])) { go->optarg = argv[go->optind++]; go->p = 0; } else go->optarg = (char *) 0;; } } return c; }
/* 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_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_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; }