path and cd builtin
This commit is contained in:
parent
294261bdc7
commit
2a3679b272
16 changed files with 814 additions and 44 deletions
4
Makefile
4
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
|
CFLAGS := -g -O0 -nostdlib
|
||||||
|
|
||||||
UNAME := $(shell uname)
|
UNAME := $(shell uname)
|
||||||
ARCH := $(shell uname -m)
|
ARCH := $(shell uname -m)
|
||||||
|
|
||||||
ifeq ($(UNAME), Linux)
|
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 += -fno-stack-protector
|
||||||
CFLAGS += -DMSH_ENTRY_POINT="void _start()"
|
CFLAGS += -DMSH_ENTRY_POINT="void _start()"
|
||||||
ifeq ($(ARCH),i386)
|
ifeq ($(ARCH),i386)
|
||||||
|
|
|
||||||
22
src/builtins/builtins.c
Normal file
22
src/builtins/builtins.c
Normal file
|
|
@ -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); };
|
||||||
8
src/builtins/builtins.h
Normal file
8
src/builtins/builtins.h
Normal file
|
|
@ -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
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
|
|
||||||
|
#include "../builtins/builtins.h"
|
||||||
#include "../system/system.h"
|
#include "../system/system.h"
|
||||||
|
|
||||||
struct msh_lexer_t {
|
struct msh_lexer_t {
|
||||||
|
|
@ -19,12 +20,12 @@ msh_lexer *msh_lexer_new() {
|
||||||
return l;
|
return l;
|
||||||
};
|
};
|
||||||
|
|
||||||
void msh_lexer_push_item(msh_lexer *lexer, msh_lexer_item_type type,
|
void msh_lexer_push_item(msh_lexer *lexer, msh_lexer_item_type type, char *buf,
|
||||||
unsigned char *buf, SIZE_TYPE strlen) {
|
SIZE_TYPE strlen) {
|
||||||
int i;
|
int i;
|
||||||
msh_lexer_item *item = msh_callocate(sizeof(msh_lexer_item), 1);
|
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++) {
|
for (i = 0; i < strlen; i++) {
|
||||||
item->str[i] = buf[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;
|
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;
|
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++) {
|
for (i = 0; i < sz; i++) {
|
||||||
int do_clear = 0;
|
int do_clear = 0;
|
||||||
|
|
@ -91,7 +92,7 @@ void msh_lexer_parse(msh_lexer *lexer, unsigned char *buf, SIZE_TYPE sz) {
|
||||||
}
|
}
|
||||||
if (do_clear) {
|
if (do_clear) {
|
||||||
msh_deallocate(cur_word_buf);
|
msh_deallocate(cur_word_buf);
|
||||||
cur_word_buf = msh_callocate(1024, sizeof(unsigned char));
|
cur_word_buf = msh_callocate(1024, sizeof(char));
|
||||||
n = 0;
|
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) {
|
void msh_lexer_execute(msh_lexer *lexer) {
|
||||||
msh_lexer_item_list *list = lexer->item_list_head;
|
msh_lexer_item_list *list = lexer->item_list_head;
|
||||||
unsigned char *cmd = 0;
|
char *cmd = 0;
|
||||||
|
|
||||||
SIZE_TYPE cmd_size = 0;
|
SIZE_TYPE cmd_size = 0;
|
||||||
SIZE_TYPE args_size = 0;
|
SIZE_TYPE args_size = 0;
|
||||||
unsigned char **args = 0;
|
char **args = 0;
|
||||||
int is_first_arg = 0, n = 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) {
|
while (list != 0) {
|
||||||
if (!list->item) {
|
if (!list->item) {
|
||||||
|
|
@ -119,7 +122,7 @@ void msh_lexer_execute(msh_lexer *lexer) {
|
||||||
list = list->next;
|
list = list->next;
|
||||||
}
|
}
|
||||||
list = lexer->item_list_head;
|
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) {
|
while (list != 0) {
|
||||||
if (!list->item) {
|
if (!list->item) {
|
||||||
break;
|
break;
|
||||||
|
|
@ -129,7 +132,49 @@ void msh_lexer_execute(msh_lexer *lexer) {
|
||||||
}
|
}
|
||||||
args[n++] = 0;
|
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);
|
msh_deallocate(args);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef __MSH_LEXER_H
|
#ifndef __MSH_LEXER_H
|
||||||
#define __MSH_LEXER_H
|
#define __MSH_LEXER_H
|
||||||
|
|
||||||
#include "../util.h"
|
#include "../util/util.h"
|
||||||
|
|
||||||
typedef enum msh_lexer_item_type_t {
|
typedef enum msh_lexer_item_type_t {
|
||||||
MSH_LEXER_ITEM_TYPE_UNKNOWN = 0,
|
MSH_LEXER_ITEM_TYPE_UNKNOWN = 0,
|
||||||
|
|
@ -16,7 +16,7 @@ typedef enum msh_lexer_item_type_t {
|
||||||
|
|
||||||
typedef struct msh_lexer_item_t {
|
typedef struct msh_lexer_item_t {
|
||||||
msh_lexer_item_type type;
|
msh_lexer_item_type type;
|
||||||
unsigned char *str;
|
char *str;
|
||||||
SIZE_TYPE strlen;
|
SIZE_TYPE strlen;
|
||||||
} msh_lexer_item;
|
} msh_lexer_item;
|
||||||
|
|
||||||
|
|
@ -28,9 +28,9 @@ typedef struct msh_lexer_item_list_t {
|
||||||
typedef struct msh_lexer_t msh_lexer;
|
typedef struct msh_lexer_t msh_lexer;
|
||||||
|
|
||||||
msh_lexer *msh_lexer_new();
|
msh_lexer *msh_lexer_new();
|
||||||
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);
|
||||||
void msh_lexer_push_item(msh_lexer *lexer, msh_lexer_item_type type,
|
void msh_lexer_push_item(msh_lexer *lexer, msh_lexer_item_type type, char *buf,
|
||||||
unsigned char *buf, SIZE_TYPE strlen);
|
SIZE_TYPE strlen);
|
||||||
|
|
||||||
void msh_lexer_flush(msh_lexer *lexer);
|
void msh_lexer_flush(msh_lexer *lexer);
|
||||||
|
|
||||||
|
|
|
||||||
14
src/main.c
14
src/main.c
|
|
@ -1,19 +1,21 @@
|
||||||
#include "lexer/lexer.h"
|
#include "lexer/lexer.h"
|
||||||
#include "system/system.h"
|
#include "system/system.h"
|
||||||
|
|
||||||
unsigned char prefix[] = "%> ";
|
|
||||||
|
|
||||||
/* Makefile-defined system entry point. */
|
/* Makefile-defined system entry point. */
|
||||||
MSH_ENTRY_POINT {
|
MSH_ENTRY_POINT {
|
||||||
msh_lexer *lexer = msh_lexer_new();
|
msh_lexer *lexer;
|
||||||
|
|
||||||
|
msh_backend_init();
|
||||||
|
|
||||||
|
lexer = msh_lexer_new();
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int out;
|
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;
|
int i = 0, n = 0, last = 0;
|
||||||
unsigned long long sz = 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) {
|
while ((out = msh_read_input(buf, &sz, 2048)), !out) {
|
||||||
}
|
}
|
||||||
|
|
@ -27,4 +29,6 @@ MSH_ENTRY_POINT {
|
||||||
}
|
}
|
||||||
|
|
||||||
msh_lexer_free(lexer);
|
msh_lexer_free(lexer);
|
||||||
|
|
||||||
|
msh_backend_deinit();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
105
src/system/linux/env.c
Normal file
105
src/system/linux/env.c
Normal file
|
|
@ -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;
|
||||||
|
};
|
||||||
405
src/system/linux/errno.c
Normal file
405
src/system/linux/errno.c
Normal file
|
|
@ -0,0 +1,405 @@
|
||||||
|
#include "linux.h"
|
||||||
|
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
|
#include "linux.h"
|
||||||
#include "../system.h"
|
#include "../system.h"
|
||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <sys/file.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
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;
|
char ch;
|
||||||
SIZE_TYPE sz;
|
SIZE_TYPE sz;
|
||||||
if (*size >= n) {
|
if (*size >= n) {
|
||||||
|
|
@ -21,19 +25,20 @@ int msh_read_input(unsigned char *ptr, SIZE_TYPE *size, SIZE_TYPE n) {
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void msh_write(unsigned char *st, SIZE_TYPE size) {
|
void msh_write(char *st, SIZE_TYPE size) { SYSCALL(write, 1, st, 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') {
|
if (*cmd == '\0') {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
SIZE_TYPE fork = (SIZE_TYPE)SYSCALL(fork, 0);
|
SIZE_TYPE fork = (SIZE_TYPE)SYSCALL(fork, 0);
|
||||||
// int fork = 0;
|
// int fork = 0;
|
||||||
if (fork == 0) {
|
if (fork == 0) {
|
||||||
SYSCALL(execve, cmd, args, 0);
|
int h = (int)SYSCALL(execve, cmd, args, 0);
|
||||||
return 0;
|
if (h == -2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
} else {
|
} else {
|
||||||
siginfo_t info;
|
siginfo_t info;
|
||||||
SYSCALL(waitid, P_ALL, 0, &info, WEXITED);
|
SYSCALL(waitid, P_ALL, 0, &info, WEXITED);
|
||||||
|
|
@ -42,3 +47,55 @@ int msh_execute(unsigned char *cmd, unsigned char **args) {
|
||||||
}
|
}
|
||||||
return 0;
|
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(); };
|
||||||
|
|
|
||||||
15
src/system/linux/linux.h
Normal file
15
src/system/linux/linux.h
Normal file
|
|
@ -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
|
||||||
|
|
@ -9,6 +9,12 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Initialize anything that the backend needs. */
|
||||||
|
void msh_backend_init();
|
||||||
|
|
||||||
|
/* Deinitialize anything that the backend needs. */
|
||||||
|
void msh_backend_deinit();
|
||||||
|
|
||||||
/* Allocate memory */
|
/* Allocate memory */
|
||||||
void *msh_allocate(SIZE_TYPE size);
|
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 */
|
/* Deallocate memory previously allocated by msh_allocate */
|
||||||
void msh_deallocate(void *ptr);
|
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.
|
* Read user input into ptr, stopping if it has reached n characters.
|
||||||
* returns 1 if finished reading, 2 if it ran out of space.
|
* 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. */
|
/* 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.
|
* 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
|
* cmd is self explanatory, args is a null terminated array of strings based on
|
||||||
* the arguments the user provided.
|
* 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
|
#ifndef MSH_ENTRY_POINT
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
12
src/util/hash.c
Normal file
12
src/util/hash.c
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
26
src/util/string.c
Normal file
26
src/util/string.c
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
17
src/util/util.h
Normal file
17
src/util/util.h
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue