1 /* $Id: tbl_data.c,v 1.16 2014/03/21 22:17:01 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 static int 35 getdata(struct tbl_node *tbl, struct tbl_span *dp, 36 int ln, const char *p, int *pos) 37 { 38 struct tbl_dat *dat; 39 struct tbl_cell *cp; 40 int sv, spans; 41 42 cp = NULL; 43 if (dp->last && dp->last->layout) 44 cp = dp->last->layout->next; 45 else if (NULL == dp->last) 46 cp = dp->layout->first; 47 48 /* 49 * Skip over spanners, since 50 * we want to match data with data layout cells in the header. 51 */ 52 53 while (cp && TBL_CELL_SPAN == cp->pos) 54 cp = cp->next; 55 56 /* 57 * Stop processing when we reach the end of the available layout 58 * cells. This means that we have extra input. 59 */ 60 61 if (NULL == cp) { 62 mandoc_msg(MANDOCERR_TBLEXTRADAT, 63 tbl->parse, ln, *pos, NULL); 64 /* Skip to the end... */ 65 while (p[*pos]) 66 (*pos)++; 67 return(1); 68 } 69 70 dat = mandoc_calloc(1, sizeof(struct tbl_dat)); 71 dat->layout = cp; 72 dat->pos = TBL_DATA_NONE; 73 74 assert(TBL_CELL_SPAN != cp->pos); 75 76 for (spans = 0, cp = cp->next; cp; cp = cp->next) 77 if (TBL_CELL_SPAN == cp->pos) 78 spans++; 79 else 80 break; 81 82 dat->spans = spans; 83 84 if (dp->last) { 85 dp->last->next = dat; 86 dp->last = dat; 87 } else 88 dp->last = dp->first = dat; 89 90 sv = *pos; 91 while (p[*pos] && p[*pos] != tbl->opts.tab) 92 (*pos)++; 93 94 /* 95 * Check for a continued-data scope opening. This consists of a 96 * trailing `T{' at the end of the line. Subsequent lines, 97 * until a standalone `T}', are included in our cell. 98 */ 99 100 if (*pos - sv == 2 && 'T' == p[sv] && '{' == p[sv + 1]) { 101 tbl->part = TBL_PART_CDATA; 102 return(1); 103 } 104 105 assert(*pos - sv >= 0); 106 107 dat->string = mandoc_malloc((size_t)(*pos - sv + 1)); 108 memcpy(dat->string, &p[sv], (size_t)(*pos - sv)); 109 dat->string[*pos - sv] = '\0'; 110 111 if (p[*pos]) 112 (*pos)++; 113 114 if ( ! strcmp(dat->string, "_")) 115 dat->pos = TBL_DATA_HORIZ; 116 else if ( ! strcmp(dat->string, "=")) 117 dat->pos = TBL_DATA_DHORIZ; 118 else if ( ! strcmp(dat->string, "\\_")) 119 dat->pos = TBL_DATA_NHORIZ; 120 else if ( ! strcmp(dat->string, "\\=")) 121 dat->pos = TBL_DATA_NDHORIZ; 122 else 123 dat->pos = TBL_DATA_DATA; 124 125 if (TBL_CELL_HORIZ == dat->layout->pos || 126 TBL_CELL_DHORIZ == dat->layout->pos || 127 TBL_CELL_DOWN == dat->layout->pos) 128 if (TBL_DATA_DATA == dat->pos && '\0' != *dat->string) 129 mandoc_msg(MANDOCERR_TBLIGNDATA, 130 tbl->parse, ln, sv, NULL); 131 132 return(1); 133 } 134 135 /* ARGSUSED */ 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 strlcat(dat->string, " ", sz); 167 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, 173 tbl->parse, 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