GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/out.c Lines: 132 145 91.0 %
Date: 2017-11-13 Branches: 108 134 80.6 %

Line Branch Exec Source
1
/*	$OpenBSD: out.c,v 1.42 2017/06/27 18:23:29 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4
 * Copyright (c) 2011, 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 <assert.h>
21
#include <stdint.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <time.h>
25
26
#include "mandoc_aux.h"
27
#include "mandoc.h"
28
#include "out.h"
29
30
static	void	tblcalc_data(struct rofftbl *, struct roffcol *,
31
			const struct tbl_opts *, const struct tbl_dat *,
32
			size_t);
33
static	void	tblcalc_literal(struct rofftbl *, struct roffcol *,
34
			const struct tbl_dat *, size_t);
35
static	void	tblcalc_number(struct rofftbl *, struct roffcol *,
36
			const struct tbl_opts *, const struct tbl_dat *);
37
38
39
/*
40
 * Parse the *src string and store a scaling unit into *dst.
41
 * If the string doesn't specify the unit, use the default.
42
 * If no default is specified, fail.
43
 * Return a pointer to the byte after the last byte used,
44
 * or NULL on total failure.
45
 */
46
const char *
47
a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
48
{
49
24936
	char		*endptr;
50
51
12468
	dst->unit = def == SCALE_MAX ? SCALE_BU : def;
52
12468
	dst->scale = strtod(src, &endptr);
53
12468
	if (endptr == src)
54
321
		return NULL;
55
56


12147
	switch (*endptr++) {
57
	case 'c':
58
		dst->unit = SCALE_CM;
59
15
		break;
60
	case 'i':
61
		dst->unit = SCALE_IN;
62
6720
		break;
63
	case 'f':
64
		dst->unit = SCALE_FS;
65
3
		break;
66
	case 'M':
67
		dst->unit = SCALE_MM;
68
15
		break;
69
	case 'm':
70
		dst->unit = SCALE_EM;
71
15
		break;
72
	case 'n':
73
		dst->unit = SCALE_EN;
74
4260
		break;
75
	case 'P':
76
		dst->unit = SCALE_PC;
77
15
		break;
78
	case 'p':
79
		dst->unit = SCALE_PT;
80
18
		break;
81
	case 'u':
82
		dst->unit = SCALE_BU;
83
15
		break;
84
	case 'v':
85
		dst->unit = SCALE_VS;
86
822
		break;
87
	default:
88
249
		endptr--;
89
249
		if (SCALE_MAX == def)
90
6
			return NULL;
91
		dst->unit = def;
92
243
		break;
93
	}
94
12141
	return endptr;
95
12468
}
96
97
/*
98
 * Calculate the abstract widths and decimal positions of columns in a
99
 * table.  This routine allocates the columns structures then runs over
100
 * all rows and cells in the table.  The function pointers in "tbl" are
101
 * used for the actual width calculations.
102
 */
103
void
104
tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
105
    size_t offset, size_t rmargin)
106
{
107
4408
	struct roffsu		 su;
108
	const struct tbl_opts	*opts;
109
	const struct tbl_dat	*dp;
110
	struct roffcol		*col;
111
	size_t			 ewidth, xwidth;
112
	int			 spans;
113
	int			 icol, maxcol, necol, nxcol, quirkcol;
114
115
	/*
116
	 * Allocate the master column specifiers.  These will hold the
117
	 * widths and decimal positions for all cells in the column.  It
118
	 * must be freed and nullified by the caller.
119
	 */
120
121
2204
	assert(NULL == tbl->cols);
122
2204
	tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
123
	    sizeof(struct roffcol));
124
2204
	opts = sp->opts;
