[BACK]Return to tbl_layout.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mandoc

File: [local] / src / usr.bin / mandoc / tbl_layout.c (download)

Revision 1.1, Fri Oct 15 19:20:03 2010 UTC (13 years, 7 months ago) by schwarze
Branch: MAIN

Import tbl parser and renderer written by kristaps@.
Unchanged code from bsd.lv release 0.1.5, but without the main program.
Not yet linked to the build; next commit will integrate it into mandoc.

/*	$Id: tbl_layout.c,v 1.1 2010/10/15 19:20:03 schwarze Exp $ */
/*
 * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <sys/queue.h>
#include <sys/types.h>

#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "tbl_extern.h"

struct	tbl_phrase {
	char		 name;
	enum tbl_cellt	 key;
};

#define	KEYS_MAX	 17

static	const struct tbl_phrase keys[KEYS_MAX] = {
	{ 'c',		 TBL_CELL_CENTRE },
	{ 'C',		 TBL_CELL_CENTRE },
	{ 'r',		 TBL_CELL_RIGHT },
	{ 'R',		 TBL_CELL_RIGHT },
	{ 'l',		 TBL_CELL_LEFT },
	{ 'L',		 TBL_CELL_LEFT },
	{ 'n',		 TBL_CELL_NUMBER },
	{ 'N',		 TBL_CELL_NUMBER },
	{ 's',		 TBL_CELL_SPAN },
	{ 'S',		 TBL_CELL_SPAN },
	{ 'a',		 TBL_CELL_LONG },
	{ 'A',		 TBL_CELL_LONG },
	{ '^',		 TBL_CELL_DOWN },
	{ '-',		 TBL_CELL_HORIZ },
	{ '_',		 TBL_CELL_HORIZ },
	{ '=',		 TBL_CELL_DHORIZ },
	{ '|',		 TBL_CELL_VERT }
};

static	int		mods(struct tbl *, struct tbl_cell *, 
				const char *, int, 
				const char *, int, int);
static	int		cell(struct tbl *, struct tbl_row *, 
				const char *, int, int);
static	int		row(struct tbl *, const char *,
				int, const char *, int *);


static int
mods(struct tbl *tbl, struct tbl_cell *cp, const char *p, 
		int pp, const char *f, int ln, int pos)
{
	char		 buf[5];
	int		 i;

	/* 
	 * XXX: since, at least for now, modifiers are non-conflicting
	 * (are separable by value, regardless of position), we let
	 * modifiers come in any order.  The existing tbl doesn't let
	 * this happen.
	 */

	if (0 == p[pp])
		return(1);

	/* Parse numerical spacing from modifier string. */

	if (isdigit((u_char)p[pp])) {
		for (i = 0; i < 4; i++) {
			if ( ! isdigit((u_char)p[pp + i]))
				break;
			buf[i] = p[pp + i];
		}
		buf[i] = 0;

		/* No greater than 4 digits. */

		if (4 == i)
			return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos + pp));

		/* 
		 * We can't change the spacing in any subsequent layout
		 * definitions.  FIXME: I don't think we can change the
		 * spacing for a column at all, after it's already been
		 * initialised.
		 */

		if (TBL_PART_CLAYOUT != tbl->part)
			cp->spacing = atoi(buf);
		else if ( ! tbl_warnx(tbl, ERR_SYNTAX, f, ln, pos + pp))
			return(0);
		
		/* Continue parsing modifiers. */

		return(mods(tbl, cp, p, pp + i, f, ln, pos));
	} 

	/* TODO: GNU has many more extensions. */

	switch (p[pp]) {
	case ('z'):
		/* FALLTHROUGH */
	case ('Z'):
		cp->flags |= TBL_CELL_WIGN;
		return(mods(tbl, cp, p, pp + 1, f, ln, pos));
	case ('u'):
		/* FALLTHROUGH */
	case ('U'):
		cp->flags |= TBL_CELL_UP;
		return(mods(tbl, cp, p, pp + 1, f, ln, pos));
	case ('e'):
		/* FALLTHROUGH */
	case ('E'):
		cp->flags |= TBL_CELL_EQUAL;
		return(mods(tbl, cp, p, pp + 1, f, ln, pos));
	case ('t'):
		/* FALLTHROUGH */
	case ('T'):
		cp->flags |= TBL_CELL_TALIGN;
		return(mods(tbl, cp, p, pp + 1, f, ln, pos));
	case ('d'):
		/* FALLTHROUGH */
	case ('D'):
		cp->flags |= TBL_CELL_BALIGN;
		return(mods(tbl, cp, p, pp + 1, f, ln, pos));
	case ('f'):
		pp++;
		/* FALLTHROUGH */
	case ('B'):
		/* FALLTHROUGH */
	case ('I'):
		/* FALLTHROUGH */
	case ('b'):
		/* FALLTHROUGH */
	case ('i'):
		break;
	default:
		return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos + pp));
	}

	switch (p[pp]) {
	case ('b'):
		/* FALLTHROUGH */
	case ('B'):
		cp->flags |= TBL_CELL_BOLD;
		return(mods(tbl, cp, p, pp + 1, f, ln, pos));
	case ('i'):
		/* FALLTHROUGH */
	case ('I'):
		cp->flags |= TBL_CELL_ITALIC;
		return(mods(tbl, cp, p, pp + 1, f, ln, pos));
	default:
		break;
	}

	return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos + pp));
}


