tbl_data.c revision 241675
10SN/A/* $Id: tbl_data.c,v 1.24 2011/03/20 16:02:05 kristaps Exp $ */ 29674SN/A/* 30SN/A * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 40SN/A * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> 50SN/A * 60SN/A * Permission to use, copy, modify, and distribute this software for any 72362SN/A * purpose with or without fee is hereby granted, provided that the above 80SN/A * copyright notice and this permission notice appear in all copies. 92362SN/A * 100SN/A * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 110SN/A * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 120SN/A * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 130SN/A * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 140SN/A * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 150SN/A * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 160SN/A * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 170SN/A */ 180SN/A#ifdef HAVE_CONFIG_H 190SN/A#include "config.h" 200SN/A#endif 212362SN/A 222362SN/A#include <assert.h> 232362SN/A#include <ctype.h> 240SN/A#include <stdlib.h> 250SN/A#include <string.h> 260SN/A#include <time.h> 270SN/A 280SN/A#include "mandoc.h" 290SN/A#include "libmandoc.h" 300SN/A#include "libroff.h" 319674SN/A 320SN/Astatic int data(struct tbl_node *, struct tbl_span *, 330SN/A int, const char *, int *); 340SN/Astatic struct tbl_span *newspan(struct tbl_node *, int, 350SN/A struct tbl_row *); 360SN/A 370SN/Astatic int 380SN/Adata(struct tbl_node *tbl, struct tbl_span *dp, 390SN/A int ln, const char *p, int *pos) 400SN/A{ 410SN/A struct tbl_dat *dat; 420SN/A struct tbl_cell *cp; 430SN/A int sv, spans; 440SN/A 450SN/A cp = NULL; 460SN/A if (dp->last && dp->last->layout) 470SN/A cp = dp->last->layout->next; 480SN/A else if (NULL == dp->last) 490SN/A cp = dp->layout->first; 500SN/A 510SN/A /* 520SN/A * Skip over spanners and vertical lines to data formats, since 530SN/A * we want to match data with data layout cells in the header. 540SN/A */ 550SN/A 560SN/A while (cp && (TBL_CELL_VERT == cp->pos || 570SN/A TBL_CELL_DVERT == cp->pos || 580SN/A TBL_CELL_SPAN == cp->pos)) 590SN/A cp = cp->next; 600SN/A 610SN/A /* 620SN/A * Stop processing when we reach the end of the available layout 630SN/A * cells. This means that we have extra input. 640SN/A */ 650SN/A 660SN/A if (NULL == cp) { 670SN/A mandoc_msg(MANDOCERR_TBLEXTRADAT, 680SN/A tbl->parse, ln, *pos, NULL); 690SN/A /* Skip to the end... */ 700SN/A while (p[*pos]) 710SN/A (*pos)++; 720SN/A return(1); 730SN/A } 740SN/A 750SN/A dat = mandoc_calloc(1, sizeof(struct tbl_dat)); 760SN/A dat->layout = cp; 770SN/A dat->pos = TBL_DATA_NONE; 780SN/A 790SN/A assert(TBL_CELL_SPAN != cp->pos); 800SN/A 810SN/A for (spans = 0, cp = cp->next; cp; cp = cp->next) 820SN/A if (TBL_CELL_SPAN == cp->pos) 830SN/A spans++; 840SN/A else 850SN/A break; 860SN/A 870SN/A dat->spans = spans; 880SN/A 890SN/A if (dp->last) { 900SN/A dp->last->next = dat; 910SN/A dp->last = dat; 920SN/A } else 930SN/A dp->last = dp->first = dat; 940SN/A 950SN/A 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 */ 141int 142tbl_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 183static struct tbl_span * 184newspan(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 206int 207tbl_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} 277