xref: /openbsd-src/usr.bin/mandoc/man_term.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /* $Id: man_term.c,v 1.1 2009/04/06 20:30:40 kristaps Exp $ */
2 /*
3  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
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
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 #include <assert.h>
20 #include <err.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "term.h"
26 #include "man.h"
27 
28 #define	DECL_ARGS 	  struct termp *p, \
29 			  const struct man_node *n, \
30 			  const struct man_meta *m
31 
32 struct	termact {
33 	int		(*pre)(DECL_ARGS);
34 	void		(*post)(DECL_ARGS);
35 };
36 
37 static	int		  pre_B(DECL_ARGS);
38 static	int		  pre_BI(DECL_ARGS);
39 static	int		  pre_BR(DECL_ARGS);
40 static	int		  pre_I(DECL_ARGS);
41 static	int		  pre_IB(DECL_ARGS);
42 static	int		  pre_IP(DECL_ARGS);
43 static	int		  pre_IR(DECL_ARGS);
44 static	int		  pre_PP(DECL_ARGS);
45 static	int		  pre_RB(DECL_ARGS);
46 static	int		  pre_RI(DECL_ARGS);
47 static	int		  pre_SH(DECL_ARGS);
48 static	int		  pre_SS(DECL_ARGS);
49 static	int		  pre_TP(DECL_ARGS);
50 
51 static	void		  post_B(DECL_ARGS);
52 static	void		  post_I(DECL_ARGS);
53 static	void		  post_SH(DECL_ARGS);
54 static	void		  post_SS(DECL_ARGS);
55 
56 static const struct termact termacts[MAN_MAX] = {
57 	{ NULL, NULL }, /* __ */
58 	{ NULL, NULL }, /* TH */
59 	{ pre_SH, post_SH }, /* SH */
60 	{ pre_SS, post_SS }, /* SS */
61 	{ pre_TP, NULL }, /* TP */
62 	{ pre_PP, NULL }, /* LP */
63 	{ pre_PP, NULL }, /* PP */
64 	{ pre_PP, NULL }, /* P */
65 	{ pre_IP, NULL }, /* IP */
66 	{ pre_PP, NULL }, /* HP */ /* FIXME */
67 	{ NULL, NULL }, /* SM */
68 	{ pre_B, post_B }, /* SB */
69 	{ pre_BI, NULL }, /* BI */
70 	{ pre_IB, NULL }, /* IB */
71 	{ pre_BR, NULL }, /* BR */
72 	{ pre_RB, NULL }, /* RB */
73 	{ NULL, NULL }, /* R */
74 	{ pre_B, post_B }, /* B */
75 	{ pre_I, post_I }, /* I */
76 	{ pre_IR, NULL }, /* IR */
77 	{ pre_RI, NULL }, /* RI */
78 	{ pre_PP, NULL }, /* br */
79 	{ NULL, NULL }, /* na */
80 	{ pre_I, post_I }, /* i */
81 };
82 
83 static	void		  print_head(struct termp *,
84 				const struct man_meta *);
85 static	void		  print_body(DECL_ARGS);
86 static	void		  print_node(DECL_ARGS);
87 static	void		  print_foot(struct termp *,
88 				const struct man_meta *);
89 
90 
91 int
92 man_run(struct termp *p, const struct man *m)
93 {
94 
95 	print_head(p, man_meta(m));
96 	p->flags |= TERMP_NOSPACE;
97 	print_body(p, man_node(m), man_meta(m));
98 	print_foot(p, man_meta(m));
99 
100 	return(1);
101 }
102 
103 
104 /* ARGSUSED */
105 static int
106 pre_I(DECL_ARGS)
107 {
108 
109 	p->flags |= TERMP_UNDER;
110 	return(1);
111 }
112 
113 
114 /* ARGSUSED */
115 static void
116 post_I(DECL_ARGS)
117 {
118 
119 	p->flags &= ~TERMP_UNDER;
120 }
121 
122 
123 /* ARGSUSED */
124 static int
125 pre_IR(DECL_ARGS)
126 {
127 	const struct man_node *nn;
128 	int		 i;
129 
130 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
131 		if ( ! (i % 2))
132 			p->flags |= TERMP_UNDER;
133 		if (i > 0)
134 			p->flags |= TERMP_NOSPACE;
135 		print_node(p, nn, m);
136 		if ( ! (i % 2))
137 			p->flags &= ~TERMP_UNDER;
138 	}
139 	return(0);
140 }
141 
142 
143 /* ARGSUSED */
144 static int
145 pre_IB(DECL_ARGS)
146 {
147 	const struct man_node *nn;
148 	int		 i;
149 
150 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
151 		p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
152 		if (i > 0)
153 			p->flags |= TERMP_NOSPACE;
154 		print_node(p, nn, m);
155 		p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
156 	}
157 	return(0);
158 }
159 
160 
161 /* ARGSUSED */
162 static int
163 pre_RB(DECL_ARGS)
164 {
165 	const struct man_node *nn;
166 	int		 i;
167 
168 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
169 		if (i % 2)
170 			p->flags |= TERMP_BOLD;
171 		if (i > 0)
172 			p->flags |= TERMP_NOSPACE;
173 		print_node(p, nn, m);
174 		if (i % 2)
175 			p->flags &= ~TERMP_BOLD;
176 	}
177 	return(0);
178 }
179 
180 
181 /* ARGSUSED */
182 static int
183 pre_RI(DECL_ARGS)
184 {
185 	const struct man_node *nn;
186 	int		 i;
187 
188 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
189 		if ( ! (i % 2))
190 			p->flags |= TERMP_UNDER;
191 		if (i > 0)
192 			p->flags |= TERMP_NOSPACE;
193 		print_node(p, nn, m);
194 		if ( ! (i % 2))
195 			p->flags &= ~TERMP_UNDER;
196 	}
197 	return(0);
198 }
199 
200 
201 /* ARGSUSED */
202 static int
203 pre_BR(DECL_ARGS)
204 {
205 	const struct man_node *nn;
206 	int		 i;
207 
208 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
209 		if ( ! (i % 2))
210 			p->flags |= TERMP_BOLD;
211 		if (i > 0)
212 			p->flags |= TERMP_NOSPACE;
213 		print_node(p, nn, m);
214 		if ( ! (i % 2))
215 			p->flags &= ~TERMP_BOLD;
216 	}
217 	return(0);
218 }
219 
220 
221 /* ARGSUSED */
222 static int
223 pre_BI(DECL_ARGS)
224 {
225 	const struct man_node *nn;
226 	int		 i;
227 
228 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
229 		p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
230 		if (i > 0)
231 			p->flags |= TERMP_NOSPACE;
232 		print_node(p, nn, m);
233 		p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
234 	}
235 	return(0);
236 }
237 
238 
239 /* ARGSUSED */
240 static int
241 pre_B(DECL_ARGS)
242 {
243 
244 	p->flags |= TERMP_BOLD;
245 	return(1);
246 }
247 
248 
249 /* ARGSUSED */
250 static void
251 post_B(DECL_ARGS)
252 {
253 
254 	p->flags &= ~TERMP_BOLD;
255 }
256 
257 
258 /* ARGSUSED */
259 static int
260 pre_PP(DECL_ARGS)
261 {
262 
263 	term_vspace(p);
264 	p->offset = INDENT;
265 	return(0);
266 }
267 
268 
269 /* ARGSUSED */
270 static int
271 pre_IP(DECL_ARGS)
272 {
273 #if 0
274 	const struct man_node *nn;
275 	size_t		 offs;
276 #endif
277 
278 	term_vspace(p);
279 	p->offset = INDENT;
280 
281 #if 0
282 	if (NULL == (nn = n->child))
283 		return(1);
284 	if (MAN_TEXT != nn->type)
285 		errx(1, "expected text line argument");
286 
287 	if (nn->next) {
288 		if (MAN_TEXT != nn->next->type)
289 			errx(1, "expected text line argument");
290 		offs = (size_t)atoi(nn->next->string);
291 	} else
292 		offs = strlen(nn->string);
293 
294 	p->offset += offs;
295 #endif
296 	p->flags |= TERMP_NOSPACE;
297 	return(0);
298 }
299 
300 
301 /* ARGSUSED */
302 static int
303 pre_TP(DECL_ARGS)
304 {
305 	const struct man_node *nn;
306 	size_t		 offs;
307 
308 	term_vspace(p);
309 	p->offset = INDENT;
310 
311 	if (NULL == (nn = n->child))
312 		return(1);
313 
314 	if (nn->line == n->line) {
315 		if (MAN_TEXT != nn->type)
316 			errx(1, "expected text line argument");
317 		offs = (size_t)atoi(nn->string);
318 		nn = nn->next;
319 	} else
320 		offs = INDENT;
321 
322 	for ( ; nn; nn = nn->next)
323 		print_node(p, nn, m);
324 
325 	term_flushln(p);
326 	p->flags |= TERMP_NOSPACE;
327 	p->offset += offs;
328 	return(0);
329 }
330 
331 
332 /* ARGSUSED */
333 static int
334 pre_SS(DECL_ARGS)
335 {
336 
337 	term_vspace(p);
338 	p->flags |= TERMP_BOLD;
339 	return(1);
340 }
341 
342 
343 /* ARGSUSED */
344 static void
345 post_SS(DECL_ARGS)
346 {
347 
348 	term_flushln(p);
349 	p->flags &= ~TERMP_BOLD;
350 	p->flags |= TERMP_NOSPACE;
351 }
352 
353 
354 /* ARGSUSED */
355 static int
356 pre_SH(DECL_ARGS)
357 {
358 
359 	term_vspace(p);
360 	p->offset = 0;
361 	p->flags |= TERMP_BOLD;
362 	return(1);
363 }
364 
365 
366 /* ARGSUSED */
367 static void
368 post_SH(DECL_ARGS)
369 {
370 
371 	term_flushln(p);
372 	p->offset = INDENT;
373 	p->flags &= ~TERMP_BOLD;
374 	p->flags |= TERMP_NOSPACE;
375 }
376 
377 
378 static void
379 print_node(DECL_ARGS)
380 {
381 	int		 c, sz;
382 
383 	c = 1;
384 
385 	switch (n->type) {
386 	case(MAN_ELEM):
387 		if (termacts[n->tok].pre)
388 			c = (*termacts[n->tok].pre)(p, n, m);
389 		break;
390 	case(MAN_TEXT):
391 		if (0 == *n->string) {
392 			term_vspace(p);
393 			break;
394 		}
395 		/*
396 		 * Note!  This is hacky.  Here, we recognise the `\c'
397 		 * escape embedded in so many -man pages.  It's supposed
398 		 * to remove the subsequent space, so we mark NOSPACE if
399 		 * it's encountered in the string.
400 		 */
401 		sz = (int)strlen(n->string);
402 		term_word(p, n->string);
403 		if (sz >= 2 && n->string[sz - 1] == 'c' &&
404 				n->string[sz - 2] == '\\')
405 			p->flags |= TERMP_NOSPACE;
406 		break;
407 	default:
408 		break;
409 	}
410 
411 	if (c && n->child)
412 		print_body(p, n->child, m);
413 
414 	switch (n->type) {
415 	case (MAN_ELEM):
416 		if (termacts[n->tok].post)
417 			(*termacts[n->tok].post)(p, n, m);
418 		break;
419 	default:
420 		break;
421 	}
422 }
423 
424 
425 static void
426 print_body(DECL_ARGS)
427 {
428 	print_node(p, n, m);
429 	if ( ! n->next)
430 		return;
431 	print_body(p, n->next, m);
432 }
433 
434 
435 static void
436 print_foot(struct termp *p, const struct man_meta *meta)
437 {
438 	struct tm	*tm;
439 	char		*buf;
440 
441 	if (NULL == (buf = malloc(p->rmargin)))
442 		err(1, "malloc");
443 
444 	tm = localtime(&meta->date);
445 
446 	if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
447 		err(1, "strftime");
448 
449 	term_vspace(p);
450 
451 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
452 	p->rmargin = p->maxrmargin - strlen(buf);
453 	p->offset = 0;
454 
455 	if (meta->source)
456 		term_word(p, meta->source);
457 	if (meta->source)
458 		term_word(p, "");
459 	term_flushln(p);
460 
461 	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
462 	p->offset = p->rmargin;
463 	p->rmargin = p->maxrmargin;
464 	p->flags &= ~TERMP_NOBREAK;
465 
466 	term_word(p, buf);
467 	term_flushln(p);
468 
469 	free(buf);
470 }
471 
472 
473 static void
474 print_head(struct termp *p, const struct man_meta *meta)
475 {
476 	char		*buf, *title;
477 
478 	p->rmargin = p->maxrmargin;
479 	p->offset = 0;
480 
481 	if (NULL == (buf = malloc(p->rmargin)))
482 		err(1, "malloc");
483 	if (NULL == (title = malloc(p->rmargin)))
484 		err(1, "malloc");
485 
486 	if (meta->vol)
487 		(void)strlcpy(buf, meta->vol, p->rmargin);
488 	else
489 		*buf = 0;
490 
491 	(void)snprintf(title, p->rmargin, "%s(%d)",
492 			meta->title, meta->msec);
493 
494 	p->offset = 0;
495 	p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
496 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
497 
498 	term_word(p, title);
499 	term_flushln(p);
500 
501 	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
502 	p->offset = p->rmargin;
503 	p->rmargin = p->maxrmargin - strlen(title);
504 
505 	term_word(p, buf);
506 	term_flushln(p);
507 
508 	p->offset = p->rmargin;
509 	p->rmargin = p->maxrmargin;
510 	p->flags &= ~TERMP_NOBREAK;
511 	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
512 
513 	term_word(p, title);
514 	term_flushln(p);
515 
516 	p->rmargin = p->maxrmargin;
517 	p->offset = 0;
518 	p->flags &= ~TERMP_NOSPACE;
519 
520 	free(title);
521 	free(buf);
522 }
523 
524