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