/* Copyright (C) 2021 C. McEnroe * * 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 * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include static char *nomagic(const char *pattern) { char *buf = malloc(2 * strlen(pattern) + 1); if (!buf) err(EX_OSERR, "malloc"); char *ptr = buf; for (const char *ch = pattern; *ch; ++ch) { if (strchr(".[*", *ch)) *ptr++ = '\\'; *ptr++ = *ch; } *ptr = '\0'; return buf; } static size_t escape(bool esc, const char *ptr, size_t len) { if (!esc) { fwrite(ptr, len, 1, stdout); return len; } for (size_t i = 0; i < len; ++i) { switch (ptr[i]) { break; case '&': printf("&"); break; case '<': printf("<"); break; case '"': printf("""); break; default: putchar(ptr[i]); } } return len; } static char *hstrstr(const char *haystack, const char *needle) { while (haystack) { char *elem = strchr(haystack, '<'); char *match = strstr(haystack, needle); if (!match) return NULL; if (!elem || match < elem) return match; haystack = strchr(elem, '>'); } return NULL; } int main(int argc, char *argv[]) { bool pre = false; bool pipe = false; bool index = false; const char *tagsFile = "tags"; for (int opt; 0 < (opt = getopt(argc, argv, "f:ipx"));) { switch (opt) { break; case 'f': tagsFile = optarg; break; case 'i': pipe = true; break; case 'p': pre = true; break; case 'x': index = true; break; default: return EX_USAGE; } } 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); size_t len = 0; size_t cap = 256; struct Tag { char *tag; int num; regex_t regex; } *tags = malloc(cap * sizeof(*tags)); if (!tags) err(EX_OSERR, "malloc"); char *buf = NULL; size_t bufCap = 0; while (0 < getline(&buf, &bufCap, file)) { char *line = buf; char *tag = strsep(&line, "\t"); char *file = strsep(&line, "\t"); char *def = strsep(&line, "\n"); if (!tag || !file || !def) errx(EX_DATAERR, "malformed tags file"); if (strcmp(file, name)) continue; if (len == cap) { tags = realloc(tags, (cap *= 2) * sizeof(*tags)); if (!tags) err(EX_OSERR, "realloc"); } tags[len].tag = strdup(tag); if (!tags[len].tag) err(EX_OSERR, "strdup"); tags[len].num = 0; 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; } } else { tags[len].num = strtol(def, &def, 10); if (*def) { warnx("invalid line number for tag %s: %s", tag, def); continue; } } len++; } fclose(file); file = fopen(name, "r"); if (!file) err(EX_NOINPUT, "%s", name); int num = 0; printf(pre ? "
" : index ? "
" : index ? "\n" : ""); }