Author | Jamozed <[email protected]> |
Date | 2021-03-29 06:31:04 |
Commit | 0a41e43bd0e8ea92106b3ec47ec3147dc5e1c1c6 |
Parent | 5ae61ff80f3d9fb33f482b2df36a755160a6b785 |
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; }