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