Merge mandoc from vendor into contrib and provide the necessary Makefile glue.

It's not yet connected to the build.
This commit is contained in:
Ulrich Spörlein 2012-10-19 22:21:01 +00:00
commit e4d7d10517
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=241754
78 changed files with 36430 additions and 0 deletions

39
contrib/mdocml/arch.c Normal file
View File

@ -0,0 +1,39 @@
/* $Id: arch.c,v 1.9 2011/03/22 14:33:05 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 "mdoc.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);
}

111
contrib/mdocml/arch.in Normal file
View File

@ -0,0 +1,111 @@
/* $Id: arch.in,v 1.12 2012/01/28 14:02:17 joerg 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.
*
* REMEMBER TO ADD NEW ARCHITECTURES TO MDOC.7!
*/
LINE("acorn26", "Acorn26")
LINE("acorn32", "Acorn32")
LINE("algor", "Algor")
LINE("alpha", "Alpha")
LINE("amd64", "AMD64")
LINE("amiga", "Amiga")
LINE("amigappc", "AmigaPPC")
LINE("arc", "ARC")
LINE("arm", "ARM")
LINE("arm26", "ARM26")
LINE("arm32", "ARM32")
LINE("armish", "ARMISH")
LINE("aviion", "AViiON")
LINE("atari", "ATARI")
LINE("beagle", "Beagle")
LINE("bebox", "BeBox")
LINE("cats", "cats")
LINE("cesfic", "CESFIC")
LINE("cobalt", "Cobalt")
LINE("dreamcast", "Dreamcast")
LINE("emips", "EMIPS")
LINE("evbarm", "evbARM")
LINE("evbmips", "evbMIPS")
LINE("evbppc", "evbPPC")
LINE("evbsh3", "evbSH3")
LINE("ews4800mips", "EWS4800MIPS")
LINE("hp300", "HP300")
LINE("hp700", "HP700")
LINE("hpcarm", "HPCARM")
LINE("hpcmips", "HPCMIPS")
LINE("hpcsh", "HPCSH")
LINE("hppa", "HPPA")
LINE("hppa64", "HPPA64")
LINE("ia64", "ia64")
LINE("i386", "i386")
LINE("ibmnws", "IBMNWS")
LINE("iyonix", "Iyonix")
LINE("landisk", "LANDISK")
LINE("loongson", "Loongson")
LINE("luna68k", "Luna68k")
LINE("luna88k", "Luna88k")
LINE("m68k", "m68k")
LINE("mac68k", "Mac68k")
LINE("macppc", "MacPPC")
LINE("mips", "MIPS")
LINE("mips64", "MIPS64")
LINE("mipsco", "MIPSCo")
LINE("mmeye", "mmEye")
LINE("mvme68k", "MVME68k")
LINE("mvme88k", "MVME88k")
LINE("mvmeppc", "MVMEPPC")
LINE("netwinder", "NetWinder")
LINE("news68k", "NeWS68k")
LINE("newsmips", "NeWSMIPS")
LINE("next68k", "NeXT68k")
LINE("ofppc", "OFPPC")
LINE("palm", "Palm")
LINE("pc532", "PC532")
LINE("playstation2", "PlayStation2")
LINE("pmax", "PMAX")
LINE("pmppc", "pmPPC")
LINE("powerpc", "PowerPC")
LINE("prep", "PReP")
LINE("rs6000", "RS6000")
LINE("sandpoint", "Sandpoint")
LINE("sbmips", "SBMIPS")
LINE("sgi", "SGI")
LINE("sgimips", "SGIMIPS")
LINE("sh3", "SH3")
LINE("shark", "Shark")
LINE("socppc", "SOCPPC")
LINE("solbourne", "Solbourne")
LINE("sparc", "SPARC")
LINE("sparc64", "SPARC64")
LINE("sun2", "Sun2")
LINE("sun3", "Sun3")
LINE("tahoe", "Tahoe")
LINE("vax", "VAX")
LINE("x68k", "X68k")
LINE("x86", "x86")
LINE("x86_64", "x86_64")
LINE("xen", "Xen")
LINE("zaurus", "Zaurus")

39
contrib/mdocml/att.c Normal file
View File

@ -0,0 +1,39 @@
/* $Id: att.c,v 1.9 2011/03/22 14:33:05 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 "mdoc.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);
}

40
contrib/mdocml/att.in Normal file
View File

@ -0,0 +1,40 @@
/* $Id: att.in,v 1.8 2011/07/31 17:30:33 schwarze 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.
* The non-breaking blanks prevent ending an output line right before
* a number. Groff prevent line breaks at the same places.
*/
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("III", "AT&T System\\~III UNIX")
LINE("V", "AT&T System\\~V UNIX")
LINE("V.1", "AT&T System\\~V Release\\~1 UNIX")
LINE("V.2", "AT&T System\\~V Release\\~2 UNIX")
LINE("V.3", "AT&T System\\~V Release\\~3 UNIX")
LINE("V.4", "AT&T System\\~V Release\\~4 UNIX")

167
contrib/mdocml/chars.c Normal file
View File

@ -0,0 +1,167 @@
/* $Id: chars.c,v 1.52 2011/11/08 00:15:23 kristaps Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 "libmandoc.h"
#define PRINT_HI 126
#define PRINT_LO 32
struct ln {
struct ln *next;
const char *code;
const char *ascii;
int unicode;
};
#define LINES_MAX 328
#define CHAR(in, ch, code) \
{ NULL, (in), (ch), (code) },
#define CHAR_TBL_START static struct ln lines[LINES_MAX] = {
#define CHAR_TBL_END };
#include "chars.in"
struct mchars {
struct ln **htab;
};
static const struct ln *find(const struct mchars *,
const char *, size_t);
void
mchars_free(struct mchars *arg)
{
free(arg->htab);
free(arg);
}
struct mchars *
mchars_alloc(void)
{
struct mchars *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.
*/
tab = mandoc_malloc(sizeof(struct mchars));
htab = mandoc_calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
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;
return(tab);
}
int
mchars_spec2cp(const struct mchars *arg, const char *p, size_t sz)
{
const struct ln *ln;
ln = find(arg, p, sz);
if (NULL == ln)
return(-1);
return(ln->unicode);
}
char
mchars_num2char(const char *p, size_t sz)
{
int i;
if ((i = mandoc_strntoi(p, sz, 10)) < 0)
return('\0');
return(i > 0 && i < 256 && isprint(i) ?
/* LINTED */ i : '\0');
}
int
mchars_num2uc(const char *p, size_t sz)
{
int i;
if ((i = mandoc_strntoi(p, sz, 16)) < 0)
return('\0');
/* FIXME: make sure we're not in a bogus range. */
return(i > 0x80 && i <= 0x10FFFF ? i : '\0');
}
const char *
mchars_spec2str(const struct mchars *arg,
const char *p, size_t sz, size_t *rsz)
{
const struct ln *ln;
ln = find(arg, p, sz);
if (NULL == ln) {
*rsz = 1;
return(NULL);
}
*rsz = strlen(ln->ascii);
return(ln->ascii);
}
static const struct ln *
find(const struct mchars *tab, const char *p, size_t sz)
{
const struct ln *pp;
int hash;
assert(p);
if (0 == sz || p[0] < PRINT_LO || p[0] > PRINT_HI)
return(NULL);
hash = (int)p[0] - PRINT_LO;
for (pp = tab->htab[hash]; pp; pp = pp->next)
if (0 == strncmp(pp->code, p, sz) &&
'\0' == pp->code[(int)sz])
return(pp);
return(NULL);
}

397
contrib/mdocml/chars.in Normal file
View File

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

View File

@ -0,0 +1,93 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_FGETLN
int dummy;
#else
/* $NetBSD: fgetln.c,v 1.3 2006/09/25 07:18:17 lukem Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Christos Zoulas.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *
fgetln(fp, len)
FILE *fp;
size_t *len;
{
static char *buf = NULL;
static size_t bufsiz = 0;
char *ptr;
if (buf == NULL) {
bufsiz = BUFSIZ;
if ((buf = malloc(bufsiz)) == NULL)
return NULL;
}
if (fgets(buf, bufsiz, fp) == NULL)
return NULL;
*len = 0;
while ((ptr = strchr(&buf[*len], '\n')) == NULL) {
size_t nbufsiz = bufsiz + BUFSIZ;
char *nbuf = realloc(buf, nbufsiz);
if (nbuf == NULL) {
int oerrno = errno;
free(buf);
errno = oerrno;
buf = NULL;
return NULL;
} else
buf = nbuf;
*len = bufsiz;
if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL)
return buf;
bufsiz = nbufsiz;
}
*len = (ptr - buf) + 1;
return buf;
}
#endif

View File

@ -0,0 +1,104 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_GETSUBOPT
int dummy;
#else
/* $OpenBSD: getsubopt.c,v 1.4 2005/08/08 08:05:36 espie Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
/*
* The SVID interface to getsubopt provides no way of figuring out which
* part of the suboptions list wasn't matched. This makes error messages
* tricky... The extern variable suboptarg is a pointer to the token
* which didn't match.
*/
char *suboptarg;
int
getsubopt(char **optionp, char * const *tokens, char **valuep)
{
int cnt;
char *p;
suboptarg = *valuep = NULL;
if (!optionp || !*optionp)
return(-1);
/* skip leading white-space, commas */
for (p = *optionp; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p);
if (!*p) {
*optionp = p;
return(-1);
}
/* save the start of the token, and skip the rest of the token. */
for (suboptarg = p;
*++p && *p != ',' && *p != '=' && *p != ' ' && *p != '\t';);
if (*p) {
/*
* If there's an equals sign, set the value pointer, and
* skip over the value part of the token. Terminate the
* token.
*/
if (*p == '=') {
*p = '\0';
for (*valuep = ++p;
*p && *p != ',' && *p != ' ' && *p != '\t'; ++p);
if (*p)
*p++ = '\0';
} else
*p++ = '\0';
/* Skip any whitespace or commas after this token. */
for (; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p);
}
/* set optionp for next round. */
*optionp = p;
for (cnt = 0; *tokens; ++tokens, ++cnt)
if (!strcmp(suboptarg, *tokens))
return(cnt);
return(-1);
}
#endif

View File

@ -0,0 +1,67 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STRLCAT
int dummy;
#else
/* $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.
*/
#include <sys/types.h>
#include <string.h>
/*
* 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

View File

@ -0,0 +1,63 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STRLCPY
int dummy;
#else
/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert 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.
*/
#include <sys/types.h>
#include <string.h>
/*
* 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

58
contrib/mdocml/config.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef MANDOC_CONFIG_H
#define MANDOC_CONFIG_H
#if defined(__linux__) || defined(__MINT__)
# define _GNU_SOURCE /* strptime(), getsubopt() */
#endif
#include <stdio.h>
#define HAVE_FGETLN
#define HAVE_STRPTIME
#define HAVE_GETSUBOPT
#define HAVE_STRLCAT
#define HAVE_MMAP
#define HAVE_STRLCPY
#include <sys/types.h>
#if !defined(__BEGIN_DECLS)
# ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# else
# define __BEGIN_DECLS
# endif
#endif
#if !defined(__END_DECLS)
# ifdef __cplusplus
# define __END_DECLS }
# else
# define __END_DECLS
# endif
#endif
#if defined(__APPLE__)
# define htobe32(x) OSSwapHostToBigInt32(x)
# define betoh32(x) OSSwapBigToHostInt32(x)
# define htobe64(x) OSSwapHostToBigInt64(x)
# define betoh64(x) OSSwapBigToHostInt64(x)
#elif defined(__linux__)
# define betoh32(x) be32toh(x)
# define betoh64(x) be64toh(x)
#endif
#ifndef HAVE_STRLCAT
extern size_t strlcat(char *, const char *, size_t);
#endif
#ifndef HAVE_STRLCPY
extern size_t strlcpy(char *, const char *, size_t);
#endif
#ifndef HAVE_GETSUBOPT
extern int getsubopt(char **, char * const *, char **);
extern char *suboptarg;
#endif
#ifndef HAVE_FGETLN
extern char *fgetln(FILE *, size_t *);
#endif
#endif /* MANDOC_CONFIG_H */

280
contrib/mdocml/eqn.7 Normal file
View File

@ -0,0 +1,280 @@
.\" $Id: eqn.7,v 1.28 2011/09/25 18:37:09 schwarze Exp $
.\"
.\" Copyright (c) 2011 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: September 25 2011 $
.Dt EQN 7
.Os
.Sh NAME
.Nm eqn
.Nd eqn language reference for mandoc
.Sh DESCRIPTION
The
.Nm eqn
language is an equation-formatting language.
It is used within
.Xr mdoc 7
and
.Xr man 7
.Ux
manual pages.
It describes the
.Em structure
of an equation, not its mathematical meaning.
This manual describes the
.Nm
language accepted by the
.Xr mandoc 1
utility, which corresponds to the Second Edition eqn specification (see
.Sx SEE ALSO
for references).
.Pp
Equations within
.Xr mdoc 7
or
.Xr man 7
documents are enclosed by the standalone
.Sq \&.EQ
and
.Sq \&.EN
tags.
Equations are multi-line blocks consisting of formulas and control
statements.
.Sh EQUATION STRUCTURE
Each equation is bracketed by
.Sq \&.EQ
and
.Sq \&.EN
strings.
.Em Note :
these are not the same as
.Xr roff 7
macros, and may only be invoked as
.Sq \&.EQ .
.Pp
The equation grammar is as follows, where quoted strings are
case-sensitive literals in the input:
.Bd -literal -offset indent
eqn : box | eqn box
box : text
| \*q{\*q eqn \*q}\*q
| \*qdefine\*q text text
| \*qndefine\*q text text
| \*qtdefine\*q text text
| \*qgfont\*q text
| \*qgsize\*q text
| \*qset\*q text text
| \*qundef\*q text
| box pos box
| box mark
| \*qmatrix\*q \*q{\*q [col \*q{\*q list \*q}\*q ]*
| pile \*q{\*q list \*q}\*q
| font box
| \*qsize\*q text box
| \*qleft\*q text eqn [\*qright\*q text]
col : \*qlcol\*q | \*qrcol\*q | \*qccol\*q | \*qcol\*q
text : [^space\e\*q]+ | \e\*q.*\e\*q
pile : \*qlpile\*q | \*qcpile\*q | \*qrpile\*q | \*qpile\*q
pos : \*qover\*q | \*qsup\*q | \*qsub\*q | \*qto\*q | \*qfrom\*q
mark : \*qdot\*q | \*qdotdot\*q | \*qhat\*q | \*qtilde\*q | \*qvec\*q
| \*qdyad\*q | \*qbar\*q | \*qunder\*q
font : \*qroman\*q | \*qitalic\*q | \*qbold\*q | \*qfat\*q
list : eqn
| list \*qabove\*q eqn
space : [\e^~ \et]
.Ed
.Pp
White-space consists of the space, tab, circumflex, and tilde
characters.
If within a quoted string, these space characters are retained.
Quoted strings are also not scanned for replacement definitions.
.Pp
The following text terms are translated into a rendered glyph, if
available: alpha, beta, chi, delta, epsilon, eta, gamma, iota, kappa,
lambda, mu, nu, omega, omicron, phi, pi, psi, rho, sigma, tau, theta,
upsilon, xi, zeta, DELTA, GAMMA, LAMBDA, OMEGA, PHI, PI, PSI, SIGMA,
THETA, UPSILON, XI, inter (intersection), union (union), prod (product),
int (integral), sum (summation), grad (gradient), del (vector
differential), times (multiply), cdot (centre-dot), nothing (zero-width
space), approx (approximately equals), prime (prime), half (one-half),
partial (partial differential), inf (infinity), >> (much greater), <<
(much less), \-> (left arrow), <\- (right arrow), += (plus-minus), !=
(not equal), == (equivalence), <= (less-than-equal), and >=
(more-than-equal).
.Pp
The following control statements are available:
.Bl -tag -width Ds
.It Cm define
Replace all occurrences of a key with a value.
Its syntax is as follows:
.Pp
.D1 define Ar key cvalc
.Pp
The first character of the value string,
.Ar c ,
is used as the delimiter for the value
.Ar val .
This allows for arbitrary enclosure of terms (not just quotes), such as
.Pp
.D1 define Ar foo 'bar baz'
.D1 define Ar foo cbar bazc
.Pp
It is an error to have an empty
.Ar key
or
.Ar val .
Note that a quoted
.Ar key
causes errors in some
.Nm
implementations and should not be considered portable.
It is not expanded for replacements.
Definitions may refer to other definitions; these are evaluated
recursively when text replacement occurs and not when the definition is
created.
.Pp
Definitions can create arbitrary strings, for example, the following is
a legal construction.
.Bd -literal -offset indent
define foo 'define'
foo bar 'baz'
.Ed
.Pp
Self-referencing definitions will raise an error.
The
.Cm ndefine
statement is a synonym for
.Cm define ,
while
.Cm tdefine
is discarded.
.It Cm gfont
Set the default font of subsequent output.
Its syntax is as follows:
.Pp
.D1 gfont Ar font
.Pp
In mandoc, this value is discarded.
.It Cm gsize
Set the default size of subsequent output.
Its syntax is as follows:
.Pp
.D1 gsize Ar size
.Pp
The
.Ar size
value should be an integer.
.It Cm set
Set an equation mode.
In mandoc, both arguments are thrown away.
Its syntax is as follows:
.Pp
.D1 set Ar key val
.Pp
The
.Ar key
and
.Ar val
are not expanded for replacements.
This statement is a GNU extension.
.It Cm undef
Unset a previously-defined key.
Its syntax is as follows:
.Pp
.D1 define Ar key
.Pp
Once invoked, the definition for
.Ar key
is discarded.
The
.Ar key
is not expanded for replacements.
This statement is a GNU extension.
.El
.Sh COMPATIBILITY
This section documents the compatibility of mandoc
.Nm
and the troff
.Nm
implementation (including GNU troff).
.Pp
.Bl -dash -compact
.It
The text string
.Sq \e\*q
is interpreted as a literal quote in troff.
In mandoc, this is interpreted as a comment.
.It
In troff, The circumflex and tilde white-space symbols map to
fixed-width spaces.
In mandoc, these characters are synonyms for the space character.
.It
The troff implementation of
.Nm
allows for equation alignment with the
.Cm mark
and
.Cm lineup
tokens.
mandoc discards these tokens.
The
.Cm back Ar n ,
.Cm fwd Ar n ,
.Cm up Ar n ,
and
.Cm down Ar n
commands are also ignored.
.El
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr man 7 ,
.Xr mandoc_char 7 ,
.Xr mdoc 7 ,
.Xr roff 7
.Rs
.%A Brian W. Kernighan
.%A Lorinda L. Cherry
.%T System for Typesetting Mathematics
.%J Communications of the ACM
.%V 18
.%P 151\(en157
.%D March, 1975
.Re
.Rs
.%A Brian W. Kernighan
.%A Lorinda L. Cherry
.%T Typesetting Mathematics, User's Guide
.%D 1976
.Re
.Rs
.%A Brian W. Kernighan
.%A Lorinda L. Cherry
.%T Typesetting Mathematics, User's Guide (Second Edition)
.%D 1978
.Re
.Sh HISTORY
The eqn utility, a preprocessor for troff, was originally written by
Brian W. Kernighan and Lorinda L. Cherry in 1975.
The GNU reimplementation of eqn, part of the GNU troff package, was
released in 1989 by James Clark.
The eqn component of
.Xr mandoc 1
was added in 2011.
.Sh AUTHORS
This
.Nm
reference was written by
.An Kristaps Dzonsons ,
.Mt kristaps@bsd.lv .

949
contrib/mdocml/eqn.c Normal file
View File

@ -0,0 +1,949 @@
/* $Id: eqn.c,v 1.38 2011/07/25 15:37:00 kristaps Exp $ */
/*
* Copyright (c) 2011 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 <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmandoc.h"
#include "libroff.h"
#define EQN_NEST_MAX 128 /* maximum nesting of defines */
#define EQN_MSG(t, x) mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL)
enum eqn_rest {
EQN_DESCOPE,
EQN_ERR,
EQN_OK,
EQN_EOF
};
enum eqn_symt {
EQNSYM_alpha,
EQNSYM_beta,
EQNSYM_chi,
EQNSYM_delta,
EQNSYM_epsilon,
EQNSYM_eta,
EQNSYM_gamma,
EQNSYM_iota,
EQNSYM_kappa,
EQNSYM_lambda,
EQNSYM_mu,
EQNSYM_nu,
EQNSYM_omega,
EQNSYM_omicron,
EQNSYM_phi,
EQNSYM_pi,
EQNSYM_ps,
EQNSYM_rho,
EQNSYM_sigma,
EQNSYM_tau,
EQNSYM_theta,
EQNSYM_upsilon,
EQNSYM_xi,
EQNSYM_zeta,
EQNSYM_DELTA,
EQNSYM_GAMMA,
EQNSYM_LAMBDA,
EQNSYM_OMEGA,
EQNSYM_PHI,
EQNSYM_PI,
EQNSYM_PSI,
EQNSYM_SIGMA,
EQNSYM_THETA,
EQNSYM_UPSILON,
EQNSYM_XI,
EQNSYM_inter,
EQNSYM_union,
EQNSYM_prod,
EQNSYM_int,
EQNSYM_sum,
EQNSYM_grad,
EQNSYM_del,
EQNSYM_times,
EQNSYM_cdot,
EQNSYM_nothing,
EQNSYM_approx,
EQNSYM_prime,
EQNSYM_half,
EQNSYM_partial,
EQNSYM_inf,
EQNSYM_muchgreat,
EQNSYM_muchless,
EQNSYM_larrow,
EQNSYM_rarrow,
EQNSYM_pm,
EQNSYM_nequal,
EQNSYM_equiv,
EQNSYM_lessequal,
EQNSYM_moreequal,
EQNSYM__MAX
};
enum eqnpartt {
EQN_DEFINE = 0,
EQN_NDEFINE,
EQN_TDEFINE,
EQN_SET,
EQN_UNDEF,
EQN_GFONT,
EQN_GSIZE,
EQN_BACK,
EQN_FWD,
EQN_UP,
EQN_DOWN,
EQN__MAX
};
struct eqnstr {
const char *name;
size_t sz;
};
#define STRNEQ(p1, sz1, p2, sz2) \
((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
#define EQNSTREQ(x, p, sz) \
STRNEQ((x)->name, (x)->sz, (p), (sz))
struct eqnpart {
struct eqnstr str;
int (*fp)(struct eqn_node *);
};
struct eqnsym {
struct eqnstr str;
const char *sym;
};
static enum eqn_rest eqn_box(struct eqn_node *, struct eqn_box *);
static struct eqn_box *eqn_box_alloc(struct eqn_node *,
struct eqn_box *);
static void eqn_box_free(struct eqn_box *);
static struct eqn_def *eqn_def_find(struct eqn_node *,
const char *, size_t);
static int eqn_do_gfont(struct eqn_node *);
static int eqn_do_gsize(struct eqn_node *);
static int eqn_do_define(struct eqn_node *);
static int eqn_do_ign1(struct eqn_node *);
static int eqn_do_ign2(struct eqn_node *);
static int eqn_do_tdefine(struct eqn_node *);
static int eqn_do_undef(struct eqn_node *);
static enum eqn_rest eqn_eqn(struct eqn_node *, struct eqn_box *);
static enum eqn_rest eqn_list(struct eqn_node *, struct eqn_box *);
static enum eqn_rest eqn_matrix(struct eqn_node *, struct eqn_box *);
static const char *eqn_nexttok(struct eqn_node *, size_t *);
static const char *eqn_nextrawtok(struct eqn_node *, size_t *);
static const char *eqn_next(struct eqn_node *,
char, size_t *, int);
static void eqn_rewind(struct eqn_node *);
static const struct eqnpart eqnparts[EQN__MAX] = {
{ { "define", 6 }, eqn_do_define }, /* EQN_DEFINE */
{ { "ndefine", 7 }, eqn_do_define }, /* EQN_NDEFINE */
{ { "tdefine", 7 }, eqn_do_tdefine }, /* EQN_TDEFINE */
{ { "set", 3 }, eqn_do_ign2 }, /* EQN_SET */
{ { "undef", 5 }, eqn_do_undef }, /* EQN_UNDEF */
{ { "gfont", 5 }, eqn_do_gfont }, /* EQN_GFONT */
{ { "gsize", 5 }, eqn_do_gsize }, /* EQN_GSIZE */
{ { "back", 4 }, eqn_do_ign1 }, /* EQN_BACK */
{ { "fwd", 3 }, eqn_do_ign1 }, /* EQN_FWD */
{ { "up", 2 }, eqn_do_ign1 }, /* EQN_UP */
{ { "down", 4 }, eqn_do_ign1 }, /* EQN_DOWN */
};
static const struct eqnstr eqnmarks[EQNMARK__MAX] = {
{ "", 0 }, /* EQNMARK_NONE */
{ "dot", 3 }, /* EQNMARK_DOT */
{ "dotdot", 6 }, /* EQNMARK_DOTDOT */
{ "hat", 3 }, /* EQNMARK_HAT */
{ "tilde", 5 }, /* EQNMARK_TILDE */
{ "vec", 3 }, /* EQNMARK_VEC */
{ "dyad", 4 }, /* EQNMARK_DYAD */
{ "bar", 3 }, /* EQNMARK_BAR */
{ "under", 5 }, /* EQNMARK_UNDER */
};
static const struct eqnstr eqnfonts[EQNFONT__MAX] = {
{ "", 0 }, /* EQNFONT_NONE */
{ "roman", 5 }, /* EQNFONT_ROMAN */
{ "bold", 4 }, /* EQNFONT_BOLD */
{ "fat", 3 }, /* EQNFONT_FAT */
{ "italic", 6 }, /* EQNFONT_ITALIC */
};
static const struct eqnstr eqnposs[EQNPOS__MAX] = {
{ "", 0 }, /* EQNPOS_NONE */
{ "over", 4 }, /* EQNPOS_OVER */
{ "sup", 3 }, /* EQNPOS_SUP */
{ "sub", 3 }, /* EQNPOS_SUB */
{ "to", 2 }, /* EQNPOS_TO */
{ "from", 4 }, /* EQNPOS_FROM */
};
static const struct eqnstr eqnpiles[EQNPILE__MAX] = {
{ "", 0 }, /* EQNPILE_NONE */
{ "pile", 4 }, /* EQNPILE_PILE */
{ "cpile", 5 }, /* EQNPILE_CPILE */
{ "rpile", 5 }, /* EQNPILE_RPILE */
{ "lpile", 5 }, /* EQNPILE_LPILE */
{ "col", 3 }, /* EQNPILE_COL */
{ "ccol", 4 }, /* EQNPILE_CCOL */
{ "rcol", 4 }, /* EQNPILE_RCOL */
{ "lcol", 4 }, /* EQNPILE_LCOL */
};
static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
{ { "alpha", 5 }, "*a" }, /* EQNSYM_alpha */
{ { "beta", 4 }, "*b" }, /* EQNSYM_beta */
{ { "chi", 3 }, "*x" }, /* EQNSYM_chi */
{ { "delta", 5 }, "*d" }, /* EQNSYM_delta */
{ { "epsilon", 7 }, "*e" }, /* EQNSYM_epsilon */
{ { "eta", 3 }, "*y" }, /* EQNSYM_eta */
{ { "gamma", 5 }, "*g" }, /* EQNSYM_gamma */
{ { "iota", 4 }, "*i" }, /* EQNSYM_iota */
{ { "kappa", 5 }, "*k" }, /* EQNSYM_kappa */
{ { "lambda", 6 }, "*l" }, /* EQNSYM_lambda */
{ { "mu", 2 }, "*m" }, /* EQNSYM_mu */
{ { "nu", 2 }, "*n" }, /* EQNSYM_nu */
{ { "omega", 5 }, "*w" }, /* EQNSYM_omega */
{ { "omicron", 7 }, "*o" }, /* EQNSYM_omicron */
{ { "phi", 3 }, "*f" }, /* EQNSYM_phi */
{ { "pi", 2 }, "*p" }, /* EQNSYM_pi */
{ { "psi", 2 }, "*q" }, /* EQNSYM_psi */
{ { "rho", 3 }, "*r" }, /* EQNSYM_rho */
{ { "sigma", 5 }, "*s" }, /* EQNSYM_sigma */
{ { "tau", 3 }, "*t" }, /* EQNSYM_tau */
{ { "theta", 5 }, "*h" }, /* EQNSYM_theta */
{ { "upsilon", 7 }, "*u" }, /* EQNSYM_upsilon */
{ { "xi", 2 }, "*c" }, /* EQNSYM_xi */
{ { "zeta", 4 }, "*z" }, /* EQNSYM_zeta */
{ { "DELTA", 5 }, "*D" }, /* EQNSYM_DELTA */
{ { "GAMMA", 5 }, "*G" }, /* EQNSYM_GAMMA */
{ { "LAMBDA", 6 }, "*L" }, /* EQNSYM_LAMBDA */
{ { "OMEGA", 5 }, "*W" }, /* EQNSYM_OMEGA */
{ { "PHI", 3 }, "*F" }, /* EQNSYM_PHI */
{ { "PI", 2 }, "*P" }, /* EQNSYM_PI */
{ { "PSI", 3 }, "*Q" }, /* EQNSYM_PSI */
{ { "SIGMA", 5 }, "*S" }, /* EQNSYM_SIGMA */
{ { "THETA", 5 }, "*H" }, /* EQNSYM_THETA */
{ { "UPSILON", 7 }, "*U" }, /* EQNSYM_UPSILON */
{ { "XI", 2 }, "*C" }, /* EQNSYM_XI */
{ { "inter", 5 }, "ca" }, /* EQNSYM_inter */
{ { "union", 5 }, "cu" }, /* EQNSYM_union */
{ { "prod", 4 }, "product" }, /* EQNSYM_prod */
{ { "int", 3 }, "integral" }, /* EQNSYM_int */
{ { "sum", 3 }, "sum" }, /* EQNSYM_sum */
{ { "grad", 4 }, "gr" }, /* EQNSYM_grad */
{ { "del", 3 }, "gr" }, /* EQNSYM_del */
{ { "times", 5 }, "mu" }, /* EQNSYM_times */
{ { "cdot", 4 }, "pc" }, /* EQNSYM_cdot */
{ { "nothing", 7 }, "&" }, /* EQNSYM_nothing */
{ { "approx", 6 }, "~~" }, /* EQNSYM_approx */
{ { "prime", 5 }, "aq" }, /* EQNSYM_prime */
{ { "half", 4 }, "12" }, /* EQNSYM_half */
{ { "partial", 7 }, "pd" }, /* EQNSYM_partial */
{ { "inf", 3 }, "if" }, /* EQNSYM_inf */
{ { ">>", 2 }, ">>" }, /* EQNSYM_muchgreat */
{ { "<<", 2 }, "<<" }, /* EQNSYM_muchless */
{ { "<-", 2 }, "<-" }, /* EQNSYM_larrow */
{ { "->", 2 }, "->" }, /* EQNSYM_rarrow */
{ { "+-", 2 }, "+-" }, /* EQNSYM_pm */
{ { "!=", 2 }, "!=" }, /* EQNSYM_nequal */
{ { "==", 2 }, "==" }, /* EQNSYM_equiv */
{ { "<=", 2 }, "<=" }, /* EQNSYM_lessequal */
{ { ">=", 2 }, ">=" }, /* EQNSYM_moreequal */
};
/* ARGSUSED */
enum rofferr
eqn_read(struct eqn_node **epp, int ln,
const char *p, int pos, int *offs)
{
size_t sz;
struct eqn_node *ep;
enum rofferr er;
ep = *epp;
/*
* If we're the terminating mark, unset our equation status and
* validate the full equation.
*/
if (0 == strncmp(p, ".EN", 3)) {
er = eqn_end(epp);
p += 3;
while (' ' == *p || '\t' == *p)
p++;
if ('\0' == *p)
return(er);
mandoc_msg(MANDOCERR_ARGSLOST, ep->parse, ln, pos, NULL);
return(er);
}
/*
* Build up the full string, replacing all newlines with regular
* whitespace.
*/
sz = strlen(p + pos) + 1;
ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1);
/* First invocation: nil terminate the string. */
if (0 == ep->sz)
*ep->data = '\0';
ep->sz += sz;
strlcat(ep->data, p + pos, ep->sz + 1);
strlcat(ep->data, " ", ep->sz + 1);
return(ROFF_IGN);
}
struct eqn_node *
eqn_alloc(const char *name, int pos, int line, struct mparse *parse)
{
struct eqn_node *p;
size_t sz;
const char *end;
p = mandoc_calloc(1, sizeof(struct eqn_node));
if (name && '\0' != *name) {
sz = strlen(name);
assert(sz);
do {
sz--;
end = name + (int)sz;
} while (' ' == *end || '\t' == *end);
p->eqn.name = mandoc_strndup(name, sz + 1);
}
p->parse = parse;
p->eqn.ln = line;
p->eqn.pos = pos;
p->gsize = EQN_DEFSIZE;
return(p);
}
enum rofferr
eqn_end(struct eqn_node **epp)
{
struct eqn_node *ep;
struct eqn_box *root;
enum eqn_rest c;
ep = *epp;
*epp = NULL;
ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box));
root = ep->eqn.root;
root->type = EQN_ROOT;
if (0 == ep->sz)
return(ROFF_IGN);
if (EQN_DESCOPE == (c = eqn_eqn(ep, root))) {
EQN_MSG(MANDOCERR_EQNNSCOPE, ep);
c = EQN_ERR;
}
return(EQN_EOF == c ? ROFF_EQN : ROFF_IGN);
}
static enum eqn_rest
eqn_eqn(struct eqn_node *ep, struct eqn_box *last)
{
struct eqn_box *bp;
enum eqn_rest c;
bp = eqn_box_alloc(ep, last);
bp->type = EQN_SUBEXPR;
while (EQN_OK == (c = eqn_box(ep, bp)))
/* Spin! */ ;
return(c);
}
static enum eqn_rest
eqn_matrix(struct eqn_node *ep, struct eqn_box *last)
{
struct eqn_box *bp;
const char *start;
size_t sz;
enum eqn_rest c;
bp = eqn_box_alloc(ep, last);
bp->type = EQN_MATRIX;
if (NULL == (start = eqn_nexttok(ep, &sz))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
}
if ( ! STRNEQ(start, sz, "{", 1)) {
EQN_MSG(MANDOCERR_EQNSYNT, ep);
return(EQN_ERR);
}
while (EQN_OK == (c = eqn_box(ep, bp)))
switch (bp->last->pile) {
case (EQNPILE_LCOL):
/* FALLTHROUGH */
case (EQNPILE_CCOL):
/* FALLTHROUGH */
case (EQNPILE_RCOL):
continue;
default:
EQN_MSG(MANDOCERR_EQNSYNT, ep);
return(EQN_ERR);
};
if (EQN_DESCOPE != c) {
if (EQN_EOF == c)
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
}
eqn_rewind(ep);
start = eqn_nexttok(ep, &sz);
assert(start);
if (STRNEQ(start, sz, "}", 1))
return(EQN_OK);
EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
return(EQN_ERR);
}
static enum eqn_rest
eqn_list(struct eqn_node *ep, struct eqn_box *last)
{
struct eqn_box *bp;
const char *start;
size_t sz;
enum eqn_rest c;
bp = eqn_box_alloc(ep, last);
bp->type = EQN_LIST;
if (NULL == (start = eqn_nexttok(ep, &sz))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
}
if ( ! STRNEQ(start, sz, "{", 1)) {
EQN_MSG(MANDOCERR_EQNSYNT, ep);
return(EQN_ERR);
}
while (EQN_DESCOPE == (c = eqn_eqn(ep, bp))) {
eqn_rewind(ep);
start = eqn_nexttok(ep, &sz);
assert(start);
if ( ! STRNEQ(start, sz, "above", 5))
break;
}
if (EQN_DESCOPE != c) {
if (EQN_ERR != c)
EQN_MSG(MANDOCERR_EQNSCOPE, ep);
return(EQN_ERR);
}
eqn_rewind(ep);
start = eqn_nexttok(ep, &sz);
assert(start);
if (STRNEQ(start, sz, "}", 1))
return(EQN_OK);
EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
return(EQN_ERR);
}
static enum eqn_rest
eqn_box(struct eqn_node *ep, struct eqn_box *last)
{
size_t sz;
const char *start;
char *left;
char sym[64];
enum eqn_rest c;
int i, size;
struct eqn_box *bp;
if (NULL == (start = eqn_nexttok(ep, &sz)))
return(EQN_EOF);
if (STRNEQ(start, sz, "}", 1))
return(EQN_DESCOPE);
else if (STRNEQ(start, sz, "right", 5))
return(EQN_DESCOPE);
else if (STRNEQ(start, sz, "above", 5))
return(EQN_DESCOPE);
else if (STRNEQ(start, sz, "mark", 4))
return(EQN_OK);
else if (STRNEQ(start, sz, "lineup", 6))
return(EQN_OK);
for (i = 0; i < (int)EQN__MAX; i++) {
if ( ! EQNSTREQ(&eqnparts[i].str, start, sz))
continue;
return((*eqnparts[i].fp)(ep) ?
EQN_OK : EQN_ERR);
}
if (STRNEQ(start, sz, "{", 1)) {
if (EQN_DESCOPE != (c = eqn_eqn(ep, last))) {
if (EQN_ERR != c)
EQN_MSG(MANDOCERR_EQNSCOPE, ep);
return(EQN_ERR);
}
eqn_rewind(ep);
start = eqn_nexttok(ep, &sz);
assert(start);
if (STRNEQ(start, sz, "}", 1))
return(EQN_OK);
EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
return(EQN_ERR);
}
for (i = 0; i < (int)EQNPILE__MAX; i++) {
if ( ! EQNSTREQ(&eqnpiles[i], start, sz))
continue;
if (EQN_OK == (c = eqn_list(ep, last)))
last->last->pile = (enum eqn_pilet)i;
return(c);
}
if (STRNEQ(start, sz, "matrix", 6))
return(eqn_matrix(ep, last));
if (STRNEQ(start, sz, "left", 4)) {
if (NULL == (start = eqn_nexttok(ep, &sz))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
}
left = mandoc_strndup(start, sz);
c = eqn_eqn(ep, last);
if (last->last)
last->last->left = left;
else
free(left);
if (EQN_DESCOPE != c)
return(c);
assert(last->last);
eqn_rewind(ep);
start = eqn_nexttok(ep, &sz);
assert(start);
if ( ! STRNEQ(start, sz, "right", 5))
return(EQN_DESCOPE);
if (NULL == (start = eqn_nexttok(ep, &sz))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
}
last->last->right = mandoc_strndup(start, sz);
return(EQN_OK);
}
for (i = 0; i < (int)EQNPOS__MAX; i++) {
if ( ! EQNSTREQ(&eqnposs[i], start, sz))
continue;
if (NULL == last->last) {
EQN_MSG(MANDOCERR_EQNSYNT, ep);
return(EQN_ERR);
}
last->last->pos = (enum eqn_post)i;
if (EQN_EOF == (c = eqn_box(ep, last))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
}
return(c);
}
for (i = 0; i < (int)EQNMARK__MAX; i++) {
if ( ! EQNSTREQ(&eqnmarks[i], start, sz))
continue;
if (NULL == last->last) {
EQN_MSG(MANDOCERR_EQNSYNT, ep);
return(EQN_ERR);
}
last->last->mark = (enum eqn_markt)i;
if (EQN_EOF == (c = eqn_box(ep, last))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
}
return(c);
}
for (i = 0; i < (int)EQNFONT__MAX; i++) {
if ( ! EQNSTREQ(&eqnfonts[i], start, sz))
continue;
if (EQN_EOF == (c = eqn_box(ep, last))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
} else if (EQN_OK == c)
last->last->font = (enum eqn_fontt)i;
return(c);
}
if (STRNEQ(start, sz, "size", 4)) {
if (NULL == (start = eqn_nexttok(ep, &sz))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
}
size = mandoc_strntoi(start, sz, 10);
if (EQN_EOF == (c = eqn_box(ep, last))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(EQN_ERR);
} else if (EQN_OK != c)
return(c);
last->last->size = size;
}
bp = eqn_box_alloc(ep, last);
bp->type = EQN_TEXT;
for (i = 0; i < (int)EQNSYM__MAX; i++)
if (EQNSTREQ(&eqnsyms[i].str, start, sz)) {
sym[63] = '\0';
snprintf(sym, 62, "\\[%s]", eqnsyms[i].sym);
bp->text = mandoc_strdup(sym);
return(EQN_OK);
}
bp->text = mandoc_strndup(start, sz);
return(EQN_OK);
}
void
eqn_free(struct eqn_node *p)
{
int i;
eqn_box_free(p->eqn.root);
for (i = 0; i < (int)p->defsz; i++) {
free(p->defs[i].key);
free(p->defs[i].val);
}
free(p->eqn.name);
free(p->data);
free(p->defs);
free(p);
}
static struct eqn_box *
eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
{
struct eqn_box *bp;
bp = mandoc_calloc(1, sizeof(struct eqn_box));
bp->parent = parent;
bp->size = ep->gsize;
if (NULL == parent->first)
parent->first = bp;
else
parent->last->next = bp;
parent->last = bp;
return(bp);
}
static void
eqn_box_free(struct eqn_box *bp)
{
if (bp->first)
eqn_box_free(bp->first);
if (bp->next)
eqn_box_free(bp->next);
free(bp->text);
free(bp->left);
free(bp->right);
free(bp);
}
static const char *
eqn_nextrawtok(struct eqn_node *ep, size_t *sz)
{
return(eqn_next(ep, '"', sz, 0));
}
static const char *
eqn_nexttok(struct eqn_node *ep, size_t *sz)
{
return(eqn_next(ep, '"', sz, 1));
}
static void
eqn_rewind(struct eqn_node *ep)
{
ep->cur = ep->rew;
}
static const char *
eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)
{
char *start, *next;
int q, diff, lim;
size_t ssz, dummy;
struct eqn_def *def;
if (NULL == sz)
sz = &dummy;
lim = 0;
ep->rew = ep->cur;
again:
/* Prevent self-definitions. */
if (lim >= EQN_NEST_MAX) {
EQN_MSG(MANDOCERR_ROFFLOOP, ep);
return(NULL);
}
ep->cur = ep->rew;
start = &ep->data[(int)ep->cur];
q = 0;
if ('\0' == *start)
return(NULL);
if (quote == *start) {
ep->cur++;
q = 1;
}
start = &ep->data[(int)ep->cur];
if ( ! q) {
if ('{' == *start || '}' == *start)
ssz = 1;
else
ssz = strcspn(start + 1, " ^~\"{}\t") + 1;
next = start + (int)ssz;
if ('\0' == *next)
next = NULL;
} else
next = strchr(start, quote);
if (NULL != next) {
*sz = (size_t)(next - start);
ep->cur += *sz;
if (q)
ep->cur++;
while (' ' == ep->data[(int)ep->cur] ||
'\t' == ep->data[(int)ep->cur] ||
'^' == ep->data[(int)ep->cur] ||
'~' == ep->data[(int)ep->cur])
ep->cur++;
} else {
if (q)
EQN_MSG(MANDOCERR_BADQUOTE, ep);
next = strchr(start, '\0');
*sz = (size_t)(next - start);
ep->cur += *sz;
}
/* Quotes aren't expanded for values. */
if (q || ! repl)
return(start);
if (NULL != (def = eqn_def_find(ep, start, *sz))) {
diff = def->valsz - *sz;
if (def->valsz > *sz) {
ep->sz += diff;
ep->data = mandoc_realloc(ep->data, ep->sz + 1);
ep->data[ep->sz] = '\0';
start = &ep->data[(int)ep->rew];
}
diff = def->valsz - *sz;
memmove(start + *sz + diff, start + *sz,
(strlen(start) - *sz) + 1);
memcpy(start, def->val, def->valsz);
goto again;
}
return(start);
}
static int
eqn_do_ign1(struct eqn_node *ep)
{
if (NULL == eqn_nextrawtok(ep, NULL))
EQN_MSG(MANDOCERR_EQNEOF, ep);
else
return(1);
return(0);
}
static int
eqn_do_ign2(struct eqn_node *ep)
{
if (NULL == eqn_nextrawtok(ep, NULL))
EQN_MSG(MANDOCERR_EQNEOF, ep);
else if (NULL == eqn_nextrawtok(ep, NULL))
EQN_MSG(MANDOCERR_EQNEOF, ep);
else
return(1);
return(0);
}
static int
eqn_do_tdefine(struct eqn_node *ep)
{
if (NULL == eqn_nextrawtok(ep, NULL))
EQN_MSG(MANDOCERR_EQNEOF, ep);
else if (NULL == eqn_next(ep, ep->data[(int)ep->cur], NULL, 0))
EQN_MSG(MANDOCERR_EQNEOF, ep);
else
return(1);
return(0);
}
static int
eqn_do_define(struct eqn_node *ep)
{
const char *start;
size_t sz;
struct eqn_def *def;
int i;
if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(0);
}
/*
* Search for a key that already exists.
* Create a new key if none is found.
*/
if (NULL == (def = eqn_def_find(ep, start, sz))) {
/* Find holes in string array. */
for (i = 0; i < (int)ep->defsz; i++)
if (0 == ep->defs[i].keysz)
break;
if (i == (int)ep->defsz) {
ep->defsz++;
ep->defs = mandoc_realloc
(ep->defs, ep->defsz *
sizeof(struct eqn_def));
ep->defs[i].key = ep->defs[i].val = NULL;
}
ep->defs[i].keysz = sz;
ep->defs[i].key = mandoc_realloc
(ep->defs[i].key, sz + 1);
memcpy(ep->defs[i].key, start, sz);
ep->defs[i].key[(int)sz] = '\0';
def = &ep->defs[i];
}
start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);
if (NULL == start) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(0);
}
def->valsz = sz;
def->val = mandoc_realloc(def->val, sz + 1);
memcpy(def->val, start, sz);
def->val[(int)sz] = '\0';
return(1);
}
static int
eqn_do_gfont(struct eqn_node *ep)
{
if (NULL == eqn_nextrawtok(ep, NULL)) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(0);
}
return(1);
}
static int
eqn_do_gsize(struct eqn_node *ep)
{
const char *start;
size_t sz;
if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(0);
}
ep->gsize = mandoc_strntoi(start, sz, 10);
return(1);
}
static int
eqn_do_undef(struct eqn_node *ep)
{
const char *start;
struct eqn_def *def;
size_t sz;
if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
EQN_MSG(MANDOCERR_EQNEOF, ep);
return(0);
} else if (NULL != (def = eqn_def_find(ep, start, sz)))
def->keysz = 0;
return(1);
}
static struct eqn_def *
eqn_def_find(struct eqn_node *ep, const char *key, size_t sz)
{
int i;
for (i = 0; i < (int)ep->defsz; i++)
if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
ep->defs[i].keysz, key, sz))
return(&ep->defs[i]);
return(NULL);
}

81
contrib/mdocml/eqn_html.c Normal file
View File

@ -0,0 +1,81 @@
/* $Id: eqn_html.c,v 1.2 2011/07/24 10:09:03 kristaps Exp $ */
/*
* Copyright (c) 2011 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 "out.h"
#include "html.h"
static const enum htmltag fontmap[EQNFONT__MAX] = {
TAG_SPAN, /* EQNFONT_NONE */
TAG_SPAN, /* EQNFONT_ROMAN */
TAG_B, /* EQNFONT_BOLD */
TAG_B, /* EQNFONT_FAT */
TAG_I /* EQNFONT_ITALIC */
};
static void eqn_box(struct html *, const struct eqn_box *);
void
print_eqn(struct html *p, const struct eqn *ep)
{
struct htmlpair tag;
struct tag *t;
PAIR_CLASS_INIT(&tag, "eqn");
t = print_otag(p, TAG_SPAN, 1, &tag);
p->flags |= HTML_NONOSPACE;
eqn_box(p, ep->root);
p->flags &= ~HTML_NONOSPACE;
print_tagq(p, t);
}
static void
eqn_box(struct html *p, const struct eqn_box *bp)
{
struct tag *t;
t = EQNFONT_NONE == bp->font ? NULL :
print_otag(p, fontmap[(int)bp->font], 0, NULL);
if (bp->left)
print_text(p, bp->left);
if (bp->text)
print_text(p, bp->text);
if (bp->first)
eqn_box(p, bp->first);
if (NULL != t)
print_tagq(p, t);
if (bp->right)
print_text(p, bp->right);
if (bp->next)
eqn_box(p, bp->next);
}

76
contrib/mdocml/eqn_term.c Normal file
View File

@ -0,0 +1,76 @@
/* $Id: eqn_term.c,v 1.4 2011/07/24 10:09:03 kristaps Exp $ */
/*
* Copyright (c) 2011 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 "out.h"
#include "term.h"
static const enum termfont fontmap[EQNFONT__MAX] = {
TERMFONT_NONE, /* EQNFONT_NONE */
TERMFONT_NONE, /* EQNFONT_ROMAN */
TERMFONT_BOLD, /* EQNFONT_BOLD */
TERMFONT_BOLD, /* EQNFONT_FAT */
TERMFONT_UNDER /* EQNFONT_ITALIC */
};
static void eqn_box(struct termp *, const struct eqn_box *);
void
term_eqn(struct termp *p, const struct eqn *ep)
{
p->flags |= TERMP_NONOSPACE;
eqn_box(p, ep->root);
term_word(p, " ");
p->flags &= ~TERMP_NONOSPACE;
}
static void
eqn_box(struct termp *p, const struct eqn_box *bp)
{
if (EQNFONT_NONE != bp->font)
term_fontpush(p, fontmap[(int)bp->font]);
if (bp->left)
term_word(p, bp->left);
if (EQN_SUBEXPR == bp->type)
term_word(p, "(");
if (bp->text)
term_word(p, bp->text);
if (bp->first)
eqn_box(p, bp->first);
if (EQN_SUBEXPR == bp->type)
term_word(p, ")");
if (bp->right)
term_word(p, bp->right);
if (EQNFONT_NONE != bp->font)
term_fontpop(p);
if (bp->next)
eqn_box(p, bp->next);
}

View File

@ -0,0 +1,110 @@
/* $Id: example.style.css,v 1.49 2011/12/15 12:18:57 kristaps Exp $ */
/*
* This is an example style-sheet provided for mandoc(1) and the -Thtml
* or -Txhtml output mode.
* It mimics the appearance of the legacy man.cgi output.
* See mdoc(7) and man(7) for macro explanations.
*/
div.mandoc { min-width: 102ex;
width: 102ex;
font-family: monospace; } /* This is the outer node of all mandoc -T[x]html documents. */
div.mandoc h1 { margin-bottom: 0ex; font-size: inherit; margin-left: -4ex; } /* Section header (Sh, SH). */
div.mandoc h2 { margin-bottom: 0ex; font-size: inherit; margin-left: -2ex; } /* Sub-section header (Ss, SS). */
div.mandoc table { width: 100%; margin-top: 0ex; margin-bottom: 0ex; } /* All tables. */
div.mandoc td { vertical-align: top; } /* All table cells. */
div.mandoc p { } /* Paragraph: Pp, Lp. */
div.mandoc blockquote { margin-left: 5ex; margin-top: 0ex; margin-bottom: 0ex; } /* D1, Dl. */
div.mandoc div.section { margin-bottom: 2ex; margin-left: 5ex; } /* Sections (Sh, SH). */
div.mandoc div.subsection { } /* Sub-sections (Ss, SS). */
div.mandoc table.synopsis { } /* SYNOPSIS section table. */
div.mandoc table.foot { } /* Document footer. */
div.mandoc td.foot-date { width: 50%; } /* Document footer: date. */
div.mandoc td.foot-os { width: 50%; text-align: right; } /* Document footer: OS/source. */
div.mandoc table.head { } /* Document header. */
div.mandoc td.head-ltitle { width: 10%; } /* Document header: left-title. */
div.mandoc td.head-vol { width: 80%; text-align: center; } /* Document header: volume. */
div.mandoc td.head-rtitle { width: 10%; text-align: right; } /* Document header: right-title. */
div.mandoc .display { } /* All Bd, D1, Dl. */
div.mandoc .list { } /* All Bl. */
div.mandoc i { } /* Italic: BI, IB, I, (implicit). */
div.mandoc b { } /* Bold: SB, BI, IB, BR, RB, B, (implicit). */
div.mandoc small { } /* Small: SB, SM. */
div.mandoc .emph { font-style: italic; font-weight: normal; } /* Emphasis: Em, Bl -emphasis. */
div.mandoc .symb { font-style: normal; font-weight: bold; } /* Symbolic: Sy, Ms, Bf -symbolic. */
div.mandoc .lit { font-style: normal; font-weight: normal; font-family: monospace; } /* Literal: Dl, Li, Ql, Bf -literal, Bl -literal, Bl -unfilled. */
div.mandoc i.addr { font-weight: normal; } /* Address (Ad). */
div.mandoc i.arg { font-weight: normal; } /* Command argument (Ar). */
div.mandoc span.author { } /* Author name (An). */
div.mandoc b.cmd { font-style: normal; } /* Command (Cm). */
div.mandoc b.config { font-style: normal; } /* Config statement (Cd). */
div.mandoc span.define { } /* Defines (Dv). */
div.mandoc span.desc { } /* Nd. After em-dash. */
div.mandoc b.diag { font-style: normal; } /* Diagnostic (Bl -diag). */
div.mandoc span.env { } /* Environment variables (Ev). */
div.mandoc span.errno { } /* Error string (Er). */
div.mandoc i.farg { font-weight: normal; } /* Function argument (Fa, Fn). */
div.mandoc i.file { font-weight: normal; } /* File (Pa). */
div.mandoc b.flag { font-style: normal; } /* Flag (Fl, Cm). */
div.mandoc b.fname { font-style: normal; } /* Function name (Fa, Fn, Rv). */
div.mandoc i.ftype { font-weight: normal; } /* Function types (Ft, Fn). */
div.mandoc b.includes { font-style: normal; } /* Header includes (In). */
div.mandoc span.lib { } /* Library (Lb). */
div.mandoc i.link-sec { font-weight: normal; } /* Section links (Sx). */
div.mandoc b.macro { font-style: normal; } /* Macro-ish thing (Fd). */
div.mandoc b.name { font-style: normal; } /* Name of utility (Nm). */
div.mandoc span.opt { } /* Options (Op, Oo/Oc). */
div.mandoc span.ref { } /* Citations (Rs). */
div.mandoc span.ref-auth { } /* Reference author (%A). */
div.mandoc i.ref-book { font-weight: normal; } /* Reference book (%B). */
div.mandoc span.ref-city { } /* Reference city (%C). */
div.mandoc span.ref-date { } /* Reference date (%D). */
div.mandoc i.ref-issue { font-weight: normal; } /* Reference issuer/publisher (%I). */
div.mandoc i.ref-jrnl { font-weight: normal; } /* Reference journal (%J). */
div.mandoc span.ref-num { } /* Reference number (%N). */
div.mandoc span.ref-opt { } /* Reference optionals (%O). */
div.mandoc span.ref-page { } /* Reference page (%P). */
div.mandoc span.ref-corp { } /* Reference corporate/foreign author (%Q). */
div.mandoc span.ref-rep { } /* Reference report (%R). */
div.mandoc span.ref-title { text-decoration: underline; } /* Reference title (%T). */
div.mandoc span.ref-vol { } /* Reference volume (%V). */
div.mandoc span.type { font-style: italic; font-weight: normal; } /* Variable types (Vt). */
div.mandoc span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */
div.mandoc b.utility { font-style: normal; } /* Name of utility (Ex). */
div.mandoc b.var { font-style: normal; } /* Variables (Rv). */
div.mandoc a.link-ext { } /* Off-site link (Lk). */
div.mandoc a.link-includes { } /* Include-file link (In). */
div.mandoc a.link-mail { } /* Mailto links (Mt). */
div.mandoc a.link-man { } /* Manual links (Xr). */
div.mandoc a.link-ref { } /* Reference section links (%Q). */
div.mandoc a.link-sec { } /* Section links (Sx). */
div.mandoc dl.list-diag { } /* Formatting for lists. See mdoc(7). */
div.mandoc dt.list-diag { }
div.mandoc dd.list-diag { }
div.mandoc dl.list-hang { }
div.mandoc dt.list-hang { }
div.mandoc dd.list-hang { }
div.mandoc dl.list-inset { }
div.mandoc dt.list-inset { }
div.mandoc dd.list-inset { }
div.mandoc dl.list-ohang { }
div.mandoc dt.list-ohang { }
div.mandoc dd.list-ohang { margin-left: 0ex; }
div.mandoc dl.list-tag { }
div.mandoc dt.list-tag { }
div.mandoc dd.list-tag { }
div.mandoc table.list-col { }
div.mandoc tr.list-col { }
div.mandoc td.list-col { }
div.mandoc ul.list-bul { list-style-type: disc; padding-left: 1em; }
div.mandoc li.list-bul { }
div.mandoc ul.list-dash { list-style-type: none; padding-left: 0em; }
div.mandoc li.list-dash:before { content: "\2014 "; }
div.mandoc ul.list-hyph { list-style-type: none; padding-left: 0em; }
div.mandoc li.list-hyph:before { content: "\2013 "; }
div.mandoc ul.list-item { list-style-type: none; padding-left: 0em; }
div.mandoc li.list-item { }
div.mandoc ol.list-enum { padding-left: 2em; }
div.mandoc li.list-enum { }
div.mandoc span.eqn { } /* Equation modes. See eqn(7). */
div.mandoc table.tbl { } /* Table modes. See tbl(7). */

BIN
contrib/mdocml/external.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

699
contrib/mdocml/html.c Normal file
View File

@ -0,0 +1,699 @@
/* $Id: html.c,v 1.150 2011/10/05 21:35:17 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 "libmandoc.h"
#include "out.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 */
{"tbody", HTML_CLRLINE}, /* TAG_TBODY */
{"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 */
{"dl", HTML_CLRLINE}, /* TAG_DL */
{"dt", HTML_CLRLINE}, /* TAG_DT */
{"dd", HTML_CLRLINE}, /* TAG_DD */
{"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */
{"p", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */
{"pre", HTML_CLRLINE }, /* TAG_PRE */
{"b", 0 }, /* TAG_B */
{"i", 0 }, /* TAG_I */
{"code", 0 }, /* TAG_CODE */
{"small", 0 }, /* TAG_SMALL */
};
static const char *const htmlattrs[ATTR_MAX] = {
"http-equiv", /* ATTR_HTTPEQUIV */
"content", /* ATTR_CONTENT */
"name", /* ATTR_NAME */
"rel", /* ATTR_REL */
"href", /* ATTR_HREF */
"type", /* ATTR_TYPE */
"media", /* ATTR_MEDIA */
"class", /* ATTR_CLASS */
"style", /* ATTR_STYLE */
"width", /* ATTR_WIDTH */
"id", /* ATTR_ID */
"summary", /* ATTR_SUMMARY */
"align", /* ATTR_ALIGN */
"colspan", /* ATTR_COLSPAN */
};
static const char *const roffscales[SCALE_MAX] = {
"cm", /* SCALE_CM */
"in", /* SCALE_IN */
"pc", /* SCALE_PC */
"pt", /* SCALE_PT */
"em", /* SCALE_EM */
"em", /* SCALE_MM */
"ex", /* SCALE_EN */
"ex", /* SCALE_BU */
"em", /* SCALE_VS */
"ex", /* SCALE_FS */
};
static void bufncat(struct html *, const char *, size_t);
static void print_ctag(struct html *, enum htmltag);
static int print_encode(struct html *, const char *, int);
static void print_metaf(struct html *, enum mandoc_esc);
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[5];
char *v;
toks[0] = "style";
toks[1] = "man";
toks[2] = "includes";
toks[3] = "fragment";
toks[4] = NULL;
h = mandoc_calloc(1, sizeof(struct html));
h->type = type;
h->tags.head = NULL;
h->symtab = mchars_alloc();
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;
case (3):
h->oflags |= HTML_FRAGMENT;
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 html *h;
h = (struct html *)p;
while ((tag = h->tags.head) != NULL) {
h->tags.head = tag->next;
free(tag);
}
if (h->symtab)
mchars_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_metaf(struct html *h, enum mandoc_esc deco)
{
enum htmlfont font;
switch (deco) {
case (ESCAPE_FONTPREV):
font = h->metal;
break;
case (ESCAPE_FONTITALIC):
font = HTMLFONT_ITALIC;
break;
case (ESCAPE_FONTBOLD):
font = HTMLFONT_BOLD;
break;
case (ESCAPE_FONT):
/* FALLTHROUGH */
case (ESCAPE_FONTROMAN):
font = HTMLFONT_NONE;
break;
default:
abort();
/* NOTREACHED */
}
if (h->metaf) {
print_tagq(h, h->metaf);
h->metaf = NULL;
}
h->metal = h->metac;
h->metac = font;
if (HTMLFONT_NONE != font)
h->metaf = HTMLFONT_BOLD == font ?
print_otag(h, TAG_B, 0, NULL) :
print_otag(h, TAG_I, 0, NULL);
}
int
html_strlen(const char *cp)
{
int ssz, sz;
const char *seq, *p;
/*
* Account for escaped sequences within string length
* calculations. This follows the logic in term_strlen() as we
* must calculate the width of produced strings.
* Assume that characters are always width of "1". This is
* hacky, but it gets the job done for approximation of widths.
*/
sz = 0;
while (NULL != (p = strchr(cp, '\\'))) {
sz += (int)(p - cp);
++cp;
switch (mandoc_escape(&cp, &seq, &ssz)) {
case (ESCAPE_ERROR):
return(sz);
case (ESCAPE_UNICODE):
/* FALLTHROUGH */
case (ESCAPE_NUMBERED):
/* FALLTHROUGH */
case (ESCAPE_SPECIAL):
sz++;
break;
default:
break;
}
}
assert(sz >= 0);
return(sz + strlen(cp));
}
static int
print_encode(struct html *h, const char *p, int norecurse)
{
size_t sz;
int c, len, nospace;
const char *seq;
enum mandoc_esc esc;
static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' };
nospace = 0;
while ('\0' != *p) {
sz = strcspn(p, rejs);
fwrite(p, 1, sz, stdout);
p += (int)sz;
if ('\0' == *p)
break;
switch (*p++) {
case ('<'):
printf("&lt;");
continue;
case ('>'):
printf("&gt;");
continue;
case ('&'):
printf("&amp;");
continue;
case (ASCII_HYPH):
putchar('-');
continue;
default:
break;
}
esc = mandoc_escape(&p, &seq, &len);
if (ESCAPE_ERROR == esc)
break;
switch (esc) {
case (ESCAPE_UNICODE):
/* Skip passed "u" header. */
c = mchars_num2uc(seq + 1, len - 1);
if ('\0' != c)
printf("&#x%x;", c);
break;
case (ESCAPE_NUMBERED):
c = mchars_num2char(seq, len);
if ('\0' != c)
putchar(c);
break;
case (ESCAPE_SPECIAL):
c = mchars_spec2cp(h->symtab, seq, len);
if (c > 0)
printf("&#%d;", c);
else if (-1 == c && 1 == len)
putchar((int)*seq);
break;
case (ESCAPE_FONT):
/* FALLTHROUGH */
case (ESCAPE_FONTPREV):
/* FALLTHROUGH */
case (ESCAPE_FONTBOLD):
/* FALLTHROUGH */
case (ESCAPE_FONTITALIC):
/* FALLTHROUGH */
case (ESCAPE_FONTROMAN):
if (norecurse)
break;
print_metaf(h, esc);
break;
case (ESCAPE_NOSPACE):
if ('\0' == *p)
nospace = 1;
break;
default:
break;
}
}
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 = mandoc_malloc(sizeof(struct tag));
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)) {
/* Manage keeps! */
if ( ! (HTML_KEEP & h->flags)) {
if (HTML_PREKEEP & h->flags)
h->flags |= HTML_KEEP;
putchar(' ');
} else
printf("&#160;");
}
if ( ! (h->flags & HTML_NONOSPACE))
h->flags &= ~HTML_NOSPACE;
else
h->flags |= HTML_NOSPACE;
/* 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");
}
/* Accommodate 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;
if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags)
putchar('\n');
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)
{
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:
puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
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 *word)
{
if ( ! (HTML_NOSPACE & h->flags)) {
/* Manage keeps! */
if ( ! (HTML_KEEP & h->flags)) {
if (HTML_PREKEEP & h->flags)
h->flags |= HTML_KEEP;
putchar(' ');
} else
printf("&#160;");
}
assert(NULL == h->metaf);
if (HTMLFONT_NONE != h->metac)
h->metaf = HTMLFONT_BOLD == h->metac ?
print_otag(h, TAG_B, 0, NULL) :
print_otag(h, TAG_I, 0, NULL);
assert(word);
if ( ! print_encode(h, word, 0)) {
if ( ! (h->flags & HTML_NONOSPACE))
h->flags &= ~HTML_NOSPACE;
} else
h->flags |= HTML_NOSPACE;
if (h->metaf) {
print_tagq(h, h->metaf);
h->metaf = NULL;
}
h->flags &= ~HTML_IGNDELIM;
}
void
print_tagq(struct html *h, const struct tag *until)
{
struct tag *tag;
while ((tag = h->tags.head) != NULL) {
/*
* Remember to close out and nullify the current
* meta-font and table, if applicable.
*/
if (tag == h->metaf)
h->metaf = NULL;
if (tag == h->tblt)
h->tblt = 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;
/*
* Remember to close out and nullify the current
* meta-font and table, if applicable.
*/
if (tag == h->metaf)
h->metaf = NULL;
if (tag == h->tblt)
h->tblt = 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);
bufcat(h, ":");
bufcat(h, val);
bufcat(h, ";");
}
void
bufcat(struct html *h, const char *p)
{
h->buflen = strlcat(h->buf, p, BUFSIZ);
assert(h->buflen < BUFSIZ);
}
void
bufcat_fmt(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);
}
static void
bufncat(struct html *h, const char *p, size_t sz)
{
assert(h->buflen + sz + 1 < BUFSIZ);
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;
bufinit(h);
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;
bufinit(h);
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'):
bufcat_fmt(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;
v = su->scale;
if (SCALE_MM == su->unit && 0.0 == (v /= 100.0))
v = 1.0;
bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]);
}
void
bufcat_id(struct html *h, const char *src)
{
/* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
while ('\0' != *src)
bufcat_fmt(h, "%.2x", *src++);
}

164
contrib/mdocml/html.h Normal file
View File

@ -0,0 +1,164 @@
/* $Id: html.h,v 1.47 2011/10/05 21:35:17 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 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_TBODY,
TAG_COL,
TAG_TR,
TAG_TD,
TAG_LI,
TAG_UL,
TAG_OL,
TAG_DL,
TAG_DT,
TAG_DD,
TAG_BLOCKQUOTE,
TAG_P,
TAG_PRE,
TAG_B,
TAG_I,
TAG_CODE,
TAG_SMALL,
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_ID,
ATTR_SUMMARY,
ATTR_ALIGN,
ATTR_COLSPAN,
ATTR_MAX
};
enum htmlfont {
HTMLFONT_NONE = 0,
HTMLFONT_BOLD,
HTMLFONT_ITALIC,
HTMLFONT_MAX
};
struct tag {
struct tag *next;
enum htmltag tag;
};
struct tagq {
struct tag *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) /* suppress next space */
#define HTML_IGNDELIM (1 << 1)
#define HTML_KEEP (1 << 2)
#define HTML_PREKEEP (1 << 3)
#define HTML_NONOSPACE (1 << 4) /* never add spaces */
#define HTML_LITERAL (1 << 5) /* literal (e.g., <PRE>) context */
struct tagq tags; /* stack of open tags */
struct rofftbl tbl; /* current table */
struct tag *tblt; /* current open table scope */
struct mchars *symtab; /* character-escapes */
char *base_man; /* base for manpage href */
char *base_includes; /* base for include href */
char *style; /* style-sheet URI */
char buf[BUFSIZ]; /* see bufcat and friends */
size_t buflen;
struct tag *metaf; /* current open font scope */
enum htmlfont metal; /* last used font */
enum htmlfont metac; /* current font mode */
enum htmltype type; /* output media type */
int oflags; /* output options */
#define HTML_FRAGMENT (1 << 0) /* don't emit HTML/HEAD/BODY */
};
void print_gen_decls(struct html *);
void print_gen_head(struct html *);
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 print_tblclose(struct html *);
void print_tbl(struct html *, const struct tbl_span *);
void print_eqn(struct html *, const struct eqn *);
void bufcat_fmt(struct html *, const char *, ...);
void bufcat(struct html *, const char *);
void bufcat_id(struct html *, const char *);
void bufcat_style(struct html *,
const char *, const char *);
void bufcat_su(struct html *, const char *,
const struct roffsu *);
void bufinit(struct html *);
void buffmt_man(struct html *,
const char *, const char *);
void buffmt_includes(struct html *, const char *);
int html_strlen(const char *);
__END_DECLS
#endif /*!HTML_H*/

39
contrib/mdocml/lib.c Normal file
View File

@ -0,0 +1,39 @@
/* $Id: lib.c,v 1.9 2011/03/22 14:33:05 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 "mdoc.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);
}

99
contrib/mdocml/lib.in Normal file
View File

@ -0,0 +1,99 @@
/* $Id: lib.in,v 1.13 2012/01/28 23:46:28 joerg 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("libdwarf", "DWARF Access Library (libdwarf, \\-ldwarf)")
LINE("libedit", "Command Line Editor Library (libedit, \\-ledit)")
LINE("libelf", "ELF Access 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("libisns", "Internet Storage Name Service Library (libisns, \\-lisns)")
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("libppath", "Property-List Paths Library (libppath, \\-lppath)")
LINE("libprop", "Property Container Object Library (libprop, \\-lprop)")
LINE("libpthread", "POSIX Threads Library (libpthread, \\-lpthread)")
LINE("libpuffs", "puffs Convenience Library (libpuffs, \\-lpuffs)")
LINE("libquota", "Disk Quota Access and Control Library (libquota, \\-lquota)")
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("libsaslc", "Simple Authentication and Security Layer client library (libsaslc, \\-lsaslc)")
LINE("libsdp", "Bluetooth Service Discovery Protocol User Library (libsdp, \\-lsdp)")
LINE("libssp", "Buffer Overflow Protection Library (libssp, \\-lssp)")
LINE("libSystem", "System Library (libSystem, \\-lSystem)")
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)")

85
contrib/mdocml/libman.h Normal file
View File

@ -0,0 +1,85 @@
/* $Id: libman.h,v 1.55 2011/11/07 01:24:40 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 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
enum man_next {
MAN_NEXT_SIBLING = 0,
MAN_NEXT_CHILD
};
struct man {
struct mparse *parse; /* parse pointer */
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)
#define MAN_NEWLINE (1 << 6) /* first macro/text in a line */
enum man_next next; /* where to put the next node */
struct man_node *last; /* the last parsed node */
struct man_node *first; /* the first parsed node */
struct man_meta meta; /* document meta-data */
struct roff *roff;
};
#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(). */
#define MAN_BSCOPE (1 << 5) /* Break BLINE scope. */
};
extern const struct man_macro *const man_macros;
__BEGIN_DECLS
#define man_pmsg(m, l, p, t) \
mandoc_msg((t), (m)->parse, (l), (p), NULL)
#define man_nmsg(m, n, t) \
mandoc_msg((t), (m)->parse, (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_tail_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_valid_post(struct man *);
int man_valid_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,92 @@
/* $Id: libmandoc.h,v 1.29 2011/12/02 01:37:14 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 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
enum rofferr {
ROFF_CONT, /* continue processing line */
ROFF_RERUN, /* re-run roff interpreter with offset */
ROFF_APPEND, /* re-run main parser, appending next line */
ROFF_REPARSE, /* re-run main parser on the result */
ROFF_SO, /* include another file */
ROFF_IGN, /* ignore current line */
ROFF_TBL, /* a table row was successfully parsed */
ROFF_EQN, /* an equation was successfully parsed */
ROFF_ERR /* badness: puke and stop */
};
enum regs {
REG_nS = 0, /* nS register */
REG__MAX
};
__BEGIN_DECLS
struct roff;
struct mdoc;
struct man;
void mandoc_msg(enum mandocerr, struct mparse *,
int, int, const char *);
void mandoc_vmsg(enum mandocerr, struct mparse *,
int, int, const char *, ...);
char *mandoc_getarg(struct mparse *, char **, int, int *);
char *mandoc_normdate(struct mparse *, char *, int, int);
int mandoc_eos(const char *, size_t, int);
int mandoc_getcontrol(const char *, int *);
int mandoc_strntoi(const char *, size_t, int);
const char *mandoc_a2msec(const char*);
void mdoc_free(struct mdoc *);
struct mdoc *mdoc_alloc(struct roff *, struct mparse *);
void mdoc_reset(struct mdoc *);
int mdoc_parseln(struct mdoc *, int, char *, int);
int mdoc_endparse(struct mdoc *);
int mdoc_addspan(struct mdoc *, const struct tbl_span *);
int mdoc_addeqn(struct mdoc *, const struct eqn *);
void man_free(struct man *);
struct man *man_alloc(struct roff *, struct mparse *);
void man_reset(struct man *);
int man_parseln(struct man *, int, char *, int);
int man_endparse(struct man *);
int man_addspan(struct man *, const struct tbl_span *);
int man_addeqn(struct man *, const struct eqn *);
void roff_free(struct roff *);
struct roff *roff_alloc(struct mparse *);
void roff_reset(struct roff *);
enum rofferr roff_parseln(struct roff *, int,
char **, size_t *, int, int *);
void roff_endparse(struct roff *);
int roff_regisset(const struct roff *, enum regs);
unsigned int roff_regget(const struct roff *, enum regs);
void roff_regunset(struct roff *, enum regs);
char *roff_strdup(const struct roff *, const char *);
#if 0
char roff_eqndelim(const struct roff *);
void roff_openeqn(struct roff *, const char *,
int, int, const char *);
int roff_closeeqn(struct roff *);
#endif
const struct tbl_span *roff_span(const struct roff *);
const struct eqn *roff_eqn(const struct roff *);
__END_DECLS
#endif /*!LIBMANDOC_H*/

141
contrib/mdocml/libmdoc.h Normal file
View File

@ -0,0 +1,141 @@
/* $Id: libmdoc.h,v 1.78 2011/12/02 01:37:14 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 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
enum mdoc_next {
MDOC_NEXT_SIBLING = 0,
MDOC_NEXT_CHILD
};
struct mdoc {
struct mparse *parse; /* parse pointer */
int flags; /* parse 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 */
#define MDOC_SYNOPSIS (1 << 7) /* SYNOPSIS-style formatting */
enum mdoc_next next; /* where to put the next node */
struct mdoc_node *last; /* the last node parsed */
struct mdoc_node *first; /* the first node parsed */
struct mdoc_meta meta; /* document meta-data */
enum mdoc_sec lastnamed;
enum mdoc_sec lastsec;
struct roff *roff;
};
#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, /* end-of-line */
ARGS_WORD, /* normal word */
ARGS_PUNCT, /* series of punctuation */
ARGS_QWORD, /* quoted word */
ARGS_PHRASE, /* Ta'd phrase (-column) */
ARGS_PPHRASE, /* tabbed phrase (-column) */
ARGS_PEND /* last phrase (-column) */
};
enum margverr {
ARGV_ERROR,
ARGV_EOLN, /* end of line */
ARGV_ARG, /* valid argument */
ARGV_WORD /* normal word (or bad argument---same thing) */
};
/*
* A punctuation delimiter is opening, closing, or "middle mark"
* punctuation. These govern spacing.
* Opening punctuation (e.g., the opening parenthesis) suppresses the
* following space; closing punctuation (e.g., the closing parenthesis)
* suppresses the leading space; middle punctuation (e.g., the vertical
* bar) can do either. The middle punctuation delimiter bends the rules
* depending on usage.
*/
enum mdelim {
DELIM_NONE = 0,
DELIM_OPEN,
DELIM_MIDDLE,
DELIM_CLOSE,
DELIM_MAX
};
extern const struct mdoc_macro *const mdoc_macros;
__BEGIN_DECLS
#define mdoc_pmsg(m, l, p, t) \
mandoc_msg((t), (m)->parse, (l), (p), NULL)
#define mdoc_nmsg(m, n, t) \
mandoc_msg((t), (m)->parse, (n)->line, (n)->pos, NULL)
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);
int mdoc_endbody_alloc(struct mdoc *m, int line, int pos,
enum mdoct tok, struct mdoc_node *body,
enum mdoc_endbody end);
void mdoc_node_delete(struct mdoc *, struct mdoc_node *);
void mdoc_hash_init(void);
enum mdoct mdoc_hash_find(const char *);
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 *);
int mdoc_valid_pre(struct mdoc *, struct mdoc_node *);
int mdoc_valid_post(struct mdoc *);
enum margverr mdoc_argv(struct mdoc *, int, enum mdoct,
struct mdoc_arg **, int *, char *);
void mdoc_argv_free(struct mdoc_arg *);
enum margserr mdoc_args(struct mdoc *, int,
int *, char *, enum mdoct, char **);
enum margserr mdoc_zargs(struct mdoc *, int,
int *, char *, char **);
int mdoc_macroend(struct mdoc *);
enum mdelim mdoc_isdelim(const char *);
__END_DECLS
#endif /*!LIBMDOC_H*/

84
contrib/mdocml/libroff.h Normal file
View File

@ -0,0 +1,84 @@
/* $Id: libroff.h,v 1.27 2011/07/25 15:37:00 kristaps Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 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 LIBROFF_H
#define LIBROFF_H
__BEGIN_DECLS
enum tbl_part {
TBL_PART_OPTS, /* in options (first line) */
TBL_PART_LAYOUT, /* describing layout */
TBL_PART_DATA, /* creating data rows */
TBL_PART_CDATA /* continue previous row */
};
struct tbl_node {
struct mparse *parse; /* parse point */
int pos; /* invocation column */
int line; /* invocation line */
enum tbl_part part;
struct tbl opts;
struct tbl_row *first_row;
struct tbl_row *last_row;
struct tbl_span *first_span;
struct tbl_span *current_span;
struct tbl_span *last_span;
struct tbl_head *first_head;
struct tbl_head *last_head;
struct tbl_node *next;
};
struct eqn_node {
struct eqn_def *defs;
size_t defsz;
char *data;
size_t rew;
size_t cur;
size_t sz;
int gsize;
struct eqn eqn;
struct mparse *parse;
struct eqn_node *next;
};
struct eqn_def {
char *key;
size_t keysz;
char *val;
size_t valsz;
};
struct tbl_node *tbl_alloc(int, int, struct mparse *);
void tbl_restart(int, int, struct tbl_node *);
void tbl_free(struct tbl_node *);
void tbl_reset(struct tbl_node *);
enum rofferr tbl_read(struct tbl_node *, int, const char *, int);
int tbl_option(struct tbl_node *, int, const char *);
int tbl_layout(struct tbl_node *, int, const char *);
int tbl_data(struct tbl_node *, int, const char *);
int tbl_cdata(struct tbl_node *, int, const char *);
const struct tbl_span *tbl_span(struct tbl_node *);
void tbl_end(struct tbl_node **);
struct eqn_node *eqn_alloc(const char *, int, int, struct mparse *);
enum rofferr eqn_end(struct eqn_node **);
void eqn_free(struct eqn_node *);
enum rofferr eqn_read(struct eqn_node **, int,
const char *, int, int *);
__END_DECLS
#endif /*LIBROFF_H*/

401
contrib/mdocml/main.c Normal file
View File

@ -0,0 +1,401 @@
/* $Id: main.c,v 1.165 2011/10/06 22:29:12 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mandoc.h"
#include "main.h"
#include "mdoc.h"
#include "man.h"
#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 *);
enum outt {
OUTT_ASCII = 0, /* -Tascii */
OUTT_LOCALE, /* -Tlocale */
OUTT_UTF8, /* -Tutf8 */
OUTT_TREE, /* -Ttree */
OUTT_MAN, /* -Tman */
OUTT_HTML, /* -Thtml */
OUTT_XHTML, /* -Txhtml */
OUTT_LINT, /* -Tlint */
OUTT_PS, /* -Tps */
OUTT_PDF /* -Tpdf */
};
struct curparse {
struct mparse *mp;
enum mandoclevel wlevel; /* ignore messages below this */
int wstop; /* stop after a file with a warning */
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 int moptions(enum mparset *, char *);
static void mmsg(enum mandocerr, enum mandoclevel,
const char *, int, int, const char *);
static void parse(struct curparse *, int,
const char *, enum mandoclevel *);
static int toptions(struct curparse *, char *);
static void usage(void) __attribute__((noreturn));
static void version(void) __attribute__((noreturn));
static int woptions(struct curparse *, char *);
static const char *progname;
int
main(int argc, char *argv[])
{
int c;
struct curparse curp;
enum mparset type;
enum mandoclevel rc;
progname = strrchr(argv[0], '/');
if (progname == NULL)
progname = argv[0];
else
++progname;
memset(&curp, 0, sizeof(struct curparse));
type = MPARSE_AUTO;
curp.outtype = OUTT_ASCII;
curp.wlevel = MANDOCLEVEL_FATAL;
/* LINTED */
while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
switch (c) {
case ('m'):
if ( ! moptions(&type, optarg))
return((int)MANDOCLEVEL_BADARG);
break;
case ('O'):
(void)strlcat(curp.outopts, optarg, BUFSIZ);
(void)strlcat(curp.outopts, ",", BUFSIZ);
break;
case ('T'):
if ( ! toptions(&curp, optarg))
return((int)MANDOCLEVEL_BADARG);
break;
case ('W'):
if ( ! woptions(&curp, optarg))
return((int)MANDOCLEVEL_BADARG);
break;
case ('V'):
version();
/* NOTREACHED */
default:
usage();
/* NOTREACHED */
}
curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp);
/*
* Conditionally start up the lookaside buffer before parsing.
*/
if (OUTT_MAN == curp.outtype)
mparse_keep(curp.mp);
argc -= optind;
argv += optind;
rc = MANDOCLEVEL_OK;
if (NULL == *argv)
parse(&curp, STDIN_FILENO, "<stdin>", &rc);
while (*argv) {
parse(&curp, -1, *argv, &rc);
if (MANDOCLEVEL_OK != rc && curp.wstop)
break;
++argv;
}
if (curp.outfree)
(*curp.outfree)(curp.outdata);
if (curp.mp)
mparse_free(curp.mp);
return((int)rc);
}
static void
version(void)
{
printf("%s %s\n", progname, VERSION);
exit((int)MANDOCLEVEL_OK);
}
static void
usage(void)
{
fprintf(stderr, "usage: %s "
"[-V] "
"[-foption] "
"[-mformat] "
"[-Ooption] "
"[-Toutput] "
"[-Wlevel] "
"[file...]\n",
progname);
exit((int)MANDOCLEVEL_BADARG);
}
static void
parse(struct curparse *curp, int fd,
const char *file, enum mandoclevel *level)
{
enum mandoclevel rc;
struct mdoc *mdoc;
struct man *man;
/* Begin by parsing the file itself. */
assert(file);
assert(fd >= -1);
rc = mparse_readfd(curp->mp, fd, file);
/* Stop immediately if the parse has failed. */
if (MANDOCLEVEL_FATAL <= rc)
goto cleanup;
/*
* With -Wstop and warnings or errors of at least the requested
* level, do not produce output.
*/
if (MANDOCLEVEL_OK != rc && curp->wstop)
goto cleanup;
/* 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);
curp->outfree = html_free;
break;
case (OUTT_HTML):
curp->outdata = html_alloc(curp->outopts);
curp->outfree = html_free;
break;
case (OUTT_UTF8):
curp->outdata = utf8_alloc(curp->outopts);
curp->outfree = ascii_free;
break;
case (OUTT_LOCALE):
curp->outdata = locale_alloc(curp->outopts);
curp->outfree = ascii_free;
break;
case (OUTT_ASCII):
curp->outdata = ascii_alloc(curp->outopts);
curp->outfree = ascii_free;
break;
case (OUTT_PDF):
curp->outdata = pdf_alloc(curp->outopts);
curp->outfree = pspdf_free;
break;
case (OUTT_PS):
curp->outdata = ps_alloc(curp->outopts);
curp->outfree = pspdf_free;
break;
default:
break;
}
switch (curp->outtype) {
case (OUTT_HTML):
/* FALLTHROUGH */
case (OUTT_XHTML):
curp->outman = html_man;
curp->outmdoc = html_mdoc;
break;
case (OUTT_TREE):
curp->outman = tree_man;
curp->outmdoc = tree_mdoc;
break;
case (OUTT_MAN):
curp->outmdoc = man_mdoc;
curp->outman = man_man;
break;
case (OUTT_PDF):
/* FALLTHROUGH */
case (OUTT_ASCII):
/* FALLTHROUGH */
case (OUTT_UTF8):
/* FALLTHROUGH */
case (OUTT_LOCALE):
/* FALLTHROUGH */
case (OUTT_PS):
curp->outman = terminal_man;
curp->outmdoc = terminal_mdoc;
break;
default:
break;
}
}
mparse_result(curp->mp, &mdoc, &man);
/* 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:
mparse_reset(curp->mp);
if (*level < rc)
*level = rc;
}
static int
moptions(enum mparset *tflags, char *arg)
{
if (0 == strcmp(arg, "doc"))
*tflags = MPARSE_MDOC;
else if (0 == strcmp(arg, "andoc"))
*tflags = MPARSE_AUTO;
else if (0 == strcmp(arg, "an"))
*tflags = MPARSE_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->wlevel = MANDOCLEVEL_WARNING;
} else if (0 == strcmp(arg, "tree"))
curp->outtype = OUTT_TREE;
else if (0 == strcmp(arg, "man"))
curp->outtype = OUTT_MAN;
else if (0 == strcmp(arg, "html"))
curp->outtype = OUTT_HTML;
else if (0 == strcmp(arg, "utf8"))
curp->outtype = OUTT_UTF8;
else if (0 == strcmp(arg, "locale"))
curp->outtype = OUTT_LOCALE;
else if (0 == strcmp(arg, "xhtml"))
curp->outtype = OUTT_XHTML;
else if (0 == strcmp(arg, "ps"))
curp->outtype = OUTT_PS;
else if (0 == strcmp(arg, "pdf"))
curp->outtype = OUTT_PDF;
else {
fprintf(stderr, "%s: Bad argument\n", arg);
return(0);
}
return(1);
}
static int
woptions(struct curparse *curp, char *arg)
{
char *v, *o;
const char *toks[6];
toks[0] = "stop";
toks[1] = "all";
toks[2] = "warning";
toks[3] = "error";
toks[4] = "fatal";
toks[5] = NULL;
while (*arg) {
o = arg;
switch (getsubopt(&arg, UNCONST(toks), &v)) {
case (0):
curp->wstop = 1;
break;
case (1):
/* FALLTHROUGH */
case (2):
curp->wlevel = MANDOCLEVEL_WARNING;
break;
case (3):
curp->wlevel = MANDOCLEVEL_ERROR;
break;
case (4):
curp->wlevel = MANDOCLEVEL_FATAL;
break;
default:
fprintf(stderr, "-W%s: Bad argument\n", o);
return(0);
}
}
return(1);
}
static void
mmsg(enum mandocerr t, enum mandoclevel lvl,
const char *file, int line, int col, const char *msg)
{
fprintf(stderr, "%s:%d:%d: %s: %s",
file, line, col + 1,
mparse_strlevel(lvl),
mparse_strerror(t));
if (msg)
fprintf(stderr, ": %s", msg);
fputc('\n', stderr);
}

61
contrib/mdocml/main.h Normal file
View File

@ -0,0 +1,61 @@
/* $Id: main.h,v 1.15 2011/10/06 22:29:12 kristaps Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 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 man_mdoc(void *, const struct mdoc *);
void man_man(void *, const struct man *);
void *locale_alloc(char *);
void *utf8_alloc(char *);
void *ascii_alloc(char *);
void ascii_free(void *);
void *pdf_alloc(char *);
void *ps_alloc(char *);
void pspdf_free(void *);
void terminal_mdoc(void *, const struct mdoc *);
void terminal_man(void *, const struct man *);
__END_DECLS
#endif /*!MAIN_H*/

913
contrib/mdocml/man.7 Normal file
View File

@ -0,0 +1,913 @@
.\" $Id: man.7,v 1.113 2012/01/03 15:16:24 kristaps Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" 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: January 3 2012 $
.Dt MAN 7
.Os
.Sh NAME
.Nm man
.Nd legacy formatting language for manual pages
.Sh DESCRIPTION
Traditionally, the
.Nm man
language has been used to write
.Ux
manuals for the
.Xr man 1
utility.
It supports limited control of presentational details like fonts,
indentation and spacing.
This reference document describes the structure of manual pages
and the syntax and usage of the man language.
.Pp
.Bf -emphasis
Do not use
.Nm
to write your manuals:
.Ef
It lacks support for semantic markup.
Use the
.Xr mdoc 7
language, instead.
.Pp
In a
.Nm
document, lines beginning with the control character
.Sq \&.
are called
.Dq macro lines .
The first word is the macro name.
It usually consists of two capital letters.
For a list of available macros, see
.Sx MACRO OVERVIEW .
The words following the macro name are arguments to the macro.
.Pp
Lines not beginning with the control character are called
.Dq text lines .
They provide free-form text to be printed; the formatting of the text
depends on the respective processing context:
.Bd -literal -offset indent
\&.SH Macro lines change control state.
Text lines are interpreted within the current state.
.Ed
.Pp
Many aspects of the basic syntax of the
.Nm
language are based on the
.Xr roff 7
language; see the
.Em LANGUAGE SYNTAX
and
.Em MACRO SYNTAX
sections in the
.Xr roff 7
manual for details, in particular regarding
comments, escape sequences, whitespace, and quoting.
.Sh MANUAL STRUCTURE
Each
.Nm
document must contain 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 line must appear in the document.
.Pp
The following is a well-formed skeleton
.Nm
file for a utility
.Qq progname :
.Bd -literal -offset indent
\&.TH PROGNAME 1 2009-10-10
\&.SH NAME
\efBprogname\efR \e(en a description goes here
\&.\e\(dq .SH LIBRARY
\&.\e\(dq For sections 2 & 3 only.
\&.\e\(dq Not used in OpenBSD.
\&.SH SYNOPSIS
\efBprogname\efR [\efB\e-options\efR] arguments...
\&.SH DESCRIPTION
The \efBfoo\efR utility processes files...
\&.\e\(dq .SH IMPLEMENTATION NOTES
\&.\e\(dq Not used in OpenBSD.
\&.\e\(dq .SH RETURN VALUES
\&.\e\(dq For sections 2, 3, & 9 only.
\&.\e\(dq .SH ENVIRONMENT
\&.\e\(dq For sections 1, 6, 7, & 8 only.
\&.\e\(dq .SH FILES
\&.\e\(dq .SH EXIT STATUS
\&.\e\(dq For sections 1, 6, & 8 only.
\&.\e\(dq .SH EXAMPLES
\&.\e\(dq .SH DIAGNOSTICS
\&.\e\(dq For sections 1, 4, 6, 7, & 8 only.
\&.\e\(dq .SH ERRORS
\&.\e\(dq For sections 2, 3, & 9 only.
\&.\e\(dq .SH SEE ALSO
\&.\e\(dq .BR foo ( 1 )
\&.\e\(dq .SH STANDARDS
\&.\e\(dq .SH HISTORY
\&.\e\(dq .SH AUTHORS
\&.\e\(dq .SH CAVEATS
\&.\e\(dq .SH BUGS
\&.\e\(dq .SH SECURITY CONSIDERATIONS
\&.\e\(dq Not used in OpenBSD.
.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 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 name and a short description of how
the file is used (created, modified, etc.).
.It Em EXIT STATUS
This section documents the command exit status for
section 1, 6, and 8 utilities.
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 sure that 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
A brief history of the subject, including where support first appeared.
.It Em AUTHORS
Credits to the person or persons who wrote the code and/or documentation.
Authors should generally be noted by both name and email address.
.It Em CAVEATS
Common misuses and misunderstandings should be explained
in this section.
.It Em BUGS
Known bugs, limitations, and work-arounds should be described
in this section.
.It Em SECURITY CONSIDERATIONS
Documents any security precautions that operators should consider.
.El
.Sh MACRO OVERVIEW
This overview is sorted such that macros of similar purpose are listed
together, to help find the best macro for any given purpose.
Deprecated macros are not included in the overview, but can be found
in the alphabetical reference below.
.Ss Page header and footer meta-data
.Bl -column "PP, LP, P" description
.It Sx TH Ta set the title: Ar title section date Op Ar source Op Ar volume
.It Sx AT Ta display AT&T UNIX version in the page footer (<= 1 argument)
.It Sx UC Ta display BSD version in the page footer (<= 1 argument)
.El
.Ss Sections and paragraphs
.Bl -column "PP, LP, P" description
.It Sx SH Ta section header (one line)
.It Sx SS Ta subsection header (one line)
.It Sx PP , LP , P Ta start an undecorated paragraph (no arguments)
.It Sx RS , RE Ta reset the left margin: Op Ar width
.It Sx IP Ta indented paragraph: Op Ar head Op Ar width
.It Sx TP Ta tagged paragraph: Op Ar width
.It Sx HP Ta hanged paragraph: Op Ar width
.It Sx \&br Ta force output line break in text mode (no arguments)
.It Sx \&sp Ta force vertical space: Op Ar height
.It Sx fi , nf Ta fill mode and no-fill mode (no arguments)
.It Sx in Ta additional indent: Op Ar width
.El
.Ss Physical markup
.Bl -column "PP, LP, P" description
.It Sx B Ta boldface font
.It Sx I Ta italic font
.It Sx R Ta roman (default) font
.It Sx SB Ta small boldface font
.It Sx SM Ta small roman font
.It Sx BI Ta alternate between boldface and italic fonts
.It Sx BR Ta alternate between boldface and roman fonts
.It Sx IB Ta alternate between italic and boldface fonts
.It Sx IR Ta alternate between italic and roman fonts
.It Sx RB Ta alternate between roman and boldface fonts
.It Sx RI Ta alternate between roman and italic fonts
.El
.Ss Semantic markup
.Bl -column "PP, LP, P" description
.It Sx OP Ta optional arguments
.El
.Sh MACRO 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
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
.Dl \&.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
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 reset to the default.
.Pp
See also
.Sx \&HP ,
.Sx \&IP ,
.Sx \&P ,
.Sx \&PP ,
and
.Sx \&TP .
.Ss \&OP
Optional command-line argument.
This has the following syntax:
.Bd -filled -offset indent
.Pf \. Sx \&OP
.Cm key Op Cm value
.Ed
.Pp
The
.Cm key
is usually a command-line flag and
.Cm value
its argument.
.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
and
.Sx \&B .
.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 .
The default left margin is restored to the state of the original
.Sx \&RS
invocation.
.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
Temporarily reset the default left margin.
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.
.Pp
See also
.Sx \&RE .
.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 reset 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 reset to the default.
.Ss \&TH
Sets the title of the manual page with the following syntax:
.Bd -filled -offset indent
.Pf \. Sx \&TH
.Ar title section date
.Op Ar source Op Ar volume
.Ed
.Pp
Conventionally, the document
.Ar title
is given in all caps.
The recommended
.Ar date
format is
.Sy YYYY-MM-DD
as specified in the ISO-8601 standard;
if the argument does not conform, it is printed verbatim.
If the
.Ar date
is empty or not specified, the current date is used.
The optional
.Ar source
string specifies the organisation providing the utility.
The
.Ar volume
string replaces the default rendered volume, which is dictated by the
manual section.
.Pp
Examples:
.Pp
.Dl \&.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 \&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 \&ft
Change the current font mode.
See
.Sx Text Decoration
for a listing of available font modes.
.Ss \&in
Indent relative to the current indentation:
.Pp
.D1 Pf \. Sx \&in Op Cm width
.Pp
If
.Cm width
is signed, the new offset is relative.
Otherwise, it is absolute.
This value is reset upon the next paragraph, section, or sub-section.
.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 .
Literal mode is implicitly ended by
.Sx \&SH
or
.Sx \&SS .
.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 .
.Sh MACRO SYNTAX
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, except for
.Sx \&br ,
.Sx \&sp ,
and
.Sx \&na .
.Pp
The syntax is as follows:
.Bd -literal -offset indent
\&.YO \(lBbody...\(rB
\(lBbody...\(rB
.Ed
.Bl -column "MacroX" "ArgumentsX" "ScopeXXXXX" "CompatX" -offset indent
.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 \&OP Ta 0, 1 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 \&ft Ta 1 Ta current Ta compat
.It Sx \&in Ta 1 Ta current Ta compat
.It Sx \&na Ta 0 Ta current Ta compat
.It Sx \&nf Ta 0 Ta current Ta compat
.It Sx \&sp Ta 1 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 comprise a head and body.
As with 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.
.Bl -column "MacroX" "ArgumentsX" "Head ScopeX" "sub-sectionX" "compatX" -offset indent
.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.
.Ss Font handling
In
.Nm
documents, both
.Sx Physical markup
macros and
.Xr roff 7
.Ql \ef
font escape sequences can be used to choose fonts.
In text lines, the effect of manual font selection by escape sequences
only lasts until the next macro invocation; in macro lines, it only lasts
until the end of the macro scope.
Note that macros like
.Sx \&BR
open and close a font scope for each argument.
.Sh COMPATIBILITY
This section documents areas of questionable portability between
implementations of the
.Nm
language.
.Pp
.Bl -dash -compact
.It
Do not depend on
.Sx \&SH
or
.Sx \&SS
to close out a literal context opened with
.Sx \&nf .
This behaviour may not be portable.
.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
troff suppresses a newline before
.Sq \(aq
macro output; in mandoc, it is an alias for the standard
.Sq \&.
control character.
.It
The
.Sq \eh
.Pq horizontal position ,
.Sq \ev
.Pq vertical position ,
.Sq \em
.Pq text colour ,
.Sq \eM
.Pq text filling colour ,
.Sq \ez
.Pq zero-length character ,
.Sq \ew
.Pq string length ,
.Sq \ek
.Pq horizontal position marker ,
.Sq \eo
.Pq text overstrike ,
and
.Sq \es
.Pq text size
escape sequences are all discarded in mandoc.
.It
The
.Sq \ef
scaling unit is accepted by mandoc, but rendered as the default unit.
.It
The
.Sx \&sp
macro does not accept negative values in mandoc.
In GNU troff, this would result in strange behaviour.
.It
In page header lines, GNU troff versions up to and including 1.21
only print
.Ar volume
names explicitly specified in the
.Sx \&TH
macro; mandoc and newer groff print the default volume name
corresponding to the
.Ar section
number when no
.Ar volume
is given, like in
.Xr mdoc 7 .
.El
.Pp
The
.Sx OP
macro is part of the extended
.Nm
macro set, and may not be portable to non-GNU troff implementations.
.Sh SEE ALSO
.Xr man 1 ,
.Xr mandoc 1 ,
.Xr eqn 7 ,
.Xr mandoc_char 7 ,
.Xr mdoc 7 ,
.Xr roff 7 ,
.Xr tbl 7
.Sh HISTORY
The
.Nm
language first appeared as a macro package for the roff typesetting
system in
.At v7 .
It was later rewritten by James Clark as a macro package for groff.
Eric S. Raymond wrote the extended
.Nm
macros for groff in 2007.
The stand-alone implementation that is part of the
.Xr mandoc 1
utility written by Kristaps Dzonsons appeared in
.Ox 4.6 .
.Sh AUTHORS
This
.Nm
reference was written by
.An Kristaps Dzonsons ,
.Mt kristaps@bsd.lv .
.Sh CAVEATS
Do not use this language.
Use
.Xr mdoc 7 ,
instead.

690
contrib/mdocml/man.c Normal file
View File

@ -0,0 +1,690 @@
/* $Id: man.c,v 1.115 2012/01/03 15:16:24 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "man.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", "sp", "nf",
"fi", "RE", "RS", "DT",
"UC", "PD", "AT", "in",
"ft", "OP"
};
const char * const *man_macronames = __man_macronames;
static struct man_node *man_node_alloc(struct man *, 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 man_descope(struct man *, int, int);
const struct man_node *
man_node(const struct man *m)
{
assert( ! (MAN_HALT & m->flags));
return(m->first);
}
const struct man_meta *
man_meta(const struct man *m)
{
assert( ! (MAN_HALT & m->flags));
return(&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(struct roff *roff, struct mparse *parse)
{
struct man *p;
p = mandoc_calloc(1, sizeof(struct man));
man_hash_init();
p->parse = parse;
p->roff = roff;
man_alloc1(p);
return(p);
}
int
man_endparse(struct man *m)
{
assert( ! (MAN_HALT & m->flags));
if (man_macroend(m))
return(1);
m->flags |= MAN_HALT;
return(0);
}
int
man_parseln(struct man *m, int ln, char *buf, int offs)
{
m->flags |= MAN_NEWLINE;
assert( ! (MAN_HALT & m->flags));
return (mandoc_getcontrol(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.date)
free(man->meta.date);
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_TAIL):
assert(MAN_BLOCK == p->parent->type);
p->parent->tail = 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_TBL):
/* FALLTHROUGH */
case (MAN_TEXT):
if ( ! man_valid_post(man))
return(0);
break;
default:
break;
}
return(1);
}
static struct man_node *
man_node_alloc(struct man *m, 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;
if (MAN_NEWLINE & m->flags)
p->flags |= MAN_LINE;
m->flags &= ~MAN_NEWLINE;
return(p);
}
int
man_elem_alloc(struct man *m, int line, int pos, enum mant tok)
{
struct man_node *p;
p = man_node_alloc(m, line, pos, MAN_ELEM, tok);
if ( ! man_node_append(m, p))
return(0);
m->next = MAN_NEXT_CHILD;
return(1);
}
int
man_tail_alloc(struct man *m, int line, int pos, enum mant tok)
{
struct man_node *p;
p = man_node_alloc(m, line, pos, MAN_TAIL, 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(m, 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(m, 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(m, 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;
n = man_node_alloc(m, line, pos, MAN_TEXT, MAN_MAX);
n->string = roff_strdup(m->roff, word);
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);
}
int
man_addeqn(struct man *m, const struct eqn *ep)
{
struct man_node *n;
assert( ! (MAN_HALT & m->flags));
n = man_node_alloc(m, ep->ln, ep->pos, MAN_EQN, MAN_MAX);
n->eqn = ep;
if ( ! man_node_append(m, n))
return(0);
m->next = MAN_NEXT_SIBLING;
return(man_descope(m, ep->ln, ep->pos));
}
int
man_addspan(struct man *m, const struct tbl_span *sp)
{
struct man_node *n;
assert( ! (MAN_HALT & m->flags));
n = man_node_alloc(m, sp->line, 0, MAN_TBL, MAN_MAX);
n->span = sp;
if ( ! man_node_append(m, n))
return(0);
m->next = MAN_NEXT_SIBLING;
return(man_descope(m, sp->line, 0));
}
static int
man_descope(struct man *m, int line, int offs)
{
/*
* 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
man_ptext(struct man *m, int line, char *buf, int offs)
{
int i;
/* Literal free-form text whitespace is preserved. */
if (MAN_LITERAL & m->flags) {
if ( ! man_word_alloc(m, line, offs, buf + offs))
return(0);
return(man_descope(m, line, offs));
}
/* 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);
return(man_descope(m, line, offs));
}
/*
* 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])
man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE);
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, 0))
m->last->flags |= MAN_EOS;
return(man_descope(m, line, offs));
}
static int
man_pmacro(struct man *m, int ln, char *buf, int offs)
{
int i, ppos;
enum mant tok;
char mac[5];
struct man_node *n;
if ('"' == buf[offs]) {
man_pmsg(m, ln, offs, MANDOCERR_BADCOMMENT);
return(1);
} else if ('\0' == buf[offs])
return(1);
ppos = offs;
/*
* Copy the first word into a nil-terminated buffer.
* Stop copying when a tab, space, or eoln is encountered.
*/
i = 0;
while (i < 4 && '\0' != buf[offs] &&
' ' != buf[offs] && '\t' != buf[offs])
mac[i++] = buf[offs++];
mac[i] = '\0';
tok = (i > 0 && i < 4) ? man_hash_find(mac) : MAN_MAX;
if (MAN_MAX == tok) {
mandoc_vmsg(MANDOCERR_MACRO, m->parse, ln,
ppos, "%s", buf + ppos - 1);
return(1);
}
/* The macro is sane. Jump to the next word. */
while (buf[offs] && ' ' == buf[offs])
offs++;
/*
* 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[offs] && ' ' == buf[offs - 1])
man_pmsg(m, ln, offs - 1, MANDOCERR_EOLNSPACE);
/*
* Remove prior ELINE macro, as it's being clobbered 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) {
n = m->last;
assert(MAN_TEXT != n->type);
/* Remove repeated NSCOPED macros causing ELINE. */
if (MAN_NSCOPED & man_macros[n->tok].flags)
n = n->parent;
mandoc_vmsg(MANDOCERR_LINESCOPE, m->parse, n->line,
n->pos, "%s breaks %s", man_macronames[tok],
man_macronames[n->tok]);
man_node_delete(m, n);
m->flags &= ~MAN_ELINE;
}
/*
* Remove prior BLINE macro that is being clobbered.
*/
if ((m->flags & MAN_BLINE) &&
(MAN_BSCOPE & man_macros[tok].flags)) {
n = m->last;
/* Might be a text node like 8 in
* .TP 8
* .SH foo
*/
if (MAN_TEXT == n->type)
n = n->parent;
/* Remove element that didn't end BLINE, if any. */
if ( ! (MAN_BSCOPE & man_macros[n->tok].flags))
n = n->parent;
assert(MAN_HEAD == n->type);
n = n->parent;
assert(MAN_BLOCK == n->type);
assert(MAN_SCOPED & man_macros[n->tok].flags);
mandoc_vmsg(MANDOCERR_LINESCOPE, m->parse, n->line,
n->pos, "%s breaks %s", man_macronames[tok],
man_macronames[n->tok]);
man_node_delete(m, n);
m->flags &= ~MAN_BLINE;
}
/*
* 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, &offs, buf))
goto err;
/*
* 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, ppos, m->last->tok));
err: /* Error out. */
m->flags |= MAN_HALT;
return(0);
}
/*
* 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;
}
const struct mparse *
man_mparse(const struct man *m)
{
assert(m && m->parse);
return(m->parse);
}

113
contrib/mdocml/man.h Normal file
View File

@ -0,0 +1,113 @@
/* $Id: man.h,v 1.60 2012/01/03 15:16:24 kristaps Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 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
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_sp,
MAN_nf,
MAN_fi,
MAN_RE,
MAN_RS,
MAN_DT,
MAN_UC,
MAN_PD,
MAN_AT,
MAN_in,
MAN_ft,
MAN_OP,
MAN_MAX
};
enum man_type {
MAN_TEXT,
MAN_ELEM,
MAN_ROOT,
MAN_BLOCK,
MAN_HEAD,
MAN_BODY,
MAN_TAIL,
MAN_TBL,
MAN_EQN
};
struct man_meta {
char *msec; /* `TH' section (1, 3p, etc.) */
char *date; /* `TH' normalised date */
char *vol; /* `TH' volume */
char *title; /* `TH' title (e.g., FOO) */
char *source; /* `TH' source (e.g., GNU) */
};
struct man_node {
struct man_node *parent; /* parent AST node */
struct man_node *child; /* first child AST node */
struct man_node *next; /* sibling AST node */
struct man_node *prev; /* prior sibling AST node */
int nchild; /* number children */
int line;
int pos;
enum mant tok; /* tok or MAN__MAX if none */
int flags;
#define MAN_VALID (1 << 0) /* has been validated */
#define MAN_EOS (1 << 2) /* at sentence boundary */
#define MAN_LINE (1 << 3) /* first macro/text on line */
enum man_type type; /* AST node type */
char *string; /* TEXT node argument */
struct man_node *head; /* BLOCK node HEAD ptr */
struct man_node *tail; /* BLOCK node TAIL ptr */
struct man_node *body; /* BLOCK node BODY ptr */
const struct tbl_span *span; /* TBL */
const struct eqn *eqn; /* EQN */
};
/* Names of macros. Index is enum mant. */
extern const char *const *man_macronames;
__BEGIN_DECLS
struct man;
const struct man_node *man_node(const struct man *);
const struct man_meta *man_meta(const struct man *);
const struct mparse *man_mparse(const struct man *);
__END_DECLS
#endif /*!MAN_H*/

107
contrib/mdocml/man_hash.c Normal file
View File

@ -0,0 +1,107 @@
/* $Id: man_hash.c,v 1.25 2011/07/24 18:15:14 kristaps Exp $ */
/*
* Copyright (c) 2008, 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.
*/
#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 "man.h"
#include "mandoc.h"
#include "libman.h"
#define HASH_DEPTH 6
#define HASH_ROW(x) do { \
if (isupper((unsigned 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 unsigned 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((unsigned char)x));
HASH_ROW(x);
for (j = 0; j < HASH_DEPTH; j++)
if (UCHAR_MAX == table[x + j]) {
table[x + j] = (unsigned 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((unsigned 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);
}

688
contrib/mdocml/man_html.c Normal file
View File

@ -0,0 +1,688 @@
/* $Id: man_html.c,v 1.86 2012/01/03 15:16:24 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 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 MAN_ARGS const struct man_meta *m, \
const struct man_node *n, \
struct mhtml *mh, \
struct html *h
struct mhtml {
int fl;
#define MANH_LITERAL (1 << 0) /* literal context */
};
struct htmlman {
int (*pre)(MAN_ARGS);
int (*post)(MAN_ARGS);
};
static void print_bvspace(struct html *,
const struct man_node *);
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_B_pre(MAN_ARGS);
static int man_HP_pre(MAN_ARGS);
static int man_IP_pre(MAN_ARGS);
static int man_I_pre(MAN_ARGS);
static int man_OP_pre(MAN_ARGS);
static int man_PP_pre(MAN_ARGS);
static int man_RS_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 int man_alt_pre(MAN_ARGS);
static int man_br_pre(MAN_ARGS);
static int man_ign_pre(MAN_ARGS);
static int man_in_pre(MAN_ARGS);
static int man_literal_pre(MAN_ARGS);
static void man_root_post(MAN_ARGS);
static void man_root_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_SM_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 */
{ man_ign_pre, NULL }, /* na */
{ man_br_pre, NULL }, /* sp */
{ man_literal_pre, NULL }, /* nf */
{ man_literal_pre, NULL }, /* fi */
{ NULL, NULL }, /* RE */
{ man_RS_pre, NULL }, /* RS */
{ man_ign_pre, NULL }, /* DT */
{ man_ign_pre, NULL }, /* UC */
{ man_ign_pre, NULL }, /* PD */
{ man_ign_pre, NULL }, /* AT */
{ man_in_pre, NULL }, /* in */
{ man_ign_pre, NULL }, /* ft */
{ man_OP_pre, NULL }, /* OP */
};
/*
* Printing leading vertical space before a block.
* This is used for the paragraph macros.
* The rules are pretty simple, since there's very little nesting going
* on here. Basically, if we're the first within another block (SS/SH),
* then don't emit vertical space. If we are (RS), then do. If not the
* first, print it.
*/
static void
print_bvspace(struct html *h, const struct man_node *n)
{
if (n->body && n->body->child)
if (MAN_TBL == n->body->child->type)
return;
if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
if (NULL == n->prev)
return;
print_otag(h, TAG_P, 0, NULL);
}
void
html_man(void *arg, const struct man *m)
{
struct mhtml mh;
memset(&mh, 0, sizeof(struct mhtml));
print_man(man_meta(m), man_node(m), &mh, (struct html *)arg);
putchar('\n');
}
static void
print_man(MAN_ARGS)
{
struct tag *t, *tt;
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "mandoc");
if ( ! (HTML_FRAGMENT & h->oflags)) {
print_gen_decls(h);
t = print_otag(h, TAG_HTML, 0, NULL);
tt = print_otag(h, TAG_HEAD, 0, NULL);
print_man_head(m, n, mh, h);
print_tagq(h, tt);
print_otag(h, TAG_BODY, 0, NULL);
print_otag(h, TAG_DIV, 1, &tag);
} else
t = print_otag(h, TAG_DIV, 1, &tag);
print_man_nodelist(m, n, mh, h);
print_tagq(h, t);
}
/* ARGSUSED */
static void
print_man_head(MAN_ARGS)
{
print_gen_head(h);
assert(m->title);
assert(m->msec);
bufcat_fmt(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, mh, h);
if (n->next)
print_man_nodelist(m, n->next, mh, h);
}
static void
print_man_node(MAN_ARGS)
{
int child;
struct tag *t;
child = 1;
t = h->tags.head;
switch (n->type) {
case (MAN_ROOT):
man_root_pre(m, n, mh, h);
break;
case (MAN_TEXT):
/*
* If we have a blank line, output a vertical space.
* If we have a space as the first character, break
* before printing the line's data.
*/
if ('\0' == *n->string) {
print_otag(h, TAG_P, 0, NULL);
return;
}
if (' ' == *n->string && MAN_LINE & n->flags)
print_otag(h, TAG_BR, 0, NULL);
else if (MANH_LITERAL & mh->fl && n->prev)
print_otag(h, TAG_BR, 0, NULL);
print_text(h, n->string);
return;
case (MAN_EQN):
print_eqn(h, n->eqn);
break;
case (MAN_TBL):
/*
* This will take care of initialising all of the table
* state data for the first table, then tearing it down
* for the last one.
*/
print_tbl(h, n->span);
return;
default:
/*
* Close out scope of font prior to opening a macro
* scope.
*/
if (HTMLFONT_NONE != h->metac) {
h->metal = h->metac;
h->metac = HTMLFONT_NONE;
}
/*
* Close out the current table, if it's open, and unset
* the "meta" table state. This will be reopened on the
* next table element.
*/
if (h->tblt) {
print_tblclose(h);
t = h->tags.head;
}
if (mans[n->tok].pre)
child = (*mans[n->tok].pre)(m, n, mh, h);
break;
}
if (child && n->child)
print_man_nodelist(m, n->child, mh, h);
/* This will automatically close out any font scope. */
print_stagq(h, t);
switch (n->type) {
case (MAN_ROOT):
man_root_post(m, n, mh, h);
break;
case (MAN_EQN):
break;
default:
if (mans[n->tok].post)
(*mans[n->tok].post)(m, n, mh, 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 void
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);
assert(m->title);
assert(m->msec);
snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
PAIR_SUMMARY_INIT(&tag[0], "Document Header");
PAIR_CLASS_INIT(&tag[1], "head");
PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
t = print_otag(h, TAG_TABLE, 3, tag);
PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
print_otag(h, TAG_COL, 1, tag);
print_otag(h, TAG_COL, 1, tag);
print_otag(h, TAG_COL, 1, tag);
print_otag(h, TAG_TBODY, 0, NULL);
tt = print_otag(h, TAG_TR, 0, NULL);
PAIR_CLASS_INIT(&tag[0], "head-ltitle");
print_otag(h, TAG_TD, 1, tag);
print_text(h, title);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag[0], "head-vol");
PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
print_otag(h, TAG_TD, 2, tag);
print_text(h, b);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag[0], "head-rtitle");
PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
print_otag(h, TAG_TD, 2, tag);
print_text(h, title);
print_tagq(h, t);
}
/* ARGSUSED */
static void
man_root_post(MAN_ARGS)
{
struct htmlpair tag[3];
struct tag *t, *tt;
PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
PAIR_CLASS_INIT(&tag[1], "foot");
PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
t = print_otag(h, TAG_TABLE, 3, tag);
PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
print_otag(h, TAG_COL, 1, tag);
print_otag(h, TAG_COL, 1, tag);
tt = print_otag(h, TAG_TR, 0, NULL);
PAIR_CLASS_INIT(&tag[0], "foot-date");
print_otag(h, TAG_TD, 1, tag);
assert(m->date);
print_text(h, m->date);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag[0], "foot-os");
PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
print_otag(h, TAG_TD, 2, 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);
if (MAN_sp == n->tok) {
if (NULL != (n = n->child))
if ( ! a2roffsu(n->string, &su, SCALE_VS))
SCALE_VS_INIT(&su, atoi(n->string));
} else
su.scale = 0;
bufinit(h);
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;
if (MAN_BLOCK == n->type) {
mh->fl &= ~MANH_LITERAL;
PAIR_CLASS_INIT(&tag, "section");
print_otag(h, TAG_DIV, 1, &tag);
return(1);
} else if (MAN_BODY == n->type)
return(1);
print_otag(h, TAG_H1, 0, NULL);
return(1);
}
/* ARGSUSED */
static int
man_alt_pre(MAN_ARGS)
{
const struct man_node *nn;
int i, savelit;
enum htmltag fp;
struct tag *t;
if ((savelit = mh->fl & MANH_LITERAL))
print_otag(h, TAG_BR, 0, NULL);
mh->fl &= ~MANH_LITERAL;
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
t = NULL;
switch (n->tok) {
case (MAN_BI):
fp = i % 2 ? TAG_I : TAG_B;
break;
case (MAN_IB):
fp = i % 2 ? TAG_B : TAG_I;
break;
case (MAN_RI):
fp = i % 2 ? TAG_I : TAG_MAX;
break;
case (MAN_IR):
fp = i % 2 ? TAG_MAX : TAG_I;
break;
case (MAN_BR):
fp = i % 2 ? TAG_MAX : TAG_B;
break;
case (MAN_RB):
fp = i % 2 ? TAG_B : TAG_MAX;
break;
default:
abort();
/* NOTREACHED */
}
if (i)
h->flags |= HTML_NOSPACE;
if (TAG_MAX != fp)
t = print_otag(h, fp, 0, NULL);
print_man_node(m, nn, mh, h);
if (t)
print_tagq(h, t);
}
if (savelit)
mh->fl |= MANH_LITERAL;
return(0);
}
/* ARGSUSED */
static int
man_SM_pre(MAN_ARGS)
{
print_otag(h, TAG_SMALL, 0, NULL);
if (MAN_SB == n->tok)
print_otag(h, TAG_B, 0, NULL);
return(1);
}
/* ARGSUSED */
static int
man_SS_pre(MAN_ARGS)
{
struct htmlpair tag;
if (MAN_BLOCK == n->type) {
mh->fl &= ~MANH_LITERAL;
PAIR_CLASS_INIT(&tag, "subsection");
print_otag(h, TAG_DIV, 1, &tag);
return(1);
} else if (MAN_BODY == n->type)
return(1);
print_otag(h, TAG_H2, 0, NULL);
return(1);
}
/* ARGSUSED */
static int
man_PP_pre(MAN_ARGS)
{
if (MAN_HEAD == n->type)
return(0);
else if (MAN_BLOCK == n->type)
print_bvspace(h, n);
return(1);
}
/* ARGSUSED */
static int
man_IP_pre(MAN_ARGS)
{
const struct man_node *nn;
if (MAN_BODY == n->type) {
print_otag(h, TAG_DD, 0, NULL);
return(1);
} else if (MAN_HEAD != n->type) {
print_otag(h, TAG_DL, 0, NULL);
return(1);
}
/* FIXME: width specification. */
print_otag(h, TAG_DT, 0, NULL);
/* For IP, only print the first header element. */
if (MAN_IP == n->tok && n->child)
print_man_node(m, n->child, mh, h);
/* For TP, only print next-line header elements. */
if (MAN_TP == n->tok)
for (nn = n->child; nn; nn = nn->next)
if (nn->line > n->line)
print_man_node(m, nn, mh, h);
return(0);
}
/* ARGSUSED */
static int
man_HP_pre(MAN_ARGS)
{
struct htmlpair tag;
struct roffsu su;
const struct man_node *np;
if (MAN_HEAD == n->type)
return(0);
else if (MAN_BLOCK != n->type)
return(1);
np = n->head->child;
if (NULL == np || ! a2width(np, &su))
SCALE_HS_INIT(&su, INDENT);
bufinit(h);
print_bvspace(h, n);
bufcat_su(h, "margin-left", &su);
su.scale = -su.scale;
bufcat_su(h, "text-indent", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_P, 1, &tag);
return(1);
}
/* ARGSUSED */
static int
man_OP_pre(MAN_ARGS)
{
struct tag *tt;
struct htmlpair tag;
print_text(h, "[");
h->flags |= HTML_NOSPACE;
PAIR_CLASS_INIT(&tag, "opt");
tt = print_otag(h, TAG_SPAN, 1, &tag);
if (NULL != (n = n->child)) {
print_otag(h, TAG_B, 0, NULL);
print_text(h, n->string);
}
print_stagq(h, tt);
if (NULL != n && NULL != n->next) {
print_otag(h, TAG_I, 0, NULL);
print_text(h, n->next->string);
}
print_stagq(h, tt);
h->flags |= HTML_NOSPACE;
print_text(h, "]");
return(0);
}
/* ARGSUSED */
static int
man_B_pre(MAN_ARGS)
{
print_otag(h, TAG_B, 0, NULL);
return(1);
}
/* ARGSUSED */
static int
man_I_pre(MAN_ARGS)
{
print_otag(h, TAG_I, 0, NULL);
return(1);
}
/* ARGSUSED */
static int
man_literal_pre(MAN_ARGS)
{
if (MAN_nf != n->tok) {
print_otag(h, TAG_BR, 0, NULL);
mh->fl &= ~MANH_LITERAL;
} else
mh->fl |= MANH_LITERAL;
return(0);
}
/* ARGSUSED */
static int
man_in_pre(MAN_ARGS)
{
print_otag(h, TAG_BR, 0, NULL);
return(0);
}
/* 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);
if (n->head->child)
a2width(n->head->child, &su);
bufinit(h);
bufcat_su(h, "margin-left", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
return(1);
}

484
contrib/mdocml/man_macro.c Normal file
View File

@ -0,0 +1,484 @@
/* $Id: man_macro.c,v 1.71 2012/01/03 15:16:24 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 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 "man.h"
#include "mandoc.h"
#include "libmandoc.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 man_args(struct man *, int,
int *, char *, char **);
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 void 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, MAN_BSCOPE }, /* TH */
{ blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SH */
{ blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SS */
{ blk_imp, MAN_BSCOPE | MAN_SCOPED | MAN_FSCOPED }, /* TP */
{ blk_imp, MAN_BSCOPE }, /* LP */
{ blk_imp, MAN_BSCOPE }, /* PP */
{ blk_imp, MAN_BSCOPE }, /* P */
{ blk_imp, MAN_BSCOPE }, /* IP */
{ blk_imp, MAN_BSCOPE }, /* 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, MAN_NSCOPED }, /* sp */
{ in_line_eoln, MAN_BSCOPE }, /* nf */
{ in_line_eoln, MAN_BSCOPE }, /* fi */
{ 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, 0 }, /* AT */
{ in_line_eoln, 0 }, /* in */
{ in_line_eoln, 0 }, /* ft */
{ in_line_eoln, 0 }, /* OP */
};
const struct man_macro * const man_macros = __man_macros;
/*
* Warn when "n" is an explicit non-roff macro.
*/
static void
rew_warn(struct man *m, struct man_node *n, enum mandocerr er)
{
if (er == MANDOCERR_MAX || MAN_BLOCK != n->type)
return;
if (MAN_VALID & n->flags)
return;
if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags))
return;
assert(er < MANDOCERR_FATAL);
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 *to,
enum mandocerr er)
{
struct man_node *n;
assert(to);
m->next = MAN_NEXT_SIBLING;
/* LINTED */
while (m->last != to) {
/*
* Save the parent here, because we may delete the
* m->last node in the post-validation phase and reset
* it to m->last->parent, causing a step in the closing
* out to be lost.
*/
n = m->last->parent;
rew_warn(m, m->last, er);
if ( ! man_valid_post(m))
return(0);
m->last = n;
assert(m->last);
}
rew_warn(m, m->last, er);
if ( ! man_valid_post(m))
return(0);
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 */
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)
man_pmsg(m, line, ppos, MANDOCERR_NOSCOPE);
if ( ! rew_scope(MAN_BODY, m, ntok))
return(0);
if ( ! rew_scope(MAN_BLOCK, m, ntok))
return(0);
return(1);
}
/* ARGSUSED */
int
blk_exp(MACRO_PROT_ARGS)
{
int 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 ( ! man_block_alloc(m, line, ppos, tok))
return(0);
if ( ! man_head_alloc(m, line, ppos, tok))
return(0);
for (;;) {
la = *pos;
if ( ! man_args(m, line, pos, buf, &p))
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.
*/
/* ARGSUSED */
int
blk_imp(MACRO_PROT_ARGS)
{
int 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;
if ( ! man_args(m, line, pos, buf, &p))
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));
}
/* ARGSUSED */
int
in_line_eoln(MACRO_PROT_ARGS)
{
int la;
char *p;
struct man_node *n;
if ( ! man_elem_alloc(m, line, ppos, tok))
return(0);
n = m->last;
for (;;) {
la = *pos;
if ( ! man_args(m, line, pos, buf, &p))
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;
}
assert(MAN_ROOT != m->last->type);
m->next = MAN_NEXT_SIBLING;
/*
* 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);
}
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);
return(1);
}
int
man_macroend(struct man *m)
{
return(man_unscope(m, m->first, MANDOCERR_SCOPEEXIT));
}
static int
man_args(struct man *m, int line, int *pos, char *buf, char **v)
{
char *start;
assert(*pos);
*v = start = buf + *pos;
assert(' ' != *start);
if ('\0' == *start)
return(0);
*v = mandoc_getarg(m->parse, v, line, pos);
return(1);
}

1117
contrib/mdocml/man_term.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,550 @@
/* $Id: man_validate.c,v 1.80 2012/01/03 15:16:24 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 <string.h>
#include <time.h>
#include "man.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_eq0(CHKARGS);
static int check_eq2(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 void check_text(CHKARGS);
static int post_AT(CHKARGS);
static int post_vs(CHKARGS);
static int post_fi(CHKARGS);
static int post_ft(CHKARGS);
static int post_nf(CHKARGS);
static int post_sec(CHKARGS);
static int post_TH(CHKARGS);
static int post_UC(CHKARGS);
static int pre_sec(CHKARGS);
static v_check posts_at[] = { post_AT, NULL };
static v_check posts_br[] = { post_vs, check_eq0, NULL };
static v_check posts_eq0[] = { check_eq0, NULL };
static v_check posts_eq2[] = { check_eq2, NULL };
static v_check posts_fi[] = { check_eq0, post_fi, NULL };
static v_check posts_ft[] = { post_ft, NULL };
static v_check posts_nf[] = { check_eq0, post_nf, NULL };
static v_check posts_par[] = { check_par, NULL };
static v_check posts_part[] = { check_part, NULL };
static v_check posts_sec[] = { post_sec, NULL };
static v_check posts_sp[] = { post_vs, check_le1, NULL };
static v_check posts_th[] = { check_ge2, check_le5, post_TH, NULL };
static v_check posts_uc[] = { post_UC, NULL };
static v_check pres_sec[] = { pre_sec, NULL };
static const struct man_valid man_valids[MAN_MAX] = {
{ NULL, posts_br }, /* br */
{ NULL, posts_th }, /* TH */
{ pres_sec, posts_sec }, /* SH */
{ pres_sec, posts_sec }, /* SS */
{ NULL, NULL }, /* TP */
{ NULL, posts_par }, /* LP */
{ NULL, posts_par }, /* PP */
{ NULL, posts_par }, /* P */
{ NULL, NULL }, /* IP */
{ NULL, NULL }, /* 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, posts_sp }, /* sp */
{ NULL, posts_nf }, /* nf */
{ NULL, posts_fi }, /* fi */
{ NULL, NULL }, /* RE */
{ NULL, posts_part }, /* RS */
{ NULL, NULL }, /* DT */
{ NULL, posts_uc }, /* UC */
{ NULL, NULL }, /* PD */
{ NULL, posts_at }, /* AT */
{ NULL, NULL }, /* in */
{ NULL, posts_ft }, /* ft */
{ NULL, posts_eq2 }, /* OP */
};
int
man_valid_pre(struct man *m, struct man_node *n)
{
v_check *cp;
switch (n->type) {
case (MAN_TEXT):
/* FALLTHROUGH */
case (MAN_ROOT):
/* FALLTHROUGH */
case (MAN_EQN):
/* FALLTHROUGH */
case (MAN_TBL):
return(1);
default:
break;
}
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):
check_text(m, m->last);
return(1);
case (MAN_ROOT):
return(check_root(m, m->last));
case (MAN_EQN):
/* FALLTHROUGH */
case (MAN_TBL):
return(1);
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)
man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
else if (MAN_ELINE & m->flags)
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) {
man_nmsg(m, n, MANDOCERR_NOTITLE);
/*
* If a title hasn't been set, do so now (by
* implication, date and section also aren't set).
*/
m->meta.title = mandoc_strdup("unknown");
m->meta.msec = mandoc_strdup("1");
m->meta.date = mandoc_normdate
(m->parse, NULL, n->line, n->pos);
}
return(1);
}
static void
check_text(CHKARGS)
{
char *cp, *p;
if (MAN_LITERAL & m->flags)
return;
cp = n->string;
for (p = cp; NULL != (p = strchr(p, '\t')); p++)
man_pmsg(m, n->line, (int)(p - cp), MANDOCERR_BADTAB);
}
#define INEQ_DEFINE(x, ineq, name) \
static int \
check_##name(CHKARGS) \
{ \
if (n->nchild ineq (x)) \
return(1); \
mandoc_vmsg(MANDOCERR_ARGCOUNT, m->parse, n->line, n->pos, \
"line arguments %s %d (have %d)", \
#ineq, (x), n->nchild); \
return(1); \
}
INEQ_DEFINE(0, ==, eq0)
INEQ_DEFINE(2, ==, eq2)
INEQ_DEFINE(1, <=, le1)
INEQ_DEFINE(2, >=, ge2)
INEQ_DEFINE(5, <=, le5)
static int
post_ft(CHKARGS)
{
char *cp;
int ok;
if (0 == n->nchild)
return(1);
ok = 0;
cp = n->child->string;
switch (*cp) {
case ('1'):
/* FALLTHROUGH */
case ('2'):
/* FALLTHROUGH */
case ('3'):
/* FALLTHROUGH */
case ('4'):
/* FALLTHROUGH */
case ('I'):
/* FALLTHROUGH */
case ('P'):
/* FALLTHROUGH */
case ('R'):
if ('\0' == cp[1])
ok = 1;
break;
case ('B'):
if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
ok = 1;
break;
case ('C'):
if ('W' == cp[1] && '\0' == cp[2])
ok = 1;
break;
default:
break;
}
if (0 == ok) {
mandoc_vmsg
(MANDOCERR_BADFONT, m->parse,
n->line, n->pos, "%s", cp);
*cp = '\0';
}
if (1 < n->nchild)
mandoc_vmsg
(MANDOCERR_ARGCOUNT, m->parse, n->line,
n->pos, "want one child (have %d)",
n->nchild);
return(1);
}
static int
pre_sec(CHKARGS)
{
if (MAN_BLOCK == n->type)
m->flags &= ~MAN_LITERAL;
return(1);
}
static int
post_sec(CHKARGS)
{
if ( ! (MAN_HEAD == n->type && 0 == n->nchild))
return(1);
man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
return(0);
}
static int
check_part(CHKARGS)
{
if (MAN_BODY == n->type && 0 == n->nchild)
mandoc_msg(MANDOCERR_ARGCWARN, m->parse, n->line,
n->pos, "want children (have none)");
return(1);
}
static int
check_par(CHKARGS)
{
switch (n->type) {
case (MAN_BLOCK):
if (0 == n->body->nchild)
man_node_delete(m, n);
break;
case (MAN_BODY):
if (0 == n->nchild)
man_nmsg(m, n, MANDOCERR_IGNPAR);
break;
case (MAN_HEAD):
if (n->nchild)
man_nmsg(m, n, MANDOCERR_ARGSLOST);
break;
default:
break;
}
return(1);
}
static int
post_TH(CHKARGS)
{
const char *p;
int line, pos;
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.date)
free(m->meta.date);
line = n->line;
pos = n->pos;
m->meta.title = m->meta.vol = m->meta.date =
m->meta.msec = m->meta.source = NULL;
/* ->TITLE<- MSEC DATE SOURCE VOL */
n = n->child;
if (n && n->string) {
for (p = n->string; '\0' != *p; p++) {
/* Only warn about this once... */
if (isalpha((unsigned char)*p) &&
! isupper((unsigned char)*p)) {
man_nmsg(m, n, MANDOCERR_UPPERCASE);
break;
}
}
m->meta.title = mandoc_strdup(n->string);
} else
m->meta.title = mandoc_strdup("");
/* TITLE ->MSEC<- DATE SOURCE VOL */
if (n)
n = n->next;
if (n && n->string)
m->meta.msec = mandoc_strdup(n->string);
else
m->meta.msec = mandoc_strdup("");
/* TITLE MSEC ->DATE<- SOURCE VOL */
if (n)
n = n->next;
if (n && n->string && '\0' != n->string[0]) {
pos = n->pos;
m->meta.date = mandoc_normdate
(m->parse, n->string, line, pos);
} else
m->meta.date = mandoc_strdup("");
/* TITLE MSEC DATE ->SOURCE<- VOL */
if (n && (n = n->next))
m->meta.source = mandoc_strdup(n->string);
/* TITLE MSEC DATE SOURCE ->VOL<- */
/* If missing, use the default VOL name for MSEC. */
if (n && (n = n->next))
m->meta.vol = mandoc_strdup(n->string);
else if ('\0' != m->meta.msec[0] &&
(NULL != (p = mandoc_a2msec(m->meta.msec))))
m->meta.vol = mandoc_strdup(p);
/*
* Remove the `TH' node after we've processed it for our
* meta-data.
*/
man_node_delete(m, m->last);
return(1);
}
static int
post_nf(CHKARGS)
{
if (MAN_LITERAL & m->flags)
man_nmsg(m, n, MANDOCERR_SCOPEREP);
m->flags |= MAN_LITERAL;
return(1);
}
static int
post_fi(CHKARGS)
{
if ( ! (MAN_LITERAL & m->flags))
man_nmsg(m, n, MANDOCERR_WNOSCOPE);
m->flags &= ~MAN_LITERAL;
return(1);
}
static int
post_UC(CHKARGS)
{
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;
n = n->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);
}
static int
post_AT(CHKARGS)
{
static const char * const unix_versions[] = {
"7th Edition",
"System III",
"System V",
"System V Release 2",
};
const char *p, *s;
struct man_node *nn;
n = n->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_vs(CHKARGS)
{
/*
* Don't warn about this because it occurs in pod2man and would
* cause considerable (unfixable) warnage.
*/
if (NULL == n->prev && MAN_ROOT == n->parent->type)
man_node_delete(m, n);
return(1);
}

669
contrib/mdocml/mandoc.1 Normal file
View File

@ -0,0 +1,669 @@
.\" $Id: mandoc.1,v 1.100 2011/12/25 19:35:44 kristaps Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 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: December 25 2011 $
.Dt MANDOC 1
.Os
.Sh NAME
.Nm mandoc
.Nd format and display UNIX manuals
.Sh SYNOPSIS
.Nm mandoc
.Op Fl V
.Op Fl m Ns Ar format
.Op Fl O Ns Ar option
.Op Fl T Ns Ar output
.Op Fl W Ns Ar level
.Op Ar
.Sh DESCRIPTION
The
.Nm
utility formats
.Ux
manual pages for display.
.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
The arguments are as follows:
.Bl -tag -width Ds
.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.
.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 level
Specify the minimum message
.Ar level
to be reported on the standard error output and to affect the exit status.
The
.Ar level
can be
.Cm warning ,
.Cm error ,
or
.Cm fatal .
The default is
.Fl W Ns Cm fatal ;
.Fl W Ns Cm all
is an alias for
.Fl W Ns Cm warning .
See
.Sx EXIT STATUS
and
.Sx DIAGNOSTICS
for details.
.Pp
The special option
.Fl W Ns Cm stop
tells
.Nm
to exit after parsing a file that causes warnings or errors of at least
the requested level.
No formatted output will be produced from that file.
If both a
.Ar level
and
.Cm stop
are requested, they can be joined with a comma, for example
.Fl W Ns Cm error , Ns Cm stop .
.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
.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, which correspond to output modes:
.Bl -tag -width "-Tlocale"
.It Fl T Ns Cm ascii
Produce 7-bit ASCII output.
This is the default.
See
.Sx ASCII Output .
.It Fl T Ns Cm html
Produce strict CSS1/HTML-4.01 output.
See
.Sx HTML Output .
.It Fl T Ns Cm lint
Parse only: produce no output.
Implies
.Fl W Ns Cm warning .
.It Fl T Ns Cm locale
Encode output using the current locale.
See
.Sx Locale Output .
.It Fl T Ns Cm man
Produce
.Xr man 7
format output.
See
.Sx Man Output .
.It Fl T Ns Cm pdf
Produce PDF output.
See
.Sx PDF Output .
.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 utf8
Encode output in the UTF\-8 multi-byte format.
See
.Sx UTF\-8 Output .
.It Fl T Ns Cm xhtml
Produce strict CSS1/XHTML-1.0 output.
See
.Sx XHTML Output .
.El
.Pp
If multiple input files are specified, these will be processed by the
corresponding filter in-order.
.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.
If no equivalent is found,
.Sq \&?
is used instead.
.Pp
Output width is limited to 78 visible columns unless literal input lines
exceed this limit.
.Pp
The following
.Fl O
arguments are accepted:
.Bl -tag -width Ds
.It Cm indent Ns = Ns Ar indent
The left margin for normal text is set to
.Ar indent
blank characters instead of the default of five for
.Xr mdoc 7
and seven for
.Xr man 7 .
Increasing this is not recommended; it may result in degraded formatting,
for example overfull lines or ugly line breaks.
.It Cm width Ns = Ns Ar width
The output width is set to
.Ar width ,
which will normalise to \(>=60.
.El
.Ss HTML Output
Output produced by
.Fl T Ns Cm html
conforms to HTML-4.01 strict.
.Pp
The
.Pa example.style.css
file documents style-sheet classes available for customising output.
If a style-sheet is not specified with
.Fl O Ns Ar style ,
.Fl T Ns Cm html
defaults to simple output readable in any graphical or text-based web
browser.
.Pp
Special characters are rendered in decimal-encoded UTF\-8.
.Pp
The following
.Fl O
arguments are accepted:
.Bl -tag -width Ds
.It Cm fragment
Omit the
.Aq !DOCTYPE
declaration and the
.Aq html ,
.Aq head ,
and
.Aq body
elements and only emit the subtree below the
.Aq body
element.
The
.Cm style
argument will be ignored.
This is useful when embedding manual content within existing documents.
.It 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 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 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
.Ss Locale Output
Locale-depending output encoding is triggered with
.Fl T Ns Cm locale .
This option is not available on all systems: systems without locale
support, or those whose internal representation is not natively UCS-4,
will fall back to
.Fl T Ns Cm ascii .
See
.Sx ASCII Output
for font style specification and available command-line arguments.
.Ss Man Output
Translate input format into
.Xr man 7
output format.
This is useful for distributing manual sources to legancy systems
lacking
.Xr mdoc 7
formatters.
.Pp
If
.Xr mdoc 7
is passed as input, it is translated into
.Xr man 7 .
If the input format is
.Xr man 7 ,
the input is copied to the output, expanding any
.Xr roff 7
.Sq so
requests.
The parser is also run, and as usual, the
.Fl W
level controls which
.Sx DIAGNOSTICS
are displayed before copying the input to the output.
.Ss PDF Output
PDF-1.1 output may be generated by
.Fl T Ns Cm pdf .
See
.Sx PostScript Output
for
.Fl O
arguments and defaults.
.Ss PostScript Output
PostScript
.Qq Adobe-3.0
Level-2 pages may be generated by
.Fl T Ns Cm ps .
Output pages default to letter sized and are rendered in the Times font
family, 11-point.
Margins are calculated as 1/9 the page length and width.
Line-height is 1.4m.
.Pp
Special characters are rendered as in
.Sx ASCII Output .
.Pp
The following
.Fl O
arguments are accepted:
.Bl -tag -width Ds
.It Cm paper Ns = Ns Ar name
The paper size
.Ar name
may be one of
.Ar a3 ,
.Ar a4 ,
.Ar a5 ,
.Ar legal ,
or
.Ar letter .
You may also manually specify dimensions as
.Ar NNxNN ,
width by height in millimetres.
If an unknown value is encountered,
.Ar letter
is used.
.El
.Ss UTF\-8 Output
Use
.Fl T Ns Cm utf8
to force a UTF\-8 locale.
See
.Sx Locale Output
for details and options.
.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 EXIT STATUS
The
.Nm
utility exits with one of the following values, controlled by the message
.Ar level
associated with the
.Fl W
option:
.Pp
.Bl -tag -width Ds -compact
.It 0
No warnings or errors occurred, or those that did were ignored because
they were lower than the requested
.Ar level .
.It 2
At least one warning occurred, but no error, and
.Fl W Ns Cm warning
was specified.
.It 3
At least one parsing error occurred, but no fatal error, and
.Fl W Ns Cm error
or
.Fl W Ns Cm warning
was specified.
.It 4
A fatal parsing error occurred.
.It 5
Invalid command line arguments were specified.
No input files have been read.
.It 6
An operating system error occurred, for example memory exhaustion or an
error accessing input files.
Such errors cause
.Nm
to exit at once, possibly in the middle of parsing or formatting a file.
.El
.Pp
Note that selecting
.Fl T Ns Cm lint
output mode implies
.Fl W Ns Cm warning .
.Sh EXAMPLES
To page manuals to the terminal:
.Pp
.Dl $ mandoc \-Wall,stop mandoc.1 2\*(Gt&1 | less
.Dl $ mandoc mandoc.1 mdoc.3 mdoc.7 | less
.Pp
To produce HTML manuals with
.Ar style.css
as the style-sheet:
.Pp
.Dl $ mandoc \-Thtml -Ostyle=style.css mdoc.7 \*(Gt mdoc.7.html
.Pp
To check over a large set of manuals:
.Pp
.Dl $ mandoc \-Tlint `find /usr/src -name \e*\e.[1-9]`
.Pp
To produce a series of PostScript manuals for A4 paper:
.Pp
.Dl $ mandoc \-Tps \-Opaper=a4 mdoc.7 man.7 \*(Gt manuals.ps
.Pp
Convert a modern
.Xr mdoc 7
manual to the older
.Xr man 7
format, for use on systems lacking an
.Xr mdoc 7
parser:
.Pp
.Dl $ mandoc \-Tman foo.mdoc \*(Gt foo.man
.Sh DIAGNOSTICS
Standard error messages reporting parsing errors are prefixed by
.Pp
.Sm off
.D1 Ar file : line : column : \ level :
.Sm on
.Pp
where the fields have the following meanings:
.Bl -tag -width "column"
.It Ar file
The name of the input file causing the message.
.It Ar line
The line number in that input file.
Line numbering starts at 1.
.It Ar column
The column number in that input file.
Column numbering starts at 1.
If the issue is caused by a word, the column number usually
points to the first character of the word.
.It Ar level
The message level, printed in capital letters.
.El
.Pp
Message levels have the following meanings:
.Bl -tag -width "warning"
.It Cm fatal
The parser is unable to parse a given input file at all.
No formatted output is produced from that input file.
.It Cm error
An input file contains syntax that cannot be safely interpreted,
either because it is invalid or because
.Nm
does not implement it yet.
By discarding part of the input or inserting missing tokens,
the parser is able to continue, and the error does not prevent
generation of formatted output, but typically, preparing that
output involves information loss, broken document structure
or unintended formatting.
.It Cm warning
An input file uses obsolete, discouraged or non-portable syntax.
All the same, the meaning of the input is unambiguous and a correct
rendering can be produced.
Documents causing warnings may render poorly when using other
formatting tools instead of
.Nm .
.El
.Pp
Messages of the
.Cm warning
and
.Cm error
levels are hidden unless their level, or a lower level, is requested using a
.Fl W
option or
.Fl T Ns Cm lint
output mode.
.Pp
The
.Nm
utility may also print messages related to invalid command line arguments
or operating system errors, for example when memory is exhausted or
input files cannot be read.
Such messages do not carry the prefix described above.
.Sh COMPATIBILITY
This section summarises
.Nm
compatibility with GNU troff.
Each input and output format is separately noted.
.Ss ASCII Compatibility
.Bl -bullet -compact
.It
Unrenderable unicode codepoints specified with
.Sq \e[uNNNN]
escapes are printed as
.Sq \&?
in mandoc.
In GNU troff, these raise an error.
.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 historic GNU troff, 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.
.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 eqn 7 ,
.Xr man 7 ,
.Xr mandoc_char 7 ,
.Xr mdoc 7 ,
.Xr roff 7 ,
.Xr tbl 7
.Sh AUTHORS
The
.Nm
utility was written by
.An Kristaps Dzonsons ,
.Mt kristaps@bsd.lv .
.Sh CAVEATS
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
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 \(aq
control character is an alias for the standard macro control character
and does not emit a line-break as stipulated in GNU troff.

600
contrib/mdocml/mandoc.3 Normal file
View File

@ -0,0 +1,600 @@
.\" $Id: mandoc.3,v 1.17 2012/01/13 15:27:14 joerg Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" 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: January 13 2012 $
.Dt MANDOC 3
.Os
.Sh NAME
.Nm mandoc ,
.Nm mandoc_escape ,
.Nm man_meta ,
.Nm man_mparse ,
.Nm man_node ,
.Nm mchars_alloc ,
.Nm mchars_free ,
.Nm mchars_num2char ,
.Nm mchars_num2uc ,
.Nm mchars_spec2cp ,
.Nm mchars_spec2str ,
.Nm mdoc_meta ,
.Nm mdoc_node ,
.Nm mparse_alloc ,
.Nm mparse_free ,
.Nm mparse_getkeep ,
.Nm mparse_keep ,
.Nm mparse_readfd ,
.Nm mparse_reset ,
.Nm mparse_result ,
.Nm mparse_strerror ,
.Nm mparse_strlevel
.Nd mandoc macro compiler library
.Sh LIBRARY
.Lb mandoc
.Sh SYNOPSIS
.In man.h
.In mdoc.h
.In mandoc.h
.Ft "enum mandoc_esc"
.Fo mandoc_escape
.Fa "const char **end"
.Fa "const char **start"
.Fa "int *sz"
.Fc
.Ft "const struct man_meta *"
.Fo man_meta
.Fa "const struct man *man"
.Fc
.Ft "const struct mparse *"
.Fo man_mparse
.Fa "const struct man *man"
.Fc
.Ft "const struct man_node *"
.Fo man_node
.Fa "const struct man *man"
.Fc
.Ft "struct mchars *"
.Fn mchars_alloc
.Ft void
.Fn mchars_free "struct mchars *p"
.Ft char
.Fn mchars_num2char "const char *cp" "size_t sz"
.Ft int
.Fn mchars_num2uc "const char *cp" "size_t sz"
.Ft "const char *"
.Fo mchars_spec2str
.Fa "const struct mchars *p"
.Fa "const char *cp"
.Fa "size_t sz"
.Fa "size_t *rsz"
.Fc
.Ft int
.Fo mchars_spec2cp
.Fa "const struct mchars *p"
.Fa "const char *cp"
.Fa "size_t sz"
.Ft "const char *"
.Fc
.Ft "const struct mdoc_meta *"
.Fo mdoc_meta
.Fa "const struct mdoc *mdoc"
.Fc
.Ft "const struct mdoc_node *"
.Fo mdoc_node
.Fa "const struct mdoc *mdoc"
.Fc
.Ft void
.Fo mparse_alloc
.Fa "enum mparset type"
.Fa "enum mandoclevel wlevel"
.Fa "mandocmsg msg"
.Fa "void *msgarg"
.Fc
.Ft void
.Fo mparse_free
.Fa "struct mparse *parse"
.Fc
.Ft void
.Fo mparse_getkeep
.Fa "const struct mparse *parse"
.Fc
.Ft void
.Fo mparse_keep
.Fa "struct mparse *parse"
.Fc
.Ft "enum mandoclevel"
.Fo mparse_readfd
.Fa "struct mparse *parse"
.Fa "int fd"
.Fa "const char *fname"
.Fc
.Ft void
.Fo mparse_reset
.Fa "struct mparse *parse"
.Fc
.Ft void
.Fo mparse_result
.Fa "struct mparse *parse"
.Fa "struct mdoc **mdoc"
.Fa "struct man **man"
.Fc
.Ft "const char *"
.Fo mparse_strerror
.Fa "enum mandocerr"
.Fc
.Ft "const char *"
.Fo mparse_strlevel
.Fa "enum mandoclevel"
.Fc
.Vt extern const char * const * man_macronames;
.Vt extern const char * const * mdoc_argnames;
.Vt extern const char * const * mdoc_macronames;
.Fd "#define ASCII_NBRSP"
.Fd "#define ASCII_HYPH"
.Sh DESCRIPTION
The
.Nm mandoc
library parses a
.Ux
manual into an abstract syntax tree (AST).
.Ux
manuals are composed of
.Xr mdoc 7
or
.Xr man 7 ,
and may be mixed with
.Xr roff 7 ,
.Xr tbl 7 ,
and
.Xr eqn 7
invocations.
.Pp
The following describes a general parse sequence:
.Bl -enum
.It
initiate a parsing sequence with
.Fn mparse_alloc ;
.It
parse files or file descriptors with
.Fn mparse_readfd ;
.It
retrieve a parsed syntax tree, if the parse was successful, with
.Fn mparse_result ;
.It
iterate over parse nodes with
.Fn mdoc_node
or
.Fn man_node ;
.It
free all allocated memory with
.Fn mparse_free ,
or invoke
.Fn mparse_reset
and parse new files.
.El
.Pp
The
.Nm
library also contains routines for translating character strings into glyphs
.Pq see Fn mchars_alloc
and parsing escape sequences from strings
.Pq see Fn mandoc_escape .
.Sh REFERENCE
This section documents the functions, types, and variables available
via
.In mandoc.h .
.Ss Types
.Bl -ohang
.It Vt "enum mandoc_esc"
An escape sequence classification.
.It Vt "enum mandocerr"
A fatal error, error, or warning message during parsing.
.It Vt "enum mandoclevel"
A classification of an
.Vt "enum mandoclevel"
as regards system operation.
.It Vt "struct mchars"
An opaque pointer to an object allowing for translation between
character strings and glyphs.
See
.Fn mchars_alloc .
.It Vt "enum mparset"
The type of parser when reading input.
This should usually be
.Dv MPARSE_AUTO
for auto-detection.
.It Vt "struct mparse"
An opaque pointer to a running parse sequence.
Created with
.Fn mparse_alloc
and freed with
.Fn mparse_free .
This may be used across parsed input if
.Fn mparse_reset
is called between parses.
.It Vt "mandocmsg"
A prototype for a function to handle fatal error, error, and warning
messages emitted by the parser.
.El
.Ss Functions
.Bl -ohang
.It Fn mandoc_escape
Scan an escape sequence, i.e., a character string beginning with
.Sq \e .
Pass a pointer to the character after the
.Sq \e
as
.Va end ;
it will be set to the supremum of the parsed escape sequence unless
returning
.Dv ESCAPE_ERROR ,
in which case the string is bogus and should be
thrown away.
If not
.Dv ESCAPE_ERROR
or
.Dv ESCAPE_IGNORE ,
.Va start
is set to the first relevant character of the substring (font, glyph,
whatever) of length
.Va sz .
Both
.Va start
and
.Va sz
may be
.Dv NULL .
.It Fn man_meta
Obtain the meta-data of a successful parse.
This may only be used on a pointer returned by
.Fn mparse_result .
.It Fn man_mparse
Get the parser used for the current output.
.It Fn man_node
Obtain the root node of a successful parse.
This may only be used on a pointer returned by
.Fn mparse_result .
.It Fn mchars_alloc
Allocate an
.Vt "struct mchars *"
object for translating special characters into glyphs.
See
.Xr mandoc_char 7
for an overview of special characters.
The object must be freed with
.Fn mchars_free .
.It Fn mchars_free
Free an object created with
.Fn mchars_alloc .
.It Fn mchars_num2char
Convert a character index (e.g., the \eN\(aq\(aq escape) into a
printable ASCII character.
Returns \e0 (the nil character) if the input sequence is malformed.
.It Fn mchars_num2uc
Convert a hexadecimal character index (e.g., the \e[uNNNN] escape) into
a Unicode codepoint.
Returns \e0 (the nil character) if the input sequence is malformed.
.It Fn mchars_spec2cp
Convert a special character into a valid Unicode codepoint.
Returns \-1 on failure or a non-zero Unicode codepoint on success.
.It Fn mchars_spec2str
Convert a special character into an ASCII string.
Returns
.Dv NULL
on failure.
.It Fn mdoc_meta
Obtain the meta-data of a successful parse.
This may only be used on a pointer returned by
.Fn mparse_result .
.It Fn mdoc_node
Obtain the root node of a successful parse.
This may only be used on a pointer returned by
.Fn mparse_result .
.It Fn mparse_alloc
Allocate a parser.
The same parser may be used for multiple files so long as
.Fn mparse_reset
is called between parses.
.Fn mparse_free
must be called to free the memory allocated by this function.
.It Fn mparse_free
Free all memory allocated by
.Fn mparse_alloc .
.It Fn mparse_getkeep
Acquire the keep buffer.
Must follow a call of
.Fn mparse_keep .
.It Fn mparse_keep
Instruct the parser to retain a copy of its parsed input.
This can be acquired with subsequent
.Fn mparse_getkeep
calls.
.It Fn mparse_readfd
Parse a file or file descriptor.
If
.Va fd
is -1,
.Va fname
is opened for reading.
Otherwise,
.Va fname
is assumed to be the name associated with
.Va fd .
This may be called multiple times with different parameters; however,
.Fn mparse_reset
should be invoked between parses.
.It Fn mparse_reset
Reset a parser so that
.Fn mparse_readfd
may be used again.
.It Fn mparse_result
Obtain the result of a parse.
Only successful parses
.Po
i.e., those where
.Fn mparse_readfd
returned less than MANDOCLEVEL_FATAL
.Pc
should invoke this function, in which case one of the two pointers will
be filled in.
.It Fn mparse_strerror
Return a statically-allocated string representation of an error code.
.It Fn mparse_strlevel
Return a statically-allocated string representation of a level code.
.El
.Ss Variables
.Bl -ohang
.It Va man_macronames
The string representation of a man macro as indexed by
.Vt "enum mant" .
.It Va mdoc_argnames
The string representation of a mdoc macro argument as indexed by
.Vt "enum mdocargt" .
.It Va mdoc_macronames
The string representation of a mdoc macro as indexed by
.Vt "enum mdoct" .
.El
.Sh IMPLEMENTATION NOTES
This section consists of structural documentation for
.Xr mdoc 7
and
.Xr man 7
syntax trees and strings.
.Ss Man and Mdoc Strings
Strings may be extracted from mdoc and man meta-data, or from text
nodes (MDOC_TEXT and MAN_TEXT, respectively).
These strings have special non-printing formatting cues embedded in the
text itself, as well as
.Xr roff 7
escapes preserved from input.
Implementing systems will need to handle both situations to produce
human-readable text.
In general, strings may be assumed to consist of 7-bit ASCII characters.
.Pp
The following non-printing characters may be embedded in text strings:
.Bl -tag -width Ds
.It Dv ASCII_NBRSP
A non-breaking space character.
.It Dv ASCII_HYPH
A soft hyphen.
.El
.Pp
Escape characters are also passed verbatim into text strings.
An escape character is a sequence of characters beginning with the
backslash
.Pq Sq \e .
To construct human-readable text, these should be intercepted with
.Fn mandoc_escape
and converted with one of
.Fn mchars_num2char ,
.Fn mchars_spec2str ,
and so on.
.Ss Man Abstract Syntax Tree
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
\(<- [[:ascii:]]*
.El
.Pp
The only elements capable of nesting other elements are those with
next-lint scope as documented in
.Xr man 7 .
.Ss Mdoc Abstract Syntax Tree
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 nchild ,
.Va next
and
.Va prev
fields) and some type-specific data, in particular, for nodes generated
from macros, the generating macro in the
.Va tok
field.
.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* [ENDBODY mnode*]
.It TAIL
\(<- mnode*
.It TEXT
\(<- [[:ascii:]]*
.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.
.Pp
The
.Xr mdoc 7
syntax tree accommodates for broken block structures as well.
The ENDBODY node is available to end the formatting associated
with a given block before the physical end of that block.
It has a non-null
.Va end
field, is of the BODY
.Va type ,
has the same
.Va tok
as the BLOCK it is ending, and has a
.Va pending
field pointing to that BLOCK's BODY node.
It is an indirect child of that BODY node
and has no children of its own.
.Pp
An ENDBODY node is generated when a block ends while one of its child
blocks is still open, like in the following example:
.Bd -literal -offset indent
\&.Ao ao
\&.Bo bo ac
\&.Ac bc
\&.Bc end
.Ed
.Pp
This example results in the following block structure:
.Bd -literal -offset indent
BLOCK Ao
HEAD Ao
BODY Ao
TEXT ao
BLOCK Bo, pending -> Ao
HEAD Bo
BODY Bo
TEXT bo
TEXT ac
ENDBODY Ao, pending -> Ao
TEXT bc
TEXT end
.Ed
.Pp
Here, the formatting of the
.Sq \&Ao
block extends from TEXT ao to TEXT ac,
while the formatting of the
.Sq \&Bo
block extends from TEXT bo to TEXT bc.
It renders as follows in
.Fl T Ns Cm ascii
mode:
.Pp
.Dl <ao [bo ac> bc] end
.Pp
Support for badly-nested blocks is only provided for backward
compatibility with some older
.Xr mdoc 7
implementations.
Using badly-nested blocks is
.Em strongly discouraged ;
for example, the
.Fl T Ns Cm html
and
.Fl T Ns Cm xhtml
front-ends to
.Xr mandoc 1
are unable to render them in any meaningful way.
Furthermore, behaviour when encountering badly-nested blocks is not
consistent across troff implementations, especially when using multiple
levels of badly-nested blocks.
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr eqn 7 ,
.Xr man 7 ,
.Xr mandoc_char 7 ,
.Xr mdoc 7 ,
.Xr roff 7 ,
.Xr tbl 7
.Sh AUTHORS
The
.Nm
library was written by
.An Kristaps Dzonsons ,
.Mt kristaps@bsd.lv .

735
contrib/mdocml/mandoc.c Normal file
View File

@ -0,0 +1,735 @@
/* $Id: mandoc.c,v 1.62 2011/12/03 16:08:51 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "mandoc.h"
#include "libmandoc.h"
#define DATESIZE 32
static int a2time(time_t *, const char *, const char *);
static char *time2a(time_t);
static int numescape(const char *);
/*
* Pass over recursive numerical expressions. This context of this
* function is important: it's only called within character-terminating
* escapes (e.g., \s[xxxyyy]), so all we need to do is handle initial
* recursion: we don't care about what's in these blocks.
* This returns the number of characters skipped or -1 if an error
* occurs (the caller should bail).
*/
static int
numescape(const char *start)
{
int i;
size_t sz;
const char *cp;
i = 0;
/* The expression consists of a subexpression. */
if ('\\' == start[i]) {
cp = &start[++i];
/*
* Read past the end of the subexpression.
* Bail immediately on errors.
*/
if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
return(-1);
return(i + cp - &start[i]);
}
if ('(' != start[i++])
return(0);
/*
* A parenthesised subexpression. Read until the closing
* parenthesis, making sure to handle any nested subexpressions
* that might ruin our parse.
*/
while (')' != start[i]) {
sz = strcspn(&start[i], ")\\");
i += (int)sz;
if ('\0' == start[i])
return(-1);
else if ('\\' != start[i])
continue;
cp = &start[++i];
if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
return(-1);
i += cp - &start[i];
}
/* Read past the terminating ')'. */
return(++i);
}
enum mandoc_esc
mandoc_escape(const char **end, const char **start, int *sz)
{
char c, term, numeric;
int i, lim, ssz, rlim;
const char *cp, *rstart;
enum mandoc_esc gly;
cp = *end;
rstart = cp;
if (start)
*start = rstart;
i = lim = 0;
gly = ESCAPE_ERROR;
term = numeric = '\0';
switch ((c = cp[i++])) {
/*
* First the glyphs. There are several different forms of
* these, but each eventually returns a substring of the glyph
* name.
*/
case ('('):
gly = ESCAPE_SPECIAL;
lim = 2;
break;
case ('['):
gly = ESCAPE_SPECIAL;
/*
* Unicode escapes are defined in groff as \[uXXXX] to
* \[u10FFFF], where the contained value must be a valid
* Unicode codepoint. Here, however, only check whether
* it's not a zero-width escape.
*/
if ('u' == cp[i] && ']' != cp[i + 1])
gly = ESCAPE_UNICODE;
term = ']';
break;
case ('C'):
if ('\'' != cp[i])
return(ESCAPE_ERROR);
gly = ESCAPE_SPECIAL;
term = '\'';
break;
/*
* Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where
* 'X' is the trigger. These have opaque sub-strings.
*/
case ('F'):
/* FALLTHROUGH */
case ('g'):
/* FALLTHROUGH */
case ('k'):
/* FALLTHROUGH */
case ('M'):
/* FALLTHROUGH */
case ('m'):
/* FALLTHROUGH */
case ('n'):
/* FALLTHROUGH */
case ('V'):
/* FALLTHROUGH */
case ('Y'):
gly = ESCAPE_IGNORE;
/* FALLTHROUGH */
case ('f'):
if (ESCAPE_ERROR == gly)
gly = ESCAPE_FONT;
rstart= &cp[i];
if (start)
*start = rstart;
switch (cp[i++]) {
case ('('):
lim = 2;
break;
case ('['):
term = ']';
break;
default:
lim = 1;
i--;
break;
}
break;
/*
* These escapes are of the form \X'Y', where 'X' is the trigger
* and 'Y' is any string. These have opaque sub-strings.
*/
case ('A'):
/* FALLTHROUGH */
case ('b'):
/* FALLTHROUGH */
case ('D'):
/* FALLTHROUGH */
case ('o'):
/* FALLTHROUGH */
case ('R'):
/* FALLTHROUGH */
case ('X'):
/* FALLTHROUGH */
case ('Z'):
if ('\'' != cp[i++])
return(ESCAPE_ERROR);
gly = ESCAPE_IGNORE;
term = '\'';
break;
/*
* These escapes are of the form \X'N', where 'X' is the trigger
* and 'N' resolves to a numerical expression.
*/
case ('B'):
/* FALLTHROUGH */
case ('h'):
/* FALLTHROUGH */
case ('H'):
/* FALLTHROUGH */
case ('L'):
/* FALLTHROUGH */
case ('l'):
gly = ESCAPE_NUMBERED;
/* FALLTHROUGH */
case ('S'):
/* FALLTHROUGH */
case ('v'):
/* FALLTHROUGH */
case ('w'):
/* FALLTHROUGH */
case ('x'):
if (ESCAPE_ERROR == gly)
gly = ESCAPE_IGNORE;
if ('\'' != cp[i++])
return(ESCAPE_ERROR);
term = numeric = '\'';
break;
/*
* Special handling for the numbered character escape.
* XXX Do any other escapes need similar handling?
*/
case ('N'):
if ('\0' == cp[i])
return(ESCAPE_ERROR);
*end = &cp[++i];
if (isdigit((unsigned char)cp[i-1]))
return(ESCAPE_IGNORE);
while (isdigit((unsigned char)**end))
(*end)++;
if (start)
*start = &cp[i];
if (sz)
*sz = *end - &cp[i];
if ('\0' != **end)
(*end)++;
return(ESCAPE_NUMBERED);
/*
* Sizes get a special category of their own.
*/
case ('s'):
gly = ESCAPE_IGNORE;
rstart = &cp[i];
if (start)
*start = rstart;
/* See +/- counts as a sign. */
c = cp[i];
if ('+' == c || '-' == c || ASCII_HYPH == c)
++i;
switch (cp[i++]) {
case ('('):
lim = 2;
break;
case ('['):
term = numeric = ']';
break;
case ('\''):
term = numeric = '\'';
break;
default:
lim = 1;
i--;
break;
}
/* See +/- counts as a sign. */
c = cp[i];
if ('+' == c || '-' == c || ASCII_HYPH == c)
++i;
break;
/*
* Anything else is assumed to be a glyph.
*/
default:
gly = ESCAPE_SPECIAL;
lim = 1;
i--;
break;
}
assert(ESCAPE_ERROR != gly);
rstart = &cp[i];
if (start)
*start = rstart;
/*
* If a terminating block has been specified, we need to
* handle the case of recursion, which could have their
* own terminating blocks that mess up our parse. This, by the
* way, means that the "start" and "size" values will be
* effectively meaningless.
*/
ssz = 0;
if (numeric && -1 == (ssz = numescape(&cp[i])))
return(ESCAPE_ERROR);
i += ssz;
rlim = -1;
/*
* We have a character terminator. Try to read up to that
* character. If we can't (i.e., we hit the nil), then return
* an error; if we can, calculate our length, read past the
* terminating character, and exit.
*/
if ('\0' != term) {
*end = strchr(&cp[i], term);
if ('\0' == *end)
return(ESCAPE_ERROR);
rlim = *end - &cp[i];
if (sz)
*sz = rlim;
(*end)++;
goto out;
}
assert(lim > 0);
/*
* We have a numeric limit. If the string is shorter than that,
* stop and return an error. Else adjust our endpoint, length,
* and return the current glyph.
*/
if ((size_t)lim > strlen(&cp[i]))
return(ESCAPE_ERROR);
rlim = lim;
if (sz)
*sz = rlim;
*end = &cp[i] + lim;
out:
assert(rlim >= 0 && rstart);
/* Run post-processors. */
switch (gly) {
case (ESCAPE_FONT):
/*
* Pretend that the constant-width font modes are the
* same as the regular font modes.
*/
if (2 == rlim && 'C' == *rstart)
rstart++;
else if (1 != rlim)
break;
switch (*rstart) {
case ('3'):
/* FALLTHROUGH */
case ('B'):
gly = ESCAPE_FONTBOLD;
break;
case ('2'):
/* FALLTHROUGH */
case ('I'):
gly = ESCAPE_FONTITALIC;
break;
case ('P'):
gly = ESCAPE_FONTPREV;
break;
case ('1'):
/* FALLTHROUGH */
case ('R'):
gly = ESCAPE_FONTROMAN;
break;
}
break;
case (ESCAPE_SPECIAL):
if (1 != rlim)
break;
if ('c' == *rstart)
gly = ESCAPE_NOSPACE;
break;
default:
break;
}
return(gly);
}
void *
mandoc_calloc(size_t num, size_t size)
{
void *ptr;
ptr = calloc(num, size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
void *
mandoc_malloc(size_t size)
{
void *ptr;
ptr = malloc(size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
void *
mandoc_realloc(void *ptr, size_t size)
{
ptr = realloc(ptr, size);
if (NULL == ptr) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(ptr);
}
char *
mandoc_strndup(const char *ptr, size_t sz)
{
char *p;
p = mandoc_malloc(sz + 1);
memcpy(p, ptr, sz);
p[(int)sz] = '\0';
return(p);
}
char *
mandoc_strdup(const char *ptr)
{
char *p;
p = strdup(ptr);
if (NULL == p) {
perror(NULL);
exit((int)MANDOCLEVEL_SYSERR);
}
return(p);
}
/*
* Parse a quoted or unquoted roff-style request or macro argument.
* Return a pointer to the parsed argument, which is either the original
* pointer or advanced by one byte in case the argument is quoted.
* Null-terminate the argument in place.
* Collapse pairs of quotes inside quoted arguments.
* Advance the argument pointer to the next argument,
* or to the null byte terminating the argument line.
*/
char *
mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
{
char *start, *cp;
int quoted, pairs, white;
/* Quoting can only start with a new word. */
start = *cpp;
quoted = 0;
if ('"' == *start) {
quoted = 1;
start++;
}
pairs = 0;
white = 0;
for (cp = start; '\0' != *cp; cp++) {
/* Move left after quoted quotes and escaped backslashes. */
if (pairs)
cp[-pairs] = cp[0];
if ('\\' == cp[0]) {
if ('\\' == cp[1]) {
/* Poor man's copy mode. */
pairs++;
cp++;
} else if (0 == quoted && ' ' == cp[1])
/* Skip escaped blanks. */
cp++;
} else if (0 == quoted) {
if (' ' == cp[0]) {
/* Unescaped blanks end unquoted args. */
white = 1;
break;
}
} else if ('"' == cp[0]) {
if ('"' == cp[1]) {
/* Quoted quotes collapse. */
pairs++;
cp++;
} else {
/* Unquoted quotes end quoted args. */
quoted = 2;
break;
}
}
}
/* Quoted argument without a closing quote. */
if (1 == quoted)
mandoc_msg(MANDOCERR_BADQUOTE, parse, ln, *pos, NULL);
/* Null-terminate this argument and move to the next one. */
if (pairs)
cp[-pairs] = '\0';
if ('\0' != *cp) {
*cp++ = '\0';
while (' ' == *cp)
cp++;
}
*pos += (int)(cp - start) + (quoted ? 1 : 0);
*cpp = cp;
if ('\0' == *cp && (white || ' ' == cp[-1]))
mandoc_msg(MANDOCERR_EOLNSPACE, parse, ln, *pos, NULL);
return(start);
}
static int
a2time(time_t *t, const char *fmt, const char *p)
{
struct tm tm;
char *pp;
memset(&tm, 0, sizeof(struct tm));
pp = NULL;
#ifdef HAVE_STRPTIME
pp = strptime(p, fmt, &tm);
#endif
if (NULL != pp && '\0' == *pp) {
*t = mktime(&tm);
return(1);
}
return(0);
}
static char *
time2a(time_t t)
{
struct tm *tm;
char *buf, *p;
size_t ssz;
int isz;
tm = localtime(&t);
/*
* Reserve space:
* up to 9 characters for the month (September) + blank
* up to 2 characters for the day + comma + blank
* 4 characters for the year and a terminating '\0'
*/
p = buf = mandoc_malloc(10 + 4 + 4 + 1);
if (0 == (ssz = strftime(p, 10 + 1, "%B ", tm)))
goto fail;
p += (int)ssz;
if (-1 == (isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)))
goto fail;
p += isz;
if (0 == strftime(p, 4 + 1, "%Y", tm))
goto fail;
return(buf);
fail:
free(buf);
return(NULL);
}
char *
mandoc_normdate(struct mparse *parse, char *in, int ln, int pos)
{
char *out;
time_t t;
if (NULL == in || '\0' == *in ||
0 == strcmp(in, "$" "Mdocdate$")) {
mandoc_msg(MANDOCERR_NODATE, parse, ln, pos, NULL);
time(&t);
}
else if (a2time(&t, "%Y-%m-%d", in))
t = 0;
else if (!a2time(&t, "$" "Mdocdate: %b %d %Y $", in) &&
!a2time(&t, "%b %d, %Y", in)) {
mandoc_msg(MANDOCERR_BADDATE, parse, ln, pos, NULL);
t = 0;
}
out = t ? time2a(t) : NULL;
return(out ? out : mandoc_strdup(in));
}
int
mandoc_eos(const char *p, size_t sz, int enclosed)
{
const char *q;
int found;
if (0 == sz)
return(0);
/*
* End-of-sentence recognition must include situations where
* some symbols, such as `)', allow prior EOS punctuation to
* propagate outward.
*/
found = 0;
for (q = p + (int)sz - 1; q >= p; q--) {
switch (*q) {
case ('\"'):
/* FALLTHROUGH */
case ('\''):
/* FALLTHROUGH */
case (']'):
/* FALLTHROUGH */
case (')'):
if (0 == found)
enclosed = 1;
break;
case ('.'):
/* FALLTHROUGH */
case ('!'):
/* FALLTHROUGH */
case ('?'):
found = 1;
break;
default:
return(found && (!enclosed || isalnum((unsigned char)*q)));
}
}
return(found && !enclosed);
}
/*
* Find out whether a line is a macro line or not. If it is, adjust the
* current position and return one; if it isn't, return zero and don't
* change the current position.
*/
int
mandoc_getcontrol(const char *cp, int *ppos)
{
int pos;
pos = *ppos;
if ('\\' == cp[pos] && '.' == cp[pos + 1])
pos += 2;
else if ('.' == cp[pos] || '\'' == cp[pos])
pos++;
else
return(0);
while (' ' == cp[pos] || '\t' == cp[pos])
pos++;
*ppos = pos;
return(1);
}
/*
* Convert a string to a long that may not be <0.
* If the string is invalid, or is less than 0, return -1.
*/
int
mandoc_strntoi(const char *p, size_t sz, int base)
{
char buf[32];
char *ep;
long v;
if (sz > 31)
return(-1);
memcpy(buf, p, sz);
buf[(int)sz] = '\0';
errno = 0;
v = strtol(buf, &ep, base);
if (buf[0] == '\0' || *ep != '\0')
return(-1);
if (v > INT_MAX)
v = INT_MAX;
if (v < INT_MIN)
v = INT_MIN;
return((int)v);
}

432
contrib/mdocml/mandoc.h Normal file
View File

@ -0,0 +1,432 @@
/* $Id: mandoc.h,v 1.99 2012/02/16 20:51:31 joerg Exp $ */
/*
* Copyright (c) 2010, 2011 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 */
/*
* Status level. This refers to both internal status (i.e., whilst
* running, when warnings/errors are reported) and an indicator of a
* threshold of when to halt (when said internal state exceeds the
* threshold).
*/
enum mandoclevel {
MANDOCLEVEL_OK = 0,
MANDOCLEVEL_RESERVED,
MANDOCLEVEL_WARNING, /* warnings: syntax, whitespace, etc. */
MANDOCLEVEL_ERROR, /* input has been thrown away */
MANDOCLEVEL_FATAL, /* input is borked */
MANDOCLEVEL_BADARG, /* bad argument in invocation */
MANDOCLEVEL_SYSERR, /* system error */
MANDOCLEVEL_MAX
};
/*
* All possible things that can go wrong within a parse, be it libroff,
* libmdoc, or libman.
*/
enum mandocerr {
MANDOCERR_OK,
MANDOCERR_WARNING, /* ===== start of warnings ===== */
/* related to the prologue */
MANDOCERR_NOTITLE, /* no title in document */
MANDOCERR_UPPERCASE, /* document title should be all caps */
MANDOCERR_BADMSEC, /* unknown manual section */
MANDOCERR_NODATE, /* date missing, using today's date */
MANDOCERR_BADDATE, /* cannot parse date, using it verbatim */
MANDOCERR_PROLOGOOO, /* prologue macros out of order */
MANDOCERR_PROLOGREP, /* duplicate prologue macro */
MANDOCERR_BADPROLOG, /* macro not allowed in prologue */
MANDOCERR_BADBODY, /* macro not allowed in body */
/* related to document structure */
MANDOCERR_SO, /* .so is fragile, better use ln(1) */
MANDOCERR_NAMESECFIRST, /* NAME section must come first */
MANDOCERR_BADNAMESEC, /* bad NAME section contents */
MANDOCERR_NONAME, /* manual name not yet set */
MANDOCERR_SECOOO, /* sections out of conventional order */
MANDOCERR_SECREP, /* duplicate section name */
MANDOCERR_SECMSEC, /* section not in conventional manual section */
/* related to macros and nesting */
MANDOCERR_MACROOBS, /* skipping obsolete macro */
MANDOCERR_IGNPAR, /* skipping paragraph macro */
MANDOCERR_IGNNS, /* skipping no-space macro */
MANDOCERR_SCOPENEST, /* blocks badly nested */
MANDOCERR_CHILD, /* child violates parent syntax */
MANDOCERR_NESTEDDISP, /* nested displays are not portable */
MANDOCERR_SCOPEREP, /* already in literal mode */
MANDOCERR_LINESCOPE, /* line scope broken */
/* related to missing macro arguments */
MANDOCERR_MACROEMPTY, /* skipping empty macro */
MANDOCERR_ARGCWARN, /* argument count wrong */
MANDOCERR_DISPTYPE, /* missing display type */
MANDOCERR_LISTFIRST, /* list type must come first */
MANDOCERR_NOWIDTHARG, /* tag lists require a width argument */
MANDOCERR_FONTTYPE, /* missing font type */
MANDOCERR_WNOSCOPE, /* skipping end of block that is not open */
/* related to bad macro arguments */
MANDOCERR_IGNARGV, /* skipping argument */
MANDOCERR_ARGVREP, /* duplicate argument */
MANDOCERR_DISPREP, /* duplicate display type */
MANDOCERR_LISTREP, /* duplicate list type */
MANDOCERR_BADATT, /* unknown AT&T UNIX version */
MANDOCERR_BADBOOL, /* bad Boolean value */
MANDOCERR_BADFONT, /* unknown font */
MANDOCERR_BADSTANDARD, /* unknown standard specifier */
MANDOCERR_BADWIDTH, /* bad width argument */
/* related to plain text */
MANDOCERR_NOBLANKLN, /* blank line in non-literal context */
MANDOCERR_BADTAB, /* tab in non-literal context */
MANDOCERR_EOLNSPACE, /* end of line whitespace */
MANDOCERR_BADCOMMENT, /* bad comment style */
MANDOCERR_BADESCAPE, /* unknown escape sequence */
MANDOCERR_BADQUOTE, /* unterminated quoted string */
/* related to equations */
MANDOCERR_EQNQUOTE, /* unexpected literal in equation */
MANDOCERR_ERROR, /* ===== start of errors ===== */
/* related to equations */
MANDOCERR_EQNNSCOPE, /* unexpected equation scope closure*/
MANDOCERR_EQNSCOPE, /* equation scope open on exit */
MANDOCERR_EQNBADSCOPE, /* overlapping equation scopes */
MANDOCERR_EQNEOF, /* unexpected end of equation */
MANDOCERR_EQNSYNT, /* equation syntax error */
/* related to tables */
MANDOCERR_TBL, /* bad table syntax */
MANDOCERR_TBLOPT, /* bad table option */
MANDOCERR_TBLLAYOUT, /* bad table layout */
MANDOCERR_TBLNOLAYOUT, /* no table layout cells specified */
MANDOCERR_TBLNODATA, /* no table data cells specified */
MANDOCERR_TBLIGNDATA, /* ignore data in cell */
MANDOCERR_TBLBLOCK, /* data block still open */
MANDOCERR_TBLEXTRADAT, /* ignoring extra data cells */
MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */
MANDOCERR_BADCHAR, /* skipping bad character */
MANDOCERR_NAMESC, /* escaped character not allowed in a name */
MANDOCERR_NOTEXT, /* skipping text before the first section header */
MANDOCERR_MACRO, /* skipping unknown macro */
MANDOCERR_REQUEST, /* NOT IMPLEMENTED: skipping request */
MANDOCERR_ARGCOUNT, /* argument count wrong */
MANDOCERR_NOSCOPE, /* skipping end of block that is not open */
MANDOCERR_SCOPEBROKEN, /* missing end of block */
MANDOCERR_SCOPEEXIT, /* scope open on exit */
MANDOCERR_UNAME, /* uname(3) system call failed */
/* 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_LISTTYPE, /* missing list type */
MANDOCERR_ARGSLOST, /* line argument(s) will be lost */
MANDOCERR_BODYLOST, /* body argument(s) will be lost */
MANDOCERR_FATAL, /* ===== start of fatal errors ===== */
MANDOCERR_NOTMANUAL, /* manual isn't really a manual */
MANDOCERR_COLUMNS, /* column syntax is inconsistent */
MANDOCERR_BADDISP, /* NOT IMPLEMENTED: .Bd -file */
MANDOCERR_SYNTARGVCOUNT, /* argument count wrong, violates syntax */
MANDOCERR_SYNTCHILD, /* child violates parent syntax */
MANDOCERR_SYNTARGCOUNT, /* argument count wrong, violates syntax */
MANDOCERR_SOPATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */
MANDOCERR_NODOCBODY, /* no document body */
MANDOCERR_NODOCPROLOG, /* no document prologue */
MANDOCERR_MEM, /* static buffer exhausted */
MANDOCERR_MAX
};
struct tbl {
char tab; /* cell-separator */
char decimal; /* decimal point */
int linesize;
int opts;
#define TBL_OPT_CENTRE (1 << 0)
#define TBL_OPT_EXPAND (1 << 1)
#define TBL_OPT_BOX (1 << 2)
#define TBL_OPT_DBOX (1 << 3)
#define TBL_OPT_ALLBOX (1 << 4)
#define TBL_OPT_NOKEEP (1 << 5)
#define TBL_OPT_NOSPACE (1 << 6)
int cols; /* number of columns */
};
enum tbl_headt {
TBL_HEAD_DATA, /* plug in data from tbl_dat */
TBL_HEAD_VERT, /* vertical spacer */
TBL_HEAD_DVERT /* double-vertical spacer */
};
/*
* The head of a table specifies all of its columns. When formatting a
* tbl_span, iterate over these and plug in data from the tbl_span when
* appropriate, using tbl_cell as a guide to placement.
*/
struct tbl_head {
enum tbl_headt pos;
int ident; /* 0 <= unique id < cols */
struct tbl_head *next;
struct tbl_head *prev;
};
enum tbl_cellt {
TBL_CELL_CENTRE, /* c, C */
TBL_CELL_RIGHT, /* r, R */
TBL_CELL_LEFT, /* l, L */
TBL_CELL_NUMBER, /* n, N */
TBL_CELL_SPAN, /* s, S */
TBL_CELL_LONG, /* a, A */
TBL_CELL_DOWN, /* ^ */
TBL_CELL_HORIZ, /* _, - */
TBL_CELL_DHORIZ, /* = */
TBL_CELL_VERT, /* | */
TBL_CELL_DVERT, /* || */
TBL_CELL_MAX
};
/*
* A cell in a layout row.
*/
struct tbl_cell {
struct tbl_cell *next;
enum tbl_cellt pos;
size_t spacing;
int flags;
#define TBL_CELL_TALIGN (1 << 0) /* t, T */
#define TBL_CELL_BALIGN (1 << 1) /* d, D */
#define TBL_CELL_BOLD (1 << 2) /* fB, B, b */
#define TBL_CELL_ITALIC (1 << 3) /* fI, I, i */
#define TBL_CELL_EQUAL (1 << 4) /* e, E */
#define TBL_CELL_UP (1 << 5) /* u, U */
#define TBL_CELL_WIGN (1 << 6) /* z, Z */
struct tbl_head *head;
};
/*
* A layout row.
*/
struct tbl_row {
struct tbl_row *next;
struct tbl_cell *first;
struct tbl_cell *last;
};
enum tbl_datt {
TBL_DATA_NONE, /* has no data */
TBL_DATA_DATA, /* consists of data/string */
TBL_DATA_HORIZ, /* horizontal line */
TBL_DATA_DHORIZ, /* double-horizontal line */
TBL_DATA_NHORIZ, /* squeezed horizontal line */
TBL_DATA_NDHORIZ /* squeezed double-horizontal line */
};
/*
* A cell within a row of data. The "string" field contains the actual
* string value that's in the cell. The rest is layout.
*/
struct tbl_dat {
struct tbl_cell *layout; /* layout cell */
int spans; /* how many spans follow */
struct tbl_dat *next;
char *string; /* data (NULL if not TBL_DATA_DATA) */
enum tbl_datt pos;
};
enum tbl_spant {
TBL_SPAN_DATA, /* span consists of data */
TBL_SPAN_HORIZ, /* span is horizontal line */
TBL_SPAN_DHORIZ /* span is double horizontal line */
};
/*
* A row of data in a table.
*/
struct tbl_span {
struct tbl *tbl;
struct tbl_head *head;
struct tbl_row *layout; /* layout row */
struct tbl_dat *first;
struct tbl_dat *last;
int line; /* parse line */
int flags;
#define TBL_SPAN_FIRST (1 << 0)
#define TBL_SPAN_LAST (1 << 1)
enum tbl_spant pos;
struct tbl_span *next;
};
enum eqn_boxt {
EQN_ROOT, /* root of parse tree */
EQN_TEXT, /* text (number, variable, whatever) */
EQN_SUBEXPR, /* nested `eqn' subexpression */
EQN_LIST, /* subexpressions list */
EQN_MATRIX /* matrix subexpression */
};
enum eqn_markt {
EQNMARK_NONE = 0,
EQNMARK_DOT,
EQNMARK_DOTDOT,
EQNMARK_HAT,
EQNMARK_TILDE,
EQNMARK_VEC,
EQNMARK_DYAD,
EQNMARK_BAR,
EQNMARK_UNDER,
EQNMARK__MAX
};
enum eqn_fontt {
EQNFONT_NONE = 0,
EQNFONT_ROMAN,
EQNFONT_BOLD,
EQNFONT_FAT,
EQNFONT_ITALIC,
EQNFONT__MAX
};
enum eqn_post {
EQNPOS_NONE = 0,
EQNPOS_OVER,
EQNPOS_SUP,
EQNPOS_SUB,
EQNPOS_TO,
EQNPOS_FROM,
EQNPOS__MAX
};
enum eqn_pilet {
EQNPILE_NONE = 0,
EQNPILE_PILE,
EQNPILE_CPILE,
EQNPILE_RPILE,
EQNPILE_LPILE,
EQNPILE_COL,
EQNPILE_CCOL,
EQNPILE_RCOL,
EQNPILE_LCOL,
EQNPILE__MAX
};
/*
* A "box" is a parsed mathematical expression as defined by the eqn.7
* grammar.
*/
struct eqn_box {
int size; /* font size of expression */
#define EQN_DEFSIZE INT_MIN
enum eqn_boxt type; /* type of node */
struct eqn_box *first; /* first child node */
struct eqn_box *last; /* last child node */
struct eqn_box *next; /* node sibling */
struct eqn_box *parent; /* node sibling */
char *text; /* text (or NULL) */
char *left;
char *right;
enum eqn_post pos; /* position of next box */
enum eqn_markt mark; /* a mark about the box */
enum eqn_fontt font; /* font of box */
enum eqn_pilet pile; /* equation piling */
};
/*
* An equation consists of a tree of expressions starting at a given
* line and position.
*/
struct eqn {
char *name; /* identifier (or NULL) */
struct eqn_box *root; /* root mathematical expression */
int ln; /* invocation line */
int pos; /* invocation position */
};
/*
* The type of parse sequence. This value is usually passed via the
* mandoc(1) command line of -man and -mdoc. It's almost exclusively
* -mandoc but the others have been retained for compatibility.
*/
enum mparset {
MPARSE_AUTO, /* magically determine the document type */
MPARSE_MDOC, /* assume -mdoc */
MPARSE_MAN /* assume -man */
};
enum mandoc_esc {
ESCAPE_ERROR = 0, /* bail! unparsable escape */
ESCAPE_IGNORE, /* escape to be ignored */
ESCAPE_SPECIAL, /* a regular special character */
ESCAPE_FONT, /* a generic font mode */
ESCAPE_FONTBOLD, /* bold font mode */
ESCAPE_FONTITALIC, /* italic font mode */
ESCAPE_FONTROMAN, /* roman font mode */
ESCAPE_FONTPREV, /* previous font mode */
ESCAPE_NUMBERED, /* a numbered glyph */
ESCAPE_UNICODE, /* a unicode codepoint */
ESCAPE_NOSPACE /* suppress space if the last on a line */
};
typedef void (*mandocmsg)(enum mandocerr, enum mandoclevel,
const char *, int, int, const char *);
struct mparse;
struct mchars;
struct mdoc;
struct man;
__BEGIN_DECLS
void *mandoc_calloc(size_t, size_t);
enum mandoc_esc mandoc_escape(const char **, const char **, int *);
void *mandoc_malloc(size_t);
void *mandoc_realloc(void *, size_t);
char *mandoc_strdup(const char *);
char *mandoc_strndup(const char *, size_t);
struct mchars *mchars_alloc(void);
void mchars_free(struct mchars *);
char mchars_num2char(const char *, size_t);
int mchars_num2uc(const char *, size_t);
int mchars_spec2cp(const struct mchars *,
const char *, size_t);
const char *mchars_spec2str(const struct mchars *,
const char *, size_t, size_t *);
struct mparse *mparse_alloc(enum mparset,
enum mandoclevel, mandocmsg, void *);
void mparse_free(struct mparse *);
void mparse_keep(struct mparse *);
enum mandoclevel mparse_readfd(struct mparse *, int, const char *);
enum mandoclevel mparse_readmem(struct mparse *, const void *, size_t,
const char *);
void mparse_reset(struct mparse *);
void mparse_result(struct mparse *,
struct mdoc **, struct man **);
const char *mparse_getkeep(const struct mparse *);
const char *mparse_strerror(enum mandocerr);
const char *mparse_strlevel(enum mandoclevel);
__END_DECLS
#endif /*!MANDOC_H*/

View File

@ -0,0 +1,743 @@
.\" $Id: mandoc_char.7,v 1.51 2011/11/23 10:09:30 kristaps Exp $
.\"
.\" Copyright (c) 2003 Jason McIntyre <jmc@openbsd.org>
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" 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: November 23 2011 $
.Dt MANDOC_CHAR 7
.Os
.Sh NAME
.Nm mandoc_char
.Nd mandoc special characters
.Sh DESCRIPTION
This page documents the
.Xr roff 7
escape sequences accepted by
.Xr mandoc 1
to represent special characters in
.Xr mdoc 7
and
.Xr man 7
documents.
.Pp
The rendering depends on the
.Xr mandoc 1
output mode; in ASCII output, most characters are completely
unintelligible.
For that reason, using any of the special characters documented here,
except those discussed in the
.Sx DESCRIPTION ,
is strongly discouraged; they are supported merely for backwards
compatibility with existing documents.
.Pp
In particular, in English manual pages, do not use special-character
escape sequences to represent national language characters in author
names; instead, provide ASCII transcriptions of the names.
.Ss Dashes and Hyphens
In typography there are different types of dashes of various width:
the hyphen (-),
the minus sign (\-),
the en-dash (\(en),
and the em-dash (\(em).
.Pp
Hyphens are used for adjectives;
to separate the two parts of a compound word;
or to separate a word across two successive lines of text.
The hyphen does not need to be escaped:
.Bd -unfilled -offset indent
blue-eyed
lorry-driver
.Ed
.Pp
The mathematical minus sign is used for negative numbers or subtraction.
It should be written as
.Sq \e- :
.Bd -unfilled -offset indent
a = 3 \e- 1;
b = \e-2;
.Ed
.Pp
The en-dash is used to separate the two elements of a range,
or can be used the same way as an em-dash.
It should be written as
.Sq \e(en :
.Bd -unfilled -offset indent
pp. 95\e(en97.
Go away \e(en or else!
.Ed
.Pp
The em-dash can be used to show an interruption
or can be used the same way as colons, semi-colons, or parentheses.
It should be written as
.Sq \e(em :
.Bd -unfilled -offset indent
Three things \e(em apples, oranges, and bananas.
This is not that \e(em rather, this is that.
.Ed
.Pp
Note:
hyphens, minus signs, and en-dashes look identical under normal ASCII output.
Other formats, such as PostScript, render them correctly,
with differing widths.
.Ss Spaces
To separate words in normal text, for indenting and alignment
in literal context, and when none of the following special cases apply,
just use the normal space character
.Pq Sq \ .
.Pp
When filling text, lines may be broken between words, i.e. at space
characters.
To prevent a line break between two particular words,
use the non-breaking space escape sequence
.Pq Sq \e~
instead of the normal space character.
For example, the input string
.Dq number\e~1
will be kept together as
.Dq number\~1
on the same output line.
.Pp
On request and macro lines, the normal space character serves as an
argument delimiter.
To include whitespace into arguments, quoting is usually the best choice.
In some cases, using either the non-breaking
.Pq Sq \e~
or the breaking
.Pq Sq \e\ \&
space escape sequence may be preferable.
To escape macro names and to protect whitespace at the end
of input lines, the zero-width space
.Pq Sq \e&
is often useful.
For example, in
.Xr mdoc 7 ,
a normal space character can be displayed in single quotes in either
of the following ways:
.Pp
.Dl .Sq \(dq \(dq
.Dl .Sq \e \e&
.Ss Quotes
On request and macro lines, the double-quote character
.Pq Sq \(dq
is handled specially to allow quoting.
One way to prevent this special handling is by using the
.Sq \e(dq
escape sequence.
.Pp
Note that on text lines, literal double-quote characters can be used
verbatim.
All other quote-like characters can be used verbatim as well,
even on request and macro lines.
.Ss Periods
The period
.Pq Sq \&.
is handled specially at the beginning of an input line,
where it introduces a
.Xr roff 7
request or a macro, and when appearing alone as a macro argument in
.Xr mdoc 7 .
In such situations, prepend a zero-width space
.Pq Sq \e&.
to make it behave like normal text.
.Pp
Do not use the
.Sq \e.
escape sequence.
It does not prevent special handling of the period.
.Ss Backslashes
To include a literal backslash
.Pq Sq \e
into the output, use the
.Pq Sq \ee
escape sequence.
.Pp
Note that doubling it
.Pq Sq \e\e
is not the right way to output a backslash.
Because
.Xr mandoc 1
does not implement full
.Xr roff 7
functionality, it may work with
.Xr mandoc 1 ,
but it may have weird effects on complete
.Xr roff 7
implementations.
.Sh SPECIAL CHARACTERS
Special characters are encoded as
.Sq \eX
.Pq for a one-character escape ,
.Sq \e(XX
.Pq two-character ,
and
.Sq \e[N]
.Pq N-character .
For details, see the
.Em Special Characters
subsection of the
.Xr roff 7
manual.
.Pp
Spacing:
.Bl -column "Input" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "xxbracketrightbpx" Rendered Description -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "xxcoproductxx" "Rendered" "Description" -offset indent -compact
.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
.It \e[12] Ta \[12] Ta one-half
.It \e[14] Ta \[14] Ta one-fourth
.It \e[34] Ta \[34] Ta three-fourths
.El
.Pp
Ligatures:
.Bl -column "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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 "Input" "Rendered" "Description" -offset indent -compact
.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
Predefined strings are inherited from the macro packages of historical
troff implementations.
They are
.Em not recommended
for use, as they differ across implementations.
Manuals using these predefined strings are almost certainly not
portable.
.Pp
Their syntax is 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 .
For details, see the
.Em Predefined Strings
subsection of the
.Xr roff 7
manual.
.Bl -column "Input" "Rendered" "Description" -offset indent
.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
.It \e*(Px Ta \*(Px Ta POSIX standard name
.It \e*(Ai Ta \*(Ai Ta ANSI standard name
.El
.Sh UNICODE CHARACTERS
The escape sequence
.Pp
.Dl \e[uXXXX]
.Pp
is interpreted as a Unicode codepoint.
The codepoint must be in the range above U+0080 and less than U+10FFFF.
For compatibility, points must be zero-padded to four characters; if
greater than four characters, no zero padding is allowed.
Unicode surrogates are not allowed.
.\" .Pp
.\" Unicode glyphs attenuate to the
.\" .Sq \&?
.\" character if invalid or not rendered by current output media.
.Sh NUMBERED CHARACTERS
For backward compatibility with existing manuals,
.Xr mandoc 1
also supports the
.Pp
.Dl \eN\(aq Ns Ar number Ns \(aq
.Pp
escape sequence, inserting the character
.Ar number
from the current character set into the output.
Of course, this is inherently non-portable and is already marked
as deprecated in the Heirloom roff manual.
For example, do not use \eN'34', use \e(dq, or even the plain
.Sq \(dq
character where possible.
.Sh COMPATIBILITY
This section documents compatibility between mandoc and other other
troff implementations, at this time limited to GNU troff
.Pq Qq groff .
.Pp
.Bl -dash -compact
.It
The \eN\(aq\(aq escape sequence is limited to printable characters; in
groff, it accepts arbitrary character numbers.
.It
In
.Fl T Ns Cm ascii ,
the
\e(ss, \e(nm, \e(nb, \e(nc, \e(ib, \e(ip, \e(pp, \e[sum], \e[product],
\e[coproduct], \e(gr, \e(\-h, and \e(a. special characters render
differently between mandoc and groff.
.It
In
.Fl T Ns Cm html
and
.Fl T Ns Cm xhtml ,
the \e(~=, \e(nb, and \e(nc special characters render differently
between mandoc and groff.
.It
The
.Fl T Ns Cm ps
and
.Fl T Ns Cm pdf
modes format like
.Fl T Ns Cm ascii
instead of rendering glyphs as in groff.
.It
The \e[radicalex], \e[sqrtex], and \e(ru special characters have been omitted
from mandoc either because they are poorly documented or they have no
known representation.
.El
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr man 7 ,
.Xr mdoc 7 ,
.Xr roff 7
.Sh AUTHORS
The
.Nm
manual page was written by
.An Kristaps Dzonsons ,
.Mt 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.

3172
contrib/mdocml/mdoc.7 Normal file

File diff suppressed because it is too large Load Diff

987
contrib/mdocml/mdoc.c Normal file
View File

@ -0,0 +1,987 @@
/* $Id: mdoc.c,v 1.196 2011/09/30 00:13:28 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mdoc.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 *);
#if 0
static int mdoc_preptext(struct mdoc *, int, char *, int);
#endif
static int mdoc_ptext(struct mdoc *, int, char *, int);
static int mdoc_pmacro(struct mdoc *, int, char *, int);
const struct mdoc_node *
mdoc_node(const struct mdoc *m)
{
assert( ! (MDOC_HALT & m->flags));
return(m->first);
}
const struct mdoc_meta *
mdoc_meta(const struct mdoc *m)
{
assert( ! (MDOC_HALT & m->flags));
return(&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);
if (mdoc->meta.date)
free(mdoc->meta.date);
}
/*
* 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->last->tok = MDOC_MAX;
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(struct roff *roff, struct mparse *parse)
{
struct mdoc *p;
p = mandoc_calloc(1, sizeof(struct mdoc));
p->parse = parse;
p->roff = roff;
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)
{
assert( ! (MDOC_HALT & m->flags));
if (mdoc_macroend(m))
return(1);
m->flags |= MDOC_HALT;
return(0);
}
int
mdoc_addeqn(struct mdoc *m, const struct eqn *ep)
{
struct mdoc_node *n;
assert( ! (MDOC_HALT & m->flags));
/* No text before an initial macro. */
if (SEC_NONE == m->lastnamed) {
mdoc_pmsg(m, ep->ln, ep->pos, MANDOCERR_NOTEXT);
return(1);
}
n = node_alloc(m, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN);
n->eqn = ep;
if ( ! node_append(m, n))
return(0);
m->next = MDOC_NEXT_SIBLING;
return(1);
}
int
mdoc_addspan(struct mdoc *m, const struct tbl_span *sp)
{
struct mdoc_node *n;
assert( ! (MDOC_HALT & m->flags));
/* No text before an initial macro. */
if (SEC_NONE == m->lastnamed) {
mdoc_pmsg(m, sp->line, 0, MANDOCERR_NOTEXT);
return(1);
}
n = node_alloc(m, sp->line, 0, MDOC_MAX, MDOC_TBL);
n->span = sp;
if ( ! node_append(m, n))
return(0);
m->next = MDOC_NEXT_SIBLING;
return(1);
}
/*
* 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)
{
assert( ! (MDOC_HALT & m->flags));
m->flags |= MDOC_NEWLINE;
/*
* Let the roff nS register switch SYNOPSIS mode early,
* such that the parser knows at all times
* whether this mode is on or off.
* Note that this mode is also switched by the Sh macro.
*/
if (roff_regisset(m->roff, REG_nS)) {
if (roff_regget(m->roff, REG_nS))
m->flags |= MDOC_SYNOPSIS;
else
m->flags &= ~MDOC_SYNOPSIS;
}
return(mandoc_getcontrol(buf, &offs) ?
mdoc_pmacro(m, ln, buf, offs) :
mdoc_ptext(m, ln, buf, offs));
}
int
mdoc_macro(MACRO_PROT_ARGS)
{
assert(tok < MDOC_MAX);
/* If we're in the body, deny prologue calls. */
if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
MDOC_PBODY & m->flags) {
mdoc_pmsg(m, line, ppos, MANDOCERR_BADBODY);
return(1);
}
/* If we're in the prologue, deny "body" macros. */
if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
! (MDOC_PBODY & m->flags)) {
mdoc_pmsg(m, line, ppos, MANDOCERR_BADPROLOG);
if (NULL == m->meta.msec)
m->meta.msec = mandoc_strdup("1");
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 (NULL == m->meta.date)
m->meta.date = mandoc_normdate
(m->parse, NULL, line, ppos);
m->flags |= MDOC_PBODY;
}
return((*mdoc_macros[tok].fp)(m, tok, line, ppos, 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++;
/*
* Copy over the normalised-data pointer of our parent. Not
* everybody has one, but copying a null pointer is fine.
*/
switch (p->type) {
case (MDOC_BODY):
/* FALLTHROUGH */
case (MDOC_TAIL):
/* FALLTHROUGH */
case (MDOC_HEAD):
p->norm = p->parent->norm;
break;
default:
break;
}
if ( ! mdoc_valid_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):
if (p->end)
break;
assert(MDOC_BLOCK == p->parent->type);
p->parent->body = p;
break;
default:
break;
}
mdoc->last = p;
switch (p->type) {
case (MDOC_TBL):
/* FALLTHROUGH */
case (MDOC_TEXT):
if ( ! mdoc_valid_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;
/* Flag analysis. */
if (MDOC_SYNOPSIS & m->flags)
p->flags |= MDOC_SYNPRETTY;
else
p->flags &= ~MDOC_SYNPRETTY;
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_endbody_alloc(struct mdoc *m, int line, int pos, enum mdoct tok,
struct mdoc_node *body, enum mdoc_endbody end)
{
struct mdoc_node *p;
p = node_alloc(m, line, pos, tok, MDOC_BODY);
p->pending = body;
p->end = end;
if ( ! node_append(m, p))
return(0);
m->next = MDOC_NEXT_SIBLING;
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)++;
switch (tok) {
case (MDOC_Bd):
/* FALLTHROUGH */
case (MDOC_Bf):
/* FALLTHROUGH */
case (MDOC_Bl):
/* FALLTHROUGH */
case (MDOC_Rs):
p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
break;
default:
break;
}
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)++;
switch (tok) {
case (MDOC_An):
p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
break;
default:
break;
}
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;
n = node_alloc(m, line, pos, MDOC_MAX, MDOC_TEXT);
n->string = roff_strdup(m->roff, p);
if ( ! node_append(m, n))
return(0);
m->next = MDOC_NEXT_SIBLING;
return(1);
}
static void
mdoc_node_free(struct mdoc_node *p)
{
if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type)
free(p->norm);
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;
if (n->parent->last == n)
n->parent->last = n->prev ? n->prev : NULL;
}
/* 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);
}
#if 0
/*
* Pre-treat a text line.
* Text lines can consist of equations, which must be handled apart from
* the regular text.
* Thus, use this function to step through a line checking if it has any
* equations embedded in it.
* This must handle multiple equations AND equations that do not end at
* the end-of-line, i.e., will re-enter in the next roff parse.
*/
static int
mdoc_preptext(struct mdoc *m, int line, char *buf, int offs)
{
char *start, *end;
char delim;
while ('\0' != buf[offs]) {
/* Mark starting position if eqn is set. */
start = NULL;
if ('\0' != (delim = roff_eqndelim(m->roff)))
if (NULL != (start = strchr(buf + offs, delim)))
*start++ = '\0';
/* Parse text as normal. */
if ( ! mdoc_ptext(m, line, buf, offs))
return(0);
/* Continue only if an equation exists. */
if (NULL == start)
break;
/* Read past the end of the equation. */
offs += start - (buf + offs);
assert(start == &buf[offs]);
if (NULL != (end = strchr(buf + offs, delim))) {
*end++ = '\0';
while (' ' == *end)
end++;
}
/* Parse the equation itself. */
roff_openeqn(m->roff, NULL, line, offs, buf);
/* Process a finished equation? */
if (roff_closeeqn(m->roff))
if ( ! mdoc_addeqn(m, roff_eqn(m->roff)))
return(0);
offs += (end - (buf + offs));
}
return(1);
}
#endif
/*
* 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;
/* No text before an initial macro. */
if (SEC_NONE == m->lastnamed) {
mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT);
return(1);
}
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->norm->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->norm->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 (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)
mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE);
if ('\0' == buf[offs] && ! (MDOC_LITERAL & m->flags)) {
mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN);
/*
* Insert a `sp' 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_sp, 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), 0))
m->last->flags |= MDOC_EOS;
return(1);
}
/*
* 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, sv;
char mac[5];
struct mdoc_node *n;
/* Empty post-control lines are ignored. */
if ('"' == buf[offs]) {
mdoc_pmsg(m, ln, offs, MANDOCERR_BADCOMMENT);
return(1);
} else if ('\0' == buf[offs])
return(1);
sv = offs;
/*
* Copy the first word into a nil-terminated buffer.
* Stop copying when a tab, space, or eoln is encountered.
*/
i = 0;
while (i < 4 && '\0' != buf[offs] &&
' ' != buf[offs] && '\t' != buf[offs])
mac[i++] = buf[offs++];
mac[i] = '\0';
tok = (i > 1 || i < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
if (MDOC_MAX == tok) {
mandoc_vmsg(MANDOCERR_MACRO, m->parse,
ln, sv, "%s", buf + sv - 1);
return(1);
}
/* Disregard the first trailing tab, if applicable. */
if ('\t' == buf[offs])
offs++;
/* Jump to the next non-whitespace word. */
while (buf[offs] && ' ' == buf[offs])
offs++;
/*
* 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[offs] && ' ' == buf[offs - 1])
mdoc_pmsg(m, ln, offs - 1, MANDOCERR_EOLNSPACE);
/*
* 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, &offs, 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->norm->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->norm->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, &offs, buf))
goto err;
return(1);
err: /* Error out. */
m->flags |= MDOC_HALT;
return(0);
}
enum mdelim
mdoc_isdelim(const char *p)
{
if ('\0' == p[0])
return(DELIM_NONE);
if ('\0' == p[1])
switch (p[0]) {
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:
return(DELIM_NONE);
}
if ('\\' != p[0])
return(DELIM_NONE);
if (0 == strcmp(p + 1, "."))
return(DELIM_CLOSE);
if (0 == strcmp(p + 1, "*(Ba"))
return(DELIM_MIDDLE);
return(DELIM_NONE);
}

392
contrib/mdocml/mdoc.h Normal file
View File

@ -0,0 +1,392 @@
/* $Id: mdoc.h,v 1.122 2011/03/22 14:05:45 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 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
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
};
enum mdocargt {
MDOC_Split, /* -split */
MDOC_Nosplit, /* -nospli */
MDOC_Ragged, /* -ragged */
MDOC_Unfilled, /* -unfilled */
MDOC_Literal, /* -literal */
MDOC_File, /* -file */
MDOC_Offset, /* -offset */
MDOC_Bullet, /* -bullet */
MDOC_Dash, /* -dash */
MDOC_Hyphen, /* -hyphen */
MDOC_Item, /* -item */
MDOC_Enum, /* -enum */
MDOC_Tag, /* -tag */
MDOC_Diag, /* -diag */
MDOC_Hang, /* -hang */
MDOC_Ohang, /* -ohang */
MDOC_Inset, /* -inset */
MDOC_Column, /* -column */
MDOC_Width, /* -width */
MDOC_Compact, /* -compact */
MDOC_Std, /* -std */
MDOC_Filled, /* -filled */
MDOC_Words, /* -words */
MDOC_Emphasis, /* -emphasis */
MDOC_Symbolic, /* -symbolic */
MDOC_Nested, /* -nested */
MDOC_Centred, /* -centered */
MDOC_ARG_MAX
};
enum mdoc_type {
MDOC_TEXT,
MDOC_ELEM,
MDOC_HEAD,
MDOC_TAIL,
MDOC_BODY,
MDOC_BLOCK,
MDOC_TBL,
MDOC_EQN,
MDOC_ROOT
};
/*
* Section (named/unnamed) of `Sh'. Note that these appear in the
* conventional order imposed by mdoc.7. In the case of SEC_NONE, no
* section has been invoked (this shouldn't happen). SEC_CUSTOM refers
* to other sections.
*/
enum mdoc_sec {
SEC_NONE = 0,
SEC_NAME, /* NAME */
SEC_LIBRARY, /* LIBRARY */
SEC_SYNOPSIS, /* SYNOPSIS */
SEC_DESCRIPTION, /* DESCRIPTION */
SEC_IMPLEMENTATION, /* IMPLEMENTATION NOTES */
SEC_RETURN_VALUES, /* RETURN VALUES */
SEC_ENVIRONMENT, /* ENVIRONMENT */
SEC_FILES, /* FILES */
SEC_EXIT_STATUS, /* EXIT STATUS */
SEC_EXAMPLES, /* EXAMPLES */
SEC_DIAGNOSTICS, /* DIAGNOSTICS */
SEC_COMPATIBILITY, /* COMPATIBILITY */
SEC_ERRORS, /* ERRORS */
SEC_SEE_ALSO, /* SEE ALSO */
SEC_STANDARDS, /* STANDARDS */
SEC_HISTORY, /* HISTORY */
SEC_AUTHORS, /* AUTHORS */
SEC_CAVEATS, /* CAVEATS */
SEC_BUGS, /* BUGS */
SEC_SECURITY, /* SECURITY */
SEC_CUSTOM,
SEC__MAX
};
struct mdoc_meta {
char *msec; /* `Dt' section (1, 3p, etc.) */
char *vol; /* `Dt' volume (implied) */
char *arch; /* `Dt' arch (i386, etc.) */
char *date; /* `Dd' normalised date */
char *title; /* `Dt' title (FOO, etc.) */
char *os; /* `Os' system (OpenBSD, etc.) */
char *name; /* leading `Nm' name */
};
/*
* An argument to a macro (multiple values = `-column xxx yyy').
*/
struct mdoc_argv {
enum mdocargt arg; /* type of argument */
int line;
int pos;
size_t sz; /* elements in "value" */
char **value; /* argument strings */
};
/*
* Reference-counted macro arguments. These are refcounted because
* blocks have multiple instances of the same arguments spread across
* the HEAD, BODY, TAIL, and BLOCK node types.
*/
struct mdoc_arg {
size_t argc;
struct mdoc_argv *argv;
unsigned int refcnt;
};
/*
* Indicates that a BODY's formatting has ended, but the scope is still
* open. Used for syntax-broken blocks.
*/
enum mdoc_endbody {
ENDBODY_NOT = 0,
ENDBODY_SPACE, /* is broken: append a space */
ENDBODY_NOSPACE /* is broken: don't append a space */
};
enum mdoc_list {
LIST__NONE = 0,
LIST_bullet, /* -bullet */
LIST_column, /* -column */
LIST_dash, /* -dash */
LIST_diag, /* -diag */
LIST_enum, /* -enum */
LIST_hang, /* -hang */
LIST_hyphen, /* -hyphen */
LIST_inset, /* -inset */
LIST_item, /* -item */
LIST_ohang, /* -ohang */
LIST_tag, /* -tag */
LIST_MAX
};
enum mdoc_disp {
DISP__NONE = 0,
DISP_centred, /* -centered */
DISP_ragged, /* -ragged */
DISP_unfilled, /* -unfilled */
DISP_filled, /* -filled */
DISP_literal /* -literal */
};
enum mdoc_auth {
AUTH__NONE = 0,
AUTH_split, /* -split */
AUTH_nosplit /* -nosplit */
};
enum mdoc_font {
FONT__NONE = 0,
FONT_Em, /* Em, -emphasis */
FONT_Li, /* Li, -literal */
FONT_Sy /* Sy, -symbolic */
};
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 */
size_t ncols; /* -column arg count */
const char **cols; /* -column val ptr */
};
struct mdoc_bf {
enum mdoc_font font; /* font */
};
struct mdoc_an {
enum mdoc_auth auth; /* -split, etc. */
};
struct mdoc_rs {
int quote_T; /* whether to quote %T */
};
/*
* Consists of normalised node arguments. These should be used instead
* of iterating through the mdoc_arg pointers of a node: defaults are
* provided, etc.
*/
union mdoc_data {
struct mdoc_an An;
struct mdoc_bd Bd;
struct mdoc_bf Bf;
struct mdoc_bl Bl;
struct mdoc_rs Rs;
};
/*
* Single node in tree-linked AST.
*/
struct mdoc_node {
struct mdoc_node *parent; /* parent AST node */
struct mdoc_node *child; /* first child AST node */
struct mdoc_node *last; /* last 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_EOS (1 << 2) /* at sentence boundary */
#define MDOC_LINE (1 << 3) /* first macro/text on line */
#define MDOC_SYNPRETTY (1 << 4) /* SYNOPSIS-style formatting */
#define MDOC_ENDED (1 << 5) /* rendering has been ended */
#define MDOC_DELIMO (1 << 6)
#define MDOC_DELIMC (1 << 7)
enum mdoc_type type; /* AST node type */
enum mdoc_sec sec; /* current named section */
union mdoc_data *norm; /* normalised args */
/* FIXME: these can be union'd to shave a few bytes. */
struct mdoc_arg *args; /* BLOCK/ELEM */
struct mdoc_node *pending; /* BLOCK */
struct mdoc_node *head; /* BLOCK */
struct mdoc_node *body; /* BLOCK */
struct mdoc_node *tail; /* BLOCK */
char *string; /* TEXT */
const struct tbl_span *span; /* TBL */
const struct eqn *eqn; /* EQN */
enum mdoc_endbody end; /* BODY */
};
/* Names of macros. Index is enum mdoct. */
extern const char *const *mdoc_macronames;
/* Names of macro args. Index is enum mdocargt. */
extern const char *const *mdoc_argnames;
__BEGIN_DECLS
struct mdoc;
const struct mdoc_node *mdoc_node(const struct mdoc *);
const struct mdoc_meta *mdoc_meta(const struct mdoc *);
__END_DECLS
#endif /*!MDOC_H*/

716
contrib/mdocml/mdoc_argv.c Normal file
View File

@ -0,0 +1,716 @@
/* $Id: mdoc_argv.c,v 1.82 2012/03/23 05:50:24 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 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 "mdoc.h"
#include "mandoc.h"
#include "libmdoc.h"
#include "libmandoc.h"
#define MULTI_STEP 5 /* pre-allocate argument values */
#define DELIMSZ 6 /* max possible size of a delimiter */
enum argsflag {
ARGSFL_NONE = 0,
ARGSFL_DELIM, /* handle delimiters of [[::delim::][ ]+]+ */
ARGSFL_TABSEP /* handle tab/`Ta' separated phrases */
};
enum argvflag {
ARGV_NONE, /* no args to flag (e.g., -split) */
ARGV_SINGLE, /* one arg to flag (e.g., -file xxx) */
ARGV_MULTI, /* multiple args (e.g., -column xxx yyy) */
ARGV_OPT_SINGLE /* optional arg (e.g., -offset [xxx]) */
};
struct mdocarg {
enum argsflag flags;
const enum mdocargt *argvs;
};
static void argn_free(struct mdoc_arg *, int);
static enum margserr args(struct mdoc *, int, int *,
char *, enum argsflag, char **);
static int args_checkpunct(const char *, int);
static int argv_multi(struct mdoc *, int,
struct mdoc_argv *, int *, char *);
static int argv_opt_single(struct mdoc *, int,
struct mdoc_argv *, int *, char *);
static int argv_single(struct mdoc *, int,
struct mdoc_argv *, int *, char *);
static const enum argvflag 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_OPT_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 const enum mdocargt args_Ex[] = {
MDOC_Std,
MDOC_ARG_MAX
};
static const enum mdocargt args_An[] = {
MDOC_Split,
MDOC_Nosplit,
MDOC_ARG_MAX
};
static const enum mdocargt args_Bd[] = {
MDOC_Ragged,
MDOC_Unfilled,
MDOC_Filled,
MDOC_Literal,
MDOC_File,
MDOC_Offset,
MDOC_Compact,
MDOC_Centred,
MDOC_ARG_MAX
};
static const enum mdocargt args_Bf[] = {
MDOC_Emphasis,
MDOC_Literal,
MDOC_Symbolic,
MDOC_ARG_MAX
};
static const enum mdocargt args_Bk[] = {
MDOC_Words,
MDOC_ARG_MAX
};
static const enum mdocargt args_Bl[] = {
MDOC_Bullet,
MDOC_Dash,
MDOC_Hyphen,
MDOC_Item,
MDOC_Enum,
MDOC_Tag,
MDOC_Diag,
MDOC_Hang,
MDOC_Ohang,
MDOC_Inset,
MDOC_Column,
MDOC_Width,
MDOC_Offset,
MDOC_Compact,
MDOC_Nested,
MDOC_ARG_MAX
};
static const struct mdocarg mdocargs[MDOC_MAX] = {
{ ARGSFL_NONE, NULL }, /* Ap */
{ ARGSFL_NONE, NULL }, /* Dd */
{ ARGSFL_NONE, NULL }, /* Dt */
{ ARGSFL_NONE, NULL }, /* Os */
{ ARGSFL_NONE, NULL }, /* Sh */
{ ARGSFL_NONE, NULL }, /* Ss */
{ ARGSFL_NONE, NULL }, /* Pp */
{ ARGSFL_DELIM, NULL }, /* D1 */
{ ARGSFL_DELIM, NULL }, /* Dl */
{ ARGSFL_NONE, args_Bd }, /* Bd */
{ ARGSFL_NONE, NULL }, /* Ed */
{ ARGSFL_NONE, args_Bl }, /* Bl */
{ ARGSFL_NONE, NULL }, /* El */
{ ARGSFL_NONE, NULL }, /* It */
{ ARGSFL_DELIM, NULL }, /* Ad */
{ ARGSFL_DELIM, args_An }, /* An */
{ ARGSFL_DELIM, NULL }, /* Ar */
{ ARGSFL_NONE, NULL }, /* Cd */
{ ARGSFL_DELIM, NULL }, /* Cm */
{ ARGSFL_DELIM, NULL }, /* Dv */
{ ARGSFL_DELIM, NULL }, /* Er */
{ ARGSFL_DELIM, NULL }, /* Ev */
{ ARGSFL_NONE, args_Ex }, /* Ex */
{ ARGSFL_DELIM, NULL }, /* Fa */
{ ARGSFL_NONE, NULL }, /* Fd */
{ ARGSFL_DELIM, NULL }, /* Fl */
{ ARGSFL_DELIM, NULL }, /* Fn */
{ ARGSFL_DELIM, NULL }, /* Ft */
{ ARGSFL_DELIM, NULL }, /* Ic */
{ ARGSFL_NONE, NULL }, /* In */
{ ARGSFL_DELIM, NULL }, /* Li */
{ ARGSFL_NONE, NULL }, /* Nd */
{ ARGSFL_DELIM, NULL }, /* Nm */
{ ARGSFL_DELIM, NULL }, /* Op */
{ ARGSFL_NONE, NULL }, /* Ot */
{ ARGSFL_DELIM, NULL }, /* Pa */
{ ARGSFL_NONE, args_Ex }, /* Rv */
{ ARGSFL_DELIM, NULL }, /* St */
{ ARGSFL_DELIM, NULL }, /* Va */
{ ARGSFL_DELIM, NULL }, /* Vt */
{ ARGSFL_DELIM, NULL }, /* Xr */
{ ARGSFL_NONE, NULL }, /* %A */
{ ARGSFL_NONE, NULL }, /* %B */
{ ARGSFL_NONE, NULL }, /* %D */
{ ARGSFL_NONE, NULL }, /* %I */
{ ARGSFL_NONE, NULL }, /* %J */
{ ARGSFL_NONE, NULL }, /* %N */
{ ARGSFL_NONE, NULL }, /* %O */
{ ARGSFL_NONE, NULL }, /* %P */
{ ARGSFL_NONE, NULL }, /* %R */
{ ARGSFL_NONE, NULL }, /* %T */
{ ARGSFL_NONE, NULL }, /* %V */
{ ARGSFL_DELIM, NULL }, /* Ac */
{ ARGSFL_NONE, NULL }, /* Ao */
{ ARGSFL_DELIM, NULL }, /* Aq */
{ ARGSFL_DELIM, NULL }, /* At */
{ ARGSFL_DELIM, NULL }, /* Bc */
{ ARGSFL_NONE, args_Bf }, /* Bf */
{ ARGSFL_NONE, NULL }, /* Bo */
{ ARGSFL_DELIM, NULL }, /* Bq */
{ ARGSFL_DELIM, NULL }, /* Bsx */
{ ARGSFL_DELIM, NULL }, /* Bx */
{ ARGSFL_NONE, NULL }, /* Db */
{ ARGSFL_DELIM, NULL }, /* Dc */
{ ARGSFL_NONE, NULL }, /* Do */
{ ARGSFL_DELIM, NULL }, /* Dq */
{ ARGSFL_DELIM, NULL }, /* Ec */
{ ARGSFL_NONE, NULL }, /* Ef */
{ ARGSFL_DELIM, NULL }, /* Em */
{ ARGSFL_NONE, NULL }, /* Eo */
{ ARGSFL_DELIM, NULL }, /* Fx */
{ ARGSFL_DELIM, NULL }, /* Ms */
{ ARGSFL_DELIM, NULL }, /* No */
{ ARGSFL_DELIM, NULL }, /* Ns */
{ ARGSFL_DELIM, NULL }, /* Nx */
{ ARGSFL_DELIM, NULL }, /* Ox */
{ ARGSFL_DELIM, NULL }, /* Pc */
{ ARGSFL_DELIM, NULL }, /* Pf */
{ ARGSFL_NONE, NULL }, /* Po */
{ ARGSFL_DELIM, NULL }, /* Pq */
{ ARGSFL_DELIM, NULL }, /* Qc */
{ ARGSFL_DELIM, NULL }, /* Ql */
{ ARGSFL_NONE, NULL }, /* Qo */
{ ARGSFL_DELIM, NULL }, /* Qq */
{ ARGSFL_NONE, NULL }, /* Re */
{ ARGSFL_NONE, NULL }, /* Rs */
{ ARGSFL_DELIM, NULL }, /* Sc */
{ ARGSFL_NONE, NULL }, /* So */
{ ARGSFL_DELIM, NULL }, /* Sq */
{ ARGSFL_NONE, NULL }, /* Sm */
{ ARGSFL_DELIM, NULL }, /* Sx */
{ ARGSFL_DELIM, NULL }, /* Sy */
{ ARGSFL_DELIM, NULL }, /* Tn */
{ ARGSFL_DELIM, NULL }, /* Ux */
{ ARGSFL_DELIM, NULL }, /* Xc */
{ ARGSFL_NONE, NULL }, /* Xo */
{ ARGSFL_NONE, NULL }, /* Fo */
{ ARGSFL_NONE, NULL }, /* Fc */
{ ARGSFL_NONE, NULL }, /* Oo */
{ ARGSFL_DELIM, NULL }, /* Oc */
{ ARGSFL_NONE, args_Bk }, /* Bk */
{ ARGSFL_NONE, NULL }, /* Ek */
{ ARGSFL_NONE, NULL }, /* Bt */
{ ARGSFL_NONE, NULL }, /* Hf */
{ ARGSFL_NONE, NULL }, /* Fr */
{ ARGSFL_NONE, NULL }, /* Ud */
{ ARGSFL_NONE, NULL }, /* Lb */
{ ARGSFL_NONE, NULL }, /* Lp */
{ ARGSFL_DELIM, NULL }, /* Lk */
{ ARGSFL_DELIM, NULL }, /* Mt */
{ ARGSFL_DELIM, NULL }, /* Brq */
{ ARGSFL_NONE, NULL }, /* Bro */
{ ARGSFL_DELIM, NULL }, /* Brc */
{ ARGSFL_NONE, NULL }, /* %C */
{ ARGSFL_NONE, NULL }, /* Es */
{ ARGSFL_NONE, NULL }, /* En */
{ ARGSFL_NONE, NULL }, /* Dx */
{ ARGSFL_NONE, NULL }, /* %Q */
{ ARGSFL_NONE, NULL }, /* br */
{ ARGSFL_NONE, NULL }, /* sp */
{ ARGSFL_NONE, NULL }, /* %U */
{ ARGSFL_NONE, NULL }, /* 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;
const enum mdocargt *ap;
if ('\0' == buf[*pos])
return(ARGV_EOLN);
else if (NULL == (ap = mdocargs[tok].argvs))
return(ARGV_WORD);
else if ('-' != buf[*pos])
return(ARGV_WORD);
/* Seek to the first unescaped space. */
p = &buf[++(*pos)];
assert(*pos > 0);
for ( ; buf[*pos] ; (*pos)++)
if (' ' == buf[*pos] && '\\' != buf[*pos - 1])
break;
/*
* We want to nil-terminate the word to look it up (it's easier
* that way). But we may not have a flag, in which case we need
* to restore the line as-is. So keep around the stray byte,
* which we'll reset upon exiting (if necessary).
*/
if ('\0' != (sv = buf[*pos]))
buf[(*pos)++] = '\0';
/*
* Now look up the word as a flag. Use temporary storage that
* we'll copy into the node's flags, if necessary.
*/
memset(&tmp, 0, sizeof(struct mdoc_argv));
tmp.line = line;
tmp.pos = *pos;
tmp.arg = MDOC_ARG_MAX;
while (MDOC_ARG_MAX != (tmp.arg = *ap++))
if (0 == strcmp(p, mdoc_argnames[tmp.arg]))
break;
if (MDOC_ARG_MAX == tmp.arg) {
/*
* The flag was not found.
* Restore saved zeroed byte and return as a word.
*/
if (sv)
buf[*pos - 1] = sv;
return(ARGV_WORD);
}
/* Read to the next word (the argument). */
while (buf[*pos] && ' ' == buf[*pos])
(*pos)++;
switch (argvflags[tmp.arg]) {
case (ARGV_SINGLE):
if ( ! argv_single(m, line, &tmp, pos, buf))
return(ARGV_ERROR);
break;
case (ARGV_MULTI):
if ( ! argv_multi(m, line, &tmp, pos, buf))
return(ARGV_ERROR);
break;
case (ARGV_OPT_SINGLE):
if ( ! argv_opt_single(m, line, &tmp, pos, buf))
return(ARGV_ERROR);
break;
case (ARGV_NONE):
break;
}
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));
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--)
argn_free(p, i);
free(p->argv);
free(p);
}
static void
argn_free(struct mdoc_arg *p, int iarg)
{
struct mdoc_argv *arg;
int j;
arg = &p->argv[iarg];
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, char **v)
{
return(args(m, line, pos, buf, ARGSFL_NONE, v));
}
enum margserr
mdoc_args(struct mdoc *m, int line, int *pos,
char *buf, enum mdoct tok, char **v)
{
enum argsflag fl;
struct mdoc_node *n;
fl = mdocargs[tok].flags;
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)
if (LIST_column == n->norm->Bl.type) {
fl = ARGSFL_TABSEP;
break;
}
return(args(m, line, pos, buf, fl, v));
}
static enum margserr
args(struct mdoc *m, int line, int *pos,
char *buf, enum argsflag fl, char **v)
{
char *p, *pp;
enum margserr rc;
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)
mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE);
m->flags &= ~MDOC_PHRASELIT;
return(ARGS_EOLN);
}
*v = &buf[*pos];
if (ARGSFL_DELIM == fl)
if (args_checkpunct(buf, *pos))
return(ARGS_PUNCT);
/*
* 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 (ARGSFL_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))
mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE);
*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 (MDOC_PPHRASE & m->flags)
return(ARGS_QWORD);
mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE);
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])
mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE);
return(ARGS_QWORD);
}
p = &buf[*pos];
*v = mandoc_getarg(m->parse, &p, line, pos);
return(ARGS_WORD);
}
/*
* Check if the string consists only of space-separated closing
* delimiters. This is a bit of a dance: the first must be a close
* delimiter, but it may be followed by middle delimiters. Arbitrary
* whitespace may separate these tokens.
*/
static int
args_checkpunct(const char *buf, int i)
{
int j;
char dbuf[DELIMSZ];
enum mdelim d;
/* First token must be a close-delimiter. */
for (j = 0; buf[i] && ' ' != buf[i] && j < DELIMSZ; j++, i++)
dbuf[j] = buf[i];
if (DELIMSZ == j)
return(0);
dbuf[j] = '\0';
if (DELIM_CLOSE != mdoc_isdelim(dbuf))
return(0);
while (' ' == buf[i])
i++;
/* Remaining must NOT be open/none. */
while (buf[i]) {
j = 0;
while (buf[i] && ' ' != buf[i] && j < DELIMSZ)
dbuf[j++] = buf[i++];
if (DELIMSZ == j)
return(0);
dbuf[j] = '\0';
d = mdoc_isdelim(dbuf);
if (DELIM_NONE == d || DELIM_OPEN == d)
return(0);
while (' ' == buf[i])
i++;
}
return('\0' == buf[i]);
}
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, ARGSFL_NONE, &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, ARGSFL_NONE, &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);
}
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, ARGSFL_NONE, &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);
}

View File

@ -0,0 +1,94 @@
/* $Id: mdoc_hash.c,v 1.18 2011/07/24 18:15:14 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 "mdoc.h"
#include "mandoc.h"
#include "libmdoc.h"
static unsigned 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((unsigned char)p[1]))
major = 12 * (tolower((unsigned char)p[1]) - 97);
else
major = 12 * 26;
for (j = 0; j < 12; j++)
if (UCHAR_MAX == table[major + j]) {
table[major + j] = (unsigned 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((unsigned char)p[0]) && '%' != p[0])
return(MDOC_MAX);
if (isalpha((unsigned char)p[1]))
major = 12 * (tolower((unsigned 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);
}

2284
contrib/mdocml/mdoc_html.c Normal file

File diff suppressed because it is too large Load Diff

1787
contrib/mdocml/mdoc_macro.c Normal file

File diff suppressed because it is too large Load Diff

637
contrib/mdocml/mdoc_man.c Normal file
View File

@ -0,0 +1,637 @@
/* $Id: mdoc_man.c,v 1.9 2011/10/24 21:47:59 schwarze Exp $ */
/*
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 <stdio.h>
#include <string.h>
#include "mandoc.h"
#include "man.h"
#include "mdoc.h"
#include "main.h"
#define DECL_ARGS const struct mdoc_meta *m, \
const struct mdoc_node *n, \
struct mman *mm
struct mman {
int need_space; /* next word needs prior ws */
int need_nl; /* next word needs prior nl */
};
struct manact {
int (*cond)(DECL_ARGS); /* DON'T run actions */
int (*pre)(DECL_ARGS); /* pre-node action */
void (*post)(DECL_ARGS); /* post-node action */
const char *prefix; /* pre-node string constant */
const char *suffix; /* post-node string constant */
};
static int cond_body(DECL_ARGS);
static int cond_head(DECL_ARGS);
static void post_bd(DECL_ARGS);
static void post_dl(DECL_ARGS);
static void post_enc(DECL_ARGS);
static void post_nm(DECL_ARGS);
static void post_percent(DECL_ARGS);
static void post_pf(DECL_ARGS);
static void post_sect(DECL_ARGS);
static void post_sp(DECL_ARGS);
static int pre_ap(DECL_ARGS);
static int pre_bd(DECL_ARGS);
static int pre_br(DECL_ARGS);
static int pre_bx(DECL_ARGS);
static int pre_dl(DECL_ARGS);
static int pre_enc(DECL_ARGS);
static int pre_it(DECL_ARGS);
static int pre_nm(DECL_ARGS);
static int pre_ns(DECL_ARGS);
static int pre_pp(DECL_ARGS);
static int pre_sp(DECL_ARGS);
static int pre_sect(DECL_ARGS);
static int pre_ux(DECL_ARGS);
static int pre_xr(DECL_ARGS);
static void print_word(struct mman *, const char *);
static void print_node(DECL_ARGS);
static const struct manact manacts[MDOC_MAX + 1] = {
{ NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
{ NULL, NULL, NULL, NULL, NULL }, /* Os */
{ NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
{ NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
{ NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
{ cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
{ cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
{ cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
{ NULL, NULL, NULL, NULL, NULL }, /* Bl */
{ NULL, NULL, NULL, NULL, NULL }, /* El */
{ NULL, pre_it, NULL, NULL, NULL }, /* _It */
{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Ad */
{ NULL, NULL, NULL, NULL, NULL }, /* _An */
{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Ar */
{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Cd */
{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Cm */
{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Dv */
{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Er */
{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Ev */
{ NULL, pre_enc, post_enc, "The \\fB",
"\\fP\nutility exits 0 on success, and >0 if an error occurs."
}, /* Ex */
{ NULL, NULL, NULL, NULL, NULL }, /* _Fa */
{ NULL, NULL, NULL, NULL, NULL }, /* _Fd */
{ NULL, pre_enc, post_enc, "\\fB-", "\\fP" }, /* Fl */
{ NULL, NULL, NULL, NULL, NULL }, /* _Fn */
{ NULL, NULL, NULL, NULL, NULL }, /* _Ft */
{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Ic */
{ NULL, NULL, NULL, NULL, NULL }, /* _In */
{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Li */
{ cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
{ NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
{ NULL, NULL, NULL, NULL, NULL }, /* Ot */
{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Pa */
{ NULL, pre_enc, post_enc, "The \\fB",
"\\fP\nfunction returns the value 0 if successful;\n"
"otherwise the value -1 is returned and the global\n"
"variable \\fIerrno\\fP is set to indicate the error."
}, /* Rv */
{ NULL, NULL, NULL, NULL, NULL }, /* St */
{ NULL, NULL, NULL, NULL, NULL }, /* _Va */
{ NULL, NULL, NULL, NULL, NULL }, /* _Vt */
{ NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
{ NULL, NULL, post_percent, NULL, NULL }, /* _%A */
{ NULL, NULL, NULL, NULL, NULL }, /* _%B */
{ NULL, NULL, post_percent, NULL, NULL }, /* _%D */
{ NULL, NULL, NULL, NULL, NULL }, /* _%I */
{ NULL, pre_enc, post_percent, "\\fI", "\\fP" }, /* %J */
{ NULL, NULL, NULL, NULL, NULL }, /* _%N */
{ NULL, NULL, NULL, NULL, NULL }, /* _%O */
{ NULL, NULL, NULL, NULL, NULL }, /* _%P */
{ NULL, NULL, NULL, NULL, NULL }, /* _%R */
{ NULL, pre_enc, post_percent, "\"", "\"" }, /* %T */
{ NULL, NULL, NULL, NULL, NULL }, /* _%V */
{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
{ cond_body, pre_enc, post_enc, "<", ">" }, /* Ao */
{ cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */
{ NULL, NULL, NULL, NULL, NULL }, /* At */
{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
{ NULL, NULL, NULL, NULL, NULL }, /* _Bf */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
{ NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
{ NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
{ NULL, NULL, NULL, NULL, NULL }, /* Db */
{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
{ cond_body, pre_enc, post_enc, "``", "''" }, /* Do */
{ cond_body, pre_enc, post_enc, "``", "''" }, /* Dq */
{ NULL, NULL, NULL, NULL, NULL }, /* _Ec */
{ NULL, NULL, NULL, NULL, NULL }, /* _Ef */
{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Em */
{ NULL, NULL, NULL, NULL, NULL }, /* _Eo */
{ NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Ms */
{ NULL, NULL, NULL, NULL, NULL }, /* No */
{ NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
{ NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */
{ NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */
{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
{ NULL, NULL, post_pf, NULL, NULL }, /* Pf */
{ cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
{ cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
{ cond_body, pre_enc, post_enc, "`", "'" }, /* Ql */
{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
{ NULL, NULL, NULL, NULL, NULL }, /* Re */
{ cond_body, pre_pp, NULL, NULL, NULL }, /* Rs */
{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
{ cond_body, pre_enc, post_enc, "`", "'" }, /* So */
{ cond_body, pre_enc, post_enc, "`", "'" }, /* Sq */
{ NULL, NULL, NULL, NULL, NULL }, /* _Sm */
{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Sx */
{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Sy */
{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Tn */
{ NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
{ NULL, NULL, NULL, NULL, NULL }, /* _Xc */
{ NULL, NULL, NULL, NULL, NULL }, /* _Xo */
{ NULL, NULL, NULL, NULL, NULL }, /* _Fo */
{ NULL, NULL, NULL, NULL, NULL }, /* _Fc */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
{ NULL, NULL, NULL, NULL, NULL }, /* _Bk */
{ NULL, NULL, NULL, NULL, NULL }, /* _Ek */
{ NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
{ NULL, NULL, NULL, NULL, NULL }, /* Fr */
{ NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
{ NULL, NULL, NULL, NULL, NULL }, /* _Lb */
{ NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
{ NULL, NULL, NULL, NULL, NULL }, /* _Lk */
{ NULL, NULL, NULL, NULL, NULL }, /* _Mt */
{ cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
{ cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
{ NULL, NULL, NULL, NULL, NULL }, /* _%C */
{ NULL, NULL, NULL, NULL, NULL }, /* _Es */
{ NULL, NULL, NULL, NULL, NULL }, /* _En */
{ NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
{ NULL, NULL, NULL, NULL, NULL }, /* _%Q */
{ NULL, pre_br, NULL, NULL, NULL }, /* br */
{ NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
{ NULL, NULL, NULL, NULL, NULL }, /* _%U */
{ NULL, NULL, NULL, NULL, NULL }, /* _Ta */
{ NULL, NULL, NULL, NULL, NULL }, /* ROOT */
};
static void
print_word(struct mman *mm, const char *s)
{
if (mm->need_nl) {
/*
* If we need a newline, print it now and start afresh.
*/
putchar('\n');
mm->need_space = 0;
mm->need_nl = 0;
} else if (mm->need_space && '\0' != s[0])
/*
* If we need a space, only print it before
* (1) a nonzero length word;
* (2) a word that is non-punctuation; and
* (3) if punctuation, non-terminating puncutation.
*/
if (NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1])
putchar(' ');
/*
* Reassign needing space if we're not following opening
* punctuation.
*/
mm->need_space =
('(' != s[0] && '[' != s[0]) || '\0' != s[1];
for ( ; *s; s++) {
switch (*s) {
case (ASCII_NBRSP):
printf("\\~");
break;
case (ASCII_HYPH):
putchar('-');
break;
default:
putchar((unsigned char)*s);
break;
}
}
}
void
man_man(void *arg, const struct man *man)
{
/*
* Dump the keep buffer.
* We're guaranteed by now that this exists (is non-NULL).
* Flush stdout afterward, just in case.
*/
fputs(mparse_getkeep(man_mparse(man)), stdout);
fflush(stdout);
}
void
man_mdoc(void *arg, const struct mdoc *mdoc)
{
const struct mdoc_meta *m;
const struct mdoc_node *n;
struct mman mm;
m = mdoc_meta(mdoc);
n = mdoc_node(mdoc);
printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
m->title, m->msec, m->date, m->os, m->vol);
memset(&mm, 0, sizeof(struct mman));
mm.need_nl = 1;
print_node(m, n, &mm);
putchar('\n');
}
static void
print_node(DECL_ARGS)
{
const struct mdoc_node *prev, *sub;
const struct manact *act;
int cond, do_sub;
/*
* Break the line if we were parsed subsequent the current node.
* This makes the page structure be more consistent.
*/
prev = n->prev ? n->prev : n->parent;
if (prev && prev->line < n->line)
mm->need_nl = 1;
act = NULL;
cond = 0;
do_sub = 1;
if (MDOC_TEXT == n->type) {
/*
* Make sure that we don't happen to start with a
* control character at the start of a line.
*/
if (mm->need_nl && ('.' == *n->string ||
'\'' == *n->string)) {
print_word(mm, "\\&");
mm->need_space = 0;
}
print_word(mm, n->string);
} else {
/*
* Conditionally run the pre-node action handler for a
* node.
*/
act = manacts + n->tok;
cond = NULL == act->cond || (*act->cond)(m, n, mm);
if (cond && act->pre)
do_sub = (*act->pre)(m, n, mm);
}
/*
* Conditionally run all child nodes.
* Note that this iterates over children instead of using
* recursion. This prevents unnecessary depth in the stack.
*/
if (do_sub)
for (sub = n->child; sub; sub = sub->next)
print_node(m, sub, mm);
/*
* Lastly, conditionally run the post-node handler.
*/
if (cond && act->post)
(*act->post)(m, n, mm);
}
static int
cond_head(DECL_ARGS)
{
return(MDOC_HEAD == n->type);
}
static int
cond_body(DECL_ARGS)
{
return(MDOC_BODY == n->type);
}
/*
* Output a font encoding before a node, e.g., \fR.
* This obviously has no trailing space.
*/
static int
pre_enc(DECL_ARGS)
{
const char *prefix;
prefix = manacts[n->tok].prefix;
if (NULL == prefix)
return(1);
print_word(mm, prefix);
mm->need_space = 0;
return(1);
}
/*
* Output a font encoding subsequent a node, e.g., \fP.
*/
static void
post_enc(DECL_ARGS)
{
const char *suffix;
suffix = manacts[n->tok].suffix;
if (NULL == suffix)
return;
mm->need_space = 0;
print_word(mm, suffix);
}
/*
* Used in listings (percent = %A, e.g.).
* FIXME: this is incomplete.
* It doesn't print a nice ", and" for lists.
*/
static void
post_percent(DECL_ARGS)
{
post_enc(m, n, mm);
if (n->next)
print_word(mm, ",");
else {
print_word(mm, ".");
mm->need_nl = 1;
}
}
/*
* Print before a section header.
*/
static int
pre_sect(DECL_ARGS)
{
if (MDOC_HEAD != n->type)
return(1);
mm->need_nl = 1;
print_word(mm, manacts[n->tok].prefix);
print_word(mm, "\"");
mm->need_space = 0;
return(1);
}
/*
* Print subsequent a section header.
*/
static void
post_sect(DECL_ARGS)
{
if (MDOC_HEAD != n->type)
return;
mm->need_space = 0;
print_word(mm, "\"");
mm->need_nl = 1;
}
static int
pre_ap(DECL_ARGS)
{
mm->need_space = 0;
print_word(mm, "'");
mm->need_space = 0;
return(0);
}
static int
pre_bd(DECL_ARGS)
{
if (DISP_unfilled == n->norm->Bd.type ||
DISP_literal == n->norm->Bd.type) {
mm->need_nl = 1;
print_word(mm, ".nf");
}
mm->need_nl = 1;
return(1);
}
static void
post_bd(DECL_ARGS)
{
if (DISP_unfilled == n->norm->Bd.type ||
DISP_literal == n->norm->Bd.type) {
mm->need_nl = 1;
print_word(mm, ".fi");
}
mm->need_nl = 1;
}
static int
pre_br(DECL_ARGS)
{
mm->need_nl = 1;
print_word(mm, ".br");
mm->need_nl = 1;
return(0);
}
static int
pre_bx(DECL_ARGS)
{
n = n->child;
if (n) {
print_word(mm, n->string);
mm->need_space = 0;
n = n->next;
}
print_word(mm, "BSD");
if (NULL == n)
return(0);
mm->need_space = 0;
print_word(mm, "-");
mm->need_space = 0;
print_word(mm, n->string);
return(0);
}
static int
pre_dl(DECL_ARGS)
{
mm->need_nl = 1;
print_word(mm, ".RS 6n");
mm->need_nl = 1;
return(1);
}
static void
post_dl(DECL_ARGS)
{
mm->need_nl = 1;
print_word(mm, ".RE");
mm->need_nl = 1;
}
static int
pre_it(DECL_ARGS)
{
const struct mdoc_node *bln;
if (MDOC_HEAD == n->type) {
mm->need_nl = 1;
print_word(mm, ".TP");
bln = n->parent->parent->prev;
switch (bln->norm->Bl.type) {
case (LIST_bullet):
print_word(mm, "4n");
mm->need_nl = 1;
print_word(mm, "\\fBo\\fP");
break;
default:
if (bln->norm->Bl.width)
print_word(mm, bln->norm->Bl.width);
break;
}
mm->need_nl = 1;
}
return(1);
}
static int
pre_nm(DECL_ARGS)
{
if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
return(1);
print_word(mm, "\\fB");
mm->need_space = 0;
if (NULL == n->child)
print_word(mm, m->name);
return(1);
}
static void
post_nm(DECL_ARGS)
{
if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
return;
mm->need_space = 0;
print_word(mm, "\\fP");
}
static int
pre_ns(DECL_ARGS)
{
mm->need_space = 0;
return(0);
}
static void
post_pf(DECL_ARGS)
{
mm->need_space = 0;
}
static int
pre_pp(DECL_ARGS)
{
mm->need_nl = 1;
if (MDOC_It == n->parent->tok)
print_word(mm, ".sp");
else
print_word(mm, ".PP");
mm->need_nl = 1;
return(1);
}
static int
pre_sp(DECL_ARGS)
{
mm->need_nl = 1;
print_word(mm, ".sp");
return(1);
}
static void
post_sp(DECL_ARGS)
{
mm->need_nl = 1;
}
static int
pre_xr(DECL_ARGS)
{
n = n->child;
if (NULL == n)
return(0);
print_node(m, n, mm);
n = n->next;
if (NULL == n)
return(0);
mm->need_space = 0;
print_word(mm, "(");
print_node(m, n, mm);
print_word(mm, ")");
return(0);
}
static int
pre_ux(DECL_ARGS)
{
print_word(mm, manacts[n->tok].prefix);
if (NULL == n->child)
return(0);
mm->need_space = 0;
print_word(mm, "\\~");
mm->need_space = 0;
return(1);
}

2257
contrib/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
contrib/mdocml/msec.c Normal file
View File

@ -0,0 +1,37 @@
/* $Id: msec.c,v 1.10 2011/12/02 01:37:14 schwarze 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 "libmandoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mandoc_a2msec(const char *p)
{
#include "msec.in"
return(NULL);
}

40
contrib/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")

303
contrib/mdocml/out.c Normal file
View File

@ -0,0 +1,303 @@
/* $Id: out.c,v 1.43 2011/09/20 23:05:49 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 "mandoc.h"
#include "out.h"
static void tblcalc_data(struct rofftbl *, struct roffcol *,
const struct tbl *, const struct tbl_dat *);
static void tblcalc_literal(struct rofftbl *, struct roffcol *,
const struct tbl_dat *);
static void tblcalc_number(struct rofftbl *, struct roffcol *,
const struct tbl *, const struct tbl_dat *);
/*
* 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((unsigned 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);
}
/* FIXME: do this in the caller. */
if ((dst->scale = atof(buf)) < 0)
dst->scale = 0;
dst->unit = unit;
return(1);
}
/*
* Calculate the abstract widths and decimal positions of columns in a
* table. This routine allocates the columns structures then runs over
* all rows and cells in the table. The function pointers in "tbl" are
* used for the actual width calculations.
*/
void
tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
{
const struct tbl_dat *dp;
const struct tbl_head *hp;
struct roffcol *col;
int spans;
/*
* Allocate the master column specifiers. These will hold the
* widths and decimal positions for all cells in the column. It
* must be freed and nullified by the caller.
*/
assert(NULL == tbl->cols);
tbl->cols = mandoc_calloc
((size_t)sp->tbl->cols, sizeof(struct roffcol));
hp = sp->head;
for ( ; sp; sp = sp->next) {
if (TBL_SPAN_DATA != sp->pos)
continue;
spans = 1;
/*
* Account for the data cells in the layout, matching it
* to data cells in the data section.
*/
for (dp = sp->first; dp; dp = dp->next) {
/* Do not used spanned cells in the calculation. */
if (0 < --spans)
continue;
spans = dp->spans;
if (1 < spans)
continue;
assert(dp->layout);
col = &tbl->cols[dp->layout->head->ident];
tblcalc_data(tbl, col, sp->tbl, dp);
}
}
/*
* Calculate width of the spanners. These get one space for a
* vertical line, two for a double-vertical line.
*/
for ( ; hp; hp = hp->next) {
col = &tbl->cols[hp->ident];
switch (hp->pos) {
case (TBL_HEAD_VERT):
col->width = (*tbl->len)(1, tbl->arg);
break;
case (TBL_HEAD_DVERT):
col->width = (*tbl->len)(2, tbl->arg);
break;
default:
break;
}
}
}
static void
tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
const struct tbl *tp, const struct tbl_dat *dp)
{
size_t sz;
/* Branch down into data sub-types. */
switch (dp->layout->pos) {
case (TBL_CELL_HORIZ):
/* FALLTHROUGH */
case (TBL_CELL_DHORIZ):
sz = (*tbl->len)(1, tbl->arg);
if (col->width < sz)
col->width = sz;
break;
case (TBL_CELL_LONG):
/* FALLTHROUGH */
case (TBL_CELL_CENTRE):
/* FALLTHROUGH */
case (TBL_CELL_LEFT):
/* FALLTHROUGH */
case (TBL_CELL_RIGHT):
tblcalc_literal(tbl, col, dp);
break;
case (TBL_CELL_NUMBER):
tblcalc_number(tbl, col, tp, dp);
break;
case (TBL_CELL_DOWN):
break;
default:
abort();
/* NOTREACHED */
}
}
static void
tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
const struct tbl_dat *dp)
{
size_t sz;
const char *str;
str = dp->string ? dp->string : "";
sz = (*tbl->slen)(str, tbl->arg);
if (col->width < sz)
col->width = sz;
}
static void
tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
const struct tbl *tp, const struct tbl_dat *dp)
{
int i;
size_t sz, psz, ssz, d;
const char *str;
char *cp;
char buf[2];
/*
* First calculate number width and decimal place (last + 1 for
* non-decimal numbers). If the stored decimal is subsequent to
* ours, make our size longer by that difference
* (right-"shifting"); similarly, if ours is subsequent the
* stored, then extend the stored size by the difference.
* Finally, re-assign the stored values.
*/
str = dp->string ? dp->string : "";
sz = (*tbl->slen)(str, tbl->arg);
/* FIXME: TBL_DATA_HORIZ et al.? */
buf[0] = tp->decimal;
buf[1] = '\0';
psz = (*tbl->slen)(buf, tbl->arg);
if (NULL != (cp = strrchr(str, tp->decimal))) {
buf[1] = '\0';
for (ssz = 0, i = 0; cp != &str[i]; i++) {
buf[0] = str[i];
ssz += (*tbl->slen)(buf, tbl->arg);
}
d = ssz + psz;
} else
d = sz + psz;
/* Adjust the settings for this column. */
if (col->decimal > d) {
sz += col->decimal - d;
d = col->decimal;
} else
col->width += d - col->decimal;
if (sz > col->width)
col->width = sz;
if (d > col->decimal)
col->decimal = d;
}

71
contrib/mdocml/out.h Normal file
View File

@ -0,0 +1,71 @@
/* $Id: out.h,v 1.21 2011/07/17 15:24:25 kristaps Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 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
enum roffscale {
SCALE_CM, /* centimeters (c) */
SCALE_IN, /* inches (i) */
SCALE_PC, /* pica (P) */
SCALE_PT, /* points (p) */
SCALE_EM, /* ems (m) */
SCALE_MM, /* mini-ems (M) */
SCALE_EN, /* ens (n) */
SCALE_BU, /* default horizontal (u) */
SCALE_VS, /* default vertical (v) */
SCALE_FS, /* syn. for u (f) */
SCALE_MAX
};
struct roffcol {
size_t width; /* width of cell */
size_t decimal; /* decimal position in cell */
};
struct roffsu {
enum roffscale unit;
double scale;
};
typedef size_t (*tbl_strlen)(const char *, void *);
typedef size_t (*tbl_len)(size_t, void *);
struct rofftbl {
tbl_strlen slen; /* calculate string length */
tbl_len len; /* produce width of empty space */
struct roffcol *cols; /* master column specifiers */
void *arg; /* passed to slen and len */
};
__BEGIN_DECLS
#define SCALE_VS_INIT(p, v) \
do { (p)->unit = SCALE_VS; \
(p)->scale = (v); } \
while (/* CONSTCOND */ 0)
#define SCALE_HS_INIT(p, v) \
do { (p)->unit = SCALE_BU; \
(p)->scale = (v); } \
while (/* CONSTCOND */ 0)
int a2roffsu(const char *, struct roffsu *, enum roffscale);
void tblcalc(struct rofftbl *tbl, const struct tbl_span *);
__END_DECLS
#endif /*!OUT_H*/

65
contrib/mdocml/predefs.in Normal file
View File

@ -0,0 +1,65 @@
/* $Id: predefs.in,v 1.3 2011/07/31 11:36:49 schwarze Exp $ */
/*
* Copyright (c) 2011 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 predefined-string translation tables. Each corresponds to a
* predefined strings from (e.g.) tmac/mdoc/doc-nroff. The left-hand
* side corresponds to the input sequence (\*x, \*(xx and so on). The
* right-hand side is what's produced by libroff.
*
* XXX - C-escape strings!
* XXX - update PREDEF_MAX in roff.c if adding more!
*/
PREDEF("Am", "&")
PREDEF("Ba", "|")
PREDEF("Ge", "\\(>=")
PREDEF("Gt", ">")
PREDEF("If", "infinity")
PREDEF("Le", "\\(<=")
PREDEF("Lq", "\\(lq")
PREDEF("Lt", "<")
PREDEF("Na", "NaN")
PREDEF("Ne", "\\(!=")
PREDEF("Pi", "pi")
PREDEF("Pm", "\\(+-")
PREDEF("Rq", "\\(rq")
PREDEF("left-bracket", "[")
PREDEF("left-parenthesis", "(")
PREDEF("lp", "(")
PREDEF("left-singlequote", "\\(oq")
PREDEF("q", "\\(dq")
PREDEF("quote-left", "\\(oq")
PREDEF("quote-right", "\\(cq")
PREDEF("R", "\\(rg")
PREDEF("right-bracket", "]")
PREDEF("right-parenthesis", ")")
PREDEF("rp", ")")
PREDEF("right-singlequote", "\\(cq")
PREDEF("Tm", "(Tm)")
PREDEF("Px", "POSIX")
PREDEF("Ai", "ANSI")
PREDEF("\'", "\\\'")
PREDEF("aa", "\\(aa")
PREDEF("ga", "\\(ga")
PREDEF("`", "\\`")
PREDEF("lq", "\\(lq")
PREDEF("rq", "\\(rq")
PREDEF("ua", "\\(ua")
PREDEF("va", "\\(va")
PREDEF("<=", "\\(<=")
PREDEF(">=", "\\(>=")

846
contrib/mdocml/read.c Normal file
View File

@ -0,0 +1,846 @@
/* $Id: read.c,v 1.28 2012/02/16 20:51:31 joerg Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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
#ifdef HAVE_MMAP
# include <sys/stat.h>
# include <sys/mman.h>
#endif
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mandoc.h"
#include "libmandoc.h"
#include "mdoc.h"
#include "man.h"
#include "main.h"
#ifndef MAP_FILE
#define MAP_FILE 0
#endif
#define REPARSE_LIMIT 1000
struct buf {
char *buf; /* binary input buffer */
size_t sz; /* size of binary buffer */
};
struct mparse {
enum mandoclevel file_status; /* status of current parse */
enum mandoclevel wlevel; /* ignore messages below this */
int line; /* line number in the file */
enum mparset inttype; /* which parser to use */
struct man *pman; /* persistent man parser */
struct mdoc *pmdoc; /* persistent mdoc parser */
struct man *man; /* man parser */
struct mdoc *mdoc; /* mdoc parser */
struct roff *roff; /* roff parser (!NULL) */
int reparse_count; /* finite interp. stack */
mandocmsg mmsg; /* warning/error message handler */
void *arg; /* argument to mmsg */
const char *file;
struct buf *secondary;
};
static void resize_buf(struct buf *, size_t);
static void mparse_buf_r(struct mparse *, struct buf, int);
static void mparse_readfd_r(struct mparse *, int, const char *, int);
static void pset(const char *, int, struct mparse *);
static int read_whole_file(const char *, int, struct buf *, int *);
static void mparse_end(struct mparse *);
static const enum mandocerr mandoclimits[MANDOCLEVEL_MAX] = {
MANDOCERR_OK,
MANDOCERR_WARNING,
MANDOCERR_WARNING,
MANDOCERR_ERROR,
MANDOCERR_FATAL,
MANDOCERR_MAX,
MANDOCERR_MAX
};
static const char * const mandocerrs[MANDOCERR_MAX] = {
"ok",
"generic warning",
/* related to the prologue */
"no title in document",
"document title should be all caps",
"unknown manual section",
"date missing, using today's date",
"cannot parse date, using it verbatim",
"prologue macros out of order",
"duplicate prologue macro",
"macro not allowed in prologue",
"macro not allowed in body",
/* related to document structure */
".so is fragile, better use ln(1)",
"NAME section must come first",
"bad NAME section contents",
"manual name not yet set",
"sections out of conventional order",
"duplicate section name",
"section not in conventional manual section",
/* related to macros and nesting */
"skipping obsolete macro",
"skipping paragraph macro",
"skipping no-space macro",
"blocks badly nested",
"child violates parent syntax",
"nested displays are not portable",
"already in literal mode",
"line scope broken",
/* related to missing macro arguments */
"skipping empty macro",
"argument count wrong",
"missing display type",
"list type must come first",
"tag lists require a width argument",
"missing font type",
"skipping end of block that is not open",
/* related to bad macro arguments */
"skipping argument",
"duplicate argument",
"duplicate display type",
"duplicate list type",
"unknown AT&T UNIX version",
"bad Boolean value",
"unknown font",
"unknown standard specifier",
"bad width argument",
/* related to plain text */
"blank line in non-literal context",
"tab in non-literal context",
"end of line whitespace",
"bad comment style",
"bad escape sequence",
"unterminated quoted string",
/* related to equations */
"unexpected literal in equation",
"generic error",
/* related to equations */
"unexpected equation scope closure",
"equation scope open on exit",
"overlapping equation scopes",
"unexpected end of equation",
"equation syntax error",
/* related to tables */
"bad table syntax",
"bad table option",
"bad table layout",
"no table layout cells specified",
"no table data cells specified",
"ignore data in cell",
"data block still open",
"ignoring extra data cells",
"input stack limit exceeded, infinite loop?",
"skipping bad character",
"escaped character not allowed in a name",
"skipping text before the first section header",
"skipping unknown macro",
"NOT IMPLEMENTED, please use groff: skipping request",
"argument count wrong",
"skipping end of block that is not open",
"missing end of block",
"scope open on exit",
"uname(3) system call failed",
"macro requires line argument(s)",
"macro requires body argument(s)",
"macro requires argument(s)",
"missing list type",
"line argument(s) will be lost",
"body argument(s) will be lost",
"generic fatal error",
"not a manual",
"column syntax is inconsistent",
"NOT IMPLEMENTED: .Bd -file",
"argument count wrong, violates syntax",
"child violates parent syntax",
"argument count wrong, violates syntax",
"NOT IMPLEMENTED: .so with absolute path or \"..\"",
"no document body",
"no document prologue",
"static buffer exhausted",
};
static const char * const mandoclevels[MANDOCLEVEL_MAX] = {
"SUCCESS",
"RESERVED",
"WARNING",
"ERROR",
"FATAL",
"BADARG",
"SYSERR"
};
static void
resize_buf(struct buf *buf, size_t initial)
{
buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
buf->buf = mandoc_realloc(buf->buf, buf->sz);
}
static void
pset(const char *buf, int pos, struct mparse *curp)
{
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.
*
* Separate out pmdoc/pman from mdoc/man: the first persists
* through all parsers, while the latter is used per-parse.
*/
if ('.' == buf[0] || '\'' == buf[0]) {
for (i = 1; buf[i]; i++)
if (' ' != buf[i] && '\t' != buf[i])
break;
if ('\0' == buf[i])
return;
}
switch (curp->inttype) {
case (MPARSE_MDOC):
if (NULL == curp->pmdoc)
curp->pmdoc = mdoc_alloc(curp->roff, curp);
assert(curp->pmdoc);
curp->mdoc = curp->pmdoc;
return;
case (MPARSE_MAN):
if (NULL == curp->pman)
curp->pman = man_alloc(curp->roff, curp);
assert(curp->pman);
curp->man = curp->pman;
return;
default:
break;
}
if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
if (NULL == curp->pmdoc)
curp->pmdoc = mdoc_alloc(curp->roff, curp);
assert(curp->pmdoc);
curp->mdoc = curp->pmdoc;
return;
}
if (NULL == curp->pman)
curp->pman = man_alloc(curp->roff, curp);
assert(curp->pman);
curp->man = curp->pman;
}
/*
* Main parse routine for an opened file. This is called for each
* opened file and simply loops around the full input file, possibly
* nesting (i.e., with `so').
*/
static void
mparse_buf_r(struct mparse *curp, struct buf blk, int start)
{
const struct tbl_span *span;
struct buf ln;
enum rofferr rr;
int i, of, rc;
int pos; /* byte number in the ln buffer */
int lnn; /* line number in the real file */
unsigned char c;
memset(&ln, 0, sizeof(struct buf));
lnn = curp->line;
pos = 0;
for (i = 0; i < (int)blk.sz; ) {
if (0 == pos && '\0' == blk.buf[i])
break;
if (start) {
curp->line = lnn;
curp->reparse_count = 0;
}
while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
/*
* When finding an unescaped newline character,
* leave the character loop to process the line.
* Skip a preceding carriage return, if any.
*/
if ('\r' == blk.buf[i] && i + 1 < (int)blk.sz &&
'\n' == blk.buf[i + 1])
++i;
if ('\n' == blk.buf[i]) {
++i;
++lnn;
break;
}
/*
* Warn about bogus characters. If you're using
* non-ASCII encoding, you're screwing your
* readers. Since I'd rather this not happen,
* I'll be helpful and replace these characters
* with "?", so we don't display gibberish.
* Note to manual writers: use special characters.
*/
c = (unsigned char) blk.buf[i];
if ( ! (isascii(c) &&
(isgraph(c) || isblank(c)))) {
mandoc_msg(MANDOCERR_BADCHAR, curp,
curp->line, pos, NULL);
i++;
if (pos >= (int)ln.sz)
resize_buf(&ln, 256);
ln.buf[pos++] = '?';
continue;
}
/* Trailing backslash = a plain char. */
if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
if (pos >= (int)ln.sz)
resize_buf(&ln, 256);
ln.buf[pos++] = blk.buf[i++];
continue;
}
/*
* Found escape and at least one other character.
* When it's a newline character, skip it.
* When there is a carriage return in between,
* skip that one as well.
*/
if ('\r' == blk.buf[i + 1] && i + 2 < (int)blk.sz &&
'\n' == blk.buf[i + 2])
++i;
if ('\n' == blk.buf[i + 1]) {
i += 2;
++lnn;
continue;
}
if ('"' == blk.buf[i + 1] || '#' == 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 & cont. */
if (pos + 1 >= (int)ln.sz)
resize_buf(&ln, 256);
ln.buf[pos++] = blk.buf[i++];
ln.buf[pos++] = blk.buf[i++];
}
if (pos >= (int)ln.sz)
resize_buf(&ln, 256);
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;
/*
* Maintain a lookaside buffer of all parsed lines. We
* only do this if mparse_keep() has been invoked (the
* buffer may be accessed with mparse_getkeep()).
*/
if (curp->secondary) {
curp->secondary->buf =
mandoc_realloc
(curp->secondary->buf,
curp->secondary->sz + pos + 2);
memcpy(curp->secondary->buf +
curp->secondary->sz,
ln.buf, pos);
curp->secondary->sz += pos;
curp->secondary->buf
[curp->secondary->sz] = '\n';
curp->secondary->sz++;
curp->secondary->buf
[curp->secondary->sz] = '\0';
}
rerun:
rr = roff_parseln
(curp->roff, curp->line,
&ln.buf, &ln.sz, of, &of);
switch (rr) {
case (ROFF_REPARSE):
if (REPARSE_LIMIT >= ++curp->reparse_count)
mparse_buf_r(curp, ln, 0);
else
mandoc_msg(MANDOCERR_ROFFLOOP, curp,
curp->line, pos, NULL);
pos = 0;
continue;
case (ROFF_APPEND):
pos = (int)strlen(ln.buf);
continue;
case (ROFF_RERUN):
goto rerun;
case (ROFF_IGN):
pos = 0;
continue;
case (ROFF_ERR):
assert(MANDOCLEVEL_FATAL <= curp->file_status);
break;
case (ROFF_SO):
/*
* We remove `so' clauses from our lookaside
* buffer because we're going to descend into
* the file recursively.
*/
if (curp->secondary)
curp->secondary->sz -= pos + 1;
mparse_readfd_r(curp, -1, ln.buf + of, 1);
if (MANDOCLEVEL_FATAL <= curp->file_status)
break;
pos = 0;
continue;
default:
break;
}
/*
* If we encounter errors in the recursive parse, make
* sure we don't continue parsing.
*/
if (MANDOCLEVEL_FATAL <= curp->file_status)
break;
/*
* If input parsers have not been allocated, do so now.
* We keep these instanced between parsers, but set them
* locally per parse routine since we can use different
* parsers with each one.
*/
if ( ! (curp->man || curp->mdoc))
pset(ln.buf + of, pos - of, curp);
/*
* Lastly, push down into the parsers themselves. One
* of these will have already been set in the pset()
* routine.
* If libroff returns ROFF_TBL, then add it to the
* currently open parse. Since we only get here if
* there does exist data (see tbl_data.c), we're
* guaranteed that something's been allocated.
* Do the same for ROFF_EQN.
*/
rc = -1;
if (ROFF_TBL == rr)
while (NULL != (span = roff_span(curp->roff))) {
rc = curp->man ?
man_addspan(curp->man, span) :
mdoc_addspan(curp->mdoc, span);
if (0 == rc)
break;
}
else if (ROFF_EQN == rr)
rc = curp->mdoc ?
mdoc_addeqn(curp->mdoc,
roff_eqn(curp->roff)) :
man_addeqn(curp->man,
roff_eqn(curp->roff));
else if (curp->man || curp->mdoc)
rc = curp->man ?
man_parseln(curp->man,
curp->line, ln.buf, of) :
mdoc_parseln(curp->mdoc,
curp->line, ln.buf, of);
if (0 == rc) {
assert(MANDOCLEVEL_FATAL <= curp->file_status);
break;
}
/* Temporary buffers typically are not full. */
if (0 == start && '\0' == blk.buf[i])
break;
/* Start the next input line. */
pos = 0;
}
free(ln.buf);
}
static int
read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
{
size_t off;
ssize_t ssz;
#ifdef HAVE_MMAP
struct stat st;
if (-1 == fstat(fd, &st)) {
perror(file);
return(0);
}
/*
* 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", file);
return(0);
}
*with_mmap = 1;
fb->sz = (size_t)st.st_size;
fb->buf = mmap(NULL, fb->sz, PROT_READ,
MAP_FILE|MAP_SHARED, 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", file);
break;
}
resize_buf(fb, 65536);
}
ssz = read(fd, fb->buf + (int)off, fb->sz - off);
if (ssz == 0) {
fb->sz = off;
return(1);
}
if (ssz == -1) {
perror(file);
break;
}
off += (size_t)ssz;
}
free(fb->buf);
fb->buf = NULL;
return(0);
}
static void
mparse_end(struct mparse *curp)
{
if (MANDOCLEVEL_FATAL <= curp->file_status)
return;
if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
assert(MANDOCLEVEL_FATAL <= curp->file_status);
return;
}
if (curp->man && ! man_endparse(curp->man)) {
assert(MANDOCLEVEL_FATAL <= curp->file_status);
return;
}
if ( ! (curp->man || curp->mdoc)) {
mandoc_msg(MANDOCERR_NOTMANUAL, curp, 1, 0, NULL);
curp->file_status = MANDOCLEVEL_FATAL;
return;
}
roff_endparse(curp->roff);
}
static void
mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file,
int re)
{
const char *svfile;
/* Line number is per-file. */
svfile = curp->file;
curp->file = file;
curp->line = 1;
mparse_buf_r(curp, blk, 1);
if (0 == re && MANDOCLEVEL_FATAL > curp->file_status)
mparse_end(curp);
curp->file = svfile;
}
enum mandoclevel
mparse_readmem(struct mparse *curp, const void *buf, size_t len,
const char *file)
{
struct buf blk;
blk.buf = UNCONST(buf);
blk.sz = len;
mparse_parse_buffer(curp, blk, file, 0);
return(curp->file_status);
}
static void
mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
{
struct buf blk;
int with_mmap;
if (-1 == fd)
if (-1 == (fd = open(file, O_RDONLY, 0))) {
perror(file);
curp->file_status = MANDOCLEVEL_SYSERR;
return;
}
/*
* Run for each opened file; may be called more than once for
* each full parse sequence if the opened file is nested (i.e.,
* from `so'). Simply sucks in the whole file and moves into
* the parse phase for the file.
*/
if ( ! read_whole_file(file, fd, &blk, &with_mmap)) {
curp->file_status = MANDOCLEVEL_SYSERR;
return;
}
mparse_parse_buffer(curp, blk, file, re);
#ifdef HAVE_MMAP
if (with_mmap)
munmap(blk.buf, blk.sz);
else
#endif
free(blk.buf);
if (STDIN_FILENO != fd && -1 == close(fd))
perror(file);
}
enum mandoclevel
mparse_readfd(struct mparse *curp, int fd, const char *file)
{
mparse_readfd_r(curp, fd, file, 0);
return(curp->file_status);
}
struct mparse *
mparse_alloc(enum mparset inttype, enum mandoclevel wlevel, mandocmsg mmsg, void *arg)
{
struct mparse *curp;
assert(wlevel <= MANDOCLEVEL_FATAL);
curp = mandoc_calloc(1, sizeof(struct mparse));
curp->wlevel = wlevel;
curp->mmsg = mmsg;
curp->arg = arg;
curp->inttype = inttype;
curp->roff = roff_alloc(curp);
return(curp);
}
void
mparse_reset(struct mparse *curp)
{
roff_reset(curp->roff);
if (curp->mdoc)
mdoc_reset(curp->mdoc);
if (curp->man)
man_reset(curp->man);
if (curp->secondary)
curp->secondary->sz = 0;
curp->file_status = MANDOCLEVEL_OK;
curp->mdoc = NULL;
curp->man = NULL;
}
void
mparse_free(struct mparse *curp)
{
if (curp->pmdoc)
mdoc_free(curp->pmdoc);
if (curp->pman)
man_free(curp->pman);
if (curp->roff)
roff_free(curp->roff);
if (curp->secondary)
free(curp->secondary->buf);
free(curp->secondary);
free(curp);
}
void
mparse_result(struct mparse *curp, struct mdoc **mdoc, struct man **man)
{
if (mdoc)
*mdoc = curp->mdoc;
if (man)
*man = curp->man;
}
void
mandoc_vmsg(enum mandocerr t, struct mparse *m,
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);
mandoc_msg(t, m, ln, pos, buf);
}
void
mandoc_msg(enum mandocerr er, struct mparse *m,
int ln, int col, const char *msg)
{
enum mandoclevel level;
level = MANDOCLEVEL_FATAL;
while (er < mandoclimits[level])
level--;
if (level < m->wlevel)
return;
if (m->mmsg)
(*m->mmsg)(er, level, m->file, ln, col, msg);
if (m->file_status < level)
m->file_status = level;
}
const char *
mparse_strerror(enum mandocerr er)
{
return(mandocerrs[er]);
}
const char *
mparse_strlevel(enum mandoclevel lvl)
{
return(mandoclevels[lvl]);
}
void
mparse_keep(struct mparse *p)
{
assert(NULL == p->secondary);
p->secondary = mandoc_calloc(1, sizeof(struct buf));
}
const char *
mparse_getkeep(const struct mparse *p)
{
assert(p->secondary);
return(p->secondary->sz ? p->secondary->buf : NULL);
}

989
contrib/mdocml/roff.7 Normal file
View File

@ -0,0 +1,989 @@
.\" $Id: roff.7,v 1.37 2011/12/11 00:38:11 schwarze Exp $
.\"
.\" Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" 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: December 11 2011 $
.Dt ROFF 7
.Os
.Sh NAME
.Nm roff
.Nd roff language reference for mandoc
.Sh DESCRIPTION
The
.Nm roff
language is a general purpose text formatting language.
Since traditional implementations of the
.Xr mdoc 7
and
.Xr man 7
manual formatting languages are based on it,
many real-world manuals use small numbers of
.Nm
requests intermixed with their
.Xr mdoc 7
or
.Xr man 7
code.
To properly format such manuals, the
.Xr mandoc 1
utility supports a tiny subset of
.Nm
requests.
Only these requests supported by
.Xr mandoc 1
are documented in the present manual,
together with the basic language syntax shared by
.Nm ,
.Xr mdoc 7 ,
and
.Xr man 7 .
For complete
.Nm
manuals, consult the
.Sx SEE ALSO
section.
.Pp
Input lines beginning with the control character
.Sq \&.
are parsed for requests and macros.
Such lines are called
.Dq request lines
or
.Dq macro lines ,
respectively.
Requests change the processing state and manipulate the formatting;
some macros also define the document structure and produce formatted
output.
The single quote
.Pq Qq \(aq
is accepted as an alternative control character,
treated by
.Xr mandoc 1
just like
.Ql \&.
.Pp
Lines not beginning with control characters are called
.Dq text lines .
They provide free-form text to be printed; the formatting of the text
depends on the respective processing context.
.Sh LANGUAGE SYNTAX
.Nm
documents may contain only graphable 7-bit ASCII characters, the space
character, and, in certain circumstances, the tab character.
The back-space character
.Sq \e
indicates the start of an escape sequence for
.Sx Comments ,
.Sx Special Characters ,
.Sx Predefined Strings ,
and
user-defined strings defined using the
.Sx ds
request.
.Ss Comments
Text following an escaped double-quote
.Sq \e\(dq ,
whether in a request, macro, or text line, is ignored to the end of the line.
A request line beginning with a control character and comment escape
.Sq \&.\e\(dq
is also ignored.
Furthermore, request lines with only a control character and optional
trailing whitespace are stripped from input.
.Pp
Examples:
.Bd -literal -offset indent -compact
\&.\e\(dq This is a comment line.
\&.\e\(dq The next line is ignored:
\&.
\&.Sh EXAMPLES \e\(dq This is a comment, too.
\&example text \e\(dq And so is this.
.Ed
.Ss Special Characters
Special characters are used to encode special glyphs and are rendered
differently across output media.
They may occur in request, macro, and text 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.
.Pp
Examples:
.Bl -tag -width Ds -offset indent -compact
.It Li \e(em
Two-letter em dash escape.
.It Li \ee
One-letter backslash escape.
.El
.Pp
See
.Xr mandoc_char 7
for a complete list.
.Ss Text Decoration
Terms may be text-decorated using the
.Sq \ef
escape followed by an indicator: B (bold), I (italic), R (regular), or P
(revert to previous mode).
A numerical representation 3, 2, or 1 (bold, italic, and regular,
respectively) may be used instead.
The indicator or numerical representative may be preceded by C
(constant-width), which is ignored.
.Pp
Examples:
.Bl -tag -width Ds -offset indent -compact
.It Li \efBbold\efR
Write in bold, then switch to regular font mode.
.It Li \efIitalic\efP
Write in italic, then return to previous font mode.
.El
.Pp
Text decoration is
.Em not
recommended for
.Xr mdoc 7 ,
which encourages semantic annotation.
.Ss Predefined Strings
Predefined strings, like
.Sx Special Characters ,
mark special output glyphs.
Predefined strings are escaped with the slash-asterisk,
.Sq \e* :
single-character
.Sq \e*X ,
two-character
.Sq \e*(XX ,
and N-character
.Sq \e*[N] .
.Pp
Examples:
.Bl -tag -width Ds -offset indent -compact
.It Li \e*(Am
Two-letter ampersand predefined string.
.It Li \e*q
One-letter double-quote predefined string.
.El
.Pp
Predefined strings are not recommended for use,
as they differ across implementations.
Those supported by
.Xr mandoc 1
are listed in
.Xr mandoc_char 7 .
Manuals using these predefined strings are almost certainly not portable.
.Ss Whitespace
Whitespace consists of the space character.
In text lines, whitespace is preserved within a line.
In request and macro lines, whitespace delimits arguments and is discarded.
.Pp
Unescaped trailing spaces are stripped from text line input unless in a
literal context.
In general, trailing whitespace on any input line is discouraged for
reasons of portability.
In the rare case that a blank character is needed at the end of an
input line, it may be forced by
.Sq \e\ \e& .
.Pp
Literal space characters can be produced in the output
using escape sequences.
In macro lines, they can also be included in arguments using quotation; see
.Sx MACRO SYNTAX
for details.
.Pp
Blank text lines, which may include whitespace, are only permitted
within literal contexts.
If the first character of a text line is a space, that line is printed
with a leading newline.
.Ss Scaling Widths
Many requests and macros support scaled widths for their arguments.
The syntax for a scaled width is
.Sq Li [+-]?[0-9]*.[0-9]*[:unit:] ,
where a decimal must be preceded or followed by at least one digit.
Negative numbers, while accepted, are truncated to zero.
.Pp
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.
See
.Sx COMPATIBILITY .
.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.
.Pp
Examples:
.Bl -tag -width ".Bl -tag -width 2i" -offset indent -compact
.It Li \&.Bl -tag -width 2i
two-inch tagged list indentation in
.Xr mdoc 7
.It Li \&.HP 2i
two-inch tagged list indentation in
.Xr man 7
.It Li \&.sp 2v
two vertical spaces
.El
.Ss Sentence Spacing
Each sentence should terminate at the end of an input line.
By doing this, a formatter 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
.Po
.Sq \&) ,
.Sq \&] ,
.Sq \&' ,
.Sq \&"
.Pc .
.Pp
The proper spacing is also intelligently preserved if a sentence ends at
the boundary of a macro line.
.Pp
Examples:
.Bd -literal -offset indent -compact
Do not end sentences mid-line like this. Instead,
end a sentence like this.
A macro would end like this:
\&.Xr mandoc 1 \&.
.Ed
.Sh REQUEST SYNTAX
A request or macro line consists of:
.Pp
.Bl -enum -compact
.It
the control character
.Sq \&.
or
.Sq \(aq
at the beginning of the line,
.It
optionally an arbitrary amount of whitespace,
.It
the name of the request or the macro, which is one word of arbitrary
length, terminated by whitespace,
.It
and zero or more arguments delimited by whitespace.
.El
.Pp
Thus, the following request lines are all equivalent:
.Bd -literal -offset indent
\&.ig end
\&.ig end
\&. ig end
.Ed
.Sh MACRO SYNTAX
Macros are provided by the
.Xr mdoc 7
and
.Xr man 7
languages and can be defined by the
.Sx \&de
request.
When called, they follow the same syntax as requests, except that
macro arguments may optionally be quoted by enclosing them
in double quote characters
.Pq Sq \(dq .
Quoted text, even if it contains whitespace or would cause
a macro invocation when unquoted, is always considered literal text.
Inside quoted text, pairs of double quote characters
.Pq Sq Qq
resolve to single double quote characters.
.Pp
To be recognised as the beginning of a quoted argument, the opening
quote character must be preceded by a space character.
A quoted argument extends to the next double quote character that is not
part of a pair, or to the end of the input line, whichever comes earlier.
Leaving out the terminating double quote character at the end of the line
is discouraged.
For clarity, if more arguments follow on the same input line,
it is recommended to follow the terminating double quote character
by a space character; in case the next character after the terminating
double quote character is anything else, it is regarded as the beginning
of the next, unquoted argument.
.Pp
Both in quoted and unquoted arguments, pairs of backslashes
.Pq Sq \e\e
resolve to single backslashes.
In unquoted arguments, space characters can alternatively be included
by preceding them with a backslash
.Pq Sq \e\~ ,
but quoting is usually better for clarity.
.Pp
Examples:
.Bl -tag -width Ds -offset indent -compact
.It Li .Fn strlen \(dqconst char *s\(dq
Group arguments
.Qq const char *s
into one function argument.
If unspecified,
.Qq const ,
.Qq char ,
and
.Qq *s
would be considered separate arguments.
.It Li .Op \(dqFl a\(dq
Consider
.Qq \&Fl a
as literal text instead of a flag macro.
.El
.Sh REQUEST REFERENCE
The
.Xr mandoc 1
.Nm
parser recognises the following requests.
Note that the
.Nm
language defines many more requests not implemented in
.Xr mandoc 1 .
.Ss \&ad
Set line adjustment mode.
This line-scoped request is intended to have one argument to select
normal, left, right, or centre adjustment for subsequent text.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
.Ss \&am
Append to a macro definition.
The syntax of this request is the same as that of
.Sx \&de .
It is currently ignored by
.Xr mandoc 1 ,
as are its children.
.Ss \&ami
Append to a macro definition, specifying the macro name indirectly.
The syntax of this request is the same as that of
.Sx \&dei .
It is currently ignored by
.Xr mandoc 1 ,
as are its children.
.Ss \&am1
Append to a macro definition, switching roff compatibility mode off
during macro execution.
The syntax of this request is the same as that of
.Sx \&de1 .
It is currently ignored by
.Xr mandoc 1 ,
as are its children.
.Ss \&de
Define a
.Nm
macro.
Its syntax can be either
.Bd -literal -offset indent
.Pf . Cm \&de Ar name
.Ar macro definition
\&..
.Ed
.Pp
or
.Bd -literal -offset indent
.Pf . Cm \&de Ar name Ar end
.Ar macro definition
.Pf . Ar end
.Ed
.Pp
Both forms define or redefine the macro
.Ar name
to represent the
.Ar macro definition ,
which may consist of one or more input lines, including the newline
characters terminating each line, optionally containing calls to
.Nm
requests,
.Nm
macros or high-level macros like
.Xr man 7
or
.Xr mdoc 7
macros, whichever applies to the document in question.
.Pp
Specifying a custom
.Ar end
macro works in the same way as for
.Sx \&ig ;
namely, the call to
.Sq Pf . Ar end
first ends the
.Ar macro definition ,
and after that, it is also evaluated as a
.Nm
request or
.Nm
macro, but not as a high-level macro.
.Pp
The macro can be invoked later using the syntax
.Pp
.D1 Pf . Ar name Op Ar argument Op Ar argument ...
.Pp
Regarding argument parsing, see
.Sx MACRO SYNTAX
above.
.Pp
The line invoking the macro will be replaced
in the input stream by the
.Ar macro definition ,
replacing all occurrences of
.No \e\e$ Ns Ar N ,
where
.Ar N
is a digit, by the
.Ar N Ns th Ar argument .
For example,
.Bd -literal -offset indent
\&.de ZN
\efI\e^\e\e$1\e^\efP\e\e$2
\&..
\&.ZN XtFree .
.Ed
.Pp
produces
.Pp
.D1 \efI\e^XtFree\e^\efP.
.Pp
in the input stream, and thus in the output: \fI\^XtFree\^\fP.
.Pp
Since macros and user-defined strings share a common string table,
defining a macro
.Ar name
clobbers the user-defined string
.Ar name ,
and the
.Ar macro definition
can also be printed using the
.Sq \e*
string interpolation syntax described below
.Sx ds ,
but this is rarely useful because every macro definition contains at least
one explicit newline character.
.Pp
In order to prevent endless recursion, both groff and
.Xr mandoc 1
limit the stack depth for expanding macros and strings
to a large, but finite number.
Do not rely on the exact value of this limit.
.Ss \&dei
Define a
.Nm
macro, specifying the macro name indirectly.
The syntax of this request is the same as that of
.Sx \&de .
It is currently ignored by
.Xr mandoc 1 ,
as are its children.
.Ss \&de1
Define a
.Nm
macro that will be executed with
.Nm
compatibility mode switched off during macro execution.
This is a GNU extension not available in traditional
.Nm
implementations and not even in older versions of groff.
Since
.Xr mandoc 1
does not implement
.Nm
compatibility mode at all, it handles this request as an alias for
.Sx \&de .
.Ss \&ds
Define a user-defined string.
Its syntax is as follows:
.Pp
.D1 Pf . Cm \&ds Ar name Oo \(dq Oc Ns Ar string
.Pp
The
.Ar name
and
.Ar string
arguments are space-separated.
If the
.Ar string
begins with a double-quote character, that character will not be part
of the string.
All remaining characters on the input line form the
.Ar string ,
including whitespace and double-quote characters, even trailing ones.
.Pp
The
.Ar string
can be interpolated into subsequent text by using
.No \e* Ns Bq Ar name
for a
.Ar name
of arbitrary length, or \e*(NN or \e*N if the length of
.Ar name
is two or one characters, respectively.
Interpolation can be prevented by escaping the leading backslash;
that is, an asterisk preceded by an even number of backslashes
does not trigger string interpolation.
.Pp
Since user-defined strings and macros share a common string table,
defining a string
.Ar name
clobbers the macro
.Ar name ,
and the
.Ar name
used for defining a string can also be invoked as a macro,
in which case the following input line will be appended to the
.Ar string ,
forming a new input line passed to the
.Nm
parser.
For example,
.Bd -literal -offset indent
\&.ds badidea .S
\&.badidea
H SYNOPSIS
.Ed
.Pp
invokes the
.Cm SH
macro when used in a
.Xr man 7
document.
Such abuse is of course strongly discouraged.
.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 request is similar to
.Sx \&if
except that the conditional is missing.
.Ss \&EN
End an equation block.
See
.Sx \&EQ .
.Ss \&EQ
Begin an equation block.
See
.Xr eqn 7
for a description of the equation language.
.Ss \&hy
Set automatic hyphenation mode.
This line-scoped request is currently ignored.
.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 nroff style as opposed to troff 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 .ig
.Pp
will discard the
.Sq \&.ig ,
which may lead to interesting results, but
.Pp
.D1 \&.if t .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 request has the following syntax:
.Bd -literal -offset indent
\&.if COND \e{\e
BODY...
\&.\e}
.Ed
.Bd -literal -offset indent
\&.if COND \e{ BODY
BODY... \e}
.Ed
.Bd -literal -offset indent
\&.if COND \e{ BODY
BODY...
\&.\e}
.Ed
.Bd -literal -offset indent
\&.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 escape sequence
.Sq \.\e} .
If the BODY is not enclosed in braces, scope continues until
the end of the line.
If the COND is followed by a BODY on the same line, whether after a
brace or not, then requests and 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 request or 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 the
.Sq \e}
is converted into a zero-width escape sequence if not passed as a
standalone macro
.Sq \&.\e} .
For example,
.Pp
.D1 \&.Fl a \e} b
.Pp
will result in
.Sq \e}
being considered an argument of the
.Sq \&Fl
macro.
.Ss \&ig
Ignore input.
Its syntax can be either
.Bd -literal -offset indent
.Pf . Cm \&ig
.Ar ignored text
\&..
.Ed
.Pp
or
.Bd -literal -offset indent
.Pf . Cm \&ig Ar end
.Ar ignored text
.Pf . Ar end
.Ed
.Pp
In the first case, input is ignored until a
.Sq \&..
request is encountered on its own line.
In the second case, input is ignored until the specified
.Sq Pf . Ar end
macro is encountered.
Do not use the escape character
.Sq \e
anywhere in the definition of
.Ar end ;
it would cause very strange behaviour.
.Pp
When the
.Ar end
macro is a roff request or a roff macro, like in
.Pp
.D1 \&.ig if
.Pp
the subsequent invocation of
.Sx \&if
will first terminate the
.Ar ignored text ,
then be invoked as usual.
Otherwise, it only terminates the
.Ar ignored text ,
and arguments following it or the
.Sq \&..
request are discarded.
.Ss \&ne
Declare the need for the specified minimum vertical space
before the next trap or the bottom of the page.
This line-scoped request is currently ignored.
.Ss \&nh
Turn off automatic hyphenation mode.
This line-scoped request is currently ignored.
.Ss \&rm
Remove a request, macro or string.
This request 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 \&nr
Define a register.
A register is an arbitrary string value that defines some sort of state,
which influences parsing and/or formatting.
Its syntax is as follows:
.Pp
.D1 Pf \. Cm \&nr Ar name Ar value
.Pp
The
.Ar value
may, at the moment, only be an integer.
So far, only the following register
.Ar name
is recognised:
.Bl -tag -width Ds
.It Cm nS
If set to a positive integer value, certain
.Xr mdoc 7
macros will behave in the same way as in the
.Em SYNOPSIS
section.
If set to 0, these macros will behave in the same way as outside the
.Em SYNOPSIS
section, even when called within the
.Em SYNOPSIS
section itself.
Note that starting a new
.Xr mdoc 7
section with the
.Cm \&Sh
macro will reset this register.
.El
.Ss \&ns
Turn on no-space mode.
This line-scoped request is intended to take no arguments.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
.Ss \&ps
Change point size.
This line-scoped request is intended to take one numerical argument.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
.Ss \&so
Include a source file.
Its syntax is as follows:
.Pp
.D1 Pf \. Cm \&so Ar file
.Pp
The
.Ar file
will be read and its contents processed as input in place of the
.Sq \&.so
request line.
To avoid inadvertent inclusion of unrelated files,
.Xr mandoc 1
only accepts relative paths not containing the strings
.Qq ../
and
.Qq /.. .
.Pp
This request requires
.Xr man 1
to change to the right directory before calling
.Xr mandoc 1 ,
per convention to the root of the manual tree.
Typical usage looks like:
.Pp
.Dl \&.so man3/Xcursor.3
.Pp
As the whole concept is rather fragile, the use of
.Sx \&so
is discouraged.
Use
.Xr ln 1
instead.
.Ss \&ta
Set tab stops.
This line-scoped request can take an arbitrary number of arguments.
Currently, it is ignored including its arguments.
.Ss \&tr
Output character translation.
Its syntax is as follows:
.Pp
.D1 Pf \. Cm \&tr Ar [ab]+
.Pp
Pairs of
.Ar ab
characters are replaced
.Ar ( a
for
.Ar b ) .
Replacement (or origin) characters may also be character escapes; thus,
.Pp
.Dl tr \e(xx\e(yy
.Pp
replaces all invocations of \e(xx with \e(yy.
.Ss \&T&
Re-start a table layout, retaining the options of the prior table
invocation.
See
.Sx \&TS .
.Ss \&TE
End a table context.
See
.Sx \&TS .
.Ss \&TS
Begin a table, which formats input in aligned rows and columns.
See
.Xr tbl 7
for a description of the tbl language.
.Sh COMPATIBILITY
This section documents compatibility between mandoc and other other
.Nm
implementations, at this time limited to GNU troff
.Pq Qq groff .
The term
.Qq historic groff
refers to groff version 1.15.
.Pp
.Bl -dash -compact
.It
In mandoc, the
.Sx \&EQ ,
.Sx \&TE ,
.Sx \&TS ,
and
.Sx \&T& ,
macros are considered regular macros.
In all other
.Nm
implementations, these are special macros that must be specified without
spacing between the control character (which must be a period) and the
macro name.
.It
The
.Cm nS
register is only compatible with OpenBSD's groff-1.15.
.It
Historic groff did not accept white-space before a custom
.Ar end
macro for the
.Sx \&ig
request.
.It
The
.Sx \&if
and family would print funny white-spaces with historic groff when
using the next-line syntax.
.El
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr eqn 7 ,
.Xr man 7 ,
.Xr mandoc_char 7 ,
.Xr mdoc 7 ,
.Xr tbl 7
.Rs
.%A Joseph F. Ossanna
.%A Brian W. Kernighan
.%I AT&T Bell Laboratories
.%T Troff User's Manual
.%R Computing Science Technical Report
.%N 54
.%C Murray Hill, New Jersey
.%D 1976 and 1992
.%U http://www.kohala.com/start/troff/cstr54.ps
.Re
.Rs
.%A Joseph F. Ossanna
.%A Brian W. Kernighan
.%A Gunnar Ritter
.%T Heirloom Documentation Tools Nroff/Troff User's Manual
.%D September 17, 2007
.%U http://heirloom.sourceforge.net/doctools/troff.pdf
.Re
.Sh HISTORY
The RUNOFF typesetting system, whose input forms the basis for
.Nm ,
was written in MAD and FAP for the CTSS operating system by Jerome E.
Saltzer in 1964.
Doug McIlroy rewrote it in BCPL in 1969, renaming it
.Nm .
Dennis M. Ritchie rewrote McIlroy's
.Nm
in PDP-11 assembly for
.At v1 ,
Joseph F. Ossanna improved roff and renamed it nroff
for
.At v2 ,
then ported nroff to C as troff, which Brian W. Kernighan released with
.At v7 .
In 1989, James Clarke re-implemented troff in C++, naming it groff.
.Sh AUTHORS
.An -nosplit
This
.Nm
reference was written by
.An Kristaps Dzonsons ,
.Mt kristaps@bsd.lv ;
and
.An Ingo Schwarze ,
.Mt schwarze@openbsd.org .

1768
contrib/mdocml/roff.c Normal file

File diff suppressed because it is too large Load Diff

39
contrib/mdocml/st.c Normal file
View File

@ -0,0 +1,39 @@
/* $Id: st.c,v 1.9 2011/03/22 14:33:05 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 "mdoc.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);
}

78
contrib/mdocml/st.in Normal file
View File

@ -0,0 +1,78 @@
/* $Id: st.in,v 1.19 2012/02/26 21:47:09 schwarze 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.
*/
/*
* 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.
* The non-breaking blanks prevent ending an output line right before
* a number. Groff prevent line breaks at the same places.
*
* REMEMBER TO ADD NEW STANDARDS TO MDOC.7!
*/
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, Amendment 1\\(rq)")
LINE("-isoC-tcor1", "ISO/IEC 9899/TCOR1:1994 (\\(lqISO\\~C90, Technical Corrigendum 1\\(rq)")
LINE("-isoC-tcor2", "ISO/IEC 9899/TCOR2:1995 (\\(lqISO\\~C90, Technical Corrigendum 2\\(rq)")
LINE("-isoC-99", "ISO/IEC 9899:1999 (\\(lqISO\\~C99\\(rq)")
LINE("-isoC-2011", "ISO/IEC 9899:2011 (\\(lqISO\\~C11\\(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\\~C89\\(rq)")
LINE("-ansiC-89", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(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("-iso8601", "ISO 8601")
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, Version\\~2 (\\(lqXPG4.2\\(rq)")
LINE("-xpg4.3", "X/Open Portability Guide Issue\\~4, Version\\~3 (\\(lqXPG4.3\\(rq)")
LINE("-xbd5", "X/Open Base 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)")

144
contrib/mdocml/style.css Normal file
View File

@ -0,0 +1,144 @@
/* $Id: style.css,v 1.25 2011/08/26 09:03:17 kristaps Exp $ */
/*
* This is an example style-sheet provided for mandoc(1) and the -Thtml
* or -Txhtml output mode.
*
* It mimics the appearance of the traditional cvsweb output.
*
* See mdoc(7) and man(7) for macro explanations.
*/
html { max-width: 880px; margin-left: 1em; }
body { font-size: smaller; font-family: Helvetica,Arial,sans-serif; }
h1 { margin-bottom: 1ex; font-size: 110%; margin-left: -4ex; } /* Section header (Sh, SH). */
h2 { margin-bottom: 1ex; font-size: 105%; margin-left: -2ex; } /* Sub-section header (Ss, SS). */
table { width: 100%; margin-top: 0ex; margin-bottom: 0ex; } /* All tables. */
td { vertical-align: top; } /* All table cells. */
p { } /* Paragraph: Pp, Lp. */
blockquote { margin-left: 5ex; margin-top: 0ex; margin-bottom: 0ex; } /* D1. */
div.section { margin-bottom: 2ex; margin-left: 5ex; } /* Sections (Sh, SH). */
div.subsection { } /* Sub-sections (Ss, SS). */
table.synopsis { } /* SYNOPSIS section table. */
/* Preamble structure. */
table.foot { font-size: smaller; margin-top: 1em; border-top: 1px dotted #dddddd; } /* Document footer. */
td.foot-date { width: 50%; } /* Document footer: date. */
td.foot-os { width: 50%; text-align: right; } /* Document footer: OS/source. */
table.head { font-size: smaller; margin-bottom: 1em; border-bottom: 1px dotted #dddddd; } /* Document header. */
td.head-ltitle { width: 10%; } /* Document header: left-title. */
td.head-vol { width: 80%; text-align: center; } /* Document header: volume. */
td.head-rtitle { width: 10%; text-align: right; } /* Document header: right-title. */
/* General font modes. */
i { } /* Italic: BI, IB, I, (implicit). */
.emph { font-style: italic; font-weight: normal; } /* Emphasis: Em, Bl -emphasis. */
b { } /* Bold: SB, BI, IB, BR, RB, B, (implicit). */
.symb { font-style: normal; font-weight: bold; } /* Symbolic: Sy, Ms, Bf -symbolic. */
small { } /* Small: SB, SM. */
.lit { font-style: normal; font-weight: normal; font-family: monospace; } /* Literal: Dl, Li, Ql, Bf -literal, Bl -literal, Bl -unfilled. */
/* Block modes. */
.display { } /* Top of all Bd, D1, Dl. */
.list { } /* Top of all Bl. */
/* Context-specific modes. */
i.addr { font-weight: normal; } /* Address (Ad). */
i.arg { font-weight: normal; } /* Command argument (Ar). */
span.author { } /* Author name (An). */
b.cmd { font-style: normal; } /* Command (Cm). */
b.config { font-style: normal; } /* Config statement (Cd). */
span.define { } /* Defines (Dv). */
span.desc { } /* Nd. After em-dash. */
b.diag { font-style: normal; } /* Diagnostic (Bl -diag). */
span.env { } /* Environment variables (Ev). */
span.errno { } /* Error string (Er). */
i.farg { font-weight: normal; } /* Function argument (Fa, Fn). */
i.file { font-weight: normal; } /* File (Pa). */
b.flag { font-style: normal; } /* Flag (Fl, Cm). */
b.fname { font-style: normal; } /* Function name (Fa, Fn, Rv). */
i.ftype { font-weight: normal; } /* Function types (Ft, Fn). */
b.includes { font-style: normal; } /* Header includes (In). */
span.lib { } /* Library (Lb). */
i.link-sec { font-weight: normal; } /* Section links (Sx). */
b.macro { font-style: normal; } /* Macro-ish thing (Fd). */
b.name { font-style: normal; } /* Name of utility (Nm). */
span.opt { } /* Options (Op, Oo/Oc). */
span.ref { } /* Citations (Rs). */
span.ref-auth { } /* Reference author (%A). */
i.ref-book { font-weight: normal; } /* Reference book (%B). */
span.ref-city { } /* Reference city (%C). */
span.ref-date { } /* Reference date (%D). */
i.ref-issue { font-weight: normal; } /* Reference issuer/publisher (%I). */
i.ref-jrnl { 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.type { font-style: italic; font-weight: normal; } /* Variable types (Vt). */
span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */
b.utility { font-style: normal; } /* Name of utility (Ex). */
b.var { 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). */
/* Formatting for lists. See mdoc(7). */
dl.list-diag { }
dt.list-diag { }
dd.list-diag { }
dl.list-hang { }
dt.list-hang { }
dd.list-hang { }
dl.list-inset { }
dt.list-inset { }
dd.list-inset { }
dl.list-ohang { }
dt.list-ohang { }
dd.list-ohang { margin-left: 0ex; }
dl.list-tag { }
dt.list-tag { }
dd.list-tag { }
table.list-col { }
tr.list-col { }
td.list-col { }
ul.list-bul { list-style-type: disc; padding-left: 1em; }
li.list-bul { }
ul.list-dash { list-style-type: none; padding-left: 0em; }
li.list-dash:before { content: "\2014 "; }
ul.list-hyph { list-style-type: none; padding-left: 0em; }
li.list-hyph:before { content: "\2013 "; }
ul.list-item { list-style-type: none; padding-left: 0em; }
li.list-item { }
ol.list-enum { padding-left: 2em; }
li.list-enum { }
/* Equation modes. See eqn(7). */
span.eqn { }
/* Table modes. See tbl(7). */
table.tbl { }

348
contrib/mdocml/tbl.7 Normal file
View File

@ -0,0 +1,348 @@
.\" $Id: tbl.7,v 1.16 2011/09/03 00:29:21 kristaps Exp $
.\"
.\" Copyright (c) 2010, 2011 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: September 3 2011 $
.Dt TBL 7
.Os
.Sh NAME
.Nm tbl
.Nd tbl language reference for mandoc
.Sh DESCRIPTION
The
.Nm tbl
language is a table-formatting language.
It is used within
.Xr mdoc 7
and
.Xr man 7
.Ux
manual pages.
This manual describes the subset of the
.Nm
language accepted by the
.Xr mandoc 1
utility.
.Pp
Tables within
.Xr mdoc 7
or
.Xr man 7
are enclosed by the
.Sq TS
and
.Sq TE
macro tags, whose precise syntax is documented in
.Xr roff 7 .
Tables consist of a series of options on a single line, followed by the
table layout, followed by data.
.Pp
For example, the following creates a boxed table with digits centred in
the cells.
.Bd -literal -offset indent
\&.TS
tab(:) box;
c5 c5 c5.
1:2:3
4:5:6
\&.TE
.Ed
.Pp
When formatted, the following output is produced:
.Bd -filled -offset indent -compact
.TS
tab(:) box;
c5 c5 c5.
1:2:3
4:5:6
.TE
.Ed
.Pp
The
.Nm
implementation in
.Xr mandoc 1
is
.Ud
.Sh TABLE STRUCTURE
Tables are enclosed by the
.Sq TS
and
.Sq TE
.Xr roff 7
macros.
A table consists of an optional single line of table
.Sx Options
terminated by a semicolon, followed by one or more lines of
.Sx Layout
specifications terminated by a period, then
.Sx Data .
All input must be 7-bit ASCII.
Example:
.Bd -literal -offset indent
\&.TS
box tab(:);
c | c
| c | c.
1:2
3:4
\&.TE
.Ed
.Pp
Table data is
.Em pre-processed ,
that is, data rows are parsed then inserted into the underlying stream
of input data.
This allows data rows to be interspersed by arbitrary
.Xr roff 7 ,
.Xr mdoc 7 ,
and
.Xr man 7
macros such as
.Bd -literal -offset indent
\&.TS
tab(:);
c c c.
1:2:3
\&.Ao
3:2:1
\&.Ac
\&.TE
.Ed
.Pp
in the case of
.Xr mdoc 7
or
.Bd -literal -offset indent
\&.TS
tab(:);
c c c.
\&.ds ab 2
1:\e*(ab:3
\&.I
3:2:1
\&.TE
.Ed
.Pp
in the case of
.Xr man 7 .
.Ss Options
The first line of a table consists of space-separated option keys and
modifiers terminated by a semicolon.
If the first line does not have a terminating semicolon, it is assumed
that no options are specified and instead a
.Sx Layout
is processed.
Some options accept arguments enclosed by parenthesis.
The following case-insensitive options are available:
.Bl -tag -width Ds
.It Cm center
This option is not supported by
.Xr mandoc 1 .
This may also be invoked with
.Cm centre .
.It Cm delim
Accepts a two-character argument.
This option is not supported by
.Xr mandoc 1 .
.It Cm expand
This option is not supported by
.Xr mandoc 1 .
.It Cm box
Draw a single-line box around the table.
This may also be invoked with
.Cm frame .
.It Cm doublebox
Draw a double-line box around the table.
This may also be invoked with
.Cm doubleframe .
.It Cm allbox
This option is not supported by
.Xr mandoc 1 .
.It Cm tab
Accepts a single-character argument.
This character is used as a delimiter between data cells, which otherwise
defaults to the tab character.
.It Cm linesize
Accepts a natural number (all digits).
This option is not supported by
.Xr mandoc 1 .
.It Cm nokeep
This option is not supported by
.Xr mandoc 1 .
.It Cm decimalpoint
Accepts a single-character argument.
This character will be used as the decimal point with the
.Cm n
layout key.
.It Cm nospaces
This option is not supported by
.Xr mandoc 1 .
.El
.Ss Layout
The table layout follows
.Sx Options
or a
.Sq \&T&
macro invocation.
Layout specifies how data rows are displayed on output.
Each layout line corresponds to a line of data; the last layout line
applies to all remaining data lines.
Layout lines may also be separated by a comma.
Each layout cell consists of one of the following case-insensitive keys:
.Bl -tag -width Ds
.It Cm c
Centre a literal string within its column.
.It Cm r
Right-justify a literal string within its column.
.It Cm l
Left-justify a literal string within its column.
.It Cm n
Justify a number around its last decimal point.
If the decimal point is not found on the number, it's assumed to trail
the number.
.It Cm s
Horizontally span columns from the last
.No non- Ns Cm s
data cell.
It is an error if spanning columns follow a
.Cm \-
or
.Cm \(ba
cell, or come first.
This option is not supported by
.Xr mandoc 1 .
.It Cm a
Left-justify a literal string and pad with one space.
.It Cm ^
Vertically span rows from the last
.No non- Ns Cm ^
data cell.
It is an error to invoke a vertical span on the first layout row.
Unlike a horizontal spanner, you must specify an empty cell (if it not
empty, the data is discarded) in the corresponding data cell.
.It Cm \-
Replace the data cell (its contents will be lost) with a single
horizontal line.
This may also be invoked with
.Cm _ .
.It Cm =
Replace the data cell (its contents will be lost) with a double
horizontal line.
.It Cm \(ba
Emit a vertical bar instead of data.
.It Cm \(ba\(ba
Emit a double-vertical bar instead of data.
.El
.Pp
Keys may be followed by a set of modifiers.
A modifier is either a modifier key or a natural number for specifying
the minimum width of a column.
The following case-insensitive modifier keys are available:
.Cm z ,
.Cm u ,
.Cm e ,
.Cm t ,
.Cm d ,
.Cm b ,
.Cm i ,
.Cm r ,
and
.Cm f
.Po
followed by
.Cm b ,
.Cm i ,
.Cm r ,
.Cm 3 ,
.Cm 2 ,
or
.Cm 1
.Pc .
All of these are ignored by
.Xr mandoc 1 .
.Pp
For example, the following layout specifies a centre-justified column of
minimum width 10, followed by vertical bar, followed by a left-justified
column of minimum width 10, another vertical bar, then a column
justified about the decimal point in numbers:
.Pp
.Dl c10 | l10 | n
.Ss Data
The data section follows the last layout row.
By default, cells in a data section are delimited by a tab.
This behaviour may be changed with the
.Cm tab
option.
If
.Cm _
or
.Cm =
is specified, a single or double line, respectively, is drawn across the
data field.
If
.Cm \e-
or
.Cm \e=
is specified, a line is drawn within the data field (i.e. terminating
within the cell and not draw to the border).
If the last cell of a line is
.Cm T{ ,
all subsequent lines are included as part of the cell until
.Cm T}
is specified as its own data cell.
It may then be followed by a tab
.Pq or as designated by Cm tab
or an end-of-line to terminate the row.
.Sh COMPATIBILITY
This section documents compatibility between mandoc and other
.Nm
implementations, at this time limited to GNU tbl.
.Pp
.Bl -dash -compact
.It
In GNU tbl, comments and macros are disallowed prior to the data block
of a table.
The
.Xr mandoc 1
implementation allows them.
.El
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr man 7 ,
.Xr mandoc_char 7 ,
.Xr mdoc 7 ,
.Xr roff 7
.Rs
.%A M. E. Lesk
.%T Tbl\(emA Program to Format Tables
.%D June 11, 1976
.Re
.Sh HISTORY
The tbl utility, a preprocessor for troff, was originally written by M.
E. Lesk at Bell Labs in 1975.
The GNU reimplementation of tbl, part of the groff package, was released
in 1990 by James Clark.
A standalone tbl implementation was written by Kristaps Dzonsons in
2010.
This formed the basis of the implementation that is part of the
.Xr mandoc 1
utility.
.Sh AUTHORS
This
.Nm
reference was written by
.An Kristaps Dzonsons ,
.Mt kristaps@bsd.lv .

175
contrib/mdocml/tbl.c Normal file
View File

@ -0,0 +1,175 @@
/* $Id: tbl.c,v 1.26 2011/07/25 15:37:00 kristaps Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 <time.h>
#include "mandoc.h"
#include "libmandoc.h"
#include "libroff.h"
enum rofferr
tbl_read(struct tbl_node *tbl, int ln, const char *p, int offs)
{
int len;
const char *cp;
cp = &p[offs];
len = (int)strlen(cp);
/*
* If we're in the options section and we don't have a
* terminating semicolon, assume we've moved directly into the
* layout section. No need to report a warning: this is,
* apparently, standard behaviour.
*/
if (TBL_PART_OPTS == tbl->part && len)
if (';' != cp[len - 1])
tbl->part = TBL_PART_LAYOUT;
/* Now process each logical section of the table. */
switch (tbl->part) {
case (TBL_PART_OPTS):
return(tbl_option(tbl, ln, p) ? ROFF_IGN : ROFF_ERR);
case (TBL_PART_LAYOUT):
return(tbl_layout(tbl, ln, p) ? ROFF_IGN : ROFF_ERR);
case (TBL_PART_CDATA):
return(tbl_cdata(tbl, ln, p) ? ROFF_TBL : ROFF_IGN);
default:
break;
}
/*
* This only returns zero if the line is empty, so we ignore it
* and continue on.
*/
return(tbl_data(tbl, ln, p) ? ROFF_TBL : ROFF_IGN);
}
struct tbl_node *
tbl_alloc(int pos, int line, struct mparse *parse)
{
struct tbl_node *p;
p = mandoc_calloc(1, sizeof(struct tbl_node));
p->line = line;
p->pos = pos;
p->parse = parse;
p->part = TBL_PART_OPTS;
p->opts.tab = '\t';
p->opts.linesize = 12;
p->opts.decimal = '.';
return(p);
}
void
tbl_free(struct tbl_node *p)
{
struct tbl_row *rp;
struct tbl_cell *cp;
struct tbl_span *sp;
struct tbl_dat *dp;
struct tbl_head *hp;
while (NULL != (rp = p->first_row)) {
p->first_row = rp->next;
while (rp->first) {
cp = rp->first;
rp->first = cp->next;
free(cp);
}
free(rp);
}
while (NULL != (sp = p->first_span)) {
p->first_span = sp->next;
while (sp->first) {
dp = sp->first;
sp->first = dp->next;
if (dp->string)
free(dp->string);
free(dp);
}
free(sp);
}
while (NULL != (hp = p->first_head)) {
p->first_head = hp->next;
free(hp);
}
free(p);
}
void
tbl_restart(int line, int pos, struct tbl_node *tbl)
{
if (TBL_PART_CDATA == tbl->part)
mandoc_msg(MANDOCERR_TBLBLOCK, tbl->parse,
tbl->line, tbl->pos, NULL);
tbl->part = TBL_PART_LAYOUT;
tbl->line = line;
tbl->pos = pos;
if (NULL == tbl->first_span || NULL == tbl->first_span->first)
mandoc_msg(MANDOCERR_TBLNODATA, tbl->parse,
tbl->line, tbl->pos, NULL);
}
const struct tbl_span *
tbl_span(struct tbl_node *tbl)
{
struct tbl_span *span;
assert(tbl);
span = tbl->current_span ? tbl->current_span->next
: tbl->first_span;
if (span)
tbl->current_span = span;
return(span);
}
void
tbl_end(struct tbl_node **tblp)
{
struct tbl_node *tbl;
tbl = *tblp;
*tblp = NULL;
if (NULL == tbl->first_span || NULL == tbl->first_span->first)
mandoc_msg(MANDOCERR_TBLNODATA, tbl->parse,
tbl->line, tbl->pos, NULL);
if (tbl->last_span)
tbl->last_span->flags |= TBL_SPAN_LAST;
if (TBL_PART_CDATA == tbl->part)
mandoc_msg(MANDOCERR_TBLBLOCK, tbl->parse,
tbl->line, tbl->pos, NULL);
}

276
contrib/mdocml/tbl_data.c Normal file
View File

@ -0,0 +1,276 @@
/* $Id: tbl_data.c,v 1.24 2011/03/20 16:02:05 kristaps Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 <time.h>
#include "mandoc.h"
#include "libmandoc.h"
#include "libroff.h"
static int data(struct tbl_node *, struct tbl_span *,
int, const char *, int *);
static struct tbl_span *newspan(struct tbl_node *, int,
struct tbl_row *);
static int
data(struct tbl_node *tbl, struct tbl_span *dp,
int ln, const char *p, int *pos)
{
struct tbl_dat *dat;
struct tbl_cell *cp;
int sv, spans;
cp = NULL;
if (dp->last && dp->last->layout)
cp = dp->last->layout->next;
else if (NULL == dp->last)
cp = dp->layout->first;
/*
* Skip over spanners and vertical lines to data formats, since
* we want to match data with data layout cells in the header.
*/
while (cp && (TBL_CELL_VERT == cp->pos ||
TBL_CELL_DVERT == cp->pos ||
TBL_CELL_SPAN == cp->pos))
cp = cp->next;
/*
* Stop processing when we reach the end of the available layout
* cells. This means that we have extra input.
*/
if (NULL == cp) {
mandoc_msg(MANDOCERR_TBLEXTRADAT,
tbl->parse, ln, *pos, NULL);
/* Skip to the end... */
while (p[*pos])
(*pos)++;
return(1);
}
dat = mandoc_calloc(1, sizeof(struct tbl_dat));
dat->layout = cp;
dat->pos = TBL_DATA_NONE;
assert(TBL_CELL_SPAN != cp->pos);
for (spans = 0, cp = cp->next; cp; cp = cp->next)
if (TBL_CELL_SPAN == cp->pos)
spans++;
else
break;
dat->spans = spans;
if (dp->last) {
dp->last->next = dat;
dp->last = dat;
} else
dp->last = dp->first = dat;
sv = *pos;
while (p[*pos] && p[*pos] != tbl->opts.tab)
(*pos)++;
/*
* Check for a continued-data scope opening. This consists of a
* trailing `T{' at the end of the line. Subsequent lines,
* until a standalone `T}', are included in our cell.
*/
if (*pos - sv == 2 && 'T' == p[sv] && '{' == p[sv + 1]) {
tbl->part = TBL_PART_CDATA;
return(0);
}
assert(*pos - sv >= 0);
dat->string = mandoc_malloc((size_t)(*pos - sv + 1));
memcpy(dat->string, &p[sv], (size_t)(*pos - sv));
dat->string[*pos - sv] = '\0';
if (p[*pos])
(*pos)++;
if ( ! strcmp(dat->string, "_"))
dat->pos = TBL_DATA_HORIZ;
else if ( ! strcmp(dat->string, "="))
dat->pos = TBL_DATA_DHORIZ;
else if ( ! strcmp(dat->string, "\\_"))
dat->pos = TBL_DATA_NHORIZ;
else if ( ! strcmp(dat->string, "\\="))
dat->pos = TBL_DATA_NDHORIZ;
else
dat->pos = TBL_DATA_DATA;
if (TBL_CELL_HORIZ == dat->layout->pos ||
TBL_CELL_DHORIZ == dat->layout->pos ||
TBL_CELL_DOWN == dat->layout->pos)
if (TBL_DATA_DATA == dat->pos && '\0' != *dat->string)
mandoc_msg(MANDOCERR_TBLIGNDATA,
tbl->parse, ln, sv, NULL);
return(1);
}
/* ARGSUSED */
int
tbl_cdata(struct tbl_node *tbl, int ln, const char *p)
{
struct tbl_dat *dat;
size_t sz;
int pos;
pos = 0;
dat = tbl->last_span->last;
if (p[pos] == 'T' && p[pos + 1] == '}') {
pos += 2;
if (p[pos] == tbl->opts.tab) {
tbl->part = TBL_PART_DATA;
pos++;
return(data(tbl, tbl->last_span, ln, p, &pos));
} else if ('\0' == p[pos]) {
tbl->part = TBL_PART_DATA;
return(1);
}
/* Fallthrough: T} is part of a word. */
}
dat->pos = TBL_DATA_DATA;
if (dat->string) {
sz = strlen(p) + strlen(dat->string) + 2;
dat->string = mandoc_realloc(dat->string, sz);
strlcat(dat->string, " ", sz);
strlcat(dat->string, p, sz);
} else
dat->string = mandoc_strdup(p);
if (TBL_CELL_DOWN == dat->layout->pos)
mandoc_msg(MANDOCERR_TBLIGNDATA,
tbl->parse, ln, pos, NULL);
return(0);
}
static struct tbl_span *
newspan(struct tbl_node *tbl, int line, struct tbl_row *rp)
{
struct tbl_span *dp;
dp = mandoc_calloc(1, sizeof(struct tbl_span));
dp->line = line;
dp->tbl = &tbl->opts;
dp->layout = rp;
dp->head = tbl->first_head;
if (tbl->last_span) {
tbl->last_span->next = dp;
tbl->last_span = dp;
} else {
tbl->last_span = tbl->first_span = dp;
tbl->current_span = NULL;
dp->flags |= TBL_SPAN_FIRST;
}
return(dp);
}
int
tbl_data(struct tbl_node *tbl, int ln, const char *p)
{
struct tbl_span *dp;
struct tbl_row *rp;
int pos;
pos = 0;
if ('\0' == p[pos]) {
mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, pos, NULL);
return(0);
}
/*
* Choose a layout row: take the one following the last parsed
* span's. If that doesn't exist, use the last parsed span's.
* If there's no last parsed span, use the first row. Lastly,
* if the last span was a horizontal line, use the same layout
* (it doesn't "consume" the layout).
*/
if (tbl->last_span) {
assert(tbl->last_span->layout);
if (tbl->last_span->pos == TBL_SPAN_DATA) {
for (rp = tbl->last_span->layout->next;
rp && rp->first; rp = rp->next) {
switch (rp->first->pos) {
case (TBL_CELL_HORIZ):
dp = newspan(tbl, ln, rp);
dp->pos = TBL_SPAN_HORIZ;
continue;
case (TBL_CELL_DHORIZ):
dp = newspan(tbl, ln, rp);
dp->pos = TBL_SPAN_DHORIZ;
continue;
default:
break;
}
break;
}
} else
rp = tbl->last_span->layout;
if (NULL == rp)
rp = tbl->last_span->layout;
} else
rp = tbl->first_row;
assert(rp);
dp = newspan(tbl, ln, rp);
if ( ! strcmp(p, "_")) {
dp->pos = TBL_SPAN_HORIZ;
return(1);
} else if ( ! strcmp(p, "=")) {
dp->pos = TBL_SPAN_DHORIZ;
return(1);
}
dp->pos = TBL_SPAN_DATA;
/* This returns 0 when TBL_PART_CDATA is entered. */
while ('\0' != p[pos])
if ( ! data(tbl, dp, ln, p, &pos))
return(0);
return(1);
}

151
contrib/mdocml/tbl_html.c Normal file
View File

@ -0,0 +1,151 @@
/* $Id: tbl_html.c,v 1.9 2011/09/18 14:14:15 schwarze Exp $ */
/*
* Copyright (c) 2011 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 "out.h"
#include "html.h"
static void html_tblopen(struct html *, const struct tbl_span *);
static size_t html_tbl_len(size_t, void *);
static size_t html_tbl_strlen(const char *, void *);
/* ARGSUSED */
static size_t
html_tbl_len(size_t sz, void *arg)
{
return(sz);
}
/* ARGSUSED */
static size_t
html_tbl_strlen(const char *p, void *arg)
{
return(strlen(p));
}
static void
html_tblopen(struct html *h, const struct tbl_span *sp)
{
const struct tbl_head *hp;
struct htmlpair tag;
struct roffsu su;
struct roffcol *col;
if (TBL_SPAN_FIRST & sp->flags) {
h->tbl.len = html_tbl_len;
h->tbl.slen = html_tbl_strlen;
tblcalc(&h->tbl, sp);
}
assert(NULL == h->tblt);
PAIR_CLASS_INIT(&tag, "tbl");
h->tblt = print_otag(h, TAG_TABLE, 1, &tag);
for (hp = sp->head; hp; hp = hp->next) {
bufinit(h);
col = &h->tbl.cols[hp->ident];
SCALE_HS_INIT(&su, col->width);
bufcat_su(h, "width", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_COL, 1, &tag);
}
print_otag(h, TAG_TBODY, 0, NULL);
}
void
print_tblclose(struct html *h)
{
assert(h->tblt);
print_tagq(h, h->tblt);
h->tblt = NULL;
}
void
print_tbl(struct html *h, const struct tbl_span *sp)
{
const struct tbl_head *hp;
const struct tbl_dat *dp;
struct htmlpair tag;
struct tag *tt;
/* Inhibit printing of spaces: we do padding ourselves. */
if (NULL == h->tblt)
html_tblopen(h, sp);
assert(h->tblt);
h->flags |= HTML_NONOSPACE;
h->flags |= HTML_NOSPACE;
tt = print_otag(h, TAG_TR, 0, NULL);
switch (sp->pos) {
case (TBL_SPAN_HORIZ):
/* FALLTHROUGH */
case (TBL_SPAN_DHORIZ):
PAIR_INIT(&tag, ATTR_COLSPAN, "0");
print_otag(h, TAG_TD, 1, &tag);
break;
default:
dp = sp->first;
for (hp = sp->head; hp; hp = hp->next) {
print_stagq(h, tt);
print_otag(h, TAG_TD, 0, NULL);
switch (hp->pos) {
case (TBL_HEAD_VERT):
/* FALLTHROUGH */
case (TBL_HEAD_DVERT):
continue;
case (TBL_HEAD_DATA):
if (NULL == dp)
break;
if (TBL_CELL_DOWN != dp->layout->pos)
if (dp->string)
print_text(h, dp->string);
dp = dp->next;
break;
}
}
break;
}
print_tagq(h, tt);
h->flags &= ~HTML_NONOSPACE;
if (TBL_SPAN_LAST & sp->flags) {
assert(h->tbl.cols);
free(h->tbl.cols);
h->tbl.cols = NULL;
print_tblclose(h);
}
}

472
contrib/mdocml/tbl_layout.c Normal file
View File

@ -0,0 +1,472 @@
/* $Id: tbl_layout.c,v 1.22 2011/09/18 14:14:15 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 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 <time.h>
#include "mandoc.h"
#include "libmandoc.h"
#include "libroff.h"
struct tbl_phrase {
char name;
enum tbl_cellt key;
};
/*
* FIXME: we can make this parse a lot nicer by, when an error is
* encountered in a layout key, bailing to the next key (i.e. to the
* next whitespace then continuing).
*/
#define KEYS_MAX 11
static const struct tbl_phrase keys[KEYS_MAX] = {
{ 'c', TBL_CELL_CENTRE },
{ 'r', TBL_CELL_RIGHT },
{ 'l', TBL_CELL_LEFT },
{ 'n', TBL_CELL_NUMBER },
{ 's', TBL_CELL_SPAN },
{ 'a', TBL_CELL_LONG },
{ '^', TBL_CELL_DOWN },
{ '-', TBL_CELL_HORIZ },
{ '_', TBL_CELL_HORIZ },
{ '=', TBL_CELL_DHORIZ },
{ '|', TBL_CELL_VERT }
};
static int mods(struct tbl_node *, struct tbl_cell *,
int, const char *, int *);
static int cell(struct tbl_node *, struct tbl_row *,
int, const char *, int *);
static void row(struct tbl_node *, int, const char *, int *);
static struct tbl_cell *cell_alloc(struct tbl_node *,
struct tbl_row *, enum tbl_cellt);
static void head_adjust(const struct tbl_cell *,
struct tbl_head *);
static int
mods(struct tbl_node *tbl, struct tbl_cell *cp,
int ln, const char *p, int *pos)
{
char buf[5];
int i;
/* Not all types accept modifiers. */
switch (cp->pos) {
case (TBL_CELL_DOWN):
/* FALLTHROUGH */
case (TBL_CELL_HORIZ):
/* FALLTHROUGH */
case (TBL_CELL_DHORIZ):
/* FALLTHROUGH */
case (TBL_CELL_VERT):
/* FALLTHROUGH */
case (TBL_CELL_DVERT):
return(1);
default:
break;
}
mod:
/*
* XXX: since, at least for now, modifiers are non-conflicting
* (are separable by value, regardless of position), we let
* modifiers come in any order. The existing tbl doesn't let
* this happen.
*/
switch (p[*pos]) {
case ('\0'):
/* FALLTHROUGH */
case (' '):
/* FALLTHROUGH */
case ('\t'):
/* FALLTHROUGH */
case (','):
/* FALLTHROUGH */
case ('.'):
return(1);
default:
break;
}
/* Throw away parenthesised expression. */
if ('(' == p[*pos]) {
(*pos)++;
while (p[*pos] && ')' != p[*pos])
(*pos)++;
if (')' == p[*pos]) {
(*pos)++;
goto mod;
}
mandoc_msg(MANDOCERR_TBLLAYOUT,
tbl->parse, ln, *pos, NULL);
return(0);
}
/* Parse numerical spacing from modifier string. */
if (isdigit((unsigned char)p[*pos])) {
for (i = 0; i < 4; i++) {
if ( ! isdigit((unsigned char)p[*pos + i]))
break;
buf[i] = p[*pos + i];
}
buf[i] = '\0';
/* No greater than 4 digits. */
if (4 == i) {
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
return(0);
}
*pos += i;
cp->spacing = (size_t)atoi(buf);
goto mod;
/* NOTREACHED */
}
/* TODO: GNU has many more extensions. */
switch (tolower((unsigned char)p[(*pos)++])) {
case ('z'):
cp->flags |= TBL_CELL_WIGN;
goto mod;
case ('u'):
cp->flags |= TBL_CELL_UP;
goto mod;
case ('e'):
cp->flags |= TBL_CELL_EQUAL;
goto mod;
case ('t'):
cp->flags |= TBL_CELL_TALIGN;
goto mod;
case ('d'):
cp->flags |= TBL_CELL_BALIGN;
goto mod;
case ('w'): /* XXX for now, ignore minimal column width */
goto mod;
case ('f'):
break;
case ('r'):
/* FALLTHROUGH */
case ('b'):
/* FALLTHROUGH */
case ('i'):
(*pos)--;
break;
default:
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos - 1, NULL);
return(0);
}
switch (tolower((unsigned char)p[(*pos)++])) {
case ('3'):
/* FALLTHROUGH */
case ('b'):
cp->flags |= TBL_CELL_BOLD;
goto mod;
case ('2'):
/* FALLTHROUGH */
case ('i'):
cp->flags |= TBL_CELL_ITALIC;
goto mod;
case ('1'):
/* FALLTHROUGH */
case ('r'):
goto mod;
default:
break;
}
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos - 1, NULL);
return(0);
}
static int
cell(struct tbl_node *tbl, struct tbl_row *rp,
int ln, const char *p, int *pos)
{
int i;
enum tbl_cellt c;
/* Parse the column position (`r', `R', `|', ...). */
for (i = 0; i < KEYS_MAX; i++)
if (tolower((unsigned char)p[*pos]) == keys[i].name)
break;
if (KEYS_MAX == i) {
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
return(0);
}
c = keys[i].key;
/*
* If a span cell is found first, raise a warning and abort the
* parse. If a span cell is found and the last layout element
* isn't a "normal" layout, bail.
*
* FIXME: recover from this somehow?
*/
if (TBL_CELL_SPAN == c) {
if (NULL == rp->first) {
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
return(0);
} else if (rp->last)
switch (rp->last->pos) {
case (TBL_CELL_VERT):
case (TBL_CELL_DVERT):
case (TBL_CELL_HORIZ):
case (TBL_CELL_DHORIZ):
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
ln, *pos, NULL);
return(0);
default:
break;
}
}
/*
* If a vertical spanner is found, we may not be in the first
* row.
*/
if (TBL_CELL_DOWN == c && rp == tbl->first_row) {
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL);
return(0);
}
(*pos)++;
/* Extra check for the double-vertical. */
if (TBL_CELL_VERT == c && '|' == p[*pos]) {
(*pos)++;
c = TBL_CELL_DVERT;
}
/* Disallow adjacent spacers. */
if (rp->last && (TBL_CELL_VERT == c || TBL_CELL_DVERT == c) &&
(TBL_CELL_VERT == rp->last->pos ||
TBL_CELL_DVERT == rp->last->pos)) {
mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL);
return(0);
}
/* Allocate cell then parse its modifiers. */
return(mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos));
}
static void
row(struct tbl_node *tbl, int ln, const char *p, int *pos)
{
struct tbl_row *rp;
row: /*
* EBNF describing this section:
*
* row ::= row_list [:space:]* [.]?[\n]
* row_list ::= [:space:]* row_elem row_tail
* row_tail ::= [:space:]*[,] row_list |
* epsilon
* row_elem ::= [\t\ ]*[:alpha:]+
*/
rp = mandoc_calloc(1, sizeof(struct tbl_row));
if (tbl->last_row) {
tbl->last_row->next = rp;
tbl->last_row = rp;
} else
tbl->last_row = tbl->first_row = rp;
cell:
while (isspace((unsigned char)p[*pos]))
(*pos)++;
/* Safely exit layout context. */
if ('.' == p[*pos]) {
tbl->part = TBL_PART_DATA;
if (NULL == tbl->first_row)
mandoc_msg(MANDOCERR_TBLNOLAYOUT, tbl->parse,
ln, *pos, NULL);
(*pos)++;
return;
}
/* End (and possibly restart) a row. */
if (',' == p[*pos]) {
(*pos)++;
goto row;
} else if ('\0' == p[*pos])
return;
if ( ! cell(tbl, rp, ln, p, pos))
return;
goto cell;
/* NOTREACHED */
}
int
tbl_layout(struct tbl_node *tbl, int ln, const char *p)
{
int pos;
pos = 0;
row(tbl, ln, p, &pos);
/* Always succeed. */
return(1);
}
static struct tbl_cell *
cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
{
struct tbl_cell *p, *pp;
struct tbl_head *h, *hp;
p = mandoc_calloc(1, sizeof(struct tbl_cell));
if (NULL != (pp = rp->last)) {
rp->last->next = p;
rp->last = p;
} else
rp->last = rp->first = p;
p->pos = pos;
/*
* This is a little bit complicated. Here we determine the
* header the corresponds to a cell. We add headers dynamically
* when need be or re-use them, otherwise. As an example, given
* the following:
*
* 1 c || l
* 2 | c | l
* 3 l l
* 3 || c | l |.
*
* We first add the new headers (as there are none) in (1); then
* in (2) we insert the first spanner (as it doesn't match up
* with the header); then we re-use the prior data headers,
* skipping over the spanners; then we re-use everything and add
* a last spanner. Note that VERT headers are made into DVERT
* ones.
*/
h = pp ? pp->head->next : tbl->first_head;
if (h) {
/* Re-use data header. */
if (TBL_HEAD_DATA == h->pos &&
(TBL_CELL_VERT != p->pos &&
TBL_CELL_DVERT != p->pos)) {
p->head = h;
return(p);
}
/* Re-use spanner header. */
if (TBL_HEAD_DATA != h->pos &&
(TBL_CELL_VERT == p->pos ||
TBL_CELL_DVERT == p->pos)) {
head_adjust(p, h);
p->head = h;
return(p);
}
/* Right-shift headers with a new spanner. */
if (TBL_HEAD_DATA == h->pos &&
(TBL_CELL_VERT == p->pos ||
TBL_CELL_DVERT == p->pos)) {
hp = mandoc_calloc(1, sizeof(struct tbl_head));
hp->ident = tbl->opts.cols++;
hp->prev = h->prev;
if (h->prev)
h->prev->next = hp;
if (h == tbl->first_head)
tbl->first_head = hp;
h->prev = hp;
hp->next = h;
head_adjust(p, hp);
p->head = hp;
return(p);
}
if (NULL != (h = h->next)) {
head_adjust(p, h);
p->head = h;
return(p);
}
/* Fall through to default case... */
}
hp = mandoc_calloc(1, sizeof(struct tbl_head));
hp->ident = tbl->opts.cols++;
if (tbl->last_head) {
hp->prev = tbl->last_head;
tbl->last_head->next = hp;
tbl->last_head = hp;
} else
tbl->last_head = tbl->first_head = hp;
head_adjust(p, hp);
p->head = hp;
return(p);
}
static void
head_adjust(const struct tbl_cell *cellp, struct tbl_head *head)
{
if (TBL_CELL_VERT != cellp->pos &&
TBL_CELL_DVERT != cellp->pos) {
head->pos = TBL_HEAD_DATA;
return;
}
if (TBL_CELL_VERT == cellp->pos)
if (TBL_HEAD_DVERT != head->pos)
head->pos = TBL_HEAD_VERT;
if (TBL_CELL_DVERT == cellp->pos)
head->pos = TBL_HEAD_DVERT;
}

270
contrib/mdocml/tbl_opts.c Normal file
View File

@ -0,0 +1,270 @@
/* $Id: tbl_opts.c,v 1.12 2011/09/18 14:14:15 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "libmandoc.h"
#include "libroff.h"
enum tbl_ident {
KEY_CENTRE = 0,
KEY_DELIM,
KEY_EXPAND,
KEY_BOX,
KEY_DBOX,
KEY_ALLBOX,
KEY_TAB,
KEY_LINESIZE,
KEY_NOKEEP,
KEY_DPOINT,
KEY_NOSPACE,
KEY_FRAME,
KEY_DFRAME,
KEY_MAX
};
struct tbl_phrase {
const char *name;
int key;
enum tbl_ident ident;
};
/* Handle Commonwealth/American spellings. */
#define KEY_MAXKEYS 14
/* Maximum length of key name string. */
#define KEY_MAXNAME 13
/* Maximum length of key number size. */
#define KEY_MAXNUMSZ 10
static const struct tbl_phrase keys[KEY_MAXKEYS] = {
{ "center", TBL_OPT_CENTRE, KEY_CENTRE},
{ "centre", TBL_OPT_CENTRE, KEY_CENTRE},
{ "delim", 0, KEY_DELIM},
{ "expand", TBL_OPT_EXPAND, KEY_EXPAND},
{ "box", TBL_OPT_BOX, KEY_BOX},
{ "doublebox", TBL_OPT_DBOX, KEY_DBOX},
{ "allbox", TBL_OPT_ALLBOX, KEY_ALLBOX},
{ "frame", TBL_OPT_BOX, KEY_FRAME},
{ "doubleframe", TBL_OPT_DBOX, KEY_DFRAME},
{ "tab", 0, KEY_TAB},
{ "linesize", 0, KEY_LINESIZE},
{ "nokeep", TBL_OPT_NOKEEP, KEY_NOKEEP},
{ "decimalpoint", 0, KEY_DPOINT},
{ "nospaces", TBL_OPT_NOSPACE, KEY_NOSPACE},
};
static int arg(struct tbl_node *, int,
const char *, int *, enum tbl_ident);
static void opt(struct tbl_node *, int,
const char *, int *);
static int
arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key)
{
int i;
char buf[KEY_MAXNUMSZ];
while (isspace((unsigned char)p[*pos]))
(*pos)++;
/* Arguments always begin with a parenthesis. */
if ('(' != p[*pos]) {
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos, NULL);
return(0);
}
(*pos)++;
/*
* The arguments can be ANY value, so we can't just stop at the
* next close parenthesis (the argument can be a closed
* parenthesis itself).
*/
switch (key) {
case (KEY_DELIM):
if ('\0' == p[(*pos)++]) {
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos - 1, NULL);
return(0);
}
if ('\0' == p[(*pos)++]) {
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos - 1, NULL);
return(0);
}
break;
case (KEY_TAB):
if ('\0' != (tbl->opts.tab = p[(*pos)++]))
break;
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos - 1, NULL);
return(0);
case (KEY_LINESIZE):
for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) {
buf[i] = p[*pos];
if ( ! isdigit((unsigned char)buf[i]))
break;
}
if (i < KEY_MAXNUMSZ) {
buf[i] = '\0';
tbl->opts.linesize = atoi(buf);
break;
}
mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL);
return(0);
case (KEY_DPOINT):
if ('\0' != (tbl->opts.decimal = p[(*pos)++]))
break;
mandoc_msg(MANDOCERR_TBL, tbl->parse,
ln, *pos - 1, NULL);
return(0);
default:
abort();
/* NOTREACHED */
}
/* End with a close parenthesis. */
if (')' == p[(*pos)++])
return(1);
mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos - 1, NULL);
return(0);
}
static void
opt(struct tbl_node *tbl, int ln, const char *p, int *pos)
{
int i, sv;
char buf[KEY_MAXNAME];
/*
* Parse individual options from the stream as surrounded by
* this goto. Each pass through the routine parses out a single
* option and registers it. Option arguments are processed in
* the arg() function.
*/
again: /*
* EBNF describing this section:
*
* options ::= option_list [:space:]* [;][\n]
* option_list ::= option option_tail
* option_tail ::= [:space:]+ option_list |
* ::= epsilon
* option ::= [:alpha:]+ args
* args ::= [:space:]* [(] [:alpha:]+ [)]
*/
while (isspace((unsigned char)p[*pos]))
(*pos)++;
/* Safe exit point. */
if (';' == p[*pos])
return;
/* Copy up to first non-alpha character. */
for (sv = *pos, i = 0; i < KEY_MAXNAME; i++, (*pos)++) {
buf[i] = (char)tolower((unsigned char)p[*pos]);
if ( ! isalpha((unsigned char)buf[i]))
break;
}
/* Exit if buffer is empty (or overrun). */
if (KEY_MAXNAME == i || 0 == i) {
mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL);
return;
}
buf[i] = '\0';
while (isspace((unsigned char)p[*pos]))
(*pos)++;
/*
* Look through all of the available keys to find one that
* matches the input. FIXME: hashtable this.
*/
for (i = 0; i < KEY_MAXKEYS; i++) {
if (strcmp(buf, keys[i].name))
continue;
/*
* Note: this is more difficult to recover from, as we
* can be anywhere in the option sequence and it's
* harder to jump to the next. Meanwhile, just bail out
* of the sequence altogether.
*/
if (keys[i].key)
tbl->opts.opts |= keys[i].key;
else if ( ! arg(tbl, ln, p, pos, keys[i].ident))
return;
break;
}
/*
* Allow us to recover from bad options by continuing to another
* parse sequence.
*/
if (KEY_MAXKEYS == i)
mandoc_msg(MANDOCERR_TBLOPT, tbl->parse, ln, sv, NULL);
goto again;
/* NOTREACHED */
}
int
tbl_option(struct tbl_node *tbl, int ln, const char *p)
{
int pos;
/*
* Table options are always on just one line, so automatically
* switch into the next input mode here.
*/
tbl->part = TBL_PART_LAYOUT;
pos = 0;
opt(tbl, ln, p, &pos);
/* Always succeed. */
return(1);
}

444
contrib/mdocml/tbl_term.c Normal file
View File

@ -0,0 +1,444 @@
/* $Id: tbl_term.c,v 1.21 2011/09/20 23:05:49 schwarze Exp $ */
/*
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 "out.h"
#include "term.h"
static size_t term_tbl_len(size_t, void *);
static size_t term_tbl_strlen(const char *, void *);
static void tbl_char(struct termp *, char, size_t);
static void tbl_data(struct termp *, const struct tbl *,
const struct tbl_dat *,
const struct roffcol *);
static size_t tbl_rulewidth(struct termp *, const struct tbl_head *);
static void tbl_hframe(struct termp *, const struct tbl_span *, int);
static void tbl_literal(struct termp *, const struct tbl_dat *,
const struct roffcol *);
static void tbl_number(struct termp *, const struct tbl *,
const struct tbl_dat *,
const struct roffcol *);
static void tbl_hrule(struct termp *, const struct tbl_span *);
static void tbl_vrule(struct termp *, const struct tbl_head *);
static size_t
term_tbl_strlen(const char *p, void *arg)
{
return(term_strlen((const struct termp *)arg, p));
}
static size_t
term_tbl_len(size_t sz, void *arg)
{
return(term_len((const struct termp *)arg, sz));
}
void
term_tbl(struct termp *tp, const struct tbl_span *sp)
{
const struct tbl_head *hp;
const struct tbl_dat *dp;
struct roffcol *col;
int spans;
size_t rmargin, maxrmargin;
rmargin = tp->rmargin;
maxrmargin = tp->maxrmargin;
tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
/* Inhibit printing of spaces: we do padding ourselves. */
tp->flags |= TERMP_NONOSPACE;
tp->flags |= TERMP_NOSPACE;
/*
* The first time we're invoked for a given table block,
* calculate the table widths and decimal positions.
*/
if (TBL_SPAN_FIRST & sp->flags) {
term_flushln(tp);
tp->tbl.len = term_tbl_len;
tp->tbl.slen = term_tbl_strlen;
tp->tbl.arg = tp;
tblcalc(&tp->tbl, sp);
}
/* Horizontal frame at the start of boxed tables. */
if (TBL_SPAN_FIRST & sp->flags) {
if (TBL_OPT_DBOX & sp->tbl->opts)
tbl_hframe(tp, sp, 1);
if (TBL_OPT_DBOX & sp->tbl->opts ||
TBL_OPT_BOX & sp->tbl->opts)
tbl_hframe(tp, sp, 0);
}
/* Vertical frame at the start of each row. */
if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
/*
* Now print the actual data itself depending on the span type.
* Spanner spans get a horizontal rule; data spanners have their
* data printed by matching data to header.
*/
switch (sp->pos) {
case (TBL_SPAN_HORIZ):
/* FALLTHROUGH */
case (TBL_SPAN_DHORIZ):
tbl_hrule(tp, sp);
break;
case (TBL_SPAN_DATA):
/* Iterate over template headers. */
dp = sp->first;
spans = 0;
for (hp = sp->head; hp; hp = hp->next) {
/*
* If the current data header is invoked during
* a spanner ("spans" > 0), don't emit anything
* at all.
*/
switch (hp->pos) {
case (TBL_HEAD_VERT):
/* FALLTHROUGH */
case (TBL_HEAD_DVERT):
if (spans <= 0)
tbl_vrule(tp, hp);
continue;
case (TBL_HEAD_DATA):
break;
}
if (--spans >= 0)
continue;
/*
* All cells get a leading blank, except the
* first one and those after double rulers.
*/
if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)
tbl_char(tp, ASCII_NBRSP, 1);
col = &tp->tbl.cols[hp->ident];
tbl_data(tp, sp->tbl, dp, col);
/* No trailing blanks. */
if (NULL == hp->next)
break;
/*
* Add another blank between cells,
* or two when there is no vertical ruler.
*/
tbl_char(tp, ASCII_NBRSP,
TBL_HEAD_VERT == hp->next->pos ||
TBL_HEAD_DVERT == hp->next->pos ? 1 : 2);
/*
* Go to the next data cell and assign the
* number of subsequent spans, if applicable.
*/
if (dp) {
spans = dp->spans;
dp = dp->next;
}
}
break;
}
/* Vertical frame at the end of each row. */
if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
term_flushln(tp);
/*
* If we're the last row, clean up after ourselves: clear the
* existing table configuration and set it to NULL.
*/
if (TBL_SPAN_LAST & sp->flags) {
if (TBL_OPT_DBOX & sp->tbl->opts ||
TBL_OPT_BOX & sp->tbl->opts)
tbl_hframe(tp, sp, 0);
if (TBL_OPT_DBOX & sp->tbl->opts)
tbl_hframe(tp, sp, 1);
assert(tp->tbl.cols);
free(tp->tbl.cols);
tp->tbl.cols = NULL;
}
tp->flags &= ~TERMP_NONOSPACE;
tp->rmargin = rmargin;
tp->maxrmargin = maxrmargin;
}
/*
* Horizontal rules extend across the entire table.
* Calculate the width by iterating over columns.
*/
static size_t
tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
{
size_t width;
width = tp->tbl.cols[hp->ident].width;
if (TBL_HEAD_DATA == hp->pos) {
/* Account for leading blanks. */
if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)
width++;
/* Account for trailing blanks. */
width++;
if (hp->next &&
TBL_HEAD_VERT != hp->next->pos &&
TBL_HEAD_DVERT != hp->next->pos)
width++;
}
return(width);
}
/*
* Rules inside the table can be single or double
* and have crossings with vertical rules marked with pluses.
*/
static void
tbl_hrule(struct termp *tp, const struct tbl_span *sp)
{
const struct tbl_head *hp;
char c;
c = '-';
if (TBL_SPAN_DHORIZ == sp->pos)
c = '=';
for (hp = sp->head; hp; hp = hp->next)
tbl_char(tp,
TBL_HEAD_DATA == hp->pos ? c : '+',
tbl_rulewidth(tp, hp));
}
/*
* Rules above and below the table are always single
* and have an additional plus at the beginning and end.
* For double frames, this function is called twice,
* and the outer one does not have crossings.
*/
static void
tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
{
const struct tbl_head *hp;
term_word(tp, "+");
for (hp = sp->head; hp; hp = hp->next)
tbl_char(tp,
outer || TBL_HEAD_DATA == hp->pos ? '-' : '+',
tbl_rulewidth(tp, hp));
term_word(tp, "+");
term_flushln(tp);
}
static void
tbl_data(struct termp *tp, const struct tbl *tbl,
const struct tbl_dat *dp,
const struct roffcol *col)
{
if (NULL == dp) {
tbl_char(tp, ASCII_NBRSP, col->width);
return;
}
assert(dp->layout);
switch (dp->pos) {
case (TBL_DATA_NONE):
tbl_char(tp, ASCII_NBRSP, col->width);
return;
case (TBL_DATA_HORIZ):
/* FALLTHROUGH */
case (TBL_DATA_NHORIZ):
tbl_char(tp, '-', col->width);
return;
case (TBL_DATA_NDHORIZ):
/* FALLTHROUGH */
case (TBL_DATA_DHORIZ):
tbl_char(tp, '=', col->width);
return;
default:
break;
}
switch (dp->layout->pos) {
case (TBL_CELL_HORIZ):
tbl_char(tp, '-', col->width);
break;
case (TBL_CELL_DHORIZ):
tbl_char(tp, '=', col->width);
break;
case (TBL_CELL_LONG):
/* FALLTHROUGH */
case (TBL_CELL_CENTRE):
/* FALLTHROUGH */
case (TBL_CELL_LEFT):
/* FALLTHROUGH */
case (TBL_CELL_RIGHT):
tbl_literal(tp, dp, col);
break;
case (TBL_CELL_NUMBER):
tbl_number(tp, tbl, dp, col);
break;
case (TBL_CELL_DOWN):
tbl_char(tp, ASCII_NBRSP, col->width);
break;
default:
abort();
/* NOTREACHED */
}
}
static void
tbl_vrule(struct termp *tp, const struct tbl_head *hp)
{
switch (hp->pos) {
case (TBL_HEAD_VERT):
term_word(tp, "|");
break;
case (TBL_HEAD_DVERT):
term_word(tp, "||");
break;
default:
break;
}
}
static void
tbl_char(struct termp *tp, char c, size_t len)
{
size_t i, sz;
char cp[2];
cp[0] = c;
cp[1] = '\0';
sz = term_strlen(tp, cp);
for (i = 0; i < len; i += sz)
term_word(tp, cp);
}
static void
tbl_literal(struct termp *tp, const struct tbl_dat *dp,
const struct roffcol *col)
{
size_t len, padl, padr;
assert(dp->string);
len = term_strlen(tp, dp->string);
padr = col->width > len ? col->width - len : 0;
padl = 0;
switch (dp->layout->pos) {
case (TBL_CELL_LONG):
padl = term_len(tp, 1);
padr = padr > padl ? padr - padl : 0;
break;
case (TBL_CELL_CENTRE):
if (2 > padr)
break;
padl = padr / 2;
padr -= padl;
break;
case (TBL_CELL_RIGHT):
padl = padr;
padr = 0;
break;
default:
break;
}
tbl_char(tp, ASCII_NBRSP, padl);
term_word(tp, dp->string);
tbl_char(tp, ASCII_NBRSP, padr);
}
static void
tbl_number(struct termp *tp, const struct tbl *tbl,
const struct tbl_dat *dp,
const struct roffcol *col)
{
char *cp;
char buf[2];
size_t sz, psz, ssz, d, padl;
int i;
/*
* See calc_data_number(). Left-pad by taking the offset of our
* and the maximum decimal; right-pad by the remaining amount.
*/
assert(dp->string);
sz = term_strlen(tp, dp->string);
buf[0] = tbl->decimal;
buf[1] = '\0';
psz = term_strlen(tp, buf);
if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
buf[1] = '\0';
for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
buf[0] = dp->string[i];
ssz += term_strlen(tp, buf);
}
d = ssz + psz;
} else
d = sz + psz;
padl = col->decimal - d;
tbl_char(tp, ASCII_NBRSP, padl);
term_word(tp, dp->string);
if (col->width > sz + padl)
tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
}

736
contrib/mdocml/term.c Normal file
View File

@ -0,0 +1,736 @@
/* $Id: term.c,v 1.201 2011/09/21 09:57:13 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
*
* 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 "out.h"
#include "term.h"
#include "main.h"
static void adjbuf(struct termp *p, int);
static void bufferc(struct termp *, char);
static void encode(struct termp *, const char *, size_t);
static void encode1(struct termp *, int);
void
term_free(struct termp *p)
{
if (p->buf)
free(p->buf);
if (p->symtab)
mchars_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);
}
/*
* 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_NOBREAK: this is the most important and is used when making
* columns. In short: don't print a newline and instead expect the
* next call to do the padding up to the start of the next column.
*
* - TERMP_TWOSPACE: make sure there is room for 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
* overrunning, 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 */
size_t dv; /* temporary for visual pos calculations */
int j; /* temporary loop index for p->buf */
int jhy; /* last hyph before overflow w/r/t j */
size_t maxvis; /* output position of visible boundary */
size_t mmax; /* used in calculating bp */
/*
* 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->rmargin >= p->offset);
dv = p->rmargin - p->offset;
maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
dv = p->maxrmargin - p->offset;
mmax = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
/*
* Calculate the required amount of padding.
*/
vbl = p->offset + p->overstep > p->viscol ?
p->offset + p->overstep - p->viscol : 0;
vis = vend = 0;
i = 0;
while (i < p->col) {
/*
* Handle literal tab characters: collapse all
* subsequent tabs into a single huge set of spaces.
*/
while (i < p->col && '\t' == p->buf[i]) {
vend = (vis / p->tabwidth + 1) * p->tabwidth;
vbl += vend - vis;
vis = vend;
i++;
}
/*
* 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).
*/
for (j = i, jhy = 0; j < p->col; j++) {
if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
break;
/* Back over the the last printed character. */
if (8 == p->buf[j]) {
assert(j);
vend -= (*p->width)(p, p->buf[j - 1]);
continue;
}
/* Regular word. */
/* Break at the hyphen point if we overrun. */
if (vend > vis && vend < bp &&
ASCII_HYPH == p->buf[j])
jhy = j;
vend += (*p->width)(p, p->buf[j]);
}
/*
* 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);
p->viscol = 0;
if (TERMP_NOBREAK & p->flags) {
vbl = p->rmargin;
vend += p->rmargin - p->offset;
} else
vbl = p->offset;
/* Remove the p->overstep width. */
bp += (size_t)p->overstep;
p->overstep = 0;
}
/* Write out the [remaining] word. */
for ( ; i < p->col; i++) {
if (vend > bp && jhy > 0 && i > jhy)
break;
if ('\t' == p->buf[i])
break;
if (' ' == p->buf[i]) {
j = i;
while (' ' == p->buf[i])
i++;
dv = (size_t)(i - j) * (*p->width)(p, ' ');
vbl += dv;
vend += dv;
break;
}
if (ASCII_NBRSP == p->buf[i]) {
vbl += (*p->width)(p, ' ');
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, '-');
p->viscol += (*p->width)(p, '-');
continue;
}
(*p->letter)(p, p->buf[i]);
if (8 == p->buf[i])
p->viscol -= (*p->width)(p, p->buf[i-1]);
else
p->viscol += (*p->width)(p, p->buf[i]);
}
vis = vend;
}
/*
* If there was trailing white space, it was not printed;
* so reset the cursor position accordingly.
*/
if (vis)
vis -= vbl;
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 = (int)(vis - maxvis + (*p->width)(p, ' '));
/*
* 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)
p->overstep = 0;
return;
} else if (TERMP_DANGLE & p->flags)
return;
/* If the column was overrun, break the line. */
if (maxvis <= vis +
((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) {
(*p->endline)(p);
p->viscol = 0;
}
}
/*
* 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 (p->col || p->viscol)
term_flushln(p);
}
/*
* 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);
}
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 *seq, *cp;
char c;
int sz, uc;
size_t ssz;
enum mandoc_esc esc;
if ( ! (TERMP_NOSPACE & p->flags)) {
if ( ! (TERMP_KEEP & p->flags)) {
if (TERMP_PREKEEP & p->flags)
p->flags |= TERMP_KEEP;
bufferc(p, ' ');
if (TERMP_SENTENCE & p->flags)
bufferc(p, ' ');
} else
bufferc(p, ASCII_NBRSP);
}
if ( ! (p->flags & TERMP_NONOSPACE))
p->flags &= ~TERMP_NOSPACE;
else
p->flags |= TERMP_NOSPACE;
p->flags &= ~(TERMP_SENTENCE | TERMP_IGNDELIM);
while ('\0' != *word) {
if ((ssz = strcspn(word, "\\")) > 0)
encode(p, word, ssz);
word += (int)ssz;
if ('\\' != *word)
continue;
word++;
esc = mandoc_escape(&word, &seq, &sz);
if (ESCAPE_ERROR == esc)
break;
if (TERMENC_ASCII != p->enc)
switch (esc) {
case (ESCAPE_UNICODE):
uc = mchars_num2uc(seq + 1, sz - 1);
if ('\0' == uc)
break;
encode1(p, uc);
continue;
case (ESCAPE_SPECIAL):
uc = mchars_spec2cp(p->symtab, seq, sz);
if (uc <= 0)
break;
encode1(p, uc);
continue;
default:
break;
}
switch (esc) {
case (ESCAPE_UNICODE):
encode1(p, '?');
break;
case (ESCAPE_NUMBERED):
c = mchars_num2char(seq, sz);
if ('\0' != c)
encode(p, &c, 1);
break;
case (ESCAPE_SPECIAL):
cp = mchars_spec2str(p->symtab, seq, sz, &ssz);
if (NULL != cp)
encode(p, cp, ssz);
else if (1 == ssz)
encode(p, seq, sz);
break;
case (ESCAPE_FONTBOLD):
term_fontrepl(p, TERMFONT_BOLD);
break;
case (ESCAPE_FONTITALIC):
term_fontrepl(p, TERMFONT_UNDER);
break;
case (ESCAPE_FONT):
/* FALLTHROUGH */
case (ESCAPE_FONTROMAN):
term_fontrepl(p, TERMFONT_NONE);
break;
case (ESCAPE_FONTPREV):
term_fontlast(p);
break;
case (ESCAPE_NOSPACE):
if ('\0' == *word)
p->flags |= TERMP_NOSPACE;
break;
default:
break;
}
}
}
static void
adjbuf(struct termp *p, int sz)
{
if (0 == p->maxcols)
p->maxcols = 1024;
while (sz >= p->maxcols)
p->maxcols <<= 2;
p->buf = mandoc_realloc
(p->buf, sizeof(int) * (size_t)p->maxcols);
}
static void
bufferc(struct termp *p, char c)
{
if (p->col + 1 >= p->maxcols)
adjbuf(p, p->col + 1);
p->buf[p->col++] = c;
}
/*
* See encode().
* Do this for a single (probably unicode) value.
* Does not check for non-decorated glyphs.
*/
static void
encode1(struct termp *p, int c)
{
enum termfont f;
if (p->col + 4 >= p->maxcols)
adjbuf(p, p->col + 4);
f = term_fonttop(p);
if (TERMFONT_NONE == f) {
p->buf[p->col++] = c;
return;
} else if (TERMFONT_UNDER == f) {
p->buf[p->col++] = '_';
} else
p->buf[p->col++] = c;
p->buf[p->col++] = 8;
p->buf[p->col++] = c;
}
static void
encode(struct termp *p, const char *word, size_t sz)
{
enum termfont f;
int i, len;
/* LINTED */
len = sz;
/*
* 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))) {
if (p->col + len >= p->maxcols)
adjbuf(p, p->col + len);
for (i = 0; i < len; i++)
p->buf[p->col++] = word[i];
return;
}
/* Pre-buffer, assuming worst-case. */
if (p->col + 1 + (len * 3) >= p->maxcols)
adjbuf(p, p->col + 1 + (len * 3));
for (i = 0; i < len; i++) {
if (ASCII_HYPH != word[i] &&
! isgraph((unsigned char)word[i])) {
p->buf[p->col++] = word[i];
continue;
}
if (TERMFONT_UNDER == f)
p->buf[p->col++] = '_';
else if (ASCII_HYPH == word[i])
p->buf[p->col++] = '-';
else
p->buf[p->col++] = word[i];
p->buf[p->col++] = 8;
p->buf[p->col++] = word[i];
}
}
size_t
term_len(const struct termp *p, size_t sz)
{
return((*p->width)(p, ' ') * sz);
}
size_t
term_strlen(const struct termp *p, const char *cp)
{
size_t sz, rsz, i;
int ssz, c;
const char *seq, *rhs;
enum mandoc_esc esc;
static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' };
/*
* Account for escaped sequences within string length
* calculations. This follows the logic in term_word() as we
* must calculate the width of produced strings.
*/
sz = 0;
while ('\0' != *cp) {
rsz = strcspn(cp, rej);
for (i = 0; i < rsz; i++)
sz += (*p->width)(p, *cp++);
c = 0;
switch (*cp) {
case ('\\'):
cp++;
esc = mandoc_escape(&cp, &seq, &ssz);
if (ESCAPE_ERROR == esc)
return(sz);
if (TERMENC_ASCII != p->enc)
switch (esc) {
case (ESCAPE_UNICODE):
c = mchars_num2uc
(seq + 1, ssz - 1);
if ('\0' == c)
break;
sz += (*p->width)(p, c);
continue;
case (ESCAPE_SPECIAL):
c = mchars_spec2cp
(p->symtab, seq, ssz);
if (c <= 0)
break;
sz += (*p->width)(p, c);
continue;
default:
break;
}
rhs = NULL;
switch (esc) {
case (ESCAPE_UNICODE):
sz += (*p->width)(p, '?');
break;
case (ESCAPE_NUMBERED):
c = mchars_num2char(seq, ssz);
if ('\0' != c)
sz += (*p->width)(p, c);
break;
case (ESCAPE_SPECIAL):
rhs = mchars_spec2str
(p->symtab, seq, ssz, &rsz);
if (ssz != 1 || rhs)
break;
rhs = seq;
rsz = ssz;
break;
default:
break;
}
if (NULL == rhs)
break;
for (i = 0; i < rsz; i++)
sz += (*p->width)(p, *rhs++);
break;
case (ASCII_NBRSP):
sz += (*p->width)(p, ' ');
cp++;
break;
case (ASCII_HYPH):
sz += (*p->width)(p, '-');
cp++;
break;
default:
break;
}
}
return(sz);
}
/* ARGSUSED */
size_t
term_vspan(const struct termp *p, 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 termp *p, const struct roffsu *su)
{
double v;
v = ((*p->hspan)(p, su));
if (v < 0.0)
v = 0.0;
return((size_t) /* LINTED */
v);
}

128
contrib/mdocml/term.h Normal file
View File

@ -0,0 +1,128 @@
/* $Id: term.h,v 1.90 2011/12/04 23:10:52 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 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,
TERMENC_LOCALE,
TERMENC_UTF8
};
enum termtype {
TERMTYPE_CHAR,
TERMTYPE_PS,
TERMTYPE_PDF
};
enum termfont {
TERMFONT_NONE = 0,
TERMFONT_BOLD,
TERMFONT_UNDER,
TERMFONT__MAX
};
#define TERM_MAXMARGIN 100000 /* FIXME */
typedef void (*term_margin)(struct termp *, const void *);
struct termp_tbl {
int width; /* width in fixed chars */
int decimal; /* decimal point position */
};
struct termp {
enum termtype type;
struct rofftbl tbl; /* table configuration */
int mdocstyle; /* imitate mdoc(7) output */
size_t defindent; /* Default indent for text. */
size_t defrmargin; /* Right margin of the device. */
size_t rmargin; /* Current right margin. */
size_t maxrmargin; /* Max right margin. */
int maxcols; /* Max size of buf. */
size_t offset; /* Margin offest. */
size_t tabwidth; /* Distance of tab positions. */
int 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_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(). */
#define TERMP_KEEP (1 << 14) /* Keep words together. */
#define TERMP_PREKEEP (1 << 15) /* ...starting with the next one. */
int *buf; /* Output buffer. */
enum termenc enc; /* Type of encoding. */
struct mchars *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 *, int);
void (*begin)(struct termp *);
void (*end)(struct termp *);
void (*endline)(struct termp *);
void (*advance)(struct termp *, size_t);
size_t (*width)(const struct termp *, int);
double (*hspan)(const struct termp *,
const struct roffsu *);
const void *argf; /* arg for headf/footf */
struct termp_ps *ps;
};
void term_eqn(struct termp *, const struct eqn *);
void term_tbl(struct termp *, const struct tbl_span *);
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 termp *,
const struct roffsu *);
size_t term_vspan(const struct termp *,
const struct roffsu *);
size_t term_strlen(const struct termp *, const char *);
size_t term_len(const struct termp *, size_t);
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*/

289
contrib/mdocml/term_ascii.c Normal file
View File

@ -0,0 +1,289 @@
/* $Id: term_ascii.c,v 1.20 2011/12/04 23:10:52 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011 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>
#ifdef USE_WCHAR
# include <locale.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef USE_WCHAR
# include <wchar.h>
#endif
#include "mandoc.h"
#include "out.h"
#include "term.h"
#include "main.h"
/*
* Sadly, this doesn't seem to be defined on systems even when they
* support it. For the time being, remove it and let those compiling
* the software decide for themselves what to use.
*/
#if 0
#if ! defined(__STDC_ISO_10646__)
# undef USE_WCHAR
#endif
#endif
static struct termp *ascii_init(enum termenc, char *);
static double ascii_hspan(const struct termp *,
const struct roffsu *);
static size_t ascii_width(const struct termp *, int);
static void ascii_advance(struct termp *, size_t);
static void ascii_begin(struct termp *);
static void ascii_end(struct termp *);
static void ascii_endline(struct termp *);
static void ascii_letter(struct termp *, int);
#ifdef USE_WCHAR
static void locale_advance(struct termp *, size_t);
static void locale_endline(struct termp *);
static void locale_letter(struct termp *, int);
static size_t locale_width(const struct termp *, int);
#endif
static struct termp *
ascii_init(enum termenc enc, char *outopts)
{
const char *toks[4];
char *v;
struct termp *p;
p = mandoc_calloc(1, sizeof(struct termp));
p->enc = enc;
p->tabwidth = 5;
p->defrmargin = 78;
p->begin = ascii_begin;
p->end = ascii_end;
p->hspan = ascii_hspan;
p->type = TERMTYPE_CHAR;
p->enc = TERMENC_ASCII;
p->advance = ascii_advance;
p->endline = ascii_endline;
p->letter = ascii_letter;
p->width = ascii_width;
#ifdef USE_WCHAR
if (TERMENC_ASCII != enc) {
v = TERMENC_LOCALE == enc ?
setlocale(LC_ALL, "") :
setlocale(LC_CTYPE, "UTF-8");
if (NULL != v && MB_CUR_MAX > 1) {
p->enc = enc;
p->advance = locale_advance;
p->endline = locale_endline;
p->letter = locale_letter;
p->width = locale_width;
}
}
#endif
toks[0] = "indent";
toks[1] = "width";
toks[2] = "mdoc";
toks[3] = NULL;
while (outopts && *outopts)
switch (getsubopt(&outopts, UNCONST(toks), &v)) {
case (0):
p->defindent = (size_t)atoi(v);
break;
case (1):
p->defrmargin = (size_t)atoi(v);
break;
case (2):
/*
* Temporary, undocumented mode
* to imitate mdoc(7) output style.
*/
p->mdocstyle = 1;
p->defindent = 5;
break;
default:
break;
}
/* Enforce a lower boundary. */
if (p->defrmargin < 58)
p->defrmargin = 58;
return(p);
}
void *
ascii_alloc(char *outopts)
{
return(ascii_init(TERMENC_ASCII, outopts));
}
void *
utf8_alloc(char *outopts)
{
return(ascii_init(TERMENC_UTF8, outopts));
}
void *
locale_alloc(char *outopts)
{
return(ascii_init(TERMENC_LOCALE, outopts));
}
/* ARGSUSED */
static size_t
ascii_width(const struct termp *p, int c)
{
return(1);
}
void
ascii_free(void *arg)
{
term_free((struct termp *)arg);
}
/* ARGSUSED */
static void
ascii_letter(struct termp *p, int 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;
for (i = 0; i < len; i++)
putchar(' ');
}
/* ARGSUSED */
static double
ascii_hspan(const struct termp *p, const struct roffsu *su)
{
double r;
/*
* Approximate based on character width. These are generated
* entirely by eyeballing the screen, but appear to be correct.
*/
switch (su->unit) {
case (SCALE_CM):
r = 4 * su->scale;
break;
case (SCALE_IN):
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;
break;
case (SCALE_VS):
r = su->scale * 2 - 1;
break;
default:
r = su->scale;
break;
}
return(r);
}
#ifdef USE_WCHAR
/* ARGSUSED */
static size_t
locale_width(const struct termp *p, int c)
{
int rc;
return((rc = wcwidth(c)) < 0 ? 0 : rc);
}
/* ARGSUSED */
static void
locale_advance(struct termp *p, size_t len)
{
size_t i;
for (i = 0; i < len; i++)
putwchar(L' ');
}
/* ARGSUSED */
static void
locale_endline(struct termp *p)
{
putwchar(L'\n');
}
/* ARGSUSED */
static void
locale_letter(struct termp *p, int c)
{
putwchar(c);
}
#endif

1185
contrib/mdocml/term_ps.c Normal file

File diff suppressed because it is too large Load Diff

349
contrib/mdocml/tree.c Normal file
View File

@ -0,0 +1,349 @@
/* $Id: tree.c,v 1.47 2011/09/18 14:14:15 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2011 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 <limits.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_box(const struct eqn_box *, int);
static void print_man(const struct man_node *, int);
static void print_mdoc(const struct mdoc_node *, int);
static void print_span(const struct tbl_span *, 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;
t = p = 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):
if (n->end)
t = "body-end";
else
t = "block-body";
break;
case (MDOC_TAIL):
t = "block-tail";
break;
case (MDOC_ELEM):
t = "elem";
break;
case (MDOC_TEXT):
t = "text";
break;
case (MDOC_TBL):
/* FALLTHROUGH */
case (MDOC_EQN):
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_TBL):
/* FALLTHROUGH */
case (MDOC_EQN):
break;
case (MDOC_ROOT):
p = "root";
break;
default:
abort();
/* NOTREACHED */
}
if (n->span) {
assert(NULL == p && NULL == t);
print_span(n->span, indent);
} else if (n->eqn) {
assert(NULL == p && NULL == t);
print_box(n->eqn->root, indent);
} else {
for (i = 0; i < indent; i++)
putchar('\t');
printf("%s (%s)", p, t);
for (i = 0; i < (int)argc; i++) {
printf(" -%s", mdoc_argnames[argv[i].arg]);
if (argv[i].sz > 0)
printf(" [");
for (j = 0; j < (int)argv[i].sz; j++)
printf(" [%s]", argv[i].value[j]);
if (argv[i].sz > 0)
printf(" ]");
}
for (i = 0; i < (int)sz; i++)
printf(" [%s]", params[i]);
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;
t = p = NULL;
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;
case (MAN_TAIL):
t = "block-tail";
break;
case (MAN_TBL):
/* FALLTHROUGH */
case (MAN_EQN):
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_TAIL):
/* FALLTHROUGH */
case (MAN_BODY):
p = man_macronames[n->tok];
break;
case (MAN_ROOT):
p = "root";
break;
case (MAN_TBL):
/* FALLTHROUGH */
case (MAN_EQN):
break;
default:
abort();
/* NOTREACHED */
}
if (n->span) {
assert(NULL == p && NULL == t);
print_span(n->span, indent);
} else if (n->eqn) {
assert(NULL == p && NULL == t);
print_box(n->eqn->root, indent);
} else {
for (i = 0; i < indent; i++)
putchar('\t');
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);
}
static void
print_box(const struct eqn_box *ep, int indent)
{
int i;
const char *t;
if (NULL == ep)
return;
for (i = 0; i < indent; i++)
putchar('\t');
t = NULL;
switch (ep->type) {
case (EQN_ROOT):
t = "eqn-root";
break;
case (EQN_LIST):
t = "eqn-list";
break;
case (EQN_SUBEXPR):
t = "eqn-expr";
break;
case (EQN_TEXT):
t = "eqn-text";
break;
case (EQN_MATRIX):
t = "eqn-matrix";
break;
}
assert(t);
printf("%s(%d, %d, %d, %d, %d, \"%s\", \"%s\") %s\n",
t, EQN_DEFSIZE == ep->size ? 0 : ep->size,
ep->pos, ep->font, ep->mark, ep->pile,
ep->left ? ep->left : "",
ep->right ? ep->right : "",
ep->text ? ep->text : "");
print_box(ep->first, indent + 1);
print_box(ep->next, indent);
}
static void
print_span(const struct tbl_span *sp, int indent)
{
const struct tbl_dat *dp;
int i;
for (i = 0; i < indent; i++)
putchar('\t');
switch (sp->pos) {
case (TBL_SPAN_HORIZ):
putchar('-');
return;
case (TBL_SPAN_DHORIZ):
putchar('=');
return;
default:
break;
}
for (dp = sp->first; dp; dp = dp->next) {
switch (dp->pos) {
case (TBL_DATA_HORIZ):
/* FALLTHROUGH */
case (TBL_DATA_NHORIZ):
putchar('-');
continue;
case (TBL_DATA_DHORIZ):
/* FALLTHROUGH */
case (TBL_DATA_NDHORIZ):
putchar('=');
continue;
default:
break;
}
printf("[\"%s\"", dp->string ? dp->string : "");
if (dp->spans)
printf("(%d)", dp->spans);
if (NULL == dp->layout)
putchar('*');
putchar(']');
putchar(' ');
}
printf("(tbl) %d:1\n", sp->line);
}

39
contrib/mdocml/vol.c Normal file
View File

@ -0,0 +1,39 @@
/* $Id: vol.c,v 1.9 2011/03/22 14:33:05 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 "mdoc.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
contrib/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")

View File

@ -944,6 +944,8 @@
man9
..
..
mdocml
..
misc
fonts
..

20
lib/libmandoc/Makefile Normal file
View File

@ -0,0 +1,20 @@
# $FreeBSD$
MDOCMLDIR= ${.CURDIR}/../../contrib/mdocml
.PATH: ${MDOCMLDIR}
LIB= mandoc
#NO_PIC=
INTERNALLIB=
MAN= mandoc.3
SRCS= arch.c att.c chars.c \
compat_fgetln.c compat_getsubopt.c compat_strlcat.c compat_strlcpy.c \
eqn.c lib.c man.c man_hash.c man_macro.c man_validate.c mandoc.c \
mdoc.c mdoc_argv.c mdoc_hash.c mdoc_macro.c mdoc_validate.c \
msec.c read.c roff.c st.c \
tbl.c tbl_data.c tbl_layout.c tbl_opts.c vol.c
WARNS?= 3
CFLAGS+= -DHAVE_CONFIG_H -DVERSION="\"1.12.1\""
.include <bsd.lib.mk>

22
usr.bin/mandoc/Makefile Normal file
View File

@ -0,0 +1,22 @@
# $FreeBSD$
MDOCMLDIR= ${.CURDIR}/../../contrib/mdocml
LIBMANDOC= ${.OBJDIR}/../../lib/libmandoc/libmandoc.a
.PATH: ${MDOCMLDIR}
PROG= mandoc
FILES= example.style.css external.png style.css
FILESDIR= ${SHAREDIR}/mdocml
LINKS= mdocml
MAN= mandoc.1 eqn.7 mandoc_char.7 tbl.7 # man.7 mdoc.7 roff.7
MLINKS= mandoc.1 mdocml.1
SRCS= eqn_html.c eqn_term.c html.c main.c man_html.c man_term.c mdoc_html.c \
mdoc_man.c mdoc_term.c out.c tbl_html.c tbl_term.c term.c \
term_ascii.c term_ps.c tree.c
WARNS?= 3
CFLAGS+= -DHAVE_CONFIG_H -DVERSION="\"1.12.1\""
DPADD= ${LIBMANDOC}
LDADD= ${LIBMANDOC}
.include <bsd.prog.mk>