GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/tbl_layout.c Lines: 146 170 85.9 %
Date: 2017-11-13 Branches: 118 146 80.8 %

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
35498
	char		*endptr;
64
17749
	size_t		 sz;
65
66
mod:
67

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

126
		while (p[*pos] && ')' != p[*pos])
80
			(*pos)++;
81
6
		if (')' == p[*pos]) {
82
6
			(*pos)++;
83
6
			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
3109
	if (isdigit((unsigned char)p[*pos])) {
93
82
		cp->spacing = strtoull(p + *pos, &endptr, 10);
94
82
		*pos = endptr - p;
95
82
		goto mod;
96
	}
97
98



3027
	switch (tolower((unsigned char)p[(*pos)++])) {
99
	case 'b':
100
27
		cp->flags |= TBL_CELL_BOLD;
101
27
		goto mod;
102
	case 'd':
103
		cp->flags |= TBL_CELL_BALIGN;
104
		goto mod;
105
	case 'e':
106
2
		cp->flags |= TBL_CELL_EQUAL;
107
2
		goto mod;
108
	case 'f':
109
		break;
110
	case 'i':
111
28
		cp->flags |= TBL_CELL_ITALIC;
112
28
		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

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

81
			while (p[*pos + sz] != '\0' && p[*pos + sz] != ')')
135
21
				sz++;
136
		} else
137
262
			while (isdigit((unsigned char)p[*pos + sz]))
138
79
				sz++;
139
58
		if (sz) {
140
58
			free(cp->wstr);
141
58
			cp->wstr = mandoc_strndup(p + *pos, sz);
142
58
			*pos += sz;
143
58
			if (p[*pos] == ')')
144
6
				(*pos)++;
145
		}
146
58
		goto mod;
147
	case 'x':
148
552
		cp->flags |= TBL_CELL_WMAX;
149
552
		goto mod;
150
	case 'z':
151
5
		cp->flags |= TBL_CELL_WIGN;
152
5
		goto mod;
153
	case '|':
154
2287
		if (cp->vert < 2)
155
2275
			cp->vert++;
156
		else
157
12
			mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
158
12
			    tbl->parse, ln, *pos - 1, NULL);
159
2287
		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
60
	if (p[*pos] == '(')
169
6
		goto mod;
170
171
	/* Support only one-character font-names for now. */
172
173

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

36
	switch (p[(*pos)++]) {
184
	case '3':
185
	case 'B':
186
18
		cp->flags |= TBL_CELL_BOLD;
187
18
		goto mod;
188
	case '2':
189
	case 'I':
190
18
		cp->flags |= TBL_CELL_ITALIC;
191
18
		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
17749
}
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

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

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

35513
	if (p[*pos] == '.' || p[*pos] == '\0')
227
12
		return;
228
229
	/* Parse the column position (`c', `l', `r', ...). */
230
231
128258
	for (i = 0; i < KEYS_MAX; i++)
232
64129
		if (tolower((unsigned char)p[*pos]) == keys[i].name)
233
			break;
234
235
17749
	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
17749
	c = keys[i].key;
242
243
	/* Special cases of spanners. */
244
245
17749
	if (c == TBL_CELL_SPAN) {
246
66
		if (rp->last == NULL)
247
			mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN,
248
			    tbl->parse, ln, *pos, NULL);
249

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

17703
	} else if (c == TBL_CELL_DOWN && rp == tbl->first_row)
253
6
		mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN,
254
6
		    tbl->parse, ln, *pos, NULL);
255
256
17749
	(*pos)++;
257
258
	/* Allocate cell then parse its modifiers. */
259
260
17749
	mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos);
261
35510
}
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
24633
	for (;;) {
270
		/* Skip whitespace before and after each cell. */
271
272

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

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

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

11433
				if (rp->next != NULL &&
321
4564
				    rp->next->first == NULL) {
322
3
					free(rp->next);
323
3
					rp->next = NULL;
324
3
					tbl->last_row = rp;
325
3
				}
326
			}
327
2308
			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
17761
		if (rp == NULL) {
338

11442
			if (tbl->last_row == NULL ||
339
4567
			    tbl->last_row->first != NULL) {
340
6872
				rp = mandoc_calloc(1, sizeof(*rp));
341
6872
				if (tbl->last_row)
342
4564
					tbl->last_row->next = rp;
343
				else
344
2308
					tbl->first_row = rp;
345
6872
				tbl->last_row = rp;
346
6872
			} else
347
				rp = tbl->last_row;
348
		}
349
17761
		cell(tbl, rp, ln, p, &pos);
350
	}
351
6872
}
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
35522
	p = mandoc_calloc(1, sizeof(*p));
359
17761
	p->spacing = SIZE_MAX;
360
17761
	p->pos = pos;
361
362
17761
	if ((pp = rp->last) != NULL) {
363
10886
		pp->next = p;
364
10886
		p->col = pp->col + 1;
365
10886
	} else
366
6875
		rp->first = p;
367
17761
	rp->last = p;
368
369
17761
	if (tbl->opts.cols <= p->col)
370
6139
		tbl->opts.cols = p->col + 1;
371
372
17761
	return p;
373
}