GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/tbl_layout.c Lines: 140 163 85.9 %
Date: 2017-11-07 Branches: 119 146 81.5 %

Line Branch Exec Source
1
/*	$OpenBSD: tbl_layout.c,v 1.31 2017/06/27 18:23:29 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4
 * Copyright (c) 2012, 2014, 2015, 2017 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 AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 <ctype.h>
21
#include <stdint.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <time.h>
25
26
#include "mandoc.h"
27
#include "mandoc_aux.h"
28
#include "libmandoc.h"
29
#include "libroff.h"
30
31
struct	tbl_phrase {
32
	char		 name;
33
	enum tbl_cellt	 key;
34
};
35
36
static	const struct tbl_phrase keys[] = {
37
	{ 'c',		 TBL_CELL_CENTRE },
38
	{ 'r',		 TBL_CELL_RIGHT },
39
	{ 'l',		 TBL_CELL_LEFT },
40
	{ 'n',		 TBL_CELL_NUMBER },
41
	{ 's',		 TBL_CELL_SPAN },
42
	{ 'a',		 TBL_CELL_LONG },
43
	{ '^',		 TBL_CELL_DOWN },
44
	{ '-',		 TBL_CELL_HORIZ },
45
	{ '_',		 TBL_CELL_HORIZ },
46
	{ '=',		 TBL_CELL_DHORIZ }
47
};
48
49
#define KEYS_MAX ((int)(sizeof(keys)/sizeof(keys[0])))
50
51
static	void		 mods(struct tbl_node *, struct tbl_cell *,
52
				int, const char *, int *);
53
static	void		 cell(struct tbl_node *, struct tbl_row *,
54
				int, const char *, int *);
55
static	struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
56
				enum tbl_cellt);
57
58
59
static void
60
mods(struct tbl_node *tbl, struct tbl_cell *cp,
61
		int ln, const char *p, int *pos)
62
{
63
106314
	char		*endptr;
64
53157
	size_t		 sz;
65
66
mod:
67

277785
	while (p[*pos] == ' ' || p[*pos] == '\t')
68
45126
		(*pos)++;
69
70
	/* Row delimiters and cell specifiers end modifier lists. */
71
72
62505
	if (strchr(".,-=^_ACLNRSaclnrs", p[*pos]) != NULL)
73
53157
		return;
74
75
	/* Throw away parenthesised expression. */
76
77
9348
	if ('(' == p[*pos]) {
78
		(*pos)++;
79

378
		while (p[*pos] && ')' != p[*pos])
80
			(*pos)++;
81
18
		if (')' == p[*pos]) {
82
18
			(*pos)++;
83
18
			goto mod;
84
		}
85
		mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, tbl->parse,
86
		    ln, *pos, NULL);
87
		return;
88
	}
89
90
	/* Parse numerical spacing from modifier string. */
91
92
9330
	if (isdigit((unsigned char)p[*pos])) {
93
246
		cp->spacing = strtoull(p + *pos, &endptr, 10);
94
246
		*pos = endptr - p;
95
246
		goto mod;
96
	}
97
98



9084
	switch (tolower((unsigned char)p[(*pos)++])) {
99
	case 'b':
100
84
		cp->flags |= TBL_CELL_BOLD;
101
84
		goto mod;
102
	case 'd':
103
		cp->flags |= TBL_CELL_BALIGN;
104
		goto mod;
105
	case 'e':
106
6
		cp->flags |= TBL_CELL_EQUAL;
107
6
		goto mod;
108
	case 'f':
109
		break;
110
	case 'i':
111
84
		cp->flags |= TBL_CELL_ITALIC;
112
84
		goto mod;
113
	case 'm':
114
		mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, tbl->parse,
115
		    ln, *pos, "m");
116
		goto mod;
117
	case 'p':
118
	case 'v':
119

18
		if (p[*pos] == '-' || p[*pos] == '+')
120
18
			(*pos)++;
121
90
		while (isdigit((unsigned char)p[*pos]))
122
			(*pos)++;
123
		goto mod;
124
	case 't':
125
6
		cp->flags |= TBL_CELL_TALIGN;
126
6
		goto mod;
