Print this page
Update to 1.12.3.
   1 /*      $Id: tbl_layout.c,v 1.22 2011/09/18 14:14:15 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>

   4  *
   5  * Permission to use, copy, modify, and distribute this software for any
   6  * purpose with or without fee is hereby granted, provided that the above
   7  * copyright notice and this permission notice appear in all copies.
   8  *
   9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16  */
  17 #ifdef HAVE_CONFIG_H
  18 #include "config.h"
  19 #endif
  20 
  21 #include <assert.h>
  22 #include <ctype.h>
  23 #include <stdlib.h>


  34 };
  35 
  36 /*
  37  * FIXME: we can make this parse a lot nicer by, when an error is
  38  * encountered in a layout key, bailing to the next key (i.e. to the
  39  * next whitespace then continuing).
  40  */
  41 
  42 #define KEYS_MAX         11
  43 
  44 static  const struct tbl_phrase keys[KEYS_MAX] = {
  45         { 'c',           TBL_CELL_CENTRE },
  46         { 'r',           TBL_CELL_RIGHT },
  47         { 'l',           TBL_CELL_LEFT },
  48         { 'n',           TBL_CELL_NUMBER },
  49         { 's',           TBL_CELL_SPAN },
  50         { 'a',           TBL_CELL_LONG },
  51         { '^',           TBL_CELL_DOWN },
  52         { '-',           TBL_CELL_HORIZ },
  53         { '_',           TBL_CELL_HORIZ },
  54         { '=',           TBL_CELL_DHORIZ },
  55         { '|',           TBL_CELL_VERT }
  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 *, 
  64                                 struct tbl_row *, enum tbl_cellt);
  65 static  void             head_adjust(const struct tbl_cell *, 
  66                                 struct tbl_head *);
  67 
  68 static int
  69 mods(struct tbl_node *tbl, struct tbl_cell *cp, 
  70                 int ln, const char *p, int *pos)
  71 {
  72         char             buf[5];
  73         int              i;
  74 
  75         /* Not all types accept modifiers. */
  76 
  77         switch (cp->pos) {
  78         case (TBL_CELL_DOWN):
  79                 /* FALLTHROUGH */
  80         case (TBL_CELL_HORIZ):
  81                 /* FALLTHROUGH */
  82         case (TBL_CELL_DHORIZ):
  83                 /* FALLTHROUGH */
  84         case (TBL_CELL_VERT):
  85                 /* FALLTHROUGH */
  86         case (TBL_CELL_DVERT):
  87                 return(1);
  88         default:
  89                 break;
  90         }
  91 
  92 mod:
  93         /* 
  94          * XXX: since, at least for now, modifiers are non-conflicting
  95          * (are separable by value, regardless of position), we let
  96          * modifiers come in any order.  The existing tbl doesn't let
  97          * this happen.
  98          */
  99         switch (p[*pos]) {
 100         case ('\0'):
 101                 /* FALLTHROUGH */
 102         case (' '):
 103                 /* FALLTHROUGH */
 104         case ('\t'):
 105                 /* FALLTHROUGH */
 106         case (','):


 197         case ('i'):
 198                 cp->flags |= TBL_CELL_ITALIC;
 199                 goto mod;
 200         case ('1'):
 201                 /* FALLTHROUGH */
 202         case ('r'):
 203                 goto mod;
 204         default:
 205                 break;
 206         }
 207 
 208         mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 209                         ln, *pos - 1, NULL);
 210         return(0);
 211 }
 212 
 213 static int
 214 cell(struct tbl_node *tbl, struct tbl_row *rp, 
 215                 int ln, const char *p, int *pos)
 216 {
 217         int              i;
 218         enum tbl_cellt   c;
 219 
 220         /* Parse the column position (`r', `R', `|', ...). */
 221 







 222         for (i = 0; i < KEYS_MAX; i++)
 223                 if (tolower((unsigned char)p[*pos]) == keys[i].name)
 224                         break;
 225 
 226         if (KEYS_MAX == i) {
 227                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 
 228                                 ln, *pos, NULL);
 229                 return(0);
 230         }
 231 
 232         c = keys[i].key;
 233 
 234         /*
 235          * If a span cell is found first, raise a warning and abort the
 236          * parse.  If a span cell is found and the last layout element
 237          * isn't a "normal" layout, bail.
 238          *
 239          * FIXME: recover from this somehow?
 240          */
 241 
 242         if (TBL_CELL_SPAN == c) {
 243                 if (NULL == rp->first) {
 244                         mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 245                                         ln, *pos, NULL);
 246                         return(0);
 247                 } else if (rp->last)
 248                         switch (rp->last->pos) {
 249                         case (TBL_CELL_VERT):
 250                         case (TBL_CELL_DVERT):
 251                         case (TBL_CELL_HORIZ):
 252                         case (TBL_CELL_DHORIZ):
 253                                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 254                                                 ln, *pos, NULL);
 255                                 return(0);
 256                         default:
 257                                 break;
 258                         }
 259         }
 260 
 261         /*
 262          * If a vertical spanner is found, we may not be in the first
 263          * row.
 264          */
 265 
 266         if (TBL_CELL_DOWN == c && rp == tbl->first_row) {
 267                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL);
 268                 return(0);
 269         }
 270 
 271         (*pos)++;
 272 
 273         /* Extra check for the double-vertical. */
 274 
 275         if (TBL_CELL_VERT == c && '|' == p[*pos]) {
 276                 (*pos)++;
 277                 c = TBL_CELL_DVERT;
 278         } 
 279         
 280         /* Disallow adjacent spacers. */
 281 
 282         if (rp->last && (TBL_CELL_VERT == c || TBL_CELL_DVERT == c) &&
 283                         (TBL_CELL_VERT == rp->last->pos || 
 284                          TBL_CELL_DVERT == rp->last->pos)) {
 285                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL);
 286                 return(0);
 287         }
 288 
 289         /* Allocate cell then parse its modifiers. */
 290 
 291         return(mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos));
 292 }
 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                 tbl->last_row = rp;
 314         } else
 315                 tbl->last_row = tbl->first_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, tbl->parse, 
 327                                         ln, *pos, NULL);
 328                 (*pos)++;
 329                 return;
 330         }
 331 
 332         /* End (and possibly restart) a row. */
 333 
 334         if (',' == p[*pos]) {
 335                 (*pos)++;


 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 {
 362         struct tbl_cell *p, *pp;
 363         struct tbl_head *h, *hp;
 364 
 365         p = mandoc_calloc(1, sizeof(struct tbl_cell));
 366 
 367         if (NULL != (pp = rp->last)) {
 368                 rp->last->next = p;





 369                 rp->last = p;
 370         } else
 371                 rp->last = rp->first = p;
 372 
 373         p->pos = pos;

 374 
 375         /*
 376          * This is a little bit complicated.  Here we determine the
 377          * header the corresponds to a cell.  We add headers dynamically
 378          * when need be or re-use them, otherwise.  As an example, given
 379          * the following:
 380          *
 381          *      1  c || l 
 382          *      2  | c | l
 383          *      3  l l
 384          *      3  || c | l |.
 385          *
 386          * We first add the new headers (as there are none) in (1); then
 387          * in (2) we insert the first spanner (as it doesn't match up
 388          * with the header); then we re-use the prior data headers,
 389          * skipping over the spanners; then we re-use everything and add
 390          * a last spanner.  Note that VERT headers are made into DVERT
 391          * ones.
 392          */
 393 
 394         h = pp ? pp->head->next : tbl->first_head;
 395 
 396         if (h) {
 397                 /* Re-use data header. */
 398                 if (TBL_HEAD_DATA == h->pos && 
 399                                 (TBL_CELL_VERT != p->pos &&
 400                                  TBL_CELL_DVERT != p->pos)) {
 401                         p->head = h;
 402                         return(p);
 403                 }
 404 
 405                 /* Re-use spanner header. */
 406                 if (TBL_HEAD_DATA != h->pos && 
 407                                 (TBL_CELL_VERT == p->pos ||
 408                                  TBL_CELL_DVERT == p->pos)) {
 409                         head_adjust(p, h);
 410                         p->head = h;
 411                         return(p);
 412                 }
 413 
 414                 /* Right-shift headers with a new spanner. */
 415                 if (TBL_HEAD_DATA == h->pos && 
 416                                 (TBL_CELL_VERT == p->pos ||
 417                                  TBL_CELL_DVERT == p->pos)) {
 418                         hp = mandoc_calloc(1, sizeof(struct tbl_head));
 419                         hp->ident = tbl->opts.cols++;
 420                         hp->prev = h->prev;
 421                         if (h->prev)
 422                                 h->prev->next = hp;
 423                         if (h == tbl->first_head)
 424                                 tbl->first_head = hp;
 425                         h->prev = hp;
 426                         hp->next = h;
 427                         head_adjust(p, hp);
 428                         p->head = hp;
 429                         return(p);
 430                 }
 431 
 432                 if (NULL != (h = h->next)) {
 433                         head_adjust(p, h);
 434                         p->head = h;
 435                         return(p);
 436                 }
 437 
 438                 /* Fall through to default case... */
 439         }
 440 
 441         hp = mandoc_calloc(1, sizeof(struct tbl_head));
 442         hp->ident = tbl->opts.cols++;
 443 
 444         if (tbl->last_head) {
 445                 hp->prev = tbl->last_head;
 446                 tbl->last_head->next = hp;
 447                 tbl->last_head = hp;
 448         } else
 449                 tbl->last_head = tbl->first_head = hp;

 450 
 451         head_adjust(p, hp);
 452         p->head = hp;
 453         return(p);
 454 }
 455 
 456 static void
 457 head_adjust(const struct tbl_cell *cellp, struct tbl_head *head)
 458 {
 459         if (TBL_CELL_VERT != cellp->pos &&
 460                         TBL_CELL_DVERT != cellp->pos) {
 461                 head->pos = TBL_HEAD_DATA;
 462                 return;
 463         }
 464 
 465         if (TBL_CELL_VERT == cellp->pos)
 466                 if (TBL_HEAD_DVERT != head->pos)
 467                         head->pos = TBL_HEAD_VERT;
 468 
 469         if (TBL_CELL_DVERT == cellp->pos)
 470                 head->pos = TBL_HEAD_DVERT;
 471 }
 472 
   1 /*      $Id: tbl_layout.c,v 1.23 2012/05/27 17:54:54 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2012 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 <assert.h>
  23 #include <ctype.h>
  24 #include <stdlib.h>


  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 static int
  67 mods(struct tbl_node *tbl, struct tbl_cell *cp, 
  68                 int ln, const char *p, int *pos)
  69 {
  70         char             buf[5];
  71         int              i;
  72 
  73         /* Not all types accept modifiers. */
  74 
  75         switch (cp->pos) {
  76         case (TBL_CELL_DOWN):
  77                 /* FALLTHROUGH */
  78         case (TBL_CELL_HORIZ):
  79                 /* FALLTHROUGH */
  80         case (TBL_CELL_DHORIZ):




  81                 return(1);
  82         default:
  83                 break;
  84         }
  85 
  86 mod:
  87         /* 
  88          * XXX: since, at least for now, modifiers are non-conflicting
  89          * (are separable by value, regardless of position), we let
  90          * modifiers come in any order.  The existing tbl doesn't let
  91          * this happen.
  92          */
  93         switch (p[*pos]) {
  94         case ('\0'):
  95                 /* FALLTHROUGH */
  96         case (' '):
  97                 /* FALLTHROUGH */
  98         case ('\t'):
  99                 /* FALLTHROUGH */
 100         case (','):


 191         case ('i'):
 192                 cp->flags |= TBL_CELL_ITALIC;
 193                 goto mod;
 194         case ('1'):
 195                 /* FALLTHROUGH */
 196         case ('r'):
 197                 goto mod;
 198         default:
 199                 break;
 200         }
 201 
 202         mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 203                         ln, *pos - 1, NULL);
 204         return(0);
 205 }
 206 
 207 static int
 208 cell(struct tbl_node *tbl, struct tbl_row *rp, 
 209                 int ln, const char *p, int *pos)
 210 {
 211         int              vert, i;
 212         enum tbl_cellt   c;
 213 
 214         /* Handle vertical lines. */
 215 
 216         for (vert = 0; '|' == p[*pos]; ++*pos)
 217                 vert++;
 218         while (' ' == p[*pos])
 219                 (*pos)++;
 220 
 221         /* Parse the column position (`c', `l', `r', ...). */
 222 
 223         for (i = 0; i < KEYS_MAX; i++)
 224                 if (tolower((unsigned char)p[*pos]) == keys[i].name)
 225                         break;
 226 
 227         if (KEYS_MAX == i) {
 228                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, 
 229                                 ln, *pos, NULL);
 230                 return(0);
 231         }
 232 
 233         c = keys[i].key;
 234 
 235         /*
 236          * If a span cell is found first, raise a warning and abort the
 237          * parse.  If a span cell is found and the last layout element
 238          * isn't a "normal" layout, bail.
 239          *
 240          * FIXME: recover from this somehow?
 241          */
 242 
 243         if (TBL_CELL_SPAN == c) {
 244                 if (NULL == rp->first) {
 245                         mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 246                                         ln, *pos, NULL);
 247                         return(0);
 248                 } else if (rp->last)
 249                         switch (rp->last->pos) {


 250                         case (TBL_CELL_HORIZ):
 251                         case (TBL_CELL_DHORIZ):
 252                                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 253                                                 ln, *pos, NULL);
 254                                 return(0);
 255                         default:
 256                                 break;
 257                         }
 258         }
 259 
 260         /*
 261          * If a vertical spanner is found, we may not be in the first
 262          * row.
 263          */
 264 
 265         if (TBL_CELL_DOWN == c && rp == tbl->first_row) {
 266                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL);
 267                 return(0);
 268         }
 269 
 270         (*pos)++;
 271 







 272         /* Disallow adjacent spacers. */
 273 
 274         if (vert > 2) {


 275                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL);
 276                 return(0);
 277         }
 278 
 279         /* Allocate cell then parse its modifiers. */
 280 
 281         return(mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos));
 282 }
 283 
 284 
 285 static void
 286 row(struct tbl_node *tbl, int ln, const char *p, int *pos)
 287 {
 288         struct tbl_row  *rp;
 289 
 290 row:    /*
 291          * EBNF describing this section:
 292          *
 293          * row          ::= row_list [:space:]* [.]?[\n]
 294          * row_list     ::= [:space:]* row_elem row_tail
 295          * row_tail     ::= [:space:]*[,] row_list |
 296          *                  epsilon
 297          * row_elem     ::= [\t\ ]*[:alpha:]+
 298          */
 299 
 300         rp = mandoc_calloc(1, sizeof(struct tbl_row));
 301         if (tbl->last_row)
 302                 tbl->last_row->next = rp;
 303         else
 304                 tbl->first_row = rp;
 305         tbl->last_row = rp;


 306 
 307 cell:
 308         while (isspace((unsigned char)p[*pos]))
 309                 (*pos)++;
 310 
 311         /* Safely exit layout context. */
 312 
 313         if ('.' == p[*pos]) {
 314                 tbl->part = TBL_PART_DATA;
 315                 if (NULL == tbl->first_row) 
 316                         mandoc_msg(MANDOCERR_TBLNOLAYOUT, tbl->parse, 
 317                                         ln, *pos, NULL);
 318                 (*pos)++;
 319                 return;
 320         }
 321 
 322         /* End (and possibly restart) a row. */
 323 
 324         if (',' == p[*pos]) {
 325                 (*pos)++;


 330         if ( ! cell(tbl, rp, ln, p, pos))
 331                 return;
 332 
 333         goto cell;
 334         /* NOTREACHED */
 335 }
 336 
 337 int
 338 tbl_layout(struct tbl_node *tbl, int ln, const char *p)
 339 {
 340         int              pos;
 341 
 342         pos = 0;
 343         row(tbl, ln, p, &pos);
 344 
 345         /* Always succeed. */
 346         return(1);
 347 }
 348 
 349 static struct tbl_cell *
 350 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
 351                 int vert)
 352 {
 353         struct tbl_cell *p, *pp;
 354         struct tbl_head *h, *hp;
 355 
 356         p = mandoc_calloc(1, sizeof(struct tbl_cell));
 357 
 358         if (NULL != (pp = rp->last)) {
 359                 pp->next = p;
 360                 h = pp->head->next;
 361         } else {
 362                 rp->first = p;
 363                 h = tbl->first_head;
 364         }
 365         rp->last = p;


 366 
 367         p->pos = pos;
 368         p->vert = vert;
 369 
 370         /* Re-use header. */

















 371 


 372         if (h) {




 373                 p->head = h;
 374                 return(p);
 375         }
 376 













 377         hp = mandoc_calloc(1, sizeof(struct tbl_head));
 378         hp->ident = tbl->opts.cols++;
 379         hp->vert = vert;










 380 












 381         if (tbl->last_head) {
 382                 hp->prev = tbl->last_head;
 383                 tbl->last_head->next = hp;

 384         } else
 385                 tbl->first_head = hp;
 386         tbl->last_head = hp;
 387 

 388         p->head = hp;
 389         return(p);
 390 }