ESH

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

AuthorJamozed <[email protected]>
Date2021-03-29 06:31:04
Commit0a41e43bd0e8ea92106b3ec47ec3147dc5e1c1c6
Parent5ae61ff80f3d9fb33f482b2df36a755160a6b785

Reimplement parsing to use lineread

Diffstat

A src/lineread.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/lineread.h | 38 ++++++++++++++++++++++++++++++++++++++
M src/main.c | 33 ++++++++++++++++++---------------
M src/parse.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------

4 files changed, 304 insertions, 32 deletions

diff --git a/src/lineread.c b/src/lineread.c
new file mode 100644
index 0000000..b6bb64f
--- /dev/null
+++ b/src/lineread.c
@@ -0,0 +1,182 @@
+// lineread.c
+// Lineread source file for OSH
+// Copyright (C) 2021, 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 "lineread.h"
+
+#include "lib/error.h"
+
+#include <termios.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct line {
+	char *s; size_t sp, sl, sc; size_t hi;
+	char *prompt; size_t pl;
+};
+
+static struct termios tco, tcn;
+
+static char *lineedit(void);
+
+static inline void tcraw(void);
+static inline void tcrestore(void);
+
+static void lineRefresh(struct line *l);
+static void lineMoveLeft(struct line *l);
+static void lineMoveRight(struct line *l);
+static void lineMoveHome(struct line *l);
+static void lineMoveEnd(struct line *l);
+static int lineInsert(struct line *l, char c);
+static void lineBackspace(struct line *l);
+static void lineDelete(struct line *l);
+
+/* Read a line from stdin */
+char *lineread(void) {
+	if (errno) { warn("%s", serr()); }
+	if (!isatty(STDIN_FILENO)) { return NULL; } // TODO
+	else { return lineedit(); }
+}
+
+/* Dynamically read a line from stdin */
+static char *lineedit(void) {
+	struct line l;
+	
+	l.sp = 0; l.sl = 0; l.sc = 1024; l.hi = 0;
+	l.prompt = "$ "; l.pl = strlen(l.prompt);
+	
+	if (!(l.s = malloc(l.sc * sizeof (*l.s)))) { return NULL; } l.s[0] = 0;
+	
+	tcraw(); fputs(l.prompt, stdout);
+	
+	for (int c; (c = fgetc(stdin));) {
+		if (errno) { warn("%s", serr()); }
+		// printf("%02X\n", c); continue;
+		
+		switch (c) {
+		case '\x01': { lineMoveHome(&l); continue; }  // CTRL + A
+		case '\x02': { lineMoveLeft(&l); continue; }  // CTRL + B
+		case '\x04': { tcrestore(); return NULL; }    // CTRL + D
+		case '\x05': { lineMoveEnd(&l); continue; }   // CTRL + E
+		case '\x06': { lineMoveRight(&l); continue; } // CTRL + F
+		case '\x0A': { goto end; }
+		case '\x1B': { int s[4];
+			if ((s[0] = fgetc(stdin)) == EOF) { break; }
+			if ((s[1] = fgetc(stdin)) == EOF) { break; }
+			
+			if (s[0] == '[') switch (s[1]) {
+			case 'A': { continue; } case 'B': { continue; }
+			case 'C': { lineMoveRight(&l); continue; }
+			case 'D': { lineMoveLeft(&l); continue; }
+			}
+		}
+		case '\x7F': { lineBackspace(&l); continue; }
+		default: { lineInsert(&l, c); }
+		}
+	}
+	
+end:;
+	register char *s = strndup(l.s, l.sl);
+	free(l.s); tcrestore(); return s;
+}
+
+/* Put stdin into raw mode */
+static inline void tcraw(void) {
+	tcgetattr(STDIN_FILENO, &tco); tcn = tco;
+	tcn.c_lflag &= ~(ICANON | ECHO);
+	tcsetattr(STDIN_FILENO, TCSANOW, &tcn);
+	return;
+}
+
+/* Restore stdin to canonical mode */
+static inline void tcrestore(void) {
+	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tco);
+	return;
+}
+
+/* Refresh line */
+static void lineRefresh(struct line *l) {
+	
+}
+
+/* Move cursor left */
+static void lineMoveLeft(struct line *l) {
+	if (l->sp) { --l->sp; fputs("\x1B[D", stdout); } return;
+}
+
+/* Move cursor right */
+static void lineMoveRight(struct line *l) {
+	if (l->sp != l->sl) { ++l->sp; fputs("\x1B[C", stdout); } return;
+}
+
+/* Move cursor to the line start */
+static void lineMoveHome(struct line *l) {
+	l->sp = 0; lineRefresh(l); return;
+}
+
+/* Move cursor to the line end */
+static void lineMoveEnd(struct line *l) {
+	l->sp = l->sl; lineRefresh(l); return;
+}
+
+/* Insert character at cursor */
+static int lineInsert(struct line *l, char c) {
+	if (l->sl + 1 == l->sc) { /* TODO expand string */ }
+	if (l->sp == l->sl) { // Cursor is at line end
+		l->s[l->sp++] = c; l->s[++l->sl] = 0;
+		return fputc(c, stdout);
+	}
+	else {
+		memmove(l->s + l->sp + 1, l->s + l->sp, l->sl - l->sp);
+		l->s[l->sp++] = c; l->s[++l->sp] = 0; lineRefresh(l);
+	} return c;
+}
+
+/* Delete character before cursor */
+static void lineBackspace(struct line *l) {
+	if (l->sp) {
+		memmove(l->s + l->sp - 1, l->s + l->sp, l->sl - l->sp);
+		--l->sp; l->s[--l->sl] = 0; lineRefresh(l);
+	} return;
+}
+
+/* Delete character at cursor */
+static void lineDelete(struct line *l) {
+	if (l->sp != l->sl) {
+		memmove(l->s + l->sp, l->s + l->sp + 1, l->sl - l->sp);
+		l->s[--l->sl] = 0; lineRefresh(l);
+	} return;
+}
diff --git a/src/lineread.h b/src/lineread.h
new file mode 100644
index 0000000..3549af1
--- /dev/null
+++ b/src/lineread.h
@@ -0,0 +1,38 @@
+// lineread.h
+// Lineread header file for OSH
+// Copyright (C) 2021, 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_OSH_LINEREAD_H_RPVXY3N7
+#define OMKOV_OSH_LINEREAD_H_RPVXY3N7
+
+extern char *lineread(void);
+
+#endif // OMKOV_OSH_LINEREAD_H_RPVXY3N7
diff --git a/src/main.c b/src/main.c
index a301d4a..fb7f153 100644
--- a/src/main.c
+++ b/src/main.c
@@ -30,11 +30,13 @@ 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 "lineread.h"
 #include "parse.h"
 
