import mdocml.
This commit is contained in:
parent
2f3c9c04cc
commit
3f22092d45
72 changed files with 27719 additions and 1 deletions
|
@ -17,7 +17,7 @@ SUBDIR= aal add_route adduser advent arp ash at autil awk \
|
|||
intr ipcrm ipcs irdpd isoread join kill last leave \
|
||||
less lex life loadkeys loadramdisk logger login look lp \
|
||||
lpd ls lspci M m4 mail make MAKEDEV man \
|
||||
mdb mesg mined mkdep mkdir mkdist mkfifo mkfs mknod \
|
||||
mdb mdocml mesg mined mkdep mkdir mkdist mkfifo mkfs mknod \
|
||||
mkproto modem mount mt netconf newroot nice nm nohup \
|
||||
nonamed od packit packman passwd paste patch pax \
|
||||
ping postinstall poweroff pr prep printf printroot \
|
||||
|
|
43
commands/mdocml/ChangeLog.xsl
Normal file
43
commands/mdocml/ChangeLog.xsl
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?xml version='1.0' encoding="utf-8"?>
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
|
||||
<xsl:output encoding="utf-8" method="html" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
|
||||
<xsl:template match="/changelog">
|
||||
<html>
|
||||
<head>
|
||||
<title>mdocml - CVS-ChangeLog</title>
|
||||
<link rel="stylesheet" href="index.css" type="text/css" media="all" />
|
||||
</head>
|
||||
<body>
|
||||
<xsl:for-each select="entry">
|
||||
<div class="clhead">
|
||||
<xsl:text>Files modified by </xsl:text>
|
||||
<xsl:value-of select="concat(author, ': ', date, ' (', time, ')')" />
|
||||
</div>
|
||||
<div class="clbody">
|
||||
<strong>
|
||||
<xsl:text>Note: </xsl:text>
|
||||
</strong>
|
||||
<xsl:value-of select="msg"/>
|
||||
<ul class="clbody">
|
||||
<xsl:for-each select="file">
|
||||
<li>
|
||||
<xsl:value-of select="name"/>
|
||||
<span class="rev">
|
||||
<xsl:text> — Rev: </xsl:text>
|
||||
<xsl:value-of select="revision"/>
|
||||
<xsl:text>, Status: </xsl:text>
|
||||
<xsl:value-of select="cvsstate"/>
|
||||
<xsl:if test="tag">
|
||||
<xsl:text>, Tag: </xsl:text>
|
||||
<xsl:value-of select="tag" />
|
||||
</xsl:if>
|
||||
</span>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ul>
|
||||
</div>
|
||||
</xsl:for-each>
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
36
commands/mdocml/Makefile
Normal file
36
commands/mdocml/Makefile
Normal file
|
@ -0,0 +1,36 @@
|
|||
|
||||
VERSION = 1.10.2
|
||||
VDATE = 19 June 2010
|
||||
|
||||
VFLAGS = -DVERSION="\"$(VERSION)\""
|
||||
|
||||
.if ${CC} == gcc
|
||||
WFLAGS = -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings
|
||||
CFLAGS += -g $(WFLAGS) $(VFLAGS) -DHAVE_CONFIG_H
|
||||
ARFLAGS = rs
|
||||
.else
|
||||
CFLAGS += ${VFLAGS} -DHAVE_CONFIG_H
|
||||
ARFLAGS = r
|
||||
.endif
|
||||
|
||||
# Specify this if you want to hard-code the operating system to appear
|
||||
# in the lower-left hand corner of -mdoc manuals.
|
||||
# CFLAGS += -DOSNAME="\"OpenBSD 4.5\""
|
||||
|
||||
# Unset this if you don't want Xo/Xc allowing split `It' lines, which
|
||||
# breaks symmetry.
|
||||
CFLAGS += -DUGLY
|
||||
|
||||
SRCS = main.c mdoc_term.c chars.c term.c tree.c compat.c \
|
||||
man_term.c html.c mdoc_html.c man_html.c out.c \
|
||||
term_ps.c term_ascii.c man_macro.c man.c man_hash.c \
|
||||
man_validate.c man_action.c mandoc.c man_argv.c roff.c \
|
||||
mdoc_macro.c mdoc.c mdoc_hash.c mdoc_strings.c mdoc_argv.c \
|
||||
mdoc_validate.c mdoc_action.c lib.c att.c arch.c vol.c \
|
||||
msec.c st.c
|
||||
|
||||
MAN = mandoc.1 mdoc.3 mdoc.7 mandoc_char.7 man.7 man.3 roff.7 roff.3
|
||||
|
||||
PROG = mandoc
|
||||
|
||||
.include <bsd.prog.mk>
|
38
commands/mdocml/arch.c
Normal file
38
commands/mdocml/arch.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* $Id: arch.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
|
||||
#define LINE(x, y) \
|
||||
if (0 == strcmp(p, x)) return(y);
|
||||
|
||||
const char *
|
||||
mdoc_a2arch(const char *p)
|
||||
{
|
||||
|
||||
#include "arch.in"
|
||||
|
||||
return(NULL);
|
||||
}
|
53
commands/mdocml/arch.in
Normal file
53
commands/mdocml/arch.in
Normal file
|
@ -0,0 +1,53 @@
|
|||
/* $Id: arch.in,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file defines the architecture token of the .Dt prologue macro.
|
||||
* All architectures that your system supports (or the manuals of your
|
||||
* system) should be included here. The right-hand-side is the
|
||||
* formatted output.
|
||||
*
|
||||
* Be sure to escape strings.
|
||||
*/
|
||||
|
||||
LINE("alpha", "Alpha")
|
||||
LINE("amd64", "AMD64")
|
||||
LINE("amiga", "Amiga")
|
||||
LINE("arc", "ARC")
|
||||
LINE("arm", "ARM")
|
||||
LINE("armish", "ARMISH")
|
||||
LINE("aviion", "AViiON")
|
||||
LINE("hp300", "HP300")
|
||||
LINE("hppa", "HPPA")
|
||||
LINE("hppa64", "HPPA64")
|
||||
LINE("i386", "i386")
|
||||
LINE("landisk", "LANDISK")
|
||||
LINE("loongson", "Loongson")
|
||||
LINE("luna88k", "Luna88k")
|
||||
LINE("mac68k", "Mac68k")
|
||||
LINE("macppc", "MacPPC")
|
||||
LINE("mvme68k", "MVME68k")
|
||||
LINE("mvme88k", "MVME88k")
|
||||
LINE("mvmeppc", "MVMEPPC")
|
||||
LINE("pmax", "PMAX")
|
||||
LINE("sgi", "SGI")
|
||||
LINE("socppc", "SOCPPC")
|
||||
LINE("sparc", "SPARC")
|
||||
LINE("sparc64", "SPARC64")
|
||||
LINE("sun3", "Sun3")
|
||||
LINE("vax", "VAX")
|
||||
LINE("zaurus", "Zaurus")
|
38
commands/mdocml/att.c
Normal file
38
commands/mdocml/att.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* $Id: att.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
|
||||
#define LINE(x, y) \
|
||||
if (0 == strcmp(p, x)) return(y);
|
||||
|
||||
const char *
|
||||
mdoc_a2att(const char *p)
|
||||
{
|
||||
|
||||
#include "att.in"
|
||||
|
||||
return(NULL);
|
||||
}
|
37
commands/mdocml/att.in
Normal file
37
commands/mdocml/att.in
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* $Id: att.in,v 1.6 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file defines the AT&T versions of the .At macro. This probably
|
||||
* isn't going to change. The right-hand side is the formatted string.
|
||||
*
|
||||
* Be sure to escape strings.
|
||||
*/
|
||||
|
||||
LINE("v1", "Version 1 AT&T UNIX")
|
||||
LINE("v2", "Version 2 AT&T UNIX")
|
||||
LINE("v3", "Version 3 AT&T UNIX")
|
||||
LINE("v4", "Version 4 AT&T UNIX")
|
||||
LINE("v5", "Version 5 AT&T UNIX")
|
||||
LINE("v6", "Version 6 AT&T UNIX")
|
||||
LINE("v7", "Version 7 AT&T UNIX")
|
||||
LINE("32v", "Version 32V AT&T UNIX")
|
||||
LINE("V", "AT&T System V UNIX")
|
||||
LINE("V.1", "AT&T System V.1 UNIX")
|
||||
LINE("V.2", "AT&T System V.2 UNIX")
|
||||
LINE("V.3", "AT&T System V.3 UNIX")
|
||||
LINE("V.4", "AT&T System V.4 UNIX")
|
206
commands/mdocml/chars.c
Normal file
206
commands/mdocml/chars.c
Normal file
|
@ -0,0 +1,206 @@
|
|||
/* $Id: chars.c,v 1.20 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "chars.h"
|
||||
|
||||
#define PRINT_HI 126
|
||||
#define PRINT_LO 32
|
||||
|
||||
struct ln {
|
||||
struct ln *next;
|
||||
const char *code;
|
||||
const char *ascii;
|
||||
const char *html;
|
||||
size_t codesz;
|
||||
size_t asciisz;
|
||||
size_t htmlsz;
|
||||
int type;
|
||||
#define CHARS_CHAR (1 << 0)
|
||||
#define CHARS_STRING (1 << 1)
|
||||
#define CHARS_BOTH (CHARS_CHAR | CHARS_STRING)
|
||||
};
|
||||
|
||||
#define LINES_MAX 370
|
||||
|
||||
#define CHAR(w, x, y, z, a, b) \
|
||||
{ NULL, (w), (y), (a), (x), (z), (b), CHARS_CHAR },
|
||||
#define STRING(w, x, y, z, a, b) \
|
||||
{ NULL, (w), (y), (a), (x), (z), (b), CHARS_STRING },
|
||||
#define BOTH(w, x, y, z, a, b) \
|
||||
{ NULL, (w), (y), (a), (x), (z), (b), CHARS_BOTH },
|
||||
|
||||
#define CHAR_TBL_START static struct ln lines[LINES_MAX] = {
|
||||
#define CHAR_TBL_END };
|
||||
|
||||
#include "chars.in"
|
||||
|
||||
struct tbl {
|
||||
enum chars type;
|
||||
struct ln **htab;
|
||||
};
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define inline
|
||||
#endif
|
||||
static inline int match(const struct ln *,
|
||||
const char *, size_t, int);
|
||||
static const char *find(struct tbl *, const char *,
|
||||
size_t, size_t *, int);
|
||||
|
||||
|
||||
void
|
||||
chars_free(void *arg)
|
||||
{
|
||||
struct tbl *tab;
|
||||
|
||||
tab = (struct tbl *)arg;
|
||||
|
||||
free(tab->htab);
|
||||
free(tab);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
chars_init(enum chars type)
|
||||
{
|
||||
struct tbl *tab;
|
||||
struct ln **htab;
|
||||
struct ln *pp;
|
||||
int i, hash;
|
||||
|
||||
/*
|
||||
* Constructs a very basic chaining hashtable. The hash routine
|
||||
* is simply the integral value of the first character.
|
||||
* Subsequent entries are chained in the order they're processed
|
||||
* (they're in-line re-ordered during lookup).
|
||||
*/
|
||||
|
||||
tab = malloc(sizeof(struct tbl));
|
||||
if (NULL == tab) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
htab = calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
|
||||
if (NULL == htab) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (i = 0; i < LINES_MAX; i++) {
|
||||
hash = (int)lines[i].code[0] - PRINT_LO;
|
||||
|
||||
if (NULL == (pp = htab[hash])) {
|
||||
htab[hash] = &lines[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( ; pp->next; pp = pp->next)
|
||||
/* Scan ahead. */ ;
|
||||
pp->next = &lines[i];
|
||||
}
|
||||
|
||||
tab->htab = htab;
|
||||
tab->type = type;
|
||||
return(tab);
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
chars_a2ascii(void *arg, const char *p, size_t sz, size_t *rsz)
|
||||
{
|
||||
|
||||
return(find((struct tbl *)arg, p, sz, rsz, CHARS_CHAR));
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
chars_a2res(void *arg, const char *p, size_t sz, size_t *rsz)
|
||||
{
|
||||
|
||||
return(find((struct tbl *)arg, p, sz, rsz, CHARS_STRING));
|
||||
}
|
||||
|
||||
|
||||
static const char *
|
||||
find(struct tbl *tab, const char *p, size_t sz, size_t *rsz, int type)
|
||||
{
|
||||
struct ln *pp, *prev;
|
||||
struct ln **htab;
|
||||
int hash;
|
||||
|
||||
assert(p);
|
||||
assert(sz > 0);
|
||||
|
||||
if (p[0] < PRINT_LO || p[0] > PRINT_HI)
|
||||
return(NULL);
|
||||
|
||||
/*
|
||||
* Lookup the symbol in the symbol hash. See ascii2htab for the
|
||||
* hashtable specs. This dynamically re-orders the hash chain
|
||||
* to optimise for repeat hits.
|
||||
*/
|
||||
|
||||
hash = (int)p[0] - PRINT_LO;
|
||||
htab = tab->htab;
|
||||
|
||||
if (NULL == (pp = htab[hash]))
|
||||
return(NULL);
|
||||
|
||||
for (prev = NULL; pp; pp = pp->next) {
|
||||
if ( ! match(pp, p, sz, type)) {
|
||||
prev = pp;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
prev->next = pp->next;
|
||||
pp->next = htab[hash];
|
||||
htab[hash] = pp;
|
||||
}
|
||||
|
||||
if (CHARS_HTML == tab->type) {
|
||||
*rsz = pp->htmlsz;
|
||||
return(pp->html);
|
||||
}
|
||||
*rsz = pp->asciisz;
|
||||
return(pp->ascii);
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
match(const struct ln *ln, const char *p, size_t sz, int type)
|
||||
{
|
||||
|
||||
if ( ! (ln->type & type))
|
||||
return(0);
|
||||
if (ln->codesz != sz)
|
||||
return(0);
|
||||
return(0 == strncmp(ln->code, p, sz));
|
||||
}
|
34
commands/mdocml/chars.h
Normal file
34
commands/mdocml/chars.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* $Id: chars.h,v 1.4 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef CHARS_H
|
||||
#define CHARS_H
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
enum chars {
|
||||
CHARS_ASCII,
|
||||
CHARS_HTML
|
||||
};
|
||||
|
||||
void *chars_init(enum chars);
|
||||
const char *chars_a2ascii(void *, const char *, size_t, size_t *);
|
||||
const char *chars_a2res(void *, const char *, size_t, size_t *);
|
||||
void chars_free(void *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!CHARS_H*/
|
446
commands/mdocml/chars.in
Normal file
446
commands/mdocml/chars.in
Normal file
|
@ -0,0 +1,446 @@
|
|||
/* $Id: chars.in,v 1.25 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The ASCII translation tables. STRING corresponds to predefined
|
||||
* strings (cf. mdoc_samples.7 and tmac/mdoc/doc-nroff). CHAR
|
||||
* corresponds to special characters (cf. groff_char.7). BOTH contains
|
||||
* sequences that are equivalent in both STRING and CHAR.
|
||||
*
|
||||
* Either way, the left-hand side corresponds to the input sequence (\x,
|
||||
* \(xx, \*(xx and so on) whose length is listed second element. The
|
||||
* right-hand side is what's produced by the front-end, with the fourth
|
||||
* element being its length.
|
||||
*
|
||||
* XXX - C-escape strings!
|
||||
* XXX - update LINES_MAX if adding more!
|
||||
*/
|
||||
|
||||
/* Non-breaking, non-collapsing space uses unit separator. */
|
||||
static const char ascii_nbrsp[2] = { ASCII_NBRSP, 0 };
|
||||
|
||||
CHAR_TBL_START
|
||||
|
||||
/* Spacing. */
|
||||
CHAR("c", 1, "", 0, "", 0)
|
||||
CHAR("0", 1, " ", 1, " ", 7)
|
||||
CHAR(" ", 1, " ", 1, " ", 7)
|
||||
CHAR("~", 1, ascii_nbrsp, 1, " ", 6)
|
||||
CHAR("%", 1, "", 0, "", 0)
|
||||
CHAR("&", 1, "", 0, "", 0)
|
||||
CHAR("^", 1, "", 0, "", 0)
|
||||
CHAR("|", 1, "", 0, "", 0)
|
||||
CHAR("}", 1, "", 0, "", 0)
|
||||
|
||||
/* Accents. */
|
||||
CHAR("a\"", 2, "\"", 1, "̋", 6)
|
||||
CHAR("a-", 2, "-", 1, "¯", 6)
|
||||
CHAR("a.", 2, ".", 1, "˙", 6)
|
||||
CHAR("a^", 2, "^", 1, "̂", 6)
|
||||
BOTH("\'", 1, "\'", 1, "́", 6)
|
||||
BOTH("aa", 2, "\'", 1, "́", 6)
|
||||
BOTH("ga", 2, "`", 1, "̀", 6)
|
||||
BOTH("`", 1, "`", 1, "̀", 6)
|
||||
CHAR("ab", 2, "`", 1, "̆", 6)
|
||||
CHAR("ac", 2, ",", 1, "̧", 6)
|
||||
CHAR("ad", 2, "\"", 1, "̈", 6)
|
||||
CHAR("ah", 2, "v", 1, "ˇ", 6)
|
||||
CHAR("ao", 2, "o", 1, "˚", 6)
|
||||
CHAR("a~", 2, "~", 1, "̃", 6)
|
||||
CHAR("ho", 2, ",", 1, "̨", 6)
|
||||
CHAR("ha", 2, "^", 1, "^", 1)
|
||||
CHAR("ti", 2, "~", 1, "~", 1)
|
||||
|
||||
/* Quotes. */
|
||||
CHAR("Bq", 2, ",,", 2, "„", 7)
|
||||
CHAR("bq", 2, ",", 1, "‚", 7)
|
||||
BOTH("lq", 2, "``", 2, "“", 7)
|
||||
BOTH("rq", 2, "\'\'", 2, "”", 7)
|
||||
CHAR("oq", 2, "`", 1, "‘", 7)
|
||||
CHAR("cq", 2, "\'", 1, "’", 7)
|
||||
CHAR("aq", 2, "\'", 1, "\'", 1)
|
||||
CHAR("dq", 2, "\"", 1, "\"", 1)
|
||||
CHAR("Fo", 2, "<<", 2, "«", 6)
|
||||
CHAR("Fc", 2, ">>", 2, "»", 6)
|
||||
CHAR("fo", 2, "<", 1, "‹", 7)
|
||||
CHAR("fc", 2, ">", 1, "›", 7)
|
||||
|
||||
/* Brackets. */
|
||||
CHAR("lB", 2, "[", 1, "[", 1)
|
||||
CHAR("rB", 2, "]", 1, "]", 1)
|
||||
CHAR("lC", 2, "{", 1, "{", 1)
|
||||
CHAR("rC", 2, "}", 1, "}", 1)
|
||||
CHAR("la", 2, "<", 1, "⟨", 8)
|
||||
CHAR("ra", 2, ">", 1, "⟩", 8)
|
||||
CHAR("bv", 2, "|", 1, "⎪", 7)
|
||||
CHAR("braceex", 7, "|", 1, "⎪", 7)
|
||||
CHAR("bracketlefttp", 13, "|", 1, "⎡", 7)
|
||||
CHAR("bracketleftbp", 13, "|", 1, "⎣", 7)
|
||||
CHAR("bracketleftex", 13, "|", 1, "⎢", 7)
|
||||
CHAR("bracketrighttp", 14, "|", 1, "⎤", 7)
|
||||
CHAR("bracketrightbp", 14, "|", 1, "⎦", 7)
|
||||
CHAR("bracketrightex", 14, "|", 1, "⎥", 7)
|
||||
CHAR("lt", 2, ",-", 2, "⎧", 7)
|
||||
CHAR("bracelefttp", 11, ",-", 2, "⎧", 7)
|
||||
CHAR("lk", 2, "{", 1, "⎨", 7)
|
||||
CHAR("braceleftmid", 12, "{", 1, "⎨", 7)
|
||||
CHAR("lb", 2, ",-", 2, "⎩", 7)
|
||||
CHAR("braceleftbp", 11, "`-", 2, "⎩", 7)
|
||||
CHAR("braceleftex", 11, "|", 1, "⎪", 7)
|
||||
CHAR("rt", 2, "-.", 2, "⎫", 7)
|
||||
CHAR("bracerighttp", 12, "-.", 2, "⎫", 7)
|
||||
CHAR("rk", 2, "}", 1, "⎬", 7)
|
||||
CHAR("bracerightmid", 13, "}", 1, "⎬", 7)
|
||||
CHAR("rb", 2, "-\'", 2, "⎭", 7)
|
||||
CHAR("bracerightbp", 12, "-\'", 2, "⎭", 7)
|
||||
CHAR("bracerightex", 12, "|", 1, "⎪", 7)
|
||||
CHAR("parenlefttp", 11, "/", 1, "⎛", 7)
|
||||
CHAR("parenleftbp", 11, "\\", 1, "⎝", 7)
|
||||
CHAR("parenleftex", 11, "|", 1, "⎜", 7)
|
||||
CHAR("parenrighttp", 12, "\\", 1, "⎞", 7)
|
||||
CHAR("parenrightbp", 12, "/", 1, "⎠", 7)
|
||||
CHAR("parenrightex", 12, "|", 1, "⎟", 7)
|
||||
|
||||
/* Greek characters. */
|
||||
CHAR("*A", 2, "A", 1, "Α", 6)
|
||||
CHAR("*B", 2, "B", 1, "Β", 6)
|
||||
CHAR("*G", 2, "|", 1, "Γ", 6)
|
||||
CHAR("*D", 2, "/\\", 2, "Δ", 6)
|
||||
CHAR("*E", 2, "E", 1, "Ε", 6)
|
||||
CHAR("*Z", 2, "Z", 1, "Ζ", 6)
|
||||
CHAR("*Y", 2, "H", 1, "Η", 6)
|
||||
CHAR("*H", 2, "O", 1, "Θ", 6)
|
||||
CHAR("*I", 2, "I", 1, "Ι", 6)
|
||||
CHAR("*K", 2, "K", 1, "Κ", 6)
|
||||
CHAR("*L", 2, "/\\", 2, "Λ", 6)
|
||||
CHAR("*M", 2, "M", 1, "Μ", 6)
|
||||
CHAR("*N", 2, "N", 1, "Ν", 6)
|
||||
CHAR("*C", 2, "H", 1, "Ξ", 6)
|
||||
CHAR("*O", 2, "O", 1, "Ο", 6)
|
||||
CHAR("*P", 2, "TT", 2, "Π", 6)
|
||||
CHAR("*R", 2, "P", 1, "Ρ", 6)
|
||||
CHAR("*S", 2, ">", 1, "Σ", 6)
|
||||
CHAR("*T", 2, "T", 1, "Τ", 6)
|
||||
CHAR("*U", 2, "Y", 1, "Υ", 6)
|
||||
CHAR("*F", 2, "O_", 1, "Φ", 6)
|
||||
CHAR("*X", 2, "X", 1, "Χ", 6)
|
||||
CHAR("*Q", 2, "Y", 1, "Ψ", 6)
|
||||
CHAR("*W", 2, "O", 1, "Ω", 6)
|
||||
CHAR("*a", 2, "a", 1, "α", 6)
|
||||
CHAR("*b", 2, "B", 1, "β", 6)
|
||||
CHAR("*g", 2, "y", 1, "γ", 6)
|
||||
CHAR("*d", 2, "d", 1, "δ", 6)
|
||||
CHAR("*e", 2, "e", 1, "ε", 6)
|
||||
CHAR("*z", 2, "C", 1, "ζ", 6)
|
||||
CHAR("*y", 2, "n", 1, "η", 6)
|
||||
CHAR("*h", 2, "0", 1, "θ", 6)
|
||||
CHAR("*i", 2, "i", 1, "ι", 6)
|
||||
CHAR("*k", 2, "k", 1, "κ", 6)
|
||||
CHAR("*l", 2, "\\", 1, "λ", 6)
|
||||
CHAR("*m", 2, "u", 1, "μ", 6)
|
||||
CHAR("*n", 2, "v", 1, "ν", 6)
|
||||
CHAR("*c", 2, "E", 1, "ξ", 6)
|
||||
CHAR("*o", 2, "o", 1, "ο", 6)
|
||||
CHAR("*p", 2, "n", 1, "π", 6)
|
||||
CHAR("*r", 2, "p", 1, "ρ", 6)
|
||||
CHAR("*s", 2, "o", 1, "σ", 6)
|
||||
CHAR("*t", 2, "t", 1, "τ", 6)
|
||||
CHAR("*u", 2, "u", 1, "υ", 6)
|
||||
CHAR("*f", 2, "o", 1, "ϕ", 6)
|
||||
CHAR("*x", 2, "x", 1, "χ", 6)
|
||||
CHAR("*q", 2, "u", 1, "ψ", 6)
|
||||
CHAR("*w", 2, "w", 1, "ω", 6)
|
||||
CHAR("+h", 2, "0", 1, "ϑ", 6)
|
||||
CHAR("+f", 2, "o", 1, "φ", 6)
|
||||
CHAR("+p", 2, "w", 1, "ϖ", 6)
|
||||
CHAR("+e", 2, "e", 1, "ϵ", 7)
|
||||
CHAR("ts", 2, "s", 1, "ς", 6)
|
||||
|
||||
/* Accented letters. */
|
||||
CHAR(",C", 2, "C", 1, "Ç", 6)
|
||||
CHAR(",c", 2, "c", 1, "ç", 6)
|
||||
CHAR("/L", 2, "L", 1, "Ł", 6)
|
||||
CHAR("/O", 2, "O", 1, "Ø", 6)
|
||||
CHAR("/l", 2, "l", 1, "ł", 6)
|
||||
CHAR("/o", 2, "o", 1, "ø", 6)
|
||||
CHAR("oA", 2, "A", 1, "Å", 6)
|
||||
CHAR("oa", 2, "a", 1, "å", 6)
|
||||
CHAR(":A", 2, "A", 1, "Ä", 6)
|
||||
CHAR(":E", 2, "E", 1, "Ë", 6)
|
||||
CHAR(":I", 2, "I", 1, "Ï", 6)
|
||||
CHAR(":O", 2, "O", 1, "Ö", 6)
|
||||
CHAR(":U", 2, "U", 1, "Ü", 6)
|
||||
CHAR(":a", 2, "a", 1, "ä", 6)
|
||||
CHAR(":e", 2, "e", 1, "ë", 6)
|
||||
CHAR(":i", 2, "i", 1, "ï", 6)
|
||||
CHAR(":o", 2, "o", 1, "õ", 6)
|
||||
CHAR(":u", 2, "u", 1, "ü", 6)
|
||||
CHAR(":y", 2, "y", 1, "ÿ", 6)
|
||||
CHAR("\'A", 2, "A", 1, "Á", 6)
|
||||
CHAR("\'E", 2, "E", 1, "É", 6)
|
||||
CHAR("\'I", 2, "I", 1, "Í", 6)
|
||||
CHAR("\'O", 2, "O", 1, "Ó", 6)
|
||||
CHAR("\'U", 2, "U", 1, "Ú", 6)
|
||||
CHAR("\'a", 2, "a", 1, "á", 6)
|
||||
CHAR("\'e", 2, "e", 1, "é", 6)
|
||||
CHAR("\'i", 2, "i", 1, "í", 6)
|
||||
CHAR("\'o", 2, "o", 1, "ó", 6)
|
||||
CHAR("\'u", 2, "u", 1, "ú", 6)
|
||||
CHAR("^A", 2, "A", 1, "Â", 6)
|
||||
CHAR("^E", 2, "E", 1, "Ê", 6)
|
||||
CHAR("^I", 2, "I", 1, "Î", 6)
|
||||
CHAR("^O", 2, "O", 1, "Ô", 6)
|
||||
CHAR("^U", 2, "U", 1, "Û", 6)
|
||||
CHAR("^a", 2, "a", 1, "â", 6)
|
||||
CHAR("^e", 2, "e", 1, "ê", 6)
|
||||
CHAR("^i", 2, "i", 1, "î", 6)
|
||||
CHAR("^o", 2, "o", 1, "ô", 6)
|
||||
CHAR("^u", 2, "u", 1, "û", 6)
|
||||
CHAR("`A", 2, "A", 1, "À", 6)
|
||||
CHAR("`E", 2, "E", 1, "È", 6)
|
||||
CHAR("`I", 2, "I", 1, "Ì", 6)
|
||||
CHAR("`O", 2, "O", 1, "Ò", 6)
|
||||
CHAR("`U", 2, "U", 1, "Ù", 6)
|
||||
CHAR("`a", 2, "a", 1, "à", 6)
|
||||
CHAR("`e", 2, "e", 1, "è", 6)
|
||||
CHAR("`i", 2, "i", 1, "ì", 6)
|
||||
CHAR("`o", 2, "o", 1, "ò", 6)
|
||||
CHAR("`u", 2, "u", 1, "ù", 6)
|
||||
CHAR("~A", 2, "A", 1, "Ã", 6)
|
||||
CHAR("~N", 2, "N", 1, "Ñ", 6)
|
||||
CHAR("~O", 2, "O", 1, "Õ", 6)
|
||||
CHAR("~a", 2, "a", 1, "ã", 6)
|
||||
CHAR("~n", 2, "n", 1, "ñ", 6)
|
||||
CHAR("~o", 2, "o", 1, "õ", 6)
|
||||
|
||||
/* Arrows and lines. */
|
||||
CHAR("<-", 2, "<-", 2, "←", 7)
|
||||
CHAR("->", 2, "->", 2, "→", 7)
|
||||
CHAR("<>", 2, "<>", 2, "↔", 7)
|
||||
CHAR("da", 2, "v", 1, "↓", 7)
|
||||
BOTH("ua", 2, "^", 1, "↑", 7)
|
||||
BOTH("va", 2, "^v", 2, "↕", 7)
|
||||
CHAR("lA", 2, "<=", 2, "⇐", 7)
|
||||
CHAR("rA", 2, "=>", 2, "⇒", 7)
|
||||
CHAR("hA", 2, "<=>", 3, "⇔", 7)
|
||||
CHAR("dA", 2, "v", 1, "⇓", 7)
|
||||
CHAR("uA", 2, "^", 1, "⇑", 7)
|
||||
CHAR("vA", 2, "^=v", 3, "⇕", 7)
|
||||
|
||||
/* Logic. */
|
||||
CHAR("AN", 2, "^", 1, "∧", 7)
|
||||
CHAR("OR", 2, "v", 1, "∨", 7)
|
||||
CHAR("no", 2, "~", 1, "¬", 6)
|
||||
CHAR("tno", 3, "~", 1, "¬", 6)
|
||||
CHAR("te", 2, "3", 1, "∃", 7)
|
||||
CHAR("fa", 2, "V", 1, "∀", 7)
|
||||
CHAR("st", 2, "-)", 2, "∋", 7)
|
||||
CHAR("tf", 2, ".:.", 3, "∴", 7)
|
||||
CHAR("3d", 2, ".:.", 3, "∴", 7)
|
||||
CHAR("or", 2, "|", 1, "|", 1)
|
||||
|
||||
/* Mathematicals. */
|
||||
CHAR("pl", 2, "+", 1, "+", 5)
|
||||
CHAR("mi", 2, "-", 1, "−", 7)
|
||||
CHAR("-", 1, "-", 1, "-", 1)
|
||||
CHAR("-+", 2, "-+", 2, "∓", 7)
|
||||
CHAR("+-", 2, "+-", 2, "±", 6)
|
||||
CHAR("t+-", 3, "+-", 2, "±", 6)
|
||||
CHAR("pc", 2, ".", 1, "·", 6)
|
||||
CHAR("md", 2, ".", 1, "⋅", 7)
|
||||
CHAR("mu", 2, "x", 1, "×", 6)
|
||||
CHAR("tmu", 3, "x", 1, "×", 6)
|
||||
CHAR("c*", 2, "x", 1, "⊗", 7)
|
||||
CHAR("c+", 2, "+", 1, "⊕", 7)
|
||||
CHAR("di", 2, "-:-", 3, "÷", 6)
|
||||
CHAR("tdi", 3, "-:-", 3, "÷", 6)
|
||||
CHAR("f/", 2, "/", 1, "⁄", 7)
|
||||
CHAR("**", 2, "*", 1, "∗", 7)
|
||||
BOTH("<=", 2, "<=", 2, "≤", 7)
|
||||
BOTH(">=", 2, ">=", 2, "≥", 7)
|
||||
CHAR("<<", 2, "<<", 2, "≪", 7)
|
||||
CHAR(">>", 2, ">>", 2, "≫", 7)
|
||||
CHAR("eq", 2, "=", 1, "=", 5)
|
||||
CHAR("!=", 2, "!=", 2, "≠", 7)
|
||||
CHAR("==", 2, "==", 2, "≡", 7)
|
||||
CHAR("ne", 2, "!==", 3, "≢", 7)
|
||||
CHAR("=~", 2, "=~", 2, "≅", 7)
|
||||
CHAR("-~", 2, "-~", 2, "≃", 7)
|
||||
CHAR("ap", 2, "~", 1, "∼", 7)
|
||||
CHAR("~~", 2, "~~", 2, "≈", 7)
|
||||
CHAR("~=", 2, "~=", 2, "≌", 7)
|
||||
CHAR("pt", 2, "oc", 2, "∝", 7)
|
||||
CHAR("es", 2, "{}", 2, "∅", 7)
|
||||
CHAR("mo", 2, "E", 1, "∈", 7)
|
||||
CHAR("nm", 2, "!E", 2, "∉", 7)
|
||||
CHAR("sb", 2, "(=", 2, "⊂", 7)
|
||||
CHAR("nb", 2, "(!=", 3, "⊄", 7)
|
||||
CHAR("sp", 2, "=)", 2, "⊃", 7)
|
||||
CHAR("nc", 2, "!=)", 3, "⊅", 7)
|
||||
CHAR("ib", 2, "(=", 2, "⊆", 7)
|
||||
CHAR("ip", 2, "=)", 2, "⊇", 7)
|
||||
CHAR("ca", 2, "(^)", 3, "∩", 7)
|
||||
CHAR("cu", 2, "U", 1, "∪", 7)
|
||||
CHAR("/_", 2, "/_", 2, "∠", 7)
|
||||
CHAR("pp", 2, "_|_", 3, "⊥", 7)
|
||||
CHAR("is", 2, "I", 1, "∫", 7)
|
||||
CHAR("integral", 8, "I", 1, "∫", 7)
|
||||
CHAR("sum", 3, "E", 1, "∑", 7)
|
||||
CHAR("product", 7, "TT", 2, "∏", 7)
|
||||
CHAR("coproduct", 9, "U", 1, "∐", 7)
|
||||
CHAR("gr", 2, "V", 1, "∇", 7)
|
||||
CHAR("sr", 2, "\\/", 2, "√", 7)
|
||||
CHAR("sqrt", 4, "\\/", 2, "√", 7)
|
||||
CHAR("lc", 2, "|~", 2, "⌈", 7)
|
||||
CHAR("rc", 2, "~|", 2, "⌉", 7)
|
||||
CHAR("lf", 2, "|_", 2, "⌊", 7)
|
||||
CHAR("rf", 2, "_|", 2, "⌋", 7)
|
||||
CHAR("if", 2, "oo", 2, "∞", 7)
|
||||
CHAR("Ah", 2, "N", 1, "ℵ", 7)
|
||||
CHAR("Im", 2, "I", 1, "ℑ", 7)
|
||||
CHAR("Re", 2, "R", 1, "ℜ", 7)
|
||||
CHAR("pd", 2, "a", 1, "∂", 7)
|
||||
CHAR("-h", 2, "/h", 2, "ℏ", 7)
|
||||
|
||||
/* Ligatures. */
|
||||
CHAR("ff", 2, "ff", 2, "ff", 8)
|
||||
CHAR("fi", 2, "fi", 2, "fi", 8)
|
||||
CHAR("fl", 2, "fl", 2, "fl", 8)
|
||||
CHAR("Fi", 2, "ffi", 3, "ffi", 8)
|
||||
CHAR("Fl", 2, "ffl", 3, "ffl", 8)
|
||||
BOTH("AE", 2, "AE", 2, "Æ", 6)
|
||||
BOTH("ae", 2, "ae", 2, "æ", 6)
|
||||
CHAR("OE", 2, "OE", 2, "Œ", 6)
|
||||
CHAR("oe", 2, "oe", 2, "œ", 6)
|
||||
CHAR("ss", 2, "ss", 2, "ß", 6)
|
||||
CHAR("IJ", 2, "IJ", 2, "IJ", 6)
|
||||
CHAR("ij", 2, "ij", 2, "ij", 6)
|
||||
|
||||
/* Special letters. */
|
||||
CHAR("-D", 2, "D", 1, "Ð", 6)
|
||||
CHAR("Sd", 2, "o", 1, "ð", 6)
|
||||
CHAR("TP", 2, "b", 1, "Þ", 6)
|
||||
CHAR("Tp", 2, "b", 1, "þ", 6)
|
||||
CHAR(".i", 2, "i", 1, "ı", 6)
|
||||
CHAR(".j", 2, "j", 1, "ȷ", 6)
|
||||
|
||||
/* Currency. */
|
||||
CHAR("Do", 2, "$", 1, "$", 1)
|
||||
CHAR("ct", 2, "c", 1, "¢", 6)
|
||||
CHAR("Eu", 2, "EUR", 3, "€", 7)
|
||||
CHAR("eu", 2, "EUR", 3, "€", 7)
|
||||
CHAR("Ye", 2, "Y", 1, "¥", 6)
|
||||
CHAR("Po", 2, "L", 1, "£", 6)
|
||||
CHAR("Cs", 2, "x", 1, "¤", 6)
|
||||
CHAR("Fn", 2, "f", 1, "ƒ", 6)
|
||||
|
||||
/* pod2man holdovers. */
|
||||
STRING("--", 2, "--", 2, "—", 7)
|
||||
STRING("PI", 2, "pi", 2, "π", 6)
|
||||
STRING("L\"", 2, "``", 2, "“", 7)
|
||||
STRING("R\"", 2, "\'\'", 2, "”", 7)
|
||||
STRING("C+", 2, "C++", 3, "C++", 3)
|
||||
STRING("C`", 2, "`", 1, "‘", 7)
|
||||
STRING("C\'", 2, "\'", 1, "’", 7)
|
||||
STRING("Aq", 2, "\'", 1, "\'", 1)
|
||||
STRING("^", 1, "^", 1, "^", 1)
|
||||
STRING(",", 1, ",", 1, ",", 1)
|
||||
STRING("~", 1, "~", 1, "~", 1)
|
||||
STRING("/", 1, "/", 1, "/", 1)
|
||||
STRING(":", 1, "\"", 1, "̈", 6)
|
||||
STRING("8", 1, "B", 1, "β", 6)
|
||||
STRING("o", 1, "o", 1, "°", 6)
|
||||
STRING("D-", 2, "D", 1, "Ð", 6)
|
||||
STRING("d-", 2, "o", 1, "ð", 6)
|
||||
STRING("Th", 2, "b", 1, "Þ", 6)
|
||||
STRING("th", 2, "b", 1, "þ", 6)
|
||||
|
||||
/* Old style. */
|
||||
STRING("Am", 2, "&", 1, "&", 5)
|
||||
STRING("Ba", 2, "|", 1, "|", 1)
|
||||
STRING("Ge", 2, ">=", 2, "≥", 7)
|
||||
STRING("Gt", 2, ">", 1, ">", 4)
|
||||
STRING("If", 2, "infinity", 8, "infinity", 8)
|
||||
STRING("Le", 2, "<=", 2, "≤", 7)
|
||||
STRING("Lq", 2, "``", 2, "“", 7)
|
||||
STRING("Lt", 2, "<", 1, "<", 4)
|
||||
STRING("Na", 2, "NaN", 3, "NaN", 3)
|
||||
STRING("Ne", 2, "!=", 2, "≠", 7)
|
||||
STRING("Pi", 2, "pi", 2, "π", 6)
|
||||
STRING("Pm", 2, "+-", 2, "±", 6)
|
||||
STRING("R", 1, "(R)", 3, "®", 6)
|
||||
STRING("Rq", 2, "\'\'", 2, "”", 7)
|
||||
STRING("Tm", 2, "tm", 2, "™", 7)
|
||||
STRING("left-bracket", 12, "[", 1, "[", 1)
|
||||
STRING("left-parenthesis", 16, "(", 1, "(", 1)
|
||||
STRING("left-singlequote", 16, "`", 1, "‘", 7)
|
||||
STRING("lp", 2, "(", 1, "(", 1)
|
||||
STRING("q", 1, "\"", 1, """, 6)
|
||||
STRING("quote-left", 10, "`", 1, "‘", 7)
|
||||
STRING("quote-right", 11, "\'", 1, "’", 7)
|
||||
STRING("right-bracket", 13, "]", 1, "]", 1)
|
||||
STRING("right-parenthesis", 17, ")", 1, ")", 1)
|
||||
STRING("right-singlequote", 17, "\'", 1, "’", 7)
|
||||
STRING("rp", 2, ")", 1, ")", 1)
|
||||
|
||||
/* Lines. */
|
||||
CHAR("ba", 2, "|", 1, "|", 6)
|
||||
CHAR("br", 2, "|", 1, "│", 7)
|
||||
CHAR("ul", 2, "_", 1, "_", 5)
|
||||
CHAR("rl", 2, "-", 1, "‾", 7)
|
||||
CHAR("bb", 2, "|", 1, "¦", 6)
|
||||
CHAR("sl", 2, "/", 1, "/", 5)
|
||||
CHAR("rs", 2, "\\", 1, "\", 5)
|
||||
|
||||
/* Text markers. */
|
||||
CHAR("ci", 2, "o", 1, "○", 7)
|
||||
CHAR("bu", 2, "o", 1, "•", 7)
|
||||
CHAR("dd", 2, "=", 1, "‡", 7)
|
||||
CHAR("dg", 2, "-", 1, "†", 7)
|
||||
CHAR("lz", 2, "<>", 2, "◊", 7)
|
||||
CHAR("sq", 2, "[]", 2, "□", 7)
|
||||
CHAR("ps", 2, "9|", 2, "¶", 6)
|
||||
CHAR("sc", 2, "S", 1, "§", 6)
|
||||
CHAR("lh", 2, "<=", 2, "☜", 7)
|
||||
CHAR("rh", 2, "=>", 2, "☞", 7)
|
||||
CHAR("at", 2, "@", 1, "@", 5)
|
||||
CHAR("sh", 2, "#", 1, "#", 5)
|
||||
CHAR("CR", 2, "_|", 2, "↵", 7)
|
||||
CHAR("OK", 2, "\\/", 2, "✓", 8)
|
||||
|
||||
/* Legal symbols. */
|
||||
CHAR("co", 2, "(C)", 3, "©", 6)
|
||||
CHAR("rg", 2, "(R)", 3, "®", 6)
|
||||
CHAR("tm", 2, "tm", 2, "™", 7)
|
||||
|
||||
/* Punctuation. */
|
||||
CHAR(".", 1, ".", 1, ".", 1)
|
||||
CHAR("r!", 2, "i", 1, "¡", 6)
|
||||
CHAR("r?", 2, "c", 1, "¿", 6)
|
||||
CHAR("em", 2, "--", 2, "—", 7)
|
||||
CHAR("en", 2, "-", 1, "–", 7)
|
||||
CHAR("hy", 2, "-", 1, "‐", 7)
|
||||
CHAR("e", 1, "\\", 1, "\\", 1)
|
||||
|
||||
/* Units. */
|
||||
CHAR("de", 2, "o", 1, "°", 6)
|
||||
CHAR("%0", 2, "%o", 2, "‰", 7)
|
||||
CHAR("fm", 2, "\'", 1, "′", 7)
|
||||
CHAR("sd", 2, "\"", 1, "″", 7)
|
||||
CHAR("mc", 2, "mu", 2, "µ", 6)
|
||||
|
||||
CHAR_TBL_END
|
95
commands/mdocml/compat.c
Normal file
95
commands/mdocml/compat.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
int dummy; /* To prevent an empty object file */
|
||||
|
||||
#ifndef HAVE_STRLCAT
|
||||
/*
|
||||
* Appends src to string dst of size siz (unlike strncat, siz is the
|
||||
* full size of dst, not space left). At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
||||
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
||||
* If retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcat(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
size_t dlen;
|
||||
|
||||
/* Find the end of dst and adjust bytes left but don't go past end */
|
||||
while (n-- != 0 && *d != '\0')
|
||||
d++;
|
||||
dlen = d - dst;
|
||||
n = siz - dlen;
|
||||
|
||||
if (n == 0)
|
||||
return(dlen + strlen(s));
|
||||
while (*s != '\0') {
|
||||
if (n != 1) {
|
||||
*d++ = *s;
|
||||
n--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
*d = '\0';
|
||||
|
||||
return(dlen + (s - src)); /* count does not include NUL */
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
/*
|
||||
* Copy src to string dst of size siz. At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz == 0).
|
||||
* Returns strlen(src); if retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcpy(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
|
||||
/* Copy as many bytes as will fit */
|
||||
if (n != 0) {
|
||||
while (--n != 0) {
|
||||
if ((*d++ = *s++) == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||
if (n == 0) {
|
||||
if (siz != 0)
|
||||
*d = '\0'; /* NUL-terminate dst */
|
||||
while (*s++)
|
||||
;
|
||||
}
|
||||
|
||||
return(s - src - 1); /* count does not include NUL */
|
||||
}
|
||||
#endif
|
20
commands/mdocml/config.h
Normal file
20
commands/mdocml/config.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
#ifndef MANDOC_CONFIG_H
|
||||
#define MANDOC_CONFIG_H
|
||||
|
||||
#define HAVE_STRLCAT
|
||||
#define HAVE_STRLCPY
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define inline
|
||||
#endif
|
||||
|
||||
#ifndef _DIAGASSERT
|
||||
#define _DIAGASSERT assert
|
||||
#endif
|
||||
|
||||
#endif /* MANDOC_CONFIG_H */
|
||||
|
72
commands/mdocml/example.style.css
Normal file
72
commands/mdocml/example.style.css
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* $Id: example.style.css,v 1.20 2010/04/08 08:17:55 kristaps Exp $ */
|
||||
|
||||
div.body { font-family: monospace;
|
||||
min-width: 580px; width: 580px; } /* Top-most div tag. */
|
||||
|
||||
div.sec-head { font-weight: bold; font-style: normal; } /* Sections (Sh). */
|
||||
div.sec-body { }
|
||||
div.sec-block { }
|
||||
|
||||
div.ssec-head { font-weight: bold; font-style: normal; } /* Sub-sections (Ss). */
|
||||
div.ssec-body { }
|
||||
div.ssec-block { }
|
||||
|
||||
span.addr { } /* Address (Ad). */
|
||||
span.arg { font-style: italic; font-weight: normal; } /* Command argument (Ar). */
|
||||
span.author { } /* Author name (An). */
|
||||
span.bold { font-weight: bold; font-style: normal; } /* Generically bold (SB, BI, IB, BR, RB, B). */
|
||||
span.cmd { font-weight: bold; font-style: normal; } /* Command (Cm). */
|
||||
span.config { font-weight: bold; font-style: normal; } /* Config statement (Cd). */
|
||||
span.define { } /* Defines (Dv). */
|
||||
span.desc { } /* Nd. After em-dash. */
|
||||
span.diag { font-weight: bold; font-style: normal; } /* Diagnostic (Bl -diag). */
|
||||
span.emph { font-style: italic; font-weight: normal; } /* Emphasis (Em). */
|
||||
span.env { } /* Environment variables (Ev). */
|
||||
span.errno { } /* Error string (Er). */
|
||||
span.farg { font-style: italic; font-weight: normal; } /* Function argument (Fa, Fn). */
|
||||
span.file { font-style: italic; font-weight: normal; } /* File (Pa). */
|
||||
span.flag { font-weight: bold; font-style: normal; } /* Flag (Fl, Cm). */
|
||||
span.fname { font-weight: bold; font-style: normal; } /* Function name (Fa, Fn, Rv). */
|
||||
span.ftype { font-style: italic; font-weight: normal; } /* Function types (Ft, Fn). */
|
||||
span.includes { font-weight: bold; font-style: normal; } /* Header includes (In). */
|
||||
span.italic { font-style: italic; font-weight: normal; } /* Generically italic (BI, IB, I). */
|
||||
span.lib { } /* Library (Lb). */
|
||||
span.lit { } /* Literals (Bf -literal). */
|
||||
span.macro { font-weight: bold; font-style: normal; } /* Macro-ish thing (Fd). */
|
||||
span.name { font-weight: bold; font-style: normal; } /* Name of utility (Nm). */
|
||||
span.opt { } /* Options (Op, Oo/Oc). */
|
||||
span.ref { } /* Citations (Rs). */
|
||||
span.ref-auth { } /* Reference author (%A). */
|
||||
span.ref-book { font-style: italic; font-weight: normal; } /* Reference book (%B). */
|
||||
span.ref-city { } /* Reference city (%C). */
|
||||
span.ref-date { } /* Reference date (%D). */
|
||||
span.ref-issue { font-style: italic; font-weight: normal; } /* Reference issuer/publisher (%I). */
|
||||
span.ref-jrnl { font-style: italic; font-weight: normal; } /* Reference journal (%J). */
|
||||
span.ref-num { } /* Reference number (%N). */
|
||||
span.ref-opt { } /* Reference optionals (%O). */
|
||||
span.ref-page { } /* Reference page (%P). */
|
||||
span.ref-corp { } /* Reference corporate/foreign author (%Q). */
|
||||
span.ref-rep { } /* Reference report (%R). */
|
||||
span.ref-title { text-decoration: underline; } /* Reference title (%T). */
|
||||
span.ref-vol { } /* Reference volume (%V). */
|
||||
span.roman { font-style: normal; font-weight: normal; } /* Generic font. */
|
||||
span.small { font-size: smaller; } /* Generically small (SB, SM). */
|
||||
span.symb { font-weight: bold; font-style: normal; } /* Symbols. */
|
||||
span.type { font-style: italic; font-weight: normal; } /* Variable types (Vt). */
|
||||
span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */
|
||||
span.utility { font-weight: bold; font-style: normal; } /* Name of utility (Ex). */
|
||||
span.var { font-weight: bold; font-style: normal; } /* Variables (Rv). */
|
||||
|
||||
a.link-ext { } /* Off-site link (Lk). */
|
||||
a.link-includes { } /* Include-file link (In). */
|
||||
a.link-mail { } /* Mailto links (Mt). */
|
||||
a.link-man { } /* Manual links (Xr). */
|
||||
a.link-ref { } /* Reference section links (%Q). */
|
||||
a.link-sec { } /* Section links (Sx). */
|
||||
|
||||
div.emph { font-style: italic; font-weight: normal; } /* Emphasis (Bl -emphasis). */
|
||||
div.lit { } /* Literal (D1, Bd -literal, Dl, Bd -literal). */
|
||||
div.symb { font-weight: bold; font-style: normal; } /* Symbols (Bl -symbolic). */
|
||||
|
||||
table.footer { } /* Document footer. */
|
||||
table.header { } /* Document header. */
|
BIN
commands/mdocml/external.png
Normal file
BIN
commands/mdocml/external.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 165 B |
754
commands/mdocml/html.c
Normal file
754
commands/mdocml/html.c
Normal file
|
@ -0,0 +1,754 @@
|
|||
/* $Id: html.c,v 1.102 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "out.h"
|
||||
#include "chars.h"
|
||||
#include "html.h"
|
||||
#include "main.h"
|
||||
|
||||
struct htmldata {
|
||||
const char *name;
|
||||
int flags;
|
||||
#define HTML_CLRLINE (1 << 0)
|
||||
#define HTML_NOSTACK (1 << 1)
|
||||
#define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */
|
||||
};
|
||||
|
||||
static const struct htmldata htmltags[TAG_MAX] = {
|
||||
{"html", HTML_CLRLINE}, /* TAG_HTML */
|
||||
{"head", HTML_CLRLINE}, /* TAG_HEAD */
|
||||
{"body", HTML_CLRLINE}, /* TAG_BODY */
|
||||
{"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */
|
||||
{"title", HTML_CLRLINE}, /* TAG_TITLE */
|
||||
{"div", HTML_CLRLINE}, /* TAG_DIV */
|
||||
{"h1", 0}, /* TAG_H1 */
|
||||
{"h2", 0}, /* TAG_H2 */
|
||||
{"span", 0}, /* TAG_SPAN */
|
||||
{"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */
|
||||
{"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */
|
||||
{"a", 0}, /* TAG_A */
|
||||
{"table", HTML_CLRLINE}, /* TAG_TABLE */
|
||||
{"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */
|
||||
{"tr", HTML_CLRLINE}, /* TAG_TR */
|
||||
{"td", HTML_CLRLINE}, /* TAG_TD */
|
||||
{"li", HTML_CLRLINE}, /* TAG_LI */
|
||||
{"ul", HTML_CLRLINE}, /* TAG_UL */
|
||||
{"ol", HTML_CLRLINE}, /* TAG_OL */
|
||||
};
|
||||
|
||||
static const char *const htmlfonts[HTMLFONT_MAX] = {
|
||||
"roman",
|
||||
"bold",
|
||||
"italic"
|
||||
};
|
||||
|
||||
static const char *const htmlattrs[ATTR_MAX] = {
|
||||
"http-equiv",
|
||||
"content",
|
||||
"name",
|
||||
"rel",
|
||||
"href",
|
||||
"type",
|
||||
"media",
|
||||
"class",
|
||||
"style",
|
||||
"width",
|
||||
"valign",
|
||||
"target",
|
||||
"id",
|
||||
"summary",
|
||||
};
|
||||
|
||||
static void print_spec(struct html *, const char *, size_t);
|
||||
static void print_res(struct html *, const char *, size_t);
|
||||
static void print_ctag(struct html *, enum htmltag);
|
||||
static void print_doctype(struct html *);
|
||||
static void print_xmltype(struct html *);
|
||||
static int print_encode(struct html *, const char *, int);
|
||||
static void print_metaf(struct html *, enum roffdeco);
|
||||
static void print_attr(struct html *,
|
||||
const char *, const char *);
|
||||
static void *ml_alloc(char *, enum htmltype);
|
||||
|
||||
|
||||
static void *
|
||||
ml_alloc(char *outopts, enum htmltype type)
|
||||
{
|
||||
struct html *h;
|
||||
const char *toks[4];
|
||||
char *v;
|
||||
|
||||
toks[0] = "style";
|
||||
toks[1] = "man";
|
||||
toks[2] = "includes";
|
||||
toks[3] = NULL;
|
||||
|
||||
h = calloc(1, sizeof(struct html));
|
||||
if (NULL == h) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
h->type = type;
|
||||
h->tags.head = NULL;
|
||||
h->ords.head = NULL;
|
||||
h->symtab = chars_init(CHARS_HTML);
|
||||
|
||||
while (outopts && *outopts)
|
||||
switch (getsubopt(&outopts, UNCONST(toks), &v)) {
|
||||
case (0):
|
||||
h->style = v;
|
||||
break;
|
||||
case (1):
|
||||
h->base_man = v;
|
||||
break;
|
||||
case (2):
|
||||
h->base_includes = v;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(h);
|
||||
}
|
||||
|
||||
void *
|
||||
html_alloc(char *outopts)
|
||||
{
|
||||
|
||||
return(ml_alloc(outopts, HTML_HTML_4_01_STRICT));
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
xhtml_alloc(char *outopts)
|
||||
{
|
||||
|
||||
return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
html_free(void *p)
|
||||
{
|
||||
struct tag *tag;
|
||||
struct ord *ord;
|
||||
struct html *h;
|
||||
|
||||
h = (struct html *)p;
|
||||
|
||||
while ((ord = h->ords.head) != NULL) {
|
||||
h->ords.head = ord->next;
|
||||
free(ord);
|
||||
}
|
||||
|
||||
while ((tag = h->tags.head) != NULL) {
|
||||
h->tags.head = tag->next;
|
||||
free(tag);
|
||||
}
|
||||
|
||||
if (h->symtab)
|
||||
chars_free(h->symtab);
|
||||
|
||||
free(h);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_gen_head(struct html *h)
|
||||
{
|
||||
struct htmlpair tag[4];
|
||||
|
||||
tag[0].key = ATTR_HTTPEQUIV;
|
||||
tag[0].val = "Content-Type";
|
||||
tag[1].key = ATTR_CONTENT;
|
||||
tag[1].val = "text/html; charset=utf-8";
|
||||
print_otag(h, TAG_META, 2, tag);
|
||||
|
||||
tag[0].key = ATTR_NAME;
|
||||
tag[0].val = "resource-type";
|
||||
tag[1].key = ATTR_CONTENT;
|
||||
tag[1].val = "document";
|
||||
print_otag(h, TAG_META, 2, tag);
|
||||
|
||||
if (h->style) {
|
||||
tag[0].key = ATTR_REL;
|
||||
tag[0].val = "stylesheet";
|
||||
tag[1].key = ATTR_HREF;
|
||||
tag[1].val = h->style;
|
||||
tag[2].key = ATTR_TYPE;
|
||||
tag[2].val = "text/css";
|
||||
tag[3].key = ATTR_MEDIA;
|
||||
tag[3].val = "all";
|
||||
print_otag(h, TAG_LINK, 4, tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_spec(struct html *h, const char *p, size_t len)
|
||||
{
|
||||
const char *rhs;
|
||||
size_t sz;
|
||||
|
||||
rhs = chars_a2ascii(h->symtab, p, len, &sz);
|
||||
|
||||
if (NULL == rhs)
|
||||
return;
|
||||
fwrite(rhs, 1, sz, stdout);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_res(struct html *h, const char *p, size_t len)
|
||||
{
|
||||
const char *rhs;
|
||||
size_t sz;
|
||||
|
||||
rhs = chars_a2res(h->symtab, p, len, &sz);
|
||||
|
||||
if (NULL == rhs)
|
||||
return;
|
||||
fwrite(rhs, 1, sz, stdout);
|
||||
}
|
||||
|
||||
|
||||
struct tag *
|
||||
print_ofont(struct html *h, enum htmlfont font)
|
||||
{
|
||||
struct htmlpair tag;
|
||||
|
||||
h->metal = h->metac;
|
||||
h->metac = font;
|
||||
|
||||
/* FIXME: DECO_ROMAN should just close out preexisting. */
|
||||
|
||||
if (h->metaf && h->tags.head == h->metaf)
|
||||
print_tagq(h, h->metaf);
|
||||
|
||||
PAIR_CLASS_INIT(&tag, htmlfonts[font]);
|
||||
h->metaf = print_otag(h, TAG_SPAN, 1, &tag);
|
||||
return(h->metaf);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_metaf(struct html *h, enum roffdeco deco)
|
||||
{
|
||||
enum htmlfont font;
|
||||
|
||||
switch (deco) {
|
||||
case (DECO_PREVIOUS):
|
||||
font = h->metal;
|
||||
break;
|
||||
case (DECO_ITALIC):
|
||||
font = HTMLFONT_ITALIC;
|
||||
break;
|
||||
case (DECO_BOLD):
|
||||
font = HTMLFONT_BOLD;
|
||||
break;
|
||||
case (DECO_ROMAN):
|
||||
font = HTMLFONT_NONE;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
(void)print_ofont(h, font);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
print_encode(struct html *h, const char *p, int norecurse)
|
||||
{
|
||||
size_t sz;
|
||||
int len, nospace;
|
||||
const char *seq;
|
||||
enum roffdeco deco;
|
||||
static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' };
|
||||
|
||||
nospace = 0;
|
||||
|
||||
for (; *p; p++) {
|
||||
sz = strcspn(p, rejs);
|
||||
|
||||
fwrite(p, 1, sz, stdout);
|
||||
p += /* LINTED */
|
||||
sz;
|
||||
|
||||
if ('<' == *p) {
|
||||
printf("<");
|
||||
continue;
|
||||
} else if ('>' == *p) {
|
||||
printf(">");
|
||||
continue;
|
||||
} else if ('&' == *p) {
|
||||
printf("&");
|
||||
continue;
|
||||
} else if (ASCII_HYPH == *p) {
|
||||
/*
|
||||
* Note: "soft hyphens" aren't graphically
|
||||
* displayed when not breaking the text; we want
|
||||
* them to be displayed.
|
||||
*/
|
||||
/*printf("­");*/
|
||||
putchar('-');
|
||||
continue;
|
||||
} else if ('\0' == *p)
|
||||
break;
|
||||
|
||||
seq = ++p;
|
||||
len = a2roffdeco(&deco, &seq, &sz);
|
||||
|
||||
switch (deco) {
|
||||
case (DECO_RESERVED):
|
||||
print_res(h, seq, sz);
|
||||
break;
|
||||
case (DECO_SPECIAL):
|
||||
print_spec(h, seq, sz);
|
||||
break;
|
||||
case (DECO_PREVIOUS):
|
||||
/* FALLTHROUGH */
|
||||
case (DECO_BOLD):
|
||||
/* FALLTHROUGH */
|
||||
case (DECO_ITALIC):
|
||||
/* FALLTHROUGH */
|
||||
case (DECO_ROMAN):
|
||||
if (norecurse)
|
||||
break;
|
||||
print_metaf(h, deco);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
p += len - 1;
|
||||
|
||||
if (DECO_NOSPACE == deco && '\0' == *(p + 1))
|
||||
nospace = 1;
|
||||
}
|
||||
|
||||
return(nospace);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_attr(struct html *h, const char *key, const char *val)
|
||||
{
|
||||
printf(" %s=\"", key);
|
||||
(void)print_encode(h, val, 1);
|
||||
putchar('\"');
|
||||
}
|
||||
|
||||
|
||||
struct tag *
|
||||
print_otag(struct html *h, enum htmltag tag,
|
||||
int sz, const struct htmlpair *p)
|
||||
{
|
||||
int i;
|
||||
struct tag *t;
|
||||
|
||||
/* Push this tags onto the stack of open scopes. */
|
||||
|
||||
if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
|
||||
t = malloc(sizeof(struct tag));
|
||||
if (NULL == t) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
t->tag = tag;
|
||||
t->next = h->tags.head;
|
||||
h->tags.head = t;
|
||||
} else
|
||||
t = NULL;
|
||||
|
||||
if ( ! (HTML_NOSPACE & h->flags))
|
||||
if ( ! (HTML_CLRLINE & htmltags[tag].flags))
|
||||
putchar(' ');
|
||||
|
||||
/* Print out the tag name and attributes. */
|
||||
|
||||
printf("<%s", htmltags[tag].name);
|
||||
for (i = 0; i < sz; i++)
|
||||
print_attr(h, htmlattrs[p[i].key], p[i].val);
|
||||
|
||||
/* Add non-overridable attributes. */
|
||||
|
||||
if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) {
|
||||
print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml");
|
||||
print_attr(h, "xml:lang", "en");
|
||||
print_attr(h, "lang", "en");
|
||||
}
|
||||
|
||||
/* Accomodate for XML "well-formed" singleton escaping. */
|
||||
|
||||
if (HTML_AUTOCLOSE & htmltags[tag].flags)
|
||||
switch (h->type) {
|
||||
case (HTML_XHTML_1_0_STRICT):
|
||||
putchar('/');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
putchar('>');
|
||||
|
||||
h->flags |= HTML_NOSPACE;
|
||||
return(t);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_ctag(struct html *h, enum htmltag tag)
|
||||
{
|
||||
|
||||
printf("</%s>", htmltags[tag].name);
|
||||
if (HTML_CLRLINE & htmltags[tag].flags) {
|
||||
h->flags |= HTML_NOSPACE;
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_gen_decls(struct html *h)
|
||||
{
|
||||
|
||||
print_xmltype(h);
|
||||
print_doctype(h);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_xmltype(struct html *h)
|
||||
{
|
||||
|
||||
if (HTML_XHTML_1_0_STRICT == h->type)
|
||||
printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_doctype(struct html *h)
|
||||
{
|
||||
const char *doctype;
|
||||
const char *dtd;
|
||||
const char *name;
|
||||
|
||||
switch (h->type) {
|
||||
case (HTML_HTML_4_01_STRICT):
|
||||
name = "HTML";
|
||||
doctype = "-//W3C//DTD HTML 4.01//EN";
|
||||
dtd = "http://www.w3.org/TR/html4/strict.dtd";
|
||||
break;
|
||||
default:
|
||||
name = "html";
|
||||
doctype = "-//W3C//DTD XHTML 1.0 Strict//EN";
|
||||
dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
|
||||
break;
|
||||
}
|
||||
|
||||
printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n",
|
||||
name, doctype, dtd);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_text(struct html *h, const char *p)
|
||||
{
|
||||
|
||||
if (*p && 0 == *(p + 1))
|
||||
switch (*p) {
|
||||
case('.'):
|
||||
/* FALLTHROUGH */
|
||||
case(','):
|
||||
/* FALLTHROUGH */
|
||||
case(';'):
|
||||
/* FALLTHROUGH */
|
||||
case(':'):
|
||||
/* FALLTHROUGH */
|
||||
case('?'):
|
||||
/* FALLTHROUGH */
|
||||
case('!'):
|
||||
/* FALLTHROUGH */
|
||||
case(')'):
|
||||
/* FALLTHROUGH */
|
||||
case(']'):
|
||||
if ( ! (HTML_IGNDELIM & h->flags))
|
||||
h->flags |= HTML_NOSPACE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! (h->flags & HTML_NOSPACE))
|
||||
putchar(' ');
|
||||
|
||||
assert(p);
|
||||
if ( ! print_encode(h, p, 0))
|
||||
h->flags &= ~HTML_NOSPACE;
|
||||
|
||||
/*
|
||||
* Note that we don't process the pipe: the parser sees it as
|
||||
* punctuation, but we don't in terms of typography.
|
||||
*/
|
||||
if (*p && 0 == *(p + 1))
|
||||
switch (*p) {
|
||||
case('('):
|
||||
/* FALLTHROUGH */
|
||||
case('['):
|
||||
h->flags |= HTML_NOSPACE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_tagq(struct html *h, const struct tag *until)
|
||||
{
|
||||
struct tag *tag;
|
||||
|
||||
while ((tag = h->tags.head) != NULL) {
|
||||
if (tag == h->metaf)
|
||||
h->metaf = NULL;
|
||||
print_ctag(h, tag->tag);
|
||||
h->tags.head = tag->next;
|
||||
free(tag);
|
||||
if (until && tag == until)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_stagq(struct html *h, const struct tag *suntil)
|
||||
{
|
||||
struct tag *tag;
|
||||
|
||||
while ((tag = h->tags.head) != NULL) {
|
||||
if (suntil && tag == suntil)
|
||||
return;
|
||||
if (tag == h->metaf)
|
||||
h->metaf = NULL;
|
||||
print_ctag(h, tag->tag);
|
||||
h->tags.head = tag->next;
|
||||
free(tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
bufinit(struct html *h)
|
||||
{
|
||||
|
||||
h->buf[0] = '\0';
|
||||
h->buflen = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
bufcat_style(struct html *h, const char *key, const char *val)
|
||||
{
|
||||
|
||||
bufcat(h, key);
|
||||
bufncat(h, ":", 1);
|
||||
bufcat(h, val);
|
||||
bufncat(h, ";", 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
bufcat(struct html *h, const char *p)
|
||||
{
|
||||
|
||||
bufncat(h, p, strlen(p));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
buffmt(struct html *h, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
(void)vsnprintf(h->buf + (int)h->buflen,
|
||||
BUFSIZ - h->buflen - 1, fmt, ap);
|
||||
va_end(ap);
|
||||
h->buflen = strlen(h->buf);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
bufncat(struct html *h, const char *p, size_t sz)
|
||||
{
|
||||
|
||||
if (h->buflen + sz > BUFSIZ - 1)
|
||||
sz = BUFSIZ - 1 - h->buflen;
|
||||
|
||||
(void)strncat(h->buf, p, sz);
|
||||
h->buflen += sz;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
buffmt_includes(struct html *h, const char *name)
|
||||
{
|
||||
const char *p, *pp;
|
||||
|
||||
pp = h->base_includes;
|
||||
|
||||
while (NULL != (p = strchr(pp, '%'))) {
|
||||
bufncat(h, pp, (size_t)(p - pp));
|
||||
switch (*(p + 1)) {
|
||||
case('I'):
|
||||
bufcat(h, name);
|
||||
break;
|
||||
default:
|
||||
bufncat(h, p, 2);
|
||||
break;
|
||||
}
|
||||
pp = p + 2;
|
||||
}
|
||||
if (pp)
|
||||
bufcat(h, pp);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
buffmt_man(struct html *h,
|
||||
const char *name, const char *sec)
|
||||
{
|
||||
const char *p, *pp;
|
||||
|
||||
pp = h->base_man;
|
||||
|
||||
/* LINTED */
|
||||
while (NULL != (p = strchr(pp, '%'))) {
|
||||
bufncat(h, pp, (size_t)(p - pp));
|
||||
switch (*(p + 1)) {
|
||||
case('S'):
|
||||
bufcat(h, sec ? sec : "1");
|
||||
break;
|
||||
case('N'):
|
||||
buffmt(h, name);
|
||||
break;
|
||||
default:
|
||||
bufncat(h, p, 2);
|
||||
break;
|
||||
}
|
||||
pp = p + 2;
|
||||
}
|
||||
if (pp)
|
||||
bufcat(h, pp);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
bufcat_su(struct html *h, const char *p, const struct roffsu *su)
|
||||
{
|
||||
double v;
|
||||
const char *u;
|
||||
|
||||
v = su->scale;
|
||||
|
||||
switch (su->unit) {
|
||||
case (SCALE_CM):
|
||||
u = "cm";
|
||||
break;
|
||||
case (SCALE_IN):
|
||||
u = "in";
|
||||
break;
|
||||
case (SCALE_PC):
|
||||
u = "pc";
|
||||
break;
|
||||
case (SCALE_PT):
|
||||
u = "pt";
|
||||
break;
|
||||
case (SCALE_EM):
|
||||
u = "em";
|
||||
break;
|
||||
case (SCALE_MM):
|
||||
if (0 == (v /= 100))
|
||||
v = 1;
|
||||
u = "em";
|
||||
break;
|
||||
case (SCALE_EN):
|
||||
u = "ex";
|
||||
break;
|
||||
case (SCALE_BU):
|
||||
u = "ex";
|
||||
break;
|
||||
case (SCALE_VS):
|
||||
u = "em";
|
||||
break;
|
||||
default:
|
||||
u = "ex";
|
||||
break;
|
||||
}
|
||||
|
||||
if (su->pt)
|
||||
buffmt(h, "%s: %f%s;", p, v, u);
|
||||
else
|
||||
/* LINTED */
|
||||
buffmt(h, "%s: %d%s;", p, (int)v, u);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
html_idcat(char *dst, const char *src, int sz)
|
||||
{
|
||||
int ssz;
|
||||
|
||||
assert(sz);
|
||||
|
||||
/* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
|
||||
|
||||
for ( ; *dst != '\0' && sz; dst++, sz--)
|
||||
/* Jump to end. */ ;
|
||||
|
||||
assert(sz > 2);
|
||||
|
||||
/* We can't start with a number (bah). */
|
||||
|
||||
*dst++ = 'x';
|
||||
*dst = '\0';
|
||||
sz--;
|
||||
|
||||
for ( ; *src != '\0' && sz > 1; src++) {
|
||||
ssz = snprintf(dst, (size_t)sz, "%.2x", *src);
|
||||
sz -= ssz;
|
||||
dst += ssz;
|
||||
}
|
||||
}
|
156
commands/mdocml/html.h
Normal file
156
commands/mdocml/html.h
Normal file
|
@ -0,0 +1,156 @@
|
|||
/* $Id: html.h,v 1.24 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef HTML_H
|
||||
#define HTML_H
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
enum htmltag {
|
||||
TAG_HTML,
|
||||
TAG_HEAD,
|
||||
TAG_BODY,
|
||||
TAG_META,
|
||||
TAG_TITLE,
|
||||
TAG_DIV,
|
||||
TAG_H1,
|
||||
TAG_H2,
|
||||
TAG_SPAN,
|
||||
TAG_LINK,
|
||||
TAG_BR,
|
||||
TAG_A,
|
||||
TAG_TABLE,
|
||||
TAG_COL,
|
||||
TAG_TR,
|
||||
TAG_TD,
|
||||
TAG_LI,
|
||||
TAG_UL,
|
||||
TAG_OL,
|
||||
TAG_MAX
|
||||
};
|
||||
|
||||
enum htmlattr {
|
||||
ATTR_HTTPEQUIV,
|
||||
ATTR_CONTENT,
|
||||
ATTR_NAME,
|
||||
ATTR_REL,
|
||||
ATTR_HREF,
|
||||
ATTR_TYPE,
|
||||
ATTR_MEDIA,
|
||||
ATTR_CLASS,
|
||||
ATTR_STYLE,
|
||||
ATTR_WIDTH,
|
||||
ATTR_VALIGN,
|
||||
ATTR_TARGET,
|
||||
ATTR_ID,
|
||||
ATTR_SUMMARY,
|
||||
ATTR_MAX
|
||||
};
|
||||
|
||||
enum htmlfont {
|
||||
HTMLFONT_NONE = 0,
|
||||
HTMLFONT_BOLD,
|
||||
HTMLFONT_ITALIC,
|
||||
HTMLFONT_MAX
|
||||
};
|
||||
|
||||
struct tag {
|
||||
struct tag *next;
|
||||
enum htmltag tag;
|
||||
};
|
||||
|
||||
struct ord {
|
||||
struct ord *next;
|
||||
const void *cookie;
|
||||
int pos;
|
||||
};
|
||||
|
||||
struct tagq {
|
||||
struct tag *head;
|
||||
};
|
||||
struct ordq {
|
||||
struct ord *head;
|
||||
};
|
||||
|
||||
struct htmlpair {
|
||||
enum htmlattr key;
|
||||
const char *val;
|
||||
};
|
||||
|
||||
#define PAIR_INIT(p, t, v) \
|
||||
do { \
|
||||
(p)->key = (t); \
|
||||
(p)->val = (v); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
#define PAIR_ID_INIT(p, v) PAIR_INIT(p, ATTR_ID, v)
|
||||
#define PAIR_CLASS_INIT(p, v) PAIR_INIT(p, ATTR_CLASS, v)
|
||||
#define PAIR_HREF_INIT(p, v) PAIR_INIT(p, ATTR_HREF, v)
|
||||
#define PAIR_STYLE_INIT(p, h) PAIR_INIT(p, ATTR_STYLE, (h)->buf)
|
||||
#define PAIR_SUMMARY_INIT(p, v) PAIR_INIT(p, ATTR_SUMMARY, v)
|
||||
|
||||
enum htmltype {
|
||||
HTML_HTML_4_01_STRICT,
|
||||
HTML_XHTML_1_0_STRICT
|
||||
};
|
||||
|
||||
struct html {
|
||||
int flags;
|
||||
#define HTML_NOSPACE (1 << 0)
|
||||
#define HTML_IGNDELIM (1 << 2)
|
||||
struct tagq tags;
|
||||
struct ordq ords;
|
||||
void *symtab;
|
||||
char *base;
|
||||
char *base_man;
|
||||
char *base_includes;
|
||||
char *style;
|
||||
char buf[BUFSIZ];
|
||||
size_t buflen;
|
||||
struct tag *metaf;
|
||||
enum htmlfont metal;
|
||||
enum htmlfont metac;
|
||||
enum htmltype type;
|
||||
};
|
||||
|
||||
struct roffsu;
|
||||
|
||||
void print_gen_decls(struct html *);
|
||||
void print_gen_head(struct html *);
|
||||
struct tag *print_ofont(struct html *, enum htmlfont);
|
||||
struct tag *print_otag(struct html *, enum htmltag,
|
||||
int, const struct htmlpair *);
|
||||
void print_tagq(struct html *, const struct tag *);
|
||||
void print_stagq(struct html *, const struct tag *);
|
||||
void print_text(struct html *, const char *);
|
||||
|
||||
void bufcat_su(struct html *, const char *,
|
||||
const struct roffsu *);
|
||||
void buffmt_man(struct html *,
|
||||
const char *, const char *);
|
||||
void buffmt_includes(struct html *, const char *);
|
||||
void buffmt(struct html *, const char *, ...);
|
||||
void bufcat(struct html *, const char *);
|
||||
void bufcat_style(struct html *,
|
||||
const char *, const char *);
|
||||
void bufncat(struct html *, const char *, size_t);
|
||||
void bufinit(struct html *);
|
||||
|
||||
void html_idcat(char *, const char *, int);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!HTML_H*/
|
48
commands/mdocml/index.css
Normal file
48
commands/mdocml/index.css
Normal file
|
@ -0,0 +1,48 @@
|
|||
body { color: #333333;
|
||||
font-size: smaller;
|
||||
font-family: Verdana, Tahoma, Arial, sans-serif; }
|
||||
|
||||
table.frame { max-width: 800px;
|
||||
padding-left: 10px; }
|
||||
|
||||
table { padding-left: 40px; }
|
||||
|
||||
p { padding-left: 40px;
|
||||
text-align: justify; }
|
||||
|
||||
h1 { font-weight: bold;
|
||||
font-size: small;
|
||||
font-family: Verdana, Tahoma, Arial, sans-serif; }
|
||||
|
||||
h2 { font-weight: bold;
|
||||
font-size: small;
|
||||
padding-left: 20px;
|
||||
margin-bottom: 0px;
|
||||
font-family: Verdana, Tahoma, Arial, sans-serif; }
|
||||
|
||||
span.nm { color: #000000; font-weight: bold; }
|
||||
|
||||
span.attn { color: #000000; font-weight: bold; }
|
||||
|
||||
span.flag { font-weight: bold; }
|
||||
|
||||
div.head { border-bottom: 1px solid #dddddd;
|
||||
padding-bottom: 5px;
|
||||
text-align: right; }
|
||||
|
||||
div.subhead { font-size: smaller;
|
||||
margin-bottom: 1em; }
|
||||
|
||||
div.foot { border-top: 1px solid #dddddd;
|
||||
padding-top: 5px;
|
||||
font-size: smaller;
|
||||
text-align: right; }
|
||||
|
||||
a.external { background: transparent url(external.png) center right no-repeat;
|
||||
padding-right: 12px; }
|
||||
|
||||
span.date { color: #000000; }
|
||||
|
||||
div.news { margin-bottom: 2em; }
|
||||
|
||||
div.news ul { margin-left: 4em; }
|
420
commands/mdocml/index.sgml
Normal file
420
commands/mdocml/index.sgml
Normal file
|
@ -0,0 +1,420 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
|
||||
<META NAME="resource-type" CONTENT="document">
|
||||
<LINK REL="stylesheet" HREF="index.css" TYPE="text/css" MEDIA="all">
|
||||
<TITLE>mdocml | mdoc macro compiler</TITLE>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<TABLE CLASS="frame" SUMMARY="[frame]">
|
||||
<COL WIDTH="100%">
|
||||
<TBODY>
|
||||
<TR>
|
||||
<TD>
|
||||
<DIV CLASS="head">
|
||||
<B>mdocml</B> – mdoc macro compiler
|
||||
</DIV>
|
||||
<DIV CLASS="subhead">
|
||||
<A HREF="#description">Description</A> |
|
||||
<A HREF="#sources">Sources</A> |
|
||||
<A HREF="#documentation">Documentation</A> |
|
||||
<A HREF="#contact">Contact</A> |
|
||||
<A HREF="#news">News</A>
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top">
|
||||
<H1>
|
||||
<A NAME="description">DESCRIPTION</A>
|
||||
</H1>
|
||||
|
||||
<P>
|
||||
<SPAN CLASS="nm">mdocml</SPAN> is a suite of tools compiling <Q>-mdoc</Q>, the roff macro package
|
||||
of choice for BSD manual pages, and <Q>-man</Q>, the predominant historical package for UNIX
|
||||
manuals. The mission of <SPAN CLASS="nm">mdocml</SPAN> is to deprecate <A
|
||||
HREF="http://www.gnu.org/software/groff/" CLASS="external">groff</A>, the GNU roff implementation, for
|
||||
displaying -mdoc pages whilst providing token support for -man.
|
||||
</P>
|
||||
|
||||
<P>
|
||||
Why? groff amounts to over 5 MB of source code, most of which is C++ and all of which is GPL. It runs
|
||||
slowly, produces uncertain output, and varies in operation from system to system. mdocml strives to fix
|
||||
this (respectively small, C, ISC-licensed, fast and regular).
|
||||
</P>
|
||||
|
||||
<P>
|
||||
The core of <SPAN CLASS="nm">mdocml</SPAN> is composed of the <A HREF="mdoc.3.html">libmdoc</A>, <A
|
||||
HREF="man.3.html">libman</A>, and <A HREF="roff.3.html">libroff</A> validating compiler libraries. All
|
||||
are simple, fast libraries operating on memory buffers, so they may be used for a variety of front-ends
|
||||
(terminal-based, CGI and so on). The front-end is <A HREF="mandoc.1.html">mandoc</A>, which formats
|
||||
manuals for display.
|
||||
</P>
|
||||
|
||||
<P>
|
||||
The <SPAN CLASS="nm">mdocml</SPAN> suite is a <A CLASS="external" HREF="http://bsd.lv/">BSD.lv
|
||||
Project</A> member.
|
||||
</P>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>
|
||||
<H1>
|
||||
<A NAME="sources">SOURCES</A>
|
||||
</H1>
|
||||
|
||||
<P>
|
||||
Sources correctly build and install on DragonFly BSD, FreeBSD, OpenBSD, NetBSD, GNU/Linux, and many
|
||||
other operating systems, tested variously on i386, AMD64, alpha, and others. The most current version
|
||||
is <SPAN CLASS="attn">@VERSION@</SPAN>, dated <SPAN class="attn">@VDATE@</SPAN>. A full <A
|
||||
HREF="ChangeLog.html">ChangeLog</A> (<A HREF="ChangeLog.txt">txt</A>) is written with each release.
|
||||
</P>
|
||||
|
||||
<H2>
|
||||
Current
|
||||
</H2>
|
||||
|
||||
<TABLE WIDTH="100%" SUMMARY="Current Sources">
|
||||
<COL WIDTH="200">
|
||||
<COL>
|
||||
<TBODY>
|
||||
<TR>
|
||||
<TD>Source archive</TD>
|
||||
<TD>
|
||||
<A HREF="/snapshots/mdocml.tar.gz">/snapshots/mdocml.tar.gz</A>
|
||||
(<A HREF="/snapshots/mdocml.md5">md5</A>)
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>Online source</TD>
|
||||
<TD>
|
||||
<A HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/?cvsroot=mdocml">cvsweb</A>
|
||||
</TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
|
||||
<H2>
|
||||
Downstream
|
||||
</H2>
|
||||
|
||||
<TABLE WIDTH="100%" SUMMARY="Downstream Sources">
|
||||
<COL WIDTH="200">
|
||||
<COL>
|
||||
<TBODY>
|
||||
<TR>
|
||||
<TD>DragonFly BSD</TD>
|
||||
<TD>
|
||||
<A HREF="http://gitweb.dragonflybsd.org/dragonfly.git/tree/HEAD:/usr.bin/mandoc"
|
||||
CLASS="external">usr.bin/mandoc</A>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>FreeBSD</TD>
|
||||
<TD>
|
||||
<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/ports/textproc/mdocml/"
|
||||
CLASS="external">ports/textproc/mdocml</A>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>NetBSD</TD>
|
||||
<TD>
|
||||
<A HREF="http://cvsweb.netbsd.org/bsdweb.cgi/src/external/bsd/mdocml/"
|
||||
CLASS="external">src/external/bsd/mdocml</A>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>OpenBSD</TD>
|
||||
<TD>
|
||||
<A HREF="http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/mandoc/"
|
||||
CLASS="external">src/usr.bin/mandoc</A>
|
||||
</TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
|
||||
<H2>
|
||||
Historical
|
||||
</H2>
|
||||
|
||||
<TABLE WIDTH="100%" SUMMARY="Archived Sources">
|
||||
<COL WIDTH="200">
|
||||
<COL>
|
||||
<TBODY>
|
||||
<TR>
|
||||
<TD>Source archive</TD>
|
||||
<TD>
|
||||
<A HREF="/snapshots/">/snapshots/</A>
|
||||
</TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>
|
||||
<H1>
|
||||
<A NAME="documentation">DOCUMENTATION</A>
|
||||
</H1>
|
||||
|
||||
<P>
|
||||
These manuals are generated automatically and refer to the current snapshot.
|
||||
</P>
|
||||
|
||||
<TABLE WIDTH="100%" SUMMARY="Documentation">
|
||||
<COL WIDTH="200">
|
||||
<COL>
|
||||
<TBODY>
|
||||
<TR>
|
||||
<TD VALIGN="top"><A HREF="man.3.html">man(3)</A></TD>
|
||||
<TD VALIGN="top">
|
||||
man macro compiler library
|
||||
<DIV STYLE="font-size: smaller;">
|
||||
<A HREF="man.3.txt">text</A> | <A HREF="man.3.ps">postscript</A>
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top"><A HREF="man.7.html">man(7)</A></TD>
|
||||
<TD VALIGN="top">
|
||||
man language reference
|
||||
<DIV STYLE="font-size: smaller;">
|
||||
<A HREF="man.7.txt">text</A> | <A HREF="man.7.ps">postscript</A>
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top"><A HREF="mandoc.1.html">mandoc(1)</A></TD>
|
||||
<TD VALIGN="top">
|
||||
format and display UNIX manuals
|
||||
<DIV STYLE="font-size: smaller;">
|
||||
<A HREF="mandoc.1.txt">text</A> | <A HREF="mandoc.1.ps">postscript</A>
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top"><A HREF="mandoc_char.7.html">mandoc_char(7)</A></TD>
|
||||
<TD VALIGN="top">
|
||||
mandoc special characters
|
||||
<DIV STYLE="font-size: smaller;">
|
||||
<A HREF="mandoc_char.7.txt">text</A> | <A HREF="mandoc_char.7.ps">postscript</A>
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top"><A HREF="mdoc.3.html">mdoc(3)</A></TD>
|
||||
<TD VALIGN="top">
|
||||
mdoc macro compiler library
|
||||
<DIV STYLE="font-size: smaller;">
|
||||
<A HREF="mdoc.3.txt">text</A> | <A HREF="mdoc.3.ps">postscript</A>
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top"><A HREF="mdoc.7.html">mdoc(7)</A></TD>
|
||||
<TD VALIGN="top">
|
||||
mdoc language reference
|
||||
<DIV STYLE="font-size: smaller;">
|
||||
<A HREF="mdoc.7.txt">text</A> | <A HREF="mdoc.7.ps">postscript</A>
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top"><A HREF="roff.3.html">roff(3)</A></TD>
|
||||
<TD VALIGN="top">
|
||||
roff macro compiler library
|
||||
<DIV STYLE="font-size: smaller;">
|
||||
<A HREF="roff.3.txt">text</A> | <A HREF="roff.3.ps">postscript</A>
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top"><A HREF="roff.7.html">roff(7)</A></TD>
|
||||
<TD VALIGN="top">
|
||||
roff-mandoc language reference
|
||||
<DIV STYLE="font-size: smaller;">
|
||||
<A HREF="roff.7.txt">text</A> | <A HREF="roff.7.ps">postscript</A>
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
|
||||
<P>
|
||||
See <Q><A CLASS="external" HREF="http://manpages.bsd.lv">Writing UNIX Manual Pages</A></Q> for a general
|
||||
introduction to manpages and mdoc.
|
||||
</P>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>
|
||||
<H1>
|
||||
<A NAME="contact">CONTACT</A>
|
||||
</H1>
|
||||
|
||||
<P>
|
||||
For all issues related to <SPAN CLASS="nm">mdocml</SPAN>, contact Kristaps Dzonsons, kris<A
|
||||
CLASS="external"
|
||||
HREF="http://mailhide.recaptcha.net/d?k=01M6h_w7twDp58ZgH57eWC_w==&c=Q2DBUt401ePlSeupJFrq_Q=="
|
||||
TITLE="Reveal this e-mail address">...</A>@bsd.lv.
|
||||
</P>
|
||||
|
||||
<P>
|
||||
You may also subscribe to several mailing lists (these require subscription, which is moderated). An
|
||||
archive is not yet available on-line, although you may request one once subscribed.
|
||||
</P>
|
||||
|
||||
<TABLE WIDTH="100%" SUMMARY="Mailing Lists">
|
||||
<COL WIDTH="200">
|
||||
<COL>
|
||||
<TBODY>
|
||||
<TR>
|
||||
<TD>
|
||||
disc<A CLASS="external" TITLE="Reveal this e-mail address"
|
||||
HREF="http://www.google.com/recaptcha/mailhide/d?k=01KQ80PFH5n3BBNpF5Gs4sRg==&c=EV1QytpQqTHSItc2IXvZyocgYLPnG5K0JKw_gwMC9yc=">...</A>@mdocml.bsd.lv
|
||||
</TD>
|
||||
<TD>high-level discussions and version announcements</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>
|
||||
tec<A CLASS="external" TITLE="Reveal this e-mail address"
|
||||
HREF="http://www.google.com/recaptcha/mailhide/d?k=01qDX_iV0RlUOarEvb6mR28g==&c=gRXsTjza0NNCFPaYu-Taj2tF0pmYZSc90EZkFkhkxgo=">...</A>@mdocml.bsd.lv
|
||||
</TD>
|
||||
<TD>low-level discussions</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>
|
||||
sou<A CLASS="external" TITLE="Reveal this e-mail address"
|
||||
HREF="http://www.google.com/recaptcha/mailhide/d?k=01prQrAZhhl2EbIwVcRfABsQ==&c=KtTW4Yic9xk-8g40KzJoca4fR3MYXv28g8NC6OQV-T8=">...</A>@mdocml.bsd.lv
|
||||
</TD>
|
||||
<TD>source commit messages</TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>
|
||||
<H1>
|
||||
<A NAME="news">NEWS</A>
|
||||
</H1>
|
||||
<DIV CLASS="news">
|
||||
<P>
|
||||
<SPAN CLASS="date">19-06-2010</SPAN>:
|
||||
version 1.10.2
|
||||
</P>
|
||||
<P>
|
||||
Small release featuring text-decoration in <SPAN CLASS="flag">-Tps</SPAN> output, a few
|
||||
minor relaxations of errors, and some optimisations.
|
||||
</P>
|
||||
</DIV>
|
||||
<DIV CLASS="news">
|
||||
<P>
|
||||
<SPAN CLASS="date">07-06-2010</SPAN>:
|
||||
version 1.10.1
|
||||
</P>
|
||||
<P>
|
||||
This primarily focusses on the <Q>Bl</Q> and <Q>It</Q> macros described in <A
|
||||
HREF="mdoc.7.html">mdoc</A>. Multi-line column support is now fully compatible with
|
||||
groff, as are implicit list entries for columns. Removed manuals.7 in favour of <A
|
||||
CLASS="external" HREF="http://manpages.bsd.lv">http://manpages.bsd.lv</A>. The way we
|
||||
handle the SYNOPSIS section (see the SYNOPSIS documentation in <A
|
||||
HREF="mdoc.7.html#x4d414e55414cx20x535452554354555245">MANUAL STRUCTURE</A>) has also
|
||||
been considerably simplified compared to groff's method. Furthermore, the <SPAN
|
||||
CLASS="flag">-Owidth=width</SPAN> output option has been added to <SPAN
|
||||
CLASS="flag">-Tascii</SPAN> (see <A HREF="mandoc.1.html">mandoc</A>). Lastly, initial
|
||||
PostScript output has been added with the <SPAN CLASS="flag">-Tps</SPAN> option to <A
|
||||
HREF="mandoc.1.html">mandoc</A>. It's brutally simple at the moment: fixed-font, with
|
||||
no font decorations.
|
||||
</P>
|
||||
</DIV>
|
||||
<DIV CLASS="news">
|
||||
<P>
|
||||
<SPAN CLASS="date">29-05-2010</SPAN>:
|
||||
version 1.10.0
|
||||
</P>
|
||||
<P>
|
||||
Release consisting of the results from the m2k10 hackathon and up-merge from OpenBSD.
|
||||
This requires a significant note of thanks to Ingo Schwarze (OpenBSD) and Joerg
|
||||
Sonnenberger (NetBSD) for their hard work, and again to Joerg for hosting m2k10.
|
||||
Highlights (mostly cribbed from Ingo's m2k10 report) follow in no particular order:
|
||||
</P>
|
||||
<UL>
|
||||
<LI>a <A HREF="roff.3.html">libroff</A> preprocessor in front of <A
|
||||
HREF="mdoc.3.html">libmdoc</A> and <A HREF="man.3.html">libman</A> stripping out
|
||||
<A HREF="roff.7.html">roff</A> instructions;</LI>
|
||||
<LI>end-of-sentence (EOS) detection in free-form and macro lines;</LI>
|
||||
<LI>correct handling of tab-separated columnar lists in <SPAN
|
||||
CLASS="flag">-mdoc</SPAN>;</LI>
|
||||
<LI>improved main calling routines to optionally use mmap() for better
|
||||
performance;</LI>
|
||||
<LI>cleaned up exiting when invoked as <SPAN CLASS="flag">-Tlint</SPAN> or over
|
||||
multiple files with <SPAN CLASS="flag">-fign-errors</SPAN>;</LI>
|
||||
<LI>error and warning message handling re-written to be unified for <A
|
||||
HREF="roff.3.html">libroff</A>, <A HREF="mdoc.3.html">libmdoc</A>, and <A
|
||||
HREF="man.3.html">libman</A>;</LI>
|
||||
<LI>handling of badly-nested explicit-scoped macros;</LI>
|
||||
<LI>improved free-form text parsing in <A HREF="man.3.html">libman</A> and <A
|
||||
HREF="mdoc.3.html">libmdoc</A>;</LI>
|
||||
<LI>significant GNU troff compatibility improvements in <SPAN
|
||||
CLASS="flag">-Tascii</SPAN>, largely in terms of spacing;</LI>
|
||||
<LI>a regression framework for making sure the many fragilities of GNU troff
|
||||
aren't trampled in subsequent work;</LI>
|
||||
<LI>support for <SPAN CLASS="flag">-Tascii</SPAN> breaking at hyphens
|
||||
encountered in free-form text;</LI>
|
||||
<LI>and many more minor fixes and improvements (no really, consult <A
|
||||
HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/?cvsroot=mdocml">cvsweb</A> and see
|
||||
for yourself!).</LI>
|
||||
</UL>
|
||||
</DIV>
|
||||
<DIV CLASS="news">
|
||||
<P>
|
||||
<SPAN CLASS="date">13-05-2010</SPAN>:
|
||||
version 1.9.25
|
||||
</P>
|
||||
<P>
|
||||
Fixed handling of <Q>\*(Ba</Q> escape. Backed out <SPAN
|
||||
CLASS="flag">-fno-ign-chars</SPAN> (pointless complexity). Fixed erroneous
|
||||
breaking of literal lines. Fixed SYNOPSIS breaking lines before non-initial
|
||||
macros. Changed default section ordering. Most importantly, the framework for
|
||||
end-of-sentence double-spacing is in place, now implemented for the
|
||||
<Q>end-of-sentence, end-of-line</Q> rule. This is a stable roll-back point
|
||||
before the mandoc hackathon in Rostock!
|
||||
</P>
|
||||
</DIV>
|
||||
<DIV CLASS="news">
|
||||
<P>
|
||||
<SPAN CLASS="date">09-05-2010</SPAN>:
|
||||
version 1.9.24
|
||||
</P>
|
||||
<P>
|
||||
Rolled back break-at-hyphen. <SPAN CLASS="flag">-DUGLY</SPAN> is now the
|
||||
default (no feature splits!). Free-form text is not de-chunked any more: lines
|
||||
are passed whole-sale into the front-end, including whitespace. Added mailing
|
||||
lists. Lastly, <SPAN CLASS="nm">mdocml</SPAN> is the focus of two <A
|
||||
CLASS="external" HREF="http://socghop.appspot.com/">Google Summer of Code</A>
|
||||
projects this year: <Q><A CLASS="external"
|
||||
HREF="http://netbsd-soc.sourceforge.net/projects/mandoc_ps/">mandoc -Tps</A></Q>
|
||||
(NetBSD) and <Q><A CLASS="external"
|
||||
HREF="http://wiki.freebsd.org/SummerOfCode2010Projects">BSD-licensed
|
||||
Text-Processing Tools</A></Q> (FreeBSD).
|
||||
</P>
|
||||
</DIV>
|
||||
<P>
|
||||
See <A HREF="http://mdocml.bsd.lv/cgi-bin/cvsweb/index.sgml?cvsroot=mdocml">cvsweb</A> for
|
||||
historical notes.
|
||||
</P>
|
||||
</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>
|
||||
<DIV CLASS="foot">
|
||||
Copyright © 2008–2010 Kristaps Dzonsons, $Date: 2010/06/19 20:43:35 $
|
||||
</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
</BODY>
|
||||
</HTML>
|
38
commands/mdocml/lib.c
Normal file
38
commands/mdocml/lib.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* $Id: lib.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
|
||||
#define LINE(x, y) \
|
||||
if (0 == strcmp(p, x)) return(y);
|
||||
|
||||
const char *
|
||||
mdoc_a2lib(const char *p)
|
||||
{
|
||||
|
||||
#include "lib.in"
|
||||
|
||||
return(NULL);
|
||||
}
|
93
commands/mdocml/lib.in
Normal file
93
commands/mdocml/lib.in
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* $Id: lib.in,v 1.9 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* These are all possible .Lb strings. When a new library is added, add
|
||||
* its short-string to the left-hand side and formatted string to the
|
||||
* right-hand side.
|
||||
*
|
||||
* Be sure to escape strings.
|
||||
*/
|
||||
|
||||
LINE("libarchive", "Reading and Writing Streaming Archives Library (libarchive, \\-larchive)")
|
||||
LINE("libarm", "ARM Architecture Library (libarm, \\-larm)")
|
||||
LINE("libarm32", "ARM32 Architecture Library (libarm32, \\-larm32)")
|
||||
LINE("libbluetooth", "Bluetooth Library (libbluetooth, \\-lbluetooth)")
|
||||
LINE("libbsm", "Basic Security Module User Library (libbsm, \\-lbsm)")
|
||||
LINE("libc", "Standard C Library (libc, \\-lc)")
|
||||
LINE("libc_r", "Reentrant C\\~Library (libc_r, \\-lc_r)")
|
||||
LINE("libcalendar", "Calendar Arithmetic Library (libcalendar, \\-lcalendar)")
|
||||
LINE("libcam", "Common Access Method User Library (libcam, \\-lcam)")
|
||||
LINE("libcdk", "Curses Development Kit Library (libcdk, \\-lcdk)")
|
||||
LINE("libcipher", "FreeSec Crypt Library (libcipher, \\-lcipher)")
|
||||
LINE("libcompat", "Compatibility Library (libcompat, \\-lcompat)")
|
||||
LINE("libcrypt", "Crypt Library (libcrypt, \\-lcrypt)")
|
||||
LINE("libcurses", "Curses Library (libcurses, \\-lcurses)")
|
||||
LINE("libdevinfo", "Device and Resource Information Utility Library (libdevinfo, \\-ldevinfo)")
|
||||
LINE("libdevstat", "Device Statistics Library (libdevstat, \\-ldevstat)")
|
||||
LINE("libdisk", "Interface to Slice and Partition Labels Library (libdisk, \\-ldisk)")
|
||||
LINE("libedit", "Command Line Editor Library (libedit, \\-ledit)")
|
||||
LINE("libelf", "ELF Parsing Library (libelf, \\-lelf)")
|
||||
LINE("libevent", "Event Notification Library (libevent, \\-levent)")
|
||||
LINE("libfetch", "File Transfer Library for URLs (libfetch, \\-lfetch)")
|
||||
LINE("libform", "Curses Form Library (libform, \\-lform)")
|
||||
LINE("libgeom", "Userland API Library for kernel GEOM subsystem (libgeom, \\-lgeom)")
|
||||
LINE("libgpib", "General-Purpose Instrument Bus (GPIB) library (libgpib, \\-lgpib)")
|
||||
LINE("libi386", "i386 Architecture Library (libi386, \\-li386)")
|
||||
LINE("libintl", "Internationalized Message Handling Library (libintl, \\-lintl)")
|
||||
LINE("libipsec", "IPsec Policy Control Library (libipsec, \\-lipsec)")
|
||||
LINE("libipx", "IPX Address Conversion Support Library (libipx, \\-lipx)")
|
||||
LINE("libiscsi", "iSCSI protocol library (libiscsi, \\-liscsi)")
|
||||
LINE("libjail", "Jail Library (libjail, \\-ljail)")
|
||||
LINE("libkiconv", "Kernel side iconv library (libkiconv, \\-lkiconv)")
|
||||
LINE("libkse", "N:M Threading Library (libkse, \\-lkse)")
|
||||
LINE("libkvm", "Kernel Data Access Library (libkvm, \\-lkvm)")
|
||||
LINE("libm", "Math Library (libm, \\-lm)")
|
||||
LINE("libm68k", "m68k Architecture Library (libm68k, \\-lm68k)")
|
||||
LINE("libmagic", "Magic Number Recognition Library (libmagic, \\-lmagic)")
|
||||
LINE("libmd", "Message Digest (MD4, MD5, etc.) Support Library (libmd, \\-lmd)")
|
||||
LINE("libmemstat", "Kernel Memory Allocator Statistics Library (libmemstat, \\-lmemstat)")
|
||||
LINE("libmenu", "Curses Menu Library (libmenu, \\-lmenu)")
|
||||
LINE("libnetgraph", "Netgraph User Library (libnetgraph, \\-lnetgraph)")
|
||||
LINE("libnetpgp", "Netpgp signing, verification, encryption and decryption (libnetpgp, \\-lnetpgp)")
|
||||
LINE("libossaudio", "OSS Audio Emulation Library (libossaudio, \\-lossaudio)")
|
||||
LINE("libpam", "Pluggable Authentication Module Library (libpam, \\-lpam)")
|
||||
LINE("libpcap", "Capture Library (libpcap, \\-lpcap)")
|
||||
LINE("libpci", "PCI Bus Access Library (libpci, \\-lpci)")
|
||||
LINE("libpmc", "Performance Counters Library (libpmc, \\-lpmc)")
|
||||
LINE("libposix", "POSIX Compatibility Library (libposix, \\-lposix)")
|
||||
LINE("libprop", "Property Container Object Library (libprop, \\-lprop)")
|
||||
LINE("libpthread", "POSIX Threads Library (libpthread, \\-lpthread)")
|
||||
LINE("libpuffs", "puffs Convenience Library (libpuffs, \\-lpuffs)")
|
||||
LINE("librefuse", "File System in Userspace Convenience Library (librefuse, \\-lrefuse)")
|
||||
LINE("libresolv", "DNS Resolver Library (libresolv, \\-lresolv)")
|
||||
LINE("librpcsec_gss", "RPC GSS-API Authentication Library (librpcsec_gss, \\-lrpcsec_gss)")
|
||||
LINE("librpcsvc", "RPC Service Library (librpcsvc, \\-lrpcsvc)")
|
||||
LINE("librt", "POSIX Real\\-time Library (librt, -lrt)")
|
||||
LINE("libsdp", "Bluetooth Service Discovery Protocol User Library (libsdp, \\-lsdp)")
|
||||
LINE("libssp", "Buffer Overflow Protection Library (libssp, \\-lssp)")
|
||||
LINE("libtermcap", "Termcap Access Library (libtermcap, \\-ltermcap)")
|
||||
LINE("libterminfo", "Terminal Information Library (libterminfo, \\-lterminfo)")
|
||||
LINE("libthr", "1:1 Threading Library (libthr, \\-lthr)")
|
||||
LINE("libufs", "UFS File System Access Library (libufs, \\-lufs)")
|
||||
LINE("libugidfw", "File System Firewall Interface Library (libugidfw, \\-lugidfw)")
|
||||
LINE("libulog", "User Login Record Library (libulog, \\-lulog)")
|
||||
LINE("libusbhid", "USB Human Interface Devices Library (libusbhid, \\-lusbhid)")
|
||||
LINE("libutil", "System Utilities Library (libutil, \\-lutil)")
|
||||
LINE("libvgl", "Video Graphics Library (libvgl, \\-lvgl)")
|
||||
LINE("libx86_64", "x86_64 Architecture Library (libx86_64, \\-lx86_64)")
|
||||
LINE("libz", "Compression Library (libz, \\-lz)")
|
90
commands/mdocml/libman.h
Normal file
90
commands/mdocml/libman.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/* $Id: libman.h,v 1.36 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef LIBMAN_H
|
||||
#define LIBMAN_H
|
||||
|
||||
#include "man.h"
|
||||
|
||||
enum man_next {
|
||||
MAN_NEXT_SIBLING = 0,
|
||||
MAN_NEXT_CHILD
|
||||
};
|
||||
|
||||
struct man {
|
||||
void *data;
|
||||
mandocmsg msg;
|
||||
int pflags; /* parse flags (see man.h) */
|
||||
int flags; /* parse flags */
|
||||
#define MAN_HALT (1 << 0) /* badness happened: die */
|
||||
#define MAN_ELINE (1 << 1) /* Next-line element scope. */
|
||||
#define MAN_BLINE (1 << 2) /* Next-line block scope. */
|
||||
#define MAN_ILINE (1 << 3) /* Ignored in next-line scope. */
|
||||
#define MAN_LITERAL (1 << 4) /* Literal input. */
|
||||
#define MAN_BPLINE (1 << 5)
|
||||
enum man_next next;
|
||||
struct man_node *last;
|
||||
struct man_node *first;
|
||||
struct man_meta meta;
|
||||
};
|
||||
|
||||
#define MACRO_PROT_ARGS struct man *m, enum mant tok, int line, \
|
||||
int ppos, int *pos, char *buf
|
||||
|
||||
struct man_macro {
|
||||
int (*fp)(MACRO_PROT_ARGS);
|
||||
int flags;
|
||||
#define MAN_SCOPED (1 << 0)
|
||||
#define MAN_EXPLICIT (1 << 1) /* See blk_imp(). */
|
||||
#define MAN_FSCOPED (1 << 2) /* See blk_imp(). */
|
||||
#define MAN_NSCOPED (1 << 3) /* See in_line_eoln(). */
|
||||
#define MAN_NOCLOSE (1 << 4) /* See blk_exp(). */
|
||||
};
|
||||
|
||||
extern const struct man_macro *const man_macros;
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define man_pmsg(m, l, p, t) \
|
||||
(*(m)->msg)((t), (m)->data, (l), (p), NULL)
|
||||
#define man_nmsg(m, n, t) \
|
||||
(*(m)->msg)((t), (m)->data, (n)->line, (n)->pos, NULL)
|
||||
int man_word_alloc(struct man *, int, int, const char *);
|
||||
int man_block_alloc(struct man *, int, int, enum mant);
|
||||
int man_head_alloc(struct man *, int, int, enum mant);
|
||||
int man_body_alloc(struct man *, int, int, enum mant);
|
||||
int man_elem_alloc(struct man *, int, int, enum mant);
|
||||
void man_node_delete(struct man *, struct man_node *);
|
||||
void man_hash_init(void);
|
||||
enum mant man_hash_find(const char *);
|
||||
int man_macroend(struct man *);
|
||||
int man_args(struct man *, int, int *, char *, char **);
|
||||
#define ARGS_ERROR (-1)
|
||||
#define ARGS_EOLN (0)
|
||||
#define ARGS_WORD (1)
|
||||
#define ARGS_QWORD (1)
|
||||
int man_vmsg(struct man *, enum mandocerr,
|
||||
int, int, const char *, ...);
|
||||
int man_valid_post(struct man *);
|
||||
int man_valid_pre(struct man *, struct man_node *);
|
||||
int man_action_post(struct man *);
|
||||
int man_action_pre(struct man *, struct man_node *);
|
||||
int man_unscope(struct man *,
|
||||
const struct man_node *, enum mandocerr);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!LIBMAN_H*/
|
37
commands/mdocml/libmandoc.h
Normal file
37
commands/mdocml/libmandoc.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* $Id: libmandoc.h,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef LIBMANDOC_H
|
||||
#define LIBMANDOC_H
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
int mandoc_special(char *);
|
||||
void *mandoc_calloc(size_t, size_t);
|
||||
char *mandoc_strdup(const char *);
|
||||
void *mandoc_malloc(size_t);
|
||||
void *mandoc_realloc(void *, size_t);
|
||||
time_t mandoc_a2time(int, const char *);
|
||||
#define MTIME_CANONICAL (1 << 0)
|
||||
#define MTIME_REDUCED (1 << 1)
|
||||
#define MTIME_MDOCDATE (1 << 2)
|
||||
#define MTIME_ISO_8601 (1 << 3)
|
||||
int mandoc_eos(const char *, size_t);
|
||||
int mandoc_hyph(const char *, const char *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!LIBMANDOC_H*/
|
142
commands/mdocml/libmdoc.h
Normal file
142
commands/mdocml/libmdoc.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
/* $Id: libmdoc.h,v 1.53 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef LIBMDOC_H
|
||||
#define LIBMDOC_H
|
||||
|
||||
#include "mdoc.h"
|
||||
|
||||
enum mdoc_next {
|
||||
MDOC_NEXT_SIBLING = 0,
|
||||
MDOC_NEXT_CHILD
|
||||
};
|
||||
|
||||
struct mdoc {
|
||||
void *data;
|
||||
mandocmsg msg;
|
||||
int flags;
|
||||
#define MDOC_HALT (1 << 0) /* error in parse: halt */
|
||||
#define MDOC_LITERAL (1 << 1) /* in a literal scope */
|
||||
#define MDOC_PBODY (1 << 2) /* in the document body */
|
||||
#define MDOC_NEWLINE (1 << 3) /* first macro/text in a line */
|
||||
#define MDOC_PHRASELIT (1 << 4) /* literal within a partila phrase */
|
||||
#define MDOC_PPHRASE (1 << 5) /* within a partial phrase */
|
||||
#define MDOC_FREECOL (1 << 6) /* `It' invocation should close */
|
||||
int pflags;
|
||||
enum mdoc_next next;
|
||||
struct mdoc_node *last;
|
||||
struct mdoc_node *first;
|
||||
struct mdoc_meta meta;
|
||||
enum mdoc_sec lastnamed;
|
||||
enum mdoc_sec lastsec;
|
||||
};
|
||||
|
||||
#define MACRO_PROT_ARGS struct mdoc *m, enum mdoct tok, \
|
||||
int line, int ppos, int *pos, char *buf
|
||||
|
||||
struct mdoc_macro {
|
||||
int (*fp)(MACRO_PROT_ARGS);
|
||||
int flags;
|
||||
#define MDOC_CALLABLE (1 << 0)
|
||||
#define MDOC_PARSED (1 << 1)
|
||||
#define MDOC_EXPLICIT (1 << 2)
|
||||
#define MDOC_PROLOGUE (1 << 3)
|
||||
#define MDOC_IGNDELIM (1 << 4)
|
||||
/* Reserved words in arguments treated as text. */
|
||||
};
|
||||
|
||||
enum margserr {
|
||||
ARGS_ERROR,
|
||||
ARGS_EOLN,
|
||||
ARGS_WORD,
|
||||
ARGS_PUNCT,
|
||||
ARGS_QWORD,
|
||||
ARGS_PHRASE,
|
||||
ARGS_PPHRASE,
|
||||
ARGS_PEND
|
||||
};
|
||||
|
||||
enum margverr {
|
||||
ARGV_ERROR,
|
||||
ARGV_EOLN,
|
||||
ARGV_ARG,
|
||||
ARGV_WORD
|
||||
};
|
||||
|
||||
enum mdelim {
|
||||
DELIM_NONE = 0,
|
||||
DELIM_OPEN,
|
||||
DELIM_MIDDLE,
|
||||
DELIM_CLOSE
|
||||
};
|
||||
|
||||
extern const struct mdoc_macro *const mdoc_macros;
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define mdoc_pmsg(m, l, p, t) \
|
||||
(*(m)->msg)((t), (m)->data, (l), (p), NULL)
|
||||
#define mdoc_nmsg(m, n, t) \
|
||||
(*(m)->msg)((t), (m)->data, (n)->line, (n)->pos, NULL)
|
||||
int mdoc_vmsg(struct mdoc *, enum mandocerr,
|
||||
int, int, const char *, ...);
|
||||
int mdoc_macro(MACRO_PROT_ARGS);
|
||||
int mdoc_word_alloc(struct mdoc *,
|
||||
int, int, const char *);
|
||||
int mdoc_elem_alloc(struct mdoc *, int, int,
|
||||
enum mdoct, struct mdoc_arg *);
|
||||
int mdoc_block_alloc(struct mdoc *, int, int,
|
||||
enum mdoct, struct mdoc_arg *);
|
||||
int mdoc_head_alloc(struct mdoc *, int, int, enum mdoct);
|
||||
int mdoc_tail_alloc(struct mdoc *, int, int, enum mdoct);
|
||||
int mdoc_body_alloc(struct mdoc *, int, int, enum mdoct);
|
||||
void mdoc_node_delete(struct mdoc *, struct mdoc_node *);
|
||||
void mdoc_hash_init(void);
|
||||
enum mdoct mdoc_hash_find(const char *);
|
||||
enum mdelim mdoc_iscdelim(char);
|
||||
enum mdelim mdoc_isdelim(const char *);
|
||||
size_t mdoc_isescape(const char *);
|
||||
enum mdoc_sec mdoc_str2sec(const char *);
|
||||
time_t mdoc_atotime(const char *);
|
||||
size_t mdoc_macro2len(enum mdoct);
|
||||
const char *mdoc_a2att(const char *);
|
||||
const char *mdoc_a2lib(const char *);
|
||||
const char *mdoc_a2st(const char *);
|
||||
const char *mdoc_a2arch(const char *);
|
||||
const char *mdoc_a2vol(const char *);
|
||||
const char *mdoc_a2msec(const char *);
|
||||
int mdoc_valid_pre(struct mdoc *, struct mdoc_node *);
|
||||
int mdoc_valid_post(struct mdoc *);
|
||||
int mdoc_action_pre(struct mdoc *,
|
||||
struct mdoc_node *);
|
||||
int mdoc_action_post(struct mdoc *);
|
||||
enum margverr mdoc_argv(struct mdoc *, int, enum mdoct,
|
||||
struct mdoc_arg **, int *, char *);
|
||||
void mdoc_argv_free(struct mdoc_arg *);
|
||||
void mdoc_argn_free(struct mdoc_arg *, int);
|
||||
enum margserr mdoc_args(struct mdoc *, int,
|
||||
int *, char *, enum mdoct, char **);
|
||||
enum margserr mdoc_zargs(struct mdoc *, int,
|
||||
int *, char *, int, char **);
|
||||
#define ARGS_DELIM (1 << 1)
|
||||
#define ARGS_TABSEP (1 << 2)
|
||||
#define ARGS_NOWARN (1 << 3)
|
||||
|
||||
int mdoc_macroend(struct mdoc *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!LIBMDOC_H*/
|
866
commands/mdocml/main.c
Normal file
866
commands/mdocml/main.c
Normal file
|
@ -0,0 +1,866 @@
|
|||
/* $Id: main.c,v 1.89 2010/06/19 20:46:27 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "mdoc.h"
|
||||
#include "man.h"
|
||||
#include "roff.h"
|
||||
#include "main.h"
|
||||
|
||||
#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
|
||||
|
||||
/* FIXME: Intel's compiler? LLVM? pcc? */
|
||||
|
||||
#if !defined(__GNUC__) || (__GNUC__ < 2)
|
||||
# if !defined(lint)
|
||||
# define __attribute__(x)
|
||||
# endif
|
||||
#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
|
||||
|
||||
typedef void (*out_mdoc)(void *, const struct mdoc *);
|
||||
typedef void (*out_man)(void *, const struct man *);
|
||||
typedef void (*out_free)(void *);
|
||||
|
||||
struct buf {
|
||||
char *buf;
|
||||
size_t sz;
|
||||
};
|
||||
|
||||
enum intt {
|
||||
INTT_AUTO,
|
||||
INTT_MDOC,
|
||||
INTT_MAN
|
||||
};
|
||||
|
||||
enum outt {
|
||||
OUTT_ASCII = 0,
|
||||
OUTT_TREE,
|
||||
OUTT_HTML,
|
||||
OUTT_XHTML,
|
||||
OUTT_LINT,
|
||||
OUTT_PS
|
||||
};
|
||||
|
||||
struct curparse {
|
||||
const char *file; /* Current parse. */
|
||||
int fd; /* Current parse. */
|
||||
int wflags;
|
||||
/* FIXME: set by max error */
|
||||
#define WARN_WALL (1 << 0) /* All-warnings mask. */
|
||||
#define WARN_WERR (1 << 2) /* Warnings->errors. */
|
||||
int fflags;
|
||||
#define FL_IGN_SCOPE (1 << 0) /* Ignore scope errors. */
|
||||
#define FL_NIGN_ESCAPE (1 << 1) /* Don't ignore bad escapes. */
|
||||
#define FL_NIGN_MACRO (1 << 2) /* Don't ignore bad macros. */
|
||||
#define FL_IGN_ERRORS (1 << 4) /* Ignore failed parse. */
|
||||
#define FL_STRICT FL_NIGN_ESCAPE | \
|
||||
FL_NIGN_MACRO /* ignore nothing */
|
||||
enum intt inttype; /* which parser to use */
|
||||
struct man *man; /* man parser */
|
||||
struct mdoc *mdoc; /* mdoc parser */
|
||||
struct roff *roff; /* roff parser (!NULL) */
|
||||
enum outt outtype; /* which output to use */
|
||||
out_mdoc outmdoc; /* mdoc output ptr */
|
||||
out_man outman; /* man output ptr */
|
||||
out_free outfree; /* free output ptr */
|
||||
void *outdata; /* data for output */
|
||||
char outopts[BUFSIZ]; /* buf of output opts */
|
||||
};
|
||||
|
||||
static const char * const mandocerrs[MANDOCERR_MAX] = {
|
||||
"ok",
|
||||
"text should be uppercase",
|
||||
"sections out of conventional order",
|
||||
"section name repeats",
|
||||
"out of order prologue",
|
||||
"repeated prologue entry",
|
||||
"list type must come first",
|
||||
"bad standard",
|
||||
"bad library",
|
||||
"bad escape sequence",
|
||||
"unterminated quoted string",
|
||||
"argument requires the width argument",
|
||||
"superfluous width argument",
|
||||
"ignoring argument",
|
||||
"bad date argument",
|
||||
"bad width argument",
|
||||
"unknown manual section",
|
||||
"section not in conventional manual section",
|
||||
"end of line whitespace",
|
||||
"scope open on exit",
|
||||
"NAME section must come first",
|
||||
"bad Boolean value",
|
||||
"child violates parent syntax",
|
||||
"bad AT&T symbol",
|
||||
"list type repeated",
|
||||
"display type repeated",
|
||||
"argument repeated",
|
||||
"manual name not yet set",
|
||||
"obsolete macro ignored",
|
||||
"empty macro ignored",
|
||||
"macro not allowed in body",
|
||||
"macro not allowed in prologue",
|
||||
"bad character",
|
||||
"bad NAME section contents",
|
||||
"no blank lines",
|
||||
"no text in this context",
|
||||
"bad comment style",
|
||||
"unknown macro will be lost",
|
||||
"line scope broken",
|
||||
"scope broken",
|
||||
"argument count wrong",
|
||||
"request scope close w/none open",
|
||||
"scope already open",
|
||||
"macro requires line argument(s)",
|
||||
"macro requires body argument(s)",
|
||||
"macro requires argument(s)",
|
||||
"no title in document",
|
||||
"missing list type",
|
||||
"missing display type",
|
||||
"line argument(s) will be lost",
|
||||
"body argument(s) will be lost",
|
||||
"column syntax is inconsistent",
|
||||
"missing font type",
|
||||
"displays may not be nested",
|
||||
"unsupported display type",
|
||||
"no scope to rewind: syntax violated",
|
||||
"scope broken, syntax violated",
|
||||
"line scope broken, syntax violated",
|
||||
"argument count wrong, violates syntax",
|
||||
"child violates parent syntax",
|
||||
"argument count wrong, violates syntax",
|
||||
"no document body",
|
||||
"no document prologue",
|
||||
"utsname system call failed",
|
||||
"memory exhausted",
|
||||
};
|
||||
|
||||
static void fdesc(struct curparse *);
|
||||
static void ffile(const char *, struct curparse *);
|
||||
static int foptions(int *, char *);
|
||||
static struct man *man_init(struct curparse *);
|
||||
static struct mdoc *mdoc_init(struct curparse *);
|
||||
static struct roff *roff_init(struct curparse *);
|
||||
static int moptions(enum intt *, char *);
|
||||
static int mmsg(enum mandocerr, void *,
|
||||
int, int, const char *);
|
||||
static int pset(const char *, int, struct curparse *,
|
||||
struct man **, struct mdoc **);
|
||||
static int toptions(struct curparse *, char *);
|
||||
static void usage(void) __attribute__((noreturn));
|
||||
static void version(void) __attribute__((noreturn));
|
||||
static int woptions(int *, char *);
|
||||
|
||||
static const char *progname;
|
||||
static int with_error;
|
||||
static int with_warning;
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
struct curparse curp;
|
||||
|
||||
progname = strrchr(argv[0], '/');
|
||||
if (progname == NULL)
|
||||
progname = argv[0];
|
||||
else
|
||||
++progname;
|
||||
|
||||
memset(&curp, 0, sizeof(struct curparse));
|
||||
|
||||
curp.inttype = INTT_AUTO;
|
||||
curp.outtype = OUTT_ASCII;
|
||||
|
||||
/* LINTED */
|
||||
while (-1 != (c = getopt(argc, argv, "f:m:O:T:VW:")))
|
||||
switch (c) {
|
||||
case ('f'):
|
||||
if ( ! foptions(&curp.fflags, optarg))
|
||||
return(EXIT_FAILURE);
|
||||
break;
|
||||
case ('m'):
|
||||
if ( ! moptions(&curp.inttype, optarg))
|
||||
return(EXIT_FAILURE);
|
||||
break;
|
||||
case ('O'):
|
||||
(void)strlcat(curp.outopts, optarg, BUFSIZ);
|
||||
(void)strlcat(curp.outopts, ",", BUFSIZ);
|
||||
break;
|
||||
case ('T'):
|
||||
if ( ! toptions(&curp, optarg))
|
||||
return(EXIT_FAILURE);
|
||||
break;
|
||||
case ('W'):
|
||||
if ( ! woptions(&curp.wflags, optarg))
|
||||
return(EXIT_FAILURE);
|
||||
break;
|
||||
case ('V'):
|
||||
version();
|
||||
/* NOTREACHED */
|
||||
default:
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (NULL == *argv) {
|
||||
curp.file = "<stdin>";
|
||||
curp.fd = STDIN_FILENO;
|
||||
|
||||
fdesc(&curp);
|
||||
}
|
||||
|
||||
while (*argv) {
|
||||
ffile(*argv, &curp);
|
||||
|
||||
if (with_error && !(curp.fflags & FL_IGN_ERRORS))
|
||||
break;
|
||||
++argv;
|
||||
}
|
||||
|
||||
if (curp.outfree)
|
||||
(*curp.outfree)(curp.outdata);
|
||||
if (curp.mdoc)
|
||||
mdoc_free(curp.mdoc);
|
||||
if (curp.man)
|
||||
man_free(curp.man);
|
||||
if (curp.roff)
|
||||
roff_free(curp.roff);
|
||||
|
||||
return((with_warning || with_error) ?
|
||||
EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
version(void)
|
||||
{
|
||||
|
||||
(void)printf("%s %s\n", progname, VERSION);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
(void)fprintf(stderr, "usage: %s [-V] [-foption] "
|
||||
"[-mformat] [-Ooption] [-Toutput] "
|
||||
"[-Werr] [file...]\n", progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
static struct man *
|
||||
man_init(struct curparse *curp)
|
||||
{
|
||||
int pflags;
|
||||
|
||||
/* Defaults from mandoc.1. */
|
||||
|
||||
pflags = MAN_IGN_MACRO | MAN_IGN_ESCAPE;
|
||||
|
||||
if (curp->fflags & FL_NIGN_MACRO)
|
||||
pflags &= ~MAN_IGN_MACRO;
|
||||
if (curp->fflags & FL_NIGN_ESCAPE)
|
||||
pflags &= ~MAN_IGN_ESCAPE;
|
||||
|
||||
return(man_alloc(curp, pflags, mmsg));
|
||||
}
|
||||
|
||||
|
||||
static struct roff *
|
||||
roff_init(struct curparse *curp)
|
||||
{
|
||||
|
||||
return(roff_alloc(mmsg, curp));
|
||||
}
|
||||
|
||||
|
||||
static struct mdoc *
|
||||
mdoc_init(struct curparse *curp)
|
||||
{
|
||||
int pflags;
|
||||
|
||||
/* Defaults from mandoc.1. */
|
||||
|
||||
pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE;
|
||||
|
||||
if (curp->fflags & FL_IGN_SCOPE)
|
||||
pflags |= MDOC_IGN_SCOPE;
|
||||
if (curp->fflags & FL_NIGN_ESCAPE)
|
||||
pflags &= ~MDOC_IGN_ESCAPE;
|
||||
if (curp->fflags & FL_NIGN_MACRO)
|
||||
pflags &= ~MDOC_IGN_MACRO;
|
||||
|
||||
return(mdoc_alloc(curp, pflags, mmsg));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ffile(const char *file, struct curparse *curp)
|
||||
{
|
||||
|
||||
curp->file = file;
|
||||
if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
|
||||
perror(curp->file);
|
||||
with_error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
fdesc(curp);
|
||||
|
||||
if (-1 == close(curp->fd))
|
||||
perror(curp->file);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
resize_buf(struct buf *buf, size_t initial)
|
||||
{
|
||||
void *tmp;
|
||||
size_t sz;
|
||||
|
||||
if (buf->sz == 0)
|
||||
sz = initial;
|
||||
else
|
||||
sz = 2 * buf->sz;
|
||||
tmp = realloc(buf->buf, sz);
|
||||
if (NULL == tmp) {
|
||||
perror(NULL);
|
||||
return(0);
|
||||
}
|
||||
buf->buf = tmp;
|
||||
buf->sz = sz;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
|
||||
{
|
||||
struct stat st;
|
||||
size_t off;
|
||||
ssize_t ssz;
|
||||
|
||||
if (-1 == fstat(curp->fd, &st)) {
|
||||
perror(curp->file);
|
||||
with_error = 1;
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifndef __minix
|
||||
/*
|
||||
* If we're a regular file, try just reading in the whole entry
|
||||
* via mmap(). This is faster than reading it into blocks, and
|
||||
* since each file is only a few bytes to begin with, I'm not
|
||||
* concerned that this is going to tank any machines.
|
||||
*/
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
if (st.st_size >= (1U << 31)) {
|
||||
fprintf(stderr, "%s: input too large\n",
|
||||
curp->file);
|
||||
with_error = 1;
|
||||
return(0);
|
||||
}
|
||||
*with_mmap = 1;
|
||||
fb->sz = (size_t)st.st_size;
|
||||
fb->buf = mmap(NULL, fb->sz, PROT_READ,
|
||||
MAP_FILE|MAP_SHARED, curp->fd, 0);
|
||||
if (fb->buf != MAP_FAILED)
|
||||
return(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If this isn't a regular file (like, say, stdin), then we must
|
||||
* go the old way and just read things in bit by bit.
|
||||
*/
|
||||
|
||||
*with_mmap = 0;
|
||||
off = 0;
|
||||
fb->sz = 0;
|
||||
fb->buf = NULL;
|
||||
for (;;) {
|
||||
if (off == fb->sz) {
|
||||
if (fb->sz == (1U << 31)) {
|
||||
fprintf(stderr, "%s: input too large\n",
|
||||
curp->file);
|
||||
break;
|
||||
}
|
||||
if (! resize_buf(fb, 65536))
|
||||
break;
|
||||
}
|
||||
ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);
|
||||
if (ssz == 0) {
|
||||
fb->sz = off;
|
||||
return(1);
|
||||
}
|
||||
if (ssz == -1) {
|
||||
perror(curp->file);
|
||||
break;
|
||||
}
|
||||
off += (size_t)ssz;
|
||||
}
|
||||
|
||||
free(fb->buf);
|
||||
fb->buf = NULL;
|
||||
with_error = 1;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fdesc(struct curparse *curp)
|
||||
{
|
||||
struct buf ln, blk;
|
||||
int i, pos, lnn, lnn_start, with_mmap, of;
|
||||
enum rofferr re;
|
||||
struct man *man;
|
||||
struct mdoc *mdoc;
|
||||
struct roff *roff;
|
||||
|
||||
man = NULL;
|
||||
mdoc = NULL;
|
||||
roff = NULL;
|
||||
memset(&ln, 0, sizeof(struct buf));
|
||||
|
||||
/*
|
||||
* Two buffers: ln and buf. buf is the input file and may be
|
||||
* memory mapped. ln is a line buffer and grows on-demand.
|
||||
*/
|
||||
|
||||
if ( ! read_whole_file(curp, &blk, &with_mmap))
|
||||
return;
|
||||
|
||||
if (NULL == curp->roff)
|
||||
curp->roff = roff_init(curp);
|
||||
if (NULL == (roff = curp->roff))
|
||||
goto bailout;
|
||||
|
||||
for (i = 0, lnn = 1; i < (int)blk.sz;) {
|
||||
pos = 0;
|
||||
lnn_start = lnn;
|
||||
while (i < (int)blk.sz) {
|
||||
if ('\n' == blk.buf[i]) {
|
||||
++i;
|
||||
++lnn;
|
||||
break;
|
||||
}
|
||||
/* Trailing backslash is like a plain character. */
|
||||
if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
|
||||
if (pos >= (int)ln.sz)
|
||||
if (! resize_buf(&ln, 256))
|
||||
goto bailout;
|
||||
ln.buf[pos++] = blk.buf[i++];
|
||||
continue;
|
||||
}
|
||||
/* Found an escape and at least one other character. */
|
||||
if ('\n' == blk.buf[i + 1]) {
|
||||
/* Escaped newlines are skipped over */
|
||||
i += 2;
|
||||
++lnn;
|
||||
continue;
|
||||
}
|
||||
if ('"' == blk.buf[i + 1]) {
|
||||
i += 2;
|
||||
/* Comment, skip to end of line */
|
||||
for (; i < (int)blk.sz; ++i) {
|
||||
if ('\n' == blk.buf[i]) {
|
||||
++i;
|
||||
++lnn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Backout trailing whitespaces */
|
||||
for (; pos > 0; --pos) {
|
||||
if (ln.buf[pos - 1] != ' ')
|
||||
break;
|
||||
if (pos > 2 && ln.buf[pos - 2] == '\\')
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Some other escape sequence, copy and continue. */
|
||||
if (pos + 1 >= (int)ln.sz)
|
||||
if (! resize_buf(&ln, 256))
|
||||
goto bailout;
|
||||
|
||||
ln.buf[pos++] = blk.buf[i++];
|
||||
ln.buf[pos++] = blk.buf[i++];
|
||||
}
|
||||
|
||||
if (pos >= (int)ln.sz)
|
||||
if (! resize_buf(&ln, 256))
|
||||
goto bailout;
|
||||
ln.buf[pos] = '\0';
|
||||
|
||||
/*
|
||||
* A significant amount of complexity is contained by
|
||||
* the roff preprocessor. It's line-oriented but can be
|
||||
* expressed on one line, so we need at times to
|
||||
* readjust our starting point and re-run it. The roff
|
||||
* preprocessor can also readjust the buffers with new
|
||||
* data, so we pass them in wholesale.
|
||||
*/
|
||||
|
||||
of = 0;
|
||||
do {
|
||||
re = roff_parseln(roff, lnn_start,
|
||||
&ln.buf, &ln.sz, of, &of);
|
||||
} while (ROFF_RERUN == re);
|
||||
|
||||
if (ROFF_IGN == re)
|
||||
continue;
|
||||
else if (ROFF_ERR == re)
|
||||
goto bailout;
|
||||
|
||||
/*
|
||||
* If input parsers have not been allocated, do so now.
|
||||
* We keep these instanced betwen parsers, but set them
|
||||
* locally per parse routine since we can use different
|
||||
* parsers with each one.
|
||||
*/
|
||||
|
||||
if ( ! (man || mdoc))
|
||||
if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc))
|
||||
goto bailout;
|
||||
|
||||
/* Lastly, push down into the parsers themselves. */
|
||||
|
||||
if (man && ! man_parseln(man, lnn_start, ln.buf, of))
|
||||
goto bailout;
|
||||
if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of))
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
/* NOTE a parser may not have been assigned, yet. */
|
||||
|
||||
if ( ! (man || mdoc)) {
|
||||
fprintf(stderr, "%s: Not a manual\n", curp->file);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
/* Clean up the parse routine ASTs. */
|
||||
|
||||
if (mdoc && ! mdoc_endparse(mdoc))
|
||||
goto bailout;
|
||||
if (man && ! man_endparse(man))
|
||||
goto bailout;
|
||||
if (roff && ! roff_endparse(roff))
|
||||
goto bailout;
|
||||
|
||||
/* If unset, allocate output dev now (if applicable). */
|
||||
|
||||
if ( ! (curp->outman && curp->outmdoc)) {
|
||||
switch (curp->outtype) {
|
||||
case (OUTT_XHTML):
|
||||
curp->outdata = xhtml_alloc(curp->outopts);
|
||||
break;
|
||||
case (OUTT_HTML):
|
||||
curp->outdata = html_alloc(curp->outopts);
|
||||
break;
|
||||
case (OUTT_ASCII):
|
||||
curp->outdata = ascii_alloc(curp->outopts);
|
||||
curp->outfree = ascii_free;
|
||||
break;
|
||||
case (OUTT_PS):
|
||||
curp->outdata = ps_alloc();
|
||||
curp->outfree = ps_free;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (curp->outtype) {
|
||||
case (OUTT_HTML):
|
||||
/* FALLTHROUGH */
|
||||
case (OUTT_XHTML):
|
||||
curp->outman = html_man;
|
||||
curp->outmdoc = html_mdoc;
|
||||
curp->outfree = html_free;
|
||||
break;
|
||||
case (OUTT_TREE):
|
||||
curp->outman = tree_man;
|
||||
curp->outmdoc = tree_mdoc;
|
||||
break;
|
||||
case (OUTT_ASCII):
|
||||
/* FALLTHROUGH */
|
||||
case (OUTT_PS):
|
||||
curp->outman = terminal_man;
|
||||
curp->outmdoc = terminal_mdoc;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute the out device, if it exists. */
|
||||
|
||||
if (man && curp->outman)
|
||||
(*curp->outman)(curp->outdata, man);
|
||||
if (mdoc && curp->outmdoc)
|
||||
(*curp->outmdoc)(curp->outdata, mdoc);
|
||||
|
||||
cleanup:
|
||||
if (mdoc)
|
||||
mdoc_reset(mdoc);
|
||||
if (man)
|
||||
man_reset(man);
|
||||
if (roff)
|
||||
roff_reset(roff);
|
||||
if (ln.buf)
|
||||
free(ln.buf);
|
||||
if (with_mmap)
|
||||
munmap(blk.buf, blk.sz);
|
||||
else
|
||||
free(blk.buf);
|
||||
|
||||
return;
|
||||
|
||||
bailout:
|
||||
with_error = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
pset(const char *buf, int pos, struct curparse *curp,
|
||||
struct man **man, struct mdoc **mdoc)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Try to intuit which kind of manual parser should be used. If
|
||||
* passed in by command-line (-man, -mdoc), then use that
|
||||
* explicitly. If passed as -mandoc, then try to guess from the
|
||||
* line: either skip dot-lines, use -mdoc when finding `.Dt', or
|
||||
* default to -man, which is more lenient.
|
||||
*/
|
||||
|
||||
if ('.' == buf[0] || '\'' == buf[0]) {
|
||||
for (i = 1; buf[i]; i++)
|
||||
if (' ' != buf[i] && '\t' != buf[i])
|
||||
break;
|
||||
if (0 == buf[i])
|
||||
return(1);
|
||||
}
|
||||
|
||||
switch (curp->inttype) {
|
||||
case (INTT_MDOC):
|
||||
if (NULL == curp->mdoc)
|
||||
curp->mdoc = mdoc_init(curp);
|
||||
if (NULL == (*mdoc = curp->mdoc))
|
||||
return(0);
|
||||
return(1);
|
||||
case (INTT_MAN):
|
||||
if (NULL == curp->man)
|
||||
curp->man = man_init(curp);
|
||||
if (NULL == (*man = curp->man))
|
||||
return(0);
|
||||
return(1);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
|
||||
if (NULL == curp->mdoc)
|
||||
curp->mdoc = mdoc_init(curp);
|
||||
if (NULL == (*mdoc = curp->mdoc))
|
||||
return(0);
|
||||
return(1);
|
||||
}
|
||||
|
||||
if (NULL == curp->man)
|
||||
curp->man = man_init(curp);
|
||||
if (NULL == (*man = curp->man))
|
||||
return(0);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
moptions(enum intt *tflags, char *arg)
|
||||
{
|
||||
|
||||
if (0 == strcmp(arg, "doc"))
|
||||
*tflags = INTT_MDOC;
|
||||
else if (0 == strcmp(arg, "andoc"))
|
||||
*tflags = INTT_AUTO;
|
||||
else if (0 == strcmp(arg, "an"))
|
||||
*tflags = INTT_MAN;
|
||||
else {
|
||||
fprintf(stderr, "%s: Bad argument\n", arg);
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
toptions(struct curparse *curp, char *arg)
|
||||
{
|
||||
|
||||
if (0 == strcmp(arg, "ascii"))
|
||||
curp->outtype = OUTT_ASCII;
|
||||
else if (0 == strcmp(arg, "lint")) {
|
||||
curp->outtype = OUTT_LINT;
|
||||
curp->wflags |= WARN_WALL;
|
||||
curp->fflags |= FL_STRICT;
|
||||
}
|
||||
else if (0 == strcmp(arg, "tree"))
|
||||
curp->outtype = OUTT_TREE;
|
||||
else if (0 == strcmp(arg, "html"))
|
||||
curp->outtype = OUTT_HTML;
|
||||
else if (0 == strcmp(arg, "xhtml"))
|
||||
curp->outtype = OUTT_XHTML;
|
||||
else if (0 == strcmp(arg, "ps"))
|
||||
curp->outtype = OUTT_PS;
|
||||
else {
|
||||
fprintf(stderr, "%s: Bad argument\n", arg);
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
foptions(int *fflags, char *arg)
|
||||
{
|
||||
char *v, *o;
|
||||
const char *toks[8];
|
||||
|
||||
toks[0] = "ign-scope";
|
||||
toks[1] = "no-ign-escape";
|
||||
toks[2] = "no-ign-macro";
|
||||
toks[3] = "ign-errors";
|
||||
toks[4] = "strict";
|
||||
toks[5] = "ign-escape";
|
||||
toks[6] = NULL;
|
||||
|
||||
while (*arg) {
|
||||
o = arg;
|
||||
switch (getsubopt(&arg, UNCONST(toks), &v)) {
|
||||
case (0):
|
||||
*fflags |= FL_IGN_SCOPE;
|
||||
break;
|
||||
case (1):
|
||||
*fflags |= FL_NIGN_ESCAPE;
|
||||
break;
|
||||
case (2):
|
||||
*fflags |= FL_NIGN_MACRO;
|
||||
break;
|
||||
case (3):
|
||||
*fflags |= FL_IGN_ERRORS;
|
||||
break;
|
||||
case (4):
|
||||
*fflags |= FL_STRICT;
|
||||
break;
|
||||
case (5):
|
||||
*fflags &= ~FL_NIGN_ESCAPE;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: Bad argument\n", o);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
woptions(int *wflags, char *arg)
|
||||
{
|
||||
char *v, *o;
|
||||
const char *toks[3];
|
||||
|
||||
toks[0] = "all";
|
||||
toks[1] = "error";
|
||||
toks[2] = NULL;
|
||||
|
||||
while (*arg) {
|
||||
o = arg;
|
||||
switch (getsubopt(&arg, UNCONST(toks), &v)) {
|
||||
case (0):
|
||||
*wflags |= WARN_WALL;
|
||||
break;
|
||||
case (1):
|
||||
*wflags |= WARN_WERR;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: Bad argument\n", o);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
|
||||
{
|
||||
struct curparse *cp;
|
||||
|
||||
cp = (struct curparse *)arg;
|
||||
|
||||
if (t <= MANDOCERR_ERROR) {
|
||||
if ( ! (cp->wflags & WARN_WALL))
|
||||
return(1);
|
||||
with_warning = 1;
|
||||
} else
|
||||
with_error = 1;
|
||||
|
||||
fprintf(stderr, "%s:%d:%d: %s", cp->file,
|
||||
ln, col + 1, mandocerrs[t]);
|
||||
|
||||
if (msg)
|
||||
fprintf(stderr, ": %s", msg);
|
||||
|
||||
fputc('\n', stderr);
|
||||
|
||||
/* This is superfluous, but whatever. */
|
||||
if (t > MANDOCERR_ERROR)
|
||||
return(0);
|
||||
if (cp->wflags & WARN_WERR) {
|
||||
with_error = 1;
|
||||
return(0);
|
||||
}
|
||||
return(1);
|
||||
}
|
55
commands/mdocml/main.h
Normal file
55
commands/mdocml/main.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* $Id: main.h,v 1.7 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
struct mdoc;
|
||||
struct man;
|
||||
|
||||
#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
|
||||
|
||||
|
||||
/*
|
||||
* Definitions for main.c-visible output device functions, e.g., -Thtml
|
||||
* and -Tascii. Note that ascii_alloc() is named as such in
|
||||
* anticipation of latin1_alloc() and so on, all of which map into the
|
||||
* terminal output routines with different character settings.
|
||||
*/
|
||||
|
||||
void *html_alloc(char *);
|
||||
void *xhtml_alloc(char *);
|
||||
void html_mdoc(void *, const struct mdoc *);
|
||||
void html_man(void *, const struct man *);
|
||||
void html_free(void *);
|
||||
|
||||
void tree_mdoc(void *, const struct mdoc *);
|
||||
void tree_man(void *, const struct man *);
|
||||
|
||||
void *ascii_alloc(char *);
|
||||
void ascii_free(void *);
|
||||
|
||||
void *ps_alloc(void);
|
||||
void ps_free(void *);
|
||||
|
||||
void terminal_mdoc(void *, const struct mdoc *);
|
||||
void terminal_man(void *, const struct man *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!MAIN_H*/
|
324
commands/mdocml/man.3
Normal file
324
commands/mdocml/man.3
Normal file
|
@ -0,0 +1,324 @@
|
|||
.\" $Id: man.3,v 1.18 2010/05/25 22:16:59 kristaps Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2009-2010 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: May 25 2010 $
|
||||
.Dt MAN 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm man ,
|
||||
.Nm man_alloc ,
|
||||
.Nm man_endparse ,
|
||||
.Nm man_free ,
|
||||
.Nm man_meta ,
|
||||
.Nm man_node ,
|
||||
.Nm man_parseln ,
|
||||
.Nm man_reset
|
||||
.Nd man macro compiler library
|
||||
.Sh SYNOPSIS
|
||||
.In mandoc.h
|
||||
.In man.h
|
||||
.Vt extern const char * const * man_macronames;
|
||||
.Ft "struct man *"
|
||||
.Fn man_alloc "void *data" "int pflags" "mandocmsg msgs"
|
||||
.Ft int
|
||||
.Fn man_endparse "struct man *man"
|
||||
.Ft void
|
||||
.Fn man_free "struct man *man"
|
||||
.Ft "const struct man_meta *"
|
||||
.Fn man_meta "const struct man *man"
|
||||
.Ft "const struct man_node *"
|
||||
.Fn man_node "const struct man *man"
|
||||
.Ft int
|
||||
.Fn man_parseln "struct man *man" "int line" "char *buf"
|
||||
.Ft void
|
||||
.Fn man_reset "struct man *man"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
library parses lines of
|
||||
.Xr man 7
|
||||
input into an abstract syntax tree (AST).
|
||||
.Pp
|
||||
In general, applications initiate a parsing sequence with
|
||||
.Fn man_alloc ,
|
||||
parse each line in a document with
|
||||
.Fn man_parseln ,
|
||||
close the parsing session with
|
||||
.Fn man_endparse ,
|
||||
operate over the syntax tree returned by
|
||||
.Fn man_node
|
||||
and
|
||||
.Fn man_meta ,
|
||||
then free all allocated memory with
|
||||
.Fn man_free .
|
||||
The
|
||||
.Fn man_reset
|
||||
function may be used in order to reset the parser for another input
|
||||
sequence.
|
||||
See the
|
||||
.Sx EXAMPLES
|
||||
section for a full example.
|
||||
.Pp
|
||||
Beyond the full set of macros defined in
|
||||
.Xr man 7 ,
|
||||
the
|
||||
.Nm
|
||||
library also accepts the following macros:
|
||||
.Pp
|
||||
.Bl -tag -width Ds -compact
|
||||
.It PD
|
||||
Has no effect. Handled as a current-scope line macro.
|
||||
.It Sp
|
||||
A synonym for
|
||||
.Sq sp 0.5v
|
||||
.Pq part of the standard preamble for Perl documentation .
|
||||
Handled as a line macro.
|
||||
.It Vb
|
||||
A synonym for
|
||||
.Sq nf
|
||||
.Pq part of the standard preamble for Perl documentation .
|
||||
Handled as a current-scope line macro.
|
||||
.It Ve
|
||||
A synonym for
|
||||
.Sq fi ,
|
||||
closing
|
||||
.Sq Vb
|
||||
.Pq part of the standard preamble for Perl documentation .
|
||||
Handled as a current-scope line macro.
|
||||
.El
|
||||
.Pp
|
||||
Furthermore, the following escapes are accepted to allow
|
||||
.Xr pod2man 1
|
||||
documents to be correctly formatted:
|
||||
\e*(-- (dash),
|
||||
\e*(PI (pi),
|
||||
\e*(L" (left double-quote),
|
||||
\e*(R" (right double-quote),
|
||||
\e*(C+ (C++),
|
||||
\e*(C` (left single-quote),
|
||||
\e*(C' (right single-quote),
|
||||
\e*(Aq (apostrophe),
|
||||
\e*^ (hat),
|
||||
\e*, (comma),
|
||||
\e*~ (tilde),
|
||||
\e*/ (forward slash),
|
||||
\e*: (umlaut),
|
||||
\e*8 (beta),
|
||||
\e*o (degree),
|
||||
\e*(D- (Eth),
|
||||
\e*(d- (eth),
|
||||
\e*(Th (Thorn),
|
||||
and
|
||||
\e*(th (thorn).
|
||||
.Sh REFERENCE
|
||||
This section further defines the
|
||||
.Sx Types ,
|
||||
.Sx Functions
|
||||
and
|
||||
.Sx Variables
|
||||
available to programmers.
|
||||
Following that, the
|
||||
.Sx Abstract Syntax Tree
|
||||
section documents the output tree.
|
||||
.Ss Types
|
||||
Both functions (see
|
||||
.Sx Functions )
|
||||
and variables (see
|
||||
.Sx Variables )
|
||||
may use the following types:
|
||||
.Bl -ohang
|
||||
.It Vt struct man
|
||||
An opaque type defined in
|
||||
.Pa man.c .
|
||||
Its values are only used privately within the library.
|
||||
.It Vt mandocmsg
|
||||
A function callback type defined in
|
||||
.Pa mandoc.h .
|
||||
.It Vt struct man_node
|
||||
A parsed node.
|
||||
Defined in
|
||||
.Pa man.h .
|
||||
See
|
||||
.Sx Abstract Syntax Tree
|
||||
for details.
|
||||
.El
|
||||
.Ss Functions
|
||||
Function descriptions follow:
|
||||
.Bl -ohang
|
||||
.It Fn man_alloc
|
||||
Allocates a parsing structure.
|
||||
The
|
||||
.Fa data
|
||||
pointer is passed to
|
||||
.Fa msgs .
|
||||
The
|
||||
.Fa pflags
|
||||
arguments are defined in
|
||||
.Pa man.h .
|
||||
Returns NULL on failure.
|
||||
If non-NULL, the pointer must be freed with
|
||||
.Fn man_free .
|
||||
.It Fn man_reset
|
||||
Reset the parser for another parse routine.
|
||||
After its use,
|
||||
.Fn man_parseln
|
||||
behaves as if invoked for the first time.
|
||||
.It Fn man_free
|
||||
Free all resources of a parser.
|
||||
The pointer is no longer valid after invocation.
|
||||
.It Fn man_parseln
|
||||
Parse a nil-terminated line of input.
|
||||
This line should not contain the trailing newline.
|
||||
Returns 0 on failure, 1 on success.
|
||||
The input buffer
|
||||
.Fa buf
|
||||
is modified by this function.
|
||||
.It Fn man_endparse
|
||||
Signals that the parse is complete.
|
||||
Note that if
|
||||
.Fn man_endparse
|
||||
is called subsequent to
|
||||
.Fn man_node ,
|
||||
the resulting tree is incomplete.
|
||||
Returns 0 on failure, 1 on success.
|
||||
.It Fn man_node
|
||||
Returns the first node of the parse.
|
||||
Note that if
|
||||
.Fn man_parseln
|
||||
or
|
||||
.Fn man_endparse
|
||||
return 0, the tree will be incomplete.
|
||||
.It Fn man_meta
|
||||
Returns the document's parsed meta-data.
|
||||
If this information has not yet been supplied or
|
||||
.Fn man_parseln
|
||||
or
|
||||
.Fn man_endparse
|
||||
return 0, the data will be incomplete.
|
||||
.El
|
||||
.Ss Variables
|
||||
The following variables are also defined:
|
||||
.Bl -ohang
|
||||
.It Va man_macronames
|
||||
An array of string-ified token names.
|
||||
.El
|
||||
.Ss Abstract Syntax Tree
|
||||
The
|
||||
.Nm
|
||||
functions produce an abstract syntax tree (AST) describing input in a
|
||||
regular form.
|
||||
It may be reviewed at any time with
|
||||
.Fn man_nodes ;
|
||||
however, if called before
|
||||
.Fn man_endparse ,
|
||||
or after
|
||||
.Fn man_endparse
|
||||
or
|
||||
.Fn man_parseln
|
||||
fail, it may be incomplete.
|
||||
.Pp
|
||||
This AST is governed by the ontological rules dictated in
|
||||
.Xr man 7
|
||||
and derives its terminology accordingly.
|
||||
.Pp
|
||||
The AST is composed of
|
||||
.Vt struct man_node
|
||||
nodes with element, root and text types as declared by the
|
||||
.Va type
|
||||
field.
|
||||
Each node also provides its parse point (the
|
||||
.Va line ,
|
||||
.Va sec ,
|
||||
and
|
||||
.Va pos
|
||||
fields), its position in the tree (the
|
||||
.Va parent ,
|
||||
.Va child ,
|
||||
.Va next
|
||||
and
|
||||
.Va prev
|
||||
fields) and some type-specific data.
|
||||
.Pp
|
||||
The tree itself is arranged according to the following normal form,
|
||||
where capitalised non-terminals represent nodes.
|
||||
.Pp
|
||||
.Bl -tag -width "ELEMENTXX" -compact
|
||||
.It ROOT
|
||||
\(<- mnode+
|
||||
.It mnode
|
||||
\(<- ELEMENT | TEXT | BLOCK
|
||||
.It BLOCK
|
||||
\(<- HEAD BODY
|
||||
.It HEAD
|
||||
\(<- mnode*
|
||||
.It BODY
|
||||
\(<- mnode*
|
||||
.It ELEMENT
|
||||
\(<- ELEMENT | TEXT*
|
||||
.It TEXT
|
||||
\(<- [[:alpha:]]*
|
||||
.El
|
||||
.Pp
|
||||
The only elements capable of nesting other elements are those with
|
||||
next-lint scope as documented in
|
||||
.Xr man 7 .
|
||||
.Sh EXAMPLES
|
||||
The following example reads lines from stdin and parses them, operating
|
||||
on the finished parse tree with
|
||||
.Fn parsed .
|
||||
This example does not error-check nor free memory upon failure.
|
||||
.Bd -literal -offset indent
|
||||
struct man *man;
|
||||
struct man_node *node;
|
||||
char *buf;
|
||||
size_t len;
|
||||
int line;
|
||||
|
||||
line = 1;
|
||||
man = man_alloc(NULL, 0, NULL);
|
||||
buf = NULL;
|
||||
alloc_len = 0;
|
||||
|
||||
while ((len = getline(&buf, &alloc_len, stdin)) >= 0) {
|
||||
if (len && buflen[len - 1] = '\en')
|
||||
buf[len - 1] = '\e0';
|
||||
if ( ! man_parseln(man, line, buf))
|
||||
errx(1, "man_parseln");
|
||||
line++;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
if ( ! man_endparse(man))
|
||||
errx(1, "man_endparse");
|
||||
if (NULL == (node = man_node(man)))
|
||||
errx(1, "man_node");
|
||||
|
||||
parsed(man, node);
|
||||
man_free(man);
|
||||
.Ed
|
||||
.Pp
|
||||
Please see
|
||||
.Pa main.c
|
||||
in the source archive for a rigorous reference.
|
||||
.Sh SEE ALSO
|
||||
.Xr mandoc 1 ,
|
||||
.Xr man 7
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
library was written by
|
||||
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
|
968
commands/mdocml/man.7
Normal file
968
commands/mdocml/man.7
Normal file
|
@ -0,0 +1,968 @@
|
|||
.\" $Id: man.7,v 1.74 2010/05/26 14:03:54 kristaps Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: May 26 2010 $
|
||||
.Dt MAN 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm man
|
||||
.Nd man language reference
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm man
|
||||
language was historically used to format
|
||||
.Ux
|
||||
manuals.
|
||||
This reference document describes its syntax, structure, and usage.
|
||||
.Pp
|
||||
.Bf -emphasis
|
||||
Do not use
|
||||
.Nm
|
||||
to write your manuals.
|
||||
.Ef
|
||||
Use the
|
||||
.Xr mdoc 7
|
||||
language, instead.
|
||||
.Pp
|
||||
An
|
||||
.Nm
|
||||
document follows simple rules: lines beginning with the control
|
||||
character
|
||||
.Sq \&.
|
||||
are parsed for macros.
|
||||
Other lines are interpreted within the scope of
|
||||
prior macros:
|
||||
.Bd -literal -offset indent
|
||||
\&.SH Macro lines change control state.
|
||||
Other lines are interpreted within the current state.
|
||||
.Ed
|
||||
.Sh INPUT ENCODING
|
||||
.Nm
|
||||
documents may contain only graphable 7-bit ASCII characters, the
|
||||
space character, and the tabs character.
|
||||
All manuals must have
|
||||
.Ux
|
||||
line termination.
|
||||
.Pp
|
||||
Blank lines are acceptable; where found, the output will assert a
|
||||
vertical space.
|
||||
.Ss Comments
|
||||
Text following a
|
||||
.Sq \e\*" ,
|
||||
whether in a macro or free-form text line, is ignored to the end of
|
||||
line.
|
||||
A macro line with only a control character and comment escape,
|
||||
.Sq \&.\e" ,
|
||||
is also ignored.
|
||||
Macro lines with only a control character and optionally whitespace are
|
||||
stripped from input.
|
||||
.Ss Special Characters
|
||||
Special characters may occur in both macro and free-form lines.
|
||||
Sequences begin with the escape character
|
||||
.Sq \e
|
||||
followed by either an open-parenthesis
|
||||
.Sq \&(
|
||||
for two-character sequences; an open-bracket
|
||||
.Sq \&[
|
||||
for n-character sequences (terminated at a close-bracket
|
||||
.Sq \&] ) ;
|
||||
or a single one-character sequence.
|
||||
See
|
||||
.Xr mandoc_char 7
|
||||
for a complete list.
|
||||
Examples include
|
||||
.Sq \e(em
|
||||
.Pq em-dash
|
||||
and
|
||||
.Sq \ee
|
||||
.Pq back-slash .
|
||||
.Ss Text Decoration
|
||||
Terms may be text-decorated using the
|
||||
.Sq \ef
|
||||
escape followed by an indicator: B (bold), I, (italic), R (Roman), or P
|
||||
(revert to previous mode):
|
||||
.Pp
|
||||
.D1 \efBbold\efR \efIitalic\efP
|
||||
.Pp
|
||||
A numerical representation 3, 2, or 1 (bold, italic, and Roman,
|
||||
respectively) may be used instead.
|
||||
A text decoration is only valid, if specified in free-form text, until
|
||||
the next macro invocation; if specified within a macro, it's only valid
|
||||
until the macro closes scope.
|
||||
Note that macros like
|
||||
.Sx \&BR
|
||||
open and close a font scope with each argument.
|
||||
.Pp
|
||||
Text may also be sized with the
|
||||
.Sq \es
|
||||
escape, whose syntax is one of
|
||||
.Sq \es+-n
|
||||
for one-digit numerals;
|
||||
.Sq \es(+-nn
|
||||
or
|
||||
.Sq \es+-(nn
|
||||
for two-digit numerals; and
|
||||
.Sq \es[+-N] ,
|
||||
.Sq \es+-[N] ,
|
||||
.Sq \es'+-N' ,
|
||||
or
|
||||
.Sq \es+-'N'
|
||||
for arbitrary-digit numerals:
|
||||
.Pp
|
||||
.D1 \es+1bigger\es-1
|
||||
.D1 \es[+10]much bigger\es[-10]
|
||||
.D1 \es+(10much bigger\es-(10
|
||||
.D1 \es+'100'much much bigger\es-'100'
|
||||
.Pp
|
||||
Both
|
||||
.Sq \es
|
||||
and
|
||||
.Sq \ef
|
||||
attributes are forgotten when entering or exiting a macro block.
|
||||
.Ss Whitespace
|
||||
Whitespace consists of the space character.
|
||||
In free-form lines, whitespace is preserved within a line; un-escaped
|
||||
trailing spaces are stripped from input (unless in a literal context).
|
||||
Blank free-form lines, which may include spaces, are permitted and
|
||||
rendered as an empty line.
|
||||
.Pp
|
||||
In macro lines, whitespace delimits arguments and is discarded.
|
||||
If arguments are quoted, whitespace within the quotes is retained.
|
||||
.Ss Dates
|
||||
The
|
||||
.Sx \&TH
|
||||
macro is the only
|
||||
.Nm
|
||||
macro that requires a date.
|
||||
The form for this date is the ISO-8601
|
||||
standard
|
||||
.Cm YYYY-MM-DD .
|
||||
.Ss Scaling Widths
|
||||
Many macros support scaled widths for their arguments, such as
|
||||
stipulating a two-inch paragraph indentation with the following:
|
||||
.Bd -literal -offset indent
|
||||
\&.HP 2i
|
||||
.Ed
|
||||
.Pp
|
||||
The syntax for scaled widths is
|
||||
.Sq Li [+-]?[0-9]*.[0-9]*[:unit:]? ,
|
||||
where a decimal must be preceded or proceeded by at least one digit.
|
||||
Negative numbers, while accepted, are truncated to zero.
|
||||
The following scaling units are accepted:
|
||||
.Pp
|
||||
.Bl -tag -width Ds -offset indent -compact
|
||||
.It c
|
||||
centimetre
|
||||
.It i
|
||||
inch
|
||||
.It P
|
||||
pica (~1/6 inch)
|
||||
.It p
|
||||
point (~1/72 inch)
|
||||
.It f
|
||||
synonym for
|
||||
.Sq u
|
||||
.It v
|
||||
default vertical span
|
||||
.It m
|
||||
width of rendered
|
||||
.Sq m
|
||||
.Pq em
|
||||
character
|
||||
.It n
|
||||
width of rendered
|
||||
.Sq n
|
||||
.Pq en
|
||||
character
|
||||
.It u
|
||||
default horizontal span
|
||||
.It M
|
||||
mini-em (~1/100 em)
|
||||
.El
|
||||
.Pp
|
||||
Using anything other than
|
||||
.Sq m ,
|
||||
.Sq n ,
|
||||
.Sq u ,
|
||||
or
|
||||
.Sq v
|
||||
is necessarily non-portable across output media.
|
||||
.Pp
|
||||
If a scaling unit is not provided, the numerical value is interpreted
|
||||
under the default rules of
|
||||
.Sq v
|
||||
for vertical spaces and
|
||||
.Sq u
|
||||
for horizontal ones.
|
||||
.Em Note :
|
||||
this differs from
|
||||
.Xr mdoc 7 ,
|
||||
which, if a unit is not provided, will instead interpret the string as
|
||||
literal text.
|
||||
.Ss Sentence Spacing
|
||||
When composing a manual, make sure that your sentences end at the end of
|
||||
a line.
|
||||
By doing so, front-ends will be able to apply the proper amount of
|
||||
spacing after the end of sentence (unescaped) period, exclamation mark,
|
||||
or question mark followed by zero or more non-sentence closing
|
||||
delimiters (
|
||||
.Ns Sq \&) ,
|
||||
.Sq \&] ,
|
||||
.Sq \&' ,
|
||||
.Sq \&" ) .
|
||||
.Sh MANUAL STRUCTURE
|
||||
Each
|
||||
.Nm
|
||||
document must contain contains at least the
|
||||
.Sx \&TH
|
||||
macro describing the document's section and title.
|
||||
It may occur anywhere in the document, although conventionally, it
|
||||
appears as the first macro.
|
||||
.Pp
|
||||
Beyond
|
||||
.Sx \&TH ,
|
||||
at least one macro or text node must appear in the document.
|
||||
Documents are generally structured as follows:
|
||||
.Bd -literal -offset indent
|
||||
\&.TH FOO 1 2009-10-10
|
||||
\&.
|
||||
\&.SH NAME
|
||||
\efBfoo\efR \e(en a description goes here
|
||||
\&.\e\*q The next is for sections 2 & 3 only.
|
||||
\&.\e\*q .SH LIBRARY
|
||||
\&.
|
||||
\&.SH SYNOPSIS
|
||||
\efBfoo\efR [\efB\e-options\efR] arguments...
|
||||
\&.
|
||||
\&.SH DESCRIPTION
|
||||
The \efBfoo\efR utility processes files...
|
||||
\&.
|
||||
\&.\e\*q .SH IMPLEMENTATION NOTES
|
||||
\&.\e\*q The next is for sections 2, 3, & 9 only.
|
||||
\&.\e\*q .SH RETURN VALUES
|
||||
\&.\e\*q The next is for sections 1, 6, 7, & 8 only.
|
||||
\&.\e\*q .SH ENVIRONMENT
|
||||
\&.\e\*q .SH FILES
|
||||
\&.\e\*q The next is for sections 1 & 8 only.
|
||||
\&.\e\*q .SH EXIT STATUS
|
||||
\&.\e\*q .SH EXAMPLES
|
||||
\&.\e\*q The next is for sections 1, 4, 6, 7, & 8 only.
|
||||
\&.\e\*q .SH DIAGNOSTICS
|
||||
\&.\e\*q The next is for sections 2, 3, & 9 only.
|
||||
\&.\e\*q .SH ERRORS
|
||||
\&.\e\*q .SH SEE ALSO
|
||||
\&.\e\*q .BR foo ( 1 )
|
||||
\&.\e\*q .SH STANDARDS
|
||||
\&.\e\*q .SH HISTORY
|
||||
\&.\e\*q .SH AUTHORS
|
||||
\&.\e\*q .SH CAVEATS
|
||||
\&.\e\*q .SH BUGS
|
||||
\&.\e\*q .SH SECURITY CONSIDERATIONS
|
||||
.Ed
|
||||
.Pp
|
||||
The sections in a
|
||||
.Nm
|
||||
document are conventionally ordered as they appear above.
|
||||
Sections should be composed as follows:
|
||||
.Bl -ohang -offset indent
|
||||
.It Em NAME
|
||||
The name(s) and a short description of the documented material.
|
||||
The syntax for this is generally as follows:
|
||||
.Pp
|
||||
.D1 \efBname\efR \e(en description
|
||||
.It Em LIBRARY
|
||||
The name of the library containing the documented material, which is
|
||||
assumed to be a function in a section 2 or 3 manual.
|
||||
For functions in the C library, this may be as follows:
|
||||
.Pp
|
||||
.D1 Standard C Library (libc, -lc)
|
||||
.It Em SYNOPSIS
|
||||
Documents the utility invocation syntax, function call syntax, or device
|
||||
configuration.
|
||||
.Pp
|
||||
For the first, utilities (sections 1, 6, and 8), this is
|
||||
generally structured as follows:
|
||||
.Pp
|
||||
.D1 \efBname\efR [-\efBab\efR] [-\efBc\efR\efIarg\efR] \efBpath\efR...
|
||||
.Pp
|
||||
For the second, function calls (sections 2, 3, 9):
|
||||
.Pp
|
||||
.D1 \&.B char *name(char *\efIarg\efR);
|
||||
.Pp
|
||||
And for the third, configurations (section 4):
|
||||
.Pp
|
||||
.D1 \&.B name* at cardbus ? function ?
|
||||
.Pp
|
||||
Manuals not in these sections generally don't need a
|
||||
.Em SYNOPSIS .
|
||||
.It Em DESCRIPTION
|
||||
This expands upon the brief, one-line description in
|
||||
.Em NAME .
|
||||
It usually contains a break-down of the options (if documenting a
|
||||
command).
|
||||
.It Em IMPLEMENTATION NOTES
|
||||
Implementation-specific notes should be kept here.
|
||||
This is useful when implementing standard functions that may have side
|
||||
effects or notable algorithmic implications.
|
||||
.It Em RETURN VALUES
|
||||
This section is the dual of
|
||||
.Em EXIT STATUS ,
|
||||
which is used for commands.
|
||||
It documents the return values of functions in sections 2, 3, and 9.
|
||||
.It Em ENVIRONMENT
|
||||
Documents any usages of environment variables, e.g.,
|
||||
.Xr environ 7 .
|
||||
.It Em FILES
|
||||
Documents files used.
|
||||
It's helpful to document both the file and a short description of how
|
||||
the file is used (created, modified, etc.).
|
||||
.It Em EXIT STATUS
|
||||
Command exit status for section 1, 6, and 8 manuals.
|
||||
This section is the dual of
|
||||
.Em RETURN VALUES ,
|
||||
which is used for functions.
|
||||
Historically, this information was described in
|
||||
.Em DIAGNOSTICS ,
|
||||
a practise that is now discouraged.
|
||||
.It Em EXAMPLES
|
||||
Example usages.
|
||||
This often contains snippets of well-formed,
|
||||
well-tested invocations.
|
||||
Make doubly sure that your examples work properly!
|
||||
.It Em DIAGNOSTICS
|
||||
Documents error conditions.
|
||||
This is most useful in section 4 manuals.
|
||||
Historically, this section was used in place of
|
||||
.Em EXIT STATUS
|
||||
for manuals in sections 1, 6, and 8; however, this practise is
|
||||
discouraged.
|
||||
.It Em ERRORS
|
||||
Documents error handling in sections 2, 3, and 9.
|
||||
.It Em SEE ALSO
|
||||
References other manuals with related topics.
|
||||
This section should exist for most manuals.
|
||||
.Pp
|
||||
.D1 \&.BR bar \&( 1 \&),
|
||||
.Pp
|
||||
Cross-references should conventionally be ordered
|
||||
first by section, then alphabetically.
|
||||
.It Em STANDARDS
|
||||
References any standards implemented or used, such as
|
||||
.Pp
|
||||
.D1 IEEE Std 1003.2 (\e(lqPOSIX.2\e(rq)
|
||||
.Pp
|
||||
If not adhering to any standards, the
|
||||
.Em HISTORY
|
||||
section should be used.
|
||||
.It Em HISTORY
|
||||
The history of any manual without a
|
||||
.Em STANDARDS
|
||||
section should be described in this section.
|
||||
.It Em AUTHORS
|
||||
Credits to authors, if applicable, should appear in this section.
|
||||
Authors should generally be noted by both name and an e-mail address.
|
||||
.It Em CAVEATS
|
||||
Explanations of common misuses and misunderstandings should be explained
|
||||
in this section.
|
||||
.It Em BUGS
|
||||
Extant bugs should be described in this section.
|
||||
.It Em SECURITY CONSIDERATIONS
|
||||
Documents any security precautions that operators should consider.
|
||||
.El
|
||||
.Sh MACRO SYNTAX
|
||||
Macros are one to three three characters in length and begin with a
|
||||
control character ,
|
||||
.Sq \&. ,
|
||||
at the beginning of the line.
|
||||
The
|
||||
.Sq \(aq
|
||||
macro control character is also accepted.
|
||||
An arbitrary amount of whitespace (spaces or tabs) may sit between the
|
||||
control character and the macro name.
|
||||
Thus, the following are equivalent:
|
||||
.Bd -literal -offset indent
|
||||
\&.PP
|
||||
\&.\ \ \ PP
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
macros are classified by scope: line scope or block scope.
|
||||
Line macros are only scoped to the current line (and, in some
|
||||
situations, the subsequent line).
|
||||
Block macros are scoped to the current line and subsequent lines until
|
||||
closed by another block macro.
|
||||
.Ss Line Macros
|
||||
Line macros are generally scoped to the current line, with the body
|
||||
consisting of zero or more arguments.
|
||||
If a macro is scoped to the next line and the line arguments are empty,
|
||||
the next line, which must be text, is used instead.
|
||||
Thus:
|
||||
.Bd -literal -offset indent
|
||||
\&.I
|
||||
foo
|
||||
.Ed
|
||||
.Pp
|
||||
is equivalent to
|
||||
.Sq \&.I foo .
|
||||
If next-line macros are invoked consecutively, only the last is used.
|
||||
If a next-line macro is followed by a non-next-line macro, an error is
|
||||
raised (unless in the case of
|
||||
.Sx \&br ,
|
||||
.Sx \&sp ,
|
||||
or
|
||||
.Sx \&na ) .
|
||||
.Pp
|
||||
The syntax is as follows:
|
||||
.Bd -literal -offset indent
|
||||
\&.YO \(lBbody...\(rB
|
||||
\(lBbody...\(rB
|
||||
.Ed
|
||||
.Pp
|
||||
.Bl -column -compact -offset indent "MacroX" "ArgumentsX" "ScopeXXXXX" "CompatX"
|
||||
.It Em Macro Ta Em Arguments Ta Em Scope Ta Em Notes
|
||||
.It Sx \&AT Ta <=1 Ta current Ta \&
|
||||
.It Sx \&B Ta n Ta next-line Ta \&
|
||||
.It Sx \&BI Ta n Ta current Ta \&
|
||||
.It Sx \&BR Ta n Ta current Ta \&
|
||||
.It Sx \&DT Ta 0 Ta current Ta \&
|
||||
.It Sx \&I Ta n Ta next-line Ta \&
|
||||
.It Sx \&IB Ta n Ta current Ta \&
|
||||
.It Sx \&IR Ta n Ta current Ta \&
|
||||
.\" .It Sx \&PD Ta n Ta current Ta compat
|
||||
.It Sx \&R Ta n Ta next-line Ta \&
|
||||
.It Sx \&RB Ta n Ta current Ta \&
|
||||
.It Sx \&RI Ta n Ta current Ta \&
|
||||
.It Sx \&SB Ta n Ta next-line Ta \&
|
||||
.It Sx \&SM Ta n Ta next-line Ta \&
|
||||
.It Sx \&TH Ta >1, <6 Ta current Ta \&
|
||||
.It Sx \&UC Ta <=1 Ta current Ta \&
|
||||
.It Sx \&br Ta 0 Ta current Ta compat
|
||||
.It Sx \&fi Ta 0 Ta current Ta compat
|
||||
.It Sx \&i Ta n Ta current Ta compat
|
||||
.It Sx \&na Ta 0 Ta current Ta compat
|
||||
.It Sx \&nf Ta 0 Ta current Ta compat
|
||||
.It Sx \&r Ta 0 Ta current Ta compat
|
||||
.It Sx \&sp Ta 1 Ta current Ta compat
|
||||
.\" .It Sx \&Sp Ta <1 Ta current Ta compat
|
||||
.\" .It Sx \&Vb Ta <1 Ta current Ta compat
|
||||
.\" .It Sx \&Ve Ta 0 Ta current Ta compat
|
||||
.El
|
||||
.Pp
|
||||
Macros marked as
|
||||
.Qq compat
|
||||
are included for compatibility with the significant corpus of existing
|
||||
manuals that mix dialects of roff.
|
||||
These macros should not be used for portable
|
||||
.Nm
|
||||
manuals.
|
||||
.Ss Block Macros
|
||||
Block macros are comprised of a head and body.
|
||||
Like for in-line macros, the head is scoped to the current line and, in
|
||||
one circumstance, the next line (the next-line stipulations as in
|
||||
.Sx Line Macros
|
||||
apply here as well).
|
||||
.Pp
|
||||
The syntax is as follows:
|
||||
.Bd -literal -offset indent
|
||||
\&.YO \(lBhead...\(rB
|
||||
\(lBhead...\(rB
|
||||
\(lBbody...\(rB
|
||||
.Ed
|
||||
.Pp
|
||||
The closure of body scope may be to the section, where a macro is closed
|
||||
by
|
||||
.Sx \&SH ;
|
||||
sub-section, closed by a section or
|
||||
.Sx \&SS ;
|
||||
part, closed by a section, sub-section, or
|
||||
.Sx \&RE ;
|
||||
or paragraph, closed by a section, sub-section, part,
|
||||
.Sx \&HP ,
|
||||
.Sx \&IP ,
|
||||
.Sx \&LP ,
|
||||
.Sx \&P ,
|
||||
.Sx \&PP ,
|
||||
or
|
||||
.Sx \&TP .
|
||||
No closure refers to an explicit block closing macro.
|
||||
.Pp
|
||||
As a rule, block macros may not be nested; thus, calling a block macro
|
||||
while another block macro scope is open, and the open scope is not
|
||||
implicitly closed, is syntactically incorrect.
|
||||
.Pp
|
||||
.Bl -column -compact -offset indent "MacroX" "ArgumentsX" "Head ScopeX" "sub-sectionX" "compatX"
|
||||
.It Em Macro Ta Em Arguments Ta Em Head Scope Ta Em Body Scope Ta Em Notes
|
||||
.It Sx \&HP Ta <2 Ta current Ta paragraph Ta \&
|
||||
.It Sx \&IP Ta <3 Ta current Ta paragraph Ta \&
|
||||
.It Sx \&LP Ta 0 Ta current Ta paragraph Ta \&
|
||||
.It Sx \&P Ta 0 Ta current Ta paragraph Ta \&
|
||||
.It Sx \&PP Ta 0 Ta current Ta paragraph Ta \&
|
||||
.It Sx \&RE Ta 0 Ta current Ta none Ta compat
|
||||
.It Sx \&RS Ta 1 Ta current Ta part Ta compat
|
||||
.It Sx \&SH Ta >0 Ta next-line Ta section Ta \&
|
||||
.It Sx \&SS Ta >0 Ta next-line Ta sub-section Ta \&
|
||||
.It Sx \&TP Ta n Ta next-line Ta paragraph Ta \&
|
||||
.El
|
||||
.Pp
|
||||
Macros marked
|
||||
.Qq compat
|
||||
are as mentioned in
|
||||
.Sx Line Macros .
|
||||
.Pp
|
||||
If a block macro is next-line scoped, it may only be followed by in-line
|
||||
macros for decorating text.
|
||||
.Sh REFERENCE
|
||||
This section is a canonical reference to all macros, arranged
|
||||
alphabetically.
|
||||
For the scoping of individual macros, see
|
||||
.Sx MACRO SYNTAX .
|
||||
.Ss \&AT
|
||||
Sets the volume for the footer for compatibility with man pages from
|
||||
.Tn AT&T UNIX
|
||||
releases.
|
||||
The optional arguments specify which release it is from.
|
||||
.Ss \&B
|
||||
Text is rendered in bold face.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&I ,
|
||||
.Sx \&R ,
|
||||
.Sx \&b ,
|
||||
.Sx \&i ,
|
||||
and
|
||||
.Sx \&r .
|
||||
.Ss \&BI
|
||||
Text is rendered alternately in bold face and italic.
|
||||
Thus,
|
||||
.Sq .BI this word and that
|
||||
causes
|
||||
.Sq this
|
||||
and
|
||||
.Sq and
|
||||
to render in bold face, while
|
||||
.Sq word
|
||||
and
|
||||
.Sq that
|
||||
render in italics.
|
||||
Whitespace between arguments is omitted in output.
|
||||
.Pp
|
||||
Examples:
|
||||
.Pp
|
||||
.D1 \&.BI bold italic bold italic
|
||||
.Pp
|
||||
The output of this example will be emboldened
|
||||
.Dq bold
|
||||
and italicised
|
||||
.Dq italic ,
|
||||
with spaces stripped between arguments.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&IB ,
|
||||
.Sx \&BR ,
|
||||
.Sx \&RB ,
|
||||
.Sx \&RI ,
|
||||
and
|
||||
.Sx \&IR .
|
||||
.Ss \&BR
|
||||
Text is rendered alternately in bold face and roman (the default font).
|
||||
Whitespace between arguments is omitted in output.
|
||||
.Pp
|
||||
See
|
||||
.Sx \&BI
|
||||
for an equivalent example.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&BI ,
|
||||
.Sx \&IB ,
|
||||
.Sx \&RB ,
|
||||
.Sx \&RI ,
|
||||
and
|
||||
.Sx \&IR .
|
||||
.Ss \&DT
|
||||
Has no effect.
|
||||
Included for compatibility.
|
||||
.Ss \&HP
|
||||
Begin a paragraph whose initial output line is left-justified, but
|
||||
subsequent output lines are indented, with the following syntax:
|
||||
.Bd -filled -offset indent
|
||||
.Pf \. Sx \&HP
|
||||
.Op Cm width
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Cm width
|
||||
argument must conform to
|
||||
.Sx Scaling Widths .
|
||||
If specified, it's saved for later paragraph left-margins; if unspecified, the
|
||||
saved or default width is used.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&IP ,
|
||||
.Sx \&LP ,
|
||||
.Sx \&P ,
|
||||
.Sx \&PP ,
|
||||
and
|
||||
.Sx \&TP .
|
||||
.Ss \&I
|
||||
Text is rendered in italics.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&B ,
|
||||
.Sx \&R ,
|
||||
.Sx \&b ,
|
||||
.Sx \&i ,
|
||||
and
|
||||
.Sx \&r .
|
||||
.Ss \&IB
|
||||
Text is rendered alternately in italics and bold face. Whitespace
|
||||
between arguments is omitted in output.
|
||||
.Pp
|
||||
See
|
||||
.Sx \&BI
|
||||
for an equivalent example.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&BI ,
|
||||
.Sx \&BR ,
|
||||
.Sx \&RB ,
|
||||
.Sx \&RI ,
|
||||
and
|
||||
.Sx \&IR .
|
||||
.Ss \&IP
|
||||
Begin an indented paragraph with the following syntax:
|
||||
.Bd -filled -offset indent
|
||||
.Pf \. Sx \&IP
|
||||
.Op Cm head Op Cm width
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Cm width
|
||||
argument defines the width of the left margin and is defined by
|
||||
.Sx Scaling Widths ,
|
||||
It's saved for later paragraph left-margins; if unspecified, the saved or
|
||||
default width is used.
|
||||
.Pp
|
||||
The
|
||||
.Cm head
|
||||
argument is used as a leading term, flushed to the left margin.
|
||||
This is useful for bulleted paragraphs and so on.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&HP ,
|
||||
.Sx \&LP ,
|
||||
.Sx \&P ,
|
||||
.Sx \&PP ,
|
||||
and
|
||||
.Sx \&TP .
|
||||
.Ss \&IR
|
||||
Text is rendered alternately in italics and roman (the default font).
|
||||
Whitespace between arguments is omitted in output.
|
||||
.Pp
|
||||
See
|
||||
.Sx \&BI
|
||||
for an equivalent example.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&BI ,
|
||||
.Sx \&IB ,
|
||||
.Sx \&BR ,
|
||||
.Sx \&RB ,
|
||||
and
|
||||
.Sx \&RI .
|
||||
.Ss \&LP
|
||||
Begin an undecorated paragraph.
|
||||
The scope of a paragraph is closed by a subsequent paragraph,
|
||||
sub-section, section, or end of file.
|
||||
The saved paragraph left-margin width is re-set to the default.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&HP ,
|
||||
.Sx \&IP ,
|
||||
.Sx \&P ,
|
||||
.Sx \&PP ,
|
||||
and
|
||||
.Sx \&TP .
|
||||
.Ss \&P
|
||||
Synonym for
|
||||
.Sx \&LP .
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&HP ,
|
||||
.Sx \&IP ,
|
||||
.Sx \&LP ,
|
||||
.Sx \&PP ,
|
||||
and
|
||||
.Sx \&TP .
|
||||
.Ss \&PP
|
||||
Synonym for
|
||||
.Sx \&LP .
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&HP ,
|
||||
.Sx \&IP ,
|
||||
.Sx \&LP ,
|
||||
.Sx \&P ,
|
||||
and
|
||||
.Sx \&TP .
|
||||
.Ss \&R
|
||||
Text is rendered in roman (the default font).
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&I ,
|
||||
.Sx \&B ,
|
||||
.Sx \&b ,
|
||||
.Sx \&i ,
|
||||
and
|
||||
.Sx \&r .
|
||||
.Ss \&RB
|
||||
Text is rendered alternately in roman (the default font) and bold face.
|
||||
Whitespace between arguments is omitted in output.
|
||||
.Pp
|
||||
See
|
||||
.Sx \&BI
|
||||
for an equivalent example.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&BI ,
|
||||
.Sx \&IB ,
|
||||
.Sx \&BR ,
|
||||
.Sx \&RI ,
|
||||
and
|
||||
.Sx \&IR .
|
||||
.Ss \&RE
|
||||
Explicitly close out the scope of a prior
|
||||
.Sx \&RS .
|
||||
.Ss \&RI
|
||||
Text is rendered alternately in roman (the default font) and italics.
|
||||
Whitespace between arguments is omitted in output.
|
||||
.Pp
|
||||
See
|
||||
.Sx \&BI
|
||||
for an equivalent example.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&BI ,
|
||||
.Sx \&IB ,
|
||||
.Sx \&BR ,
|
||||
.Sx \&RB ,
|
||||
and
|
||||
.Sx \&IR .
|
||||
.Ss \&RS
|
||||
Begin a part setting the left margin.
|
||||
The left margin controls the offset, following an initial indentation,
|
||||
to un-indented text such as that of
|
||||
.Sx \&PP .
|
||||
This has the following syntax:
|
||||
.Bd -filled -offset indent
|
||||
.Pf \. Sx \&Rs
|
||||
.Op Cm width
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Cm width
|
||||
argument must conform to
|
||||
.Sx Scaling Widths .
|
||||
If not specified, the saved or default width is used.
|
||||
.Ss \&SB
|
||||
Text is rendered in small size (one point smaller than the default font)
|
||||
bold face.
|
||||
.Ss \&SH
|
||||
Begin a section.
|
||||
The scope of a section is only closed by another section or the end of
|
||||
file.
|
||||
The paragraph left-margin width is re-set to the default.
|
||||
.Ss \&SM
|
||||
Text is rendered in small size (one point smaller than the default
|
||||
font).
|
||||
.Ss \&SS
|
||||
Begin a sub-section.
|
||||
The scope of a sub-section is closed by a subsequent sub-section,
|
||||
section, or end of file.
|
||||
The paragraph left-margin width is re-set to the default.
|
||||
.Ss \&TH
|
||||
Sets the title of the manual page with the following syntax:
|
||||
.Bd -filled -offset indent
|
||||
.Pf \. Sx \&TH
|
||||
.Cm title section
|
||||
.Op Cm date Op Cm source Op Cm volume
|
||||
.Ed
|
||||
.Pp
|
||||
At least the upper-case document title
|
||||
.Cm title
|
||||
and numeric manual section
|
||||
.Cm section
|
||||
arguments must be provided.
|
||||
The
|
||||
.Cm date
|
||||
argument should be formatted as described in
|
||||
.Sx Dates ,
|
||||
but will be printed verbatim if it is not.
|
||||
If the date is not specified, the current date is used.
|
||||
The
|
||||
.Cm source
|
||||
string specifies the organisation providing the utility.
|
||||
The
|
||||
.Cm volume
|
||||
string replaces the default rendered volume, which is dictated by the
|
||||
manual section.
|
||||
.Pp
|
||||
Examples:
|
||||
.Pp
|
||||
.D1 \&.TH CVS 5 "1992-02-12" GNU
|
||||
.Ss \&TP
|
||||
Begin a paragraph where the head, if exceeding the indentation width, is
|
||||
followed by a newline; if not, the body follows on the same line after a
|
||||
buffer to the indentation width.
|
||||
Subsequent output lines are indented.
|
||||
The syntax is as follows:
|
||||
.Bd -filled -offset indent
|
||||
.Pf \. Sx \&TP
|
||||
.Op Cm width
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Cm width
|
||||
argument must conform to
|
||||
.Sx Scaling Widths .
|
||||
If specified, it's saved for later paragraph left-margins; if
|
||||
unspecified, the saved or default width is used.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&HP ,
|
||||
.Sx \&IP ,
|
||||
.Sx \&LP ,
|
||||
.Sx \&P ,
|
||||
and
|
||||
.Sx \&PP .
|
||||
.\" .
|
||||
.\" .
|
||||
.\" .Ss \&PD
|
||||
.\" Has no effect. Included for compatibility.
|
||||
.\" .
|
||||
.\" .
|
||||
.Ss \&UC
|
||||
Sets the volume for the footer for compatibility with man pages from
|
||||
BSD releases.
|
||||
The optional first argument specifies which release it is from.
|
||||
.Ss \&br
|
||||
Breaks the current line.
|
||||
Consecutive invocations have no further effect.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&sp .
|
||||
.Ss \&fi
|
||||
End literal mode begun by
|
||||
.Sx \&nf .
|
||||
.Ss \&i
|
||||
Italicise arguments.
|
||||
Synonym for
|
||||
.Sx \&I .
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&B ,
|
||||
.Sx \&I ,
|
||||
.Sx \&R .
|
||||
.Sx \&b ,
|
||||
and
|
||||
.Sx \&r .
|
||||
.Ss \&na
|
||||
Don't align to the right margin.
|
||||
.Ss \&nf
|
||||
Begin literal mode: all subsequent free-form lines have their end of
|
||||
line boundaries preserved.
|
||||
May be ended by
|
||||
.Sx \&fi .
|
||||
.Ss \&r
|
||||
Fonts and styles (bold face, italics) reset to roman (default font).
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&B ,
|
||||
.Sx \&I ,
|
||||
.Sx \&R ,
|
||||
.Sx \&b ,
|
||||
and
|
||||
.Sx \&i .
|
||||
.Ss \&sp
|
||||
Insert vertical spaces into output with the following syntax:
|
||||
.Bd -filled -offset indent
|
||||
.Pf \. Sx \&sp
|
||||
.Op Cm height
|
||||
.Ed
|
||||
.Pp
|
||||
Insert
|
||||
.Cm height
|
||||
spaces, which must conform to
|
||||
.Sx Scaling Widths .
|
||||
If 0, this is equivalent to the
|
||||
.Sx \&br
|
||||
macro.
|
||||
Defaults to 1, if unspecified.
|
||||
.Pp
|
||||
See also
|
||||
.Sx \&br .
|
||||
.\" .Ss \&Sp
|
||||
.\" A synonym for
|
||||
.\" .Sx \&sp
|
||||
.\" .Cm 0.5v .
|
||||
.\" .
|
||||
.\" .Ss \&Vb
|
||||
.\" A synonym for
|
||||
.\" .Sx \&nf .
|
||||
.\" Accepts an argument (the height of the formatted space) which is
|
||||
.\" disregarded.
|
||||
.\" .
|
||||
.\" .Ss \&Ve
|
||||
.\" A synonym for
|
||||
.\" .Sx \&fi .
|
||||
.\" .
|
||||
.Sh COMPATIBILITY
|
||||
This section documents areas of questionable portability between
|
||||
implementations of the
|
||||
.Nm
|
||||
language.
|
||||
.Pp
|
||||
.Bl -dash -compact
|
||||
.It
|
||||
In quoted literals, GNU troff allowed pair-wise double-quotes to produce
|
||||
a standalone double-quote in formatted output.
|
||||
It is not known whether this behaviour is exhibited by other formatters.
|
||||
.It
|
||||
The
|
||||
.Sx \&sp
|
||||
macro does not accept negative values in mandoc.
|
||||
In GNU troff, this would result in strange behaviour.
|
||||
.It
|
||||
The
|
||||
.Sq \(aq
|
||||
macro control character, in GNU troff (and prior troffs) suppresses a
|
||||
newline before macro output; in mandoc, it is an alias for the standard
|
||||
.Sq \&.
|
||||
control character.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr mandoc 1 ,
|
||||
.Xr mandoc_char 7
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
reference was written by
|
||||
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
|
||||
.Sh CAVEATS
|
||||
Do not use this language.
|
||||
Use
|
||||
.Xr mdoc 7 ,
|
||||
instead.
|
677
commands/mdocml/man.c
Normal file
677
commands/mdocml/man.c
Normal file
|
@ -0,0 +1,677 @@
|
|||
/* $Id: man.c,v 1.76 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libman.h"
|
||||
#include "libmandoc.h"
|
||||
|
||||
const char *const __man_macronames[MAN_MAX] = {
|
||||
"br", "TH", "SH", "SS",
|
||||
"TP", "LP", "PP", "P",
|
||||
"IP", "HP", "SM", "SB",
|
||||
"BI", "IB", "BR", "RB",
|
||||
"R", "B", "I", "IR",
|
||||
"RI", "na", "i", "sp",
|
||||
"nf", "fi", "r", "RE",
|
||||
"RS", "DT", "UC", "PD",
|
||||
"Sp", "Vb", "Ve", "AT",
|
||||
};
|
||||
|
||||
const char * const *man_macronames = __man_macronames;
|
||||
|
||||
static struct man_node *man_node_alloc(int, int,
|
||||
enum man_type, enum mant);
|
||||
static int man_node_append(struct man *,
|
||||
struct man_node *);
|
||||
static void man_node_free(struct man_node *);
|
||||
static void man_node_unlink(struct man *,
|
||||
struct man_node *);
|
||||
static int man_ptext(struct man *, int, char *, int);
|
||||
static int man_pmacro(struct man *, int, char *, int);
|
||||
static void man_free1(struct man *);
|
||||
static void man_alloc1(struct man *);
|
||||
static int macrowarn(struct man *, int, const char *, int);
|
||||
|
||||
|
||||
const struct man_node *
|
||||
man_node(const struct man *m)
|
||||
{
|
||||
|
||||
return(MAN_HALT & m->flags ? NULL : m->first);
|
||||
}
|
||||
|
||||
|
||||
const struct man_meta *
|
||||
man_meta(const struct man *m)
|
||||
{
|
||||
|
||||
return(MAN_HALT & m->flags ? NULL : &m->meta);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
man_reset(struct man *man)
|
||||
{
|
||||
|
||||
man_free1(man);
|
||||
man_alloc1(man);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
man_free(struct man *man)
|
||||
{
|
||||
|
||||
man_free1(man);
|
||||
free(man);
|
||||
}
|
||||
|
||||
|
||||
struct man *
|
||||
man_alloc(void *data, int pflags, mandocmsg msg)
|
||||
{
|
||||
struct man *p;
|
||||
|
||||
p = mandoc_calloc(1, sizeof(struct man));
|
||||
|
||||
man_hash_init();
|
||||
p->data = data;
|
||||
p->pflags = pflags;
|
||||
p->msg = msg;
|
||||
|
||||
man_alloc1(p);
|
||||
return(p);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_endparse(struct man *m)
|
||||
{
|
||||
|
||||
if (MAN_HALT & m->flags)
|
||||
return(0);
|
||||
else if (man_macroend(m))
|
||||
return(1);
|
||||
m->flags |= MAN_HALT;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_parseln(struct man *m, int ln, char *buf, int offs)
|
||||
{
|
||||
|
||||
if (MAN_HALT & m->flags)
|
||||
return(0);
|
||||
|
||||
return(('.' == buf[offs] || '\'' == buf[offs]) ?
|
||||
man_pmacro(m, ln, buf, offs) :
|
||||
man_ptext(m, ln, buf, offs));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
man_free1(struct man *man)
|
||||
{
|
||||
|
||||
if (man->first)
|
||||
man_node_delete(man, man->first);
|
||||
if (man->meta.title)
|
||||
free(man->meta.title);
|
||||
if (man->meta.source)
|
||||
free(man->meta.source);
|
||||
if (man->meta.rawdate)
|
||||
free(man->meta.rawdate);
|
||||
if (man->meta.vol)
|
||||
free(man->meta.vol);
|
||||
if (man->meta.msec)
|
||||
free(man->meta.msec);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
man_alloc1(struct man *m)
|
||||
{
|
||||
|
||||
memset(&m->meta, 0, sizeof(struct man_meta));
|
||||
m->flags = 0;
|
||||
m->last = mandoc_calloc(1, sizeof(struct man_node));
|
||||
m->first = m->last;
|
||||
m->last->type = MAN_ROOT;
|
||||
m->last->tok = MAN_MAX;
|
||||
m->next = MAN_NEXT_CHILD;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
man_node_append(struct man *man, struct man_node *p)
|
||||
{
|
||||
|
||||
assert(man->last);
|
||||
assert(man->first);
|
||||
assert(MAN_ROOT != p->type);
|
||||
|
||||
switch (man->next) {
|
||||
case (MAN_NEXT_SIBLING):
|
||||
man->last->next = p;
|
||||
p->prev = man->last;
|
||||
p->parent = man->last->parent;
|
||||
break;
|
||||
case (MAN_NEXT_CHILD):
|
||||
man->last->child = p;
|
||||
p->parent = man->last;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
assert(p->parent);
|
||||
p->parent->nchild++;
|
||||
|
||||
if ( ! man_valid_pre(man, p))
|
||||
return(0);
|
||||
|
||||
switch (p->type) {
|
||||
case (MAN_HEAD):
|
||||
assert(MAN_BLOCK == p->parent->type);
|
||||
p->parent->head = p;
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
assert(MAN_BLOCK == p->parent->type);
|
||||
p->parent->body = p;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
man->last = p;
|
||||
|
||||
switch (p->type) {
|
||||
case (MAN_TEXT):
|
||||
if ( ! man_valid_post(man))
|
||||
return(0);
|
||||
if ( ! man_action_post(man))
|
||||
return(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static struct man_node *
|
||||
man_node_alloc(int line, int pos, enum man_type type, enum mant tok)
|
||||
{
|
||||
struct man_node *p;
|
||||
|
||||
p = mandoc_calloc(1, sizeof(struct man_node));
|
||||
p->line = line;
|
||||
p->pos = pos;
|
||||
p->type = type;
|
||||
p->tok = tok;
|
||||
return(p);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_elem_alloc(struct man *m, int line, int pos, enum mant tok)
|
||||
{
|
||||
struct man_node *p;
|
||||
|
||||
p = man_node_alloc(line, pos, MAN_ELEM, tok);
|
||||
if ( ! man_node_append(m, p))
|
||||
return(0);
|
||||
m->next = MAN_NEXT_CHILD;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_head_alloc(struct man *m, int line, int pos, enum mant tok)
|
||||
{
|
||||
struct man_node *p;
|
||||
|
||||
p = man_node_alloc(line, pos, MAN_HEAD, tok);
|
||||
if ( ! man_node_append(m, p))
|
||||
return(0);
|
||||
m->next = MAN_NEXT_CHILD;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_body_alloc(struct man *m, int line, int pos, enum mant tok)
|
||||
{
|
||||
struct man_node *p;
|
||||
|
||||
p = man_node_alloc(line, pos, MAN_BODY, tok);
|
||||
if ( ! man_node_append(m, p))
|
||||
return(0);
|
||||
m->next = MAN_NEXT_CHILD;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_block_alloc(struct man *m, int line, int pos, enum mant tok)
|
||||
{
|
||||
struct man_node *p;
|
||||
|
||||
p = man_node_alloc(line, pos, MAN_BLOCK, tok);
|
||||
if ( ! man_node_append(m, p))
|
||||
return(0);
|
||||
m->next = MAN_NEXT_CHILD;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_word_alloc(struct man *m, int line, int pos, const char *word)
|
||||
{
|
||||
struct man_node *n;
|
||||
size_t sv, len;
|
||||
|
||||
len = strlen(word);
|
||||
|
||||
n = man_node_alloc(line, pos, MAN_TEXT, MAN_MAX);
|
||||
n->string = mandoc_malloc(len + 1);
|
||||
sv = strlcpy(n->string, word, len + 1);
|
||||
|
||||
/* Prohibit truncation. */
|
||||
assert(sv < len + 1);
|
||||
|
||||
if ( ! man_node_append(m, n))
|
||||
return(0);
|
||||
|
||||
m->next = MAN_NEXT_SIBLING;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Free all of the resources held by a node. This does NOT unlink a
|
||||
* node from its context; for that, see man_node_unlink().
|
||||
*/
|
||||
static void
|
||||
man_node_free(struct man_node *p)
|
||||
{
|
||||
|
||||
if (p->string)
|
||||
free(p->string);
|
||||
free(p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
man_node_delete(struct man *m, struct man_node *p)
|
||||
{
|
||||
|
||||
while (p->child)
|
||||
man_node_delete(m, p->child);
|
||||
|
||||
man_node_unlink(m, p);
|
||||
man_node_free(p);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
man_ptext(struct man *m, int line, char *buf, int offs)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Ignore bogus comments. */
|
||||
|
||||
if ('\\' == buf[offs] &&
|
||||
'.' == buf[offs + 1] &&
|
||||
'"' == buf[offs + 2])
|
||||
return(man_pmsg(m, line, offs, MANDOCERR_BADCOMMENT));
|
||||
|
||||
/* Literal free-form text whitespace is preserved. */
|
||||
|
||||
if (MAN_LITERAL & m->flags) {
|
||||
if ( ! man_word_alloc(m, line, offs, buf + offs))
|
||||
return(0);
|
||||
goto descope;
|
||||
}
|
||||
|
||||
/* Pump blank lines directly into the backend. */
|
||||
|
||||
for (i = offs; ' ' == buf[i]; i++)
|
||||
/* Skip leading whitespace. */ ;
|
||||
|
||||
if ('\0' == buf[i]) {
|
||||
/* Allocate a blank entry. */
|
||||
if ( ! man_word_alloc(m, line, offs, ""))
|
||||
return(0);
|
||||
goto descope;
|
||||
}
|
||||
|
||||
/*
|
||||
* Warn if the last un-escaped character is whitespace. Then
|
||||
* strip away the remaining spaces (tabs stay!).
|
||||
*/
|
||||
|
||||
i = (int)strlen(buf);
|
||||
assert(i);
|
||||
|
||||
if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
|
||||
if (i > 1 && '\\' != buf[i - 2])
|
||||
if ( ! man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE))
|
||||
return(0);
|
||||
|
||||
for (--i; i && ' ' == buf[i]; i--)
|
||||
/* Spin back to non-space. */ ;
|
||||
|
||||
/* Jump ahead of escaped whitespace. */
|
||||
i += '\\' == buf[i] ? 2 : 1;
|
||||
|
||||
buf[i] = '\0';
|
||||
}
|
||||
|
||||
if ( ! man_word_alloc(m, line, offs, buf + offs))
|
||||
return(0);
|
||||
|
||||
/*
|
||||
* End-of-sentence check. If the last character is an unescaped
|
||||
* EOS character, then flag the node as being the end of a
|
||||
* sentence. The front-end will know how to interpret this.
|
||||
*/
|
||||
|
||||
assert(i);
|
||||
if (mandoc_eos(buf, (size_t)i))
|
||||
m->last->flags |= MAN_EOS;
|
||||
|
||||
descope:
|
||||
/*
|
||||
* Co-ordinate what happens with having a next-line scope open:
|
||||
* first close out the element scope (if applicable), then close
|
||||
* out the block scope (also if applicable).
|
||||
*/
|
||||
|
||||
if (MAN_ELINE & m->flags) {
|
||||
m->flags &= ~MAN_ELINE;
|
||||
if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
|
||||
return(0);
|
||||
}
|
||||
|
||||
if ( ! (MAN_BLINE & m->flags))
|
||||
return(1);
|
||||
m->flags &= ~MAN_BLINE;
|
||||
|
||||
if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
|
||||
return(0);
|
||||
return(man_body_alloc(m, line, offs, m->last->tok));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
macrowarn(struct man *m, int ln, const char *buf, int offs)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = man_vmsg(m, MANDOCERR_MACRO, ln, offs,
|
||||
"unknown macro: %s%s",
|
||||
buf, strlen(buf) > 3 ? "..." : "");
|
||||
|
||||
return(MAN_IGN_MACRO & m->pflags ? rc : 0);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
man_pmacro(struct man *m, int ln, char *buf, int offs)
|
||||
{
|
||||
int i, j, ppos;
|
||||
enum mant tok;
|
||||
char mac[5];
|
||||
struct man_node *n;
|
||||
|
||||
/* Comments and empties are quickly ignored. */
|
||||
|
||||
offs++;
|
||||
|
||||
if ('\0' == buf[offs])
|
||||
return(1);
|
||||
|
||||
i = offs;
|
||||
|
||||
/*
|
||||
* Skip whitespace between the control character and initial
|
||||
* text. "Whitespace" is both spaces and tabs.
|
||||
*/
|
||||
|
||||
if (' ' == buf[i] || '\t' == buf[i]) {
|
||||
i++;
|
||||
while (buf[i] && (' ' == buf[i] || '\t' == buf[i]))
|
||||
i++;
|
||||
if ('\0' == buf[i])
|
||||
goto out;
|
||||
}
|
||||
|
||||
ppos = i;
|
||||
|
||||
/* Copy the first word into a nil-terminated buffer. */
|
||||
|
||||
for (j = 0; j < 4; j++, i++) {
|
||||
if ('\0' == (mac[j] = buf[i]))
|
||||
break;
|
||||
else if (' ' == buf[i])
|
||||
break;
|
||||
|
||||
/* Check for invalid characters. */
|
||||
|
||||
if (isgraph((u_char)buf[i]))
|
||||
continue;
|
||||
if ( ! man_pmsg(m, ln, i, MANDOCERR_BADCHAR))
|
||||
return(0);
|
||||
i--;
|
||||
}
|
||||
|
||||
mac[j] = '\0';
|
||||
|
||||
if (j == 4 || j < 1) {
|
||||
if ( ! macrowarn(m, ln, mac, ppos))
|
||||
goto err;
|
||||
return(1);
|
||||
}
|
||||
|
||||
if (MAN_MAX == (tok = man_hash_find(mac))) {
|
||||
if ( ! macrowarn(m, ln, mac, ppos))
|
||||
goto err;
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* The macro is sane. Jump to the next word. */
|
||||
|
||||
while (buf[i] && ' ' == buf[i])
|
||||
i++;
|
||||
|
||||
/*
|
||||
* Trailing whitespace. Note that tabs are allowed to be passed
|
||||
* into the parser as "text", so we only warn about spaces here.
|
||||
*/
|
||||
|
||||
if ('\0' == buf[i] && ' ' == buf[i - 1])
|
||||
if ( ! man_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Remove prior ELINE macro, as it's being clobbering by a new
|
||||
* macro. Note that NSCOPED macros do not close out ELINE
|
||||
* macros---they don't print text---so we let those slip by.
|
||||
*/
|
||||
|
||||
if ( ! (MAN_NSCOPED & man_macros[tok].flags) &&
|
||||
m->flags & MAN_ELINE) {
|
||||
assert(MAN_TEXT != m->last->type);
|
||||
|
||||
/*
|
||||
* This occurs in the following construction:
|
||||
* .B
|
||||
* .br
|
||||
* .B
|
||||
* .br
|
||||
* I hate man macros.
|
||||
* Flat-out disallow this madness.
|
||||
*/
|
||||
if (MAN_NSCOPED & man_macros[m->last->tok].flags) {
|
||||
man_pmsg(m, ln, ppos, MANDOCERR_SYNTLINESCOPE);
|
||||
return(0);
|
||||
}
|
||||
|
||||
n = m->last;
|
||||
|
||||
assert(n);
|
||||
assert(NULL == n->child);
|
||||
assert(0 == n->nchild);
|
||||
|
||||
if ( ! man_nmsg(m, n, MANDOCERR_LINESCOPE))
|
||||
return(0);
|
||||
|
||||
man_node_delete(m, n);
|
||||
m->flags &= ~MAN_ELINE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the fact that we're in the next-line for a block. In
|
||||
* this way, embedded roff instructions can "remember" state
|
||||
* when they exit.
|
||||
*/
|
||||
|
||||
if (MAN_BLINE & m->flags)
|
||||
m->flags |= MAN_BPLINE;
|
||||
|
||||
/* Call to handler... */
|
||||
|
||||
assert(man_macros[tok].fp);
|
||||
if ( ! (*man_macros[tok].fp)(m, tok, ln, ppos, &i, buf))
|
||||
goto err;
|
||||
|
||||
out:
|
||||
/*
|
||||
* We weren't in a block-line scope when entering the
|
||||
* above-parsed macro, so return.
|
||||
*/
|
||||
|
||||
if ( ! (MAN_BPLINE & m->flags)) {
|
||||
m->flags &= ~MAN_ILINE;
|
||||
return(1);
|
||||
}
|
||||
m->flags &= ~MAN_BPLINE;
|
||||
|
||||
/*
|
||||
* If we're in a block scope, then allow this macro to slip by
|
||||
* without closing scope around it.
|
||||
*/
|
||||
|
||||
if (MAN_ILINE & m->flags) {
|
||||
m->flags &= ~MAN_ILINE;
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've opened a new next-line element scope, then return
|
||||
* now, as the next line will close out the block scope.
|
||||
*/
|
||||
|
||||
if (MAN_ELINE & m->flags)
|
||||
return(1);
|
||||
|
||||
/* Close out the block scope opened in the prior line. */
|
||||
|
||||
assert(MAN_BLINE & m->flags);
|
||||
m->flags &= ~MAN_BLINE;
|
||||
|
||||
if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
|
||||
return(0);
|
||||
return(man_body_alloc(m, ln, offs, m->last->tok));
|
||||
|
||||
err: /* Error out. */
|
||||
|
||||
m->flags |= MAN_HALT;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_vmsg(struct man *man, enum mandocerr t,
|
||||
int ln, int pos, const char *fmt, ...)
|
||||
{
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
|
||||
va_end(ap);
|
||||
return((*man->msg)(t, man->data, ln, pos, buf));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Unlink a node from its context. If "m" is provided, the last parse
|
||||
* point will also be adjusted accordingly.
|
||||
*/
|
||||
static void
|
||||
man_node_unlink(struct man *m, struct man_node *n)
|
||||
{
|
||||
|
||||
/* Adjust siblings. */
|
||||
|
||||
if (n->prev)
|
||||
n->prev->next = n->next;
|
||||
if (n->next)
|
||||
n->next->prev = n->prev;
|
||||
|
||||
/* Adjust parent. */
|
||||
|
||||
if (n->parent) {
|
||||
n->parent->nchild--;
|
||||
if (n->parent->child == n)
|
||||
n->parent->child = n->prev ? n->prev : n->next;
|
||||
}
|
||||
|
||||
/* Adjust parse point, if applicable. */
|
||||
|
||||
if (m && m->last == n) {
|
||||
/*XXX: this can occur when bailing from validation. */
|
||||
/*assert(NULL == n->next);*/
|
||||
if (n->prev) {
|
||||
m->last = n->prev;
|
||||
m->next = MAN_NEXT_SIBLING;
|
||||
} else {
|
||||
m->last = n->parent;
|
||||
m->next = MAN_NEXT_CHILD;
|
||||
}
|
||||
}
|
||||
|
||||
if (m && m->first == n)
|
||||
m->first = NULL;
|
||||
}
|
119
commands/mdocml/man.h
Normal file
119
commands/mdocml/man.h
Normal file
|
@ -0,0 +1,119 @@
|
|||
/* $Id: man.h,v 1.37 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef MAN_H
|
||||
#define MAN_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
enum mant {
|
||||
MAN_br = 0,
|
||||
MAN_TH,
|
||||
MAN_SH,
|
||||
MAN_SS,
|
||||
MAN_TP,
|
||||
MAN_LP,
|
||||
MAN_PP,
|
||||
MAN_P,
|
||||
MAN_IP,
|
||||
MAN_HP,
|
||||
MAN_SM,
|
||||
MAN_SB,
|
||||
MAN_BI,
|
||||
MAN_IB,
|
||||
MAN_BR,
|
||||
MAN_RB,
|
||||
MAN_R,
|
||||
MAN_B,
|
||||
MAN_I,
|
||||
MAN_IR,
|
||||
MAN_RI,
|
||||
MAN_na,
|
||||
MAN_i,
|
||||
MAN_sp,
|
||||
MAN_nf,
|
||||
MAN_fi,
|
||||
MAN_r,
|
||||
MAN_RE,
|
||||
MAN_RS,
|
||||
MAN_DT,
|
||||
MAN_UC,
|
||||
MAN_PD,
|
||||
MAN_Sp,
|
||||
MAN_Vb,
|
||||
MAN_Ve,
|
||||
MAN_AT,
|
||||
MAN_MAX
|
||||
};
|
||||
|
||||
enum man_type {
|
||||
MAN_TEXT,
|
||||
MAN_ELEM,
|
||||
MAN_ROOT,
|
||||
MAN_BLOCK,
|
||||
MAN_HEAD,
|
||||
MAN_BODY
|
||||
};
|
||||
|
||||
struct man_meta {
|
||||
char *msec;
|
||||
time_t date;
|
||||
char *rawdate;
|
||||
char *vol;
|
||||
char *title;
|
||||
char *source;
|
||||
};
|
||||
|
||||
struct man_node {
|
||||
struct man_node *parent;
|
||||
struct man_node *child;
|
||||
struct man_node *next;
|
||||
struct man_node *prev;
|
||||
int nchild;
|
||||
int line;
|
||||
int pos;
|
||||
enum mant tok;
|
||||
int flags;
|
||||
#define MAN_VALID (1 << 0)
|
||||
#define MAN_ACTED (1 << 1)
|
||||
#define MAN_EOS (1 << 2)
|
||||
enum man_type type;
|
||||
char *string;
|
||||
struct man_node *head;
|
||||
struct man_node *body;
|
||||
};
|
||||
|
||||
#define MAN_IGN_MACRO (1 << 0)
|
||||
#define MAN_IGN_ESCAPE (1 << 2)
|
||||
|
||||
extern const char *const *man_macronames;
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
struct man;
|
||||
|
||||
void man_free(struct man *);
|
||||
struct man *man_alloc(void *, int, mandocmsg);
|
||||
void man_reset(struct man *);
|
||||
int man_parseln(struct man *, int, char *, int);
|
||||
int man_endparse(struct man *);
|
||||
|
||||
const struct man_node *man_node(const struct man *);
|
||||
const struct man_meta *man_meta(const struct man *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!MAN_H*/
|
280
commands/mdocml/man_action.c
Normal file
280
commands/mdocml/man_action.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
/* $Id: man_action.c,v 1.39 2010/05/26 14:03:54 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libman.h"
|
||||
#include "libmandoc.h"
|
||||
|
||||
struct actions {
|
||||
int (*post)(struct man *);
|
||||
};
|
||||
|
||||
static int post_TH(struct man *);
|
||||
static int post_fi(struct man *);
|
||||
static int post_nf(struct man *);
|
||||
static int post_AT(struct man *);
|
||||
static int post_UC(struct man *);
|
||||
|
||||
const struct actions man_actions[MAN_MAX] = {
|
||||
{ NULL }, /* br */
|
||||
{ post_TH }, /* TH */
|
||||
{ NULL }, /* SH */
|
||||
{ NULL }, /* SS */
|
||||
{ NULL }, /* TP */
|
||||
{ NULL }, /* LP */
|
||||
{ NULL }, /* PP */
|
||||
{ NULL }, /* P */
|
||||
{ NULL }, /* IP */
|
||||
{ NULL }, /* HP */
|
||||
{ NULL }, /* SM */
|
||||
{ NULL }, /* SB */
|
||||
{ NULL }, /* BI */
|
||||
{ NULL }, /* IB */
|
||||
{ NULL }, /* BR */
|
||||
{ NULL }, /* RB */
|
||||
{ NULL }, /* R */
|
||||
{ NULL }, /* B */
|
||||
{ NULL }, /* I */
|
||||
{ NULL }, /* IR */
|
||||
{ NULL }, /* RI */
|
||||
{ NULL }, /* na */
|
||||
{ NULL }, /* i */
|
||||
{ NULL }, /* sp */
|
||||
{ post_nf }, /* nf */
|
||||
{ post_fi }, /* fi */
|
||||
{ NULL }, /* r */
|
||||
{ NULL }, /* RE */
|
||||
{ NULL }, /* RS */
|
||||
{ NULL }, /* DT */
|
||||
{ post_UC }, /* UC */
|
||||
{ NULL }, /* PD */
|
||||
{ NULL }, /* Sp */
|
||||
{ post_nf }, /* Vb */
|
||||
{ post_fi }, /* Ve */
|
||||
{ post_AT }, /* AT */
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
man_action_post(struct man *m)
|
||||
{
|
||||
|
||||
if (MAN_ACTED & m->last->flags)
|
||||
return(1);
|
||||
m->last->flags |= MAN_ACTED;
|
||||
|
||||
switch (m->last->type) {
|
||||
case (MAN_TEXT):
|
||||
/* FALLTHROUGH */
|
||||
case (MAN_ROOT):
|
||||
return(1);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (NULL == man_actions[m->last->tok].post)
|
||||
return(1);
|
||||
return((*man_actions[m->last->tok].post)(m));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
post_fi(struct man *m)
|
||||
{
|
||||
|
||||
if ( ! (MAN_LITERAL & m->flags))
|
||||
if ( ! man_nmsg(m, m->last, MANDOCERR_NOSCOPE))
|
||||
return(0);
|
||||
m->flags &= ~MAN_LITERAL;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
post_nf(struct man *m)
|
||||
{
|
||||
|
||||
if (MAN_LITERAL & m->flags)
|
||||
if ( ! man_nmsg(m, m->last, MANDOCERR_SCOPEREP))
|
||||
return(0);
|
||||
m->flags |= MAN_LITERAL;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
post_TH(struct man *m)
|
||||
{
|
||||
struct man_node *n;
|
||||
|
||||
if (m->meta.title)
|
||||
free(m->meta.title);
|
||||
if (m->meta.vol)
|
||||
free(m->meta.vol);
|
||||
if (m->meta.source)
|
||||
free(m->meta.source);
|
||||
if (m->meta.msec)
|
||||
free(m->meta.msec);
|
||||
if (m->meta.rawdate)
|
||||
free(m->meta.rawdate);
|
||||
|
||||
m->meta.title = m->meta.vol = m->meta.rawdate =
|
||||
m->meta.msec = m->meta.source = NULL;
|
||||
m->meta.date = 0;
|
||||
|
||||
/* ->TITLE<- MSEC DATE SOURCE VOL */
|
||||
|
||||
n = m->last->child;
|
||||
assert(n);
|
||||
m->meta.title = mandoc_strdup(n->string);
|
||||
|
||||
/* TITLE ->MSEC<- DATE SOURCE VOL */
|
||||
|
||||
n = n->next;
|
||||
assert(n);
|
||||
m->meta.msec = mandoc_strdup(n->string);
|
||||
|
||||
/* TITLE MSEC ->DATE<- SOURCE VOL */
|
||||
|
||||
/*
|
||||
* Try to parse the date. If this works, stash the epoch (this
|
||||
* is optimal because we can reformat it in the canonical form).
|
||||
* If it doesn't parse, isn't specified at all, or is an empty
|
||||
* string, then use the current date.
|
||||
*/
|
||||
|
||||
n = n->next;
|
||||
if (n && n->string && *n->string) {
|
||||
m->meta.date = mandoc_a2time
|
||||
(MTIME_ISO_8601, n->string);
|
||||
if (0 == m->meta.date) {
|
||||
if ( ! man_nmsg(m, n, MANDOCERR_BADDATE))
|
||||
return(0);
|
||||
m->meta.rawdate = mandoc_strdup(n->string);
|
||||
}
|
||||
} else
|
||||
m->meta.date = time(NULL);
|
||||
|
||||
/* TITLE MSEC DATE ->SOURCE<- VOL */
|
||||
|
||||
if (n && (n = n->next))
|
||||
m->meta.source = mandoc_strdup(n->string);
|
||||
|
||||
/* TITLE MSEC DATE SOURCE ->VOL<- */
|
||||
|
||||
if (n && (n = n->next))
|
||||
m->meta.vol = mandoc_strdup(n->string);
|
||||
|
||||
/*
|
||||
* Remove the `TH' node after we've processed it for our
|
||||
* meta-data.
|
||||
*/
|
||||
man_node_delete(m, m->last);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
post_AT(struct man *m)
|
||||
{
|
||||
static const char * const unix_versions[] = {
|
||||
"7th Edition",
|
||||
"System III",
|
||||
"System V",
|
||||
"System V Release 2",
|
||||
};
|
||||
|
||||
const char *p, *s;
|
||||
struct man_node *n, *nn;
|
||||
|
||||
n = m->last->child;
|
||||
|
||||
if (NULL == n || MAN_TEXT != n->type)
|
||||
p = unix_versions[0];
|
||||
else {
|
||||
s = n->string;
|
||||
if (0 == strcmp(s, "3"))
|
||||
p = unix_versions[0];
|
||||
else if (0 == strcmp(s, "4"))
|
||||
p = unix_versions[1];
|
||||
else if (0 == strcmp(s, "5")) {
|
||||
nn = n->next;
|
||||
if (nn && MAN_TEXT == nn->type && nn->string[0])
|
||||
p = unix_versions[3];
|
||||
else
|
||||
p = unix_versions[2];
|
||||
} else
|
||||
p = unix_versions[0];
|
||||
}
|
||||
|
||||
if (m->meta.source)
|
||||
free(m->meta.source);
|
||||
|
||||
m->meta.source = mandoc_strdup(p);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
post_UC(struct man *m)
|
||||
{
|
||||
static const char * const bsd_versions[] = {
|
||||
"3rd Berkeley Distribution",
|
||||
"4th Berkeley Distribution",
|
||||
"4.2 Berkeley Distribution",
|
||||
"4.3 Berkeley Distribution",
|
||||
"4.4 Berkeley Distribution",
|
||||
};
|
||||
|
||||
const char *p, *s;
|
||||
struct man_node *n;
|
||||
|
||||
n = m->last->child;
|
||||
|
||||
if (NULL == n || MAN_TEXT != n->type)
|
||||
p = bsd_versions[0];
|
||||
else {
|
||||
s = n->string;
|
||||
if (0 == strcmp(s, "3"))
|
||||
p = bsd_versions[0];
|
||||
else if (0 == strcmp(s, "4"))
|
||||
p = bsd_versions[1];
|
||||
else if (0 == strcmp(s, "5"))
|
||||
p = bsd_versions[2];
|
||||
else if (0 == strcmp(s, "6"))
|
||||
p = bsd_versions[3];
|
||||
else if (0 == strcmp(s, "7"))
|
||||
p = bsd_versions[4];
|
||||
else
|
||||
p = bsd_versions[0];
|
||||
}
|
||||
|
||||
if (m->meta.source)
|
||||
free(m->meta.source);
|
||||
|
||||
m->meta.source = mandoc_strdup(p);
|
||||
|
||||
return(1);
|
||||
}
|
104
commands/mdocml/man_argv.c
Normal file
104
commands/mdocml/man_argv.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/* $Id: man_argv.c,v 1.4 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libman.h"
|
||||
|
||||
|
||||
int
|
||||
man_args(struct man *m, int line, int *pos, char *buf, char **v)
|
||||
{
|
||||
|
||||
assert(*pos);
|
||||
assert(' ' != buf[*pos]);
|
||||
|
||||
if (0 == buf[*pos])
|
||||
return(ARGS_EOLN);
|
||||
|
||||
*v = &buf[*pos];
|
||||
|
||||
/*
|
||||
* Process a quoted literal. A quote begins with a double-quote
|
||||
* and ends with a double-quote NOT preceded by a double-quote.
|
||||
* Whitespace is NOT involved in literal termination.
|
||||
*/
|
||||
|
||||
if ('\"' == buf[*pos]) {
|
||||
*v = &buf[++(*pos)];
|
||||
|
||||
for ( ; buf[*pos]; (*pos)++) {
|
||||
if ('\"' != buf[*pos])
|
||||
continue;
|
||||
if ('\"' != buf[*pos + 1])
|
||||
break;
|
||||
(*pos)++;
|
||||
}
|
||||
|
||||
if (0 == buf[*pos]) {
|
||||
if ( ! man_pmsg(m, line, *pos, MANDOCERR_BADQUOTE))
|
||||
return(ARGS_ERROR);
|
||||
return(ARGS_QWORD);
|
||||
}
|
||||
|
||||
buf[(*pos)++] = 0;
|
||||
|
||||
if (0 == buf[*pos])
|
||||
return(ARGS_QWORD);
|
||||
|
||||
while (' ' == buf[*pos])
|
||||
(*pos)++;
|
||||
|
||||
if (0 == buf[*pos])
|
||||
if ( ! man_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
|
||||
return(ARGS_ERROR);
|
||||
|
||||
return(ARGS_QWORD);
|
||||
}
|
||||
|
||||
/*
|
||||
* A non-quoted term progresses until either the end of line or
|
||||
* a non-escaped whitespace.
|
||||
*/
|
||||
|
||||
for ( ; buf[*pos]; (*pos)++)
|
||||
if (' ' == buf[*pos] && '\\' != buf[*pos - 1])
|
||||
break;
|
||||
|
||||
if (0 == buf[*pos])
|
||||
return(ARGS_WORD);
|
||||
|
||||
buf[(*pos)++] = 0;
|
||||
|
||||
while (' ' == buf[*pos])
|
||||
(*pos)++;
|
||||
|
||||
if (0 == buf[*pos])
|
||||
if ( ! man_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
|
||||
return(ARGS_ERROR);
|
||||
|
||||
return(ARGS_WORD);
|
||||
}
|
||||
|
106
commands/mdocml/man_hash.c
Normal file
106
commands/mdocml/man_hash.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
/* $Id: man_hash.c,v 1.22 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libman.h"
|
||||
|
||||
#define HASH_DEPTH 6
|
||||
|
||||
#define HASH_ROW(x) do { \
|
||||
if (isupper((u_char)(x))) \
|
||||
(x) -= 65; \
|
||||
else \
|
||||
(x) -= 97; \
|
||||
(x) *= HASH_DEPTH; \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
/*
|
||||
* Lookup table is indexed first by lower-case first letter (plus one
|
||||
* for the period, which is stored in the last row), then by lower or
|
||||
* uppercase second letter. Buckets correspond to the index of the
|
||||
* macro (the integer value of the enum stored as a char to save a bit
|
||||
* of space).
|
||||
*/
|
||||
static u_char table[26 * HASH_DEPTH];
|
||||
|
||||
/*
|
||||
* XXX - this hash has global scope, so if intended for use as a library
|
||||
* with multiple callers, it will need re-invocation protection.
|
||||
*/
|
||||
void
|
||||
man_hash_init(void)
|
||||
{
|
||||
int i, j, x;
|
||||
|
||||
memset(table, UCHAR_MAX, sizeof(table));
|
||||
|
||||
assert(/* LINTED */
|
||||
MAN_MAX < UCHAR_MAX);
|
||||
|
||||
for (i = 0; i < (int)MAN_MAX; i++) {
|
||||
x = man_macronames[i][0];
|
||||
|
||||
assert(isalpha((u_char)x));
|
||||
|
||||
HASH_ROW(x);
|
||||
|
||||
for (j = 0; j < HASH_DEPTH; j++)
|
||||
if (UCHAR_MAX == table[x + j]) {
|
||||
table[x + j] = (u_char)i;
|
||||
break;
|
||||
}
|
||||
|
||||
assert(j < HASH_DEPTH);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum mant
|
||||
man_hash_find(const char *tmp)
|
||||
{
|
||||
int x, y, i;
|
||||
enum mant tok;
|
||||
|
||||
if ('\0' == (x = tmp[0]))
|
||||
return(MAN_MAX);
|
||||
if ( ! (isalpha((u_char)x)))
|
||||
return(MAN_MAX);
|
||||
|
||||
HASH_ROW(x);
|
||||
|
||||
for (i = 0; i < HASH_DEPTH; i++) {
|
||||
if (UCHAR_MAX == (y = table[x + i]))
|
||||
return(MAN_MAX);
|
||||
|
||||
tok = (enum mant)y;
|
||||
if (0 == strcmp(tmp, man_macronames[tok]))
|
||||
return(tok);
|
||||
}
|
||||
|
||||
return(MAN_MAX);
|
||||
}
|
751
commands/mdocml/man_html.c
Normal file
751
commands/mdocml/man_html.c
Normal file
|
@ -0,0 +1,751 @@
|
|||
/* $Id: man_html.c,v 1.37 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "out.h"
|
||||
#include "html.h"
|
||||
#include "man.h"
|
||||
#include "main.h"
|
||||
|
||||
/* TODO: preserve ident widths. */
|
||||
/* FIXME: have PD set the default vspace width. */
|
||||
|
||||
#define INDENT 5
|
||||
#define HALFINDENT 3
|
||||
|
||||
#define MAN_ARGS const struct man_meta *m, \
|
||||
const struct man_node *n, \
|
||||
struct html *h
|
||||
|
||||
struct htmlman {
|
||||
int (*pre)(MAN_ARGS);
|
||||
int (*post)(MAN_ARGS);
|
||||
};
|
||||
|
||||
static void print_man(MAN_ARGS);
|
||||
static void print_man_head(MAN_ARGS);
|
||||
static void print_man_nodelist(MAN_ARGS);
|
||||
static void print_man_node(MAN_ARGS);
|
||||
|
||||
static int a2width(const struct man_node *,
|
||||
struct roffsu *);
|
||||
|
||||
static int man_alt_pre(MAN_ARGS);
|
||||
static int man_br_pre(MAN_ARGS);
|
||||
static int man_ign_pre(MAN_ARGS);
|
||||
static void man_root_post(MAN_ARGS);
|
||||
static int man_root_pre(MAN_ARGS);
|
||||
static int man_B_pre(MAN_ARGS);
|
||||
static int man_HP_pre(MAN_ARGS);
|
||||
static int man_I_pre(MAN_ARGS);
|
||||
static int man_IP_pre(MAN_ARGS);
|
||||
static int man_PP_pre(MAN_ARGS);
|
||||
static int man_RS_pre(MAN_ARGS);
|
||||
static int man_SB_pre(MAN_ARGS);
|
||||
static int man_SH_pre(MAN_ARGS);
|
||||
static int man_SM_pre(MAN_ARGS);
|
||||
static int man_SS_pre(MAN_ARGS);
|
||||
|
||||
static const struct htmlman mans[MAN_MAX] = {
|
||||
{ man_br_pre, NULL }, /* br */
|
||||
{ NULL, NULL }, /* TH */
|
||||
{ man_SH_pre, NULL }, /* SH */
|
||||
{ man_SS_pre, NULL }, /* SS */
|
||||
{ man_IP_pre, NULL }, /* TP */
|
||||
{ man_PP_pre, NULL }, /* LP */
|
||||
{ man_PP_pre, NULL }, /* PP */
|
||||
{ man_PP_pre, NULL }, /* P */
|
||||
{ man_IP_pre, NULL }, /* IP */
|
||||
{ man_HP_pre, NULL }, /* HP */
|
||||
{ man_SM_pre, NULL }, /* SM */
|
||||
{ man_SB_pre, NULL }, /* SB */
|
||||
{ man_alt_pre, NULL }, /* BI */
|
||||
{ man_alt_pre, NULL }, /* IB */
|
||||
{ man_alt_pre, NULL }, /* BR */
|
||||
{ man_alt_pre, NULL }, /* RB */
|
||||
{ NULL, NULL }, /* R */
|
||||
{ man_B_pre, NULL }, /* B */
|
||||
{ man_I_pre, NULL }, /* I */
|
||||
{ man_alt_pre, NULL }, /* IR */
|
||||
{ man_alt_pre, NULL }, /* RI */
|
||||
{ NULL, NULL }, /* na */
|
||||
{ NULL, NULL }, /* i */
|
||||
{ man_br_pre, NULL }, /* sp */
|
||||
{ NULL, NULL }, /* nf */
|
||||
{ NULL, NULL }, /* fi */
|
||||
{ NULL, NULL }, /* r */
|
||||
{ NULL, NULL }, /* RE */
|
||||
{ man_RS_pre, NULL }, /* RS */
|
||||
{ man_ign_pre, NULL }, /* DT */
|
||||
{ man_ign_pre, NULL }, /* UC */
|
||||
{ man_ign_pre, NULL }, /* PD */
|
||||
{ man_br_pre, NULL }, /* Sp */
|
||||
{ man_ign_pre, NULL }, /* Vb */
|
||||
{ NULL, NULL }, /* Ve */
|
||||
{ man_ign_pre, NULL }, /* AT */
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
html_man(void *arg, const struct man *m)
|
||||
{
|
||||
struct html *h;
|
||||
struct tag *t;
|
||||
|
||||
h = (struct html *)arg;
|
||||
|
||||
print_gen_decls(h);
|
||||
|
||||
t = print_otag(h, TAG_HTML, 0, NULL);
|
||||
print_man(man_meta(m), man_node(m), h);
|
||||
print_tagq(h, t);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_man(MAN_ARGS)
|
||||
{
|
||||
struct tag *t;
|
||||
struct htmlpair tag;
|
||||
|
||||
t = print_otag(h, TAG_HEAD, 0, NULL);
|
||||
|
||||
print_man_head(m, n, h);
|
||||
print_tagq(h, t);
|
||||
t = print_otag(h, TAG_BODY, 0, NULL);
|
||||
|
||||
tag.key = ATTR_CLASS;
|
||||
tag.val = "body";
|
||||
print_otag(h, TAG_DIV, 1, &tag);
|
||||
|
||||
print_man_nodelist(m, n, h);
|
||||
|
||||
print_tagq(h, t);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
print_man_head(MAN_ARGS)
|
||||
{
|
||||
|
||||
print_gen_head(h);
|
||||
bufinit(h);
|
||||
buffmt(h, "%s(%s)", m->title, m->msec);
|
||||
|
||||
print_otag(h, TAG_TITLE, 0, NULL);
|
||||
print_text(h, h->buf);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_man_nodelist(MAN_ARGS)
|
||||
{
|
||||
|
||||
print_man_node(m, n, h);
|
||||
if (n->next)
|
||||
print_man_nodelist(m, n->next, h);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_man_node(MAN_ARGS)
|
||||
{
|
||||
int child;
|
||||
struct tag *t;
|
||||
|
||||
child = 1;
|
||||
t = h->tags.head;
|
||||
|
||||
bufinit(h);
|
||||
|
||||
/*
|
||||
* FIXME: embedded elements within next-line scopes (e.g., `br'
|
||||
* within an empty `B') will cause formatting to be forgotten
|
||||
* due to scope closing out.
|
||||
*/
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_ROOT):
|
||||
child = man_root_pre(m, n, h);
|
||||
break;
|
||||
case (MAN_TEXT):
|
||||
print_text(h, n->string);
|
||||
return;
|
||||
default:
|
||||
/*
|
||||
* Close out scope of font prior to opening a macro
|
||||
* scope. Assert that the metafont is on the top of the
|
||||
* stack (it's never nested).
|
||||
*/
|
||||
if (h->metaf) {
|
||||
assert(h->metaf == t);
|
||||
print_tagq(h, h->metaf);
|
||||
assert(NULL == h->metaf);
|
||||
t = h->tags.head;
|
||||
}
|
||||
if (mans[n->tok].pre)
|
||||
child = (*mans[n->tok].pre)(m, n, h);
|
||||
break;
|
||||
}
|
||||
|
||||
if (child && n->child)
|
||||
print_man_nodelist(m, n->child, h);
|
||||
|
||||
/* This will automatically close out any font scope. */
|
||||
print_stagq(h, t);
|
||||
|
||||
bufinit(h);
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_ROOT):
|
||||
man_root_post(m, n, h);
|
||||
break;
|
||||
case (MAN_TEXT):
|
||||
break;
|
||||
default:
|
||||
if (mans[n->tok].post)
|
||||
(*mans[n->tok].post)(m, n, h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
a2width(const struct man_node *n, struct roffsu *su)
|
||||
{
|
||||
|
||||
if (MAN_TEXT != n->type)
|
||||
return(0);
|
||||
if (a2roffsu(n->string, su, SCALE_BU))
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_root_pre(MAN_ARGS)
|
||||
{
|
||||
struct htmlpair tag[3];
|
||||
struct tag *t, *tt;
|
||||
char b[BUFSIZ], title[BUFSIZ];
|
||||
|
||||
b[0] = 0;
|
||||
if (m->vol)
|
||||
(void)strlcat(b, m->vol, BUFSIZ);
|
||||
|
||||
snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
|
||||
|
||||
PAIR_CLASS_INIT(&tag[0], "header");
|
||||
bufcat_style(h, "width", "100%");
|
||||
PAIR_STYLE_INIT(&tag[1], h);
|
||||
PAIR_SUMMARY_INIT(&tag[2], "header");
|
||||
|
||||
t = print_otag(h, TAG_TABLE, 3, tag);
|
||||
tt = print_otag(h, TAG_TR, 0, NULL);
|
||||
|
||||
bufinit(h);
|
||||
bufcat_style(h, "width", "10%");
|
||||
PAIR_STYLE_INIT(&tag[0], h);
|
||||
print_otag(h, TAG_TD, 1, tag);
|
||||
print_text(h, title);
|
||||
print_stagq(h, tt);
|
||||
|
||||
bufinit(h);
|
||||
bufcat_style(h, "width", "80%");
|
||||
bufcat_style(h, "white-space", "nowrap");
|
||||
bufcat_style(h, "text-align", "center");
|
||||
PAIR_STYLE_INIT(&tag[0], h);
|
||||
print_otag(h, TAG_TD, 1, tag);
|
||||
print_text(h, b);
|
||||
print_stagq(h, tt);
|
||||
|
||||
bufinit(h);
|
||||
bufcat_style(h, "width", "10%");
|
||||
bufcat_style(h, "text-align", "right");
|
||||
PAIR_STYLE_INIT(&tag[0], h);
|
||||
print_otag(h, TAG_TD, 1, tag);
|
||||
print_text(h, title);
|
||||
print_tagq(h, t);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
man_root_post(MAN_ARGS)
|
||||
{
|
||||
struct htmlpair tag[3];
|
||||
struct tag *t, *tt;
|
||||
char b[DATESIZ];
|
||||
|
||||
if (m->rawdate)
|
||||
strlcpy(b, m->rawdate, DATESIZ);
|
||||
else
|
||||
time2a(m->date, b, DATESIZ);
|
||||
|
||||
PAIR_CLASS_INIT(&tag[0], "footer");
|
||||
bufcat_style(h, "width", "100%");
|
||||
PAIR_STYLE_INIT(&tag[1], h);
|
||||
PAIR_SUMMARY_INIT(&tag[2], "footer");
|
||||
|
||||
t = print_otag(h, TAG_TABLE, 3, tag);
|
||||
tt = print_otag(h, TAG_TR, 0, NULL);
|
||||
|
||||
bufinit(h);
|
||||
bufcat_style(h, "width", "50%");
|
||||
PAIR_STYLE_INIT(&tag[0], h);
|
||||
print_otag(h, TAG_TD, 1, tag);
|
||||
print_text(h, b);
|
||||
print_stagq(h, tt);
|
||||
|
||||
bufinit(h);
|
||||
bufcat_style(h, "width", "50%");
|
||||
bufcat_style(h, "text-align", "right");
|
||||
PAIR_STYLE_INIT(&tag[0], h);
|
||||
print_otag(h, TAG_TD, 1, tag);
|
||||
if (m->source)
|
||||
print_text(h, m->source);
|
||||
print_tagq(h, t);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_br_pre(MAN_ARGS)
|
||||
{
|
||||
struct roffsu su;
|
||||
struct htmlpair tag;
|
||||
|
||||
SCALE_VS_INIT(&su, 1);
|
||||
|
||||
switch (n->tok) {
|
||||
case (MAN_Sp):
|
||||
SCALE_VS_INIT(&su, 0.5);
|
||||
break;
|
||||
case (MAN_sp):
|
||||
if (n->child)
|
||||
a2roffsu(n->child->string, &su, SCALE_VS);
|
||||
break;
|
||||
default:
|
||||
su.scale = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
bufcat_su(h, "height", &su);
|
||||
PAIR_STYLE_INIT(&tag, h);
|
||||
print_otag(h, TAG_DIV, 1, &tag);
|
||||
|
||||
/* So the div isn't empty: */
|
||||
print_text(h, "\\~");
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_SH_pre(MAN_ARGS)
|
||||
{
|
||||
struct htmlpair tag[2];
|
||||
struct roffsu su;
|
||||
|
||||
if (MAN_BODY == n->type) {
|
||||
SCALE_HS_INIT(&su, INDENT);
|
||||
bufcat_su(h, "margin-left", &su);
|
||||
PAIR_CLASS_INIT(&tag[0], "sec-body");
|
||||
PAIR_STYLE_INIT(&tag[1], h);
|
||||
print_otag(h, TAG_DIV, 2, tag);
|
||||
return(1);
|
||||
} else if (MAN_BLOCK == n->type) {
|
||||
PAIR_CLASS_INIT(&tag[0], "sec-block");
|
||||
if (n->prev && MAN_SH == n->prev->tok)
|
||||
if (NULL == n->prev->body->child) {
|
||||
print_otag(h, TAG_DIV, 1, tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
SCALE_VS_INIT(&su, 1);
|
||||
bufcat_su(h, "margin-top", &su);
|
||||
if (NULL == n->next)
|
||||
bufcat_su(h, "margin-bottom", &su);
|
||||
PAIR_STYLE_INIT(&tag[1], h);
|
||||
print_otag(h, TAG_DIV, 2, tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
PAIR_CLASS_INIT(&tag[0], "sec-head");
|
||||
print_otag(h, TAG_DIV, 1, tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_alt_pre(MAN_ARGS)
|
||||
{
|
||||
const struct man_node *nn;
|
||||
struct tag *t;
|
||||
int i;
|
||||
enum htmlfont fp;
|
||||
|
||||
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
|
||||
switch (n->tok) {
|
||||
case (MAN_BI):
|
||||
fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD;
|
||||
break;
|
||||
case (MAN_IB):
|
||||
fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC;
|
||||
break;
|
||||
case (MAN_RI):
|
||||
fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE;
|
||||
break;
|
||||
case (MAN_IR):
|
||||
fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC;
|
||||
break;
|
||||
case (MAN_BR):
|
||||
fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD;
|
||||
break;
|
||||
case (MAN_RB):
|
||||
fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
if (i)
|
||||
h->flags |= HTML_NOSPACE;
|
||||
|
||||
/*
|
||||
* Open and close the scope with each argument, so that
|
||||
* internal \f escapes, which are common, are also
|
||||
* closed out with the scope.
|
||||
*/
|
||||
t = print_ofont(h, fp);
|
||||
print_man_node(m, nn, h);
|
||||
print_tagq(h, t);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_SB_pre(MAN_ARGS)
|
||||
{
|
||||
struct htmlpair tag;
|
||||
|
||||
/* FIXME: print_ofont(). */
|
||||
PAIR_CLASS_INIT(&tag, "small bold");
|
||||
print_otag(h, TAG_SPAN, 1, &tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_SM_pre(MAN_ARGS)
|
||||
{
|
||||
struct htmlpair tag;
|
||||
|
||||
PAIR_CLASS_INIT(&tag, "small");
|
||||
print_otag(h, TAG_SPAN, 1, &tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_SS_pre(MAN_ARGS)
|
||||
{
|
||||
struct htmlpair tag[3];
|
||||
struct roffsu su;
|
||||
|
||||
SCALE_VS_INIT(&su, 1);
|
||||
|
||||
if (MAN_BODY == n->type) {
|
||||
PAIR_CLASS_INIT(&tag[0], "ssec-body");
|
||||
if (n->parent->next && n->child) {
|
||||
bufcat_su(h, "margin-bottom", &su);
|
||||
PAIR_STYLE_INIT(&tag[1], h);
|
||||
print_otag(h, TAG_DIV, 2, tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
print_otag(h, TAG_DIV, 1, tag);
|
||||
return(1);
|
||||
} else if (MAN_BLOCK == n->type) {
|
||||
PAIR_CLASS_INIT(&tag[0], "ssec-block");
|
||||
if (n->prev && MAN_SS == n->prev->tok)
|
||||
if (n->prev->body->child) {
|
||||
bufcat_su(h, "margin-top", &su);
|
||||
PAIR_STYLE_INIT(&tag[1], h);
|
||||
print_otag(h, TAG_DIV, 2, tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
print_otag(h, TAG_DIV, 1, tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
SCALE_HS_INIT(&su, INDENT - HALFINDENT);
|
||||
bufcat_su(h, "margin-left", &su);
|
||||
PAIR_CLASS_INIT(&tag[0], "ssec-head");
|
||||
PAIR_STYLE_INIT(&tag[1], h);
|
||||
print_otag(h, TAG_DIV, 2, tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_PP_pre(MAN_ARGS)
|
||||
{
|
||||
struct htmlpair tag;
|
||||
struct roffsu su;
|
||||
int i;
|
||||
|
||||
if (MAN_BLOCK != n->type)
|
||||
return(1);
|
||||
|
||||
i = 0;
|
||||
|
||||
if (MAN_ROOT == n->parent->type) {
|
||||
SCALE_HS_INIT(&su, INDENT);
|
||||
bufcat_su(h, "margin-left", &su);
|
||||
i = 1;
|
||||
}
|
||||
if (n->prev) {
|
||||
SCALE_VS_INIT(&su, 1);
|
||||
bufcat_su(h, "margin-top", &su);
|
||||
i = 1;
|
||||
}
|
||||
|
||||
PAIR_STYLE_INIT(&tag, h);
|
||||
print_otag(h, TAG_DIV, i, &tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_IP_pre(MAN_ARGS)
|
||||
{
|
||||
struct roffsu su;
|
||||
struct htmlpair tag;
|
||||
const struct man_node *nn;
|
||||
int width;
|
||||
|
||||
/*
|
||||
* This scattering of 1-BU margins and pads is to make sure that
|
||||
* when text overruns its box, the subsequent text isn't flush
|
||||
* up against it. However, the rest of the right-hand box must
|
||||
* also be adjusted in consideration of this 1-BU space.
|
||||
*/
|
||||
|
||||
if (MAN_BODY == n->type) {
|
||||
SCALE_HS_INIT(&su, INDENT);
|
||||
bufcat_su(h, "margin-left", &su);
|
||||
PAIR_STYLE_INIT(&tag, h);
|
||||
print_otag(h, TAG_DIV, 1, &tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
nn = MAN_BLOCK == n->type ?
|
||||
n->head->child : n->parent->head->child;
|
||||
|
||||
SCALE_HS_INIT(&su, INDENT);
|
||||
width = 0;
|
||||
|
||||
/* Width is the last token. */
|
||||
|
||||
if (MAN_IP == n->tok && NULL != nn)
|
||||
if (NULL != (nn = nn->next)) {
|
||||
for ( ; nn->next; nn = nn->next)
|
||||
/* Do nothing. */ ;
|
||||
width = a2width(nn, &su);
|
||||
}
|
||||
|
||||
/* Width is the first token. */
|
||||
|
||||
if (MAN_TP == n->tok && NULL != nn) {
|
||||
/* Skip past non-text children. */
|
||||
while (nn && MAN_TEXT != nn->type)
|
||||
nn = nn->next;
|
||||
if (nn)
|
||||
width = a2width(nn, &su);
|
||||
}
|
||||
|
||||
if (MAN_BLOCK == n->type) {
|
||||
bufcat_su(h, "margin-left", &su);
|
||||
SCALE_VS_INIT(&su, 1);
|
||||
bufcat_su(h, "margin-top", &su);
|
||||
bufcat_style(h, "clear", "both");
|
||||
PAIR_STYLE_INIT(&tag, h);
|
||||
print_otag(h, TAG_DIV, 1, &tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
bufcat_su(h, "min-width", &su);
|
||||
SCALE_INVERT(&su);
|
||||
bufcat_su(h, "margin-left", &su);
|
||||
SCALE_HS_INIT(&su, 1);
|
||||
bufcat_su(h, "margin-right", &su);
|
||||
bufcat_style(h, "clear", "left");
|
||||
|
||||
if (n->next && n->next->child)
|
||||
bufcat_style(h, "float", "left");
|
||||
|
||||
PAIR_STYLE_INIT(&tag, h);
|
||||
print_otag(h, TAG_DIV, 1, &tag);
|
||||
|
||||
/*
|
||||
* Without a length string, we can print all of our children.
|
||||
*/
|
||||
|
||||
if ( ! width)
|
||||
return(1);
|
||||
|
||||
/*
|
||||
* When a length has been specified, we need to carefully print
|
||||
* our child context: IP gets all children printed but the last
|
||||
* (the width), while TP gets all children printed but the first
|
||||
* (the width).
|
||||
*/
|
||||
|
||||
if (MAN_IP == n->tok)
|
||||
for (nn = n->child; nn->next; nn = nn->next)
|
||||
print_man_node(m, nn, h);
|
||||
if (MAN_TP == n->tok)
|
||||
for (nn = n->child->next; nn; nn = nn->next)
|
||||
print_man_node(m, nn, h);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_HP_pre(MAN_ARGS)
|
||||
{
|
||||
const struct man_node *nn;
|
||||
struct htmlpair tag;
|
||||
struct roffsu su;
|
||||
|
||||
if (MAN_HEAD == n->type)
|
||||
return(0);
|
||||
|
||||
nn = MAN_BLOCK == n->type ?
|
||||
n->head->child : n->parent->head->child;
|
||||
|
||||
SCALE_HS_INIT(&su, INDENT);
|
||||
|
||||
if (NULL != nn)
|
||||
(void)a2width(nn, &su);
|
||||
|
||||
if (MAN_BLOCK == n->type) {
|
||||
bufcat_su(h, "margin-left", &su);
|
||||
SCALE_VS_INIT(&su, 1);
|
||||
bufcat_su(h, "margin-top", &su);
|
||||
bufcat_style(h, "clear", "both");
|
||||
PAIR_STYLE_INIT(&tag, h);
|
||||
print_otag(h, TAG_DIV, 1, &tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
bufcat_su(h, "margin-left", &su);
|
||||
SCALE_INVERT(&su);
|
||||
bufcat_su(h, "text-indent", &su);
|
||||
|
||||
PAIR_STYLE_INIT(&tag, h);
|
||||
print_otag(h, TAG_DIV, 1, &tag);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_B_pre(MAN_ARGS)
|
||||
{
|
||||
|
||||
print_ofont(h, HTMLFONT_BOLD);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_I_pre(MAN_ARGS)
|
||||
{
|
||||
|
||||
print_ofont(h, HTMLFONT_ITALIC);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_ign_pre(MAN_ARGS)
|
||||
{
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
man_RS_pre(MAN_ARGS)
|
||||
{
|
||||
struct htmlpair tag;
|
||||
struct roffsu su;
|
||||
|
||||
if (MAN_HEAD == n->type)
|
||||
return(0);
|
||||
else if (MAN_BODY == n->type)
|
||||
return(1);
|
||||
|
||||
SCALE_HS_INIT(&su, INDENT);
|
||||
bufcat_su(h, "margin-left", &su);
|
||||
|
||||
if (n->head->child) {
|
||||
SCALE_VS_INIT(&su, 1);
|
||||
a2width(n->head->child, &su);
|
||||
bufcat_su(h, "margin-top", &su);
|
||||
}
|
||||
|
||||
PAIR_STYLE_INIT(&tag, h);
|
||||
print_otag(h, TAG_DIV, 1, &tag);
|
||||
return(1);
|
||||
}
|
485
commands/mdocml/man_macro.c
Normal file
485
commands/mdocml/man_macro.c
Normal file
|
@ -0,0 +1,485 @@
|
|||
/* $Id: man_macro.c,v 1.47 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libman.h"
|
||||
|
||||
enum rew {
|
||||
REW_REWIND,
|
||||
REW_NOHALT,
|
||||
REW_HALT
|
||||
};
|
||||
|
||||
static int blk_close(MACRO_PROT_ARGS);
|
||||
static int blk_exp(MACRO_PROT_ARGS);
|
||||
static int blk_imp(MACRO_PROT_ARGS);
|
||||
static int in_line_eoln(MACRO_PROT_ARGS);
|
||||
|
||||
static int rew_scope(enum man_type,
|
||||
struct man *, enum mant);
|
||||
static enum rew rew_dohalt(enum mant, enum man_type,
|
||||
const struct man_node *);
|
||||
static enum rew rew_block(enum mant, enum man_type,
|
||||
const struct man_node *);
|
||||
static int rew_warn(struct man *,
|
||||
struct man_node *, enum mandocerr);
|
||||
|
||||
const struct man_macro __man_macros[MAN_MAX] = {
|
||||
{ in_line_eoln, MAN_NSCOPED }, /* br */
|
||||
{ in_line_eoln, 0 }, /* TH */
|
||||
{ blk_imp, MAN_SCOPED }, /* SH */
|
||||
{ blk_imp, MAN_SCOPED }, /* SS */
|
||||
{ blk_imp, MAN_SCOPED | MAN_FSCOPED }, /* TP */
|
||||
{ blk_imp, 0 }, /* LP */
|
||||
{ blk_imp, 0 }, /* PP */
|
||||
{ blk_imp, 0 }, /* P */
|
||||
{ blk_imp, 0 }, /* IP */
|
||||
{ blk_imp, 0 }, /* HP */
|
||||
{ in_line_eoln, MAN_SCOPED }, /* SM */
|
||||
{ in_line_eoln, MAN_SCOPED }, /* SB */
|
||||
{ in_line_eoln, 0 }, /* BI */
|
||||
{ in_line_eoln, 0 }, /* IB */
|
||||
{ in_line_eoln, 0 }, /* BR */
|
||||
{ in_line_eoln, 0 }, /* RB */
|
||||
{ in_line_eoln, MAN_SCOPED }, /* R */
|
||||
{ in_line_eoln, MAN_SCOPED }, /* B */
|
||||
{ in_line_eoln, MAN_SCOPED }, /* I */
|
||||
{ in_line_eoln, 0 }, /* IR */
|
||||
{ in_line_eoln, 0 }, /* RI */
|
||||
{ in_line_eoln, MAN_NSCOPED }, /* na */
|
||||
{ in_line_eoln, 0 }, /* i */
|
||||
{ in_line_eoln, MAN_NSCOPED }, /* sp */
|
||||
{ in_line_eoln, 0 }, /* nf */
|
||||
{ in_line_eoln, 0 }, /* fi */
|
||||
{ in_line_eoln, 0 }, /* r */
|
||||
{ blk_close, 0 }, /* RE */
|
||||
{ blk_exp, MAN_EXPLICIT }, /* RS */
|
||||
{ in_line_eoln, 0 }, /* DT */
|
||||
{ in_line_eoln, 0 }, /* UC */
|
||||
{ in_line_eoln, 0 }, /* PD */
|
||||
{ in_line_eoln, MAN_NSCOPED }, /* Sp */
|
||||
{ in_line_eoln, 0 }, /* Vb */
|
||||
{ in_line_eoln, 0 }, /* Ve */
|
||||
{ in_line_eoln, 0 }, /* AT */
|
||||
};
|
||||
|
||||
const struct man_macro * const man_macros = __man_macros;
|
||||
|
||||
|
||||
/*
|
||||
* Warn when "n" is an explicit non-roff macro.
|
||||
*/
|
||||
static int
|
||||
rew_warn(struct man *m, struct man_node *n, enum mandocerr er)
|
||||
{
|
||||
|
||||
if (er == MANDOCERR_MAX || MAN_BLOCK != n->type)
|
||||
return(1);
|
||||
if (MAN_VALID & n->flags)
|
||||
return(1);
|
||||
if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags))
|
||||
return(1);
|
||||
return(man_nmsg(m, n, er));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Rewind scope. If a code "er" != MANDOCERR_MAX has been provided, it
|
||||
* will be used if an explicit block scope is being closed out.
|
||||
*/
|
||||
int
|
||||
man_unscope(struct man *m, const struct man_node *n,
|
||||
enum mandocerr er)
|
||||
{
|
||||
|
||||
assert(n);
|
||||
|
||||
/* LINTED */
|
||||
while (m->last != n) {
|
||||
if ( ! rew_warn(m, m->last, er))
|
||||
return(0);
|
||||
if ( ! man_valid_post(m))
|
||||
return(0);
|
||||
if ( ! man_action_post(m))
|
||||
return(0);
|
||||
m->last = m->last->parent;
|
||||
assert(m->last);
|
||||
}
|
||||
|
||||
if ( ! rew_warn(m, m->last, er))
|
||||
return(0);
|
||||
if ( ! man_valid_post(m))
|
||||
return(0);
|
||||
if ( ! man_action_post(m))
|
||||
return(0);
|
||||
|
||||
m->next = MAN_ROOT == m->last->type ?
|
||||
MAN_NEXT_CHILD : MAN_NEXT_SIBLING;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static enum rew
|
||||
rew_block(enum mant ntok, enum man_type type, const struct man_node *n)
|
||||
{
|
||||
|
||||
if (MAN_BLOCK == type && ntok == n->parent->tok &&
|
||||
MAN_BODY == n->parent->type)
|
||||
return(REW_REWIND);
|
||||
return(ntok == n->tok ? REW_HALT : REW_NOHALT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* There are three scope levels: scoped to the root (all), scoped to the
|
||||
* section (all less sections), and scoped to subsections (all less
|
||||
* sections and subsections).
|
||||
*/
|
||||
static enum rew
|
||||
rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
|
||||
{
|
||||
enum rew c;
|
||||
|
||||
/* We cannot progress beyond the root ever. */
|
||||
if (MAN_ROOT == n->type)
|
||||
return(REW_HALT);
|
||||
|
||||
assert(n->parent);
|
||||
|
||||
/* Normal nodes shouldn't go to the level of the root. */
|
||||
if (MAN_ROOT == n->parent->type)
|
||||
return(REW_REWIND);
|
||||
|
||||
/* Already-validated nodes should be closed out. */
|
||||
if (MAN_VALID & n->flags)
|
||||
return(REW_NOHALT);
|
||||
|
||||
/* First: rewind to ourselves. */
|
||||
if (type == n->type && tok == n->tok)
|
||||
return(REW_REWIND);
|
||||
|
||||
/*
|
||||
* Next follow the implicit scope-smashings as defined by man.7:
|
||||
* section, sub-section, etc.
|
||||
*/
|
||||
|
||||
switch (tok) {
|
||||
case (MAN_SH):
|
||||
break;
|
||||
case (MAN_SS):
|
||||
/* Rewind to a section, if a block. */
|
||||
if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
|
||||
return(c);
|
||||
break;
|
||||
case (MAN_RS):
|
||||
/* Rewind to a subsection, if a block. */
|
||||
if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
|
||||
return(c);
|
||||
/* Rewind to a section, if a block. */
|
||||
if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
|
||||
return(c);
|
||||
break;
|
||||
default:
|
||||
/* Rewind to an offsetter, if a block. */
|
||||
if (REW_NOHALT != (c = rew_block(MAN_RS, type, n)))
|
||||
return(c);
|
||||
/* Rewind to a subsection, if a block. */
|
||||
if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
|
||||
return(c);
|
||||
/* Rewind to a section, if a block. */
|
||||
if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
|
||||
return(c);
|
||||
break;
|
||||
}
|
||||
|
||||
return(REW_NOHALT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Rewinding entails ascending the parse tree until a coherent point,
|
||||
* for example, the `SH' macro will close out any intervening `SS'
|
||||
* scopes. When a scope is closed, it must be validated and actioned.
|
||||
*/
|
||||
static int
|
||||
rew_scope(enum man_type type, struct man *m, enum mant tok)
|
||||
{
|
||||
struct man_node *n;
|
||||
enum rew c;
|
||||
|
||||
/* LINTED */
|
||||
for (n = m->last; n; n = n->parent) {
|
||||
/*
|
||||
* Whether we should stop immediately (REW_HALT), stop
|
||||
* and rewind until this point (REW_REWIND), or keep
|
||||
* rewinding (REW_NOHALT).
|
||||
*/
|
||||
c = rew_dohalt(tok, type, n);
|
||||
if (REW_HALT == c)
|
||||
return(1);
|
||||
if (REW_REWIND == c)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rewind until the current point. Warn if we're a roff
|
||||
* instruction that's mowing over explicit scopes.
|
||||
*/
|
||||
assert(n);
|
||||
|
||||
return(man_unscope(m, n, MANDOCERR_MAX));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Close out a generic explicit macro.
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
blk_close(MACRO_PROT_ARGS)
|
||||
{
|
||||
enum mant ntok;
|
||||
const struct man_node *nn;
|
||||
|
||||
switch (tok) {
|
||||
case (MAN_RE):
|
||||
ntok = MAN_RS;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
for (nn = m->last->parent; nn; nn = nn->parent)
|
||||
if (ntok == nn->tok)
|
||||
break;
|
||||
|
||||
if (NULL == nn)
|
||||
if ( ! man_pmsg(m, line, ppos, MANDOCERR_NOSCOPE))
|
||||
return(0);
|
||||
|
||||
if ( ! rew_scope(MAN_BODY, m, ntok))
|
||||
return(0);
|
||||
if ( ! rew_scope(MAN_BLOCK, m, ntok))
|
||||
return(0);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
blk_exp(MACRO_PROT_ARGS)
|
||||
{
|
||||
int w, la;
|
||||
char *p;
|
||||
|
||||
/*
|
||||
* Close out prior scopes. "Regular" explicit macros cannot be
|
||||
* nested, but we allow roff macros to be placed just about
|
||||
* anywhere.
|
||||
*/
|
||||
|
||||
if ( ! rew_scope(MAN_BODY, m, tok))
|
||||
return(0);
|
||||
if ( ! rew_scope(MAN_BLOCK, m, tok))
|
||||
return(0);
|
||||
|
||||
if ( ! man_block_alloc(m, line, ppos, tok))
|
||||
return(0);
|
||||
if ( ! man_head_alloc(m, line, ppos, tok))
|
||||
return(0);
|
||||
|
||||
for (;;) {
|
||||
la = *pos;
|
||||
w = man_args(m, line, pos, buf, &p);
|
||||
|
||||
if (-1 == w)
|
||||
return(0);
|
||||
if (0 == w)
|
||||
break;
|
||||
|
||||
if ( ! man_word_alloc(m, line, la, p))
|
||||
return(0);
|
||||
}
|
||||
|
||||
assert(m);
|
||||
assert(tok != MAN_MAX);
|
||||
|
||||
if ( ! rew_scope(MAN_HEAD, m, tok))
|
||||
return(0);
|
||||
return(man_body_alloc(m, line, ppos, tok));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Parse an implicit-block macro. These contain a MAN_HEAD and a
|
||||
* MAN_BODY contained within a MAN_BLOCK. Rules for closing out other
|
||||
* scopes, such as `SH' closing out an `SS', are defined in the rew
|
||||
* routines.
|
||||
*/
|
||||
static int
|
||||
blk_imp(MACRO_PROT_ARGS)
|
||||
{
|
||||
int w, la;
|
||||
char *p;
|
||||
struct man_node *n;
|
||||
|
||||
/* Close out prior scopes. */
|
||||
|
||||
if ( ! rew_scope(MAN_BODY, m, tok))
|
||||
return(0);
|
||||
if ( ! rew_scope(MAN_BLOCK, m, tok))
|
||||
return(0);
|
||||
|
||||
/* Allocate new block & head scope. */
|
||||
|
||||
if ( ! man_block_alloc(m, line, ppos, tok))
|
||||
return(0);
|
||||
if ( ! man_head_alloc(m, line, ppos, tok))
|
||||
return(0);
|
||||
|
||||
n = m->last;
|
||||
|
||||
/* Add line arguments. */
|
||||
|
||||
for (;;) {
|
||||
la = *pos;
|
||||
w = man_args(m, line, pos, buf, &p);
|
||||
|
||||
if (-1 == w)
|
||||
return(0);
|
||||
if (0 == w)
|
||||
break;
|
||||
|
||||
if ( ! man_word_alloc(m, line, la, p))
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Close out head and open body (unless MAN_SCOPE). */
|
||||
|
||||
if (MAN_SCOPED & man_macros[tok].flags) {
|
||||
/* If we're forcing scope (`TP'), keep it open. */
|
||||
if (MAN_FSCOPED & man_macros[tok].flags) {
|
||||
m->flags |= MAN_BLINE;
|
||||
return(1);
|
||||
} else if (n == m->last) {
|
||||
m->flags |= MAN_BLINE;
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! rew_scope(MAN_HEAD, m, tok))
|
||||
return(0);
|
||||
return(man_body_alloc(m, line, ppos, tok));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
in_line_eoln(MACRO_PROT_ARGS)
|
||||
{
|
||||
int w, la;
|
||||
char *p;
|
||||
struct man_node *n;
|
||||
|
||||
if ( ! man_elem_alloc(m, line, ppos, tok))
|
||||
return(0);
|
||||
|
||||
n = m->last;
|
||||
|
||||
for (;;) {
|
||||
la = *pos;
|
||||
w = man_args(m, line, pos, buf, &p);
|
||||
|
||||
if (-1 == w)
|
||||
return(0);
|
||||
if (0 == w)
|
||||
break;
|
||||
if ( ! man_word_alloc(m, line, la, p))
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If no arguments are specified and this is MAN_SCOPED (i.e.,
|
||||
* next-line scoped), then set our mode to indicate that we're
|
||||
* waiting for terms to load into our context.
|
||||
*/
|
||||
|
||||
if (n == m->last && MAN_SCOPED & man_macros[tok].flags) {
|
||||
assert( ! (MAN_NSCOPED & man_macros[tok].flags));
|
||||
m->flags |= MAN_ELINE;
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* Set ignorable context, if applicable. */
|
||||
|
||||
if (MAN_NSCOPED & man_macros[tok].flags) {
|
||||
assert( ! (MAN_SCOPED & man_macros[tok].flags));
|
||||
m->flags |= MAN_ILINE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rewind our element scope. Note that when TH is pruned, we'll
|
||||
* be back at the root, so make sure that we don't clobber as
|
||||
* its sibling.
|
||||
*/
|
||||
|
||||
for ( ; m->last; m->last = m->last->parent) {
|
||||
if (m->last == n)
|
||||
break;
|
||||
if (m->last->type == MAN_ROOT)
|
||||
break;
|
||||
if ( ! man_valid_post(m))
|
||||
return(0);
|
||||
if ( ! man_action_post(m))
|
||||
return(0);
|
||||
}
|
||||
|
||||
assert(m->last);
|
||||
|
||||
/*
|
||||
* Same here regarding whether we're back at the root.
|
||||
*/
|
||||
|
||||
if (m->last->type != MAN_ROOT && ! man_valid_post(m))
|
||||
return(0);
|
||||
if (m->last->type != MAN_ROOT && ! man_action_post(m))
|
||||
return(0);
|
||||
|
||||
m->next = MAN_ROOT == m->last->type ?
|
||||
MAN_NEXT_CHILD : MAN_NEXT_SIBLING;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_macroend(struct man *m)
|
||||
{
|
||||
|
||||
return(man_unscope(m, m->first, MANDOCERR_SCOPEEXIT));
|
||||
}
|
||||
|
964
commands/mdocml/man_term.c
Normal file
964
commands/mdocml/man_term.c
Normal file
|
@ -0,0 +1,964 @@
|
|||
/* $Id: man_term.c,v 1.76 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "out.h"
|
||||
#include "man.h"
|
||||
#include "term.h"
|
||||
#include "chars.h"
|
||||
#include "main.h"
|
||||
|
||||
#define INDENT 7
|
||||
#define HALFINDENT 3
|
||||
|
||||
/* FIXME: have PD set the default vspace width. */
|
||||
|
||||
struct mtermp {
|
||||
int fl;
|
||||
#define MANT_LITERAL (1 << 0)
|
||||
/*
|
||||
* Default amount to indent the left margin after leading text
|
||||
* has been printed (e.g., `HP' left-indent, `TP' and `IP' body
|
||||
* indent). This needs to be saved because `HP' and so on, if
|
||||
* not having a specified value, must default.
|
||||
*
|
||||
* Note that this is the indentation AFTER the left offset, so
|
||||
* the total offset is usually offset + lmargin.
|
||||
*/
|
||||
size_t lmargin;
|
||||
/*
|
||||
* The default offset, i.e., the amount between any text and the
|
||||
* page boundary.
|
||||
*/
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
#define DECL_ARGS struct termp *p, \
|
||||
struct mtermp *mt, \
|
||||
const struct man_node *n, \
|
||||
const struct man_meta *m
|
||||
|
||||
struct termact {
|
||||
int (*pre)(DECL_ARGS);
|
||||
void (*post)(DECL_ARGS);
|
||||
int flags;
|
||||
#define MAN_NOTEXT (1 << 0) /* Never has text children. */
|
||||
};
|
||||
|
||||
static int a2width(const struct man_node *);
|
||||
static int a2height(const struct man_node *);
|
||||
|
||||
static void print_man_nodelist(DECL_ARGS);
|
||||
static void print_man_node(DECL_ARGS);
|
||||
static void print_man_head(struct termp *, const void *);
|
||||
static void print_man_foot(struct termp *, const void *);
|
||||
static void print_bvspace(struct termp *,
|
||||
const struct man_node *);
|
||||
|
||||
static int pre_B(DECL_ARGS);
|
||||
static int pre_BI(DECL_ARGS);
|
||||
static int pre_HP(DECL_ARGS);
|
||||
static int pre_I(DECL_ARGS);
|
||||
static int pre_IP(DECL_ARGS);
|
||||
static int pre_PP(DECL_ARGS);
|
||||
static int pre_RB(DECL_ARGS);
|
||||
static int pre_RI(DECL_ARGS);
|
||||
static int pre_RS(DECL_ARGS);
|
||||
static int pre_SH(DECL_ARGS);
|
||||
static int pre_SS(DECL_ARGS);
|
||||
static int pre_TP(DECL_ARGS);
|
||||
static int pre_br(DECL_ARGS);
|
||||
static int pre_fi(DECL_ARGS);
|
||||
static int pre_ign(DECL_ARGS);
|
||||
static int pre_nf(DECL_ARGS);
|
||||
static int pre_sp(DECL_ARGS);
|
||||
|
||||
static void post_IP(DECL_ARGS);
|
||||
static void post_HP(DECL_ARGS);
|
||||
static void post_RS(DECL_ARGS);
|
||||
static void post_SH(DECL_ARGS);
|
||||
static void post_SS(DECL_ARGS);
|
||||
static void post_TP(DECL_ARGS);
|
||||
|
||||
static const struct termact termacts[MAN_MAX] = {
|
||||
{ pre_br, NULL, MAN_NOTEXT }, /* br */
|
||||
{ NULL, NULL, 0 }, /* TH */
|
||||
{ pre_SH, post_SH, 0 }, /* SH */
|
||||
{ pre_SS, post_SS, 0 }, /* SS */
|
||||
{ pre_TP, post_TP, 0 }, /* TP */
|
||||
{ pre_PP, NULL, 0 }, /* LP */
|
||||
{ pre_PP, NULL, 0 }, /* PP */
|
||||
{ pre_PP, NULL, 0 }, /* P */
|
||||
{ pre_IP, post_IP, 0 }, /* IP */
|
||||
{ pre_HP, post_HP, 0 }, /* HP */
|
||||
{ NULL, NULL, 0 }, /* SM */
|
||||
{ pre_B, NULL, 0 }, /* SB */
|
||||
{ pre_BI, NULL, 0 }, /* BI */
|
||||
{ pre_BI, NULL, 0 }, /* IB */
|
||||
{ pre_RB, NULL, 0 }, /* BR */
|
||||
{ pre_RB, NULL, 0 }, /* RB */
|
||||
{ NULL, NULL, 0 }, /* R */
|
||||
{ pre_B, NULL, 0 }, /* B */
|
||||
{ pre_I, NULL, 0 }, /* I */
|
||||
{ pre_RI, NULL, 0 }, /* IR */
|
||||
{ pre_RI, NULL, 0 }, /* RI */
|
||||
{ NULL, NULL, MAN_NOTEXT }, /* na */
|
||||
{ pre_I, NULL, 0 }, /* i */
|
||||
{ pre_sp, NULL, MAN_NOTEXT }, /* sp */
|
||||
{ pre_nf, NULL, 0 }, /* nf */
|
||||
{ pre_fi, NULL, 0 }, /* fi */
|
||||
{ NULL, NULL, 0 }, /* r */
|
||||
{ NULL, NULL, 0 }, /* RE */
|
||||
{ pre_RS, post_RS, 0 }, /* RS */
|
||||
{ pre_ign, NULL, 0 }, /* DT */
|
||||
{ pre_ign, NULL, 0 }, /* UC */
|
||||
{ pre_ign, NULL, 0 }, /* PD */
|
||||
{ pre_sp, NULL, MAN_NOTEXT }, /* Sp */
|
||||
{ pre_nf, NULL, 0 }, /* Vb */
|
||||
{ pre_fi, NULL, 0 }, /* Ve */
|
||||
{ pre_ign, NULL, 0 }, /* AT */
|
||||
};
|
||||
|
||||
|
||||
|
||||
void
|
||||
terminal_man(void *arg, const struct man *man)
|
||||
{
|
||||
struct termp *p;
|
||||
const struct man_node *n;
|
||||
const struct man_meta *m;
|
||||
struct mtermp mt;
|
||||
|
||||
p = (struct termp *)arg;
|
||||
|
||||
p->overstep = 0;
|
||||
p->maxrmargin = p->defrmargin;
|
||||
p->tabwidth = 5;
|
||||
|
||||
if (NULL == p->symtab)
|
||||
switch (p->enc) {
|
||||
case (TERMENC_ASCII):
|
||||
p->symtab = chars_init(CHARS_ASCII);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
n = man_node(man);
|
||||
m = man_meta(man);
|
||||
|
||||
term_begin(p, print_man_head, print_man_foot, m);
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
|
||||
mt.fl = 0;
|
||||
mt.lmargin = INDENT;
|
||||
mt.offset = INDENT;
|
||||
|
||||
if (n->child)
|
||||
print_man_nodelist(p, &mt, n->child, m);
|
||||
|
||||
term_end(p);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
a2height(const struct man_node *n)
|
||||
{
|
||||
struct roffsu su;
|
||||
|
||||
assert(MAN_TEXT == n->type);
|
||||
assert(n->string);
|
||||
if ( ! a2roffsu(n->string, &su, SCALE_VS))
|
||||
SCALE_VS_INIT(&su, strlen(n->string));
|
||||
|
||||
return((int)term_vspan(&su));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
a2width(const struct man_node *n)
|
||||
{
|
||||
struct roffsu su;
|
||||
|
||||
assert(MAN_TEXT == n->type);
|
||||
assert(n->string);
|
||||
if ( ! a2roffsu(n->string, &su, SCALE_BU))
|
||||
return(-1);
|
||||
|
||||
return((int)term_hspan(&su));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_bvspace(struct termp *p, const struct man_node *n)
|
||||
{
|
||||
term_newln(p);
|
||||
|
||||
if (NULL == n->prev)
|
||||
return;
|
||||
|
||||
if (MAN_SS == n->prev->tok)
|
||||
return;
|
||||
if (MAN_SH == n->prev->tok)
|
||||
return;
|
||||
|
||||
term_vspace(p);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_ign(DECL_ARGS)
|
||||
{
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_I(DECL_ARGS)
|
||||
{
|
||||
|
||||
term_fontrepl(p, TERMFONT_UNDER);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_fi(DECL_ARGS)
|
||||
{
|
||||
|
||||
mt->fl &= ~MANT_LITERAL;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_nf(DECL_ARGS)
|
||||
{
|
||||
|
||||
mt->fl |= MANT_LITERAL;
|
||||
return(MAN_Vb != n->tok);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_RB(DECL_ARGS)
|
||||
{
|
||||
const struct man_node *nn;
|
||||
int i;
|
||||
|
||||
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
|
||||
if (i % 2 && MAN_RB == n->tok)
|
||||
term_fontrepl(p, TERMFONT_BOLD);
|
||||
else if ( ! (i % 2) && MAN_RB != n->tok)
|
||||
term_fontrepl(p, TERMFONT_BOLD);
|
||||
else
|
||||
term_fontrepl(p, TERMFONT_NONE);
|
||||
|
||||
if (i > 0)
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
|
||||
print_man_node(p, mt, nn, m);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_RI(DECL_ARGS)
|
||||
{
|
||||
const struct man_node *nn;
|
||||
int i;
|
||||
|
||||
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
|
||||
if (i % 2 && MAN_RI == n->tok)
|
||||
term_fontrepl(p, TERMFONT_UNDER);
|
||||
else if ( ! (i % 2) && MAN_RI != n->tok)
|
||||
term_fontrepl(p, TERMFONT_UNDER);
|
||||
else
|
||||
term_fontrepl(p, TERMFONT_NONE);
|
||||
|
||||
if (i > 0)
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
|
||||
print_man_node(p, mt, nn, m);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_BI(DECL_ARGS)
|
||||
{
|
||||
const struct man_node *nn;
|
||||
int i;
|
||||
|
||||
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
|
||||
if (i % 2 && MAN_BI == n->tok)
|
||||
term_fontrepl(p, TERMFONT_UNDER);
|
||||
else if (i % 2)
|
||||
term_fontrepl(p, TERMFONT_BOLD);
|
||||
else if (MAN_BI == n->tok)
|
||||
term_fontrepl(p, TERMFONT_BOLD);
|
||||
else
|
||||
term_fontrepl(p, TERMFONT_UNDER);
|
||||
|
||||
if (i)
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
|
||||
print_man_node(p, mt, nn, m);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_B(DECL_ARGS)
|
||||
{
|
||||
|
||||
term_fontrepl(p, TERMFONT_BOLD);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_sp(DECL_ARGS)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
len = n->child ? a2height(n->child) : 1;
|
||||
|
||||
if (0 == len)
|
||||
term_newln(p);
|
||||
for (i = 0; i <= len; i++)
|
||||
term_vspace(p);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_br(DECL_ARGS)
|
||||
{
|
||||
|
||||
term_newln(p);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_HP(DECL_ARGS)
|
||||
{
|
||||
size_t len;
|
||||
int ival;
|
||||
const struct man_node *nn;
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_BLOCK):
|
||||
print_bvspace(p, n);
|
||||
return(1);
|
||||
case (MAN_BODY):
|
||||
p->flags |= TERMP_NOBREAK;
|
||||
p->flags |= TERMP_TWOSPACE;
|
||||
break;
|
||||
default:
|
||||
return(0);
|
||||
}
|
||||
|
||||
len = mt->lmargin;
|
||||
ival = -1;
|
||||
|
||||
/* Calculate offset. */
|
||||
|
||||
if (NULL != (nn = n->parent->head->child))
|
||||
if ((ival = a2width(nn)) >= 0)
|
||||
len = (size_t)ival;
|
||||
|
||||
if (0 == len)
|
||||
len = 1;
|
||||
|
||||
p->offset = mt->offset;
|
||||
p->rmargin = mt->offset + len;
|
||||
|
||||
if (ival >= 0)
|
||||
mt->lmargin = (size_t)ival;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
post_HP(DECL_ARGS)
|
||||
{
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_BLOCK):
|
||||
term_flushln(p);
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
term_flushln(p);
|
||||
p->flags &= ~TERMP_NOBREAK;
|
||||
p->flags &= ~TERMP_TWOSPACE;
|
||||
p->offset = mt->offset;
|
||||
p->rmargin = p->maxrmargin;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_PP(DECL_ARGS)
|
||||
{
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_BLOCK):
|
||||
mt->lmargin = INDENT;
|
||||
print_bvspace(p, n);
|
||||
break;
|
||||
default:
|
||||
p->offset = mt->offset;
|
||||
break;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_IP(DECL_ARGS)
|
||||
{
|
||||
const struct man_node *nn;
|
||||
size_t len;
|
||||
int ival;
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_BODY):
|
||||
p->flags |= TERMP_NOLPAD;
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
break;
|
||||
case (MAN_HEAD):
|
||||
p->flags |= TERMP_NOBREAK;
|
||||
break;
|
||||
case (MAN_BLOCK):
|
||||
print_bvspace(p, n);
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
return(1);
|
||||
}
|
||||
|
||||
len = mt->lmargin;
|
||||
ival = -1;
|
||||
|
||||
/* Calculate offset. */
|
||||
|
||||
if (NULL != (nn = n->parent->head->child))
|
||||
if (NULL != (nn = nn->next)) {
|
||||
for ( ; nn->next; nn = nn->next)
|
||||
/* Do nothing. */ ;
|
||||
if ((ival = a2width(nn)) >= 0)
|
||||
len = (size_t)ival;
|
||||
}
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_HEAD):
|
||||
/* Handle zero-width lengths. */
|
||||
if (0 == len)
|
||||
len = 1;
|
||||
|
||||
p->offset = mt->offset;
|
||||
p->rmargin = mt->offset + len;
|
||||
if (ival < 0)
|
||||
break;
|
||||
|
||||
/* Set the saved left-margin. */
|
||||
mt->lmargin = (size_t)ival;
|
||||
|
||||
/* Don't print the length value. */
|
||||
for (nn = n->child; nn->next; nn = nn->next)
|
||||
print_man_node(p, mt, nn, m);
|
||||
return(0);
|
||||
case (MAN_BODY):
|
||||
p->offset = mt->offset + len;
|
||||
p->rmargin = p->maxrmargin;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
post_IP(DECL_ARGS)
|
||||
{
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_HEAD):
|
||||
term_flushln(p);
|
||||
p->flags &= ~TERMP_NOBREAK;
|
||||
p->rmargin = p->maxrmargin;
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
term_flushln(p);
|
||||
p->flags &= ~TERMP_NOLPAD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_TP(DECL_ARGS)
|
||||
{
|
||||
const struct man_node *nn;
|
||||
size_t len;
|
||||
int ival;
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_HEAD):
|
||||
p->flags |= TERMP_NOBREAK;
|
||||
p->flags |= TERMP_TWOSPACE;
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
p->flags |= TERMP_NOLPAD;
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
break;
|
||||
case (MAN_BLOCK):
|
||||
print_bvspace(p, n);
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
return(1);
|
||||
}
|
||||
|
||||
len = (size_t)mt->lmargin;
|
||||
ival = -1;
|
||||
|
||||
/* Calculate offset. */
|
||||
|
||||
if (NULL != (nn = n->parent->head->child)) {
|
||||
while (nn && MAN_TEXT != nn->type)
|
||||
nn = nn->next;
|
||||
if (nn && nn->next)
|
||||
if ((ival = a2width(nn)) >= 0)
|
||||
len = (size_t)ival;
|
||||
}
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_HEAD):
|
||||
/* Handle zero-length properly. */
|
||||
if (0 == len)
|
||||
len = 1;
|
||||
|
||||
p->offset = mt->offset;
|
||||
p->rmargin = mt->offset + len;
|
||||
|
||||
/* Don't print same-line elements. */
|
||||
for (nn = n->child; nn; nn = nn->next)
|
||||
if (nn->line > n->line)
|
||||
print_man_node(p, mt, nn, m);
|
||||
|
||||
if (ival >= 0)
|
||||
mt->lmargin = (size_t)ival;
|
||||
|
||||
return(0);
|
||||
case (MAN_BODY):
|
||||
p->offset = mt->offset + len;
|
||||
p->rmargin = p->maxrmargin;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
post_TP(DECL_ARGS)
|
||||
{
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_HEAD):
|
||||
term_flushln(p);
|
||||
p->flags &= ~TERMP_NOBREAK;
|
||||
p->flags &= ~TERMP_TWOSPACE;
|
||||
p->rmargin = p->maxrmargin;
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
term_flushln(p);
|
||||
p->flags &= ~TERMP_NOLPAD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_SS(DECL_ARGS)
|
||||
{
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_BLOCK):
|
||||
mt->lmargin = INDENT;
|
||||
mt->offset = INDENT;
|
||||
/* If following a prior empty `SS', no vspace. */
|
||||
if (n->prev && MAN_SS == n->prev->tok)
|
||||
if (NULL == n->prev->body->child)
|
||||
break;
|
||||
if (NULL == n->prev)
|
||||
break;
|
||||
term_vspace(p);
|
||||
break;
|
||||
case (MAN_HEAD):
|
||||
term_fontrepl(p, TERMFONT_BOLD);
|
||||
p->offset = HALFINDENT;
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
p->offset = mt->offset;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
post_SS(DECL_ARGS)
|
||||
{
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_HEAD):
|
||||
term_newln(p);
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
term_newln(p);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_SH(DECL_ARGS)
|
||||
{
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_BLOCK):
|
||||
mt->lmargin = INDENT;
|
||||
mt->offset = INDENT;
|
||||
/* If following a prior empty `SH', no vspace. */
|
||||
if (n->prev && MAN_SH == n->prev->tok)
|
||||
if (NULL == n->prev->body->child)
|
||||
break;
|
||||
/* If the first macro, no vspae. */
|
||||
if (NULL == n->prev)
|
||||
break;
|
||||
term_vspace(p);
|
||||
break;
|
||||
case (MAN_HEAD):
|
||||
term_fontrepl(p, TERMFONT_BOLD);
|
||||
p->offset = 0;
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
p->offset = mt->offset;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
post_SH(DECL_ARGS)
|
||||
{
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_HEAD):
|
||||
term_newln(p);
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
term_newln(p);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
pre_RS(DECL_ARGS)
|
||||
{
|
||||
const struct man_node *nn;
|
||||
int ival;
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_BLOCK):
|
||||
term_newln(p);
|
||||
return(1);
|
||||
case (MAN_HEAD):
|
||||
return(0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (NULL == (nn = n->parent->head->child)) {
|
||||
mt->offset = mt->lmargin + INDENT;
|
||||
p->offset = mt->offset;
|
||||
return(1);
|
||||
}
|
||||
|
||||
if ((ival = a2width(nn)) < 0)
|
||||
return(1);
|
||||
|
||||
mt->offset = INDENT + (size_t)ival;
|
||||
p->offset = mt->offset;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
post_RS(DECL_ARGS)
|
||||
{
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_BLOCK):
|
||||
mt->offset = mt->lmargin = INDENT;
|
||||
break;
|
||||
case (MAN_HEAD):
|
||||
break;
|
||||
default:
|
||||
term_newln(p);
|
||||
p->offset = INDENT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_man_node(DECL_ARGS)
|
||||
{
|
||||
size_t rm, rmax;
|
||||
int c;
|
||||
|
||||
c = 1;
|
||||
|
||||
switch (n->type) {
|
||||
case(MAN_TEXT):
|
||||
if (0 == *n->string) {
|
||||
term_vspace(p);
|
||||
break;
|
||||
}
|
||||
|
||||
term_word(p, n->string);
|
||||
|
||||
/* FIXME: this means that macro lines are munged! */
|
||||
|
||||
if (MANT_LITERAL & mt->fl) {
|
||||
rm = p->rmargin;
|
||||
rmax = p->maxrmargin;
|
||||
p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
term_flushln(p);
|
||||
p->rmargin = rm;
|
||||
p->maxrmargin = rmax;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
|
||||
term_fontrepl(p, TERMFONT_NONE);
|
||||
if (termacts[n->tok].pre)
|
||||
c = (*termacts[n->tok].pre)(p, mt, n, m);
|
||||
break;
|
||||
}
|
||||
|
||||
if (c && n->child)
|
||||
print_man_nodelist(p, mt, n->child, m);
|
||||
|
||||
if (MAN_TEXT != n->type) {
|
||||
if (termacts[n->tok].post)
|
||||
(*termacts[n->tok].post)(p, mt, n, m);
|
||||
if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
|
||||
term_fontrepl(p, TERMFONT_NONE);
|
||||
}
|
||||
|
||||
if (MAN_EOS & n->flags)
|
||||
p->flags |= TERMP_SENTENCE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_man_nodelist(DECL_ARGS)
|
||||
{
|
||||
|
||||
print_man_node(p, mt, n, m);
|
||||
if ( ! n->next)
|
||||
return;
|
||||
print_man_nodelist(p, mt, n->next, m);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_man_foot(struct termp *p, const void *arg)
|
||||
{
|
||||
char buf[DATESIZ];
|
||||
const struct man_meta *meta;
|
||||
|
||||
meta = (const struct man_meta *)arg;
|
||||
|
||||
term_fontrepl(p, TERMFONT_NONE);
|
||||
|
||||
if (meta->rawdate)
|
||||
strlcpy(buf, meta->rawdate, DATESIZ);
|
||||
else
|
||||
time2a(meta->date, buf, DATESIZ);
|
||||
|
||||
term_vspace(p);
|
||||
term_vspace(p);
|
||||
term_vspace(p);
|
||||
|
||||
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
|
||||
p->rmargin = p->maxrmargin - strlen(buf);
|
||||
p->offset = 0;
|
||||
|
||||
if (meta->source)
|
||||
term_word(p, meta->source);
|
||||
if (meta->source)
|
||||
term_word(p, "");
|
||||
term_flushln(p);
|
||||
|
||||
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
|
||||
p->offset = p->rmargin;
|
||||
p->rmargin = p->maxrmargin;
|
||||
p->flags &= ~TERMP_NOBREAK;
|
||||
|
||||
term_word(p, buf);
|
||||
term_flushln(p);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_man_head(struct termp *p, const void *arg)
|
||||
{
|
||||
char buf[BUFSIZ], title[BUFSIZ];
|
||||
size_t buflen, titlen;
|
||||
const struct man_meta *m;
|
||||
|
||||
m = (const struct man_meta *)arg;
|
||||
|
||||
/*
|
||||
* Note that old groff would spit out some spaces before the
|
||||
* header. We discontinue this strange behaviour, but at one
|
||||
* point we did so here.
|
||||
*/
|
||||
|
||||
p->rmargin = p->maxrmargin;
|
||||
|
||||
p->offset = 0;
|
||||
buf[0] = title[0] = '\0';
|
||||
|
||||
if (m->vol)
|
||||
strlcpy(buf, m->vol, BUFSIZ);
|
||||
buflen = strlen(buf);
|
||||
|
||||
snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
|
||||
titlen = strlen(title);
|
||||
|
||||
p->offset = 0;
|
||||
p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
|
||||
(p->maxrmargin - strlen(buf) + 1) / 2 :
|
||||
p->maxrmargin - buflen;
|
||||
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
|
||||
|
||||
term_word(p, title);
|
||||
term_flushln(p);
|
||||
|
||||
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
|
||||
p->offset = p->rmargin;
|
||||
p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
|
||||
p->maxrmargin - titlen : p->maxrmargin;
|
||||
|
||||
term_word(p, buf);
|
||||
term_flushln(p);
|
||||
|
||||
p->flags &= ~TERMP_NOBREAK;
|
||||
if (p->rmargin + titlen <= p->maxrmargin) {
|
||||
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
|
||||
p->offset = p->rmargin;
|
||||
p->rmargin = p->maxrmargin;
|
||||
term_word(p, title);
|
||||
term_flushln(p);
|
||||
}
|
||||
|
||||
p->rmargin = p->maxrmargin;
|
||||
p->offset = 0;
|
||||
p->flags &= ~TERMP_NOSPACE;
|
||||
|
||||
/*
|
||||
* Groff likes to have some leading spaces before content. Well
|
||||
* that's fine by me.
|
||||
*/
|
||||
|
||||
term_vspace(p);
|
||||
term_vspace(p);
|
||||
term_vspace(p);
|
||||
}
|
328
commands/mdocml/man_validate.c
Normal file
328
commands/mdocml/man_validate.c
Normal file
|
@ -0,0 +1,328 @@
|
|||
/* $Id: man_validate.c,v 1.44 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libman.h"
|
||||
#include "libmandoc.h"
|
||||
|
||||
#define CHKARGS struct man *m, struct man_node *n
|
||||
|
||||
typedef int (*v_check)(CHKARGS);
|
||||
|
||||
struct man_valid {
|
||||
v_check *pres;
|
||||
v_check *posts;
|
||||
};
|
||||
|
||||
static int check_bline(CHKARGS);
|
||||
static int check_eq0(CHKARGS);
|
||||
static int check_le1(CHKARGS);
|
||||
static int check_ge2(CHKARGS);
|
||||
static int check_le5(CHKARGS);
|
||||
static int check_par(CHKARGS);
|
||||
static int check_part(CHKARGS);
|
||||
static int check_root(CHKARGS);
|
||||
static int check_sec(CHKARGS);
|
||||
static int check_text(CHKARGS);
|
||||
static int check_title(CHKARGS);
|
||||
|
||||
static v_check posts_eq0[] = { check_eq0, NULL };
|
||||
static v_check posts_th[] = { check_ge2, check_le5, check_title, NULL };
|
||||
static v_check posts_par[] = { check_par, NULL };
|
||||
static v_check posts_part[] = { check_part, NULL };
|
||||
static v_check posts_sec[] = { check_sec, NULL };
|
||||
static v_check posts_le1[] = { check_le1, NULL };
|
||||
static v_check pres_bline[] = { check_bline, NULL };
|
||||
|
||||
static const struct man_valid man_valids[MAN_MAX] = {
|
||||
{ NULL, posts_eq0 }, /* br */
|
||||
{ pres_bline, posts_th }, /* TH */
|
||||
{ pres_bline, posts_sec }, /* SH */
|
||||
{ pres_bline, posts_sec }, /* SS */
|
||||
{ pres_bline, posts_par }, /* TP */
|
||||
{ pres_bline, posts_par }, /* LP */
|
||||
{ pres_bline, posts_par }, /* PP */
|
||||
{ pres_bline, posts_par }, /* P */
|
||||
{ pres_bline, posts_par }, /* IP */
|
||||
{ pres_bline, posts_par }, /* HP */
|
||||
{ NULL, NULL }, /* SM */
|
||||
{ NULL, NULL }, /* SB */
|
||||
{ NULL, NULL }, /* BI */
|
||||
{ NULL, NULL }, /* IB */
|
||||
{ NULL, NULL }, /* BR */
|
||||
{ NULL, NULL }, /* RB */
|
||||
{ NULL, NULL }, /* R */
|
||||
{ NULL, NULL }, /* B */
|
||||
{ NULL, NULL }, /* I */
|
||||
{ NULL, NULL }, /* IR */
|
||||
{ NULL, NULL }, /* RI */
|
||||
{ NULL, posts_eq0 }, /* na */
|
||||
{ NULL, NULL }, /* i */
|
||||
{ NULL, posts_le1 }, /* sp */
|
||||
{ pres_bline, posts_eq0 }, /* nf */
|
||||
{ pres_bline, posts_eq0 }, /* fi */
|
||||
{ NULL, NULL }, /* r */
|
||||
{ NULL, NULL }, /* RE */
|
||||
{ NULL, posts_part }, /* RS */
|
||||
{ NULL, NULL }, /* DT */
|
||||
{ NULL, NULL }, /* UC */
|
||||
{ NULL, NULL }, /* PD */
|
||||
{ NULL, posts_le1 }, /* Sp */
|
||||
{ pres_bline, posts_le1 }, /* Vb */
|
||||
{ pres_bline, posts_eq0 }, /* Ve */
|
||||
{ NULL, NULL }, /* AT */
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
man_valid_pre(struct man *m, struct man_node *n)
|
||||
{
|
||||
v_check *cp;
|
||||
|
||||
if (MAN_TEXT == n->type)
|
||||
return(1);
|
||||
if (MAN_ROOT == n->type)
|
||||
return(1);
|
||||
|
||||
if (NULL == (cp = man_valids[n->tok].pres))
|
||||
return(1);
|
||||
for ( ; *cp; cp++)
|
||||
if ( ! (*cp)(m, n))
|
||||
return(0);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
man_valid_post(struct man *m)
|
||||
{
|
||||
v_check *cp;
|
||||
|
||||
if (MAN_VALID & m->last->flags)
|
||||
return(1);
|
||||
m->last->flags |= MAN_VALID;
|
||||
|
||||
switch (m->last->type) {
|
||||
case (MAN_TEXT):
|
||||
return(check_text(m, m->last));
|
||||
case (MAN_ROOT):
|
||||
return(check_root(m, m->last));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (NULL == (cp = man_valids[m->last->tok].posts))
|
||||
return(1);
|
||||
for ( ; *cp; cp++)
|
||||
if ( ! (*cp)(m, m->last))
|
||||
return(0);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_root(CHKARGS)
|
||||
{
|
||||
|
||||
if (MAN_BLINE & m->flags)
|
||||
return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT));
|
||||
if (MAN_ELINE & m->flags)
|
||||
return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT));
|
||||
|
||||
m->flags &= ~MAN_BLINE;
|
||||
m->flags &= ~MAN_ELINE;
|
||||
|
||||
if (NULL == m->first->child) {
|
||||
man_nmsg(m, n, MANDOCERR_NODOCBODY);
|
||||
return(0);
|
||||
} else if (NULL == m->meta.title) {
|
||||
if ( ! man_nmsg(m, n, MANDOCERR_NOTITLE))
|
||||
return(0);
|
||||
/*
|
||||
* If a title hasn't been set, do so now (by
|
||||
* implication, date and section also aren't set).
|
||||
*
|
||||
* FIXME: this should be in man_action.c.
|
||||
*/
|
||||
m->meta.title = mandoc_strdup("unknown");
|
||||
m->meta.date = time(NULL);
|
||||
m->meta.msec = mandoc_strdup("1");
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_title(CHKARGS)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
assert(n->child);
|
||||
/* FIXME: is this sufficient? */
|
||||
if ('\0' == *n->child->string) {
|
||||
man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
|
||||
return(0);
|
||||
}
|
||||
|
||||
for (p = n->child->string; '\0' != *p; p++)
|
||||
if (isalpha((u_char)*p) && ! isupper((u_char)*p))
|
||||
if ( ! man_nmsg(m, n, MANDOCERR_UPPERCASE))
|
||||
return(0);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_text(CHKARGS)
|
||||
{
|
||||
char *p;
|
||||
int pos, c;
|
||||
|
||||
assert(n->string);
|
||||
|
||||
for (p = n->string, pos = n->pos + 1; *p; p++, pos++) {
|
||||
if ('\\' == *p) {
|
||||
c = mandoc_special(p);
|
||||
if (c) {
|
||||
p += c - 1;
|
||||
pos += c - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
c = man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE);
|
||||
if ( ! (MAN_IGN_ESCAPE & m->pflags) && ! c)
|
||||
return(c);
|
||||
}
|
||||
|
||||
if ('\t' == *p || isprint((u_char)*p) || ASCII_HYPH == *p)
|
||||
continue;
|
||||
if ( ! man_pmsg(m, n->line, pos, MANDOCERR_BADCHAR))
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
#define INEQ_DEFINE(x, ineq, name) \
|
||||
static int \
|
||||
check_##name(CHKARGS) \
|
||||
{ \
|
||||
if (n->nchild ineq (x)) \
|
||||
return(1); \
|
||||
man_vmsg(m, MANDOCERR_SYNTARGCOUNT, n->line, n->pos, \
|
||||
"line arguments %s %d (have %d)", \
|
||||
#ineq, (x), n->nchild); \
|
||||
return(0); \
|
||||
}
|
||||
|
||||
INEQ_DEFINE(0, ==, eq0)
|
||||
INEQ_DEFINE(1, <=, le1)
|
||||
INEQ_DEFINE(2, >=, ge2)
|
||||
INEQ_DEFINE(5, <=, le5)
|
||||
|
||||
|
||||
static int
|
||||
check_sec(CHKARGS)
|
||||
{
|
||||
|
||||
if (MAN_HEAD == n->type && 0 == n->nchild) {
|
||||
man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
|
||||
return(0);
|
||||
} else if (MAN_BODY == n->type && 0 == n->nchild)
|
||||
return(man_nmsg(m, n, MANDOCERR_NOBODY));
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_part(CHKARGS)
|
||||
{
|
||||
|
||||
if (MAN_BODY == n->type && 0 == n->nchild)
|
||||
return(man_nmsg(m, n, MANDOCERR_NOBODY));
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_par(CHKARGS)
|
||||
{
|
||||
|
||||
if (MAN_BODY == n->type)
|
||||
switch (n->tok) {
|
||||
case (MAN_IP):
|
||||
/* FALLTHROUGH */
|
||||
case (MAN_HP):
|
||||
/* FALLTHROUGH */
|
||||
case (MAN_TP):
|
||||
/* Body-less lists are ok. */
|
||||
break;
|
||||
default:
|
||||
if (n->nchild)
|
||||
break;
|
||||
return(man_nmsg(m, n, MANDOCERR_NOBODY));
|
||||
}
|
||||
if (MAN_HEAD == n->type)
|
||||
switch (n->tok) {
|
||||
case (MAN_PP):
|
||||
/* FALLTHROUGH */
|
||||
case (MAN_P):
|
||||
/* FALLTHROUGH */
|
||||
case (MAN_LP):
|
||||
if (0 == n->nchild)
|
||||
break;
|
||||
return(man_nmsg(m, n, MANDOCERR_ARGSLOST));
|
||||
default:
|
||||
if (n->nchild)
|
||||
break;
|
||||
return(man_nmsg(m, n, MANDOCERR_NOARGS));
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_bline(CHKARGS)
|
||||
{
|
||||
|
||||
assert( ! (MAN_ELINE & m->flags));
|
||||
if (MAN_BLINE & m->flags) {
|
||||
man_nmsg(m, n, MANDOCERR_SYNTLINESCOPE);
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
530
commands/mdocml/mandoc.1
Normal file
530
commands/mdocml/mandoc.1
Normal file
|
@ -0,0 +1,530 @@
|
|||
.\" $Id: mandoc.1,v 1.63 2010/06/11 07:15:42 kristaps Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: June 11 2010 $
|
||||
.Dt MANDOC 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mandoc
|
||||
.Nd format and display UNIX manuals
|
||||
.Sh SYNOPSIS
|
||||
.Nm mandoc
|
||||
.Op Fl V
|
||||
.Op Fl f Ns Ar option
|
||||
.Op Fl m Ns Ar format
|
||||
.Op Fl O Ns Ar option
|
||||
.Op Fl T Ns Ar output
|
||||
.Op Fl W Ns Ar err
|
||||
.Op Ar file...
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility formats
|
||||
.Ux
|
||||
manual pages for display.
|
||||
The arguments are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl f Ns Ar option
|
||||
Comma-separated compiler options.
|
||||
See
|
||||
.Sx Compiler Options
|
||||
for details.
|
||||
.It Fl m Ns Ar format
|
||||
Input format.
|
||||
See
|
||||
.Sx Input Formats
|
||||
for available formats.
|
||||
Defaults to
|
||||
.Fl m Ns Cm andoc .
|
||||
.It Fl O Ns Ar option
|
||||
Comma-separated output options.
|
||||
See
|
||||
.Sx Output Options
|
||||
for details.
|
||||
.It Fl T Ns Ar output
|
||||
Output format.
|
||||
See
|
||||
.Sx Output Formats
|
||||
for available formats.
|
||||
Defaults to
|
||||
.Fl T Ns Cm ascii .
|
||||
.It Fl V
|
||||
Print version and exit.
|
||||
.It Fl W Ns Ar err
|
||||
Comma-separated warning options.
|
||||
Use
|
||||
.Fl W Ns Cm all
|
||||
to print warnings,
|
||||
.Fl W Ns Cm error
|
||||
for warnings to be considered errors and cause utility
|
||||
termination.
|
||||
Multiple
|
||||
.Fl W
|
||||
arguments may be comma-separated, such as
|
||||
.Fl W Ns Cm error , Ns Cm all .
|
||||
.It Ar file
|
||||
Read input from zero or more files.
|
||||
If unspecified, reads from stdin.
|
||||
If multiple files are specified,
|
||||
.Nm
|
||||
will halt with the first failed parse.
|
||||
.El
|
||||
.Pp
|
||||
By default,
|
||||
.Nm
|
||||
reads
|
||||
.Xr mdoc 7
|
||||
or
|
||||
.Xr man 7
|
||||
text from stdin, implying
|
||||
.Fl m Ns Cm andoc ,
|
||||
and produces
|
||||
.Fl T Ns Cm ascii
|
||||
output.
|
||||
.Pp
|
||||
.Ex -std mandoc
|
||||
.Ss Input Formats
|
||||
The
|
||||
.Nm
|
||||
utility accepts
|
||||
.Xr mdoc 7
|
||||
and
|
||||
.Xr man 7
|
||||
input with
|
||||
.Fl m Ns Cm doc
|
||||
and
|
||||
.Fl m Ns Cm an ,
|
||||
respectively.
|
||||
The
|
||||
.Xr mdoc 7
|
||||
format is
|
||||
.Em strongly
|
||||
recommended;
|
||||
.Xr man 7
|
||||
should only be used for legacy manuals.
|
||||
.Pp
|
||||
A third option,
|
||||
.Fl m Ns Cm andoc ,
|
||||
which is also the default, determines encoding on-the-fly: if the first
|
||||
non-comment macro is
|
||||
.Sq \&Dd
|
||||
or
|
||||
.Sq \&Dt ,
|
||||
the
|
||||
.Xr mdoc 7
|
||||
parser is used; otherwise, the
|
||||
.Xr man 7
|
||||
parser is used.
|
||||
.Pp
|
||||
If multiple
|
||||
files are specified with
|
||||
.Fl m Ns Cm andoc ,
|
||||
each has its file-type determined this way.
|
||||
If multiple files are
|
||||
specified and
|
||||
.Fl m Ns Cm doc
|
||||
or
|
||||
.Fl m Ns Cm an
|
||||
is specified, then this format is used exclusively.
|
||||
.Ss Output Formats
|
||||
The
|
||||
.Nm
|
||||
utility accepts the following
|
||||
.Fl T
|
||||
arguments (see
|
||||
.Sx OUTPUT ) :
|
||||
.Bl -tag -width Ds
|
||||
.It Fl T Ns Cm ascii
|
||||
Produce 7-bit ASCII output, backspace-encoded for bold and underline
|
||||
styles.
|
||||
This is the default.
|
||||
See
|
||||
.Sx ASCII Output .
|
||||
.It Fl T Ns Cm html
|
||||
Produce strict HTML-4.01 output, with a sane default style.
|
||||
See
|
||||
.Sx HTML Output .
|
||||
.It Fl T Ns Cm lint
|
||||
Parse only: produce no output.
|
||||
Implies
|
||||
.Fl W Ns Cm all
|
||||
and
|
||||
.Fl f Ns Cm strict .
|
||||
.It Fl T Ns Cm ps
|
||||
Produce PostScript output.
|
||||
See
|
||||
.Sx PostScript Output .
|
||||
.It Fl T Ns Cm tree
|
||||
Produce an indented parse tree.
|
||||
.It Fl T Ns Cm xhtml
|
||||
Produce strict XHTML-1.0 output, with a sane default style.
|
||||
See
|
||||
.Sx XHTML Output .
|
||||
.El
|
||||
.Pp
|
||||
If multiple input files are specified, these will be processed by the
|
||||
corresponding filter in-order.
|
||||
.Ss Compiler Options
|
||||
Default compiler behaviour may be overridden with the
|
||||
.Fl f
|
||||
flag.
|
||||
.Bl -tag -width Ds
|
||||
.It Fl f Ns Cm ign-errors
|
||||
When parsing multiple files, don't halt when one errors out.
|
||||
Useful with
|
||||
.Fl T Ns Cm lint
|
||||
over a large set of manuals passed on the command line.
|
||||
.It Fl f Ns Cm ign-escape
|
||||
Ignore invalid escape sequences.
|
||||
This is the default, but the option can be used to override an earlier
|
||||
.Fl f Ns Cm strict .
|
||||
.It Fl f Ns Cm ign-scope
|
||||
When rewinding the scope of a block macro, forces the compiler to ignore
|
||||
scope violations.
|
||||
This can seriously mangle the resulting tree.
|
||||
.Pq mdoc only
|
||||
.It Fl f Ns Cm no-ign-escape
|
||||
Do not ignore invalid escape sequences.
|
||||
.It Fl f Ns Cm no-ign-macro
|
||||
Do not ignore unknown macros at the start of input lines.
|
||||
.It Fl f Ns Cm strict
|
||||
Implies
|
||||
.Fl f Ns Cm no-ign-escape
|
||||
and
|
||||
.Fl f Ns Cm no-ign-macro .
|
||||
.El
|
||||
.Ss Output Options
|
||||
The
|
||||
.Fl T Ns Ar html
|
||||
and
|
||||
.Fl T Ns Ar xhtml
|
||||
modes accept the following output options:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl O Ns Cm includes Ns = Ns Ar fmt
|
||||
The string
|
||||
.Ar fmt ,
|
||||
for example,
|
||||
.Ar ../src/%I.html ,
|
||||
is used as a template for linked header files (usually via the
|
||||
.Sq \&In
|
||||
macro).
|
||||
Instances of
|
||||
.Sq \&%I
|
||||
are replaced with the include filename.
|
||||
The default is not to present a
|
||||
hyperlink.
|
||||
.It Fl O Ns Cm man Ns = Ns Ar fmt
|
||||
The string
|
||||
.Ar fmt ,
|
||||
for example,
|
||||
.Ar ../html%S/%N.%S.html ,
|
||||
is used as a template for linked manuals (usually via the
|
||||
.Sq \&Xr
|
||||
macro).
|
||||
Instances of
|
||||
.Sq \&%N
|
||||
and
|
||||
.Sq %S
|
||||
are replaced with the linked manual's name and section, respectively.
|
||||
If no section is included, section 1 is assumed.
|
||||
The default is not to
|
||||
present a hyperlink.
|
||||
.It Fl O Ns Cm style Ns = Ns Ar style.css
|
||||
The file
|
||||
.Ar style.css
|
||||
is used for an external style-sheet.
|
||||
This must be a valid absolute or
|
||||
relative URI.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fl T Ns Ar ascii
|
||||
mode accepts the following output option:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl O Ns Cm width Ns = Ns Ar width
|
||||
The output width is set to
|
||||
.Ar width ,
|
||||
which will normalise to \(>=60.
|
||||
.El
|
||||
.Sh OUTPUT
|
||||
This section documents output details of
|
||||
.Nm .
|
||||
In general, output conforms to the traditional manual style of a header,
|
||||
a body composed of sections and sub-sections, and a footer.
|
||||
.Pp
|
||||
The text style of output characters (non-macro characters, punctuation,
|
||||
and white-space) is dictated by context.
|
||||
.Pp
|
||||
White-space is generally stripped from input.
|
||||
This can be changed with
|
||||
character escapes (specified in
|
||||
.Xr mandoc_char 7 )
|
||||
or literal modes (specified in
|
||||
.Xr mdoc 7
|
||||
and
|
||||
.Xr man 7 ) .
|
||||
.Pp
|
||||
If non-macro punctuation is set apart from words, such as in the phrase
|
||||
.Dq to be \&, or not to be ,
|
||||
it's processed by
|
||||
.Nm ,
|
||||
regardless of output format, according to the following rules: opening
|
||||
punctuation
|
||||
.Po
|
||||
.Sq \&( ,
|
||||
.Sq \&[ ,
|
||||
and
|
||||
.Sq \&{
|
||||
.Pc
|
||||
is not followed by a space; closing punctuation
|
||||
.Po
|
||||
.Sq \&. ,
|
||||
.Sq \&, ,
|
||||
.Sq \&; ,
|
||||
.Sq \&: ,
|
||||
.Sq \&? ,
|
||||
.Sq \&! ,
|
||||
.Sq \&) ,
|
||||
.Sq \&]
|
||||
and
|
||||
.Sq \&}
|
||||
.Pc
|
||||
is not preceded by white-space.
|
||||
.Pp
|
||||
If the input is
|
||||
.Xr mdoc 7 ,
|
||||
however, these rules are also applied to macro arguments when appropriate.
|
||||
.Ss ASCII Output
|
||||
Output produced by
|
||||
.Fl T Ns Cm ascii ,
|
||||
which is the default, is rendered in standard 7-bit ASCII documented in
|
||||
.Xr ascii 7 .
|
||||
.Pp
|
||||
Font styles are applied by using back-spaced encoding such that an
|
||||
underlined character
|
||||
.Sq c
|
||||
is rendered as
|
||||
.Sq _ Ns \e[bs] Ns c ,
|
||||
where
|
||||
.Sq \e[bs]
|
||||
is the back-space character number 8.
|
||||
Emboldened characters are rendered as
|
||||
.Sq c Ns \e[bs] Ns c .
|
||||
.Pp
|
||||
The special characters documented in
|
||||
.Xr mandoc_char 7
|
||||
are rendered best-effort in an ASCII equivalent.
|
||||
.Pp
|
||||
Output width is limited to 78 visible columns unless literal input lines
|
||||
exceed this limit.
|
||||
.Ss HTML Output
|
||||
Output produced by
|
||||
.Fl T Ns Cm html
|
||||
conforms to HTML-4.01 strict.
|
||||
.Pp
|
||||
Font styles and page structure are applied using CSS2.
|
||||
By default, no font style is applied to any text,
|
||||
although CSS2 is hard-coded to format
|
||||
the basic structure of output.
|
||||
.Pp
|
||||
The
|
||||
.Pa example.style.css
|
||||
file documents the range of styles applied to output and, if used, will
|
||||
cause rendered documents to appear as they do in
|
||||
.Fl T Ns Cm ascii .
|
||||
.Pp
|
||||
Special characters are rendered in decimal-encoded UTF-8.
|
||||
.Ss PostScript Output
|
||||
PostScript Level 2 pages may be generated by
|
||||
.Fl T Ns Cm ps .
|
||||
Output pages are US-letter sized (215.9 x 279.4 mm) and rendered in
|
||||
fixed, 10-point Courier font.
|
||||
.Ss XHTML Output
|
||||
Output produced by
|
||||
.Fl T Ns Cm xhtml
|
||||
conforms to XHTML-1.0 strict.
|
||||
.Pp
|
||||
See
|
||||
.Sx HTML Output
|
||||
for details; beyond generating XHTML tags instead of HTML tags, these
|
||||
output modes are identical.
|
||||
.Sh EXAMPLES
|
||||
To page manuals to the terminal:
|
||||
.Pp
|
||||
.D1 $ mandoc \-Wall,error \-fstrict mandoc.1 2\*(Gt&1 | less
|
||||
.D1 $ mandoc mandoc.1 mdoc.3 mdoc.7 | less
|
||||
.Pp
|
||||
To produce HTML manuals with
|
||||
.Ar style.css
|
||||
as the style-sheet:
|
||||
.Pp
|
||||
.D1 $ mandoc \-Thtml -Ostyle=style.css mdoc.7 \*(Gt mdoc.7.html
|
||||
.Pp
|
||||
To check over a large set of manuals:
|
||||
.Pp
|
||||
.Dl $ mandoc \-Tlint \-fign-errors `find /usr/src -name \e*\e.[1-9]`
|
||||
.Sh COMPATIBILITY
|
||||
This section summarises
|
||||
.Nm
|
||||
compatibility with
|
||||
.Xr groff 1 .
|
||||
Each input and output format is separately noted.
|
||||
.Ss ASCII Compatibility
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
The
|
||||
.Sq \e~
|
||||
special character doesn't produce expected behaviour in
|
||||
.Fl T Ns Cm ascii .
|
||||
.It
|
||||
The
|
||||
.Sq \&Bd \-literal
|
||||
and
|
||||
.Sq \&Bd \-unfilled
|
||||
macros of
|
||||
.Xr mdoc 7
|
||||
in
|
||||
.Fl T Ns Cm ascii
|
||||
are synonyms, as are \-filled and \-ragged.
|
||||
.It
|
||||
In
|
||||
.Xr groff 1 ,
|
||||
the
|
||||
.Sq \&Pa
|
||||
.Xr mdoc 7
|
||||
macro does not underline when scoped under an
|
||||
.Sq \&It
|
||||
in the FILES section.
|
||||
This behaves correctly in
|
||||
.Nm .
|
||||
.It
|
||||
A list or display following the
|
||||
.Sq \&Ss
|
||||
.Xr mdoc 7
|
||||
macro in
|
||||
.Fl T Ns Cm ascii
|
||||
does not assert a prior vertical break, just as it doesn't with
|
||||
.Sq \&Sh .
|
||||
.It
|
||||
The
|
||||
.Sq \&na
|
||||
.Xr man 7
|
||||
macro in
|
||||
.Fl T Ns Cm ascii
|
||||
has no effect.
|
||||
.It
|
||||
Words aren't hyphenated.
|
||||
.It
|
||||
In normal mode (not a literal block), blocks of spaces aren't preserved,
|
||||
so double spaces following sentence closure are reduced to a single space;
|
||||
.Xr groff 1
|
||||
retains spaces.
|
||||
.It
|
||||
Sentences are unilaterally monospaced.
|
||||
.El
|
||||
.Ss HTML/XHTML Compatibility
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
The
|
||||
.Sq \efP
|
||||
escape will revert the font to the previous
|
||||
.Sq \ef
|
||||
escape, not to the last rendered decoration, which is now dictated by
|
||||
CSS instead of hard-coded.
|
||||
It also will not span past the current scope,
|
||||
for the same reason.
|
||||
Note that in
|
||||
.Sx ASCII Output
|
||||
mode, this will work fine.
|
||||
.It
|
||||
The
|
||||
.Xr mdoc 7
|
||||
.Sq \&Bl \-hang
|
||||
and
|
||||
.Sq \&Bl \-tag
|
||||
list types render similarly (no break following overreached left-hand
|
||||
side) due to the expressive constraints of HTML.
|
||||
.It
|
||||
The
|
||||
.Xr man 7
|
||||
.Sq IP
|
||||
and
|
||||
.Sq TP
|
||||
lists render similarly.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr man 7 ,
|
||||
.Xr mandoc_char 7 ,
|
||||
.Xr mdoc 7
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
utility was written by
|
||||
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
|
||||
.Sh CAVEATS
|
||||
The
|
||||
.Fl T Ns Cm html
|
||||
and
|
||||
.Fl T Ns Cm xhtml
|
||||
CSS2 styling used for
|
||||
.Fl m Ns Cm doc
|
||||
input lists does not render properly in older browsers, such as Internet
|
||||
Explorer 6 and earlier.
|
||||
.Pp
|
||||
In
|
||||
.Fl T Ns Cm html
|
||||
and
|
||||
.Fl T Ns Cm xhtml ,
|
||||
the maximum size of an element attribute is determined by
|
||||
.Dv BUFSIZ ,
|
||||
which is usually 1024 bytes.
|
||||
Be aware of this when setting long link
|
||||
formats such as
|
||||
.Fl O Ns Cm style Ns = Ns Ar really/long/link .
|
||||
.Pp
|
||||
The
|
||||
.Fl T Ns Cm html
|
||||
and
|
||||
.Fl T Ns Cm xhtml
|
||||
output modes don't render the
|
||||
.Sq \es
|
||||
font size escape documented in
|
||||
.Xr mdoc 7
|
||||
and
|
||||
.Xr man 7 .
|
||||
.Pp
|
||||
Nesting elements within next-line element scopes of
|
||||
.Fl m Ns Cm an ,
|
||||
such as
|
||||
.Sq br
|
||||
within an empty
|
||||
.Sq B ,
|
||||
will confuse
|
||||
.Fl T Ns Cm html
|
||||
and
|
||||
.Fl T Ns Cm xhtml
|
||||
and cause them to forget the formatting of the prior next-line scope.
|
||||
.Pp
|
||||
The
|
||||
.Sq i
|
||||
macro in
|
||||
.Fl m Ns Cm an
|
||||
should italicise all subsequent text if a line argument is not provided.
|
||||
This behaviour is not implemented.
|
||||
The
|
||||
.Sq \(aq
|
||||
control character is an alias for the standard macro control character
|
||||
and does not emit a line-break as stipulated in GNU troff.
|
396
commands/mdocml/mandoc.c
Normal file
396
commands/mdocml/mandoc.c
Normal file
|
@ -0,0 +1,396 @@
|
|||
/* $Id: mandoc.c,v 1.19 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmandoc.h"
|
||||
|
||||
static int a2time(time_t *, const char *, const char *);
|
||||
static int spec_norm(char *, int);
|
||||
|
||||
|
||||
/*
|
||||
* "Normalise" a special string by converting its ASCII_HYPH entries
|
||||
* into actual hyphens.
|
||||
*/
|
||||
static int
|
||||
spec_norm(char *p, int sz)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sz; i++)
|
||||
if (ASCII_HYPH == p[i])
|
||||
p[i] = '-';
|
||||
|
||||
return(sz);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mandoc_special(char *p)
|
||||
{
|
||||
int terminator; /* Terminator for \s. */
|
||||
int lim; /* Limit for N in \s. */
|
||||
int c, i;
|
||||
char *sv;
|
||||
|
||||
sv = p;
|
||||
|
||||
if ('\\' != *p++)
|
||||
return(spec_norm(sv, 0));
|
||||
|
||||
switch (*p) {
|
||||
case ('\''):
|
||||
/* FALLTHROUGH */
|
||||
case ('`'):
|
||||
/* FALLTHROUGH */
|
||||
case ('q'):
|
||||
/* FALLTHROUGH */
|
||||
case (ASCII_HYPH):
|
||||
/* FALLTHROUGH */
|
||||
case ('-'):
|
||||
/* FALLTHROUGH */
|
||||
case ('~'):
|
||||
/* FALLTHROUGH */
|
||||
case ('^'):
|
||||
/* FALLTHROUGH */
|
||||
case ('%'):
|
||||
/* FALLTHROUGH */
|
||||
case ('0'):
|
||||
/* FALLTHROUGH */
|
||||
case (' '):
|
||||
/* FALLTHROUGH */
|
||||
case ('}'):
|
||||
/* FALLTHROUGH */
|
||||
case ('|'):
|
||||
/* FALLTHROUGH */
|
||||
case ('&'):
|
||||
/* FALLTHROUGH */
|
||||
case ('.'):
|
||||
/* FALLTHROUGH */
|
||||
case (':'):
|
||||
/* FALLTHROUGH */
|
||||
case ('c'):
|
||||
/* FALLTHROUGH */
|
||||
case ('e'):
|
||||
return(spec_norm(sv, 2));
|
||||
case ('s'):
|
||||
if ('\0' == *++p)
|
||||
return(spec_norm(sv, 2));
|
||||
|
||||
c = 2;
|
||||
terminator = 0;
|
||||
lim = 1;
|
||||
|
||||
if (*p == '\'') {
|
||||
lim = 0;
|
||||
terminator = 1;
|
||||
++p;
|
||||
++c;
|
||||
} else if (*p == '[') {
|
||||
lim = 0;
|
||||
terminator = 2;
|
||||
++p;
|
||||
++c;
|
||||
} else if (*p == '(') {
|
||||
lim = 2;
|
||||
terminator = 3;
|
||||
++p;
|
||||
++c;
|
||||
}
|
||||
|
||||
if (*p == '+' || *p == '-') {
|
||||
++p;
|
||||
++c;
|
||||
}
|
||||
|
||||
if (*p == '\'') {
|
||||
if (terminator)
|
||||
return(spec_norm(sv, 0));
|
||||
lim = 0;
|
||||
terminator = 1;
|
||||
++p;
|
||||
++c;
|
||||
} else if (*p == '[') {
|
||||
if (terminator)
|
||||
return(spec_norm(sv, 0));
|
||||
lim = 0;
|
||||
terminator = 2;
|
||||
++p;
|
||||
++c;
|
||||
} else if (*p == '(') {
|
||||
if (terminator)
|
||||
return(spec_norm(sv, 0));
|
||||
lim = 2;
|
||||
terminator = 3;
|
||||
++p;
|
||||
++c;
|
||||
}
|
||||
|
||||
/* TODO: needs to handle floating point. */
|
||||
|
||||
if ( ! isdigit((u_char)*p))
|
||||
return(spec_norm(sv, 0));
|
||||
|
||||
for (i = 0; isdigit((u_char)*p); i++) {
|
||||
if (lim && i >= lim)
|
||||
break;
|
||||
++p;
|
||||
++c;
|
||||
}
|
||||
|
||||
if (terminator && terminator < 3) {
|
||||
if (1 == terminator && *p != '\'')
|
||||
return(spec_norm(sv, 0));
|
||||
if (2 == terminator && *p != ']')
|
||||
return(spec_norm(sv, 0));
|
||||
++p;
|
||||
++c;
|
||||
}
|
||||
|
||||
return(spec_norm(sv, c));
|
||||
case ('f'):
|
||||
/* FALLTHROUGH */
|
||||
case ('F'):
|
||||
/* FALLTHROUGH */
|
||||
case ('*'):
|
||||
if ('\0' == *++p || isspace((u_char)*p))
|
||||
return(spec_norm(sv, 0));
|
||||
switch (*p) {
|
||||
case ('('):
|
||||
if ('\0' == *++p || isspace((u_char)*p))
|
||||
return(spec_norm(sv, 0));
|
||||
return(spec_norm(sv, 4));
|
||||
case ('['):
|
||||
for (c = 3, p++; *p && ']' != *p; p++, c++)
|
||||
if (isspace((u_char)*p))
|
||||
break;
|
||||
return(spec_norm(sv, *p == ']' ? c : 0));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return(spec_norm(sv, 3));
|
||||
case ('('):
|
||||
if ('\0' == *++p || isspace((u_char)*p))
|
||||
return(spec_norm(sv, 0));
|
||||
if ('\0' == *++p || isspace((u_char)*p))
|
||||
return(spec_norm(sv, 0));
|
||||
return(spec_norm(sv, 4));
|
||||
case ('['):
|
||||
break;
|
||||
default:
|
||||
return(spec_norm(sv, 0));
|
||||
}
|
||||
|
||||
for (c = 3, p++; *p && ']' != *p; p++, c++)
|
||||
if (isspace((u_char)*p))
|
||||
break;
|
||||
|
||||
return(spec_norm(sv, *p == ']' ? c : 0));
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
mandoc_calloc(size_t num, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = calloc(num, size);
|
||||
if (NULL == ptr) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
mandoc_malloc(size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = malloc(size);
|
||||
if (NULL == ptr) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
mandoc_realloc(void *ptr, size_t size)
|
||||
{
|
||||
|
||||
ptr = realloc(ptr, size);
|
||||
if (NULL == ptr) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return(ptr);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
mandoc_strdup(const char *ptr)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = strdup(ptr);
|
||||
if (NULL == p) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return(p);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
a2time(time_t *t, const char *fmt, const char *p)
|
||||
{
|
||||
struct tm tm;
|
||||
char *pp;
|
||||
|
||||
memset(&tm, 0, sizeof(struct tm));
|
||||
|
||||
pp = strptime(p, fmt, &tm);
|
||||
if (NULL != pp && '\0' == *pp) {
|
||||
*t = mktime(&tm);
|
||||
return(1);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert from a manual date string (see mdoc(7) and man(7)) into a
|
||||
* date according to the stipulated date type.
|
||||
*/
|
||||
time_t
|
||||
mandoc_a2time(int flags, const char *p)
|
||||
{
|
||||
time_t t;
|
||||
|
||||
if (MTIME_MDOCDATE & flags) {
|
||||
if (0 == strcmp(p, "$" "Mdocdate$"))
|
||||
return(time(NULL));
|
||||
if (a2time(&t, "$" "Mdocdate: %b %d %Y $", p))
|
||||
return(t);
|
||||
}
|
||||
|
||||
if (MTIME_CANONICAL & flags || MTIME_REDUCED & flags)
|
||||
if (a2time(&t, "%b %d, %Y", p))
|
||||
return(t);
|
||||
|
||||
if (MTIME_ISO_8601 & flags)
|
||||
if (a2time(&t, "%Y-%m-%d", p))
|
||||
return(t);
|
||||
|
||||
if (MTIME_REDUCED & flags) {
|
||||
if (a2time(&t, "%d, %Y", p))
|
||||
return(t);
|
||||
if (a2time(&t, "%Y", p))
|
||||
return(t);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mandoc_eos(const char *p, size_t sz)
|
||||
{
|
||||
|
||||
if (0 == sz)
|
||||
return(0);
|
||||
|
||||
/*
|
||||
* End-of-sentence recognition must include situations where
|
||||
* some symbols, such as `)', allow prior EOS punctuation to
|
||||
* propogate outward.
|
||||
*/
|
||||
|
||||
for ( ; sz; sz--) {
|
||||
switch (p[(int)sz - 1]) {
|
||||
case ('\"'):
|
||||
/* FALLTHROUGH */
|
||||
case ('\''):
|
||||
/* FALLTHROUGH */
|
||||
case (']'):
|
||||
/* FALLTHROUGH */
|
||||
case (')'):
|
||||
break;
|
||||
case ('.'):
|
||||
/* Escaped periods. */
|
||||
if (sz > 1 && '\\' == p[(int)sz - 2])
|
||||
return(0);
|
||||
/* FALLTHROUGH */
|
||||
case ('!'):
|
||||
/* FALLTHROUGH */
|
||||
case ('?'):
|
||||
return(1);
|
||||
default:
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mandoc_hyph(const char *start, const char *c)
|
||||
{
|
||||
|
||||
/*
|
||||
* Choose whether to break at a hyphenated character. We only
|
||||
* do this if it's free-standing within a word.
|
||||
*/
|
||||
|
||||
/* Skip first/last character of buffer. */
|
||||
if (c == start || '\0' == *(c + 1))
|
||||
return(0);
|
||||
/* Skip first/last character of word. */
|
||||
if ('\t' == *(c + 1) || '\t' == *(c - 1))
|
||||
return(0);
|
||||
if (' ' == *(c + 1) || ' ' == *(c - 1))
|
||||
return(0);
|
||||
/* Skip double invocations. */
|
||||
if ('-' == *(c + 1) || '-' == *(c - 1))
|
||||
return(0);
|
||||
/* Skip escapes. */
|
||||
if ('\\' == *(c - 1))
|
||||
return(0);
|
||||
|
||||
return(1);
|
||||
}
|
110
commands/mdocml/mandoc.h
Normal file
110
commands/mdocml/mandoc.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/* $Id: mandoc.h,v 1.12 2010/06/12 11:41:50 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef MANDOC_H
|
||||
#define MANDOC_H
|
||||
|
||||
#define ASCII_NBRSP 31 /* non-breaking space */
|
||||
#define ASCII_HYPH 30 /* breakable hyphen */
|
||||
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
enum mandocerr {
|
||||
MANDOCERR_OK,
|
||||
MANDOCERR_UPPERCASE, /* text should be uppercase */
|
||||
MANDOCERR_SECOOO, /* sections out of conventional order */
|
||||
MANDOCERR_SECREP, /* section name repeats */
|
||||
MANDOCERR_PROLOGOOO, /* out of order prologue */
|
||||
MANDOCERR_PROLOGREP, /* repeated prologue entry */
|
||||
MANDOCERR_LISTFIRST, /* list type must come first */
|
||||
MANDOCERR_BADSTANDARD, /* bad standard */
|
||||
MANDOCERR_BADLIB, /* bad library */
|
||||
MANDOCERR_BADESCAPE, /* bad escape sequence */
|
||||
MANDOCERR_BADQUOTE, /* unterminated quoted string */
|
||||
MANDOCERR_NOWIDTHARG, /* argument requires the width argument */
|
||||
/* FIXME: merge with MANDOCERR_IGNARGV. */
|
||||
MANDOCERR_WIDTHARG, /* superfluous width argument */
|
||||
MANDOCERR_IGNARGV, /* macro ignoring argv */
|
||||
MANDOCERR_BADDATE, /* bad date argument */
|
||||
MANDOCERR_BADWIDTH, /* bad width argument */
|
||||
MANDOCERR_BADMSEC, /* unknown manual section */
|
||||
MANDOCERR_SECMSEC, /* section not in conventional manual section */
|
||||
MANDOCERR_EOLNSPACE, /* end of line whitespace */
|
||||
MANDOCERR_SCOPEEXIT, /* scope open on exit */
|
||||
#define MANDOCERR_WARNING MANDOCERR_SCOPEEXIT
|
||||
|
||||
MANDOCERR_NAMESECFIRST, /* NAME section must come first */
|
||||
MANDOCERR_BADBOOL, /* bad Boolean value */
|
||||
MANDOCERR_CHILD, /* child violates parent syntax */
|
||||
MANDOCERR_BADATT, /* bad AT&T symbol */
|
||||
MANDOCERR_LISTREP, /* list type repeated */
|
||||
MANDOCERR_DISPREP, /* display type repeated */
|
||||
MANDOCERR_ARGVREP, /* argument repeated */
|
||||
MANDOCERR_NONAME, /* manual name not yet set */
|
||||
MANDOCERR_MACROOBS, /* obsolete macro ignored */
|
||||
MANDOCERR_MACROEMPTY, /* empty macro ignored */
|
||||
MANDOCERR_BADBODY, /* macro not allowed in body */
|
||||
MANDOCERR_BADPROLOG, /* macro not allowed in prologue */
|
||||
MANDOCERR_BADCHAR, /* bad character */
|
||||
MANDOCERR_BADNAMESEC, /* bad NAME section contents */
|
||||
MANDOCERR_NOBLANKLN, /* no blank lines */
|
||||
MANDOCERR_NOTEXT, /* no text in this context */
|
||||
MANDOCERR_BADCOMMENT, /* bad comment style */
|
||||
MANDOCERR_MACRO, /* unknown macro will be lost */
|
||||
MANDOCERR_LINESCOPE, /* line scope broken */
|
||||
MANDOCERR_SCOPE, /* scope broken */
|
||||
MANDOCERR_ARGCOUNT, /* argument count wrong */
|
||||
MANDOCERR_NOSCOPE, /* request scope close w/none open */
|
||||
MANDOCERR_SCOPEREP, /* scope already open */
|
||||
/* FIXME: merge following with MANDOCERR_ARGCOUNT */
|
||||
MANDOCERR_NOARGS, /* macro requires line argument(s) */
|
||||
MANDOCERR_NOBODY, /* macro requires body argument(s) */
|
||||
MANDOCERR_NOARGV, /* macro requires argument(s) */
|
||||
MANDOCERR_NOTITLE, /* no title in document */
|
||||
MANDOCERR_LISTTYPE, /* missing list type */
|
||||
MANDOCERR_DISPTYPE, /* missing display type */
|
||||
MANDOCERR_ARGSLOST, /* line argument(s) will be lost */
|
||||
MANDOCERR_BODYLOST, /* body argument(s) will be lost */
|
||||
#define MANDOCERR_ERROR MANDOCERR_BODYLOST
|
||||
|
||||
MANDOCERR_COLUMNS, /* column syntax is inconsistent */
|
||||
/* FIXME: this should be a MANDOCERR_ERROR */
|
||||
MANDOCERR_FONTTYPE, /* missing font type */
|
||||
/* FIXME: this should be a MANDOCERR_ERROR */
|
||||
MANDOCERR_NESTEDDISP, /* displays may not be nested */
|
||||
MANDOCERR_BADDISP, /* unsupported display type */
|
||||
MANDOCERR_SYNTNOSCOPE, /* request scope close w/none open */
|
||||
MANDOCERR_SYNTSCOPE, /* scope broken, syntax violated */
|
||||
MANDOCERR_SYNTLINESCOPE, /* line scope broken, syntax violated */
|
||||
MANDOCERR_SYNTARGVCOUNT, /* argument count wrong, violates syntax */
|
||||
MANDOCERR_SYNTCHILD, /* child violates parent syntax */
|
||||
MANDOCERR_SYNTARGCOUNT, /* argument count wrong, violates syntax */
|
||||
MANDOCERR_NODOCBODY, /* no document body */
|
||||
MANDOCERR_NODOCPROLOG, /* no document prologue */
|
||||
MANDOCERR_UTSNAME, /* utsname() system call failed */
|
||||
MANDOCERR_MEM, /* memory exhausted */
|
||||
#define MANDOCERR_FATAL MANDOCERR_MEM
|
||||
|
||||
MANDOCERR_MAX
|
||||
};
|
||||
|
||||
typedef int (*mandocmsg)(enum mandocerr,
|
||||
void *, int, int, const char *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!MANDOC_H*/
|
559
commands/mdocml/mandoc_char.7
Normal file
559
commands/mdocml/mandoc_char.7
Normal file
|
@ -0,0 +1,559 @@
|
|||
.\" $Id: mandoc_char.7,v 1.39 2010/05/12 08:29:23 kristaps Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: May 12 2010 $
|
||||
.Dt MANDOC_CHAR 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mandoc_char
|
||||
.Nd mandoc special characters
|
||||
.Sh DESCRIPTION
|
||||
This page documents the special characters and predefined strings accepted by
|
||||
.Xr mandoc 1
|
||||
to format
|
||||
.Xr mdoc 7
|
||||
and
|
||||
.Xr man 7
|
||||
documents.
|
||||
.Pp
|
||||
Both
|
||||
.Xr mdoc 7
|
||||
and
|
||||
.Xr man 7
|
||||
encode special characters with
|
||||
.Sq \eX
|
||||
.Pq for a one-character escape ,
|
||||
.Sq \e(XX
|
||||
.Pq two-character ,
|
||||
and
|
||||
.Sq \e[N]
|
||||
.Pq N-character .
|
||||
One may generalise
|
||||
.Sq \e(XX
|
||||
as
|
||||
.Sq \e[XX]
|
||||
and
|
||||
.Sq \eX
|
||||
as
|
||||
.Sq \e[X] .
|
||||
Predefined strings are functionally similar to special characters, using
|
||||
.Sq \e*X
|
||||
.Pq for a one-character escape ,
|
||||
.Sq \e*(XX
|
||||
.Pq two-character ,
|
||||
and
|
||||
.Sq \e*[N]
|
||||
.Pq N-character .
|
||||
One may generalise
|
||||
.Sq \e*(XX
|
||||
as
|
||||
.Sq \e*[XX]
|
||||
and
|
||||
.Sq \e*X
|
||||
as
|
||||
.Sq \e*[X] .
|
||||
.Pp
|
||||
Note that each output mode will have a different rendering of the
|
||||
characters.
|
||||
It's guaranteed that each input symbol will correspond to a
|
||||
(more or less) meaningful output rendering, regardless the mode.
|
||||
.Sh SPECIAL CHARACTERS
|
||||
These are the preferred input symbols for producing special characters.
|
||||
.Pp
|
||||
Spacing:
|
||||
.Bl -column -compact -offset indent "Input" "Description"
|
||||
.It Em Input Ta Em Description
|
||||
.It \e~ Ta non-breaking, non-collapsing space
|
||||
.It \e Ta breaking, non-collapsing n-width space
|
||||
.It \e^ Ta zero-width space
|
||||
.It \e% Ta zero-width space
|
||||
.It \e& Ta zero-width space
|
||||
.It \e| Ta zero-width space
|
||||
.It \e0 Ta breaking, non-collapsing digit-width space
|
||||
.It \ec Ta removes any trailing space (if applicable)
|
||||
.El
|
||||
.Pp
|
||||
Lines:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(ba Ta \(ba Ta bar
|
||||
.It \e(br Ta \(br Ta box rule
|
||||
.It \e(ul Ta \(ul Ta underscore
|
||||
.It \e(rl Ta \(rl Ta overline
|
||||
.It \e(bb Ta \(bb Ta broken bar
|
||||
.It \e(sl Ta \(sl Ta forward slash
|
||||
.It \e(rs Ta \(rs Ta backward slash
|
||||
.El
|
||||
.Pp
|
||||
Text markers:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(ci Ta \(ci Ta circle
|
||||
.It \e(bu Ta \(bu Ta bullet
|
||||
.It \e(dd Ta \(dd Ta double dagger
|
||||
.It \e(dg Ta \(dg Ta dagger
|
||||
.It \e(lz Ta \(lz Ta lozenge
|
||||
.It \e(sq Ta \(sq Ta white square
|
||||
.It \e(ps Ta \(ps Ta paragraph
|
||||
.It \e(sc Ta \(sc Ta section
|
||||
.It \e(lh Ta \(lh Ta left hand
|
||||
.It \e(rh Ta \(rh Ta right hand
|
||||
.It \e(at Ta \(at Ta at
|
||||
.It \e(sh Ta \(sh Ta hash (pound)
|
||||
.It \e(CR Ta \(CR Ta carriage return
|
||||
.It \e(OK Ta \(OK Ta check mark
|
||||
.El
|
||||
.Pp
|
||||
Legal symbols:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(co Ta \(co Ta copyright
|
||||
.It \e(rg Ta \(rg Ta registered
|
||||
.It \e(tm Ta \(tm Ta trademarked
|
||||
.El
|
||||
.Pp
|
||||
Punctuation:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(em Ta \(em Ta em-dash
|
||||
.It \e(en Ta \(en Ta en-dash
|
||||
.It \e(hy Ta \(hy Ta hyphen
|
||||
.It \ee Ta \e Ta back-slash
|
||||
.It \e. Ta \. Ta period
|
||||
.It \e(r! Ta \(r! Ta upside-down exclamation
|
||||
.It \e(r? Ta \(r? Ta upside-down question
|
||||
.El
|
||||
.Pp
|
||||
Quotes:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(Bq Ta \(Bq Ta right low double-quote
|
||||
.It \e(bq Ta \(bq Ta right low single-quote
|
||||
.It \e(lq Ta \(lq Ta left double-quote
|
||||
.It \e(rq Ta \(rq Ta right double-quote
|
||||
.It \e(oq Ta \(oq Ta left single-quote
|
||||
.It \e(cq Ta \(cq Ta right single-quote
|
||||
.It \e(aq Ta \(aq Ta apostrophe quote (text)
|
||||
.It \e(dq Ta \(dq Ta double quote (text)
|
||||
.It \e(Fo Ta \(Fo Ta left guillemet
|
||||
.It \e(Fc Ta \(Fc Ta right guillemet
|
||||
.It \e(fo Ta \(fo Ta left single guillemet
|
||||
.It \e(fc Ta \(fc Ta right single guillemet
|
||||
.El
|
||||
.Pp
|
||||
Brackets:
|
||||
.Bl -column -compact -offset indent "xxbracketrightbpx" Rendered Description
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(lB Ta \(lB Ta left bracket
|
||||
.It \e(rB Ta \(rB Ta right bracket
|
||||
.It \e(lC Ta \(lC Ta left brace
|
||||
.It \e(rC Ta \(rC Ta right brace
|
||||
.It \e(la Ta \(la Ta left angle
|
||||
.It \e(ra Ta \(ra Ta right angle
|
||||
.It \e(bv Ta \(bv Ta brace extension
|
||||
.It \e[braceex] Ta \[braceex] Ta brace extension
|
||||
.It \e[bracketlefttp] Ta \[bracketlefttp] Ta top-left hooked bracket
|
||||
.It \e[bracketleftbp] Ta \[bracketleftbp] Ta bottom-left hooked bracket
|
||||
.It \e[bracketleftex] Ta \[bracketleftex] Ta left hooked bracket extension
|
||||
.It \e[bracketrighttp] Ta \[bracketrighttp] Ta top-right hooked bracket
|
||||
.It \e[bracketrightbp] Ta \[bracketrightbp] Ta bottom-right hooked bracket
|
||||
.It \e[bracketrightex] Ta \[bracketrightex] Ta right hooked bracket extension
|
||||
.It \e(lt Ta \(lt Ta top-left hooked brace
|
||||
.It \e[bracelefttp] Ta \[bracelefttp] Ta top-left hooked brace
|
||||
.It \e(lk Ta \(lk Ta mid-left hooked brace
|
||||
.It \e[braceleftmid] Ta \[braceleftmid] Ta mid-left hooked brace
|
||||
.It \e(lb Ta \(lb Ta bottom-left hooked brace
|
||||
.It \e[braceleftbp] Ta \[braceleftbp] Ta bottom-left hooked brace
|
||||
.It \e[braceleftex] Ta \[braceleftex] Ta left hooked brace extension
|
||||
.It \e(rt Ta \(rt Ta top-left hooked brace
|
||||
.It \e[bracerighttp] Ta \[bracerighttp] Ta top-right hooked brace
|
||||
.It \e(rk Ta \(rk Ta mid-right hooked brace
|
||||
.It \e[bracerightmid] Ta \[bracerightmid] Ta mid-right hooked brace
|
||||
.It \e(rb Ta \(rb Ta bottom-right hooked brace
|
||||
.It \e[bracerightbp] Ta \[bracerightbp] Ta bottom-right hooked brace
|
||||
.It \e[bracerightex] Ta \[bracerightex] Ta right hooked brace extension
|
||||
.It \e[parenlefttp] Ta \[parenlefttp] Ta top-left hooked parenthesis
|
||||
.It \e[parenleftbp] Ta \[parenleftbp] Ta bottom-left hooked parenthesis
|
||||
.It \e[parenleftex] Ta \[parenleftex] Ta left hooked parenthesis extension
|
||||
.It \e[parenrighttp] Ta \[parenrighttp] Ta top-right hooked parenthesis
|
||||
.It \e[parenrightbp] Ta \[parenrightbp] Ta bottom-right hooked parenthesis
|
||||
.It \e[parenrightex] Ta \[parenrightex] Ta right hooked parenthesis extension
|
||||
.El
|
||||
.Pp
|
||||
Arrows:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(<- Ta \(<- Ta left arrow
|
||||
.It \e(-> Ta \(-> Ta right arrow
|
||||
.It \e(<> Ta \(<> Ta left-right arrow
|
||||
.It \e(da Ta \(da Ta down arrow
|
||||
.It \e(ua Ta \(ua Ta up arrow
|
||||
.It \e(va Ta \(va Ta up-down arrow
|
||||
.It \e(lA Ta \(lA Ta left double-arrow
|
||||
.It \e(rA Ta \(rA Ta right double-arrow
|
||||
.It \e(hA Ta \(hA Ta left-right double-arrow
|
||||
.It \e(uA Ta \(uA Ta up double-arrow
|
||||
.It \e(dA Ta \(dA Ta down double-arrow
|
||||
.It \e(vA Ta \(vA Ta up-down double-arrow
|
||||
.El
|
||||
.Pp
|
||||
Logical:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(AN Ta \(AN Ta logical and
|
||||
.It \e(OR Ta \(OR Ta logical or
|
||||
.It \e(no Ta \(no Ta logical not
|
||||
.It \e[tno] Ta \[tno] Ta logical not (text)
|
||||
.It \e(te Ta \(te Ta existential quantifier
|
||||
.It \e(fa Ta \(fa Ta universal quantifier
|
||||
.It \e(st Ta \(st Ta such that
|
||||
.It \e(tf Ta \(tf Ta therefore
|
||||
.It \e(3d Ta \(3d Ta therefore
|
||||
.It \e(or Ta \(or Ta bitwise or
|
||||
.El
|
||||
.Pp
|
||||
Mathematical:
|
||||
.Bl -column -compact -offset indent "xxcoproductxx" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(pl Ta \(pl Ta plus
|
||||
.It \e(mi Ta \(mi Ta minus
|
||||
.It \e- Ta \- Ta minus (text)
|
||||
.It \e(-+ Ta \(-+ Ta minus-plus
|
||||
.It \e(+- Ta \(+- Ta plus-minus
|
||||
.It \e[t+-] Ta \[t+-] Ta plus-minus (text)
|
||||
.It \e(pc Ta \(pc Ta centre-dot
|
||||
.It \e(mu Ta \(mu Ta multiply
|
||||
.It \e[tmu] Ta \[tmu] Ta multiply (text)
|
||||
.It \e(c* Ta \(c* Ta circle-multiply
|
||||
.It \e(c+ Ta \(c+ Ta circle-plus
|
||||
.It \e(di Ta \(di Ta divide
|
||||
.It \e[tdi] Ta \[tdi] Ta divide (text)
|
||||
.It \e(f/ Ta \(f/ Ta fraction
|
||||
.It \e(** Ta \(** Ta asterisk
|
||||
.It \e(<= Ta \(<= Ta less-than-equal
|
||||
.It \e(>= Ta \(>= Ta greater-than-equal
|
||||
.It \e(<< Ta \(<< Ta much less
|
||||
.It \e(>> Ta \(>> Ta much greater
|
||||
.It \e(eq Ta \(eq Ta equal
|
||||
.It \e(!= Ta \(!= Ta not equal
|
||||
.It \e(== Ta \(== Ta equivalent
|
||||
.It \e(ne Ta \(ne Ta not equivalent
|
||||
.It \e(=~ Ta \(=~ Ta congruent
|
||||
.It \e(-~ Ta \(-~ Ta asymptotically congruent
|
||||
.It \e(ap Ta \(ap Ta asymptotically similar
|
||||
.It \e(~~ Ta \(~~ Ta approximately similar
|
||||
.It \e(~= Ta \(~= Ta approximately equal
|
||||
.It \e(pt Ta \(pt Ta proportionate
|
||||
.It \e(es Ta \(es Ta empty set
|
||||
.It \e(mo Ta \(mo Ta element
|
||||
.It \e(nm Ta \(nm Ta not element
|
||||
.It \e(sb Ta \(sb Ta proper subset
|
||||
.It \e(nb Ta \(nb Ta not subset
|
||||
.It \e(sp Ta \(sp Ta proper superset
|
||||
.It \e(nc Ta \(nc Ta not superset
|
||||
.It \e(ib Ta \(ib Ta reflexive subset
|
||||
.It \e(ip Ta \(ip Ta reflexive superset
|
||||
.It \e(ca Ta \(ca Ta intersection
|
||||
.It \e(cu Ta \(cu Ta union
|
||||
.It \e(/_ Ta \(/_ Ta angle
|
||||
.It \e(pp Ta \(pp Ta perpendicular
|
||||
.It \e(is Ta \(is Ta integral
|
||||
.It \e[integral] Ta \[integral] Ta integral
|
||||
.It \e[sum] Ta \[sum] Ta summation
|
||||
.It \e[product] Ta \[product] Ta product
|
||||
.It \e[coproduct] Ta \[coproduct] Ta coproduct
|
||||
.It \e(gr Ta \(gr Ta gradient
|
||||
.It \e(sr Ta \(sr Ta square root
|
||||
.It \e[sqrt] Ta \[sqrt] Ta square root
|
||||
.It \e(lc Ta \(lc Ta left-ceiling
|
||||
.It \e(rc Ta \(rc Ta right-ceiling
|
||||
.It \e(lf Ta \(lf Ta left-floor
|
||||
.It \e(rf Ta \(rf Ta right-floor
|
||||
.It \e(if Ta \(if Ta infinity
|
||||
.It \e(Ah Ta \(Ah Ta aleph
|
||||
.It \e(Im Ta \(Im Ta imaginary
|
||||
.It \e(Re Ta \(Re Ta real
|
||||
.It \e(pd Ta \(pd Ta partial differential
|
||||
.It \e(-h Ta \(-h Ta Planck constant over 2\(*p
|
||||
.El
|
||||
.Pp
|
||||
Ligatures:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(ff Ta \(ff Ta ff ligature
|
||||
.It \e(fi Ta \(fi Ta fi ligature
|
||||
.It \e(fl Ta \(fl Ta fl ligature
|
||||
.It \e(Fi Ta \(Fi Ta ffi ligature
|
||||
.It \e(Fl Ta \(Fl Ta ffl ligature
|
||||
.It \e(AE Ta \(AE Ta AE
|
||||
.It \e(ae Ta \(ae Ta ae
|
||||
.It \e(OE Ta \(OE Ta OE
|
||||
.It \e(oe Ta \(oe Ta oe
|
||||
.It \e(ss Ta \(ss Ta German eszett
|
||||
.It \e(IJ Ta \(IJ Ta IJ ligature
|
||||
.It \e(ij Ta \(ij Ta ij ligature
|
||||
.El
|
||||
.Pp
|
||||
Accents:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(a" Ta \(a" Ta Hungarian umlaut
|
||||
.It \e(a- Ta \(a- Ta macron
|
||||
.It \e(a. Ta \(a. Ta dotted
|
||||
.It \e(a^ Ta \(a^ Ta circumflex
|
||||
.It \e(aa Ta \(aa Ta acute
|
||||
.It \e' Ta \' Ta acute
|
||||
.It \e(ga Ta \(ga Ta grave
|
||||
.It \e` Ta \` Ta grave
|
||||
.It \e(ab Ta \(ab Ta breve
|
||||
.It \e(ac Ta \(ac Ta cedilla
|
||||
.It \e(ad Ta \(ad Ta dieresis
|
||||
.It \e(ah Ta \(ah Ta caron
|
||||
.It \e(ao Ta \(ao Ta ring
|
||||
.It \e(a~ Ta \(a~ Ta tilde
|
||||
.It \e(ho Ta \(ho Ta ogonek
|
||||
.It \e(ha Ta \(ha Ta hat (text)
|
||||
.It \e(ti Ta \(ti Ta tilde (text)
|
||||
.El
|
||||
.Pp
|
||||
Accented letters:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e('A Ta \('A Ta acute A
|
||||
.It \e('E Ta \('E Ta acute E
|
||||
.It \e('I Ta \('I Ta acute I
|
||||
.It \e('O Ta \('O Ta acute O
|
||||
.It \e('U Ta \('U Ta acute U
|
||||
.It \e('a Ta \('a Ta acute a
|
||||
.It \e('e Ta \('e Ta acute e
|
||||
.It \e('i Ta \('i Ta acute i
|
||||
.It \e('o Ta \('o Ta acute o
|
||||
.It \e('u Ta \('u Ta acute u
|
||||
.It \e(`A Ta \(`A Ta grave A
|
||||
.It \e(`E Ta \(`E Ta grave E
|
||||
.It \e(`I Ta \(`I Ta grave I
|
||||
.It \e(`O Ta \(`O Ta grave O
|
||||
.It \e(`U Ta \(`U Ta grave U
|
||||
.It \e(`a Ta \(`a Ta grave a
|
||||
.It \e(`e Ta \(`e Ta grave e
|
||||
.It \e(`i Ta \(`i Ta grave i
|
||||
.It \e(`o Ta \(`i Ta grave o
|
||||
.It \e(`u Ta \(`u Ta grave u
|
||||
.It \e(~A Ta \(~A Ta tilde A
|
||||
.It \e(~N Ta \(~N Ta tilde N
|
||||
.It \e(~O Ta \(~O Ta tilde O
|
||||
.It \e(~a Ta \(~a Ta tilde a
|
||||
.It \e(~n Ta \(~n Ta tilde n
|
||||
.It \e(~o Ta \(~o Ta tilde o
|
||||
.It \e(:A Ta \(:A Ta dieresis A
|
||||
.It \e(:E Ta \(:E Ta dieresis E
|
||||
.It \e(:I Ta \(:I Ta dieresis I
|
||||
.It \e(:O Ta \(:O Ta dieresis O
|
||||
.It \e(:U Ta \(:U Ta dieresis U
|
||||
.It \e(:a Ta \(:a Ta dieresis a
|
||||
.It \e(:e Ta \(:e Ta dieresis e
|
||||
.It \e(:i Ta \(:i Ta dieresis i
|
||||
.It \e(:o Ta \(:o Ta dieresis o
|
||||
.It \e(:u Ta \(:u Ta dieresis u
|
||||
.It \e(:y Ta \(:y Ta dieresis y
|
||||
.It \e(^A Ta \(^A Ta circumflex A
|
||||
.It \e(^E Ta \(^E Ta circumflex E
|
||||
.It \e(^I Ta \(^I Ta circumflex I
|
||||
.It \e(^O Ta \(^O Ta circumflex O
|
||||
.It \e(^U Ta \(^U Ta circumflex U
|
||||
.It \e(^a Ta \(^a Ta circumflex a
|
||||
.It \e(^e Ta \(^e Ta circumflex e
|
||||
.It \e(^i Ta \(^i Ta circumflex i
|
||||
.It \e(^o Ta \(^o Ta circumflex o
|
||||
.It \e(^u Ta \(^u Ta circumflex u
|
||||
.It \e(,C Ta \(,C Ta cedilla C
|
||||
.It \e(,c Ta \(,c Ta cedilla c
|
||||
.It \e(/L Ta \(/L Ta stroke L
|
||||
.It \e(/l Ta \(/l Ta stroke l
|
||||
.It \e(/O Ta \(/O Ta stroke O
|
||||
.It \e(/o Ta \(/o Ta stroke o
|
||||
.It \e(oA Ta \(oA Ta ring A
|
||||
.It \e(oa Ta \(oa Ta ring a
|
||||
.El
|
||||
.Pp
|
||||
Special letters:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(-D Ta \(-D Ta Eth
|
||||
.It \e(Sd Ta \(Sd Ta eth
|
||||
.It \e(TP Ta \(TP Ta Thorn
|
||||
.It \e(Tp Ta \(Tp Ta thorn
|
||||
.It \e(.i Ta \(.i Ta dotless i
|
||||
.It \e(.j Ta \(.j Ta dotless j
|
||||
.El
|
||||
.Pp
|
||||
Currency:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(Do Ta \(Do Ta dollar
|
||||
.It \e(ct Ta \(ct Ta cent
|
||||
.It \e(Eu Ta \(Eu Ta Euro symbol
|
||||
.It \e(eu Ta \(eu Ta Euro symbol
|
||||
.It \e(Ye Ta \(Ye Ta yen
|
||||
.It \e(Po Ta \(Po Ta pound
|
||||
.It \e(Cs Ta \(Cs Ta Scandinavian
|
||||
.It \e(Fn Ta \(Fn Ta florin
|
||||
.El
|
||||
.Pp
|
||||
Units:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(de Ta \(de Ta degree
|
||||
.It \e(%0 Ta \(%0 Ta per-thousand
|
||||
.It \e(fm Ta \(fm Ta minute
|
||||
.It \e(sd Ta \(sd Ta second
|
||||
.It \e(mc Ta \(mc Ta micro
|
||||
.El
|
||||
.Pp
|
||||
Greek letters:
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e(*A Ta \(*A Ta Alpha
|
||||
.It \e(*B Ta \(*B Ta Beta
|
||||
.It \e(*G Ta \(*G Ta Gamma
|
||||
.It \e(*D Ta \(*D Ta Delta
|
||||
.It \e(*E Ta \(*E Ta Epsilon
|
||||
.It \e(*Z Ta \(*Z Ta Zeta
|
||||
.It \e(*Y Ta \(*Y Ta Eta
|
||||
.It \e(*H Ta \(*H Ta Theta
|
||||
.It \e(*I Ta \(*I Ta Iota
|
||||
.It \e(*K Ta \(*K Ta Kappa
|
||||
.It \e(*L Ta \(*L Ta Lambda
|
||||
.It \e(*M Ta \(*M Ta Mu
|
||||
.It \e(*N Ta \(*N Ta Nu
|
||||
.It \e(*C Ta \(*C Ta Xi
|
||||
.It \e(*O Ta \(*O Ta Omicron
|
||||
.It \e(*P Ta \(*P Ta Pi
|
||||
.It \e(*R Ta \(*R Ta Rho
|
||||
.It \e(*S Ta \(*S Ta Sigma
|
||||
.It \e(*T Ta \(*T Ta Tau
|
||||
.It \e(*U Ta \(*U Ta Upsilon
|
||||
.It \e(*F Ta \(*F Ta Phi
|
||||
.It \e(*X Ta \(*X Ta Chi
|
||||
.It \e(*Q Ta \(*Q Ta Psi
|
||||
.It \e(*W Ta \(*W Ta Omega
|
||||
.It \e(*a Ta \(*a Ta alpha
|
||||
.It \e(*b Ta \(*b Ta beta
|
||||
.It \e(*g Ta \(*g Ta gamma
|
||||
.It \e(*d Ta \(*d Ta delta
|
||||
.It \e(*e Ta \(*e Ta epsilon
|
||||
.It \e(*z Ta \(*z Ta zeta
|
||||
.It \e(*y Ta \(*y Ta eta
|
||||
.It \e(*h Ta \(*h Ta theta
|
||||
.It \e(*i Ta \(*i Ta iota
|
||||
.It \e(*k Ta \(*k Ta kappa
|
||||
.It \e(*l Ta \(*l Ta lambda
|
||||
.It \e(*m Ta \(*m Ta mu
|
||||
.It \e(*n Ta \(*n Ta nu
|
||||
.It \e(*c Ta \(*c Ta xi
|
||||
.It \e(*o Ta \(*o Ta omicron
|
||||
.It \e(*p Ta \(*p Ta pi
|
||||
.It \e(*r Ta \(*r Ta rho
|
||||
.It \e(*s Ta \(*s Ta sigma
|
||||
.It \e(*t Ta \(*t Ta tau
|
||||
.It \e(*u Ta \(*u Ta upsilon
|
||||
.It \e(*f Ta \(*f Ta phi
|
||||
.It \e(*x Ta \(*x Ta chi
|
||||
.It \e(*q Ta \(*q Ta psi
|
||||
.It \e(*w Ta \(*w Ta omega
|
||||
.It \e(+h Ta \(+h Ta theta variant
|
||||
.It \e(+f Ta \(+f Ta phi variant
|
||||
.It \e(+p Ta \(+p Ta pi variant
|
||||
.It \e(+e Ta \(+e Ta epsilon variant
|
||||
.It \e(ts Ta \(ts Ta sigma terminal
|
||||
.El
|
||||
.Sh PREDEFINED STRINGS
|
||||
These are not recommended for use, as they differ across
|
||||
implementations:
|
||||
.Pp
|
||||
.Bl -column -compact -offset indent "Input" "Rendered" "Description"
|
||||
.It Em Input Ta Em Rendered Ta Em Description
|
||||
.It \e*(Ba Ta \*(Ba Ta vertical bar
|
||||
.It \e*(Ne Ta \*(Ne Ta not equal
|
||||
.It \e*(Ge Ta \*(Ge Ta greater-than-equal
|
||||
.It \e*(Le Ta \*(Le Ta less-than-equal
|
||||
.It \e*(Gt Ta \*(Gt Ta greater-than
|
||||
.It \e*(Lt Ta \*(Lt Ta less-than
|
||||
.It \e*(Pm Ta \*(Pm Ta plus-minus
|
||||
.It \e*(If Ta \*(If Ta infinity
|
||||
.It \e*(Pi Ta \*(Pi Ta pi
|
||||
.It \e*(Na Ta \*(Na Ta NaN
|
||||
.It \e*(Am Ta \*(Am Ta ampersand
|
||||
.It \e*R Ta \*R Ta restricted mark
|
||||
.It \e*(Tm Ta \*(Tm Ta trade mark
|
||||
.It \e*q Ta \*q Ta double-quote
|
||||
.It \e*(Rq Ta \*(Rq Ta right-double-quote
|
||||
.It \e*(Lq Ta \*(Lq Ta left-double-quote
|
||||
.It \e*(lp Ta \*(lp Ta right-parenthesis
|
||||
.It \e*(rp Ta \*(rp Ta left-parenthesis
|
||||
.It \e*(lq Ta \*(lq Ta left double-quote
|
||||
.It \e*(rq Ta \*(rq Ta right double-quote
|
||||
.It \e*(ua Ta \*(ua Ta up arrow
|
||||
.It \e*(va Ta \*(va Ta up-down arrow
|
||||
.It \e*(<= Ta \*(<= Ta less-than-equal
|
||||
.It \e*(>= Ta \*(>= Ta greater-than-equal
|
||||
.It \e*(aa Ta \*(aa Ta acute
|
||||
.It \e*(ga Ta \*(ga Ta grave
|
||||
.El
|
||||
.Sh COMPATIBILITY
|
||||
This section documents compatibility of
|
||||
.Nm
|
||||
with older or existing versions of
|
||||
.Xr groff 1 .
|
||||
.Pp
|
||||
The following render differently in
|
||||
.Fl T Ns Ar ascii
|
||||
output mode:
|
||||
.Bd -ragged -offset indent
|
||||
\e(ss, \e(nm, \e(nb, \e(nc, \e(ib, \e(ip, \e(pp, \e[sum], \e[product],
|
||||
\e[coproduct], \e(gr, \e(-h, \e(a.
|
||||
.Ed
|
||||
.Pp
|
||||
The following render differently in
|
||||
.Fl T Ns Ar html
|
||||
output mode:
|
||||
.Bd -ragged -offset indent
|
||||
\e(~=, \e(nb, \e(nc
|
||||
.Ed
|
||||
.Pp
|
||||
Finally, the following have been omitted by being poorly documented or
|
||||
having no known representation:
|
||||
.Bd -ragged -offset indent
|
||||
\e[radicalex], \e[sqrtex], \e(ru
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr mandoc 1
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
manual page was written by
|
||||
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
|
||||
.Sh CAVEATS
|
||||
The
|
||||
.Sq \e*(Ba
|
||||
escape mimics the behaviour of the
|
||||
.Sq \&|
|
||||
character in
|
||||
.Xr mdoc 7 ;
|
||||
thus, if you wish to render a vertical bar with no side effects, use
|
||||
the
|
||||
.Sq \e(ba
|
||||
escape.
|
290
commands/mdocml/mdoc.3
Normal file
290
commands/mdocml/mdoc.3
Normal file
|
@ -0,0 +1,290 @@
|
|||
.\" $Id: mdoc.3,v 1.41 2010/05/30 22:56:02 kristaps Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2009-2010 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: May 30 2010 $
|
||||
.Dt MDOC 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mdoc ,
|
||||
.Nm mdoc_alloc ,
|
||||
.Nm mdoc_endparse ,
|
||||
.Nm mdoc_free ,
|
||||
.Nm mdoc_meta ,
|
||||
.Nm mdoc_node ,
|
||||
.Nm mdoc_parseln ,
|
||||
.Nm mdoc_reset
|
||||
.Nd mdoc macro compiler library
|
||||
.Sh SYNOPSIS
|
||||
.In mandoc.h
|
||||
.In mdoc.h
|
||||
.Vt extern const char * const * mdoc_macronames;
|
||||
.Vt extern const char * const * mdoc_argnames;
|
||||
.Ft "struct mdoc *"
|
||||
.Fn mdoc_alloc "void *data" "int pflags" "mandocmsg msgs"
|
||||
.Ft int
|
||||
.Fn mdoc_endparse "struct mdoc *mdoc"
|
||||
.Ft void
|
||||
.Fn mdoc_free "struct mdoc *mdoc"
|
||||
.Ft "const struct mdoc_meta *"
|
||||
.Fn mdoc_meta "const struct mdoc *mdoc"
|
||||
.Ft "const struct mdoc_node *"
|
||||
.Fn mdoc_node "const struct mdoc *mdoc"
|
||||
.Ft int
|
||||
.Fn mdoc_parseln "struct mdoc *mdoc" "int line" "char *buf"
|
||||
.Ft int
|
||||
.Fn mdoc_reset "struct mdoc *mdoc"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm mdoc
|
||||
library parses lines of
|
||||
.Xr mdoc 7
|
||||
input
|
||||
into an abstract syntax tree (AST).
|
||||
.Pp
|
||||
In general, applications initiate a parsing sequence with
|
||||
.Fn mdoc_alloc ,
|
||||
parse each line in a document with
|
||||
.Fn mdoc_parseln ,
|
||||
close the parsing session with
|
||||
.Fn mdoc_endparse ,
|
||||
operate over the syntax tree returned by
|
||||
.Fn mdoc_node
|
||||
and
|
||||
.Fn mdoc_meta ,
|
||||
then free all allocated memory with
|
||||
.Fn mdoc_free .
|
||||
The
|
||||
.Fn mdoc_reset
|
||||
function may be used in order to reset the parser for another input
|
||||
sequence.
|
||||
See the
|
||||
.Sx EXAMPLES
|
||||
section for a simple example.
|
||||
.Pp
|
||||
This section further defines the
|
||||
.Sx Types ,
|
||||
.Sx Functions
|
||||
and
|
||||
.Sx Variables
|
||||
available to programmers.
|
||||
Following that, the
|
||||
.Sx Abstract Syntax Tree
|
||||
section documents the output tree.
|
||||
.Ss Types
|
||||
Both functions (see
|
||||
.Sx Functions )
|
||||
and variables (see
|
||||
.Sx Variables )
|
||||
may use the following types:
|
||||
.Bl -ohang
|
||||
.It Vt struct mdoc
|
||||
An opaque type defined in
|
||||
.Pa mdoc.c .
|
||||
Its values are only used privately within the library.
|
||||
.It Vt struct mdoc_node
|
||||
A parsed node.
|
||||
Defined in
|
||||
.Pa mdoc.h .
|
||||
See
|
||||
.Sx Abstract Syntax Tree
|
||||
for details.
|
||||
.It Vt mandocmsg
|
||||
A function callback type defined in
|
||||
.Pa mandoc.h .
|
||||
.El
|
||||
.Ss Functions
|
||||
Function descriptions follow:
|
||||
.Bl -ohang
|
||||
.It Fn mdoc_alloc
|
||||
Allocates a parsing structure.
|
||||
The
|
||||
.Fa data
|
||||
pointer is passed to
|
||||
.Fa msgs .
|
||||
The
|
||||
.Fa pflags
|
||||
arguments are defined in
|
||||
.Pa mdoc.h .
|
||||
Returns NULL on failure.
|
||||
If non-NULL, the pointer must be freed with
|
||||
.Fn mdoc_free .
|
||||
.It Fn mdoc_reset
|
||||
Reset the parser for another parse routine.
|
||||
After its use,
|
||||
.Fn mdoc_parseln
|
||||
behaves as if invoked for the first time.
|
||||
If it returns 0, memory could not be allocated.
|
||||
.It Fn mdoc_free
|
||||
Free all resources of a parser.
|
||||
The pointer is no longer valid after invocation.
|
||||
.It Fn mdoc_parseln
|
||||
Parse a nil-terminated line of input.
|
||||
This line should not contain the trailing newline.
|
||||
Returns 0 on failure, 1 on success.
|
||||
The input buffer
|
||||
.Fa buf
|
||||
is modified by this function.
|
||||
.It Fn mdoc_endparse
|
||||
Signals that the parse is complete.
|
||||
Note that if
|
||||
.Fn mdoc_endparse
|
||||
is called subsequent to
|
||||
.Fn mdoc_node ,
|
||||
the resulting tree is incomplete.
|
||||
Returns 0 on failure, 1 on success.
|
||||
.It Fn mdoc_node
|
||||
Returns the first node of the parse.
|
||||
Note that if
|
||||
.Fn mdoc_parseln
|
||||
or
|
||||
.Fn mdoc_endparse
|
||||
return 0, the tree will be incomplete.
|
||||
.It Fn mdoc_meta
|
||||
Returns the document's parsed meta-data.
|
||||
If this information has not yet been supplied or
|
||||
.Fn mdoc_parseln
|
||||
or
|
||||
.Fn mdoc_endparse
|
||||
return 0, the data will be incomplete.
|
||||
.El
|
||||
.Ss Variables
|
||||
The following variables are also defined:
|
||||
.Bl -ohang
|
||||
.It Va mdoc_macronames
|
||||
An array of string-ified token names.
|
||||
.It Va mdoc_argnames
|
||||
An array of string-ified token argument names.
|
||||
.El
|
||||
.Ss Abstract Syntax Tree
|
||||
The
|
||||
.Nm
|
||||
functions produce an abstract syntax tree (AST) describing input in a
|
||||
regular form.
|
||||
It may be reviewed at any time with
|
||||
.Fn mdoc_nodes ;
|
||||
however, if called before
|
||||
.Fn mdoc_endparse ,
|
||||
or after
|
||||
.Fn mdoc_endparse
|
||||
or
|
||||
.Fn mdoc_parseln
|
||||
fail, it may be incomplete.
|
||||
.Pp
|
||||
This AST is governed by the ontological
|
||||
rules dictated in
|
||||
.Xr mdoc 7
|
||||
and derives its terminology accordingly.
|
||||
.Qq In-line
|
||||
elements described in
|
||||
.Xr mdoc 7
|
||||
are described simply as
|
||||
.Qq elements .
|
||||
.Pp
|
||||
The AST is composed of
|
||||
.Vt struct mdoc_node
|
||||
nodes with block, head, body, element, root and text types as declared
|
||||
by the
|
||||
.Va type
|
||||
field.
|
||||
Each node also provides its parse point (the
|
||||
.Va line ,
|
||||
.Va sec ,
|
||||
and
|
||||
.Va pos
|
||||
fields), its position in the tree (the
|
||||
.Va parent ,
|
||||
.Va child ,
|
||||
.Va next
|
||||
and
|
||||
.Va prev
|
||||
fields) and some type-specific data.
|
||||
.Pp
|
||||
The tree itself is arranged according to the following normal form,
|
||||
where capitalised non-terminals represent nodes.
|
||||
.Pp
|
||||
.Bl -tag -width "ELEMENTXX" -compact
|
||||
.It ROOT
|
||||
\(<- mnode+
|
||||
.It mnode
|
||||
\(<- BLOCK | ELEMENT | TEXT
|
||||
.It BLOCK
|
||||
\(<- HEAD [TEXT] (BODY [TEXT])+ [TAIL [TEXT]]
|
||||
.It ELEMENT
|
||||
\(<- TEXT*
|
||||
.It HEAD
|
||||
\(<- mnode+
|
||||
.It BODY
|
||||
\(<- mnode+
|
||||
.It TAIL
|
||||
\(<- mnode+
|
||||
.It TEXT
|
||||
\(<- [[:printable:],0x1e]*
|
||||
.El
|
||||
.Pp
|
||||
Of note are the TEXT nodes following the HEAD, BODY and TAIL nodes of
|
||||
the BLOCK production: these refer to punctuation marks.
|
||||
Furthermore, although a TEXT node will generally have a non-zero-length
|
||||
string, in the specific case of
|
||||
.Sq \&.Bd \-literal ,
|
||||
an empty line will produce a zero-length string.
|
||||
Multiple body parts are only found in invocations of
|
||||
.Sq \&Bl \-column ,
|
||||
where a new body introduces a new phrase.
|
||||
.Sh EXAMPLES
|
||||
The following example reads lines from stdin and parses them, operating
|
||||
on the finished parse tree with
|
||||
.Fn parsed .
|
||||
This example does not error-check nor free memory upon failure.
|
||||
.Bd -literal -offset indent
|
||||
struct mdoc *mdoc;
|
||||
const struct mdoc_node *node;
|
||||
char *buf;
|
||||
size_t len;
|
||||
int line;
|
||||
|
||||
line = 1;
|
||||
mdoc = mdoc_alloc(NULL, 0, NULL);
|
||||
buf = NULL;
|
||||
alloc_len = 0;
|
||||
|
||||
while ((len = getline(&buf, &alloc_len, stdin)) >= 0) {
|
||||
if (len && buflen[len - 1] = '\en')
|
||||
buf[len - 1] = '\e0';
|
||||
if ( ! mdoc_parseln(mdoc, line, buf))
|
||||
errx(1, "mdoc_parseln");
|
||||
line++;
|
||||
}
|
||||
|
||||
if ( ! mdoc_endparse(mdoc))
|
||||
errx(1, "mdoc_endparse");
|
||||
if (NULL == (node = mdoc_node(mdoc)))
|
||||
errx(1, "mdoc_node");
|
||||
|
||||
parsed(mdoc, node);
|
||||
mdoc_free(mdoc);
|
||||
.Ed
|
||||
.Pp
|
||||
Please see
|
||||
.Pa main.c
|
||||
in the source archive for a rigorous reference.
|
||||
.Sh SEE ALSO
|
||||
.Xr mandoc 1 ,
|
||||
.Xr mdoc 7
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
library was written by
|
||||
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
|
2375
commands/mdocml/mdoc.7
Normal file
2375
commands/mdocml/mdoc.7
Normal file
File diff suppressed because it is too large
Load diff
822
commands/mdocml/mdoc.c
Normal file
822
commands/mdocml/mdoc.c
Normal file
|
@ -0,0 +1,822 @@
|
|||
/* $Id: mdoc.c,v 1.146 2010/06/12 11:58:22 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
#include "libmandoc.h"
|
||||
|
||||
const char *const __mdoc_macronames[MDOC_MAX] = {
|
||||
"Ap", "Dd", "Dt", "Os",
|
||||
"Sh", "Ss", "Pp", "D1",
|
||||
"Dl", "Bd", "Ed", "Bl",
|
||||
"El", "It", "Ad", "An",
|
||||
"Ar", "Cd", "Cm", "Dv",
|
||||
"Er", "Ev", "Ex", "Fa",
|
||||
"Fd", "Fl", "Fn", "Ft",
|
||||
"Ic", "In", "Li", "Nd",
|
||||
"Nm", "Op", "Ot", "Pa",
|
||||
"Rv", "St", "Va", "Vt",
|
||||
/* LINTED */
|
||||
"Xr", "%A", "%B", "%D",
|
||||
/* LINTED */
|
||||
"%I", "%J", "%N", "%O",
|
||||
/* LINTED */
|
||||
"%P", "%R", "%T", "%V",
|
||||
"Ac", "Ao", "Aq", "At",
|
||||
"Bc", "Bf", "Bo", "Bq",
|
||||
"Bsx", "Bx", "Db", "Dc",
|
||||
"Do", "Dq", "Ec", "Ef",
|
||||
"Em", "Eo", "Fx", "Ms",
|
||||
"No", "Ns", "Nx", "Ox",
|
||||
"Pc", "Pf", "Po", "Pq",
|
||||
"Qc", "Ql", "Qo", "Qq",
|
||||
"Re", "Rs", "Sc", "So",
|
||||
"Sq", "Sm", "Sx", "Sy",
|
||||
"Tn", "Ux", "Xc", "Xo",
|
||||
"Fo", "Fc", "Oo", "Oc",
|
||||
"Bk", "Ek", "Bt", "Hf",
|
||||
"Fr", "Ud", "Lb", "Lp",
|
||||
"Lk", "Mt", "Brq", "Bro",
|
||||
/* LINTED */
|
||||
"Brc", "%C", "Es", "En",
|
||||
/* LINTED */
|
||||
"Dx", "%Q", "br", "sp",
|
||||
/* LINTED */
|
||||
"%U", "Ta"
|
||||
};
|
||||
|
||||
const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
|
||||
"split", "nosplit", "ragged",
|
||||
"unfilled", "literal", "file",
|
||||
"offset", "bullet", "dash",
|
||||
"hyphen", "item", "enum",
|
||||
"tag", "diag", "hang",
|
||||
"ohang", "inset", "column",
|
||||
"width", "compact", "std",
|
||||
"filled", "words", "emphasis",
|
||||
"symbolic", "nested", "centered"
|
||||
};
|
||||
|
||||
const char * const *mdoc_macronames = __mdoc_macronames;
|
||||
const char * const *mdoc_argnames = __mdoc_argnames;
|
||||
|
||||
static void mdoc_node_free(struct mdoc_node *);
|
||||
static void mdoc_node_unlink(struct mdoc *,
|
||||
struct mdoc_node *);
|
||||
static void mdoc_free1(struct mdoc *);
|
||||
static void mdoc_alloc1(struct mdoc *);
|
||||
static struct mdoc_node *node_alloc(struct mdoc *, int, int,
|
||||
enum mdoct, enum mdoc_type);
|
||||
static int node_append(struct mdoc *,
|
||||
struct mdoc_node *);
|
||||
static int mdoc_ptext(struct mdoc *, int, char *, int);
|
||||
static int mdoc_pmacro(struct mdoc *, int, char *, int);
|
||||
static int macrowarn(struct mdoc *, int,
|
||||
const char *, int);
|
||||
|
||||
|
||||
const struct mdoc_node *
|
||||
mdoc_node(const struct mdoc *m)
|
||||
{
|
||||
|
||||
return(MDOC_HALT & m->flags ? NULL : m->first);
|
||||
}
|
||||
|
||||
|
||||
const struct mdoc_meta *
|
||||
mdoc_meta(const struct mdoc *m)
|
||||
{
|
||||
|
||||
return(MDOC_HALT & m->flags ? NULL : &m->meta);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Frees volatile resources (parse tree, meta-data, fields).
|
||||
*/
|
||||
static void
|
||||
mdoc_free1(struct mdoc *mdoc)
|
||||
{
|
||||
|
||||
if (mdoc->first)
|
||||
mdoc_node_delete(mdoc, mdoc->first);
|
||||
if (mdoc->meta.title)
|
||||
free(mdoc->meta.title);
|
||||
if (mdoc->meta.os)
|
||||
free(mdoc->meta.os);
|
||||
if (mdoc->meta.name)
|
||||
free(mdoc->meta.name);
|
||||
if (mdoc->meta.arch)
|
||||
free(mdoc->meta.arch);
|
||||
if (mdoc->meta.vol)
|
||||
free(mdoc->meta.vol);
|
||||
if (mdoc->meta.msec)
|
||||
free(mdoc->meta.msec);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate all volatile resources (parse tree, meta-data, fields).
|
||||
*/
|
||||
static void
|
||||
mdoc_alloc1(struct mdoc *mdoc)
|
||||
{
|
||||
|
||||
memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
|
||||
mdoc->flags = 0;
|
||||
mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
|
||||
mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
|
||||
mdoc->first = mdoc->last;
|
||||
mdoc->last->type = MDOC_ROOT;
|
||||
mdoc->next = MDOC_NEXT_CHILD;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Free up volatile resources (see mdoc_free1()) then re-initialises the
|
||||
* data with mdoc_alloc1(). After invocation, parse data has been reset
|
||||
* and the parser is ready for re-invocation on a new tree; however,
|
||||
* cross-parse non-volatile data is kept intact.
|
||||
*/
|
||||
void
|
||||
mdoc_reset(struct mdoc *mdoc)
|
||||
{
|
||||
|
||||
mdoc_free1(mdoc);
|
||||
mdoc_alloc1(mdoc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Completely free up all volatile and non-volatile parse resources.
|
||||
* After invocation, the pointer is no longer usable.
|
||||
*/
|
||||
void
|
||||
mdoc_free(struct mdoc *mdoc)
|
||||
{
|
||||
|
||||
mdoc_free1(mdoc);
|
||||
free(mdoc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate volatile and non-volatile parse resources.
|
||||
*/
|
||||
struct mdoc *
|
||||
mdoc_alloc(void *data, int pflags, mandocmsg msg)
|
||||
{
|
||||
struct mdoc *p;
|
||||
|
||||
p = mandoc_calloc(1, sizeof(struct mdoc));
|
||||
|
||||
p->msg = msg;
|
||||
p->data = data;
|
||||
p->pflags = pflags;
|
||||
|
||||
mdoc_hash_init();
|
||||
mdoc_alloc1(p);
|
||||
return(p);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Climb back up the parse tree, validating open scopes. Mostly calls
|
||||
* through to macro_end() in macro.c.
|
||||
*/
|
||||
int
|
||||
mdoc_endparse(struct mdoc *m)
|
||||
{
|
||||
|
||||
if (MDOC_HALT & m->flags)
|
||||
return(0);
|
||||
else if (mdoc_macroend(m))
|
||||
return(1);
|
||||
m->flags |= MDOC_HALT;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Main parse routine. Parses a single line -- really just hands off to
|
||||
* the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
|
||||
*/
|
||||
int
|
||||
mdoc_parseln(struct mdoc *m, int ln, char *buf, int offs)
|
||||
{
|
||||
|
||||
if (MDOC_HALT & m->flags)
|
||||
return(0);
|
||||
|
||||
m->flags |= MDOC_NEWLINE;
|
||||
return(('.' == buf[offs] || '\'' == buf[offs]) ?
|
||||
mdoc_pmacro(m, ln, buf, offs) :
|
||||
mdoc_ptext(m, ln, buf, offs));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mdoc_vmsg(struct mdoc *mdoc, enum mandocerr t,
|
||||
int ln, int pos, const char *fmt, ...)
|
||||
{
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return((*mdoc->msg)(t, mdoc->data, ln, pos, buf));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mdoc_macro(struct mdoc *m, enum mdoct tok,
|
||||
int ln, int pp, int *pos, char *buf)
|
||||
{
|
||||
assert(tok < MDOC_MAX);
|
||||
|
||||
/* If we're in the body, deny prologue calls. */
|
||||
|
||||
if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
|
||||
MDOC_PBODY & m->flags)
|
||||
return(mdoc_pmsg(m, ln, pp, MANDOCERR_BADBODY));
|
||||
|
||||
/* If we're in the prologue, deny "body" macros. */
|
||||
|
||||
if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
|
||||
! (MDOC_PBODY & m->flags)) {
|
||||
if ( ! mdoc_pmsg(m, ln, pp, MANDOCERR_BADPROLOG))
|
||||
return(0);
|
||||
if (NULL == m->meta.title)
|
||||
m->meta.title = mandoc_strdup("UNKNOWN");
|
||||
if (NULL == m->meta.vol)
|
||||
m->meta.vol = mandoc_strdup("LOCAL");
|
||||
if (NULL == m->meta.os)
|
||||
m->meta.os = mandoc_strdup("LOCAL");
|
||||
if (0 == m->meta.date)
|
||||
m->meta.date = time(NULL);
|
||||
m->flags |= MDOC_PBODY;
|
||||
}
|
||||
|
||||
return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
node_append(struct mdoc *mdoc, struct mdoc_node *p)
|
||||
{
|
||||
|
||||
assert(mdoc->last);
|
||||
assert(mdoc->first);
|
||||
assert(MDOC_ROOT != p->type);
|
||||
|
||||
switch (mdoc->next) {
|
||||
case (MDOC_NEXT_SIBLING):
|
||||
mdoc->last->next = p;
|
||||
p->prev = mdoc->last;
|
||||
p->parent = mdoc->last->parent;
|
||||
break;
|
||||
case (MDOC_NEXT_CHILD):
|
||||
mdoc->last->child = p;
|
||||
p->parent = mdoc->last;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
p->parent->nchild++;
|
||||
|
||||
if ( ! mdoc_valid_pre(mdoc, p))
|
||||
return(0);
|
||||
if ( ! mdoc_action_pre(mdoc, p))
|
||||
return(0);
|
||||
|
||||
switch (p->type) {
|
||||
case (MDOC_HEAD):
|
||||
assert(MDOC_BLOCK == p->parent->type);
|
||||
p->parent->head = p;
|
||||
break;
|
||||
case (MDOC_TAIL):
|
||||
assert(MDOC_BLOCK == p->parent->type);
|
||||
p->parent->tail = p;
|
||||
break;
|
||||
case (MDOC_BODY):
|
||||
assert(MDOC_BLOCK == p->parent->type);
|
||||
p->parent->body = p;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mdoc->last = p;
|
||||
|
||||
switch (p->type) {
|
||||
case (MDOC_TEXT):
|
||||
if ( ! mdoc_valid_post(mdoc))
|
||||
return(0);
|
||||
if ( ! mdoc_action_post(mdoc))
|
||||
return(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static struct mdoc_node *
|
||||
node_alloc(struct mdoc *m, int line, int pos,
|
||||
enum mdoct tok, enum mdoc_type type)
|
||||
{
|
||||
struct mdoc_node *p;
|
||||
|
||||
p = mandoc_calloc(1, sizeof(struct mdoc_node));
|
||||
p->sec = m->lastsec;
|
||||
p->line = line;
|
||||
p->pos = pos;
|
||||
p->tok = tok;
|
||||
p->type = type;
|
||||
if (MDOC_NEWLINE & m->flags)
|
||||
p->flags |= MDOC_LINE;
|
||||
m->flags &= ~MDOC_NEWLINE;
|
||||
return(p);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mdoc_tail_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
|
||||
{
|
||||
struct mdoc_node *p;
|
||||
|
||||
p = node_alloc(m, line, pos, tok, MDOC_TAIL);
|
||||
if ( ! node_append(m, p))
|
||||
return(0);
|
||||
m->next = MDOC_NEXT_CHILD;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mdoc_head_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
|
||||
{
|
||||
struct mdoc_node *p;
|
||||
|
||||
assert(m->first);
|
||||
assert(m->last);
|
||||
|
||||
p = node_alloc(m, line, pos, tok, MDOC_HEAD);
|
||||
if ( ! node_append(m, p))
|
||||
return(0);
|
||||
m->next = MDOC_NEXT_CHILD;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
|
||||
{
|
||||
struct mdoc_node *p;
|
||||
|
||||
p = node_alloc(m, line, pos, tok, MDOC_BODY);
|
||||
if ( ! node_append(m, p))
|
||||
return(0);
|
||||
m->next = MDOC_NEXT_CHILD;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mdoc_block_alloc(struct mdoc *m, int line, int pos,
|
||||
enum mdoct tok, struct mdoc_arg *args)
|
||||
{
|
||||
struct mdoc_node *p;
|
||||
|
||||
p = node_alloc(m, line, pos, tok, MDOC_BLOCK);
|
||||
p->args = args;
|
||||
if (p->args)
|
||||
(args->refcnt)++;
|
||||
if ( ! node_append(m, p))
|
||||
return(0);
|
||||
m->next = MDOC_NEXT_CHILD;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mdoc_elem_alloc(struct mdoc *m, int line, int pos,
|
||||
enum mdoct tok, struct mdoc_arg *args)
|
||||
{
|
||||
struct mdoc_node *p;
|
||||
|
||||
p = node_alloc(m, line, pos, tok, MDOC_ELEM);
|
||||
p->args = args;
|
||||
if (p->args)
|
||||
(args->refcnt)++;
|
||||
if ( ! node_append(m, p))
|
||||
return(0);
|
||||
m->next = MDOC_NEXT_CHILD;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p)
|
||||
{
|
||||
struct mdoc_node *n;
|
||||
size_t sv, len;
|
||||
|
||||
len = strlen(p);
|
||||
|
||||
n = node_alloc(m, line, pos, MDOC_MAX, MDOC_TEXT);
|
||||
n->string = mandoc_malloc(len + 1);
|
||||
sv = strlcpy(n->string, p, len + 1);
|
||||
|
||||
/* Prohibit truncation. */
|
||||
assert(sv < len + 1);
|
||||
|
||||
if ( ! node_append(m, n))
|
||||
return(0);
|
||||
|
||||
m->next = MDOC_NEXT_SIBLING;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mdoc_node_free(struct mdoc_node *p)
|
||||
{
|
||||
|
||||
if (p->string)
|
||||
free(p->string);
|
||||
if (p->args)
|
||||
mdoc_argv_free(p->args);
|
||||
free(p);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n)
|
||||
{
|
||||
|
||||
/* Adjust siblings. */
|
||||
|
||||
if (n->prev)
|
||||
n->prev->next = n->next;
|
||||
if (n->next)
|
||||
n->next->prev = n->prev;
|
||||
|
||||
/* Adjust parent. */
|
||||
|
||||
if (n->parent) {
|
||||
n->parent->nchild--;
|
||||
if (n->parent->child == n)
|
||||
n->parent->child = n->prev ? n->prev : n->next;
|
||||
}
|
||||
|
||||
/* Adjust parse point, if applicable. */
|
||||
|
||||
if (m && m->last == n) {
|
||||
if (n->prev) {
|
||||
m->last = n->prev;
|
||||
m->next = MDOC_NEXT_SIBLING;
|
||||
} else {
|
||||
m->last = n->parent;
|
||||
m->next = MDOC_NEXT_CHILD;
|
||||
}
|
||||
}
|
||||
|
||||
if (m && m->first == n)
|
||||
m->first = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mdoc_node_delete(struct mdoc *m, struct mdoc_node *p)
|
||||
{
|
||||
|
||||
while (p->child) {
|
||||
assert(p->nchild);
|
||||
mdoc_node_delete(m, p->child);
|
||||
}
|
||||
assert(0 == p->nchild);
|
||||
|
||||
mdoc_node_unlink(m, p);
|
||||
mdoc_node_free(p);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse free-form text, that is, a line that does not begin with the
|
||||
* control character.
|
||||
*/
|
||||
static int
|
||||
mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
|
||||
{
|
||||
char *c, *ws, *end;
|
||||
struct mdoc_node *n;
|
||||
|
||||
/* Ignore bogus comments. */
|
||||
|
||||
if ('\\' == buf[offs] &&
|
||||
'.' == buf[offs + 1] &&
|
||||
'"' == buf[offs + 2])
|
||||
return(mdoc_pmsg(m, line, offs, MANDOCERR_BADCOMMENT));
|
||||
|
||||
/* No text before an initial macro. */
|
||||
|
||||
if (SEC_NONE == m->lastnamed)
|
||||
return(mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT));
|
||||
|
||||
assert(m->last);
|
||||
n = m->last;
|
||||
|
||||
/*
|
||||
* Divert directly to list processing if we're encountering a
|
||||
* columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry
|
||||
* (a MDOC_BODY means it's already open, in which case we should
|
||||
* process within its context in the normal way).
|
||||
*/
|
||||
|
||||
if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
|
||||
LIST_column == n->data.Bl.type) {
|
||||
/* `Bl' is open without any children. */
|
||||
m->flags |= MDOC_FREECOL;
|
||||
return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
|
||||
}
|
||||
|
||||
if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
|
||||
NULL != n->parent &&
|
||||
MDOC_Bl == n->parent->tok &&
|
||||
LIST_column == n->parent->data.Bl.type) {
|
||||
/* `Bl' has block-level `It' children. */
|
||||
m->flags |= MDOC_FREECOL;
|
||||
return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for the beginning of unescaped trailing whitespace (ws)
|
||||
* and for the first character not to be output (end).
|
||||
*/
|
||||
|
||||
/* FIXME: replace with strcspn(). */
|
||||
ws = NULL;
|
||||
for (c = end = buf + offs; *c; c++) {
|
||||
switch (*c) {
|
||||
case '-':
|
||||
if (mandoc_hyph(buf + offs, c))
|
||||
*c = ASCII_HYPH;
|
||||
ws = NULL;
|
||||
break;
|
||||
case ' ':
|
||||
if (NULL == ws)
|
||||
ws = c;
|
||||
continue;
|
||||
case '\t':
|
||||
/*
|
||||
* Always warn about trailing tabs,
|
||||
* even outside literal context,
|
||||
* where they should be put on the next line.
|
||||
*/
|
||||
if (NULL == ws)
|
||||
ws = c;
|
||||
/*
|
||||
* Strip trailing tabs in literal context only;
|
||||
* outside, they affect the next line.
|
||||
*/
|
||||
if (MDOC_LITERAL & m->flags)
|
||||
continue;
|
||||
break;
|
||||
case '\\':
|
||||
/* Skip the escaped character, too, if any. */
|
||||
if (c[1])
|
||||
c++;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
ws = NULL;
|
||||
break;
|
||||
}
|
||||
end = c + 1;
|
||||
}
|
||||
*end = '\0';
|
||||
|
||||
if (ws)
|
||||
if ( ! mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE))
|
||||
return(0);
|
||||
|
||||
if ('\0' == buf[offs] && ! (MDOC_LITERAL & m->flags)) {
|
||||
if ( ! mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN))
|
||||
return(0);
|
||||
|
||||
/*
|
||||
* Insert a `Pp' in the case of a blank line. Technically,
|
||||
* blank lines aren't allowed, but enough manuals assume this
|
||||
* behaviour that we want to work around it.
|
||||
*/
|
||||
if ( ! mdoc_elem_alloc(m, line, offs, MDOC_Pp, NULL))
|
||||
return(0);
|
||||
|
||||
m->next = MDOC_NEXT_SIBLING;
|
||||
return(1);
|
||||
}
|
||||
|
||||
if ( ! mdoc_word_alloc(m, line, offs, buf+offs))
|
||||
return(0);
|
||||
|
||||
if (MDOC_LITERAL & m->flags)
|
||||
return(1);
|
||||
|
||||
/*
|
||||
* End-of-sentence check. If the last character is an unescaped
|
||||
* EOS character, then flag the node as being the end of a
|
||||
* sentence. The front-end will know how to interpret this.
|
||||
*/
|
||||
|
||||
assert(buf < end);
|
||||
|
||||
if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
|
||||
m->last->flags |= MDOC_EOS;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
macrowarn(struct mdoc *m, int ln, const char *buf, int offs)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = mdoc_vmsg(m, MANDOCERR_MACRO, ln, offs,
|
||||
"unknown macro: %s%s",
|
||||
buf, strlen(buf) > 3 ? "..." : "");
|
||||
|
||||
/* FIXME: logic should be in driver. */
|
||||
/* FIXME: broken, will error out and not omit a message. */
|
||||
return(MDOC_IGN_MACRO & m->pflags ? rc : 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse a macro line, that is, a line beginning with the control
|
||||
* character.
|
||||
*/
|
||||
static int
|
||||
mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
|
||||
{
|
||||
enum mdoct tok;
|
||||
int i, j, sv;
|
||||
char mac[5];
|
||||
struct mdoc_node *n;
|
||||
|
||||
/* Empty lines are ignored. */
|
||||
|
||||
offs++;
|
||||
|
||||
if ('\0' == buf[offs])
|
||||
return(1);
|
||||
|
||||
i = offs;
|
||||
|
||||
/* Accept whitespace after the initial control char. */
|
||||
|
||||
if (' ' == buf[i]) {
|
||||
i++;
|
||||
while (buf[i] && ' ' == buf[i])
|
||||
i++;
|
||||
if ('\0' == buf[i])
|
||||
return(1);
|
||||
}
|
||||
|
||||
sv = i;
|
||||
|
||||
/* Copy the first word into a nil-terminated buffer. */
|
||||
|
||||
for (j = 0; j < 4; j++, i++) {
|
||||
if ('\0' == (mac[j] = buf[i]))
|
||||
break;
|
||||
else if (' ' == buf[i])
|
||||
break;
|
||||
|
||||
/* Check for invalid characters. */
|
||||
|
||||
if (isgraph((u_char)buf[i]))
|
||||
continue;
|
||||
if ( ! mdoc_pmsg(m, ln, i, MANDOCERR_BADCHAR))
|
||||
return(0);
|
||||
i--;
|
||||
}
|
||||
|
||||
mac[j] = '\0';
|
||||
|
||||
if (j == 4 || j < 2) {
|
||||
if ( ! macrowarn(m, ln, mac, sv))
|
||||
goto err;
|
||||
return(1);
|
||||
}
|
||||
|
||||
if (MDOC_MAX == (tok = mdoc_hash_find(mac))) {
|
||||
if ( ! macrowarn(m, ln, mac, sv))
|
||||
goto err;
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* The macro is sane. Jump to the next word. */
|
||||
|
||||
while (buf[i] && ' ' == buf[i])
|
||||
i++;
|
||||
|
||||
/*
|
||||
* Trailing whitespace. Note that tabs are allowed to be passed
|
||||
* into the parser as "text", so we only warn about spaces here.
|
||||
*/
|
||||
|
||||
if ('\0' == buf[i] && ' ' == buf[i - 1])
|
||||
if ( ! mdoc_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* If an initial macro or a list invocation, divert directly
|
||||
* into macro processing.
|
||||
*/
|
||||
|
||||
if (NULL == m->last || MDOC_It == tok || MDOC_El == tok) {
|
||||
if ( ! mdoc_macro(m, tok, ln, sv, &i, buf))
|
||||
goto err;
|
||||
return(1);
|
||||
}
|
||||
|
||||
n = m->last;
|
||||
assert(m->last);
|
||||
|
||||
/*
|
||||
* If the first macro of a `Bl -column', open an `It' block
|
||||
* context around the parsed macro.
|
||||
*/
|
||||
|
||||
if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
|
||||
LIST_column == n->data.Bl.type) {
|
||||
m->flags |= MDOC_FREECOL;
|
||||
if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
|
||||
goto err;
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're following a block-level `It' within a `Bl -column'
|
||||
* context (perhaps opened in the above block or in ptext()),
|
||||
* then open an `It' block context around the parsed macro.
|
||||
*/
|
||||
|
||||
if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
|
||||
NULL != n->parent &&
|
||||
MDOC_Bl == n->parent->tok &&
|
||||
LIST_column == n->parent->data.Bl.type) {
|
||||
m->flags |= MDOC_FREECOL;
|
||||
if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
|
||||
goto err;
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* Normal processing of a macro. */
|
||||
|
||||
if ( ! mdoc_macro(m, tok, ln, sv, &i, buf))
|
||||
goto err;
|
||||
|
||||
return(1);
|
||||
|
||||
err: /* Error out. */
|
||||
|
||||
m->flags |= MDOC_HALT;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
345
commands/mdocml/mdoc.h
Normal file
345
commands/mdocml/mdoc.h
Normal file
|
@ -0,0 +1,345 @@
|
|||
/* $Id: mdoc.h,v 1.90 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef MDOC_H
|
||||
#define MDOC_H
|
||||
|
||||
/*
|
||||
* This library implements a validating scanner/parser for ``mdoc'' roff
|
||||
* macro documents, a.k.a. BSD manual page documents. The mdoc.c file
|
||||
* drives the parser, while macro.c describes the macro ontologies.
|
||||
* validate.c pre- and post-validates parsed macros, and action.c
|
||||
* performs actions on parsed and validated macros.
|
||||
*/
|
||||
|
||||
/* What follows is a list of ALL possible macros. */
|
||||
|
||||
enum mdoct {
|
||||
MDOC_Ap = 0,
|
||||
MDOC_Dd,
|
||||
MDOC_Dt,
|
||||
MDOC_Os,
|
||||
MDOC_Sh,
|
||||
MDOC_Ss,
|
||||
MDOC_Pp,
|
||||
MDOC_D1,
|
||||
MDOC_Dl,
|
||||
MDOC_Bd,
|
||||
MDOC_Ed,
|
||||
MDOC_Bl,
|
||||
MDOC_El,
|
||||
MDOC_It,
|
||||
MDOC_Ad,
|
||||
MDOC_An,
|
||||
MDOC_Ar,
|
||||
MDOC_Cd,
|
||||
MDOC_Cm,
|
||||
MDOC_Dv,
|
||||
MDOC_Er,
|
||||
MDOC_Ev,
|
||||
MDOC_Ex,
|
||||
MDOC_Fa,
|
||||
MDOC_Fd,
|
||||
MDOC_Fl,
|
||||
MDOC_Fn,
|
||||
MDOC_Ft,
|
||||
MDOC_Ic,
|
||||
MDOC_In,
|
||||
MDOC_Li,
|
||||
MDOC_Nd,
|
||||
MDOC_Nm,
|
||||
MDOC_Op,
|
||||
MDOC_Ot,
|
||||
MDOC_Pa,
|
||||
MDOC_Rv,
|
||||
MDOC_St,
|
||||
MDOC_Va,
|
||||
MDOC_Vt,
|
||||
MDOC_Xr,
|
||||
MDOC__A,
|
||||
MDOC__B,
|
||||
MDOC__D,
|
||||
MDOC__I,
|
||||
MDOC__J,
|
||||
MDOC__N,
|
||||
MDOC__O,
|
||||
MDOC__P,
|
||||
MDOC__R,
|
||||
MDOC__T,
|
||||
MDOC__V,
|
||||
MDOC_Ac,
|
||||
MDOC_Ao,
|
||||
MDOC_Aq,
|
||||
MDOC_At,
|
||||
MDOC_Bc,
|
||||
MDOC_Bf,
|
||||
MDOC_Bo,
|
||||
MDOC_Bq,
|
||||
MDOC_Bsx,
|
||||
MDOC_Bx,
|
||||
MDOC_Db,
|
||||
MDOC_Dc,
|
||||
MDOC_Do,
|
||||
MDOC_Dq,
|
||||
MDOC_Ec,
|
||||
MDOC_Ef,
|
||||
MDOC_Em,
|
||||
MDOC_Eo,
|
||||
MDOC_Fx,
|
||||
MDOC_Ms,
|
||||
MDOC_No,
|
||||
MDOC_Ns,
|
||||
MDOC_Nx,
|
||||
MDOC_Ox,
|
||||
MDOC_Pc,
|
||||
MDOC_Pf,
|
||||
MDOC_Po,
|
||||
MDOC_Pq,
|
||||
MDOC_Qc,
|
||||
MDOC_Ql,
|
||||
MDOC_Qo,
|
||||
MDOC_Qq,
|
||||
MDOC_Re,
|
||||
MDOC_Rs,
|
||||
MDOC_Sc,
|
||||
MDOC_So,
|
||||
MDOC_Sq,
|
||||
MDOC_Sm,
|
||||
MDOC_Sx,
|
||||
MDOC_Sy,
|
||||
MDOC_Tn,
|
||||
MDOC_Ux,
|
||||
MDOC_Xc,
|
||||
MDOC_Xo,
|
||||
MDOC_Fo,
|
||||
MDOC_Fc,
|
||||
MDOC_Oo,
|
||||
MDOC_Oc,
|
||||
MDOC_Bk,
|
||||
MDOC_Ek,
|
||||
MDOC_Bt,
|
||||
MDOC_Hf,
|
||||
MDOC_Fr,
|
||||
MDOC_Ud,
|
||||
MDOC_Lb,
|
||||
MDOC_Lp,
|
||||
MDOC_Lk,
|
||||
MDOC_Mt,
|
||||
MDOC_Brq,
|
||||
MDOC_Bro,
|
||||
MDOC_Brc,
|
||||
MDOC__C,
|
||||
MDOC_Es,
|
||||
MDOC_En,
|
||||
MDOC_Dx,
|
||||
MDOC__Q,
|
||||
MDOC_br,
|
||||
MDOC_sp,
|
||||
MDOC__U,
|
||||
MDOC_Ta,
|
||||
MDOC_MAX
|
||||
};
|
||||
|
||||
/* What follows is a list of ALL possible macro arguments. */
|
||||
|
||||
#define MDOC_Split 0
|
||||
#define MDOC_Nosplit 1
|
||||
#define MDOC_Ragged 2
|
||||
#define MDOC_Unfilled 3
|
||||
#define MDOC_Literal 4
|
||||
#define MDOC_File 5
|
||||
#define MDOC_Offset 6
|
||||
#define MDOC_Bullet 7
|
||||
#define MDOC_Dash 8
|
||||
#define MDOC_Hyphen 9
|
||||
#define MDOC_Item 10
|
||||
#define MDOC_Enum 11
|
||||
#define MDOC_Tag 12
|
||||
#define MDOC_Diag 13
|
||||
#define MDOC_Hang 14
|
||||
#define MDOC_Ohang 15
|
||||
#define MDOC_Inset 16
|
||||
#define MDOC_Column 17
|
||||
#define MDOC_Width 18
|
||||
#define MDOC_Compact 19
|
||||
#define MDOC_Std 20
|
||||
#define MDOC_Filled 21
|
||||
#define MDOC_Words 22
|
||||
#define MDOC_Emphasis 23
|
||||
#define MDOC_Symbolic 24
|
||||
#define MDOC_Nested 25
|
||||
#define MDOC_Centred 26
|
||||
#define MDOC_ARG_MAX 27
|
||||
|
||||
/* Type of a syntax node. */
|
||||
enum mdoc_type {
|
||||
MDOC_TEXT,
|
||||
MDOC_ELEM,
|
||||
MDOC_HEAD,
|
||||
MDOC_TAIL,
|
||||
MDOC_BODY,
|
||||
MDOC_BLOCK,
|
||||
MDOC_ROOT
|
||||
};
|
||||
|
||||
/* Section (named/unnamed) of `Sh'. */
|
||||
enum mdoc_sec {
|
||||
SEC_NONE, /* No section, yet. */
|
||||
SEC_NAME,
|
||||
SEC_LIBRARY,
|
||||
SEC_SYNOPSIS,
|
||||
SEC_DESCRIPTION,
|
||||
SEC_IMPLEMENTATION,
|
||||
SEC_RETURN_VALUES,
|
||||
SEC_ENVIRONMENT,
|
||||
SEC_FILES,
|
||||
SEC_EXIT_STATUS,
|
||||
SEC_EXAMPLES,
|
||||
SEC_DIAGNOSTICS,
|
||||
SEC_COMPATIBILITY,
|
||||
SEC_ERRORS,
|
||||
SEC_SEE_ALSO,
|
||||
SEC_STANDARDS,
|
||||
SEC_HISTORY,
|
||||
SEC_AUTHORS,
|
||||
SEC_CAVEATS,
|
||||
SEC_BUGS,
|
||||
SEC_SECURITY,
|
||||
SEC_CUSTOM, /* User-defined. */
|
||||
SEC__MAX
|
||||
};
|
||||
|
||||
/* Information from prologue. */
|
||||
struct mdoc_meta {
|
||||
char *msec;
|
||||
char *vol;
|
||||
char *arch;
|
||||
time_t date;
|
||||
char *title;
|
||||
char *os;
|
||||
char *name;
|
||||
};
|
||||
|
||||
/* An argument to a macro (multiple values = `It -column'). */
|
||||
struct mdoc_argv {
|
||||
int arg;
|
||||
int line;
|
||||
int pos;
|
||||
size_t sz;
|
||||
char **value;
|
||||
};
|
||||
|
||||
struct mdoc_arg {
|
||||
size_t argc;
|
||||
struct mdoc_argv *argv;
|
||||
unsigned int refcnt;
|
||||
};
|
||||
|
||||
enum mdoc_list {
|
||||
LIST__NONE = 0,
|
||||
LIST_bullet,
|
||||
LIST_column,
|
||||
LIST_dash,
|
||||
LIST_diag,
|
||||
LIST_enum,
|
||||
LIST_hang,
|
||||
LIST_hyphen,
|
||||
LIST_inset,
|
||||
LIST_item,
|
||||
LIST_ohang,
|
||||
LIST_tag
|
||||
};
|
||||
|
||||
enum mdoc_disp {
|
||||
DISP__NONE = 0,
|
||||
DISP_centred,
|
||||
DISP_ragged,
|
||||
DISP_unfilled,
|
||||
DISP_filled,
|
||||
DISP_literal
|
||||
};
|
||||
|
||||
struct mdoc_bd {
|
||||
const char *offs; /* -offset */
|
||||
enum mdoc_disp type; /* -ragged, etc. */
|
||||
int comp; /* -compact */
|
||||
};
|
||||
|
||||
struct mdoc_bl {
|
||||
const char *width; /* -width */
|
||||
const char *offs; /* -offset */
|
||||
enum mdoc_list type; /* -tag, -enum, etc. */
|
||||
int comp; /* -compact */
|
||||
};
|
||||
|
||||
/* Node in AST. */
|
||||
struct mdoc_node {
|
||||
struct mdoc_node *parent; /* parent AST node */
|
||||
struct mdoc_node *child; /* first child AST node */
|
||||
struct mdoc_node *next; /* sibling AST node */
|
||||
struct mdoc_node *prev; /* prior sibling AST node */
|
||||
int nchild; /* number children */
|
||||
int line; /* parse line */
|
||||
int pos; /* parse column */
|
||||
enum mdoct tok; /* tok or MDOC__MAX if none */
|
||||
int flags;
|
||||
#define MDOC_VALID (1 << 0) /* has been validated */
|
||||
#define MDOC_ACTED (1 << 1) /* has been acted upon */
|
||||
#define MDOC_EOS (1 << 2) /* at sentence boundary */
|
||||
#define MDOC_LINE (1 << 3) /* first macro/text on line */
|
||||
enum mdoc_type type; /* AST node type */
|
||||
enum mdoc_sec sec; /* current named section */
|
||||
struct mdoc_arg *args; /* BLOCK/ELEM */
|
||||
#ifdef UGLY
|
||||
struct mdoc_node *pending; /* BLOCK */
|
||||
#endif
|
||||
struct mdoc_node *head; /* BLOCK */
|
||||
struct mdoc_node *body; /* BLOCK */
|
||||
struct mdoc_node *tail; /* BLOCK */
|
||||
char *string; /* TEXT */
|
||||
|
||||
union {
|
||||
struct mdoc_bl Bl;
|
||||
struct mdoc_bd Bd;
|
||||
} data;
|
||||
};
|
||||
|
||||
#define MDOC_IGN_SCOPE (1 << 0) /* Ignore scope violations. */
|
||||
#define MDOC_IGN_ESCAPE (1 << 1) /* Ignore bad escape sequences. */
|
||||
#define MDOC_IGN_MACRO (1 << 2) /* Ignore unknown macros. */
|
||||
|
||||
/* See mdoc.3 for documentation. */
|
||||
|
||||
extern const char *const *mdoc_macronames;
|
||||
extern const char *const *mdoc_argnames;
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
struct mdoc;
|
||||
|
||||
/* See mdoc.3 for documentation. */
|
||||
|
||||
void mdoc_free(struct mdoc *);
|
||||
struct mdoc *mdoc_alloc(void *, int, mandocmsg);
|
||||
void mdoc_reset(struct mdoc *);
|
||||
int mdoc_parseln(struct mdoc *, int, char *, int);
|
||||
const struct mdoc_node *mdoc_node(const struct mdoc *);
|
||||
const struct mdoc_meta *mdoc_meta(const struct mdoc *);
|
||||
int mdoc_endparse(struct mdoc *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!MDOC_H*/
|
1034
commands/mdocml/mdoc_action.c
Normal file
1034
commands/mdocml/mdoc_action.c
Normal file
File diff suppressed because it is too large
Load diff
790
commands/mdocml/mdoc_argv.c
Normal file
790
commands/mdocml/mdoc_argv.c
Normal file
|
@ -0,0 +1,790 @@
|
|||
/* $Id: mdoc_argv.c,v 1.54 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
#include "libmandoc.h"
|
||||
|
||||
/*
|
||||
* Routines to parse arguments of macros. Arguments follow the syntax
|
||||
* of `-arg [val [valN...]]'. Arguments come in all types: quoted
|
||||
* arguments, multiple arguments per value, no-value arguments, etc.
|
||||
*
|
||||
* There's no limit to the number or arguments that may be allocated.
|
||||
*/
|
||||
|
||||
#define ARGV_NONE (1 << 0)
|
||||
#define ARGV_SINGLE (1 << 1)
|
||||
#define ARGV_MULTI (1 << 2)
|
||||
#define ARGV_OPT_SINGLE (1 << 3)
|
||||
|
||||
#define MULTI_STEP 5
|
||||
|
||||
static int argv_a2arg(enum mdoct, const char *);
|
||||
static enum margserr args(struct mdoc *, int, int *,
|
||||
char *, int, char **);
|
||||
static int argv(struct mdoc *, int,
|
||||
struct mdoc_argv *, int *, char *);
|
||||
static int argv_single(struct mdoc *, int,
|
||||
struct mdoc_argv *, int *, char *);
|
||||
static int argv_opt_single(struct mdoc *, int,
|
||||
struct mdoc_argv *, int *, char *);
|
||||
static int argv_multi(struct mdoc *, int,
|
||||
struct mdoc_argv *, int *, char *);
|
||||
|
||||
/* Per-argument flags. */
|
||||
|
||||
static int mdoc_argvflags[MDOC_ARG_MAX] = {
|
||||
ARGV_NONE, /* MDOC_Split */
|
||||
ARGV_NONE, /* MDOC_Nosplit */
|
||||
ARGV_NONE, /* MDOC_Ragged */
|
||||
ARGV_NONE, /* MDOC_Unfilled */
|
||||
ARGV_NONE, /* MDOC_Literal */
|
||||
ARGV_SINGLE, /* MDOC_File */
|
||||
ARGV_OPT_SINGLE, /* MDOC_Offset */
|
||||
ARGV_NONE, /* MDOC_Bullet */
|
||||
ARGV_NONE, /* MDOC_Dash */
|
||||
ARGV_NONE, /* MDOC_Hyphen */
|
||||
ARGV_NONE, /* MDOC_Item */
|
||||
ARGV_NONE, /* MDOC_Enum */
|
||||
ARGV_NONE, /* MDOC_Tag */
|
||||
ARGV_NONE, /* MDOC_Diag */
|
||||
ARGV_NONE, /* MDOC_Hang */
|
||||
ARGV_NONE, /* MDOC_Ohang */
|
||||
ARGV_NONE, /* MDOC_Inset */
|
||||
ARGV_MULTI, /* MDOC_Column */
|
||||
ARGV_SINGLE, /* MDOC_Width */
|
||||
ARGV_NONE, /* MDOC_Compact */
|
||||
ARGV_NONE, /* MDOC_Std */
|
||||
ARGV_NONE, /* MDOC_Filled */
|
||||
ARGV_NONE, /* MDOC_Words */
|
||||
ARGV_NONE, /* MDOC_Emphasis */
|
||||
ARGV_NONE, /* MDOC_Symbolic */
|
||||
ARGV_NONE /* MDOC_Symbolic */
|
||||
};
|
||||
|
||||
static int mdoc_argflags[MDOC_MAX] = {
|
||||
0, /* Ap */
|
||||
0, /* Dd */
|
||||
0, /* Dt */
|
||||
0, /* Os */
|
||||
0, /* Sh */
|
||||
0, /* Ss */
|
||||
ARGS_DELIM, /* Pp */
|
||||
ARGS_DELIM, /* D1 */
|
||||
ARGS_DELIM, /* Dl */
|
||||
0, /* Bd */
|
||||
0, /* Ed */
|
||||
0, /* Bl */
|
||||
0, /* El */
|
||||
0, /* It */
|
||||
ARGS_DELIM, /* Ad */
|
||||
ARGS_DELIM, /* An */
|
||||
ARGS_DELIM, /* Ar */
|
||||
0, /* Cd */
|
||||
ARGS_DELIM, /* Cm */
|
||||
ARGS_DELIM, /* Dv */
|
||||
ARGS_DELIM, /* Er */
|
||||
ARGS_DELIM, /* Ev */
|
||||
0, /* Ex */
|
||||
ARGS_DELIM, /* Fa */
|
||||
0, /* Fd */
|
||||
ARGS_DELIM, /* Fl */
|
||||
ARGS_DELIM, /* Fn */
|
||||
ARGS_DELIM, /* Ft */
|
||||
ARGS_DELIM, /* Ic */
|
||||
0, /* In */
|
||||
ARGS_DELIM, /* Li */
|
||||
0, /* Nd */
|
||||
ARGS_DELIM, /* Nm */
|
||||
ARGS_DELIM, /* Op */
|
||||
0, /* Ot */
|
||||
ARGS_DELIM, /* Pa */
|
||||
0, /* Rv */
|
||||
ARGS_DELIM, /* St */
|
||||
ARGS_DELIM, /* Va */
|
||||
ARGS_DELIM, /* Vt */
|
||||
ARGS_DELIM, /* Xr */
|
||||
0, /* %A */
|
||||
0, /* %B */
|
||||
0, /* %D */
|
||||
0, /* %I */
|
||||
0, /* %J */
|
||||
0, /* %N */
|
||||
0, /* %O */
|
||||
0, /* %P */
|
||||
0, /* %R */
|
||||
0, /* %T */
|
||||
0, /* %V */
|
||||
ARGS_DELIM, /* Ac */
|
||||
0, /* Ao */
|
||||
ARGS_DELIM, /* Aq */
|
||||
ARGS_DELIM, /* At */
|
||||
ARGS_DELIM, /* Bc */
|
||||
0, /* Bf */
|
||||
0, /* Bo */
|
||||
ARGS_DELIM, /* Bq */
|
||||
ARGS_DELIM, /* Bsx */
|
||||
ARGS_DELIM, /* Bx */
|
||||
0, /* Db */
|
||||
ARGS_DELIM, /* Dc */
|
||||
0, /* Do */
|
||||
ARGS_DELIM, /* Dq */
|
||||
ARGS_DELIM, /* Ec */
|
||||
0, /* Ef */
|
||||
ARGS_DELIM, /* Em */
|
||||
0, /* Eo */
|
||||
ARGS_DELIM, /* Fx */
|
||||
ARGS_DELIM, /* Ms */
|
||||
ARGS_DELIM, /* No */
|
||||
ARGS_DELIM, /* Ns */
|
||||
ARGS_DELIM, /* Nx */
|
||||
ARGS_DELIM, /* Ox */
|
||||
ARGS_DELIM, /* Pc */
|
||||
ARGS_DELIM, /* Pf */
|
||||
0, /* Po */
|
||||
ARGS_DELIM, /* Pq */
|
||||
ARGS_DELIM, /* Qc */
|
||||
ARGS_DELIM, /* Ql */
|
||||
0, /* Qo */
|
||||
ARGS_DELIM, /* Qq */
|
||||
0, /* Re */
|
||||
0, /* Rs */
|
||||
ARGS_DELIM, /* Sc */
|
||||
0, /* So */
|
||||
ARGS_DELIM, /* Sq */
|
||||
0, /* Sm */
|
||||
ARGS_DELIM, /* Sx */
|
||||
ARGS_DELIM, /* Sy */
|
||||
ARGS_DELIM, /* Tn */
|
||||
ARGS_DELIM, /* Ux */
|
||||
ARGS_DELIM, /* Xc */
|
||||
0, /* Xo */
|
||||
0, /* Fo */
|
||||
0, /* Fc */
|
||||
0, /* Oo */
|
||||
ARGS_DELIM, /* Oc */
|
||||
0, /* Bk */
|
||||
0, /* Ek */
|
||||
0, /* Bt */
|
||||
0, /* Hf */
|
||||
0, /* Fr */
|
||||
0, /* Ud */
|
||||
0, /* Lb */
|
||||
ARGS_DELIM, /* Lp */
|
||||
ARGS_DELIM, /* Lk */
|
||||
ARGS_DELIM, /* Mt */
|
||||
ARGS_DELIM, /* Brq */
|
||||
0, /* Bro */
|
||||
ARGS_DELIM, /* Brc */
|
||||
0, /* %C */
|
||||
0, /* Es */
|
||||
0, /* En */
|
||||
0, /* Dx */
|
||||
0, /* %Q */
|
||||
0, /* br */
|
||||
0, /* sp */
|
||||
0, /* %U */
|
||||
0, /* Ta */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Parse an argument from line text. This comes in the form of -key
|
||||
* [value0...], which may either have a single mandatory value, at least
|
||||
* one mandatory value, an optional single value, or no value.
|
||||
*/
|
||||
enum margverr
|
||||
mdoc_argv(struct mdoc *m, int line, enum mdoct tok,
|
||||
struct mdoc_arg **v, int *pos, char *buf)
|
||||
{
|
||||
char *p, sv;
|
||||
struct mdoc_argv tmp;
|
||||
struct mdoc_arg *arg;
|
||||
|
||||
if ('\0' == buf[*pos])
|
||||
return(ARGV_EOLN);
|
||||
|
||||
assert(' ' != buf[*pos]);
|
||||
|
||||
/* Parse through to the first unescaped space. */
|
||||
|
||||
p = &buf[++(*pos)];
|
||||
|
||||
assert(*pos > 0);
|
||||
|
||||
/* LINTED */
|
||||
while (buf[*pos]) {
|
||||
if (' ' == buf[*pos])
|
||||
if ('\\' != buf[*pos - 1])
|
||||
break;
|
||||
(*pos)++;
|
||||
}
|
||||
|
||||
/* XXX - save zeroed byte, if not an argument. */
|
||||
|
||||
sv = '\0';
|
||||
if (buf[*pos]) {
|
||||
sv = buf[*pos];
|
||||
buf[(*pos)++] = '\0';
|
||||
}
|
||||
|
||||
(void)memset(&tmp, 0, sizeof(struct mdoc_argv));
|
||||
tmp.line = line;
|
||||
tmp.pos = *pos;
|
||||
|
||||
/* See if our token accepts the argument. */
|
||||
|
||||
if (MDOC_ARG_MAX == (tmp.arg = argv_a2arg(tok, p))) {
|
||||
/* XXX - restore saved zeroed byte. */
|
||||
if (sv)
|
||||
buf[*pos - 1] = sv;
|
||||
return(ARGV_WORD);
|
||||
}
|
||||
|
||||
while (buf[*pos] && ' ' == buf[*pos])
|
||||
(*pos)++;
|
||||
|
||||
if ( ! argv(m, line, &tmp, pos, buf))
|
||||
return(ARGV_ERROR);
|
||||
|
||||
if (NULL == (arg = *v))
|
||||
arg = *v = mandoc_calloc(1, sizeof(struct mdoc_arg));
|
||||
|
||||
arg->argc++;
|
||||
arg->argv = mandoc_realloc
|
||||
(arg->argv, arg->argc * sizeof(struct mdoc_argv));
|
||||
|
||||
(void)memcpy(&arg->argv[(int)arg->argc - 1],
|
||||
&tmp, sizeof(struct mdoc_argv));
|
||||
|
||||
return(ARGV_ARG);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mdoc_argv_free(struct mdoc_arg *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (NULL == p)
|
||||
return;
|
||||
|
||||
if (p->refcnt) {
|
||||
--(p->refcnt);
|
||||
if (p->refcnt)
|
||||
return;
|
||||
}
|
||||
assert(p->argc);
|
||||
|
||||
for (i = (int)p->argc - 1; i >= 0; i--)
|
||||
mdoc_argn_free(p, i);
|
||||
|
||||
free(p->argv);
|
||||
free(p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mdoc_argn_free(struct mdoc_arg *p, int iarg)
|
||||
{
|
||||
struct mdoc_argv *arg = &p->argv[iarg];
|
||||
int j;
|
||||
|
||||
if (arg->sz && arg->value) {
|
||||
for (j = (int)arg->sz - 1; j >= 0; j--)
|
||||
free(arg->value[j]);
|
||||
free(arg->value);
|
||||
}
|
||||
|
||||
for (--p->argc; iarg < (int)p->argc; iarg++)
|
||||
p->argv[iarg] = p->argv[iarg+1];
|
||||
}
|
||||
|
||||
|
||||
enum margserr
|
||||
mdoc_zargs(struct mdoc *m, int line, int *pos,
|
||||
char *buf, int flags, char **v)
|
||||
{
|
||||
|
||||
return(args(m, line, pos, buf, flags, v));
|
||||
}
|
||||
|
||||
|
||||
enum margserr
|
||||
mdoc_args(struct mdoc *m, int line, int *pos,
|
||||
char *buf, enum mdoct tok, char **v)
|
||||
{
|
||||
int fl;
|
||||
struct mdoc_node *n;
|
||||
|
||||
fl = mdoc_argflags[tok];
|
||||
|
||||
if (MDOC_It != tok)
|
||||
return(args(m, line, pos, buf, fl, v));
|
||||
|
||||
/*
|
||||
* We know that we're in an `It', so it's reasonable to expect
|
||||
* us to be sitting in a `Bl'. Someday this may not be the case
|
||||
* (if we allow random `It's sitting out there), so provide a
|
||||
* safe fall-back into the default behaviour.
|
||||
*/
|
||||
|
||||
for (n = m->last; n; n = n->parent)
|
||||
if (MDOC_Bl == n->tok)
|
||||
break;
|
||||
|
||||
if (n && LIST_column == n->data.Bl.type) {
|
||||
fl |= ARGS_TABSEP;
|
||||
fl &= ~ARGS_DELIM;
|
||||
}
|
||||
|
||||
return(args(m, line, pos, buf, fl, v));
|
||||
}
|
||||
|
||||
|
||||
static enum margserr
|
||||
args(struct mdoc *m, int line, int *pos,
|
||||
char *buf, int fl, char **v)
|
||||
{
|
||||
int i;
|
||||
char *p, *pp;
|
||||
enum margserr rc;
|
||||
enum mdelim d;
|
||||
|
||||
/*
|
||||
* Parse out the terms (like `val' in `.Xx -arg val' or simply
|
||||
* `.Xx val'), which can have all sorts of properties:
|
||||
*
|
||||
* ARGS_DELIM: use special handling if encountering trailing
|
||||
* delimiters in the form of [[::delim::][ ]+]+.
|
||||
*
|
||||
* ARGS_NOWARN: don't post warnings. This is only used when
|
||||
* re-parsing delimiters, as the warnings have already been
|
||||
* posted.
|
||||
*
|
||||
* ARGS_TABSEP: use special handling for tab/`Ta' separated
|
||||
* phrases like in `Bl -column'.
|
||||
*/
|
||||
|
||||
assert(' ' != buf[*pos]);
|
||||
|
||||
if ('\0' == buf[*pos]) {
|
||||
if (MDOC_PPHRASE & m->flags)
|
||||
return(ARGS_EOLN);
|
||||
/*
|
||||
* If we're not in a partial phrase and the flag for
|
||||
* being a phrase literal is still set, the punctuation
|
||||
* is unterminated.
|
||||
*/
|
||||
if (MDOC_PHRASELIT & m->flags)
|
||||
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE))
|
||||
return(ARGS_ERROR);
|
||||
|
||||
m->flags &= ~MDOC_PHRASELIT;
|
||||
return(ARGS_EOLN);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the first character is a closing delimiter and we're to
|
||||
* look for delimited strings, then pass down the buffer seeing
|
||||
* if it follows the pattern of [[::delim::][ ]+]+. Note that
|
||||
* we ONLY care about closing delimiters.
|
||||
*/
|
||||
|
||||
if ((fl & ARGS_DELIM) && DELIM_CLOSE == mdoc_iscdelim(buf[*pos])) {
|
||||
for (i = *pos; buf[i]; ) {
|
||||
d = mdoc_iscdelim(buf[i]);
|
||||
if (DELIM_NONE == d || DELIM_OPEN == d)
|
||||
break;
|
||||
i++;
|
||||
if ('\0' == buf[i] || ' ' != buf[i])
|
||||
break;
|
||||
i++;
|
||||
while (buf[i] && ' ' == buf[i])
|
||||
i++;
|
||||
}
|
||||
|
||||
if ('\0' == buf[i]) {
|
||||
*v = &buf[*pos];
|
||||
if (i && ' ' != buf[i - 1])
|
||||
return(ARGS_PUNCT);
|
||||
if (ARGS_NOWARN & fl)
|
||||
return(ARGS_PUNCT);
|
||||
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
|
||||
return(ARGS_ERROR);
|
||||
return(ARGS_PUNCT);
|
||||
}
|
||||
}
|
||||
|
||||
*v = &buf[*pos];
|
||||
|
||||
/*
|
||||
* First handle TABSEP items, restricted to `Bl -column'. This
|
||||
* ignores conventional token parsing and instead uses tabs or
|
||||
* `Ta' macros to separate phrases. Phrases are parsed again
|
||||
* for arguments at a later phase.
|
||||
*/
|
||||
|
||||
if (ARGS_TABSEP & fl) {
|
||||
/* Scan ahead to tab (can't be escaped). */
|
||||
p = strchr(*v, '\t');
|
||||
pp = NULL;
|
||||
|
||||
/* Scan ahead to unescaped `Ta'. */
|
||||
if ( ! (MDOC_PHRASELIT & m->flags))
|
||||
for (pp = *v; ; pp++) {
|
||||
if (NULL == (pp = strstr(pp, "Ta")))
|
||||
break;
|
||||
if (pp > *v && ' ' != *(pp - 1))
|
||||
continue;
|
||||
if (' ' == *(pp + 2) || '\0' == *(pp + 2))
|
||||
break;
|
||||
}
|
||||
|
||||
/* By default, assume a phrase. */
|
||||
rc = ARGS_PHRASE;
|
||||
|
||||
/*
|
||||
* Adjust new-buffer position to be beyond delimiter
|
||||
* mark (e.g., Ta -> end + 2).
|
||||
*/
|
||||
if (p && pp) {
|
||||
*pos += pp < p ? 2 : 1;
|
||||
rc = pp < p ? ARGS_PHRASE : ARGS_PPHRASE;
|
||||
p = pp < p ? pp : p;
|
||||
} else if (p && ! pp) {
|
||||
rc = ARGS_PPHRASE;
|
||||
*pos += 1;
|
||||
} else if (pp && ! p) {
|
||||
p = pp;
|
||||
*pos += 2;
|
||||
} else {
|
||||
rc = ARGS_PEND;
|
||||
p = strchr(*v, 0);
|
||||
}
|
||||
|
||||
/* Whitespace check for eoln case... */
|
||||
if ('\0' == *p && ' ' == *(p - 1) && ! (ARGS_NOWARN & fl))
|
||||
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
|
||||
return(ARGS_ERROR);
|
||||
|
||||
*pos += (int)(p - *v);
|
||||
|
||||
/* Strip delimiter's preceding whitespace. */
|
||||
pp = p - 1;
|
||||
while (pp > *v && ' ' == *pp) {
|
||||
if (pp > *v && '\\' == *(pp - 1))
|
||||
break;
|
||||
pp--;
|
||||
}
|
||||
*(pp + 1) = 0;
|
||||
|
||||
/* Strip delimiter's proceeding whitespace. */
|
||||
for (pp = &buf[*pos]; ' ' == *pp; pp++, (*pos)++)
|
||||
/* Skip ahead. */ ;
|
||||
|
||||
return(rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a quoted literal. A quote begins with a double-quote
|
||||
* and ends with a double-quote NOT preceded by a double-quote.
|
||||
* Whitespace is NOT involved in literal termination.
|
||||
*/
|
||||
|
||||
if (MDOC_PHRASELIT & m->flags || '\"' == buf[*pos]) {
|
||||
if ( ! (MDOC_PHRASELIT & m->flags))
|
||||
*v = &buf[++(*pos)];
|
||||
|
||||
if (MDOC_PPHRASE & m->flags)
|
||||
m->flags |= MDOC_PHRASELIT;
|
||||
|
||||
for ( ; buf[*pos]; (*pos)++) {
|
||||
if ('\"' != buf[*pos])
|
||||
continue;
|
||||
if ('\"' != buf[*pos + 1])
|
||||
break;
|
||||
(*pos)++;
|
||||
}
|
||||
|
||||
if ('\0' == buf[*pos]) {
|
||||
if (ARGS_NOWARN & fl || MDOC_PPHRASE & m->flags)
|
||||
return(ARGS_QWORD);
|
||||
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE))
|
||||
return(ARGS_ERROR);
|
||||
return(ARGS_QWORD);
|
||||
}
|
||||
|
||||
m->flags &= ~MDOC_PHRASELIT;
|
||||
buf[(*pos)++] = '\0';
|
||||
|
||||
if ('\0' == buf[*pos])
|
||||
return(ARGS_QWORD);
|
||||
|
||||
while (' ' == buf[*pos])
|
||||
(*pos)++;
|
||||
|
||||
if (0 == buf[*pos] && ! (ARGS_NOWARN & fl))
|
||||
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
|
||||
return(ARGS_ERROR);
|
||||
|
||||
return(ARGS_QWORD);
|
||||
}
|
||||
|
||||
/*
|
||||
* A non-quoted term progresses until either the end of line or
|
||||
* a non-escaped whitespace.
|
||||
*/
|
||||
|
||||
for ( ; buf[*pos]; (*pos)++)
|
||||
if (*pos && ' ' == buf[*pos] && '\\' != buf[*pos - 1])
|
||||
break;
|
||||
|
||||
if ('\0' == buf[*pos])
|
||||
return(ARGS_WORD);
|
||||
|
||||
buf[(*pos)++] = '\0';
|
||||
|
||||
while (' ' == buf[*pos])
|
||||
(*pos)++;
|
||||
|
||||
if ('\0' == buf[*pos] && ! (ARGS_NOWARN & fl))
|
||||
if ( ! mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE))
|
||||
return(ARGS_ERROR);
|
||||
|
||||
return(ARGS_WORD);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
argv_a2arg(enum mdoct tok, const char *p)
|
||||
{
|
||||
|
||||
/*
|
||||
* Parse an argument identifier from its text. XXX - this
|
||||
* should really be table-driven to clarify the code.
|
||||
*
|
||||
* If you add an argument to the list, make sure that you
|
||||
* register it here with its one or more macros!
|
||||
*/
|
||||
|
||||
switch (tok) {
|
||||
case (MDOC_An):
|
||||
if (0 == strcmp(p, "split"))
|
||||
return(MDOC_Split);
|
||||
else if (0 == strcmp(p, "nosplit"))
|
||||
return(MDOC_Nosplit);
|
||||
break;
|
||||
|
||||
case (MDOC_Bd):
|
||||
if (0 == strcmp(p, "ragged"))
|
||||
return(MDOC_Ragged);
|
||||
else if (0 == strcmp(p, "unfilled"))
|
||||
return(MDOC_Unfilled);
|
||||
else if (0 == strcmp(p, "filled"))
|
||||
return(MDOC_Filled);
|
||||
else if (0 == strcmp(p, "literal"))
|
||||
return(MDOC_Literal);
|
||||
else if (0 == strcmp(p, "file"))
|
||||
return(MDOC_File);
|
||||
else if (0 == strcmp(p, "offset"))
|
||||
return(MDOC_Offset);
|
||||
else if (0 == strcmp(p, "compact"))
|
||||
return(MDOC_Compact);
|
||||
else if (0 == strcmp(p, "centered"))
|
||||
return(MDOC_Centred);
|
||||
break;
|
||||
|
||||
case (MDOC_Bf):
|
||||
if (0 == strcmp(p, "emphasis"))
|
||||
return(MDOC_Emphasis);
|
||||
else if (0 == strcmp(p, "literal"))
|
||||
return(MDOC_Literal);
|
||||
else if (0 == strcmp(p, "symbolic"))
|
||||
return(MDOC_Symbolic);
|
||||
break;
|
||||
|
||||
case (MDOC_Bk):
|
||||
if (0 == strcmp(p, "words"))
|
||||
return(MDOC_Words);
|
||||
break;
|
||||
|
||||
case (MDOC_Bl):
|
||||
if (0 == strcmp(p, "bullet"))
|
||||
return(MDOC_Bullet);
|
||||
else if (0 == strcmp(p, "dash"))
|
||||
return(MDOC_Dash);
|
||||
else if (0 == strcmp(p, "hyphen"))
|
||||
return(MDOC_Hyphen);
|
||||
else if (0 == strcmp(p, "item"))
|
||||
return(MDOC_Item);
|
||||
else if (0 == strcmp(p, "enum"))
|
||||
return(MDOC_Enum);
|
||||
else if (0 == strcmp(p, "tag"))
|
||||
return(MDOC_Tag);
|
||||
else if (0 == strcmp(p, "diag"))
|
||||
return(MDOC_Diag);
|
||||
else if (0 == strcmp(p, "hang"))
|
||||
return(MDOC_Hang);
|
||||
else if (0 == strcmp(p, "ohang"))
|
||||
return(MDOC_Ohang);
|
||||
else if (0 == strcmp(p, "inset"))
|
||||
return(MDOC_Inset);
|
||||
else if (0 == strcmp(p, "column"))
|
||||
return(MDOC_Column);
|
||||
else if (0 == strcmp(p, "width"))
|
||||
return(MDOC_Width);
|
||||
else if (0 == strcmp(p, "offset"))
|
||||
return(MDOC_Offset);
|
||||
else if (0 == strcmp(p, "compact"))
|
||||
return(MDOC_Compact);
|
||||
else if (0 == strcmp(p, "nested"))
|
||||
return(MDOC_Nested);
|
||||
break;
|
||||
|
||||
case (MDOC_Rv):
|
||||
/* FALLTHROUGH */
|
||||
case (MDOC_Ex):
|
||||
if (0 == strcmp(p, "std"))
|
||||
return(MDOC_Std);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(MDOC_ARG_MAX);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
argv_multi(struct mdoc *m, int line,
|
||||
struct mdoc_argv *v, int *pos, char *buf)
|
||||
{
|
||||
enum margserr ac;
|
||||
char *p;
|
||||
|
||||
for (v->sz = 0; ; v->sz++) {
|
||||
if ('-' == buf[*pos])
|
||||
break;
|
||||
ac = args(m, line, pos, buf, 0, &p);
|
||||
if (ARGS_ERROR == ac)
|
||||
return(0);
|
||||
else if (ARGS_EOLN == ac)
|
||||
break;
|
||||
|
||||
if (0 == v->sz % MULTI_STEP)
|
||||
v->value = mandoc_realloc(v->value,
|
||||
(v->sz + MULTI_STEP) * sizeof(char *));
|
||||
|
||||
v->value[(int)v->sz] = mandoc_strdup(p);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
argv_opt_single(struct mdoc *m, int line,
|
||||
struct mdoc_argv *v, int *pos, char *buf)
|
||||
{
|
||||
enum margserr ac;
|
||||
char *p;
|
||||
|
||||
if ('-' == buf[*pos])
|
||||
return(1);
|
||||
|
||||
ac = args(m, line, pos, buf, 0, &p);
|
||||
if (ARGS_ERROR == ac)
|
||||
return(0);
|
||||
if (ARGS_EOLN == ac)
|
||||
return(1);
|
||||
|
||||
v->sz = 1;
|
||||
v->value = mandoc_malloc(sizeof(char *));
|
||||
v->value[0] = mandoc_strdup(p);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse a single, mandatory value from the stream.
|
||||
*/
|
||||
static int
|
||||
argv_single(struct mdoc *m, int line,
|
||||
struct mdoc_argv *v, int *pos, char *buf)
|
||||
{
|
||||
int ppos;
|
||||
enum margserr ac;
|
||||
char *p;
|
||||
|
||||
ppos = *pos;
|
||||
|
||||
ac = args(m, line, pos, buf, 0, &p);
|
||||
if (ARGS_EOLN == ac) {
|
||||
mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTARGVCOUNT);
|
||||
return(0);
|
||||
} else if (ARGS_ERROR == ac)
|
||||
return(0);
|
||||
|
||||
v->sz = 1;
|
||||
v->value = mandoc_malloc(sizeof(char *));
|
||||
v->value[0] = mandoc_strdup(p);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Determine rules for parsing arguments. Arguments can either accept
|
||||
* no parameters, an optional single parameter, one parameter, or
|
||||
* multiple parameters.
|
||||
*/
|
||||
static int
|
||||
argv(struct mdoc *mdoc, int line,
|
||||
struct mdoc_argv *v, int *pos, char *buf)
|
||||
{
|
||||
|
||||
v->sz = 0;
|
||||
v->value = NULL;
|
||||
|
||||
switch (mdoc_argvflags[v->arg]) {
|
||||
case (ARGV_SINGLE):
|
||||
return(argv_single(mdoc, line, v, pos, buf));
|
||||
case (ARGV_MULTI):
|
||||
return(argv_multi(mdoc, line, v, pos, buf));
|
||||
case (ARGV_OPT_SINGLE):
|
||||
return(argv_opt_single(mdoc, line, v, pos, buf));
|
||||
default:
|
||||
/* ARGV_NONE */
|
||||
break;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
93
commands/mdocml/mdoc_hash.c
Normal file
93
commands/mdocml/mdoc_hash.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* $Id: mdoc_hash.c,v 1.16 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
|
||||
static u_char table[27 * 12];
|
||||
|
||||
/*
|
||||
* XXX - this hash has global scope, so if intended for use as a library
|
||||
* with multiple callers, it will need re-invocation protection.
|
||||
*/
|
||||
void
|
||||
mdoc_hash_init(void)
|
||||
{
|
||||
int i, j, major;
|
||||
const char *p;
|
||||
|
||||
memset(table, UCHAR_MAX, sizeof(table));
|
||||
|
||||
for (i = 0; i < (int)MDOC_MAX; i++) {
|
||||
p = mdoc_macronames[i];
|
||||
|
||||
if (isalpha((u_char)p[1]))
|
||||
major = 12 * (tolower((u_char)p[1]) - 97);
|
||||
else
|
||||
major = 12 * 26;
|
||||
|
||||
for (j = 0; j < 12; j++)
|
||||
if (UCHAR_MAX == table[major + j]) {
|
||||
table[major + j] = (u_char)i;
|
||||
break;
|
||||
}
|
||||
|
||||
assert(j < 12);
|
||||
}
|
||||
}
|
||||
|
||||
enum mdoct
|
||||
mdoc_hash_find(const char *p)
|
||||
{
|
||||
int major, i, j;
|
||||
|
||||
if (0 == p[0])
|
||||
return(MDOC_MAX);
|
||||
if ( ! isalpha((u_char)p[0]) && '%' != p[0])
|
||||
return(MDOC_MAX);
|
||||
|
||||
if (isalpha((u_char)p[1]))
|
||||
major = 12 * (tolower((u_char)p[1]) - 97);
|
||||
else if ('1' == p[1])
|
||||
major = 12 * 26;
|
||||
else
|
||||
return(MDOC_MAX);
|
||||
|
||||
if (p[2] && p[3])
|
||||
return(MDOC_MAX);
|
||||
|
||||
for (j = 0; j < 12; j++) {
|
||||
if (UCHAR_MAX == (i = table[major + j]))
|
||||
break;
|
||||
if (0 == strcmp(p, mdoc_macronames[i]))
|
||||
return((enum mdoct)i);
|
||||
}
|
||||
|
||||
return(MDOC_MAX);
|
||||
}
|
2195
commands/mdocml/mdoc_html.c
Normal file
2195
commands/mdocml/mdoc_html.c
Normal file
File diff suppressed because it is too large
Load diff
1728
commands/mdocml/mdoc_macro.c
Normal file
1728
commands/mdocml/mdoc_macro.c
Normal file
File diff suppressed because it is too large
Load diff
219
commands/mdocml/mdoc_strings.c
Normal file
219
commands/mdocml/mdoc_strings.c
Normal file
|
@ -0,0 +1,219 @@
|
|||
/* $Id: mdoc_strings.c,v 1.23 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
|
||||
static const char * const secnames[SEC__MAX] = {
|
||||
NULL,
|
||||
"NAME",
|
||||
"LIBRARY",
|
||||
"SYNOPSIS",
|
||||
"DESCRIPTION",
|
||||
"IMPLEMENTATION NOTES",
|
||||
"RETURN VALUES",
|
||||
"ENVIRONMENT",
|
||||
"FILES",
|
||||
"EXIT STATUS",
|
||||
"EXAMPLES",
|
||||
"DIAGNOSTICS",
|
||||
"COMPATIBILITY",
|
||||
"ERRORS",
|
||||
"SEE ALSO",
|
||||
"STANDARDS",
|
||||
"HISTORY",
|
||||
"AUTHORS",
|
||||
"CAVEATS",
|
||||
"BUGS",
|
||||
"SECURITY CONSIDERATIONS",
|
||||
NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* FIXME: this is repeated in print_text() (html.c) and term_word()
|
||||
* (term.c).
|
||||
*/
|
||||
enum mdelim
|
||||
mdoc_iscdelim(char p)
|
||||
{
|
||||
|
||||
switch (p) {
|
||||
case('('):
|
||||
/* FALLTHROUGH */
|
||||
case('['):
|
||||
return(DELIM_OPEN);
|
||||
case('|'):
|
||||
return(DELIM_MIDDLE);
|
||||
case('.'):
|
||||
/* FALLTHROUGH */
|
||||
case(','):
|
||||
/* FALLTHROUGH */
|
||||
case(';'):
|
||||
/* FALLTHROUGH */
|
||||
case(':'):
|
||||
/* FALLTHROUGH */
|
||||
case('?'):
|
||||
/* FALLTHROUGH */
|
||||
case('!'):
|
||||
/* FALLTHROUGH */
|
||||
case(')'):
|
||||
/* FALLTHROUGH */
|
||||
case(']'):
|
||||
return(DELIM_CLOSE);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(DELIM_NONE);
|
||||
}
|
||||
|
||||
|
||||
enum mdelim
|
||||
mdoc_isdelim(const char *p)
|
||||
{
|
||||
|
||||
if ('\0' == p[0])
|
||||
return(DELIM_NONE);
|
||||
if ('\0' == p[1])
|
||||
return(mdoc_iscdelim(p[0]));
|
||||
|
||||
/*
|
||||
* XXX; account for groff bubu where the \*(Ba reserved string
|
||||
* is treated in exactly the same way as the vertical bar. This
|
||||
* is the only function that checks for this.
|
||||
*/
|
||||
return(strcmp(p, "\\*(Ba") ? DELIM_NONE : DELIM_MIDDLE);
|
||||
}
|
||||
|
||||
|
||||
enum mdoc_sec
|
||||
mdoc_str2sec(const char *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)SEC__MAX; i++)
|
||||
if (secnames[i] && 0 == strcmp(p, secnames[i]))
|
||||
return((enum mdoc_sec)i);
|
||||
|
||||
return(SEC_CUSTOM);
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: move this into an editable .in file. */
|
||||
size_t
|
||||
mdoc_macro2len(enum mdoct macro)
|
||||
{
|
||||
|
||||
switch (macro) {
|
||||
case(MDOC_Ad):
|
||||
return(12);
|
||||
case(MDOC_Ao):
|
||||
return(12);
|
||||
case(MDOC_An):
|
||||
return(12);
|
||||
case(MDOC_Aq):
|
||||
return(12);
|
||||
case(MDOC_Ar):
|
||||
return(12);
|
||||
case(MDOC_Bo):
|
||||
return(12);
|
||||
case(MDOC_Bq):
|
||||
return(12);
|
||||
case(MDOC_Cd):
|
||||
return(12);
|
||||
case(MDOC_Cm):
|
||||
return(10);
|
||||
case(MDOC_Do):
|
||||
return(10);
|
||||
case(MDOC_Dq):
|
||||
return(12);
|
||||
case(MDOC_Dv):
|
||||
return(12);
|
||||
case(MDOC_Eo):
|
||||
return(12);
|
||||
case(MDOC_Em):
|
||||
return(10);
|
||||
case(MDOC_Er):
|
||||
return(17);
|
||||
case(MDOC_Ev):
|
||||
return(15);
|
||||
case(MDOC_Fa):
|
||||
return(12);
|
||||
case(MDOC_Fl):
|
||||
return(10);
|
||||
case(MDOC_Fo):
|
||||
return(16);
|
||||
case(MDOC_Fn):
|
||||
return(16);
|
||||
case(MDOC_Ic):
|
||||
return(10);
|
||||
case(MDOC_Li):
|
||||
return(16);
|
||||
case(MDOC_Ms):
|
||||
return(6);
|
||||
case(MDOC_Nm):
|
||||
return(10);
|
||||
case(MDOC_No):
|
||||
return(12);
|
||||
case(MDOC_Oo):
|
||||
return(10);
|
||||
case(MDOC_Op):
|
||||
return(14);
|
||||
case(MDOC_Pa):
|
||||
return(32);
|
||||
case(MDOC_Pf):
|
||||
return(12);
|
||||
case(MDOC_Po):
|
||||
return(12);
|
||||
case(MDOC_Pq):
|
||||
return(12);
|
||||
case(MDOC_Ql):
|
||||
return(16);
|
||||
case(MDOC_Qo):
|
||||
return(12);
|
||||
case(MDOC_So):
|
||||
return(12);
|
||||
case(MDOC_Sq):
|
||||
return(12);
|
||||
case(MDOC_Sy):
|
||||
return(6);
|
||||
case(MDOC_Sx):
|
||||
return(16);
|
||||
case(MDOC_Tn):
|
||||
return(10);
|
||||
case(MDOC_Va):
|
||||
return(12);
|
||||
case(MDOC_Vt):
|
||||
return(12);
|
||||
case(MDOC_Xr):
|
||||
return(10);
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return(0);
|
||||
}
|
2109
commands/mdocml/mdoc_term.c
Normal file
2109
commands/mdocml/mdoc_term.c
Normal file
File diff suppressed because it is too large
Load diff
1413
commands/mdocml/mdoc_validate.c
Normal file
1413
commands/mdocml/mdoc_validate.c
Normal file
File diff suppressed because it is too large
Load diff
37
commands/mdocml/msec.c
Normal file
37
commands/mdocml/msec.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* $Id: msec.c,v 1.8 2010/05/17 22:11:42 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
|
||||
#define LINE(x, y) \
|
||||
if (0 == strcmp(p, x)) return(y);
|
||||
|
||||
const char *
|
||||
mdoc_a2msec(const char *p)
|
||||
{
|
||||
|
||||
#include "msec.in"
|
||||
|
||||
return(NULL);
|
||||
}
|
40
commands/mdocml/msec.in
Normal file
40
commands/mdocml/msec.in
Normal file
|
@ -0,0 +1,40 @@
|
|||
/* $Id: msec.in,v 1.6 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* These are all possible manual-section macros and what they correspond
|
||||
* to when rendered as the volume title.
|
||||
*
|
||||
* Be sure to escape strings.
|
||||
*/
|
||||
|
||||
LINE("1", "General Commands Manual")
|
||||
LINE("2", "System Calls Manual")
|
||||
LINE("3", "Library Functions Manual")
|
||||
LINE("3p", "Perl Library Functions Manual")
|
||||
LINE("4", "Kernel Interfaces Manual")
|
||||
LINE("5", "File Formats Manual")
|
||||
LINE("6", "Games Manual")
|
||||
LINE("7", "Miscellaneous Information Manual")
|
||||
LINE("8", "System Manager\'s Manual")
|
||||
LINE("9", "Kernel Developer\'s Manual")
|
||||
LINE("X11", "X11 Developer\'s Manual")
|
||||
LINE("X11R6", "X11 Developer\'s Manual")
|
||||
LINE("unass", "Unassociated")
|
||||
LINE("local", "Local")
|
||||
LINE("draft", "Draft")
|
||||
LINE("paper", "Paper")
|
399
commands/mdocml/out.c
Normal file
399
commands/mdocml/out.c
Normal file
|
@ -0,0 +1,399 @@
|
|||
/* $Id: out.c,v 1.16 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "out.h"
|
||||
|
||||
/* See a2roffdeco(). */
|
||||
#define C2LIM(c, l) do { \
|
||||
(l) = 1; \
|
||||
if ('[' == (c) || '\'' == (c)) \
|
||||
(l) = 0; \
|
||||
else if ('(' == (c)) \
|
||||
(l) = 2; } \
|
||||
while (/* CONSTCOND */ 0)
|
||||
|
||||
/* See a2roffdeco(). */
|
||||
#define C2TERM(c, t) do { \
|
||||
(t) = 0; \
|
||||
if ('\'' == (c)) \
|
||||
(t) = 1; \
|
||||
else if ('[' == (c)) \
|
||||
(t) = 2; \
|
||||
else if ('(' == (c)) \
|
||||
(t) = 3; } \
|
||||
while (/* CONSTCOND */ 0)
|
||||
|
||||
/*
|
||||
* Convert a `scaling unit' to a consistent form, or fail. Scaling
|
||||
* units are documented in groff.7, mdoc.7, man.7.
|
||||
*/
|
||||
int
|
||||
a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
|
||||
{
|
||||
char buf[BUFSIZ], hasd;
|
||||
int i;
|
||||
enum roffscale unit;
|
||||
|
||||
if ('\0' == *src)
|
||||
return(0);
|
||||
|
||||
i = hasd = 0;
|
||||
|
||||
switch (*src) {
|
||||
case ('+'):
|
||||
src++;
|
||||
break;
|
||||
case ('-'):
|
||||
buf[i++] = *src++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ('\0' == *src)
|
||||
return(0);
|
||||
|
||||
while (i < BUFSIZ) {
|
||||
if ( ! isdigit((u_char)*src)) {
|
||||
if ('.' != *src)
|
||||
break;
|
||||
else if (hasd)
|
||||
break;
|
||||
else
|
||||
hasd = 1;
|
||||
}
|
||||
buf[i++] = *src++;
|
||||
}
|
||||
|
||||
if (BUFSIZ == i || (*src && *(src + 1)))
|
||||
return(0);
|
||||
|
||||
buf[i] = '\0';
|
||||
|
||||
switch (*src) {
|
||||
case ('c'):
|
||||
unit = SCALE_CM;
|
||||
break;
|
||||
case ('i'):
|
||||
unit = SCALE_IN;
|
||||
break;
|
||||
case ('P'):
|
||||
unit = SCALE_PC;
|
||||
break;
|
||||
case ('p'):
|
||||
unit = SCALE_PT;
|
||||
break;
|
||||
case ('f'):
|
||||
unit = SCALE_FS;
|
||||
break;
|
||||
case ('v'):
|
||||
unit = SCALE_VS;
|
||||
break;
|
||||
case ('m'):
|
||||
unit = SCALE_EM;
|
||||
break;
|
||||
case ('\0'):
|
||||
if (SCALE_MAX == def)
|
||||
return(0);
|
||||
unit = SCALE_BU;
|
||||
break;
|
||||
case ('u'):
|
||||
unit = SCALE_BU;
|
||||
break;
|
||||
case ('M'):
|
||||
unit = SCALE_MM;
|
||||
break;
|
||||
case ('n'):
|
||||
unit = SCALE_EN;
|
||||
break;
|
||||
default:
|
||||
return(0);
|
||||
}
|
||||
|
||||
if ((dst->scale = atof(buf)) < 0)
|
||||
dst->scale = 0;
|
||||
dst->unit = unit;
|
||||
dst->pt = hasd;
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Correctly writes the time in nroff form, which differs from standard
|
||||
* form in that a space isn't printed in lieu of the extra %e field for
|
||||
* single-digit dates.
|
||||
*/
|
||||
void
|
||||
time2a(time_t t, char *dst, size_t sz)
|
||||
{
|
||||
struct tm tm;
|
||||
char buf[5];
|
||||
char *p;
|
||||
size_t nsz;
|
||||
|
||||
assert(sz > 1);
|
||||
localtime_r(&t, &tm);
|
||||
|
||||
p = dst;
|
||||
nsz = 0;
|
||||
|
||||
dst[0] = '\0';
|
||||
|
||||
if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
|
||||
return;
|
||||
|
||||
p += (int)nsz;
|
||||
sz -= nsz;
|
||||
|
||||
if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
|
||||
return;
|
||||
|
||||
nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
|
||||
|
||||
if (nsz >= sz)
|
||||
return;
|
||||
|
||||
p += (int)nsz;
|
||||
sz -= nsz;
|
||||
|
||||
(void)strftime(p, sz, "%Y", &tm);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns length of parsed string (the leading "\" should NOT be
|
||||
* included). This can be zero if the current character is the nil
|
||||
* terminator. "d" is set to the type of parsed decorator, which may
|
||||
* have an adjoining "word" of size "sz" (e.g., "(ab" -> "ab", 2).
|
||||
*/
|
||||
int
|
||||
a2roffdeco(enum roffdeco *d,
|
||||
const char **word, size_t *sz)
|
||||
{
|
||||
int j, term, lim;
|
||||
char set;
|
||||
const char *wp, *sp;
|
||||
|
||||
*d = DECO_NONE;
|
||||
wp = *word;
|
||||
|
||||
switch ((set = *wp)) {
|
||||
case ('\0'):
|
||||
return(0);
|
||||
|
||||
case ('('):
|
||||
if ('\0' == *(++wp))
|
||||
return(1);
|
||||
if ('\0' == *(wp + 1))
|
||||
return(2);
|
||||
|
||||
*d = DECO_SPECIAL;
|
||||
*sz = 2;
|
||||
*word = wp;
|
||||
return(3);
|
||||
|
||||
case ('F'):
|
||||
/* FALLTHROUGH */
|
||||
case ('f'):
|
||||
/*
|
||||
* FIXME: this needs work and consolidation (it should
|
||||
* follow the sequence that special characters do, for
|
||||
* one), but isn't a priority at the moment. Note, for
|
||||
* one, that in reality \fB != \FB, although here we let
|
||||
* these slip by.
|
||||
*/
|
||||
switch (*(++wp)) {
|
||||
case ('\0'):
|
||||
return(1);
|
||||
case ('3'):
|
||||
/* FALLTHROUGH */
|
||||
case ('B'):
|
||||
*d = DECO_BOLD;
|
||||
return(2);
|
||||
case ('2'):
|
||||
/* FALLTHROUGH */
|
||||
case ('I'):
|
||||
*d = DECO_ITALIC;
|
||||
return(2);
|
||||
case ('P'):
|
||||
*d = DECO_PREVIOUS;
|
||||
return(2);
|
||||
case ('1'):
|
||||
/* FALLTHROUGH */
|
||||
case ('R'):
|
||||
*d = DECO_ROMAN;
|
||||
return(2);
|
||||
case ('('):
|
||||
if ('\0' == *(++wp))
|
||||
return(2);
|
||||
if ('\0' == *(wp + 1))
|
||||
return(3);
|
||||
|
||||
*d = 'F' == set ? DECO_FFONT : DECO_FONT;
|
||||
*sz = 2;
|
||||
*word = wp;
|
||||
return(4);
|
||||
case ('['):
|
||||
*word = ++wp;
|
||||
for (j = 0; *wp && ']' != *wp; wp++, j++)
|
||||
/* Loop... */ ;
|
||||
|
||||
if ('\0' == *wp)
|
||||
return(j + 2);
|
||||
|
||||
*d = 'F' == set ? DECO_FFONT : DECO_FONT;
|
||||
*sz = (size_t)j;
|
||||
return(j + 3);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*d = 'F' == set ? DECO_FFONT : DECO_FONT;
|
||||
*sz = 1;
|
||||
*word = wp;
|
||||
return(2);
|
||||
|
||||
case ('*'):
|
||||
switch (*(++wp)) {
|
||||
case ('\0'):
|
||||
return(1);
|
||||
|
||||
case ('('):
|
||||
if ('\0' == *(++wp))
|
||||
return(2);
|
||||
if ('\0' == *(wp + 1))
|
||||
return(3);
|
||||
|
||||
*d = DECO_RESERVED;
|
||||
*sz = 2;
|
||||
*word = wp;
|
||||
return(4);
|
||||
|
||||
case ('['):
|
||||
*word = ++wp;
|
||||
for (j = 0; *wp && ']' != *wp; wp++, j++)
|
||||
/* Loop... */ ;
|
||||
|
||||
if ('\0' == *wp)
|
||||
return(j + 2);
|
||||
|
||||
*d = DECO_RESERVED;
|
||||
*sz = (size_t)j;
|
||||
return(j + 3);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*d = DECO_RESERVED;
|
||||
*sz = 1;
|
||||
*word = wp;
|
||||
return(2);
|
||||
|
||||
case ('s'):
|
||||
sp = wp;
|
||||
if ('\0' == *(++wp))
|
||||
return(1);
|
||||
|
||||
C2LIM(*wp, lim);
|
||||
C2TERM(*wp, term);
|
||||
|
||||
if (term)
|
||||
wp++;
|
||||
|
||||
*word = wp;
|
||||
|
||||
if (*wp == '+' || *wp == '-')
|
||||
++wp;
|
||||
|
||||
switch (*wp) {
|
||||
case ('\''):
|
||||
/* FALLTHROUGH */
|
||||
case ('['):
|
||||
/* FALLTHROUGH */
|
||||
case ('('):
|
||||
if (term)
|
||||
return((int)(wp - sp));
|
||||
|
||||
C2LIM(*wp, lim);
|
||||
C2TERM(*wp, term);
|
||||
wp++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! isdigit((u_char)*wp))
|
||||
return((int)(wp - sp));
|
||||
|
||||
for (j = 0; isdigit((u_char)*wp); j++) {
|
||||
if (lim && j >= lim)
|
||||
break;
|
||||
++wp;
|
||||
}
|
||||
|
||||
if (term && term < 3) {
|
||||
if (1 == term && *wp != '\'')
|
||||
return((int)(wp - sp));
|
||||
if (2 == term && *wp != ']')
|
||||
return((int)(wp - sp));
|
||||
++wp;
|
||||
}
|
||||
|
||||
*d = DECO_SIZE;
|
||||
return((int)(wp - sp));
|
||||
|
||||
case ('['):
|
||||
*word = ++wp;
|
||||
|
||||
for (j = 0; *wp && ']' != *wp; wp++, j++)
|
||||
/* Loop... */ ;
|
||||
|
||||
if ('\0' == *wp)
|
||||
return(j + 1);
|
||||
|
||||
*d = DECO_SPECIAL;
|
||||
*sz = (size_t)j;
|
||||
return(j + 2);
|
||||
|
||||
case ('c'):
|
||||
*d = DECO_NOSPACE;
|
||||
*sz = 1;
|
||||
return(1);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*d = DECO_SPECIAL;
|
||||
*word = wp;
|
||||
*sz = 1;
|
||||
return(1);
|
||||
}
|
82
commands/mdocml/out.h
Normal file
82
commands/mdocml/out.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/* $Id: out.h,v 1.11 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef OUT_H
|
||||
#define OUT_H
|
||||
|
||||
#define DATESIZ 24
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
enum roffscale {
|
||||
SCALE_CM,
|
||||
SCALE_IN,
|
||||
SCALE_PC,
|
||||
SCALE_PT,
|
||||
SCALE_EM,
|
||||
SCALE_MM,
|
||||
SCALE_EN,
|
||||
SCALE_BU,
|
||||
SCALE_VS,
|
||||
SCALE_FS,
|
||||
SCALE_MAX
|
||||
};
|
||||
|
||||
enum roffdeco {
|
||||
DECO_NONE,
|
||||
DECO_SPECIAL,
|
||||
DECO_RESERVED,
|
||||
DECO_BOLD,
|
||||
DECO_ITALIC,
|
||||
DECO_ROMAN,
|
||||
DECO_PREVIOUS,
|
||||
DECO_SIZE,
|
||||
DECO_NOSPACE,
|
||||
DECO_FONT, /* font */
|
||||
DECO_FFONT, /* font family */
|
||||
DECO_MAX
|
||||
};
|
||||
|
||||
struct roffsu {
|
||||
enum roffscale unit;
|
||||
double scale;
|
||||
int pt;
|
||||
};
|
||||
|
||||
#define SCALE_INVERT(p) \
|
||||
do { (p)->scale = -(p)->scale; } \
|
||||
while (/* CONSTCOND */ 0)
|
||||
|
||||
#define SCALE_VS_INIT(p, v) \
|
||||
do { (p)->unit = SCALE_VS; \
|
||||
(p)->scale = (v); \
|
||||
(p)->pt = 0; } \
|
||||
while (/* CONSTCOND */ 0)
|
||||
|
||||
#define SCALE_HS_INIT(p, v) \
|
||||
do { (p)->unit = SCALE_BU; \
|
||||
(p)->scale = (v); \
|
||||
(p)->pt = 0; } \
|
||||
while (/* CONSTCOND */ 0)
|
||||
|
||||
int a2roffsu(const char *,
|
||||
struct roffsu *, enum roffscale);
|
||||
int a2roffdeco(enum roffdeco *, const char **, size_t *);
|
||||
void time2a(time_t, char *, size_t);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!HTML_H*/
|
156
commands/mdocml/roff.3
Normal file
156
commands/mdocml/roff.3
Normal file
|
@ -0,0 +1,156 @@
|
|||
.\" $Id: roff.3,v 1.1 2010/05/25 22:16:59 kristaps Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: May 25 2010 $
|
||||
.Dt ROFF 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm roff ,
|
||||
.Nm roff_alloc ,
|
||||
.Nm roff_endparse ,
|
||||
.Nm roff_free ,
|
||||
.Nm roff_parseln ,
|
||||
.Nm roff_reset
|
||||
.Nd roff macro compiler library
|
||||
.Sh SYNOPSIS
|
||||
.In mandoc.h
|
||||
.In roff.h
|
||||
.Ft "struct roff *"
|
||||
.Fn roff_alloc "mandocmsg msgs" "void *data"
|
||||
.Ft int
|
||||
.Fn roff_endparse "struct roff *roff"
|
||||
.Ft void
|
||||
.Fn roff_free "struct roff *roff"
|
||||
.Ft "enum rofferr"
|
||||
.Fo roff_parseln
|
||||
.Fa "struct roff *roff"
|
||||
.Fa "int line"
|
||||
.Fa "char **bufp"
|
||||
.Fa "size_t *bufsz"
|
||||
.Fa "int pos"
|
||||
.Fa "int *offs"
|
||||
.Fc
|
||||
.Ft void
|
||||
.Fn roff_reset "struct roff *roff"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
library processes lines of
|
||||
.Xr roff 7
|
||||
input.
|
||||
.Pp
|
||||
In general, applications initiate a parsing sequence with
|
||||
.Fn roff_alloc ,
|
||||
parse each line in a document with
|
||||
.Fn roff_parseln ,
|
||||
close the parsing session with
|
||||
.Fn roff_endparse ,
|
||||
and finally free all allocated memory with
|
||||
.Fn roff_free .
|
||||
The
|
||||
.Fn roff_reset
|
||||
function may be used in order to reset the parser for another input
|
||||
sequence.
|
||||
.Pp
|
||||
The
|
||||
.Fn roff_parseln
|
||||
function should be invoked before passing a line into the
|
||||
.Xr mdoc 3
|
||||
or
|
||||
.Xr man 3
|
||||
libraries.
|
||||
.Pp
|
||||
See the
|
||||
.Sx EXAMPLES
|
||||
section for a full example.
|
||||
.Sh REFERENCE
|
||||
This section further defines the
|
||||
.Sx Types
|
||||
and
|
||||
.Sx Functions
|
||||
available to programmers.
|
||||
.Ss Types
|
||||
Functions (see
|
||||
.Sx Functions )
|
||||
may use the following types:
|
||||
.Bl -ohang
|
||||
.It Vt "enum rofferr"
|
||||
Instructions for further processing to the caller of
|
||||
.Fn roff_parseln .
|
||||
.It Vt struct roff
|
||||
An opaque type defined in
|
||||
.Pa roff.c .
|
||||
Its values are only used privately within the library.
|
||||
.It Vt mandocmsg
|
||||
A function callback type defined in
|
||||
.Pa mandoc.h .
|
||||
.El
|
||||
.Ss Functions
|
||||
Function descriptions follow:
|
||||
.Bl -ohang
|
||||
.It Fn roff_alloc
|
||||
Allocates a parsing structure.
|
||||
The
|
||||
.Fa data
|
||||
pointer is passed to
|
||||
.Fa msgs .
|
||||
The
|
||||
.Fa pflags
|
||||
arguments are defined in
|
||||
.Pa roff.h .
|
||||
Returns NULL on failure.
|
||||
If non-NULL, the pointer must be freed with
|
||||
.Fn roff_free .
|
||||
.It Fn roff_reset
|
||||
Reset the parser for another parse routine.
|
||||
After its use,
|
||||
.Fn roff_parseln
|
||||
behaves as if invoked for the first time.
|
||||
.It Fn roff_free
|
||||
Free all resources of a parser.
|
||||
The pointer is no longer valid after invocation.
|
||||
.It Fn roff_parseln
|
||||
Parse a nil-terminated line of input.
|
||||
The character array
|
||||
.Fa bufp
|
||||
may be modified or reallocated within this function.
|
||||
In the latter case,
|
||||
.Fa bufsz
|
||||
will be modified accordingly.
|
||||
The
|
||||
.Fa offs
|
||||
pointer will be modified if the line start during subsequent processing
|
||||
of the line is not at the zeroth index.
|
||||
This line should not contain the trailing newline.
|
||||
Returns 0 on failure, 1 on success.
|
||||
.It Fn roff_endparse
|
||||
Signals that the parse is complete.
|
||||
Returns 0 on failure, 1 on success.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
See
|
||||
.Pa main.c
|
||||
in the source distribution for an example of usage.
|
||||
.Sh SEE ALSO
|
||||
.Xr mandoc 1 ,
|
||||
.Xr man 3 ,
|
||||
.Xr mdoc 3 ,
|
||||
.Xr roff 7
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
library was written by
|
||||
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
|
304
commands/mdocml/roff.7
Normal file
304
commands/mdocml/roff.7
Normal file
|
@ -0,0 +1,304 @@
|
|||
.\" $Id: roff.7,v 1.9 2010/06/10 21:42:02 kristaps Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: June 10 2010 $
|
||||
.Dt ROFF 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm roff
|
||||
.Nd roff language reference
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm roff
|
||||
language is a general-purpose text-formatting language. The purpose of
|
||||
this document is to consistently describe those language constructs
|
||||
accepted by the
|
||||
.Xr mandoc 1
|
||||
utility. It is a work in progress.
|
||||
.Pp
|
||||
An
|
||||
.Nm
|
||||
document follows simple rules: lines beginning with the control
|
||||
characters
|
||||
.Sq \.
|
||||
or
|
||||
.Sq \(aq
|
||||
are parsed for macros. Other lines are interpreted within the scope of
|
||||
prior macros:
|
||||
.Bd -literal -offset indent
|
||||
\&.xx Macro lines change control state.
|
||||
Other lines are interpreted within the current state.
|
||||
.Ed
|
||||
.Sh LANGUAGE SYNTAX
|
||||
.Nm
|
||||
documents may contain only graphable 7-bit ASCII characters, the space
|
||||
character, and, in certain circumstances, the tab character. All
|
||||
manuals must have
|
||||
.Ux
|
||||
line terminators.
|
||||
.Sh MACRO SYNTAX
|
||||
Macros are arbitrary in length and begin with a control character ,
|
||||
.Sq \.
|
||||
or
|
||||
.Sq \(aq ,
|
||||
at the beginning of the line.
|
||||
An arbitrary amount of whitespace may sit between the control character
|
||||
and the macro name.
|
||||
Thus, the following are equivalent:
|
||||
.Bd -literal -offset indent
|
||||
\&.if
|
||||
\&.\ \ \ \&if
|
||||
.Ed
|
||||
.Sh REFERENCE
|
||||
This section is a canonical reference of all macros, arranged
|
||||
alphabetically.
|
||||
.Ss \&am
|
||||
The syntax of this macro is the same as that of
|
||||
.Sx \&ig ,
|
||||
except that a leading argument must be specified.
|
||||
It is ignored, as are its children.
|
||||
.Ss \&ami
|
||||
The syntax of this macro is the same as that of
|
||||
.Sx \&ig ,
|
||||
except that a leading argument must be specified.
|
||||
It is ignored, as are its children.
|
||||
.Ss \&am1
|
||||
The syntax of this macro is the same as that of
|
||||
.Sx \&ig ,
|
||||
except that a leading argument must be specified.
|
||||
It is ignored, as are its children.
|
||||
.Ss \&de
|
||||
The syntax of this macro is the same as that of
|
||||
.Sx \&ig ,
|
||||
except that a leading argument must be specified.
|
||||
It is ignored, as are its children.
|
||||
.Ss \&dei
|
||||
The syntax of this macro is the same as that of
|
||||
.Sx \&ig ,
|
||||
except that a leading argument must be specified.
|
||||
It is ignored, as are its children.
|
||||
.Ss \&ds
|
||||
Define a string.
|
||||
This macro is intended to have two arguments,
|
||||
the name of the string to define and its content.
|
||||
Currently, it is ignored including its arguments,
|
||||
and the number of arguments is not checked.
|
||||
.Ss \&de1
|
||||
The syntax of this macro is the same as that of
|
||||
.Sx \&ig ,
|
||||
except that a leading argument must be specified.
|
||||
It is ignored, as are its children.
|
||||
.Ss \&el
|
||||
The
|
||||
.Qq else
|
||||
half of an if/else conditional.
|
||||
Pops a result off the stack of conditional evaluations pushed by
|
||||
.Sx \&ie
|
||||
and uses it as its conditional.
|
||||
If no stack entries are present (e.g., due to no prior
|
||||
.Sx \&ie
|
||||
calls)
|
||||
then false is assumed.
|
||||
The syntax of this macro is similar to
|
||||
.Sx \&if
|
||||
except that the conditional is missing.
|
||||
.Ss \&ie
|
||||
The
|
||||
.Qq if
|
||||
half of an if/else conditional.
|
||||
The result of the conditional is pushed into a stack used by subsequent
|
||||
invocations of
|
||||
.Sx \&el ,
|
||||
which may be separated by any intervening input (or not exist at all).
|
||||
Its syntax is equivalent to
|
||||
.Sx \&if .
|
||||
.Ss \&if
|
||||
Begins a conditional.
|
||||
Right now, the conditional evaluates to true
|
||||
if and only if it starts with the letter
|
||||
.Sy n ,
|
||||
indicating processing in
|
||||
.Xr nroff 1
|
||||
style as opposed to
|
||||
.Xr troff 1
|
||||
style.
|
||||
If a conditional is false, its children are not processed, but are
|
||||
syntactically interpreted to preserve the integrity of the input
|
||||
document.
|
||||
Thus,
|
||||
.Pp
|
||||
.D1 \&.if t \e .ig
|
||||
.Pp
|
||||
will discard the
|
||||
.Sq \&.ig ,
|
||||
which may lead to interesting results, but
|
||||
.Pp
|
||||
.D1 \&.if t \e .if t \e{\e
|
||||
.Pp
|
||||
will continue to syntactically interpret to the block close of the final
|
||||
conditional.
|
||||
Sub-conditionals, in this case, obviously inherit the truth value of
|
||||
the parent.
|
||||
This macro has the following syntax:
|
||||
.Pp
|
||||
.Bd -literal -offset indent -compact
|
||||
\&.if COND \e{\e
|
||||
BODY...
|
||||
\&.\e}
|
||||
.Ed
|
||||
.Bd -literal -offset indent -compact
|
||||
\&.if COND \e{ BODY
|
||||
BODY... \e}
|
||||
.Ed
|
||||
.Bd -literal -offset indent -compact
|
||||
\&.if COND \e{ BODY
|
||||
BODY...
|
||||
\&.\e}
|
||||
.Ed
|
||||
.Bd -literal -offset indent -compact
|
||||
\&.if COND \e
|
||||
BODY
|
||||
.Ed
|
||||
.Pp
|
||||
COND is a conditional statement.
|
||||
roff allows for complicated conditionals; mandoc is much simpler.
|
||||
At this time, mandoc supports only
|
||||
.Sq n ,
|
||||
evaluating to true;
|
||||
and
|
||||
.Sq t ,
|
||||
.Sq e ,
|
||||
and
|
||||
.Sq o ,
|
||||
evaluating to false.
|
||||
All other invocations are read up to the next end of line or space and
|
||||
evaluate as false.
|
||||
.Pp
|
||||
If the BODY section is begun by an escaped brace
|
||||
.Sq \e{ ,
|
||||
scope continues until a closing-brace macro
|
||||
.Sq \.\e} .
|
||||
If the BODY is not enclosed in braces, scope continues until the next
|
||||
macro or word.
|
||||
If the COND is followed by a BODY on the same line, whether after a
|
||||
brace or not, then macros
|
||||
.Em must
|
||||
begin with a control character.
|
||||
It is generally more intuitive, in this case, to write
|
||||
.Bd -literal -offset indent
|
||||
\&.if COND \e{\e
|
||||
\&.foo
|
||||
bar
|
||||
\&.\e}
|
||||
.Ed
|
||||
.Pp
|
||||
than having the macro follow as
|
||||
.Pp
|
||||
.D1 \&.if COND \e{ .foo
|
||||
.Pp
|
||||
The scope of a conditional is always parsed, but only executed if the
|
||||
conditional evaluates to true.
|
||||
.Pp
|
||||
Note that text subsequent a
|
||||
.Sq \&.\e}
|
||||
macro is discarded.
|
||||
Furthermore, if an explicit closing sequence
|
||||
.Sq \e}
|
||||
is specified in a free-form line, the entire line is accepted within the
|
||||
scope of the prior macro, not only the text preceding the close, with the
|
||||
.Sq \e}
|
||||
collapsing into a zero-width space.
|
||||
.Ss \&ig
|
||||
Ignore input.
|
||||
Accepts the following syntax:
|
||||
.Pp
|
||||
.Bd -literal -offset indent -compact
|
||||
\&.ig
|
||||
BODY...
|
||||
\&..
|
||||
.Ed
|
||||
.Bd -literal -offset indent -compact
|
||||
\&.ig END
|
||||
BODY...
|
||||
\&.END
|
||||
.Ed
|
||||
.Pp
|
||||
In the first case, input is ignored until a
|
||||
.Sq \&..
|
||||
macro is encountered on its own line.
|
||||
In the second case, input is ignored until a
|
||||
.Sq \&.END
|
||||
is encountered.
|
||||
Text subsequent the
|
||||
.Sq \&.END
|
||||
or
|
||||
.Sq \&..
|
||||
is discarded.
|
||||
.Pp
|
||||
Do not use the escape
|
||||
.Sq \e
|
||||
anywhere in the definition of END.
|
||||
It causes very strange behaviour.
|
||||
Furthermore, if you redefine a
|
||||
.Nm
|
||||
macro, such as
|
||||
.Pp
|
||||
.D1 \&.ig if
|
||||
.Pp
|
||||
the subsequent invocation of
|
||||
.Sx \&if
|
||||
will first signify the end of comment, then be invoked as a macro.
|
||||
This behaviour really shouldn't be counted upon.
|
||||
.Ss \&rm
|
||||
Remove a request, macro or string.
|
||||
This macro is intended to have one argument,
|
||||
the name of the request, macro or string to be undefined.
|
||||
Currently, it is ignored including its arguments,
|
||||
and the number of arguments is not checked.
|
||||
.Ss \&tr
|
||||
Output character translation.
|
||||
This macro is intended to have one argument,
|
||||
consisting of an even number of characters.
|
||||
Currently, it is ignored including its arguments,
|
||||
and the number of arguments is not checked.
|
||||
.Sh COMPATIBILITY
|
||||
This section documents compatibility between mandoc and other other
|
||||
troff implementations, at this time limited to GNU troff
|
||||
.Pq Qq groff .
|
||||
The term
|
||||
.Qq historic groff
|
||||
refers to groff versions before the
|
||||
.Pa doc.tmac
|
||||
file re-write
|
||||
.Pq somewhere between 1.15 and 1.19 .
|
||||
.Pp
|
||||
.Bl -dash -compact
|
||||
.It
|
||||
Historic groff did not accept white-space buffering the custom END tag
|
||||
for the
|
||||
.Sx \&ig
|
||||
macro.
|
||||
.It
|
||||
The
|
||||
.Sx \&if
|
||||
and family would print funny white-spaces with historic groff when
|
||||
depending on next-line syntax.
|
||||
.El
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Nm
|
||||
reference was written by
|
||||
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
|
857
commands/mdocml/roff.c
Normal file
857
commands/mdocml/roff.c
Normal file
|
@ -0,0 +1,857 @@
|
|||
/* $Id: roff.c,v 1.88 2010/06/10 21:42:02 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "roff.h"
|
||||
|
||||
#define RSTACK_MAX 128
|
||||
|
||||
#define ROFF_CTL(c) \
|
||||
('.' == (c) || '\'' == (c))
|
||||
|
||||
#ifndef __minix
|
||||
#if 1
|
||||
#define ROFF_DEBUG(fmt, args...) \
|
||||
do { /* Nothing. */ } while (/*CONSTCOND*/ 0)
|
||||
#else
|
||||
#define ROFF_DEBUG(fmt, args...) \
|
||||
do { fprintf(stderr, fmt , ##args); } while (/*CONSTCOND*/ 0)
|
||||
#endif
|
||||
#else
|
||||
void do_nothing(char *fmt, ...)
|
||||
{
|
||||
}
|
||||
#if 1
|
||||
#define ROFF_DEBUG do_nothing
|
||||
#else
|
||||
#define ROFF_DEBUG fprintf
|
||||
#endif
|
||||
#endif
|
||||
|
||||
enum rofft {
|
||||
ROFF_am,
|
||||
ROFF_ami,
|
||||
ROFF_am1,
|
||||
ROFF_de,
|
||||
ROFF_dei,
|
||||
ROFF_de1,
|
||||
ROFF_ds,
|
||||
ROFF_el,
|
||||
ROFF_ie,
|
||||
ROFF_if,
|
||||
ROFF_ig,
|
||||
ROFF_rm,
|
||||
ROFF_tr,
|
||||
ROFF_cblock,
|
||||
ROFF_ccond,
|
||||
ROFF_MAX
|
||||
};
|
||||
|
||||
enum roffrule {
|
||||
ROFFRULE_ALLOW,
|
||||
ROFFRULE_DENY
|
||||
};
|
||||
|
||||
struct roff {
|
||||
struct roffnode *last; /* leaf of stack */
|
||||
mandocmsg msg; /* err/warn/fatal messages */
|
||||
void *data; /* privdata for messages */
|
||||
enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */
|
||||
int rstackpos; /* position in rstack */
|
||||
};
|
||||
|
||||
struct roffnode {
|
||||
enum rofft tok; /* type of node */
|
||||
struct roffnode *parent; /* up one in stack */
|
||||
int line; /* parse line */
|
||||
int col; /* parse col */
|
||||
char *end; /* end-rules: custom token */
|
||||
int endspan; /* end-rules: next-line or infty */
|
||||
enum roffrule rule; /* current evaluation rule */
|
||||
};
|
||||
|
||||
#define ROFF_ARGS struct roff *r, /* parse ctx */ \
|
||||
enum rofft tok, /* tok of macro */ \
|
||||
char **bufp, /* input buffer */ \
|
||||
size_t *szp, /* size of input buffer */ \
|
||||
int ln, /* parse line */ \
|
||||
int ppos, /* original pos in buffer */ \
|
||||
int pos, /* current pos in buffer */ \
|
||||
int *offs /* reset offset of buffer data */
|
||||
|
||||
typedef enum rofferr (*roffproc)(ROFF_ARGS);
|
||||
|
||||
struct roffmac {
|
||||
const char *name; /* macro name */
|
||||
roffproc proc; /* process new macro */
|
||||
roffproc text; /* process as child text of macro */
|
||||
roffproc sub; /* process as child of macro */
|
||||
int flags;
|
||||
#define ROFFMAC_STRUCT (1 << 0) /* always interpret */
|
||||
struct roffmac *next;
|
||||
};
|
||||
|
||||
static enum rofferr roff_block(ROFF_ARGS);
|
||||
static enum rofferr roff_block_text(ROFF_ARGS);
|
||||
static enum rofferr roff_block_sub(ROFF_ARGS);
|
||||
static enum rofferr roff_cblock(ROFF_ARGS);
|
||||
static enum rofferr roff_ccond(ROFF_ARGS);
|
||||
static enum rofferr roff_cond(ROFF_ARGS);
|
||||
static enum rofferr roff_cond_text(ROFF_ARGS);
|
||||
static enum rofferr roff_cond_sub(ROFF_ARGS);
|
||||
static enum roffrule roff_evalcond(const char *, int *);
|
||||
static enum rofferr roff_line(ROFF_ARGS);
|
||||
|
||||
/* See roff_hash_find() */
|
||||
|
||||
#define ASCII_HI 126
|
||||
#define ASCII_LO 33
|
||||
#define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
|
||||
|
||||
static struct roffmac *hash[HASHWIDTH];
|
||||
|
||||
static struct roffmac roffs[ROFF_MAX] = {
|
||||
{ "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
|
||||
{ "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
|
||||
{ "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
|
||||
{ "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
|
||||
{ "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
|
||||
{ "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
|
||||
{ "ds", roff_line, NULL, NULL, 0, NULL },
|
||||
{ "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
|
||||
{ "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
|
||||
{ "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
|
||||
{ "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
|
||||
{ "rm", roff_line, NULL, NULL, 0, NULL },
|
||||
{ "tr", roff_line, NULL, NULL, 0, NULL },
|
||||
{ ".", roff_cblock, NULL, NULL, 0, NULL },
|
||||
{ "\\}", roff_ccond, NULL, NULL, 0, NULL },
|
||||
};
|
||||
|
||||
static void roff_free1(struct roff *);
|
||||
static enum rofft roff_hash_find(const char *);
|
||||
static void roff_hash_init(void);
|
||||
static void roffnode_cleanscope(struct roff *);
|
||||
static int roffnode_push(struct roff *,
|
||||
enum rofft, int, int);
|
||||
static void roffnode_pop(struct roff *);
|
||||
static enum rofft roff_parse(const char *, int *);
|
||||
|
||||
/* See roff_hash_find() */
|
||||
#define ROFF_HASH(p) (p[0] - ASCII_LO)
|
||||
|
||||
static void
|
||||
roff_hash_init(void)
|
||||
{
|
||||
struct roffmac *n;
|
||||
int buc, i;
|
||||
|
||||
for (i = 0; i < (int)ROFF_MAX; i++) {
|
||||
assert(roffs[i].name[0] >= ASCII_LO);
|
||||
assert(roffs[i].name[0] <= ASCII_HI);
|
||||
|
||||
buc = ROFF_HASH(roffs[i].name);
|
||||
|
||||
if (NULL != (n = hash[buc])) {
|
||||
for ( ; n->next; n = n->next)
|
||||
/* Do nothing. */ ;
|
||||
n->next = &roffs[i];
|
||||
} else
|
||||
hash[buc] = &roffs[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Look up a roff token by its name. Returns ROFF_MAX if no macro by
|
||||
* the nil-terminated string name could be found.
|
||||
*/
|
||||
static enum rofft
|
||||
roff_hash_find(const char *p)
|
||||
{
|
||||
int buc;
|
||||
struct roffmac *n;
|
||||
|
||||
/*
|
||||
* libroff has an extremely simple hashtable, for the time
|
||||
* being, which simply keys on the first character, which must
|
||||
* be printable, then walks a chain. It works well enough until
|
||||
* optimised.
|
||||
*/
|
||||
|
||||
if (p[0] < ASCII_LO || p[0] > ASCII_HI)
|
||||
return(ROFF_MAX);
|
||||
|
||||
buc = ROFF_HASH(p);
|
||||
|
||||
if (NULL == (n = hash[buc]))
|
||||
return(ROFF_MAX);
|
||||
for ( ; n; n = n->next)
|
||||
if (0 == strcmp(n->name, p))
|
||||
return((enum rofft)(n - roffs));
|
||||
|
||||
return(ROFF_MAX);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pop the current node off of the stack of roff instructions currently
|
||||
* pending.
|
||||
*/
|
||||
static void
|
||||
roffnode_pop(struct roff *r)
|
||||
{
|
||||
struct roffnode *p;
|
||||
|
||||
assert(r->last);
|
||||
p = r->last;
|
||||
|
||||
if (ROFF_el == p->tok)
|
||||
if (r->rstackpos > -1)
|
||||
r->rstackpos--;
|
||||
|
||||
r->last = r->last->parent;
|
||||
if (p->end)
|
||||
free(p->end);
|
||||
free(p);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Push a roff node onto the instruction stack. This must later be
|
||||
* removed with roffnode_pop().
|
||||
*/
|
||||
static int
|
||||
roffnode_push(struct roff *r, enum rofft tok, int line, int col)
|
||||
{
|
||||
struct roffnode *p;
|
||||
|
||||
if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
|
||||
(*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
|
||||
return(0);
|
||||
}
|
||||
|
||||
p->tok = tok;
|
||||
p->parent = r->last;
|
||||
p->line = line;
|
||||
p->col = col;
|
||||
p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
|
||||
|
||||
r->last = p;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
roff_free1(struct roff *r)
|
||||
{
|
||||
|
||||
while (r->last)
|
||||
roffnode_pop(r);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
roff_reset(struct roff *r)
|
||||
{
|
||||
|
||||
roff_free1(r);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
roff_free(struct roff *r)
|
||||
{
|
||||
|
||||
roff_free1(r);
|
||||
free(r);
|
||||
}
|
||||
|
||||
|
||||
struct roff *
|
||||
roff_alloc(const mandocmsg msg, void *data)
|
||||
{
|
||||
struct roff *r;
|
||||
|
||||
if (NULL == (r = calloc(1, sizeof(struct roff)))) {
|
||||
(*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
|
||||
return(0);
|
||||
}
|
||||
|
||||
r->msg = msg;
|
||||
r->data = data;
|
||||
r->rstackpos = -1;
|
||||
|
||||
roff_hash_init();
|
||||
return(r);
|
||||
}
|
||||
|
||||
|
||||
enum rofferr
|
||||
roff_parseln(struct roff *r, int ln,
|
||||
char **bufp, size_t *szp, int pos, int *offs)
|
||||
{
|
||||
enum rofft t;
|
||||
int ppos;
|
||||
|
||||
/*
|
||||
* First, if a scope is open and we're not a macro, pass the
|
||||
* text through the macro's filter. If a scope isn't open and
|
||||
* we're not a macro, just let it through.
|
||||
*/
|
||||
|
||||
if (r->last && ! ROFF_CTL((*bufp)[pos])) {
|
||||
t = r->last->tok;
|
||||
assert(roffs[t].text);
|
||||
|
||||
ROFF_DEBUG("roff: intercept scoped text: %s, [%s]\n",
|
||||
roffs[t].name, &(*bufp)[pos]);
|
||||
return((*roffs[t].text)
|
||||
(r, t, bufp, szp, ln, pos, pos, offs));
|
||||
} else if ( ! ROFF_CTL((*bufp)[pos])) {
|
||||
ROFF_DEBUG("roff: pass non-scoped text: [%s]\n",
|
||||
&(*bufp)[pos]);
|
||||
return(ROFF_CONT);
|
||||
}
|
||||
|
||||
/*
|
||||
* If a scope is open, go to the child handler for that macro,
|
||||
* as it may want to preprocess before doing anything with it.
|
||||
*/
|
||||
|
||||
if (r->last) {
|
||||
t = r->last->tok;
|
||||
assert(roffs[t].sub);
|
||||
ROFF_DEBUG("roff: intercept scoped context: %s\n",
|
||||
roffs[t].name);
|
||||
return((*roffs[t].sub)
|
||||
(r, t, bufp, szp, ln, pos, pos, offs));
|
||||
}
|
||||
|
||||
/*
|
||||
* Lastly, as we've no scope open, try to look up and execute
|
||||
* the new macro. If no macro is found, simply return and let
|
||||
* the compilers handle it.
|
||||
*/
|
||||
|
||||
ppos = pos;
|
||||
if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
|
||||
ROFF_DEBUG("roff: pass non-scoped non-macro: [%s]\n",
|
||||
&(*bufp)[pos]);
|
||||
return(ROFF_CONT);
|
||||
}
|
||||
|
||||
ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n",
|
||||
roffs[t].name, &(*bufp)[pos]);
|
||||
assert(roffs[t].proc);
|
||||
return((*roffs[t].proc)
|
||||
(r, t, bufp, szp, ln, ppos, pos, offs));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
roff_endparse(struct roff *r)
|
||||
{
|
||||
|
||||
if (NULL == r->last)
|
||||
return(1);
|
||||
return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
|
||||
r->last->col, NULL));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse a roff node's type from the input buffer. This must be in the
|
||||
* form of ".foo xxx" in the usual way.
|
||||
*/
|
||||
static enum rofft
|
||||
roff_parse(const char *buf, int *pos)
|
||||
{
|
||||
int j;
|
||||
char mac[5];
|
||||
enum rofft t;
|
||||
|
||||
assert(ROFF_CTL(buf[*pos]));
|
||||
(*pos)++;
|
||||
|
||||
while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
|
||||
(*pos)++;
|
||||
|
||||
if ('\0' == buf[*pos])
|
||||
return(ROFF_MAX);
|
||||
|
||||
for (j = 0; j < 4; j++, (*pos)++)
|
||||
if ('\0' == (mac[j] = buf[*pos]))
|
||||
break;
|
||||
else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))
|
||||
break;
|
||||
|
||||
if (j == 4 || j < 1)
|
||||
return(ROFF_MAX);
|
||||
|
||||
mac[j] = '\0';
|
||||
|
||||
if (ROFF_MAX == (t = roff_hash_find(mac)))
|
||||
return(t);
|
||||
|
||||
while (buf[*pos] && ' ' == buf[*pos])
|
||||
(*pos)++;
|
||||
|
||||
return(t);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum rofferr
|
||||
roff_cblock(ROFF_ARGS)
|
||||
{
|
||||
|
||||
/*
|
||||
* A block-close `..' should only be invoked as a child of an
|
||||
* ignore macro, otherwise raise a warning and just ignore it.
|
||||
*/
|
||||
|
||||
if (NULL == r->last) {
|
||||
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
|
||||
return(ROFF_ERR);
|
||||
return(ROFF_IGN);
|
||||
}
|
||||
|
||||
switch (r->last->tok) {
|
||||
case (ROFF_am):
|
||||
/* FALLTHROUGH */
|
||||
case (ROFF_ami):
|
||||
/* FALLTHROUGH */
|
||||
case (ROFF_am1):
|
||||
/* FALLTHROUGH */
|
||||
case (ROFF_de):
|
||||
/* FALLTHROUGH */
|
||||
case (ROFF_dei):
|
||||
/* FALLTHROUGH */
|
||||
case (ROFF_de1):
|
||||
/* FALLTHROUGH */
|
||||
case (ROFF_ig):
|
||||
break;
|
||||
default:
|
||||
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
|
||||
return(ROFF_ERR);
|
||||
return(ROFF_IGN);
|
||||
}
|
||||
|
||||
if ((*bufp)[pos])
|
||||
if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
|
||||
return(ROFF_ERR);
|
||||
|
||||
roffnode_pop(r);
|
||||
roffnode_cleanscope(r);
|
||||
return(ROFF_IGN);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
roffnode_cleanscope(struct roff *r)
|
||||
{
|
||||
|
||||
while (r->last) {
|
||||
if (--r->last->endspan < 0)
|
||||
break;
|
||||
roffnode_pop(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum rofferr
|
||||
roff_ccond(ROFF_ARGS)
|
||||
{
|
||||
|
||||
if (NULL == r->last) {
|
||||
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
|
||||
return(ROFF_ERR);
|
||||
return(ROFF_IGN);
|
||||
}
|
||||
|
||||
switch (r->last->tok) {
|
||||
case (ROFF_el):
|
||||
/* FALLTHROUGH */
|
||||
case (ROFF_ie):
|
||||
/* FALLTHROUGH */
|
||||
case (ROFF_if):
|
||||
break;
|
||||
default:
|
||||
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
|
||||
return(ROFF_ERR);
|
||||
return(ROFF_IGN);
|
||||
}
|
||||
|
||||
if (r->last->endspan > -1) {
|
||||
if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
|
||||
return(ROFF_ERR);
|
||||
return(ROFF_IGN);
|
||||
}
|
||||
|
||||
if ((*bufp)[pos])
|
||||
if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
|
||||
return(ROFF_ERR);
|
||||
|
||||
roffnode_pop(r);
|
||||
roffnode_cleanscope(r);
|
||||
return(ROFF_IGN);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum rofferr
|
||||
roff_block(ROFF_ARGS)
|
||||
{
|
||||
int sv;
|
||||
size_t sz;
|
||||
|
||||
if (ROFF_ig != tok && '\0' == (*bufp)[pos]) {
|
||||
if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
|
||||
return(ROFF_ERR);
|
||||
return(ROFF_IGN);
|
||||
} else if (ROFF_ig != tok) {
|
||||
while ((*bufp)[pos] && ' ' != (*bufp)[pos])
|
||||
pos++;
|
||||
while (' ' == (*bufp)[pos])
|
||||
pos++;
|
||||
}
|
||||
|
||||
if ( ! roffnode_push(r, tok, ln, ppos))
|
||||
return(ROFF_ERR);
|
||||
|
||||
if ('\0' == (*bufp)[pos])
|
||||
return(ROFF_IGN);
|
||||
|
||||
sv = pos;
|
||||
while ((*bufp)[pos] && ' ' != (*bufp)[pos] &&
|
||||
'\t' != (*bufp)[pos])
|
||||
pos++;
|
||||
|
||||
/*
|
||||
* Note: groff does NOT like escape characters in the input.
|
||||
* Instead of detecting this, we're just going to let it fly and
|
||||
* to hell with it.
|
||||
*/
|
||||
|
||||
assert(pos > sv);
|
||||
sz = (size_t)(pos - sv);
|
||||
|
||||
if (1 == sz && '.' == (*bufp)[sv])
|
||||
return(ROFF_IGN);
|
||||
|
||||
r->last->end = malloc(sz + 1);
|
||||
|
||||
if (NULL == r->last->end) {
|
||||
(*r->msg)(MANDOCERR_MEM, r->data, ln, pos, NULL);
|
||||
return(ROFF_ERR);
|
||||
}
|
||||
|
||||
memcpy(r->last->end, *bufp + sv, sz);
|
||||
r->last->end[(int)sz] = '\0';
|
||||
|
||||
if ((*bufp)[pos])
|
||||
if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
|
||||
return(ROFF_ERR);
|
||||
|
||||
return(ROFF_IGN);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum rofferr
|
||||
roff_block_sub(ROFF_ARGS)
|
||||
{
|
||||
enum rofft t;
|
||||
int i, j;
|
||||
|
||||
/*
|
||||
* First check whether a custom macro exists at this level. If
|
||||
* it does, then check against it. This is some of groff's
|
||||
* stranger behaviours. If we encountered a custom end-scope
|
||||
* tag and that tag also happens to be a "real" macro, then we
|
||||
* need to try interpreting it again as a real macro. If it's
|
||||
* not, then return ignore. Else continue.
|
||||
*/
|
||||
|
||||
if (r->last->end) {
|
||||
i = pos + 1;
|
||||
while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
|
||||
i++;
|
||||
|
||||
for (j = 0; r->last->end[j]; j++, i++)
|
||||
if ((*bufp)[i] != r->last->end[j])
|
||||
break;
|
||||
|
||||
if ('\0' == r->last->end[j] &&
|
||||
('\0' == (*bufp)[i] ||
|
||||
' ' == (*bufp)[i] ||
|
||||
'\t' == (*bufp)[i])) {
|
||||
roffnode_pop(r);
|
||||
roffnode_cleanscope(r);
|
||||
|
||||
if (ROFF_MAX != roff_parse(*bufp, &pos))
|
||||
return(ROFF_RERUN);
|
||||
return(ROFF_IGN);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have no custom end-query or lookup failed, then try
|
||||
* pulling it out of the hashtable.
|
||||
*/
|
||||
|
||||
ppos = pos;
|
||||
t = roff_parse(*bufp, &pos);
|
||||
|
||||
/* If we're not a comment-end, then throw it away. */
|
||||
if (ROFF_cblock != t)
|
||||
return(ROFF_IGN);
|
||||
|
||||
assert(roffs[t].proc);
|
||||
return((*roffs[t].proc)(r, t, bufp,
|
||||
szp, ln, ppos, pos, offs));
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum rofferr
|
||||
roff_block_text(ROFF_ARGS)
|
||||
{
|
||||
|
||||
return(ROFF_IGN);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum rofferr
|
||||
roff_cond_sub(ROFF_ARGS)
|
||||
{
|
||||
enum rofft t;
|
||||
enum roffrule rr;
|
||||
struct roffnode *l;
|
||||
|
||||
ppos = pos;
|
||||
rr = r->last->rule;
|
||||
|
||||
/*
|
||||
* Clean out scope. If we've closed ourselves, then don't
|
||||
* continue.
|
||||
*/
|
||||
|
||||
l = r->last;
|
||||
roffnode_cleanscope(r);
|
||||
|
||||
if (l != r->last)
|
||||
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
|
||||
|
||||
if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
|
||||
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
|
||||
|
||||
/*
|
||||
* A denied conditional must evaluate its children if and only
|
||||
* if they're either structurally required (such as loops and
|
||||
* conditionals) or a closing macro.
|
||||
*/
|
||||
if (ROFFRULE_DENY == rr)
|
||||
if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
|
||||
if (ROFF_ccond != t)
|
||||
return(ROFF_IGN);
|
||||
|
||||
assert(roffs[t].proc);
|
||||
return((*roffs[t].proc)
|
||||
(r, t, bufp, szp, ln, ppos, pos, offs));
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum rofferr
|
||||
roff_cond_text(ROFF_ARGS)
|
||||
{
|
||||
char *ep, *st;
|
||||
enum roffrule rr;
|
||||
|
||||
rr = r->last->rule;
|
||||
|
||||
/*
|
||||
* We display the value of the text if out current evaluation
|
||||
* scope permits us to do so.
|
||||
*/
|
||||
|
||||
st = &(*bufp)[pos];
|
||||
if (NULL == (ep = strstr(st, "\\}"))) {
|
||||
roffnode_cleanscope(r);
|
||||
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
|
||||
}
|
||||
|
||||
if (ep == st || (ep > st && '\\' != *(ep - 1)))
|
||||
roffnode_pop(r);
|
||||
|
||||
roffnode_cleanscope(r);
|
||||
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
|
||||
}
|
||||
|
||||
|
||||
static enum roffrule
|
||||
roff_evalcond(const char *v, int *pos)
|
||||
{
|
||||
|
||||
switch (v[*pos]) {
|
||||
case ('n'):
|
||||
(*pos)++;
|
||||
return(ROFFRULE_ALLOW);
|
||||
case ('e'):
|
||||
/* FALLTHROUGH */
|
||||
case ('o'):
|
||||
/* FALLTHROUGH */
|
||||
case ('t'):
|
||||
(*pos)++;
|
||||
return(ROFFRULE_DENY);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
while (v[*pos] && ' ' != v[*pos])
|
||||
(*pos)++;
|
||||
return(ROFFRULE_DENY);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum rofferr
|
||||
roff_cond(ROFF_ARGS)
|
||||
{
|
||||
int sv;
|
||||
enum roffrule rule;
|
||||
|
||||
/* Stack overflow! */
|
||||
|
||||
if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
|
||||
(*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
|
||||
return(ROFF_ERR);
|
||||
}
|
||||
|
||||
/* First, evaluate the conditional. */
|
||||
|
||||
if (ROFF_el == tok) {
|
||||
/*
|
||||
* An `.el' will get the value of the current rstack
|
||||
* entry set in prior `ie' calls or defaults to DENY.
|
||||
*/
|
||||
if (r->rstackpos < 0)
|
||||
rule = ROFFRULE_DENY;
|
||||
else
|
||||
rule = r->rstack[r->rstackpos];
|
||||
} else
|
||||
rule = roff_evalcond(*bufp, &pos);
|
||||
|
||||
sv = pos;
|
||||
|
||||
while (' ' == (*bufp)[pos])
|
||||
pos++;
|
||||
|
||||
/*
|
||||
* Roff is weird. If we have just white-space after the
|
||||
* conditional, it's considered the BODY and we exit without
|
||||
* really doing anything. Warn about this. It's probably
|
||||
* wrong.
|
||||
*/
|
||||
|
||||
if ('\0' == (*bufp)[pos] && sv != pos) {
|
||||
if ((*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
|
||||
return(ROFF_IGN);
|
||||
return(ROFF_ERR);
|
||||
}
|
||||
|
||||
if ( ! roffnode_push(r, tok, ln, ppos))
|
||||
return(ROFF_ERR);
|
||||
|
||||
r->last->rule = rule;
|
||||
|
||||
ROFF_DEBUG("roff: cond: %s -> %s\n", roffs[tok].name,
|
||||
ROFFRULE_ALLOW == rule ? "allow" : "deny");
|
||||
|
||||
if (ROFF_ie == tok) {
|
||||
/*
|
||||
* An if-else will put the NEGATION of the current
|
||||
* evaluated conditional into the stack.
|
||||
*/
|
||||
r->rstackpos++;
|
||||
if (ROFFRULE_DENY == r->last->rule)
|
||||
r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
|
||||
else
|
||||
r->rstack[r->rstackpos] = ROFFRULE_DENY;
|
||||
}
|
||||
|
||||
/* If the parent has false as its rule, then so do we. */
|
||||
|
||||
if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) {
|
||||
r->last->rule = ROFFRULE_DENY;
|
||||
ROFF_DEBUG("roff: cond override: %s -> deny\n",
|
||||
roffs[tok].name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine scope. If we're invoked with "\{" trailing the
|
||||
* conditional, then we're in a multiline scope. Else our scope
|
||||
* expires on the next line.
|
||||
*/
|
||||
|
||||
r->last->endspan = 1;
|
||||
|
||||
if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
|
||||
r->last->endspan = -1;
|
||||
pos += 2;
|
||||
ROFF_DEBUG("roff: cond-scope: %s, multi-line\n",
|
||||
roffs[tok].name);
|
||||
} else
|
||||
ROFF_DEBUG("roff: cond-scope: %s, one-line\n",
|
||||
roffs[tok].name);
|
||||
|
||||
/*
|
||||
* If there are no arguments on the line, the next-line scope is
|
||||
* assumed.
|
||||
*/
|
||||
|
||||
if ('\0' == (*bufp)[pos])
|
||||
return(ROFF_IGN);
|
||||
|
||||
/* Otherwise re-run the roff parser after recalculating. */
|
||||
|
||||
*offs = pos;
|
||||
return(ROFF_RERUN);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum rofferr
|
||||
roff_line(ROFF_ARGS)
|
||||
{
|
||||
|
||||
return(ROFF_IGN);
|
||||
}
|
40
commands/mdocml/roff.h
Normal file
40
commands/mdocml/roff.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/* $Id: roff.h,v 1.15 2010/05/17 00:06:36 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef ROFF_H
|
||||
#define ROFF_H
|
||||
|
||||
enum rofferr {
|
||||
ROFF_CONT, /* continue processing line */
|
||||
ROFF_RERUN, /* re-run roff interpreter with offset */
|
||||
ROFF_IGN, /* ignore current line */
|
||||
ROFF_ERR /* badness: puke and stop */
|
||||
};
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
struct roff;
|
||||
|
||||
void roff_free(struct roff *);
|
||||
struct roff *roff_alloc(mandocmsg, void *);
|
||||
void roff_reset(struct roff *);
|
||||
enum rofferr roff_parseln(struct roff *, int,
|
||||
char **, size_t *, int, int *);
|
||||
int roff_endparse(struct roff *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!ROFF_H*/
|
38
commands/mdocml/st.c
Normal file
38
commands/mdocml/st.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* $Id: st.c,v 1.8 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
|
||||
#define LINE(x, y) \
|
||||
if (0 == strcmp(p, x)) return(y);
|
||||
|
||||
const char *
|
||||
mdoc_a2st(const char *p)
|
||||
{
|
||||
|
||||
#include "st.in"
|
||||
|
||||
return(NULL);
|
||||
}
|
72
commands/mdocml/st.in
Normal file
72
commands/mdocml/st.in
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* $Id: st.in,v 1.13 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file defines the .St macro arguments. If you add a new
|
||||
* standard, make sure that the left-and side corresponds to the .St
|
||||
* argument (like .St -p1003.1) and the right-hand side corresponds to
|
||||
* the formatted output string.
|
||||
*
|
||||
* Be sure to escape strings.
|
||||
*/
|
||||
|
||||
LINE("-p1003.1-88", "IEEE Std 1003.1-1988 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1-90", "IEEE Std 1003.1-1990 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1-2001", "IEEE Std 1003.1-2001 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1-2004", "IEEE Std 1003.1-2004 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1-2008", "IEEE Std 1003.1-2008 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1", "IEEE Std 1003.1 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1b", "IEEE Std 1003.1b (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1b-93", "IEEE Std 1003.1b-1993 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1c-95", "IEEE Std 1003.1c-1995 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1g-2000", "IEEE Std 1003.1g-2000 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.1i-95", "IEEE Std 1003.1i-1995 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-p1003.2-92", "IEEE Std 1003.2-1992 (\\(lqPOSIX.2\\(rq)")
|
||||
LINE("-p1003.2a-92", "IEEE Std 1003.2a-1992 (\\(lqPOSIX.2\\(rq)")
|
||||
LINE("-p1387.2-95", "IEEE Std 1387.2-1995 (\\(lqPOSIX.7.2\\(rq)")
|
||||
LINE("-p1003.2", "IEEE Std 1003.2 (\\(lqPOSIX.2\\(rq)")
|
||||
LINE("-p1387.2", "IEEE Std 1387.2 (\\(lqPOSIX.7.2\\(rq)")
|
||||
LINE("-isoC", "ISO/IEC 9899:1990 (\\(lqISO C90\\(rq)")
|
||||
LINE("-isoC-90", "ISO/IEC 9899:1990 (\\(lqISO C90\\(rq)")
|
||||
LINE("-isoC-amd1", "ISO/IEC 9899/AMD1:1995 (\\(lqISO C90\\(rq)")
|
||||
LINE("-isoC-tcor1", "ISO/IEC 9899/TCOR1:1994 (\\(lqISO C90\\(rq)")
|
||||
LINE("-isoC-tcor2", "ISO/IEC 9899/TCOR2:1995 (\\(lqISO C90\\(rq)")
|
||||
LINE("-isoC-99", "ISO/IEC 9899:1999 (\\(lqISO C99\\(rq)")
|
||||
LINE("-iso9945-1-90", "ISO/IEC 9945-1:1990 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-iso9945-1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
|
||||
LINE("-iso9945-2-93", "ISO/IEC 9945-2:1993 (\\(lqPOSIX.2\\(rq)")
|
||||
LINE("-ansiC", "ANSI X3.159-1989 (\\(lqANSI C\\(rq)")
|
||||
LINE("-ansiC-89", "ANSI X3.159-1989 (\\(lqANSI C\\(rq)")
|
||||
LINE("-ansiC-99", "ANSI/ISO/IEC 9899-1999 (\\(lqANSI C99\\(rq)")
|
||||
LINE("-ieee754", "IEEE Std 754-1985")
|
||||
LINE("-iso8802-3", "ISO 8802-3: 1989")
|
||||
LINE("-ieee1275-94", "IEEE Std 1275-1994 (\\(lqOpen Firmware\\(rq)")
|
||||
LINE("-xpg3", "X/Open Portability Guide Issue 3 (\\(lqXPG3\\(rq)")
|
||||
LINE("-xpg4", "X/Open Portability Guide Issue 4 (\\(lqXPG4\\(rq)")
|
||||
LINE("-xpg4.2", "X/Open Portability Guide Issue 4.2 (\\(lqXPG4.2\\(rq)")
|
||||
LINE("-xpg4.3", "X/Open Portability Guide Issue 4.3 (\\(lqXPG4.3\\(rq)")
|
||||
LINE("-xbd5", "X/Open System Interface Definitions Issue 5 (\\(lqXBD5\\(rq)")
|
||||
LINE("-xcu5", "X/Open Commands and Utilities Issue 5 (\\(lqXCU5\\(rq)")
|
||||
LINE("-xsh5", "X/Open System Interfaces and Headers Issue 5 (\\(lqXSH5\\(rq)")
|
||||
LINE("-xns5", "X/Open Networking Services Issue 5 (\\(lqXNS5\\(rq)")
|
||||
LINE("-xns5.2", "X/Open Networking Services Issue 5.2 (\\(lqXNS5.2\\(rq)")
|
||||
LINE("-xns5.2d2.0", "X/Open Networking Services Issue 5.2 Draft 2.0 (\\(lqXNS5.2D2.0\\(rq)")
|
||||
LINE("-xcurses4.2", "X/Open Curses Issue 4 Version 2 (\\(lqXCURSES4.2\\(rq)")
|
||||
LINE("-susv2", "Version 2 of the Single UNIX Specification")
|
||||
LINE("-susv3", "Version 3 of the Single UNIX Specification")
|
||||
LINE("-svid4", "System V Interface Definition, Fourth Edition (\\(lqSVID4\\(rq)")
|
77
commands/mdocml/style.css
Normal file
77
commands/mdocml/style.css
Normal file
|
@ -0,0 +1,77 @@
|
|||
div.body { color: #333333;
|
||||
max-width: 800px;
|
||||
padding-left: 10px;
|
||||
font-size: smaller;
|
||||
font-family: Verdana, Tahoma, Arial, sans-serif; }
|
||||
|
||||
div.sec-head { color: #000000;
|
||||
font-weight: bold; }
|
||||
div.sec-body { }
|
||||
div.sec-block { padding-bottom: 1em; }
|
||||
div.ssec-head { color: #000000;
|
||||
font-weight: bold; }
|
||||
div.ssec-body { }
|
||||
div.ssec-block { }
|
||||
|
||||
span.addr { } /* Address (Ad). */
|
||||
span.arg { font-style: italic; } /* Command argument (Ar). */
|
||||
span.author { } /* Author name (An). */
|
||||
span.cmd { font-weight: bold; } /* Command (Cm). */
|
||||
span.config { font-weight: bold; } /* Config statement (Cd). */
|
||||
span.define { } /* Defines (Dv). */
|
||||
span.desc { } /* Nd. After em-dash. */
|
||||
span.diag { font-weight: bold; } /* Diagnostic (Bl -diag). */
|
||||
span.emph { font-style: italic; } /* Emphasis (Em). */
|
||||
span.env { } /* Environment variables (Ev). */
|
||||
span.errno { } /* Error string (Er). */
|
||||
span.farg { font-style: italic; } /* Function argument (Fa, Fn). */
|
||||
span.file { font-style: italic; } /* File (Pa). */
|
||||
span.flag { font-weight: bold; } /* Flag (Fl, Cm). */
|
||||
span.fname { font-weight: bold; } /* Function name (Fa, Fn, Rv). */
|
||||
span.ftype { font-style: italic; } /* Function types (Ft, Fn). */
|
||||
span.includes { font-weight: bold; } /* Header includes (In). */
|
||||
span.lib { } /* Library (Lb). */
|
||||
span.lit { font-family: monospace; } /* Literals (Bf -literal). */
|
||||
span.macro { font-weight: bold; } /* Macro-ish thing (Fd). */
|
||||
span.name { color: #003333; font-weight: bold; } /* Name of utility (Nm). */
|
||||
span.opt { } /* Options (Op, Oo/Oc). */
|
||||
span.ref { } /* Citations (Rs). */
|
||||
span.ref-auth { } /* Reference author (%A). */
|
||||
span.ref-book { font-style: italic; } /* Reference book (%B). */
|
||||
span.ref-city { } /* Reference city (%C). */
|
||||
span.ref-date { } /* Reference date (%D). */
|
||||
span.ref-issue { font-style: italic; } /* Reference issuer/publisher (%I). */
|
||||
span.ref-jrnl { font-style: italic; } /* Reference journal (%J). */
|
||||
span.ref-num { } /* Reference number (%N). */
|
||||
span.ref-opt { } /* Reference optionals (%O). */
|
||||
span.ref-page { } /* Reference page (%P). */
|
||||
span.ref-corp { } /* Reference corporate/foreign author (%Q). */
|
||||
span.ref-rep { } /* Reference report (%R). */
|
||||
span.ref-title { } /* Reference title (%T). */
|
||||
span.ref-vol { } /* Reference volume (%V). */
|
||||
span.symb { font-weight: bold; } /* Symbols. */
|
||||
span.type { font-style: italic; } /* Variable types (Vt). */
|
||||
span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */
|
||||
span.utility { font-weight: bold; } /* Name of utility (Ex). */
|
||||
span.var { font-weight: bold; } /* Variables (Rv). */
|
||||
|
||||
a.link-ext { background: transparent url(external.png) center right no-repeat; padding-right: 12px; }/* Off-site link (Lk). */
|
||||
a.link-includes { } /* Include-file link (In). */
|
||||
a.link-mail { background: transparent url(external.png) center right no-repeat; padding-right: 12px; }/* Mailto links (Mt). */
|
||||
a.link-man { } /* Manual links (Xr). */
|
||||
a.link-sec { text-decoration: none; border-bottom: 1px dotted #339999; } /* Section links (Sx). */
|
||||
|
||||
div.emph { font-style: italic; } /* Emphasis (Bl -emphasis). */
|
||||
div.lit { margin: 3px;
|
||||
padding: 3px;
|
||||
background-color: #EEEEEE;
|
||||
border: 1px solid #339999;
|
||||
color: #000000;
|
||||
font-family: monospace; }
|
||||
div.symb { font-weight: bold; } /* Symbols (Bl -symbolic). */
|
||||
|
||||
table.header { border-bottom: 1px dotted #dddddd;
|
||||
color: #999999; }
|
||||
table.footer { border-top: 1px dotted #dddddd;
|
||||
color: #999999; }
|
||||
|
702
commands/mdocml/term.c
Normal file
702
commands/mdocml/term.c
Normal file
|
@ -0,0 +1,702 @@
|
|||
/* $Id: term.c,v 1.148 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "chars.h"
|
||||
#include "out.h"
|
||||
#include "term.h"
|
||||
#include "man.h"
|
||||
#include "mdoc.h"
|
||||
#include "main.h"
|
||||
|
||||
static void spec(struct termp *, const char *, size_t);
|
||||
static void res(struct termp *, const char *, size_t);
|
||||
static void buffera(struct termp *, const char *, size_t);
|
||||
static void bufferc(struct termp *, char);
|
||||
static void adjbuf(struct termp *p, size_t);
|
||||
static void encode(struct termp *, const char *, size_t);
|
||||
|
||||
|
||||
void
|
||||
term_free(struct termp *p)
|
||||
{
|
||||
|
||||
if (p->buf)
|
||||
free(p->buf);
|
||||
if (p->symtab)
|
||||
chars_free(p->symtab);
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
term_begin(struct termp *p, term_margin head,
|
||||
term_margin foot, const void *arg)
|
||||
{
|
||||
|
||||
p->headf = head;
|
||||
p->footf = foot;
|
||||
p->argf = arg;
|
||||
(*p->begin)(p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
term_end(struct termp *p)
|
||||
{
|
||||
|
||||
(*p->end)(p);
|
||||
}
|
||||
|
||||
|
||||
struct termp *
|
||||
term_alloc(enum termenc enc)
|
||||
{
|
||||
struct termp *p;
|
||||
|
||||
p = calloc(1, sizeof(struct termp));
|
||||
if (NULL == p) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
p->tabwidth = 5;
|
||||
p->enc = enc;
|
||||
p->defrmargin = 78;
|
||||
return(p);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Flush a line of text. A "line" is loosely defined as being something
|
||||
* that should be followed by a newline, regardless of whether it's
|
||||
* broken apart by newlines getting there. A line can also be a
|
||||
* fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
|
||||
* not have a trailing newline.
|
||||
*
|
||||
* The following flags may be specified:
|
||||
*
|
||||
* - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
|
||||
* offset value. This is useful when doing columnar lists where the
|
||||
* prior column has right-padded.
|
||||
*
|
||||
* - TERMP_NOBREAK: this is the most important and is used when making
|
||||
* columns. In short: don't print a newline and instead pad to the
|
||||
* right margin. Used in conjunction with TERMP_NOLPAD.
|
||||
*
|
||||
* - TERMP_TWOSPACE: when padding, make sure there are at least two
|
||||
* space characters of padding. Otherwise, rather break the line.
|
||||
*
|
||||
* - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
|
||||
* the line is overrun, and don't pad-right if it's underrun.
|
||||
*
|
||||
* - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
|
||||
* overruning, instead save the position and continue at that point
|
||||
* when the next invocation.
|
||||
*
|
||||
* In-line line breaking:
|
||||
*
|
||||
* If TERMP_NOBREAK is specified and the line overruns the right
|
||||
* margin, it will break and pad-right to the right margin after
|
||||
* writing. If maxrmargin is violated, it will break and continue
|
||||
* writing from the right-margin, which will lead to the above scenario
|
||||
* upon exit. Otherwise, the line will break at the right margin.
|
||||
*/
|
||||
void
|
||||
term_flushln(struct termp *p)
|
||||
{
|
||||
int i; /* current input position in p->buf */
|
||||
size_t vis; /* current visual position on output */
|
||||
size_t vbl; /* number of blanks to prepend to output */
|
||||
size_t vend; /* end of word visual position on output */
|
||||
size_t bp; /* visual right border position */
|
||||
int j; /* temporary loop index */
|
||||
int jhy; /* last hyphen before line overflow */
|
||||
size_t maxvis, mmax;
|
||||
|
||||
/*
|
||||
* First, establish the maximum columns of "visible" content.
|
||||
* This is usually the difference between the right-margin and
|
||||
* an indentation, but can be, for tagged lists or columns, a
|
||||
* small set of values.
|
||||
*/
|
||||
|
||||
assert(p->offset < p->rmargin);
|
||||
|
||||
maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ?
|
||||
/* LINTED */
|
||||
0 : p->rmargin - p->offset - p->overstep;
|
||||
mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ?
|
||||
/* LINTED */
|
||||
0 : p->maxrmargin - p->offset - p->overstep;
|
||||
|
||||
bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
|
||||
|
||||
/*
|
||||
* Indent the first line of a paragraph.
|
||||
*/
|
||||
vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset;
|
||||
|
||||
/*
|
||||
* FIXME: if bp is zero, we still output the first word before
|
||||
* breaking the line.
|
||||
*/
|
||||
|
||||
vis = vend = i = 0;
|
||||
while (i < (int)p->col) {
|
||||
|
||||
/*
|
||||
* Handle literal tab characters.
|
||||
*/
|
||||
for (j = i; j < (int)p->col; j++) {
|
||||
if ('\t' != p->buf[j])
|
||||
break;
|
||||
vend = (vis/p->tabwidth+1)*p->tabwidth;
|
||||
vbl += vend - vis;
|
||||
vis = vend;
|
||||
}
|
||||
|
||||
/*
|
||||
* Count up visible word characters. Control sequences
|
||||
* (starting with the CSI) aren't counted. A space
|
||||
* generates a non-printing word, which is valid (the
|
||||
* space is printed according to regular spacing rules).
|
||||
*/
|
||||
|
||||
/* LINTED */
|
||||
for (jhy = 0; j < (int)p->col; j++) {
|
||||
if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
|
||||
break;
|
||||
if (8 != p->buf[j]) {
|
||||
if (vend > vis && vend < bp &&
|
||||
ASCII_HYPH == p->buf[j])
|
||||
jhy = j;
|
||||
vend++;
|
||||
} else
|
||||
vend--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find out whether we would exceed the right margin.
|
||||
* If so, break to the next line.
|
||||
*/
|
||||
if (vend > bp && 0 == jhy && vis > 0) {
|
||||
vend -= vis;
|
||||
(*p->endline)(p);
|
||||
if (TERMP_NOBREAK & p->flags) {
|
||||
p->viscol = p->rmargin;
|
||||
(*p->advance)(p, p->rmargin);
|
||||
vend += p->rmargin - p->offset;
|
||||
} else {
|
||||
p->viscol = 0;
|
||||
vbl = p->offset;
|
||||
}
|
||||
|
||||
/* Remove the p->overstep width. */
|
||||
|
||||
bp += (int)/* LINTED */
|
||||
p->overstep;
|
||||
p->overstep = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip leading tabs, they were handled above.
|
||||
*/
|
||||
while (i < (int)p->col && '\t' == p->buf[i])
|
||||
i++;
|
||||
|
||||
/* Write out the [remaining] word. */
|
||||
for ( ; i < (int)p->col; i++) {
|
||||
if (vend > bp && jhy > 0 && i > jhy)
|
||||
break;
|
||||
if ('\t' == p->buf[i])
|
||||
break;
|
||||
if (' ' == p->buf[i]) {
|
||||
while (' ' == p->buf[i]) {
|
||||
vbl++;
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ASCII_NBRSP == p->buf[i]) {
|
||||
vbl++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we definitely know there will be
|
||||
* printable characters to output,
|
||||
* so write preceding white space now.
|
||||
*/
|
||||
if (vbl) {
|
||||
(*p->advance)(p, vbl);
|
||||
p->viscol += vbl;
|
||||
vbl = 0;
|
||||
}
|
||||
|
||||
if (ASCII_HYPH == p->buf[i])
|
||||
(*p->letter)(p, '-');
|
||||
else
|
||||
(*p->letter)(p, p->buf[i]);
|
||||
|
||||
p->viscol += 1;
|
||||
}
|
||||
vend += vbl;
|
||||
vis = vend;
|
||||
}
|
||||
|
||||
p->col = 0;
|
||||
p->overstep = 0;
|
||||
|
||||
if ( ! (TERMP_NOBREAK & p->flags)) {
|
||||
p->viscol = 0;
|
||||
(*p->endline)(p);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TERMP_HANG & p->flags) {
|
||||
/* We need one blank after the tag. */
|
||||
p->overstep = /* LINTED */
|
||||
vis - maxvis + 1;
|
||||
|
||||
/*
|
||||
* Behave exactly the same way as groff:
|
||||
* If we have overstepped the margin, temporarily move
|
||||
* it to the right and flag the rest of the line to be
|
||||
* shorter.
|
||||
* If we landed right at the margin, be happy.
|
||||
* If we are one step before the margin, temporarily
|
||||
* move it one step LEFT and flag the rest of the line
|
||||
* to be longer.
|
||||
*/
|
||||
if (p->overstep >= -1) {
|
||||
assert((int)maxvis + p->overstep >= 0);
|
||||
/* LINTED */
|
||||
maxvis += p->overstep;
|
||||
} else
|
||||
p->overstep = 0;
|
||||
|
||||
} else if (TERMP_DANGLE & p->flags)
|
||||
return;
|
||||
|
||||
/* Right-pad. */
|
||||
if (maxvis > vis + /* LINTED */
|
||||
((TERMP_TWOSPACE & p->flags) ? 1 : 0)) {
|
||||
p->viscol += maxvis - vis;
|
||||
(*p->advance)(p, maxvis - vis);
|
||||
vis += (maxvis - vis);
|
||||
} else { /* ...or newline break. */
|
||||
(*p->endline)(p);
|
||||
p->viscol = p->rmargin;
|
||||
(*p->advance)(p, p->rmargin);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* A newline only breaks an existing line; it won't assert vertical
|
||||
* space. All data in the output buffer is flushed prior to the newline
|
||||
* assertion.
|
||||
*/
|
||||
void
|
||||
term_newln(struct termp *p)
|
||||
{
|
||||
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
if (0 == p->col && 0 == p->viscol) {
|
||||
p->flags &= ~TERMP_NOLPAD;
|
||||
return;
|
||||
}
|
||||
term_flushln(p);
|
||||
p->flags &= ~TERMP_NOLPAD;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Asserts a vertical space (a full, empty line-break between lines).
|
||||
* Note that if used twice, this will cause two blank spaces and so on.
|
||||
* All data in the output buffer is flushed prior to the newline
|
||||
* assertion.
|
||||
*/
|
||||
void
|
||||
term_vspace(struct termp *p)
|
||||
{
|
||||
|
||||
term_newln(p);
|
||||
p->viscol = 0;
|
||||
(*p->endline)(p);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
spec(struct termp *p, const char *word, size_t len)
|
||||
{
|
||||
const char *rhs;
|
||||
size_t sz;
|
||||
|
||||
rhs = chars_a2ascii(p->symtab, word, len, &sz);
|
||||
if (rhs)
|
||||
encode(p, rhs, sz);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
res(struct termp *p, const char *word, size_t len)
|
||||
{
|
||||
const char *rhs;
|
||||
size_t sz;
|
||||
|
||||
rhs = chars_a2res(p->symtab, word, len, &sz);
|
||||
if (rhs)
|
||||
encode(p, rhs, sz);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
term_fontlast(struct termp *p)
|
||||
{
|
||||
enum termfont f;
|
||||
|
||||
f = p->fontl;
|
||||
p->fontl = p->fontq[p->fonti];
|
||||
p->fontq[p->fonti] = f;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
term_fontrepl(struct termp *p, enum termfont f)
|
||||
{
|
||||
|
||||
p->fontl = p->fontq[p->fonti];
|
||||
p->fontq[p->fonti] = f;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
term_fontpush(struct termp *p, enum termfont f)
|
||||
{
|
||||
|
||||
assert(p->fonti + 1 < 10);
|
||||
p->fontl = p->fontq[p->fonti];
|
||||
p->fontq[++p->fonti] = f;
|
||||
}
|
||||
|
||||
|
||||
const void *
|
||||
term_fontq(struct termp *p)
|
||||
{
|
||||
|
||||
return(&p->fontq[p->fonti]);
|
||||
}
|
||||
|
||||
|
||||
enum termfont
|
||||
term_fonttop(struct termp *p)
|
||||
{
|
||||
|
||||
return(p->fontq[p->fonti]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
term_fontpopq(struct termp *p, const void *key)
|
||||
{
|
||||
|
||||
while (p->fonti >= 0 && key != &p->fontq[p->fonti])
|
||||
p->fonti--;
|
||||
assert(p->fonti >= 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
term_fontpop(struct termp *p)
|
||||
{
|
||||
|
||||
assert(p->fonti);
|
||||
p->fonti--;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle pwords, partial words, which may be either a single word or a
|
||||
* phrase that cannot be broken down (such as a literal string). This
|
||||
* handles word styling.
|
||||
*/
|
||||
void
|
||||
term_word(struct termp *p, const char *word)
|
||||
{
|
||||
const char *sv, *seq;
|
||||
int sz;
|
||||
size_t ssz;
|
||||
enum roffdeco deco;
|
||||
|
||||
sv = word;
|
||||
|
||||
if (word[0] && '\0' == word[1])
|
||||
switch (word[0]) {
|
||||
case('.'):
|
||||
/* FALLTHROUGH */
|
||||
case(','):
|
||||
/* FALLTHROUGH */
|
||||
case(';'):
|
||||
/* FALLTHROUGH */
|
||||
case(':'):
|
||||
/* FALLTHROUGH */
|
||||
case('?'):
|
||||
/* FALLTHROUGH */
|
||||
case('!'):
|
||||
/* FALLTHROUGH */
|
||||
case(')'):
|
||||
/* FALLTHROUGH */
|
||||
case(']'):
|
||||
if ( ! (TERMP_IGNDELIM & p->flags))
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! (TERMP_NOSPACE & p->flags)) {
|
||||
bufferc(p, ' ');
|
||||
if (TERMP_SENTENCE & p->flags)
|
||||
bufferc(p, ' ');
|
||||
}
|
||||
|
||||
if ( ! (p->flags & TERMP_NONOSPACE))
|
||||
p->flags &= ~TERMP_NOSPACE;
|
||||
|
||||
p->flags &= ~TERMP_SENTENCE;
|
||||
|
||||
/* FIXME: use strcspn. */
|
||||
|
||||
while (*word) {
|
||||
if ('\\' != *word) {
|
||||
encode(p, word, 1);
|
||||
word++;
|
||||
continue;
|
||||
}
|
||||
|
||||
seq = ++word;
|
||||
sz = a2roffdeco(&deco, &seq, &ssz);
|
||||
|
||||
switch (deco) {
|
||||
case (DECO_RESERVED):
|
||||
res(p, seq, ssz);
|
||||
break;
|
||||
case (DECO_SPECIAL):
|
||||
spec(p, seq, ssz);
|
||||
break;
|
||||
case (DECO_BOLD):
|
||||
term_fontrepl(p, TERMFONT_BOLD);
|
||||
break;
|
||||
case (DECO_ITALIC):
|
||||
term_fontrepl(p, TERMFONT_UNDER);
|
||||
break;
|
||||
case (DECO_ROMAN):
|
||||
term_fontrepl(p, TERMFONT_NONE);
|
||||
break;
|
||||
case (DECO_PREVIOUS):
|
||||
term_fontlast(p);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
word += sz;
|
||||
if (DECO_NOSPACE == deco && '\0' == *word)
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that we don't process the pipe: the parser sees it as
|
||||
* punctuation, but we don't in terms of typography.
|
||||
*/
|
||||
if (sv[0] && 0 == sv[1])
|
||||
switch (sv[0]) {
|
||||
case('('):
|
||||
/* FALLTHROUGH */
|
||||
case('['):
|
||||
p->flags |= TERMP_NOSPACE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
adjbuf(struct termp *p, size_t sz)
|
||||
{
|
||||
|
||||
if (0 == p->maxcols)
|
||||
p->maxcols = 1024;
|
||||
while (sz >= p->maxcols)
|
||||
p->maxcols <<= 2;
|
||||
|
||||
p->buf = realloc(p->buf, p->maxcols);
|
||||
if (NULL == p->buf) {
|
||||
perror(NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
buffera(struct termp *p, const char *word, size_t sz)
|
||||
{
|
||||
|
||||
if (p->col + sz >= p->maxcols)
|
||||
adjbuf(p, p->col + sz);
|
||||
|
||||
memcpy(&p->buf[(int)p->col], word, sz);
|
||||
p->col += sz;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
bufferc(struct termp *p, char c)
|
||||
{
|
||||
|
||||
if (p->col + 1 >= p->maxcols)
|
||||
adjbuf(p, p->col + 1);
|
||||
|
||||
p->buf[(int)p->col++] = c;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
encode(struct termp *p, const char *word, size_t sz)
|
||||
{
|
||||
enum termfont f;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Encode and buffer a string of characters. If the current
|
||||
* font mode is unset, buffer directly, else encode then buffer
|
||||
* character by character.
|
||||
*/
|
||||
|
||||
if (TERMFONT_NONE == (f = term_fonttop(p))) {
|
||||
buffera(p, word, sz);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < (int)sz; i++) {
|
||||
if ( ! isgraph((u_char)word[i])) {
|
||||
bufferc(p, word[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TERMFONT_UNDER == f)
|
||||
bufferc(p, '_');
|
||||
else
|
||||
bufferc(p, word[i]);
|
||||
|
||||
bufferc(p, 8);
|
||||
bufferc(p, word[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
term_vspan(const struct roffsu *su)
|
||||
{
|
||||
double r;
|
||||
|
||||
switch (su->unit) {
|
||||
case (SCALE_CM):
|
||||
r = su->scale * 2;
|
||||
break;
|
||||
case (SCALE_IN):
|
||||
r = su->scale * 6;
|
||||
break;
|
||||
case (SCALE_PC):
|
||||
r = su->scale;
|
||||
break;
|
||||
case (SCALE_PT):
|
||||
r = su->scale / 8;
|
||||
break;
|
||||
case (SCALE_MM):
|
||||
r = su->scale / 1000;
|
||||
break;
|
||||
case (SCALE_VS):
|
||||
r = su->scale;
|
||||
break;
|
||||
default:
|
||||
r = su->scale - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (r < 0.0)
|
||||
r = 0.0;
|
||||
return(/* LINTED */(size_t)
|
||||
r);
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
term_hspan(const struct roffsu *su)
|
||||
{
|
||||
double r;
|
||||
|
||||
/* XXX: CM, IN, and PT are approximations. */
|
||||
|
||||
switch (su->unit) {
|
||||
case (SCALE_CM):
|
||||
r = 4 * su->scale;
|
||||
break;
|
||||
case (SCALE_IN):
|
||||
/* XXX: this is an approximation. */
|
||||
r = 10 * su->scale;
|
||||
break;
|
||||
case (SCALE_PC):
|
||||
r = (10 * su->scale) / 6;
|
||||
break;
|
||||
case (SCALE_PT):
|
||||
r = (10 * su->scale) / 72;
|
||||
break;
|
||||
case (SCALE_MM):
|
||||
r = su->scale / 1000; /* FIXME: double-check. */
|
||||
break;
|
||||
case (SCALE_VS):
|
||||
r = su->scale * 2 - 1; /* FIXME: double-check. */
|
||||
break;
|
||||
default:
|
||||
r = su->scale;
|
||||
break;
|
||||
}
|
||||
|
||||
if (r < 0.0)
|
||||
r = 0.0;
|
||||
return((size_t)/* LINTED */
|
||||
r);
|
||||
}
|
||||
|
||||
|
123
commands/mdocml/term.h
Normal file
123
commands/mdocml/term.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/* $Id: term.h,v 1.64 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifndef TERM_H
|
||||
#define TERM_H
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
struct termp;
|
||||
|
||||
enum termenc {
|
||||
TERMENC_ASCII
|
||||
};
|
||||
|
||||
enum termtype {
|
||||
TERMTYPE_CHAR,
|
||||
TERMTYPE_PS
|
||||
};
|
||||
|
||||
enum termfont {
|
||||
TERMFONT_NONE = 0,
|
||||
TERMFONT_BOLD,
|
||||
TERMFONT_UNDER
|
||||
};
|
||||
|
||||
#define TERM_MAXMARGIN 100000 /* FIXME */
|
||||
|
||||
typedef void (*term_margin)(struct termp *, const void *);
|
||||
|
||||
struct termp_ps {
|
||||
int psstate; /* state of ps output */
|
||||
#define PS_INLINE (1 << 0) /* we're in a word */
|
||||
#define PS_MARGINS (1 << 1) /* we're in the margins */
|
||||
size_t pscol; /* visible column */
|
||||
size_t psrow; /* visible row */
|
||||
char *psmarg; /* margin buf */
|
||||
size_t psmargsz; /* margin buf size */
|
||||
size_t psmargcur; /* current pos in margin buf */
|
||||
size_t pspage; /* current page */
|
||||
char last; /* character buffer */
|
||||
enum termfont lastf; /* last set font */
|
||||
};
|
||||
|
||||
struct termp {
|
||||
enum termtype type;
|
||||
size_t defrmargin; /* Right margin of the device.. */
|
||||
size_t rmargin; /* Current right margin. */
|
||||
size_t maxrmargin; /* Max right margin. */
|
||||
size_t maxcols; /* Max size of buf. */
|
||||
size_t offset; /* Margin offest. */
|
||||
size_t tabwidth; /* Distance of tab positions. */
|
||||
size_t col; /* Bytes in buf. */
|
||||
size_t viscol; /* Chars on current line. */
|
||||
int overstep; /* See termp_flushln(). */
|
||||
int flags;
|
||||
#define TERMP_SENTENCE (1 << 1) /* Space before a sentence. */
|
||||
#define TERMP_NOSPACE (1 << 2) /* No space before words. */
|
||||
#define TERMP_NOLPAD (1 << 3) /* See term_flushln(). */
|
||||
#define TERMP_NOBREAK (1 << 4) /* See term_flushln(). */
|
||||
#define TERMP_IGNDELIM (1 << 6) /* Delims like regulars. */
|
||||
#define TERMP_NONOSPACE (1 << 7) /* No space (no autounset). */
|
||||
#define TERMP_DANGLE (1 << 8) /* See term_flushln(). */
|
||||
#define TERMP_HANG (1 << 9) /* See term_flushln(). */
|
||||
#define TERMP_TWOSPACE (1 << 10) /* See term_flushln(). */
|
||||
#define TERMP_NOSPLIT (1 << 11) /* See termp_an_pre/post(). */
|
||||
#define TERMP_SPLIT (1 << 12) /* See termp_an_pre/post(). */
|
||||
#define TERMP_ANPREC (1 << 13) /* See termp_an_pre(). */
|
||||
char *buf; /* Output buffer. */
|
||||
enum termenc enc; /* Type of encoding. */
|
||||
void *symtab; /* Encoded-symbol table. */
|
||||
enum termfont fontl; /* Last font set. */
|
||||
enum termfont fontq[10]; /* Symmetric fonts. */
|
||||
int fonti; /* Index of font stack. */
|
||||
term_margin headf; /* invoked to print head */
|
||||
term_margin footf; /* invoked to print foot */
|
||||
void (*letter)(struct termp *, char);
|
||||
void (*begin)(struct termp *);
|
||||
void (*end)(struct termp *);
|
||||
void (*endline)(struct termp *);
|
||||
void (*advance)(struct termp *, size_t);
|
||||
const void *argf; /* arg for headf/footf */
|
||||
union {
|
||||
struct termp_ps ps;
|
||||
} engine;
|
||||
};
|
||||
|
||||
struct termp *term_alloc(enum termenc);
|
||||
void term_free(struct termp *);
|
||||
void term_newln(struct termp *);
|
||||
void term_vspace(struct termp *);
|
||||
void term_word(struct termp *, const char *);
|
||||
void term_flushln(struct termp *);
|
||||
void term_begin(struct termp *, term_margin,
|
||||
term_margin, const void *);
|
||||
void term_end(struct termp *);
|
||||
|
||||
size_t term_hspan(const struct roffsu *);
|
||||
size_t term_vspan(const struct roffsu *);
|
||||
|
||||
enum termfont term_fonttop(struct termp *);
|
||||
const void *term_fontq(struct termp *);
|
||||
void term_fontpush(struct termp *, enum termfont);
|
||||
void term_fontpop(struct termp *);
|
||||
void term_fontpopq(struct termp *, const void *);
|
||||
void term_fontrepl(struct termp *, enum termfont);
|
||||
void term_fontlast(struct termp *);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /*!TERM_H*/
|
128
commands/mdocml/term_ascii.c
Normal file
128
commands/mdocml/term_ascii.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
/* $Id: term_ascii.c,v 1.4 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "out.h"
|
||||
#include "term.h"
|
||||
#include "main.h"
|
||||
|
||||
static void ascii_endline(struct termp *);
|
||||
static void ascii_letter(struct termp *, char);
|
||||
static void ascii_begin(struct termp *);
|
||||
static void ascii_advance(struct termp *, size_t);
|
||||
static void ascii_end(struct termp *);
|
||||
|
||||
|
||||
void *
|
||||
ascii_alloc(char *outopts)
|
||||
{
|
||||
struct termp *p;
|
||||
const char *toks[2];
|
||||
char *v;
|
||||
|
||||
if (NULL == (p = term_alloc(TERMENC_ASCII)))
|
||||
return(NULL);
|
||||
|
||||
p->type = TERMTYPE_CHAR;
|
||||
p->letter = ascii_letter;
|
||||
p->begin = ascii_begin;
|
||||
p->end = ascii_end;
|
||||
p->endline = ascii_endline;
|
||||
p->advance = ascii_advance;
|
||||
|
||||
toks[0] = "width";
|
||||
toks[1] = NULL;
|
||||
|
||||
while (outopts && *outopts)
|
||||
switch (getsubopt(&outopts, UNCONST(toks), &v)) {
|
||||
case (0):
|
||||
p->defrmargin = (size_t)atoi(v);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enforce a lower boundary. */
|
||||
if (p->defrmargin < 58)
|
||||
p->defrmargin = 58;
|
||||
|
||||
return(p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ascii_free(void *arg)
|
||||
{
|
||||
|
||||
term_free((struct termp *)arg);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
ascii_letter(struct termp *p, char c)
|
||||
{
|
||||
|
||||
putchar(c);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ascii_begin(struct termp *p)
|
||||
{
|
||||
|
||||
(*p->headf)(p, p->argf);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ascii_end(struct termp *p)
|
||||
{
|
||||
|
||||
(*p->footf)(p, p->argf);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
ascii_endline(struct termp *p)
|
||||
{
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
ascii_advance(struct termp *p, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* Just print whitespace on the terminal. */
|
||||
for (i = 0; i < len; i++)
|
||||
putchar(' ');
|
||||
}
|
430
commands/mdocml/term_ps.c
Normal file
430
commands/mdocml/term_ps.c
Normal file
|
@ -0,0 +1,430 @@
|
|||
/* $Id: term_ps.c,v 1.10 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "out.h"
|
||||
#include "main.h"
|
||||
#include "term.h"
|
||||
|
||||
#define PS_CHAR_WIDTH 6
|
||||
#define PS_CHAR_HEIGHT 12
|
||||
#define PS_CHAR_TOPMARG (792 - 24)
|
||||
#define PS_CHAR_TOP (PS_CHAR_TOPMARG - 36)
|
||||
#define PS_CHAR_LEFT 36
|
||||
#define PS_CHAR_BOTMARG 24
|
||||
#define PS_CHAR_BOT (PS_CHAR_BOTMARG + 36)
|
||||
|
||||
#define PS_BUFSLOP 128
|
||||
#define PS_GROWBUF(p, sz) \
|
||||
do if ((p)->engine.ps.psmargcur + (sz) > \
|
||||
(p)->engine.ps.psmargsz) { \
|
||||
(p)->engine.ps.psmargsz += /* CONSTCOND */ \
|
||||
MAX(PS_BUFSLOP, (sz)); \
|
||||
(p)->engine.ps.psmarg = realloc \
|
||||
((p)->engine.ps.psmarg, \
|
||||
(p)->engine.ps.psmargsz); \
|
||||
if (NULL == (p)->engine.ps.psmarg) { \
|
||||
perror(NULL); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
||||
#endif
|
||||
static void ps_letter(struct termp *, char);
|
||||
static void ps_begin(struct termp *);
|
||||
static void ps_end(struct termp *);
|
||||
static void ps_advance(struct termp *, size_t);
|
||||
static void ps_endline(struct termp *);
|
||||
static void ps_fclose(struct termp *);
|
||||
static void ps_pclose(struct termp *);
|
||||
static void ps_pletter(struct termp *, char);
|
||||
static void ps_printf(struct termp *, const char *, ...);
|
||||
static void ps_putchar(struct termp *, char);
|
||||
static void ps_setfont(struct termp *, enum termfont);
|
||||
|
||||
|
||||
void *
|
||||
ps_alloc(void)
|
||||
{
|
||||
struct termp *p;
|
||||
|
||||
if (NULL == (p = term_alloc(TERMENC_ASCII)))
|
||||
return(NULL);
|
||||
|
||||
p->type = TERMTYPE_PS;
|
||||
p->letter = ps_letter;
|
||||
p->begin = ps_begin;
|
||||
p->end = ps_end;
|
||||
p->advance = ps_advance;
|
||||
p->endline = ps_endline;
|
||||
return(p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ps_free(void *arg)
|
||||
{
|
||||
struct termp *p;
|
||||
|
||||
p = (struct termp *)arg;
|
||||
|
||||
if (p->engine.ps.psmarg)
|
||||
free(p->engine.ps.psmarg);
|
||||
|
||||
term_free(p);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_printf(struct termp *p, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int pos;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
/*
|
||||
* If we're running in regular mode, then pipe directly into
|
||||
* vprintf(). If we're processing margins, then push the data
|
||||
* into our growable margin buffer.
|
||||
*/
|
||||
|
||||
if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: I assume that the in-margin print won't exceed
|
||||
* PS_BUFSLOP (128 bytes), which is reasonable but still an
|
||||
* assumption that will cause pukeage if it's not the case.
|
||||
*/
|
||||
|
||||
PS_GROWBUF(p, PS_BUFSLOP);
|
||||
|
||||
pos = (int)p->engine.ps.psmargcur;
|
||||
vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap);
|
||||
p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_putchar(struct termp *p, char c)
|
||||
{
|
||||
int pos;
|
||||
|
||||
/* See ps_printf(). */
|
||||
|
||||
if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
|
||||
putchar(c);
|
||||
return;
|
||||
}
|
||||
|
||||
PS_GROWBUF(p, 2);
|
||||
|
||||
pos = (int)p->engine.ps.psmargcur++;
|
||||
p->engine.ps.psmarg[pos++] = c;
|
||||
p->engine.ps.psmarg[pos] = '\0';
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
ps_end(struct termp *p)
|
||||
{
|
||||
|
||||
/*
|
||||
* At the end of the file, do one last showpage. This is the
|
||||
* same behaviour as groff(1) and works for multiple pages as
|
||||
* well as just one.
|
||||
*/
|
||||
|
||||
assert(0 == p->engine.ps.psstate);
|
||||
assert('\0' == p->engine.ps.last);
|
||||
assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
|
||||
printf("%s", p->engine.ps.psmarg);
|
||||
printf("showpage\n");
|
||||
printf("%s\n", "%%EOF");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_begin(struct termp *p)
|
||||
{
|
||||
|
||||
/*
|
||||
* Print margins into margin buffer. Nothing gets output to the
|
||||
* screen yet, so we don't need to initialise the primary state.
|
||||
*/
|
||||
|
||||
if (p->engine.ps.psmarg) {
|
||||
assert(p->engine.ps.psmargsz);
|
||||
p->engine.ps.psmarg[0] = '\0';
|
||||
}
|
||||
|
||||
p->engine.ps.psmargcur = 0;
|
||||
p->engine.ps.psstate = PS_MARGINS;
|
||||
p->engine.ps.pscol = PS_CHAR_LEFT;
|
||||
p->engine.ps.psrow = PS_CHAR_TOPMARG;
|
||||
|
||||
ps_setfont(p, TERMFONT_NONE);
|
||||
|
||||
(*p->headf)(p, p->argf);
|
||||
(*p->endline)(p);
|
||||
|
||||
p->engine.ps.pscol = PS_CHAR_LEFT;
|
||||
p->engine.ps.psrow = PS_CHAR_BOTMARG;
|
||||
|
||||
(*p->footf)(p, p->argf);
|
||||
(*p->endline)(p);
|
||||
|
||||
p->engine.ps.psstate &= ~PS_MARGINS;
|
||||
|
||||
assert(0 == p->engine.ps.psstate);
|
||||
assert(p->engine.ps.psmarg);
|
||||
assert('\0' != p->engine.ps.psmarg[0]);
|
||||
|
||||
/*
|
||||
* Print header and initialise page state. Following this,
|
||||
* stuff gets printed to the screen, so make sure we're sane.
|
||||
*/
|
||||
|
||||
printf("%s\n", "%!PS");
|
||||
ps_setfont(p, TERMFONT_NONE);
|
||||
p->engine.ps.pscol = PS_CHAR_LEFT;
|
||||
p->engine.ps.psrow = PS_CHAR_TOP;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_pletter(struct termp *p, char c)
|
||||
{
|
||||
|
||||
/*
|
||||
* If we're not in a PostScript "word" context, then open one
|
||||
* now at the current cursor.
|
||||
*/
|
||||
|
||||
if ( ! (PS_INLINE & p->engine.ps.psstate)) {
|
||||
ps_printf(p, "%zu %zu moveto\n(",
|
||||
p->engine.ps.pscol,
|
||||
p->engine.ps.psrow);
|
||||
p->engine.ps.psstate |= PS_INLINE;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to escape these characters as per the PostScript
|
||||
* specification. We would also escape non-graphable characters
|
||||
* (like tabs), but none of them would get to this point and
|
||||
* it's superfluous to abort() on them.
|
||||
*/
|
||||
|
||||
switch (c) {
|
||||
case ('('):
|
||||
/* FALLTHROUGH */
|
||||
case (')'):
|
||||
/* FALLTHROUGH */
|
||||
case ('\\'):
|
||||
ps_putchar(p, '\\');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write the character and adjust where we are on the page. */
|
||||
|
||||
ps_putchar(p, c);
|
||||
p->engine.ps.pscol += PS_CHAR_WIDTH;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_pclose(struct termp *p)
|
||||
{
|
||||
|
||||
/*
|
||||
* Spit out that we're exiting a word context (this is a
|
||||
* "partial close" because we don't check the last-char buffer
|
||||
* or anything).
|
||||
*/
|
||||
|
||||
if ( ! (PS_INLINE & p->engine.ps.psstate))
|
||||
return;
|
||||
|
||||
ps_printf(p, ") show\n");
|
||||
p->engine.ps.psstate &= ~PS_INLINE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_fclose(struct termp *p)
|
||||
{
|
||||
|
||||
/*
|
||||
* Strong closure: if we have a last-char, spit it out after
|
||||
* checking that we're in the right font mode. This will of
|
||||
* course open a new scope, if applicable.
|
||||
*
|
||||
* Following this, close out any scope that's open.
|
||||
*/
|
||||
|
||||
if ('\0' != p->engine.ps.last) {
|
||||
if (p->engine.ps.lastf != TERMFONT_NONE) {
|
||||
ps_pclose(p);
|
||||
ps_setfont(p, TERMFONT_NONE);
|
||||
}
|
||||
ps_pletter(p, p->engine.ps.last);
|
||||
p->engine.ps.last = '\0';
|
||||
}
|
||||
|
||||
if ( ! (PS_INLINE & p->engine.ps.psstate))
|
||||
return;
|
||||
|
||||
ps_pclose(p);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_letter(struct termp *p, char c)
|
||||
{
|
||||
char cc;
|
||||
|
||||
/*
|
||||
* State machine dictates whether to buffer the last character
|
||||
* or not. Basically, encoded words are detected by checking if
|
||||
* we're an "8" and switching on the buffer. Then we put "8" in
|
||||
* our buffer, and on the next charater, flush both character
|
||||
* and buffer. Thus, "regular" words are detected by having a
|
||||
* regular character and a regular buffer character.
|
||||
*/
|
||||
|
||||
if ('\0' == p->engine.ps.last) {
|
||||
assert(8 != c);
|
||||
p->engine.ps.last = c;
|
||||
return;
|
||||
} else if (8 == p->engine.ps.last) {
|
||||
assert(8 != c);
|
||||
p->engine.ps.last = '\0';
|
||||
} else if (8 == c) {
|
||||
assert(8 != p->engine.ps.last);
|
||||
if ('_' == p->engine.ps.last) {
|
||||
if (p->engine.ps.lastf != TERMFONT_UNDER) {
|
||||
ps_pclose(p);
|
||||
ps_setfont(p, TERMFONT_UNDER);
|
||||
}
|
||||
} else if (p->engine.ps.lastf != TERMFONT_BOLD) {
|
||||
ps_pclose(p);
|
||||
ps_setfont(p, TERMFONT_BOLD);
|
||||
}
|
||||
p->engine.ps.last = c;
|
||||
return;
|
||||
} else {
|
||||
if (p->engine.ps.lastf != TERMFONT_NONE) {
|
||||
ps_pclose(p);
|
||||
ps_setfont(p, TERMFONT_NONE);
|
||||
}
|
||||
cc = p->engine.ps.last;
|
||||
p->engine.ps.last = c;
|
||||
c = cc;
|
||||
}
|
||||
|
||||
ps_pletter(p, c);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_advance(struct termp *p, size_t len)
|
||||
{
|
||||
|
||||
/*
|
||||
* Advance some spaces. This can probably be made smarter,
|
||||
* i.e., to have multiple space-separated words in the same
|
||||
* scope, but this is easier: just close out the current scope
|
||||
* and readjust our column settings.
|
||||
*/
|
||||
|
||||
ps_fclose(p);
|
||||
p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_endline(struct termp *p)
|
||||
{
|
||||
|
||||
/* Close out any scopes we have open: we're at eoln. */
|
||||
|
||||
ps_fclose(p);
|
||||
|
||||
/*
|
||||
* If we're in the margin, don't try to recalculate our current
|
||||
* row. XXX: if the column tries to be fancy with multiple
|
||||
* lines, we'll do nasty stuff.
|
||||
*/
|
||||
|
||||
if (PS_MARGINS & p->engine.ps.psstate)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Put us down a line. If we're at the page bottom, spit out a
|
||||
* showpage and restart our row.
|
||||
*/
|
||||
|
||||
p->engine.ps.pscol = PS_CHAR_LEFT;
|
||||
if (p->engine.ps.psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) {
|
||||
p->engine.ps.psrow -= PS_CHAR_HEIGHT;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
|
||||
printf("%s", p->engine.ps.psmarg);
|
||||
printf("showpage\n");
|
||||
p->engine.ps.psrow = PS_CHAR_TOP;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps_setfont(struct termp *p, enum termfont f)
|
||||
{
|
||||
|
||||
if (TERMFONT_BOLD == f)
|
||||
ps_printf(p, "/Courier-Bold\n");
|
||||
else if (TERMFONT_UNDER == f)
|
||||
ps_printf(p, "/Courier-Oblique\n");
|
||||
else
|
||||
ps_printf(p, "/Courier\n");
|
||||
|
||||
ps_printf(p, "10 selectfont\n");
|
||||
p->engine.ps.lastf = f;
|
||||
}
|
||||
|
8
commands/mdocml/test-strlcat.c
Normal file
8
commands/mdocml/test-strlcat.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <string.h>
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
strlcat(argv[0], argv[1], 10);
|
||||
return 0;
|
||||
}
|
8
commands/mdocml/test-strlcpy.c
Normal file
8
commands/mdocml/test-strlcpy.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <string.h>
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
strlcpy(argv[0], argv[1], 10);
|
||||
return 0;
|
||||
}
|
213
commands/mdocml/tree.c
Normal file
213
commands/mdocml/tree.c
Normal file
|
@ -0,0 +1,213 @@
|
|||
/* $Id: tree.c,v 1.21 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "mdoc.h"
|
||||
#include "man.h"
|
||||
#include "main.h"
|
||||
|
||||
static void print_mdoc(const struct mdoc_node *, int);
|
||||
static void print_man(const struct man_node *, int);
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
void
|
||||
tree_mdoc(void *arg, const struct mdoc *mdoc)
|
||||
{
|
||||
|
||||
print_mdoc(mdoc_node(mdoc), 0);
|
||||
}
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
void
|
||||
tree_man(void *arg, const struct man *man)
|
||||
{
|
||||
|
||||
print_man(man_node(man), 0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_mdoc(const struct mdoc_node *n, int indent)
|
||||
{
|
||||
const char *p, *t;
|
||||
int i, j;
|
||||
size_t argc, sz;
|
||||
char **params;
|
||||
struct mdoc_argv *argv;
|
||||
|
||||
argv = NULL;
|
||||
argc = sz = 0;
|
||||
params = NULL;
|
||||
|
||||
switch (n->type) {
|
||||
case (MDOC_ROOT):
|
||||
t = "root";
|
||||
break;
|
||||
case (MDOC_BLOCK):
|
||||
t = "block";
|
||||
break;
|
||||
case (MDOC_HEAD):
|
||||
t = "block-head";
|
||||
break;
|
||||
case (MDOC_BODY):
|
||||
t = "block-body";
|
||||
break;
|
||||
case (MDOC_TAIL):
|
||||
t = "block-tail";
|
||||
break;
|
||||
case (MDOC_ELEM):
|
||||
t = "elem";
|
||||
break;
|
||||
case (MDOC_TEXT):
|
||||
t = "text";
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
switch (n->type) {
|
||||
case (MDOC_TEXT):
|
||||
p = n->string;
|
||||
break;
|
||||
case (MDOC_BODY):
|
||||
p = mdoc_macronames[n->tok];
|
||||
break;
|
||||
case (MDOC_HEAD):
|
||||
p = mdoc_macronames[n->tok];
|
||||
break;
|
||||
case (MDOC_TAIL):
|
||||
p = mdoc_macronames[n->tok];
|
||||
break;
|
||||
case (MDOC_ELEM):
|
||||
p = mdoc_macronames[n->tok];
|
||||
if (n->args) {
|
||||
argv = n->args->argv;
|
||||
argc = n->args->argc;
|
||||
}
|
||||
break;
|
||||
case (MDOC_BLOCK):
|
||||
p = mdoc_macronames[n->tok];
|
||||
if (n->args) {
|
||||
argv = n->args->argv;
|
||||
argc = n->args->argc;
|
||||
}
|
||||
break;
|
||||
case (MDOC_ROOT):
|
||||
p = "root";
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
for (i = 0; i < indent; i++)
|
||||
(void)printf(" ");
|
||||
(void)printf("%s (%s)", p, t);
|
||||
|
||||
for (i = 0; i < (int)argc; i++) {
|
||||
(void)printf(" -%s", mdoc_argnames[argv[i].arg]);
|
||||
if (argv[i].sz > 0)
|
||||
(void)printf(" [");
|
||||
for (j = 0; j < (int)argv[i].sz; j++)
|
||||
(void)printf(" [%s]", argv[i].value[j]);
|
||||
if (argv[i].sz > 0)
|
||||
(void)printf(" ]");
|
||||
}
|
||||
|
||||
for (i = 0; i < (int)sz; i++)
|
||||
(void)printf(" [%s]", params[i]);
|
||||
|
||||
(void)printf(" %d:%d\n", n->line, n->pos);
|
||||
|
||||
if (n->child)
|
||||
print_mdoc(n->child, indent + 1);
|
||||
if (n->next)
|
||||
print_mdoc(n->next, indent);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_man(const struct man_node *n, int indent)
|
||||
{
|
||||
const char *p, *t;
|
||||
int i;
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_ROOT):
|
||||
t = "root";
|
||||
break;
|
||||
case (MAN_ELEM):
|
||||
t = "elem";
|
||||
break;
|
||||
case (MAN_TEXT):
|
||||
t = "text";
|
||||
break;
|
||||
case (MAN_BLOCK):
|
||||
t = "block";
|
||||
break;
|
||||
case (MAN_HEAD):
|
||||
t = "block-head";
|
||||
break;
|
||||
case (MAN_BODY):
|
||||
t = "block-body";
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
switch (n->type) {
|
||||
case (MAN_TEXT):
|
||||
p = n->string;
|
||||
break;
|
||||
case (MAN_ELEM):
|
||||
/* FALLTHROUGH */
|
||||
case (MAN_BLOCK):
|
||||
/* FALLTHROUGH */
|
||||
case (MAN_HEAD):
|
||||
/* FALLTHROUGH */
|
||||
case (MAN_BODY):
|
||||
p = man_macronames[n->tok];
|
||||
break;
|
||||
case (MAN_ROOT):
|
||||
p = "root";
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
for (i = 0; i < indent; i++)
|
||||
(void)printf(" ");
|
||||
(void)printf("%s (%s) %d:%d\n", p, t, n->line, n->pos);
|
||||
|
||||
if (n->child)
|
||||
print_man(n->child, indent + 1);
|
||||
if (n->next)
|
||||
print_man(n->next, indent);
|
||||
}
|
38
commands/mdocml/vol.c
Normal file
38
commands/mdocml/vol.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* $Id: vol.c,v 1.8 2010/06/19 20:46:28 kristaps Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mandoc.h"
|
||||
#include "libmdoc.h"
|
||||
|
||||
#define LINE(x, y) \
|
||||
if (0 == strcmp(p, x)) return(y);
|
||||
|
||||
const char *
|
||||
mdoc_a2vol(const char *p)
|
||||
{
|
||||
|
||||
#include "vol.in"
|
||||
|
||||
return(NULL);
|
||||
}
|
35
commands/mdocml/vol.in
Normal file
35
commands/mdocml/vol.in
Normal 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")
|
Loading…
Reference in a new issue