GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/man_html.c Lines: 0 256 0.0 %
Date: 2016-12-06 Branches: 0 171 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: man_html.c,v 1.73 2016/01/08 17:48:04 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4
 * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
#include <sys/types.h>
19
20
#include <assert.h>
21
#include <ctype.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "mandoc_aux.h"
27
#include "roff.h"
28
#include "man.h"
29
#include "out.h"
30
#include "html.h"
31
#include "main.h"
32
33
/* TODO: preserve ident widths. */
34
/* FIXME: have PD set the default vspace width. */
35
36
#define	INDENT		  5
37
38
#define	MAN_ARGS	  const struct roff_meta *man, \
39
			  const struct roff_node *n, \
40
			  struct mhtml *mh, \
41
			  struct html *h
42
43
struct	mhtml {
44
	int		  fl;
45
#define	MANH_LITERAL	 (1 << 0) /* literal context */
46
};
47
48
struct	htmlman {
49
	int		(*pre)(MAN_ARGS);
50
	int		(*post)(MAN_ARGS);
51
};
52
53
static	void		  print_bvspace(struct html *,
54
				const struct roff_node *);
55
static	void		  print_man_head(MAN_ARGS);
56
static	void		  print_man_nodelist(MAN_ARGS);
57
static	void		  print_man_node(MAN_ARGS);
58
static	int		  a2width(const struct roff_node *,
59
				struct roffsu *);
60
static	int		  man_B_pre(MAN_ARGS);
61
static	int		  man_HP_pre(MAN_ARGS);
62
static	int		  man_IP_pre(MAN_ARGS);
63
static	int		  man_I_pre(MAN_ARGS);
64
static	int		  man_OP_pre(MAN_ARGS);
65
static	int		  man_PP_pre(MAN_ARGS);
66
static	int		  man_RS_pre(MAN_ARGS);
67
static	int		  man_SH_pre(MAN_ARGS);
68
static	int		  man_SM_pre(MAN_ARGS);
69
static	int		  man_SS_pre(MAN_ARGS);
70
static	int		  man_UR_pre(MAN_ARGS);
71
static	int		  man_alt_pre(MAN_ARGS);
72
static	int		  man_br_pre(MAN_ARGS);
73
static	int		  man_ign_pre(MAN_ARGS);
74
static	int		  man_in_pre(MAN_ARGS);
75
static	int		  man_literal_pre(MAN_ARGS);
76
static	void		  man_root_post(MAN_ARGS);
77
static	void		  man_root_pre(MAN_ARGS);
78
79
static	const struct htmlman mans[MAN_MAX] = {
80
	{ man_br_pre, NULL }, /* br */
81
	{ NULL, NULL }, /* TH */
82
	{ man_SH_pre, NULL }, /* SH */
83
	{ man_SS_pre, NULL }, /* SS */
84
	{ man_IP_pre, NULL }, /* TP */
85
	{ man_PP_pre, NULL }, /* LP */
86
	{ man_PP_pre, NULL }, /* PP */
87
	{ man_PP_pre, NULL }, /* P */
88
	{ man_IP_pre, NULL }, /* IP */
89
	{ man_HP_pre, NULL }, /* HP */
90
	{ man_SM_pre, NULL }, /* SM */
91
	{ man_SM_pre, NULL }, /* SB */
92
	{ man_alt_pre, NULL }, /* BI */
93
	{ man_alt_pre, NULL }, /* IB */
94
	{ man_alt_pre, NULL }, /* BR */
95
	{ man_alt_pre, NULL }, /* RB */
96
	{ NULL, NULL }, /* R */
97
	{ man_B_pre, NULL }, /* B */
98
	{ man_I_pre, NULL }, /* I */
99
	{ man_alt_pre, NULL }, /* IR */
100
	{ man_alt_pre, NULL }, /* RI */
101
	{ man_br_pre, NULL }, /* sp */
102
	{ man_literal_pre, NULL }, /* nf */
103
	{ man_literal_pre, NULL }, /* fi */
104
	{ NULL, NULL }, /* RE */
105
	{ man_RS_pre, NULL }, /* RS */
106
	{ man_ign_pre, NULL }, /* DT */
107
	{ man_ign_pre, NULL }, /* UC */
108
	{ man_ign_pre, NULL }, /* PD */
109
	{ man_ign_pre, NULL }, /* AT */
110
	{ man_in_pre, NULL }, /* in */
111
	{ man_ign_pre, NULL }, /* ft */
112
	{ man_OP_pre, NULL }, /* OP */
113
	{ man_literal_pre, NULL }, /* EX */
114
	{ man_literal_pre, NULL }, /* EE */
115
	{ man_UR_pre, NULL }, /* UR */
116
	{ NULL, NULL }, /* UE */
117
	{ man_ign_pre, NULL }, /* ll */
118
};
119
120
121
/*
122
 * Printing leading vertical space before a block.
123
 * This is used for the paragraph macros.
124
 * The rules are pretty simple, since there's very little nesting going
125
 * on here.  Basically, if we're the first within another block (SS/SH),
126
 * then don't emit vertical space.  If we are (RS), then do.  If not the
127
 * first, print it.
128
 */
