diff options
| author | Mattias Andrée <m@maandree.se> | 2026-05-23 20:53:27 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-05-23 21:09:19 +0200 |
| commit | 41305ac7747516f86d03d0b750816c02e624ac4c (patch) | |
| tree | 152c00464676b95fdbb60ed29dd6526bbaed93cf /semicolons.c | |
| download | semicolons-1.0.tar.gz semicolons-1.0.tar.bz2 semicolons-1.0.tar.xz | |
First commit1.0
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to '')
| -rw-r--r-- | semicolons.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/semicolons.c b/semicolons.c new file mode 100644 index 0000000..6a9fbbc --- /dev/null +++ b/semicolons.c @@ -0,0 +1,199 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <fcntl.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> + +#include "arg.h" + + +struct result { + ssize_t n; + int width; +}; + + +char *argv0; + + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-s] [file] ...\n", argv0); + exit(1); +} + + +static ssize_t +count(int fd, const char *fname) +{ + char buf[BUFSIZ], c; + ssize_t i, n, ret = 0; + char quote = 0; + char comment = 0; + signed char escape = 0; + signed char slash = 0; + signed char asterisk = 0; + + for (;;) { + n = read(fd, buf, sizeof(buf)); + if (n <= 0) { + if (!n) + break; + if (errno == EINTR) + continue; + fprintf(stderr, "%s: %s :%s\n", argv0, fname, strerror(errno)); + return -1; + } + + for (i = 0; i < n; i++) { + c = buf[i]; + + again: + if (slash) { + slash = 0; + if (c == '*' || c == '/') + comment = c; + else + goto again; + } else if (comment == '/') { + if (c == '\n') + comment = 0; + } else if (comment == '*') { + if (c == '*') { + asterisk = 1; + } else if (asterisk) { + asterisk = 0; + if (c == '/') + comment = 0; + } + } else if (escape) { + escape = 0; + } else if (quote) { + if (c == '\\') + escape = 1; + else if (c == quote) + quote = 0; + } else if (c == '/') { + slash = 1; + } else if (c == '"' || c == '\'') { + quote = c; + } else if (c == ';') { + ret += 1; + } + } + } + + return ret; +} + + +static ssize_t +open_and_count(const char *path) +{ + ssize_t r; + int fd; + + if (!path || !strcmp(path, "-")) { + r = count(STDIN_FILENO, "<stdin>"); + } else { + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); + return -1; + } + r = count(fd, path); + close(fd); + } + + return r; +} + + +static int +strwidth(const char *s) +{ + size_t wn, n = strlen(s) + 1u; + wchar_t *ws = calloc(n, sizeof(*ws)); + int r = -1; + + if (!ws) { + fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); + exit(1); + } + + wn = mbstowcs(ws, s, n); + if (wn != (size_t)-1) + r = wcswidth(ws, wn); + + free(ws); + return r < 0 ? (int)n - 1 : r; +} + + +int +main(int argc, char *argv[]) +{ + struct result *results; + int sum_only = 0; + int ret = 0; + ssize_t r; + size_t total = 0u; + int i, left, right; + int maxwidth = 0; + + ARGBEGIN { + case 's': + sum_only = 1; + break; + default: + usage(); + } ARGEND; + + setlocale(LC_ALL, ""); + + if (argc < 2) { + r = open_and_count(argv[0]); + if (r < 0) + return 1; + printf("%zi\n", r); + } else { + results = calloc((size_t)argc, sizeof(*results)); + if (!results) { + fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); + exit(1); + } + for (i = 0; i < argc; i++) { + results[i].n = open_and_count(argv[0]); + if (results[i].n < 0) { + ret = 1; + continue; + } + left = strwidth(argv[i]); + right = snprintf(NULL, 0u, "%zi", results[i].n); + results[i].width = left + right; + if (maxwidth < results[i].width) + maxwidth = results[i].width; + } + for (i = 0; i < argc; i++) { + if (results[i].n < 0) + continue; + total += (size_t)results[i].n; + if (sum_only) + continue; + printf("%s:%*s %zi\n", argv[i], maxwidth - results[i].width, "", results[i].n); + } + free(results); + printf("%zu\n", total); + } + + if (fflush(stdout) || ferror(stdout) || fclose(stdout)) { + fprintf(stderr, "%s: <stdout>: %s\n", argv0, strerror(errno)); + exit(1); + } + return ret; +} |
