Merge branch 'vendor/FILE'
[dragonfly.git] / contrib / mdocml / tbl_term.c
1 /*      $Id: tbl_term.c,v 1.19 2011/01/25 12:07:30 schwarze Exp $ */
2 /*
3  * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@kth.se>
4  * Copyright (c) 2011 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 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <assert.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "mandoc.h"
28 #include "out.h"
29 #include "term.h"
30
31 static  size_t  term_tbl_len(size_t, void *);
32 static  size_t  term_tbl_strlen(const char *, void *);
33 static  void    tbl_char(struct termp *, char, size_t);
34 static  void    tbl_data(struct termp *, const struct tbl *,
35                         const struct tbl_dat *, 
36                         const struct roffcol *);
37 static  void    tbl_hframe(struct termp *, const struct tbl_span *);
38 static  void    tbl_literal(struct termp *, const struct tbl_dat *, 
39                         const struct roffcol *);
40 static  void    tbl_number(struct termp *, const struct tbl *, 
41                         const struct tbl_dat *, 
42                         const struct roffcol *);
43 static  void    tbl_hrule(struct termp *, const struct tbl_span *);
44 static  void    tbl_vframe(struct termp *, const struct tbl *);
45 static  void    tbl_vrule(struct termp *, const struct tbl_head *);
46
47
48 static size_t
49 term_tbl_strlen(const char *p, void *arg)
50 {
51
52         return(term_strlen((const struct termp *)arg, p));
53 }
54
55 static size_t
56 term_tbl_len(size_t sz, void *arg)
57 {
58
59         return(term_len((const struct termp *)arg, sz));
60 }
61
62 void
63 term_tbl(struct termp *tp, const struct tbl_span *sp)
64 {
65         const struct tbl_head   *hp;
66         const struct tbl_dat    *dp;
67         struct roffcol          *col;
68         int                      spans;
69         size_t                   rmargin, maxrmargin;
70
71         rmargin = tp->rmargin;
72         maxrmargin = tp->maxrmargin;
73
74         tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
75
76         /* Inhibit printing of spaces: we do padding ourselves. */
77
78         tp->flags |= TERMP_NONOSPACE;
79         tp->flags |= TERMP_NOSPACE;
80
81         /*
82          * The first time we're invoked for a given table block,
83          * calculate the table widths and decimal positions.
84          */
85
86         if (TBL_SPAN_FIRST & sp->flags) {
87                 term_flushln(tp);
88
89                 tp->tbl.len = term_tbl_len;
90                 tp->tbl.slen = term_tbl_strlen;
91                 tp->tbl.arg = tp;
92
93                 tblcalc(&tp->tbl, sp);
94         }
95
96         /* Horizontal frame at the start of boxed tables. */
97
98         if (TBL_SPAN_FIRST & sp->flags)
99                 tbl_hframe(tp, sp);
100
101         /* Vertical frame at the start of each row. */
102
103         tbl_vframe(tp, sp->tbl);
104
105         /*
106          * Now print the actual data itself depending on the span type.
107          * Spanner spans get a horizontal rule; data spanners have their
108          * data printed by matching data to header.
109          */
110
111         switch (sp->pos) {
112         case (TBL_SPAN_HORIZ):
113                 /* FALLTHROUGH */
114         case (TBL_SPAN_DHORIZ):
115                 tbl_hrule(tp, sp);
116                 break;
117         case (TBL_SPAN_DATA):
118                 /* Iterate over template headers. */
119                 dp = sp->first;
120                 spans = 0;
121                 for (hp = sp->head; hp; hp = hp->next) {
122                         /* 
123                          * If the current data header is invoked during
124                          * a spanner ("spans" > 0), don't emit anything
125                          * at all.
126                          */
127                         switch (hp->pos) {
128                         case (TBL_HEAD_VERT):
129                                 /* FALLTHROUGH */
130                         case (TBL_HEAD_DVERT):
131                                 if (spans <= 0)
132                                         tbl_vrule(tp, hp);
133                                 continue;
134                         case (TBL_HEAD_DATA):
135                                 break;
136                         }
137
138                         if (--spans >= 0)
139                                 continue;
140
141                         col = &tp->tbl.cols[hp->ident];
142                         tbl_data(tp, sp->tbl, dp, col);
143
144                         /* 
145                          * Go to the next data cell and assign the
146                          * number of subsequent spans, if applicable.
147                          */
148
149                         if (dp) {
150                                 spans = dp->spans;
151                                 dp = dp->next;
152                         }
153                 }
154                 break;
155         }
156
157         tbl_vframe(tp, sp->tbl);
158         term_flushln(tp);
159
160         /*
161          * If we're the last row, clean up after ourselves: clear the
162          * existing table configuration and set it to NULL.
163          */
164
165         if (TBL_SPAN_LAST & sp->flags) {
166                 tbl_hframe(tp, sp);
167                 assert(tp->tbl.cols);
168                 free(tp->tbl.cols);
169                 tp->tbl.cols = NULL;
170         }
171
172         tp->flags &= ~TERMP_NONOSPACE;
173         tp->rmargin = rmargin;
174         tp->maxrmargin = maxrmargin;
175
176 }
177
178 static void
179 tbl_hrule(struct termp *tp, const struct tbl_span *sp)
180 {
181         const struct tbl_head *hp;
182         char             c;
183         size_t           width;
184
185         /*
186          * An hrule extends across the entire table and is demarked by a
187          * standalone `_' or whatnot in lieu of a table row.  Spanning
188          * headers are marked by a `+', as are table boundaries.
189          */
190
191         c = '-';
192         if (TBL_SPAN_DHORIZ == sp->pos)
193                 c = '=';
194
195         /* FIXME: don't use `+' between data and a spanner! */
196
197         for (hp = sp->head; hp; hp = hp->next) {
198                 width = tp->tbl.cols[hp->ident].width;
199                 switch (hp->pos) {
200                 case (TBL_HEAD_DATA):
201                         if (hp->next)
202                                 width += 2;
203                         tbl_char(tp, c, width);
204                         break;
205                 case (TBL_HEAD_DVERT):
206                         tbl_char(tp, '+', width);
207                         /* FALLTHROUGH */
208                 case (TBL_HEAD_VERT):
209                         tbl_char(tp, '+', width);
210                         break;
211                 default:
212                         abort();
213                         /* NOTREACHED */
214                 }
215         }
216 }
217
218 static void
219 tbl_hframe(struct termp *tp, const struct tbl_span *sp)
220 {
221         const struct tbl_head *hp;
222         size_t           width;
223
224         if ( ! (TBL_OPT_BOX & sp->tbl->opts || 
225                         TBL_OPT_DBOX & sp->tbl->opts))
226                 return;
227
228         /* 
229          * Print out the horizontal part of a frame or double frame.  A
230          * double frame has an unbroken `-' outer line the width of the
231          * table, bordered by `+'.  The frame (or inner frame, in the
232          * case of the double frame) is a `-' bordered by `+' and broken
233          * by `+' whenever a span is encountered.
234          */
235
236         if (TBL_OPT_DBOX & sp->tbl->opts) {
237                 term_word(tp, "+");
238                 for (hp = sp->head; hp; hp = hp->next) {
239                         width = tp->tbl.cols[hp->ident].width;
240                         tbl_char(tp, '-', width);
241                 }
242                 term_word(tp, "+");
243                 term_flushln(tp);
244         }
245
246         term_word(tp, "+");
247         for (hp = sp->head; hp; hp = hp->next) {
248                 width = tp->tbl.cols[hp->ident].width;
249                 switch (hp->pos) {
250                 case (TBL_HEAD_DATA):
251                         tbl_char(tp, '-', width);
252                         break;
253                 default:
254                         tbl_char(tp, '+', width);
255                         break;
256                 }
257         }
258         term_word(tp, "+");
259         term_flushln(tp);
260 }
261
262 static void
263 tbl_data(struct termp *tp, const struct tbl *tbl,
264                 const struct tbl_dat *dp, 
265                 const struct roffcol *col)
266 {
267
268         if (NULL == dp) {
269                 tbl_char(tp, ASCII_NBRSP, col->width);
270                 return;
271         }
272         assert(dp->layout);
273
274         switch (dp->pos) {
275         case (TBL_DATA_NONE):
276                 tbl_char(tp, ASCII_NBRSP, col->width);
277                 return;
278         case (TBL_DATA_HORIZ):
279                 /* FALLTHROUGH */
280         case (TBL_DATA_NHORIZ):
281                 tbl_char(tp, '-', col->width);
282                 return;
283         case (TBL_DATA_NDHORIZ):
284                 /* FALLTHROUGH */
285         case (TBL_DATA_DHORIZ):
286                 tbl_char(tp, '=', col->width);
287                 return;
288         default:
289                 break;
290         }
291         
292         switch (dp->layout->pos) {
293         case (TBL_CELL_HORIZ):
294                 tbl_char(tp, '-', col->width);
295                 break;
296         case (TBL_CELL_DHORIZ):
297                 tbl_char(tp, '=', col->width);
298                 break;
299         case (TBL_CELL_LONG):
300                 /* FALLTHROUGH */
301         case (TBL_CELL_CENTRE):
302                 /* FALLTHROUGH */
303         case (TBL_CELL_LEFT):
304                 /* FALLTHROUGH */
305         case (TBL_CELL_RIGHT):
306                 tbl_literal(tp, dp, col);
307                 break;
308         case (TBL_CELL_NUMBER):
309                 tbl_number(tp, tbl, dp, col);
310                 break;
311         case (TBL_CELL_DOWN):
312                 tbl_char(tp, ASCII_NBRSP, col->width);
313                 break;
314         default:
315                 abort();
316                 /* NOTREACHED */
317         }
318 }
319
320 static void
321 tbl_vrule(struct termp *tp, const struct tbl_head *hp)
322 {
323
324         switch (hp->pos) {
325         case (TBL_HEAD_VERT):
326                 term_word(tp, "|");
327                 break;
328         case (TBL_HEAD_DVERT):
329                 term_word(tp, "||");
330                 break;
331         default:
332                 break;
333         }
334 }
335
336 static void
337 tbl_vframe(struct termp *tp, const struct tbl *tbl)
338 {
339
340         if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts)
341                 term_word(tp, "|");
342 }
343
344 static void
345 tbl_char(struct termp *tp, char c, size_t len)
346 {
347         size_t          i, sz;
348         char            cp[2];
349
350         cp[0] = c;
351         cp[1] = '\0';
352
353         sz = term_strlen(tp, cp);
354
355         for (i = 0; i < len; i += sz)
356                 term_word(tp, cp);
357 }
358
359 static void
360 tbl_literal(struct termp *tp, const struct tbl_dat *dp, 
361                 const struct roffcol *col)
362 {
363         size_t           padl, padr, ssz;
364
365         padl = padr = 0;
366
367         assert(dp->string);
368
369         ssz = term_len(tp, 1);
370
371         switch (dp->layout->pos) {
372         case (TBL_CELL_LONG):
373                 padl = ssz;
374                 padr = col->width - term_strlen(tp, dp->string) - ssz;
375                 break;
376         case (TBL_CELL_CENTRE):
377                 padr = col->width - term_strlen(tp, dp->string);
378                 if (3 > padr)
379                         break;
380                 padl = (padr - 1) / 2;
381                 padr -= padl;
382                 break;
383         case (TBL_CELL_RIGHT):
384                 padl = col->width - term_strlen(tp, dp->string);
385                 break;
386         default:
387                 padr = col->width - term_strlen(tp, dp->string);
388                 break;
389         }
390
391         tbl_char(tp, ASCII_NBRSP, padl);
392         term_word(tp, dp->string);
393         tbl_char(tp, ASCII_NBRSP, padr + 2);
394 }
395
396 static void
397 tbl_number(struct termp *tp, const struct tbl *tbl,
398                 const struct tbl_dat *dp,
399                 const struct roffcol *col)
400 {
401         char            *cp;
402         char             buf[2];
403         size_t           sz, psz, ssz, d, padl;
404         int              i;
405
406         /*
407          * See calc_data_number().  Left-pad by taking the offset of our
408          * and the maximum decimal; right-pad by the remaining amount.
409          */
410
411         assert(dp->string);
412
413         sz = term_strlen(tp, dp->string);
414
415         buf[0] = tbl->decimal;
416         buf[1] = '\0';
417
418         psz = term_strlen(tp, buf);
419
420         if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
421                 buf[1] = '\0';
422                 for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
423                         buf[0] = dp->string[i];
424                         ssz += term_strlen(tp, buf);
425                 }
426                 d = ssz + psz;
427         } else
428                 d = sz + psz;
429
430         sz += term_len(tp, 2);
431         d += term_len(tp, 1);
432
433         padl = col->decimal - d;
434
435         tbl_char(tp, ASCII_NBRSP, padl);
436         term_word(tp, dp->string);
437         tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
438 }
439