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