| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: tbl_term.c,v 1.45 2017/07/31 16:14:04 schwarze Exp $ */  | 
    ||
2  | 
    /*  | 
    ||
3  | 
    * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>  | 
    ||
4  | 
    * Copyright (c) 2011,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 <assert.h>  | 
    ||
21  | 
    #include <stdio.h>  | 
    ||
22  | 
    #include <stdlib.h>  | 
    ||
23  | 
    #include <string.h>  | 
    ||
24  | 
    |||
25  | 
    #include "mandoc.h"  | 
    ||
26  | 
    #include "out.h"  | 
    ||
27  | 
    #include "term.h"  | 
    ||
28  | 
    |||
29  | 
    #define IS_HORIZ(cp) ((cp)->pos == TBL_CELL_HORIZ || \  | 
    ||
30  | 
    (cp)->pos == TBL_CELL_DHORIZ)  | 
    ||
31  | 
    |||
32  | 
    static size_t term_tbl_len(size_t, void *);  | 
    ||
33  | 
    static size_t term_tbl_strlen(const char *, void *);  | 
    ||
34  | 
    static size_t term_tbl_sulen(const struct roffsu *, void *);  | 
    ||
35  | 
    static void tbl_char(struct termp *, char, size_t);  | 
    ||
36  | 
    static void tbl_data(struct termp *, const struct tbl_opts *,  | 
    ||
37  | 
    const struct tbl_cell *,  | 
    ||
38  | 
    const struct tbl_dat *,  | 
    ||
39  | 
    const struct roffcol *);  | 
    ||
40  | 
    static void tbl_literal(struct termp *, const struct tbl_dat *,  | 
    ||
41  | 
    const struct roffcol *);  | 
    ||
42  | 
    static void tbl_number(struct termp *, const struct tbl_opts *,  | 
    ||
43  | 
    const struct tbl_dat *,  | 
    ||
44  | 
    const struct roffcol *);  | 
    ||
45  | 
    static void tbl_hrule(struct termp *, const struct tbl_span *, int);  | 
    ||
46  | 
    static void tbl_word(struct termp *, const struct tbl_dat *);  | 
    ||
47  | 
    |||
48  | 
    |||
49  | 
    static size_t  | 
    ||
50  | 
    term_tbl_sulen(const struct roffsu *su, void *arg)  | 
    ||
51  | 
    { | 
    ||
52  | 
    int i;  | 
    ||
53  | 
    |||
54  | 
    108  | 
    i = term_hen((const struct termp *)arg, su);  | 
    |
55  | 
    54  | 
    return i > 0 ? i : 0;  | 
    |
56  | 
    }  | 
    ||
57  | 
    |||
58  | 
    static size_t  | 
    ||
59  | 
    term_tbl_strlen(const char *p, void *arg)  | 
    ||
60  | 
    { | 
    ||
61  | 
    99684  | 
    return term_strlen((const struct termp *)arg, p);  | 
    |
62  | 
    }  | 
    ||
63  | 
    |||
64  | 
    static size_t  | 
    ||
65  | 
    term_tbl_len(size_t sz, void *arg)  | 
    ||
66  | 
    { | 
    ||
67  | 
    8208  | 
    return term_len((const struct termp *)arg, sz);  | 
    |
68  | 
    }  | 
    ||
69  | 
    |||
70  | 
    void  | 
    ||
71  | 
    term_tbl(struct termp *tp, const struct tbl_span *sp)  | 
    ||
72  | 
    { | 
    ||
73  | 
    const struct tbl_cell *cp, *cpn, *cpp;  | 
    ||
74  | 
    const struct tbl_dat *dp;  | 
    ||
75  | 
    static size_t offset;  | 
    ||
76  | 
    size_t coloff, tsz;  | 
    ||
77  | 
    int ic, horiz, spans, vert, more;  | 
    ||
78  | 
    char fc;  | 
    ||
79  | 
    |||
80  | 
    /* Inhibit printing of spaces: we do padding ourselves. */  | 
    ||
81  | 
    |||
82  | 
    42498  | 
    tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;  | 
    |
83  | 
    |||
84  | 
    /*  | 
    ||
85  | 
    * The first time we're invoked for a given table block,  | 
    ||
86  | 
    * calculate the table widths and decimal positions.  | 
    ||
87  | 
    */  | 
    ||
88  | 
    |||
89  | 
    ✓✓ | 21249  | 
    	if (tp->tbl.cols == NULL) { | 
    
90  | 
    6588  | 
    tp->tbl.len = term_tbl_len;  | 
    |
91  | 
    6588  | 
    tp->tbl.slen = term_tbl_strlen;  | 
    |
92  | 
    6588  | 
    tp->tbl.sulen = term_tbl_sulen;  | 
    |
93  | 
    6588  | 
    tp->tbl.arg = tp;  | 
    |
94  | 
    |||
95  | 
    6588  | 
    tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin);  | 
    |
96  | 
    |||
97  | 
    /* Tables leak .ta settings to subsequent text. */  | 
    ||
98  | 
    |||
99  | 
    6588  | 
    term_tab_set(tp, NULL);  | 
    |
100  | 
    ✓✓ | 15066  | 
    coloff = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||  | 
    
101  | 
    1890  | 
    sp->opts->lvert;  | 
    |
102  | 
    ✓✓ | 48114  | 
    		for (ic = 0; ic < sp->opts->cols; ic++) { | 
    
103  | 
    17469  | 
    coloff += tp->tbl.cols[ic].width;  | 
    |
104  | 
    17469  | 
    term_tab_iset(coloff);  | 
    |
105  | 
    17469  | 
    coloff += tp->tbl.cols[ic].spacing;  | 
    |
106  | 
    }  | 
    ||
107  | 
    |||