129
static void
130
print_bvspace(struct html *h, const struct roff_node *n)
131
{
132
133
	if (n->body && n->body->child)
134
		if (n->body->child->type == ROFFT_TBL)
135
			return;
136
137
	if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
138
		if (NULL == n->prev)
139
			return;
140
141
	print_paragraph(h);
142
}
143
144
void
145
html_man(void *arg, const struct roff_man *man)
146
{
147
	struct mhtml	 mh;
148
	struct htmlpair	 tag;
149
	struct html	*h;
150
	struct tag	*t, *tt;
151
152
	memset(&mh, 0, sizeof(mh));
153
	PAIR_CLASS_INIT(&tag, "mandoc");
154
	h = (struct html *)arg;
155
156
	if ( ! (HTML_FRAGMENT & h->oflags)) {
157
		print_gen_decls(h);
158
		t = print_otag(h, TAG_HTML, 0, NULL);
159
		tt = print_otag(h, TAG_HEAD, 0, NULL);
160
		print_man_head(&man->meta, man->first, &mh, h);
161
		print_tagq(h, tt);
162
		print_otag(h, TAG_BODY, 0, NULL);
163
		print_otag(h, TAG_DIV, 1, &tag);
164
	} else
165
		t = print_otag(h, TAG_DIV, 1, &tag);
166
167
	print_man_nodelist(&man->meta, man->first, &mh, h);
168
	print_tagq(h, t);
169
	putchar('\n');
170
}
171
172
static void
173
print_man_head(MAN_ARGS)
174
{
175
176
	print_gen_head(h);
177
	assert(man->title);
178
	assert(man->msec);
179
	bufcat_fmt(h, "%s(%s)", man->title, man->msec);
180
	print_otag(h, TAG_TITLE, 0, NULL);
181
	print_text(h, h->buf);
182
}
183
184
static void
185
print_man_nodelist(MAN_ARGS)
186
{
187
188
	while (n != NULL) {
189
		print_man_node(man, n, mh, h);
190
		n = n->next;
191
	}
192
}
193
194
static void
195
print_man_node(MAN_ARGS)
196
{
197
	int		 child;
198
	struct tag	*t;
199
200
	child = 1;
201
	t = h->tags.head;
202
203
	switch (n->type) {
204
	case ROFFT_ROOT:
205
		man_root_pre(man, n, mh, h);
206
		break;
207
	case ROFFT_TEXT:
208
		if ('\0' == *n->string) {
209
			print_paragraph(h);
210
			return;
211
		}
212
		if (n->flags & MAN_LINE && (*n->string == ' ' ||
213
		    (n->prev != NULL && mh->fl & MANH_LITERAL &&
214
		     ! (h->flags & HTML_NONEWLINE))))
215
			print_otag(h, TAG_BR, 0, NULL);
216
		print_text(h, n->string);
217
		return;
218
	case ROFFT_EQN:
219
		if (n->flags & MAN_LINE)
220
			putchar('\n');
221
		print_eqn(h, n->eqn);
222
		break;
223
	case ROFFT_TBL:
224
		/*
225
		 * This will take care of initialising all of the table
226
		 * state data for the first table, then tearing it down
227
		 * for the last one.
228
		 */
229
		print_tbl(h, n->span);
230
		return;
231
	default:
232
		/*
233
		 * Close out scope of font prior to opening a macro
234
		 * scope.
235
		 */
236
		if (HTMLFONT_NONE != h->metac) {
237
			h->metal = h->metac;
238
			h->metac = HTMLFONT_NONE;
239
		}
240
241
		/*
242
		 * Close out the current table, if it's open, and unset
243
		 * the "meta" table state.  This will be reopened on the
244
		 * next table element.
245
		 */
246
		if (h->tblt) {
247
			print_tblclose(h);
248
			t = h->tags.head;
249
		}
250
		if (mans[n->tok].pre)
251
			child = (*mans[n->tok].pre)(man, n, mh, h);
252
		break;
253
	}
254
255
	if (child && n->child)
256
		print_man_nodelist(man, n->child, mh, h);
257
258
	/* This will automatically close out any font scope. */
259
	print_stagq(h, t);
260
261
	switch (n->type) {
262
	case ROFFT_ROOT:
263
		man_root_post(man, n, mh, h);
264
		break;
265
	case ROFFT_EQN:
266
		break;
267
	default:
268
		if (mans[n->tok].post)
269
			(*mans[n->tok].post)(man, n, mh, h);
270
		break;
271
	}
272
}
273
274
static int
275
a2width(const struct roff_node *n, struct roffsu *su)
276
{
277
278
	if (n->type != ROFFT_TEXT)
279
		return 0;
280
	if (a2roffsu(n->string, su, SCALE_EN))
281
		return 1;
282
283
	return 0;
284
}
285
286
static void
287
man_root_pre(MAN_ARGS)
288
{
289
	struct htmlpair	 tag;
290
	struct tag	*t, *tt;
291
	char		*title;
292
293
	assert(man->title);
294
	assert(man->msec);
295
	mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
296
297
	PAIR_CLASS_INIT(&tag, "head");
298
	t = print_otag(h, TAG_TABLE, 1, &tag);
299
300
	print_otag(h, TAG_TBODY, 0, NULL);
301
302
	tt = print_otag(h, TAG_TR, 0, NULL);
303
304
	PAIR_CLASS_INIT(&tag, "head-ltitle");
305
	print_otag(h, TAG_TD, 1, &tag);
306
	print_text(h, title);
307
	print_stagq(h, tt);
308
309
	PAIR_CLASS_INIT(&tag, "head-vol");
310
	print_otag(h, TAG_TD, 1, &tag);
311
	if (NULL != man->vol)
312
		print_text(h, man->vol);
313
	print_stagq(h, tt);
314
315
	PAIR_CLASS_INIT(&tag, "head-rtitle");
316
	print_otag(h, TAG_TD, 1, &tag);
317
	print_text(h, title);
318
	print_tagq(h, t);
319
	free(title);
320
}
321
322
static void
323
man_root_post(MAN_ARGS)
324
{
325
	struct htmlpair	 tag;
326
	struct tag	*t, *tt;
327
328
	PAIR_CLASS_INIT(&tag, "foot");
329
	t = print_otag(h, TAG_TABLE, 1, &tag);
330
331
	tt = print_otag(h, TAG_TR, 0, NULL);
332
333
	PAIR_CLASS_INIT(&tag, "foot-date");
334
	print_otag(h, TAG_TD, 1, &tag);
335
336
	assert(man->date);
337
	print_text(h, man->date);
338
	print_stagq(h, tt);
339
340
	PAIR_CLASS_INIT(&tag, "foot-os");
341
	print_otag(h, TAG_TD, 1, &tag);
342
343
	if (man->os)
344
		print_text(h, man->os);
345
	print_tagq(h, t);
346
}
347
348
349
static int
350
man_br_pre(MAN_ARGS)
351
{
352
	struct roffsu	 su;
353
	struct htmlpair	 tag;
354
355
	SCALE_VS_INIT(&su, 1);
356
357
	if (MAN_sp == n->tok) {
358
		if (NULL != (n = n->child))
359
			if ( ! a2roffsu(n->string, &su, SCALE_VS))
360
				su.scale = 1.0;
361
	} else
362
		su.scale = 0.0;
363
364
	bufinit(h);
365
	bufcat_su(h, "height", &su);
366
	PAIR_STYLE_INIT(&tag, h);
367
	print_otag(h, TAG_DIV, 1, &tag);
368
369
	/* So the div isn't empty: */
370
	print_text(h, "\\~");
371
372
	return 0;
373
}
374
375
static int
376
man_SH_pre(MAN_ARGS)
377
{
378
	struct htmlpair	 tag;
379
380
	if (n->type == ROFFT_BLOCK) {
381
		mh->fl &= ~MANH_LITERAL;
382
		PAIR_CLASS_INIT(&tag, "section");
383
		print_otag(h, TAG_DIV, 1, &tag);
384
		return 1;
385
	} else if (n->type == ROFFT_BODY)
386
		return 1;
387
388
	print_otag(h, TAG_H1, 0, NULL);
389
	return 1;
390
}
391
392
static int
393
man_alt_pre(MAN_ARGS)
394
{
395
	const struct roff_node	*nn;
396
	int		 i, savelit;
397
	enum htmltag	 fp;
398
	struct tag	*t;
399
400
	if ((savelit = mh->fl & MANH_LITERAL))
401
		print_otag(h, TAG_BR, 0, NULL);
402
403
	mh->fl &= ~MANH_LITERAL;
404
405
	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
406
		t = NULL;
407
		switch (n->tok) {
408
		case MAN_BI:
409
			fp = i % 2 ? TAG_I : TAG_B;
410
			break;
411
		case MAN_IB:
412
			fp = i % 2 ? TAG_B : TAG_I;
413
			break;
414
		case MAN_RI:
415
			fp = i % 2 ? TAG_I : TAG_MAX;
416
			break;
417
		case MAN_IR:
418
			fp = i % 2 ? TAG_MAX : TAG_I;
419
			break;
420
		case MAN_BR:
421
			fp = i % 2 ? TAG_MAX : TAG_B;
422
			break;
423
		case MAN_RB:
424
			fp = i % 2 ? TAG_B : TAG_MAX;
425
			break;
426
		default:
427
			abort();
428
		}
429
430
		if (i)
431
			h->flags |= HTML_NOSPACE;
432
433
		if (TAG_MAX != fp)
434
			t = print_otag(h, fp, 0, NULL);
435
436
		print_man_node(man, nn, mh, h);
437
438
		if (t)
439
			print_tagq(h, t);
440
	}
