diff options
-rw-r--r-- | Makefile | 19 | ||||
-rw-r--r-- | libman.h | 1 | ||||
-rw-r--r-- | man.7 | 1 | ||||
-rw-r--r-- | man.c | 60 | ||||
-rw-r--r-- | man.h | 3 | ||||
-rw-r--r-- | man_action.c | 14 | ||||
-rw-r--r-- | man_macro.c | 42 | ||||
-rw-r--r-- | man_term.c | 369 | ||||
-rw-r--r-- | man_validate.c | 5 | ||||
-rw-r--r-- | mdoc_term.c (renamed from term.c) | 387 | ||||
-rw-r--r-- | term.h | 37 | ||||
-rw-r--r-- | terminal.c | 319 |
12 files changed, 852 insertions, 405 deletions
@@ -34,9 +34,12 @@ MANOBJS = man_macro.o man.o man_hash.o man_validate.o \ MANSRCS = man_macro.c man.c man_hash.c man_validate.c \ man_action.c -MAINLNS = main.ln term.ln ascii.ln terminal.ln tree.ln compat.ln -MAINOBJS = main.o term.o ascii.o terminal.o tree.o compat.o -MAINSRCS = main.c term.c ascii.c terminal.c tree.c compat.c +MAINLNS = main.ln mdoc_term.ln ascii.ln terminal.ln tree.ln \ + compat.ln man_term.ln +MAINOBJS = main.o mdoc_term.o ascii.o terminal.o tree.o compat.o \ + man_term.o +MAINSRCS = main.c mdoc_term.c ascii.c terminal.c tree.c compat.c \ + man_term.c LLNS = llib-llibmdoc.ln llib-llibman.ln llib-lmandoc.ln LNS = $(MAINLNS) $(MDOCLNS) $(MANLNS) @@ -129,8 +132,8 @@ st.o: st.c st.in libmdoc.h mdoc_macro.ln: mdoc_macro.c libmdoc.h mdoc_macro.o: mdoc_macro.c libmdoc.h -term.ln: term.c term.h -term.o: term.c term.h +mdoc_term.ln: mdoc_term.c term.h mdoc.h +mdoc_term.o: mdoc_term.c term.h mdoc.h strings.ln: strings.c libmdoc.h strings.o: strings.c libmdoc.h @@ -150,8 +153,8 @@ man.o: man.c libman.h main.ln: main.c mdoc.h main.o: main.c mdoc.h -terminal.ln: terminal.c term.h -terminal.o: terminal.c term.h +terminal.ln: terminal.c term.h man.h mdoc.h +terminal.o: terminal.c term.h man.h mdoc.h xstd.ln: xstd.c libmdoc.h xstd.o: xstd.c libmdoc.h @@ -170,8 +173,6 @@ mdoc_action.o: mdoc_action.c libmdoc.h libmdoc.h: mdoc.h -term.h: mdoc.h - mdocml-nport-$(VERSION).tar.gz: mdocml-$(VERSION).tar.gz Makefile.netbsd DESCR mkdir -p .dist/mdocml/ sed -e "s!@VERSION@!$(VERSION)!" Makefile.netbsd > \ @@ -33,6 +33,7 @@ struct man { int pflags; int flags; #define MAN_HALT (1 << 0) +#define MAN_NLINE (1 << 1) enum man_next next; struct man_node *last; struct man_node *first; @@ -136,6 +136,7 @@ macros, arranged alphabetically, with the number of arguments. .It \&.B Ta n .It \&.I Ta n .It \&.IR Ta n +.It \&.RI Ta n .El .\" SECTION .Sh SEE ALSO @@ -31,7 +31,8 @@ const char *const __man_macronames[MAN_MAX] = { "TP", "LP", "PP", "P", "IP", "HP", "SM", "SB", "BI", "IB", "BR", "RB", - "R", "B", "I", "IR" + "R", "B", "I", "IR", + "RI" }; const char * const *man_macronames = __man_macronames; @@ -268,6 +269,25 @@ man_ptext(struct man *m, int line, char *buf) if ( ! man_word_alloc(m, line, 0, buf)) return(0); m->next = MAN_NEXT_SIBLING; + + /* + * If this is one of the zany NLINE macros that consumes the + * next line of input as being influenced, then close out the + * existing macro "scope" and continue processing. + */ + + if ( ! (MAN_NLINE & m->flags)) + return(1); + + m->flags &= ~MAN_NLINE; + m->last = m->last->parent; + + assert(MAN_ROOT != m->last->type); + if ( ! man_valid_post(m)) + return(0); + if ( ! man_action_post(m)) + return(0); + return(1); } @@ -275,13 +295,17 @@ man_ptext(struct man *m, int line, char *buf) int man_pmacro(struct man *m, int ln, char *buf) { - int i, j, c, ppos; + int i, j, c, ppos, fl; char mac[5]; + struct man_node *n; /* Comments and empties are quickly ignored. */ + n = m->last; + fl = MAN_NLINE & m->flags; + if (0 == buf[1]) - return(1); + goto out; i = 1; @@ -290,14 +314,14 @@ man_pmacro(struct man *m, int ln, char *buf) while (buf[i] && ' ' == buf[i]) i++; if (0 == buf[i]) - return(1); + goto out; } ppos = i; if (buf[i] && '\\' == buf[i]) if (buf[i + 1] && '\"' == buf[i + 1]) - return(1); + goto out; /* Copy the first word into a nil-terminated buffer. */ @@ -319,7 +343,7 @@ man_pmacro(struct man *m, int ln, char *buf) if ( ! man_vwarn(m, ln, ppos, "ill-formed macro: %s", mac)) goto err; - return(1); + goto out; } if (MAN_MAX == (c = man_hash_find(m->htab, mac))) { @@ -331,7 +355,7 @@ man_pmacro(struct man *m, int ln, char *buf) if ( ! man_vwarn(m, ln, ppos, "unknown macro: %s", mac)) goto err; - return(1); + goto out; } /* The macro is sane. Jump to the next word. */ @@ -344,6 +368,28 @@ man_pmacro(struct man *m, int ln, char *buf) if ( ! man_macro(m, c, ln, ppos, &i, buf)) goto err; +out: + if (fl) { + /* + * A NLINE macro has been immediately followed with + * another. Close out the preceeding macro's scope, and + * continue. + */ + assert(MAN_ROOT != m->last->type); + assert(m->last->parent); + assert(MAN_ROOT != m->last->parent->type); + + if (n != m->last) + m->last = m->last->parent; + + if ( ! man_valid_post(m)) + return(0); + if ( ! man_action_post(m)) + return(0); + m->next = MAN_NEXT_SIBLING; + m->flags &= ~MAN_NLINE; + } + return(1); err: /* Error out. */ @@ -41,7 +41,8 @@ #define MAN_B 17 #define MAN_I 18 #define MAN_IR 19 -#define MAN_MAX 20 +#define MAN_RI 20 +#define MAN_MAX 21 enum man_type { MAN_TEXT, diff --git a/man_action.c b/man_action.c index e159baa5..631a1d66 100644 --- a/man_action.c +++ b/man_action.c @@ -37,7 +37,7 @@ struct actions { static int post_TH(struct man *); -static time_t man_atotime(const char *); +static time_t man_atotime(const char *); const struct actions man_actions[MAN_MAX] = { { NULL }, /* __ */ @@ -60,6 +60,7 @@ const struct actions man_actions[MAN_MAX] = { { NULL }, /* B */ { NULL }, /* I */ { NULL }, /* IR */ + { NULL }, /* RI */ }; @@ -125,12 +126,9 @@ post_TH(struct man *m) /* TITLE MSEC ->DATE<- SOURCE VOL */ - if (NULL == (n = n->next)) { + if (NULL == (n = n->next)) m->meta.date = time(NULL); - return(1); - } - - if (0 == (m->meta.date = man_atotime(n->string))) { + else if (0 == (m->meta.date = man_atotime(n->string))) { if ( ! man_vwarn(m, n->line, n->pos, "invalid date")) return(0); m->meta.date = time(NULL); @@ -138,13 +136,13 @@ post_TH(struct man *m) /* TITLE MSEC DATE ->SOURCE<- VOL */ - if ((n = n->next)) + if (n && (n = n->next)) if (NULL == (m->meta.source = strdup(n->string))) return(man_verr(m, n->line, n->pos, "malloc")); /* TITLE MSEC DATE SOURCE ->VOL<- */ - if ((n = n->next)) + if (n && (n = n->next)) if (NULL == (m->meta.vol = strdup(n->string))) return(man_verr(m, n->line, n->pos, "malloc")); diff --git a/man_macro.c b/man_macro.c index 518b1e8e..2b389b14 100644 --- a/man_macro.c +++ b/man_macro.c @@ -24,9 +24,35 @@ #include "libman.h" +#define FL_NLINE (1 << 0) +#define FL_TLINE (1 << 1) + static int man_args(struct man *, int, int *, char *, char **); +static int man_flags[MAN_MAX] = { + 0, /* __ */ + 0, /* TH */ + 0, /* SH */ + 0, /* SS */ + FL_TLINE, /* TP */ + 0, /* LP */ + 0, /* PP */ + 0, /* P */ + 0, /* IP */ + 0, /* HP */ + FL_NLINE, /* SM */ + FL_NLINE, /* SB */ + FL_NLINE, /* BI */ + FL_NLINE, /* IB */ + FL_NLINE, /* BR */ + FL_NLINE, /* RB */ + FL_NLINE, /* R */ + FL_NLINE, /* B */ + FL_NLINE, /* I */ + FL_NLINE, /* IR */ + FL_NLINE, /* RI */ +}; int man_macro(struct man *man, int tok, int line, @@ -55,6 +81,22 @@ man_macro(struct man *man, int tok, int line, man->next = MAN_NEXT_SIBLING; } + if (n == man->last && (FL_NLINE & man_flags[tok])) { + if (MAN_NLINE & man->flags) + return(man_verr(man, line, ppos, + "next-line scope already open")); + man->flags |= MAN_NLINE; + return(1); + } + + if (FL_TLINE & man_flags[tok]) { + if (MAN_NLINE & man->flags) + return(man_verr(man, line, ppos, + "next-line scope already open")); + man->flags |= MAN_NLINE; + return(1); + } + /* * Note that when TH is pruned, we'll be back at the root, so * make sure that we don't clobber as its sibling. diff --git a/man_term.c b/man_term.c new file mode 100644 index 00000000..0a8e5fce --- /dev/null +++ b/man_term.c @@ -0,0 +1,369 @@ +/* $Id$ */ +/* + * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include <assert.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "term.h" +#include "man.h" + +#define DECL_ARGS struct termp *p, \ + const struct man_node *n, \ + const struct man_meta *m + +struct termact { + int (*pre)(DECL_ARGS); + void (*post)(DECL_ARGS); +}; + +static int pre_B(DECL_ARGS); +static int pre_I(DECL_ARGS); +static int pre_PP(DECL_ARGS); +static int pre_SH(DECL_ARGS); +static int pre_SS(DECL_ARGS); +static int pre_TP(DECL_ARGS); + +static void post_B(DECL_ARGS); +static void post_I(DECL_ARGS); +static void post_SH(DECL_ARGS); +static void post_SS(DECL_ARGS); + +static const struct termact termacts[MAN_MAX] = { + { NULL, NULL }, /* __ */ + { NULL, NULL }, /* TH */ + { pre_SH, post_SH }, /* SH */ + { pre_SS, post_SS }, /* SS */ + { pre_TP, NULL }, /* TP */ + { pre_PP, NULL }, /* LP */ + { pre_PP, NULL }, /* PP */ + { pre_PP, NULL }, /* P */ + { NULL, NULL }, /* IP */ + { pre_PP, NULL }, /* HP */ /* XXX */ + { NULL, NULL }, /* SM */ + { pre_B, post_B }, /* SB */ + { NULL, NULL }, /* BI */ + { NULL, NULL }, /* IB */ + { NULL, NULL }, /* BR */ + { NULL, NULL }, /* RB */ + { NULL, NULL }, /* R */ + { pre_B, post_B }, /* B */ + { pre_I, post_I }, /* I */ + { NULL, NULL }, /* IR */ + { NULL, NULL }, /* RI */ +}; + +static void print_head(struct termp *, + const struct man_meta *); +static void print_body(DECL_ARGS); +static void print_node(DECL_ARGS); +static void print_foot(struct termp *, + const struct man_meta *); + + +int +man_run(struct termp *p, const struct man *m) +{ + + print_head(p, man_meta(m)); + p->flags |= TERMP_NOSPACE; + print_body(p, man_node(m), man_meta(m)); + print_foot(p, man_meta(m)); + + return(1); +} + + +static int +pre_I(DECL_ARGS) +{ + + p->flags |= TERMP_UNDER; + return(1); +} + + +static void +post_I(DECL_ARGS) +{ + + p->flags &= ~TERMP_UNDER; +} + + +static int +pre_B(DECL_ARGS) +{ + + p->flags |= TERMP_BOLD; + return(1); +} + + +static void +post_B(DECL_ARGS) +{ + + p->flags &= ~TERMP_BOLD; +} + + +static int +pre_PP(DECL_ARGS) +{ + + term_vspace(p); + p->offset = INDENT; + return(0); +} + + +static int +pre_TP(DECL_ARGS) +{ + const struct man_node *nn; + size_t offs; + + term_vspace(p); + p->offset = INDENT; + + if (NULL == (nn = n->child)) + return(1); + + if (nn->line == n->line) { + if (MAN_TEXT != nn->type) + errx(1, "expected text line argument"); + offs = atoi(nn->string); + nn = nn->next; + } else + offs = INDENT; + + for ( ; nn; nn = nn->next) + print_node(p, nn, m); + + term_flushln(p); + p->flags |= TERMP_NOSPACE; + p->offset += offs; + return(0); +} + + +static int +pre_SS(DECL_ARGS) +{ + + term_vspace(p); + p->flags |= TERMP_BOLD; + return(1); +} + + +static void +post_SS(DECL_ARGS) +{ + + term_flushln(p); + p->flags &= ~TERMP_BOLD; + p->flags |= TERMP_NOSPACE; +} + + +static int +pre_SH(DECL_ARGS) +{ + + term_vspace(p); + p->offset = 0; + p->flags |= TERMP_BOLD; + return(1); +} + + +static void +post_SH(DECL_ARGS) +{ + + term_flushln(p); + p->offset = INDENT; + p->flags &= ~TERMP_BOLD; + p->flags |= TERMP_NOSPACE; +} + + +static void +print_node(DECL_ARGS) +{ + int c; + + c = 1; + + switch (n->type) { + case(MAN_ELEM): + if (termacts[n->tok].pre) + c = (*termacts[n->tok].pre)(p, n, m); + break; + case(MAN_TEXT): + if (*n->string) { + term_word(p, n->string); + break; + } + term_vspace(p); + break; + default: + break; + } + + if (c && n->child) + print_body(p, n->child, m); + + switch (n->type) { + case (MAN_ELEM): + if (termacts[n->tok].post) + (*termacts[n->tok].post)(p, n, m); + break; + default: + break; + } +} + + +static void +print_body(DECL_ARGS) +{ + print_node(p, n, m); + if ( ! n->next) + return; + print_body(p, n->next, m); +} + + +static void +print_foot(struct termp *p, const struct man_meta *meta) +{ + struct tm *tm; + char *buf; + + if (NULL == (buf = malloc(p->rmargin))) + err(1, "malloc"); + + tm = localtime(&meta->date); + +#ifdef __OpenBSD__ + if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm)) +#else + if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm)) +#endif + err(1, "strftime"); + + /* + * This is /slightly/ different from regular groff output + * because we don't have page numbers. Print the following: + * + * OS MDOCDATE + */ + + term_vspace(p); + + p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; + p->rmargin = p->maxrmargin - strlen(buf); + p->offset = 0; + + if (meta->source) + term_word(p, meta->source); + if (meta->source) + term_word(p, ""); + term_flushln(p); + + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->offset = p->rmargin; + p->rmargin = p->maxrmargin; + p->flags &= ~TERMP_NOBREAK; + + term_word(p, buf); + term_flushln(p); + + free(buf); +} + + +static void +print_head(struct termp *p, const struct man_meta *meta) +{ + char *buf, *title; + + p->rmargin = p->maxrmargin; + p->offset = 0; + + if (NULL == (buf = malloc(p->rmargin))) + err(1, "malloc"); + if (NULL == (title = malloc(p->rmargin))) + err(1, "malloc"); + + /* + * 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. + */ + + if (meta->vol) + (void)strlcpy(buf, meta->vol, p->rmargin); + else + *buf = 0; + + (void)snprintf(title, p->rmargin, "%s(%d)", + meta->title, meta->msec); + + p->offset = 0; + p->rmargin = (p->maxrmargin - strlen(buf)) / 2; + p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; + + term_word(p, title); + term_flushln(p); + + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->offset = p->rmargin; + p->rmargin = p->maxrmargin - strlen(title); + + term_word(p, buf); + term_flushln(p); + + p->offset = p->rmargin; + p->rmargin = p->maxrmargin; + p->flags &= ~TERMP_NOBREAK; + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + + term_word(p, title); + term_flushln(p); + + p->rmargin = p->maxrmargin; + p->offset = 0; + p->flags &= ~TERMP_NOSPACE; + + free(title); + free(buf); +} + diff --git a/man_validate.c b/man_validate.c index 495e4329..fb7dd295 100644 --- a/man_validate.c +++ b/man_validate.c @@ -54,7 +54,7 @@ static const struct man_valid man_valids[MAN_MAX] = { { posts_ge2_le5 }, /* TH */ { posts_ge1 }, /* SH */ { posts_ge1 }, /* SS */ - { posts_le1 }, /* TP */ + { NULL }, /* TP */ { posts_eq0 }, /* LP */ { posts_eq0 }, /* PP */ { posts_eq0 }, /* P */ @@ -70,6 +70,7 @@ static const struct man_valid man_valids[MAN_MAX] = { { NULL }, /* B */ { NULL }, /* I */ { NULL }, /* IR */ + { NULL }, /* RI */ }; @@ -119,7 +120,7 @@ check_##name(POSTARGS) \ int c; \ if ((c = count(n->child)) ineq (x)) \ return(1); \ - return(man_vwarn(m, n->line, n->pos, \ + return(man_verr(m, n->line, n->pos, \ "expected line arguments %s %d, have %d", \ #ineq, (x), c)); \ } @@ -26,13 +26,10 @@ #include <string.h> #include "term.h" - -/* - * Performs actions on nodes of the abstract syntax tree. Both pre- and - * post-fix operations are defined here. - */ +#include "mdoc.h" /* FIXME: macro arguments can be escaped. */ +/* FIXME: support more offset/width tokens. */ #define TTYPE_PROG 0 #define TTYPE_CMD_FLAG 1 @@ -59,14 +56,6 @@ #define TTYPE_LIST 22 #define TTYPE_NMAX 23 -/* - * These define "styles" for element types, like command arguments or - * executable names. This is useful when multiple macros must decorate - * the same thing (like .Ex -std cmd and .Nm cmd). - */ - -/* TODO: abstract this into mdocterm.c. */ - const int ttypes[TTYPE_NMAX] = { TERMP_BOLD, /* TTYPE_PROG */ TERMP_BOLD, /* TTYPE_CMD_FLAG */ @@ -93,25 +82,28 @@ const int ttypes[TTYPE_NMAX] = { TERMP_BOLD /* TTYPE_LIST */ }; -static int arg_hasattr(int, const struct mdoc_node *); -static int arg_getattrs(const int *, int *, size_t, - const struct mdoc_node *); -static int arg_getattr(int, const struct mdoc_node *); -static size_t arg_offset(const struct mdoc_argv *); -static size_t arg_width(const struct mdoc_argv *, int); -static int arg_listtype(const struct mdoc_node *); -static int fmt_block_vspace(struct termp *, - const struct mdoc_node *, - const struct mdoc_node *); +/* XXX - clean this up. */ -/* - * What follows describes prefix and postfix operations for the abstract - * syntax tree descent. - */ +struct termpair { + struct termpair *ppair; + int type; +#define TERMPAIR_FLAG (1 << 0) + int flag; + size_t offset; + size_t rmargin; + int count; +}; + +#define TERMPAIR_SETFLAG(termp, p, fl) \ + do { \ + assert(! (TERMPAIR_FLAG & (p)->type)); \ + (termp)->flags |= (fl); \ + (p)->flag = (fl); \ + (p)->type |= TERMPAIR_FLAG; \ + } while ( /* CONSTCOND */ 0) #define DECL_ARGS \ - struct termp *p, \ - struct termpair *pair, \ + struct termp *p, struct termpair *pair, \ const struct mdoc_meta *meta, \ const struct mdoc_node *node @@ -187,7 +179,12 @@ DECL_POST(termp___); DECL_POST(termp_bl); DECL_POST(termp_bx); -const struct termact __termacts[MDOC_MAX] = { +struct termact { + int (*pre)(DECL_ARGS); + void (*post)(DECL_ARGS); +}; + +static const struct termact termacts[MDOC_MAX] = { { NULL, NULL }, /* \" */ { NULL, NULL }, /* Dd */ { NULL, NULL }, /* Dt */ @@ -309,7 +306,331 @@ const struct termact __termacts[MDOC_MAX] = { { NULL, NULL }, /* %Q */ }; -const struct termact *termacts = __termacts; +static int arg_hasattr(int, const struct mdoc_node *); +static int arg_getattrs(const int *, int *, size_t, + const struct mdoc_node *); +static int arg_getattr(int, const struct mdoc_node *); +static size_t arg_offset(const struct mdoc_argv *); +static size_t arg_width(const struct mdoc_argv *, int); +static int arg_listtype(const struct mdoc_node *); +static int fmt_block_vspace(struct termp *, + const struct mdoc_node *, + const struct mdoc_node *); +static int print_node(DECL_ARGS); +static int print_head(struct termp *, + const struct mdoc_meta *); +static int print_body(DECL_ARGS); +static int print_foot(struct termp *, + const struct mdoc_meta *); +static void sanity(const struct mdoc_node *); + + +int +mdoc_run(struct termp *p, const struct mdoc *m) +{ + + if ( ! print_head(p, mdoc_meta(m))) + return(0); + if ( ! print_body(p, NULL, mdoc_meta(m), mdoc_node(m))) + return(0); + return(print_foot(p, mdoc_meta(m))); +} + + +static int +print_body(DECL_ARGS) +{ + + if ( ! print_node(p, pair, meta, node)) + return(0); + if ( ! node->next) + return(1); + return(print_body(p, pair, meta, node->next)); +} + + +static int +print_node(DECL_ARGS) +{ + int dochild; + struct termpair npair; + + /* Some quick sanity-checking. */ + + sanity(node); + + /* Pre-processing. */ + + dochild = 1; + npair.ppair = pair; + npair.type = 0; + npair.offset = npair.rmargin = 0; + npair.flag = 0; + npair.count = 0; + + if (MDOC_TEXT != node->type) { + if (termacts[node->tok].pre) + if ( ! (*termacts[node->tok].pre)(p, &npair, meta, node)) + dochild = 0; + } else /* MDOC_TEXT == node->type */ + term_word(p, node->string); + + /* Children. */ + + if (TERMPAIR_FLAG & npair.type) + p->flags |= npair.flag; + + if (dochild && node->child) + print_body(p, &npair, meta, node->child); + + if (TERMPAIR_FLAG & npair.type) + p->flags &= ~npair.flag; + + /* Post-processing. */ + + if (MDOC_TEXT != node->type) + if (termacts[node->tok].post) + (*termacts[node->tok].post)(p, &npair, meta, node); + + return(1); +} + + +static int +print_foot(struct termp *p, const struct mdoc_meta *meta) +{ + struct tm *tm; + char *buf, *os; + + if (NULL == (buf = malloc(p->rmargin))) + err(1, "malloc"); + if (NULL == (os = malloc(p->rmargin))) + err(1, "malloc"); + + tm = localtime(&meta->date); + +#ifdef __OpenBSD__ + if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm)) +#else + if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm)) +#endif + err(1, "strftime"); + + (void)strlcpy(os, meta->os, p->rmargin); + + /* + * This is /slightly/ different from regular groff output + * because we don't have page numbers. Print the following: + * + * OS MDOCDATE + */ + + term_vspace(p); + + p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; + p->rmargin = p->maxrmargin - strlen(buf); + p->offset = 0; + + term_word(p, os); + term_flushln(p); + + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->offset = p->rmargin; + p->rmargin = p->maxrmargin; + p->flags &= ~TERMP_NOBREAK; + + term_word(p, buf); + term_flushln(p); + + free(buf); + free(os); + + return(1); +} + + +static int +print_head(struct termp *p, const struct mdoc_meta *meta) +{ + char *buf, *title; + + p->rmargin = p->maxrmargin; + p->offset = 0; + + if (NULL == (buf = malloc(p->rmargin))) + err(1, "malloc"); + if (NULL == (title = malloc(p->rmargin))) + err(1, "malloc"); + + /* + * 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); + (void)strlcpy(buf, meta->vol, p->rmargin); + + if (meta->arch) { + (void)strlcat(buf, " (", p->rmargin); + (void)strlcat(buf, meta->arch, p->rmargin); + (void)strlcat(buf, ")", p->rmargin); + } + + (void)snprintf(title, p->rmargin, "%s(%d)", + meta->title, meta->msec); + + p->offset = 0; + p->rmargin = (p->maxrmargin - strlen(buf)) / 2; + p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; + + term_word(p, title); + term_flushln(p); + + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->offset = p->rmargin; + p->rmargin = p->maxrmargin - strlen(title); + + term_word(p, buf); + term_flushln(p); + + p->offset = p->rmargin; + p->rmargin = p->maxrmargin; + p->flags &= ~TERMP_NOBREAK; + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + + term_word(p, title); + term_flushln(p); + + p->rmargin = p->maxrmargin; + p->offset = 0; + p->flags &= ~TERMP_NOSPACE; + + free(title); + free(buf); + + return(1); +} + + +static void +sanity(const struct mdoc_node *n) +{ + char *p; + + p = "regular form violated"; + + switch (n->type) { + case (MDOC_TEXT): + if (n->child) + errx(1, p); + if (NULL == n->parent) + errx(1, p); + if (NULL == n->string) + errx(1, p); + switch (n->parent->type) { + case (MDOC_TEXT): + /* FALLTHROUGH */ + case (MDOC_ROOT): + errx(1, p); + /* NOTREACHED */ + default: + break; + } + break; + case (MDOC_ELEM): + if (NULL == n->parent) + errx(1, p); + switch (n->parent->type) { + case (MDOC_TAIL): + /* FALLTHROUGH */ + case (MDOC_BODY): + /* FALLTHROUGH */ + case (MDOC_HEAD): + break; + default: + errx(1, p); + /* NOTREACHED */ + } + if (n->child) switch (n->child->type) { + case (MDOC_TEXT): + break; + default: + errx(1, p); + /* NOTREACHED */ + } + break; + case (MDOC_HEAD): + /* FALLTHROUGH */ + case (MDOC_BODY): + /* FALLTHROUGH */ + case (MDOC_TAIL): + if (NULL == n->parent) + errx(1, p); + if (MDOC_BLOCK != n->parent->type) + errx(1, p); + if (n->child) switch (n->child->type) { + case (MDOC_BLOCK): + /* FALLTHROUGH */ + case (MDOC_ELEM): + /* FALLTHROUGH */ + case (MDOC_TEXT): + break; + default: + errx(1, p); + /* NOTREACHED */ + } + break; + case (MDOC_BLOCK): + if (NULL == n->parent) + errx(1, p); + if (NULL == n->child) + errx(1, p); + switch (n->parent->type) { + case (MDOC_ROOT): + /* FALLTHROUGH */ + case (MDOC_HEAD): + /* FALLTHROUGH */ + case (MDOC_BODY): + /* FALLTHROUGH */ + case (MDOC_TAIL): + break; + default: + errx(1, p); + /* NOTREACHED */ + } + switch (n->child->type) { + case (MDOC_ROOT): + /* FALLTHROUGH */ + case (MDOC_ELEM): + errx(1, p); + /* NOTREACHED */ + default: + break; + } + break; + case (MDOC_ROOT): + if (n->parent) + errx(1, p); + if (NULL == n->child) + errx(1, p); + switch (n->child->type) { + case (MDOC_BLOCK): + break; + default: + errx(1, p); + /* NOTREACHED */ + } + break; + } +} static size_t @@ -387,7 +708,6 @@ static size_t arg_offset(const struct mdoc_argv *arg) { - /* TODO */ assert(*arg->value); if (0 == strcmp(*arg->value, "indent")) return(INDENT); @@ -1349,7 +1669,7 @@ termp_bd_pre(DECL_ARGS) p->flags |= TERMP_NOSPACE; } ln = node->line; - term_node(p, pair, meta, node); + print_node(p, pair, meta, node); } return(0); @@ -1924,3 +2244,4 @@ termp_mt_pre(DECL_ARGS) return(1); } + @@ -19,9 +19,6 @@ #ifndef TERM_H #define TERM_H -#include "mdoc.h" -#include "man.h" - /* FIXME - clean up tabs. */ #define INDENT 6 @@ -56,35 +53,6 @@ struct termp { void *symtab; /* Encoded-symbol table. */ }; -/* XXX - clean this up. */ - -struct termpair { - struct termpair *ppair; - int type; -#define TERMPAIR_FLAG (1 << 0) - int flag; - size_t offset; - size_t rmargin; - int count; -}; - -#define TERMPAIR_SETFLAG(termp, p, fl) \ - do { \ - assert(! (TERMPAIR_FLAG & (p)->type)); \ - (termp)->flags |= (fl); \ - (p)->flag = (fl); \ - (p)->type |= TERMPAIR_FLAG; \ - } while ( /* CONSTCOND */ 0) - -struct termact { - int (*pre)(struct termp *, struct termpair *, - const struct mdoc_meta *, - const struct mdoc_node *); - void (*post)(struct termp *, struct termpair *, - const struct mdoc_meta *, - const struct mdoc_node *); -}; - void *term_ascii2htab(void); const char *term_a2ascii(void *, const char *, size_t, size_t *); void term_asciifree(void *); @@ -93,11 +61,6 @@ void term_newln(struct termp *); void term_vspace(struct termp *); void term_word(struct termp *, const char *); void term_flushln(struct termp *); -void term_node(struct termp *, struct termpair *, - const struct mdoc_meta *, - const struct mdoc_node *); - -const struct termact *termacts; __END_DECLS @@ -23,21 +23,21 @@ #include <string.h> #include "term.h" +#include "man.h" +#include "mdoc.h" #ifdef __linux__ extern size_t strlcpy(char *, const char *, size_t); extern size_t strlcat(char *, const char *, size_t); #endif +extern int man_run(struct termp *, + const struct man *); +extern int mdoc_run(struct termp *, + const struct mdoc *); + static struct termp *term_alloc(enum termenc); static void term_free(struct termp *); -static void term_body(struct termp *, struct termpair *, - const struct mdoc_meta *, - const struct mdoc_node *); -static void term_head(struct termp *, - const struct mdoc_meta *); -static void term_foot(struct termp *, - const struct mdoc_meta *); static void term_pword(struct termp *, const char *, int); static void term_pescape(struct termp *, const char *, int *, int); @@ -48,7 +48,6 @@ static void term_stringa(struct termp *, const char *, size_t); static int term_isopendelim(const char *, int); static int term_isclosedelim(const char *, int); -static void sanity(const struct mdoc_node *); /* XXX */ void * @@ -65,17 +64,15 @@ terminal_run(void *arg, const struct man *man, { struct termp *p; - if (NULL == mdoc) - return(1); - p = (struct termp *)arg; if (NULL == p->symtab) p->symtab = term_ascii2htab(); - term_head(p, mdoc_meta(mdoc)); - term_body(p, NULL, mdoc_meta(mdoc), mdoc_node(mdoc)); - term_foot(p, mdoc_meta(mdoc)); + if (man) + return(man_run(p, man)); + if (mdoc) + return(mdoc_run(p, mdoc)); return(1); } @@ -403,191 +400,6 @@ term_word(struct termp *p, const char *word) } -static void -term_body(struct termp *p, struct termpair *ppair, - const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - term_node(p, ppair, meta, node); - if (node->next) - term_body(p, ppair, meta, node->next); -} - - -/* - * This is the main function for printing out nodes. It's constituted - * of PRE and POST functions, which correspond to prefix and infix - * processing. The termpair structure allows data to persist between - * prefix and postfix invocations. - */ -void -term_node(struct termp *p, struct termpair *ppair, - const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - int dochild; - struct termpair pair; - - /* Some quick sanity-checking. */ - - sanity(node); - - /* Pre-processing. */ - - dochild = 1; - pair.ppair = ppair; - pair.type = 0; - pair.offset = pair.rmargin = 0; - pair.flag = 0; - pair.count = 0; - - if (MDOC_TEXT != node->type) { - if (termacts[node->tok].pre) - if ( ! (*termacts[node->tok].pre)(p, &pair, meta, node)) - dochild = 0; - } else /* MDOC_TEXT == node->type */ - term_word(p, node->string); - - /* Children. */ - - if (TERMPAIR_FLAG & pair.type) - p->flags |= pair.flag; - - if (dochild && node->child) - term_body(p, &pair, meta, node->child); - - if (TERMPAIR_FLAG & pair.type) - p->flags &= ~pair.flag; - - /* Post-processing. */ - - if (MDOC_TEXT != node->type) - if (termacts[node->tok].post) - (*termacts[node->tok].post)(p, &pair, meta, node); -} - - -static void -term_foot(struct termp *p, const struct mdoc_meta *meta) -{ - struct tm *tm; - char *buf, *os; - - if (NULL == (buf = malloc(p->rmargin))) - err(1, "malloc"); - if (NULL == (os = malloc(p->rmargin))) - err(1, "malloc"); - - tm = localtime(&meta->date); - -#ifdef __OpenBSD__ - if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm)) -#else - if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm)) -#endif - err(1, "strftime"); - - (void)strlcpy(os, meta->os, p->rmargin); - - /* - * This is /slightly/ different from regular groff output - * because we don't have page numbers. Print the following: - * - * OS MDOCDATE - */ - - term_vspace(p); - - p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; - p->rmargin = p->maxrmargin - strlen(buf); - p->offset = 0; - - term_word(p, os); - term_flushln(p); - - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; - p->flags &= ~TERMP_NOBREAK; - - term_word(p, buf); - term_flushln(p); - - free(buf); - free(os); -} - - -static void -term_head(struct termp *p, const struct mdoc_meta *meta) -{ - char *buf, *title; - - p->rmargin = p->maxrmargin; - p->offset = 0; - - if (NULL == (buf = malloc(p->rmargin))) - err(1, "malloc"); - if (NULL == (title = malloc(p->rmargin))) - err(1, "malloc"); - - /* - * 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); - (void)strlcpy(buf, meta->vol, p->rmargin); - - if (meta->arch) { - (void)strlcat(buf, " (", p->rmargin); - (void)strlcat(buf, meta->arch, p->rmargin); - (void)strlcat(buf, ")", p->rmargin); - } - - (void)snprintf(title, p->rmargin, "%s(%d)", - meta->title, meta->msec); - - p->offset = 0; - p->rmargin = (p->maxrmargin - strlen(buf)) / 2; - p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; - - term_word(p, title); - term_flushln(p); - - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; - p->offset = p->rmargin; - p->rmargin = p->maxrmargin - strlen(title); - - term_word(p, buf); - term_flushln(p); - - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; - p->flags &= ~TERMP_NOBREAK; - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; - - term_word(p, title); - term_flushln(p); - - p->rmargin = p->maxrmargin; - p->offset = 0; - p->flags &= ~TERMP_NOSPACE; - - free(title); - free(buf); -} - - /* * Determine the symbol indicated by an escape sequences, that is, one * starting with a backslash. Once done, we pass this value into the @@ -765,112 +577,3 @@ term_chara(struct termp *p, char c) p->buf[(int)(p->col)++] = c; } - -static void -sanity(const struct mdoc_node *n) -{ - - switch (n->type) { - case (MDOC_TEXT): - if (n->child) - errx(1, "regular form violated (1)"); - if (NULL == n->parent) - errx(1, "regular form violated (2)"); - if (NULL == n->string) - errx(1, "regular form violated (3)"); - switch (n->parent->type) { - case (MDOC_TEXT): - /* FALLTHROUGH */ - case (MDOC_ROOT): - errx(1, "regular form violated (4)"); - /* NOTREACHED */ - default: - break; - } - break; - case (MDOC_ELEM): - if (NULL == n->parent) - errx(1, "regular form violated (5)"); - switch (n->parent->type) { - case (MDOC_TAIL): - /* FALLTHROUGH */ - case (MDOC_BODY): - /* FALLTHROUGH */ - case (MDOC_HEAD): - break; - default: - errx(1, "regular form violated (6)"); - /* NOTREACHED */ - } - if (n->child) switch (n->child->type) { - case (MDOC_TEXT): - break; - default: - errx(1, "regular form violated (7("); - /* NOTREACHED */ - } - break; - case (MDOC_HEAD): - /* FALLTHROUGH */ - case (MDOC_BODY): - /* FALLTHROUGH */ - case (MDOC_TAIL): - if (NULL == n->parent) - errx(1, "regular form violated (8)"); - if (MDOC_BLOCK != n->parent->type) - errx(1, "regular form violated (9)"); - if (n->child) switch (n->child->type) { - case (MDOC_BLOCK): - /* FALLTHROUGH */ - case (MDOC_ELEM): - /* FALLTHROUGH */ - case (MDOC_TEXT): - break; - default: - errx(1, "regular form violated (a)"); - /* NOTREACHED */ - } - break; - case (MDOC_BLOCK): - if (NULL == n->parent) - errx(1, "regular form violated (b)"); - if (NULL == n->child) - errx(1, "regular form violated (c)"); - switch (n->parent->type) { - case (MDOC_ROOT): - /* FALLTHROUGH */ - case (MDOC_HEAD): - /* FALLTHROUGH */ - case (MDOC_BODY): - /* FALLTHROUGH */ - case (MDOC_TAIL): - break; - default: - errx(1, "regular form violated (d)"); - /* NOTREACHED */ - } - switch (n->child->type) { - case (MDOC_ROOT): - /* FALLTHROUGH */ - case (MDOC_ELEM): - errx(1, "regular form violated (e)"); - /* NOTREACHED */ - default: - break; - } - break; - case (MDOC_ROOT): - if (n->parent) - errx(1, "regular form violated (f)"); - if (NULL == n->child) - errx(1, "regular form violated (10)"); - switch (n->child->type) { - case (MDOC_BLOCK): - break; - default: - errx(1, "regular form violated (11)"); - /* NOTREACHED */ - } - break; - } -} |