rc

[fork] interactive rc shell
Log | Files | Refs | README | LICENSE

commit 2dd0aa249c9bac4e5c616d1f386dc4271fdbab46
parent 1128baa58acaff32e4538d5878b916fba091c574
Author: Toby Goodwin <toby@paccrat.org>
Date:   Sat, 17 Mar 2018 18:36:15 +0000

Merge branch 'master' into ifnot

Diffstat:
Mbuiltins.c | 259++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mmain.c | 16++++++++++------
Mrc.1 | 30++++++++++++++++++++++++++++++
Mrc.h | 5++++-
Mtrip.rc | 8++++++++
5 files changed, 216 insertions(+), 102 deletions(-)

diff --git a/builtins.c b/builtins.c @@ -20,8 +20,8 @@ #include "rlimit.h" #include "sigmsgs.h" -static void b_break(char **), b_cd(char **), b_continue(char **), b_eval(char **), b_exit(char **), - b_newpgrp(char **), b_return(char **), b_shift(char **), b_umask(char **), +static void b_break(char **), b_cd(char **), b_continue(char **), b_eval(char **), b_flag(char **), + b_exit(char **), b_newpgrp(char **), b_return(char **), b_shift(char **), b_umask(char **), b_wait(char **), b_whatis(char **); #if HAVE_SETRLIMIT @@ -46,27 +46,28 @@ static struct { { b_eval, "eval" }, { b_exec, "exec" }, { b_exit, "exit" }, + { b_flag, "flag" }, #if HAVE_SETRLIMIT - { b_limit, "limit" }, + { b_limit, "limit" }, #endif - { b_newpgrp, "newpgrp" }, - { b_return, "return" }, - { b_shift, "shift" }, - { b_umask, "umask" }, - { b_wait, "wait" }, - { b_whatis, "whatis" }, - { b_dot, "." }, + { b_newpgrp, "newpgrp" }, + { b_return, "return" }, + { b_shift, "shift" }, + { b_umask, "umask" }, + { b_wait, "wait" }, + { b_whatis, "whatis" }, + { b_dot, "." }, #ifdef ADDONS - ADDONS + ADDONS #endif }; extern builtin_t *isbuiltin(char *s) { - int i; - for (i = 0; i < arraysize(builtins); i++) - if (streq(builtins[i].name, s)) - return builtins[i].p; - return NULL; + int i; + for (i = 0; i < arraysize(builtins); i++) + if (streq(builtins[i].name, s)) + return builtins[i].p; + return NULL; } /* funcall() is the wrapper used to invoke shell functions. pushes $*, and "return" returns here. */ @@ -89,13 +90,13 @@ extern void funcall(char **av) { } static void arg_count(char *name) { - fprint(2, RC "too many arguments to %s\n", name); - set(FALSE); + fprint(2, RC "too many arguments to %s\n", name); + set(FALSE); } static void badnum(char *num) { - fprint(2, RC "`%s' is a bad number\n", num); - set(FALSE); + fprint(2, RC "`%s' is a bad number\n", num); + set(FALSE); } /* a dummy command. (exec() performs "exec" simply by not forking) */ @@ -107,90 +108,90 @@ extern void b_exec(char **ignore) { /* echo -n omits a newline. echo -- -n echos '-n' */ static void b_echo(char **av) { - char *format = "%A\n"; - if (*++av != NULL) { - if (streq(*av, "-n")) - format = "%A", av++; - else if (streq(*av, "--")) - av++; - } - fprint(1, format, av); - set(TRUE); + char *format = "%A\n"; + if (*++av != NULL) { + if (streq(*av, "-n")) + format = "%A", av++; + else if (streq(*av, "--")) + av++; + } + fprint(1, format, av); + set(TRUE); } #endif /* cd. traverse $cdpath if the directory given is not an absolute pathname */ static void b_cd(char **av) { - List *s, nil; - char *path = NULL; - size_t t, pathlen = 0; - if (*++av == NULL) { - s = varlookup("home"); - *av = (s == NULL) ? "/" : s->w; - } else if (av[1] != NULL) { - arg_count("cd"); - return; - } - if (isabsolute(*av) || streq(*av, ".") || streq(*av, "..")) { /* absolute pathname? */ - if (chdir(*av) < 0) { - set(FALSE); - uerror(*av); - } else - set(TRUE); - } else { - s = varlookup("cdpath"); - if (s == NULL) { - s = &nil; - nil.w = ""; - nil.n = NULL; - } - do { - if (s != &nil && *s->w != '\0') { - t = strlen(*av) + strlen(s->w) + 2; - if (t > pathlen) - path = nalloc(pathlen = t); - strcpy(path, s->w); - if (!streq(s->w, "/")) /* "//" is special to POSIX */ - strcat(path, "/"); - strcat(path, *av); - } else { - pathlen = 0; - path = *av; - } - if (chdir(path) >= 0) { - set(TRUE); - if (interactive && *s->w != '\0' && !streq(s->w, ".")) - fprint(1, "%s\n", path); - return; - } - s = s->n; - } while (s != NULL); - fprint(2, "couldn't cd to %s\n", *av); - set(FALSE); - } + List *s, nil; + char *path = NULL; + size_t t, pathlen = 0; + if (*++av == NULL) { + s = varlookup("home"); + *av = (s == NULL) ? "/" : s->w; + } else if (av[1] != NULL) { + arg_count("cd"); + return; + } + if (isabsolute(*av) || streq(*av, ".") || streq(*av, "..")) { /* absolute pathname? */ + if (chdir(*av) < 0) { + set(FALSE); + uerror(*av); + } else + set(TRUE); + } else { + s = varlookup("cdpath"); + if (s == NULL) { + s = &nil; + nil.w = ""; + nil.n = NULL; + } + do { + if (s != &nil && *s->w != '\0') { + t = strlen(*av) + strlen(s->w) + 2; + if (t > pathlen) + path = nalloc(pathlen = t); + strcpy(path, s->w); + if (!streq(s->w, "/")) /* "//" is special to POSIX */ + strcat(path, "/"); + strcat(path, *av); + } else { + pathlen = 0; + path = *av; + } + if (chdir(path) >= 0) { + set(TRUE); + if (interactive && *s->w != '\0' && !streq(s->w, ".")) + fprint(1, "%s\n", path); + return; + } + s = s->n; + } while (s != NULL); + fprint(2, "couldn't cd to %s\n", *av); + set(FALSE); + } } static void b_umask(char **av) { - int i; - if (*++av == NULL) { - set(TRUE); - i = umask(0); - umask(i); - fprint(1, "0%o\n", i); - } else if (av[1] == NULL) { - i = o2u(*av); - if ((unsigned int) i > 0777) { - fprint(2, "bad umask\n"); - set(FALSE); - } else { - umask(i); - set(TRUE); - } - } else { - arg_count("umask"); - return; - } + int i; + if (*++av == NULL) { + set(TRUE); + i = umask(0); + umask(i); + fprint(1, "0%o\n", i); + } else if (av[1] == NULL) { + i = o2u(*av); + if ((unsigned int) i > 0777) { + fprint(2, "bad umask\n"); + set(FALSE); + } else { + umask(i); + set(TRUE); + } + } else { + arg_count("umask"); + return; + } } static void b_exit(char **av) { @@ -199,6 +200,74 @@ static void b_exit(char **av) { rc_exit(getstatus()); } +static void b_flag(char **av) { + bool *flagp = NULL; + char f; + int mode = 3; /* 0 = reset (-), 1 = set (+), 2 = test */ + const char *usage = "usage: flag f [ + | - ]\n"; + + if (*++av == NULL) { + fprint(2, RC "not enough arguments to flag\n"); + set(FALSE); + return; + } + f = av[0][0]; + if (f == '\0' || av[0][1] != '\0') goto flag_usage; + if (*++av == NULL) { + mode = 2; + } else if (av[0][0] == '+' && av[0][1] == '\0') { + mode = 1; + } else if (av[0][0] == '-' && av[0][1] == '\0') { + mode = 0; + } + if (mode == 3) goto flag_usage; + switch (f) { + case 'c': + if (mode != 2) goto flag_immutable; + set(dashsee[0] != NULL); + return; + case 'd': + if (mode != 2) goto flag_immutable; + flagp = &dashdee; break; + case 'e': flagp = &dashee; break; + case 'i': flagp = &interactive; break; + case 'l': + if (mode != 2) goto flag_immutable; + flagp = &dashell; break; + case 'n': flagp = &dashen; break; + case 'o': + if (mode != 2) goto flag_immutable; + flagp = &dashoh; break; + case 'p': + if (mode != 2) goto flag_immutable; + flagp = &dashpee; break; + case 's': + if (mode != 2) goto flag_immutable; + flagp = &dashess; break; + case 'v': flagp = &dashvee; break; + case 'x': flagp = &dashex; break; + } + if (flagp != NULL) { + if (mode == 2) + set(*flagp); + else { + *flagp = mode; + set(TRUE); + } + } else { + fprint(2, RC "unknown flag"); + set(FALSE); + } + return; +flag_immutable: + fprint(2, RC "flag immutable\n"); + set(FALSE); + return; +flag_usage: + fprint(2, usage); + set(FALSE); +} + /* raise a "return" exception, i.e., return from a function. if an integer argument is present, set $status to it */ static void b_return(char **av) { diff --git a/main.c b/main.c @@ -6,17 +6,19 @@ #include "input.h" -bool dashdee, dashee, dashvee, dashex, dasheye, - dashen, dashpee, interactive; +bool dashdee, dashee, dasheye, dashell, dashen; +bool dashpee, dashoh, dashess, dashvee, dashex; +bool interactive; +static bool dashEYE; +char *dashsee[2]; pid_t rc_pid; -static bool dashEYE, dashell, dashoh, dashess; static void assigndefault(char *,...); static void checkfd(int, enum redirtype); extern int main(int argc, char *argv[], char *envp[]) { - char *dashsee[2], *dollarzero, *null[1]; + char *dollarzero, *null[1]; int c; initprint(); dashsee[0] = dashsee[1] = NULL; @@ -67,8 +69,10 @@ extern int main(int argc, char *argv[], char *envp[]) { } quitopts: argv += rc_optind; - /* use isatty() iff neither -i nor -I is set, and iff the input is not from a script or -c flags */ - if (!dasheye && !dashEYE && dashsee[0] == NULL && (dashess || *argv == NULL)) + /* use isatty() iff neither -i nor -I is set, and iff the input is not + * from a script or -c flags */ + if (!dasheye && !dashEYE && dashsee[0] == NULL && + (dashess || *argv == NULL)) interactive = isatty(0); if (!dashoh) { checkfd(0, rFrom); diff --git a/rc.1 b/rc.1 @@ -1717,6 +1717,36 @@ If no argument is given, the current value of .Cr $status is used. .TP +\fBflag\fR \fIf\fR [ \fB+\fR | \fB\-\fR ] +Test, set (\fB+\fR), or reset (\fB\-\fR) command-line flag +.IR f . +For example, a script that requires ``exit if command fails'' semantics can say +.Ds +.Cr "flag e +" +.De +.TP +\& +Some flags cannot be set or reset using +.BR flag , +but they can still be tested. These are +.BR c , +.BR d , +.BR l , +.BR o , +.BR p , +and +.BR s . +As a special case, +.Cr "flag i" +operates on +.BR rc 's +internal +.IR interactive +flag, which may have been set by +.Cr -i +on the command line, or if standard input was a terminal; there is no +.Cr "flag I" . +.TP \fBlimit \fR[\fB\-h\fR] [\fIresource \fR[\fIvalue\fR]] Similar to the .IR csh (1) diff --git a/rc.h b/rc.h @@ -170,7 +170,10 @@ enum { /* main.c */ extern Rq *redirq; -extern bool dashdee, dashee, dashvee, dashex, dasheye, dashen, dashpee, interactive; +extern bool dashdee, dashee, dasheye, dashell, dashen; +extern bool dashpee, dashoh, dashess, dashvee, dashex; +extern bool interactive; +extern char *dashsee[]; extern pid_t rc_pid; extern int lineno; diff --git a/trip.rc b/trip.rc @@ -707,3 +707,11 @@ if (~ $status 0) { # exercise "if not" submatch 'if (false) echo foo; if not echo bar' 'bar' 'if not 1' submatch 'if (false) echo foo; echo qux; if not echo bar' 'rc: `if not'' must follow `if''' 'if not 2' + +# exercise flag builtin +submatch 'flag' 'rc: not enough arguments to flag' 'flag no args' +submatch 'flag a b c' 'usage: flag f [ + | - ]' 'flag 3 args' +submatch 'flag xx' 'usage: flag f [ + | - ]' 'flag wrong first arg' +submatch 'flag x x' 'usage: flag f [ + | - ]' 'flag wrong second arg' +submatch 'flag c && echo yes' yes 'flag c' +submatch 'flag x +; flag x -' 'flag x -' 'setting x flag'