1 /*      $Id: out.c,v 1.43 2011/09/20 23:05:49 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2009, 2010, 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 <sys/types.h>
  23 
  24 #include <assert.h>
  25 #include <ctype.h>
  26 #include <stdio.h>
  27 #include <stdlib.h>
  28 #include <string.h>
  29 #include <time.h>
  30 
  31 #include "mandoc.h"
  32 #include "out.h"
  33 
  34 static  void    tblcalc_data(struct rofftbl *, struct roffcol *,
  35                         const struct tbl *, const struct tbl_dat *);
  36 static  void    tblcalc_literal(struct rofftbl *, struct roffcol *,
  37                         const struct tbl_dat *);
  38 static  void    tblcalc_number(struct rofftbl *, struct roffcol *,
  39                         const struct tbl *, const struct tbl_dat *);
  40 
  41 /* 
  42  * Convert a `scaling unit' to a consistent form, or fail.  Scaling
  43  * units are documented in groff.7, mdoc.7, man.7.
  44  */
  45 int
  46 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
  47 {
  48         char             buf[BUFSIZ], hasd;
  49         int              i;
  50         enum roffscale   unit;
  51 
  52         if ('\0' == *src)
  53                 return(0);
  54 
  55         i = hasd = 0;
  56 
  57         switch (*src) {
  58         case ('+'):
  59                 src++;
  60                 break;
  61         case ('-'):
  62                 buf[i++] = *src++;
  63                 break;
  64         default:
  65                 break;
  66         }
  67 
  68         if ('\0' == *src)
  69                 return(0);
  70 
  71         while (i < BUFSIZ) {
  72                 if ( ! isdigit((unsigned char)*src)) {
  73                         if ('.' != *src)
  74                                 break;
  75                         else if (hasd)
  76                                 break;
  77                         else
  78                                 hasd = 1;
  79                 }
  80                 buf[i++] = *src++;
  81         }
  82 
  83         if (BUFSIZ == i || (*src && *(src + 1)))
  84                 return(0);
  85 
  86         buf[i] = '\0';
  87 
  88         switch (*src) {
  89         case ('c'):
  90                 unit = SCALE_CM;
  91                 break;
  92         case ('i'):
  93                 unit = SCALE_IN;
  94                 break;
  95         case ('P'):
  96                 unit = SCALE_PC;
  97                 break;
  98         case ('p'):
  99                 unit = SCALE_PT;
 100                 break;
 101         case ('f'):
 102                 unit = SCALE_FS;
 103                 break;
 104         case ('v'):
 105                 unit = SCALE_VS;
 106                 break;
 107         case ('m'):
 108                 unit = SCALE_EM;
 109                 break;
 110         case ('\0'):
 111                 if (SCALE_MAX == def)
 112                         return(0);
 113                 unit = SCALE_BU;
 114                 break;
 115         case ('u'):
 116                 unit = SCALE_BU;
 117                 break;
 118         case ('M'):
 119                 unit = SCALE_MM;
 120                 break;
 121         case ('n'):
 122                 unit = SCALE_EN;
 123                 break;
 124         default:
 125                 return(0);
 126         }
 127 
 128         /* FIXME: do this in the caller. */
 129         if ((dst->scale = atof(buf)) < 0)
 130                 dst->scale = 0;
 131         dst->unit = unit;
 132         return(1);
 133 }
 134 
 135 /*
 136  * Calculate the abstract widths and decimal positions of columns in a
 137  * table.  This routine allocates the columns structures then runs over
 138  * all rows and cells in the table.  The function pointers in "tbl" are
 139  * used for the actual width calculations.
 140  */
 141 void
 142 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
 143 {
 144         const struct tbl_dat    *dp;
 145         const struct tbl_head   *hp;
 146         struct roffcol          *col;
 147         int                      spans;
 148 
 149         /*
 150          * Allocate the master column specifiers.  These will hold the
 151          * widths and decimal positions for all cells in the column.  It
 152          * must be freed and nullified by the caller.
 153          */
 154 
 155         assert(NULL == tbl->cols);
 156         tbl->cols = mandoc_calloc
 157                 ((size_t)sp->tbl->cols, sizeof(struct roffcol));
 158 
 159         hp = sp->head;
 160 
 161         for ( ; sp; sp = sp->next) {
 162                 if (TBL_SPAN_DATA != sp->pos)
 163                         continue;
 164                 spans = 1;
 165                 /*
 166                  * Account for the data cells in the layout, matching it
 167                  * to data cells in the data section.
 168                  */
 169                 for (dp = sp->first; dp; dp = dp->next) {
 170                         /* Do not used spanned cells in the calculation. */
 171                         if (0 < --spans)
 172                                 continue;
 173                         spans = dp->spans;
 174                         if (1 < spans)
 175                                 continue;
 176                         assert(dp->layout);
 177                         col = &tbl->cols[dp->layout->head->ident];
 178                         tblcalc_data(tbl, col, sp->tbl, dp);
 179                 }
 180         }
 181 
 182         /* 
 183          * Calculate width of the spanners.  These get one space for a
 184          * vertical line, two for a double-vertical line. 
 185          */
 186 
 187         for ( ; hp; hp = hp->next) {
 188                 col = &tbl->cols[hp->ident];
 189                 switch (hp->pos) {
 190                 case (TBL_HEAD_VERT):
 191                         col->width = (*tbl->len)(1, tbl->arg);
 192                         break;
 193                 case (TBL_HEAD_DVERT):
 194                         col->width = (*tbl->len)(2, tbl->arg);
 195                         break;
 196                 default:
 197                         break;
 198                 }
 199         }
 200 }
 201 
 202 static void
 203 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
 204                 const struct tbl *tp, const struct tbl_dat *dp)
 205 {
 206         size_t           sz;
 207 
 208         /* Branch down into data sub-types. */
 209 
 210         switch (dp->layout->pos) {
 211         case (TBL_CELL_HORIZ):
 212                 /* FALLTHROUGH */
 213         case (TBL_CELL_DHORIZ):
 214                 sz = (*tbl->len)(1, tbl->arg);
 215                 if (col->width < sz)
 216                         col->width = sz;
 217                 break;
 218         case (TBL_CELL_LONG):
 219                 /* FALLTHROUGH */
 220         case (TBL_CELL_CENTRE):
 221                 /* FALLTHROUGH */
 222         case (TBL_CELL_LEFT):
 223                 /* FALLTHROUGH */
 224         case (TBL_CELL_RIGHT):
 225                 tblcalc_literal(tbl, col, dp);
 226                 break;
 227         case (TBL_CELL_NUMBER):
 228                 tblcalc_number(tbl, col, tp, dp);
 229                 break;
 230         case (TBL_CELL_DOWN):
 231                 break;
 232         default:
 233                 abort();
 234                 /* NOTREACHED */
 235         }
 236 }
 237 
 238 static void
 239 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
 240                 const struct tbl_dat *dp)
 241 {
 242         size_t           sz;
 243         const char      *str;
 244 
 245         str = dp->string ? dp->string : "";
 246         sz = (*tbl->slen)(str, tbl->arg);
 247 
 248         if (col->width < sz)
 249                 col->width = sz;
 250 }
 251 
 252 static void
 253 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
 254                 const struct tbl *tp, const struct tbl_dat *dp)
 255 {
 256         int              i;
 257         size_t           sz, psz, ssz, d;
 258         const char      *str;
 259         char            *cp;
 260         char             buf[2];
 261 
 262         /*
 263          * First calculate number width and decimal place (last + 1 for
 264          * non-decimal numbers).  If the stored decimal is subsequent to
 265          * ours, make our size longer by that difference
 266          * (right-"shifting"); similarly, if ours is subsequent the
 267          * stored, then extend the stored size by the difference.
 268          * Finally, re-assign the stored values.
 269          */
 270 
 271         str = dp->string ? dp->string : "";
 272         sz = (*tbl->slen)(str, tbl->arg);
 273 
 274         /* FIXME: TBL_DATA_HORIZ et al.? */
 275 
 276         buf[0] = tp->decimal;
 277         buf[1] = '\0';
 278 
 279         psz = (*tbl->slen)(buf, tbl->arg);
 280 
 281         if (NULL != (cp = strrchr(str, tp->decimal))) {
 282                 buf[1] = '\0';
 283                 for (ssz = 0, i = 0; cp != &str[i]; i++) {
 284                         buf[0] = str[i];
 285                         ssz += (*tbl->slen)(buf, tbl->arg);
 286                 }
 287                 d = ssz + psz;
 288         } else
 289                 d = sz + psz;
 290 
 291         /* Adjust the settings for this column. */
 292 
 293         if (col->decimal > d) {
 294                 sz += col->decimal - d;
 295                 d = col->decimal;
 296         } else
 297                 col->width += d - col->decimal;
 298 
 299         if (sz > col->width)
 300                 col->width = sz;
 301         if (d > col->decimal)
 302                 col->decimal = d;
 303 }