From 0df93b7e0e6a0f49092ef2eb0f9f57d0420b808b Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Sat, 26 Jul 2025 03:22:46 -0400 Subject: [PATCH] Builtins reorganized and automatically detected --- .gitignore | 2 +- build.c | 25 +----- external/cbs | 2 +- external/cbsfile.c | 30 +++++++ src/.gitignore | 1 + src/build.c | 26 ++++++ src/builtin/.gitignore | 1 + src/builtin/bg.c | 62 +++++++++++++ src/builtin/build.c | 67 ++++++++++++++ src/builtin/builtin.c | 17 ++++ src/builtin/builtin.h | 3 + src/builtin/cd.c | 24 +++++ src/builtin/fg.c | 47 ++++++++++ src/builtin/list.h | 6 ++ src/builtin/which.c | 53 +++++++++++ src/builtins.c | 196 ----------------------------------------- src/builtins.h | 1 - src/config.h | 1 + src/run.c | 2 +- 19 files changed, 343 insertions(+), 223 deletions(-) create mode 100644 external/cbsfile.c create mode 100644 src/.gitignore create mode 100644 src/build.c create mode 100644 src/builtin/.gitignore create mode 100644 src/builtin/bg.c create mode 100644 src/builtin/build.c create mode 100644 src/builtin/builtin.c create mode 100644 src/builtin/builtin.h create mode 100644 src/builtin/cd.c create mode 100644 src/builtin/fg.c create mode 100644 src/builtin/list.h create mode 100644 src/builtin/which.c delete mode 100644 src/builtins.c delete mode 100644 src/builtins.h diff --git a/.gitignore b/.gitignore index 7353e7e..c685519 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -build +*build *.o diff --git a/build.c b/build.c index d84fbf4..41025fd 100644 --- a/build.c +++ b/build.c @@ -1,30 +1,9 @@ #include "external/cbs/cbs.c" -#define SRCDIR "src/" -#define SRC \ - SRCDIR "builtins", \ - SRCDIR "input", \ - SRCDIR "history", \ - SRCDIR "job", \ - SRCDIR "parse", \ - SRCDIR "main", \ - SRCDIR "options", \ - SRCDIR "run", \ - SRCDIR "stack", \ - SRCDIR "utils" -#define BINDIR "bin/" -#define ASH BINDIR "ash" - int main(void) { - char **src; - - build(NULL); - - src = (char *[]){SRC, NULL}; - // cflags = (char *[]){"-ferror-limit=1", NULL}; - while (*src) compile(*src++, NULL); + build("./"); - load('x', ASH, SRC, NULL); + build("src/"); return EXIT_SUCCESS; } diff --git a/external/cbs b/external/cbs index a2cb47c..47569fb 160000 --- a/external/cbs +++ b/external/cbs @@ -1 +1 @@ -Subproject commit a2cb47c262bee92a2f7cab689e42ae8452c9e952 +Subproject commit 47569fbe806a5138763e7ef3447dca254357dff8 diff --git a/external/cbsfile.c b/external/cbsfile.c new file mode 100644 index 0000000..e4657e8 --- /dev/null +++ b/external/cbsfile.c @@ -0,0 +1,30 @@ +struct cbsfile { + char *name, **flags, type; +}; + +void buildfiles(struct cbsfile *files) { + char **c, **l, **names; + struct cbsfile *target; + size_t i; + + c = cflags; + l = lflags; + + target = files++; + + for (i = 0; files[i].name; ++i) if (files[i].flags) { + cflags = files[i].flags; + compile(files[i].name); + } + + names = allocate((i + 1) * sizeof *names); + for (i = 0; files[i].name; ++i) names[i] = files[i].name; + + lflags = target->flags; + load(target->type, target->name, names); + + free(names); + + cflags = c; + lflags = l; +} diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..d08de4e --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +libbuiltin.a diff --git a/src/build.c b/src/build.c new file mode 100644 index 0000000..1f74e83 --- /dev/null +++ b/src/build.c @@ -0,0 +1,26 @@ +#include "../external/cbs/cbs.c" +#include "../external/cbsfile.c" + +int main(void) { + build("./"); + + build("builtin/"); + + buildfiles((struct cbsfile []){{"../bin/ash", NONE, 'x'}, + + {"input", NONE}, + {"history", NONE}, + {"job", NONE}, + {"parse", NONE}, + {"main", NONE}, + {"options", NONE}, + {"run", LIST("-Ibuiltin/")}, + {"stack", NONE}, + {"utils", NONE}, + + {"builtin.a"}, + + {NULL}}); + + return EXIT_SUCCESS; +} diff --git a/src/builtin/.gitignore b/src/builtin/.gitignore new file mode 100644 index 0000000..3309b20 --- /dev/null +++ b/src/builtin/.gitignore @@ -0,0 +1 @@ +list.c diff --git a/src/builtin/bg.c b/src/builtin/bg.c new file mode 100644 index 0000000..76c58a8 --- /dev/null +++ b/src/builtin/bg.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include + +#include "builtin.h" +#include "job.h" +#include "stack.h" +#include "utils.h" + +BUILTINSIG(bg) { + long jobid; + struct job *job; + + 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(argv[1], NULL, 10)) == LONG_MAX && errno + || jobid <= 0) { + note("Invalid job id"); + return 1; + } + if (!(job = findjob((pid_t)jobid))) { + note("Unable to find job %d", (pid_t)jobid); + return 1; + } + if (job->type == BACKGROUND) { + note("Job %d already in background", (pid_t)jobid); + return 1; + } + } 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; + } + } + job = deletejob(); + + if (!(push(&jobs, job))) { + note("Unable to add job to background; too many jobs"); + return 1; + } + if (killpg(job->id, SIGCONT) == -1) { + note("Unable to wake up suspended process group %d", job->id); + return 1; + } + job->type = BACKGROUND; + + if (sigaction(SIGCHLD, &sigchld, NULL) == -1) { + note("Unable to install SIGCHLD handler"); + return 1; + } + + return 0; +} diff --git a/src/builtin/build.c b/src/builtin/build.c new file mode 100644 index 0000000..30de555 --- /dev/null +++ b/src/builtin/build.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +#include "../../external/cbs/cbs.c" +#include "../../external/cbsfile.c" +#include "../config.h" + +int main(void) { + int listfd, l; + DIR *dir; + size_t i, offset; + struct cbsfile files[1 + MAXBUILTINS + 1]; + struct dirent *entry; + char *name, *identifier; + + build("./"); + + if ((listfd = open("list.c", O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) + err(EXIT_FAILURE, "Unable to open/create `list.c'"); + if (!(dir = opendir("./"))) + err(EXIT_FAILURE, "Unable to open current directory"); + + dprintf(listfd, "#include \n\n#include \"builtin.h\"\n" + "#include \"list.h\"\n\n"); + + errno = i = 0; + files[i++] = (struct cbsfile){"../builtin", NONE, 's'}; + files[i++] = (struct cbsfile){"list", NONE}; + offset = i; + while ((entry = readdir(dir))) { + if (strcmp(entry->d_name, "build.c") == 0) continue; + if (!(name = strrchr(entry->d_name, '.')) || strcmp(name, ".c") != 0) + continue; + if (i == 1 + MAXBUILTINS + 1) + errx(EXIT_FAILURE, "Maximum allowed builtins (%d) exceeded", MAXBUILTINS); + if (!(name = strdup(entry->d_name))) + err(EXIT_FAILURE, "Unable to duplicate directory entry"); + name[strlen(name) - strlen(".c")] = '\0'; + if (strcmp(name, "list") == 0) continue; + if (strcmp(name, "builtin") != 0) + dprintf(listfd, "extern BUILTINSIG(%s);\n", name); + files[i++] = (struct cbsfile){name, LIST("-I../")}; + } + if (errno) err(EXIT_FAILURE, "Unable to read from current directory"); + files[i] = (struct cbsfile){NULL}; + + identifier = "struct builtin builtins[] = {"; + l = (int)strlen(identifier); + dprintf(listfd, "\n%s", identifier); + for (i = offset; (name = files[i].name); ++i) + if (strcmp(name, "builtin") != 0 && strcmp(name, "list") != 0) + dprintf(listfd, "{\"%s\", %s},\n%*s", name, name, l, ""); + dprintf(listfd, "{NULL}};"); + + if (closedir(dir) == -1) + err(EXIT_FAILURE, "Unable to close current directory"); + if (close(listfd) == -1) err(EXIT_FAILURE, "Unable to close `list.c'"); + + buildfiles(files); + + while (files[offset].name) free(files[offset++].name); + + return EXIT_SUCCESS; +} diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c new file mode 100644 index 0000000..03731af --- /dev/null +++ b/src/builtin/builtin.c @@ -0,0 +1,17 @@ +#include + +#include "builtin.h" +#include "list.h" + +int isbuiltin(char **args, int *statusp) { + struct builtin *builtinp; + size_t n; + + for (builtinp = builtins; builtinp->func; ++builtinp) + if (strcmp(*args, builtinp->name) == 0) { + for (n = 0; args[n]; ++n); + *statusp = builtinp->func(n, args); + return 1; + } + return 0; +} diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h new file mode 100644 index 0000000..cc96b3a --- /dev/null +++ b/src/builtin/builtin.h @@ -0,0 +1,3 @@ +#define BUILTINSIG(name) int name(int argc, char **argv) + +int isbuiltin(char **args, int *statusp); diff --git a/src/builtin/cd.c b/src/builtin/cd.c new file mode 100644 index 0000000..d3ef299 --- /dev/null +++ b/src/builtin/cd.c @@ -0,0 +1,24 @@ +#include +#include + +#include "builtin.h" +#include "utils.h" + +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; +} diff --git a/src/builtin/fg.c b/src/builtin/fg.c new file mode 100644 index 0000000..f53a261 --- /dev/null +++ b/src/builtin/fg.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +#include "builtin.h" +#include "job.h" +#include "stack.h" +#include "utils.h" + +BUILTINSIG(fg) { + long jobid; + struct job *job; + + 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(argv[1], NULL, 10)) == LONG_MAX && errno + || jobid <= 0) { + note("Invalid process group id"); + return 1; + } + if (!(job = findjob((pid_t)jobid))) { + note("Unable to find process group %d", (pid_t)jobid); + return 1; + } + job = deletejob(); + } else if (!(job = pull(&jobs))) { + 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); + + return 0; +} diff --git a/src/builtin/list.h b/src/builtin/list.h new file mode 100644 index 0000000..7c33a2f --- /dev/null +++ b/src/builtin/list.h @@ -0,0 +1,6 @@ +struct builtin { + char *name; + BUILTINSIG((*func)); +}; + +extern struct builtin builtins[]; diff --git a/src/builtin/which.c b/src/builtin/which.c new file mode 100644 index 0000000..c956da8 --- /dev/null +++ b/src/builtin/which.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include + +#include "builtin.h" +#include "list.h" +#include "utils.h" + +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 (!argv[1]) return 1; + for (builtin = builtins; builtin->func; ++builtin) + if (strcmp(argv[1], builtin->name) == 0) { + puts("Built-in command\r"); + return 0; + } + + 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; +} diff --git a/src/builtins.c b/src/builtins.c deleted file mode 100644 index 9112d67..0000000 --- a/src/builtins.c +++ /dev/null @@ -1,196 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "job.h" -#include "stack.h" -#include "term.h" -#include "utils.h" - -#define BUILTINSIG(name) int name(int argc, char **argv) - -struct builtin { - char *name; - BUILTINSIG((*func)); -}; - -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 (sigaction(SIGCHLD, &sigdfl, NULL) == -1) { - note("Unable to acquire lock on the job stack"); - return 1; - } - - if (argv[1]) { - errno = 0; - if ((jobid = strtol(argv[1], NULL, 10)) == LONG_MAX && errno - || jobid <= 0) { - note("Invalid process group id"); - return 1; - } - if (!(job = findjob((pid_t)jobid))) { - note("Unable to find process group %d", (pid_t)jobid); - return 1; - } - job = deletejob(); - } else if (!(job = pull(&jobs))) { - 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); - - return 0; -} - -BUILTINSIG(bg) { - long jobid; - struct job *job; - - 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(argv[1], NULL, 10)) == LONG_MAX && errno - || jobid <= 0) { - note("Invalid job id"); - return 1; - } - if (!(job = findjob((pid_t)jobid))) { - note("Unable to find job %d", (pid_t)jobid); - return 1; - } - if (job->type == BACKGROUND) { - note("Job %d already in background", (pid_t)jobid); - return 1; - } - } 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; - } - } - job = deletejob(); - - if (!(push(&jobs, job))) { - note("Unable to add job to background; too many jobs"); - return 1; - } - if (killpg(job->id, SIGCONT) == -1) { - note("Unable to wake up suspended process group %d", job->id); - return 1; - } - job->type = BACKGROUND; - - if (sigaction(SIGCHLD, &sigchld, NULL) == -1) { - note("Unable to install SIGCHLD handler"); - return 1; - } - - return 0; -} - -#define BUILTIN(name) {#name, name} -BUILTINSIG(which); -struct builtin builtins[] = { - BUILTIN(cd), - BUILTIN(fg), - BUILTIN(bg), - BUILTIN(which), - 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 (!argv[1]) return 1; - for (builtin = builtins; builtin->func; ++builtin) - if (strcmp(argv[1], builtin->name) == 0) { - puts("Built-in command\r"); - return 0; - } - - 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) { - for (n = 0; args[n]; ++n); - *statusp = builtinp->func(n, args); - return 1; - } - return 0; -} diff --git a/src/builtins.h b/src/builtins.h deleted file mode 100644 index 0798e58..0000000 --- a/src/builtins.h +++ /dev/null @@ -1 +0,0 @@ -int isbuiltin(char **args, int *statusp); diff --git a/src/config.h b/src/config.h index 97e9448..9e6818a 100644 --- a/src/config.h +++ b/src/config.h @@ -4,6 +4,7 @@ #define INTERACTIVEFILE ".ashinteractive" #define LOGINFILE ".ashlogin" +#define MAXBUILTINS 100 // Maximum number of builtin commands #define MAXCHARS 1000 // Maximum number of character per line #define MAXCMDS 100 // Maximum number of commands per line #define MAXHIST 100 // Maximum number of entries to store in history file diff --git a/src/run.c b/src/run.c index 9adbe86..160cf9b 100644 --- a/src/run.c +++ b/src/run.c @@ -5,7 +5,7 @@ #include #include -#include "builtins.h" +#include "builtin.h" #include "config.h" #include "job.h" #include "parse.h" -- 2.51.0