=================================================================== RCS file: /cvs/mandoc/out.c,v retrieving revision 1.4 retrieving revision 1.14 diff -u -p -r1.4 -r1.14 --- mandoc/out.c 2009/10/09 06:54:11 1.4 +++ mandoc/out.c 2010/04/07 11:25:38 1.14 @@ -1,4 +1,4 @@ -/* $Id: out.c,v 1.4 2009/10/09 06:54:11 kristaps Exp $ */ +/* $Id: out.c,v 1.14 2010/04/07 11:25:38 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -14,42 +14,55 @@ * 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 +#include #include #include #include +#include +#include #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, which stipulates the following: - * - * (1) A scaling unit is a signed/unsigned integer/float with or - * without a unit. - * - * (2) The following units exist: - * c Centimeter - * i Inch - * P Pica = 1/6 inch - * p Point = 1/72 inch - * m Em = the font size in points (width of letter m) - * M 100th of an Em - * n En = Em/2 - * u Basic unit for actual output device - * v Vertical line space in basic units scaled point = - * 1/sizescale of a point (defined in font DESC file) - * f Scale by 65536. + * units are documented in groff.7, mdoc.7, man.7. */ int -a2roffsu(const char *src, struct roffsu *dst) +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) { @@ -63,6 +76,9 @@ a2roffsu(const char *src, struct roffsu *dst) break; } + if ('\0' == *src) + return(0); + while (i < BUFSIZ) { if ( ! isdigit((u_char)*src)) { if ('.' != *src) @@ -103,7 +119,10 @@ a2roffsu(const char *src, struct roffsu *dst) unit = SCALE_EM; break; case ('\0'): - /* FALLTHROUGH */ + if (SCALE_MAX == def) + return(0); + unit = SCALE_BU; + break; case ('u'): unit = SCALE_BU; break; @@ -122,5 +141,260 @@ a2roffsu(const char *src, struct roffsu *dst) 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, offs, term, lim; + char set; + const char *wp, *sp; + + *d = DECO_NONE; + wp = *word; + offs = 0; + + 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); }