From d8f2c10b7108fff6b7e437291093a1cadc15ab9f Mon Sep 17 00:00:00 2001 From: Tyler Murphy Date: Sat, 6 May 2023 00:39:44 -0400 Subject: refactor --- .clangd | 19 -- Makefile | 10 +- command/cat.c | 142 ++++++++ command/cp.c | 245 ++++++++++++++ command/dd.c | 63 ++++ command/echo.c | 107 ++++++ command/ed.c | 920 ++++++++++++++++++++++++++++++++++++++++++++++++++ command/grep.c | 269 +++++++++++++++ command/groups.c | 63 ++++ command/head.c | 141 ++++++++ command/id.c | 76 +++++ command/ls.c | 563 ++++++++++++++++++++++++++++++ command/mkdir.c | 70 ++++ command/mv.c | 121 +++++++ command/printf.c | 144 ++++++++ command/rm.c | 140 ++++++++ command/tac.c | 119 +++++++ command/tail.c | 243 +++++++++++++ command/tee.c | 83 +++++ command/wc.c | 161 +++++++++ command/whoami.c | 30 ++ command/xargs.c | 192 +++++++++++ command/yes.c | 27 ++ compile_flags.txt | 15 + lib/args.c | 111 ++++++ lib/args.h | 18 + lib/buffer.c | 60 ++++ lib/buffer.h | 16 + lib/convert.c | 126 +++++++ lib/convert.h | 20 ++ lib/def.h | 31 ++ lib/error.c | 35 ++ lib/error.h | 13 + lib/file.c | 63 ++++ lib/file.h | 14 + lib/lslib.h | 16 + lib/regex.c | 528 +++++++++++++++++++++++++++++ lib/regex.h | 54 +++ lib/stack.c | 43 +++ lib/stack.h | 22 ++ src/command.h | 19 +- src/commands/cat.c | 140 -------- src/commands/cp.c | 240 ------------- src/commands/dd.c | 60 ---- src/commands/echo.c | 104 ------ src/commands/ed.c | 917 ------------------------------------------------- src/commands/grep.c | 265 --------------- src/commands/groups.c | 42 --- src/commands/head.c | 138 -------- src/commands/id.c | 53 --- src/commands/ls.c | 552 ------------------------------ src/commands/mkdir.c | 65 ---- src/commands/mv.c | 116 ------- src/commands/printf.c | 141 -------- src/commands/rm.c | 136 -------- src/commands/tac.c | 117 ------- src/commands/tail.c | 238 ------------- src/commands/tee.c | 81 ----- src/commands/wc.c | 159 --------- src/commands/whoami.c | 27 -- src/commands/xargs.c | 187 ---------- src/commands/yes.c | 26 -- src/main.c | 10 +- src/util/regex.c | 528 ----------------------------- src/util/regex.h | 65 ---- src/util/shared.c | 372 -------------------- src/util/shared.h | 86 ----- src/util/stack.c | 43 --- src/util/stack.h | 22 -- 69 files changed, 5115 insertions(+), 4967 deletions(-) delete mode 100644 .clangd create mode 100644 command/cat.c create mode 100644 command/cp.c create mode 100644 command/dd.c create mode 100644 command/echo.c create mode 100644 command/ed.c create mode 100644 command/grep.c create mode 100644 command/groups.c create mode 100644 command/head.c create mode 100644 command/id.c create mode 100644 command/ls.c create mode 100644 command/mkdir.c create mode 100644 command/mv.c create mode 100644 command/printf.c create mode 100644 command/rm.c create mode 100644 command/tac.c create mode 100644 command/tail.c create mode 100644 command/tee.c create mode 100644 command/wc.c create mode 100644 command/whoami.c create mode 100644 command/xargs.c create mode 100644 command/yes.c create mode 100644 compile_flags.txt create mode 100644 lib/args.c create mode 100644 lib/args.h create mode 100644 lib/buffer.c create mode 100644 lib/buffer.h create mode 100644 lib/convert.c create mode 100644 lib/convert.h create mode 100644 lib/def.h create mode 100644 lib/error.c create mode 100644 lib/error.h create mode 100644 lib/file.c create mode 100644 lib/file.h create mode 100644 lib/lslib.h create mode 100644 lib/regex.c create mode 100644 lib/regex.h create mode 100644 lib/stack.c create mode 100644 lib/stack.h delete mode 100644 src/commands/cat.c delete mode 100644 src/commands/cp.c delete mode 100644 src/commands/dd.c delete mode 100644 src/commands/echo.c delete mode 100644 src/commands/ed.c delete mode 100644 src/commands/grep.c delete mode 100644 src/commands/groups.c delete mode 100644 src/commands/head.c delete mode 100644 src/commands/id.c delete mode 100644 src/commands/ls.c delete mode 100644 src/commands/mkdir.c delete mode 100644 src/commands/mv.c delete mode 100644 src/commands/printf.c delete mode 100644 src/commands/rm.c delete mode 100644 src/commands/tac.c delete mode 100644 src/commands/tail.c delete mode 100644 src/commands/tee.c delete mode 100644 src/commands/wc.c delete mode 100644 src/commands/whoami.c delete mode 100644 src/commands/xargs.c delete mode 100644 src/commands/yes.c delete mode 100644 src/util/regex.c delete mode 100644 src/util/regex.h delete mode 100644 src/util/shared.c delete mode 100644 src/util/shared.h delete mode 100644 src/util/stack.c delete mode 100644 src/util/stack.h diff --git a/.clangd b/.clangd deleted file mode 100644 index ef142b4..0000000 --- a/.clangd +++ /dev/null @@ -1,19 +0,0 @@ -If: - PathMatch: - - .*\.c - - .*\.h - - -CompileFlags: - Add: - - -xc - - -std=c89 - - -Isrc - - -Wall - - -Wextra, - - -pedantic - - -O2 - - -ferror-limit=0 - - -D_DEFAULT_SOURCE - - -D_CHECK_LINK - Compiler: clang diff --git a/Makefile b/Makefile index d68e96a..07ffbf4 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,14 @@ CC = gcc -SOURCE = src +SOURCE = src +SOURCE += lib +SOURCE += command MAJOR = 0 MINOR = 0 PATCH = 1 -INCFLAGS = $(shell echo $(SOURCE) | xargs printf -- '-I%s') +INCFLAGS = $(shell echo $(SOURCE) | xargs printf -- '-I%s ') CCFLAGS = -std=c89 -Wall -Wextra -pedantic -O2 CCFLAGS += -D_DEFAULT_SOURCE -DMAJOR=$(MAJOR) -DMINOR=$(MINOR) -DPATCH=$(PATCH) -DCHECK_LINK @@ -21,9 +23,9 @@ SRC = $(shell find $(SOURCE) -name "*.c") DIR = $(shell find $(SOURCE) -type d) OBJ = $(SRC:%.c=$(BIN)/%.o) -.PHONY: dirs run clean build install uninstall +.PHONY: empty dirs run clean build install uninstall -EOF: clean build +empty: clean build dirs: echo $(DIR) | xargs printf -- '$(BIN)/%s\n' | xargs mkdir -p diff --git a/command/cat.c b/command/cat.c new file mode 100644 index 0000000..c392a46 --- /dev/null +++ b/command/cat.c @@ -0,0 +1,142 @@ +#include "command.h" +#include "lslib.h" + +#include +#include + +static struct { + bool number_lines; + bool number_non_empty; + bool change_non_print; + bool change_tabs; + bool end_lines_dollar; +} flags; + +static bool printable(char c) { + switch (c) { + case '\n': return true; + case '\r': return true; + case '\b': return true; + case '\t': return true; + default: return isprint(c) == 0; + } +} + +static void help(void) { + printf("Usage: cat [-nbvteA] [FILE]...\n\n"); + printf("Print FILEs to stdout\n\n"); + printf("\t-n Number output lines\n"); + printf("\t-b Number nonempty lines\n"); + printf("\t-v Show nonprinting characters as ^x or M-x\n"); + printf("\t-t ...and tabs as ^I\n"); + printf("\t-e ...and end lines with $\n"); + printf("\t-A Same as -vte\n"); +} + +static void cat_file(FILE* file) { + char c; + size_t read; + + size_t line = 1; + bool empty = true; + bool newline = true; + + while ((read = fread(&c, 1, 1, file)) != 0) { + if (c == '\n') { + if (empty && flags.number_lines) { + printf("\t%ld ", line); + } + if (flags.end_lines_dollar) { + printf("$"); + } + line++; + newline = true; + empty = true; + goto print; + } else { + empty = false; + } + + if (!newline) { + goto print; + } + + if (!empty && (flags.number_non_empty || flags.number_lines)) { + printf("\t%ld ", line); + newline = false; + } + print: + if (!flags.change_non_print || printable(c)) { + if (flags.change_tabs && c == '\t') { + fwrite("^I", 1, 2, stdout); + } else { + fwrite(&c, 1, 1, stdout); + } + } else { + c |= '@'; + fwrite(&c, 1, 1, stdout); + } + } +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'n': + flags.number_lines = true; + break; + case 'b': + flags.number_non_empty = true; + break; + case 'v': + flags.change_non_print = true; + break; + case 't': + flags.change_non_print = true; + flags.change_tabs = true; + break; + case 'e': + flags.change_non_print = true; + flags.end_lines_dollar = true; + break; + case 'A': + flags.change_non_print = true; + flags.change_tabs = true; + flags.end_lines_dollar = true; + break; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +COMMAND(cat) { + + int start; + int arg_len; + int i; + + flags.number_lines = false; + flags.number_non_empty = false; + flags.change_non_print = false; + flags.change_tabs = false; + flags.end_lines_dollar = false; + + start = parse_args(argc, argv, help, short_arg, NULL); + + arg_len = argc - start; + + if (arg_len < 1) { + cat_file(stdin); + return EXIT_SUCCESS; + } + + for (i = start; i < argc; i++) { + FILE* in = get_file(argv[i], "r"); + cat_file(in); + if (in != stdin) + fclose(in); + } + + return EXIT_SUCCESS; +} diff --git a/command/cp.c b/command/cp.c new file mode 100644 index 0000000..ca80f69 --- /dev/null +++ b/command/cp.c @@ -0,0 +1,245 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct { + bool recurse; + bool preserve; + bool sym_link; + bool hard_link; + bool verbose; +} flags; + +static void help(void) { + printf("Usage: cp [-rplsv] SOURCE... DEST\n\n"); + printf("Copy SOURCEs to DEST\n\n"); + printf("\t-R,-r\tRecurse\n"); + printf("\t-p\tPreserve file attributes if possible\n"); + printf("\t-l,-s\tCreate (sym)links\n"); + printf("\t-v\tVerbose\n"); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'R': + case 'r': + flags.recurse = true; + break; + case 'p': + flags.preserve = true; + break; + case 'l': + flags.hard_link = true; + flags.sym_link = false; + break; + case 's': + flags.sym_link = true; + flags.hard_link = false; + break; + case 'v': + flags.verbose = true; + break; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +static bool copy_file(char* from, char* to) { + #define BS 1024 + + FILE *from_f, *to_f; + char buf[BS]; + int read; + + from_f = get_file_s(from, "r"); + if (from_f == NULL) { return false; } + + to_f = get_file_s(to, "w"); + if (to_f == NULL) { fclose(from_f); return false; } + + + while ((read = fread(buf, 1, BS, from_f)) > 0) { + fwrite(buf, 1, read, to_f); + } + + if (flags.verbose) { + output("copied '%s'", from); + } + + return true; +} + +static bool symlink_file(char* from, char* to) { + if (symlink(from, to) < 0) { + error_s("failed to symlink '%s': %s", from, strerror(errno)); + return false; + } else if (flags.verbose) { + output("symlinked '%s'", from); + } + return true; +} + +static bool hardlink_file(char* from, char* to) { + if (link(from, to) < 0) { + error_s("failed to hardlink '%s': %s", from, strerror(errno)); + return false; + } else if (flags.verbose) { + output("hardlinked '%s'", from); + } + return true; +} + +static void run_copy(struct stat* s) { + + char* from = get_path_buffer(); + char* to = get_path_buffer_2(); + + bool result; + if (flags.sym_link) { + result = symlink_file(from, to); + } else if (flags.hard_link) { + result = hardlink_file(from, to); + } else { + result = copy_file(from, to); + } + + if (!result) return; + if (!flags.preserve) return; + + if (chmod(to, s->st_mode) < 0) { + error_s("cannot chmod '%s': %s", to, strerror(errno)); + return; + } + + if (chown(to, s->st_uid, s->st_gid) < 0) { + error_s("cannot chown '%s': %s", to, strerror(errno)); + return; + } +} + +static void cp_file(char* path); + +static void cp_directory(struct stat* s) { + + DIR* d; + struct dirent* file; + + if (!flags.recurse) { + error_s("-r not specified; omitting directory '%s'", get_path_buffer()); + return; + } + + if (mkdir(get_path_buffer_2(), s->st_mode) < 0 && errno != EEXIST) { + error_s("cannot create directory '%s': %s", get_path_buffer_2(), strerror(errno)); + return; + } + + d = opendir(get_path_buffer()); + + if (d == NULL) { + error_s("cannot open directory '%s': %s", get_path_buffer(), strerror(errno)); + return; + } + + while ((file = readdir(d)) != NULL) { + if (is_dot_dir(file->d_name)) continue; + cp_file(file->d_name); + } +} + +static char* get_file_name(char* path) { + int last = 0; + int i = 0; + + if (path[0] == '\0') return path; + + while (true) { + if (path[i+1] == '\0') break; + if (path[i] == '/') { + last = i; + } + i++; + } + + if (last == 0) return path; + else return path + last + 1; +} + +static void cp_file(char* path) { + + int save = push_path_buffer(path); + int save2 = push_path_buffer_2(get_file_name(path)); + + struct stat s; + if (lstat(get_path_buffer(), &s) < 0) { + pop_path_buffer(save); + error_s("cannot stat '%s': %s", get_path_buffer(), strerror(errno)); + return; + } + + if (S_ISDIR(s.st_mode)) { + cp_directory(&s); + } else { + run_copy(&s); + } + + pop_path_buffer(save); + pop_path_buffer_2(save2); +} + +COMMAND(cp) { + + int start, i; + struct stat s; + + flags.hard_link = false; + flags.sym_link = false; + flags.preserve = false; + flags.recurse = false; + flags.verbose = false; + + start = parse_args(argc, argv, help, short_arg, NULL); + + if (argc - start < 2) { + global_help(help); + } + + /* only when 2 args and first is a file, the 2nd will be a file */ + if (argc - start == 2) { + struct stat s; + if (lstat(argv[start], &s) < 0) { + error("cannot stat '%s': %s", argv[start], strerror(errno)); + } + push_path_buffer(argv[argc-2]); + push_path_buffer_2(argv[argc-1]); + if (!S_ISDIR(s.st_mode)) { + run_copy(&s); + } else { + cp_directory(&s); + } + return EXIT_SUCCESS; + } + + /* push directory */ + push_path_buffer_2(argv[argc-1]); + + if (lstat(get_path_buffer_2(), &s) < 0) { + error("target: '%s': %s", get_path_buffer_2(), strerror(errno)); + } + + for (i = start; i < argc - 1; i++) { + cp_file(argv[i]); + } + + return EXIT_SUCCESS; +} diff --git a/command/dd.c b/command/dd.c new file mode 100644 index 0000000..67f8be3 --- /dev/null +++ b/command/dd.c @@ -0,0 +1,63 @@ +#include "command.h" +#include "lslib.h" + +#include + +static void help(void) { + printf("Usage: dd [if=FILE] [of=FILE] [bs=N] [count=N]\n\n"); + printf("Copy a file with converting and formatting\n\n"); + printf("\tif=FILE\t\tRead from FILE instead of stdin\n"); + printf("\tof=FILE\t\tWrite to FILE instead of stdout\n"); + printf("\tbs=N\t\tRead and write N bytes at a time\n"); + printf("\tcount=N\t\tCopy only N input blocks\n"); +} + +COMMAND(dd) { + + FILE* in_file = stdin; + FILE* out_file = stdout; + int bs = 1024; + int count = -1; + int i; + char* buffer; + size_t read; + + parse_help(argc, argv, help); + + for (i = 0; i < argc; i++) { + if (prefix("if=", argv[i])) { + char* path = argv[i] + 3; + in_file = get_file(path, "rb"); + } else if (prefix("of=", argv[i])) { + char* path = argv[i] + 3; + out_file = get_file(path, "wb"); + } else if (prefix("bs=", argv[i])) { + char* str = argv[i] + 3; + bs = get_number(str); + if (bs < 1) { + error("block size must be greater than 0"); + } + } else if (prefix("count=", argv[i])) { + char* str = argv[i] + 6; + count = get_number(str); + if (count < 1) { + error("count must be greather than 0"); + } + } else { + error("unkown option %s", argv[i]); + } + } + + buffer = malloc(bs); + + while ((read = fread(buffer, 1, bs, in_file)) != 0) { + fwrite(buffer, 1, read, out_file); + if (--count, count == 0) break; + } + + free(buffer); + fclose(in_file); + fclose(out_file); + + return EXIT_SUCCESS; +} diff --git a/command/echo.c b/command/echo.c new file mode 100644 index 0000000..a0ab78d --- /dev/null +++ b/command/echo.c @@ -0,0 +1,107 @@ +#include "command.h" +#include "lslib.h" + +#include + +static struct { + bool escape_codes; + bool newline; +} flags; + +static void print_with_escape_codes(const char* str) { + + size_t index = 0; + char n; + + while (true) { + char c = str[index]; + index++; + + if (c == '\0') break; + if (c != '\\') { + putchar(c); + continue; + } + + n = str[index]; + index++; + + switch (n) { + case '\\': + putchar('\\'); + break; + case 'b': + putchar('\b'); + break; + case 'c': + exit(EXIT_SUCCESS); + case 'n': + putchar('\n'); + break; + case 'r': + putchar('\r'); + break; + case 't': + putchar('\t'); + break; + case 'v': + putchar('\v'); + break; + default: + putchar(c); + putchar(n); + } + } +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'e': + flags.escape_codes = true; + break; + case 'E': + flags.escape_codes = false; + break; + case 'n': + flags.newline = false; + break; + default: + flags.newline = true; + flags.escape_codes = false; + return ARG_IGNORE; + }; + return ARG_UNUSED; +} + +COMMAND(echo) { + + int start, i; + + if (argc < 1) { + return EXIT_SUCCESS; + } + + flags.escape_codes = false; + flags.newline = true; + + start = parse_args(argc, argv, NULL, short_arg, NULL); + + for (i = start; i < argc; i++) { + if (flags.escape_codes) { + print_with_escape_codes(argv[i]); + } else { + printf("%s", argv[i]); + } + + if (i + 1 != argc) { + putchar(' '); + } + } + + if (flags.newline) { + putchar('\n'); + } + + return EXIT_SUCCESS; +} diff --git a/command/ed.c b/command/ed.c new file mode 100644 index 0000000..472473f --- /dev/null +++ b/command/ed.c @@ -0,0 +1,920 @@ +#include "command.h" +#include "lslib.h" + +#include +#include + +#define INPUT_LEN 1024 + +static char** lines = NULL; +static unsigned long line_capacity; +static unsigned long line_count; +static unsigned long line_current; +static bool pending_writes; +static char* default_filename = NULL; +static re_t last_regex = NULL; + +enum LineAddressType { + INDEX, + RANGE, + SET, + FREE +}; + +struct LineAddress { + enum LineAddressType type; + union { + struct { + long int i; + } index; + struct { + long int a; + long int b; + } range; + struct { + long int* b; + unsigned long c; + unsigned long s; + } set; + } data; + bool empty; +}; + +enum RegexDirection { + BEFORE, + AFTER, + ALL +}; + +static bool read_regex(char** end, re_t* regex, enum RegexDirection dir) { + char c; + char* index = *end; + char* regex_str = index; + while(true) { + c = *(index++); + if (c == '\0') { + error_s("missing regex after %c\n", dir == BEFORE ? '?' : '/'); + return false; + } + if (c == (dir == BEFORE ? '?' : '/')) { + *(index - 1) = '\0'; + break; + } + } + *regex = re_compile(regex_str); + last_regex = *regex; + *end = index; + return true; +} + +static bool parse_regex(char** end, struct LineAddress* address, enum RegexDirection dir) { + re_t regex; + unsigned long cap, siz, i, until; + long int* buf; + + if (!read_regex(end, ®ex, dir)) return false; + + cap = 8; + siz = 0; + buf = malloc(cap * sizeof(long int)); + + i = (dir == ALL ? 0 : line_current); + until = (dir == BEFORE ? 0 : line_count - 1); + for (; (dir == BEFORE ? i >= until : i < until); dir == BEFORE ? i-- : i++) { + int len; + if (re_matchp(regex, lines[i], &len) == -1) { + if (dir == BEFORE && i == 0) break; + continue; + } + if (cap == siz) { + cap *= 2; + buf = realloc(buf, cap * sizeof(long int)); + } + buf[siz] = i; + siz++; + if (dir == BEFORE && i == 0) break; + } + + address->empty = false; + address->type = SET; + address->data.set.s = siz; + address->data.set.c = cap; + address->data.set.b = buf; + return true; +} + +static void free_address (struct LineAddress address) { + if (address.type != SET) return; + address.type = FREE; + free(address.data.set.b); +} + +static void expand_buffer(long int** buf, unsigned long* cap, unsigned long* size) { + if (*cap == *size) { + *cap *= 2; + *buf = realloc(*buf, sizeof(long int) * *cap); + } +} + +static bool parse_regex_lines(char** end, struct LineAddress* address) { + re_t regex; + unsigned long cap, siz; + long int* buf; + int len; + struct LineAddress addr; + + if (!read_regex(end, ®ex, ALL)) return false; + + cap = 8; + siz = 0; + buf = malloc(cap * sizeof(long int)); + + addr = *address; + if (addr.type == INDEX) { + if (re_matchp(regex, lines[addr.data.index.i], &len) != -1) { + buf[0] = addr.data.index.i; + siz = 1; + } + } else if (addr.type == RANGE) { + long int i; + for (i = addr.data.range.a; i < addr.data.range.b; i++) { + if (re_matchp(regex, lines[i], &len) == -1) continue; + expand_buffer(&buf, &cap, &siz); + buf[siz] = i; + siz++; + } + } else if (addr.type == SET) { + unsigned long i; + for (i = 0; i < addr.data.set.s; i++) { + if (re_matchp(regex, lines[addr.data.set.b[i]], &len) == -1) continue; + expand_buffer(&buf, &cap, &siz); + buf[siz] = addr.data.set.b[i]; + siz++; + } + } + + free_address(*address); + address->empty = false; + address->type = SET; + address->data.set.s = siz; + address->data.set.c = cap; + address->data.set.b = buf; + return true; +} + +static bool read_address(char** command, bool whitespace, struct LineAddress* a) { + char* index = *command; + struct LineAddress address; + + char* end_pre; + long int n_pre; + char pre; + + memset(&address, 0, sizeof(struct LineAddress)); + + address.empty = false; + if (strlen(*command) < 1) { + address.type = INDEX; + address.data.index.i = line_current + 1; + if (line_current >= line_count) line_current = line_count - 1; + *a = address; + return true; + } + + n_pre = strtol(index, &end_pre, 10) - 1; + if (end_pre == index) { + n_pre = -1; + } else { + if (n_pre < 0) { + error_s("input cannot be negative\n"); + return false; + } + index = end_pre; + } + + pre = *(index++); + switch (pre) { + case '.': + address.type = INDEX; + address.data.index.i = line_current; + break; + case '$': + address.type = INDEX; + address.data.index.i = line_count - 1; + break; + case '-': + case '^': { + char* end; + long int n; + + address.type = INDEX; + n = strtol(index, &end, 10) - 1; + + if (n < 0) { + error_s("input cannot be negative\n"); + return false; + } + + if (index == end) { + address.data.index.i = line_current - 1; + } else { + address.data.index.i = line_current - n; + } + + if (address.data.index.i < 0) { + error_s("line number %ld does not exist\n", address.data.index.i + 1); + return false; + } + + break; + } + case '+': { + char* end; + long int n; + + address.type = INDEX; + n = strtol(index, &end, 10) - 1; + + if (n < 0) { + error_s("input cannot be negative\n"); + return false; + } + if (index == end) { + address.data.index.i = line_current + 1; + } else { + address.data.index.i = line_current + n; + } + if (address.data.index.i >= (long int) line_count) { + error_s("line number %ld does not exist\n", address.data.index.i + 1); + return false; + } + break; + } + case '%': + address.type = RANGE; + address.data.range.a = 0; + address.data.range.b = line_count - 1; + break; + case ';': + address.type = RANGE; + address.data.range.a = line_current; + address.data.range.b = line_count - 1; + break; + case '/': + if (!parse_regex(&index, &address, AFTER)) return false; + break; + case '?': + if (!parse_regex(&index, &address, BEFORE)) return false; + break; + default: { + index--; + if (n_pre == -1) { + address.type = INDEX; + address.data.index.i = line_current; + address.empty = true; + break; + } else if (whitespace) { + address.type = INDEX; + address.data.index.i = line_current + n_pre; + } else { + address.type = INDEX; + address.data.index.i = n_pre; + } + if (address.data.index.i < 0 || address.data.index.i >= (long int) line_count) { + error_s("line number %ld does not exist\n", address.data.index.i + 1); + return false; + } + } + } + *command = index; + *a = address; + return true; +} + +static void free_data(bool all) { + if (lines != NULL) { + unsigned long i; + for (i = 0; i < line_count; i++) { + free(lines[i]); + } + free(lines); + lines = NULL; + } + if (all && default_filename != NULL) { + free(default_filename); + default_filename = NULL; + } +} + +static void load_empty(void) { + free_data(false); + + line_capacity = 8; + lines = malloc(sizeof(char*) * line_capacity); + + line_count = 0; + line_current = 0; + pending_writes = false; +} + +static void get_input(FILE* file, char*** buffer, unsigned long* capacity, unsigned long* size) { + unsigned long cap = 8; + unsigned long siz = 0; + char** buf = malloc(sizeof(char*) * cap); + + char* line = NULL; + size_t offset = 0; + + clearerr(stdin); + while (getline(&line, &offset, file) != -1) { + if (cap == siz) { + cap *= 2; + buf = realloc(buf, sizeof(char*) * cap); + } + buf[siz] = line; + siz++; + line = NULL; + } + + free(line); + + *buffer = buf; + *capacity = cap; + *size = siz; +} + +int ed_getline(char *buf, size_t size) { + size_t i = 0; + int ch; + clearerr(stdin); + while ((ch = getchar()) != EOF) { /* Read until EOF ... */ + if (i + 1 < size) { + buf[i++] = ch; + } + if (ch == '\n') { /* ... or end of line */ + break; + } + } + buf[i] = '\0'; + if (i == 0) { + return EOF; + } + return i; +} + +static void load_file(FILE* file) { + free_data(false); + line_current = 0; + get_input(file, &lines, &line_capacity, &line_count); + if (file != stdin) + fclose(file); + +} + +static bool check_if_sure(char* prompt) { + char buf[INPUT_LEN]; + + if (!pending_writes) { + return true; + } + + printf("%s", prompt); + fflush(stdout); + + if (ed_getline(buf, INPUT_LEN) == EOF) { + putchar('\n'); + return false; + } + + return prefix("y", buf); +} + +static bool skip_whitespace(char** index) { + char c; + bool w = false; + while (c = **index, c == ' ' || c == '\t') { (*index)++; w = true; } + return w; +} + +static void expand(unsigned long count) { + if (count < line_capacity) return; + line_capacity *= 2; + if (count > line_capacity) line_capacity = count; + lines = realloc(lines, line_capacity * sizeof(char*)); +} + +static void append_lines(unsigned long index, char** new, unsigned long new_len) { + if (new_len < 1) return; + pending_writes = true; + expand(line_count + new_len); + if (index + 1 <= line_count) + memmove(&lines[index+new_len], &lines[index], sizeof(char*) * (line_count - index)); + memcpy(&lines[index], new, sizeof(char*) * new_len); + line_count += new_len; +} + +static void delete_lines(unsigned long a, unsigned long b) { + unsigned long i; + + if (b < a) return; + pending_writes = true; + + for (i = a; i <= b; i++) { + free(lines[i]); + } + if (b == line_count - 1) { + line_count = a; + return; + } + memmove(&lines[a], &lines[b+1], sizeof(char*) * (line_count - (b + 1))); + line_count -= (b - a) + 1; + line_current = a; + if (line_current >= line_count) line_current = line_count - 1; +} + +static bool handle_append(struct LineAddress* address) { + char** buf; + unsigned long cap, size; + + if (address->type != INDEX) { + error_s("append command requires index addressing\n"); + return false; + } + + if (line_count == 0) { + address->data.index.i = -1; + } + + get_input(stdin, &buf, &cap, &size); + + if (size > 0) { + append_lines(address->data.index.i + 1, buf, size); + printf("ed: appened %lu lines\n", size); + } + + line_current += size; + free(buf); + return true; +} + +static bool handle_delete(struct LineAddress* address) { + if (address->empty && address->data.index.i >= (long int) line_count) { + error_s("line number %ld does not exist\n", address->data.index.i + 1); + return false; + } + + if (address->type == INDEX) { + delete_lines(address->data.index.i, address->data.index.i); + output("deleted line %ld\n", address->data.index.i+1); + } else if (address->type == RANGE) { + delete_lines(address->data.range.a, address->data.range.b); + output("deleted lines %ld-%ld\n", address->data.range.a+1, address->data.range.b+1); + } else if (address->type == SET) { + unsigned long i; + for (i = 0; i < address->data.set.s; i++) { + delete_lines(address->data.set.b[i], address->data.set.b[i]); + } + output("deleted %lu lines\n", address->data.set.s); + } + + return true; +} + +static bool get_file_name(char** filename) { + size_t len = strlen(*filename); + + if (len < 1 || (len == 1 && **filename == '\n')) { + if (default_filename == NULL) { + error_s("no default filename specified\n"); + return false; + } + *filename = default_filename; + } else { + if ((*filename)[len - 1] == '\n') { + (*filename)[len - 1] = '\0'; + len--; + } + if (default_filename != NULL) { + default_filename = realloc(default_filename, len + 1); + } else { + default_filename = malloc(len + 1); + } + memcpy(default_filename, *filename, len + 1); + *filename = default_filename; + } + return true; +} + +static void write_file(char* filename, struct LineAddress* address, char* type) { + FILE* file; + int wrote; + + if (line_count < 1) { + error_s("cannot write empty file\n"); + return; + } + + if (!get_file_name(&filename)) return; + file = get_file_s(filename, type); + if (file == NULL) return; + + wrote = 0; + + if (address->empty) { + unsigned long i; + for (i = 0; i < line_count; i++) { + fprintf(file, "%s", lines[i]); + wrote++; + } + } else if (address->type == INDEX) { + long int i = address->data.index.i; + fprintf(file, "%s", lines[i]); + wrote++; + } else if (address->type == RANGE) { + long int i; + for (i = address->data.range.a; i < address->data.range.b; i++) { + fprintf(file, "%s", lines[i]); + wrote++; + } + } else if (address->type == SET) { + unsigned long i; + for (i = 0; i < address->data.set.s; i++) { + fprintf(file, "%s", lines[address->data.set.b[i]]); + wrote++; + } + } + + pending_writes = false; + fclose(file); + output("wrote %d lines from %s\n", wrote, filename); +} + +static void read_file(char* filename) { + FILE* file; + char** buf; + long int line; + unsigned long capacity, size; + + if (!get_file_name(&filename)) return; + file = get_file_s(filename, "r"); + if (file == NULL) return; + + capacity = 8; + size = 0; + buf = malloc(capacity * sizeof(char*)); + get_input(file, &buf, &capacity, &size); + + if (size < 1) { + free(buf); + error_s("attempted to read a empty file\n"); + return; + } + + line = -1; + if (line_count > 0) { + line = line_count - 1; + } + + append_lines(line, buf, size); + free(buf); + output("read and appended %lu lines from %s\n", size, filename); +} + +static void expand_string(char** buf, int* capacity, int* size, char* text, int len) { + if (*size + len >= *capacity) { + *capacity *= 2; + if (*capacity < *size + len) *capacity = *size + len; + *buf = realloc(*buf, *capacity); + } + memcpy(*buf + *size, text, len); + *size += len; +} + +static int substute_string(long int index, long int matches, re_t regex, char* sub, int sub_len) { + int capacity = 8; + int size = 0; + char* buf = malloc(sizeof(char) * capacity); + long int left; + + int offset = 0; + int matches_found = 0; + while(true) { + int distance, len; + + if (lines[index][offset] == '\0') break; + + if (matches_found >= matches && matches > 0) break; + + if ((distance = re_matchp(regex, &lines[index][offset], &len)) == -1) { + break; + } + + if (distance > 0) { + expand_string(&buf, &capacity, &size, &lines[index][offset], distance); + } + + expand_string(&buf, &capacity, &size, sub, sub_len); + offset += len + distance; + matches_found++; + } + + left = strlen(lines[index] + offset); + expand_string(&buf, &capacity, &size, &lines[index][offset], left + 1); + + free(lines[index]); + lines[index] = buf; + return matches_found; +} + +static void prompt(void) { + char buf[INPUT_LEN]; + char* index; + char cmd; + bool whitespace, linenumbers; + struct LineAddress address; + + printf(": "); + fflush(stdout); + + if (ed_getline(buf, INPUT_LEN) == EOF) { putchar('\n'); return; } + if (buf[0] == '\0') { putchar('\n'); return; } + + index = &buf[0]; + whitespace = skip_whitespace(&index); + + if (!read_address(&index, whitespace, &address)) return; + + cmd = *(index++); + + if (cmd == ',') { + struct LineAddress address2; + + if (address.type != INDEX) { + error_s("comma range addressing requires two index addresses\n"); + free_address(address); + return; + } + + whitespace = skip_whitespace(&index); + + if (!read_address(&index, whitespace, &address2)) { + free_address(address); + return; + } + + if (address2.type != INDEX) { + error_s("comma range addressing requires two index addresses\n"); + free_address(address); + free_address(address2); + return; + } + + address.type = RANGE; + address.data.range.a = address.data.index.i; /* cursed */ + address.data.range.b = address2.data.index.i; + + cmd = *(index++); + } + + if (address.type == RANGE && address.data.range.a > address.data.range.b) { + error_s("range addressing must be in ascending order\n"); + free_address(address); + return; + } + + linenumbers = false; + +test: + switch (cmd) { + case '\0': + case '\n': + if (address.empty) { + if (line_current == line_count) { + error_s("line number %ld does not exist\n", line_current + 1); + break; + } else if (line_current + 1 == line_count) { + error_s("line number %ld does not exist\n", line_current + 2); + break; + } else { + line_current++; + } + printf("%s", lines[line_current]); + break; + } + if (address.type == INDEX) { + line_current = address.data.index.i; + } else if (address.type == RANGE) { + line_current = address.data.range.b; + } else if (address.type == SET) { + error_s("unexpected range addressing\n"); + break; + } + printf("%s", lines[line_current]); + break; + case 'a': + handle_append(&address); + break; + case 'd': + handle_delete(&address); + break; + case 'c': + if (!handle_delete(&address)) { break; } + address.type = INDEX; + address.data.index.i = line_current - 1; + handle_append(&address); + break; + case 'n': + linenumbers = true; + __attribute__((fallthrough)); + case 'p': + if (address.empty && address.data.index.i >= (long int) line_count) { + error_s("line number %ld does not exist\n", address.data.index.i + 1); + break; + } + if (address.type == INDEX) { + if (linenumbers) printf("%ld\t", address.data.index.i + 1); + printf("%s", lines[address.data.index.i]); + } else if (address.type == RANGE) { + long int i; + for (i = address.data.range.a; i <= address.data.range.b; i++) { + if (linenumbers) printf("%ld\t", i + 1); + printf("%s", lines[i]); + } + } else if (address.type == SET) { + unsigned long i; + for (i = 0; i < address.data.set.s; i++) { + if (linenumbers) printf("%ld\t", address.data.set.b[i] +1); + printf("%s", lines[address.data.set.b[i]]); + } + } + break; + case 'q': + if (!check_if_sure("Quit for sure? ")) break; + __attribute__((fallthrough)); + case 'Q': + free_address(address); + free_data(true); + exit(EXIT_SUCCESS); + break; + case 'g': + skip_whitespace(&index); + free_address(address); + if (*(index++) != '/') { + error_s("unexpected character at start of regex\n"); + break; + } + if (!parse_regex(&index, &address, ALL)) { return; } + skip_whitespace(&index); + if (*index == '\n' || *index == '\0') { + cmd = 'p'; + } else { + cmd = *(index++); + } + goto test; + break; + case 's': { + char* replace = index; + long int matches, sub_len, matches_found; + unsigned long i; + + skip_whitespace(&index); + if (*(index++) != '/') { + error_s("unexpected character at start of regex\n"); + break; + } + if (!parse_regex_lines(&index, &address)) { return; } + while(*index != '\0' && *index != '/') index++; + if (*index != '/') { + error_s("/ missing after %c\n", *index); + break; + } + if (address.data.set.s < 1) { + error_s("no matches found\n"); + break; + } + *(index++) = '\0'; + if (*index == '\0' || *index == '\n') { + matches = 1; + } else if (*index == 'g') { + matches = -1; + } else { + char* end; + matches = strtol(index, &end, 10); + if (end == index) { + error_s("invalid number: %s\n", index); + break; + } + if (matches < 1) { + error_s("matches cannot be less than 1\n"); + break; + } + } + sub_len = strlen(replace); + matches_found = 0; + + for (i = 0; i < address.data.set.s; i++) { + matches_found += substute_string(address.data.set.b[i], matches, last_regex, replace, sub_len); + } + output("replaced %ld matches over %ld lines\n", matches_found, address.data.set.s); + pending_writes = true; + break; + } + case 'w': { + bool quit = false; + if (*index == 'q') { + index++; + quit = true; + } + skip_whitespace(&index); + write_file(index, &address, "w"); + if (quit) { + free_address(address); + free_data(true); + exit(EXIT_SUCCESS); + } + break; + } + case 'r': { + skip_whitespace(&index); + read_file(index); + break; + } + case 'e': + if (!check_if_sure("Load new file for sure? ")) break; + __attribute__((fallthrough)); + case 'E': { + char* filename; + FILE* file; + + skip_whitespace(&index); + + filename = index; + if(!get_file_name(&filename)) break; + + file = get_file_s(filename, "r"); + if (file == NULL) break; + + load_file(file); + break; + } + case 'W': + skip_whitespace(&index); + write_file(index, &address, "a"); + break; + case '=': + printf("%ld\n", line_current + 1); + break; + default: + error_s("unimplemented command\n"); + break; + } + + free_address(address); + +} + +static void prompt_loop(void) { + while (true) { + prompt(); + } +} + +static void help(void) { + printf("Usage: ed [FILE]\n\n"); + printf("Edit a given [FILE] or create a new text file\n\n"); + printf("\t(.,.)\tnewline\t\tgo to address line and print\n"); + printf("\t(.)\ta\t\tappend new data after given line\n"); + printf("\t(.,.)\tc\t\treplace given lines with new data\n"); + printf("\t(.,.)\td\t\tdelete given lines\n"); + printf("\t\te file\t\tdelete current buffer and edit file\n"); + printf("\t\tE file\t\tdelete current buffer and edit file unconditionally\n"); + printf("\t\tg/re/command\tgrep all lines with regex and run command on matches\n"); + printf("\t(.,.)\tn\t\tprint given lines along with their line numbers\n"); + printf("\t(.,.)\tp\t\tprint given lines\n"); + printf("\t\tq\t\tquit file\n"); + printf("\t\tQ\t\tquit file unconditionally\n"); + printf("\t($)\tr file\t\tread file and append to end of buffer\n"); + printf("\t(.,.)\ts/re/replace/\treplace the first match on each matching line\n"); + printf("\t(.,.)\ts/re/replace/g\treplace all matches on each matching line\n"); + printf("\t(.,.)\ts/re/replace/n\treplace n matches on each matching line\n"); + printf("\t(.,.)\tw file\t\twrite contents of lines to file\n"); + printf("\t(.,.)\twq file\t\twrite contents of lines to file then quit\n"); + printf("\t(.,.)\tW file\t\tappend contents of line to file\n"); + printf("\t\t=\t\tprint current line number\n"); +} + +COMMAND(ed) { + + parse_help(argc, argv, help); + + if (argc < 1) { + load_empty(); + prompt_loop(); + } else { + FILE* file = get_file(argv[0], "r"); + load_file(file); + get_file_name(&argv[0]); + prompt_loop(); + } + return EXIT_SUCCESS; +} diff --git a/command/grep.c b/command/grep.c new file mode 100644 index 0000000..0dad094 --- /dev/null +++ b/command/grep.c @@ -0,0 +1,269 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include + +static struct { + bool filename_prefix; + bool never_file_prefix; + bool line_number; + bool only_matching_names; + bool only_non_matching_names; + bool only_line_count; + bool only_matching_part; + bool quiet; + bool inverse; + bool ignore_case; + bool is_regex; +} flags; + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'H': + flags.filename_prefix = true; + break; + case 'h': + flags.never_file_prefix = true; + break; + case 'n': + flags.line_number = true; + break; + case 'l': + flags.only_matching_names = true; + break; + case 'L': + flags.only_non_matching_names = true; + break; + case 'c': + flags.only_line_count = true; + break; + case 'o': + flags.only_matching_part = true; + break; + case 'q': + flags.quiet = true; + break; + case 'v': + flags.inverse = true; + break; + case 'i': + flags.ignore_case = true; + break; + case 'F': + flags.is_regex = false; + break; + case 'E': + flags.is_regex = true; + break; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +static void help(void) { + printf("Usage: grep [-HhlLoqviFE] [-m N] PATTERN [FILE]...\n"); + printf("Search for PATTERN in FILEs (or stdin)\n"); + printf("\t-H\tAdd 'filename:' prefix\n"); + printf("\t-h\tDo not add 'filename:' prefix\n"); + printf("\t-n\tAdd 'line_no:' prefix\n"); + printf("\t-l\tShow only names of files that match\n"); + printf("\t-L\tShow only names of files that don't match\n"); + printf("\t-c\tShow only count of matching lines\n"); + printf("\t-o\tShow only the matching part of line\n"); + printf("\t-q\tQuiet. Return 0 if PATTERN is found, 1 otherwise\n"); + printf("\t-v\tSelect non-matching lines\n"); + printf("\t-i\tIgnore case\n"); + printf("\t-F\tPATTERN is a literal (not regexp)\n"); + printf("\t-E\tPATTERN is an extended regexp\n"); +} + +static bool match_regex(char** string, re_t pattern) { + int len; + int index; + if ((index = re_matchp(pattern, *string, &len)) < 0) return false; + if (flags.only_matching_part) { + (*string) += index; + (*string)[len] = '\0'; + } + return true; +} + +static bool match_literal(char** string, char* pattern) { + char* match = *string; + size_t match_len = strlen(match); + size_t pattern_len = strlen(pattern); + size_t i; + + if (match_len < pattern_len) return false; + + for (i = 0; i < match_len - pattern_len + 1; i++) { + if ( + (!flags.ignore_case && strncmp(match + i, pattern, pattern_len) == 0) || + (flags.ignore_case && strncasecmp(match + i, pattern, pattern_len) == 0) + ) { + if (flags.only_matching_part) { + *string = (*string) + i; + (*string)[pattern_len] = '\0'; + } + return true; + } + } + + return false; +} + +static bool match(char** string, void* pattern) { + bool result; + if (flags.is_regex) { + result = match_regex(string, (re_t) pattern); + } else { + result = match_literal(string, (char*) pattern); + } + return (flags.inverse ? !result : result); +} + +static bool match_any(char* path, void* pattern) { + FILE* file; + char* buf = NULL; + size_t offset; + bool matched = false; + int read; + + file = get_file_s(path, "r"); + if (file == NULL) return false; + + while ((read = getline(&buf, &offset, file)) > 0) { + char* save = buf; + if (buf[read-1] == '\n') buf[read-1] = '\0'; + if (match(&save, pattern)) { + matched = true; + break; + } + } + + if (buf != NULL) free(buf); + + return matched; +} + +static bool match_file(char* path, void* pattern, bool many) { + FILE* file; + char* buf = NULL; + size_t offset; + int num_matched = 0; + int line_num = 0; + int read; + + file = get_file_s(path, "r"); + if (file == NULL) return false; + + while((read = getline(&buf, &offset, file)) > 0) { + char* matched = buf; + + if (buf[read-1] == '\n') buf[read-1] = '\0'; + line_num++; + + if (!match(&matched, pattern)) { + continue; + } + + num_matched++; + + if (flags.only_line_count || flags.quiet) continue; + + if ((many || flags.filename_prefix) && !flags.never_file_prefix) { + print_file_path(path); + putchar(':'); + } + + if (flags.line_number) { + printf("%d:", line_num); + } + + if (flags.only_matching_part) { + printf("%s\n", matched); + } else { + printf("%s\n", buf); + } + } + + if (!flags.quiet && flags.only_line_count) { + if ((many || flags.filename_prefix) && !flags.never_file_prefix) { + print_file_path(path); + putchar(':'); + } + printf("%d\n", num_matched); + } + + if (buf != NULL) free(buf); + + return num_matched != 0; +} + +static void* compile(char* pattern) { + if (flags.is_regex) { + return re_compile(pattern); + } else { + return pattern; + } +} + +static bool run_match(char* path, void* pattern, bool many) { + bool result; + if (flags.only_matching_names || flags.only_non_matching_names) { + result = match_any(path, pattern); + if (flags.only_non_matching_names) result = !result; + if (result && !flags.quiet) { + print_file_path(path); + putchar('\n'); + } + return result; + } else { + return match_file(path, pattern, many); + } +} + +COMMAND(grep) { + + int start, i; + char* pattern; + bool many, ok; + void* compiled; + + flags.only_matching_part = false; + flags.only_non_matching_names = false; + flags.only_matching_names = false; + flags.only_line_count = false; + flags.quiet = false; + flags.is_regex = true; + flags.line_number = false; + flags.never_file_prefix = false; + flags.filename_prefix = false; + flags.inverse = false; + + start = parse_args(argc, argv, help, short_arg, NULL); + + if (argc - start < 1) global_help(help); + + pattern = argv[start++]; + + many = argc - start > 0; + ok = false; + + compiled = compile(pattern); + if (run_match("-", compiled, many)) ok = true; + + for (i = start; i < argc; i++) { + if (run_match(argv[i], compiled, many)) ok = true; + } + + if (flags.quiet) { + return ok ? EXIT_SUCCESS : EXIT_FAILURE; + } else { + return EXIT_SUCCESS; + } +} diff --git a/command/groups.c b/command/groups.c new file mode 100644 index 0000000..cb950be --- /dev/null +++ b/command/groups.c @@ -0,0 +1,63 @@ +#include "args.h" +#include "command.h" +#include "lslib.h" + +#include +#include +#include +#include +#include +#include + +static void help (void) { + printf("Usage: groups [USER]\n\n"); + printf("Print the groups USER is in\n"); +} + +COMMAND(groups) { + + uid_t uid; + int ngroups, i; + gid_t* groups; + struct passwd* pw; + + parse_help(argc, argv, help); + + if (argc < 1) { + uid = getuid(); + pw = getpwuid(uid); + } else { + pw = getpwnam(argv[0]); + } + + if(pw == NULL){ + if (errno == 0) { + error("user not found"); + } else { + error("failed to fetch groups: %s", strerror(errno)); + } + } + + ngroups = 0; + getgrouplist(pw->pw_name, pw->pw_gid, NULL, &ngroups); + + groups = malloc(sizeof(gid_t) * ngroups); + getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); + + for (i = 0; i < ngroups; i++){ + struct group* gr = getgrgid(groups[i]); + + if(gr == NULL) { + free(groups); + error("failed to fetch groups: %s", strerror(errno)); + } + + printf("%s ",gr->gr_name); + } + + printf("\n"); + + free(groups); + + return EXIT_SUCCESS; +} diff --git a/command/head.c b/command/head.c new file mode 100644 index 0000000..c28a82c --- /dev/null +++ b/command/head.c @@ -0,0 +1,141 @@ +#include "command.h" +#include "lslib.h" + +#include + +static struct { + int count; + bool lines; + bool print_headers; + bool dont_print_headers; +} flags; + +static void head_file_lines(FILE* file) { + size_t len = 0; + char* line = NULL; + + int count = flags.count; + while(count > 0 && getline(&line, &len, file) != -1) { + printf("%s", line); + count--; + } + + free(line); + fclose(file); +} + +static void head_file_chars(FILE* file) { + char c; + int count = flags.count; + while(count > 0 && (c = getc(file)) != EOF) { + putchar(c); + count--; + } + + fclose(file); +} + +static void help(void) { + printf("Usage: head [OPTIONS] [FILE]...\n\n"); + printf("Print first 10 lines of FILEs (or stdin)\n"); + printf("With more than one FILE, precede each with a filename header.\n\n"); + printf("\t-c [+]N[bkm]\tPrint first N bytes\n"); + printf("\t-n N[bkm]\tPrint first N lines\n"); + printf("\t\t\t(b:*512 k:*1024 m:*1024^2)\n"); + printf("\t-q\t\tNever print headers\n"); + printf("\t-v\t\tAlways print headers\n"); +} + +static void print_header(char* path, bool many) { + if (flags.dont_print_headers) return; + if (!many && !flags.print_headers) return; + if (streql("-", path)) { + printf("\n==> standard input <==\n"); + } else { + printf("\n=>> %s <==\n", path); + } +} + +static void head_file(char* path, bool many) { + FILE* file = get_file(path, "r"); + print_header(path, many); + if (flags.lines) { + head_file_lines(file); + } else { + head_file_chars(file); + } +} + +static int short_arg(char c, char* next) { + switch(c) { + case 'c': { + long int bkm; + + flags.lines = false; + + check_arg(next); + bkm = get_blkm(next); + + if (bkm < 1) { + error("bkm cannot be less than 1"); + } + + flags.count = bkm; + return ARG_USED; + } + case 'n': { + long int bkm; + + flags.lines = true; + + check_arg(next); + bkm = get_blkm(next); + + if (bkm < 1) { + error("bkm cannot be less than 1"); + } + + flags.count = bkm; + return ARG_USED; + } + case 'q': + flags.dont_print_headers = true; + break; + case 'v': + flags.print_headers = true; + break; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +COMMAND(head) { + + int start, count, i; + + flags.count = 10; + flags.lines = true; + flags.print_headers = false; + flags.dont_print_headers = false; + + start = parse_args(argc, argv, help, short_arg, NULL); + + count = argc - start; + + if (count < 1) { + head_file_lines(stdin); + return EXIT_SUCCESS; + } + + if (count == 1) { + head_file(argv[start], false); + return EXIT_SUCCESS; + } + + for (i = 0; i < count; i++) { + head_file(argv[start + i], true); + } + + return EXIT_SUCCESS; +} diff --git a/command/id.c b/command/id.c new file mode 100644 index 0000000..3a63989 --- /dev/null +++ b/command/id.c @@ -0,0 +1,76 @@ +#include "args.h" +#include "command.h" +#include "lslib.h" + +#include +#include +#include +#include +#include +#include + +static void help (void) { + printf("Usage: id [USER]\n\n"); + printf("Print information about the USER\n"); +} + +COMMAND(user_id) { + + uid_t uid; + gid_t gid, *groups; + int ngroups, i; + struct passwd* pw; + struct group* ugr; + + parse_help(argc, argv, help); + + if (argc < 1) { + uid = getuid(); + pw = getpwuid(uid); + } else { + pw = getpwnam(argv[0]); + } + + if(pw == NULL){ + if (errno == 0) { + error("user not found"); + } else { + error("failed to fetch groups: %s", strerror(errno)); + } + } + + uid = pw->pw_uid; + + ngroups = 0; + getgrouplist(pw->pw_name, pw->pw_gid, NULL, &ngroups); + + groups = malloc(sizeof(gid_t) * ngroups); + getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); + + gid = pw->pw_gid; + ugr = getgrgid(gid); + printf("uid=%d(%s) gid=%d(%s) ", + uid, ugr->gr_name, gid, ugr->gr_name); + + if (ngroups > 0) { + printf("groups="); + } + + for (i = 0; i < ngroups; i++){ + struct group* gr = getgrgid(groups[i]); + if(gr == NULL) { + free(groups); + error("failed to fetch groups: %s", strerror(errno)); + } + + printf("%d(%s)", gr->gr_gid, gr->gr_name); + + if (i + 1 < ngroups) putchar(','); + } + + printf("\b\n"); + + free(groups); + + return EXIT_SUCCESS; +} diff --git a/command/ls.c b/command/ls.c new file mode 100644 index 0000000..8ed796f --- /dev/null +++ b/command/ls.c @@ -0,0 +1,563 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FILE_COLOR ANSCII BLACK COLOR +#define DIR_COLOR ANSCII BOLD NEXT NORMAL BLUE COLOR +#define DIR_COLOR_EXEC ANSCII BACKGROUND GREEN NEXT NORMAL BLACK COLOR +#define LINK_COLOR ANSCII BOLD NEXT NORMAL TURQUOISE COLOR +#define SET_UID_COLOR ANSCII BACKGROUND RED NEXT NORMAL WHITE COLOR +#define SET_GID_COLOR ANSCII BACKGROUND YELLOW NEXT NORMAL BLACK COLOR +#define EXEC_COLOR ANSCII BOLD NEXT NORMAL GREEN COLOR +#define BLK_COLOR ANSCII BOLD NEXT NORMAL YELLOW COLOR +#define SOCK_COLOR ANSCII BOLD NEXT NORMAL MAGENTA COLOR + +enum When { + YES, + NO, + AUTO +}; + +static struct { + bool hidden; + bool hide_dot; + bool more_info; + bool one_column; + bool recurse; + enum When colored; +} flags; + +struct FileInfo { + struct passwd* usr; + struct group* grp; + char* name; + char date[13]; + char mode[11]; + char size[5]; + int links; + int bytes; + bool set_uid; + bool set_gid; + bool exec; + unsigned char type; +}; + +struct FileListInfo { + int max_link; + int max_usr; + int max_grp; + int max_size; + int max_name; + int total_len; + int total_size; +}; + +static DIR* get_directory(char* path) { + DIR* d = opendir(path); + if (d == NULL) { + if (errno == ENOTDIR) { + error_s("`%s` is a a file\n", path); + } else { + error_s("failed to open directory '%s': %s\n", path, strerror(errno)); + } + } + return d; +} + +static bool get_file_info(const char* file_name, struct FileInfo* info) { + + uid_t uid; + gid_t gid; + int save, ty; + struct stat s; + size_t file_len; + + uid = getuid(); + gid = getgid(); + + memset(&s, 0, sizeof(struct stat)); + + save = push_path_buffer(file_name); + + if (lstat(get_path_buffer(), &s) < 0) { + error_s("failed to read file '%s': %s\n", get_path_buffer(), strerror(errno)); + pop_path_buffer(save); + return false; + } + + ty = (s.st_mode & S_IFMT) >> 12; + + info->set_uid = false; + info->set_gid = false; + info->exec = false; + + switch (ty) { + case DT_BLK: + info->mode[0] = 'b'; + break; + case DT_CHR: + info->mode[0] = 'c'; + break; + case DT_DIR: + info->mode[0] = 'd'; + break; + case DT_FIFO: + info->mode[0] = 'f'; + break; + case DT_LNK: + info->mode[0] = 'l'; + break; + case DT_SOCK: + info->mode[0] = 's'; + break; + case DT_UNKNOWN: + info->mode[0] = 'u'; + break; + case DT_WHT: + info->mode[0] = 'w'; + break; + default: + info->mode[0] = '-'; + break; + } + + info->mode[1] = (s.st_mode & S_IRUSR) ? 'r' : '-'; + info->mode[2] = (s.st_mode & S_IWUSR) ? 'w' : '-'; + if (s.st_mode & S_IXUSR) { + if (s.st_mode & S_ISUID) { + info->mode[3] = 's'; + info->set_uid = true; + } else { + info->mode[3] = 'x'; + } + if (!info->exec) info->exec = s.st_uid == uid; + } else { + info->mode[3] = '-'; + } + + info->mode[4] = (s.st_mode & S_IRGRP) ? 'r' : '-'; + info->mode[5] = (s.st_mode & S_IWGRP) ? 'w' : '-'; + if (s.st_mode & S_IXGRP) { + if (s.st_mode & S_ISGID) { + info->mode[6] = 's'; + info->set_gid = true; + } else { + info->mode[6] = 'x'; + } + if (!info->exec) info->exec = s.st_gid == gid; + } else { + info->mode[6] = '-'; + } + + info->mode[7] = (s.st_mode & S_IROTH) ? 'r' : '-'; + info->mode[8] = (s.st_mode & S_IWOTH) ? 'w' : '-'; + if (s.st_mode & S_IXOTH) { + info->mode[9] = 'x'; + info->exec = true; + } else { + info->mode[9] = '-'; + } + + info->mode[10] = '\0'; + + info->usr = getpwuid(s.st_uid); + if (info->usr == NULL) { + error_s("failed to get user from `%s`\n", get_path_buffer()); + pop_path_buffer(save); + return false; + } + + info->grp = getgrgid(s.st_gid); + if (info->grp == NULL) { + error_s("failed to get user from `%s`\n", get_path_buffer()); + pop_path_buffer(save); + return false; + } + + info->links = s.st_nlink; + info->type = ty; + + file_len = strlen(file_name) + 1; + info->name = malloc(file_len); + memcpy(info->name, file_name, file_len); + + print_file_size(s.st_size, info->size); + print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000000, info->date); + + info->bytes = (s.st_size + s.st_blksize - 1) / s.st_blksize; + + pop_path_buffer(save); + return true; +} + +static char* get_file_color(struct FileInfo* info) { + char* color; + if (info->type == DT_DIR) { + if (info->mode[8] == 'w') { + color = DIR_COLOR_EXEC; + } else { + color = DIR_COLOR; + } + } else if (info->type == DT_LNK) { + color = LINK_COLOR; + } else if (info->type == DT_SOCK) { + color = SOCK_COLOR; + } else if ( + info->type == DT_CHR || + info->type == DT_BLK + ) { + color = BLK_COLOR; + } else { + if (info->set_uid) { + color = SET_UID_COLOR; + } else if (info->set_gid) { + color = SET_GID_COLOR; + } else if (info->exec) { + color = EXEC_COLOR; + } else { + color = FILE_COLOR; + } + } + return color; +} + +static void list_files(struct FileInfo* files, int file_len, struct FileListInfo info) { + + struct winsize w; + char* color; + int column_width, row_count, i; + + if (flags.more_info) { + char total[13]; + print_file_size(info.total_size, total); + printf("total %s\n", total); + } + + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + + if (!isatty(1)) { + flags.one_column = true; + if (flags.colored == AUTO) + flags.colored = NO; + } + + column_width = info.max_name + 1; + row_count = w.ws_col / column_width; + + for (i = 0; i < file_len; i++) { + struct FileInfo finfo = files[i]; + color = get_file_color(&finfo); + if (flags.more_info) { + printf("%s %*d %*s %*s %*s %s %s%s%s", + finfo.mode, + info.max_link, + finfo.links, + info.max_usr, + finfo.usr->pw_name, + info.max_grp, + finfo.grp->gr_name, + info.max_size, + finfo.size, + finfo.date, + flags.colored != NO ? color : "", + finfo.name, + flags.colored != NO ? "\x1b[0m" : "" + ); + if (finfo.type == DT_LNK) { + int save = push_path_buffer(finfo.name); + + char lnk[PATH_MAX]; + ssize_t n; + if ((n = readlink(get_path_buffer(), lnk, PATH_MAX)) != -1) { + printf(" -> %.*s\n", (int)n, lnk); + } else { + putchar('\n'); + } + + pop_path_buffer(save); + } else { + putchar('\n'); + } + } else if (flags.one_column) { + printf("%s%s%s\n", flags.colored != NO ? color : "", finfo.name, flags.colored != NO ? "\x1b[0m" : ""); + } else { + if (info.total_len > w.ws_col) { + if (i != 0 && i % row_count == 0) putchar('\n'); + printf("%s%*s%s", flags.colored != NO ? color : "", -column_width, + finfo.name, flags.colored != NO ? "\x1b[0m" : ""); + } else { + printf("%s%s%s ", flags.colored != NO ? color : "", finfo.name, + flags.colored != NO ? "\x1b[0m" : ""); + } + } + free(finfo.name); + } + + if (!flags.more_info) printf("\n"); +} + +static int num_places (int n) { + int r = 1; + if (n < 0) n = (n == INT_MIN) ? INT_MAX: -n; + while (n > 9) { + n /= 10; + r++; + } + return r; +} + +static void push_file( + struct FileInfo** files, + struct FileListInfo* info, + int* size, int* capacity, + const char* file_path +) { + struct FileInfo finfo; + int user_len, group_len, name_len, size_len, link_len; + + if (!get_file_info(file_path, &finfo)) return; + + if (*size == *capacity) { + *capacity *= 2; + *files = realloc(*files, sizeof(struct FileInfo) * *capacity); + } + + user_len = strlen(finfo.usr->pw_name); + if (user_len > info->max_usr) info->max_usr = user_len; + + group_len = strlen(finfo.grp->gr_name); + if (group_len > info->max_grp) info->max_grp = group_len; + + name_len = strlen(file_path); + if (name_len > info->max_name) info->max_name = name_len; + + size_len = strlen(finfo.size); + if (size_len > info->max_size) info->max_size = size_len; + + link_len = num_places(finfo.links); + if (link_len > info->max_link) info->max_link = link_len; + + info->total_len += name_len + 2; + info->total_size += finfo.bytes; + + (*files)[*size] = finfo; + (*size)++; +} + +static void recurse_directory(char* dir_name) { + DIR* d; + int capacity, size, save; + struct dirent* file; + struct FileInfo* files; + struct FileListInfo info; + + save = push_path_buffer(dir_name); + + d = get_directory(get_path_buffer()); + if (d == NULL) { + return; + } + + capacity = 8; + size = 0; + + files = malloc(sizeof(struct FileInfo) * capacity); + memset(&info, 0, sizeof(struct FileListInfo)); + + while((file = readdir(d)) != NULL) { + if (!flags.hidden && prefix(".", file->d_name)) continue; + if (flags.hide_dot && is_dot_dir(file->d_name)) continue; + if (file->d_type == DT_DIR && !is_dot_dir(file->d_name)) { + recurse_directory(file->d_name); + } else { + push_file(&files, &info, &size, &capacity, file->d_name); + } + } + + + if (flags.colored == NO) { + printf("\n%s:\n", get_path_buffer()); + } else { + printf("\n%s%s:%s\n", DIR_COLOR, get_path_buffer(), FILE_COLOR); + } + + list_files(files, size, info); + + free(files); + + if (!flags.more_info) printf("\n"); + + closedir(d); + + pop_path_buffer(save); +} + +static void list_directory(char* path) { + + DIR* d; + int capacity, size, save; + struct FileInfo* files; + struct FileListInfo info; + struct dirent* file; + + if (flags.recurse) { + recurse_directory(path); + return; + } + + d = get_directory(path); + if (d == NULL) return; + + save = push_path_buffer(path); + + capacity = 8; + size = 0; + + files = malloc(sizeof(struct FileInfo) * capacity); + memset(&info, 0, sizeof(struct FileListInfo)); + + while ((file = readdir(d)) != NULL) { + if (!flags.hidden && prefix(".", file->d_name)) continue; + if (flags.hide_dot && is_dot_dir(file->d_name)) continue; + push_file(&files, &info, &size, &capacity, file->d_name); + } + + if (size > 0) list_files(files, size, info); + free(files); + + pop_path_buffer(save); + + closedir(d); +} + +static bool is_dir(const char* path) { + struct stat s; + if (stat(path, &s) < 0) return false; + return S_ISDIR(s.st_mode); +} + +static void list_file_args(int start, int argc, char** argv) { + + int capacity, size, i; + struct FileInfo* files; + struct FileListInfo info; + + capacity = 8; + size = 0; + + files = malloc(sizeof(struct FileInfo) * capacity); + memset(&info, 0, sizeof(struct FileListInfo)); + + for (i = start; i < argc; i++) { + if (is_dir(argv[i])) continue; + push_file(&files, &info, &size, &capacity, argv[i]); + } + + if (size > 0) list_files(files, size, info); + + free(files); +} + +static void help(void) { + printf("Usage: ls [FILE]...\n\n"); + printf("List directory contents\n\n"); + printf("\t-1\tOne column output\n"); + printf("\t-l\tLong format\n"); + printf("\t-a\tInclude names starting with .\n"); + printf("\t-A\tLike -a but without . and ..\n"); + printf("\t-R\tRecurse\n"); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'R': + flags.recurse = true; + break; + case '1': + flags.one_column = true; + break; + case 'A': + flags.hide_dot = true; + flags.hidden = true; + break; + case 'a': + flags.hidden = true; + break; + case 'l': + flags.more_info = true; + break; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +static int long_arg(char* cur, char* next) { + UNUSED(next); + if (prefix("--color=", cur)) { + char* arg = cur + 8; + if (streql("yes", arg) || streql("always", arg)) { + flags.colored = YES; + } else if (streql("auto", arg)) { + flags.colored = AUTO; + } else if (streql("no", arg) || streql("never", arg)) { + flags.colored = NO; + } else { + error("invalid color options: %s", arg); + } + } else { + return ARG_IGNORE; + } + return ARG_UNUSED; +} + +COMMAND(ls) { + + int start, i; + bool titled; + + flags.hidden = false; + flags.more_info = false; + flags.hide_dot = false; + flags.one_column = false; + flags.recurse = false; + flags.colored = NO; + + start = parse_args(argc, argv, help, short_arg, long_arg); + + if (argc - start == 0) { + list_directory("."); + return EXIT_SUCCESS; + } + + list_file_args(start, argc, argv); + + titled = argc - start > 1; + for (i = start; i < argc; i++) { + + if (!is_dir(argv[i])) continue; + + if (titled && !flags.recurse) { + if (flags.colored != NO) { + printf("\n%s%s:%s\n", DIR_COLOR, argv[i], FILE_COLOR); + } else { + printf("\n%s:\n", argv[i]); + } + } + + list_directory(argv[i]); + } + + return EXIT_SUCCESS; +} diff --git a/command/mkdir.c b/command/mkdir.c new file mode 100644 index 0000000..0d3950d --- /dev/null +++ b/command/mkdir.c @@ -0,0 +1,70 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include + +static struct { + bool make_parent; + mode_t mode; +} flags; + +static int short_arg(char c, char* next) { + switch (c) { + case 'p': + flags.make_parent = true; + break; + case 'm': + check_arg(next); + flags.mode = get_mode(next); + return ARG_USED; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +static void help(void) { + printf("Usage: mkdir [-m MODE] [-p] DIRECTORY...\n\n"); + printf("Create DIRECTORY\n\n"); + printf("\t-m\tMODE\n"); + printf("\t-p\tNo error if exists; make parent directories as needed\n"); +} + +static bool mkdir_parents(char* path) { + size_t i; + for (i = 1; i < strlen(path); i++) { + if (path[i] != '/') continue; + path[i] = '\0'; + if (mkdir(path, flags.mode) < 0 && errno != EEXIST) { + error_s("failed to create directory '%s': %s", path, strerror(errno)); + return false; + }; + path[i] = '/'; + } + return true; +} + +COMMAND(makedir) { + + int start, i; + + if (argc < 1) global_help(help); + + flags.make_parent = false; + flags.mode = 0755; + + start = parse_args(argc, argv, help, short_arg, NULL); + + for (i = start; i < argc; i++) { + if (flags.make_parent && !mkdir_parents(argv[i])) { + continue; + } + if (mkdir(argv[i], flags.mode) < 0) { + error_s("failed to create directory '%s': %s", argv[i], strerror(errno)); + } + } + + return EXIT_SUCCESS; +} diff --git a/command/mv.c b/command/mv.c new file mode 100644 index 0000000..adce2b7 --- /dev/null +++ b/command/mv.c @@ -0,0 +1,121 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include + +static struct { + bool prompt; + bool dont_overwrite; + bool refuse_if_dir; + bool verbose; +} flags; + +static void help(void) { + printf("Usage: mv [-inT] SOURCE... DIRECTORY\n\n"); + printf("Rename SOURCE to DEST, or move SOURCEs to DIRECTORY\n"); + printf("\t-i\tInteractive, prompt before overwriting\n"); + printf("\t-n\tDon't overwrite an existing file\n"); + printf("\t-T\tRefuse to move if DEST is a directory\n"); + printf("\t-v\tVerbose\n"); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 't': + flags.prompt = true; + break; + case 'n': + flags.dont_overwrite = true; + break; + case 'T': + flags.refuse_if_dir = true; + break; + case 'v': + flags.verbose = true; + break; + default: + return ARG_UNUSED; + } + return ARG_USED; +} + +static void mv_dir(bool exists) { + + char c; + + if (exists && flags.dont_overwrite) { + if (flags.verbose) output("skipping '%s'; overwrise is false", get_path_buffer_2()); + return; + } + + if (exists && flags.prompt) { + fprintf(stderr, "overwrite '%s'? ", get_path_buffer_2()); + fflush(stderr); + + c = getchar(); + if (c != 'y' && c != 'Y') { + if (flags.verbose) output("skipping..."); + return; + } + + } + + if (rename(get_path_buffer(), get_path_buffer_2()) < 0) { + error_s("cannot move '%s': %s", get_path_buffer(), strerror(errno)); + } else if (flags.verbose) { + output("moved '%s'", get_path_buffer()); + } +} + +COMMAND(mv) { + + int start, dest, i; + struct stat s; + + flags.refuse_if_dir = false; + flags.dont_overwrite = false; + flags.prompt = false; + flags.verbose = false; + + start = parse_args(argc, argv, help, short_arg, NULL); + + if (argc - start < 2) { + global_help(help); + } + + push_path_buffer_2(argv[argc-1]); + + dest = true; + if (lstat(get_path_buffer_2(), &s) < 0 && argc - start > 2) { + dest = false; + error("cannot stat '%s': %s", get_path_buffer_2(), strerror(errno)); + } + + if (dest && flags.refuse_if_dir) { + if (S_ISDIR(s.st_mode)) { + error("target '%s': Is A Directory", get_path_buffer_2()); + } + } + + if (argc - start == 2) { + push_path_buffer(argv[argc-2]); + mv_dir(dest); + return EXIT_SUCCESS; + } + + if (dest && !S_ISDIR(s.st_mode)) { + error("target '%s': Is Not A Directory", get_path_buffer_2()); + } + + for (i = start; i < argc - 1; i++) { + int save = push_path_buffer(argv[i]); + bool exists = lstat(get_path_buffer(), &s) >= 0; + mv_dir(exists); + pop_path_buffer(save); + } + + return EXIT_SUCCESS; +} diff --git a/command/printf.c b/command/printf.c new file mode 100644 index 0000000..99139d0 --- /dev/null +++ b/command/printf.c @@ -0,0 +1,144 @@ +#include "command.h" +#include "lslib.h" + +#include + +static long cast_long(const char* arg) { + char* end; + long l = strtol(arg, &end, 10); + if (end == arg) { + return 0; + } else { + return l; + } +} + +static double cast_double(const char* arg) { + char* end; + double d = strtod(arg, &end); + if (end == arg) { + return 0.0; + } else { + return d; + } +} + +#define NUMBER(name, type, arg) \ + long l = cast_long(arg); \ + type* t = (type*) &l; \ + type name = *t; + +static void handle_percent(char n, const char* arg) { + switch (n) { + case 'd': + case 'z': { + NUMBER(i, int, arg) + printf("%d", i); + break; + } + case 'u': { + NUMBER(u, unsigned int, arg); + printf("%u", u); + break; + } + case 'f': { + double d = cast_double(arg); + printf("%f", d); + break; + } + case 'c': { + putchar(arg[0]); + break; + } + case 's': { + printf("%s", arg); + break; + } + default: { + putchar('%'); + putchar(n); + } + } +} + +static void handle_slash(char n) { + switch (n) { + case 'n': + putchar('\n'); + break; + case 't': + putchar('\t'); + break; + case 'v': + putchar('\v'); + break; + case 'b': + putchar('\b'); + break; + case 'f': + putchar('\f'); + break; + case 'a': + putchar('\a'); + break; + case '"': + putchar('"'); + break; + case 'c': + exit(EXIT_SUCCESS); + default: + putchar('\\'); + putchar(n); + } +} + +static void help(void) { + printf("Usage printf FORMAT [ARG]...\n\n"); + printf("Format and print ARG(s) according to FORMAT (a-la C prinf)\n"); +} + +COMMAND(print) { + + size_t index; + int arg_index; + char n, *arg; + + if (argc < 1) { + global_help(help); + return EXIT_SUCCESS; + } + + parse_help(argc, argv, help); + + index = 0; + arg_index = 0; + + while (true) { + char c = argv[0][index]; + index++; + + if (c == '\0') break; + if (c != '%' && c != '\\') { + putchar(c); + continue; + } + + n = argv[0][index]; + index++; + + arg = NULL; + if (arg_index < argc) { + arg = argv[arg_index + 1]; + } + + if (c == '%') { + handle_percent(n, arg); + } else { + handle_slash(n); + } + + arg_index++; + } + + return EXIT_SUCCESS; +} diff --git a/command/rm.c b/command/rm.c new file mode 100644 index 0000000..81a956a --- /dev/null +++ b/command/rm.c @@ -0,0 +1,140 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include +#include + +static struct { + bool force; + bool prompt; + bool verbose; + bool recurse; +} flags; + +static void help(void) { + printf("Usage: rm [-irfv] FILE...\n\n"); + printf("Remove (unlink) FILESs\n\n"); + printf("\t-i\tAlways prompt before removing\n"); + printf("\t-f\tForce, never prompt\n"); + printf("\t-v\tVerbose\n"); + printf("\t-R,-r\tRecurse\n"); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'i': + flags.prompt = true; + break; + case 'f': + flags.force = true; + break; + case 'v': + flags.verbose = true; + break; + case 'R': + case 'r': + flags.recurse = true; + break; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +static void rm_file (char* path); + +static bool rm_dir (void) { + DIR* d; + struct dirent* file; + + d = opendir(get_path_buffer()); + if (d == NULL) { + error_s("failed to stat '%s': %s\n", get_path_buffer(), strerror(errno)); + return false; + } + + while ((file = readdir(d)) != NULL) { + if (is_dot_dir(file->d_name)) continue; + rm_file(file->d_name); + } + + closedir(d); + return true; +} + +static void rm_file(char* path) { + int save = push_path_buffer(path); + + struct stat s; + if (lstat(get_path_buffer(), &s) < 0) { + pop_path_buffer(save); + error_s("failed to stat '%s': %s\n", get_path_buffer(), strerror(errno)); + return; + } + + if (S_ISDIR(s.st_mode)) { + if (!flags.force) { + error_s("cannot delete '%s': Is a directory\n", get_path_buffer()); + pop_path_buffer(save); + return; + } + if (flags.recurse && !rm_dir()) { + pop_path_buffer(save); + return; + } + } + + if (flags.prompt) { + char c; + + fprintf(stderr, "delete '%s'? ", get_path_buffer()); + fflush(stderr); + + c = getchar(); + if (c != 'y' && c != 'Y') { + fprintf(stderr, "Skipping...\n"); + pop_path_buffer(save); + return; + } + } + + if (remove(get_path_buffer()) < 0) { + error_s("failed to delete '%s': %s\n", get_path_buffer(), strerror(errno)); + } else if (flags.verbose) { + output("deleted '%s'\n", get_path_buffer()); + } + + pop_path_buffer(save); +} + +COMMAND(rm) { + + int start, i; + + if (argc < 1) { + global_help(help); + return EXIT_SUCCESS; + } + + flags.prompt = false; + flags.force = false; + flags.verbose = false; + flags.recurse = false; + + start = parse_args(argc, argv, help, short_arg, NULL); + +#ifdef FRENCH + if (streql(argv[0], "-fr")) { + printf("\x1b[94mremoving \x1b[97mthe \x1b[91mfrench \x1b[93m(baguette noises)\x1b[0m\n"); + } +#endif + + for (i = start; i < argc; i++) { + rm_file(argv[i]); + } + + return EXIT_SUCCESS; +} diff --git a/command/tac.c b/command/tac.c new file mode 100644 index 0000000..9e9e48e --- /dev/null +++ b/command/tac.c @@ -0,0 +1,119 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include +#include + +static void help(void) { + printf("Usage: tac [FILE]...\n\n"); + printf("Concatenate FILEs and print them in reverse\n"); +} + +static void print_range(FILE* file, int start, int end) { + int len, i; + + len = end - start; + fseek(file, start, SEEK_SET); + + for (i = 0; i < len; i++) { + putchar(getc(file)); + } + + fflush(stdout); +} + +static char stdin_path[PATH_MAX]; + +static FILE* read_stdin (void) { + static bool read; + static FILE* file; + int r; + char c; + + if (read) goto finished; + read = true; + + srand(time(NULL)); + + r = rand() % 1000000; + + sprintf(stdin_path, "/tmp/%d.tac", r); + file = get_file(stdin_path, "w"); + + while((c = getchar()) != EOF) putc(c, file); + fclose(file); + + file = get_file(stdin_path, "r"); + +finished: + return file; +} + +static void parse_file(FILE* file, struct Stack* stack) { + char buf[1024], c; + int read, i; + int total = 1; + + stack_push_int(stack, 0); + rewind(file); + while ((read = fread(buf, 1, 1024, file)) > 0) { + for (i = 0; i < read; i++) { + c = buf[i]; + if (c != '\n') continue; + stack_push_int(stack, total + i); + } + total += read; + } +} + +static void tac_file(FILE* file) { + struct Stack stack; + int last, current; + + stack_init(&stack, 80); + parse_file(file, &stack); + rewind(file); + + if (!stack_pop_int(&stack, &last)) goto cleanup; + + while(stack_pop_int(&stack, ¤t)) { + print_range(file, current, last); + last = current; + } + +cleanup: + + stack_free(&stack); +} + +COMMAND(tac) { + + FILE* in; + int i; + + parse_help(argc, argv, help); + + in = read_stdin(); + + if (argc < 1) { + tac_file(in); + return EXIT_SUCCESS; + } + + for (i = 0; i < argc; i++) { + FILE* file = get_file(argv[i], "r"); + if (file == stdin) { + tac_file(in); + } else { + tac_file(file); + fclose(file); + } + } + + fclose(in); + remove(stdin_path); + + return EXIT_SUCCESS; +} diff --git a/command/tail.c b/command/tail.c new file mode 100644 index 0000000..8137eca --- /dev/null +++ b/command/tail.c @@ -0,0 +1,243 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include + +static struct { + bool lines; + int count; + bool print_headers; + bool dont_print_headers; + bool print_as_grow; + int grow_wait; +} flags; + +static size_t tail_file_lines(FILE* file, unsigned int count, size_t skip) { + + char** ring; + int* ring_len; + int index, read; + unsigned int size, i; + size_t len; + char* line; + + ring = malloc(sizeof(char*) * count); + memset(ring, 0, sizeof(char*) * count); + + ring_len = malloc(sizeof(int) * count); + + index = 0; + size = 0; + + fseek(file, skip, SEEK_SET); + + len = skip; + line = NULL; + + while ((read = getline(&line, &len, file)) != -1) { + + if (ring[index] != NULL) free(ring[index]); + ring[index] = line; + ring_len[index] = read; + + index++; + index %= count; + if (size < count) size++; + + line = NULL; + } + + index += count - size; + index %= count; + + for (i = 0; i < size; i++) { + fwrite(ring[index], ring_len[index], 1, stdout); + free(ring[index]); + index += 1; + index %= count; + } + + free(line); + fclose(file); + free(ring); + free(ring_len); + + return len; +} + +static size_t tail_file_chars(FILE* file, unsigned int count, size_t skip) { + + char* ring; + int index; + unsigned int size, i; + int read, c; + + ring = malloc(sizeof(char) * count); + memset(ring, 0, count); + + index = 0; + size = 0; + + fseek(file, skip, SEEK_SET); + read = skip; + + while((c = getc(file)) != EOF) { + ring[index] = c; + index++; + read++; + index %= count; + if (size < count) size++; + } + + index += count - size; + index %= count; + + for (i = 0; i < size; i++) { + putchar(ring[index]); + index += 1; + index %= count; + } + + fclose(file); + + return read; +} + +static void help(void) { + printf("Usage: tail [OPTIONS] [FILE]...\n\n"); + printf("Print last 10 lines of FILEs (or stdin) to.\n"); + printf("With more than one FILE, precede each with a filename header.\n\n"); + printf("\t-c [+]N[bkm]\tPrint last N bytes\n"); + printf("\t-n N[bkm]\tPrint last N lines\n"); + printf("\t\t\t(b:*512 k:*1024 m:*1024^2)\n"); + printf("\t-q\t\tNever print headers\n"); + printf("\t-v\t\tAlways print headers\n"); + printf("\t-f\t\tPrint data as file grows\n"); + printf("\t-s SECONDS\tWait SECONDS between reads with -f\n"); + exit(EXIT_SUCCESS); +} + +static void print_header(char* path, bool many) { + if (flags.dont_print_headers) return; + if (!many && !flags.print_headers) return; + if (streql("-", path)) { + printf("\n==> standard input <==\n"); + } else { + printf("\n=>> %s <==\n", path); + } +} + +static void tail_file(char* path, bool many) { + + FILE* file; + size_t skip; + + file = get_file(path, "r"); + print_header(path, many); + + skip = 0; + while (true) { + if (flags.lines) { + skip = tail_file_lines(file, flags.count, skip); + } else { + skip = tail_file_chars(file, flags.count, skip); + } + if (!flags.print_as_grow) break; + sleep(flags.grow_wait); + get_file(path, "r"); + }; +} + +static int short_arg(char c, char* next) { + switch (c) { + case 'c': { + long int bkm; + + flags.lines = false; + + check_arg(next); + bkm = get_blkm(next); + + if (bkm < 1) { + error("bkm cannot be less than 1"); + } + + flags.count = bkm; + return ARG_USED; + } + case 'n': { + long int bkm; + + flags.lines = true; + + check_arg(next); + bkm = get_blkm(next); + + if (bkm < 1) { + error("bkm cannot be less than 1"); + } + + flags.count = bkm; + return ARG_USED; + } + case 'q': + flags.dont_print_headers = true; + break; + case 'v': + flags.print_headers = true; + break; + case 'f': + flags.print_as_grow = true; + break; + case 's': { + long int sec; + + check_arg(next); + sec = get_number(next); + + if (sec < 1) { + error("wait seconds cannot be less than 1"); + } + + flags.grow_wait = sec; + return ARG_USED; + } + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +COMMAND(tail) { + + int start, count, i; + + flags.count = 10; + flags.dont_print_headers = false; + flags.print_headers = false; + flags.lines = true; + flags.print_as_grow = false; + flags.grow_wait = 10; + + start = parse_args(argc, argv, help, short_arg, NULL); + + count = argc - start; + + if (count < 1) { + tail_file_lines(stdin, 10, 0); + return EXIT_SUCCESS; + } + + if (count == 1) { + tail_file(argv[start], false); + return EXIT_SUCCESS; + } + + for (i = 0; i < count; i++) { + tail_file(argv[start + i], true); + } + + return EXIT_SUCCESS; +} diff --git a/command/tee.c b/command/tee.c new file mode 100644 index 0000000..0462517 --- /dev/null +++ b/command/tee.c @@ -0,0 +1,83 @@ +#include "command.h" +#include "lslib.h" + +#include +#include + +static struct { + bool append; + bool handle_sigint; +} flags; + +static void help(void) { + printf("Usage: tee [-ai] [FILE]...\n\n"); + printf("Copy stdin to each FILE, and also to stdout\n\n"); + printf("\t-a Append to the given FILEs, don't overwrite\n"); + printf("\t-i Ignore interrupt signals (SIGINT)\n"); + exit(EXIT_SUCCESS); +} + +static void handle(int dummy){UNUSED(dummy);} + +static void run_tee(int file_count, FILE** files) { + char c; + int i; + + while((c = getchar()) != EOF) { + int i; + for (i = 0; i < file_count; i++) { + fwrite(&c, 1, 1, files[i]); + fflush(files[i]); + } + putchar(c); + } + + for (i = 0; i < file_count; i++) { + fclose(files[i]); + } +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'a': + flags.append = true; + break; + case 'i': + flags.handle_sigint = true; + break; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +COMMAND(tee) { + + int start, i; + FILE** files; + + flags.append = false; + flags.handle_sigint = false; + + start = parse_args(argc, argv, help, short_arg, NULL); + + if (flags.handle_sigint) { + signal(SIGINT, handle); + } + + if (argc - start < 1) { + run_tee(0, NULL); + return EXIT_SUCCESS; + } + + files = malloc(sizeof(FILE*) * (argc - start)); + + for (i = start; i < argc; i++) { + FILE* file = get_file(argv[i], flags.append ? "a" : "w"); + files[i - start] = file; + } + + run_tee(argc - start, files); + return EXIT_SUCCESS; +} diff --git a/command/wc.c b/command/wc.c new file mode 100644 index 0000000..3150045 --- /dev/null +++ b/command/wc.c @@ -0,0 +1,161 @@ +#include "command.h" +#include "lslib.h" + +#include +#include + +static struct { + bool newlines; + bool words; + bool characters; + bool bytes; + bool longest_line; + bool has_flags; +} flags; + +static int lines = 0; +static int words = 0; +static int chars = 0; +static int bytes = 0; +static int logst = 0; + +static void list(int l, int w, int c, int b, int lg) { + if (flags.newlines) { + printf("\t%d", l); + } + if (flags.words) { + printf("\t%d", w); + } + if (flags.characters) { + printf("\t%d", c); + } + if (flags.bytes) { + printf("\t%d", b); + } + if (flags.longest_line) { + printf("\t%d", lg); + } +} + +#define BS 1024 + +static bool is_delimiter(char c) { + return c == ' ' || c == '\t' || c == '\n'; +} + +static void run_wc(FILE* file) { + int l = 0, w = 0, c = 0, b = 0, lg = 0; + + bool in_word = false; + int current_length = 0; + + int read; + char buf[BS]; + while ((read = fread(buf, 1, 1024, file)) > 0) { + int i; + for (i = 0; i < read; i++) { + char ch = buf[i]; + b++; + if (ch == '\n') { + l++; + lg = lg > current_length ? lg : current_length; + current_length = 0; + } else { + current_length++; + } + if (isprint(ch) || is_delimiter(ch)) c++; + if (in_word && is_delimiter(ch)) { + in_word = false; + w++; + } else if (!in_word && !is_delimiter(ch) && isprint(ch)) { + in_word = true; + } + } + } + if (in_word) w++; + lg = lg > current_length ? lg : current_length; + list(l, w, c, b, lg); + lines += l; + words += w; + chars += c; + bytes += b; + logst += lg; + if (file != stdin) + fclose(file); +} + +static void help(void) { + printf("Usage: wc [-cmlwL] [FILE]...\n\n"); + printf("Count lines, words, and bytes for FILEs (or stdin)\n\n"); + printf("\t-c Count bytes\n"); + printf("\t-m Count characters\n"); + printf("\t-l Count newlines\n"); + printf("\t-w Count words\n"); + printf("\t-L Print longest line length\n"); + exit(EXIT_SUCCESS); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'c': + flags.bytes = true; + break; + case 'm': + flags.characters = true; + break; + case 'l': + flags.newlines = true; + break; + case 'w': + flags.words = true; + break; + case 'L': + flags.longest_line = true; + break; + default: + return ARG_INVALID; + } + flags.has_flags = true; + return ARG_UNUSED; +} + +COMMAND(wc) { + + int start, i; + + flags.newlines = false; + flags.words = false; + flags.characters = false; + flags.bytes = false; + flags.longest_line = false; + flags.has_flags = false; + + + start = parse_args(argc, argv, help, short_arg, NULL); + + if (!flags.has_flags) { + flags.newlines = true; + flags.words = true; + flags.characters = true; + } + + if (argc - start < 1) { + run_wc(stdin); + printf("\n"); + return EXIT_SUCCESS; + } + + for (i = start; i < argc; i++) { + FILE* file = get_file(argv[i], "r"); + run_wc(file); + printf("\t%s\n", argv[i]); + } + + if (argc - start > 1) { + list(lines, words, chars, bytes, logst); + printf("\ttotal\n"); + } + + return EXIT_SUCCESS; +} diff --git a/command/whoami.c b/command/whoami.c new file mode 100644 index 0000000..5823b8f --- /dev/null +++ b/command/whoami.c @@ -0,0 +1,30 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include + +static void help(void) { + printf("Usage: whoami\n\n"); + printf("Print the username associated with the current effective user id\n"); + exit(EXIT_SUCCESS); +} + +COMMAND(whoami) { + + uid_t usr; + struct passwd* passwd; + + parse_help(argc, argv, help); + + usr = getuid(); + passwd = getpwuid(usr); + + if (passwd == NULL) { + printf("\x1b[1;91myou do not exist.\n"); + } else { + printf("%s\n", passwd->pw_name); + } + return EXIT_SUCCESS; +} diff --git a/command/xargs.c b/command/xargs.c new file mode 100644 index 0000000..2a41460 --- /dev/null +++ b/command/xargs.c @@ -0,0 +1,192 @@ +#include "command.h" +#include "lslib.h" + +#include +#include +#include +#include +#include + +static struct { + bool null_seperated; + bool ignore_empty; + bool print_command; + bool prompt_command; + int max_args; + FILE* file; +} flags; + +static void help(void) { + printf("Usage: xargs [OPTIONS] [PROG ARGS]\n\n"); + printf("Run PROG on every item given by stdin\n\n"); + printf("\t-0\tInput is separated by NULs\n"); + printf("\t-a FILE\tRead from FILE instead of stdin\n"); + printf("\t-r\tDon't run command if input is empty\n"); + printf("\t-t\tPrint the command on stderr before execution\n"); + printf("\t-p\tAsk user whether to run each command\n"); + printf("\t-n N\tPass no more than N args to PROG\n"); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + + switch (c) { + case '0': + flags.null_seperated = true; + break; + case 'a': + check_arg(next); + flags.file = get_file(next, "r"); + return ARG_USED; + case 'r': + flags.ignore_empty = true; + break; + case 't': + flags.print_command = true; + break; + case 'p': + flags.prompt_command = true; + break; + case 'n': { + long int n; + + check_arg(next); + n = get_number(next); + + if (n < 1) { + error("max arg count must be at least 1"); + } + + flags.max_args = n; + return ARG_USED; + } + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +char* read_next(FILE* file, int arg_count) { + + int size, capacity; + char* buf; + char c; + + if (flags.max_args != -1 && arg_count == flags.max_args) return NULL; + + size = 0; + capacity = 8; + buf = malloc(sizeof(char) * capacity); + + while(c = getc(file), true) { + if (c == EOF && size == 0) { + free(buf); + return NULL; + } + + if (size == capacity) { + capacity *= 2; + buf = realloc(buf, sizeof(char) * capacity); + } + + if (c == '\0' || c == EOF || (!flags.null_seperated && c == '\n')) { + buf[size++] = '\0'; + return buf; + } else { + buf[size++] = c; + } + } +} + +void read_args(FILE* file, char*** args, int* size, int* capacity) { + char* arg; + static int read = 0; + while (arg = read_next(file, read), true) { + if (*size == *capacity) { + *capacity *= 2; + *args = realloc(*args, sizeof(char*) * *capacity); + } + (*args)[(*size)++] = arg; + read++; + if (arg == NULL) break; + } +} + +COMMAND(xargs) { + + int start, arg_start, arg_on_stack_count; + int size, capacity, i; + char* command; + char** args; + + flags.null_seperated = false; + flags.ignore_empty = false; + flags.print_command = false; + flags.print_command = false; + flags.max_args = -1; + flags.file = stdin; + + start = parse_args(argc, argv, help, short_arg, NULL); + + if (start >= argc) { + command = "echo"; + } else { + command = argv[start]; + } + + arg_start = start + 1; + + if (arg_start >= argc) { + arg_on_stack_count = 0; + arg_start = argc - 1; + } else { + arg_on_stack_count = argc - arg_start; + } + + size = arg_on_stack_count + 1; + capacity = size + 8; + + args = malloc(sizeof(char*) * capacity); + args[0] = command; + memcpy(&args[1], &argv[arg_start], arg_on_stack_count * sizeof(char*)); + read_args(flags.file, &args, &size, &capacity); + + if (flags.ignore_empty && size < 2) goto cleanup; + + if (flags.prompt_command || flags.print_command) { + for (i = 0; i < size - 1; i++) { + fprintf(stderr, "%s ", args[i]); + } + fprintf(stderr, "\b\n"); + } + + if (flags.prompt_command) { + FILE* in; + char c; + + fprintf(stderr, "Run command? "); + fflush(stderr); + + in = get_tty_stream("r"); + c = getc(in); + fclose(in); + + if (c != 'y' && c != 'Y') { + fprintf(stderr, "Skipping...\n"); + goto cleanup; + } + } + + if (execvp(command, args) == -1) { + error("error: failed to execute command: %s", strerror(errno)); + } + +cleanup: + + for (i = arg_on_stack_count + 1; i < size - 1; i++) { + free(args[i]); + } + fclose(flags.file); + + return EXIT_SUCCESS; +} diff --git a/command/yes.c b/command/yes.c new file mode 100644 index 0000000..f979a9f --- /dev/null +++ b/command/yes.c @@ -0,0 +1,27 @@ +#include "command.h" +#include "lslib.h" + +static void help(void) { + printf("Usage: yes [STRING]\n\n"); + printf("Repeatedly output a line with all specified STRING(s), or 'y'.\n"); +} + +COMMAND(yes) { + const char* repeat; + int i; + + parse_help(argc, argv, help); + + if (argc == 0) { + repeat = "y"; + } else { + repeat = argv[0]; + for (i = 1; i < argc; i++) { + *(argv[i]-1) = ' '; + } + } + + while (true) { + printf("%s\n", repeat); + } +} diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..396bda2 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,15 @@ +-c +-std=c89 +-Wall +-Wextra +-pedantic +-O2 +-Isrc +-Icommand +-Ilib +-D_DEFAULT_SOURCE +-DMAJOR=0 +-DMINOR=0 +-DPATCH=0 +-DCHECK_LINK + diff --git a/lib/args.c b/lib/args.c new file mode 100644 index 0000000..f907746 --- /dev/null +++ b/lib/args.c @@ -0,0 +1,111 @@ +#include "args.h" +#include "error.h" +#include "convert.h" + +#include +#include +#include + +#ifndef MAJOR +#define MAJOR 0 +#endif + +#ifndef MINOR +#define MINOR 0 +#endif + +#ifndef PATCH +#define PATCH 0 +#endif + +void check_arg (char* arg) { + if (arg == NULL) { + error("expected another argument after option"); + } +} + +void global_help(void (*help)(void)) { + printf("LazySphere v%d.%d.%d multi-call binary.\n\n", MAJOR, MINOR, PATCH); + help(); + exit(EXIT_SUCCESS); +} + +void parse_help(int argc, char** argv, void (*help)(void)) { + int i; + + if (argc < 1) return; + + for (i = 0; i < argc; i++) { + if (!prefix("-", argv[i]) || streql("-", argv[i])) break; + if (help != NULL && streql("--help", argv[i])) global_help(help); + } +} + +int parse_args(int argc, char** argv, void (*help)(void), int (*short_arg)(char, char*), int (*long_arg)(char*, char*)) { + int start, i, current; + char* next_arg; + + if (argc < 1) return 0; + + start = 0; + for (i = 0; i < argc; i++) { + + if (!prefix("-", argv[i]) || streql("-", argv[i])) break; + if (help != NULL && streql("--help", argv[i])) global_help(help); + + if (i + 1 == argc) { + next_arg = NULL; + } else { + next_arg = argv[i+1]; + } + + current = i; + + if (prefix("--", argv[i])) { + int r; + + if (long_arg == NULL) { + goto exit; + } + + r = long_arg(argv[current], next_arg); + + if (r == ARG_USED) { + i++; + start++; + } else if (r == ARG_IGNORE) { + goto exit; + } else if (r == ARG_INVALID) { + error("invalid argument %s", argv[current]); + + } + } else { + size_t j; + int r; + + if (short_arg == NULL) { + goto exit; + } + + for (j = 1; j < strlen(argv[current]); j++) { + + r = short_arg(argv[current][j], next_arg); + + if (r == ARG_USED) { + i++; + start++; + } else if (r == ARG_IGNORE) { + goto exit; + } else if (r == ARG_INVALID) { + error("invalid argument -%c", argv[current][j]); + } + } + } + + start++; + } + +exit: + + return start; +} diff --git a/lib/args.h b/lib/args.h new file mode 100644 index 0000000..0b89fca --- /dev/null +++ b/lib/args.h @@ -0,0 +1,18 @@ +#ifndef ARGS_H +#define ARGS_H + +#define UNUSED(x) (void)(x) +#define ARG_UNUSED 0 +#define ARG_USED 1 +#define ARG_IGNORE 2 +#define ARG_INVALID 3 + +void check_arg (char* arg); + +void global_help(void (*help)(void)); + +void parse_help (int argc, char** argv, void (*help)(void)); + +int parse_args (int argc, char** argv, void (*help)(void), int (*short_arg)(char, char*), int (*long_arg)(char*, char*)); + +#endif diff --git a/lib/buffer.c b/lib/buffer.c new file mode 100644 index 0000000..2bee94b --- /dev/null +++ b/lib/buffer.c @@ -0,0 +1,60 @@ +#include "buffer.h" +#include "convert.h" + +#include +#include + +static int push_path_buffer_b(char* buf, int* index, const char* string) { + int save, string_len; + + save = *index; + if (*index > 1 || (*index == 1 && buf[0] != '/')) { + buf[(*index)++] = '/'; + } + + string_len = strlen(string); + memcpy(buf + *index, string, string_len + 1); + + *index += string_len; + + return save; +} + +static void pop_path_buffer_b(char* buf, int* index, int i) { + *index = i; + buf[*index] = '\0'; +} + +static char path_buffer[PATH_MAX + 1]; +static int path_buffer_index = 0; + +char* get_path_buffer(void) { + return path_buffer; +} + +int push_path_buffer(const char* string) { + return push_path_buffer_b(path_buffer, &path_buffer_index, string); +} + +void pop_path_buffer(int i) { + pop_path_buffer_b(path_buffer, &path_buffer_index, i); +} + +static char path_buffer_2[PATH_MAX + 1]; +static int path_buffer_index_2 = 0; + +char* get_path_buffer_2(void) { + return path_buffer_2; +} + +int push_path_buffer_2(const char* string) { + return push_path_buffer_b(path_buffer_2, &path_buffer_index_2, string); +} + +void pop_path_buffer_2(int i) { + pop_path_buffer_b(path_buffer_2, &path_buffer_index_2, i); +} + +bool is_dot_dir(const char* path) { + return streql(path, ".") || streql(path, ".."); +} diff --git a/lib/buffer.h b/lib/buffer.h new file mode 100644 index 0000000..fd47ee7 --- /dev/null +++ b/lib/buffer.h @@ -0,0 +1,16 @@ +#ifndef BUFFER_H +#define BUFFER_H + +#include "def.h" + +char* get_path_buffer(void); +int push_path_buffer(const char* string); +void pop_path_buffer(int i); + +char* get_path_buffer_2(void); +int push_path_buffer_2(const char* string); +void pop_path_buffer_2(int i); + +bool is_dot_dir(const char* path); + +#endif diff --git a/lib/convert.c b/lib/convert.c new file mode 100644 index 0000000..071268e --- /dev/null +++ b/lib/convert.c @@ -0,0 +1,126 @@ +#include "convert.h" +#include "error.h" + +#include +#include +#include +#include +#include + +long int get_number(const char* text) { + char* end; + long int n = strtol(text, &end, 10); + if (text == end) { + error("%s is not a valid number", text); + } + return n; +} + +long int get_blkm(const char* text) { + char* end; + long int n = strtol(text, &end, 10); + if (text == end) { + error("%s is not a valid bkm", text); + } + if (*end == '\0') return n; + switch (*end) { + case 'K': + case 'k': + return n * 1024; + case 'B': + case 'b': + return n * 512; + case 'M': + case 'm': + return n * 1024 * 1204; + default: + error("invalid bkm type %c", *end); + } + + /* shouldnt get here anyways */ + return 0; +} + +mode_t get_mode(const char* next) { + char* end = NULL; + mode_t mode = (mode_t)strtol(next, &end, 8); + if (!end) return 0; + while(isspace(*end)) end++; + if (*end != '\0' || (unsigned) mode < 010000) { + error("invalid file mode: `%s`", next); + } + return mode; +} + +bool streql(const char* a, const char* b) { + int n = 0; + + if (*a != *b) return false; + + while (true) { + if (*(a+n) != *(b+n)) return false; + if (*(a+n) == '\0') return true; + ++n; + } +} + +bool prefix(const char* pre, const char* str) { + return strncmp(pre, str, strlen(pre)) == 0; +} + +static char fs_types[5] = {'K','M','G','T','P'}; +void print_file_size(size_t bytes, char buf[5]) { + int index, n; + float next; + + index = 0; + next = bytes; + + while (true) { + if (next < 1000) { + break; + } + + if (index == 5) { + printf("999P"); + return; + } + + next /= 1024; + index++; + } + + n = snprintf(buf, 4, "%u", (int)(next+.5)); + + if (index > 0) { + buf[n] = (fs_types[index - 1]); + n++; + } + + buf[n] = '\0'; +} + +static char* months[12] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +void print_date_time(time_t mills, char buf[13]) { + struct tm* info; + int n; + + info = localtime(&mills); + n = snprintf(buf, 5, "%s ", months[info->tm_mon]); + + if (info->tm_mday < 10) { + buf[n] = ' '; + n++; + } + + snprintf(buf + n, 13 - n, "%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec); +} + +void print_file_path(char* path) { + if (streql("-", path)) { + printf("(standard input)"); + } else { + printf("%s", path); + } +} diff --git a/lib/convert.h b/lib/convert.h new file mode 100644 index 0000000..f07614b --- /dev/null +++ b/lib/convert.h @@ -0,0 +1,20 @@ +#ifndef CONVERT_H +#define CONVERT_H + +#include "def.h" + +#include +#include + +long int get_number(const char* text); +long int get_blkm(const char* text); +mode_t get_mode(const char* text); + +bool streql(const char* a, const char* b); +bool prefix(const char* pre, const char* str); + +void print_file_size(size_t bytes, char buf[5]); +void print_date_time(time_t mills, char buf[13]); +void print_file_path(char* path); + +#endif diff --git a/lib/def.h b/lib/def.h new file mode 100644 index 0000000..33a32a0 --- /dev/null +++ b/lib/def.h @@ -0,0 +1,31 @@ +#ifndef COLOR_H +#define COLOR_H + +#include + +#define ANSCII "\x1b[" +#define NEXT ";" + +#define RESET "0" +#define BOLD "1" + +#define NORMAL "3" +#define BACKGROUND "4" +#define HIGHLIGHT "9" + +#define BLACK "0" +#define RED "1" +#define GREEN "2" +#define YELLOW "3" +#define BLUE "4" +#define MAGENTA "5" +#define TURQUOISE "6" +#define WHITE "7" + +#define COLOR "m" + +typedef uint8_t bool; +#define true 1 +#define false 0 + +#endif diff --git a/lib/error.c b/lib/error.c new file mode 100644 index 0000000..67828c9 --- /dev/null +++ b/lib/error.c @@ -0,0 +1,35 @@ +#include "error.h" + +#include +#include +#include + +extern char* cmd; + +void error_s(const char *format, ...) { + va_list list; + va_start(list, format); + + fprintf(stderr, "%s: ", cmd); + vfprintf(stderr, format, list); + fprintf(stderr, "\n"); +} + +void error(const char *format, ...) { + va_list list; + va_start(list, format); + + fprintf(stderr, "%s: ", cmd); + vfprintf(stderr, format, list); + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); +} + +void output(const char *format, ...) { + va_list list; + va_start(list, format); + + fprintf(stdout, "%s: ", cmd); + vfprintf(stdout, format, list); + fprintf(stdout, "\n"); +} diff --git a/lib/error.h b/lib/error.h new file mode 100644 index 0000000..bae532e --- /dev/null +++ b/lib/error.h @@ -0,0 +1,13 @@ +#ifndef ERROR_H +#define ERROR_H + +__attribute__ ((__format__(printf, 1, 2))) +void error_s(const char* format, ...); + +__attribute__ ((__format__(printf, 1, 2))) +void error(const char* format, ...); + +__attribute__ ((__format__(printf, 1, 2))) +void output(const char* format, ...); + +#endif diff --git a/lib/file.c b/lib/file.c new file mode 100644 index 0000000..f89b489 --- /dev/null +++ b/lib/file.c @@ -0,0 +1,63 @@ +#include "file.h" +#include "error.h" +#include "convert.h" + +#include +#include +#include +#include +#include +#include + +FILE* get_file_s(const char* path, const char* type) { + struct stat s; + FILE* file; + + if (streql("-", path) && type[0] == 'r') { + clearerr(stdin); + fflush(stdin); + return stdin; + } + + if (lstat(path, &s) < 0) { + if (type[0] != 'r') goto read; + error_s("failed to read %s: %s", path, strerror(errno)); + return NULL; + } + + if (S_ISDIR(s.st_mode)) { + error_s("%s is a directory", path); + return NULL; + } + +read: + + file = fopen(path, type); + + if (file == NULL) { + error_s("failed to %s file %s: %s", type[0] == 'r' ? "read" : "write", path, strerror(errno)); + } + + return file; +} + +FILE* get_file(const char* path, const char* type) { + FILE* file = get_file_s(path, type); + if (file == NULL) exit(EXIT_FAILURE); + return file; +} + +int get_tty (void) { + int fd = open(_PATH_TTY, O_RDONLY); + if (fd < 0) error("failed to get tty: %s", strerror(errno)); + return fd; +} + +FILE* get_tty_stream(char* type) { + int fd = get_tty(); + FILE* file = fdopen(fd, type); + if (file == NULL) { + error("failed to open tty stream: %s", strerror(errno)); + } + return file; +} diff --git a/lib/file.h b/lib/file.h new file mode 100644 index 0000000..0bb5939 --- /dev/null +++ b/lib/file.h @@ -0,0 +1,14 @@ +#ifndef FILE_H +#define FILE_H + +#include + +FILE* get_file_s(const char* path, const char* type); + +FILE* get_file(const char* path, const char* type); + +int get_tty(void); + +FILE* get_tty_stream(char* type); + +#endif diff --git a/lib/lslib.h b/lib/lslib.h new file mode 100644 index 0000000..dfa1202 --- /dev/null +++ b/lib/lslib.h @@ -0,0 +1,16 @@ +#ifndef SHARED_H +#define SHARED_H + +#include +#include + +#include "args.h" +#include "buffer.h" +#include "def.h" +#include "convert.h" +#include "error.h" +#include "file.h" +#include "regex.h" +#include "stack.h" + +#endif diff --git a/lib/regex.c b/lib/regex.c new file mode 100644 index 0000000..0e0dc52 --- /dev/null +++ b/lib/regex.c @@ -0,0 +1,528 @@ +/* + * + * Mini regex-module inspired by Rob Pike's regex code described in: + * + * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + * + * + * + * Supports: + * --------- + * '.' Dot, matches any character + * '^' Start anchor, matches beginning of string + * '$' End anchor, matches end of string + * '*' Asterisk, match zero or more (greedy) + * '+' Plus, match one or more (greedy) + * '?' Question, match zero or one (non-greedy) + * '[abc]' Character class, match if one of {'a', 'b', 'c'} + * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! + * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } + * '\s' Whitespace, \t \f \r \n \v and spaces + * '\S' Non-whitespace + * '\w' Alphanumeric, [a-zA-Z0-9_] + * '\W' Non-alphanumeric + * '\d' Digits, [0-9] + * '\D' Non-digits + * + * + */ + + + +#include "regex.h" +#include +#include + +/* Definitions: */ + +#define MAX_REGEXP_OBJECTS 30 /* Max number of regex symbols in expression. */ +#define MAX_CHAR_CLASS_LEN 40 /* Max length of character-class buffer in. */ + + +enum { UNUSED, DOT, BEGIN, END, QUESTIONMARK, STAR, PLUS, CHAR, CHAR_CLASS, INV_CHAR_CLASS, DIGIT, NOT_DIGIT, ALPHA, NOT_ALPHA, WHITESPACE, NOT_WHITESPACE}; + +typedef struct regex_t +{ + unsigned char type; /* CHAR, STAR, etc. */ + union + { + unsigned char ch; /* the character itself */ + unsigned char* ccl; /* OR a pointer to characters in class */ + } u; +} regex_t; + + + +/* Private function declarations: */ +static int matchpattern(regex_t* pattern, const char* text, int* matchlength); +static int matchcharclass(char c, const char* str); +static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength); +static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength); +static int matchone(regex_t p, char c); +static int matchdigit(char c); +static int matchalpha(char c); +static int matchwhitespace(char c); +static int matchmetachar(char c, const char* str); +static int matchrange(char c, const char* str); +static int matchdot(char c); +static int ismetachar(char c); + + + +/* Public functions: */ +int re_match(const char* pattern, const char* text, int* matchlength) +{ + return re_matchp(re_compile(pattern), text, matchlength); +} + +int re_matchp(re_t pattern, const char* text, int* matchlength) +{ + *matchlength = 0; + if (pattern != 0) + { + if (pattern[0].type == BEGIN) + { + return ((matchpattern(&pattern[1], text, matchlength)) ? 0 : -1); + } + else + { + int idx = -1; + + do + { + idx += 1; + + if (matchpattern(pattern, text, matchlength)) + { + if (text[0] == '\0') + return -1; + + return idx; + } + } + while (*text++ != '\0'); + } + } + return -1; +} + +re_t re_compile(const char* pattern) +{ + /* The sizes of the two static arrays below substantiates the static RAM usage of this module. + MAX_REGEXP_OBJECTS is the max number of symbols in the expression. + MAX_CHAR_CLASS_LEN determines the size of buffer for chars in all char-classes in the expression. */ + static regex_t re_compiled[MAX_REGEXP_OBJECTS]; + static unsigned char ccl_buf[MAX_CHAR_CLASS_LEN]; + int ccl_bufidx = 1; + + char c; /* current char in pattern */ + int i = 0; /* index into pattern */ + int j = 0; /* index into re_compiled */ + + while (pattern[i] != '\0' && (j+1 < MAX_REGEXP_OBJECTS)) + { + c = pattern[i]; + + switch (c) + { + /* Meta-characters: */ + case '^': { re_compiled[j].type = BEGIN; } break; + case '$': { re_compiled[j].type = END; } break; + case '.': { re_compiled[j].type = DOT; } break; + case '*': { re_compiled[j].type = STAR; } break; + case '+': { re_compiled[j].type = PLUS; } break; + case '?': { re_compiled[j].type = QUESTIONMARK; } break; +/* case '|': { re_compiled[j].type = BRANCH; } break; <-- not working properly */ + + /* Escaped character-classes (\s \w ...): */ + case '\\': + { + if (pattern[i+1] != '\0') + { + /* Skip the escape-char '\\' */ + i += 1; + /* ... and check the next */ + switch (pattern[i]) + { + /* Meta-character: */ + case 'd': { re_compiled[j].type = DIGIT; } break; + case 'D': { re_compiled[j].type = NOT_DIGIT; } break; + case 'w': { re_compiled[j].type = ALPHA; } break; + case 'W': { re_compiled[j].type = NOT_ALPHA; } break; + case 's': { re_compiled[j].type = WHITESPACE; } break; + case 'S': { re_compiled[j].type = NOT_WHITESPACE; } break; + + /* Escaped character, e.g. '.' or '$' */ + default: + { + re_compiled[j].type = CHAR; + re_compiled[j].u.ch = pattern[i]; + } break; + } + } + /* '\\' as last char in pattern -> invalid regular expression. */ +/* + else + { + re_compiled[j].type = CHAR; + re_compiled[j].ch = pattern[i]; + } +*/ + } break; + + /* Character class: */ + case '[': + { + /* Remember where the char-buffer starts. */ + int buf_begin = ccl_bufidx; + + /* Look-ahead to determine if negated */ + if (pattern[i+1] == '^') + { + re_compiled[j].type = INV_CHAR_CLASS; + i += 1; /* Increment i to avoid including '^' in the char-buffer */ + if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '^' */ + { + return 0; + } + } + else + { + re_compiled[j].type = CHAR_CLASS; + } + + /* Copy characters inside [..] to buffer */ + while ( (pattern[++i] != ']') + && (pattern[i] != '\0')) /* Missing ] */ + { + if (pattern[i] == '\\') + { + if (ccl_bufidx >= MAX_CHAR_CLASS_LEN - 1) + { + /* fputs("exceeded internal buffer!\n", stderr); */ + return 0; + } + if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '\\' */ + { + return 0; + } + ccl_buf[ccl_bufidx++] = pattern[i++]; + } + else if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) + { + /* fputs("exceeded internal buffer!\n", stderr); */ + return 0; + } + ccl_buf[ccl_bufidx++] = pattern[i]; + } + if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) + { + /* Catches cases such as [00000000000000000000000000000000000000][ */ + /* fputs("exceeded internal buffer!\n", stderr); */ + return 0; + } + /* Null-terminate string end */ + ccl_buf[ccl_bufidx++] = 0; + re_compiled[j].u.ccl = &ccl_buf[buf_begin]; + } break; + + /* Other characters: */ + default: + { + re_compiled[j].type = CHAR; + re_compiled[j].u.ch = c; + } break; + } + /* no buffer-out-of-bounds access on invalid patterns - see https://github.com/kokke/tiny-regex-c/commit/1a279e04014b70b0695fba559a7c05d55e6ee90b */ + if (pattern[i] == 0) + { + return 0; + } + + i += 1; + j += 1; + } + /* 'UNUSED' is a sentinel used to indicate end-of-pattern */ + re_compiled[j].type = UNUSED; + + return (re_t) re_compiled; +} + +void re_print(regex_t* pattern) +{ + const char* types[] = { "UNUSED", "DOT", "BEGIN", "END", "QUESTIONMARK", "STAR", "PLUS", "CHAR", "CHAR_CLASS", "INV_CHAR_CLASS", "DIGIT", "NOT_DIGIT", "ALPHA", "NOT_ALPHA", "WHITESPACE", "NOT_WHITESPACE", "BRANCH" }; + + int i; + int j; + char c; + for (i = 0; i < MAX_REGEXP_OBJECTS; ++i) + { + if (pattern[i].type == UNUSED) + { + break; + } + + printf("type: %s", types[pattern[i].type]); + if (pattern[i].type == CHAR_CLASS || pattern[i].type == INV_CHAR_CLASS) + { + printf(" ["); + for (j = 0; j < MAX_CHAR_CLASS_LEN; ++j) + { + c = pattern[i].u.ccl[j]; + if ((c == '\0') || (c == ']')) + { + break; + } + printf("%c", c); + } + printf("]"); + } + else if (pattern[i].type == CHAR) + { + printf(" '%c'", pattern[i].u.ch); + } + printf("\n"); + } +} + + + +/* Private functions: */ +static int matchdigit(char c) +{ + return isdigit(c); +} +static int matchalpha(char c) +{ + return isalpha(c); +} +static int matchwhitespace(char c) +{ + return isspace(c); +} +static int matchalphanum(char c) +{ + return ((c == '_') || matchalpha(c) || matchdigit(c)); +} +static int matchrange(char c, const char* str) +{ + return ( (c != '-') + && (str[0] != '\0') + && (str[0] != '-') + && (str[1] == '-') + && (str[2] != '\0') + && ( (c >= str[0]) + && (c <= str[2]))); +} +static int matchdot(char c) +{ +#if defined(RE_DOT_MATCHES_NEWLINE) && (RE_DOT_MATCHES_NEWLINE == 1) + (void)c; + return 1; +#else + return c != '\n' && c != '\r'; +#endif +} +static int ismetachar(char c) +{ + return ((c == 's') || (c == 'S') || (c == 'w') || (c == 'W') || (c == 'd') || (c == 'D')); +} + +static int matchmetachar(char c, const char* str) +{ + switch (str[0]) + { + case 'd': return matchdigit(c); + case 'D': return !matchdigit(c); + case 'w': return matchalphanum(c); + case 'W': return !matchalphanum(c); + case 's': return matchwhitespace(c); + case 'S': return !matchwhitespace(c); + default: return (c == str[0]); + } +} + +static int matchcharclass(char c, const char* str) +{ + do + { + if (matchrange(c, str)) + { + return 1; + } + else if (str[0] == '\\') + { + /* Escape-char: increment str-ptr and match on next char */ + str += 1; + if (matchmetachar(c, str)) + { + return 1; + } + else if ((c == str[0]) && !ismetachar(c)) + { + return 1; + } + } + else if (c == str[0]) + { + if (c == '-') + { + return ((str[-1] == '\0') || (str[1] == '\0')); + } + else + { + return 1; + } + } + } + while (*str++ != '\0'); + + return 0; +} + +static int matchone(regex_t p, char c) +{ + switch (p.type) + { + case DOT: return matchdot(c); + case CHAR_CLASS: return matchcharclass(c, (const char*)p.u.ccl); + case INV_CHAR_CLASS: return !matchcharclass(c, (const char*)p.u.ccl); + case DIGIT: return matchdigit(c); + case NOT_DIGIT: return !matchdigit(c); + case ALPHA: return matchalphanum(c); + case NOT_ALPHA: return !matchalphanum(c); + case WHITESPACE: return matchwhitespace(c); + case NOT_WHITESPACE: return !matchwhitespace(c); + default: return (p.u.ch == c); + } +} + +static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength) +{ + int prelen = *matchlength; + const char* prepoint = text; + while ((text[0] != '\0') && matchone(p, *text)) + { + text++; + (*matchlength)++; + } + while (text >= prepoint) + { + if (matchpattern(pattern, text--, matchlength)) + return 1; + (*matchlength)--; + } + + *matchlength = prelen; + return 0; +} + +static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength) +{ + const char* prepoint = text; + while ((text[0] != '\0') && matchone(p, *text)) + { + text++; + (*matchlength)++; + } + while (text > prepoint) + { + if (matchpattern(pattern, text--, matchlength)) + return 1; + (*matchlength)--; + } + + return 0; +} + +static int matchquestion(regex_t p, regex_t* pattern, const char* text, int* matchlength) +{ + if (p.type == UNUSED) + return 1; + if (matchpattern(pattern, text, matchlength)) + return 1; + if (*text && matchone(p, *text++)) + { + if (matchpattern(pattern, text, matchlength)) + { + (*matchlength)++; + return 1; + } + } + return 0; +} + + +#if 0 + +/* Recursive matching */ +static int matchpattern(regex_t* pattern, const char* text, int *matchlength) +{ + int pre = *matchlength; + if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) + { + return matchquestion(pattern[1], &pattern[2], text, matchlength); + } + else if (pattern[1].type == STAR) + { + return matchstar(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == PLUS) + { + return matchplus(pattern[0], &pattern[2], text, matchlength); + } + else if ((pattern[0].type == END) && pattern[1].type == UNUSED) + { + return text[0] == '\0'; + } + else if ((text[0] != '\0') && matchone(pattern[0], text[0])) + { + (*matchlength)++; + return matchpattern(&pattern[1], text+1); + } + else + { + *matchlength = pre; + return 0; + } +} + +#else + +/* Iterative matching */ +static int matchpattern(regex_t* pattern, const char* text, int* matchlength) +{ + int pre = *matchlength; + do + { + if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) + { + return matchquestion(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == STAR) + { + return matchstar(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == PLUS) + { + return matchplus(pattern[0], &pattern[2], text, matchlength); + } + else if ((pattern[0].type == END) && pattern[1].type == UNUSED) + { + return (text[0] == '\0'); + } +/* Branching is not working properly + else if (pattern[1].type == BRANCH) + { + return (matchpattern(pattern, text) || matchpattern(&pattern[2], text)); + } +*/ + (*matchlength)++; + } + while ((text[0] != '\0') && matchone(*pattern++, *text++)); + + *matchlength = pre; + return 0; +} + +#endif diff --git a/lib/regex.h b/lib/regex.h new file mode 100644 index 0000000..d4c61b6 --- /dev/null +++ b/lib/regex.h @@ -0,0 +1,54 @@ +/* + * + * Mini regex-module inspired by Rob Pike's regex code described in: + * + * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + * + * + * + * Supports: + * --------- + * '.' Dot, matches any character + * '^' Start anchor, matches beginning of string + * '$' End anchor, matches end of string + * '*' Asterisk, match zero or more (greedy) + * '+' Plus, match one or more (greedy) + * '?' Question, match zero or one (non-greedy) + * '[abc]' Character class, match if one of {'a', 'b', 'c'} + * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! + * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } + * '\s' Whitespace, \t \f \r \n \v and spaces + * '\S' Non-whitespace + * '\w' Alphanumeric, [a-zA-Z0-9_] + * '\W' Non-alphanumeric + * '\d' Digits, [0-9] + * '\D' Non-digits + * + * + */ + +#ifndef _TINY_REGEX_C +#define _TINY_REGEX_C + + +#ifndef RE_DOT_MATCHES_NEWLINE +/* Define to 0 if you DON'T want '.' to match '\r' + '\n' */ +#define RE_DOT_MATCHES_NEWLINE 1 +#endif + +/* Typedef'd pointer to get abstract datatype. */ +typedef struct regex_t* re_t; + +/* Compile regex string pattern to a regex_t-array. */ +re_t re_compile(const char* pattern); + + +/* Find matches of the compiled pattern inside text. */ +int re_matchp(re_t pattern, const char* text, int* matchlength); + + +/* Find matches of the txt pattern inside text (will compile automatically first). */ +int re_match(const char* pattern, const char* text, int* matchlength); + + +#endif /* ifndef _TINY_REGEX_C */ diff --git a/lib/stack.c b/lib/stack.c new file mode 100644 index 0000000..acffc1a --- /dev/null +++ b/lib/stack.c @@ -0,0 +1,43 @@ +#include "stack.h" + +#include +#include +#include +#include + +void stack_init(struct Stack* stack, size_t size) { + stack->size = 0; + stack->capacity = size; + stack->data = malloc(size); +} + +void stack_push(struct Stack* stack, void* data, size_t len) { + size_t new_size = stack->size + len; + if (new_size >= stack->capacity) { + stack->capacity = new_size * 2; + stack->data = realloc(stack->data, stack->capacity); + } + memcpy((uint8_t*)stack->data + stack->size, data, len); + stack->size += len; +} + +void* stack_pop(struct Stack* stack, size_t len) { + if (stack->size < len) return NULL; + stack->size -= len; + return (uint8_t*)stack->data + stack->size; +} + +void stack_free(struct Stack *stack) { + free(stack->data); +} + +void stack_push_int(struct Stack *stack, int value) { + stack_push(stack, &value, sizeof(int)); +} + +bool stack_pop_int(struct Stack *stack, int* value) { + void* d = stack_pop(stack, sizeof(int)); + if (d == NULL) return false; + *value = *(int*)(d); + return true; +} diff --git a/lib/stack.h b/lib/stack.h new file mode 100644 index 0000000..38fb185 --- /dev/null +++ b/lib/stack.h @@ -0,0 +1,22 @@ +#ifndef STACK_H +#define STACK_H + +#include "def.h" + +#include + +struct Stack { + size_t size; + size_t capacity; + void* data; +}; + +void stack_init(struct Stack* stack, size_t size); +void stack_push(struct Stack* stack, void* data, size_t len); +void* stack_pop(struct Stack* stack, size_t len); +void stack_free(struct Stack* stack); + +void stack_push_int(struct Stack* stack, int value); +bool stack_pop_int(struct Stack* stack, int* value); + +#endif diff --git a/src/command.h b/src/command.h index cca2877..87cc749 100644 --- a/src/command.h +++ b/src/command.h @@ -1,32 +1,17 @@ #ifndef COMMAND_H #define COMMAND_H -#include "util/shared.h" -#include "util/stack.h" -#include "util/regex.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - #define ARGUMENTS int argc, char** argv #define NEXT_ARGS argc - 1, &argv[1] #define COMMAND(name) int name (ARGUMENTS) -#define COMMAND_EMPTY(name) int name (void) COMMAND(dd); COMMAND(cat); COMMAND(yes); COMMAND(echo); COMMAND(print); -COMMAND_EMPTY(groups); -COMMAND_EMPTY(user_id); +COMMAND(groups); +COMMAND(user_id); COMMAND(ls); COMMAND(tail); COMMAND(head); diff --git a/src/commands/cat.c b/src/commands/cat.c deleted file mode 100644 index 0495fe3..0000000 --- a/src/commands/cat.c +++ /dev/null @@ -1,140 +0,0 @@ -#include "../command.h" - -#include - -static struct { - bool number_lines; - bool number_non_empty; - bool change_non_print; - bool change_tabs; - bool end_lines_dollar; -} flags; - -static bool printable(char c) { - switch (c) { - case '\n': return true; - case '\r': return true; - case '\b': return true; - case '\t': return true; - default: return isprint(c) == 0; - } -} - -static void help(void) { - printf("Usage: cat [-nbvteA] [FILE]...\n\n"); - printf("Print FILEs to stdout\n\n"); - printf("\t-n Number output lines\n"); - printf("\t-b Number nonempty lines\n"); - printf("\t-v Show nonprinting characters as ^x or M-x\n"); - printf("\t-t ...and tabs as ^I\n"); - printf("\t-e ...and end lines with $\n"); - printf("\t-A Same as -vte\n"); -} - -static void cat_file(FILE* file) { - char c; - size_t read; - - size_t line = 1; - bool empty = true; - bool newline = true; - - while ((read = fread(&c, 1, 1, file)) != 0) { - if (c == '\n') { - if (empty && flags.number_lines) { - printf("\t%ld ", line); - } - if (flags.end_lines_dollar) { - printf("$"); - } - line++; - newline = true; - empty = true; - goto print; - } else { - empty = false; - } - - if (!newline) { - goto print; - } - - if (!empty && (flags.number_non_empty || flags.number_lines)) { - printf("\t%ld ", line); - newline = false; - } - print: - if (!flags.change_non_print || printable(c)) { - if (flags.change_tabs && c == '\t') { - fwrite("^I", 1, 2, stdout); - } else { - fwrite(&c, 1, 1, stdout); - } - } else { - c |= '@'; - fwrite(&c, 1, 1, stdout); - } - } -} - -static int short_arg(char c, char* next) { - UNUSED(next); - switch (c) { - case 'n': - flags.number_lines = true; - break; - case 'b': - flags.number_non_empty = true; - break; - case 'v': - flags.change_non_print = true; - break; - case 't': - flags.change_non_print = true; - flags.change_tabs = true; - break; - case 'e': - flags.change_non_print = true; - flags.end_lines_dollar = true; - break; - case 'A': - flags.change_non_print = true; - flags.change_tabs = true; - flags.end_lines_dollar = true; - break; - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -COMMAND(cat) { - - int start; - int arg_len; - int i; - - flags.number_lines = false; - flags.number_non_empty = false; - flags.change_non_print = false; - flags.change_tabs = false; - flags.end_lines_dollar = false; - - start = parse_args(argc, argv, help, short_arg, NULL); - - arg_len = argc - start; - - if (arg_len < 1) { - cat_file(stdin); - return EXIT_SUCCESS; - } - - for (i = start; i < argc; i++) { - FILE* in = get_file(argv[i], "r"); - cat_file(in); - if (in != stdin) - fclose(in); - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/cp.c b/src/commands/cp.c deleted file mode 100644 index df88155..0000000 --- a/src/commands/cp.c +++ /dev/null @@ -1,240 +0,0 @@ -#include "../command.h" -#include -#include -#include -#include -#include - -static struct { - bool recurse; - bool preserve; - bool sym_link; - bool hard_link; - bool verbose; -} flags; - -static void help(void) { - printf("Usage: cp [-rplsv] SOURCE... DEST\n\n"); - printf("Copy SOURCEs to DEST\n\n"); - printf("\t-R,-r\tRecurse\n"); - printf("\t-p\tPreserve file attributes if possible\n"); - printf("\t-l,-s\tCreate (sym)links\n"); - printf("\t-v\tVerbose\n"); -} - -static int short_arg(char c, char* next) { - UNUSED(next); - switch (c) { - case 'R': - case 'r': - flags.recurse = true; - break; - case 'p': - flags.preserve = true; - break; - case 'l': - flags.hard_link = true; - flags.sym_link = false; - break; - case 's': - flags.sym_link = true; - flags.hard_link = false; - break; - case 'v': - flags.verbose = true; - break; - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -static bool copy_file(char* from, char* to) { - #define BS 1024 - - FILE *from_f, *to_f; - char buf[BS]; - int read; - - from_f = get_file_s(from, "r"); - if (from_f == NULL) { return false; } - - to_f = get_file_s(to, "w"); - if (to_f == NULL) { fclose(from_f); return false; } - - - while ((read = fread(buf, 1, BS, from_f)) > 0) { - fwrite(buf, 1, read, to_f); - } - - if (flags.verbose) { - output("copied '%s'", from); - } - - return true; -} - -static bool symlink_file(char* from, char* to) { - if (symlink(from, to) < 0) { - error_s("failed to symlink '%s': %s", from, strerror(errno)); - return false; - } else if (flags.verbose) { - output("symlinked '%s'", from); - } - return true; -} - -static bool hardlink_file(char* from, char* to) { - if (link(from, to) < 0) { - error_s("failed to hardlink '%s': %s", from, strerror(errno)); - return false; - } else if (flags.verbose) { - output("hardlinked '%s'", from); - } - return true; -} - -static void run_copy(struct stat* s) { - - char* from = get_path_buffer(); - char* to = get_path_buffer_2(); - - bool result; - if (flags.sym_link) { - result = symlink_file(from, to); - } else if (flags.hard_link) { - result = hardlink_file(from, to); - } else { - result = copy_file(from, to); - } - - if (!result) return; - if (!flags.preserve) return; - - if (chmod(to, s->st_mode) < 0) { - error_s("cannot chmod '%s': %s", to, strerror(errno)); - return; - } - - if (chown(to, s->st_uid, s->st_gid) < 0) { - error_s("cannot chown '%s': %s", to, strerror(errno)); - return; - } -} - -static void cp_file(char* path); - -static void cp_directory(struct stat* s) { - - DIR* d; - struct dirent* file; - - if (!flags.recurse) { - error_s("-r not specified; omitting directory '%s'", get_path_buffer()); - return; - } - - if (mkdir(get_path_buffer_2(), s->st_mode) < 0 && errno != EEXIST) { - error_s("cannot create directory '%s': %s", get_path_buffer_2(), strerror(errno)); - return; - } - - d = opendir(get_path_buffer()); - - if (d == NULL) { - error_s("cannot open directory '%s': %s", get_path_buffer(), strerror(errno)); - return; - } - - while ((file = readdir(d)) != NULL) { - if (is_dot_dir(file->d_name)) continue; - cp_file(file->d_name); - } -} - -static char* get_file_name(char* path) { - int last = 0; - int i = 0; - - if (path[0] == '\0') return path; - - while (true) { - if (path[i+1] == '\0') break; - if (path[i] == '/') { - last = i; - } - i++; - } - - if (last == 0) return path; - else return path + last + 1; -} - -static void cp_file(char* path) { - - int save = push_path_buffer(path); - int save2 = push_path_buffer_2(get_file_name(path)); - - struct stat s; - if (lstat(get_path_buffer(), &s) < 0) { - pop_path_buffer(save); - error_s("cannot stat '%s': %s", get_path_buffer(), strerror(errno)); - return; - } - - if (S_ISDIR(s.st_mode)) { - cp_directory(&s); - } else { - run_copy(&s); - } - - pop_path_buffer(save); - pop_path_buffer_2(save2); -} - -COMMAND(cp) { - - int start, i; - struct stat s; - - flags.hard_link = false; - flags.sym_link = false; - flags.preserve = false; - flags.recurse = false; - flags.verbose = false; - - start = parse_args(argc, argv, help, short_arg, NULL); - - if (argc - start < 2) { - global_help(help); - } - - /* only when 2 args and first is a file, the 2nd will be a file */ - if (argc - start == 2) { - struct stat s; - if (lstat(argv[start], &s) < 0) { - error("cannot stat '%s': %s", argv[start], strerror(errno)); - } - push_path_buffer(argv[argc-2]); - push_path_buffer_2(argv[argc-1]); - if (!S_ISDIR(s.st_mode)) { - run_copy(&s); - } else { - cp_directory(&s); - } - return EXIT_SUCCESS; - } - - /* push directory */ - push_path_buffer_2(argv[argc-1]); - - if (lstat(get_path_buffer_2(), &s) < 0) { - error("target: '%s': %s", get_path_buffer_2(), strerror(errno)); - } - - for (i = start; i < argc - 1; i++) { - cp_file(argv[i]); - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/dd.c b/src/commands/dd.c deleted file mode 100644 index 1387317..0000000 --- a/src/commands/dd.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "../command.h" - -static void help(void) { - printf("Usage: dd [if=FILE] [of=FILE] [bs=N] [count=N]\n\n"); - printf("Copy a file with converting and formatting\n\n"); - printf("\tif=FILE\t\tRead from FILE instead of stdin\n"); - printf("\tof=FILE\t\tWrite to FILE instead of stdout\n"); - printf("\tbs=N\t\tRead and write N bytes at a time\n"); - printf("\tcount=N\t\tCopy only N input blocks\n"); -} - -COMMAND(dd) { - - FILE* in_file = stdin; - FILE* out_file = stdout; - int bs = 1024; - int count = -1; - int i; - char* buffer; - size_t read; - - parse_help(argc, argv, help); - - for (i = 0; i < argc; i++) { - if (prefix("if=", argv[i])) { - char* path = argv[i] + 3; - in_file = get_file(path, "rb"); - } else if (prefix("of=", argv[i])) { - char* path = argv[i] + 3; - out_file = get_file(path, "wb"); - } else if (prefix("bs=", argv[i])) { - char* str = argv[i] + 3; - bs = get_number(str); - if (bs < 1) { - error("block size must be greater than 0"); - } - } else if (prefix("count=", argv[i])) { - char* str = argv[i] + 6; - count = get_number(str); - if (count < 1) { - error("count must be greather than 0"); - } - } else { - error("unkown option %s", argv[i]); - } - } - - buffer = malloc(bs); - - while ((read = fread(buffer, 1, bs, in_file)) != 0) { - fwrite(buffer, 1, read, out_file); - if (--count, count == 0) break; - } - - free(buffer); - fclose(in_file); - fclose(out_file); - - return EXIT_SUCCESS; -} diff --git a/src/commands/echo.c b/src/commands/echo.c deleted file mode 100644 index fe70a6a..0000000 --- a/src/commands/echo.c +++ /dev/null @@ -1,104 +0,0 @@ -#include "../command.h" - -static struct { - bool escape_codes; - bool newline; -} flags; - -static void print_with_escape_codes(const char* str) { - - size_t index = 0; - char n; - - while (true) { - char c = str[index]; - index++; - - if (c == '\0') break; - if (c != '\\') { - putchar(c); - continue; - } - - n = str[index]; - index++; - - switch (n) { - case '\\': - putchar('\\'); - break; - case 'b': - putchar('\b'); - break; - case 'c': - exit(EXIT_SUCCESS); - case 'n': - putchar('\n'); - break; - case 'r': - putchar('\r'); - break; - case 't': - putchar('\t'); - break; - case 'v': - putchar('\v'); - break; - default: - putchar(c); - putchar(n); - } - } -} - -static int short_arg(char c, char* next) { - UNUSED(next); - switch (c) { - case 'e': - flags.escape_codes = true; - break; - case 'E': - flags.escape_codes = false; - break; - case 'n': - flags.newline = false; - break; - default: - flags.newline = true; - flags.escape_codes = false; - return ARG_IGNORE; - }; - return ARG_UNUSED; -} - -COMMAND(echo) { - - int start, i; - - if (argc < 1) { - return EXIT_SUCCESS; - } - - flags.escape_codes = false; - flags.newline = true; - - start = parse_args(argc, argv, NULL, short_arg, NULL); - - for (i = start; i < argc; i++) { - if (flags.escape_codes) { - print_with_escape_codes(argv[i]); - } else { - printf("%s", argv[i]); - } - - if (i + 1 != argc) { - putchar(' '); - } - } - - if (flags.newline) { - putchar('\n'); - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/ed.c b/src/commands/ed.c deleted file mode 100644 index d7e8881..0000000 --- a/src/commands/ed.c +++ /dev/null @@ -1,917 +0,0 @@ -#include "../command.h" -#include "../util//regex.h" - -#define INPUT_LEN 1024 - -static char** lines = NULL; -static unsigned long line_capacity; -static unsigned long line_count; -static unsigned long line_current; -static bool pending_writes; -static char* default_filename = NULL; -static re_t last_regex = NULL; - -enum LineAddressType { - INDEX, - RANGE, - SET, - FREE -}; - -struct LineAddress { - enum LineAddressType type; - union { - struct { - long int i; - } index; - struct { - long int a; - long int b; - } range; - struct { - long int* b; - unsigned long c; - unsigned long s; - } set; - } data; - bool empty; -}; - -enum RegexDirection { - BEFORE, - AFTER, - ALL -}; - -static bool read_regex(char** end, re_t* regex, enum RegexDirection dir) { - char c; - char* index = *end; - char* regex_str = index; - while(true) { - c = *(index++); - if (c == '\0') { - error_s("missing regex after %c\n", dir == BEFORE ? '?' : '/'); - return false; - } - if (c == (dir == BEFORE ? '?' : '/')) { - *(index - 1) = '\0'; - break; - } - } - *regex = re_compile(regex_str); - last_regex = *regex; - *end = index; - return true; -} - -static bool parse_regex(char** end, struct LineAddress* address, enum RegexDirection dir) { - re_t regex; - unsigned long cap, siz, i, until; - long int* buf; - - if (!read_regex(end, ®ex, dir)) return false; - - cap = 8; - siz = 0; - buf = malloc(cap * sizeof(long int)); - - i = (dir == ALL ? 0 : line_current); - until = (dir == BEFORE ? 0 : line_count - 1); - for (; (dir == BEFORE ? i >= until : i < until); dir == BEFORE ? i-- : i++) { - int len; - if (re_matchp(regex, lines[i], &len) == -1) { - if (dir == BEFORE && i == 0) break; - continue; - } - if (cap == siz) { - cap *= 2; - buf = realloc(buf, cap * sizeof(long int)); - } - buf[siz] = i; - siz++; - if (dir == BEFORE && i == 0) break; - } - - address->empty = false; - address->type = SET; - address->data.set.s = siz; - address->data.set.c = cap; - address->data.set.b = buf; - return true; -} - -static void free_address (struct LineAddress address) { - if (address.type != SET) return; - address.type = FREE; - free(address.data.set.b); -} - -static void expand_buffer(long int** buf, unsigned long* cap, unsigned long* size) { - if (*cap == *size) { - *cap *= 2; - *buf = realloc(*buf, sizeof(long int) * *cap); - } -} - -static bool parse_regex_lines(char** end, struct LineAddress* address) { - re_t regex; - unsigned long cap, siz; - long int* buf; - int len; - struct LineAddress addr; - - if (!read_regex(end, ®ex, ALL)) return false; - - cap = 8; - siz = 0; - buf = malloc(cap * sizeof(long int)); - - addr = *address; - if (addr.type == INDEX) { - if (re_matchp(regex, lines[addr.data.index.i], &len) != -1) { - buf[0] = addr.data.index.i; - siz = 1; - } - } else if (addr.type == RANGE) { - long int i; - for (i = addr.data.range.a; i < addr.data.range.b; i++) { - if (re_matchp(regex, lines[i], &len) == -1) continue; - expand_buffer(&buf, &cap, &siz); - buf[siz] = i; - siz++; - } - } else if (addr.type == SET) { - unsigned long i; - for (i = 0; i < addr.data.set.s; i++) { - if (re_matchp(regex, lines[addr.data.set.b[i]], &len) == -1) continue; - expand_buffer(&buf, &cap, &siz); - buf[siz] = addr.data.set.b[i]; - siz++; - } - } - - free_address(*address); - address->empty = false; - address->type = SET; - address->data.set.s = siz; - address->data.set.c = cap; - address->data.set.b = buf; - return true; -} - -static bool read_address(char** command, bool whitespace, struct LineAddress* a) { - char* index = *command; - struct LineAddress address; - - char* end_pre; - long int n_pre; - char pre; - - memset(&address, 0, sizeof(struct LineAddress)); - - address.empty = false; - if (strlen(*command) < 1) { - address.type = INDEX; - address.data.index.i = line_current + 1; - if (line_current >= line_count) line_current = line_count - 1; - *a = address; - return true; - } - - n_pre = strtol(index, &end_pre, 10) - 1; - if (end_pre == index) { - n_pre = -1; - } else { - if (n_pre < 0) { - error_s("input cannot be negative\n"); - return false; - } - index = end_pre; - } - - pre = *(index++); - switch (pre) { - case '.': - address.type = INDEX; - address.data.index.i = line_current; - break; - case '$': - address.type = INDEX; - address.data.index.i = line_count - 1; - break; - case '-': - case '^': { - char* end; - long int n; - - address.type = INDEX; - n = strtol(index, &end, 10) - 1; - - if (n < 0) { - error_s("input cannot be negative\n"); - return false; - } - - if (index == end) { - address.data.index.i = line_current - 1; - } else { - address.data.index.i = line_current - n; - } - - if (address.data.index.i < 0) { - error_s("line number %ld does not exist\n", address.data.index.i + 1); - return false; - } - - break; - } - case '+': { - char* end; - long int n; - - address.type = INDEX; - n = strtol(index, &end, 10) - 1; - - if (n < 0) { - error_s("input cannot be negative\n"); - return false; - } - if (index == end) { - address.data.index.i = line_current + 1; - } else { - address.data.index.i = line_current + n; - } - if (address.data.index.i >= (long int) line_count) { - error_s("line number %ld does not exist\n", address.data.index.i + 1); - return false; - } - break; - } - case '%': - address.type = RANGE; - address.data.range.a = 0; - address.data.range.b = line_count - 1; - break; - case ';': - address.type = RANGE; - address.data.range.a = line_current; - address.data.range.b = line_count - 1; - break; - case '/': - if (!parse_regex(&index, &address, AFTER)) return false; - break; - case '?': - if (!parse_regex(&index, &address, BEFORE)) return false; - break; - default: { - index--; - if (n_pre == -1) { - address.type = INDEX; - address.data.index.i = line_current; - address.empty = true; - break; - } else if (whitespace) { - address.type = INDEX; - address.data.index.i = line_current + n_pre; - } else { - address.type = INDEX; - address.data.index.i = n_pre; - } - if (address.data.index.i < 0 || address.data.index.i >= (long int) line_count) { - error_s("line number %ld does not exist\n", address.data.index.i + 1); - return false; - } - } - } - *command = index; - *a = address; - return true; -} - -static void free_data(bool all) { - if (lines != NULL) { - unsigned long i; - for (i = 0; i < line_count; i++) { - free(lines[i]); - } - free(lines); - lines = NULL; - } - if (all && default_filename != NULL) { - free(default_filename); - default_filename = NULL; - } -} - -static void load_empty(void) { - free_data(false); - - line_capacity = 8; - lines = malloc(sizeof(char*) * line_capacity); - - line_count = 0; - line_current = 0; - pending_writes = false; -} - -static void get_input(FILE* file, char*** buffer, unsigned long* capacity, unsigned long* size) { - unsigned long cap = 8; - unsigned long siz = 0; - char** buf = malloc(sizeof(char*) * cap); - - char* line = NULL; - size_t offset = 0; - - clearerr(stdin); - while (getline(&line, &offset, file) != -1) { - if (cap == siz) { - cap *= 2; - buf = realloc(buf, sizeof(char*) * cap); - } - buf[siz] = line; - siz++; - line = NULL; - } - - free(line); - - *buffer = buf; - *capacity = cap; - *size = siz; -} - -int ed_getline(char *buf, size_t size) { - size_t i = 0; - int ch; - clearerr(stdin); - while ((ch = getchar()) != EOF) { /* Read until EOF ... */ - if (i + 1 < size) { - buf[i++] = ch; - } - if (ch == '\n') { /* ... or end of line */ - break; - } - } - buf[i] = '\0'; - if (i == 0) { - return EOF; - } - return i; -} - -static void load_file(FILE* file) { - free_data(false); - line_current = 0; - get_input(file, &lines, &line_capacity, &line_count); - if (file != stdin) - fclose(file); - -} - -static bool check_if_sure(char* prompt) { - char buf[INPUT_LEN]; - - if (!pending_writes) { - return true; - } - - printf("%s", prompt); - fflush(stdout); - - if (ed_getline(buf, INPUT_LEN) == EOF) { - putchar('\n'); - return false; - } - - return prefix("y", buf); -} - -static bool skip_whitespace(char** index) { - char c; - bool w = false; - while (c = **index, c == ' ' || c == '\t') { (*index)++; w = true; } - return w; -} - -static void expand(unsigned long count) { - if (count < line_capacity) return; - line_capacity *= 2; - if (count > line_capacity) line_capacity = count; - lines = realloc(lines, line_capacity * sizeof(char*)); -} - -static void append_lines(unsigned long index, char** new, unsigned long new_len) { - if (new_len < 1) return; - pending_writes = true; - expand(line_count + new_len); - if (index + 1 <= line_count) - memmove(&lines[index+new_len], &lines[index], sizeof(char*) * (line_count - index)); - memcpy(&lines[index], new, sizeof(char*) * new_len); - line_count += new_len; -} - -static void delete_lines(unsigned long a, unsigned long b) { - unsigned long i; - - if (b < a) return; - pending_writes = true; - - for (i = a; i <= b; i++) { - free(lines[i]); - } - if (b == line_count - 1) { - line_count = a; - return; - } - memmove(&lines[a], &lines[b+1], sizeof(char*) * (line_count - (b + 1))); - line_count -= (b - a) + 1; - line_current = a; - if (line_current >= line_count) line_current = line_count - 1; -} - -static bool handle_append(struct LineAddress* address) { - char** buf; - unsigned long cap, size; - - if (address->type != INDEX) { - error_s("append command requires index addressing\n"); - return false; - } - - if (line_count == 0) { - address->data.index.i = -1; - } - - get_input(stdin, &buf, &cap, &size); - - if (size > 0) { - append_lines(address->data.index.i + 1, buf, size); - printf("ed: appened %lu lines\n", size); - } - - line_current += size; - free(buf); - return true; -} - -static bool handle_delete(struct LineAddress* address) { - if (address->empty && address->data.index.i >= (long int) line_count) { - error_s("line number %ld does not exist\n", address->data.index.i + 1); - return false; - } - - if (address->type == INDEX) { - delete_lines(address->data.index.i, address->data.index.i); - output("deleted line %ld\n", address->data.index.i+1); - } else if (address->type == RANGE) { - delete_lines(address->data.range.a, address->data.range.b); - output("deleted lines %ld-%ld\n", address->data.range.a+1, address->data.range.b+1); - } else if (address->type == SET) { - unsigned long i; - for (i = 0; i < address->data.set.s; i++) { - delete_lines(address->data.set.b[i], address->data.set.b[i]); - } - output("deleted %lu lines\n", address->data.set.s); - } - - return true; -} - -static bool get_file_name(char** filename) { - size_t len = strlen(*filename); - - if (len < 1 || (len == 1 && **filename == '\n')) { - if (default_filename == NULL) { - error_s("no default filename specified\n"); - return false; - } - *filename = default_filename; - } else { - if ((*filename)[len - 1] == '\n') { - (*filename)[len - 1] = '\0'; - len--; - } - if (default_filename != NULL) { - default_filename = realloc(default_filename, len + 1); - } else { - default_filename = malloc(len + 1); - } - memcpy(default_filename, *filename, len + 1); - *filename = default_filename; - } - return true; -} - -static void write_file(char* filename, struct LineAddress* address, char* type) { - FILE* file; - int wrote; - - if (line_count < 1) { - error_s("cannot write empty file\n"); - return; - } - - if (!get_file_name(&filename)) return; - file = get_file_s(filename, type); - if (file == NULL) return; - - wrote = 0; - - if (address->empty) { - unsigned long i; - for (i = 0; i < line_count; i++) { - fprintf(file, "%s", lines[i]); - wrote++; - } - } else if (address->type == INDEX) { - long int i = address->data.index.i; - fprintf(file, "%s", lines[i]); - wrote++; - } else if (address->type == RANGE) { - long int i; - for (i = address->data.range.a; i < address->data.range.b; i++) { - fprintf(file, "%s", lines[i]); - wrote++; - } - } else if (address->type == SET) { - unsigned long i; - for (i = 0; i < address->data.set.s; i++) { - fprintf(file, "%s", lines[address->data.set.b[i]]); - wrote++; - } - } - - pending_writes = false; - fclose(file); - output("wrote %d lines from %s\n", wrote, filename); -} - -static void read_file(char* filename) { - FILE* file; - char** buf; - long int line; - unsigned long capacity, size; - - if (!get_file_name(&filename)) return; - file = get_file_s(filename, "r"); - if (file == NULL) return; - - capacity = 8; - size = 0; - buf = malloc(capacity * sizeof(char*)); - get_input(file, &buf, &capacity, &size); - - if (size < 1) { - free(buf); - error_s("attempted to read a empty file\n"); - return; - } - - line = -1; - if (line_count > 0) { - line = line_count - 1; - } - - append_lines(line, buf, size); - free(buf); - output("read and appended %lu lines from %s\n", size, filename); -} - -static void expand_string(char** buf, int* capacity, int* size, char* text, int len) { - if (*size + len >= *capacity) { - *capacity *= 2; - if (*capacity < *size + len) *capacity = *size + len; - *buf = realloc(*buf, *capacity); - } - memcpy(*buf + *size, text, len); - *size += len; -} - -static int substute_string(long int index, long int matches, re_t regex, char* sub, int sub_len) { - int capacity = 8; - int size = 0; - char* buf = malloc(sizeof(char) * capacity); - long int left; - - int offset = 0; - int matches_found = 0; - while(true) { - int distance, len; - - if (lines[index][offset] == '\0') break; - - if (matches_found >= matches && matches > 0) break; - - if ((distance = re_matchp(regex, &lines[index][offset], &len)) == -1) { - break; - } - - if (distance > 0) { - expand_string(&buf, &capacity, &size, &lines[index][offset], distance); - } - - expand_string(&buf, &capacity, &size, sub, sub_len); - offset += len + distance; - matches_found++; - } - - left = strlen(lines[index] + offset); - expand_string(&buf, &capacity, &size, &lines[index][offset], left + 1); - - free(lines[index]); - lines[index] = buf; - return matches_found; -} - -static void prompt(void) { - char buf[INPUT_LEN]; - char* index; - char cmd; - bool whitespace, linenumbers; - struct LineAddress address; - - printf(": "); - fflush(stdout); - - if (ed_getline(buf, INPUT_LEN) == EOF) { putchar('\n'); return; } - if (buf[0] == '\0') { putchar('\n'); return; } - - index = &buf[0]; - whitespace = skip_whitespace(&index); - - if (!read_address(&index, whitespace, &address)) return; - - cmd = *(index++); - - if (cmd == ',') { - struct LineAddress address2; - - if (address.type != INDEX) { - error_s("comma range addressing requires two index addresses\n"); - free_address(address); - return; - } - - whitespace = skip_whitespace(&index); - - if (!read_address(&index, whitespace, &address2)) { - free_address(address); - return; - } - - if (address2.type != INDEX) { - error_s("comma range addressing requires two index addresses\n"); - free_address(address); - free_address(address2); - return; - } - - address.type = RANGE; - address.data.range.a = address.data.index.i; /* cursed */ - address.data.range.b = address2.data.index.i; - - cmd = *(index++); - } - - if (address.type == RANGE && address.data.range.a > address.data.range.b) { - error_s("range addressing must be in ascending order\n"); - free_address(address); - return; - } - - linenumbers = false; - -test: - switch (cmd) { - case '\0': - case '\n': - if (address.empty) { - if (line_current == line_count) { - error_s("line number %ld does not exist\n", line_current + 1); - break; - } else if (line_current + 1 == line_count) { - error_s("line number %ld does not exist\n", line_current + 2); - break; - } else { - line_current++; - } - printf("%s", lines[line_current]); - break; - } - if (address.type == INDEX) { - line_current = address.data.index.i; - } else if (address.type == RANGE) { - line_current = address.data.range.b; - } else if (address.type == SET) { - error_s("unexpected range addressing\n"); - break; - } - printf("%s", lines[line_current]); - break; - case 'a': - handle_append(&address); - break; - case 'd': - handle_delete(&address); - break; - case 'c': - if (!handle_delete(&address)) { break; } - address.type = INDEX; - address.data.index.i = line_current - 1; - handle_append(&address); - break; - case 'n': - linenumbers = true; - __attribute__((fallthrough)); - case 'p': - if (address.empty && address.data.index.i >= (long int) line_count) { - error_s("line number %ld does not exist\n", address.data.index.i + 1); - break; - } - if (address.type == INDEX) { - if (linenumbers) printf("%ld\t", address.data.index.i + 1); - printf("%s", lines[address.data.index.i]); - } else if (address.type == RANGE) { - long int i; - for (i = address.data.range.a; i <= address.data.range.b; i++) { - if (linenumbers) printf("%ld\t", i + 1); - printf("%s", lines[i]); - } - } else if (address.type == SET) { - unsigned long i; - for (i = 0; i < address.data.set.s; i++) { - if (linenumbers) printf("%ld\t", address.data.set.b[i] +1); - printf("%s", lines[address.data.set.b[i]]); - } - } - break; - case 'q': - if (!check_if_sure("Quit for sure? ")) break; - __attribute__((fallthrough)); - case 'Q': - free_address(address); - free_data(true); - exit(EXIT_SUCCESS); - break; - case 'g': - skip_whitespace(&index); - free_address(address); - if (*(index++) != '/') { - error_s("unexpected character at start of regex\n"); - break; - } - if (!parse_regex(&index, &address, ALL)) { return; } - skip_whitespace(&index); - if (*index == '\n' || *index == '\0') { - cmd = 'p'; - } else { - cmd = *(index++); - } - goto test; - break; - case 's': { - char* replace = index; - long int matches, sub_len, matches_found; - unsigned long i; - - skip_whitespace(&index); - if (*(index++) != '/') { - error_s("unexpected character at start of regex\n"); - break; - } - if (!parse_regex_lines(&index, &address)) { return; } - while(*index != '\0' && *index != '/') index++; - if (*index != '/') { - error_s("/ missing after %c\n", *index); - break; - } - if (address.data.set.s < 1) { - error_s("no matches found\n"); - break; - } - *(index++) = '\0'; - if (*index == '\0' || *index == '\n') { - matches = 1; - } else if (*index == 'g') { - matches = -1; - } else { - char* end; - matches = strtol(index, &end, 10); - if (end == index) { - error_s("invalid number: %s\n", index); - break; - } - if (matches < 1) { - error_s("matches cannot be less than 1\n"); - break; - } - } - sub_len = strlen(replace); - matches_found = 0; - - for (i = 0; i < address.data.set.s; i++) { - matches_found += substute_string(address.data.set.b[i], matches, last_regex, replace, sub_len); - } - output("replaced %ld matches over %ld lines\n", matches_found, address.data.set.s); - pending_writes = true; - break; - } - case 'w': { - bool quit = false; - if (*index == 'q') { - index++; - quit = true; - } - skip_whitespace(&index); - write_file(index, &address, "w"); - if (quit) { - free_address(address); - free_data(true); - exit(EXIT_SUCCESS); - } - break; - } - case 'r': { - skip_whitespace(&index); - read_file(index); - break; - } - case 'e': - if (!check_if_sure("Load new file for sure? ")) break; - __attribute__((fallthrough)); - case 'E': { - char* filename; - FILE* file; - - skip_whitespace(&index); - - filename = index; - if(!get_file_name(&filename)) break; - - file = get_file_s(filename, "r"); - if (file == NULL) break; - - load_file(file); - break; - } - case 'W': - skip_whitespace(&index); - write_file(index, &address, "a"); - break; - case '=': - printf("%ld\n", line_current + 1); - break; - default: - error_s("unimplemented command\n"); - break; - } - - free_address(address); - -} - -static void prompt_loop(void) { - while (true) { - prompt(); - } -} - -static void help(void) { - printf("Usage: ed [FILE]\n\n"); - printf("Edit a given [FILE] or create a new text file\n\n"); - printf("\t(.,.)\tnewline\t\tgo to address line and print\n"); - printf("\t(.)\ta\t\tappend new data after given line\n"); - printf("\t(.,.)\tc\t\treplace given lines with new data\n"); - printf("\t(.,.)\td\t\tdelete given lines\n"); - printf("\t\te file\t\tdelete current buffer and edit file\n"); - printf("\t\tE file\t\tdelete current buffer and edit file unconditionally\n"); - printf("\t\tg/re/command\tgrep all lines with regex and run command on matches\n"); - printf("\t(.,.)\tn\t\tprint given lines along with their line numbers\n"); - printf("\t(.,.)\tp\t\tprint given lines\n"); - printf("\t\tq\t\tquit file\n"); - printf("\t\tQ\t\tquit file unconditionally\n"); - printf("\t($)\tr file\t\tread file and append to end of buffer\n"); - printf("\t(.,.)\ts/re/replace/\treplace the first match on each matching line\n"); - printf("\t(.,.)\ts/re/replace/g\treplace all matches on each matching line\n"); - printf("\t(.,.)\ts/re/replace/n\treplace n matches on each matching line\n"); - printf("\t(.,.)\tw file\t\twrite contents of lines to file\n"); - printf("\t(.,.)\twq file\t\twrite contents of lines to file then quit\n"); - printf("\t(.,.)\tW file\t\tappend contents of line to file\n"); - printf("\t\t=\t\tprint current line number\n"); -} - -COMMAND(ed) { - - parse_help(argc, argv, help); - - if (argc < 1) { - load_empty(); - prompt_loop(); - } else { - FILE* file = get_file(argv[0], "r"); - load_file(file); - get_file_name(&argv[0]); - prompt_loop(); - } - return EXIT_SUCCESS; -} diff --git a/src/commands/grep.c b/src/commands/grep.c deleted file mode 100644 index 4062734..0000000 --- a/src/commands/grep.c +++ /dev/null @@ -1,265 +0,0 @@ -#include "../command.h" -#include - -static struct { - bool filename_prefix; - bool never_file_prefix; - bool line_number; - bool only_matching_names; - bool only_non_matching_names; - bool only_line_count; - bool only_matching_part; - bool quiet; - bool inverse; - bool ignore_case; - bool is_regex; -} flags; - -static int short_arg(char c, char* next) { - UNUSED(next); - switch (c) { - case 'H': - flags.filename_prefix = true; - break; - case 'h': - flags.never_file_prefix = true; - break; - case 'n': - flags.line_number = true; - break; - case 'l': - flags.only_matching_names = true; - break; - case 'L': - flags.only_non_matching_names = true; - break; - case 'c': - flags.only_line_count = true; - break; - case 'o': - flags.only_matching_part = true; - break; - case 'q': - flags.quiet = true; - break; - case 'v': - flags.inverse = true; - break; - case 'i': - flags.ignore_case = true; - break; - case 'F': - flags.is_regex = false; - break; - case 'E': - flags.is_regex = true; - break; - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -static void help(void) { - printf("Usage: grep [-HhlLoqviFE] [-m N] PATTERN [FILE]...\n"); - printf("Search for PATTERN in FILEs (or stdin)\n"); - printf("\t-H\tAdd 'filename:' prefix\n"); - printf("\t-h\tDo not add 'filename:' prefix\n"); - printf("\t-n\tAdd 'line_no:' prefix\n"); - printf("\t-l\tShow only names of files that match\n"); - printf("\t-L\tShow only names of files that don't match\n"); - printf("\t-c\tShow only count of matching lines\n"); - printf("\t-o\tShow only the matching part of line\n"); - printf("\t-q\tQuiet. Return 0 if PATTERN is found, 1 otherwise\n"); - printf("\t-v\tSelect non-matching lines\n"); - printf("\t-i\tIgnore case\n"); - printf("\t-F\tPATTERN is a literal (not regexp)\n"); - printf("\t-E\tPATTERN is an extended regexp\n"); -} - -static bool match_regex(char** string, re_t pattern) { - int len; - int index; - if ((index = re_matchp(pattern, *string, &len)) < 0) return false; - if (flags.only_matching_part) { - (*string) += index; - (*string)[len] = '\0'; - } - return true; -} - -static bool match_literal(char** string, char* pattern) { - char* match = *string; - size_t match_len = strlen(match); - size_t pattern_len = strlen(pattern); - size_t i; - - if (match_len < pattern_len) return false; - - for (i = 0; i < match_len - pattern_len + 1; i++) { - if ( - (!flags.ignore_case && strncmp(match + i, pattern, pattern_len) == 0) || - (flags.ignore_case && strncasecmp(match + i, pattern, pattern_len) == 0) - ) { - if (flags.only_matching_part) { - *string = (*string) + i; - (*string)[pattern_len] = '\0'; - } - return true; - } - } - - return false; -} - -static bool match(char** string, void* pattern) { - bool result; - if (flags.is_regex) { - result = match_regex(string, (re_t) pattern); - } else { - result = match_literal(string, (char*) pattern); - } - return (flags.inverse ? !result : result); -} - -static bool match_any(char* path, void* pattern) { - FILE* file; - char* buf = NULL; - size_t offset; - bool matched = false; - int read; - - file = get_file_s(path, "r"); - if (file == NULL) return false; - - while ((read = getline(&buf, &offset, file)) > 0) { - char* save = buf; - if (buf[read-1] == '\n') buf[read-1] = '\0'; - if (match(&save, pattern)) { - matched = true; - break; - } - } - - if (buf != NULL) free(buf); - - return matched; -} - -static bool match_file(char* path, void* pattern, bool many) { - FILE* file; - char* buf = NULL; - size_t offset; - int num_matched = 0; - int line_num = 0; - int read; - - file = get_file_s(path, "r"); - if (file == NULL) return false; - - while((read = getline(&buf, &offset, file)) > 0) { - char* matched = buf; - - if (buf[read-1] == '\n') buf[read-1] = '\0'; - line_num++; - - if (!match(&matched, pattern)) { - continue; - } - - num_matched++; - - if (flags.only_line_count || flags.quiet) continue; - - if ((many || flags.filename_prefix) && !flags.never_file_prefix) { - print_file_path(path); - putchar(':'); - } - - if (flags.line_number) { - printf("%d:", line_num); - } - - if (flags.only_matching_part) { - printf("%s\n", matched); - } else { - printf("%s\n", buf); - } - } - - if (!flags.quiet && flags.only_line_count) { - if ((many || flags.filename_prefix) && !flags.never_file_prefix) { - print_file_path(path); - putchar(':'); - } - printf("%d\n", num_matched); - } - - if (buf != NULL) free(buf); - - return num_matched != 0; -} - -static void* compile(char* pattern) { - if (flags.is_regex) { - return re_compile(pattern); - } else { - return pattern; - } -} - -static bool run_match(char* path, void* pattern, bool many) { - bool result; - if (flags.only_matching_names || flags.only_non_matching_names) { - result = match_any(path, pattern); - if (flags.only_non_matching_names) result = !result; - if (result && !flags.quiet) { - print_file_path(path); - putchar('\n'); - } - return result; - } else { - return match_file(path, pattern, many); - } -} - -COMMAND(grep) { - - int start, i; - char* pattern; - bool many, ok; - void* compiled; - - flags.only_matching_part = false; - flags.only_non_matching_names = false; - flags.only_matching_names = false; - flags.only_line_count = false; - flags.quiet = false; - flags.is_regex = true; - flags.line_number = false; - flags.never_file_prefix = false; - flags.filename_prefix = false; - flags.inverse = false; - - start = parse_args(argc, argv, help, short_arg, NULL); - - if (argc - start < 1) global_help(help); - - pattern = argv[start++]; - - many = argc - start > 0; - ok = false; - - compiled = compile(pattern); - if (run_match("-", compiled, many)) ok = true; - - for (i = start; i < argc; i++) { - if (run_match(argv[i], compiled, many)) ok = true; - } - - if (flags.quiet) { - return ok ? EXIT_SUCCESS : EXIT_FAILURE; - } else { - return EXIT_SUCCESS; - } -} diff --git a/src/commands/groups.c b/src/commands/groups.c deleted file mode 100644 index bd2e5f9..0000000 --- a/src/commands/groups.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "../command.h" - -#include -#include - -COMMAND_EMPTY(groups) { - - uid_t uid; - int ngroups, i; - gid_t* groups; - struct passwd* pw; - - uid = getuid(); - - pw = getpwuid(uid); - if(pw == NULL){ - error("failed to fetch groups: %s", strerror(errno)); - } - - ngroups = 0; - getgrouplist(pw->pw_name, pw->pw_gid, NULL, &ngroups); - - groups = malloc(sizeof(gid_t) * ngroups); - getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); - - for (i = 0; i < ngroups; i++){ - struct group* gr = getgrgid(groups[i]); - - if(gr == NULL) { - free(groups); - error("failed to fetch groups: %s", strerror(errno)); - } - - printf("%s ",gr->gr_name); - } - - printf("\n"); - - free(groups); - - return EXIT_SUCCESS; -} diff --git a/src/commands/head.c b/src/commands/head.c deleted file mode 100644 index da8b9b3..0000000 --- a/src/commands/head.c +++ /dev/null @@ -1,138 +0,0 @@ -#include "../command.h" - -static struct { - int count; - bool lines; - bool print_headers; - bool dont_print_headers; -} flags; - -static void head_file_lines(FILE* file) { - size_t len = 0; - char* line = NULL; - - int count = flags.count; - while(count > 0 && getline(&line, &len, file) != -1) { - printf("%s", line); - count--; - } - - free(line); - fclose(file); -} - -static void head_file_chars(FILE* file) { - char c; - int count = flags.count; - while(count > 0 && (c = getc(file)) != EOF) { - putchar(c); - count--; - } - - fclose(file); -} - -static void help(void) { - printf("Usage: head [OPTIONS] [FILE]...\n\n"); - printf("Print first 10 lines of FILEs (or stdin)\n"); - printf("With more than one FILE, precede each with a filename header.\n\n"); - printf("\t-c [+]N[bkm]\tPrint first N bytes\n"); - printf("\t-n N[bkm]\tPrint first N lines\n"); - printf("\t\t\t(b:*512 k:*1024 m:*1024^2)\n"); - printf("\t-q\t\tNever print headers\n"); - printf("\t-v\t\tAlways print headers\n"); -} - -static void print_header(char* path, bool many) { - if (flags.dont_print_headers) return; - if (!many && !flags.print_headers) return; - if (streql("-", path)) { - printf("\n==> standard input <==\n"); - } else { - printf("\n=>> %s <==\n", path); - } -} - -static void head_file(char* path, bool many) { - FILE* file = get_file(path, "r"); - print_header(path, many); - if (flags.lines) { - head_file_lines(file); - } else { - head_file_chars(file); - } -} - -static int short_arg(char c, char* next) { - switch(c) { - case 'c': { - long int bkm; - - flags.lines = false; - - check_arg(next); - bkm = get_blkm(next); - - if (bkm < 1) { - error("bkm cannot be less than 1"); - } - - flags.count = bkm; - return ARG_USED; - } - case 'n': { - long int bkm; - - flags.lines = true; - - check_arg(next); - bkm = get_blkm(next); - - if (bkm < 1) { - error("bkm cannot be less than 1"); - } - - flags.count = bkm; - return ARG_USED; - } - case 'q': - flags.dont_print_headers = true; - break; - case 'v': - flags.print_headers = true; - break; - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -COMMAND(head) { - - int start, count, i; - - flags.count = 10; - flags.lines = true; - flags.print_headers = false; - flags.dont_print_headers = false; - - start = parse_args(argc, argv, help, short_arg, NULL); - - count = argc - start; - - if (count < 1) { - head_file_lines(stdin); - return EXIT_SUCCESS; - } - - if (count == 1) { - head_file(argv[start], false); - return EXIT_SUCCESS; - } - - for (i = 0; i < count; i++) { - head_file(argv[start + i], true); - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/id.c b/src/commands/id.c deleted file mode 100644 index 3bef4f6..0000000 --- a/src/commands/id.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "../command.h" - -#include -#include - -COMMAND_EMPTY(user_id) { - - uid_t uid; - gid_t gid, *groups; - int ngroups, i; - struct passwd* pw; - struct group* ugr; - - uid = getuid(); - gid = getgid(); - - pw = getpwuid(uid); - if(pw == NULL){ - error("failed to fetch groups: %s", strerror(errno)); - } - - ngroups = 0; - getgrouplist(pw->pw_name, pw->pw_gid, NULL, &ngroups); - - groups = malloc(sizeof(gid_t) * ngroups); - getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); - - ugr = getgrgid(gid); - printf("uid=%d(%s) gid=%d(%s) ", - uid, ugr->gr_name, gid, ugr->gr_name); - - if (ngroups > 0) { - printf("groups="); - } - - for (i = 0; i < ngroups; i++){ - struct group* gr = getgrgid(groups[i]); - if(gr == NULL) { - free(groups); - error("failed to fetch groups: %s", strerror(errno)); - } - - printf("%d(%s)", gr->gr_gid, gr->gr_name); - - if (i + 1 < ngroups) putchar(','); - } - - printf("\b\n"); - - free(groups); - - return EXIT_SUCCESS; -} diff --git a/src/commands/ls.c b/src/commands/ls.c deleted file mode 100644 index e8d58d0..0000000 --- a/src/commands/ls.c +++ /dev/null @@ -1,552 +0,0 @@ -#include "../command.h" - -#include -#include -#include -#include -#include -#include - -#define FILE_COLOR ANSCII BLACK COLOR -#define DIR_COLOR ANSCII BOLD NEXT NORMAL BLUE COLOR -#define DIR_COLOR_EXEC ANSCII BACKGROUND GREEN NEXT NORMAL BLACK COLOR -#define LINK_COLOR ANSCII BOLD NEXT NORMAL TURQUOISE COLOR -#define SET_UID_COLOR ANSCII BACKGROUND RED NEXT NORMAL WHITE COLOR -#define SET_GID_COLOR ANSCII BACKGROUND YELLOW NEXT NORMAL BLACK COLOR -#define EXEC_COLOR ANSCII BOLD NEXT NORMAL GREEN COLOR -#define BLK_COLOR ANSCII BOLD NEXT NORMAL YELLOW COLOR -#define SOCK_COLOR ANSCII BOLD NEXT NORMAL MAGENTA COLOR - -static struct { - bool hidden; - bool hide_dot; - bool more_info; - bool one_column; - bool recurse; - enum When colored; -} flags; - -struct FileInfo { - struct passwd* usr; - struct group* grp; - char* name; - char date[13]; - char mode[11]; - char size[5]; - int links; - int bytes; - bool set_uid; - bool set_gid; - bool exec; - unsigned char type; -}; - -struct FileListInfo { - int max_link; - int max_usr; - int max_grp; - int max_size; - int max_name; - int total_len; - int total_size; -}; - -static DIR* get_directory(char* path) { - DIR* d = opendir(path); - if (d == NULL) { - if (errno == ENOTDIR) { - error_s("`%s` is a a file\n", path); - } else { - error_s("failed to open directory '%s': %s\n", path, strerror(errno)); - } - } - return d; -} - -static bool get_file_info(const char* file_name, struct FileInfo* info) { - - uid_t uid; - gid_t gid; - int save, ty; - struct stat s; - size_t file_len; - - uid = getuid(); - gid = getgid(); - - memset(&s, 0, sizeof(struct stat)); - - save = push_path_buffer(file_name); - - if (lstat(get_path_buffer(), &s) < 0) { - error_s("failed to read file '%s': %s\n", get_path_buffer(), strerror(errno)); - pop_path_buffer(save); - return false; - } - - ty = (s.st_mode & S_IFMT) >> 12; - - info->set_uid = false; - info->set_gid = false; - info->exec = false; - - switch (ty) { - case DT_BLK: - info->mode[0] = 'b'; - break; - case DT_CHR: - info->mode[0] = 'c'; - break; - case DT_DIR: - info->mode[0] = 'd'; - break; - case DT_FIFO: - info->mode[0] = 'f'; - break; - case DT_LNK: - info->mode[0] = 'l'; - break; - case DT_SOCK: - info->mode[0] = 's'; - break; - case DT_UNKNOWN: - info->mode[0] = 'u'; - break; - case DT_WHT: - info->mode[0] = 'w'; - break; - default: - info->mode[0] = '-'; - break; - } - - info->mode[1] = (s.st_mode & S_IRUSR) ? 'r' : '-'; - info->mode[2] = (s.st_mode & S_IWUSR) ? 'w' : '-'; - if (s.st_mode & S_IXUSR) { - if (s.st_mode & S_ISUID) { - info->mode[3] = 's'; - info->set_uid = true; - } else { - info->mode[3] = 'x'; - } - if (!info->exec) info->exec = s.st_uid == uid; - } else { - info->mode[3] = '-'; - } - - info->mode[4] = (s.st_mode & S_IRGRP) ? 'r' : '-'; - info->mode[5] = (s.st_mode & S_IWGRP) ? 'w' : '-'; - if (s.st_mode & S_IXGRP) { - if (s.st_mode & S_ISGID) { - info->mode[6] = 's'; - info->set_gid = true; - } else { - info->mode[6] = 'x'; - } - if (!info->exec) info->exec = s.st_gid == gid; - } else { - info->mode[6] = '-'; - } - - info->mode[7] = (s.st_mode & S_IROTH) ? 'r' : '-'; - info->mode[8] = (s.st_mode & S_IWOTH) ? 'w' : '-'; - if (s.st_mode & S_IXOTH) { - info->mode[9] = 'x'; - info->exec = true; - } else { - info->mode[9] = '-'; - } - - info->mode[10] = '\0'; - - info->usr = getpwuid(s.st_uid); - if (info->usr == NULL) { - error_s("failed to get user from `%s`\n", get_path_buffer()); - pop_path_buffer(save); - return false; - } - - info->grp = getgrgid(s.st_gid); - if (info->grp == NULL) { - error_s("failed to get user from `%s`\n", get_path_buffer()); - pop_path_buffer(save); - return false; - } - - info->links = s.st_nlink; - info->type = ty; - - file_len = strlen(file_name) + 1; - info->name = malloc(file_len); - memcpy(info->name, file_name, file_len); - - print_file_size(s.st_size, info->size); - print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000000, info->date); - - info->bytes = (s.st_size + s.st_blksize - 1) / s.st_blksize; - - pop_path_buffer(save); - return true; -} - -static char* get_file_color(struct FileInfo* info) { - char* color; - if (info->type == DT_DIR) { - if (info->mode[8] == 'w') { - color = DIR_COLOR_EXEC; - } else { - color = DIR_COLOR; - } - } else if (info->type == DT_LNK) { - color = LINK_COLOR; - } else if (info->type == DT_SOCK) { - color = SOCK_COLOR; - } else if ( - info->type == DT_CHR || - info->type == DT_BLK - ) { - color = BLK_COLOR; - } else { - if (info->set_uid) { - color = SET_UID_COLOR; - } else if (info->set_gid) { - color = SET_GID_COLOR; - } else if (info->exec) { - color = EXEC_COLOR; - } else { - color = FILE_COLOR; - } - } - return color; -} - -static void list_files(struct FileInfo* files, int file_len, struct FileListInfo info) { - - struct winsize w; - char* color; - int column_width, row_count, i; - - if (flags.more_info) { - char total[13]; - print_file_size(info.total_size, total); - printf("total %s\n", total); - } - - ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - - if (!isatty(1)) { - flags.one_column = true; - if (flags.colored == AUTO) - flags.colored = NO; - } - - column_width = info.max_name + 1; - row_count = w.ws_col / column_width; - - for (i = 0; i < file_len; i++) { - struct FileInfo finfo = files[i]; - color = get_file_color(&finfo); - if (flags.more_info) { - printf("%s %*d %*s %*s %*s %s %s%s%s", - finfo.mode, - info.max_link, - finfo.links, - info.max_usr, - finfo.usr->pw_name, - info.max_grp, - finfo.grp->gr_name, - info.max_size, - finfo.size, - finfo.date, - flags.colored != NO ? color : "", - finfo.name, - flags.colored != NO ? "\x1b[0m" : "" - ); - if (finfo.type == DT_LNK) { - int save = push_path_buffer(finfo.name); - - char lnk[PATH_MAX]; - ssize_t n; - if ((n = readlink(get_path_buffer(), lnk, PATH_MAX)) != -1) { - printf(" -> %.*s\n", (int)n, lnk); - } else { - putchar('\n'); - } - - pop_path_buffer(save); - } else { - putchar('\n'); - } - } else if (flags.one_column) { - printf("%s%s%s\n", flags.colored != NO ? color : "", finfo.name, flags.colored != NO ? "\x1b[0m" : ""); - } else { - if (info.total_len > w.ws_col) { - if (i != 0 && i % row_count == 0) putchar('\n'); - printf("%s%*s%s", flags.colored != NO ? color : "", -column_width, - finfo.name, flags.colored != NO ? "\x1b[0m" : ""); - } else { - printf("%s%s%s ", flags.colored != NO ? color : "", finfo.name, - flags.colored != NO ? "\x1b[0m" : ""); - } - } - free(finfo.name); - } - - if (!flags.more_info) printf("\n"); -} - -static int num_places (int n) { - int r = 1; - if (n < 0) n = (n == INT_MIN) ? INT_MAX: -n; - while (n > 9) { - n /= 10; - r++; - } - return r; -} - -static void push_file( - struct FileInfo** files, - struct FileListInfo* info, - int* size, int* capacity, - const char* file_path -) { - struct FileInfo finfo; - int user_len, group_len, name_len, size_len, link_len; - - if (!get_file_info(file_path, &finfo)) return; - - if (*size == *capacity) { - *capacity *= 2; - *files = realloc(*files, sizeof(struct FileInfo) * *capacity); - } - - user_len = strlen(finfo.usr->pw_name); - if (user_len > info->max_usr) info->max_usr = user_len; - - group_len = strlen(finfo.grp->gr_name); - if (group_len > info->max_grp) info->max_grp = group_len; - - name_len = strlen(file_path); - if (name_len > info->max_name) info->max_name = name_len; - - size_len = strlen(finfo.size); - if (size_len > info->max_size) info->max_size = size_len; - - link_len = num_places(finfo.links); - if (link_len > info->max_link) info->max_link = link_len; - - info->total_len += name_len + 2; - info->total_size += finfo.bytes; - - (*files)[*size] = finfo; - (*size)++; -} - -static void recurse_directory(char* dir_name) { - DIR* d; - int capacity, size, save; - struct dirent* file; - struct FileInfo* files; - struct FileListInfo info; - - save = push_path_buffer(dir_name); - - d = get_directory(get_path_buffer()); - if (d == NULL) { - return; - } - - capacity = 8; - size = 0; - - files = malloc(sizeof(struct FileInfo) * capacity); - memset(&info, 0, sizeof(struct FileListInfo)); - - while((file = readdir(d)) != NULL) { - if (!flags.hidden && prefix(".", file->d_name)) continue; - if (flags.hide_dot && is_dot_dir(file->d_name)) continue; - if (file->d_type == DT_DIR && !is_dot_dir(file->d_name)) { - recurse_directory(file->d_name); - } else { - push_file(&files, &info, &size, &capacity, file->d_name); - } - } - - - if (flags.colored == NO) { - printf("\n%s:\n", get_path_buffer()); - } else { - printf("\n%s%s:%s\n", DIR_COLOR, get_path_buffer(), FILE_COLOR); - } - - list_files(files, size, info); - - free(files); - - if (!flags.more_info) printf("\n"); - - closedir(d); - - pop_path_buffer(save); -} - -static void list_directory(char* path) { - - DIR* d; - int capacity, size, save; - struct FileInfo* files; - struct FileListInfo info; - struct dirent* file; - - if (flags.recurse) { - recurse_directory(path); - return; - } - - d = get_directory(path); - if (d == NULL) return; - - save = push_path_buffer(path); - - capacity = 8; - size = 0; - - files = malloc(sizeof(struct FileInfo) * capacity); - memset(&info, 0, sizeof(struct FileListInfo)); - - while ((file = readdir(d)) != NULL) { - if (!flags.hidden && prefix(".", file->d_name)) continue; - if (flags.hide_dot && is_dot_dir(file->d_name)) continue; - push_file(&files, &info, &size, &capacity, file->d_name); - } - - if (size > 0) list_files(files, size, info); - free(files); - - pop_path_buffer(save); - - closedir(d); -} - -static bool is_dir(const char* path) { - struct stat s; - if (stat(path, &s) < 0) return false; - return S_ISDIR(s.st_mode); -} - -static void list_file_args(int start, int argc, char** argv) { - - int capacity, size, i; - struct FileInfo* files; - struct FileListInfo info; - - capacity = 8; - size = 0; - - files = malloc(sizeof(struct FileInfo) * capacity); - memset(&info, 0, sizeof(struct FileListInfo)); - - for (i = start; i < argc; i++) { - if (is_dir(argv[i])) continue; - push_file(&files, &info, &size, &capacity, argv[i]); - } - - if (size > 0) list_files(files, size, info); - - free(files); -} - -static void help(void) { - printf("Usage: ls [FILE]...\n\n"); - printf("List directory contents\n\n"); - printf("\t-1\tOne column output\n"); - printf("\t-l\tLong format\n"); - printf("\t-a\tInclude names starting with .\n"); - printf("\t-A\tLike -a but without . and ..\n"); - printf("\t-R\tRecurse\n"); -} - -static int short_arg(char c, char* next) { - UNUSED(next); - switch (c) { - case 'R': - flags.recurse = true; - break; - case '1': - flags.one_column = true; - break; - case 'A': - flags.hide_dot = true; - flags.hidden = true; - break; - case 'a': - flags.hidden = true; - break; - case 'l': - flags.more_info = true; - break; - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -static int long_arg(char* cur, char* next) { - UNUSED(next); - if (prefix("--color=", cur)) { - char* arg = cur + 8; - if (streql("yes", arg) || streql("always", arg)) { - flags.colored = YES; - } else if (streql("auto", arg)) { - flags.colored = AUTO; - } else if (streql("no", arg) || streql("never", arg)) { - flags.colored = NO; - } else { - error("invalid color options: %s", arg); - } - } else { - return ARG_IGNORE; - } - return ARG_UNUSED; -} - -COMMAND(ls) { - - int start, i; - bool titled; - - flags.hidden = false; - flags.more_info = false; - flags.hide_dot = false; - flags.one_column = false; - flags.recurse = false; - flags.colored = NO; - - start = parse_args(argc, argv, help, short_arg, long_arg); - - if (argc - start == 0) { - list_directory("."); - return EXIT_SUCCESS; - } - - list_file_args(start, argc, argv); - - titled = argc - start > 1; - for (i = start; i < argc; i++) { - - if (!is_dir(argv[i])) continue; - - if (titled && !flags.recurse) { - if (flags.colored != NO) { - printf("\n%s%s:%s\n", DIR_COLOR, argv[i], FILE_COLOR); - } else { - printf("\n%s:\n", argv[i]); - } - } - - list_directory(argv[i]); - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/mkdir.c b/src/commands/mkdir.c deleted file mode 100644 index 02fccca..0000000 --- a/src/commands/mkdir.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "../command.h" - -static struct { - bool make_parent; - mode_t mode; -} flags; - -static int short_arg(char c, char* next) { - switch (c) { - case 'p': - flags.make_parent = true; - break; - case 'm': - check_arg(next); - flags.mode = get_mode(next); - return ARG_USED; - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -static void help(void) { - printf("Usage: mkdir [-m MODE] [-p] DIRECTORY...\n\n"); - printf("Create DIRECTORY\n\n"); - printf("\t-m\tMODE\n"); - printf("\t-p\tNo error if exists; make parent directories as needed\n"); -} - -static bool mkdir_parents(char* path) { - size_t i; - for (i = 1; i < strlen(path); i++) { - if (path[i] != '/') continue; - path[i] = '\0'; - if (mkdir(path, flags.mode) < 0 && errno != EEXIST) { - error_s("failed to create directory '%s': %s", path, strerror(errno)); - return false; - }; - path[i] = '/'; - } - return true; -} - -COMMAND(makedir) { - - int start, i; - - if (argc < 1) global_help(help); - - flags.make_parent = false; - flags.mode = 0755; - - start = parse_args(argc, argv, help, short_arg, NULL); - - for (i = start; i < argc; i++) { - if (flags.make_parent && !mkdir_parents(argv[i])) { - continue; - } - if (mkdir(argv[i], flags.mode) < 0) { - error_s("failed to create directory '%s': %s", argv[i], strerror(errno)); - } - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/mv.c b/src/commands/mv.c deleted file mode 100644 index d203607..0000000 --- a/src/commands/mv.c +++ /dev/null @@ -1,116 +0,0 @@ -#include "../command.h" - -static struct { - bool prompt; - bool dont_overwrite; - bool refuse_if_dir; - bool verbose; -} flags; - -static void help(void) { - printf("Usage: mv [-inT] SOURCE... DIRECTORY\n\n"); - printf("Rename SOURCE to DEST, or move SOURCEs to DIRECTORY\n"); - printf("\t-i\tInteractive, prompt before overwriting\n"); - printf("\t-n\tDon't overwrite an existing file\n"); - printf("\t-T\tRefuse to move if DEST is a directory\n"); - printf("\t-v\tVerbose\n"); -} - -static int short_arg(char c, char* next) { - UNUSED(next); - switch (c) { - case 't': - flags.prompt = true; - break; - case 'n': - flags.dont_overwrite = true; - break; - case 'T': - flags.refuse_if_dir = true; - break; - case 'v': - flags.verbose = true; - break; - default: - return ARG_UNUSED; - } - return ARG_USED; -} - -static void mv_dir(bool exists) { - - char c; - - if (exists && flags.dont_overwrite) { - if (flags.verbose) output("skipping '%s'; overwrise is false", get_path_buffer_2()); - return; - } - - if (exists && flags.prompt) { - fprintf(stderr, "overwrite '%s'? ", get_path_buffer_2()); - fflush(stderr); - - c = getchar(); - if (c != 'y' && c != 'Y') { - if (flags.verbose) output("skipping..."); - return; - } - - } - - if (rename(get_path_buffer(), get_path_buffer_2()) < 0) { - error_s("cannot move '%s': %s", get_path_buffer(), strerror(errno)); - } else if (flags.verbose) { - output("moved '%s'", get_path_buffer()); - } -} - -COMMAND(mv) { - - int start, dest, i; - struct stat s; - - flags.refuse_if_dir = false; - flags.dont_overwrite = false; - flags.prompt = false; - flags.verbose = false; - - start = parse_args(argc, argv, help, short_arg, NULL); - - if (argc - start < 2) { - global_help(help); - } - - push_path_buffer_2(argv[argc-1]); - - dest = true; - if (lstat(get_path_buffer_2(), &s) < 0 && argc - start > 2) { - dest = false; - error("cannot stat '%s': %s", get_path_buffer_2(), strerror(errno)); - } - - if (dest && flags.refuse_if_dir) { - if (S_ISDIR(s.st_mode)) { - error("target '%s': Is A Directory", get_path_buffer_2()); - } - } - - if (argc - start == 2) { - push_path_buffer(argv[argc-2]); - mv_dir(dest); - return EXIT_SUCCESS; - } - - if (dest && !S_ISDIR(s.st_mode)) { - error("target '%s': Is Not A Directory", get_path_buffer_2()); - } - - for (i = start; i < argc - 1; i++) { - int save = push_path_buffer(argv[i]); - bool exists = lstat(get_path_buffer(), &s) >= 0; - mv_dir(exists); - pop_path_buffer(save); - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/printf.c b/src/commands/printf.c deleted file mode 100644 index 519b8a2..0000000 --- a/src/commands/printf.c +++ /dev/null @@ -1,141 +0,0 @@ -#include "../command.h" - -static long cast_long(const char* arg) { - char* end; - long l = strtol(arg, &end, 10); - if (end == arg) { - return 0; - } else { - return l; - } -} - -static double cast_double(const char* arg) { - char* end; - double d = strtod(arg, &end); - if (end == arg) { - return 0.0; - } else { - return d; - } -} - -#define NUMBER(name, type, arg) \ - long l = cast_long(arg); \ - type* t = (type*) &l; \ - type name = *t; - -static void handle_percent(char n, const char* arg) { - switch (n) { - case 'd': - case 'z': { - NUMBER(i, int, arg) - printf("%d", i); - break; - } - case 'u': { - NUMBER(u, unsigned int, arg); - printf("%u", u); - break; - } - case 'f': { - double d = cast_double(arg); - printf("%f", d); - break; - } - case 'c': { - putchar(arg[0]); - break; - } - case 's': { - printf("%s", arg); - break; - } - default: { - putchar('%'); - putchar(n); - } - } -} - -static void handle_slash(char n) { - switch (n) { - case 'n': - putchar('\n'); - break; - case 't': - putchar('\t'); - break; - case 'v': - putchar('\v'); - break; - case 'b': - putchar('\b'); - break; - case 'f': - putchar('\f'); - break; - case 'a': - putchar('\a'); - break; - case '"': - putchar('"'); - break; - case 'c': - exit(EXIT_SUCCESS); - default: - putchar('\\'); - putchar(n); - } -} - -static void help(void) { - printf("Usage printf FORMAT [ARG]...\n\n"); - printf("Format and print ARG(s) according to FORMAT (a-la C prinf)\n"); -} - -COMMAND(print) { - - size_t index; - int arg_index; - char n, *arg; - - if (argc < 1) { - global_help(help); - return EXIT_SUCCESS; - } - - parse_help(argc, argv, help); - - index = 0; - arg_index = 0; - - while (true) { - char c = argv[0][index]; - index++; - - if (c == '\0') break; - if (c != '%' && c != '\\') { - putchar(c); - continue; - } - - n = argv[0][index]; - index++; - - arg = NULL; - if (arg_index < argc) { - arg = argv[arg_index + 1]; - } - - if (c == '%') { - handle_percent(n, arg); - } else { - handle_slash(n); - } - - arg_index++; - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/rm.c b/src/commands/rm.c deleted file mode 100644 index 8ce3e1c..0000000 --- a/src/commands/rm.c +++ /dev/null @@ -1,136 +0,0 @@ -#include "../command.h" -#include -#include - -static struct { - bool force; - bool prompt; - bool verbose; - bool recurse; -} flags; - -static void help(void) { - printf("Usage: rm [-irfv] FILE...\n\n"); - printf("Remove (unlink) FILESs\n\n"); - printf("\t-i\tAlways prompt before removing\n"); - printf("\t-f\tForce, never prompt\n"); - printf("\t-v\tVerbose\n"); - printf("\t-R,-r\tRecurse\n"); -} - -static int short_arg(char c, char* next) { - UNUSED(next); - switch (c) { - case 'i': - flags.prompt = true; - break; - case 'f': - flags.force = true; - break; - case 'v': - flags.verbose = true; - break; - case 'R': - case 'r': - flags.recurse = true; - break; - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -static void rm_file (char* path); - -static bool rm_dir (void) { - DIR* d; - struct dirent* file; - - d = opendir(get_path_buffer()); - if (d == NULL) { - error_s("failed to stat '%s': %s\n", get_path_buffer(), strerror(errno)); - return false; - } - - while ((file = readdir(d)) != NULL) { - if (is_dot_dir(file->d_name)) continue; - rm_file(file->d_name); - } - - closedir(d); - return true; -} - -static void rm_file(char* path) { - int save = push_path_buffer(path); - - struct stat s; - if (lstat(get_path_buffer(), &s) < 0) { - pop_path_buffer(save); - error_s("failed to stat '%s': %s\n", get_path_buffer(), strerror(errno)); - return; - } - - if (S_ISDIR(s.st_mode)) { - if (!flags.force) { - error_s("cannot delete '%s': Is a directory\n", get_path_buffer()); - pop_path_buffer(save); - return; - } - if (flags.recurse && !rm_dir()) { - pop_path_buffer(save); - return; - } - } - - if (flags.prompt) { - char c; - - fprintf(stderr, "delete '%s'? ", get_path_buffer()); - fflush(stderr); - - c = getchar(); - if (c != 'y' && c != 'Y') { - fprintf(stderr, "Skipping...\n"); - pop_path_buffer(save); - return; - } - } - - if (remove(get_path_buffer()) < 0) { - error_s("failed to delete '%s': %s\n", get_path_buffer(), strerror(errno)); - } else if (flags.verbose) { - output("deleted '%s'\n", get_path_buffer()); - } - - pop_path_buffer(save); -} - -COMMAND(rm) { - - int start, i; - - if (argc < 1) { - global_help(help); - return EXIT_SUCCESS; - } - - flags.prompt = false; - flags.force = false; - flags.verbose = false; - flags.recurse = false; - - start = parse_args(argc, argv, help, short_arg, NULL); - -#ifdef FRENCH - if (streql(argv[0], "-fr")) { - printf("\x1b[94mremoving \x1b[97mthe \x1b[91mfrench \x1b[93m(baguette noises)\x1b[0m\n"); - } -#endif - - for (i = start; i < argc; i++) { - rm_file(argv[i]); - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/tac.c b/src/commands/tac.c deleted file mode 100644 index d188de9..0000000 --- a/src/commands/tac.c +++ /dev/null @@ -1,117 +0,0 @@ -#include "../command.h" - -#include -#include -#include - -static void help(void) { - printf("Usage: tac [FILE]...\n\n"); - printf("Concatenate FILEs and print them in reverse\n"); -} - -static void print_range(FILE* file, int start, int end) { - int len, i; - - len = end - start; - fseek(file, start, SEEK_SET); - - for (i = 0; i < len; i++) { - putchar(getc(file)); - } - - fflush(stdout); -} - -static char stdin_path[PATH_MAX]; - -static FILE* read_stdin (void) { - static bool read; - static FILE* file; - int r; - char c; - - if (read) goto finished; - read = true; - - srand(time(NULL)); - - r = rand() % 1000000; - - sprintf(stdin_path, "/tmp/%d.tac", r); - file = get_file(stdin_path, "w"); - - while((c = getchar()) != EOF) putc(c, file); - fclose(file); - - file = get_file(stdin_path, "r"); - -finished: - return file; -} - -static void parse_file(FILE* file, struct Stack* stack) { - char buf[1024], c; - int read, i; - int total = 1; - - stack_push_int(stack, 0); - rewind(file); - while ((read = fread(buf, 1, 1024, file)) > 0) { - for (i = 0; i < read; i++) { - c = buf[i]; - if (c != '\n') continue; - stack_push_int(stack, total + i); - } - total += read; - } -} - -static void tac_file(FILE* file) { - struct Stack stack; - int last, current; - - stack_init(&stack, 80); - parse_file(file, &stack); - rewind(file); - - if (!stack_pop_int(&stack, &last)) goto cleanup; - - while(stack_pop_int(&stack, ¤t)) { - print_range(file, current, last); - last = current; - } - -cleanup: - - stack_free(&stack); -} - -COMMAND(tac) { - - FILE* in; - int i; - - parse_help(argc, argv, help); - - in = read_stdin(); - - if (argc < 1) { - tac_file(in); - return EXIT_SUCCESS; - } - - for (i = 0; i < argc; i++) { - FILE* file = get_file(argv[i], "r"); - if (file == stdin) { - tac_file(in); - } else { - tac_file(file); - fclose(file); - } - } - - fclose(in); - remove(stdin_path); - - return EXIT_SUCCESS; -} diff --git a/src/commands/tail.c b/src/commands/tail.c deleted file mode 100644 index 07b3d2b..0000000 --- a/src/commands/tail.c +++ /dev/null @@ -1,238 +0,0 @@ -#include "../command.h" - -static struct { - bool lines; - int count; - bool print_headers; - bool dont_print_headers; - bool print_as_grow; - int grow_wait; -} flags; - -static size_t tail_file_lines(FILE* file, unsigned int count, size_t skip) { - - char** ring; - int* ring_len; - int index, read; - unsigned int size, i; - size_t len; - char* line; - - ring = malloc(sizeof(char*) * count); - memset(ring, 0, sizeof(char*) * count); - - ring_len = malloc(sizeof(int) * count); - - index = 0; - size = 0; - - fseek(file, skip, SEEK_SET); - - len = skip; - line = NULL; - - while ((read = getline(&line, &len, file)) != -1) { - - if (ring[index] != NULL) free(ring[index]); - ring[index] = line; - ring_len[index] = read; - - index++; - index %= count; - if (size < count) size++; - - line = NULL; - } - - index += count - size; - index %= count; - - for (i = 0; i < size; i++) { - fwrite(ring[index], ring_len[index], 1, stdout); - free(ring[index]); - index += 1; - index %= count; - } - - free(line); - fclose(file); - free(ring); - free(ring_len); - - return len; -} - -static size_t tail_file_chars(FILE* file, unsigned int count, size_t skip) { - - char* ring; - int index; - unsigned int size, i; - int read, c; - - ring = malloc(sizeof(char) * count); - memset(ring, 0, count); - - index = 0; - size = 0; - - fseek(file, skip, SEEK_SET); - read = skip; - - while((c = getc(file)) != EOF) { - ring[index] = c; - index++; - read++; - index %= count; - if (size < count) size++; - } - - index += count - size; - index %= count; - - for (i = 0; i < size; i++) { - putchar(ring[index]); - index += 1; - index %= count; - } - - fclose(file); - - return read; -} - -static void help(void) { - printf("Usage: tail [OPTIONS] [FILE]...\n\n"); - printf("Print last 10 lines of FILEs (or stdin) to.\n"); - printf("With more than one FILE, precede each with a filename header.\n\n"); - printf("\t-c [+]N[bkm]\tPrint last N bytes\n"); - printf("\t-n N[bkm]\tPrint last N lines\n"); - printf("\t\t\t(b:*512 k:*1024 m:*1024^2)\n"); - printf("\t-q\t\tNever print headers\n"); - printf("\t-v\t\tAlways print headers\n"); - printf("\t-f\t\tPrint data as file grows\n"); - printf("\t-s SECONDS\tWait SECONDS between reads with -f\n"); - exit(EXIT_SUCCESS); -} - -static void print_header(char* path, bool many) { - if (flags.dont_print_headers) return; - if (!many && !flags.print_headers) return; - if (streql("-", path)) { - printf("\n==> standard input <==\n"); - } else { - printf("\n=>> %s <==\n", path); - } -} - -static void tail_file(char* path, bool many) { - - FILE* file; - size_t skip; - - file = get_file(path, "r"); - print_header(path, many); - - skip = 0; - while (true) { - if (flags.lines) { - skip = tail_file_lines(file, flags.count, skip); - } else { - skip = tail_file_chars(file, flags.count, skip); - } - if (!flags.print_as_grow) break; - sleep(flags.grow_wait); - get_file(path, "r"); - }; -} - -static int short_arg(char c, char* next) { - switch (c) { - case 'c': { - long int bkm; - - flags.lines = false; - - check_arg(next); - bkm = get_blkm(next); - - if (bkm < 1) { - error("bkm cannot be less than 1"); - } - - flags.count = bkm; - return ARG_USED; - } - case 'n': { - long int bkm; - - flags.lines = true; - - check_arg(next); - bkm = get_blkm(next); - - if (bkm < 1) { - error("bkm cannot be less than 1"); - } - - flags.count = bkm; - return ARG_USED; - } - case 'q': - flags.dont_print_headers = true; - break; - case 'v': - flags.print_headers = true; - break; - case 'f': - flags.print_as_grow = true; - break; - case 's': { - long int sec; - - check_arg(next); - sec = get_number(next); - - if (sec < 1) { - error("wait seconds cannot be less than 1"); - } - - flags.grow_wait = sec; - return ARG_USED; - } - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -COMMAND(tail) { - - int start, count, i; - - flags.count = 10; - flags.dont_print_headers = false; - flags.print_headers = false; - flags.lines = true; - flags.print_as_grow = false; - flags.grow_wait = 10; - - start = parse_args(argc, argv, help, short_arg, NULL); - - count = argc - start; - - if (count < 1) { - tail_file_lines(stdin, 10, 0); - return EXIT_SUCCESS; - } - - if (count == 1) { - tail_file(argv[start], false); - return EXIT_SUCCESS; - } - - for (i = 0; i < count; i++) { - tail_file(argv[start + i], true); - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/tee.c b/src/commands/tee.c deleted file mode 100644 index b9b31be..0000000 --- a/src/commands/tee.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "../command.h" - -#include - -static struct { - bool append; - bool handle_sigint; -} flags; - -static void help(void) { - printf("Usage: tee [-ai] [FILE]...\n\n"); - printf("Copy stdin to each FILE, and also to stdout\n\n"); - printf("\t-a Append to the given FILEs, don't overwrite\n"); - printf("\t-i Ignore interrupt signals (SIGINT)\n"); - exit(EXIT_SUCCESS); -} - -static void handle(int dummy){UNUSED(dummy);} - -static void run_tee(int file_count, FILE** files) { - char c; - int i; - - while((c = getchar()) != EOF) { - int i; - for (i = 0; i < file_count; i++) { - fwrite(&c, 1, 1, files[i]); - fflush(files[i]); - } - putchar(c); - } - - for (i = 0; i < file_count; i++) { - fclose(files[i]); - } -} - -static int short_arg(char c, char* next) { - UNUSED(next); - switch (c) { - case 'a': - flags.append = true; - break; - case 'i': - flags.handle_sigint = true; - break; - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -COMMAND(tee) { - - int start, i; - FILE** files; - - flags.append = false; - flags.handle_sigint = false; - - start = parse_args(argc, argv, help, short_arg, NULL); - - if (flags.handle_sigint) { - signal(SIGINT, handle); - } - - if (argc - start < 1) { - run_tee(0, NULL); - return EXIT_SUCCESS; - } - - files = malloc(sizeof(FILE*) * (argc - start)); - - for (i = start; i < argc; i++) { - FILE* file = get_file(argv[i], flags.append ? "a" : "w"); - files[i - start] = file; - } - - run_tee(argc - start, files); - return EXIT_SUCCESS; -} diff --git a/src/commands/wc.c b/src/commands/wc.c deleted file mode 100644 index d8905a5..0000000 --- a/src/commands/wc.c +++ /dev/null @@ -1,159 +0,0 @@ -#include "../command.h" - -#include - -static struct { - bool newlines; - bool words; - bool characters; - bool bytes; - bool longest_line; - bool has_flags; -} flags; - -static int lines = 0; -static int words = 0; -static int chars = 0; -static int bytes = 0; -static int logst = 0; - -static void list(int l, int w, int c, int b, int lg) { - if (flags.newlines) { - printf("\t%d", l); - } - if (flags.words) { - printf("\t%d", w); - } - if (flags.characters) { - printf("\t%d", c); - } - if (flags.bytes) { - printf("\t%d", b); - } - if (flags.longest_line) { - printf("\t%d", lg); - } -} - -#define BS 1024 - -static bool is_delimiter(char c) { - return c == ' ' || c == '\t' || c == '\n'; -} - -static void run_wc(FILE* file) { - int l = 0, w = 0, c = 0, b = 0, lg = 0; - - bool in_word = false; - int current_length = 0; - - int read; - char buf[BS]; - while ((read = fread(buf, 1, 1024, file)) > 0) { - int i; - for (i = 0; i < read; i++) { - char ch = buf[i]; - b++; - if (ch == '\n') { - l++; - lg = lg > current_length ? lg : current_length; - current_length = 0; - } else { - current_length++; - } - if (isprint(ch) || is_delimiter(ch)) c++; - if (in_word && is_delimiter(ch)) { - in_word = false; - w++; - } else if (!in_word && !is_delimiter(ch) && isprint(ch)) { - in_word = true; - } - } - } - if (in_word) w++; - lg = lg > current_length ? lg : current_length; - list(l, w, c, b, lg); - lines += l; - words += w; - chars += c; - bytes += b; - logst += lg; - if (file != stdin) - fclose(file); -} - -static void help(void) { - printf("Usage: wc [-cmlwL] [FILE]...\n\n"); - printf("Count lines, words, and bytes for FILEs (or stdin)\n\n"); - printf("\t-c Count bytes\n"); - printf("\t-m Count characters\n"); - printf("\t-l Count newlines\n"); - printf("\t-w Count words\n"); - printf("\t-L Print longest line length\n"); - exit(EXIT_SUCCESS); -} - -static int short_arg(char c, char* next) { - UNUSED(next); - switch (c) { - case 'c': - flags.bytes = true; - break; - case 'm': - flags.characters = true; - break; - case 'l': - flags.newlines = true; - break; - case 'w': - flags.words = true; - break; - case 'L': - flags.longest_line = true; - break; - default: - return ARG_INVALID; - } - flags.has_flags = true; - return ARG_UNUSED; -} - -COMMAND(wc) { - - int start, i; - - flags.newlines = false; - flags.words = false; - flags.characters = false; - flags.bytes = false; - flags.longest_line = false; - flags.has_flags = false; - - - start = parse_args(argc, argv, help, short_arg, NULL); - - if (!flags.has_flags) { - flags.newlines = true; - flags.words = true; - flags.characters = true; - } - - if (argc - start < 1) { - run_wc(stdin); - printf("\n"); - return EXIT_SUCCESS; - } - - for (i = start; i < argc; i++) { - FILE* file = get_file(argv[i], "r"); - run_wc(file); - printf("\t%s\n", argv[i]); - } - - if (argc - start > 1) { - list(lines, words, chars, bytes, logst); - printf("\ttotal\n"); - } - - return EXIT_SUCCESS; -} diff --git a/src/commands/whoami.c b/src/commands/whoami.c deleted file mode 100644 index 7fd7c85..0000000 --- a/src/commands/whoami.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "../command.h" - -#include - -static void help(void) { - printf("Usage: whoami\n\n"); - printf("Print the username associated with the current effective user id\n"); - exit(EXIT_SUCCESS); -} - -COMMAND(whoami) { - - uid_t usr; - struct passwd* passwd; - - parse_help(argc, argv, help); - - usr = getuid(); - passwd = getpwuid(usr); - - if (passwd == NULL) { - printf("\x1b[1;91myou do not exist.\n"); - } else { - printf("%s\n", passwd->pw_name); - } - return EXIT_SUCCESS; -} diff --git a/src/commands/xargs.c b/src/commands/xargs.c deleted file mode 100644 index 3008c3c..0000000 --- a/src/commands/xargs.c +++ /dev/null @@ -1,187 +0,0 @@ -#include "../command.h" -#include -#include - -static struct { - bool null_seperated; - bool ignore_empty; - bool print_command; - bool prompt_command; - int max_args; - FILE* file; -} flags; - -static void help(void) { - printf("Usage: xargs [OPTIONS] [PROG ARGS]\n\n"); - printf("Run PROG on every item given by stdin\n\n"); - printf("\t-0\tInput is separated by NULs\n"); - printf("\t-a FILE\tRead from FILE instead of stdin\n"); - printf("\t-r\tDon't run command if input is empty\n"); - printf("\t-t\tPrint the command on stderr before execution\n"); - printf("\t-p\tAsk user whether to run each command\n"); - printf("\t-n N\tPass no more than N args to PROG\n"); -} - -static int short_arg(char c, char* next) { - UNUSED(next); - - switch (c) { - case '0': - flags.null_seperated = true; - break; - case 'a': - check_arg(next); - flags.file = get_file(next, "r"); - return ARG_USED; - case 'r': - flags.ignore_empty = true; - break; - case 't': - flags.print_command = true; - break; - case 'p': - flags.prompt_command = true; - break; - case 'n': { - long int n; - - check_arg(next); - n = get_number(next); - - if (n < 1) { - error("max arg count must be at least 1"); - } - - flags.max_args = n; - return ARG_USED; - } - default: - return ARG_INVALID; - } - return ARG_UNUSED; -} - -char* read_next(FILE* file, int arg_count) { - - int size, capacity; - char* buf; - char c; - - if (flags.max_args != -1 && arg_count == flags.max_args) return NULL; - - size = 0; - capacity = 8; - buf = malloc(sizeof(char) * capacity); - - while(c = getc(file), true) { - if (c == EOF && size == 0) { - free(buf); - return NULL; - } - - if (size == capacity) { - capacity *= 2; - buf = realloc(buf, sizeof(char) * capacity); - } - - if (c == '\0' || c == EOF || (!flags.null_seperated && c == '\n')) { - buf[size++] = '\0'; - return buf; - } else { - buf[size++] = c; - } - } -} - -void read_args(FILE* file, char*** args, int* size, int* capacity) { - char* arg; - static int read = 0; - while (arg = read_next(file, read), true) { - if (*size == *capacity) { - *capacity *= 2; - *args = realloc(*args, sizeof(char*) * *capacity); - } - (*args)[(*size)++] = arg; - read++; - if (arg == NULL) break; - } -} - -COMMAND(xargs) { - - int start, arg_start, arg_on_stack_count; - int size, capacity, i; - char* command; - char** args; - - flags.null_seperated = false; - flags.ignore_empty = false; - flags.print_command = false; - flags.print_command = false; - flags.max_args = -1; - flags.file = stdin; - - start = parse_args(argc, argv, help, short_arg, NULL); - - if (start >= argc) { - command = "echo"; - } else { - command = argv[start]; - } - - arg_start = start + 1; - - if (arg_start >= argc) { - arg_on_stack_count = 0; - arg_start = argc - 1; - } else { - arg_on_stack_count = argc - arg_start; - } - - size = arg_on_stack_count + 1; - capacity = size + 8; - - args = malloc(sizeof(char*) * capacity); - args[0] = command; - memcpy(&args[1], &argv[arg_start], arg_on_stack_count * sizeof(char*)); - read_args(flags.file, &args, &size, &capacity); - - if (flags.ignore_empty && size < 2) goto cleanup; - - if (flags.prompt_command || flags.print_command) { - for (i = 0; i < size - 1; i++) { - fprintf(stderr, "%s ", args[i]); - } - fprintf(stderr, "\b\n"); - } - - if (flags.prompt_command) { - FILE* in; - char c; - - fprintf(stderr, "Run command? "); - fflush(stderr); - - in = get_tty_stream("r"); - c = getc(in); - fclose(in); - - if (c != 'y' && c != 'Y') { - fprintf(stderr, "Skipping...\n"); - goto cleanup; - } - } - - if (execvp(command, args) == -1) { - error("error: failed to execute command: %s", strerror(errno)); - } - -cleanup: - - for (i = arg_on_stack_count + 1; i < size - 1; i++) { - free(args[i]); - } - fclose(flags.file); - - return EXIT_SUCCESS; -} diff --git a/src/commands/yes.c b/src/commands/yes.c deleted file mode 100644 index 6a44789..0000000 --- a/src/commands/yes.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "../command.h" - -static void help(void) { - printf("Usage: yes [STRING]\n\n"); - printf("Repeatedly output a line with all specified STRING(s), or 'y'.\n"); -} - -COMMAND(yes) { - const char* repeat; - int i; - - parse_help(argc, argv, help); - - if (argc == 0) { - repeat = "y"; - } else { - repeat = argv[0]; - for (i = 1; i < argc; i++) { - *(argv[i]-1) = ' '; - } - } - - while (true) { - printf("%s\n", repeat); - } -} diff --git a/src/main.c b/src/main.c index 1977e17..ad64b10 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,8 @@ -#include "util/shared.h" #include "command.h" +#include "lslib.h" -#include #include #include -#include -#include -#include char* cmd; @@ -51,9 +47,9 @@ int main (ARGUMENTS) { } else if (streql(cmd, "printf")) { return print(NEXT_ARGS); } else if (streql(cmd, "groups")) { - return groups(); + return groups(NEXT_ARGS); } else if (streql(cmd, "id")) { - return user_id(); + return user_id(NEXT_ARGS); } else if (streql(cmd, "ls") || streql(cmd, "dir")) { return ls(NEXT_ARGS); } else if (streql(cmd, "lsd")) { diff --git a/src/util/regex.c b/src/util/regex.c deleted file mode 100644 index 0e0dc52..0000000 --- a/src/util/regex.c +++ /dev/null @@ -1,528 +0,0 @@ -/* - * - * Mini regex-module inspired by Rob Pike's regex code described in: - * - * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html - * - * - * - * Supports: - * --------- - * '.' Dot, matches any character - * '^' Start anchor, matches beginning of string - * '$' End anchor, matches end of string - * '*' Asterisk, match zero or more (greedy) - * '+' Plus, match one or more (greedy) - * '?' Question, match zero or one (non-greedy) - * '[abc]' Character class, match if one of {'a', 'b', 'c'} - * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! - * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } - * '\s' Whitespace, \t \f \r \n \v and spaces - * '\S' Non-whitespace - * '\w' Alphanumeric, [a-zA-Z0-9_] - * '\W' Non-alphanumeric - * '\d' Digits, [0-9] - * '\D' Non-digits - * - * - */ - - - -#include "regex.h" -#include -#include - -/* Definitions: */ - -#define MAX_REGEXP_OBJECTS 30 /* Max number of regex symbols in expression. */ -#define MAX_CHAR_CLASS_LEN 40 /* Max length of character-class buffer in. */ - - -enum { UNUSED, DOT, BEGIN, END, QUESTIONMARK, STAR, PLUS, CHAR, CHAR_CLASS, INV_CHAR_CLASS, DIGIT, NOT_DIGIT, ALPHA, NOT_ALPHA, WHITESPACE, NOT_WHITESPACE}; - -typedef struct regex_t -{ - unsigned char type; /* CHAR, STAR, etc. */ - union - { - unsigned char ch; /* the character itself */ - unsigned char* ccl; /* OR a pointer to characters in class */ - } u; -} regex_t; - - - -/* Private function declarations: */ -static int matchpattern(regex_t* pattern, const char* text, int* matchlength); -static int matchcharclass(char c, const char* str); -static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength); -static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength); -static int matchone(regex_t p, char c); -static int matchdigit(char c); -static int matchalpha(char c); -static int matchwhitespace(char c); -static int matchmetachar(char c, const char* str); -static int matchrange(char c, const char* str); -static int matchdot(char c); -static int ismetachar(char c); - - - -/* Public functions: */ -int re_match(const char* pattern, const char* text, int* matchlength) -{ - return re_matchp(re_compile(pattern), text, matchlength); -} - -int re_matchp(re_t pattern, const char* text, int* matchlength) -{ - *matchlength = 0; - if (pattern != 0) - { - if (pattern[0].type == BEGIN) - { - return ((matchpattern(&pattern[1], text, matchlength)) ? 0 : -1); - } - else - { - int idx = -1; - - do - { - idx += 1; - - if (matchpattern(pattern, text, matchlength)) - { - if (text[0] == '\0') - return -1; - - return idx; - } - } - while (*text++ != '\0'); - } - } - return -1; -} - -re_t re_compile(const char* pattern) -{ - /* The sizes of the two static arrays below substantiates the static RAM usage of this module. - MAX_REGEXP_OBJECTS is the max number of symbols in the expression. - MAX_CHAR_CLASS_LEN determines the size of buffer for chars in all char-classes in the expression. */ - static regex_t re_compiled[MAX_REGEXP_OBJECTS]; - static unsigned char ccl_buf[MAX_CHAR_CLASS_LEN]; - int ccl_bufidx = 1; - - char c; /* current char in pattern */ - int i = 0; /* index into pattern */ - int j = 0; /* index into re_compiled */ - - while (pattern[i] != '\0' && (j+1 < MAX_REGEXP_OBJECTS)) - { - c = pattern[i]; - - switch (c) - { - /* Meta-characters: */ - case '^': { re_compiled[j].type = BEGIN; } break; - case '$': { re_compiled[j].type = END; } break; - case '.': { re_compiled[j].type = DOT; } break; - case '*': { re_compiled[j].type = STAR; } break; - case '+': { re_compiled[j].type = PLUS; } break; - case '?': { re_compiled[j].type = QUESTIONMARK; } break; -/* case '|': { re_compiled[j].type = BRANCH; } break; <-- not working properly */ - - /* Escaped character-classes (\s \w ...): */ - case '\\': - { - if (pattern[i+1] != '\0') - { - /* Skip the escape-char '\\' */ - i += 1; - /* ... and check the next */ - switch (pattern[i]) - { - /* Meta-character: */ - case 'd': { re_compiled[j].type = DIGIT; } break; - case 'D': { re_compiled[j].type = NOT_DIGIT; } break; - case 'w': { re_compiled[j].type = ALPHA; } break; - case 'W': { re_compiled[j].type = NOT_ALPHA; } break; - case 's': { re_compiled[j].type = WHITESPACE; } break; - case 'S': { re_compiled[j].type = NOT_WHITESPACE; } break; - - /* Escaped character, e.g. '.' or '$' */ - default: - { - re_compiled[j].type = CHAR; - re_compiled[j].u.ch = pattern[i]; - } break; - } - } - /* '\\' as last char in pattern -> invalid regular expression. */ -/* - else - { - re_compiled[j].type = CHAR; - re_compiled[j].ch = pattern[i]; - } -*/ - } break; - - /* Character class: */ - case '[': - { - /* Remember where the char-buffer starts. */ - int buf_begin = ccl_bufidx; - - /* Look-ahead to determine if negated */ - if (pattern[i+1] == '^') - { - re_compiled[j].type = INV_CHAR_CLASS; - i += 1; /* Increment i to avoid including '^' in the char-buffer */ - if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '^' */ - { - return 0; - } - } - else - { - re_compiled[j].type = CHAR_CLASS; - } - - /* Copy characters inside [..] to buffer */ - while ( (pattern[++i] != ']') - && (pattern[i] != '\0')) /* Missing ] */ - { - if (pattern[i] == '\\') - { - if (ccl_bufidx >= MAX_CHAR_CLASS_LEN - 1) - { - /* fputs("exceeded internal buffer!\n", stderr); */ - return 0; - } - if (pattern[i+1] == 0) /* incomplete pattern, missing non-zero char after '\\' */ - { - return 0; - } - ccl_buf[ccl_bufidx++] = pattern[i++]; - } - else if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) - { - /* fputs("exceeded internal buffer!\n", stderr); */ - return 0; - } - ccl_buf[ccl_bufidx++] = pattern[i]; - } - if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) - { - /* Catches cases such as [00000000000000000000000000000000000000][ */ - /* fputs("exceeded internal buffer!\n", stderr); */ - return 0; - } - /* Null-terminate string end */ - ccl_buf[ccl_bufidx++] = 0; - re_compiled[j].u.ccl = &ccl_buf[buf_begin]; - } break; - - /* Other characters: */ - default: - { - re_compiled[j].type = CHAR; - re_compiled[j].u.ch = c; - } break; - } - /* no buffer-out-of-bounds access on invalid patterns - see https://github.com/kokke/tiny-regex-c/commit/1a279e04014b70b0695fba559a7c05d55e6ee90b */ - if (pattern[i] == 0) - { - return 0; - } - - i += 1; - j += 1; - } - /* 'UNUSED' is a sentinel used to indicate end-of-pattern */ - re_compiled[j].type = UNUSED; - - return (re_t) re_compiled; -} - -void re_print(regex_t* pattern) -{ - const char* types[] = { "UNUSED", "DOT", "BEGIN", "END", "QUESTIONMARK", "STAR", "PLUS", "CHAR", "CHAR_CLASS", "INV_CHAR_CLASS", "DIGIT", "NOT_DIGIT", "ALPHA", "NOT_ALPHA", "WHITESPACE", "NOT_WHITESPACE", "BRANCH" }; - - int i; - int j; - char c; - for (i = 0; i < MAX_REGEXP_OBJECTS; ++i) - { - if (pattern[i].type == UNUSED) - { - break; - } - - printf("type: %s", types[pattern[i].type]); - if (pattern[i].type == CHAR_CLASS || pattern[i].type == INV_CHAR_CLASS) - { - printf(" ["); - for (j = 0; j < MAX_CHAR_CLASS_LEN; ++j) - { - c = pattern[i].u.ccl[j]; - if ((c == '\0') || (c == ']')) - { - break; - } - printf("%c", c); - } - printf("]"); - } - else if (pattern[i].type == CHAR) - { - printf(" '%c'", pattern[i].u.ch); - } - printf("\n"); - } -} - - - -/* Private functions: */ -static int matchdigit(char c) -{ - return isdigit(c); -} -static int matchalpha(char c) -{ - return isalpha(c); -} -static int matchwhitespace(char c) -{ - return isspace(c); -} -static int matchalphanum(char c) -{ - return ((c == '_') || matchalpha(c) || matchdigit(c)); -} -static int matchrange(char c, const char* str) -{ - return ( (c != '-') - && (str[0] != '\0') - && (str[0] != '-') - && (str[1] == '-') - && (str[2] != '\0') - && ( (c >= str[0]) - && (c <= str[2]))); -} -static int matchdot(char c) -{ -#if defined(RE_DOT_MATCHES_NEWLINE) && (RE_DOT_MATCHES_NEWLINE == 1) - (void)c; - return 1; -#else - return c != '\n' && c != '\r'; -#endif -} -static int ismetachar(char c) -{ - return ((c == 's') || (c == 'S') || (c == 'w') || (c == 'W') || (c == 'd') || (c == 'D')); -} - -static int matchmetachar(char c, const char* str) -{ - switch (str[0]) - { - case 'd': return matchdigit(c); - case 'D': return !matchdigit(c); - case 'w': return matchalphanum(c); - case 'W': return !matchalphanum(c); - case 's': return matchwhitespace(c); - case 'S': return !matchwhitespace(c); - default: return (c == str[0]); - } -} - -static int matchcharclass(char c, const char* str) -{ - do - { - if (matchrange(c, str)) - { - return 1; - } - else if (str[0] == '\\') - { - /* Escape-char: increment str-ptr and match on next char */ - str += 1; - if (matchmetachar(c, str)) - { - return 1; - } - else if ((c == str[0]) && !ismetachar(c)) - { - return 1; - } - } - else if (c == str[0]) - { - if (c == '-') - { - return ((str[-1] == '\0') || (str[1] == '\0')); - } - else - { - return 1; - } - } - } - while (*str++ != '\0'); - - return 0; -} - -static int matchone(regex_t p, char c) -{ - switch (p.type) - { - case DOT: return matchdot(c); - case CHAR_CLASS: return matchcharclass(c, (const char*)p.u.ccl); - case INV_CHAR_CLASS: return !matchcharclass(c, (const char*)p.u.ccl); - case DIGIT: return matchdigit(c); - case NOT_DIGIT: return !matchdigit(c); - case ALPHA: return matchalphanum(c); - case NOT_ALPHA: return !matchalphanum(c); - case WHITESPACE: return matchwhitespace(c); - case NOT_WHITESPACE: return !matchwhitespace(c); - default: return (p.u.ch == c); - } -} - -static int matchstar(regex_t p, regex_t* pattern, const char* text, int* matchlength) -{ - int prelen = *matchlength; - const char* prepoint = text; - while ((text[0] != '\0') && matchone(p, *text)) - { - text++; - (*matchlength)++; - } - while (text >= prepoint) - { - if (matchpattern(pattern, text--, matchlength)) - return 1; - (*matchlength)--; - } - - *matchlength = prelen; - return 0; -} - -static int matchplus(regex_t p, regex_t* pattern, const char* text, int* matchlength) -{ - const char* prepoint = text; - while ((text[0] != '\0') && matchone(p, *text)) - { - text++; - (*matchlength)++; - } - while (text > prepoint) - { - if (matchpattern(pattern, text--, matchlength)) - return 1; - (*matchlength)--; - } - - return 0; -} - -static int matchquestion(regex_t p, regex_t* pattern, const char* text, int* matchlength) -{ - if (p.type == UNUSED) - return 1; - if (matchpattern(pattern, text, matchlength)) - return 1; - if (*text && matchone(p, *text++)) - { - if (matchpattern(pattern, text, matchlength)) - { - (*matchlength)++; - return 1; - } - } - return 0; -} - - -#if 0 - -/* Recursive matching */ -static int matchpattern(regex_t* pattern, const char* text, int *matchlength) -{ - int pre = *matchlength; - if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) - { - return matchquestion(pattern[1], &pattern[2], text, matchlength); - } - else if (pattern[1].type == STAR) - { - return matchstar(pattern[0], &pattern[2], text, matchlength); - } - else if (pattern[1].type == PLUS) - { - return matchplus(pattern[0], &pattern[2], text, matchlength); - } - else if ((pattern[0].type == END) && pattern[1].type == UNUSED) - { - return text[0] == '\0'; - } - else if ((text[0] != '\0') && matchone(pattern[0], text[0])) - { - (*matchlength)++; - return matchpattern(&pattern[1], text+1); - } - else - { - *matchlength = pre; - return 0; - } -} - -#else - -/* Iterative matching */ -static int matchpattern(regex_t* pattern, const char* text, int* matchlength) -{ - int pre = *matchlength; - do - { - if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) - { - return matchquestion(pattern[0], &pattern[2], text, matchlength); - } - else if (pattern[1].type == STAR) - { - return matchstar(pattern[0], &pattern[2], text, matchlength); - } - else if (pattern[1].type == PLUS) - { - return matchplus(pattern[0], &pattern[2], text, matchlength); - } - else if ((pattern[0].type == END) && pattern[1].type == UNUSED) - { - return (text[0] == '\0'); - } -/* Branching is not working properly - else if (pattern[1].type == BRANCH) - { - return (matchpattern(pattern, text) || matchpattern(&pattern[2], text)); - } -*/ - (*matchlength)++; - } - while ((text[0] != '\0') && matchone(*pattern++, *text++)); - - *matchlength = pre; - return 0; -} - -#endif diff --git a/src/util/regex.h b/src/util/regex.h deleted file mode 100644 index 69facc6..0000000 --- a/src/util/regex.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Mini regex-module inspired by Rob Pike's regex code described in: - * - * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html - * - * - * - * Supports: - * --------- - * '.' Dot, matches any character - * '^' Start anchor, matches beginning of string - * '$' End anchor, matches end of string - * '*' Asterisk, match zero or more (greedy) - * '+' Plus, match one or more (greedy) - * '?' Question, match zero or one (non-greedy) - * '[abc]' Character class, match if one of {'a', 'b', 'c'} - * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! - * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } - * '\s' Whitespace, \t \f \r \n \v and spaces - * '\S' Non-whitespace - * '\w' Alphanumeric, [a-zA-Z0-9_] - * '\W' Non-alphanumeric - * '\d' Digits, [0-9] - * '\D' Non-digits - * - * - */ - -#ifndef _TINY_REGEX_C -#define _TINY_REGEX_C - - -#ifndef RE_DOT_MATCHES_NEWLINE -/* Define to 0 if you DON'T want '.' to match '\r' + '\n' */ -#define RE_DOT_MATCHES_NEWLINE 1 -#endif - -#ifdef __cplusplus -extern "C"{ -#endif - - - -/* Typedef'd pointer to get abstract datatype. */ -typedef struct regex_t* re_t; - - -/* Compile regex string pattern to a regex_t-array. */ -re_t re_compile(const char* pattern); - - -/* Find matches of the compiled pattern inside text. */ -int re_matchp(re_t pattern, const char* text, int* matchlength); - - -/* Find matches of the txt pattern inside text (will compile automatically first). */ -int re_match(const char* pattern, const char* text, int* matchlength); - - -#ifdef __cplusplus -} -#endif - -#endif /* ifndef _TINY_REGEX_C */ diff --git a/src/util/shared.c b/src/util/shared.c deleted file mode 100644 index 2e0c98a..0000000 --- a/src/util/shared.c +++ /dev/null @@ -1,372 +0,0 @@ -#include "shared.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern char* cmd; - -void error_s(const char *format, ...) { - va_list list; - va_start(list, format); - - fprintf(stderr, "%s: ", cmd); - vfprintf(stderr, format, list); - fprintf(stderr, "\n"); -} - -void error(const char *format, ...) { - va_list list; - va_start(list, format); - - fprintf(stderr, "%s: ", cmd); - vfprintf(stderr, format, list); - fprintf(stderr, "\n"); - exit(EXIT_FAILURE); -} - -void output(const char *format, ...) { - va_list list; - va_start(list, format); - - fprintf(stdout, "%s: ", cmd); - vfprintf(stdout, format, list); - fprintf(stdout, "\n"); -} - -FILE* get_file_s(const char* path, const char* type) { - struct stat s; - FILE* file; - - if (streql("-", path) && type[0] == 'r') { - clearerr(stdin); - fflush(stdin); - return stdin; - } - - if (lstat(path, &s) < 0) { - if (type[0] != 'r') goto read; - error_s("failed to read %s: %s", path, strerror(errno)); - return NULL; - } - - if (S_ISDIR(s.st_mode)) { - error_s("%s is a directory", path); - return NULL; - } - -read: - - file = fopen(path, type); - - if (file == NULL) { - error_s("failed to %s file %s: %s", type[0] == 'r' ? "read" : "write", path, strerror(errno)); - } - - return file; -} - -FILE* get_file(const char* path, const char* type) { - FILE* file = get_file_s(path, type); - if (file == NULL) exit(EXIT_FAILURE); - return file; -} - -long int get_number(const char* text) { - char* end; - long int n = strtol(text, &end, 10); - if (text == end) { - error("%s is not a valid number", text); - } - return n; -} - -long int get_blkm(const char* text) { - char* end; - long int n = strtol(text, &end, 10); - if (text == end) { - error("%s is not a valid bkm", text); - } - if (*end == '\0') return n; - switch (*end) { - case 'K': - case 'k': - return n * 1024; - case 'B': - case 'b': - return n * 512; - case 'M': - case 'm': - return n * 1024 * 1204; - default: - error("invalid bkm type %c", *end); - } - - /* shouldnt get here anyways */ - return 0; -} - -mode_t get_mode(const char* next) { - char* end = NULL; - mode_t mode = (mode_t)strtol(next, &end, 8); - if (!end) return 0; - while(isspace(*end)) end++; - if (*end != '\0' || (unsigned) mode < 010000) { - error("invalid file mode: `%s`", next); - } - return mode; -} - -bool streql(const char* a, const char* b) { - int n = 0; - - if (*a != *b) return false; - - while (true) { - if (*(a+n) != *(b+n)) return false; - if (*(a+n) == '\0') return true; - ++n; - } -} - -bool prefix(const char* pre, const char* str) { - return strncmp(pre, str, strlen(pre)) == 0; -} - -static char fs_types[5] = {'K','M','G','T','P'}; -void print_file_size(size_t bytes, char buf[5]) { - int index, n; - float next; - - index = 0; - next = bytes; - - while (true) { - if (next < 1000) { - break; - } - - if (index == 5) { - printf("999P"); - return; - } - - next /= 1024; - index++; - } - - n = snprintf(buf, 4, "%u", (int)(next+.5)); - - if (index > 0) { - buf[n] = (fs_types[index - 1]); - n++; - } - - buf[n] = '\0'; -} - -static char* months[12] = - {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; -void print_date_time(time_t mills, char buf[13]) { - struct tm* info; - int n; - - info = localtime(&mills); - n = snprintf(buf, 5, "%s ", months[info->tm_mon]); - - if (info->tm_mday < 10) { - buf[n] = ' '; - n++; - } - - snprintf(buf + n, 13 - n, "%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec); -} - -void print_file_path(char* path) { - if (streql("-", path)) { - printf("(standard input)"); - } else { - printf("%s", path); - } -} - -#ifndef MAJOR -#define MAJOR 0 -#endif - -#ifndef MINOR -#define MINOR 0 -#endif - -#ifndef PATCH -#define PATCH 0 -#endif - -void check_arg (char* arg) { - if (arg == NULL) { - error("expected another argument after option"); - } -} - -void global_help(void (*help)(void)) { - printf("LazySphere v%d.%d.%d multi-call binary.\n\n", MAJOR, MINOR, PATCH); - help(); - exit(EXIT_SUCCESS); -} - -void parse_help(int argc, char** argv, void (*help)(void)) { - int i; - - if (argc < 1) return; - - for (i = 0; i < argc; i++) { - if (!prefix("-", argv[i]) || streql("-", argv[i])) break; - if (help != NULL && streql("--help", argv[i])) global_help(help); - } -} - -int parse_args(int argc, char** argv, void (*help)(void), int (*short_arg)(char, char*), int (*long_arg)(char*, char*)) { - int start, i, current; - char* next_arg; - - if (argc < 1) return 0; - - start = 0; - for (i = 0; i < argc; i++) { - - if (!prefix("-", argv[i]) || streql("-", argv[i])) break; - if (help != NULL && streql("--help", argv[i])) global_help(help); - - if (i + 1 == argc) { - next_arg = NULL; - } else { - next_arg = argv[i+1]; - } - - current = i; - - if (prefix("--", argv[i])) { - int r; - - if (long_arg == NULL) { - goto exit; - } - - r = long_arg(argv[current], next_arg); - - if (r == ARG_USED) { - i++; - start++; - } else if (r == ARG_IGNORE) { - goto exit; - } else if (r == ARG_INVALID) { - error("invalid argument %s", argv[current]); - - } - } else { - size_t j; - int r; - - if (short_arg == NULL) { - goto exit; - } - - for (j = 1; j < strlen(argv[current]); j++) { - - r = short_arg(argv[current][j], next_arg); - - if (r == ARG_USED) { - i++; - start++; - } else if (r == ARG_IGNORE) { - goto exit; - } else if (r == ARG_INVALID) { - error("invalid argument -%c", argv[current][j]); - } - } - } - - start++; - } - -exit: - - return start; -} - -int get_tty (void) { - int fd = open(_PATH_TTY, O_RDONLY); - if (fd < 0) error("failed to get tty: %s", strerror(errno)); - return fd; -} - -FILE* get_tty_stream(char* type) { - int fd = get_tty(); - FILE* file = fdopen(fd, type); - if (file == NULL) { - error("failed to open tty stream: %s", strerror(errno)); - } - return file; -} - -static int push_path_buffer_b(char* buf, int* index, const char* string) { - int save, string_len; - - save = *index; - if (*index > 1 || (*index == 1 && buf[0] != '/')) { - buf[(*index)++] = '/'; - } - - string_len = strlen(string); - memcpy(buf + *index, string, string_len + 1); - - *index += string_len; - - return save; -} - -static void pop_path_buffer_b(char* buf, int* index, int i) { - *index = i; - buf[*index] = '\0'; -} - -static char path_buffer[PATH_MAX + 1]; -static int path_buffer_index = 0; - -char* get_path_buffer(void) { - return path_buffer; -} - -int push_path_buffer(const char* string) { - return push_path_buffer_b(path_buffer, &path_buffer_index, string); -} - -void pop_path_buffer(int i) { - pop_path_buffer_b(path_buffer, &path_buffer_index, i); -} - -static char path_buffer_2[PATH_MAX + 1]; -static int path_buffer_index_2 = 0; - -char* get_path_buffer_2(void) { - return path_buffer_2; -} - -int push_path_buffer_2(const char* string) { - return push_path_buffer_b(path_buffer_2, &path_buffer_index_2, string); -} - -void pop_path_buffer_2(int i) { - pop_path_buffer_b(path_buffer_2, &path_buffer_index_2, i); -} - -bool is_dot_dir(const char* path) { - return streql(path, ".") || streql(path, ".."); -} diff --git a/src/util/shared.h b/src/util/shared.h deleted file mode 100644 index a3e6a56..0000000 --- a/src/util/shared.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef SHARED_H -#define SHARED_H - -#include -#include -#include -#include - -#define ANSCII "\x1b[" -#define NEXT ";" - -#define RESET "0" -#define BOLD "1" - -#define NORMAL "3" -#define BACKGROUND "4" -#define HIGHLIGHT "9" - -#define BLACK "0" -#define RED "1" -#define GREEN "2" -#define YELLOW "3" -#define BLUE "4" -#define MAGENTA "5" -#define TURQUOISE "6" -#define WHITE "7" - -#define COLOR "m" - -typedef uint8_t bool; -#define true 1 -#define false 0 - -enum When { - YES, - NO, - AUTO -}; - -__attribute__ ((__format__(printf, 1, 2))) -void error_s(const char* format, ...); - -__attribute__ ((__format__(printf, 1, 2))) -void error(const char* format, ...); - -__attribute__ ((__format__(printf, 1, 2))) -void output(const char* format, ...); - -FILE* get_file_s(const char* path, const char* type); -FILE* get_file(const char* path, const char* type); -long int get_number(const char* text); -long int get_blkm(const char* text); -mode_t get_mode(const char* next); - -bool streql(const char* a, const char* b); -bool prefix(const char* pre, const char* str); - -void print_file_size(size_t bytes, char buf[5]); -void print_date_time(time_t mills, char buf[13]); -void print_file_path(char* path); - -#define UNUSED(x) (void)(x) -#define ARG_UNUSED 0 -#define ARG_USED 1 -#define ARG_IGNORE 2 -#define ARG_INVALID 3 - -void check_arg (char* arg); -void global_help(void (*help)(void)); -void parse_help (int argc, char** argv, void (*help)(void)); -int parse_args (int argc, char** argv, void (*help)(void), int (*short_arg)(char, char*), int (*long_arg)(char*, char*)); - -int get_tty(void); -FILE* get_tty_stream(char* type); - -char* get_path_buffer(void); -int push_path_buffer(const char* string); -void pop_path_buffer(int i); - -char* get_path_buffer_2(void); -int push_path_buffer_2(const char* string); -void pop_path_buffer_2(int i); - -bool is_dot_dir(const char* path); - -#endif diff --git a/src/util/stack.c b/src/util/stack.c deleted file mode 100644 index acffc1a..0000000 --- a/src/util/stack.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "stack.h" - -#include -#include -#include -#include - -void stack_init(struct Stack* stack, size_t size) { - stack->size = 0; - stack->capacity = size; - stack->data = malloc(size); -} - -void stack_push(struct Stack* stack, void* data, size_t len) { - size_t new_size = stack->size + len; - if (new_size >= stack->capacity) { - stack->capacity = new_size * 2; - stack->data = realloc(stack->data, stack->capacity); - } - memcpy((uint8_t*)stack->data + stack->size, data, len); - stack->size += len; -} - -void* stack_pop(struct Stack* stack, size_t len) { - if (stack->size < len) return NULL; - stack->size -= len; - return (uint8_t*)stack->data + stack->size; -} - -void stack_free(struct Stack *stack) { - free(stack->data); -} - -void stack_push_int(struct Stack *stack, int value) { - stack_push(stack, &value, sizeof(int)); -} - -bool stack_pop_int(struct Stack *stack, int* value) { - void* d = stack_pop(stack, sizeof(int)); - if (d == NULL) return false; - *value = *(int*)(d); - return true; -} diff --git a/src/util/stack.h b/src/util/stack.h deleted file mode 100644 index 8d6fc80..0000000 --- a/src/util/stack.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef STACK_H -#define STACK_H - -#include "shared.h" - -#include - -struct Stack { - size_t size; - size_t capacity; - void* data; -}; - -void stack_init(struct Stack* stack, size_t size); -void stack_push(struct Stack* stack, void* data, size_t len); -void* stack_pop(struct Stack* stack, size_t len); -void stack_free(struct Stack* stack); - -void stack_push_int(struct Stack* stack, int value); -bool stack_pop_int(struct Stack* stack, int* value); - -#endif -- cgit v1.2.3-freya