build.bat can create the image if missing, split the shell into native, posix and win32, clean up the build by moving executables into directories, the bootloader is no longer responsible to resolve drivers, fix ResolvePath, every process gets a copy of a module (dll), the child always inherits the cwd, fix function exports in posix, add unlink and exit to posix, add path translator to win32, first bugcheck.

This commit is contained in:
maksw committed 2026-05-13 09:24:16 +02:00
1 parent 0c7f461c6d
commit f80e66ecde
38 files changed
+1620 -462

No files matched your search

+352
View File
@@ -0,0 +1,352 @@
#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(const char* path) {
WIN32_FIND_DATAA fd;
char search_path[MAX_PATH];
size_t path_len = 0;
while (path[path_len] != '\0' && path_len < MAX_PATH) {
path_len++;
}
// Ensure we have room for the path + "\*" + null terminator
if (path_len + 3 > MAX_PATH) {
print("dir: path too long\n");
return;
}
// Manual concatenation
memcpy(search_path, path, path_len);
// Append the wildcard pattern
search_path[path_len] = '\\';
search_path[path_len + 1] = '*';
search_path[path_len + 2] = '\0';
HANDLE hFind = FindFirstFileA(search_path, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
return;
}
print("dir: failed to read directory\n");
return;
}
do {
// Skip the "." and ".." entries to keep output clean
if (path_len > 0 && fd.cFileName[0] == '.') {
if (fd.cFileName[1] == '\0' || (fd.cFileName[1] == '.' && fd.cFileName[2] == '\0')) {
continue;
}
}
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, "crash") == 0) {
*(int*)0 = 42;
return 0;
}
if (strcmp(cmd, "clear") == 0) {
ClearConsoleScreenBuffer(hOut);
return 0;
}
if (strcmp(cmd, "dir") == 0) {
cmd_dir(".");
return 0;
}
if (strncmp(cmd, "dir ", 4) == 0) {
cmd_dir(cmd + 4);
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);
}