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