#include #include #include #include #include #include #include #include #include #include #include #include "util/debug.h" #include "util/error.h" #include "util/loop_files.h" #include "util/map.h" #include "util/versie.h" #include "io/read_file.h" static int modeMap; struct finfo { size_t values[3]; char *fname; }; static struct finfo *finfo = NULL; static size_t finfolen = 0; static size_t finfocap = 0; static void usage(FILE *f) { fprintf(f, "Gebruik: toilet [-nchV] [BESTAND]...\n" "\n" "Toon de hoeveelheid regels, woorden en beten for elk BESTAND\n" "\n" " -c Geef het aantal beten weer\n" " -w Geef het aantal woorden weer\n" " -l Geef het aantal regels weer\n" " -h Toon deze hulptekst\n" " -V Toon versienummer\n"); } enum MODE { M_LINES = 1 << 0, M_WORDS = 1 << 1, M_BYTES = 1 << 2, }; // Returns pointer to argument array containing the file names static char** parse_options(int argc, char **argv, int *modeMap) { int opt; while ((opt = getopt(argc, argv, "cwlhV")) != -1) { switch (opt) { case 'c': *modeMap |= M_BYTES; break; case 'w': *modeMap |= M_WORDS; break; case 'l': *modeMap |= M_LINES; break; case 'h': usage(stdout); exit(0); case 'V': drukkedoos_print_versie(stdout); exit(0); case '?': fprintf(stderr, "toilet: Ongeldige optie: -%c\n", optopt); usage(stderr); exit(1); } } return argv + optind; } static size_t get_count(enum MODE mode, struct filebuf *fb) { switch (mode) { case M_BYTES: return fb->sz; case M_WORDS: { #define IN(i) (i < fb->sz) size_t words = 0; // (c) Tom Forging for (size_t i = 0; IN(i);) { size_t previ = i; while (IN(i) && !isspace(fb->buf[i])) i++; words += i != previ; while (IN(i) && isspace(fb->buf[i])) i++; } return words; #undef IN } case M_LINES: { size_t lines = 0; size_t i = 0; while (i != fb->sz) { if (fb->buf[i] == '\n') lines++; i++; } // handle case if file does not have trailing newline if (fb->buf[i - 1] != '\n') { lines++; } return lines; } default: assert(false); } } static int mlog2(int val) { #define C(n) case (1 << (n)): return (n); switch (val) { C(0) C(1) C(2) } assert(false); #undef C } static void init_finfo(int argc) { assert(finfo == NULL); argc += 1; // one more for the total tally if (argc < 8) argc = 8; finfo = calloc(argc, sizeof(struct finfo)); finfocap = argc; } static struct finfo *add_fname(char *fname) { assert(finfo != NULL); if (finfolen == finfocap) { finfocap *= 2; finfo = realloc(finfo, sizeof(struct finfo)*finfocap); } struct finfo *ptr = finfo + finfolen; ptr->values[0] = 0; ptr->values[1] = 0; ptr->values[2] = 0; ptr->fname = fname; finfolen++; return ptr; } static int process(struct filebuf *fb, char *fname, bool) { struct finfo *finfo = add_fname(fname); for (enum MODE mode = 1; mode <= M_BYTES; mode <<= 1) { if (mode & modeMap) { finfo->values[mlog2(mode)] = get_count(mode, fb); } } free_filebuf(fb); return 0; } // TODO: be smarter, toilet doesn't have to read the whole file in memory (for // unmappable files) int entry_toilet(int argc, char **argv) { init_finfo(argc); modeMap = 0; char **args = parse_options(argc, argv, &modeMap); if (modeMap == 0) { modeMap = INT_MAX; } const int res = loop_files(args, process); // tally file totals { struct finfo *ptr = add_fname("total"); for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < finfolen; j++) { ptr->values[i] += finfo[j].values[i]; } } } // calculate required padding int pad[3] = {0}; for (size_t i = 0; i < finfolen; i++) { for (size_t j = 0; j < 3; j++) { const int sz = ceill(log10l(finfo[i].values[j])) + 1; if (sz > pad[j]) pad[j] = sz; } } // print all results for (size_t i = 0; i < finfolen; i++) { struct finfo *ptr = finfo + i; for (size_t j = 0; j < 3; j++) { printf("% *li ", pad[j], ptr->values[j]); } printf("%s\n", ptr->fname); } return res; }