108  | 
    /* Center the table as a whole. */  | 
    ||
109  | 
    |||
110  | 
    6588  | 
    offset = tp->tcol->offset;  | 
    |
111  | 
    ✓✓ | 6588  | 
    		if (sp->opts->opts & TBL_OPT_CENTRE) { | 
    
112  | 
    ✓✓ | 522  | 
    tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)  | 
    
113  | 
    162  | 
    ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;  | 
    |
114  | 
    ✓✓ | 396  | 
    for (ic = 0; ic + 1 < sp->opts->cols; ic++)  | 
    
115  | 
    36  | 
    tsz += tp->tbl.cols[ic].width +  | 
    |
116  | 
    18  | 
    tp->tbl.cols[ic].spacing;  | 
    |
117  | 
    ✓✗ | 180  | 
    if (sp->opts->cols)  | 
    
118  | 
    180  | 
    tsz += tp->tbl.cols[sp->opts->cols - 1].width;  | 
    |
119  | 
    ✓✓ | 180  | 
    if (offset + tsz > tp->tcol->rmargin)  | 
    
120  | 
    54  | 
    tsz -= 1;  | 
    |
121  | 
    ✓✓ | 531  | 
    tp->tcol->offset = offset + tp->tcol->rmargin > tsz ?  | 
    
122  | 
    171  | 
    (offset + tp->tcol->rmargin - tsz) / 2 : 0;  | 
    |
123  | 
    180  | 
    }  | 
    |
124  | 
    |||
125  | 
    /* Horizontal frame at the start of boxed tables. */  | 
    ||
126  | 
    |||
127  | 
    ✓✓ | 6588  | 
    if (sp->opts->opts & TBL_OPT_DBOX)  | 
    
128  | 
    2196  | 
    tbl_hrule(tp, sp, 3);  | 
    |
129  | 
    ✓✓ | 6588  | 
    if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))  | 
    
130  | 
    4698  | 
    tbl_hrule(tp, sp, 2);  | 
    |
131  | 
    }  | 
    ||
132  | 
    |||
133  | 
    /* Set up the columns. */  | 
    ||
134  | 
    |||
135  | 
    42498  | 
    tp->flags |= TERMP_MULTICOL;  | 
    |
136  | 
    horiz = 0;  | 
    ||
137  | 
    ✗✓✓✓ | 
    42498  | 
    	switch (sp->pos) { | 
    
138  | 
    case TBL_SPAN_HORIZ:  | 
    ||
139  | 
    case TBL_SPAN_DHORIZ:  | 
    ||
140  | 
    horiz = 1;  | 
    ||
141  | 
    234  | 
    term_setcol(tp, 1);  | 
    |
142  | 
    234  | 
    break;  | 
    |
143  | 
    case TBL_SPAN_DATA:  | 
    ||
144  | 
    21015  | 
    term_setcol(tp, sp->opts->cols + 2);  | 
    |
145  | 
    21015  | 
    coloff = tp->tcol->offset;  | 
    |
146  | 
    |||
147  | 
    /* Set up a column for a left vertical frame. */  | 
    ||
148  | 
    |||
149  | 
    ✓✓✓✓ | 
    26442  | 
    if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||  | 
    
150  | 
    5427  | 
    sp->opts->lvert)  | 
    |
151  | 
    16335  | 
    coloff++;  | 
    |
152  | 
    21015  | 
    tp->tcol->rmargin = coloff;  | 
    |
153  | 
    |||
154  | 
    /* Set up the data columns. */  | 
    ||
155  | 
    |||
156  | 
    21015  | 
    dp = sp->first;  | 
    |
157  | 
    spans = 0;  | 
    ||
158  | 
    ✓✓ | 152316  | 
    		for (ic = 0; ic < sp->opts->cols; ic++) { | 
    
159  | 
    ✓✓ | 55143  | 
    			if (spans == 0) { | 
    
160  | 
    55008  | 
    tp->tcol++;  | 
    |
161  | 
    55008  | 
    tp->tcol->offset = coloff;  | 
    |
162  | 
    55008  | 
    }  | 
    |
163  | 
    55143  | 
    coloff += tp->tbl.cols[ic].width;  | 
    |
164  | 
    55143  | 
    tp->tcol->rmargin = coloff;  | 
    |
165  | 
    ✓✓ | 55143  | 
    if (ic + 1 < sp->opts->cols)  | 
    
166  | 
    34128  | 
    coloff += tp->tbl.cols[ic].spacing;  | 
    |
167  | 
    ✓✓ | 55143  | 
    			if (spans) { | 
    
168  | 
    135  | 
    spans--;  | 
    |
169  | 
    135  | 
    continue;  | 
    |
170  | 
    }  | 
    ||
171  | 
    ✓✓ | 55008  | 
    if (dp == NULL)  | 
    
172  | 
    continue;  | 
    ||
173  | 
    53550  | 
    spans = dp->spans;  | 
    |
174  | 
    ✓✓✓✗ | 
    74538  | 
    if (ic || sp->layout->first->pos != TBL_CELL_SPAN)  | 
    
175  | 
    53550  | 
    dp = dp->next;  | 
    |
176  | 
    }  | 
    ||
177  | 
    |||
178  | 
    /* Set up a column for a right vertical frame. */  | 
    ||
179  | 
    |||
180  | 
    21015  | 
    tp->tcol++;  | 
    |
181  | 
    21015  | 
    tp->tcol->offset = coloff + 1;  | 
    |
182  | 
    21015  | 
    tp->tcol->rmargin = tp->maxrmargin;  | 
    |
183  | 
    |||
184  | 
    /* Spans may have reduced the number of columns. */  | 
    ||
185  | 
    |||
186  | 
    21015  | 
    tp->lasttcol = tp->tcol - tp->tcols;  | 
    |
