summaryrefslogtreecommitdiff
path: root/src/commands/tac.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands/tac.c')
-rw-r--r--src/commands/tac.c105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/commands/tac.c b/src/commands/tac.c
new file mode 100644
index 0000000..fbd0f74
--- /dev/null
+++ b/src/commands/tac.c
@@ -0,0 +1,105 @@
+#include "../command.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+static void help(void) {
+ printf("Usage: tac [FILE]...\n\n");
+ printf("Concatenate FILEs and print them in reverse\n");
+}
+
+static void print_range(FILE* file, int start, int end) {
+ int len = end - start;
+ fseek(file, start, SEEK_SET);
+ for (int i = 0; i < len; i++) {
+ putchar(getc(file));
+ }
+ fflush(stdout);
+}
+
+static char stdin_path[PATH_MAX];
+
+static FILE* read_stdin() {
+ static bool read;
+ static FILE* file;
+
+ if (read) goto finished;
+ read = true;
+
+ srand(time(NULL));
+ int r = rand() % 1000000;
+ sprintf(stdin_path, "/tmp/%d.tac", r);
+ file = get_file(stdin_path, "w");
+
+ char c;
+ while((c = getchar()) != EOF) putc(c, file);
+ fclose(file);
+
+ file = get_file(stdin_path, "r");
+
+finished:
+ return file;
+}
+
+static void parse_file(FILE* file, struct Stack* stack) {
+ char buf[1024];
+ int read;
+ int total = 1;
+
+ stack_push_int(stack, 0);
+ rewind(file);
+ while ((read = fread(buf, 1, 1024, file)) > 0) {
+ for (int i = 0; i < read; i++) {
+ char c = buf[i];
+ if (c != '\n') continue;
+ stack_push_int(stack, total + i);
+ }
+ total += read;
+ }
+}
+
+static void tac_file(FILE* file) {
+ struct Stack stack;
+ stack_init(&stack, 80);
+
+ parse_file(file, &stack);
+
+ rewind(file);
+ int last, current;
+ if (!stack_pop_int(&stack, &last)) goto cleanup;
+ while(stack_pop_int(&stack, &current)) {
+ print_range(file, current, last);
+ last = current;
+ }
+
+cleanup:
+ stack_free(&stack);
+}
+
+COMMAND(tac) {
+
+ parse_help(argc, argv, help);
+
+ FILE* in = read_stdin();
+
+ if (argc < 1) {
+ tac_file(in);
+ return EXIT_SUCCESS;
+ }
+
+ for (int i = 0; i < argc; i++) {
+ FILE* file = get_file(argv[i], "r");
+ if (file == stdin) {
+ tac_file(in);
+ } else {
+ tac_file(file);
+ fclose(file);
+ }
+ }
+
+ fclose(in);
+ remove(stdin_path);
+
+ return EXIT_SUCCESS;
+}