summaryrefslogtreecommitdiff
path: root/Userland/wc.cpp
blob: 499eb855599d3fbaa4f43650653d390f293bb782 (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibCore/CArgsParser.h>

#include <ctype.h>
#include <stdio.h>

static bool output_chars = false;
static bool output_words = false;
static bool output_lines = false;

struct Count {
    String file;
    unsigned long chars = 0;
    unsigned long words = 0;
    unsigned long lines = 0;
};

int wc(const String& filename, Vector<Count>& counts);

void report(const Count& count)
{
    if (output_lines) {
        printf("%lu ", count.lines);
    }
    if (output_words) {
        printf("%lu ", count.words);
    }
    if (output_chars) {
        printf("%lu ", count.chars);
    }
    printf("%s\n", count.file.characters());
}

void report(const Vector<Count>& counts)
{
    Count total { "total" };
    for (const auto& c : counts) {
        report(c);
        total.lines += c.lines;
        total.words += c.words;
        total.chars += c.chars;
    }
    if (counts.size() > 1) {
        report(total);
    }
    fflush(stdout);
}

int count_words(const char* s)
{
    int n = 0;
    bool in_word = false;
    for (; *s; ++s) {
        if (!isspace(*s)) {
            if (!in_word) {
                in_word = true;
                ++n;
            }
        } else if (in_word) {
            in_word = false;
        }
    }
    return n;
}

int main(int argc, char** argv)
{
    if (argc < 2) {
        printf("usage: wc [-c|-m] [-lw] [file...]\n");
        return 0;
    }

    CArgsParser args_parser("wc");
    args_parser.add_arg("l", "Include lines in count");
    args_parser.add_arg("c", "Include bytes in count");
    args_parser.add_arg("m", "Include chars in count");
    args_parser.add_arg("w", "Include words in count");
    CArgsParserResult args = args_parser.parse(argc, (char**)argv);

    if (args.is_present("l")) {
        output_lines = true;
    }
    if (args.is_present("c")) {
        output_chars = true;
    }
    if (args.is_present("m")) {
        output_chars = true;
    }
    if (args.is_present("w")) {
        output_words = true;
    }
    if (!output_lines && !output_words && !output_chars) {
        output_lines = output_chars = output_words = true;
    }

    Vector<String> files = args.get_single_values();
    Vector<Count> counts;
    int status;
    if (files.is_empty()) {
        status = wc("", counts);
        if (status != 0)
            return status;
    } else {
        for (const auto& f : files) {
            status = wc(f, counts);
            if (status != 0)
                return status;
        }
    }
    report(counts);
    return 0;
}

int wc(const String& filename, Vector<Count>& counts)
{
    FILE* fp = nullptr;
    if (filename == "" || filename == "-") {
        fp = stdin;
    } else {
        fp = fopen(filename.characters(), "r");
        if (fp == nullptr) {
            fprintf(stderr, "wc: Could not open file '%s'\n", filename.characters());
            return 1;
        }
    }

    Count count { filename };
    char* line = nullptr;
    size_t len = 0;
    ssize_t n_read = 0;
    while ((n_read = getline(&line, &len, fp)) != -1) {
        count.lines++;
        count.words += count_words(line);
        count.chars += n_read;
    }

    counts.append(count);
    fclose(fp);
    if (line) {
        free(line);
    }
    return 0;
}