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