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

Line Branch Exec Source
1
/*	$OpenBSD: tbl_layout.c,v 1.28 2015/10/12 00:07:27 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4
 * Copyright (c) 2012, 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 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 <stdlib.h>
22
#include <string.h>
23
#include <time.h>
24
25
#include "mandoc.h"
26
#include "mandoc_aux.h"
27
#include "libmandoc.h"
28
#include "libroff.h"
29
30
struct	tbl_phrase {
31
	char		 name;
32
	enum tbl_cellt	 key;
33
};
34
35
static	const struct tbl_phrase keys[] = {
36
	{ 'c',		 TBL_CELL_CENTRE },
37
	{ 'r',		 TBL_CELL_RIGHT },
38
	{ 'l',		 TBL_CELL_LEFT },
39
	{ 'n',		 TBL_CELL_NUMBER },
40
	{ 's',		 TBL_CELL_SPAN },
41
	{ 'a',		 TBL_CELL_LONG },
42
	{ '^',		 TBL_CELL_DOWN },
43
	{ '-',		 TBL_CELL_HORIZ },
44
	{ '_',		 TBL_CELL_HORIZ },
45
	{ '=',		 TBL_CELL_DHORIZ }
46
};
47
48
#define KEYS_MAX ((int)(sizeof(keys)/sizeof(keys[0])))
49
50
static	void		 mods(struct tbl_node *, struct tbl_cell *,
51
				int, const char *, int *);
52
static	void		 cell(struct tbl_node *, struct tbl_row *,
53
				int, const char *, int *);
54
static	struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
55
				enum tbl_cellt);
56
57
58
static void
59
mods(struct tbl_node *tbl, struct tbl_cell *cp,
60
		int ln, const char *p, int *pos)
61
{
62
	char		*endptr;
63
64
mod:
65
	while (p[*pos] == ' ' || p[*pos] == '\t')
66
		(*pos)++;
67
68
	/* Row delimiters and cell specifiers end modifier lists. */
69
70
	if (strchr(".,-=^_ACLNRSaclnrs", p[*pos]) != NULL)
71
		return;
72
73
	/* Throw away parenthesised expression. */
74
75
	if ('(' == p[*pos]) {
76
		(*pos)++;
77
		while (p[*pos] && ')' != p[*pos])
78
			(*pos)++;
79
		if (')' == p[*pos]) {
80
			(*pos)++;
81
			goto mod;
82
		}
83
		mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, tbl->parse,
84
		    ln, *pos, NULL);
85
		return;
86
	}
87
88
	/* Parse numerical spacing from modifier string. */
89
90
	if (isdigit((unsigned char)p[*pos])) {
91
		cp->spacing = strtoull(p + *pos, &endptr, 10);
92
		*pos = endptr - p;
93
		goto mod;
94
	}
95
96
	switch (tolower((unsigned char)p[(*pos)++])) {
97
	case 'b':
98
		cp->flags |= TBL_CELL_BOLD;
99
		goto mod;
100
	case 'd':
101
		cp->flags |= TBL_CELL_BALIGN;
102
		goto mod;
103
	case 'e':
104
		cp->flags |= TBL_CELL_EQUAL;
105
		goto mod;
106
	case 'f':
107
		break;
108
	case 'i':
109
		cp->flags |= TBL_CELL_ITALIC;
110
		goto mod;
111
	case 'm':
112
		mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, tbl->parse,
113
		    ln, *pos, "m");
114
		goto mod;
115
	case 'p':
116
	case 'v':
117
		if (p[*pos] == '-' || p[*pos] == '+')
118
			(*pos)++;
119
		while (isdigit((unsigned char)p[*pos]))
120
			(*pos)++;
121
		goto mod;
122
	case 't':
123
		cp->flags |= TBL_CELL_TALIGN;
124
		goto mod;
125
	case 'u':
126
		cp->flags |= TBL_CELL_UP;
127
		goto mod;
128
	case 'w':  /* XXX for now, ignore minimal column width */
129
		goto mod;
130
	case 'x':
131
		cp->flags |= TBL_CELL_WMAX;
132
		goto mod;
133
	case 'z':
134
		cp->flags |= TBL_CELL_WIGN;
135
		goto mod;
136
	case '|':
137
		if (cp->vert < 2)
138
			cp->vert++;
139
		else
140
			mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
141
			    tbl->parse, ln, *pos - 1, NULL);
142
		goto mod;
143
	default:
144
		mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
145
		    ln, *pos - 1, "%c", p[*pos - 1]);
146
		goto mod;
147
	}
148
149
	/* Ignore parenthised font names for now. */
150
151
	if (p[*pos] == '(')
152
		goto mod;
153
154
	/* Support only one-character font-names for now. */
155
156
	if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) {
157
		mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
158
		    ln, *pos, "TS %s", p + *pos - 1);
159
		if (p[*pos] != '\0')
160
			(*pos)++;
161
		if (p[*pos] != '\0')
162
			(*pos)++;
163
		goto mod;
164
	}
165
166
	switch (p[(*pos)++]) {
167
	case '3':
168
	case 'B':
169
		cp->flags |= TBL_CELL_BOLD;
170
		goto mod;
171
	case '2':
172
	case 'I':
173
		cp->flags |= TBL_CELL_ITALIC;
174
		goto mod;
175
	case '1':
176
	case 'R':
177
		goto mod;
178
	default:
179
		mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
180
		    ln, *pos - 1, "TS f%c", p[*pos - 1]);