125
126
18742
	for (maxcol = -1; sp; sp = sp->next) {
127
7167
		if (TBL_SPAN_DATA != sp->pos)
128
			continue;
129
		spans = 1;
130
		/*
131
		 * Account for the data cells in the layout, matching it
132
		 * to data cells in the data section.
133
		 */
134
50550
		for (dp = sp->first; dp; dp = dp->next) {
135
			/* Do not used spanned cells in the calculation. */
136
18186
			if (0 < --spans)
137
				continue;
138
18183
			spans = dp->spans;
139
18183
			if (1 < spans)
140
				continue;
141
18180
			icol = dp->layout->col;
142
48070
			while (maxcol < icol)
143
5855
				tbl->cols[++maxcol].spacing = SIZE_MAX;
144
18180
			col = tbl->cols + icol;
145
18180
			col->flags |= dp->layout->flags;
146
18180
			if (dp->layout->flags & TBL_CELL_WIGN)
147
				continue;
148

18186
			if (dp->layout->wstr != NULL &&
149
24
			    dp->layout->width == 0 &&
150
18
			    a2roffsu(dp->layout->wstr, &su, SCALE_EN)
151
18
			    != NULL)
152
18
				dp->layout->width =
153
18
				    (*tbl->sulen)(&su, tbl->arg);
154
18168
			if (col->width < dp->layout->width)
155
18
				col->width = dp->layout->width;
156

18222
			if (dp->layout->spacing != SIZE_MAX &&
157
84
			    (col->spacing == SIZE_MAX ||
158
54
			     col->spacing < dp->layout->spacing))
159
30
				col->spacing = dp->layout->spacing;
160
18168
			tblcalc_data(tbl, col, opts, dp,
161
36336
			    dp->block == 0 ? 0 :
162
99
			    dp->layout->width ? dp->layout->width :
163
225
			    rmargin ? (rmargin + sp->opts->cols / 2)
164
75
			    / (sp->opts->cols + 1) : 0);
165
18168
		}
166
	}
167
168
	/*
169
	 * Count columns to equalize and columns to maximize.
170
	 * Find maximum width of the columns to equalize.
171
	 * Find total width of the columns *not* to maximize.
172
	 */
173
174
	necol = nxcol = 0;
175
	ewidth = xwidth = 0;
176
16118
	for (icol = 0; icol <= maxcol; icol++) {
177
5855
		col = tbl->cols + icol;
178

5885
		if (col->spacing == SIZE_MAX || icol == maxcol)
179
5831
			col->spacing = 3;
180
5855
		if (col->flags & TBL_CELL_EQUAL) {
181
			necol++;
182
			if (ewidth < col->width)
183
				ewidth = col->width;
184
		}
185
5855
		if (col->flags & TBL_CELL_WMAX)
186
552
			nxcol++;
187
		else
188
5303
			xwidth += col->width;
189
	}
190
191
	/*
192
	 * Equalize columns, if requested for any of them.
193
	 * Update total width of the columns not to maximize.
194
	 */
195
196
2204
	if (necol) {
197
		for (icol = 0; icol <= maxcol; icol++) {
198
			col = tbl->cols + icol;
199
			if ( ! (col->flags & TBL_CELL_EQUAL))
200
				continue;
201
			if (col->width == ewidth)
202
				continue;
203
			if (nxcol && rmargin)
204
				xwidth += ewidth - col->width;
205
			col->width = ewidth;
206
		}
207
	}
208
209
	/*
210
	 * If there are any columns to maximize, find the total
211
	 * available width, deducting 3n margins between columns.
212
	 * Distribute the available width evenly.
213
	 */
214
215
2204
	if (nxcol && rmargin) {
216
270
		xwidth += 3*maxcol +
217
405
		    (opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
218
135
		     2 : !!opts->lvert + !!opts->rvert);
219
135
		if (rmargin <= offset + xwidth)
220
3
			return;
221
132
		xwidth = rmargin - offset - xwidth;
222
223
		/*
224
		 * Emulate a bug in GNU tbl width calculation that
225
		 * manifests itself for large numbers of x-columns.
226
		 * Emulating it for 5 x-columns gives identical
227
		 * behaviour for up to 6 x-columns.
228
		 */
229
230
132
		if (nxcol == 5) {
231
30
			quirkcol = xwidth % nxcol + 2;
232
30
			if (quirkcol != 3 && quirkcol != 4)
233
				quirkcol = -1;
234
		} else
235
			quirkcol = -1;
236
237
		necol = 0;
238
		ewidth = 0;
239
1620
		for (icol = 0; icol <= maxcol; icol++) {
240
678
			col = tbl->cols + icol;
241
678
			if ( ! (col->flags & TBL_CELL_WMAX))
242
				continue;
243
1092
			col->width = (double)xwidth * ++necol / nxcol
244
546
			    - ewidth + 0.4995;
245
546
			if (necol == quirkcol)
246
12
				col->width--;
247
546
			ewidth += col->width;
248
546
		}
249
	}