+#include "lib/error.h"
+
+#include <errno.h>
 #include <setjmp.h>
 #include <signal.h>
 #include <stdbool.h>
@@ -45,33 +47,34 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 
 int _loop = 1, _ret;
 
-static sigjmp_buf   jmp;
+static jmp_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();
+int main(int ac, char *av[]) { (void)(ac);
+	signal(SIGINT, &reset); signal(SIGTSTP, SIG_IGN); signal(SIGQUIT, SIG_IGN);
+	if (errno) { warn("A: %s", serr()); }
 
 	do {
 		if (sigsetjmp(jmp, 1)) { fputc('\n', stdout); } jmpflag = true;
 
-		char **args = NULL;
+		char *line = lineread(); fputc('\n', stdout);
+		if (!line) { printf("NULL\n"); error(0, "%s", serr()); }
 
-		fputs("$ ", stdout);
+		char **args = parse(line); free(line);
+		if (!args) { printf("PARSEFAIL\n"); error(1, "%s", serr()); }
+		
+		for (int i = 0; args[i]; ++i) {
+			printf("%s\n", args[i]);
+		}
 
-		args = parse(stdin);
 		_ret = execute(args);
 
-end:	free(args);
+		for (size_t i = 0; args[i]; ++i) { free(args[i]); } free(args);
 	} while (_loop);
 
-	fputc('\n', stdout);
-	freealias();
-	
-	return 0;
+	fputc('\n', stdout); return 0;
 }
 
 static void reset(int signo) { (void)(signo);
diff --git a/src/parse.c b/src/parse.c
index 2c14ea4..c5b09b5 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -30,29 +30,78 @@ 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 <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.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; }
+struct lex { char *s; size_t sp; };
+
+struct arr { char **a; size_t al, ac; };
+struct str { char *s; size_t sl, sc; };
+
+static struct lex l;
+
+static inline char *lex(void);
+
+static inline void spush(struct str *s, char c);
+
+/* Parse a string into an argument vector */
+char **parse(char *s) {
+	struct arr a; a.al = 0; a.ac = 64; l.s = s; l.sp = 0;
+	if (!(a.a = malloc(a.ac * sizeof (*a.a)))) { return NULL; }
+	
+	for (char *s = lex(); s; s = lex()) {
+		// TODO dynamic expansion of a
+		a.a[a.al] = s; ++a.al;
+	}
+	
+	a.a[a.al] = NULL; return a.a;
+}
+
+#define c l.s[l.sp]
+
+/* Lex the next token from the string */
+static inline char *lex(void) {
+	struct str s; s.sl = 0; s.sc = 64;
+	
+skip:;
+	for (; isspace(c); ++l.sp) {} if (c == 0) { return NULL; }
+	if (c == '#') { for (++l.sp; !(c == '\n'); ++l.sp) {} goto skip; }
 
-	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; }
-		}
+	if (!(s.s = malloc(s.sc * sizeof (*s.s)))) { return NULL; }
+	
+	goto word; for (;;) { ++l.sp;
+word:;
+		// if (c == '\\') { ++l.sp;
+		// 	if (c == '\n') { continue; }
+		// 	else { spush(&s, '\\'); }
+		// }
+		// else if (c == '\'') for (++l.sp;;) {
+		// 	if (c == '\'') { break; } spush(&s, c);
+		// }
+		// else if (c == '\"') for (++l.sp;;) {
+		// 	if (c == '\\') { ++l.sp;
+		// 		if (c == '$' || c == '`' || c == '\"' || c == '\\');
+		// 		else if (c == '\n') { continue; }
+		// 		else { spush(&s, '\\'); } spush(&s, c);
+		// 	}
+		// 	else if (c == '\"') { break; } spush(&s, c);
+		// }
+		if (isspace(c) || c == 0) { break; }
+		else { spush(&s, c); }
 	}
 
-	argv[len] = NULL; return argv;
+	return s.s;
+}
+
+#undef c // l.s[l.sp]
+
+/* Push a character to the end of a string */
+static inline void spush(struct str *s, char c) {
+	if (s->sl + 1 > s->sc) { s->sc *= 2;
+		s->s = realloc(s->s, s->sc * sizeof (*s->s));
+	} s->s[s->sl] = c; s->s[++s->sl] = 0; return;
 }