From d0b7f65e3ea7442f41bdd6bb6e8695fe02593416 Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Tue, 19 Aug 2025 22:44:33 -0400 Subject: [PATCH] Clean up builtins --- README.md | 18 +++++++----------- bin/.gitignore | 2 +- src/build.c | 4 ++-- src/builtin/README.md | 5 +---- src/builtin/alias.c | 3 +-- src/builtin/bg.c | 11 +++++++++-- src/builtin/builtin.c | 11 +++++++++-- src/builtin/builtin.h | 1 + src/builtin/cd.c | 31 ++++++++++++++++++++++--------- src/builtin/fg.c | 27 +++++++++++++-------------- src/builtin/set.c | 10 +--------- src/builtin/unset.c | 6 +----- src/builtin/which.c | 5 +---- src/main.c | 4 ++-- src/options.c | 23 +++++++++++------------ src/utils.c | 10 ++++++++-- 16 files changed, 90 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index c36531c..09408b0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Ash +# 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: @@ -11,32 +11,28 @@ While Unix shells are a solved problem in computer science, recent iterations ha - Foreground and background process groups (`fg`, `bg`, `&`) - Unix pipes - Conditional execution (`&&`, `||`) -- Shell history (cached in `~/.ashhistory`) - -### Future features - +- Shell history (cached in `~/.thushistory`) - File redirection - Environment variables -- Scripting - File globbing ## Building -Similar to my other projects, ash uses [cbs](https://github.com/trenthuber/cbs) as its build system, included as a git submodule, so make sure to clone recursively. +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. ```console -$ git clone --recursive https://github.com/trenthuber/ash -$ cd ash +$ git clone --recursive https://github.com/trenthuber/thus +$ cd thus $ cc -o build build.c $ ./build -$ ./bin/ash +$ ./bin/thus ``` 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. ## Resources -These websites have been invaluable in the making of ash. +These websites have been invaluable in the making of thus. - [TTY Demystified](http://www.linusakesson.net/programming/tty/) - [Process Groups and Terminal Signaling](https://cs162.org/static/readings/ic221_s16_lec17.html) diff --git a/bin/.gitignore b/bin/.gitignore index 229a8d2..720ae2a 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -1 +1 @@ -ash +thus diff --git a/src/build.c b/src/build.c index c09161e..8e76274 100644 --- a/src/build.c +++ b/src/build.c @@ -8,14 +8,14 @@ int main(void) { build("builtin/"); - buildfiles((struct cbsfile []){{"../bin/ash", NONE, 'x'}, + buildfiles((struct cbsfile []){{"../bin/thus", NONE, 'x'}, {"context", NONE}, {"history", NONE}, {"input", NONE}, {"job", NONE}, {"main", BUILTINS}, - {"options", NONE}, + {"options", BUILTINS}, {"parse", BUILTINS}, {"run", BUILTINS}, {"utils", BUILTINS}, diff --git a/src/builtin/README.md b/src/builtin/README.md index ea1db20..65108f7 100644 --- a/src/builtin/README.md +++ b/src/builtin/README.md @@ -2,7 +2,4 @@ ## TODO - Documentation on how to add builtins of your own - - Reworking aliasing stuff - - unset - - which needs to test for aliases - - pwd + - unalias builtin diff --git a/src/builtin/alias.c b/src/builtin/alias.c index 6ceb185..a4c5d6d 100644 --- a/src/builtin/alias.c +++ b/src/builtin/alias.c @@ -75,8 +75,7 @@ BUILTIN(alias) { break; default: - fputs("Usage: alias [lhs rhs]\r\n", stderr); - return EXIT_FAILURE; + return usage(argv[0], "[lhs rhs]"); } return EXIT_SUCCESS; diff --git a/src/builtin/bg.c b/src/builtin/bg.c index 4ab87b4..55232ec 100644 --- a/src/builtin/bg.c +++ b/src/builtin/bg.c @@ -13,7 +13,11 @@ BUILTIN(bg) { pid_t id; struct job *job; - if (argc > 1) { + switch (argc) { + case 1: + if (!(job = peeksuspendedjob())) return EXIT_FAILURE; + break; + case 2: errno = 0; if ((l = strtol(argv[1], NULL, 10)) == LONG_MAX && errno || l <= 0) { note("Invalid job id %ld", l); @@ -27,7 +31,10 @@ BUILTIN(bg) { note("Job %d already in background", id); return EXIT_FAILURE; } - } else if (!(job = peeksuspendedjob())) return EXIT_FAILURE; + break; + default: + return usage(argv[0], "[pgid]"); + } deletejobid(job->id); if (!pushjob(job)) return EXIT_FAILURE; diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index 08904e0..7a2831c 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -1,3 +1,5 @@ +#include +#include #include #include "builtin.h" @@ -9,10 +11,15 @@ int isbuiltin(char **args) { size_t n; for (builtinp = builtins; builtinp->func; ++builtinp) - if (strcmp(*args, builtinp->name) == 0) { - for (n = 0; args[n]; ++n); + if (strcmp(args[0], builtinp->name) == 0) { + for (n = 1; args[n]; ++n); status = builtinp->func(n, args); return 1; } return 0; } + +int usage(char *program, char *options) { + fprintf(stderr, "Usage: %s %s\r\n", program, options); + return EXIT_FAILURE; +} diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index bb197f8..004f02f 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -1,3 +1,4 @@ #define BUILTIN(name) int name(int argc, char **argv) int isbuiltin(char **args); +int usage(char *program, char *options); diff --git a/src/builtin/cd.c b/src/builtin/cd.c index ecd67ca..5c04eac 100644 --- a/src/builtin/cd.c +++ b/src/builtin/cd.c @@ -1,27 +1,40 @@ +#include #include #include +#include #include #include "builtin.h" #include "utils.h" BUILTIN(cd) { - char *path; + char *path, buffer[PATH_MAX + 1]; + size_t l; - if (argc > 2) { - fputs("Usage: cd [directory]\r\n", stderr); - return EXIT_FAILURE; + switch (argc) { + case 1: + path = home; + break; + case 2: + if (!realpath(argv[1], path = buffer)) { + note(argv[1]); + return EXIT_FAILURE; + } + l = strlen(path); + path[l + 1] = '\0'; + path[l] = '/'; + break; + default: + return usage(argv[0], "[directory]"); } - path = argc == 1 ? home : argv[1]; - if (chdir(path) == -1) { - note("Unable to change directory to `%s'", path); + note(path); return EXIT_FAILURE; } - if (setenv("PWD", path, 1) == -1) { // TODO: Slash-terminate path - note("Unable to change $PWD$ to `%s'", path); + if (setenv("PWD", path, 1) == -1) { + note("Unable to set $PWD$"); return EXIT_FAILURE; } diff --git a/src/builtin/fg.c b/src/builtin/fg.c index 0ff7935..1335957 100644 --- a/src/builtin/fg.c +++ b/src/builtin/fg.c @@ -5,7 +5,6 @@ #include #include #include -#include // XXX #include "builtin.h" #include "job.h" @@ -20,7 +19,6 @@ static struct termios raw; struct sigaction sigchld, sigdfl; static int setconfig(struct termios *mode) { -// printf("Setting config to %s\r\n", mode == &raw ? "raw" : "canonical"); if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) { note("Unable to set termios config"); return 0; @@ -64,7 +62,6 @@ void initfg(void) { } int setfg(struct job job) { -// puts("setfg\r"); if (!setconfig(&job.config)) return 0; if (tcsetpgrp(STDIN_FILENO, job.id) == -1) { note("Unable to bring job %d to foreground", job.id); @@ -99,13 +96,10 @@ void waitfg(struct job job) { } if (tcgetattr(STDIN_FILENO, &job.config) == -1) note("Unable to save termios config of job %d", job.id); -// puts("waitfg\r"); setconfig(&raw); -// printf("fgstatus = %d\r\n", fgstatus); if (WIFEXITED(fgpg.status)) status = WEXITSTATUS(fgpg.status); - else if (WIFSIGNALED(fgpg.status)) { puts("SIGNAL RECEIVED\r"); status = WTERMSIG(fgpg.status); } - else { + else if (WIFSTOPPED(fgpg.status)) { status = WSTOPSIG(fgpg.status); job.suspended = 1; if (!pushjob(&job)) { @@ -114,12 +108,10 @@ void waitfg(struct job job) { if (setfg(job)) return waitfg(job); note("Manual intervention required for job %d", job.id); } - } -// printf("status = %d\r\n", status); + } else status = WTERMSIG(fgpg.status); } void deinitfg(void) { -// puts("deinitfg"); setconfig(&canonical); } @@ -128,7 +120,14 @@ BUILTIN(fg) { pid_t id; struct job *job; - if (argc > 1) { + switch (argc) { + case 1: + if (!(job = pulljob())) { + note("No job to bring into the foreground"); + return EXIT_FAILURE; + } + break; + case 2: errno = 0; if ((l = strtol(argv[1], NULL, 10)) == LONG_MAX && errno || l <= 0) { note("Invalid process group id %ld", l); @@ -139,9 +138,9 @@ BUILTIN(fg) { return EXIT_FAILURE; } deletejobid(id); - } else if (!(job = pulljob())) { - note("No job to bring into the foreground"); - return EXIT_FAILURE; + break; + default: + return usage(argv[0], "[pgid]"); } if (!setfg(*job)) return EXIT_FAILURE; diff --git a/src/builtin/set.c b/src/builtin/set.c index 5eca3c6..df28cc8 100644 --- a/src/builtin/set.c +++ b/src/builtin/set.c @@ -1,18 +1,10 @@ -#include #include #include "builtin.h" #include "utils.h" -extern char **environ; - BUILTIN(set) { - char **e; - - if (argc != 3) { - fputs("Usage: set [name value]\r\n", stderr); - return EXIT_FAILURE; - } + if (argc != 3) return usage(argv[0], "name value"); if (setenv(argv[1], argv[2], 1) == -1) { note("Unable to set %s to %s", argv[1], argv[2]); diff --git a/src/builtin/unset.c b/src/builtin/unset.c index fbdccf4..28dfbcd 100644 --- a/src/builtin/unset.c +++ b/src/builtin/unset.c @@ -1,14 +1,10 @@ -#include #include #include "builtin.h" #include "utils.h" BUILTIN(unset) { - if (argc != 2) { - fputs("Usage: unset [name]\r\n", stderr); - return EXIT_FAILURE; - } + if (argc != 2) return usage(argv[0], "name"); if (!getenv(argv[1])) { note("Environment variable does not exist"); diff --git a/src/builtin/which.c b/src/builtin/which.c index 5d05e93..6b3d6a5 100644 --- a/src/builtin/which.c +++ b/src/builtin/which.c @@ -80,10 +80,7 @@ BUILTIN(which) { int type; char *result; - if (argc != 2) { - fputs("Usage: which name\r\n", stderr); - return EXIT_FAILURE; - } + if (argc != 2) return usage(argv[0], "name"); if (!(result = getpathtype(argv[1], &type))) { printf("%s not found\r\n", argv[1]); diff --git a/src/main.c b/src/main.c index 9b434d3..d7a8ffc 100644 --- a/src/main.c +++ b/src/main.c @@ -17,8 +17,8 @@ int main(int argc, char **argv) { init(); - if (login) config(".ashlogin"); - if (interactive) config(".ashrc"); + if (login) config(".thuslogin"); + if (interactive) config(".thusrc"); while (run(&context)); diff --git a/src/options.c b/src/options.c index 73de4e4..58df464 100644 --- a/src/options.c +++ b/src/options.c @@ -2,23 +2,21 @@ #include #include +#include "builtin.h" #include "context.h" #include "input.h" #include "utils.h" int login, interactive; -static void usage(char *program, int code) { - printf("Usage: %s [file] [-c string] [-hl]\n" - " ... Run script\n" - " -c ... Run commands\n" - " -h Show this help message\n" - " -l Run as a login shell\n", program); - exit(code); -} - 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"; + login = **arglist == '-'; interactive = 1; @@ -33,17 +31,18 @@ void options(struct context *context) { arglist[--optind] = ""; // Empty program name when running a string break; case 'h': - usage(*arglist, EXIT_SUCCESS); + usage(arglist[0], message); + exit(EXIT_SUCCESS); case 'l': login = 1; break; case ':': note("Expected argument following `-%c'\n", optopt); - usage(*arglist, EXIT_FAILURE); + exit(usage(arglist[0], message)); case '?': default: note("Unknown command line option `-%c'\n", optopt); - usage(*arglist, EXIT_FAILURE); + exit(usage(arglist[0], message)); } if (opt == 'c') break; } diff --git a/src/utils.c b/src/utils.c index 649b1f4..e0ff12a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -40,8 +40,7 @@ void init(void) { size_t l; long shlvl; - if (!(home = getenv("HOME"))) - fatal("Unable to query $HOME$"); + if (!(home = getenv("HOME"))) fatal("Unable to find home directory"); strcpy(buffer, home); l = strlen(buffer); buffer[l + 1] = '\0'; @@ -49,6 +48,13 @@ void init(void) { if (setenv("HOME", buffer, 1) == -1 || !(home = getenv("HOME"))) fatal("Unable to append trailing slash to $HOME$"); + if (!getcwd(buffer, PATH_MAX)) fatal("Unable to find current directory"); + l = strlen(buffer); + buffer[l + 1] = '\0'; + buffer[l] = '/'; + if (setenv("PWD", buffer, 1) == -1) + fatal("Unable to append trailing slash to $PWD$"); + if (!(shlvlstr = getenv("SHLVL"))) shlvlstr = "0"; if ((shlvl = strtol(shlvlstr, NULL, 10)) < 0) shlvl = 0; sprintf(buffer, "%ld", shlvl + 1); -- 2.51.0