1 /*      $Id: tbl_data.c,v 1.24 2011/03/20 16:02:05 kristaps 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 <assert.h>
  23 #include <ctype.h>
  24 #include <stdlib.h>
  25 #include <string.h>
  26 #include <time.h>
  27 
  28 #include "mandoc.h"
  29 #include "libmandoc.h"
  30 #include "libroff.h"
  31 
  32 static  int              data(struct tbl_node *, struct tbl_span *, 
  33                                 int, const char *, int *);
  34 static  struct tbl_span *newspan(struct tbl_node *, int, 
  35                                 struct tbl_row *);
  36 
  37 static int
  38 data(struct tbl_node *tbl, struct tbl_span *dp, 
  39                 int ln, const char *p, int *pos)
  40 {
  41         struct tbl_dat  *dat;
  42         struct tbl_cell *cp;
  43         int              sv, spans;
  44 
  45         cp = NULL;
  46         if (dp->last && dp->last->layout)
  47                 cp = dp->last->layout->next;
  48         else if (NULL == dp->last)
  49                 cp = dp->layout->first;
  50 
  51         /* 
  52          * Skip over spanners and vertical lines to data formats, since
  53          * we want to match data with data layout cells in the header.
  54          */
  55 
  56         while (cp && (TBL_CELL_VERT == cp->pos || 
  57                                 TBL_CELL_DVERT == cp->pos ||
  58                                 TBL_CELL_SPAN == cp->pos))
  59                 cp = cp->next;
  60 
  61         /*
  62          * Stop processing when we reach the end of the available layout
  63          * cells.  This means that we have extra input.
  64          */
  65 
  66         if (NULL == cp) {
  67                 mandoc_msg(MANDOCERR_TBLEXTRADAT, 
  68                                 tbl->parse, ln, *pos, NULL);
  69                 /* Skip to the end... */
  70                 while (p[*pos])
  71                         (*pos)++;
  72                 return(1);
  73         }
  74 
  75         dat = mandoc_calloc(1, sizeof(struct tbl_dat));
  76         dat->layout = cp;
  77         dat->pos = TBL_DATA_NONE;
  78 
  79         assert(TBL_CELL_SPAN != cp->pos);
  80 
  81         for (spans = 0, cp = cp->next; cp; cp = cp->next)
  82                 if (TBL_CELL_SPAN == cp->pos)
  83                         spans++;
  84                 else
  85                         break;
  86         
  87         dat->spans = spans;
  88 
  89         if (dp->last) {
  90                 dp->last->next = dat;
  91                 dp->last = dat;
  92         } else
  93                 dp->last = dp->first = dat;
  94 
  95         sv = *pos;
  96         while (p[*pos] && p[*pos] != tbl->opts.tab)
  97                 (*pos)++;
  98 
  99         /*
 100          * Check for a continued-data scope opening.  This consists of a
 101          * trailing `T{' at the end of the line.  Subsequent lines,
 102          * until a standalone `T}', are included in our cell.
 103          */
 104 
 105         if (*pos - sv == 2 && 'T' == p[sv] && '{' == p[sv + 1]) {
 106                 tbl->part = TBL_PART_CDATA;
 107                 return(0);
 108         }
 109 
 110         assert(*pos - sv >= 0);
 111 
 112         dat->string = mandoc_malloc((size_t)(*pos - sv + 1));
 113         memcpy(dat->string, &p[sv], (size_t)(*pos - sv));
 114         dat->string[*pos - sv] = '\0';
 115 
 116         if (p[*pos])
 117                 (*pos)++;
 118 
 119         if ( ! strcmp(dat->string, "_"))
 120                 dat->pos = TBL_DATA_HORIZ;
 121         else if ( ! strcmp(dat->string, "="))
 122                 dat->pos = TBL_DATA_DHORIZ;
 123         else if ( ! strcmp(dat->string, "\\_"))
 124                 dat->pos = TBL_DATA_NHORIZ;
 125         else if ( ! strcmp(dat->string, "\\="))
 126                 dat->pos = TBL_DATA_NDHORIZ;
 127         else
 128                 dat->pos = TBL_DATA_DATA;
 129 
 130         if (TBL_CELL_HORIZ == dat->layout->pos ||
 131                         TBL_CELL_DHORIZ == dat->layout->pos ||
 132                         TBL_CELL_DOWN == dat->layout->pos)
 133                 if (TBL_DATA_DATA == dat->pos && '\0' != *dat->string)
 134                         mandoc_msg(MANDOCERR_TBLIGNDATA, 
 135                                         tbl->parse, ln, sv, NULL);
 136 
 137         return(1);
 138 }
 139 
 140 /* ARGSUSED */
 141 int
 142 tbl_cdata(struct tbl_node *tbl, int ln, const char *p)
 143 {
 144         struct tbl_dat  *dat;
 145         size_t           sz;
 146         int              pos;
 147 
 148         pos = 0;
 149 
 150         dat = tbl->last_span->last;
 151 
 152         if (p[pos] == 'T' && p[pos + 1] == '}') {
 153                 pos += 2;
 154                 if (p[pos] == tbl->opts.tab) {
 155                         tbl->part = TBL_PART_DATA;
 156                         pos++;
 157                         return(data(tbl, tbl->last_span, ln, p, &pos));
 158                 } else if ('\0' == p[pos]) {
 159                         tbl->part = TBL_PART_DATA;
 160                         return(1);
 161                 }
 162 
 163                 /* Fallthrough: T} is part of a word. */
 164         }
 165 
 166         dat->pos = TBL_DATA_DATA;
 167 
 168         if (dat->string) {
 169                 sz = strlen(p) + strlen(dat->string) + 2;
 170                 dat->string = mandoc_realloc(dat->string, sz);
 171                 strlcat(dat->string, " ", sz);
 172                 strlcat(dat->string, p, sz);
 173         } else
 174                 dat->string = mandoc_strdup(p);
 175 
 176         if (TBL_CELL_DOWN == dat->layout->pos) 
 177                 mandoc_msg(MANDOCERR_TBLIGNDATA, 
 178                                 tbl->parse, ln, pos, NULL);
 179 
 180         return(0);
 181 }
 182 
 183 static struct tbl_span *
 184 newspan(struct tbl_node *tbl, int line, struct tbl_row *rp)
 185 {
 186         struct tbl_span *dp;
 187 
 188         dp = mandoc_calloc(1, sizeof(struct tbl_span));
 189         dp->line = line;
 190         dp->tbl = &tbl->opts;
 191         dp->layout = rp;
 192         dp->head = tbl->first_head;
 193 
 194         if (tbl->last_span) {
 195                 tbl->last_span->next = dp;
 196                 tbl->last_span = dp;
 197         } else {
 198                 tbl->last_span = tbl->first_span = dp;
 199                 tbl->current_span = NULL;
 200                 dp->flags |= TBL_SPAN_FIRST;
 201         }
 202 
 203         return(dp);
 204 }
 205 
 206 int
 207 tbl_data(struct tbl_node *tbl, int ln, const char *p)
 208 {
 209         struct tbl_span *dp;
 210         struct tbl_row  *rp;
 211         int              pos;
 212 
 213         pos = 0;
 214 
 215         if ('\0' == p[pos]) {
 216                 mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, pos, NULL);
 217                 return(0);
 218         }
 219 
 220         /* 
 221          * Choose a layout row: take the one following the last parsed
 222          * span's.  If that doesn't exist, use the last parsed span's.
 223          * If there's no last parsed span, use the first row.  Lastly,
 224          * if the last span was a horizontal line, use the same layout
 225          * (it doesn't "consume" the layout).
 226          */
 227 
 228         if (tbl->last_span) {
 229                 assert(tbl->last_span->layout);
 230                 if (tbl->last_span->pos == TBL_SPAN_DATA) {
 231                         for (rp = tbl->last_span->layout->next;
 232                                         rp && rp->first; rp = rp->next) {
 233                                 switch (rp->first->pos) {
 234                                 case (TBL_CELL_HORIZ):
 235                                         dp = newspan(tbl, ln, rp);
 236                                         dp->pos = TBL_SPAN_HORIZ;
 237                                         continue;
 238                                 case (TBL_CELL_DHORIZ):
 239                                         dp = newspan(tbl, ln, rp);
 240                                         dp->pos = TBL_SPAN_DHORIZ;
 241                                         continue;
 242                                 default:
 243                                         break;
 244                                 }
 245                                 break;
 246                         }
 247                 } else
 248                         rp = tbl->last_span->layout;
 249 
 250                 if (NULL == rp)
 251                         rp = tbl->last_span->layout;
 252         } else
 253                 rp = tbl->first_row;
 254 
 255         assert(rp);
 256 
 257         dp = newspan(tbl, ln, rp);
 258 
 259         if ( ! strcmp(p, "_")) {
 260                 dp->pos = TBL_SPAN_HORIZ;
 261                 return(1);
 262         } else if ( ! strcmp(p, "=")) {
 263                 dp->pos = TBL_SPAN_DHORIZ;
 264                 return(1);
 265         }
 266 
 267         dp->pos = TBL_SPAN_DATA;
 268 
 269         /* This returns 0 when TBL_PART_CDATA is entered. */
 270 
 271         while ('\0' != p[pos])
 272                 if ( ! data(tbl, dp, ln, p, &pos))
 273                         return(0);
 274 
 275         return(1);
 276 }