summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Kaivo <jkk@ung.org>2019-03-13 21:47:51 -0400
committerJakob Kaivo <jkk@ung.org>2019-03-13 21:47:51 -0400
commita088e851fb45fe38e6a228770fa5d0c04cddb7f5 (patch)
treeccec5f171001fde3ad8d18b8241a8c1f441a77fc
migrate to gitlab
-rw-r--r--Makefile43
-rw-r--r--alias.c94
-rw-r--r--bg.c8
-rw-r--r--builtins.c167
-rw-r--r--cd.c122
-rw-r--r--command.c72
-rw-r--r--dot.c28
-rw-r--r--eval.c39
-rw-r--r--exec.c48
-rw-r--r--exit.c52
-rw-r--r--export.c100
-rw-r--r--false.c24
-rw-r--r--fc.c8
-rw-r--r--fg.c8
-rw-r--r--getopts.c8
-rw-r--r--hash.c8
-rw-r--r--init.c51
-rw-r--r--interactive.c58
-rw-r--r--jobs.c8
-rw-r--r--kill.c101
-rw-r--r--main.c105
-rw-r--r--newgrp.c7
-rw-r--r--parse.c178
-rw-r--r--pwd.c64
-rw-r--r--read.c68
-rw-r--r--readonly.c28
-rw-r--r--set.c135
-rw-r--r--sh.h68
-rw-r--r--sh.l65
-rw-r--r--sh.y312
-rw-r--r--shift.c28
-rw-r--r--times.c51
-rw-r--r--trap.c183
-rw-r--r--true.c24
-rw-r--r--type.c7
-rw-r--r--ulimit.c61
-rw-r--r--umask.c68
-rw-r--r--unset.c28
-rw-r--r--wait.c8
39 files changed, 2535 insertions, 0 deletions
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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+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 <stdio.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stddef.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+#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 <jkk@ung.org>
+ *
+ * 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 <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+
+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 <stdio.h>
+
+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 <stdio.h>
+
+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 <stdio.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+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 <stdio.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <ctype.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+
+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 <stdio.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+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 <jakob@kaivo.net>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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; }
+<COMMENT>. ;
+<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; }
+
+<FOR>{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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/* 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<s> WORD ASSIGNMENT_WORD NAME
+%token NEWLINE
+%token<n> 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<cmd> simple_command */
+%type<s> filename here_end cmd_name cmd_word
+%type<redir> 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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <time.h>
+#include <sys/times.h>
+#include <unistd.h>
+
+#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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+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 <jkk@ung.org>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+
+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 <stdio.h>
+
+int
+wait_main(int argc, char **argv)
+{
+ printf("Sorry, %s isn't implemented yet.\n", argv[0]);
+ return argc;
+}