/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include "arg.h" char *argv0; struct result { ssize_t n; int width; }; static void usage(void) { fprintf(stderr, "usage: %s [file] ...\n", argv0); exit(1); } static ssize_t count(int fd, const char *fname) { char buf[BUFSIZ]; ssize_t i, n, ret = 0; int beginning = 1; int escaped = 0; int slash_asterisk = 0; int cxx_comment = 0; int c_comment = 0; char quote = 0; for (;;) { n = read(fd, buf, sizeof(buf)); if (n <= 0) { if (!n) break; fprintf(stderr, "%s: %s: %s\n", argv0, fname, strerror(errno)); return -1; } for (i = 0; i < n; i++) { if (quote) { if (buf[i] == '\n') { ret += (ssize_t)!beginning; beginning = 1; escaped = 0; continue; } else if (escaped) { escaped = 0; } else if (buf[i] == quote) { quote = 0; } else if (buf[i] == '\\') { escaped = 1; } beginning = 0; } else if (c_comment) { if (buf[i] == '\n') { ret += (ssize_t)!beginning; beginning = 1; } else if (buf[i] == '*') { slash_asterisk = 1; } else if (slash_asterisk) { slash_asterisk = 0; if (buf[i] == '/') c_comment = 0; } } else if (cxx_comment) { if (escaped) { escaped = 0; } else if (buf[i] == '\n') { ret += (ssize_t)!beginning; beginning = 1; cxx_comment = 0; } else if (buf[i] == '\\') { escaped = 1; } } else if (slash_asterisk && buf[i] == '/') { cxx_comment = 1; slash_asterisk = 0; } else if (slash_asterisk && buf[i] == '*') { c_comment = 1; slash_asterisk = 0; } else { slash_asterisk = 0; if (buf[i] == '\n') { ret += (ssize_t)!beginning; beginning = 1; escaped = 0; } else if (strchr("{}()[]\t\f\r\v; ", buf[i])) { escaped = 0; } else if (escaped) { beginning = 0; escaped = 0; } else if (buf[i] == '\'' || buf[i] == '"') { beginning = 0; quote = buf[i]; } else if (buf[i] == '/') { slash_asterisk = 1; } else if (buf[i] == '\\') { escaped = 1; } else { beginning = 0; } } } } return ret + (ssize_t)!beginning; } static int strwidth(const char *str) { size_t n = strlen(str) + 1; wchar_t *wcs = calloc(n, sizeof(*wcs)); int r = -1; if (!wcs) fprintf(stderr, "%s: out of memory\n", argv0), exit(1); if (mbstowcs(wcs, str, n) != (size_t)-1) r = wcswidth(wcs, n); free(wcs); return r < 0 ? (int)(n - 1) : r; } static int zuwidth(size_t num) { char buf[3 * sizeof(num) + 1]; return sprintf(buf, "%zu", num); } static int xopen(const char *path, int flags, int *closep) { int fd; const char *num; unsigned long int tmp; *closep = 0; if (!strcmp(path, "/dev/stdin")) return STDIN_FILENO; if (!strcmp(path, "/dev/stdout")) return STDOUT_FILENO; if (!strcmp(path, "/dev/stderr")) return STDERR_FILENO; if (strncmp(path, "/dev/fd/", sizeof("/dev/fd/") - 1)) { num = &path[sizeof("/dev/fd/") - 1]; goto inherited; } if (strncmp(path, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1)) { num = &path[sizeof("/proc/self/fd/") - 1]; goto inherited; } normal: fd = open(path, flags); if (fd < 0) { fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); return -1; } *closep = 1; return fd; inherited: if (!isdigit(*num)) goto normal; errno = 0; tmp = strtoul(num, (void *)&num, 10); if (errno || *num || tmp > (unsigned long int)INT_MAX) goto normal; return (int)tmp; } int main(int argc, char *argv[]) { ssize_t n; struct result *res; int i, fd, doclose, maxleft = 0, maxright = 0, maxwidth, left, right, ret = 0; ARGBEGIN { default: usage(); } ARGEND; if (argc < 2) { if (!argc || !strcmp(argv[0], "-")) { n = count(STDIN_FILENO, ""); } else { fd = xopen(argv[0], O_RDONLY, &doclose); if (fd < 0) return 1; n = count(fd, argv[0]); if (doclose) close(fd); } printf("%zi\n", n); } else { if (!(res = calloc(argc, sizeof(struct result)))) fprintf(stderr, "%s: out of memory\n", argv0), exit(1); for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-")) { res[i].n = count(STDIN_FILENO, ""); } else if ((fd = xopen(argv[i], O_RDONLY, &doclose)) < 0) { res[i].n = -1; } else { res[i].n = count(fd, argv[i]); if (doclose) close(fd); } if (res[i].n >= 0) { left = strwidth(argv[i]); if (left > maxleft) maxleft = left; right = zuwidth(res[i].n); if (right > maxright) maxright = right; res[i].width = left + right; } } maxwidth = maxleft + maxright; for (i = 0; i < argc; i++) if (res[i].n < 0) ret = 1; else printf("%s:%*s %zi\n", argv[i], maxwidth - res[i].width, "", res[i].n); free(res); } if (fflush(stdin) || ferror(stdin) || fclose(stdin)) fprintf(stderr, "%s: : %s\n", argv0, strerror(errno)), exit(1); return ret; }