250
4405
}
251
252
static void
253
tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
254
    const struct tbl_opts *opts, const struct tbl_dat *dp, size_t mw)
255
{
256
	size_t		 sz;
257
258
	/* Branch down into data sub-types. */
259
260


54495
	switch (dp->layout->pos) {
261
	case TBL_CELL_HORIZ:
262
	case TBL_CELL_DHORIZ:
263
1368
		sz = (*tbl->len)(1, tbl->arg);
264
1368
		if (col->width < sz)
265
420
			col->width = sz;
266
		break;
267
	case TBL_CELL_LONG:
268
	case TBL_CELL_CENTRE:
269
	case TBL_CELL_LEFT:
270
	case TBL_CELL_RIGHT:
271
16764
		tblcalc_literal(tbl, col, dp, mw);
272
16764
		break;
273
	case TBL_CELL_NUMBER:
274
27
		tblcalc_number(tbl, col, opts, dp);
275
27
		break;
276
	case TBL_CELL_DOWN:
277
		break;
278
	default:
279
		abort();
280
	}
281
18168
}
282
283
static void
284
tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
285
    const struct tbl_dat *dp, size_t mw)
286
{
287
	const char	*str;	/* Beginning of the first line. */
288
	const char	*beg;	/* Beginning of the current line. */
289
	char		*end;	/* End of the current line. */
290
	size_t		 lsz;	/* Length of the current line. */
291
	size_t		 wsz;	/* Length of the current word. */
292
293

50292
	if (dp->string == NULL || *dp->string == '\0')
294
32
		return;
295
33551
	str = mw ? mandoc_strdup(dp->string) : dp->string;
296
	lsz = 0;
297

117556
	for (beg = str; beg != NULL && *beg != '\0'; beg = end) {
298
33875
		end = mw ? strchr(beg, ' ') : NULL;
299
16840
		if (end != NULL) {
300
108
			*end++ = '\0';
301
216
			while (*end == ' ')
302
				end++;
303
		}
304
16840
		wsz = (*tbl->slen)(beg, tbl->arg);
305

16948
		if (mw && lsz && lsz + 1 + wsz <= mw)
306
63
			lsz += 1 + wsz;
307
		else
308
			lsz = wsz;
309
16840
		if (col->width < lsz)
310
6086
			col->width = lsz;
311
	}
312
16732
	if (mw)
313
87
		free((void *)str);
314
33496
}
315
316
static void
317
tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
318
		const struct tbl_opts *opts, const struct tbl_dat *dp)
319
{
320
	int		 i;
321
	size_t		 sz, psz, ssz, d;
322
	const char	*str;
323
	char		*cp;
324
54
	char		 buf[2];
325
326
	/*
327
	 * First calculate number width and decimal place (last + 1 for
328
	 * non-decimal numbers).  If the stored decimal is subsequent to
329
	 * ours, make our size longer by that difference
330
	 * (right-"shifting"); similarly, if ours is subsequent the
331
	 * stored, then extend the stored size by the difference.
332
	 * Finally, re-assign the stored values.
333
	 */
334
335
81
	str = dp->string ? dp->string : "";
336
27
	sz = (*tbl->slen)(str, tbl->arg);
337
338
	/* FIXME: TBL_DATA_HORIZ et al.? */
339
340
27
	buf[0] = opts->decimal;
341
27
	buf[1] = '\0';
342
343
27
	psz = (*tbl->slen)(buf, tbl->arg);
344
345
27
	if (NULL != (cp = strrchr(str, opts->decimal))) {
346
27
		buf[1] = '\0';
347
150
		for (ssz = 0, i = 0; cp != &str[i]; i++) {
348
48
			buf[0] = str[i];
349
48
			ssz += (*tbl->slen)(buf, tbl->arg);
350
		}
351
27
		d = ssz + psz;
352
27
	} else
353
		d = sz + psz;
354
355
	/* Adjust the settings for this column. */
356
357
27
	if (col->decimal > d) {
358
6
		sz += col->decimal - d;
359
		d = col->decimal;
360
6
	} else
361
21
		col->width += d - col->decimal;
362
363
27
	if (sz > col->width)
364
21
		col->width = sz;
365
27
	if (d > col->decimal)
366
21
		col->decimal = d;
367
27
}