1*95c635efSGarrett D'Amore /* $Id: tbl_layout.c,v 1.22 2011/09/18 14:14:15 schwarze Exp $ */ 2*95c635efSGarrett D'Amore /* 3*95c635efSGarrett D'Amore * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4*95c635efSGarrett D'Amore * 5*95c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 6*95c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 7*95c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 8*95c635efSGarrett D'Amore * 9*95c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10*95c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11*95c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12*95c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13*95c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14*95c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15*95c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16*95c635efSGarrett D'Amore */ 17*95c635efSGarrett D'Amore #ifdef HAVE_CONFIG_H 18*95c635efSGarrett D'Amore #include "config.h" 19*95c635efSGarrett D'Amore #endif 20*95c635efSGarrett D'Amore 21*95c635efSGarrett D'Amore #include <assert.h> 22*95c635efSGarrett D'Amore #include <ctype.h> 23*95c635efSGarrett D'Amore #include <stdlib.h> 24*95c635efSGarrett D'Amore #include <string.h> 25*95c635efSGarrett D'Amore #include <time.h> 26*95c635efSGarrett D'Amore 27*95c635efSGarrett D'Amore #include "mandoc.h" 28*95c635efSGarrett D'Amore #include "libmandoc.h" 29*95c635efSGarrett D'Amore #include "libroff.h" 30*95c635efSGarrett D'Amore 31*95c635efSGarrett D'Amore struct tbl_phrase { 32*95c635efSGarrett D'Amore char name; 33*95c635efSGarrett D'Amore enum tbl_cellt key; 34*95c635efSGarrett D'Amore }; 35*95c635efSGarrett D'Amore 36*95c635efSGarrett D'Amore /* 37*95c635efSGarrett D'Amore * FIXME: we can make this parse a lot nicer by, when an error is 38*95c635efSGarrett D'Amore * encountered in a layout key, bailing to the next key (i.e. to the 39*95c635efSGarrett D'Amore * next whitespace then continuing). 40*95c635efSGarrett D'Amore */ 41*95c635efSGarrett D'Amore 42*95c635efSGarrett D'Amore #define KEYS_MAX 11 43*95c635efSGarrett D'Amore 44*95c635efSGarrett D'Amore static const struct tbl_phrase keys[KEYS_MAX] = { 45*95c635efSGarrett D'Amore { 'c', TBL_CELL_CENTRE }, 46*95c635efSGarrett D'Amore { 'r', TBL_CELL_RIGHT }, 47*95c635efSGarrett D'Amore { 'l', TBL_CELL_LEFT }, 48*95c635efSGarrett D'Amore { 'n', TBL_CELL_NUMBER }, 49*95c635efSGarrett D'Amore { 's', TBL_CELL_SPAN }, 50*95c635efSGarrett D'Amore { 'a', TBL_CELL_LONG }, 51*95c635efSGarrett D'Amore { '^', TBL_CELL_DOWN }, 52*95c635efSGarrett D'Amore { '-', TBL_CELL_HORIZ }, 53*95c635efSGarrett D'Amore { '_', TBL_CELL_HORIZ }, 54*95c635efSGarrett D'Amore { '=', TBL_CELL_DHORIZ }, 55*95c635efSGarrett D'Amore { '|', TBL_CELL_VERT } 56*95c635efSGarrett D'Amore }; 57*95c635efSGarrett D'Amore 58*95c635efSGarrett D'Amore static int mods(struct tbl_node *, struct tbl_cell *, 59*95c635efSGarrett D'Amore int, const char *, int *); 60*95c635efSGarrett D'Amore static int cell(struct tbl_node *, struct tbl_row *, 61*95c635efSGarrett D'Amore int, const char *, int *); 62*95c635efSGarrett D'Amore static void row(struct tbl_node *, int, const char *, int *); 63*95c635efSGarrett D'Amore static struct tbl_cell *cell_alloc(struct tbl_node *, 64*95c635efSGarrett D'Amore struct tbl_row *, enum tbl_cellt); 65*95c635efSGarrett D'Amore static void head_adjust(const struct tbl_cell *, 66*95c635efSGarrett D'Amore struct tbl_head *); 67*95c635efSGarrett D'Amore 68*95c635efSGarrett D'Amore static int 69*95c635efSGarrett D'Amore mods(struct tbl_node *tbl, struct tbl_cell *cp, 70*95c635efSGarrett D'Amore int ln, const char *p, int *pos) 71*95c635efSGarrett D'Amore { 72*95c635efSGarrett D'Amore char buf[5]; 73*95c635efSGarrett D'Amore int i; 74*95c635efSGarrett D'Amore 75*95c635efSGarrett D'Amore /* Not all types accept modifiers. */ 76*95c635efSGarrett D'Amore 77*95c635efSGarrett D'Amore switch (cp->pos) { 78*95c635efSGarrett D'Amore case (TBL_CELL_DOWN): 79*95c635efSGarrett D'Amore /* FALLTHROUGH */ 80*95c635efSGarrett D'Amore case (TBL_CELL_HORIZ): 81*95c635efSGarrett D'Amore /* FALLTHROUGH */ 82*95c635efSGarrett D'Amore case (TBL_CELL_DHORIZ): 83*95c635efSGarrett D'Amore /* FALLTHROUGH */ 84*95c635efSGarrett D'Amore case (TBL_CELL_VERT): 85*95c635efSGarrett D'Amore /* FALLTHROUGH */ 86*95c635efSGarrett D'Amore case (TBL_CELL_DVERT): 87*95c635efSGarrett D'Amore return(1); 88*95c635efSGarrett D'Amore default: 89*95c635efSGarrett D'Amore break; 90*95c635efSGarrett D'Amore } 91*95c635efSGarrett D'Amore 92*95c635efSGarrett D'Amore mod: 93*95c635efSGarrett D'Amore /* 94*95c635efSGarrett D'Amore * XXX: since, at least for now, modifiers are non-conflicting 95*95c635efSGarrett D'Amore * (are separable by value, regardless of position), we let 96*95c635efSGarrett D'Amore * modifiers come in any order. The existing tbl doesn't let 97*95c635efSGarrett D'Amore * this happen. 98*95c635efSGarrett D'Amore */ 99*95c635efSGarrett D'Amore switch (p[*pos]) { 100*95c635efSGarrett D'Amore case ('\0'): 101*95c635efSGarrett D'Amore /* FALLTHROUGH */ 102*95c635efSGarrett D'Amore case (' '): 103*95c635efSGarrett D'Amore /* FALLTHROUGH */ 104*95c635efSGarrett D'Amore case ('\t'): 105*95c635efSGarrett D'Amore /* FALLTHROUGH */ 106*95c635efSGarrett D'Amore case (','): 107*95c635efSGarrett D'Amore /* FALLTHROUGH */ 108*95c635efSGarrett D'Amore case ('.'): 109*95c635efSGarrett D'Amore return(1); 110*95c635efSGarrett D'Amore default: 111*95c635efSGarrett D'Amore break; 112*95c635efSGarrett D'Amore } 113*95c635efSGarrett D'Amore 114*95c635efSGarrett D'Amore /* Throw away parenthesised expression. */ 115*95c635efSGarrett D'Amore 116*95c635efSGarrett D'Amore if ('(' == p[*pos]) { 117*95c635efSGarrett D'Amore (*pos)++; 118*95c635efSGarrett D'Amore while (p[*pos] && ')' != p[*pos]) 119*95c635efSGarrett D'Amore (*pos)++; 120*95c635efSGarrett D'Amore if (')' == p[*pos]) { 121*95c635efSGarrett D'Amore (*pos)++; 122*95c635efSGarrett D'Amore goto mod; 123*95c635efSGarrett D'Amore } 124*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLLAYOUT, 125*95c635efSGarrett D'Amore tbl->parse, ln, *pos, NULL); 126*95c635efSGarrett D'Amore return(0); 127*95c635efSGarrett D'Amore } 128*95c635efSGarrett D'Amore 129*95c635efSGarrett D'Amore /* Parse numerical spacing from modifier string. */ 130*95c635efSGarrett D'Amore 131*95c635efSGarrett D'Amore if (isdigit((unsigned char)p[*pos])) { 132*95c635efSGarrett D'Amore for (i = 0; i < 4; i++) { 133*95c635efSGarrett D'Amore if ( ! isdigit((unsigned char)p[*pos + i])) 134*95c635efSGarrett D'Amore break; 135*95c635efSGarrett D'Amore buf[i] = p[*pos + i]; 136*95c635efSGarrett D'Amore } 137*95c635efSGarrett D'Amore buf[i] = '\0'; 138*95c635efSGarrett D'Amore 139*95c635efSGarrett D'Amore /* No greater than 4 digits. */ 140*95c635efSGarrett D'Amore 141*95c635efSGarrett D'Amore if (4 == i) { 142*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 143*95c635efSGarrett D'Amore ln, *pos, NULL); 144*95c635efSGarrett D'Amore return(0); 145*95c635efSGarrett D'Amore } 146*95c635efSGarrett D'Amore 147*95c635efSGarrett D'Amore *pos += i; 148*95c635efSGarrett D'Amore cp->spacing = (size_t)atoi(buf); 149*95c635efSGarrett D'Amore 150*95c635efSGarrett D'Amore goto mod; 151*95c635efSGarrett D'Amore /* NOTREACHED */ 152*95c635efSGarrett D'Amore } 153*95c635efSGarrett D'Amore 154*95c635efSGarrett D'Amore /* TODO: GNU has many more extensions. */ 155*95c635efSGarrett D'Amore 156*95c635efSGarrett D'Amore switch (tolower((unsigned char)p[(*pos)++])) { 157*95c635efSGarrett D'Amore case ('z'): 158*95c635efSGarrett D'Amore cp->flags |= TBL_CELL_WIGN; 159*95c635efSGarrett D'Amore goto mod; 160*95c635efSGarrett D'Amore case ('u'): 161*95c635efSGarrett D'Amore cp->flags |= TBL_CELL_UP; 162*95c635efSGarrett D'Amore goto mod; 163*95c635efSGarrett D'Amore case ('e'): 164*95c635efSGarrett D'Amore cp->flags |= TBL_CELL_EQUAL; 165*95c635efSGarrett D'Amore goto mod; 166*95c635efSGarrett D'Amore case ('t'): 167*95c635efSGarrett D'Amore cp->flags |= TBL_CELL_TALIGN; 168*95c635efSGarrett D'Amore goto mod; 169*95c635efSGarrett D'Amore case ('d'): 170*95c635efSGarrett D'Amore cp->flags |= TBL_CELL_BALIGN; 171*95c635efSGarrett D'Amore goto mod; 172*95c635efSGarrett D'Amore case ('w'): /* XXX for now, ignore minimal column width */ 173*95c635efSGarrett D'Amore goto mod; 174*95c635efSGarrett D'Amore case ('f'): 175*95c635efSGarrett D'Amore break; 176*95c635efSGarrett D'Amore case ('r'): 177*95c635efSGarrett D'Amore /* FALLTHROUGH */ 178*95c635efSGarrett D'Amore case ('b'): 179*95c635efSGarrett D'Amore /* FALLTHROUGH */ 180*95c635efSGarrett D'Amore case ('i'): 181*95c635efSGarrett D'Amore (*pos)--; 182*95c635efSGarrett D'Amore break; 183*95c635efSGarrett D'Amore default: 184*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 185*95c635efSGarrett D'Amore ln, *pos - 1, NULL); 186*95c635efSGarrett D'Amore return(0); 187*95c635efSGarrett D'Amore } 188*95c635efSGarrett D'Amore 189*95c635efSGarrett D'Amore switch (tolower((unsigned char)p[(*pos)++])) { 190*95c635efSGarrett D'Amore case ('3'): 191*95c635efSGarrett D'Amore /* FALLTHROUGH */ 192*95c635efSGarrett D'Amore case ('b'): 193*95c635efSGarrett D'Amore cp->flags |= TBL_CELL_BOLD; 194*95c635efSGarrett D'Amore goto mod; 195*95c635efSGarrett D'Amore case ('2'): 196*95c635efSGarrett D'Amore /* FALLTHROUGH */ 197*95c635efSGarrett D'Amore case ('i'): 198*95c635efSGarrett D'Amore cp->flags |= TBL_CELL_ITALIC; 199*95c635efSGarrett D'Amore goto mod; 200*95c635efSGarrett D'Amore case ('1'): 201*95c635efSGarrett D'Amore /* FALLTHROUGH */ 202*95c635efSGarrett D'Amore case ('r'): 203*95c635efSGarrett D'Amore goto mod; 204*95c635efSGarrett D'Amore default: 205*95c635efSGarrett D'Amore break; 206*95c635efSGarrett D'Amore } 207*95c635efSGarrett D'Amore 208*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 209*95c635efSGarrett D'Amore ln, *pos - 1, NULL); 210*95c635efSGarrett D'Amore return(0); 211*95c635efSGarrett D'Amore } 212*95c635efSGarrett D'Amore 213*95c635efSGarrett D'Amore static int 214*95c635efSGarrett D'Amore cell(struct tbl_node *tbl, struct tbl_row *rp, 215*95c635efSGarrett D'Amore int ln, const char *p, int *pos) 216*95c635efSGarrett D'Amore { 217*95c635efSGarrett D'Amore int i; 218*95c635efSGarrett D'Amore enum tbl_cellt c; 219*95c635efSGarrett D'Amore 220*95c635efSGarrett D'Amore /* Parse the column position (`r', `R', `|', ...). */ 221*95c635efSGarrett D'Amore 222*95c635efSGarrett D'Amore for (i = 0; i < KEYS_MAX; i++) 223*95c635efSGarrett D'Amore if (tolower((unsigned char)p[*pos]) == keys[i].name) 224*95c635efSGarrett D'Amore break; 225*95c635efSGarrett D'Amore 226*95c635efSGarrett D'Amore if (KEYS_MAX == i) { 227*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 228*95c635efSGarrett D'Amore ln, *pos, NULL); 229*95c635efSGarrett D'Amore return(0); 230*95c635efSGarrett D'Amore } 231*95c635efSGarrett D'Amore 232*95c635efSGarrett D'Amore c = keys[i].key; 233*95c635efSGarrett D'Amore 234*95c635efSGarrett D'Amore /* 235*95c635efSGarrett D'Amore * If a span cell is found first, raise a warning and abort the 236*95c635efSGarrett D'Amore * parse. If a span cell is found and the last layout element 237*95c635efSGarrett D'Amore * isn't a "normal" layout, bail. 238*95c635efSGarrett D'Amore * 239*95c635efSGarrett D'Amore * FIXME: recover from this somehow? 240*95c635efSGarrett D'Amore */ 241*95c635efSGarrett D'Amore 242*95c635efSGarrett D'Amore if (TBL_CELL_SPAN == c) { 243*95c635efSGarrett D'Amore if (NULL == rp->first) { 244*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 245*95c635efSGarrett D'Amore ln, *pos, NULL); 246*95c635efSGarrett D'Amore return(0); 247*95c635efSGarrett D'Amore } else if (rp->last) 248*95c635efSGarrett D'Amore switch (rp->last->pos) { 249*95c635efSGarrett D'Amore case (TBL_CELL_VERT): 250*95c635efSGarrett D'Amore case (TBL_CELL_DVERT): 251*95c635efSGarrett D'Amore case (TBL_CELL_HORIZ): 252*95c635efSGarrett D'Amore case (TBL_CELL_DHORIZ): 253*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 254*95c635efSGarrett D'Amore ln, *pos, NULL); 255*95c635efSGarrett D'Amore return(0); 256*95c635efSGarrett D'Amore default: 257*95c635efSGarrett D'Amore break; 258*95c635efSGarrett D'Amore } 259*95c635efSGarrett D'Amore } 260*95c635efSGarrett D'Amore 261*95c635efSGarrett D'Amore /* 262*95c635efSGarrett D'Amore * If a vertical spanner is found, we may not be in the first 263*95c635efSGarrett D'Amore * row. 264*95c635efSGarrett D'Amore */ 265*95c635efSGarrett D'Amore 266*95c635efSGarrett D'Amore if (TBL_CELL_DOWN == c && rp == tbl->first_row) { 267*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL); 268*95c635efSGarrett D'Amore return(0); 269*95c635efSGarrett D'Amore } 270*95c635efSGarrett D'Amore 271*95c635efSGarrett D'Amore (*pos)++; 272*95c635efSGarrett D'Amore 273*95c635efSGarrett D'Amore /* Extra check for the double-vertical. */ 274*95c635efSGarrett D'Amore 275*95c635efSGarrett D'Amore if (TBL_CELL_VERT == c && '|' == p[*pos]) { 276*95c635efSGarrett D'Amore (*pos)++; 277*95c635efSGarrett D'Amore c = TBL_CELL_DVERT; 278*95c635efSGarrett D'Amore } 279*95c635efSGarrett D'Amore 280*95c635efSGarrett D'Amore /* Disallow adjacent spacers. */ 281*95c635efSGarrett D'Amore 282*95c635efSGarrett D'Amore if (rp->last && (TBL_CELL_VERT == c || TBL_CELL_DVERT == c) && 283*95c635efSGarrett D'Amore (TBL_CELL_VERT == rp->last->pos || 284*95c635efSGarrett D'Amore TBL_CELL_DVERT == rp->last->pos)) { 285*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL); 286*95c635efSGarrett D'Amore return(0); 287*95c635efSGarrett D'Amore } 288*95c635efSGarrett D'Amore 289*95c635efSGarrett D'Amore /* Allocate cell then parse its modifiers. */ 290*95c635efSGarrett D'Amore 291*95c635efSGarrett D'Amore return(mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos)); 292*95c635efSGarrett D'Amore } 293*95c635efSGarrett D'Amore 294*95c635efSGarrett D'Amore 295*95c635efSGarrett D'Amore static void 296*95c635efSGarrett D'Amore row(struct tbl_node *tbl, int ln, const char *p, int *pos) 297*95c635efSGarrett D'Amore { 298*95c635efSGarrett D'Amore struct tbl_row *rp; 299*95c635efSGarrett D'Amore 300*95c635efSGarrett D'Amore row: /* 301*95c635efSGarrett D'Amore * EBNF describing this section: 302*95c635efSGarrett D'Amore * 303*95c635efSGarrett D'Amore * row ::= row_list [:space:]* [.]?[\n] 304*95c635efSGarrett D'Amore * row_list ::= [:space:]* row_elem row_tail 305*95c635efSGarrett D'Amore * row_tail ::= [:space:]*[,] row_list | 306*95c635efSGarrett D'Amore * epsilon 307*95c635efSGarrett D'Amore * row_elem ::= [\t\ ]*[:alpha:]+ 308*95c635efSGarrett D'Amore */ 309*95c635efSGarrett D'Amore 310*95c635efSGarrett D'Amore rp = mandoc_calloc(1, sizeof(struct tbl_row)); 311*95c635efSGarrett D'Amore if (tbl->last_row) { 312*95c635efSGarrett D'Amore tbl->last_row->next = rp; 313*95c635efSGarrett D'Amore tbl->last_row = rp; 314*95c635efSGarrett D'Amore } else 315*95c635efSGarrett D'Amore tbl->last_row = tbl->first_row = rp; 316*95c635efSGarrett D'Amore 317*95c635efSGarrett D'Amore cell: 318*95c635efSGarrett D'Amore while (isspace((unsigned char)p[*pos])) 319*95c635efSGarrett D'Amore (*pos)++; 320*95c635efSGarrett D'Amore 321*95c635efSGarrett D'Amore /* Safely exit layout context. */ 322*95c635efSGarrett D'Amore 323*95c635efSGarrett D'Amore if ('.' == p[*pos]) { 324*95c635efSGarrett D'Amore tbl->part = TBL_PART_DATA; 325*95c635efSGarrett D'Amore if (NULL == tbl->first_row) 326*95c635efSGarrett D'Amore mandoc_msg(MANDOCERR_TBLNOLAYOUT, tbl->parse, 327*95c635efSGarrett D'Amore ln, *pos, NULL); 328*95c635efSGarrett D'Amore (*pos)++; 329*95c635efSGarrett D'Amore return; 330*95c635efSGarrett D'Amore } 331*95c635efSGarrett D'Amore 332*95c635efSGarrett D'Amore /* End (and possibly restart) a row. */ 333*95c635efSGarrett D'Amore 334*95c635efSGarrett D'Amore if (',' == p[*pos]) { 335*95c635efSGarrett D'Amore (*pos)++; 336*95c635efSGarrett D'Amore goto row; 337*95c635efSGarrett D'Amore } else if ('\0' == p[*pos]) 338*95c635efSGarrett D'Amore return; 339*95c635efSGarrett D'Amore 340*95c635efSGarrett D'Amore if ( ! cell(tbl, rp, ln, p, pos)) 341*95c635efSGarrett D'Amore return; 342*95c635efSGarrett D'Amore 343*95c635efSGarrett D'Amore goto cell; 344*95c635efSGarrett D'Amore /* NOTREACHED */ 345*95c635efSGarrett D'Amore } 346*95c635efSGarrett D'Amore 347*95c635efSGarrett D'Amore int 348*95c635efSGarrett D'Amore tbl_layout(struct tbl_node *tbl, int ln, const char *p) 349*95c635efSGarrett D'Amore { 350*95c635efSGarrett D'Amore int pos; 351*95c635efSGarrett D'Amore 352*95c635efSGarrett D'Amore pos = 0; 353*95c635efSGarrett D'Amore row(tbl, ln, p, &pos); 354*95c635efSGarrett D'Amore 355*95c635efSGarrett D'Amore /* Always succeed. */ 356*95c635efSGarrett D'Amore return(1); 357*95c635efSGarrett D'Amore } 358*95c635efSGarrett D'Amore 359*95c635efSGarrett D'Amore static struct tbl_cell * 360*95c635efSGarrett D'Amore cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) 361*95c635efSGarrett D'Amore { 362*95c635efSGarrett D'Amore struct tbl_cell *p, *pp; 363*95c635efSGarrett D'Amore struct tbl_head *h, *hp; 364*95c635efSGarrett D'Amore 365*95c635efSGarrett D'Amore p = mandoc_calloc(1, sizeof(struct tbl_cell)); 366*95c635efSGarrett D'Amore 367*95c635efSGarrett D'Amore if (NULL != (pp = rp->last)) { 368*95c635efSGarrett D'Amore rp->last->next = p; 369*95c635efSGarrett D'Amore rp->last = p; 370*95c635efSGarrett D'Amore } else 371*95c635efSGarrett D'Amore rp->last = rp->first = p; 372*95c635efSGarrett D'Amore 373*95c635efSGarrett D'Amore p->pos = pos; 374*95c635efSGarrett D'Amore 375*95c635efSGarrett D'Amore /* 376*95c635efSGarrett D'Amore * This is a little bit complicated. Here we determine the 377*95c635efSGarrett D'Amore * header the corresponds to a cell. We add headers dynamically 378*95c635efSGarrett D'Amore * when need be or re-use them, otherwise. As an example, given 379*95c635efSGarrett D'Amore * the following: 380*95c635efSGarrett D'Amore * 381*95c635efSGarrett D'Amore * 1 c || l 382*95c635efSGarrett D'Amore * 2 | c | l 383*95c635efSGarrett D'Amore * 3 l l 384*95c635efSGarrett D'Amore * 3 || c | l |. 385*95c635efSGarrett D'Amore * 386*95c635efSGarrett D'Amore * We first add the new headers (as there are none) in (1); then 387*95c635efSGarrett D'Amore * in (2) we insert the first spanner (as it doesn't match up 388*95c635efSGarrett D'Amore * with the header); then we re-use the prior data headers, 389*95c635efSGarrett D'Amore * skipping over the spanners; then we re-use everything and add 390*95c635efSGarrett D'Amore * a last spanner. Note that VERT headers are made into DVERT 391*95c635efSGarrett D'Amore * ones. 392*95c635efSGarrett D'Amore */ 393*95c635efSGarrett D'Amore 394*95c635efSGarrett D'Amore h = pp ? pp->head->next : tbl->first_head; 395*95c635efSGarrett D'Amore 396*95c635efSGarrett D'Amore if (h) { 397*95c635efSGarrett D'Amore /* Re-use data header. */ 398*95c635efSGarrett D'Amore if (TBL_HEAD_DATA == h->pos && 399*95c635efSGarrett D'Amore (TBL_CELL_VERT != p->pos && 400*95c635efSGarrett D'Amore TBL_CELL_DVERT != p->pos)) { 401*95c635efSGarrett D'Amore p->head = h; 402*95c635efSGarrett D'Amore return(p); 403*95c635efSGarrett D'Amore } 404*95c635efSGarrett D'Amore 405*95c635efSGarrett D'Amore /* Re-use spanner header. */ 406*95c635efSGarrett D'Amore if (TBL_HEAD_DATA != h->pos && 407*95c635efSGarrett D'Amore (TBL_CELL_VERT == p->pos || 408*95c635efSGarrett D'Amore TBL_CELL_DVERT == p->pos)) { 409*95c635efSGarrett D'Amore head_adjust(p, h); 410*95c635efSGarrett D'Amore p->head = h; 411*95c635efSGarrett D'Amore return(p); 412*95c635efSGarrett D'Amore } 413*95c635efSGarrett D'Amore 414*95c635efSGarrett D'Amore /* Right-shift headers with a new spanner. */ 415*95c635efSGarrett D'Amore if (TBL_HEAD_DATA == h->pos && 416*95c635efSGarrett D'Amore (TBL_CELL_VERT == p->pos || 417*95c635efSGarrett D'Amore TBL_CELL_DVERT == p->pos)) { 418*95c635efSGarrett D'Amore hp = mandoc_calloc(1, sizeof(struct tbl_head)); 419*95c635efSGarrett D'Amore hp->ident = tbl->opts.cols++; 420*95c635efSGarrett D'Amore hp->prev = h->prev; 421*95c635efSGarrett D'Amore if (h->prev) 422*95c635efSGarrett D'Amore h->prev->next = hp; 423*95c635efSGarrett D'Amore if (h == tbl->first_head) 424*95c635efSGarrett D'Amore tbl->first_head = hp; 425*95c635efSGarrett D'Amore h->prev = hp; 426*95c635efSGarrett D'Amore hp->next = h; 427*95c635efSGarrett D'Amore head_adjust(p, hp); 428*95c635efSGarrett D'Amore p->head = hp; 429*95c635efSGarrett D'Amore return(p); 430*95c635efSGarrett D'Amore } 431*95c635efSGarrett D'Amore 432*95c635efSGarrett D'Amore if (NULL != (h = h->next)) { 433*95c635efSGarrett D'Amore head_adjust(p, h); 434*95c635efSGarrett D'Amore p->head = h; 435*95c635efSGarrett D'Amore return(p); 436*95c635efSGarrett D'Amore } 437*95c635efSGarrett D'Amore 438*95c635efSGarrett D'Amore /* Fall through to default case... */ 439*95c635efSGarrett D'Amore } 440*95c635efSGarrett D'Amore 441*95c635efSGarrett D'Amore hp = mandoc_calloc(1, sizeof(struct tbl_head)); 442*95c635efSGarrett D'Amore hp->ident = tbl->opts.cols++; 443*95c635efSGarrett D'Amore 444*95c635efSGarrett D'Amore if (tbl->last_head) { 445*95c635efSGarrett D'Amore hp->prev = tbl->last_head; 446*95c635efSGarrett D'Amore tbl->last_head->next = hp; 447*95c635efSGarrett D'Amore tbl->last_head = hp; 448*95c635efSGarrett D'Amore } else 449*95c635efSGarrett D'Amore tbl->last_head = tbl->first_head = hp; 450*95c635efSGarrett D'Amore 451*95c635efSGarrett D'Amore head_adjust(p, hp); 452*95c635efSGarrett D'Amore p->head = hp; 453*95c635efSGarrett D'Amore return(p); 454*95c635efSGarrett D'Amore } 455*95c635efSGarrett D'Amore 456*95c635efSGarrett D'Amore static void 457*95c635efSGarrett D'Amore head_adjust(const struct tbl_cell *cellp, struct tbl_head *head) 458*95c635efSGarrett D'Amore { 459*95c635efSGarrett D'Amore if (TBL_CELL_VERT != cellp->pos && 460*95c635efSGarrett D'Amore TBL_CELL_DVERT != cellp->pos) { 461*95c635efSGarrett D'Amore head->pos = TBL_HEAD_DATA; 462*95c635efSGarrett D'Amore return; 463*95c635efSGarrett D'Amore } 464*95c635efSGarrett D'Amore 465*95c635efSGarrett D'Amore if (TBL_CELL_VERT == cellp->pos) 466*95c635efSGarrett D'Amore if (TBL_HEAD_DVERT != head->pos) 467*95c635efSGarrett D'Amore head->pos = TBL_HEAD_VERT; 468*95c635efSGarrett D'Amore 469*95c635efSGarrett D'Amore if (TBL_CELL_DVERT == cellp->pos) 470*95c635efSGarrett D'Amore head->pos = TBL_HEAD_DVERT; 471*95c635efSGarrett D'Amore } 472*95c635efSGarrett D'Amore 473