source@mandoc.bsd.lv
 help / color / mirror / Atom feed
* mdocml: Initial, still somewhat experimental implementation to leverage
@ 2015-07-17 22:39 schwarze
  0 siblings, 0 replies; only message in thread
From: schwarze @ 2015-07-17 22:39 UTC (permalink / raw)
  To: source

Log Message:
-----------
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@.

Modified Files:
--------------
    mdocml:
        Makefile
        Makefile.depend
        main.c
        man.1
        mdoc_term.c
        term.h
        term_ascii.c

Added Files:
-----------
    mdocml:
        tag.c
        tag.h

Revision Data
-------------
Index: mdoc_term.c
===================================================================
RCS file: /home/cvs/mdocml/mdocml/mdoc_term.c,v
retrieving revision 1.319
retrieving revision 1.320
diff -Lmdoc_term.c -Lmdoc_term.c -u -p -r1.319 -r1.320
--- mdoc_term.c
+++ mdoc_term.c
@@ -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[MDO
 	{ 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);
 }
@@ -2250,5 +2254,21 @@ 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);
 }
Index: Makefile
===================================================================
RCS file: /home/cvs/mdocml/mdocml/Makefile,v
retrieving revision 1.464
retrieving revision 1.465
diff -LMakefile -LMakefile -u -p -r1.464 -r1.465
--- Makefile
+++ Makefile
@@ -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)
--- /dev/null
+++ 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);
+}
Index: term.h
===================================================================
RCS file: /home/cvs/mdocml/mdocml/term.h,v
retrieving revision 1.114
retrieving revision 1.115
diff -Lterm.h -Lterm.h -u -p -r1.114 -r1.115
--- term.h
+++ term.h
@@ -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. */
Index: main.c
===================================================================
RCS file: /home/cvs/mdocml/mdocml/main.c,v
retrieving revision 1.238
retrieving revision 1.239
diff -Lmain.c -Lmain.c -u -p -r1.238 -r1.239
--- main.c
+++ main.c
@@ -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));
@@ -997,32 +1040,6 @@ spawn_pager(void)
 		exit((int)MANDOCLEVEL_SYSERR);
 	}
 	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. */
 
Index: Makefile.depend
===================================================================
RCS file: /home/cvs/mdocml/mdocml/Makefile.depend,v
retrieving revision 1.13
retrieving revision 1.14
diff -LMakefile.depend -LMakefile.depend -u -p -r1.13 -r1.14
--- Makefile.depend
+++ Makefile.depend
@@ -22,7 +22,7 @@ eqn_html.o: eqn_html.c config.h mandoc.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_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 man
 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
--- /dev/null
+++ 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
Index: man.1
===================================================================
RCS file: /home/cvs/mdocml/mdocml/man.1,v
retrieving revision 1.14
retrieving revision 1.15
diff -Lman.1 -Lman.1 -u -p -r1.14 -r1.15
--- man.1
+++ man.1
@@ -360,6 +360,13 @@ Any non-empty value of the environment v
 .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
Index: term_ascii.c
===================================================================
RCS file: /home/cvs/mdocml/mdocml/term_ascii.c,v
retrieving revision 1.46
retrieving revision 1.47
diff -Lterm_ascii.c -Lterm_ascii.c -u -p -r1.46 -r1.47
--- term_ascii.c
+++ term_ascii.c
@@ -70,6 +70,7 @@ ascii_init(enum termenc enc, const struc
 	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');
 }
 
--
 To unsubscribe send an email to source+unsubscribe@mdocml.bsd.lv

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2015-07-17 22:39 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-17 22:39 mdocml: Initial, still somewhat experimental implementation to leverage schwarze

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).