aboutsummaryrefslogtreecommitdiffstats
path: root/semicolons.c
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2026-05-23 20:53:27 +0200
committerMattias Andrée <m@maandree.se>2026-05-23 21:09:19 +0200
commit41305ac7747516f86d03d0b750816c02e624ac4c (patch)
tree152c00464676b95fdbb60ed29dd6526bbaed93cf /semicolons.c
downloadsemicolons-41305ac7747516f86d03d0b750816c02e624ac4c.tar.gz
semicolons-41305ac7747516f86d03d0b750816c02e624ac4c.tar.bz2
semicolons-41305ac7747516f86d03d0b750816c02e624ac4c.tar.xz
First commit1.0
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'semicolons.c')
-rw-r--r--semicolons.c199
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;
+}