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:
1 parent
0c7f461c6d
commit
f80e66ecde
38 files changed
+1620
-462
No files matched your search
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user