#include "../command.h" #include #include #include #include #include struct Flags { bool number_lines; bool number_non_empty; bool change_non_print; bool change_tabs; bool end_lines_dollar; }; 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); } } static void help() { printf("Usage: cat [-nbvteA] [FILE]...\n\n"); printf("Print FILEs to stdout\n\n"); printf("-n Number output lines\n"); printf("-b Number nonempty lines\n"); printf("-v Show nonprinting characters as ^x or M-x\n"); printf("-t ...and tabs as ^I\n"); printf("-e ...and end lines with $\n"); printf("-A Same as -vte\n"); exit(EXIT_SUCCESS); } static void cat_file(FILE* file, struct Flags flags) { 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); } } } 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 arg_len = argc - start; if (arg_len < 1) { cat_file(stdin, flags); return EXIT_SUCCESS; } for (int i = start; i < argc; i++) { FILE* in = get_file(argv[i], "r"); cat_file(in, flags); if (in != stdin) fclose(in); } return EXIT_SUCCESS; }