diff options
Diffstat (limited to 'src/commands/wc.c')
-rw-r--r-- | src/commands/wc.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/src/commands/wc.c b/src/commands/wc.c new file mode 100644 index 0000000..7132370 --- /dev/null +++ b/src/commands/wc.c @@ -0,0 +1,161 @@ +#include "../command.h" + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +struct Flags { + bool newlines; + bool words; + bool characters; + bool bytes; + bool longest_line; +}; + +static int lines = 0; +static int words = 0; +static int chars = 0; +static int bytes = 0; +static int logst = 0; + +static void list(int l, int w, int c, int b, int lg, struct Flags* flags) { + if (flags->newlines) { + printf("\t%d", l); + } + if (flags->words) { + printf("\t%d", w); + } + if (flags->characters) { + printf("\t%d", c); + } + if (flags->bytes) { + printf("\t%d", b); + } + if (flags->longest_line) { + printf("\t%d", lg); + } +} + +#define BS 1024 + +static bool is_delimiter(char c) { + return c == ' ' || c == '\t' || c == '\n'; +} + +static void run_wc(FILE* file, struct Flags* flags) { + int l = 0, w = 0, c = 0, b = 0, lg = 0; + + bool in_word = false; + int current_length = 0; + + int read; + char buf[BS]; + while ((read = fread(buf, 1, 1024, file)) > 0) { + for (int i = 0; i < read; i++) { + char ch = buf[i]; + b++; + if (ch == '\n') { + l++; + lg = lg > current_length ? lg : current_length; + current_length = 0; + } else { + current_length++; + } + if (isprint(ch) || is_delimiter(ch)) c++; + if (in_word && is_delimiter(ch)) { + in_word = false; + w++; + } else if (!in_word && !is_delimiter(ch) && isprint(ch)) { + in_word = true; + } + } + } + if (in_word) w++; + lg = lg > current_length ? lg : current_length; + list(l, w, c, b, lg, flags); + lines += l; + words += w; + chars += c; + bytes += b; + logst += lg; + if (file != stdin) + fclose(file); +} + +static void help() { + printf("Usage: wc [-cmlwL] [FILE]...\n\n"); + printf("Count lines, words, and bytes for FILEs (or stdin)\n\n"); + printf("\t-c Count bytes\n"); + printf("\t-m Count characters\n"); + printf("\t-l Count newlines\n"); + printf("\t-w Count words\n"); + printf("\t-L Print longest line length\n"); + exit(EXIT_SUCCESS); +} + +COMMAND(wc) { + struct Flags flags; + flags.newlines = false; + flags.words = false; + flags.characters = false; + flags.bytes = false; + flags.longest_line = false; + + bool has_flags = false; + + int start = 0; + for (int i = 0; i < argc; i++) { + if (!prefix("-", argv[i])) break; + if (streql("--help", argv[i])) help(); + + start++; + for (size_t j = 1; j < strlen(argv[i]); j++) { + char c = argv[i][j]; + switch (c) { + case 'c': + flags.bytes = true; + break; + case 'm': + flags.characters = true; + break; + case 'l': + flags.newlines = true; + break; + case 'w': + flags.words = true; + break; + case 'L': + flags.longest_line = true; + break; + default: + error("error: invald option -%c", c); + } + has_flags = true; + } + } + + if (!has_flags) { + flags.newlines = true; + flags.words = true; + flags.characters = true; + } + + if (argc - start < 1) { + run_wc(stdin, &flags); + printf("\n"); + return EXIT_SUCCESS; + } + + for (int i = start; i < argc; i++) { + FILE* file = get_file(argv[i], "r"); + run_wc(file, &flags); + printf("\t%s\n", argv[i]); + } + + if (argc - start > 1) { + list(lines, words, chars, bytes, logst, &flags); + printf("\ttotal\n"); + } + + return EXIT_SUCCESS; +} |