From 6c62712009faf085f632607989d0263be61c126b Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Mon, 19 Feb 2018 18:44:13 +0100 Subject: First commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 6 ++ LICENSE | 15 ++++ arg.h | 88 +++++++++++++++++++++++ loc.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 349 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 arg.h create mode 100644 loc.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16fae82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*~ +\#*\# +*.o +*.out +*.su +/loc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b339294 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +© 2018 Mattias Andrée + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/arg.h b/arg.h new file mode 100644 index 0000000..b280e69 --- /dev/null +++ b/arg.h @@ -0,0 +1,88 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][0] == '-') {\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGALT(SYMBOL) }\ + } else if (argv[0][0] == SYMBOL) {\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +#define ARGEND }\ + } else {\ + break;\ + }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define UARGF() EARGF(usage()) + +#define LNGARG() &argv[0][0] + +#define UNOFLAGS(...) ARGBEGIN {\ + default:\ + usage();\ + } ARGEND;\ + if (__VA_ARGS__)\ + usage() + + +#endif diff --git a/loc.c b/loc.c new file mode 100644 index 0000000..4434a19 --- /dev/null +++ b/loc.c @@ -0,0 +1,240 @@ +/* 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; + } else if (escaped) { + escaped = 0; + } else if (buf[i] == quote) { + quote = 0; + } else if (buf[i] == '\\') { + escaped = 1; + } + } 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; +} -- cgit v1.2.3-70-g09d2