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 }