1 /* $Id: tbl_layout.c,v 1.41 2015/10/12 00:08:16 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 case 'v': 119 if (p[*pos] == '-' || p[*pos] == '+') 120 (*pos)++; 121 while (isdigit((unsigned char)p[*pos])) 122 (*pos)++; 123 goto mod; 124 case 't': 125 cp->flags |= TBL_CELL_TALIGN; 126 goto mod; 127 case 'u': 128 cp->flags |= TBL_CELL_UP; 129 goto mod; 130 case 'w': /* XXX for now, ignore minimal column width */ 131 goto mod; 132 case 'x': 133 cp->flags |= TBL_CELL_WMAX; 134 goto mod; 135 case 'z': 136 cp->flags |= TBL_CELL_WIGN; 137 goto mod; 138 case '|': 139 if (cp->vert < 2) 140 cp->vert++; 141 else 142 mandoc_msg(MANDOCERR_TBLLAYOUT_VERT, 143 tbl->parse, ln, *pos - 1, NULL); 144 goto mod; 145 default: 146 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse, 147 ln, *pos - 1, "%c", p[*pos - 1]); 148 goto mod; 149 } 150 151 /* Ignore parenthised font names for now. */ 152 153 if (p[*pos] == '(') 154 goto mod; 155 156 /* Support only one-character font-names for now. */ 157 158 if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) { 159 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse, 160 ln, *pos, "TS %s", p + *pos - 1); 161 if (p[*pos] != '\0') 162 (*pos)++; 163 if (p[*pos] != '\0') 164 (*pos)++; 165 goto mod; 166 } 167 168 switch (p[(*pos)++]) { 169 case '3': 170 case 'B': 171 cp->flags |= TBL_CELL_BOLD; 172 goto mod; 173 case '2': 174 case 'I': 175 cp->flags |= TBL_CELL_ITALIC; 176 goto mod; 177 case '1': 178 case 'R': 179 goto mod; 180 default: 181 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse, 182 ln, *pos - 1, "TS f%c", p[*pos - 1]); 183 goto mod; 184 } 185 } 186 187 static void 188 cell(struct tbl_node *tbl, struct tbl_row *rp, 189 int ln, const char *p, int *pos) 190 { 191 int i; 192 enum tbl_cellt c; 193 194 /* Handle leading vertical lines */ 195 196 while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') { 197 if (p[*pos] == '|') { 198 if (rp->vert < 2) 199 rp->vert++; 200 else 201 mandoc_msg(MANDOCERR_TBLLAYOUT_VERT, 202 tbl->parse, ln, *pos, NULL); 203 } 204 (*pos)++; 205 } 206 207 again: 208 while (p[*pos] == ' ' || p[*pos] == '\t') 209 (*pos)++; 210 211 if (p[*pos] == '.' || p[*pos] == '\0') 212 return; 213 214 /* Parse the column position (`c', `l', `r', ...). */ 215 216 for (i = 0; i < KEYS_MAX; i++) 217 if (tolower((unsigned char)p[*pos]) == keys[i].name) 218 break; 219 220 if (i == KEYS_MAX) { 221 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse, 222 ln, *pos, "%c", p[*pos]); 223 (*pos)++; 224 goto again; 225 } 226 c = keys[i].key; 227 228 /* Special cases of spanners. */ 229 230 if (c == TBL_CELL_SPAN) { 231 if (rp->last == NULL) 232 mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN, 233 tbl->parse, ln, *pos, NULL); 234 else if (rp->last->pos == TBL_CELL_HORIZ || 235 rp->last->pos == TBL_CELL_DHORIZ) 236 c = rp->last->pos; 237 } else if (c == TBL_CELL_DOWN && rp == tbl->first_row) 238 mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN, 239 tbl->parse, ln, *pos, NULL); 240 241 (*pos)++; 242 243 /* Allocate cell then parse its modifiers. */ 244 245 mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos); 246 } 247 248 void 249 tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos) 250 { 251 struct tbl_row *rp; 252 253 rp = NULL; 254 for (;;) { 255 /* Skip whitespace before and after each cell. */ 256 257 while (p[pos] == ' ' || p[pos] == '\t') 258 pos++; 259 260 switch (p[pos]) { 261 case ',': /* Next row on this input line. */ 262 pos++; 263 rp = NULL; 264 continue; 265 case '\0': /* Next row on next input line. */ 266 return; 267 case '.': /* End of layout. */ 268 pos++; 269 tbl->part = TBL_PART_DATA; 270 271 /* 272 * When the layout is completely empty, 273 * default to one left-justified column. 274 */ 275 276 if (tbl->first_row == NULL) { 277 tbl->first_row = tbl->last_row = 278 mandoc_calloc(1, sizeof(*rp)); 279 } 280 if (tbl->first_row->first == NULL) { 281 mandoc_msg(MANDOCERR_TBLLAYOUT_NONE, 282 tbl->parse, ln, pos, NULL); 283 cell_alloc(tbl, tbl->first_row, 284 TBL_CELL_LEFT); 285 return; 286 } 287 288 /* 289 * Search for the widest line 290 * along the left and right margins. 291 */ 292 293 for (rp = tbl->first_row; rp; rp = rp->next) { 294 if (tbl->opts.lvert < rp->vert) 295 tbl->opts.lvert = rp->vert; 296 if (rp->last != NULL && 297 rp->last->col + 1 == tbl->opts.cols && 298 tbl->opts.rvert < rp->last->vert) 299 tbl->opts.rvert = rp->last->vert; 300 301 /* If the last line is empty, drop it. */ 302 303 if (rp->next != NULL && 304 rp->next->first == NULL) { 305 free(rp->next); 306 rp->next = NULL; 307 tbl->last_row = rp; 308 } 309 } 310 return; 311 default: /* Cell. */ 312 break; 313 } 314 315 /* 316 * If the last line had at least one cell, 317 * start a new one; otherwise, continue it. 318 */ 319 320 if (rp == NULL) { 321 if (tbl->last_row == NULL || 322 tbl->last_row->first != NULL) { 323 rp = mandoc_calloc(1, sizeof(*rp)); 324 if (tbl->last_row) 325 tbl->last_row->next = rp; 326 else 327 tbl->first_row = rp; 328 tbl->last_row = rp; 329 } else 330 rp = tbl->last_row; 331 } 332 cell(tbl, rp, ln, p, &pos); 333 } 334 } 335 336 static struct tbl_cell * 337 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) 338 { 339 struct tbl_cell *p, *pp; 340 341 p = mandoc_calloc(1, sizeof(*p)); 342 p->pos = pos; 343 344 if ((pp = rp->last) != NULL) { 345 pp->next = p; 346 p->col = pp->col + 1; 347 } else 348 rp->first = p; 349 rp->last = p; 350 351 if (tbl->opts.cols <= p->col) 352 tbl->opts.cols = p->col + 1; 353 354 return p; 355 } 356