From 2a3679b2722953d35a264c8b6e00f446297fb089 Mon Sep 17 00:00:00 2001 From: IoIxD Date: Mon, 19 Jan 2026 15:03:36 -0700 Subject: [PATCH] path and cd builtin --- Makefile | 4 +- src/builtins/builtins.c | 22 ++ src/builtins/builtins.h | 8 + src/lexer/lexer.c | 67 +++++- src/lexer/lexer.h | 10 +- src/main.c | 14 +- src/system/linux/env.c | 105 +++++++++ src/system/linux/errno.c | 405 ++++++++++++++++++++++++++++++++++ src/system/linux/linux.c | 71 +++++- src/system/linux/linux.h | 15 ++ src/system/system.h | 74 ++++++- src/util.h | 8 - src/{util.c => util/calloc.c} | 0 src/util/hash.c | 12 + src/util/string.c | 26 +++ src/util/util.h | 17 ++ 16 files changed, 814 insertions(+), 44 deletions(-) create mode 100644 src/builtins/builtins.c create mode 100644 src/builtins/builtins.h create mode 100644 src/system/linux/env.c create mode 100644 src/system/linux/errno.c create mode 100644 src/system/linux/linux.h delete mode 100644 src/util.h rename src/{util.c => util/calloc.c} (100%) create mode 100644 src/util/hash.c create mode 100644 src/util/string.c create mode 100644 src/util/util.h diff --git a/Makefile b/Makefile index bce7885..ef4c691 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ -SOURCES := src/main.c src/util.c src/lexer/lexer.c +SOURCES := src/main.c src/util/calloc.c src/util/string.c src/util/hash.c src/lexer/lexer.c src/builtins/builtins.c CFLAGS := -g -O0 -nostdlib UNAME := $(shell uname) ARCH := $(shell uname -m) ifeq ($(UNAME), Linux) -SOURCES += src/system/linux/linux.c src/system/linux/alloc.c +SOURCES += src/system/linux/linux.c src/system/linux/alloc.c src/system/linux/env.c src/system/linux/errno.c CFLAGS += -fno-stack-protector CFLAGS += -DMSH_ENTRY_POINT="void _start()" ifeq ($(ARCH),i386) diff --git a/src/builtins/builtins.c b/src/builtins/builtins.c new file mode 100644 index 0000000..b68144d --- /dev/null +++ b/src/builtins/builtins.c @@ -0,0 +1,22 @@ +#include "builtins.h" +#include "../system/system.h" +#include "../util/util.h" + +int hashes_calced = 0; +int cd_hash = 0; + +int msh_try_builtin(char *cmd, char **args) { + int hash = str_hash(cmd); + + if (hashes_calced == 0) { + cd_hash = str_hash("cd"); + hashes_calced = 1; + } + if (hash == cd_hash) { + msh_builtin_cd(args[1]); + return 1; + } + return 0; +}; + +void msh_builtin_cd(char *ch) { msh_set_working_directory(ch); }; diff --git a/src/builtins/builtins.h b/src/builtins/builtins.h new file mode 100644 index 0000000..ea85771 --- /dev/null +++ b/src/builtins/builtins.h @@ -0,0 +1,8 @@ +#ifndef __MSH_BUILTINS_H +#define __MSH_BUILTINS_H + +int msh_try_builtin(char *cmd, char **args); + +void msh_builtin_cd(char *); + +#endif diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index 2387e17..e310f88 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -1,5 +1,6 @@ #include "lexer.h" +#include "../builtins/builtins.h" #include "../system/system.h" struct msh_lexer_t { @@ -19,12 +20,12 @@ msh_lexer *msh_lexer_new() { return l; }; -void msh_lexer_push_item(msh_lexer *lexer, msh_lexer_item_type type, - unsigned char *buf, SIZE_TYPE strlen) { +void msh_lexer_push_item(msh_lexer *lexer, msh_lexer_item_type type, char *buf, + SIZE_TYPE strlen) { int i; msh_lexer_item *item = msh_callocate(sizeof(msh_lexer_item), 1); - item->str = msh_allocate(strlen * sizeof(unsigned char)); + item->str = msh_allocate(strlen * sizeof(char)); for (i = 0; i < strlen; i++) { item->str[i] = buf[i]; } @@ -38,10 +39,10 @@ void msh_lexer_push_item(msh_lexer *lexer, msh_lexer_item_type type, lexer->item_list_cur = lexer->item_list_cur->next; }; -void msh_lexer_parse(msh_lexer *lexer, unsigned char *buf, SIZE_TYPE sz) { +void msh_lexer_parse(msh_lexer *lexer, char *buf, SIZE_TYPE sz) { int i = 0, n = 0, last = 0; - unsigned char *cur_word_buf = msh_callocate(1024, sizeof(unsigned char)); + char *cur_word_buf = msh_callocate(1024, sizeof(char)); for (i = 0; i < sz; i++) { int do_clear = 0; @@ -91,7 +92,7 @@ void msh_lexer_parse(msh_lexer *lexer, unsigned char *buf, SIZE_TYPE sz) { } if (do_clear) { msh_deallocate(cur_word_buf); - cur_word_buf = msh_callocate(1024, sizeof(unsigned char)); + cur_word_buf = msh_callocate(1024, sizeof(char)); n = 0; } } @@ -99,12 +100,14 @@ void msh_lexer_parse(msh_lexer *lexer, unsigned char *buf, SIZE_TYPE sz) { void msh_lexer_execute(msh_lexer *lexer) { msh_lexer_item_list *list = lexer->item_list_head; - unsigned char *cmd = 0; + char *cmd = 0; SIZE_TYPE cmd_size = 0; SIZE_TYPE args_size = 0; - unsigned char **args = 0; - int is_first_arg = 0, n = 0; + char **args = 0; + int is_first_arg = 0, i = 0, n = 0, j = 0, counter = 0, exec_success = 0; + const char *path = msh_env_get_path(); + char *full_path; while (list != 0) { if (!list->item) { @@ -119,7 +122,7 @@ void msh_lexer_execute(msh_lexer *lexer) { list = list->next; } list = lexer->item_list_head; - args = msh_allocate((args_size + 1) * sizeof(unsigned char *)); + args = msh_allocate((args_size + 1) * sizeof(char *)); while (list != 0) { if (!list->item) { break; @@ -129,7 +132,49 @@ void msh_lexer_execute(msh_lexer *lexer) { } args[n++] = 0; - msh_execute(cmd, args); + /* Check if the command is a built-in */ + if (msh_try_builtin(cmd, args)) { + exec_success = 1; + } + /* If the command is a file, first try and execute it. */ + else if (msh_path_exists(cmd)) { + msh_execute(cmd, args); + exec_success = 1; + } else { + /* loop through all the path entries and check if the file exists in them. + */ + i = 0, n = 0, j = 0; + full_path = msh_allocate(1024); + while (1) { + full_path[n++] = path[j]; + if (path[j] == '\0') { + full_path[n - 1] = msh_path_seperator; + for (i = 0; cmd[i] != 0; i++) { + full_path[i + n] = cmd[i]; + } + n += i; + full_path[n++] = '\0'; + + if (msh_path_exists(full_path)) { + msh_execute(full_path, args); + exec_success = 1; + break; + } + + counter++; + if (counter > 2) { + break; + } + n = 0; + } else { + counter = 0; + } + j++; + } + } + if (!exec_success) { + msh_print_strings("msh: ", cmd, ": command not found\n", 0); + } msh_deallocate(args); diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index 276e429..88c8303 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -1,7 +1,7 @@ #ifndef __MSH_LEXER_H #define __MSH_LEXER_H -#include "../util.h" +#include "../util/util.h" typedef enum msh_lexer_item_type_t { MSH_LEXER_ITEM_TYPE_UNKNOWN = 0, @@ -16,7 +16,7 @@ typedef enum msh_lexer_item_type_t { typedef struct msh_lexer_item_t { msh_lexer_item_type type; - unsigned char *str; + char *str; SIZE_TYPE strlen; } msh_lexer_item; @@ -28,9 +28,9 @@ typedef struct msh_lexer_item_list_t { typedef struct msh_lexer_t msh_lexer; msh_lexer *msh_lexer_new(); -void msh_lexer_parse(msh_lexer *lexer, unsigned char *buf, SIZE_TYPE sz); -void msh_lexer_push_item(msh_lexer *lexer, msh_lexer_item_type type, - unsigned char *buf, SIZE_TYPE strlen); +void msh_lexer_parse(msh_lexer *lexer, char *buf, SIZE_TYPE sz); +void msh_lexer_push_item(msh_lexer *lexer, msh_lexer_item_type type, char *buf, + SIZE_TYPE strlen); void msh_lexer_flush(msh_lexer *lexer); diff --git a/src/main.c b/src/main.c index cf6c43f..e14356e 100644 --- a/src/main.c +++ b/src/main.c @@ -1,19 +1,21 @@ #include "lexer/lexer.h" #include "system/system.h" -unsigned char prefix[] = "%> "; - /* Makefile-defined system entry point. */ MSH_ENTRY_POINT { - msh_lexer *lexer = msh_lexer_new(); + msh_lexer *lexer; + + msh_backend_init(); + + lexer = msh_lexer_new(); while (1) { int out; - unsigned char *buf = msh_callocate(2048, sizeof(unsigned char)); + char *buf = msh_callocate(2048, sizeof(char)); int i = 0, n = 0, last = 0; unsigned long long sz = 0; - msh_write(prefix, sizeof(prefix)); + msh_print_strings(msh_working_directory(), " >", 0); while ((out = msh_read_input(buf, &sz, 2048)), !out) { } @@ -27,4 +29,6 @@ MSH_ENTRY_POINT { } msh_lexer_free(lexer); + + msh_backend_deinit(); } diff --git a/src/system/linux/env.c b/src/system/linux/env.c new file mode 100644 index 0000000..676c6e9 --- /dev/null +++ b/src/system/linux/env.c @@ -0,0 +1,105 @@ +#include "../system.h" +#include "linux.h" +#include "syscall.h" + +typedef struct msh_linux_env_item_t msh_linux_env_item; +struct msh_linux_env_item_t { + unsigned long key; + char *value; + int is_path; + msh_linux_env_item *next; +}; + +static msh_linux_env_item *env_items = 0; + +void msh_linux_env_map_init() { + msh_file *env_file; + SIZE_TYPE file_size; + char *file_buf; + char ch = 1; + int counter = 0, i = 0; + msh_linux_env_item *env_item_cur; + + if (!env_items) { + env_items = msh_callocate(1, sizeof(msh_linux_env_item)); + } + env_item_cur = env_items; + + env_file = msh_file_open("/proc/self/environ"); + file_buf = msh_callocate(512, sizeof(char)); + + while (1) { + msh_file_read(env_file, &ch, 1); + + if (ch == ':' && env_item_cur->is_path == 1) { + file_buf[file_size++] = '\0'; + } else if (ch != '=') { + file_buf[file_size++] = ch; + } else { + file_buf[file_size++] = '\0'; + } + + if (ch == '=') { + env_item_cur->key = str_hash(file_buf); + if (env_item_cur->key == str_hash("PATH")) { + env_item_cur->is_path = 1; + } + for (i = 0; i < file_size; i++) { + file_buf[i] = 0; + } + file_size = 0; + } else if (ch == '\0') { + env_item_cur->value = file_buf; + env_item_cur->next = msh_callocate(1, sizeof(msh_linux_env_item)); + + /* double null for path variables */ + if (env_item_cur->is_path) { + file_buf[file_size++] = '\0'; + } + + file_buf = msh_callocate(512, sizeof(char)); + file_size = 0; + env_item_cur = env_item_cur->next; + + counter++; + + if (counter >= 2) { + break; + } + } else { + counter = 0; + } + } + + msh_deallocate(file_buf); + + msh_file_close(env_file); +}; +void msh_linux_env_map_deinit() {}; + +const char *msh_env_get(const char *key) { + msh_linux_env_item *env_item_cur = env_items; + int hash = str_hash((char *)key); + + while (env_item_cur->next != 0) { + if (env_item_cur->key == hash) { + return env_item_cur->value; + } + env_item_cur = env_item_cur->next; + } + + return 0; +}; + +const char *msh_env_get_path() { + msh_linux_env_item *env_item_cur = env_items; + + while (env_item_cur->next != 0) { + if (env_item_cur->is_path) { + return env_item_cur->value; + } + env_item_cur = env_item_cur->next; + } + + return 0; +}; diff --git a/src/system/linux/errno.c b/src/system/linux/errno.c new file mode 100644 index 0000000..d6f4829 --- /dev/null +++ b/src/system/linux/errno.c @@ -0,0 +1,405 @@ +#include "linux.h" + +#include +#undef errno + +void msh_linux_print_errno(int errno) { + switch (errno) { + case EPERM: + msh_print_string("Operation not permitted"); + break; + case ENOENT: + msh_print_string("No such file or directory"); + break; + case ESRCH: + msh_print_string("No such process"); + break; + case EINTR: + msh_print_string("Interrupted system call"); + break; + case EIO: + msh_print_string("Input/output error"); + break; + case ENXIO: + msh_print_string("No such device or address"); + break; + case E2BIG: + msh_print_string("Argument list too long"); + break; + case ENOEXEC: + msh_print_string("Exec format error"); + break; + case EBADF: + msh_print_string("Bad file descriptor"); + break; + case ECHILD: + msh_print_string("No child processes"); + break; + case EAGAIN: + msh_print_string("Resource temporarily unavailable"); + break; + case ENOMEM: + msh_print_string("Cannot allocate memory"); + break; + case EACCES: + msh_print_string("Permission denied"); + break; + case EFAULT: + msh_print_string("Bad address"); + break; + case ENOTBLK: + msh_print_string("Block device required"); + break; + case EBUSY: + msh_print_string("Device or resource busy"); + break; + case EEXIST: + msh_print_string("File exists"); + break; + case EXDEV: + msh_print_string("Invalid cross-device link"); + break; + case ENODEV: + msh_print_string("No such device"); + break; + case ENOTDIR: + msh_print_string("Not a directory"); + break; + case EISDIR: + msh_print_string("Is a directory"); + break; + case EINVAL: + msh_print_string("Invalid argument"); + break; + case ENFILE: + msh_print_string("Too many open files in system"); + break; + case EMFILE: + msh_print_string("Too many open files"); + break; + case ENOTTY: + msh_print_string("Inappropriate ioctl for device"); + break; + case ETXTBSY: + msh_print_string("Text file busy"); + break; + case EFBIG: + msh_print_string("File too large"); + break; + case ENOSPC: + msh_print_string("No space left on device"); + break; + case ESPIPE: + msh_print_string("Illegal seek"); + break; + case EROFS: + msh_print_string("Read-only file system"); + break; + case EMLINK: + msh_print_string("Too many links"); + break; + case EPIPE: + msh_print_string("Broken pipe"); + break; + case EDOM: + msh_print_string("Numerical argument out of domain"); + break; + case ERANGE: + msh_print_string("Numerical result out of range"); + break; + case EDEADLK: + msh_print_string("Resource deadlock avoided"); + break; + case ENAMETOOLONG: + msh_print_string("File name too long"); + break; + case ENOLCK: + msh_print_string("No locks available"); + break; + case ENOSYS: + msh_print_string("Function not implemented"); + break; + case ENOTEMPTY: + msh_print_string("Directory not empty"); + break; + case ELOOP: + msh_print_string("Too many levels of symbolic links"); + break; + case ENOMSG: + msh_print_string("No message of desired type"); + break; + case EIDRM: + msh_print_string("Identifier removed"); + break; + case ECHRNG: + msh_print_string("Channel number out of range"); + break; + case EL2NSYNC: + msh_print_string("Level 2 not synchronized"); + break; + case EL3HLT: + msh_print_string("Level 3 halted"); + break; + case EL3RST: + msh_print_string("Level 3 reset"); + break; + case ELNRNG: + msh_print_string("Link number out of range"); + break; + case EUNATCH: + msh_print_string("Protocol driver not attached"); + break; + case ENOCSI: + msh_print_string("No CSI structure available"); + break; + case EL2HLT: + msh_print_string("Level 2 halted"); + break; + case EBADE: + msh_print_string("Invalid exchange"); + break; + case EBADR: + msh_print_string("Invalid request descriptor"); + break; + case EXFULL: + msh_print_string("Exchange full"); + break; + case ENOANO: + msh_print_string("No anode"); + break; + case EBADRQC: + msh_print_string("Invalid request code"); + break; + case EBADSLT: + msh_print_string("Invalid slot"); + break; + case EBFONT: + msh_print_string("Bad font file format"); + break; + case ENOSTR: + msh_print_string("Device not a stream"); + break; + case ENODATA: + msh_print_string("No data available"); + break; + case ETIME: + msh_print_string("Timer expired"); + break; + case ENOSR: + msh_print_string("Out of streams resources"); + break; + case ENONET: + msh_print_string("Machine is not on the network"); + break; + case ENOPKG: + msh_print_string("Package not installed"); + break; + case EREMOTE: + msh_print_string("Object is remote"); + break; + case ENOLINK: + msh_print_string("Link has been severed"); + break; + case EADV: + msh_print_string("Advertise error"); + break; + case ESRMNT: + msh_print_string("Srmount error"); + break; + case ECOMM: + msh_print_string("Communication error on send"); + break; + case EPROTO: + msh_print_string("Protocol error"); + break; + case EMULTIHOP: + msh_print_string("Multihop attempted"); + break; + case EDOTDOT: + msh_print_string("RFS specific error"); + break; + case EBADMSG: + msh_print_string("Bad message"); + break; + case EOVERFLOW: + msh_print_string("Value too large for defined data type"); + break; + case ENOTUNIQ: + msh_print_string("Name not unique on network"); + break; + case EBADFD: + msh_print_string("File descriptor in bad state"); + break; + case EREMCHG: + msh_print_string("Remote address changed"); + break; + case ELIBACC: + msh_print_string("Can not access a needed shared library"); + break; + case ELIBBAD: + msh_print_string("Accessing a corrupted shared library"); + break; + case ELIBSCN: + msh_print_string(".lib section in a.out corrupted"); + break; + case ELIBMAX: + msh_print_string("Attempting to link in too many shared libraries"); + break; + case ELIBEXEC: + msh_print_string("Cannot exec a shared library directly"); + break; + case EILSEQ: + msh_print_string("Invalid or incomplete multibyte or wide character"); + break; + case ERESTART: + msh_print_string("Interrupted system call should be restarted"); + break; + case ESTRPIPE: + msh_print_string("Streams pipe error"); + break; + case EUSERS: + msh_print_string("Too many users"); + break; + case ENOTSOCK: + msh_print_string("Socket operation on non-socket"); + break; + case EDESTADDRREQ: + msh_print_string("Destination address required"); + break; + case EMSGSIZE: + msh_print_string("Message too long"); + break; + case EPROTOTYPE: + msh_print_string("Protocol wrong type for socket"); + break; + case ENOPROTOOPT: + msh_print_string("Protocol not available"); + break; + case EPROTONOSUPPORT: + msh_print_string("Protocol not supported"); + break; + case ESOCKTNOSUPPORT: + msh_print_string("Socket type not supported"); + break; + case EOPNOTSUPP: + msh_print_string("Operation not supported"); + break; + case EPFNOSUPPORT: + msh_print_string("Protocol family not supported"); + break; + case EAFNOSUPPORT: + msh_print_string("Address family not supported by protocol"); + break; + case EADDRINUSE: + msh_print_string("Address already in use"); + break; + case EADDRNOTAVAIL: + msh_print_string("Cannot assign requested address"); + break; + case ENETDOWN: + msh_print_string("Network is down"); + break; + case ENETUNREACH: + msh_print_string("Network is unreachable"); + break; + case ENETRESET: + msh_print_string("Network dropped connection on reset"); + break; + case ECONNABORTED: + msh_print_string("Software caused connection abort"); + break; + case ECONNRESET: + msh_print_string("Connection reset by peer"); + break; + case ENOBUFS: + msh_print_string("No buffer space available"); + break; + case EISCONN: + msh_print_string("Transport endpoint is already connected"); + break; + case ENOTCONN: + msh_print_string("Transport endpoint is not connected"); + break; + case ESHUTDOWN: + msh_print_string("Cannot send after transport endpoint shutdown"); + break; + case ETOOMANYREFS: + msh_print_string("Too many references: cannot splice"); + break; + case ETIMEDOUT: + msh_print_string("Connection timed out"); + break; + case ECONNREFUSED: + msh_print_string("Connection refused"); + break; + case EHOSTDOWN: + msh_print_string("Host is down"); + break; + case EHOSTUNREACH: + msh_print_string("No route to host"); + break; + case EALREADY: + msh_print_string("Operation already in progress"); + break; + case EINPROGRESS: + msh_print_string("Operation now in progress"); + break; + case ESTALE: + msh_print_string("Stale file handle"); + break; + case EUCLEAN: + msh_print_string("Structure needs cleaning"); + break; + case ENOTNAM: + msh_print_string("Not a XENIX named type file"); + break; + case ENAVAIL: + msh_print_string("No XENIX semaphores available"); + break; + case EISNAM: + msh_print_string("Is a named type file"); + break; + case EREMOTEIO: + msh_print_string("Remote I/O error"); + break; + case EDQUOT: + msh_print_string("Disk quota exceeded"); + break; + case ENOMEDIUM: + msh_print_string("No medium found"); + break; + case EMEDIUMTYPE: + msh_print_string("Wrong medium type"); + break; + case ECANCELED: + msh_print_string("Operation canceled"); + break; + case ENOKEY: + msh_print_string("Required key not available"); + break; + case EKEYEXPIRED: + msh_print_string("Key has expired"); + break; + case EKEYREVOKED: + msh_print_string("Key has been revoked"); + break; + case EKEYREJECTED: + msh_print_string("Key was rejected by service"); + break; + case EOWNERDEAD: + msh_print_string("Owner died"); + break; + case ENOTRECOVERABLE: + msh_print_string("State not recoverable"); + break; + case ERFKILL: + msh_print_string("Operation not possible due to RF-kill"); + break; + case EHWPOISON: + msh_print_string("Memory page has hardware error"); + break; + default: + msh_print_string("Unknown"); + break; + } +} diff --git a/src/system/linux/linux.c b/src/system/linux/linux.c index 69269f1..a82524f 100644 --- a/src/system/linux/linux.c +++ b/src/system/linux/linux.c @@ -1,10 +1,14 @@ +#include "linux.h" #include "../system.h" #include "syscall.h" #include +#include #include -int msh_read_input(unsigned char *ptr, SIZE_TYPE *size, SIZE_TYPE n) { +char msh_path_seperator = '/'; + +int msh_read_input(char *ptr, SIZE_TYPE *size, SIZE_TYPE n) { char ch; SIZE_TYPE sz; if (*size >= n) { @@ -21,19 +25,20 @@ int msh_read_input(unsigned char *ptr, SIZE_TYPE *size, SIZE_TYPE n) { return 0; }; -void msh_write(unsigned char *st, SIZE_TYPE size) { - SYSCALL(write, 1, st, size); -} +void msh_write(char *st, SIZE_TYPE size) { SYSCALL(write, 1, st, size); } -int msh_execute(unsigned char *cmd, unsigned char **args) { +int msh_execute(char *cmd, char **args) { if (*cmd == '\0') { return 0; } SIZE_TYPE fork = (SIZE_TYPE)SYSCALL(fork, 0); // int fork = 0; if (fork == 0) { - SYSCALL(execve, cmd, args, 0); - return 0; + int h = (int)SYSCALL(execve, cmd, args, 0); + if (h == -2) { + return -1; + } + return h; } else { siginfo_t info; SYSCALL(waitid, P_ALL, 0, &info, WEXITED); @@ -42,3 +47,55 @@ int msh_execute(unsigned char *cmd, unsigned char **args) { } return 0; }; + +void msh_exit_shell() { SYSCALL(exit, 0); }; + +msh_file *msh_file_open(const char *file) { + msh_file *f = msh_allocate(sizeof(struct msh_file_t)); + + f->fd = (int)(SIZE_TYPE)SYSCALL(open, file, O_RDONLY); + + if (f->fd < 0) { + msh_print_strings("Error opening ", file, ": ", 0); + msh_linux_print_errno(f->fd * -1); + msh_print_string("\n"); + + msh_exit_shell(); + } + + return f; +}; + +void msh_file_close(msh_file *file) { SYSCALL(close, file->fd); }; + +void msh_file_read(msh_file *file, char *ptr, SIZE_TYPE n) { + SYSCALL(read, file->fd, ptr, n); +}; + +SIZE_TYPE msh_file_size(msh_file *file) { + SIZE_TYPE sz; + + SYSCALL(lseek, file->fd, 0, SEEK_END); + + sz = (SIZE_TYPE)SYSCALL(lseek, file->fd, 0, SEEK_CUR); + + SYSCALL(lseek, file->fd, 0, SEEK_SET); + + return sz; +}; + +static char cwd[512]; +int num; + +const char *msh_working_directory() { + SYSCALL(getcwd, &cwd, &num); + return cwd; +}; + +void msh_set_working_directory(const char *dir) { SYSCALL(chdir, dir); } + +int msh_path_exists(char *file) { return SYSCALL(access, file, F_OK) == 0; }; + +void msh_backend_init() { msh_linux_env_map_init(); }; + +void msh_backend_deinit() { msh_linux_env_map_deinit(); }; diff --git a/src/system/linux/linux.h b/src/system/linux/linux.h new file mode 100644 index 0000000..b272463 --- /dev/null +++ b/src/system/linux/linux.h @@ -0,0 +1,15 @@ +#ifndef __MSH_SYSTEM_LINUX_LINUX_H +#define __MSH_SYSTEM_LINUX_LINUX_H + +#include "../../util/util.h" +#include "../system.h" + +void msh_linux_print_errno(int errno); +void msh_linux_env_map_init(); +void msh_linux_env_map_deinit(); + +struct msh_file_t { + int fd; +}; + +#endif diff --git a/src/system/system.h b/src/system/system.h index b2d8833..241ca51 100644 --- a/src/system/system.h +++ b/src/system/system.h @@ -9,6 +9,12 @@ #endif #endif +/* Initialize anything that the backend needs. */ +void msh_backend_init(); + +/* Deinitialize anything that the backend needs. */ +void msh_backend_deinit(); + /* Allocate memory */ void *msh_allocate(SIZE_TYPE size); @@ -18,14 +24,55 @@ void *msh_callocate(SIZE_TYPE number, SIZE_TYPE size); /* Deallocate memory previously allocated by msh_allocate */ void msh_deallocate(void *ptr); +/* Exit shell */ +void msh_exit_shell(); + +/* File given by the file functions */ +typedef struct msh_file_t msh_file; + +/* + * Open the given file + */ +msh_file *msh_file_open(const char *file); + +/* + * Close the given file + */ +void msh_file_close(msh_file *file); + +/* + * Get the file's size + */ +SIZE_TYPE msh_file_size(msh_file *file); + +/* + * Return whether the path exists + */ +int msh_path_exists(char *file); + +/* + * Read up to x characters from file into ptr + */ +void msh_file_read(msh_file *file, char *ptr, SIZE_TYPE n); + /* * Read user input into ptr, stopping if it has reached n characters. * returns 1 if finished reading, 2 if it ran out of space. */ -int msh_read_input(unsigned char *ptr, SIZE_TYPE *size, SIZE_TYPE n); +int msh_read_input(char *ptr, SIZE_TYPE *size, SIZE_TYPE n); /* Write characters to terminal. */ -void msh_write(unsigned char *st, SIZE_TYPE size); +void msh_write(char *st, SIZE_TYPE size); + +/* + * Get working directory + */ +const char *msh_working_directory(); + +/* + * Set working directory + */ +void msh_set_working_directory(const char *); /* * Execute given command with args. @@ -33,12 +80,27 @@ void msh_write(unsigned char *st, SIZE_TYPE size); * cmd is self explanatory, args is a null terminated array of strings based on * the arguments the user provided. * - * The function is also responsible for searching the user's PATH to execute the - * first command. * - * Returns exit status (or 0 on platforms where that isn't a thing). + * Non-zero return values are the exit status. -1 implies the command was not + * found. */ -int msh_execute(unsigned char *cmd, unsigned char **args); +int msh_execute(char *cmd, char **args); + +/* + Get an environment variable with the given key. + */ +const char *msh_env_get(const char *key); + +/* + * Gets a NULL-delimited string representing the folders to search for in the + * path, ending in two NULLs. + */ +const char *msh_env_get_path(); + +/* + * System path seperator + */ +extern char msh_path_seperator; #ifndef MSH_ENTRY_POINT /* diff --git a/src/util.h b/src/util.h deleted file mode 100644 index 926e238..0000000 --- a/src/util.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __MSH_UTIL_H -#define __MSH_UTIL_H - -#include "system/system.h" - -void *msh_callocate(SIZE_TYPE number, SIZE_TYPE size); - -#endif diff --git a/src/util.c b/src/util/calloc.c similarity index 100% rename from src/util.c rename to src/util/calloc.c diff --git a/src/util/hash.c b/src/util/hash.c new file mode 100644 index 0000000..734c07e --- /dev/null +++ b/src/util/hash.c @@ -0,0 +1,12 @@ +#include "util.h" + +unsigned long str_hash(char *str) { + unsigned long hash = 5381; + int c; + + while ((c = *str++), *str) { + hash = ((hash << 5) + hash) + c; + } + + return hash; +} diff --git a/src/util/string.c b/src/util/string.c new file mode 100644 index 0000000..2b0fe68 --- /dev/null +++ b/src/util/string.c @@ -0,0 +1,26 @@ +#include "util.h" + +#include + +void msh_print_string(char *str) { + int i = 0; + while (str[i] != '\0') { + msh_write(&str[i], 1); + i++; + } +} + +void msh_print_strings(char *str, ...) { + va_list va; + + msh_print_string(str); + + va_start(va, str); + for (;;) { + char *st = va_arg(va, char *); + if (st == 0) { + break; + } + msh_print_string(st); + } +}; diff --git a/src/util/util.h b/src/util/util.h new file mode 100644 index 0000000..496e670 --- /dev/null +++ b/src/util/util.h @@ -0,0 +1,17 @@ +#ifndef __MSH_UTIL_H +#define __MSH_UTIL_H + +#include "../system/system.h" + +void *msh_callocate(SIZE_TYPE number, SIZE_TYPE size); + +/* Print NULL terminated array of strings */ +void msh_print_strings(char *str, ...); + +/* Print array of strings */ +void msh_print_string(char *str); + +/* Hash string */ +unsigned long str_hash(char *str); + +#endif