import mdocml.

This commit is contained in:
Ben Gras 2010-06-26 02:20:06 +00:00
parent 2f3c9c04cc
commit 3f22092d45
72 changed files with 27719 additions and 1 deletions

View file

@ -17,7 +17,7 @@ SUBDIR= aal add_route adduser advent arp ash at autil awk \
intr ipcrm ipcs irdpd isoread join kill last leave \ intr ipcrm ipcs irdpd isoread join kill last leave \
less lex life loadkeys loadramdisk logger login look lp \ less lex life loadkeys loadramdisk logger login look lp \
lpd ls lspci M m4 mail make MAKEDEV man \ lpd ls lspci M m4 mail make MAKEDEV man \
mdb mesg mined mkdep mkdir mkdist mkfifo mkfs mknod \ mdb mdocml mesg mined mkdep mkdir mkdist mkfifo mkfs mknod \
mkproto modem mount mt netconf newroot nice nm nohup \ mkproto modem mount mt netconf newroot nice nm nohup \
nonamed od packit packman passwd paste patch pax \ nonamed od packit packman passwd paste patch pax \
ping postinstall poweroff pr prep printf printroot \ ping postinstall poweroff pr prep printf printroot \

View file

@ -0,0 +1,43 @@
<?xml version='1.0' encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:output encoding="utf-8" method="html" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
<xsl:template match="/changelog">
<html>
<head>
<title>mdocml - CVS-ChangeLog</title>
<link rel="stylesheet" href="index.css" type="text/css" media="all" />
</head>
<body>
<xsl:for-each select="entry">
<div class="clhead">
<xsl:text>Files modified by </xsl:text>
<xsl:value-of select="concat(author, ': ', date, ' (', time, ')')" />
</div>
<div class="clbody">
<strong>
<xsl:text>Note: </xsl:text>
</strong>
<xsl:value-of select="msg"/>
<ul class="clbody">
<xsl:for-each select="file">
<li>
<xsl:value-of select="name"/>
<span class="rev">
<xsl:text> &#8212; Rev: </xsl:text>
<xsl:value-of select="revision"/>
<xsl:text>, Status: </xsl:text>
<xsl:value-of select="cvsstate"/>
<xsl:if test="tag">
<xsl:text>, Tag: </xsl:text>
<xsl:value-of select="tag" />
</xsl:if>
</span>
</li>
</xsl:for-each>
</ul>
</div>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

36
commands/mdocml/Makefile Normal file
View file

@ -0,0 +1,36 @@
VERSION = 1.10.2
VDATE = 19 June 2010
VFLAGS = -DVERSION="\"$(VERSION)\""
.if ${CC} == gcc
WFLAGS = -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings
CFLAGS += -g $(WFLAGS) $(VFLAGS) -DHAVE_CONFIG_H
ARFLAGS = rs
.else
CFLAGS += ${VFLAGS} -DHAVE_CONFIG_H
ARFLAGS = r
.endif
# Specify this if you want to hard-code the operating system to appear
# in the lower-left hand corner of -mdoc manuals.
# CFLAGS += -DOSNAME="\"OpenBSD 4.5\""
# Unset this if you don't want Xo/Xc allowing split `It' lines, which
# breaks symmetry.
CFLAGS += -DUGLY
SRCS = main.c mdoc_term.c chars.c term.c tree.c compat.c \
man_term.c html.c mdoc_html.c man_html.c out.c \
term_ps.c term_ascii.c man_macro.c man.c man_hash.c \
man_validate.c man_action.c mandoc.c man_argv.c roff.c \
mdoc_macro.c mdoc.c mdoc_hash.c mdoc_strings.c mdoc_argv.c \
mdoc_validate.c mdoc_action.c lib.c att.c arch.c vol.c \
msec.c st.c
MAN = mandoc.1 mdoc.3 mdoc.7 mandoc_char.7 man.7 man.3 roff.7 roff.3
PROG = mandoc
.include <bsd.prog.mk>

38
commands/mdocml/arch.c Normal file
View file

@ -0,0 +1,38 @@
/* $Id: arch.c,v 1.8 2010/06/19 20:46:27 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 <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mdoc_a2arch(const char *p)
{
#include "arch.in"
return(NULL);
}

53
commands/mdocml/arch.in Normal file
View file

@ -0,0 +1,53 @@
/* $Id: arch.in,v 1.8 2010/06/19 20:46:27 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.
*/
/*
* This file defines the architecture token of the .Dt prologue macro.
* All architectures that your system supports (or the manuals of your
* system) should be included here. The right-hand-side is the
* formatted output.
*
* Be sure to escape strings.
*/
LINE("alpha", "Alpha")
LINE("amd64", "AMD64")
LINE("amiga", "Amiga")
LINE("arc", "ARC")
LINE("arm", "ARM")
LINE("armish", "ARMISH")
LINE("aviion", "AViiON")
LINE("hp300", "HP300")
LINE("hppa", "HPPA")
LINE("hppa64", "HPPA64")
LINE("i386", "i386")
LINE("landisk", "LANDISK")
LINE("loongson", "Loongson")
LINE("luna88k", "Luna88k")
LINE("mac68k", "Mac68k")
LINE("macppc", "MacPPC")
LINE("mvme68k", "MVME68k")
LINE("mvme88k", "MVME88k")
LINE("mvmeppc", "MVMEPPC")
LINE("pmax", "PMAX")
LINE("sgi", "SGI")
LINE("socppc", "SOCPPC")
LINE("sparc", "SPARC")
LINE("sparc64", "SPARC64")
LINE("sun3", "Sun3")
LINE("vax", "VAX")
LINE("zaurus", "Zaurus")

38
commands/mdocml/att.c Normal file
View file

@ -0,0 +1,38 @@
/* $Id: att.c,v 1.8 2010/06/19 20:46:27 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 <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mdoc_a2att(const char *p)
{
#include "att.in"
return(NULL);
}

37
commands/mdocml/att.in Normal file
View file

@ -0,0 +1,37 @@
/* $Id: att.in,v 1.6 2010/06/19 20:46:27 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.
*/
/*
* This file defines the AT&T versions of the .At macro. This probably
* isn't going to change. The right-hand side is the formatted string.
*
* Be sure to escape strings.
*/
LINE("v1", "Version 1 AT&T UNIX")
LINE("v2", "Version 2 AT&T UNIX")
LINE("v3", "Version 3 AT&T UNIX")
LINE("v4", "Version 4 AT&T UNIX")
LINE("v5", "Version 5 AT&T UNIX")
LINE("v6", "Version 6 AT&T UNIX")
LINE("v7", "Version 7 AT&T UNIX")
LINE("32v", "Version 32V AT&T UNIX")
LINE("V", "AT&T System V UNIX")
LINE("V.1", "AT&T System V.1 UNIX")
LINE("V.2", "AT&T System V.2 UNIX")
LINE("V.3", "AT&T System V.3 UNIX")
LINE("V.4", "AT&T System V.4 UNIX")

206
commands/mdocml/chars.c Normal file
View file

@ -0,0 +1,206 @@
/* $Id: chars.c,v 1.20 2010/06/19 20:46:27 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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "chars.h"
#define PRINT_HI 126
#define PRINT_LO 32
struct ln {
struct ln *next;
const char *code;
const char *ascii;
const char *html;
size_t codesz;
size_t asciisz;
size_t htmlsz;
int type;
#define CHARS_CHAR (1 << 0)
#define CHARS_STRING (1 << 1)
#define CHARS_BOTH (CHARS_CHAR | CHARS_STRING)
};
#define LINES_MAX 370
#define CHAR(w, x, y, z, a, b) \
{ NULL, (w), (y), (a), (x), (z), (b), CHARS_CHAR },
#define STRING(w, x, y, z, a, b) \
{ NULL, (w), (y), (a), (x), (z), (b), CHARS_STRING },
#define BOTH(w, x, y, z, a, b) \
{ NULL, (w), (y), (a), (x), (z), (b), CHARS_BOTH },
#define CHAR_TBL_START static struct ln lines[LINES_MAX] = {
#define CHAR_TBL_END };
#include "chars.in"
struct tbl {
enum chars type;
struct ln **htab;
};
#ifndef __GNUC__
#define inline
#endif
static inline int match(const struct ln *,
const char *, size_t, int);
static const char *find(struct tbl *, const char *,
size_t, size_t *, int);
void
chars_free(void *arg)
{
struct tbl *tab;
tab = (struct tbl *)arg;
free(tab->htab);
free(tab);
}
void *
chars_init(enum chars type)
{
struct tbl *tab;
struct ln **htab;
struct ln *pp;
int i, hash;
/*
* Constructs a very basic chaining hashtable. The hash routine
* is simply the integral value of the first character.
* Subsequent entries are chained in the order they're processed
* (they're in-line re-ordered during lookup).
*/
tab = malloc(sizeof(struct tbl));
if (NULL == tab) {
perror(NULL);
exit(EXIT_FAILURE);
}
htab = calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
if (NULL == htab) {
perror(NULL);
exit(EXIT_FAILURE);
}
for (i = 0; i < LINES_MAX; i++) {
hash = (int)lines[i].code[0] - PRINT_LO;
if (NULL == (pp = htab[hash])) {
htab[hash] = &lines[i];
continue;
}
for ( ; pp->next; pp = pp->next)
/* Scan ahead. */ ;
pp->next = &lines[i];
}
tab->htab = htab;
tab->type = type;
return(tab);
}
const char *
chars_a2ascii(void *arg, const char *p, size_t sz, size_t *rsz)
{
return(find((struct tbl *)arg, p, sz, rsz, CHARS_CHAR));
}
const char *
chars_a2res(void *arg, const char *p, size_t sz, size_t *rsz)
{
return(find((struct tbl *)arg, p, sz, rsz, CHARS_STRING));
}
static const char *
find(struct tbl *tab, const char *p, size_t sz, size_t *rsz, int type)
{
struct ln *pp, *prev;
struct ln **htab;
int hash;
assert(p);
assert(sz > 0);
if (p[0] < PRINT_LO || p[0] > PRINT_HI)
return(NULL);
/*
* Lookup the symbol in the symbol hash. See ascii2htab for the
* hashtable specs. This dynamically re-orders the hash chain
* to optimise for repeat hits.
*/
hash = (int)p[0] - PRINT_LO;
htab = tab->htab;
if (NULL == (pp = htab[hash]))
return(NULL);
for (prev = NULL; pp; pp = pp->next) {
if ( ! match(pp, p, sz, type)) {
prev = pp;
continue;
}
if (prev) {
prev->next = pp->next;
pp->next = htab[hash];
htab[hash] = pp;
}
if (CHARS_HTML == tab->type) {
*rsz = pp->htmlsz;
return(pp->html);
}
*rsz = pp->asciisz;
return(pp->ascii);
}
return(NULL);
}
static inline int
match(const struct ln *ln, const char *p, size_t sz, int type)
{
if ( ! (ln->type & type))
return(0);
if (ln->codesz != sz)
return(0);
return(0 == strncmp(ln->code, p, sz));
}

34
commands/mdocml/chars.h Normal file
View file

@ -0,0 +1,34 @@
/* $Id: chars.h,v 1.4 2010/06/19 20:46:27 kristaps Exp $ */
/*
* Copyright (c) 2008, 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.
*/
#ifndef CHARS_H
#define CHARS_H
__BEGIN_DECLS
enum chars {
CHARS_ASCII,
CHARS_HTML
};
void *chars_init(enum chars);
const char *chars_a2ascii(void *, const char *, size_t, size_t *);
const char *chars_a2res(void *, const char *, size_t, size_t *);
void chars_free(void *);
__END_DECLS
#endif /*!CHARS_H*/

446
commands/mdocml/chars.in Normal file
View file

@ -0,0 +1,446 @@
/* $Id: chars.in,v 1.25 2010/06/19 20:46:27 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.
*/
/*
* The ASCII translation tables. STRING corresponds to predefined
* strings (cf. mdoc_samples.7 and tmac/mdoc/doc-nroff). CHAR
* corresponds to special characters (cf. groff_char.7). BOTH contains
* sequences that are equivalent in both STRING and CHAR.
*
* Either way, the left-hand side corresponds to the input sequence (\x,
* \(xx, \*(xx and so on) whose length is listed second element. The
* right-hand side is what's produced by the front-end, with the fourth
* element being its length.
*
* XXX - C-escape strings!
* XXX - update LINES_MAX if adding more!
*/
/* Non-breaking, non-collapsing space uses unit separator. */
static const char ascii_nbrsp[2] = { ASCII_NBRSP, 0 };
CHAR_TBL_START
/* Spacing. */
CHAR("c", 1, "", 0, "", 0)
CHAR("0", 1, " ", 1, "&#8194;", 7)
CHAR(" ", 1, " ", 1, "&#8194;", 7)
CHAR("~", 1, ascii_nbrsp, 1, "&#160;", 6)
CHAR("%", 1, "", 0, "", 0)
CHAR("&", 1, "", 0, "", 0)
CHAR("^", 1, "", 0, "", 0)
CHAR("|", 1, "", 0, "", 0)
CHAR("}", 1, "", 0, "", 0)
/* Accents. */
CHAR("a\"", 2, "\"", 1, "&#779;", 6)
CHAR("a-", 2, "-", 1, "&#175;", 6)
CHAR("a.", 2, ".", 1, "&#729;", 6)
CHAR("a^", 2, "^", 1, "&#770;", 6)
BOTH("\'", 1, "\'", 1, "&#769;", 6)
BOTH("aa", 2, "\'", 1, "&#769;", 6)
BOTH("ga", 2, "`", 1, "&#768;", 6)
BOTH("`", 1, "`", 1, "&#768;", 6)
CHAR("ab", 2, "`", 1, "&#774;", 6)
CHAR("ac", 2, ",", 1, "&#807;", 6)
CHAR("ad", 2, "\"", 1, "&#776;", 6)
CHAR("ah", 2, "v", 1, "&#711;", 6)
CHAR("ao", 2, "o", 1, "&#730;", 6)
CHAR("a~", 2, "~", 1, "&#771;", 6)
CHAR("ho", 2, ",", 1, "&#808;", 6)
CHAR("ha", 2, "^", 1, "^", 1)
CHAR("ti", 2, "~", 1, "~", 1)
/* Quotes. */
CHAR("Bq", 2, ",,", 2, "&#8222;", 7)
CHAR("bq", 2, ",", 1, "&#8218;", 7)
BOTH("lq", 2, "``", 2, "&#8220;", 7)
BOTH("rq", 2, "\'\'", 2, "&#8221;", 7)
CHAR("oq", 2, "`", 1, "&#8216;", 7)
CHAR("cq", 2, "\'", 1, "&#8217;", 7)
CHAR("aq", 2, "\'", 1, "\'", 1)
CHAR("dq", 2, "\"", 1, "\"", 1)
CHAR("Fo", 2, "<<", 2, "&#171;", 6)
CHAR("Fc", 2, ">>", 2, "&#187;", 6)
CHAR("fo", 2, "<", 1, "&#8249;", 7)
CHAR("fc", 2, ">", 1, "&#8250;", 7)
/* Brackets. */
CHAR("lB", 2, "[", 1, "[", 1)
CHAR("rB", 2, "]", 1, "]", 1)
CHAR("lC", 2, "{", 1, "{", 1)
CHAR("rC", 2, "}", 1, "}", 1)
CHAR("la", 2, "<", 1, "&#10216;", 8)
CHAR("ra", 2, ">", 1, "&#10217;", 8)
CHAR("bv", 2, "|", 1, "&#9130;", 7)
CHAR("braceex", 7, "|", 1, "&#9130;", 7)
CHAR("bracketlefttp", 13, "|", 1, "&#9121;", 7)
CHAR("bracketleftbp", 13, "|", 1, "&#9123;", 7)
CHAR("bracketleftex", 13, "|", 1, "&#9122;", 7)
CHAR("bracketrighttp", 14, "|", 1, "&#9124;", 7)
CHAR("bracketrightbp", 14, "|", 1, "&#9126;", 7)
CHAR("bracketrightex", 14, "|", 1, "&#9125;", 7)
CHAR("lt", 2, ",-", 2, "&#9127;", 7)
CHAR("bracelefttp", 11, ",-", 2, "&#9127;", 7)
CHAR("lk", 2, "{", 1, "&#9128;", 7)
CHAR("braceleftmid", 12, "{", 1, "&#9128;", 7)
CHAR("lb", 2, ",-", 2, "&#9129;", 7)
CHAR("braceleftbp", 11, "`-", 2, "&#9129;", 7)
CHAR("braceleftex", 11, "|", 1, "&#9130;", 7)
CHAR("rt", 2, "-.", 2, "&#9131;", 7)
CHAR("bracerighttp", 12, "-.", 2, "&#9131;", 7)
CHAR("rk", 2, "}", 1, "&#9132;", 7)
CHAR("bracerightmid", 13, "}", 1, "&#9132;", 7)
CHAR("rb", 2, "-\'", 2, "&#9133;", 7)
CHAR("bracerightbp", 12, "-\'", 2, "&#9133;", 7)
CHAR("bracerightex", 12, "|", 1, "&#9130;", 7)
CHAR("parenlefttp", 11, "/", 1, "&#9115;", 7)
CHAR("parenleftbp", 11, "\\", 1, "&#9117;", 7)
CHAR("parenleftex", 11, "|", 1, "&#9116;", 7)
CHAR("parenrighttp", 12, "\\", 1, "&#9118;", 7)
CHAR("parenrightbp", 12, "/", 1, "&#9120;", 7)
CHAR("parenrightex", 12, "|", 1, "&#9119;", 7)
/* Greek characters. */
CHAR("*A", 2, "A", 1, "&#913;", 6)
CHAR("*B", 2, "B", 1, "&#914;", 6)
CHAR("*G", 2, "|", 1, "&#915;", 6)
CHAR("*D", 2, "/\\", 2, "&#916;", 6)
CHAR("*E", 2, "E", 1, "&#917;", 6)
CHAR("*Z", 2, "Z", 1, "&#918;", 6)
CHAR("*Y", 2, "H", 1, "&#919;", 6)
CHAR("*H", 2, "O", 1, "&#920;", 6)
CHAR("*I", 2, "I", 1, "&#921;", 6)
CHAR("*K", 2, "K", 1, "&#922;", 6)
CHAR("*L", 2, "/\\", 2, "&#923;", 6)
CHAR("*M", 2, "M", 1, "&#924;", 6)
CHAR("*N", 2, "N", 1, "&#925;", 6)
CHAR("*C", 2, "H", 1, "&#926;", 6)
CHAR("*O", 2, "O", 1, "&#927;", 6)
CHAR("*P", 2, "TT", 2, "&#928;", 6)
CHAR("*R", 2, "P", 1, "&#929;", 6)
CHAR("*S", 2, ">", 1, "&#931;", 6)
CHAR("*T", 2, "T", 1, "&#932;", 6)
CHAR("*U", 2, "Y", 1, "&#933;", 6)
CHAR("*F", 2, "O_", 1, "&#934;", 6)
CHAR("*X", 2, "X", 1, "&#935;", 6)
CHAR("*Q", 2, "Y", 1, "&#936;", 6)
CHAR("*W", 2, "O", 1, "&#937;", 6)
CHAR("*a", 2, "a", 1, "&#945;", 6)
CHAR("*b", 2, "B", 1, "&#946;", 6)
CHAR("*g", 2, "y", 1, "&#947;", 6)
CHAR("*d", 2, "d", 1, "&#948;", 6)
CHAR("*e", 2, "e", 1, "&#949;", 6)
CHAR("*z", 2, "C", 1, "&#950;", 6)
CHAR("*y", 2, "n", 1, "&#951;", 6)
CHAR("*h", 2, "0", 1, "&#952;", 6)
CHAR("*i", 2, "i", 1, "&#953;", 6)
CHAR("*k", 2, "k", 1, "&#954;", 6)
CHAR("*l", 2, "\\", 1, "&#955;", 6)
CHAR("*m", 2, "u", 1, "&#956;", 6)
CHAR("*n", 2, "v", 1, "&#957;", 6)
CHAR("*c", 2, "E", 1, "&#958;", 6)
CHAR("*o", 2, "o", 1, "&#959;", 6)
CHAR("*p", 2, "n", 1, "&#960;", 6)
CHAR("*r", 2, "p", 1, "&#961;", 6)
CHAR("*s", 2, "o", 1, "&#963;", 6)
CHAR("*t", 2, "t", 1, "&#964;", 6)
CHAR("*u", 2, "u", 1, "&#965;", 6)
CHAR("*f", 2, "o", 1, "&#981;", 6)
CHAR("*x", 2, "x", 1, "&#967;", 6)
CHAR("*q", 2, "u", 1, "&#968;", 6)
CHAR("*w", 2, "w", 1, "&#969;", 6)
CHAR("+h", 2, "0", 1, "&#977;", 6)
CHAR("+f", 2, "o", 1, "&#966;", 6)
CHAR("+p", 2, "w", 1, "&#982;", 6)
CHAR("+e", 2, "e", 1, "&#1013;", 7)
CHAR("ts", 2, "s", 1, "&#962;", 6)
/* Accented letters. */
CHAR(",C", 2, "C", 1, "&#199;", 6)
CHAR(",c", 2, "c", 1, "&#231;", 6)
CHAR("/L", 2, "L", 1, "&#321;", 6)
CHAR("/O", 2, "O", 1, "&#216;", 6)
CHAR("/l", 2, "l", 1, "&#322;", 6)
CHAR("/o", 2, "o", 1, "&#248;", 6)
CHAR("oA", 2, "A", 1, "&#197;", 6)
CHAR("oa", 2, "a", 1, "&#229;", 6)
CHAR(":A", 2, "A", 1, "&#196;", 6)
CHAR(":E", 2, "E", 1, "&#203;", 6)
CHAR(":I", 2, "I", 1, "&#207;", 6)
CHAR(":O", 2, "O", 1, "&#214;", 6)
CHAR(":U", 2, "U", 1, "&#220;", 6)
CHAR(":a", 2, "a", 1, "&#228;", 6)
CHAR(":e", 2, "e", 1, "&#235;", 6)
CHAR(":i", 2, "i", 1, "&#239;", 6)
CHAR(":o", 2, "o", 1, "&#245;", 6)
CHAR(":u", 2, "u", 1, "&#252;", 6)
CHAR(":y", 2, "y", 1, "&#255;", 6)
CHAR("\'A", 2, "A", 1, "&#193;", 6)
CHAR("\'E", 2, "E", 1, "&#201;", 6)
CHAR("\'I", 2, "I", 1, "&#205;", 6)
CHAR("\'O", 2, "O", 1, "&#211;", 6)
CHAR("\'U", 2, "U", 1, "&#218;", 6)
CHAR("\'a", 2, "a", 1, "&#225;", 6)
CHAR("\'e", 2, "e", 1, "&#233;", 6)
CHAR("\'i", 2, "i", 1, "&#237;", 6)
CHAR("\'o", 2, "o", 1, "&#243;", 6)
CHAR("\'u", 2, "u", 1, "&#250;", 6)
CHAR("^A", 2, "A", 1, "&#194;", 6)
CHAR("^E", 2, "E", 1, "&#202;", 6)
CHAR("^I", 2, "I", 1, "&#206;", 6)
CHAR("^O", 2, "O", 1, "&#212;", 6)
CHAR("^U", 2, "U", 1, "&#219;", 6)
CHAR("^a", 2, "a", 1, "&#226;", 6)
CHAR("^e", 2, "e", 1, "&#234;", 6)
CHAR("^i", 2, "i", 1, "&#238;", 6)
CHAR("^o", 2, "o", 1, "&#244;", 6)
CHAR("^u", 2, "u", 1, "&#251;", 6)
CHAR("`A", 2, "A", 1, "&#192;", 6)
CHAR("`E", 2, "E", 1, "&#200;", 6)
CHAR("`I", 2, "I", 1, "&#204;", 6)
CHAR("`O", 2, "O", 1, "&#210;", 6)
CHAR("`U", 2, "U", 1, "&#217;", 6)
CHAR("`a", 2, "a", 1, "&#224;", 6)
CHAR("`e", 2, "e", 1, "&#232;", 6)
CHAR("`i", 2, "i", 1, "&#236;", 6)
CHAR("`o", 2, "o", 1, "&#242;", 6)
CHAR("`u", 2, "u", 1, "&#249;", 6)
CHAR("~A", 2, "A", 1, "&#195;", 6)
CHAR("~N", 2, "N", 1, "&#209;", 6)
CHAR("~O", 2, "O", 1, "&#213;", 6)
CHAR("~a", 2, "a", 1, "&#227;", 6)
CHAR("~n", 2, "n", 1, "&#241;", 6)
CHAR("~o", 2, "o", 1, "&#245;", 6)
/* Arrows and lines. */
CHAR("<-", 2, "<-", 2, "&#8592;", 7)
CHAR("->", 2, "->", 2, "&#8594;", 7)
CHAR("<>", 2, "<>", 2, "&#8596;", 7)
CHAR("da", 2, "v", 1, "&#8595;", 7)
BOTH("ua", 2, "^", 1, "&#8593;", 7)
BOTH("va", 2, "^v", 2, "&#8597;", 7)
CHAR("lA", 2, "<=", 2, "&#8656;", 7)
CHAR("rA", 2, "=>", 2, "&#8658;", 7)
CHAR("hA", 2, "<=>", 3, "&#8660;", 7)
CHAR("dA", 2, "v", 1, "&#8659;", 7)
CHAR("uA", 2, "^", 1, "&#8657;", 7)
CHAR("vA", 2, "^=v", 3, "&#8661;", 7)
/* Logic. */
CHAR("AN", 2, "^", 1, "&#8743;", 7)
CHAR("OR", 2, "v", 1, "&#8744;", 7)
CHAR("no", 2, "~", 1, "&#172;", 6)
CHAR("tno", 3, "~", 1, "&#172;", 6)
CHAR("te", 2, "3", 1, "&#8707;", 7)
CHAR("fa", 2, "V", 1, "&#8704;", 7)
CHAR("st", 2, "-)", 2, "&#8715;", 7)
CHAR("tf", 2, ".:.", 3, "&#8756;", 7)
CHAR("3d", 2, ".:.", 3, "&#8756;", 7)
CHAR("or", 2, "|", 1, "|", 1)
/* Mathematicals. */
CHAR("pl", 2, "+", 1, "&#43;", 5)
CHAR("mi", 2, "-", 1, "&#8722;", 7)
CHAR("-", 1, "-", 1, "-", 1)
CHAR("-+", 2, "-+", 2, "&#8723;", 7)
CHAR("+-", 2, "+-", 2, "&#177;", 6)
CHAR("t+-", 3, "+-", 2, "&#177;", 6)
CHAR("pc", 2, ".", 1, "&#183;", 6)
CHAR("md", 2, ".", 1, "&#8901;", 7)
CHAR("mu", 2, "x", 1, "&#215;", 6)
CHAR("tmu", 3, "x", 1, "&#215;", 6)
CHAR("c*", 2, "x", 1, "&#8855;", 7)
CHAR("c+", 2, "+", 1, "&#8853;", 7)
CHAR("di", 2, "-:-", 3, "&#247;", 6)
CHAR("tdi", 3, "-:-", 3, "&#247;", 6)
CHAR("f/", 2, "/", 1, "&#8260;", 7)
CHAR("**", 2, "*", 1, "&#8727;", 7)
BOTH("<=", 2, "<=", 2, "&#8804;", 7)
BOTH(">=", 2, ">=", 2, "&#8805;", 7)
CHAR("<<", 2, "<<", 2, "&#8810;", 7)
CHAR(">>", 2, ">>", 2, "&#8811;", 7)
CHAR("eq", 2, "=", 1, "&#61;", 5)
CHAR("!=", 2, "!=", 2, "&#8800;", 7)
CHAR("==", 2, "==", 2, "&#8801;", 7)
CHAR("ne", 2, "!==", 3, "&#8802;", 7)
CHAR("=~", 2, "=~", 2, "&#8773;", 7)
CHAR("-~", 2, "-~", 2, "&#8771;", 7)
CHAR("ap", 2, "~", 1, "&#8764;", 7)
CHAR("~~", 2, "~~", 2, "&#8776;", 7)
CHAR("~=", 2, "~=", 2, "&#8780;", 7)
CHAR("pt", 2, "oc", 2, "&#8733;", 7)
CHAR("es", 2, "{}", 2, "&#8709;", 7)
CHAR("mo", 2, "E", 1, "&#8712;", 7)
CHAR("nm", 2, "!E", 2, "&#8713;", 7)
CHAR("sb", 2, "(=", 2, "&#8834;", 7)
CHAR("nb", 2, "(!=", 3, "&#8836;", 7)
CHAR("sp", 2, "=)", 2, "&#8835;", 7)
CHAR("nc", 2, "!=)", 3, "&#8837;", 7)
CHAR("ib", 2, "(=", 2, "&#8838;", 7)
CHAR("ip", 2, "=)", 2, "&#8839;", 7)
CHAR("ca", 2, "(^)", 3, "&#8745;", 7)
CHAR("cu", 2, "U", 1, "&#8746;", 7)
CHAR("/_", 2, "/_", 2, "&#8736;", 7)
CHAR("pp", 2, "_|_", 3, "&#8869;", 7)
CHAR("is", 2, "I", 1, "&#8747;", 7)
CHAR("integral", 8, "I", 1, "&#8747;", 7)
CHAR("sum", 3, "E", 1, "&#8721;", 7)
CHAR("product", 7, "TT", 2, "&#8719;", 7)
CHAR("coproduct", 9, "U", 1, "&#8720;", 7)
CHAR("gr", 2, "V", 1, "&#8711;", 7)
CHAR("sr", 2, "\\/", 2, "&#8730;", 7)
CHAR("sqrt", 4, "\\/", 2, "&#8730;", 7)
CHAR("lc", 2, "|~", 2, "&#8968;", 7)
CHAR("rc", 2, "~|", 2, "&#8969;", 7)
CHAR("lf", 2, "|_", 2, "&#8970;", 7)
CHAR("rf", 2, "_|", 2, "&#8971;", 7)
CHAR("if", 2, "oo", 2, "&#8734;", 7)
CHAR("Ah", 2, "N", 1, "&#8501;", 7)
CHAR("Im", 2, "I", 1, "&#8465;", 7)
CHAR("Re", 2, "R", 1, "&#8476;", 7)
CHAR("pd", 2, "a", 1, "&#8706;", 7)
CHAR("-h", 2, "/h", 2, "&#8463;", 7)
/* Ligatures. */
CHAR("ff", 2, "ff", 2, "&#64256;", 8)
CHAR("fi", 2, "fi", 2, "&#64257;", 8)
CHAR("fl", 2, "fl", 2, "&#64258;", 8)
CHAR("Fi", 2, "ffi", 3, "&#64259;", 8)
CHAR("Fl", 2, "ffl", 3, "&#64260;", 8)
BOTH("AE", 2, "AE", 2, "&#198;", 6)
BOTH("ae", 2, "ae", 2, "&#230;", 6)
CHAR("OE", 2, "OE", 2, "&#338;", 6)
CHAR("oe", 2, "oe", 2, "&#339;", 6)
CHAR("ss", 2, "ss", 2, "&#223;", 6)
CHAR("IJ", 2, "IJ", 2, "&#306;", 6)
CHAR("ij", 2, "ij", 2, "&#307;", 6)
/* Special letters. */
CHAR("-D", 2, "D", 1, "&#208;", 6)
CHAR("Sd", 2, "o", 1, "&#240;", 6)
CHAR("TP", 2, "b", 1, "&#222;", 6)
CHAR("Tp", 2, "b", 1, "&#254;", 6)
CHAR(".i", 2, "i", 1, "&#305;", 6)
CHAR(".j", 2, "j", 1, "&#567;", 6)
/* Currency. */
CHAR("Do", 2, "$", 1, "$", 1)
CHAR("ct", 2, "c", 1, "&#162;", 6)
CHAR("Eu", 2, "EUR", 3, "&#8364;", 7)
CHAR("eu", 2, "EUR", 3, "&#8364;", 7)
CHAR("Ye", 2, "Y", 1, "&#165;", 6)
CHAR("Po", 2, "L", 1, "&#163;", 6)
CHAR("Cs", 2, "x", 1, "&#164;", 6)
CHAR("Fn", 2, "f", 1, "&#402;", 6)
/* pod2man holdovers. */
STRING("--", 2, "--", 2, "&#8212;", 7)
STRING("PI", 2, "pi", 2, "&#960;", 6)
STRING("L\"", 2, "``", 2, "&#8220;", 7)
STRING("R\"", 2, "\'\'", 2, "&#8221;", 7)
STRING("C+", 2, "C++", 3, "C++", 3)
STRING("C`", 2, "`", 1, "&#8216;", 7)
STRING("C\'", 2, "\'", 1, "&#8217;", 7)
STRING("Aq", 2, "\'", 1, "\'", 1)
STRING("^", 1, "^", 1, "^", 1)
STRING(",", 1, ",", 1, ",", 1)
STRING("~", 1, "~", 1, "~", 1)
STRING("/", 1, "/", 1, "/", 1)
STRING(":", 1, "\"", 1, "&#776;", 6)
STRING("8", 1, "B", 1, "&#946;", 6)
STRING("o", 1, "o", 1, "&#176;", 6)
STRING("D-", 2, "D", 1, "&#208;", 6)
STRING("d-", 2, "o", 1, "&#240;", 6)
STRING("Th", 2, "b", 1, "&#222;", 6)
STRING("th", 2, "b", 1, "&#254;", 6)
/* Old style. */
STRING("Am", 2, "&", 1, "&amp;", 5)
STRING("Ba", 2, "|", 1, "|", 1)
STRING("Ge", 2, ">=", 2, "&#8805;", 7)
STRING("Gt", 2, ">", 1, "&gt;", 4)
STRING("If", 2, "infinity", 8, "infinity", 8)
STRING("Le", 2, "<=", 2, "&#8804;", 7)
STRING("Lq", 2, "``", 2, "&#8220;", 7)
STRING("Lt", 2, "<", 1, "&lt;", 4)
STRING("Na", 2, "NaN", 3, "NaN", 3)
STRING("Ne", 2, "!=", 2, "&#8800;", 7)
STRING("Pi", 2, "pi", 2, "&#960;", 6)
STRING("Pm", 2, "+-", 2, "&#177;", 6)
STRING("R", 1, "(R)", 3, "&#174;", 6)
STRING("Rq", 2, "\'\'", 2, "&#8221;", 7)
STRING("Tm", 2, "tm", 2, "&#8482;", 7)
STRING("left-bracket", 12, "[", 1, "[", 1)
STRING("left-parenthesis", 16, "(", 1, "(", 1)
STRING("left-singlequote", 16, "`", 1, "&#8216;", 7)
STRING("lp", 2, "(", 1, "(", 1)
STRING("q", 1, "\"", 1, "&quot;", 6)
STRING("quote-left", 10, "`", 1, "&#8216;", 7)
STRING("quote-right", 11, "\'", 1, "&#8217;", 7)
STRING("right-bracket", 13, "]", 1, "]", 1)
STRING("right-parenthesis", 17, ")", 1, ")", 1)
STRING("right-singlequote", 17, "\'", 1, "&#8217;", 7)
STRING("rp", 2, ")", 1, ")", 1)
/* Lines. */
CHAR("ba", 2, "|", 1, "&#124;", 6)
CHAR("br", 2, "|", 1, "&#9474;", 7)
CHAR("ul", 2, "_", 1, "&#95;", 5)
CHAR("rl", 2, "-", 1, "&#8254;", 7)
CHAR("bb", 2, "|", 1, "&#166;", 6)
CHAR("sl", 2, "/", 1, "&#47;", 5)
CHAR("rs", 2, "\\", 1, "&#92;", 5)
/* Text markers. */
CHAR("ci", 2, "o", 1, "&#9675;", 7)
CHAR("bu", 2, "o", 1, "&#8226;", 7)
CHAR("dd", 2, "=", 1, "&#8225;", 7)
CHAR("dg", 2, "-", 1, "&#8224;", 7)
CHAR("lz", 2, "<>", 2, "&#9674;", 7)
CHAR("sq", 2, "[]", 2, "&#9633;", 7)
CHAR("ps", 2, "9|", 2, "&#182;", 6)
CHAR("sc", 2, "S", 1, "&#167;", 6)
CHAR("lh", 2, "<=", 2, "&#9756;", 7)
CHAR("rh", 2, "=>", 2, "&#9758;", 7)
CHAR("at", 2, "@", 1, "&#64;", 5)
CHAR("sh", 2, "#", 1, "&#35;", 5)
CHAR("CR", 2, "_|", 2, "&#8629;", 7)
CHAR("OK", 2, "\\/", 2, "&#10003;", 8)
/* Legal symbols. */
CHAR("co", 2, "(C)", 3, "&#169;", 6)
CHAR("rg", 2, "(R)", 3, "&#174;", 6)
CHAR("tm", 2, "tm", 2, "&#8482;", 7)
/* Punctuation. */
CHAR(".", 1, ".", 1, ".", 1)
CHAR("r!", 2, "i", 1, "&#161;", 6)
CHAR("r?", 2, "c", 1, "&#191;", 6)
CHAR("em", 2, "--", 2, "&#8212;", 7)
CHAR("en", 2, "-", 1, "&#8211;", 7)
CHAR("hy", 2, "-", 1, "&#8208;", 7)
CHAR("e", 1, "\\", 1, "\\", 1)
/* Units. */
CHAR("de", 2, "o", 1, "&#176;", 6)
CHAR("%0", 2, "%o", 2, "&#8240;", 7)
CHAR("fm", 2, "\'", 1, "&#8242;", 7)
CHAR("sd", 2, "\"", 1, "&#8243;", 7)
CHAR("mc", 2, "mu", 2, "&#181;", 6)
CHAR_TBL_END

95
commands/mdocml/compat.c Normal file
View file

@ -0,0 +1,95 @@
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* 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 <string.h>
int dummy; /* To prevent an empty object file */
#ifndef HAVE_STRLCAT
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
#endif
#ifndef HAVE_STRLCPY
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
#endif

20
commands/mdocml/config.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef MANDOC_CONFIG_H
#define MANDOC_CONFIG_H
#define HAVE_STRLCAT
#define HAVE_STRLCPY
#include <sys/types.h>
#include <sys/cdefs.h>
#ifndef __GNUC__
#define inline
#endif
#ifndef _DIAGASSERT
#define _DIAGASSERT assert
#endif
#endif /* MANDOC_CONFIG_H */

View file

@ -0,0 +1,72 @@
/* $Id: example.style.css,v 1.20 2010/04/08 08:17:55 kristaps Exp $ */
div.body { font-family: monospace;
min-width: 580px; width: 580px; } /* Top-most div tag. */
div.sec-head { font-weight: bold; font-style: normal; } /* Sections (Sh). */
div.sec-body { }
div.sec-block { }
div.ssec-head { font-weight: bold; font-style: normal; } /* Sub-sections (Ss). */
div.ssec-body { }
div.ssec-block { }
span.addr { } /* Address (Ad). */
span.arg { font-style: italic; font-weight: normal; } /* Command argument (Ar). */
span.author { } /* Author name (An). */
span.bold { font-weight: bold; font-style: normal; } /* Generically bold (SB, BI, IB, BR, RB, B). */
span.cmd { font-weight: bold; font-style: normal; } /* Command (Cm). */
span.config { font-weight: bold; font-style: normal; } /* Config statement (Cd). */
span.define { } /* Defines (Dv). */
span.desc { } /* Nd. After em-dash. */
span.diag { font-weight: bold; font-style: normal; } /* Diagnostic (Bl -diag). */
span.emph { font-style: italic; font-weight: normal; } /* Emphasis (Em). */
span.env { } /* Environment variables (Ev). */
span.errno { } /* Error string (Er). */
span.farg { font-style: italic; font-weight: normal; } /* Function argument (Fa, Fn). */
span.file { font-style: italic; font-weight: normal; } /* File (Pa). */
span.flag { font-weight: bold; font-style: normal; } /* Flag (Fl, Cm). */
span.fname { font-weight: bold; font-style: normal; } /* Function name (Fa, Fn, Rv). */
span.ftype { font-style: italic; font-weight: normal; } /* Function types (Ft, Fn). */
span.includes { font-weight: bold; font-style: normal; } /* Header includes (In). */
span.italic { font-style: italic; font-weight: normal; } /* Generically italic (BI, IB, I). */
span.lib { } /* Library (Lb). */
span.lit { } /* Literals (Bf -literal). */
span.macro { font-weight: bold; font-style: normal; } /* Macro-ish thing (Fd). */
span.name { font-weight: bold; font-style: normal; } /* Name of utility (Nm). */
span.opt { } /* Options (Op, Oo/Oc). */
span.ref { } /* Citations (Rs). */
span.ref-auth { } /* Reference author (%A). */
span.ref-book { font-style: italic; font-weight: normal; } /* Reference book (%B). */
span.ref-city { } /* Reference city (%C). */
span.ref-date { } /* Reference date (%D). */
span.ref-issue { font-style: italic; font-weight: normal; } /* Reference issuer/publisher (%I). */
span.ref-jrnl { font-style: italic; font-weight: normal; } /* Reference journal (%J). */
span.ref-num { } /* Reference number (%N). */
span.ref-opt { } /* Reference optionals (%O). */
span.ref-page { } /* Reference page (%P). */
span.ref-corp { } /* Reference corporate/foreign author (%Q). */
span.ref-rep { } /* Reference report (%R). */
span.ref-title { text-decoration: underline; } /* Reference title (%T). */
span.ref-vol { } /* Reference volume (%V). */
span.roman { font-style: normal; font-weight: normal; } /* Generic font. */
span.small { font-size: smaller; } /* Generically small (SB, SM). */
span.symb { font-weight: bold; font-style: normal; } /* Symbols. */
span.type { font-style: italic; font-weight: normal; } /* Variable types (Vt). */
span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */
span.utility { font-weight: bold; font-style: normal; } /* Name of utility (Ex). */
span.var { font-weight: bold; font-style: normal; } /* Variables (Rv). */
a.link-ext { } /* Off-site link (Lk). */
a.link-includes { } /* Include-file link (In). */
a.link-mail { } /* Mailto links (Mt). */
a.link-man { } /* Manual links (Xr). */
a.link-ref { } /* Reference section links (%Q). */
a.link-sec { } /* Section links (Sx). */
div.emph { font-style: italic; font-weight: normal; } /* Emphasis (Bl -emphasis). */
div.lit { } /* Literal (D1, Bd -literal, Dl, Bd -literal). */
div.symb { font-weight: bold; font-style: normal; } /* Symbols (Bl -symbolic). */
table.footer { } /* Document footer. */
table.header { } /* Document header. */

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

754
commands/mdocml/html.c Normal file
View file

@ -0,0 +1,754 @@
/* $Id: html.c,v 1.102 2010/06/19 20:46:27 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mandoc.h"
#include "out.h"
#include "chars.h"
#include "html.h"
#include "main.h"
struct htmldata {
const char *name;
int flags;
#define HTML_CLRLINE (1 << 0)
#define HTML_NOSTACK (1 << 1)
#define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */
};
static const struct htmldata htmltags[TAG_MAX] = {
{"html", HTML_CLRLINE}, /* TAG_HTML */
{"head", HTML_CLRLINE}, /* TAG_HEAD */
{"body", HTML_CLRLINE}, /* TAG_BODY */
{"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */
{"title", HTML_CLRLINE}, /* TAG_TITLE */
{"div", HTML_CLRLINE}, /* TAG_DIV */
{"h1", 0}, /* TAG_H1 */
{"h2", 0}, /* TAG_H2 */
{"span", 0}, /* TAG_SPAN */
{"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */
{"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */
{"a", 0}, /* TAG_A */
{"table", HTML_CLRLINE}, /* TAG_TABLE */
{"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */
{"tr", HTML_CLRLINE}, /* TAG_TR */
{"td", HTML_CLRLINE}, /* TAG_TD */
{"li", HTML_CLRLINE}, /* TAG_LI */
{"ul", HTML_CLRLINE}, /* TAG_UL */
{"ol", HTML_CLRLINE}, /* TAG_OL */
};
static const char *const htmlfonts[HTMLFONT_MAX] = {
"roman",
"bold",
"italic"
};
static const char *const htmlattrs[ATTR_MAX] = {
"http-equiv",
"content",
"name",
"rel",
"href",
"type",
"media",
"class",
"style",
"width",
"valign",
"target",
"id",
"summary",
};
static void print_spec(struct html *, const char *, size_t);
static void print_res(struct html *, const char *, size_t);
static void print_ctag(struct html *, enum htmltag);
static void print_doctype(struct html *);
static void print_xmltype(struct html *);
static int print_encode(struct html *, const char *, int);
static void print_metaf(struct html *, enum roffdeco);
static void print_attr(struct html *,
const char *, const char *);
static void *ml_alloc(char *, enum htmltype);
static void *
ml_alloc(char *outopts, enum htmltype type)
{
struct html *h;
const char *toks[4];
char *v;
toks[0] = "style";
toks[1] = "man";
toks[2] = "includes";
toks[3] = NULL;
h = calloc(1, sizeof(struct html));
if (NULL == h) {
perror(NULL);
exit(EXIT_FAILURE);
}
h->type = type;
h->tags.head = NULL;
h->ords.head = NULL;
h->symtab = chars_init(CHARS_HTML);
while (outopts && *outopts)
switch (getsubopt(&outopts, UNCONST(toks), &v)) {
case (0):
h->style = v;
break;
case (1):
h->base_man = v;
break;
case (2):
h->base_includes = v;
break;
default:
break;
}
return(h);
}
void *
html_alloc(char *outopts)
{
return(ml_alloc(outopts, HTML_HTML_4_01_STRICT));
}
void *
xhtml_alloc(char *outopts)
{
return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT));
}
void
html_free(void *p)
{
struct tag *tag;
struct ord *ord;
struct html *h;
h = (struct html *)p;
while ((ord = h->ords.head) != NULL) {
h->ords.head = ord->next;
free(ord);
}
while ((tag = h->tags.head) != NULL) {
h->tags.head = tag->next;
free(tag);
}
if (h->symtab)
chars_free(h->symtab);
free(h);
}
void
print_gen_head(struct html *h)
{
struct htmlpair tag[4];
tag[0].key = ATTR_HTTPEQUIV;
tag[0].val = "Content-Type";
tag[1].key = ATTR_CONTENT;
tag[1].val = "text/html; charset=utf-8";
print_otag(h, TAG_META, 2, tag);
tag[0].key = ATTR_NAME;
tag[0].val = "resource-type";
tag[1].key = ATTR_CONTENT;
tag[1].val = "document";
print_otag(h, TAG_META, 2, tag);
if (h->style) {
tag[0].key = ATTR_REL;
tag[0].val = "stylesheet";
tag[1].key = ATTR_HREF;
tag[1].val = h->style;
tag[2].key = ATTR_TYPE;
tag[2].val = "text/css";
tag[3].key = ATTR_MEDIA;
tag[3].val = "all";
print_otag(h, TAG_LINK, 4, tag);
}
}
static void
print_spec(struct html *h, const char *p, size_t len)
{
const char *rhs;
size_t sz;
rhs = chars_a2ascii(h->symtab, p, len, &sz);
if (NULL == rhs)
return;
fwrite(rhs, 1, sz, stdout);
}
static void
print_res(struct html *h, const char *p, size_t len)
{
const char *rhs;
size_t sz;
rhs = chars_a2res(h->symtab, p, len, &sz);
if (NULL == rhs)
return;
fwrite(rhs, 1, sz, stdout);
}
struct tag *
print_ofont(struct html *h, enum htmlfont font)
{
struct htmlpair tag;
h->metal = h->metac;
h->metac = font;
/* FIXME: DECO_ROMAN should just close out preexisting. */
if (h->metaf && h->tags.head == h->metaf)
print_tagq(h, h->metaf);
PAIR_CLASS_INIT(&tag, htmlfonts[font]);
h->metaf = print_otag(h, TAG_SPAN, 1, &tag);
return(h->metaf);
}
static void
print_metaf(struct html *h, enum roffdeco deco)
{
enum htmlfont font;
switch (deco) {
case (DECO_PREVIOUS):
font = h->metal;
break;
case (DECO_ITALIC):
font = HTMLFONT_ITALIC;
break;
case (DECO_BOLD):
font = HTMLFONT_BOLD;
break;
case (DECO_ROMAN):
font = HTMLFONT_NONE;
break;
default:
abort();
/* NOTREACHED */
}
(void)print_ofont(h, font);
}
static int
print_encode(struct html *h, const char *p, int norecurse)
{
size_t sz;
int len, nospace;
const char *seq;
enum roffdeco deco;
static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' };
nospace = 0;
for (; *p; p++) {
sz = strcspn(p, rejs);
fwrite(p, 1, sz, stdout);
p += /* LINTED */
sz;
if ('<' == *p) {
printf("&lt;");
continue;
} else if ('>' == *p) {
printf("&gt;");
continue;
} else if ('&' == *p) {
printf("&amp;");
continue;
} else if (ASCII_HYPH == *p) {
/*
* Note: "soft hyphens" aren't graphically
* displayed when not breaking the text; we want
* them to be displayed.
*/
/*printf("&#173;");*/
putchar('-');
continue;
} else if ('\0' == *p)
break;
seq = ++p;
len = a2roffdeco(&deco, &seq, &sz);
switch (deco) {
case (DECO_RESERVED):
print_res(h, seq, sz);
break;
case (DECO_SPECIAL):
print_spec(h, seq, sz);
break;
case (DECO_PREVIOUS):
/* FALLTHROUGH */
case (DECO_BOLD):
/* FALLTHROUGH */
case (DECO_ITALIC):
/* FALLTHROUGH */
case (DECO_ROMAN):
if (norecurse)
break;
print_metaf(h, deco);
break;
default:
break;
}
p += len - 1;
if (DECO_NOSPACE == deco && '\0' == *(p + 1))
nospace = 1;
}
return(nospace);
}
static void
print_attr(struct html *h, const char *key, const char *val)
{
printf(" %s=\"", key);
(void)print_encode(h, val, 1);
putchar('\"');
}
struct tag *
print_otag(struct html *h, enum htmltag tag,
int sz, const struct htmlpair *p)
{
int i;
struct tag *t;
/* Push this tags onto the stack of open scopes. */
if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
t = malloc(sizeof(struct tag));
if (NULL == t) {
perror(NULL);
exit(EXIT_FAILURE);
}
t->tag = tag;
t->next = h->tags.head;
h->tags.head = t;
} else
t = NULL;
if ( ! (HTML_NOSPACE & h->flags))
if ( ! (HTML_CLRLINE & htmltags[tag].flags))
putchar(' ');
/* Print out the tag name and attributes. */
printf("<%s", htmltags[tag].name);
for (i = 0; i < sz; i++)
print_attr(h, htmlattrs[p[i].key], p[i].val);
/* Add non-overridable attributes. */
if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) {
print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml");
print_attr(h, "xml:lang", "en");
print_attr(h, "lang", "en");
}
/* Accomodate for XML "well-formed" singleton escaping. */
if (HTML_AUTOCLOSE & htmltags[tag].flags)
switch (h->type) {
case (HTML_XHTML_1_0_STRICT):
putchar('/');
break;
default:
break;
}
putchar('>');
h->flags |= HTML_NOSPACE;
return(t);
}
static void
print_ctag(struct html *h, enum htmltag tag)
{
printf("</%s>", htmltags[tag].name);
if (HTML_CLRLINE & htmltags[tag].flags) {
h->flags |= HTML_NOSPACE;
putchar('\n');
}
}
void
print_gen_decls(struct html *h)
{
print_xmltype(h);
print_doctype(h);
}
static void
print_xmltype(struct html *h)
{
if (HTML_XHTML_1_0_STRICT == h->type)
printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
static void
print_doctype(struct html *h)
{
const char *doctype;
const char *dtd;
const char *name;
switch (h->type) {
case (HTML_HTML_4_01_STRICT):
name = "HTML";
doctype = "-//W3C//DTD HTML 4.01//EN";
dtd = "http://www.w3.org/TR/html4/strict.dtd";
break;
default:
name = "html";
doctype = "-//W3C//DTD XHTML 1.0 Strict//EN";
dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
break;
}
printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n",
name, doctype, dtd);
}
void
print_text(struct html *h, const char *p)
{
if (*p && 0 == *(p + 1))
switch (*p) {
case('.'):
/* FALLTHROUGH */
case(','):
/* FALLTHROUGH */
case(';'):
/* FALLTHROUGH */
case(':'):
/* FALLTHROUGH */
case('?'):
/* FALLTHROUGH */
case('!'):
/* FALLTHROUGH */
case(')'):
/* FALLTHROUGH */
case(']'):
if ( ! (HTML_IGNDELIM & h->flags))
h->flags |= HTML_NOSPACE;
break;
default:
break;
}
if ( ! (h->flags & HTML_NOSPACE))
putchar(' ');
assert(p);
if ( ! print_encode(h, p, 0))
h->flags &= ~HTML_NOSPACE;
/*
* Note that we don't process the pipe: the parser sees it as
* punctuation, but we don't in terms of typography.
*/
if (*p && 0 == *(p + 1))
switch (*p) {
case('('):
/* FALLTHROUGH */
case('['):
h->flags |= HTML_NOSPACE;
break;
default:
break;
}
}
void
print_tagq(struct html *h, const struct tag *until)
{
struct tag *tag;
while ((tag = h->tags.head) != NULL) {
if (tag == h->metaf)
h->metaf = NULL;
print_ctag(h, tag->tag);
h->tags.head = tag->next;
free(tag);
if (until && tag == until)
return;
}
}
void
print_stagq(struct html *h, const struct tag *suntil)
{
struct tag *tag;
while ((tag = h->tags.head) != NULL) {
if (suntil && tag == suntil)
return;
if (tag == h->metaf)
h->metaf = NULL;
print_ctag(h, tag->tag);
h->tags.head = tag->next;
free(tag);
}
}
void
bufinit(struct html *h)
{
h->buf[0] = '\0';
h->buflen = 0;
}
void
bufcat_style(struct html *h, const char *key, const char *val)
{
bufcat(h, key);
bufncat(h, ":", 1);
bufcat(h, val);
bufncat(h, ";", 1);
}
void
bufcat(struct html *h, const char *p)
{
bufncat(h, p, strlen(p));
}
void
buffmt(struct html *h, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void)vsnprintf(h->buf + (int)h->buflen,
BUFSIZ - h->buflen - 1, fmt, ap);
va_end(ap);
h->buflen = strlen(h->buf);
}
void
bufncat(struct html *h, const char *p, size_t sz)
{
if (h->buflen + sz > BUFSIZ - 1)
sz = BUFSIZ - 1 - h->buflen;
(void)strncat(h->buf, p, sz);
h->buflen += sz;
}
void
buffmt_includes(struct html *h, const char *name)
{
const char *p, *pp;
pp = h->base_includes;
while (NULL != (p = strchr(pp, '%'))) {
bufncat(h, pp, (size_t)(p - pp));
switch (*(p + 1)) {
case('I'):
bufcat(h, name);
break;
default:
bufncat(h, p, 2);
break;
}
pp = p + 2;
}
if (pp)
bufcat(h, pp);
}
void
buffmt_man(struct html *h,
const char *name, const char *sec)
{
const char *p, *pp;
pp = h->base_man;
/* LINTED */
while (NULL != (p = strchr(pp, '%'))) {
bufncat(h, pp, (size_t)(p - pp));
switch (*(p + 1)) {
case('S'):
bufcat(h, sec ? sec : "1");
break;
case('N'):
buffmt(h, name);
break;
default:
bufncat(h, p, 2);
break;
}
pp = p + 2;
}
if (pp)
bufcat(h, pp);
}
void
bufcat_su(struct html *h, const char *p, const struct roffsu *su)
{
double v;
const char *u;
v = su->scale;
switch (su->unit) {
case (SCALE_CM):
u = "cm";
break;
case (SCALE_IN):
u = "in";
break;
case (SCALE_PC):
u = "pc";
break;
case (SCALE_PT):
u = "pt";
break;
case (SCALE_EM):
u = "em";
break;
case (SCALE_MM):
if (0 == (v /= 100))
v = 1;
u = "em";
break;
case (SCALE_EN):
u = "ex";
break;
case (SCALE_BU):
u = "ex";
break;
case (SCALE_VS):
u = "em";
break;
default:
u = "ex";
break;
}
if (su->pt)
buffmt(h, "%s: %f%s;", p, v, u);
else
/* LINTED */
buffmt(h, "%s: %d%s;", p, (int)v, u);
}
void
html_idcat(char *dst, const char *src, int sz)
{
int ssz;
assert(sz);
/* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
for ( ; *dst != '\0' && sz; dst++, sz--)
/* Jump to end. */ ;
assert(sz > 2);
/* We can't start with a number (bah). */
*dst++ = 'x';
*dst = '\0';
sz--;
for ( ; *src != '\0' && sz > 1; src++) {
ssz = snprintf(dst, (size_t)sz, "%.2x", *src);
sz -= ssz;
dst += ssz;
}
}

156
commands/mdocml/html.h Normal file
View file

@ -0,0 +1,156 @@
/* $Id: html.h,v 1.24 2010/06/19 20:46:27 kristaps Exp $ */
/*
* Copyright (c) 2008, 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.
*/
#ifndef HTML_H
#define HTML_H
__BEGIN_DECLS
enum htmltag {
TAG_HTML,
TAG_HEAD,
TAG_BODY,
TAG_META,
TAG_TITLE,
TAG_DIV,
TAG_H1,
TAG_H2,
TAG_SPAN,
TAG_LINK,
TAG_BR,
TAG_A,
TAG_TABLE,
TAG_COL,
TAG_TR,
TAG_TD,
TAG_LI,
TAG_UL,
TAG_OL,
TAG_MAX
};
enum htmlattr {
ATTR_HTTPEQUIV,
ATTR_CONTENT,
ATTR_NAME,
ATTR_REL,
ATTR_HREF,
ATTR_TYPE,
ATTR_MEDIA,
ATTR_CLASS,
ATTR_STYLE,
ATTR_WIDTH,
ATTR_VALIGN,
ATTR_TARGET,
ATTR_ID,
ATTR_SUMMARY,
ATTR_MAX
};
enum htmlfont {
HTMLFONT_NONE = 0,
HTMLFONT_BOLD,
HTMLFONT_ITALIC,
HTMLFONT_MAX
};
struct tag {
struct tag *next;
enum htmltag tag;
};
struct ord {
struct ord *next;
const void *cookie;
int pos;
};
struct tagq {
struct tag *head;
};
struct ordq {
struct ord *head;
};
struct htmlpair {
enum htmlattr key;
const char *val;
};
#define PAIR_INIT(p, t, v) \
do { \
(p)->key = (t); \
(p)->val = (v); \
} while (/* CONSTCOND */ 0)
#define PAIR_ID_INIT(p, v) PAIR_INIT(p, ATTR_ID, v)
#define PAIR_CLASS_INIT(p, v) PAIR_INIT(p, ATTR_CLASS, v)
#define PAIR_HREF_INIT(p, v) PAIR_INIT(p, ATTR_HREF, v)
#define PAIR_STYLE_INIT(p, h) PAIR_INIT(p, ATTR_STYLE, (h)->buf)
#define PAIR_SUMMARY_INIT(p, v) PAIR_INIT(p, ATTR_SUMMARY, v)
enum htmltype {
HTML_HTML_4_01_STRICT,
HTML_XHTML_1_0_STRICT
};
struct html {
int flags;
#define HTML_NOSPACE (1 << 0)
#define HTML_IGNDELIM (1 << 2)
struct tagq tags;
struct ordq ords;
void *symtab;
char *base;
char *base_man;
char *base_includes;
char *style;
char buf[BUFSIZ];
size_t buflen;
struct tag *metaf;
enum htmlfont metal;
enum htmlfont metac;
enum htmltype type;
};
struct roffsu;
void print_gen_decls(struct html *);
void print_gen_head(struct html *);
struct tag *print_ofont(struct html *, enum htmlfont);
struct tag *print_otag(struct html *, enum htmltag,
int, const struct htmlpair *);
void print_tagq(struct html *, const struct tag *);
void print_stagq(struct html *, const struct tag *);
void print_text(struct html *, const char *);
void bufcat_su(struct html *, const char *,
const struct roffsu *);
void buffmt_man(struct html *,
const char *, const char *);
void buffmt_includes(struct html *, const char *);
void buffmt(struct html *, const char *, ...);
void bufcat(struct html *, const char *);
void bufcat_style(struct html *,
const char *, const char *);
void bufncat(struct html *, const char *, size_t);
void bufinit(struct html *);
void html_idcat(char *, const char *, int);
__END_DECLS
#endif /*!HTML_H*/

48
commands/mdocml/index.css Normal file
View file

@ -0,0 +1,48 @@
body { color: #333333;
font-size: smaller;
font-family: Verdana, Tahoma, Arial, sans-serif; }
table.frame { max-width: 800px;
padding-left: 10px; }
table { padding-left: 40px; }
p { padding-left: 40px;
text-align: justify; }
h1 { font-weight: bold;
font-size: small;
font-family: Verdana, Tahoma, Arial, sans-serif; }
h2 { font-weight: bold;
font-size: small;
padding-left: 20px;
margin-bottom: 0px;
font-family: Verdana, Tahoma, Arial, sans-serif; }
span.nm { color: #000000; font-weight: bold; }
span.attn { color: #000000; font-weight: bold; }
span.flag { font-weight: bold; }
div.head { border-bottom: 1px solid #dddddd;
padding-bottom: 5px;
text-align: right; }
div.subhead { font-size: smaller;
margin-bottom: 1em; }
div.foot { border-top: 1px solid #dddddd;
padding-top: 5px;
font-size: smaller;
text-align: right; }
a.external { background: transparent url(external.png) center right no-repeat;
padding-right: 12px; }
span.date { color: #000000; }
div.news { margin-bottom: 2em; }
div.news ul { margin-left: 4em; }

420
commands/mdocml/index.sgml Normal file
View file

@ -0,0 +1,420 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<META NAME="resource-type" CONTENT="document">
<LINK REL="stylesheet" HREF="index.css" TYPE="text/css" MEDIA="all">
<TITLE>mdocml | mdoc macro compiler</TITLE>
</HEAD>
<BODY>
<TABLE CLASS="frame" SUMMARY="[frame]">
<COL WIDTH="100%">
<TBODY>
<TR>
<TD>
<DIV CLASS="head">
<B>mdocml</B> &#8211; mdoc macro compiler
</DIV>
<DIV CLASS="subhead">
<A HREF="#description">Description</A> |
<A HREF="#sources">Sources</A> |
<A HREF="#documentation">Documentation</A> |
<A HREF="#contact">Contact</A> |
<A HREF="#news">News</A>
</DIV>
</TD>
</TR>
<TR>
<TD VALIGN="top">
<H1>
<A NAME="description">DESCRIPTION</A>
</H1>
<P>
<SPAN CLASS="nm">mdocml</SPAN> is a suite of tools compiling <Q>-mdoc</Q>, the roff macro package
of choice for BSD manual pages, and <Q>-man</Q>, the predominant historical package for UNIX
manuals. The mission of <SPAN CLASS="nm">mdocml</SPAN> is to deprecate <A
HREF="http://www.gnu.org/software/groff/" CLASS="external">groff</A>, the GNU roff implementation, for
displaying -mdoc pages whilst providing token support for -man.
</P>
<P>
Why? groff amounts to over 5 MB of source code, most of which is C++ and all of which is GPL. It runs
slowly, produces uncertain output, and varies in operation from system to system. mdocml strives to fix
this (respectively small, C, ISC-licensed, fast and regular).
</P>
<P>
The core of <SPAN CLASS="nm">mdocml</SPAN> is composed of the <A HREF="mdoc.3.html">libmdoc</A>, <A
HREF="man.3.html">libman</A>, and <A HREF="roff.3.html">libroff</A> validating compiler libraries. All
are simple, fast libraries operating on memory buffers, so they may be used for a variety of front-ends
(terminal-based, CGI and so on). The front-end is <A HREF="mandoc.1.html">mandoc</A>, which formats
manuals for display.
</P>
<P>
The <SPAN CLASS="nm">mdocml</SPAN> suite is a <A CLASS="external" HREF="http://bsd.lv/">BSD.lv
Project</A> member.
</P>
</TD>
</TR>
<TR>
<TD>
<H1>
<A NAME="sources">SOURCES</A>
</H1>
<P>
Sources correctly build and install on DragonFly BSD, FreeBSD, OpenBSD, NetBSD, GNU/Linux, and many
other operating systems, tested variously on i386, AMD64, alpha, and others. The most current version
is <SPAN CLASS="attn">@VERSION@</SPAN>, dated <SPAN class="attn">@VDATE@</SPAN>. A full <A
HREF="ChangeLog.html">ChangeLog</A> (<A HREF="ChangeLog.txt">txt</A>) is written with each release.
</P>
<H2>
Current
</H2>
<TABLE WIDTH="100%" SUMMARY="Current Sources">
<COL WIDTH="200">
<COL>
<TBODY>
<TR>
<TD>Source archive</TD>
<TD>
<A HREF="/snapshots/mdocml.tar.gz">/snapshots/mdocml.tar.gz</A>
(<A HREF="/snapshots/mdocml.md5">md5</A>)
</TD>
</TR>
<TR>
<TD>Online source</TD>
<TD>
<A HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/?cvsroot=mdocml">cvsweb</A>
</TD>
</TR>
</TBODY>
</TABLE>
<H2>
Downstream
</H2>
<TABLE WIDTH="100%" SUMMARY="Downstream Sources">
<COL WIDTH="200">
<COL>
<TBODY>
<TR>
<TD>DragonFly BSD</TD>
<TD>
<A HREF="http://gitweb.dragonflybsd.org/dragonfly.git/tree/HEAD:/usr.bin/mandoc"
CLASS="external">usr.bin/mandoc</A>
</TD>
</TR>
<TR>
<TD>FreeBSD</TD>
<TD>
<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/ports/textproc/mdocml/"
CLASS="external">ports/textproc/mdocml</A>
</TD>
</TR>
<TR>
<TD>NetBSD</TD>
<TD>
<A HREF="http://cvsweb.netbsd.org/bsdweb.cgi/src/external/bsd/mdocml/"
CLASS="external">src/external/bsd/mdocml</A>
</TD>
</TR>
<TR>
<TD>OpenBSD</TD>
<TD>
<A HREF="http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/mandoc/"
CLASS="external">src/usr.bin/mandoc</A>
</TD>
</TR>
</TBODY>
</TABLE>
<H2>
Historical
</H2>
<TABLE WIDTH="100%" SUMMARY="Archived Sources">
<COL WIDTH="200">
<COL>
<TBODY>
<TR>
<TD>Source archive</TD>
<TD>
<A HREF="/snapshots/">/snapshots/</A>
</TD>
</TR>
</TBODY>
</TABLE>
</TD>
</TR>
<TR>
<TD>
<H1>
<A NAME="documentation">DOCUMENTATION</A>
</H1>
<P>
These manuals are generated automatically and refer to the current snapshot.
</P>
<TABLE WIDTH="100%" SUMMARY="Documentation">
<COL WIDTH="200">
<COL>
<TBODY>
<TR>
<TD VALIGN="top"><A HREF="man.3.html">man(3)</A></TD>
<TD VALIGN="top">
man macro compiler library
<DIV STYLE="font-size: smaller;">
<A HREF="man.3.txt">text</A> | <A HREF="man.3.ps">postscript</A>
</DIV>
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="man.7.html">man(7)</A></TD>
<TD VALIGN="top">
man language reference
<DIV STYLE="font-size: smaller;">
<A HREF="man.7.txt">text</A> | <A HREF="man.7.ps">postscript</A>
</DIV>
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="mandoc.1.html">mandoc(1)</A></TD>
<TD VALIGN="top">
format and display UNIX manuals
<DIV STYLE="font-size: smaller;">
<A HREF="mandoc.1.txt">text</A> | <A HREF="mandoc.1.ps">postscript</A>
</DIV>
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="mandoc_char.7.html">mandoc_char(7)</A></TD>
<TD VALIGN="top">
mandoc special characters
<DIV STYLE="font-size: smaller;">
<A HREF="mandoc_char.7.txt">text</A> | <A HREF="mandoc_char.7.ps">postscript</A>
</DIV>
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="mdoc.3.html">mdoc(3)</A></TD>
<TD VALIGN="top">
mdoc macro compiler library
<DIV STYLE="font-size: smaller;">
<A HREF="mdoc.3.txt">text</A> | <A HREF="mdoc.3.ps">postscript</A>
</DIV>
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="mdoc.7.html">mdoc(7)</A></TD>
<TD VALIGN="top">
mdoc language reference
<DIV STYLE="font-size: smaller;">
<A HREF="mdoc.7.txt">text</A> | <A HREF="mdoc.7.ps">postscript</A>
</DIV>
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="roff.3.html">roff(3)</A></TD>
<TD VALIGN="top">
roff macro compiler library
<DIV STYLE="font-size: smaller;">
<A HREF="roff.3.txt">text</A> | <A HREF="roff.3.ps">postscript</A>
</DIV>
</TD>
</TR>
<TR>
<TD VALIGN="top"><A HREF="roff.7.html">roff(7)</A></TD>
<TD VALIGN="top">
roff-mandoc language reference
<DIV STYLE="font-size: smaller;">
<A HREF="roff.7.txt">text</A> | <A HREF="roff.7.ps">postscript</A>
</DIV>
</TD>
</TR>
</TBODY>
</TABLE>
<P>
See <Q><A CLASS="external" HREF="http://manpages.bsd.lv">Writing UNIX Manual Pages</A></Q> for a general
introduction to manpages and mdoc.
</P>
</TD>
</TR>
<TR>
<TD>
<H1>
<A NAME="contact">CONTACT</A>
</H1>
<P>
For all issues related to <SPAN CLASS="nm">mdocml</SPAN>, contact Kristaps Dzonsons, kris<A
CLASS="external"
HREF="http://mailhide.recaptcha.net/d?k=01M6h_w7twDp58ZgH57eWC_w==&amp;c=Q2DBUt401ePlSeupJFrq_Q=="
TITLE="Reveal this e-mail address">...</A>@bsd.lv.
</P>
<P>
You may also subscribe to several mailing lists (these require subscription, which is moderated). An
archive is not yet available on-line, although you may request one once subscribed.
</P>
<TABLE WIDTH="100%" SUMMARY="Mailing Lists">
<COL WIDTH="200">
<COL>
<TBODY>
<TR>
<TD>
disc<A CLASS="external" TITLE="Reveal this e-mail address"
HREF="http://www.google.com/recaptcha/mailhide/d?k=01KQ80PFH5n3BBNpF5Gs4sRg==&amp;c=EV1QytpQqTHSItc2IXvZyocgYLPnG5K0JKw_gwMC9yc=">...</A>@mdocml.bsd.lv
</TD>
<TD>high-level discussions and version announcements</TD>
</TR>
<TR>
<TD>
tec<A CLASS="external" TITLE="Reveal this e-mail address"
HREF="http://www.google.com/recaptcha/mailhide/d?k=01qDX_iV0RlUOarEvb6mR28g==&amp;c=gRXsTjza0NNCFPaYu-Taj2tF0pmYZSc90EZkFkhkxgo=">...</A>@mdocml.bsd.lv
</TD>
<TD>low-level discussions</TD>
</TR>
<TR>
<TD>
sou<A CLASS="external" TITLE="Reveal this e-mail address"
HREF="http://www.google.com/recaptcha/mailhide/d?k=01prQrAZhhl2EbIwVcRfABsQ==&amp;c=KtTW4Yic9xk-8g40KzJoca4fR3MYXv28g8NC6OQV-T8=">...</A>@mdocml.bsd.lv
</TD>
<TD>source commit messages</TD>
</TR>
</TBODY>
</TABLE>
</TD>
</TR>
<TR>
<TD>
<H1>
<A NAME="news">NEWS</A>
</H1>
<DIV CLASS="news">
<P>
<SPAN CLASS="date">19-06-2010</SPAN>:
version 1.10.2
</P>
<P>
Small release featuring text-decoration in <SPAN CLASS="flag">-Tps</SPAN> output, a few
minor relaxations of errors, and some optimisations.
</P>
</DIV>
<DIV CLASS="news">
<P>
<SPAN CLASS="date">07-06-2010</SPAN>:
version 1.10.1
</P>
<P>
This primarily focusses on the <Q>Bl</Q> and <Q>It</Q> macros described in <A
HREF="mdoc.7.html">mdoc</A>. Multi-line column support is now fully compatible with
groff, as are implicit list entries for columns. Removed manuals.7 in favour of <A
CLASS="external" HREF="http://manpages.bsd.lv">http://manpages.bsd.lv</A>. The way we
handle the SYNOPSIS section (see the SYNOPSIS documentation in <A
HREF="mdoc.7.html#x4d414e55414cx20x535452554354555245">MANUAL STRUCTURE</A>) has also
been considerably simplified compared to groff's method. Furthermore, the <SPAN
CLASS="flag">-Owidth=width</SPAN> output option has been added to <SPAN
CLASS="flag">-Tascii</SPAN> (see <A HREF="mandoc.1.html">mandoc</A>). Lastly, initial
PostScript output has been added with the <SPAN CLASS="flag">-Tps</SPAN> option to <A
HREF="mandoc.1.html">mandoc</A>. It's brutally simple at the moment: fixed-font, with
no font decorations.
</P>
</DIV>
<DIV CLASS="news">
<P>
<SPAN CLASS="date">29-05-2010</SPAN>:
version 1.10.0
</P>
<P>
Release consisting of the results from the m2k10 hackathon and up-merge from OpenBSD.
This requires a significant note of thanks to Ingo Schwarze (OpenBSD) and Joerg
Sonnenberger (NetBSD) for their hard work, and again to Joerg for hosting m2k10.
Highlights (mostly cribbed from Ingo's m2k10 report) follow in no particular order:
</P>
<UL>
<LI>a <A HREF="roff.3.html">libroff</A> preprocessor in front of <A
HREF="mdoc.3.html">libmdoc</A> and <A HREF="man.3.html">libman</A> stripping out
<A HREF="roff.7.html">roff</A> instructions;</LI>
<LI>end-of-sentence (EOS) detection in free-form and macro lines;</LI>
<LI>correct handling of tab-separated columnar lists in <SPAN
CLASS="flag">-mdoc</SPAN>;</LI>
<LI>improved main calling routines to optionally use mmap() for better
performance;</LI>
<LI>cleaned up exiting when invoked as <SPAN CLASS="flag">-Tlint</SPAN> or over
multiple files with <SPAN CLASS="flag">-fign-errors</SPAN>;</LI>
<LI>error and warning message handling re-written to be unified for <A
HREF="roff.3.html">libroff</A>, <A HREF="mdoc.3.html">libmdoc</A>, and <A
HREF="man.3.html">libman</A>;</LI>
<LI>handling of badly-nested explicit-scoped macros;</LI>
<LI>improved free-form text parsing in <A HREF="man.3.html">libman</A> and <A
HREF="mdoc.3.html">libmdoc</A>;</LI>
<LI>significant GNU troff compatibility improvements in <SPAN
CLASS="flag">-Tascii</SPAN>, largely in terms of spacing;</LI>
<LI>a regression framework for making sure the many fragilities of GNU troff
aren't trampled in subsequent work;</LI>
<LI>support for <SPAN CLASS="flag">-Tascii</SPAN> breaking at hyphens
encountered in free-form text;</LI>
<LI>and many more minor fixes and improvements (no really, consult <A
HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/?cvsroot=mdocml">cvsweb</A> and see
for yourself!).</LI>
</UL>
</DIV>
<DIV CLASS="news">
<P>
<SPAN CLASS="date">13-05-2010</SPAN>:
version 1.9.25
</P>
<P>
Fixed handling of <Q>\*(Ba</Q> escape. Backed out <SPAN
CLASS="flag">-fno-ign-chars</SPAN> (pointless complexity). Fixed erroneous
breaking of literal lines. Fixed SYNOPSIS breaking lines before non-initial
macros. Changed default section ordering. Most importantly, the framework for
end-of-sentence double-spacing is in place, now implemented for the
<Q>end-of-sentence, end-of-line</Q> rule. This is a stable roll-back point
before the mandoc hackathon in Rostock!
</P>
</DIV>
<DIV CLASS="news">
<P>
<SPAN CLASS="date">09-05-2010</SPAN>:
version 1.9.24
</P>
<P>
Rolled back break-at-hyphen. <SPAN CLASS="flag">-DUGLY</SPAN> is now the
default (no feature splits!). Free-form text is not de-chunked any more: lines
are passed whole-sale into the front-end, including whitespace. Added mailing
lists. Lastly, <SPAN CLASS="nm">mdocml</SPAN> is the focus of two <A
CLASS="external" HREF="http://socghop.appspot.com/">Google Summer of Code</A>
projects this year: <Q><A CLASS="external"
HREF="http://netbsd-soc.sourceforge.net/projects/mandoc_ps/">mandoc -Tps</A></Q>
(NetBSD) and <Q><A CLASS="external"
HREF="http://wiki.freebsd.org/SummerOfCode2010Projects">BSD-licensed
Text-Processing Tools</A></Q> (FreeBSD).
</P>
</DIV>
<P>
See <A HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/index.sgml?cvsroot=mdocml">cvsweb</A> for
historical notes.
</P>
</TD>
</TR>
<TR>
<TD>
<DIV CLASS="foot">
Copyright &#169; 2008&#8211;2010 Kristaps Dzonsons, $Date: 2010/06/19 20:43:35 $
</DIV>
</TD>
</TR>
</TBODY>
</TABLE>
</BODY>
</HTML>

38
commands/mdocml/lib.c Normal file
View file

@ -0,0 +1,38 @@
/* $Id: lib.c,v 1.8 2010/06/19 20:46:27 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 <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mdoc_a2lib(const char *p)
{
#include "lib.in"
return(NULL);
}

93
commands/mdocml/lib.in Normal file
View file

@ -0,0 +1,93 @@
/* $Id: lib.in,v 1.9 2010/06/19 20:46:27 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.
*/
/*
* These are all possible .Lb strings. When a new library is added, add
* its short-string to the left-hand side and formatted string to the
* right-hand side.
*
* Be sure to escape strings.
*/
LINE("libarchive", "Reading and Writing Streaming Archives Library (libarchive, \\-larchive)")
LINE("libarm", "ARM Architecture Library (libarm, \\-larm)")
LINE("libarm32", "ARM32 Architecture Library (libarm32, \\-larm32)")
LINE("libbluetooth", "Bluetooth Library (libbluetooth, \\-lbluetooth)")
LINE("libbsm", "Basic Security Module User Library (libbsm, \\-lbsm)")
LINE("libc", "Standard C Library (libc, \\-lc)")
LINE("libc_r", "Reentrant C\\~Library (libc_r, \\-lc_r)")
LINE("libcalendar", "Calendar Arithmetic Library (libcalendar, \\-lcalendar)")
LINE("libcam", "Common Access Method User Library (libcam, \\-lcam)")
LINE("libcdk", "Curses Development Kit Library (libcdk, \\-lcdk)")
LINE("libcipher", "FreeSec Crypt Library (libcipher, \\-lcipher)")
LINE("libcompat", "Compatibility Library (libcompat, \\-lcompat)")
LINE("libcrypt", "Crypt Library (libcrypt, \\-lcrypt)")
LINE("libcurses", "Curses Library (libcurses, \\-lcurses)")
LINE("libdevinfo", "Device and Resource Information Utility Library (libdevinfo, \\-ldevinfo)")
LINE("libdevstat", "Device Statistics Library (libdevstat, \\-ldevstat)")
LINE("libdisk", "Interface to Slice and Partition Labels Library (libdisk, \\-ldisk)")
LINE("libedit", "Command Line Editor Library (libedit, \\-ledit)")
LINE("libelf", "ELF Parsing Library (libelf, \\-lelf)")
LINE("libevent", "Event Notification Library (libevent, \\-levent)")
LINE("libfetch", "File Transfer Library for URLs (libfetch, \\-lfetch)")
LINE("libform", "Curses Form Library (libform, \\-lform)")
LINE("libgeom", "Userland API Library for kernel GEOM subsystem (libgeom, \\-lgeom)")
LINE("libgpib", "General-Purpose Instrument Bus (GPIB) library (libgpib, \\-lgpib)")
LINE("libi386", "i386 Architecture Library (libi386, \\-li386)")
LINE("libintl", "Internationalized Message Handling Library (libintl, \\-lintl)")
LINE("libipsec", "IPsec Policy Control Library (libipsec, \\-lipsec)")
LINE("libipx", "IPX Address Conversion Support Library (libipx, \\-lipx)")
LINE("libiscsi", "iSCSI protocol library (libiscsi, \\-liscsi)")
LINE("libjail", "Jail Library (libjail, \\-ljail)")
LINE("libkiconv", "Kernel side iconv library (libkiconv, \\-lkiconv)")
LINE("libkse", "N:M Threading Library (libkse, \\-lkse)")
LINE("libkvm", "Kernel Data Access Library (libkvm, \\-lkvm)")
LINE("libm", "Math Library (libm, \\-lm)")
LINE("libm68k", "m68k Architecture Library (libm68k, \\-lm68k)")
LINE("libmagic", "Magic Number Recognition Library (libmagic, \\-lmagic)")
LINE("libmd", "Message Digest (MD4, MD5, etc.) Support Library (libmd, \\-lmd)")
LINE("libmemstat", "Kernel Memory Allocator Statistics Library (libmemstat, \\-lmemstat)")
LINE("libmenu", "Curses Menu Library (libmenu, \\-lmenu)")
LINE("libnetgraph", "Netgraph User Library (libnetgraph, \\-lnetgraph)")
LINE("libnetpgp", "Netpgp signing, verification, encryption and decryption (libnetpgp, \\-lnetpgp)")
LINE("libossaudio", "OSS Audio Emulation Library (libossaudio, \\-lossaudio)")
LINE("libpam", "Pluggable Authentication Module Library (libpam, \\-lpam)")
LINE("libpcap", "Capture Library (libpcap, \\-lpcap)")
LINE("libpci", "PCI Bus Access Library (libpci, \\-lpci)")
LINE("libpmc", "Performance Counters Library (libpmc, \\-lpmc)")
LINE("libposix", "POSIX Compatibility Library (libposix, \\-lposix)")
LINE("libprop", "Property Container Object Library (libprop, \\-lprop)")
LINE("libpthread", "POSIX Threads Library (libpthread, \\-lpthread)")
LINE("libpuffs", "puffs Convenience Library (libpuffs, \\-lpuffs)")
LINE("librefuse", "File System in Userspace Convenience Library (librefuse, \\-lrefuse)")
LINE("libresolv", "DNS Resolver Library (libresolv, \\-lresolv)")
LINE("librpcsec_gss", "RPC GSS-API Authentication Library (librpcsec_gss, \\-lrpcsec_gss)")
LINE("librpcsvc", "RPC Service Library (librpcsvc, \\-lrpcsvc)")
LINE("librt", "POSIX Real\\-time Library (librt, -lrt)")
LINE("libsdp", "Bluetooth Service Discovery Protocol User Library (libsdp, \\-lsdp)")
LINE("libssp", "Buffer Overflow Protection Library (libssp, \\-lssp)")
LINE("libtermcap", "Termcap Access Library (libtermcap, \\-ltermcap)")
LINE("libterminfo", "Terminal Information Library (libterminfo, \\-lterminfo)")
LINE("libthr", "1:1 Threading Library (libthr, \\-lthr)")
LINE("libufs", "UFS File System Access Library (libufs, \\-lufs)")
LINE("libugidfw", "File System Firewall Interface Library (libugidfw, \\-lugidfw)")
LINE("libulog", "User Login Record Library (libulog, \\-lulog)")
LINE("libusbhid", "USB Human Interface Devices Library (libusbhid, \\-lusbhid)")
LINE("libutil", "System Utilities Library (libutil, \\-lutil)")
LINE("libvgl", "Video Graphics Library (libvgl, \\-lvgl)")
LINE("libx86_64", "x86_64 Architecture Library (libx86_64, \\-lx86_64)")
LINE("libz", "Compression Library (libz, \\-lz)")

90
commands/mdocml/libman.h Normal file
View file

@ -0,0 +1,90 @@
/* $Id: libman.h,v 1.36 2010/06/19 20:46:27 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.
*/
#ifndef LIBMAN_H
#define LIBMAN_H
#include "man.h"
enum man_next {
MAN_NEXT_SIBLING = 0,
MAN_NEXT_CHILD
};
struct man {
void *data;
mandocmsg msg;
int pflags; /* parse flags (see man.h) */
int flags; /* parse flags */
#define MAN_HALT (1 << 0) /* badness happened: die */
#define MAN_ELINE (1 << 1) /* Next-line element scope. */
#define MAN_BLINE (1 << 2) /* Next-line block scope. */
#define MAN_ILINE (1 << 3) /* Ignored in next-line scope. */
#define MAN_LITERAL (1 << 4) /* Literal input. */
#define MAN_BPLINE (1 << 5)
enum man_next next;
struct man_node *last;
struct man_node *first;
struct man_meta meta;
};
#define MACRO_PROT_ARGS struct man *m, enum mant tok, int line, \
int ppos, int *pos, char *buf
struct man_macro {
int (*fp)(MACRO_PROT_ARGS);
int flags;
#define MAN_SCOPED (1 << 0)
#define MAN_EXPLICIT (1 << 1) /* See blk_imp(). */
#define MAN_FSCOPED (1 << 2) /* See blk_imp(). */
#define MAN_NSCOPED (1 << 3) /* See in_line_eoln(). */
#define MAN_NOCLOSE (1 << 4) /* See blk_exp(). */
};
extern const struct man_macro *const man_macros;
__BEGIN_DECLS
#define man_pmsg(m, l, p, t) \
(*(m)->msg)((t), (m)->data, (l), (p), NULL)
#define man_nmsg(m, n, t) \
(*(m)->msg)((t), (m)->data, (n)->line, (n)->pos, NULL)
int man_word_alloc(struct man *, int, int, const char *);
int man_block_alloc(struct man *, int, int, enum mant);
int man_head_alloc(struct man *, int, int, enum mant);
int man_body_alloc(struct man *, int, int, enum mant);
int man_elem_alloc(struct man *, int, int, enum mant);
void man_node_delete(struct man *, struct man_node *);
void man_hash_init(void);
enum mant man_hash_find(const char *);
int man_macroend(struct man *);
int man_args(struct man *, int, int *, char *, char **);
#define ARGS_ERROR (-1)
#define ARGS_EOLN (0)
#define ARGS_WORD (1)
#define ARGS_QWORD (1)
int man_vmsg(struct man *, enum mandocerr,
int, int, const char *, ...);
int man_valid_post(struct man *);
int man_valid_pre(struct man *, struct man_node *);
int man_action_post(struct man *);
int man_action_pre(struct man *, struct man_node *);
int man_unscope(struct man *,
const struct man_node *, enum mandocerr);
__END_DECLS
#endif /*!LIBMAN_H*/

View file

@ -0,0 +1,37 @@
/* $Id: libmandoc.h,v 1.8 2010/06/19 20:46:27 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.
*/
#ifndef LIBMANDOC_H
#define LIBMANDOC_H
__BEGIN_DECLS
int mandoc_special(char *);
void *mandoc_calloc(size_t, size_t);
char *mandoc_strdup(const char *);
void *mandoc_malloc(size_t);
void *mandoc_realloc(void *, size_t);
time_t mandoc_a2time(int, const char *);
#define MTIME_CANONICAL (1 << 0)
#define MTIME_REDUCED (1 << 1)
#define MTIME_MDOCDATE (1 << 2)
#define MTIME_ISO_8601 (1 << 3)
int mandoc_eos(const char *, size_t);
int mandoc_hyph(const char *, const char *);
__END_DECLS
#endif /*!LIBMANDOC_H*/

142
commands/mdocml/libmdoc.h Normal file
View file

@ -0,0 +1,142 @@
/* $Id: libmdoc.h,v 1.53 2010/06/19 20:46:27 kristaps Exp $ */
/*
* Copyright (c) 2008, 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.
*/
#ifndef LIBMDOC_H
#define LIBMDOC_H
#include "mdoc.h"
enum mdoc_next {
MDOC_NEXT_SIBLING = 0,
MDOC_NEXT_CHILD
};
struct mdoc {
void *data;
mandocmsg msg;
int flags;
#define MDOC_HALT (1 << 0) /* error in parse: halt */
#define MDOC_LITERAL (1 << 1) /* in a literal scope */
#define MDOC_PBODY (1 << 2) /* in the document body */
#define MDOC_NEWLINE (1 << 3) /* first macro/text in a line */
#define MDOC_PHRASELIT (1 << 4) /* literal within a partila phrase */
#define MDOC_PPHRASE (1 << 5) /* within a partial phrase */
#define MDOC_FREECOL (1 << 6) /* `It' invocation should close */
int pflags;
enum mdoc_next next;
struct mdoc_node *last;
struct mdoc_node *first;
struct mdoc_meta meta;
enum mdoc_sec lastnamed;
enum mdoc_sec lastsec;
};
#define MACRO_PROT_ARGS struct mdoc *m, enum mdoct tok, \
int line, int ppos, int *pos, char *buf
struct mdoc_macro {
int (*fp)(MACRO_PROT_ARGS);
int flags;
#define MDOC_CALLABLE (1 << 0)
#define MDOC_PARSED (1 << 1)
#define MDOC_EXPLICIT (1 << 2)
#define MDOC_PROLOGUE (1 << 3)
#define MDOC_IGNDELIM (1 << 4)
/* Reserved words in arguments treated as text. */
};
enum margserr {
ARGS_ERROR,
ARGS_EOLN,
ARGS_WORD,
ARGS_PUNCT,
ARGS_QWORD,
ARGS_PHRASE,
ARGS_PPHRASE,
ARGS_PEND
};
enum margverr {
ARGV_ERROR,
ARGV_EOLN,
ARGV_ARG,
ARGV_WORD
};
enum mdelim {
DELIM_NONE = 0,
DELIM_OPEN,
DELIM_MIDDLE,
DELIM_CLOSE
};
extern const struct mdoc_macro *const mdoc_macros;
__BEGIN_DECLS
#define mdoc_pmsg(m, l, p, t) \
(*(m)->msg)((t), (m)->data, (l), (p), NULL)
#define mdoc_nmsg(m, n, t) \
(*(m)->msg)((t), (m)->data, (n)->line, (n)->pos, NULL)
int mdoc_vmsg(struct mdoc *, enum mandocerr,
int, int, const char *, ...);
int mdoc_macro(MACRO_PROT_ARGS);
int mdoc_word_alloc(struct mdoc *,
int, int, const char *);
int mdoc_elem_alloc(struct mdoc *, int, int,
enum mdoct, struct mdoc_arg *);
int mdoc_block_alloc(struct mdoc *, int, int,
enum mdoct, struct mdoc_arg *);
int mdoc_head_alloc(struct mdoc *, int, int, enum mdoct);
int mdoc_tail_alloc(struct mdoc *, int, int, enum mdoct);
int mdoc_body_alloc(struct mdoc *, int, int, enum mdoct);
void mdoc_node_delete(struct mdoc *, struct mdoc_node *);
void mdoc_hash_init(void);
enum mdoct mdoc_hash_find(const char *);
enum mdelim mdoc_iscdelim(char);
enum mdelim mdoc_isdelim(const char *);
size_t mdoc_isescape(const char *);
enum mdoc_sec mdoc_str2sec(const char *);
time_t mdoc_atotime(const char *);
size_t mdoc_macro2len(enum mdoct);
const char *mdoc_a2att(const char *);
const char *mdoc_a2lib(const char *);
const char *mdoc_a2st(const char *);
const char *mdoc_a2arch(const char *);
const char *mdoc_a2vol(const char *);
const char *mdoc_a2msec(const char *);
int mdoc_valid_pre(struct mdoc *, struct mdoc_node *);
int mdoc_valid_post(struct mdoc *);
int mdoc_action_pre(struct mdoc *,
struct mdoc_node *);
int mdoc_action_post(struct mdoc *);
enum margverr mdoc_argv(struct mdoc *, int, enum mdoct,
struct mdoc_arg **, int *, char *);
void mdoc_argv_free(struct mdoc_arg *);
void mdoc_argn_free(struct mdoc_arg *, int);
enum margserr mdoc_args(struct mdoc *, int,
int *, char *, enum mdoct, char **);
enum margserr mdoc_zargs(struct mdoc *, int,
int *, char *, int, char **);
#define ARGS_DELIM (1 << 1)
#define ARGS_TABSEP (1 << 2)
#define ARGS_NOWARN (1 << 3)
int mdoc_macroend(struct mdoc *);
__END_DECLS
#endif /*!LIBMDOC_H*/

866
commands/mdocml/main.c Normal file
View file

@ -0,0 +1,866 @@
/* $Id: main.c,v 1.89 2010/06/19 20:46:27 kristaps Exp $ */
/*
* Copyright (c) 2008, 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/mman.h>
#include <sys/stat.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mandoc.h"
#include "mdoc.h"
#include "man.h"
#include "roff.h"
#include "main.h"
#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
/* FIXME: Intel's compiler? LLVM? pcc? */
#if !defined(__GNUC__) || (__GNUC__ < 2)
# if !defined(lint)
# define __attribute__(x)
# endif
#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
typedef void (*out_mdoc)(void *, const struct mdoc *);
typedef void (*out_man)(void *, const struct man *);
typedef void (*out_free)(void *);
struct buf {
char *buf;
size_t sz;
};
enum intt {
INTT_AUTO,
INTT_MDOC,
INTT_MAN
};
enum outt {
OUTT_ASCII = 0,
OUTT_TREE,
OUTT_HTML,
OUTT_XHTML,
OUTT_LINT,
OUTT_PS
};
struct curparse {
const char *file; /* Current parse. */
int fd; /* Current parse. */
int wflags;
/* FIXME: set by max error */
#define WARN_WALL (1 << 0) /* All-warnings mask. */
#define WARN_WERR (1 << 2) /* Warnings->errors. */
int fflags;
#define FL_IGN_SCOPE (1 << 0) /* Ignore scope errors. */
#define FL_NIGN_ESCAPE (1 << 1) /* Don't ignore bad escapes. */
#define FL_NIGN_MACRO (1 << 2) /* Don't ignore bad macros. */
#define FL_IGN_ERRORS (1 << 4) /* Ignore failed parse. */
#define FL_STRICT FL_NIGN_ESCAPE | \
FL_NIGN_MACRO /* ignore nothing */
enum intt inttype; /* which parser to use */
struct man *man; /* man parser */
struct mdoc *mdoc; /* mdoc parser */
struct roff *roff; /* roff parser (!NULL) */
enum outt outtype; /* which output to use */
out_mdoc outmdoc; /* mdoc output ptr */
out_man outman; /* man output ptr */
out_free outfree; /* free output ptr */
void *outdata; /* data for output */
char outopts[BUFSIZ]; /* buf of output opts */
};
static const char * const mandocerrs[MANDOCERR_MAX] = {
"ok",
"text should be uppercase",
"sections out of conventional order",
"section name repeats",
"out of order prologue",
"repeated prologue entry",
"list type must come first",
"bad standard",
"bad library",
"bad escape sequence",
"unterminated quoted string",
"argument requires the width argument",
"superfluous width argument",
"ignoring argument",
"bad date argument",
"bad width argument",
"unknown manual section",
"section not in conventional manual section",
"end of line whitespace",
"scope open on exit",
"NAME section must come first",
"bad Boolean value",
"child violates parent syntax",
"bad AT&T symbol",
"list type repeated",
"display type repeated",
"argument repeated",
"manual name not yet set",
"obsolete macro ignored",
"empty macro ignored",
"macro not allowed in body",
"macro not allowed in prologue",
"bad character",
"bad NAME section contents",
"no blank lines",
"no text in this context",
"bad comment style",
"unknown macro will be lost",
"line scope broken",
"scope broken",
"argument count wrong",
"request scope close w/none open",
"scope already open",
"macro requires line argument(s)",
"macro requires body argument(s)",
"macro requires argument(s)",
"no title in document",
"missing list type",
"missing display type",
"line argument(s) will be lost",
"body argument(s) will be lost",
"column syntax is inconsistent",
"missing font type",
"displays may not be nested",
"unsupported display type",
"no scope to rewind: syntax violated",
"scope broken, syntax violated",
"line scope broken, syntax violated",
"argument count wrong, violates syntax",
"child violates parent syntax",
"argument count wrong, violates syntax",
"no document body",
"no document prologue",
"utsname system call failed",
"memory exhausted",
};
static void fdesc(struct curparse *);
static void ffile(const char *, struct curparse *);
static int foptions(int *, char *);
static struct man *man_init(struct curparse *);
static struct mdoc *mdoc_init(struct curparse *);
static struct roff *roff_init(struct curparse *);
static int moptions(enum intt *, char *);
static int mmsg(enum mandocerr, void *,
int, int, const char *);
static int pset(const char *, int, struct curparse *,
struct man **, struct mdoc **);
static int toptions(struct curparse *, char *);
static void usage(void) __attribute__((noreturn));
static void version(void) __attribute__((noreturn));
static int woptions(int *, char *);
static const char *progname;
static int with_error;
static int with_warning;
int
main(int argc, char *argv[])
{
int c;
struct curparse curp;
progname = strrchr(argv[0], '/');
if (progname == NULL)
progname = argv[0];
else
++progname;
memset(&curp, 0, sizeof(struct curparse));
curp.inttype = INTT_AUTO;
curp.outtype = OUTT_ASCII;
/* LINTED */
while (-1 != (c = getopt(argc, argv, "f:m:O:T:VW:")))
switch (c) {
case ('f'):
if ( ! foptions(&curp.fflags, optarg))
return(EXIT_FAILURE);
break;
case ('m'):
if ( ! moptions(&curp.inttype, optarg))
return(EXIT_FAILURE);
break;
case ('O'):
(void)strlcat(curp.outopts, optarg, BUFSIZ);
(void)strlcat(curp.outopts, ",", BUFSIZ);
break;
case ('T'):
if ( ! toptions(&curp, optarg))
return(EXIT_FAILURE);
break;
case ('W'):
if ( ! woptions(&curp.wflags, optarg))
return(EXIT_FAILURE);
break;
case ('V'):
version();
/* NOTREACHED */
default:
usage();
/* NOTREACHED */
}
argc -= optind;
argv += optind;
if (NULL == *argv) {
curp.file = "<stdin>";
curp.fd = STDIN_FILENO;
fdesc(&curp);
}
while (*argv) {
ffile(*argv, &curp);
if (with_error && !(curp.fflags & FL_IGN_ERRORS))
break;
++argv;
}
if (curp.outfree)
(*curp.outfree)(curp.outdata);
if (curp.mdoc)
mdoc_free(curp.mdoc);
if (curp.man)
man_free(curp.man);
if (curp.roff)
roff_free(curp.roff);
return((with_warning || with_error) ?
EXIT_FAILURE : EXIT_SUCCESS);
}
static void
version(void)
{
(void)printf("%s %s\n", progname, VERSION);
exit(EXIT_SUCCESS);
}
static void
usage(void)
{
(void)fprintf(stderr, "usage: %s [-V] [-foption] "
"[-mformat] [-Ooption] [-Toutput] "
"[-Werr] [file...]\n", progname);
exit(EXIT_FAILURE);
}
static struct man *
man_init(struct curparse *curp)
{
int pflags;
/* Defaults from mandoc.1. */
pflags = MAN_IGN_MACRO | MAN_IGN_ESCAPE;
if (curp->fflags & FL_NIGN_MACRO)
pflags &= ~MAN_IGN_MACRO;
if (curp->fflags & FL_NIGN_ESCAPE)
pflags &= ~MAN_IGN_ESCAPE;
return(man_alloc(curp, pflags, mmsg));
}
static struct roff *
roff_init(struct curparse *curp)
{
return(roff_alloc(mmsg, curp));
}
static struct mdoc *
mdoc_init(struct curparse *curp)
{
int pflags;
/* Defaults from mandoc.1. */
pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE;
if (curp->fflags & FL_IGN_SCOPE)
pflags |= MDOC_IGN_SCOPE;
if (curp->fflags & FL_NIGN_ESCAPE)
pflags &= ~MDOC_IGN_ESCAPE;
if (curp->fflags & FL_NIGN_MACRO)
pflags &= ~MDOC_IGN_MACRO;
return(mdoc_alloc(curp, pflags, mmsg));
}
static void
ffile(const char *file, struct curparse *curp)
{
curp->file = file;
if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
perror(curp->file);
with_error = 1;
return;
}
fdesc(curp);
if (-1 == close(curp->fd))
perror(curp->file);
}
static int
resize_buf(struct buf *buf, size_t initial)
{
void *tmp;
size_t sz;
if (buf->sz == 0)
sz = initial;
else
sz = 2 * buf->sz;
tmp = realloc(buf->buf, sz);
if (NULL == tmp) {
perror(NULL);
return(0);
}
buf->buf = tmp;
buf->sz = sz;
return(1);
}
static int
read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
{
struct stat st;
size_t off;
ssize_t ssz;
if (-1 == fstat(curp->fd, &st)) {
perror(curp->file);
with_error = 1;
return(0);
}
#ifndef __minix
/*
* If we're a regular file, try just reading in the whole entry
* via mmap(). This is faster than reading it into blocks, and
* since each file is only a few bytes to begin with, I'm not
* concerned that this is going to tank any machines.
*/
if (S_ISREG(st.st_mode)) {
if (st.st_size >= (1U << 31)) {
fprintf(stderr, "%s: input too large\n",
curp->file);
with_error = 1;
return(0);
}
*with_mmap = 1;
fb->sz = (size_t)st.st_size;
fb->buf = mmap(NULL, fb->sz, PROT_READ,
MAP_FILE|MAP_SHARED, curp->fd, 0);
if (fb->buf != MAP_FAILED)
return(1);
}
#endif
/*
* If this isn't a regular file (like, say, stdin), then we must
* go the old way and just read things in bit by bit.
*/
*with_mmap = 0;
off = 0;
fb->sz = 0;
fb->buf = NULL;
for (;;) {
if (off == fb->sz) {
if (fb->sz == (1U << 31)) {
fprintf(stderr, "%s: input too large\n",
curp->file);
break;
}
if (! resize_buf(fb, 65536))
break;
}
ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);
if (ssz == 0) {
fb->sz = off;
return(1);
}
if (ssz == -1) {
perror(curp->file);
break;
}
off += (size_t)ssz;
}
free(fb->buf);
fb->buf = NULL;
with_error = 1;
return(0);
}
static void
fdesc(struct curparse *curp)
{
struct buf ln, blk;
int i, pos, lnn, lnn_start, with_mmap, of;
enum rofferr re;
struct man *man;
struct mdoc *mdoc;
struct roff *roff;
man = NULL;
mdoc = NULL;
roff = NULL;
memset(&ln, 0, sizeof(struct buf));
/*
* Two buffers: ln and buf. buf is the input file and may be
* memory mapped. ln is a line buffer and grows on-demand.
*/
if ( ! read_whole_file(curp, &blk, &with_mmap))
return;
if (NULL == curp->roff)
curp->roff = roff_init(curp);
if (NULL == (roff = curp->roff))
goto bailout;
for (i = 0, lnn = 1; i < (int)blk.sz;) {
pos = 0;
lnn_start = lnn;
while (i < (int)blk.sz) {
if ('\n' == blk.buf[i]) {
++i;
++lnn;
break;
}
/* Trailing backslash is like a plain character. */
if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
if (pos >= (int)ln.sz)
if (! resize_buf(&ln, 256))
goto bailout;
ln.buf[pos++] = blk.buf[i++];
continue;
}
/* Found an escape and at least one other character. */
if ('\n' == blk.buf[i + 1]) {
/* Escaped newlines are skipped over */
i += 2;
++lnn;
continue;
}
if ('"' == blk.buf[i + 1]) {
i += 2;
/* Comment, skip to end of line */
for (; i < (int)blk.sz; ++i) {
if ('\n' == blk.buf[i]) {
++i;
++lnn;
break;
}
}
/* Backout trailing whitespaces */
for (; pos > 0; --pos) {
if (ln.buf[pos - 1] != ' ')
break;
if (pos > 2 && ln.buf[pos - 2] == '\\')
break;
}
break;
}
/* Some other escape sequence, copy and continue. */
if (pos + 1 >= (int)ln.sz)
if (! resize_buf(&ln, 256))
goto bailout;
ln.buf[pos++] = blk.buf[i++];
ln.buf[pos++] = blk.buf[i++];
}
if (pos >= (int)ln.sz)
if (! resize_buf(&ln, 256))
goto bailout;
ln.buf[pos] = '\0';
/*
* A significant amount of complexity is contained by
* the roff preprocessor. It's line-oriented but can be
* expressed on one line, so we need at times to
* readjust our starting point and re-run it. The roff
* preprocessor can also readjust the buffers with new
* data, so we pass them in wholesale.
*/
of = 0;
do {
re = roff_parseln(roff, lnn_start,
&ln.buf, &ln.sz, of, &of);
} while (ROFF_RERUN == re);
if (ROFF_IGN == re)
continue;
else if (ROFF_ERR == re)
goto bailout;
/*
* If input parsers have not been allocated, do so now.
* We keep these instanced betwen parsers, but set them
* locally per parse routine since we can use different
* parsers with each one.
*/
if ( ! (man || mdoc))
if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc))
goto bailout;
/* Lastly, push down into the parsers themselves. */
if (man && ! man_parseln(man, lnn_start, ln.buf, of))
goto bailout;
if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of))
goto bailout;
}
/* NOTE a parser may not have been assigned, yet. */
if ( ! (man || mdoc)) {
fprintf(stderr, "%s: Not a manual\n", curp->file);
goto bailout;
}
/* Clean up the parse routine ASTs. */
if (mdoc && ! mdoc_endparse(mdoc))
goto bailout;
if (man && ! man_endparse(man))
goto bailout;
if (roff && ! roff_endparse(roff))
goto bailout;
/* If unset, allocate output dev now (if applicable). */
if ( ! (curp->outman && curp->outmdoc)) {
switch (curp->outtype) {
case (OUTT_XHTML):
curp->outdata = xhtml_alloc(curp->outopts);
break;
case (OUTT_HTML):
curp->outdata = html_alloc(curp->outopts);
break;
case (OUTT_ASCII):
curp->outdata = ascii_alloc(curp->outopts);
curp->outfree = ascii_free;
break;
case (OUTT_PS):
curp->outdata = ps_alloc();
curp->outfree = ps_free;
break;
default:
break;
}
switch (curp->outtype) {
case (OUTT_HTML):
/* FALLTHROUGH */
case (OUTT_XHTML):
curp->outman = html_man;
curp->outmdoc = html_mdoc;
curp->outfree = html_free;
break;
case (OUTT_TREE):
curp->outman = tree_man;
curp->outmdoc = tree_mdoc;
break;
case (OUTT_ASCII):
/* FALLTHROUGH */
case (OUTT_PS):
curp->outman = terminal_man;
curp->outmdoc = terminal_mdoc;
break;
default:
break;
}
}
/* Execute the out device, if it exists. */
if (man && curp->outman)
(*curp->outman)(curp->outdata, man);
if (mdoc && curp->outmdoc)
(*curp->outmdoc)(curp->outdata, mdoc);
cleanup:
if (mdoc)
mdoc_reset(mdoc);
if (man)
man_reset(man);
if (roff)
roff_reset(roff);
if (ln.buf)
free(ln.buf);
if (with_mmap)
munmap(blk.buf, blk.sz);
else
free(blk.buf);
return;
bailout:
with_error = 1;
goto cleanup;
}
static int
pset(const char *buf, int pos, struct curparse *curp,
struct man **man, struct mdoc **mdoc)
{
int i;
/*
* Try to intuit which kind of manual parser should be used. If
* passed in by command-line (-man, -mdoc), then use that
* explicitly. If passed as -mandoc, then try to guess from the
* line: either skip dot-lines, use -mdoc when finding `.Dt', or
* default to -man, which is more lenient.
*/
if ('.' == buf[0] || '\'' == buf[0]) {
for (i = 1; buf[i]; i++)
if (' ' != buf[i] && '\t' != buf[i])
break;
if (0 == buf[i])
return(1);
}
switch (curp->inttype) {
case (INTT_MDOC):
if (NULL == curp->mdoc)
curp->mdoc = mdoc_init(curp);
if (NULL == (*mdoc = curp->mdoc))
return(0);
return(1);
case (INTT_MAN):
if (NULL == curp->man)
curp->man = man_init(curp);
if (NULL == (*man = curp->man))
return(0);
return(1);
default:
break;
}
if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
if (NULL == curp->mdoc)
curp->mdoc = mdoc_init(curp);
if (NULL == (*mdoc = curp->mdoc))
return(0);
return(1);
}
if (NULL == curp->man)
curp->man = man_init(curp);
if (NULL == (*man = curp->man))
return(0);
return(1);
}
static int
moptions(enum intt *tflags, char *arg)
{
if (0 == strcmp(arg, "doc"))
*tflags = INTT_MDOC;
else if (0 == strcmp(arg, "andoc"))
*tflags = INTT_AUTO;
else if (0 == strcmp(arg, "an"))
*tflags = INTT_MAN;
else {
fprintf(stderr, "%s: Bad argument\n", arg);
return(0);
}
return(1);
}
static int
toptions(struct curparse *curp, char *arg)
{
if (0 == strcmp(arg, "ascii"))
curp->outtype = OUTT_ASCII;
else if (0 == strcmp(arg, "lint")) {
curp->outtype = OUTT_LINT;
curp->wflags |= WARN_WALL;
curp->fflags |= FL_STRICT;
}
else if (0 == strcmp(arg, "tree"))
curp->outtype = OUTT_TREE;
else if (0 == strcmp(arg, "html"))
curp->outtype = OUTT_HTML;
else if (0 == strcmp(arg, "xhtml"))
curp->outtype = OUTT_XHTML;
else if (0 == strcmp(arg, "ps"))
curp->outtype = OUTT_PS;
else {
fprintf(stderr, "%s: Bad argument\n", arg);
return(0);
}
return(1);
}
static int
foptions(int *fflags, char *arg)
{
char *v, *o;
const char *toks[8];
toks[0] = "ign-scope";
toks[1] = "no-ign-escape";
toks[2] = "no-ign-macro";
toks[3] = "ign-errors";
toks[4] = "strict";
toks[5] = "ign-escape";
toks[6] = NULL;
while (*arg) {
o = arg;
switch (getsubopt(&arg, UNCONST(toks), &v)) {
case (0):
*fflags |= FL_IGN_SCOPE;
break;
case (1):
*fflags |= FL_NIGN_ESCAPE;
break;
case (2):
*fflags |= FL_NIGN_MACRO;
break;
case (3):
*fflags |= FL_IGN_ERRORS;
break;
case (4):
*fflags |= FL_STRICT;
break;
case (5):
*fflags &= ~FL_NIGN_ESCAPE;
break;
default:
fprintf(stderr, "%s: Bad argument\n", o);
return(0);
}
}
return(1);
}
static int
woptions(int *wflags, char *arg)
{
char *v, *o;
const char *toks[3];
toks[0] = "all";
toks[1] = "error";
toks[2] = NULL;
while (*arg) {
o = arg;
switch (getsubopt(&arg, UNCONST(toks), &v)) {
case (0):
*wflags |= WARN_WALL;
break;
case (1):
*wflags |= WARN_WERR;
break;
default:
fprintf(stderr, "%s: Bad argument\n", o);
return(0);
}
}
return(1);
}
static int
mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
{
struct curparse *cp;
cp = (struct curparse *)arg;
if (t <= MANDOCERR_ERROR) {
if ( ! (cp->wflags & WARN_WALL))
return(1);
with_warning = 1;
} else
with_error = 1;
fprintf(stderr, "%s:%d:%d: %s", cp->file,
ln, col + 1, mandocerrs[t]);
if (msg)
fprintf(stderr, ": %s", msg);
fputc('\n', stderr);
/* This is superfluous, but whatever. */
if (t > MANDOCERR_ERROR)
return(0);
if (cp->wflags & WARN_WERR) {
with_error = 1;
return(0);
}
return(1);
}

55
commands/mdocml/main.h Normal file
View file

@ -0,0 +1,55 @@
/* $Id: main.h,v 1.7 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.
*/
#ifndef MAIN_H
#define MAIN_H
__BEGIN_DECLS
struct mdoc;
struct man;
#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
/*
* Definitions for main.c-visible output device functions, e.g., -Thtml
* and -Tascii. Note that ascii_alloc() is named as such in
* anticipation of latin1_alloc() and so on, all of which map into the
* terminal output routines with different character settings.
*/
void *html_alloc(char *);
void *xhtml_alloc(char *);
void html_mdoc(void *, const struct mdoc *);
void html_man(void *, const struct man *);
void html_free(void *);
void tree_mdoc(void *, const struct mdoc *);
void tree_man(void *, const struct man *);
void *ascii_alloc(char *);
void ascii_free(void *);
void *ps_alloc(void);
void ps_free(void *);
void terminal_mdoc(void *, const struct mdoc *);
void terminal_man(void *, const struct man *);
__END_DECLS
#endif /*!MAIN_H*/

324
commands/mdocml/man.3 Normal file
View file

@ -0,0 +1,324 @@
.\" $Id: man.3,v 1.18 2010/05/25 22:16:59 kristaps Exp $
.\"
.\" Copyright (c) 2009-2010 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.
.\"
.Dd $Mdocdate: May 25 2010 $
.Dt MAN 3
.Os
.Sh NAME
.Nm man ,
.Nm man_alloc ,
.Nm man_endparse ,
.Nm man_free ,
.Nm man_meta ,
.Nm man_node ,
.Nm man_parseln ,
.Nm man_reset
.Nd man macro compiler library
.Sh SYNOPSIS
.In mandoc.h
.In man.h
.Vt extern const char * const * man_macronames;
.Ft "struct man *"
.Fn man_alloc "void *data" "int pflags" "mandocmsg msgs"
.Ft int
.Fn man_endparse "struct man *man"
.Ft void
.Fn man_free "struct man *man"
.Ft "const struct man_meta *"
.Fn man_meta "const struct man *man"
.Ft "const struct man_node *"
.Fn man_node "const struct man *man"
.Ft int
.Fn man_parseln "struct man *man" "int line" "char *buf"
.Ft void
.Fn man_reset "struct man *man"
.Sh DESCRIPTION
The
.Nm
library parses lines of
.Xr man 7
input into an abstract syntax tree (AST).
.Pp
In general, applications initiate a parsing sequence with
.Fn man_alloc ,
parse each line in a document with
.Fn man_parseln ,
close the parsing session with
.Fn man_endparse ,
operate over the syntax tree returned by
.Fn man_node
and
.Fn man_meta ,
then free all allocated memory with
.Fn man_free .
The
.Fn man_reset
function may be used in order to reset the parser for another input
sequence.
See the
.Sx EXAMPLES
section for a full example.
.Pp
Beyond the full set of macros defined in
.Xr man 7 ,
the
.Nm
library also accepts the following macros:
.Pp
.Bl -tag -width Ds -compact
.It PD
Has no effect. Handled as a current-scope line macro.
.It Sp
A synonym for
.Sq sp 0.5v
.Pq part of the standard preamble for Perl documentation .
Handled as a line macro.
.It Vb
A synonym for
.Sq nf
.Pq part of the standard preamble for Perl documentation .
Handled as a current-scope line macro.
.It Ve
A synonym for
.Sq fi ,
closing
.Sq Vb
.Pq part of the standard preamble for Perl documentation .
Handled as a current-scope line macro.
.El
.Pp
Furthermore, the following escapes are accepted to allow
.Xr pod2man 1
documents to be correctly formatted:
\e*(-- (dash),
\e*(PI (pi),
\e*(L" (left double-quote),
\e*(R" (right double-quote),
\e*(C+ (C++),
\e*(C` (left single-quote),
\e*(C' (right single-quote),
\e*(Aq (apostrophe),
\e*^ (hat),
\e*, (comma),
\e*~ (tilde),
\e*/ (forward slash),
\e*: (umlaut),
\e*8 (beta),
\e*o (degree),
\e*(D- (Eth),
\e*(d- (eth),
\e*(Th (Thorn),
and
\e*(th (thorn).
.Sh REFERENCE
This section further defines the
.Sx Types ,
.Sx Functions
and
.Sx Variables
available to programmers.
Following that, the
.Sx Abstract Syntax Tree
section documents the output tree.
.Ss Types
Both functions (see
.Sx Functions )
and variables (see
.Sx Variables )
may use the following types:
.Bl -ohang
.It Vt struct man
An opaque type defined in
.Pa man.c .
Its values are only used privately within the library.
.It Vt mandocmsg
A function callback type defined in
.Pa mandoc.h .
.It Vt struct man_node
A parsed node.
Defined in
.Pa man.h .
See
.Sx Abstract Syntax Tree
for details.
.El
.Ss Functions
Function descriptions follow:
.Bl -ohang
.It Fn man_alloc
Allocates a parsing structure.
The
.Fa data
pointer is passed to
.Fa msgs .
The
.Fa pflags
arguments are defined in
.Pa man.h .
Returns NULL on failure.
If non-NULL, the pointer must be freed with
.Fn man_free .
.It Fn man_reset
Reset the parser for another parse routine.
After its use,
.Fn man_parseln
behaves as if invoked for the first time.
.It Fn man_free
Free all resources of a parser.
The pointer is no longer valid after invocation.
.It Fn man_parseln
Parse a nil-terminated line of input.
This line should not contain the trailing newline.
Returns 0 on failure, 1 on success.
The input buffer
.Fa buf
is modified by this function.
.It Fn man_endparse
Signals that the parse is complete.
Note that if
.Fn man_endparse
is called subsequent to
.Fn man_node ,
the resulting tree is incomplete.
Returns 0 on failure, 1 on success.
.It Fn man_node
Returns the first node of the parse.
Note that if
.Fn man_parseln
or
.Fn man_endparse
return 0, the tree will be incomplete.
.It Fn man_meta
Returns the document's parsed meta-data.
If this information has not yet been supplied or
.Fn man_parseln
or
.Fn man_endparse
return 0, the data will be incomplete.
.El
.Ss Variables
The following variables are also defined:
.Bl -ohang
.It Va man_macronames
An array of string-ified token names.
.El
.Ss Abstract Syntax Tree
The
.Nm
functions produce an abstract syntax tree (AST) describing input in a
regular form.
It may be reviewed at any time with
.Fn man_nodes ;
however, if called before
.Fn man_endparse ,
or after
.Fn man_endparse
or
.Fn man_parseln
fail, it may be incomplete.
.Pp
This AST is governed by the ontological rules dictated in
.Xr man 7
and derives its terminology accordingly.
.Pp
The AST is composed of
.Vt struct man_node
nodes with element, root and text types as declared by the
.Va type
field.
Each node also provides its parse point (the
.Va line ,
.Va sec ,
and
.Va pos
fields), its position in the tree (the
.Va parent ,
.Va child ,
.Va next
and
.Va prev
fields) and some type-specific data.
.Pp
The tree itself is arranged according to the following normal form,
where capitalised non-terminals represent nodes.
.Pp
.Bl -tag -width "ELEMENTXX" -compact
.It ROOT
\(<- mnode+
.It mnode
\(<- ELEMENT | TEXT | BLOCK
.It BLOCK
\(<- HEAD BODY
.It HEAD
\(<- mnode*
.It BODY
\(<- mnode*
.It ELEMENT
\(<- ELEMENT | TEXT*
.It TEXT
\(<- [[:alpha:]]*
.El
.Pp
The only elements capable of nesting other elements are those with
next-lint scope as documented in
.Xr man 7 .
.Sh EXAMPLES
The following example reads lines from stdin and parses them, operating
on the finished parse tree with
.Fn parsed .
This example does not error-check nor free memory upon failure.
.Bd -literal -offset indent
struct man *man;
struct man_node *node;
char *buf;
size_t len;
int line;
line = 1;
man = man_alloc(NULL, 0, NULL);
buf = NULL;
alloc_len = 0;
while ((len = getline(&buf, &alloc_len, stdin)) >= 0) {
if (len && buflen[len - 1] = '\en')
buf[len - 1] = '\e0';
if ( ! man_parseln(man, line, buf))
errx(1, "man_parseln");
line++;
}
free(buf);
if ( ! man_endparse(man))
errx(1, "man_endparse");
if (NULL == (node = man_node(man)))
errx(1, "man_node");
parsed(man, node);
man_free(man);
.Ed
.Pp
Please see
.Pa main.c
in the source archive for a rigorous reference.
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr man 7
.Sh AUTHORS
The
.Nm
library was written by
.An Kristaps Dzonsons Aq kristaps@bsd.lv .

968
commands/mdocml/man.7 Normal file
View file

@ -0,0 +1,968 @@
.\" $Id: man.7,v 1.74 2010/05/26 14:03:54 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.
.\"
.Dd $Mdocdate: May 26 2010 $
.Dt MAN 7
.Os
.Sh NAME
.Nm man
.Nd man language reference
.Sh DESCRIPTION
The
.Nm man
language was historically used to format
.Ux
manuals.
This reference document describes its syntax, structure, and usage.
.Pp
.Bf -emphasis
Do not use
.Nm
to write your manuals.
.Ef
Use the
.Xr mdoc 7
language, instead.
.Pp
An
.Nm
document follows simple rules: lines beginning with the control
character
.Sq \&.
are parsed for macros.
Other lines are interpreted within the scope of
prior macros:
.Bd -literal -offset indent
\&.SH Macro lines change control state.
Other lines are interpreted within the current state.
.Ed
.Sh INPUT ENCODING
.Nm
documents may contain only graphable 7-bit ASCII characters, the
space character, and the tabs character.
All manuals must have
.Ux
line termination.
.Pp
Blank lines are acceptable; where found, the output will assert a
vertical space.
.Ss Comments
Text following a
.Sq \e\*" ,
whether in a macro or free-form text line, is ignored to the end of
line.
A macro line with only a control character and comment escape,
.Sq \&.\e" ,
is also ignored.
Macro lines with only a control character and optionally whitespace are
stripped from input.
.Ss Special Characters
Special characters may occur in both macro and free-form lines.
Sequences begin with the escape character
.Sq \e
followed by either an open-parenthesis
.Sq \&(
for two-character sequences; an open-bracket
.Sq \&[
for n-character sequences (terminated at a close-bracket
.Sq \&] ) ;
or a single one-character sequence.
See
.Xr mandoc_char 7
for a complete list.
Examples include
.Sq \e(em
.Pq em-dash
and
.Sq \ee
.Pq back-slash .
.Ss Text Decoration
Terms may be text-decorated using the
.Sq \ef
escape followed by an indicator: B (bold), I, (italic), R (Roman), or P
(revert to previous mode):
.Pp
.D1 \efBbold\efR \efIitalic\efP
.Pp
A numerical representation 3, 2, or 1 (bold, italic, and Roman,
respectively) may be used instead.
A text decoration is only valid, if specified in free-form text, until
the next macro invocation; if specified within a macro, it's only valid
until the macro closes scope.
Note that macros like
.Sx \&BR
open and close a font scope with each argument.
.Pp
Text may also be sized with the
.Sq \es
escape, whose syntax is one of
.Sq \es+-n
for one-digit numerals;
.Sq \es(+-nn
or
.Sq \es+-(nn
for two-digit numerals; and
.Sq \es[+-N] ,
.Sq \es+-[N] ,
.Sq \es'+-N' ,
or
.Sq \es+-'N'
for arbitrary-digit numerals:
.Pp
.D1 \es+1bigger\es-1
.D1 \es[+10]much bigger\es[-10]
.D1 \es+(10much bigger\es-(10
.D1 \es+'100'much much bigger\es-'100'
.Pp
Both
.Sq \es
and
.Sq \ef
attributes are forgotten when entering or exiting a macro block.
.Ss Whitespace
Whitespace consists of the space character.
In free-form lines, whitespace is preserved within a line; un-escaped
trailing spaces are stripped from input (unless in a literal context).
Blank free-form lines, which may include spaces, are permitted and
rendered as an empty line.
.Pp
In macro lines, whitespace delimits arguments and is discarded.
If arguments are quoted, whitespace within the quotes is retained.
.Ss Dates
The
.Sx \&TH
macro is the only
.Nm
macro that requires a date.
The form for this date is the ISO-8601
standard
.Cm YYYY-MM-DD .
.Ss Scaling Widths
Many macros support scaled widths for their arguments, such as
stipulating a two-inch paragraph indentation with the following:
.Bd -literal -offset indent
\&.HP 2i
.Ed
.Pp
The syntax for scaled widths is
.Sq Li [+-]?[0-9]*.[0-9]*[:unit:]? ,
where a decimal must be preceded or proceeded by at least one digit.
Negative numbers, while accepted, are truncated to zero.
The following scaling units are accepted:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It c
centimetre
.It i
inch
.It P
pica (~1/6 inch)
.It p
point (~1/72 inch)
.It f
synonym for
.Sq u
.It v
default vertical span
.It m
width of rendered
.Sq m
.Pq em
character
.It n
width of rendered
.Sq n
.Pq en
character
.It u
default horizontal span
.It M
mini-em (~1/100 em)
.El
.Pp
Using anything other than
.Sq m ,
.Sq n ,
.Sq u ,
or
.Sq v
is necessarily non-portable across output media.
.Pp
If a scaling unit is not provided, the numerical value is interpreted
under the default rules of
.Sq v
for vertical spaces and
.Sq u
for horizontal ones.
.Em Note :
this differs from
.Xr mdoc 7 ,
which, if a unit is not provided, will instead interpret the string as
literal text.
.Ss Sentence Spacing
When composing a manual, make sure that your sentences end at the end of
a line.
By doing so, front-ends will be able to apply the proper amount of
spacing after the end of sentence (unescaped) period, exclamation mark,
or question mark followed by zero or more non-sentence closing
delimiters (
.Ns Sq \&) ,
.Sq \&] ,
.Sq \&' ,
.Sq \&" ) .
.Sh MANUAL STRUCTURE
Each
.Nm
document must contain contains at least the
.Sx \&TH
macro describing the document's section and title.
It may occur anywhere in the document, although conventionally, it
appears as the first macro.
.Pp
Beyond
.Sx \&TH ,
at least one macro or text node must appear in the document.
Documents are generally structured as follows:
.Bd -literal -offset indent
\&.TH FOO 1 2009-10-10
\&.
\&.SH NAME
\efBfoo\efR \e(en a description goes here
\&.\e\*q The next is for sections 2 & 3 only.
\&.\e\*q .SH LIBRARY
\&.
\&.SH SYNOPSIS
\efBfoo\efR [\efB\e-options\efR] arguments...
\&.
\&.SH DESCRIPTION
The \efBfoo\efR utility processes files...
\&.
\&.\e\*q .SH IMPLEMENTATION NOTES
\&.\e\*q The next is for sections 2, 3, & 9 only.
\&.\e\*q .SH RETURN VALUES
\&.\e\*q The next is for sections 1, 6, 7, & 8 only.
\&.\e\*q .SH ENVIRONMENT
\&.\e\*q .SH FILES
\&.\e\*q The next is for sections 1 & 8 only.
\&.\e\*q .SH EXIT STATUS
\&.\e\*q .SH EXAMPLES
\&.\e\*q The next is for sections 1, 4, 6, 7, & 8 only.
\&.\e\*q .SH DIAGNOSTICS
\&.\e\*q The next is for sections 2, 3, & 9 only.
\&.\e\*q .SH ERRORS
\&.\e\*q .SH SEE ALSO
\&.\e\*q .BR foo ( 1 )
\&.\e\*q .SH STANDARDS
\&.\e\*q .SH HISTORY
\&.\e\*q .SH AUTHORS
\&.\e\*q .SH CAVEATS
\&.\e\*q .SH BUGS
\&.\e\*q .SH SECURITY CONSIDERATIONS
.Ed
.Pp
The sections in a
.Nm
document are conventionally ordered as they appear above.
Sections should be composed as follows:
.Bl -ohang -offset indent
.It Em NAME
The name(s) and a short description of the documented material.
The syntax for this is generally as follows:
.Pp
.D1 \efBname\efR \e(en description
.It Em LIBRARY
The name of the library containing the documented material, which is
assumed to be a function in a section 2 or 3 manual.
For functions in the C library, this may be as follows:
.Pp
.D1 Standard C Library (libc, -lc)
.It Em SYNOPSIS
Documents the utility invocation syntax, function call syntax, or device
configuration.
.Pp
For the first, utilities (sections 1, 6, and 8), this is
generally structured as follows:
.Pp
.D1 \efBname\efR [-\efBab\efR] [-\efBc\efR\efIarg\efR] \efBpath\efR...
.Pp
For the second, function calls (sections 2, 3, 9):
.Pp
.D1 \&.B char *name(char *\efIarg\efR);
.Pp
And for the third, configurations (section 4):
.Pp
.D1 \&.B name* at cardbus ? function ?
.Pp
Manuals not in these sections generally don't need a
.Em SYNOPSIS .
.It Em DESCRIPTION
This expands upon the brief, one-line description in
.Em NAME .
It usually contains a break-down of the options (if documenting a
command).
.It Em IMPLEMENTATION NOTES
Implementation-specific notes should be kept here.
This is useful when implementing standard functions that may have side
effects or notable algorithmic implications.
.It Em RETURN VALUES
This section is the dual of
.Em EXIT STATUS ,
which is used for commands.
It documents the return values of functions in sections 2, 3, and 9.
.It Em ENVIRONMENT
Documents any usages of environment variables, e.g.,
.Xr environ 7 .
.It Em FILES
Documents files used.
It's helpful to document both the file and a short description of how
the file is used (created, modified, etc.).
.It Em EXIT STATUS
Command exit status for section 1, 6, and 8 manuals.
This section is the dual of
.Em RETURN VALUES ,
which is used for functions.
Historically, this information was described in
.Em DIAGNOSTICS ,
a practise that is now discouraged.
.It Em EXAMPLES
Example usages.
This often contains snippets of well-formed,
well-tested invocations.
Make doubly sure that your examples work properly!
.It Em DIAGNOSTICS
Documents error conditions.
This is most useful in section 4 manuals.
Historically, this section was used in place of
.Em EXIT STATUS
for manuals in sections 1, 6, and 8; however, this practise is
discouraged.
.It Em ERRORS
Documents error handling in sections 2, 3, and 9.
.It Em SEE ALSO
References other manuals with related topics.
This section should exist for most manuals.
.Pp
.D1 \&.BR bar \&( 1 \&),
.Pp
Cross-references should conventionally be ordered
first by section, then alphabetically.
.It Em STANDARDS
References any standards implemented or used, such as
.Pp
.D1 IEEE Std 1003.2 (\e(lqPOSIX.2\e(rq)
.Pp
If not adhering to any standards, the
.Em HISTORY
section should be used.
.It Em HISTORY
The history of any manual without a
.Em STANDARDS
section should be described in this section.
.It Em AUTHORS
Credits to authors, if applicable, should appear in this section.
Authors should generally be noted by both name and an e-mail address.
.It Em CAVEATS
Explanations of common misuses and misunderstandings should be explained
in this section.
.It Em BUGS
Extant bugs should be described in this section.
.It Em SECURITY CONSIDERATIONS
Documents any security precautions that operators should consider.
.El
.Sh MACRO SYNTAX
Macros are one to three three characters in length and begin with a
control character ,
.Sq \&. ,
at the beginning of the line.
The
.Sq \(aq
macro control character is also accepted.
An arbitrary amount of whitespace (spaces or tabs) may sit between the
control character and the macro name.
Thus, the following are equivalent:
.Bd -literal -offset indent
\&.PP
\&.\ \ \ PP
.Ed
.Pp
The
.Nm
macros are classified by scope: line scope or block scope.
Line macros are only scoped to the current line (and, in some
situations, the subsequent line).
Block macros are scoped to the current line and subsequent lines until
closed by another block macro.
.Ss Line Macros
Line macros are generally scoped to the current line, with the body
consisting of zero or more arguments.
If a macro is scoped to the next line and the line arguments are empty,
the next line, which must be text, is used instead.
Thus:
.Bd -literal -offset indent
\&.I
foo
.Ed
.Pp
is equivalent to
.Sq \&.I foo .
If next-line macros are invoked consecutively, only the last is used.
If a next-line macro is followed by a non-next-line macro, an error is
raised (unless in the case of
.Sx \&br ,
.Sx \&sp ,
or
.Sx \&na ) .
.Pp
The syntax is as follows:
.Bd -literal -offset indent
\&.YO \(lBbody...\(rB
\(lBbody...\(rB
.Ed
.Pp
.Bl -column -compact -offset indent "MacroX" "ArgumentsX" "ScopeXXXXX" "CompatX"
.It Em Macro Ta Em Arguments Ta Em Scope Ta Em Notes
.It Sx \&AT Ta <=1 Ta current Ta \&
.It Sx \&B Ta n Ta next-line Ta \&
.It Sx \&BI Ta n Ta current Ta \&
.It Sx \&BR Ta n Ta current Ta \&
.It Sx \&DT Ta 0 Ta current Ta \&
.It Sx \&I Ta n Ta next-line Ta \&
.It Sx \&IB Ta n Ta current Ta \&
.It Sx \&IR Ta n Ta current Ta \&
.\" .It Sx \&PD Ta n Ta current Ta compat
.It Sx \&R Ta n Ta next-line Ta \&
.It Sx \&RB Ta n Ta current Ta \&
.It Sx \&RI Ta n Ta current Ta \&
.It Sx \&SB Ta n Ta next-line Ta \&
.It Sx \&SM Ta n Ta next-line Ta \&
.It Sx \&TH Ta >1, <6 Ta current Ta \&
.It Sx \&UC Ta <=1 Ta current Ta \&
.It Sx \&br Ta 0 Ta current Ta compat
.It Sx \&fi Ta 0 Ta current Ta compat
.It Sx \&i Ta n Ta current Ta compat
.It Sx \&na Ta 0 Ta current Ta compat
.It Sx \&nf Ta 0 Ta current Ta compat
.It Sx \&r Ta 0 Ta current Ta compat
.It Sx \&sp Ta 1 Ta current Ta compat
.\" .It Sx \&Sp Ta <1 Ta current Ta compat
.\" .It Sx \&Vb Ta <1 Ta current Ta compat
.\" .It Sx \&Ve Ta 0 Ta current Ta compat
.El
.Pp
Macros marked as
.Qq compat
are included for compatibility with the significant corpus of existing
manuals that mix dialects of roff.
These macros should not be used for portable
.Nm
manuals.
.Ss Block Macros
Block macros are comprised of a head and body.
Like for in-line macros, the head is scoped to the current line and, in
one circumstance, the next line (the next-line stipulations as in
.Sx Line Macros
apply here as well).
.Pp
The syntax is as follows:
.Bd -literal -offset indent
\&.YO \(lBhead...\(rB
\(lBhead...\(rB
\(lBbody...\(rB
.Ed
.Pp
The closure of body scope may be to the section, where a macro is closed
by
.Sx \&SH ;
sub-section, closed by a section or
.Sx \&SS ;
part, closed by a section, sub-section, or
.Sx \&RE ;
or paragraph, closed by a section, sub-section, part,
.Sx \&HP ,
.Sx \&IP ,
.Sx \&LP ,
.Sx \&P ,
.Sx \&PP ,
or
.Sx \&TP .
No closure refers to an explicit block closing macro.
.Pp
As a rule, block macros may not be nested; thus, calling a block macro
while another block macro scope is open, and the open scope is not
implicitly closed, is syntactically incorrect.
.Pp
.Bl -column -compact -offset indent "MacroX" "ArgumentsX" "Head ScopeX" "sub-sectionX" "compatX"
.It Em Macro Ta Em Arguments Ta Em Head Scope Ta Em Body Scope Ta Em Notes
.It Sx \&HP Ta <2 Ta current Ta paragraph Ta \&
.It Sx \&IP Ta <3 Ta current Ta paragraph Ta \&
.It Sx \&LP Ta 0 Ta current Ta paragraph Ta \&
.It Sx \&P Ta 0 Ta current Ta paragraph Ta \&
.It Sx \&PP Ta 0 Ta current Ta paragraph Ta \&
.It Sx \&RE Ta 0 Ta current Ta none Ta compat
.It Sx \&RS Ta 1 Ta current Ta part Ta compat
.It Sx \&SH Ta >0 Ta next-line Ta section Ta \&
.It Sx \&SS Ta >0 Ta next-line Ta sub-section Ta \&
.It Sx \&TP Ta n Ta next-line Ta paragraph Ta \&
.El
.Pp
Macros marked
.Qq compat
are as mentioned in
.Sx Line Macros .
.Pp
If a block macro is next-line scoped, it may only be followed by in-line
macros for decorating text.
.Sh REFERENCE
This section is a canonical reference to all macros, arranged
alphabetically.
For the scoping of individual macros, see
.Sx MACRO SYNTAX .
.Ss \&AT
Sets the volume for the footer for compatibility with man pages from
.Tn AT&T UNIX
releases.
The optional arguments specify which release it is from.
.Ss \&B
Text is rendered in bold face.
.Pp
See also
.Sx \&I ,
.Sx \&R ,
.Sx \&b ,
.Sx \&i ,
and
.Sx \&r .
.Ss \&BI
Text is rendered alternately in bold face and italic.
Thus,
.Sq .BI this word and that
causes
.Sq this
and
.Sq and
to render in bold face, while
.Sq word
and
.Sq that
render in italics.
Whitespace between arguments is omitted in output.
.Pp
Examples:
.Pp
.D1 \&.BI bold italic bold italic
.Pp
The output of this example will be emboldened
.Dq bold
and italicised
.Dq italic ,
with spaces stripped between arguments.
.Pp
See also
.Sx \&IB ,
.Sx \&BR ,
.Sx \&RB ,
.Sx \&RI ,
and
.Sx \&IR .
.Ss \&BR
Text is rendered alternately in bold face and roman (the default font).
Whitespace between arguments is omitted in output.
.Pp
See
.Sx \&BI
for an equivalent example.
.Pp
See also
.Sx \&BI ,
.Sx \&IB ,
.Sx \&RB ,
.Sx \&RI ,
and
.Sx \&IR .
.Ss \&DT
Has no effect.
Included for compatibility.
.Ss \&HP
Begin a paragraph whose initial output line is left-justified, but
subsequent output lines are indented, with the following syntax:
.Bd -filled -offset indent
.Pf \. Sx \&HP
.Op Cm width
.Ed
.Pp
The
.Cm width
argument must conform to
.Sx Scaling Widths .
If specified, it's saved for later paragraph left-margins; if unspecified, the
saved or default width is used.
.Pp
See also
.Sx \&IP ,
.Sx \&LP ,
.Sx \&P ,
.Sx \&PP ,
and
.Sx \&TP .
.Ss \&I
Text is rendered in italics.
.Pp
See also
.Sx \&B ,
.Sx \&R ,
.Sx \&b ,
.Sx \&i ,
and
.Sx \&r .
.Ss \&IB
Text is rendered alternately in italics and bold face. Whitespace
between arguments is omitted in output.
.Pp
See
.Sx \&BI
for an equivalent example.
.Pp
See also
.Sx \&BI ,
.Sx \&BR ,
.Sx \&RB ,
.Sx \&RI ,
and
.Sx \&IR .
.Ss \&IP
Begin an indented paragraph with the following syntax:
.Bd -filled -offset indent
.Pf \. Sx \&IP
.Op Cm head Op Cm width
.Ed
.Pp
The
.Cm width
argument defines the width of the left margin and is defined by
.Sx Scaling Widths ,
It's saved for later paragraph left-margins; if unspecified, the saved or
default width is used.
.Pp
The
.Cm head
argument is used as a leading term, flushed to the left margin.
This is useful for bulleted paragraphs and so on.
.Pp
See also
.Sx \&HP ,
.Sx \&LP ,
.Sx \&P ,
.Sx \&PP ,
and
.Sx \&TP .
.Ss \&IR
Text is rendered alternately in italics and roman (the default font).
Whitespace between arguments is omitted in output.
.Pp
See
.Sx \&BI
for an equivalent example.
.Pp
See also
.Sx \&BI ,
.Sx \&IB ,
.Sx \&BR ,
.Sx \&RB ,
and
.Sx \&RI .
.Ss \&LP
Begin an undecorated paragraph.
The scope of a paragraph is closed by a subsequent paragraph,
sub-section, section, or end of file.
The saved paragraph left-margin width is re-set to the default.
.Pp
See also
.Sx \&HP ,
.Sx \&IP ,
.Sx \&P ,
.Sx \&PP ,
and
.Sx \&TP .
.Ss \&P
Synonym for
.Sx \&LP .
.Pp
See also
.Sx \&HP ,
.Sx \&IP ,
.Sx \&LP ,
.Sx \&PP ,
and
.Sx \&TP .
.Ss \&PP
Synonym for
.Sx \&LP .
.Pp
See also
.Sx \&HP ,
.Sx \&IP ,
.Sx \&LP ,
.Sx \&P ,
and
.Sx \&TP .
.Ss \&R
Text is rendered in roman (the default font).
.Pp
See also
.Sx \&I ,
.Sx \&B ,
.Sx \&b ,
.Sx \&i ,
and
.Sx \&r .
.Ss \&RB
Text is rendered alternately in roman (the default font) and bold face.
Whitespace between arguments is omitted in output.
.Pp
See
.Sx \&BI
for an equivalent example.
.Pp
See also
.Sx \&BI ,
.Sx \&IB ,
.Sx \&BR ,
.Sx \&RI ,
and
.Sx \&IR .
.Ss \&RE
Explicitly close out the scope of a prior
.Sx \&RS .
.Ss \&RI
Text is rendered alternately in roman (the default font) and italics.
Whitespace between arguments is omitted in output.
.Pp
See
.Sx \&BI
for an equivalent example.
.Pp
See also
.Sx \&BI ,
.Sx \&IB ,
.Sx \&BR ,
.Sx \&RB ,
and
.Sx \&IR .
.Ss \&RS
Begin a part setting the left margin.
The left margin controls the offset, following an initial indentation,
to un-indented text such as that of
.Sx \&PP .
This has the following syntax:
.Bd -filled -offset indent
.Pf \. Sx \&Rs
.Op Cm width
.Ed
.Pp
The
.Cm width
argument must conform to
.Sx Scaling Widths .
If not specified, the saved or default width is used.
.Ss \&SB
Text is rendered in small size (one point smaller than the default font)
bold face.
.Ss \&SH
Begin a section.
The scope of a section is only closed by another section or the end of
file.
The paragraph left-margin width is re-set to the default.
.Ss \&SM
Text is rendered in small size (one point smaller than the default
font).
.Ss \&SS
Begin a sub-section.
The scope of a sub-section is closed by a subsequent sub-section,
section, or end of file.
The paragraph left-margin width is re-set to the default.
.Ss \&TH
Sets the title of the manual page with the following syntax:
.Bd -filled -offset indent
.Pf \. Sx \&TH
.Cm title section
.Op Cm date Op Cm source Op Cm volume
.Ed
.Pp
At least the upper-case document title
.Cm title
and numeric manual section
.Cm section
arguments must be provided.
The
.Cm date
argument should be formatted as described in
.Sx Dates ,
but will be printed verbatim if it is not.
If the date is not specified, the current date is used.
The
.Cm source
string specifies the organisation providing the utility.
The
.Cm volume
string replaces the default rendered volume, which is dictated by the
manual section.
.Pp
Examples:
.Pp
.D1 \&.TH CVS 5 "1992-02-12" GNU
.Ss \&TP
Begin a paragraph where the head, if exceeding the indentation width, is
followed by a newline; if not, the body follows on the same line after a
buffer to the indentation width.
Subsequent output lines are indented.
The syntax is as follows:
.Bd -filled -offset indent
.Pf \. Sx \&TP
.Op Cm width
.Ed
.Pp
The
.Cm width
argument must conform to
.Sx Scaling Widths .
If specified, it's saved for later paragraph left-margins; if
unspecified, the saved or default width is used.
.Pp
See also
.Sx \&HP ,
.Sx \&IP ,
.Sx \&LP ,
.Sx \&P ,
and
.Sx \&PP .
.\" .
.\" .
.\" .Ss \&PD
.\" Has no effect. Included for compatibility.
.\" .
.\" .
.Ss \&UC
Sets the volume for the footer for compatibility with man pages from
BSD releases.
The optional first argument specifies which release it is from.
.Ss \&br
Breaks the current line.
Consecutive invocations have no further effect.
.Pp
See also
.Sx \&sp .
.Ss \&fi
End literal mode begun by
.Sx \&nf .
.Ss \&i
Italicise arguments.
Synonym for
.Sx \&I .
.Pp
See also
.Sx \&B ,
.Sx \&I ,
.Sx \&R .
.Sx \&b ,
and
.Sx \&r .
.Ss \&na
Don't align to the right margin.
.Ss \&nf
Begin literal mode: all subsequent free-form lines have their end of
line boundaries preserved.
May be ended by
.Sx \&fi .
.Ss \&r
Fonts and styles (bold face, italics) reset to roman (default font).
.Pp
See also
.Sx \&B ,
.Sx \&I ,
.Sx \&R ,
.Sx \&b ,
and
.Sx \&i .
.Ss \&sp
Insert vertical spaces into output with the following syntax:
.Bd -filled -offset indent
.Pf \. Sx \&sp
.Op Cm height
.Ed
.Pp
Insert
.Cm height
spaces, which must conform to
.Sx Scaling Widths .
If 0, this is equivalent to the
.Sx \&br
macro.
Defaults to 1, if unspecified.
.Pp
See also
.Sx \&br .
.\" .Ss \&Sp
.\" A synonym for
.\" .Sx \&sp
.\" .Cm 0.5v .
.\" .
.\" .Ss \&Vb
.\" A synonym for
.\" .Sx \&nf .
.\" Accepts an argument (the height of the formatted space) which is
.\" disregarded.
.\" .
.\" .Ss \&Ve
.\" A synonym for
.\" .Sx \&fi .
.\" .
.Sh COMPATIBILITY
This section documents areas of questionable portability between
implementations of the
.Nm
language.
.Pp
.Bl -dash -compact
.It
In quoted literals, GNU troff allowed pair-wise double-quotes to produce
a standalone double-quote in formatted output.
It is not known whether this behaviour is exhibited by other formatters.
.It
The
.Sx \&sp
macro does not accept negative values in mandoc.
In GNU troff, this would result in strange behaviour.
.It
The
.Sq \(aq
macro control character, in GNU troff (and prior troffs) suppresses a
newline before macro output; in mandoc, it is an alias for the standard
.Sq \&.
control character.
.El
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr mandoc_char 7
.Sh AUTHORS
The
.Nm
reference was written by
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
.Sh CAVEATS
Do not use this language.
Use
.Xr mdoc 7 ,
instead.

677
commands/mdocml/man.c Normal file
View file

@ -0,0 +1,677 @@
/* $Id: man.c,v 1.76 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "libman.h"
#include "libmandoc.h"
const char *const __man_macronames[MAN_MAX] = {
"br", "TH", "SH", "SS",
"TP", "LP", "PP", "P",
"IP", "HP", "SM", "SB",
"BI", "IB", "BR", "RB",
"R", "B", "I", "IR",
"RI", "na", "i", "sp",
"nf", "fi", "r", "RE",
"RS", "DT", "UC", "PD",
"Sp", "Vb", "Ve", "AT",
};
const char * const *man_macronames = __man_macronames;
static struct man_node *man_node_alloc(int, int,
enum man_type, enum mant);
static int man_node_append(struct man *,
struct man_node *);
static void man_node_free(struct man_node *);
static void man_node_unlink(struct man *,
struct man_node *);
static int man_ptext(struct man *, int, char *, int);
static int man_pmacro(struct man *, int, char *, int);
static void man_free1(struct man *);
static void man_alloc1(struct man *);
static int macrowarn(struct man *, int, const char *, int);
const struct man_node *
man_node(const struct man *m)
{
return(MAN_HALT & m->flags ? NULL : m->first);
}
const struct man_meta *
man_meta(const struct man *m)
{
return(MAN_HALT & m->flags ? NULL : &m->meta);
}
void
man_reset(struct man *man)
{
man_free1(man);
man_alloc1(man);
}
void
man_free(struct man *man)
{
man_free1(man);
free(man);
}
struct man *
man_alloc(void *data, int pflags, mandocmsg msg)
{
struct man *p;
p = mandoc_calloc(1, sizeof(struct man));
man_hash_init();
p->data = data;
p->pflags = pflags;
p->msg = msg;
man_alloc1(p);
return(p);
}
int
man_endparse(struct man *m)
{
if (MAN_HALT & m->flags)
return(0);
else if (man_macroend(m))
return(1);
m->flags |= MAN_HALT;
return(0);
}
int
man_parseln(struct man *m, int ln, char *buf, int offs)
{
if (MAN_HALT & m->flags)
return(0);
return(('.' == buf[offs] || '\'' == buf[offs]) ?
man_pmacro(m, ln, buf, offs) :
man_ptext(m, ln, buf, offs));
}
static void
man_free1(struct man *man)
{
if (man->first)
man_node_delete(man, man->first);
if (man->meta.title)
free(man->meta.title);
if (man->meta.source)
free(man->meta.source);
if (man->meta.rawdate)
free(man->meta.rawdate);
if (man->meta.vol)
free(man->meta.vol);
if (man->meta.msec)
free(man->meta.msec);
}
static void
man_alloc1(struct man *m)
{
memset(&m->meta, 0, sizeof(struct man_meta));
m->flags = 0;
m->last = mandoc_calloc(1, sizeof(struct man_node));
m->first = m->last;
m->last->type = MAN_ROOT;
m->last->tok = MAN_MAX;
m->next = MAN_NEXT_CHILD;
}
static int
man_node_append(struct man *man, struct man_node *p)
{
assert(man->last);
assert(man->first);
assert(MAN_ROOT != p->type);
switch (man->next) {
case (MAN_NEXT_SIBLING):
man->last->next = p;
p->prev = man->last;
p->parent = man->last->parent;
break;
case (MAN_NEXT_CHILD):
man->last->child = p;
p->parent = man->last;
break;
default:
abort();
/* NOTREACHED */
}
assert(p->parent);
p->parent->nchild++;
if ( ! man_valid_pre(man, p))
return(0);
switch (p->type) {
case (MAN_HEAD):
assert(MAN_BLOCK == p->parent->type);
p->parent->head = p;
break;
case (MAN_BODY):
assert(MAN_BLOCK == p->parent->type);
p->parent->body = p;
break;
default:
break;
}
man->last = p;
switch (p->type) {
case (MAN_TEXT):
if ( ! man_valid_post(man))
return(0);
if ( ! man_action_post(man))
return(0);
break;
default:
break;
}
return(1);
}
static struct man_node *
man_node_alloc(int line, int pos, enum man_type type, enum mant tok)
{
struct man_node *p;
p = mandoc_calloc(1, sizeof(struct man_node));
p->line = line;
p->pos = pos;
p->type = type;
p->tok = tok;
return(p);
}
int
man_elem_alloc(struct man *m, int line, int pos, enum mant tok)
{
struct man_node *p;
p = man_node_alloc(line, pos, MAN_ELEM, tok);
if ( ! man_node_append(m, p))
return(0);
m->next = MAN_NEXT_CHILD;
return(1);
}
int
man_head_alloc(struct man *m, int line, int pos, enum mant tok)
{
struct man_node *p;
p = man_node_alloc(line, pos, MAN_HEAD, tok);
if ( ! man_node_append(m, p))
return(0);
m->next = MAN_NEXT_CHILD;
return(1);
}
int
man_body_alloc(struct man *m, int line, int pos, enum mant tok)
{
struct man_node *p;
p = man_node_alloc(line, pos, MAN_BODY, tok);
if ( ! man_node_append(m, p))
return(0);
m->next = MAN_NEXT_CHILD;
return(1);
}
int
man_block_alloc(struct man *m, int line, int pos, enum mant tok)
{
struct man_node *p;
p = man_node_alloc(line, pos, MAN_BLOCK, tok);
if ( ! man_node_append(m, p))
return(0);
m->next = MAN_NEXT_CHILD;
return(1);
}
int
man_word_alloc(struct man *m, int line, int pos, const char *word)
{
struct man_node *n;
size_t sv, len;
len = strlen(word);
n = man_node_alloc(line, pos, MAN_TEXT, MAN_MAX);
n->string = mandoc_malloc(len + 1);
sv = strlcpy(n->string, word, len + 1);
/* Prohibit truncation. */
assert(sv < len + 1);
if ( ! man_node_append(m, n))
return(0);
m->next = MAN_NEXT_SIBLING;
return(1);
}
/*
* Free all of the resources held by a node. This does NOT unlink a
* node from its context; for that, see man_node_unlink().
*/
static void
man_node_free(struct man_node *p)
{
if (p->string)
free(p->string);
free(p);
}
void
man_node_delete(struct man *m, struct man_node *p)
{
while (p->child)
man_node_delete(m, p->child);
man_node_unlink(m, p);
man_node_free(p);
}
static int
man_ptext(struct man *m, int line, char *buf, int offs)
{
int i;
/* Ignore bogus comments. */
if ('\\' == buf[offs] &&
'.' == buf[offs + 1] &&
'"' == buf[offs + 2])
return(man_pmsg(m, line, offs, MANDOCERR_BADCOMMENT));
/* Literal free-form text whitespace is preserved. */
if (MAN_LITERAL & m->flags) {
if ( ! man_word_alloc(m, line, offs, buf + offs))
return(0);
goto descope;
}
/* Pump blank lines directly into the backend. */
for (i = offs; ' ' == buf[i]; i++)
/* Skip leading whitespace. */ ;
if ('\0' == buf[i]) {
/* Allocate a blank entry. */
if ( ! man_word_alloc(m, line, offs, ""))
return(0);
goto descope;
}
/*
* Warn if the last un-escaped character is whitespace. Then
* strip away the remaining spaces (tabs stay!).
*/
i = (int)strlen(buf);
assert(i);
if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
if (i > 1 && '\\' != buf[i - 2])
if ( ! man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE))
return(0);
for (--i; i && ' ' == buf[i]; i--)
/* Spin back to non-space. */ ;
/* Jump ahead of escaped whitespace. */
i += '\\' == buf[i] ? 2 : 1;
buf[i] = '\0';
}
if ( ! man_word_alloc(m, line, offs, buf + offs))
return(0);
/*
* End-of-sentence check. If the last character is an unescaped
* EOS character, then flag the node as being the end of a
* sentence. The front-end will know how to interpret this.
*/
assert(i);
if (mandoc_eos(buf, (size_t)i))
m->last->flags |= MAN_EOS;
descope:
/*
* Co-ordinate what happens with having a next-line scope open:
* first close out the element scope (if applicable), then close
* out the block scope (also if applicable).
*/
if (MAN_ELINE & m->flags) {
m->flags &= ~MAN_ELINE;
if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
return(0);
}
if ( ! (MAN_BLINE & m->flags))
return(1);
m->flags &= ~MAN_BLINE;
if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
return(0);
return(man_body_alloc(m, line, offs, m->last->tok));
}
static int
macrowarn(struct man *m, int ln, const char *buf, int offs)
{
int rc;
rc = man_vmsg(m, MANDOCERR_MACRO, ln, offs,
"unknown macro: %s%s",
buf, strlen(buf) > 3 ? "..." : "");
return(MAN_IGN_MACRO & m->pflags ? rc : 0);
}
static int
man_pmacro(struct man *m, int ln, char *buf, int offs)
{
int i, j, ppos;
enum mant tok;
char mac[5];
struct man_node *n;
/* Comments and empties are quickly ignored. */
offs++;
if ('\0' == buf[offs])
return(1);
i = offs;
/*
* Skip whitespace between the control character and initial
* text. "Whitespace" is both spaces and tabs.
*/
if (' ' == buf[i] || '\t' == buf[i]) {
i++;
while (buf[i] && (' ' == buf[i] || '\t' == buf[i]))
i++;
if ('\0' == buf[i])
goto out;
}
ppos = i;
/* Copy the first word into a nil-terminated buffer. */
for (j = 0; j < 4; j++, i++) {
if ('\0' == (mac[j] = buf[i]))
break;
else if (' ' == buf[i])
break;
/* Check for invalid characters. */
if (isgraph((u_char)buf[i]))
continue;
if ( ! man_pmsg(m, ln, i, MANDOCERR_BADCHAR))
return(0);
i--;
}
mac[j] = '\0';
if (j == 4 || j < 1) {
if ( ! macrowarn(m, ln, mac, ppos))
goto err;
return(1);
}
if (MAN_MAX == (tok = man_hash_find(mac))) {
if ( ! macrowarn(m, ln, mac, ppos))
goto err;
return(1);
}
/* The macro is sane. Jump to the next word. */
while (buf[i] && ' ' == buf[i])
i++;
/*
* Trailing whitespace. Note that tabs are allowed to be passed
* into the parser as "text", so we only warn about spaces here.
*/
if ('\0' == buf[i] && ' ' == buf[i - 1])
if ( ! man_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE))
goto err;
/*
* Remove prior ELINE macro, as it's being clobbering by a new
* macro. Note that NSCOPED macros do not close out ELINE
* macros---they don't print text---so we let those slip by.
*/
if ( ! (MAN_NSCOPED & man_macros[tok].flags) &&
m->flags & MAN_ELINE) {
assert(MAN_TEXT != m->last->type);
/*
* This occurs in the following construction:
* .B
* .br
* .B
* .br
* I hate man macros.
* Flat-out disallow this madness.
*/
if (MAN_NSCOPED & man_macros[m->last->tok].flags) {
man_pmsg(m, ln, ppos, MANDOCERR_SYNTLINESCOPE);
return(0);
}
n = m->last;
assert(n);
assert(NULL == n->child);
assert(0 == n->nchild);
if ( ! man_nmsg(m, n, MANDOCERR_LINESCOPE))
return(0);
man_node_delete(m, n);
m->flags &= ~MAN_ELINE;
}
/*
* Save the fact that we're in the next-line for a block. In
* this way, embedded roff instructions can "remember" state
* when they exit.
*/
if (MAN_BLINE & m->flags)
m->flags |= MAN_BPLINE;
/* Call to handler... */
assert(man_macros[tok].fp);
if ( ! (*man_macros[tok].fp)(m, tok, ln, ppos, &i, buf))
goto err;
out:
/*
* We weren't in a block-line scope when entering the
* above-parsed macro, so return.
*/
if ( ! (MAN_BPLINE & m->flags)) {
m->flags &= ~MAN_ILINE;
return(1);
}
m->flags &= ~MAN_BPLINE;
/*
* If we're in a block scope, then allow this macro to slip by
* without closing scope around it.
*/
if (MAN_ILINE & m->flags) {
m->flags &= ~MAN_ILINE;
return(1);
}
/*
* If we've opened a new next-line element scope, then return
* now, as the next line will close out the block scope.
*/
if (MAN_ELINE & m->flags)
return(1);
/* Close out the block scope opened in the prior line. */
assert(MAN_BLINE & m->flags);
m->flags &= ~MAN_BLINE;
if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
return(0);
return(man_body_alloc(m, ln, offs, m->last->tok));
err: /* Error out. */
m->flags |= MAN_HALT;
return(0);
}
int
man_vmsg(struct man *man, enum mandocerr t,
int ln, int pos, const char *fmt, ...)
{
char buf[256];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
va_end(ap);
return((*man->msg)(t, man->data, ln, pos, buf));
}
/*
* Unlink a node from its context. If "m" is provided, the last parse
* point will also be adjusted accordingly.
*/
static void
man_node_unlink(struct man *m, struct man_node *n)
{
/* Adjust siblings. */
if (n->prev)
n->prev->next = n->next;
if (n->next)
n->next->prev = n->prev;
/* Adjust parent. */
if (n->parent) {
n->parent->nchild--;
if (n->parent->child == n)
n->parent->child = n->prev ? n->prev : n->next;
}
/* Adjust parse point, if applicable. */
if (m && m->last == n) {
/*XXX: this can occur when bailing from validation. */
/*assert(NULL == n->next);*/
if (n->prev) {
m->last = n->prev;
m->next = MAN_NEXT_SIBLING;
} else {
m->last = n->parent;
m->next = MAN_NEXT_CHILD;
}
}
if (m && m->first == n)
m->first = NULL;
}

119
commands/mdocml/man.h Normal file
View file

@ -0,0 +1,119 @@
/* $Id: man.h,v 1.37 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.
*/
#ifndef MAN_H
#define MAN_H
#include <time.h>
enum mant {
MAN_br = 0,
MAN_TH,
MAN_SH,
MAN_SS,
MAN_TP,
MAN_LP,
MAN_PP,
MAN_P,
MAN_IP,
MAN_HP,
MAN_SM,
MAN_SB,
MAN_BI,
MAN_IB,
MAN_BR,
MAN_RB,
MAN_R,
MAN_B,
MAN_I,
MAN_IR,
MAN_RI,
MAN_na,
MAN_i,
MAN_sp,
MAN_nf,
MAN_fi,
MAN_r,
MAN_RE,
MAN_RS,
MAN_DT,
MAN_UC,
MAN_PD,
MAN_Sp,
MAN_Vb,
MAN_Ve,
MAN_AT,
MAN_MAX
};
enum man_type {
MAN_TEXT,
MAN_ELEM,
MAN_ROOT,
MAN_BLOCK,
MAN_HEAD,
MAN_BODY
};
struct man_meta {
char *msec;
time_t date;
char *rawdate;
char *vol;
char *title;
char *source;
};
struct man_node {
struct man_node *parent;
struct man_node *child;
struct man_node *next;
struct man_node *prev;
int nchild;
int line;
int pos;
enum mant tok;
int flags;
#define MAN_VALID (1 << 0)
#define MAN_ACTED (1 << 1)
#define MAN_EOS (1 << 2)
enum man_type type;
char *string;
struct man_node *head;
struct man_node *body;
};
#define MAN_IGN_MACRO (1 << 0)
#define MAN_IGN_ESCAPE (1 << 2)
extern const char *const *man_macronames;
__BEGIN_DECLS
struct man;
void man_free(struct man *);
struct man *man_alloc(void *, int, mandocmsg);
void man_reset(struct man *);
int man_parseln(struct man *, int, char *, int);
int man_endparse(struct man *);
const struct man_node *man_node(const struct man *);
const struct man_meta *man_meta(const struct man *);
__END_DECLS
#endif /*!MAN_H*/

View file

@ -0,0 +1,280 @@
/* $Id: man_action.c,v 1.39 2010/05/26 14:03:54 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <assert.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "libman.h"
#include "libmandoc.h"
struct actions {
int (*post)(struct man *);
};
static int post_TH(struct man *);
static int post_fi(struct man *);
static int post_nf(struct man *);
static int post_AT(struct man *);
static int post_UC(struct man *);
const struct actions man_actions[MAN_MAX] = {
{ NULL }, /* br */
{ post_TH }, /* TH */
{ NULL }, /* SH */
{ NULL }, /* SS */
{ NULL }, /* TP */
{ NULL }, /* LP */
{ NULL }, /* PP */
{ NULL }, /* P */
{ NULL }, /* IP */
{ NULL }, /* HP */
{ NULL }, /* SM */
{ NULL }, /* SB */
{ NULL }, /* BI */
{ NULL }, /* IB */
{ NULL }, /* BR */
{ NULL }, /* RB */
{ NULL }, /* R */
{ NULL }, /* B */
{ NULL }, /* I */
{ NULL }, /* IR */
{ NULL }, /* RI */
{ NULL }, /* na */
{ NULL }, /* i */
{ NULL }, /* sp */
{ post_nf }, /* nf */
{ post_fi }, /* fi */
{ NULL }, /* r */
{ NULL }, /* RE */
{ NULL }, /* RS */
{ NULL }, /* DT */
{ post_UC }, /* UC */
{ NULL }, /* PD */
{ NULL }, /* Sp */
{ post_nf }, /* Vb */
{ post_fi }, /* Ve */
{ post_AT }, /* AT */
};
int
man_action_post(struct man *m)
{
if (MAN_ACTED & m->last->flags)
return(1);
m->last->flags |= MAN_ACTED;
switch (m->last->type) {
case (MAN_TEXT):
/* FALLTHROUGH */
case (MAN_ROOT):
return(1);
default:
break;
}
if (NULL == man_actions[m->last->tok].post)
return(1);
return((*man_actions[m->last->tok].post)(m));
}
static int
post_fi(struct man *m)
{
if ( ! (MAN_LITERAL & m->flags))
if ( ! man_nmsg(m, m->last, MANDOCERR_NOSCOPE))
return(0);
m->flags &= ~MAN_LITERAL;
return(1);
}
static int
post_nf(struct man *m)
{
if (MAN_LITERAL & m->flags)
if ( ! man_nmsg(m, m->last, MANDOCERR_SCOPEREP))
return(0);
m->flags |= MAN_LITERAL;
return(1);
}
static int
post_TH(struct man *m)
{
struct man_node *n;
if (m->meta.title)
free(m->meta.title);
if (m->meta.vol)
free(m->meta.vol);
if (m->meta.source)
free(m->meta.source);
if (m->meta.msec)
free(m->meta.msec);
if (m->meta.rawdate)
free(m->meta.rawdate);
m->meta.title = m->meta.vol = m->meta.rawdate =
m->meta.msec = m->meta.source = NULL;
m->meta.date = 0;
/* ->TITLE<- MSEC DATE SOURCE VOL */
n = m->last->child;
assert(n);
m->meta.title = mandoc_strdup(n->string);
/* TITLE ->MSEC<- DATE SOURCE VOL */
n = n->next;
assert(n);
m->meta.msec = mandoc_strdup(n->string);
/* TITLE MSEC ->DATE<- SOURCE VOL */
/*
* Try to parse the date. If this works, stash the epoch (this
* is optimal because we can reformat it in the canonical form).
* If it doesn't parse, isn't specified at all, or is an empty
* string, then use the current date.
*/
n = n->next;
if (n && n->string && *n->string) {
m->meta.date = mandoc_a2time
(MTIME_ISO_8601, n->string);
if (0 == m->meta.date) {
if ( ! man_nmsg(m, n, MANDOCERR_BADDATE))
return(0);
m->meta.rawdate = mandoc_strdup(n->string);
}
} else
m->meta.date = time(NULL);
/* TITLE MSEC DATE ->SOURCE<- VOL */
if (n && (n = n->next))
m->meta.source = mandoc_strdup(n->string);
/* TITLE MSEC DATE SOURCE ->VOL<- */
if (n && (n = n->next))
m->meta.vol = mandoc_strdup(n->string);
/*
* Remove the `TH' node after we've processed it for our
* meta-data.
*/
man_node_delete(m, m->last);
return(1);
}
static int
post_AT(struct man *m)
{
static const char * const unix_versions[] = {
"7th Edition",
"System III",
"System V",
"System V Release 2",
};
const char *p, *s;
struct man_node *n, *nn;
n = m->last->child;
if (NULL == n || MAN_TEXT != n->type)
p = unix_versions[0];
else {
s = n->string;
if (0 == strcmp(s, "3"))
p = unix_versions[0];
else if (0 == strcmp(s, "4"))
p = unix_versions[1];
else if (0 == strcmp(s, "5")) {
nn = n->next;
if (nn && MAN_TEXT == nn->type && nn->string[0])
p = unix_versions[3];
else
p = unix_versions[2];
} else
p = unix_versions[0];
}
if (m->meta.source)
free(m->meta.source);
m->meta.source = mandoc_strdup(p);
return(1);
}
static int
post_UC(struct man *m)
{
static const char * const bsd_versions[] = {
"3rd Berkeley Distribution",
"4th Berkeley Distribution",
"4.2 Berkeley Distribution",
"4.3 Berkeley Distribution",
"4.4 Berkeley Distribution",
};
const char *p, *s;
struct man_node *n;
n = m->last->child;
if (NULL == n || MAN_TEXT != n->type)
p = bsd_versions[0];
else {
s = n->string;
if (0 == strcmp(s, "3"))
p = bsd_versions[0];
else if (0 == strcmp(s, "4"))
p = bsd_versions[1];
else if (0 == strcmp(s, "5"))
p = bsd_versions[2];
else if (0 == strcmp(s, "6"))
p = bsd_versions[3];
else if (0 == strcmp(s, "7"))
p = bsd_versions[4];
else
p = bsd_versions[0];
}
if (m->meta.source)
free(m->meta.source);
m->meta.source = mandoc_strdup(p);
return(1);
}

104
commands/mdocml/man_argv.c Normal file
View file

@ -0,0 +1,104 @@
/* $Id: man_argv.c,v 1.4 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "libman.h"
int
man_args(struct man *m, int line, int *pos, char *buf, char **v)
{
assert(*pos);
assert(' ' != buf[*pos]);
if (0 == buf[*pos])
return(ARGS_EOLN);
*v = &buf[*pos];
/*
* Process a quoted literal. A quote begins with a double-quote
* and ends with a double-quote NOT preceded by a double-quote.
* Whitespace is NOT involved in literal termination.
*/
if ('\"' == buf[*pos]) {
*v = &buf[++(*pos)];
for ( ; buf[*pos]; (*pos)++) {
if ('\"' != buf[*pos])
continue;
if ('\"' != buf[*pos + 1])
break;
(*pos)++;
}
if (0 == buf[*pos]) {
if ( ! man_pmsg(m, line, *pos, MANDOCERR_BADQUOTE))
return(ARGS_ERROR);
return(ARGS_QWORD);
}
buf[(*pos)++] = 0;
if (0 == buf[*pos])
return(ARGS_QWORD);
while (' ' == buf[*pos])
(*pos)++;
if (0 == buf[*pos])
if ( ! man_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
return(ARGS_ERROR);
return(ARGS_QWORD);
}
/*
* A non-quoted term progresses until either the end of line or
* a non-escaped whitespace.
*/
for ( ; buf[*pos]; (*pos)++)
if (' ' == buf[*pos] && '\\' != buf[*pos - 1])
break;
if (0 == buf[*pos])
return(ARGS_WORD);
buf[(*pos)++] = 0;
while (' ' == buf[*pos])
(*pos)++;
if (0 == buf[*pos])
if ( ! man_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
return(ARGS_ERROR);
return(ARGS_WORD);
}

106
commands/mdocml/man_hash.c Normal file
View file

@ -0,0 +1,106 @@
/* $Id: man_hash.c,v 1.22 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <limits.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "libman.h"
#define HASH_DEPTH 6
#define HASH_ROW(x) do { \
if (isupper((u_char)(x))) \
(x) -= 65; \
else \
(x) -= 97; \
(x) *= HASH_DEPTH; \
} while (/* CONSTCOND */ 0)
/*
* Lookup table is indexed first by lower-case first letter (plus one
* for the period, which is stored in the last row), then by lower or
* uppercase second letter. Buckets correspond to the index of the
* macro (the integer value of the enum stored as a char to save a bit
* of space).
*/
static u_char table[26 * HASH_DEPTH];
/*
* XXX - this hash has global scope, so if intended for use as a library
* with multiple callers, it will need re-invocation protection.
*/
void
man_hash_init(void)
{
int i, j, x;
memset(table, UCHAR_MAX, sizeof(table));
assert(/* LINTED */
MAN_MAX < UCHAR_MAX);
for (i = 0; i < (int)MAN_MAX; i++) {
x = man_macronames[i][0];
assert(isalpha((u_char)x));
HASH_ROW(x);
for (j = 0; j < HASH_DEPTH; j++)
if (UCHAR_MAX == table[x + j]) {
table[x + j] = (u_char)i;
break;
}
assert(j < HASH_DEPTH);
}
}
enum mant
man_hash_find(const char *tmp)
{
int x, y, i;
enum mant tok;
if ('\0' == (x = tmp[0]))
return(MAN_MAX);
if ( ! (isalpha((u_char)x)))
return(MAN_MAX);
HASH_ROW(x);
for (i = 0; i < HASH_DEPTH; i++) {
if (UCHAR_MAX == (y = table[x + i]))
return(MAN_MAX);
tok = (enum mant)y;
if (0 == strcmp(tmp, man_macronames[tok]))
return(tok);
}
return(MAN_MAX);
}

751
commands/mdocml/man_html.c Normal file
View file

@ -0,0 +1,751 @@
/* $Id: man_html.c,v 1.37 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 "mandoc.h"
#include "out.h"
#include "html.h"
#include "man.h"
#include "main.h"
/* TODO: preserve ident widths. */
/* FIXME: have PD set the default vspace width. */
#define INDENT 5
#define HALFINDENT 3
#define MAN_ARGS const struct man_meta *m, \
const struct man_node *n, \
struct html *h
struct htmlman {
int (*pre)(MAN_ARGS);
int (*post)(MAN_ARGS);
};
static void print_man(MAN_ARGS);
static void print_man_head(MAN_ARGS);
static void print_man_nodelist(MAN_ARGS);
static void print_man_node(MAN_ARGS);
static int a2width(const struct man_node *,
struct roffsu *);
static int man_alt_pre(MAN_ARGS);
static int man_br_pre(MAN_ARGS);
static int man_ign_pre(MAN_ARGS);
static void man_root_post(MAN_ARGS);
static int man_root_pre(MAN_ARGS);
static int man_B_pre(MAN_ARGS);
static int man_HP_pre(MAN_ARGS);
static int man_I_pre(MAN_ARGS);
static int man_IP_pre(MAN_ARGS);
static int man_PP_pre(MAN_ARGS);
static int man_RS_pre(MAN_ARGS);
static int man_SB_pre(MAN_ARGS);
static int man_SH_pre(MAN_ARGS);
static int man_SM_pre(MAN_ARGS);
static int man_SS_pre(MAN_ARGS);
static const struct htmlman mans[MAN_MAX] = {
{ man_br_pre, NULL }, /* br */
{ NULL, NULL }, /* TH */
{ man_SH_pre, NULL }, /* SH */
{ man_SS_pre, NULL }, /* SS */
{ man_IP_pre, NULL }, /* TP */
{ man_PP_pre, NULL }, /* LP */
{ man_PP_pre, NULL }, /* PP */
{ man_PP_pre, NULL }, /* P */
{ man_IP_pre, NULL }, /* IP */
{ man_HP_pre, NULL }, /* HP */
{ man_SM_pre, NULL }, /* SM */
{ man_SB_pre, NULL }, /* SB */
{ man_alt_pre, NULL }, /* BI */
{ man_alt_pre, NULL }, /* IB */
{ man_alt_pre, NULL }, /* BR */
{ man_alt_pre, NULL }, /* RB */
{ NULL, NULL }, /* R */
{ man_B_pre, NULL }, /* B */
{ man_I_pre, NULL }, /* I */
{ man_alt_pre, NULL }, /* IR */
{ man_alt_pre, NULL }, /* RI */
{ NULL, NULL }, /* na */
{ NULL, NULL }, /* i */
{ man_br_pre, NULL }, /* sp */
{ NULL, NULL }, /* nf */
{ NULL, NULL }, /* fi */
{ NULL, NULL }, /* r */
{ NULL, NULL }, /* RE */
{ man_RS_pre, NULL }, /* RS */
{ man_ign_pre, NULL }, /* DT */
{ man_ign_pre, NULL }, /* UC */
{ man_ign_pre, NULL }, /* PD */
{ man_br_pre, NULL }, /* Sp */
{ man_ign_pre, NULL }, /* Vb */
{ NULL, NULL }, /* Ve */
{ man_ign_pre, NULL }, /* AT */
};
void
html_man(void *arg, const struct man *m)
{
struct html *h;
struct tag *t;
h = (struct html *)arg;
print_gen_decls(h);
t = print_otag(h, TAG_HTML, 0, NULL);
print_man(man_meta(m), man_node(m), h);
print_tagq(h, t);
printf("\n");
}
static void
print_man(MAN_ARGS)
{
struct tag *t;
struct htmlpair tag;
t = print_otag(h, TAG_HEAD, 0, NULL);
print_man_head(m, n, h);
print_tagq(h, t);
t = print_otag(h, TAG_BODY, 0, NULL);
tag.key = ATTR_CLASS;
tag.val = "body";
print_otag(h, TAG_DIV, 1, &tag);
print_man_nodelist(m, n, h);
print_tagq(h, t);
}
/* ARGSUSED */
static void
print_man_head(MAN_ARGS)
{
print_gen_head(h);
bufinit(h);
buffmt(h, "%s(%s)", m->title, m->msec);
print_otag(h, TAG_TITLE, 0, NULL);
print_text(h, h->buf);
}
static void
print_man_nodelist(MAN_ARGS)
{
print_man_node(m, n, h);
if (n->next)
print_man_nodelist(m, n->next, h);
}
static void
print_man_node(MAN_ARGS)
{
int child;
struct tag *t;
child = 1;
t = h->tags.head;
bufinit(h);
/*
* FIXME: embedded elements within next-line scopes (e.g., `br'
* within an empty `B') will cause formatting to be forgotten
* due to scope closing out.
*/
switch (n->type) {
case (MAN_ROOT):
child = man_root_pre(m, n, h);
break;
case (MAN_TEXT):
print_text(h, n->string);
return;
default:
/*
* Close out scope of font prior to opening a macro
* scope. Assert that the metafont is on the top of the
* stack (it's never nested).
*/
if (h->metaf) {
assert(h->metaf == t);
print_tagq(h, h->metaf);
assert(NULL == h->metaf);
t = h->tags.head;
}
if (mans[n->tok].pre)
child = (*mans[n->tok].pre)(m, n, h);
break;
}
if (child && n->child)
print_man_nodelist(m, n->child, h);
/* This will automatically close out any font scope. */
print_stagq(h, t);
bufinit(h);
switch (n->type) {
case (MAN_ROOT):
man_root_post(m, n, h);
break;
case (MAN_TEXT):
break;
default:
if (mans[n->tok].post)
(*mans[n->tok].post)(m, n, h);
break;
}
}
static int
a2width(const struct man_node *n, struct roffsu *su)
{
if (MAN_TEXT != n->type)
return(0);
if (a2roffsu(n->string, su, SCALE_BU))
return(1);
return(0);
}
/* ARGSUSED */
static int
man_root_pre(MAN_ARGS)
{
struct htmlpair tag[3];
struct tag *t, *tt;
char b[BUFSIZ], title[BUFSIZ];
b[0] = 0;
if (m->vol)
(void)strlcat(b, m->vol, BUFSIZ);
snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
PAIR_CLASS_INIT(&tag[0], "header");
bufcat_style(h, "width", "100%");
PAIR_STYLE_INIT(&tag[1], h);
PAIR_SUMMARY_INIT(&tag[2], "header");
t = print_otag(h, TAG_TABLE, 3, tag);
tt = print_otag(h, TAG_TR, 0, NULL);
bufinit(h);
bufcat_style(h, "width", "10%");
PAIR_STYLE_INIT(&tag[0], h);
print_otag(h, TAG_TD, 1, tag);
print_text(h, title);
print_stagq(h, tt);
bufinit(h);
bufcat_style(h, "width", "80%");
bufcat_style(h, "white-space", "nowrap");
bufcat_style(h, "text-align", "center");
PAIR_STYLE_INIT(&tag[0], h);
print_otag(h, TAG_TD, 1, tag);
print_text(h, b);
print_stagq(h, tt);
bufinit(h);
bufcat_style(h, "width", "10%");
bufcat_style(h, "text-align", "right");
PAIR_STYLE_INIT(&tag[0], h);
print_otag(h, TAG_TD, 1, tag);
print_text(h, title);
print_tagq(h, t);
return(1);
}
/* ARGSUSED */
static void
man_root_post(MAN_ARGS)
{
struct htmlpair tag[3];
struct tag *t, *tt;
char b[DATESIZ];
if (m->rawdate)
strlcpy(b, m->rawdate, DATESIZ);
else
time2a(m->date, b, DATESIZ);
PAIR_CLASS_INIT(&tag[0], "footer");
bufcat_style(h, "width", "100%");
PAIR_STYLE_INIT(&tag[1], h);
PAIR_SUMMARY_INIT(&tag[2], "footer");
t = print_otag(h, TAG_TABLE, 3, tag);
tt = print_otag(h, TAG_TR, 0, NULL);
bufinit(h);
bufcat_style(h, "width", "50%");
PAIR_STYLE_INIT(&tag[0], h);
print_otag(h, TAG_TD, 1, tag);
print_text(h, b);
print_stagq(h, tt);
bufinit(h);
bufcat_style(h, "width", "50%");
bufcat_style(h, "text-align", "right");
PAIR_STYLE_INIT(&tag[0], h);
print_otag(h, TAG_TD, 1, tag);
if (m->source)
print_text(h, m->source);
print_tagq(h, t);
}
/* ARGSUSED */
static int
man_br_pre(MAN_ARGS)
{
struct roffsu su;
struct htmlpair tag;
SCALE_VS_INIT(&su, 1);
switch (n->tok) {
case (MAN_Sp):
SCALE_VS_INIT(&su, 0.5);
break;
case (MAN_sp):
if (n->child)
a2roffsu(n->child->string, &su, SCALE_VS);
break;
default:
su.scale = 0;
break;
}
bufcat_su(h, "height", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
/* So the div isn't empty: */
print_text(h, "\\~");
return(0);
}
/* ARGSUSED */
static int
man_SH_pre(MAN_ARGS)
{
struct htmlpair tag[2];
struct roffsu su;
if (MAN_BODY == n->type) {
SCALE_HS_INIT(&su, INDENT);
bufcat_su(h, "margin-left", &su);
PAIR_CLASS_INIT(&tag[0], "sec-body");
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_DIV, 2, tag);
return(1);
} else if (MAN_BLOCK == n->type) {
PAIR_CLASS_INIT(&tag[0], "sec-block");
if (n->prev && MAN_SH == n->prev->tok)
if (NULL == n->prev->body->child) {
print_otag(h, TAG_DIV, 1, tag);
return(1);
}
SCALE_VS_INIT(&su, 1);
bufcat_su(h, "margin-top", &su);
if (NULL == n->next)
bufcat_su(h, "margin-bottom", &su);
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_DIV, 2, tag);
return(1);
}
PAIR_CLASS_INIT(&tag[0], "sec-head");
print_otag(h, TAG_DIV, 1, tag);
return(1);
}
/* ARGSUSED */
static int
man_alt_pre(MAN_ARGS)
{
const struct man_node *nn;
struct tag *t;
int i;
enum htmlfont fp;
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
switch (n->tok) {
case (MAN_BI):
fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD;
break;
case (MAN_IB):
fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC;
break;
case (MAN_RI):
fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE;
break;
case (MAN_IR):
fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC;
break;
case (MAN_BR):
fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD;
break;
case (MAN_RB):
fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE;
break;
default:
abort();
/* NOTREACHED */
}
if (i)
h->flags |= HTML_NOSPACE;
/*
* Open and close the scope with each argument, so that
* internal \f escapes, which are common, are also
* closed out with the scope.
*/
t = print_ofont(h, fp);
print_man_node(m, nn, h);
print_tagq(h, t);
}
return(0);
}
/* ARGSUSED */
static int
man_SB_pre(MAN_ARGS)
{
struct htmlpair tag;
/* FIXME: print_ofont(). */
PAIR_CLASS_INIT(&tag, "small bold");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
/* ARGSUSED */
static int
man_SM_pre(MAN_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "small");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
/* ARGSUSED */
static int
man_SS_pre(MAN_ARGS)
{
struct htmlpair tag[3];
struct roffsu su;
SCALE_VS_INIT(&su, 1);
if (MAN_BODY == n->type) {
PAIR_CLASS_INIT(&tag[0], "ssec-body");
if (n->parent->next && n->child) {
bufcat_su(h, "margin-bottom", &su);
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_DIV, 2, tag);
return(1);
}
print_otag(h, TAG_DIV, 1, tag);
return(1);
} else if (MAN_BLOCK == n->type) {
PAIR_CLASS_INIT(&tag[0], "ssec-block");
if (n->prev && MAN_SS == n->prev->tok)
if (n->prev->body->child) {
bufcat_su(h, "margin-top", &su);
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_DIV, 2, tag);
return(1);
}
print_otag(h, TAG_DIV, 1, tag);
return(1);
}
SCALE_HS_INIT(&su, INDENT - HALFINDENT);
bufcat_su(h, "margin-left", &su);
PAIR_CLASS_INIT(&tag[0], "ssec-head");
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_DIV, 2, tag);
return(1);
}
/* ARGSUSED */
static int
man_PP_pre(MAN_ARGS)
{
struct htmlpair tag;
struct roffsu su;
int i;
if (MAN_BLOCK != n->type)
return(1);
i = 0;
if (MAN_ROOT == n->parent->type) {
SCALE_HS_INIT(&su, INDENT);
bufcat_su(h, "margin-left", &su);
i = 1;
}
if (n->prev) {
SCALE_VS_INIT(&su, 1);
bufcat_su(h, "margin-top", &su);
i = 1;
}
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, i, &tag);
return(1);
}
/* ARGSUSED */
static int
man_IP_pre(MAN_ARGS)
{
struct roffsu su;
struct htmlpair tag;
const struct man_node *nn;
int width;
/*
* This scattering of 1-BU margins and pads is to make sure that
* when text overruns its box, the subsequent text isn't flush
* up against it. However, the rest of the right-hand box must
* also be adjusted in consideration of this 1-BU space.
*/
if (MAN_BODY == n->type) {
SCALE_HS_INIT(&su, INDENT);
bufcat_su(h, "margin-left", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
return(1);
}
nn = MAN_BLOCK == n->type ?
n->head->child : n->parent->head->child;
SCALE_HS_INIT(&su, INDENT);
width = 0;
/* Width is the last token. */
if (MAN_IP == n->tok && NULL != nn)
if (NULL != (nn = nn->next)) {
for ( ; nn->next; nn = nn->next)
/* Do nothing. */ ;
width = a2width(nn, &su);
}
/* Width is the first token. */
if (MAN_TP == n->tok && NULL != nn) {
/* Skip past non-text children. */
while (nn && MAN_TEXT != nn->type)
nn = nn->next;
if (nn)
width = a2width(nn, &su);
}
if (MAN_BLOCK == n->type) {
bufcat_su(h, "margin-left", &su);
SCALE_VS_INIT(&su, 1);
bufcat_su(h, "margin-top", &su);
bufcat_style(h, "clear", "both");
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
return(1);
}
bufcat_su(h, "min-width", &su);
SCALE_INVERT(&su);
bufcat_su(h, "margin-left", &su);
SCALE_HS_INIT(&su, 1);
bufcat_su(h, "margin-right", &su);
bufcat_style(h, "clear", "left");
if (n->next && n->next->child)
bufcat_style(h, "float", "left");
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
/*
* Without a length string, we can print all of our children.
*/
if ( ! width)
return(1);
/*
* When a length has been specified, we need to carefully print
* our child context: IP gets all children printed but the last
* (the width), while TP gets all children printed but the first
* (the width).
*/
if (MAN_IP == n->tok)
for (nn = n->child; nn->next; nn = nn->next)
print_man_node(m, nn, h);
if (MAN_TP == n->tok)
for (nn = n->child->next; nn; nn = nn->next)
print_man_node(m, nn, h);
return(0);
}
/* ARGSUSED */
static int
man_HP_pre(MAN_ARGS)
{
const struct man_node *nn;
struct htmlpair tag;
struct roffsu su;
if (MAN_HEAD == n->type)
return(0);
nn = MAN_BLOCK == n->type ?
n->head->child : n->parent->head->child;
SCALE_HS_INIT(&su, INDENT);
if (NULL != nn)
(void)a2width(nn, &su);
if (MAN_BLOCK == n->type) {
bufcat_su(h, "margin-left", &su);
SCALE_VS_INIT(&su, 1);
bufcat_su(h, "margin-top", &su);
bufcat_style(h, "clear", "both");
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
return(1);
}
bufcat_su(h, "margin-left", &su);
SCALE_INVERT(&su);
bufcat_su(h, "text-indent", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
return(1);
}
/* ARGSUSED */
static int
man_B_pre(MAN_ARGS)
{
print_ofont(h, HTMLFONT_BOLD);
return(1);
}
/* ARGSUSED */
static int
man_I_pre(MAN_ARGS)
{
print_ofont(h, HTMLFONT_ITALIC);
return(1);
}
/* ARGSUSED */
static int
man_ign_pre(MAN_ARGS)
{
return(0);
}
/* ARGSUSED */
static int
man_RS_pre(MAN_ARGS)
{
struct htmlpair tag;
struct roffsu su;
if (MAN_HEAD == n->type)
return(0);
else if (MAN_BODY == n->type)
return(1);
SCALE_HS_INIT(&su, INDENT);
bufcat_su(h, "margin-left", &su);
if (n->head->child) {
SCALE_VS_INIT(&su, 1);
a2width(n->head->child, &su);
bufcat_su(h, "margin-top", &su);
}
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
return(1);
}

485
commands/mdocml/man_macro.c Normal file
View file

@ -0,0 +1,485 @@
/* $Id: man_macro.c,v 1.47 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "libman.h"
enum rew {
REW_REWIND,
REW_NOHALT,
REW_HALT
};
static int blk_close(MACRO_PROT_ARGS);
static int blk_exp(MACRO_PROT_ARGS);
static int blk_imp(MACRO_PROT_ARGS);
static int in_line_eoln(MACRO_PROT_ARGS);
static int rew_scope(enum man_type,
struct man *, enum mant);
static enum rew rew_dohalt(enum mant, enum man_type,
const struct man_node *);
static enum rew rew_block(enum mant, enum man_type,
const struct man_node *);
static int rew_warn(struct man *,
struct man_node *, enum mandocerr);
const struct man_macro __man_macros[MAN_MAX] = {
{ in_line_eoln, MAN_NSCOPED }, /* br */
{ in_line_eoln, 0 }, /* TH */
{ blk_imp, MAN_SCOPED }, /* SH */
{ blk_imp, MAN_SCOPED }, /* SS */
{ blk_imp, MAN_SCOPED | MAN_FSCOPED }, /* TP */
{ blk_imp, 0 }, /* LP */
{ blk_imp, 0 }, /* PP */
{ blk_imp, 0 }, /* P */
{ blk_imp, 0 }, /* IP */
{ blk_imp, 0 }, /* HP */
{ in_line_eoln, MAN_SCOPED }, /* SM */
{ in_line_eoln, MAN_SCOPED }, /* SB */
{ in_line_eoln, 0 }, /* BI */
{ in_line_eoln, 0 }, /* IB */
{ in_line_eoln, 0 }, /* BR */
{ in_line_eoln, 0 }, /* RB */
{ in_line_eoln, MAN_SCOPED }, /* R */
{ in_line_eoln, MAN_SCOPED }, /* B */
{ in_line_eoln, MAN_SCOPED }, /* I */
{ in_line_eoln, 0 }, /* IR */
{ in_line_eoln, 0 }, /* RI */
{ in_line_eoln, MAN_NSCOPED }, /* na */
{ in_line_eoln, 0 }, /* i */
{ in_line_eoln, MAN_NSCOPED }, /* sp */
{ in_line_eoln, 0 }, /* nf */
{ in_line_eoln, 0 }, /* fi */
{ in_line_eoln, 0 }, /* r */
{ blk_close, 0 }, /* RE */
{ blk_exp, MAN_EXPLICIT }, /* RS */
{ in_line_eoln, 0 }, /* DT */
{ in_line_eoln, 0 }, /* UC */
{ in_line_eoln, 0 }, /* PD */
{ in_line_eoln, MAN_NSCOPED }, /* Sp */
{ in_line_eoln, 0 }, /* Vb */
{ in_line_eoln, 0 }, /* Ve */
{ in_line_eoln, 0 }, /* AT */
};
const struct man_macro * const man_macros = __man_macros;
/*
* Warn when "n" is an explicit non-roff macro.
*/
static int
rew_warn(struct man *m, struct man_node *n, enum mandocerr er)
{
if (er == MANDOCERR_MAX || MAN_BLOCK != n->type)
return(1);
if (MAN_VALID & n->flags)
return(1);
if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags))
return(1);
return(man_nmsg(m, n, er));
}
/*
* Rewind scope. If a code "er" != MANDOCERR_MAX has been provided, it
* will be used if an explicit block scope is being closed out.
*/
int
man_unscope(struct man *m, const struct man_node *n,
enum mandocerr er)
{
assert(n);
/* LINTED */
while (m->last != n) {
if ( ! rew_warn(m, m->last, er))
return(0);
if ( ! man_valid_post(m))
return(0);
if ( ! man_action_post(m))
return(0);
m->last = m->last->parent;
assert(m->last);
}
if ( ! rew_warn(m, m->last, er))
return(0);
if ( ! man_valid_post(m))
return(0);
if ( ! man_action_post(m))
return(0);
m->next = MAN_ROOT == m->last->type ?
MAN_NEXT_CHILD : MAN_NEXT_SIBLING;
return(1);
}
static enum rew
rew_block(enum mant ntok, enum man_type type, const struct man_node *n)
{
if (MAN_BLOCK == type && ntok == n->parent->tok &&
MAN_BODY == n->parent->type)
return(REW_REWIND);
return(ntok == n->tok ? REW_HALT : REW_NOHALT);
}
/*
* There are three scope levels: scoped to the root (all), scoped to the
* section (all less sections), and scoped to subsections (all less
* sections and subsections).
*/
static enum rew
rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
{
enum rew c;
/* We cannot progress beyond the root ever. */
if (MAN_ROOT == n->type)
return(REW_HALT);
assert(n->parent);
/* Normal nodes shouldn't go to the level of the root. */
if (MAN_ROOT == n->parent->type)
return(REW_REWIND);
/* Already-validated nodes should be closed out. */
if (MAN_VALID & n->flags)
return(REW_NOHALT);
/* First: rewind to ourselves. */
if (type == n->type && tok == n->tok)
return(REW_REWIND);
/*
* Next follow the implicit scope-smashings as defined by man.7:
* section, sub-section, etc.
*/
switch (tok) {
case (MAN_SH):
break;
case (MAN_SS):
/* Rewind to a section, if a block. */
if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
return(c);
break;
case (MAN_RS):
/* Rewind to a subsection, if a block. */
if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
return(c);
/* Rewind to a section, if a block. */
if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
return(c);
break;
default:
/* Rewind to an offsetter, if a block. */
if (REW_NOHALT != (c = rew_block(MAN_RS, type, n)))
return(c);
/* Rewind to a subsection, if a block. */
if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
return(c);
/* Rewind to a section, if a block. */
if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
return(c);
break;
}
return(REW_NOHALT);
}
/*
* Rewinding entails ascending the parse tree until a coherent point,
* for example, the `SH' macro will close out any intervening `SS'
* scopes. When a scope is closed, it must be validated and actioned.
*/
static int
rew_scope(enum man_type type, struct man *m, enum mant tok)
{
struct man_node *n;
enum rew c;
/* LINTED */
for (n = m->last; n; n = n->parent) {
/*
* Whether we should stop immediately (REW_HALT), stop
* and rewind until this point (REW_REWIND), or keep
* rewinding (REW_NOHALT).
*/
c = rew_dohalt(tok, type, n);
if (REW_HALT == c)
return(1);
if (REW_REWIND == c)
break;
}
/*
* Rewind until the current point. Warn if we're a roff
* instruction that's mowing over explicit scopes.
*/
assert(n);
return(man_unscope(m, n, MANDOCERR_MAX));
}
/*
* Close out a generic explicit macro.
*/
/* ARGSUSED */
static int
blk_close(MACRO_PROT_ARGS)
{
enum mant ntok;
const struct man_node *nn;
switch (tok) {
case (MAN_RE):
ntok = MAN_RS;
break;
default:
abort();
/* NOTREACHED */
}
for (nn = m->last->parent; nn; nn = nn->parent)
if (ntok == nn->tok)
break;
if (NULL == nn)
if ( ! man_pmsg(m, line, ppos, MANDOCERR_NOSCOPE))
return(0);
if ( ! rew_scope(MAN_BODY, m, ntok))
return(0);
if ( ! rew_scope(MAN_BLOCK, m, ntok))
return(0);
return(1);
}
static int
blk_exp(MACRO_PROT_ARGS)
{
int w, la;
char *p;
/*
* Close out prior scopes. "Regular" explicit macros cannot be
* nested, but we allow roff macros to be placed just about
* anywhere.
*/
if ( ! rew_scope(MAN_BODY, m, tok))
return(0);
if ( ! rew_scope(MAN_BLOCK, m, tok))
return(0);
if ( ! man_block_alloc(m, line, ppos, tok))
return(0);
if ( ! man_head_alloc(m, line, ppos, tok))
return(0);
for (;;) {
la = *pos;
w = man_args(m, line, pos, buf, &p);
if (-1 == w)
return(0);
if (0 == w)
break;
if ( ! man_word_alloc(m, line, la, p))
return(0);
}
assert(m);
assert(tok != MAN_MAX);
if ( ! rew_scope(MAN_HEAD, m, tok))
return(0);
return(man_body_alloc(m, line, ppos, tok));
}
/*
* Parse an implicit-block macro. These contain a MAN_HEAD and a
* MAN_BODY contained within a MAN_BLOCK. Rules for closing out other
* scopes, such as `SH' closing out an `SS', are defined in the rew
* routines.
*/
static int
blk_imp(MACRO_PROT_ARGS)
{
int w, la;
char *p;
struct man_node *n;
/* Close out prior scopes. */
if ( ! rew_scope(MAN_BODY, m, tok))
return(0);
if ( ! rew_scope(MAN_BLOCK, m, tok))
return(0);
/* Allocate new block & head scope. */
if ( ! man_block_alloc(m, line, ppos, tok))
return(0);
if ( ! man_head_alloc(m, line, ppos, tok))
return(0);
n = m->last;
/* Add line arguments. */
for (;;) {
la = *pos;
w = man_args(m, line, pos, buf, &p);
if (-1 == w)
return(0);
if (0 == w)
break;
if ( ! man_word_alloc(m, line, la, p))
return(0);
}
/* Close out head and open body (unless MAN_SCOPE). */
if (MAN_SCOPED & man_macros[tok].flags) {
/* If we're forcing scope (`TP'), keep it open. */
if (MAN_FSCOPED & man_macros[tok].flags) {
m->flags |= MAN_BLINE;
return(1);
} else if (n == m->last) {
m->flags |= MAN_BLINE;
return(1);
}
}
if ( ! rew_scope(MAN_HEAD, m, tok))
return(0);
return(man_body_alloc(m, line, ppos, tok));
}
static int
in_line_eoln(MACRO_PROT_ARGS)
{
int w, la;
char *p;
struct man_node *n;
if ( ! man_elem_alloc(m, line, ppos, tok))
return(0);
n = m->last;
for (;;) {
la = *pos;
w = man_args(m, line, pos, buf, &p);
if (-1 == w)
return(0);
if (0 == w)
break;
if ( ! man_word_alloc(m, line, la, p))
return(0);
}
/*
* If no arguments are specified and this is MAN_SCOPED (i.e.,
* next-line scoped), then set our mode to indicate that we're
* waiting for terms to load into our context.
*/
if (n == m->last && MAN_SCOPED & man_macros[tok].flags) {
assert( ! (MAN_NSCOPED & man_macros[tok].flags));
m->flags |= MAN_ELINE;
return(1);
}
/* Set ignorable context, if applicable. */
if (MAN_NSCOPED & man_macros[tok].flags) {
assert( ! (MAN_SCOPED & man_macros[tok].flags));
m->flags |= MAN_ILINE;
}
/*
* Rewind our element scope. Note that when TH is pruned, we'll
* be back at the root, so make sure that we don't clobber as
* its sibling.
*/
for ( ; m->last; m->last = m->last->parent) {
if (m->last == n)
break;
if (m->last->type == MAN_ROOT)
break;
if ( ! man_valid_post(m))
return(0);
if ( ! man_action_post(m))
return(0);
}
assert(m->last);
/*
* Same here regarding whether we're back at the root.
*/
if (m->last->type != MAN_ROOT && ! man_valid_post(m))
return(0);
if (m->last->type != MAN_ROOT && ! man_action_post(m))
return(0);
m->next = MAN_ROOT == m->last->type ?
MAN_NEXT_CHILD : MAN_NEXT_SIBLING;
return(1);
}
int
man_macroend(struct man *m)
{
return(man_unscope(m, m->first, MANDOCERR_SCOPEEXIT));
}

964
commands/mdocml/man_term.c Normal file
View file

@ -0,0 +1,964 @@
/* $Id: man_term.c,v 1.76 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 "mandoc.h"
#include "out.h"
#include "man.h"
#include "term.h"
#include "chars.h"
#include "main.h"
#define INDENT 7
#define HALFINDENT 3
/* FIXME: have PD set the default vspace width. */
struct mtermp {
int fl;
#define MANT_LITERAL (1 << 0)
/*
* Default amount to indent the left margin after leading text
* has been printed (e.g., `HP' left-indent, `TP' and `IP' body
* indent). This needs to be saved because `HP' and so on, if
* not having a specified value, must default.
*
* Note that this is the indentation AFTER the left offset, so
* the total offset is usually offset + lmargin.
*/
size_t lmargin;
/*
* The default offset, i.e., the amount between any text and the
* page boundary.
*/
size_t offset;
};
#define DECL_ARGS struct termp *p, \
struct mtermp *mt, \
const struct man_node *n, \
const struct man_meta *m
struct termact {
int (*pre)(DECL_ARGS);
void (*post)(DECL_ARGS);
int flags;
#define MAN_NOTEXT (1 << 0) /* Never has text children. */
};
static int a2width(const struct man_node *);
static int a2height(const struct man_node *);
static void print_man_nodelist(DECL_ARGS);
static void print_man_node(DECL_ARGS);
static void print_man_head(struct termp *, const void *);
static void print_man_foot(struct termp *, const void *);
static void print_bvspace(struct termp *,
const struct man_node *);
static int pre_B(DECL_ARGS);
static int pre_BI(DECL_ARGS);
static int pre_HP(DECL_ARGS);
static int pre_I(DECL_ARGS);
static int pre_IP(DECL_ARGS);
static int pre_PP(DECL_ARGS);
static int pre_RB(DECL_ARGS);
static int pre_RI(DECL_ARGS);
static int pre_RS(DECL_ARGS);
static int pre_SH(DECL_ARGS);
static int pre_SS(DECL_ARGS);
static int pre_TP(DECL_ARGS);
static int pre_br(DECL_ARGS);
static int pre_fi(DECL_ARGS);
static int pre_ign(DECL_ARGS);
static int pre_nf(DECL_ARGS);
static int pre_sp(DECL_ARGS);
static void post_IP(DECL_ARGS);
static void post_HP(DECL_ARGS);
static void post_RS(DECL_ARGS);
static void post_SH(DECL_ARGS);
static void post_SS(DECL_ARGS);
static void post_TP(DECL_ARGS);
static const struct termact termacts[MAN_MAX] = {
{ pre_br, NULL, MAN_NOTEXT }, /* br */
{ NULL, NULL, 0 }, /* TH */
{ pre_SH, post_SH, 0 }, /* SH */
{ pre_SS, post_SS, 0 }, /* SS */
{ pre_TP, post_TP, 0 }, /* TP */
{ pre_PP, NULL, 0 }, /* LP */
{ pre_PP, NULL, 0 }, /* PP */
{ pre_PP, NULL, 0 }, /* P */
{ pre_IP, post_IP, 0 }, /* IP */
{ pre_HP, post_HP, 0 }, /* HP */
{ NULL, NULL, 0 }, /* SM */
{ pre_B, NULL, 0 }, /* SB */
{ pre_BI, NULL, 0 }, /* BI */
{ pre_BI, NULL, 0 }, /* IB */
{ pre_RB, NULL, 0 }, /* BR */
{ pre_RB, NULL, 0 }, /* RB */
{ NULL, NULL, 0 }, /* R */
{ pre_B, NULL, 0 }, /* B */
{ pre_I, NULL, 0 }, /* I */
{ pre_RI, NULL, 0 }, /* IR */
{ pre_RI, NULL, 0 }, /* RI */
{ NULL, NULL, MAN_NOTEXT }, /* na */
{ pre_I, NULL, 0 }, /* i */
{ pre_sp, NULL, MAN_NOTEXT }, /* sp */
{ pre_nf, NULL, 0 }, /* nf */
{ pre_fi, NULL, 0 }, /* fi */
{ NULL, NULL, 0 }, /* r */
{ NULL, NULL, 0 }, /* RE */
{ pre_RS, post_RS, 0 }, /* RS */
{ pre_ign, NULL, 0 }, /* DT */
{ pre_ign, NULL, 0 }, /* UC */
{ pre_ign, NULL, 0 }, /* PD */
{ pre_sp, NULL, MAN_NOTEXT }, /* Sp */
{ pre_nf, NULL, 0 }, /* Vb */
{ pre_fi, NULL, 0 }, /* Ve */
{ pre_ign, NULL, 0 }, /* AT */
};
void
terminal_man(void *arg, const struct man *man)
{
struct termp *p;
const struct man_node *n;
const struct man_meta *m;
struct mtermp mt;
p = (struct termp *)arg;
p->overstep = 0;
p->maxrmargin = p->defrmargin;
p->tabwidth = 5;
if (NULL == p->symtab)
switch (p->enc) {
case (TERMENC_ASCII):
p->symtab = chars_init(CHARS_ASCII);
break;
default:
abort();
/* NOTREACHED */
}
n = man_node(man);
m = man_meta(man);
term_begin(p, print_man_head, print_man_foot, m);
p->flags |= TERMP_NOSPACE;
mt.fl = 0;
mt.lmargin = INDENT;
mt.offset = INDENT;
if (n->child)
print_man_nodelist(p, &mt, n->child, m);
term_end(p);
}
static int
a2height(const struct man_node *n)
{
struct roffsu su;
assert(MAN_TEXT == n->type);
assert(n->string);
if ( ! a2roffsu(n->string, &su, SCALE_VS))
SCALE_VS_INIT(&su, strlen(n->string));
return((int)term_vspan(&su));
}
static int
a2width(const struct man_node *n)
{
struct roffsu su;
assert(MAN_TEXT == n->type);
assert(n->string);
if ( ! a2roffsu(n->string, &su, SCALE_BU))
return(-1);
return((int)term_hspan(&su));
}
static void
print_bvspace(struct termp *p, const struct man_node *n)
{
term_newln(p);
if (NULL == n->prev)
return;
if (MAN_SS == n->prev->tok)
return;
if (MAN_SH == n->prev->tok)
return;
term_vspace(p);
}
/* ARGSUSED */
static int
pre_ign(DECL_ARGS)
{
return(0);
}
/* ARGSUSED */
static int
pre_I(DECL_ARGS)
{
term_fontrepl(p, TERMFONT_UNDER);
return(1);
}
/* ARGSUSED */
static int
pre_fi(DECL_ARGS)
{
mt->fl &= ~MANT_LITERAL;
return(1);
}
/* ARGSUSED */
static int
pre_nf(DECL_ARGS)
{
mt->fl |= MANT_LITERAL;
return(MAN_Vb != n->tok);
}
/* ARGSUSED */
static int
pre_RB(DECL_ARGS)
{
const struct man_node *nn;
int i;
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
if (i % 2 && MAN_RB == n->tok)
term_fontrepl(p, TERMFONT_BOLD);
else if ( ! (i % 2) && MAN_RB != n->tok)
term_fontrepl(p, TERMFONT_BOLD);
else
term_fontrepl(p, TERMFONT_NONE);
if (i > 0)
p->flags |= TERMP_NOSPACE;
print_man_node(p, mt, nn, m);
}
return(0);
}
/* ARGSUSED */
static int
pre_RI(DECL_ARGS)
{
const struct man_node *nn;
int i;
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
if (i % 2 && MAN_RI == n->tok)
term_fontrepl(p, TERMFONT_UNDER);
else if ( ! (i % 2) && MAN_RI != n->tok)
term_fontrepl(p, TERMFONT_UNDER);
else
term_fontrepl(p, TERMFONT_NONE);
if (i > 0)
p->flags |= TERMP_NOSPACE;
print_man_node(p, mt, nn, m);
}
return(0);
}
/* ARGSUSED */
static int
pre_BI(DECL_ARGS)
{
const struct man_node *nn;
int i;
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
if (i % 2 && MAN_BI == n->tok)
term_fontrepl(p, TERMFONT_UNDER);
else if (i % 2)
term_fontrepl(p, TERMFONT_BOLD);
else if (MAN_BI == n->tok)
term_fontrepl(p, TERMFONT_BOLD);
else
term_fontrepl(p, TERMFONT_UNDER);
if (i)
p->flags |= TERMP_NOSPACE;
print_man_node(p, mt, nn, m);
}
return(0);
}
/* ARGSUSED */
static int
pre_B(DECL_ARGS)
{
term_fontrepl(p, TERMFONT_BOLD);
return(1);
}
/* ARGSUSED */
static int
pre_sp(DECL_ARGS)
{
int i, len;
len = n->child ? a2height(n->child) : 1;
if (0 == len)
term_newln(p);
for (i = 0; i <= len; i++)
term_vspace(p);
return(0);
}
/* ARGSUSED */
static int
pre_br(DECL_ARGS)
{
term_newln(p);
return(0);
}
/* ARGSUSED */
static int
pre_HP(DECL_ARGS)
{
size_t len;
int ival;
const struct man_node *nn;
switch (n->type) {
case (MAN_BLOCK):
print_bvspace(p, n);
return(1);
case (MAN_BODY):
p->flags |= TERMP_NOBREAK;
p->flags |= TERMP_TWOSPACE;
break;
default:
return(0);
}
len = mt->lmargin;
ival = -1;
/* Calculate offset. */
if (NULL != (nn = n->parent->head->child))
if ((ival = a2width(nn)) >= 0)
len = (size_t)ival;
if (0 == len)
len = 1;
p->offset = mt->offset;
p->rmargin = mt->offset + len;
if (ival >= 0)
mt->lmargin = (size_t)ival;
return(1);
}
/* ARGSUSED */
static void
post_HP(DECL_ARGS)
{
switch (n->type) {
case (MAN_BLOCK):
term_flushln(p);
break;
case (MAN_BODY):
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
p->flags &= ~TERMP_TWOSPACE;
p->offset = mt->offset;
p->rmargin = p->maxrmargin;
break;
default:
break;
}
}
/* ARGSUSED */
static int
pre_PP(DECL_ARGS)
{
switch (n->type) {
case (MAN_BLOCK):
mt->lmargin = INDENT;
print_bvspace(p, n);
break;
default:
p->offset = mt->offset;
break;
}
return(1);
}
/* ARGSUSED */
static int
pre_IP(DECL_ARGS)
{
const struct man_node *nn;
size_t len;
int ival;
switch (n->type) {
case (MAN_BODY):
p->flags |= TERMP_NOLPAD;
p->flags |= TERMP_NOSPACE;
break;
case (MAN_HEAD):
p->flags |= TERMP_NOBREAK;
break;
case (MAN_BLOCK):
print_bvspace(p, n);
/* FALLTHROUGH */
default:
return(1);
}
len = mt->lmargin;
ival = -1;
/* Calculate offset. */
if (NULL != (nn = n->parent->head->child))
if (NULL != (nn = nn->next)) {
for ( ; nn->next; nn = nn->next)
/* Do nothing. */ ;
if ((ival = a2width(nn)) >= 0)
len = (size_t)ival;
}
switch (n->type) {
case (MAN_HEAD):
/* Handle zero-width lengths. */
if (0 == len)
len = 1;
p->offset = mt->offset;
p->rmargin = mt->offset + len;
if (ival < 0)
break;
/* Set the saved left-margin. */
mt->lmargin = (size_t)ival;
/* Don't print the length value. */
for (nn = n->child; nn->next; nn = nn->next)
print_man_node(p, mt, nn, m);
return(0);
case (MAN_BODY):
p->offset = mt->offset + len;
p->rmargin = p->maxrmargin;
break;
default:
break;
}
return(1);
}
/* ARGSUSED */
static void
post_IP(DECL_ARGS)
{
switch (n->type) {
case (MAN_HEAD):
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
p->rmargin = p->maxrmargin;
break;
case (MAN_BODY):
term_flushln(p);
p->flags &= ~TERMP_NOLPAD;
break;
default:
break;
}
}
/* ARGSUSED */
static int
pre_TP(DECL_ARGS)
{
const struct man_node *nn;
size_t len;
int ival;
switch (n->type) {
case (MAN_HEAD):
p->flags |= TERMP_NOBREAK;
p->flags |= TERMP_TWOSPACE;
break;
case (MAN_BODY):
p->flags |= TERMP_NOLPAD;
p->flags |= TERMP_NOSPACE;
break;
case (MAN_BLOCK):
print_bvspace(p, n);
/* FALLTHROUGH */
default:
return(1);
}
len = (size_t)mt->lmargin;
ival = -1;
/* Calculate offset. */
if (NULL != (nn = n->parent->head->child)) {
while (nn && MAN_TEXT != nn->type)
nn = nn->next;
if (nn && nn->next)
if ((ival = a2width(nn)) >= 0)
len = (size_t)ival;
}
switch (n->type) {
case (MAN_HEAD):
/* Handle zero-length properly. */
if (0 == len)
len = 1;
p->offset = mt->offset;
p->rmargin = mt->offset + len;
/* Don't print same-line elements. */
for (nn = n->child; nn; nn = nn->next)
if (nn->line > n->line)
print_man_node(p, mt, nn, m);
if (ival >= 0)
mt->lmargin = (size_t)ival;
return(0);
case (MAN_BODY):
p->offset = mt->offset + len;
p->rmargin = p->maxrmargin;
break;
default:
break;
}
return(1);
}
/* ARGSUSED */
static void
post_TP(DECL_ARGS)
{
switch (n->type) {
case (MAN_HEAD):
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
p->flags &= ~TERMP_TWOSPACE;
p->rmargin = p->maxrmargin;
break;
case (MAN_BODY):
term_flushln(p);
p->flags &= ~TERMP_NOLPAD;
break;
default:
break;
}
}
/* ARGSUSED */
static int
pre_SS(DECL_ARGS)
{
switch (n->type) {
case (MAN_BLOCK):
mt->lmargin = INDENT;
mt->offset = INDENT;
/* If following a prior empty `SS', no vspace. */
if (n->prev && MAN_SS == n->prev->tok)
if (NULL == n->prev->body->child)
break;
if (NULL == n->prev)
break;
term_vspace(p);
break;
case (MAN_HEAD):
term_fontrepl(p, TERMFONT_BOLD);
p->offset = HALFINDENT;
break;
case (MAN_BODY):
p->offset = mt->offset;
break;
default:
break;
}
return(1);
}
/* ARGSUSED */
static void
post_SS(DECL_ARGS)
{
switch (n->type) {
case (MAN_HEAD):
term_newln(p);
break;
case (MAN_BODY):
term_newln(p);
break;
default:
break;
}
}
/* ARGSUSED */
static int
pre_SH(DECL_ARGS)
{
switch (n->type) {
case (MAN_BLOCK):
mt->lmargin = INDENT;
mt->offset = INDENT;
/* If following a prior empty `SH', no vspace. */
if (n->prev && MAN_SH == n->prev->tok)
if (NULL == n->prev->body->child)
break;
/* If the first macro, no vspae. */
if (NULL == n->prev)
break;
term_vspace(p);
break;
case (MAN_HEAD):
term_fontrepl(p, TERMFONT_BOLD);
p->offset = 0;
break;
case (MAN_BODY):
p->offset = mt->offset;
break;
default:
break;
}
return(1);
}
/* ARGSUSED */
static void
post_SH(DECL_ARGS)
{
switch (n->type) {
case (MAN_HEAD):
term_newln(p);
break;
case (MAN_BODY):
term_newln(p);
break;
default:
break;
}
}
/* ARGSUSED */
static int
pre_RS(DECL_ARGS)
{
const struct man_node *nn;
int ival;
switch (n->type) {
case (MAN_BLOCK):
term_newln(p);
return(1);
case (MAN_HEAD):
return(0);
default:
break;
}
if (NULL == (nn = n->parent->head->child)) {
mt->offset = mt->lmargin + INDENT;
p->offset = mt->offset;
return(1);
}
if ((ival = a2width(nn)) < 0)
return(1);
mt->offset = INDENT + (size_t)ival;
p->offset = mt->offset;
return(1);
}
/* ARGSUSED */
static void
post_RS(DECL_ARGS)
{
switch (n->type) {
case (MAN_BLOCK):
mt->offset = mt->lmargin = INDENT;
break;
case (MAN_HEAD):
break;
default:
term_newln(p);
p->offset = INDENT;
break;
}
}
static void
print_man_node(DECL_ARGS)
{
size_t rm, rmax;
int c;
c = 1;
switch (n->type) {
case(MAN_TEXT):
if (0 == *n->string) {
term_vspace(p);
break;
}
term_word(p, n->string);
/* FIXME: this means that macro lines are munged! */
if (MANT_LITERAL & mt->fl) {
rm = p->rmargin;
rmax = p->maxrmargin;
p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
p->flags |= TERMP_NOSPACE;
term_flushln(p);
p->rmargin = rm;
p->maxrmargin = rmax;
}
break;
default:
if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
term_fontrepl(p, TERMFONT_NONE);
if (termacts[n->tok].pre)
c = (*termacts[n->tok].pre)(p, mt, n, m);
break;
}
if (c && n->child)
print_man_nodelist(p, mt, n->child, m);
if (MAN_TEXT != n->type) {
if (termacts[n->tok].post)
(*termacts[n->tok].post)(p, mt, n, m);
if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
term_fontrepl(p, TERMFONT_NONE);
}
if (MAN_EOS & n->flags)
p->flags |= TERMP_SENTENCE;
}
static void
print_man_nodelist(DECL_ARGS)
{
print_man_node(p, mt, n, m);
if ( ! n->next)
return;
print_man_nodelist(p, mt, n->next, m);
}
static void
print_man_foot(struct termp *p, const void *arg)
{
char buf[DATESIZ];
const struct man_meta *meta;
meta = (const struct man_meta *)arg;
term_fontrepl(p, TERMFONT_NONE);
if (meta->rawdate)
strlcpy(buf, meta->rawdate, DATESIZ);
else
time2a(meta->date, buf, DATESIZ);
term_vspace(p);
term_vspace(p);
term_vspace(p);
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
p->rmargin = p->maxrmargin - strlen(buf);
p->offset = 0;
if (meta->source)
term_word(p, meta->source);
if (meta->source)
term_word(p, "");
term_flushln(p);
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
p->offset = p->rmargin;
p->rmargin = p->maxrmargin;
p->flags &= ~TERMP_NOBREAK;
term_word(p, buf);
term_flushln(p);
}
static void
print_man_head(struct termp *p, const void *arg)
{
char buf[BUFSIZ], title[BUFSIZ];
size_t buflen, titlen;
const struct man_meta *m;
m = (const struct man_meta *)arg;
/*
* Note that old groff would spit out some spaces before the
* header. We discontinue this strange behaviour, but at one
* point we did so here.
*/
p->rmargin = p->maxrmargin;
p->offset = 0;
buf[0] = title[0] = '\0';
if (m->vol)
strlcpy(buf, m->vol, BUFSIZ);
buflen = strlen(buf);
snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
titlen = strlen(title);
p->offset = 0;
p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
(p->maxrmargin - strlen(buf) + 1) / 2 :
p->maxrmargin - buflen;
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
term_word(p, title);
term_flushln(p);
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
p->offset = p->rmargin;
p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
p->maxrmargin - titlen : p->maxrmargin;
term_word(p, buf);
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
if (p->rmargin + titlen <= p->maxrmargin) {
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
p->offset = p->rmargin;
p->rmargin = p->maxrmargin;
term_word(p, title);
term_flushln(p);
}
p->rmargin = p->maxrmargin;
p->offset = 0;
p->flags &= ~TERMP_NOSPACE;
/*
* Groff likes to have some leading spaces before content. Well
* that's fine by me.
*/
term_vspace(p);
term_vspace(p);
term_vspace(p);
}

View file

@ -0,0 +1,328 @@
/* $Id: man_validate.c,v 1.44 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include "mandoc.h"
#include "libman.h"
#include "libmandoc.h"
#define CHKARGS struct man *m, struct man_node *n
typedef int (*v_check)(CHKARGS);
struct man_valid {
v_check *pres;
v_check *posts;
};
static int check_bline(CHKARGS);
static int check_eq0(CHKARGS);
static int check_le1(CHKARGS);
static int check_ge2(CHKARGS);
static int check_le5(CHKARGS);
static int check_par(CHKARGS);
static int check_part(CHKARGS);
static int check_root(CHKARGS);
static int check_sec(CHKARGS);
static int check_text(CHKARGS);
static int check_title(CHKARGS);
static v_check posts_eq0[] = { check_eq0, NULL };
static v_check posts_th[] = { check_ge2, check_le5, check_title, NULL };
static v_check posts_par[] = { check_par, NULL };
static v_check posts_part[] = { check_part, NULL };
static v_check posts_sec[] = { check_sec, NULL };
static v_check posts_le1[] = { check_le1, NULL };
static v_check pres_bline[] = { check_bline, NULL };
static const struct man_valid man_valids[MAN_MAX] = {
{ NULL, posts_eq0 }, /* br */
{ pres_bline, posts_th }, /* TH */
{ pres_bline, posts_sec }, /* SH */
{ pres_bline, posts_sec }, /* SS */
{ pres_bline, posts_par }, /* TP */
{ pres_bline, posts_par }, /* LP */
{ pres_bline, posts_par }, /* PP */
{ pres_bline, posts_par }, /* P */
{ pres_bline, posts_par }, /* IP */
{ pres_bline, posts_par }, /* HP */
{ NULL, NULL }, /* SM */
{ NULL, NULL }, /* SB */
{ NULL, NULL }, /* BI */
{ NULL, NULL }, /* IB */
{ NULL, NULL }, /* BR */
{ NULL, NULL }, /* RB */
{ NULL, NULL }, /* R */
{ NULL, NULL }, /* B */
{ NULL, NULL }, /* I */
{ NULL, NULL }, /* IR */
{ NULL, NULL }, /* RI */
{ NULL, posts_eq0 }, /* na */
{ NULL, NULL }, /* i */
{ NULL, posts_le1 }, /* sp */
{ pres_bline, posts_eq0 }, /* nf */
{ pres_bline, posts_eq0 }, /* fi */
{ NULL, NULL }, /* r */
{ NULL, NULL }, /* RE */
{ NULL, posts_part }, /* RS */
{ NULL, NULL }, /* DT */
{ NULL, NULL }, /* UC */
{ NULL, NULL }, /* PD */
{ NULL, posts_le1 }, /* Sp */
{ pres_bline, posts_le1 }, /* Vb */
{ pres_bline, posts_eq0 }, /* Ve */
{ NULL, NULL }, /* AT */
};
int
man_valid_pre(struct man *m, struct man_node *n)
{
v_check *cp;
if (MAN_TEXT == n->type)
return(1);
if (MAN_ROOT == n->type)
return(1);
if (NULL == (cp = man_valids[n->tok].pres))
return(1);
for ( ; *cp; cp++)
if ( ! (*cp)(m, n))
return(0);
return(1);
}
int
man_valid_post(struct man *m)
{
v_check *cp;
if (MAN_VALID & m->last->flags)
return(1);
m->last->flags |= MAN_VALID;
switch (m->last->type) {
case (MAN_TEXT):
return(check_text(m, m->last));
case (MAN_ROOT):
return(check_root(m, m->last));
default:
break;
}
if (NULL == (cp = man_valids[m->last->tok].posts))
return(1);
for ( ; *cp; cp++)
if ( ! (*cp)(m, m->last))
return(0);
return(1);
}
static int
check_root(CHKARGS)
{
if (MAN_BLINE & m->flags)
return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT));
if (MAN_ELINE & m->flags)
return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT));
m->flags &= ~MAN_BLINE;
m->flags &= ~MAN_ELINE;
if (NULL == m->first->child) {
man_nmsg(m, n, MANDOCERR_NODOCBODY);
return(0);
} else if (NULL == m->meta.title) {
if ( ! man_nmsg(m, n, MANDOCERR_NOTITLE))
return(0);
/*
* If a title hasn't been set, do so now (by
* implication, date and section also aren't set).
*
* FIXME: this should be in man_action.c.
*/
m->meta.title = mandoc_strdup("unknown");
m->meta.date = time(NULL);
m->meta.msec = mandoc_strdup("1");
}
return(1);
}
static int
check_title(CHKARGS)
{
const char *p;
assert(n->child);
/* FIXME: is this sufficient? */
if ('\0' == *n->child->string) {
man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
return(0);
}
for (p = n->child->string; '\0' != *p; p++)
if (isalpha((u_char)*p) && ! isupper((u_char)*p))
if ( ! man_nmsg(m, n, MANDOCERR_UPPERCASE))
return(0);
return(1);
}
static int
check_text(CHKARGS)
{
char *p;
int pos, c;
assert(n->string);
for (p = n->string, pos = n->pos + 1; *p; p++, pos++) {
if ('\\' == *p) {
c = mandoc_special(p);
if (c) {
p += c - 1;
pos += c - 1;
continue;
}
c = man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE);
if ( ! (MAN_IGN_ESCAPE & m->pflags) && ! c)
return(c);
}
if ('\t' == *p || isprint((u_char)*p) || ASCII_HYPH == *p)
continue;
if ( ! man_pmsg(m, n->line, pos, MANDOCERR_BADCHAR))
return(0);
}
return(1);
}
#define INEQ_DEFINE(x, ineq, name) \
static int \
check_##name(CHKARGS) \
{ \
if (n->nchild ineq (x)) \
return(1); \
man_vmsg(m, MANDOCERR_SYNTARGCOUNT, n->line, n->pos, \
"line arguments %s %d (have %d)", \
#ineq, (x), n->nchild); \
return(0); \
}
INEQ_DEFINE(0, ==, eq0)
INEQ_DEFINE(1, <=, le1)
INEQ_DEFINE(2, >=, ge2)
INEQ_DEFINE(5, <=, le5)
static int
check_sec(CHKARGS)
{
if (MAN_HEAD == n->type && 0 == n->nchild) {
man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
return(0);
} else if (MAN_BODY == n->type && 0 == n->nchild)
return(man_nmsg(m, n, MANDOCERR_NOBODY));
return(1);
}
static int
check_part(CHKARGS)
{
if (MAN_BODY == n->type && 0 == n->nchild)
return(man_nmsg(m, n, MANDOCERR_NOBODY));
return(1);
}
static int
check_par(CHKARGS)
{
if (MAN_BODY == n->type)
switch (n->tok) {
case (MAN_IP):
/* FALLTHROUGH */
case (MAN_HP):
/* FALLTHROUGH */
case (MAN_TP):
/* Body-less lists are ok. */
break;
default:
if (n->nchild)
break;
return(man_nmsg(m, n, MANDOCERR_NOBODY));
}
if (MAN_HEAD == n->type)
switch (n->tok) {
case (MAN_PP):
/* FALLTHROUGH */
case (MAN_P):
/* FALLTHROUGH */
case (MAN_LP):
if (0 == n->nchild)
break;
return(man_nmsg(m, n, MANDOCERR_ARGSLOST));
default:
if (n->nchild)
break;
return(man_nmsg(m, n, MANDOCERR_NOARGS));
}
return(1);
}
static int
check_bline(CHKARGS)
{
assert( ! (MAN_ELINE & m->flags));
if (MAN_BLINE & m->flags) {
man_nmsg(m, n, MANDOCERR_SYNTLINESCOPE);
return(0);
}
return(1);
}

530
commands/mdocml/mandoc.1 Normal file
View file

@ -0,0 +1,530 @@
.\" $Id: mandoc.1,v 1.63 2010/06/11 07:15:42 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.
.\"
.Dd $Mdocdate: June 11 2010 $
.Dt MANDOC 1
.Os
.Sh NAME
.Nm mandoc
.Nd format and display UNIX manuals
.Sh SYNOPSIS
.Nm mandoc
.Op Fl V
.Op Fl f Ns Ar option
.Op Fl m Ns Ar format
.Op Fl O Ns Ar option
.Op Fl T Ns Ar output
.Op Fl W Ns Ar err
.Op Ar file...
.Sh DESCRIPTION
The
.Nm
utility formats
.Ux
manual pages for display.
The arguments are as follows:
.Bl -tag -width Ds
.It Fl f Ns Ar option
Comma-separated compiler options.
See
.Sx Compiler Options
for details.
.It Fl m Ns Ar format
Input format.
See
.Sx Input Formats
for available formats.
Defaults to
.Fl m Ns Cm andoc .
.It Fl O Ns Ar option
Comma-separated output options.
See
.Sx Output Options
for details.
.It Fl T Ns Ar output
Output format.
See
.Sx Output Formats
for available formats.
Defaults to
.Fl T Ns Cm ascii .
.It Fl V
Print version and exit.
.It Fl W Ns Ar err
Comma-separated warning options.
Use
.Fl W Ns Cm all
to print warnings,
.Fl W Ns Cm error
for warnings to be considered errors and cause utility
termination.
Multiple
.Fl W
arguments may be comma-separated, such as
.Fl W Ns Cm error , Ns Cm all .
.It Ar file
Read input from zero or more files.
If unspecified, reads from stdin.
If multiple files are specified,
.Nm
will halt with the first failed parse.
.El
.Pp
By default,
.Nm
reads
.Xr mdoc 7
or
.Xr man 7
text from stdin, implying
.Fl m Ns Cm andoc ,
and produces
.Fl T Ns Cm ascii
output.
.Pp
.Ex -std mandoc
.Ss Input Formats
The
.Nm
utility accepts
.Xr mdoc 7
and
.Xr man 7
input with
.Fl m Ns Cm doc
and
.Fl m Ns Cm an ,
respectively.
The
.Xr mdoc 7
format is
.Em strongly
recommended;
.Xr man 7
should only be used for legacy manuals.
.Pp
A third option,
.Fl m Ns Cm andoc ,
which is also the default, determines encoding on-the-fly: if the first
non-comment macro is
.Sq \&Dd
or
.Sq \&Dt ,
the
.Xr mdoc 7
parser is used; otherwise, the
.Xr man 7
parser is used.
.Pp
If multiple
files are specified with
.Fl m Ns Cm andoc ,
each has its file-type determined this way.
If multiple files are
specified and
.Fl m Ns Cm doc
or
.Fl m Ns Cm an
is specified, then this format is used exclusively.
.Ss Output Formats
The
.Nm
utility accepts the following
.Fl T
arguments (see
.Sx OUTPUT ) :
.Bl -tag -width Ds
.It Fl T Ns Cm ascii
Produce 7-bit ASCII output, backspace-encoded for bold and underline
styles.
This is the default.
See
.Sx ASCII Output .
.It Fl T Ns Cm html
Produce strict HTML-4.01 output, with a sane default style.
See
.Sx HTML Output .
.It Fl T Ns Cm lint
Parse only: produce no output.
Implies
.Fl W Ns Cm all
and
.Fl f Ns Cm strict .
.It Fl T Ns Cm ps
Produce PostScript output.
See
.Sx PostScript Output .
.It Fl T Ns Cm tree
Produce an indented parse tree.
.It Fl T Ns Cm xhtml
Produce strict XHTML-1.0 output, with a sane default style.
See
.Sx XHTML Output .
.El
.Pp
If multiple input files are specified, these will be processed by the
corresponding filter in-order.
.Ss Compiler Options
Default compiler behaviour may be overridden with the
.Fl f
flag.
.Bl -tag -width Ds
.It Fl f Ns Cm ign-errors
When parsing multiple files, don't halt when one errors out.
Useful with
.Fl T Ns Cm lint
over a large set of manuals passed on the command line.
.It Fl f Ns Cm ign-escape
Ignore invalid escape sequences.
This is the default, but the option can be used to override an earlier
.Fl f Ns Cm strict .
.It Fl f Ns Cm ign-scope
When rewinding the scope of a block macro, forces the compiler to ignore
scope violations.
This can seriously mangle the resulting tree.
.Pq mdoc only
.It Fl f Ns Cm no-ign-escape
Do not ignore invalid escape sequences.
.It Fl f Ns Cm no-ign-macro
Do not ignore unknown macros at the start of input lines.
.It Fl f Ns Cm strict
Implies
.Fl f Ns Cm no-ign-escape
and
.Fl f Ns Cm no-ign-macro .
.El
.Ss Output Options
The
.Fl T Ns Ar html
and
.Fl T Ns Ar xhtml
modes accept the following output options:
.Bl -tag -width Ds
.It Fl O Ns Cm includes Ns = Ns Ar fmt
The string
.Ar fmt ,
for example,
.Ar ../src/%I.html ,
is used as a template for linked header files (usually via the
.Sq \&In
macro).
Instances of
.Sq \&%I
are replaced with the include filename.
The default is not to present a
hyperlink.
.It Fl O Ns Cm man Ns = Ns Ar fmt
The string
.Ar fmt ,
for example,
.Ar ../html%S/%N.%S.html ,
is used as a template for linked manuals (usually via the
.Sq \&Xr
macro).
Instances of
.Sq \&%N
and
.Sq %S
are replaced with the linked manual's name and section, respectively.
If no section is included, section 1 is assumed.
The default is not to
present a hyperlink.
.It Fl O Ns Cm style Ns = Ns Ar style.css
The file
.Ar style.css
is used for an external style-sheet.
This must be a valid absolute or
relative URI.
.El
.Pp
The
.Fl T Ns Ar ascii
mode accepts the following output option:
.Bl -tag -width Ds
.It Fl O Ns Cm width Ns = Ns Ar width
The output width is set to
.Ar width ,
which will normalise to \(>=60.
.El
.Sh OUTPUT
This section documents output details of
.Nm .
In general, output conforms to the traditional manual style of a header,
a body composed of sections and sub-sections, and a footer.
.Pp
The text style of output characters (non-macro characters, punctuation,
and white-space) is dictated by context.
.Pp
White-space is generally stripped from input.
This can be changed with
character escapes (specified in
.Xr mandoc_char 7 )
or literal modes (specified in
.Xr mdoc 7
and
.Xr man 7 ) .
.Pp
If non-macro punctuation is set apart from words, such as in the phrase
.Dq to be \&, or not to be ,
it's processed by
.Nm ,
regardless of output format, according to the following rules: opening
punctuation
.Po
.Sq \&( ,
.Sq \&[ ,
and
.Sq \&{
.Pc
is not followed by a space; closing punctuation
.Po
.Sq \&. ,
.Sq \&, ,
.Sq \&; ,
.Sq \&: ,
.Sq \&? ,
.Sq \&! ,
.Sq \&) ,
.Sq \&]
and
.Sq \&}
.Pc
is not preceded by white-space.
.Pp
If the input is
.Xr mdoc 7 ,
however, these rules are also applied to macro arguments when appropriate.
.Ss ASCII Output
Output produced by
.Fl T Ns Cm ascii ,
which is the default, is rendered in standard 7-bit ASCII documented in
.Xr ascii 7 .
.Pp
Font styles are applied by using back-spaced encoding such that an
underlined character
.Sq c
is rendered as
.Sq _ Ns \e[bs] Ns c ,
where
.Sq \e[bs]
is the back-space character number 8.
Emboldened characters are rendered as
.Sq c Ns \e[bs] Ns c .
.Pp
The special characters documented in
.Xr mandoc_char 7
are rendered best-effort in an ASCII equivalent.
.Pp
Output width is limited to 78 visible columns unless literal input lines
exceed this limit.
.Ss HTML Output
Output produced by
.Fl T Ns Cm html
conforms to HTML-4.01 strict.
.Pp
Font styles and page structure are applied using CSS2.
By default, no font style is applied to any text,
although CSS2 is hard-coded to format
the basic structure of output.
.Pp
The
.Pa example.style.css
file documents the range of styles applied to output and, if used, will
cause rendered documents to appear as they do in
.Fl T Ns Cm ascii .
.Pp
Special characters are rendered in decimal-encoded UTF-8.
.Ss PostScript Output
PostScript Level 2 pages may be generated by
.Fl T Ns Cm ps .
Output pages are US-letter sized (215.9 x 279.4 mm) and rendered in
fixed, 10-point Courier font.
.Ss XHTML Output
Output produced by
.Fl T Ns Cm xhtml
conforms to XHTML-1.0 strict.
.Pp
See
.Sx HTML Output
for details; beyond generating XHTML tags instead of HTML tags, these
output modes are identical.
.Sh EXAMPLES
To page manuals to the terminal:
.Pp
.D1 $ mandoc \-Wall,error \-fstrict mandoc.1 2\*(Gt&1 | less
.D1 $ mandoc mandoc.1 mdoc.3 mdoc.7 | less
.Pp
To produce HTML manuals with
.Ar style.css
as the style-sheet:
.Pp
.D1 $ mandoc \-Thtml -Ostyle=style.css mdoc.7 \*(Gt mdoc.7.html
.Pp
To check over a large set of manuals:
.Pp
.Dl $ mandoc \-Tlint \-fign-errors `find /usr/src -name \e*\e.[1-9]`
.Sh COMPATIBILITY
This section summarises
.Nm
compatibility with
.Xr groff 1 .
Each input and output format is separately noted.
.Ss ASCII Compatibility
.Bl -bullet -compact
.It
The
.Sq \e~
special character doesn't produce expected behaviour in
.Fl T Ns Cm ascii .
.It
The
.Sq \&Bd \-literal
and
.Sq \&Bd \-unfilled
macros of
.Xr mdoc 7
in
.Fl T Ns Cm ascii
are synonyms, as are \-filled and \-ragged.
.It
In
.Xr groff 1 ,
the
.Sq \&Pa
.Xr mdoc 7
macro does not underline when scoped under an
.Sq \&It
in the FILES section.
This behaves correctly in
.Nm .
.It
A list or display following the
.Sq \&Ss
.Xr mdoc 7
macro in
.Fl T Ns Cm ascii
does not assert a prior vertical break, just as it doesn't with
.Sq \&Sh .
.It
The
.Sq \&na
.Xr man 7
macro in
.Fl T Ns Cm ascii
has no effect.
.It
Words aren't hyphenated.
.It
In normal mode (not a literal block), blocks of spaces aren't preserved,
so double spaces following sentence closure are reduced to a single space;
.Xr groff 1
retains spaces.
.It
Sentences are unilaterally monospaced.
.El
.Ss HTML/XHTML Compatibility
.Bl -bullet -compact
.It
The
.Sq \efP
escape will revert the font to the previous
.Sq \ef
escape, not to the last rendered decoration, which is now dictated by
CSS instead of hard-coded.
It also will not span past the current scope,
for the same reason.
Note that in
.Sx ASCII Output
mode, this will work fine.
.It
The
.Xr mdoc 7
.Sq \&Bl \-hang
and
.Sq \&Bl \-tag
list types render similarly (no break following overreached left-hand
side) due to the expressive constraints of HTML.
.It
The
.Xr man 7
.Sq IP
and
.Sq TP
lists render similarly.
.El
.Sh SEE ALSO
.Xr man 7 ,
.Xr mandoc_char 7 ,
.Xr mdoc 7
.Sh AUTHORS
The
.Nm
utility was written by
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
.Sh CAVEATS
The
.Fl T Ns Cm html
and
.Fl T Ns Cm xhtml
CSS2 styling used for
.Fl m Ns Cm doc
input lists does not render properly in older browsers, such as Internet
Explorer 6 and earlier.
.Pp
In
.Fl T Ns Cm html
and
.Fl T Ns Cm xhtml ,
the maximum size of an element attribute is determined by
.Dv BUFSIZ ,
which is usually 1024 bytes.
Be aware of this when setting long link
formats such as
.Fl O Ns Cm style Ns = Ns Ar really/long/link .
.Pp
The
.Fl T Ns Cm html
and
.Fl T Ns Cm xhtml
output modes don't render the
.Sq \es
font size escape documented in
.Xr mdoc 7
and
.Xr man 7 .
.Pp
Nesting elements within next-line element scopes of
.Fl m Ns Cm an ,
such as
.Sq br
within an empty
.Sq B ,
will confuse
.Fl T Ns Cm html
and
.Fl T Ns Cm xhtml
and cause them to forget the formatting of the prior next-line scope.
.Pp
The
.Sq i
macro in
.Fl m Ns Cm an
should italicise all subsequent text if a line argument is not provided.
This behaviour is not implemented.
The
.Sq \(aq
control character is an alias for the standard macro control character
and does not emit a line-break as stipulated in GNU troff.

396
commands/mdocml/mandoc.c Normal file
View file

@ -0,0 +1,396 @@
/* $Id: mandoc.c,v 1.19 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmandoc.h"
static int a2time(time_t *, const char *, const char *);
static int spec_norm(char *, int);
/*
* "Normalise" a special string by converting its ASCII_HYPH entries
* into actual hyphens.
*/
static int
spec_norm(char *p, int sz)
{
int i;
for (i = 0; i < sz; i++)
if (ASCII_HYPH == p[i])
p[i] = '-';
return(sz);
}
int
mandoc_special(char *p)
{
int terminator; /* Terminator for \s. */
int lim; /* Limit for N in \s. */
int c, i;
char *sv;
sv = p;
if ('\\' != *p++)
return(spec_norm(sv, 0));
switch (*p) {
case ('\''):
/* FALLTHROUGH */
case ('`'):
/* FALLTHROUGH */
case ('q'):
/* FALLTHROUGH */
case (ASCII_HYPH):
/* FALLTHROUGH */
case ('-'):
/* FALLTHROUGH */
case ('~'):
/* FALLTHROUGH */
case ('^'):
/* FALLTHROUGH */
case ('%'):
/* FALLTHROUGH */
case ('0'):
/* FALLTHROUGH */
case (' '):
/* FALLTHROUGH */
case ('}'):
/* FALLTHROUGH */
case ('|'):
/* FALLTHROUGH */
case ('&'):
/* FALLTHROUGH */
case ('.'):
/* FALLTHROUGH */
case (':'):
/* FALLTHROUGH */
case ('c'):
/* FALLTHROUGH */
case ('e'):
return(spec_norm(sv, 2));
case ('s'):
if ('\0' == *++p)
return(spec_norm(sv, 2));
c = 2;
terminator = 0;
lim = 1;
if (*p == '\'') {
lim = 0;
terminator = 1;
++p;
++c;
} else if (*p == '[') {
lim = 0;
terminator = 2;
++p;
++c;
} else if (*p == '(') {
lim = 2;
terminator = 3;
++p;
++c;
}
if (*p == '+' || *p == '-') {
++p;
++c;
}
if (*p == '\'') {
if (terminator)
return(spec_norm(sv, 0));
lim = 0;
terminator = 1;
++p;
++c;
} else if (*p == '[') {
if (terminator)
return(spec_norm(sv, 0));
lim = 0;
terminator = 2;
++p;
++c;
} else if (*p == '(') {
if (terminator)
return(spec_norm(sv, 0));
lim = 2;
terminator = 3;
++p;
++c;
}
/* TODO: needs to handle floating point. */
if ( ! isdigit((u_char)*p))
return(spec_norm(sv, 0));
for (i = 0; isdigit((u_char)*p); i++) {
if (lim && i >= lim)
break;
++p;
++c;
}
if (terminator && terminator < 3) {
if (1 == terminator && *p != '\'')
return(spec_norm(sv, 0));
if (2 == terminator && *p != ']')
return(spec_norm(sv, 0));
++p;
++c;
}
return(spec_norm(sv, c));
case ('f'):
/* FALLTHROUGH */
case ('F'):
/* FALLTHROUGH */
case ('*'):
if ('\0' == *++p || isspace((u_char)*p))
return(spec_norm(sv, 0));
switch (*p) {
case ('('):
if ('\0' == *++p || isspace((u_char)*p))
return(spec_norm(sv, 0));
return(spec_norm(sv, 4));
case ('['):
for (c = 3, p++; *p && ']' != *p; p++, c++)
if (isspace((u_char)*p))
break;
return(spec_norm(sv, *p == ']' ? c : 0));
default:
break;
}
return(spec_norm(sv, 3));
case ('('):
if ('\0' == *++p || isspace((u_char)*p))
return(spec_norm(sv, 0));
if ('\0' == *++p || isspace((u_char)*p))
return(spec_norm(sv, 0));
return(spec_norm(sv, 4));
case ('['):
break;
default:
return(spec_norm(sv, 0));
}
for (c = 3, p++; *p && ']' != *p; p++, c++)
if (isspace((u_char)*p))
break;
return(spec_norm(sv, *p == ']' ? c : 0));
}
void *
mandoc_calloc(size_t num, size_t size)
{
void *ptr;
ptr = calloc(num, size);
if (NULL == ptr) {
perror(NULL);
exit(EXIT_FAILURE);
}
return(ptr);
}
void *
mandoc_malloc(size_t size)
{
void *ptr;
ptr = malloc(size);
if (NULL == ptr) {
perror(NULL);
exit(EXIT_FAILURE);
}
return(ptr);
}
void *
mandoc_realloc(void *ptr, size_t size)
{
ptr = realloc(ptr, size);
if (NULL == ptr) {
perror(NULL);
exit(EXIT_FAILURE);
}
return(ptr);
}
char *
mandoc_strdup(const char *ptr)
{
char *p;
p = strdup(ptr);
if (NULL == p) {
perror(NULL);
exit(EXIT_FAILURE);
}
return(p);
}
static int
a2time(time_t *t, const char *fmt, const char *p)
{
struct tm tm;
char *pp;
memset(&tm, 0, sizeof(struct tm));
pp = strptime(p, fmt, &tm);
if (NULL != pp && '\0' == *pp) {
*t = mktime(&tm);
return(1);
}
return(0);
}
/*
* Convert from a manual date string (see mdoc(7) and man(7)) into a
* date according to the stipulated date type.
*/
time_t
mandoc_a2time(int flags, const char *p)
{
time_t t;
if (MTIME_MDOCDATE & flags) {
if (0 == strcmp(p, "$" "Mdocdate$"))
return(time(NULL));
if (a2time(&t, "$" "Mdocdate: %b %d %Y $", p))
return(t);
}
if (MTIME_CANONICAL & flags || MTIME_REDUCED & flags)
if (a2time(&t, "%b %d, %Y", p))
return(t);
if (MTIME_ISO_8601 & flags)
if (a2time(&t, "%Y-%m-%d", p))
return(t);
if (MTIME_REDUCED & flags) {
if (a2time(&t, "%d, %Y", p))
return(t);
if (a2time(&t, "%Y", p))
return(t);
}
return(0);
}
int
mandoc_eos(const char *p, size_t sz)
{
if (0 == sz)
return(0);
/*
* End-of-sentence recognition must include situations where
* some symbols, such as `)', allow prior EOS punctuation to
* propogate outward.
*/
for ( ; sz; sz--) {
switch (p[(int)sz - 1]) {
case ('\"'):
/* FALLTHROUGH */
case ('\''):
/* FALLTHROUGH */
case (']'):
/* FALLTHROUGH */
case (')'):
break;
case ('.'):
/* Escaped periods. */
if (sz > 1 && '\\' == p[(int)sz - 2])
return(0);
/* FALLTHROUGH */
case ('!'):
/* FALLTHROUGH */
case ('?'):
return(1);
default:
return(0);
}
}
return(0);
}
int
mandoc_hyph(const char *start, const char *c)
{
/*
* Choose whether to break at a hyphenated character. We only
* do this if it's free-standing within a word.
*/
/* Skip first/last character of buffer. */
if (c == start || '\0' == *(c + 1))
return(0);
/* Skip first/last character of word. */
if ('\t' == *(c + 1) || '\t' == *(c - 1))
return(0);
if (' ' == *(c + 1) || ' ' == *(c - 1))
return(0);
/* Skip double invocations. */
if ('-' == *(c + 1) || '-' == *(c - 1))
return(0);
/* Skip escapes. */
if ('\\' == *(c - 1))
return(0);
return(1);
}

110
commands/mdocml/mandoc.h Normal file
View file

@ -0,0 +1,110 @@
/* $Id: mandoc.h,v 1.12 2010/06/12 11:41:50 kristaps Exp $ */
/*
* Copyright (c) 2010 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.
*/
#ifndef MANDOC_H
#define MANDOC_H
#define ASCII_NBRSP 31 /* non-breaking space */
#define ASCII_HYPH 30 /* breakable hyphen */
__BEGIN_DECLS
enum mandocerr {
MANDOCERR_OK,
MANDOCERR_UPPERCASE, /* text should be uppercase */
MANDOCERR_SECOOO, /* sections out of conventional order */
MANDOCERR_SECREP, /* section name repeats */
MANDOCERR_PROLOGOOO, /* out of order prologue */
MANDOCERR_PROLOGREP, /* repeated prologue entry */
MANDOCERR_LISTFIRST, /* list type must come first */
MANDOCERR_BADSTANDARD, /* bad standard */
MANDOCERR_BADLIB, /* bad library */
MANDOCERR_BADESCAPE, /* bad escape sequence */
MANDOCERR_BADQUOTE, /* unterminated quoted string */
MANDOCERR_NOWIDTHARG, /* argument requires the width argument */
/* FIXME: merge with MANDOCERR_IGNARGV. */
MANDOCERR_WIDTHARG, /* superfluous width argument */
MANDOCERR_IGNARGV, /* macro ignoring argv */
MANDOCERR_BADDATE, /* bad date argument */
MANDOCERR_BADWIDTH, /* bad width argument */
MANDOCERR_BADMSEC, /* unknown manual section */
MANDOCERR_SECMSEC, /* section not in conventional manual section */
MANDOCERR_EOLNSPACE, /* end of line whitespace */
MANDOCERR_SCOPEEXIT, /* scope open on exit */
#define MANDOCERR_WARNING MANDOCERR_SCOPEEXIT
MANDOCERR_NAMESECFIRST, /* NAME section must come first */
MANDOCERR_BADBOOL, /* bad Boolean value */
MANDOCERR_CHILD, /* child violates parent syntax */
MANDOCERR_BADATT, /* bad AT&T symbol */
MANDOCERR_LISTREP, /* list type repeated */
MANDOCERR_DISPREP, /* display type repeated */
MANDOCERR_ARGVREP, /* argument repeated */
MANDOCERR_NONAME, /* manual name not yet set */
MANDOCERR_MACROOBS, /* obsolete macro ignored */
MANDOCERR_MACROEMPTY, /* empty macro ignored */
MANDOCERR_BADBODY, /* macro not allowed in body */
MANDOCERR_BADPROLOG, /* macro not allowed in prologue */
MANDOCERR_BADCHAR, /* bad character */
MANDOCERR_BADNAMESEC, /* bad NAME section contents */
MANDOCERR_NOBLANKLN, /* no blank lines */
MANDOCERR_NOTEXT, /* no text in this context */
MANDOCERR_BADCOMMENT, /* bad comment style */
MANDOCERR_MACRO, /* unknown macro will be lost */
MANDOCERR_LINESCOPE, /* line scope broken */
MANDOCERR_SCOPE, /* scope broken */
MANDOCERR_ARGCOUNT, /* argument count wrong */
MANDOCERR_NOSCOPE, /* request scope close w/none open */
MANDOCERR_SCOPEREP, /* scope already open */
/* FIXME: merge following with MANDOCERR_ARGCOUNT */
MANDOCERR_NOARGS, /* macro requires line argument(s) */
MANDOCERR_NOBODY, /* macro requires body argument(s) */
MANDOCERR_NOARGV, /* macro requires argument(s) */
MANDOCERR_NOTITLE, /* no title in document */
MANDOCERR_LISTTYPE, /* missing list type */
MANDOCERR_DISPTYPE, /* missing display type */
MANDOCERR_ARGSLOST, /* line argument(s) will be lost */
MANDOCERR_BODYLOST, /* body argument(s) will be lost */
#define MANDOCERR_ERROR MANDOCERR_BODYLOST
MANDOCERR_COLUMNS, /* column syntax is inconsistent */
/* FIXME: this should be a MANDOCERR_ERROR */
MANDOCERR_FONTTYPE, /* missing font type */
/* FIXME: this should be a MANDOCERR_ERROR */
MANDOCERR_NESTEDDISP, /* displays may not be nested */
MANDOCERR_BADDISP, /* unsupported display type */
MANDOCERR_SYNTNOSCOPE, /* request scope close w/none open */
MANDOCERR_SYNTSCOPE, /* scope broken, syntax violated */
MANDOCERR_SYNTLINESCOPE, /* line scope broken, syntax violated */
MANDOCERR_SYNTARGVCOUNT, /* argument count wrong, violates syntax */
MANDOCERR_SYNTCHILD, /* child violates parent syntax */
MANDOCERR_SYNTARGCOUNT, /* argument count wrong, violates syntax */
MANDOCERR_NODOCBODY, /* no document body */
MANDOCERR_NODOCPROLOG, /* no document prologue */
MANDOCERR_UTSNAME, /* utsname() system call failed */
MANDOCERR_MEM, /* memory exhausted */
#define MANDOCERR_FATAL MANDOCERR_MEM
MANDOCERR_MAX
};
typedef int (*mandocmsg)(enum mandocerr,
void *, int, int, const char *);
__END_DECLS
#endif /*!MANDOC_H*/

View file

@ -0,0 +1,559 @@
.\" $Id: mandoc_char.7,v 1.39 2010/05/12 08:29:23 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.
.\"
.Dd $Mdocdate: May 12 2010 $
.Dt MANDOC_CHAR 7
.Os
.Sh NAME
.Nm mandoc_char
.Nd mandoc special characters
.Sh DESCRIPTION
This page documents the special characters and predefined strings accepted by
.Xr mandoc 1
to format
.Xr mdoc 7
and
.Xr man 7
documents.
.Pp
Both
.Xr mdoc 7
and
.Xr man 7
encode special characters with
.Sq \eX
.Pq for a one-character escape ,
.Sq \e(XX
.Pq two-character ,
and
.Sq \e[N]
.Pq N-character .
One may generalise
.Sq \e(XX
as
.Sq \e[XX]
and
.Sq \eX
as
.Sq \e[X] .
Predefined strings are functionally similar to special characters, using
.Sq \e*X
.Pq for a one-character escape ,
.Sq \e*(XX
.Pq two-character ,
and
.Sq \e*[N]
.Pq N-character .
One may generalise
.Sq \e*(XX
as
.Sq \e*[XX]
and
.Sq \e*X
as
.Sq \e*[X] .
.Pp
Note that each output mode will have a different rendering of the
characters.
It's guaranteed that each input symbol will correspond to a
(more or less) meaningful output rendering, regardless the mode.
.Sh SPECIAL CHARACTERS
These are the preferred input symbols for producing special characters.
.Pp
Spacing:
.Bl -column -compact -offset indent "Input" "Description"
.It Em Input Ta Em Description
.It \e~ Ta non-breaking, non-collapsing space
.It \e Ta breaking, non-collapsing n-width space
.It \e^ Ta zero-width space
.It \e% Ta zero-width space
.It \e& Ta zero-width space
.It \e| Ta zero-width space
.It \e0 Ta breaking, non-collapsing digit-width space
.It \ec Ta removes any trailing space (if applicable)
.El
.Pp
Lines:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(ba Ta \(ba Ta bar
.It \e(br Ta \(br Ta box rule
.It \e(ul Ta \(ul Ta underscore
.It \e(rl Ta \(rl Ta overline
.It \e(bb Ta \(bb Ta broken bar
.It \e(sl Ta \(sl Ta forward slash
.It \e(rs Ta \(rs Ta backward slash
.El
.Pp
Text markers:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(ci Ta \(ci Ta circle
.It \e(bu Ta \(bu Ta bullet
.It \e(dd Ta \(dd Ta double dagger
.It \e(dg Ta \(dg Ta dagger
.It \e(lz Ta \(lz Ta lozenge
.It \e(sq Ta \(sq Ta white square
.It \e(ps Ta \(ps Ta paragraph
.It \e(sc Ta \(sc Ta section
.It \e(lh Ta \(lh Ta left hand
.It \e(rh Ta \(rh Ta right hand
.It \e(at Ta \(at Ta at
.It \e(sh Ta \(sh Ta hash (pound)
.It \e(CR Ta \(CR Ta carriage return
.It \e(OK Ta \(OK Ta check mark
.El
.Pp
Legal symbols:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(co Ta \(co Ta copyright
.It \e(rg Ta \(rg Ta registered
.It \e(tm Ta \(tm Ta trademarked
.El
.Pp
Punctuation:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(em Ta \(em Ta em-dash
.It \e(en Ta \(en Ta en-dash
.It \e(hy Ta \(hy Ta hyphen
.It \ee Ta \e Ta back-slash
.It \e. Ta \. Ta period
.It \e(r! Ta \(r! Ta upside-down exclamation
.It \e(r? Ta \(r? Ta upside-down question
.El
.Pp
Quotes:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(Bq Ta \(Bq Ta right low double-quote
.It \e(bq Ta \(bq Ta right low single-quote
.It \e(lq Ta \(lq Ta left double-quote
.It \e(rq Ta \(rq Ta right double-quote
.It \e(oq Ta \(oq Ta left single-quote
.It \e(cq Ta \(cq Ta right single-quote
.It \e(aq Ta \(aq Ta apostrophe quote (text)
.It \e(dq Ta \(dq Ta double quote (text)
.It \e(Fo Ta \(Fo Ta left guillemet
.It \e(Fc Ta \(Fc Ta right guillemet
.It \e(fo Ta \(fo Ta left single guillemet
.It \e(fc Ta \(fc Ta right single guillemet
.El
.Pp
Brackets:
.Bl -column -compact -offset indent "xxbracketrightbpx" Rendered Description
.It Em Input Ta Em Rendered Ta Em Description
.It \e(lB Ta \(lB Ta left bracket
.It \e(rB Ta \(rB Ta right bracket
.It \e(lC Ta \(lC Ta left brace
.It \e(rC Ta \(rC Ta right brace
.It \e(la Ta \(la Ta left angle
.It \e(ra Ta \(ra Ta right angle
.It \e(bv Ta \(bv Ta brace extension
.It \e[braceex] Ta \[braceex] Ta brace extension
.It \e[bracketlefttp] Ta \[bracketlefttp] Ta top-left hooked bracket
.It \e[bracketleftbp] Ta \[bracketleftbp] Ta bottom-left hooked bracket
.It \e[bracketleftex] Ta \[bracketleftex] Ta left hooked bracket extension
.It \e[bracketrighttp] Ta \[bracketrighttp] Ta top-right hooked bracket
.It \e[bracketrightbp] Ta \[bracketrightbp] Ta bottom-right hooked bracket
.It \e[bracketrightex] Ta \[bracketrightex] Ta right hooked bracket extension
.It \e(lt Ta \(lt Ta top-left hooked brace
.It \e[bracelefttp] Ta \[bracelefttp] Ta top-left hooked brace
.It \e(lk Ta \(lk Ta mid-left hooked brace
.It \e[braceleftmid] Ta \[braceleftmid] Ta mid-left hooked brace
.It \e(lb Ta \(lb Ta bottom-left hooked brace
.It \e[braceleftbp] Ta \[braceleftbp] Ta bottom-left hooked brace
.It \e[braceleftex] Ta \[braceleftex] Ta left hooked brace extension
.It \e(rt Ta \(rt Ta top-left hooked brace
.It \e[bracerighttp] Ta \[bracerighttp] Ta top-right hooked brace
.It \e(rk Ta \(rk Ta mid-right hooked brace
.It \e[bracerightmid] Ta \[bracerightmid] Ta mid-right hooked brace
.It \e(rb Ta \(rb Ta bottom-right hooked brace
.It \e[bracerightbp] Ta \[bracerightbp] Ta bottom-right hooked brace
.It \e[bracerightex] Ta \[bracerightex] Ta right hooked brace extension
.It \e[parenlefttp] Ta \[parenlefttp] Ta top-left hooked parenthesis
.It \e[parenleftbp] Ta \[parenleftbp] Ta bottom-left hooked parenthesis
.It \e[parenleftex] Ta \[parenleftex] Ta left hooked parenthesis extension
.It \e[parenrighttp] Ta \[parenrighttp] Ta top-right hooked parenthesis
.It \e[parenrightbp] Ta \[parenrightbp] Ta bottom-right hooked parenthesis
.It \e[parenrightex] Ta \[parenrightex] Ta right hooked parenthesis extension
.El
.Pp
Arrows:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(<- Ta \(<- Ta left arrow
.It \e(-> Ta \(-> Ta right arrow
.It \e(<> Ta \(<> Ta left-right arrow
.It \e(da Ta \(da Ta down arrow
.It \e(ua Ta \(ua Ta up arrow
.It \e(va Ta \(va Ta up-down arrow
.It \e(lA Ta \(lA Ta left double-arrow
.It \e(rA Ta \(rA Ta right double-arrow
.It \e(hA Ta \(hA Ta left-right double-arrow
.It \e(uA Ta \(uA Ta up double-arrow
.It \e(dA Ta \(dA Ta down double-arrow
.It \e(vA Ta \(vA Ta up-down double-arrow
.El
.Pp
Logical:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(AN Ta \(AN Ta logical and
.It \e(OR Ta \(OR Ta logical or
.It \e(no Ta \(no Ta logical not
.It \e[tno] Ta \[tno] Ta logical not (text)
.It \e(te Ta \(te Ta existential quantifier
.It \e(fa Ta \(fa Ta universal quantifier
.It \e(st Ta \(st Ta such that
.It \e(tf Ta \(tf Ta therefore
.It \e(3d Ta \(3d Ta therefore
.It \e(or Ta \(or Ta bitwise or
.El
.Pp
Mathematical:
.Bl -column -compact -offset indent "xxcoproductxx" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(pl Ta \(pl Ta plus
.It \e(mi Ta \(mi Ta minus
.It \e- Ta \- Ta minus (text)
.It \e(-+ Ta \(-+ Ta minus-plus
.It \e(+- Ta \(+- Ta plus-minus
.It \e[t+-] Ta \[t+-] Ta plus-minus (text)
.It \e(pc Ta \(pc Ta centre-dot
.It \e(mu Ta \(mu Ta multiply
.It \e[tmu] Ta \[tmu] Ta multiply (text)
.It \e(c* Ta \(c* Ta circle-multiply
.It \e(c+ Ta \(c+ Ta circle-plus
.It \e(di Ta \(di Ta divide
.It \e[tdi] Ta \[tdi] Ta divide (text)
.It \e(f/ Ta \(f/ Ta fraction
.It \e(** Ta \(** Ta asterisk
.It \e(<= Ta \(<= Ta less-than-equal
.It \e(>= Ta \(>= Ta greater-than-equal
.It \e(<< Ta \(<< Ta much less
.It \e(>> Ta \(>> Ta much greater
.It \e(eq Ta \(eq Ta equal
.It \e(!= Ta \(!= Ta not equal
.It \e(== Ta \(== Ta equivalent
.It \e(ne Ta \(ne Ta not equivalent
.It \e(=~ Ta \(=~ Ta congruent
.It \e(-~ Ta \(-~ Ta asymptotically congruent
.It \e(ap Ta \(ap Ta asymptotically similar
.It \e(~~ Ta \(~~ Ta approximately similar
.It \e(~= Ta \(~= Ta approximately equal
.It \e(pt Ta \(pt Ta proportionate
.It \e(es Ta \(es Ta empty set
.It \e(mo Ta \(mo Ta element
.It \e(nm Ta \(nm Ta not element
.It \e(sb Ta \(sb Ta proper subset
.It \e(nb Ta \(nb Ta not subset
.It \e(sp Ta \(sp Ta proper superset
.It \e(nc Ta \(nc Ta not superset
.It \e(ib Ta \(ib Ta reflexive subset
.It \e(ip Ta \(ip Ta reflexive superset
.It \e(ca Ta \(ca Ta intersection
.It \e(cu Ta \(cu Ta union
.It \e(/_ Ta \(/_ Ta angle
.It \e(pp Ta \(pp Ta perpendicular
.It \e(is Ta \(is Ta integral
.It \e[integral] Ta \[integral] Ta integral
.It \e[sum] Ta \[sum] Ta summation
.It \e[product] Ta \[product] Ta product
.It \e[coproduct] Ta \[coproduct] Ta coproduct
.It \e(gr Ta \(gr Ta gradient
.It \e(sr Ta \(sr Ta square root
.It \e[sqrt] Ta \[sqrt] Ta square root
.It \e(lc Ta \(lc Ta left-ceiling
.It \e(rc Ta \(rc Ta right-ceiling
.It \e(lf Ta \(lf Ta left-floor
.It \e(rf Ta \(rf Ta right-floor
.It \e(if Ta \(if Ta infinity
.It \e(Ah Ta \(Ah Ta aleph
.It \e(Im Ta \(Im Ta imaginary
.It \e(Re Ta \(Re Ta real
.It \e(pd Ta \(pd Ta partial differential
.It \e(-h Ta \(-h Ta Planck constant over 2\(*p
.El
.Pp
Ligatures:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(ff Ta \(ff Ta ff ligature
.It \e(fi Ta \(fi Ta fi ligature
.It \e(fl Ta \(fl Ta fl ligature
.It \e(Fi Ta \(Fi Ta ffi ligature
.It \e(Fl Ta \(Fl Ta ffl ligature
.It \e(AE Ta \(AE Ta AE
.It \e(ae Ta \(ae Ta ae
.It \e(OE Ta \(OE Ta OE
.It \e(oe Ta \(oe Ta oe
.It \e(ss Ta \(ss Ta German eszett
.It \e(IJ Ta \(IJ Ta IJ ligature
.It \e(ij Ta \(ij Ta ij ligature
.El
.Pp
Accents:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(a" Ta \(a" Ta Hungarian umlaut
.It \e(a- Ta \(a- Ta macron
.It \e(a. Ta \(a. Ta dotted
.It \e(a^ Ta \(a^ Ta circumflex
.It \e(aa Ta \(aa Ta acute
.It \e' Ta \' Ta acute
.It \e(ga Ta \(ga Ta grave
.It \e` Ta \` Ta grave
.It \e(ab Ta \(ab Ta breve
.It \e(ac Ta \(ac Ta cedilla
.It \e(ad Ta \(ad Ta dieresis
.It \e(ah Ta \(ah Ta caron
.It \e(ao Ta \(ao Ta ring
.It \e(a~ Ta \(a~ Ta tilde
.It \e(ho Ta \(ho Ta ogonek
.It \e(ha Ta \(ha Ta hat (text)
.It \e(ti Ta \(ti Ta tilde (text)
.El
.Pp
Accented letters:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e('A Ta \('A Ta acute A
.It \e('E Ta \('E Ta acute E
.It \e('I Ta \('I Ta acute I
.It \e('O Ta \('O Ta acute O
.It \e('U Ta \('U Ta acute U
.It \e('a Ta \('a Ta acute a
.It \e('e Ta \('e Ta acute e
.It \e('i Ta \('i Ta acute i
.It \e('o Ta \('o Ta acute o
.It \e('u Ta \('u Ta acute u
.It \e(`A Ta \(`A Ta grave A
.It \e(`E Ta \(`E Ta grave E
.It \e(`I Ta \(`I Ta grave I
.It \e(`O Ta \(`O Ta grave O
.It \e(`U Ta \(`U Ta grave U
.It \e(`a Ta \(`a Ta grave a
.It \e(`e Ta \(`e Ta grave e
.It \e(`i Ta \(`i Ta grave i
.It \e(`o Ta \(`i Ta grave o
.It \e(`u Ta \(`u Ta grave u
.It \e(~A Ta \(~A Ta tilde A
.It \e(~N Ta \(~N Ta tilde N
.It \e(~O Ta \(~O Ta tilde O
.It \e(~a Ta \(~a Ta tilde a
.It \e(~n Ta \(~n Ta tilde n
.It \e(~o Ta \(~o Ta tilde o
.It \e(:A Ta \(:A Ta dieresis A
.It \e(:E Ta \(:E Ta dieresis E
.It \e(:I Ta \(:I Ta dieresis I
.It \e(:O Ta \(:O Ta dieresis O
.It \e(:U Ta \(:U Ta dieresis U
.It \e(:a Ta \(:a Ta dieresis a
.It \e(:e Ta \(:e Ta dieresis e
.It \e(:i Ta \(:i Ta dieresis i
.It \e(:o Ta \(:o Ta dieresis o
.It \e(:u Ta \(:u Ta dieresis u
.It \e(:y Ta \(:y Ta dieresis y
.It \e(^A Ta \(^A Ta circumflex A
.It \e(^E Ta \(^E Ta circumflex E
.It \e(^I Ta \(^I Ta circumflex I
.It \e(^O Ta \(^O Ta circumflex O
.It \e(^U Ta \(^U Ta circumflex U
.It \e(^a Ta \(^a Ta circumflex a
.It \e(^e Ta \(^e Ta circumflex e
.It \e(^i Ta \(^i Ta circumflex i
.It \e(^o Ta \(^o Ta circumflex o
.It \e(^u Ta \(^u Ta circumflex u
.It \e(,C Ta \(,C Ta cedilla C
.It \e(,c Ta \(,c Ta cedilla c
.It \e(/L Ta \(/L Ta stroke L
.It \e(/l Ta \(/l Ta stroke l
.It \e(/O Ta \(/O Ta stroke O
.It \e(/o Ta \(/o Ta stroke o
.It \e(oA Ta \(oA Ta ring A
.It \e(oa Ta \(oa Ta ring a
.El
.Pp
Special letters:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(-D Ta \(-D Ta Eth
.It \e(Sd Ta \(Sd Ta eth
.It \e(TP Ta \(TP Ta Thorn
.It \e(Tp Ta \(Tp Ta thorn
.It \e(.i Ta \(.i Ta dotless i
.It \e(.j Ta \(.j Ta dotless j
.El
.Pp
Currency:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(Do Ta \(Do Ta dollar
.It \e(ct Ta \(ct Ta cent
.It \e(Eu Ta \(Eu Ta Euro symbol
.It \e(eu Ta \(eu Ta Euro symbol
.It \e(Ye Ta \(Ye Ta yen
.It \e(Po Ta \(Po Ta pound
.It \e(Cs Ta \(Cs Ta Scandinavian
.It \e(Fn Ta \(Fn Ta florin
.El
.Pp
Units:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(de Ta \(de Ta degree
.It \e(%0 Ta \(%0 Ta per-thousand
.It \e(fm Ta \(fm Ta minute
.It \e(sd Ta \(sd Ta second
.It \e(mc Ta \(mc Ta micro
.El
.Pp
Greek letters:
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e(*A Ta \(*A Ta Alpha
.It \e(*B Ta \(*B Ta Beta
.It \e(*G Ta \(*G Ta Gamma
.It \e(*D Ta \(*D Ta Delta
.It \e(*E Ta \(*E Ta Epsilon
.It \e(*Z Ta \(*Z Ta Zeta
.It \e(*Y Ta \(*Y Ta Eta
.It \e(*H Ta \(*H Ta Theta
.It \e(*I Ta \(*I Ta Iota
.It \e(*K Ta \(*K Ta Kappa
.It \e(*L Ta \(*L Ta Lambda
.It \e(*M Ta \(*M Ta Mu
.It \e(*N Ta \(*N Ta Nu
.It \e(*C Ta \(*C Ta Xi
.It \e(*O Ta \(*O Ta Omicron
.It \e(*P Ta \(*P Ta Pi
.It \e(*R Ta \(*R Ta Rho
.It \e(*S Ta \(*S Ta Sigma
.It \e(*T Ta \(*T Ta Tau
.It \e(*U Ta \(*U Ta Upsilon
.It \e(*F Ta \(*F Ta Phi
.It \e(*X Ta \(*X Ta Chi
.It \e(*Q Ta \(*Q Ta Psi
.It \e(*W Ta \(*W Ta Omega
.It \e(*a Ta \(*a Ta alpha
.It \e(*b Ta \(*b Ta beta
.It \e(*g Ta \(*g Ta gamma
.It \e(*d Ta \(*d Ta delta
.It \e(*e Ta \(*e Ta epsilon
.It \e(*z Ta \(*z Ta zeta
.It \e(*y Ta \(*y Ta eta
.It \e(*h Ta \(*h Ta theta
.It \e(*i Ta \(*i Ta iota
.It \e(*k Ta \(*k Ta kappa
.It \e(*l Ta \(*l Ta lambda
.It \e(*m Ta \(*m Ta mu
.It \e(*n Ta \(*n Ta nu
.It \e(*c Ta \(*c Ta xi
.It \e(*o Ta \(*o Ta omicron
.It \e(*p Ta \(*p Ta pi
.It \e(*r Ta \(*r Ta rho
.It \e(*s Ta \(*s Ta sigma
.It \e(*t Ta \(*t Ta tau
.It \e(*u Ta \(*u Ta upsilon
.It \e(*f Ta \(*f Ta phi
.It \e(*x Ta \(*x Ta chi
.It \e(*q Ta \(*q Ta psi
.It \e(*w Ta \(*w Ta omega
.It \e(+h Ta \(+h Ta theta variant
.It \e(+f Ta \(+f Ta phi variant
.It \e(+p Ta \(+p Ta pi variant
.It \e(+e Ta \(+e Ta epsilon variant
.It \e(ts Ta \(ts Ta sigma terminal
.El
.Sh PREDEFINED STRINGS
These are not recommended for use, as they differ across
implementations:
.Pp
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
.It Em Input Ta Em Rendered Ta Em Description
.It \e*(Ba Ta \*(Ba Ta vertical bar
.It \e*(Ne Ta \*(Ne Ta not equal
.It \e*(Ge Ta \*(Ge Ta greater-than-equal
.It \e*(Le Ta \*(Le Ta less-than-equal
.It \e*(Gt Ta \*(Gt Ta greater-than
.It \e*(Lt Ta \*(Lt Ta less-than
.It \e*(Pm Ta \*(Pm Ta plus-minus
.It \e*(If Ta \*(If Ta infinity
.It \e*(Pi Ta \*(Pi Ta pi
.It \e*(Na Ta \*(Na Ta NaN
.It \e*(Am Ta \*(Am Ta ampersand
.It \e*R Ta \*R Ta restricted mark
.It \e*(Tm Ta \*(Tm Ta trade mark
.It \e*q Ta \*q Ta double-quote
.It \e*(Rq Ta \*(Rq Ta right-double-quote
.It \e*(Lq Ta \*(Lq Ta left-double-quote
.It \e*(lp Ta \*(lp Ta right-parenthesis
.It \e*(rp Ta \*(rp Ta left-parenthesis
.It \e*(lq Ta \*(lq Ta left double-quote
.It \e*(rq Ta \*(rq Ta right double-quote
.It \e*(ua Ta \*(ua Ta up arrow
.It \e*(va Ta \*(va Ta up-down arrow
.It \e*(<= Ta \*(<= Ta less-than-equal
.It \e*(>= Ta \*(>= Ta greater-than-equal
.It \e*(aa Ta \*(aa Ta acute
.It \e*(ga Ta \*(ga Ta grave
.El
.Sh COMPATIBILITY
This section documents compatibility of
.Nm
with older or existing versions of
.Xr groff 1 .
.Pp
The following render differently in
.Fl T Ns Ar ascii
output mode:
.Bd -ragged -offset indent
\e(ss, \e(nm, \e(nb, \e(nc, \e(ib, \e(ip, \e(pp, \e[sum], \e[product],
\e[coproduct], \e(gr, \e(-h, \e(a.
.Ed
.Pp
The following render differently in
.Fl T Ns Ar html
output mode:
.Bd -ragged -offset indent
\e(~=, \e(nb, \e(nc
.Ed
.Pp
Finally, the following have been omitted by being poorly documented or
having no known representation:
.Bd -ragged -offset indent
\e[radicalex], \e[sqrtex], \e(ru
.Ed
.Sh SEE ALSO
.Xr mandoc 1
.Sh AUTHORS
The
.Nm
manual page was written by
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
.Sh CAVEATS
The
.Sq \e*(Ba
escape mimics the behaviour of the
.Sq \&|
character in
.Xr mdoc 7 ;
thus, if you wish to render a vertical bar with no side effects, use
the
.Sq \e(ba
escape.

290
commands/mdocml/mdoc.3 Normal file
View file

@ -0,0 +1,290 @@
.\" $Id: mdoc.3,v 1.41 2010/05/30 22:56:02 kristaps Exp $
.\"
.\" Copyright (c) 2009-2010 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.
.\"
.Dd $Mdocdate: May 30 2010 $
.Dt MDOC 3
.Os
.Sh NAME
.Nm mdoc ,
.Nm mdoc_alloc ,
.Nm mdoc_endparse ,
.Nm mdoc_free ,
.Nm mdoc_meta ,
.Nm mdoc_node ,
.Nm mdoc_parseln ,
.Nm mdoc_reset
.Nd mdoc macro compiler library
.Sh SYNOPSIS
.In mandoc.h
.In mdoc.h
.Vt extern const char * const * mdoc_macronames;
.Vt extern const char * const * mdoc_argnames;
.Ft "struct mdoc *"
.Fn mdoc_alloc "void *data" "int pflags" "mandocmsg msgs"
.Ft int
.Fn mdoc_endparse "struct mdoc *mdoc"
.Ft void
.Fn mdoc_free "struct mdoc *mdoc"
.Ft "const struct mdoc_meta *"
.Fn mdoc_meta "const struct mdoc *mdoc"
.Ft "const struct mdoc_node *"
.Fn mdoc_node "const struct mdoc *mdoc"
.Ft int
.Fn mdoc_parseln "struct mdoc *mdoc" "int line" "char *buf"
.Ft int
.Fn mdoc_reset "struct mdoc *mdoc"
.Sh DESCRIPTION
The
.Nm mdoc
library parses lines of
.Xr mdoc 7
input
into an abstract syntax tree (AST).
.Pp
In general, applications initiate a parsing sequence with
.Fn mdoc_alloc ,
parse each line in a document with
.Fn mdoc_parseln ,
close the parsing session with
.Fn mdoc_endparse ,
operate over the syntax tree returned by
.Fn mdoc_node
and
.Fn mdoc_meta ,
then free all allocated memory with
.Fn mdoc_free .
The
.Fn mdoc_reset
function may be used in order to reset the parser for another input
sequence.
See the
.Sx EXAMPLES
section for a simple example.
.Pp
This section further defines the
.Sx Types ,
.Sx Functions
and
.Sx Variables
available to programmers.
Following that, the
.Sx Abstract Syntax Tree
section documents the output tree.
.Ss Types
Both functions (see
.Sx Functions )
and variables (see
.Sx Variables )
may use the following types:
.Bl -ohang
.It Vt struct mdoc
An opaque type defined in
.Pa mdoc.c .
Its values are only used privately within the library.
.It Vt struct mdoc_node
A parsed node.
Defined in
.Pa mdoc.h .
See
.Sx Abstract Syntax Tree
for details.
.It Vt mandocmsg
A function callback type defined in
.Pa mandoc.h .
.El
.Ss Functions
Function descriptions follow:
.Bl -ohang
.It Fn mdoc_alloc
Allocates a parsing structure.
The
.Fa data
pointer is passed to
.Fa msgs .
The
.Fa pflags
arguments are defined in
.Pa mdoc.h .
Returns NULL on failure.
If non-NULL, the pointer must be freed with
.Fn mdoc_free .
.It Fn mdoc_reset
Reset the parser for another parse routine.
After its use,
.Fn mdoc_parseln
behaves as if invoked for the first time.
If it returns 0, memory could not be allocated.
.It Fn mdoc_free
Free all resources of a parser.
The pointer is no longer valid after invocation.
.It Fn mdoc_parseln
Parse a nil-terminated line of input.
This line should not contain the trailing newline.
Returns 0 on failure, 1 on success.
The input buffer
.Fa buf
is modified by this function.
.It Fn mdoc_endparse
Signals that the parse is complete.
Note that if
.Fn mdoc_endparse
is called subsequent to
.Fn mdoc_node ,
the resulting tree is incomplete.
Returns 0 on failure, 1 on success.
.It Fn mdoc_node
Returns the first node of the parse.
Note that if
.Fn mdoc_parseln
or
.Fn mdoc_endparse
return 0, the tree will be incomplete.
.It Fn mdoc_meta
Returns the document's parsed meta-data.
If this information has not yet been supplied or
.Fn mdoc_parseln
or
.Fn mdoc_endparse
return 0, the data will be incomplete.
.El
.Ss Variables
The following variables are also defined:
.Bl -ohang
.It Va mdoc_macronames
An array of string-ified token names.
.It Va mdoc_argnames
An array of string-ified token argument names.
.El
.Ss Abstract Syntax Tree
The
.Nm
functions produce an abstract syntax tree (AST) describing input in a
regular form.
It may be reviewed at any time with
.Fn mdoc_nodes ;
however, if called before
.Fn mdoc_endparse ,
or after
.Fn mdoc_endparse
or
.Fn mdoc_parseln
fail, it may be incomplete.
.Pp
This AST is governed by the ontological
rules dictated in
.Xr mdoc 7
and derives its terminology accordingly.
.Qq In-line
elements described in
.Xr mdoc 7
are described simply as
.Qq elements .
.Pp
The AST is composed of
.Vt struct mdoc_node
nodes with block, head, body, element, root and text types as declared
by the
.Va type
field.
Each node also provides its parse point (the
.Va line ,
.Va sec ,
and
.Va pos
fields), its position in the tree (the
.Va parent ,
.Va child ,
.Va next
and
.Va prev
fields) and some type-specific data.
.Pp
The tree itself is arranged according to the following normal form,
where capitalised non-terminals represent nodes.
.Pp
.Bl -tag -width "ELEMENTXX" -compact
.It ROOT
\(<- mnode+
.It mnode
\(<- BLOCK | ELEMENT | TEXT
.It BLOCK
\(<- HEAD [TEXT] (BODY [TEXT])+ [TAIL [TEXT]]
.It ELEMENT
\(<- TEXT*
.It HEAD
\(<- mnode+
.It BODY
\(<- mnode+
.It TAIL
\(<- mnode+
.It TEXT
\(<- [[:printable:],0x1e]*
.El
.Pp
Of note are the TEXT nodes following the HEAD, BODY and TAIL nodes of
the BLOCK production: these refer to punctuation marks.
Furthermore, although a TEXT node will generally have a non-zero-length
string, in the specific case of
.Sq \&.Bd \-literal ,
an empty line will produce a zero-length string.
Multiple body parts are only found in invocations of
.Sq \&Bl \-column ,
where a new body introduces a new phrase.
.Sh EXAMPLES
The following example reads lines from stdin and parses them, operating
on the finished parse tree with
.Fn parsed .
This example does not error-check nor free memory upon failure.
.Bd -literal -offset indent
struct mdoc *mdoc;
const struct mdoc_node *node;
char *buf;
size_t len;
int line;
line = 1;
mdoc = mdoc_alloc(NULL, 0, NULL);
buf = NULL;
alloc_len = 0;
while ((len = getline(&buf, &alloc_len, stdin)) >= 0) {
if (len && buflen[len - 1] = '\en')
buf[len - 1] = '\e0';
if ( ! mdoc_parseln(mdoc, line, buf))
errx(1, "mdoc_parseln");
line++;
}
if ( ! mdoc_endparse(mdoc))
errx(1, "mdoc_endparse");
if (NULL == (node = mdoc_node(mdoc)))
errx(1, "mdoc_node");
parsed(mdoc, node);
mdoc_free(mdoc);
.Ed
.Pp
Please see
.Pa main.c
in the source archive for a rigorous reference.
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr mdoc 7
.Sh AUTHORS
The
.Nm
library was written by
.An Kristaps Dzonsons Aq kristaps@bsd.lv .

2375
commands/mdocml/mdoc.7 Normal file

File diff suppressed because it is too large Load diff

822
commands/mdocml/mdoc.c Normal file
View file

@ -0,0 +1,822 @@
/* $Id: mdoc.c,v 1.146 2010/06/12 11:58:22 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmdoc.h"
#include "libmandoc.h"
const char *const __mdoc_macronames[MDOC_MAX] = {
"Ap", "Dd", "Dt", "Os",
"Sh", "Ss", "Pp", "D1",
"Dl", "Bd", "Ed", "Bl",
"El", "It", "Ad", "An",
"Ar", "Cd", "Cm", "Dv",
"Er", "Ev", "Ex", "Fa",
"Fd", "Fl", "Fn", "Ft",
"Ic", "In", "Li", "Nd",
"Nm", "Op", "Ot", "Pa",
"Rv", "St", "Va", "Vt",
/* LINTED */
"Xr", "%A", "%B", "%D",
/* LINTED */
"%I", "%J", "%N", "%O",
/* LINTED */
"%P", "%R", "%T", "%V",
"Ac", "Ao", "Aq", "At",
"Bc", "Bf", "Bo", "Bq",
"Bsx", "Bx", "Db", "Dc",
"Do", "Dq", "Ec", "Ef",
"Em", "Eo", "Fx", "Ms",
"No", "Ns", "Nx", "Ox",
"Pc", "Pf", "Po", "Pq",
"Qc", "Ql", "Qo", "Qq",
"Re", "Rs", "Sc", "So",
"Sq", "Sm", "Sx", "Sy",
"Tn", "Ux", "Xc", "Xo",
"Fo", "Fc", "Oo", "Oc",
"Bk", "Ek", "Bt", "Hf",
"Fr", "Ud", "Lb", "Lp",
"Lk", "Mt", "Brq", "Bro",
/* LINTED */
"Brc", "%C", "Es", "En",
/* LINTED */
"Dx", "%Q", "br", "sp",
/* LINTED */
"%U", "Ta"
};
const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
"split", "nosplit", "ragged",
"unfilled", "literal", "file",
"offset", "bullet", "dash",
"hyphen", "item", "enum",
"tag", "diag", "hang",
"ohang", "inset", "column",
"width", "compact", "std",
"filled", "words", "emphasis",
"symbolic", "nested", "centered"
};
const char * const *mdoc_macronames = __mdoc_macronames;
const char * const *mdoc_argnames = __mdoc_argnames;
static void mdoc_node_free(struct mdoc_node *);
static void mdoc_node_unlink(struct mdoc *,
struct mdoc_node *);
static void mdoc_free1(struct mdoc *);
static void mdoc_alloc1(struct mdoc *);
static struct mdoc_node *node_alloc(struct mdoc *, int, int,
enum mdoct, enum mdoc_type);
static int node_append(struct mdoc *,
struct mdoc_node *);
static int mdoc_ptext(struct mdoc *, int, char *, int);
static int mdoc_pmacro(struct mdoc *, int, char *, int);
static int macrowarn(struct mdoc *, int,
const char *, int);
const struct mdoc_node *
mdoc_node(const struct mdoc *m)
{
return(MDOC_HALT & m->flags ? NULL : m->first);
}
const struct mdoc_meta *
mdoc_meta(const struct mdoc *m)
{
return(MDOC_HALT & m->flags ? NULL : &m->meta);
}
/*
* Frees volatile resources (parse tree, meta-data, fields).
*/
static void
mdoc_free1(struct mdoc *mdoc)
{
if (mdoc->first)
mdoc_node_delete(mdoc, mdoc->first);
if (mdoc->meta.title)
free(mdoc->meta.title);
if (mdoc->meta.os)
free(mdoc->meta.os);
if (mdoc->meta.name)
free(mdoc->meta.name);
if (mdoc->meta.arch)
free(mdoc->meta.arch);
if (mdoc->meta.vol)
free(mdoc->meta.vol);
if (mdoc->meta.msec)
free(mdoc->meta.msec);
}
/*
* Allocate all volatile resources (parse tree, meta-data, fields).
*/
static void
mdoc_alloc1(struct mdoc *mdoc)
{
memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
mdoc->flags = 0;
mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
mdoc->first = mdoc->last;
mdoc->last->type = MDOC_ROOT;
mdoc->next = MDOC_NEXT_CHILD;
}
/*
* Free up volatile resources (see mdoc_free1()) then re-initialises the
* data with mdoc_alloc1(). After invocation, parse data has been reset
* and the parser is ready for re-invocation on a new tree; however,
* cross-parse non-volatile data is kept intact.
*/
void
mdoc_reset(struct mdoc *mdoc)
{
mdoc_free1(mdoc);
mdoc_alloc1(mdoc);
}
/*
* Completely free up all volatile and non-volatile parse resources.
* After invocation, the pointer is no longer usable.
*/
void
mdoc_free(struct mdoc *mdoc)
{
mdoc_free1(mdoc);
free(mdoc);
}
/*
* Allocate volatile and non-volatile parse resources.
*/
struct mdoc *
mdoc_alloc(void *data, int pflags, mandocmsg msg)
{
struct mdoc *p;
p = mandoc_calloc(1, sizeof(struct mdoc));
p->msg = msg;
p->data = data;
p->pflags = pflags;
mdoc_hash_init();
mdoc_alloc1(p);
return(p);
}
/*
* Climb back up the parse tree, validating open scopes. Mostly calls
* through to macro_end() in macro.c.
*/
int
mdoc_endparse(struct mdoc *m)
{
if (MDOC_HALT & m->flags)
return(0);
else if (mdoc_macroend(m))
return(1);
m->flags |= MDOC_HALT;
return(0);
}
/*
* Main parse routine. Parses a single line -- really just hands off to
* the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
*/
int
mdoc_parseln(struct mdoc *m, int ln, char *buf, int offs)
{
if (MDOC_HALT & m->flags)
return(0);
m->flags |= MDOC_NEWLINE;
return(('.' == buf[offs] || '\'' == buf[offs]) ?
mdoc_pmacro(m, ln, buf, offs) :
mdoc_ptext(m, ln, buf, offs));
}
int
mdoc_vmsg(struct mdoc *mdoc, enum mandocerr t,
int ln, int pos, const char *fmt, ...)
{
char buf[256];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
va_end(ap);
return((*mdoc->msg)(t, mdoc->data, ln, pos, buf));
}
int
mdoc_macro(struct mdoc *m, enum mdoct tok,
int ln, int pp, int *pos, char *buf)
{
assert(tok < MDOC_MAX);
/* If we're in the body, deny prologue calls. */
if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
MDOC_PBODY & m->flags)
return(mdoc_pmsg(m, ln, pp, MANDOCERR_BADBODY));
/* If we're in the prologue, deny "body" macros. */
if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
! (MDOC_PBODY & m->flags)) {
if ( ! mdoc_pmsg(m, ln, pp, MANDOCERR_BADPROLOG))
return(0);
if (NULL == m->meta.title)
m->meta.title = mandoc_strdup("UNKNOWN");
if (NULL == m->meta.vol)
m->meta.vol = mandoc_strdup("LOCAL");
if (NULL == m->meta.os)
m->meta.os = mandoc_strdup("LOCAL");
if (0 == m->meta.date)
m->meta.date = time(NULL);
m->flags |= MDOC_PBODY;
}
return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
}
static int
node_append(struct mdoc *mdoc, struct mdoc_node *p)
{
assert(mdoc->last);
assert(mdoc->first);
assert(MDOC_ROOT != p->type);
switch (mdoc->next) {
case (MDOC_NEXT_SIBLING):
mdoc->last->next = p;
p->prev = mdoc->last;
p->parent = mdoc->last->parent;
break;
case (MDOC_NEXT_CHILD):
mdoc->last->child = p;
p->parent = mdoc->last;
break;
default:
abort();
/* NOTREACHED */
}
p->parent->nchild++;
if ( ! mdoc_valid_pre(mdoc, p))
return(0);
if ( ! mdoc_action_pre(mdoc, p))
return(0);
switch (p->type) {
case (MDOC_HEAD):
assert(MDOC_BLOCK == p->parent->type);
p->parent->head = p;
break;
case (MDOC_TAIL):
assert(MDOC_BLOCK == p->parent->type);
p->parent->tail = p;
break;
case (MDOC_BODY):
assert(MDOC_BLOCK == p->parent->type);
p->parent->body = p;
break;
default:
break;
}
mdoc->last = p;
switch (p->type) {
case (MDOC_TEXT):
if ( ! mdoc_valid_post(mdoc))
return(0);
if ( ! mdoc_action_post(mdoc))
return(0);
break;
default:
break;
}
return(1);
}
static struct mdoc_node *
node_alloc(struct mdoc *m, int line, int pos,
enum mdoct tok, enum mdoc_type type)
{
struct mdoc_node *p;
p = mandoc_calloc(1, sizeof(struct mdoc_node));
p->sec = m->lastsec;
p->line = line;
p->pos = pos;
p->tok = tok;
p->type = type;
if (MDOC_NEWLINE & m->flags)
p->flags |= MDOC_LINE;
m->flags &= ~MDOC_NEWLINE;
return(p);
}
int
mdoc_tail_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
{
struct mdoc_node *p;
p = node_alloc(m, line, pos, tok, MDOC_TAIL);
if ( ! node_append(m, p))
return(0);
m->next = MDOC_NEXT_CHILD;
return(1);
}
int
mdoc_head_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
{
struct mdoc_node *p;
assert(m->first);
assert(m->last);
p = node_alloc(m, line, pos, tok, MDOC_HEAD);
if ( ! node_append(m, p))
return(0);
m->next = MDOC_NEXT_CHILD;
return(1);
}
int
mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
{
struct mdoc_node *p;
p = node_alloc(m, line, pos, tok, MDOC_BODY);
if ( ! node_append(m, p))
return(0);
m->next = MDOC_NEXT_CHILD;
return(1);
}
int
mdoc_block_alloc(struct mdoc *m, int line, int pos,
enum mdoct tok, struct mdoc_arg *args)
{
struct mdoc_node *p;
p = node_alloc(m, line, pos, tok, MDOC_BLOCK);
p->args = args;
if (p->args)
(args->refcnt)++;
if ( ! node_append(m, p))
return(0);
m->next = MDOC_NEXT_CHILD;
return(1);
}
int
mdoc_elem_alloc(struct mdoc *m, int line, int pos,
enum mdoct tok, struct mdoc_arg *args)
{
struct mdoc_node *p;
p = node_alloc(m, line, pos, tok, MDOC_ELEM);
p->args = args;
if (p->args)
(args->refcnt)++;
if ( ! node_append(m, p))
return(0);
m->next = MDOC_NEXT_CHILD;
return(1);
}
int
mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p)
{
struct mdoc_node *n;
size_t sv, len;
len = strlen(p);
n = node_alloc(m, line, pos, MDOC_MAX, MDOC_TEXT);
n->string = mandoc_malloc(len + 1);
sv = strlcpy(n->string, p, len + 1);
/* Prohibit truncation. */
assert(sv < len + 1);
if ( ! node_append(m, n))
return(0);
m->next = MDOC_NEXT_SIBLING;
return(1);
}
static void
mdoc_node_free(struct mdoc_node *p)
{
if (p->string)
free(p->string);
if (p->args)
mdoc_argv_free(p->args);
free(p);
}
static void
mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n)
{
/* Adjust siblings. */
if (n->prev)
n->prev->next = n->next;
if (n->next)
n->next->prev = n->prev;
/* Adjust parent. */
if (n->parent) {
n->parent->nchild--;
if (n->parent->child == n)
n->parent->child = n->prev ? n->prev : n->next;
}
/* Adjust parse point, if applicable. */
if (m && m->last == n) {
if (n->prev) {
m->last = n->prev;
m->next = MDOC_NEXT_SIBLING;
} else {
m->last = n->parent;
m->next = MDOC_NEXT_CHILD;
}
}
if (m && m->first == n)
m->first = NULL;
}
void
mdoc_node_delete(struct mdoc *m, struct mdoc_node *p)
{
while (p->child) {
assert(p->nchild);
mdoc_node_delete(m, p->child);
}
assert(0 == p->nchild);
mdoc_node_unlink(m, p);
mdoc_node_free(p);
}
/*
* Parse free-form text, that is, a line that does not begin with the
* control character.
*/
static int
mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
{
char *c, *ws, *end;
struct mdoc_node *n;
/* Ignore bogus comments. */
if ('\\' == buf[offs] &&
'.' == buf[offs + 1] &&
'"' == buf[offs + 2])
return(mdoc_pmsg(m, line, offs, MANDOCERR_BADCOMMENT));
/* No text before an initial macro. */
if (SEC_NONE == m->lastnamed)
return(mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT));
assert(m->last);
n = m->last;
/*
* Divert directly to list processing if we're encountering a
* columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry
* (a MDOC_BODY means it's already open, in which case we should
* process within its context in the normal way).
*/
if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
LIST_column == n->data.Bl.type) {
/* `Bl' is open without any children. */
m->flags |= MDOC_FREECOL;
return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
}
if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
NULL != n->parent &&
MDOC_Bl == n->parent->tok &&
LIST_column == n->parent->data.Bl.type) {
/* `Bl' has block-level `It' children. */
m->flags |= MDOC_FREECOL;
return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
}
/*
* Search for the beginning of unescaped trailing whitespace (ws)
* and for the first character not to be output (end).
*/
/* FIXME: replace with strcspn(). */
ws = NULL;
for (c = end = buf + offs; *c; c++) {
switch (*c) {
case '-':
if (mandoc_hyph(buf + offs, c))
*c = ASCII_HYPH;
ws = NULL;
break;
case ' ':
if (NULL == ws)
ws = c;
continue;
case '\t':
/*
* Always warn about trailing tabs,
* even outside literal context,
* where they should be put on the next line.
*/
if (NULL == ws)
ws = c;
/*
* Strip trailing tabs in literal context only;
* outside, they affect the next line.
*/
if (MDOC_LITERAL & m->flags)
continue;
break;
case '\\':
/* Skip the escaped character, too, if any. */
if (c[1])
c++;
/* FALLTHROUGH */
default:
ws = NULL;
break;
}
end = c + 1;
}
*end = '\0';
if (ws)
if ( ! mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE))
return(0);
if ('\0' == buf[offs] && ! (MDOC_LITERAL & m->flags)) {
if ( ! mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN))
return(0);
/*
* Insert a `Pp' in the case of a blank line. Technically,
* blank lines aren't allowed, but enough manuals assume this
* behaviour that we want to work around it.
*/
if ( ! mdoc_elem_alloc(m, line, offs, MDOC_Pp, NULL))
return(0);
m->next = MDOC_NEXT_SIBLING;
return(1);
}
if ( ! mdoc_word_alloc(m, line, offs, buf+offs))
return(0);
if (MDOC_LITERAL & m->flags)
return(1);
/*
* End-of-sentence check. If the last character is an unescaped
* EOS character, then flag the node as being the end of a
* sentence. The front-end will know how to interpret this.
*/
assert(buf < end);
if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
m->last->flags |= MDOC_EOS;
return(1);
}
static int
macrowarn(struct mdoc *m, int ln, const char *buf, int offs)
{
int rc;
rc = mdoc_vmsg(m, MANDOCERR_MACRO, ln, offs,
"unknown macro: %s%s",
buf, strlen(buf) > 3 ? "..." : "");
/* FIXME: logic should be in driver. */
/* FIXME: broken, will error out and not omit a message. */
return(MDOC_IGN_MACRO & m->pflags ? rc : 0);
}
/*
* Parse a macro line, that is, a line beginning with the control
* character.
*/
static int
mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
{
enum mdoct tok;
int i, j, sv;
char mac[5];
struct mdoc_node *n;
/* Empty lines are ignored. */
offs++;
if ('\0' == buf[offs])
return(1);
i = offs;
/* Accept whitespace after the initial control char. */
if (' ' == buf[i]) {
i++;
while (buf[i] && ' ' == buf[i])
i++;
if ('\0' == buf[i])
return(1);
}
sv = i;
/* Copy the first word into a nil-terminated buffer. */
for (j = 0; j < 4; j++, i++) {
if ('\0' == (mac[j] = buf[i]))
break;
else if (' ' == buf[i])
break;
/* Check for invalid characters. */
if (isgraph((u_char)buf[i]))
continue;
if ( ! mdoc_pmsg(m, ln, i, MANDOCERR_BADCHAR))
return(0);
i--;
}
mac[j] = '\0';
if (j == 4 || j < 2) {
if ( ! macrowarn(m, ln, mac, sv))
goto err;
return(1);
}
if (MDOC_MAX == (tok = mdoc_hash_find(mac))) {
if ( ! macrowarn(m, ln, mac, sv))
goto err;
return(1);
}
/* The macro is sane. Jump to the next word. */
while (buf[i] && ' ' == buf[i])
i++;
/*
* Trailing whitespace. Note that tabs are allowed to be passed
* into the parser as "text", so we only warn about spaces here.
*/
if ('\0' == buf[i] && ' ' == buf[i - 1])
if ( ! mdoc_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE))
goto err;
/*
* If an initial macro or a list invocation, divert directly
* into macro processing.
*/
if (NULL == m->last || MDOC_It == tok || MDOC_El == tok) {
if ( ! mdoc_macro(m, tok, ln, sv, &i, buf))
goto err;
return(1);
}
n = m->last;
assert(m->last);
/*
* If the first macro of a `Bl -column', open an `It' block
* context around the parsed macro.
*/
if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
LIST_column == n->data.Bl.type) {
m->flags |= MDOC_FREECOL;
if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
goto err;
return(1);
}
/*
* If we're following a block-level `It' within a `Bl -column'
* context (perhaps opened in the above block or in ptext()),
* then open an `It' block context around the parsed macro.
*/
if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
NULL != n->parent &&
MDOC_Bl == n->parent->tok &&
LIST_column == n->parent->data.Bl.type) {
m->flags |= MDOC_FREECOL;
if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
goto err;
return(1);
}
/* Normal processing of a macro. */
if ( ! mdoc_macro(m, tok, ln, sv, &i, buf))
goto err;
return(1);
err: /* Error out. */
m->flags |= MDOC_HALT;
return(0);
}

345
commands/mdocml/mdoc.h Normal file
View file

@ -0,0 +1,345 @@
/* $Id: mdoc.h,v 1.90 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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.
*/
#ifndef MDOC_H
#define MDOC_H
/*
* This library implements a validating scanner/parser for ``mdoc'' roff
* macro documents, a.k.a. BSD manual page documents. The mdoc.c file
* drives the parser, while macro.c describes the macro ontologies.
* validate.c pre- and post-validates parsed macros, and action.c
* performs actions on parsed and validated macros.
*/
/* What follows is a list of ALL possible macros. */
enum mdoct {
MDOC_Ap = 0,
MDOC_Dd,
MDOC_Dt,
MDOC_Os,
MDOC_Sh,
MDOC_Ss,
MDOC_Pp,
MDOC_D1,
MDOC_Dl,
MDOC_Bd,
MDOC_Ed,
MDOC_Bl,
MDOC_El,
MDOC_It,
MDOC_Ad,
MDOC_An,
MDOC_Ar,
MDOC_Cd,
MDOC_Cm,
MDOC_Dv,
MDOC_Er,
MDOC_Ev,
MDOC_Ex,
MDOC_Fa,
MDOC_Fd,
MDOC_Fl,
MDOC_Fn,
MDOC_Ft,
MDOC_Ic,
MDOC_In,
MDOC_Li,
MDOC_Nd,
MDOC_Nm,
MDOC_Op,
MDOC_Ot,
MDOC_Pa,
MDOC_Rv,
MDOC_St,
MDOC_Va,
MDOC_Vt,
MDOC_Xr,
MDOC__A,
MDOC__B,
MDOC__D,
MDOC__I,
MDOC__J,
MDOC__N,
MDOC__O,
MDOC__P,
MDOC__R,
MDOC__T,
MDOC__V,
MDOC_Ac,
MDOC_Ao,
MDOC_Aq,
MDOC_At,
MDOC_Bc,
MDOC_Bf,
MDOC_Bo,
MDOC_Bq,
MDOC_Bsx,
MDOC_Bx,
MDOC_Db,
MDOC_Dc,
MDOC_Do,
MDOC_Dq,
MDOC_Ec,
MDOC_Ef,
MDOC_Em,
MDOC_Eo,
MDOC_Fx,
MDOC_Ms,
MDOC_No,
MDOC_Ns,
MDOC_Nx,
MDOC_Ox,
MDOC_Pc,
MDOC_Pf,
MDOC_Po,
MDOC_Pq,
MDOC_Qc,
MDOC_Ql,
MDOC_Qo,
MDOC_Qq,
MDOC_Re,
MDOC_Rs,
MDOC_Sc,
MDOC_So,
MDOC_Sq,
MDOC_Sm,
MDOC_Sx,
MDOC_Sy,
MDOC_Tn,
MDOC_Ux,
MDOC_Xc,
MDOC_Xo,
MDOC_Fo,
MDOC_Fc,
MDOC_Oo,
MDOC_Oc,
MDOC_Bk,
MDOC_Ek,
MDOC_Bt,
MDOC_Hf,
MDOC_Fr,
MDOC_Ud,
MDOC_Lb,
MDOC_Lp,
MDOC_Lk,
MDOC_Mt,
MDOC_Brq,
MDOC_Bro,
MDOC_Brc,
MDOC__C,
MDOC_Es,
MDOC_En,
MDOC_Dx,
MDOC__Q,
MDOC_br,
MDOC_sp,
MDOC__U,
MDOC_Ta,
MDOC_MAX
};
/* What follows is a list of ALL possible macro arguments. */
#define MDOC_Split 0
#define MDOC_Nosplit 1
#define MDOC_Ragged 2
#define MDOC_Unfilled 3
#define MDOC_Literal 4
#define MDOC_File 5
#define MDOC_Offset 6
#define MDOC_Bullet 7
#define MDOC_Dash 8
#define MDOC_Hyphen 9
#define MDOC_Item 10
#define MDOC_Enum 11
#define MDOC_Tag 12
#define MDOC_Diag 13
#define MDOC_Hang 14
#define MDOC_Ohang 15
#define MDOC_Inset 16
#define MDOC_Column 17
#define MDOC_Width 18
#define MDOC_Compact 19
#define MDOC_Std 20
#define MDOC_Filled 21
#define MDOC_Words 22
#define MDOC_Emphasis 23
#define MDOC_Symbolic 24
#define MDOC_Nested 25
#define MDOC_Centred 26
#define MDOC_ARG_MAX 27
/* Type of a syntax node. */
enum mdoc_type {
MDOC_TEXT,
MDOC_ELEM,
MDOC_HEAD,
MDOC_TAIL,
MDOC_BODY,
MDOC_BLOCK,
MDOC_ROOT
};
/* Section (named/unnamed) of `Sh'. */
enum mdoc_sec {
SEC_NONE, /* No section, yet. */
SEC_NAME,
SEC_LIBRARY,
SEC_SYNOPSIS,
SEC_DESCRIPTION,
SEC_IMPLEMENTATION,
SEC_RETURN_VALUES,
SEC_ENVIRONMENT,
SEC_FILES,
SEC_EXIT_STATUS,
SEC_EXAMPLES,
SEC_DIAGNOSTICS,
SEC_COMPATIBILITY,
SEC_ERRORS,
SEC_SEE_ALSO,
SEC_STANDARDS,
SEC_HISTORY,
SEC_AUTHORS,
SEC_CAVEATS,
SEC_BUGS,
SEC_SECURITY,
SEC_CUSTOM, /* User-defined. */
SEC__MAX
};
/* Information from prologue. */
struct mdoc_meta {
char *msec;
char *vol;
char *arch;
time_t date;
char *title;
char *os;
char *name;
};
/* An argument to a macro (multiple values = `It -column'). */
struct mdoc_argv {
int arg;
int line;
int pos;
size_t sz;
char **value;
};
struct mdoc_arg {
size_t argc;
struct mdoc_argv *argv;
unsigned int refcnt;
};
enum mdoc_list {
LIST__NONE = 0,
LIST_bullet,
LIST_column,
LIST_dash,
LIST_diag,
LIST_enum,
LIST_hang,
LIST_hyphen,
LIST_inset,
LIST_item,
LIST_ohang,
LIST_tag
};
enum mdoc_disp {
DISP__NONE = 0,
DISP_centred,
DISP_ragged,
DISP_unfilled,
DISP_filled,
DISP_literal
};
struct mdoc_bd {
const char *offs; /* -offset */
enum mdoc_disp type; /* -ragged, etc. */
int comp; /* -compact */
};
struct mdoc_bl {
const char *width; /* -width */
const char *offs; /* -offset */
enum mdoc_list type; /* -tag, -enum, etc. */
int comp; /* -compact */
};
/* Node in AST. */
struct mdoc_node {
struct mdoc_node *parent; /* parent AST node */
struct mdoc_node *child; /* first child AST node */
struct mdoc_node *next; /* sibling AST node */
struct mdoc_node *prev; /* prior sibling AST node */
int nchild; /* number children */
int line; /* parse line */
int pos; /* parse column */
enum mdoct tok; /* tok or MDOC__MAX if none */
int flags;
#define MDOC_VALID (1 << 0) /* has been validated */
#define MDOC_ACTED (1 << 1) /* has been acted upon */
#define MDOC_EOS (1 << 2) /* at sentence boundary */
#define MDOC_LINE (1 << 3) /* first macro/text on line */
enum mdoc_type type; /* AST node type */
enum mdoc_sec sec; /* current named section */
struct mdoc_arg *args; /* BLOCK/ELEM */
#ifdef UGLY
struct mdoc_node *pending; /* BLOCK */
#endif
struct mdoc_node *head; /* BLOCK */
struct mdoc_node *body; /* BLOCK */
struct mdoc_node *tail; /* BLOCK */
char *string; /* TEXT */
union {
struct mdoc_bl Bl;
struct mdoc_bd Bd;
} data;
};
#define MDOC_IGN_SCOPE (1 << 0) /* Ignore scope violations. */
#define MDOC_IGN_ESCAPE (1 << 1) /* Ignore bad escape sequences. */
#define MDOC_IGN_MACRO (1 << 2) /* Ignore unknown macros. */
/* See mdoc.3 for documentation. */
extern const char *const *mdoc_macronames;
extern const char *const *mdoc_argnames;
__BEGIN_DECLS
struct mdoc;
/* See mdoc.3 for documentation. */
void mdoc_free(struct mdoc *);
struct mdoc *mdoc_alloc(void *, int, mandocmsg);
void mdoc_reset(struct mdoc *);
int mdoc_parseln(struct mdoc *, int, char *, int);
const struct mdoc_node *mdoc_node(const struct mdoc *);
const struct mdoc_meta *mdoc_meta(const struct mdoc *);
int mdoc_endparse(struct mdoc *);
__END_DECLS
#endif /*!MDOC_H*/

File diff suppressed because it is too large Load diff

790
commands/mdocml/mdoc_argv.c Normal file
View file

@ -0,0 +1,790 @@
/* $Id: mdoc_argv.c,v 1.54 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "libmdoc.h"
#include "libmandoc.h"
/*
* Routines to parse arguments of macros. Arguments follow the syntax
* of `-arg [val [valN...]]'. Arguments come in all types: quoted
* arguments, multiple arguments per value, no-value arguments, etc.
*
* There's no limit to the number or arguments that may be allocated.
*/
#define ARGV_NONE (1 << 0)
#define ARGV_SINGLE (1 << 1)
#define ARGV_MULTI (1 << 2)
#define ARGV_OPT_SINGLE (1 << 3)
#define MULTI_STEP 5
static int argv_a2arg(enum mdoct, const char *);
static enum margserr args(struct mdoc *, int, int *,
char *, int, char **);
static int argv(struct mdoc *, int,
struct mdoc_argv *, int *, char *);
static int argv_single(struct mdoc *, int,
struct mdoc_argv *, int *, char *);
static int argv_opt_single(struct mdoc *, int,
struct mdoc_argv *, int *, char *);
static int argv_multi(struct mdoc *, int,
struct mdoc_argv *, int *, char *);
/* Per-argument flags. */
static int mdoc_argvflags[MDOC_ARG_MAX] = {
ARGV_NONE, /* MDOC_Split */
ARGV_NONE, /* MDOC_Nosplit */
ARGV_NONE, /* MDOC_Ragged */
ARGV_NONE, /* MDOC_Unfilled */
ARGV_NONE, /* MDOC_Literal */
ARGV_SINGLE, /* MDOC_File */
ARGV_OPT_SINGLE, /* MDOC_Offset */
ARGV_NONE, /* MDOC_Bullet */
ARGV_NONE, /* MDOC_Dash */
ARGV_NONE, /* MDOC_Hyphen */
ARGV_NONE, /* MDOC_Item */
ARGV_NONE, /* MDOC_Enum */
ARGV_NONE, /* MDOC_Tag */
ARGV_NONE, /* MDOC_Diag */
ARGV_NONE, /* MDOC_Hang */
ARGV_NONE, /* MDOC_Ohang */
ARGV_NONE, /* MDOC_Inset */
ARGV_MULTI, /* MDOC_Column */
ARGV_SINGLE, /* MDOC_Width */
ARGV_NONE, /* MDOC_Compact */
ARGV_NONE, /* MDOC_Std */
ARGV_NONE, /* MDOC_Filled */
ARGV_NONE, /* MDOC_Words */
ARGV_NONE, /* MDOC_Emphasis */
ARGV_NONE, /* MDOC_Symbolic */
ARGV_NONE /* MDOC_Symbolic */
};
static int mdoc_argflags[MDOC_MAX] = {
0, /* Ap */
0, /* Dd */
0, /* Dt */
0, /* Os */
0, /* Sh */
0, /* Ss */
ARGS_DELIM, /* Pp */
ARGS_DELIM, /* D1 */
ARGS_DELIM, /* Dl */
0, /* Bd */
0, /* Ed */
0, /* Bl */
0, /* El */
0, /* It */
ARGS_DELIM, /* Ad */
ARGS_DELIM, /* An */
ARGS_DELIM, /* Ar */
0, /* Cd */
ARGS_DELIM, /* Cm */
ARGS_DELIM, /* Dv */
ARGS_DELIM, /* Er */
ARGS_DELIM, /* Ev */
0, /* Ex */
ARGS_DELIM, /* Fa */
0, /* Fd */
ARGS_DELIM, /* Fl */
ARGS_DELIM, /* Fn */
ARGS_DELIM, /* Ft */
ARGS_DELIM, /* Ic */
0, /* In */
ARGS_DELIM, /* Li */
0, /* Nd */
ARGS_DELIM, /* Nm */
ARGS_DELIM, /* Op */
0, /* Ot */
ARGS_DELIM, /* Pa */
0, /* Rv */
ARGS_DELIM, /* St */
ARGS_DELIM, /* Va */
ARGS_DELIM, /* Vt */
ARGS_DELIM, /* Xr */
0, /* %A */
0, /* %B */
0, /* %D */
0, /* %I */
0, /* %J */
0, /* %N */
0, /* %O */
0, /* %P */
0, /* %R */
0, /* %T */
0, /* %V */
ARGS_DELIM, /* Ac */
0, /* Ao */
ARGS_DELIM, /* Aq */
ARGS_DELIM, /* At */
ARGS_DELIM, /* Bc */
0, /* Bf */
0, /* Bo */
ARGS_DELIM, /* Bq */
ARGS_DELIM, /* Bsx */
ARGS_DELIM, /* Bx */
0, /* Db */
ARGS_DELIM, /* Dc */
0, /* Do */
ARGS_DELIM, /* Dq */
ARGS_DELIM, /* Ec */
0, /* Ef */
ARGS_DELIM, /* Em */
0, /* Eo */
ARGS_DELIM, /* Fx */
ARGS_DELIM, /* Ms */
ARGS_DELIM, /* No */
ARGS_DELIM, /* Ns */
ARGS_DELIM, /* Nx */
ARGS_DELIM, /* Ox */
ARGS_DELIM, /* Pc */
ARGS_DELIM, /* Pf */
0, /* Po */
ARGS_DELIM, /* Pq */
ARGS_DELIM, /* Qc */
ARGS_DELIM, /* Ql */
0, /* Qo */
ARGS_DELIM, /* Qq */
0, /* Re */
0, /* Rs */
ARGS_DELIM, /* Sc */
0, /* So */
ARGS_DELIM, /* Sq */
0, /* Sm */
ARGS_DELIM, /* Sx */
ARGS_DELIM, /* Sy */
ARGS_DELIM, /* Tn */
ARGS_DELIM, /* Ux */
ARGS_DELIM, /* Xc */
0, /* Xo */
0, /* Fo */
0, /* Fc */
0, /* Oo */
ARGS_DELIM, /* Oc */
0, /* Bk */
0, /* Ek */
0, /* Bt */
0, /* Hf */
0, /* Fr */
0, /* Ud */
0, /* Lb */
ARGS_DELIM, /* Lp */
ARGS_DELIM, /* Lk */
ARGS_DELIM, /* Mt */
ARGS_DELIM, /* Brq */
0, /* Bro */
ARGS_DELIM, /* Brc */
0, /* %C */
0, /* Es */
0, /* En */
0, /* Dx */
0, /* %Q */
0, /* br */
0, /* sp */
0, /* %U */
0, /* Ta */
};
/*
* Parse an argument from line text. This comes in the form of -key
* [value0...], which may either have a single mandatory value, at least
* one mandatory value, an optional single value, or no value.
*/
enum margverr
mdoc_argv(struct mdoc *m, int line, enum mdoct tok,
struct mdoc_arg **v, int *pos, char *buf)
{
char *p, sv;
struct mdoc_argv tmp;
struct mdoc_arg *arg;
if ('\0' == buf[*pos])
return(ARGV_EOLN);
assert(' ' != buf[*pos]);
/* Parse through to the first unescaped space. */
p = &buf[++(*pos)];
assert(*pos > 0);
/* LINTED */
while (buf[*pos]) {
if (' ' == buf[*pos])
if ('\\' != buf[*pos - 1])
break;
(*pos)++;
}
/* XXX - save zeroed byte, if not an argument. */
sv = '\0';
if (buf[*pos]) {
sv = buf[*pos];
buf[(*pos)++] = '\0';
}
(void)memset(&tmp, 0, sizeof(struct mdoc_argv));
tmp.line = line;
tmp.pos = *pos;
/* See if our token accepts the argument. */
if (MDOC_ARG_MAX == (tmp.arg = argv_a2arg(tok, p))) {
/* XXX - restore saved zeroed byte. */
if (sv)
buf[*pos - 1] = sv;
return(ARGV_WORD);
}
while (buf[*pos] && ' ' == buf[*pos])
(*pos)++;
if ( ! argv(m, line, &tmp, pos, buf))
return(ARGV_ERROR);
if (NULL == (arg = *v))
arg = *v = mandoc_calloc(1, sizeof(struct mdoc_arg));
arg->argc++;
arg->argv = mandoc_realloc
(arg->argv, arg->argc * sizeof(struct mdoc_argv));
(void)memcpy(&arg->argv[(int)arg->argc - 1],
&tmp, sizeof(struct mdoc_argv));
return(ARGV_ARG);
}
void
mdoc_argv_free(struct mdoc_arg *p)
{
int i;
if (NULL == p)
return;
if (p->refcnt) {
--(p->refcnt);
if (p->refcnt)
return;
}
assert(p->argc);
for (i = (int)p->argc - 1; i >= 0; i--)
mdoc_argn_free(p, i);
free(p->argv);
free(p);
}
void
mdoc_argn_free(struct mdoc_arg *p, int iarg)
{
struct mdoc_argv *arg = &p->argv[iarg];
int j;
if (arg->sz && arg->value) {
for (j = (int)arg->sz - 1; j >= 0; j--)
free(arg->value[j]);
free(arg->value);
}
for (--p->argc; iarg < (int)p->argc; iarg++)
p->argv[iarg] = p->argv[iarg+1];
}
enum margserr
mdoc_zargs(struct mdoc *m, int line, int *pos,
char *buf, int flags, char **v)
{
return(args(m, line, pos, buf, flags, v));
}
enum margserr
mdoc_args(struct mdoc *m, int line, int *pos,
char *buf, enum mdoct tok, char **v)
{
int fl;
struct mdoc_node *n;
fl = mdoc_argflags[tok];
if (MDOC_It != tok)
return(args(m, line, pos, buf, fl, v));
/*
* We know that we're in an `It', so it's reasonable to expect
* us to be sitting in a `Bl'. Someday this may not be the case
* (if we allow random `It's sitting out there), so provide a
* safe fall-back into the default behaviour.
*/
for (n = m->last; n; n = n->parent)
if (MDOC_Bl == n->tok)
break;
if (n && LIST_column == n->data.Bl.type) {
fl |= ARGS_TABSEP;
fl &= ~ARGS_DELIM;
}
return(args(m, line, pos, buf, fl, v));
}
static enum margserr
args(struct mdoc *m, int line, int *pos,
char *buf, int fl, char **v)
{
int i;
char *p, *pp;
enum margserr rc;
enum mdelim d;
/*
* Parse out the terms (like `val' in `.Xx -arg val' or simply
* `.Xx val'), which can have all sorts of properties:
*
* ARGS_DELIM: use special handling if encountering trailing
* delimiters in the form of [[::delim::][ ]+]+.
*
* ARGS_NOWARN: don't post warnings. This is only used when
* re-parsing delimiters, as the warnings have already been
* posted.
*
* ARGS_TABSEP: use special handling for tab/`Ta' separated
* phrases like in `Bl -column'.
*/
assert(' ' != buf[*pos]);
if ('\0' == buf[*pos]) {
if (MDOC_PPHRASE & m->flags)
return(ARGS_EOLN);
/*
* If we're not in a partial phrase and the flag for
* being a phrase literal is still set, the punctuation
* is unterminated.
*/
if (MDOC_PHRASELIT & m->flags)
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE))
return(ARGS_ERROR);
m->flags &= ~MDOC_PHRASELIT;
return(ARGS_EOLN);
}
/*
* If the first character is a closing delimiter and we're to
* look for delimited strings, then pass down the buffer seeing
* if it follows the pattern of [[::delim::][ ]+]+. Note that
* we ONLY care about closing delimiters.
*/
if ((fl & ARGS_DELIM) && DELIM_CLOSE == mdoc_iscdelim(buf[*pos])) {
for (i = *pos; buf[i]; ) {
d = mdoc_iscdelim(buf[i]);
if (DELIM_NONE == d || DELIM_OPEN == d)
break;
i++;
if ('\0' == buf[i] || ' ' != buf[i])
break;
i++;
while (buf[i] && ' ' == buf[i])
i++;
}
if ('\0' == buf[i]) {
*v = &buf[*pos];
if (i && ' ' != buf[i - 1])
return(ARGS_PUNCT);
if (ARGS_NOWARN & fl)
return(ARGS_PUNCT);
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
return(ARGS_ERROR);
return(ARGS_PUNCT);
}
}
*v = &buf[*pos];
/*
* First handle TABSEP items, restricted to `Bl -column'. This
* ignores conventional token parsing and instead uses tabs or
* `Ta' macros to separate phrases. Phrases are parsed again
* for arguments at a later phase.
*/
if (ARGS_TABSEP & fl) {
/* Scan ahead to tab (can't be escaped). */
p = strchr(*v, '\t');
pp = NULL;
/* Scan ahead to unescaped `Ta'. */
if ( ! (MDOC_PHRASELIT & m->flags))
for (pp = *v; ; pp++) {
if (NULL == (pp = strstr(pp, "Ta")))
break;
if (pp > *v && ' ' != *(pp - 1))
continue;
if (' ' == *(pp + 2) || '\0' == *(pp + 2))
break;
}
/* By default, assume a phrase. */
rc = ARGS_PHRASE;
/*
* Adjust new-buffer position to be beyond delimiter
* mark (e.g., Ta -> end + 2).
*/
if (p && pp) {
*pos += pp < p ? 2 : 1;
rc = pp < p ? ARGS_PHRASE : ARGS_PPHRASE;
p = pp < p ? pp : p;
} else if (p && ! pp) {
rc = ARGS_PPHRASE;
*pos += 1;
} else if (pp && ! p) {
p = pp;
*pos += 2;
} else {
rc = ARGS_PEND;
p = strchr(*v, 0);
}
/* Whitespace check for eoln case... */
if ('\0' == *p && ' ' == *(p - 1) && ! (ARGS_NOWARN & fl))
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
return(ARGS_ERROR);
*pos += (int)(p - *v);
/* Strip delimiter's preceding whitespace. */
pp = p - 1;
while (pp > *v && ' ' == *pp) {
if (pp > *v && '\\' == *(pp - 1))
break;
pp--;
}
*(pp + 1) = 0;
/* Strip delimiter's proceeding whitespace. */
for (pp = &buf[*pos]; ' ' == *pp; pp++, (*pos)++)
/* Skip ahead. */ ;
return(rc);
}
/*
* Process a quoted literal. A quote begins with a double-quote
* and ends with a double-quote NOT preceded by a double-quote.
* Whitespace is NOT involved in literal termination.
*/
if (MDOC_PHRASELIT & m->flags || '\"' == buf[*pos]) {
if ( ! (MDOC_PHRASELIT & m->flags))
*v = &buf[++(*pos)];
if (MDOC_PPHRASE & m->flags)
m->flags |= MDOC_PHRASELIT;
for ( ; buf[*pos]; (*pos)++) {
if ('\"' != buf[*pos])
continue;
if ('\"' != buf[*pos + 1])
break;
(*pos)++;
}
if ('\0' == buf[*pos]) {
if (ARGS_NOWARN & fl || MDOC_PPHRASE & m->flags)
return(ARGS_QWORD);
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE))
return(ARGS_ERROR);
return(ARGS_QWORD);
}
m->flags &= ~MDOC_PHRASELIT;
buf[(*pos)++] = '\0';
if ('\0' == buf[*pos])
return(ARGS_QWORD);
while (' ' == buf[*pos])
(*pos)++;
if (0 == buf[*pos] && ! (ARGS_NOWARN & fl))
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
return(ARGS_ERROR);
return(ARGS_QWORD);
}
/*
* A non-quoted term progresses until either the end of line or
* a non-escaped whitespace.
*/
for ( ; buf[*pos]; (*pos)++)
if (*pos && ' ' == buf[*pos] && '\\' != buf[*pos - 1])
break;
if ('\0' == buf[*pos])
return(ARGS_WORD);
buf[(*pos)++] = '\0';
while (' ' == buf[*pos])
(*pos)++;
if ('\0' == buf[*pos] && ! (ARGS_NOWARN & fl))
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
return(ARGS_ERROR);
return(ARGS_WORD);
}
static int
argv_a2arg(enum mdoct tok, const char *p)
{
/*
* Parse an argument identifier from its text. XXX - this
* should really be table-driven to clarify the code.
*
* If you add an argument to the list, make sure that you
* register it here with its one or more macros!
*/
switch (tok) {
case (MDOC_An):
if (0 == strcmp(p, "split"))
return(MDOC_Split);
else if (0 == strcmp(p, "nosplit"))
return(MDOC_Nosplit);
break;
case (MDOC_Bd):
if (0 == strcmp(p, "ragged"))
return(MDOC_Ragged);
else if (0 == strcmp(p, "unfilled"))
return(MDOC_Unfilled);
else if (0 == strcmp(p, "filled"))
return(MDOC_Filled);
else if (0 == strcmp(p, "literal"))
return(MDOC_Literal);
else if (0 == strcmp(p, "file"))
return(MDOC_File);
else if (0 == strcmp(p, "offset"))
return(MDOC_Offset);
else if (0 == strcmp(p, "compact"))
return(MDOC_Compact);
else if (0 == strcmp(p, "centered"))
return(MDOC_Centred);
break;
case (MDOC_Bf):
if (0 == strcmp(p, "emphasis"))
return(MDOC_Emphasis);
else if (0 == strcmp(p, "literal"))
return(MDOC_Literal);
else if (0 == strcmp(p, "symbolic"))
return(MDOC_Symbolic);
break;
case (MDOC_Bk):
if (0 == strcmp(p, "words"))
return(MDOC_Words);
break;
case (MDOC_Bl):
if (0 == strcmp(p, "bullet"))
return(MDOC_Bullet);
else if (0 == strcmp(p, "dash"))
return(MDOC_Dash);
else if (0 == strcmp(p, "hyphen"))
return(MDOC_Hyphen);
else if (0 == strcmp(p, "item"))
return(MDOC_Item);
else if (0 == strcmp(p, "enum"))
return(MDOC_Enum);
else if (0 == strcmp(p, "tag"))
return(MDOC_Tag);
else if (0 == strcmp(p, "diag"))
return(MDOC_Diag);
else if (0 == strcmp(p, "hang"))
return(MDOC_Hang);
else if (0 == strcmp(p, "ohang"))
return(MDOC_Ohang);
else if (0 == strcmp(p, "inset"))
return(MDOC_Inset);
else if (0 == strcmp(p, "column"))
return(MDOC_Column);
else if (0 == strcmp(p, "width"))
return(MDOC_Width);
else if (0 == strcmp(p, "offset"))
return(MDOC_Offset);
else if (0 == strcmp(p, "compact"))
return(MDOC_Compact);
else if (0 == strcmp(p, "nested"))
return(MDOC_Nested);
break;
case (MDOC_Rv):
/* FALLTHROUGH */
case (MDOC_Ex):
if (0 == strcmp(p, "std"))
return(MDOC_Std);
break;
default:
break;
}
return(MDOC_ARG_MAX);
}
static int
argv_multi(struct mdoc *m, int line,
struct mdoc_argv *v, int *pos, char *buf)
{
enum margserr ac;
char *p;
for (v->sz = 0; ; v->sz++) {
if ('-' == buf[*pos])
break;
ac = args(m, line, pos, buf, 0, &p);
if (ARGS_ERROR == ac)
return(0);
else if (ARGS_EOLN == ac)
break;
if (0 == v->sz % MULTI_STEP)
v->value = mandoc_realloc(v->value,
(v->sz + MULTI_STEP) * sizeof(char *));
v->value[(int)v->sz] = mandoc_strdup(p);
}
return(1);
}
static int
argv_opt_single(struct mdoc *m, int line,
struct mdoc_argv *v, int *pos, char *buf)
{
enum margserr ac;
char *p;
if ('-' == buf[*pos])
return(1);
ac = args(m, line, pos, buf, 0, &p);
if (ARGS_ERROR == ac)
return(0);
if (ARGS_EOLN == ac)
return(1);
v->sz = 1;
v->value = mandoc_malloc(sizeof(char *));
v->value[0] = mandoc_strdup(p);
return(1);
}
/*
* Parse a single, mandatory value from the stream.
*/
static int
argv_single(struct mdoc *m, int line,
struct mdoc_argv *v, int *pos, char *buf)
{
int ppos;
enum margserr ac;
char *p;
ppos = *pos;
ac = args(m, line, pos, buf, 0, &p);
if (ARGS_EOLN == ac) {
mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTARGVCOUNT);
return(0);
} else if (ARGS_ERROR == ac)
return(0);
v->sz = 1;
v->value = mandoc_malloc(sizeof(char *));
v->value[0] = mandoc_strdup(p);
return(1);
}
/*
* Determine rules for parsing arguments. Arguments can either accept
* no parameters, an optional single parameter, one parameter, or
* multiple parameters.
*/
static int
argv(struct mdoc *mdoc, int line,
struct mdoc_argv *v, int *pos, char *buf)
{
v->sz = 0;
v->value = NULL;
switch (mdoc_argvflags[v->arg]) {
case (ARGV_SINGLE):
return(argv_single(mdoc, line, v, pos, buf));
case (ARGV_MULTI):
return(argv_multi(mdoc, line, v, pos, buf));
case (ARGV_OPT_SINGLE):
return(argv_opt_single(mdoc, line, v, pos, buf));
default:
/* ARGV_NONE */
break;
}
return(1);
}

View file

@ -0,0 +1,93 @@
/* $Id: mdoc_hash.c,v 1.16 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "libmdoc.h"
static u_char table[27 * 12];
/*
* XXX - this hash has global scope, so if intended for use as a library
* with multiple callers, it will need re-invocation protection.
*/
void
mdoc_hash_init(void)
{
int i, j, major;
const char *p;
memset(table, UCHAR_MAX, sizeof(table));
for (i = 0; i < (int)MDOC_MAX; i++) {
p = mdoc_macronames[i];
if (isalpha((u_char)p[1]))
major = 12 * (tolower((u_char)p[1]) - 97);
else
major = 12 * 26;
for (j = 0; j < 12; j++)
if (UCHAR_MAX == table[major + j]) {
table[major + j] = (u_char)i;
break;
}
assert(j < 12);
}
}
enum mdoct
mdoc_hash_find(const char *p)
{
int major, i, j;
if (0 == p[0])
return(MDOC_MAX);
if ( ! isalpha((u_char)p[0]) && '%' != p[0])
return(MDOC_MAX);
if (isalpha((u_char)p[1]))
major = 12 * (tolower((u_char)p[1]) - 97);
else if ('1' == p[1])
major = 12 * 26;
else
return(MDOC_MAX);
if (p[2] && p[3])
return(MDOC_MAX);
for (j = 0; j < 12; j++) {
if (UCHAR_MAX == (i = table[major + j]))
break;
if (0 == strcmp(p, mdoc_macronames[i]))
return((enum mdoct)i);
}
return(MDOC_MAX);
}

2195
commands/mdocml/mdoc_html.c Normal file

File diff suppressed because it is too large Load diff

1728
commands/mdocml/mdoc_macro.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,219 @@
/* $Id: mdoc_strings.c,v 1.23 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmdoc.h"
static const char * const secnames[SEC__MAX] = {
NULL,
"NAME",
"LIBRARY",
"SYNOPSIS",
"DESCRIPTION",
"IMPLEMENTATION NOTES",
"RETURN VALUES",
"ENVIRONMENT",
"FILES",
"EXIT STATUS",
"EXAMPLES",
"DIAGNOSTICS",
"COMPATIBILITY",
"ERRORS",
"SEE ALSO",
"STANDARDS",
"HISTORY",
"AUTHORS",
"CAVEATS",
"BUGS",
"SECURITY CONSIDERATIONS",
NULL
};
/*
* FIXME: this is repeated in print_text() (html.c) and term_word()
* (term.c).
*/
enum mdelim
mdoc_iscdelim(char p)
{
switch (p) {
case('('):
/* FALLTHROUGH */
case('['):
return(DELIM_OPEN);
case('|'):
return(DELIM_MIDDLE);
case('.'):
/* FALLTHROUGH */
case(','):
/* FALLTHROUGH */
case(';'):
/* FALLTHROUGH */
case(':'):
/* FALLTHROUGH */
case('?'):
/* FALLTHROUGH */
case('!'):
/* FALLTHROUGH */
case(')'):
/* FALLTHROUGH */
case(']'):
return(DELIM_CLOSE);
default:
break;
}
return(DELIM_NONE);
}
enum mdelim
mdoc_isdelim(const char *p)
{
if ('\0' == p[0])
return(DELIM_NONE);
if ('\0' == p[1])
return(mdoc_iscdelim(p[0]));
/*
* XXX; account for groff bubu where the \*(Ba reserved string
* is treated in exactly the same way as the vertical bar. This
* is the only function that checks for this.
*/
return(strcmp(p, "\\*(Ba") ? DELIM_NONE : DELIM_MIDDLE);
}
enum mdoc_sec
mdoc_str2sec(const char *p)
{
int i;
for (i = 0; i < (int)SEC__MAX; i++)
if (secnames[i] && 0 == strcmp(p, secnames[i]))
return((enum mdoc_sec)i);
return(SEC_CUSTOM);
}
/* FIXME: move this into an editable .in file. */
size_t
mdoc_macro2len(enum mdoct macro)
{
switch (macro) {
case(MDOC_Ad):
return(12);
case(MDOC_Ao):
return(12);
case(MDOC_An):
return(12);
case(MDOC_Aq):
return(12);
case(MDOC_Ar):
return(12);
case(MDOC_Bo):
return(12);
case(MDOC_Bq):
return(12);
case(MDOC_Cd):
return(12);
case(MDOC_Cm):
return(10);
case(MDOC_Do):
return(10);
case(MDOC_Dq):
return(12);
case(MDOC_Dv):
return(12);
case(MDOC_Eo):
return(12);
case(MDOC_Em):
return(10);
case(MDOC_Er):
return(17);
case(MDOC_Ev):
return(15);
case(MDOC_Fa):
return(12);
case(MDOC_Fl):
return(10);
case(MDOC_Fo):
return(16);
case(MDOC_Fn):
return(16);
case(MDOC_Ic):
return(10);
case(MDOC_Li):
return(16);
case(MDOC_Ms):
return(6);
case(MDOC_Nm):
return(10);
case(MDOC_No):
return(12);
case(MDOC_Oo):
return(10);
case(MDOC_Op):
return(14);
case(MDOC_Pa):
return(32);
case(MDOC_Pf):
return(12);
case(MDOC_Po):
return(12);
case(MDOC_Pq):
return(12);
case(MDOC_Ql):
return(16);
case(MDOC_Qo):
return(12);
case(MDOC_So):
return(12);
case(MDOC_Sq):
return(12);
case(MDOC_Sy):
return(6);
case(MDOC_Sx):
return(16);
case(MDOC_Tn):
return(10);
case(MDOC_Va):
return(12);
case(MDOC_Vt):
return(12);
case(MDOC_Xr):
return(10);
default:
break;
};
return(0);
}

2109
commands/mdocml/mdoc_term.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

37
commands/mdocml/msec.c Normal file
View file

@ -0,0 +1,37 @@
/* $Id: msec.c,v 1.8 2010/05/17 22:11:42 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 <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mdoc_a2msec(const char *p)
{
#include "msec.in"
return(NULL);
}

40
commands/mdocml/msec.in Normal file
View file

@ -0,0 +1,40 @@
/* $Id: msec.in,v 1.6 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.
*/
/*
* These are all possible manual-section macros and what they correspond
* to when rendered as the volume title.
*
* Be sure to escape strings.
*/
LINE("1", "General Commands Manual")
LINE("2", "System Calls Manual")
LINE("3", "Library Functions Manual")
LINE("3p", "Perl Library Functions Manual")
LINE("4", "Kernel Interfaces Manual")
LINE("5", "File Formats Manual")
LINE("6", "Games Manual")
LINE("7", "Miscellaneous Information Manual")
LINE("8", "System Manager\'s Manual")
LINE("9", "Kernel Developer\'s Manual")
LINE("X11", "X11 Developer\'s Manual")
LINE("X11R6", "X11 Developer\'s Manual")
LINE("unass", "Unassociated")
LINE("local", "Local")
LINE("draft", "Draft")
LINE("paper", "Paper")

399
commands/mdocml/out.c Normal file
View file

@ -0,0 +1,399 @@
/* $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);
}

82
commands/mdocml/out.h Normal file
View file

@ -0,0 +1,82 @@
/* $Id: out.h,v 1.11 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.
*/
#ifndef OUT_H
#define OUT_H
#define DATESIZ 24
__BEGIN_DECLS
enum roffscale {
SCALE_CM,
SCALE_IN,
SCALE_PC,
SCALE_PT,
SCALE_EM,
SCALE_MM,
SCALE_EN,
SCALE_BU,
SCALE_VS,
SCALE_FS,
SCALE_MAX
};
enum roffdeco {
DECO_NONE,
DECO_SPECIAL,
DECO_RESERVED,
DECO_BOLD,
DECO_ITALIC,
DECO_ROMAN,
DECO_PREVIOUS,
DECO_SIZE,
DECO_NOSPACE,
DECO_FONT, /* font */
DECO_FFONT, /* font family */
DECO_MAX
};
struct roffsu {
enum roffscale unit;
double scale;
int pt;
};
#define SCALE_INVERT(p) \
do { (p)->scale = -(p)->scale; } \
while (/* CONSTCOND */ 0)
#define SCALE_VS_INIT(p, v) \
do { (p)->unit = SCALE_VS; \
(p)->scale = (v); \
(p)->pt = 0; } \
while (/* CONSTCOND */ 0)
#define SCALE_HS_INIT(p, v) \
do { (p)->unit = SCALE_BU; \
(p)->scale = (v); \
(p)->pt = 0; } \
while (/* CONSTCOND */ 0)
int a2roffsu(const char *,
struct roffsu *, enum roffscale);
int a2roffdeco(enum roffdeco *, const char **, size_t *);
void time2a(time_t, char *, size_t);
__END_DECLS
#endif /*!HTML_H*/

156
commands/mdocml/roff.3 Normal file
View file

@ -0,0 +1,156 @@
.\" $Id: roff.3,v 1.1 2010/05/25 22:16:59 kristaps Exp $
.\"
.\" Copyright (c) 2010 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.
.\"
.Dd $Mdocdate: May 25 2010 $
.Dt ROFF 3
.Os
.Sh NAME
.Nm roff ,
.Nm roff_alloc ,
.Nm roff_endparse ,
.Nm roff_free ,
.Nm roff_parseln ,
.Nm roff_reset
.Nd roff macro compiler library
.Sh SYNOPSIS
.In mandoc.h
.In roff.h
.Ft "struct roff *"
.Fn roff_alloc "mandocmsg msgs" "void *data"
.Ft int
.Fn roff_endparse "struct roff *roff"
.Ft void
.Fn roff_free "struct roff *roff"
.Ft "enum rofferr"
.Fo roff_parseln
.Fa "struct roff *roff"
.Fa "int line"
.Fa "char **bufp"
.Fa "size_t *bufsz"
.Fa "int pos"
.Fa "int *offs"
.Fc
.Ft void
.Fn roff_reset "struct roff *roff"
.Sh DESCRIPTION
The
.Nm
library processes lines of
.Xr roff 7
input.
.Pp
In general, applications initiate a parsing sequence with
.Fn roff_alloc ,
parse each line in a document with
.Fn roff_parseln ,
close the parsing session with
.Fn roff_endparse ,
and finally free all allocated memory with
.Fn roff_free .
The
.Fn roff_reset
function may be used in order to reset the parser for another input
sequence.
.Pp
The
.Fn roff_parseln
function should be invoked before passing a line into the
.Xr mdoc 3
or
.Xr man 3
libraries.
.Pp
See the
.Sx EXAMPLES
section for a full example.
.Sh REFERENCE
This section further defines the
.Sx Types
and
.Sx Functions
available to programmers.
.Ss Types
Functions (see
.Sx Functions )
may use the following types:
.Bl -ohang
.It Vt "enum rofferr"
Instructions for further processing to the caller of
.Fn roff_parseln .
.It Vt struct roff
An opaque type defined in
.Pa roff.c .
Its values are only used privately within the library.
.It Vt mandocmsg
A function callback type defined in
.Pa mandoc.h .
.El
.Ss Functions
Function descriptions follow:
.Bl -ohang
.It Fn roff_alloc
Allocates a parsing structure.
The
.Fa data
pointer is passed to
.Fa msgs .
The
.Fa pflags
arguments are defined in
.Pa roff.h .
Returns NULL on failure.
If non-NULL, the pointer must be freed with
.Fn roff_free .
.It Fn roff_reset
Reset the parser for another parse routine.
After its use,
.Fn roff_parseln
behaves as if invoked for the first time.
.It Fn roff_free
Free all resources of a parser.
The pointer is no longer valid after invocation.
.It Fn roff_parseln
Parse a nil-terminated line of input.
The character array
.Fa bufp
may be modified or reallocated within this function.
In the latter case,
.Fa bufsz
will be modified accordingly.
The
.Fa offs
pointer will be modified if the line start during subsequent processing
of the line is not at the zeroth index.
This line should not contain the trailing newline.
Returns 0 on failure, 1 on success.
.It Fn roff_endparse
Signals that the parse is complete.
Returns 0 on failure, 1 on success.
.El
.Sh EXAMPLES
See
.Pa main.c
in the source distribution for an example of usage.
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr man 3 ,
.Xr mdoc 3 ,
.Xr roff 7
.Sh AUTHORS
The
.Nm
library was written by
.An Kristaps Dzonsons Aq kristaps@bsd.lv .

304
commands/mdocml/roff.7 Normal file
View file

@ -0,0 +1,304 @@
.\" $Id: roff.7,v 1.9 2010/06/10 21:42:02 kristaps Exp $
.\"
.\" Copyright (c) 2010 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.
.\"
.Dd $Mdocdate: June 10 2010 $
.Dt ROFF 7
.Os
.Sh NAME
.Nm roff
.Nd roff language reference
.Sh DESCRIPTION
The
.Nm roff
language is a general-purpose text-formatting language. The purpose of
this document is to consistently describe those language constructs
accepted by the
.Xr mandoc 1
utility. It is a work in progress.
.Pp
An
.Nm
document follows simple rules: lines beginning with the control
characters
.Sq \.
or
.Sq \(aq
are parsed for macros. Other lines are interpreted within the scope of
prior macros:
.Bd -literal -offset indent
\&.xx Macro lines change control state.
Other lines are interpreted within the current state.
.Ed
.Sh LANGUAGE SYNTAX
.Nm
documents may contain only graphable 7-bit ASCII characters, the space
character, and, in certain circumstances, the tab character. All
manuals must have
.Ux
line terminators.
.Sh MACRO SYNTAX
Macros are arbitrary in length and begin with a control character ,
.Sq \.
or
.Sq \(aq ,
at the beginning of the line.
An arbitrary amount of whitespace may sit between the control character
and the macro name.
Thus, the following are equivalent:
.Bd -literal -offset indent
\&.if
\&.\ \ \ \&if
.Ed
.Sh REFERENCE
This section is a canonical reference of all macros, arranged
alphabetically.
.Ss \&am
The syntax of this macro is the same as that of
.Sx \&ig ,
except that a leading argument must be specified.
It is ignored, as are its children.
.Ss \&ami
The syntax of this macro is the same as that of
.Sx \&ig ,
except that a leading argument must be specified.
It is ignored, as are its children.
.Ss \&am1
The syntax of this macro is the same as that of
.Sx \&ig ,
except that a leading argument must be specified.
It is ignored, as are its children.
.Ss \&de
The syntax of this macro is the same as that of
.Sx \&ig ,
except that a leading argument must be specified.
It is ignored, as are its children.
.Ss \&dei
The syntax of this macro is the same as that of
.Sx \&ig ,
except that a leading argument must be specified.
It is ignored, as are its children.
.Ss \&ds
Define a string.
This macro is intended to have two arguments,
the name of the string to define and its content.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
.Ss \&de1
The syntax of this macro is the same as that of
.Sx \&ig ,
except that a leading argument must be specified.
It is ignored, as are its children.
.Ss \&el
The
.Qq else
half of an if/else conditional.
Pops a result off the stack of conditional evaluations pushed by
.Sx \&ie
and uses it as its conditional.
If no stack entries are present (e.g., due to no prior
.Sx \&ie
calls)
then false is assumed.
The syntax of this macro is similar to
.Sx \&if
except that the conditional is missing.
.Ss \&ie
The
.Qq if
half of an if/else conditional.
The result of the conditional is pushed into a stack used by subsequent
invocations of
.Sx \&el ,
which may be separated by any intervening input (or not exist at all).
Its syntax is equivalent to
.Sx \&if .
.Ss \&if
Begins a conditional.
Right now, the conditional evaluates to true
if and only if it starts with the letter
.Sy n ,
indicating processing in
.Xr nroff 1
style as opposed to
.Xr troff 1
style.
If a conditional is false, its children are not processed, but are
syntactically interpreted to preserve the integrity of the input
document.
Thus,
.Pp
.D1 \&.if t \e .ig
.Pp
will discard the
.Sq \&.ig ,
which may lead to interesting results, but
.Pp
.D1 \&.if t \e .if t \e{\e
.Pp
will continue to syntactically interpret to the block close of the final
conditional.
Sub-conditionals, in this case, obviously inherit the truth value of
the parent.
This macro has the following syntax:
.Pp
.Bd -literal -offset indent -compact
\&.if COND \e{\e
BODY...
\&.\e}
.Ed
.Bd -literal -offset indent -compact
\&.if COND \e{ BODY
BODY... \e}
.Ed
.Bd -literal -offset indent -compact
\&.if COND \e{ BODY
BODY...
\&.\e}
.Ed
.Bd -literal -offset indent -compact
\&.if COND \e
BODY
.Ed
.Pp
COND is a conditional statement.
roff allows for complicated conditionals; mandoc is much simpler.
At this time, mandoc supports only
.Sq n ,
evaluating to true;
and
.Sq t ,
.Sq e ,
and
.Sq o ,
evaluating to false.
All other invocations are read up to the next end of line or space and
evaluate as false.
.Pp
If the BODY section is begun by an escaped brace
.Sq \e{ ,
scope continues until a closing-brace macro
.Sq \.\e} .
If the BODY is not enclosed in braces, scope continues until the next
macro or word.
If the COND is followed by a BODY on the same line, whether after a
brace or not, then macros
.Em must
begin with a control character.
It is generally more intuitive, in this case, to write
.Bd -literal -offset indent
\&.if COND \e{\e
\&.foo
bar
\&.\e}
.Ed
.Pp
than having the macro follow as
.Pp
.D1 \&.if COND \e{ .foo
.Pp
The scope of a conditional is always parsed, but only executed if the
conditional evaluates to true.
.Pp
Note that text subsequent a
.Sq \&.\e}
macro is discarded.
Furthermore, if an explicit closing sequence
.Sq \e}
is specified in a free-form line, the entire line is accepted within the
scope of the prior macro, not only the text preceding the close, with the
.Sq \e}
collapsing into a zero-width space.
.Ss \&ig
Ignore input.
Accepts the following syntax:
.Pp
.Bd -literal -offset indent -compact
\&.ig
BODY...
\&..
.Ed
.Bd -literal -offset indent -compact
\&.ig END
BODY...
\&.END
.Ed
.Pp
In the first case, input is ignored until a
.Sq \&..
macro is encountered on its own line.
In the second case, input is ignored until a
.Sq \&.END
is encountered.
Text subsequent the
.Sq \&.END
or
.Sq \&..
is discarded.
.Pp
Do not use the escape
.Sq \e
anywhere in the definition of END.
It causes very strange behaviour.
Furthermore, if you redefine a
.Nm
macro, such as
.Pp
.D1 \&.ig if
.Pp
the subsequent invocation of
.Sx \&if
will first signify the end of comment, then be invoked as a macro.
This behaviour really shouldn't be counted upon.
.Ss \&rm
Remove a request, macro or string.
This macro is intended to have one argument,
the name of the request, macro or string to be undefined.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
.Ss \&tr
Output character translation.
This macro is intended to have one argument,
consisting of an even number of characters.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
.Sh COMPATIBILITY
This section documents compatibility between mandoc and other other
troff implementations, at this time limited to GNU troff
.Pq Qq groff .
The term
.Qq historic groff
refers to groff versions before the
.Pa doc.tmac
file re-write
.Pq somewhere between 1.15 and 1.19 .
.Pp
.Bl -dash -compact
.It
Historic groff did not accept white-space buffering the custom END tag
for the
.Sx \&ig
macro.
.It
The
.Sx \&if
and family would print funny white-spaces with historic groff when
depending on next-line syntax.
.El
.Sh AUTHORS
The
.Nm
reference was written by
.An Kristaps Dzonsons Aq kristaps@bsd.lv .

857
commands/mdocml/roff.c Normal file
View file

@ -0,0 +1,857 @@
/* $Id: roff.c,v 1.88 2010/06/10 21:42:02 kristaps Exp $ */
/*
* Copyright (c) 2010 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 <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "mandoc.h"
#include "roff.h"
#define RSTACK_MAX 128
#define ROFF_CTL(c) \
('.' == (c) || '\'' == (c))
#ifndef __minix
#if 1
#define ROFF_DEBUG(fmt, args...) \
do { /* Nothing. */ } while (/*CONSTCOND*/ 0)
#else
#define ROFF_DEBUG(fmt, args...) \
do { fprintf(stderr, fmt , ##args); } while (/*CONSTCOND*/ 0)
#endif
#else
void do_nothing(char *fmt, ...)
{
}
#if 1
#define ROFF_DEBUG do_nothing
#else
#define ROFF_DEBUG fprintf
#endif
#endif
enum rofft {
ROFF_am,
ROFF_ami,
ROFF_am1,
ROFF_de,
ROFF_dei,
ROFF_de1,
ROFF_ds,
ROFF_el,
ROFF_ie,
ROFF_if,
ROFF_ig,
ROFF_rm,
ROFF_tr,
ROFF_cblock,
ROFF_ccond,
ROFF_MAX
};
enum roffrule {
ROFFRULE_ALLOW,
ROFFRULE_DENY
};
struct roff {
struct roffnode *last; /* leaf of stack */
mandocmsg msg; /* err/warn/fatal messages */
void *data; /* privdata for messages */
enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */
int rstackpos; /* position in rstack */
};
struct roffnode {
enum rofft tok; /* type of node */
struct roffnode *parent; /* up one in stack */
int line; /* parse line */
int col; /* parse col */
char *end; /* end-rules: custom token */
int endspan; /* end-rules: next-line or infty */
enum roffrule rule; /* current evaluation rule */
};
#define ROFF_ARGS struct roff *r, /* parse ctx */ \
enum rofft tok, /* tok of macro */ \
char **bufp, /* input buffer */ \
size_t *szp, /* size of input buffer */ \
int ln, /* parse line */ \
int ppos, /* original pos in buffer */ \
int pos, /* current pos in buffer */ \
int *offs /* reset offset of buffer data */
typedef enum rofferr (*roffproc)(ROFF_ARGS);
struct roffmac {
const char *name; /* macro name */
roffproc proc; /* process new macro */
roffproc text; /* process as child text of macro */
roffproc sub; /* process as child of macro */
int flags;
#define ROFFMAC_STRUCT (1 << 0) /* always interpret */
struct roffmac *next;
};
static enum rofferr roff_block(ROFF_ARGS);
static enum rofferr roff_block_text(ROFF_ARGS);
static enum rofferr roff_block_sub(ROFF_ARGS);
static enum rofferr roff_cblock(ROFF_ARGS);
static enum rofferr roff_ccond(ROFF_ARGS);
static enum rofferr roff_cond(ROFF_ARGS);
static enum rofferr roff_cond_text(ROFF_ARGS);
static enum rofferr roff_cond_sub(ROFF_ARGS);
static enum roffrule roff_evalcond(const char *, int *);
static enum rofferr roff_line(ROFF_ARGS);
/* See roff_hash_find() */
#define ASCII_HI 126
#define ASCII_LO 33
#define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
static struct roffmac *hash[HASHWIDTH];
static struct roffmac roffs[ROFF_MAX] = {
{ "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "ds", roff_line, NULL, NULL, 0, NULL },
{ "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
{ "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
{ "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
{ "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "rm", roff_line, NULL, NULL, 0, NULL },
{ "tr", roff_line, NULL, NULL, 0, NULL },
{ ".", roff_cblock, NULL, NULL, 0, NULL },
{ "\\}", roff_ccond, NULL, NULL, 0, NULL },
};
static void roff_free1(struct roff *);
static enum rofft roff_hash_find(const char *);
static void roff_hash_init(void);
static void roffnode_cleanscope(struct roff *);
static int roffnode_push(struct roff *,
enum rofft, int, int);
static void roffnode_pop(struct roff *);
static enum rofft roff_parse(const char *, int *);
/* See roff_hash_find() */
#define ROFF_HASH(p) (p[0] - ASCII_LO)
static void
roff_hash_init(void)
{
struct roffmac *n;
int buc, i;
for (i = 0; i < (int)ROFF_MAX; i++) {
assert(roffs[i].name[0] >= ASCII_LO);
assert(roffs[i].name[0] <= ASCII_HI);
buc = ROFF_HASH(roffs[i].name);
if (NULL != (n = hash[buc])) {
for ( ; n->next; n = n->next)
/* Do nothing. */ ;
n->next = &roffs[i];
} else
hash[buc] = &roffs[i];
}
}
/*
* Look up a roff token by its name. Returns ROFF_MAX if no macro by
* the nil-terminated string name could be found.
*/
static enum rofft
roff_hash_find(const char *p)
{
int buc;
struct roffmac *n;
/*
* libroff has an extremely simple hashtable, for the time
* being, which simply keys on the first character, which must
* be printable, then walks a chain. It works well enough until
* optimised.
*/
if (p[0] < ASCII_LO || p[0] > ASCII_HI)
return(ROFF_MAX);
buc = ROFF_HASH(p);
if (NULL == (n = hash[buc]))
return(ROFF_MAX);
for ( ; n; n = n->next)
if (0 == strcmp(n->name, p))
return((enum rofft)(n - roffs));
return(ROFF_MAX);
}
/*
* Pop the current node off of the stack of roff instructions currently
* pending.
*/
static void
roffnode_pop(struct roff *r)
{
struct roffnode *p;
assert(r->last);
p = r->last;
if (ROFF_el == p->tok)
if (r->rstackpos > -1)
r->rstackpos--;
r->last = r->last->parent;
if (p->end)
free(p->end);
free(p);
}
/*
* Push a roff node onto the instruction stack. This must later be
* removed with roffnode_pop().
*/
static int
roffnode_push(struct roff *r, enum rofft tok, int line, int col)
{
struct roffnode *p;
if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
(*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
return(0);
}
p->tok = tok;
p->parent = r->last;
p->line = line;
p->col = col;
p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
r->last = p;
return(1);
}
static void
roff_free1(struct roff *r)
{
while (r->last)
roffnode_pop(r);
}
void
roff_reset(struct roff *r)
{
roff_free1(r);
}
void
roff_free(struct roff *r)
{
roff_free1(r);
free(r);
}
struct roff *
roff_alloc(const mandocmsg msg, void *data)
{
struct roff *r;
if (NULL == (r = calloc(1, sizeof(struct roff)))) {
(*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
return(0);
}
r->msg = msg;
r->data = data;
r->rstackpos = -1;
roff_hash_init();
return(r);
}
enum rofferr
roff_parseln(struct roff *r, int ln,
char **bufp, size_t *szp, int pos, int *offs)
{
enum rofft t;
int ppos;
/*
* First, if a scope is open and we're not a macro, pass the
* text through the macro's filter. If a scope isn't open and
* we're not a macro, just let it through.
*/
if (r->last && ! ROFF_CTL((*bufp)[pos])) {
t = r->last->tok;
assert(roffs[t].text);
ROFF_DEBUG("roff: intercept scoped text: %s, [%s]\n",
roffs[t].name, &(*bufp)[pos]);
return((*roffs[t].text)
(r, t, bufp, szp, ln, pos, pos, offs));
} else if ( ! ROFF_CTL((*bufp)[pos])) {
ROFF_DEBUG("roff: pass non-scoped text: [%s]\n",
&(*bufp)[pos]);
return(ROFF_CONT);
}
/*
* If a scope is open, go to the child handler for that macro,
* as it may want to preprocess before doing anything with it.
*/
if (r->last) {
t = r->last->tok;
assert(roffs[t].sub);
ROFF_DEBUG("roff: intercept scoped context: %s\n",
roffs[t].name);
return((*roffs[t].sub)
(r, t, bufp, szp, ln, pos, pos, offs));
}
/*
* Lastly, as we've no scope open, try to look up and execute
* the new macro. If no macro is found, simply return and let
* the compilers handle it.
*/
ppos = pos;
if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
ROFF_DEBUG("roff: pass non-scoped non-macro: [%s]\n",
&(*bufp)[pos]);
return(ROFF_CONT);
}
ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n",
roffs[t].name, &(*bufp)[pos]);
assert(roffs[t].proc);
return((*roffs[t].proc)
(r, t, bufp, szp, ln, ppos, pos, offs));
}
int
roff_endparse(struct roff *r)
{
if (NULL == r->last)
return(1);
return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
r->last->col, NULL));
}
/*
* Parse a roff node's type from the input buffer. This must be in the
* form of ".foo xxx" in the usual way.
*/
static enum rofft
roff_parse(const char *buf, int *pos)
{
int j;
char mac[5];
enum rofft t;
assert(ROFF_CTL(buf[*pos]));
(*pos)++;
while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
(*pos)++;
if ('\0' == buf[*pos])
return(ROFF_MAX);
for (j = 0; j < 4; j++, (*pos)++)
if ('\0' == (mac[j] = buf[*pos]))
break;
else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))
break;
if (j == 4 || j < 1)
return(ROFF_MAX);
mac[j] = '\0';
if (ROFF_MAX == (t = roff_hash_find(mac)))
return(t);
while (buf[*pos] && ' ' == buf[*pos])
(*pos)++;
return(t);
}
/* ARGSUSED */
static enum rofferr
roff_cblock(ROFF_ARGS)
{
/*
* A block-close `..' should only be invoked as a child of an
* ignore macro, otherwise raise a warning and just ignore it.
*/
if (NULL == r->last) {
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
return(ROFF_ERR);
return(ROFF_IGN);
}
switch (r->last->tok) {
case (ROFF_am):
/* FALLTHROUGH */
case (ROFF_ami):
/* FALLTHROUGH */
case (ROFF_am1):
/* FALLTHROUGH */
case (ROFF_de):
/* FALLTHROUGH */
case (ROFF_dei):
/* FALLTHROUGH */
case (ROFF_de1):
/* FALLTHROUGH */
case (ROFF_ig):
break;
default:
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
return(ROFF_ERR);
return(ROFF_IGN);
}
if ((*bufp)[pos])
if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
return(ROFF_ERR);
roffnode_pop(r);
roffnode_cleanscope(r);
return(ROFF_IGN);
}
static void
roffnode_cleanscope(struct roff *r)
{
while (r->last) {
if (--r->last->endspan < 0)
break;
roffnode_pop(r);
}
}
/* ARGSUSED */
static enum rofferr
roff_ccond(ROFF_ARGS)
{
if (NULL == r->last) {
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
return(ROFF_ERR);
return(ROFF_IGN);
}
switch (r->last->tok) {
case (ROFF_el):
/* FALLTHROUGH */
case (ROFF_ie):
/* FALLTHROUGH */
case (ROFF_if):
break;
default:
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
return(ROFF_ERR);
return(ROFF_IGN);
}
if (r->last->endspan > -1) {
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
return(ROFF_ERR);
return(ROFF_IGN);
}
if ((*bufp)[pos])
if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
return(ROFF_ERR);
roffnode_pop(r);
roffnode_cleanscope(r);
return(ROFF_IGN);
}
/* ARGSUSED */
static enum rofferr
roff_block(ROFF_ARGS)
{
int sv;
size_t sz;
if (ROFF_ig != tok && '\0' == (*bufp)[pos]) {
if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
return(ROFF_ERR);
return(ROFF_IGN);
} else if (ROFF_ig != tok) {
while ((*bufp)[pos] && ' ' != (*bufp)[pos])
pos++;
while (' ' == (*bufp)[pos])
pos++;
}
if ( ! roffnode_push(r, tok, ln, ppos))
return(ROFF_ERR);
if ('\0' == (*bufp)[pos])
return(ROFF_IGN);
sv = pos;
while ((*bufp)[pos] && ' ' != (*bufp)[pos] &&
'\t' != (*bufp)[pos])
pos++;
/*
* Note: groff does NOT like escape characters in the input.
* Instead of detecting this, we're just going to let it fly and
* to hell with it.
*/
assert(pos > sv);
sz = (size_t)(pos - sv);
if (1 == sz && '.' == (*bufp)[sv])
return(ROFF_IGN);
r->last->end = malloc(sz + 1);
if (NULL == r->last->end) {
(*r->msg)(MANDOCERR_MEM, r->data, ln, pos, NULL);
return(ROFF_ERR);
}
memcpy(r->last->end, *bufp + sv, sz);
r->last->end[(int)sz] = '\0';
if ((*bufp)[pos])
if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
return(ROFF_ERR);
return(ROFF_IGN);
}
/* ARGSUSED */
static enum rofferr
roff_block_sub(ROFF_ARGS)
{
enum rofft t;
int i, j;
/*
* First check whether a custom macro exists at this level. If
* it does, then check against it. This is some of groff's
* stranger behaviours. If we encountered a custom end-scope
* tag and that tag also happens to be a "real" macro, then we
* need to try interpreting it again as a real macro. If it's
* not, then return ignore. Else continue.
*/
if (r->last->end) {
i = pos + 1;
while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
i++;
for (j = 0; r->last->end[j]; j++, i++)
if ((*bufp)[i] != r->last->end[j])
break;
if ('\0' == r->last->end[j] &&
('\0' == (*bufp)[i] ||
' ' == (*bufp)[i] ||
'\t' == (*bufp)[i])) {
roffnode_pop(r);
roffnode_cleanscope(r);
if (ROFF_MAX != roff_parse(*bufp, &pos))
return(ROFF_RERUN);
return(ROFF_IGN);
}
}
/*
* If we have no custom end-query or lookup failed, then try
* pulling it out of the hashtable.
*/
ppos = pos;
t = roff_parse(*bufp, &pos);
/* If we're not a comment-end, then throw it away. */
if (ROFF_cblock != t)
return(ROFF_IGN);
assert(roffs[t].proc);
return((*roffs[t].proc)(r, t, bufp,
szp, ln, ppos, pos, offs));
}
/* ARGSUSED */
static enum rofferr
roff_block_text(ROFF_ARGS)
{
return(ROFF_IGN);
}
/* ARGSUSED */
static enum rofferr
roff_cond_sub(ROFF_ARGS)
{
enum rofft t;
enum roffrule rr;
struct roffnode *l;
ppos = pos;
rr = r->last->rule;
/*
* Clean out scope. If we've closed ourselves, then don't
* continue.
*/
l = r->last;
roffnode_cleanscope(r);
if (l != r->last)
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
/*
* A denied conditional must evaluate its children if and only
* if they're either structurally required (such as loops and
* conditionals) or a closing macro.
*/
if (ROFFRULE_DENY == rr)
if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
if (ROFF_ccond != t)
return(ROFF_IGN);
assert(roffs[t].proc);
return((*roffs[t].proc)
(r, t, bufp, szp, ln, ppos, pos, offs));
}
/* ARGSUSED */
static enum rofferr
roff_cond_text(ROFF_ARGS)
{
char *ep, *st;
enum roffrule rr;
rr = r->last->rule;
/*
* We display the value of the text if out current evaluation
* scope permits us to do so.
*/
st = &(*bufp)[pos];
if (NULL == (ep = strstr(st, "\\}"))) {
roffnode_cleanscope(r);
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
}
if (ep == st || (ep > st && '\\' != *(ep - 1)))
roffnode_pop(r);
roffnode_cleanscope(r);
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
}
static enum roffrule
roff_evalcond(const char *v, int *pos)
{
switch (v[*pos]) {
case ('n'):
(*pos)++;
return(ROFFRULE_ALLOW);
case ('e'):
/* FALLTHROUGH */
case ('o'):
/* FALLTHROUGH */
case ('t'):
(*pos)++;
return(ROFFRULE_DENY);
default:
break;
}
while (v[*pos] && ' ' != v[*pos])
(*pos)++;
return(ROFFRULE_DENY);
}
/* ARGSUSED */
static enum rofferr
roff_cond(ROFF_ARGS)
{
int sv;
enum roffrule rule;
/* Stack overflow! */
if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
(*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
return(ROFF_ERR);
}
/* First, evaluate the conditional. */
if (ROFF_el == tok) {
/*
* An `.el' will get the value of the current rstack
* entry set in prior `ie' calls or defaults to DENY.
*/
if (r->rstackpos < 0)
rule = ROFFRULE_DENY;
else
rule = r->rstack[r->rstackpos];
} else
rule = roff_evalcond(*bufp, &pos);
sv = pos;
while (' ' == (*bufp)[pos])
pos++;
/*
* Roff is weird. If we have just white-space after the
* conditional, it's considered the BODY and we exit without
* really doing anything. Warn about this. It's probably
* wrong.
*/
if ('\0' == (*bufp)[pos] && sv != pos) {
if ((*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
return(ROFF_IGN);
return(ROFF_ERR);
}
if ( ! roffnode_push(r, tok, ln, ppos))
return(ROFF_ERR);
r->last->rule = rule;
ROFF_DEBUG("roff: cond: %s -> %s\n", roffs[tok].name,
ROFFRULE_ALLOW == rule ? "allow" : "deny");
if (ROFF_ie == tok) {
/*
* An if-else will put the NEGATION of the current
* evaluated conditional into the stack.
*/
r->rstackpos++;
if (ROFFRULE_DENY == r->last->rule)
r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
else
r->rstack[r->rstackpos] = ROFFRULE_DENY;
}
/* If the parent has false as its rule, then so do we. */
if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) {
r->last->rule = ROFFRULE_DENY;
ROFF_DEBUG("roff: cond override: %s -> deny\n",
roffs[tok].name);
}
/*
* Determine scope. If we're invoked with "\{" trailing the
* conditional, then we're in a multiline scope. Else our scope
* expires on the next line.
*/
r->last->endspan = 1;
if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
r->last->endspan = -1;
pos += 2;
ROFF_DEBUG("roff: cond-scope: %s, multi-line\n",
roffs[tok].name);
} else
ROFF_DEBUG("roff: cond-scope: %s, one-line\n",
roffs[tok].name);
/*
* If there are no arguments on the line, the next-line scope is
* assumed.
*/
if ('\0' == (*bufp)[pos])
return(ROFF_IGN);
/* Otherwise re-run the roff parser after recalculating. */
*offs = pos;
return(ROFF_RERUN);
}
/* ARGSUSED */
static enum rofferr
roff_line(ROFF_ARGS)
{
return(ROFF_IGN);
}

40
commands/mdocml/roff.h Normal file
View file

@ -0,0 +1,40 @@
/* $Id: roff.h,v 1.15 2010/05/17 00:06:36 kristaps Exp $ */
/*
* Copyright (c) 2010 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.
*/
#ifndef ROFF_H
#define ROFF_H
enum rofferr {
ROFF_CONT, /* continue processing line */
ROFF_RERUN, /* re-run roff interpreter with offset */
ROFF_IGN, /* ignore current line */
ROFF_ERR /* badness: puke and stop */
};
__BEGIN_DECLS
struct roff;
void roff_free(struct roff *);
struct roff *roff_alloc(mandocmsg, void *);
void roff_reset(struct roff *);
enum rofferr roff_parseln(struct roff *, int,
char **, size_t *, int, int *);
int roff_endparse(struct roff *);
__END_DECLS
#endif /*!ROFF_H*/

38
commands/mdocml/st.c Normal file
View file

@ -0,0 +1,38 @@
/* $Id: st.c,v 1.8 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 <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mdoc_a2st(const char *p)
{
#include "st.in"
return(NULL);
}

72
commands/mdocml/st.in Normal file
View file

@ -0,0 +1,72 @@
/* $Id: st.in,v 1.13 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.
*/
/*
* This file defines the .St macro arguments. If you add a new
* standard, make sure that the left-and side corresponds to the .St
* argument (like .St -p1003.1) and the right-hand side corresponds to
* the formatted output string.
*
* Be sure to escape strings.
*/
LINE("-p1003.1-88", "IEEE Std 1003.1-1988 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-90", "IEEE Std 1003.1-1990 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2001", "IEEE Std 1003.1-2001 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2004", "IEEE Std 1003.1-2004 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1-2008", "IEEE Std 1003.1-2008 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1", "IEEE Std 1003.1 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1b", "IEEE Std 1003.1b (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1b-93", "IEEE Std 1003.1b-1993 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1c-95", "IEEE Std 1003.1c-1995 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1g-2000", "IEEE Std 1003.1g-2000 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.1i-95", "IEEE Std 1003.1i-1995 (\\(lqPOSIX.1\\(rq)")
LINE("-p1003.2-92", "IEEE Std 1003.2-1992 (\\(lqPOSIX.2\\(rq)")
LINE("-p1003.2a-92", "IEEE Std 1003.2a-1992 (\\(lqPOSIX.2\\(rq)")
LINE("-p1387.2-95", "IEEE Std 1387.2-1995 (\\(lqPOSIX.7.2\\(rq)")
LINE("-p1003.2", "IEEE Std 1003.2 (\\(lqPOSIX.2\\(rq)")
LINE("-p1387.2", "IEEE Std 1387.2 (\\(lqPOSIX.7.2\\(rq)")
LINE("-isoC", "ISO/IEC 9899:1990 (\\(lqISO C90\\(rq)")
LINE("-isoC-90", "ISO/IEC 9899:1990 (\\(lqISO C90\\(rq)")
LINE("-isoC-amd1", "ISO/IEC 9899/AMD1:1995 (\\(lqISO C90\\(rq)")
LINE("-isoC-tcor1", "ISO/IEC 9899/TCOR1:1994 (\\(lqISO C90\\(rq)")
LINE("-isoC-tcor2", "ISO/IEC 9899/TCOR2:1995 (\\(lqISO C90\\(rq)")
LINE("-isoC-99", "ISO/IEC 9899:1999 (\\(lqISO C99\\(rq)")
LINE("-iso9945-1-90", "ISO/IEC 9945-1:1990 (\\(lqPOSIX.1\\(rq)")
LINE("-iso9945-1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
LINE("-iso9945-2-93", "ISO/IEC 9945-2:1993 (\\(lqPOSIX.2\\(rq)")
LINE("-ansiC", "ANSI X3.159-1989 (\\(lqANSI C\\(rq)")
LINE("-ansiC-89", "ANSI X3.159-1989 (\\(lqANSI C\\(rq)")
LINE("-ansiC-99", "ANSI/ISO/IEC 9899-1999 (\\(lqANSI C99\\(rq)")
LINE("-ieee754", "IEEE Std 754-1985")
LINE("-iso8802-3", "ISO 8802-3: 1989")
LINE("-ieee1275-94", "IEEE Std 1275-1994 (\\(lqOpen Firmware\\(rq)")
LINE("-xpg3", "X/Open Portability Guide Issue 3 (\\(lqXPG3\\(rq)")
LINE("-xpg4", "X/Open Portability Guide Issue 4 (\\(lqXPG4\\(rq)")
LINE("-xpg4.2", "X/Open Portability Guide Issue 4.2 (\\(lqXPG4.2\\(rq)")
LINE("-xpg4.3", "X/Open Portability Guide Issue 4.3 (\\(lqXPG4.3\\(rq)")
LINE("-xbd5", "X/Open System Interface Definitions Issue 5 (\\(lqXBD5\\(rq)")
LINE("-xcu5", "X/Open Commands and Utilities Issue 5 (\\(lqXCU5\\(rq)")
LINE("-xsh5", "X/Open System Interfaces and Headers Issue 5 (\\(lqXSH5\\(rq)")
LINE("-xns5", "X/Open Networking Services Issue 5 (\\(lqXNS5\\(rq)")
LINE("-xns5.2", "X/Open Networking Services Issue 5.2 (\\(lqXNS5.2\\(rq)")
LINE("-xns5.2d2.0", "X/Open Networking Services Issue 5.2 Draft 2.0 (\\(lqXNS5.2D2.0\\(rq)")
LINE("-xcurses4.2", "X/Open Curses Issue 4 Version 2 (\\(lqXCURSES4.2\\(rq)")
LINE("-susv2", "Version 2 of the Single UNIX Specification")
LINE("-susv3", "Version 3 of the Single UNIX Specification")
LINE("-svid4", "System V Interface Definition, Fourth Edition (\\(lqSVID4\\(rq)")

77
commands/mdocml/style.css Normal file
View file

@ -0,0 +1,77 @@
div.body { color: #333333;
max-width: 800px;
padding-left: 10px;
font-size: smaller;
font-family: Verdana, Tahoma, Arial, sans-serif; }
div.sec-head { color: #000000;
font-weight: bold; }
div.sec-body { }
div.sec-block { padding-bottom: 1em; }
div.ssec-head { color: #000000;
font-weight: bold; }
div.ssec-body { }
div.ssec-block { }
span.addr { } /* Address (Ad). */
span.arg { font-style: italic; } /* Command argument (Ar). */
span.author { } /* Author name (An). */
span.cmd { font-weight: bold; } /* Command (Cm). */
span.config { font-weight: bold; } /* Config statement (Cd). */
span.define { } /* Defines (Dv). */
span.desc { } /* Nd. After em-dash. */
span.diag { font-weight: bold; } /* Diagnostic (Bl -diag). */
span.emph { font-style: italic; } /* Emphasis (Em). */
span.env { } /* Environment variables (Ev). */
span.errno { } /* Error string (Er). */
span.farg { font-style: italic; } /* Function argument (Fa, Fn). */
span.file { font-style: italic; } /* File (Pa). */
span.flag { font-weight: bold; } /* Flag (Fl, Cm). */
span.fname { font-weight: bold; } /* Function name (Fa, Fn, Rv). */
span.ftype { font-style: italic; } /* Function types (Ft, Fn). */
span.includes { font-weight: bold; } /* Header includes (In). */
span.lib { } /* Library (Lb). */
span.lit { font-family: monospace; } /* Literals (Bf -literal). */
span.macro { font-weight: bold; } /* Macro-ish thing (Fd). */
span.name { color: #003333; font-weight: bold; } /* Name of utility (Nm). */
span.opt { } /* Options (Op, Oo/Oc). */
span.ref { } /* Citations (Rs). */
span.ref-auth { } /* Reference author (%A). */
span.ref-book { font-style: italic; } /* Reference book (%B). */
span.ref-city { } /* Reference city (%C). */
span.ref-date { } /* Reference date (%D). */
span.ref-issue { font-style: italic; } /* Reference issuer/publisher (%I). */
span.ref-jrnl { font-style: italic; } /* Reference journal (%J). */
span.ref-num { } /* Reference number (%N). */
span.ref-opt { } /* Reference optionals (%O). */
span.ref-page { } /* Reference page (%P). */
span.ref-corp { } /* Reference corporate/foreign author (%Q). */
span.ref-rep { } /* Reference report (%R). */
span.ref-title { } /* Reference title (%T). */
span.ref-vol { } /* Reference volume (%V). */
span.symb { font-weight: bold; } /* Symbols. */
span.type { font-style: italic; } /* Variable types (Vt). */
span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */
span.utility { font-weight: bold; } /* Name of utility (Ex). */
span.var { font-weight: bold; } /* Variables (Rv). */
a.link-ext { background: transparent url(external.png) center right no-repeat; padding-right: 12px; }/* Off-site link (Lk). */
a.link-includes { } /* Include-file link (In). */
a.link-mail { background: transparent url(external.png) center right no-repeat; padding-right: 12px; }/* Mailto links (Mt). */
a.link-man { } /* Manual links (Xr). */
a.link-sec { text-decoration: none; border-bottom: 1px dotted #339999; } /* Section links (Sx). */
div.emph { font-style: italic; } /* Emphasis (Bl -emphasis). */
div.lit { margin: 3px;
padding: 3px;
background-color: #EEEEEE;
border: 1px solid #339999;
color: #000000;
font-family: monospace; }
div.symb { font-weight: bold; } /* Symbols (Bl -symbolic). */
table.header { border-bottom: 1px dotted #dddddd;
color: #999999; }
table.footer { border-top: 1px dotted #dddddd;
color: #999999; }

702
commands/mdocml/term.c Normal file
View file

@ -0,0 +1,702 @@
/* $Id: term.c,v 1.148 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "chars.h"
#include "out.h"
#include "term.h"
#include "man.h"
#include "mdoc.h"
#include "main.h"
static void spec(struct termp *, const char *, size_t);
static void res(struct termp *, const char *, size_t);
static void buffera(struct termp *, const char *, size_t);
static void bufferc(struct termp *, char);
static void adjbuf(struct termp *p, size_t);
static void encode(struct termp *, const char *, size_t);
void
term_free(struct termp *p)
{
if (p->buf)
free(p->buf);
if (p->symtab)
chars_free(p->symtab);
free(p);
}
void
term_begin(struct termp *p, term_margin head,
term_margin foot, const void *arg)
{
p->headf = head;
p->footf = foot;
p->argf = arg;
(*p->begin)(p);
}
void
term_end(struct termp *p)
{
(*p->end)(p);
}
struct termp *
term_alloc(enum termenc enc)
{
struct termp *p;
p = calloc(1, sizeof(struct termp));
if (NULL == p) {
perror(NULL);
exit(EXIT_FAILURE);
}
p->tabwidth = 5;
p->enc = enc;
p->defrmargin = 78;
return(p);
}
/*
* Flush a line of text. A "line" is loosely defined as being something
* that should be followed by a newline, regardless of whether it's
* broken apart by newlines getting there. A line can also be a
* fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
* not have a trailing newline.
*
* The following flags may be specified:
*
* - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
* offset value. This is useful when doing columnar lists where the
* prior column has right-padded.
*
* - TERMP_NOBREAK: this is the most important and is used when making
* columns. In short: don't print a newline and instead pad to the
* right margin. Used in conjunction with TERMP_NOLPAD.
*
* - TERMP_TWOSPACE: when padding, make sure there are at least two
* space characters of padding. Otherwise, rather break the line.
*
* - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
* the line is overrun, and don't pad-right if it's underrun.
*
* - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
* overruning, instead save the position and continue at that point
* when the next invocation.
*
* In-line line breaking:
*
* If TERMP_NOBREAK is specified and the line overruns the right
* margin, it will break and pad-right to the right margin after
* writing. If maxrmargin is violated, it will break and continue
* writing from the right-margin, which will lead to the above scenario
* upon exit. Otherwise, the line will break at the right margin.
*/
void
term_flushln(struct termp *p)
{
int i; /* current input position in p->buf */
size_t vis; /* current visual position on output */
size_t vbl; /* number of blanks to prepend to output */
size_t vend; /* end of word visual position on output */
size_t bp; /* visual right border position */
int j; /* temporary loop index */
int jhy; /* last hyphen before line overflow */
size_t maxvis, mmax;
/*
* First, establish the maximum columns of "visible" content.
* This is usually the difference between the right-margin and
* an indentation, but can be, for tagged lists or columns, a
* small set of values.
*/
assert(p->offset < p->rmargin);
maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ?
/* LINTED */
0 : p->rmargin - p->offset - p->overstep;
mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ?
/* LINTED */
0 : p->maxrmargin - p->offset - p->overstep;
bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
/*
* Indent the first line of a paragraph.
*/
vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset;
/*
* FIXME: if bp is zero, we still output the first word before
* breaking the line.
*/
vis = vend = i = 0;
while (i < (int)p->col) {
/*
* Handle literal tab characters.
*/
for (j = i; j < (int)p->col; j++) {
if ('\t' != p->buf[j])
break;
vend = (vis/p->tabwidth+1)*p->tabwidth;
vbl += vend - vis;
vis = vend;
}
/*
* Count up visible word characters. Control sequences
* (starting with the CSI) aren't counted. A space
* generates a non-printing word, which is valid (the
* space is printed according to regular spacing rules).
*/
/* LINTED */
for (jhy = 0; j < (int)p->col; j++) {
if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
break;
if (8 != p->buf[j]) {
if (vend > vis && vend < bp &&
ASCII_HYPH == p->buf[j])
jhy = j;
vend++;
} else
vend--;
}
/*
* Find out whether we would exceed the right margin.
* If so, break to the next line.
*/
if (vend > bp && 0 == jhy && vis > 0) {
vend -= vis;
(*p->endline)(p);
if (TERMP_NOBREAK & p->flags) {
p->viscol = p->rmargin;
(*p->advance)(p, p->rmargin);
vend += p->rmargin - p->offset;
} else {
p->viscol = 0;
vbl = p->offset;
}
/* Remove the p->overstep width. */
bp += (int)/* LINTED */
p->overstep;
p->overstep = 0;
}
/*
* Skip leading tabs, they were handled above.
*/
while (i < (int)p->col && '\t' == p->buf[i])
i++;
/* Write out the [remaining] word. */
for ( ; i < (int)p->col; i++) {
if (vend > bp && jhy > 0 && i > jhy)
break;
if ('\t' == p->buf[i])
break;
if (' ' == p->buf[i]) {
while (' ' == p->buf[i]) {
vbl++;
i++;
}
break;
}
if (ASCII_NBRSP == p->buf[i]) {
vbl++;
continue;
}
/*
* Now we definitely know there will be
* printable characters to output,
* so write preceding white space now.
*/
if (vbl) {
(*p->advance)(p, vbl);
p->viscol += vbl;
vbl = 0;
}
if (ASCII_HYPH == p->buf[i])
(*p->letter)(p, '-');
else
(*p->letter)(p, p->buf[i]);
p->viscol += 1;
}
vend += vbl;
vis = vend;
}
p->col = 0;
p->overstep = 0;
if ( ! (TERMP_NOBREAK & p->flags)) {
p->viscol = 0;
(*p->endline)(p);
return;
}
if (TERMP_HANG & p->flags) {
/* We need one blank after the tag. */
p->overstep = /* LINTED */
vis - maxvis + 1;
/*
* Behave exactly the same way as groff:
* If we have overstepped the margin, temporarily move
* it to the right and flag the rest of the line to be
* shorter.
* If we landed right at the margin, be happy.
* If we are one step before the margin, temporarily
* move it one step LEFT and flag the rest of the line
* to be longer.
*/
if (p->overstep >= -1) {
assert((int)maxvis + p->overstep >= 0);
/* LINTED */
maxvis += p->overstep;
} else
p->overstep = 0;
} else if (TERMP_DANGLE & p->flags)
return;
/* Right-pad. */
if (maxvis > vis + /* LINTED */
((TERMP_TWOSPACE & p->flags) ? 1 : 0)) {
p->viscol += maxvis - vis;
(*p->advance)(p, maxvis - vis);
vis += (maxvis - vis);
} else { /* ...or newline break. */
(*p->endline)(p);
p->viscol = p->rmargin;
(*p->advance)(p, p->rmargin);
}
}
/*
* A newline only breaks an existing line; it won't assert vertical
* space. All data in the output buffer is flushed prior to the newline
* assertion.
*/
void
term_newln(struct termp *p)
{
p->flags |= TERMP_NOSPACE;
if (0 == p->col && 0 == p->viscol) {
p->flags &= ~TERMP_NOLPAD;
return;
}
term_flushln(p);
p->flags &= ~TERMP_NOLPAD;
}
/*
* Asserts a vertical space (a full, empty line-break between lines).
* Note that if used twice, this will cause two blank spaces and so on.
* All data in the output buffer is flushed prior to the newline
* assertion.
*/
void
term_vspace(struct termp *p)
{
term_newln(p);
p->viscol = 0;
(*p->endline)(p);
}
static void
spec(struct termp *p, const char *word, size_t len)
{
const char *rhs;
size_t sz;
rhs = chars_a2ascii(p->symtab, word, len, &sz);
if (rhs)
encode(p, rhs, sz);
}
static void
res(struct termp *p, const char *word, size_t len)
{
const char *rhs;
size_t sz;
rhs = chars_a2res(p->symtab, word, len, &sz);
if (rhs)
encode(p, rhs, sz);
}
void
term_fontlast(struct termp *p)
{
enum termfont f;
f = p->fontl;
p->fontl = p->fontq[p->fonti];
p->fontq[p->fonti] = f;
}
void
term_fontrepl(struct termp *p, enum termfont f)
{
p->fontl = p->fontq[p->fonti];
p->fontq[p->fonti] = f;
}
void
term_fontpush(struct termp *p, enum termfont f)
{
assert(p->fonti + 1 < 10);
p->fontl = p->fontq[p->fonti];
p->fontq[++p->fonti] = f;
}
const void *
term_fontq(struct termp *p)
{
return(&p->fontq[p->fonti]);
}
enum termfont
term_fonttop(struct termp *p)
{
return(p->fontq[p->fonti]);
}
void
term_fontpopq(struct termp *p, const void *key)
{
while (p->fonti >= 0 && key != &p->fontq[p->fonti])
p->fonti--;
assert(p->fonti >= 0);
}
void
term_fontpop(struct termp *p)
{
assert(p->fonti);
p->fonti--;
}
/*
* Handle pwords, partial words, which may be either a single word or a
* phrase that cannot be broken down (such as a literal string). This
* handles word styling.
*/
void
term_word(struct termp *p, const char *word)
{
const char *sv, *seq;
int sz;
size_t ssz;
enum roffdeco deco;
sv = word;
if (word[0] && '\0' == word[1])
switch (word[0]) {
case('.'):
/* FALLTHROUGH */
case(','):
/* FALLTHROUGH */
case(';'):
/* FALLTHROUGH */
case(':'):
/* FALLTHROUGH */
case('?'):
/* FALLTHROUGH */
case('!'):
/* FALLTHROUGH */
case(')'):
/* FALLTHROUGH */
case(']'):
if ( ! (TERMP_IGNDELIM & p->flags))
p->flags |= TERMP_NOSPACE;
break;
default:
break;
}
if ( ! (TERMP_NOSPACE & p->flags)) {
bufferc(p, ' ');
if (TERMP_SENTENCE & p->flags)
bufferc(p, ' ');
}
if ( ! (p->flags & TERMP_NONOSPACE))
p->flags &= ~TERMP_NOSPACE;
p->flags &= ~TERMP_SENTENCE;
/* FIXME: use strcspn. */
while (*word) {
if ('\\' != *word) {
encode(p, word, 1);
word++;
continue;
}
seq = ++word;
sz = a2roffdeco(&deco, &seq, &ssz);
switch (deco) {
case (DECO_RESERVED):
res(p, seq, ssz);
break;
case (DECO_SPECIAL):
spec(p, seq, ssz);
break;
case (DECO_BOLD):
term_fontrepl(p, TERMFONT_BOLD);
break;
case (DECO_ITALIC):
term_fontrepl(p, TERMFONT_UNDER);
break;
case (DECO_ROMAN):
term_fontrepl(p, TERMFONT_NONE);
break;
case (DECO_PREVIOUS):
term_fontlast(p);
break;
default:
break;
}
word += sz;
if (DECO_NOSPACE == deco && '\0' == *word)
p->flags |= TERMP_NOSPACE;
}
/*
* Note that we don't process the pipe: the parser sees it as
* punctuation, but we don't in terms of typography.
*/
if (sv[0] && 0 == sv[1])
switch (sv[0]) {
case('('):
/* FALLTHROUGH */
case('['):
p->flags |= TERMP_NOSPACE;
break;
default:
break;
}
}
static void
adjbuf(struct termp *p, size_t sz)
{
if (0 == p->maxcols)
p->maxcols = 1024;
while (sz >= p->maxcols)
p->maxcols <<= 2;
p->buf = realloc(p->buf, p->maxcols);
if (NULL == p->buf) {
perror(NULL);
exit(EXIT_FAILURE);
}
}
static void
buffera(struct termp *p, const char *word, size_t sz)
{
if (p->col + sz >= p->maxcols)
adjbuf(p, p->col + sz);
memcpy(&p->buf[(int)p->col], word, sz);
p->col += sz;
}
static void
bufferc(struct termp *p, char c)
{
if (p->col + 1 >= p->maxcols)
adjbuf(p, p->col + 1);
p->buf[(int)p->col++] = c;
}
static void
encode(struct termp *p, const char *word, size_t sz)
{
enum termfont f;
int i;
/*
* Encode and buffer a string of characters. If the current
* font mode is unset, buffer directly, else encode then buffer
* character by character.
*/
if (TERMFONT_NONE == (f = term_fonttop(p))) {
buffera(p, word, sz);
return;
}
for (i = 0; i < (int)sz; i++) {
if ( ! isgraph((u_char)word[i])) {
bufferc(p, word[i]);
continue;
}
if (TERMFONT_UNDER == f)
bufferc(p, '_');
else
bufferc(p, word[i]);
bufferc(p, 8);
bufferc(p, word[i]);
}
}
size_t
term_vspan(const struct roffsu *su)
{
double r;
switch (su->unit) {
case (SCALE_CM):
r = su->scale * 2;
break;
case (SCALE_IN):
r = su->scale * 6;
break;
case (SCALE_PC):
r = su->scale;
break;
case (SCALE_PT):
r = su->scale / 8;
break;
case (SCALE_MM):
r = su->scale / 1000;
break;
case (SCALE_VS):
r = su->scale;
break;
default:
r = su->scale - 1;
break;
}
if (r < 0.0)
r = 0.0;
return(/* LINTED */(size_t)
r);
}
size_t
term_hspan(const struct roffsu *su)
{
double r;
/* XXX: CM, IN, and PT are approximations. */
switch (su->unit) {
case (SCALE_CM):
r = 4 * su->scale;
break;
case (SCALE_IN):
/* XXX: this is an approximation. */
r = 10 * su->scale;
break;
case (SCALE_PC):
r = (10 * su->scale) / 6;
break;
case (SCALE_PT):
r = (10 * su->scale) / 72;
break;
case (SCALE_MM):
r = su->scale / 1000; /* FIXME: double-check. */
break;
case (SCALE_VS):
r = su->scale * 2 - 1; /* FIXME: double-check. */
break;
default:
r = su->scale;
break;
}
if (r < 0.0)
r = 0.0;
return((size_t)/* LINTED */
r);
}

123
commands/mdocml/term.h Normal file
View file

@ -0,0 +1,123 @@
/* $Id: term.h,v 1.64 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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.
*/
#ifndef TERM_H
#define TERM_H
__BEGIN_DECLS
struct termp;
enum termenc {
TERMENC_ASCII
};
enum termtype {
TERMTYPE_CHAR,
TERMTYPE_PS
};
enum termfont {
TERMFONT_NONE = 0,
TERMFONT_BOLD,
TERMFONT_UNDER
};
#define TERM_MAXMARGIN 100000 /* FIXME */
typedef void (*term_margin)(struct termp *, const void *);
struct termp_ps {
int psstate; /* state of ps output */
#define PS_INLINE (1 << 0) /* we're in a word */
#define PS_MARGINS (1 << 1) /* we're in the margins */
size_t pscol; /* visible column */
size_t psrow; /* visible row */
char *psmarg; /* margin buf */
size_t psmargsz; /* margin buf size */
size_t psmargcur; /* current pos in margin buf */
size_t pspage; /* current page */
char last; /* character buffer */
enum termfont lastf; /* last set font */
};
struct termp {
enum termtype type;
size_t defrmargin; /* Right margin of the device.. */
size_t rmargin; /* Current right margin. */
size_t maxrmargin; /* Max right margin. */
size_t maxcols; /* Max size of buf. */
size_t offset; /* Margin offest. */
size_t tabwidth; /* Distance of tab positions. */
size_t col; /* Bytes in buf. */
size_t viscol; /* Chars on current line. */
int overstep; /* See termp_flushln(). */
int flags;
#define TERMP_SENTENCE (1 << 1) /* Space before a sentence. */
#define TERMP_NOSPACE (1 << 2) /* No space before words. */
#define TERMP_NOLPAD (1 << 3) /* See term_flushln(). */
#define TERMP_NOBREAK (1 << 4) /* See term_flushln(). */
#define TERMP_IGNDELIM (1 << 6) /* Delims like regulars. */
#define TERMP_NONOSPACE (1 << 7) /* No space (no autounset). */
#define TERMP_DANGLE (1 << 8) /* See term_flushln(). */
#define TERMP_HANG (1 << 9) /* See term_flushln(). */
#define TERMP_TWOSPACE (1 << 10) /* See term_flushln(). */
#define TERMP_NOSPLIT (1 << 11) /* See termp_an_pre/post(). */
#define TERMP_SPLIT (1 << 12) /* See termp_an_pre/post(). */
#define TERMP_ANPREC (1 << 13) /* See termp_an_pre(). */
char *buf; /* Output buffer. */
enum termenc enc; /* Type of encoding. */
void *symtab; /* Encoded-symbol table. */
enum termfont fontl; /* Last font set. */
enum termfont fontq[10]; /* Symmetric fonts. */
int fonti; /* Index of font stack. */
term_margin headf; /* invoked to print head */
term_margin footf; /* invoked to print foot */
void (*letter)(struct termp *, char);
void (*begin)(struct termp *);
void (*end)(struct termp *);
void (*endline)(struct termp *);
void (*advance)(struct termp *, size_t);
const void *argf; /* arg for headf/footf */
union {
struct termp_ps ps;
} engine;
};
struct termp *term_alloc(enum termenc);
void term_free(struct termp *);
void term_newln(struct termp *);
void term_vspace(struct termp *);
void term_word(struct termp *, const char *);
void term_flushln(struct termp *);
void term_begin(struct termp *, term_margin,
term_margin, const void *);
void term_end(struct termp *);
size_t term_hspan(const struct roffsu *);
size_t term_vspan(const struct roffsu *);
enum termfont term_fonttop(struct termp *);
const void *term_fontq(struct termp *);
void term_fontpush(struct termp *, enum termfont);
void term_fontpop(struct termp *);
void term_fontpopq(struct termp *, const void *);
void term_fontrepl(struct termp *, enum termfont);
void term_fontlast(struct termp *);
__END_DECLS
#endif /*!TERM_H*/

View file

@ -0,0 +1,128 @@
/* $Id: term_ascii.c,v 1.4 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "out.h"
#include "term.h"
#include "main.h"
static void ascii_endline(struct termp *);
static void ascii_letter(struct termp *, char);
static void ascii_begin(struct termp *);
static void ascii_advance(struct termp *, size_t);
static void ascii_end(struct termp *);
void *
ascii_alloc(char *outopts)
{
struct termp *p;
const char *toks[2];
char *v;
if (NULL == (p = term_alloc(TERMENC_ASCII)))
return(NULL);
p->type = TERMTYPE_CHAR;
p->letter = ascii_letter;
p->begin = ascii_begin;
p->end = ascii_end;
p->endline = ascii_endline;
p->advance = ascii_advance;
toks[0] = "width";
toks[1] = NULL;
while (outopts && *outopts)
switch (getsubopt(&outopts, UNCONST(toks), &v)) {
case (0):
p->defrmargin = (size_t)atoi(v);
break;
default:
break;
}
/* Enforce a lower boundary. */
if (p->defrmargin < 58)
p->defrmargin = 58;
return(p);
}
void
ascii_free(void *arg)
{
term_free((struct termp *)arg);
}
/* ARGSUSED */
static void
ascii_letter(struct termp *p, char c)
{
putchar(c);
}
static void
ascii_begin(struct termp *p)
{
(*p->headf)(p, p->argf);
}
static void
ascii_end(struct termp *p)
{
(*p->footf)(p, p->argf);
}
/* ARGSUSED */
static void
ascii_endline(struct termp *p)
{
putchar('\n');
}
/* ARGSUSED */
static void
ascii_advance(struct termp *p, size_t len)
{
size_t i;
/* Just print whitespace on the terminal. */
for (i = 0; i < len; i++)
putchar(' ');
}

430
commands/mdocml/term_ps.c Normal file
View file

@ -0,0 +1,430 @@
/* $Id: term_ps.c,v 1.10 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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/param.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "out.h"
#include "main.h"
#include "term.h"
#define PS_CHAR_WIDTH 6
#define PS_CHAR_HEIGHT 12
#define PS_CHAR_TOPMARG (792 - 24)
#define PS_CHAR_TOP (PS_CHAR_TOPMARG - 36)
#define PS_CHAR_LEFT 36
#define PS_CHAR_BOTMARG 24
#define PS_CHAR_BOT (PS_CHAR_BOTMARG + 36)
#define PS_BUFSLOP 128
#define PS_GROWBUF(p, sz) \
do if ((p)->engine.ps.psmargcur + (sz) > \
(p)->engine.ps.psmargsz) { \
(p)->engine.ps.psmargsz += /* CONSTCOND */ \
MAX(PS_BUFSLOP, (sz)); \
(p)->engine.ps.psmarg = realloc \
((p)->engine.ps.psmarg, \
(p)->engine.ps.psmargsz); \
if (NULL == (p)->engine.ps.psmarg) { \
perror(NULL); \
exit(EXIT_FAILURE); \
} \
} while (/* CONSTCOND */ 0)
#ifndef MAX
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#endif
static void ps_letter(struct termp *, char);
static void ps_begin(struct termp *);
static void ps_end(struct termp *);
static void ps_advance(struct termp *, size_t);
static void ps_endline(struct termp *);
static void ps_fclose(struct termp *);
static void ps_pclose(struct termp *);
static void ps_pletter(struct termp *, char);
static void ps_printf(struct termp *, const char *, ...);
static void ps_putchar(struct termp *, char);
static void ps_setfont(struct termp *, enum termfont);
void *
ps_alloc(void)
{
struct termp *p;
if (NULL == (p = term_alloc(TERMENC_ASCII)))
return(NULL);
p->type = TERMTYPE_PS;
p->letter = ps_letter;
p->begin = ps_begin;
p->end = ps_end;
p->advance = ps_advance;
p->endline = ps_endline;
return(p);
}
void
ps_free(void *arg)
{
struct termp *p;
p = (struct termp *)arg;
if (p->engine.ps.psmarg)
free(p->engine.ps.psmarg);
term_free(p);
}
static void
ps_printf(struct termp *p, const char *fmt, ...)
{
va_list ap;
int pos;
va_start(ap, fmt);
/*
* If we're running in regular mode, then pipe directly into
* vprintf(). If we're processing margins, then push the data
* into our growable margin buffer.
*/
if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
vprintf(fmt, ap);
va_end(ap);
return;
}
/*
* XXX: I assume that the in-margin print won't exceed
* PS_BUFSLOP (128 bytes), which is reasonable but still an
* assumption that will cause pukeage if it's not the case.
*/
PS_GROWBUF(p, PS_BUFSLOP);
pos = (int)p->engine.ps.psmargcur;
vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap);
p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg);
va_end(ap);
}
static void
ps_putchar(struct termp *p, char c)
{
int pos;
/* See ps_printf(). */
if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
putchar(c);
return;
}
PS_GROWBUF(p, 2);
pos = (int)p->engine.ps.psmargcur++;
p->engine.ps.psmarg[pos++] = c;
p->engine.ps.psmarg[pos] = '\0';
}
/* ARGSUSED */
static void
ps_end(struct termp *p)
{
/*
* At the end of the file, do one last showpage. This is the
* same behaviour as groff(1) and works for multiple pages as
* well as just one.
*/
assert(0 == p->engine.ps.psstate);
assert('\0' == p->engine.ps.last);
assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
printf("%s", p->engine.ps.psmarg);
printf("showpage\n");
printf("%s\n", "%%EOF");
}
static void
ps_begin(struct termp *p)
{
/*
* Print margins into margin buffer. Nothing gets output to the
* screen yet, so we don't need to initialise the primary state.
*/
if (p->engine.ps.psmarg) {
assert(p->engine.ps.psmargsz);
p->engine.ps.psmarg[0] = '\0';
}
p->engine.ps.psmargcur = 0;
p->engine.ps.psstate = PS_MARGINS;
p->engine.ps.pscol = PS_CHAR_LEFT;
p->engine.ps.psrow = PS_CHAR_TOPMARG;
ps_setfont(p, TERMFONT_NONE);
(*p->headf)(p, p->argf);
(*p->endline)(p);
p->engine.ps.pscol = PS_CHAR_LEFT;
p->engine.ps.psrow = PS_CHAR_BOTMARG;
(*p->footf)(p, p->argf);
(*p->endline)(p);
p->engine.ps.psstate &= ~PS_MARGINS;
assert(0 == p->engine.ps.psstate);
assert(p->engine.ps.psmarg);
assert('\0' != p->engine.ps.psmarg[0]);
/*
* Print header and initialise page state. Following this,
* stuff gets printed to the screen, so make sure we're sane.
*/
printf("%s\n", "%!PS");
ps_setfont(p, TERMFONT_NONE);
p->engine.ps.pscol = PS_CHAR_LEFT;
p->engine.ps.psrow = PS_CHAR_TOP;
}
static void
ps_pletter(struct termp *p, char c)
{
/*
* If we're not in a PostScript "word" context, then open one
* now at the current cursor.
*/
if ( ! (PS_INLINE & p->engine.ps.psstate)) {
ps_printf(p, "%zu %zu moveto\n(",
p->engine.ps.pscol,
p->engine.ps.psrow);
p->engine.ps.psstate |= PS_INLINE;
}
/*
* We need to escape these characters as per the PostScript
* specification. We would also escape non-graphable characters
* (like tabs), but none of them would get to this point and
* it's superfluous to abort() on them.
*/
switch (c) {
case ('('):
/* FALLTHROUGH */
case (')'):
/* FALLTHROUGH */
case ('\\'):
ps_putchar(p, '\\');
break;
default:
break;
}
/* Write the character and adjust where we are on the page. */
ps_putchar(p, c);
p->engine.ps.pscol += PS_CHAR_WIDTH;
}
static void
ps_pclose(struct termp *p)
{
/*
* Spit out that we're exiting a word context (this is a
* "partial close" because we don't check the last-char buffer
* or anything).
*/
if ( ! (PS_INLINE & p->engine.ps.psstate))
return;
ps_printf(p, ") show\n");
p->engine.ps.psstate &= ~PS_INLINE;
}
static void
ps_fclose(struct termp *p)
{
/*
* Strong closure: if we have a last-char, spit it out after
* checking that we're in the right font mode. This will of
* course open a new scope, if applicable.
*
* Following this, close out any scope that's open.
*/
if ('\0' != p->engine.ps.last) {
if (p->engine.ps.lastf != TERMFONT_NONE) {
ps_pclose(p);
ps_setfont(p, TERMFONT_NONE);
}
ps_pletter(p, p->engine.ps.last);
p->engine.ps.last = '\0';
}
if ( ! (PS_INLINE & p->engine.ps.psstate))
return;
ps_pclose(p);
}
static void
ps_letter(struct termp *p, char c)
{
char cc;
/*
* State machine dictates whether to buffer the last character
* or not. Basically, encoded words are detected by checking if
* we're an "8" and switching on the buffer. Then we put "8" in
* our buffer, and on the next charater, flush both character
* and buffer. Thus, "regular" words are detected by having a
* regular character and a regular buffer character.
*/
if ('\0' == p->engine.ps.last) {
assert(8 != c);
p->engine.ps.last = c;
return;
} else if (8 == p->engine.ps.last) {
assert(8 != c);
p->engine.ps.last = '\0';
} else if (8 == c) {
assert(8 != p->engine.ps.last);
if ('_' == p->engine.ps.last) {
if (p->engine.ps.lastf != TERMFONT_UNDER) {
ps_pclose(p);
ps_setfont(p, TERMFONT_UNDER);
}
} else if (p->engine.ps.lastf != TERMFONT_BOLD) {
ps_pclose(p);
ps_setfont(p, TERMFONT_BOLD);
}
p->engine.ps.last = c;
return;
} else {
if (p->engine.ps.lastf != TERMFONT_NONE) {
ps_pclose(p);
ps_setfont(p, TERMFONT_NONE);
}
cc = p->engine.ps.last;
p->engine.ps.last = c;
c = cc;
}
ps_pletter(p, c);
}
static void
ps_advance(struct termp *p, size_t len)
{
/*
* Advance some spaces. This can probably be made smarter,
* i.e., to have multiple space-separated words in the same
* scope, but this is easier: just close out the current scope
* and readjust our column settings.
*/
ps_fclose(p);
p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0;
}
static void
ps_endline(struct termp *p)
{
/* Close out any scopes we have open: we're at eoln. */
ps_fclose(p);
/*
* If we're in the margin, don't try to recalculate our current
* row. XXX: if the column tries to be fancy with multiple
* lines, we'll do nasty stuff.
*/
if (PS_MARGINS & p->engine.ps.psstate)
return;
/*
* Put us down a line. If we're at the page bottom, spit out a
* showpage and restart our row.
*/
p->engine.ps.pscol = PS_CHAR_LEFT;
if (p->engine.ps.psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) {
p->engine.ps.psrow -= PS_CHAR_HEIGHT;
return;
}
assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
printf("%s", p->engine.ps.psmarg);
printf("showpage\n");
p->engine.ps.psrow = PS_CHAR_TOP;
}
static void
ps_setfont(struct termp *p, enum termfont f)
{
if (TERMFONT_BOLD == f)
ps_printf(p, "/Courier-Bold\n");
else if (TERMFONT_UNDER == f)
ps_printf(p, "/Courier-Oblique\n");
else
ps_printf(p, "/Courier\n");
ps_printf(p, "10 selectfont\n");
p->engine.ps.lastf = f;
}

View file

@ -0,0 +1,8 @@
#include <string.h>
int
main(int argc, char **argv)
{
strlcat(argv[0], argv[1], 10);
return 0;
}

View file

@ -0,0 +1,8 @@
#include <string.h>
int
main(int argc, char **argv)
{
strlcpy(argv[0], argv[1], 10);
return 0;
}

213
commands/mdocml/tree.c Normal file
View file

@ -0,0 +1,213 @@
/* $Id: tree.c,v 1.21 2010/06/19 20:46:28 kristaps Exp $ */
/*
* Copyright (c) 2008, 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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "mandoc.h"
#include "mdoc.h"
#include "man.h"
#include "main.h"
static void print_mdoc(const struct mdoc_node *, int);
static void print_man(const struct man_node *, int);
/* ARGSUSED */
void
tree_mdoc(void *arg, const struct mdoc *mdoc)
{
print_mdoc(mdoc_node(mdoc), 0);
}
/* ARGSUSED */
void
tree_man(void *arg, const struct man *man)
{
print_man(man_node(man), 0);
}
static void
print_mdoc(const struct mdoc_node *n, int indent)
{
const char *p, *t;
int i, j;
size_t argc, sz;
char **params;
struct mdoc_argv *argv;
argv = NULL;
argc = sz = 0;
params = NULL;
switch (n->type) {
case (MDOC_ROOT):
t = "root";
break;
case (MDOC_BLOCK):
t = "block";
break;
case (MDOC_HEAD):
t = "block-head";
break;
case (MDOC_BODY):
t = "block-body";
break;
case (MDOC_TAIL):
t = "block-tail";
break;
case (MDOC_ELEM):
t = "elem";
break;
case (MDOC_TEXT):
t = "text";
break;
default:
abort();
/* NOTREACHED */
}
switch (n->type) {
case (MDOC_TEXT):
p = n->string;
break;
case (MDOC_BODY):
p = mdoc_macronames[n->tok];
break;
case (MDOC_HEAD):
p = mdoc_macronames[n->tok];
break;
case (MDOC_TAIL):
p = mdoc_macronames[n->tok];
break;
case (MDOC_ELEM):
p = mdoc_macronames[n->tok];
if (n->args) {
argv = n->args->argv;
argc = n->args->argc;
}
break;
case (MDOC_BLOCK):
p = mdoc_macronames[n->tok];
if (n->args) {
argv = n->args->argv;
argc = n->args->argc;
}
break;
case (MDOC_ROOT):
p = "root";
break;
default:
abort();
/* NOTREACHED */
}
for (i = 0; i < indent; i++)
(void)printf(" ");
(void)printf("%s (%s)", p, t);
for (i = 0; i < (int)argc; i++) {
(void)printf(" -%s", mdoc_argnames[argv[i].arg]);
if (argv[i].sz > 0)
(void)printf(" [");
for (j = 0; j < (int)argv[i].sz; j++)
(void)printf(" [%s]", argv[i].value[j]);
if (argv[i].sz > 0)
(void)printf(" ]");
}
for (i = 0; i < (int)sz; i++)
(void)printf(" [%s]", params[i]);
(void)printf(" %d:%d\n", n->line, n->pos);
if (n->child)
print_mdoc(n->child, indent + 1);
if (n->next)
print_mdoc(n->next, indent);
}
static void
print_man(const struct man_node *n, int indent)
{
const char *p, *t;
int i;
switch (n->type) {
case (MAN_ROOT):
t = "root";
break;
case (MAN_ELEM):
t = "elem";
break;
case (MAN_TEXT):
t = "text";
break;
case (MAN_BLOCK):
t = "block";
break;
case (MAN_HEAD):
t = "block-head";
break;
case (MAN_BODY):
t = "block-body";
break;
default:
abort();
/* NOTREACHED */
}
switch (n->type) {
case (MAN_TEXT):
p = n->string;
break;
case (MAN_ELEM):
/* FALLTHROUGH */
case (MAN_BLOCK):
/* FALLTHROUGH */
case (MAN_HEAD):
/* FALLTHROUGH */
case (MAN_BODY):
p = man_macronames[n->tok];
break;
case (MAN_ROOT):
p = "root";
break;
default:
abort();
/* NOTREACHED */
}
for (i = 0; i < indent; i++)
(void)printf(" ");
(void)printf("%s (%s) %d:%d\n", p, t, n->line, n->pos);
if (n->child)
print_man(n->child, indent + 1);
if (n->next)
print_man(n->next, indent);
}

38
commands/mdocml/vol.c Normal file
View file

@ -0,0 +1,38 @@
/* $Id: vol.c,v 1.8 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 <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmdoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mdoc_a2vol(const char *p)
{
#include "vol.in"
return(NULL);
}

35
commands/mdocml/vol.in Normal file
View file

@ -0,0 +1,35 @@
/* $Id: vol.in,v 1.6 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.
*/
/*
* This file defines volume titles for .Dt.
*
* Be sure to escape strings.
*/
LINE("USD", "User\'s Supplementary Documents")
LINE("PS1", "Programmer\'s Supplementary Documents")
LINE("AMD", "Ancestral Manual Documents")
LINE("SMM", "System Manager\'s Manual")
LINE("URM", "User\'s Reference Manual")
LINE("PRM", "Programmer\'s Manual")
LINE("KM", "Kernel Manual")
LINE("IND", "Manual Master Index")
LINE("MMI", "Manual Master Index")
LINE("LOCAL", "Local Manual")
LINE("LOC", "Local Manual")
LINE("CON", "Contributed Software Manual")