summaryrefslogtreecommitdiff
path: root/command/cat.c
diff options
context:
space:
mode:
Diffstat (limited to 'command/cat.c')
-rw-r--r--command/cat.c142
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;
+}