187  | 
    |||
188  | 
    /* Fill the buffers for all data columns. */  | 
    ||
189  | 
    |||
190  | 
    21015  | 
    tp->tcol = tp->tcols;  | 
    |
191  | 
    21015  | 
    cp = cpn = sp->layout->first;  | 
    |
192  | 
    21015  | 
    dp = sp->first;  | 
    |
193  | 
    spans = 0;  | 
    ||
194  | 
    ✓✓ | 152316  | 
    		for (ic = 0; ic < sp->opts->cols; ic++) { | 
    
195  | 
    ✓✗ | 55143  | 
    			if (cpn != NULL) { | 
    
196  | 
    cp = cpn;  | 
    ||
197  | 
    55143  | 
    cpn = cpn->next;  | 
    |
198  | 
    55143  | 
    }  | 
    |
199  | 
    ✓✓ | 55143  | 
    			if (spans) { | 
    
200  | 
    135  | 
    spans--;  | 
    |
201  | 
    135  | 
    continue;  | 
    |
202  | 
    }  | 
    ||
203  | 
    55008  | 
    tp->tcol++;  | 
    |
204  | 
    55008  | 
    tp->col = 0;  | 
    |
205  | 
    55008  | 
    tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);  | 
    |
206  | 
    ✓✓ | 55008  | 
    if (dp == NULL)  | 
    
207  | 
    continue;  | 
    ||
208  | 
    53550  | 
    spans = dp->spans;  | 
    |
209  | 
    ✓✗ | 53550  | 
    if (cp->pos != TBL_CELL_SPAN)  | 
    
210  | 
    53550  | 
    dp = dp->next;  | 
    |
211  | 
    }  | 
    ||
212  | 
    break;  | 
    ||
213  | 
    }  | 
    ||
214  | 
    |||
215  | 
    	do { | 
    ||
216  | 
    /* Print the vertical frame at the start of each row. */  | 
    ||
217  | 
    |||
218  | 
    21384  | 
    tp->tcol = tp->tcols;  | 
    |
219  | 
    fc = '\0';  | 
    ||
220  | 
    ✓✓✓✓ | 
    40662  | 
    if (sp->layout->vert ||  | 
    
221  | 
    ✓✓✓✓ | 
    33957  | 
    (sp->next != NULL && sp->next->layout->vert &&  | 
    
222  | 
    ✓✓ | 729  | 
    sp->next->pos == TBL_SPAN_DATA) ||  | 
    
223  | 
    ✓✓✓✓ | 
    32688  | 
    (sp->prev != NULL && sp->prev->layout->vert &&  | 
    
224  | 
    ✓✓✓✓ ✗✓  | 
    1989  | 
    (horiz || (IS_HORIZ(sp->layout->first) &&  | 
    
225  | 
    ✓✗✗✓ | 
    360  | 
    !IS_HORIZ(sp->prev->layout->first)))) ||  | 
    
226  | 
    19278  | 
    sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))  | 
    |
227  | 
    ✓✓✓✓ | 
    63972  | 
    fc = horiz || IS_HORIZ(sp->layout->first) ? '+' : '|';  | 
    
228  | 
    ✓✓✓✓ | 
    5004  | 
    else if (horiz && sp->opts->lvert)  | 
    
229  | 
    9  | 
    fc = '-';  | 
    |
230  | 
    ✓✓ | 21384  | 
    		if (fc != '\0') { | 
    
231  | 
    16416  | 
    (*tp->advance)(tp, tp->tcols->offset);  | 
    |
232  | 
    16416  | 
    (*tp->letter)(tp, fc);  | 
    |
233  | 
    16416  | 
    tp->viscol = tp->tcol->offset + 1;  | 
    |
234  | 
    16416  | 
    }  | 
    |
235  | 
    |||
236  | 
    /* Print the data cells. */  | 
    ||
237  | 
    |||
238  | 
    more = 0;  | 
    ||
