demandoc.c revision 1.1.1.4
1/*	Id: demandoc.c,v 1.27 2016/07/09 15:24:19 schwarze Exp  */
2/*
3 * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17#include "config.h"
18
19#include <sys/types.h>
20
21#include <assert.h>
22#include <ctype.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27
28#include "roff.h"
29#include "man.h"
30#include "mdoc.h"
31#include "mandoc.h"
32
33static	void	 pline(int, int *, int *, int);
34static	void	 pman(const struct roff_node *, int *, int *, int);
35static	void	 pmandoc(struct mparse *, int, const char *, int);
36static	void	 pmdoc(const struct roff_node *, int *, int *, int);
37static	void	 pstring(const char *, int, int *, int);
38static	void	 usage(void);
39
40static	const char	 *progname;
41
42int
43main(int argc, char *argv[])
44{
45	struct mparse	*mp;
46	int		 ch, fd, i, list;
47	extern int	 optind;
48
49	if (argc < 1)
50		progname = "demandoc";
51	else if ((progname = strrchr(argv[0], '/')) == NULL)
52		progname = argv[0];
53	else
54		++progname;
55
56	mp = NULL;
57	list = 0;
58
59	while (-1 != (ch = getopt(argc, argv, "ikm:pw")))
60		switch (ch) {
61		case ('i'):
62			/* FALLTHROUGH */
63		case ('k'):
64			/* FALLTHROUGH */
65		case ('m'):
66			/* FALLTHROUGH */
67		case ('p'):
68			break;
69		case ('w'):
70			list = 1;
71			break;
72		default:
73			usage();
74			return (int)MANDOCLEVEL_BADARG;
75		}
76
77	argc -= optind;
78	argv += optind;
79
80	mchars_alloc();
81	mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_BADARG, NULL, NULL);
82	assert(mp);
83
84	if (argc < 1)
85		pmandoc(mp, STDIN_FILENO, "<stdin>", list);
86
87	for (i = 0; i < argc; i++) {
88		mparse_reset(mp);
89		if ((fd = mparse_open(mp, argv[i])) == -1) {
90			perror(argv[i]);
91			continue;
92		}
93		pmandoc(mp, fd, argv[i], list);
94	}
95
96	mparse_free(mp);
97	mchars_free();
98	return (int)MANDOCLEVEL_OK;
99}
100
101static void
102usage(void)
103{
104
105	fprintf(stderr, "usage: %s [-w] [files...]\n", progname);
106}
107
108static void
109pmandoc(struct mparse *mp, int fd, const char *fn, int list)
110{
111	struct roff_man	*man;
112	int		 line, col;
113
114	mparse_readfd(mp, fd, fn);
115	close(fd);
116	mparse_result(mp, &man, NULL);
117	line = 1;
118	col = 0;
119
120	if (man == NULL)
121		return;
122	if (man->macroset == MACROSET_MDOC) {
123		mdoc_validate(man);
124		pmdoc(man->first->child, &line, &col, list);
125	} else {
126		man_validate(man);
127		pman(man->first->child, &line, &col, list);
128	}
129
130	if ( ! list)
131		putchar('\n');
132}
133
134/*
135 * Strip the escapes out of a string, emitting the results.
136 */
137static void
138pstring(const char *p, int col, int *colp, int list)
139{
140	enum mandoc_esc	 esc;
141	const char	*start, *end;
142	int		 emit;
143
144	/*
145	 * Print as many column spaces til we achieve parity with the
146	 * input document.
147	 */
148
149again:
150	if (list && '\0' != *p) {
151		while (isspace((unsigned char)*p))
152			p++;
153
154		while ('\'' == *p || '(' == *p || '"' == *p)
155			p++;
156
157		emit = isalpha((unsigned char)p[0]) &&
158			isalpha((unsigned char)p[1]);
159
160		for (start = p; '\0' != *p; p++)
161			if ('\\' == *p) {
162				p++;
163				esc = mandoc_escape(&p, NULL, NULL);
164				if (ESCAPE_ERROR == esc)
165					return;
166				emit = 0;
167			} else if (isspace((unsigned char)*p))
168				break;
169
170		end = p - 1;
171
172		while (end > start)
173			if ('.' == *end || ',' == *end ||
174					'\'' == *end || '"' == *end ||
175					')' == *end || '!' == *end ||
176					'?' == *end || ':' == *end ||
177					';' == *end)
178				end--;
179			else
180				break;
181
182		if (emit && end - start >= 1) {
183			for ( ; start <= end; start++)
184				if (ASCII_HYPH == *start)
185					putchar('-');
186				else
187					putchar((unsigned char)*start);
188			putchar('\n');
189		}
190
191		if (isspace((unsigned char)*p))
192			goto again;
193
194		return;
195	}
196
197	while (*colp < col) {
198		putchar(' ');
199		(*colp)++;
200	}
201
202	/*
203	 * Print the input word, skipping any special characters.
204	 */
205	while ('\0' != *p)
206		if ('\\' == *p) {
207			p++;
208			esc = mandoc_escape(&p, NULL, NULL);
209			if (ESCAPE_ERROR == esc)
210				break;
211		} else {
212			putchar((unsigned char )*p++);
213			(*colp)++;
214		}
215}
216
217static void
218pline(int line, int *linep, int *col, int list)
219{
220
221	if (list)
222		return;
223
224	/*
225	 * Print out as many lines as needed to reach parity with the
226	 * original input.
227	 */
228
229	while (*linep < line) {
230		putchar('\n');
231		(*linep)++;
232	}
233
234	*col = 0;
235}
236
237static void
238pmdoc(const struct roff_node *p, int *line, int *col, int list)
239{
240
241	for ( ; p; p = p->next) {
242		if (MDOC_LINE & p->flags)
243			pline(p->line, line, col, list);
244		if (ROFFT_TEXT == p->type)
245			pstring(p->string, p->pos, col, list);
246		if (p->child)
247			pmdoc(p->child, line, col, list);
248	}
249}
250
251static void
252pman(const struct roff_node *p, int *line, int *col, int list)
253{
254
255	for ( ; p; p = p->next) {
256		if (MAN_LINE & p->flags)
257			pline(p->line, line, col, list);
258		if (ROFFT_TEXT == p->type)
259			pstring(p->string, p->pos, col, list);
260		if (p->child)
261			pman(p->child, line, col, list);
262	}
263}
264