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