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