/* $Id: man_term.c,v 1.187 2016/01/08 17:48:09 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 AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mandoc_aux.h"
#include "mandoc.h"
#include "roff.h"
#include "man.h"
#include "out.h"
#include "term.h"
#include "main.h"
struct mtermp {
int fl;
};
struct roff_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 struct roff_meta *);
static void print_man_foot(struct termp *,
const struct roff_meta *);
static void print_bvspace(struct termp *,
const struct roff_node *, int);
static int pre_alternate(DECL_ARGS);
static int pre_literal(DECL_ARGS);
};
void
{
struct termp *p;
struct roff_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_SENTENCE;
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':
case '3':
case 'B':
break;
case '2':
case 'I':
break;
case 'P':
term_fontlast(p);
break;
case '1':
case 'C':
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;
if (less < 0)
else if (less > 0)
p->offset += v;
else
p->offset = v;
return 0;
}
static int
{
int i, len;
case MAN_SH:
case MAN_SS:
case MAN_PP:
case MAN_LP:
case MAN_P:
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);
/*
* Handle an explicit break request in the same way
* as an overflowing line.
*/
if (p->flags & TERMP_BRIND) {
p->rmargin = p->maxrmargin;
}
return 0;
}
static int
{
int len;
switch (n->type) {
case ROFFT_BLOCK:
return 1;
case ROFFT_BODY:
break;
default:
return 0;
}
p->trailspace = 2;
}
/* Calculate offset. */
} else
return 1;
}
static void
{
switch (n->type) {
case ROFFT_BODY:
term_newln(p);
/*
* Compatibility with a groff bug.
* The .HP macro uses the undocumented .tag request
* which causes a line break and cancels no-space
* mode even if there isn't any output.
*/
term_vspace(p);
p->trailspace = 0;
p->rmargin = p->maxrmargin;
break;
default:
break;
}
}
static int
{
switch (n->type) {
case ROFFT_BLOCK:
break;
default:
break;
}
return n->type != ROFFT_HEAD;
}
static int
{
switch (n->type) {
case ROFFT_BODY:
p->flags |= TERMP_NOSPACE;
break;
case ROFFT_HEAD:
p->flags |= TERMP_NOBREAK;
p->trailspace = 1;
break;
case ROFFT_BLOCK:
/* FALLTHROUGH */
default:
return 1;
}
/* Calculate the offset from the optional second argument. */
} else
switch (n->type) {
case ROFFT_HEAD:
if (n->child)
if (savelit)
return 0;
case ROFFT_BODY:
p->rmargin = p->maxrmargin;
break;
default:
break;
}
return 1;
}
static void
{
switch (n->type) {
case ROFFT_HEAD:
term_flushln(p);
p->flags &= ~TERMP_NOBREAK;
p->trailspace = 0;
p->rmargin = p->maxrmargin;
break;
case ROFFT_BODY:
term_newln(p);
break;
default:
break;
}
}
static int
{
switch (n->type) {
case ROFFT_HEAD:
p->trailspace = 1;
break;
case ROFFT_BODY:
p->flags |= TERMP_NOSPACE;
break;
case ROFFT_BLOCK:
/* FALLTHROUGH */
default:
return 1;
}
/* Calculate offset. */
} else
switch (n->type) {
case ROFFT_HEAD:
/* Don't print same-line elements. */
}
if (savelit)
return 0;
case ROFFT_BODY:
p->rmargin = p->maxrmargin;
p->trailspace = 0;
break;
default:
break;
}
return 1;
}
static void
{
switch (n->type) {
case ROFFT_HEAD:
term_flushln(p);
break;
case ROFFT_BODY:
term_newln(p);
break;
default:
break;
}
}
static int
{
int i;
switch (n->type) {
case ROFFT_BLOCK:
/*
* No vertical space before the first subsection
* and after an empty subsection.
*/
do {
n = n->prev;
break;
term_vspace(p);
break;
case ROFFT_HEAD:
break;
case ROFFT_BODY:
p->rmargin = p->maxrmargin;
p->trailspace = 0;
break;
default:
break;
}
return 1;
}
static void
{
switch (n->type) {
case ROFFT_HEAD:
term_newln(p);
break;
case ROFFT_BODY:
term_newln(p);
break;
default:
break;
}
}
static int
{
int i;
switch (n->type) {
case ROFFT_BLOCK:
/*
* No vertical space before the first section
* and after an empty section.
*/
do {
n = n->prev;
break;
term_vspace(p);
break;
case ROFFT_HEAD:
p->offset = 0;
break;
case ROFFT_BODY:
p->rmargin = p->maxrmargin;
p->trailspace = 0;
break;
default:
break;
}
return 1;
}
static void
{
switch (n->type) {
case ROFFT_HEAD:
term_newln(p);
break;
case ROFFT_BODY:
term_newln(p);
break;
default:
break;
}
}
static int
{
switch (n->type) {
case ROFFT_BLOCK:
term_newln(p);
return 1;
case ROFFT_HEAD:
return 0;
default:
break;
}
p->rmargin = p->maxrmargin;
return 1;
}
static void
{
switch (n->type) {
case ROFFT_BLOCK:
return;
case ROFFT_HEAD:
return;
default:
term_newln(p);
break;
}
}
static int
{
return n->type != ROFFT_HEAD;
}
static void
{
if (n->type != ROFFT_BLOCK)
return;
term_word(p, "<");
p->flags |= TERMP_NOSPACE;
p->flags |= TERMP_NOSPACE;
term_word(p, ">");
}
static void
{
int c;
switch (n->type) {
case ROFFT_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 ROFFT_EQN:
p->flags |= TERMP_NOSPACE;
p->flags |= TERMP_NOSPACE;
return;
case ROFFT_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 operating system
* instead of the title.
*/
if ( ! p->mdocstyle) {
term_vspace(p);
term_vspace(p);
}
} else {
}
/* Bottom left corner: operating system. */
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);
}
}