From a088e851fb45fe38e6a228770fa5d0c04cddb7f5 Mon Sep 17 00:00:00 2001 From: Jakob Kaivo Date: Wed, 13 Mar 2019 21:47:51 -0400 Subject: migrate to gitlab --- Makefile | 43 ++++++++ alias.c | 94 ++++++++++++++++++ bg.c | 8 ++ builtins.c | 167 +++++++++++++++++++++++++++++++ cd.c | 122 +++++++++++++++++++++++ command.c | 72 ++++++++++++++ dot.c | 28 ++++++ eval.c | 39 ++++++++ exec.c | 48 +++++++++ exit.c | 52 ++++++++++ export.c | 100 +++++++++++++++++++ false.c | 24 +++++ fc.c | 8 ++ fg.c | 8 ++ getopts.c | 8 ++ hash.c | 8 ++ init.c | 51 ++++++++++ interactive.c | 58 +++++++++++ jobs.c | 8 ++ kill.c | 101 +++++++++++++++++++ main.c | 105 ++++++++++++++++++++ newgrp.c | 7 ++ parse.c | 178 +++++++++++++++++++++++++++++++++ pwd.c | 64 ++++++++++++ read.c | 68 +++++++++++++ readonly.c | 28 ++++++ set.c | 135 +++++++++++++++++++++++++ sh.h | 68 +++++++++++++ sh.l | 65 ++++++++++++ sh.y | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ shift.c | 28 ++++++ times.c | 51 ++++++++++ trap.c | 183 ++++++++++++++++++++++++++++++++++ true.c | 24 +++++ type.c | 7 ++ ulimit.c | 61 ++++++++++++ umask.c | 68 +++++++++++++ unset.c | 28 ++++++ wait.c | 8 ++ 39 files changed, 2535 insertions(+) create mode 100644 Makefile create mode 100644 alias.c create mode 100644 bg.c create mode 100644 builtins.c create mode 100644 cd.c create mode 100644 command.c create mode 100644 dot.c create mode 100644 eval.c create mode 100644 exec.c create mode 100644 exit.c create mode 100644 export.c create mode 100644 false.c create mode 100644 fc.c create mode 100644 fg.c create mode 100644 getopts.c create mode 100644 hash.c create mode 100644 init.c create mode 100644 interactive.c create mode 100644 jobs.c create mode 100644 kill.c create mode 100644 main.c create mode 100644 newgrp.c create mode 100644 parse.c create mode 100644 pwd.c create mode 100644 read.c create mode 100644 readonly.c create mode 100644 set.c create mode 100644 sh.h create mode 100644 sh.l create mode 100644 sh.y create mode 100644 shift.c create mode 100644 times.c create mode 100644 trap.c create mode 100644 true.c create mode 100644 type.c create mode 100644 ulimit.c create mode 100644 umask.c create mode 100644 unset.c create mode 100644 wait.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8e9252e --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +.POSIX: + +.SUFFIXES: .cat .msg + +default: all + +CFLAGS=-g -Wall -Wextra -Wpedantic -Werror -Wno-unused-function + +UTILITY=sh +SOURCES=alias.c bg.c builtins.c cd.c command.c false.c fc.c fg.c getopts.c \ + hash.c jobs.c kill.c newgrp.c pwd.c read.c set.c main.c true.c type.c \ + ulimit.c umask.c wait.c \ + dot.c eval.c exec.c exit.c export.c readonly.c shift.c times.c trap.c \ + unset.c \ + interactive.c parse.c init.c +HEADERS=sh.h +OBJECTS=alias.o bg.o builtins.o cd.o command.o false.o fc.o fg.o getopts.o \ + hash.o jobs.o kill.o newgrp.o pwd.o read.o set.o main.o true.o type.o \ + ulimit.o umask.o wait.o \ + dot.o eval.o exec.o exit.o export.o readonly.o shift.o times.o trap.o \ + unset.o \ + interactive.o parse.o init.o \ + sh.tab.o sh.yy.o +BUILTINS=alias bg cd command false fc fg getopts jobs kill newgrp pwd read \ + true ulimit umask unalias wait + +all: $(UTILITY) $(L10N) $(BUILTINS) + +sh: $(OBJECTS) $(HEADERS) + $(CC) -o $@ $(CFLAGS) $(OBJECTS) -ly -ll + +$(BUILTINS): $(UTILITY) + #ln -s $(UTILITY) $@ + for i in $(BUILTINS); do ln -s sh $$i; done + +sh.yy.c: sh.l sh.tab.h + $(LEX) -t sh.l > $@ + +sh.tab.h sh.tab.c: sh.y + $(YACC) -b sh -tvd sh.y + +clean: + rm -f *.o $(L10N) $(UTILITY) $(BUILTINS) sh.tab.[ch] sh.yy.c diff --git a/alias.c b/alias.c new file mode 100644 index 0000000..23c557a --- /dev/null +++ b/alias.c @@ -0,0 +1,94 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include + +struct alias { + char *alias; + char *command; +}; + +static struct alias *aliases = NULL; + +static void set_alias(const char *alias, const char *command) +{ + (void)alias; (void)command; +} + +char * sh_get_alias(const char *alias) +{ + if (aliases == NULL) { + return NULL; + } + + for (int i = 0; aliases[i].alias; i++) { + if (!strcmp(aliases[i].alias, alias)) { + return aliases[i].command; + } + } + + return NULL; +} + +static void show_alias(const char *alias) +{ + char *command = sh_get_alias(alias); + if (command) { + printf("%s=%s\n", alias, command); + } else { + fprintf(stderr, "alias: %s: not found\n", alias); + } +} + +int alias_main(int argc, char *argv[]) +{ + while (getopt(argc, argv, "") != -1) { + return 1; + } + + if (argv[optind] == NULL) { + for (int i = 0; aliases && aliases[i].alias; i++) { + printf("%s=%s\n", aliases[i].alias, aliases[i].command); + } + return 0; + } + + do { + char *a = argv[optind++]; + char *eq = strchr(a, '='); + if (eq) { + *eq = '\0'; + set_alias(a, eq + 1); + } else { + show_alias(a); + } + } while (optind < argc); + + return 0; +} + +int unalias_main(int argc, char **argv) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} diff --git a/bg.c b/bg.c new file mode 100644 index 0000000..e700864 --- /dev/null +++ b/bg.c @@ -0,0 +1,8 @@ +#define _XOPEN_SOURCE 700 +#include + +int bg_main(int argc, char *argv[]) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} diff --git a/builtins.c b/builtins.c new file mode 100644 index 0000000..886ebaf --- /dev/null +++ b/builtins.c @@ -0,0 +1,167 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include + +struct builtin { + const char *name; + int (*main)(int argc, char *argv[]); +}; + +/* Special built-ins */ +/* break */ +/* : */ +/* continue */ +int dot_main(int argc, char *argv[]); +int eval_main(int argc, char *argv[]); +int exec_main(int argc, char *argv[]); +int exit_main(int argc, char *argv[]); +int export_main(int argc, char *argv[]); +int readonly_main(int argc, char *argv[]); +/* return */ +int set_main(int argc, char *argv[]); +int shift_main(int argc, char *argv[]); +int times_main(int argc, char *argv[]); +int trap_main(int argc, char *argv[]); +int unset_main(int argc, char *argv[]); + +static struct builtin special_builtins[] = { + { "break", NULL }, + { ":", NULL }, + { "continue", NULL }, + { ".", dot_main }, + { "eval", eval_main }, + { "exec", exec_main }, + { "exit", exit_main }, + { "export", export_main }, + { "readonly", readonly_main }, + { "return", NULL }, + { "set", set_main }, + { "shift", shift_main }, + { "times", times_main }, + { "trap", trap_main }, + { "unset", unset_main }, + { 0, 0 }, +}; + +/* Regular built-ins */ +int alias_main(int argc, char *argv[]); +int bg_main(int argc, char *argv[]); +int cd_main(int argc, char *argv[]); +int command_main(int argc, char *argv[]); +int false_main(int argc, char *argv[]); +int fc_main(int argc, char *argv[]); +int fg_main(int argc, char *argv[]); +int getopts_main(int argc, char *argv[]); +int jobs_main(int argc, char *argv[]); +int kill_main(int argc, char *argv[]); +int newgrp_main(int argc, char *argv[]); +int pwd_main(int argc, char *argv[]); +int read_main(int argc, char *argv[]); +int true_main(int argc, char *argv[]); +int ulimit_main(int argc, char *argv[]); +int umask_main(int argc, char *argv[]); +int unalias_main(int argc, char *argv[]); +int wait_main(int argc, char *argv[]); + +static struct builtin regular_builtins[] = { + { "alias", alias_main }, + { "bg", bg_main }, + { "cd", cd_main }, + { "command", command_main }, + { "false", false_main }, + { "fc", fc_main }, + { "fg", fg_main }, + { "getopts", getopts_main }, + { "jobs", jobs_main }, + { "kill", kill_main }, + { "newgrp", newgrp_main }, + { "pwd", pwd_main }, + { "read", read_main }, + { "true", true_main }, + { "ulimit", ulimit_main }, + { "umask", umask_main }, + { "unalias", unalias_main }, + { "wait", wait_main }, + { 0, 0 }, +}; + +static int (*sh_getbuiltin(const char *util, struct builtin *bi))(int argc, char *argv[]) +{ + for (int i = 0; bi[i].name; i++) { + if (!strcmp(bi[i].name, util)) { + return bi[i].main; + } + } + + return NULL; +} + +int sh_builtin(int argc, char *argv[]) +{ + char *util = basename(argv[0]); + if (!strcmp(util, "break")) { + return BREAK; + } else if (!strcmp(util, ":")) { + return 0; + } else if (!strcmp(util, "continue")) { + return CONTINUE; + } else if (!strcmp(util, "return")) { + return RETURN; + } + + int (*m)(int, char *[]) = sh_getbuiltin(util, special_builtins); + if (m == NULL) { + m = sh_getbuiltin(util, regular_builtins); + if (m == NULL) { + return 1; + } + } + + optind = 1; + opterr = 1; + optarg = NULL; + + return m(argc, argv); +} + +static int is_builtin(const char *util, struct builtin *bi) +{ + for (int i = 0; bi[i].name; i++) { + if (!strcmp(bi[i].name, util)) { + return 1; + } + } + + return 0; +} + +int sh_is_special_builtin(const char *util) +{ + return is_builtin(util, special_builtins); +} + +int sh_is_regular_builtin(const char *util) +{ + return is_builtin(util, regular_builtins); +} diff --git a/cd.c b/cd.c new file mode 100644 index 0000000..a914c19 --- /dev/null +++ b/cd.c @@ -0,0 +1,122 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include +#include +#include + +#define FLAG_L (1) +#define FLAG_P (2) + +static int cd(char *path, int flag) +{ + int alloced = 0; + int print = 0; + + if (path == NULL) { + path = getenv("HOME"); + if (path == NULL || !strcmp(path, "")) { + fprintf(stderr, "cd: Directory not specified and HOME not found\n"); + return 1; + } + } + + if (!strcmp(path, "-")) { + path = getenv("OLDPWD"); + print = 1; + } + + /* + if (!(path[0] == '/' || !strncmp(path, "./", 2) || !strncmp(path, "../", 3))) { + char *opath = path; + path = sh_find_in_path(opath, "CDPATH"); + if (path == NULL) { + fprintf(stderr, "cd: %s not found in CDPATH\n", opath); + return 1; + } + alloced = 1; + } + */ + + if (flag != FLAG_P) { + if (path[0] != '/') { + char *base = getenv("PWD"); + char newpath[strlen(base) + strlen(path)]; + sprintf(newpath, "%s/%s", base, path); + strcpy(path, newpath); + } + + /* TODO: canonicalize path */ + /* TODO: shorten to <= PATH_MAX */ + } + + char *oldpwd = getenv("PWD"); + if (chdir(path) != 0) { + if (alloced) { + free(path); + } + + fprintf(stderr, "cd: Couldn't change to %s: %s\n", path, strerror(errno)); + return 1; + } + + if (alloced) { + free(path); + } + + if (print) { + puts(path); + } + + setenv("OLDPWD", oldpwd, 1); + setenv("PWD", path, 1); + return 0; +} + +int cd_main(int argc, char *argv[]) +{ + int flag = 0; + int c; + + while ((c = getopt(argc, argv, ":LP")) != -1) { + switch (c) { + case 'L': + flag = FLAG_L; + break; + + case 'P': + flag = FLAG_P; + break; + + default: + return 1; + } + } + + if (optind < argc - 1) { + fprintf(stderr, "cd: too many arguments\n"); + return 1; + } + + return cd(argv[optind], flag); +} diff --git a/command.c b/command.c new file mode 100644 index 0000000..0307927 --- /dev/null +++ b/command.c @@ -0,0 +1,72 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include + +static int show(const char *cmd, const char *path, bool showtype) +{ + (void)cmd; (void)path; (void)showtype; + return 0; +} + +int command_main(int argc, char *argv[]) +{ + char *path = getenv("PATH"); + char defaultpath[PATH_MAX]; + bool showcommand = false; + bool showtype = false; + + int c = 0; + while ((c = getopt(argc, argv, "pvV")) != -1) { + switch(c) { + case 'p': + confstr(_CS_PATH, defaultpath, sizeof(defaultpath)); + path = defaultpath; + break; + + case 'V': + showtype = true; + /* fallthru */ + case 'v': + showcommand = true; + break; + + default: + return 1; + } + } + + if (showcommand && optind != argc - 1) { + fprintf(stderr, "command: Options -v and -V require command and no arguments\n"); + return 1; + } + + if (showcommand) { + return show(argv[optind], path, showtype); + } + + /* Run command */ + + return 0; +} diff --git a/dot.c b/dot.c new file mode 100644 index 0000000..36f5e59 --- /dev/null +++ b/dot.c @@ -0,0 +1,28 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include + +int dot_main(int argc, char *argv[]) +{ + printf("%s: not implemented\n", argv[0]); + return argc; +} diff --git a/eval.c b/eval.c new file mode 100644 index 0000000..1a3d895 --- /dev/null +++ b/eval.c @@ -0,0 +1,39 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include + +struct sh_redirect *sh_add_redirect(int fd, int direction, char *name) +{ + struct sh_redirect *red = malloc(sizeof(*red)); + red->fd = fd; + red->direction = direction; + red->name = strdup(name); + return red; +} + +int eval_main(int argc, char *argv[]) +{ + printf("%s: not implemented\n", argv[0]); + return argc; +} diff --git a/exec.c b/exec.c new file mode 100644 index 0000000..1ebf63c --- /dev/null +++ b/exec.c @@ -0,0 +1,48 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include +#include + +int exec_main(int argc, char *argv[]) +{ + while (getopt(argc, argv, "") != -1) { + return 1; + } + + if (argv[optind] == NULL) { + /* keep redirects for this shell */ + return 0; + } + + char *path = sh_find_in_path(argv[optind], "PATH"); + if (path == NULL) { + fprintf(stderr, "exec: %s: %s\n", argv[optind], strerror(ENOENT)); + exit(127); + } + + execve(path, argv + optind, exported_environ); + fprintf(stderr, "exec: %s: %s\n", argv[optind], strerror(errno)); + exit(126); + return 126; +} diff --git a/exit.c b/exit.c new file mode 100644 index 0000000..0b827f9 --- /dev/null +++ b/exit.c @@ -0,0 +1,52 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include + +int exit_main(int argc, char *argv[]) +{ + static int exiting = 0; + + while (getopt(argc, argv, "") != -1) { + return 1; + } + + if (optind < argc - 1) { + fprintf(stderr, "exit: only one exit status supported\n"); + return 1; + } + + int r = 0; + + if (argv[optind]) { + r = atoi(argv[optind]); + } + + if (exiting == 0) { + exiting = 1; + sh_trap(0); + } + + exit(r); + _exit(r); + return r; +} diff --git a/export.c b/export.c new file mode 100644 index 0000000..214a3d4 --- /dev/null +++ b/export.c @@ -0,0 +1,100 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include + +char **exported_environ = NULL; +static int nexports = 0; + +void sh_export(const char *env) +{ + char *value = getenv(env); + if (value == NULL) { + return; + } + size_t klen = strlen(env); + + char *keyval = calloc(klen + strlen(value) + 2, 1); + sprintf(keyval, "%s=%s", env, value); + + for (int i = 0; i < nexports; i++) { + if (!strncmp(exported_environ[i], env, klen) + && exported_environ[i][klen] == '=') { + free(exported_environ[i]); + exported_environ[i] = keyval; + return; + } + } + + nexports++; + exported_environ = realloc(exported_environ, + nexports * sizeof(*exported_environ)); + exported_environ[nexports-1] = keyval; +} + +int export_main(int argc, char *argv[]) +{ + int print = 0; + + int c; + while ((c = getopt(argc, argv, "p")) != EOF) { + switch (c) { + case 'p': /** print exported variables **/ + print = 1; + break; + + default: + return 1; + } + } + + if (print && argv[optind] != NULL) { + fprintf(stderr, "export: can't display and export at the same time\n"); + return 1; + } + + if (print) { + for (char **e = exported_environ; e && *e; e++) { + char *env = strdup(*e); + size_t len = strlen(env); + if (env[len - 1] == '=') { + env[len - 1] = '\0'; + } + printf("export %s\n", env); + free(env); + } + return 0; + } + + do { + char *var = argv[optind++]; + char *eq = strchr(var, '='); + if (eq) { + putenv(var); + *eq = '\0'; + } + sh_export(var); + } while (optind < argc); + + return 0; +} diff --git a/false.c b/false.c new file mode 100644 index 0000000..0997f7d --- /dev/null +++ b/false.c @@ -0,0 +1,24 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +int false_main(int argc, char *argv[]) +{ + (void)argc; (void)argv; + return 1; +} diff --git a/fc.c b/fc.c new file mode 100644 index 0000000..3eb3b55 --- /dev/null +++ b/fc.c @@ -0,0 +1,8 @@ +#include + +int +fc_main(int argc, char **argv) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} diff --git a/fg.c b/fg.c new file mode 100644 index 0000000..0240ee7 --- /dev/null +++ b/fg.c @@ -0,0 +1,8 @@ +#define _XOPEN_SOURCE 700 +#include + +int fg_main(int argc, char *argv[]) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} diff --git a/getopts.c b/getopts.c new file mode 100644 index 0000000..263b1a2 --- /dev/null +++ b/getopts.c @@ -0,0 +1,8 @@ +#define _XOPEN_SOURCE 700 +#include + +int getopts_main(int argc, char *argv[]) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..1f08dd8 --- /dev/null +++ b/hash.c @@ -0,0 +1,8 @@ +#include + +int +hash_main(int argc, char **argv) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} diff --git a/init.c b/init.c new file mode 100644 index 0000000..fa6c229 --- /dev/null +++ b/init.c @@ -0,0 +1,51 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include + +extern char **environ; + +void sh_init(int interactive) +{ + if (interactive && getuid() == geteuid() && getgid() == getegid()) { + char *env = getenv("ENV"); + if (env) { + /* + char *profile = sh_expand(env); + sh_source(env); + free(profile); + */ + } + } + + /* export all inherited environment variables */ + char **exp = environ; + while (exp && *exp) { + char *e = strdup(*exp); + char *eq = strchr(e, '='); + *eq = '\0'; + sh_export(e); + free(e); + exp++; + } +} diff --git a/interactive.c b/interactive.c new file mode 100644 index 0000000..7460327 --- /dev/null +++ b/interactive.c @@ -0,0 +1,58 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include + +static char *sh_getline(char **cmdline) +{ + char *prompt = getenv(*cmdline ? "PS2" : "PS1"); + /* wordexpand prompt */ + fprintf(stderr, "%s", prompt); + + size_t n = 0; + getline(cmdline, &n, stdin); + return *cmdline; +} + +int sh_interactive(void) +{ + while (!feof(stdin)) { + char *cmdline = NULL; + sh_getline(&cmdline); + + if (cmdline == NULL) { + break; + } + + struct command *command = sh_parse(cmdline); + while (command == NULL) { + /* append more text */ + /* attempt parsing again */ + } + + sh_execute(command); + + free(cmdline); + sh_freecmd(command); + } + + return 0; +} diff --git a/jobs.c b/jobs.c new file mode 100644 index 0000000..5fe1c6e --- /dev/null +++ b/jobs.c @@ -0,0 +1,8 @@ +#define _XOPEN_SOURCE 700 +#include + +int jobs_main(int argc, char *argv[]) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} diff --git a/kill.c b/kill.c new file mode 100644 index 0000000..31cc706 --- /dev/null +++ b/kill.c @@ -0,0 +1,101 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include + +struct { + char *name; + int num; +} ksigs[] = { + { "ABRT", SIGABRT }, + { "ALRM", SIGALRM }, + { "BUS", SIGBUS }, + { "CHLD", SIGCHLD }, + { "CONT", SIGCONT }, + { "FPE", SIGFPE }, + { "HUP", SIGHUP }, + { "ILL", SIGILL }, + { "INT", SIGINT }, + { "KILL", SIGKILL }, + { "PIPE", SIGPIPE }, + { "QUIT", SIGQUIT }, + { "SEGV", SIGSEGV }, + { "STOP", SIGSTOP }, + { "TERM", SIGTERM }, + { "TSTP", SIGTSTP }, + { "TTIN", SIGTTIN }, + { "TTOU", SIGTTOU }, + { "USR1", SIGUSR1 }, + { "USR2", SIGUSR2 }, + { "POLL", SIGPOLL }, + { "PROF", SIGPROF }, + { "SYS", SIGSYS }, + { "TRAP", SIGTRAP }, + { "URG", SIGURG }, + { "VTALRM", SIGVTALRM }, + { "XCPU", SIGXCPU }, + { "XFSZ", SIGXFSZ }, +}; +size_t nsigs = sizeof(ksigs) / sizeof(ksigs[0]); + +int kill_main(int argc, char *argv[]) +{ + char *signame = "TERM"; + int list = 0; + + opterr = 0; + int c; + while ((c = getopt(argc, argv, "s:l") != -1)) { + switch (c) { + case 's': /** send signal [signal_name] **/ + signame = optarg; + break; + + case 'l': + list = 1; + break; + + default: + if (isdigit(optopt) || isupper(optopt)) { + signame = argv[optind]; + break; + } + fprintf(stderr, "kill: unknown option '-%c'\n", optopt); + //return 1; + } + } + + if (list) { + if (argv[optind]) { + } + + for (size_t i = 0; i < nsigs; i++) { + printf("%s%c", ksigs[i].name, i < nsigs - 1 ? ' ' : '\n'); + } + return 0; + } + + printf("%s\n", signame); + + return 0; +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..61b55f3 --- /dev/null +++ b/main.c @@ -0,0 +1,105 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include +#include +#include + +int sh_main(int argc, char *argv[]) +{ + enum { STDIN, INTERACTIVE, COMMAND } mode = STDIN; + int c; + while ((c = getopt(argc, argv, "abCefhimnuvxo:cs")) != -1) { + switch (c) { + case 'a': /** export all variables **/ + case 'b': /** notify of background job completions **/ + case 'C': /** don't clobber existing files with > **/ + case 'e': /** exit on failure **/ + case 'f': /** disable pathname expansion **/ + case 'h': /** hash utilities in functions when defined **/ + case 'n': /** read commands but don't execute **/ + case 'u': /** don't expand unset parameters **/ + case 'v': /** echo input to /stderr/ **/ + case 'x': /** enable tracing **/ + sh_set(c, 1); + break; + + case 'o': /** enable [option] **/ + sh_seto(optarg, 1); + break; + + case 'c': /** execute [command_string] and return **/ + mode = COMMAND; + break; + + case 'i': /** run an interactive shell **/ + mode = INTERACTIVE; + break; + + case 's': /** read commands from /stdin/ **/ + mode = STDIN; + break; + + default: + return 1; + } + } + + while (argv[optind] && argv[optind][0] == '+') { + /* consume these */ + optind++; + } + + if (mode == COMMAND) { + /* shift argv */ + execvp(argv[optind], argv + optind); + fprintf(stderr, "sh: %s: %s\n", argv[optind], strerror(errno)); + return 1; + } + + if (argv[optind] == NULL && isatty(STDIN_FILENO) && isatty(STDERR_FILENO)) { + mode = INTERACTIVE; + } + + sh_init(mode == INTERACTIVE); + + /* set $1-$# */ + + if (mode == INTERACTIVE) { + return sh_interactive(); + } + + /* mode is STDIN */ + return yyparse(); +} + +int main(int argc, char *argv[]) +{ + putenv("PS1=$ "); + char *base = basename(argv[0]); + if (sh_is_regular_builtin(base)) { + return sh_builtin(argc, argv); + } + + return sh_main(argc, argv); +} diff --git a/newgrp.c b/newgrp.c new file mode 100644 index 0000000..3148df1 --- /dev/null +++ b/newgrp.c @@ -0,0 +1,7 @@ +#include + +int newgrp_main(int argc, char **argv) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..91ce97f --- /dev/null +++ b/parse.c @@ -0,0 +1,178 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include +#include +#include +#include + +struct simple_command { + int argc; + char **argv; + char **redirection; +}; + +struct pipeline_command { + struct simple_command s; +}; + +struct list_command { + struct simple_command s; +}; + +struct compound_command { + enum { GROUPING, FOR, CASE, IF, UNTIL } type; + struct simple_command *commands; +}; + +struct command { + enum { EMPTY, SIMPLE, PIPELINE, LIST, COMPOUND } type; + union { + struct simple_command simple; + struct pipeline_command pipeline; + struct list_command list; + struct compound_command compound; + } cmd; +}; + +struct command *sh_parse(const char *cmdline) +{ + char *l = strdup(cmdline); + char *start = l; + while (isspace(*start)) { + start++; + } + + char *end = l + strlen(l) - 1; + while (isspace(*end) && end > start) { + *end = '\0'; + end--; + } + + struct command *cmd = calloc(1, sizeof(*cmd)); + if (cmd == NULL) { + /* TODO: should probably crash here */ + return cmd; + } + + cmd->type = SIMPLE; + cmd->cmd.simple.argv = calloc(100, sizeof(char *)); + + char *next = strtok(start, " "); + do { + cmd->cmd.simple.argv[cmd->cmd.simple.argc++] = strdup(next); + } while ((next = strtok(NULL, " ")) != NULL); + + free(l); + return cmd; +} + +void sh_freecmd(struct command *command) +{ + if (command->type == SIMPLE) { + struct simple_command *c = &(command->cmd.simple); + for (int i = 0; c->argv[i]; i++) { + free(c->argv[i]); + } + } + free(command); +} + +char *sh_find_in_path(const char *file, const char *pathvar) +{ + char *path = getenv(pathvar); + if (path == NULL) { + return NULL; + } + + path = strdup(path); + char *checkpath = strtok(path, ":"); + do { + char fullpath[strlen(checkpath) + strlen(file) + 2]; + sprintf(fullpath, "%s/%s", checkpath, file); + if (access(fullpath, F_OK) == 0) { + free(path); + return strdup(fullpath); + } + } while ((checkpath = strtok(NULL, ":")) != NULL); + + free(path); + return NULL; +} + +int sh_simple_command(struct simple_command *c) +{ + char *path = c->argv[0]; + + if (!strchr(path, '/')) { + if (sh_is_special_builtin(path)) { + return sh_builtin(c->argc, c->argv); + } + + /* + if (sh_is_function(path)) { + return sh_function(c->argc, c->argv); + } + */ + + if (sh_is_regular_builtin(path)) { + return sh_builtin(c->argc, c->argv); + } + + path = sh_find_in_path(path, "PATH"); + } + + if (path == NULL) { + fprintf(stderr, "sh: %s: %s\n", c->argv[0], strerror(ENOENT)); + } + + pid_t pid = fork(); + if (pid == 0) { + execv(path, c->argv/*, exported_environ*/); + fprintf(stderr, "sh: %s: %s\n", path, strerror(errno)); + exit(1); + } + free(path); + + if (pid < 0) { + perror("sh"); + return 1; + } + + int rv; + waitpid(pid, &rv, 0); + return rv; +} + +int sh_execute(struct command *command) +{ + if (command->type == EMPTY) { + return 0; + } + + if (command->type == SIMPLE) { + return sh_simple_command(&(command->cmd.simple)); + } + + return 0; +} diff --git a/pwd.c b/pwd.c new file mode 100644 index 0000000..00e26e2 --- /dev/null +++ b/pwd.c @@ -0,0 +1,64 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include + +const char *pwd_desc = "return working directory name"; +const char *pwd_inv = "pwd [-L|-P]"; + +int +pwd_main (int argc, char **argv) +{ + //char mode = 'L'; + char *cwd; + int c; + + while ((c = getopt (argc, argv, ":LP")) != -1) { + switch (c) { + case 'L': + case 'P': + ////mode = c; + break; + default: + return 1; + } + } + + if (optind < argc) + return 1; + + // FIXME: + // if (mode == 'L') + // if (cwd !~ /./ && cwd !~ /../) + // print (cwd); + // else if (strlen (cwd) > PATH_MAX && NO_DOT_OR_DOT_DOT) + // unspecified + // else + // mode = 'P'; + // if (mode == 'P') + // decompose_to_shortest_path + // print (decomposed) + cwd = (char*)getenv ("PWD"); + printf ("%s\n", cwd); + + return 0; +} diff --git a/read.c b/read.c new file mode 100644 index 0000000..c60255f --- /dev/null +++ b/read.c @@ -0,0 +1,68 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include + +int read_main(int argc, char *argv[]) +{ + int escapes = 1; + + int c; + while ((c = getopt(argc, argv, "r")) != -1) { + switch (c) { + case 'r': /** do not interpet \ escapes **/ + escapes = 0; + break; + + default: + return 1; + } + } + + if (argv[optind] == NULL) { + fprintf(stderr, "read: at least one variable required\n"); + return 1; + } + + while (argv[optind]) { + char *line = NULL; + size_t n = 0; + + getline(&line, &n, stdin); + + /* strip trailing newline */ + if (escapes) { + } + + setenv(argv[optind], line, 1); + + if (sh_get('a')) { + sh_export(argv[optind]); + } + + free(line); + + optind++; + } + + return 0; +} diff --git a/readonly.c b/readonly.c new file mode 100644 index 0000000..93809cc --- /dev/null +++ b/readonly.c @@ -0,0 +1,28 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include + +int readonly_main(int argc, char *argv[]) +{ + printf("%s: not implemented\n", argv[0]); + return argc; +} diff --git a/set.c b/set.c new file mode 100644 index 0000000..de7f288 --- /dev/null +++ b/set.c @@ -0,0 +1,135 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include + +static struct option { + char c; + char *s; + int value; +} sh_options[] = { + { 'a', "allexport", 0 }, + { 'b', NULL, 0 }, + { 'C', "noclobber", 0 }, + { 'e', "errexit", 0 }, + { 'f', "noglob", 0 }, + { 'h', NULL, 0 }, + { 'm', "monitor", 0 }, + { 'n', NULL, 0 }, + { 'u', "nounset", 0 }, + { 'v', "verbose", 0 }, + { 'x', "xtrace", 0 }, + { '-', "ignoreeof", 0 }, + { '-', "nolog", 0 }, + { '-', "vi", 0 }, +}; +static size_t noptions = sizeof(sh_options) / sizeof(sh_options[0]); + +void sh_set(char option, int value) +{ + for (size_t i = 0; i < noptions; i++) { + if (sh_options[i].c == option) { + sh_options[i].value = value; + return; + } + } +} + +int sh_get(char option) +{ + for (size_t i = 0; i < noptions; i++) { + if (sh_options[i].c == option) { + return sh_options[i].value; + } + } + return 0; +} + +void sh_seto(char *option, int value) +{ + for (size_t i = 0; i < noptions; i++) { + if (sh_options[i].s && !strcmp(sh_options[i].s, option)) { + sh_options[i].value = value; + return; + } + } +} + +int sh_geto(char *option) +{ + for (size_t i = 0; i < noptions; i++) { + if (sh_options[i].s && !strcmp(sh_options[i].s, option)) { + return sh_options[i].value; + } + } + return 0; +} + +int set_main(int argc, char *argv[]) +{ + int show = 0; + + int c; + while ((c = getopt(argc, argv, ":abCefhmnuvxo:")) != -1) { + switch (c) { + case 'a': + case 'b': + case 'C': + case 'e': + case 'f': + case 'h': + case 'm': + case 'n': + case 'u': + case 'v': + case 'x': + sh_set(c, 1); + break; + + case 'o': + sh_seto(optarg, 1); + break; + + case ':': + if (optopt == 'o') { + show = 1; + } else { + return 1; + } + break; + + default: + return 1; + } + } + + if (show) { + for (size_t i = 0; i < noptions; i++) { + if (sh_options[i].value) { + putchar(sh_options[i].c); + } + } + putchar('\n'); + } + + return 0; +} diff --git a/sh.h b/sh.h new file mode 100644 index 0000000..ba43bac --- /dev/null +++ b/sh.h @@ -0,0 +1,68 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 200809L + +struct sh_redirect { + int fd; + enum { WRITE, APPEND, OVERWRITE, WRITEFD, READWRITE, READ, READFD, READHERE, READDASH } direction; + char *name; +}; + +struct sh_redirect *sh_add_redirect(int fd, int direction, char *name); + +struct sh_command { + int argc; + char *cmd; + char **argv; + struct sh_redirect *redirect; +}; + +struct command; +struct command *sh_parse(const char *cmdline); +void sh_freecmd(struct command *cmd); +int sh_execute(struct command *cmd); + +void sh_set(char option, int value); +void sh_seto(char *option, int value); +int sh_get(char option); +int sh_geto(char *option); + +int sh_interactive(void); + +int sh_is_special_builtin(const char *util); +int sh_is_regular_builtin(const char *util); +int (*sh_get_builtin(const char *util))(int argc, char *argv[]); +int sh_builtin(int argc, char *argv[]); + +enum { BREAK = -1, CONTINUE = -2, RETURN = -3 }; + +char *sh_find_in_path(const char *file, const char *pathvar); + +void sh_export(const char *var); +extern char **exported_environ; + +void sh_init(int interactive); + +enum { HUP = 1, INT = 2, QUIT = 3, ABRT = 6, KILL = 9, ALRM = 14, TERM = 15 }; + +void sh_trap(int trapno); + +int yyparse(void); diff --git a/sh.l b/sh.l new file mode 100644 index 0000000..539a251 --- /dev/null +++ b/sh.l @@ -0,0 +1,65 @@ +%{ +#include "sh.h" +#include "sh.tab.h" +/* thanks, flex, for implicitly declaring identifiers */ +/* bonus points for something that isn't part of ISO C */ +extern int fileno(FILE *); +%} + +NAME [_a-zA-Z0-9] +NONDIGIT [_a-zA-Z] +DIGIT [0-9] +WHITESPACE [ \t] +OPERATOR [()|;&<>] + +%s FOR +%x COMMENT + +%% + +"#" { BEGIN COMMENT; } +. ; +\n { BEGIN INITIAL; } + +"&&"/. { return AND_IF; } +"||"/. { return OR_IF; } +";;"/. { return DSEMI; } +"<<"/. { return DLESS; } +">>"/. { return DGREAT; } +"<&"/. { return LESSAND; } +">&"/. { return GREATAND; } +"<>"/. { return LESSGREAT; } +"<<-"/. { return DLESSDASH; } +">|"/. { return CLOBBER; } + +"if" { return If; } +"then" { return Then; } +"else" { return Else; } +"elif" { return Elif; } +"fi" { return Fi; } +"do" { return Do; } +"done" { BEGIN INITIAL; return Done; } +"case" { return Case; } +"esac" { return Esac; } +"while" { return While; } +"until" { return Until; } +"for" { BEGIN FOR; return For; } + +"{" { return Lbrace; } +"}" { return Rbrace; } +"!" { return Bang; } + +"in" { return In; } + +{OPERATOR}/. { return yytext[0]; } + +{DIGIT}+/< { yylval.n = atoi(yytext); return IO_NUMBER; } +{DIGIT}+/> { yylval.n = atoi(yytext); return IO_NUMBER; } + +\n { return NEWLINE; } + +{NONDIGIT}{NAME}* { yylval.s = yytext; return NAME; } + +[^ \t\n()|;&<>]+ { yylval.s = yytext; return WORD; } + +{WHITESPACE} ; diff --git a/sh.y b/sh.y new file mode 100644 index 0000000..14ad022 --- /dev/null +++ b/sh.y @@ -0,0 +1,312 @@ +%{ +#include "sh.h" +#include +#include +#include + +/* Thanks, Bison, for implicitly declaring identifiers */ +extern int yylex(void); +extern int yyerror(const char *); +extern char *yytext; +%} + +%union { + int n; + char *s; + struct sh_command *cmd; + struct sh_redirect *redir; +} + +%token WORD ASSIGNMENT_WORD NAME +%token NEWLINE +%token IO_NUMBER +%token AND_IF OR_IF DSEMI +%token DLESS DGREAT LESSAND GREATAND LESSGREAT DLESSDASH +%token CLOBBER +%token If Then Else Elif Fi Do Done +%token Case Esac While Until For +%token Lbrace Rbrace Bang +%token In + + /* %type simple_command */ +%type filename here_end cmd_name cmd_word +%type io_redirect io_file io_here + +%start complete_command +%% +complete_command : list separator + | list + ; + +list : list separator_op and_or + | and_or + ; + +and_or : pipeline + | and_or AND_IF linebreak pipeline + | and_or OR_IF linebreak pipeline + ; + +pipeline : pipe_sequence + | Bang pipe_sequence + ; + +pipe_sequence : command + | pipe_sequence '|' linebreak command + ; + +command : simple_command + | compound_command + | compound_command redirect_list + | function_definition + ; + +compound_command : brace_group + | subshell + | for_clause + | case_clause + | if_clause + | while_clause + | until_clause + ; + +subshell : '(' compound_list ')' + ; + +compound_list : term + | newline_list term + | term separator + | newline_list term separator + ; + +term : term separator and_or + | and_or + ; + +for_clause : For name linebreak do_group + | For name linebreak in sequential_sep do_group + | For name linebreak in wordlist sequential_sep do_group + ; + +name : NAME { /* apply rule 5 */ + printf("NAME: %s\n", yytext); + } + ; + +in : In /* apply rule 6 */ + ; + +wordlist : wordlist WORD + | WORD { + printf("WORD: %s\n", yytext); + } + ; + +case_clause : Case WORD linebreak in linebreak case_list Esac + | Case WORD linebreak in linebreak case_list_ns Esac + | Case WORD linebreak in linebreak Esac + ; + +case_list_ns : case_list case_item_ns + | case_item_ns + ; + +case_list : case_list case_item + | case_item + ; + +case_item_ns : pattern ')' linebreak + | pattern ')' compound_list linebreak + | '(' pattern ')' linebreak + | '(' pattern ')' compound_list linebreak + ; + +case_item : pattern ')' linebreak DSEMI linebreak + | pattern ')' compound_list DSEMI linebreak + | '(' pattern ')' linebreak DSEMI linebreak + | '(' pattern ')' compound_list DSEMI linebreak + ; + +pattern : WORD /* apply rule 4 */ + | pattern '|' WORD /* do *NOT* apply rule 4 */ + ; + +if_clause : If compound_list Then compound_list else_part Fi + | If compound_list Then compound_list Fi + ; + +else_part : Elif compound_list Then compound_list + | Elif compound_list Then compound_list else_part + | Else compound_list + ; + +while_clause : While compound_list do_group + ; + +until_clause : Until compound_list do_group + ; + +function_definition : fname '(' ')' linebreak function_body + +function_body : compound_command /* apply rule 9 */ + | compound_command redirect_list /* apply rule 9 */ + ; + +fname : NAME /* apply rule 8 */ + ; + +brace_group : Lbrace compound_list Rbrace + ; + +do_group : Do compound_list Done /* apply rule 6 */ + ; + +simple_command : cmd_prefix cmd_word cmd_suffix + | cmd_prefix cmd_word { + printf("simple_command:cmd_prefix '%s'\n", $2); + } + | cmd_prefix + | cmd_name cmd_suffix { + printf("simple_command:'%s' cmd_suffix\n", $1); + } + | cmd_name { + printf("simple_command:'%s'\n", $1); + } + ; + +cmd_name : WORD { + /* rule 7a */ + $$ = strdup($1); + } + ; + +cmd_word : WORD { + /* rule 7b */ + $$ = strdup($1); + } + ; + +cmd_prefix : io_redirect + | cmd_prefix io_redirect + | ASSIGNMENT_WORD + | cmd_prefix ASSIGNMENT_WORD + ; + +cmd_suffix : io_redirect + | cmd_suffix io_redirect + | WORD { + printf("cmd_suffix:WORD:%s\n", $1); + } + | cmd_suffix WORD { + printf("cmd_suffix:cmd_suffix WORD:%s\n", $2); + } + ; + +redirect_list : io_redirect { + printf("Redirect fd %d, dir %d, name %s\n", $1->fd, $1->direction, $1->name); + } + | redirect_list io_redirect { + printf("Redirect fd %d, dir %d, name %s\n", $2->fd, $2->direction, $2->name); + } + ; + +io_redirect : io_file { + $$ = $1; + printf("Redirect fd %d to %s\n", $1->fd, $1->name); + } + | IO_NUMBER io_file { + $2->fd = $1; + printf("Redirect fd %d to %s\n", $2->fd, $2->name); + $$ = $2; + } + | io_here { + printf("Redirect fd %d until %s\n", $1->fd, $1->name); + $$ = $1; + } + | IO_NUMBER io_here { + $2->fd = $1; + printf("Redirect fd %d until %s\n", $2->fd, $2->name); + $$ = $2; + } + ; + +io_file : '<' filename { + $$ = sh_add_redirect(STDIN_FILENO, READ, $2); + } + | LESSAND filename { + $$ = sh_add_redirect(STDIN_FILENO, READFD, $2); + } + | '>' filename { + $$ = sh_add_redirect(STDOUT_FILENO, WRITE, $2); + } + | GREATAND filename { + $$ = sh_add_redirect(STDOUT_FILENO, WRITEFD, $2); + } + | DGREAT filename { + $$ = sh_add_redirect(STDOUT_FILENO, APPEND, $2); + } + | LESSGREAT filename { + $$ = sh_add_redirect(-1, READWRITE, $2); + } + | CLOBBER filename { + $$ = sh_add_redirect(STDOUT_FILENO, OVERWRITE, $2); + } + ; + +filename : WORD { + /* rule 2 */ + $$ = yytext; + } + ; + +io_here : DLESS here_end { + $$ = sh_add_redirect(STDIN_FILENO, READHERE, $2); + } + | DLESSDASH here_end { + $$ = sh_add_redirect(STDIN_FILENO, READDASH, $2); + } + ; + +here_end : WORD { + /* rule 3 */ + $$ = yytext; + } + ; + +newline_list : NEWLINE { + puts("newline_list:NEWLINE"); + } + | newline_list NEWLINE { + puts("newline_list:newline_list:NEWLINE"); + } + ; + +linebreak : newline_list { + puts("linebreak:newline_list"); + } + | /* empty */ + ; + +separator_op : '&' { + puts("separator_op:&"); + } + | ';' { + puts("separator_op:;"); + } + ; + +separator : separator_op linebreak { + puts("separator: separator_op linebreak"); + } + | newline_list { + puts("separator:newline_list"); + } + ; + +sequential_sep : ';' linebreak { + puts("sequential_sep:; linebreak"); + } + | newline_list { + puts("sequential_sep:newline_list"); + } + ; diff --git a/shift.c b/shift.c new file mode 100644 index 0000000..01ff3c7 --- /dev/null +++ b/shift.c @@ -0,0 +1,28 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include + +int shift_main(int argc, char *argv[]) +{ + printf("%s: not implemented\n", argv[0]); + return argc; +} diff --git a/times.c b/times.c new file mode 100644 index 0000000..d70c153 --- /dev/null +++ b/times.c @@ -0,0 +1,51 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include + +#define clocktos(c) ((double)(c) / (double)CLOCKS_PER_SEC) +#define clocktom(c) ((long)clocktos(c) / 60) +#define clocktof(c) ((double)(clocktos(c)) - (double)(clocktom(c) * 60)) + +/* The Standard specifies an exit status of "Zero." with no other options. */ + +int times_main(int argc, char *argv[]) +{ + while (getopt(argc, argv, "") != -1) { + /* produce warning only */ + } + + if (argv[optind]) { + fprintf(stderr, "times: no arguments supported\n"); + /* keep going */ + } + + struct tms tms; + times(&tms); + printf("%ldm%fs %ldm%fs\n%ldm%fs %ldm%fs\n", + clocktom(tms.tms_utime), clocktof(tms.tms_utime), + clocktom(tms.tms_stime), clocktof(tms.tms_stime), + clocktom(tms.tms_cutime), clocktof(tms.tms_cutime), + clocktom(tms.tms_cstime), clocktof(tms.tms_cstime)); + return 0; +} diff --git a/trap.c b/trap.c new file mode 100644 index 0000000..20cb65b --- /dev/null +++ b/trap.c @@ -0,0 +1,183 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include +#include + +static struct { + char *name; + char *value; +} traps[] = { + [0] = { "EXIT", 0 }, + [SIGABRT] = { "ABRT", 0 }, + [SIGALRM] = { "ALRM", 0 }, + [SIGBUS] = { "BUS", 0 }, + [SIGCHLD] = { "CHLD", 0 }, + [SIGCONT] = { "CONT", 0 }, + [SIGFPE] = { "FPE", 0 }, + [SIGHUP] = { "HUP", 0 }, + [SIGILL] = { "ILL", 0 }, + [SIGINT] = { "INT", 0 }, + [SIGKILL] = { "KILL", 0 }, + [SIGPIPE] = { "PIPE", 0 }, + [SIGQUIT] = { "QUIT", 0 }, + [SIGSEGV] = { "SEGV", 0 }, + [SIGSTOP] = { "STOP", 0 }, + [SIGTERM] = { "TERM", 0 }, + [SIGTSTP] = { "TSTP", 0 }, + [SIGTTIN] = { "TTIN", 0 }, + [SIGTTOU] = { "TTOU", 0 }, + [SIGUSR1] = { "USR1", 0 }, + [SIGUSR2] = { "USR2", 0 }, + [SIGPOLL] = { "POLL", 0 }, + [SIGPROF] = { "PROF", 0 }, + [SIGSYS] = { "SYS", 0 }, + [SIGTRAP] = { "TRAP", 0 }, + [SIGURG] = { "URG", 0 }, + [SIGVTALRM] = { "VTALRM", 0 }, + [SIGXCPU] = { "XCPU", 0 }, + [SIGXFSZ] = { "XFSZ", 0 }, +}; +static size_t ntraps = sizeof(traps) / sizeof(traps[0]); + +static char *tnums[] = { + [0] = "EXIT", + [HUP] = "HUP", + [INT] = "INT", + [QUIT] = "QUIT", + [ABRT] = "ABRT", + [KILL] = "KILL", + [ALRM] = "ALRM", + [TERM] = "TERM", +}; +static size_t nnums = sizeof(tnums) / sizeof(tnums[0]); + +static int trap_num(const char *name) +{ + if (!strcmp(name, "0")) { + return 0; + } + + int t = atoi(name); + if (t != 0) { + if ((size_t)t < nnums && tnums[t] != NULL) { + name = tnums[t]; + } + } + + for (size_t i = 0; i < ntraps; i++) { + if (traps[i].name && !strcmp(traps[i].name, name)) { + return i; + } + } + fprintf(stderr, "trap: unknown trap '%s'\n", name); + return -1; +} + +static int trap_reset(const char *trap) +{ + int t = trap_num(trap); + if (t == -1) { + return 1; + } + + free(traps[t].value); + traps[t].value = NULL; + signal(t, SIG_DFL); + return 0; +} + +void sh_trap(int trapno) +{ + if (traps[trapno].value) { + /* sh_eval(traps[trapno].value); */ + } +} + +static int trap_set(const char *trap, const char *action) +{ + int t = trap_num(trap); + if (t == -1) { + return 1; + } + + if (t == SIGKILL || t == SIGSTOP) { + fprintf(stderr, "trap: setting trap for %s is undefined\n", trap); + return 1; + } + + if (traps[t].value) { + free(traps[t].value); + } + traps[t].value = strdup(action); + + if (!strcmp(action, "")) { + signal(t, SIG_IGN); + } else { + signal(t, sh_trap); + } + + return 0; +} + +int trap_main(int argc, char *argv[]) +{ + while (getopt(argc, argv, "") != -1) { + return 1; + } + + if (argv[optind] == NULL) { + for (size_t i = 0; i < ntraps; i++) { + if (traps[i].value) { + printf("trap -- %s %s\n", traps[i].value, traps[i].name); + } + } + return 0; + } + + char *action = argv[optind]; + int r = 0; + + /* FIXME */ + if (atoi(action) > 0) { + do { + r |= trap_reset(argv[optind++]); + } while (argv[optind]); + return r; + } + + optind++; + + if (!strcmp(action, "-")) { + do { + r |= trap_reset(argv[optind++]); + } while (argv[optind]); + return r; + } + + do { + r |= trap_set(argv[optind++], action); + } while (argv[optind]); + + return r; +} diff --git a/true.c b/true.c new file mode 100644 index 0000000..c3c9e8c --- /dev/null +++ b/true.c @@ -0,0 +1,24 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +int true_main(int argc, char *argv[]) +{ + (void)argc; (void)argv; + return 0; +} diff --git a/type.c b/type.c new file mode 100644 index 0000000..b3f0bf5 --- /dev/null +++ b/type.c @@ -0,0 +1,7 @@ +#include + +int type_main(int argc, char **argv) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} diff --git a/ulimit.c b/ulimit.c new file mode 100644 index 0000000..3367a61 --- /dev/null +++ b/ulimit.c @@ -0,0 +1,61 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include +#include + +#define BLOCKSIZE (512) + +int ulimit_main(int argc, char *argv[]) +{ + int c; + while ((c = getopt(argc, argv, "f")) != -1) { + switch (c) { + case 'f': /** set or report limit in blocks **/ + /* is default */ + break; + + default: + return 1; + } + } + + if (argv[optind]) { + struct rlimit rl; + rl.rlim_cur = atoi(argv[optind]) * BLOCKSIZE; + if (setrlimit(RLIMIT_FSIZE, &rl) == -1) { + perror("ulimit"); + return 1; + } + return 0; + } + + struct rlimit rl; + getrlimit(RLIMIT_FSIZE, &rl); + if (rl.rlim_cur == RLIM_INFINITY) { + printf("unlimited\n"); + } else { + printf("%ld\n", rl.rlim_cur / BLOCKSIZE); + } + + return 0; +} diff --git a/umask.c b/umask.c new file mode 100644 index 0000000..f6fca22 --- /dev/null +++ b/umask.c @@ -0,0 +1,68 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include +#include + +int umask_main(int argc, char *argv[]) +{ + int symbolic = 0; + + int c; + while ((c = getopt(argc, argv, "S")) != -1) { + switch (c) { + case 'S': /** produce symbolic output **/ + symbolic = 1; + break; + + default: + return 1; + } + } + + if (argv[optind]) { + fprintf(stderr, "umask: TODO\n"); + return 1; + } + + mode_t mask = umask(0); + umask(mask); + + if (symbolic) { + fputs("u=", stdout); + fputs(mask & S_IRUSR ? "" : "r", stdout); + fputs(mask & S_IWUSR ? "" : "w", stdout); + fputs(mask & S_IXUSR ? "" : "x", stdout); + fputs(",g=", stdout); + fputs(mask & S_IRGRP ? "" : "r", stdout); + fputs(mask & S_IWGRP ? "" : "w", stdout); + fputs(mask & S_IXGRP ? "" : "x", stdout); + fputs(",o=", stdout); + fputs(mask & S_IRGRP ? "" : "r", stdout); + fputs(mask & S_IWGRP ? "" : "w", stdout); + fputs(mask & S_IXGRP ? "" : "x", stdout); + putchar('\n'); + } else { + printf("%04o\n", mask); + } + + return 0; +} diff --git a/unset.c b/unset.c new file mode 100644 index 0000000..62be7be --- /dev/null +++ b/unset.c @@ -0,0 +1,28 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2011-2017, Jakob Kaivo + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "sh.h" +#include +#include + +int unset_main(int argc, char *argv[]) +{ + printf("%s: not implemented\n", argv[0]); + return argc; +} diff --git a/wait.c b/wait.c new file mode 100644 index 0000000..16b64d7 --- /dev/null +++ b/wait.c @@ -0,0 +1,8 @@ +#include + +int +wait_main(int argc, char **argv) +{ + printf("Sorry, %s isn't implemented yet.\n", argv[0]); + return argc; +} -- cgit v1.2.1