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