aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@openbsd.org>2015-07-17 22:38:29 +0000
committerIngo Schwarze <schwarze@openbsd.org>2015-07-17 22:38:29 +0000
commit7221f76a236baf4056bfe1c909f3c978a4a94349 (patch)
treeeed560ed48c0b821fddc22bf6623153f70031431
parente1c3bf5030411d1b27f5509c2027238b63c1a49e (diff)
downloadmandoc-7221f76a236baf4056bfe1c909f3c978a4a94349.tar.gz
mandoc-7221f76a236baf4056bfe1c909f3c978a4a94349.tar.zst
mandoc-7221f76a236baf4056bfe1c909f3c978a4a94349.zip
Initial, still somewhat experimental implementation to leverage
less(1) -T and :t ctags(1)-like functionality to jump to the definitions of various terms inside manual pages. To be polished in the tree, so bear with me and report issues. Technically, if less(1) is used as a pager, information is collected by the mdoc(7) terminal formatter, first stored using the ohash library, then ultimately written to a temporary file which is passed to less via -T. No change intended for other output formatters or when running without a pager. Based on an idea from Kristaps using feedback from many, in particular phessler@ nicm@ millert@ halex@ doug@ kspillner@ deraadt@.
-rw-r--r--Makefile5
-rw-r--r--Makefile.depend5
-rw-r--r--main.c71
-rw-r--r--man.111
-rw-r--r--mdoc_term.c24
-rw-r--r--tag.c183
-rw-r--r--tag.h27
-rw-r--r--term.h3
-rw-r--r--term_ascii.c6
9 files changed, 299 insertions, 36 deletions
diff --git a/Makefile b/Makefile
index cc7fd8ab..4ebf6980 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.464 2015/07/14 23:16:29 schwarze Exp $
+# $Id: Makefile,v 1.465 2015/07/17 22:38:29 schwarze Exp $
#
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
# Copyright (c) 2011, 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -93,6 +93,7 @@ SRCS = att.c \
roff.c \
soelim.c \
st.c \
+ tag.c \
tbl.c \
tbl_data.c \
tbl_html.c \
@@ -160,6 +161,7 @@ DISTFILES = INSTALL \
soelim.1 \
st.in \
style.css \
+ tag.h \
tbl.3 \
tbl.7 \
term.h \
@@ -234,6 +236,7 @@ BASE_OBJS = $(MANDOC_HTML_OBJS) \
main.o \
manpath.o \
out.o \
+ tag.o \
tree.o
MAIN_OBJS = $(BASE_OBJS)
diff --git a/Makefile.depend b/Makefile.depend
index 52e95807..1481d1cd 100644
--- a/Makefile.depend
+++ b/Makefile.depend
@@ -22,7 +22,7 @@ eqn_html.o: eqn_html.c config.h mandoc.h out.h html.h
eqn_term.o: eqn_term.c config.h mandoc.h out.h term.h
html.o: html.c config.h mandoc.h mandoc_aux.h out.h html.h manconf.h main.h
lib.o: lib.c config.h roff.h mdoc.h libmdoc.h lib.in
-main.o: main.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h main.h manconf.h mansearch.h
+main.o: main.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h tag.h main.h manconf.h mansearch.h
man.o: man.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
man_hash.o: man_hash.c config.h roff.h man.h libman.h
man_html.o: man_html.c config.h mandoc_aux.h roff.h man.h out.h html.h main.h
@@ -42,7 +42,7 @@ mdoc_hash.o: mdoc_hash.c config.h roff.h mdoc.h libmdoc.h
mdoc_html.o: mdoc_html.c config.h mandoc_aux.h roff.h mdoc.h out.h html.h main.h
mdoc_macro.o: mdoc_macro.c config.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
mdoc_man.o: mdoc_man.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h out.h main.h
-mdoc_term.o: mdoc_term.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h out.h term.h main.h
+mdoc_term.o: mdoc_term.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h out.h term.h tag.h main.h
mdoc_validate.o: mdoc_validate.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
msec.o: msec.c config.h mandoc.h libmandoc.h msec.in
out.o: out.c config.h mandoc_aux.h mandoc.h out.h
@@ -51,6 +51,7 @@ read.o: read.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h libmandoc.h ro
roff.o: roff.c config.h mandoc.h mandoc_aux.h roff.h libmandoc.h roff_int.h libroff.h predefs.in
soelim.o: soelim.c config.h compat_stringlist.h
st.o: st.c config.h roff.h mdoc.h libmdoc.h st.in
+tag.o: tag.c compat_ohash.h mandoc_aux.h tag.h
tbl.o: tbl.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
tbl_data.o: tbl_data.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
tbl_html.o: tbl_html.c config.h mandoc.h out.h html.h
diff --git a/main.c b/main.c
index c7f703a3..43a90ab8 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,4 @@
-/* $Id: main.c,v 1.238 2015/04/29 11:04:17 schwarze Exp $ */
+/* $Id: main.c,v 1.239 2015/07/17 22:38:29 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -39,6 +39,7 @@
#include "roff.h"
#include "mdoc.h"
#include "man.h"
+#include "tag.h"
#include "main.h"
#include "manconf.h"
#include "mansearch.h"
@@ -497,7 +498,9 @@ out:
if (pager_pid != 0 && pager_pid != 1) {
fclose(stdout);
+ tag_write();
waitpid(pager_pid, NULL, 0);
+ tag_unlink();
}
return((int)rc);
@@ -959,10 +962,50 @@ spawn_pager(void)
char *argv[MAX_PAGER_ARGS];
const char *pager;
char *cp;
+ size_t cmdlen;
int fildes[2];
int argc;
pid_t pager_pid;
+ pager = getenv("MANPAGER");
+ if (pager == NULL || *pager == '\0')
+ pager = getenv("PAGER");
+ if (pager == NULL || *pager == '\0')
+ pager = "more -s";
+ cp = mandoc_strdup(pager);
+
+ /*
+ * Parse the pager command into words.
+ * Intentionally do not do anything fancy here.
+ */
+
+ argc = 0;
+ while (argc + 4 < MAX_PAGER_ARGS) {
+ argv[argc++] = cp;
+ cp = strchr(cp, ' ');
+ if (cp == NULL)
+ break;
+ *cp++ = '\0';
+ while (*cp == ' ')
+ cp++;
+ if (*cp == '\0')
+ break;
+ }
+
+ /* Read all text right away and use the tag file. */
+
+ if ((cmdlen = strlen(argv[0])) >= 4) {
+ cp = argv[0] + cmdlen - 4;
+ if (strcmp(cp, "less") == 0 ||
+ strcmp(cp, "more") == 0) {
+ tag_init();
+ argv[argc++] = mandoc_strdup("+G1G");
+ argv[argc++] = mandoc_strdup("-T");
+ argv[argc++] = tag_filename();
+ }
+ }
+ argv[argc] = NULL;
+
if (pipe(fildes) == -1) {
fprintf(stderr, "%s: pipe: %s\n",
progname, strerror(errno));
@@ -998,32 +1041,6 @@ spawn_pager(void)
}
close(fildes[0]);
- pager = getenv("MANPAGER");
- if (pager == NULL || *pager == '\0')
- pager = getenv("PAGER");
- if (pager == NULL || *pager == '\0')
- pager = "more -s";
- cp = mandoc_strdup(pager);
-
- /*
- * Parse the pager command into words.
- * Intentionally do not do anything fancy here.
- */
-
- argc = 0;
- while (argc + 1 < MAX_PAGER_ARGS) {
- argv[argc++] = cp;
- cp = strchr(cp, ' ');
- if (cp == NULL)
- break;
- *cp++ = '\0';
- while (*cp == ' ')
- cp++;
- if (*cp == '\0')
- break;
- }
- argv[argc] = NULL;
-
/* Hand over to the pager. */
execvp(argv[0], argv);
diff --git a/man.1 b/man.1
index d0d74a77..c9ddfa7d 100644
--- a/man.1
+++ b/man.1
@@ -1,4 +1,4 @@
-.\" $Id: man.1,v 1.14 2015/04/03 08:46:17 schwarze Exp $
+.\" $Id: man.1,v 1.15 2015/07/17 22:38:29 schwarze Exp $
.\"
.\" Copyright (c) 1989, 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -31,7 +31,7 @@
.\"
.\" @(#)man.1 8.2 (Berkeley) 1/2/94
.\"
-.Dd $Mdocdate: April 3 2015 $
+.Dd $Mdocdate: July 17 2015 $
.Dt MAN 1
.Os
.Sh NAME
@@ -360,6 +360,13 @@ Any non-empty value of the environment variable
.Ev MANPAGER
will be used instead of the standard pagination program,
.Xr more 1 .
+If
+.Xr less 1
+is used, the interactive
+.Ic :t
+command can be used to go to the definitions of various terms, for
+example command line options, command modifiers, internal commands,
+and environment variables.
.It Ev MANPATH
The standard search path used by
.Nm
diff --git a/mdoc_term.c b/mdoc_term.c
index be170bf5..d8a8ff3f 100644
--- a/mdoc_term.c
+++ b/mdoc_term.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_term.c,v 1.319 2015/04/18 17:53:21 schwarze Exp $ */
+/* $Id: mdoc_term.c,v 1.320 2015/07/17 22:38:29 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2012-2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -34,6 +34,7 @@
#include "mdoc.h"
#include "out.h"
#include "term.h"
+#include "tag.h"
#include "main.h"
struct termpair {
@@ -117,6 +118,7 @@ static int termp_skip_pre(DECL_ARGS);
static int termp_sm_pre(DECL_ARGS);
static int termp_sp_pre(DECL_ARGS);
static int termp_ss_pre(DECL_ARGS);
+static int termp_tag_pre(DECL_ARGS);
static int termp_under_pre(DECL_ARGS);
static int termp_ud_pre(DECL_ARGS);
static int termp_vt_pre(DECL_ARGS);
@@ -145,7 +147,7 @@ static const struct termact termacts[MDOC_MAX] = {
{ termp_bold_pre, NULL }, /* Cm */
{ NULL, NULL }, /* Dv */
{ NULL, NULL }, /* Er */
- { NULL, NULL }, /* Ev */
+ { termp_tag_pre, NULL }, /* Ev */
{ termp_ex_pre, NULL }, /* Ex */
{ termp_fa_pre, NULL }, /* Fa */
{ termp_fd_pre, termp_fd_post }, /* Fd */
@@ -1049,6 +1051,7 @@ static int
termp_fl_pre(DECL_ARGS)
{
+ termp_tag_pre(p, pair, meta, n);
term_fontpush(p, TERMFONT_BOLD);
term_word(p, "\\-");
@@ -1330,6 +1333,7 @@ static int
termp_bold_pre(DECL_ARGS)
{
+ termp_tag_pre(p, pair, meta, n);
term_fontpush(p, TERMFONT_BOLD);
return(1);
}
@@ -2252,3 +2256,19 @@ termp_under_pre(DECL_ARGS)
term_fontpush(p, TERMFONT_UNDER);
return(1);
}
+
+static int
+termp_tag_pre(DECL_ARGS)
+{
+
+ if (n->child != NULL &&
+ n->child->type == ROFFT_TEXT &&
+ n->prev == NULL &&
+ (n->parent->tok == MDOC_It ||
+ (n->parent->tok == MDOC_Xo &&
+ n->parent->parent->prev == NULL &&
+ n->parent->parent->parent->tok == MDOC_It)) &&
+ ! tag_get(n->child->string, 0))
+ tag_put(n->child->string, 0, p->line);
+ return(1);
+}
diff --git a/tag.c b/tag.c
new file mode 100644
index 00000000..f281b81f
--- /dev/null
+++ b/tag.c
@@ -0,0 +1,183 @@
+/* $Id: tag.c,v 1.1 2015/07/17 22:38:29 schwarze Exp $ */
+/*
+ * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * 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 <sys/types.h>
+
+#include <stddef.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 "tag.h"
+
+struct tag_entry {
+ size_t line;
+ 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 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");
+ 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);
+}
+
+/*
+ * 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);
+}
diff --git a/tag.h b/tag.h
new file mode 100644
index 00000000..4456f323
--- /dev/null
+++ b/tag.h
@@ -0,0 +1,27 @@
+/* $Id: tag.h,v 1.1 2015/07/17 22:38:29 schwarze Exp $ */
+/*
+ * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * 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.
+ */
+
+__BEGIN_DECLS
+
+void tag_init(void);
+char *tag_filename(void);
+size_t tag_get(const char *, size_t);
+void tag_put(const char *, size_t, size_t);
+void tag_write(void);
+void tag_unlink(void);
+
+__END_DECLS
diff --git a/term.h b/term.h
index 0aa467b4..2db32864 100644
--- a/term.h
+++ b/term.h
@@ -1,4 +1,4 @@
-/* $Id: term.h,v 1.114 2015/04/29 18:35:00 schwarze Exp $ */
+/* $Id: term.h,v 1.115 2015/07/17 22:38:29 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011-2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -53,6 +53,7 @@ struct termp {
struct rofftbl tbl; /* table configuration */
int synopsisonly; /* print the synopsis only */
int mdocstyle; /* imitate mdoc(7) output */
+ size_t line; /* Current output line number. */
size_t defindent; /* Default indent for text. */
size_t defrmargin; /* Right margin of the device. */
size_t lastrmargin; /* Right margin before the last ll. */
diff --git a/term_ascii.c b/term_ascii.c
index a66fa77a..b7c69d7e 100644
--- a/term_ascii.c
+++ b/term_ascii.c
@@ -1,4 +1,4 @@
-/* $Id: term_ascii.c,v 1.46 2015/06/10 19:17:15 schwarze Exp $ */
+/* $Id: term_ascii.c,v 1.47 2015/07/17 22:38:29 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -70,6 +70,7 @@ ascii_init(enum termenc enc, const struct mchars *mchars,
p = mandoc_calloc(1, sizeof(struct termp));
p->symtab = mchars;
+ p->line = 1;
p->tabwidth = 5;
p->defrmargin = p->lastrmargin = 78;
p->fontq = mandoc_reallocarray(NULL,
@@ -163,6 +164,7 @@ ascii_sepline(void *arg)
size_t i;
p = (struct termp *)arg;
+ p->line += 3;
putchar('\n');
for (i = 0; i < p->defrmargin; i++)
putchar('-');
@@ -209,6 +211,7 @@ static void
ascii_endline(struct termp *p)
{
+ p->line++;
putchar('\n');
}
@@ -365,6 +368,7 @@ static void
locale_endline(struct termp *p)
{
+ p->line++;
putwchar(L'\n');
}