xref: /freebsd/contrib/mandoc/demandoc.c (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
1 /* $Id: demandoc.c,v 1.34 2022/04/14 16:43:43 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 "mandoc.h"
29 #if DEBUG_MEMORY
30 #define DEBUG_NODEF
31 #include "mandoc_dbg.h"
32 #endif
33 #include "roff.h"
34 #include "man.h"
35 #include "mdoc.h"
36 #include "mandoc_parse.h"
37 
38 static	void	 pline(int, int *, int *, int);
39 static	void	 pman(const struct roff_node *, int *, int *, int);
40 static	void	 pmandoc(struct mparse *, int, const char *, int);
41 static	void	 pmdoc(const struct roff_node *, int *, int *, int);
42 static	void	 pstring(const char *, int, int *, int);
43 static	void	 usage(void);
44 
45 static	const char	 *progname;
46 
47 int
48 main(int argc, char *argv[])
49 {
50 	struct mparse	*mp;
51 	int		 ch, fd, i, list;
52 	extern int	 optind;
53 
54 #if DEBUG_MEMORY
55 	mandoc_dbg_init(argc, argv);
56 #endif
57 
58 	if (argc < 1)
59 		progname = "demandoc";
60 	else if ((progname = strrchr(argv[0], '/')) == NULL)
61 		progname = argv[0];
62 	else
63 		++progname;
64 
65 	mp = NULL;
66 	list = 0;
67 
68 	while (-1 != (ch = getopt(argc, argv, "ikm:pw")))
69 		switch (ch) {
70 		case ('i'):
71 			/* FALLTHROUGH */
72 		case ('k'):
73 			/* FALLTHROUGH */
74 		case ('m'):
75 			/* FALLTHROUGH */
76 		case ('p'):
77 			break;
78 		case ('w'):
79 			list = 1;
80 			break;
81 		default:
82 			usage();
83 			return (int)MANDOCLEVEL_BADARG;
84 		}
85 
86 	argc -= optind;
87 	argv += optind;
88 
89 	mchars_alloc();
90 	mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
91 	    MPARSE_VALIDATE, MANDOC_OS_OTHER, NULL);
92 	assert(mp);
93 
94 	if (argc < 1)
95 		pmandoc(mp, STDIN_FILENO, "<stdin>", list);
96 
97 	for (i = 0; i < argc; i++) {
98 		mparse_reset(mp);
99 		if ((fd = mparse_open(mp, argv[i])) == -1) {
100 			perror(argv[i]);
101 			continue;
102 		}
103 		pmandoc(mp, fd, argv[i], list);
104 	}
105 
106 	mparse_free(mp);
107 	mchars_free();
108 #if DEBUG_MEMORY
109 	mandoc_dbg_finish();
110 #endif
111 	return (int)MANDOCLEVEL_OK;
112 }
113 
114 static void
115 usage(void)
116 {
117 
118 	fprintf(stderr, "usage: %s [-w] [files...]\n", progname);
119 }
120 
121 static void
122 pmandoc(struct mparse *mp, int fd, const char *fn, int list)
123 {
124 	struct roff_meta	*meta;
125 	int		 line, col;
126 
127 	mparse_readfd(mp, fd, fn);
128 	close(fd);
129 	meta = mparse_result(mp);
130 	line = 1;
131 	col = 0;
132 
133 	if (meta->macroset == MACROSET_MDOC)
134 		pmdoc(meta->first->child, &line, &col, list);
135 	else
136 		pman(meta->first->child, &line, &col, list);
137 
138 	if ( ! list)
139 		putchar('\n');
140 }
141 
142 /*
143  * Strip the escapes out of a string, emitting the results.
144  */
145 static void
146 pstring(const char *p, int col, int *colp, int list)
147 {
148 	enum mandoc_esc	 esc;
149 	const char	*start, *end;
150 	int		 emit;
151 
152 	/*
153 	 * Print as many column spaces til we achieve parity with the
154 	 * input document.
155 	 */
156 
157 again:
158 	if (list && '\0' != *p) {
159 		while (isspace((unsigned char)*p))
160 			p++;
161 
162 		while ('\'' == *p || '(' == *p || '"' == *p)
163 			p++;
164 
165 		emit = isalpha((unsigned char)p[0]) &&
166 			isalpha((unsigned char)p[1]);
167 
168 		for (start = p; '\0' != *p; p++)
169 			if ('\\' == *p) {
170 				p++;
171 				esc = mandoc_escape(&p, NULL, NULL);
172 				if (ESCAPE_ERROR == esc)
173 					return;
174 				emit = 0;
175 			} else if (isspace((unsigned char)*p))
176 				break;
177 
178 		end = p - 1;
179 
180 		while (end > start)
181 			if ('.' == *end || ',' == *end ||
182 					'\'' == *end || '"' == *end ||
183 					')' == *end || '!' == *end ||
184 					'?' == *end || ':' == *end ||
185 					';' == *end)
186 				end--;
187 			else
188 				break;
189 
190 		if (emit && end - start >= 1) {
191 			for ( ; start <= end; start++)
192 				if (ASCII_HYPH == *start)
193 					putchar('-');
194 				else
195 					putchar((unsigned char)*start);
196 			putchar('\n');
197 		}
198 
199 		if (isspace((unsigned char)*p))
200 			goto again;
201 
202 		return;
203 	}
204 
205 	while (*colp < col) {
206 		putchar(' ');
207 		(*colp)++;
208 	}
209 
210 	/*
211 	 * Print the input word, skipping any special characters.
212 	 */
213 	while ('\0' != *p)
214 		if ('\\' == *p) {
215 			p++;
216 			esc = mandoc_escape(&p, NULL, NULL);
217 			if (ESCAPE_ERROR == esc)
218 				break;
219 		} else {
220 			putchar((unsigned char )*p++);
221 			(*colp)++;
222 		}
223 }
224 
225 static void
226 pline(int line, int *linep, int *col, int list)
227 {
228 
229 	if (list)
230 		return;
231 
232 	/*
233 	 * Print out as many lines as needed to reach parity with the
234 	 * original input.
235 	 */
236 
237 	while (*linep < line) {
238 		putchar('\n');
239 		(*linep)++;
240 	}
241 
242 	*col = 0;
243 }
244 
245 static void
246 pmdoc(const struct roff_node *p, int *line, int *col, int list)
247 {
248 
249 	for ( ; p; p = p->next) {
250 		if (NODE_LINE & p->flags)
251 			pline(p->line, line, col, list);
252 		if (ROFFT_TEXT == p->type)
253 			pstring(p->string, p->pos, col, list);
254 		if (p->child)
255 			pmdoc(p->child, line, col, list);
256 	}
257 }
258 
259 static void
260 pman(const struct roff_node *p, int *line, int *col, int list)
261 {
262 
263 	for ( ; p; p = p->next) {
264 		if (NODE_LINE & p->flags)
265 			pline(p->line, line, col, list);
266 		if (ROFFT_TEXT == p->type)
267 			pstring(p->string, p->pos, col, list);
268 		if (p->child)
269 			pman(p->child, line, col, list);
270 	}
271 }
272