瀏覽代碼

Userland: Add wc program (#228)

Fixes #159.
Callum Attryde 6 年之前
父節點
當前提交
267672efee
共有 3 個文件被更改,包括 174 次插入0 次删除
  1. 44 0
      LibC/stdio.cpp
  2. 2 0
      LibC/stdio.h
  3. 128 0
      Userland/wc.cpp

+ 44 - 0
LibC/stdio.cpp

@@ -155,6 +155,50 @@ int getchar()
     return getc(stdin);
 }
 
+ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
+{
+    char *ptr, *eptr;
+    if (*lineptr == nullptr || *n == 0) {
+        *n = BUFSIZ;
+        if ((*lineptr = static_cast<char*>(malloc(*n))) == nullptr) {
+            return -1;
+        }
+    }
+
+    for (ptr = *lineptr, eptr = *lineptr + *n;;) {
+        int c = fgetc(stream);
+        if (c == -1) {
+            if (feof(stream)) {
+                return ptr == *lineptr ? -1 : ptr - *lineptr;
+            } else {
+                return -1;
+            }
+        }
+        *ptr++ = c;
+        if (c == delim) {
+            *ptr = '\0';
+            return ptr - *lineptr;
+        }
+        if (ptr + 2 >= eptr) {
+            char *nbuf;
+            size_t nbuf_sz = *n * 2;
+            ssize_t d = ptr - *lineptr;
+            if ((nbuf = static_cast<char*>(realloc(*lineptr, nbuf_sz))) == nullptr) {
+                return -1;
+            }
+            *lineptr = nbuf;
+            *n = nbuf_sz;
+            eptr = nbuf + nbuf_sz;
+            ptr = nbuf + d;
+        }
+    }
+}
+
+ssize_t getline(char **lineptr, size_t *n, FILE *stream)
+{
+    return getdelim(lineptr, n, '\n', stream);
+}
+
 int ungetc(int c, FILE* stream)
 {
     ASSERT(stream);

+ 2 - 0
LibC/stdio.h

@@ -57,6 +57,8 @@ int fileno(FILE*);
 int fgetc(FILE*);
 int getc(FILE*);
 int getchar();
+ssize_t getdelim(char**, size_t*, int, FILE*);
+ssize_t getline(char**, size_t*, FILE*);
 int ungetc(int c, FILE*);
 int remove(const char* pathname);
 FILE* fdopen(int fd, const char* mode);

+ 128 - 0
Userland/wc.cpp

@@ -0,0 +1,128 @@
+#include <AK/AKString.h>
+#include <AK/Vector.h>
+#include <LibCore/CArgsParser.h>
+
+#include <ctype.h>
+#include <stdio.h>
+
+bool output_chars = false;
+bool output_words = false;
+bool output_lines = false;
+
+struct Count {
+    String file;
+    unsigned long chars = 0;
+    unsigned long words = 0;
+    unsigned long lines = 0;
+};
+
+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...]");
+        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, (const 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();
+    if (files.is_empty()) {
+        fprintf(stderr, "wc: No files provided");
+        return 1;
+    }
+
+    Vector<Count> counts;
+    for (const auto& f : files) {
+        FILE* fp = fopen(f.characters(), "r");
+        if (fp == nullptr) {
+            fprintf(stderr, "wc: Could not open file '%s'\n", f.characters());
+            return 1;
+        }
+
+        Count count { f };
+        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);
+        }
+    }
+
+    report(counts);
+    return 0;
+}