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