1 /* $Id: tbl_layout.c,v 1.38 2015/02/10 11:03:13 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2012, 2014, 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 <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 static const struct tbl_phrase keys[] = { 38 { 'c', TBL_CELL_CENTRE }, 39 { 'r', TBL_CELL_RIGHT }, 40 { 'l', TBL_CELL_LEFT }, 41 { 'n', TBL_CELL_NUMBER }, 42 { 's', TBL_CELL_SPAN }, 43 { 'a', TBL_CELL_LONG }, 44 { '^', TBL_CELL_DOWN }, 45 { '-', TBL_CELL_HORIZ }, 46 { '_', TBL_CELL_HORIZ }, 47 { '=', TBL_CELL_DHORIZ } 48 }; 49 50 #define KEYS_MAX ((int)(sizeof(keys)/sizeof(keys[0]))) 51 52 static void mods(struct tbl_node *, struct tbl_cell *, 53 int, const char *, int *); 54 static void cell(struct tbl_node *, struct tbl_row *, 55 int, const char *, int *); 56 static struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *, 57 enum tbl_cellt); 58 59 60 static void 61 mods(struct tbl_node *tbl, struct tbl_cell *cp, 62 int ln, const char *p, int *pos) 63 { 64 char *endptr; 65 66 mod: 67 while (p[*pos] == ' ' || p[*pos] == '\t') 68 (*pos)++; 69 70 /* Row delimiters and cell specifiers end modifier lists. */ 71 72 if (strchr(".,-=^_ACLNRSaclnrs", p[*pos]) != NULL) 73 return; 74 75 /* Throw away parenthesised expression. */ 76 77 if ('(' == p[*pos]) { 78 (*pos)++; 79 while (p[*pos] && ')' != p[*pos]) 80 (*pos)++; 81 if (')' == p[*pos]) { 82 (*pos)++; 83 goto mod; 84 } 85 mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, tbl->parse, 86 ln, *pos, NULL); 87 return; 88 } 89 90 /* Parse numerical spacing from modifier string. */ 91 92 if (isdigit((unsigned char)p[*pos])) { 93 cp->spacing = strtoull(p + *pos, &endptr, 10); 94 *pos = endptr - p; 95 goto mod; 96 } 97 98 switch (tolower((unsigned char)p[(*pos)++])) { 99 case 'b': 100 cp->flags |= TBL_CELL_BOLD; 101 goto mod; 102 case 'd': 103 cp->flags |= TBL_CELL_BALIGN; 104 goto mod; 105 case 'e': 106 cp->flags |= TBL_CELL_EQUAL; 107 goto mod; 108 case 'f': 109 break; 110 case 'i': 111 cp->flags |= TBL_CELL_ITALIC; 112 goto mod; 113 case 'm': 114 mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, tbl->parse, 115 ln, *pos, "m"); 116 goto mod; 117 case 'p': 118 /* FALLTHROUGH */ 119 case 'v': 120 if (p[*pos] == '-' || p[*pos] == '+') 121 (*pos)++; 122 while (isdigit((unsigned char)p[*pos])) 123 (*pos)++; 124 goto mod; 125 case 't': 126 cp->flags |= TBL_CELL_TALIGN; 127 goto mod; 128 case 'u': 129 cp->flags |= TBL_CELL_UP; 130 goto mod; 131 case 'w': /* XXX for now, ignore minimal column width */ 132 goto mod; 133 case 'x': 134 cp->flags |= TBL_CELL_WMAX; 135 goto mod; 136 case 'z': 137 cp->flags |= TBL_CELL_WIGN; 138 goto mod; 139 case '|': 140 if (cp->vert < 2) 141 cp->vert++; 142 else 143 mandoc_msg(MANDOCERR_TBLLAYOUT_VERT, 144 tbl->parse, ln, *pos - 1, NULL); 145 goto mod; 146 default: 147 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse, 148 ln, *pos - 1, "%c", p[*pos - 1]); 149 goto mod; 150 } 151 152 /* Ignore parenthised font names for now. */ 153 154 if (p[*pos] == '(') 155 goto mod; 156 157 /* Support only one-character font-names for now. */ 158 159 if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) { 160 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse, 161 ln, *pos, "TS %s", p + *pos - 1); 162 if (p[*pos] != '\0') 163 (*pos)++; 164 if (p[*pos] != '\0') 165 (*pos)++; 166 goto mod; 167 } 168 169 switch (p[(*pos)++]) { 170 case '3': 171 /* FALLTHROUGH */ 172 case 'B': 173 cp->flags |= TBL_CELL_BOLD; 174 goto mod; 175 case '2': 176 /* FALLTHROUGH */ 177 case 'I': 178 cp->flags |= TBL_CELL_ITALIC; 179 goto mod; 180 case '1': 181 /* FALLTHROUGH */ 182 case 'R': 183 goto mod; 184 default: 185 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse, 186 ln, *pos - 1, "TS f%c", p[*pos - 1]); 187 goto mod; 188 } 189 } 190 191 static void 192 cell(struct tbl_node *tbl, struct tbl_row *rp, 193 int ln, const char *p, int *pos) 194 { 195 int i; 196 enum tbl_cellt c; 197 198 /* Handle leading vertical lines */ 199 200 while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') { 201 if (p[*pos] == '|') { 202 if (rp->vert < 2) 203 rp->vert++; 204 else 205 mandoc_msg(MANDOCERR_TBLLAYOUT_VERT, 206 tbl->parse, ln, *pos, NULL); 207 } 208 (*pos)++; 209 } 210 211 again: 212 while (p[*pos] == ' ' || p[*pos] == '\t') 213 (*pos)++; 214 215 if (p[*pos] == '.' || p[*pos] == '\0') 216 return; 217 218 /* Parse the column position (`c', `l', `r', ...). */ 219 220 for (i = 0; i < KEYS_MAX; i++) 221 if (tolower((unsigned char)p[*pos]) == keys[i].name) 222 break; 223 224 if (i == KEYS_MAX) { 225 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse, 226 ln, *pos, "%c", p[*pos]); 227 (*pos)++; 228 goto again; 229 } 230 c = keys[i].key; 231 232 /* Special cases of spanners. */ 233 234 if (c == TBL_CELL_SPAN) { 235 if (rp->last == NULL) 236 mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN, 237 tbl->parse, ln, *pos, NULL); 238 else if (rp->last->pos == TBL_CELL_HORIZ || 239 rp->last->pos == TBL_CELL_DHORIZ) 240 c = rp->last->pos; 241 } else if (c == TBL_CELL_DOWN && rp == tbl->first_row) 242 mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN, 243 tbl->parse, ln, *pos, NULL); 244 245 (*pos)++; 246 247 /* Allocate cell then parse its modifiers. */ 248 249 mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos); 250 } 251 252 void 253 tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos) 254 { 255 struct tbl_row *rp; 256 257 rp = NULL; 258 for (;;) { 259 /* Skip whitespace before and after each cell. */ 260 261 while (p[pos] == ' ' || p[pos] == '\t') 262 pos++; 263 264 switch (p[pos]) { 265 case ',': /* Next row on this input line. */ 266 pos++; 267 rp = NULL; 268 continue; 269 case '\0': /* Next row on next input line. */ 270 return; 271 case '.': /* End of layout. */ 272 pos++; 273 tbl->part = TBL_PART_DATA; 274 275 /* 276 * When the layout is completely empty, 277 * default to one left-justified column. 278 */ 279 280 if (tbl->first_row == NULL) { 281 tbl->first_row = tbl->last_row = 282 mandoc_calloc(1, sizeof(*rp)); 283 } 284 if (tbl->first_row->first == NULL) { 285 mandoc_msg(MANDOCERR_TBLLAYOUT_NONE, 286 tbl->parse, ln, pos, NULL); 287 cell_alloc(tbl, tbl->first_row, 288 TBL_CELL_LEFT); 289 return; 290 } 291 292 /* 293 * Search for the widest line 294 * along the left and right margins. 295 */ 296 297 for (rp = tbl->first_row; rp; rp = rp->next) { 298 if (tbl->opts.lvert < rp->vert) 299 tbl->opts.lvert = rp->vert; 300 if (rp->last != NULL && 301 rp->last->col + 1 == tbl->opts.cols && 302 tbl->opts.rvert < rp->last->vert) 303 tbl->opts.rvert = rp->last->vert; 304 305 /* If the last line is empty, drop it. */ 306 307 if (rp->next != NULL && 308 rp->next->first == NULL) { 309 free(rp->next); 310 rp->next = NULL; 311 } 312 } 313 return; 314 default: /* Cell. */ 315 break; 316 } 317 318 /* 319 * If the last line had at least one cell, 320 * start a new one; otherwise, continue it. 321 */ 322 323 if (rp == NULL) { 324 if (tbl->last_row == NULL || 325 tbl->last_row->first != NULL) { 326 rp = mandoc_calloc(1, sizeof(*rp)); 327 if (tbl->last_row) 328 tbl->last_row->next = rp; 329 else 330 tbl->first_row = rp; 331 tbl->last_row = rp; 332 } else 333 rp = tbl->last_row; 334 } 335 cell(tbl, rp, ln, p, &pos); 336 } 337 } 338 339 static struct tbl_cell * 340 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) 341 { 342 struct tbl_cell *p, *pp; 343 344 p = mandoc_calloc(1, sizeof(*p)); 345 p->pos = pos; 346 347 if ((pp = rp->last) != NULL) { 348 pp->next = p; 349 p->col = pp->col + 1; 350 } else 351 rp->first = p; 352 rp->last = p; 353 354 if (tbl->opts.cols <= p->col) 355 tbl->opts.cols = p->col + 1; 356 357 return(p); 358 } 359