diff --git a/src/assembly/syscalls.asm b/src/assembly/syscalls.asm index 144aaa2..ba704fb 100644 --- a/src/assembly/syscalls.asm +++ b/src/assembly/syscalls.asm @@ -1,45 +1,65 @@ -global _start -global sys_read -global sys_write -global sys_fork -global sys_execve -global sys_wait4 -global sys_exit -extern main + global _start + global sys_open + global sys_close + global sys_getdents64 + global sys_stat + global sys_write + global sys_read + global sys_wait4 + global sys_exit + global environ -section .text + extern main + + section .data +environ: dq 0 + + section .text + +sys_open: + mov rax, 2 ; __NR_open + syscall + ret + +sys_close: + mov rax, 3 ; __NR_close + syscall + ret + +sys_getdents64: + mov rax, 217 ; __NR_getdents64 + syscall + ret + +sys_stat: + mov rax, 4 ; __NR_stat (legacy) + syscall + ret sys_read: - mov rax, 0 + mov rax, 0 ; __NR_read syscall ret sys_write: - mov rax, 1 - syscall - ret - -sys_fork: - mov rax, 57 - syscall - ret - -sys_execve: - mov rax, 59 + mov rax, 1 ; __NR_write syscall ret sys_wait4: - mov rax, 61 + mov rax, 61 ; __NR_wait4 syscall ret sys_exit: - mov rax, 60 + mov rax, 60 ; __NR_exit syscall + ret _start: - call main - mov rdi, rax - mov rax, 60 - syscall + ; SysV ABI: rdi=argc, rsi=argv, rdx=envp + mov [rel environ], rdx + call main + mov rdi, rax ; exit code = return value of main + mov rax, 60 ; __NR_exit + syscall \ No newline at end of file diff --git a/src/headers/nstdo.h b/src/headers/nstdo.h index 3cd7aab..499268d 100644 --- a/src/headers/nstdo.h +++ b/src/headers/nstdo.h @@ -6,48 +6,97 @@ extern "C" { #endif typedef unsigned long size_t; -typedef long off_t; +typedef long off_t; #define NULL ((void*)0) -#define O_RDONLY 0 -#define O_WRONLY 1 -#define O_RDWR 2 -#define O_CREAT 64 -#define O_TRUNC 512 -#define O_APPEND 1024 -#define O_CLOEXEC 02000000 +extern char **environ; +char *getenv(const char *name); +/* minimal open flags */ +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#define O_CREAT 64 +#define O_TRUNC 512 +#define O_APPEND 1024 +#define O_CLOEXEC 02000000 + +/* minimal stdarg */ typedef __builtin_va_list va_list; #define va_start(ap,last) __builtin_va_start(ap,last) -#define va_arg(ap,type) __builtin_va_arg(ap,type) -#define va_end(ap) __builtin_va_end(ap) +#define va_arg(ap,type) __builtin_va_arg(ap,type) +#define va_end(ap) __builtin_va_end(ap) -#define SYS_READ 0 -#define SYS_WRITE 1 -#define SYS_OPEN 2 -#define SYS_CLOSE 3 -#define SYS_LSEEK 8 +/* syscall numbers */ +#define SYS_READ 0 +#define SYS_WRITE 1 +#define SYS_OPEN 2 +#define SYS_CLOSE 3 +#define SYS_LSEEK 8 -static inline long _syscall3(long nr,long a1,long a2,long a3){long ret;asm volatile("syscall":"=a"(ret):"a"(nr),"D"(a1),"S"(a2),"d"(a3):"rcx","r11","memory");return ret;} +/* raw syscall wrapper */ +static inline long _syscall3(long nr, long a1, long a2, long a3) { + long ret; + asm volatile( + "syscall" + : "=a"(ret) + : "a"(nr), "D"(a1), "S"(a2), "d"(a3) + : "rcx","r11","memory" + ); + return ret; +} #ifndef MY_BUFSIZ #define MY_BUFSIZ 4096 #endif -typedef struct{int fd;char buf[MY_BUFSIZ];size_t pos;size_t len;int mode;}MY_FILE; +typedef struct { + int fd; + char buf[MY_BUFSIZ]; + size_t pos; + size_t len; + int mode; +} MY_FILE; -MY_FILE* my_fopen(const char* path,int flags,int mode); -int my_fclose(MY_FILE* f); -size_t my_fread(void* ptr,size_t size,size_t nmemb,MY_FILE* f); -size_t my_fwrite(const void* ptr,size_t size,size_t nmemb,MY_FILE* f); -int my_putc(int c,MY_FILE* f); -int my_puts(const char* s,MY_FILE* f); -off_t my_seek(MY_FILE* f,off_t offset,int whence); -int my_printf(MY_FILE* f,const char* fmt,...); -int my_vprintf(MY_FILE* f,const char* fmt,va_list ap); +MY_FILE *my_fopen(const char *path, int flags, int mode); +int my_fclose(MY_FILE *f); +size_t my_fread(void *ptr, size_t size, size_t nmemb, MY_FILE *f); +size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, MY_FILE *f); +int my_putc(int c, MY_FILE *f); +int my_puts(const char *s, MY_FILE *f); +off_t my_seek(MY_FILE *f, off_t offset, int whence); +int my_printf(MY_FILE *f, const char *fmt, ...); +int my_vprintf(MY_FILE *f, const char *fmt, va_list ap); + +struct linux_dirent64 { + unsigned long d_ino; + off_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[]; +}; + +struct stat_t { + unsigned long st_dev; + unsigned long st_ino; + unsigned long st_nlink; + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long st_atime_nsec; + unsigned long st_mtime; + unsigned long st_mtime_nsec; + unsigned long st_ctime; + unsigned long st_ctime_nsec; +}; #ifdef __cplusplus } #endif -#endif +#endif /* MINI_STDIO_H */ \ No newline at end of file diff --git a/src/main.c b/src/main.c index 9f81a83..6f3dbb5 100644 --- a/src/main.c +++ b/src/main.c @@ -1,114 +1,189 @@ #include "headers/nstdo.h" -extern long sys_read(int fd, void *buf, size_t cnt); -extern long sys_write(int fd, const void *buf, size_t cnt); -extern long sys_fork(void); -extern long sys_execve(const char *filename, char *const argv[], char *const envp[]); -extern long sys_wait4(int pid, int *wstatus, int options, void *rusage); +extern long sys_open(const char *pathname, int flags, int mode); +extern long sys_close(int fd); +extern long sys_getdents64(int fd, void *dirp, size_t count); +extern long sys_stat(const char *pathname, struct stat_t *statbuf); +extern long sys_write(int fd, const void *buf, size_t count); +extern long sys_read(int fd, void *buf, size_t count); extern void sys_exit(int status); static int streq(const char *a, const char *b) { while (*a && *b) { - if (*a != *b) return 0; - a++; - b++; + if (*a++ != *b++) return 0; } return *a == *b; } static long str_len(const char *s) { - long len = 0; - while (s[len]) len++; - return len + 1; // Include the null terminator in the length + long l = 0; + while (s[l]) l++; + return l; } -static void fancyMessage(void) { - const char *fM1 = "+-------------------------------------------------+\n"; - const char *fM2 = "| |\n"; - const char *fM3 = "| Welcome to the Mini Shell! |\n"; - const char *fM4 = "| |\n"; - const char *fM5 = "| Type 'help' to view all 16 commands. |\n"; - const char *fM6 = "| Type 'exit' to leave the shell. |\n"; - const char *fM7 = "| |\n"; - const char *fM8 = "| Have a productive coding session! |\n"; - const char *fM9 = "| |\n"; - const char *fM0 = "+-------------------------------------------------+\n"; - - sys_write(1, fM1, str_len(fM1)); - sys_write(1, fM2, str_len(fM2)); - sys_write(1, fM3, str_len(fM3)); - sys_write(1, fM4, str_len(fM4)); - sys_write(1, fM5, str_len(fM5)); - sys_write(1, fM6, str_len(fM6)); - sys_write(1, fM7, str_len(fM7)); - sys_write(1, fM8, str_len(fM8)); - sys_write(1, fM9, str_len(fM9)); - sys_write(1, fM0, str_len(fM0)); - sys_write(1, "\n", 1); - sys_write(1, "\n", 1); -} - -static void helpCommand(void) { - const char *helpText[] = { - "Available commands:\n\n", - "\tcd \t\tChange the current directory to \n", - "\tls [-l] [dir]\t\tList the contents of the directory [dir] (default: current directory), with an optional long format (-l)\n", - "\thistory\t\t\tDisplay the command history\n", - "\thelp\t\t\tShow this help message\n", - "\tpwd\t\t\tPrint the current working directory\n", - "\tclear\t\t\tClear the terminal screen\n", - "\ttouch \tCreate an empty file named \n", - "\tcat \t\tDisplay the contents of \n", - "\trm \t\tRemove the specified file\n", - "\tmkdir \t\tCreate a directory named \n", - "\trmdir \t\tRemove a directory named \n", - "\techo \t\tDisplay the text \n", - "\tman \t\tDisplay help for the specified command (e.g., 'man ls')\n", - "\tcp \tCopy a file from to \n", - "\tmv \tMove or rename a file from to \n", - "\thead \t\tDisplay the first 10 lines of \n", - "\tnano \t\tEdit in a simple text editor\n", - "\nFor more detailed information on a specific command, use 'man '.\n" - }; - - for (size_t i = 0; i < sizeof(helpText) / sizeof(helpText[0]); i++) { - sys_write(1, helpText[i], str_len(helpText[i])); +static int utoa(unsigned long v, char *buf) { + char tmp[32]; + int i = 0; + if (v == 0) { + buf[0] = '0'; + return 1; } + while (v) { + tmp[i++] = '0' + (v % 10); + v /= 10; + } + for (int j = 0; j < i; j++) { + buf[j] = tmp[i - 1 - j]; + } + return i; +} + +static int prefix_eq(const char *s, const char *t, long n) { + for (long i = 0; i < n; i++) { + if (s[i] != t[i]) return 0; + } + return 1; +} + +char *getenv(const char *name) { + if (!environ || !name) return NULL; + long keylen = 0; + while (name[keylen]) keylen++; + for (char **ep = environ; *ep; ep++) { + if (prefix_eq(*ep, name, keylen) && (*ep)[keylen] == '=') { + return &(*ep)[keylen + 1]; + } + } + return NULL; +} + +static void lsCommand(const char *path, int longFlag) { + if (!path || !*path) path = "."; + long fd = sys_open(path, 0, 0); + if (fd < 0) { + const char *err = "Error: cannot open directory\n"; + sys_write(1, err, str_len(err)); + return; + } + char dentbuf[8192]; + while (1) { + long nread = sys_getdents64(fd, dentbuf, sizeof(dentbuf)); + if (nread <= 0) break; + long bpos = 0; + while (bpos < nread) { + struct linux_dirent64 *d = + (struct linux_dirent64 *)(dentbuf + bpos); + char *name = d->d_name; + if (d->d_ino) { + char full[1024]; + int off = 0; + long plen = str_len(path); + for (long i = 0; i < plen; i++) full[off++] = path[i]; + if (plen && path[plen - 1] != '/') full[off++] = '/'; + for (int i = 0; name[i]; i++) full[off++] = name[i]; + full[off] = '\0'; + + struct stat_t st; + sys_stat(full, &st); + + char out[2048]; + int o = 0; + char numbuf[32]; + + if (longFlag) { + unsigned int m = st.st_mode; + out[o++] = (m & 0040000) ? 'd' : '-'; + out[o++] = (m & 0000400) ? 'r' : '-'; + out[o++] = (m & 0000200) ? 'w' : '-'; + out[o++] = (m & 0000100) ? 'x' : '-'; + out[o++] = (m & 0000040) ? 'r' : '-'; + out[o++] = (m & 0000020) ? 'w' : '-'; + out[o++] = (m & 0000010) ? 'x' : '-'; + out[o++] = (m & 0000004) ? 'r' : '-'; + out[o++] = (m & 0000002) ? 'w' : '-'; + out[o++] = (m & 0000001) ? 'x' : '-'; + out[o++] = ' '; + o += utoa(st.st_nlink, numbuf); + for (int i = 0; i < utoa(st.st_nlink, numbuf); i++) + out[o - utoa(st.st_nlink, numbuf) + i] = numbuf[i]; + out[o++] = ' '; + o += utoa(st.st_uid, numbuf); + for (int i = 0; i < utoa(st.st_uid, numbuf); i++) + out[o - utoa(st.st_uid, numbuf) + i] = numbuf[i]; + out[o++] = ' '; + o += utoa(st.st_gid, numbuf); + for (int i = 0; i < utoa(st.st_gid, numbuf); i++) + out[o - utoa(st.st_gid, numbuf) + i] = numbuf[i]; + out[o++] = ' '; + } + + o += utoa(st.st_size, numbuf); + for (int i = 0; i < utoa(st.st_size, numbuf); i++) + out[o - utoa(st.st_size, numbuf) + i] = numbuf[i]; + out[o++] = '\t'; + + for (int i = 0; name[i]; i++) out[o++] = name[i]; + out[o++] = '\n'; + + sys_write(1, out, o); + } + bpos += d->d_reclen; + } + } + sys_close(fd); } int main(int argc, char *argv[], char *envp[]) { - (void)argc; - (void)argv; - - const char prompt[] = "lala@mini-shell> "; + (void)argc; (void)argv; (void)envp; char buf[512]; - fancyMessage(); + const char *user = getenv("USER"); + if (!user) user = getenv("LOGNAME"); + if (!user) user = getenv("USERNAME"); + if (!user) user = "user"; + const char *host = "mini-shell"; + char prompt[64]; + int prompt_len = 0; + for (const char *p = user; *p; p++) prompt[prompt_len++] = *p; + prompt[prompt_len++] = '@'; + for (const char *p = host; *p; p++) prompt[prompt_len++] = *p; + prompt[prompt_len++] = '>'; + prompt[prompt_len++] = ' '; while (1) { - sys_write(1, prompt, sizeof(prompt) - 1); + sys_write(1, prompt, prompt_len); long n = sys_read(0, buf, sizeof(buf) - 1); if (n <= 0) break; - buf[n - 1] = '\0'; - if (streq(buf, "help")) { - helpCommand(); - continue; + char *p = buf; + while (*p == ' ') p++; + char *cmd = p; + while (*p && *p != ' ') p++; + if (*p) *p++ = '\0'; + + char *opt = NULL; + char *arg = NULL; + while (*p == ' ') p++; + if (*p) { + opt = p; + while (*p && *p != ' ') p++; + if (*p) *p++ = '\0'; + } + while (*p == ' ') p++; + if (*p) { + arg = p; } - if (streq(buf, "exit")) { + if (streq(cmd, "exit")) { break; - } - - char *args[] = { buf, NULL }; - long pid = sys_fork(); - - if (pid == 0) { - sys_execve(buf, args, envp); - sys_exit(1); - } else { - sys_wait4(pid, 0, 0, 0); + } else if (streq(cmd, "help")) { + const char *h = "Usage:\n ls [-l] [directory]\n exit\n"; + sys_write(1, h, str_len(h)); + } else if (streq(cmd, "ls")) { + int longFlag = (opt && streq(opt, "-l")) ? 1 : 0; + const char *path = arg ? arg : "."; + lsCommand(path, longFlag); } }