#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; }