127
	case 'u':
128
		cp->flags |= TBL_CELL_UP;
129
		goto mod;
130
	case 'w':
131
		sz = 0;
132
174
		if (p[*pos] == '(') {
133
18
			(*pos)++;
134

243
			while (p[*pos + sz] != '\0' && p[*pos + sz] != ')')
135
63
				sz++;
136
		} else
137
630
			while (isdigit((unsigned char)p[*pos + sz]))
138
237
				sz++;
139
174
		if (sz) {
140
174
			free(cp->wstr);
141
174
			cp->wstr = mandoc_strndup(p + *pos, sz);
142
174
			*pos += sz;
143
174
			if (p[*pos] == ')')
144
18
				(*pos)++;
145
		}
146
		goto mod;
147
	case 'x':
148
1656
		cp->flags |= TBL_CELL_WMAX;
149
1656
		goto mod;
150
	case 'z':
151
15
		cp->flags |= TBL_CELL_WIGN;
152
15
		goto mod;
153
	case '|':
154
6861
		if (cp->vert < 2)
155
6825
			cp->vert++;
156
		else
157
36
			mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
158
36
			    tbl->parse, ln, *pos - 1, NULL);
159
		goto mod;
160
	default:
161
		mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
162
		    ln, *pos - 1, "%c", p[*pos - 1]);
163
		goto mod;
164
	}
165
166
	/* Ignore parenthised font names for now. */
167
168
180
	if (p[*pos] == '(')
169
		goto mod;
170
171
	/* Support only one-character font-names for now. */
172
173

378
	if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) {
174
108
		mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
175
54
		    ln, *pos, "TS %s", p + *pos - 1);
176
54
		if (p[*pos] != '\0')
177
36
			(*pos)++;
178
54
		if (p[*pos] != '\0')
179
18
			(*pos)++;
180
		goto mod;
181
	}
182
183

270
	switch (p[(*pos)++]) {
184
	case '3':
185
	case 'B':
186
54
		cp->flags |= TBL_CELL_BOLD;
187
54
		goto mod;
188
	case '2':
189
	case 'I':
190
54
		cp->flags |= TBL_CELL_ITALIC;
191
54
		goto mod;
192
	case '1':
193
	case 'R':
194
		goto mod;
195
	default:
196
		mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
197
		    ln, *pos - 1, "TS f%c", p[*pos - 1]);
198
		goto mod;
199
	}
200
53157
}
201
202
static void
203
cell(struct tbl_node *tbl, struct tbl_row *rp,
204
		int ln, const char *p, int *pos)
205
{
206
	int		 i;
207
	enum tbl_cellt	 c;
208
209
	/* Handle leading vertical lines */
210
211

273957
	while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') {
212
2448
		if (p[*pos] == '|') {
213
1548
			if (rp->vert < 2)
214
1548
				rp->vert++;
215
			else
216
				mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
217
				    tbl->parse, ln, *pos, NULL);
218
		}
219
2448
		(*pos)++;
220
	}
221
222
again:
223

159579
	while (p[*pos] == ' ' || p[*pos] == '\t')
224
		(*pos)++;
225
226

106359
	if (p[*pos] == '.' || p[*pos] == '\0')
227
36
		return;
228
229
	/* Parse the column position (`c', `l', `r', ...). */
230
231
384234
	for (i = 0; i < KEYS_MAX; i++)
232
192117
		if (tolower((unsigned char)p[*pos]) == keys[i].name)
233
			break;
234
235
53157
	if (i == KEYS_MAX) {
236
		mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
237
		    ln, *pos, "%c", p[*pos]);
238
		(*pos)++;
239
		goto again;
240
	}
241
53157
	c = keys[i].key;
242
243
	/* Special cases of spanners. */
244
245
53157
	if (c == TBL_CELL_SPAN) {
246
198
		if (rp->last == NULL)
247
			mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN,
248
			    tbl->parse, ln, *pos, NULL);
249

396
		else if (rp->last->pos == TBL_CELL_HORIZ ||
250
198
		    rp->last->pos == TBL_CELL_DHORIZ)
251
			c = rp->last->pos;
252

