400 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			400 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*	$Id: out.c,v 1.16 2010/06/19 20:46:28 kristaps Exp $ */
 | 
						|
/*
 | 
						|
 * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
#ifdef HAVE_CONFIG_H
 | 
						|
#include "config.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <time.h>
 | 
						|
 | 
						|
#include "out.h"
 | 
						|
 | 
						|
/* See a2roffdeco(). */
 | 
						|
#define	C2LIM(c, l) do { \
 | 
						|
	(l) = 1; \
 | 
						|
	if ('[' == (c) || '\'' == (c)) \
 | 
						|
		(l) = 0; \
 | 
						|
	else if ('(' == (c)) \
 | 
						|
		(l) = 2; } \
 | 
						|
	while (/* CONSTCOND */ 0)
 | 
						|
 | 
						|
/* See a2roffdeco(). */
 | 
						|
#define	C2TERM(c, t) do { \
 | 
						|
	(t) = 0; \
 | 
						|
	if ('\'' == (c)) \
 | 
						|
		(t) = 1; \
 | 
						|
	else if ('[' == (c)) \
 | 
						|
		(t) = 2; \
 | 
						|
	else if ('(' == (c)) \
 | 
						|
		(t) = 3; } \
 | 
						|
	while (/* CONSTCOND */ 0)
 | 
						|
 | 
						|
/* 
 | 
						|
 * Convert a `scaling unit' to a consistent form, or fail.  Scaling
 | 
						|
 * units are documented in groff.7, mdoc.7, man.7.
 | 
						|
 */
 | 
						|
int
 | 
						|
