1 /* $Vendor-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 426