From 34c5ee4c2a489a506e93d5b303fbc80b263747f0 Mon Sep 17 00:00:00 2001 From: onefang Date: Thu, 27 Feb 2020 11:18:47 +1000 Subject: The new sledjchisl binary, to replace the management scripts and web stuff. --- src/dumbsh.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 src/dumbsh.c (limited to 'src/dumbsh.c') diff --git a/src/dumbsh.c b/src/dumbsh.c new file mode 100644 index 0000000..9ad0204 --- /dev/null +++ b/src/dumbsh.c @@ -0,0 +1,283 @@ +/* dumbsh.c - A really dumb shell, to demonstrate handle_keys usage. + * + * Copyright 2014 David Seikel + * + * Not a real shell, so doesn't follow any standards, + * coz it wont implement them anyway. + +USE_DUMBSH(NEWTOY(dumbsh, "", TOYFLAG_USR|TOYFLAG_BIN)) + +config DUMBSH + bool "dumbsh" + default n + help + usage: dumbsh + + A really dumb shell. +*/ + +#include "toys.h" +#include "lib/handlekeys.h" + +typedef void (*eventHandler) (void); + +struct keyCommand +{ + char *key; + eventHandler handler; +}; + +GLOBALS( + unsigned h, w; + int x, y; + struct double_list *history; +) + +#define TT this.dumbsh + +// Sanity check cursor location and update the current line. +static void updateLine() +{ + if (0 > TT.x) TT.x = 0; + if (0 > TT.y) TT.y = 0; + if (TT.w < TT.x) TT.x = TT.w; + if (TT.h < TT.y) TT.y = TT.h; + if (strlen(toybuf) < TT.x) TT.x = strlen(toybuf); + printf("\x1B[%d;0H%-*s\x1B[%d;%dH", + TT.y + 1, TT.w, toybuf, TT.y + 1, TT.x + 1); + fflush(stdout); +} + +// The various commands. +static void deleteChar() +{ + int j; + + for (j = TT.x; toybuf[j]; j++) + toybuf[j] = toybuf[j + 1]; + updateLine(); +} + +static void backSpaceChar() +{ + if (TT.x) + { + TT.x--; + deleteChar(); + } +} + +// This is where we would actually deal with +// what ever command the user had typed in. +// For now we just move on to the next line. +// TODO - We would want to redirect I/O, capture some keys (^C), +// but pass the rest on. +// Dunno yet how to deal with that. +// We still want handle_keys to be doing it's thing, +// so maybe handing it another fd, and a callback. +// A function to add and remove fd and callback pairs for +// handle_keys to check? +static void doCommand() +{ + toybuf[0] = 0; + TT.x = 0; + TT.y++; + printf("\n"); + fflush(stdout); + updateLine(); +} + +static void endOfLine() +{ + TT.x = strlen(toybuf); + updateLine(); +} + +static void leftChar() +{ + TT.x--; + updateLine(); +} + +static void nextHistory() +{ + TT.history = TT.history->next; + strcpy(toybuf, TT.history->data); + TT.x = strlen(toybuf); + updateLine(); +} + +static void prevHistory() +{ + TT.history = TT.history->prev; + strcpy(toybuf, TT.history->data); + TT.x = strlen(toybuf); + updateLine(); +} + +static void quit() +{ + handle_keys_quit(); +} + +static void rightChar() +{ + TT.x++; + updateLine(); +} + +static void startOfLine() +{ + TT.x = 0; + updateLine(); +} + +// The key to command mappings, Emacs style. +static const struct keyCommand simpleEmacsKeys[] = +{ + {"BS", backSpaceChar}, + {"Del", deleteChar}, + {"^D", deleteChar}, + {"Return", doCommand}, + {"Enter", doCommand}, + {"Down", nextHistory}, + {"^N", nextHistory}, + {"End", endOfLine}, + {"^E", endOfLine}, + {"Left", leftChar}, + {"^B", leftChar}, + {"^X^C", quit}, + {"^C", quit}, + {"Right", rightChar}, + {"^F", rightChar}, + {"Home", startOfLine}, + {"^A", startOfLine}, + {"Up", prevHistory}, + {"^P", prevHistory} +}; + +// Callback for incoming sequences from the terminal. +static int handleEvent(long extra, struct keyevent *event) +{ + switch (event->type) + { + case HK_KEYS : + { + int j, l = strlen(event->sequence); + + // Search for a key sequence bound to a command. + for (j = 0; j < ARRAY_LEN(simpleEmacsKeys); j++) + { + if (strncmp(simpleEmacsKeys[j].key, event->sequence, l) == 0) + { + // If it's a partial match, keep accumulating them. + if (strlen(simpleEmacsKeys[j].key) != l) + return 0; + else + { + if (simpleEmacsKeys[j].handler) simpleEmacsKeys[j].handler(); + return 1; + } + } + } + + // See if it's ordinary keys. + // NOTE - with vi style ordinary keys can be commands, + // but they would be found by the command check above first. + if (!event->isTranslated) + { + if (TT.x < sizeof(toybuf)) + { + int j, l = strlen(event->sequence); + + for (j = strlen(toybuf); j >= TT.x; j--) + toybuf[j + l] = toybuf[j]; + for (j = 0; j < l; j++) + toybuf[TT.x + j] = event->sequence[j]; + TT.x += l; + updateLine(); + } + } + break; + } + + case HK_CSI : + { + // Is it a cursor location report? + if (strcmp("R", event->sequence) == 0) + { + // Parameters are cursor line and column. + // NOTE - This may be sent at other times, not just during terminal resize. + // We are assuming here that it's a resize. + // The defaults are 1, which get ignored by the heuristic below. + int r = event->params[0], c = event->params[1]; + + // Check it's not an F3 key variation, coz some of them use + // the same CSI function command. + // This is a heuristic, we are checking against an unusable terminal size. + if ((2 == event->count) && (8 < r) && (8 < c)) + { + TT.h = r; + TT.w = c; + updateLine(); + } + break; + } + } + + default : break; + } + + // Tell handle_keys to drop it, coz we dealt with it, or it's not one of ours. + return 1; +} + +void dumbsh_main(void) +{ + struct termios termIo, oldTermIo; + char *t = getenv("HOME"); + int fd; + + // Load bash history. + t = xmprintf("%s/%s", t ? t : "", ".bash_history"); + if (-1 != (fd = open(t, O_RDONLY))) + { + while ((t = get_line(fd))) TT.history = dlist_add(&TT.history, t); + close(fd); + } + if (!TT.history) + TT.history = dlist_add(&TT.history, ""); + + // Grab the old terminal settings and save it. + tcgetattr(0, &oldTermIo); + tcflush(0, TCIFLUSH); + termIo = oldTermIo; + + // Mould the terminal to our will. + // In this example we are turning off all the terminal smarts, but real code + // might not want that. + termIo.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL + | IUCLC | IXON | IXOFF | IXANY); + termIo.c_oflag &= ~OPOST; + termIo.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | TOSTOP | ICANON | ISIG + | IEXTEN); + termIo.c_cflag &= ~(CSIZE | PARENB); + termIo.c_cflag |= CS8; + termIo.c_cc[VTIME]=0; // deciseconds. + termIo.c_cc[VMIN]=1; + tcsetattr(0, TCSANOW, &termIo); + + // Let the mouldy old terminal mold us. + TT.w = 80; + TT.h = 24; + terminal_size(&TT.w, &TT.h); + + // Let's rock! + updateLine(); + handle_keys(0, handleEvent); + + // Clean up. + tcsetattr(0, TCSANOW, &oldTermIo); + puts(""); + fflush(stdout); +} -- cgit v1.1