/* $Id: tag.c,v 1.2 2015/07/18 03:41:37 schwarze Exp $ */ /* * Copyright (c) 2015 Ingo Schwarze * * Permission to use, copy, modify, and 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. */ #include #include #include #include #include #include #include #if HAVE_OHASH #include #else #include "compat_ohash.h" #endif #include "mandoc_aux.h" #include "tag.h" struct tag_entry { size_t line; char s[]; }; static void tag_signal(int); static void *tag_alloc(size_t, void *); static void tag_free(void *, void *); static void *tag_calloc(size_t, size_t, void *); static struct ohash tag_data; static char *tag_fn = NULL; static int tag_fd = -1; /* * Set up the ohash table to collect output line numbers * where various marked-up terms are documented and create * the temporary tags file, saving the name for the pager. */ void tag_init(void) { struct ohash_info tag_info; tag_fn = mandoc_strdup("/tmp/man.XXXXXXXXXX"); signal(SIGHUP, tag_signal); signal(SIGINT, tag_signal); signal(SIGTERM, tag_signal); if ((tag_fd = mkstemp(tag_fn)) == -1) { free(tag_fn); tag_fn = NULL; return; } tag_info.alloc = tag_alloc; tag_info.calloc = tag_calloc; tag_info.free = tag_free; tag_info.key_offset = offsetof(struct tag_entry, s); tag_info.data = NULL; ohash_init(&tag_data, 4, &tag_info); } char * tag_filename(void) { return(tag_fn); } /* * Return the line number where a term is defined, * or 0 if the term is unknown. */ size_t tag_get(const char *s, size_t len) { struct tag_entry *entry; const char *end; unsigned int slot; if (tag_fd == -1) return(0); if (len == 0) len = strlen(s); end = s + len; slot = ohash_qlookupi(&tag_data, s, &end); entry = ohash_find(&tag_data, slot); return(entry == NULL ? 0 : entry->line); } /* * Set the line number where a term is defined. */ void tag_put(const char *s, size_t len, size_t line) { struct tag_entry *entry; const char *end; unsigned int slot; if (tag_fd == -1) return; if (len == 0) len = strlen(s); end = s + len; slot = ohash_qlookupi(&tag_data, s, &end); entry = ohash_find(&tag_data, slot); if (entry == NULL) { entry = mandoc_malloc(sizeof(*entry) + len + 1); memcpy(entry->s, s, len); entry->s[len] = '\0'; ohash_insert(&tag_data, slot, entry); } entry->line = line; } /* * Write out the tags file using the previously collected * information and clear the ohash table while going along. */ void tag_write(void) { FILE *stream; struct tag_entry *entry; unsigned int slot; if (tag_fd == -1) return; stream = fdopen(tag_fd, "w"); entry = ohash_first(&tag_data, &slot); while (entry != NULL) { if (stream != NULL) fprintf(stream, "%s - %zu\n", entry->s, entry->line); free(entry); entry = ohash_next(&tag_data, &slot); } ohash_delete(&tag_data); if (stream != NULL) fclose(stream); } void tag_unlink(void) { if (tag_fn != NULL) unlink(tag_fn); } static void tag_signal(int signum) { tag_unlink(); signal(signum, SIG_DFL); kill(getpid(), signum); /* NOTREACHED */ _exit(1); } /* * Memory management callback functions for ohash. */ static void * tag_alloc(size_t sz, void *arg) { return(mandoc_malloc(sz)); } static void * tag_calloc(size_t nmemb, size_t sz, void *arg) { return(mandoc_calloc(nmemb, sz)); } static void tag_free(void *p, void *arg) { free(p); }