160 lines
3.7 KiB
C
160 lines
3.7 KiB
C
|
#include "../command.h"
|
||
|
|
||
|
#include <ctype.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;
|
||
|
}
|