From: Trent Huber Date: Sat, 26 Jul 2025 18:42:39 +0000 (-0400) Subject: Tilde expansion, previous command status, altered cmd structure X-Git-Url: https://trenthuber.com/code?a=commitdiff_plain;h=94126f4517dbf53a40d154a5b3e80c0f52864876;p=thus.git Tilde expansion, previous command status, altered cmd structure --- diff --git a/external/cbs b/external/cbs index 47569fb..4711a83 160000 --- a/external/cbs +++ b/external/cbs @@ -1 +1 @@ -Subproject commit 47569fbe806a5138763e7ef3447dca254357dff8 +Subproject commit 4711a83fafa0282fadeaba701a25539b3e73d073 diff --git a/src/builtin/bg.c b/src/builtin/bg.c index 76c58a8..1e090f0 100644 --- a/src/builtin/bg.c +++ b/src/builtin/bg.c @@ -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; } diff --git a/src/builtin/cd.c b/src/builtin/cd.c index d3ef299..518df50 100644 --- a/src/builtin/cd.c +++ b/src/builtin/cd.c @@ -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; } diff --git a/src/builtin/fg.c b/src/builtin/fg.c index f53a261..c2f035c 100644 --- a/src/builtin/fg.c +++ b/src/builtin/fg.c @@ -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; } diff --git a/src/builtin/which.c b/src/builtin/which.c index c956da8..9c4c48a 100644 --- a/src/builtin/which.c +++ b/src/builtin/which.c @@ -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; } diff --git a/src/config.h b/src/config.h index 9e6818a..b16bd76 100644 --- a/src/config.h +++ b/src/config.h @@ -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 diff --git a/src/input.c b/src/input.c index fbe0ee1..dea4330 100644 --- a/src/input.c +++ b/src/input.c @@ -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; diff --git a/src/main.c b/src/main.c index cabe510..a547092 100644 --- a/src/main.c +++ b/src/main.c @@ -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()))); diff --git a/src/parse.c b/src/parse.c index 4dea8ca..e377aa5 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -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 ∅ + 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 ∅ + 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 ∅ + 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 ∅ + 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 ∅ + 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 ∅ + 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 ∅ + return c; default: break; } diff --git a/src/parse.h b/src/parse.h index 4d1a430..2962394 100644 --- a/src/parse.h +++ b/src/parse.h @@ -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); diff --git a/src/run.c b/src/run.c index 160cf9b..e78656b 100644 --- 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 = ∅ 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; diff --git a/src/run.h b/src/run.h index bf296d0..7760e69 100644 --- a/src/run.h +++ b/src/run.h @@ -1 +1,3 @@ +extern int status; + int run(struct cmd *cmd);