Files
nt-lite/code/user/apps/shell_posix/shell_posix.c
T

254 lines
7.8 KiB
C

// User application entry points for shell_posix.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <spawn.h>
#include <sys/wait.h>
#include <ntlite/posix_extensions.h>
#include <ntlite/syscall.h>
#define MAX_CMD 256
static int hIn = 0; static int hOut = 1;
// Prints the requested operation.
static void print(const char* s) { write(hOut, s, strlen(s)); }
// Returns line.
static void readLine(char* buf, int len) {
int pos = 0;
while (true) {
char c; if (read(hIn, &c, 1) <= 0) break;
if (c == '\r' || c == '\n') { print("\n"); break; }
if (c == '\x1B') {
while (pos > 0) { --pos; print("\b \b"); }
continue;
}
if ((c == '\b' || c == 127) && pos > 0) { pos--; print("\b \b"); continue; }
if (c < 0x20 || c == 127) continue;
if (pos < len - 1) { buf[pos++] = c; char echo[2] = {c, '\0'}; print(echo); }
}
buf[pos] = '\0';
}
// Implements the split Command Line operation.
static int splitCommandLine(char* line, char** argv, int maxArgv) {
int argc = 0;
char* p = line;
while (*p && argc < maxArgv - 1) {
while (*p == ' ' || *p == '\t') p++;
if (!*p) break;
if (*p == '"') {
p++;
argv[argc++] = p;
while (*p && *p != '"') p++;
if (*p) *p++ = '\0';
} else {
argv[argc++] = p;
while (*p && *p != ' ' && *p != '\t') p++;
if (*p) *p++ = '\0';
}
}
argv[argc] = NULL;
return argc;
}
// Implements the trim operation.
static char* trim(char* s) {
while (*s == ' ') s++;
int len = (int)strlen(s);
while (len > 0 && s[len - 1] == ' ') len--;
s[len] = '\0';
return s;
}
// Implements the cmd help operation.
static void cmd_help() {
print("Available commands:\n"
" help - Show this list\n"
" dir [path] - List directory contents\n"
" cd <path> - Change directory\n"
" type <file> - Display file contents\n"
" del <file> - Delete a file\n"
" write <file> <txt> - Create a file with text\n"
" clear - Clear the screen\n"
" shutdown - Request system shutdown\n"
" reboot - Request system restart\n"
" exit - Close the shell\n");
}
// Implements the cmd write operation.
static void cmd_write(const char* args) {
char filename[MAX_CMD]; int i = 0;
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
filename[i] = args[i]; i++;
}
filename[i] = '\0';
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
int fd = open(filename, 0x0241, 0644); // O_WRONLY | O_CREAT | O_TRUNC
if (fd < 0) { print("write: could not create file\n"); return; }
write(fd, args + i + 1, strlen(args + i + 1));
close(fd);
print("File written successfully.\n");
}
typedef struct SHELL_POSIX_DIR_ENTRY {
char Name[NAME_MAX + 1];
unsigned char Type;
} SHELL_POSIX_DIR_ENTRY;
// Returns an ASCII lower-case value for case-insensitive directory sorting.
static char shellAsciiLower(char c) {
if (c >= 'A' && c <= 'Z')
return (char)(c - 'A' + 'a');
return c;
}
// Compares two names using case-insensitive order with a stable case tie-breaker.
static int shellCompareNames(const char* a, const char* b) {
size_t i = 0;
if (!a) a = "";
if (!b) b = "";
while (true) {
char ca = shellAsciiLower(a[i]);
char cb = shellAsciiLower(b[i]);
if (ca != cb)
return (ca < cb) ? -1 : 1;
if (ca == '\0')
break;
i++;
}
return strcmp(a, b);
}
// Sorts directory entries alphabetically.
static void shellSortDirEntries(SHELL_POSIX_DIR_ENTRY* entries, size_t count) {
for (size_t i = 1; i < count; i++) {
SHELL_POSIX_DIR_ENTRY current = entries[i];
size_t j = i;
while (j > 0 && shellCompareNames(current.Name, entries[j - 1].Name) < 0) {
entries[j] = entries[j - 1];
j--;
}
entries[j] = current;
}
}
// Implements the cmd dir operation.
static void cmd_dir(const char* path) {
const char* target = (path && *path != '\0') ? path : ".";
DIR* d = opendir(target);
if (!d) { print("dir: cannot access directory\n"); return; }
SHELL_POSIX_DIR_ENTRY* entries = NULL;
size_t count = 0;
size_t capacity = 0;
dirent* entry;
while ((entry = readdir(d)) != NULL) {
if (count == capacity) {
size_t nextCapacity = capacity ? capacity * 2u : 64u;
SHELL_POSIX_DIR_ENTRY* next = (SHELL_POSIX_DIR_ENTRY*)realloc(entries, nextCapacity * sizeof(SHELL_POSIX_DIR_ENTRY));
if (!next) {
print("dir: out of memory\n");
free(entries);
closedir(d);
return;
}
entries = next;
capacity = nextCapacity;
}
strncpy(entries[count].Name, entry->d_name, NAME_MAX);
entries[count].Name[NAME_MAX] = '\0';
entries[count].Type = entry->d_type;
count++;
}
closedir(d);
shellSortDirEntries(entries, count);
for (size_t i = 0; i < count; i++) {
print(entries[i].Type == DT_DIR ? "<DIR> " : " ");
print(entries[i].Name);
print("\n");
}
free(entries);
}
// Implements the cmd type operation.
static void cmd_type(const char* filename) {
int fd = open(filename, 0); // O_RDONLY
if (fd < 0) { print("type: cannot open file\n"); return; }
char buffer[512];
ssize_t n;
while ((n = read(fd, buffer, 511)) > 0) { buffer[n] = '\0'; print(buffer); }
close(fd); print("\n");
}
// Runs command.
static bool runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') return false;
if (strcmp(cmd, "shutdown") == 0 || strcmp(cmd, "reboot") == 0) {
uint32_t action = strcmp(cmd, "reboot") == 0
? SYS_POWER_ACTION_RESTART : SYS_POWER_ACTION_SHUTDOWN;
KSTATUS st = SysRequestSystemPower(action);
if (!KSUCCESS(st)) print("power: request failed\n");
return false;
}
if (strcmp(cmd, "exit") == 0) return true;
if (strcmp(cmd, "help") == 0) { cmd_help(); return false; }
if (strcmp(cmd, "clear") == 0) { clearscreen(); return false; }
if (strcmp(cmd, "crash") == 0) { *(volatile int*)0 = 42; return false; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return false; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return false; }
if (strncmp(cmd, "cd ", 3) == 0) { if (chdir(cmd + 3) != 0) print("cd: failed\n"); return false; }
if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return false; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return false; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return false; }
char cmdBuf[MAX_CMD];
strncpy(cmdBuf, cmd, MAX_CMD - 1);
cmdBuf[MAX_CMD - 1] = '\0';
char* argv[32];
int argc = splitCommandLine(cmdBuf, argv, 32);
if (argc == 0) return false;
int pid;
if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL) == 0) {
int status; waitpid(pid, &status, 0);
clearkeyboard();
} else { print("shell: launch failed\n"); }
return false;
}
// Runs the requested operation.
int main(void) {
const char* cmdline = posix_get_command_line();
char lineBuf[MAX_CMD];
print("shell: command line: ");
print(cmdline);
print("\n");
print("shell: ready\n");
while (true) {
print("> ");
readLine(lineBuf, MAX_CMD);
if (runCommand(trim(lineBuf))) break;
}
print("shell: goodbye\n");
exit(0);
}