From: Trent Huber Date: Sun, 20 Jul 2025 08:16:14 +0000 (-0400) Subject: Finish TODOs, add positional parameters X-Git-Url: https://trenthuber.com/code?a=commitdiff_plain;h=fa5d8b0ac71b1bc677acc4c1312a0854247ab3ac;p=thus.git Finish TODOs, add positional parameters --- diff --git a/src/builtins.c b/src/builtins.c index 5ec4be3..9112d67 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -6,33 +6,51 @@ #include #include #include +#include #include "job.h" #include "stack.h" #include "term.h" #include "utils.h" -#define BUILTINSIG(name) int name(char **tokens) +#define BUILTINSIG(name) int name(int argc, char **argv) struct builtin { char *name; BUILTINSIG((*func)); }; -BUILTINSIG(cd) { // TODO: Affect $PWD$ env var - if (!tokens[1]) return 1; - if (chdir(tokens[1]) != -1) return 0; - note("Unable to change directory to `%s'", tokens[1]); - return 1; +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 (chdir(fullpath) == -1) { + note("Unable to change directory to `%s'", argv[1]); + return 1; + } + if (setenv("PWD", fullpath, 1) == -1) + note("Unable to change $PWD$ to `%s'", fullpath); + if (fullpath != home) free(fullpath); + return 0; } BUILTINSIG(fg) { long jobid; struct job *job; - if (tokens[1]) { + if (sigaction(SIGCHLD, &sigdfl, NULL) == -1) { + note("Unable to acquire lock on the job stack"); + return 1; + } + + if (argv[1]) { errno = 0; - if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno + if ((jobid = strtol(argv[1], NULL, 10)) == LONG_MAX && errno || jobid <= 0) { note("Invalid process group id"); return 1; @@ -46,6 +64,12 @@ BUILTINSIG(fg) { note("No processes to bring into the foreground"); return 1; } + + if (sigaction(SIGCHLD, &sigchld, NULL) == -1) { + note("Unable to install SIGCHLD handler"); + return 1; + } + if (!setfg(*job)) return 1; waitfg(*job); @@ -56,9 +80,14 @@ BUILTINSIG(bg) { long jobid; struct job *job; - if (tokens[1]) { + if (sigaction(SIGCHLD, &sigdfl, NULL) == -1) { + note("Unable to acquire lock on the job stack"); + return 1; + } + + if (argv[1]) { errno = 0; - if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno + if ((jobid = strtol(argv[1], NULL, 10)) == LONG_MAX && errno || jobid <= 0) { note("Invalid job id"); return 1; @@ -91,6 +120,11 @@ BUILTINSIG(bg) { } job->type = BACKGROUND; + if (sigaction(SIGCHLD, &sigchld, NULL) == -1) { + note("Unable to install SIGCHLD handler"); + return 1; + } + return 0; } @@ -104,25 +138,58 @@ struct builtin builtins[] = { BUILTIN(NULL), }; +static int inpath(char *dir, char *filename) { + char *filepath; + struct stat estat; + + if (stat(filepath = catpath(dir, filename), &estat) != -1) { + if (estat.st_mode & S_IXUSR) { + puts(filepath); + putchar('\r'); + return 1; + } + } else if (errno != ENOENT) note("Unable to check if `%s' exists", filepath); + return 0; +} + BUILTINSIG(which) { struct builtin *builtin; + char *path, *dir, *p; - if (!tokens[1]) return 1; + if (!argv[1]) return 1; for (builtin = builtins; builtin->func; ++builtin) - if (strcmp(tokens[1], builtin->name) == 0) { - puts("Built-in command"); + if (strcmp(argv[1], builtin->name) == 0) { + puts("Built-in command\r"); return 0; } - // TODO: Find command in PATH + + if (!(path = getenv("PATH"))) { + note("Unable to examine $PATH$"); + return 1; + } + if (!(path = p = strdup(path))) { + note("Unable to duplicate $PATH$"); + return 1; + } + do { + if (!(dir = p)) break; + if ((p = strchr(dir, ':'))) *p++ = '\0'; + } while (!inpath(dir, argv[1])); + free(path); + if (dir) return 0; + + printf("%s not found\r\n", argv[1]); return 1; } int isbuiltin(char **args, int *statusp) { struct builtin *builtinp; + size_t n; for (builtinp = builtins; builtinp->func; ++builtinp) if (strcmp(*args, builtinp->name) == 0) { - *statusp = builtinp->func(args); + for (n = 0; args[n]; ++n); + *statusp = builtinp->func(n, args); return 1; } return 0; diff --git a/src/config.h b/src/config.h index add21bf..97e9448 100644 --- a/src/config.h +++ b/src/config.h @@ -1,3 +1,5 @@ +#define DEFAULTPROMPT ">" + #define HISTORYFILE ".ashhistory" #define INTERACTIVEFILE ".ashinteractive" #define LOGINFILE ".ashlogin" diff --git a/src/history.c b/src/history.c index 121b9a6..cc48df9 100644 --- a/src/history.c +++ b/src/history.c @@ -14,7 +14,7 @@ INITSTACK(history, historyarr, 1); void readhistory(void) { FILE *file; - if (!(file = fopen(prependhome(HISTORYFILE), "r"))) { + if (!(file = fopen(catpath(home, HISTORYFILE), "r"))) { if (errno == ENOENT) return; fatal("Unable to open history file for reading"); } @@ -30,7 +30,7 @@ void readhistory(void) { void writehistory(void) { FILE *file; - if (!(file = fopen(prependhome(HISTORYFILE), "w"))) { + if (!(file = fopen(catpath(home, HISTORYFILE), "w"))) { note("Unable to open history file for writing"); return; } diff --git a/src/input.c b/src/input.c index a7d01e3..fbe0ee1 100644 --- a/src/input.c +++ b/src/input.c @@ -15,9 +15,7 @@ #include "stack.h" #include "utils.h" -#define PROMPT "> " // TODO: Have prompt be an environment variable - -enum character { +enum { CTRLC = '\003', CTRLD, BACKSPACE = '\010', @@ -84,7 +82,7 @@ char *config(char *name) { if (!origscript) { origscript = script; origstr = string; - script = prependhome(name); + script = catpath(home, name); } if (!(result = scriptinput())) { @@ -96,23 +94,26 @@ char *config(char *name) { return result; } -static void waitbgsig(int sig) { - (void)sig; - waitbg(); +static size_t prompt(void) { + char *p; + + if (!(p = getenv("PROMPT")) && setenv("PROMPT", p = DEFAULTPROMPT, 1) == -1) + note("Unable to update $PROMPT$ environment variable"); + printf("%s ", p); + return strlen(p) + 1; } INPUT(userinput) { char *cursor, *end; + size_t promptlen; unsigned int c; int i; - signal(SIGCHLD, waitbgsig); // TODO: Use sigaction for portability - end = cursor = buffer; *history.t = *buffer = '\0'; history.c = history.t; while (buffer == end) { - fputs(PROMPT, stdout); + promptlen = prompt(); while ((c = getchar()) != '\r') switch (c) { default: if (c >= ' ' && c <= '~') { @@ -132,11 +133,10 @@ INPUT(userinput) { return buffer; case CTRLD: puts("^D\r"); - signal(SIGCHLD, SIG_DFL); // XXX return NULL; case CLEAR: fputs("\033[H\033[J", stdout); - fputs(PROMPT, stdout); + prompt(); fputs(buffer, stdout); continue; case ESCAPE: @@ -156,7 +156,7 @@ INPUT(userinput) { if (history.c == (c == UP ? history.b : history.t)) continue; putchar('\r'); - for (i = end - buffer + strlen(PROMPT); i > 0; --i) putchar(' '); + for (i = end - buffer + promptlen; i > 0; --i) putchar(' '); putchar('\r'); if (strcmp((char *)history.c, buffer) != 0) @@ -165,7 +165,7 @@ INPUT(userinput) { strcpy(buffer, (char *)history.c); end = cursor = buffer + strlen(buffer); - fputs(PROMPT, stdout); + prompt(); fputs(buffer, stdout); break; case LEFT: @@ -196,13 +196,10 @@ INPUT(userinput) { } puts("\r"); } - fpurge(stdout); push(&history, buffer); *end++ = ';'; *end = '\0'; - signal(SIGCHLD, SIG_DFL); // XXX - return buffer; } diff --git a/src/job.c b/src/job.c index 7c9bb09..a412e89 100644 --- a/src/job.c +++ b/src/job.c @@ -15,6 +15,8 @@ static struct job jobarr[MAXJOBS + 1]; INITSTACK(jobs, jobarr, 0); struct termios raw, canonical; +struct sigaction sigchld, sigdfl, sigign; +static int fgstatus; void *findjob(pid_t jobid) { if (jobs.b == jobs.t) return NULL; @@ -52,22 +54,12 @@ int setfg(struct job job) { } int waitfg(struct job job) { - int status, pgid, result; + while (waitpid(job.id, NULL, 0) != -1); + errno = 0; - do { - errno = 0; - waitpid(job.id, &status, WUNTRACED); - } while (errno == EINTR); - if (!errno && !WIFSTOPPED(status)) do { - errno = 0; - while (waitpid(-job.id, NULL, 0) != -1); - } while (errno == EINTR); - result = errno != ECHILD ? errno : 0; - - // TODO: Use sigaction >:( - if ((pgid = getpgid(0)) == -1 || signal(SIGTTOU, SIG_IGN) == SIG_ERR - || tcsetpgrp(STDIN_FILENO, pgid) == -1 - || signal(SIGTTOU, SIG_DFL) == SIG_ERR) { + if (sigaction(SIGTTOU, &sigign, NULL) == -1 + || tcsetpgrp(STDIN_FILENO, getpgrp()) == -1 + || sigaction(SIGTTOU, &sigdfl, NULL) == -1) { note("Unable to reclaim foreground; terminating"); deinitialize(); exit(EXIT_FAILURE); @@ -75,38 +67,34 @@ int waitfg(struct job job) { if (tcgetattr(STDIN_FILENO, &job.config) == -1) note("Unable to save termios config of job %d", job.id); setconfig(&raw); - if (result) return result; - if (WIFSIGNALED(status)) { - result = WTERMSIG(status); - puts("\r"); - } else if (WIFSTOPPED(status)) { - result = WSTOPSIG(status); - job.type = SUSPENDED; - if (push(&jobs, &job)) return result; + if (WIFEXITED(fgstatus)) return WEXITSTATUS(fgstatus); + else if (WIFSIGNALED(fgstatus)) return WTERMSIG(fgstatus); + job.type = SUSPENDED; + if (!push(&jobs, &job)) { note("Unable to add job %d to list; too many jobs\r\n" - "Press any key to continue", job.id); + "Press any key to continue", job.id); getchar(); if (setfg(job)) return waitfg(job); note("Manual intervention required for job %d", job.id); - } else if (WIFEXITED(status)) result = WEXITSTATUS(status); - - return result; + } + return WSTOPSIG(fgstatus); } -void waitbg(void) { +void sigchldhandler(int sig) { int status; pid_t id; - for (jobs.c = jobs.b; jobs.c != jobs.t; INC(jobs, c)) { - if (CURRENT->type != BACKGROUND) continue; - id = CURRENT->id; - - // TODO: weird EINTR thing here too?? - while ((id = waitpid(-id, &status, WNOHANG | WUNTRACED)) > 0) + (void)sig; + while ((id = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { + for (jobs.c = jobs.b; jobs.c != jobs.t; INC(jobs, c)) if (CURRENT->id == id) { if (WIFSTOPPED(status)) CURRENT->type = SUSPENDED; - - if (id == -1 && errno != ECHILD) - note("Unable to wait on some child processes"); + else deletejob(); + break; + } + if (jobs.c == jobs.t) { + fgstatus = status; + if (!WIFSTOPPED(fgstatus)) while (waitpid(-id, NULL, 0) != -1); + } } } diff --git a/src/job.h b/src/job.h index 12fa031..7b33124 100644 --- a/src/job.h +++ b/src/job.h @@ -13,10 +13,11 @@ struct job { extern struct stack jobs; extern struct termios raw, canonical; +extern struct sigaction sigchld, sigdfl, sigign; void *findjob(pid_t jobid); void *deletejob(void); int setconfig(struct termios *mode); int setfg(struct job job); int waitfg(struct job job); -void waitbg(void); +void sigchldhandler(int sig); diff --git a/src/main.c b/src/main.c index 30a92fe..cabe510 100644 --- a/src/main.c +++ b/src/main.c @@ -7,8 +7,11 @@ #include "run.h" #include "utils.h" -int main(int argc, char **argv) { - options(&argc, &argv); +int main(int localargc, char **localargv) { + argc = localargc; + argv = localargv; + + options(); initialize(); diff --git a/src/options.c b/src/options.c index 4536139..0476c18 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -6,40 +7,55 @@ #include "input.h" #include "options.h" -int login, interactive; +int login, interactive, argc; Input input; +char **argv; -void options(int *argcp, char ***argvp) { +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(void) { int opt, l; - char *usage = "TODO: WRITE USAGE"; - login = ***argvp == '-'; + login = **argv == '-'; interactive = 1; input = userinput; - while ((opt = getopt(*argcp, *argvp, ":c:hl")) != -1) switch (opt) { - case 'c': - interactive = 0; - input = stringinput; - string = optarg; - break; - case 'h': - errx(EXIT_SUCCESS, "%s", usage); - case 'l': - login = 1; - break; - case ':': - errx(EXIT_FAILURE, "Expected argument following `-%c'\n%s", optopt, usage); - case '?': - default: - errx(EXIT_FAILURE, "Unknown command line option `-%c'\n%s", optopt, usage); + while ((opt = getopt(argc, argv, ":c:hl")) != -1) { + switch (opt) { + case 'c': + interactive = 0; + input = stringinput; + string = optarg; + break; + case 'h': + usage(*argv, EXIT_SUCCESS); + case 'l': + login = 1; + break; + case ':': + warnx("Expected argument following `-%c'\n", optopt); + usage(*argv, EXIT_FAILURE); + case '?': + default: + warnx("Unknown command line option `-%c'\n", optopt); + usage(*argv, EXIT_FAILURE); + } + if (opt == 'c') break; } - *argcp -= optind; - *argvp += optind; - - if (!string && **argvp) { + if (!string && argv[optind]) { interactive = 0; input = scriptinput; - script = **argvp; + script = argv[optind]; + } + if (!interactive) { + argc -= optind; + argv += optind; } } diff --git a/src/options.h b/src/options.h index 724c3aa..9918207 100644 --- a/src/options.h +++ b/src/options.h @@ -1,6 +1,7 @@ typedef INPUT((*Input)); -extern int login, interactive; +extern int login, interactive, argc; extern Input input; +extern char **argv; -void options(int *argcp, char ***argvp); +void options(void); diff --git a/src/parse.c b/src/parse.c index 7a93f74..4dea8ca 100644 --- a/src/parse.c +++ b/src/parse.c @@ -4,6 +4,7 @@ #include "config.h" #include "input.h" +#include "options.h" #include "parse.h" #include "utils.h" @@ -12,7 +13,7 @@ static struct cmd cmds[MAXCMDS + 1]; struct cmd empty = {0}; struct cmd *parse(char *b) { - char **t, *name, *value, *end, *p, *env; + char **t, *name, *value, *stlend, *p, *end, *env; struct cmd *c; long l; int e, offset; @@ -33,8 +34,8 @@ struct cmd *parse(char *b) { c->r->newfd = *b == '>'; if (*(b - 1)) { if (c->args == --t) c->args = NULL; - if ((l = strtol(*t, &end, 10)) < 0 || l > INT_MAX || end != b) { - note("Incorrect syntax for file redirection\r"); + if ((l = strtol(*t, &stlend, 10)) < 0 || l > INT_MAX || stlend != b) { + note("Incorrect syntax for file redirection"); return ∅ } c->r->newfd = l; @@ -56,7 +57,7 @@ struct cmd *parse(char *b) { if (*end == '\\') ++end; } if (!b) { - note("Quote left open-ended\r"); + note("Quote left open-ended"); return ∅ } memmove(p, p + 1, end-- - p); @@ -95,14 +96,16 @@ struct cmd *parse(char *b) { p = b++; while (*b && *b != '$') ++b; if (!*b) { - note("Environment variable lacks a terminating `$'\r"); + note("Environment variable lacks a terminating `$'"); return ∅ } *b++ = '\0'; for (end = b; *end; ++end); - if ((env = getenv(p + 1)) == NULL) { - note("Environment variable does not exist\r"); + 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) { + note("Environment variable does not exist"); return ∅ } e = strlen(env); @@ -126,8 +129,9 @@ struct cmd *parse(char *b) { *b = '\0'; c->r->mode = END; for (c->r = c->rds; c->r->mode; ++c->r) if (*c->r->oldname == '&') { - if ((l = strtol(++c->r->oldname, &end, 10)) < 0 || l > INT_MAX || *end) { - note("Incorrect syntax for file redirection\r"); + if ((l = strtol(++c->r->oldname, &stlend, 10)) < 0 + || l > INT_MAX || *stlend) { + note("Incorrect syntax for file redirection"); return ∅ } c->r->oldfd = l; @@ -142,7 +146,7 @@ struct cmd *parse(char *b) { *b = '\0'; if (value) { if (setenv(name, value, 1) == -1) { - note("Unable to set environment variable\r"); + note("Unable to set environment variable"); return ∅ } value = name = NULL; @@ -153,7 +157,7 @@ struct cmd *parse(char *b) { case AND: case PIPE: case OR: - note("Expected another command\r"); + note("Expected another command"); return ∅ default: break; diff --git a/src/run.c b/src/run.c index a93f65f..9adbe86 100644 --- a/src/run.c +++ b/src/run.c @@ -132,7 +132,6 @@ int run(struct cmd *cmd) { if (cmd->term == OR && status == 0) break; } } - waitbg(); return 1; } diff --git a/src/utils.c b/src/utils.c index f943c20..b8cf6b9 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -14,12 +15,15 @@ #include "options.h" #include "utils.h" +char *home; + void note(char *fmt, ...) { va_list args; va_start(args, fmt); (errno ? vwarn : vwarnx)(fmt, args); va_end(args); putchar('\r'); + errno = 0; } void fatal(char *fmt, ...) { @@ -31,31 +35,54 @@ void fatal(char *fmt, ...) { exit(EXIT_FAILURE); } -char *prependhome(char *name) { - static char *p, path[MAXPATH + 1]; +char *catpath(char *dir, char *filename) { + static char path[MAXPATH + 1]; - if (!p) { - if (!(p = getenv("HOME"))) - fatal("Unable to access $HOME$ environment variable"); - strcpy(path, p); - strcat(path, "/"); - p = path + strlen(path); - } - *p = '\0'; - strcat(path, name); + strcpy(path, dir); + strcat(path, "/"); + strcat(path, filename); return path; } -void initialize(void) { // <-- TODO: Set $SHLVL$ in this function - cfmakeraw(&raw); +void initialize(void) { + char *shlvlstr; + long shlvl; + + // Raw mode if (tcgetattr(STDIN_FILENO, &canonical) == -1) - fatal("Unable to get default termios config"); + err(EXIT_FAILURE, "Unable to get default termios config"); + cfmakeraw(&raw); if (!setconfig(&raw)) exit(EXIT_FAILURE); + + // Set signal actions + sigchld.sa_handler = sigchldhandler; + sigdfl.sa_handler = SIG_DFL; + sigign.sa_handler = SIG_IGN; + if (sigaction(SIGCHLD, &sigchld, NULL) == -1) + fatal("Unable to install SIGCHLD handler"); + + // Initialize `home' + if (!(home = getenv("HOME"))) + fatal("Unable to locate user's home directory, $HOME$ not set"); + + // Update $SHLVL$ + if (!(shlvlstr = getenv("SHLVL"))) shlvlstr = "0"; + if ((shlvl = strtol(shlvlstr, NULL, 10)) < 0) shlvl = 0; + asprintf(&shlvlstr, "%ld", shlvl + 1); + if (setenv("SHLVL", shlvlstr, 1) == -1) + note("Unable to update $SHLVL$ environment variable"); + free(shlvlstr); + + // History read if (interactive) readhistory(); } void deinitialize(void) { + + // History write if (interactive) writehistory(); + + // Canonical mode setconfig(&canonical); } diff --git a/src/utils.h b/src/utils.h index a78c43b..24bd6e8 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,5 +1,7 @@ +extern char *home; + void note(char *fmt, ...); void fatal(char *fmt, ...); -char *prependhome(char *name); +char *catpath(char *dir, char *filename); void initialize(void); void deinitialize(void);