-/* $Id: tag.c,v 1.1 2015/07/17 22:38:29 schwarze Exp $ */
+/* $Id: tag.c,v 1.10 2015/10/13 15:53:05 schwarze Exp $ */
/*
* Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
*
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "config.h"
+
#include <sys/types.h>
+#include <signal.h>
#include <stddef.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#if HAVE_OHASH
-#include <ohash.h>
-#else
-#include "compat_ohash.h"
-#endif
-
#include "mandoc_aux.h"
+#include "mandoc_ohash.h"
#include "tag.h"
struct tag_entry {
size_t line;
+ int prio;
char s[];
};
-static void *tag_alloc(size_t, void *);
-static void tag_free(void *, void *);
-static void *tag_calloc(size_t, size_t, void *);
+static void tag_signal(int);
static struct ohash tag_data;
-static char *tag_fn = NULL;
-static int tag_fd = -1;
+static struct tag_files tag_files;
/*
- * 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.
+ * Prepare for using a pager.
+ * Not all pagers are capable of using a tag file,
+ * but for simplicity, create it anyway.
*/
-void
+struct tag_files *
tag_init(void)
{
- struct ohash_info tag_info;
-
- tag_fn = mandoc_strdup("/tmp/man.XXXXXXXXXX");
- 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);
+ int ofd;
+
+ ofd = -1;
+ tag_files.tfd = -1;
+
+ /* Save the original standard output for use by the pager. */
+
+ if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1)
+ goto fail;
+
+ /* Create both temporary output files. */
+
+ (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX",
+ sizeof(tag_files.ofn));
+ (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
+ sizeof(tag_files.tfn));
+ signal(SIGHUP, tag_signal);
+ signal(SIGINT, tag_signal);
+ signal(SIGTERM, tag_signal);
+ if ((ofd = mkstemp(tag_files.ofn)) == -1)
+ goto fail;
+ if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1)
+ goto fail;
+ if (dup2(ofd, STDOUT_FILENO) == -1)
+ goto fail;
+ close(ofd);
+
+ /*
+ * Set up the ohash table to collect output line numbers
+ * where various marked-up terms are documented.
+ */
+
+ mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
+ return &tag_files;
+
+fail:
+ tag_unlink();
+ if (ofd != -1)
+ close(ofd);
+ if (tag_files.ofd != -1)
+ close(tag_files.ofd);
+ if (tag_files.tfd != -1)
+ close(tag_files.tfd);
+ *tag_files.ofn = '\0';
+ *tag_files.tfn = '\0';
+ tag_files.ofd = -1;
+ tag_files.tfd = -1;
+ return NULL;
}
/*
- * Set the line number where a term is defined.
+ * Set the line number where a term is defined,
+ * unless it is already defined at a higher priority.
*/
void
-tag_put(const char *s, size_t len, size_t line)
+tag_put(const char *s, int prio, size_t line)
{
struct tag_entry *entry;
- const char *end;
+ size_t len;
unsigned int slot;
- if (tag_fd == -1)
+ if (tag_files.tfd <= 0 || strchr(s, ' ') != NULL)
return;
- if (len == 0)
- len = strlen(s);
- end = s + len;
- slot = ohash_qlookupi(&tag_data, s, &end);
+ slot = ohash_qlookup(&tag_data, s);
entry = ohash_find(&tag_data, slot);
if (entry == NULL) {
- entry = mandoc_malloc(sizeof(*entry) + len + 1);
+ len = strlen(s) + 1;
+ entry = mandoc_malloc(sizeof(*entry) + len);
memcpy(entry->s, s, len);
- entry->s[len] = '\0';
ohash_insert(&tag_data, slot, entry);
- }
+ } else if (entry->prio <= prio)
+ return;
entry->line = line;
+ entry->prio = prio;
}
/*
struct tag_entry *entry;
unsigned int slot;
- if (tag_fd == -1)
+ if (tag_files.tfd <= 0)
return;
- stream = fdopen(tag_fd, "w");
+ stream = fdopen(tag_files.tfd, "w");
entry = ohash_first(&tag_data, &slot);
while (entry != NULL) {
if (stream != NULL)
- fprintf(stream, "%s - %zu\n", entry->s, entry->line);
+ fprintf(stream, "%s %s %zu\n",
+ entry->s, tag_files.ofn, entry->line);
free(entry);
entry = ohash_next(&tag_data, &slot);
}
tag_unlink(void)
{
- if (tag_fn != NULL)
- unlink(tag_fn);
-}
-
-/*
- * 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));
+ if (*tag_files.ofn != '\0')
+ unlink(tag_files.ofn);
+ if (*tag_files.tfn != '\0')
+ unlink(tag_files.tfn);
}
static void
-tag_free(void *p, void *arg)
+tag_signal(int signum)
{
- free(p);
+ tag_unlink();
+ signal(signum, SIG_DFL);
+ kill(getpid(), signum);
+ /* NOTREACHED */
+ _exit(1);
}