307 lines
9.2 KiB
C
307 lines
9.2 KiB
C
//----------------------------------------------------------------------------//
|
|
// GNU GPL OS/K //
|
|
// //
|
|
// Desc: Kernel shell //
|
|
// //
|
|
// //
|
|
// Copyright © 2018-2020 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 <https://www.gnu.org/licenses/>. //
|
|
//----------------------------------------------------------------------------//
|
|
|
|
#include <io/vga.h>
|
|
#include <io/keyb.h>
|
|
#include <io/spkr.h>
|
|
#include <lib/buf.h>
|
|
#include <sh/argv.h>
|
|
#include <sh/shell.h>
|
|
#include <po/shtdwn.h>
|
|
#include <io/cursor.h>
|
|
|
|
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();
|
|
}
|
|
}
|