239  | 
    ✓✓ | 21384  | 
    		if (horiz) { | 
    
240  | 
    234  | 
    tbl_hrule(tp, sp, 0);  | 
    |
241  | 
    234  | 
    term_flushln(tp);  | 
    |
242  | 
    234  | 
    		} else { | 
    |
243  | 
    21150  | 
    cp = sp->layout->first;  | 
    |
244  | 
    ✓✓ | 56790  | 
    cpn = sp->next == NULL ? NULL :  | 
    
245  | 
    14490  | 
    sp->next->layout->first;  | 
    |
246  | 
    ✓✓ | 56925  | 
    cpp = sp->prev == NULL ? NULL :  | 
    
247  | 
    14625  | 
    sp->prev->layout->first;  | 
    |
248  | 
    21150  | 
    dp = sp->first;  | 
    |
249  | 
    spans = 0;  | 
    ||
250  | 
    ✓✓ | 153486  | 
    			for (ic = 0; ic < sp->opts->cols; ic++) { | 
    
251  | 
    |||
252  | 
    /*  | 
    ||
253  | 
    * Figure out whether to print a  | 
    ||
254  | 
    * vertical line after this cell  | 
    ||
255  | 
    * and advance to next layout cell.  | 
    ||
256  | 
    */  | 
    ||
257  | 
    |||
258  | 
    ✓✗ | 55593  | 
    				if (cp != NULL) { | 
    
259  | 
    55593  | 
    vert = cp->vert;  | 
    |
260  | 
    ✓✗✓ | 55593  | 
    					switch (cp->pos) { | 
    
261  | 
    case TBL_CELL_HORIZ:  | 
    ||
262  | 
    fc = '-';  | 
    ||
263  | 
    5535  | 
    break;  | 
    |
264  | 
    case TBL_CELL_DHORIZ:  | 
    ||
265  | 
    fc = '=';  | 
    ||
266  | 
    break;  | 
    ||
267  | 
    default:  | 
    ||
268  | 
    fc = ' ';  | 
    ||
269  | 
    50058  | 
    break;  | 
    |
270  | 
    }  | 
    ||
271  | 
    				} else { | 
    ||
272  | 
    vert = 0;  | 
    ||
273  | 
    fc = ' ';  | 
    ||
274  | 
    }  | 
    ||
275  | 
    ✓✓ | 55593  | 
    				if (cpp != NULL) { | 
    
276  | 
    ✓✓✓✗ | 
    40221  | 
    if (vert == 0 &&  | 
    
277  | 
    cp != NULL &&  | 
    ||
278  | 
    ✓✓✗✓ | 
    65844  | 
    ((IS_HORIZ(cp) &&  | 
    
279  | 
    ✓✗✗✓ | 
    6444  | 
    !IS_HORIZ(cpp)) ||  | 
    
280  | 
    ✓✓ | 31311  | 
    (cp->next != NULL &&  | 
    
281  | 
    ✓✗ | 18333  | 
    cpp->next != NULL &&  | 
    
282  | 
    ✓✓✗✓ | 
    34641  | 
    IS_HORIZ(cp->next) &&  | 
    
283  | 
    ✓✗ | 4050  | 
    !IS_HORIZ(cpp->next))))  | 
    
284  | 
    5247  | 
    vert = cpp->vert;  | 
    |
285  | 
    38196  | 
    cpp = cpp->next;  | 
    |
286  | 
    38196  | 
    }  | 
    |
287  | 
    ✓✓✓✓ | 
    105525  | 
    if (vert == 0 &&  | 
    
288  | 
    49932  | 
    sp->opts->opts & TBL_OPT_ALLBOX)  | 
    |
289  | 
    17991  | 
    vert = 1;  | 
    |
290  | 
    ✓✓ | 55593  | 
    				if (cpn != NULL) { | 
    
291  | 
    ✓✓ | 37800  | 
    if (vert == 0)  | 
    
292  | 
    21258  | 
    vert = cpn->vert;  | 
    |
293  | 
    37800  | 
    cpn = cpn->next;  | 
    |
294  | 
    37800  | 
    }  | 
    |
295  | 
    ✓✗ | 55593  | 
    if (cp != NULL)  | 
    
296  | 
    55593  | 
    cp = cp->next;  | 
    |
297  | 
    |||
298  | 
    /*  | 
    ||
299  | 
    * Skip later cells in a span,  | 
    ||
300  | 
    * figure out whether to start a span,  | 
    ||
301  | 
    * and advance to next data cell.  | 
    ||
302  | 
    */  | 
    ||
303  | 
    |||
304  | 
    ✓✓ | 55593  | 
    				if (spans) { | 
    
305  | 
    135  | 
    spans--;  | 
    |
306  | 
    135  | 
    continue;  | 
    |
307  | 
    }  | 
    ||
308  | 
    ✓✓ | 55458  | 
    				if (dp != NULL) { | 
    
309  | 
    54000  | 
    spans = dp->spans;  | 
    |
310  | 
    ✓✓✓✗ | 
    96246  | 
    if (ic || sp->layout->first->pos  | 
    
311  | 
    21123  | 
    != TBL_CELL_SPAN)  | 
    |
312  | 
    54000  | 
    dp = dp->next;  | 
    |
313  | 
    }  | 
    ||
314  | 
    |||
315  | 
    /*  | 
    ||
316  | 
    * Print one line of text in the cell  | 
    ||
317  | 
    * and remember whether there is more.  | 
    ||
318  | 
    */  | 
    ||
319  | 
    |||
320  | 
    55458  | 
    tp->tcol++;  | 
    |
321  | 
    ✓✓ | 55458  | 
    if (tp->tcol->col < tp->tcol->lastcol)  | 
    
322  | 
    55089  | 
    term_flushln(tp);  | 
    |
323  | 
    ✓✓ | 55458  | 
    if (tp->tcol->col < tp->tcol->lastcol)  | 
    
324  | 
    135  | 
    more = 1;  | 
    |
325  | 
    |||
326  | 
    /*  | 
    ||
327  | 
    * Vertical frames between data cells,  | 
    ||
328  | 
    * but not after the last column.  | 
    ||
329  | 
    */  | 
    ||
330  | 
    |||
331  | 
    ✓✓✓✓ ✓✓  | 
    128700  | 
    if (fc == ' ' && ((vert == 0 &&  | 
    
332  | 
    ✓✓✓✓ ✗✓  | 
    59094  | 
    (cp == NULL || !IS_HORIZ(cp))) ||  | 
    
333  | 
    23319  | 
    tp->tcol + 1 == tp->tcols + tp->lasttcol))  | 
    |
334  | 
    continue;  | 
    ||
335  | 
    |||
336  | 
    ✓✓ | 20808  | 
    				if (tp->viscol < tp->tcol->rmargin) { | 
    
337  | 
    828  | 
    (*tp->advance)(tp, tp->tcol->rmargin  | 
    |
338  | 
    414  | 
    - tp->viscol);  | 
    |
339  | 
    414  | 
    tp->viscol = tp->tcol->rmargin;  | 
    |
340  | 
    414  | 
    }  | 
    |
341  | 
    ✓✓ | 104391  | 
    while (tp->viscol < tp->tcol->rmargin +  | 
    
342  | 
    41733  | 
    				    tp->tbl.cols[ic].spacing / 2) { | 
    |
343  | 
    20925  | 
    (*tp->letter)(tp, fc);  | 
    |
344  | 
    20925  | 
    tp->viscol++;  | 
    |
345  | 
    }  | 
    ||
346  | 
    |||
347  | 
    ✓✓ | 20808  | 
    if (tp->tcol + 1 == tp->tcols + tp->lasttcol)  | 
    
348  | 
    continue;  | 
    ||
349  | 
    |||
350  | 
    ✓✓ | 19395  | 
    				if (fc == ' ' && cp != NULL) { | 
    
351  | 
    ✓✗✓ | 18603  | 
    					switch (cp->pos) { | 
    
352  | 
    case TBL_CELL_HORIZ:  | 
    ||
353  | 
    fc = '-';  | 
    ||
354  | 
    3330  | 
    break;  | 
    |
355  | 
    case TBL_CELL_DHORIZ:  | 
    ||
356  | 
    fc = '=';  | 
    ||
357  | 
    break;  | 
    ||
358  | 
    default:  | 
    ||
359  | 
    break;  | 
    ||
360  | 
    }  | 
    ||
361  | 
    }  | 
    ||
362  | 
    ✓✗ | 19395  | 
    				if (tp->tbl.cols[ic].spacing) { | 
    
363  | 
    ✓✓ | 38790  | 
    (*tp->letter)(tp, fc == ' ' ? '|' :  | 
    
364  | 
    7452  | 
    vert ? '+' : fc);  | 
    |
365  | 
    19395  | 
    tp->viscol++;  | 
    |
366  | 
    19395  | 
    }  | 
    |
367  | 
    |||
368  | 
    ✓✓ | 19395  | 
    				if (fc != ' ') { | 
    
369  | 
    ✓✗✓✓ | 
    14904  | 
    if (cp != NULL &&  | 
    
370  | 
    7452  | 
    cp->pos == TBL_CELL_HORIZ)  | 
    |
371  | 
    4113  | 
    fc = '-';  | 
    |
372  | 
    ✓✗✗✓ | 
    6678  | 
    else if (cp != NULL &&  | 
    
373  | 
    3339  | 
    cp->pos == TBL_CELL_DHORIZ)  | 
    |
374  | 
    fc = '=';  | 
    ||
375  | 
    else  | 
    ||
376  | 
    fc = ' ';  | 
    ||
377  | 
    }  | 
    ||
378  | 
    ✓✓✓✓ | 
    35757  | 
    if (tp->tbl.cols[ic].spacing > 2 &&  | 
    
379  | 
    ✓✓ | 35703  | 
    				    (vert > 1 || fc != ' ')) { | 
    
380  | 
    ✓✓ | 12600  | 
    (*tp->letter)(tp, fc == ' ' ? '|' :  | 
    
381  | 
    4113  | 
    vert > 1 ? '+' : fc);  | 
    |
382  | 
    6300  | 
    tp->viscol++;  | 
    |
383  | 
    6300  | 
    }  | 
    |
384  | 
    }  | 
    ||
385  | 
    }  | 
    ||
