diff options
Diffstat (limited to 'src/cmd/shell/builtins.c')
-rw-r--r-- | src/cmd/shell/builtins.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/src/cmd/shell/builtins.c b/src/cmd/shell/builtins.c new file mode 100644 index 0000000..9c294b2 --- /dev/null +++ b/src/cmd/shell/builtins.c @@ -0,0 +1,254 @@ +#include "builtins.h" +#include "shell.h" +#include <camellia.h> +#include <camellia/fs/misc.h> +#include <camellia/path.h> +#include <err.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#define DEFAULT_ARGV(...) \ + char *_argv_default[] = {argv[0], __VA_ARGS__}; \ + if (argc <= 1) { \ + argc = sizeof(_argv_default) / sizeof(*_argv_default); \ + argv = _argv_default; \ + } + +static void cmd_cat(int argc, char **argv) { + const size_t buflen = 4096; + char *buf = malloc(buflen); + + DEFAULT_ARGV("!stdin"); + for (int i = 1; i < argc; i++) { + FILE *file = fopen(argv[i], "r"); + if (!file) { + perror(argv[i]); + return; + } + if (!strcmp(argv[i], "!stdin")) fextflags(file, FEXT_NOFILL); + for (;;) { + int len = fread(buf, 1, buflen, file); + if (len <= 0) break; + fwrite(buf, 1, len, stdout); + } + if (ferror(file)) { + perror(argv[i]); + return; + } + fclose(file); + } +} + +static void cmd_echo(int argc, char **argv) { + bool newline = true; + int i = 1; + + if (!strcmp("-n", argv[i])) { + i++; + newline = false; + } + + printf("%s", argv[i++]); + for (; i < argc; i++) + printf(" %s", argv[i]); + if (newline) + printf("\n"); +} + +void cmd_getsize(int argc, char **argv) { + if (argc < 2) errx(1, "no arguments"); + for (int i = 1; i < argc; i++) { + hid_t h = camellia_open(argv[i], OPEN_READ); + if (h < 0) { + warn("error opening %s", argv[i]); + continue; + } + printf("%s: %d\n", argv[i], (int)_sys_getsize(h)); + _sys_close(h); + } +} + +void cmd_hexdump(int argc, char **argv) { + DEFAULT_ARGV("!stdin"); + const size_t buflen = 4096; + uint8_t *buf = malloc(buflen); + FILE *file; + bool canonical = strcmp(argv[0], "hd") == 0; + size_t readlen = ~0; + size_t pos = 0; + bool raw = false; + + int c; + optind = 0; + while ((c = getopt(argc, argv, "Cn:s:r")) != -1) { + switch (c) { + case 'C': + canonical = true; + break; + case 'n': + readlen = strtol(optarg, NULL, 0); + break; + case 's': + pos = strtol(optarg, NULL, 0); + break; + case 'r': /* "raw" mode, print data as soon as it's read without buffering */ + raw = true; + break; + default: + return; + } + } + if (readlen != (size_t)~0) + readlen += pos; + + for (; optind < argc; optind++) { + file = fopen(argv[optind], "r"); + if (!file) { + warn("couldn't open %s", argv[optind]); + continue; + } + if (raw) fextflags(file, FEXT_NOFILL); + fseek(file, pos, SEEK_SET); + bool skipped = false; + while (pos < readlen) { + size_t len = buflen; + if (len > readlen - pos) + len = readlen - pos; + len = fread(buf, 1, len, file); + if (len == 0) break; + + for (size_t i = 0; i < len; i += 16) { + if (i >= 16 && !memcmp(buf + i, buf + i - 16, 16)) { + if (!skipped) { + printf("*\n"); + skipped = true; + } + continue; + } else skipped = false; + printf("%08zx ", pos + i); + + for (size_t j = i; j < i + 8 && j < len; j++) + printf("%02x ", buf[j]); + printf(" "); + for (size_t j = i + 8; j < i + 16 && j < len; j++) + printf("%02x ", buf[j]); + + if (canonical) { + printf(" |"); + + for (size_t j = i; j < i + 16 && j < len; j++) { + char c = '.'; + if (0x20 <= buf[j] && buf[j] < 0x7f) c = buf[j]; + printf("%c", c); + } + printf("|\n"); + } else { + printf("\n"); + } + } + pos += len; + } + printf("%08zx\n", pos); + fclose(file); + } +} + +static void cmd_ls(int argc, char **argv) { + FILE *file; + const size_t buflen = 4096; + char *buf = malloc(buflen); + + DEFAULT_ARGV("."); + for (int i = 1; i < argc; i++) { + char *path = (void*)argv[i]; + int pathlen = strlen(path); + + if (!pathlen || path[pathlen - 1] != '/') { + memcpy(buf, path, pathlen); + buf[pathlen] = '/'; + buf[pathlen+1] = '\0'; + path = buf; + } + + file = fopen(path, "r"); + if (!file) { + warn("couldn't open %s", argv[i]); + continue; + } + for (;;) { + int len = fread(buf, 1, buflen, file); + if (len <= 0) break; + for (int i = 0; i < len; i++) + if (buf[i] == '\0') buf[i] = '\n'; + fwrite(buf, 1, len, stdout); + } + fclose(file); + } +} + +static void cmd_mkdir(int argc, char **argv) { + // TODO mkdir -p + if (argc < 2) errx(1, "no arguments"); + for (int i = 1; i < argc; i++) { + if (mkdir(argv[i], 0777) < 0) + perror(argv[i]); + } +} + +static void cmd_rm(int argc, char **argv) { + if (argc < 2) errx(1, "no arguments"); + for (int i = 1; i < argc; i++) { + if (unlink(argv[i]) < 0) + perror(argv[i]); + } +} + +static void cmd_sleep(int argc, char **argv) { + if (argc < 2) errx(1, "no arguments"); + _sys_sleep(strtol(argv[1], NULL, 0) * 1000); +} + +static void cmd_touch(int argc, char **argv) { + if (argc < 2) errx(1, "no arguments"); + for (int i = 1; i < argc; i++) { + FILE *f = fopen(argv[i], "a"); + if (!f) perror(argv[i]); + fclose(f); + } +} + +static void cmd_whitelist(int argc, char **argv) { + int split = 1; + for (; split < argc;) { + if (!strcmp("--", argv[split])) break; + split++; + } + argv[split] = NULL; + MOUNT_AT("/") { fs_whitelist((void*)&argv[1]); } + + if (split < argc) { + run_args(argc - split - 1, &argv[split + 1], NULL); + } else { + const char **argv = (const char*[]){"shell", NULL}; + run_args(1, (void*)argv, NULL); + } +} + +struct builtin builtins[] = { + {"cat", cmd_cat}, + {"echo", cmd_echo}, + {"getsize", cmd_getsize}, + {"hd", cmd_hexdump}, + {"hexdump", cmd_hexdump}, + {"ls", cmd_ls}, + {"mkdir", cmd_mkdir}, + {"rm", cmd_rm}, + {"sleep", cmd_sleep}, + {"touch", cmd_touch}, + {"whitelist", cmd_whitelist}, + {NULL, NULL}, +}; |