summary refs log tree commit diff
path: root/bin/htagml.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/htagml.c')
-rw-r--r--bin/htagml.c94
1 files changed, 55 insertions, 39 deletions
diff --git a/bin/htagml.c b/bin/htagml.c
index eb5128d1..1f547be6 100644
--- a/bin/htagml.c
+++ b/bin/htagml.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -16,7 +16,6 @@
 
 #include <ctype.h>
 #include <err.h>
-#include <regex.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -24,13 +23,18 @@
 #include <sysexits.h>
 #include <unistd.h>
 
-static char *nomagic(const char *pattern) {
-	char *buf = malloc(2 * strlen(pattern) + 1);
+static char *deregex(const char *patt) {
+	char *buf = malloc(strlen(patt) + 1);
 	if (!buf) err(EX_OSERR, "malloc");
 	char *ptr = buf;
-	for (const char *ch = pattern; *ch; ++ch) {
-		if (strchr(".[*", *ch)) *ptr++ = '\\';
-		*ptr++ = *ch;
+	if (*patt == '^') patt++;
+	for (; *patt; ++patt) {
+		if (patt[0] == '$' && !patt[1]) {
+			*ptr++ = '\n';
+			break;
+		}
+		if (patt[0] == '\\' && patt[1]) patt++;
+		*ptr++ = *patt;
 	}
 	*ptr = '\0';
 	return buf;
@@ -73,15 +77,21 @@ static char *hstrstr(const char *haystack, const char *needle) {
 	return NULL;
 }
 
+static int isident(int c) {
+	return isalnum(c) || c == '_';
+}
+
 int main(int argc, char *argv[]) {
 	bool pre = false;
 	bool pipe = false;
+	bool main = false;
 	bool index = false;
-	const char *tagsFile = "tags";
-	for (int opt; 0 < (opt = getopt(argc, argv, "f:ipx"));) {
+	const char *tagsPath = "tags";
+	for (int opt; 0 < (opt = getopt(argc, argv, "f:impx"));) {
 		switch (opt) {
-			break; case 'f': tagsFile = optarg;
+			break; case 'f': tagsPath = optarg;
 			break; case 'i': pipe = true;
+			break; case 'm': main = true;
 			break; case 'p': pre = true;
 			break; case 'x': index = true;
 			break; default:  return EX_USAGE;
@@ -90,21 +100,30 @@ int main(int argc, char *argv[]) {
 	if (optind == argc) errx(EX_USAGE, "name required");
 	const char *name = argv[optind];
 
-	FILE *file = fopen(tagsFile, "r");
-	if (!file) err(EX_NOINPUT, "%s", tagsFile);
+	FILE *file = fopen(name, "r");
+	if (!file) err(EX_NOINPUT, "%s", name);
+
+	FILE *tagsFile = fopen(tagsPath, "r");
+	if (!tagsFile) err(EX_NOINPUT, "%s", tagsPath);
+
+#ifdef __OpenBSD__
+	int error = pledge("stdio", NULL);
+	if (error) err(EX_OSERR, "pledge");
+#endif
 
 	size_t len = 0;
 	size_t cap = 256;
 	struct Tag {
 		char *tag;
 		int num;
-		regex_t regex;
+		char *str;
+		size_t len;
 	} *tags = malloc(cap * sizeof(*tags));
 	if (!tags) err(EX_OSERR, "malloc");
 
 	char *buf = NULL;
 	size_t bufCap = 0;
-	while (0 < getline(&buf, &bufCap, file)) {
+	while (0 < getline(&buf, &bufCap, tagsFile)) {
 		char *line = buf;
 		char *tag = strsep(&line, "\t");
 		char *file = strsep(&line, "\t");
@@ -123,15 +142,11 @@ int main(int argc, char *argv[]) {
 		if (def[0] == '/' || def[0] == '?') {
 			def++;
 			def[strlen(def)-1] = '\0';
-			char *search = nomagic(def);
-			int error = regcomp(
-				&tags[len].regex, search, REG_NEWLINE | REG_NOSUB
-			);
-			free(search);
-			if (error) {
-				warnx("invalid regex for tag %s: %s", tag, def);
-				continue;
+			if (def[0] != '^') {
+				warnx("unanchored regex for tag %s: %s", tag, def);
 			}
+			tags[len].str = deregex(def);
+			tags[len].len = strlen(tags[len].str);
 		} else {
 			tags[len].num = strtol(def, &def, 10);
 			if (*def) {
@@ -141,31 +156,28 @@ int main(int argc, char *argv[]) {
 		}
 		len++;
 	}
-	fclose(file);
-
-	file = fopen(name, "r");
-	if (!file) err(EX_NOINPUT, "%s", name);
+	fclose(tagsFile);
 
 	int num = 0;
 	printf(pre ? "<pre>" : index ? "<ul class=\"index\">\n" : "");
 	while (0 < getline(&buf, &bufCap, file) && ++num) {
-		struct Tag *tag = NULL;
+		char *tag = NULL;
 		for (size_t i = 0; i < len; ++i) {
 			if (tags[i].num) {
 				if (num != tags[i].num) continue;
 			} else {
-				if (regexec(&tags[i].regex, buf, 0, NULL, 0)) continue;
+				if (strncmp(tags[i].str, buf, tags[i].len)) continue;
 			}
-			tag = &tags[i];
-			tag->num = num;
+			tag = tags[i].tag;
+			tags[i] = tags[--len];
 			break;
 		}
 		if (index) {
 			if (!tag) continue;
 			printf("<li><a class=\"tag\" href=\"#");
-			id(tag->tag);
+			id(tag);
 			printf("\">");
-			escape(true, tag->tag, strlen(tag->tag));
+			escape(true, tag, strlen(tag));
 			printf("</a></li>\n");
 			continue;
 		}
@@ -180,14 +192,18 @@ int main(int argc, char *argv[]) {
 			continue;
 		}
 
-		size_t mlen = strlen(tag->tag);
-		char *match = (pipe ? hstrstr : strstr)(buf, tag->tag);
-		while (match > buf && isalnum(match[-1])) {
-			match = (pipe ? hstrstr : strstr)(&match[mlen], tag->tag);
+		size_t mlen = strlen(tag);
+		char *match = (pipe ? hstrstr : strstr)(buf, tag);
+		while (
+			match &&
+			((match > buf && isident(match[-1])) || isident(match[mlen]))
+		) {
+			match = (pipe ? hstrstr : strstr)(&match[mlen], tag);
 		}
-		if (!match && tag->tag[0] == 'M') {
+		if (!match && tag[0] == 'M') {
 			mlen = 4;
 			match = (pipe ? hstrstr : strstr)(buf, "main");
+			if (main) tag = "main";
 		}
 		if (!match) {
 			mlen = strlen(buf) - 1;
@@ -195,9 +211,9 @@ int main(int argc, char *argv[]) {
 		}
 		escape(!pipe, buf, match - buf);
 		printf("<a class=\"tag\" id=\"");
-		id(tag->tag);
+		id(tag);
 		printf("\" href=\"#");
-		id(tag->tag);
+		id(tag);
 		printf("\">");
 		match += escape(!pipe, match, mlen);
 		printf("</a>");