static int
cell(struct tbl *tbl, struct tbl_row *rp, 
		const char *f, int ln, int pos)
{
	struct tbl_cell	*cp;
	const char	*p;
	int		 j, i;
	enum tbl_cellt	 c;

	/* Parse the column position (`r', `R', `|', ...). */

	c = TBL_CELL_MAX;
	for (p = tbl_last(), i = 0; i < KEYS_MAX; i++) {
		if (keys[i].name != p[0])
			continue;
		c = keys[i].key;
		break;
	}

	if (i == KEYS_MAX)
		return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos));

	/* Extra check for the double-vertical. */

	if (TBL_CELL_VERT == c && '|' == p[1]) {
		j = 2;
		c = TBL_CELL_DVERT;
	} else
		j = 1;
	
	/* Disallow subsequent spacers. */

	/* LINTED */
	cp = TAILQ_LAST(&rp->cell, tbl_cellh);

	if (cp && (TBL_CELL_VERT == c || TBL_CELL_DVERT == c) && 
			(TBL_CELL_VERT == cp->pos || 
			 TBL_CELL_DVERT == cp->pos))
		return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos));

	/* Allocate cell then parse its modifiers. */

	if (NULL == (cp = tbl_cell_alloc(rp, c)))
		return(0);
	return(mods(tbl, cp, p, j, f, ln, pos));
}


static int
row(struct tbl *tbl, const char *f, int ln,
		const char *p, int *pos)
{
	struct tbl_row	*rp;
	int		 sv;

	rp = tbl_row_alloc(tbl);
again:
	sv = *pos;

	/*
	 * EBNF describing this section:
	 *
	 * row		::= row_list [:space:]* [.]?[\n]
	 * row_list	::= [:space:]* row_elem row_tail
	 * row_tail	::= [:space:]*[,] row_list |
	 *                  epsilon
	 * row_elem	::= [\t\ ]*[:alpha:]+
	 */

	switch (tbl_next(p, pos)) {
	case (TBL_TOK_TAB):
		/* FALLTHROUGH */
	case (TBL_TOK_SPACE):
		goto again;
	case (TBL_TOK_WORD):
		if ( ! cell(tbl, rp, f, ln, sv))
			return(0);
		goto again;
	case (TBL_TOK_COMMA):
		if (NULL == TAILQ_FIRST(&rp->cell))
			return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv));
		return(row(tbl, f, ln, p, pos));
	case (TBL_TOK_PERIOD):
		if (NULL == TAILQ_FIRST(&rp->cell))
			return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv));
		tbl->part = TBL_PART_DATA;
		break;
	case (TBL_TOK_NIL):
		if (NULL == TAILQ_FIRST(&rp->cell))
			return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv));
		break;
	default:
		return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv));
	}

	return(1);
}


int
tbl_layout(struct tbl *tbl, const char *f, int ln, const char *p)
{
	int		 pos;

	pos = 0;
	return(row(tbl, f, ln, p, &pos));
}