#include "../command.h" #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; for (int i = 0; i < argc; i++) { if (!prefix("-", argv[i])) { continue; } if (streql("--help", argv[i])) { help(); } size_t len = strlen(argv[i] + 1); for (size_t j = 0; j < len; j++) { char c = argv[i][j + 1]; 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); } } } bool files = false; for (int i = 0; i < argc; i++) { if (streql("-", argv[i])) { files = true; cat_file(stdin, flags); } else if (prefix("-", argv[i])) { continue; } else { files = true; struct stat s; if (stat(argv[i], &s) < 0) { printf("error: failed to read %s: %s\n", argv[i], strerror(errno)); continue; } if (!S_ISREG(s.st_mode)) { printf("error: %s is not a file\n", argv[i]); continue; } FILE* in = get_file(argv[i], "r"); cat_file(in, flags); fclose(in); } } if (!files) { cat_file(stdin, flags); } return EXIT_SUCCESS; }