53019
	} else if (c == TBL_CELL_DOWN && rp == tbl->first_row)
253
18
		mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN,
254
18
		    tbl->parse, ln, *pos, NULL);
255
256
53157
	(*pos)++;
257
258
	/* Allocate cell then parse its modifiers. */
259
260
53157
	mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos);
261
106350
}
262
263
void
264
tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos)
265
{
266
	struct tbl_row	*rp;
267
268
	rp = NULL;
269
73787
	for (;;) {
270
		/* Skip whitespace before and after each cell. */
271
272

232692
		while (p[pos] == ' ' || p[pos] == '\t')
273
5625
			pos++;
274
275

73814
		switch (p[pos]) {
276
		case ',':  /* Next row on this input line. */
277
27
			pos++;
278
			rp = NULL;
279
27
			continue;
280
		case '\0':  /* Next row on next input line. */
281
13657
			return;
282
		case '.':  /* End of layout. */
283
6937
			pos++;
284
6937
			tbl->part = TBL_PART_DATA;
285
286
			/*
287
			 * When the layout is completely empty,
288
			 * default to one left-justified column.
289
			 */
290
291
6937
			if (tbl->first_row == NULL) {
292
18
				tbl->first_row = tbl->last_row =
293
18
				    mandoc_calloc(1, sizeof(*rp));
294
18
			}
295
6937
			if (tbl->first_row->first == NULL) {
296
36
				mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
297
36
				    tbl->parse, ln, pos, NULL);
298
36
				cell_alloc(tbl, tbl->first_row,
299
				    TBL_CELL_LEFT);
300
36
				if (tbl->opts.lvert < tbl->first_row->vert)
301
18
					tbl->opts.lvert = tbl->first_row->vert;
302
36
				return;
303
			}
304
305
			/*
306
			 * Search for the widest line
307
			 * along the left and right margins.
308
			 */
309
310
54972
			for (rp = tbl->first_row; rp; rp = rp->next) {
311
20585
				if (tbl->opts.lvert < rp->vert)
312
1008
					tbl->opts.lvert = rp->vert;
313

41140
				if (rp->last != NULL &&
314
20585
				    rp->last->col + 1 == tbl->opts.cols &&
315
20555
				    tbl->opts.rvert < rp->last->vert)
316
999
					tbl->opts.rvert = rp->last->vert;
317
318
				/* If the last line is empty, drop it. */
319
320

34278
				if (rp->next != NULL &&
321
13693
				    rp->next->first == NULL) {
322
9
					free(rp->next);
323
9
					rp->next = NULL;
324
9
					tbl->last_row = rp;
325
9
				}
326
			}
327
6901
			return;
328
		default:  /* Cell. */
329
			break;
330
		}
331
332
		/*
333
		 * If the last line had at least one cell,
334
		 * start a new one; otherwise, continue it.
335
		 */
336
337
53193
		if (rp == NULL) {
338

34305
			if (tbl->last_row == NULL ||
339
13702
			    tbl->last_row->first != NULL) {
340
20594
				rp = mandoc_calloc(1, sizeof(*rp));
341
20594
				if (tbl->last_row)
342
13693
					tbl->last_row->next = rp;
343
				else
344
6901
					tbl->first_row = rp;
345
20594
				tbl->last_row = rp;
346
20594
			} else
347
				rp = tbl->last_row;
348
		}
349
53193
		cell(tbl, rp, ln, p, &pos);
350
	}
351
20594
}
352
353
static struct tbl_cell *
354
cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
355
{
356
	struct tbl_cell	*p, *pp;
357
358
106386
	p = mandoc_calloc(1, sizeof(*p));
359
53193
	p->spacing = SIZE_MAX;
360
53193
	p->pos = pos;
361
362
53193
	if ((pp = rp->last) != NULL) {
363
32590
		pp->next = p;
364
32590
		p->col = pp->col + 1;
365
32590
	} else
366
20603
		rp->first = p;
367
53193
	rp->last = p;
368
369
53193
	if (tbl->opts.cols <= p->col)
370
18324
		tbl->opts.cols = p->col + 1;
371
372
53193
	return p;
373
}