man_term.c revision 260e9a87725c090ba5835b1f9f0b62fa2f96036f
/* $Id: man_term.c,v 1.169 2015/03/06 15:48:52 schwarze Exp $ */
/*
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc.h"
#include "mandoc_aux.h"
#include "out.h"
#include "man.h"
#include "term.h"
#include "main.h"
struct mtermp {
int fl;
#define MANT_LITERAL (1 << 0)
int lmargincur; /* index of current margin */
int lmarginsz; /* actual number of nested margins */
int pardist; /* vert. space before par., unit: [v] */
};
struct man_node *n, \
struct termact {
int flags;
};
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_alternate(DECL_ARGS);
static int pre_literal(DECL_ARGS);
};
void
{
struct termp *p;
struct man_node *n;
p->overstep = 0;
if (p->synopsisonly) {
while (n != NULL) {
print_man_nodelist(p, &mt,
term_newln(p);
break;
}
n = n->next;
}
} else {
if (p->defindent == 0)
p->defindent = 7;
p->flags |= TERMP_NOSPACE;
if (n != NULL)
term_end(p);
}
}
/*
* 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
* then don't emit vertical space. If we are (RS), then do. If not the
* first, print it.
*/
static void
{
int i;
term_newln(p);
return;
return;
for (i = 0; i < pardist; i++)
term_vspace(p);
}
static int
{
return(0);
}
static int
{
return(0);
}
static int
{
return(1);
}
static int
{
term_newln(p);
else
/*
* 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.
*/
p->rmargin = p->maxrmargin;
p->trailspace = 0;
p->flags |= TERMP_NOSPACE;
}
return(0);
}
static int
{
n = n->child;
if (n == NULL) {
return(0);
}
return(0);
}
static int
{
int savelit, i;
switch (n->tok) {
case MAN_RB:
font[0] = TERMFONT_NONE;
break;
case MAN_RI:
font[0] = TERMFONT_NONE;
break;
case MAN_BR:
font[0] = TERMFONT_BOLD;
break;
case MAN_BI:
font[0] = TERMFONT_BOLD;
break;
case MAN_IR:
font[0] = TERMFONT_UNDER;
break;
case MAN_IB:
font[0] = TERMFONT_UNDER;
break;
default:
abort();
}
term_fontrepl(p, font[i]);
p->flags |= TERMP_NOSPACE;
}
return(0);
}
static int
{
return(1);
}
static int
{
term_word(p, "[");
p->flags |= TERMP_NOSPACE;
}
}
p->flags |= TERMP_NOSPACE;
term_word(p, "]");
return(0);
}
static int
{
const char *cp;
term_fontlast(p);
return(0);
}
switch (*cp) {
case '4':
/* FALLTHROUGH */
case '3':
/* FALLTHROUGH */
case 'B':
break;
case '2':
/* FALLTHROUGH */
case 'I':
break;
case 'P':
term_fontlast(p);
break;
case '1':
/* FALLTHROUGH */
case 'C':
/* FALLTHROUGH */
case 'R':
break;
default:
break;
}
return(0);
}
static int
{
const char *cp;
size_t v;
int less;
term_newln(p);
return(0);
}
less = 0;
if ('-' == *cp)
less = -1;
else if ('+' == *cp)
less = 1;
else
cp--;
return(0);
v = term_hspan(p, &su);
if (less < 0)
else if (less > 0)
p->offset += v;
else
p->offset = v;
return(0);
}
static int
{
int i, len;
case MAN_SH:
/* FALLTHROUGH */
case MAN_SS:
/* FALLTHROUGH */
case MAN_PP:
/* FALLTHROUGH */
case MAN_LP:
/* FALLTHROUGH */
case MAN_P:
/* FALLTHROUGH */
return(0);
default:
break;
}
}
len = 0;
len = 1;
else {
}
if (len == 0)
term_newln(p);
else if (len < 0)
else
for (i = 0; i < len; i++)
term_vspace(p);
return(0);
}
static int
{
int len;
switch (n->type) {
case MAN_BLOCK:
return(1);
case MAN_BODY:
break;
default:
return(0);
}
p->trailspace = 2;
}
/* Calculate offset. */
} else
return(1);
}
static void
{
switch (n->type) {
case MAN_BODY:
term_newln(p);
p->trailspace = 0;
p->rmargin = p->maxrmargin;
break;
default:
break;
}
}
static int
{
switch (n->type) {
case MAN_BLOCK:
break;
default:
break;
}
}
static int
{
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:
/* FALLTHROUGH */
default:
return(1);
}
/* Calculate the offset from the optional second argument. */
} else
switch (n->type) {
case MAN_HEAD:
if (n->child)
if (savelit)
return(0);
case MAN_BODY:
p->rmargin = p->maxrmargin;
break;
default:
break;
}
return(1);
}
static void
{
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);
break;
default:
break;
}
}
static int
{
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:
/* FALLTHROUGH */
default:
return(1);
}
/* Calculate offset. */
} else
switch (n->type) {
case MAN_HEAD:
/* Don't print same-line elements. */
}
if (savelit)
return(0);
case MAN_BODY:
p->rmargin = p->maxrmargin;
p->trailspace = 0;
p->flags &= ~TERMP_NOBREAK;
break;
default:
break;
}
return(1);
}
static void
{
switch (n->type) {
case MAN_HEAD:
term_flushln(p);
break;
case MAN_BODY:
term_newln(p);
break;
default:
break;
}
}
static int
{
int i;
switch (n->type) {
case MAN_BLOCK:
/*
* No vertical space before the first subsection
* and after an empty subsection.
*/
do {
n = n->prev;
break;
term_vspace(p);
break;
case MAN_HEAD:
break;
case MAN_BODY:
break;
default:
break;
}
return(1);
}
static void
{
switch (n->type) {
case MAN_HEAD:
term_newln(p);
break;
case MAN_BODY:
term_newln(p);
break;
default:
break;
}
}
static int
{
int i;
switch (n->type) {
case MAN_BLOCK:
/*
* No vertical space before the first section
* and after an empty section.
*/
do {
n = n->prev;
break;
term_vspace(p);
break;
case MAN_HEAD:
p->offset = 0;
break;
case MAN_BODY:
break;
default:
break;
}
return(1);
}
static void
{
switch (n->type) {
case MAN_HEAD:
term_newln(p);
break;
case MAN_BODY:
term_newln(p);
break;
default:
break;
}
}
static int
{
switch (n->type) {
case MAN_BLOCK:
term_newln(p);
return(1);
case MAN_HEAD:
return(0);
default:
break;
}
p->rmargin = p->maxrmargin;
return(1);
}
static void
{
switch (n->type) {
case MAN_BLOCK:
return;
case MAN_HEAD:
return;
default:
term_newln(p);
break;
}
}
static int
{
}
static void
{
return;
term_word(p, "<");
p->flags |= TERMP_NOSPACE;
p->flags |= TERMP_NOSPACE;
term_word(p, ">");
}
static void
{
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;
term_newln(p);
goto out;
case MAN_EQN:
p->flags |= TERMP_NOSPACE;
p->flags |= TERMP_NOSPACE;
return;
case MAN_TBL:
term_vspace(p);
return;
default:
break;
}
c = 1;
if (c && n->child)
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.
*/
rmax = p->maxrmargin;
p->flags |= TERMP_NOSPACE;
term_flushln(p);
else
term_newln(p);
} else
p->maxrmargin = rmax;
}
p->flags |= TERMP_SENTENCE;
}
static void
{
while (n != NULL) {
n = n->next;
}
}
static void
{
char *title;
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) {
term_vspace(p);
term_vspace(p);
}
} else {
}
/* Bottom left corner: manual source. */
p->trailspace = 1;
p->offset = 0;
term_flushln(p);
/* At the bottom in the middle: manual date. */
p->flags |= TERMP_NOSPACE;
term_flushln(p);
/* Bottom right corner: manual title and section. */
p->flags &= ~TERMP_NOBREAK;
p->flags |= TERMP_NOSPACE;
p->trailspace = 0;
p->rmargin = p->maxrmargin;
term_flushln(p);
}
static void
{
const char *volume;
char *title;
/* Top left corner: manual title and section. */
p->trailspace = 1;
p->offset = 0;
term_flushln(p);
/* At the top in the middle: manual volume. */
p->flags |= TERMP_NOSPACE;
term_flushln(p);
/* Top right corner: title and section, again. */
p->flags &= ~TERMP_NOBREAK;
p->trailspace = 0;
p->flags |= TERMP_NOSPACE;
p->rmargin = p->maxrmargin;
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);
}
}