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