1 /* $Id: tbl_data.c,v 1.31 2014/04/23 16:08:33 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 "mandoc_aux.h" 30 #include "libmandoc.h" 31 #include "libroff.h" 32 33 static int getdata(struct tbl_node *, struct tbl_span *, 34 int, const char *, int *); 35 static struct tbl_span *newspan(struct tbl_node *, int, 36 struct tbl_row *); 37 38 39 static int 40 getdata(struct tbl_node *tbl, struct tbl_span *dp, 41 int ln, const char *p, int *pos) 42 { 43 struct tbl_dat *dat; 44 struct tbl_cell *cp; 45 int sv, spans; 46 47 cp = NULL; 48 if (dp->last && dp->last->layout) 49 cp = dp->last->layout->next; 50 else if (NULL == dp->last) 51 cp = dp->layout->first; 52 53 /* 54 * Skip over spanners, since 55 * we want to match data with data layout cells in the header. 56 */ 57 58 while (cp && 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, tbl->parse, 68 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(1); 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 int 141 tbl_cdata(struct tbl_node *tbl, int ln, const char *p) 142 { 143 struct tbl_dat *dat; 144 size_t sz; 145 int pos; 146 147 pos = 0; 148 149 dat = tbl->last_span->last; 150 151 if (p[pos] == 'T' && p[pos + 1] == '}') { 152 pos += 2; 153 if (p[pos] == tbl->opts.tab) { 154 tbl->part = TBL_PART_DATA; 155 pos++; 156 return(getdata(tbl, tbl->last_span, ln, p, &pos)); 157 } else if ('\0' == p[pos]) { 158 tbl->part = TBL_PART_DATA; 159 return(1); 160 } 161 162 /* Fallthrough: T} is part of a word. */ 163 } 164 165 dat->pos = TBL_DATA_DATA; 166 167 if (dat->string) { 168 sz = strlen(p) + strlen(dat->string) + 2; 169 dat->string = mandoc_realloc(dat->string, sz); 170 (void)strlcat(dat->string, " ", sz); 171 (void)strlcat(dat->string, p, sz); 172 } else 173 dat->string = mandoc_strdup(p); 174 175 if (TBL_CELL_DOWN == dat->layout->pos) 176 mandoc_msg(MANDOCERR_TBLIGNDATA, tbl->parse, 177 ln, pos, NULL); 178 179 return(0); 180 } 181 182 static struct tbl_span * 183 newspan(struct tbl_node *tbl, int line, struct tbl_row *rp) 184 { 185 struct tbl_span *dp; 186 187 dp = mandoc_calloc(1, sizeof(struct tbl_span)); 188 dp->line = line; 189 dp->opts = &tbl->opts; 190 dp->layout = rp; 191 dp->head = tbl->first_head; 192 193 if (tbl->last_span) { 194 tbl->last_span->next = dp; 195 tbl->last_span = dp; 196 } else { 197 tbl->last_span = tbl->first_span = dp; 198 tbl->current_span = NULL; 199 dp->flags |= TBL_SPAN_FIRST; 200 } 201 202 return(dp); 203 } 204 205 int 206 tbl_data(struct tbl_node *tbl, int ln, const char *p) 207 { 208 struct tbl_span *dp; 209 struct tbl_row *rp; 210 int pos; 211 212 pos = 0; 213 214 if ('\0' == p[pos]) { 215 mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, pos, NULL); 216 return(0); 217 } 218 219 /* 220 * Choose a layout row: take the one following the last parsed 221 * span's. If that doesn't exist, use the last parsed span's. 222 * If there's no last parsed span, use the first row. Lastly, 223 * if the last span was a horizontal line, use the same layout 224 * (it doesn't "consume" the layout). 225 */ 226 227 if (tbl->last_span) { 228 assert(tbl->last_span->layout); 229 if (tbl->last_span->pos == TBL_SPAN_DATA) { 230 for (rp = tbl->last_span->layout->next; 231 rp && rp->first; rp = rp->next) { 232 switch (rp->first->pos) { 233 case TBL_CELL_HORIZ: 234 dp = newspan(tbl, ln, rp); 235 dp->pos = TBL_SPAN_HORIZ; 236 continue; 237 case TBL_CELL_DHORIZ: 238 dp = newspan(tbl, ln, rp); 239 dp->pos = TBL_SPAN_DHORIZ; 240 continue; 241 default: 242 break; 243 } 244 break; 245 } 246 } else 247 rp = tbl->last_span->layout; 248 249 if (NULL == rp) 250 rp = tbl->last_span->layout; 251 } else 252 rp = tbl->first_row; 253 254 assert(rp); 255 256 dp = newspan(tbl, ln, rp); 257 258 if ( ! strcmp(p, "_")) { 259 dp->pos = TBL_SPAN_HORIZ; 260 return(1); 261 } else if ( ! strcmp(p, "=")) { 262 dp->pos = TBL_SPAN_DHORIZ; 263 return(1); 264 } 265 266 dp->pos = TBL_SPAN_DATA; 267 268 /* This returns 0 when TBL_PART_CDATA is entered. */ 269 270 while ('\0' != p[pos]) 271 if ( ! getdata(tbl, dp, ln, p, &pos)) 272 return(0); 273 274 return(1); 275 } 276