386  | 
    |||
387  | 
    /* Print the vertical frame at the end of each row. */  | 
    ||
388  | 
    |||
389  | 
    fc = '\0';  | 
    ||
390  | 
    ✓✓✓✓ | 
    40698  | 
    if ((sp->layout->last->vert &&  | 
    
391  | 
    ✗✓ | 1161  | 
    sp->layout->last->col + 1 == sp->opts->cols) ||  | 
    
392  | 
    ✓✓ | 20223  | 
    (sp->next != NULL &&  | 
    
393  | 
    ✓✓ | 13788  | 
    sp->next->layout->last->vert &&  | 
    
394  | 
    ✗✓ | 720  | 
    sp->next->layout->last->col + 1 == sp->opts->cols) ||  | 
    
395  | 
    ✓✓ | 19503  | 
    (sp->prev != NULL &&  | 
    
396  | 
    ✓✓ | 13248  | 
    sp->prev->layout->last->vert &&  | 
    
397  | 
    ✓✗ | 729  | 
    sp->prev->layout->last->col + 1 == sp->opts->cols &&  | 
    
398  | 
    ✓✓✓✓ ✗✓  | 
    1989  | 
    (horiz || (IS_HORIZ(sp->layout->last) &&  | 
    
399  | 
    ✓✗✗✓ | 
    360  | 
    !IS_HORIZ(sp->prev->layout->last)))) ||  | 
    
400  | 
    19314  | 
    (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)))  | 
    |
401  | 
    ✓✓✓✓ | 
    63846  | 
    fc = horiz || IS_HORIZ(sp->layout->last) ? '+' : '|';  | 
    
402  | 
    ✓✓✓✓ | 
    5049  | 
    else if (horiz && sp->opts->rvert)  | 
    
403  | 
    9  | 
    fc = '-';  | 
    |
404  | 
    ✓✓ | 21384  | 
    		if (fc != '\0') { | 
    
405  | 
    ✓✓✓✓ ✓✓✗✓  | 
    64899  | 
    if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||  | 
    
406  | 
    1242  | 
    			    sp->layout->last->col + 1 < sp->opts->cols)) { | 
    |
407  | 
    14931  | 
    tp->tcol++;  | 
    |
408  | 
    29862  | 
    (*tp->advance)(tp,  | 
    |
409  | 
    ✓✓ | 44775  | 
    tp->tcol->offset > tp->viscol ?  | 
    
410  | 
    14913  | 
    tp->tcol->offset - tp->viscol : 1);  | 
    |
411  | 
    14931  | 
    }  | 
    |
412  | 
    16380  | 
    (*tp->letter)(tp, fc);  | 
    |
413  | 
    16380  | 
    }  | 
    |
414  | 
    21384  | 
    (*tp->endline)(tp);  | 
    |
415  | 
    21384  | 
    tp->viscol = 0;  | 
    |
416  | 
    ✓✓ | 21384  | 
    } while (more);  | 
    
417  | 
    |||
