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