441
442
	if (savelit)
443
		mh->fl |= MANH_LITERAL;
444
445
	return 0;
446
}
447
448
static int
449
man_SM_pre(MAN_ARGS)
450
{
451
452
	print_otag(h, TAG_SMALL, 0, NULL);
453
	if (MAN_SB == n->tok)
454
		print_otag(h, TAG_B, 0, NULL);
455
	return 1;
456
}
457
458
static int
459
man_SS_pre(MAN_ARGS)
460
{
461
	struct htmlpair	 tag;
462
463
	if (n->type == ROFFT_BLOCK) {
464
		mh->fl &= ~MANH_LITERAL;
465
		PAIR_CLASS_INIT(&tag, "subsection");
466
		print_otag(h, TAG_DIV, 1, &tag);
467
		return 1;
468
	} else if (n->type == ROFFT_BODY)
469
		return 1;
470
471
	print_otag(h, TAG_H2, 0, NULL);
472
	return 1;
473
}
474
475
static int
476
man_PP_pre(MAN_ARGS)
477
{
478
479
	if (n->type == ROFFT_HEAD)
480
		return 0;
481
	else if (n->type == ROFFT_BLOCK)
482
		print_bvspace(h, n);
483
484
	return 1;
485
}
486
487
static int
488
man_IP_pre(MAN_ARGS)
489
{
490
	const struct roff_node	*nn;
491
492
	if (n->type == ROFFT_BODY) {
493
		print_otag(h, TAG_DD, 0, NULL);
494
		return 1;
495
	} else if (n->type != ROFFT_HEAD) {
496
		print_otag(h, TAG_DL, 0, NULL);
497
		return 1;
498
	}
499
500
	/* FIXME: width specification. */
501
502
	print_otag(h, TAG_DT, 0, NULL);
503
504
	/* For IP, only print the first header element. */
505
506
	if (MAN_IP == n->tok && n->child)
507
		print_man_node(man, n->child, mh, h);
508
509
	/* For TP, only print next-line header elements. */
510
511
	if (MAN_TP == n->tok) {
512
		nn = n->child;
513
		while (NULL != nn && 0 == (MAN_LINE & nn->flags))
514
			nn = nn->next;
515
		while (NULL != nn) {
516
			print_man_node(man, nn, mh, h);
517
			nn = nn->next;
518
		}
519
	}
520
521
	return 0;
522
}
523
524
static int
525
man_HP_pre(MAN_ARGS)
526
{
527
	struct htmlpair	 tag[2];
528
	struct roffsu	 su;
529
	const struct roff_node *np;
530
531
	if (n->type == ROFFT_HEAD)
532
		return 0;
533
	else if (n->type != ROFFT_BLOCK)
534
		return 1;
535
536
	np = n->head->child;
537
538
	if (NULL == np || ! a2width(np, &su))
539
		SCALE_HS_INIT(&su, INDENT);
540
541
	bufinit(h);
542
543
	print_bvspace(h, n);
544
	bufcat_su(h, "margin-left", &su);
545
	su.scale = -su.scale;
546
	bufcat_su(h, "text-indent", &su);
547
	PAIR_STYLE_INIT(&tag[0], h);
548
	PAIR_CLASS_INIT(&tag[1], "spacer");
549
	print_otag(h, TAG_DIV, 2, tag);
550
	return 1;
551
}
552
553
static int
554
man_OP_pre(MAN_ARGS)
555
{
556
	struct tag	*tt;
557
	struct htmlpair	 tag;
558
559
	print_text(h, "[");
560
	h->flags |= HTML_NOSPACE;
561
	PAIR_CLASS_INIT(&tag, "opt");
562
	tt = print_otag(h, TAG_SPAN, 1, &tag);
563
564
	if (NULL != (n = n->child)) {
565
		print_otag(h, TAG_B, 0, NULL);
566
		print_text(h, n->string);
567
	}
568
569
	print_stagq(h, tt);
570
571
	if (NULL != n && NULL != n->next) {
572
		print_otag(h, TAG_I, 0, NULL);
573
		print_text(h, n->next->string);
574
	}
575
576
	print_stagq(h, tt);
577
	h->flags |= HTML_NOSPACE;
578
	print_text(h, "]");
579
	return 0;
580
}
581
582
static int
583
man_B_pre(MAN_ARGS)
584
{
585
586
	print_otag(h, TAG_B, 0, NULL);
587
	return 1;
588
}
589
590
static int
591
man_I_pre(MAN_ARGS)
592
{
593
594
	print_otag(h, TAG_I, 0, NULL);
595
	return 1;
596
}
597
598
static int
599
man_literal_pre(MAN_ARGS)
600
{
601
602
	if (MAN_fi == n->tok || MAN_EE == n->tok) {
603
		print_otag(h, TAG_BR, 0, NULL);
604
		mh->fl &= ~MANH_LITERAL;
605
	} else
606
		mh->fl |= MANH_LITERAL;
607
608
	return 0;
609
}
610
611
static int
612
man_in_pre(MAN_ARGS)
613
{
614
615
	print_otag(h, TAG_BR, 0, NULL);
616
	return 0;
617
}
618
619
static int
620
man_ign_pre(MAN_ARGS)
621
{
622
623
	return 0;
624
}
625
626
static int
627
man_RS_pre(MAN_ARGS)
628
{
629
	struct htmlpair	 tag;
630
	struct roffsu	 su;
631
632
	if (n->type == ROFFT_HEAD)
633
		return 0;
634
	else if (n->type == ROFFT_BODY)
635
		return 1;
636
637
	SCALE_HS_INIT(&su, INDENT);
638
	if (n->head->child)
639
		a2width(n->head->child, &su);
640
641
	bufinit(h);
642
	bufcat_su(h, "margin-left", &su);
643
	PAIR_STYLE_INIT(&tag, h);
644
	print_otag(h, TAG_DIV, 1, &tag);
645
	return 1;
646
}
647
648
static int
649
man_UR_pre(MAN_ARGS)
650
{
651
	struct htmlpair		 tag[2];
652
653
	n = n->child;
654
	assert(n->type == ROFFT_HEAD);
655
	if (n->child != NULL) {
656
		assert(n->child->type == ROFFT_TEXT);
657
		PAIR_CLASS_INIT(&tag[0], "link-ext");
658
		PAIR_HREF_INIT(&tag[1], n->child->string);
659
		print_otag(h, TAG_A, 2, tag);
660
	}
661
662
	assert(n->next->type == ROFFT_BODY);
663
	if (n->next->child != NULL)
664
		n = n->next;
665
666
	print_man_nodelist(man, n->child, mh, h);
667
668
	return 0;
669
}