diff --git a/Makefile b/Makefile index 611ea8d..708443e 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,24 @@ CC = gcc -INCFLAGS = -Isrc +SOURCE = src -CCFLAGS = -std=c99 -Wall -Wextra -pedantic -O2 -D_DEFAULT_SOURCE +MAJOR = 0 +MINOR = 0 +PATCH = 1 + +INCFLAGS = $(shell echo $(SOURCE) | xargs printf -- '-I%s') + +CCFLAGS = -std=c99 -Wall -Wextra -pedantic -O2 +CCFLAGS += -D_DEFAULT_SOURCE -DMAJOR=$(MAJOR) -DMINOR=$(MINOR) -DPATCH=$(PATCH) -DCHECK_LINK CCFLAGS += $(INCFLAGS) +LDFLAGS = -s LDFLAGS += $(INCFLAGS) BIN = bin APP = $(BIN)/app -SRC = $(shell find src -name "*.c") +SRC = $(shell find $(SOURCE) -name "*.c") +DIR = $(shell find $(SOURCE) -type d) OBJ = $(SRC:%.c=$(BIN)/%.o) .PHONY: dirs run clean build install uninstall @@ -17,10 +26,7 @@ OBJ = $(SRC:%.c=$(BIN)/%.o) EOF: clean build dirs: - mkdir -p ./$(BIN) - mkdir -p ./$(BIN)/src - mkdir -p ./$(BIN)/src/util - mkdir -p ./$(BIN)/src/commands + echo $(DIR) | xargs printf -- '$(BIN)/%s\n' | xargs mkdir -p run: build $(APP) diff --git a/readme.md b/readme.md index 50f9067..15ab698 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ A terrible busybox/gnu coreutils clone. Currently the only supported commands are: -`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`, `ed`, `tee`, `true`, `false`, `tee`, `whoami`, `wc` +`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`, `ed`, `tee`, `true`, `false`, `tee`, `whoami`, `wc`, `xargs` ## How to diff --git a/src/command.h b/src/command.h index 015b1a2..e596847 100644 --- a/src/command.h +++ b/src/command.h @@ -30,3 +30,4 @@ COMMAND(ed); COMMAND(tee); COMMAND(whoami); COMMAND(wc); +COMMAND(xargs); diff --git a/src/commands/cat.c b/src/commands/cat.c index bb050c0..6ed77e8 100644 --- a/src/commands/cat.c +++ b/src/commands/cat.c @@ -2,13 +2,13 @@ #include -struct Flags { +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) { @@ -20,7 +20,7 @@ static bool printable(char c) { } } -static void help() { +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"); @@ -29,10 +29,9 @@ static void help() { printf("\t-t ...and tabs as ^I\n"); printf("\t-e ...and end lines with $\n"); printf("\t-A Same as -vte\n"); - exit(EXIT_SUCCESS); } -static void cat_file(FILE* file, struct Flags flags) { +static void cat_file(FILE* file) { char c; size_t read; @@ -78,74 +77,57 @@ static void cat_file(FILE* file, struct Flags flags) { } } +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: + error("error: unkown flag -%c", c); + } + return ARG_UNUSED; +} + COMMAND(cat) { - struct Flags flags; flags.number_lines = false; flags.number_non_empty = false; flags.change_non_print = false; flags.change_tabs = false; flags.end_lines_dollar = false; - int start = 0; - - for (int i = 0; i < argc; i++) { - if (!prefix("-", argv[i])) { - continue; - } - - if (streql("-", argv[i])) { - break; - } - - if (streql("--help", argv[i])) { - help(); - } - - start++; - - size_t len = strlen(argv[i]); - for (size_t j = 1; j < len; j++) { - char c = argv[i][j]; - 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: - error("error: unkown flag -%c", c); - } - } - } + int start = parse_args(argc, argv, help, short_arg, NULL); int arg_len = argc - start; if (arg_len < 1) { - cat_file(stdin, flags); + cat_file(stdin); return EXIT_SUCCESS; } for (int i = start; i < argc; i++) { FILE* in = get_file(argv[i], "r"); - cat_file(in, flags); + cat_file(in); if (in != stdin) fclose(in); } diff --git a/src/commands/dd.c b/src/commands/dd.c index 2b6955f..6b973e2 100644 --- a/src/commands/dd.c +++ b/src/commands/dd.c @@ -1,13 +1,12 @@ #include "../command.h" -static void help() { +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"); - exit(EXIT_SUCCESS); } COMMAND(dd) { @@ -17,6 +16,8 @@ COMMAND(dd) { int bs = 1024; int count = -1; + parse_help(argc, argv, help); + for (int i = 0; i < argc; i++) { if (prefix("if=", argv[i])) { char* path = argv[i] + 3; @@ -36,8 +37,6 @@ COMMAND(dd) { if (count < 1) { error("error: count must be greather than 0"); } - } else if (streql("--help", argv[i])) { - help(); } else { error("error: unkown option %s", argv[i]); } diff --git a/src/commands/echo.c b/src/commands/echo.c index a13947c..8b39531 100644 --- a/src/commands/echo.c +++ b/src/commands/echo.c @@ -1,5 +1,10 @@ #include "../command.h" +static struct { + bool escape_codes; + bool newline; +} flags; + static void print_with_escape_codes(const char* str) { size_t index = 0; @@ -44,44 +49,39 @@ static void print_with_escape_codes(const char* str) { } } +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) { if (argc < 1) { return EXIT_SUCCESS; } - bool escape_codes = false; - bool newline = true; + flags.escape_codes = false; + flags.newline = true; - int start = 0; - - if (prefix("-", argv[0])) { - - start = 1; - - for (size_t i = 0; i < strlen(argv[0] + 1); i++) { - char c = argv[0][i + 1]; - switch (c) { - case 'e': - escape_codes = true; - break; - case 'E': - escape_codes = false; - break; - case 'n': - newline = false; - break; - default: - escape_codes = false; - newline = true; - start = 0; - break; - } - } - } + int start = parse_args(argc, argv, NULL, short_arg, NULL); for (int i = start; i < argc; i++) { - if (escape_codes) { + if (flags.escape_codes) { print_with_escape_codes(argv[i]); } else { printf("%s", argv[i]); @@ -92,7 +92,7 @@ COMMAND(echo) { } } - if (newline) { + if (flags.newline) { putchar('\n'); } diff --git a/src/commands/ed.c b/src/commands/ed.c index ed4ce24..44f1a2c 100644 --- a/src/commands/ed.c +++ b/src/commands/ed.c @@ -141,21 +141,6 @@ static bool parse_regex_lines(char** end, struct LineAddress* address) { } } - // 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(unsigned long)); - // } - // buf[siz] = i; - // siz++; - // if (dir == BEFORE && i == 0) break; - // } - free_address(*address); address->empty = false; address->type = SET; @@ -295,7 +280,7 @@ static void free_data(bool all) { } } -static void load_empty() { +static void load_empty(void) { free_data(false); line_capacity = 8; @@ -579,7 +564,7 @@ static int substute_string(long int index, long int matches, re_t regex, char* s return matches_found; } -static void prompt() { +static void prompt(void) { printf(": "); fflush(stdout); @@ -805,13 +790,13 @@ test: } -static void prompt_loop() { +static void prompt_loop(void) { while (true) { prompt(); } } -static void help() { +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"); @@ -832,15 +817,16 @@ static void help() { 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"); - exit(EXIT_SUCCESS); + printf("\t\t=\t\tprint current line number\n"); } COMMAND(ed) { + + parse_help(argc, argv, help); + if (argc < 1) { load_empty(); prompt_loop(); - } else if (streql(argv[0], "--help")) { - help(); } else { FILE* file = get_file(argv[0], "r"); load_file(file); diff --git a/src/commands/head.c b/src/commands/head.c index 018da5a..44bc45b 100644 --- a/src/commands/head.c +++ b/src/commands/head.c @@ -1,17 +1,17 @@ #include "../command.h" -struct Flags { +static struct { int count; bool lines; bool print_headers; bool dont_print_headers; -}; +} flags; -static void head_file_lines(FILE* file, struct Flags* flags) { +static void head_file_lines(FILE* file) { size_t len = 0; char* line = NULL; - int count = flags->count; + int count = flags.count; while(count > 0 && getline(&line, &len, file) != -1) { printf("%s", line); count--; @@ -21,9 +21,9 @@ static void head_file_lines(FILE* file, struct Flags* flags) { fclose(file); } -static void head_file_chars(FILE* file, struct Flags* flags) { +static void head_file_chars(FILE* file) { char c; - int count = flags->count; + int count = flags.count; while(count > 0 && (c = getc(file)) != EOF) { putchar(c); count--; @@ -32,7 +32,7 @@ static void head_file_chars(FILE* file, struct Flags* flags) { fclose(file); } -static void help() { +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"); @@ -41,19 +41,11 @@ static void help() { 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"); - exit(EXIT_SUCCESS); } -static char* next_arg(int argc, char** argv, int index) { - if (index >= argc) { - error("error: expected another argument after option"); - } - return argv[index]; -} - -static void print_header(char* path, struct Flags* flags, bool many) { - if (flags->dont_print_headers) return; - if (!many && !flags->print_headers) return; +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 { @@ -61,81 +53,73 @@ static void print_header(char* path, struct Flags* flags, bool many) { } } -static void head_file(char* path, struct Flags* flags, bool many) { +static void head_file(char* path, bool many) { FILE* file = get_file(path, "r"); - print_header(path, flags, many); - if (flags->lines) { - head_file_lines(file, flags); + print_header(path, many); + if (flags.lines) { + head_file_lines(file); } else { - head_file_chars(file, flags); + head_file_chars(file); } } +static int short_arg(char c, char* next) { + switch(c) { + case 'c': { + flags.lines = false; + check_arg(next); + long int bkm = get_blkm(next); + if (bkm < 1) { + error("error: bkm cannot be less than 1"); + } + flags.count = bkm; + return ARG_USED; + } + case 'n': { + flags.lines = true; + check_arg(next); + long int bkm = get_blkm(next); + if (bkm < 1) { + error("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: { + error("error: unknown option -%c", c); + } + } + return ARG_UNUSED; +} + COMMAND(head) { - struct Flags flags; flags.count = 10; flags.lines = true; flags.print_headers = false; flags.dont_print_headers = false; - int start = 0; - for (int i = 0; i < argc; i++) { - if (!prefix("-", argv[i])) break; - if (streql(argv[0], "--help")) help(); - start++; - int i_s = i; - for (size_t j = 1; j < strlen(argv[i_s]); j++) { - char c = argv[i_s][j]; - switch(c) { - case 'c': { - start++; - flags.lines = false; - char* arg = next_arg(argc, argv, ++i); - long int bkm = get_blkm(arg); - if (bkm < 1) { - error("error: bkm cannot be less than 1"); - } - flags.count = bkm; - break; - } - case 'n': { - start++; - flags.lines = true; - char* arg = next_arg(argc, argv, ++i); - long int bkm = get_blkm(arg); - if (bkm < 1) { - error("error: bkm cannot be less than 1"); - } - flags.count = bkm; - break; - } - case 'q': - flags.dont_print_headers = true; - break; - case 'v': - flags.print_headers = true; - break; - default: { - error("error: unknown option -%c", c); - } - } - } - } + int start = parse_args(argc, argv, help, short_arg, NULL); int count = argc - start; if (count < 1) { - head_file_lines(stdin, &flags); + head_file_lines(stdin); return EXIT_SUCCESS; } if (count == 1) { - head_file(argv[start], &flags, false); + head_file(argv[start], false); return EXIT_SUCCESS; } for (int i = 0; i < count; i++) { - head_file(argv[start + i], &flags, true); + head_file(argv[start + i], true); } return EXIT_SUCCESS; diff --git a/src/commands/ls.c b/src/commands/ls.c index 855168c..ce7206c 100644 --- a/src/commands/ls.c +++ b/src/commands/ls.c @@ -15,14 +15,14 @@ #define EXEC_COLOR ANSCII BOLD NEXT NORMAL GREEN COLOR #define BLK_COLOR ANSCII BOLD NEXT NORMAL YELLOW COLOR -struct Flags { +static struct { bool hidden; bool hide_dot; bool more_info; bool one_column; bool recurse; enum When colored; -}; +} flags; struct FileInfo { struct passwd* usr; @@ -206,14 +206,14 @@ static char* get_file_color(struct FileInfo* info) { return color; } -static void list_files(struct FileInfo* files, int file_len, struct FileListInfo info, struct Flags* flags, const char* dir_path) { +static void list_files(struct FileInfo* files, int file_len, struct FileListInfo info, const char* dir_path) { struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); if (!isatty(1)) { - flags->one_column = true; - if (flags->colored == AUTO) - flags->colored = NO; + flags.one_column = true; + if (flags.colored == AUTO) + flags.colored = NO; } char* color; @@ -223,7 +223,7 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo for (int i = 0; i < file_len; i++) { struct FileInfo finfo = files[i]; color = get_file_color(&finfo); - if (flags->more_info) { + if (flags.more_info) { printf("%s %*d %*s %*s %*s %s %s%s%s", finfo.mode, info.max_link, @@ -235,9 +235,9 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo info.max_size, finfo.size, finfo.date, - flags->colored != NO ? color : "", + flags.colored != NO ? color : "", finfo.name, - flags->colored != NO ? "\x1b[0m" : "" + flags.colored != NO ? "\x1b[0m" : "" ); if (finfo.type == DT_LNK) { char path[PATH_MAX]; @@ -254,21 +254,21 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo } 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 (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" : ""); + 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" : ""); + printf("%s%s%s ", flags.colored != NO ? color : "", finfo.name, + flags.colored != NO ? "\x1b[0m" : ""); } } } - if (!flags->more_info) printf("\n"); + if (!flags.more_info) printf("\n"); } static bool is_dot_dir(const char* path) { @@ -321,7 +321,7 @@ static void push_file( (*size)++; } -static bool recurse_directory(char* path, struct Flags* flags) { +static bool recurse_directory(char* path) { DIR* d; struct dirent* file; bool first = true; @@ -338,10 +338,10 @@ static bool recurse_directory(char* path, struct Flags* flags) { while((file = readdir(d)) != NULL) { if (file->d_type == DT_DIR) continue; - if (!flags->hidden && prefix(".", file->d_name)) continue; + if (!flags.hidden && prefix(".", file->d_name)) continue; if (is_dot_dir(file->d_name)) continue; if (first) { - if (flags->colored == NO) + if (flags.colored == NO) printf("\n%s:\n", path); else printf("\n%s%s:%s\n", DIR_COLOR, path, FILE_COLOR); @@ -350,10 +350,10 @@ static bool recurse_directory(char* path, struct Flags* flags) { push_file(&files, &info, &size, &capacity, file->d_name, path); } - list_files(files, size, info, flags, path); + list_files(files, size, info, path); free(files); - if (!flags->more_info) printf("\n"); + if (!flags.more_info) printf("\n"); closedir(d); @@ -362,11 +362,11 @@ static bool recurse_directory(char* path, struct Flags* flags) { while((file = readdir(d)) != NULL) { if (file->d_type != DT_DIR) continue; - if (!flags->hidden && prefix(".", file->d_name)) continue; + if (!flags.hidden && prefix(".", file->d_name)) continue; if (is_dot_dir(file->d_name)) continue; char buf[PATH_MAX]; append_path(buf, path, file->d_name); - recurse_directory(buf, flags); + recurse_directory(buf); } closedir(d); @@ -374,9 +374,9 @@ static bool recurse_directory(char* path, struct Flags* flags) { return true; } -static bool list_directory(char* path, struct Flags* flags) { - if (flags->recurse) { - return recurse_directory(path, flags); +static bool list_directory(char* path) { + if (flags.recurse) { + return recurse_directory(path); } DIR* d = get_directory(path); @@ -390,12 +390,12 @@ static bool list_directory(char* path, struct Flags* flags) { struct dirent* file; 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 (!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, path); } - if (size > 0) list_files(files, size, info, flags, path); + if (size > 0) list_files(files, size, info, path); free(files); closedir(d); @@ -408,7 +408,7 @@ static bool is_dir(const char* path) { return S_ISDIR(s.st_mode); } -static void list_file_args(int start, int argc, char** argv, struct Flags* flags) { +static void list_file_args(int start, int argc, char** argv) { int capacity = 8; int size = 0; struct FileInfo* files = malloc(sizeof(struct FileInfo) * capacity); @@ -420,12 +420,12 @@ static void list_file_args(int start, int argc, char** argv, struct Flags* flags push_file((struct FileInfo**) &files, &info, &size, &capacity, argv[i], "."); } - if (size > 0) list_files(files, size, info, flags, "."); + if (size > 0) list_files(files, size, info, "."); free(files); } -static void help() { +static void help(void) { printf("Usage: ls [FILE]...\n\n"); printf("List directory contents\n\n"); printf("\t-1\tOne column output\n"); @@ -433,12 +433,54 @@ static void help() { printf("\t-a\tInclude names starting with .\n"); printf("\t-A\tLike -a but without . and ..\n"); printf("\t-R\tRecurse\n"); - exit(EXIT_SUCCESS); +} + +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: + error("error: unkown option -%c", c); + } + 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("error: invalid color options: %s", arg); + } + } else { + return ARG_IGNORE; + } + return ARG_UNUSED; } COMMAND(ls) { - struct Flags flags; flags.hidden = false; flags.more_info = false; flags.hide_dot = false; @@ -446,55 +488,14 @@ COMMAND(ls) { flags.recurse = false; flags.colored = AUTO; - int start = 0; - for (int i = 0; i < argc; i++) { - if (!prefix("-", argv[i])) break; - if (streql("--help", argv[i])) help(); - start++; - if (prefix("--color=", argv[i])) { - char* arg = argv[i] + 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("error: invalid color options: %s", arg); - } - continue; - } - for (size_t j = 1; j < strlen(argv[i]); j++) { - char c = argv[i][j]; - 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: - error("error: unkown option -%c", c); - } - } - } + int start = parse_args(argc, argv, help, short_arg, long_arg); if (argc - start == 0) { - list_directory(".", &flags); + list_directory("."); return EXIT_SUCCESS; } - list_file_args(start, argc, argv, &flags); + list_file_args(start, argc, argv); bool titled = argc - start > 1; for (int i = start; i < argc; i++) { @@ -505,7 +506,7 @@ COMMAND(ls) { else printf("\n%s:\n", argv[i]); } - if (list_directory(argv[i], &flags) && i + 1 != argc) + if (list_directory(argv[i]) && i + 1 != argc) if (titled && !flags.recurse) printf("\n"); } diff --git a/src/commands/printf.c b/src/commands/printf.c index 2a84b80..fd08aa8 100644 --- a/src/commands/printf.c +++ b/src/commands/printf.c @@ -78,10 +78,9 @@ static void handle_slash(char n) { } } -static void help() { +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"); - exit(EXIT_SUCCESS); } COMMAND(print) { @@ -89,7 +88,7 @@ COMMAND(print) { error("usage: printf FORMAT [ARG]...\n"); } - if (streql("--help", argv[0])) help(); + parse_help(argc, argv, help); size_t index = 0; int arg_index = 0; diff --git a/src/commands/tail.c b/src/commands/tail.c index 118a771..a611842 100644 --- a/src/commands/tail.c +++ b/src/commands/tail.c @@ -1,13 +1,13 @@ #include "../command.h" -struct Flags { +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[count]; @@ -83,7 +83,7 @@ static size_t tail_file_chars(FILE* file, unsigned int count, size_t skip) { return read; } -static void help() { +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"); @@ -97,16 +97,9 @@ static void help() { exit(EXIT_SUCCESS); } -static char* next_arg(int argc, char** argv, int index) { - if (index >= argc) { - error("error: expected another argument after option"); - } - return argv[index]; -} - -static void print_header(char* path, struct Flags* flags, bool many) { - if (flags->dont_print_headers) return; - if (!many && !flags->print_headers) return; +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 { @@ -114,26 +107,72 @@ static void print_header(char* path, struct Flags* flags, bool many) { } } -static void tail_file(char* path, struct Flags* flags, bool many) { +static void tail_file(char* path, bool many) { FILE* file = get_file(path, "r"); - print_header(path, flags, many); + print_header(path, many); size_t skip = 0; while (true) { - if (flags->lines) { - skip = tail_file_lines(file, flags->count, skip); + if (flags.lines) { + skip = tail_file_lines(file, flags.count, skip); } else { - skip = tail_file_chars(file, flags->count, skip); + skip = tail_file_chars(file, flags.count, skip); } - if (!flags->print_as_grow) break; - sleep(flags->grow_wait); + 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': { + flags.lines = false; + check_arg(next); + long int bkm = get_blkm(next); + if (bkm < 1) { + error("error: bkm cannot be less than 1"); + } + flags.count = bkm; + return ARG_USED; + } + case 'n': { + flags.lines = true; + check_arg(next); + long int bkm = get_blkm(next); + if (bkm < 1) { + error("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': { + check_arg(next); + long int sec = get_number(next); + if (sec < 1) { + error("error: wait seconds cannot be less than 1"); + } + flags.grow_wait = sec; + return ARG_USED; + } + default: { + error("error: unknown option -%c", c); + } + } + return ARG_UNUSED; +} + COMMAND(tail) { - struct Flags flags; flags.count = 10; flags.dont_print_headers = false; flags.print_headers = false; @@ -141,62 +180,7 @@ COMMAND(tail) { flags.print_as_grow = false; flags.grow_wait = 10; - int start = 0; - for (int i = 0; i < argc; i++) { - if (!prefix("-", argv[i])) break; - if (streql(argv[0], "--help")) help(); - start++; - int i_s = i; - for (size_t j = 1; j < strlen(argv[i_s]); j++) { - char c = argv[i_s][j]; - switch(c) { - case 'c': { - start++; - flags.lines = false; - char* arg = next_arg(argc, argv, ++i); - long int bkm = get_blkm(arg); - if (bkm < 1) { - error("error: bkm cannot be less than 1"); - } - flags.count = bkm; - break; - } - case 'n': { - start++; - flags.lines = true; - char* arg = next_arg(argc, argv, ++i); - long int bkm = get_blkm(arg); - if (bkm < 1) { - error("error: bkm cannot be less than 1"); - } - flags.count = bkm; - break; - } - 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': { - start++; - char* arg = next_arg(argc, argv, ++i); - long int sec = get_number(arg); - if (sec < 1) { - error("error: wait seconds cannot be less than 1"); - } - flags.grow_wait = sec; - break; - } - default: { - error("error: unknown option -%c", c); - } - } - } - } + int start = parse_args(argc, argv, help, short_arg, NULL); int count = argc - start; @@ -206,12 +190,12 @@ COMMAND(tail) { } if (count == 1) { - tail_file(argv[start], &flags, false); + tail_file(argv[start], false); return EXIT_SUCCESS; } for (int i = 0; i < count; i++) { - tail_file(argv[start + i], &flags, true); + tail_file(argv[start + i], true); } return EXIT_SUCCESS; diff --git a/src/commands/tee.c b/src/commands/tee.c index c500508..ced5b06 100644 --- a/src/commands/tee.c +++ b/src/commands/tee.c @@ -2,7 +2,12 @@ #include -static void help() { +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"); @@ -10,7 +15,7 @@ static void help() { exit(EXIT_SUCCESS); } -static void handle(){} +static void handle(int dummy){UNUSED(dummy);} static void run_tee(int file_count, FILE* files[file_count]) { char c; @@ -26,33 +31,29 @@ static void run_tee(int file_count, FILE* files[file_count]) { } } +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: + error("error: unkown option: %c", c); + } + return ARG_UNUSED; +} + COMMAND(tee) { - bool append = false; - bool handle_sigint = false; + flags.append = false; + flags.handle_sigint = false; - int start = 0; - for (int i = 0; i < argc; i++) { - if (!prefix("-", argv[i])) break; - if (streql("--help", argv[i])) help(); + int start = parse_args(argc, argv, help, short_arg, NULL); - start++; - for (size_t j = 1; j < strlen(argv[i]); j++) { - char o = argv[i][j]; - switch (o) { - case 'a': - append = true; - break; - case 'i': - handle_sigint = true; - break; - default: - error("error: unkown option: %c", o); - } - } - } - - if (handle_sigint) { + if (flags.handle_sigint) { signal(SIGINT, handle); } @@ -63,7 +64,7 @@ COMMAND(tee) { FILE* files[argc - start]; for (int i = start; i < argc; i++) { - FILE* file = get_file(argv[i], append ? "a" : "w"); + FILE* file = get_file(argv[i], flags.append ? "a" : "w"); files[i - start] = file; } diff --git a/src/commands/wc.c b/src/commands/wc.c index 892ef2e..9acbf5c 100644 --- a/src/commands/wc.c +++ b/src/commands/wc.c @@ -2,13 +2,14 @@ #include -struct Flags { +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; @@ -16,20 +17,20 @@ 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, struct Flags* flags) { - if (flags->newlines) { +static void list(int l, int w, int c, int b, int lg) { + if (flags.newlines) { printf("\t%d", l); } - if (flags->words) { + if (flags.words) { printf("\t%d", w); } - if (flags->characters) { + if (flags.characters) { printf("\t%d", c); } - if (flags->bytes) { + if (flags.bytes) { printf("\t%d", b); } - if (flags->longest_line) { + if (flags.longest_line) { printf("\t%d", lg); } } @@ -40,7 +41,7 @@ static bool is_delimiter(char c) { return c == ' ' || c == '\t' || c == '\n'; } -static void run_wc(FILE* file, struct Flags* flags) { +static void run_wc(FILE* file) { int l = 0, w = 0, c = 0, b = 0, lg = 0; bool in_word = false; @@ -70,7 +71,7 @@ static void run_wc(FILE* file, struct Flags* flags) { } if (in_word) w++; lg = lg > current_length ? lg : current_length; - list(l, w, c, b, lg, flags); + list(l, w, c, b, lg); lines += l; words += w; chars += c; @@ -80,7 +81,7 @@ static void run_wc(FILE* file, struct Flags* flags) { fclose(file); } -static void help() { +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"); @@ -91,67 +92,62 @@ static void help() { 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: + error("error: invald option -%c", c); + } + flags.has_flags = true; + return ARG_UNUSED; +} + COMMAND(wc) { - struct Flags flags; flags.newlines = false; flags.words = false; flags.characters = false; flags.bytes = false; flags.longest_line = false; + flags.has_flags = false; - bool has_flags = false; - int start = 0; - for (int i = 0; i < argc; i++) { - if (!prefix("-", argv[i])) break; - if (streql("--help", argv[i])) help(); + int start = parse_args(argc, argv, help, short_arg, NULL); - start++; - for (size_t j = 1; j < strlen(argv[i]); j++) { - char c = argv[i][j]; - 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: - error("error: invald option -%c", c); - } - has_flags = true; - } - } - - if (!has_flags) { + if (!flags.has_flags) { flags.newlines = true; flags.words = true; flags.characters = true; } if (argc - start < 1) { - run_wc(stdin, &flags); + run_wc(stdin); printf("\n"); return EXIT_SUCCESS; } for (int i = start; i < argc; i++) { FILE* file = get_file(argv[i], "r"); - run_wc(file, &flags); + run_wc(file); printf("\t%s\n", argv[i]); } if (argc - start > 1) { - list(lines, words, chars, bytes, logst, &flags); + list(lines, words, chars, bytes, logst); printf("\ttotal\n"); } diff --git a/src/commands/whoami.c b/src/commands/whoami.c index f19a724..623a35a 100644 --- a/src/commands/whoami.c +++ b/src/commands/whoami.c @@ -2,14 +2,14 @@ #include -static void help() { +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) { - if (argc > 0 && streql("--help", argv[0])) help(); + parse_help(argc, argv, help); uid_t usr = getuid(); struct passwd* passwd = getpwuid(usr); diff --git a/src/commands/xargs.c b/src/commands/xargs.c new file mode 100644 index 0000000..3b96bb6 --- /dev/null +++ b/src/commands/xargs.c @@ -0,0 +1,163 @@ +#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': + check_arg(next); + long int n = get_number(next); + if (n < 1) { + error("error: max arg count must be at least 1"); + } + return ARG_USED; + } + return ARG_UNUSED; +} + +char* read_next(FILE* file, int arg_count) { + + if (arg_count == flags.max_args) return NULL; + + int size = 0; + int capacity = 8; + char* buf = malloc(sizeof(char) * capacity); + + char c; + 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; + while (arg = read_next(file, *size), true) { + if (*size == *capacity) { + *capacity *= 2; + *args = realloc(*args, sizeof(char*) * *capacity); + } + (*args)[(*size)++] = arg; + if (arg == NULL) break; + } +} + +COMMAND(xargs) { + flags.null_seperated = false; + flags.ignore_empty = false; + flags.print_command = false; + flags.print_command = false; + flags.max_args = -1; + flags.file = stdin; + + int start = parse_args(argc, argv, help, short_arg, NULL); + + char* command; + if (start >= argc) { + command = "echo"; + } else { + command = argv[start]; + } + + int arg_start = start + 1; + int arg_on_stack_count; + + if (arg_start >= argc) { + arg_on_stack_count = 0; + arg_start = argc - 1; + } else { + arg_on_stack_count = argc - arg_start; + } + + int size = arg_on_stack_count + 1; + int capacity = size + 8; + char** 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 (int i = 0; i < size - 1; i++) { + fprintf(stderr, "%s ", args[i]); + } + fprintf(stderr, "\b\n"); + } + + if (flags.prompt_command) { + fprintf(stderr, "Run command? "); + fflush(stderr); + FILE* in = get_tty_stream("r"); + char 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 (int 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 index c954810..39cf75d 100644 --- a/src/commands/yes.c +++ b/src/commands/yes.c @@ -1,19 +1,18 @@ #include "../command.h" -static void help() { +static void help(void) { printf("Usage: yes [STRING]\n\n"); printf("Repeatedly output a line with all specified STRING(s), or 'y'.\n"); - exit(EXIT_SUCCESS); } COMMAND(yes) { + + parse_help(argc, argv, help); + const char* repeat; if (argc == 0) { repeat = "y"; } else { - if (streql("--help", argv[0])) { - help(); - } repeat = argv[0]; for (int i = 1; i < argc; i++) { *(argv[i]-1) = ' '; diff --git a/src/main.c b/src/main.c index d8ee1b4..35899e2 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,7 @@ int main (ARGUMENTS) { error("error: argument 0 missing"); } +#ifdef CHECK_LINK struct stat buf; lstat(argv[0], &buf); @@ -20,12 +21,13 @@ int main (ARGUMENTS) { if (argc < 2) { printf("usage: lazysphere [function [arguments]...]\n\n"); printf("currently defined functions:\n"); - printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head, ed, tee, true, false, tee, whoami, wc\n"); + printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head, ed, tee, true, false, tee, whoami, wc, xargs\n"); return EXIT_SUCCESS; } argc--; argv = &argv[1]; } +#endif const char* cmd; if (strncmp("./", argv[0], 2) == 0) { @@ -66,6 +68,8 @@ int main (ARGUMENTS) { return whoami(NEXT_ARGS); } else if (streql(cmd, "wc")) { return wc(NEXT_ARGS); + } else if (streql(cmd, "xargs")) { + return xargs(NEXT_ARGS); } else { error("error: invalid command %s", cmd); } diff --git a/src/util/shared.c b/src/util/shared.c index d512972..e207c6d 100644 --- a/src/util/shared.c +++ b/src/util/shared.c @@ -2,10 +2,12 @@ #include #include +#include #include #include #include #include +#include void error(const char* format, ...) { va_list list; @@ -132,3 +134,100 @@ void print_date_time(time_t mills, char buf[13]) { snprintf(buf + n, 13 - n, "%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec); } + +#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("error: expected another argument after option"); + } +} + +static 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)) { + if (argc < 1) return; + for (int 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*)) { + if (argc < 1) return 0; + + int start = 0; + for (int i = 0; i < argc; i++) { + if (!prefix("-", argv[i]) || streql("-", argv[i])) break; + if (help != NULL && streql("--help", argv[i])) global_help(help); + + char* next_arg; + if (i + 1 == argc) { + next_arg = NULL; + } else { + next_arg = argv[i+1]; + } + + int current = i; + + if (prefix("--", argv[i])) { + if (long_arg == NULL) { + goto exit; + } + int r = long_arg(argv[current], next_arg); + if (r == ARG_USED) { + i++; + start++; + } else if (r == ARG_IGNORE) { + goto exit; + } + } else { + if (short_arg == NULL) { + goto exit; + } + for (size_t j = 1; j < strlen(argv[current]); j++) { + int r = short_arg(argv[current][j], next_arg); + if (r == ARG_USED) { + i++; + start++; + } else if (r == ARG_IGNORE) { + goto exit; + } + } + } + + start++; + } +exit: + return start; +} + +int get_tty() { + int fd = open(_PATH_TTY, O_RDONLY); + if (fd < 0) error("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("error: failed to open tty stream: %s", strerror(errno)); + } + return file; +} + diff --git a/src/util/shared.h b/src/util/shared.h index 5e5f4a9..c0945c1 100644 --- a/src/util/shared.h +++ b/src/util/shared.h @@ -3,6 +3,7 @@ #include #include #include +#include #define ANSCII "\x1b[" #define NEXT ";" @@ -44,3 +45,15 @@ 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]); + +#define UNUSED(x) (void)(x) +#define ARG_UNUSED 0 +#define ARG_USED 1 +#define ARG_IGNORE 2 + +void check_arg (char* arg); +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(); +FILE* get_tty_stream(char* type);