]> Trent Huber's Code - thus.git/commitdiff
Tilde expansion, previous command status, altered cmd structure
authorTrent Huber <trentmhuber@gmail.com>
Sat, 26 Jul 2025 18:42:39 +0000 (14:42 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Sat, 26 Jul 2025 18:42:39 +0000 (14:42 -0400)
12 files changed:
external/cbs
src/builtin/bg.c
src/builtin/cd.c
src/builtin/fg.c
src/builtin/which.c
src/config.h
src/input.c
src/main.c
src/parse.c
src/parse.h
src/run.c
src/run.h

index 47569fbe806a5138763e7ef3447dca254357dff8..4711a83fafa0282fadeaba701a25539b3e73d073 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 47569fbe806a5138763e7ef3447dca254357dff8
+Subproject commit 4711a83fafa0282fadeaba701a25539b3e73d073
index 76c58a820ce440c44fa3b1170db9c6363a7878ba..1e090f02b0614f66b889d325c8e2b7951beef870 100644 (file)
@@ -15,7 +15,7 @@ BUILTINSIG(bg) {
 
    if (sigaction(SIGCHLD, &sigdfl, NULL) == -1) {
        note("Unable to acquire lock on the job stack");
-       return 1;
+       return EXIT_FAILURE;
    }
 
    if (argv[1]) {
@@ -23,40 +23,40 @@ BUILTINSIG(bg) {
        if ((jobid = strtol(argv[1], NULL, 10)) == LONG_MAX && errno
            || jobid <= 0) {
            note("Invalid job id");
-           return 1;
+           return EXIT_FAILURE;
        }
        if (!(job = findjob((pid_t)jobid))) {
            note("Unable to find job %d", (pid_t)jobid);
-           return 1;
+           return EXIT_FAILURE;
        }
        if (job->type == BACKGROUND) {
            note("Job %d already in background", (pid_t)jobid);
-           return 1;
+           return EXIT_FAILURE;
        }
    } else {
        for (jobs.c = MINUSONE(jobs, t); jobs.c != MINUSONE(jobs, b); DEC(jobs, c))
            if (CURRENT->type == SUSPENDED) break;
        if (jobs.c == MINUSONE(jobs, b)) {
            note("No suspended jobs to run in background");
-           return 1;
+           return EXIT_FAILURE;
        }
    }
    job = deletejob();
 
    if (!(push(&jobs, job))) {
        note("Unable to add job to background; too many jobs");
-       return 1;
+       return EXIT_FAILURE;
    }
    if (killpg(job->id, SIGCONT) == -1) {
        note("Unable to wake up suspended process group %d", job->id);
-       return 1;
+       return EXIT_FAILURE;
    }
    job->type = BACKGROUND;
 
    if (sigaction(SIGCHLD, &sigchld, NULL) == -1) {
        note("Unable to install SIGCHLD handler");
-       return 1;
+       return EXIT_FAILURE;
    }
 
-   return 0;
+   return EXIT_SUCCESS;
 }
index d3ef2997761d2b59a72e798338d9b00fa73c0647..518df502189d95973f1a3b69cd25138f781d16e5 100644 (file)
@@ -7,18 +7,21 @@
 BUILTINSIG(cd) {
    char *fullpath;
 
-   if (argv[1]) {
-       if (!(fullpath = realpath(argv[1], NULL))) {
-           note("Could not resolve path name");
-           return 1;
-       }
-   } else fullpath = home;
+   if (!argv[1]) fullpath = home;
+   else if (!(fullpath = realpath(argv[1], NULL))) {
+       note("Could not resolve path name");
+       return EXIT_FAILURE;
+   }
+
    if (chdir(fullpath) == -1) {
        note("Unable to change directory to `%s'", argv[1]);
-       return 1;
+       return EXIT_FAILURE;
    }
+
    if (setenv("PWD", fullpath, 1) == -1)
        note("Unable to change $PWD$ to `%s'", fullpath);
+
    if (fullpath != home) free(fullpath);
-   return 0;
+
+   return EXIT_SUCCESS;
 }
index f53a261ba3a989c3cd5484cedebd2e84ec63668d..c2f035c92d7ea9a38fcbf805ef966059e8952b3a 100644 (file)
@@ -15,7 +15,7 @@ BUILTINSIG(fg) {
 
    if (sigaction(SIGCHLD, &sigdfl, NULL) == -1) {
        note("Unable to acquire lock on the job stack");
-       return 1;
+       return EXIT_FAILURE;
    }
 
    if (argv[1]) {
@@ -23,25 +23,25 @@ BUILTINSIG(fg) {
        if ((jobid = strtol(argv[1], NULL, 10)) == LONG_MAX && errno
            || jobid <= 0) {
            note("Invalid process group id");
-           return 1;
+           return EXIT_FAILURE;
        }
        if (!(job = findjob((pid_t)jobid))) {
            note("Unable to find process group %d", (pid_t)jobid);
-           return 1;
+           return EXIT_FAILURE;
        }
        job = deletejob();
    } else if (!(job = pull(&jobs))) {
        note("No processes to bring into the foreground");
-       return 1;
+       return EXIT_FAILURE;
    }
 
    if (sigaction(SIGCHLD, &sigchld, NULL) == -1) {
        note("Unable to install SIGCHLD handler");
-       return 1;
+       return EXIT_FAILURE;
    }
 
-   if (!setfg(*job)) return 1;
+   if (!setfg(*job)) return EXIT_FAILURE;
    waitfg(*job);
 
-   return 0;
+   return EXIT_SUCCESS;
 }
index c956da80a3357e29fae328e8ea6bfa175a95d5dd..9c4c48af6742f053ba4a60fd2fa14b4a08600b57 100644 (file)
@@ -19,6 +19,7 @@ static int inpath(char *dir, char *filename) {
            return 1;
        }
    } else if (errno != ENOENT) note("Unable to check if `%s' exists", filepath);
+
    return 0;
 }
 
@@ -26,28 +27,28 @@ BUILTINSIG(which) {
    struct builtin *builtin;
    char *path, *dir, *p;
    
-   if (!argv[1]) return 1;
+   if (!argv[1]) return EXIT_FAILURE;
    for (builtin = builtins; builtin->func; ++builtin)
        if (strcmp(argv[1], builtin->name) == 0) {
-           puts("Built-in command\r");
-           return 0;
+           printf("%s is built-in\r\n", argv[1]);
+           return EXIT_SUCCESS;
        }
 
    if (!(path = getenv("PATH"))) {
        note("Unable to examine $PATH$");
-       return 1;
+       return EXIT_FAILURE;
    }
    if (!(path = p = strdup(path))) {
        note("Unable to duplicate $PATH$");
-       return 1;
+       return EXIT_FAILURE;
    }
    do {
        if (!(dir = p)) break;
        if ((p = strchr(dir, ':'))) *p++ = '\0';
    } while (!inpath(dir, argv[1]));
    free(path);
-   if (dir) return 0;
+   if (dir) return EXIT_SUCCESS;
 
    printf("%s not found\r\n", argv[1]);
-   return 1;
+   return EXIT_FAILURE;
 }
index 9e6818aed0bddaed46f1ae0a2ae44e6e176c77fa..b16bd76be1eab0b2682488e61684833ac97d9fad 100644 (file)
@@ -1,7 +1,7 @@
 #define DEFAULTPROMPT ">"
 
 #define HISTORYFILE ".ashhistory"
-#define INTERACTIVEFILE ".ashinteractive"
+#define INTFILE ".ashint"
 #define LOGINFILE ".ashlogin"
 
 #define MAXBUILTINS 100 // Maximum number of builtin commands
index fbe0ee1b7c128d7ac629bfeef3a5b2c17738ab15..dea4330379ece545266efe34911df4a94cfb1214 100644 (file)
@@ -76,6 +76,7 @@ INPUT(scriptinput) {
 }
 
 char *config(char *name) {
+   int fd;
    char *result;
    static char *origscript, *origstr;
 
@@ -85,6 +86,11 @@ char *config(char *name) {
        script = catpath(home, name);
    }
 
+   if ((fd = open(script, O_RDONLY | O_CREAT, 0644)) == -1)
+       fatal("Unable to open `%s'\n", script);
+   if (close(fd) == -1)
+       fatal("Unable to close `%s'", script);
+
    if (!(result = scriptinput())) {
        script = origscript;
        string = origstr;
index cabe510f1f135e27ddb705c91fa95d15c2c7afa3..a547092874e09c3098dea2edade4058621920eaa 100644 (file)
@@ -16,7 +16,7 @@ int main(int localargc, char **localargv) {
    initialize();
 
    if (login) while (run(parse(config(LOGINFILE))));
-   if (interactive) while (run(parse(config(INTERACTIVEFILE))));
+   if (interactive) while (run(parse(config(INTFILE))));
 
    while (run(parse(input())));
 
index 4dea8ca97645c012cfa53a706bb0549a03391adb..e377aa58593f63d1d4220790bd2ca7d4ba8d541b 100644 (file)
@@ -1,4 +1,5 @@
 #include <limits.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -6,11 +7,19 @@
 #include "input.h"
 #include "options.h"
 #include "parse.h"
+#include "run.h"
 #include "utils.h"
 
 static char *tokens[MAXCHARS + 1];
 static struct cmd cmds[MAXCMDS + 1];
-struct cmd empty = {0};
+
+static void initcmd(struct cmd *cmd) {
+   cmd->args = NULL;
+   cmd->r = cmd->rds;
+   cmd->r->mode = END;
+   cmd->prev = cmd - 1;
+   cmd->next = NULL;
+}
 
 struct cmd *parse(char *b) {
    char **t, *name, *value, *stlend, *p, *end, *env;
@@ -21,9 +30,11 @@ struct cmd *parse(char *b) {
    if (!b) return NULL;
    t = tokens;
    c = cmds;
+   c->next = c + 1;
    value = name = NULL;
-   for (c->args = NULL, c->r = c->rds; *b; ++b) switch (*b) {
+   for (initcmd(++c); *b; ++b) switch (*b) {
    default:
+       if (c->r->mode) break;
        if (!c->args) c->args = t;
        if (!*(b - 1)) {
            if (!name) *t++ = b; else if (!value) value = b;
@@ -31,12 +42,16 @@ struct cmd *parse(char *b) {
        break;
    case '<':
    case '>':
+       if (c->r->mode) {
+           note("File redirections should be separated by spaces");
+           return c;
+       }
        c->r->newfd = *b == '>';
        if (*(b - 1)) {
            if (c->args == --t) c->args = NULL;
            if ((l = strtol(*t, &stlend, 10)) < 0 || l > INT_MAX || stlend != b) {
-               note("Incorrect syntax for file redirection");
-               return &empty;
+               note("Invalid value for a file redirection");
+               return c;
            }
            c->r->newfd = l;
        }
@@ -45,7 +60,7 @@ struct cmd *parse(char *b) {
            ++c->r->mode;
            ++b;
        }
-       c->r++->oldname = b + 1;
+       c->r->oldname = b + 1;
        if (*(b + 1) == '&') ++b;
        break;
    case '"':
@@ -58,7 +73,7 @@ struct cmd *parse(char *b) {
        }
        if (!b) {
            note("Quote left open-ended");
-           return &empty;
+           return c;
        }
        memmove(p, p + 1, end-- - p);
        --b;
@@ -97,23 +112,38 @@ struct cmd *parse(char *b) {
        while (*b && *b != '$') ++b;
        if (!*b) {
            note("Environment variable lacks a terminating `$'");
-           return &empty;
+           return c;
        }
        *b++ = '\0';
        for (end = b; *end; ++end);
 
        l = strtol(p + 1, &stlend, 10);
        if (stlend == b - 1) env = l >= 0 && l < argc ? argv[l] : b - 1;
-       else if ((env = getenv(p + 1)) == NULL) {
+       else if (strcmp(p + 1, "?") == 0) {
+           if (!sprintf(env = (char [12]){0}, "%d", status)) {
+               note("Unable to get previous command status");
+               return c;
+           }
+       } else if ((env = getenv(p + 1)) == NULL) {
            note("Environment variable does not exist");
-           return &empty;
+           return c;
        }
+
        e = strlen(env);
        offset = e - (b - p);
-       memmove(b + offset, b, end - b);
+       memmove(b + offset, b, end - b + 1);
        strncpy(p, env, e);
        b += offset - 1;
-       *(end + offset) = '\0';
+       break;
+   case '~':
+       if (!*(b - 1)) {
+           if (!name) *t++ = b; else if (!value) value = b;
+       }
+       for (end = b; *end; ++end);
+       offset = strlen(home);
+       memmove(b + offset, b + 1, end - b);
+       strncpy(b, home, offset);
+       b += offset - 1;
        break;
    case '#':
        *(b + 1) = '\0';
@@ -121,44 +151,47 @@ struct cmd *parse(char *b) {
    case '|':
    case ';':
        if (name && *c->args == name) c->args = NULL;
-       if (c->args) {
+       if (c->args || c->rds->mode) {
            if ((c->term = *b) == *(b + 1) && (*b == '&' || *b == '|')) {
                ++c->term;
                *b++ = '\0';
            }
            *b = '\0';
-           c->r->mode = END;
+           if (c->r->mode) (++c->r)->mode = END;
            for (c->r = c->rds; c->r->mode; ++c->r) if (*c->r->oldname == '&') {
                if ((l = strtol(++c->r->oldname, &stlend, 10)) < 0
                    || l > INT_MAX || *stlend) {
                    note("Incorrect syntax for file redirection");
-                   return &empty;
+                   return c;
                }
                c->r->oldfd = l;
                c->r->oldname = NULL;
            }
+           c->r = c->rds;
+           c->next = c + 1;
 
-           (++c)->args = NULL;
+           initcmd(++c);
            *t++ = NULL;
        }
-       c->r = c->rds;
    case ' ':
        *b = '\0';
        if (value) {
            if (setenv(name, value, 1) == -1) {
                note("Unable to set environment variable");
-               return &empty;
+               return c;
            }
            value = name = NULL;
        }
+       if (c->r->mode) (++c->r)->mode = END;
    }
 
-   if (c-- != cmds) switch (c->term) {
+   (--c)->next = NULL;
+   switch (c->term) {
    case AND:
    case PIPE:
    case OR:
        note("Expected another command");
-       return &empty;
+       return c;
    default:
        break;
    }
index 4d1a430fb7155b85b284bccb00c2925247653a48..29623943265c8dc93851b90619eba63f1cafa9e1 100644 (file)
@@ -25,8 +25,7 @@ struct cmd {
    struct redirect *r, rds[MAXRDS + 1];
    enum terminator term;
    int pipe[2];
+   struct cmd *prev, *next;
 };
 
-extern struct cmd empty;
-
 struct cmd *parse(char *b);
index 160cf9b178f7d89c352c9fcee7d856f25678aff1..e78656bc627a36c7ba0bbea3a56428fbc068ea4e 100644 (file)
--- a/src/run.c
+++ b/src/run.c
@@ -12,6 +12,8 @@
 #include "stack.h"
 #include "utils.h"
 
+int status;
+
 static int closepipe(struct cmd *cmd) {
    int result;
 
@@ -52,24 +54,45 @@ static void redirectfiles(struct redirect *r) {
    }
 }
 
+static void exec(struct cmd *cmd) {
+   char *cwd;
+
+   execvp(*cmd->args, cmd->args);
+   if (!(cwd = getcwd(NULL, 0)))
+       fatal("Unable to check current working directory");
+   execvP(*cmd->args, cwd, cmd->args);
+   free(cwd);
+   fatal("Couldn't find `%s' command", *cmd->args);
+}
+
 int run(struct cmd *cmd) {
-   struct cmd *prev;
    int ispipe, ispipestart, ispipeend;
-   static int status;
    pid_t cpid, jobid;
    struct job job;
 
    if (!cmd) return 0;
 
-   for (prev = &empty; cmd->args; prev = cmd++) {
-       ispipe = cmd->term == PIPE || prev->term == PIPE;
-       ispipestart = ispipe && prev->term != PIPE;
+   while ((cmd = cmd->next)) {
+       if (!cmd->args) {
+           if (!cmd->r->mode) break;
+           if ((cpid = fork()) == -1) {
+               note("Unable to create child process");
+               break;
+           } else if (cpid == 0) {
+               redirectfiles(cmd->r);
+               exit(EXIT_SUCCESS);
+           }
+           continue;
+       }
+
+       ispipe = cmd->term == PIPE || cmd->prev->term == PIPE;
+       ispipestart = ispipe && cmd->prev->term != PIPE;
        ispipeend = ispipe && cmd->term != PIPE;
 
        if (ispipe) {
            if (!ispipeend && pipe(cmd->pipe) == -1) {
                note("Unable to create pipe");
-               if (!ispipestart) closepipe(prev);
+               if (!ispipestart) closepipe(cmd->prev);
                break;
            }
            if ((jobid = cpid = fork()) == -1) {
@@ -77,9 +100,9 @@ int run(struct cmd *cmd) {
                break;
            } else if (cpid == 0) {
                if (!ispipestart) {
-                   if (dup2(prev->pipe[0], 0) == -1)
-                       fatal("Unable to duplicate read end of `%s' pipe", *prev->args);
-                   if (!closepipe(prev)) exit(EXIT_FAILURE);
+                   if (dup2(cmd->prev->pipe[0], 0) == -1)
+                       fatal("Unable to duplicate read end of `%s' pipe", *cmd->prev->args);
+                   if (!closepipe(cmd->prev)) exit(EXIT_FAILURE);
                }
                if (!ispipeend) {
                    if (dup2(cmd->pipe[1], 1) == -1)
@@ -88,49 +111,47 @@ int run(struct cmd *cmd) {
                }
 
                redirectfiles(cmd->rds);
-               if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS);
-               if (execvp(*cmd->args, cmd->args) == -1)
-                   fatal("Couldn't find `%s' command", *cmd->args);
+               if (isbuiltin(cmd->args, &status)) exit(status);
+               exec(cmd);
            }
            if (!ispipestart) {
-               closepipe(prev);
+               closepipe(cmd->prev);
                jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id;
            }
-       } else {
-           if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break;
-           if ((jobid = cpid = fork()) == -1) {
-               note("Unable to create child process");
-               break;
-           } else if (cpid == 0) {
-               redirectfiles(cmd->rds);
-               if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS);
-               if (execvp(*cmd->args, cmd->args) == -1)
-                   fatal("Couldn't find `%s' command", *cmd->args);
-           }
-       }
-
-       if (setpgid(cpid, jobid) == -1) {
-           if (errno != ESRCH) {
-               note("Unable to set pgid of `%s' command to %d", *cmd->args, jobid);
-               if (kill(cpid, SIGKILL) == -1)
-                   note("Unable to kill process %d; may need to manually terminate", cpid);
-           }
+       } else if (!cmd->rds->mode && isbuiltin(cmd->args, &status)) cpid = 0;
+       else if ((jobid = cpid = fork()) == -1) {
+           note("Unable to create child process");
            break;
+       } else if (cpid == 0) {
+           redirectfiles(cmd->rds);
+           if (isbuiltin(cmd->args, &status)) exit(status);
+           exec(cmd);
        }
-       job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND};
-       if (ispipestart || cmd->term == BG) {
-           if (!push(&jobs, &job)) {
-               note("Unable to add job to background; too many background jobs");
-               if (ispipestart) closepipe(cmd);
+
+       if (cpid) {
+           if (setpgid(cpid, jobid) == -1) {
+               if (errno != ESRCH) {
+                   note("Unable to set pgid of `%s' command to %d", *cmd->args, jobid);
+                   if (kill(cpid, SIGKILL) == -1)
+                       note("Unable to kill process %d; may need to manually terminate", cpid);
+               }
                break;
            }
-       } else if (cmd->term != PIPE) {
-           if (!setfg(job)) break;
-           status = waitfg(job);
-
-           if (cmd->term == AND && status != 0) break;
-           if (cmd->term == OR && status == 0) break;
+           job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND};
+           if (ispipestart || cmd->term == BG) {
+               if (!push(&jobs, &job)) {
+                   note("Unable to add job to background; too many background jobs");
+                   if (ispipestart) closepipe(cmd);
+                   break;
+               }
+           } else if (cmd->term != PIPE) {
+               if (!setfg(job)) break;
+               status = waitfg(job);
+           }
        }
+
+       if (cmd->term == AND && status != EXIT_SUCCESS) break;
+       if (cmd->term == OR && status == EXIT_SUCCESS) break;
    }
 
    return 1;
index bf296d07020c58828b2bb45486d16d3d4e94f3a5..7760e69ed1adf6886cbb80ea94865e7854a94f7e 100644 (file)
--- a/src/run.h
+++ b/src/run.h
@@ -1 +1,3 @@
+extern int status;
+
 int run(struct cmd *cmd);