diff options
Diffstat (limited to 'command/cat.c')
-rw-r--r-- | command/cat.c | 142 |
1 files changed, 142 insertions, 0 deletions
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 <ctype.h> +#include <stdlib.h> + +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; +} |