181
		goto mod;
182
	}
183
}
184
185
static void
186
cell(struct tbl_node *tbl, struct tbl_row *rp,
187
		int ln, const char *p, int *pos)
188
{
189
	int		 i;
190
	enum tbl_cellt	 c;
191
192
	/* Handle leading vertical lines */
193
194
	while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') {
195
		if (p[*pos] == '|') {
196
			if (rp->vert < 2)
197
				rp->vert++;
198
			else
199
				mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
200
				    tbl->parse, ln, *pos, NULL);
201
		}
202
		(*pos)++;
203
	}
204
205
again:
206
	while (p[*pos] == ' ' || p[*pos] == '\t')
207
		(*pos)++;
208
209
	if (p[*pos] == '.' || p[*pos] == '\0')
210
		return;
211
212
	/* Parse the column position (`c', `l', `r', ...). */
213
214
	for (i = 0; i < KEYS_MAX; i++)
215
		if (tolower((unsigned char)p[*pos]) == keys[i].name)
216
			break;
217
218
	if (i == KEYS_MAX) {
219
		mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
220
		    ln, *pos, "%c", p[*pos]);
221
		(*pos)++;
222
		goto again;
223
	}
224
	c = keys[i].key;
225
226
	/* Special cases of spanners. */
227
228
	if (c == TBL_CELL_SPAN) {
229
		if (rp->last == NULL)
230
			mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN,
231
			    tbl->parse, ln, *pos, NULL);
232
		else if (rp->last->pos == TBL_CELL_HORIZ ||
233
		    rp->last->pos == TBL_CELL_DHORIZ)
234
			c = rp->last->pos;
235
	} else if (c == TBL_CELL_DOWN && rp == tbl->first_row)
236
		mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN,
237
		    tbl->parse, ln, *pos, NULL);
238
239
	(*pos)++;
240
241
	/* Allocate cell then parse its modifiers. */
242
243
	mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos);
244
}
245
246
void
247
tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos)
248
{
249
	struct tbl_row	*rp;
250
251
	rp = NULL;
252
	for (;;) {
253
		/* Skip whitespace before and after each cell. */
254
255
		while (p[pos] == ' ' || p[pos] == '\t')
256
			pos++;
257
258
		switch (p[pos]) {
259
		case ',':  /* Next row on this input line. */
260
			pos++;
261
			rp = NULL;
262
			continue;
263
		case '\0':  /* Next row on next input line. */
264
			return;
265
		case '.':  /* End of layout. */
266
			pos++;
267
			tbl->part = TBL_PART_DATA;
268
269
			/*
270
			 * When the layout is completely empty,
271
			 * default to one left-justified column.
272
			 */
273
274
			if (tbl->first_row == NULL) {
275
				tbl->first_row = tbl->last_row =
276
				    mandoc_calloc(1, sizeof(*rp));
277
			}
278
			if (tbl->first_row->first == NULL) {
279
				mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
280
				    tbl->parse, ln, pos, NULL);
281
				cell_alloc(tbl, tbl->first_row,
282
				    TBL_CELL_LEFT);
283
				return;
284
			}
285
286
			/*
287
			 * Search for the widest line
288
			 * along the left and right margins.
289
			 */
290
291
			for (rp = tbl->first_row; rp; rp = rp->next) {
292
				if (tbl->opts.lvert < rp->vert)
293
					tbl->opts.lvert = rp->vert;
294
				if (rp->last != NULL &&
295
				    rp->last->col + 1 == tbl->opts.cols &&
296
				    tbl->opts.rvert < rp->last->vert)
297
					tbl->opts.rvert = rp->last->vert;
298
299
				/* If the last line is empty, drop it. */
300
301
				if (rp->next != NULL &&
302
				    rp->next->first == NULL) {
303
					free(rp->next);
304
					rp->next = NULL;
305
					tbl->last_row = rp;
306
				}
307
			}
308
			return;
309
		default:  /* Cell. */
310
			break;
311
		}
312
313
		/*
314
		 * If the last line had at least one cell,
315
		 * start a new one; otherwise, continue it.
316
		 */
317
318
		if (rp == NULL) {
319
			if (tbl->last_row == NULL ||
320
			    tbl->last_row->first != NULL) {
321
				rp = mandoc_calloc(1, sizeof(*rp));
322
				if (tbl->last_row)
323
					tbl->last_row->next = rp;
324
				else
325
					tbl->first_row = rp;
326
				tbl->last_row = rp;
327
			} else
328
				rp = tbl->last_row;
329
		}
330
		cell(tbl, rp, ln, p, &pos);
331
	}
332
}
333
334
static struct tbl_cell *
335
cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
336
{
337
	struct tbl_cell	*p, *pp;
338
339
	p = mandoc_calloc(1, sizeof(*p));
340
	p->pos = pos;
341
342
	if ((pp = rp->last) != NULL) {
343
		pp->next = p;
344
		p->col = pp->col + 1;
345
	} else
346
		rp->first = p;
347
	rp->last = p;
348
349
	if (tbl->opts.cols <= p->col)
350
		tbl->opts.cols = p->col + 1;
351
352
	return p;
353
}