1 /* $Id: tbl_layout.c,v 1.26 2014/04/20 16:46:05 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2012, 2014 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 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include <ctype.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <time.h> 26 27 #include "mandoc.h" 28 #include "mandoc_aux.h" 29 #include "libmandoc.h" 30 #include "libroff.h" 31 32 struct tbl_phrase { 33 char name; 34 enum tbl_cellt key; 35 }; 36 37 /* 38 * FIXME: we can make this parse a lot nicer by, when an error is 39 * encountered in a layout key, bailing to the next key (i.e. to the 40 * next whitespace then continuing). 41 */ 42 43 #define KEYS_MAX 11 44 45 static const struct tbl_phrase keys[KEYS_MAX] = { 46 { 'c', TBL_CELL_CENTRE }, 47 { 'r', TBL_CELL_RIGHT }, 48 { 'l', TBL_CELL_LEFT }, 49 { 'n', TBL_CELL_NUMBER }, 50 { 's', TBL_CELL_SPAN }, 51 { 'a', TBL_CELL_LONG }, 52 { '^', TBL_CELL_DOWN }, 53 { '-', TBL_CELL_HORIZ }, 54 { '_', TBL_CELL_HORIZ }, 55 { '=', TBL_CELL_DHORIZ } 56 }; 57 58 static int mods(struct tbl_node *, struct tbl_cell *, 59 int, const char *, int *); 60 static int cell(struct tbl_node *, struct tbl_row *, 61 int, const char *, int *); 62 static void row(struct tbl_node *, int, const char *, int *); 63 static struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *, 64 enum tbl_cellt, int vert); 65 66 67 static int 68 mods(struct tbl_node *tbl, struct tbl_cell *cp, 69 int ln, const char *p, int *pos) 70 { 71 char buf[5]; 72 int i; 73 74 /* Not all types accept modifiers. */ 75 76 switch (cp->pos) { 77 case TBL_CELL_DOWN: 78 /* FALLTHROUGH */ 79 case TBL_CELL_HORIZ: 80 /* FALLTHROUGH */ 81 case TBL_CELL_DHORIZ: 82 return(1); 83 default: 84 break; 85 } 86 87 mod: 88 /* 89 * XXX: since, at least for now, modifiers are non-conflicting 90 * (are separable by value, regardless of position), we let 91 * modifiers come in any order. The existing tbl doesn't let 92 * this happen. 93 */ 94 switch (p[*pos]) { 95 case '\0': 96 /* FALLTHROUGH */ 97 case ' ': 98 /* FALLTHROUGH */ 99 case '\t': 100 /* FALLTHROUGH */ 101 case ',': 102 /* FALLTHROUGH */ 103 case '.': 104 /* FALLTHROUGH */ 105 case '|': 106 return(1); 107 default: 108 break; 109 } 110 111 /* Throw away parenthesised expression. */ 112 113 if ('(' == p[*pos]) { 114 (*pos)++; 115 while (p[*pos] && ')' != p[*pos]) 116 (*pos)++; 117 if (')' == p[*pos]) { 118 (*pos)++; 119 goto mod; 120 } 121 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 122 ln, *pos, NULL); 123 return(0); 124 } 125 126 /* Parse numerical spacing from modifier string. */ 127 128 if (isdigit((unsigned char)p[*pos])) { 129 for (i = 0; i < 4; i++) { 130 if ( ! isdigit((unsigned char)p[*pos + i])) 131 break; 132 buf[i] = p[*pos + i]; 133 } 134 buf[i] = '\0'; 135 136 /* No greater than 4 digits. */ 137 138 if (4 == i) { 139 mandoc_msg(MANDOCERR_TBLLAYOUT, 140 tbl->parse, ln, *pos, NULL); 141 return(0); 142 } 143 144 *pos += i; 145 cp->spacing = (size_t)atoi(buf); 146 147 goto mod; 148 /* NOTREACHED */ 149 } 150 151 /* TODO: GNU has many more extensions. */ 152 153 switch (tolower((unsigned char)p[(*pos)++])) { 154 case 'z': 155 cp->flags |= TBL_CELL_WIGN; 156 goto mod; 157 case 'u': 158 cp->flags |= TBL_CELL_UP; 159 goto mod; 160 case 'e': 161 cp->flags |= TBL_CELL_EQUAL; 162 goto mod; 163 case 't': 164 cp->flags |= TBL_CELL_TALIGN; 165 goto mod; 166 case 'd': 167 cp->flags |= TBL_CELL_BALIGN; 168 goto mod; 169 case 'w': /* XXX for now, ignore minimal column width */ 170 goto mod; 171 case 'f': 172 break; 173 case 'r': 174 /* FALLTHROUGH */ 175 case 'b': 176 /* FALLTHROUGH */ 177 case 'i': 178 (*pos)--; 179 break; 180 default: 181 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 182 ln, *pos - 1, NULL); 183 return(0); 184 } 185 186 switch (tolower((unsigned char)p[(*pos)++])) { 187 case '3': 188 /* FALLTHROUGH */ 189 case 'b': 190 cp->flags |= TBL_CELL_BOLD; 191 goto mod; 192 case '2': 193 /* FALLTHROUGH */ 194 case 'i': 195 cp->flags |= TBL_CELL_ITALIC; 196 goto mod; 197 case '1': 198 /* FALLTHROUGH */ 199 case 'r': 200 goto mod; 201 default: 202 break; 203 } 204 205 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 206 ln, *pos - 1, NULL); 207 return(0); 208 } 209 210 static int 211 cell(struct tbl_node *tbl, struct tbl_row *rp, 212 int ln, const char *p, int *pos) 213 { 214 int vert, i; 215 enum tbl_cellt c; 216 217 /* Handle vertical lines. */ 218 219 for (vert = 0; '|' == p[*pos]; ++*pos) 220 vert++; 221 while (' ' == p[*pos]) 222 (*pos)++; 223 224 /* Handle trailing vertical lines */ 225 226 if ('.' == p[*pos] || '\0' == p[*pos]) { 227 rp->vert = vert; 228 return(1); 229 } 230 231 /* Parse the column position (`c', `l', `r', ...). */ 232 233 for (i = 0; i < KEYS_MAX; i++) 234 if (tolower((unsigned char)p[*pos]) == keys[i].name) 235 break; 236 237 if (KEYS_MAX == i) { 238 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 239 ln, *pos, NULL); 240 return(0); 241 } 242 243 c = keys[i].key; 244 245 /* 246 * If a span cell is found first, raise a warning and abort the 247 * parse. If a span cell is found and the last layout element 248 * isn't a "normal" layout, bail. 249 * 250 * FIXME: recover from this somehow? 251 */ 252 253 if (TBL_CELL_SPAN == c) { 254 if (NULL == rp->first) { 255 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 256 ln, *pos, NULL); 257 return(0); 258 } else if (rp->last) 259 switch (rp->last->pos) { 260 case TBL_CELL_HORIZ: 261 /* FALLTHROUGH */ 262 case TBL_CELL_DHORIZ: 263 mandoc_msg(MANDOCERR_TBLLAYOUT, 264 tbl->parse, ln, *pos, NULL); 265 return(0); 266 default: 267 break; 268 } 269 } 270 271 /* 272 * If a vertical spanner is found, we may not be in the first 273 * row. 274 */ 275 276 if (TBL_CELL_DOWN == c && rp == tbl->first_row) { 277 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL); 278 return(0); 279 } 280 281 (*pos)++; 282 283 /* Disallow adjacent spacers. */ 284 285 if (vert > 2) { 286 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL); 287 return(0); 288 } 289 290 /* Allocate cell then parse its modifiers. */ 291 292 return(mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos)); 293 } 294 295 static void 296 row(struct tbl_node *tbl, int ln, const char *p, int *pos) 297 { 298 struct tbl_row *rp; 299 300 row: /* 301 * EBNF describing this section: 302 * 303 * row ::= row_list [:space:]* [.]?[\n] 304 * row_list ::= [:space:]* row_elem row_tail 305 * row_tail ::= [:space:]*[,] row_list | 306 * epsilon 307 * row_elem ::= [\t\ ]*[:alpha:]+ 308 */ 309 310 rp = mandoc_calloc(1, sizeof(struct tbl_row)); 311 if (tbl->last_row) 312 tbl->last_row->next = rp; 313 else 314 tbl->first_row = rp; 315 tbl->last_row = rp; 316 317 cell: 318 while (isspace((unsigned char)p[*pos])) 319 (*pos)++; 320 321 /* Safely exit layout context. */ 322 323 if ('.' == p[*pos]) { 324 tbl->part = TBL_PART_DATA; 325 if (NULL == tbl->first_row) 326 mandoc_msg(MANDOCERR_TBLNOLAYOUT, 327 tbl->parse, ln, *pos, NULL); 328 (*pos)++; 329 return; 330 } 331 332 /* End (and possibly restart) a row. */ 333 334 if (',' == p[*pos]) { 335 (*pos)++; 336 goto row; 337 } else if ('\0' == p[*pos]) 338 return; 339 340 if ( ! cell(tbl, rp, ln, p, pos)) 341 return; 342 343 goto cell; 344 /* NOTREACHED */ 345 } 346 347 int 348 tbl_layout(struct tbl_node *tbl, int ln, const char *p) 349 { 350 int pos; 351 352 pos = 0; 353 row(tbl, ln, p, &pos); 354 355 /* Always succeed. */ 356 return(1); 357 } 358 359 static struct tbl_cell * 360 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos, 361 int vert) 362 { 363 struct tbl_cell *p, *pp; 364 struct tbl_head *h, *hp; 365 366 p = mandoc_calloc(1, sizeof(struct tbl_cell)); 367 368 if (NULL != (pp = rp->last)) { 369 pp->next = p; 370 h = pp->head->next; 371 } else { 372 rp->first = p; 373 h = tbl->first_head; 374 } 375 rp->last = p; 376 377 p->pos = pos; 378 p->vert = vert; 379 380 /* Re-use header. */ 381 382 if (h) { 383 p->head = h; 384 return(p); 385 } 386 387 hp = mandoc_calloc(1, sizeof(struct tbl_head)); 388 hp->ident = tbl->opts.cols++; 389 hp->vert = vert; 390 391 if (tbl->last_head) { 392 hp->prev = tbl->last_head; 393 tbl->last_head->next = hp; 394 } else 395 tbl->first_head = hp; 396 tbl->last_head = hp; 397 398 p->head = hp; 399 return(p); 400 } 401