418  | 
    /*  | 
    ||
419  | 
    * Clean up after this row. If it is the last line  | 
    ||
420  | 
    * of the table, print the box line and clean up  | 
    ||
421  | 
    * column data; otherwise, print the allbox line.  | 
    ||
422  | 
    */  | 
    ||
423  | 
    |||
424  | 
    21249  | 
    term_setcol(tp, 1);  | 
    |
425  | 
    21249  | 
    tp->flags &= ~TERMP_MULTICOL;  | 
    |
426  | 
    21249  | 
    tp->tcol->rmargin = tp->maxrmargin;  | 
    |
427  | 
    ✓✓ | 21249  | 
    	if (sp->next == NULL) { | 
    
428  | 
    ✓✓ | 6588  | 
    		if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) { | 
    
429  | 
    4698  | 
    tbl_hrule(tp, sp, 2);  | 
    |
430  | 
    4698  | 
    tp->skipvsp = 1;  | 
    |
431  | 
    4698  | 
    }  | 
    |
432  | 
    ✓✓ | 6588  | 
    		if (sp->opts->opts & TBL_OPT_DBOX) { | 
    
433  | 
    2196  | 
    tbl_hrule(tp, sp, 3);  | 
    |
434  | 
    2196  | 
    tp->skipvsp = 2;  | 
    |
435  | 
    2196  | 
    }  | 
    |
436  | 
    ✗✓ | 6588  | 
    assert(tp->tbl.cols);  | 
    
437  | 
    6588  | 
    free(tp->tbl.cols);  | 
    |
438  | 
    6588  | 
    tp->tbl.cols = NULL;  | 
    |
439  | 
    6588  | 
    tp->tcol->offset = offset;  | 
    |
440  | 
    ✓✓✓✓ ✓✓  | 
    35730  | 
    } else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX &&  | 
    
441  | 
    ✓✗✓✓ | 
    10746  | 
    (sp->next == NULL || sp->next->pos == TBL_SPAN_DATA ||  | 
    
442  | 
    18  | 
    sp->next->next != NULL))  | 
    |
443  | 
    5364  | 
    tbl_hrule(tp, sp, 1);  | 
    |
444  | 
    |||
445  | 
    21249  | 
    tp->flags &= ~TERMP_NONOSPACE;  | 
    |
446  | 
    21249  | 
    }  | 
    |
447  | 
    |||
448  | 
    /*  | 
    ||
449  | 
    * Kinds of horizontal rulers:  | 
    ||
450  | 
    * 0: inside the table (single or double line with crossings)  | 
    ||
451  | 
    * 1: inside the table (single or double line with crossings and ends)  | 
    ||
452  | 
    * 2: inner frame (single line with crossings and ends)  | 
    ||
453  | 
    * 3: outer frame (single line without crossings with ends)  | 
    ||
454  | 
    */  | 
    ||
455  | 
    static void  | 
    ||
456  | 
    tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind)  | 
    ||
