//----------------------------------------------------------------------------// // OS on Kaleid // // // // Desc: Kernel shell // // // // // // Copyright © 2018-2021 The OS/K Team // // // // This file is part of OS/K. // // // // OS/K is free software: you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // // the Free Software Foundation, either version 3 of the License, or // // any later version. // // // // OS/K is distributed in the hope that it will be useful, // // but WITHOUT ANY WARRANTY//without even the implied warranty of // // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // // GNU General Public License for more details. // // // // You should have received a copy of the GNU General Public License // // along with OS/K. If not, see . // //----------------------------------------------------------------------------// #include #include #include #include #include #include #include #include int shargc = 0; char **shargv = 0; int shcol = VGA_COLOR_LIGHT_GREY; char *shprompt = "shell> "; size_t shpromptlen = 7; static char *argvbuf = 0; void ExecuteCommand(char *cmdbuf, Command_t *cmdtable) { error_t rc; Command_t *cmd; bool found = false; if (!cmdbuf || !*cmdbuf) return; memzero(*shargv, ARG_MAX); rc = ShCmdLineToArgVec(cmdbuf, &shargc, argvbuf); if (rc) KeStartPanic("Shell: Couldn't parse command line: %d", rc); for (cmd = cmdtable; cmd->name != NULL; cmd++) { if (!strcmp(cmd->name, shargv[0])) { cmd->func(shargc, shargv, cmdbuf); found = true; break; } } assert(shargv[0] == argvbuf + ARG_MAX); if (found == false) { KernLog("err: command not found: '%.255s' (%ld)\n", shargv[0], strlen(shargv[0])); } } #define CMDBUFSIZE (BStdOut->lineLen - shpromptlen - 2) void ShStartShell(void) { uchar ch; error_t rc; char *ptr; // temp // Current line - beginning & current pos char *cmdbuf = malloc(CMDBUFSIZE); char *bufptr = cmdbuf; char *bufend = cmdbuf; // History... // We use a basic N-entries command history #define N 10 // must be >1 char *history = malloc(CMDBUFSIZE * N); int historyIndex = 0; int historyScroll = 0; bool insertMode = 0; size_t nLines; argvbuf = malloc(ARG_MAX * 2); memzero(argvbuf, ARG_MAX * 2); shargv = (char **)argvbuf; shprompt = malloc(SHPROMPT_MAXLEN); strcpy(shprompt, "shell> "); shpromptlen = 7; KernLog("\n%C%s%C", VGA_COLOR_WHITE, shprompt, shcol); BFlushBuf(BStdOut); while ((rc = BGetFromBuf(BStdIn, &ch)) == EOK || rc == EENDF) { // Reset BStdIn's content if (rc == EENDF) { if (!(BStdIn->flags & BF_ERR)) { *bufptr = 0; bufptr = cmdbuf; ExecuteCommand(cmdbuf, shcmdtable); memzero(cmdbuf, CMDBUFSIZE); BFlushBuf(BStdIn); KernLog("%C%s%C", VGA_COLOR_WHITE, shprompt, shcol); BFlushBuf(BStdOut); } else break; } switch (ch) { // Backspace character case K_BACKSPACE: if (bufptr == cmdbuf) break; memmove(bufptr-1, bufptr, bufend-bufptr+1); bufptr--; bufend--; goto print_current_line; // DEL character case K_DEL: if (bufptr == bufend) break; memmove(bufptr, bufptr+1, bufend-bufptr+1); IoSetCursorOffset(IoGetCursorOffset()+1); bufend--; goto print_current_line; case K_BELL: IoDoBeep(); break; case K_ESCAPE: PoShutdown(); break; case K_PAGE_UP: IoScrollUp(); break; case K_PAGE_DOWN: IoScrollDown(); break; case K_INSERT: insertMode = 1 - insertMode; break; // Move within buffer case K_ARRW_LEFT: if (!(bufptr > cmdbuf)) break; bufptr--; IoSetCursorOffset(IoGetCursorOffset()-1); BFlushBuf(BStdOut); break; case K_ARRW_RIGHT: if (bufptr == bufend) break; bufptr++; IoSetCursorOffset(IoGetCursorOffset()+1); BFlushBuf(BStdOut); break; case K_HOME: if (bufptr == cmdbuf) break; IoSetCursorOffset(IoGetCursorOffset()-(int)(bufptr-cmdbuf)); bufptr = cmdbuf; BFlushBuf(BStdOut); break; case K_END: if (bufptr == bufend) break; IoSetCursorOffset(IoGetCursorOffset()+(int)(bufend-bufptr)); bufptr = bufend; BFlushBuf(BStdOut); break; // Handle history scroll up/down case K_ARRW_UP: case K_ARRW_DOWN: if (historyIndex == 0) break; // No history yet if (ch == K_ARRW_UP) { memmove(cmdbuf, &history[historyScroll*CMDBUFSIZE], CMDBUFSIZE); if (historyScroll > 0) historyScroll--; } else if (historyScroll < historyIndex-1) { historyScroll++; memmove(cmdbuf, &history[historyScroll*CMDBUFSIZE], CMDBUFSIZE); } else break; for (bufend = cmdbuf; *bufend && bufend < cmdbuf+CMDBUFSIZE; bufend++); bufptr = bufend; IoSetCursorOffset(0); goto print_current_line; // Regular character default: if (bufptr >= bufend) { *bufptr++ = (char)ch; bufend = bufptr; *bufptr = 0; // Advance cursor if (IoGetCursorOffset() < 0) IoSetCursorOffset(IoGetCursorOffset() + 1); } else { if (!insertMode) { *++bufend = 0; for (ptr = bufend-1; ptr != bufptr; ptr--) *ptr = *(ptr - 1); } else { // insertMode // Advance cursor if (IoGetCursorOffset() < 0) IoSetCursorOffset(IoGetCursorOffset() + 1); } *bufptr++ = (char)ch; } print_current_line: BLockBuf(BStdOut); BStdOut->wp -= BStdOut->lastLF; BStdOut->size -= BStdOut->lastLF; BStdOut->lastLF = 0; bprintf(BStdOut, "%C%s%C%s", VGA_COLOR_WHITE, shprompt, shcol, cmdbuf); BUnlockBuf(BStdOut); IoSetScroll(1); IoScrollDown(); // This calls the flusher // End of buffer? if (bufend != cmdbuf+CMDBUFSIZE-1) { break; // No } // Else, *FALLTHROUGH* to case '\n' case '\n': BPutOnBuf(BStdOut, '\n'); while (IoGetScroll() > 0) { IoScrollDown(); } IoSetCursorOffset(0); bufptr = cmdbuf; // Reset bufptr bufend = cmdbuf; if (cmdbuf[0] && !isspace(cmdbuf[0])) { // Forget old commands if (historyIndex >= N) { assert(historyIndex == N); memmove(history, history+CMDBUFSIZE, CMDBUFSIZE * (N-1)); historyIndex = historyScroll = N-1; } // Update history memmove(&history[historyIndex*CMDBUFSIZE], cmdbuf, CMDBUFSIZE); historyScroll = historyIndex; historyIndex++; // Execute & reset ExecuteCommand(cmdbuf, shcmdtable); memzero(cmdbuf, CMDBUFSIZE); } KernLog("%C%s%C", VGA_COLOR_WHITE, shprompt, shcol); BFlushBuf(BStdIn); BFlushBuf(BStdOut); break; } KePauseCPU(); } }