272 lines
8.8 KiB
C
272 lines
8.8 KiB
C
// User application entry points for shell_win32.
|
|
|
|
#include <windows.h>
|
|
#include <ntlite/syscall.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#define MAX_CMD 256
|
|
static HANDLE hIn = INVALID_HANDLE_VALUE;
|
|
static HANDLE hOut = INVALID_HANDLE_VALUE;
|
|
|
|
// External/Custom API.
|
|
BOOL ClearConsoleScreenBuffer(HANDLE hConsole);
|
|
|
|
// --- Output Helpers ---.
|
|
static void print(const char* s) {
|
|
DWORD written;
|
|
if (hOut != INVALID_HANDLE_VALUE) WriteConsoleA(hOut, s, (DWORD)strlen(s), &written, 0);
|
|
else OutputDebugStringA(s);
|
|
}
|
|
|
|
// Prints u Int.
|
|
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; }
|
|
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
|
|
}
|
|
|
|
// Prints int.
|
|
static void printInt(int32_t n) {
|
|
if (n < 0) { print("-"); n = -n; }
|
|
printUInt((uint32_t)n);
|
|
}
|
|
|
|
// --- Line Editor ---.
|
|
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 == '\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 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;
|
|
}
|
|
|
|
// --- Built-in Commands ---.
|
|
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> <tx> - 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_PATH]; int i = 0;
|
|
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;
|
|
}
|
|
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;
|
|
WriteFile(hFile, args + i + 1, (DWORD)strlen(args + i + 1), &written, 0);
|
|
CloseHandle(hFile);
|
|
print("File written successfully.\n");
|
|
}
|
|
|
|
typedef struct SHELL_WIN32_DIR_ENTRY {
|
|
char Name[MAX_PATH];
|
|
DWORD Attributes;
|
|
} SHELL_WIN32_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_WIN32_DIR_ENTRY* entries, size_t count) {
|
|
for (size_t i = 1; i < count; i++) {
|
|
SHELL_WIN32_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) {
|
|
WIN32_FIND_DATAA fd;
|
|
char search_path[MAX_PATH];
|
|
const char* target = (path && *path != '\0') ? path : ".";
|
|
|
|
size_t len = strlen(target);
|
|
if (len + 3 > MAX_PATH) { print("dir: path too long\n"); return; }
|
|
strcpy(search_path, target);
|
|
strcat(search_path, "\\*");
|
|
|
|
HANDLE hFind = FindFirstFileA(search_path, &fd);
|
|
if (hFind == INVALID_HANDLE_VALUE) { print("dir: failed to read directory\n"); return; }
|
|
|
|
void* heap = GetProcessHeap();
|
|
SHELL_WIN32_DIR_ENTRY* entries = NULL;
|
|
size_t count = 0;
|
|
size_t capacity = 0;
|
|
|
|
do {
|
|
if (count == capacity) {
|
|
size_t nextCapacity = capacity ? capacity * 2u : 64u;
|
|
size_t bytes = nextCapacity * sizeof(SHELL_WIN32_DIR_ENTRY);
|
|
SHELL_WIN32_DIR_ENTRY* next;
|
|
if (entries)
|
|
next = (SHELL_WIN32_DIR_ENTRY*)HeapReAlloc(heap, 0, entries, bytes);
|
|
else
|
|
next = (SHELL_WIN32_DIR_ENTRY*)HeapAlloc(heap, 0, bytes);
|
|
if (!next) {
|
|
print("dir: out of memory\n");
|
|
if (entries) HeapFree(heap, 0, entries);
|
|
FindClose(hFind);
|
|
return;
|
|
}
|
|
entries = next;
|
|
capacity = nextCapacity;
|
|
}
|
|
|
|
strncpy(entries[count].Name, fd.cFileName, MAX_PATH - 1);
|
|
entries[count].Name[MAX_PATH - 1] = '\0';
|
|
entries[count].Attributes = fd.dwFileAttributes;
|
|
count++;
|
|
} while (FindNextFileA(hFind, &fd));
|
|
FindClose(hFind);
|
|
|
|
shellSortDirEntries(entries, count);
|
|
for (size_t i = 0; i < count; i++) {
|
|
print(entries[i].Attributes & FILE_ATTRIBUTE_DIRECTORY ? "<DIR> " : " ");
|
|
print(entries[i].Name);
|
|
print("\n");
|
|
}
|
|
if (entries)
|
|
HeapFree(heap, 0, entries);
|
|
}
|
|
|
|
// Implements the cmd type operation.
|
|
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: cannot open file\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");
|
|
}
|
|
|
|
// 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) { ClearConsoleScreenBuffer(hOut); return false; }
|
|
if (strcmp(cmd, "crash") == 0) { *(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 (!SetCurrentDirectoryA(cmd + 3)) print("cd: failed\n"); return false; }
|
|
if (strncmp(cmd, "del ", 4) == 0) { DeleteFileA(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; }
|
|
|
|
PROCESS_INFORMATION pi;
|
|
char cmdBuf[MAX_CMD];
|
|
strncpy(cmdBuf, cmd, MAX_CMD - 1);
|
|
cmdBuf[MAX_CMD - 1] = '\0';
|
|
if (CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi)) {
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
if (pi.hThread && pi.hThread != INVALID_HANDLE_VALUE)
|
|
CloseHandle(pi.hThread);
|
|
if (pi.hProcess && pi.hProcess != INVALID_HANDLE_VALUE)
|
|
CloseHandle(pi.hProcess);
|
|
FlushConsoleInputBuffer(hIn);
|
|
} else {
|
|
print("shell: launch failed\n");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Runs the requested operation.
|
|
int main(void) {
|
|
hIn = GetStdHandle(STD_INPUT_HANDLE);
|
|
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
const char* cmdline = GetCommandLineA();
|
|
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");
|
|
ExitProcess(0);
|
|
}
|