summaryrefslogtreecommitdiff
path: root/src/commands/tac.c
blob: fbd0f747cdd6f1939a59384d8dca0a626aceb2df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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;
}