]> Trent Huber's Code - thus.git/commitdiff
Clean up builtins
authorTrent Huber <trentmhuber@gmail.com>
Wed, 20 Aug 2025 02:44:33 +0000 (22:44 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Wed, 20 Aug 2025 02:44:33 +0000 (22:44 -0400)
16 files changed:
README.md
bin/.gitignore
src/build.c
src/builtin/README.md
src/builtin/alias.c
src/builtin/bg.c
src/builtin/builtin.c
src/builtin/builtin.h
src/builtin/cd.c
src/builtin/fg.c
src/builtin/set.c
src/builtin/unset.c
src/builtin/which.c
src/main.c
src/options.c
src/utils.c

index c36531cd2dd14c04634c5ddd838f47aacedc23c9..09408b07d50183de75ae1309d65bfdf1224f7570 100644 (file)
--- 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)
index 229a8d26e7db7729b3391cfc0c7178f2e771312f..720ae2aacc401d257590497df960fdcac08c4d02 100644 (file)
@@ -1 +1 @@
-ash
+thus
index c09161e248c85b1b0c2c2adaa52bda90432fa457..8e7627442d65127ff37729f5b4c9efb3b46b8521 100644 (file)
@@ -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},
index ea1db20714f5ecd7c43c9a5222e74b423b60b63a..65108f77e37f0d0ed79ab76125a18d04bdb12505 100644 (file)
@@ -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
index 6ceb185010a43a43d449ca9afaa804771adecc75..a4c5d6d4e35bde6c86123e6aefaee95fc6b3b00d 100644 (file)
@@ -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;
index 4ab87b47e53392bca2a932dec736a13e96e8934c..55232ec6fd7ff339e1efcad973fbbff077217e42 100644 (file)
@@ -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;
index 08904e023c272e883553b409a9c6be186ddb8391..7a2831c3199a0ce6cf624427fa8425068944bb60 100644 (file)
@@ -1,3 +1,5 @@
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #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;
+}
index bb197f867edee68aea05a92b576d71828af6775b..004f02f2dfeb42101e61da7f39b46950cec5549d 100644 (file)
@@ -1,3 +1,4 @@
 #define BUILTIN(name) int name(int argc, char **argv)
 
 int isbuiltin(char **args);
+int usage(char *program, char *options);
index ecd67cabfd04db1e901d82a1f9f4bb543848a7fe..5c04eac25e8358ba0090b4a430269dba589ae4e5 100644 (file)
@@ -1,27 +1,40 @@
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #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;
    }
 
index 0ff7935578f289ddc014724fae2ddf3c2e5e21e4..1335957a1e53442cc769477decdd872596d6dd23 100644 (file)
@@ -5,7 +5,6 @@
 #include <sys/errno.h>
 #include <termios.h>
 #include <unistd.h>
-#include <string.h> // 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;
index 5eca3c683adc90d998310d7aaf43f4b573c00e95..df28cc8f209ab8b6569cf60d6eabfe153b279937 100644 (file)
@@ -1,18 +1,10 @@
-#include <stdio.h>
 #include <stdlib.h>
 
 #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]);
index fbdccf4e3bee41640dbbe926b5ba0aa7bf8bf8bc..28dfbcd87502e71d2df402192849a0519e25e2f2 100644 (file)
@@ -1,14 +1,10 @@
-#include <stdio.h>
 #include <stdlib.h>
 
 #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");
index 5d05e93b51db799a7e6295f06d15783c669458b3..6b3d6a58f7e3d3a3d632c04061202cc7ae6c361f 100644 (file)
@@ -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]);
index 9b434d30499698ab286339d5305c61ac8cc5fea0..d7a8ffc80b2413700661f84457a0402b3cdfb8a5 100644 (file)
@@ -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));
 
index 73de4e48fa9482ddc262fcc28793da799e6afc1b..58df464d4c87383e4fa07f1e2eda14f678610845 100644 (file)
@@ -2,23 +2,21 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#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"
-          "    <file> ...      Run script\n"
-          "    -c <string> ... 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"
+                   "    <file> [arg ...]      Run script with args\n"
+                   "    -c <string> [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;
    }
index 649b1f4ca871d3131cef4c3fb2f137bb206b103c..e0ff12acd198257612f00e0f08d3ce5da5ceb864 100644 (file)
@@ -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);