Annotation of src/usr.bin/mandoc/tbl.c, Revision 1.1
1.1 ! schwarze 1: /* $Id: tbl.c,v 1.14 2009/09/12 16:05:34 kristaps Exp $ */
! 2: /*
! 3: * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
! 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: #include <sys/queue.h>
! 18:
! 19: #include <assert.h>
! 20: #include <errno.h>
! 21: #include <limits.h>
! 22: #include <stdio.h>
! 23: #include <stdlib.h>
! 24: #include <string.h>
! 25:
! 26: #include "tbl.h"
! 27: #include "tbl_extern.h"
! 28:
! 29:
! 30: const char *const errnames[ERR_MAX] = {
! 31: "bad syntax", /* ERR_SYNTAX */
! 32: "bad option" /* ERR_OPTION */
! 33: };
! 34:
! 35: static char buf[1024]; /* XXX */
! 36:
! 37: static enum tbl_tok tbl_next_char(char);
! 38: static void tbl_init(struct tbl *);
! 39: static void tbl_clear(struct tbl *);
! 40: static struct tbl_head *tbl_head_alloc(struct tbl *);
! 41: static void tbl_span_free(struct tbl_span *);
! 42: static void tbl_data_free(struct tbl_data *);
! 43: static void tbl_row_free(struct tbl_row *);
! 44:
! 45: static void headadj(const struct tbl_cell *,
! 46: struct tbl_head *);
! 47:
! 48: static void
! 49: tbl_init(struct tbl *tbl)
! 50: {
! 51:
! 52: bzero(tbl, sizeof(struct tbl));
! 53:
! 54: tbl->part = TBL_PART_OPTS;
! 55: tbl->tab = '\t';
! 56: tbl->linesize = 12;
! 57: tbl->decimal = '.';
! 58:
! 59: TAILQ_INIT(&tbl->span);
! 60: TAILQ_INIT(&tbl->row);
! 61: TAILQ_INIT(&tbl->head);
! 62: }
! 63:
! 64:
! 65: int
! 66: tbl_read(struct tbl *tbl, const char *f, int ln, const char *p, int len)
! 67: {
! 68:
! 69: if (len && TBL_PART_OPTS == tbl->part)
! 70: if (';' != p[len - 1])
! 71: tbl->part = TBL_PART_LAYOUT;
! 72:
! 73: switch (tbl->part) {
! 74: case (TBL_PART_OPTS):
! 75: return(tbl_option(tbl, f, ln, p));
! 76: case (TBL_PART_CLAYOUT):
! 77: /* FALLTHROUGH */
! 78: case (TBL_PART_LAYOUT):
! 79: return(tbl_layout(tbl, f, ln, p));
! 80: case (TBL_PART_DATA):
! 81: return(tbl_data(tbl, f, ln, p));
! 82: case (TBL_PART_ERROR):
! 83: break;
! 84: }
! 85:
! 86: return(0);
! 87: }
! 88:
! 89:
! 90: int
! 91: tbl_close(struct tbl *tbl, const char *f, int ln)
! 92: {
! 93:
! 94: if (TBL_PART_DATA != tbl->part)
! 95: return(tbl_errx(tbl, ERR_SYNTAX, f, ln, 0));
! 96: if ( ! tbl_data_close(tbl, f, ln))
! 97: return(0);
! 98: #if 1
! 99: return(tbl_calc_term(tbl));
! 100: #else
! 101: return(tbl_calc_tree(tbl));
! 102: #endif
! 103: }
! 104:
! 105:
! 106: int
! 107: tbl_write(const struct tbl *tbl)
! 108: {
! 109:
! 110: #if 1
! 111: return(tbl_write_term(tbl));
! 112: #else
! 113: return(tbl_write_tree(tbl));
! 114: #endif
! 115: }
! 116:
! 117:
! 118: static enum tbl_tok
! 119: tbl_next_char(char c)
! 120: {
! 121:
! 122: /*
! 123: * These are delimiting tokens. They separate out words in the
! 124: * token stream.
! 125: */
! 126:
! 127: switch (c) {
! 128: case ('('):
! 129: return(TBL_TOK_OPENPAREN);
! 130: case (')'):
! 131: return(TBL_TOK_CLOSEPAREN);
! 132: case (' '):
! 133: return(TBL_TOK_SPACE);
! 134: case ('\t'):
! 135: return(TBL_TOK_TAB);
! 136: case (';'):
! 137: return(TBL_TOK_SEMICOLON);
! 138: case ('.'):
! 139: return(TBL_TOK_PERIOD);
! 140: case (','):
! 141: return(TBL_TOK_COMMA);
! 142: case (0):
! 143: return(TBL_TOK_NIL);
! 144: default:
! 145: break;
! 146: }
! 147:
! 148: return(TBL_TOK_WORD);
! 149: }
! 150:
! 151:
! 152: const char *
! 153: tbl_last(void)
! 154: {
! 155:
! 156: return(buf);
! 157: }
! 158:
! 159:
! 160: int
! 161: tbl_last_uint(void)
! 162: {
! 163: char *ep;
! 164: long lval;
! 165:
! 166: /* From OpenBSD's strtol(3). Gross. */
! 167:
! 168: errno = 0;
! 169: lval = strtol(buf, &ep, 10);
! 170: if (buf[0] == 0 || *ep != 0)
! 171: return(-1);
! 172: if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))
! 173: return(-1);
! 174: if (lval < 0 || lval > INT_MAX)
! 175: return(-1);
! 176:
! 177: return((int)lval);
! 178: }
! 179:
! 180:
! 181: enum tbl_tok
! 182: tbl_next(const char *p, int *pos)
! 183: {
! 184: int i;
! 185: enum tbl_tok c;
! 186:
! 187: buf[0] = 0;
! 188:
! 189: if (TBL_TOK_WORD != (c = tbl_next_char(p[*pos]))) {
! 190: if (TBL_TOK_NIL != c) {
! 191: buf[0] = p[*pos];
! 192: buf[1] = 0;
! 193: (*pos)++;
! 194: }
! 195: return(c);
! 196: }
! 197:
! 198: /*
! 199: * Copy words into a nil-terminated buffer. For now, we use a
! 200: * static buffer. Eventually this should be made into a dynamic
! 201: * one living in struct tbl.
! 202: */
! 203:
! 204: for (i = 0; i < 1023; i++, (*pos)++)
! 205: if (TBL_TOK_WORD == tbl_next_char(p[*pos]))
! 206: buf[i] = p[*pos];
! 207: else
! 208: break;
! 209:
! 210: assert(i < 1023);
! 211: buf[i] = 0;
! 212:
! 213: return(TBL_TOK_WORD);
! 214: }
! 215:
! 216:
! 217: int
! 218: tbl_err(struct tbl *tbl)
! 219: {
! 220:
! 221: (void)fprintf(stderr, "%s\n", strerror(errno));
! 222: tbl->part = TBL_PART_ERROR;
! 223: return(0);
! 224: }
! 225:
! 226:
! 227: /* ARGSUSED */
! 228: int
! 229: tbl_warnx(struct tbl *tbl, enum tbl_err tok,
! 230: const char *f, int line, int pos)
! 231: {
! 232:
! 233: (void)fprintf(stderr, "%s:%d:%d: %s\n",
! 234: f, line, pos + 1, errnames[tok]);
! 235:
! 236: /* TODO: -Werror */
! 237: return(1);
! 238: }
! 239:
! 240:
! 241: int
! 242: tbl_errx(struct tbl *tbl, enum tbl_err tok,
! 243: const char *f, int line, int pos)
! 244: {
! 245:
! 246: (void)fprintf(stderr, "%s:%d:%d: %s\n",
! 247: f, line, pos + 1, errnames[tok]);
! 248:
! 249: tbl->part = TBL_PART_ERROR;
! 250: return(0);
! 251: }
! 252:
! 253:
! 254: struct tbl *
! 255: tbl_alloc(void)
! 256: {
! 257: struct tbl *p;
! 258:
! 259: if (NULL == (p = malloc(sizeof(struct tbl))))
! 260: return(NULL);
! 261:
! 262: tbl_init(p);
! 263: return(p);
! 264: }
! 265:
! 266:
! 267: void
! 268: tbl_free(struct tbl *p)
! 269: {
! 270:
! 271: tbl_clear(p);
! 272: free(p);
! 273: }
! 274:
! 275:
! 276: void
! 277: tbl_reset(struct tbl *tbl)
! 278: {
! 279:
! 280: tbl_clear(tbl);
! 281: tbl_init(tbl);
! 282: }
! 283:
! 284:
! 285: struct tbl_span *
! 286: tbl_span_alloc(struct tbl *tbl)
! 287: {
! 288: struct tbl_span *p, *pp;
! 289: struct tbl_row *row;
! 290:
! 291: if (NULL == (p = calloc(1, sizeof(struct tbl_span)))) {
! 292: (void)tbl_err(tbl);
! 293: return(NULL);
! 294: }
! 295:
! 296: TAILQ_INIT(&p->data);
! 297: TAILQ_INSERT_TAIL(&tbl->span, p, entries);
! 298:
! 299: /* LINTED */
! 300: pp = TAILQ_PREV(p, tbl_spanh, entries);
! 301:
! 302: if (pp) {
! 303: row = TAILQ_NEXT(pp->row, entries);
! 304: if (NULL == row)
! 305: row = pp->row;
! 306: } else {
! 307: row = TAILQ_FIRST(&tbl->row);
! 308: }
! 309:
! 310: assert(row);
! 311: p->row = row;
! 312: p->tbl = tbl;
! 313: return(p);
! 314: }
! 315:
! 316:
! 317: struct tbl_row *
! 318: tbl_row_alloc(struct tbl *tbl)
! 319: {
! 320: struct tbl_row *p;
! 321:
! 322: if (NULL == (p = calloc(1, sizeof(struct tbl_row)))) {
! 323: (void)tbl_err(tbl);
! 324: return(NULL);
! 325: }
! 326:
! 327: TAILQ_INIT(&p->cell);
! 328: TAILQ_INSERT_TAIL(&tbl->row, p, entries);
! 329: p->tbl = tbl;
! 330: return(p);
! 331: }
! 332:
! 333:
! 334: static void
! 335: headadj(const struct tbl_cell *cell, struct tbl_head *head)
! 336: {
! 337: if (TBL_CELL_VERT != cell->pos &&
! 338: TBL_CELL_DVERT != cell->pos) {
! 339: head->pos = TBL_HEAD_DATA;
! 340: return;
! 341: }
! 342: if (TBL_CELL_VERT == cell->pos)
! 343: if (TBL_HEAD_DVERT != head->pos)
! 344: head->pos = TBL_HEAD_VERT;
! 345: if (TBL_CELL_DVERT == cell->pos)
! 346: head->pos = TBL_HEAD_DVERT;
! 347: }
! 348:
! 349:
! 350: static struct tbl_head *
! 351: tbl_head_alloc(struct tbl *tbl)
! 352: {
! 353: struct tbl_head *p;
! 354:
! 355: if (NULL == (p = calloc(1, sizeof(struct tbl_head)))) {
! 356: (void)tbl_err(tbl);
! 357: return(NULL);
! 358: }
! 359: p->tbl = tbl;
! 360: return(p);
! 361: }
! 362:
! 363:
! 364: struct tbl_cell *
! 365: tbl_cell_alloc(struct tbl_row *rp, enum tbl_cellt pos)
! 366: {
! 367: struct tbl_cell *p, *pp;
! 368: struct tbl_head *h, *hp;
! 369:
! 370: if (NULL == (p = calloc(1, sizeof(struct tbl_cell)))) {
! 371: (void)tbl_err(rp->tbl);
! 372: return(NULL);
! 373: }
! 374:
! 375: TAILQ_INSERT_TAIL(&rp->cell, p, entries);
! 376: p->pos = pos;
! 377: p->row = rp;
! 378:
! 379: /*
! 380: * This is a little bit complicated. Here we determine the
! 381: * header the corresponds to a cell. We add headers dynamically
! 382: * when need be or re-use them, otherwise. As an example, given
! 383: * the following:
! 384: *
! 385: * 1 c || l
! 386: * 2 | c | l
! 387: * 3 l l
! 388: * 3 || c | l |.
! 389: *
! 390: * We first add the new headers (as there are none) in (1); then
! 391: * in (2) we insert the first spanner (as it doesn't match up
! 392: * with the header); then we re-use the prior data headers,
! 393: * skipping over the spanners; then we re-use everything and add
! 394: * a last spanner. Note that VERT headers are made into DVERT
! 395: * ones.
! 396: */
! 397:
! 398: /* LINTED */
! 399: pp = TAILQ_PREV(p, tbl_cellh, entries);
! 400:
! 401: h = pp ? TAILQ_NEXT(pp->head, entries) :
! 402: TAILQ_FIRST(&rp->tbl->head);
! 403:
! 404: if (h) {
! 405: /* Re-use data header. */
! 406: if (TBL_HEAD_DATA == h->pos &&
! 407: (TBL_CELL_VERT != p->pos &&
! 408: TBL_CELL_DVERT != p->pos)) {
! 409: p->head = h;
! 410: return(p);
! 411: }
! 412:
! 413: /* Re-use spanner header. */
! 414: if (TBL_HEAD_DATA != h->pos &&
! 415: (TBL_CELL_VERT == p->pos ||
! 416: TBL_CELL_DVERT == p->pos)) {
! 417: headadj(p, h);
! 418: p->head = h;
! 419: return(p);
! 420: }
! 421:
! 422: /* Right-shift headers with a new spanner. */
! 423: if (TBL_HEAD_DATA == h->pos &&
! 424: (TBL_CELL_VERT == p->pos ||
! 425: TBL_CELL_DVERT == p->pos)) {
! 426: if (NULL == (hp = tbl_head_alloc(rp->tbl)))
! 427: return(NULL);
! 428: TAILQ_INSERT_BEFORE(h, hp, entries);
! 429: headadj(p, hp);
! 430: p->head = hp;
! 431: return(p);
! 432: }
! 433:
! 434: h = TAILQ_NEXT(h, entries);
! 435: if (h) {
! 436: headadj(p, h);
! 437: p->head = h;
! 438: return(p);
! 439: }
! 440:
! 441: /* Fall through to default case... */
! 442: }
! 443:
! 444: if (NULL == (hp = tbl_head_alloc(rp->tbl)))
! 445: return(NULL);
! 446: TAILQ_INSERT_TAIL(&rp->tbl->head, hp, entries);
! 447: headadj(p, hp);
! 448: p->head = hp;
! 449: return(p);
! 450: }
! 451:
! 452:
! 453: struct tbl_data *
! 454: tbl_data_alloc(struct tbl_span *sp)
! 455: {
! 456: struct tbl_data *p;
! 457: struct tbl_cell *cp;
! 458: struct tbl_data *dp;
! 459:
! 460: if (NULL == (p = calloc(1, sizeof(struct tbl_data)))) {
! 461: (void)tbl_err(sp->row->tbl);
! 462: return(NULL);
! 463: }
! 464:
! 465: cp = NULL;
! 466: /* LINTED */
! 467: if (NULL == (dp = TAILQ_LAST(&sp->data, tbl_datah)))
! 468: cp = TAILQ_FIRST(&sp->row->cell);
! 469: else if (dp->cell)
! 470: cp = TAILQ_NEXT(dp->cell, entries);
! 471:
! 472: TAILQ_INSERT_TAIL(&sp->data, p, entries);
! 473:
! 474: if (cp && (TBL_CELL_VERT == cp->pos ||
! 475: TBL_CELL_DVERT == cp->pos))
! 476: cp = TAILQ_NEXT(cp, entries);
! 477:
! 478: p->span = sp;
! 479: p->cell = cp;
! 480: return(p);
! 481: }
! 482:
! 483:
! 484: static void
! 485: tbl_clear(struct tbl *p)
! 486: {
! 487: struct tbl_span *span;
! 488: struct tbl_head *head;
! 489: struct tbl_row *row;
! 490:
! 491: /* LINTED */
! 492: while ((span = TAILQ_FIRST(&p->span))) {
! 493: TAILQ_REMOVE(&p->span, span, entries);
! 494: tbl_span_free(span);
! 495: }
! 496: /* LINTED */
! 497: while ((row = TAILQ_FIRST(&p->row))) {
! 498: TAILQ_REMOVE(&p->row, row, entries);
! 499: tbl_row_free(row);
! 500: }
! 501: /* LINTED */
! 502: while ((head = TAILQ_FIRST(&p->head))) {
! 503: TAILQ_REMOVE(&p->head, head, entries);
! 504: free(head);
! 505: }
! 506: }
! 507:
! 508:
! 509: static void
! 510: tbl_span_free(struct tbl_span *p)
! 511: {
! 512: struct tbl_data *data;
! 513:
! 514: /* LINTED */
! 515: while ((data = TAILQ_FIRST(&p->data))) {
! 516: TAILQ_REMOVE(&p->data, data, entries);
! 517: tbl_data_free(data);
! 518: }
! 519: free(p);
! 520: }
! 521:
! 522:
! 523: static void
! 524: tbl_data_free(struct tbl_data *p)
! 525: {
! 526:
! 527: if (p->string)
! 528: free(p->string);
! 529: free(p);
! 530: }
! 531:
! 532:
! 533: static void
! 534: tbl_row_free(struct tbl_row *p)
! 535: {
! 536: struct tbl_cell *cell;
! 537:
! 538: /* LINTED */
! 539: while ((cell = TAILQ_FIRST(&p->cell))) {
! 540: TAILQ_REMOVE(&p->cell, cell, entries);
! 541: free(cell);
! 542: }
! 543: free(p);
! 544: }