Files
nt-lite/code/shell/shell.c
T

313 lines
8.2 KiB
C

#include <windows.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
// !Made-up! clear console api.
BOOL ClearConsoleScreenBuffer(HANDLE hConsole);
// ---------------------------------------------------------------------------
// Global handles
// ---------------------------------------------------------------------------
static HANDLE hIn = INVALID_HANDLE_VALUE;
static HANDLE hOut = INVALID_HANDLE_VALUE;
// ---------------------------------------------------------------------------
// Output helpers
// ---------------------------------------------------------------------------
static void print(const char* s) {
DWORD written;
// Write to the actual console window
if (hOut != INVALID_HANDLE_VALUE) {
WriteConsoleA(hOut, s, (DWORD)strlen(s), &written, 0);
} else {
OutputDebugStringA(s);
}
}
static void printUInt(uint32_t n) {
char buf[12];
int i = 0;
if (n == 0) { print("0"); return; }
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
char rev[12];
int j = 0;
while (i > 0) rev[j++] = buf[--i];
rev[j] = '\0';
print(rev);
}
static void printInt(int32_t n) {
char buf[12];
int i = 0;
uint32_t val = (n < 0) ? (print("-"), (uint32_t)-(n + 1) + 1) : (uint32_t)n;
if (val == 0) { print("0"); return; }
while (val > 0) {
buf[i++] = (char)('0' + (val % 10));
val /= 10;
}
while (i > 0) {
char c[2] = { buf[--i], '\0' };
print(c); // Assumes print() handles strings
}
}
// ---------------------------------------------------------------------------
// Line editor
// ---------------------------------------------------------------------------
#define MAX_CMD 256
static void readLine(char* buf, int len) {
int pos = 0;
while (true) {
char c;
DWORD nRead;
ReadConsoleA(hIn, &c, 1, &nRead, 0);
if (!nRead) continue;
if (c == '\r' || c == '\n') {
print("\n");
break;
}
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';
}
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;
}
// ---------------------------------------------------------------------------
// Built-in commands
// ---------------------------------------------------------------------------
static void cmd_help() {
print("Available commands:\n"
" help - Show this list\n"
" dir - List files in current directory\n"
" cd <path> - Change directory\n"
" type <file> - Display file contents\n"
" del <file> - Delete a file\n"
" write <file> <tx> - Create a file with text\n"
" exit - Close the shell\n");
}
static void cmd_write(const char* args) {
char filename[MAX_PATH];
int i = 0;
// Parse filename (up to first space)
while (args[i] != ' ' && args[i] != '\0' && i < MAX_PATH - 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;
}
const char* text = args + i + 1; // Content starts after the space
HANDLE hFile = CreateFileA(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) {
print("write: could not create file\n");
return;
}
DWORD written;
if (!WriteFile(hFile, text, (DWORD)strlen(text), &written, 0)) {
print("write: error writing to file\n");
} else {
print("File written successfully.\n");
}
CloseHandle(hFile);
}
static void cmd_dir() {
WIN32_FIND_DATAA fd;
HANDLE hFind = FindFirstFileA("*", &fd);
if (hFind == INVALID_HANDLE_VALUE) {
print("dir: failed to read directory\n");
return;
}
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
print("<DIR> ");
} else {
print(" ");
}
print(fd.cFileName);
print("\n");
} while (FindNextFileA(hFind, &fd));
FindClose(hFind);
}
static void cmd_type(const char* filename) {
HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) {
print("type: could not open '");
print(filename);
print("'\n");
return;
}
char buffer[512];
DWORD bytesRead;
while (ReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead, 0) && bytesRead > 0) {
buffer[bytesRead] = '\0';
print(buffer);
}
CloseHandle(hFile);
print("\n");
}
// ---------------------------------------------------------------------------
// Run one command. Returns 1 if the shell should exit.
// ---------------------------------------------------------------------------
static int runCommand(const char* cmd) {
if (!cmd || *cmd == '\0')
return 0;
// --- Built-in commands ---
if (strcmp(cmd, "exit") == 0) {
return 1;
}
if (strcmp(cmd, "help") == 0) {
cmd_help();
return 0;
}
if (strcmp(cmd, "clear") == 0) {
ClearConsoleScreenBuffer(hOut);
return 0;
}
if (strcmp(cmd, "dir") == 0) {
cmd_dir();
return 0;
}
if (strncmp(cmd, "cd ", 3) == 0) {
const char* path = cmd + 3;
if (!SetCurrentDirectoryA(path)) {
print("cd: failed to change directory to '");
print(path);
print("'\n");
}
return 0;
}
if (strncmp(cmd, "del ", 4) == 0) {
const char* file = cmd + 4;
if (!DeleteFileA(file)) {
print("del: failed to delete '");
print(file);
print("'\n");
}
return 0;
}
if (strncmp(cmd, "type ", 5) == 0) {
cmd_type(cmd + 5);
return 0;
}
if (strncmp(cmd, "write ", 6) == 0) {
cmd_write(cmd + 6);
return 0;
}
// --- External Process Execution ---
STARTUPINFOA si;
PROCESS_INFORMATION pi;
unsigned char* p = (unsigned char*)&si;
for (size_t i = 0; i < sizeof(si); i++) p[i] = 0;
si.cb = (DWORD)sizeof(si);
// CreateProcessA requires a mutable buffer for the command line argument
char cmdBuf[MAX_CMD];
strncpy(cmdBuf, cmd, MAX_CMD - 1);
cmdBuf[MAX_CMD - 1] = '\0';
if (!CreateProcessA(0, cmdBuf, 0, 0, 0, 0, 0, 0, &si, &pi)) {
print("shell: could not launch '");
print(cmd);
print("', error code: ");
printUInt(GetLastError());
print("\n");
return 0;
}
WaitForSingleObject(pi.hProcess, INFINITE);
FlushConsoleInputBuffer(hOut);
DWORD exitCode = 0;
GetExitCodeProcess(pi.hProcess, &exitCode);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (exitCode != 0) {
print("shell: '");
print(cmd);
print("' exited with code ");
printInt(exitCode);
print("\n");
}
return 0;
}
// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------
int main(const char* cmdline) {
hIn = GetStdHandle(STD_INPUT_HANDLE);
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
char lineBuf[MAX_CMD];
print("shell: ready\n");
print(cmdline);
print("\n");
while (true) {
print("> ");
readLine(lineBuf, MAX_CMD);
if (runCommand(trim(lineBuf)))
break;
}
print("shell: goodbye\n");
ExitProcess(0);
}