138 lines
3.9 KiB
C
138 lines
3.9 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <fnmatch.h>
|
|
|
|
typedef struct { long lines, words, chars; } Counts;
|
|
|
|
static int show_lines, show_words, show_chars;
|
|
|
|
static void count_file(const char *path, Counts *c) {
|
|
FILE *f = fopen(path, "r");
|
|
if (!f) { perror(path); return; }
|
|
int ch, in_word = 0;
|
|
while ((ch = fgetc(f)) != EOF) {
|
|
c->chars++;
|
|
if (ch == '\n') c->lines++;
|
|
if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') {
|
|
in_word = 0;
|
|
} else if (!in_word) {
|
|
in_word = 1;
|
|
c->words++;
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
static void print_counts(const Counts *c, const char *label) {
|
|
if (show_lines) printf(" %7ld", c->lines);
|
|
if (show_words) printf(" %7ld", c->words);
|
|
if (show_chars) printf(" %7ld", c->chars);
|
|
printf(" %s\n", label);
|
|
}
|
|
|
|
static void walk(const char *dir, const char **pats, int npat, Counts *total, int *n) {
|
|
DIR *d = opendir(dir);
|
|
if (!d) { perror(dir); return; }
|
|
struct dirent *e;
|
|
while ((e = readdir(d))) {
|
|
if (e->d_name[0] == '.') continue;
|
|
char path[4096];
|
|
snprintf(path, sizeof(path), "%s/%s", dir, e->d_name);
|
|
struct stat st;
|
|
if (stat(path, &st) < 0) continue;
|
|
if (S_ISDIR(st.st_mode)) {
|
|
walk(path, pats, npat, total, n);
|
|
} else if (S_ISREG(st.st_mode)) {
|
|
for (int i = 0; i < npat; i++) {
|
|
if (fnmatch(pats[i], e->d_name, 0) == 0) {
|
|
Counts c = {0};
|
|
count_file(path, &c);
|
|
print_counts(&c, path);
|
|
total->lines += c.lines;
|
|
total->words += c.words;
|
|
total->chars += c.chars;
|
|
(*n)++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir(d);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int recursive = 0;
|
|
const char **pats = NULL;
|
|
int npat = 0;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
if (argv[i][0] == '-') {
|
|
for (char *f = argv[i] + 1; *f; f++) {
|
|
switch (*f) {
|
|
case 'l': show_lines = 1; break;
|
|
case 'w': show_words = 1; break;
|
|
case 'c': show_chars = 1; break;
|
|
case 'r': recursive = 1; break;
|
|
default: fprintf(stderr, "unknown flag: -%c\n", *f); return 1;
|
|
}
|
|
}
|
|
} else {
|
|
pats = realloc(pats, (npat + 1) * sizeof(char *));
|
|
pats[npat++] = argv[i];
|
|
}
|
|
}
|
|
|
|
if (!show_lines && !show_words && !show_chars)
|
|
show_lines = show_words = show_chars = 1;
|
|
|
|
if (npat == 0) {
|
|
fprintf(stderr, "usage: wcr [-lwcr] pattern...\n");
|
|
return 1;
|
|
}
|
|
|
|
Counts total = {0};
|
|
int n = 0;
|
|
|
|
if (recursive) {
|
|
/* For each pattern, split into dir + basename and walk from dir */
|
|
for (int i = 0; i < npat; i++) {
|
|
char tmp[4096];
|
|
strncpy(tmp, pats[i], sizeof(tmp) - 1);
|
|
tmp[sizeof(tmp) - 1] = '\0';
|
|
char *slash = strrchr(tmp, '/');
|
|
const char *dir, *base;
|
|
char dirbuf[4096];
|
|
if (slash) {
|
|
*slash = '\0';
|
|
dir = tmp;
|
|
base = slash + 1;
|
|
} else {
|
|
dir = ".";
|
|
base = pats[i];
|
|
}
|
|
strncpy(dirbuf, dir, sizeof(dirbuf) - 1);
|
|
const char *bp = base;
|
|
walk(dirbuf, &bp, 1, &total, &n);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < npat; i++) {
|
|
Counts c = {0};
|
|
count_file(pats[i], &c);
|
|
print_counts(&c, pats[i]);
|
|
total.lines += c.lines;
|
|
total.words += c.words;
|
|
total.chars += c.chars;
|
|
n++;
|
|
}
|
|
}
|
|
|
|
if (n > 1)
|
|
print_counts(&total, "total");
|
|
|
|
free(pats);
|
|
return 0;
|
|
}
|