1 /*      $Id: out.c,v 1.46 2013/10/05 20:30:05 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_opts *, 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_opts *, 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         struct roffcol          *col;
 146         int                      spans;
 147 
 148         /*
 149          * Allocate the master column specifiers.  These will hold the
 150          * widths and decimal positions for all cells in the column.  It
 151          * must be freed and nullified by the caller.
 152          */
 153 
 154         assert(NULL == tbl->cols);
 155         tbl->cols = mandoc_calloc
 156                 ((size_t)sp->opts->cols, sizeof(struct roffcol));
 157 
 158         for ( ; sp; sp = sp->next) {
 159                 if (TBL_SPAN_DATA != sp->pos)
 160                         continue;
 161                 spans = 1;
 162                 /*
 163                  * Account for the data cells in the layout, matching it
 164                  * to data cells in the data section.
 165                  */
 166                 for (dp = sp->first; dp; dp = dp->next) {
 167                         /* Do not used spanned cells in the calculation. */
 168                         if (0 < --spans)
 169                                 continue;
 170                         spans = dp->spans;
 171                         if (1 < spans)
 172                                 continue;
 173                         assert(dp->layout);
 174                         col = &tbl->cols[dp->layout->head->ident];
 175                         tblcalc_data(tbl, col, sp->opts, dp);
 176                 }
 177         }
 178 }
 179 
 180 static void
 181 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
 182                 const struct tbl_opts *opts, const struct tbl_dat *dp)
 183 {
 184         size_t           sz;
 185 
 186         /* Branch down into data sub-types. */
 187 
 188         switch (dp->layout->pos) {
 189         case (TBL_CELL_HORIZ):
 190                 /* FALLTHROUGH */
 191         case (TBL_CELL_DHORIZ):
 192                 sz = (*tbl->len)(1, tbl->arg);
 193                 if (col->width < sz)
 194                         col->width = sz;
 195                 break;
 196         case (TBL_CELL_LONG):
 197                 /* FALLTHROUGH */
 198         case (TBL_CELL_CENTRE):
 199                 /* FALLTHROUGH */
 200         case (TBL_CELL_LEFT):
 201                 /* FALLTHROUGH */
 202         case (TBL_CELL_RIGHT):
 203                 tblcalc_literal(tbl, col, dp);
 204                 break;
 205         case (TBL_CELL_NUMBER):
 206                 tblcalc_number(tbl, col, opts, dp);
 207                 break;
 208         case (TBL_CELL_DOWN):
 209                 break;
 210         default:
 211                 abort();
 212                 /* NOTREACHED */
 213         }
 214 }
 215 
 216 static void
 217 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
 218                 const struct tbl_dat *dp)
 219 {
 220         size_t           sz;
 221         const char      *str;
 222 
 223         str = dp->string ? dp->string : "";
 224         sz = (*tbl->slen)(str, tbl->arg);
 225 
 226         if (col->width < sz)
 227                 col->width = sz;
 228 }
 229 
 230 static void
 231 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
 232                 const struct tbl_opts *opts, const struct tbl_dat *dp)
 233 {
 234         int              i;
 235         size_t           sz, psz, ssz, d;
 236         const char      *str;
 237         char            *cp;
 238         char             buf[2];
 239 
 240         /*
 241          * First calculate number width and decimal place (last + 1 for
 242          * non-decimal numbers).  If the stored decimal is subsequent to
 243          * ours, make our size longer by that difference
 244          * (right-"shifting"); similarly, if ours is subsequent the
 245          * stored, then extend the stored size by the difference.
 246          * Finally, re-assign the stored values.
 247          */
 248 
 249         str = dp->string ? dp->string : "";
 250         sz = (*tbl->slen)(str, tbl->arg);
 251 
 252         /* FIXME: TBL_DATA_HORIZ et al.? */
 253 
 254         buf[0] = opts->decimal;
 255         buf[1] = '\0';
 256 
 257         psz = (*tbl->slen)(buf, tbl->arg);
 258 
 259         if (NULL != (cp = strrchr(str, opts->decimal))) {
 260                 buf[1] = '\0';
 261                 for (ssz = 0, i = 0; cp != &str[i]; i++) {
 262                         buf[0] = str[i];
 263                         ssz += (*tbl->slen)(buf, tbl->arg);
 264                 }
 265                 d = ssz + psz;
 266         } else
 267                 d = sz + psz;
 268 
 269         /* Adjust the settings for this column. */
 270 
 271         if (col->decimal > d) {
 272                 sz += col->decimal - d;
 273                 d = col->decimal;
 274         } else
 275                 col->width += d - col->decimal;
 276 
 277         if (sz > col->width)
 278                 col->width = sz;
 279         if (d > col->decimal)
 280                 col->decimal = d;
 281 }