1 /* $OpenBSD: tbl_data.c,v 1.34 2018/11/25 21:17:30 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2011, 2015, 2017, 2018 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 void 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 void 38 getdata(struct tbl_node *tbl, struct tbl_span *dp, 39 int ln, const char *p, int *pos) 40 { 41 struct tbl_dat *dat, *pdat; 42 struct tbl_cell *cp; 43 struct tbl_span *pdp; 44 int sv; 45 46 /* 47 * Determine the length of the string in the cell 48 * and advance the parse point to the end of the cell. 49 */ 50 51 sv = *pos; 52 while (p[*pos] != '\0' && p[*pos] != tbl->opts.tab) 53 (*pos)++; 54 55 /* Advance to the next layout cell, skipping spanners. */ 56 57 cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next; 58 while (cp != NULL && cp->pos == TBL_CELL_SPAN) 59 cp = cp->next; 60 61 /* 62 * If the current layout row is out of cells, allocate 63 * a new cell if another row of the table has at least 64 * this number of columns, or discard the input if we 65 * are beyond the last column of the table as a whole. 66 */ 67 68 if (cp == NULL) { 69 if (dp->layout->last->col + 1 < dp->opts->cols) { 70 cp = mandoc_calloc(1, sizeof(*cp)); 71 cp->pos = TBL_CELL_LEFT; 72 dp->layout->last->next = cp; 73 cp->col = dp->layout->last->col + 1; 74 dp->layout->last = cp; 75 } else { 76 mandoc_msg(MANDOCERR_TBLDATA_EXTRA, tbl->parse, 77 ln, sv, p + sv); 78 while (p[*pos] != '\0') 79 (*pos)++; 80 return; 81 } 82 } 83 84 dat = mandoc_malloc(sizeof(*dat)); 85 dat->layout = cp; 86 dat->next = NULL; 87 dat->string = NULL; 88 dat->hspans = 0; 89 dat->vspans = 0; 90 dat->block = 0; 91 dat->pos = TBL_DATA_NONE; 92 93 /* 94 * Increment the number of vertical spans in a data cell above, 95 * if this cell vertically extends one or more cells above. 96 * The iteration must be done over data rows, 97 * not over layout rows, because one layout row 98 * can be reused for more than one data row. 99 */ 100 101 if (cp->pos == TBL_CELL_DOWN || 102 (*pos - sv == 2 && p[sv] == '\\' && p[sv + 1] == '^')) { 103 pdp = dp; 104 while ((pdp = pdp->prev) != NULL) { 105 pdat = pdp->first; 106 while (pdat != NULL && 107 pdat->layout->col < dat->layout->col) 108 pdat = pdat->next; 109 if (pdat == NULL) 110 break; 111 if (pdat->layout->pos != TBL_CELL_DOWN && 112 strcmp(pdat->string, "\\^") != 0) { 113 pdat->vspans++; 114 break; 115 } 116 } 117 } 118 119 /* 120 * Count the number of horizontal spans to the right of this cell. 121 * This is purely a matter of the layout, independent of the data. 122 */ 123 124 for (cp = cp->next; cp != NULL; cp = cp->next) 125 if (cp->pos == TBL_CELL_SPAN) 126 dat->hspans++; 127 else 128 break; 129 130 /* Append the new data cell to the data row. */ 131 132 if (dp->last == NULL) 133 dp->first = dat; 134 else 135 dp->last->next = dat; 136 dp->last = dat; 137 138 /* 139 * Check for a continued-data scope opening. This consists of a 140 * trailing `T{' at the end of the line. Subsequent lines, 141 * until a standalone `T}', are included in our cell. 142 */ 143 144 if (*pos - sv == 2 && p[sv] == 'T' && p[sv + 1] == '{') { 145 tbl->part = TBL_PART_CDATA; 146 return; 147 } 148 149 dat->string = mandoc_strndup(p + sv, *pos - sv); 150 151 if (p[*pos] != '\0') 152 (*pos)++; 153 154 if ( ! strcmp(dat->string, "_")) 155 dat->pos = TBL_DATA_HORIZ; 156 else if ( ! strcmp(dat->string, "=")) 157 dat->pos = TBL_DATA_DHORIZ; 158 else if ( ! strcmp(dat->string, "\\_")) 159 dat->pos = TBL_DATA_NHORIZ; 160 else if ( ! strcmp(dat->string, "\\=")) 161 dat->pos = TBL_DATA_NDHORIZ; 162 else 163 dat->pos = TBL_DATA_DATA; 164 165 if ((dat->layout->pos == TBL_CELL_HORIZ || 166 dat->layout->pos == TBL_CELL_DHORIZ || 167 dat->layout->pos == TBL_CELL_DOWN) && 168 dat->pos == TBL_DATA_DATA && *dat->string != '\0') 169 mandoc_msg(MANDOCERR_TBLDATA_SPAN, 170 tbl->parse, ln, sv, dat->string); 171 } 172 173 void 174 tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos) 175 { 176 struct tbl_dat *dat; 177 size_t sz; 178 179 dat = tbl->last_span->last; 180 181 if (p[pos] == 'T' && p[pos + 1] == '}') { 182 pos += 2; 183 if (p[pos] == tbl->opts.tab) { 184 tbl->part = TBL_PART_DATA; 185 pos++; 186 while (p[pos] != '\0') 187 getdata(tbl, tbl->last_span, ln, p, &pos); 188 return; 189 } else if (p[pos] == '\0') { 190 tbl->part = TBL_PART_DATA; 191 return; 192 } 193 194 /* Fallthrough: T} is part of a word. */ 195 } 196 197 dat->pos = TBL_DATA_DATA; 198 dat->block = 1; 199 200 if (dat->string != NULL) { 201 sz = strlen(p + pos) + strlen(dat->string) + 2; 202 dat->string = mandoc_realloc(dat->string, sz); 203 (void)strlcat(dat->string, " ", sz); 204 (void)strlcat(dat->string, p + pos, sz); 205 } else 206 dat->string = mandoc_strdup(p + pos); 207 208 if (dat->layout->pos == TBL_CELL_DOWN) 209 mandoc_msg(MANDOCERR_TBLDATA_SPAN, tbl->parse, 210 ln, pos, dat->string); 211 } 212 213 static struct tbl_span * 214 newspan(struct tbl_node *tbl, int line, struct tbl_row *rp) 215 { 216 struct tbl_span *dp; 217 218 dp = mandoc_calloc(1, sizeof(*dp)); 219 dp->line = line; 220 dp->opts = &tbl->opts; 221 dp->layout = rp; 222 dp->prev = tbl->last_span; 223 224 if (dp->prev == NULL) { 225 tbl->first_span = dp; 226 tbl->current_span = NULL; 227 } else 228 dp->prev->next = dp; 229 tbl->last_span = dp; 230 231 return dp; 232 } 233 234 void 235 tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos) 236 { 237 struct tbl_row *rp; 238 struct tbl_cell *cp; 239 struct tbl_span *sp; 240 241 rp = (sp = tbl->last_span) == NULL ? tbl->first_row : 242 sp->pos == TBL_SPAN_DATA && sp->layout->next != NULL ? 243 sp->layout->next : sp->layout; 244 245 assert(rp != NULL); 246 247 if ( ! strcmp(p, "_")) { 248 sp = newspan(tbl, ln, rp); 249 sp->pos = TBL_SPAN_HORIZ; 250 return; 251 } else if ( ! strcmp(p, "=")) { 252 sp = newspan(tbl, ln, rp); 253 sp->pos = TBL_SPAN_DHORIZ; 254 return; 255 } 256 257 /* 258 * If the layout row contains nothing but horizontal lines, 259 * allocate an empty span for it and assign the current span 260 * to the next layout row accepting data. 261 */ 262 263 while (rp->next != NULL) { 264 if (rp->last->col + 1 < tbl->opts.cols) 265 break; 266 for (cp = rp->first; cp != NULL; cp = cp->next) 267 if (cp->pos != TBL_CELL_HORIZ && 268 cp->pos != TBL_CELL_DHORIZ) 269 break; 270 if (cp != NULL) 271 break; 272 sp = newspan(tbl, ln, rp); 273 sp->pos = TBL_SPAN_DATA; 274 rp = rp->next; 275 } 276 277 /* Process a real data row. */ 278 279 sp = newspan(tbl, ln, rp); 280 sp->pos = TBL_SPAN_DATA; 281 while (p[pos] != '\0') 282 getdata(tbl, sp, ln, p, &pos); 283 } 284