457  | 
    { | 
    ||
458  | 
    const struct tbl_cell *cp, *cpn, *cpp;  | 
    ||
459  | 
    const struct roffcol *col;  | 
    ||
460  | 
    int vert;  | 
    ||
461  | 
    char line, cross;  | 
    ||
462  | 
    |||
463  | 
    ✓✓ | 63756  | 
    line = (kind < 2 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-';  | 
    
464  | 
    19386  | 
    cross = (kind < 3) ? '+' : '-';  | 
    |
465  | 
    |||
466  | 
    ✓✓ | 19386  | 
    if (kind)  | 
    
467  | 
    19152  | 
    term_word(tp, "+");  | 
    |
468  | 
    19386  | 
    cp = sp->layout->first;  | 
    |
469  | 
    ✓✓✓✓ | 
    39150  | 
    cpp = kind || sp->prev == NULL ? NULL : sp->prev->layout->first;  | 
    
470  | 
    19386  | 
    if (cpp == cp)  | 
    |
471  | 
    cpp = NULL;  | 
    ||
472  | 
    ✓✓✓✓ | 
    49932  | 
    cpn = kind > 1 || sp->next == NULL ? NULL : sp->next->layout->first;  | 
    
473  | 
    19386  | 
    if (cpn == cp)  | 
    |
474  | 
    cpn = NULL;  | 
    ||
475  | 
    19386  | 
    	for (;;) { | 
    |
476  | 
    49041  | 
    col = tp->tbl.cols + cp->col;  | 
    |
477  | 
    49041  | 
    tbl_char(tp, line, col->width + col->spacing / 2);  | 
    |
478  | 
    49041  | 
    vert = cp->vert;  | 
    |
479  | 
    ✓✓ | 49041  | 
    if ((cp = cp->next) == NULL)  | 
    
480  | 
    break;  | 
    ||
481  | 
    ✓✓ | 29655  | 
    		if (cpp != NULL) { | 
    
482  | 
    ✓✓ | 36  | 
    if (vert < cpp->vert)  | 
    
483  | 
    18  | 
    vert = cpp->vert;  | 
    |
484  | 
    36  | 
    cpp = cpp->next;  | 
    |
485  | 
    36  | 
    }  | 
    |
486  | 
    ✓✓ | 29655  | 
    		if (cpn != NULL) { | 
    
487  | 
    ✓✓ | 8064  | 
    if (vert < cpn->vert)  | 
    
488  | 
    1044  | 
    vert = cpn->vert;  | 
    |
489  | 
    8064  | 
    cpn = cpn->next;  | 
    |
490  | 
    8064  | 
    }  | 
    |
491  | 
    ✓✓ | 29655  | 
    if (sp->opts->opts & TBL_OPT_ALLBOX && !vert)  | 
    
492  | 
    16173  | 
    vert = 1;  | 
    |
493  | 
    ✓✓ | 29655  | 
    if (col->spacing)  | 
    
494  | 
    29637  | 
    tbl_char(tp, vert ? cross : line, 1);  | 
    |
495  | 
    ✓✓ | 29655  | 
    if (col->spacing > 2)  | 
    
496  | 
    29601  | 
    tbl_char(tp, vert > 1 ? cross : line, 1);  | 
    |
497  | 
    ✓✓ | 29655  | 
    if (col->spacing > 4)  | 
    
498  | 
    54  | 
    tbl_char(tp, line, (col->spacing - 3) / 2);  | 
    |
499  | 
    }  | 
    ||
500  | 
    ✓✓ | 19386  | 
    	if (kind) { | 
    
501  | 
    19152  | 
    term_word(tp, "+");  | 
    |
502  | 
    19152  | 
    term_flushln(tp);  | 
    |
503  | 
    19152  | 
    }  | 
    |
504  | 
    19386  | 
    }  | 
    |
505  | 
    |||
506  | 
    static void  | 
    ||
507  | 
    tbl_data(struct termp *tp, const struct tbl_opts *opts,  | 
    ||
508  | 
    const struct tbl_cell *cp, const struct tbl_dat *dp,  | 
    ||
509  | 
    const struct roffcol *col)  | 
    ||
510  | 
    { | 
    ||
511  | 
    ✓✗✓ | 110016  | 
    	switch (cp->pos) { | 
    
512  | 
    case TBL_CELL_HORIZ:  | 
    ||
513  | 
    5535  | 
    tbl_char(tp, '-', col->width);  | 
    |
514  | 
    5535  | 
    return;  | 
    |
515  | 
    case TBL_CELL_DHORIZ:  | 
    ||
516  | 
    tbl_char(tp, '=', col->width);  | 
    ||
517  | 
    return;  | 
    ||
518  | 
    default:  | 
    ||
519  | 
    break;  | 
    ||
520  | 
    }  | 
    ||
521  | 
    |||
522  | 
    ✓✓ | 49473  | 
    if (dp == NULL)  | 
    
523  | 
    return;  | 
    ||
524  | 
    |||
525  | 
    ✗✗✗✗ ✓✗  | 
    49446  | 
    	switch (dp->pos) { | 
    
526  | 
    case TBL_DATA_NONE:  | 
    ||
527  | 
    return;  | 
    ||
528  | 
    case TBL_DATA_HORIZ:  | 
    ||
529  | 
    case TBL_DATA_NHORIZ:  | 
    ||
530  | 
    tbl_char(tp, '-', col->width);  | 
    ||
531  | 
    return;  | 
    ||
532  | 
    case TBL_DATA_NDHORIZ:  | 
    ||
533  | 
    case TBL_DATA_DHORIZ:  | 
    ||
534  | 
    tbl_char(tp, '=', col->width);  | 
    ||
535  | 
    return;  | 
    ||
536  | 
    default:  | 
    ||
537  | 
    break;  | 
    ||
538  | 
    }  | 
    ||
539  | 
    |||
540  | 
    ✗✗✗✓ ✓✗✗✓  | 
    98865  | 
    	switch (cp->pos) { | 
    
541  | 
    case TBL_CELL_LONG:  | 
    ||
542  | 
    case TBL_CELL_CENTRE:  | 
    ||
543  | 
    case TBL_CELL_LEFT:  | 
    ||
544  | 
    case TBL_CELL_RIGHT:  | 
    ||
545  | 
    49302  | 
    tbl_literal(tp, dp, col);  | 
    |
546  | 
    49302  | 
    break;  | 
    |
547  | 
    case TBL_CELL_NUMBER:  | 
    ||
548  | 
    117  | 
    tbl_number(tp, opts, dp, col);  | 
    |
549  | 
    117  | 
    break;  | 
    |
550  | 
    case TBL_CELL_DOWN:  | 
    ||
551  | 
    case TBL_CELL_SPAN:  | 
    ||
552  | 
    break;  | 
    ||
553  | 
    default:  | 
    ||
554  | 
    abort();  | 
    ||
555  | 
    }  | 
    ||
556  | 
    55008  | 
    }  | 
    |
557  | 
    |||
558  | 
    static void  | 
    ||
559  | 
    tbl_char(struct termp *tp, char c, size_t len)  | 
    ||
560  | 
    { | 
    ||
561  | 
    size_t i, sz;  | 
    ||
562  | 
    425088  | 
    char cp[2];  | 
    |
563  | 
    |||
564  | 
    212544  | 
    cp[0] = c;  | 
    |
565  | 
    212544  | 
    cp[1] = '\0';  | 
    |
566  | 
    |||
567  | 
    212544  | 
    sz = term_strlen(tp, cp);  | 
    |
568  | 
    |||
569  | 
    ✓✓ | 895572  | 
    for (i = 0; i < len; i += sz)  | 
    
570  | 
    235242  | 
    term_word(tp, cp);  | 
    |
571  | 
    212544  | 
    }  | 
    |
572  | 
    |||
573  | 
    static void  | 
    ||
574  | 
    tbl_literal(struct termp *tp, const struct tbl_dat *dp,  | 
    ||
575  | 
    const struct roffcol *col)  | 
    ||
576  | 
    { | 
    ||
577  | 
    size_t len, padl, padr, width;  | 
    ||
578  | 
    int ic, spans;  | 
    ||
579  | 
    |||
580  | 
    ✗✓ | 98604  | 
    assert(dp->string);  | 
    
581  | 
    49302  | 
    len = term_strlen(tp, dp->string);  | 
    |
582  | 
    49302  | 
    width = col->width;  | 
    |
583  | 
    49302  | 
    ic = dp->layout->col;  | 
    |
584  | 
    49302  | 
    spans = dp->spans;  | 
    |
585  | 
    ✓✓ | 98874  | 
    while (spans--)  | 
    
586  | 
    135  | 
    width += tp->tbl.cols[++ic].width + 3;  | 
    |
587  | 
    |||
588  | 
    ✓✓ | 102582  | 
    padr = width > len ? width - len : 0;  | 
    
589  | 
    padl = 0;  | 
    ||
590  | 
    |||
591  | 
    ✗✓✓✓ | 
    50103  | 
    	switch (dp->layout->pos) { | 
    
592  | 
    case TBL_CELL_LONG:  | 
    ||
593  | 
    padl = term_len(tp, 1);  | 
    ||
594  | 
    padr = padr > padl ? padr - padl : 0;  | 
    ||
595  | 
    break;  | 
    ||
596  | 
    case TBL_CELL_CENTRE:  | 
    ||
597  | 
    ✓✓ | 288  | 
    if (2 > padr)  | 
    
598  | 
    break;  | 
    ||
599  | 
    144  | 
    padl = padr / 2;  | 
    |
600  | 
    144  | 
    padr -= padl;  | 
    |
601  | 
    144  | 
    break;  | 
    |
602  | 
    case TBL_CELL_RIGHT:  | 
    ||
603  | 
    padl = padr;  | 
    ||
604  | 
    padr = 0;  | 
    ||
605  | 
    513  | 
    break;  | 
    |
606  | 
    default:  | 
    ||
607  | 
    break;  | 
    ||
608  | 
    }  | 
    ||
609  | 
    |||
610  | 
    49302  | 
    tbl_char(tp, ASCII_NBRSP, padl);  | 
    |
611  | 
    49302  | 
    tbl_word(tp, dp);  | 
    |
612  | 
    49302  | 
    tbl_char(tp, ASCII_NBRSP, padr);  | 
    |
613  | 
    49302  | 
    }  | 
    |
614  | 
    |||
615  | 
    static void  | 
    ||
616  | 
    tbl_number(struct termp *tp, const struct tbl_opts *opts,  | 
    ||
617  | 
    const struct tbl_dat *dp,  | 
    ||
618  | 
    const struct roffcol *col)  | 
    ||
619  | 
    { | 
    ||
620  | 
    char *cp;  | 
    ||
621  | 
    234  | 
    char buf[2];  | 
    |
622  | 
    size_t sz, psz, ssz, d, padl;  | 
    ||
623  | 
    int i;  | 
    ||
624  | 
    |||
625  | 
    /*  | 
    ||
626  | 
    * See calc_data_number(). Left-pad by taking the offset of our  | 
    ||
627  | 
    * and the maximum decimal; right-pad by the remaining amount.  | 
    ||
628  | 
    */  | 
    ||
629  | 
    |||
630  | 
    ✗✓ | 117  | 
    assert(dp->string);  | 
    
631  | 
    |||
632  | 
    117  | 
    sz = term_strlen(tp, dp->string);  | 
    |
633  | 
    |||
634  | 
    117  | 
    buf[0] = opts->decimal;  | 
    |
635  | 
    117  | 
    buf[1] = '\0';  | 
    |
636  | 
    |||
637  | 
    117  | 
    psz = term_strlen(tp, buf);  | 
    |
638  | 
    |||
639  | 
    ✓✗ | 117  | 
    	if ((cp = strrchr(dp->string, opts->decimal)) != NULL) { | 
    
640  | 
    ✓✓ | 684  | 
    		for (ssz = 0, i = 0; cp != &dp->string[i]; i++) { | 
    
641  | 
    225  | 
    buf[0] = dp->string[i];  | 
    |
642  | 
    225  | 
    ssz += term_strlen(tp, buf);  | 
    |
643  | 
    }  | 
    ||
644  | 
    117  | 
    d = ssz + psz;  | 
    |
645  | 
    117  | 
    } else  | 
    |
646  | 
    d = sz + psz;  | 
    ||
647  | 
    |||
648  | 
    ✓✓✓✓ | 
    171  | 
    	if (col->decimal > d && col->width > sz) { | 
    
649  | 
    36  | 
    padl = col->decimal - d;  | 
    |
650  | 
    ✗✓ | 36  | 
    if (padl + sz > col->width)  | 
    
651  | 
    padl = col->width - sz;  | 
    ||
652  | 
    36  | 
    tbl_char(tp, ASCII_NBRSP, padl);  | 
    |
653  | 
    36  | 
    } else  | 
    |
654  | 
    padl = 0;  | 
    ||
655  | 
    117  | 
    tbl_word(tp, dp);  | 
    |
656  | 
    ✓✓ | 117  | 
    if (col->width > sz + padl)  | 
    
657  | 
    36  | 
    tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);  | 
    |
658  | 
    117  | 
    }  | 
    |
659  | 
    |||
660  | 
    static void  | 
    ||
661  | 
    tbl_word(struct termp *tp, const struct tbl_dat *dp)  | 
    ||
662  | 
    { | 
    ||
663  | 
    int prev_font;  | 
    ||
664  | 
    |||
665  | 
    98838  | 
    prev_font = tp->fonti;  | 
    |
666  | 
    ✓✓ | 49419  | 
    if (dp->layout->flags & TBL_CELL_BOLD)  | 
    
667  | 
    81  | 
    term_fontpush(tp, TERMFONT_BOLD);  | 
    |
668  | 
    ✓✓ | 49338  | 
    else if (dp->layout->flags & TBL_CELL_ITALIC)  | 
    
669  | 
    54  | 
    term_fontpush(tp, TERMFONT_UNDER);  | 
    |
670  | 
    |||
671  | 
    49419  | 
    term_word(tp, dp->string);  | 
    |
672  | 
    |||
673  | 
    49419  | 
    term_fontpopq(tp, prev_font);  | 
    |
674  | 
    49419  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |