From: Trent Huber Date: Wed, 27 Aug 2025 08:40:05 +0000 (-0400) Subject: Finish README.md, finish cleaning X-Git-Url: https://trenthuber.com/code?a=commitdiff_plain;h=3b3daf772ff478bf0887331f51d107b61f8db29d;p=thus.git Finish README.md, finish cleaning --- diff --git a/README.md b/README.md index 09408b0..c353907 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,64 @@ # thus -While Unix shells are a solved problem in computer science, recent iterations have admittedly been more and more complicated by feature sets that attempt to cater to all users. Ash seeks to be a completely stripped down, simplified approach to shells, only including the essential parts, i.e., just the things *I personally use*. In fact, the main goals for this project in order or priority have been: +thus is a custom UNIX shell built entirely from scratch for POSIX platforms. -1. To learn more about the interaction between shells, terminals, and the operating system. -2. To create a utility I would personally want to use. -3. To create a utility other people would want to use. +## Features -## Feature Set - -- Foreground and background process groups (`fg`, `bg`, `&`) -- Unix pipes +- Job control (`fg`, `bg`, `&`, `^Z`) +- Pipelines - Conditional execution (`&&`, `||`) -- Shell history (cached in `~/.thushistory`) -- File redirection -- Environment variables -- File globbing +- File redirection (`&1`, etc.) +- Globbing (`*`, `?`, `[...]`) +- Quoting with escape sequences (`"\r...\n"`) +- Environment variables (`set`, `unset`, `$VAR$`, etc.) +- Aliasing (`alias`, `unalias`) +- Configuration files (`~.thuslogin`, `~.thusrc`) +- Cached history (`~.thushistory`) +- [Automated integration of built-in commands](src/builtins/README.md) ## Building -Similar to my other projects, thus uses [cbs](https://github.com/trenthuber/cbs) as its build system, included as a git submodule, so make sure to clone recursively. +thus uses [cbs](https://github.com/trenthuber/cbs/) as its build system. ```console -$ git clone --recursive https://github.com/trenthuber/thus -$ cd thus -$ cc -o build build.c -$ ./build -$ ./bin/thus +> git clone --recursive https://github.com/trenthuber/thus/ +> cd thus/ +> cc -o build build.c +> build ``` -Note, you only need to run the `cc` command the first time you build the project, as the `./build` executable will recompile itself everytime it is run. +After building, you can use `install` to add the shell to your path. The default installation prefix is `/usr/local/`, but a custom one can be passed as an argument to the utility. -## Resources +```console +> sudo ./install +``` + +After installing, you can use `uninstall` to remove the shell from the install location. + +```console +> sudo ./uninstall +``` + +## Quirks -These websites have been invaluable in the making of thus. +While thus operates for the most part like Bourne shell, there are a few places where it takes a subtly different approach. + +### Quotes + +Quoting is done with double quotes (`"..."`) and undergoes no shell substitution, similar to single quotes in Bourne shell. In place of substitution, quotes can be concatenated with surrounding tokens not separated by whitespace. + +### Environment variables and aliases + +Environment variables are referred to by tokens that begin and end with a `$`. For example, evaluating the path would look like `$PATH$`. Setting environment variables is done with the `set` built-in command, not with the `name=value` syntax. This syntax is similarly avoided when declaring aliases with the `alias` built-in command. + +### Leading and trailing slashes + +Prepending `./` to executables located in the current directory is not mandatory unless there already exists an executable in `$PATH$` with the same name that you would like to override. + +The `$HOME$`, `$PWD$`, and `$PATH$` environment variables are always initialized with trailing slashes. Therefore, whenever one of these variables or `~` is substituted in the shell, it will retain the trailing slash. + +## Resources - [TTY Demystified](http://www.linusakesson.net/programming/tty/) - [Process Groups and Terminal Signaling](https://cs162.org/static/readings/ic221_s16_lec17.html) +- [Terminal Input Sequences](https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_input_sequences) diff --git a/external/cbsext.c b/external/cbsext.c deleted file mode 100644 index e4657e8..0000000 --- a/external/cbsext.c +++ /dev/null @@ -1,30 +0,0 @@ -struct cbsfile { - char *name, **flags, type; -}; - -void buildfiles(struct cbsfile *files) { - char **c, **l, **names; - struct cbsfile *target; - size_t i; - - c = cflags; - l = lflags; - - target = files++; - - for (i = 0; files[i].name; ++i) if (files[i].flags) { - cflags = files[i].flags; - compile(files[i].name); - } - - names = allocate((i + 1) * sizeof *names); - for (i = 0; files[i].name; ++i) names[i] = files[i].name; - - lflags = target->flags; - load(target->type, target->name, names); - - free(names); - - cflags = c; - lflags = l; -} diff --git a/src/build.c b/src/build.c index 07a1eac..177e59c 100644 --- a/src/build.c +++ b/src/build.c @@ -1,27 +1,20 @@ #include "../external/cbs/cbs.c" -#include "../external/cbsext.c" -#define BUILTINS LIST("-Ibuiltin/") +#define SRC1 "context", "history", "input" +#define SRC2 "main", "options", "parse", "run", "utils" int main(void) { - build("./"); - - build("builtin/"); + char **src; - buildfiles((struct cbsfile []){{"../bin/thus", NONE, 'x'}, + build("./"); - {"context", NONE}, - {"history", NONE}, - {"input", NONE}, - {"main", BUILTINS}, - {"options", BUILTINS}, - {"parse", BUILTINS}, - {"run", BUILTINS}, - {"utils", BUILTINS}, + build("builtins/"); - {"builtin.a"}, + for (src = LIST(SRC1); *src; ++src) compile(*src); + cflags = LIST("-Ibuiltins/"); + for (src = LIST(SRC2); *src; ++src) compile(*src); - {NULL}}); + load('x', "../bin/thus", LIST(SRC1, SRC2, "builtins.a")); return EXIT_SUCCESS; } diff --git a/src/builtin/README.md b/src/builtin/README.md deleted file mode 100644 index 65108f7..0000000 --- a/src/builtin/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Builtins - -## TODO - - Documentation on how to add builtins of your own - - unalias builtin diff --git a/src/builtin/alias.h b/src/builtin/alias.h deleted file mode 100644 index 52a3725..0000000 --- a/src/builtin/alias.h +++ /dev/null @@ -1,2 +0,0 @@ -char *getaliasrhs(char *token); -char **getalias(char *token); diff --git a/src/builtin/build.c b/src/builtin/build.c deleted file mode 100644 index 36f41e2..0000000 --- a/src/builtin/build.c +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../../external/cbs/cbs.c" -#include "../../external/cbsext.c" - -#define MAXBUILTINS 50 - -int main(void) { - int listfd, l; - DIR *dir; - size_t i, offset; - - /* The three extra files are: - * 1) ../libbuiltin.a (target file) - * 2) list.c (generated by this code) - * 3) builtin.c (not a builtin, just the API) - * - * The remaining files all correspond to shell builtins. */ - struct cbsfile files[3 + MAXBUILTINS + 1]; - - struct dirent *entry; - char *name, *identifier; - - build("./"); - - if ((listfd = open("list.c", O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) - err(EXIT_FAILURE, "Unable to open/create `list.c'"); - if (!(dir = opendir("./"))) - err(EXIT_FAILURE, "Unable to open current directory"); - - dprintf(listfd, "#include \n\n#include \"builtin.h\"\n" - "#include \"list.h\"\n\n"); - - errno = i = 0; - files[i++] = (struct cbsfile){"../builtin", NONE, 's'}; - files[i++] = (struct cbsfile){"list", NONE}; - offset = i; - while ((entry = readdir(dir))) { - if (strcmp(entry->d_name, "build.c") == 0) continue; - if (!(name = strrchr(entry->d_name, '.')) || strcmp(name, ".c") != 0) - continue; - if (i == 3 + MAXBUILTINS + 1) - errx(EXIT_FAILURE, "Unable to add built-in `%s', maximum reached (%d)", - name, MAXBUILTINS); - if (!(name = strdup(entry->d_name))) - err(EXIT_FAILURE, "Unable to duplicate directory entry"); - name[strlen(name) - strlen(".c")] = '\0'; - if (strcmp(name, "list") == 0) continue; - if (strcmp(name, "builtin") != 0) - dprintf(listfd, "extern BUILTIN(%s);\n", name); - files[i++] = (struct cbsfile){name, LIST("-I../")}; - } - if (errno) err(EXIT_FAILURE, "Unable to read from current directory"); - files[i] = (struct cbsfile){NULL}; - - identifier = "struct builtin builtins[] = {"; - l = (int)strlen(identifier); - dprintf(listfd, "\n%s", identifier); - for (i = offset; (name = files[i].name); ++i) - if (strcmp(name, "builtin") != 0 && strcmp(name, "list") != 0) - dprintf(listfd, "{\"%s\", %s},\n%*s", name, name, l, ""); - dprintf(listfd, "{NULL}};"); - - if (closedir(dir) == -1) - err(EXIT_FAILURE, "Unable to close current directory"); - if (close(listfd) == -1) err(EXIT_FAILURE, "Unable to close `list.c'"); - - buildfiles(files); - - while (files[offset].name) free(files[offset++].name); - - return EXIT_SUCCESS; -} diff --git a/src/builtin/pwd.c b/src/builtin/pwd.c deleted file mode 100644 index c9e6e2d..0000000 --- a/src/builtin/pwd.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include -#include - -#include "builtin.h" -#include "utils.h" - -BUILTIN(pwd) { - char *cwd, buffer[PATH_MAX]; - - if (argc != 1) return usage(argv[0], NULL); - - if (!(cwd = getenv("PWD")) && !(cwd = getcwd(buffer, PATH_MAX))) - fatal("Unable to get current working directory"); - - puts(cwd); - - return EXIT_SUCCESS; -} diff --git a/src/builtin/.gitignore b/src/builtins/.gitignore similarity index 100% rename from src/builtin/.gitignore rename to src/builtins/.gitignore diff --git a/src/builtins/README.md b/src/builtins/README.md new file mode 100644 index 0000000..3c1954b --- /dev/null +++ b/src/builtins/README.md @@ -0,0 +1,22 @@ +# Adding a built-in shell command + +To add a built-in command to the shell, first make a source file in the `src/builtins/` directory with the same name as the built-in. The source file should contain at least the following code (where "foo" is the name of the built-in). + +```c +// foo.c + +#include + +#include "builtin.h" +#include "utils.h" + +BUILTIN(foo) { + return EXIT_SUCCESS; +} +``` + +The `BUILTIN()` macro is defined in [`builtin.h`](builtin.h#L1) and provides an interface similar to that of `main()`, passing the arguments from the user as an array of C strings (`argv`) along with a count (`argc`). This allows you to write code for built-ins exactly as you would write them in a regular C program. + +Errors should be reported to the user using the `note()` function defined in [`utils.c`](../utils.c#L20). + +Once the source is done being written, simply rebuild the shell and it will automatically incorporate the new built-in. diff --git a/src/builtin/alias.c b/src/builtins/alias.c similarity index 88% rename from src/builtin/alias.c rename to src/builtins/alias.c index a208a86..e798f77 100644 --- a/src/builtin/alias.c +++ b/src/builtins/alias.c @@ -4,7 +4,6 @@ #include "builtin.h" #include "context.h" -#include "input.h" #include "parse.h" #include "utils.h" @@ -17,7 +16,7 @@ static struct { size_t size; } aliases; -char *getaliasrhs(char *token) { +char *getrawalias(char *token) { size_t i; for (i = 0; i < aliases.size; ++i) @@ -31,8 +30,9 @@ char **getalias(char *token) { size_t l; static struct context context; - if (!(rhs = getaliasrhs(token))) return NULL; + if (!(rhs = getrawalias(token))) return NULL; + while (*rhs == ' ') ++rhs; strcpy(context.buffer, rhs); l = strlen(rhs); context.buffer[l + 1] = '\0'; @@ -41,6 +41,7 @@ char **getalias(char *token) { context.b = context.buffer; if (!parse(&context)) return NULL; + if (!context.t) *context.tokens = NULL; return context.tokens; } @@ -59,10 +60,6 @@ BUILTIN(alias) { note("Unable to add alias `%s', maximum reached (%d)", argv[1], MAXALIAS); return EXIT_FAILURE; } - if (!*argv[2]) { - note("Cannot add empty alias"); - return EXIT_FAILURE; - } for (i = 0; i < aliases.size; ++i) if (strcmp(aliases.entries[i].lhs, argv[1]) == 0) break; diff --git a/src/builtins/alias.h b/src/builtins/alias.h new file mode 100644 index 0000000..fc6edcb --- /dev/null +++ b/src/builtins/alias.h @@ -0,0 +1,2 @@ +char *getrawalias(char *token); +char **getalias(char *token); diff --git a/src/builtin/bg.c b/src/builtins/bg.c similarity index 97% rename from src/builtin/bg.c rename to src/builtins/bg.c index b8503a3..6c50e1c 100644 --- a/src/builtin/bg.c +++ b/src/builtins/bg.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -19,7 +18,7 @@ struct bglink { }; static struct { - struct bglink entries[MAXBG + 1], *active, *free; + struct bglink entries[MAXBG], *active, *free; } bgjobs; void initbg(void) { @@ -49,6 +48,7 @@ int pushbg(struct bgjob job) { int peekbg(struct bgjob *job) { if (bgjobs.active && job) *job = bgjobs.active->job; + return bgjobs.active != NULL; } @@ -88,12 +88,11 @@ void waitbg(int sig) { e = errno; p = bgjobs.active; while (p) { - while ((id = waitpid(-p->job.id, &s, WNOHANG | WUNTRACED)) > 0) { + while ((id = waitpid(-p->job.id, &s, WNOHANG | WUNTRACED)) > 0) if (WIFSTOPPED(s)) { p->job.suspended = 1; break; } - } if (id == -1) { id = p->job.id; p = p->next; diff --git a/src/builtin/bg.h b/src/builtins/bg.h similarity index 100% rename from src/builtin/bg.h rename to src/builtins/bg.h diff --git a/src/builtins/build.c b/src/builtins/build.c new file mode 100644 index 0000000..24eae10 --- /dev/null +++ b/src/builtins/build.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#include "../../external/cbs/cbs.c" + +#define MAXBUILTINS 50 + +int main(void) { + int listfd, l; + DIR *dir; + char *src[MAXBUILTINS + 2 + 1], **p; + struct dirent *entry; + + build("./"); + + if ((listfd = open("list.c", O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) + err(EXIT_FAILURE, "Unable to open/create `list.c'"); + if (!(dir = opendir("./"))) + err(EXIT_FAILURE, "Unable to open current directory"); + + dprintf(listfd, "#include \n\n#include \"builtin.h\"\n" + "#include \"list.h\"\n\n"); + + p = src; + errno = 0; + while ((entry = readdir(dir))) { + if (strcmp(entry->d_name, "build.c") == 0 + || !(*p = strrchr(entry->d_name, '.')) || strcmp(*p, ".c") != 0) + continue; + if (!(*p = strdup(entry->d_name))) + err(EXIT_FAILURE, "Unable to duplicate directory entry"); + (*p)[strlen(*p) - 2] = '\0'; + if (p - src == 2 + MAXBUILTINS + 1) + errx(EXIT_FAILURE, "Unable to add %s built-in, maximum reached (%d)", + *p, MAXBUILTINS); + if (strcmp(*p, "builtin") != 0 && strcmp(*p, "list") != 0) + dprintf(listfd, "extern BUILTIN(%s);\n", *p); + ++p; + } + if (errno) err(EXIT_FAILURE, "Unable to read from current directory"); + + *p = "struct builtin builtins[] = {"; + l = (int)strlen(*p); + dprintf(listfd, "\n%s", *p); + *p = NULL; + for (p = src; *p; ++p) + if (strcmp(*p, "builtin") != 0 && strcmp(*p, "list") != 0) + dprintf(listfd, "{\"%s\", %s},\n%*s", *p, *p, l, ""); + dprintf(listfd, "{NULL}};\n"); + + if (closedir(dir) == -1) + err(EXIT_FAILURE, "Unable to close current directory"); + if (close(listfd) == -1) err(EXIT_FAILURE, "Unable to close `list.c'"); + + cflags = LIST("-I../"); + for (p = src; *p; ++p) compile(*p); + load('s', "../builtins", src); + + for (p = src; *p; ++p) free(*p); + + return EXIT_SUCCESS; +} diff --git a/src/builtin/builtin.c b/src/builtins/builtin.c similarity index 63% rename from src/builtin/builtin.c rename to src/builtins/builtin.c index 52ea765..ccb7832 100644 --- a/src/builtin/builtin.c +++ b/src/builtins/builtin.c @@ -7,13 +7,13 @@ #include "utils.h" int isbuiltin(char **args) { - struct builtin *builtinp; + struct builtin *builtin; size_t n; - for (builtinp = builtins; builtinp->func; ++builtinp) - if (strcmp(args[0], builtinp->name) == 0) { + for (builtin = builtins; builtin->func; ++builtin) + if (strcmp(args[0], builtin->name) == 0) { for (n = 1; args[n]; ++n); - status = builtinp->func(n, args); + status = builtin->func(n, args); return 1; } @@ -21,7 +21,7 @@ int isbuiltin(char **args) { } int usage(char *program, char *options) { - fprintf(stderr, "Usage: %s", program); + fprintf(stderr, "usage: %s", program); if (options) fprintf(stderr, " %s", options); fputc('\n', stderr); diff --git a/src/builtin/builtin.h b/src/builtins/builtin.h similarity index 100% rename from src/builtin/builtin.h rename to src/builtins/builtin.h diff --git a/src/builtin/cd.c b/src/builtins/cd.c similarity index 97% rename from src/builtin/cd.c rename to src/builtins/cd.c index 5c04eac..023c9db 100644 --- a/src/builtin/cd.c +++ b/src/builtins/cd.c @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/src/builtin/fg.c b/src/builtins/fg.c similarity index 98% rename from src/builtin/fg.c rename to src/builtins/fg.c index 15fb381..a868bf4 100644 --- a/src/builtin/fg.c +++ b/src/builtins/fg.c @@ -9,8 +9,6 @@ #include "bg.h" #include "builtin.h" -#include "context.h" -#include "input.h" #include "utils.h" static struct { diff --git a/src/builtin/fg.h b/src/builtins/fg.h similarity index 100% rename from src/builtin/fg.h rename to src/builtins/fg.h diff --git a/src/builtin/list.h b/src/builtins/list.h similarity index 100% rename from src/builtin/list.h rename to src/builtins/list.h diff --git a/src/builtins/pwd.c b/src/builtins/pwd.c new file mode 100644 index 0000000..d16c3f7 --- /dev/null +++ b/src/builtins/pwd.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +#include "builtin.h" +#include "utils.h" + +BUILTIN(pwd) { + char *cwd, buffer[PATH_MAX]; + size_t l; + + if (argc != 1) return usage(argv[0], NULL); + + if (!(cwd = getenv("PWD"))) { + if (!(cwd = getcwd(buffer, PATH_MAX))) { + note("Unable to get current working directory"); + return EXIT_FAILURE; + } + l = strlen(cwd); + if (cwd[l - 1] != '/') { + cwd[l] = '/'; + cwd[l + 1] = '\0'; + } + if (setenv("PWD", cwd, 1) == -1) { + note("Unable to set $PWD$"); + return EXIT_FAILURE; + } + } + + puts(cwd); + + return EXIT_SUCCESS; +} diff --git a/src/builtin/set.c b/src/builtins/set.c similarity index 100% rename from src/builtin/set.c rename to src/builtins/set.c diff --git a/src/builtin/source.c b/src/builtins/source.c similarity index 95% rename from src/builtin/source.c rename to src/builtins/source.c index 51001b0..676c414 100644 --- a/src/builtin/source.c +++ b/src/builtins/source.c @@ -1,13 +1,11 @@ #include #include -#include #include #include #include "builtin.h" #include "context.h" #include "input.h" -#include "parse.h" #include "run.h" #include "utils.h" diff --git a/src/builtin/source.h b/src/builtins/source.h similarity index 100% rename from src/builtin/source.h rename to src/builtins/source.h diff --git a/src/builtin/unset.c b/src/builtins/unset.c similarity index 100% rename from src/builtin/unset.c rename to src/builtins/unset.c diff --git a/src/builtin/which.c b/src/builtins/which.c similarity index 86% rename from src/builtin/which.c rename to src/builtins/which.c index 2cde228..493c75f 100644 --- a/src/builtin/which.c +++ b/src/builtins/which.c @@ -11,11 +11,13 @@ #include "utils.h" enum { + ALIAS, BUILTIN, PATH, - ALIAS, }; +static int type; + static int exists(char *path) { struct stat pstat; mode_t mask; @@ -24,26 +26,27 @@ static int exists(char *path) { mask = S_IFREG | S_IXUSR; if ((pstat.st_mode & mask) == mask) return 1; } else if (errno != ENOENT) note("Unable to check if `%s' exists", path); + else errno = 0; return 0; } -static char *getpathtype(char *file, int *type) { +char *getpath(char *file) { char *slash, *entry, *end, dir[PATH_MAX]; struct builtin *builtin; size_t l; static char path[PATH_MAX]; - *type = PATH; + type = PATH; if (!(slash = strchr(file, '/'))) { - if ((entry = getaliasrhs(file))) { - *type = ALIAS; + if ((entry = getrawalias(file))) { + type = ALIAS; return entry; } for (builtin = builtins; builtin->func; ++builtin) if (strcmp(file, builtin->name) == 0) { - *type = BUILTIN; + type = BUILTIN; return file; } @@ -70,19 +73,12 @@ static char *getpathtype(char *file, int *type) { return NULL; } -char *getpath(char *file) { - int type; - - return getpathtype(file, &type); -} - BUILTIN(which) { - int type; char *result; if (argc != 2) return usage(argv[0], "name"); - if (!(result = getpathtype(argv[1], &type))) { + if (!(result = getpath(argv[1]))) { printf("%s not found\n", argv[1]); return EXIT_SUCCESS; } diff --git a/src/builtin/which.h b/src/builtins/which.h similarity index 100% rename from src/builtin/which.h rename to src/builtins/which.h diff --git a/src/context.c b/src/context.c index 35f9b61..a3f7655 100644 --- a/src/context.c +++ b/src/context.c @@ -1,4 +1,4 @@ -#include +#include #include "context.h" #include "input.h" diff --git a/src/history.c b/src/history.c index b7a1b36..899eaf9 100644 --- a/src/history.c +++ b/src/history.c @@ -6,13 +6,13 @@ #include #include "context.h" -#include "input.h" +#include "options.h" #include "utils.h" #define MAXHIST 100 -#define INC(v) (history.v = (history.v + 1) % (MAXHIST + 1)) -#define DEC(v) (history.v = (history.v + MAXHIST) % (MAXHIST + 1)) +#define INC(x) (history.x = (history.x + 1) % (MAXHIST + 1)) +#define DEC(x) (history.x = (history.x + MAXHIST) % (MAXHIST + 1)) static struct { char path[PATH_MAX], entries[MAXHIST + 1][MAXCHARS + 1]; @@ -22,6 +22,8 @@ static struct { void inithistory(void) { FILE *file; + if (!interactive) return; + if (!catpath(home, ".thushistory", history.path)) exit(EXIT_FAILURE); if (!(file = fopen(history.path, "r"))) { if (errno == ENOENT) return; @@ -58,6 +60,8 @@ void deinithistory(void) { int fd; FILE *file; + if (!interactive) return; + if ((fd = open(history.path, O_WRONLY | O_CREAT | O_TRUNC, 0600)) == -1) { note("Unable to open history file for writing"); return; diff --git a/src/input.c b/src/input.c index 9e6622f..b49a390 100644 --- a/src/input.c +++ b/src/input.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -9,7 +8,6 @@ #include "context.h" #include "history.h" -#include "input.h" #include "utils.h" enum { @@ -130,8 +128,7 @@ int userinput(struct context *c) { /* This is a very minimal way to handle arrow keys. All modifiers except for * the ALT key are processed but ignored. * - * Reference: - * https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_input_sequences */ + * See "Terminal Input Sequences" reference in `README.md'. */ case ESCAPE: if ((current = getchar()) == '[') { while ((current = getchar()) >= '0' && current <= '9'); diff --git a/src/libbuiltins.a b/src/libbuiltins.a new file mode 100644 index 0000000..7e53159 Binary files /dev/null and b/src/libbuiltins.a differ diff --git a/src/options.c b/src/options.c index 58df464..611d916 100644 --- a/src/options.c +++ b/src/options.c @@ -1,5 +1,5 @@ -#include #include +#include #include #include "builtin.h" @@ -10,19 +10,23 @@ int login, interactive; void options(struct context *context) { - int opt, l; - char *message = "[file | -c string] [arg ...] [-hl]\n" - " [arg ...] Run script with args\n" - " -c [arg ...] Run string with args\n" - " -h Show this help message\n" - " -l Run as a login shell\n"; + int opt; + char *p, *message = "[file | -c string] [arg ...] [-hl]\n" + " [arg ...] Run script\n" + " -c [arg ...] Run string\n" + " -h Show this help message\n" + " -l Run as a login shell"; - - login = **arglist == '-'; + opt = 0; + if (*arglist[0] == '-') { + ++arglist[0]; + login = 1; + } + if ((p = strrchr(arglist[0], '/'))) arglist[0] = p + 1; interactive = 1; context->input = userinput; - while ((opt = getopt(argcount, arglist, ":c:hl")) != -1) { + while (opt != 'c' && (opt = getopt(argcount, arglist, ":c:hl")) != -1) switch (opt) { case 'c': interactive = 0; @@ -44,8 +48,6 @@ void options(struct context *context) { note("Unknown command line option `-%c'\n", optopt); exit(usage(arglist[0], message)); } - if (opt == 'c') break; - } if (!context->string && arglist[optind]) { interactive = 0; context->script = arglist[optind]; diff --git a/src/parse.c b/src/parse.c index 65e1308..b81a33e 100644 --- a/src/parse.c +++ b/src/parse.c @@ -7,7 +7,6 @@ #include "alias.h" #include "context.h" -#include "options.h" #include "utils.h" int parse(struct context *c) { diff --git a/src/run.c b/src/run.c index a367511..105c0f8 100644 --- a/src/run.c +++ b/src/run.c @@ -1,17 +1,14 @@ #include -#include #include #include -#include #include #include #include #include -#include // XXX +#include "bg.h" #include "builtin.h" #include "context.h" -#include "bg.h" #include "fg.h" #include "parse.h" #include "utils.h" @@ -61,8 +58,6 @@ static void redirectfiles(struct redirect *r) { } static void exec(char *path, struct context *c) { - char cwd[PATH_MAX]; - redirectfiles(c->redirects); if (isbuiltin(c->tokens)) exit(status); @@ -82,8 +77,7 @@ int run(struct context *c) { islist = c->prev.term > BG || c->current.term > BG; if (c->t) { if (c->current.term == BG && fullbg()) { - note("Unable to place job in background, too many background jobs", - c->current.name); + note("Unable to place job in background, too many background jobs"); return quit(c); } if (!(path = getpath(c->current.name))) { diff --git a/src/utils.c b/src/utils.c index 0e0c25e..ddd4661 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -9,26 +8,43 @@ #include #include "bg.h" -#include "context.h" #include "fg.h" #include "history.h" -#include "options.h" int argcount, status; char **arglist, *home; void note(char *fmt, ...) { va_list args; + + fprintf(stderr, "%s: ", arglist[0]); + va_start(args, fmt); - (errno ? vwarn : vwarnx)(fmt, args); + vfprintf(stderr, fmt, args); va_end(args); - errno = 0; + + if (errno) { + fprintf(stderr, ": %s", strerror(errno)); + errno = 0; + } + + putchar('\n'); } void fatal(char *fmt, ...) { va_list args; + + fprintf(stderr, "%s: ", arglist[0]); + va_start(args, fmt); - (errno ? verr : verrx)(EXIT_FAILURE, fmt, args); + vfprintf(stderr, fmt, args); + va_end(args); + + if (errno) fprintf(stderr, ": %s", strerror(errno)); + + putchar('\n'); + + exit(EXIT_FAILURE); } void init(void) { @@ -51,6 +67,10 @@ void init(void) { if (setenv("PWD", buffer, 1) == -1) fatal("Unable to append trailing slash to $PWD$"); + if (setenv("PATH", "/usr/local/bin/:/usr/local/sbin/" + ":/usr/bin/:/usr/sbin/:/bin/:/sbin/", 1) == -1) + fatal("Unable to initialize $PATH$"); + if (!(shlvlstr = getenv("SHLVL"))) shlvlstr = "0"; if ((shlvl = strtol(shlvlstr, NULL, 10)) < 0) shlvl = 0; sprintf(buffer, "%ld", shlvl + 1); @@ -59,7 +79,7 @@ void init(void) { initfg(); initbg(); - if (interactive) inithistory(); + inithistory(); } char *catpath(char *dir, char *filename, char *buffer) { @@ -81,7 +101,7 @@ char *catpath(char *dir, char *filename, char *buffer) { } void deinit(void) { - if (interactive) deinithistory(); + deinithistory(); deinitbg(); deinitfg(); } diff --git a/tools/install.c b/tools/install.c index 85c7394..7e653b2 100644 --- a/tools/install.c +++ b/tools/install.c @@ -8,7 +8,7 @@ int main(int argc, char **argv) { int slash; pid_t cpid; - if (argc > 2) errx(EXIT_FAILURE, "%s [prefix]", argv[0]); + if (argc > 2) errx(EXIT_FAILURE, "usage: %s [prefix]", argv[0]); path = stpcpy(define, "-DPATH=\""); if (argc == 2) { diff --git a/tools/uninstall.c b/tools/uninstall.c index da5876b..15ba8e4 100644 --- a/tools/uninstall.c +++ b/tools/uninstall.c @@ -2,9 +2,11 @@ #include "cbs.c" -int main(void) { +int main(int argc, char **argv) { pid_t cpid; + if (argc != 1) err(EXIT_FAILURE, "usage: %s\n", argv[0]); + if ((cpid = fork()) == -1) err(EXIT_FAILURE, "Unable to fork"); else if (cpid == 0) run("/bin/rm", LIST("rm", PATH, "uninstall"), "remove", PATH);