Print this page
5051 import mdocml-1.12.3
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Approved by: TBD
   1 /*      $Id: tbl_term.c,v 1.21 2011/09/20 23:05:49 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   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  size_t  tbl_rulewidth(struct termp *, const struct tbl_head *);
  38 static  void    tbl_hframe(struct termp *, const struct tbl_span *, int);
  39 static  void    tbl_literal(struct termp *, const struct tbl_dat *, 
  40                         const struct roffcol *);
  41 static  void    tbl_number(struct termp *, const struct tbl *, 
  42                         const struct tbl_dat *, 
  43                         const struct roffcol *);
  44 static  void    tbl_hrule(struct termp *, const struct tbl_span *);
  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 


  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                 if (TBL_OPT_DBOX & sp->tbl->opts)
 100                         tbl_hframe(tp, sp, 1);
 101                 if (TBL_OPT_DBOX & sp->tbl->opts ||
 102                     TBL_OPT_BOX  & sp->tbl->opts)
 103                         tbl_hframe(tp, sp, 0);
 104         }
 105 
 106         /* Vertical frame at the start of each row. */
 107 
 108         if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
 109                 term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
 110                         TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
 111 
 112         /*
 113          * Now print the actual data itself depending on the span type.
 114          * Spanner spans get a horizontal rule; data spanners have their
 115          * data printed by matching data to header.
 116          */
 117 
 118         switch (sp->pos) {
 119         case (TBL_SPAN_HORIZ):
 120                 /* FALLTHROUGH */
 121         case (TBL_SPAN_DHORIZ):
 122                 tbl_hrule(tp, sp);
 123                 break;
 124         case (TBL_SPAN_DATA):
 125                 /* Iterate over template headers. */
 126                 dp = sp->first;
 127                 spans = 0;
 128                 for (hp = sp->head; hp; hp = hp->next) {

 129                         /* 
 130                          * If the current data header is invoked during
 131                          * a spanner ("spans" > 0), don't emit anything
 132                          * at all.
 133                          */
 134                         switch (hp->pos) {
 135                         case (TBL_HEAD_VERT):
 136                                 /* FALLTHROUGH */
 137                         case (TBL_HEAD_DVERT):
 138                                 if (spans <= 0)
 139                                         tbl_vrule(tp, hp);
 140                                 continue;
 141                         case (TBL_HEAD_DATA):
 142                                 break;
 143                         }
 144 
 145                         if (--spans >= 0)
 146                                 continue;
 147 
 148                         /*
 149                          * All cells get a leading blank, except the
 150                          * first one and those after double rulers.
 151                          */
 152 
 153                         if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)
 154                                 tbl_char(tp, ASCII_NBRSP, 1);
 155 
 156                         col = &tp->tbl.cols[hp->ident];
 157                         tbl_data(tp, sp->tbl, dp, col);
 158 
 159                         /* No trailing blanks. */
 160 
 161                         if (NULL == hp->next)
 162                                 break;
 163 
 164                         /*
 165                          * Add another blank between cells,
 166                          * or two when there is no vertical ruler.
 167                          */
 168 
 169                         tbl_char(tp, ASCII_NBRSP,
 170                             TBL_HEAD_VERT  == hp->next->pos ||
 171                             TBL_HEAD_DVERT == hp->next->pos ? 1 : 2);
 172 
 173                         /* 
 174                          * Go to the next data cell and assign the
 175                          * number of subsequent spans, if applicable.
 176                          */
 177 
 178                         if (dp) {
 179                                 spans = dp->spans;
 180                                 dp = dp->next;
 181                         }
 182                 }
 183                 break;
 184         }
 185 
 186         /* Vertical frame at the end of each row. */
 187 
 188         if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
 189                 term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
 190                         TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
 191         term_flushln(tp);
 192 
 193         /*
 194          * If we're the last row, clean up after ourselves: clear the
 195          * existing table configuration and set it to NULL.
 196          */
 197 
 198         if (TBL_SPAN_LAST & sp->flags) {
 199                 if (TBL_OPT_DBOX & sp->tbl->opts ||
 200                     TBL_OPT_BOX  & sp->tbl->opts)
 201                         tbl_hframe(tp, sp, 0);
 202                 if (TBL_OPT_DBOX & sp->tbl->opts)


 203                         tbl_hframe(tp, sp, 1);


 204                 assert(tp->tbl.cols);
 205                 free(tp->tbl.cols);
 206                 tp->tbl.cols = NULL;
 207         }
 208 
 209         tp->flags &= ~TERMP_NONOSPACE;
 210         tp->rmargin = rmargin;
 211         tp->maxrmargin = maxrmargin;
 212 
 213 }
 214 
 215 /*
 216  * Horizontal rules extend across the entire table.
 217  * Calculate the width by iterating over columns.
 218  */
 219 static size_t
 220 tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
 221 {
 222         size_t           width;
 223 
 224         width = tp->tbl.cols[hp->ident].width;
 225         if (TBL_HEAD_DATA == hp->pos) {
 226                 /* Account for leading blanks. */
 227                 if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)



 228                         width++;
 229                 /* Account for trailing blanks. */
 230                 width++;
 231                 if (hp->next &&
 232                     TBL_HEAD_VERT  != hp->next->pos &&
 233                     TBL_HEAD_DVERT != hp->next->pos)
 234                         width++;
 235         }
 236         return(width);
 237 }
 238 
 239 /*
 240  * Rules inside the table can be single or double
 241  * and have crossings with vertical rules marked with pluses.
 242  */
 243 static void
 244 tbl_hrule(struct termp *tp, const struct tbl_span *sp)
 245 {
 246         const struct tbl_head *hp;
 247         char             c;
 248 
 249         c = '-';
 250         if (TBL_SPAN_DHORIZ == sp->pos)
 251                 c = '=';
 252 
 253         for (hp = sp->head; hp; hp = hp->next)
 254                 tbl_char(tp,
 255                     TBL_HEAD_DATA == hp->pos ? c : '+',
 256                     tbl_rulewidth(tp, hp));

 257 }
 258 
 259 /*
 260  * Rules above and below the table are always single
 261  * and have an additional plus at the beginning and end.
 262  * For double frames, this function is called twice,
 263  * and the outer one does not have crossings.
 264  */
 265 static void
 266 tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
 267 {
 268         const struct tbl_head *hp;
 269 
 270         term_word(tp, "+");
 271         for (hp = sp->head; hp; hp = hp->next)
 272                 tbl_char(tp,
 273                     outer || TBL_HEAD_DATA == hp->pos ? '-' : '+',
 274                     tbl_rulewidth(tp, hp));

 275         term_word(tp, "+");
 276         term_flushln(tp);
 277 }
 278 
 279 static void
 280 tbl_data(struct termp *tp, const struct tbl *tbl,
 281                 const struct tbl_dat *dp, 
 282                 const struct roffcol *col)
 283 {
 284 
 285         if (NULL == dp) {
 286                 tbl_char(tp, ASCII_NBRSP, col->width);
 287                 return;
 288         }
 289         assert(dp->layout);
 290 
 291         switch (dp->pos) {
 292         case (TBL_DATA_NONE):
 293                 tbl_char(tp, ASCII_NBRSP, col->width);
 294                 return;
 295         case (TBL_DATA_HORIZ):
 296                 /* FALLTHROUGH */
 297         case (TBL_DATA_NHORIZ):
 298                 tbl_char(tp, '-', col->width);
 299                 return;
 300         case (TBL_DATA_NDHORIZ):


 306                 break;
 307         }
 308         
 309         switch (dp->layout->pos) {
 310         case (TBL_CELL_HORIZ):
 311                 tbl_char(tp, '-', col->width);
 312                 break;
 313         case (TBL_CELL_DHORIZ):
 314                 tbl_char(tp, '=', col->width);
 315                 break;
 316         case (TBL_CELL_LONG):
 317                 /* FALLTHROUGH */
 318         case (TBL_CELL_CENTRE):
 319                 /* FALLTHROUGH */
 320         case (TBL_CELL_LEFT):
 321                 /* FALLTHROUGH */
 322         case (TBL_CELL_RIGHT):
 323                 tbl_literal(tp, dp, col);
 324                 break;
 325         case (TBL_CELL_NUMBER):
 326                 tbl_number(tp, tbl, dp, col);
 327                 break;
 328         case (TBL_CELL_DOWN):
 329                 tbl_char(tp, ASCII_NBRSP, col->width);
 330                 break;
 331         default:
 332                 abort();
 333                 /* NOTREACHED */
 334         }
 335 }
 336 
 337 static void
 338 tbl_vrule(struct termp *tp, const struct tbl_head *hp)
 339 {
 340 
 341         switch (hp->pos) {
 342         case (TBL_HEAD_VERT):
 343                 term_word(tp, "|");
 344                 break;
 345         case (TBL_HEAD_DVERT):
 346                 term_word(tp, "||");
 347                 break;
 348         default:
 349                 break;
 350         }
 351 }
 352 
 353 static void
 354 tbl_char(struct termp *tp, char c, size_t len)
 355 {
 356         size_t          i, sz;
 357         char            cp[2];
 358 
 359         cp[0] = c;
 360         cp[1] = '\0';
 361 
 362         sz = term_strlen(tp, cp);
 363 
 364         for (i = 0; i < len; i += sz)
 365                 term_word(tp, cp);
 366 }
 367 
 368 static void
 369 tbl_literal(struct termp *tp, const struct tbl_dat *dp, 
 370                 const struct roffcol *col)
 371 {
 372         size_t           len, padl, padr;


 373 
 374         assert(dp->string);
 375         len = term_strlen(tp, dp->string);
 376         padr = col->width > len ? col->width - len : 0;






 377         padl = 0;
 378 
 379         switch (dp->layout->pos) {
 380         case (TBL_CELL_LONG):
 381                 padl = term_len(tp, 1);
 382                 padr = padr > padl ? padr - padl : 0;
 383                 break;
 384         case (TBL_CELL_CENTRE):
 385                 if (2 > padr)
 386                         break;
 387                 padl = padr / 2;
 388                 padr -= padl;
 389                 break;
 390         case (TBL_CELL_RIGHT):
 391                 padl = padr;
 392                 padr = 0;
 393                 break;
 394         default:
 395                 break;
 396         }
 397 
 398         tbl_char(tp, ASCII_NBRSP, padl);
 399         term_word(tp, dp->string);
 400         tbl_char(tp, ASCII_NBRSP, padr);
 401 }
 402 
 403 static void
 404 tbl_number(struct termp *tp, const struct tbl *tbl,
 405                 const struct tbl_dat *dp,
 406                 const struct roffcol *col)
 407 {
 408         char            *cp;
 409         char             buf[2];
 410         size_t           sz, psz, ssz, d, padl;
 411         int              i;
 412 
 413         /*
 414          * See calc_data_number().  Left-pad by taking the offset of our
 415          * and the maximum decimal; right-pad by the remaining amount.
 416          */
 417 
 418         assert(dp->string);
 419 
 420         sz = term_strlen(tp, dp->string);
 421 
 422         buf[0] = tbl->decimal;
 423         buf[1] = '\0';
 424 
 425         psz = term_strlen(tp, buf);
 426 
 427         if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
 428                 buf[1] = '\0';
 429                 for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
 430                         buf[0] = dp->string[i];
 431                         ssz += term_strlen(tp, buf);
 432                 }
 433                 d = ssz + psz;
 434         } else
 435                 d = sz + psz;
 436 
 437         padl = col->decimal - d;
 438 
 439         tbl_char(tp, ASCII_NBRSP, padl);
 440         term_word(tp, dp->string);
 441         if (col->width > sz + padl)
 442                 tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
 443 }
 444 
   1 /*      $Id: tbl_term.c,v 1.25 2013/05/31 21:37:17 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2011, 2012 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_opts *,
  35                         const struct tbl_dat *, 
  36                         const struct roffcol *);
  37 static  size_t  tbl_rulewidth(struct termp *, const struct tbl_head *);
  38 static  void    tbl_hframe(struct termp *, const struct tbl_span *, int);
  39 static  void    tbl_literal(struct termp *, const struct tbl_dat *, 
  40                         const struct roffcol *);
  41 static  void    tbl_number(struct termp *, const struct tbl_opts *, 
  42                         const struct tbl_dat *, 
  43                         const struct roffcol *);
  44 static  void    tbl_hrule(struct termp *, const struct tbl_span *);
  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 


  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                 if (TBL_OPT_DBOX & sp->opts->opts)
 100                         tbl_hframe(tp, sp, 1);
 101                 if (TBL_OPT_DBOX & sp->opts->opts ||
 102                     TBL_OPT_BOX  & sp->opts->opts)
 103                         tbl_hframe(tp, sp, 0);
 104         }
 105 
 106         /* Vertical frame at the start of each row. */
 107 
 108         if (TBL_OPT_BOX & sp->opts->opts || TBL_OPT_DBOX & sp->opts->opts)
 109                 term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
 110                         TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
 111 
 112         /*
 113          * Now print the actual data itself depending on the span type.
 114          * Spanner spans get a horizontal rule; data spanners have their
 115          * data printed by matching data to header.
 116          */
 117 
 118         switch (sp->pos) {
 119         case (TBL_SPAN_HORIZ):
 120                 /* FALLTHROUGH */
 121         case (TBL_SPAN_DHORIZ):
 122                 tbl_hrule(tp, sp);
 123                 break;
 124         case (TBL_SPAN_DATA):
 125                 /* Iterate over template headers. */
 126                 dp = sp->first;
 127                 spans = 0;
 128                 for (hp = sp->head; hp; hp = hp->next) {
 129 
 130                         /* 
 131                          * If the current data header is invoked during
 132                          * a spanner ("spans" > 0), don't emit anything
 133                          * at all.
 134                          */










 135 
 136                         if (--spans >= 0)
 137                                 continue;
 138 
 139                         /* Separate columns. */



 140 
 141                         if (NULL != hp->prev)
 142                                 tbl_vrule(tp, hp);
 143 
 144                         col = &tp->tbl.cols[hp->ident];
 145                         tbl_data(tp, sp->opts, dp, col);
 146 





 147                         /* 









 148                          * Go to the next data cell and assign the
 149                          * number of subsequent spans, if applicable.
 150                          */
 151 
 152                         if (dp) {
 153                                 spans = dp->spans;
 154                                 dp = dp->next;
 155                         }
 156                 }
 157                 break;
 158         }
 159 
 160         /* Vertical frame at the end of each row. */
 161 
 162         if (TBL_OPT_BOX & sp->opts->opts || TBL_OPT_DBOX & sp->opts->opts)
 163                 term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
 164                         TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
 165         term_flushln(tp);
 166 
 167         /*
 168          * If we're the last row, clean up after ourselves: clear the
 169          * existing table configuration and set it to NULL.
 170          */
 171 
 172         if (TBL_SPAN_LAST & sp->flags) {
 173                 if (TBL_OPT_DBOX & sp->opts->opts ||
 174                     TBL_OPT_BOX  & sp->opts->opts) {
 175                         tbl_hframe(tp, sp, 0);
 176                         tp->skipvsp = 1;
 177                 }
 178                 if (TBL_OPT_DBOX & sp->opts->opts) {
 179                         tbl_hframe(tp, sp, 1);
 180                         tp->skipvsp = 2;
 181                 }
 182                 assert(tp->tbl.cols);
 183                 free(tp->tbl.cols);
 184                 tp->tbl.cols = NULL;
 185         }
 186 
 187         tp->flags &= ~TERMP_NONOSPACE;
 188         tp->rmargin = rmargin;
 189         tp->maxrmargin = maxrmargin;
 190 
 191 }
 192 
 193 /*
 194  * Horizontal rules extend across the entire table.
 195  * Calculate the width by iterating over columns.
 196  */
 197 static size_t
 198 tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
 199 {
 200         size_t           width;
 201 
 202         width = tp->tbl.cols[hp->ident].width;
 203 
 204         /* Account for leading blanks. */
 205         if (hp->prev)
 206                 width += 2 - hp->vert;
 207 
 208         /* Account for trailing blank. */
 209         width++;
 210 






 211         return(width);
 212 }
 213 
 214 /*
 215  * Rules inside the table can be single or double
 216  * and have crossings with vertical rules marked with pluses.
 217  */
 218 static void
 219 tbl_hrule(struct termp *tp, const struct tbl_span *sp)
 220 {
 221         const struct tbl_head *hp;
 222         char             c;
 223 
 224         c = '-';
 225         if (TBL_SPAN_DHORIZ == sp->pos)
 226                 c = '=';
 227 
 228         for (hp = sp->head; hp; hp = hp->next) {
 229                 if (hp->prev && hp->vert)
 230                         tbl_char(tp, '+', hp->vert);
 231                 tbl_char(tp, c, tbl_rulewidth(tp, hp));
 232         }
 233 }
 234 
 235 /*
 236  * Rules above and below the table are always single
 237  * and have an additional plus at the beginning and end.
 238  * For double frames, this function is called twice,
 239  * and the outer one does not have crossings.
 240  */
 241 static void
 242 tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
 243 {
 244         const struct tbl_head *hp;
 245 
 246         term_word(tp, "+");
 247         for (hp = sp->head; hp; hp = hp->next) {
 248                 if (hp->prev && hp->vert)
 249                         tbl_char(tp, (outer ? '-' : '+'), hp->vert);
 250                 tbl_char(tp, '-', tbl_rulewidth(tp, hp));
 251         }
 252         term_word(tp, "+");
 253         term_flushln(tp);
 254 }
 255 
 256 static void
 257 tbl_data(struct termp *tp, const struct tbl_opts *opts,
 258                 const struct tbl_dat *dp, 
 259                 const struct roffcol *col)
 260 {
 261 
 262         if (NULL == dp) {
 263                 tbl_char(tp, ASCII_NBRSP, col->width);
 264                 return;
 265         }
 266         assert(dp->layout);
 267 
 268         switch (dp->pos) {
 269         case (TBL_DATA_NONE):
 270                 tbl_char(tp, ASCII_NBRSP, col->width);
 271                 return;
 272         case (TBL_DATA_HORIZ):
 273                 /* FALLTHROUGH */
 274         case (TBL_DATA_NHORIZ):
 275                 tbl_char(tp, '-', col->width);
 276                 return;
 277         case (TBL_DATA_NDHORIZ):


 283                 break;
 284         }
 285         
 286         switch (dp->layout->pos) {
 287         case (TBL_CELL_HORIZ):
 288                 tbl_char(tp, '-', col->width);
 289                 break;
 290         case (TBL_CELL_DHORIZ):
 291                 tbl_char(tp, '=', col->width);
 292                 break;
 293         case (TBL_CELL_LONG):
 294                 /* FALLTHROUGH */
 295         case (TBL_CELL_CENTRE):
 296                 /* FALLTHROUGH */
 297         case (TBL_CELL_LEFT):
 298                 /* FALLTHROUGH */
 299         case (TBL_CELL_RIGHT):
 300                 tbl_literal(tp, dp, col);
 301                 break;
 302         case (TBL_CELL_NUMBER):
 303                 tbl_number(tp, opts, dp, col);
 304                 break;
 305         case (TBL_CELL_DOWN):
 306                 tbl_char(tp, ASCII_NBRSP, col->width);
 307                 break;
 308         default:
 309                 abort();
 310                 /* NOTREACHED */
 311         }
 312 }
 313 
 314 static void
 315 tbl_vrule(struct termp *tp, const struct tbl_head *hp)
 316 {
 317 
 318         tbl_char(tp, ASCII_NBRSP, 1);
 319         if (0 < hp->vert)
 320                 tbl_char(tp, '|', hp->vert);
 321         if (2 > hp->vert)
 322                 tbl_char(tp, ASCII_NBRSP, 2 - hp->vert);





 323 }
 324 
 325 static void
 326 tbl_char(struct termp *tp, char c, size_t len)
 327 {
 328         size_t          i, sz;
 329         char            cp[2];
 330 
 331         cp[0] = c;
 332         cp[1] = '\0';
 333 
 334         sz = term_strlen(tp, cp);
 335 
 336         for (i = 0; i < len; i += sz)
 337                 term_word(tp, cp);
 338 }
 339 
 340 static void
 341 tbl_literal(struct termp *tp, const struct tbl_dat *dp, 
 342                 const struct roffcol *col)
 343 {
 344         struct tbl_head         *hp;
 345         size_t                   width, len, padl, padr;
 346         int                      spans;
 347 
 348         assert(dp->string);
 349         len = term_strlen(tp, dp->string);
 350 
 351         hp = dp->layout->head->next;
 352         width = col->width;
 353         for (spans = dp->spans; spans--; hp = hp->next)
 354                 width += tp->tbl.cols[hp->ident].width + 3;
 355 
 356         padr = width > len ? width - len : 0;
 357         padl = 0;
 358 
 359         switch (dp->layout->pos) {
 360         case (TBL_CELL_LONG):
 361                 padl = term_len(tp, 1);
 362                 padr = padr > padl ? padr - padl : 0;
 363                 break;
 364         case (TBL_CELL_CENTRE):
 365                 if (2 > padr)
 366                         break;
 367                 padl = padr / 2;
 368                 padr -= padl;
 369                 break;
 370         case (TBL_CELL_RIGHT):
 371                 padl = padr;
 372                 padr = 0;
 373                 break;
 374         default:
 375                 break;
 376         }
 377 
 378         tbl_char(tp, ASCII_NBRSP, padl);
 379         term_word(tp, dp->string);
 380         tbl_char(tp, ASCII_NBRSP, padr);
 381 }
 382 
 383 static void
 384 tbl_number(struct termp *tp, const struct tbl_opts *opts,
 385                 const struct tbl_dat *dp,
 386                 const struct roffcol *col)
 387 {
 388         char            *cp;
 389         char             buf[2];
 390         size_t           sz, psz, ssz, d, padl;
 391         int              i;
 392 
 393         /*
 394          * See calc_data_number().  Left-pad by taking the offset of our
 395          * and the maximum decimal; right-pad by the remaining amount.
 396          */
 397 
 398         assert(dp->string);
 399 
 400         sz = term_strlen(tp, dp->string);
 401 
 402         buf[0] = opts->decimal;
 403         buf[1] = '\0';
 404 
 405         psz = term_strlen(tp, buf);
 406 
 407         if (NULL != (cp = strrchr(dp->string, opts->decimal))) {
 408                 buf[1] = '\0';
 409                 for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
 410                         buf[0] = dp->string[i];
 411                         ssz += term_strlen(tp, buf);
 412                 }
 413                 d = ssz + psz;
 414         } else
 415                 d = sz + psz;
 416 
 417         padl = col->decimal - d;
 418 
 419         tbl_char(tp, ASCII_NBRSP, padl);
 420         term_word(tp, dp->string);
 421         if (col->width > sz + padl)
 422                 tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
 423 }
 424