a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
 | 
						|
{
 | 
						|
	char		 buf[BUFSIZ], hasd;
 | 
						|
	int		 i;
 | 
						|
	enum roffscale	 unit;
 | 
						|
 | 
						|
	if ('\0' == *src)
 | 
						|
		return(0);
 | 
						|
 | 
						|
	i = hasd = 0;
 | 
						|
 | 
						|
	switch (*src) {
 | 
						|
	case ('+'):
 | 
						|
		src++;
 | 
						|
		break;
 | 
						|
	case ('-'):
 | 
						|
		buf[i++] = *src++;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	if ('\0' == *src)
 | 
						|
		return(0);
 | 
						|
 | 
						|
	while (i < BUFSIZ) {
 | 
						|
		if ( ! isdigit((u_char)*src)) {
 | 
						|
			if ('.' != *src)
 | 
						|
				break;
 | 
						|
			else if (hasd)
 | 
						|
				break;
 | 
						|
			else
 | 
						|
				hasd = 1;
 | 
						|
		}
 | 
						|
		buf[i++] = *src++;
 | 
						|
	}
 | 
						|
 | 
						|
	if (BUFSIZ == i || (*src && *(src + 1)))
 | 
						|
		return(0);
 | 
						|
 | 
						|
	buf[i] = '\0';
 | 
						|
 | 
						|
	switch (*src) {
 | 
						|
	case ('c'):
 | 
						|
		unit = SCALE_CM;
 | 
						|
		break;
 | 
						|
	case ('i'):
 | 
						|
		unit = SCALE_IN;
 | 
						|
		break;
 | 
						|
	case ('P'):
 | 
						|
		unit = SCALE_PC;
 | 
						|
		break;
 | 
						|
	case ('p'):
 | 
						|
		unit = SCALE_PT;
 | 
						|
		break;
 | 
						|
	case ('f'):
 | 
						|
		unit = SCALE_FS;
 | 
						|
		break;
 | 
						|
	case ('v'):
 | 
						|
		unit = SCALE_VS;
 | 
						|
		break;
 | 
						|
	case ('m'):
 | 
						|
		unit = SCALE_EM;
 | 
						|
		break;
 | 
						|
	case ('\0'):
 | 
						|
		if (SCALE_MAX == def)
 | 
						|
			return(0);
 | 
						|
		unit = SCALE_BU;
 | 
						|
		break;
 | 
						|
	case ('u'):
 | 
						|
		unit = SCALE_BU;
 | 
						|
		break;
 | 
						|
	case ('M'):
 | 
						|
		unit = SCALE_MM;
 | 
						|
		break;
 | 
						|
	case ('n'):
 | 
						|
		unit = SCALE_EN;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		return(0);
 | 
						|
	}
 | 
						|
 | 
						|
	if ((dst->scale = atof(buf)) < 0)
 | 
						|
		dst->scale = 0;
 | 
						|
	dst->unit = unit;
 | 
						|
	dst->pt = hasd;
 | 
						|
 | 
						|
	return(1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Correctly writes the time in nroff form, which differs from standard
 | 
						|
 * form in that a space isn't printed in lieu of the extra %e field for
 | 
						|
 * single-digit dates.
 | 
						|
 */
 | 
						|
void
 | 
						|
time2a(time_t t, char *dst, size_t sz)
 | 
						|
{
 | 
						|
	struct tm	 tm;
 | 
						|
	char		 buf[5];
 | 
						|
	char		*p;
 | 
						|
	size_t		 nsz;
 | 
						|
 | 
						|
	assert(sz > 1);
 | 
						|
	localtime_r(&t, &tm);
 | 
						|
 | 
						|
	p = dst;
 | 
						|
	nsz = 0;
 | 
						|
 | 
						|
	dst[0] = '\0';
 | 
						|
 | 
						|
	if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
 | 
						|
		return;
 | 
						|
 | 
						|
	p += (int)nsz;
 | 
						|
	sz -= nsz;
 | 
						|
 | 
						|
	if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
 | 
						|
		return;
 | 
						|
 | 
						|
	nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
 | 
						|
 | 
						|
	if (nsz >= sz)
 | 
						|
		return;
 | 
						|
 | 
						|
	p += (int)nsz;
 | 
						|
	sz -= nsz;
 | 
						|
 | 
						|
	(void)strftime(p, sz, "%Y", &tm);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* 
 | 
						|
 * Returns length of parsed string (the leading "\" should NOT be
 | 
						|
 * included).  This can be zero if the current character is the nil
 | 
						|
 * terminator.  "d" is set to the type of parsed decorator, which may
 | 
						|
 * have an adjoining "word" of size "sz" (e.g., "(ab" -> "ab", 2).
 | 
						|
 */
 | 
						|
int
 | 
						|
a2roffdeco(enum roffdeco *d,
 | 
						|
		const char **word, size_t *sz)
 | 
						|
{
 | 
						|
	int		 j, term, lim;
 | 
						|
	char		 set;
 | 
						|
	const char	*wp, *sp;
 | 
						|
 | 
						|
	*d = DECO_NONE;
 | 
						|
	wp = *word;
 | 
						|
 | 
						|
	switch ((set = *wp)) {
 | 
						|
	case ('\0'):
 | 
						|
		return(0);
 | 
						|
 | 
						|
	case ('('):
 | 
						|
		if ('\0' == *(++wp))
 | 
						|
			return(1);
 | 
						|
		if ('\0' == *(wp + 1))
 | 
						|
			return(2);
 | 
						|
 | 
						|
		*d = DECO_SPECIAL;
 | 
						|
		*sz = 2;
 | 
						|
		*word = wp;
 | 
						|
		return(3);
 | 
						|
 | 
						|
	case ('F'):
 | 
						|
		/* FALLTHROUGH */
 | 
						|
	case ('f'):
 | 
						|
		/*
 | 
						|
		 * FIXME: this needs work and consolidation (it should
 | 
						|
		 * follow the sequence that special characters do, for
 | 
						|
		 * one), but isn't a priority at the moment.  Note, for
 | 
						|
		 * one, that in reality \fB != \FB, although here we let
 | 
						|
		 * these slip by.
 | 
						|
		 */
 | 
						|
		switch (*(++wp)) {
 | 
						|
		case ('\0'):
 | 
						|
			return(1);
 | 
						|
		case ('3'):
 | 
						|
			/* FALLTHROUGH */
 | 
						|
		case ('B'):
 | 
						|
			*d = DECO_BOLD;
 | 
						|
			return(2);
 | 
						|
		case ('2'):
 | 
						|
			/* FALLTHROUGH */
 | 
						|
		case ('I'):
 | 
						|
			*d = DECO_ITALIC;
 | 
						|
			return(2);
 | 
						|
		case ('P'):
 | 
						|
			*d = DECO_PREVIOUS;
 | 
						|
			return(2);
 | 
						|
		case ('1'):
 | 
						|
			/* FALLTHROUGH */
 | 
						|
		case ('R'):
 | 
						|
			*d = DECO_ROMAN;
 | 
						|
			return(2);
 | 
						|
		case ('('):
 | 
						|
			if ('\0' == *(++wp))
 | 
						|
				return(2);
 | 
						|
			if ('\0' == *(wp + 1))
 | 
						|
				return(3);
 | 
						|
 | 
						|
			*d = 'F' == set ? DECO_FFONT : DECO_FONT;
 | 
						|
			*sz = 2;
 | 
						|
			*word = wp;
 | 
						|
			return(4);
 | 
						|
		case ('['):
 | 
						|
			*word = ++wp;
 | 
						|
			for (j = 0; *wp && ']' != *wp; wp++, j++)
 | 
						|
				/* Loop... */ ;
 | 
						|
 | 
						|
			if ('\0' == *wp)
 | 
						|
				return(j + 2);
 | 
						|
 | 
						|
			*d = 'F' == set ? DECO_FFONT : DECO_FONT;
 | 
						|
			*sz = (size_t)j;
 | 
						|
			return(j + 3);
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		*d = 'F' == set ? DECO_FFONT : DECO_FONT;
 | 
						|
		*sz = 1;
 | 
						|
		*word = wp;
 | 
						|
		return(2);
 | 
						|
 | 
						|
	case ('*'):
 | 
						|
		switch (*(++wp)) {
 | 
						|
		case ('\0'):
 | 
						|
			return(1);
 | 
						|
 | 
						|
		case ('('):
 | 
						|
			if ('\0' == *(++wp))
 | 
						|
				return(2);
 | 
						|
			if ('\0' == *(wp + 1))
 | 
						|
				return(3);
 | 
						|
 | 
						|
			*d = DECO_RESERVED;
 | 
						|
			*sz = 2;
 | 
						|
			*word = wp;
 | 
						|
			return(4);
 | 
						|
 | 
						|
		case ('['):
 | 
						|
			*word = ++wp;
 | 
						|
			for (j = 0; *wp && ']' != *wp; wp++, j++)
 | 
						|
				/* Loop... */ ;
 | 
						|
 | 
						|
			if ('\0' == *wp)
 | 
						|
				return(j + 2);
 | 
						|
 | 
						|
			*d = DECO_RESERVED;
 | 
						|
			*sz = (size_t)j;
 | 
						|
			return(j + 3);
 | 
						|
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		*d = DECO_RESERVED;
 | 
						|
		*sz = 1;
 | 
						|
		*word = wp;
 | 
						|
		return(2);
 | 
						|
 | 
						|
	case ('s'):
 | 
						|
		sp = wp;
 | 
						|
		if ('\0' == *(++wp))
 | 
						|
			return(1);
 | 
						|
 | 
						|
		C2LIM(*wp, lim);
 | 
						|
		C2TERM(*wp, term);
 | 
						|
 | 
						|
		if (term) 
 | 
						|
			wp++;
 | 
						|
 | 
						|
		*word = wp;
 | 
						|
 | 
						|
		if (*wp == '+' || *wp == '-')
 | 
						|
			++wp;
 | 
						|
 | 
						|
		switch (*wp) {
 | 
						|
		case ('\''):
 | 
						|
			/* FALLTHROUGH */
 | 
						|
		case ('['):
 | 
						|
			/* FALLTHROUGH */
 | 
						|
		case ('('):
 | 
						|
			if (term) 
 | 
						|
				return((int)(wp - sp));
 | 
						|
 | 
						|
			C2LIM(*wp, lim);
 | 
						|
			C2TERM(*wp, term);
 | 
						|
			wp++;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( ! isdigit((u_char)*wp))
 | 
						|
			return((int)(wp - sp));
 | 
						|
 | 
						|
		for (j = 0; isdigit((u_char)*wp); j++) {
 | 
						|
			if (lim && j >= lim)
 | 
						|
				break;
 | 
						|
			++wp;
 | 
						|
		}
 | 
						|
 | 
						|
		if (term && term < 3) {
 | 
						|
			if (1 == term && *wp != '\'')
 | 
						|
				return((int)(wp - sp));
 | 
						|
			if (2 == term && *wp != ']')
 | 
						|
				return((int)(wp - sp));
 | 
						|
			++wp;
 | 
						|
		}
 | 
						|
 | 
						|
		*d = DECO_SIZE;
 | 
						|
		return((int)(wp - sp));
 | 
						|
 | 
						|
	case ('['):
 | 
						|
		*word = ++wp;
 | 
						|
 | 
						|
		for (j = 0; *wp && ']' != *wp; wp++, j++)
 | 
						|
			/* Loop... */ ;
 | 
						|
 | 
						|
		if ('\0' == *wp)
 | 
						|
			return(j + 1);
 | 
						|
 | 
						|
		*d = DECO_SPECIAL;
 | 
						|
		*sz = (size_t)j;
 | 
						|
		return(j + 2);
 | 
						|
 | 
						|
	case ('c'):
 | 
						|
		*d = DECO_NOSPACE;
 | 
						|
		*sz = 1;
 | 
						|
		return(1);
 | 
						|
 | 
						|
	default:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	*d = DECO_SPECIAL;
 | 
						|
	*word = wp;
 | 
						|
	*sz = 1;
 | 
						|
	return(1);
 | 
						|
}
 |