ESH

Executive Shell
git clone http://git.omkov.net/ESH
Log | Tree | Refs | README | Download

AuthorJamozed <[email protected]>
Date2021-01-15 01:08:47
Commit8e5fd79617a63e4877d24408528bc36594e2f6c9

Restore OSH 0.2.1 to Git

Diffstat

A .gitignore | 5 +++++
A BuildUNIX.sh | 10 ++++++++++
A CHANGELOG | 18 ++++++++++++++++++
A CMakeLists.txt | 13 +++++++++++++
A LICENCE | 24 ++++++++++++++++++++++++
A README.md | 24 ++++++++++++++++++++++++
A src/alias.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/alias.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
A src/bltn.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/bltn.h | 39 +++++++++++++++++++++++++++++++++++++++
A src/bltns/cd.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/bltns/pwd.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/exec.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/exec.h | 40 ++++++++++++++++++++++++++++++++++++++++
A src/lex.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/lex.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++
A src/lib/error.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/lib/error.h | 43 +++++++++++++++++++++++++++++++++++++++++++
A src/lib/optget.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/lib/optget.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/main.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/parse.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/parse.h | 40 ++++++++++++++++++++++++++++++++++++++++
A src/str.h | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

24 files changed, 1224 insertions, 0 deletions

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5497595
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/.cache/
+/bin/
+/build/
+/compile_commands.json
+/lib/
diff --git a/BuildUNIX.sh b/BuildUNIX.sh
new file mode 100755
index 0000000..d59505f
--- /dev/null
+++ b/BuildUNIX.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env sh
+dir="$(dirname $(realpath "$0"))"
+
+cmake -S "${dir}" -B "${dir}/build" \
+	-DCMAKE_BUILD_TYPE:STRING=Release \
+	-DCMAKE_EXPORT_COMPILE_COMMANDS=1
+
+cmake --build "${dir}/build"
+
+mv -f "${dir}/build/compile_commands.json" "${dir}/compile_commands.json"
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..5973569
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,18 @@
+0.2.1, 2021-01-15
+* Implement SIGSTOP and SIGQUIT handling
+* Update buld scripts
+* Update lib dependencies
+
+0.2.0, 2020-04-16
+* Implement SIGINT and EOF handling
+* Improve builtin execution
+* Implement command return value handling
+* Add PWD builtin
+* Move to token based parsing
+* Add rudimentary support for multi-line commands
+* Rebrand to OSH, OMKOV Shell
+
+0.1.0, 2020-01-31
+* Implement basic shell loop
+* Implement program execution
+* Implement builtins cd, help and exit
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..cfea3e4
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,13 @@
+# CMakeLists.txt
+# CMakeLists file for OSH
+
+CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
+PROJECT(OSH VERSION 0.2.1 LANGUAGES C)
+
+SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
+ADD_COMPILE_DEFINITIONS(PROJECT_VERSION="${PROJECT_VERSION}")
+
+FILE(GLOB LIBSRC ${CMAKE_SOURCE_DIR}/src/lib/*)
+FILE(GLOB SOURCES ${CMAKE_SOURCE_DIR}/src/*)
+
+ADD_EXECUTABLE(osh ${SOURCES} ${LIBSRC})
diff --git a/LICENCE b/LICENCE
new file mode 100644
index 0000000..5e4b579
--- /dev/null
+++ b/LICENCE
@@ -0,0 +1,24 @@
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b32a757
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
+# OSH
+
+> OMKOV Shell
+
+OSH is a modern shell with an emphasis on performance.
+
+## Build Instructions
+
+OSH uses CMake to build.  
+Binaries will be located in the `bin` directory.
+
+### UNIX Build
+
+Run `BuildUNIX.sh`, or
+
+```sh
+cmake -S . -B build && cmake --build build
+```
+
+## Meta
+
+Copyright (C) 2020, Jakob Wakeling  
+All rights reserved.  
+[OMKOV Permissive Licence](https://www.omkov.net/OLPE)
diff --git a/src/alias.c b/src/alias.c
new file mode 100644
index 0000000..b226a94
--- /dev/null
+++ b/src/alias.c
@@ -0,0 +1,84 @@
+// alias.c
+// Alias source file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "alias.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+akas_t akas;
+static const aka_t AKA = {NULL, NULL};
+
+void initalias(void) {
+	akas.cap = 16; akas.len = 0;
+	akas.dat = (aka_t *)malloc(sizeof(aka_t) * akas.cap);
+	akas.dat[0].str = NULL;
+	return;
+}
+
+void freealias(void) {
+	for (aka_t *a = akas.dat; a->str; ++a) {
+		free(a->str); free(a->com);
+	} free(akas.dat);
+	return;
+}
+
+void pushalias(akas_t *akas, aka_t aka) {
+	if (akas->len + 1 > akas->cap) { akas->cap *= 2;
+		akas->dat = (aka_t *)realloc(akas->dat, sizeof(aka_t) * akas->cap);
+	} akas->dat[akas->len] = aka; akas->dat[++akas->len] = AKA;
+	return;
+}
+
+int bltn_alias(char *argv[]) {
+	if (!argv[1]) { for (aka_t *a = akas.dat; a->str; ++a) {
+		printf("alias %s='%s'\n", a->str, a->com);
+	} return 0; }
+	
+	for (int i = 0; argv[i]; ++i) {
+		char *c = strchr(argv[i], '=');
+		if (c) { aka_t aka;
+			size_t slen = c - argv[i], clen = strlen(++c);
+			aka.str = (char *)malloc(slen + 1); strncpy(aka.str, argv[i], slen);
+			aka.com = (char *)malloc(clen + 1); strncpy(aka.com, c, clen);
+			pushalias(&akas, aka);
+		}
+		else for (aka_t *a = akas.dat; a->str; ++a) {
+			if (strcmp(argv[i], a->str) == 0) {
+				printf("alias %s='%s'\n", a->str, a->com); break;
+			}
+		}
+	}
+	
+	return 0;
+}
diff --git a/src/alias.h b/src/alias.h
new file mode 100644
index 0000000..a44041e
--- /dev/null
+++ b/src/alias.h
@@ -0,0 +1,48 @@
+// alias.h
+// Alias header file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef OSH_ALIAS_H_0BXOPDLM
+#define OSH_ALIAS_H_0BXOPDLM
+
+#include <stddef.h>
+
+typedef struct { char *str, *com; } aka_t;
+typedef struct { aka_t *dat; size_t cap, len; } akas_t;
+extern akas_t akas;
+
+extern void initalias(void);
+extern void freealias(void);
+extern void pushalias(akas_t *akas, aka_t aka);
+
+extern int bltn_alias(char *argv[]);
+
+#endif // OSH_ALIAS_H_0BXOPDLM
diff --git a/src/bltn.c b/src/bltn.c
new file mode 100644
index 0000000..7624fd0
--- /dev/null
+++ b/src/bltn.c
@@ -0,0 +1,69 @@
+// bltn.c
+// Builtin source file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "bltns/cd.c"
+#include "bltns/pwd.c"
+
+#include "alias.h"
+#include "bltn.h"
+#include "exec.h"
+
+#include "lib/error.h"
+#include "lib/optget.h"
+
+#include <stdio.h>
+
+static int bltn_eval(char *av[]) { return execute(&av[1]); }
+static int bltn_exit(char *av[]) { (void)(av); _loop = 0; return 0; }
+static int bltn_false(char *av[]) { (void)(av); return 1; }
+static int bltn_help(char *av[]);
+static int bltn_true(char *av[]) { (void)(av); return 0; }
+
+static int getret(char *argv[]) { printf("%d\n", _ret); return 0; }
+
+bltn_t bltns[] = {
+	{"alias", &bltn_alias},
+	{"cd",    &bltn_cd},
+	{"eval",  &bltn_eval},
+	{"exit",  &bltn_exit},
+	{"false", &bltn_false},
+	{"help",  &bltn_help},
+	{"pwd",   &bltn_pwd},
+	{"true",  &bltn_true},
+	{"ret",   &getret},
+	{NULL, NULL}
+};
+
+static int bltn_help(char *argv[]) {
+	puts("OSH, version " PROJECT_VERSION);
+	return 0;
+}
diff --git a/src/bltn.h b/src/bltn.h
new file mode 100644
index 0000000..9b7c5f9
--- /dev/null
+++ b/src/bltn.h
@@ -0,0 +1,39 @@
+// bltn.h
+// Builtin header file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef OSH_BLTN_H_XQ5D812A
+#define OSH_BLTN_H_XQ5D812A
+
+typedef struct { const char *str; int (*fun)(char *[]); } bltn_t;
+extern bltn_t bltns[];
+
+#endif // OSH_BLTN_H_XQ5D812A
diff --git a/src/bltns/cd.c b/src/bltns/cd.c
new file mode 100644
index 0000000..edc9dd2
--- /dev/null
+++ b/src/bltns/cd.c
@@ -0,0 +1,59 @@
+// cd.c, version 0.1.0
+// cd builtin source file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "../lib/optget.h"
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int bltn_cd(char *av[]) {
+	struct opt opt = OPTGET_INIT; opt.str = "LP"; opt.lops = NULL;
+	for (int o; (o = optget(&opt, av, 1)) != -1;) switch (o) {
+	case 'L': { break; }
+	case 'P': { break; }
+	default: { return 1; }
+	}
+	
+	char *path = av[1] ? av[1] : getenv("HOME");
+	if (chdir(path)) {
+		fprintf(stderr, "%s: %s: %s\n", av[0], path, strerror(errno));
+		return 1;
+	}
+	
+	path = getcwd(NULL, 0); setenv("PWD", path, 1);
+	
+	free(path); return 0;
+}
diff --git a/src/bltns/pwd.c b/src/bltns/pwd.c
new file mode 100644
index 0000000..a21e380
--- /dev/null
+++ b/src/bltns/pwd.c
@@ -0,0 +1,58 @@
+// pwd.c, version 1.0.2b
+// OMKOV coreutils pwd adapted as a OSH builtin
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "../lib/optget.h"
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int bltn_pwd(char *av[]) { int mode = 0;
+	struct opt opt = OPTGET_INIT; opt.str = "LP"; opt.lops = NULL;
+	for (int o; (o = optget(&opt, av, 1)) != -1;) switch (o) {
+	case 'L': { mode = 0; break; }
+	case 'P': { mode = 1; break; }
+	default: { return 1; }
+	}
+	
+pwd:;
+	char *cwd = mode ? getcwd(NULL, 0) : getenv("PWD");
+	if (!cwd && !mode) { mode = 1; goto pwd; }
+	else if (!cwd) { perror(av[0]); return 1; }
+	
+	fputs(cwd, stdout); fputc('\n', stdout);
+	
+	if (mode) { free(cwd); }
+	return 0;
+}
diff --git a/src/exec.c b/src/exec.c
new file mode 100644
index 0000000..2a22041
--- /dev/null
+++ b/src/exec.c
@@ -0,0 +1,82 @@
+// exec.c
+// Exec source file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "bltn.h"
+#include "exec.h"
+
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int launch(char *argv[]);
+static void launcherror(char *s);
+
+/*
+	Execute a command
+*/
+int execute(char *argv[]) {
+	if (!argv[0]) { return _ret; }
+	for (bltn_t *b = bltns; b->str; ++b) {
+		if (strcmp(argv[0], b->str) == 0) { return b->fun(argv); }
+	} return launch(argv);
+}
+
+/*
+	Fork and execute an executable
+*/
+static int launch(char *argv[]) {
+	pid_t pid, wpid; int status;
+	
+	pid = fork();
+	if (pid == 0) {
+		signal(SIGINT, SIG_DFL);
+		if (execvp(argv[0], argv) == -1) {
+			if (errno == ENOENT) { launcherror(argv[0]); }
+			else { perror(argv[0]); } exit(1);
+		}
+	}
+	else if (pid == -1) { puts("test"); perror(argv[0]); }
+	else { waitpid(pid, &status, 0); }
+	return WEXITSTATUS(status);
+}
+
+static void launcherror(char *s) {
+	for (char *p = s; *p; ++p) {
+		if (*p == '\n') { fputs("\\n", stderr); }
+		else { fputc(*p, stderr); }
+	} fputs(": Command not found\n", stderr);
+	return;
+}
diff --git a/src/exec.h b/src/exec.h
new file mode 100644
index 0000000..8d9fa61
--- /dev/null
+++ b/src/exec.h
@@ -0,0 +1,40 @@
+// exec.h
+// Exec header file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef OSH_EXEC_H_VI5E8LVV
+#define OSH_EXEC_H_VI5E8LVV
+
+extern int _loop, _ret;
+
+extern int execute(char *argv[]);
+
+#endif // OSH_EXEC_H_VI5E8LVV
diff --git a/src/lex.c b/src/lex.c
new file mode 100644
index 0000000..d3092b0
--- /dev/null
+++ b/src/lex.c
@@ -0,0 +1,78 @@
+// lex.c
+// Lexer source file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "lex.h"
+#include "str.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define GETC c = fgetc(file)
+
+tok_t lex(FILE *file) { tok_t tok; tok.str = NULL;
+	int c = fgetc(file);
+	
+skip:;
+	for (; isspace(c) && c != '\n'; GETC);
+	if (c == '\n') { tok.tok = NEWLINE; return tok; }
+	if (c == EOF) { tok.tok = EOF; return tok; }
+	if (c == '#') { for (GETC; !(c == EOF || c == '\n'); GETC) {} goto skip; }
+	
+	str_t str = sinit();
+	if (0) {}
+	else { tok.tok = WORD; goto word;
+		for (;;) { GETC;
+word:		if (c == '\\') { c = fgetc(file);
+				if (c == '\n') { fputs("> ", stdout); continue; }
+				else { spush(&str, '\\'); }
+			}
+			else if (c == '\'') for (;;) { c = fgetc(file);
+				if (c == '\'') { break; } else { spush(&str, c); }
+			}
+			else if (c == '\"') for (;;) { c = fgetc(file);
+				if (c == '\\') { c = fgetc(file);
+					if (c == '$' || c == '`' || c == '\"' || c == '\\');
+					else if (c == '\n') { continue; }
+					else { spush(&str, '\\'); } spush(&str, c);
+				}
+				else if (c == '\"') { break; } else { spush(&str, c); }
+			}
+			else if (isspace(c) && c != '\n') { break; }
+			else if (c == '\n' || c == EOF) { ungetc('\n', file); break; }
+			else { spush(&str, c); }
+		} tok.str = str.dat;
+	}
+	
+	return tok;
+}
diff --git a/src/lex.h b/src/lex.h
new file mode 100644
index 0000000..11f4da7
--- /dev/null
+++ b/src/lex.h
@@ -0,0 +1,46 @@
+// lex.h
+// Lexer header file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef OSH_LEX_H_MA6B50VH
+#define OSH_LEX_H_MA6B50VH
+
+#include <stdio.h>
+
+enum token {
+	END = EOF, NUL = 0, NEWLINE, WORD
+};
+
+typedef struct { enum token tok; char *str; } tok_t;
+
+extern tok_t lex(FILE *file);
+
+#endif // OSH_LEX_H_MA6B50VH
diff --git a/src/lib/error.c b/src/lib/error.c
new file mode 100644
index 0000000..f246723
--- /dev/null
+++ b/src/lib/error.c
@@ -0,0 +1,55 @@
+// error.c, version 1.0.1
+// Error source file for OMKOV lib
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "error.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *A0 = NULL;
+
+_Noreturn void error(int status, const char *format, ...) {
+	fflush(stdout); if (A0) { fputs(A0, stderr); fputs(": ", stderr); }
+	va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap);
+	fputc('\n', stderr); exit(status);
+}
+
+void warn(const char *format, ...) {
+	fflush(stdout); if (A0) { fputs(A0, stderr); fputs(": ", stderr); }
+	va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap);
+	fputc('\n', stderr); return;
+}
+
+char *serr(void) { return strerror(errno); }
diff --git a/src/lib/error.h b/src/lib/error.h
new file mode 100644
index 0000000..d0d4585
--- /dev/null
+++ b/src/lib/error.h
@@ -0,0 +1,43 @@
+// error.h, version 1.0.1
+// Error header file for OMKOV lib
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef OMKOV_LIB_ERROR_H_38W06M3W
+#define OMKOV_LIB_ERROR_H_38W06M3W
+
+extern char *A0;
+
+extern _Noreturn void error(int status, const char *format, ...);
+extern void warn(const char *format, ...);
+
+extern char *serr(void);
+
+#endif // OMKOV_LIB_ERROR_H_38W06M3W
diff --git a/src/lib/optget.c b/src/lib/optget.c
new file mode 100644
index 0000000..da61ebd
--- /dev/null
+++ b/src/lib/optget.c
@@ -0,0 +1,122 @@
+// optget.h, version 1.6.0
+// optget source file for OMKOV lib
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "error.h"
+#include "optget.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#define cur av[opt->ind]
+
+const struct opt OPTGET_INIT = { 1, 0, 1, 0, NULL, NULL, NULL, NULL };
+
+static inline void permute(char **av, int i, int n);
+
+int optget(struct opt *opt, char *av[], int flags) {
+	if (flags & 1) {
+		for (; cur && (cur[0] != '-' || cur[1] == 0); ++opt->ind, ++opt->nop);
+		if (!cur) { opt->ind -= opt->nop; opt->nop = 0; return -1; }
+	}
+	else if (!cur || (cur[0] != '-' || cur[1] == 0)) { return -1; }
+	
+	int optind = opt->ind, optret;
+	
+	if (cur[1] == '-') {
+		if (cur[2] == 0) { if (opt->nop) {
+				permute(av, opt->ind++, opt->nop);
+				opt->ind -= opt->nop; opt->nop = 0;
+			} else { ++opt->ind; } return -1;
+		}
+		
+		int optend, lop; optret = '?'; opt->opt = 0; opt->lop = cur;
+		if (!opt->lops) { goto nol; }
+		for (optend = 2; cur[optend] != '=' && cur[optend] != 0; ++optend);
+		
+		for (lop = 0; opt->lops[lop].str; ++lop) {
+			if (strncmp(&cur[2], opt->lops[lop].str, (size_t)optend - 2) == 0) {
+				if (!opt->lops[lop].str[optend - 2]) {
+					optret = opt->opt = opt->lops[lop].val; break;
+				}
+			}
+		}
+		
+		if (opt->lops[lop].arg > ARG_NUL) {
+			if (cur[optend]) { opt->arg = &cur[optend + 1]; }
+			else if (av[opt->ind + 1]) { opt->arg = av[++opt->ind]; }
+			else {
+				if (opt->lops[lop].arg == ARG_REQ) { optret = ':'; }
+				opt->arg = NULL;
+			}
+		}
+		else { opt->arg = NULL; }
+		
+nol:	opt->pos = 0;
+	}
+	else {
+		optret = opt->opt = cur[opt->pos++]; opt->lop = NULL;
+		const char *optchr = strchr(opt->str, opt->opt);
+		
+		if (!optchr) { optret = '?'; }
+		else if (optchr[1] == ':') {
+			if (cur[opt->pos]) { opt->arg = &cur[opt->pos]; }
+			else if (av[opt->ind + 1]) { opt->arg = av[++opt->ind]; }
+			else { opt->arg = NULL; optret = ':'; }
+			opt->pos = 0;
+		}
+		else { opt->arg = NULL; }
+	}
+	
+	if (!opt->pos || !cur[opt->pos]) {
+		++opt->ind; opt->pos = 1;
+		if (opt->nop) for (; optind < opt->ind; ++optind) {
+			permute(av, optind, opt->nop);
+		}
+	}
+	
+	if (optret == '?' && opt->str[0] != ':') {
+		if (opt->opt) { warn("%c: invalid option", opt->opt); }
+		else if (opt->lop) { warn("%s: invalid option", opt->lop); }
+	}
+	if (optret == ':' && opt->str[0] != ':') {
+		if (opt->opt) { warn("%c: option requires argument", opt->opt); }
+		else if (opt->lop) { warn("%s: option requires argument", opt->lop); }
+	}
+	
+	return optret;
+}
+
+static inline void permute(char **av, int i, int n) {
+	char *a = av[i]; memmove(&av[i - n + 1], &av[i - n], n * sizeof (av));
+	av[i - n] = a; return;
+}
diff --git a/src/lib/optget.h b/src/lib/optget.h
new file mode 100644
index 0000000..f83d132
--- /dev/null
+++ b/src/lib/optget.h
@@ -0,0 +1,55 @@
+// optget.h, version 1.6.0
+// optget header file for OMKOV lib
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef OMKOV_LIB_OPTGET_H_W3LIZK1S
+#define OMKOV_LIB_OPTGET_H_W3LIZK1S
+
+#define ARG_NUL 0
+#define ARG_REQ 1
+#define ARG_OPT 2
+
+struct lop {
+	char *str;
+	int arg, val;
+};
+
+struct opt {
+	int ind, opt, pos, nop;
+	char *arg, *lop, *str;
+	struct lop *lops;
+};
+
+extern const struct opt OPTGET_INIT;
+
+extern int optget(struct opt *opt, char *av[], int flags);
+
+#endif // OMKOV_LIB_OPTGET_H_W3LIZK1S
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..a301d4a
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,79 @@
+// main.c
+// Main source file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "alias.h"
+#include "exec.h"
+#include "lex.h"
+#include "parse.h"
+
+#include <setjmp.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int _loop = 1, _ret;
+
+static sigjmp_buf   jmp;
+static sig_atomic_t jmpflag = false;
+
+static void reset(int signo);
+
+int main(int argc, char *argv[]) { (void)(argc);
+	signal(SIGINT, &reset); signal(SIGSTOP, SIG_IGN); signal(SIGQUIT, SIG_IGN);
+	
+	initalias();
+	
+	do {
+		if (sigsetjmp(jmp, 1)) { fputc('\n', stdout); } jmpflag = true;
+		
+		char **args = NULL;
+		
+		fputs("$ ", stdout);
+		
+		args = parse(stdin);
+		_ret = execute(args);
+		
+end:	free(args);
+	} while (_loop);
+	
+	fputc('\n', stdout);
+	freealias();
+	
+	return 0;
+}
+
+static void reset(int signo) { (void)(signo);
+	if (jmpflag) { siglongjmp(jmp, 1); }
+}
diff --git a/src/parse.c b/src/parse.c
new file mode 100644
index 0000000..2c14ea4
--- /dev/null
+++ b/src/parse.c
@@ -0,0 +1,58 @@
+// parse.c
+// Parser source file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "exec.h"
+#include "lex.h"
+#include "parse.h"
+#include "str.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+char **parse(FILE *file) {
+	size_t cap = 64, len = 0; char *arg = NULL;
+	char **argv = (char **)malloc(cap * sizeof(char *));
+	if (!argv) { return NULL; }
+	
+	for (tok_t tok = lex(file); tok.tok != NEWLINE; tok = lex(file)) {
+		if (tok.tok == EOF) { _loop = false; break; }
+		if (tok.tok == WORD) { argv[len++] = tok.str; }
+		
+		if (len == cap) {
+			cap *= 2; argv = (char **)realloc(argv, cap * sizeof(char *));
+			if (!argv) { return NULL; }
+		}
+	}
+	
+	argv[len] = NULL; return argv;
+}
diff --git a/src/parse.h b/src/parse.h
new file mode 100644
index 0000000..e27e8c5
--- /dev/null
+++ b/src/parse.h
@@ -0,0 +1,40 @@
+// parse.h
+// Parser header file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef OSH_PARSE_H_G9TJ04KR
+#define OSH_PARSE_H_G9TJ04KR
+
+#include <stdio.h>
+
+extern char **parse(FILE *file);
+
+#endif // OSH_PARSE_H_G9TJ04KR
diff --git a/src/str.h b/src/str.h
new file mode 100644
index 0000000..ead6846
--- /dev/null
+++ b/src/str.h
@@ -0,0 +1,75 @@
+// str.h
+// Dynamic string header file for OSH
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef OSH_STR_H_LJ7LE814
+#define OSH_STR_H_LJ7LE814
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct { char *dat; size_t cap, len; } str_t;
+
+static inline str_t sinit(void) {
+	str_t str; str.cap = 64; str.len = 0;
+	str.dat = (char *)malloc(str.cap);
+	return str;
+}
+
+static void spush(str_t *str, char c) {
+	if (str->len + 1 > str->cap) { str->cap *= 2;
+		str->dat = (char *)realloc(str->dat, str->cap);
+	} str->dat[str->len] = c; str->dat[++str->len] = 0;
+	return;
+}
+
+static char spop(str_t *str) {
+	char c = str->dat[--str->len];
+	str->dat[str->len] = 0;
+	return c;
+}
+
+static void sapp(str_t *str, char *s) {
+	size_t len = strlen(s);
+	if (str->len + len > str->cap) {
+		str->cap += len * 2; str->dat = (char *)realloc(str->dat, str->cap);
+	} memcpy(&str->dat[str->len], s, len);
+	str->len += len; str->dat[str->len] = 0;
+	return;
+}
+
+static inline void sclear(str_t *str) {
+	str->len = 0; str->dat[0] = 0;
+	return;
+}
+
+#endif // OSH_STR_H_LJ7LE814