*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef HTML_H
-#define HTML_H
-__BEGIN_DECLS
-
enum htmltag {
TAG_HTML,
TAG_HEAD,
TAG_BODY,
TAG_META,
TAG_TITLE,
TAG_DIV,
TAG_H1,
TAG_H2,
TAG_SPAN,
TAG_LINK,
TAG_BR,
TAG_A,
TAG_TABLE,
TAG_TBODY,
TAG_COL,
TAG_TR,
TAG_TD,
TAG_LI,
TAG_UL,
TAG_OL,
TAG_DL,
TAG_DT,
TAG_DD,
TAG_BLOCKQUOTE,
TAG_PRE,
TAG_B,
TAG_I,
TAG_CODE,
TAG_SMALL,
TAG_STYLE,
TAG_MATH,
TAG_MROW,
TAG_MI,
TAG_MO,
TAG_MSUP,
TAG_MSUB,
TAG_MSUBSUP,
TAG_MFRAC,
TAG_MSQRT,
TAG_MFENCED,
TAG_MTABLE,
TAG_MTR,
TAG_MTD,
TAG_MUNDEROVER,
TAG_MUNDER,
TAG_MOVER,
TAG_MAX
};
enum htmlattr {
ATTR_NAME,
ATTR_REL,
ATTR_HREF,
ATTR_TYPE,
ATTR_MEDIA,
ATTR_CLASS,
ATTR_STYLE,
ATTR_ID,
ATTR_COLSPAN,
ATTR_CHARSET,
ATTR_OPEN,
ATTR_CLOSE,
ATTR_MATHVARIANT,
ATTR_MAX
};
enum htmlfont {
HTMLFONT_NONE = 0,
HTMLFONT_BOLD,
HTMLFONT_ITALIC,
HTMLFONT_BI,
HTMLFONT_MAX
};
struct tag {
struct tag *next;
enum htmltag tag;
};
struct tagq {
struct tag *head;
};
struct htmlpair {
enum htmlattr key;
const char *val;
};
#define PAIR_INIT(p, t, v) \
do { \
(p)->key = (t); \
(p)->val = (v); \
} while (/* CONSTCOND */ 0)
#define PAIR_ID_INIT(p, v) PAIR_INIT(p, ATTR_ID, v)
#define PAIR_CLASS_INIT(p, v) PAIR_INIT(p, ATTR_CLASS, v)
#define PAIR_HREF_INIT(p, v) PAIR_INIT(p, ATTR_HREF, v)
#define PAIR_STYLE_INIT(p, h) PAIR_INIT(p, ATTR_STYLE, (h)->buf)
struct html {
int flags;
#define HTML_NOSPACE (1 << 0) /* suppress next space */
#define HTML_IGNDELIM (1 << 1)
#define HTML_KEEP (1 << 2)
#define HTML_PREKEEP (1 << 3)
#define HTML_NONOSPACE (1 << 4) /* never add spaces */
#define HTML_LITERAL (1 << 5) /* literal (e.g., ) context */
#define HTML_SKIPCHAR (1 << 6) /* skip the next character */
#define HTML_NOSPLIT (1 << 7) /* do not break line before .An */
#define HTML_SPLIT (1 << 8) /* break line before .An */
+#define HTML_NONEWLINE (1 << 9) /* No line break in nofill mode. */
struct tagq tags; /* stack of open tags */
struct rofftbl tbl; /* current table */
struct tag *tblt; /* current open table scope */
const struct mchars *symtab; /* character table */
char *base_man; /* base for manpage href */
char *base_includes; /* base for include href */
char *style; /* style-sheet URI */
char buf[BUFSIZ]; /* see bufcat and friends */
size_t buflen;
struct tag *metaf; /* current open font scope */
enum htmlfont metal; /* last used font */
enum htmlfont metac; /* current font mode */
int oflags; /* output options */
#define HTML_FRAGMENT (1 << 0) /* don't emit HTML/HEAD/BODY */
};
+__BEGIN_DECLS
+
+struct tbl_span;
+struct eqn;
+
void print_gen_decls(struct html *);
void print_gen_head(struct html *);
struct tag *print_otag(struct html *, enum htmltag,
int, const struct htmlpair *);
void print_tagq(struct html *, const struct tag *);
void print_stagq(struct html *, const struct tag *);
void print_text(struct html *, const char *);
void print_tblclose(struct html *);
void print_tbl(struct html *, const struct tbl_span *);
void print_eqn(struct html *, const struct eqn *);
void print_paragraph(struct html *);
#if __GNUC__ - 0 >= 4
__attribute__((__format__ (__printf__, 2, 3)))
#endif
void bufcat_fmt(struct html *, const char *, ...);
void bufcat(struct html *, const char *);
void bufcat_id(struct html *, const char *);
void bufcat_style(struct html *,
const char *, const char *);
void bufcat_su(struct html *, const char *,
const struct roffsu *);
void bufinit(struct html *);
void buffmt_man(struct html *,
const char *, const char *);
void buffmt_includes(struct html *, const char *);
int html_strlen(const char *);
__END_DECLS
-
-#endif /*!HTML_H*/
Index: vendor/mdocml/dist/libman.h
===================================================================
--- vendor/mdocml/dist/libman.h (revision 276215)
+++ vendor/mdocml/dist/libman.h (revision 276216)
@@ -1,79 +1,75 @@
-/* $Id: libman.h,v 1.65 2014/11/28 05:51:32 schwarze Exp $ */
+/* $Id: libman.h,v 1.66 2014/12/01 04:05:31 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons
* Copyright (c) 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef LIBMAN_H
-#define LIBMAN_H
enum man_next {
MAN_NEXT_SIBLING = 0,
MAN_NEXT_CHILD
};
struct man {
struct mparse *parse; /* parse pointer */
int quick; /* abort parse early */
int flags; /* parse flags */
#define MAN_ELINE (1 << 1) /* Next-line element scope. */
#define MAN_BLINE (1 << 2) /* Next-line block scope. */
#define MAN_LITERAL (1 << 4) /* Literal input. */
#define MAN_NEWLINE (1 << 6) /* first macro/text in a line */
enum man_next next; /* where to put the next node */
struct man_node *last; /* the last parsed node */
struct man_node *first; /* the first parsed node */
struct man_meta meta; /* document meta-data */
struct roff *roff;
};
#define MACRO_PROT_ARGS struct man *man, \
enum mant tok, \
int line, \
int ppos, \
int *pos, \
char *buf
struct man_macro {
void (*fp)(MACRO_PROT_ARGS);
int flags;
#define MAN_SCOPED (1 << 0)
#define MAN_EXPLICIT (1 << 1) /* See blk_imp(). */
#define MAN_FSCOPED (1 << 2) /* See blk_imp(). */
#define MAN_NSCOPED (1 << 3) /* See in_line_eoln(). */
#define MAN_NOCLOSE (1 << 4) /* See blk_exp(). */
#define MAN_BSCOPE (1 << 5) /* Break BLINE scope. */
#define MAN_JOIN (1 << 6) /* Join arguments together. */
};
extern const struct man_macro *const man_macros;
__BEGIN_DECLS
void man_word_alloc(struct man *, int, int, const char *);
void man_word_append(struct man *, const char *);
void man_block_alloc(struct man *, int, int, enum mant);
void man_head_alloc(struct man *, int, int, enum mant);
void man_body_alloc(struct man *, int, int, enum mant);
void 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 *);
void man_macroend(struct man *);
void man_valid_post(struct man *);
void man_unscope(struct man *, const struct man_node *);
__END_DECLS
-
-#endif /*!LIBMAN_H*/
Index: vendor/mdocml/dist/libmandoc.h
===================================================================
--- vendor/mdocml/dist/libmandoc.h (revision 276215)
+++ vendor/mdocml/dist/libmandoc.h (revision 276216)
@@ -1,95 +1,96 @@
-/* $Id: libmandoc.h,v 1.49 2014/11/28 06:27:05 schwarze Exp $ */
+/* $Id: libmandoc.h,v 1.51 2014/12/01 08:05:52 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons
* Copyright (c) 2013, 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef LIBMANDOC_H
-#define LIBMANDOC_H
enum rofferr {
ROFF_CONT, /* continue processing line */
ROFF_RERUN, /* re-run roff interpreter with offset */
ROFF_APPEND, /* re-run main parser, appending next line */
ROFF_REPARSE, /* re-run main parser on the result */
ROFF_SO, /* include another file */
ROFF_IGN, /* ignore current line */
ROFF_TBL, /* a table row was successfully parsed */
ROFF_EQN, /* an equation was successfully parsed */
ROFF_ERR /* badness: puke and stop */
};
struct buf {
char *buf;
size_t sz;
};
__BEGIN_DECLS
+struct mparse;
+struct mchars;
+enum mandocerr;
+struct tbl_span;
+struct eqn;
struct roff;
struct mdoc;
struct man;
void mandoc_msg(enum mandocerr, struct mparse *,
int, int, const char *);
#if __GNUC__ - 0 >= 4
__attribute__((__format__ (__printf__, 5, 6)))
#endif
void mandoc_vmsg(enum mandocerr, struct mparse *,
int, int, const char *, ...);
char *mandoc_getarg(struct mparse *, char **, int, int *);
char *mandoc_normdate(struct mparse *, char *, int, int);
int mandoc_eos(const char *, size_t);
int mandoc_strntoi(const char *, size_t, int);
const char *mandoc_a2msec(const char*);
void mdoc_free(struct mdoc *);
struct mdoc *mdoc_alloc(struct roff *, struct mparse *,
const char *, int);
void mdoc_reset(struct mdoc *);
int mdoc_parseln(struct mdoc *, int, char *, int);
int mdoc_endparse(struct mdoc *);
void mdoc_addspan(struct mdoc *, const struct tbl_span *);
void mdoc_addeqn(struct mdoc *, const struct eqn *);
void man_free(struct man *);
struct man *man_alloc(struct roff *, struct mparse *, int);
void man_reset(struct man *);
int man_parseln(struct man *, int, char *, int);
int man_endparse(struct man *);
void man_addspan(struct man *, const struct tbl_span *);
void man_addeqn(struct man *, const struct eqn *);
int preconv_cue(const struct buf *, size_t);
int preconv_encode(struct buf *, size_t *,
struct buf *, size_t *, int *);
void roff_free(struct roff *);
struct roff *roff_alloc(struct mparse *, const struct mchars *, int);
void roff_reset(struct roff *);
enum rofferr roff_parseln(struct roff *, int, struct buf *, int *);
void roff_endparse(struct roff *);
void roff_setreg(struct roff *, const char *, int, char sign);
int roff_getreg(const struct roff *, const char *);
char *roff_strdup(const struct roff *, const char *);
int roff_getcontrol(const struct roff *,
const char *, int *);
int roff_getformat(const struct roff *);
const struct tbl_span *roff_span(const struct roff *);
const struct eqn *roff_eqn(const struct roff *);
__END_DECLS
-
-#endif /*!LIBMANDOC_H*/
Index: vendor/mdocml/dist/libmdoc.h
===================================================================
--- vendor/mdocml/dist/libmdoc.h (revision 276215)
+++ vendor/mdocml/dist/libmdoc.h (revision 276216)
@@ -1,133 +1,129 @@
-/* $Id: libmdoc.h,v 1.95 2014/11/29 03:37:44 schwarze Exp $ */
+/* $Id: libmdoc.h,v 1.96 2014/12/01 04:05:32 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons
* Copyright (c) 2013, 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef LIBMDOC_H
-#define LIBMDOC_H
enum mdoc_next {
MDOC_NEXT_SIBLING = 0,
MDOC_NEXT_CHILD
};
struct mdoc {
struct mparse *parse; /* parse pointer */
const char *defos; /* default argument for .Os */
int quick; /* abort parse early */
int flags; /* parse flags */
#define MDOC_LITERAL (1 << 1) /* in a literal scope */
#define MDOC_PBODY (1 << 2) /* in the document body */
#define MDOC_NEWLINE (1 << 3) /* first macro/text in a line */
#define MDOC_PHRASELIT (1 << 4) /* literal within a partila phrase */
#define MDOC_PPHRASE (1 << 5) /* within a partial phrase */
#define MDOC_FREECOL (1 << 6) /* `It' invocation should close */
#define MDOC_SYNOPSIS (1 << 7) /* SYNOPSIS-style formatting */
#define MDOC_KEEP (1 << 8) /* in a word keep */
#define MDOC_SMOFF (1 << 9) /* spacing is off */
#define MDOC_NODELIMC (1 << 10) /* disable closing delimiter handling */
enum mdoc_next next; /* where to put the next node */
struct mdoc_node *last; /* the last node parsed */
struct mdoc_node *first; /* the first node parsed */
struct mdoc_node *last_es; /* the most recent Es node */
struct mdoc_meta meta; /* document meta-data */
enum mdoc_sec lastnamed;
enum mdoc_sec lastsec;
struct roff *roff;
};
#define MACRO_PROT_ARGS struct mdoc *mdoc, \
enum mdoct tok, \
int line, \
int ppos, \
int *pos, \
char *buf
struct mdoc_macro {
void (*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)
#define MDOC_JOIN (1 << 5)
};
enum margserr {
ARGS_ERROR,
ARGS_EOLN, /* end-of-line */
ARGS_WORD, /* normal word */
ARGS_PUNCT, /* series of punctuation */
ARGS_QWORD, /* quoted word */
ARGS_PHRASE, /* Ta'd phrase (-column) */
ARGS_PPHRASE, /* tabbed phrase (-column) */
ARGS_PEND /* last phrase (-column) */
};
/*
* A punctuation delimiter is opening, closing, or "middle mark"
* punctuation. These govern spacing.
* Opening punctuation (e.g., the opening parenthesis) suppresses the
* following space; closing punctuation (e.g., the closing parenthesis)
* suppresses the leading space; middle punctuation (e.g., the vertical
* bar) can do either. The middle punctuation delimiter bends the rules
* depending on usage.
*/
enum mdelim {
DELIM_NONE = 0,
DELIM_OPEN,
DELIM_MIDDLE,
DELIM_CLOSE,
DELIM_MAX
};
extern const struct mdoc_macro *const mdoc_macros;
__BEGIN_DECLS
void mdoc_macro(MACRO_PROT_ARGS);
void mdoc_word_alloc(struct mdoc *, int, int, const char *);
void mdoc_word_append(struct mdoc *, const char *);
void mdoc_elem_alloc(struct mdoc *, int, int,
enum mdoct, struct mdoc_arg *);
struct mdoc_node *mdoc_block_alloc(struct mdoc *, int, int,
enum mdoct, struct mdoc_arg *);
struct mdoc_node *mdoc_head_alloc(struct mdoc *, int, int, enum mdoct);
void mdoc_tail_alloc(struct mdoc *, int, int, enum mdoct);
struct mdoc_node *mdoc_body_alloc(struct mdoc *, int, int, enum mdoct);
void mdoc_endbody_alloc(struct mdoc *, int, int, enum mdoct,
struct mdoc_node *, enum mdoc_endbody);
void mdoc_node_delete(struct mdoc *, struct mdoc_node *);
void mdoc_node_relink(struct mdoc *, struct mdoc_node *);
void mdoc_hash_init(void);
enum mdoct mdoc_hash_find(const char *);
const char *mdoc_a2att(const char *);
const char *mdoc_a2lib(const char *);
const char *mdoc_a2st(const char *);
const char *mdoc_a2arch(const char *);
void mdoc_valid_pre(struct mdoc *, struct mdoc_node *);
void mdoc_valid_post(struct mdoc *);
void mdoc_argv(struct mdoc *, int, enum mdoct,
struct mdoc_arg **, int *, char *);
void mdoc_argv_free(struct mdoc_arg *);
enum margserr mdoc_args(struct mdoc *, int,
int *, char *, enum mdoct, char **);
void mdoc_macroend(struct mdoc *);
enum mdelim mdoc_isdelim(const char *);
__END_DECLS
-
-#endif /*!LIBMDOC_H*/
Index: vendor/mdocml/dist/libroff.h
===================================================================
--- vendor/mdocml/dist/libroff.h (revision 276215)
+++ vendor/mdocml/dist/libroff.h (revision 276216)
@@ -1,88 +1,84 @@
-/* $Id: libroff.h,v 1.31 2014/10/25 14:35:37 schwarze Exp $ */
+/* $Id: libroff.h,v 1.33 2014/12/01 08:05:52 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons
* Copyright (c) 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef LIBROFF_H
-#define LIBROFF_H
-__BEGIN_DECLS
-
enum tbl_part {
TBL_PART_OPTS, /* in options (first line) */
TBL_PART_LAYOUT, /* describing layout */
TBL_PART_DATA, /* creating data rows */
TBL_PART_CDATA /* continue previous row */
};
struct tbl_node {
struct mparse *parse; /* parse point */
int pos; /* invocation column */
int line; /* invocation line */
enum tbl_part part;
struct tbl_opts opts;
struct tbl_row *first_row;
struct tbl_row *last_row;
struct tbl_span *first_span;
struct tbl_span *current_span;
struct tbl_span *last_span;
struct tbl_head *first_head;
struct tbl_head *last_head;
struct tbl_node *next;
};
struct eqn_node {
struct eqn eqn; /* syntax tree of this equation */
struct mparse *parse; /* main parser, for error reporting */
struct eqn_node *next; /* singly linked list of equations */
struct eqn_def *defs; /* array of definitions */
char *data; /* source code of this equation */
size_t defsz; /* number of definitions */
size_t sz; /* length of the source code */
size_t cur; /* parse point in the source code */
size_t rew; /* beginning of the current token */
int gsize; /* default point size */
int delim; /* in-line delimiters enabled */
char odelim; /* in-line opening delimiter */
char cdelim; /* in-line closing delimiter */
};
struct eqn_def {
char *key;
size_t keysz;
char *val;
size_t valsz;
};
+__BEGIN_DECLS
+
struct tbl_node *tbl_alloc(int, int, struct mparse *);
void tbl_restart(int, int, struct tbl_node *);
void tbl_free(struct tbl_node *);
void tbl_reset(struct tbl_node *);
enum rofferr tbl_read(struct tbl_node *, int, const char *, int);
int tbl_option(struct tbl_node *, int, const char *);
int tbl_layout(struct tbl_node *, int, const char *);
int tbl_data(struct tbl_node *, int, const char *);
int tbl_cdata(struct tbl_node *, int, const char *);
const struct tbl_span *tbl_span(struct tbl_node *);
void tbl_end(struct tbl_node **);
struct eqn_node *eqn_alloc(int, int, struct mparse *);
enum rofferr eqn_end(struct eqn_node **);
void eqn_free(struct eqn_node *);
enum rofferr eqn_read(struct eqn_node **, int,
const char *, int, int *);
__END_DECLS
-
-#endif /*LIBROFF_H*/
Index: vendor/mdocml/dist/main.c
===================================================================
--- vendor/mdocml/dist/main.c (revision 276215)
+++ vendor/mdocml/dist/main.c (revision 276216)
@@ -1,861 +1,897 @@
-/* $Id: main.c,v 1.200 2014/11/26 21:40:17 schwarze Exp $ */
+/* $Id: main.c,v 1.205 2014/12/11 19:19:35 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons
* Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze
* Copyright (c) 2010 Joerg Sonnenberger
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mandoc.h"
#include "mandoc_aux.h"
#include "main.h"
#include "mdoc.h"
#include "man.h"
#include "manpath.h"
#include "mansearch.h"
#if !defined(__GNUC__) || (__GNUC__ < 2)
# if !defined(lint)
# define __attribute__(x)
# endif
#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
enum outmode {
OUTMODE_DEF = 0,
OUTMODE_FLN,
OUTMODE_LST,
OUTMODE_ALL,
OUTMODE_INT,
OUTMODE_ONE
};
typedef void (*out_mdoc)(void *, const struct mdoc *);
typedef void (*out_man)(void *, const struct man *);
typedef void (*out_free)(void *);
enum outt {
OUTT_ASCII = 0, /* -Tascii */
OUTT_LOCALE, /* -Tlocale */
OUTT_UTF8, /* -Tutf8 */
OUTT_TREE, /* -Ttree */
OUTT_MAN, /* -Tman */
OUTT_HTML, /* -Thtml */
OUTT_LINT, /* -Tlint */
OUTT_PS, /* -Tps */
OUTT_PDF /* -Tpdf */
};
struct curparse {
struct mparse *mp;
struct mchars *mchars; /* character table */
enum mandoclevel wlevel; /* ignore messages below this */
int wstop; /* stop after a file with a warning */
enum outt outtype; /* which output to use */
out_mdoc outmdoc; /* mdoc output ptr */
out_man outman; /* man output ptr */
out_free outfree; /* free output ptr */
void *outdata; /* data for output */
char outopts[BUFSIZ]; /* buf of output opts */
};
static int koptions(int *, char *);
+#if HAVE_SQLITE3
+int mandocdb(int, char**);
+#endif
static int moptions(int *, char *);
static void mmsg(enum mandocerr, enum mandoclevel,
const char *, int, int, const char *);
static void parse(struct curparse *, int,
const char *, enum mandoclevel *);
+#if HAVE_SQLITE3
static enum mandoclevel passthrough(const char *, int, int);
+#endif
static void spawn_pager(void);
static int toptions(struct curparse *, char *);
static void usage(enum argmode) __attribute__((noreturn));
static void version(void) __attribute__((noreturn));
static int woptions(struct curparse *, char *);
static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
+static char help_arg[] = "help";
+static char *help_argv[] = {help_arg, NULL};
static const char *progname;
int
main(int argc, char *argv[])
{
struct curparse curp;
struct mansearch search;
struct manpaths paths;
- char *conf_file, *defpaths, *auxpaths;
+ char *auxpaths;
char *defos;
#if HAVE_SQLITE3
struct manpage *res, *resp;
+ char *conf_file, *defpaths;
size_t isec, i, sz;
- int prio, best_prio;
+ int prio, best_prio, synopsis_only;
char sec;
#endif
enum mandoclevel rc;
enum outmode outmode;
int fd;
int show_usage;
int use_pager;
- int synopsis_only;
int options;
int c;
progname = strrchr(argv[0], '/');
if (progname == NULL)
progname = argv[0];
else
++progname;
+#if HAVE_SQLITE3
+ if (strcmp(progname, BINM_MAKEWHATIS) == 0)
+ return(mandocdb(argc, argv));
+#endif
+
/* Search options. */
memset(&paths, 0, sizeof(struct manpaths));
- conf_file = defpaths = auxpaths = NULL;
+#if HAVE_SQLITE3
+ conf_file = defpaths = NULL;
+#endif
+ auxpaths = NULL;
memset(&search, 0, sizeof(struct mansearch));
search.outkey = "Nd";
- if (strcmp(progname, "man") == 0)
+ if (strcmp(progname, BINM_MAN) == 0)
search.argmode = ARG_NAME;
- else if (strncmp(progname, "apropos", 7) == 0)
+ else if (strcmp(progname, BINM_APROPOS) == 0)
search.argmode = ARG_EXPR;
- else if (strncmp(progname, "whatis", 6) == 0)
+ else if (strcmp(progname, BINM_WHATIS) == 0)
search.argmode = ARG_WORD;
+ else if (strncmp(progname, "help", 4) == 0)
+ search.argmode = ARG_NAME;
else
search.argmode = ARG_FILE;
/* Parser and formatter options. */
memset(&curp, 0, sizeof(struct curparse));
- curp.outtype = OUTT_ASCII;
+ curp.outtype = OUTT_LOCALE;
curp.wlevel = MANDOCLEVEL_FATAL;
options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1;
defos = NULL;
use_pager = 1;
show_usage = 0;
+#if HAVE_SQLITE3
synopsis_only = 0;
+#endif
outmode = OUTMODE_DEF;
while (-1 != (c = getopt(argc, argv,
"aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) {
switch (c) {
case 'a':
outmode = OUTMODE_ALL;
break;
case 'C':
+#if HAVE_SQLITE3
conf_file = optarg;
+#endif
break;
case 'c':
use_pager = 0;
break;
case 'f':
search.argmode = ARG_WORD;
break;
case 'h':
(void)strlcat(curp.outopts, "synopsis,", BUFSIZ);
+#if HAVE_SQLITE3
synopsis_only = 1;
+#endif
use_pager = 0;
outmode = OUTMODE_ALL;
break;
case 'I':
if (strncmp(optarg, "os=", 3)) {
fprintf(stderr,
"%s: -I%s: Bad argument\n",
progname, optarg);
return((int)MANDOCLEVEL_BADARG);
}
if (defos) {
fprintf(stderr,
"%s: -I%s: Duplicate argument\n",
progname, optarg);
return((int)MANDOCLEVEL_BADARG);
}
defos = mandoc_strdup(optarg + 3);
break;
case 'i':
outmode = OUTMODE_INT;
break;
case 'K':
if ( ! koptions(&options, optarg))
return((int)MANDOCLEVEL_BADARG);
break;
case 'k':
search.argmode = ARG_EXPR;
break;
case 'l':
search.argmode = ARG_FILE;
outmode = OUTMODE_ALL;
break;
case 'M':
+#if HAVE_SQLITE3
defpaths = optarg;
+#endif
break;
case 'm':
auxpaths = optarg;
break;
case 'O':
search.outkey = optarg;
(void)strlcat(curp.outopts, optarg, BUFSIZ);
(void)strlcat(curp.outopts, ",", BUFSIZ);
break;
case 'S':
search.arch = optarg;
break;
case 's':
search.sec = optarg;
break;
case 'T':
if ( ! toptions(&curp, optarg))
return((int)MANDOCLEVEL_BADARG);
break;
case 'W':
if ( ! woptions(&curp, optarg))
return((int)MANDOCLEVEL_BADARG);
break;
case 'w':
outmode = OUTMODE_FLN;
break;
case 'V':
version();
/* NOTREACHED */
default:
show_usage = 1;
break;
}
}
if (show_usage)
usage(search.argmode);
/* Postprocess options. */
if (outmode == OUTMODE_DEF) {
switch (search.argmode) {
case ARG_FILE:
outmode = OUTMODE_ALL;
use_pager = 0;
break;
case ARG_NAME:
outmode = OUTMODE_ONE;
break;
default:
outmode = OUTMODE_LST;
break;
}
}
/* Parse arguments. */
argc -= optind;
argv += optind;
#if HAVE_SQLITE3
resp = NULL;
#endif
- /* Quirk for a man(1) section argument without -s. */
+ /*
+ * Quirks for help(1)
+ * and for a man(1) section argument without -s.
+ */
- if (search.argmode == ARG_NAME &&
- argv[0] != NULL &&
- isdigit((unsigned char)argv[0][0]) &&
- (argv[0][1] == '\0' || !strcmp(argv[0], "3p"))) {
- search.sec = argv[0];
- argv++;
- argc--;
+ if (search.argmode == ARG_NAME) {
+ if (*progname == 'h') {
+ if (argc == 0) {
+ argv = help_argv;
+ argc = 1;
+ }
+ } else if (argv[0] != NULL &&
+ isdigit((unsigned char)argv[0][0]) &&
+ (argv[0][1] == '\0' || !strcmp(argv[0], "3p"))) {
+ search.sec = argv[0];
+ argv++;
+ argc--;
+ }
}
rc = MANDOCLEVEL_OK;
/* man(1), whatis(1), apropos(1) */
if (search.argmode != ARG_FILE) {
#if HAVE_SQLITE3
if (argc == 0)
usage(search.argmode);
if (search.argmode == ARG_NAME &&
outmode == OUTMODE_ONE)
search.firstmatch = 1;
/* Access the mandoc database. */
manpath_parse(&paths, conf_file, defpaths, auxpaths);
mansearch_setup(1);
if( ! mansearch(&search, &paths, argc, argv, &res, &sz))
usage(search.argmode);
resp = res;
if (sz == 0) {
if (search.argmode == ARG_NAME)
fprintf(stderr, "%s: No entry for %s "
"in the manual.\n", progname, argv[0]);
rc = MANDOCLEVEL_BADARG;
goto out;
}
/*
* For standard man(1) and -a output mode,
* prepare for copying filename pointers
* into the program parameter array.
*/
if (outmode == OUTMODE_ONE) {
argc = 1;
best_prio = 10;
} else if (outmode == OUTMODE_ALL)
argc = (int)sz;
/* Iterate all matching manuals. */
for (i = 0; i < sz; i++) {
if (outmode == OUTMODE_FLN)
puts(res[i].file);
else if (outmode == OUTMODE_LST)
printf("%s - %s\n", res[i].names,
res[i].output == NULL ? "" :
res[i].output);
else if (outmode == OUTMODE_ONE) {
/* Search for the best section. */
isec = strcspn(res[i].file, "123456789");
sec = res[i].file[isec];
if ('\0' == sec)
continue;
prio = sec_prios[sec - '1'];
if (prio >= best_prio)
continue;
best_prio = prio;
resp = res + i;
}
}
/*
* For man(1), -a and -i output mode, fall through
* to the main mandoc(1) code iterating files
* and running the parsers on each of them.
*/
if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST)
goto out;
#else
fputs("mandoc: database support not compiled in\n",
stderr);
return((int)MANDOCLEVEL_BADARG);
#endif
}
/* mandoc(1) */
if ( ! moptions(&options, auxpaths))
return((int)MANDOCLEVEL_BADARG);
if (use_pager && isatty(STDOUT_FILENO))
spawn_pager();
curp.mchars = mchars_alloc();
curp.mp = mparse_alloc(options, curp.wlevel, mmsg,
curp.mchars, defos);
/*
* Conditionally start up the lookaside buffer before parsing.
*/
if (OUTT_MAN == curp.outtype)
mparse_keep(curp.mp);
if (argc == 0)
parse(&curp, STDIN_FILENO, "", &rc);
while (argc) {
#if HAVE_SQLITE3
if (resp != NULL) {
rc = mparse_open(curp.mp, &fd, resp->file);
if (fd == -1)
/* nothing */;
else if (resp->form & FORM_SRC) {
/* For .so only; ignore failure. */
chdir(paths.paths[resp->ipath]);
parse(&curp, fd, resp->file, &rc);
} else
rc = passthrough(resp->file, fd,
synopsis_only);
resp++;
} else
#endif
{
rc = mparse_open(curp.mp, &fd, *argv++);
if (fd != -1)
parse(&curp, fd, argv[-1], &rc);
}
if (mparse_wait(curp.mp) != MANDOCLEVEL_OK)
rc = MANDOCLEVEL_SYSERR;
if (MANDOCLEVEL_OK != rc && curp.wstop)
break;
argc--;
}
if (curp.outfree)
(*curp.outfree)(curp.outdata);
mparse_free(curp.mp);
mchars_free(curp.mchars);
#if HAVE_SQLITE3
out:
if (search.argmode != ARG_FILE) {
manpath_free(&paths);
mansearch_free(res, sz);
mansearch_setup(0);
}
#endif
free(defos);
return((int)rc);
}
static void
version(void)
{
printf("mandoc %s\n", VERSION);
exit((int)MANDOCLEVEL_OK);
}
static void
usage(enum argmode argmode)
{
switch (argmode) {
case ARG_FILE:
fputs("usage: mandoc [-acfhklV] [-Ios=name] "
"[-Kencoding] [-mformat] [-Ooption]\n"
"\t [-Toutput] [-Wlevel] [file ...]\n", stderr);
break;
case ARG_NAME:
fputs("usage: man [-acfhklVw] [-C file] "
"[-M path] [-m path] [-S arch] [-s section]\n"
"\t [section] name ...\n", stderr);
break;
case ARG_WORD:
fputs("usage: whatis [-acfhklVw] [-C file] "
"[-M path] [-m path] [-O outkey] [-S arch]\n"
"\t [-s section] name ...\n", stderr);
break;
case ARG_EXPR:
fputs("usage: apropos [-acfhklVw] [-C file] "
"[-M path] [-m path] [-O outkey] [-S arch]\n"
"\t [-s section] expression ...\n", stderr);
break;
}
exit((int)MANDOCLEVEL_BADARG);
}
static void
parse(struct curparse *curp, int fd, const char *file,
enum mandoclevel *level)
{
enum mandoclevel rc;
struct mdoc *mdoc;
struct man *man;
/* Begin by parsing the file itself. */
assert(file);
assert(fd >= -1);
rc = mparse_readfd(curp->mp, fd, file);
/* Stop immediately if the parse has failed. */
if (MANDOCLEVEL_FATAL <= rc)
goto cleanup;
/*
* With -Wstop and warnings or errors of at least the requested
* level, do not produce output.
*/
if (MANDOCLEVEL_OK != rc && curp->wstop)
goto cleanup;
/* If unset, allocate output dev now (if applicable). */
if ( ! (curp->outman && curp->outmdoc)) {
switch (curp->outtype) {
case OUTT_HTML:
curp->outdata = html_alloc(curp->mchars,
curp->outopts);
curp->outfree = html_free;
break;
case OUTT_UTF8:
curp->outdata = utf8_alloc(curp->mchars,
curp->outopts);
curp->outfree = ascii_free;
break;
case OUTT_LOCALE:
curp->outdata = locale_alloc(curp->mchars,
curp->outopts);
curp->outfree = ascii_free;
break;
case OUTT_ASCII:
curp->outdata = ascii_alloc(curp->mchars,
curp->outopts);
curp->outfree = ascii_free;
break;
case OUTT_PDF:
curp->outdata = pdf_alloc(curp->mchars,
curp->outopts);
curp->outfree = pspdf_free;
break;
case OUTT_PS:
curp->outdata = ps_alloc(curp->mchars,
curp->outopts);
curp->outfree = pspdf_free;
break;
default:
break;
}
switch (curp->outtype) {
case OUTT_HTML:
curp->outman = html_man;
curp->outmdoc = html_mdoc;
break;
case OUTT_TREE:
curp->outman = tree_man;
curp->outmdoc = tree_mdoc;
break;
case OUTT_MAN:
curp->outmdoc = man_mdoc;
curp->outman = man_man;
break;
case OUTT_PDF:
/* FALLTHROUGH */
case OUTT_ASCII:
/* FALLTHROUGH */
case OUTT_UTF8:
/* FALLTHROUGH */
case OUTT_LOCALE:
/* FALLTHROUGH */
case OUTT_PS:
curp->outman = terminal_man;
curp->outmdoc = terminal_mdoc;
break;
default:
break;
}
}
mparse_result(curp->mp, &mdoc, &man, NULL);
/* Execute the out device, if it exists. */
if (man && curp->outman)
(*curp->outman)(curp->outdata, man);
if (mdoc && curp->outmdoc)
(*curp->outmdoc)(curp->outdata, mdoc);
cleanup:
mparse_reset(curp->mp);
if (*level < rc)
*level = rc;
}
+#if HAVE_SQLITE3
static enum mandoclevel
passthrough(const char *file, int fd, int synopsis_only)
{
const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS";
const char synr[] = "SYNOPSIS";
FILE *stream;
const char *syscall;
char *line;
size_t len, off;
ssize_t nw;
int print;
if ((stream = fdopen(fd, "r")) == NULL) {
close(fd);
syscall = "fdopen";
goto fail;
}
print = 0;
while ((line = fgetln(stream, &len)) != NULL) {
if (synopsis_only) {
if (print) {
if ( ! isspace((unsigned char)*line))
goto done;
while (len &&
isspace((unsigned char)*line)) {
line++;
len--;
}
} else {
if ((len == sizeof(synb) &&
! strncmp(line, synb, len - 1)) ||
(len == sizeof(synr) &&
! strncmp(line, synr, len - 1)))
print = 1;
continue;
}
}
for (off = 0; off < len; off += nw)
if ((nw = write(STDOUT_FILENO, line + off,
len - off)) == -1 || nw == 0) {
fclose(stream);
syscall = "write";
goto fail;
}
}
if (ferror(stream)) {
fclose(stream);
syscall = "fgetln";
goto fail;
}
done:
fclose(stream);
return(MANDOCLEVEL_OK);
fail:
fprintf(stderr, "%s: %s: SYSERR: %s: %s",
progname, file, syscall, strerror(errno));
return(MANDOCLEVEL_SYSERR);
}
+#endif
static int
koptions(int *options, char *arg)
{
if ( ! strcmp(arg, "utf-8")) {
*options |= MPARSE_UTF8;
*options &= ~MPARSE_LATIN1;
} else if ( ! strcmp(arg, "iso-8859-1")) {
*options |= MPARSE_LATIN1;
*options &= ~MPARSE_UTF8;
} else if ( ! strcmp(arg, "us-ascii")) {
*options &= ~(MPARSE_UTF8 | MPARSE_LATIN1);
} else {
fprintf(stderr, "%s: -K%s: Bad argument\n",
progname, arg);
return(0);
}
return(1);
}
static int
moptions(int *options, char *arg)
{
if (arg == NULL)
/* nothing to do */;
else if (0 == strcmp(arg, "doc"))
*options |= MPARSE_MDOC;
else if (0 == strcmp(arg, "andoc"))
/* nothing to do */;
else if (0 == strcmp(arg, "an"))
*options |= MPARSE_MAN;
else {
fprintf(stderr, "%s: -m%s: Bad argument\n",
progname, arg);
return(0);
}
return(1);
}
static int
toptions(struct curparse *curp, char *arg)
{
if (0 == strcmp(arg, "ascii"))
curp->outtype = OUTT_ASCII;
else if (0 == strcmp(arg, "lint")) {
curp->outtype = OUTT_LINT;
curp->wlevel = MANDOCLEVEL_WARNING;
} else if (0 == strcmp(arg, "tree"))
curp->outtype = OUTT_TREE;
else if (0 == strcmp(arg, "man"))
curp->outtype = OUTT_MAN;
else if (0 == strcmp(arg, "html"))
curp->outtype = OUTT_HTML;
else if (0 == strcmp(arg, "utf8"))
curp->outtype = OUTT_UTF8;
else if (0 == strcmp(arg, "locale"))
curp->outtype = OUTT_LOCALE;
else if (0 == strcmp(arg, "xhtml"))
curp->outtype = OUTT_HTML;
else if (0 == strcmp(arg, "ps"))
curp->outtype = OUTT_PS;
else if (0 == strcmp(arg, "pdf"))
curp->outtype = OUTT_PDF;
else {
fprintf(stderr, "%s: -T%s: Bad argument\n",
progname, arg);
return(0);
}
return(1);
}
static int
woptions(struct curparse *curp, char *arg)
{
char *v, *o;
const char *toks[6];
toks[0] = "stop";
toks[1] = "all";
toks[2] = "warning";
toks[3] = "error";
toks[4] = "fatal";
toks[5] = NULL;
while (*arg) {
o = arg;
switch (getsubopt(&arg, UNCONST(toks), &v)) {
case 0:
curp->wstop = 1;
break;
case 1:
/* FALLTHROUGH */
case 2:
curp->wlevel = MANDOCLEVEL_WARNING;
break;
case 3:
curp->wlevel = MANDOCLEVEL_ERROR;
break;
case 4:
curp->wlevel = MANDOCLEVEL_FATAL;
break;
default:
fprintf(stderr, "%s: -W%s: Bad argument\n",
progname, o);
return(0);
}
}
return(1);
}
static void
mmsg(enum mandocerr t, enum mandoclevel lvl,
const char *file, int line, int col, const char *msg)
{
const char *mparse_msg;
fprintf(stderr, "%s: %s:", progname, file);
if (line)
fprintf(stderr, "%d:%d:", line, col + 1);
fprintf(stderr, " %s", mparse_strlevel(lvl));
if (NULL != (mparse_msg = mparse_strerror(t)))
fprintf(stderr, ": %s", mparse_msg);
if (msg)
fprintf(stderr, ": %s", msg);
fputc('\n', stderr);
}
static void
spawn_pager(void)
{
#define MAX_PAGER_ARGS 16
char *argv[MAX_PAGER_ARGS];
const char *pager;
char *cp;
int fildes[2];
int argc;
if (pipe(fildes) == -1) {
fprintf(stderr, "%s: pipe: %s\n",
progname, strerror(errno));
return;
}
switch (fork()) {
case -1:
fprintf(stderr, "%s: fork: %s\n",
progname, strerror(errno));
exit((int)MANDOCLEVEL_SYSERR);
case 0:
close(fildes[0]);
if (dup2(fildes[1], STDOUT_FILENO) == -1) {
fprintf(stderr, "%s: dup output: %s\n",
progname, strerror(errno));
exit((int)MANDOCLEVEL_SYSERR);
}
return;
default:
break;
}
/* The original process becomes the pager. */
close(fildes[1]);
if (dup2(fildes[0], STDIN_FILENO) == -1) {
fprintf(stderr, "%s: dup input: %s\n",
progname, strerror(errno));
exit((int)MANDOCLEVEL_SYSERR);
}
pager = getenv("MANPAGER");
if (pager == NULL || *pager == '\0')
pager = getenv("PAGER");
if (pager == NULL || *pager == '\0')
pager = "/usr/bin/more -s";
cp = mandoc_strdup(pager);
/*
* Parse the pager command into words.
* Intentionally do not do anything fancy here.
*/
argc = 0;
while (argc + 1 < MAX_PAGER_ARGS) {
argv[argc++] = cp;
cp = strchr(cp, ' ');
if (cp == NULL)
break;
*cp++ = '\0';
while (*cp == ' ')
cp++;
if (*cp == '\0')
break;
}
argv[argc] = NULL;
/* Hand over to the pager. */
execvp(argv[0], argv);
fprintf(stderr, "%s: exec: %s\n",
progname, strerror(errno));
exit((int)MANDOCLEVEL_SYSERR);
}
Index: vendor/mdocml/dist/main.h
===================================================================
--- vendor/mdocml/dist/main.h (revision 276215)
+++ vendor/mdocml/dist/main.h (revision 276216)
@@ -1,60 +1,56 @@
-/* $Id: main.h,v 1.17 2014/10/28 17:36:19 schwarze Exp $ */
+/* $Id: main.h,v 1.19 2014/12/01 08:05:52 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons
*
* 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
+#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
+
__BEGIN_DECLS
+struct mchars;
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(const struct mchars *, char *);
void html_mdoc(void *, const struct mdoc *);
void html_man(void *, const struct man *);
void html_free(void *);
void tree_mdoc(void *, const struct mdoc *);
void tree_man(void *, const struct man *);
void man_mdoc(void *, const struct mdoc *);
void man_man(void *, const struct man *);
void *locale_alloc(const struct mchars *, char *);
void *utf8_alloc(const struct mchars *, char *);
void *ascii_alloc(const struct mchars *, char *);
void ascii_free(void *);
void *pdf_alloc(const struct mchars *, char *);
void *ps_alloc(const struct mchars *, char *);
void pspdf_free(void *);
void terminal_mdoc(void *, const struct mdoc *);
void terminal_man(void *, const struct man *);
__END_DECLS
-
-#endif /*!MAIN_H*/
Index: vendor/mdocml/dist/man.h
===================================================================
--- vendor/mdocml/dist/man.h (revision 276215)
+++ vendor/mdocml/dist/man.h (revision 276216)
@@ -1,120 +1,116 @@
-/* $Id: man.h,v 1.66 2014/11/28 05:51:32 schwarze Exp $ */
+/* $Id: man.h,v 1.67 2014/12/01 04:05:32 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons
* Copyright (c) 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef MAN_H
-#define MAN_H
enum mant {
MAN_br = 0,
MAN_TH,
MAN_SH,
MAN_SS,
MAN_TP,
MAN_LP,
MAN_PP,
MAN_P,
MAN_IP,
MAN_HP,
MAN_SM,
MAN_SB,
MAN_BI,
MAN_IB,
MAN_BR,
MAN_RB,
MAN_R,
MAN_B,
MAN_I,
MAN_IR,
MAN_RI,
MAN_na,
MAN_sp,
MAN_nf,
MAN_fi,
MAN_RE,
MAN_RS,
MAN_DT,
MAN_UC,
MAN_PD,
MAN_AT,
MAN_in,
MAN_ft,
MAN_OP,
MAN_EX,
MAN_EE,
MAN_UR,
MAN_UE,
MAN_ll,
MAN_MAX
};
enum man_type {
MAN_TEXT,
MAN_ELEM,
MAN_ROOT,
MAN_BLOCK,
MAN_HEAD,
MAN_BODY,
MAN_TBL,
MAN_EQN
};
struct man_meta {
char *msec; /* `TH' section (1, 3p, etc.) */
char *date; /* `TH' normalised date */
char *vol; /* `TH' volume */
char *title; /* `TH' title (e.g., FOO) */
char *source; /* `TH' source (e.g., GNU) */
int hasbody; /* document is not empty */
};
struct man_node {
struct man_node *parent; /* parent AST node */
struct man_node *child; /* first child AST node */
struct man_node *next; /* sibling AST node */
struct man_node *prev; /* prior sibling AST node */
int nchild; /* number children */
int line;
int pos;
enum mant tok; /* tok or MAN__MAX if none */
int flags;
#define MAN_VALID (1 << 0) /* has been validated */
#define MAN_EOS (1 << 2) /* at sentence boundary */
#define MAN_LINE (1 << 3) /* first macro/text on line */
enum man_type type; /* AST node type */
char *string; /* TEXT node argument */
struct man_node *head; /* BLOCK node HEAD ptr */
struct man_node *tail; /* BLOCK node TAIL ptr */
struct man_node *body; /* BLOCK node BODY ptr */
const struct tbl_span *span; /* TBL */
const struct eqn *eqn; /* EQN */
};
/* Names of macros. Index is enum mant. */
extern const char *const *man_macronames;
__BEGIN_DECLS
struct man;
const struct man_node *man_node(const struct man *);
const struct man_meta *man_meta(const struct man *);
const struct mparse *man_mparse(const struct man *);
void man_deroff(char **, const struct man_node *);
__END_DECLS
-
-#endif /*!MAN_H*/
Index: vendor/mdocml/dist/man_hash.c
===================================================================
--- vendor/mdocml/dist/man_hash.c (revision 276215)
+++ vendor/mdocml/dist/man_hash.c (revision 276216)
@@ -1,103 +1,102 @@
-/* $Id: man_hash.c,v 1.28 2014/08/10 23:54:41 schwarze Exp $ */
+/* $Id: man_hash.c,v 1.29 2014/12/01 08:05:52 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include "man.h"
-#include "mandoc.h"
#include "libman.h"
#define HASH_DEPTH 6
#define HASH_ROW(x) do { \
if (isupper((unsigned char)(x))) \
(x) -= 65; \
else \
(x) -= 97; \
(x) *= HASH_DEPTH; \
} while (/* CONSTCOND */ 0)
/*
* Lookup table is indexed first by lower-case first letter (plus one
* for the period, which is stored in the last row), then by lower or
* uppercase second letter. Buckets correspond to the index of the
* macro (the integer value of the enum stored as a char to save a bit
* of space).
*/
static unsigned char table[26 * HASH_DEPTH];
/*
* XXX - this hash has global scope, so if intended for use as a library
* with multiple callers, it will need re-invocation protection.
*/
void
man_hash_init(void)
{
int i, j, x;
memset(table, UCHAR_MAX, sizeof(table));
assert(MAN_MAX < UCHAR_MAX);
for (i = 0; i < (int)MAN_MAX; i++) {
x = man_macronames[i][0];
assert(isalpha((unsigned char)x));
HASH_ROW(x);
for (j = 0; j < HASH_DEPTH; j++)
if (UCHAR_MAX == table[x + j]) {
table[x + j] = (unsigned char)i;
break;
}
assert(j < HASH_DEPTH);
}
}
enum mant
man_hash_find(const char *tmp)
{
int x, y, i;
enum mant tok;
if ('\0' == (x = tmp[0]))
return(MAN_MAX);
if ( ! (isalpha((unsigned char)x)))
return(MAN_MAX);
HASH_ROW(x);
for (i = 0; i < HASH_DEPTH; i++) {
if (UCHAR_MAX == (y = table[x + i]))
return(MAN_MAX);
tok = (enum mant)y;
if (0 == strcmp(tmp, man_macronames[tok]))
return(tok);
}
return(MAN_MAX);
}
Index: vendor/mdocml/dist/man_html.c
===================================================================
--- vendor/mdocml/dist/man_html.c (revision 276215)
+++ vendor/mdocml/dist/man_html.c (revision 276216)
@@ -1,683 +1,675 @@
-/* $Id: man_html.c,v 1.104 2014/09/27 11:17:19 kristaps Exp $ */
+/* $Id: man_html.c,v 1.107 2014/12/04 02:05:42 schwarze Exp $ */
/*
* Copyright (c) 2008-2012, 2014 Kristaps Dzonsons
* Copyright (c) 2013, 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
-#include "mandoc.h"
#include "mandoc_aux.h"
+#include "man.h"
#include "out.h"
#include "html.h"
-#include "man.h"
#include "main.h"
/* TODO: preserve ident widths. */
/* FIXME: have PD set the default vspace width. */
#define INDENT 5
#define MAN_ARGS const struct man_meta *man, \
const struct man_node *n, \
struct mhtml *mh, \
struct html *h
struct mhtml {
int fl;
#define MANH_LITERAL (1 << 0) /* literal context */
};
struct htmlman {
int (*pre)(MAN_ARGS);
int (*post)(MAN_ARGS);
};
static void print_bvspace(struct html *,
const struct man_node *);
static void print_man(MAN_ARGS);
static void print_man_head(MAN_ARGS);
static void print_man_nodelist(MAN_ARGS);
static void print_man_node(MAN_ARGS);
static int a2width(const struct man_node *,
struct roffsu *);
static int man_B_pre(MAN_ARGS);
static int man_HP_pre(MAN_ARGS);
static int man_IP_pre(MAN_ARGS);
static int man_I_pre(MAN_ARGS);
static int man_OP_pre(MAN_ARGS);
static int man_PP_pre(MAN_ARGS);
static int man_RS_pre(MAN_ARGS);
static int man_SH_pre(MAN_ARGS);
static int man_SM_pre(MAN_ARGS);
static int man_SS_pre(MAN_ARGS);
static int man_UR_pre(MAN_ARGS);
static int man_alt_pre(MAN_ARGS);
static int man_br_pre(MAN_ARGS);
static int man_ign_pre(MAN_ARGS);
static int man_in_pre(MAN_ARGS);
static int man_literal_pre(MAN_ARGS);
static void man_root_post(MAN_ARGS);
static void man_root_pre(MAN_ARGS);
static const struct htmlman mans[MAN_MAX] = {
{ man_br_pre, NULL }, /* br */
{ NULL, NULL }, /* TH */
{ man_SH_pre, NULL }, /* SH */
{ man_SS_pre, NULL }, /* SS */
{ man_IP_pre, NULL }, /* TP */
{ man_PP_pre, NULL }, /* LP */
{ man_PP_pre, NULL }, /* PP */
{ man_PP_pre, NULL }, /* P */
{ man_IP_pre, NULL }, /* IP */
{ man_HP_pre, NULL }, /* HP */
{ man_SM_pre, NULL }, /* SM */
{ man_SM_pre, NULL }, /* SB */
{ man_alt_pre, NULL }, /* BI */
{ man_alt_pre, NULL }, /* IB */
{ man_alt_pre, NULL }, /* BR */
{ man_alt_pre, NULL }, /* RB */
{ NULL, NULL }, /* R */
{ man_B_pre, NULL }, /* B */
{ man_I_pre, NULL }, /* I */
{ man_alt_pre, NULL }, /* IR */
{ man_alt_pre, NULL }, /* RI */
{ man_ign_pre, NULL }, /* na */
{ man_br_pre, NULL }, /* sp */
{ man_literal_pre, NULL }, /* nf */
{ man_literal_pre, NULL }, /* fi */
{ NULL, NULL }, /* RE */
{ man_RS_pre, NULL }, /* RS */
{ man_ign_pre, NULL }, /* DT */
{ man_ign_pre, NULL }, /* UC */
{ man_ign_pre, NULL }, /* PD */
{ man_ign_pre, NULL }, /* AT */
{ man_in_pre, NULL }, /* in */
{ man_ign_pre, NULL }, /* ft */
{ man_OP_pre, NULL }, /* OP */
{ man_literal_pre, NULL }, /* EX */
{ man_literal_pre, NULL }, /* EE */
{ man_UR_pre, NULL }, /* UR */
{ NULL, NULL }, /* UE */
{ man_ign_pre, NULL }, /* ll */
};
/*
* Printing leading vertical space before a block.
* This is used for the paragraph macros.
* The rules are pretty simple, since there's very little nesting going
* on here. Basically, if we're the first within another block (SS/SH),
* then don't emit vertical space. If we are (RS), then do. If not the
* first, print it.
*/
static void
print_bvspace(struct html *h, const struct man_node *n)
{
if (n->body && n->body->child)
if (MAN_TBL == n->body->child->type)
return;
if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
if (NULL == n->prev)
return;
print_paragraph(h);
}
void
html_man(void *arg, const struct man *man)
{
struct mhtml mh;
memset(&mh, 0, sizeof(struct mhtml));
print_man(man_meta(man), man_node(man), &mh, (struct html *)arg);
putchar('\n');
}
static void
print_man(MAN_ARGS)
{
struct tag *t, *tt;
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "mandoc");
if ( ! (HTML_FRAGMENT & h->oflags)) {
print_gen_decls(h);
t = print_otag(h, TAG_HTML, 0, NULL);
tt = print_otag(h, TAG_HEAD, 0, NULL);
print_man_head(man, n, mh, h);
print_tagq(h, tt);
print_otag(h, TAG_BODY, 0, NULL);
print_otag(h, TAG_DIV, 1, &tag);
} else
t = print_otag(h, TAG_DIV, 1, &tag);
print_man_nodelist(man, n, mh, h);
print_tagq(h, t);
}
static void
print_man_head(MAN_ARGS)
{
print_gen_head(h);
assert(man->title);
assert(man->msec);
bufcat_fmt(h, "%s(%s)", man->title, man->msec);
print_otag(h, TAG_TITLE, 0, NULL);
print_text(h, h->buf);
}
static void
print_man_nodelist(MAN_ARGS)
{
print_man_node(man, n, mh, h);
if (n->next)
print_man_nodelist(man, n->next, mh, h);
}
static void
print_man_node(MAN_ARGS)
{
int child;
struct tag *t;
child = 1;
t = h->tags.head;
switch (n->type) {
case MAN_ROOT:
man_root_pre(man, n, mh, h);
break;
case MAN_TEXT:
- /*
- * If we have a blank line, output a vertical space.
- * If we have a space as the first character, break
- * before printing the line's data.
- */
if ('\0' == *n->string) {
print_paragraph(h);
return;
}
-
- if (' ' == *n->string && MAN_LINE & n->flags)
+ if (n->flags & MAN_LINE && (*n->string == ' ' ||
+ (n->prev != NULL && mh->fl & MANH_LITERAL &&
+ ! (h->flags & HTML_NONEWLINE))))
print_otag(h, TAG_BR, 0, NULL);
- else if (MANH_LITERAL & mh->fl && n->prev)
- print_otag(h, TAG_BR, 0, NULL);
-
print_text(h, n->string);
return;
case MAN_EQN:
print_eqn(h, n->eqn);
break;
case MAN_TBL:
/*
* This will take care of initialising all of the table
* state data for the first table, then tearing it down
* for the last one.
*/
print_tbl(h, n->span);
return;
default:
/*
* Close out scope of font prior to opening a macro
* scope.
*/
if (HTMLFONT_NONE != h->metac) {
h->metal = h->metac;
h->metac = HTMLFONT_NONE;
}
/*
* Close out the current table, if it's open, and unset
* the "meta" table state. This will be reopened on the
* next table element.
*/
if (h->tblt) {
print_tblclose(h);
t = h->tags.head;
}
if (mans[n->tok].pre)
child = (*mans[n->tok].pre)(man, n, mh, h);
break;
}
if (child && n->child)
print_man_nodelist(man, n->child, mh, h);
/* This will automatically close out any font scope. */
print_stagq(h, t);
switch (n->type) {
case MAN_ROOT:
man_root_post(man, n, mh, h);
break;
case MAN_EQN:
break;
default:
if (mans[n->tok].post)
(*mans[n->tok].post)(man, n, mh, h);
break;
}
}
static int
a2width(const struct man_node *n, struct roffsu *su)
{
if (MAN_TEXT != n->type)
return(0);
- if (a2roffsu(n->string, su, SCALE_BU))
+ if (a2roffsu(n->string, su, SCALE_EN))
return(1);
return(0);
}
static void
man_root_pre(MAN_ARGS)
{
struct htmlpair tag;
struct tag *t, *tt;
char *title;
assert(man->title);
assert(man->msec);
mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
PAIR_CLASS_INIT(&tag, "head");
t = print_otag(h, TAG_TABLE, 1, &tag);
print_otag(h, TAG_TBODY, 0, NULL);
tt = print_otag(h, TAG_TR, 0, NULL);
PAIR_CLASS_INIT(&tag, "head-ltitle");
print_otag(h, TAG_TD, 1, &tag);
print_text(h, title);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag, "head-vol");
print_otag(h, TAG_TD, 1, &tag);
if (NULL != man->vol)
print_text(h, man->vol);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag, "head-rtitle");
print_otag(h, TAG_TD, 1, &tag);
print_text(h, title);
print_tagq(h, t);
free(title);
}
static void
man_root_post(MAN_ARGS)
{
struct htmlpair tag;
struct tag *t, *tt;
PAIR_CLASS_INIT(&tag, "foot");
t = print_otag(h, TAG_TABLE, 1, &tag);
tt = print_otag(h, TAG_TR, 0, NULL);
PAIR_CLASS_INIT(&tag, "foot-date");
print_otag(h, TAG_TD, 1, &tag);
assert(man->date);
print_text(h, man->date);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag, "foot-os");
print_otag(h, TAG_TD, 1, &tag);
if (man->source)
print_text(h, man->source);
print_tagq(h, t);
}
static int
man_br_pre(MAN_ARGS)
{
struct roffsu su;
struct htmlpair tag;
SCALE_VS_INIT(&su, 1);
if (MAN_sp == n->tok) {
if (NULL != (n = n->child))
if ( ! a2roffsu(n->string, &su, SCALE_VS))
SCALE_VS_INIT(&su, atoi(n->string));
} else
su.scale = 0.0;
bufinit(h);
bufcat_su(h, "height", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
/* So the div isn't empty: */
print_text(h, "\\~");
return(0);
}
static int
man_SH_pre(MAN_ARGS)
{
struct htmlpair tag;
if (MAN_BLOCK == n->type) {
mh->fl &= ~MANH_LITERAL;
PAIR_CLASS_INIT(&tag, "section");
print_otag(h, TAG_DIV, 1, &tag);
return(1);
} else if (MAN_BODY == n->type)
return(1);
print_otag(h, TAG_H1, 0, NULL);
return(1);
}
static int
man_alt_pre(MAN_ARGS)
{
const struct man_node *nn;
int i, savelit;
enum htmltag fp;
struct tag *t;
if ((savelit = mh->fl & MANH_LITERAL))
print_otag(h, TAG_BR, 0, NULL);
mh->fl &= ~MANH_LITERAL;
for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
t = NULL;
switch (n->tok) {
case MAN_BI:
fp = i % 2 ? TAG_I : TAG_B;
break;
case MAN_IB:
fp = i % 2 ? TAG_B : TAG_I;
break;
case MAN_RI:
fp = i % 2 ? TAG_I : TAG_MAX;
break;
case MAN_IR:
fp = i % 2 ? TAG_MAX : TAG_I;
break;
case MAN_BR:
fp = i % 2 ? TAG_MAX : TAG_B;
break;
case MAN_RB:
fp = i % 2 ? TAG_B : TAG_MAX;
break;
default:
abort();
/* NOTREACHED */
}
if (i)
h->flags |= HTML_NOSPACE;
if (TAG_MAX != fp)
t = print_otag(h, fp, 0, NULL);
print_man_node(man, nn, mh, h);
if (t)
print_tagq(h, t);
}
if (savelit)
mh->fl |= MANH_LITERAL;
return(0);
}
static int
man_SM_pre(MAN_ARGS)
{
print_otag(h, TAG_SMALL, 0, NULL);
if (MAN_SB == n->tok)
print_otag(h, TAG_B, 0, NULL);
return(1);
}
static int
man_SS_pre(MAN_ARGS)
{
struct htmlpair tag;
if (MAN_BLOCK == n->type) {
mh->fl &= ~MANH_LITERAL;
PAIR_CLASS_INIT(&tag, "subsection");
print_otag(h, TAG_DIV, 1, &tag);
return(1);
} else if (MAN_BODY == n->type)
return(1);
print_otag(h, TAG_H2, 0, NULL);
return(1);
}
static int
man_PP_pre(MAN_ARGS)
{
if (MAN_HEAD == n->type)
return(0);
else if (MAN_BLOCK == n->type)
print_bvspace(h, n);
return(1);
}
static int
man_IP_pre(MAN_ARGS)
{
const struct man_node *nn;
if (MAN_BODY == n->type) {
print_otag(h, TAG_DD, 0, NULL);
return(1);
} else if (MAN_HEAD != n->type) {
print_otag(h, TAG_DL, 0, NULL);
return(1);
}
/* FIXME: width specification. */
print_otag(h, TAG_DT, 0, NULL);
/* For IP, only print the first header element. */
if (MAN_IP == n->tok && n->child)
print_man_node(man, n->child, mh, h);
/* For TP, only print next-line header elements. */
if (MAN_TP == n->tok) {
nn = n->child;
while (NULL != nn && 0 == (MAN_LINE & nn->flags))
nn = nn->next;
while (NULL != nn) {
print_man_node(man, nn, mh, h);
nn = nn->next;
}
}
return(0);
}
static int
man_HP_pre(MAN_ARGS)
{
struct htmlpair tag[2];
struct roffsu su;
const struct man_node *np;
if (MAN_HEAD == n->type)
return(0);
else if (MAN_BLOCK != n->type)
return(1);
np = n->head->child;
if (NULL == np || ! a2width(np, &su))
SCALE_HS_INIT(&su, INDENT);
bufinit(h);
print_bvspace(h, n);
bufcat_su(h, "margin-left", &su);
su.scale = -su.scale;
bufcat_su(h, "text-indent", &su);
PAIR_STYLE_INIT(&tag[0], h);
PAIR_CLASS_INIT(&tag[1], "spacer");
print_otag(h, TAG_DIV, 2, tag);
return(1);
}
static int
man_OP_pre(MAN_ARGS)
{
struct tag *tt;
struct htmlpair tag;
print_text(h, "[");
h->flags |= HTML_NOSPACE;
PAIR_CLASS_INIT(&tag, "opt");
tt = print_otag(h, TAG_SPAN, 1, &tag);
if (NULL != (n = n->child)) {
print_otag(h, TAG_B, 0, NULL);
print_text(h, n->string);
}
print_stagq(h, tt);
if (NULL != n && NULL != n->next) {
print_otag(h, TAG_I, 0, NULL);
print_text(h, n->next->string);
}
print_stagq(h, tt);
h->flags |= HTML_NOSPACE;
print_text(h, "]");
return(0);
}
static int
man_B_pre(MAN_ARGS)
{
print_otag(h, TAG_B, 0, NULL);
return(1);
}
static int
man_I_pre(MAN_ARGS)
{
print_otag(h, TAG_I, 0, NULL);
return(1);
}
static int
man_literal_pre(MAN_ARGS)
{
if (MAN_fi == n->tok || MAN_EE == n->tok) {
print_otag(h, TAG_BR, 0, NULL);
mh->fl &= ~MANH_LITERAL;
} else
mh->fl |= MANH_LITERAL;
return(0);
}
static int
man_in_pre(MAN_ARGS)
{
print_otag(h, TAG_BR, 0, NULL);
return(0);
}
static int
man_ign_pre(MAN_ARGS)
{
return(0);
}
static int
man_RS_pre(MAN_ARGS)
{
struct htmlpair tag;
struct roffsu su;
if (MAN_HEAD == n->type)
return(0);
else if (MAN_BODY == n->type)
return(1);
SCALE_HS_INIT(&su, INDENT);
if (n->head->child)
a2width(n->head->child, &su);
bufinit(h);
bufcat_su(h, "margin-left", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
return(1);
}
static int
man_UR_pre(MAN_ARGS)
{
struct htmlpair tag[2];
n = n->child;
assert(MAN_HEAD == n->type);
if (n->nchild) {
assert(MAN_TEXT == n->child->type);
PAIR_CLASS_INIT(&tag[0], "link-ext");
PAIR_HREF_INIT(&tag[1], n->child->string);
print_otag(h, TAG_A, 2, tag);
}
assert(MAN_BODY == n->next->type);
if (n->next->nchild)
n = n->next;
print_man_nodelist(man, n->child, mh, h);
return(0);
}
Index: vendor/mdocml/dist/man_term.c
===================================================================
--- vendor/mdocml/dist/man_term.c (revision 276215)
+++ vendor/mdocml/dist/man_term.c (revision 276216)
@@ -1,1193 +1,1205 @@
-/* $Id: man_term.c,v 1.156 2014/11/21 01:52:53 schwarze Exp $ */
+/* $Id: man_term.c,v 1.159 2014/12/04 02:05:42 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons
* Copyright (c) 2010-2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include "mandoc.h"
#include "mandoc_aux.h"
#include "out.h"
#include "man.h"
#include "term.h"
#include "main.h"
#define MAXMARGINS 64 /* maximum number of indented scopes */
struct mtermp {
int fl;
#define MANT_LITERAL (1 << 0)
size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */
int lmargincur; /* index of current margin */
int lmarginsz; /* actual number of nested margins */
size_t offset; /* default offset to visible page */
int pardist; /* vert. space before par., unit: [v] */
};
#define DECL_ARGS struct termp *p, \
struct mtermp *mt, \
const struct man_node *n, \
const struct man_meta *meta
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 termp *, const char *);
static size_t a2height(const struct termp *, const char *);
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 *, int);
static int pre_B(DECL_ARGS);
static int pre_HP(DECL_ARGS);
static int pre_I(DECL_ARGS);
static int pre_IP(DECL_ARGS);
static int pre_OP(DECL_ARGS);
static int pre_PD(DECL_ARGS);
static int pre_PP(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_UR(DECL_ARGS);
static int pre_alternate(DECL_ARGS);
static int pre_ft(DECL_ARGS);
static int pre_ign(DECL_ARGS);
static int pre_in(DECL_ARGS);
static int pre_literal(DECL_ARGS);
static int pre_ll(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 void post_UR(DECL_ARGS);
static const struct termact termacts[MAN_MAX] = {
{ pre_sp, 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_alternate, NULL, 0 }, /* BI */
{ pre_alternate, NULL, 0 }, /* IB */
{ pre_alternate, NULL, 0 }, /* BR */
{ pre_alternate, NULL, 0 }, /* RB */
{ NULL, NULL, 0 }, /* R */
{ pre_B, NULL, 0 }, /* B */
{ pre_I, NULL, 0 }, /* I */
{ pre_alternate, NULL, 0 }, /* IR */
{ pre_alternate, NULL, 0 }, /* RI */
{ pre_ign, NULL, MAN_NOTEXT }, /* na */
{ pre_sp, NULL, MAN_NOTEXT }, /* sp */
{ pre_literal, NULL, 0 }, /* nf */
{ pre_literal, NULL, 0 }, /* fi */
{ NULL, NULL, 0 }, /* RE */
{ pre_RS, post_RS, 0 }, /* RS */
{ pre_ign, NULL, 0 }, /* DT */
- { pre_ign, NULL, 0 }, /* UC */
+ { pre_ign, NULL, MAN_NOTEXT }, /* UC */
{ pre_PD, NULL, MAN_NOTEXT }, /* PD */
{ pre_ign, NULL, 0 }, /* AT */
{ pre_in, NULL, MAN_NOTEXT }, /* in */
{ pre_ft, NULL, MAN_NOTEXT }, /* ft */
{ pre_OP, NULL, 0 }, /* OP */
{ pre_literal, NULL, 0 }, /* EX */
{ pre_literal, NULL, 0 }, /* EE */
{ pre_UR, post_UR, 0 }, /* UR */
{ NULL, NULL, 0 }, /* UE */
{ pre_ll, NULL, MAN_NOTEXT }, /* ll */
};
void
terminal_man(void *arg, const struct man *man)
{
struct termp *p;
const struct man_meta *meta;
struct man_node *n;
struct mtermp mt;
p = (struct termp *)arg;
p->overstep = 0;
p->rmargin = p->maxrmargin = p->defrmargin;
p->tabwidth = term_len(p, 5);
n = man_node(man)->child;
meta = man_meta(man);
memset(&mt, 0, sizeof(struct mtermp));
mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
mt.offset = term_len(p, p->defindent);
mt.pardist = 1;
if (p->synopsisonly) {
while (n != NULL) {
if (n->tok == MAN_SH &&
n->child->child->type == MAN_TEXT &&
!strcmp(n->child->child->string, "SYNOPSIS")) {
if (n->child->next->child != NULL)
print_man_nodelist(p, &mt,
n->child->next->child, meta);
term_newln(p);
break;
}
n = n->next;
}
} else {
if (p->defindent == 0)
p->defindent = 7;
term_begin(p, print_man_head, print_man_foot, meta);
p->flags |= TERMP_NOSPACE;
if (n != NULL)
print_man_nodelist(p, &mt, n, meta);
term_end(p);
}
}
static size_t
a2height(const struct termp *p, const char *cp)
{
struct roffsu su;
if ( ! a2roffsu(cp, &su, SCALE_VS))
SCALE_VS_INIT(&su, atoi(cp));
return(term_vspan(p, &su));
}
static int
a2width(const struct termp *p, const char *cp)
{
struct roffsu su;
- if ( ! a2roffsu(cp, &su, SCALE_BU))
+ if ( ! a2roffsu(cp, &su, SCALE_EN))
return(-1);
return((int)term_hspan(p, &su));
}
/*
* Printing leading vertical space before a block.
* This is used for the paragraph macros.
* The rules are pretty simple, since there's very little nesting going
* on here. Basically, if we're the first within another block (SS/SH),
* then don't emit vertical space. If we are (RS), then do. If not the
* first, print it.
*/
static void
print_bvspace(struct termp *p, const struct man_node *n, int pardist)
{
int i;
term_newln(p);
if (n->body && n->body->child)
if (MAN_TBL == n->body->child->type)
return;
if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
if (NULL == n->prev)
return;
for (i = 0; i < pardist; i++)
term_vspace(p);
}
static int
pre_ign(DECL_ARGS)
{
return(0);
}
static int
pre_ll(DECL_ARGS)
{
term_setwidth(p, n->nchild ? n->child->string : NULL);
return(0);
}
static int
pre_I(DECL_ARGS)
{
term_fontrepl(p, TERMFONT_UNDER);
return(1);
}
static int
pre_literal(DECL_ARGS)
{
term_newln(p);
if (MAN_nf == n->tok || MAN_EX == n->tok)
mt->fl |= MANT_LITERAL;
else
mt->fl &= ~MANT_LITERAL;
/*
* Unlike .IP and .TP, .HP does not have a HEAD.
* So in case a second call to term_flushln() is needed,
* indentation has to be set up explicitly.
*/
if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
p->offset = p->rmargin;
p->rmargin = p->maxrmargin;
p->trailspace = 0;
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
p->flags |= TERMP_NOSPACE;
}
return(0);
}
static int
pre_PD(DECL_ARGS)
{
n = n->child;
if (0 == n) {
mt->pardist = 1;
return(0);
}
assert(MAN_TEXT == n->type);
mt->pardist = atoi(n->string);
return(0);
}
static int
pre_alternate(DECL_ARGS)
{
enum termfont font[2];
const struct man_node *nn;
int savelit, i;
switch (n->tok) {
case MAN_RB:
font[0] = TERMFONT_NONE;
font[1] = TERMFONT_BOLD;
break;
case MAN_RI:
font[0] = TERMFONT_NONE;
font[1] = TERMFONT_UNDER;
break;
case MAN_BR:
font[0] = TERMFONT_BOLD;
font[1] = TERMFONT_NONE;
break;
case MAN_BI:
font[0] = TERMFONT_BOLD;
font[1] = TERMFONT_UNDER;
break;
case MAN_IR:
font[0] = TERMFONT_UNDER;
font[1] = TERMFONT_NONE;
break;
case MAN_IB:
font[0] = TERMFONT_UNDER;
font[1] = TERMFONT_BOLD;
break;
default:
abort();
}
savelit = MANT_LITERAL & mt->fl;
mt->fl &= ~MANT_LITERAL;
for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
term_fontrepl(p, font[i]);
if (savelit && NULL == nn->next)
mt->fl |= MANT_LITERAL;
print_man_node(p, mt, nn, meta);
if (nn->next)
p->flags |= TERMP_NOSPACE;
}
return(0);
}
static int
pre_B(DECL_ARGS)
{
term_fontrepl(p, TERMFONT_BOLD);
return(1);
}
static int
pre_OP(DECL_ARGS)
{
term_word(p, "[");
p->flags |= TERMP_NOSPACE;
if (NULL != (n = n->child)) {
term_fontrepl(p, TERMFONT_BOLD);
term_word(p, n->string);
}
if (NULL != n && NULL != n->next) {
term_fontrepl(p, TERMFONT_UNDER);
term_word(p, n->next->string);
}
term_fontrepl(p, TERMFONT_NONE);
p->flags |= TERMP_NOSPACE;
term_word(p, "]");
return(0);
}
static int
pre_ft(DECL_ARGS)
{
const char *cp;
if (NULL == n->child) {
term_fontlast(p);
return(0);
}
cp = n->child->string;
switch (*cp) {
case '4':
/* FALLTHROUGH */
case '3':
/* FALLTHROUGH */
case 'B':
term_fontrepl(p, TERMFONT_BOLD);
break;
case '2':
/* FALLTHROUGH */
case 'I':
term_fontrepl(p, TERMFONT_UNDER);
break;
case 'P':
term_fontlast(p);
break;
case '1':
/* FALLTHROUGH */
case 'C':
/* FALLTHROUGH */
case 'R':
term_fontrepl(p, TERMFONT_NONE);
break;
default:
break;
}
return(0);
}
static int
pre_in(DECL_ARGS)
{
int len, less;
size_t v;
const char *cp;
term_newln(p);
if (NULL == n->child) {
p->offset = mt->offset;
return(0);
}
cp = n->child->string;
less = 0;
if ('-' == *cp)
less = -1;
else if ('+' == *cp)
less = 1;
else
cp--;
if ((len = a2width(p, ++cp)) < 0)
return(0);
v = (size_t)len;
if (less < 0)
p->offset -= p->offset > v ? v : p->offset;
else if (less > 0)
p->offset += v;
else
p->offset = v;
return(0);
}
static int
pre_sp(DECL_ARGS)
{
char *s;
size_t i, len;
int neg;
if ((NULL == n->prev && n->parent)) {
switch (n->parent->tok) {
case MAN_SH:
/* FALLTHROUGH */
case MAN_SS:
/* FALLTHROUGH */
case MAN_PP:
/* FALLTHROUGH */
case MAN_LP:
/* FALLTHROUGH */
case MAN_P:
/* FALLTHROUGH */
return(0);
default:
break;
}
}
neg = 0;
switch (n->tok) {
case MAN_br:
len = 0;
break;
default:
if (NULL == n->child) {
len = 1;
break;
}
s = n->child->string;
if ('-' == *s) {
neg = 1;
s++;
}
len = a2height(p, s);
break;
}
if (0 == len)
term_newln(p);
else if (neg)
p->skipvsp += len;
else
for (i = 0; i < len; i++)
term_vspace(p);
return(0);
}
static int
pre_HP(DECL_ARGS)
{
size_t len, one;
int ival;
const struct man_node *nn;
switch (n->type) {
case MAN_BLOCK:
print_bvspace(p, n, mt->pardist);
return(1);
case MAN_BODY:
break;
default:
return(0);
}
if ( ! (MANT_LITERAL & mt->fl)) {
p->flags |= TERMP_NOBREAK | TERMP_BRIND;
p->trailspace = 2;
}
len = mt->lmargin[mt->lmargincur];
ival = -1;
/* Calculate offset. */
if (NULL != (nn = n->parent->head->child))
if ((ival = a2width(p, nn->string)) >= 0)
len = (size_t)ival;
one = term_len(p, 1);
if (len < one)
len = one;
p->offset = mt->offset;
p->rmargin = mt->offset + len;
if (ival >= 0)
mt->lmargin[mt->lmargincur] = (size_t)ival;
return(1);
}
static void
post_HP(DECL_ARGS)
{
switch (n->type) {
case MAN_BODY:
term_newln(p);
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
p->trailspace = 0;
p->offset = mt->offset;
p->rmargin = p->maxrmargin;
break;
default:
break;
}
}
static int
pre_PP(DECL_ARGS)
{
switch (n->type) {
case MAN_BLOCK:
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
print_bvspace(p, n, mt->pardist);
break;
default:
p->offset = mt->offset;
break;
}
return(MAN_HEAD != n->type);
}
static int
pre_IP(DECL_ARGS)
{
const struct man_node *nn;
size_t len;
int savelit, ival;
switch (n->type) {
case MAN_BODY:
p->flags |= TERMP_NOSPACE;
break;
case MAN_HEAD:
p->flags |= TERMP_NOBREAK;
p->trailspace = 1;
break;
case MAN_BLOCK:
print_bvspace(p, n, mt->pardist);
/* FALLTHROUGH */
default:
return(1);
}
len = mt->lmargin[mt->lmargincur];
ival = -1;
/* Calculate the offset from the optional second argument. */
if (NULL != (nn = n->parent->head->child))
if (NULL != (nn = nn->next))
if ((ival = a2width(p, nn->string)) >= 0)
len = (size_t)ival;
switch (n->type) {
case MAN_HEAD:
/* Handle zero-width lengths. */
if (0 == len)
len = term_len(p, 1);
p->offset = mt->offset;
p->rmargin = mt->offset + len;
if (ival < 0)
break;
/* Set the saved left-margin. */
mt->lmargin[mt->lmargincur] = (size_t)ival;
savelit = MANT_LITERAL & mt->fl;
mt->fl &= ~MANT_LITERAL;
if (n->child)
print_man_node(p, mt, n->child, meta);
if (savelit)
mt->fl |= MANT_LITERAL;
return(0);
case MAN_BODY:
p->offset = mt->offset + len;
p->rmargin = p->maxrmargin;
break;
default:
break;
}
return(1);
}
static void
post_IP(DECL_ARGS)
{
switch (n->type) {
case MAN_HEAD:
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
p->trailspace = 0;
p->rmargin = p->maxrmargin;
break;
case MAN_BODY:
term_newln(p);
p->offset = mt->offset;
break;
default:
break;
}
}
static int
pre_TP(DECL_ARGS)
{
const struct man_node *nn;
size_t len;
int savelit, ival;
switch (n->type) {
case MAN_HEAD:
p->flags |= TERMP_NOBREAK;
p->trailspace = 1;
break;
case MAN_BODY:
p->flags |= TERMP_NOSPACE;
break;
case MAN_BLOCK:
print_bvspace(p, n, mt->pardist);
/* FALLTHROUGH */
default:
return(1);
}
len = (size_t)mt->lmargin[mt->lmargincur];
ival = -1;
/* Calculate offset. */
if (NULL != (nn = n->parent->head->child))
if (nn->string && 0 == (MAN_LINE & nn->flags))
if ((ival = a2width(p, nn->string)) >= 0)
len = (size_t)ival;
switch (n->type) {
case MAN_HEAD:
/* Handle zero-length properly. */
if (0 == len)
len = term_len(p, 1);
p->offset = mt->offset;
p->rmargin = mt->offset + len;
savelit = MANT_LITERAL & mt->fl;
mt->fl &= ~MANT_LITERAL;
/* Don't print same-line elements. */
nn = n->child;
while (NULL != nn && 0 == (MAN_LINE & nn->flags))
nn = nn->next;
while (NULL != nn) {
print_man_node(p, mt, nn, meta);
nn = nn->next;
}
if (savelit)
mt->fl |= MANT_LITERAL;
if (ival >= 0)
mt->lmargin[mt->lmargincur] = (size_t)ival;
return(0);
case MAN_BODY:
p->offset = mt->offset + len;
p->rmargin = p->maxrmargin;
p->trailspace = 0;
p->flags &= ~TERMP_NOBREAK;
break;
default:
break;
}
return(1);
}
static void
post_TP(DECL_ARGS)
{
switch (n->type) {
case MAN_HEAD:
term_flushln(p);
break;
case MAN_BODY:
term_newln(p);
p->offset = mt->offset;
break;
default:
break;
}
}
static int
pre_SS(DECL_ARGS)
{
int i;
switch (n->type) {
case MAN_BLOCK:
mt->fl &= ~MANT_LITERAL;
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
mt->offset = term_len(p, p->defindent);
- /* 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)
+
+ /*
+ * No vertical space before the first subsection
+ * and after an empty subsection.
+ */
+
+ do {
+ n = n->prev;
+ } while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT);
+ if (n == NULL || (n->tok == MAN_SS && n->body->child == NULL))
break;
+
for (i = 0; i < mt->pardist; i++)
term_vspace(p);
break;
case MAN_HEAD:
term_fontrepl(p, TERMFONT_BOLD);
p->offset = term_len(p, 3);
break;
case MAN_BODY:
p->offset = mt->offset;
break;
default:
break;
}
return(1);
}
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;
}
}
static int
pre_SH(DECL_ARGS)
{
int i;
switch (n->type) {
case MAN_BLOCK:
mt->fl &= ~MANT_LITERAL;
mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
mt->offset = term_len(p, p->defindent);
- /* 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)
+
+ /*
+ * No vertical space before the first section
+ * and after an empty section.
+ */
+
+ do {
+ n = n->prev;
+ } while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT);
+ if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL))
break;
+
for (i = 0; i < mt->pardist; i++)
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);
}
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;
}
}
static int
pre_RS(DECL_ARGS)
{
int ival;
size_t sz;
switch (n->type) {
case MAN_BLOCK:
term_newln(p);
return(1);
case MAN_HEAD:
return(0);
default:
break;
}
sz = term_len(p, p->defindent);
if (NULL != (n = n->parent->head->child))
if ((ival = a2width(p, n->string)) >= 0)
sz = (size_t)ival;
mt->offset += sz;
p->offset = mt->offset;
p->rmargin = p->maxrmargin;
if (++mt->lmarginsz < MAXMARGINS)
mt->lmargincur = mt->lmarginsz;
mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
return(1);
}
static void
post_RS(DECL_ARGS)
{
int ival;
size_t sz;
switch (n->type) {
case MAN_BLOCK:
return;
case MAN_HEAD:
return;
default:
term_newln(p);
break;
}
sz = term_len(p, p->defindent);
if (NULL != (n = n->parent->head->child))
if ((ival = a2width(p, n->string)) >= 0)
sz = (size_t)ival;
mt->offset = mt->offset < sz ? 0 : mt->offset - sz;
p->offset = mt->offset;
if (--mt->lmarginsz < MAXMARGINS)
mt->lmargincur = mt->lmarginsz;
}
static int
pre_UR(DECL_ARGS)
{
return (MAN_HEAD != n->type);
}
static void
post_UR(DECL_ARGS)
{
if (MAN_BLOCK != n->type)
return;
term_word(p, "<");
p->flags |= TERMP_NOSPACE;
if (NULL != n->child->child)
print_man_node(p, mt, n->child->child, meta);
p->flags |= TERMP_NOSPACE;
term_word(p, ">");
}
static void
print_man_node(DECL_ARGS)
{
size_t rm, rmax;
int c;
switch (n->type) {
case MAN_TEXT:
/*
* If we have a blank line, output a vertical space.
* If we have a space as the first character, break
* before printing the line's data.
*/
if ('\0' == *n->string) {
term_vspace(p);
return;
} else if (' ' == *n->string && MAN_LINE & n->flags)
term_newln(p);
term_word(p, n->string);
goto out;
case MAN_EQN:
if ( ! (n->flags & MAN_LINE))
p->flags |= TERMP_NOSPACE;
term_eqn(p, n->eqn);
if (n->next != NULL && ! (n->next->flags & MAN_LINE))
p->flags |= TERMP_NOSPACE;
return;
case MAN_TBL:
/*
* Tables are preceded by a newline. Then process a
* table line, which will cause line termination,
*/
if (TBL_SPAN_FIRST & n->span->flags)
term_newln(p);
term_tbl(p, n->span);
return;
default:
break;
}
if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
term_fontrepl(p, TERMFONT_NONE);
c = 1;
if (termacts[n->tok].pre)
c = (*termacts[n->tok].pre)(p, mt, n, meta);
if (c && n->child)
print_man_nodelist(p, mt, n->child, meta);
if (termacts[n->tok].post)
(*termacts[n->tok].post)(p, mt, n, meta);
if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
term_fontrepl(p, TERMFONT_NONE);
out:
/*
* If we're in a literal context, make sure that words
* together on the same line stay together. This is a
* POST-printing call, so we check the NEXT word. Since
* -man doesn't have nested macros, we don't need to be
* more specific than this.
*/
- if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
- (NULL == n->next || MAN_LINE & n->next->flags)) {
+ if (mt->fl & MANT_LITERAL &&
+ ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
+ (n->next == NULL || n->next->flags & MAN_LINE)) {
rm = p->rmargin;
rmax = p->maxrmargin;
p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
p->flags |= TERMP_NOSPACE;
- if (NULL != n->string && '\0' != *n->string)
+ if (n->string != NULL && *n->string != '\0')
term_flushln(p);
else
term_newln(p);
if (rm < rmax && n->parent->tok == MAN_HP) {
p->offset = rm;
p->rmargin = rmax;
} else
p->rmargin = rm;
p->maxrmargin = rmax;
}
if (MAN_EOS & n->flags)
p->flags |= TERMP_SENTENCE;
}
static void
print_man_nodelist(DECL_ARGS)
{
print_man_node(p, mt, n, meta);
if ( ! n->next)
return;
print_man_nodelist(p, mt, n->next, meta);
}
static void
print_man_foot(struct termp *p, const void *arg)
{
const struct man_meta *meta;
char *title;
size_t datelen, titlen;
meta = (const struct man_meta *)arg;
assert(meta->title);
assert(meta->msec);
assert(meta->date);
term_fontrepl(p, TERMFONT_NONE);
if (meta->hasbody)
term_vspace(p);
/*
* Temporary, undocumented option to imitate mdoc(7) output.
* In the bottom right corner, use the source instead of
* the title.
*/
if ( ! p->mdocstyle) {
if (meta->hasbody) {
term_vspace(p);
term_vspace(p);
}
mandoc_asprintf(&title, "%s(%s)",
meta->title, meta->msec);
} else if (meta->source) {
title = mandoc_strdup(meta->source);
} else {
title = mandoc_strdup("");
}
datelen = term_strlen(p, meta->date);
/* Bottom left corner: manual source. */
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
p->trailspace = 1;
p->offset = 0;
p->rmargin = p->maxrmargin > datelen ?
(p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
if (meta->source)
term_word(p, meta->source);
term_flushln(p);
/* At the bottom in the middle: manual date. */
p->offset = p->rmargin;
titlen = term_strlen(p, title);
p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0;
p->flags |= TERMP_NOSPACE;
term_word(p, meta->date);
term_flushln(p);
/* Bottom right corner: manual title and section. */
p->flags &= ~TERMP_NOBREAK;
p->flags |= TERMP_NOSPACE;
p->trailspace = 0;
p->offset = p->rmargin;
p->rmargin = p->maxrmargin;
term_word(p, title);
term_flushln(p);
free(title);
}
static void
print_man_head(struct termp *p, const void *arg)
{
const struct man_meta *meta;
const char *volume;
char *title;
size_t vollen, titlen;
meta = (const struct man_meta *)arg;
assert(meta->title);
assert(meta->msec);
volume = NULL == meta->vol ? "" : meta->vol;
vollen = term_strlen(p, volume);
/* Top left corner: manual title and section. */
mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
titlen = term_strlen(p, title);
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
p->trailspace = 1;
p->offset = 0;
p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
(p->maxrmargin - vollen + term_len(p, 1)) / 2 :
vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
term_word(p, title);
term_flushln(p);
/* At the top in the middle: manual volume. */
p->flags |= TERMP_NOSPACE;
p->offset = p->rmargin;
p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
p->maxrmargin - titlen : p->maxrmargin;
term_word(p, volume);
term_flushln(p);
/* Top right corner: title and section, again. */
p->flags &= ~TERMP_NOBREAK;
p->trailspace = 0;
if (p->rmargin + titlen <= p->maxrmargin) {
p->flags |= TERMP_NOSPACE;
p->offset = p->rmargin;
p->rmargin = p->maxrmargin;
term_word(p, title);
term_flushln(p);
}
p->flags &= ~TERMP_NOSPACE;
p->offset = 0;
p->rmargin = p->maxrmargin;
/*
* Groff prints three blank lines before the content.
* Do the same, except in the temporary, undocumented
* mode imitating mdoc(7) output.
*/
term_vspace(p);
if ( ! p->mdocstyle) {
term_vspace(p);
term_vspace(p);
}
free(title);
}
Index: vendor/mdocml/dist/mandoc.1
===================================================================
--- vendor/mdocml/dist/mandoc.1 (revision 276215)
+++ vendor/mdocml/dist/mandoc.1 (revision 276216)
@@ -1,1640 +1,1657 @@
-.\" $Id: mandoc.1,v 1.125 2014/11/28 18:09:01 schwarze Exp $
+.\" $Id: mandoc.1,v 1.128 2014/12/02 11:31:51 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons
.\" Copyright (c) 2012, 2014 Ingo Schwarze
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: November 28 2014 $
+.Dd $Mdocdate: December 2 2014 $
.Dt MANDOC 1
.Os
.Sh NAME
.Nm mandoc
.Nd format and display UNIX manuals
.Sh SYNOPSIS
.Nm mandoc
.Op Fl acfhklV
.Sm off
.Op Fl I Cm os Li = Ar name
.Sm on
.Op Fl K Ns Ar encoding
.Op Fl m Ns Ar format
.Op Fl O Ns Ar option
.Op Fl T Ns Ar output
.Op Fl W Ns Ar level
.Op Ar
.Sh DESCRIPTION
The
.Nm
utility formats
.Ux
manual pages for display.
.Pp
By default,
.Nm
reads
.Xr mdoc 7
or
.Xr man 7
text from stdin, implying
.Fl m Ns Cm andoc ,
and produces
-.Fl T Ns Cm ascii
+.Fl T Ns Cm locale
output.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl a
If the standard output is a terminal device and
.Fl c
is not specified, use
.Xr more 1
to paginate the output, just like
.Xr man 1
would.
.It Fl c
Copy the formatted manual pages to the standard output without using
.Xr more 1
to paginate them.
This is the default.
It can be specified to override
.Fl a .
.It Fl f
A synonym for
.Xr whatis 1 .
This overrides any earlier
.Fl k
and
.Fl l
options.
.Sm off
.It Fl I Cm os Li = Ar name
.Sm on
Override the default operating system
.Ar name
for the
.Xr mdoc 7
.Sq \&Os
macro.
.It Fl h
Display only the SYNOPSIS lines.
Implies
.Fl c .
.It Fl K Ns Ar encoding
Specify the input encoding.
The supported
.Ar encoding
arguments are
.Cm us-ascii ,
.Cm iso-8859-1 ,
and
.Cm utf-8 .
If not specified, autodetection uses the first match:
.Bl -tag -width iso-8859-1
.It Cm utf-8
if the first three bytes of the input file
are the UTF-8 byte order mark (BOM, 0xefbbbf)
.It Ar encoding
if the first or second line of the input file matches the
.Sy emacs
mode line format
.Pp
.D1 .\e" -*- Oo ...; Oc coding: Ar encoding ; No -*-
.It Cm utf-8
if the first non-ASCII byte in the file introduces a valid UTF-8 sequence
.It Cm iso-8859-1
otherwise
.El
.It Fl k
A synonym for
.Xr apropos 1 .
This overrides any earlier
.Fl f
and
.Fl l
options.
.It Fl l
A synonym for
.Fl a .
Also reverts any earlier
.Fl f
and
.Fl k
options.
.It Fl m Ns Ar format
Input format.
See
.Sx Input Formats
for available formats.
Defaults to
.Fl m Ns Cm andoc .
.It Fl O Ns Ar option
Comma-separated output options.
.It Fl T Ns Ar output
Output format.
See
.Sx Output Formats
for available formats.
Defaults to
-.Fl T Ns Cm ascii .
+.Fl T Ns Cm locale .
.It Fl V
Print version and exit.
.It Fl W Ns Ar level
Specify the minimum message
.Ar level
to be reported on the standard error output and to affect the exit status.
The
.Ar level
can be
.Cm warning ,
.Cm error ,
or
.Cm fatal .
The default is
.Fl W Ns Cm fatal ;
.Fl W Ns Cm all
is an alias for
.Fl W Ns Cm warning .
See
.Sx EXIT STATUS
and
.Sx DIAGNOSTICS
for details.
.Pp
The special option
.Fl W Ns Cm stop
tells
.Nm
to exit after parsing a file that causes warnings or errors of at least
the requested level.
No formatted output will be produced from that file.
If both a
.Ar level
and
.Cm stop
are requested, they can be joined with a comma, for example
.Fl W Ns Cm error , Ns Cm stop .
.It Ar file
Read input from zero or more files.
If unspecified, reads from stdin.
If multiple files are specified,
.Nm
will halt with the first failed parse.
.El
.Pp
In
.Fl f
and
.Fl k
mode,
.Nm
also supports the options
.Fl CMmOSsw
described in the
.Xr apropos 1
manual.
.Ss Input Formats
The
.Nm
utility accepts
.Xr mdoc 7
and
.Xr man 7
input with
.Fl m Ns Cm doc
and
.Fl m Ns Cm an ,
respectively.
The
.Xr mdoc 7
format is
.Em strongly
recommended;
.Xr man 7
should only be used for legacy manuals.
.Pp
A third option,
.Fl m Ns Cm andoc ,
which is also the default, determines encoding on-the-fly: if the first
non-comment macro is
.Sq \&Dd
or
.Sq \&Dt ,
the
.Xr mdoc 7
parser is used; otherwise, the
.Xr man 7
parser is used.
.Pp
If multiple
files are specified with
.Fl m Ns Cm andoc ,
each has its file-type determined this way.
If multiple files are
specified and
.Fl m Ns Cm doc
or
.Fl m Ns Cm an
is specified, then this format is used exclusively.
.Ss Output Formats
The
.Nm
utility accepts the following
.Fl T
arguments, which correspond to output modes:
.Bl -tag -width "-Tlocale"
.It Fl T Ns Cm ascii
Produce 7-bit ASCII output.
-This is the default.
See
.Sx ASCII Output .
.It Fl T Ns Cm html
Produce HTML5, CSS1, and MathML output.
See
.Sx HTML Output .
.It Fl T Ns Cm lint
Parse only: produce no output.
Implies
.Fl W Ns Cm warning .
.It Fl T Ns Cm locale
Encode output using the current locale.
+This is the default.
See
.Sx Locale Output .
.It Fl T Ns Cm man
Produce
.Xr man 7
format output.
See
.Sx Man Output .
.It Fl T Ns Cm pdf
Produce PDF output.
See
.Sx PDF Output .
.It Fl T Ns Cm ps
Produce PostScript output.
See
.Sx PostScript Output .
.It Fl T Ns Cm tree
Produce an indented parse tree.
.It Fl T Ns Cm utf8
Encode output in the UTF\-8 multi-byte format.
See
.Sx UTF\-8 Output .
.It Fl T Ns Cm xhtml
This is a synonym for
.Fl T Ns Cm html .
.El
.Pp
If multiple input files are specified, these will be processed by the
corresponding filter in-order.
.Ss ASCII Output
Output produced by
-.Fl T Ns Cm ascii ,
-which is the default, is rendered in standard 7-bit ASCII documented in
+.Fl T Ns Cm ascii
+is rendered in standard 7-bit ASCII documented in
.Xr ascii 7 .
.Pp
Font styles are applied by using back-spaced encoding such that an
underlined character
.Sq c
is rendered as
.Sq _ Ns \e[bs] Ns c ,
where
.Sq \e[bs]
is the back-space character number 8.
Emboldened characters are rendered as
.Sq c Ns \e[bs] Ns c .
.Pp
The special characters documented in
.Xr mandoc_char 7
are rendered best-effort in an ASCII equivalent.
If no equivalent is found,
.Sq \&?
is used instead.
.Pp
Output width is limited to 78 visible columns unless literal input lines
exceed this limit.
.Pp
The following
.Fl O
arguments are accepted:
.Bl -tag -width Ds
.It Cm indent Ns = Ns Ar indent
The left margin for normal text is set to
.Ar indent
blank characters instead of the default of five for
.Xr mdoc 7
and seven for
.Xr man 7 .
Increasing this is not recommended; it may result in degraded formatting,
for example overfull lines or ugly line breaks.
.It Cm width Ns = Ns Ar width
The output width is set to
.Ar width ,
which will normalise to \(>=60.
.El
.Ss HTML Output
Output produced by
.Fl T Ns Cm html
conforms to HTML5 using optional self-closing tags.
Default styles use only CSS1.
Equations rendered from
.Xr eqn 7
blocks use MathML.
.Pp
The
.Pa example.style.css
file documents style-sheet classes available for customising output.
If a style-sheet is not specified with
.Fl O Ns Ar style ,
.Fl T Ns Cm html
defaults to simple output (via an embedded style-sheet)
readable in any graphical or text-based web
browser.
.Pp
Special characters are rendered in decimal-encoded UTF\-8.
.Pp
The following
.Fl O
arguments are accepted:
.Bl -tag -width Ds
.It Cm fragment
Omit the declaration and the , , and
elements and only emit the subtree below the element.
The
.Cm style
argument will be ignored.
This is useful when embedding manual content within existing documents.
.It Cm includes Ns = Ns Ar fmt
The string
.Ar fmt ,
for example,
.Ar ../src/%I.html ,
is used as a template for linked header files (usually via the
.Sq \&In
macro).
Instances of
.Sq \&%I
are replaced with the include filename.
The default is not to present a
hyperlink.
.It Cm man Ns = Ns Ar fmt
The string
.Ar fmt ,
for example,
.Ar ../html%S/%N.%S.html ,
is used as a template for linked manuals (usually via the
.Sq \&Xr
macro).
Instances of
.Sq \&%N
and
.Sq %S
are replaced with the linked manual's name and section, respectively.
If no section is included, section 1 is assumed.
The default is not to
present a hyperlink.
.It Cm style Ns = Ns Ar style.css
The file
.Ar style.css
is used for an external style-sheet.
This must be a valid absolute or
relative URI.
.El
.Ss Locale Output
Locale-depending output encoding is triggered with
.Fl T Ns Cm locale .
+This is the default.
+.Pp
This option is not available on all systems: systems without locale
support, or those whose internal representation is not natively UCS-4,
will fall back to
.Fl T Ns Cm ascii .
See
.Sx ASCII Output
for font style specification and available command-line arguments.
.Ss Man Output
Translate input format into
.Xr man 7
output format.
This is useful for distributing manual sources to legacy systems
lacking
.Xr mdoc 7
formatters.
.Pp
If
.Xr mdoc 7
is passed as input, it is translated into
.Xr man 7 .
If the input format is
.Xr man 7 ,
the input is copied to the output, expanding any
.Xr roff 7
.Sq so
requests.
The parser is also run, and as usual, the
.Fl W
level controls which
.Sx DIAGNOSTICS
are displayed before copying the input to the output.
.Ss PDF Output
PDF-1.1 output may be generated by
.Fl T Ns Cm pdf .
See
.Sx PostScript Output
for
.Fl O
arguments and defaults.
.Ss PostScript Output
PostScript
.Qq Adobe-3.0
Level-2 pages may be generated by
.Fl T Ns Cm ps .
Output pages default to letter sized and are rendered in the Times font
family, 11-point.
Margins are calculated as 1/9 the page length and width.
Line-height is 1.4m.
.Pp
Special characters are rendered as in
.Sx ASCII Output .
.Pp
The following
.Fl O
arguments are accepted:
.Bl -tag -width Ds
.It Cm paper Ns = Ns Ar name
The paper size
.Ar name
may be one of
.Ar a3 ,
.Ar a4 ,
.Ar a5 ,
.Ar legal ,
or
.Ar letter .
You may also manually specify dimensions as
.Ar NNxNN ,
width by height in millimetres.
If an unknown value is encountered,
.Ar letter
is used.
.El
.Ss UTF\-8 Output
Use
.Fl T Ns Cm utf8
to force a UTF\-8 locale.
See
.Sx Locale Output
for details and options.
.Sh ENVIRONMENT
.Bl -tag -width MANPAGER
.It Ev MANPAGER
Any non-empty value of the environment variable
.Ev MANPAGER
will be used instead of the standard pagination program,
.Xr more 1 .
.It Ev PAGER
Specifies the pagination program to use when
.Ev MANPAGER
is not defined.
If neither PAGER nor MANPAGER is defined,
.Pa /usr/bin/more Fl s
will be used.
.El
.Sh EXIT STATUS
The
.Nm
utility exits with one of the following values, controlled by the message
.Ar level
associated with the
.Fl W
option:
.Pp
.Bl -tag -width Ds -compact
.It 0
No warnings or errors occurred, or those that did were ignored because
they were lower than the requested
.Ar level .
.It 2
At least one warning occurred, but no error, and
.Fl W Ns Cm warning
was specified.
.It 3
At least one parsing error occurred, but no fatal error, and
.Fl W Ns Cm error
or
.Fl W Ns Cm warning
was specified.
.It 4
A fatal parsing error occurred.
.It 5
Invalid command line arguments were specified.
No input files have been read.
.It 6
An operating system error occurred, for example memory exhaustion or an
error accessing input files.
Such errors cause
.Nm
to exit at once, possibly in the middle of parsing or formatting a file.
.El
.Pp
Note that selecting
.Fl T Ns Cm lint
output mode implies
.Fl W Ns Cm warning .
.Sh EXAMPLES
To page manuals to the terminal:
.Pp
.Dl $ mandoc \-Wall,stop mandoc.1 2\*(Gt&1 | less
.Dl $ mandoc mandoc.1 mdoc.3 mdoc.7 | less
.Pp
To produce HTML manuals with
.Ar style.css
as the style-sheet:
.Pp
.Dl $ mandoc \-Thtml -Ostyle=style.css mdoc.7 \*(Gt mdoc.7.html
.Pp
To check over a large set of manuals:
.Pp
.Dl $ mandoc \-Tlint `find /usr/src -name \e*\e.[1-9]`
.Pp
To produce a series of PostScript manuals for A4 paper:
.Pp
.Dl $ mandoc \-Tps \-Opaper=a4 mdoc.7 man.7 \*(Gt manuals.ps
.Pp
Convert a modern
.Xr mdoc 7
manual to the older
.Xr man 7
format, for use on systems lacking an
.Xr mdoc 7
parser:
.Pp
.Dl $ mandoc \-Tman foo.mdoc \*(Gt foo.man
.Sh DIAGNOSTICS
Messages displayed by
.Nm
follow this format:
.Pp
.D1 Nm Ns : Ar file : Ns Ar line : Ns Ar column : level : message : macro args
.Pp
Line and column numbers start at 1.
Both are omitted for messages referring to an input file as a whole.
Macro names and arguments are omitted where meaningless.
Fatal messages about invalid command line arguments
or operating system errors, for example when memory is exhausted,
may also omit the
.Ar file
and
.Ar level
fields.
.Pp
Message levels have the following meanings:
.Bl -tag -width "warning"
.It Cm syserr
Opening or reading an input file failed, so the parser cannot
even be started and no output is produced from that input file.
.It Cm fatal
The parser is unable to parse a given input file at all.
No formatted output is produced from that input file.
.It Cm error
An input file contains syntax that cannot be safely interpreted,
either because it is invalid or because
.Nm
does not implement it yet.
By discarding part of the input or inserting missing tokens,
the parser is able to continue, and the error does not prevent
generation of formatted output, but typically, preparing that
output involves information loss, broken document structure
or unintended formatting.
.It Cm warning
An input file uses obsolete, discouraged or non-portable syntax.
All the same, the meaning of the input is unambiguous and a correct
rendering can be produced.
Documents causing warnings may render poorly when using other
formatting tools instead of
.Nm .
.El
.Pp
Messages of the
.Cm warning
and
.Cm error
levels are hidden unless their level, or a lower level, is requested using a
.Fl W
option or
.Fl T Ns Cm lint
output mode.
.Ss Warnings related to the document prologue
.Bl -ohang
.It Sy "missing manual title, using UNTITLED"
.Pq mdoc
A
.Ic \&Dt
macro has no arguments, or there is no
.Ic \&Dt
macro before the first non-prologue macro.
.It Sy "missing manual title, using \(dq\(dq"
.Pq man
There is no
.Ic \&TH
macro, or it has no arguments.
.It Sy "lower case character in document title"
.Pq mdoc , man
The title is still used as given in the
.Ic \&Dt
or
.Ic \&TH
macro.
.It Sy "missing manual section, using \(dq\(dq"
.Pq mdoc , man
A
.Ic \&Dt
or
.Ic \&TH
macro lacks the mandatory section argument.
.It Sy "unknown manual section"
.Pq mdoc
The section number in a
.Ic \&Dt
line is invalid, but still used.
.It Sy "missing date, using today's date"
.Pq mdoc, man
The document was parsed as
.Xr mdoc 7
and it has no
.Ic \&Dd
macro, or the
.Ic \&Dd
macro has no arguments or only empty arguments;
or the document was parsed as
.Xr man 7
and it has no
.Ic \&TH
macro, or the
.Ic \&TH
macro has less than three arguments or its third argument is empty.
.It Sy "cannot parse date, using it verbatim"
.Pq mdoc , man
The date given in a
.Ic \&Dd
or
.Ic \&TH
macro does not follow the conventional format.
.It Sy "missing Os macro, using \(dq\(dq"
.Pq mdoc
The default or current system is not shown in this case.
.It Sy "duplicate prologue macro"
.Pq mdoc
One of the prologue macros occurs more than once.
The last instance overrides all previous ones.
.It Sy "late prologue macro"
.Pq mdoc
A
.Ic \&Dd
or
.Ic \&Os
macro occurs after some non-prologue macro, but still takes effect.
.It Sy "skipping late title macro"
.Pq mdoc
The
.Ic \&Dt
macro can only occur before the first non-prologue macro
because traditional formatters write the page header
before parsing the document body.
Even though this technical restriction does not apply to
.Nm ,
traditional semantics is preserved.
The late macro is discarded including its arguments.
.It Sy "prologue macros out of order"
.Pq mdoc
The prologue macros are not given in the conventional order
.Ic \&Dd ,
.Ic \&Dt ,
.Ic \&Os .
All three macros are used even when given in another order.
.El
.Ss Warnings regarding document structure
.Bl -ohang
.It Sy ".so is fragile, better use ln(1)"
.Pq roff
Including files only works when the parser program runs with the correct
current working directory.
.It Sy "no document body"
.Pq mdoc , man
The document body contains neither text nor macros.
An empty document is shown, consisting only of a header and a footer line.
.It Sy "content before first section header"
.Pq mdoc , man
Some macros or text precede the first
.Ic \&Sh
or
.Ic \&SH
section header.
The offending macros and text are parsed and added to the top level
of the syntax tree, outside any section block.
.It Sy "first section is not NAME"
.Pq mdoc
The argument of the first
.Ic \&Sh
macro is not
.Sq NAME .
This may confuse
.Xr makewhatis 8
and
.Xr apropos 1 .
.It Sy "bad NAME section contents"
.Pq mdoc
The last node in the NAME section is not an
.Ic \&Nd
macro, or any preceding macro is not
.Ic \&Nm ,
or the NAME section is completely empty.
This may confuse
.Xr makewhatis 8
and
.Xr apropos 1 .
.It Sy "sections out of conventional order"
.Pq mdoc
A standard section occurs after another section it usually precedes.
All section titles are used as given,
and the order of sections is not changed.
.It Sy "duplicate section title"
.Pq mdoc
The same standard section title occurs more than once.
.It Sy "unexpected section"
.Pq mdoc
A standard section header occurs in a section of the manual
where it normally isn't useful.
.It Sy "unusual Xr order"
.Pq mdoc
In the SEE ALSO section, an
.Ic \&Xr
macro with a lower section number follows one with a higher number,
or two
.Ic \&Xr
macros refering to the same section are out of alphabetical order.
.It Sy "unusual Xr punctuation"
.Pq mdoc
In the SEE ALSO section, punctuation between two
.Ic \&Xr
macros differs from a single comma, or there is trailing punctuation
after the last
.Ic \&Xr
macro.
.It Sy "AUTHORS section without An macro"
.Pq mdoc
An AUTHORS sections contains no
.Ic \&An
macros, or only empty ones.
Probably, there are author names lacking markup.
.El
.Ss "Warnings related to macros and nesting"
.Bl -ohang
.It Sy "obsolete macro"
.Pq mdoc
See the
.Xr mdoc 7
manual for replacements.
+.It Sy "macro neither callable nor escaped"
+.Pq mdoc
+The name of a macro that is not callable appears on a macro line.
+It is printed verbatim.
+If the intention is to call it, move it to its own line;
+otherwise, escape it by prepending
+.Sq \e& .
.It Sy "skipping paragraph macro"
In
.Xr mdoc 7
documents, this happens
.Bl -dash -compact
.It
at the beginning and end of sections and subsections
.It
right before non-compact lists and displays
.It
at the end of items in non-column, non-compact lists
.It
and for multiple consecutive paragraph macros.
.El
In
.Xr man 7
documents, it happens
.Bl -dash -compact
.It
for empty
.Ic \&P ,
.Ic \&PP ,
and
.Ic \&LP
macros
.It
for
.Ic \&IP
macros having neither head nor body arguments
.It
for
.Ic \&br
or
.Ic \&sp
right after
.Ic \&SH
or
.Ic \&SS
.El
.It Sy "moving paragraph macro out of list"
.Pq mdoc
A list item in a
.Ic \&Bl
list contains a trailing paragraph macro.
The paragraph macro is moved after the end of the list.
.It Sy "skipping no-space macro"
.Pq mdoc
An input line begins with an
.Ic \&Ns
macro.
The macro is ignored.
.It Sy "blocks badly nested"
.Pq mdoc
If two blocks intersect, one should completely contain the other.
Otherwise, rendered output is likely to look strange in any output
format, and rendering in SGML-based output formats is likely to be
outright wrong because such languages do not support badly nested
blocks at all.
Typical examples of badly nested blocks are
.Qq Ic \&Ao \&Bo \&Ac \&Bc
and
.Qq Ic \&Ao \&Bq \&Ac .
In these examples,
.Ic \&Ac
breaks
.Ic \&Bo
and
.Ic \&Bq ,
respectively.
.It Sy "nested displays are not portable"
.Pq mdoc
A
.Ic \&Bd ,
.Ic \&D1 ,
or
.Ic \&Dl
display occurs nested inside another
.Ic \&Bd
display.
This works with
.Nm ,
but fails with most other implementations.
.It Sy "moving content out of list"
.Pq mdoc
A
.Ic \&Bl
list block contains text or macros before the first
.Ic \&It
macro.
The offending children are moved before the beginning of the list.
.It Sy ".Vt block has child macro"
.Pq mdoc
The
.Ic \&Vt
macro supports plain text arguments only.
Formatting may be ugly and semantic searching
for the affected content might not work.
.It Sy "fill mode already enabled, skipping"
.Pq man
A
.Ic \&fi
request occurs even though the document is still in fill mode,
or already switched back to fill mode.
It has no effect.
.It Sy "fill mode already disabled, skipping"
.Pq man
An
.Ic \&nf
request occurs even though the document already switched to no-fill mode
and did not switch back to fill mode yet.
It has no effect.
.It Sy "line scope broken"
.Pq man
While parsing the next-line scope of the previous macro,
another macro is found that prematurely terminates the previous one.
The previous, interrupted macro is deleted from the parse tree.
.El
.Ss "Warnings related to missing arguments"
.Bl -ohang
.It Sy "skipping empty request"
.Pq roff , eqn
The macro name is missing from a macro definition request,
or an
.Xr eqn 7
control statement or operation keyword lacks its required argument.
.It Sy "conditional request controls empty scope"
.Pq roff
A conditional request is only useful if any of the following
follows it on the same logical input line:
.Bl -dash -compact
.It
The
.Sq \e{
keyword to open a multi-line scope.
.It
A request or macro or some text, resulting in a single-line scope.
.It
The immediate end of the logical line without any intervening whitespace,
resulting in next-line scope.
.El
Here, a conditional request is followed by trailing whitespace only,
and there is no other content on its logical input line.
Note that it doesn't matter whether the logical input line is split
across multiple physical input lines using
.Sq \e
line continuation characters.
This is one of the rare cases
where trailing whitespace is syntactically significant.
The conditional request controls a scope containing whitespace only,
so it is unlikely to have a significant effect,
except that it may control a following
.Ic \&el
clause.
.It Sy "skipping empty macro"
.Pq mdoc
The indicated macro has no arguments and hence no effect.
.It Sy "empty argument, using 0n"
.Pq mdoc
The required width is missing after
.Ic \&Bd
or
.Ic \&Bl
.Fl offset
or
.Fl width.
.It Sy "argument count wrong"
.Pq mdoc , man
The indicated macro has too few or too many arguments.
The syntax tree will contain the wrong number of arguments as given.
Formatting behaviour depends on the specific macro in question.
Note that the same message may also occur as an ERROR, see below.
.It Sy "missing display type, using -ragged"
.Pq mdoc
The
.Ic \&Bd
macro is invoked without the required display type.
.It Sy "list type is not the first argument"
.Pq mdoc
In a
.Ic \&Bl
macro, at least one other argument precedes the type argument.
The
.Nm
utility copes with any argument order, but some other
.Xr mdoc 7
implementations do not.
.It Sy "missing -width in -tag list, using 8n"
.Pq mdoc
Every
.Ic \&Bl
macro having the
.Fl tag
argument requires
.Fl width ,
too.
.It Sy "missing utility name, using \(dq\(dq"
.Pq mdoc
The
.Ic \&Ex Fl std
macro is called without an argument before
.Ic \&Nm
has first been called with an argument.
.It Sy "empty head in list item"
.Pq mdoc
In a
.Ic \&Bl
.Fl diag ,
.Fl hang ,
.Fl inset ,
.Fl ohang ,
or
.Fl tag
list, an
.Ic \&It
macro lacks the required argument.
The item head is left empty.
.It Sy "empty list item"
.Pq mdoc
In a
.Ic \&Bl
.Fl bullet ,
.Fl dash ,
.Fl enum ,
or
.Fl hyphen
list, an
.Ic \&It
block is empty.
An empty list item is shown.
.It Sy "missing font type"
.Pq mdoc
A
.Ic \&Bf
macro has no argument.
It switches to the default font,
.Cm \efR .
.It Sy "unknown font type"
.Pq mdoc
The
.Ic \&Bf
argument is invalid.
The default font
.Cm \efR
is used instead.
+.It Sy "nothing follows prefix"
+.Pq mdoc
+A
+.Ic \&Pf
+macro has no argument, or only one argument and no macro follows
+on the same input line.
+This defeats its purpose; in particular, spacing is not suppressed
+before the text or macros following on the next input line.
.It Sy "missing -std argument, adding it"
.Pq mdoc
An
.Ic \&Ex
or
.Ic \&Rv
macro lacks the required
.Fl std
argument.
The
.Nm
utility assumes
.Fl std
even when it is not specified, but other implementations may not.
.It Sy "missing eqn box, using \(dq\(dq"
.Pq eqn
A diacritic mark or a binary operator is found,
but there is nothing to the left of it.
An empty box is inserted.
.El
.Ss "Warnings related to bad macro arguments"
.Bl -ohang
.It Sy "unterminated quoted argument"
.Pq roff
Macro arguments can be enclosed in double quote characters
such that space characters and macro names contained in the quoted
argument need not be escaped.
The closing quote of the last argument of a macro can be omitted.
However, omitting it is not recommended because it makes the code
harder to read.
.It Sy "duplicate argument"
.Pq mdoc
A
.Ic \&Bd
or
.Ic \&Bl
macro has more than one
.Fl compact ,
more than one
.Fl offset ,
or more than one
.Fl width
argument.
All but the last instances of these arguments are ignored.
.It Sy "skipping duplicate argument"
.Pq mdoc
An
.Ic \&An
macro has more than one
.Fl split
or
.Fl nosplit
argument.
All but the first of these arguments are ignored.
.It Sy "skipping duplicate display type"
.Pq mdoc
A
.Ic \&Bd
macro has more than one type argument; the first one is used.
.It Sy "skipping duplicate list type"
.Pq mdoc
A
.Ic \&Bl
macro has more than one type argument; the first one is used.
.It Sy "skipping -width argument"
.Pq mdoc
A
.Ic \&Bl
.Fl column ,
.Fl diag ,
.Fl ohang ,
.Fl inset ,
or
.Fl item
list has a
.Fl width
argument.
That has no effect.
.It Sy "unknown AT&T UNIX version"
.Pq mdoc
An
.Ic \&At
macro has an invalid argument.
It is used verbatim, with
.Qq "AT&T UNIX "
prefixed to it.
.It Sy "comma in function argument"
.Pq mdoc
An argument of an
.Ic \&Fa
or
.Ic \&Fn
macro contains a comma; it should probably be split into two arguments.
.It Sy "parenthesis in function name"
.Pq mdoc
The first argument of an
.Ic \&Fc
or
.Ic \&Fn
macro contains an opening or closing parenthesis; that's probably wrong,
parentheses are added automatically.
.It Sy "invalid content in Rs block"
.Pq mdoc
An
.Ic \&Rs
block contains plain text or non-% macros.
The bogus content is left in the syntax tree.
Formatting may be poor.
.It Sy "invalid Boolean argument"
.Pq mdoc
An
.Ic \&Sm
macro has an argument other than
.Cm on
or
.Cm off .
The invalid argument is moved out of the macro, which leaves the macro
empty, causing it to toggle the spacing mode.
.It Sy "unknown font, skipping request"
.Pq man , tbl
A
.Xr roff 7
.Ic \&ft
request or a
.Xr tbl 7
.Ic \&f
layout modifier has an unknown
.Ar font
argument.
.El
.Ss "Warnings related to plain text"
.Bl -ohang
.It Sy "blank line in fill mode, using .sp"
.Pq mdoc
The meaning of blank input lines is only well-defined in non-fill mode:
In fill mode, line breaks of text input lines are not supposed to be
significant.
However, for compatibility with groff, blank lines in fill mode
are replaced with
.Ic \&sp
requests.
.It Sy "tab in filled text"
.Pq mdoc , man
The meaning of tab characters is only well-defined in non-fill mode:
In fill mode, whitespace is not supposed to be significant
on text input lines.
As an implementation dependent choice, tab characters on text lines
are passed through to the formatters in any case.
Given that the text before the tab character will be filled,
it is hard to predict which tab stop position the tab will advance to.
.It Sy "whitespace at end of input line"
.Pq mdoc , man , roff
Whitespace at the end of input lines is almost never semantically
significant \(em but in the odd case where it might be, it is
extremely confusing when reviewing and maintaining documents.
.It Sy "bad comment style"
.Pq roff
Comment lines start with a dot, a backslash, and a double-quote character.
The
.Nm
utility treats the line as a comment line even without the backslash,
but leaving out the backslash might not be portable.
.It Sy "invalid escape sequence"
.Pq roff
An escape sequence has an invalid opening argument delimiter, lacks the
closing argument delimiter, or the argument has too few characters.
If the argument is incomplete,
.Ic \e*
and
.Ic \en
expand to an empty string,
.Ic \eB
to the digit
.Sq 0 ,
and
.Ic \ew
to the length of the incomplete argument.
All other invalid escape sequences are ignored.
.It Sy "undefined string, using \(dq\(dq"
.Pq roff
If a string is used without being defined before,
its value is implicitly set to the empty string.
However, defining strings explicitly before use
keeps the code more readable.
.El
.Ss "Errors related to equations"
.Bl -inset -compact
.It "unexpected equation scope closure"
.It "equation scope open on exit"
.It "overlapping equation scopes"
.It "unexpected end of equation"
.El
.Ss "Errors related to tables"
.Bl -inset -compact
.It "bad table syntax"
.It "bad table option"
.It "bad table layout"
.It "no table layout cells specified"
.It "no table data cells specified"
.It "ignore data in cell"
.It "data block still open"
.It "ignoring extra data cells"
.El
.Ss "Errors related to roff, mdoc, and man code"
.Bl -ohang
.It Sy "input stack limit exceeded, infinite loop?"
.Pq roff
Explicit recursion limits are implemented for the following features,
in order to prevent infinite loops:
.Bl -dash -compact
.It
expansion of nested escape sequences
including expansion of strings and number registers,
.It
expansion of nested user-defined macros,
.It
and
.Ic \&so
file inclusion.
.El
When a limit is hit, the output is incorrect, typically losing
some content, but the parser can continue.
.It Sy "skipping bad character"
.Pq mdoc , man , roff
The input file contains a byte that is not a printable
.Xr ascii 7
character.
The message mentions the character number.
The offending byte is replaced with a question mark
.Pq Sq \&? .
Consider editing the input file to replace the byte with an ASCII
transliteration of the intended character.
.It Sy "skipping unknown macro"
.Pq mdoc , man , roff
The first identifier on a request or macro line is neither recognized as a
.Xr roff 7
request, nor as a user-defined macro, nor, respectively, as an
.Xr mdoc 7
or
.Xr man 7
macro.
It may be mistyped or unsupported.
The request or macro is discarded including its arguments.
.It Sy "skipping item outside list"
.Pq mdoc , eqn
An
.Ic \&It
macro occurs outside any
.Ic \&Bl
list, or an
.Xr eqn 7
.Ic above
delimiter occurs outside any pile.
It is discarded including its arguments.
.It Sy "skipping column outside column list"
.Pq mdoc
A
.Ic \&Ta
macro occurs outside any
.Ic \&Bl Fl column
block.
It is discarded including its arguments.
.It Sy "skipping end of block that is not open"
.Pq mdoc , man , eqn , tbl , roff
Various syntax elements can only be used to explicitly close blocks
that have previously been opened.
An
.Xr mdoc 7
block closing macro, a
.Xr man 7
.Ic \&RE
or
.Ic \&UE
macro, an
.Xr eqn 7
right delimiter or closing brace, or the end of an equation, table, or
.Xr roff 7
conditional request is encountered but no matching block is open.
The offending request or macro is discarded.
.It Sy "inserting missing end of block"
.Pq mdoc , tbl
Various
.Xr mdoc 7
macros as well as tables require explicit closing by dedicated macros.
A block that doesn't support bad nesting
ends before all of its children are properly closed.
The open child nodes are closed implicitly.
.It Sy "scope open on exit"
.Pq mdoc , man , eqn , tbl , roff
At the end of the document, an explicit
.Xr mdoc 7
block, a
.Xr man 7
next-line scope or
.Ic \&RS
or
.Ic \&UR
block, an equation, table, or
.Xr roff 7
conditional or ignore block is still open.
The open block is closed implicitly.
.It Sy "escaped character not allowed in a name"
.Pq roff
Macro, string and register identifiers consist of printable,
non-whitespace ASCII characters.
Escape sequences and characters and strings expressed in terms of them
cannot form part of a name.
The first argument of an
.Ic \&am ,
.Ic \&as ,
.Ic \&de ,
.Ic \&ds ,
.Ic \&nr ,
or
.Ic \&rr
request, or any argument of an
.Ic \&rm
request, or the name of a request or user defined macro being called,
is terminated by an escape sequence.
In the cases of
.Ic \&as ,
.Ic \&ds ,
and
.Ic \&nr ,
the request has no effect at all.
In the cases of
.Ic \&am ,
.Ic \&de ,
.Ic \&rr ,
and
.Ic \&rm ,
what was parsed up to this point is used as the arguments to the request,
and the rest of the input line is discarded including the escape sequence.
When parsing for a request or a user-defined macro name to be called,
only the escape sequence is discarded.
The characters preceding it are used as the request or macro name,
the characters following it are used as the arguments to the request or macro.
.It Sy "argument count wrong"
.Pq mdoc , man , roff
The indicated request or macro has too few or too many arguments.
The syntax tree will contain the wrong number of arguments as given.
Formatting behaviour depends on the specific request or macro in question.
Note that the same message may also occur as a WARNING, see above.
.It Sy "NOT IMPLEMENTED: Bd -file"
.Pq mdoc
For security reasons, the
.Ic \&Bd
macro does not support the
.Fl file
argument.
By requesting the inclusion of a sensitive file, a malicious document
might otherwise trick a privileged user into inadvertently displaying
the file on the screen, revealing the file content to bystanders.
The argument is ignored including the file name following it.
.It Sy "missing list type, using -item"
.Pq mdoc
A
.Ic \&Bl
macro fails to specify the list type.
.It Sy "missing manual name, using \(dq\(dq"
.Pq mdoc
The first call to
.Ic \&Nm
lacks the required argument.
.It Sy "uname(3) system call failed, using UNKNOWN"
.Pq mdoc
The
.Ic \&Os
macro is called without arguments, and the
.Xr uname 3
system call failed.
As a workaround,
.Nm
can be compiled with
.Sm off
.Fl D Cm OSNAME=\(dq\e\(dq Ar string Cm \e\(dq\(dq .
.Sm on
.It Sy "unknown standard specifier"
.Pq mdoc
An
.Ic \&St
macro has an unknown argument and is discarded.
.It Sy "skipping request without numeric argument"
.Pq roff , eqn
An
.Ic \&it
request or an
.Xr eqn 7
.Ic \&size
or
.Ic \&gsize
statement has a non-numeric or negative argument or no argument at all.
The invalid request or statement is ignored.
.It Sy "skipping all arguments"
.Pq mdoc , man , eqn , roff
An
.Xr mdoc 7
.Ic \&Bt ,
.Ic \&Ed ,
.Ic \&Ef ,
.Ic \&Ek ,
.Ic \&El ,
.Ic \&Re ,
or
.Ic \&Ud
macro, an
.Ic \&It
macro in a list that don't support item heads, a
.Xr man 7
.Ic \&LP ,
.Ic \&P ,
or
.Ic \&PP
macro, an
.Xr eqn 7
.Ic \&EQ
or
.Ic \&EN
macro, or a
.Xr roff 7
.Sq \&..
block closing request is invoked with at least one argument.
All arguments are ignored.
.It Sy "skipping excess arguments"
.Pq mdoc , roff
The
.Ic \&Bf
macro is invoked with more than one argument, or a request of the
.Ic \&de
family is invoked with more than two arguments.
The excess arguments are ignored.
.El
.Ss FATAL errors
.Bl -ohang
.It Sy "input too large"
.Pq mdoc , man
Currently,
.Nm
cannot handle input files larger than its arbitrary size limit
of 2^31 bytes (2 Gigabytes).
Since useful manuals are always small, this is not a problem in practice.
Parsing is aborted as soon as the condition is detected.
.It Sy "NOT IMPLEMENTED: .so with absolute path or \(dq..\(dq"
.Pq roff
For security reasons,
.Nm
allows
.Ic \&so
file inclusion requests only with relative paths
and only without ascending to any parent directory.
By requesting the inclusion of a sensitive file, a malicious document
might otherwise trick a privileged user into inadvertently displaying
the file on the screen, revealing the file content to bystanders.
The parser exits immediately.
.It Sy ".so request failed"
.Pq roff
Servicing a
.Ic \&so
request requires reading an external file.
While trying to do so, an
.Xr open 2 ,
.Xr stat 2 ,
or
.Xr read 2
system call failed.
The parser exits immediately.
Before showing this message,
.Nm
always shows another message explaining why the system call failed.
.El
.Sh COMPATIBILITY
This section summarises
.Nm
compatibility with GNU troff.
Each input and output format is separately noted.
.Ss ASCII Compatibility
.Bl -bullet -compact
.It
Unrenderable unicode codepoints specified with
.Sq \e[uNNNN]
escapes are printed as
.Sq \&?
in mandoc.
In GNU troff, these raise an error.
.It
The
.Sq \&Bd \-literal
and
.Sq \&Bd \-unfilled
macros of
.Xr mdoc 7
in
.Fl T Ns Cm ascii
are synonyms, as are \-filled and \-ragged.
.It
In historic GNU troff, the
.Sq \&Pa
.Xr mdoc 7
macro does not underline when scoped under an
.Sq \&It
in the FILES section.
This behaves correctly in
.Nm .
.It
A list or display following the
.Sq \&Ss
.Xr mdoc 7
macro in
.Fl T Ns Cm ascii
does not assert a prior vertical break, just as it doesn't with
.Sq \&Sh .
.It
The
.Sq \&na
.Xr man 7
macro in
.Fl T Ns Cm ascii
has no effect.
.It
Words aren't hyphenated.
.El
.Ss HTML Compatibility
.Bl -bullet -compact
.It
The
.Sq \efP
escape will revert the font to the previous
.Sq \ef
escape, not to the last rendered decoration, which is now dictated by
CSS instead of hard-coded.
It also will not span past the current scope,
for the same reason.
Note that in
.Sx ASCII Output
mode, this will work fine.
.It
The
.Xr mdoc 7
.Sq \&Bl \-hang
and
.Sq \&Bl \-tag
list types render similarly (no break following overreached left-hand
side) due to the expressive constraints of HTML.
.It
The
.Xr man 7
.Sq IP
and
.Sq TP
lists render similarly.
.El
.Sh SEE ALSO
.Xr eqn 7 ,
.Xr man 7 ,
.Xr mandoc_char 7 ,
.Xr mdoc 7 ,
.Xr roff 7 ,
.Xr tbl 7
.Sh AUTHORS
The
.Nm
utility was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
.Sh CAVEATS
In
.Fl T Ns Cm html
and
.Fl T Ns Cm xhtml ,
the maximum size of an element attribute is determined by
.Dv BUFSIZ ,
which is usually 1024 bytes.
Be aware of this when setting long link
formats such as
.Fl O Ns Cm style Ns = Ns Ar really/long/link .
.Pp
Nesting elements within next-line element scopes of
.Fl m Ns Cm an ,
such as
.Sq br
within an empty
.Sq B ,
will confuse
.Fl T Ns Cm html
and
.Fl T Ns Cm xhtml
and cause them to forget the formatting of the prior next-line scope.
.Pp
The
.Sq \(aq
control character is an alias for the standard macro control character
and does not emit a line-break as stipulated in GNU troff.
Index: vendor/mdocml/dist/mandoc.h
===================================================================
--- vendor/mdocml/dist/mandoc.h (revision 276215)
+++ vendor/mdocml/dist/mandoc.h (revision 276216)
@@ -1,452 +1,450 @@
-/* $Id: mandoc.h,v 1.171 2014/11/28 18:09:01 schwarze Exp $ */
+/* $Id: mandoc.h,v 1.176 2014/12/01 08:05:52 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons
* Copyright (c) 2010-2014 Ingo Schwarze
*
* 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 */
#define ASCII_BREAK 29 /* breakable zero-width space */
/*
* Status level. This refers to both internal status (i.e., whilst
* running, when warnings/errors are reported) and an indicator of a
* threshold of when to halt (when said internal state exceeds the
* threshold).
*/
enum mandoclevel {
MANDOCLEVEL_OK = 0,
MANDOCLEVEL_RESERVED,
MANDOCLEVEL_WARNING, /* warnings: syntax, whitespace, etc. */
MANDOCLEVEL_ERROR, /* input has been thrown away */
MANDOCLEVEL_FATAL, /* input is borked */
MANDOCLEVEL_BADARG, /* bad argument in invocation */
MANDOCLEVEL_SYSERR, /* system error */
MANDOCLEVEL_MAX
};
/*
* All possible things that can go wrong within a parse, be it libroff,
* libmdoc, or libman.
*/
enum mandocerr {
MANDOCERR_OK,
MANDOCERR_WARNING, /* ===== start of warnings ===== */
/* related to the prologue */
MANDOCERR_DT_NOTITLE, /* missing manual title, using UNTITLED: line */
MANDOCERR_TH_NOTITLE, /* missing manual title, using "": [macro] */
MANDOCERR_TITLE_CASE, /* lower case character in document title */
MANDOCERR_MSEC_MISSING, /* missing manual section, using "": macro */
MANDOCERR_MSEC_BAD, /* unknown manual section: Dt ... section */
MANDOCERR_DATE_MISSING, /* missing date, using today's date */
MANDOCERR_DATE_BAD, /* cannot parse date, using it verbatim: date */
MANDOCERR_OS_MISSING, /* missing Os macro, using "" */
MANDOCERR_PROLOG_REP, /* duplicate prologue macro: macro */
MANDOCERR_PROLOG_LATE, /* late prologue macro: macro */
MANDOCERR_DT_LATE, /* skipping late title macro: Dt args */
MANDOCERR_PROLOG_ORDER, /* prologue macros out of order: macros */
/* related to document structure */
MANDOCERR_SO, /* .so is fragile, better use ln(1): so path */
MANDOCERR_DOC_EMPTY, /* no document body */
MANDOCERR_SEC_BEFORE, /* content before first section header: macro */
MANDOCERR_NAMESEC_FIRST, /* first section is not NAME: Sh title */
MANDOCERR_NAMESEC_BAD, /* bad NAME section contents: macro */
MANDOCERR_SEC_ORDER, /* sections out of conventional order: Sh title */
MANDOCERR_SEC_REP, /* duplicate section title: Sh title */
MANDOCERR_SEC_MSEC, /* unexpected section: Sh title for ... only */
MANDOCERR_XR_ORDER, /* unusual Xr order: ... after ... */
MANDOCERR_XR_PUNCT, /* unusual Xr punctuation: ... after ... */
MANDOCERR_AN_MISSING, /* AUTHORS section without An macro */
/* related to macros and nesting */
MANDOCERR_MACRO_OBS, /* obsolete macro: macro */
+ MANDOCERR_MACRO_CALL, /* macro neither callable nor escaped: macro */
MANDOCERR_PAR_SKIP, /* skipping paragraph macro: macro ... */
MANDOCERR_PAR_MOVE, /* moving paragraph macro out of list: macro */
MANDOCERR_NS_SKIP, /* skipping no-space macro */
MANDOCERR_BLK_NEST, /* blocks badly nested: macro ... */
MANDOCERR_BD_NEST, /* nested displays are not portable: macro ... */
MANDOCERR_BL_MOVE, /* moving content out of list: macro */
MANDOCERR_VT_CHILD, /* .Vt block has child macro: macro */
MANDOCERR_FI_SKIP, /* fill mode already enabled, skipping: fi */
MANDOCERR_NF_SKIP, /* fill mode already disabled, skipping: nf */
MANDOCERR_BLK_LINE, /* line scope broken: macro breaks macro */
/* related to missing arguments */
MANDOCERR_REQ_EMPTY, /* skipping empty request: request */
MANDOCERR_COND_EMPTY, /* conditional request controls empty scope */
MANDOCERR_MACRO_EMPTY, /* skipping empty macro: macro */
MANDOCERR_ARG_EMPTY, /* empty argument, using 0n: macro arg */
MANDOCERR_ARGCWARN, /* argument count wrong */
MANDOCERR_BD_NOTYPE, /* missing display type, using -ragged: Bd */
MANDOCERR_BL_LATETYPE, /* list type is not the first argument: Bl arg */
MANDOCERR_BL_NOWIDTH, /* missing -width in -tag list, using 8n */
MANDOCERR_EX_NONAME, /* missing utility name, using "": Ex */
MANDOCERR_IT_NOHEAD, /* empty head in list item: Bl -type It */
MANDOCERR_IT_NOBODY, /* empty list item: Bl -type It */
MANDOCERR_BF_NOFONT, /* missing font type, using \fR: Bf */
MANDOCERR_BF_BADFONT, /* unknown font type, using \fR: Bf font */
+ MANDOCERR_PF_SKIP, /* nothing follows prefix: Pf arg */
MANDOCERR_ARG_STD, /* missing -std argument, adding it: macro */
MANDOCERR_EQN_NOBOX, /* missing eqn box, using "": op */
/* related to bad arguments */
MANDOCERR_ARG_QUOTE, /* unterminated quoted argument */
MANDOCERR_ARG_REP, /* duplicate argument: macro arg */
MANDOCERR_AN_REP, /* skipping duplicate argument: An -arg */
MANDOCERR_BD_REP, /* skipping duplicate display type: Bd -type */
MANDOCERR_BL_REP, /* skipping duplicate list type: Bl -type */
MANDOCERR_BL_SKIPW, /* skipping -width argument: Bl -type */
MANDOCERR_AT_BAD, /* unknown AT&T UNIX version: At version */
MANDOCERR_FA_COMMA, /* comma in function argument: arg */
MANDOCERR_FN_PAREN, /* parenthesis in function name: arg */
MANDOCERR_RS_BAD, /* invalid content in Rs block: macro */
MANDOCERR_SM_BAD, /* invalid Boolean argument: macro arg */
MANDOCERR_FT_BAD, /* unknown font, skipping request: ft font */
/* related to plain text */
MANDOCERR_FI_BLANK, /* blank line in fill mode, using .sp */
MANDOCERR_FI_TAB, /* tab in filled text */
MANDOCERR_SPACE_EOL, /* whitespace at end of input line */
MANDOCERR_COMMENT_BAD, /* bad comment style */
MANDOCERR_ESC_BAD, /* invalid escape sequence: esc */
MANDOCERR_STR_UNDEF, /* undefined string, using "": name */
MANDOCERR_ERROR, /* ===== start of errors ===== */
/* related to equations */
MANDOCERR_EQNNSCOPE, /* unexpected equation scope closure*/
MANDOCERR_EQNSCOPE, /* equation scope open on exit */
MANDOCERR_EQNBADSCOPE, /* overlapping equation scopes */
MANDOCERR_EQNEOF, /* unexpected end of equation */
/* related to tables */
MANDOCERR_TBL, /* bad table syntax */
MANDOCERR_TBLOPT, /* bad table option */
MANDOCERR_TBLLAYOUT, /* bad table layout */
MANDOCERR_TBLNOLAYOUT, /* no table layout cells specified */
MANDOCERR_TBLNODATA, /* no table data cells specified */
MANDOCERR_TBLIGNDATA, /* ignore data in cell */
MANDOCERR_TBLBLOCK, /* data block still open */
MANDOCERR_TBLEXTRADAT, /* ignoring extra data cells */
/* related to document structure and macros */
MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */
MANDOCERR_BADCHAR, /* skipping bad character: number */
MANDOCERR_MACRO, /* skipping unknown macro: macro */
MANDOCERR_IT_STRAY, /* skipping item outside list: It ... */
MANDOCERR_TA_STRAY, /* skipping column outside column list: Ta */
MANDOCERR_BLK_NOTOPEN, /* skipping end of block that is not open */
MANDOCERR_BLK_BROKEN, /* inserting missing end of block: macro ... */
MANDOCERR_BLK_NOEND, /* appending missing end of block: macro */
/* related to request and macro arguments */
MANDOCERR_NAMESC, /* escaped character not allowed in a name: name */
MANDOCERR_ARGCOUNT, /* argument count wrong */
MANDOCERR_BD_FILE, /* NOT IMPLEMENTED: Bd -file */
MANDOCERR_BL_NOTYPE, /* missing list type, using -item: Bl */
MANDOCERR_NM_NONAME, /* missing manual name, using "": Nm */
MANDOCERR_OS_UNAME, /* uname(3) system call failed, using UNKNOWN */
MANDOCERR_ST_BAD, /* unknown standard specifier: St standard */
MANDOCERR_IT_NONUM, /* skipping request without numeric argument */
MANDOCERR_ARG_SKIP, /* skipping all arguments: macro args */
MANDOCERR_ARG_EXCESS, /* skipping excess arguments: macro ... args */
MANDOCERR_DIVZERO, /* divide by zero */
MANDOCERR_FATAL, /* ===== start of fatal errors ===== */
MANDOCERR_TOOLARGE, /* input too large */
MANDOCERR_SO_PATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */
MANDOCERR_SO_FAIL, /* .so request failed */
/* ===== system errors ===== */
MANDOCERR_SYSDUP, /* cannot dup file descriptor */
MANDOCERR_SYSEXEC, /* cannot exec */
MANDOCERR_SYSEXIT, /* gunzip failed with code */
MANDOCERR_SYSFORK, /* cannot fork */
MANDOCERR_SYSOPEN, /* cannot open file */
MANDOCERR_SYSPIPE, /* cannot open pipe */
MANDOCERR_SYSREAD, /* cannot read file */
MANDOCERR_SYSSIG, /* gunzip died from signal */
MANDOCERR_SYSSTAT, /* cannot stat file */
MANDOCERR_SYSWAIT, /* wait failed */
MANDOCERR_MAX
};
struct tbl_opts {
char tab; /* cell-separator */
char decimal; /* decimal point */
int linesize;
int opts;
#define TBL_OPT_CENTRE (1 << 0)
#define TBL_OPT_EXPAND (1 << 1)
#define TBL_OPT_BOX (1 << 2)
#define TBL_OPT_DBOX (1 << 3)
#define TBL_OPT_ALLBOX (1 << 4)
#define TBL_OPT_NOKEEP (1 << 5)
#define TBL_OPT_NOSPACE (1 << 6)
int cols; /* number of columns */
};
/*
* The head of a table specifies all of its columns. When formatting a
* tbl_span, iterate over these and plug in data from the tbl_span when
* appropriate, using tbl_cell as a guide to placement.
*/
struct tbl_head {
int ident; /* 0 <= unique id < cols */
int vert; /* width of preceding vertical line */
struct tbl_head *next;
struct tbl_head *prev;
};
enum tbl_cellt {
TBL_CELL_CENTRE, /* c, C */
TBL_CELL_RIGHT, /* r, R */
TBL_CELL_LEFT, /* l, L */
TBL_CELL_NUMBER, /* n, N */
TBL_CELL_SPAN, /* s, S */
TBL_CELL_LONG, /* a, A */
TBL_CELL_DOWN, /* ^ */
TBL_CELL_HORIZ, /* _, - */
TBL_CELL_DHORIZ, /* = */
TBL_CELL_MAX
};
/*
* A cell in a layout row.
*/
struct tbl_cell {
struct tbl_cell *next;
int vert; /* width of preceding vertical line */
enum tbl_cellt pos;
size_t spacing;
int flags;
#define TBL_CELL_TALIGN (1 << 0) /* t, T */
#define TBL_CELL_BALIGN (1 << 1) /* d, D */
#define TBL_CELL_BOLD (1 << 2) /* fB, B, b */
#define TBL_CELL_ITALIC (1 << 3) /* fI, I, i */
#define TBL_CELL_EQUAL (1 << 4) /* e, E */
#define TBL_CELL_UP (1 << 5) /* u, U */
#define TBL_CELL_WIGN (1 << 6) /* z, Z */
#define TBL_CELL_WMAX (1 << 7) /* x, X */
struct tbl_head *head;
};
/*
* A layout row.
*/
struct tbl_row {
struct tbl_row *next;
struct tbl_cell *first;
struct tbl_cell *last;
int vert; /* trailing vertical line */
};
enum tbl_datt {
TBL_DATA_NONE, /* has no data */
TBL_DATA_DATA, /* consists of data/string */
TBL_DATA_HORIZ, /* horizontal line */
TBL_DATA_DHORIZ, /* double-horizontal line */
TBL_DATA_NHORIZ, /* squeezed horizontal line */
TBL_DATA_NDHORIZ /* squeezed double-horizontal line */
};
/*
* A cell within a row of data. The "string" field contains the actual
* string value that's in the cell. The rest is layout.
*/
struct tbl_dat {
struct tbl_cell *layout; /* layout cell */
int spans; /* how many spans follow */
struct tbl_dat *next;
char *string; /* data (NULL if not TBL_DATA_DATA) */
enum tbl_datt pos;
};
enum tbl_spant {
TBL_SPAN_DATA, /* span consists of data */
TBL_SPAN_HORIZ, /* span is horizontal line */
TBL_SPAN_DHORIZ /* span is double horizontal line */
};
/*
* A row of data in a table.
*/
struct tbl_span {
struct tbl_opts *opts;
struct tbl_head *head;
struct tbl_row *layout; /* layout row */
struct tbl_dat *first;
struct tbl_dat *last;
int line; /* parse line */
int flags;
#define TBL_SPAN_FIRST (1 << 0)
#define TBL_SPAN_LAST (1 << 1)
enum tbl_spant pos;
struct tbl_span *next;
};
enum eqn_boxt {
EQN_ROOT, /* root of parse tree */
EQN_TEXT, /* text (number, variable, whatever) */
EQN_SUBEXPR, /* nested `eqn' subexpression */
EQN_LIST, /* list (braces, etc.) */
EQN_LISTONE, /* singleton list */
EQN_PILE, /* vertical pile */
EQN_MATRIX /* pile of piles */
};
enum eqn_fontt {
EQNFONT_NONE = 0,
EQNFONT_ROMAN,
EQNFONT_BOLD,
EQNFONT_FAT,
EQNFONT_ITALIC,
EQNFONT__MAX
};
enum eqn_post {
EQNPOS_NONE = 0,
EQNPOS_SUP,
EQNPOS_SUBSUP,
EQNPOS_SUB,
EQNPOS_TO,
EQNPOS_FROM,
EQNPOS_FROMTO,
EQNPOS_OVER,
EQNPOS_SQRT,
EQNPOS__MAX
};
enum eqn_pilet {
EQNPILE_NONE = 0,
EQNPILE_PILE,
EQNPILE_CPILE,
EQNPILE_RPILE,
EQNPILE_LPILE,
EQNPILE_COL,
EQNPILE_CCOL,
EQNPILE_RCOL,
EQNPILE_LCOL,
EQNPILE__MAX
};
/*
* A "box" is a parsed mathematical expression as defined by the eqn.7
* grammar.
*/
struct eqn_box {
int size; /* font size of expression */
#define EQN_DEFSIZE INT_MIN
enum eqn_boxt type; /* type of node */
struct eqn_box *first; /* first child node */
struct eqn_box *last; /* last child node */
struct eqn_box *next; /* node sibling */
struct eqn_box *prev; /* node sibling */
struct eqn_box *parent; /* node sibling */
char *text; /* text (or NULL) */
char *left; /* fence left-hand */
char *right; /* fence right-hand */
char *top; /* expression over-symbol */
char *bottom; /* expression under-symbol */
size_t args; /* arguments in parent */
size_t expectargs; /* max arguments in parent */
enum eqn_post pos; /* position of next box */
enum eqn_fontt font; /* font of box */
enum eqn_pilet pile; /* equation piling */
};
/*
* An equation consists of a tree of expressions starting at a given
* line and position.
*/
struct eqn {
char *name; /* identifier (or NULL) */
struct eqn_box *root; /* root mathematical expression */
int ln; /* invocation line */
int pos; /* invocation position */
};
/*
* Parse options.
*/
#define MPARSE_MDOC 1 /* assume -mdoc */
#define MPARSE_MAN 2 /* assume -man */
#define MPARSE_SO 4 /* honour .so requests */
#define MPARSE_QUICK 8 /* abort the parse early */
#define MPARSE_UTF8 16 /* accept UTF-8 input */
#define MPARSE_LATIN1 32 /* accept ISO-LATIN-1 input */
enum mandoc_esc {
ESCAPE_ERROR = 0, /* bail! unparsable escape */
ESCAPE_IGNORE, /* escape to be ignored */
ESCAPE_SPECIAL, /* a regular special character */
ESCAPE_FONT, /* a generic font mode */
ESCAPE_FONTBOLD, /* bold font mode */
ESCAPE_FONTITALIC, /* italic font mode */
ESCAPE_FONTBI, /* bold italic font mode */
ESCAPE_FONTROMAN, /* roman font mode */
ESCAPE_FONTPREV, /* previous font mode */
ESCAPE_NUMBERED, /* a numbered glyph */
ESCAPE_UNICODE, /* a unicode codepoint */
ESCAPE_NOSPACE, /* suppress space if the last on a line */
ESCAPE_SKIPCHAR /* skip the next character */
};
typedef void (*mandocmsg)(enum mandocerr, enum mandoclevel,
const char *, int, int, const char *);
+__BEGIN_DECLS
+
struct mparse;
struct mchars;
struct mdoc;
struct man;
-__BEGIN_DECLS
-
enum mandoc_esc mandoc_escape(const char **, const char **, int *);
struct mchars *mchars_alloc(void);
void mchars_free(struct mchars *);
int mchars_num2char(const char *, size_t);
const char *mchars_uc2str(int);
int mchars_num2uc(const char *, size_t);
int mchars_spec2cp(const struct mchars *,
const char *, size_t);
const char *mchars_spec2str(const struct mchars *,
const char *, size_t, size_t *);
struct mparse *mparse_alloc(int, enum mandoclevel, mandocmsg,
const struct mchars *, const char *);
void mparse_free(struct mparse *);
void mparse_keep(struct mparse *);
enum mandoclevel mparse_open(struct mparse *, int *, const char *);
enum mandoclevel mparse_readfd(struct mparse *, int, const char *);
-enum mandoclevel mparse_readmem(struct mparse *, const void *, size_t,
+enum mandoclevel mparse_readmem(struct mparse *, void *, size_t,
const char *);
void mparse_reset(struct mparse *);
void mparse_result(struct mparse *,
struct mdoc **, struct man **, char **);
const char *mparse_getkeep(const struct mparse *);
const char *mparse_strerror(enum mandocerr);
const char *mparse_strlevel(enum mandoclevel);
enum mandoclevel mparse_wait(struct mparse *);
__END_DECLS
-
-#endif /*!MANDOC_H*/
Index: vendor/mdocml/dist/mandoc_aux.h
===================================================================
--- vendor/mdocml/dist/mandoc_aux.h (revision 276215)
+++ vendor/mdocml/dist/mandoc_aux.h (revision 276216)
@@ -1,33 +1,29 @@
-/* $Id: mandoc_aux.h,v 1.2 2014/04/23 21:06:41 schwarze Exp $ */
+/* $Id: mandoc_aux.h,v 1.3 2014/12/01 04:05:32 schwarze Exp $ */
/*
* Copyright (c) 2009, 2011 Kristaps Dzonsons
* Copyright (c) 2014 Ingo Schwarze
*
* 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_AUX_H
-#define MANDOC_AUX_H
__BEGIN_DECLS
int mandoc_asprintf(char **, const char *, ...);
void *mandoc_calloc(size_t, size_t);
void *mandoc_malloc(size_t);
void *mandoc_realloc(void *, size_t);
void *mandoc_reallocarray(void *, size_t, size_t);
char *mandoc_strdup(const char *);
char *mandoc_strndup(const char *, size_t);
__END_DECLS
-
-#endif /*!MANDOC_AUX_H*/
Index: vendor/mdocml/dist/mandoc_headers.3
===================================================================
--- vendor/mdocml/dist/mandoc_headers.3 (nonexistent)
+++ vendor/mdocml/dist/mandoc_headers.3 (revision 276216)
@@ -0,0 +1,511 @@
+.Dd December 1, 2014
+.Dt MANDOC_HEADERS 3
+.Os
+.Sh NAME
+.Nm mandoc_headers
+.Nd ordering of mandoc include files
+.Sh DESCRIPTION
+To support a cleaner coding style, the mandoc header files do not
+contain any include directives and do not guard against multiple
+inclusion.
+The application developer has to make sure that the headers are
+included in a proper order, and that no header is included more
+than once.
+.Pp
+The headers and functions form three major groups:
+.Sx Parser interface ,
+.Sx Parser internals ,
+and
+.Sx Formatter interface .
+.Pp
+Various rules are given below prohibiting the inclusion of certain
+combinations of headers into the same file.
+The intention is to keep the following functional components
+separate from each other:
+.Pp
+.Bl -dash -offset indent -compact
+.It
+.Xr mdoc 7
+parser
+.It
+.Xr man 7
+parser
+.It
+.Xr roff 7
+parser
+.It
+.Xr tbl 7
+parser
+.It
+.Xr eqn 7
+parser
+.It
+terminal formatters
+.It
+HTML formatters
+.It
+search tools
+.El
+.Pp
+Note that mere usage of an opaque type does
+.Em not
+require inclusion of the header where that type is defined.
+.Ss Parser interface
+Each of the following headers can be included without including
+any other mandoc header.
+These headers should be included before any other mandoc headers.
+Afterwards, any other mandoc headers can be included as needed.
+.Bl -tag -width Ds
+.It Qq Pa mandoc_aux.h
+Requires
+.In sys/types.h
+for
+.Vt size_t .
+Provides the utility functions documented in
+.Xr mandoc_malloc 3 .
+.It Qq Pa mandoc.h
+Requires
+.In sys/types.h
+for
+.Vt size_t .
+.Pp
+Provides
+.Vt enum mandoc_esc ,
+.Vt enum mandocerr ,
+.Vt enum mandoclevel ,
+.Vt enum tbl_cellt ,
+.Vt enum tbl_datt ,
+.Vt enum tbl_spant ,
+.Vt enum eqn_boxt ,
+.Vt enum eqn_fontt ,
+.Vt enum eqn_pilet ,
+.Vt enum eqn_post ,
+.Vt struct tbl_opts ,
+.Vt struct tbl_head ,
+.Vt struct tbl_cell ,
+.Vt struct tbl_row ,
+.Vt struct tbl_dat ,
+.Vt struct tbl_span ,
+.Vt struct eqn_box ,
+.Vt struct eqn ,
+the function prototype typedef
+.Fn mandocmsg ,
+the function
+.Xr mandoc_escape 3 ,
+the functions described in
+.Xr mchars_alloc 3 ,
+and the functions
+.Fn mparse_*
+described in
+.Xr mandoc 3 .
+.Pp
+Uses the opaque types
+.Vt struct mparse
+from
+.Pa read.c
+and
+.Vt struct mchars
+from
+.Pa chars.c
+for function prototypes.
+Uses the types
+.Vt struct mdoc
+from
+.Pa libmdoc.h
+and
+.Vt struct man
+from
+.Pa libman.h
+as opaque types for function prototypes.
+.It Qq Pa mdoc.h
+Requires
+.In sys/types.h
+for
+.Vt size_t .
+.Pp
+Provides
+.Vt enum mdoct ,
+.Vt enum mdocargt ,
+.Vt enum mdoc_type ,
+.Vt enum mdoc_sec ,
+.Vt enum mdoc_endbody ,
+.Vt enum mdoc_disp ,
+.Vt enum mdoc_list ,
+.Vt enum mdoc_auth ,
+.Vt enum mdoc_font ,
+.Vt struct mdoc_meta ,
+.Vt struct mdoc_argv ,
+.Vt struct mdoc_arg ,
+.Vt struct mdoc_bd ,
+.Vt struct mdoc_bl ,
+.Vt struct mdoc_an ,
+.Vt struct mdoc_bf ,
+.Vt struct mdoc_rs ,
+.Vt struct mdoc_node ,
+and the functions
+.Fn mdoc_*
+described in
+.Xr mandoc 3 .
+.Pp
+Uses the type
+.Vt struct mdoc
+from
+.Pa libmdoc.h
+as an opaque type for function prototypes.
+Uses pointers to the types
+.Vt struct tbl_span
+and
+.Vt struct eqn
+as opaque struct members.
+.Pp
+When this header is included, the same file should not include
+.Pa libman.h
+or
+.Pa libroff.h .
+.It Qq Pa man.h
+Provides
+.Vt enum mant ,
+.Vt enum man_type ,
+.Vt struct man_meta ,
+.Vt struct man_node ,
+and the functions
+.Fn man_*
+described in
+.Xr mandoc 3 .
+.Pp
+Uses the opaque type
+.Vt struct mparse
+from
+.Pa read.c
+for function prototypes.
+Uses the type
+.Vt struct man
+from
+.Pa libman.h
+as an opaque type for function prototypes.
+Uses pointers to the types
+.Vt struct tbl_span
+and
+.Vt struct eqn
+as opaque struct members.
+.Pp
+When this header is included, the same file should not include
+.Pa libmdoc.h
+or
+.Pa libroff.h .
+.El
+.Ss Parser internals
+The following headers require inclusion of a parser interface header
+before they can be included. All parser interface headers should
+precede all parser internal headers. When any parser internal headers
+are included, the same file should not include any formatter headers.
+.Bl -tag -width Ds
+.It Qq Pa libmandoc.h
+Requires
+.In sys/types.h
+for
+.Vt size_t .
+.Pp
+Provides
+.Vt enum rofferr ,
+.Vt struct buf ,
+utility functions needed by multiple parsers,
+and the top-level functions to call the parsers.
+.Pp
+Uses the opaque types
+.Vt struct mparse
+from
+.Pa read.c
+and
+.Vt struct roff
+from
+.Pa roff.c
+for function prototypes.
+Uses the types
+.Vt enum mandocerr ,
+.Vt struct tbl_span ,
+and
+.Vt struct eqn
+from
+.Pa mandoc.h ,
+.Vt struct mdoc
+from
+.Pa libmdoc.h ,
+and
+.Vt struct man
+from
+.Pa libman.h
+as opaque types for function prototypes.
+.It Qq Pa libmdoc.h
+Requires
+.Qq Pa mdoc.h
+for
+.Vt enum mdoct ,
+.Vt enum mdoc_* ,
+and
+.Vt struct mdoc_* .
+.Pp
+Provides
+.Vt enum mdoc_next ,
+.Vt enum margserr ,
+.Vt enum mdelim ,
+.Vt struct mdoc ,
+.Vt struct mdoc_macro ,
+and many functions internal to the
+.Xr mdoc 7
+parser.
+.Pp
+Uses the opaque types
+.Vt struct mparse
+from
+.Pa read.c
+and
+.Vt struct roff
+from
+.Pa roff.c .
+.Pp
+When this header is included, the same file should not include
+.Pa man.h ,
+.Pa libman.h ,
+or
+.Pa libroff.h .
+.It Qq Pa libman.h
+Requires
+.Qq Pa man.h
+for
+.Vt enum mant
+and
+.Vt struct man_node.
+.Pp
+Provides
+.Vt enum man_next ,
+.Vt struct man ,
+.Vt struct man_macro ,
+and many functions internal to the
+.Xr man 7
+parser.
+.Pp
+Uses the opaque types
+.Vt struct mparse
+from
+.Pa read.c
+and
+.Vt struct roff
+from
+.Pa roff.c .
+.Pp
+When this header is included, the same file should not include
+.Pa mdoc.h ,
+.Pa libmdoc.h ,
+or
+.Pa libroff.h .
+.It Qq Pa libroff.h
+Requires
+.In sys/types.h
+for
+.Vt size_t ,
+.Qq Pa mandoc.h
+for
+.Vt struct tbl_*
+and
+.Vt struct eqn ,
+and
+.Qq Pa libmandoc.h
+for
+.Vt enum rofferr .
+.Pp
+Provides
+.Vt enum tbl_part ,
+.Vt struct tbl_node ,
+.Vt struct eqn_def ,
+.Vt struct eqn_node ,
+and many functions internal to the
+.Xr tbl 7
+and
+.Xr eqn 7
+parsers.
+.Pp
+Uses the opaque type
+.Vt struct mparse
+from
+.Pa read.c .
+.Pp
+When this header is included, the same file should not include
+.Pa man.h ,
+.Pa mdoc.h ,
+.Pa libman.h ,
+or
+.Pa libmdoc.h .
+.El
+.Ss Formatter interface
+These headers should be included after any parser interface headers.
+No parser internal headers should be included by the same file.
+.Bl -tag -width Ds
+.It Qq Pa out.h
+Requires
+.In sys/types.h
+for
+.Vt size_t .
+.Pp
+Provides
+.Vt enum roffscale ,
+.Vt struct roffcol ,
+.Vt struct roffsu ,
+.Vt struct rofftbl ,
+.Fn a2roffsu ,
+and
+.Fn tblcalc .
+.Pp
+Uses
+.Vt struct tbl_span
+from
+.Pa mandoc.h
+as an opaque type for function prototypes.
+.Pp
+When this header is included, the same file should not include
+.Pa manpath.h
+or
+.Pa mansearch.h .
+.It Qq Pa term.h
+Requires
+.In sys/types.h
+for
+.Vt size_t
+and
+.Qq Pa out.h
+for
+.Vt struct roffsu
+and
+.Vt struct rofftbl .
+.Pp
+Provides
+.Vt enum termenc ,
+.Vt enum termfont ,
+.Vt enum termtype ,
+.Vt struct termp_tbl ,
+.Vt struct termp ,
+and many terminal formatting functions.
+.Pp
+Uses the opaque types
+.Vt struct mchars
+from
+.Pa chars.c
+and
+.Vt struct termp_ps
+from
+.Pa term_ps.c .
+Uses
+.Vt struct tbl_span
+and
+.Vt struct eqn
+from
+.Pa mandoc.h
+as opaque types for function prototypes.
+.Pp
+When this header is included, the same file should not include
+.Pa html.h ,
+.Pa manpath.h
+or
+.Pa mansearch.h .
+.It Qq Pa html.h
+Requires
+.In sys/types.h
+for
+.Vt size_t ,
+.In stdio.h
+for
+.Dv BUFSIZ ,
+and
+.Qq Pa out.h
+for
+.Vt struct roffsu
+and
+.Vt struct rofftbl .
+.Pp
+Provides
+.Vt enum htmltag ,
+.Vt enum htmlattr ,
+.Vt enum htmlfont ,
+.Vt struct tag ,
+.Vt struct tagq ,
+.Vt struct htmlpair ,
+.Vt struct html ,
+and many HTML formatting functions.
+.Pp
+Uses the opaque type
+.Vt struct mchars
+from
+.Pa chars.c .
+.Pp
+When this header is included, the same file should not include
+.Pa term.h ,
+.Pa manpath.h
+or
+.Pa mansearch.h .
+.It Qq Pa main.h
+Provides the top level steering functions for all formatters.
+.Pp
+Uses the opaque type
+.Vt struct mchars
+from
+.Pa chars.c .
+Uses the types
+.Vt struct mdoc
+from
+.Pa libmdoc.h
+and
+.Vt struct man
+from
+.Pa libman.h
+as opaque types for function prototypes.
+.It Qq Pa manpath.h
+Requires
+.In sys/types.h
+for
+.Vt size_t .
+.Pp
+Provides
+.Vt struct manpaths
+and the functions
+.Fn manpath_manconf ,
+.Fn manpath_parse ,
+and
+.Fn manpath_free .
+.Pp
+When this header is included, the same file should not include
+.Pa out.h ,
+.Pa term.h ,
+or
+.Pa html.h .
+.It Qq Pa mansearch.h
+Requires
+.In sys/types.h
+for
+.Vt size_t
+and
+.In stdint.h
+for
+.Vt uint64_t .
+.Pp
+Provides
+.Vt enum argmode ,
+.Vt struct manpage ,
+.Vt struct mansearch ,
+and the functions
+.Fn mansearch_setup ,
+.Fn mansearch ,
+and
+.Fn mansearch_free .
+.Pp
+Uses
+.Vt struct manpaths
+from
+.Pa manpath.h
+as an opaque type for function prototypes.
+.Pp
+When this header is included, the same file should not include
+.Pa out.h ,
+.Pa term.h ,
+or
+.Pa html.h .
+.El
Property changes on: vendor/mdocml/dist/mandoc_headers.3
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: vendor/mdocml/dist/mandocdb.c
===================================================================
--- vendor/mdocml/dist/mandocdb.c (revision 276215)
+++ vendor/mdocml/dist/mandocdb.c (revision 276216)
@@ -1,2477 +1,2542 @@
-/* $Id: mandocdb.c,v 1.171 2014/11/27 01:58:21 schwarze Exp $ */
+/* $Id: mandocdb.c,v 1.179 2014/12/09 07:29:42 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012 Kristaps Dzonsons
* Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#if HAVE_FTS
#include
#else
#include "compat_fts.h"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#if HAVE_OHASH
#include
#else
#include "compat_ohash.h"
#endif
#include
#include "mdoc.h"
#include "man.h"
#include "mandoc.h"
#include "mandoc_aux.h"
#include "manpath.h"
#include "mansearch.h"
extern int mansearch_keymax;
extern const char *const mansearch_keynames[];
#define SQL_EXEC(_v) \
if (SQLITE_OK != sqlite3_exec(db, (_v), NULL, NULL, NULL)) \
say("", "%s: %s", (_v), sqlite3_errmsg(db))
#define SQL_BIND_TEXT(_s, _i, _v) \
if (SQLITE_OK != sqlite3_bind_text \
((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \
say(mlink->file, "%s", sqlite3_errmsg(db))
#define SQL_BIND_INT(_s, _i, _v) \
if (SQLITE_OK != sqlite3_bind_int \
((_s), (_i)++, (_v))) \
say(mlink->file, "%s", sqlite3_errmsg(db))
#define SQL_BIND_INT64(_s, _i, _v) \
if (SQLITE_OK != sqlite3_bind_int64 \
((_s), (_i)++, (_v))) \
say(mlink->file, "%s", sqlite3_errmsg(db))
#define SQL_STEP(_s) \
if (SQLITE_DONE != sqlite3_step((_s))) \
say(mlink->file, "%s", sqlite3_errmsg(db))
enum op {
OP_DEFAULT = 0, /* new dbs from dir list or default config */
OP_CONFFILE, /* new databases from custom config file */
OP_UPDATE, /* delete/add entries in existing database */
OP_DELETE, /* delete entries from existing database */
OP_TEST /* change no databases, report potential problems */
};
struct str {
- char *rendered; /* key in UTF-8 or ASCII form */
const struct mpage *mpage; /* if set, the owning parse */
uint64_t mask; /* bitmask in sequence */
- char key[]; /* may contain escape sequences */
+ char key[]; /* rendered text */
};
struct inodev {
ino_t st_ino;
dev_t st_dev;
};
struct mpage {
struct inodev inodev; /* used for hashing routine */
int64_t pageid; /* pageid in mpages SQL table */
char *sec; /* section from file content */
char *arch; /* architecture from file content */
char *title; /* title from file content */
char *desc; /* description from file content */
struct mlink *mlinks; /* singly linked list */
int form; /* format from file content */
+ int name_head_done;
};
struct mlink {
char file[PATH_MAX]; /* filename rel. to manpath */
char *dsec; /* section from directory */
char *arch; /* architecture from directory */
char *name; /* name from file name (not empty) */
char *fsec; /* section from file name suffix */
struct mlink *next; /* singly linked list */
struct mpage *mpage; /* parent */
int dform; /* format from directory */
int fform; /* format from file name suffix */
int gzip; /* filename has a .gz suffix */
};
enum stmt {
STMT_DELETE_PAGE = 0, /* delete mpage */
STMT_INSERT_PAGE, /* insert mpage */
STMT_INSERT_LINK, /* insert mlink */
STMT_INSERT_NAME, /* insert name */
+ STMT_SELECT_NAME, /* retrieve existing name flags */
STMT_INSERT_KEY, /* insert parsed key */
STMT__MAX
};
-typedef int (*mdoc_fp)(struct mpage *, const struct mdoc_node *);
+typedef int (*mdoc_fp)(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
struct mdoc_handler {
mdoc_fp fp; /* optional handler */
uint64_t mask; /* set unless handler returns 0 */
};
static void dbclose(int);
-static void dbadd(struct mpage *, struct mchars *);
+static void dbadd(struct mpage *);
static void dbadd_mlink(const struct mlink *mlink);
static void dbadd_mlink_name(const struct mlink *mlink);
static int dbopen(int);
static void dbprune(void);
static void filescan(const char *);
static void *hash_alloc(size_t, void *);
static void hash_free(void *, void *);
static void *hash_calloc(size_t, size_t, void *);
static void mlink_add(struct mlink *, const struct stat *);
static void mlink_check(struct mpage *, struct mlink *);
static void mlink_free(struct mlink *);
static void mlinks_undupe(struct mpage *);
static void mpages_free(void);
-static void mpages_merge(struct mchars *, struct mparse *);
+static void mpages_merge(struct mparse *);
static void names_check(void);
static void parse_cat(struct mpage *, int);
-static void parse_man(struct mpage *, const struct man_node *);
-static void parse_mdoc(struct mpage *, const struct mdoc_node *);
-static int parse_mdoc_body(struct mpage *, const struct mdoc_node *);
-static int parse_mdoc_head(struct mpage *, const struct mdoc_node *);
-static int parse_mdoc_Fd(struct mpage *, const struct mdoc_node *);
-static int parse_mdoc_Fn(struct mpage *, const struct mdoc_node *);
-static int parse_mdoc_Nd(struct mpage *, const struct mdoc_node *);
-static int parse_mdoc_Nm(struct mpage *, const struct mdoc_node *);
-static int parse_mdoc_Sh(struct mpage *, const struct mdoc_node *);
-static int parse_mdoc_Xr(struct mpage *, const struct mdoc_node *);
+static void parse_man(struct mpage *, const struct man_meta *,
+ const struct man_node *);
+static void parse_mdoc(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
+static int parse_mdoc_body(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
+static int parse_mdoc_head(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
+static int parse_mdoc_Fd(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
+static void parse_mdoc_fname(struct mpage *, const struct mdoc_node *);
+static int parse_mdoc_Fn(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
+static int parse_mdoc_Fo(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
+static int parse_mdoc_Nd(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
+static int parse_mdoc_Nm(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
+static int parse_mdoc_Sh(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
+static int parse_mdoc_Xr(struct mpage *, const struct mdoc_meta *,
+ const struct mdoc_node *);
static void putkey(const struct mpage *, char *, uint64_t);
-static void putkeys(const struct mpage *,
- const char *, size_t, uint64_t);
+static void putkeys(const struct mpage *, char *, size_t, uint64_t);
static void putmdockey(const struct mpage *,
const struct mdoc_node *, uint64_t);
-static void render_key(struct mchars *, struct str *);
+static int render_string(char **, size_t *);
static void say(const char *, const char *, ...);
static int set_basedir(const char *, int);
static int treescan(void);
static size_t utf8(unsigned int, char [7]);
static char tempfilename[32];
static char *progname;
static int nodb; /* no database changes */
static int mparse_options; /* abort the parse early */
static int use_all; /* use all found files */
static int debug; /* print what we're doing */
static int warnings; /* warn about crap */
static int write_utf8; /* write UTF-8 output; else ASCII */
static int exitcode; /* to be returned by main */
static enum op op; /* operational mode */
static char basedir[PATH_MAX]; /* current base directory */
+static struct mchars *mchars; /* table of named characters */
static struct ohash mpages; /* table of distinct manual pages */
static struct ohash mlinks; /* table of directory entries */
static struct ohash names; /* table of all names */
static struct ohash strings; /* table of all strings */
static sqlite3 *db = NULL; /* current database */
static sqlite3_stmt *stmts[STMT__MAX]; /* current statements */
static uint64_t name_mask;
static const struct mdoc_handler mdocs[MDOC_MAX] = {
{ NULL, 0 }, /* Ap */
{ NULL, 0 }, /* Dd */
{ NULL, 0 }, /* Dt */
{ NULL, 0 }, /* Os */
{ parse_mdoc_Sh, TYPE_Sh }, /* Sh */
{ parse_mdoc_head, TYPE_Ss }, /* Ss */
{ NULL, 0 }, /* Pp */
{ NULL, 0 }, /* D1 */
{ NULL, 0 }, /* Dl */
{ NULL, 0 }, /* Bd */
{ NULL, 0 }, /* Ed */
{ NULL, 0 }, /* Bl */
{ NULL, 0 }, /* El */
{ NULL, 0 }, /* It */
{ NULL, 0 }, /* Ad */
{ NULL, TYPE_An }, /* An */
{ NULL, TYPE_Ar }, /* Ar */
{ NULL, TYPE_Cd }, /* Cd */
{ NULL, TYPE_Cm }, /* Cm */
{ NULL, TYPE_Dv }, /* Dv */
{ NULL, TYPE_Er }, /* Er */
{ NULL, TYPE_Ev }, /* Ev */
{ NULL, 0 }, /* Ex */
{ NULL, TYPE_Fa }, /* Fa */
{ parse_mdoc_Fd, 0 }, /* Fd */
{ NULL, TYPE_Fl }, /* Fl */
{ parse_mdoc_Fn, 0 }, /* Fn */
{ NULL, TYPE_Ft }, /* Ft */
{ NULL, TYPE_Ic }, /* Ic */
{ NULL, TYPE_In }, /* In */
{ NULL, TYPE_Li }, /* Li */
{ parse_mdoc_Nd, 0 }, /* Nd */
{ parse_mdoc_Nm, 0 }, /* Nm */
{ NULL, 0 }, /* Op */
{ NULL, 0 }, /* Ot */
{ NULL, TYPE_Pa }, /* Pa */
{ NULL, 0 }, /* Rv */
{ NULL, TYPE_St }, /* St */
{ NULL, TYPE_Va }, /* Va */
{ parse_mdoc_body, TYPE_Va }, /* Vt */
{ parse_mdoc_Xr, 0 }, /* Xr */
{ NULL, 0 }, /* %A */
{ NULL, 0 }, /* %B */
{ NULL, 0 }, /* %D */
{ NULL, 0 }, /* %I */
{ NULL, 0 }, /* %J */
{ NULL, 0 }, /* %N */
{ NULL, 0 }, /* %O */
{ NULL, 0 }, /* %P */
{ NULL, 0 }, /* %R */
{ NULL, 0 }, /* %T */
{ NULL, 0 }, /* %V */
{ NULL, 0 }, /* Ac */
{ NULL, 0 }, /* Ao */
{ NULL, 0 }, /* Aq */
{ NULL, TYPE_At }, /* At */
{ NULL, 0 }, /* Bc */
{ NULL, 0 }, /* Bf */
{ NULL, 0 }, /* Bo */
{ NULL, 0 }, /* Bq */
{ NULL, TYPE_Bsx }, /* Bsx */
{ NULL, TYPE_Bx }, /* Bx */
{ NULL, 0 }, /* Db */
{ NULL, 0 }, /* Dc */
{ NULL, 0 }, /* Do */
{ NULL, 0 }, /* Dq */
{ NULL, 0 }, /* Ec */
{ NULL, 0 }, /* Ef */
{ NULL, TYPE_Em }, /* Em */
{ NULL, 0 }, /* Eo */
{ NULL, TYPE_Fx }, /* Fx */
{ NULL, TYPE_Ms }, /* Ms */
{ NULL, 0 }, /* No */
{ NULL, 0 }, /* Ns */
{ NULL, TYPE_Nx }, /* Nx */
{ NULL, TYPE_Ox }, /* Ox */
{ NULL, 0 }, /* Pc */
{ NULL, 0 }, /* Pf */
{ NULL, 0 }, /* Po */
{ NULL, 0 }, /* Pq */
{ NULL, 0 }, /* Qc */
{ NULL, 0 }, /* Ql */
{ NULL, 0 }, /* Qo */
{ NULL, 0 }, /* Qq */
{ NULL, 0 }, /* Re */
{ NULL, 0 }, /* Rs */
{ NULL, 0 }, /* Sc */
{ NULL, 0 }, /* So */
{ NULL, 0 }, /* Sq */
{ NULL, 0 }, /* Sm */
{ NULL, 0 }, /* Sx */
{ NULL, TYPE_Sy }, /* Sy */
{ NULL, TYPE_Tn }, /* Tn */
{ NULL, 0 }, /* Ux */
{ NULL, 0 }, /* Xc */
{ NULL, 0 }, /* Xo */
- { parse_mdoc_head, 0 }, /* Fo */
+ { parse_mdoc_Fo, 0 }, /* Fo */
{ NULL, 0 }, /* Fc */
{ NULL, 0 }, /* Oo */
{ NULL, 0 }, /* Oc */
{ NULL, 0 }, /* Bk */
{ NULL, 0 }, /* Ek */
{ NULL, 0 }, /* Bt */
{ NULL, 0 }, /* Hf */
{ NULL, 0 }, /* Fr */
{ NULL, 0 }, /* Ud */
{ NULL, TYPE_Lb }, /* Lb */
{ NULL, 0 }, /* Lp */
{ NULL, TYPE_Lk }, /* Lk */
{ NULL, TYPE_Mt }, /* Mt */
{ NULL, 0 }, /* Brq */
{ NULL, 0 }, /* Bro */
{ NULL, 0 }, /* Brc */
{ NULL, 0 }, /* %C */
{ NULL, 0 }, /* Es */
{ NULL, 0 }, /* En */
{ NULL, TYPE_Dx }, /* Dx */
{ NULL, 0 }, /* %Q */
{ NULL, 0 }, /* br */
{ NULL, 0 }, /* sp */
{ NULL, 0 }, /* %U */
{ NULL, 0 }, /* Ta */
{ NULL, 0 }, /* ll */
};
int
-main(int argc, char *argv[])
+mandocdb(int argc, char *argv[])
{
int ch, i;
size_t j, sz;
const char *path_arg;
- struct mchars *mc;
struct manpaths dirs;
struct mparse *mp;
struct ohash_info mpages_info, mlinks_info;
memset(stmts, 0, STMT__MAX * sizeof(sqlite3_stmt *));
memset(&dirs, 0, sizeof(struct manpaths));
mpages_info.alloc = mlinks_info.alloc = hash_alloc;
mpages_info.calloc = mlinks_info.calloc = hash_calloc;
mpages_info.free = mlinks_info.free = hash_free;
mpages_info.key_offset = offsetof(struct mpage, inodev);
mlinks_info.key_offset = offsetof(struct mlink, file);
progname = strrchr(argv[0], '/');
if (progname == NULL)
progname = argv[0];
else
++progname;
/*
* We accept a few different invocations.
* The CHECKOP macro makes sure that invocation styles don't
* clobber each other.
*/
#define CHECKOP(_op, _ch) do \
if (OP_DEFAULT != (_op)) { \
fprintf(stderr, "%s: -%c: Conflicting option\n", \
progname, (_ch)); \
goto usage; \
} while (/*CONSTCOND*/0)
path_arg = NULL;
op = OP_DEFAULT;
while (-1 != (ch = getopt(argc, argv, "aC:Dd:npQT:tu:v")))
switch (ch) {
case 'a':
use_all = 1;
break;
case 'C':
CHECKOP(op, ch);
path_arg = optarg;
op = OP_CONFFILE;
break;
case 'D':
debug++;
break;
case 'd':
CHECKOP(op, ch);
path_arg = optarg;
op = OP_UPDATE;
break;
case 'n':
nodb = 1;
break;
case 'p':
warnings = 1;
break;
case 'Q':
mparse_options |= MPARSE_QUICK;
break;
case 'T':
if (strcmp(optarg, "utf8")) {
fprintf(stderr, "%s: -T%s: "
"Unsupported output format\n",
progname, optarg);
goto usage;
}
write_utf8 = 1;
break;
case 't':
CHECKOP(op, ch);
dup2(STDOUT_FILENO, STDERR_FILENO);
op = OP_TEST;
nodb = warnings = 1;
break;
case 'u':
CHECKOP(op, ch);
path_arg = optarg;
op = OP_DELETE;
break;
case 'v':
/* Compatibility with espie@'s makewhatis. */
break;
default:
goto usage;
}
argc -= optind;
argv += optind;
if (OP_CONFFILE == op && argc > 0) {
fprintf(stderr, "%s: -C: Too many arguments\n",
progname);
goto usage;
}
exitcode = (int)MANDOCLEVEL_OK;
- mc = mchars_alloc();
+ mchars = mchars_alloc();
mp = mparse_alloc(mparse_options, MANDOCLEVEL_FATAL, NULL,
- mc, NULL);
+ mchars, NULL);
ohash_init(&mpages, 6, &mpages_info);
ohash_init(&mlinks, 6, &mlinks_info);
if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) {
/*
* Most of these deal with a specific directory.
* Jump into that directory first.
*/
if (OP_TEST != op && 0 == set_basedir(path_arg, 1))
goto out;
if (dbopen(1)) {
/*
* The existing database is usable. Process
* all files specified on the command-line.
*/
use_all = 1;
for (i = 0; i < argc; i++)
filescan(argv[i]);
if (OP_TEST != op)
dbprune();
} else {
/*
* Database missing or corrupt.
* Recreate from scratch.
*/
exitcode = (int)MANDOCLEVEL_OK;
op = OP_DEFAULT;
if (0 == treescan())
goto out;
if (0 == dbopen(0))
goto out;
}
if (OP_DELETE != op)
- mpages_merge(mc, mp);
+ mpages_merge(mp);
dbclose(OP_DEFAULT == op ? 0 : 1);
} else {
/*
* If we have arguments, use them as our manpaths.
* If we don't, grok from manpath(1) or however else
* manpath_parse() wants to do it.
*/
if (argc > 0) {
dirs.paths = mandoc_reallocarray(NULL,
argc, sizeof(char *));
dirs.sz = (size_t)argc;
for (i = 0; i < argc; i++)
dirs.paths[i] = mandoc_strdup(argv[i]);
} else
manpath_parse(&dirs, path_arg, NULL, NULL);
if (0 == dirs.sz) {
exitcode = (int)MANDOCLEVEL_BADARG;
say("", "Empty manpath");
}
/*
* First scan the tree rooted at a base directory, then
* build a new database and finally move it into place.
* Ignore zero-length directories and strip trailing
* slashes.
*/
for (j = 0; j < dirs.sz; j++) {
sz = strlen(dirs.paths[j]);
if (sz && '/' == dirs.paths[j][sz - 1])
dirs.paths[j][--sz] = '\0';
if (0 == sz)
continue;
if (j) {
ohash_init(&mpages, 6, &mpages_info);
ohash_init(&mlinks, 6, &mlinks_info);
}
if (0 == set_basedir(dirs.paths[j], argc > 0))
continue;
if (0 == treescan())
continue;
if (0 == dbopen(0))
continue;
- mpages_merge(mc, mp);
+ mpages_merge(mp);
if (warnings && !nodb &&
! (MPARSE_QUICK & mparse_options))
names_check();
dbclose(0);
if (j + 1 < dirs.sz) {
mpages_free();
ohash_delete(&mpages);
ohash_delete(&mlinks);
}
}
}
out:
manpath_free(&dirs);
mparse_free(mp);
- mchars_free(mc);
+ mchars_free(mchars);
mpages_free();
ohash_delete(&mpages);
ohash_delete(&mlinks);
return(exitcode);
usage:
fprintf(stderr, "usage: %s [-aDnpQ] [-C file] [-Tutf8]\n"
" %s [-aDnpQ] [-Tutf8] dir ...\n"
" %s [-DnpQ] [-Tutf8] -d dir [file ...]\n"
" %s [-Dnp] -u dir [file ...]\n"
" %s [-Q] -t file ...\n",
progname, progname, progname,
progname, progname);
return((int)MANDOCLEVEL_BADARG);
}
/*
* Scan a directory tree rooted at "basedir" for manpages.
* We use fts(), scanning directory parts along the way for clues to our
* section and architecture.
*
* If use_all has been specified, grok all files.
* If not, sanitise paths to the following:
*
* [./]man*[/]/.
* or
* [./]cat[/]/.0
*
* TODO: accomodate for multi-language directories.
*/
static int
treescan(void)
{
char buf[PATH_MAX];
FTS *f;
FTSENT *ff;
struct mlink *mlink;
int dform, gzip;
char *dsec, *arch, *fsec, *cp;
const char *path;
const char *argv[2];
argv[0] = ".";
argv[1] = (char *)NULL;
f = fts_open((char * const *)argv,
FTS_PHYSICAL | FTS_NOCHDIR, NULL);
if (NULL == f) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "&fts_open");
return(0);
}
dsec = arch = NULL;
dform = FORM_NONE;
while (NULL != (ff = fts_read(f))) {
path = ff->fts_path + 2;
switch (ff->fts_info) {
/*
* Symbolic links require various sanity checks,
* then get handled just like regular files.
*/
case FTS_SL:
if (NULL == realpath(path, buf)) {
if (warnings)
say(path, "&realpath");
continue;
}
if (strstr(buf, basedir) != buf) {
if (warnings) say("",
"%s: outside base directory", buf);
continue;
}
/* Use logical inode to avoid mpages dupe. */
if (-1 == stat(path, ff->fts_statp)) {
if (warnings)
say(path, "&stat");
continue;
}
/* FALLTHROUGH */
/*
* If we're a regular file, add an mlink by using the
* stored directory data and handling the filename.
*/
case FTS_F:
if (0 == strcmp(path, MANDOC_DB))
continue;
if ( ! use_all && ff->fts_level < 2) {
if (warnings)
say(path, "Extraneous file");
continue;
}
gzip = 0;
fsec = NULL;
while (NULL == fsec) {
fsec = strrchr(ff->fts_name, '.');
if (NULL == fsec || strcmp(fsec+1, "gz"))
break;
gzip = 1;
*fsec = '\0';
fsec = NULL;
}
if (NULL == fsec) {
if ( ! use_all) {
if (warnings)
say(path,
"No filename suffix");
continue;
}
} else if (0 == strcmp(++fsec, "html")) {
if (warnings)
say(path, "Skip html");
continue;
} else if (0 == strcmp(fsec, "ps")) {
if (warnings)
say(path, "Skip ps");
continue;
} else if (0 == strcmp(fsec, "pdf")) {
if (warnings)
say(path, "Skip pdf");
continue;
} else if ( ! use_all &&
((FORM_SRC == dform && strcmp(fsec, dsec)) ||
(FORM_CAT == dform && strcmp(fsec, "0")))) {
if (warnings)
say(path, "Wrong filename suffix");
continue;
} else
fsec[-1] = '\0';
mlink = mandoc_calloc(1, sizeof(struct mlink));
if (strlcpy(mlink->file, path,
sizeof(mlink->file)) >=
sizeof(mlink->file)) {
say(path, "Filename too long");
free(mlink);
continue;
}
mlink->dform = dform;
mlink->dsec = dsec;
mlink->arch = arch;
mlink->name = ff->fts_name;
mlink->fsec = fsec;
mlink->gzip = gzip;
mlink_add(mlink, ff->fts_statp);
continue;
case FTS_D:
/* FALLTHROUGH */
case FTS_DP:
break;
default:
if (warnings)
say(path, "Not a regular file");
continue;
}
switch (ff->fts_level) {
case 0:
/* Ignore the root directory. */
break;
case 1:
/*
* This might contain manX/ or catX/.
* Try to infer this from the name.
* If we're not in use_all, enforce it.
*/
cp = ff->fts_name;
if (FTS_DP == ff->fts_info)
break;
if (0 == strncmp(cp, "man", 3)) {
dform = FORM_SRC;
dsec = cp + 3;
} else if (0 == strncmp(cp, "cat", 3)) {
dform = FORM_CAT;
dsec = cp + 3;
} else {
dform = FORM_NONE;
dsec = NULL;
}
if (NULL != dsec || use_all)
break;
if (warnings)
say(path, "Unknown directory part");
fts_set(f, ff, FTS_SKIP);
break;
case 2:
/*
* Possibly our architecture.
* If we're descending, keep tabs on it.
*/
if (FTS_DP != ff->fts_info && NULL != dsec)
arch = ff->fts_name;
else
arch = NULL;
break;
default:
if (FTS_DP == ff->fts_info || use_all)
break;
if (warnings)
say(path, "Extraneous directory part");
fts_set(f, ff, FTS_SKIP);
break;
}
}
fts_close(f);
return(1);
}
/*
* Add a file to the mlinks table.
* Do not verify that it's a "valid" looking manpage (we'll do that
* later).
*
* Try to infer the manual section, architecture, and page name from the
* path, assuming it looks like
*
* [./]man*[/]/.
* or
* [./]cat[/]/.0
*
* See treescan() for the fts(3) version of this.
*/
static void
filescan(const char *file)
{
char buf[PATH_MAX];
struct stat st;
struct mlink *mlink;
char *p, *start;
assert(use_all);
if (0 == strncmp(file, "./", 2))
file += 2;
/*
* We have to do lstat(2) before realpath(3) loses
* the information whether this is a symbolic link.
* We need to know that because for symbolic links,
* we want to use the orginal file name, while for
* regular files, we want to use the real path.
*/
if (-1 == lstat(file, &st)) {
exitcode = (int)MANDOCLEVEL_BADARG;
say(file, "&lstat");
return;
} else if (0 == ((S_IFREG | S_IFLNK) & st.st_mode)) {
exitcode = (int)MANDOCLEVEL_BADARG;
say(file, "Not a regular file");
return;
}
/*
* We have to resolve the file name to the real path
* in any case for the base directory check.
*/
if (NULL == realpath(file, buf)) {
exitcode = (int)MANDOCLEVEL_BADARG;
say(file, "&realpath");
return;
}
if (OP_TEST == op)
start = buf;
else if (strstr(buf, basedir) == buf)
start = buf + strlen(basedir);
else {
exitcode = (int)MANDOCLEVEL_BADARG;
say("", "%s: outside base directory", buf);
return;
}
/*
* Now we are sure the file is inside our tree.
* If it is a symbolic link, ignore the real path
* and use the original name.
* This implies passing stuff like "cat1/../man1/foo.1"
* on the command line won't work. So don't do that.
* Note the stat(2) can still fail if the link target
* doesn't exist.
*/
if (S_IFLNK & st.st_mode) {
if (-1 == stat(buf, &st)) {
exitcode = (int)MANDOCLEVEL_BADARG;
say(file, "&stat");
return;
}
if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) {
say(file, "Filename too long");
return;
}
start = buf;
if (OP_TEST != op && strstr(buf, basedir) == buf)
start += strlen(basedir);
}
mlink = mandoc_calloc(1, sizeof(struct mlink));
mlink->dform = FORM_NONE;
if (strlcpy(mlink->file, start, sizeof(mlink->file)) >=
sizeof(mlink->file)) {
say(start, "Filename too long");
return;
}
/*
* First try to guess our directory structure.
* If we find a separator, try to look for man* or cat*.
* If we find one of these and what's underneath is a directory,
* assume it's an architecture.
*/
if (NULL != (p = strchr(start, '/'))) {
*p++ = '\0';
if (0 == strncmp(start, "man", 3)) {
mlink->dform = FORM_SRC;
mlink->dsec = start + 3;
} else if (0 == strncmp(start, "cat", 3)) {
mlink->dform = FORM_CAT;
mlink->dsec = start + 3;
}
start = p;
if (NULL != mlink->dsec && NULL != (p = strchr(start, '/'))) {
*p++ = '\0';
mlink->arch = start;
start = p;
}
}
/*
* Now check the file suffix.
* Suffix of `.0' indicates a catpage, `.1-9' is a manpage.
*/
p = strrchr(start, '\0');
while (p-- > start && '/' != *p && '.' != *p)
/* Loop. */ ;
if ('.' == *p) {
*p++ = '\0';
mlink->fsec = p;
}
/*
* Now try to parse the name.
* Use the filename portion of the path.
*/
mlink->name = start;
if (NULL != (p = strrchr(start, '/'))) {
mlink->name = p + 1;
*p = '\0';
}
mlink_add(mlink, &st);
}
static void
mlink_add(struct mlink *mlink, const struct stat *st)
{
struct inodev inodev;
struct mpage *mpage;
unsigned int slot;
assert(NULL != mlink->file);
mlink->dsec = mandoc_strdup(mlink->dsec ? mlink->dsec : "");
mlink->arch = mandoc_strdup(mlink->arch ? mlink->arch : "");
mlink->name = mandoc_strdup(mlink->name ? mlink->name : "");
mlink->fsec = mandoc_strdup(mlink->fsec ? mlink->fsec : "");
if ('0' == *mlink->fsec) {
free(mlink->fsec);
mlink->fsec = mandoc_strdup(mlink->dsec);
mlink->fform = FORM_CAT;
} else if ('1' <= *mlink->fsec && '9' >= *mlink->fsec)
mlink->fform = FORM_SRC;
else
mlink->fform = FORM_NONE;
slot = ohash_qlookup(&mlinks, mlink->file);
assert(NULL == ohash_find(&mlinks, slot));
ohash_insert(&mlinks, slot, mlink);
inodev.st_ino = st->st_ino;
inodev.st_dev = st->st_dev;
slot = ohash_lookup_memory(&mpages, (char *)&inodev,
sizeof(struct inodev), inodev.st_ino);
mpage = ohash_find(&mpages, slot);
if (NULL == mpage) {
mpage = mandoc_calloc(1, sizeof(struct mpage));
mpage->inodev.st_ino = inodev.st_ino;
mpage->inodev.st_dev = inodev.st_dev;
ohash_insert(&mpages, slot, mpage);
} else
mlink->next = mpage->mlinks;
mpage->mlinks = mlink;
mlink->mpage = mpage;
}
static void
mlink_free(struct mlink *mlink)
{
free(mlink->dsec);
free(mlink->arch);
free(mlink->name);
free(mlink->fsec);
free(mlink);
}
static void
mpages_free(void)
{
struct mpage *mpage;
struct mlink *mlink;
unsigned int slot;
mpage = ohash_first(&mpages, &slot);
while (NULL != mpage) {
while (NULL != (mlink = mpage->mlinks)) {
mpage->mlinks = mlink->next;
mlink_free(mlink);
}
free(mpage->sec);
free(mpage->arch);
free(mpage->title);
free(mpage->desc);
free(mpage);
mpage = ohash_next(&mpages, &slot);
}
}
/*
* For each mlink to the mpage, check whether the path looks like
* it is formatted, and if it does, check whether a source manual
* exists by the same name, ignoring the suffix.
* If both conditions hold, drop the mlink.
*/
static void
mlinks_undupe(struct mpage *mpage)
{
char buf[PATH_MAX];
struct mlink **prev;
struct mlink *mlink;
char *bufp;
mpage->form = FORM_CAT;
prev = &mpage->mlinks;
while (NULL != (mlink = *prev)) {
if (FORM_CAT != mlink->dform) {
mpage->form = FORM_NONE;
goto nextlink;
}
(void)strlcpy(buf, mlink->file, sizeof(buf));
bufp = strstr(buf, "cat");
assert(NULL != bufp);
memcpy(bufp, "man", 3);
if (NULL != (bufp = strrchr(buf, '.')))
*++bufp = '\0';
(void)strlcat(buf, mlink->dsec, sizeof(buf));
if (NULL == ohash_find(&mlinks,
ohash_qlookup(&mlinks, buf)))
goto nextlink;
if (warnings)
say(mlink->file, "Man source exists: %s", buf);
if (use_all)
goto nextlink;
*prev = mlink->next;
mlink_free(mlink);
continue;
nextlink:
prev = &(*prev)->next;
}
}
static void
mlink_check(struct mpage *mpage, struct mlink *mlink)
{
struct str *str;
unsigned int slot;
/*
* Check whether the manual section given in a file
* agrees with the directory where the file is located.
* Some manuals have suffixes like (3p) on their
* section number either inside the file or in the
* directory name, some are linked into more than one
* section, like encrypt(1) = makekey(8).
*/
if (FORM_SRC == mpage->form &&
strcasecmp(mpage->sec, mlink->dsec))
say(mlink->file, "Section \"%s\" manual in %s directory",
mpage->sec, mlink->dsec);
/*
* Manual page directories exist for each kernel
* architecture as returned by machine(1).
* However, many manuals only depend on the
* application architecture as returned by arch(1).
* For example, some (2/ARM) manuals are shared
* across the "armish" and "zaurus" kernel
* architectures.
* A few manuals are even shared across completely
* different architectures, for example fdformat(1)
* on amd64, i386, sparc, and sparc64.
*/
if (strcasecmp(mpage->arch, mlink->arch))
say(mlink->file, "Architecture \"%s\" manual in "
"\"%s\" directory", mpage->arch, mlink->arch);
/*
* XXX
* parse_cat() doesn't set NAME_TITLE yet.
*/
if (FORM_CAT == mpage->form)
return;
/*
* Check whether this mlink
* appears as a name in the NAME section.
*/
slot = ohash_qlookup(&names, mlink->name);
str = ohash_find(&names, slot);
assert(NULL != str);
if ( ! (NAME_TITLE & str->mask))
say(mlink->file, "Name missing in NAME section");
}
/*
* Run through the files in the global vector "mpages"
* and add them to the database specified in "basedir".
*
* This handles the parsing scheme itself, using the cues of directory
* and filename to determine whether the file is parsable or not.
*/
static void
-mpages_merge(struct mchars *mc, struct mparse *mp)
+mpages_merge(struct mparse *mp)
{
char any[] = "any";
struct ohash_info str_info;
struct mpage *mpage, *mpage_dest;
struct mlink *mlink, *mlink_dest;
struct mdoc *mdoc;
struct man *man;
char *sodest;
char *cp;
int fd;
unsigned int pslot;
enum mandoclevel lvl;
str_info.alloc = hash_alloc;
str_info.calloc = hash_calloc;
str_info.free = hash_free;
str_info.key_offset = offsetof(struct str, key);
if ( ! nodb)
SQL_EXEC("BEGIN TRANSACTION");
mpage = ohash_first(&mpages, &pslot);
while (mpage != NULL) {
mlinks_undupe(mpage);
if (mpage->mlinks == NULL) {
mpage = ohash_next(&mpages, &pslot);
continue;
}
name_mask = NAME_MASK;
ohash_init(&names, 4, &str_info);
ohash_init(&strings, 6, &str_info);
mparse_reset(mp);
mdoc = NULL;
man = NULL;
sodest = NULL;
mparse_open(mp, &fd, mpage->mlinks->file);
if (fd == -1) {
say(mpage->mlinks->file, "&open");
goto nextpage;
}
/*
* Try interpreting the file as mdoc(7) or man(7)
* source code, unless it is already known to be
* formatted. Fall back to formatted mode.
*/
if (mpage->mlinks->dform != FORM_CAT ||
mpage->mlinks->fform != FORM_CAT) {
lvl = mparse_readfd(mp, fd, mpage->mlinks->file);
if (lvl < MANDOCLEVEL_FATAL)
mparse_result(mp, &mdoc, &man, &sodest);
}
if (sodest != NULL) {
mlink_dest = ohash_find(&mlinks,
ohash_qlookup(&mlinks, sodest));
if (mlink_dest == NULL) {
mandoc_asprintf(&cp, "%s.gz", sodest);
mlink_dest = ohash_find(&mlinks,
ohash_qlookup(&mlinks, cp));
free(cp);
}
if (mlink_dest != NULL) {
/* The .so target exists. */
mpage_dest = mlink_dest->mpage;
mlink = mpage->mlinks;
while (1) {
mlink->mpage = mpage_dest;
/*
* If the target was already
* processed, add the links
* to the database now.
* Otherwise, this will
* happen when we come
* to the target.
*/
if (mpage_dest->pageid)
dbadd_mlink_name(mlink);
if (mlink->next == NULL)
break;
mlink = mlink->next;
}
/* Move all links to the target. */
mlink->next = mlink_dest->next;
mlink_dest->next = mpage->mlinks;
mpage->mlinks = NULL;
}
goto nextpage;
} else if (mdoc != NULL) {
mpage->form = FORM_SRC;
mpage->sec = mdoc_meta(mdoc)->msec;
mpage->sec = mandoc_strdup(
mpage->sec == NULL ? "" : mpage->sec);
mpage->arch = mdoc_meta(mdoc)->arch;
mpage->arch = mandoc_strdup(
mpage->arch == NULL ? "" : mpage->arch);
mpage->title =
mandoc_strdup(mdoc_meta(mdoc)->title);
} else if (man != NULL) {
mpage->form = FORM_SRC;
mpage->sec =
mandoc_strdup(man_meta(man)->msec);
mpage->arch =
mandoc_strdup(mpage->mlinks->arch);
mpage->title =
mandoc_strdup(man_meta(man)->title);
} else {
mpage->form = FORM_CAT;
mpage->sec =
mandoc_strdup(mpage->mlinks->dsec);
mpage->arch =
mandoc_strdup(mpage->mlinks->arch);
mpage->title =
mandoc_strdup(mpage->mlinks->name);
}
putkey(mpage, mpage->sec, TYPE_sec);
if (*mpage->arch != '\0')
putkey(mpage, mpage->arch, TYPE_arch);
for (mlink = mpage->mlinks; mlink; mlink = mlink->next) {
if ('\0' != *mlink->dsec)
putkey(mpage, mlink->dsec, TYPE_sec);
if ('\0' != *mlink->fsec)
putkey(mpage, mlink->fsec, TYPE_sec);
putkey(mpage, '\0' == *mlink->arch ?
any : mlink->arch, TYPE_arch);
putkey(mpage, mlink->name, NAME_FILE);
}
- assert(NULL == mpage->desc);
- if (NULL != mdoc) {
- if (NULL != (cp = mdoc_meta(mdoc)->name))
- putkey(mpage, cp, NAME_HEAD);
- parse_mdoc(mpage, mdoc_node(mdoc));
- } else if (NULL != man)
- parse_man(mpage, man_node(man));
+ assert(mpage->desc == NULL);
+ if (mdoc != NULL)
+ parse_mdoc(mpage, mdoc_meta(mdoc), mdoc_node(mdoc));
+ else if (man != NULL)
+ parse_man(mpage, man_meta(man), man_node(man));
else
parse_cat(mpage, fd);
- if (NULL == mpage->desc)
+ if (mpage->desc == NULL)
mpage->desc = mandoc_strdup(mpage->mlinks->name);
if (warnings && !use_all)
for (mlink = mpage->mlinks; mlink;
mlink = mlink->next)
mlink_check(mpage, mlink);
- dbadd(mpage, mc);
+ dbadd(mpage);
nextpage:
if (mparse_wait(mp) != MANDOCLEVEL_OK) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say(mpage->mlinks->file, "&wait gunzip");
}
ohash_delete(&strings);
ohash_delete(&names);
mpage = ohash_next(&mpages, &pslot);
}
if (0 == nodb)
SQL_EXEC("END TRANSACTION");
}
static void
names_check(void)
{
sqlite3_stmt *stmt;
const char *name, *sec, *arch, *key;
int irc;
sqlite3_prepare_v2(db,
"SELECT name, sec, arch, key FROM ("
"SELECT name AS key, pageid FROM names "
"WHERE bits & ? AND NOT EXISTS ("
"SELECT pageid FROM mlinks "
"WHERE mlinks.pageid == names.pageid "
"AND mlinks.name == names.name"
")"
") JOIN ("
"SELECT sec, arch, name, pageid FROM mlinks "
"GROUP BY pageid"
") USING (pageid);",
-1, &stmt, NULL);
if (SQLITE_OK != sqlite3_bind_int64(stmt, 1, NAME_TITLE))
say("", "%s", sqlite3_errmsg(db));
while (SQLITE_ROW == (irc = sqlite3_step(stmt))) {
name = (const char *)sqlite3_column_text(stmt, 0);
sec = (const char *)sqlite3_column_text(stmt, 1);
arch = (const char *)sqlite3_column_text(stmt, 2);
key = (const char *)sqlite3_column_text(stmt, 3);
say("", "%s(%s%s%s) lacks mlink \"%s\"", name, sec,
'\0' == *arch ? "" : "/",
'\0' == *arch ? "" : arch, key);
}
sqlite3_finalize(stmt);
}
static void
parse_cat(struct mpage *mpage, int fd)
{
FILE *stream;
char *line, *p, *title;
size_t len, plen, titlesz;
stream = (-1 == fd) ?
fopen(mpage->mlinks->file, "r") :
fdopen(fd, "r");
if (NULL == stream) {
if (-1 != fd)
close(fd);
if (warnings)
say(mpage->mlinks->file, "&fopen");
return;
}
/* Skip to first blank line. */
while (NULL != (line = fgetln(stream, &len)))
if ('\n' == *line)
break;
/*
* Assume the first line that is not indented
* is the first section header. Skip to it.
*/
while (NULL != (line = fgetln(stream, &len)))
if ('\n' != *line && ' ' != *line)
break;
/*
* Read up until the next section into a buffer.
* Strip the leading and trailing newline from each read line,
* appending a trailing space.
* Ignore empty (whitespace-only) lines.
*/
titlesz = 0;
title = NULL;
while (NULL != (line = fgetln(stream, &len))) {
if (' ' != *line || '\n' != line[len - 1])
break;
while (len > 0 && isspace((unsigned char)*line)) {
line++;
len--;
}
if (1 == len)
continue;
title = mandoc_realloc(title, titlesz + len);
memcpy(title + titlesz, line, len);
titlesz += len;
title[titlesz - 1] = ' ';
}
/*
* If no page content can be found, or the input line
* is already the next section header, or there is no
* trailing newline, reuse the page title as the page
* description.
*/
if (NULL == title || '\0' == *title) {
if (warnings)
say(mpage->mlinks->file,
"Cannot find NAME section");
fclose(stream);
free(title);
return;
}
title = mandoc_realloc(title, titlesz + 1);
title[titlesz] = '\0';
/*
* Skip to the first dash.
* Use the remaining line as the description (no more than 70
* bytes).
*/
if (NULL != (p = strstr(title, "- "))) {
for (p += 2; ' ' == *p || '\b' == *p; p++)
/* Skip to next word. */ ;
} else {
if (warnings)
say(mpage->mlinks->file,
"No dash in title line");
p = title;
}
plen = strlen(p);
/* Strip backspace-encoding from line. */
while (NULL != (line = memchr(p, '\b', plen))) {
len = line - p;
if (0 == len) {
memmove(line, line + 1, plen--);
continue;
}
memmove(line - 1, line + 1, plen - len);
plen -= 2;
}
mpage->desc = mandoc_strdup(p);
fclose(stream);
free(title);
}
/*
* Put a type/word pair into the word database for this particular file.
*/
static void
putkey(const struct mpage *mpage, char *value, uint64_t type)
{
char *cp;
assert(NULL != value);
if (TYPE_arch == type)
for (cp = value; *cp; cp++)
if (isupper((unsigned char)*cp))
*cp = _tolower((unsigned char)*cp);
putkeys(mpage, value, strlen(value), type);
}
/*
* Grok all nodes at or below a certain mdoc node into putkey().
*/
static void
putmdockey(const struct mpage *mpage,
const struct mdoc_node *n, uint64_t m)
{
for ( ; NULL != n; n = n->next) {
if (NULL != n->child)
putmdockey(mpage, n->child, m);
if (MDOC_TEXT == n->type)
putkey(mpage, n->string, m);
}
}
static void
-parse_man(struct mpage *mpage, const struct man_node *n)
+parse_man(struct mpage *mpage, const struct man_meta *meta,
+ const struct man_node *n)
{
const struct man_node *head, *body;
char *start, *title;
char byte;
size_t sz;
if (NULL == n)
return;
/*
* We're only searching for one thing: the first text child in
* the BODY of a NAME section. Since we don't keep track of
* sections in -man, run some hoops to find out whether we're in
* the correct section or not.
*/
if (MAN_BODY == n->type && MAN_SH == n->tok) {
body = n;
assert(body->parent);
if (NULL != (head = body->parent->head) &&
1 == head->nchild &&
NULL != (head = (head->child)) &&
MAN_TEXT == head->type &&
0 == strcmp(head->string, "NAME") &&
NULL != body->child) {
/*
* Suck the entire NAME section into memory.
* Yes, we might run away.
* But too many manuals have big, spread-out
* NAME sections over many lines.
*/
title = NULL;
man_deroff(&title, body);
if (NULL == title)
return;
/*
* Go through a special heuristic dance here.
* Conventionally, one or more manual names are
* comma-specified prior to a whitespace, then a
* dash, then a description. Try to puzzle out
* the name parts here.
*/
start = title;
for ( ;; ) {
sz = strcspn(start, " ,");
if ('\0' == start[sz])
break;
byte = start[sz];
start[sz] = '\0';
/*
* Assume a stray trailing comma in the
* name list if a name begins with a dash.
*/
if ('-' == start[0] ||
('\\' == start[0] && '-' == start[1]))
break;
putkey(mpage, start, NAME_TITLE);
+ if ( ! (mpage->name_head_done ||
+ strcasecmp(start, meta->title))) {
+ putkey(mpage, start, NAME_HEAD);
+ mpage->name_head_done = 1;
+ }
if (' ' == byte) {
start += sz + 1;
break;
}
assert(',' == byte);
start += sz + 1;
while (' ' == *start)
start++;
}
if (start == title) {
putkey(mpage, start, NAME_TITLE);
+ if ( ! (mpage->name_head_done ||
+ strcasecmp(start, meta->title))) {
+ putkey(mpage, start, NAME_HEAD);
+ mpage->name_head_done = 1;
+ }
free(title);
return;
}
while (isspace((unsigned char)*start))
start++;
if (0 == strncmp(start, "-", 1))
start += 1;
else if (0 == strncmp(start, "\\-\\-", 4))
start += 4;
else if (0 == strncmp(start, "\\-", 2))
start += 2;
else if (0 == strncmp(start, "\\(en", 4))
start += 4;
else if (0 == strncmp(start, "\\(em", 4))
start += 4;
while (' ' == *start)
start++;
mpage->desc = mandoc_strdup(start);
free(title);
return;
}
}
for (n = n->child; n; n = n->next) {
if (NULL != mpage->desc)
break;
- parse_man(mpage, n);
+ parse_man(mpage, meta, n);
}
}
static void
-parse_mdoc(struct mpage *mpage, const struct mdoc_node *n)
+parse_mdoc(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
{
assert(NULL != n);
for (n = n->child; NULL != n; n = n->next) {
switch (n->type) {
case MDOC_ELEM:
/* FALLTHROUGH */
case MDOC_BLOCK:
/* FALLTHROUGH */
case MDOC_HEAD:
/* FALLTHROUGH */
case MDOC_BODY:
/* FALLTHROUGH */
case MDOC_TAIL:
if (NULL != mdocs[n->tok].fp)
- if (0 == (*mdocs[n->tok].fp)(mpage, n))
+ if (0 == (*mdocs[n->tok].fp)(mpage, meta, n))
break;
if (mdocs[n->tok].mask)
putmdockey(mpage, n->child,
mdocs[n->tok].mask);
break;
default:
assert(MDOC_ROOT != n->type);
continue;
}
if (NULL != n->child)
- parse_mdoc(mpage, n);
+ parse_mdoc(mpage, meta, n);
}
}
static int
-parse_mdoc_Fd(struct mpage *mpage, const struct mdoc_node *n)
+parse_mdoc_Fd(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
{
- const char *start, *end;
+ char *start, *end;
size_t sz;
if (SEC_SYNOPSIS != n->sec ||
NULL == (n = n->child) ||
MDOC_TEXT != n->type)
return(0);
/*
* Only consider those `Fd' macro fields that begin with an
* "inclusion" token (versus, e.g., #define).
*/
if (strcmp("#include", n->string))
return(0);
if (NULL == (n = n->next) || MDOC_TEXT != n->type)
return(0);
/*
* Strip away the enclosing angle brackets and make sure we're
* not zero-length.
*/
start = n->string;
if ('<' == *start || '"' == *start)
start++;
if (0 == (sz = strlen(start)))
return(0);
end = &start[(int)sz - 1];
if ('>' == *end || '"' == *end)
end--;
if (end > start)
putkeys(mpage, start, end - start + 1, TYPE_In);
return(0);
}
-static int
-parse_mdoc_Fn(struct mpage *mpage, const struct mdoc_node *n)
+static void
+parse_mdoc_fname(struct mpage *mpage, const struct mdoc_node *n)
{
char *cp;
+ size_t sz;
- if (NULL == (n = n->child) || MDOC_TEXT != n->type)
- return(0);
+ if (n->type != MDOC_TEXT)
+ return;
- /*
- * Parse: .Fn "struct type *name" "char *arg".
- * First strip away pointer symbol.
- * Then store the function name, then type.
- * Finally, store the arguments.
- */
+ /* Skip function pointer punctuation. */
- if (NULL == (cp = strrchr(n->string, ' ')))
- cp = n->string;
-
- while ('*' == *cp)
+ cp = n->string;
+ while (*cp == '(' || *cp == '*')
cp++;
+ sz = strcspn(cp, "()");
- putkey(mpage, cp, TYPE_Fn);
+ putkeys(mpage, cp, sz, TYPE_Fn);
+ if (n->sec == SEC_SYNOPSIS)
+ putkeys(mpage, cp, sz, NAME_SYN);
+}
- if (n->string < cp)
- putkeys(mpage, n->string, cp - n->string, TYPE_Ft);
+static int
+parse_mdoc_Fn(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
+{
- for (n = n->next; NULL != n; n = n->next)
- if (MDOC_TEXT == n->type)
+ if (n->child == NULL)
+ return(0);
+
+ parse_mdoc_fname(mpage, n->child);
+
+ for (n = n->child->next; n != NULL; n = n->next)
+ if (n->type == MDOC_TEXT)
putkey(mpage, n->string, TYPE_Fa);
return(0);
}
static int
-parse_mdoc_Xr(struct mpage *mpage, const struct mdoc_node *n)
+parse_mdoc_Fo(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
{
+
+ if (n->type != MDOC_HEAD)
+ return(1);
+
+ if (n->child != NULL)
+ parse_mdoc_fname(mpage, n->child);
+
+ return(0);
+}
+
+static int
+parse_mdoc_Xr(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
+{
char *cp;
if (NULL == (n = n->child))
return(0);
if (NULL == n->next) {
putkey(mpage, n->string, TYPE_Xr);
return(0);
}
mandoc_asprintf(&cp, "%s(%s)", n->string, n->next->string);
putkey(mpage, cp, TYPE_Xr);
free(cp);
return(0);
}
static int
-parse_mdoc_Nd(struct mpage *mpage, const struct mdoc_node *n)
+parse_mdoc_Nd(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
{
if (MDOC_BODY == n->type)
mdoc_deroff(&mpage->desc, n);
return(0);
}
static int
-parse_mdoc_Nm(struct mpage *mpage, const struct mdoc_node *n)
+parse_mdoc_Nm(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
{
if (SEC_NAME == n->sec)
putmdockey(mpage, n->child, NAME_TITLE);
- else if (SEC_SYNOPSIS == n->sec && MDOC_HEAD == n->type)
- putmdockey(mpage, n->child, NAME_SYN);
+ else if (SEC_SYNOPSIS == n->sec && MDOC_HEAD == n->type) {
+ if (n->child == NULL)
+ putkey(mpage, meta->name, NAME_SYN);
+ else
+ putmdockey(mpage, n->child, NAME_SYN);
+ }
+ if ( ! (mpage->name_head_done ||
+ n->child == NULL || n->child->string == NULL ||
+ strcasecmp(n->child->string, meta->title))) {
+ putkey(mpage, n->child->string, NAME_HEAD);
+ mpage->name_head_done = 1;
+ }
return(0);
}
static int
-parse_mdoc_Sh(struct mpage *mpage, const struct mdoc_node *n)
+parse_mdoc_Sh(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
{
return(SEC_CUSTOM == n->sec && MDOC_HEAD == n->type);
}
static int
-parse_mdoc_head(struct mpage *mpage, const struct mdoc_node *n)
+parse_mdoc_head(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
{
return(MDOC_HEAD == n->type);
}
static int
-parse_mdoc_body(struct mpage *mpage, const struct mdoc_node *n)
+parse_mdoc_body(struct mpage *mpage, const struct mdoc_meta *meta,
+ const struct mdoc_node *n)
{
return(MDOC_BODY == n->type);
}
/*
* Add a string to the hash table for the current manual.
* Each string has a bitmask telling which macros it belongs to.
* When we finish the manual, we'll dump the table.
*/
static void
-putkeys(const struct mpage *mpage,
- const char *cp, size_t sz, uint64_t v)
+putkeys(const struct mpage *mpage, char *cp, size_t sz, uint64_t v)
{
struct ohash *htab;
struct str *s;
const char *end;
unsigned int slot;
- int i;
+ int i, mustfree;
if (0 == sz)
return;
+ mustfree = render_string(&cp, &sz);
+
if (TYPE_Nm & v) {
htab = &names;
v &= name_mask;
if (v & NAME_FIRST)
name_mask &= ~NAME_FIRST;
if (debug > 1)
say(mpage->mlinks->file,
- "Adding name %*s", sz, cp);
+ "Adding name %*s, bits=%d", sz, cp, v);
} else {
htab = &strings;
if (debug > 1)
for (i = 0; i < mansearch_keymax; i++)
if ((uint64_t)1 << i & v)
say(mpage->mlinks->file,
"Adding key %s=%*s",
mansearch_keynames[i], sz, cp);
}
end = cp + sz;
slot = ohash_qlookupi(htab, cp, &end);
s = ohash_find(htab, slot);
if (NULL != s && mpage == s->mpage) {
s->mask |= v;
return;
} else if (NULL == s) {
s = mandoc_calloc(1, sizeof(struct str) + sz + 1);
memcpy(s->key, cp, sz);
ohash_insert(htab, slot, s);
}
s->mpage = mpage;
s->mask = v;
+
+ if (mustfree)
+ free(cp);
}
/*
* Take a Unicode codepoint and produce its UTF-8 encoding.
* This isn't the best way to do this, but it works.
* The magic numbers are from the UTF-8 packaging.
* They're not as scary as they seem: read the UTF-8 spec for details.
*/
static size_t
utf8(unsigned int cp, char out[7])
{
size_t rc;
rc = 0;
if (cp <= 0x0000007F) {
rc = 1;
out[0] = (char)cp;
} else if (cp <= 0x000007FF) {
rc = 2;
out[0] = (cp >> 6 & 31) | 192;
out[1] = (cp & 63) | 128;
} else if (cp <= 0x0000FFFF) {
rc = 3;
out[0] = (cp >> 12 & 15) | 224;
out[1] = (cp >> 6 & 63) | 128;
out[2] = (cp & 63) | 128;
} else if (cp <= 0x001FFFFF) {
rc = 4;
out[0] = (cp >> 18 & 7) | 240;
out[1] = (cp >> 12 & 63) | 128;
out[2] = (cp >> 6 & 63) | 128;
out[3] = (cp & 63) | 128;
} else if (cp <= 0x03FFFFFF) {
rc = 5;
out[0] = (cp >> 24 & 3) | 248;
out[1] = (cp >> 18 & 63) | 128;
out[2] = (cp >> 12 & 63) | 128;
out[3] = (cp >> 6 & 63) | 128;
out[4] = (cp & 63) | 128;
} else if (cp <= 0x7FFFFFFF) {
rc = 6;
out[0] = (cp >> 30 & 1) | 252;
out[1] = (cp >> 24 & 63) | 128;
out[2] = (cp >> 18 & 63) | 128;
out[3] = (cp >> 12 & 63) | 128;
out[4] = (cp >> 6 & 63) | 128;
out[5] = (cp & 63) | 128;
} else
return(0);
out[rc] = '\0';
return(rc);
}
/*
- * Store the rendered version of a key, or alias the pointer
- * if the key contains no escape sequences.
+ * If the string contains escape sequences,
+ * replace it with an allocated rendering and return 1,
+ * such that the caller can free it after use.
+ * Otherwise, do nothing and return 0.
*/
-static void
-render_key(struct mchars *mc, struct str *key)
+static int
+render_string(char **public, size_t *psz)
{
- size_t sz, bsz, pos;
+ const char *src, *scp, *addcp, *seq;
+ char *dst;
+ size_t ssz, dsz, addsz;
char utfbuf[7], res[6];
- char *buf;
- const char *seq, *cpp, *val;
- int len, u;
- enum mandoc_esc esc;
+ int seqlen, unicode;
- assert(NULL == key->rendered);
-
res[0] = '\\';
res[1] = '\t';
res[2] = ASCII_NBRSP;
res[3] = ASCII_HYPH;
res[4] = ASCII_BREAK;
res[5] = '\0';
- val = key->key;
- bsz = strlen(val);
+ src = scp = *public;
+ ssz = *psz;
+ dst = NULL;
+ dsz = 0;
- /*
- * Pre-check: if we have no stop-characters, then set the
- * pointer as ourselvse and get out of here.
- */
- if (strcspn(val, res) == bsz) {
- key->rendered = key->key;
- return;
- }
+ while (scp < src + *psz) {
- /* Pre-allocate by the length of the input */
+ /* Leave normal characters unchanged. */
- buf = mandoc_malloc(++bsz);
- pos = 0;
+ if (strchr(res, *scp) == NULL) {
+ if (dst != NULL)
+ dst[dsz++] = *scp;
+ scp++;
+ continue;
+ }
- while ('\0' != *val) {
/*
- * Halt on the first escape sequence.
- * This also halts on the end of string, in which case
- * we just copy, fallthrough, and exit the loop.
+ * Found something that requires replacing,
+ * make sure we have a destination buffer.
*/
- if ((sz = strcspn(val, res)) > 0) {
- memcpy(&buf[pos], val, sz);
- pos += sz;
- val += sz;
+
+ if (dst == NULL) {
+ dst = mandoc_malloc(ssz + 1);
+ dsz = scp - src;
+ memcpy(dst, src, dsz);
}
- switch (*val) {
- case ASCII_HYPH:
- buf[pos++] = '-';
- val++;
- continue;
+ /* Handle single-char special characters. */
+
+ switch (*scp) {
+ case '\\':
+ break;
case '\t':
/* FALLTHROUGH */
case ASCII_NBRSP:
- buf[pos++] = ' ';
- val++;
+ dst[dsz++] = ' ';
+ scp++;
+ continue;
+ case ASCII_HYPH:
+ dst[dsz++] = '-';
/* FALLTHROUGH */
case ASCII_BREAK:
+ scp++;
continue;
default:
- break;
+ abort();
}
- if ('\\' != *val)
- break;
- /* Read past the slash. */
-
- val++;
-
/*
- * Parse the escape sequence and see if it's a
- * predefined character or special character.
+ * Found an escape sequence.
+ * Read past the slash, then parse it.
+ * Ignore everything except characters.
*/
- esc = mandoc_escape((const char **)&val,
- &seq, &len);
- if (ESCAPE_ERROR == esc)
- break;
- if (ESCAPE_SPECIAL != esc)
+ scp++;
+ if (mandoc_escape(&scp, &seq, &seqlen) != ESCAPE_SPECIAL)
continue;
/*
* Render the special character
* as either UTF-8 or ASCII.
*/
if (write_utf8) {
- if ((u = mchars_spec2cp(mc, seq, len)) <= 0)
+ unicode = mchars_spec2cp(mchars, seq, seqlen);
+ if (unicode <= 0)
continue;
- cpp = utfbuf;
- if (0 == (sz = utf8(u, utfbuf)))
+ addsz = utf8(unicode, utfbuf);
+ if (addsz == 0)
continue;
- sz = strlen(cpp);
+ addcp = utfbuf;
} else {
- cpp = mchars_spec2str(mc, seq, len, &sz);
- if (NULL == cpp)
+ addcp = mchars_spec2str(mchars, seq, seqlen, &addsz);
+ if (addcp == NULL)
continue;
- if (ASCII_NBRSP == *cpp) {
- cpp = " ";
- sz = 1;
+ if (*addcp == ASCII_NBRSP) {
+ addcp = " ";
+ addsz = 1;
}
}
/* Copy the rendered glyph into the stream. */
- bsz += sz;
- buf = mandoc_realloc(buf, bsz);
- memcpy(&buf[pos], cpp, sz);
- pos += sz;
+ ssz += addsz;
+ dst = mandoc_realloc(dst, ssz + 1);
+ memcpy(dst + dsz, addcp, addsz);
+ dsz += addsz;
}
+ if (dst != NULL) {
+ *public = dst;
+ *psz = dsz;
+ }
- buf[pos] = '\0';
- key->rendered = buf;
+ /* Trim trailing whitespace and NUL-terminate. */
+
+ while (*psz > 0 && (*public)[*psz - 1] == ' ')
+ --*psz;
+ if (dst != NULL) {
+ (*public)[*psz] = '\0';
+ return(1);
+ } else
+ return(0);
}
static void
dbadd_mlink(const struct mlink *mlink)
{
size_t i;
i = 1;
SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->dsec);
SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->arch);
SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->name);
SQL_BIND_INT64(stmts[STMT_INSERT_LINK], i, mlink->mpage->pageid);
SQL_STEP(stmts[STMT_INSERT_LINK]);
sqlite3_reset(stmts[STMT_INSERT_LINK]);
}
static void
dbadd_mlink_name(const struct mlink *mlink)
{
+ uint64_t bits;
size_t i;
dbadd_mlink(mlink);
i = 1;
- SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, NAME_FILE & NAME_MASK);
+ SQL_BIND_INT64(stmts[STMT_SELECT_NAME], i, mlink->mpage->pageid);
+ bits = NAME_FILE & NAME_MASK;
+ if (sqlite3_step(stmts[STMT_SELECT_NAME]) == SQLITE_ROW) {
+ bits |= sqlite3_column_int64(stmts[STMT_SELECT_NAME], 0);
+ sqlite3_reset(stmts[STMT_SELECT_NAME]);
+ }
+
+ i = 1;
+ SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, bits);
SQL_BIND_TEXT(stmts[STMT_INSERT_NAME], i, mlink->name);
SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, mlink->mpage->pageid);
SQL_STEP(stmts[STMT_INSERT_NAME]);
sqlite3_reset(stmts[STMT_INSERT_NAME]);
}
/*
* Flush the current page's terms (and their bits) into the database.
* Wrap the entire set of additions in a transaction to make sqlite be a
* little faster.
* Also, handle escape sequences at the last possible moment.
*/
static void
-dbadd(struct mpage *mpage, struct mchars *mc)
+dbadd(struct mpage *mpage)
{
struct mlink *mlink;
struct str *key;
+ char *cp;
size_t i;
unsigned int slot;
+ int mustfree;
mlink = mpage->mlinks;
if (nodb) {
for (key = ohash_first(&names, &slot); NULL != key;
- key = ohash_next(&names, &slot)) {
- if (key->rendered != key->key)
- free(key->rendered);
+ key = ohash_next(&names, &slot))
free(key);
- }
for (key = ohash_first(&strings, &slot); NULL != key;
- key = ohash_next(&strings, &slot)) {
- if (key->rendered != key->key)
- free(key->rendered);
+ key = ohash_next(&strings, &slot))
free(key);
- }
if (0 == debug)
return;
while (NULL != mlink) {
fputs(mlink->name, stdout);
if (NULL == mlink->next ||
strcmp(mlink->dsec, mlink->next->dsec) ||
strcmp(mlink->fsec, mlink->next->fsec) ||
strcmp(mlink->arch, mlink->next->arch)) {
putchar('(');
if ('\0' == *mlink->dsec)
fputs(mlink->fsec, stdout);
else
fputs(mlink->dsec, stdout);
if ('\0' != *mlink->arch)
printf("/%s", mlink->arch);
putchar(')');
}
mlink = mlink->next;
if (NULL != mlink)
fputs(", ", stdout);
}
printf(" - %s\n", mpage->desc);
return;
}
if (debug)
say(mlink->file, "Adding to database");
- i = strlen(mpage->desc) + 1;
- key = mandoc_calloc(1, sizeof(struct str) + i);
- memcpy(key->key, mpage->desc, i);
- render_key(mc, key);
-
+ cp = mpage->desc;
+ i = strlen(cp);
+ mustfree = render_string(&cp, &i);
i = 1;
- SQL_BIND_TEXT(stmts[STMT_INSERT_PAGE], i, key->rendered);
+ SQL_BIND_TEXT(stmts[STMT_INSERT_PAGE], i, cp);
SQL_BIND_INT(stmts[STMT_INSERT_PAGE], i, mpage->form);
SQL_STEP(stmts[STMT_INSERT_PAGE]);
mpage->pageid = sqlite3_last_insert_rowid(db);
sqlite3_reset(stmts[STMT_INSERT_PAGE]);
+ if (mustfree)
+ free(cp);
- if (key->rendered != key->key)
- free(key->rendered);
- free(key);
-
while (NULL != mlink) {
dbadd_mlink(mlink);
mlink = mlink->next;
}
mlink = mpage->mlinks;
for (key = ohash_first(&names, &slot); NULL != key;
key = ohash_next(&names, &slot)) {
assert(key->mpage == mpage);
- if (NULL == key->rendered)
- render_key(mc, key);
i = 1;
SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, key->mask);
- SQL_BIND_TEXT(stmts[STMT_INSERT_NAME], i, key->rendered);
+ SQL_BIND_TEXT(stmts[STMT_INSERT_NAME], i, key->key);
SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, mpage->pageid);
SQL_STEP(stmts[STMT_INSERT_NAME]);
sqlite3_reset(stmts[STMT_INSERT_NAME]);
- if (key->rendered != key->key)
- free(key->rendered);
free(key);
}
for (key = ohash_first(&strings, &slot); NULL != key;
key = ohash_next(&strings, &slot)) {
assert(key->mpage == mpage);
- if (NULL == key->rendered)
- render_key(mc, key);
i = 1;
SQL_BIND_INT64(stmts[STMT_INSERT_KEY], i, key->mask);
- SQL_BIND_TEXT(stmts[STMT_INSERT_KEY], i, key->rendered);
+ SQL_BIND_TEXT(stmts[STMT_INSERT_KEY], i, key->key);
SQL_BIND_INT64(stmts[STMT_INSERT_KEY], i, mpage->pageid);
SQL_STEP(stmts[STMT_INSERT_KEY]);
sqlite3_reset(stmts[STMT_INSERT_KEY]);
- if (key->rendered != key->key)
- free(key->rendered);
free(key);
}
}
static void
dbprune(void)
{
struct mpage *mpage;
struct mlink *mlink;
size_t i;
unsigned int slot;
if (0 == nodb)
SQL_EXEC("BEGIN TRANSACTION");
for (mpage = ohash_first(&mpages, &slot); NULL != mpage;
mpage = ohash_next(&mpages, &slot)) {
mlink = mpage->mlinks;
if (debug)
say(mlink->file, "Deleting from database");
if (nodb)
continue;
for ( ; NULL != mlink; mlink = mlink->next) {
i = 1;
SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE],
i, mlink->dsec);
SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE],
i, mlink->arch);
SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE],
i, mlink->name);
SQL_STEP(stmts[STMT_DELETE_PAGE]);
sqlite3_reset(stmts[STMT_DELETE_PAGE]);
}
}
if (0 == nodb)
SQL_EXEC("END TRANSACTION");
}
/*
* Close an existing database and its prepared statements.
* If "real" is not set, rename the temporary file into the real one.
*/
static void
dbclose(int real)
{
size_t i;
int status;
pid_t child;
if (nodb)
return;
for (i = 0; i < STMT__MAX; i++) {
sqlite3_finalize(stmts[i]);
stmts[i] = NULL;
}
sqlite3_close(db);
db = NULL;
if (real)
return;
if ('\0' == *tempfilename) {
if (-1 == rename(MANDOC_DB "~", MANDOC_DB)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say(MANDOC_DB, "&rename");
}
return;
}
switch (child = fork()) {
case -1:
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "&fork cmp");
return;
case 0:
execlp("cmp", "cmp", "-s",
tempfilename, MANDOC_DB, NULL);
say("", "&exec cmp");
exit(0);
default:
break;
}
if (-1 == waitpid(child, &status, 0)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "&wait cmp");
} else if (WIFSIGNALED(status)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "cmp died from signal %d", WTERMSIG(status));
} else if (WEXITSTATUS(status)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say(MANDOC_DB,
"Data changed, but cannot replace database");
}
*strrchr(tempfilename, '/') = '\0';
switch (child = fork()) {
case -1:
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "&fork rm");
return;
case 0:
execlp("rm", "rm", "-rf", tempfilename, NULL);
say("", "&exec rm");
exit((int)MANDOCLEVEL_SYSERR);
default:
break;
}
if (-1 == waitpid(child, &status, 0)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "&wait rm");
} else if (WIFSIGNALED(status) || WEXITSTATUS(status)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "%s: Cannot remove temporary directory",
tempfilename);
}
}
/*
* This is straightforward stuff.
* Open a database connection to a "temporary" database, then open a set
* of prepared statements we'll use over and over again.
* If "real" is set, we use the existing database; if not, we truncate a
* temporary one.
* Must be matched by dbclose().
*/
static int
dbopen(int real)
{
const char *sql;
int rc, ofl;
if (nodb)
return(1);
*tempfilename = '\0';
ofl = SQLITE_OPEN_READWRITE;
if (real) {
rc = sqlite3_open_v2(MANDOC_DB, &db, ofl, NULL);
if (SQLITE_OK != rc) {
exitcode = (int)MANDOCLEVEL_SYSERR;
if (SQLITE_CANTOPEN != rc)
say(MANDOC_DB, "%s", sqlite3_errstr(rc));
return(0);
}
goto prepare_statements;
}
ofl |= SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE;
remove(MANDOC_DB "~");
rc = sqlite3_open_v2(MANDOC_DB "~", &db, ofl, NULL);
if (SQLITE_OK == rc)
goto create_tables;
if (MPARSE_QUICK & mparse_options) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say(MANDOC_DB "~", "%s", sqlite3_errstr(rc));
return(0);
}
(void)strlcpy(tempfilename, "/tmp/mandocdb.XXXXXX",
sizeof(tempfilename));
if (NULL == mkdtemp(tempfilename)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "&%s", tempfilename);
return(0);
}
(void)strlcat(tempfilename, "/" MANDOC_DB,
sizeof(tempfilename));
rc = sqlite3_open_v2(tempfilename, &db, ofl, NULL);
if (SQLITE_OK != rc) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "%s: %s", tempfilename, sqlite3_errstr(rc));
return(0);
}
create_tables:
sql = "CREATE TABLE \"mpages\" (\n"
" \"desc\" TEXT NOT NULL,\n"
" \"form\" INTEGER NOT NULL,\n"
" \"pageid\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL\n"
");\n"
"\n"
"CREATE TABLE \"mlinks\" (\n"
" \"sec\" TEXT NOT NULL,\n"
" \"arch\" TEXT NOT NULL,\n"
" \"name\" TEXT NOT NULL,\n"
" \"pageid\" INTEGER NOT NULL REFERENCES mpages(pageid) "
"ON DELETE CASCADE\n"
");\n"
"CREATE INDEX mlinks_pageid_idx ON mlinks (pageid);\n"
"\n"
"CREATE TABLE \"names\" (\n"
" \"bits\" INTEGER NOT NULL,\n"
" \"name\" TEXT NOT NULL,\n"
" \"pageid\" INTEGER NOT NULL REFERENCES mpages(pageid) "
- "ON DELETE CASCADE\n"
+ "ON DELETE CASCADE,\n"
+ " UNIQUE (\"name\", \"pageid\") ON CONFLICT REPLACE\n"
");\n"
"\n"
"CREATE TABLE \"keys\" (\n"
" \"bits\" INTEGER NOT NULL,\n"
" \"key\" TEXT NOT NULL,\n"
" \"pageid\" INTEGER NOT NULL REFERENCES mpages(pageid) "
"ON DELETE CASCADE\n"
");\n"
"CREATE INDEX keys_pageid_idx ON keys (pageid);\n";
if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, NULL)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say(MANDOC_DB, "%s", sqlite3_errmsg(db));
sqlite3_close(db);
return(0);
}
prepare_statements:
if (SQLITE_OK != sqlite3_exec(db,
"PRAGMA foreign_keys = ON", NULL, NULL, NULL)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say(MANDOC_DB, "PRAGMA foreign_keys: %s",
sqlite3_errmsg(db));
sqlite3_close(db);
return(0);
}
sql = "DELETE FROM mpages WHERE pageid IN "
"(SELECT pageid FROM mlinks WHERE "
"sec=? AND arch=? AND name=?)";
sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_DELETE_PAGE], NULL);
sql = "INSERT INTO mpages "
"(desc,form) VALUES (?,?)";
sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_PAGE], NULL);
sql = "INSERT INTO mlinks "
"(sec,arch,name,pageid) VALUES (?,?,?,?)";
sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_LINK], NULL);
+ sql = "SELECT bits FROM names where pageid = ?";
+ sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_SELECT_NAME], NULL);
sql = "INSERT INTO names "
"(bits,name,pageid) VALUES (?,?,?)";
sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_NAME], NULL);
sql = "INSERT INTO keys "
"(bits,key,pageid) VALUES (?,?,?)";
sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_KEY], NULL);
#ifndef __APPLE__
/*
* When opening a new database, we can turn off
* synchronous mode for much better performance.
*/
if (real && SQLITE_OK != sqlite3_exec(db,
"PRAGMA synchronous = OFF", NULL, NULL, NULL)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say(MANDOC_DB, "PRAGMA synchronous: %s",
sqlite3_errmsg(db));
sqlite3_close(db);
return(0);
}
#endif
return(1);
}
static void *
hash_calloc(size_t n, size_t sz, void *arg)
{
return(mandoc_calloc(n, sz));
}
static void *
hash_alloc(size_t sz, void *arg)
{
return(mandoc_malloc(sz));
}
static void
hash_free(void *p, void *arg)
{
free(p);
}
static int
set_basedir(const char *targetdir, int report_baddir)
{
static char startdir[PATH_MAX];
static int getcwd_status; /* 1 = ok, 2 = failure */
static int chdir_status; /* 1 = changed directory */
char *cp;
/*
* Remember the original working directory, if possible.
* This will be needed if the second or a later directory
* on the command line is given as a relative path.
* Do not error out if the current directory is not
* searchable: Maybe it won't be needed after all.
*/
if (0 == getcwd_status) {
if (NULL == getcwd(startdir, sizeof(startdir))) {
getcwd_status = 2;
(void)strlcpy(startdir, strerror(errno),
sizeof(startdir));
} else
getcwd_status = 1;
}
/*
* We are leaving the old base directory.
* Do not use it any longer, not even for messages.
*/
*basedir = '\0';
/*
* If and only if the directory was changed earlier and
* the next directory to process is given as a relative path,
* first go back, or bail out if that is impossible.
*/
if (chdir_status && '/' != *targetdir) {
if (2 == getcwd_status) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "getcwd: %s", startdir);
return(0);
}
if (-1 == chdir(startdir)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "&chdir %s", startdir);
return(0);
}
}
/*
* Always resolve basedir to the canonicalized absolute
* pathname and append a trailing slash, such that
* we can reliably check whether files are inside.
*/
if (NULL == realpath(targetdir, basedir)) {
if (report_baddir || errno != ENOENT) {
exitcode = (int)MANDOCLEVEL_BADARG;
say("", "&%s: realpath", targetdir);
}
return(0);
} else if (-1 == chdir(basedir)) {
if (report_baddir || errno != ENOENT) {
exitcode = (int)MANDOCLEVEL_BADARG;
say("", "&chdir");
}
return(0);
}
chdir_status = 1;
cp = strchr(basedir, '\0');
if ('/' != cp[-1]) {
if (cp - basedir >= PATH_MAX - 1) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "Filename too long");
return(0);
}
*cp++ = '/';
*cp = '\0';
}
return(1);
}
static void
say(const char *file, const char *format, ...)
{
va_list ap;
int use_errno;
if ('\0' != *basedir)
fprintf(stderr, "%s", basedir);
if ('\0' != *basedir && '\0' != *file)
fputc('/', stderr);
if ('\0' != *file)
fprintf(stderr, "%s", file);
use_errno = 1;
if (NULL != format) {
switch (*format) {
case '&':
format++;
break;
case '\0':
format = NULL;
break;
default:
use_errno = 0;
break;
}
}
if (NULL != format) {
if ('\0' != *basedir || '\0' != *file)
fputs(": ", stderr);
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
if (use_errno) {
if ('\0' != *basedir || '\0' != *file || NULL != format)
fputs(": ", stderr);
perror(NULL);
} else
fputc('\n', stderr);
}
Index: vendor/mdocml/dist/manpath.h
===================================================================
--- vendor/mdocml/dist/manpath.h (revision 276215)
+++ vendor/mdocml/dist/manpath.h (revision 276216)
@@ -1,38 +1,34 @@
-/* $Id: manpath.h,v 1.6 2012/06/08 10:32:40 kristaps Exp $ */
+/* $Id: manpath.h,v 1.7 2014/12/01 04:05:32 schwarze Exp $ */
/*
* Copyright (c) 2011 Ingo Schwarze
* Copyright (c) 2011 Kristaps Dzonsons
*
* 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 MANPATH_H
-#define MANPATH_H
/*
* Unsorted list of unique, absolute paths to be searched for manual
* databases.
*/
struct manpaths {
size_t sz;
char **paths;
};
__BEGIN_DECLS
void manpath_manconf(struct manpaths *, const char *);
void manpath_parse(struct manpaths *, const char *, char *, char *);
void manpath_free(struct manpaths *);
__END_DECLS
-
-#endif /*!MANPATH_H*/
Index: vendor/mdocml/dist/mansearch.3
===================================================================
--- vendor/mdocml/dist/mansearch.3 (revision 276215)
+++ vendor/mdocml/dist/mansearch.3 (revision 276216)
@@ -1,228 +1,228 @@
-.\" $Id: mansearch.3,v 1.2 2014/08/05 15:29:30 schwarze Exp $
+.\" $Id: mansearch.3,v 1.3 2014/12/12 21:44:33 schwarze Exp $
.\"
.\" Copyright (c) 2014 Ingo Schwarze
.\"
.\" 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: August 5 2014 $
+.Dd $Mdocdate: December 12 2014 $
.Dt MANSEARCH 3
.Os
.Sh NAME
.Nm mansearch ,
.Nm mansearch_setup
.Nd search manual page databases
.Sh SYNOPSIS
.In stdint.h
.In manpath.h
.In mansearch.h
.Ft int
.Fo mansearch_setup
.Fa "int start"
.Fc
.Ft int
.Fo mansearch
.Fa "const struct mansearch *search"
.Fa "const struct manpaths *paths"
.Fa "int argc"
.Fa "char *argv[]"
.Fa "const char *outkey"
.Fa "struct manpage **res"
.Fa "size_t *sz"
.Fc
.Sh DESCRIPTION
The
.Fn mansearch
function returns information about manuals matching a search query from a
.Xr mandoc.db 5
SQLite3 database.
.Pp
The query arguments are as follows:
.Bl -tag -width Ds
.It Fa "const struct mansearch *search"
Search options, defined in
.In mansearch.h .
.It Fa "const struct manpaths *paths"
Directories to be searched, defined in
.In manpath.h .
.It Fa "int argc" , "char *argv[]"
Search criteria, usually taken from the command line.
.El
.Pp
The
.Fa "const char *outkey"
selects which data to return in the
.Va output
field of the
.Fa res
structures.
It takes any of the macro keys defined in
.Pa mansearch_const.c
and described in
.Xr apropos 1 .
.Pp
The output arguments are as follows:
.Bl -tag -width Ds
.It Fa "struct manpage **res"
Returns a pointer to an array of result structures defined in
.In mansearch.h .
The user is expected to call
.Xr free 3
on the
.Va file ,
.Va names ,
and
.Va output
fields of all structures, as well as the
.Fa res
array itself.
.It Fa "size_t *sz"
Returns the number of result structures contained in
.Fa res .
.El
.Pp
To speed up searches, the
.Fn mansearch_setup
function can optionally be called with a
.Fa start
argument of 1 before
.Fn mansearch
to set up an SQLite3 pagecache.
If it was called, it has to be called again with a
.Fa start
argument of 0 after the last call to
.Fn mansearch
to release the memory used for the pagecache.
.Sh IMPLEMENTATION NOTES
For each manual page tree, the search is done in two steps.
In the first step, a list of pages matching the search criteria is built.
In the second step, the requested information about these pages is
retrieved from the database and assembled into the
.Fa res
array.
.Pp
All function mentioned here are defined in the file
.Pa mansearch.c .
No functions except
.Fn mansearch
and
.Fn sql_statement
build any SQL code, and no functions except
.Fn mansearch ,
.Fn buildnames ,
and
.Fn buildoutput
execute it.
.Ss Finding matches
The query is built using the following grammar:
.Bd -literal -offset indent
::= "SELECT * FROM mpages WHERE"
::= "(" ")" |
"OR" |
"AND" |
"desc" "?" |
"id IN (SELECT pageid FROM" ")"
::= "names WHERE name" "?" |
"keys WHERE key" "? AND bits & ?"
::= "MATCH" | "REGEXP"
.Ed
.Pp
The MATCH and REGEXP operators are implemented by the functions
.Fn sql_match
and
.Fn sql_regexp ,
respectively.
This is required because SQLite3 natively neither supports
case-insensitive substring matching nor regular expression matching,
but only string identity, shell globbing, and the weird home-brewed
LIKE operator.
.Pp
Command line parsing is done by the function
.Fn exprcomp
building a singly linked list of
.Vt expr
structures, using the helper functions
.Fn exprterm
and
.Fn exprspec .
The resulting SQL statement is assembled by the function
.Fn sql_statement
and evaluated in the main loop of the
.Fn mansearch
function.
.Ss Assembling the results
The names, sections, and architectures of the manuals found
are assembled into the
.Va names
field of the result structure by the function
.Fn buildnames ,
using the following query:
.Pp
.Dl "SELECT * FROM mlinks WHERE pageid=? ORDER BY sec, arch, name"
.Pp
If the
.Fa outkey
differs from
-.Qq Nd ,
+.Qq Ic \&Nd ,
the requested output data is assembled into the
.Va output
field of the result structure by the function
.Fn buildoutput ,
using the following query:
.Pp
.Dl "SELECT * FROM keys WHERE pageid=? AND bits & ?"
.Sh FILES
.Bl -tag -width mandoc.db -compact
.It Pa mandoc.db
The manual page database.
.El
.Sh EXAMPLES
The simplest invocation
.Pp
.Dl apropos keyword
.Pp
results in the following SQL query:
.Bd -literal
SELECT * FROM mpages WHERE (
id IN (SELECT pageid FROM names WHERE name MATCH 'keyword') OR
desc MATCH 'keyword'
);
.Ed
.Pp
A more complicated request like
.Pp
.Dl apropos -s 2 Nm,Xr=getuid
.Pp
results in:
.Bd -literal
SELECT * FROM mpages WHERE (
id IN (SELECT pageid FROM names WHERE name MATCH 'getuid') OR
id IN (SELECT pageid FROM keys WHERE key MATCH 'getuid' AND bits & 4)
) AND id IN (SELECT pageid FROM keys WHERE key REGEXP '^2$' AND bits & 2);
.Ed
.Sh SEE ALSO
.Xr apropos 1 ,
.Xr mandoc.db 5 ,
.Xr makewhatis 8
.Sh HISTORY
The
.Fn mansearch
subsystem first appeared in
.Ox 5.6 .
.Sh AUTHORS
.An -nosplit
A module to search manual page databases was first written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
in 2011, at first using the Berkeley DB;
he rewrote it for SQLite3 in 2012.
The current version received major changes from
.An Ingo Schwarze Aq Mt schwarze@openbsd.org .
Index: vendor/mdocml/dist/mansearch.c
===================================================================
--- vendor/mdocml/dist/mansearch.c (revision 276215)
+++ vendor/mdocml/dist/mansearch.c (revision 276216)
@@ -1,877 +1,879 @@
-/* $Id: mansearch.c,v 1.51 2014/11/27 01:58:21 schwarze Exp $ */
+/* $Id: mansearch.c,v 1.52 2014/12/06 01:23:24 schwarze Exp $ */
/*
* Copyright (c) 2012 Kristaps Dzonsons
* Copyright (c) 2013, 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if HAVE_OHASH
#include
#else
#include "compat_ohash.h"
#endif
#include
#ifndef SQLITE_DETERMINISTIC
#define SQLITE_DETERMINISTIC 0
#endif
#include "mandoc.h"
#include "mandoc_aux.h"
#include "manpath.h"
#include "mansearch.h"
extern int mansearch_keymax;
extern const char *const mansearch_keynames[];
#define SQL_BIND_TEXT(_db, _s, _i, _v) \
do { if (SQLITE_OK != sqlite3_bind_text \
((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \
fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \
} while (0)
#define SQL_BIND_INT64(_db, _s, _i, _v) \
do { if (SQLITE_OK != sqlite3_bind_int64 \
((_s), (_i)++, (_v))) \
fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \
} while (0)
#define SQL_BIND_BLOB(_db, _s, _i, _v) \
do { if (SQLITE_OK != sqlite3_bind_blob \
((_s), (_i)++, (&_v), sizeof(_v), SQLITE_STATIC)) \
fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \
} while (0)
struct expr {
regex_t regexp; /* compiled regexp, if applicable */
const char *substr; /* to search for, if applicable */
struct expr *next; /* next in sequence */
uint64_t bits; /* type-mask */
int equal; /* equality, not subsring match */
int open; /* opening parentheses before */
int and; /* logical AND before */
int close; /* closing parentheses after */
};
struct match {
uint64_t pageid; /* identifier in database */
uint64_t bits; /* name type mask */
char *desc; /* manual page description */
int form; /* bit field: formatted, zipped? */
};
static void buildnames(struct manpage *, sqlite3 *,
sqlite3_stmt *, uint64_t,
const char *, int form);
static char *buildoutput(sqlite3 *, sqlite3_stmt *,
uint64_t, uint64_t);
static void *hash_alloc(size_t, void *);
static void hash_free(void *, void *);
static void *hash_calloc(size_t, size_t, void *);
static struct expr *exprcomp(const struct mansearch *,
int, char *[]);
static void exprfree(struct expr *);
static struct expr *exprspec(struct expr *, uint64_t,
const char *, const char *);
static struct expr *exprterm(const struct mansearch *, char *, int);
static int manpage_compare(const void *, const void *);
static void sql_append(char **sql, size_t *sz,
const char *newstr, int count);
static void sql_match(sqlite3_context *context,
int argc, sqlite3_value **argv);
static void sql_regexp(sqlite3_context *context,
int argc, sqlite3_value **argv);
static char *sql_statement(const struct expr *);
int
mansearch_setup(int start)
{
static void *pagecache;
int c;
#define PC_PAGESIZE 1280
#define PC_NUMPAGES 256
if (start) {
if (NULL != pagecache) {
fprintf(stderr, "pagecache already enabled\n");
return((int)MANDOCLEVEL_BADARG);
}
pagecache = mmap(NULL, PC_PAGESIZE * PC_NUMPAGES,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANON, -1, 0);
if (MAP_FAILED == pagecache) {
perror("mmap");
pagecache = NULL;
return((int)MANDOCLEVEL_SYSERR);
}
c = sqlite3_config(SQLITE_CONFIG_PAGECACHE,
pagecache, PC_PAGESIZE, PC_NUMPAGES);
if (SQLITE_OK == c)
return((int)MANDOCLEVEL_OK);
fprintf(stderr, "pagecache: %s\n", sqlite3_errstr(c));
} else if (NULL == pagecache) {
fprintf(stderr, "pagecache missing\n");
return((int)MANDOCLEVEL_BADARG);
}
if (-1 == munmap(pagecache, PC_PAGESIZE * PC_NUMPAGES)) {
perror("munmap");
pagecache = NULL;
return((int)MANDOCLEVEL_SYSERR);
}
pagecache = NULL;
return((int)MANDOCLEVEL_OK);
}
int
mansearch(const struct mansearch *search,
const struct manpaths *paths,
int argc, char *argv[],
struct manpage **res, size_t *sz)
{
int fd, rc, c, indexbit;
int64_t pageid;
uint64_t outbit, iterbit;
char buf[PATH_MAX];
char *sql;
struct manpage *mpage;
struct expr *e, *ep;
sqlite3 *db;
sqlite3_stmt *s, *s2;
struct match *mp;
struct ohash_info info;
struct ohash htab;
unsigned int idx;
size_t i, j, cur, maxres;
info.calloc = hash_calloc;
info.alloc = hash_alloc;
info.free = hash_free;
info.key_offset = offsetof(struct match, pageid);
*sz = cur = maxres = 0;
sql = NULL;
*res = NULL;
fd = -1;
e = NULL;
rc = 0;
if (0 == argc)
goto out;
if (NULL == (e = exprcomp(search, argc, argv)))
goto out;
outbit = 0;
if (NULL != search->outkey) {
for (indexbit = 0, iterbit = 1;
indexbit < mansearch_keymax;
indexbit++, iterbit <<= 1) {
if (0 == strcasecmp(search->outkey,
mansearch_keynames[indexbit])) {
outbit = iterbit;
break;
}
}
}
/*
* Save a descriptor to the current working directory.
* Since pathnames in the "paths" variable might be relative,
* and we'll be chdir()ing into them, we need to keep a handle
* on our current directory from which to start the chdir().
*/
if (NULL == getcwd(buf, PATH_MAX)) {
perror("getcwd");
goto out;
} else if (-1 == (fd = open(buf, O_RDONLY, 0))) {
perror(buf);
goto out;
}
sql = sql_statement(e);
/*
* Loop over the directories (containing databases) for us to
* search.
* Don't let missing/bad databases/directories phase us.
* In each, try to open the resident database and, if it opens,
* scan it for our match expression.
*/
for (i = 0; i < paths->sz; i++) {
if (-1 == fchdir(fd)) {
perror(buf);
free(*res);
break;
} else if (-1 == chdir(paths->paths[i])) {
perror(paths->paths[i]);
continue;
}
c = sqlite3_open_v2(MANDOC_DB, &db,
SQLITE_OPEN_READONLY, NULL);
if (SQLITE_OK != c) {
- perror(MANDOC_DB);
+ fprintf(stderr, "%s/%s: %s\n",
+ paths->paths[i], MANDOC_DB, strerror(errno));
sqlite3_close(db);
continue;
}
/*
* Define the SQL functions for substring
* and regular expression matching.
*/
c = sqlite3_create_function(db, "match", 2,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
NULL, sql_match, NULL, NULL);
assert(SQLITE_OK == c);
c = sqlite3_create_function(db, "regexp", 2,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
NULL, sql_regexp, NULL, NULL);
assert(SQLITE_OK == c);
j = 1;
c = sqlite3_prepare_v2(db, sql, -1, &s, NULL);
if (SQLITE_OK != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
for (ep = e; NULL != ep; ep = ep->next) {
if (NULL == ep->substr) {
SQL_BIND_BLOB(db, s, j, ep->regexp);
} else
SQL_BIND_TEXT(db, s, j, ep->substr);
if (0 == ((TYPE_Nd | TYPE_Nm) & ep->bits))
SQL_BIND_INT64(db, s, j, ep->bits);
}
memset(&htab, 0, sizeof(struct ohash));
ohash_init(&htab, 4, &info);
/*
* Hash each entry on its [unique] document identifier.
* This is a uint64_t.
* Instead of using a hash function, simply convert the
* uint64_t to a uint32_t, the hash value's type.
* This gives good performance and preserves the
* distribution of buckets in the table.
*/
while (SQLITE_ROW == (c = sqlite3_step(s))) {
pageid = sqlite3_column_int64(s, 2);
idx = ohash_lookup_memory(&htab,
(char *)&pageid, sizeof(uint64_t),
(uint32_t)pageid);
if (NULL != ohash_find(&htab, idx))
continue;
mp = mandoc_calloc(1, sizeof(struct match));
mp->pageid = pageid;
mp->form = sqlite3_column_int(s, 1);
mp->bits = sqlite3_column_int64(s, 3);
if (TYPE_Nd == outbit)
mp->desc = mandoc_strdup((const char *)
sqlite3_column_text(s, 0));
ohash_insert(&htab, idx, mp);
}
if (SQLITE_DONE != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
sqlite3_finalize(s);
c = sqlite3_prepare_v2(db,
"SELECT sec, arch, name, pageid FROM mlinks "
"WHERE pageid=? ORDER BY sec, arch, name",
-1, &s, NULL);
if (SQLITE_OK != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
c = sqlite3_prepare_v2(db,
"SELECT bits, key, pageid FROM keys "
"WHERE pageid=? AND bits & ?",
-1, &s2, NULL);
if (SQLITE_OK != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
for (mp = ohash_first(&htab, &idx);
NULL != mp;
mp = ohash_next(&htab, &idx)) {
if (cur + 1 > maxres) {
maxres += 1024;
*res = mandoc_reallocarray(*res,
maxres, sizeof(struct manpage));
}
mpage = *res + cur;
mpage->ipath = i;
mpage->bits = mp->bits;
mpage->sec = 10;
mpage->form = mp->form;
buildnames(mpage, db, s, mp->pageid,
paths->paths[i], mp->form);
mpage->output = TYPE_Nd & outbit ?
mp->desc : outbit ?
buildoutput(db, s2, mp->pageid, outbit) : NULL;
free(mp);
cur++;
}
sqlite3_finalize(s);
sqlite3_finalize(s2);
sqlite3_close(db);
ohash_delete(&htab);
/*
* In man(1) mode, prefer matches in earlier trees
* over matches in later trees.
*/
if (cur && search->firstmatch)
break;
}
qsort(*res, cur, sizeof(struct manpage), manpage_compare);
rc = 1;
out:
if (-1 != fd) {
if (-1 == fchdir(fd))
perror(buf);
close(fd);
}
exprfree(e);
free(sql);
*sz = cur;
return(rc);
}
void
mansearch_free(struct manpage *res, size_t sz)
{
size_t i;
for (i = 0; i < sz; i++) {
free(res[i].file);
free(res[i].names);
free(res[i].output);
}
free(res);
}
static int
manpage_compare(const void *vp1, const void *vp2)
{
const struct manpage *mp1, *mp2;
int diff;
mp1 = vp1;
mp2 = vp2;
return( (diff = mp2->bits - mp1->bits) ? diff :
(diff = mp1->sec - mp2->sec) ? diff :
strcasecmp(mp1->names, mp2->names));
}
static void
buildnames(struct manpage *mpage, sqlite3 *db, sqlite3_stmt *s,
uint64_t pageid, const char *path, int form)
{
char *newnames, *prevsec, *prevarch;
const char *oldnames, *sep1, *name, *sec, *sep2, *arch, *fsec;
size_t i;
int c;
mpage->file = NULL;
mpage->names = NULL;
prevsec = prevarch = NULL;
i = 1;
SQL_BIND_INT64(db, s, i, pageid);
while (SQLITE_ROW == (c = sqlite3_step(s))) {
/* Decide whether we already have some names. */
if (NULL == mpage->names) {
oldnames = "";
sep1 = "";
} else {
oldnames = mpage->names;
sep1 = ", ";
}
/* Fetch the next name. */
sec = (const char *)sqlite3_column_text(s, 0);
arch = (const char *)sqlite3_column_text(s, 1);
name = (const char *)sqlite3_column_text(s, 2);
/* Remember the first section found. */
if (9 < mpage->sec && '1' <= *sec && '9' >= *sec)
mpage->sec = (*sec - '1') + 1;
/* If the section changed, append the old one. */
if (NULL != prevsec &&
(strcmp(sec, prevsec) ||
strcmp(arch, prevarch))) {
sep2 = '\0' == *prevarch ? "" : "/";
mandoc_asprintf(&newnames, "%s(%s%s%s)",
oldnames, prevsec, sep2, prevarch);
free(mpage->names);
oldnames = mpage->names = newnames;
free(prevsec);
free(prevarch);
prevsec = prevarch = NULL;
}
/* Save the new section, to append it later. */
if (NULL == prevsec) {
prevsec = mandoc_strdup(sec);
prevarch = mandoc_strdup(arch);
}
/* Append the new name. */
mandoc_asprintf(&newnames, "%s%s%s",
oldnames, sep1, name);
free(mpage->names);
mpage->names = newnames;
/* Also save the first file name encountered. */
if (mpage->file != NULL)
continue;
if (form & FORM_SRC) {
sep1 = "man";
fsec = sec;
} else {
sep1 = "cat";
fsec = "0";
}
sep2 = *arch == '\0' ? "" : "/";
mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.%s",
path, sep1, sec, sep2, arch, name, fsec);
}
if (c != SQLITE_DONE)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
sqlite3_reset(s);
/* Append one final section to the names. */
if (prevsec != NULL) {
sep2 = *prevarch == '\0' ? "" : "/";
mandoc_asprintf(&newnames, "%s(%s%s%s)",
mpage->names, prevsec, sep2, prevarch);
free(mpage->names);
mpage->names = newnames;
free(prevsec);
free(prevarch);
}
}
static char *
buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t pageid, uint64_t outbit)
{
char *output, *newoutput;
const char *oldoutput, *sep1, *data;
size_t i;
int c;
output = NULL;
i = 1;
SQL_BIND_INT64(db, s, i, pageid);
SQL_BIND_INT64(db, s, i, outbit);
while (SQLITE_ROW == (c = sqlite3_step(s))) {
if (NULL == output) {
oldoutput = "";
sep1 = "";
} else {
oldoutput = output;
sep1 = " # ";
}
data = (const char *)sqlite3_column_text(s, 1);
mandoc_asprintf(&newoutput, "%s%s%s",
oldoutput, sep1, data);
free(output);
output = newoutput;
}
if (SQLITE_DONE != c)
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
sqlite3_reset(s);
return(output);
}
/*
* Implement substring match as an application-defined SQL function.
* Using the SQL LIKE or GLOB operators instead would be a bad idea
* because that would require escaping metacharacters in the string
* being searched for.
*/
static void
sql_match(sqlite3_context *context, int argc, sqlite3_value **argv)
{
assert(2 == argc);
sqlite3_result_int(context, NULL != strcasestr(
(const char *)sqlite3_value_text(argv[1]),
(const char *)sqlite3_value_text(argv[0])));
}
/*
* Implement regular expression match
* as an application-defined SQL function.
*/
static void
sql_regexp(sqlite3_context *context, int argc, sqlite3_value **argv)
{
assert(2 == argc);
sqlite3_result_int(context, !regexec(
(regex_t *)sqlite3_value_blob(argv[0]),
(const char *)sqlite3_value_text(argv[1]),
0, NULL, 0));
}
static void
sql_append(char **sql, size_t *sz, const char *newstr, int count)
{
size_t newsz;
newsz = 1 < count ? (size_t)count : strlen(newstr);
*sql = mandoc_realloc(*sql, *sz + newsz + 1);
if (1 < count)
memset(*sql + *sz, *newstr, (size_t)count);
else
memcpy(*sql + *sz, newstr, newsz);
*sz += newsz;
(*sql)[*sz] = '\0';
}
/*
* Prepare the search SQL statement.
*/
static char *
sql_statement(const struct expr *e)
{
char *sql;
size_t sz;
int needop;
sql = mandoc_strdup(e->equal ?
"SELECT desc, form, pageid, bits "
"FROM mpages NATURAL JOIN names WHERE " :
"SELECT desc, form, pageid, 0 FROM mpages WHERE ");
sz = strlen(sql);
for (needop = 0; NULL != e; e = e->next) {
if (e->and)
sql_append(&sql, &sz, " AND ", 1);
else if (needop)
sql_append(&sql, &sz, " OR ", 1);
if (e->open)
sql_append(&sql, &sz, "(", e->open);
sql_append(&sql, &sz,
TYPE_Nd & e->bits
? (NULL == e->substr
? "desc REGEXP ?"
: "desc MATCH ?")
: TYPE_Nm == e->bits
? (NULL == e->substr
? "pageid IN (SELECT pageid FROM names "
"WHERE name REGEXP ?)"
: e->equal
? "name = ? "
: "pageid IN (SELECT pageid FROM names "
"WHERE name MATCH ?)")
: (NULL == e->substr
? "pageid IN (SELECT pageid FROM keys "
"WHERE key REGEXP ? AND bits & ?)"
: "pageid IN (SELECT pageid FROM keys "
"WHERE key MATCH ? AND bits & ?)"), 1);
if (e->close)
sql_append(&sql, &sz, ")", e->close);
needop = 1;
}
return(sql);
}
/*
* Compile a set of string tokens into an expression.
* Tokens in "argv" are assumed to be individual expression atoms (e.g.,
* "(", "foo=bar", etc.).
*/
static struct expr *
exprcomp(const struct mansearch *search, int argc, char *argv[])
{
uint64_t mask;
int i, toopen, logic, igncase, toclose;
struct expr *first, *prev, *cur, *next;
first = cur = NULL;
logic = igncase = toclose = 0;
toopen = NULL != search->sec || NULL != search->arch;
for (i = 0; i < argc; i++) {
if (0 == strcmp("(", argv[i])) {
if (igncase)
goto fail;
toopen++;
toclose++;
continue;
} else if (0 == strcmp(")", argv[i])) {
if (toopen || logic || igncase || NULL == cur)
goto fail;
cur->close++;
if (0 > --toclose)
goto fail;
continue;
} else if (0 == strcmp("-a", argv[i])) {
if (toopen || logic || igncase || NULL == cur)
goto fail;
logic = 1;
continue;
} else if (0 == strcmp("-o", argv[i])) {
if (toopen || logic || igncase || NULL == cur)
goto fail;
logic = 2;
continue;
} else if (0 == strcmp("-i", argv[i])) {
if (igncase)
goto fail;
igncase = 1;
continue;
}
next = exprterm(search, argv[i], !igncase);
if (NULL == next)
goto fail;
if (NULL == first)
first = next;
else
cur->next = next;
prev = cur = next;
/*
* Searching for descriptions must be split out
* because they are stored in the mpages table,
* not in the keys table.
*/
for (mask = TYPE_Nm; mask <= TYPE_Nd; mask <<= 1) {
if (mask & cur->bits && ~mask & cur->bits) {
next = mandoc_calloc(1,
sizeof(struct expr));
memcpy(next, cur, sizeof(struct expr));
prev->open = 1;
cur->bits = mask;
cur->next = next;
cur = next;
cur->bits &= ~mask;
}
}
prev->and = (1 == logic);
prev->open += toopen;
if (cur != prev)
cur->close = 1;
toopen = logic = igncase = 0;
}
if (toopen || logic || igncase || toclose)
goto fail;
if (NULL != search->sec || NULL != search->arch)
cur->close++;
if (NULL != search->arch)
cur = exprspec(cur, TYPE_arch, search->arch, "^(%s|any)$");
if (NULL != search->sec)
exprspec(cur, TYPE_sec, search->sec, "^%s$");
return(first);
fail:
if (NULL != first)
exprfree(first);
return(NULL);
}
static struct expr *
exprspec(struct expr *cur, uint64_t key, const char *value,
const char *format)
{
char errbuf[BUFSIZ];
char *cp;
int irc;
mandoc_asprintf(&cp, format, value);
cur->next = mandoc_calloc(1, sizeof(struct expr));
cur = cur->next;
cur->and = 1;
cur->bits = key;
if (0 != (irc = regcomp(&cur->regexp, cp,
REG_EXTENDED | REG_NOSUB | REG_ICASE))) {
regerror(irc, &cur->regexp, errbuf, sizeof(errbuf));
fprintf(stderr, "regcomp: %s\n", errbuf);
cur->substr = value;
}
free(cp);
return(cur);
}
static struct expr *
exprterm(const struct mansearch *search, char *buf, int cs)
{
char errbuf[BUFSIZ];
struct expr *e;
char *key, *val;
uint64_t iterbit;
int i, irc;
if ('\0' == *buf)
return(NULL);
e = mandoc_calloc(1, sizeof(struct expr));
if (search->argmode == ARG_NAME) {
e->bits = TYPE_Nm;
e->substr = buf;
e->equal = 1;
return(e);
}
/*
* Separate macro keys from search string.
* If needed, request regular expression handling
* by setting e->substr to NULL.
*/
if (search->argmode == ARG_WORD) {
e->bits = TYPE_Nm;
e->substr = NULL;
mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf);
cs = 0;
} else if ((val = strpbrk(buf, "=~")) == NULL) {
e->bits = TYPE_Nm | TYPE_Nd;
e->substr = buf;
} else {
if (val == buf)
e->bits = TYPE_Nm | TYPE_Nd;
if ('=' == *val)
e->substr = val + 1;
*val++ = '\0';
if (NULL != strstr(buf, "arch"))
cs = 0;
}
/* Compile regular expressions. */
if (NULL == e->substr) {
irc = regcomp(&e->regexp, val,
REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE));
if (search->argmode == ARG_WORD)
free(val);
if (irc) {
regerror(irc, &e->regexp, errbuf, sizeof(errbuf));
fprintf(stderr, "regcomp: %s\n", errbuf);
free(e);
return(NULL);
}
}
if (e->bits)
return(e);
/*
* Parse out all possible fields.
* If the field doesn't resolve, bail.
*/
while (NULL != (key = strsep(&buf, ","))) {
if ('\0' == *key)
continue;
for (i = 0, iterbit = 1;
i < mansearch_keymax;
i++, iterbit <<= 1) {
if (0 == strcasecmp(key,
mansearch_keynames[i])) {
e->bits |= iterbit;
break;
}
}
if (i == mansearch_keymax) {
if (strcasecmp(key, "any")) {
free(e);
return(NULL);
}
e->bits |= ~0ULL;
}
}
return(e);
}
static void
exprfree(struct expr *p)
{
struct expr *pp;
while (NULL != p) {
pp = p->next;
free(p);
p = pp;
}
}
static void *
hash_calloc(size_t nmemb, size_t sz, void *arg)
{
return(mandoc_calloc(nmemb, sz));
}
static void *
hash_alloc(size_t sz, void *arg)
{
return(mandoc_malloc(sz));
}
static void
hash_free(void *p, void *arg)
{
free(p);
}
Index: vendor/mdocml/dist/mansearch.h
===================================================================
--- vendor/mdocml/dist/mansearch.h (revision 276215)
+++ vendor/mdocml/dist/mansearch.h (revision 276216)
@@ -1,113 +1,111 @@
-/* $Id: mansearch.h,v 1.21 2014/11/27 01:58:21 schwarze Exp $ */
+/* $Id: mansearch.h,v 1.23 2014/12/01 08:05:52 schwarze Exp $ */
/*
* Copyright (c) 2012 Kristaps Dzonsons
* Copyright (c) 2013, 2014 Ingo Schwarze
*
* 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 MANSEARCH_H
-#define MANSEARCH_H
#define MANDOC_DB "mandoc.db"
#define TYPE_arch 0x0000000000000001ULL
#define TYPE_sec 0x0000000000000002ULL
#define TYPE_Xr 0x0000000000000004ULL
#define TYPE_Ar 0x0000000000000008ULL
#define TYPE_Fa 0x0000000000000010ULL
#define TYPE_Fl 0x0000000000000020ULL
#define TYPE_Dv 0x0000000000000040ULL
#define TYPE_Fn 0x0000000000000080ULL
#define TYPE_Ic 0x0000000000000100ULL
#define TYPE_Pa 0x0000000000000200ULL
#define TYPE_Cm 0x0000000000000400ULL
#define TYPE_Li 0x0000000000000800ULL
#define TYPE_Em 0x0000000000001000ULL
#define TYPE_Cd 0x0000000000002000ULL
#define TYPE_Va 0x0000000000004000ULL
#define TYPE_Ft 0x0000000000008000ULL
#define TYPE_Tn 0x0000000000010000ULL
#define TYPE_Er 0x0000000000020000ULL
#define TYPE_Ev 0x0000000000040000ULL
#define TYPE_Sy 0x0000000000080000ULL
#define TYPE_Sh 0x0000000000100000ULL
#define TYPE_In 0x0000000000200000ULL
#define TYPE_Ss 0x0000000000400000ULL
#define TYPE_Ox 0x0000000000800000ULL
#define TYPE_An 0x0000000001000000ULL
#define TYPE_Mt 0x0000000002000000ULL
#define TYPE_St 0x0000000004000000ULL
#define TYPE_Bx 0x0000000008000000ULL
#define TYPE_At 0x0000000010000000ULL
#define TYPE_Nx 0x0000000020000000ULL
#define TYPE_Fx 0x0000000040000000ULL
#define TYPE_Lk 0x0000000080000000ULL
#define TYPE_Ms 0x0000000100000000ULL
#define TYPE_Bsx 0x0000000200000000ULL
#define TYPE_Dx 0x0000000400000000ULL
#define TYPE_Rs 0x0000000800000000ULL
#define TYPE_Vt 0x0000001000000000ULL
#define TYPE_Lb 0x0000002000000000ULL
#define TYPE_Nm 0x0000004000000000ULL
#define TYPE_Nd 0x0000008000000000ULL
#define NAME_SYN 0x0000004000000001ULL
#define NAME_FIRST 0x0000004000000004ULL
#define NAME_TITLE 0x0000004000000006ULL
#define NAME_HEAD 0x0000004000000008ULL
#define NAME_FILE 0x0000004000000010ULL
#define NAME_MASK 0x000000000000001fULL
#define FORM_CAT 0 /* manual page is preformatted */
#define FORM_SRC 1 /* format is mdoc(7) or man(7) */
#define FORM_NONE 4 /* format is unknown */
enum argmode {
ARG_FILE = 0,
ARG_NAME,
ARG_WORD,
ARG_EXPR
};
struct manpage {
char *file; /* to be prefixed by manpath */
char *names; /* a list of names with sections */
char *output; /* user-defined additional output */
size_t ipath; /* number of the manpath */
uint64_t bits; /* name type mask */
int sec; /* section number, 10 means invalid */
int form; /* 0 == catpage */
};
struct mansearch {
const char *arch; /* architecture/NULL */
const char *sec; /* mansection/NULL */
const char *outkey; /* show content of this macro */
enum argmode argmode; /* interpretation of arguments */
int firstmatch; /* first matching database only */
};
__BEGIN_DECLS
+struct manpaths;
+
int mansearch_setup(int);
int mansearch(const struct mansearch *cfg, /* options */
const struct manpaths *paths, /* manpaths */
int argc, /* size of argv */
char *argv[], /* search terms */
struct manpage **res, /* results */
size_t *ressz); /* results returned */
void mansearch_free(struct manpage *, size_t);
__END_DECLS
-
-#endif /* MANSEARCH_H */
Index: vendor/mdocml/dist/mansearch_const.c
===================================================================
--- vendor/mdocml/dist/mansearch_const.c (revision 276215)
+++ vendor/mdocml/dist/mansearch_const.c (revision 276216)
@@ -1,34 +1,33 @@
-/* $Id: mansearch_const.c,v 1.6 2014/08/10 23:54:41 schwarze Exp $ */
+/* $Id: mansearch_const.c,v 1.7 2014/12/01 08:05:52 schwarze Exp $ */
/*
* Copyright (c) 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
-#include "manpath.h"
#include "mansearch.h"
const int mansearch_keymax = 40;
const char *const mansearch_keynames[40] = {
"arch", "sec", "Xr", "Ar", "Fa", "Fl", "Dv", "Fn",
"Ic", "Pa", "Cm", "Li", "Em", "Cd", "Va", "Ft",
"Tn", "Er", "Ev", "Sy", "Sh", "In", "Ss", "Ox",
"An", "Mt", "St", "Bx", "At", "Nx", "Fx", "Lk",
"Ms", "Bsx", "Dx", "Rs", "Vt", "Lb", "Nm", "Nd"
};
Index: vendor/mdocml/dist/mdoc.7
===================================================================
--- vendor/mdocml/dist/mdoc.7 (revision 276215)
+++ vendor/mdocml/dist/mdoc.7 (revision 276216)
@@ -1,3312 +1,3302 @@
-.\" $Id: mdoc.7,v 1.244 2014/11/28 18:36:35 schwarze Exp $
+.\" $Id: mdoc.7,v 1.245 2014/11/30 21:56:18 schwarze Exp $
.\"
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons
.\" Copyright (c) 2010, 2011, 2013 Ingo Schwarze
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: November 28 2014 $
+.Dd $Mdocdate: November 30 2014 $
.Dt MDOC 7
.Os
.Sh NAME
.Nm mdoc
.Nd semantic markup language for formatting manual pages
.Sh DESCRIPTION
The
.Nm mdoc
language supports authoring of manual pages for the
.Xr man 1
utility by allowing semantic annotations of words, phrases,
page sections and complete manual pages.
Such annotations are used by formatting tools to achieve a uniform
presentation across all manuals written in
.Nm ,
and to support hyperlinking if supported by the output medium.
.Pp
This reference document describes the structure of manual pages
and the syntax and usage of the
.Nm
language.
The reference implementation of a parsing and formatting tool is
.Xr mandoc 1 ;
the
.Sx COMPATIBILITY
section describes compatibility with other implementations.
.Pp
In an
.Nm
document, lines beginning with the control character
.Sq \&.
are called
.Dq macro lines .
The first word is the macro name.
It consists of two or three letters.
Most macro names begin with a capital letter.
For a list of available macros, see
.Sx MACRO OVERVIEW .
The words following the macro name are arguments to the macro, optionally
including the names of other, callable macros; see
.Sx MACRO SYNTAX
for details.
.Pp
Lines not beginning with the control character are called
.Dq text lines .
They provide free-form text to be printed; the formatting of the text
depends on the respective processing context:
.Bd -literal -offset indent
\&.Sh Macro lines change control state.
Text lines are interpreted within the current state.
.Ed
.Pp
Many aspects of the basic syntax of the
.Nm
language are based on the
.Xr roff 7
language; see the
.Em LANGUAGE SYNTAX
and
.Em MACRO SYNTAX
sections in the
.Xr roff 7
manual for details, in particular regarding
comments, escape sequences, whitespace, and quoting.
However, using
.Xr roff 7
requests in
.Nm
documents is discouraged;
.Xr mandoc 1
supports some of them merely for backward compatibility.
.Sh MANUAL STRUCTURE
A well-formed
.Nm
document consists of a document prologue followed by one or more
sections.
.Pp
The prologue, which consists of the
.Sx \&Dd ,
.Sx \&Dt ,
and
.Sx \&Os
macros in that order, is required for every document.
.Pp
The first section (sections are denoted by
.Sx \&Sh )
must be the NAME section, consisting of at least one
.Sx \&Nm
followed by
.Sx \&Nd .
.Pp
Following that, convention dictates specifying at least the
.Em SYNOPSIS
and
.Em DESCRIPTION
sections, although this varies between manual sections.
.Pp
The following is a well-formed skeleton
.Nm
file for a utility
.Qq progname :
.Bd -literal -offset indent
\&.Dd $\&Mdocdate$
\&.Dt PROGNAME section
\&.Os
\&.Sh NAME
\&.Nm progname
\&.Nd one line about what it does
\&.\e\(dq .Sh LIBRARY
\&.\e\(dq For sections 2, 3, and 9 only.
\&.\e\(dq Not used in OpenBSD.
\&.Sh SYNOPSIS
\&.Nm progname
\&.Op Fl options
\&.Ar
\&.Sh DESCRIPTION
The
\&.Nm
utility processes files ...
\&.\e\(dq .Sh CONTEXT
\&.\e\(dq For section 9 functions only.
\&.\e\(dq .Sh IMPLEMENTATION NOTES
\&.\e\(dq Not used in OpenBSD.
\&.\e\(dq .Sh RETURN VALUES
\&.\e\(dq For sections 2, 3, and 9 function return values only.
\&.\e\(dq .Sh ENVIRONMENT
\&.\e\(dq For sections 1, 6, 7, and 8 only.
\&.\e\(dq .Sh FILES
\&.\e\(dq .Sh EXIT STATUS
\&.\e\(dq For sections 1, 6, and 8 only.
\&.\e\(dq .Sh EXAMPLES
\&.\e\(dq .Sh DIAGNOSTICS
\&.\e\(dq For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only.
\&.\e\(dq .Sh ERRORS
\&.\e\(dq For sections 2, 3, 4, and 9 errno settings only.
\&.\e\(dq .Sh SEE ALSO
\&.\e\(dq .Xr foobar 1
\&.\e\(dq .Sh STANDARDS
\&.\e\(dq .Sh HISTORY
\&.\e\(dq .Sh AUTHORS
\&.\e\(dq .Sh CAVEATS
\&.\e\(dq .Sh BUGS
\&.\e\(dq .Sh SECURITY CONSIDERATIONS
\&.\e\(dq Not used in OpenBSD.
.Ed
.Pp
The sections in an
.Nm
document are conventionally ordered as they appear above.
Sections should be composed as follows:
.Bl -ohang -offset Ds
.It Em NAME
The name(s) and a one line description of the documented material.
The syntax for this as follows:
.Bd -literal -offset indent
\&.Nm name0 ,
\&.Nm name1 ,
\&.Nm name2
\&.Nd a one line description
.Ed
.Pp
Multiple
.Sq \&Nm
names should be separated by commas.
.Pp
The
.Sx \&Nm
macro(s) must precede the
.Sx \&Nd
macro.
.Pp
See
.Sx \&Nm
and
.Sx \&Nd .
.It Em LIBRARY
The name of the library containing the documented material, which is
assumed to be a function in a section 2, 3, or 9 manual.
The syntax for this is as follows:
.Bd -literal -offset indent
\&.Lb libarm
.Ed
.Pp
See
.Sx \&Lb .
.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:
.Bd -literal -offset indent
\&.Nm bar
\&.Op Fl v
\&.Op Fl o Ar file
\&.Op Ar
\&.Nm foo
\&.Op Fl v
\&.Op Fl o Ar file
\&.Op Ar
.Ed
.Pp
Commands should be ordered alphabetically.
.Pp
For the second, function calls (sections 2, 3, 9):
.Bd -literal -offset indent
\&.In header.h
\&.Vt extern const char *global;
\&.Ft "char *"
\&.Fn foo "const char *src"
\&.Ft "char *"
\&.Fn bar "const char *src"
.Ed
.Pp
Ordering of
.Sx \&In ,
.Sx \&Vt ,
.Sx \&Fn ,
and
.Sx \&Fo
macros should follow C header-file conventions.
.Pp
And for the third, configurations (section 4):
.Bd -literal -offset indent
\&.Cd \(dqit* at isa? port 0x2e\(dq
\&.Cd \(dqit* at isa? port 0x4e\(dq
.Ed
.Pp
Manuals not in these sections generally don't need a
.Em SYNOPSIS .
.Pp
Some macros are displayed differently in the
.Em SYNOPSIS
section, particularly
.Sx \&Nm ,
.Sx \&Cd ,
.Sx \&Fd ,
.Sx \&Fn ,
.Sx \&Fo ,
.Sx \&In ,
.Sx \&Vt ,
and
.Sx \&Ft .
All of these macros are output on their own line.
If two such dissimilar macros are pairwise invoked (except for
.Sx \&Ft
before
.Sx \&Fo
or
.Sx \&Fn ) ,
they are separated by a vertical space, unless in the case of
.Sx \&Fo ,
.Sx \&Fn ,
and
.Sx \&Ft ,
which are always separated by vertical space.
.Pp
When text and macros following an
.Sx \&Nm
macro starting an input line span multiple output lines,
all output lines but the first will be indented to align
with the text immediately following the
.Sx \&Nm
macro, up to the next
.Sx \&Nm ,
.Sx \&Sh ,
or
.Sx \&Ss
macro or the end of an enclosing block, whichever comes first.
.It Em DESCRIPTION
This begins with an expansion of the brief, one line description in
.Em NAME :
.Bd -literal -offset indent
The
\&.Nm
utility does this, that, and the other.
.Ed
.Pp
It usually follows with a breakdown of the options (if documenting a
command), such as:
.Bd -literal -offset indent
The arguments are as follows:
\&.Bl \-tag \-width Ds
\&.It Fl v
Print verbose information.
\&.El
.Ed
.Pp
Manuals not documenting a command won't include the above fragment.
.Pp
Since the
.Em DESCRIPTION
section usually contains most of the text of a manual, longer manuals
often use the
.Sx \&Ss
macro to form subsections.
In very long manuals, the
.Em DESCRIPTION
may be split into multiple sections, each started by an
.Sx \&Sh
macro followed by a non-standard section name, and each having
several subsections, like in the present
.Nm
manual.
.It Em CONTEXT
This section lists the contexts in which functions can be called in section 9.
The contexts are autoconf, process, or interrupt.
.It Em IMPLEMENTATION NOTES
Implementation-specific notes should be kept here.
This is useful when implementing standard functions that may have side
effects or notable algorithmic implications.
.It Em RETURN VALUES
This section documents the
return values of functions in sections 2, 3, and 9.
.Pp
See
.Sx \&Rv .
.It Em ENVIRONMENT
Lists the environment variables used by the utility,
and explains the syntax and semantics of their values.
The
.Xr environ 7
manual provides examples of typical content and formatting.
.Pp
See
.Sx \&Ev .
.It Em FILES
Documents files used.
It's helpful to document both the file name and a short description of how
the file is used (created, modified, etc.).
.Pp
See
.Sx \&Pa .
.It Em EXIT STATUS
This section documents the
command exit status for section 1, 6, and 8 utilities.
Historically, this information was described in
.Em DIAGNOSTICS ,
a practise that is now discouraged.
.Pp
See
.Sx \&Ex .
.It Em EXAMPLES
Example usages.
This often contains snippets of well-formed, well-tested invocations.
Make sure that examples work properly!
.It Em DIAGNOSTICS
Documents error messages.
In section 4 and 9 manuals, these are usually messages printed by the
kernel to the console and to the kernel log.
In section 1, 6, 7, and 8, these are usually messages printed by
userland programs to the standard error output.
.Pp
Historically, this section was used in place of
.Em EXIT STATUS
for manuals in sections 1, 6, and 8; however, this practise is
discouraged.
.Pp
See
.Sx \&Bl
.Fl diag .
.It Em ERRORS
Documents
.Xr errno 2
settings in sections 2, 3, 4, and 9.
.Pp
See
.Sx \&Er .
.It Em SEE ALSO
References other manuals with related topics.
This section should exist for most manuals.
Cross-references should conventionally be ordered first by section, then
alphabetically (ignoring case).
.Pp
References to other documentation concerning the topic of the manual page,
for example authoritative books or journal articles, may also be
provided in this section.
.Pp
See
.Sx \&Rs
and
.Sx \&Xr .
.It Em STANDARDS
References any standards implemented or used.
If not adhering to any standards, the
.Em HISTORY
section should be used instead.
.Pp
See
.Sx \&St .
.It Em HISTORY
A brief history of the subject, including where it was first implemented,
and when it was ported to or reimplemented for the operating system at hand.
.It Em AUTHORS
Credits to the person or persons who wrote the code and/or documentation.
Authors should generally be noted by both name and email address.
.Pp
See
.Sx \&An .
.It Em CAVEATS
Common misuses and misunderstandings should be explained
in this section.
.It Em BUGS
Known bugs, limitations, and work-arounds should be described
in this section.
.It Em SECURITY CONSIDERATIONS
Documents any security precautions that operators should consider.
.El
.Sh MACRO OVERVIEW
This overview is sorted such that macros of similar purpose are listed
together, to help find the best macro for any given purpose.
Deprecated macros are not included in the overview, but can be found below
in the alphabetical
.Sx MACRO REFERENCE .
.Ss Document preamble and NAME section macros
.Bl -column "Brq, Bro, Brc" description
.It Sx \&Dd Ta document date: Cm $\&Mdocdate$ | Ar month day , year
.It Sx \&Dt Ta document title: Ar TITLE section Op Ar arch
.It Sx \&Os Ta operating system version: Op Ar system Op Ar version
.It Sx \&Nm Ta document name (one argument)
.It Sx \&Nd Ta document description (one line)
.El
.Ss Sections and cross references
.Bl -column "Brq, Bro, Brc" description
.It Sx \&Sh Ta section header (one line)
.It Sx \&Ss Ta subsection header (one line)
.It Sx \&Sx Ta internal cross reference to a section or subsection
.It Sx \&Xr Ta cross reference to another manual page: Ar name section
.It Sx \&Pp , \&Lp Ta start a text paragraph (no arguments)
.El
.Ss Displays and lists
.Bl -column "Brq, Bro, Brc" description
.It Sx \&Bd , \&Ed Ta display block:
.Fl Ar type
.Op Fl offset Ar width
.Op Fl compact
.It Sx \&D1 Ta indented display (one line)
.It Sx \&Dl Ta indented literal display (one line)
.It Sx \&Bl , \&El Ta list block:
.Fl Ar type
.Op Fl width Ar val
.Op Fl offset Ar val
.Op Fl compact
.It Sx \&It Ta list item (syntax depends on Fl Ar type )
.It Sx \&Ta Ta table cell separator in Sx \&Bl Fl column No lists
.It Sx \&Rs , \&%* , \&Re Ta bibliographic block (references)
.El
.Ss Spacing control
.Bl -column "Brq, Bro, Brc" description
.It Sx \&Pf Ta prefix, no following horizontal space (one argument)
.It Sx \&Ns Ta roman font, no preceding horizontal space (no arguments)
.It Sx \&Ap Ta apostrophe without surrounding whitespace (no arguments)
.It Sx \&Sm Ta switch horizontal spacing mode: Op Cm on | off
.It Sx \&Bk , \&Ek Ta keep block: Fl words
.It Sx \&br Ta force output line break in text mode (no arguments)
.It Sx \&sp Ta force vertical space: Op Ar height
.El
.Ss Semantic markup for command line utilities:
.Bl -column "Brq, Bro, Brc" description
.It Sx \&Nm Ta start a SYNOPSIS block with the name of a utility
.It Sx \&Fl Ta command line options (flags) (>=0 arguments)
.It Sx \&Cm Ta command modifier (>0 arguments)
.It Sx \&Ar Ta command arguments (>=0 arguments)
.It Sx \&Op , \&Oo , \&Oc Ta optional syntax elements (enclosure)
.It Sx \&Ic Ta internal or interactive command (>0 arguments)
.It Sx \&Ev Ta environmental variable (>0 arguments)
.It Sx \&Pa Ta file system path (>=0 arguments)
.El
.Ss Semantic markup for function libraries:
.Bl -column "Brq, Bro, Brc" description
.It Sx \&Lb Ta function library (one argument)
.It Sx \&In Ta include file (one argument)
.It Sx \&Fd Ta other preprocessor directive (>0 arguments)
.It Sx \&Ft Ta function type (>0 arguments)
.It Sx \&Fo , \&Fc Ta function block: Ar funcname
.It Sx \&Fn Ta function name:
.Op Ar functype
.Ar funcname
.Oo
.Op Ar argtype
.Ar argname
.Oc
.It Sx \&Fa Ta function argument (>0 arguments)
.It Sx \&Vt Ta variable type (>0 arguments)
.It Sx \&Va Ta variable name (>0 arguments)
.It Sx \&Dv Ta defined variable or preprocessor constant (>0 arguments)
.It Sx \&Er Ta error constant (>0 arguments)
.It Sx \&Ev Ta environmental variable (>0 arguments)
.El
.Ss Various semantic markup:
.Bl -column "Brq, Bro, Brc" description
.It Sx \&An Ta author name (>0 arguments)
.It Sx \&Lk Ta hyperlink: Ar uri Op Ar name
.It Sx \&Mt Ta Do mailto Dc hyperlink: Ar address
.It Sx \&Cd Ta kernel configuration declaration (>0 arguments)
.It Sx \&Ad Ta memory address (>0 arguments)
.It Sx \&Ms Ta mathematical symbol (>0 arguments)
.El
.Ss Physical markup
.Bl -column "Brq, Bro, Brc" description
.It Sx \&Em Ta italic font or underline (emphasis) (>0 arguments)
.It Sx \&Sy Ta boldface font (symbolic) (>0 arguments)
.It Sx \&Li Ta typewriter font (literal) (>0 arguments)
.It Sx \&No Ta return to roman font (normal) (no arguments)
.It Sx \&Bf , \&Ef Ta font block:
.Op Fl Ar type | Cm \&Em | \&Li | \&Sy
.El
.Ss Physical enclosures
.Bl -column "Brq, Bro, Brc" description
.It Sx \&Dq , \&Do , \&Dc Ta enclose in typographic double quotes: Dq text
.It Sx \&Qq , \&Qo , \&Qc Ta enclose in typewriter double quotes: Qq text
.It Sx \&Sq , \&So , \&Sc Ta enclose in single quotes: Sq text
.It Sx \&Ql Ta single-quoted literal text: Ql text
.It Sx \&Pq , \&Po , \&Pc Ta enclose in parentheses: Pq text
.It Sx \&Bq , \&Bo , \&Bc Ta enclose in square brackets: Bq text
.It Sx \&Brq , \&Bro , \&Brc Ta enclose in curly braces: Brq text
.It Sx \&Aq , \&Ao , \&Ac Ta enclose in angle brackets: Aq text
.It Sx \&Eo , \&Ec Ta generic enclosure
.El
.Ss Text production
.Bl -column "Brq, Bro, Brc" description
.It Sx \&Ex Fl std Ta standard command exit values: Op Ar utility ...
.It Sx \&Rv Fl std Ta standard function return values: Op Ar function ...
.It Sx \&St Ta reference to a standards document (one argument)
.It Sx \&At Ta At
.It Sx \&Bx Ta Bx
.It Sx \&Bsx Ta Bsx
.It Sx \&Nx Ta Nx
.It Sx \&Fx Ta Fx
.It Sx \&Ox Ta Ox
.It Sx \&Dx Ta Dx
.El
.Sh MACRO REFERENCE
This section is a canonical reference of all macros, arranged
alphabetically.
For the scoping of individual macros, see
.Sx MACRO SYNTAX .
.Ss \&%A
Author name of an
.Sx \&Rs
block.
Multiple authors should each be accorded their own
.Sx \%%A
line.
Author names should be ordered with full or abbreviated forename(s)
first, then full surname.
.Ss \&%B
Book title of an
.Sx \&Rs
block.
This macro may also be used in a non-bibliographic context when
referring to book titles.
.Ss \&%C
Publication city or location of an
.Sx \&Rs
block.
.Ss \&%D
Publication date of an
.Sx \&Rs
block.
Recommended formats of arguments are
.Ar month day , year
or just
.Ar year .
.Ss \&%I
Publisher or issuer name of an
.Sx \&Rs
block.
.Ss \&%J
Journal name of an
.Sx \&Rs
block.
.Ss \&%N
Issue number (usually for journals) of an
.Sx \&Rs
block.
.Ss \&%O
Optional information of an
.Sx \&Rs
block.
.Ss \&%P
Book or journal page number of an
.Sx \&Rs
block.
.Ss \&%Q
Institutional author (school, government, etc.) of an
.Sx \&Rs
block.
Multiple institutional authors should each be accorded their own
.Sx \&%Q
line.
.Ss \&%R
Technical report name of an
.Sx \&Rs
block.
.Ss \&%T
Article title of an
.Sx \&Rs
block.
This macro may also be used in a non-bibliographical context when
referring to article titles.
.Ss \&%U
URI of reference document.
.Ss \&%V
Volume number of an
.Sx \&Rs
block.
.Ss \&Ac
Close an
.Sx \&Ao
block.
Does not have any tail arguments.
.Ss \&Ad
Memory address.
Do not use this for postal addresses.
.Pp
Examples:
.Dl \&.Ad [0,$]
.Dl \&.Ad 0x00000000
.Ss \&An
Author name.
Can be used both for the authors of the program, function, or driver
documented in the manual, or for the authors of the manual itself.
Requires either the name of an author or one of the following arguments:
.Pp
.Bl -tag -width "-nosplitX" -offset indent -compact
.It Fl split
Start a new output line before each subsequent invocation of
.Sx \&An .
.It Fl nosplit
The opposite of
.Fl split .
.El
.Pp
The default is
.Fl nosplit .
The effect of selecting either of the
.Fl split
modes ends at the beginning of the
.Em AUTHORS
section.
In the
.Em AUTHORS
section, the default is
.Fl nosplit
for the first author listing and
.Fl split
for all other author listings.
.Pp
Examples:
.Dl \&.An -nosplit
.Dl \&.An Kristaps Dzonsons \&Aq \&Mt kristaps@bsd.lv
.Ss \&Ao
Begin a block enclosed by angle brackets.
Does not have any head arguments.
.Pp
Examples:
.Dl \&.Fl -key= \&Ns \&Ao \&Ar val \&Ac
.Pp
See also
.Sx \&Aq .
.Ss \&Ap
Inserts an apostrophe without any surrounding whitespace.
This is generally used as a grammatical device when referring to the verb
form of a function.
.Pp
Examples:
.Dl \&.Fn execve \&Ap d
.Ss \&Aq
Encloses its arguments in angle brackets.
.Pp
Examples:
.Dl \&.Fl -key= \&Ns \&Aq \&Ar val
.Pp
.Em Remarks :
this macro is often abused for rendering URIs, which should instead use
.Sx \&Lk
or
.Sx \&Mt ,
or to note pre-processor
.Dq Li #include
statements, which should use
.Sx \&In .
.Pp
See also
.Sx \&Ao .
.Ss \&Ar
Command arguments.
If an argument is not provided, the string
.Dq file ...\&
is used as a default.
.Pp
Examples:
.Dl ".Fl o Ar file"
.Dl ".Ar"
.Dl ".Ar arg1 , arg2 ."
.Pp
The arguments to the
.Sx \&Ar
macro are names and placeholders for command arguments;
for fixed strings to be passed verbatim as arguments, use
.Sx \&Fl
or
.Sx \&Cm .
.Ss \&At
Formats an
.At
version.
Accepts one optional argument:
.Pp
.Bl -tag -width "v[1-7] | 32vX" -offset indent -compact
.It Cm v[1-7] | 32v
A version of
.At .
.It Cm III
.At III .
.It Cm V[.[1-4]]?
A version of
.At V .
.El
.Pp
Note that these arguments do not begin with a hyphen.
.Pp
Examples:
.Dl \&.At
.Dl \&.At III
.Dl \&.At V.1
.Pp
See also
.Sx \&Bsx ,
.Sx \&Bx ,
.Sx \&Dx ,
.Sx \&Fx ,
.Sx \&Nx ,
and
.Sx \&Ox .
.Ss \&Bc
Close a
.Sx \&Bo
block.
Does not have any tail arguments.
.Ss \&Bd
Begin a display block.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Bd
.Fl Ns Ar type
.Op Fl offset Ar width
.Op Fl compact
.Ed
.Pp
Display blocks are used to select a different indentation and
justification than the one used by the surrounding text.
They may contain both macro lines and text lines.
By default, a display block is preceded by a vertical space.
.Pp
The
.Ar type
must be one of the following:
.Bl -tag -width 13n -offset indent
.It Fl centered
Produce one output line from each input line, and centre-justify each line.
Using this display type is not recommended; many
.Nm
implementations render it poorly.
.It Fl filled
Change the positions of line breaks to fill each line, and left- and
right-justify the resulting block.
.It Fl literal
Produce one output line from each input line,
and do not justify the block at all.
Preserve white space as it appears in the input.
Always use a constant-width font.
Use this for displaying source code.
.It Fl ragged
Change the positions of line breaks to fill each line, and left-justify
the resulting block.
.It Fl unfilled
The same as
.Fl literal ,
but using the same font as for normal text, which is a variable width font
if supported by the output device.
.El
.Pp
The
.Ar type
must be provided first.
Additional arguments may follow:
.Bl -tag -width 13n -offset indent
.It Fl offset Ar width
Indent the display by the
.Ar width ,
which may be one of the following:
.Bl -item
.It
One of the pre-defined strings
.Cm indent ,
the width of a standard indentation (six constant width characters);
.Cm indent-two ,
twice
.Cm indent ;
.Cm left ,
which has no effect;
.Cm right ,
which justifies to the right margin; or
.Cm center ,
which aligns around an imagined centre axis.
.It
A macro invocation, which selects a predefined width
associated with that macro.
The most popular is the imaginary macro
.Ar \&Ds ,
which resolves to
.Sy 6n .
.It
A scaling width as described in
.Xr roff 7 .
.It
An arbitrary string, which indents by the length of this string.
.El
.Pp
When the argument is missing,
.Fl offset
is ignored.
.It Fl compact
Do not assert vertical space before the display.
.El
.Pp
Examples:
.Bd -literal -offset indent
\&.Bd \-literal \-offset indent \-compact
Hello world.
\&.Ed
.Ed
.Pp
See also
.Sx \&D1
and
.Sx \&Dl .
.Ss \&Bf
Change the font mode for a scoped block of text.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Bf
.Oo
.Fl emphasis | literal | symbolic |
.Cm \&Em | \&Li | \&Sy
.Oc
.Ed
.Pp
The
.Fl emphasis
and
.Cm \&Em
argument are equivalent, as are
.Fl symbolic
and
.Cm \&Sy ,
and
.Fl literal
and
.Cm \&Li .
Without an argument, this macro does nothing.
The font mode continues until broken by a new font mode in a nested
scope or
.Sx \&Ef
is encountered.
.Pp
See also
.Sx \&Li ,
.Sx \&Ef ,
.Sx \&Em ,
and
.Sx \&Sy .
.Ss \&Bk
For each macro, keep its output together on the same output line,
until the end of the macro or the end of the input line is reached,
whichever comes first.
Line breaks in text lines are unaffected.
The syntax is as follows:
.Pp
.D1 Pf \. Sx \&Bk Fl words
.Pp
The
.Fl words
argument is required; additional arguments are ignored.
.Pp
The following example will not break within each
.Sx \&Op
macro line:
.Bd -literal -offset indent
\&.Bk \-words
\&.Op Fl f Ar flags
\&.Op Fl o Ar output
\&.Ek
.Ed
.Pp
Be careful in using over-long lines within a keep block!
Doing so will clobber the right margin.
.Ss \&Bl
Begin a list.
Lists consist of items specified using the
.Sx \&It
macro, containing a head or a body or both.
The list syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Bl
.Fl Ns Ar type
.Op Fl width Ar val
.Op Fl offset Ar val
.Op Fl compact
.Op HEAD ...
.Ed
.Pp
The list
.Ar type
is mandatory and must be specified first.
The
.Fl width
and
.Fl offset
arguments accept macro names as described for
.Sx \&Bd
.Fl offset ,
scaling widths as described in
.Xr roff 7 ,
or use the length of the given string.
The
.Fl offset
is a global indentation for the whole list, affecting both item heads
and bodies.
For those list types supporting it, the
.Fl width
argument requests an additional indentation of item bodies,
to be added to the
.Fl offset .
Unless the
.Fl compact
argument is specified, list entries are separated by vertical space.
.Pp
A list must specify one of the following list types:
.Bl -tag -width 12n -offset indent
.It Fl bullet
No item heads can be specified, but a bullet will be printed at the head
of each item.
Item bodies start on the same output line as the bullet
and are indented according to the
.Fl width
argument.
.It Fl column
A columnated list.
The
.Fl width
argument has no effect; instead, each argument specifies the width
of one column, using either the scaling width syntax described in
.Xr roff 7
or the string length of the argument.
If the first line of the body of a
.Fl column
list is not an
.Sx \&It
macro line,
.Sx \&It
contexts spanning one input line each are implied until an
.Sx \&It
macro line is encountered, at which point items start being interpreted as
described in the
.Sx \&It
documentation.
.It Fl dash
Like
.Fl bullet ,
except that dashes are used in place of bullets.
.It Fl diag
Like
.Fl inset ,
except that item heads are not parsed for macro invocations.
Most often used in the
.Em DIAGNOSTICS
section with error constants in the item heads.
.It Fl enum
A numbered list.
No item heads can be specified.
Formatted like
.Fl bullet ,
except that cardinal numbers are used in place of bullets,
starting at 1.
.It Fl hang
Like
.Fl tag ,
except that the first lines of item bodies are not indented, but follow
the item heads like in
.Fl inset
lists.
.It Fl hyphen
Synonym for
.Fl dash .
.It Fl inset
Item bodies follow items heads on the same line, using normal inter-word
spacing.
Bodies are not indented, and the
.Fl width
argument is ignored.
.It Fl item
No item heads can be specified, and none are printed.
Bodies are not indented, and the
.Fl width
argument is ignored.
.It Fl ohang
Item bodies start on the line following item heads and are not indented.
The
.Fl width
argument is ignored.
.It Fl tag
Item bodies are indented according to the
.Fl width
argument.
When an item head fits inside the indentation, the item body follows
this head on the same output line.
Otherwise, the body starts on the output line following the head.
.El
.Pp
Lists may be nested within lists and displays.
Nesting of
.Fl column
and
.Fl enum
lists may not be portable.
.Pp
See also
.Sx \&El
and
.Sx \&It .
.Ss \&Bo
Begin a block enclosed by square brackets.
Does not have any head arguments.
.Pp
Examples:
.Bd -literal -offset indent -compact
\&.Bo 1 ,
\&.Dv BUFSIZ \&Bc
.Ed
.Pp
See also
.Sx \&Bq .
.Ss \&Bq
Encloses its arguments in square brackets.
.Pp
Examples:
.Dl \&.Bq 1 , \&Dv BUFSIZ
.Pp
.Em Remarks :
this macro is sometimes abused to emulate optional arguments for
commands; the correct macros to use for this purpose are
.Sx \&Op ,
.Sx \&Oo ,
and
.Sx \&Oc .
.Pp
See also
.Sx \&Bo .
.Ss \&Brc
Close a
.Sx \&Bro
block.
Does not have any tail arguments.
.Ss \&Bro
Begin a block enclosed by curly braces.
Does not have any head arguments.
.Pp
Examples:
.Bd -literal -offset indent -compact
\&.Bro 1 , ... ,
\&.Va n \&Brc
.Ed
.Pp
See also
.Sx \&Brq .
.Ss \&Brq
Encloses its arguments in curly braces.
.Pp
Examples:
.Dl \&.Brq 1 , ... , \&Va n
.Pp
See also
.Sx \&Bro .
.Ss \&Bsx
Format the
.Bsx
version provided as an argument, or a default value if
no argument is provided.
.Pp
Examples:
.Dl \&.Bsx 1.0
.Dl \&.Bsx
.Pp
See also
.Sx \&At ,
.Sx \&Bx ,
.Sx \&Dx ,
.Sx \&Fx ,
.Sx \&Nx ,
and
.Sx \&Ox .
.Ss \&Bt
Supported only for compatibility, do not use this in new manuals.
Prints
.Dq is currently in beta test.
.Ss \&Bx
Format the
.Bx
version provided as an argument, or a default value if no
argument is provided.
.Pp
Examples:
.Dl \&.Bx 4.3 Tahoe
.Dl \&.Bx 4.4
.Dl \&.Bx
.Pp
See also
.Sx \&At ,
.Sx \&Bsx ,
.Sx \&Dx ,
.Sx \&Fx ,
.Sx \&Nx ,
and
.Sx \&Ox .
.Ss \&Cd
Kernel configuration declaration.
This denotes strings accepted by
.Xr config 8 .
It is most often used in section 4 manual pages.
.Pp
Examples:
.Dl \&.Cd device le0 at scode?
.Pp
.Em Remarks :
this macro is commonly abused by using quoted literals to retain
whitespace and align consecutive
.Sx \&Cd
declarations.
This practise is discouraged.
.Ss \&Cm
Command modifiers.
Typically used for fixed strings passed as arguments, unless
.Sx \&Fl
is more appropriate.
Also useful when specifying configuration options or keys.
.Pp
Examples:
.Dl ".Nm mt Fl f Ar device Cm rewind"
.Dl ".Nm ps Fl o Cm pid , Ns Cm command"
.Dl ".Nm dd Cm if= Ns Ar file1 Cm of= Ns Ar file2"
.Dl ".Cm IdentityFile Pa ~/.ssh/id_rsa"
.Dl ".Cm LogLevel Dv DEBUG"
.Ss \&D1
One-line indented display.
This is formatted by the default rules and is useful for simple indented
statements.
It is followed by a newline.
.Pp
Examples:
.Dl \&.D1 \&Fl abcdefgh
.Pp
See also
.Sx \&Bd
and
.Sx \&Dl .
.Ss \&Db
This macro is obsolete.
No replacement is needed.
It is ignored by
.Xr mandoc 1
and groff including its arguments.
It was formerly used to toggle a debugging mode.
.Ss \&Dc
Close a
.Sx \&Do
block.
Does not have any tail arguments.
.Ss \&Dd
Document date for display in the page footer.
This is the mandatory first macro of any
.Nm
manual.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Dd Ar month day , year
.Pp
The
.Ar month
is the full English month name, the
.Ar day
is an optionally zero-padded numeral, and the
.Ar year
is the full four-digit year.
.Pp
Other arguments are not portable; the
.Xr mandoc 1
utility handles them as follows:
.Bl -dash -offset 3n -compact
.It
To have the date automatically filled in by the
.Ox
version of
.Xr cvs 1 ,
the special string
.Dq $\&Mdocdate$
can be given as an argument.
.It
The traditional, purely numeric
.Xr man 7
format
.Ar year Ns \(en Ns Ar month Ns \(en Ns Ar day
is accepted, too.
.It
If a date string cannot be parsed, it is used verbatim.
.It
If no date string is given, the current date is used.
.El
.Pp
Examples:
.Dl \&.Dd $\&Mdocdate$
.Dl \&.Dd $\&Mdocdate: July 21 2007$
.Dl \&.Dd July 21, 2007
.Pp
See also
.Sx \&Dt
and
.Sx \&Os .
.Ss \&Dl
One-line indented display.
This is formatted as literal text and is useful for commands and
invocations.
It is followed by a newline.
.Pp
Examples:
.Dl \&.Dl % mandoc mdoc.7 \e(ba less
.Pp
See also
.Sx \&Bd
and
.Sx \&D1 .
.Ss \&Do
Begin a block enclosed by double quotes.
Does not have any head arguments.
.Pp
Examples:
.Bd -literal -offset indent -compact
\&.Do
April is the cruellest month
\&.Dc
\e(em T.S. Eliot
.Ed
.Pp
See also
.Sx \&Dq .
.Ss \&Dq
Encloses its arguments in
.Dq typographic
double-quotes.
.Pp
Examples:
.Bd -literal -offset indent -compact
\&.Dq April is the cruellest month
\e(em T.S. Eliot
.Ed
.Pp
See also
.Sx \&Qq ,
.Sx \&Sq ,
and
.Sx \&Do .
.Ss \&Dt
Document title for display in the page header.
This is the mandatory second macro of any
.Nm
file.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Dt
.Ar TITLE
.Ar section
.Op Ar arch
.Ed
.Pp
Its arguments are as follows:
.Bl -tag -width section -offset 2n
.It Ar TITLE
The document's title (name), defaulting to
.Dq UNTITLED
if unspecified.
To achieve a uniform appearance of page header lines,
it should by convention be all caps.
.It Ar section
The manual section.
This may be one of
.Cm 1
.Pq utilities ,
.Cm 2
.Pq system calls ,
.Cm 3
.Pq libraries ,
.Cm 3p
.Pq Perl libraries ,
.Cm 4
.Pq devices ,
.Cm 5
.Pq file formats ,
.Cm 6
.Pq games ,
.Cm 7
.Pq miscellaneous ,
.Cm 8
.Pq system utilities ,
.Cm 9
.Pq kernel functions ,
.Cm X11
.Pq X Window System ,
.Cm X11R6
.Pq X Window System ,
.Cm unass
.Pq unassociated ,
.Cm local
.Pq local system ,
.Cm draft
.Pq draft manual ,
or
.Cm paper
.Pq paper .
It should correspond to the manual's filename suffix and defaults to
the empty string if unspecified.
.It Ar arch
This specifies the machine architecture a manual page applies to,
where relevant, for example
.Cm alpha ,
.Cm amd64 ,
.Cm i386 ,
or
.Cm sparc64 .
The list of valid architectures varies by operating system.
.El
.Pp
Examples:
.Dl \&.Dt FOO 1
.Dl \&.Dt FOO 9 i386
.Pp
See also
.Sx \&Dd
and
.Sx \&Os .
.Ss \&Dv
Defined variables such as preprocessor constants, constant symbols,
enumeration values, and so on.
.Pp
Examples:
.Dl \&.Dv NULL
.Dl \&.Dv BUFSIZ
.Dl \&.Dv STDOUT_FILENO
.Pp
See also
.Sx \&Er
and
.Sx \&Ev
for special-purpose constants,
.Sx \&Va
for variable symbols, and
.Sx \&Fd
for listing preprocessor variable definitions in the
.Em SYNOPSIS .
.Ss \&Dx
Format the
.Dx
version provided as an argument, or a default
value if no argument is provided.
.Pp
Examples:
.Dl \&.Dx 2.4.1
.Dl \&.Dx
.Pp
See also
.Sx \&At ,
.Sx \&Bsx ,
.Sx \&Bx ,
.Sx \&Fx ,
.Sx \&Nx ,
and
.Sx \&Ox .
.Ss \&Ec
Close a scope started by
.Sx \&Eo .
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Ec Op Ar TERM
.Pp
The
.Ar TERM
argument is used as the enclosure tail, for example, specifying \e(rq
will emulate
.Sx \&Dc .
.Ss \&Ed
End a display context started by
.Sx \&Bd .
.Ss \&Ef
End a font mode context started by
.Sx \&Bf .
.Ss \&Ek
End a keep context started by
.Sx \&Bk .
.Ss \&El
End a list context started by
.Sx \&Bl .
.Pp
See also
.Sx \&Bl
and
.Sx \&It .
.Ss \&Em
Request an italic font.
If the output device does not provide that, underline.
.Pp
This is most often used for stress emphasis (not to be confused with
importance, see
.Sx \&Sy ) .
In the rare cases where none of the semantic markup macros fit,
it can also be used for technical terms and placeholders, except
that for syntax elements,
.Sx \&Sy
and
.Sx \&Ar
are preferred, respectively.
.Pp
Examples:
.Bd -literal -compact -offset indent
Selected lines are those
\&.Em not
matching any of the specified patterns.
Some of the functions use a
\&.Em hold space
to save the pattern space for subsequent retrieval.
.Ed
.Pp
See also
.Sx \&Bf ,
.Sx \&Li ,
.Sx \&No ,
and
.Sx \&Sy .
.Ss \&En
This macro is obsolete.
Use
.Sx \&Eo
or any of the other enclosure macros.
.Pp
It encloses its argument in the delimiters specified by the last
.Sx \&Es
macro.
.Ss \&Eo
An arbitrary enclosure.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Eo Op Ar TERM
.Pp
The
.Ar TERM
argument is used as the enclosure head, for example, specifying \e(lq
will emulate
.Sx \&Do .
.Ss \&Er
Error constants for definitions of the
.Va errno
libc global variable.
This is most often used in section 2 and 3 manual pages.
.Pp
Examples:
.Dl \&.Er EPERM
.Dl \&.Er ENOENT
.Pp
See also
.Sx \&Dv
for general constants.
.Ss \&Es
This macro is obsolete.
Use
.Sx \&Eo
or any of the other enclosure macros.
.Pp
It takes two arguments, defining the delimiters to be used by subsequent
.Sx \&En
macros.
.Ss \&Ev
Environmental variables such as those specified in
.Xr environ 7 .
.Pp
Examples:
.Dl \&.Ev DISPLAY
.Dl \&.Ev PATH
.Pp
See also
.Sx \&Dv
for general constants.
.Ss \&Ex
Insert a standard sentence regarding command exit values of 0 on success
and >0 on failure.
This is most often used in section 1, 6, and 8 manual pages.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Ex Fl std Op Ar utility ...
.Pp
If
.Ar utility
is not specified, the document's name set by
.Sx \&Nm
is used.
Multiple
.Ar utility
arguments are treated as separate utilities.
.Pp
See also
.Sx \&Rv .
.Ss \&Fa
Function argument or parameter.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Fa
.Qo
.Op Ar argtype
.Op Ar argname
.Qc Ar \&...
.Ed
.Pp
Each argument may be a name and a type (recommended for the
.Em SYNOPSIS
section), a name alone (for function invocations),
or a type alone (for function prototypes).
If both a type and a name are given or if the type consists of multiple
words, all words belonging to the same function argument have to be
given in a single argument to the
.Sx \&Fa
macro.
.Pp
This macro is also used to specify the field name of a structure.
.Pp
Most often, the
.Sx \&Fa
macro is used in the
.Em SYNOPSIS
within
.Sx \&Fo
blocks when documenting multi-line function prototypes.
If invoked with multiple arguments, the arguments are separated by a
comma.
Furthermore, if the following macro is another
.Sx \&Fa ,
the last argument will also have a trailing comma.
.Pp
Examples:
.Dl \&.Fa \(dqconst char *p\(dq
.Dl \&.Fa \(dqint a\(dq \(dqint b\(dq \(dqint c\(dq
.Dl \&.Fa \(dqchar *\(dq size_t
.Pp
See also
.Sx \&Fo .
.Ss \&Fc
End a function context started by
.Sx \&Fo .
.Ss \&Fd
Preprocessor directive, in particular for listing it in the
.Em SYNOPSIS .
Historically, it was also used to document include files.
The latter usage has been deprecated in favour of
.Sx \&In .
.Pp
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Fd
.Li # Ns Ar directive
.Op Ar argument ...
.Ed
.Pp
Examples:
.Dl \&.Fd #define sa_handler __sigaction_u.__sa_handler
.Dl \&.Fd #define SIO_MAXNFDS
.Dl \&.Fd #ifdef FS_DEBUG
.Dl \&.Ft void
.Dl \&.Fn dbg_open \(dqconst char *\(dq
.Dl \&.Fd #endif
.Pp
See also
.Sx MANUAL STRUCTURE ,
.Sx \&In ,
and
.Sx \&Dv .
.Ss \&Fl
Command-line flag or option.
Used when listing arguments to command-line utilities.
Prints a fixed-width hyphen
.Sq \-
directly followed by each argument.
If no arguments are provided, a hyphen is printed followed by a space.
If the argument is a macro, a hyphen is prefixed to the subsequent macro
output.
.Pp
Examples:
.Dl ".Fl R Op Fl H | L | P"
.Dl ".Op Fl 1AaCcdFfgHhikLlmnopqRrSsTtux"
.Dl ".Fl type Cm d Fl name Pa CVS"
.Dl ".Fl Ar signal_number"
.Dl ".Fl o Fl"
.Pp
See also
.Sx \&Cm .
.Ss \&Fn
A function name.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Ns Sx \&Fn
.Op Ar functype
.Ar funcname
.Op Oo Ar argtype Oc Ar argname
.Ed
.Pp
Function arguments are surrounded in parenthesis and
are delimited by commas.
If no arguments are specified, blank parenthesis are output.
In the
.Em SYNOPSIS
section, this macro starts a new output line,
and a blank line is automatically inserted between function definitions.
.Pp
Examples:
.Dl \&.Fn \(dqint funcname\(dq \(dqint arg0\(dq \(dqint arg1\(dq
.Dl \&.Fn funcname \(dqint arg0\(dq
.Dl \&.Fn funcname arg0
.Pp
.Bd -literal -offset indent -compact
\&.Ft functype
\&.Fn funcname
.Ed
.Pp
When referring to a function documented in another manual page, use
.Sx \&Xr
instead.
See also
.Sx MANUAL STRUCTURE ,
.Sx \&Fo ,
and
.Sx \&Ft .
.Ss \&Fo
Begin a function block.
This is a multi-line version of
.Sx \&Fn .
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Fo Ar funcname
.Pp
Invocations usually occur in the following context:
.Bd -ragged -offset indent
.Pf \. Sx \&Ft Ar functype
.br
.Pf \. Sx \&Fo Ar funcname
.br
.Pf \. Sx \&Fa Qq Ar argtype Ar argname
.br
\&.\.\.
.br
.Pf \. Sx \&Fc
.Ed
.Pp
A
.Sx \&Fo
scope is closed by
.Sx \&Fc .
.Pp
See also
.Sx MANUAL STRUCTURE ,
.Sx \&Fa ,
.Sx \&Fc ,
and
.Sx \&Ft .
.Ss \&Fr
This macro is obsolete.
No replacement markup is needed.
.Pp
It was used to show numerical function return values in an italic font.
.Ss \&Ft
A function type.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Ft Ar functype
.Pp
In the
.Em SYNOPSIS
section, a new output line is started after this macro.
.Pp
Examples:
.Dl \&.Ft int
.Bd -literal -offset indent -compact
\&.Ft functype
\&.Fn funcname
.Ed
.Pp
See also
.Sx MANUAL STRUCTURE ,
.Sx \&Fn ,
and
.Sx \&Fo .
.Ss \&Fx
Format the
.Fx
version provided as an argument, or a default value
if no argument is provided.
.Pp
Examples:
.Dl \&.Fx 7.1
.Dl \&.Fx
.Pp
See also
.Sx \&At ,
.Sx \&Bsx ,
.Sx \&Bx ,
.Sx \&Dx ,
.Sx \&Nx ,
and
.Sx \&Ox .
.Ss \&Hf
This macro is not implemented in
.Xr mandoc 1 .
.Pp
It was used to include the contents of a (header) file literally.
The syntax was:
.Pp
.Dl Pf . Sx \&Hf Ar filename
.Ss \&Ic
Designate an internal or interactive command.
This is similar to
.Sx \&Cm
but used for instructions rather than values.
.Pp
Examples:
.Dl \&.Ic :wq
.Dl \&.Ic hash
.Dl \&.Ic alias
.Pp
Note that using
.Sx \&Bd Fl literal
or
.Sx \&D1
is preferred for displaying code; the
.Sx \&Ic
macro is used when referring to specific instructions.
.Ss \&In
An
.Dq include
file.
When invoked as the first macro on an input line in the
.Em SYNOPSIS
section, the argument is displayed in angle brackets
and preceded by
.Dq #include ,
and a blank line is inserted in front if there is a preceding
function declaration.
This is most often used in section 2, 3, and 9 manual pages.
.Pp
Examples:
.Dl \&.In sys/types.h
.Pp
See also
.Sx MANUAL STRUCTURE .
.Ss \&It
A list item.
The syntax of this macro depends on the list type.
.Pp
Lists
of type
.Fl hang ,
.Fl ohang ,
.Fl inset ,
and
.Fl diag
have the following syntax:
.Pp
.D1 Pf \. Sx \&It Ar args
.Pp
Lists of type
.Fl bullet ,
.Fl dash ,
.Fl enum ,
.Fl hyphen
and
.Fl item
have the following syntax:
.Pp
.D1 Pf \. Sx \&It
.Pp
with subsequent lines interpreted within the scope of the
.Sx \&It
until either a closing
.Sx \&El
or another
.Sx \&It .
.Pp
The
.Fl tag
list has the following syntax:
.Pp
.D1 Pf \. Sx \&It Op Cm args
.Pp
Subsequent lines are interpreted as with
.Fl bullet
and family.
The line arguments correspond to the list's left-hand side; body
arguments correspond to the list's contents.
.Pp
The
.Fl column
list is the most complicated.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&It Ar cell Op Ar cell ...
.D1 Pf \. Sx \&It Ar cell Op Sx \&Ta Ar cell ...
.Pp
The arguments consist of one or more lines of text and macros
representing a complete table line.
Cells within the line are delimited by tabs or by the special
.Sx \&Ta
block macro.
The tab cell delimiter may only be used within the
.Sx \&It
line itself; on following lines, only the
.Sx \&Ta
macro can be used to delimit cells, and
.Sx \&Ta
is only recognised as a macro when called by other macros,
not as the first macro on a line.
.Pp
Note that quoted strings may span tab-delimited cells on an
.Sx \&It
line.
For example,
.Pp
.Dl .It \(dqcol1 ; col2 ;\(dq \&;
.Pp
will preserve the semicolon whitespace except for the last.
.Pp
See also
.Sx \&Bl .
.Ss \&Lb
Specify a library.
The syntax is as follows:
.Pp
.D1 Pf \. Sx \&Lb Ar library
.Pp
The
.Ar library
parameter may be a system library, such as
.Cm libz
or
.Cm libpam ,
in which case a small library description is printed next to the linker
invocation; or a custom library, in which case the library name is
printed in quotes.
This is most commonly used in the
.Em SYNOPSIS
section as described in
.Sx MANUAL STRUCTURE .
.Pp
Examples:
.Dl \&.Lb libz
.Dl \&.Lb libmandoc
.Ss \&Li
Denotes text that should be in a
.Li literal
font mode.
Note that this is a presentation term and should not be used for
stylistically decorating technical terms.
.Pp
On terminal output devices, this is often indistinguishable from
normal text.
.Pp
See also
.Sx \&Bf ,
.Sx \&Em ,
.Sx \&No ,
and
.Sx \&Sy .
.Ss \&Lk
Format a hyperlink.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Lk Ar uri Op Ar name
.Pp
Examples:
.Dl \&.Lk http://bsd.lv \(dqThe BSD.lv Project\(dq
.Dl \&.Lk http://bsd.lv
.Pp
See also
.Sx \&Mt .
.Ss \&Lp
Synonym for
.Sx \&Pp .
.Ss \&Ms
Display a mathematical symbol.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Ms Ar symbol
.Pp
Examples:
.Dl \&.Ms sigma
.Dl \&.Ms aleph
.Ss \&Mt
Format a
.Dq mailto:
hyperlink.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Mt Ar address
.Pp
Examples:
.Dl \&.Mt discuss@manpages.bsd.lv
.Dl \&.An Kristaps Dzonsons \&Aq \&Mt kristaps@bsd.lv
.Ss \&Nd
A one line description of the manual's content.
This may only be invoked in the
.Em SYNOPSIS
section subsequent the
.Sx \&Nm
macro.
.Pp
Examples:
.Dl Pf . Sx \&Nd mdoc language reference
.Dl Pf . Sx \&Nd format and display UNIX manuals
.Pp
The
.Sx \&Nd
macro technically accepts child macros and terminates with a subsequent
.Sx \&Sh
invocation.
Do not assume this behaviour: some
.Xr whatis 1
database generators are not smart enough to parse more than the line
arguments and will display macros verbatim.
.Pp
See also
.Sx \&Nm .
.Ss \&Nm
The name of the manual page, or \(em in particular in section 1, 6,
and 8 pages \(em of an additional command or feature documented in
the manual page.
When first invoked, the
.Sx \&Nm
macro expects a single argument, the name of the manual page.
Usually, the first invocation happens in the
.Em NAME
section of the page.
The specified name will be remembered and used whenever the macro is
called again without arguments later in the page.
The
.Sx \&Nm
macro uses
.Sx Block full-implicit
semantics when invoked as the first macro on an input line in the
.Em SYNOPSIS
section; otherwise, it uses ordinary
.Sx In-line
semantics.
.Pp
Examples:
.Bd -literal -offset indent
\&.Sh SYNOPSIS
\&.Nm cat
\&.Op Fl benstuv
\&.Op Ar
.Ed
.Pp
In the
.Em SYNOPSIS
of section 2, 3 and 9 manual pages, use the
.Sx \&Fn
macro rather than
.Sx \&Nm
to mark up the name of the manual page.
.Ss \&No
Normal text.
Closes the scope of any preceding in-line macro.
When used after physical formatting macros like
.Sx \&Em
or
.Sx \&Sy ,
switches back to the standard font face and weight.
Can also be used to embed plain text strings in macro lines
using semantic annotation macros.
.Pp
Examples:
.Dl ".Em italic , Sy bold , No and roman"
.Pp
.Bd -literal -offset indent -compact
\&.Sm off
\&.Cm :C No / Ar pattern No / Ar replacement No /
\&.Sm on
.Ed
.Pp
See also
.Sx \&Em ,
.Sx \&Li ,
and
.Sx \&Sy .
.Ss \&Ns
Suppress a space between the output of the preceding macro
and the following text or macro.
Following invocation, input is interpreted as normal text
just like after an
.Sx \&No
macro.
.Pp
This has no effect when invoked at the start of a macro line.
.Pp
Examples:
.Dl ".Ar name Ns = Ns Ar value"
.Dl ".Cm :M Ns Ar pattern"
.Dl ".Fl o Ns Ar output"
.Pp
See also
.Sx \&No
and
.Sx \&Sm .
.Ss \&Nx
Format the
.Nx
version provided as an argument, or a default value if
no argument is provided.
.Pp
Examples:
.Dl \&.Nx 5.01
.Dl \&.Nx
.Pp
See also
.Sx \&At ,
.Sx \&Bsx ,
.Sx \&Bx ,
.Sx \&Dx ,
.Sx \&Fx ,
and
.Sx \&Ox .
.Ss \&Oc
Close multi-line
.Sx \&Oo
context.
.Ss \&Oo
Multi-line version of
.Sx \&Op .
.Pp
Examples:
.Bd -literal -offset indent -compact
\&.Oo
\&.Op Fl flag Ns Ar value
\&.Oc
.Ed
.Ss \&Op
Optional part of a command line.
Prints the argument(s) in brackets.
This is most often used in the
.Em SYNOPSIS
section of section 1 and 8 manual pages.
.Pp
Examples:
.Dl \&.Op \&Fl a \&Ar b
.Dl \&.Op \&Ar a | b
.Pp
See also
.Sx \&Oo .
.Ss \&Os
Operating system version for display in the page footer.
This is the mandatory third macro of
any
.Nm
file.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Os Op Ar system Op Ar version
.Pp
The optional
.Ar system
parameter specifies the relevant operating system or environment.
Left unspecified, it defaults to the local operating system version.
This is the suggested form.
.Pp
Examples:
.Dl \&.Os
.Dl \&.Os KTH/CSC/TCS
.Dl \&.Os BSD 4.3
.Pp
See also
.Sx \&Dd
and
.Sx \&Dt .
.Ss \&Ot
This macro is obsolete.
Use
.Sx \&Ft
instead; with
.Xr mandoc 1 ,
both have the same effect.
.Pp
Historical
.Nm
packages described it as
.Dq "old function type (FORTRAN)" .
.Ss \&Ox
Format the
.Ox
version provided as an argument, or a default value
if no argument is provided.
.Pp
Examples:
.Dl \&.Ox 4.5
.Dl \&.Ox
.Pp
See also
.Sx \&At ,
.Sx \&Bsx ,
.Sx \&Bx ,
.Sx \&Dx ,
.Sx \&Fx ,
and
.Sx \&Nx .
.Ss \&Pa
An absolute or relative file system path, or a file or directory name.
If an argument is not provided, the character
.Sq \(ti
is used as a default.
.Pp
Examples:
.Dl \&.Pa /usr/bin/mandoc
.Dl \&.Pa /usr/share/man/man7/mdoc.7
.Pp
See also
.Sx \&Lk .
.Ss \&Pc
Close parenthesised context opened by
.Sx \&Po .
.Ss \&Pf
Removes the space between its argument
.Pq Dq prefix
and the following macro.
Its syntax is as follows:
.Pp
.D1 .Pf Ar prefix macro arguments ...
.Pp
This is equivalent to:
.Pp
.D1 .No Ar prefix No \&Ns Ar macro arguments ...
.Pp
Examples:
.Dl ".Pf $ Ar variable_name"
.Dl ".Pf 0x Ar hex_digits"
.Pp
See also
.Sx \&Ns
and
.Sx \&Sm .
.Ss \&Po
Multi-line version of
.Sx \&Pq .
.Ss \&Pp
Break a paragraph.
This will assert vertical space between prior and subsequent macros
and/or text.
.Pp
Paragraph breaks are not needed before or after
.Sx \&Sh
or
.Sx \&Ss
macros or before displays
.Pq Sx \&Bd
or lists
.Pq Sx \&Bl
unless the
.Fl compact
flag is given.
.Ss \&Pq
Parenthesised enclosure.
.Pp
See also
.Sx \&Po .
.Ss \&Qc
Close quoted context opened by
.Sx \&Qo .
.Ss \&Ql
Format a single-quoted literal.
See also
.Sx \&Qq
and
.Sx \&Sq .
.Ss \&Qo
Multi-line version of
.Sx \&Qq .
.Ss \&Qq
Encloses its arguments in
.Qq typewriter
double-quotes.
Consider using
.Sx \&Dq .
.Pp
See also
.Sx \&Dq ,
.Sx \&Sq ,
and
.Sx \&Qo .
.Ss \&Re
Close an
.Sx \&Rs
block.
Does not have any tail arguments.
.Ss \&Rs
Begin a bibliographic
.Pq Dq reference
block.
Does not have any head arguments.
The block macro may only contain
.Sx \&%A ,
.Sx \&%B ,
.Sx \&%C ,
.Sx \&%D ,
.Sx \&%I ,
.Sx \&%J ,
.Sx \&%N ,
.Sx \&%O ,
.Sx \&%P ,
.Sx \&%Q ,
.Sx \&%R ,
.Sx \&%T ,
.Sx \&%U ,
and
.Sx \&%V
child macros (at least one must be specified).
.Pp
Examples:
.Bd -literal -offset indent -compact
\&.Rs
\&.%A J. E. Hopcroft
\&.%A J. D. Ullman
\&.%B Introduction to Automata Theory, Languages, and Computation
\&.%I Addison-Wesley
\&.%C Reading, Massachusettes
\&.%D 1979
\&.Re
.Ed
.Pp
If an
.Sx \&Rs
block is used within a SEE ALSO section, a vertical space is asserted
before the rendered output, else the block continues on the current
line.
.Ss \&Rv
Insert a standard sentence regarding a function call's return value of 0
on success and \-1 on error, with the
.Va errno
libc global variable set on error.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Rv Fl std Op Ar function ...
.Pp
If
.Ar function
is not specified, the document's name set by
.Sx \&Nm
is used.
Multiple
.Ar function
arguments are treated as separate functions.
.Pp
See also
.Sx \&Ex .
.Ss \&Sc
Close single-quoted context opened by
.Sx \&So .
.Ss \&Sh
Begin a new section.
For a list of conventional manual sections, see
.Sx MANUAL STRUCTURE .
These sections should be used unless it's absolutely necessary that
custom sections be used.
.Pp
Section names should be unique so that they may be keyed by
.Sx \&Sx .
Although this macro is parsed, it should not consist of child node or it
may not be linked with
.Sx \&Sx .
.Pp
See also
.Sx \&Pp ,
.Sx \&Ss ,
and
.Sx \&Sx .
.Ss \&Sm
Switches the spacing mode for output generated from macros.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Sm Op Cm on | off
.Pp
By default, spacing is
.Cm on .
When switched
.Cm off ,
no white space is inserted between macro arguments and between the
output generated from adjacent macros, but text lines
still get normal spacing between words and sentences.
.Pp
When called without an argument, the
.Sx \&Sm
macro toggles the spacing mode.
Using this is not recommended because it makes the code harder to read.
.Ss \&So
Multi-line version of
.Sx \&Sq .
.Ss \&Sq
Encloses its arguments in
.Sq typewriter
single-quotes.
.Pp
See also
.Sx \&Dq ,
.Sx \&Qq ,
and
.Sx \&So .
.Ss \&Ss
Begin a new subsection.
Unlike with
.Sx \&Sh ,
there is no convention for the naming of subsections.
Except
.Em DESCRIPTION ,
the conventional sections described in
.Sx MANUAL STRUCTURE
rarely have subsections.
.Pp
Sub-section names should be unique so that they may be keyed by
.Sx \&Sx .
Although this macro is parsed, it should not consist of child node or it
may not be linked with
.Sx \&Sx .
.Pp
See also
.Sx \&Pp ,
.Sx \&Sh ,
and
.Sx \&Sx .
.Ss \&St
Replace an abbreviation for a standard with the full form.
The following standards are recognised.
Where multiple lines are given without a blank line in between,
they all refer to the same standard, and using the first form
is recommended.
.Bl -tag -width 1n
.It C language standards
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-ansiC
.St -ansiC
.It \-ansiC-89
.St -ansiC-89
.It \-isoC
.St -isoC
.It \-isoC-90
.St -isoC-90
.br
The original C standard.
.Pp
.It \-isoC-amd1
.St -isoC-amd1
.Pp
.It \-isoC-tcor1
.St -isoC-tcor1
.Pp
.It \-isoC-tcor2
.St -isoC-tcor2
.Pp
.It \-isoC-99
.St -isoC-99
-.It \-ansiC-99
-.St -ansiC-99
.br
The second major version of the C language standard.
.Pp
.It \-isoC-2011
.St -isoC-2011
.br
The third major version of the C language standard.
.El
.It POSIX.1 before the Single UNIX Specification
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-p1003.1-88
.St -p1003.1-88
.It \-p1003.1
.St -p1003.1
.br
The original POSIX standard, based on ANSI C.
.Pp
.It \-p1003.1-90
.St -p1003.1-90
.It \-iso9945-1-90
.St -iso9945-1-90
.br
The first update of POSIX.1.
.Pp
.It \-p1003.1b-93
.St -p1003.1b-93
.It \-p1003.1b
.St -p1003.1b
.br
Real-time extensions.
.Pp
.It \-p1003.1c-95
.St -p1003.1c-95
.br
POSIX thread interfaces.
.Pp
.It \-p1003.1i-95
.St -p1003.1i-95
.br
Technical Corrigendum.
.Pp
.It \-p1003.1-96
.St -p1003.1-96
.It \-iso9945-1-96
.St -iso9945-1-96
.br
Includes POSIX.1-1990, 1b, 1c, and 1i.
.El
.It X/Open Portability Guide version 4 and related standards
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-xpg3
.St -xpg3
.br
An XPG4 precursor, published in 1989.
.Pp
.It \-p1003.2
.St -p1003.2
.It \-p1003.2-92
.St -p1003.2-92
.It \-iso9945-2-93
.St -iso9945-2-93
.br
An XCU4 precursor.
.Pp
.It \-p1003.2a-92
.St -p1003.2a-92
.br
Updates to POSIX.2.
.Pp
.It \-xpg4
.St -xpg4
.br
Based on POSIX.1 and POSIX.2, published in 1992.
.El
.It Single UNIX Specification version 1 and related standards
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-susv1
.St -susv1
.It \-xpg4.2
.St -xpg4.2
.br
This standard was published in 1994.
It was used as the basis for UNIX 95 certification.
The following three refer to parts of it.
.Pp
.It \-xsh4.2
.St -xsh4.2
.Pp
.It \-xcurses4.2
.St -xcurses4.2
.Pp
.It \-p1003.1g-2000
.St -p1003.1g-2000
.br
Networking APIs, including sockets.
.Pp
-.It \-xpg4.3
-.St -xpg4.3
-.Pp
.It \-svid4
.St -svid4 ,
.br
Published in 1995.
.El
.It Single UNIX Specification version 2 and related standards
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-susv2
.St -susv2
This Standard was published in 1997
and is also called X/Open Portability Guide version 5.
It was used as the basis for UNIX 98 certification.
The following refer to parts of it.
.Pp
.It \-xbd5
.St -xbd5
.Pp
.It \-xsh5
.St -xsh5
.Pp
.It \-xcu5
.St -xcu5
.Pp
.It \-xns5
.St -xns5
.It \-xns5.2
.St -xns5.2
.El
-.It Single UNIX Specification version 3 and related standards
+.It Single UNIX Specification version 3
.Pp
-.Bl -tag -width "-p1003.1g-2000X" -compact
-.It \-p1003.1d-99
-.St -p1003.1d-99
-.br
-Additional real-time extensions.
-.Pp
+.Bl -tag -width "-p1003.1-2001" -compact
.It \-p1003.1-2001
.St -p1003.1-2001
.It \-susv3
.St -susv3
.br
This standard is based on C99, SUSv2, POSIX.1-1996, 1d, and 1j.
It is also called X/Open Portability Guide version 6.
It is used as the basis for UNIX 03 certification.
.Pp
.It \-p1003.1-2004
.St -p1003.1-2004
.br
The second and last Technical Corrigendum.
.El
.It Single UNIX Specification version 4
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-p1003.1-2008
.St -p1003.1-2008
.It \-susv4
.St -susv4
.br
This standard is also called
X/Open Portability Guide version 7.
.Pp
.It \-p1003.1-2013
.St -p1003.1-2013
.br
This is the first Technical Corrigendum.
.El
.It Other standards
.Pp
.Bl -tag -width "-p1003.1g-2000" -compact
.It \-ieee754
.St -ieee754
.br
Floating-point arithmetic.
.Pp
.It \-iso8601
.St -iso8601
.br
Representation of dates and times, published in 1988.
.Pp
.It \-iso8802-3
.St -iso8802-3
.br
Ethernet local area networks.
.Pp
.It \-ieee1275-94
.St -ieee1275-94
.El
.El
.Ss \&Sx
Reference a section or subsection in the same manual page.
The referenced section or subsection name must be identical to the
enclosed argument, including whitespace.
.Pp
Examples:
.Dl \&.Sx MANUAL STRUCTURE
.Pp
See also
.Sx \&Sh
and
.Sx \&Ss .
.Ss \&Sy
Request a boldface font.
.Pp
This is most often used to indicate importance or seriousness (not to be
confused with stress emphasis, see
.Sx \&Em ) .
When none of the semantic macros fit, it is also adequate for syntax
elements that have to be given or that appear verbatim.
.Pp
Examples:
.Bd -literal -compact -offset indent
\&.Sy Warning :
If
\&.Sy s
appears in the owner permissions, set-user-ID mode is set.
This utility replaces the former
\&.Sy dumpdir
program.
.Ed
.Pp
See also
.Sx \&Bf ,
.Sx \&Em ,
.Sx \&Li ,
and
.Sx \&No .
.Ss \&Ta
Table cell separator in
.Sx \&Bl Fl column
lists; can only be used below
.Sx \&It .
.Ss \&Tn
Supported only for compatibility, do not use this in new manuals.
Even though the macro name
.Pq Dq tradename
suggests a semantic function, historic usage is inconsistent, mostly
using it as a presentation-level macro to request a small caps font.
.Ss \&Ud
Supported only for compatibility, do not use this in new manuals.
Prints out
.Dq currently under development.
.Ss \&Ux
Supported only for compatibility, do not use this in new manuals.
Prints out
.Dq Ux .
.Ss \&Va
A variable name.
.Pp
Examples:
.Dl \&.Va foo
.Dl \&.Va const char *bar ;
.Pp
For function arguments and parameters, use
.Sx \&Fa
instead.
For declarations of global variables in the
.Em SYNOPSIS
section, use
.Sx \&Vt .
.Ss \&Vt
A variable type.
.Pp
This is also used for indicating global variables in the
.Em SYNOPSIS
section, in which case a variable name is also specified.
Note that it accepts
.Sx Block partial-implicit
syntax when invoked as the first macro on an input line in the
.Em SYNOPSIS
section, else it accepts ordinary
.Sx In-line
syntax.
In the former case, this macro starts a new output line,
and a blank line is inserted in front if there is a preceding
function definition or include directive.
.Pp
Examples:
.Dl \&.Vt unsigned char
.Dl \&.Vt extern const char * const sys_signame[] \&;
.Pp
For parameters in function prototypes, use
.Sx \&Fa
instead, for function return types
.Sx \&Ft ,
and for variable names outside the
.Em SYNOPSIS
section
.Sx \&Va ,
even when including a type with the name.
See also
.Sx MANUAL STRUCTURE .
.Ss \&Xc
Close a scope opened by
.Sx \&Xo .
.Ss \&Xo
Extend the header of an
.Sx \&It
macro or the body of a partial-implicit block macro
beyond the end of the input line.
This macro originally existed to work around the 9-argument limit
of historic
.Xr roff 7 .
.Ss \&Xr
Link to another manual
.Pq Qq cross-reference .
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Xr Ar name Op section
.Pp
Cross reference the
.Ar name
and
.Ar section
number of another man page;
omitting the section number is rarely useful.
.Pp
Examples:
.Dl \&.Xr mandoc 1
.Dl \&.Xr mandoc 1 \&;
.Dl \&.Xr mandoc 1 \&Ns s behaviour
.Ss \&br
Emits a line-break.
This macro should not be used; it is implemented for compatibility with
historical manuals.
.Pp
Consider using
.Sx \&Pp
in the event of natural paragraph breaks.
.Ss \&sp
Emits vertical space.
This macro should not be used; it is implemented for compatibility with
historical manuals.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&sp Op Ar height
.Pp
The
.Ar height
argument is a scaling width as described in
.Xr roff 7 .
If unspecified,
.Sx \&sp
asserts a single vertical space.
.Sh MACRO SYNTAX
The syntax of a macro depends on its classification.
In this section,
.Sq \-arg
refers to macro arguments, which may be followed by zero or more
.Sq parm
parameters;
.Sq \&Yo
opens the scope of a macro; and if specified,
.Sq \&Yc
closes it out.
.Pp
The
.Em Callable
column indicates that the macro may also be called by passing its name
as an argument to another macro.
For example,
.Sq \&.Op \&Fl O \&Ar file
produces
.Sq Op Fl O Ar file .
To prevent a macro call and render the macro name literally,
escape it by prepending a zero-width space,
.Sq \e& .
For example,
.Sq \&Op \e&Fl O
produces
.Sq Op \&Fl O .
If a macro is not callable but its name appears as an argument
to another macro, it is interpreted as opaque text.
For example,
.Sq \&.Fl \&Sh
produces
.Sq Fl \&Sh .
.Pp
The
.Em Parsed
column indicates whether the macro may call other macros by receiving
their names as arguments.
If a macro is not parsed but the name of another macro appears
as an argument, it is interpreted as opaque text.
.Pp
The
.Em Scope
column, if applicable, describes closure rules.
.Ss Block full-explicit
Multi-line scope closed by an explicit closing macro.
All macros contains bodies; only
.Sx \&Bf
and
.Pq optionally
.Sx \&Bl
contain a head.
.Bd -literal -offset indent
\&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB
\(lBbody...\(rB
\&.Yc
.Ed
.Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXX" -offset indent
.It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope
.It Sx \&Bd Ta \&No Ta \&No Ta closed by Sx \&Ed
.It Sx \&Bf Ta \&No Ta \&No Ta closed by Sx \&Ef
.It Sx \&Bk Ta \&No Ta \&No Ta closed by Sx \&Ek
.It Sx \&Bl Ta \&No Ta \&No Ta closed by Sx \&El
.It Sx \&Ed Ta \&No Ta \&No Ta opened by Sx \&Bd
.It Sx \&Ef Ta \&No Ta \&No Ta opened by Sx \&Bf
.It Sx \&Ek Ta \&No Ta \&No Ta opened by Sx \&Bk
.It Sx \&El Ta \&No Ta \&No Ta opened by Sx \&Bl
.El
.Ss Block full-implicit
Multi-line scope closed by end-of-file or implicitly by another macro.
All macros have bodies; some
.Po
.Sx \&It Fl bullet ,
.Fl hyphen ,
.Fl dash ,
.Fl enum ,
.Fl item
.Pc
don't have heads; only one
.Po
.Sx \&It
in
.Sx \&Bl Fl column
.Pc
has multiple heads.
.Bd -literal -offset indent
\&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead... \(lBTa head...\(rB\(rB
\(lBbody...\(rB
.Ed
.Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXXXXXXXXX" -offset indent
.It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope
.It Sx \&It Ta \&No Ta Yes Ta closed by Sx \&It , Sx \&El
.It Sx \&Nd Ta \&No Ta \&No Ta closed by Sx \&Sh
.It Sx \&Nm Ta \&No Ta Yes Ta closed by Sx \&Nm , Sx \&Sh , Sx \&Ss
.It Sx \&Sh Ta \&No Ta Yes Ta closed by Sx \&Sh
.It Sx \&Ss Ta \&No Ta Yes Ta closed by Sx \&Sh , Sx \&Ss
.El
.Pp
Note that the
.Sx \&Nm
macro is a
.Sx Block full-implicit
macro only when invoked as the first macro
in a
.Em SYNOPSIS
section line, else it is
.Sx In-line .
.Ss Block partial-explicit
Like block full-explicit, but also with single-line scope.
Each has at least a body and, in limited circumstances, a head
.Po
.Sx \&Fo ,
.Sx \&Eo
.Pc
and/or tail
.Pq Sx \&Ec .
.Bd -literal -offset indent
\&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB
\(lBbody...\(rB
\&.Yc \(lBtail...\(rB
\&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB \
\(lBbody...\(rB \&Yc \(lBtail...\(rB
.Ed
.Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXX" -offset indent
.It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope
.It Sx \&Ac Ta Yes Ta Yes Ta opened by Sx \&Ao
.It Sx \&Ao Ta Yes Ta Yes Ta closed by Sx \&Ac
.It Sx \&Bc Ta Yes Ta Yes Ta closed by Sx \&Bo
.It Sx \&Bo Ta Yes Ta Yes Ta opened by Sx \&Bc
.It Sx \&Brc Ta Yes Ta Yes Ta opened by Sx \&Bro
.It Sx \&Bro Ta Yes Ta Yes Ta closed by Sx \&Brc
.It Sx \&Dc Ta Yes Ta Yes Ta opened by Sx \&Do
.It Sx \&Do Ta Yes Ta Yes Ta closed by Sx \&Dc
.It Sx \&Ec Ta Yes Ta Yes Ta opened by Sx \&Eo
.It Sx \&Eo Ta Yes Ta Yes Ta closed by Sx \&Ec
.It Sx \&Fc Ta Yes Ta Yes Ta opened by Sx \&Fo
.It Sx \&Fo Ta \&No Ta \&No Ta closed by Sx \&Fc
.It Sx \&Oc Ta Yes Ta Yes Ta closed by Sx \&Oo
.It Sx \&Oo Ta Yes Ta Yes Ta opened by Sx \&Oc
.It Sx \&Pc Ta Yes Ta Yes Ta closed by Sx \&Po
.It Sx \&Po Ta Yes Ta Yes Ta opened by Sx \&Pc
.It Sx \&Qc Ta Yes Ta Yes Ta opened by Sx \&Oo
.It Sx \&Qo Ta Yes Ta Yes Ta closed by Sx \&Oc
.It Sx \&Re Ta \&No Ta \&No Ta opened by Sx \&Rs
.It Sx \&Rs Ta \&No Ta \&No Ta closed by Sx \&Re
.It Sx \&Sc Ta Yes Ta Yes Ta opened by Sx \&So
.It Sx \&So Ta Yes Ta Yes Ta closed by Sx \&Sc
.It Sx \&Xc Ta Yes Ta Yes Ta opened by Sx \&Xo
.It Sx \&Xo Ta Yes Ta Yes Ta closed by Sx \&Xc
.El
.Ss Block partial-implicit
Like block full-implicit, but with single-line scope closed by the
end of the line.
.Bd -literal -offset indent
\&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBbody...\(rB \(lBres...\(rB
.Ed
.Bl -column "MacroX" "CallableX" "ParsedX" -offset indent
.It Em Macro Ta Em Callable Ta Em Parsed
.It Sx \&Aq Ta Yes Ta Yes
.It Sx \&Bq Ta Yes Ta Yes
.It Sx \&Brq Ta Yes Ta Yes
.It Sx \&D1 Ta \&No Ta \&Yes
.It Sx \&Dl Ta \&No Ta Yes
.It Sx \&Dq Ta Yes Ta Yes
.It Sx \&En Ta Yes Ta Yes
.It Sx \&Op Ta Yes Ta Yes
.It Sx \&Pq Ta Yes Ta Yes
.It Sx \&Ql Ta Yes Ta Yes
.It Sx \&Qq Ta Yes Ta Yes
.It Sx \&Sq Ta Yes Ta Yes
.It Sx \&Vt Ta Yes Ta Yes
.El
.Pp
Note that the
.Sx \&Vt
macro is a
.Sx Block partial-implicit
only when invoked as the first macro
in a
.Em SYNOPSIS
section line, else it is
.Sx In-line .
.Ss Special block macro
The
.Sx \&Ta
macro can only be used below
.Sx \&It
in
.Sx \&Bl Fl column
lists.
It delimits blocks representing table cells;
these blocks have bodies, but no heads.
.Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXX" -offset indent
.It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope
.It Sx \&Ta Ta Yes Ta Yes Ta closed by Sx \&Ta , Sx \&It
.El
.Ss In-line
Closed by the end of the line, fixed argument lengths,
and/or subsequent macros.
In-line macros have only text children.
If a number (or inequality) of arguments is
.Pq n ,
then the macro accepts an arbitrary number of arguments.
.Bd -literal -offset indent
\&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBargs...\(rB \(lBres...\(rB
\&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBargs...\(rB Yc...
\&.Yo \(lB\-arg \(lBval...\(rB\(rB arg0 arg1 argN
.Ed
.Bl -column "MacroX" "CallableX" "ParsedX" "Arguments" -offset indent
.It Em Macro Ta Em Callable Ta Em Parsed Ta Em Arguments
.It Sx \&%A Ta \&No Ta \&No Ta >0
.It Sx \&%B Ta \&No Ta \&No Ta >0
.It Sx \&%C Ta \&No Ta \&No Ta >0
.It Sx \&%D Ta \&No Ta \&No Ta >0
.It Sx \&%I Ta \&No Ta \&No Ta >0
.It Sx \&%J Ta \&No Ta \&No Ta >0
.It Sx \&%N Ta \&No Ta \&No Ta >0
.It Sx \&%O Ta \&No Ta \&No Ta >0
.It Sx \&%P Ta \&No Ta \&No Ta >0
.It Sx \&%Q Ta \&No Ta \&No Ta >0
.It Sx \&%R Ta \&No Ta \&No Ta >0
.It Sx \&%T Ta \&No Ta \&No Ta >0
.It Sx \&%U Ta \&No Ta \&No Ta >0
.It Sx \&%V Ta \&No Ta \&No Ta >0
.It Sx \&Ad Ta Yes Ta Yes Ta >0
.It Sx \&An Ta Yes Ta Yes Ta >0
.It Sx \&Ap Ta Yes Ta Yes Ta 0
.It Sx \&Ar Ta Yes Ta Yes Ta n
.It Sx \&At Ta Yes Ta Yes Ta 1
.It Sx \&Bsx Ta Yes Ta Yes Ta n
.It Sx \&Bt Ta \&No Ta \&No Ta 0
.It Sx \&Bx Ta Yes Ta Yes Ta n
.It Sx \&Cd Ta Yes Ta Yes Ta >0
.It Sx \&Cm Ta Yes Ta Yes Ta >0
.It Sx \&Db Ta \&No Ta \&No Ta 1
.It Sx \&Dd Ta \&No Ta \&No Ta n
.It Sx \&Dt Ta \&No Ta \&No Ta n
.It Sx \&Dv Ta Yes Ta Yes Ta >0
.It Sx \&Dx Ta Yes Ta Yes Ta n
.It Sx \&Em Ta Yes Ta Yes Ta >0
.It Sx \&Er Ta Yes Ta Yes Ta >0
.It Sx \&Es Ta Yes Ta Yes Ta 2
.It Sx \&Ev Ta Yes Ta Yes Ta >0
.It Sx \&Ex Ta \&No Ta \&No Ta n
.It Sx \&Fa Ta Yes Ta Yes Ta >0
.It Sx \&Fd Ta \&No Ta \&No Ta >0
.It Sx \&Fl Ta Yes Ta Yes Ta n
.It Sx \&Fn Ta Yes Ta Yes Ta >0
.It Sx \&Fr Ta Yes Ta Yes Ta >0
.It Sx \&Ft Ta Yes Ta Yes Ta >0
.It Sx \&Fx Ta Yes Ta Yes Ta n
.It Sx \&Hf Ta \&No Ta \&No Ta n
.It Sx \&Ic Ta Yes Ta Yes Ta >0
.It Sx \&In Ta \&No Ta \&No Ta 1
.It Sx \&Lb Ta \&No Ta \&No Ta 1
.It Sx \&Li Ta Yes Ta Yes Ta >0
.It Sx \&Lk Ta Yes Ta Yes Ta >0
.It Sx \&Lp Ta \&No Ta \&No Ta 0
.It Sx \&Ms Ta Yes Ta Yes Ta >0
.It Sx \&Mt Ta Yes Ta Yes Ta >0
.It Sx \&Nm Ta Yes Ta Yes Ta n
.It Sx \&No Ta Yes Ta Yes Ta 0
.It Sx \&Ns Ta Yes Ta Yes Ta 0
.It Sx \&Nx Ta Yes Ta Yes Ta n
.It Sx \&Os Ta \&No Ta \&No Ta n
.It Sx \&Ot Ta Yes Ta Yes Ta >0
.It Sx \&Ox Ta Yes Ta Yes Ta n
.It Sx \&Pa Ta Yes Ta Yes Ta n
.It Sx \&Pf Ta Yes Ta Yes Ta 1
.It Sx \&Pp Ta \&No Ta \&No Ta 0
.It Sx \&Rv Ta \&No Ta \&No Ta n
.It Sx \&Sm Ta \&No Ta \&No Ta <2
.It Sx \&St Ta \&No Ta Yes Ta 1
.It Sx \&Sx Ta Yes Ta Yes Ta >0
.It Sx \&Sy Ta Yes Ta Yes Ta >0
.It Sx \&Tn Ta Yes Ta Yes Ta >0
.It Sx \&Ud Ta \&No Ta \&No Ta 0
.It Sx \&Ux Ta Yes Ta Yes Ta n
.It Sx \&Va Ta Yes Ta Yes Ta n
.It Sx \&Vt Ta Yes Ta Yes Ta >0
.It Sx \&Xr Ta Yes Ta Yes Ta >0
.It Sx \&br Ta \&No Ta \&No Ta 0
.It Sx \&sp Ta \&No Ta \&No Ta 1
.El
.Ss Delimiters
When a macro argument consists of one single input character
considered as a delimiter, the argument gets special handling.
This does not apply when delimiters appear in arguments containing
more than one character.
Consequently, to prevent special handling and just handle it
like any other argument, a delimiter can be escaped by prepending
a zero-width space
.Pq Sq \e& .
In text lines, delimiters never need escaping, but may be used
as normal punctuation.
.Pp
For many macros, when the leading arguments are opening delimiters,
these delimiters are put before the macro scope,
and when the trailing arguments are closing delimiters,
these delimiters are put after the macro scope.
For example,
.Pp
.D1 Pf \. \&Aq "( [ word ] ) ."
.Pp
renders as:
.Pp
.D1 Aq ( [ word ] ) .
.Pp
Opening delimiters are:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It \&(
left parenthesis
.It \&[
left bracket
.El
.Pp
Closing delimiters are:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It \&.
period
.It \&,
comma
.It \&:
colon
.It \&;
semicolon
.It \&)
right parenthesis
.It \&]
right bracket
.It \&?
question mark
.It \&!
exclamation mark
.El
.Pp
Note that even a period preceded by a backslash
.Pq Sq \e.\&
gets this special handling; use
.Sq \e&.
to prevent that.
.Pp
Many in-line macros interrupt their scope when they encounter
delimiters, and resume their scope when more arguments follow that
are not delimiters.
For example,
.Pp
.D1 Pf \. \&Fl "a ( b | c \e*(Ba d ) e"
.Pp
renders as:
.Pp
.D1 Fl a ( b | c \*(Ba d ) e
.Pp
This applies to both opening and closing delimiters,
and also to the middle delimiter:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It \&|
vertical bar
.El
.Pp
As a special case, the predefined string \e*(Ba is handled and rendered
in the same way as a plain
.Sq \&|
character.
Using this predefined string is not recommended in new manuals.
.Ss Font handling
In
.Nm
documents, usage of semantic markup is recommended in order to have
proper fonts automatically selected; only when no fitting semantic markup
is available, consider falling back to
.Sx Physical markup
macros.
Whenever any
.Nm
macro switches the
.Xr roff 7
font mode, it will automatically restore the previous font when exiting
its scope.
Manually switching the font using the
.Xr roff 7
.Ql \ef
font escape sequences is never required.
.Sh COMPATIBILITY
This section provides an incomplete list of compatibility issues
between mandoc and other troff implementations, at this time limited
to GNU troff
.Pq Qq groff .
The term
.Qq historic groff
refers to groff versions before 1.17,
which featured a significant update of the
.Pa doc.tmac
file.
.Pp
Heirloom troff, the other significant troff implementation accepting
\-mdoc, is similar to historic groff.
.Pp
The following problematic behaviour is found in groff:
.ds hist (Historic groff only.)
.Pp
.Bl -dash -compact
.It
Display macros
.Po
.Sx \&Bd ,
.Sx \&Dl ,
and
.Sx \&D1
.Pc
may not be nested.
\*[hist]
.It
.Sx \&At
with unknown arguments produces no output at all.
\*[hist]
Newer groff and mandoc print
.Qq AT&T UNIX
and the arguments.
.It
.Sx \&Bl Fl column
does not recognise trailing punctuation characters when they immediately
precede tabulator characters, but treats them as normal text and
outputs a space before them.
.It
.Sx \&Bd Fl ragged compact
does not start a new line.
\*[hist]
.It
.Sx \&Dd
with non-standard arguments behaves very strangely.
When there are three arguments, they are printed verbatim.
Any other number of arguments is replaced by the current date,
but without any arguments the string
.Dq Epoch
is printed.
.It
.Sx \&Fl
does not print a dash for an empty argument.
\*[hist]
.It
.Sx \&Fn
does not start a new line unless invoked as the line macro in the
.Em SYNOPSIS
section.
\*[hist]
.It
.Sx \&Fo
with
.Pf non- Sx \&Fa
children causes inconsistent spacing between arguments.
In mandoc, a single space is always inserted between arguments.
.It
.Sx \&Ft
in the
.Em SYNOPSIS
causes inconsistent vertical spacing, depending on whether a prior
.Sx \&Fn
has been invoked.
See
.Sx \&Ft
and
.Sx \&Fn
for the normalised behaviour in mandoc.
.It
.Sx \&In
ignores additional arguments and is not treated specially in the
.Em SYNOPSIS .
\*[hist]
.It
.Sx \&It
sometimes requires a
.Fl nested
flag.
\*[hist]
In new groff and mandoc, any list may be nested by default and
.Fl enum
lists will restart the sequence only for the sub-list.
.It
.Sx \&Li
followed by a delimiter is incorrectly used in some manuals
instead of properly quoting that character, which sometimes works with
historic groff.
.It
.Sx \&Lk
only accepts a single link-name argument; the remainder is misformatted.
.It
.Sx \&Pa
does not format its arguments when used in the FILES section under
certain list types.
.It
.Sx \&Ta
can only be called by other macros, but not at the beginning of a line.
.It
.Sx \&%C
is not implemented (up to and including groff-1.22.2).
.It
Historic groff only allows up to eight or nine arguments per macro input
line, depending on the exact situation.
Providing more arguments causes garbled output.
The number of arguments on one input line is not limited with mandoc.
.It
Historic groff has many un-callable macros.
Most of these (excluding some block-level macros) are callable
in new groff and mandoc.
.It
.Sq \(ba
(vertical bar) is not fully supported as a delimiter.
\*[hist]
.It
.Sq \ef
.Pq font face
and
.Sq \eF
.Pq font family face
.Sx Text Decoration
escapes behave irregularly when specified within line-macro scopes.
.It
Negative scaling units return to prior lines.
Instead, mandoc truncates them to zero.
.El
.Pp
The following features are unimplemented in mandoc:
.Pp
.Bl -dash -compact
.It
.Sx \&Bd
.Fl file Ar file .
.It
.Sx \&Bd
.Fl offset Cm center
and
.Fl offset Cm right .
Groff does not implement centred and flush-right rendering either,
but produces large indentations.
.El
.Sh SEE ALSO
.Xr man 1 ,
.Xr mandoc 1 ,
.Xr eqn 7 ,
.Xr man 7 ,
.Xr mandoc_char 7 ,
.Xr roff 7 ,
.Xr tbl 7
.Sh HISTORY
The
.Nm
language first appeared as a troff macro package in
.Bx 4.4 .
It was later significantly updated by Werner Lemberg and Ruslan Ermilov
in groff-1.17.
The standalone implementation that is part of the
.Xr mandoc 1
utility written by Kristaps Dzonsons appeared in
.Ox 4.6 .
.Sh AUTHORS
The
.Nm
reference was written by
.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
Index: vendor/mdocml/dist/mdoc.h
===================================================================
--- vendor/mdocml/dist/mdoc.h (revision 276215)
+++ vendor/mdocml/dist/mdoc.h (revision 276216)
@@ -1,399 +1,395 @@
-/* $Id: mdoc.h,v 1.131 2014/07/29 13:58:18 schwarze Exp $ */
+/* $Id: mdoc.h,v 1.132 2014/12/01 04:05:32 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef MDOC_H
-#define MDOC_H
enum mdoct {
MDOC_Ap = 0,
MDOC_Dd,
MDOC_Dt,
MDOC_Os,
MDOC_Sh,
MDOC_Ss,
MDOC_Pp,
MDOC_D1,
MDOC_Dl,
MDOC_Bd,
MDOC_Ed,
MDOC_Bl,
MDOC_El,
MDOC_It,
MDOC_Ad,
MDOC_An,
MDOC_Ar,
MDOC_Cd,
MDOC_Cm,
MDOC_Dv,
MDOC_Er,
MDOC_Ev,
MDOC_Ex,
MDOC_Fa,
MDOC_Fd,
MDOC_Fl,
MDOC_Fn,
MDOC_Ft,
MDOC_Ic,
MDOC_In,
MDOC_Li,
MDOC_Nd,
MDOC_Nm,
MDOC_Op,
MDOC_Ot,
MDOC_Pa,
MDOC_Rv,
MDOC_St,
MDOC_Va,
MDOC_Vt,
MDOC_Xr,
MDOC__A,
MDOC__B,
MDOC__D,
MDOC__I,
MDOC__J,
MDOC__N,
MDOC__O,
MDOC__P,
MDOC__R,
MDOC__T,
MDOC__V,
MDOC_Ac,
MDOC_Ao,
MDOC_Aq,
MDOC_At,
MDOC_Bc,
MDOC_Bf,
MDOC_Bo,
MDOC_Bq,
MDOC_Bsx,
MDOC_Bx,
MDOC_Db,
MDOC_Dc,
MDOC_Do,
MDOC_Dq,
MDOC_Ec,
MDOC_Ef,
MDOC_Em,
MDOC_Eo,
MDOC_Fx,
MDOC_Ms,
MDOC_No,
MDOC_Ns,
MDOC_Nx,
MDOC_Ox,
MDOC_Pc,
MDOC_Pf,
MDOC_Po,
MDOC_Pq,
MDOC_Qc,
MDOC_Ql,
MDOC_Qo,
MDOC_Qq,
MDOC_Re,
MDOC_Rs,
MDOC_Sc,
MDOC_So,
MDOC_Sq,
MDOC_Sm,
MDOC_Sx,
MDOC_Sy,
MDOC_Tn,
MDOC_Ux,
MDOC_Xc,
MDOC_Xo,
MDOC_Fo,
MDOC_Fc,
MDOC_Oo,
MDOC_Oc,
MDOC_Bk,
MDOC_Ek,
MDOC_Bt,
MDOC_Hf,
MDOC_Fr,
MDOC_Ud,
MDOC_Lb,
MDOC_Lp,
MDOC_Lk,
MDOC_Mt,
MDOC_Brq,
MDOC_Bro,
MDOC_Brc,
MDOC__C,
MDOC_Es,
MDOC_En,
MDOC_Dx,
MDOC__Q,
MDOC_br,
MDOC_sp,
MDOC__U,
MDOC_Ta,
MDOC_ll,
MDOC_MAX
};
enum mdocargt {
MDOC_Split, /* -split */
MDOC_Nosplit, /* -nospli */
MDOC_Ragged, /* -ragged */
MDOC_Unfilled, /* -unfilled */
MDOC_Literal, /* -literal */
MDOC_File, /* -file */
MDOC_Offset, /* -offset */
MDOC_Bullet, /* -bullet */
MDOC_Dash, /* -dash */
MDOC_Hyphen, /* -hyphen */
MDOC_Item, /* -item */
MDOC_Enum, /* -enum */
MDOC_Tag, /* -tag */
MDOC_Diag, /* -diag */
MDOC_Hang, /* -hang */
MDOC_Ohang, /* -ohang */
MDOC_Inset, /* -inset */
MDOC_Column, /* -column */
MDOC_Width, /* -width */
MDOC_Compact, /* -compact */
MDOC_Std, /* -std */
MDOC_Filled, /* -filled */
MDOC_Words, /* -words */
MDOC_Emphasis, /* -emphasis */
MDOC_Symbolic, /* -symbolic */
MDOC_Nested, /* -nested */
MDOC_Centred, /* -centered */
MDOC_ARG_MAX
};
enum mdoc_type {
MDOC_TEXT,
MDOC_ELEM,
MDOC_HEAD,
MDOC_TAIL,
MDOC_BODY,
MDOC_BLOCK,
MDOC_TBL,
MDOC_EQN,
MDOC_ROOT
};
/*
* Section (named/unnamed) of `Sh'. Note that these appear in the
* conventional order imposed by mdoc.7. In the case of SEC_NONE, no
* section has been invoked (this shouldn't happen). SEC_CUSTOM refers
* to other sections.
*/
enum mdoc_sec {
SEC_NONE = 0,
SEC_NAME, /* NAME */
SEC_LIBRARY, /* LIBRARY */
SEC_SYNOPSIS, /* SYNOPSIS */
SEC_DESCRIPTION, /* DESCRIPTION */
SEC_CONTEXT, /* CONTEXT */
SEC_IMPLEMENTATION, /* IMPLEMENTATION NOTES */
SEC_RETURN_VALUES, /* RETURN VALUES */
SEC_ENVIRONMENT, /* ENVIRONMENT */
SEC_FILES, /* FILES */
SEC_EXIT_STATUS, /* EXIT STATUS */
SEC_EXAMPLES, /* EXAMPLES */
SEC_DIAGNOSTICS, /* DIAGNOSTICS */
SEC_COMPATIBILITY, /* COMPATIBILITY */
SEC_ERRORS, /* ERRORS */
SEC_SEE_ALSO, /* SEE ALSO */
SEC_STANDARDS, /* STANDARDS */
SEC_HISTORY, /* HISTORY */
SEC_AUTHORS, /* AUTHORS */
SEC_CAVEATS, /* CAVEATS */
SEC_BUGS, /* BUGS */
SEC_SECURITY, /* SECURITY */
SEC_CUSTOM,
SEC__MAX
};
struct mdoc_meta {
char *msec; /* `Dt' section (1, 3p, etc.) */
char *vol; /* `Dt' volume (implied) */
char *arch; /* `Dt' arch (i386, etc.) */
char *date; /* `Dd' normalised date */
char *title; /* `Dt' title (FOO, etc.) */
char *os; /* `Os' system (OpenBSD, etc.) */
char *name; /* leading `Nm' name */
};
/*
* An argument to a macro (multiple values = `-column xxx yyy').
*/
struct mdoc_argv {
enum mdocargt arg; /* type of argument */
int line;
int pos;
size_t sz; /* elements in "value" */
char **value; /* argument strings */
};
/*
* Reference-counted macro arguments. These are refcounted because
* blocks have multiple instances of the same arguments spread across
* the HEAD, BODY, TAIL, and BLOCK node types.
*/
struct mdoc_arg {
size_t argc;
struct mdoc_argv *argv;
unsigned int refcnt;
};
/*
* Indicates that a BODY's formatting has ended, but the scope is still
* open. Used for syntax-broken blocks.
*/
enum mdoc_endbody {
ENDBODY_NOT = 0,
ENDBODY_SPACE, /* is broken: append a space */
ENDBODY_NOSPACE /* is broken: don't append a space */
};
enum mdoc_list {
LIST__NONE = 0,
LIST_bullet, /* -bullet */
LIST_column, /* -column */
LIST_dash, /* -dash */
LIST_diag, /* -diag */
LIST_enum, /* -enum */
LIST_hang, /* -hang */
LIST_hyphen, /* -hyphen */
LIST_inset, /* -inset */
LIST_item, /* -item */
LIST_ohang, /* -ohang */
LIST_tag, /* -tag */
LIST_MAX
};
enum mdoc_disp {
DISP__NONE = 0,
DISP_centered, /* -centered */
DISP_ragged, /* -ragged */
DISP_unfilled, /* -unfilled */
DISP_filled, /* -filled */
DISP_literal /* -literal */
};
enum mdoc_auth {
AUTH__NONE = 0,
AUTH_split, /* -split */
AUTH_nosplit /* -nosplit */
};
enum mdoc_font {
FONT__NONE = 0,
FONT_Em, /* Em, -emphasis */
FONT_Li, /* Li, -literal */
FONT_Sy /* Sy, -symbolic */
};
struct mdoc_bd {
const char *offs; /* -offset */
enum mdoc_disp type; /* -ragged, etc. */
int comp; /* -compact */
};
struct mdoc_bl {
const char *width; /* -width */
const char *offs; /* -offset */
enum mdoc_list type; /* -tag, -enum, etc. */
int comp; /* -compact */
size_t ncols; /* -column arg count */
const char **cols; /* -column val ptr */
int count; /* -enum counter */
};
struct mdoc_bf {
enum mdoc_font font; /* font */
};
struct mdoc_an {
enum mdoc_auth auth; /* -split, etc. */
};
struct mdoc_rs {
int quote_T; /* whether to quote %T */
};
/*
* Consists of normalised node arguments. These should be used instead
* of iterating through the mdoc_arg pointers of a node: defaults are
* provided, etc.
*/
union mdoc_data {
struct mdoc_an An;
struct mdoc_bd Bd;
struct mdoc_bf Bf;
struct mdoc_bl Bl;
struct mdoc_node *Es;
struct mdoc_rs Rs;
};
/*
* Single node in tree-linked AST.
*/
struct mdoc_node {
struct mdoc_node *parent; /* parent AST node */
struct mdoc_node *child; /* first child AST node */
struct mdoc_node *last; /* last child AST node */
struct mdoc_node *next; /* sibling AST node */
struct mdoc_node *prev; /* prior sibling AST node */
int nchild; /* number children */
int line; /* parse line */
int pos; /* parse column */
int lastline; /* the node ends on this line */
enum mdoct tok; /* tok or MDOC__MAX if none */
int flags;
#define MDOC_VALID (1 << 0) /* has been validated */
#define MDOC_EOS (1 << 2) /* at sentence boundary */
#define MDOC_LINE (1 << 3) /* first macro/text on line */
#define MDOC_SYNPRETTY (1 << 4) /* SYNOPSIS-style formatting */
#define MDOC_ENDED (1 << 5) /* rendering has been ended */
#define MDOC_DELIMO (1 << 6)
#define MDOC_DELIMC (1 << 7)
enum mdoc_type type; /* AST node type */
enum mdoc_sec sec; /* current named section */
union mdoc_data *norm; /* normalised args */
const void *prev_font; /* before entering this node */
/* FIXME: these can be union'd to shave a few bytes. */
struct mdoc_arg *args; /* BLOCK/ELEM */
struct mdoc_node *pending; /* BLOCK */
struct mdoc_node *head; /* BLOCK */
struct mdoc_node *body; /* BLOCK */
struct mdoc_node *tail; /* BLOCK */
char *string; /* TEXT */
const struct tbl_span *span; /* TBL */
const struct eqn *eqn; /* EQN */
enum mdoc_endbody end; /* BODY */
};
/* Names of macros. Index is enum mdoct. */
extern const char *const *mdoc_macronames;
/* Names of macro args. Index is enum mdocargt. */
extern const char *const *mdoc_argnames;
__BEGIN_DECLS
struct mdoc;
const struct mdoc_node *mdoc_node(const struct mdoc *);
const struct mdoc_meta *mdoc_meta(const struct mdoc *);
void mdoc_deroff(char **, const struct mdoc_node *);
__END_DECLS
-
-#endif /*!MDOC_H*/
Index: vendor/mdocml/dist/mdoc_html.c
===================================================================
--- vendor/mdocml/dist/mdoc_html.c (revision 276215)
+++ vendor/mdocml/dist/mdoc_html.c (revision 276216)
@@ -1,2212 +1,2213 @@
-/* $Id: mdoc_html.c,v 1.213 2014/11/27 22:27:56 schwarze Exp $ */
+/* $Id: mdoc_html.c,v 1.216 2014/12/02 10:08:06 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons
* Copyright (c) 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
-#include "mandoc.h"
#include "mandoc_aux.h"
+#include "mdoc.h"
#include "out.h"
#include "html.h"
-#include "mdoc.h"
#include "main.h"
#define INDENT 5
#define MDOC_ARGS const struct mdoc_meta *meta, \
const struct mdoc_node *n, \
struct html *h
#ifndef MIN
#define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b))
#endif
struct htmlmdoc {
int (*pre)(MDOC_ARGS);
void (*post)(MDOC_ARGS);
};
static void print_mdoc(MDOC_ARGS);
static void print_mdoc_head(MDOC_ARGS);
static void print_mdoc_node(MDOC_ARGS);
static void print_mdoc_nodelist(MDOC_ARGS);
static void synopsis_pre(struct html *,
const struct mdoc_node *);
static void a2width(const char *, struct roffsu *);
static void mdoc_root_post(MDOC_ARGS);
static int mdoc_root_pre(MDOC_ARGS);
static void mdoc__x_post(MDOC_ARGS);
static int mdoc__x_pre(MDOC_ARGS);
static int mdoc_ad_pre(MDOC_ARGS);
static int mdoc_an_pre(MDOC_ARGS);
static int mdoc_ap_pre(MDOC_ARGS);
static int mdoc_ar_pre(MDOC_ARGS);
static int mdoc_bd_pre(MDOC_ARGS);
static int mdoc_bf_pre(MDOC_ARGS);
static void mdoc_bk_post(MDOC_ARGS);
static int mdoc_bk_pre(MDOC_ARGS);
static int mdoc_bl_pre(MDOC_ARGS);
static int mdoc_bt_pre(MDOC_ARGS);
static int mdoc_bx_pre(MDOC_ARGS);
static int mdoc_cd_pre(MDOC_ARGS);
static int mdoc_d1_pre(MDOC_ARGS);
static int mdoc_dv_pre(MDOC_ARGS);
static int mdoc_fa_pre(MDOC_ARGS);
static int mdoc_fd_pre(MDOC_ARGS);
static int mdoc_fl_pre(MDOC_ARGS);
static int mdoc_fn_pre(MDOC_ARGS);
static int mdoc_ft_pre(MDOC_ARGS);
static int mdoc_em_pre(MDOC_ARGS);
static int mdoc_er_pre(MDOC_ARGS);
static int mdoc_ev_pre(MDOC_ARGS);
static int mdoc_ex_pre(MDOC_ARGS);
static void mdoc_fo_post(MDOC_ARGS);
static int mdoc_fo_pre(MDOC_ARGS);
static int mdoc_ic_pre(MDOC_ARGS);
static int mdoc_igndelim_pre(MDOC_ARGS);
static int mdoc_in_pre(MDOC_ARGS);
static int mdoc_it_pre(MDOC_ARGS);
static int mdoc_lb_pre(MDOC_ARGS);
static int mdoc_li_pre(MDOC_ARGS);
static int mdoc_lk_pre(MDOC_ARGS);
static int mdoc_mt_pre(MDOC_ARGS);
static int mdoc_ms_pre(MDOC_ARGS);
static int mdoc_nd_pre(MDOC_ARGS);
static int mdoc_nm_pre(MDOC_ARGS);
static int mdoc_no_pre(MDOC_ARGS);
static int mdoc_ns_pre(MDOC_ARGS);
static int mdoc_pa_pre(MDOC_ARGS);
static void mdoc_pf_post(MDOC_ARGS);
static int mdoc_pp_pre(MDOC_ARGS);
static void mdoc_quote_post(MDOC_ARGS);
static int mdoc_quote_pre(MDOC_ARGS);
static int mdoc_rs_pre(MDOC_ARGS);
static int mdoc_rv_pre(MDOC_ARGS);
static int mdoc_sh_pre(MDOC_ARGS);
static int mdoc_skip_pre(MDOC_ARGS);
static int mdoc_sm_pre(MDOC_ARGS);
static int mdoc_sp_pre(MDOC_ARGS);
static int mdoc_ss_pre(MDOC_ARGS);
static int mdoc_sx_pre(MDOC_ARGS);
static int mdoc_sy_pre(MDOC_ARGS);
static int mdoc_ud_pre(MDOC_ARGS);
static int mdoc_va_pre(MDOC_ARGS);
static int mdoc_vt_pre(MDOC_ARGS);
static int mdoc_xr_pre(MDOC_ARGS);
static int mdoc_xx_pre(MDOC_ARGS);
static const struct htmlmdoc mdocs[MDOC_MAX] = {
{mdoc_ap_pre, NULL}, /* Ap */
{NULL, NULL}, /* Dd */
{NULL, NULL}, /* Dt */
{NULL, NULL}, /* Os */
{mdoc_sh_pre, NULL }, /* Sh */
{mdoc_ss_pre, NULL }, /* Ss */
{mdoc_pp_pre, NULL}, /* Pp */
{mdoc_d1_pre, NULL}, /* D1 */
{mdoc_d1_pre, NULL}, /* Dl */
{mdoc_bd_pre, NULL}, /* Bd */
{NULL, NULL}, /* Ed */
{mdoc_bl_pre, NULL}, /* Bl */
{NULL, NULL}, /* El */
{mdoc_it_pre, NULL}, /* It */
{mdoc_ad_pre, NULL}, /* Ad */
{mdoc_an_pre, NULL}, /* An */
{mdoc_ar_pre, NULL}, /* Ar */
{mdoc_cd_pre, NULL}, /* Cd */
{mdoc_fl_pre, NULL}, /* Cm */
{mdoc_dv_pre, NULL}, /* Dv */
{mdoc_er_pre, NULL}, /* Er */
{mdoc_ev_pre, NULL}, /* Ev */
{mdoc_ex_pre, NULL}, /* Ex */
{mdoc_fa_pre, NULL}, /* Fa */
{mdoc_fd_pre, NULL}, /* Fd */
{mdoc_fl_pre, NULL}, /* Fl */
{mdoc_fn_pre, NULL}, /* Fn */
{mdoc_ft_pre, NULL}, /* Ft */
{mdoc_ic_pre, NULL}, /* Ic */
{mdoc_in_pre, NULL}, /* In */
{mdoc_li_pre, NULL}, /* Li */
{mdoc_nd_pre, NULL}, /* Nd */
{mdoc_nm_pre, NULL}, /* Nm */
{mdoc_quote_pre, mdoc_quote_post}, /* Op */
{mdoc_ft_pre, NULL}, /* Ot */
{mdoc_pa_pre, NULL}, /* Pa */
{mdoc_rv_pre, NULL}, /* Rv */
{NULL, NULL}, /* St */
{mdoc_va_pre, NULL}, /* Va */
{mdoc_vt_pre, NULL}, /* Vt */
{mdoc_xr_pre, NULL}, /* Xr */
{mdoc__x_pre, mdoc__x_post}, /* %A */
{mdoc__x_pre, mdoc__x_post}, /* %B */
{mdoc__x_pre, mdoc__x_post}, /* %D */
{mdoc__x_pre, mdoc__x_post}, /* %I */
{mdoc__x_pre, mdoc__x_post}, /* %J */
{mdoc__x_pre, mdoc__x_post}, /* %N */
{mdoc__x_pre, mdoc__x_post}, /* %O */
{mdoc__x_pre, mdoc__x_post}, /* %P */
{mdoc__x_pre, mdoc__x_post}, /* %R */
{mdoc__x_pre, mdoc__x_post}, /* %T */
{mdoc__x_pre, mdoc__x_post}, /* %V */
{NULL, NULL}, /* Ac */
{mdoc_quote_pre, mdoc_quote_post}, /* Ao */
{mdoc_quote_pre, mdoc_quote_post}, /* Aq */
{NULL, NULL}, /* At */
{NULL, NULL}, /* Bc */
{mdoc_bf_pre, NULL}, /* Bf */
{mdoc_quote_pre, mdoc_quote_post}, /* Bo */
{mdoc_quote_pre, mdoc_quote_post}, /* Bq */
{mdoc_xx_pre, NULL}, /* Bsx */
{mdoc_bx_pre, NULL}, /* Bx */
{mdoc_skip_pre, NULL}, /* Db */
{NULL, NULL}, /* Dc */
{mdoc_quote_pre, mdoc_quote_post}, /* Do */
{mdoc_quote_pre, mdoc_quote_post}, /* Dq */
{NULL, NULL}, /* Ec */ /* FIXME: no space */
{NULL, NULL}, /* Ef */
{mdoc_em_pre, NULL}, /* Em */
{mdoc_quote_pre, mdoc_quote_post}, /* Eo */
{mdoc_xx_pre, NULL}, /* Fx */
{mdoc_ms_pre, NULL}, /* Ms */
{mdoc_no_pre, NULL}, /* No */
{mdoc_ns_pre, NULL}, /* Ns */
{mdoc_xx_pre, NULL}, /* Nx */
{mdoc_xx_pre, NULL}, /* Ox */
{NULL, NULL}, /* Pc */
{mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */
{mdoc_quote_pre, mdoc_quote_post}, /* Po */
{mdoc_quote_pre, mdoc_quote_post}, /* Pq */
{NULL, NULL}, /* Qc */
{mdoc_quote_pre, mdoc_quote_post}, /* Ql */
{mdoc_quote_pre, mdoc_quote_post}, /* Qo */
{mdoc_quote_pre, mdoc_quote_post}, /* Qq */
{NULL, NULL}, /* Re */
{mdoc_rs_pre, NULL}, /* Rs */
{NULL, NULL}, /* Sc */
{mdoc_quote_pre, mdoc_quote_post}, /* So */
{mdoc_quote_pre, mdoc_quote_post}, /* Sq */
{mdoc_sm_pre, NULL}, /* Sm */
{mdoc_sx_pre, NULL}, /* Sx */
{mdoc_sy_pre, NULL}, /* Sy */
{NULL, NULL}, /* Tn */
{mdoc_xx_pre, NULL}, /* Ux */
{NULL, NULL}, /* Xc */
{NULL, NULL}, /* Xo */
{mdoc_fo_pre, mdoc_fo_post}, /* Fo */
{NULL, NULL}, /* Fc */
{mdoc_quote_pre, mdoc_quote_post}, /* Oo */
{NULL, NULL}, /* Oc */
{mdoc_bk_pre, mdoc_bk_post}, /* Bk */
{NULL, NULL}, /* Ek */
{mdoc_bt_pre, NULL}, /* Bt */
{NULL, NULL}, /* Hf */
{mdoc_em_pre, NULL}, /* Fr */
{mdoc_ud_pre, NULL}, /* Ud */
{mdoc_lb_pre, NULL}, /* Lb */
{mdoc_pp_pre, NULL}, /* Lp */
{mdoc_lk_pre, NULL}, /* Lk */
{mdoc_mt_pre, NULL}, /* Mt */
{mdoc_quote_pre, mdoc_quote_post}, /* Brq */
{mdoc_quote_pre, mdoc_quote_post}, /* Bro */
{NULL, NULL}, /* Brc */
{mdoc__x_pre, mdoc__x_post}, /* %C */
{mdoc_skip_pre, NULL}, /* Es */
{mdoc_quote_pre, mdoc_quote_post}, /* En */
{mdoc_xx_pre, NULL}, /* Dx */
{mdoc__x_pre, mdoc__x_post}, /* %Q */
{mdoc_sp_pre, NULL}, /* br */
{mdoc_sp_pre, NULL}, /* sp */
{mdoc__x_pre, mdoc__x_post}, /* %U */
{NULL, NULL}, /* Ta */
{mdoc_skip_pre, NULL}, /* ll */
};
static const char * const lists[LIST_MAX] = {
NULL,
"list-bul",
"list-col",
"list-dash",
"list-diag",
"list-enum",
"list-hang",
"list-hyph",
"list-inset",
"list-item",
"list-ohang",
"list-tag"
};
void
html_mdoc(void *arg, const struct mdoc *mdoc)
{
print_mdoc(mdoc_meta(mdoc), mdoc_node(mdoc),
(struct html *)arg);
putchar('\n');
}
/*
* Calculate the scaling unit passed in a `-width' argument. This uses
* either a native scaling unit (e.g., 1i, 2m) or the string length of
* the value.
*/
static void
a2width(const char *p, struct roffsu *su)
{
if ( ! a2roffsu(p, su, SCALE_MAX)) {
su->unit = SCALE_EN;
su->scale = html_strlen(p);
}
}
/*
* See the same function in mdoc_term.c for documentation.
*/
static void
synopsis_pre(struct html *h, const struct mdoc_node *n)
{
if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
return;
if (n->prev->tok == n->tok &&
MDOC_Fo != n->tok &&
MDOC_Ft != n->tok &&
MDOC_Fn != n->tok) {
print_otag(h, TAG_BR, 0, NULL);
return;
}
switch (n->prev->tok) {
case MDOC_Fd:
/* FALLTHROUGH */
case MDOC_Fn:
/* FALLTHROUGH */
case MDOC_Fo:
/* FALLTHROUGH */
case MDOC_In:
/* FALLTHROUGH */
case MDOC_Vt:
print_paragraph(h);
break;
case MDOC_Ft:
if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
print_paragraph(h);
break;
}
/* FALLTHROUGH */
default:
print_otag(h, TAG_BR, 0, NULL);
break;
}
}
static void
print_mdoc(MDOC_ARGS)
{
struct tag *t, *tt;
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "mandoc");
if ( ! (HTML_FRAGMENT & h->oflags)) {
print_gen_decls(h);
t = print_otag(h, TAG_HTML, 0, NULL);
tt = print_otag(h, TAG_HEAD, 0, NULL);
print_mdoc_head(meta, n, h);
print_tagq(h, tt);
print_otag(h, TAG_BODY, 0, NULL);
print_otag(h, TAG_DIV, 1, &tag);
} else
t = print_otag(h, TAG_DIV, 1, &tag);
print_mdoc_nodelist(meta, n, h);
print_tagq(h, t);
}
static void
print_mdoc_head(MDOC_ARGS)
{
print_gen_head(h);
bufinit(h);
bufcat(h, meta->title);
if (meta->msec)
bufcat_fmt(h, "(%s)", meta->msec);
if (meta->arch)
bufcat_fmt(h, " (%s)", meta->arch);
print_otag(h, TAG_TITLE, 0, NULL);
print_text(h, h->buf);
}
static void
print_mdoc_nodelist(MDOC_ARGS)
{
print_mdoc_node(meta, n, h);
if (n->next)
print_mdoc_nodelist(meta, n->next, h);
}
static void
print_mdoc_node(MDOC_ARGS)
{
int child;
struct tag *t;
child = 1;
t = h->tags.head;
switch (n->type) {
case MDOC_ROOT:
child = mdoc_root_pre(meta, n, h);
break;
case MDOC_TEXT:
/* No tables in this mode... */
assert(NULL == h->tblt);
/*
* Make sure that if we're in a literal mode already
* (i.e., within a ) don't print the newline.
*/
if (' ' == *n->string && MDOC_LINE & n->flags)
if ( ! (HTML_LITERAL & h->flags))
print_otag(h, TAG_BR, 0, NULL);
if (MDOC_DELIMC & n->flags)
h->flags |= HTML_NOSPACE;
print_text(h, n->string);
if (MDOC_DELIMO & n->flags)
h->flags |= HTML_NOSPACE;
return;
case MDOC_EQN:
print_eqn(h, n->eqn);
break;
case MDOC_TBL:
/*
* This will take care of initialising all of the table
* state data for the first table, then tearing it down
* for the last one.
*/
print_tbl(h, n->span);
return;
default:
/*
* Close out the current table, if it's open, and unset
* the "meta" table state. This will be reopened on the
* next table element.
*/
if (h->tblt != NULL) {
print_tblclose(h);
t = h->tags.head;
}
assert(h->tblt == NULL);
if (mdocs[n->tok].pre && (n->end == ENDBODY_NOT || n->child))
child = (*mdocs[n->tok].pre)(meta, n, h);
break;
}
if (HTML_KEEP & h->flags) {
if (n->prev ? (n->prev->lastline != n->line) :
(n->parent && n->parent->line != n->line)) {
h->flags &= ~HTML_KEEP;
h->flags |= HTML_PREKEEP;
}
}
if (child && n->child)
print_mdoc_nodelist(meta, n->child, h);
print_stagq(h, t);
switch (n->type) {
case MDOC_ROOT:
mdoc_root_post(meta, n, h);
break;
case MDOC_EQN:
break;
default:
if ( ! mdocs[n->tok].post || n->flags & MDOC_ENDED)
break;
(*mdocs[n->tok].post)(meta, n, h);
if (n->end != ENDBODY_NOT)
n->pending->flags |= MDOC_ENDED;
if (n->end == ENDBODY_NOSPACE)
h->flags |= HTML_NOSPACE;
break;
}
}
static void
mdoc_root_post(MDOC_ARGS)
{
struct htmlpair tag;
struct tag *t, *tt;
PAIR_CLASS_INIT(&tag, "foot");
t = print_otag(h, TAG_TABLE, 1, &tag);
print_otag(h, TAG_TBODY, 0, NULL);
tt = print_otag(h, TAG_TR, 0, NULL);
PAIR_CLASS_INIT(&tag, "foot-date");
print_otag(h, TAG_TD, 1, &tag);
print_text(h, meta->date);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag, "foot-os");
print_otag(h, TAG_TD, 1, &tag);
print_text(h, meta->os);
print_tagq(h, t);
}
static int
mdoc_root_pre(MDOC_ARGS)
{
struct htmlpair tag;
struct tag *t, *tt;
char *volume, *title;
if (NULL == meta->arch)
volume = mandoc_strdup(meta->vol);
else
mandoc_asprintf(&volume, "%s (%s)",
meta->vol, meta->arch);
if (NULL == meta->msec)
title = mandoc_strdup(meta->title);
else
mandoc_asprintf(&title, "%s(%s)",
meta->title, meta->msec);
PAIR_CLASS_INIT(&tag, "head");
t = print_otag(h, TAG_TABLE, 1, &tag);
print_otag(h, TAG_TBODY, 0, NULL);
tt = print_otag(h, TAG_TR, 0, NULL);
PAIR_CLASS_INIT(&tag, "head-ltitle");
print_otag(h, TAG_TD, 1, &tag);
print_text(h, title);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag, "head-vol");
print_otag(h, TAG_TD, 1, &tag);
print_text(h, volume);
print_stagq(h, tt);
PAIR_CLASS_INIT(&tag, "head-rtitle");
print_otag(h, TAG_TD, 1, &tag);
print_text(h, title);
print_tagq(h, t);
free(title);
free(volume);
return(1);
}
static int
mdoc_sh_pre(MDOC_ARGS)
{
struct htmlpair tag;
switch (n->type) {
case MDOC_BLOCK:
PAIR_CLASS_INIT(&tag, "section");
print_otag(h, TAG_DIV, 1, &tag);
return(1);
case MDOC_BODY:
if (n->sec == SEC_AUTHORS)
h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT);
return(1);
default:
break;
}
bufinit(h);
bufcat(h, "x");
for (n = n->child; n && MDOC_TEXT == n->type; ) {
bufcat_id(h, n->string);
if (NULL != (n = n->next))
bufcat_id(h, " ");
}
if (NULL == n) {
PAIR_ID_INIT(&tag, h->buf);
print_otag(h, TAG_H1, 1, &tag);
} else
print_otag(h, TAG_H1, 0, NULL);
return(1);
}
static int
mdoc_ss_pre(MDOC_ARGS)
{
struct htmlpair tag;
if (MDOC_BLOCK == n->type) {
PAIR_CLASS_INIT(&tag, "subsection");
print_otag(h, TAG_DIV, 1, &tag);
return(1);
} else if (MDOC_BODY == n->type)
return(1);
bufinit(h);
bufcat(h, "x");
for (n = n->child; n && MDOC_TEXT == n->type; ) {
bufcat_id(h, n->string);
if (NULL != (n = n->next))
bufcat_id(h, " ");
}
if (NULL == n) {
PAIR_ID_INIT(&tag, h->buf);
print_otag(h, TAG_H2, 1, &tag);
} else
print_otag(h, TAG_H2, 0, NULL);
return(1);
}
static int
mdoc_fl_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "flag");
print_otag(h, TAG_B, 1, &tag);
/* `Cm' has no leading hyphen. */
if (MDOC_Cm == n->tok)
return(1);
print_text(h, "\\-");
if ( ! (n->nchild == 0 &&
(n->next == NULL ||
n->next->type == MDOC_TEXT ||
n->next->flags & MDOC_LINE)))
h->flags |= HTML_NOSPACE;
return(1);
}
static int
mdoc_nd_pre(MDOC_ARGS)
{
struct htmlpair tag;
if (MDOC_BODY != n->type)
return(1);
/* XXX: this tag in theory can contain block elements. */
print_text(h, "\\(em");
PAIR_CLASS_INIT(&tag, "desc");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_nm_pre(MDOC_ARGS)
{
struct htmlpair tag;
struct roffsu su;
int len;
switch (n->type) {
case MDOC_ELEM:
synopsis_pre(h, n);
PAIR_CLASS_INIT(&tag, "name");
print_otag(h, TAG_B, 1, &tag);
if (NULL == n->child && meta->name)
print_text(h, meta->name);
return(1);
case MDOC_HEAD:
print_otag(h, TAG_TD, 0, NULL);
if (NULL == n->child && meta->name)
print_text(h, meta->name);
return(1);
case MDOC_BODY:
print_otag(h, TAG_TD, 0, NULL);
return(1);
default:
break;
}
synopsis_pre(h, n);
PAIR_CLASS_INIT(&tag, "synopsis");
print_otag(h, TAG_TABLE, 1, &tag);
for (len = 0, n = n->child; n; n = n->next)
if (MDOC_TEXT == n->type)
len += html_strlen(n->string);
if (0 == len && meta->name)
len = html_strlen(meta->name);
SCALE_HS_INIT(&su, len);
bufinit(h);
bufcat_su(h, "width", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_COL, 1, &tag);
print_otag(h, TAG_COL, 0, NULL);
print_otag(h, TAG_TBODY, 0, NULL);
print_otag(h, TAG_TR, 0, NULL);
return(1);
}
static int
mdoc_xr_pre(MDOC_ARGS)
{
struct htmlpair tag[2];
if (NULL == n->child)
return(0);
PAIR_CLASS_INIT(&tag[0], "link-man");
if (h->base_man) {
buffmt_man(h, n->child->string,
n->child->next ?
n->child->next->string : NULL);
PAIR_HREF_INIT(&tag[1], h->buf);
print_otag(h, TAG_A, 2, tag);
} else
print_otag(h, TAG_A, 1, tag);
n = n->child;
print_text(h, n->string);
if (NULL == (n = n->next))
return(0);
h->flags |= HTML_NOSPACE;
print_text(h, "(");
h->flags |= HTML_NOSPACE;
print_text(h, n->string);
h->flags |= HTML_NOSPACE;
print_text(h, ")");
return(0);
}
static int
mdoc_ns_pre(MDOC_ARGS)
{
if ( ! (MDOC_LINE & n->flags))
h->flags |= HTML_NOSPACE;
return(1);
}
static int
mdoc_ar_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "arg");
print_otag(h, TAG_I, 1, &tag);
return(1);
}
static int
mdoc_xx_pre(MDOC_ARGS)
{
const char *pp;
struct htmlpair tag;
int flags;
switch (n->tok) {
case MDOC_Bsx:
pp = "BSD/OS";
break;
case MDOC_Dx:
pp = "DragonFly";
break;
case MDOC_Fx:
pp = "FreeBSD";
break;
case MDOC_Nx:
pp = "NetBSD";
break;
case MDOC_Ox:
pp = "OpenBSD";
break;
case MDOC_Ux:
pp = "UNIX";
break;
default:
return(1);
}
PAIR_CLASS_INIT(&tag, "unix");
print_otag(h, TAG_SPAN, 1, &tag);
print_text(h, pp);
if (n->child) {
flags = h->flags;
h->flags |= HTML_KEEP;
print_text(h, n->child->string);
h->flags = flags;
}
return(0);
}
static int
mdoc_bx_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "unix");
print_otag(h, TAG_SPAN, 1, &tag);
if (NULL != (n = n->child)) {
print_text(h, n->string);
h->flags |= HTML_NOSPACE;
print_text(h, "BSD");
} else {
print_text(h, "BSD");
return(0);
}
if (NULL != (n = n->next)) {
h->flags |= HTML_NOSPACE;
print_text(h, "-");
h->flags |= HTML_NOSPACE;
print_text(h, n->string);
}
return(0);
}
static int
mdoc_it_pre(MDOC_ARGS)
{
struct roffsu su;
enum mdoc_list type;
struct htmlpair tag[2];
const struct mdoc_node *bl;
bl = n->parent;
while (bl && MDOC_Bl != bl->tok)
bl = bl->parent;
assert(bl);
type = bl->norm->Bl.type;
assert(lists[type]);
PAIR_CLASS_INIT(&tag[0], lists[type]);
bufinit(h);
if (MDOC_HEAD == n->type) {
switch (type) {
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_item:
/* FALLTHROUGH */
case LIST_hyphen:
/* FALLTHROUGH */
case LIST_enum:
return(0);
case LIST_diag:
/* FALLTHROUGH */
case LIST_hang:
/* FALLTHROUGH */
case LIST_inset:
/* FALLTHROUGH */
case LIST_ohang:
/* FALLTHROUGH */
case LIST_tag:
SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
bufcat_su(h, "margin-top", &su);
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_DT, 2, tag);
if (LIST_diag != type)
break;
PAIR_CLASS_INIT(&tag[0], "diag");
print_otag(h, TAG_B, 1, tag);
break;
case LIST_column:
break;
default:
break;
}
} else if (MDOC_BODY == n->type) {
switch (type) {
case LIST_bullet:
/* FALLTHROUGH */
case LIST_hyphen:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_enum:
/* FALLTHROUGH */
case LIST_item:
SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
bufcat_su(h, "margin-top", &su);
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_LI, 2, tag);
break;
case LIST_diag:
/* FALLTHROUGH */
case LIST_hang:
/* FALLTHROUGH */
case LIST_inset:
/* FALLTHROUGH */
case LIST_ohang:
/* FALLTHROUGH */
case LIST_tag:
if (NULL == bl->norm->Bl.width) {
print_otag(h, TAG_DD, 1, tag);
break;
}
a2width(bl->norm->Bl.width, &su);
bufcat_su(h, "margin-left", &su);
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_DD, 2, tag);
break;
case LIST_column:
SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
bufcat_su(h, "margin-top", &su);
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_TD, 2, tag);
break;
default:
break;
}
} else {
switch (type) {
case LIST_column:
print_otag(h, TAG_TR, 1, tag);
break;
default:
break;
}
}
return(1);
}
static int
mdoc_bl_pre(MDOC_ARGS)
{
int i;
struct htmlpair tag[3];
struct roffsu su;
char buf[BUFSIZ];
if (MDOC_BODY == n->type) {
if (LIST_column == n->norm->Bl.type)
print_otag(h, TAG_TBODY, 0, NULL);
return(1);
}
if (MDOC_HEAD == n->type) {
if (LIST_column != n->norm->Bl.type)
return(0);
/*
* For each column, print out the tag with our
* suggested width. The last column gets min-width, as
* in terminal mode it auto-sizes to the width of the
* screen and we want to preserve that behaviour.
*/
for (i = 0; i < (int)n->norm->Bl.ncols; i++) {
bufinit(h);
a2width(n->norm->Bl.cols[i], &su);
if (i < (int)n->norm->Bl.ncols - 1)
bufcat_su(h, "width", &su);
else
bufcat_su(h, "min-width", &su);
PAIR_STYLE_INIT(&tag[0], h);
print_otag(h, TAG_COL, 1, tag);
}
return(0);
}
SCALE_VS_INIT(&su, 0);
bufinit(h);
bufcat_su(h, "margin-top", &su);
bufcat_su(h, "margin-bottom", &su);
PAIR_STYLE_INIT(&tag[0], h);
assert(lists[n->norm->Bl.type]);
(void)strlcpy(buf, "list ", BUFSIZ);
(void)strlcat(buf, lists[n->norm->Bl.type], BUFSIZ);
PAIR_INIT(&tag[1], ATTR_CLASS, buf);
/* Set the block's left-hand margin. */
if (n->norm->Bl.offs) {
a2width(n->norm->Bl.offs, &su);
bufcat_su(h, "margin-left", &su);
}
switch (n->norm->Bl.type) {
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_hyphen:
/* FALLTHROUGH */
case LIST_item:
print_otag(h, TAG_UL, 2, tag);
break;
case LIST_enum:
print_otag(h, TAG_OL, 2, tag);
break;
case LIST_diag:
/* FALLTHROUGH */
case LIST_hang:
/* FALLTHROUGH */
case LIST_inset:
/* FALLTHROUGH */
case LIST_ohang:
/* FALLTHROUGH */
case LIST_tag:
print_otag(h, TAG_DL, 2, tag);
break;
case LIST_column:
print_otag(h, TAG_TABLE, 2, tag);
break;
default:
abort();
/* NOTREACHED */
}
return(1);
}
static int
mdoc_ex_pre(MDOC_ARGS)
{
struct tag *t;
struct htmlpair tag;
int nchild;
if (n->prev)
print_otag(h, TAG_BR, 0, NULL);
PAIR_CLASS_INIT(&tag, "utility");
print_text(h, "The");
nchild = n->nchild;
for (n = n->child; n; n = n->next) {
assert(MDOC_TEXT == n->type);
t = print_otag(h, TAG_B, 1, &tag);
print_text(h, n->string);
print_tagq(h, t);
if (nchild > 2 && n->next) {
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
if (n->next && NULL == n->next->next)
print_text(h, "and");
}
if (nchild > 1)
print_text(h, "utilities exit\\~0");
else
print_text(h, "utility exits\\~0");
print_text(h, "on success, and\\~>0 if an error occurs.");
return(0);
}
static int
mdoc_em_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "emph");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_d1_pre(MDOC_ARGS)
{
struct htmlpair tag[2];
struct roffsu su;
if (MDOC_BLOCK != n->type)
return(1);
SCALE_VS_INIT(&su, 0);
bufinit(h);
bufcat_su(h, "margin-top", &su);
bufcat_su(h, "margin-bottom", &su);
PAIR_STYLE_INIT(&tag[0], h);
print_otag(h, TAG_BLOCKQUOTE, 1, tag);
/* BLOCKQUOTE needs a block body. */
PAIR_CLASS_INIT(&tag[0], "display");
print_otag(h, TAG_DIV, 1, tag);
if (MDOC_Dl == n->tok) {
PAIR_CLASS_INIT(&tag[0], "lit");
print_otag(h, TAG_CODE, 1, tag);
}
return(1);
}
static int
mdoc_sx_pre(MDOC_ARGS)
{
struct htmlpair tag[2];
bufinit(h);
bufcat(h, "#x");
for (n = n->child; n; ) {
bufcat_id(h, n->string);
if (NULL != (n = n->next))
bufcat_id(h, " ");
}
PAIR_CLASS_INIT(&tag[0], "link-sec");
PAIR_HREF_INIT(&tag[1], h->buf);
print_otag(h, TAG_I, 1, tag);
print_otag(h, TAG_A, 2, tag);
return(1);
}
static int
mdoc_bd_pre(MDOC_ARGS)
{
struct htmlpair tag[2];
int comp, sv;
const struct mdoc_node *nn;
struct roffsu su;
if (MDOC_HEAD == n->type)
return(0);
if (MDOC_BLOCK == n->type) {
comp = n->norm->Bd.comp;
for (nn = n; nn && ! comp; nn = nn->parent) {
if (MDOC_BLOCK != nn->type)
continue;
if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
comp = 1;
if (nn->prev)
break;
}
if ( ! comp)
print_paragraph(h);
return(1);
}
/* Handle the -offset argument. */
if (n->norm->Bd.offs == NULL ||
! strcmp(n->norm->Bd.offs, "left"))
SCALE_HS_INIT(&su, 0);
else if ( ! strcmp(n->norm->Bd.offs, "indent"))
SCALE_HS_INIT(&su, INDENT);
else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
SCALE_HS_INIT(&su, INDENT * 2);
else
a2width(n->norm->Bd.offs, &su);
bufinit(h);
bufcat_su(h, "margin-left", &su);
PAIR_STYLE_INIT(&tag[0], h);
if (DISP_unfilled != n->norm->Bd.type &&
DISP_literal != n->norm->Bd.type) {
PAIR_CLASS_INIT(&tag[1], "display");
print_otag(h, TAG_DIV, 2, tag);
return(1);
}
PAIR_CLASS_INIT(&tag[1], "lit display");
print_otag(h, TAG_PRE, 2, tag);
/* This can be recursive: save & set our literal state. */
sv = h->flags & HTML_LITERAL;
h->flags |= HTML_LITERAL;
for (nn = n->child; nn; nn = nn->next) {
print_mdoc_node(meta, nn, h);
/*
* If the printed node flushes its own line, then we
* needn't do it here as well. This is hacky, but the
* notion of selective eoln whitespace is pretty dumb
* anyway, so don't sweat it.
*/
switch (nn->tok) {
case MDOC_Sm:
/* FALLTHROUGH */
case MDOC_br:
/* FALLTHROUGH */
case MDOC_sp:
/* FALLTHROUGH */
case MDOC_Bl:
/* FALLTHROUGH */
case MDOC_D1:
/* FALLTHROUGH */
case MDOC_Dl:
/* FALLTHROUGH */
case MDOC_Lp:
/* FALLTHROUGH */
case MDOC_Pp:
continue;
default:
break;
}
- if (nn->next && nn->next->line == nn->line)
+ if (h->flags & HTML_NONEWLINE ||
+ (nn->next && ! (nn->next->flags & MDOC_LINE)))
continue;
else if (nn->next)
print_text(h, "\n");
h->flags |= HTML_NOSPACE;
}
if (0 == sv)
h->flags &= ~HTML_LITERAL;
return(0);
}
static int
mdoc_pa_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "file");
print_otag(h, TAG_I, 1, &tag);
return(1);
}
static int
mdoc_ad_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "addr");
print_otag(h, TAG_I, 1, &tag);
return(1);
}
static int
mdoc_an_pre(MDOC_ARGS)
{
struct htmlpair tag;
if (n->norm->An.auth == AUTH_split) {
h->flags &= ~HTML_NOSPLIT;
h->flags |= HTML_SPLIT;
return(0);
}
if (n->norm->An.auth == AUTH_nosplit) {
h->flags &= ~HTML_SPLIT;
h->flags |= HTML_NOSPLIT;
return(0);
}
if (n->child == NULL)
return(0);
if (h->flags & HTML_SPLIT)
print_otag(h, TAG_BR, 0, NULL);
if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT))
h->flags |= HTML_SPLIT;
PAIR_CLASS_INIT(&tag, "author");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_cd_pre(MDOC_ARGS)
{
struct htmlpair tag;
synopsis_pre(h, n);
PAIR_CLASS_INIT(&tag, "config");
print_otag(h, TAG_B, 1, &tag);
return(1);
}
static int
mdoc_dv_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "define");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_ev_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "env");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_er_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "errno");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_fa_pre(MDOC_ARGS)
{
const struct mdoc_node *nn;
struct htmlpair tag;
struct tag *t;
PAIR_CLASS_INIT(&tag, "farg");
if (n->parent->tok != MDOC_Fo) {
print_otag(h, TAG_I, 1, &tag);
return(1);
}
for (nn = n->child; nn; nn = nn->next) {
t = print_otag(h, TAG_I, 1, &tag);
print_text(h, nn->string);
print_tagq(h, t);
if (nn->next) {
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
}
if (n->child && n->next && n->next->tok == MDOC_Fa) {
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
return(0);
}
static int
mdoc_fd_pre(MDOC_ARGS)
{
struct htmlpair tag[2];
char buf[BUFSIZ];
size_t sz;
int i;
struct tag *t;
synopsis_pre(h, n);
if (NULL == (n = n->child))
return(0);
assert(MDOC_TEXT == n->type);
if (strcmp(n->string, "#include")) {
PAIR_CLASS_INIT(&tag[0], "macro");
print_otag(h, TAG_B, 1, tag);
return(1);
}
PAIR_CLASS_INIT(&tag[0], "includes");
print_otag(h, TAG_B, 1, tag);
print_text(h, n->string);
if (NULL != (n = n->next)) {
assert(MDOC_TEXT == n->type);
/*
* XXX This is broken and not easy to fix.
* When using -Oincludes, truncation may occur.
* Dynamic allocation wouldn't help because
* passing long strings to buffmt_includes()
* does not work either.
*/
strlcpy(buf, '<' == *n->string || '"' == *n->string ?
n->string + 1 : n->string, BUFSIZ);
sz = strlen(buf);
if (sz && ('>' == buf[sz - 1] || '"' == buf[sz - 1]))
buf[sz - 1] = '\0';
PAIR_CLASS_INIT(&tag[0], "link-includes");
i = 1;
if (h->base_includes) {
buffmt_includes(h, buf);
PAIR_HREF_INIT(&tag[i], h->buf);
i++;
}
t = print_otag(h, TAG_A, i, tag);
print_text(h, n->string);
print_tagq(h, t);
n = n->next;
}
for ( ; n; n = n->next) {
assert(MDOC_TEXT == n->type);
print_text(h, n->string);
}
return(0);
}
static int
mdoc_vt_pre(MDOC_ARGS)
{
struct htmlpair tag;
if (MDOC_BLOCK == n->type) {
synopsis_pre(h, n);
return(1);
} else if (MDOC_ELEM == n->type) {
synopsis_pre(h, n);
} else if (MDOC_HEAD == n->type)
return(0);
PAIR_CLASS_INIT(&tag, "type");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_ft_pre(MDOC_ARGS)
{
struct htmlpair tag;
synopsis_pre(h, n);
PAIR_CLASS_INIT(&tag, "ftype");
print_otag(h, TAG_I, 1, &tag);
return(1);
}
static int
mdoc_fn_pre(MDOC_ARGS)
{
struct tag *t;
struct htmlpair tag[2];
char nbuf[BUFSIZ];
const char *sp, *ep;
int sz, i, pretty;
pretty = MDOC_SYNPRETTY & n->flags;
synopsis_pre(h, n);
/* Split apart into type and name. */
assert(n->child->string);
sp = n->child->string;
ep = strchr(sp, ' ');
if (NULL != ep) {
PAIR_CLASS_INIT(&tag[0], "ftype");
t = print_otag(h, TAG_I, 1, tag);
while (ep) {
sz = MIN((int)(ep - sp), BUFSIZ - 1);
(void)memcpy(nbuf, sp, (size_t)sz);
nbuf[sz] = '\0';
print_text(h, nbuf);
sp = ++ep;
ep = strchr(sp, ' ');
}
print_tagq(h, t);
}
PAIR_CLASS_INIT(&tag[0], "fname");
/*
* FIXME: only refer to IDs that we know exist.
*/
#if 0
if (MDOC_SYNPRETTY & n->flags) {
nbuf[0] = '\0';
html_idcat(nbuf, sp, BUFSIZ);
PAIR_ID_INIT(&tag[1], nbuf);
} else {
strlcpy(nbuf, "#", BUFSIZ);
html_idcat(nbuf, sp, BUFSIZ);
PAIR_HREF_INIT(&tag[1], nbuf);
}
#endif
t = print_otag(h, TAG_B, 1, tag);
if (sp)
print_text(h, sp);
print_tagq(h, t);
h->flags |= HTML_NOSPACE;
print_text(h, "(");
h->flags |= HTML_NOSPACE;
PAIR_CLASS_INIT(&tag[0], "farg");
bufinit(h);
bufcat_style(h, "white-space", "nowrap");
PAIR_STYLE_INIT(&tag[1], h);
for (n = n->child->next; n; n = n->next) {
i = 1;
if (MDOC_SYNPRETTY & n->flags)
i = 2;
t = print_otag(h, TAG_I, i, tag);
print_text(h, n->string);
print_tagq(h, t);
if (n->next) {
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
}
h->flags |= HTML_NOSPACE;
print_text(h, ")");
if (pretty) {
h->flags |= HTML_NOSPACE;
print_text(h, ";");
}
return(0);
}
static int
mdoc_sm_pre(MDOC_ARGS)
{
if (NULL == n->child)
h->flags ^= HTML_NONOSPACE;
else if (0 == strcmp("on", n->child->string))
h->flags &= ~HTML_NONOSPACE;
else
h->flags |= HTML_NONOSPACE;
if ( ! (HTML_NONOSPACE & h->flags))
h->flags &= ~HTML_NOSPACE;
return(0);
}
static int
mdoc_skip_pre(MDOC_ARGS)
{
return(0);
}
static int
mdoc_pp_pre(MDOC_ARGS)
{
print_paragraph(h);
return(0);
}
static int
mdoc_sp_pre(MDOC_ARGS)
{
struct roffsu su;
struct htmlpair tag;
SCALE_VS_INIT(&su, 1);
if (MDOC_sp == n->tok) {
if (NULL != (n = n->child))
if ( ! a2roffsu(n->string, &su, SCALE_VS))
SCALE_VS_INIT(&su, atoi(n->string));
} else
su.scale = 0.0;
bufinit(h);
bufcat_su(h, "height", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
/* So the div isn't empty: */
print_text(h, "\\~");
return(0);
}
static int
mdoc_lk_pre(MDOC_ARGS)
{
struct htmlpair tag[2];
if (NULL == (n = n->child))
return(0);
assert(MDOC_TEXT == n->type);
PAIR_CLASS_INIT(&tag[0], "link-ext");
PAIR_HREF_INIT(&tag[1], n->string);
print_otag(h, TAG_A, 2, tag);
if (NULL == n->next)
print_text(h, n->string);
for (n = n->next; n; n = n->next)
print_text(h, n->string);
return(0);
}
static int
mdoc_mt_pre(MDOC_ARGS)
{
struct htmlpair tag[2];
struct tag *t;
PAIR_CLASS_INIT(&tag[0], "link-mail");
for (n = n->child; n; n = n->next) {
assert(MDOC_TEXT == n->type);
bufinit(h);
bufcat(h, "mailto:");
bufcat(h, n->string);
PAIR_HREF_INIT(&tag[1], h->buf);
t = print_otag(h, TAG_A, 2, tag);
print_text(h, n->string);
print_tagq(h, t);
}
return(0);
}
static int
mdoc_fo_pre(MDOC_ARGS)
{
struct htmlpair tag;
struct tag *t;
if (MDOC_BODY == n->type) {
h->flags |= HTML_NOSPACE;
print_text(h, "(");
h->flags |= HTML_NOSPACE;
return(1);
} else if (MDOC_BLOCK == n->type) {
synopsis_pre(h, n);
return(1);
}
/* XXX: we drop non-initial arguments as per groff. */
assert(n->child);
assert(n->child->string);
PAIR_CLASS_INIT(&tag, "fname");
t = print_otag(h, TAG_B, 1, &tag);
print_text(h, n->child->string);
print_tagq(h, t);
return(0);
}
static void
mdoc_fo_post(MDOC_ARGS)
{
if (MDOC_BODY != n->type)
return;
h->flags |= HTML_NOSPACE;
print_text(h, ")");
h->flags |= HTML_NOSPACE;
print_text(h, ";");
}
static int
mdoc_in_pre(MDOC_ARGS)
{
struct tag *t;
struct htmlpair tag[2];
int i;
synopsis_pre(h, n);
PAIR_CLASS_INIT(&tag[0], "includes");
print_otag(h, TAG_B, 1, tag);
/*
* The first argument of the `In' gets special treatment as
* being a linked value. Subsequent values are printed
* afterward. groff does similarly. This also handles the case
* of no children.
*/
if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags)
print_text(h, "#include");
print_text(h, "<");
h->flags |= HTML_NOSPACE;
if (NULL != (n = n->child)) {
assert(MDOC_TEXT == n->type);
PAIR_CLASS_INIT(&tag[0], "link-includes");
i = 1;
if (h->base_includes) {
buffmt_includes(h, n->string);
PAIR_HREF_INIT(&tag[i], h->buf);
i++;
}
t = print_otag(h, TAG_A, i, tag);
print_text(h, n->string);
print_tagq(h, t);
n = n->next;
}
h->flags |= HTML_NOSPACE;
print_text(h, ">");
for ( ; n; n = n->next) {
assert(MDOC_TEXT == n->type);
print_text(h, n->string);
}
return(0);
}
static int
mdoc_ic_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "cmd");
print_otag(h, TAG_B, 1, &tag);
return(1);
}
static int
mdoc_rv_pre(MDOC_ARGS)
{
struct htmlpair tag;
struct tag *t;
int nchild;
if (n->prev)
print_otag(h, TAG_BR, 0, NULL);
PAIR_CLASS_INIT(&tag, "fname");
nchild = n->nchild;
if (nchild > 0) {
print_text(h, "The");
for (n = n->child; n; n = n->next) {
t = print_otag(h, TAG_B, 1, &tag);
print_text(h, n->string);
print_tagq(h, t);
h->flags |= HTML_NOSPACE;
print_text(h, "()");
if (n->next == NULL)
continue;
if (nchild > 2) {
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
if (n->next->next == NULL)
print_text(h, "and");
}
if (nchild > 1)
print_text(h, "functions return");
else
print_text(h, "function returns");
print_text(h, "the value\\~0 if successful;");
} else
print_text(h, "Upon successful completion,"
" the value\\~0 is returned;");
print_text(h, "otherwise the value\\~\\-1 is returned"
" and the global variable");
PAIR_CLASS_INIT(&tag, "var");
t = print_otag(h, TAG_B, 1, &tag);
print_text(h, "errno");
print_tagq(h, t);
print_text(h, "is set to indicate the error.");
return(0);
}
static int
mdoc_va_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "var");
print_otag(h, TAG_B, 1, &tag);
return(1);
}
static int
mdoc_ap_pre(MDOC_ARGS)
{
h->flags |= HTML_NOSPACE;
print_text(h, "\\(aq");
h->flags |= HTML_NOSPACE;
return(1);
}
static int
mdoc_bf_pre(MDOC_ARGS)
{
struct htmlpair tag[2];
struct roffsu su;
if (MDOC_HEAD == n->type)
return(0);
else if (MDOC_BODY != n->type)
return(1);
if (FONT_Em == n->norm->Bf.font)
PAIR_CLASS_INIT(&tag[0], "emph");
else if (FONT_Sy == n->norm->Bf.font)
PAIR_CLASS_INIT(&tag[0], "symb");
else if (FONT_Li == n->norm->Bf.font)
PAIR_CLASS_INIT(&tag[0], "lit");
else
PAIR_CLASS_INIT(&tag[0], "none");
/*
* We want this to be inline-formatted, but needs to be div to
* accept block children.
*/
bufinit(h);
bufcat_style(h, "display", "inline");
SCALE_HS_INIT(&su, 1);
/* Needs a left-margin for spacing. */
bufcat_su(h, "margin-left", &su);
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_DIV, 2, tag);
return(1);
}
static int
mdoc_ms_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "symb");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_igndelim_pre(MDOC_ARGS)
{
h->flags |= HTML_IGNDELIM;
return(1);
}
static void
mdoc_pf_post(MDOC_ARGS)
{
- h->flags |= HTML_NOSPACE;
+ if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
+ h->flags |= HTML_NOSPACE;
}
static int
mdoc_rs_pre(MDOC_ARGS)
{
struct htmlpair tag;
if (MDOC_BLOCK != n->type)
return(1);
if (n->prev && SEC_SEE_ALSO == n->sec)
print_paragraph(h);
PAIR_CLASS_INIT(&tag, "ref");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_no_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "none");
print_otag(h, TAG_CODE, 1, &tag);
return(1);
}
static int
mdoc_li_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "lit");
print_otag(h, TAG_CODE, 1, &tag);
return(1);
}
static int
mdoc_sy_pre(MDOC_ARGS)
{
struct htmlpair tag;
PAIR_CLASS_INIT(&tag, "symb");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc_bt_pre(MDOC_ARGS)
{
print_text(h, "is currently in beta test.");
return(0);
}
static int
mdoc_ud_pre(MDOC_ARGS)
{
print_text(h, "currently under development.");
return(0);
}
static int
mdoc_lb_pre(MDOC_ARGS)
{
struct htmlpair tag;
if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev)
print_otag(h, TAG_BR, 0, NULL);
PAIR_CLASS_INIT(&tag, "lib");
print_otag(h, TAG_SPAN, 1, &tag);
return(1);
}
static int
mdoc__x_pre(MDOC_ARGS)
{
struct htmlpair tag[2];
enum htmltag t;
t = TAG_SPAN;
switch (n->tok) {
case MDOC__A:
PAIR_CLASS_INIT(&tag[0], "ref-auth");
if (n->prev && MDOC__A == n->prev->tok)
if (NULL == n->next || MDOC__A != n->next->tok)
print_text(h, "and");
break;
case MDOC__B:
PAIR_CLASS_INIT(&tag[0], "ref-book");
t = TAG_I;
break;
case MDOC__C:
PAIR_CLASS_INIT(&tag[0], "ref-city");
break;
case MDOC__D:
PAIR_CLASS_INIT(&tag[0], "ref-date");
break;
case MDOC__I:
PAIR_CLASS_INIT(&tag[0], "ref-issue");
t = TAG_I;
break;
case MDOC__J:
PAIR_CLASS_INIT(&tag[0], "ref-jrnl");
t = TAG_I;
break;
case MDOC__N:
PAIR_CLASS_INIT(&tag[0], "ref-num");
break;
case MDOC__O:
PAIR_CLASS_INIT(&tag[0], "ref-opt");
break;
case MDOC__P:
PAIR_CLASS_INIT(&tag[0], "ref-page");
break;
case MDOC__Q:
PAIR_CLASS_INIT(&tag[0], "ref-corp");
break;
case MDOC__R:
PAIR_CLASS_INIT(&tag[0], "ref-rep");
break;
case MDOC__T:
PAIR_CLASS_INIT(&tag[0], "ref-title");
break;
case MDOC__U:
PAIR_CLASS_INIT(&tag[0], "link-ref");
break;
case MDOC__V:
PAIR_CLASS_INIT(&tag[0], "ref-vol");
break;
default:
abort();
/* NOTREACHED */
}
if (MDOC__U != n->tok) {
print_otag(h, t, 1, tag);
return(1);
}
PAIR_HREF_INIT(&tag[1], n->child->string);
print_otag(h, TAG_A, 2, tag);
return(1);
}
static void
mdoc__x_post(MDOC_ARGS)
{
if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
if (NULL == n->next->next || MDOC__A != n->next->next->tok)
if (NULL == n->prev || MDOC__A != n->prev->tok)
return;
/* TODO: %U */
if (NULL == n->parent || MDOC_Rs != n->parent->tok)
return;
h->flags |= HTML_NOSPACE;
print_text(h, n->next ? "," : ".");
}
static int
mdoc_bk_pre(MDOC_ARGS)
{
switch (n->type) {
case MDOC_BLOCK:
break;
case MDOC_HEAD:
return(0);
case MDOC_BODY:
if (n->parent->args || 0 == n->prev->nchild)
h->flags |= HTML_PREKEEP;
break;
default:
abort();
/* NOTREACHED */
}
return(1);
}
static void
mdoc_bk_post(MDOC_ARGS)
{
if (MDOC_BODY == n->type)
h->flags &= ~(HTML_KEEP | HTML_PREKEEP);
}
static int
mdoc_quote_pre(MDOC_ARGS)
{
struct htmlpair tag;
if (MDOC_BODY != n->type)
return(1);
switch (n->tok) {
case MDOC_Ao:
/* FALLTHROUGH */
case MDOC_Aq:
print_text(h, n->parent->prev != NULL &&
n->parent->prev->tok == MDOC_An ? "<" : "\\(la");
break;
case MDOC_Bro:
/* FALLTHROUGH */
case MDOC_Brq:
print_text(h, "\\(lC");
break;
case MDOC_Bo:
/* FALLTHROUGH */
case MDOC_Bq:
print_text(h, "\\(lB");
break;
case MDOC_Oo:
/* FALLTHROUGH */
case MDOC_Op:
print_text(h, "\\(lB");
h->flags |= HTML_NOSPACE;
PAIR_CLASS_INIT(&tag, "opt");
print_otag(h, TAG_SPAN, 1, &tag);
break;
case MDOC_En:
if (NULL == n->norm->Es ||
NULL == n->norm->Es->child)
return(1);
print_text(h, n->norm->Es->child->string);
break;
case MDOC_Eo:
break;
case MDOC_Do:
/* FALLTHROUGH */
case MDOC_Dq:
/* FALLTHROUGH */
case MDOC_Qo:
/* FALLTHROUGH */
case MDOC_Qq:
print_text(h, "\\(lq");
break;
case MDOC_Po:
/* FALLTHROUGH */
case MDOC_Pq:
print_text(h, "(");
break;
case MDOC_Ql:
print_text(h, "\\(oq");
h->flags |= HTML_NOSPACE;
PAIR_CLASS_INIT(&tag, "lit");
print_otag(h, TAG_CODE, 1, &tag);
break;
case MDOC_So:
/* FALLTHROUGH */
case MDOC_Sq:
print_text(h, "\\(oq");
break;
default:
abort();
/* NOTREACHED */
}
h->flags |= HTML_NOSPACE;
return(1);
}
static void
mdoc_quote_post(MDOC_ARGS)
{
if (n->type != MDOC_BODY && n->type != MDOC_ELEM)
return;
if ( ! (n->tok == MDOC_En ||
(n->tok == MDOC_Eo && n->end == ENDBODY_SPACE)))
h->flags |= HTML_NOSPACE;
switch (n->tok) {
case MDOC_Ao:
/* FALLTHROUGH */
case MDOC_Aq:
print_text(h, n->parent->prev != NULL &&
n->parent->prev->tok == MDOC_An ? ">" : "\\(ra");
break;
case MDOC_Bro:
/* FALLTHROUGH */
case MDOC_Brq:
print_text(h, "\\(rC");
break;
case MDOC_Oo:
/* FALLTHROUGH */
case MDOC_Op:
/* FALLTHROUGH */
case MDOC_Bo:
/* FALLTHROUGH */
case MDOC_Bq:
print_text(h, "\\(rB");
break;
case MDOC_En:
if (NULL != n->norm->Es &&
NULL != n->norm->Es->child &&
NULL != n->norm->Es->child->next) {
h->flags |= HTML_NOSPACE;
print_text(h, n->norm->Es->child->next->string);
}
break;
case MDOC_Eo:
break;
case MDOC_Qo:
/* FALLTHROUGH */
case MDOC_Qq:
/* FALLTHROUGH */
case MDOC_Do:
/* FALLTHROUGH */
case MDOC_Dq:
print_text(h, "\\(rq");
break;
case MDOC_Po:
/* FALLTHROUGH */
case MDOC_Pq:
print_text(h, ")");
break;
case MDOC_Ql:
/* FALLTHROUGH */
case MDOC_So:
/* FALLTHROUGH */
case MDOC_Sq:
print_text(h, "\\(cq");
break;
default:
abort();
/* NOTREACHED */
}
}
Index: vendor/mdocml/dist/mdoc_macro.c
===================================================================
--- vendor/mdocml/dist/mdoc_macro.c (revision 276215)
+++ vendor/mdocml/dist/mdoc_macro.c (revision 276216)
@@ -1,1548 +1,1564 @@
-/* $Id: mdoc_macro.c,v 1.154 2014/11/29 04:31:35 schwarze Exp $ */
+/* $Id: mdoc_macro.c,v 1.157 2014/12/13 13:14:39 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons
* Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include "mdoc.h"
#include "mandoc.h"
#include "libmdoc.h"
#include "libmandoc.h"
enum rew { /* see rew_dohalt() */
REWIND_NONE,
REWIND_THIS,
REWIND_MORE,
REWIND_FORCE,
REWIND_LATER,
REWIND_ERROR
};
static void blk_full(MACRO_PROT_ARGS);
static void blk_exp_close(MACRO_PROT_ARGS);
static void blk_part_exp(MACRO_PROT_ARGS);
static void blk_part_imp(MACRO_PROT_ARGS);
static void ctx_synopsis(MACRO_PROT_ARGS);
static void in_line_eoln(MACRO_PROT_ARGS);
static void in_line_argn(MACRO_PROT_ARGS);
static void in_line(MACRO_PROT_ARGS);
static void phrase_ta(MACRO_PROT_ARGS);
static void dword(struct mdoc *, int, int, const char *,
enum mdelim, int);
static void append_delims(struct mdoc *, int, int *, char *);
-static enum mdoct lookup(enum mdoct, const char *);
+static enum mdoct lookup(struct mdoc *, enum mdoct,
+ int, int, const char *);
static int macro_or_word(MACRO_PROT_ARGS, int);
static int make_pending(struct mdoc_node *, enum mdoct,
struct mdoc *, int, int);
static int parse_rest(struct mdoc *, enum mdoct,
int, int *, char *);
static enum mdoct rew_alt(enum mdoct);
static enum rew rew_dohalt(enum mdoct, enum mdoc_type,
const struct mdoc_node *);
static void rew_elem(struct mdoc *, enum mdoct);
static void rew_last(struct mdoc *, const struct mdoc_node *);
static void rew_sub(enum mdoc_type, struct mdoc *,
enum mdoct, int, int);
const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ap */
{ in_line_eoln, MDOC_PROLOGUE }, /* Dd */
{ in_line_eoln, MDOC_PROLOGUE }, /* Dt */
{ in_line_eoln, MDOC_PROLOGUE }, /* Os */
{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* Sh */
{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* Ss */
{ in_line_eoln, 0 }, /* Pp */
{ blk_part_imp, MDOC_PARSED | MDOC_JOIN }, /* D1 */
{ blk_part_imp, MDOC_PARSED | MDOC_JOIN }, /* Dl */
{ blk_full, MDOC_EXPLICIT }, /* Bd */
{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ed */
{ blk_full, MDOC_EXPLICIT }, /* Bl */
{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* El */
{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* It */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* An */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Cd */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */
{ in_line_eoln, 0 }, /* Ex */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */
{ in_line_eoln, 0 }, /* Fd */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ft */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ic */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* In */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Li */
{ blk_full, MDOC_JOIN }, /* Nd */
{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ot */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
{ in_line_eoln, 0 }, /* Rv */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */
{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */
{ in_line_eoln, MDOC_JOIN }, /* %A */
{ in_line_eoln, MDOC_JOIN }, /* %B */
{ in_line_eoln, MDOC_JOIN }, /* %D */
{ in_line_eoln, MDOC_JOIN }, /* %I */
{ in_line_eoln, MDOC_JOIN }, /* %J */
{ in_line_eoln, 0 }, /* %N */
{ in_line_eoln, MDOC_JOIN }, /* %O */
{ in_line_eoln, 0 }, /* %P */
{ in_line_eoln, MDOC_JOIN }, /* %R */
{ in_line_eoln, MDOC_JOIN }, /* %T */
{ in_line_eoln, 0 }, /* %V */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Ac */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Ao */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Aq */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* At */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Bc */
{ blk_full, MDOC_EXPLICIT }, /* Bf */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Bo */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Bq */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bsx */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bx */
{ in_line_eoln, 0 }, /* Db */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Dc */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Do */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Dq */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ec */
{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ef */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Em */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ms */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* No */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED |
MDOC_IGNDELIM | MDOC_JOIN }, /* Ns */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Pc */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_IGNDELIM }, /* Pf */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Po */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Pq */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Qc */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ql */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Qo */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Qq */
{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Re */
{ blk_full, MDOC_EXPLICIT }, /* Rs */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Sc */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* So */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sq */
{ in_line_argn, 0 }, /* Sm */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sx */
{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sy */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ux */
{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */
{ blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Fc */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Oo */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Oc */
{ blk_full, MDOC_EXPLICIT }, /* Bk */
{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ek */
{ in_line_eoln, 0 }, /* Bt */
{ in_line_eoln, 0 }, /* Hf */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fr */
{ in_line_eoln, 0 }, /* Ud */
{ in_line, 0 }, /* Lb */
{ in_line_eoln, 0 }, /* Lp */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Lk */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Mt */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Brq */
{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Bro */
{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
MDOC_EXPLICIT | MDOC_JOIN }, /* Brc */
{ in_line_eoln, MDOC_JOIN }, /* %C */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Es */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* En */
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */
{ in_line_eoln, MDOC_JOIN }, /* %Q */
{ in_line_eoln, 0 }, /* br */
{ in_line_eoln, 0 }, /* sp */
{ in_line_eoln, 0 }, /* %U */
{ phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */
{ in_line_eoln, MDOC_PROLOGUE }, /* ll */
};
const struct mdoc_macro * const mdoc_macros = __mdoc_macros;
/*
* This is called at the end of parsing. It must traverse up the tree,
* closing out open [implicit] scopes. Obviously, open explicit scopes
* are errors.
*/
void
mdoc_macroend(struct mdoc *mdoc)
{
struct mdoc_node *n;
/* Scan for open explicit scopes. */
n = mdoc->last->flags & MDOC_VALID ?
mdoc->last->parent : mdoc->last;
for ( ; n; n = n->parent)
if (n->type == MDOC_BLOCK &&
mdoc_macros[n->tok].flags & MDOC_EXPLICIT)
mandoc_msg(MANDOCERR_BLK_NOEND, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
/* Rewind to the first. */
rew_last(mdoc, mdoc->first);
}
/*
* Look up the macro at *p called by "from",
* or as a line macro if from == MDOC_MAX.
*/
static enum mdoct
-lookup(enum mdoct from, const char *p)
+lookup(struct mdoc *mdoc, enum mdoct from, int line, int ppos, const char *p)
{
enum mdoct res;
if (from == MDOC_MAX || mdoc_macros[from].flags & MDOC_PARSED) {
res = mdoc_hash_find(p);
- if (res != MDOC_MAX && mdoc_macros[res].flags & MDOC_CALLABLE)
- return(res);
+ if (res != MDOC_MAX) {
+ if (mdoc_macros[res].flags & MDOC_CALLABLE)
+ return(res);
+ if (res != MDOC_br && res != MDOC_sp && res != MDOC_ll)
+ mandoc_msg(MANDOCERR_MACRO_CALL,
+ mdoc->parse, line, ppos, p);
+ }
}
return(MDOC_MAX);
}
static void
rew_last(struct mdoc *mdoc, const struct mdoc_node *to)
{
struct mdoc_node *n, *np;
assert(to);
mdoc->next = MDOC_NEXT_SIBLING;
while (mdoc->last != to) {
/*
* Save the parent here, because we may delete the
* mdoc->last node in the post-validation phase and reset
* it to mdoc->last->parent, causing a step in the closing
* out to be lost.
*/
np = mdoc->last->parent;
mdoc_valid_post(mdoc);
n = mdoc->last;
mdoc->last = np;
assert(mdoc->last);
mdoc->last->last = n;
}
mdoc_valid_post(mdoc);
}
/*
* For a block closing macro, return the corresponding opening one.
* Otherwise, return the macro itself.
*/
static enum mdoct
rew_alt(enum mdoct tok)
{
switch (tok) {
case MDOC_Ac:
return(MDOC_Ao);
case MDOC_Bc:
return(MDOC_Bo);
case MDOC_Brc:
return(MDOC_Bro);
case MDOC_Dc:
return(MDOC_Do);
case MDOC_Ec:
return(MDOC_Eo);
case MDOC_Ed:
return(MDOC_Bd);
case MDOC_Ef:
return(MDOC_Bf);
case MDOC_Ek:
return(MDOC_Bk);
case MDOC_El:
return(MDOC_Bl);
case MDOC_Fc:
return(MDOC_Fo);
case MDOC_Oc:
return(MDOC_Oo);
case MDOC_Pc:
return(MDOC_Po);
case MDOC_Qc:
return(MDOC_Qo);
case MDOC_Re:
return(MDOC_Rs);
case MDOC_Sc:
return(MDOC_So);
case MDOC_Xc:
return(MDOC_Xo);
default:
return(tok);
}
/* NOTREACHED */
}
/*
* Rewinding to tok, how do we have to handle *p?
* REWIND_NONE: *p would delimit tok, but no tok scope is open
* inside *p, so there is no need to rewind anything at all.
* REWIND_THIS: *p matches tok, so rewind *p and nothing else.
* REWIND_MORE: *p is implicit, rewind it and keep searching for tok.
* REWIND_FORCE: *p is explicit, but tok is full, force rewinding *p.
* REWIND_LATER: *p is explicit and still open, postpone rewinding.
* REWIND_ERROR: No tok block is open at all.
*/
static enum rew
rew_dohalt(enum mdoct tok, enum mdoc_type type,
const struct mdoc_node *p)
{
/*
* No matching token, no delimiting block, no broken block.
* This can happen when full implicit macros are called for
* the first time but try to rewind their previous
* instance anyway.
*/
if (MDOC_ROOT == p->type)
return(MDOC_BLOCK == type &&
MDOC_EXPLICIT & mdoc_macros[tok].flags ?
REWIND_ERROR : REWIND_NONE);
/*
* When starting to rewind, skip plain text
* and nodes that have already been rewound.
*/
if (MDOC_TEXT == p->type || MDOC_VALID & p->flags)
return(REWIND_MORE);
/*
* The easiest case: Found a matching token.
* This applies to both blocks and elements.
*/
tok = rew_alt(tok);
if (tok == p->tok)
return(p->end ? REWIND_NONE :
type == p->type ? REWIND_THIS : REWIND_MORE);
/*
* While elements do require rewinding for themselves,
* they never affect rewinding of other nodes.
*/
if (MDOC_ELEM == p->type)
return(REWIND_MORE);
/*
* Blocks delimited by our target token get REWIND_MORE.
* Blocks delimiting our target token get REWIND_NONE.
*/
switch (tok) {
case MDOC_Bl:
if (MDOC_It == p->tok)
return(REWIND_MORE);
break;
case MDOC_It:
if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
return(REWIND_NONE);
break;
/*
* XXX Badly nested block handling still fails badly
* when one block is breaking two blocks of the same type.
* This is an incomplete and extremely ugly workaround,
* required to let the OpenBSD tree build.
*/
case MDOC_Oo:
if (MDOC_Op == p->tok)
return(REWIND_MORE);
break;
case MDOC_Nm:
return(REWIND_NONE);
case MDOC_Nd:
/* FALLTHROUGH */
case MDOC_Ss:
if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
return(REWIND_NONE);
/* FALLTHROUGH */
case MDOC_Sh:
if (MDOC_ROOT == p->parent->type)
return(REWIND_THIS);
if (MDOC_Nd == p->tok || MDOC_Ss == p->tok ||
MDOC_Sh == p->tok)
return(REWIND_MORE);
break;
default:
break;
}
/*
* Default block rewinding rules.
* In particular, always skip block end markers,
* and let all blocks rewind Nm children.
* Do not warn again when closing a block,
* since closing the body already warned.
*/
if (ENDBODY_NOT != p->end || MDOC_Nm == p->tok ||
MDOC_BLOCK == type || (MDOC_BLOCK == p->type &&
! (MDOC_EXPLICIT & mdoc_macros[tok].flags)))
return(REWIND_MORE);
/*
* By default, closing out full blocks
* forces closing of broken explicit blocks,
* while closing out partial blocks
* allows delayed rewinding by default.
*/
return (&blk_full == mdoc_macros[tok].fp ?
REWIND_FORCE : REWIND_LATER);
}
static void
rew_elem(struct mdoc *mdoc, enum mdoct tok)
{
struct mdoc_node *n;
n = mdoc->last;
if (MDOC_ELEM != n->type)
n = n->parent;
assert(MDOC_ELEM == n->type);
assert(tok == n->tok);
rew_last(mdoc, n);
}
/*
* We are trying to close a block identified by tok,
* but the child block *broken is still open.
* Thus, postpone closing the tok block
* until the rew_sub call closing *broken.
*/
static int
make_pending(struct mdoc_node *broken, enum mdoct tok,
struct mdoc *mdoc, int line, int ppos)
{
struct mdoc_node *breaker;
/*
* Iterate backwards, searching for the block matching tok,
* that is, the block breaking the *broken block.
*/
for (breaker = broken->parent; breaker; breaker = breaker->parent) {
/*
* If the *broken block had already been broken before
* and we encounter its breaker, make the tok block
* pending on the inner breaker.
* Graphically, "[A breaker=[B broken=[C->B B] tok=A] C]"
* becomes "[A broken=[B [C->B B] tok=A] C]"
* and finally "[A [B->A [C->B B] A] C]".
*/
if (breaker == broken->pending) {
broken = breaker;
continue;
}
if (REWIND_THIS != rew_dohalt(tok, MDOC_BLOCK, breaker))
continue;
if (MDOC_BODY == broken->type)
broken = broken->parent;
/*
* Found the breaker.
* If another, outer breaker is already pending on
* the *broken block, we must not clobber the link
* to the outer breaker, but make it pending on the
* new, now inner breaker.
* Graphically, "[A breaker=[B broken=[C->A A] tok=B] C]"
* becomes "[A breaker=[B->A broken=[C A] tok=B] C]"
* and finally "[A [B->A [C->B A] B] C]".
*/
if (broken->pending) {
struct mdoc_node *taker;
/*
* If the breaker had also been broken before,
* it cannot take on the outer breaker itself,
* but must hand it on to its own breakers.
* Graphically, this is the following situation:
* "[A [B breaker=[C->B B] broken=[D->A A] tok=C] D]"
* "[A taker=[B->A breaker=[C->B B] [D->C A] C] D]"
*/
taker = breaker;
while (taker->pending)
taker = taker->pending;
taker->pending = broken->pending;
}
broken->pending = breaker;
mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos,
"%s breaks %s", mdoc_macronames[tok],
mdoc_macronames[broken->tok]);
return(1);
}
/*
* Found no matching block for tok.
* Are you trying to close a block that is not open?
*/
return(0);
}
static void
rew_sub(enum mdoc_type t, struct mdoc *mdoc,
enum mdoct tok, int line, int ppos)
{
struct mdoc_node *n;
n = mdoc->last;
while (n) {
switch (rew_dohalt(tok, t, n)) {
case REWIND_NONE:
return;
case REWIND_THIS:
n->lastline = line -
(mdoc->flags & MDOC_NEWLINE &&
! (mdoc_macros[tok].flags & MDOC_EXPLICIT));
break;
case REWIND_FORCE:
mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse,
line, ppos, "%s breaks %s",
mdoc_macronames[tok],
mdoc_macronames[n->tok]);
/* FALLTHROUGH */
case REWIND_MORE:
n->lastline = line -
(mdoc->flags & MDOC_NEWLINE ? 1 : 0);
n = n->parent;
continue;
case REWIND_LATER:
if (make_pending(n, tok, mdoc, line, ppos) ||
t != MDOC_BLOCK)
return;
/* FALLTHROUGH */
case REWIND_ERROR:
mandoc_msg(MANDOCERR_BLK_NOTOPEN,
mdoc->parse, line, ppos,
mdoc_macronames[tok]);
return;
}
break;
}
assert(n);
rew_last(mdoc, n);
/*
* The current block extends an enclosing block.
* Now that the current block ends, close the enclosing block, too.
*/
while ((n = n->pending) != NULL) {
rew_last(mdoc, n);
if (n->type == MDOC_HEAD)
mdoc_body_alloc(mdoc, n->line, n->pos, n->tok);
}
}
/*
* Allocate a word and check whether it's punctuation or not.
* Punctuation consists of those tokens found in mdoc_isdelim().
*/
static void
dword(struct mdoc *mdoc, int line, int col, const char *p,
enum mdelim d, int may_append)
{
if (d == DELIM_MAX)
d = mdoc_isdelim(p);
if (may_append &&
! (mdoc->flags & (MDOC_SYNOPSIS | MDOC_KEEP | MDOC_SMOFF)) &&
d == DELIM_NONE && mdoc->last->type == MDOC_TEXT &&
mdoc_isdelim(mdoc->last->string) == DELIM_NONE) {
mdoc_word_append(mdoc, p);
return;
}
mdoc_word_alloc(mdoc, line, col, p);
/*
* If the word consists of a bare delimiter,
* flag the new node accordingly,
* unless doing so was vetoed by the invoking macro.
* Always clear the veto, it is only valid for one word.
*/
if (d == DELIM_OPEN)
mdoc->last->flags |= MDOC_DELIMO;
else if (d == DELIM_CLOSE &&
! (mdoc->flags & MDOC_NODELIMC) &&
mdoc->last->parent->tok != MDOC_Fd)
mdoc->last->flags |= MDOC_DELIMC;
mdoc->flags &= ~MDOC_NODELIMC;
}
static void
append_delims(struct mdoc *mdoc, int line, int *pos, char *buf)
{
char *p;
int la;
if (buf[*pos] == '\0')
return;
for (;;) {
la = *pos;
if (mdoc_args(mdoc, line, pos, buf, MDOC_MAX, &p) == ARGS_EOLN)
break;
dword(mdoc, line, la, p, DELIM_MAX, 1);
/*
* If we encounter end-of-sentence symbols, then trigger
* the double-space.
*
* XXX: it's easy to allow this to propagate outward to
* the last symbol, such that `. )' will cause the
* correct double-spacing. However, (1) groff isn't
* smart enough to do this and (2) it would require
* knowing which symbols break this behaviour, for
* example, `. ;' shouldn't propagate the double-space.
*/
if (mandoc_eos(p, strlen(p)))
mdoc->last->flags |= MDOC_EOS;
}
}
/*
* Parse one word.
* If it is a macro, call it and return 1.
* Otherwise, allocate it and return 0.
*/
static int
macro_or_word(MACRO_PROT_ARGS, int parsed)
{
char *p;
enum mdoct ntok;
p = buf + ppos;
ntok = MDOC_MAX;
- if (mdoc->flags & MDOC_PHRASELIT)
- /* nothing */;
- else if (*p == '"')
+ if (*p == '"')
p++;
- else if (parsed)
- ntok = lookup(tok, p);
+ else if (parsed && ! (mdoc->flags & MDOC_PHRASELIT))
+ ntok = lookup(mdoc, tok, line, ppos, p);
if (ntok == MDOC_MAX) {
dword(mdoc, line, ppos, p, DELIM_MAX, tok == MDOC_MAX ||
mdoc_macros[tok].flags & MDOC_JOIN);
return(0);
} else {
if (mdoc_macros[tok].fp == in_line_eoln)
rew_elem(mdoc, tok);
mdoc_macro(mdoc, ntok, line, ppos, pos, buf);
if (tok == MDOC_MAX)
append_delims(mdoc, line, pos, buf);
return(1);
}
}
/*
* Close out block partial/full explicit.
*/
static void
blk_exp_close(MACRO_PROT_ARGS)
{
struct mdoc_node *body; /* Our own body. */
struct mdoc_node *endbody; /* Our own end marker. */
struct mdoc_node *later; /* A sub-block starting later. */
struct mdoc_node *n; /* For searching backwards. */
int j, lastarg, maxargs, flushed, nl;
enum margserr ac;
enum mdoct atok, ntok;
char *p;
nl = MDOC_NEWLINE & mdoc->flags;
switch (tok) {
case MDOC_Ec:
maxargs = 1;
break;
case MDOC_Ek:
mdoc->flags &= ~MDOC_KEEP;
/* FALLTHROUGH */
default:
maxargs = 0;
break;
}
/*
* Search backwards for beginnings of blocks,
* both of our own and of pending sub-blocks.
*/
atok = rew_alt(tok);
body = endbody = later = NULL;
for (n = mdoc->last; n; n = n->parent) {
if (n->flags & MDOC_VALID)
continue;
/* Remember the start of our own body. */
if (n->type == MDOC_BODY && atok == n->tok) {
if (n->end == ENDBODY_NOT)
body = n;
continue;
}
if (n->type != MDOC_BLOCK || n->tok == MDOC_Nm)
continue;
if (atok == n->tok) {
assert(body);
/*
* Found the start of our own block.
* When there is no pending sub block,
* just proceed to closing out.
*/
if (later == NULL)
break;
/*
* When there is a pending sub block,
* postpone closing out the current block
* until the rew_sub() closing out the sub-block.
*/
make_pending(later, tok, mdoc, line, ppos);
/*
* Mark the place where the formatting - but not
* the scope - of the current block ends.
*/
mdoc_endbody_alloc(mdoc, line, ppos,
atok, body, ENDBODY_SPACE);
/*
* If a block closing macro taking arguments
* breaks another block, put the arguments
* into the end marker and remeber the
* end marker in order to close it out.
*/
if (maxargs) {
endbody = mdoc->last;
mdoc->next = MDOC_NEXT_CHILD;
}
break;
}
/*
* When finding an open sub block, remember the last
* open explicit block, or, in case there are only
* implicit ones, the first open implicit block.
*/
if (later &&
mdoc_macros[later->tok].flags & MDOC_EXPLICIT)
continue;
if (n->tok != MDOC_It)
later = n;
}
rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
if ( ! (mdoc_macros[tok].flags & MDOC_PARSED)) {
if (buf[*pos] != '\0')
mandoc_vmsg(MANDOCERR_ARG_SKIP,
mdoc->parse, line, ppos,
"%s %s", mdoc_macronames[tok],
buf + *pos);
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
return;
}
if (maxargs && endbody == NULL) {
if (n == NULL) {
/*
* Stray .Ec without previous .Eo:
* Break the output line, ignore any arguments.
*/
mdoc_elem_alloc(mdoc, line, ppos, MDOC_br, NULL);
rew_elem(mdoc, MDOC_br);
} else
mdoc_tail_alloc(mdoc, line, ppos, atok);
}
flushed = n == NULL;
for (j = 0; ; j++) {
lastarg = *pos;
if (j == maxargs && ! flushed) {
if (endbody == NULL)
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
else
rew_last(mdoc, endbody);
flushed = 1;
}
ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
if (ac == ARGS_PUNCT || ac == ARGS_EOLN)
break;
- ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p);
+ ntok = ac == ARGS_QWORD ? MDOC_MAX :
+ lookup(mdoc, tok, line, lastarg, p);
if (ntok == MDOC_MAX) {
dword(mdoc, line, lastarg, p, DELIM_MAX,
MDOC_JOIN & mdoc_macros[tok].flags);
continue;
}
if ( ! flushed) {
if (endbody == NULL)
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
else
rew_last(mdoc, endbody);
flushed = 1;
}
mdoc->flags &= ~MDOC_NEWLINE;
mdoc_macro(mdoc, ntok, line, lastarg, pos, buf);
break;
}
if ( ! flushed) {
if (endbody == NULL)
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
else
rew_last(mdoc, endbody);
}
if (nl)
append_delims(mdoc, line, pos, buf);
}
static void
in_line(MACRO_PROT_ARGS)
{
int la, scope, cnt, firstarg, mayopen, nc, nl;
enum mdoct ntok;
enum margserr ac;
enum mdelim d;
struct mdoc_arg *arg;
char *p;
nl = MDOC_NEWLINE & mdoc->flags;
/*
* Whether we allow ignored elements (those without content,
* usually because of reserved words) to squeak by.
*/
switch (tok) {
case MDOC_An:
/* FALLTHROUGH */
case MDOC_Ar:
/* FALLTHROUGH */
case MDOC_Fl:
/* FALLTHROUGH */
case MDOC_Mt:
/* FALLTHROUGH */
case MDOC_Nm:
/* FALLTHROUGH */
case MDOC_Pa:
nc = 1;
break;
default:
nc = 0;
break;
}
mdoc_argv(mdoc, line, tok, &arg, pos, buf);
d = DELIM_NONE;
firstarg = 1;
mayopen = 1;
for (cnt = scope = 0;; ) {
la = *pos;
ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
/*
* At the end of a macro line,
* opening delimiters do not suppress spacing.
*/
if (ac == ARGS_EOLN) {
if (d == DELIM_OPEN)
mdoc->last->flags &= ~MDOC_DELIMO;
break;
}
/*
* The rest of the macro line is only punctuation,
* to be handled by append_delims().
* If there were no other arguments,
* do not allow the first one to suppress spacing,
* even if it turns out to be a closing one.
*/
if (ac == ARGS_PUNCT) {
if (cnt == 0 && nc == 0)
mdoc->flags |= MDOC_NODELIMC;
break;
}
ntok = (ac == ARGS_QWORD || (tok == MDOC_Fn && !cnt)) ?
- MDOC_MAX : lookup(tok, p);
+ MDOC_MAX : lookup(mdoc, tok, line, la, p);
/*
* In this case, we've located a submacro and must
* execute it. Close out scope, if open. If no
* elements have been generated, either create one (nc)
* or raise a warning.
*/
if (ntok != MDOC_MAX) {
if (scope)
rew_elem(mdoc, tok);
if (nc && ! cnt) {
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
rew_last(mdoc, mdoc->last);
} else if ( ! nc && ! cnt) {
mdoc_argv_free(arg);
mandoc_msg(MANDOCERR_MACRO_EMPTY,
mdoc->parse, line, ppos,
mdoc_macronames[tok]);
}
mdoc_macro(mdoc, ntok, line, la, pos, buf);
if (nl)
append_delims(mdoc, line, pos, buf);
return;
}
/*
* Non-quote-enclosed punctuation. Set up our scope, if
* a word; rewind the scope, if a delimiter; then append
* the word.
*/
d = ac == ARGS_QWORD ? DELIM_NONE : mdoc_isdelim(p);
if (DELIM_NONE != d) {
/*
* If we encounter closing punctuation, no word
* has been emitted, no scope is open, and we're
* allowed to have an empty element, then start
* a new scope.
*/
if ((d == DELIM_CLOSE ||
(d == DELIM_MIDDLE && tok == MDOC_Fl)) &&
!cnt && !scope && nc && mayopen) {
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
scope = 1;
cnt++;
if (tok == MDOC_Nm)
mayopen = 0;
}
/*
* Close out our scope, if one is open, before
* any punctuation.
*/
if (scope)
rew_elem(mdoc, tok);
scope = 0;
if (tok == MDOC_Fn)
mayopen = 0;
} else if (mayopen && !scope) {
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
scope = 1;
cnt++;
}
dword(mdoc, line, la, p, d,
MDOC_JOIN & mdoc_macros[tok].flags);
/*
* If the first argument is a closing delimiter,
* do not suppress spacing before it.
*/
if (firstarg && d == DELIM_CLOSE && !nc)
mdoc->last->flags &= ~MDOC_DELIMC;
firstarg = 0;
/*
* `Fl' macros have their scope re-opened with each new
* word so that the `-' can be added to each one without
* having to parse out spaces.
*/
if (scope && tok == MDOC_Fl) {
rew_elem(mdoc, tok);
scope = 0;
}
}
if (scope)
rew_elem(mdoc, tok);
/*
* If no elements have been collected and we're allowed to have
* empties (nc), open a scope and close it out. Otherwise,
* raise a warning.
*/
if ( ! cnt) {
if (nc) {
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
rew_last(mdoc, mdoc->last);
} else {
mdoc_argv_free(arg);
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
line, ppos, mdoc_macronames[tok]);
}
}
if (nl)
append_delims(mdoc, line, pos, buf);
}
static void
blk_full(MACRO_PROT_ARGS)
{
int la, nl, parsed;
struct mdoc_arg *arg;
struct mdoc_node *head; /* save of head macro */
struct mdoc_node *body; /* save of body macro */
struct mdoc_node *n;
enum margserr ac, lac;
char *p;
nl = MDOC_NEWLINE & mdoc->flags;
/* Skip items outside lists. */
if (tok == MDOC_It) {
for (n = mdoc->last; n; n = n->parent)
if (n->tok == MDOC_Bl &&
! (n->flags & MDOC_VALID))
break;
if (n == NULL) {
mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse,
line, ppos, "It %s", buf + *pos);
mdoc_elem_alloc(mdoc, line, ppos, MDOC_br, NULL);
rew_elem(mdoc, MDOC_br);
return;
}
}
/* Close out prior implicit scope. */
if ( ! (mdoc_macros[tok].flags & MDOC_EXPLICIT)) {
rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
}
/*
* This routine accommodates implicitly- and explicitly-scoped
* macro openings. Implicit ones first close out prior scope
* (seen above). Delay opening the head until necessary to
* allow leading punctuation to print. Special consideration
* for `It -column', which has phrase-part syntax instead of
* regular child nodes.
*/
mdoc_argv(mdoc, line, tok, &arg, pos, buf);
mdoc_block_alloc(mdoc, line, ppos, tok, arg);
head = body = NULL;
/*
* Exception: Heads of `It' macros in `-diag' lists are not
* parsed, even though `It' macros in general are parsed.
*/
parsed = tok != MDOC_It ||
mdoc->last->parent->tok != MDOC_Bl ||
mdoc->last->parent->norm->Bl.type != LIST_diag;
/*
* The `Nd' macro has all arguments in its body: it's a hybrid
* of block partial-explicit and full-implicit. Stupid.
*/
if (tok == MDOC_Nd) {
head = mdoc_head_alloc(mdoc, line, ppos, tok);
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
body = mdoc_body_alloc(mdoc, line, ppos, tok);
}
if (tok == MDOC_Bk)
mdoc->flags |= MDOC_KEEP;
ac = ARGS_PEND;
for (;;) {
la = *pos;
lac = ac;
ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
if (ac == ARGS_PUNCT)
break;
if (ac == ARGS_EOLN) {
if (lac != ARGS_PPHRASE && lac != ARGS_PHRASE)
break;
/*
* This is necessary: if the last token on a
* line is a `Ta' or tab, then we'll get
* ARGS_EOLN, so we must be smart enough to
* reopen our scope if the last parse was a
* phrase or partial phrase.
*/
rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
body = mdoc_body_alloc(mdoc, line, ppos, tok);
break;
}
/*
* Emit leading punctuation (i.e., punctuation before
* the MDOC_HEAD) for non-phrase types.
*/
if (head == NULL &&
ac != ARGS_PEND &&
ac != ARGS_PHRASE &&
ac != ARGS_PPHRASE &&
ac != ARGS_QWORD &&
mdoc_isdelim(p) == DELIM_OPEN) {
dword(mdoc, line, la, p, DELIM_OPEN, 0);
continue;
}
/* Open a head if one hasn't been opened. */
if (head == NULL)
head = mdoc_head_alloc(mdoc, line, ppos, tok);
if (ac == ARGS_PHRASE ||
ac == ARGS_PEND ||
ac == ARGS_PPHRASE) {
/*
* If we haven't opened a body yet, rewind the
* head; if we have, rewind that instead.
*/
rew_sub(body ? MDOC_BODY : MDOC_HEAD,
mdoc, tok, line, ppos);
body = mdoc_body_alloc(mdoc, line, ppos, tok);
/*
* Process phrases: set whether we're in a
* partial-phrase (this effects line handling)
* then call down into the phrase parser.
*/
if (ac == ARGS_PPHRASE)
mdoc->flags |= MDOC_PPHRASE;
if (ac == ARGS_PEND && lac == ARGS_PPHRASE)
mdoc->flags |= MDOC_PPHRASE;
parse_rest(mdoc, MDOC_MAX, line, &la, buf);
mdoc->flags &= ~MDOC_PPHRASE;
continue;
}
if (macro_or_word(mdoc, tok, line, la, pos, buf, parsed))
break;
}
if (head == NULL)
head = mdoc_head_alloc(mdoc, line, ppos, tok);
if (nl)
append_delims(mdoc, line, pos, buf);
if (body != NULL)
goto out;
/*
* If there is an open (i.e., unvalidated) sub-block requiring
* explicit close-out, postpone switching the current block from
* head to body until the rew_sub() call closing out that
* sub-block.
*/
for (n = mdoc->last; n && n != head; n = n->parent) {
if (n->type == MDOC_BLOCK &&
mdoc_macros[n->tok].flags & MDOC_EXPLICIT &&
! (n->flags & MDOC_VALID)) {
n->pending = head;
return;
}
}
/* Close out scopes to remain in a consistent state. */
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
mdoc_body_alloc(mdoc, line, ppos, tok);
out:
if (mdoc->flags & MDOC_FREECOL) {
rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
mdoc->flags &= ~MDOC_FREECOL;
}
}
static void
blk_part_imp(MACRO_PROT_ARGS)
{
int la, nl;
enum margserr ac;
char *p;
struct mdoc_node *blk; /* saved block context */
struct mdoc_node *body; /* saved body context */
struct mdoc_node *n;
nl = MDOC_NEWLINE & mdoc->flags;
/*
* A macro that spans to the end of the line. This is generally
* (but not necessarily) called as the first macro. The block
* has a head as the immediate child, which is always empty,
* followed by zero or more opening punctuation nodes, then the
* body (which may be empty, depending on the macro), then zero
* or more closing punctuation nodes.
*/
blk = mdoc_block_alloc(mdoc, line, ppos, tok, NULL);
mdoc_head_alloc(mdoc, line, ppos, tok);
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
/*
* Open the body scope "on-demand", that is, after we've
* processed all our the leading delimiters (open parenthesis,
* etc.).
*/
for (body = NULL; ; ) {
la = *pos;
ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
if (ac == ARGS_EOLN || ac == ARGS_PUNCT)
break;
if (body == NULL && ac != ARGS_QWORD &&
mdoc_isdelim(p) == DELIM_OPEN) {
dword(mdoc, line, la, p, DELIM_OPEN, 0);
continue;
}
if (body == NULL)
body = mdoc_body_alloc(mdoc, line, ppos, tok);
if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
break;
}
if (body == NULL)
body = mdoc_body_alloc(mdoc, line, ppos, tok);
/*
* If there is an open sub-block requiring explicit close-out,
* postpone closing out the current block
* until the rew_sub() call closing out the sub-block.
*/
for (n = mdoc->last; n && n != body && n != blk->parent;
n = n->parent) {
if (n->type == MDOC_BLOCK &&
mdoc_macros[n->tok].flags & MDOC_EXPLICIT &&
! (n->flags & MDOC_VALID)) {
make_pending(n, tok, mdoc, line, ppos);
mdoc_endbody_alloc(mdoc, line, ppos,
tok, body, ENDBODY_NOSPACE);
return;
}
}
assert(n == body);
rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
if (nl)
append_delims(mdoc, line, pos, buf);
rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
/* Move trailing .Ns out of scope. */
for (n = body->child; n && n->next; n = n->next)
/* Do nothing. */ ;
if (n && n->tok == MDOC_Ns)
mdoc_node_relink(mdoc, n);
}
static void
blk_part_exp(MACRO_PROT_ARGS)
{
int la, nl;
enum margserr ac;
struct mdoc_node *head; /* keep track of head */
struct mdoc_node *body; /* keep track of body */
char *p;
nl = MDOC_NEWLINE & mdoc->flags;
/*
* The opening of an explicit macro having zero or more leading
* punctuation nodes; a head with optional single element (the
* case of `Eo'); and a body that may be empty.
*/
mdoc_block_alloc(mdoc, line, ppos, tok, NULL);
for (head = body = NULL; ; ) {
la = *pos;
ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
if (ac == ARGS_PUNCT || ac == ARGS_EOLN)
break;
/* Flush out leading punctuation. */
if (head == NULL && ac != ARGS_QWORD &&
mdoc_isdelim(p) == DELIM_OPEN) {
assert(NULL == body);
dword(mdoc, line, la, p, DELIM_OPEN, 0);
continue;
}
if (head == NULL) {
assert(body == NULL);
head = mdoc_head_alloc(mdoc, line, ppos, tok);
}
/*
* `Eo' gobbles any data into the head, but most other
* macros just immediately close out and begin the body.
*/
if (body == NULL) {
assert(head);
/* No check whether it's a macro! */
if (tok == MDOC_Eo)
dword(mdoc, line, la, p, DELIM_MAX, 0);
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
body = mdoc_body_alloc(mdoc, line, ppos, tok);
if (tok == MDOC_Eo)
continue;
}
assert(head != NULL && body != NULL);
if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
break;
}
/* Clean-up to leave in a consistent state. */
if (head == NULL)
mdoc_head_alloc(mdoc, line, ppos, tok);
if (body == NULL) {
rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
mdoc_body_alloc(mdoc, line, ppos, tok);
}
if (nl)
append_delims(mdoc, line, pos, buf);
}
static void
in_line_argn(MACRO_PROT_ARGS)
{
int la, flushed, j, maxargs, nl;
enum margserr ac;
struct mdoc_arg *arg;
char *p;
enum mdoct ntok;
- nl = MDOC_NEWLINE & mdoc->flags;
+ nl = mdoc->flags & MDOC_NEWLINE;
/*
* A line macro that has a fixed number of arguments (maxargs).
* Only open the scope once the first non-leading-punctuation is
* found (unless MDOC_IGNDELIM is noted, like in `Pf'), then
* keep it open until the maximum number of arguments are
* exhausted.
*/
switch (tok) {
case MDOC_Ap:
/* FALLTHROUGH */
case MDOC_Ns:
/* FALLTHROUGH */
case MDOC_Ux:
maxargs = 0;
break;
case MDOC_Bx:
/* FALLTHROUGH */
case MDOC_Es:
/* FALLTHROUGH */
case MDOC_Xr:
maxargs = 2;
break;
default:
maxargs = 1;
break;
}
mdoc_argv(mdoc, line, tok, &arg, pos, buf);
- for (flushed = j = 0; ; ) {
+ p = NULL;
+ flushed = j = 0;
+ for (;;) {
la = *pos;
ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
- if (ac == ARGS_PUNCT || ac == ARGS_EOLN)
+ if (ac == ARGS_PUNCT || ac == ARGS_EOLN) {
+ if (j < 2 && tok == MDOC_Pf)
+ mandoc_vmsg(MANDOCERR_PF_SKIP,
+ mdoc->parse, line, ppos, "Pf %s",
+ p == NULL ? "at eol" : p);
break;
+ }
if ( ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) &&
ac != ARGS_QWORD && j == 0 &&
mdoc_isdelim(p) == DELIM_OPEN) {
dword(mdoc, line, la, p, DELIM_OPEN, 0);
continue;
} else if (j == 0)
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
if (j == maxargs && ! flushed) {
rew_elem(mdoc, tok);
flushed = 1;
}
- ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p);
+ ntok = (ac == ARGS_QWORD || (tok == MDOC_Pf && j == 0)) ?
+ MDOC_MAX : lookup(mdoc, tok, line, la, p);
if (ntok != MDOC_MAX) {
if ( ! flushed)
rew_elem(mdoc, tok);
flushed = 1;
mdoc_macro(mdoc, ntok, line, la, pos, buf);
j++;
break;
}
if ( ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) &&
ac != ARGS_QWORD && ! flushed &&
mdoc_isdelim(p) != DELIM_NONE) {
rew_elem(mdoc, tok);
flushed = 1;
}
dword(mdoc, line, la, p, DELIM_MAX,
MDOC_JOIN & mdoc_macros[tok].flags);
j++;
}
- if (j == 0)
+ if (j == 0) {
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
+ if (ac == ARGS_PUNCT && tok == MDOC_Pf)
+ append_delims(mdoc, line, pos, buf);
+ }
if ( ! flushed)
rew_elem(mdoc, tok);
if (nl)
append_delims(mdoc, line, pos, buf);
}
static void
in_line_eoln(MACRO_PROT_ARGS)
{
struct mdoc_arg *arg;
if (tok == MDOC_Pp)
rew_sub(MDOC_BLOCK, mdoc, MDOC_Nm, line, ppos);
mdoc_argv(mdoc, line, tok, &arg, pos, buf);
mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
if (parse_rest(mdoc, tok, line, pos, buf))
return;
rew_elem(mdoc, tok);
}
/*
* The simplest argument parser available: Parse the remaining
* words until the end of the phrase or line and return 0
* or until the next macro, call that macro, and return 1.
*/
static int
parse_rest(struct mdoc *mdoc, enum mdoct tok, int line, int *pos, char *buf)
{
int la;
for (;;) {
la = *pos;
if (mdoc_args(mdoc, line, pos, buf, tok, NULL) == ARGS_EOLN)
return(0);
if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
return(1);
}
}
static void
ctx_synopsis(MACRO_PROT_ARGS)
{
if (~mdoc->flags & (MDOC_SYNOPSIS | MDOC_NEWLINE))
in_line(mdoc, tok, line, ppos, pos, buf);
else if (tok == MDOC_Nm)
blk_full(mdoc, tok, line, ppos, pos, buf);
else {
assert(tok == MDOC_Vt);
blk_part_imp(mdoc, tok, line, ppos, pos, buf);
}
}
/*
* Phrases occur within `Bl -column' entries, separated by `Ta' or tabs.
* They're unusual because they're basically free-form text until a
* macro is encountered.
*/
static void
phrase_ta(MACRO_PROT_ARGS)
{
struct mdoc_node *n;
/* Make sure we are in a column list or ignore this macro. */
n = mdoc->last;
while (n != NULL && n->tok != MDOC_Bl)
n = n->parent;
if (n == NULL || n->norm->Bl.type != LIST_column) {
mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse,
line, ppos, "Ta");
return;
}
/* Advance to the next column. */
rew_sub(MDOC_BODY, mdoc, MDOC_It, line, ppos);
mdoc_body_alloc(mdoc, line, ppos, MDOC_It);
parse_rest(mdoc, MDOC_MAX, line, pos, buf);
}
Index: vendor/mdocml/dist/mdoc_man.c
===================================================================
--- vendor/mdocml/dist/mdoc_man.c (revision 276215)
+++ vendor/mdocml/dist/mdoc_man.c (revision 276216)
@@ -1,1790 +1,1791 @@
-/* $Id: mdoc_man.c,v 1.76 2014/11/27 22:27:56 schwarze Exp $ */
+/* $Id: mdoc_man.c,v 1.77 2014/11/30 05:29:00 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include "mandoc.h"
#include "mandoc_aux.h"
#include "out.h"
#include "man.h"
#include "mdoc.h"
#include "main.h"
#define DECL_ARGS const struct mdoc_meta *meta, \
const struct mdoc_node *n
struct manact {
int (*cond)(DECL_ARGS); /* DON'T run actions */
int (*pre)(DECL_ARGS); /* pre-node action */
void (*post)(DECL_ARGS); /* post-node action */
const char *prefix; /* pre-node string constant */
const char *suffix; /* post-node string constant */
};
static int cond_body(DECL_ARGS);
static int cond_head(DECL_ARGS);
static void font_push(char);
static void font_pop(void);
static void mid_it(void);
static void post__t(DECL_ARGS);
static void post_aq(DECL_ARGS);
static void post_bd(DECL_ARGS);
static void post_bf(DECL_ARGS);
static void post_bk(DECL_ARGS);
static void post_bl(DECL_ARGS);
static void post_dl(DECL_ARGS);
static void post_en(DECL_ARGS);
static void post_enc(DECL_ARGS);
static void post_eo(DECL_ARGS);
static void post_fa(DECL_ARGS);
static void post_fd(DECL_ARGS);
static void post_fl(DECL_ARGS);
static void post_fn(DECL_ARGS);
static void post_fo(DECL_ARGS);
static void post_font(DECL_ARGS);
static void post_in(DECL_ARGS);
static void post_it(DECL_ARGS);
static void post_lb(DECL_ARGS);
static void post_nm(DECL_ARGS);
static void post_percent(DECL_ARGS);
static void post_pf(DECL_ARGS);
static void post_sect(DECL_ARGS);
static void post_sp(DECL_ARGS);
static void post_vt(DECL_ARGS);
static int pre__t(DECL_ARGS);
static int pre_an(DECL_ARGS);
static int pre_ap(DECL_ARGS);
static int pre_aq(DECL_ARGS);
static int pre_bd(DECL_ARGS);
static int pre_bf(DECL_ARGS);
static int pre_bk(DECL_ARGS);
static int pre_bl(DECL_ARGS);
static int pre_br(DECL_ARGS);
static int pre_bx(DECL_ARGS);
static int pre_dl(DECL_ARGS);
static int pre_en(DECL_ARGS);
static int pre_enc(DECL_ARGS);
static int pre_em(DECL_ARGS);
static int pre_skip(DECL_ARGS);
static int pre_eo(DECL_ARGS);
static int pre_ex(DECL_ARGS);
static int pre_fa(DECL_ARGS);
static int pre_fd(DECL_ARGS);
static int pre_fl(DECL_ARGS);
static int pre_fn(DECL_ARGS);
static int pre_fo(DECL_ARGS);
static int pre_ft(DECL_ARGS);
static int pre_in(DECL_ARGS);
static int pre_it(DECL_ARGS);
static int pre_lk(DECL_ARGS);
static int pre_li(DECL_ARGS);
static int pre_ll(DECL_ARGS);
static int pre_nm(DECL_ARGS);
static int pre_no(DECL_ARGS);
static int pre_ns(DECL_ARGS);
static int pre_pp(DECL_ARGS);
static int pre_rs(DECL_ARGS);
static int pre_rv(DECL_ARGS);
static int pre_sm(DECL_ARGS);
static int pre_sp(DECL_ARGS);
static int pre_sect(DECL_ARGS);
static int pre_sy(DECL_ARGS);
static void pre_syn(const struct mdoc_node *);
static int pre_vt(DECL_ARGS);
static int pre_ux(DECL_ARGS);
static int pre_xr(DECL_ARGS);
static void print_word(const char *);
static void print_line(const char *, int);
static void print_block(const char *, int);
static void print_offs(const char *, int);
static void print_width(const char *,
const struct mdoc_node *, size_t);
static void print_count(int *);
static void print_node(DECL_ARGS);
static const struct manact manacts[MDOC_MAX + 1] = {
{ NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
{ NULL, NULL, NULL, NULL, NULL }, /* Os */
{ NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
{ NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
{ NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
{ cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
{ cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
{ cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
{ cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
{ NULL, NULL, NULL, NULL, NULL }, /* El */
{ NULL, pre_it, post_it, NULL, NULL }, /* It */
{ NULL, pre_em, post_font, NULL, NULL }, /* Ad */
{ NULL, pre_an, NULL, NULL, NULL }, /* An */
{ NULL, pre_em, post_font, NULL, NULL }, /* Ar */
{ NULL, pre_sy, post_font, NULL, NULL }, /* Cd */
{ NULL, pre_sy, post_font, NULL, NULL }, /* Cm */
{ NULL, pre_li, post_font, NULL, NULL }, /* Dv */
{ NULL, pre_li, post_font, NULL, NULL }, /* Er */
{ NULL, pre_li, post_font, NULL, NULL }, /* Ev */
{ NULL, pre_ex, NULL, NULL, NULL }, /* Ex */
{ NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
{ NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
{ NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
{ NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */
{ NULL, pre_ft, post_font, NULL, NULL }, /* Ft */
{ NULL, pre_sy, post_font, NULL, NULL }, /* Ic */
{ NULL, pre_in, post_in, NULL, NULL }, /* In */
{ NULL, pre_li, post_font, NULL, NULL }, /* Li */
{ cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
{ NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
{ NULL, pre_ft, post_font, NULL, NULL }, /* Ot */
{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
{ NULL, pre_rv, NULL, NULL, NULL }, /* Rv */
{ NULL, NULL, NULL, NULL, NULL }, /* St */
{ NULL, pre_em, post_font, NULL, NULL }, /* Va */
{ NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
{ NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
{ NULL, NULL, post_percent, NULL, NULL }, /* %A */
{ NULL, pre_em, post_percent, NULL, NULL }, /* %B */
{ NULL, NULL, post_percent, NULL, NULL }, /* %D */
{ NULL, pre_em, post_percent, NULL, NULL }, /* %I */
{ NULL, pre_em, post_percent, NULL, NULL }, /* %J */
{ NULL, NULL, post_percent, NULL, NULL }, /* %N */
{ NULL, NULL, post_percent, NULL, NULL }, /* %O */
{ NULL, NULL, post_percent, NULL, NULL }, /* %P */
{ NULL, NULL, post_percent, NULL, NULL }, /* %R */
{ NULL, pre__t, post__t, NULL, NULL }, /* %T */
{ NULL, NULL, post_percent, NULL, NULL }, /* %V */
{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Ao */
{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Aq */
{ NULL, NULL, NULL, NULL, NULL }, /* At */
{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
{ NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
{ NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
{ NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
{ NULL, pre_skip, NULL, NULL, NULL }, /* Db */
{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
{ cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Do */
{ cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Dq */
{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
{ NULL, pre_em, post_font, NULL, NULL }, /* Em */
{ cond_body, pre_eo, post_eo, NULL, NULL }, /* Eo */
{ NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
{ NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
{ NULL, pre_no, NULL, NULL, NULL }, /* No */
{ NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
{ NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */
{ NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */
{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
{ NULL, NULL, post_pf, NULL, NULL }, /* Pf */
{ cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
{ cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Ql */
{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
{ NULL, NULL, NULL, NULL, NULL }, /* Re */
{ cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* So */
{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Sq */
{ NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
{ NULL, pre_em, post_font, NULL, NULL }, /* Sx */
{ NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
{ NULL, pre_li, post_font, NULL, NULL }, /* Tn */
{ NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
{ NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
{ NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
{ NULL, pre_em, post_font, NULL, NULL }, /* Fr */
{ NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
{ NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
{ NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
{ NULL, pre_em, post_font, NULL, NULL }, /* Mt */
{ cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
{ cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
{ NULL, NULL, post_percent, NULL, NULL }, /* %C */
{ NULL, pre_skip, NULL, NULL, NULL }, /* Es */
{ cond_body, pre_en, post_en, NULL, NULL }, /* En */
{ NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
{ NULL, pre_br, NULL, NULL, NULL }, /* br */
{ NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
{ NULL, pre_ll, post_sp, NULL, NULL }, /* ll */
{ NULL, NULL, NULL, NULL, NULL }, /* ROOT */
};
static int outflags;
#define MMAN_spc (1 << 0) /* blank character before next word */
#define MMAN_spc_force (1 << 1) /* even before trailing punctuation */
#define MMAN_nl (1 << 2) /* break man(7) code line */
#define MMAN_br (1 << 3) /* break output line */
#define MMAN_sp (1 << 4) /* insert a blank output line */
#define MMAN_PP (1 << 5) /* reset indentation etc. */
#define MMAN_Sm (1 << 6) /* horizontal spacing mode */
#define MMAN_Bk (1 << 7) /* word keep mode */
#define MMAN_Bk_susp (1 << 8) /* suspend this (after a macro) */
#define MMAN_An_split (1 << 9) /* author mode is "split" */
#define MMAN_An_nosplit (1 << 10) /* author mode is "nosplit" */
#define MMAN_PD (1 << 11) /* inter-paragraph spacing disabled */
#define MMAN_nbrword (1 << 12) /* do not break the next word */
#define BL_STACK_MAX 32
static size_t Bl_stack[BL_STACK_MAX]; /* offsets [chars] */
static int Bl_stack_post[BL_STACK_MAX]; /* add final .RE */
static int Bl_stack_len; /* number of nested Bl blocks */
static int TPremain; /* characters before tag is full */
static struct {
char *head;
char *tail;
size_t size;
} fontqueue;
static void
font_push(char newfont)
{
if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
fontqueue.size += 8;
fontqueue.head = mandoc_realloc(fontqueue.head,
fontqueue.size);
}
*fontqueue.tail = newfont;
print_word("");
printf("\\f");
putchar(newfont);
outflags &= ~MMAN_spc;
}
static void
font_pop(void)
{
if (fontqueue.tail > fontqueue.head)
fontqueue.tail--;
outflags &= ~MMAN_spc;
print_word("");
printf("\\f");
putchar(*fontqueue.tail);
}
static void
print_word(const char *s)
{
if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
/*
* If we need a newline, print it now and start afresh.
*/
if (MMAN_PP & outflags) {
if (MMAN_sp & outflags) {
if (MMAN_PD & outflags) {
printf("\n.PD");
outflags &= ~MMAN_PD;
}
} else if ( ! (MMAN_PD & outflags)) {
printf("\n.PD 0");
outflags |= MMAN_PD;
}
printf("\n.PP\n");
} else if (MMAN_sp & outflags)
printf("\n.sp\n");
else if (MMAN_br & outflags)
printf("\n.br\n");
else if (MMAN_nl & outflags)
putchar('\n');
outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
if (1 == TPremain)
printf(".br\n");
TPremain = 0;
} else if (MMAN_spc & outflags) {
/*
* If we need a space, only print it if
* (1) it is forced by `No' or
* (2) what follows is not terminating punctuation or
* (3) what follows is longer than one character.
*/
if (MMAN_spc_force & outflags || '\0' == s[0] ||
NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
if (MMAN_Bk & outflags &&
! (MMAN_Bk_susp & outflags))
putchar('\\');
putchar(' ');
if (TPremain)
TPremain--;
}
}
/*
* Reassign needing space if we're not following opening
* punctuation.
*/
if (MMAN_Sm & outflags && ('\0' == s[0] ||
(('(' != s[0] && '[' != s[0]) || '\0' != s[1])))
outflags |= MMAN_spc;
else
outflags &= ~MMAN_spc;
outflags &= ~(MMAN_spc_force | MMAN_Bk_susp);
for ( ; *s; s++) {
switch (*s) {
case ASCII_NBRSP:
printf("\\ ");
break;
case ASCII_HYPH:
putchar('-');
break;
case ASCII_BREAK:
printf("\\:");
break;
case ' ':
if (MMAN_nbrword & outflags) {
printf("\\ ");
break;
}
/* FALLTHROUGH */
default:
putchar((unsigned char)*s);
break;
}
if (TPremain)
TPremain--;
}
outflags &= ~MMAN_nbrword;
}
static void
print_line(const char *s, int newflags)
{
outflags &= ~MMAN_br;
outflags |= MMAN_nl;
print_word(s);
outflags |= newflags;
}
static void
print_block(const char *s, int newflags)
{
outflags &= ~MMAN_PP;
if (MMAN_sp & outflags) {
outflags &= ~(MMAN_sp | MMAN_br);
if (MMAN_PD & outflags) {
print_line(".PD", 0);
outflags &= ~MMAN_PD;
}
} else if (! (MMAN_PD & outflags))
print_line(".PD 0", MMAN_PD);
outflags |= MMAN_nl;
print_word(s);
outflags |= MMAN_Bk_susp | newflags;
}
static void
print_offs(const char *v, int keywords)
{
char buf[24];
struct roffsu su;
size_t sz;
print_line(".RS", MMAN_Bk_susp);
/* Convert v into a number (of characters). */
if (NULL == v || '\0' == *v || (keywords && !strcmp(v, "left")))
sz = 0;
else if (keywords && !strcmp(v, "indent"))
sz = 6;
else if (keywords && !strcmp(v, "indent-two"))
sz = 12;
else if (a2roffsu(v, &su, SCALE_MAX)) {
if (SCALE_EN == su.unit)
sz = su.scale;
else {
/*
* XXX
* If we are inside an enclosing list,
* there is no easy way to add the two
* indentations because they are provided
* in terms of different units.
*/
print_word(v);
outflags |= MMAN_nl;
return;
}
} else
sz = strlen(v);
/*
* We are inside an enclosing list.
* Add the two indentations.
*/
if (Bl_stack_len)
sz += Bl_stack[Bl_stack_len - 1];
(void)snprintf(buf, sizeof(buf), "%zun", sz);
print_word(buf);
outflags |= MMAN_nl;
}
/*
* Set up the indentation for a list item; used from pre_it().
*/
static void
print_width(const char *v, const struct mdoc_node *child, size_t defsz)
{
char buf[24];
struct roffsu su;
size_t sz, chsz;
int numeric, remain;
numeric = 1;
remain = 0;
/* Convert v into a number (of characters). */
if (NULL == v)
sz = defsz;
else if (a2roffsu(v, &su, SCALE_MAX)) {
if (SCALE_EN == su.unit)
sz = su.scale;
else {
sz = 0;
numeric = 0;
}
} else
sz = strlen(v);
/* XXX Rough estimation, might have multiple parts. */
chsz = (NULL != child && MDOC_TEXT == child->type) ?
strlen(child->string) : 0;
/* Maybe we are inside an enclosing list? */
mid_it();
/*
* Save our own indentation,
* such that child lists can use it.
*/
Bl_stack[Bl_stack_len++] = sz + 2;
/* Set up the current list. */
if (defsz && chsz > sz)
print_block(".HP", 0);
else {
print_block(".TP", 0);
remain = sz + 2;
}
if (numeric) {
(void)snprintf(buf, sizeof(buf), "%zun", sz + 2);
print_word(buf);
} else
print_word(v);
TPremain = remain;
}
static void
print_count(int *count)
{
char buf[24];
(void)snprintf(buf, sizeof(buf), "%d.", ++*count);
print_word(buf);
}
void
man_man(void *arg, const struct man *man)
{
/*
* Dump the keep buffer.
* We're guaranteed by now that this exists (is non-NULL).
* Flush stdout afterward, just in case.
*/
fputs(mparse_getkeep(man_mparse(man)), stdout);
fflush(stdout);
}
void
man_mdoc(void *arg, const struct mdoc *mdoc)
{
const struct mdoc_meta *meta;
const struct mdoc_node *n;
meta = mdoc_meta(mdoc);
n = mdoc_node(mdoc);
printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
meta->title,
(meta->msec == NULL ? "" : meta->msec),
meta->date, meta->os, meta->vol);
/* Disable hyphenation and if nroff, disable justification. */
printf(".nh\n.if n .ad l");
outflags = MMAN_nl | MMAN_Sm;
if (0 == fontqueue.size) {
fontqueue.size = 8;
fontqueue.head = fontqueue.tail = mandoc_malloc(8);
*fontqueue.tail = 'R';
}
print_node(meta, n);
putchar('\n');
}
static void
print_node(DECL_ARGS)
{
const struct mdoc_node *sub;
const struct manact *act;
int cond, do_sub;
/*
* Break the line if we were parsed subsequent the current node.
* This makes the page structure be more consistent.
*/
if (MMAN_spc & outflags && MDOC_LINE & n->flags)
outflags |= MMAN_nl;
act = NULL;
cond = 0;
do_sub = 1;
if (MDOC_TEXT == n->type) {
/*
* Make sure that we don't happen to start with a
* control character at the start of a line.
*/
if (MMAN_nl & outflags &&
('.' == *n->string || '\'' == *n->string)) {
print_word("");
printf("\\&");
outflags &= ~MMAN_spc;
}
if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMC))
outflags |= MMAN_spc_force;
print_word(n->string);
if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMO))
outflags |= MMAN_spc;
} else {
/*
* Conditionally run the pre-node action handler for a
* node.
*/
act = manacts + n->tok;
cond = act->cond == NULL || (*act->cond)(meta, n);
if (cond && act->pre && (n->end == ENDBODY_NOT || n->nchild))
do_sub = (*act->pre)(meta, n);
}
/*
* Conditionally run all child nodes.
* Note that this iterates over children instead of using
* recursion. This prevents unnecessary depth in the stack.
*/
if (do_sub)
for (sub = n->child; sub; sub = sub->next)
print_node(meta, sub);
/*
* Lastly, conditionally run the post-node handler.
*/
if (MDOC_ENDED & n->flags)
return;
if (cond && act->post)
(*act->post)(meta, n);
if (ENDBODY_NOT != n->end)
n->pending->flags |= MDOC_ENDED;
if (ENDBODY_NOSPACE == n->end)
outflags &= ~(MMAN_spc | MMAN_nl);
}
static int
cond_head(DECL_ARGS)
{
return(MDOC_HEAD == n->type);
}
static int
cond_body(DECL_ARGS)
{
return(MDOC_BODY == n->type);
}
static int
pre_enc(DECL_ARGS)
{
const char *prefix;
prefix = manacts[n->tok].prefix;
if (NULL == prefix)
return(1);
print_word(prefix);
outflags &= ~MMAN_spc;
return(1);
}
static void
post_enc(DECL_ARGS)
{
const char *suffix;
suffix = manacts[n->tok].suffix;
if (NULL == suffix)
return;
outflags &= ~(MMAN_spc | MMAN_nl);
print_word(suffix);
}
static int
pre_ex(DECL_ARGS)
{
int nchild;
outflags |= MMAN_br | MMAN_nl;
print_word("The");
nchild = n->nchild;
for (n = n->child; n; n = n->next) {
font_push('B');
print_word(n->string);
font_pop();
if (n->next == NULL)
continue;
if (nchild > 2) {
outflags &= ~MMAN_spc;
print_word(",");
}
if (n->next->next == NULL)
print_word("and");
}
if (nchild > 1)
print_word("utilities exit\\~0");
else
print_word("utility exits\\~0");
print_word("on success, and\\~>0 if an error occurs.");
outflags |= MMAN_nl;
return(0);
}
static void
post_font(DECL_ARGS)
{
font_pop();
}
static void
post_percent(DECL_ARGS)
{
if (pre_em == manacts[n->tok].pre)
font_pop();
if (n->next) {
print_word(",");
if (n->prev && n->prev->tok == n->tok &&
n->next->tok == n->tok)
print_word("and");
} else {
print_word(".");
outflags |= MMAN_nl;
}
}
static int
pre__t(DECL_ARGS)
{
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T) {
print_word("");
putchar('\"');
outflags &= ~MMAN_spc;
} else
font_push('I');
return(1);
}
static void
post__t(DECL_ARGS)
{
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T) {
outflags &= ~MMAN_spc;
print_word("");
putchar('\"');
} else
font_pop();
post_percent(meta, n);
}
/*
* Print before a section header.
*/
static int
pre_sect(DECL_ARGS)
{
if (MDOC_HEAD == n->type) {
outflags |= MMAN_sp;
print_block(manacts[n->tok].prefix, 0);
print_word("");
putchar('\"');
outflags &= ~MMAN_spc;
}
return(1);
}
/*
* Print subsequent a section header.
*/
static void
post_sect(DECL_ARGS)
{
if (MDOC_HEAD != n->type)
return;
outflags &= ~MMAN_spc;
print_word("");
putchar('\"');
outflags |= MMAN_nl;
if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
}
/* See mdoc_term.c, synopsis_pre() for comments. */
static void
pre_syn(const struct mdoc_node *n)
{
if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
return;
if (n->prev->tok == n->tok &&
MDOC_Ft != n->tok &&
MDOC_Fo != n->tok &&
MDOC_Fn != n->tok) {
outflags |= MMAN_br;
return;
}
switch (n->prev->tok) {
case MDOC_Fd:
/* FALLTHROUGH */
case MDOC_Fn:
/* FALLTHROUGH */
case MDOC_Fo:
/* FALLTHROUGH */
case MDOC_In:
/* FALLTHROUGH */
case MDOC_Vt:
outflags |= MMAN_sp;
break;
case MDOC_Ft:
if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
outflags |= MMAN_sp;
break;
}
/* FALLTHROUGH */
default:
outflags |= MMAN_br;
break;
}
}
static int
pre_an(DECL_ARGS)
{
switch (n->norm->An.auth) {
case AUTH_split:
outflags &= ~MMAN_An_nosplit;
outflags |= MMAN_An_split;
return(0);
case AUTH_nosplit:
outflags &= ~MMAN_An_split;
outflags |= MMAN_An_nosplit;
return(0);
default:
if (MMAN_An_split & outflags)
outflags |= MMAN_br;
else if (SEC_AUTHORS == n->sec &&
! (MMAN_An_nosplit & outflags))
outflags |= MMAN_An_split;
return(1);
}
}
static int
pre_ap(DECL_ARGS)
{
outflags &= ~MMAN_spc;
print_word("'");
outflags &= ~MMAN_spc;
return(0);
}
static int
pre_aq(DECL_ARGS)
{
print_word(n->parent->prev != NULL &&
n->parent->prev->tok == MDOC_An ? "<" : "\\(la");
outflags &= ~MMAN_spc;
return(1);
}
static void
post_aq(DECL_ARGS)
{
outflags &= ~(MMAN_spc | MMAN_nl);
print_word(n->parent->prev != NULL &&
n->parent->prev->tok == MDOC_An ? ">" : "\\(ra");
}
static int
pre_bd(DECL_ARGS)
{
outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
if (DISP_unfilled == n->norm->Bd.type ||
DISP_literal == n->norm->Bd.type)
print_line(".nf", 0);
if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
outflags |= MMAN_sp;
print_offs(n->norm->Bd.offs, 1);
return(1);
}
static void
post_bd(DECL_ARGS)
{
/* Close out this display. */
print_line(".RE", MMAN_nl);
if (DISP_unfilled == n->norm->Bd.type ||
DISP_literal == n->norm->Bd.type)
print_line(".fi", MMAN_nl);
/* Maybe we are inside an enclosing list? */
if (NULL != n->parent->next)
mid_it();
}
static int
pre_bf(DECL_ARGS)
{
switch (n->type) {
case MDOC_BLOCK:
return(1);
case MDOC_BODY:
break;
default:
return(0);
}
switch (n->norm->Bf.font) {
case FONT_Em:
font_push('I');
break;
case FONT_Sy:
font_push('B');
break;
default:
font_push('R');
break;
}
return(1);
}
static void
post_bf(DECL_ARGS)
{
if (MDOC_BODY == n->type)
font_pop();
}
static int
pre_bk(DECL_ARGS)
{
switch (n->type) {
case MDOC_BLOCK:
return(1);
case MDOC_BODY:
outflags |= MMAN_Bk;
return(1);
default:
return(0);
}
}
static void
post_bk(DECL_ARGS)
{
if (MDOC_BODY == n->type)
outflags &= ~MMAN_Bk;
}
static int
pre_bl(DECL_ARGS)
{
size_t icol;
/*
* print_offs() will increase the -offset to account for
* a possible enclosing .It, but any enclosed .It blocks
* just nest and do not add up their indentation.
*/
if (n->norm->Bl.offs) {
print_offs(n->norm->Bl.offs, 0);
Bl_stack[Bl_stack_len++] = 0;
}
switch (n->norm->Bl.type) {
case LIST_enum:
n->norm->Bl.count = 0;
return(1);
case LIST_column:
break;
default:
return(1);
}
print_line(".TS", MMAN_nl);
for (icol = 0; icol < n->norm->Bl.ncols; icol++)
print_word("l");
print_word(".");
outflags |= MMAN_nl;
return(1);
}
static void
post_bl(DECL_ARGS)
{
switch (n->norm->Bl.type) {
case LIST_column:
print_line(".TE", 0);
break;
case LIST_enum:
n->norm->Bl.count = 0;
break;
default:
break;
}
if (n->norm->Bl.offs) {
print_line(".RE", MMAN_nl);
assert(Bl_stack_len);
Bl_stack_len--;
assert(0 == Bl_stack[Bl_stack_len]);
} else {
outflags |= MMAN_PP | MMAN_nl;
outflags &= ~(MMAN_sp | MMAN_br);
}
/* Maybe we are inside an enclosing list? */
if (NULL != n->parent->next)
mid_it();
}
static int
pre_br(DECL_ARGS)
{
outflags |= MMAN_br;
return(0);
}
static int
pre_bx(DECL_ARGS)
{
n = n->child;
if (n) {
print_word(n->string);
outflags &= ~MMAN_spc;
n = n->next;
}
print_word("BSD");
if (NULL == n)
return(0);
outflags &= ~MMAN_spc;
print_word("-");
outflags &= ~MMAN_spc;
print_word(n->string);
return(0);
}
static int
pre_dl(DECL_ARGS)
{
print_offs("6n", 0);
return(1);
}
static void
post_dl(DECL_ARGS)
{
print_line(".RE", MMAN_nl);
/* Maybe we are inside an enclosing list? */
if (NULL != n->parent->next)
mid_it();
}
static int
pre_em(DECL_ARGS)
{
font_push('I');
return(1);
}
static int
pre_en(DECL_ARGS)
{
if (NULL == n->norm->Es ||
NULL == n->norm->Es->child)
return(1);
print_word(n->norm->Es->child->string);
outflags &= ~MMAN_spc;
return(1);
}
static void
post_en(DECL_ARGS)
{
if (NULL == n->norm->Es ||
NULL == n->norm->Es->child ||
NULL == n->norm->Es->child->next)
return;
outflags &= ~MMAN_spc;
print_word(n->norm->Es->child->next->string);
return;
}
static int
pre_eo(DECL_ARGS)
{
outflags &= ~(MMAN_spc | MMAN_nl);
return(1);
}
static void
post_eo(DECL_ARGS)
{
if (n->end != ENDBODY_SPACE)
outflags &= ~MMAN_spc;
}
static int
pre_fa(DECL_ARGS)
{
int am_Fa;
am_Fa = MDOC_Fa == n->tok;
if (am_Fa)
n = n->child;
while (NULL != n) {
font_push('I');
if (am_Fa || MDOC_SYNPRETTY & n->flags)
outflags |= MMAN_nbrword;
print_node(meta, n);
font_pop();
if (NULL != (n = n->next))
print_word(",");
}
return(0);
}
static void
post_fa(DECL_ARGS)
{
if (NULL != n->next && MDOC_Fa == n->next->tok)
print_word(",");
}
static int
pre_fd(DECL_ARGS)
{
pre_syn(n);
font_push('B');
return(1);
}
static void
post_fd(DECL_ARGS)
{
font_pop();
outflags |= MMAN_br;
}
static int
pre_fl(DECL_ARGS)
{
font_push('B');
print_word("\\-");
if (n->nchild)
outflags &= ~MMAN_spc;
return(1);
}
static void
post_fl(DECL_ARGS)
{
font_pop();
if ( ! (n->nchild ||
n->next == NULL ||
n->next->type == MDOC_TEXT ||
n->next->flags & MDOC_LINE))
outflags &= ~MMAN_spc;
}
static int
pre_fn(DECL_ARGS)
{
pre_syn(n);
n = n->child;
if (NULL == n)
return(0);
if (MDOC_SYNPRETTY & n->flags)
print_block(".HP 4n", MMAN_nl);
font_push('B');
print_node(meta, n);
font_pop();
outflags &= ~MMAN_spc;
print_word("(");
outflags &= ~MMAN_spc;
n = n->next;
if (NULL != n)
pre_fa(meta, n);
return(0);
}
static void
post_fn(DECL_ARGS)
{
print_word(")");
if (MDOC_SYNPRETTY & n->flags) {
print_word(";");
outflags |= MMAN_PP;
}
}
static int
pre_fo(DECL_ARGS)
{
switch (n->type) {
case MDOC_BLOCK:
pre_syn(n);
break;
case MDOC_HEAD:
if (MDOC_SYNPRETTY & n->flags)
print_block(".HP 4n", MMAN_nl);
font_push('B');
break;
case MDOC_BODY:
outflags &= ~MMAN_spc;
print_word("(");
outflags &= ~MMAN_spc;
break;
default:
break;
}
return(1);
}
static void
post_fo(DECL_ARGS)
{
switch (n->type) {
case MDOC_HEAD:
font_pop();
break;
case MDOC_BODY:
post_fn(meta, n);
break;
default:
break;
}
}
static int
pre_ft(DECL_ARGS)
{
pre_syn(n);
font_push('I');
return(1);
}
static int
pre_in(DECL_ARGS)
{
if (MDOC_SYNPRETTY & n->flags) {
pre_syn(n);
font_push('B');
print_word("#include <");
outflags &= ~MMAN_spc;
} else {
print_word("<");
outflags &= ~MMAN_spc;
font_push('I');
}
return(1);
}
static void
post_in(DECL_ARGS)
{
if (MDOC_SYNPRETTY & n->flags) {
outflags &= ~MMAN_spc;
print_word(">");
font_pop();
outflags |= MMAN_br;
} else {
font_pop();
outflags &= ~MMAN_spc;
print_word(">");
}
}
static int
pre_it(DECL_ARGS)
{
const struct mdoc_node *bln;
switch (n->type) {
case MDOC_HEAD:
outflags |= MMAN_PP | MMAN_nl;
bln = n->parent->parent;
if (0 == bln->norm->Bl.comp ||
(NULL == n->parent->prev &&
NULL == bln->parent->prev))
outflags |= MMAN_sp;
outflags &= ~MMAN_br;
switch (bln->norm->Bl.type) {
case LIST_item:
return(0);
case LIST_inset:
/* FALLTHROUGH */
case LIST_diag:
/* FALLTHROUGH */
case LIST_ohang:
if (bln->norm->Bl.type == LIST_diag)
print_line(".B \"", 0);
else
print_line(".R \"", 0);
outflags &= ~MMAN_spc;
return(1);
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_hyphen:
print_width(bln->norm->Bl.width, NULL, 0);
TPremain = 0;
outflags |= MMAN_nl;
font_push('B');
if (LIST_bullet == bln->norm->Bl.type)
print_word("\\(bu");
else
print_word("-");
font_pop();
outflags |= MMAN_nl;
return(0);
case LIST_enum:
print_width(bln->norm->Bl.width, NULL, 0);
TPremain = 0;
outflags |= MMAN_nl;
print_count(&bln->norm->Bl.count);
outflags |= MMAN_nl;
return(0);
case LIST_hang:
print_width(bln->norm->Bl.width, n->child, 6);
TPremain = 0;
outflags |= MMAN_nl;
return(1);
case LIST_tag:
print_width(bln->norm->Bl.width, n->child, 0);
putchar('\n');
outflags &= ~MMAN_spc;
return(1);
default:
return(1);
}
default:
break;
}
return(1);
}
/*
* This function is called after closing out an indented block.
* If we are inside an enclosing list, restore its indentation.
*/
static void
mid_it(void)
{
char buf[24];
/* Nothing to do outside a list. */
if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
return;
/* The indentation has already been set up. */
if (Bl_stack_post[Bl_stack_len - 1])
return;
/* Restore the indentation of the enclosing list. */
print_line(".RS", MMAN_Bk_susp);
(void)snprintf(buf, sizeof(buf), "%zun",
Bl_stack[Bl_stack_len - 1]);
print_word(buf);
/* Remeber to close out this .RS block later. */
Bl_stack_post[Bl_stack_len - 1] = 1;
}
static void
post_it(DECL_ARGS)
{
const struct mdoc_node *bln;
bln = n->parent->parent;
switch (n->type) {
case MDOC_HEAD:
switch (bln->norm->Bl.type) {
case LIST_diag:
outflags &= ~MMAN_spc;
print_word("\\ ");
break;
case LIST_ohang:
outflags |= MMAN_br;
break;
default:
break;
}
break;
case MDOC_BODY:
switch (bln->norm->Bl.type) {
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_hyphen:
/* FALLTHROUGH */
case LIST_enum:
/* FALLTHROUGH */
case LIST_hang:
/* FALLTHROUGH */
case LIST_tag:
assert(Bl_stack_len);
Bl_stack[--Bl_stack_len] = 0;
/*
* Our indentation had to be restored
* after a child display or child list.
* Close out that indentation block now.
*/
if (Bl_stack_post[Bl_stack_len]) {
print_line(".RE", MMAN_nl);
Bl_stack_post[Bl_stack_len] = 0;
}
break;
case LIST_column:
if (NULL != n->next) {
putchar('\t');
outflags &= ~MMAN_spc;
}
break;
default:
break;
}
break;
default:
break;
}
}
static void
post_lb(DECL_ARGS)
{
if (SEC_LIBRARY == n->sec)
outflags |= MMAN_br;
}
static int
pre_lk(DECL_ARGS)
{
const struct mdoc_node *link, *descr;
if (NULL == (link = n->child))
return(0);
if (NULL != (descr = link->next)) {
font_push('I');
while (NULL != descr) {
print_word(descr->string);
descr = descr->next;
}
print_word(":");
font_pop();
}
font_push('B');
print_word(link->string);
font_pop();
return(0);
}
static int
pre_ll(DECL_ARGS)
{
print_line(".ll", 0);
return(1);
}
static int
pre_li(DECL_ARGS)
{
font_push('R');
return(1);
}
static int
pre_nm(DECL_ARGS)
{
char *name;
if (MDOC_BLOCK == n->type) {
outflags |= MMAN_Bk;
pre_syn(n);
}
if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
return(1);
name = n->child ? n->child->string : meta->name;
if (NULL == name)
return(0);
if (MDOC_HEAD == n->type) {
if (NULL == n->parent->prev)
outflags |= MMAN_sp;
print_block(".HP", 0);
printf(" %zun", strlen(name) + 1);
outflags |= MMAN_nl;
}
font_push('B');
if (NULL == n->child)
print_word(meta->name);
return(1);
}
static void
post_nm(DECL_ARGS)
{
switch (n->type) {
case MDOC_BLOCK:
outflags &= ~MMAN_Bk;
break;
case MDOC_HEAD:
/* FALLTHROUGH */
case MDOC_ELEM:
if (n->child != NULL || meta->name != NULL)
font_pop();
break;
default:
break;
}
}
static int
pre_no(DECL_ARGS)
{
outflags |= MMAN_spc_force;
return(1);
}
static int
pre_ns(DECL_ARGS)
{
outflags &= ~MMAN_spc;
return(0);
}
static void
post_pf(DECL_ARGS)
{
- outflags &= ~MMAN_spc;
+ if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
+ outflags &= ~MMAN_spc;
}
static int
pre_pp(DECL_ARGS)
{
if (MDOC_It != n->parent->tok)
outflags |= MMAN_PP;
outflags |= MMAN_sp | MMAN_nl;
outflags &= ~MMAN_br;
return(0);
}
static int
pre_rs(DECL_ARGS)
{
if (SEC_SEE_ALSO == n->sec) {
outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
outflags &= ~MMAN_br;
}
return(1);
}
static int
pre_rv(DECL_ARGS)
{
int nchild;
outflags |= MMAN_br | MMAN_nl;
nchild = n->nchild;
if (nchild > 0) {
print_word("The");
for (n = n->child; n; n = n->next) {
font_push('B');
print_word(n->string);
font_pop();
outflags &= ~MMAN_spc;
print_word("()");
if (n->next == NULL)
continue;
if (nchild > 2) {
outflags &= ~MMAN_spc;
print_word(",");
}
if (n->next->next == NULL)
print_word("and");
}
if (nchild > 1)
print_word("functions return");
else
print_word("function returns");
print_word("the value\\~0 if successful;");
} else
print_word("Upon successful completion, "
"the value\\~0 is returned;");
print_word("otherwise the value\\~\\-1 is returned"
" and the global variable");
font_push('I');
print_word("errno");
font_pop();
print_word("is set to indicate the error.");
outflags |= MMAN_nl;
return(0);
}
static int
pre_skip(DECL_ARGS)
{
return(0);
}
static int
pre_sm(DECL_ARGS)
{
if (NULL == n->child)
outflags ^= MMAN_Sm;
else if (0 == strcmp("on", n->child->string))
outflags |= MMAN_Sm;
else
outflags &= ~MMAN_Sm;
if (MMAN_Sm & outflags)
outflags |= MMAN_spc;
return(0);
}
static int
pre_sp(DECL_ARGS)
{
if (MMAN_PP & outflags) {
outflags &= ~MMAN_PP;
print_line(".PP", 0);
} else
print_line(".sp", 0);
return(1);
}
static void
post_sp(DECL_ARGS)
{
outflags |= MMAN_nl;
}
static int
pre_sy(DECL_ARGS)
{
font_push('B');
return(1);
}
static int
pre_vt(DECL_ARGS)
{
if (MDOC_SYNPRETTY & n->flags) {
switch (n->type) {
case MDOC_BLOCK:
pre_syn(n);
return(1);
case MDOC_BODY:
break;
default:
return(0);
}
}
font_push('I');
return(1);
}
static void
post_vt(DECL_ARGS)
{
if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type)
return;
font_pop();
}
static int
pre_xr(DECL_ARGS)
{
n = n->child;
if (NULL == n)
return(0);
print_node(meta, n);
n = n->next;
if (NULL == n)
return(0);
outflags &= ~MMAN_spc;
print_word("(");
print_node(meta, n);
print_word(")");
return(0);
}
static int
pre_ux(DECL_ARGS)
{
print_word(manacts[n->tok].prefix);
if (NULL == n->child)
return(0);
outflags &= ~MMAN_spc;
print_word("\\ ");
outflags &= ~MMAN_spc;
return(1);
}
Index: vendor/mdocml/dist/mdoc_term.c
===================================================================
--- vendor/mdocml/dist/mdoc_term.c (revision 276215)
+++ vendor/mdocml/dist/mdoc_term.c (revision 276216)
@@ -1,2219 +1,2221 @@
-/* $Id: mdoc_term.c,v 1.297 2014/11/28 16:54:23 schwarze Exp $ */
+/* $Id: mdoc_term.c,v 1.299 2014/12/02 10:08:06 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons
* Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze
* Copyright (c) 2013 Franco Fichtner
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include "mandoc.h"
#include "mandoc_aux.h"
#include "out.h"
#include "term.h"
#include "mdoc.h"
#include "main.h"
struct termpair {
struct termpair *ppair;
int count;
};
#define DECL_ARGS struct termp *p, \
struct termpair *pair, \
const struct mdoc_meta *meta, \
struct mdoc_node *n
struct termact {
int (*pre)(DECL_ARGS);
void (*post)(DECL_ARGS);
};
static size_t a2width(const struct termp *, const char *);
static size_t a2height(const struct termp *, const char *);
static void print_bvspace(struct termp *,
const struct mdoc_node *,
const struct mdoc_node *);
static void print_mdoc_node(DECL_ARGS);
static void print_mdoc_nodelist(DECL_ARGS);
static void print_mdoc_head(struct termp *, const void *);
static void print_mdoc_foot(struct termp *, const void *);
static void synopsis_pre(struct termp *,
const struct mdoc_node *);
static void termp____post(DECL_ARGS);
static void termp__t_post(DECL_ARGS);
static void termp_bd_post(DECL_ARGS);
static void termp_bk_post(DECL_ARGS);
static void termp_bl_post(DECL_ARGS);
static void termp_fd_post(DECL_ARGS);
static void termp_fo_post(DECL_ARGS);
static void termp_in_post(DECL_ARGS);
static void termp_it_post(DECL_ARGS);
static void termp_lb_post(DECL_ARGS);
static void termp_nm_post(DECL_ARGS);
static void termp_pf_post(DECL_ARGS);
static void termp_quote_post(DECL_ARGS);
static void termp_sh_post(DECL_ARGS);
static void termp_ss_post(DECL_ARGS);
static int termp__a_pre(DECL_ARGS);
static int termp__t_pre(DECL_ARGS);
static int termp_an_pre(DECL_ARGS);
static int termp_ap_pre(DECL_ARGS);
static int termp_bd_pre(DECL_ARGS);
static int termp_bf_pre(DECL_ARGS);
static int termp_bk_pre(DECL_ARGS);
static int termp_bl_pre(DECL_ARGS);
static int termp_bold_pre(DECL_ARGS);
static int termp_bt_pre(DECL_ARGS);
static int termp_bx_pre(DECL_ARGS);
static int termp_cd_pre(DECL_ARGS);
static int termp_d1_pre(DECL_ARGS);
static int termp_ex_pre(DECL_ARGS);
static int termp_fa_pre(DECL_ARGS);
static int termp_fd_pre(DECL_ARGS);
static int termp_fl_pre(DECL_ARGS);
static int termp_fn_pre(DECL_ARGS);
static int termp_fo_pre(DECL_ARGS);
static int termp_ft_pre(DECL_ARGS);
static int termp_in_pre(DECL_ARGS);
static int termp_it_pre(DECL_ARGS);
static int termp_li_pre(DECL_ARGS);
static int termp_ll_pre(DECL_ARGS);
static int termp_lk_pre(DECL_ARGS);
static int termp_nd_pre(DECL_ARGS);
static int termp_nm_pre(DECL_ARGS);
static int termp_ns_pre(DECL_ARGS);
static int termp_quote_pre(DECL_ARGS);
static int termp_rs_pre(DECL_ARGS);
static int termp_rv_pre(DECL_ARGS);
static int termp_sh_pre(DECL_ARGS);
static int termp_skip_pre(DECL_ARGS);
static int termp_sm_pre(DECL_ARGS);
static int termp_sp_pre(DECL_ARGS);
static int termp_ss_pre(DECL_ARGS);
static int termp_under_pre(DECL_ARGS);
static int termp_ud_pre(DECL_ARGS);
static int termp_vt_pre(DECL_ARGS);
static int termp_xr_pre(DECL_ARGS);
static int termp_xx_pre(DECL_ARGS);
static const struct termact termacts[MDOC_MAX] = {
{ termp_ap_pre, NULL }, /* Ap */
{ NULL, NULL }, /* Dd */
{ NULL, NULL }, /* Dt */
{ NULL, NULL }, /* Os */
{ termp_sh_pre, termp_sh_post }, /* Sh */
{ termp_ss_pre, termp_ss_post }, /* Ss */
{ termp_sp_pre, NULL }, /* Pp */
{ termp_d1_pre, termp_bl_post }, /* D1 */
{ termp_d1_pre, termp_bl_post }, /* Dl */
{ termp_bd_pre, termp_bd_post }, /* Bd */
{ NULL, NULL }, /* Ed */
{ termp_bl_pre, termp_bl_post }, /* Bl */
{ NULL, NULL }, /* El */
{ termp_it_pre, termp_it_post }, /* It */
{ termp_under_pre, NULL }, /* Ad */
{ termp_an_pre, NULL }, /* An */
{ termp_under_pre, NULL }, /* Ar */
{ termp_cd_pre, NULL }, /* Cd */
{ termp_bold_pre, NULL }, /* Cm */
{ NULL, NULL }, /* Dv */
{ NULL, NULL }, /* Er */
{ NULL, NULL }, /* Ev */
{ termp_ex_pre, NULL }, /* Ex */
{ termp_fa_pre, NULL }, /* Fa */
{ termp_fd_pre, termp_fd_post }, /* Fd */
{ termp_fl_pre, NULL }, /* Fl */
{ termp_fn_pre, NULL }, /* Fn */
{ termp_ft_pre, NULL }, /* Ft */
{ termp_bold_pre, NULL }, /* Ic */
{ termp_in_pre, termp_in_post }, /* In */
{ termp_li_pre, NULL }, /* Li */
{ termp_nd_pre, NULL }, /* Nd */
{ termp_nm_pre, termp_nm_post }, /* Nm */
{ termp_quote_pre, termp_quote_post }, /* Op */
{ termp_ft_pre, NULL }, /* Ot */
{ termp_under_pre, NULL }, /* Pa */
{ termp_rv_pre, NULL }, /* Rv */
{ NULL, NULL }, /* St */
{ termp_under_pre, NULL }, /* Va */
{ termp_vt_pre, NULL }, /* Vt */
{ termp_xr_pre, NULL }, /* Xr */
{ termp__a_pre, termp____post }, /* %A */
{ termp_under_pre, termp____post }, /* %B */
{ NULL, termp____post }, /* %D */
{ termp_under_pre, termp____post }, /* %I */
{ termp_under_pre, termp____post }, /* %J */
{ NULL, termp____post }, /* %N */
{ NULL, termp____post }, /* %O */
{ NULL, termp____post }, /* %P */
{ NULL, termp____post }, /* %R */
{ termp__t_pre, termp__t_post }, /* %T */
{ NULL, termp____post }, /* %V */
{ NULL, NULL }, /* Ac */
{ termp_quote_pre, termp_quote_post }, /* Ao */
{ termp_quote_pre, termp_quote_post }, /* Aq */
{ NULL, NULL }, /* At */
{ NULL, NULL }, /* Bc */
{ termp_bf_pre, NULL }, /* Bf */
{ termp_quote_pre, termp_quote_post }, /* Bo */
{ termp_quote_pre, termp_quote_post }, /* Bq */
{ termp_xx_pre, NULL }, /* Bsx */
{ termp_bx_pre, NULL }, /* Bx */
{ termp_skip_pre, NULL }, /* Db */
{ NULL, NULL }, /* Dc */
{ termp_quote_pre, termp_quote_post }, /* Do */
{ termp_quote_pre, termp_quote_post }, /* Dq */
{ NULL, NULL }, /* Ec */ /* FIXME: no space */
{ NULL, NULL }, /* Ef */
{ termp_under_pre, NULL }, /* Em */
{ termp_quote_pre, termp_quote_post }, /* Eo */
{ termp_xx_pre, NULL }, /* Fx */
{ termp_bold_pre, NULL }, /* Ms */
{ termp_li_pre, NULL }, /* No */
{ termp_ns_pre, NULL }, /* Ns */
{ termp_xx_pre, NULL }, /* Nx */
{ termp_xx_pre, NULL }, /* Ox */
{ NULL, NULL }, /* Pc */
{ NULL, termp_pf_post }, /* Pf */
{ termp_quote_pre, termp_quote_post }, /* Po */
{ termp_quote_pre, termp_quote_post }, /* Pq */
{ NULL, NULL }, /* Qc */
{ termp_quote_pre, termp_quote_post }, /* Ql */
{ termp_quote_pre, termp_quote_post }, /* Qo */
{ termp_quote_pre, termp_quote_post }, /* Qq */
{ NULL, NULL }, /* Re */
{ termp_rs_pre, NULL }, /* Rs */
{ NULL, NULL }, /* Sc */
{ termp_quote_pre, termp_quote_post }, /* So */
{ termp_quote_pre, termp_quote_post }, /* Sq */
{ termp_sm_pre, NULL }, /* Sm */
{ termp_under_pre, NULL }, /* Sx */
{ termp_bold_pre, NULL }, /* Sy */
{ NULL, NULL }, /* Tn */
{ termp_xx_pre, NULL }, /* Ux */
{ NULL, NULL }, /* Xc */
{ NULL, NULL }, /* Xo */
{ termp_fo_pre, termp_fo_post }, /* Fo */
{ NULL, NULL }, /* Fc */
{ termp_quote_pre, termp_quote_post }, /* Oo */
{ NULL, NULL }, /* Oc */
{ termp_bk_pre, termp_bk_post }, /* Bk */
{ NULL, NULL }, /* Ek */
{ termp_bt_pre, NULL }, /* Bt */
{ NULL, NULL }, /* Hf */
{ termp_under_pre, NULL }, /* Fr */
{ termp_ud_pre, NULL }, /* Ud */
{ NULL, termp_lb_post }, /* Lb */
{ termp_sp_pre, NULL }, /* Lp */
{ termp_lk_pre, NULL }, /* Lk */
{ termp_under_pre, NULL }, /* Mt */
{ termp_quote_pre, termp_quote_post }, /* Brq */
{ termp_quote_pre, termp_quote_post }, /* Bro */
{ NULL, NULL }, /* Brc */
{ NULL, termp____post }, /* %C */
{ termp_skip_pre, NULL }, /* Es */
{ termp_quote_pre, termp_quote_post }, /* En */
{ termp_xx_pre, NULL }, /* Dx */
{ NULL, termp____post }, /* %Q */
{ termp_sp_pre, NULL }, /* br */
{ termp_sp_pre, NULL }, /* sp */
{ NULL, termp____post }, /* %U */
{ NULL, NULL }, /* Ta */
{ termp_ll_pre, NULL }, /* ll */
};
void
terminal_mdoc(void *arg, const struct mdoc *mdoc)
{
const struct mdoc_meta *meta;
struct mdoc_node *n;
struct termp *p;
p = (struct termp *)arg;
p->overstep = 0;
p->rmargin = p->maxrmargin = p->defrmargin;
p->tabwidth = term_len(p, 5);
n = mdoc_node(mdoc)->child;
meta = mdoc_meta(mdoc);
if (p->synopsisonly) {
while (n != NULL) {
if (n->tok == MDOC_Sh && n->sec == SEC_SYNOPSIS) {
if (n->child->next->child != NULL)
print_mdoc_nodelist(p, NULL,
meta, n->child->next->child);
term_newln(p);
break;
}
n = n->next;
}
} else {
if (p->defindent == 0)
p->defindent = 5;
term_begin(p, print_mdoc_head, print_mdoc_foot, meta);
if (n != NULL) {
if (n->tok != MDOC_Sh)
term_vspace(p);
print_mdoc_nodelist(p, NULL, meta, n);
}
term_end(p);
}
}
static void
print_mdoc_nodelist(DECL_ARGS)
{
print_mdoc_node(p, pair, meta, n);
if (n->next)
print_mdoc_nodelist(p, pair, meta, n->next);
}
static void
print_mdoc_node(DECL_ARGS)
{
int chld;
struct termpair npair;
size_t offset, rmargin;
chld = 1;
offset = p->offset;
rmargin = p->rmargin;
n->prev_font = term_fontq(p);
memset(&npair, 0, sizeof(struct termpair));
npair.ppair = pair;
/*
* Keeps only work until the end of a line. If a keep was
* invoked in a prior line, revert it to PREKEEP.
*/
if (TERMP_KEEP & p->flags) {
if (n->prev ? (n->prev->lastline != n->line) :
(n->parent && n->parent->line != n->line)) {
p->flags &= ~TERMP_KEEP;
p->flags |= TERMP_PREKEEP;
}
}
/*
* After the keep flags have been set up, we may now
* produce output. Note that some pre-handlers do so.
*/
switch (n->type) {
case MDOC_TEXT:
if (' ' == *n->string && MDOC_LINE & n->flags)
term_newln(p);
if (MDOC_DELIMC & n->flags)
p->flags |= TERMP_NOSPACE;
term_word(p, n->string);
if (MDOC_DELIMO & n->flags)
p->flags |= TERMP_NOSPACE;
break;
case MDOC_EQN:
if ( ! (n->flags & MDOC_LINE))
p->flags |= TERMP_NOSPACE;
term_eqn(p, n->eqn);
if (n->next != NULL && ! (n->next->flags & MDOC_LINE))
p->flags |= TERMP_NOSPACE;
break;
case MDOC_TBL:
term_tbl(p, n->span);
break;
default:
if (termacts[n->tok].pre &&
(n->end == ENDBODY_NOT || n->nchild))
chld = (*termacts[n->tok].pre)
(p, &npair, meta, n);
break;
}
if (chld && n->child)
print_mdoc_nodelist(p, &npair, meta, n->child);
term_fontpopq(p,
(ENDBODY_NOT == n->end ? n : n->pending)->prev_font);
switch (n->type) {
case MDOC_TEXT:
break;
case MDOC_TBL:
break;
case MDOC_EQN:
break;
default:
if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags)
break;
(void)(*termacts[n->tok].post)(p, &npair, meta, n);
/*
* Explicit end tokens not only call the post
* handler, but also tell the respective block
* that it must not call the post handler again.
*/
if (ENDBODY_NOT != n->end)
n->pending->flags |= MDOC_ENDED;
/*
* End of line terminating an implicit block
* while an explicit block is still open.
* Continue the explicit block without spacing.
*/
if (ENDBODY_NOSPACE == n->end)
p->flags |= TERMP_NOSPACE;
break;
}
if (MDOC_EOS & n->flags)
p->flags |= TERMP_SENTENCE;
if (MDOC_ll != n->tok) {
p->offset = offset;
p->rmargin = rmargin;
}
}
static void
print_mdoc_foot(struct termp *p, const void *arg)
{
const struct mdoc_meta *meta;
size_t sz;
meta = (const struct mdoc_meta *)arg;
term_fontrepl(p, TERMFONT_NONE);
/*
* Output the footer in new-groff style, that is, three columns
* with the middle being the manual date and flanking columns
* being the operating system:
*
* SYSTEM DATE SYSTEM
*/
term_vspace(p);
p->offset = 0;
sz = term_strlen(p, meta->date);
p->rmargin = p->maxrmargin > sz ?
(p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
p->trailspace = 1;
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
term_word(p, meta->os);
term_flushln(p);
p->offset = p->rmargin;
sz = term_strlen(p, meta->os);
p->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
p->flags |= TERMP_NOSPACE;
term_word(p, meta->date);
term_flushln(p);
p->offset = p->rmargin;
p->rmargin = p->maxrmargin;
p->trailspace = 0;
p->flags &= ~TERMP_NOBREAK;
p->flags |= TERMP_NOSPACE;
term_word(p, meta->os);
term_flushln(p);
p->offset = 0;
p->rmargin = p->maxrmargin;
p->flags = 0;
}
static void
print_mdoc_head(struct termp *p, const void *arg)
{
const struct mdoc_meta *meta;
char *volume, *title;
size_t vollen, titlen;
meta = (const struct mdoc_meta *)arg;
/*
* The header is strange. It has three components, which are
* really two with the first duplicated. It goes like this:
*
* IDENTIFIER TITLE IDENTIFIER
*
* The IDENTIFIER is NAME(SECTION), which is the command-name
* (if given, or "unknown" if not) followed by the manual page
* section. These are given in `Dt'. The TITLE is a free-form
* string depending on the manual volume. If not specified, it
* switches on the manual section.
*/
assert(meta->vol);
if (NULL == meta->arch)
volume = mandoc_strdup(meta->vol);
else
mandoc_asprintf(&volume, "%s (%s)",
meta->vol, meta->arch);
vollen = term_strlen(p, volume);
if (NULL == meta->msec)
title = mandoc_strdup(meta->title);
else
mandoc_asprintf(&title, "%s(%s)",
meta->title, meta->msec);
titlen = term_strlen(p, title);
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
p->trailspace = 1;
p->offset = 0;
p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
(p->maxrmargin - vollen + term_len(p, 1)) / 2 :
vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
term_word(p, title);
term_flushln(p);
p->flags |= TERMP_NOSPACE;
p->offset = p->rmargin;
p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
p->maxrmargin - titlen : p->maxrmargin;
term_word(p, volume);
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
p->trailspace = 0;
if (p->rmargin + titlen <= p->maxrmargin) {
p->flags |= TERMP_NOSPACE;
p->offset = p->rmargin;
p->rmargin = p->maxrmargin;
term_word(p, title);
term_flushln(p);
}
p->flags &= ~TERMP_NOSPACE;
p->offset = 0;
p->rmargin = p->maxrmargin;
free(title);
free(volume);
}
static size_t
a2height(const struct termp *p, const char *v)
{
struct roffsu su;
assert(v);
if ( ! a2roffsu(v, &su, SCALE_VS))
SCALE_VS_INIT(&su, atoi(v));
return(term_vspan(p, &su));
}
static size_t
a2width(const struct termp *p, const char *v)
{
struct roffsu su;
assert(v);
if ( ! a2roffsu(v, &su, SCALE_MAX)) {
SCALE_HS_INIT(&su, term_strlen(p, v));
su.scale /= term_strlen(p, "0");
}
return(term_hspan(p, &su));
}
/*
* Determine how much space to print out before block elements of `It'
* (and thus `Bl') and `Bd'. And then go ahead and print that space,
* too.
*/
static void
print_bvspace(struct termp *p,
const struct mdoc_node *bl,
const struct mdoc_node *n)
{
const struct mdoc_node *nn;
assert(n);
term_newln(p);
if (MDOC_Bd == bl->tok && bl->norm->Bd.comp)
return;
if (MDOC_Bl == bl->tok && bl->norm->Bl.comp)
return;
/* Do not vspace directly after Ss/Sh. */
nn = n;
while (nn->prev == NULL) {
do {
nn = nn->parent;
if (nn->type == MDOC_ROOT)
return;
} while (nn->type != MDOC_BLOCK);
if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
return;
if (nn->tok == MDOC_It &&
nn->parent->parent->norm->Bl.type != LIST_item)
break;
}
/* A `-column' does not assert vspace within the list. */
if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type)
if (n->prev && MDOC_It == n->prev->tok)
return;
/* A `-diag' without body does not vspace. */
if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type)
if (n->prev && MDOC_It == n->prev->tok) {
assert(n->prev->body);
if (NULL == n->prev->body->child)
return;
}
term_vspace(p);
}
static int
termp_ll_pre(DECL_ARGS)
{
term_setwidth(p, n->nchild ? n->child->string : NULL);
return(0);
}
static int
termp_it_pre(DECL_ARGS)
{
const struct mdoc_node *bl, *nn;
char buf[24];
int i;
size_t width, offset, ncols, dcol;
enum mdoc_list type;
if (MDOC_BLOCK == n->type) {
print_bvspace(p, n->parent->parent, n);
return(1);
}
bl = n->parent->parent->parent;
type = bl->norm->Bl.type;
/*
* First calculate width and offset. This is pretty easy unless
* we're a -column list, in which case all prior columns must
* be accounted for.
*/
width = offset = 0;
if (bl->norm->Bl.offs)
offset = a2width(p, bl->norm->Bl.offs);
switch (type) {
case LIST_column:
if (MDOC_HEAD == n->type)
break;
/*
* Imitate groff's column handling:
* - For each earlier column, add its width.
* - For less than 5 columns, add four more blanks per
* column.
* - For exactly 5 columns, add three more blank per
* column.
* - For more than 5 columns, add only one column.
*/
ncols = bl->norm->Bl.ncols;
dcol = ncols < 5 ? term_len(p, 4) :
ncols == 5 ? term_len(p, 3) : term_len(p, 1);
/*
* Calculate the offset by applying all prior MDOC_BODY,
* so we stop at the MDOC_HEAD (NULL == nn->prev).
*/
for (i = 0, nn = n->prev;
nn->prev && i < (int)ncols;
nn = nn->prev, i++)
offset += dcol + a2width(p,
bl->norm->Bl.cols[i]);
/*
* When exceeding the declared number of columns, leave
* the remaining widths at 0. This will later be
* adjusted to the default width of 10, or, for the last
* column, stretched to the right margin.
*/
if (i >= (int)ncols)
break;
/*
* Use the declared column widths, extended as explained
* in the preceding paragraph.
*/
width = a2width(p, bl->norm->Bl.cols[i]) + dcol;
break;
default:
if (NULL == bl->norm->Bl.width)
break;
/*
* Note: buffer the width by 2, which is groff's magic
* number for buffering single arguments. See the above
* handling for column for how this changes.
*/
assert(bl->norm->Bl.width);
width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
break;
}
/*
* List-type can override the width in the case of fixed-head
* values (bullet, dash/hyphen, enum). Tags need a non-zero
* offset.
*/
switch (type) {
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_hyphen:
/* FALLTHROUGH */
case LIST_enum:
if (width < term_len(p, 2))
width = term_len(p, 2);
break;
case LIST_hang:
if (0 == width)
width = term_len(p, 8);
break;
case LIST_column:
/* FALLTHROUGH */
case LIST_tag:
if (0 == width)
width = term_len(p, 10);
break;
default:
break;
}
/*
* Whitespace control. Inset bodies need an initial space,
* while diagonal bodies need two.
*/
p->flags |= TERMP_NOSPACE;
switch (type) {
case LIST_diag:
if (MDOC_BODY == n->type)
term_word(p, "\\ \\ ");
break;
case LIST_inset:
if (MDOC_BODY == n->type && n->parent->head->nchild)
term_word(p, "\\ ");
break;
default:
break;
}
p->flags |= TERMP_NOSPACE;
switch (type) {
case LIST_diag:
if (MDOC_HEAD == n->type)
term_fontpush(p, TERMFONT_BOLD);
break;
default:
break;
}
/*
* Pad and break control. This is the tricky part. These flags
* are documented in term_flushln() in term.c. Note that we're
* going to unset all of these flags in termp_it_post() when we
* exit.
*/
switch (type) {
case LIST_enum:
/*
* Weird special case.
* Very narrow enum lists actually hang.
*/
if (width == term_len(p, 2))
p->flags |= TERMP_HANG;
/* FALLTHROUGH */
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_hyphen:
if (MDOC_HEAD != n->type)
break;
p->flags |= TERMP_NOBREAK;
p->trailspace = 1;
break;
case LIST_hang:
if (MDOC_HEAD != n->type)
break;
/*
* This is ugly. If `-hang' is specified and the body
* is a `Bl' or `Bd', then we want basically to nullify
* the "overstep" effect in term_flushln() and treat
* this as a `-ohang' list instead.
*/
if (NULL != n->next &&
NULL != n->next->child &&
(MDOC_Bl == n->next->child->tok ||
MDOC_Bd == n->next->child->tok))
break;
p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
p->trailspace = 1;
break;
case LIST_tag:
if (MDOC_HEAD != n->type)
break;
p->flags |= TERMP_NOBREAK | TERMP_BRIND;
p->trailspace = 2;
if (NULL == n->next || NULL == n->next->child)
p->flags |= TERMP_DANGLE;
break;
case LIST_column:
if (MDOC_HEAD == n->type)
break;
if (NULL == n->next) {
p->flags &= ~TERMP_NOBREAK;
p->trailspace = 0;
} else {
p->flags |= TERMP_NOBREAK;
p->trailspace = 1;
}
break;
case LIST_diag:
if (MDOC_HEAD != n->type)
break;
p->flags |= TERMP_NOBREAK | TERMP_BRIND;
p->trailspace = 1;
break;
default:
break;
}
/*
* Margin control. Set-head-width lists have their right
* margins shortened. The body for these lists has the offset
* necessarily lengthened. Everybody gets the offset.
*/
p->offset += offset;
switch (type) {
case LIST_hang:
/*
* Same stipulation as above, regarding `-hang'. We
* don't want to recalculate rmargin and offsets when
* using `Bd' or `Bl' within `-hang' overstep lists.
*/
if (MDOC_HEAD == n->type &&
NULL != n->next &&
NULL != n->next->child &&
(MDOC_Bl == n->next->child->tok ||
MDOC_Bd == n->next->child->tok))
break;
/* FALLTHROUGH */
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_enum:
/* FALLTHROUGH */
case LIST_hyphen:
/* FALLTHROUGH */
case LIST_tag:
assert(width);
if (MDOC_HEAD == n->type)
p->rmargin = p->offset + width;
else
p->offset += width;
break;
case LIST_column:
assert(width);
p->rmargin = p->offset + width;
/*
* XXX - this behaviour is not documented: the
* right-most column is filled to the right margin.
*/
if (MDOC_HEAD == n->type)
break;
if (NULL == n->next && p->rmargin < p->maxrmargin)
p->rmargin = p->maxrmargin;
break;
default:
break;
}
/*
* The dash, hyphen, bullet and enum lists all have a special
* HEAD character (temporarily bold, in some cases).
*/
if (MDOC_HEAD == n->type)
switch (type) {
case LIST_bullet:
term_fontpush(p, TERMFONT_BOLD);
term_word(p, "\\[bu]");
term_fontpop(p);
break;
case LIST_dash:
/* FALLTHROUGH */
case LIST_hyphen:
term_fontpush(p, TERMFONT_BOLD);
term_word(p, "\\(hy");
term_fontpop(p);
break;
case LIST_enum:
(pair->ppair->ppair->count)++;
(void)snprintf(buf, sizeof(buf), "%d.",
pair->ppair->ppair->count);
term_word(p, buf);
break;
default:
break;
}
/*
* If we're not going to process our children, indicate so here.
*/
switch (type) {
case LIST_bullet:
/* FALLTHROUGH */
case LIST_item:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_hyphen:
/* FALLTHROUGH */
case LIST_enum:
if (MDOC_HEAD == n->type)
return(0);
break;
case LIST_column:
if (MDOC_HEAD == n->type)
return(0);
break;
default:
break;
}
return(1);
}
static void
termp_it_post(DECL_ARGS)
{
enum mdoc_list type;
if (MDOC_BLOCK == n->type)
return;
type = n->parent->parent->parent->norm->Bl.type;
switch (type) {
case LIST_item:
/* FALLTHROUGH */
case LIST_diag:
/* FALLTHROUGH */
case LIST_inset:
if (MDOC_BODY == n->type)
term_newln(p);
break;
case LIST_column:
if (MDOC_BODY == n->type)
term_flushln(p);
break;
default:
term_newln(p);
break;
}
/*
* Now that our output is flushed, we can reset our tags. Since
* only `It' sets these flags, we're free to assume that nobody
* has munged them in the meanwhile.
*/
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
TERMP_DANGLE | TERMP_HANG);
p->trailspace = 0;
}
static int
termp_nm_pre(DECL_ARGS)
{
const char *cp;
if (MDOC_BLOCK == n->type) {
p->flags |= TERMP_PREKEEP;
return(1);
}
if (MDOC_BODY == n->type) {
if (NULL == n->child)
return(0);
p->flags |= TERMP_NOSPACE;
cp = NULL;
if (n->prev->child != NULL)
cp = n->prev->child->string;
if (cp == NULL)
cp = meta->name;
if (cp == NULL)
p->offset += term_len(p, 6);
else
p->offset += term_len(p, 1) + term_strlen(p, cp);
return(1);
}
if (NULL == n->child && NULL == meta->name)
return(0);
if (MDOC_HEAD == n->type)
synopsis_pre(p, n->parent);
if (MDOC_HEAD == n->type &&
NULL != n->next && NULL != n->next->child) {
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
p->trailspace = 1;
p->rmargin = p->offset + term_len(p, 1);
if (NULL == n->child) {
p->rmargin += term_strlen(p, meta->name);
} else if (MDOC_TEXT == n->child->type) {
p->rmargin += term_strlen(p, n->child->string);
if (n->child->next)
p->flags |= TERMP_HANG;
} else {
p->rmargin += term_len(p, 5);
p->flags |= TERMP_HANG;
}
}
term_fontpush(p, TERMFONT_BOLD);
if (NULL == n->child)
term_word(p, meta->name);
return(1);
}
static void
termp_nm_post(DECL_ARGS)
{
if (MDOC_BLOCK == n->type) {
p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
} else if (MDOC_HEAD == n->type &&
NULL != n->next && NULL != n->next->child) {
term_flushln(p);
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
p->trailspace = 0;
} else if (MDOC_BODY == n->type && n->child)
term_flushln(p);
}
static int
termp_fl_pre(DECL_ARGS)
{
term_fontpush(p, TERMFONT_BOLD);
term_word(p, "\\-");
if ( ! (n->nchild == 0 &&
(n->next == NULL ||
n->next->type == MDOC_TEXT ||
n->next->flags & MDOC_LINE)))
p->flags |= TERMP_NOSPACE;
return(1);
}
static int
termp__a_pre(DECL_ARGS)
{
if (n->prev && MDOC__A == n->prev->tok)
if (NULL == n->next || MDOC__A != n->next->tok)
term_word(p, "and");
return(1);
}
static int
termp_an_pre(DECL_ARGS)
{
if (n->norm->An.auth == AUTH_split) {
p->flags &= ~TERMP_NOSPLIT;
p->flags |= TERMP_SPLIT;
return(0);
}
if (n->norm->An.auth == AUTH_nosplit) {
p->flags &= ~TERMP_SPLIT;
p->flags |= TERMP_NOSPLIT;
return(0);
}
if (n->child == NULL)
return(0);
if (p->flags & TERMP_SPLIT)
term_newln(p);
if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
p->flags |= TERMP_SPLIT;
return(1);
}
static int
termp_ns_pre(DECL_ARGS)
{
if ( ! (MDOC_LINE & n->flags))
p->flags |= TERMP_NOSPACE;
return(1);
}
static int
termp_rs_pre(DECL_ARGS)
{
if (SEC_SEE_ALSO != n->sec)
return(1);
if (MDOC_BLOCK == n->type && n->prev)
term_vspace(p);
return(1);
}
static int
termp_rv_pre(DECL_ARGS)
{
int nchild;
term_newln(p);
nchild = n->nchild;
if (nchild > 0) {
term_word(p, "The");
for (n = n->child; n; n = n->next) {
term_fontpush(p, TERMFONT_BOLD);
term_word(p, n->string);
term_fontpop(p);
p->flags |= TERMP_NOSPACE;
term_word(p, "()");
if (n->next == NULL)
continue;
if (nchild > 2) {
p->flags |= TERMP_NOSPACE;
term_word(p, ",");
}
if (n->next->next == NULL)
term_word(p, "and");
}
if (nchild > 1)
term_word(p, "functions return");
else
term_word(p, "function returns");
term_word(p, "the value\\~0 if successful;");
} else
term_word(p, "Upon successful completion,"
" the value\\~0 is returned;");
term_word(p, "otherwise the value\\~\\-1 is returned"
" and the global variable");
term_fontpush(p, TERMFONT_UNDER);
term_word(p, "errno");
term_fontpop(p);
term_word(p, "is set to indicate the error.");
p->flags |= TERMP_SENTENCE;
return(0);
}
static int
termp_ex_pre(DECL_ARGS)
{
int nchild;
term_newln(p);
term_word(p, "The");
nchild = n->nchild;
for (n = n->child; n; n = n->next) {
term_fontpush(p, TERMFONT_BOLD);
term_word(p, n->string);
term_fontpop(p);
if (nchild > 2 && n->next) {
p->flags |= TERMP_NOSPACE;
term_word(p, ",");
}
if (n->next && NULL == n->next->next)
term_word(p, "and");
}
if (nchild > 1)
term_word(p, "utilities exit\\~0");
else
term_word(p, "utility exits\\~0");
term_word(p, "on success, and\\~>0 if an error occurs.");
p->flags |= TERMP_SENTENCE;
return(0);
}
static int
termp_nd_pre(DECL_ARGS)
{
if (n->type == MDOC_BODY)
term_word(p, "\\(en");
return(1);
}
static int
termp_bl_pre(DECL_ARGS)
{
return(MDOC_HEAD != n->type);
}
static void
termp_bl_post(DECL_ARGS)
{
if (MDOC_BLOCK == n->type)
term_newln(p);
}
static int
termp_xr_pre(DECL_ARGS)
{
if (NULL == (n = n->child))
return(0);
assert(MDOC_TEXT == n->type);
term_word(p, n->string);
if (NULL == (n = n->next))
return(0);
p->flags |= TERMP_NOSPACE;
term_word(p, "(");
p->flags |= TERMP_NOSPACE;
assert(MDOC_TEXT == n->type);
term_word(p, n->string);
p->flags |= TERMP_NOSPACE;
term_word(p, ")");
return(0);
}
/*
* This decides how to assert whitespace before any of the SYNOPSIS set
* of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
* macro combos).
*/
static void
synopsis_pre(struct termp *p, const struct mdoc_node *n)
{
/*
* Obviously, if we're not in a SYNOPSIS or no prior macros
* exist, do nothing.
*/
if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
return;
/*
* If we're the second in a pair of like elements, emit our
* newline and return. UNLESS we're `Fo', `Fn', `Fn', in which
* case we soldier on.
*/
if (n->prev->tok == n->tok &&
MDOC_Ft != n->tok &&
MDOC_Fo != n->tok &&
MDOC_Fn != n->tok) {
term_newln(p);
return;
}
/*
* If we're one of the SYNOPSIS set and non-like pair-wise after
* another (or Fn/Fo, which we've let slip through) then assert
* vertical space, else only newline and move on.
*/
switch (n->prev->tok) {
case MDOC_Fd:
/* FALLTHROUGH */
case MDOC_Fn:
/* FALLTHROUGH */
case MDOC_Fo:
/* FALLTHROUGH */
case MDOC_In:
/* FALLTHROUGH */
case MDOC_Vt:
term_vspace(p);
break;
case MDOC_Ft:
if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
term_vspace(p);
break;
}
/* FALLTHROUGH */
default:
term_newln(p);
break;
}
}
static int
termp_vt_pre(DECL_ARGS)
{
if (MDOC_ELEM == n->type) {
synopsis_pre(p, n);
return(termp_under_pre(p, pair, meta, n));
} else if (MDOC_BLOCK == n->type) {
synopsis_pre(p, n);
return(1);
} else if (MDOC_HEAD == n->type)
return(0);
return(termp_under_pre(p, pair, meta, n));
}
static int
termp_bold_pre(DECL_ARGS)
{
term_fontpush(p, TERMFONT_BOLD);
return(1);
}
static int
termp_fd_pre(DECL_ARGS)
{
synopsis_pre(p, n);
return(termp_bold_pre(p, pair, meta, n));
}
static void
termp_fd_post(DECL_ARGS)
{
term_newln(p);
}
static int
termp_sh_pre(DECL_ARGS)
{
switch (n->type) {
case MDOC_BLOCK:
/*
* Vertical space before sections, except
* when the previous section was empty.
*/
if (n->prev == NULL ||
MDOC_Sh != n->prev->tok ||
(n->prev->body != NULL &&
n->prev->body->child != NULL))
term_vspace(p);
break;
case MDOC_HEAD:
term_fontpush(p, TERMFONT_BOLD);
break;
case MDOC_BODY:
p->offset = term_len(p, p->defindent);
if (SEC_AUTHORS == n->sec)
p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
break;
default:
break;
}
return(1);
}
static void
termp_sh_post(DECL_ARGS)
{
switch (n->type) {
case MDOC_HEAD:
term_newln(p);
break;
case MDOC_BODY:
term_newln(p);
p->offset = 0;
break;
default:
break;
}
}
static int
termp_bt_pre(DECL_ARGS)
{
term_word(p, "is currently in beta test.");
p->flags |= TERMP_SENTENCE;
return(0);
}
static void
termp_lb_post(DECL_ARGS)
{
if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags)
term_newln(p);
}
static int
termp_ud_pre(DECL_ARGS)
{
term_word(p, "currently under development.");
p->flags |= TERMP_SENTENCE;
return(0);
}
static int
termp_d1_pre(DECL_ARGS)
{
if (MDOC_BLOCK != n->type)
return(1);
term_newln(p);
p->offset += term_len(p, p->defindent + 1);
return(1);
}
static int
termp_ft_pre(DECL_ARGS)
{
/* NB: MDOC_LINE does not effect this! */
synopsis_pre(p, n);
term_fontpush(p, TERMFONT_UNDER);
return(1);
}
static int
termp_fn_pre(DECL_ARGS)
{
size_t rmargin = 0;
int pretty;
pretty = MDOC_SYNPRETTY & n->flags;
synopsis_pre(p, n);
if (NULL == (n = n->child))
return(0);
if (pretty) {
rmargin = p->rmargin;
p->rmargin = p->offset + term_len(p, 4);
p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
}
assert(MDOC_TEXT == n->type);
term_fontpush(p, TERMFONT_BOLD);
term_word(p, n->string);
term_fontpop(p);
if (pretty) {
term_flushln(p);
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
p->offset = p->rmargin;
p->rmargin = rmargin;
}
p->flags |= TERMP_NOSPACE;
term_word(p, "(");
p->flags |= TERMP_NOSPACE;
for (n = n->next; n; n = n->next) {
assert(MDOC_TEXT == n->type);
term_fontpush(p, TERMFONT_UNDER);
if (pretty)
p->flags |= TERMP_NBRWORD;
term_word(p, n->string);
term_fontpop(p);
if (n->next) {
p->flags |= TERMP_NOSPACE;
term_word(p, ",");
}
}
p->flags |= TERMP_NOSPACE;
term_word(p, ")");
if (pretty) {
p->flags |= TERMP_NOSPACE;
term_word(p, ";");
term_flushln(p);
}
return(0);
}
static int
termp_fa_pre(DECL_ARGS)
{
const struct mdoc_node *nn;
if (n->parent->tok != MDOC_Fo) {
term_fontpush(p, TERMFONT_UNDER);
return(1);
}
for (nn = n->child; nn; nn = nn->next) {
term_fontpush(p, TERMFONT_UNDER);
p->flags |= TERMP_NBRWORD;
term_word(p, nn->string);
term_fontpop(p);
if (nn->next || (n->next && n->next->tok == MDOC_Fa)) {
p->flags |= TERMP_NOSPACE;
term_word(p, ",");
}
}
return(0);
}
static int
termp_bd_pre(DECL_ARGS)
{
size_t tabwidth, lm, len, rm, rmax;
struct mdoc_node *nn;
if (MDOC_BLOCK == n->type) {
print_bvspace(p, n, n);
return(1);
} else if (MDOC_HEAD == n->type)
return(0);
/* Handle the -offset argument. */
if (n->norm->Bd.offs == NULL ||
! strcmp(n->norm->Bd.offs, "left"))
/* nothing */;
else if ( ! strcmp(n->norm->Bd.offs, "indent"))
p->offset += term_len(p, p->defindent + 1);
else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
p->offset += term_len(p, (p->defindent + 1) * 2);
else
p->offset += a2width(p, n->norm->Bd.offs);
/*
* If -ragged or -filled are specified, the block does nothing
* but change the indentation. If -unfilled or -literal are
* specified, text is printed exactly as entered in the display:
* for macro lines, a newline is appended to the line. Blank
* lines are allowed.
*/
if (DISP_literal != n->norm->Bd.type &&
DISP_unfilled != n->norm->Bd.type &&
DISP_centered != n->norm->Bd.type)
return(1);
tabwidth = p->tabwidth;
if (DISP_literal == n->norm->Bd.type)
p->tabwidth = term_len(p, 8);
lm = p->offset;
rm = p->rmargin;
rmax = p->maxrmargin;
p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
for (nn = n->child; nn; nn = nn->next) {
if (DISP_centered == n->norm->Bd.type) {
if (MDOC_TEXT == nn->type) {
len = term_strlen(p, nn->string);
p->offset = len >= rm ? 0 :
lm + len >= rm ? rm - len :
(lm + rm - len) / 2;
} else
p->offset = lm;
}
print_mdoc_node(p, pair, meta, nn);
/*
* If the printed node flushes its own line, then we
* needn't do it here as well. This is hacky, but the
* notion of selective eoln whitespace is pretty dumb
* anyway, so don't sweat it.
*/
switch (nn->tok) {
case MDOC_Sm:
/* FALLTHROUGH */
case MDOC_br:
/* FALLTHROUGH */
case MDOC_sp:
/* FALLTHROUGH */
case MDOC_Bl:
/* FALLTHROUGH */
case MDOC_D1:
/* FALLTHROUGH */
case MDOC_Dl:
/* FALLTHROUGH */
case MDOC_Lp:
/* FALLTHROUGH */
case MDOC_Pp:
continue;
default:
break;
}
- if (nn->next && nn->next->line == nn->line)
+ if (p->flags & TERMP_NONEWLINE ||
+ (nn->next && ! (nn->next->flags & MDOC_LINE)))
continue;
term_flushln(p);
p->flags |= TERMP_NOSPACE;
}
p->tabwidth = tabwidth;
p->rmargin = rm;
p->maxrmargin = rmax;
return(0);
}
static void
termp_bd_post(DECL_ARGS)
{
size_t rm, rmax;
if (MDOC_BODY != n->type)
return;
rm = p->rmargin;
rmax = p->maxrmargin;
if (DISP_literal == n->norm->Bd.type ||
DISP_unfilled == n->norm->Bd.type)
p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
p->flags |= TERMP_NOSPACE;
term_newln(p);
p->rmargin = rm;
p->maxrmargin = rmax;
}
static int
termp_bx_pre(DECL_ARGS)
{
if (NULL != (n = n->child)) {
term_word(p, n->string);
p->flags |= TERMP_NOSPACE;
term_word(p, "BSD");
} else {
term_word(p, "BSD");
return(0);
}
if (NULL != (n = n->next)) {
p->flags |= TERMP_NOSPACE;
term_word(p, "-");
p->flags |= TERMP_NOSPACE;
term_word(p, n->string);
}
return(0);
}
static int
termp_xx_pre(DECL_ARGS)
{
const char *pp;
int flags;
pp = NULL;
switch (n->tok) {
case MDOC_Bsx:
pp = "BSD/OS";
break;
case MDOC_Dx:
pp = "DragonFly";
break;
case MDOC_Fx:
pp = "FreeBSD";
break;
case MDOC_Nx:
pp = "NetBSD";
break;
case MDOC_Ox:
pp = "OpenBSD";
break;
case MDOC_Ux:
pp = "UNIX";
break;
default:
abort();
/* NOTREACHED */
}
term_word(p, pp);
if (n->child) {
flags = p->flags;
p->flags |= TERMP_KEEP;
term_word(p, n->child->string);
p->flags = flags;
}
return(0);
}
static void
termp_pf_post(DECL_ARGS)
{
- p->flags |= TERMP_NOSPACE;
+ if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
+ p->flags |= TERMP_NOSPACE;
}
static int
termp_ss_pre(DECL_ARGS)
{
switch (n->type) {
case MDOC_BLOCK:
term_newln(p);
if (n->prev)
term_vspace(p);
break;
case MDOC_HEAD:
term_fontpush(p, TERMFONT_BOLD);
p->offset = term_len(p, (p->defindent+1)/2);
break;
case MDOC_BODY:
p->offset = term_len(p, p->defindent);
break;
default:
break;
}
return(1);
}
static void
termp_ss_post(DECL_ARGS)
{
if (n->type == MDOC_HEAD || n->type == MDOC_BODY)
term_newln(p);
}
static int
termp_cd_pre(DECL_ARGS)
{
synopsis_pre(p, n);
term_fontpush(p, TERMFONT_BOLD);
return(1);
}
static int
termp_in_pre(DECL_ARGS)
{
synopsis_pre(p, n);
if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) {
term_fontpush(p, TERMFONT_BOLD);
term_word(p, "#include");
term_word(p, "<");
} else {
term_word(p, "<");
term_fontpush(p, TERMFONT_UNDER);
}
p->flags |= TERMP_NOSPACE;
return(1);
}
static void
termp_in_post(DECL_ARGS)
{
if (MDOC_SYNPRETTY & n->flags)
term_fontpush(p, TERMFONT_BOLD);
p->flags |= TERMP_NOSPACE;
term_word(p, ">");
if (MDOC_SYNPRETTY & n->flags)
term_fontpop(p);
}
static int
termp_sp_pre(DECL_ARGS)
{
size_t i, len;
switch (n->tok) {
case MDOC_sp:
len = n->child ? a2height(p, n->child->string) : 1;
break;
case MDOC_br:
len = 0;
break;
default:
len = 1;
break;
}
if (0 == len)
term_newln(p);
for (i = 0; i < len; i++)
term_vspace(p);
return(0);
}
static int
termp_skip_pre(DECL_ARGS)
{
return(0);
}
static int
termp_quote_pre(DECL_ARGS)
{
if (MDOC_BODY != n->type && MDOC_ELEM != n->type)
return(1);
switch (n->tok) {
case MDOC_Ao:
/* FALLTHROUGH */
case MDOC_Aq:
term_word(p, n->parent->prev != NULL &&
n->parent->prev->tok == MDOC_An ? "<" : "\\(la");
break;
case MDOC_Bro:
/* FALLTHROUGH */
case MDOC_Brq:
term_word(p, "{");
break;
case MDOC_Oo:
/* FALLTHROUGH */
case MDOC_Op:
/* FALLTHROUGH */
case MDOC_Bo:
/* FALLTHROUGH */
case MDOC_Bq:
term_word(p, "[");
break;
case MDOC_Do:
/* FALLTHROUGH */
case MDOC_Dq:
term_word(p, "\\(lq");
break;
case MDOC_En:
if (NULL == n->norm->Es ||
NULL == n->norm->Es->child)
return(1);
term_word(p, n->norm->Es->child->string);
break;
case MDOC_Eo:
break;
case MDOC_Po:
/* FALLTHROUGH */
case MDOC_Pq:
term_word(p, "(");
break;
case MDOC__T:
/* FALLTHROUGH */
case MDOC_Qo:
/* FALLTHROUGH */
case MDOC_Qq:
term_word(p, "\"");
break;
case MDOC_Ql:
/* FALLTHROUGH */
case MDOC_So:
/* FALLTHROUGH */
case MDOC_Sq:
term_word(p, "\\(oq");
break;
default:
abort();
/* NOTREACHED */
}
p->flags |= TERMP_NOSPACE;
return(1);
}
static void
termp_quote_post(DECL_ARGS)
{
if (n->type != MDOC_BODY && n->type != MDOC_ELEM)
return;
if ( ! (n->tok == MDOC_En ||
(n->tok == MDOC_Eo && n->end == ENDBODY_SPACE)))
p->flags |= TERMP_NOSPACE;
switch (n->tok) {
case MDOC_Ao:
/* FALLTHROUGH */
case MDOC_Aq:
term_word(p, n->parent->prev != NULL &&
n->parent->prev->tok == MDOC_An ? ">" : "\\(ra");
break;
case MDOC_Bro:
/* FALLTHROUGH */
case MDOC_Brq:
term_word(p, "}");
break;
case MDOC_Oo:
/* FALLTHROUGH */
case MDOC_Op:
/* FALLTHROUGH */
case MDOC_Bo:
/* FALLTHROUGH */
case MDOC_Bq:
term_word(p, "]");
break;
case MDOC_Do:
/* FALLTHROUGH */
case MDOC_Dq:
term_word(p, "\\(rq");
break;
case MDOC_En:
if (NULL != n->norm->Es &&
NULL != n->norm->Es->child &&
NULL != n->norm->Es->child->next) {
p->flags |= TERMP_NOSPACE;
term_word(p, n->norm->Es->child->next->string);
}
break;
case MDOC_Eo:
break;
case MDOC_Po:
/* FALLTHROUGH */
case MDOC_Pq:
term_word(p, ")");
break;
case MDOC__T:
/* FALLTHROUGH */
case MDOC_Qo:
/* FALLTHROUGH */
case MDOC_Qq:
term_word(p, "\"");
break;
case MDOC_Ql:
/* FALLTHROUGH */
case MDOC_So:
/* FALLTHROUGH */
case MDOC_Sq:
term_word(p, "\\(cq");
break;
default:
abort();
/* NOTREACHED */
}
}
static int
termp_fo_pre(DECL_ARGS)
{
size_t rmargin = 0;
int pretty;
pretty = MDOC_SYNPRETTY & n->flags;
if (MDOC_BLOCK == n->type) {
synopsis_pre(p, n);
return(1);
} else if (MDOC_BODY == n->type) {
if (pretty) {
rmargin = p->rmargin;
p->rmargin = p->offset + term_len(p, 4);
p->flags |= TERMP_NOBREAK | TERMP_BRIND |
TERMP_HANG;
}
p->flags |= TERMP_NOSPACE;
term_word(p, "(");
p->flags |= TERMP_NOSPACE;
if (pretty) {
term_flushln(p);
p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
TERMP_HANG);
p->offset = p->rmargin;
p->rmargin = rmargin;
}
return(1);
}
if (NULL == n->child)
return(0);
/* XXX: we drop non-initial arguments as per groff. */
assert(n->child->string);
term_fontpush(p, TERMFONT_BOLD);
term_word(p, n->child->string);
return(0);
}
static void
termp_fo_post(DECL_ARGS)
{
if (MDOC_BODY != n->type)
return;
p->flags |= TERMP_NOSPACE;
term_word(p, ")");
if (MDOC_SYNPRETTY & n->flags) {
p->flags |= TERMP_NOSPACE;
term_word(p, ";");
term_flushln(p);
}
}
static int
termp_bf_pre(DECL_ARGS)
{
if (MDOC_HEAD == n->type)
return(0);
else if (MDOC_BODY != n->type)
return(1);
if (FONT_Em == n->norm->Bf.font)
term_fontpush(p, TERMFONT_UNDER);
else if (FONT_Sy == n->norm->Bf.font)
term_fontpush(p, TERMFONT_BOLD);
else
term_fontpush(p, TERMFONT_NONE);
return(1);
}
static int
termp_sm_pre(DECL_ARGS)
{
if (NULL == n->child)
p->flags ^= TERMP_NONOSPACE;
else if (0 == strcmp("on", n->child->string))
p->flags &= ~TERMP_NONOSPACE;
else
p->flags |= TERMP_NONOSPACE;
if (p->col && ! (TERMP_NONOSPACE & p->flags))
p->flags &= ~TERMP_NOSPACE;
return(0);
}
static int
termp_ap_pre(DECL_ARGS)
{
p->flags |= TERMP_NOSPACE;
term_word(p, "'");
p->flags |= TERMP_NOSPACE;
return(1);
}
static void
termp____post(DECL_ARGS)
{
/*
* Handle lists of authors. In general, print each followed by
* a comma. Don't print the comma if there are only two
* authors.
*/
if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
if (NULL == n->next->next || MDOC__A != n->next->next->tok)
if (NULL == n->prev || MDOC__A != n->prev->tok)
return;
/* TODO: %U. */
if (NULL == n->parent || MDOC_Rs != n->parent->tok)
return;
p->flags |= TERMP_NOSPACE;
if (NULL == n->next) {
term_word(p, ".");
p->flags |= TERMP_SENTENCE;
} else
term_word(p, ",");
}
static int
termp_li_pre(DECL_ARGS)
{
term_fontpush(p, TERMFONT_NONE);
return(1);
}
static int
termp_lk_pre(DECL_ARGS)
{
const struct mdoc_node *link, *descr;
if (NULL == (link = n->child))
return(0);
if (NULL != (descr = link->next)) {
term_fontpush(p, TERMFONT_UNDER);
while (NULL != descr) {
term_word(p, descr->string);
descr = descr->next;
}
p->flags |= TERMP_NOSPACE;
term_word(p, ":");
term_fontpop(p);
}
term_fontpush(p, TERMFONT_BOLD);
term_word(p, link->string);
term_fontpop(p);
return(0);
}
static int
termp_bk_pre(DECL_ARGS)
{
switch (n->type) {
case MDOC_BLOCK:
break;
case MDOC_HEAD:
return(0);
case MDOC_BODY:
if (n->parent->args || 0 == n->prev->nchild)
p->flags |= TERMP_PREKEEP;
break;
default:
abort();
/* NOTREACHED */
}
return(1);
}
static void
termp_bk_post(DECL_ARGS)
{
if (MDOC_BODY == n->type)
p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
}
static void
termp__t_post(DECL_ARGS)
{
/*
* If we're in an `Rs' and there's a journal present, then quote
* us instead of underlining us (for disambiguation).
*/
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T)
termp_quote_post(p, pair, meta, n);
termp____post(p, pair, meta, n);
}
static int
termp__t_pre(DECL_ARGS)
{
/*
* If we're in an `Rs' and there's a journal present, then quote
* us instead of underlining us (for disambiguation).
*/
if (n->parent && MDOC_Rs == n->parent->tok &&
n->parent->norm->Rs.quote_T)
return(termp_quote_pre(p, pair, meta, n));
term_fontpush(p, TERMFONT_UNDER);
return(1);
}
static int
termp_under_pre(DECL_ARGS)
{
term_fontpush(p, TERMFONT_UNDER);
return(1);
}
Index: vendor/mdocml/dist/mdoc_validate.c
===================================================================
--- vendor/mdocml/dist/mdoc_validate.c (revision 276215)
+++ vendor/mdocml/dist/mdoc_validate.c (revision 276216)
@@ -1,2491 +1,2491 @@
-/* $Id: mdoc_validate.c,v 1.262 2014/11/28 18:36:35 schwarze Exp $ */
+/* $Id: mdoc_validate.c,v 1.263 2014/11/30 05:29:00 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons
* Copyright (c) 2010-2014 Ingo Schwarze
* Copyright (c) 2010 Joerg Sonnenberger
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#ifndef OSNAME
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include "mdoc.h"
#include "mandoc.h"
#include "mandoc_aux.h"
#include "libmdoc.h"
#include "libmandoc.h"
/* FIXME: .Bl -diag can't have non-text children in HEAD. */
#define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n
#define POST_ARGS struct mdoc *mdoc
enum check_ineq {
CHECK_LT,
CHECK_GT,
CHECK_EQ
};
enum check_lvl {
CHECK_WARN,
CHECK_ERROR,
};
typedef void (*v_pre)(PRE_ARGS);
typedef void (*v_post)(POST_ARGS);
struct valids {
v_pre pre;
v_post post;
};
static void check_count(struct mdoc *, enum mdoc_type,
enum check_lvl, enum check_ineq, int);
static void check_text(struct mdoc *, int, int, char *);
static void check_argv(struct mdoc *,
struct mdoc_node *, struct mdoc_argv *);
static void check_args(struct mdoc *, struct mdoc_node *);
static int child_an(const struct mdoc_node *);
static enum mdoc_sec a2sec(const char *);
static size_t macro2len(enum mdoct);
static void rewrite_macro2len(char **);
static void bwarn_ge1(POST_ARGS);
static void ewarn_eq1(POST_ARGS);
static void ewarn_ge1(POST_ARGS);
static void hwarn_eq0(POST_ARGS);
static void post_an(POST_ARGS);
static void post_at(POST_ARGS);
static void post_bf(POST_ARGS);
static void post_bk(POST_ARGS);
static void post_bl(POST_ARGS);
static void post_bl_block(POST_ARGS);
static void post_bl_block_tag(POST_ARGS);
static void post_bl_head(POST_ARGS);
static void post_bx(POST_ARGS);
static void post_d1(POST_ARGS);
static void post_defaults(POST_ARGS);
static void post_dd(POST_ARGS);
static void post_dt(POST_ARGS);
static void post_en(POST_ARGS);
static void post_es(POST_ARGS);
static void post_eoln(POST_ARGS);
static void post_ex(POST_ARGS);
static void post_fa(POST_ARGS);
static void post_fn(POST_ARGS);
static void post_fname(POST_ARGS);
static void post_fo(POST_ARGS);
static void post_hyph(POST_ARGS);
static void post_hyphtext(POST_ARGS);
static void post_ignpar(POST_ARGS);
static void post_it(POST_ARGS);
static void post_lb(POST_ARGS);
static void post_literal(POST_ARGS);
static void post_nd(POST_ARGS);
static void post_nm(POST_ARGS);
static void post_ns(POST_ARGS);
static void post_os(POST_ARGS);
static void post_par(POST_ARGS);
static void post_root(POST_ARGS);
static void post_rs(POST_ARGS);
static void post_sh(POST_ARGS);
static void post_sh_head(POST_ARGS);
static void post_sh_name(POST_ARGS);
static void post_sh_see_also(POST_ARGS);
static void post_sh_authors(POST_ARGS);
static void post_sm(POST_ARGS);
static void post_st(POST_ARGS);
static void post_vt(POST_ARGS);
static void pre_an(PRE_ARGS);
static void pre_bd(PRE_ARGS);
static void pre_bl(PRE_ARGS);
static void pre_dd(PRE_ARGS);
static void pre_display(PRE_ARGS);
static void pre_dt(PRE_ARGS);
static void pre_literal(PRE_ARGS);
static void pre_obsolete(PRE_ARGS);
static void pre_os(PRE_ARGS);
static void pre_par(PRE_ARGS);
static void pre_std(PRE_ARGS);
static const struct valids mdoc_valids[MDOC_MAX] = {
{ NULL, NULL }, /* Ap */
{ pre_dd, post_dd }, /* Dd */
{ pre_dt, post_dt }, /* Dt */
{ pre_os, post_os }, /* Os */
{ NULL, post_sh }, /* Sh */
{ NULL, post_ignpar }, /* Ss */
{ pre_par, post_par }, /* Pp */
{ pre_display, post_d1 }, /* D1 */
{ pre_literal, post_literal }, /* Dl */
{ pre_bd, post_literal }, /* Bd */
{ NULL, NULL }, /* Ed */
{ pre_bl, post_bl }, /* Bl */
{ NULL, NULL }, /* El */
{ pre_par, post_it }, /* It */
{ NULL, NULL }, /* Ad */
{ pre_an, post_an }, /* An */
{ NULL, post_defaults }, /* Ar */
{ NULL, NULL }, /* Cd */
{ NULL, NULL }, /* Cm */
{ NULL, NULL }, /* Dv */
{ NULL, NULL }, /* Er */
{ NULL, NULL }, /* Ev */
{ pre_std, post_ex }, /* Ex */
{ NULL, post_fa }, /* Fa */
{ NULL, ewarn_ge1 }, /* Fd */
{ NULL, NULL }, /* Fl */
{ NULL, post_fn }, /* Fn */
{ NULL, NULL }, /* Ft */
{ NULL, NULL }, /* Ic */
{ NULL, ewarn_eq1 }, /* In */
{ NULL, post_defaults }, /* Li */
{ NULL, post_nd }, /* Nd */
{ NULL, post_nm }, /* Nm */
{ NULL, NULL }, /* Op */
{ pre_obsolete, NULL }, /* Ot */
{ NULL, post_defaults }, /* Pa */
{ pre_std, NULL }, /* Rv */
{ NULL, post_st }, /* St */
{ NULL, NULL }, /* Va */
{ NULL, post_vt }, /* Vt */
{ NULL, ewarn_ge1 }, /* Xr */
{ NULL, ewarn_ge1 }, /* %A */
{ NULL, post_hyphtext }, /* %B */ /* FIXME: can be used outside Rs/Re. */
{ NULL, ewarn_ge1 }, /* %D */
{ NULL, ewarn_ge1 }, /* %I */
{ NULL, ewarn_ge1 }, /* %J */
{ NULL, post_hyphtext }, /* %N */
{ NULL, post_hyphtext }, /* %O */
{ NULL, ewarn_ge1 }, /* %P */
{ NULL, post_hyphtext }, /* %R */
{ NULL, post_hyphtext }, /* %T */ /* FIXME: can be used outside Rs/Re. */
{ NULL, ewarn_ge1 }, /* %V */
{ NULL, NULL }, /* Ac */
{ NULL, NULL }, /* Ao */
{ NULL, NULL }, /* Aq */
{ NULL, post_at }, /* At */
{ NULL, NULL }, /* Bc */
{ NULL, post_bf }, /* Bf */
{ NULL, NULL }, /* Bo */
{ NULL, NULL }, /* Bq */
{ NULL, NULL }, /* Bsx */
{ NULL, post_bx }, /* Bx */
{ pre_obsolete, NULL }, /* Db */
{ NULL, NULL }, /* Dc */
{ NULL, NULL }, /* Do */
{ NULL, NULL }, /* Dq */
{ NULL, NULL }, /* Ec */
{ NULL, NULL }, /* Ef */
{ NULL, NULL }, /* Em */
{ NULL, NULL }, /* Eo */
{ NULL, NULL }, /* Fx */
{ NULL, NULL }, /* Ms */
{ NULL, NULL }, /* No */
{ NULL, post_ns }, /* Ns */
{ NULL, NULL }, /* Nx */
{ NULL, NULL }, /* Ox */
{ NULL, NULL }, /* Pc */
- { NULL, ewarn_eq1 }, /* Pf */
+ { NULL, NULL }, /* Pf */
{ NULL, NULL }, /* Po */
{ NULL, NULL }, /* Pq */
{ NULL, NULL }, /* Qc */
{ NULL, NULL }, /* Ql */
{ NULL, NULL }, /* Qo */
{ NULL, NULL }, /* Qq */
{ NULL, NULL }, /* Re */
{ NULL, post_rs }, /* Rs */
{ NULL, NULL }, /* Sc */
{ NULL, NULL }, /* So */
{ NULL, NULL }, /* Sq */
{ NULL, post_sm }, /* Sm */
{ NULL, post_hyph }, /* Sx */
{ NULL, NULL }, /* Sy */
{ NULL, NULL }, /* Tn */
{ NULL, NULL }, /* Ux */
{ NULL, NULL }, /* Xc */
{ NULL, NULL }, /* Xo */
{ NULL, post_fo }, /* Fo */
{ NULL, NULL }, /* Fc */
{ NULL, NULL }, /* Oo */
{ NULL, NULL }, /* Oc */
{ NULL, post_bk }, /* Bk */
{ NULL, NULL }, /* Ek */
{ NULL, post_eoln }, /* Bt */
{ NULL, NULL }, /* Hf */
{ pre_obsolete, NULL }, /* Fr */
{ NULL, post_eoln }, /* Ud */
{ NULL, post_lb }, /* Lb */
{ pre_par, post_par }, /* Lp */
{ NULL, NULL }, /* Lk */
{ NULL, post_defaults }, /* Mt */
{ NULL, NULL }, /* Brq */
{ NULL, NULL }, /* Bro */
{ NULL, NULL }, /* Brc */
{ NULL, ewarn_ge1 }, /* %C */
{ pre_obsolete, post_es }, /* Es */
{ pre_obsolete, post_en }, /* En */
{ NULL, NULL }, /* Dx */
{ NULL, ewarn_ge1 }, /* %Q */
{ NULL, post_par }, /* br */
{ NULL, post_par }, /* sp */
{ NULL, ewarn_eq1 }, /* %U */
{ NULL, NULL }, /* Ta */
{ NULL, NULL }, /* ll */
};
#define RSORD_MAX 14 /* Number of `Rs' blocks. */
static const enum mdoct rsord[RSORD_MAX] = {
MDOC__A,
MDOC__T,
MDOC__B,
MDOC__I,
MDOC__J,
MDOC__R,
MDOC__N,
MDOC__V,
MDOC__U,
MDOC__P,
MDOC__Q,
MDOC__C,
MDOC__D,
MDOC__O
};
static const char * const secnames[SEC__MAX] = {
NULL,
"NAME",
"LIBRARY",
"SYNOPSIS",
"DESCRIPTION",
"CONTEXT",
"IMPLEMENTATION NOTES",
"RETURN VALUES",
"ENVIRONMENT",
"FILES",
"EXIT STATUS",
"EXAMPLES",
"DIAGNOSTICS",
"COMPATIBILITY",
"ERRORS",
"SEE ALSO",
"STANDARDS",
"HISTORY",
"AUTHORS",
"CAVEATS",
"BUGS",
"SECURITY CONSIDERATIONS",
NULL
};
void
mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
{
v_pre p;
switch (n->type) {
case MDOC_TEXT:
check_text(mdoc, n->line, n->pos, n->string);
/* FALLTHROUGH */
case MDOC_TBL:
/* FALLTHROUGH */
case MDOC_EQN:
/* FALLTHROUGH */
case MDOC_ROOT:
return;
default:
break;
}
check_args(mdoc, n);
p = mdoc_valids[n->tok].pre;
if (*p)
(*p)(mdoc, n);
}
void
mdoc_valid_post(struct mdoc *mdoc)
{
struct mdoc_node *n;
v_post p;
n = mdoc->last;
if (n->flags & MDOC_VALID)
return;
n->flags |= MDOC_VALID;
switch (n->type) {
case MDOC_TEXT:
/* FALLTHROUGH */
case MDOC_EQN:
/* FALLTHROUGH */
case MDOC_TBL:
break;
case MDOC_ROOT:
post_root(mdoc);
break;
default:
/*
* Closing delimiters are not special at the
* beginning of a block, opening delimiters
* are not special at the end.
*/
if (n->child != NULL)
n->child->flags &= ~MDOC_DELIMC;
if (n->last != NULL)
n->last->flags &= ~MDOC_DELIMO;
/* Call the macro's postprocessor. */
p = mdoc_valids[n->tok].post;
if (*p)
(*p)(mdoc);
break;
}
}
static void
check_count(struct mdoc *mdoc, enum mdoc_type type,
enum check_lvl lvl, enum check_ineq ineq, int val)
{
const char *p;
enum mandocerr t;
if (mdoc->last->type != type)
return;
switch (ineq) {
case CHECK_LT:
p = "less than ";
if (mdoc->last->nchild < val)
return;
break;
case CHECK_GT:
p = "more than ";
if (mdoc->last->nchild > val)
return;
break;
case CHECK_EQ:
p = "";
if (val == mdoc->last->nchild)
return;
break;
default:
abort();
/* NOTREACHED */
}
t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
mandoc_vmsg(t, mdoc->parse, mdoc->last->line,
mdoc->last->pos, "want %s%d children (have %d)",
p, val, mdoc->last->nchild);
}
static void
bwarn_ge1(POST_ARGS)
{
check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
}
static void
ewarn_eq1(POST_ARGS)
{
check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
}
static void
ewarn_ge1(POST_ARGS)
{
check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
}
static void
hwarn_eq0(POST_ARGS)
{
check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
}
static void
check_args(struct mdoc *mdoc, struct mdoc_node *n)
{
int i;
if (NULL == n->args)
return;
assert(n->args->argc);
for (i = 0; i < (int)n->args->argc; i++)
check_argv(mdoc, n, &n->args->argv[i]);
}
static void
check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
{
int i;
for (i = 0; i < (int)v->sz; i++)
check_text(mdoc, v->line, v->pos, v->value[i]);
}
static void
check_text(struct mdoc *mdoc, int ln, int pos, char *p)
{
char *cp;
if (MDOC_LITERAL & mdoc->flags)
return;
for (cp = p; NULL != (p = strchr(p, '\t')); p++)
mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
ln, pos + (int)(p - cp), NULL);
}
static void
pre_display(PRE_ARGS)
{
struct mdoc_node *node;
if (MDOC_BLOCK != n->type)
return;
for (node = mdoc->last->parent; node; node = node->parent)
if (MDOC_BLOCK == node->type)
if (MDOC_Bd == node->tok)
break;
if (node)
mandoc_vmsg(MANDOCERR_BD_NEST,
mdoc->parse, n->line, n->pos,
"%s in Bd", mdoc_macronames[n->tok]);
}
static void
pre_bl(PRE_ARGS)
{
struct mdoc_node *np;
struct mdoc_argv *argv, *wa;
int i;
enum mdocargt mdoclt;
enum mdoc_list lt;
if (MDOC_BLOCK != n->type) {
if (ENDBODY_NOT != n->end) {
assert(n->pending);
np = n->pending->parent;
} else
np = n->parent;
assert(np);
assert(MDOC_BLOCK == np->type);
assert(MDOC_Bl == np->tok);
return;
}
/*
* First figure out which kind of list to use: bind ourselves to
* the first mentioned list type and warn about any remaining
* ones. If we find no list type, we default to LIST_item.
*/
wa = (n->args == NULL) ? NULL : n->args->argv;
mdoclt = MDOC_ARG_MAX;
for (i = 0; n->args && i < (int)n->args->argc; i++) {
argv = n->args->argv + i;
lt = LIST__NONE;
switch (argv->arg) {
/* Set list types. */
case MDOC_Bullet:
lt = LIST_bullet;
break;
case MDOC_Dash:
lt = LIST_dash;
break;
case MDOC_Enum:
lt = LIST_enum;
break;
case MDOC_Hyphen:
lt = LIST_hyphen;
break;
case MDOC_Item:
lt = LIST_item;
break;
case MDOC_Tag:
lt = LIST_tag;
break;
case MDOC_Diag:
lt = LIST_diag;
break;
case MDOC_Hang:
lt = LIST_hang;
break;
case MDOC_Ohang:
lt = LIST_ohang;
break;
case MDOC_Inset:
lt = LIST_inset;
break;
case MDOC_Column:
lt = LIST_column;
break;
/* Set list arguments. */
case MDOC_Compact:
if (n->norm->Bl.comp)
mandoc_msg(MANDOCERR_ARG_REP,
mdoc->parse, argv->line,
argv->pos, "Bl -compact");
n->norm->Bl.comp = 1;
break;
case MDOC_Width:
wa = argv;
if (0 == argv->sz) {
mandoc_msg(MANDOCERR_ARG_EMPTY,
mdoc->parse, argv->line,
argv->pos, "Bl -width");
n->norm->Bl.width = "0n";
break;
}
if (NULL != n->norm->Bl.width)
mandoc_vmsg(MANDOCERR_ARG_REP,
mdoc->parse, argv->line,
argv->pos, "Bl -width %s",
argv->value[0]);
rewrite_macro2len(argv->value);
n->norm->Bl.width = argv->value[0];
break;
case MDOC_Offset:
if (0 == argv->sz) {
mandoc_msg(MANDOCERR_ARG_EMPTY,
mdoc->parse, argv->line,
argv->pos, "Bl -offset");
break;
}
if (NULL != n->norm->Bl.offs)
mandoc_vmsg(MANDOCERR_ARG_REP,
mdoc->parse, argv->line,
argv->pos, "Bl -offset %s",
argv->value[0]);
rewrite_macro2len(argv->value);
n->norm->Bl.offs = argv->value[0];
break;
default:
continue;
}
if (LIST__NONE == lt)
continue;
mdoclt = argv->arg;
/* Check: multiple list types. */
if (LIST__NONE != n->norm->Bl.type) {
mandoc_vmsg(MANDOCERR_BL_REP,
mdoc->parse, n->line, n->pos,
"Bl -%s", mdoc_argnames[argv->arg]);
continue;
}
/* The list type should come first. */
if (n->norm->Bl.width ||
n->norm->Bl.offs ||
n->norm->Bl.comp)
mandoc_vmsg(MANDOCERR_BL_LATETYPE,
mdoc->parse, n->line, n->pos, "Bl -%s",
mdoc_argnames[n->args->argv[0].arg]);
n->norm->Bl.type = lt;
if (LIST_column == lt) {
n->norm->Bl.ncols = argv->sz;
n->norm->Bl.cols = (void *)argv->value;
}
}
/* Allow lists to default to LIST_item. */
if (LIST__NONE == n->norm->Bl.type) {
mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
n->line, n->pos, "Bl");
n->norm->Bl.type = LIST_item;
}
/*
* Validate the width field. Some list types don't need width
* types and should be warned about them. Others should have it
* and must also be warned. Yet others have a default and need
* no warning.
*/
switch (n->norm->Bl.type) {
case LIST_tag:
if (NULL == n->norm->Bl.width)
mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
n->line, n->pos, "Bl -tag");
break;
case LIST_column:
/* FALLTHROUGH */
case LIST_diag:
/* FALLTHROUGH */
case LIST_ohang:
/* FALLTHROUGH */
case LIST_inset:
/* FALLTHROUGH */
case LIST_item:
if (n->norm->Bl.width)
mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
wa->line, wa->pos, "Bl -%s",
mdoc_argnames[mdoclt]);
break;
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_hyphen:
if (NULL == n->norm->Bl.width)
n->norm->Bl.width = "2n";
break;
case LIST_enum:
if (NULL == n->norm->Bl.width)
n->norm->Bl.width = "3n";
break;
default:
break;
}
pre_par(mdoc, n);
}
static void
pre_bd(PRE_ARGS)
{
struct mdoc_node *np;
struct mdoc_argv *argv;
int i;
enum mdoc_disp dt;
pre_literal(mdoc, n);
if (MDOC_BLOCK != n->type) {
if (ENDBODY_NOT != n->end) {
assert(n->pending);
np = n->pending->parent;
} else
np = n->parent;
assert(np);
assert(MDOC_BLOCK == np->type);
assert(MDOC_Bd == np->tok);
return;
}
for (i = 0; n->args && i < (int)n->args->argc; i++) {
argv = n->args->argv + i;
dt = DISP__NONE;
switch (argv->arg) {
case MDOC_Centred:
dt = DISP_centered;
break;
case MDOC_Ragged:
dt = DISP_ragged;
break;
case MDOC_Unfilled:
dt = DISP_unfilled;
break;
case MDOC_Filled:
dt = DISP_filled;
break;
case MDOC_Literal:
dt = DISP_literal;
break;
case MDOC_File:
mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
n->line, n->pos, NULL);
break;
case MDOC_Offset:
if (0 == argv->sz) {
mandoc_msg(MANDOCERR_ARG_EMPTY,
mdoc->parse, argv->line,
argv->pos, "Bd -offset");
break;
}
if (NULL != n->norm->Bd.offs)
mandoc_vmsg(MANDOCERR_ARG_REP,
mdoc->parse, argv->line,
argv->pos, "Bd -offset %s",
argv->value[0]);
rewrite_macro2len(argv->value);
n->norm->Bd.offs = argv->value[0];
break;
case MDOC_Compact:
if (n->norm->Bd.comp)
mandoc_msg(MANDOCERR_ARG_REP,
mdoc->parse, argv->line,
argv->pos, "Bd -compact");
n->norm->Bd.comp = 1;
break;
default:
abort();
/* NOTREACHED */
}
if (DISP__NONE == dt)
continue;
if (DISP__NONE == n->norm->Bd.type)
n->norm->Bd.type = dt;
else
mandoc_vmsg(MANDOCERR_BD_REP,
mdoc->parse, n->line, n->pos,
"Bd -%s", mdoc_argnames[argv->arg]);
}
if (DISP__NONE == n->norm->Bd.type) {
mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
n->line, n->pos, "Bd");
n->norm->Bd.type = DISP_ragged;
}
pre_par(mdoc, n);
}
static void
pre_an(PRE_ARGS)
{
struct mdoc_argv *argv;
size_t i;
if (n->args == NULL)
return;
for (i = 1; i < n->args->argc; i++) {
argv = n->args->argv + i;
mandoc_vmsg(MANDOCERR_AN_REP,
mdoc->parse, argv->line, argv->pos,
"An -%s", mdoc_argnames[argv->arg]);
}
argv = n->args->argv;
if (argv->arg == MDOC_Split)
n->norm->An.auth = AUTH_split;
else if (argv->arg == MDOC_Nosplit)
n->norm->An.auth = AUTH_nosplit;
else
abort();
}
static void
pre_std(PRE_ARGS)
{
if (n->args && 1 == n->args->argc)
if (MDOC_Std == n->args->argv[0].arg)
return;
mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
}
static void
pre_obsolete(PRE_ARGS)
{
if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
}
static void
pre_dt(PRE_ARGS)
{
if (mdoc->meta.title != NULL)
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
n->line, n->pos, "Dt");
else if (mdoc->meta.os != NULL)
mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
n->line, n->pos, "Dt after Os");
}
static void
pre_os(PRE_ARGS)
{
if (mdoc->meta.os != NULL)
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
n->line, n->pos, "Os");
else if (mdoc->flags & MDOC_PBODY)
mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
n->line, n->pos, "Os");
}
static void
pre_dd(PRE_ARGS)
{
if (mdoc->meta.date != NULL)
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
n->line, n->pos, "Dd");
else if (mdoc->flags & MDOC_PBODY)
mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
n->line, n->pos, "Dd");
else if (mdoc->meta.title != NULL)
mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
n->line, n->pos, "Dd after Dt");
else if (mdoc->meta.os != NULL)
mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
n->line, n->pos, "Dd after Os");
}
static void
post_bf(POST_ARGS)
{
struct mdoc_node *np, *nch;
enum mdocargt arg;
/*
* Unlike other data pointers, these are "housed" by the HEAD
* element, which contains the goods.
*/
if (MDOC_HEAD != mdoc->last->type) {
if (ENDBODY_NOT != mdoc->last->end) {
assert(mdoc->last->pending);
np = mdoc->last->pending->parent->head;
} else if (MDOC_BLOCK != mdoc->last->type) {
np = mdoc->last->parent->head;
} else
np = mdoc->last->head;
assert(np);
assert(MDOC_HEAD == np->type);
assert(MDOC_Bf == np->tok);
return;
}
np = mdoc->last;
assert(MDOC_BLOCK == np->parent->type);
assert(MDOC_Bf == np->parent->tok);
/* Check the number of arguments. */
nch = np->child;
if (NULL == np->parent->args) {
if (NULL == nch) {
mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
np->line, np->pos, "Bf");
return;
}
nch = nch->next;
}
if (NULL != nch)
mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
nch->line, nch->pos, "Bf ... %s", nch->string);
/* Extract argument into data. */
if (np->parent->args) {
arg = np->parent->args->argv[0].arg;
if (MDOC_Emphasis == arg)
np->norm->Bf.font = FONT_Em;
else if (MDOC_Literal == arg)
np->norm->Bf.font = FONT_Li;
else if (MDOC_Symbolic == arg)
np->norm->Bf.font = FONT_Sy;
else
abort();
return;
}
/* Extract parameter into data. */
if (0 == strcmp(np->child->string, "Em"))
np->norm->Bf.font = FONT_Em;
else if (0 == strcmp(np->child->string, "Li"))
np->norm->Bf.font = FONT_Li;
else if (0 == strcmp(np->child->string, "Sy"))
np->norm->Bf.font = FONT_Sy;
else
mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
np->child->line, np->child->pos,
"Bf %s", np->child->string);
}
static void
post_lb(POST_ARGS)
{
struct mdoc_node *n;
const char *stdlibname;
char *libname;
check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
n = mdoc->last->child;
assert(MDOC_TEXT == n->type);
if (NULL == (stdlibname = mdoc_a2lib(n->string)))
mandoc_asprintf(&libname,
"library \\(lq%s\\(rq", n->string);
else
libname = mandoc_strdup(stdlibname);
free(n->string);
n->string = libname;
}
static void
post_eoln(POST_ARGS)
{
const struct mdoc_node *n;
n = mdoc->last;
if (n->child)
mandoc_vmsg(MANDOCERR_ARG_SKIP,
mdoc->parse, n->line, n->pos,
"%s %s", mdoc_macronames[n->tok],
n->child->string);
}
static void
post_fname(POST_ARGS)
{
const struct mdoc_node *n;
const char *cp;
size_t pos;
n = mdoc->last->child;
pos = strcspn(n->string, "()");
cp = n->string + pos;
if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
n->line, n->pos + pos, n->string);
}
static void
post_fn(POST_ARGS)
{
post_fname(mdoc);
post_fa(mdoc);
}
static void
post_fo(POST_ARGS)
{
check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1);
bwarn_ge1(mdoc);
if (mdoc->last->type == MDOC_HEAD && mdoc->last->nchild)
post_fname(mdoc);
}
static void
post_fa(POST_ARGS)
{
const struct mdoc_node *n;
const char *cp;
for (n = mdoc->last->child; n != NULL; n = n->next) {
for (cp = n->string; *cp != '\0'; cp++) {
/* Ignore callbacks and alterations. */
if (*cp == '(' || *cp == '{')
break;
if (*cp != ',')
continue;
mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
n->line, n->pos + (cp - n->string),
n->string);
break;
}
}
}
static void
post_vt(POST_ARGS)
{
const struct mdoc_node *n;
/*
* The Vt macro comes in both ELEM and BLOCK form, both of which
* have different syntaxes (yet more context-sensitive
* behaviour). ELEM types must have a child, which is already
* guaranteed by the in_line parsing routine; BLOCK types,
* specifically the BODY, should only have TEXT children.
*/
if (MDOC_BODY != mdoc->last->type)
return;
for (n = mdoc->last->child; n; n = n->next)
if (MDOC_TEXT != n->type)
mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
}
static void
post_nm(POST_ARGS)
{
if (NULL != mdoc->meta.name)
return;
mdoc_deroff(&mdoc->meta.name, mdoc->last);
if (NULL == mdoc->meta.name)
mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
mdoc->last->line, mdoc->last->pos, "Nm");
}
static void
post_nd(POST_ARGS)
{
check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0);
post_hyph(mdoc);
}
static void
post_d1(POST_ARGS)
{
bwarn_ge1(mdoc);
post_hyph(mdoc);
}
static void
post_literal(POST_ARGS)
{
if (mdoc->last->tok == MDOC_Bd)
hwarn_eq0(mdoc);
bwarn_ge1(mdoc);
/*
* The `Dl' (note "el" not "one") and `Bd' macros unset the
* MDOC_LITERAL flag as they leave. Note that `Bd' only sets
* this in literal mode, but it doesn't hurt to just switch it
* off in general since displays can't be nested.
*/
if (MDOC_BODY == mdoc->last->type)
mdoc->flags &= ~MDOC_LITERAL;
}
static void
post_defaults(POST_ARGS)
{
struct mdoc_node *nn;
/*
* The `Ar' defaults to "file ..." if no value is provided as an
* argument; the `Mt' and `Pa' macros use "~"; the `Li' just
* gets an empty string.
*/
if (mdoc->last->child)
return;
nn = mdoc->last;
mdoc->next = MDOC_NEXT_CHILD;
switch (nn->tok) {
case MDOC_Ar:
mdoc_word_alloc(mdoc, nn->line, nn->pos, "file");
mdoc_word_alloc(mdoc, nn->line, nn->pos, "...");
break;
case MDOC_Pa:
/* FALLTHROUGH */
case MDOC_Mt:
mdoc_word_alloc(mdoc, nn->line, nn->pos, "~");
break;
default:
abort();
/* NOTREACHED */
}
mdoc->last = nn;
}
static void
post_at(POST_ARGS)
{
struct mdoc_node *n;
const char *std_att;
char *att;
n = mdoc->last;
if (n->child == NULL) {
mdoc->next = MDOC_NEXT_CHILD;
mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
mdoc->last = n;
return;
}
/*
* If we have a child, look it up in the standard keys. If a
* key exist, use that instead of the child; if it doesn't,
* prefix "AT&T UNIX " to the existing data.
*/
n = n->child;
assert(MDOC_TEXT == n->type);
if (NULL == (std_att = mdoc_a2att(n->string))) {
mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
n->line, n->pos, "At %s", n->string);
mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
} else
att = mandoc_strdup(std_att);
free(n->string);
n->string = att;
}
static void
post_an(POST_ARGS)
{
struct mdoc_node *np;
np = mdoc->last;
if (AUTH__NONE == np->norm->An.auth) {
if (0 == np->child)
check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
} else if (np->child)
check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
}
static void
post_en(POST_ARGS)
{
if (MDOC_BLOCK == mdoc->last->type)
mdoc->last->norm->Es = mdoc->last_es;
}
static void
post_es(POST_ARGS)
{
mdoc->last_es = mdoc->last;
}
static void
post_it(POST_ARGS)
{
int i, cols;
enum mdoc_list lt;
struct mdoc_node *nbl, *nit, *nch;
nit = mdoc->last;
if (MDOC_BLOCK != nit->type)
return;
nbl = nit->parent->parent;
lt = nbl->norm->Bl.type;
switch (lt) {
case LIST_tag:
/* FALLTHROUGH */
case LIST_hang:
/* FALLTHROUGH */
case LIST_ohang:
/* FALLTHROUGH */
case LIST_inset:
/* FALLTHROUGH */
case LIST_diag:
if (NULL == nit->head->child)
mandoc_vmsg(MANDOCERR_IT_NOHEAD,
mdoc->parse, nit->line, nit->pos,
"Bl -%s It",
mdoc_argnames[nbl->args->argv[0].arg]);
break;
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_enum:
/* FALLTHROUGH */
case LIST_hyphen:
if (NULL == nit->body->child)
mandoc_vmsg(MANDOCERR_IT_NOBODY,
mdoc->parse, nit->line, nit->pos,
"Bl -%s It",
mdoc_argnames[nbl->args->argv[0].arg]);
/* FALLTHROUGH */
case LIST_item:
if (NULL != nit->head->child)
mandoc_vmsg(MANDOCERR_ARG_SKIP,
mdoc->parse, nit->line, nit->pos,
"It %s", nit->head->child->string);
break;
case LIST_column:
cols = (int)nbl->norm->Bl.ncols;
assert(NULL == nit->head->child);
for (i = 0, nch = nit->child; nch; nch = nch->next)
if (MDOC_BODY == nch->type)
i++;
if (i < cols || i > cols + 1)
mandoc_vmsg(MANDOCERR_ARGCOUNT,
mdoc->parse, nit->line, nit->pos,
"columns == %d (have %d)", cols, i);
break;
default:
abort();
}
}
static void
post_bl_block(POST_ARGS)
{
struct mdoc_node *n, *ni, *nc;
/*
* These are fairly complicated, so we've broken them into two
* functions. post_bl_block_tag() is called when a -tag is
* specified, but no -width (it must be guessed). The second
* when a -width is specified (macro indicators must be
* rewritten into real lengths).
*/
n = mdoc->last;
if (LIST_tag == n->norm->Bl.type &&
NULL == n->norm->Bl.width) {
post_bl_block_tag(mdoc);
assert(n->norm->Bl.width);
}
for (ni = n->body->child; ni; ni = ni->next) {
if (NULL == ni->body)
continue;
nc = ni->body->last;
while (NULL != nc) {
switch (nc->tok) {
case MDOC_Pp:
/* FALLTHROUGH */
case MDOC_Lp:
/* FALLTHROUGH */
case MDOC_br:
break;
default:
nc = NULL;
continue;
}
if (NULL == ni->next) {
mandoc_msg(MANDOCERR_PAR_MOVE,
mdoc->parse, nc->line, nc->pos,
mdoc_macronames[nc->tok]);
mdoc_node_relink(mdoc, nc);
} else if (0 == n->norm->Bl.comp &&
LIST_column != n->norm->Bl.type) {
mandoc_vmsg(MANDOCERR_PAR_SKIP,
mdoc->parse, nc->line, nc->pos,
"%s before It",
mdoc_macronames[nc->tok]);
mdoc_node_delete(mdoc, nc);
} else
break;
nc = ni->body->last;
}
}
}
/*
* If the argument of -offset or -width is a macro,
* replace it with the associated default width.
*/
void
rewrite_macro2len(char **arg)
{
size_t width;
enum mdoct tok;
if (*arg == NULL)
return;
else if ( ! strcmp(*arg, "Ds"))
width = 6;
else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX)
return;
else
width = macro2len(tok);
free(*arg);
mandoc_asprintf(arg, "%zun", width);
}
static void
post_bl_block_tag(POST_ARGS)
{
struct mdoc_node *n, *nn;
size_t sz, ssz;
int i;
char buf[24];
/*
* Calculate the -width for a `Bl -tag' list if it hasn't been
* provided. Uses the first head macro. NOTE AGAIN: this is
* ONLY if the -width argument has NOT been provided. See
* rewrite_macro2len() for converting the -width string.
*/
sz = 10;
n = mdoc->last;
for (nn = n->body->child; nn; nn = nn->next) {
if (MDOC_It != nn->tok)
continue;
assert(MDOC_BLOCK == nn->type);
nn = nn->head->child;
if (nn == NULL)
break;
if (MDOC_TEXT == nn->type) {
sz = strlen(nn->string) + 1;
break;
}
if (0 != (ssz = macro2len(nn->tok)))
sz = ssz;
break;
}
/* Defaults to ten ens. */
(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
/*
* We have to dynamically add this to the macro's argument list.
* We're guaranteed that a MDOC_Width doesn't already exist.
*/
assert(n->args);
i = (int)(n->args->argc)++;
n->args->argv = mandoc_reallocarray(n->args->argv,
n->args->argc, sizeof(struct mdoc_argv));
n->args->argv[i].arg = MDOC_Width;
n->args->argv[i].line = n->line;
n->args->argv[i].pos = n->pos;
n->args->argv[i].sz = 1;
n->args->argv[i].value = mandoc_malloc(sizeof(char *));
n->args->argv[i].value[0] = mandoc_strdup(buf);
/* Set our width! */
n->norm->Bl.width = n->args->argv[i].value[0];
}
static void
post_bl_head(POST_ARGS)
{
struct mdoc_node *np, *nn, *nnp;
struct mdoc_argv *argv;
int i, j;
if (LIST_column != mdoc->last->norm->Bl.type) {
/* FIXME: this should be ERROR class... */
hwarn_eq0(mdoc);
return;
}
/*
* Append old-style lists, where the column width specifiers
* trail as macro parameters, to the new-style ("normal-form")
* lists where they're argument values following -column.
*/
if (mdoc->last->child == NULL)
return;
np = mdoc->last->parent;
assert(np->args);
for (j = 0; j < (int)np->args->argc; j++)
if (MDOC_Column == np->args->argv[j].arg)
break;
assert(j < (int)np->args->argc);
/*
* Accommodate for new-style groff column syntax. Shuffle the
* child nodes, all of which must be TEXT, as arguments for the
* column field. Then, delete the head children.
*/
argv = np->args->argv + j;
i = argv->sz;
argv->sz += mdoc->last->nchild;
argv->value = mandoc_reallocarray(argv->value,
argv->sz, sizeof(char *));
mdoc->last->norm->Bl.ncols = argv->sz;
mdoc->last->norm->Bl.cols = (void *)argv->value;
for (nn = mdoc->last->child; nn; i++) {
argv->value[i] = nn->string;
nn->string = NULL;
nnp = nn;
nn = nn->next;
mdoc_node_delete(NULL, nnp);
}
mdoc->last->nchild = 0;
mdoc->last->child = NULL;
}
static void
post_bl(POST_ARGS)
{
struct mdoc_node *nparent, *nprev; /* of the Bl block */
struct mdoc_node *nblock, *nbody; /* of the Bl */
struct mdoc_node *nchild, *nnext; /* of the Bl body */
nbody = mdoc->last;
switch (nbody->type) {
case MDOC_BLOCK:
post_bl_block(mdoc);
return;
case MDOC_HEAD:
post_bl_head(mdoc);
return;
case MDOC_BODY:
break;
default:
return;
}
bwarn_ge1(mdoc);
nchild = nbody->child;
while (NULL != nchild) {
if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
nchild = nchild->next;
continue;
}
mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
nchild->line, nchild->pos,
mdoc_macronames[nchild->tok]);
/*
* Move the node out of the Bl block.
* First, collect all required node pointers.
*/
nblock = nbody->parent;
nprev = nblock->prev;
nparent = nblock->parent;
nnext = nchild->next;
/*
* Unlink this child.
*/
assert(NULL == nchild->prev);
if (0 == --nbody->nchild) {
nbody->child = NULL;
nbody->last = NULL;
assert(NULL == nnext);
} else {
nbody->child = nnext;
nnext->prev = NULL;
}
/*
* Relink this child.
*/
nchild->parent = nparent;
nchild->prev = nprev;
nchild->next = nblock;
nblock->prev = nchild;
nparent->nchild++;
if (NULL == nprev)
nparent->child = nchild;
else
nprev->next = nchild;
nchild = nnext;
}
}
static void
post_bk(POST_ARGS)
{
hwarn_eq0(mdoc);
bwarn_ge1(mdoc);
}
static void
post_sm(struct mdoc *mdoc)
{
struct mdoc_node *nch;
nch = mdoc->last->child;
if (nch == NULL) {
mdoc->flags ^= MDOC_SMOFF;
return;
}
assert(nch->type == MDOC_TEXT);
if ( ! strcmp(nch->string, "on")) {
mdoc->flags &= ~MDOC_SMOFF;
return;
}
if ( ! strcmp(nch->string, "off")) {
mdoc->flags |= MDOC_SMOFF;
return;
}
mandoc_vmsg(MANDOCERR_SM_BAD,
mdoc->parse, nch->line, nch->pos,
"%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
mdoc_node_relink(mdoc, nch);
return;
}
static void
post_root(POST_ARGS)
{
struct mdoc_node *n;
/* Add missing prologue data. */
if (mdoc->meta.date == NULL)
mdoc->meta.date = mdoc->quick ?
mandoc_strdup("") :
mandoc_normdate(mdoc->parse, NULL, 0, 0);
if (mdoc->meta.title == NULL) {
mandoc_msg(MANDOCERR_DT_NOTITLE,
mdoc->parse, 0, 0, "EOF");
mdoc->meta.title = mandoc_strdup("UNTITLED");
}
if (mdoc->meta.vol == NULL)
mdoc->meta.vol = mandoc_strdup("LOCAL");
if (mdoc->meta.os == NULL) {
mandoc_msg(MANDOCERR_OS_MISSING,
mdoc->parse, 0, 0, NULL);
mdoc->meta.os = mandoc_strdup("");
}
/* Check that we begin with a proper `Sh'. */
n = mdoc->first->child;
while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
n = n->next;
if (n == NULL)
mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
else if (n->tok != MDOC_Sh)
mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
}
static void
post_st(POST_ARGS)
{
struct mdoc_node *n, *nch;
const char *p;
n = mdoc->last;
nch = n->child;
if (NULL == nch) {
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
mdoc_node_delete(mdoc, n);
return;
}
assert(MDOC_TEXT == nch->type);
if (NULL == (p = mdoc_a2st(nch->string))) {
mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
nch->line, nch->pos, "St %s", nch->string);
mdoc_node_delete(mdoc, n);
} else {
free(nch->string);
nch->string = mandoc_strdup(p);
}
}
static void
post_rs(POST_ARGS)
{
struct mdoc_node *nn, *next, *prev;
int i, j;
switch (mdoc->last->type) {
case MDOC_HEAD:
check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
return;
case MDOC_BODY:
if (mdoc->last->child)
break;
check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
return;
default:
return;
}
/*
* The full `Rs' block needs special handling to order the
* sub-elements according to `rsord'. Pick through each element
* and correctly order it. This is an insertion sort.
*/
next = NULL;
for (nn = mdoc->last->child->next; nn; nn = next) {
/* Determine order of `nn'. */
for (i = 0; i < RSORD_MAX; i++)
if (rsord[i] == nn->tok)
break;
if (i == RSORD_MAX) {
mandoc_msg(MANDOCERR_RS_BAD,
mdoc->parse, nn->line, nn->pos,
mdoc_macronames[nn->tok]);
i = -1;
} else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
mdoc->last->norm->Rs.quote_T++;
/*
* Remove `nn' from the chain. This somewhat
* repeats mdoc_node_unlink(), but since we're
* just re-ordering, there's no need for the
* full unlink process.
*/
if (NULL != (next = nn->next))
next->prev = nn->prev;
if (NULL != (prev = nn->prev))
prev->next = nn->next;
nn->prev = nn->next = NULL;
/*
* Scan back until we reach a node that's
* ordered before `nn'.
*/
for ( ; prev ; prev = prev->prev) {
/* Determine order of `prev'. */
for (j = 0; j < RSORD_MAX; j++)
if (rsord[j] == prev->tok)
break;
if (j == RSORD_MAX)
j = -1;
if (j <= i)
break;
}
/*
* Set `nn' back into its correct place in front
* of the `prev' node.
*/
nn->prev = prev;
if (prev) {
if (prev->next)
prev->next->prev = nn;
nn->next = prev->next;
prev->next = nn;
} else {
mdoc->last->child->prev = nn;
nn->next = mdoc->last->child;
mdoc->last->child = nn;
}
}
}
/*
* For some arguments of some macros,
* convert all breakable hyphens into ASCII_HYPH.
*/
static void
post_hyph(POST_ARGS)
{
struct mdoc_node *n, *nch;
char *cp;
n = mdoc->last;
switch (n->type) {
case MDOC_HEAD:
if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
break;
return;
case MDOC_BODY:
if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
break;
return;
case MDOC_ELEM:
break;
default:
return;
}
for (nch = n->child; nch; nch = nch->next) {
if (MDOC_TEXT != nch->type)
continue;
cp = nch->string;
if ('\0' == *cp)
continue;
while ('\0' != *(++cp))
if ('-' == *cp &&
isalpha((unsigned char)cp[-1]) &&
isalpha((unsigned char)cp[1]))
*cp = ASCII_HYPH;
}
}
static void
post_hyphtext(POST_ARGS)
{
ewarn_ge1(mdoc);
post_hyph(mdoc);
}
static void
post_ns(POST_ARGS)
{
if (MDOC_LINE & mdoc->last->flags)
mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
mdoc->last->line, mdoc->last->pos, NULL);
}
static void
post_sh(POST_ARGS)
{
post_ignpar(mdoc);
switch (mdoc->last->type) {
case MDOC_HEAD:
post_sh_head(mdoc);
break;
case MDOC_BODY:
switch (mdoc->lastsec) {
case SEC_NAME:
post_sh_name(mdoc);
break;
case SEC_SEE_ALSO:
post_sh_see_also(mdoc);
break;
case SEC_AUTHORS:
post_sh_authors(mdoc);
break;
default:
break;
}
break;
default:
break;
}
}
static void
post_sh_name(POST_ARGS)
{
struct mdoc_node *n;
/*
* Warn if the NAME section doesn't contain the `Nm' and `Nd'
* macros (can have multiple `Nm' and one `Nd'). Note that the
* children of the BODY declaration can also be "text".
*/
if (NULL == (n = mdoc->last->child)) {
mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
mdoc->last->line, mdoc->last->pos, "empty");
return;
}
for ( ; n && n->next; n = n->next) {
if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
continue;
if (MDOC_TEXT == n->type)
continue;
mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
}
assert(n);
if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
return;
mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
n->line, n->pos, mdoc_macronames[n->tok]);
}
static void
post_sh_see_also(POST_ARGS)
{
const struct mdoc_node *n;
const char *name, *sec;
const char *lastname, *lastsec, *lastpunct;
int cmp;
n = mdoc->last->child;
lastname = lastsec = lastpunct = NULL;
while (n != NULL) {
if (n->tok != MDOC_Xr || n->nchild < 2)
break;
/* Process one .Xr node. */
name = n->child->string;
sec = n->child->next->string;
if (lastsec != NULL) {
if (lastpunct[0] != ',' || lastpunct[1] != '\0')
mandoc_vmsg(MANDOCERR_XR_PUNCT,
mdoc->parse, n->line, n->pos,
"%s before %s(%s)", lastpunct,
name, sec);
cmp = strcmp(lastsec, sec);
if (cmp > 0)
mandoc_vmsg(MANDOCERR_XR_ORDER,
mdoc->parse, n->line, n->pos,
"%s(%s) after %s(%s)", name,
sec, lastname, lastsec);
else if (cmp == 0 &&
strcasecmp(lastname, name) > 0)
mandoc_vmsg(MANDOCERR_XR_ORDER,
mdoc->parse, n->line, n->pos,
"%s after %s", name, lastname);
}
lastname = name;
lastsec = sec;
/* Process the following node. */
n = n->next;
if (n == NULL)
break;
if (n->tok == MDOC_Xr) {
lastpunct = "none";
continue;
}
if (n->type != MDOC_TEXT)
break;
for (name = n->string; *name != '\0'; name++)
if (isalpha((const unsigned char)*name))
return;
lastpunct = n->string;
if (n->next == NULL)
mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
n->line, n->pos, "%s after %s(%s)",
lastpunct, lastname, lastsec);
n = n->next;
}
}
static int
child_an(const struct mdoc_node *n)
{
for (n = n->child; n != NULL; n = n->next)
if ((n->tok == MDOC_An && n->nchild) || child_an(n))
return(1);
return(0);
}
static void
post_sh_authors(POST_ARGS)
{
if ( ! child_an(mdoc->last))
mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
mdoc->last->line, mdoc->last->pos, NULL);
}
static void
post_sh_head(POST_ARGS)
{
struct mdoc_node *n;
const char *goodsec;
char *secname;
enum mdoc_sec sec;
/*
* Process a new section. Sections are either "named" or
* "custom". Custom sections are user-defined, while named ones
* follow a conventional order and may only appear in certain
* manual sections.
*/
secname = NULL;
sec = SEC_CUSTOM;
mdoc_deroff(&secname, mdoc->last);
sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
/* The NAME should be first. */
if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
mdoc->last->line, mdoc->last->pos,
"Sh %s", secname);
/* The SYNOPSIS gets special attention in other areas. */
if (SEC_SYNOPSIS == sec) {
roff_setreg(mdoc->roff, "nS", 1, '=');
mdoc->flags |= MDOC_SYNOPSIS;
} else {
roff_setreg(mdoc->roff, "nS", 0, '=');
mdoc->flags &= ~MDOC_SYNOPSIS;
}
/* Mark our last section. */
mdoc->lastsec = sec;
/*
* Set the section attribute for the current HEAD, for its
* parent BLOCK, and for the HEAD children; the latter can
* only be TEXT nodes, so no recursion is needed.
* For other blocks and elements, including .Sh BODY, this is
* done when allocating the node data structures, but for .Sh
* BLOCK and HEAD, the section is still unknown at that time.
*/
mdoc->last->parent->sec = sec;
mdoc->last->sec = sec;
for (n = mdoc->last->child; n; n = n->next)
n->sec = sec;
/* We don't care about custom sections after this. */
if (SEC_CUSTOM == sec) {
free(secname);
return;
}
/*
* Check whether our non-custom section is being repeated or is
* out of order.
*/
if (sec == mdoc->lastnamed)
mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
mdoc->last->line, mdoc->last->pos,
"Sh %s", secname);
if (sec < mdoc->lastnamed)
mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
mdoc->last->line, mdoc->last->pos,
"Sh %s", secname);
/* Mark the last named section. */
mdoc->lastnamed = sec;
/* Check particular section/manual conventions. */
if (mdoc->meta.msec == NULL) {
free(secname);
return;
}
goodsec = NULL;
switch (sec) {
case SEC_ERRORS:
if (*mdoc->meta.msec == '4')
break;
goodsec = "2, 3, 4, 9";
/* FALLTHROUGH */
case SEC_RETURN_VALUES:
/* FALLTHROUGH */
case SEC_LIBRARY:
if (*mdoc->meta.msec == '2')
break;
if (*mdoc->meta.msec == '3')
break;
if (NULL == goodsec)
goodsec = "2, 3, 9";
/* FALLTHROUGH */
case SEC_CONTEXT:
if (*mdoc->meta.msec == '9')
break;
if (NULL == goodsec)
goodsec = "9";
mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
mdoc->last->line, mdoc->last->pos,
"Sh %s for %s only", secname, goodsec);
break;
default:
break;
}
free(secname);
}
static void
post_ignpar(POST_ARGS)
{
struct mdoc_node *np;
check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0);
post_hyph(mdoc);
if (MDOC_BODY != mdoc->last->type)
return;
if (NULL != (np = mdoc->last->child))
if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
mandoc_vmsg(MANDOCERR_PAR_SKIP,
mdoc->parse, np->line, np->pos,
"%s after %s", mdoc_macronames[np->tok],
mdoc_macronames[mdoc->last->tok]);
mdoc_node_delete(mdoc, np);
}
if (NULL != (np = mdoc->last->last))
if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
np->line, np->pos, "%s at the end of %s",
mdoc_macronames[np->tok],
mdoc_macronames[mdoc->last->tok]);
mdoc_node_delete(mdoc, np);
}
}
static void
pre_par(PRE_ARGS)
{
if (NULL == mdoc->last)
return;
if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
return;
/*
* Don't allow prior `Lp' or `Pp' prior to a paragraph-type
* block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
*/
if (MDOC_Pp != mdoc->last->tok &&
MDOC_Lp != mdoc->last->tok &&
MDOC_br != mdoc->last->tok)
return;
if (MDOC_Bl == n->tok && n->norm->Bl.comp)
return;
if (MDOC_Bd == n->tok && n->norm->Bd.comp)
return;
if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
return;
mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
mdoc->last->line, mdoc->last->pos,
"%s before %s", mdoc_macronames[mdoc->last->tok],
mdoc_macronames[n->tok]);
mdoc_node_delete(mdoc, mdoc->last);
}
static void
post_par(POST_ARGS)
{
struct mdoc_node *np;
if (mdoc->last->tok == MDOC_sp)
check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
else
check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
if (MDOC_ELEM != mdoc->last->type &&
MDOC_BLOCK != mdoc->last->type)
return;
if (NULL == (np = mdoc->last->prev)) {
np = mdoc->last->parent;
if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
return;
} else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
(MDOC_br != mdoc->last->tok ||
(MDOC_sp != np->tok && MDOC_br != np->tok)))
return;
mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
mdoc->last->line, mdoc->last->pos,
"%s after %s", mdoc_macronames[mdoc->last->tok],
mdoc_macronames[np->tok]);
mdoc_node_delete(mdoc, mdoc->last);
}
static void
pre_literal(PRE_ARGS)
{
pre_display(mdoc, n);
if (MDOC_BODY != n->type)
return;
/*
* The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
* -unfilled' macros set MDOC_LITERAL on entrance to the body.
*/
switch (n->tok) {
case MDOC_Dl:
mdoc->flags |= MDOC_LITERAL;
break;
case MDOC_Bd:
if (DISP_literal == n->norm->Bd.type)
mdoc->flags |= MDOC_LITERAL;
if (DISP_unfilled == n->norm->Bd.type)
mdoc->flags |= MDOC_LITERAL;
break;
default:
abort();
/* NOTREACHED */
}
}
static void
post_dd(POST_ARGS)
{
struct mdoc_node *n;
char *datestr;
if (mdoc->meta.date)
free(mdoc->meta.date);
n = mdoc->last;
if (NULL == n->child || '\0' == n->child->string[0]) {
mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
goto out;
}
datestr = NULL;
mdoc_deroff(&datestr, n);
if (mdoc->quick)
mdoc->meta.date = datestr;
else {
mdoc->meta.date = mandoc_normdate(mdoc->parse,
datestr, n->line, n->pos);
free(datestr);
}
out:
mdoc_node_delete(mdoc, n);
}
static void
post_dt(POST_ARGS)
{
struct mdoc_node *nn, *n;
const char *cp;
char *p;
n = mdoc->last;
free(mdoc->meta.title);
free(mdoc->meta.msec);
free(mdoc->meta.vol);
free(mdoc->meta.arch);
mdoc->meta.title = NULL;
mdoc->meta.msec = NULL;
mdoc->meta.vol = NULL;
mdoc->meta.arch = NULL;
/* First check that all characters are uppercase. */
if (NULL != (nn = n->child))
for (p = nn->string; *p; p++) {
if (toupper((unsigned char)*p) == *p)
continue;
mandoc_vmsg(MANDOCERR_TITLE_CASE,
mdoc->parse, nn->line,
nn->pos + (p - nn->string),
"Dt %s", nn->string);
break;
}
/* No argument: msec and arch remain NULL. */
if (NULL == (nn = n->child)) {
mandoc_msg(MANDOCERR_DT_NOTITLE,
mdoc->parse, n->line, n->pos, "Dt");
mdoc->meta.title = mandoc_strdup("UNTITLED");
mdoc->meta.vol = mandoc_strdup("LOCAL");
goto out;
}
/* One argument: msec and arch remain NULL. */
mdoc->meta.title = mandoc_strdup(
'\0' == nn->string[0] ? "UNTITLED" : nn->string);
if (NULL == (nn = nn->next)) {
mandoc_vmsg(MANDOCERR_MSEC_MISSING,
mdoc->parse, n->line, n->pos,
"Dt %s", mdoc->meta.title);
mdoc->meta.vol = mandoc_strdup("LOCAL");
goto out;
}
/* Handles: `.Dt TITLE SEC'
* title = TITLE,
* volume = SEC is msec ? format(msec) : SEC,
* msec = SEC is msec ? atoi(msec) : 0,
* arch = NULL
*/
cp = mandoc_a2msec(nn->string);
if (cp) {
mdoc->meta.vol = mandoc_strdup(cp);
mdoc->meta.msec = mandoc_strdup(nn->string);
} else {
mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
nn->line, nn->pos, "Dt ... %s", nn->string);
mdoc->meta.vol = mandoc_strdup(nn->string);
mdoc->meta.msec = mandoc_strdup(nn->string);
}
/* Handle an optional architecture */
if ((nn = nn->next) != NULL) {
for (p = nn->string; *p; p++)
*p = tolower((unsigned char)*p);
mdoc->meta.arch = mandoc_strdup(nn->string);
}
/* Ignore any subsequent parameters... */
/* FIXME: warn about subsequent parameters. */
out:
mdoc_node_delete(mdoc, n);
}
static void
post_bx(POST_ARGS)
{
struct mdoc_node *n;
/*
* Make `Bx's second argument always start with an uppercase
* letter. Groff checks if it's an "accepted" term, but we just
* uppercase blindly.
*/
n = mdoc->last->child;
if (n && NULL != (n = n->next))
*n->string = (char)toupper((unsigned char)*n->string);
}
static void
post_os(POST_ARGS)
{
#ifndef OSNAME
struct utsname utsname;
static char *defbuf;
#endif
struct mdoc_node *n;
n = mdoc->last;
/*
* Set the operating system by way of the `Os' macro.
* The order of precedence is:
* 1. the argument of the `Os' macro, unless empty
* 2. the -Ios=foo command line argument, if provided
* 3. -DOSNAME="\"foo\"", if provided during compilation
* 4. "sysname release" from uname(3)
*/
free(mdoc->meta.os);
mdoc->meta.os = NULL;
mdoc_deroff(&mdoc->meta.os, n);
if (mdoc->meta.os)
goto out;
if (mdoc->defos) {
mdoc->meta.os = mandoc_strdup(mdoc->defos);
goto out;
}
#ifdef OSNAME
mdoc->meta.os = mandoc_strdup(OSNAME);
#else /*!OSNAME */
if (NULL == defbuf) {
if (-1 == uname(&utsname)) {
mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
n->line, n->pos, "Os");
defbuf = mandoc_strdup("UNKNOWN");
} else
mandoc_asprintf(&defbuf, "%s %s",
utsname.sysname, utsname.release);
}
mdoc->meta.os = mandoc_strdup(defbuf);
#endif /*!OSNAME*/
out:
mdoc_node_delete(mdoc, n);
}
/*
* If no argument is provided,
* fill in the name of the current manual page.
*/
static void
post_ex(POST_ARGS)
{
struct mdoc_node *n;
n = mdoc->last;
if (n->child)
return;
if (mdoc->meta.name == NULL) {
mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
n->line, n->pos, "Ex");
return;
}
mdoc->next = MDOC_NEXT_CHILD;
mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
mdoc->last = n;
}
static enum mdoc_sec
a2sec(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);
}
static size_t
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);
}
Index: vendor/mdocml/dist/msec.c
===================================================================
--- vendor/mdocml/dist/msec.c (revision 276215)
+++ vendor/mdocml/dist/msec.c (revision 276216)
@@ -1,36 +1,35 @@
-/* $Id: msec.c,v 1.12 2014/08/10 23:54:41 schwarze Exp $ */
+/* $Id: msec.c,v 1.13 2014/12/01 08:05:52 schwarze Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
-#include "mandoc.h"
#include "libmandoc.h"
#define LINE(x, y) \
if (0 == strcmp(p, x)) return(y);
const char *
mandoc_a2msec(const char *p)
{
#include "msec.in"
return(NULL);
}
Index: vendor/mdocml/dist/out.c
===================================================================
--- vendor/mdocml/dist/out.c (revision 276215)
+++ vendor/mdocml/dist/out.c (revision 276216)
@@ -1,345 +1,345 @@
-/* $Id: out.c,v 1.53 2014/10/14 18:18:05 schwarze Exp $ */
+/* $Id: out.c,v 1.54 2014/12/04 02:05:42 schwarze Exp $ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons
* Copyright (c) 2011, 2014 Ingo Schwarze
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include