377 lines
11 KiB
C
377 lines
11 KiB
C
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Win32 imports & definitions
|
|
// ---------------------------------------------------------------------------
|
|
_Noreturn void ExitProcess(unsigned int code);
|
|
void OutputDebugStringA(const char* s);
|
|
|
|
typedef void* HANDLE;
|
|
typedef unsigned long DWORD;
|
|
typedef int BOOL;
|
|
|
|
#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1)
|
|
#define INFINITE 0xFFFFFFFFUL
|
|
#define STD_INPUT_HANDLE ((DWORD)-10)
|
|
#define STD_OUTPUT_HANDLE ((DWORD)-11)
|
|
#define MAX_PATH 260
|
|
|
|
// File I/O constants
|
|
#define GENERIC_READ 0x80000000L
|
|
#define GENERIC_WRITE 0x40000000L
|
|
#define OPEN_EXISTING 3
|
|
#define CREATE_ALWAYS 2
|
|
#define FILE_SHARE_READ 0x00000001
|
|
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
|
|
|
|
HANDLE GetStdHandle(DWORD nStdHandle);
|
|
DWORD GetLastError();
|
|
|
|
BOOL ReadConsoleA(HANDLE hConsoleInput, void* lpBuffer,
|
|
DWORD nNumberOfCharsToRead, DWORD* lpNumberOfCharsRead,
|
|
void* pInputControl);
|
|
|
|
BOOL WriteConsoleA(HANDLE hConsoleOutput, const void* lpBuffer,
|
|
DWORD nNumberOfCharsToWrite, DWORD* lpNumberOfCharsWritten,
|
|
void* lpReserved);
|
|
|
|
typedef struct {
|
|
DWORD cb;
|
|
char* lpReserved;
|
|
char* lpDesktop;
|
|
char* lpTitle;
|
|
DWORD dwX, dwY, dwXSize, dwYSize;
|
|
DWORD dwXCountChars, dwYCountChars;
|
|
DWORD dwFillAttribute, dwFlags;
|
|
unsigned short wShowWindow, cbReserved2;
|
|
unsigned char* lpReserved2;
|
|
HANDLE hStdInput, hStdOutput, hStdError;
|
|
} STARTUPINFOA;
|
|
|
|
typedef struct {
|
|
HANDLE hProcess;
|
|
HANDLE hThread;
|
|
DWORD dwProcessId;
|
|
DWORD dwThreadId;
|
|
} PROCESS_INFORMATION;
|
|
|
|
typedef struct _FILETIME {
|
|
DWORD dwLowDateTime;
|
|
DWORD dwHighDateTime;
|
|
} FILETIME;
|
|
|
|
typedef struct _WIN32_FIND_DATAA {
|
|
DWORD dwFileAttributes;
|
|
FILETIME ftCreationTime;
|
|
FILETIME ftLastAccessTime;
|
|
FILETIME ftLastWriteTime;
|
|
DWORD nFileSizeHigh;
|
|
DWORD nFileSizeLow;
|
|
DWORD dwReserved0;
|
|
DWORD dwReserved1;
|
|
char cFileName[MAX_PATH];
|
|
char cAlternateFileName[14];
|
|
} WIN32_FIND_DATAA;
|
|
|
|
BOOL CreateProcessA(const char* lpApp, char* lpCmdLine,
|
|
void* lpPA, void* lpTA, BOOL bInherit,
|
|
DWORD dwFlags, void* lpEnv, const char* lpDir,
|
|
STARTUPINFOA* lpSI, PROCESS_INFORMATION* lpPI);
|
|
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMs);
|
|
BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode);
|
|
BOOL CloseHandle(HANDLE hObject);
|
|
|
|
// File operation imports
|
|
HANDLE FindFirstFileA(const char* lpFileName, WIN32_FIND_DATAA* lpFindFileData);
|
|
BOOL FindNextFileA(HANDLE hFindFile, WIN32_FIND_DATAA* lpFindFileData);
|
|
BOOL FindClose(HANDLE hFindFile);
|
|
BOOL SetCurrentDirectoryA(const char* lpPathName);
|
|
BOOL DeleteFileA(const char* lpFileName);
|
|
HANDLE CreateFileA(const char* lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
|
|
void* lpSecurityAttributes, DWORD dwCreationDisposition,
|
|
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
|
|
BOOL ReadFile(HANDLE hFile, void* lpBuffer, DWORD nNumberOfBytesToRead,
|
|
DWORD* lpNumberOfBytesRead, void* lpOverlapped);
|
|
BOOL WriteFile(HANDLE hFile, const void* lpBuffer, DWORD nNumberOfBytesToWrite,
|
|
DWORD* lpNumberOfBytesWritten, void* lpOverlapped);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 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);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 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, "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);
|
|
|
|
DWORD exitCode = 0;
|
|
GetExitCodeProcess(pi.hProcess, &exitCode);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
|
|
if (exitCode != 0) {
|
|
print("shell: '");
|
|
print(cmd);
|
|
print("' exited with code ");
|
|
printUInt(exitCode);
|
|
print("\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Entry point
|
|
// ---------------------------------------------------------------------------
|
|
int main() {
|
|
hIn = GetStdHandle(STD_INPUT_HANDLE);
|
|
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
char lineBuf[MAX_CMD];
|
|
|
|
print("shell: ready\n");
|
|
|
|
while (true) {
|
|
print("> ");
|
|
readLine(lineBuf, MAX_CMD);
|
|
if (runCommand(trim(lineBuf)))
|
|
break;
|
|
}
|
|
|
|
print("shell: goodbye\n");
|
|
ExitProcess(0);
|
|
}
|