gno/usr.bin/nroff/text.c

851 lines
16 KiB
C

#undef OLD_WAY
/*
* text.c - text output processing portion of nroff word processor
*
* adapted for atariST/TOS by Bill Rosenkranz 11/89
* net: rosenkra@hall.cray.com
* CIS: 71460,17
* GENIE: W.ROSENKRANZ
*
* original author:
*
* Stephen L. Browning
* 5723 North Parker Avenue
* Indianapolis, Indiana 46220
*
* history:
*
* - Originally written in BDS C;
* - Adapted for standard C by W. N. Paul
* - Heavily hacked up to conform to "real" nroff by Bill Rosenkranz
* - Heavily modified by Devin Reade to avoid memory trashing bugs.
*
* $Id: text.c,v 1.2 1997/03/20 06:40:51 gdr Exp $
*/
#ifdef __ORCAC__
segment "text______";
#pragma noroot
#pragma optimize 78
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef __GNO__
#include <termcap.h>
#else
#include "unix/termcap.h"
#endif
#include "nroff.h"
#include "io.h"
#include "escape.h"
/*
* output buffer control parameters
*/
static struct {
int outp; /* next avail char pos in outbuf, init = 0 */
int outw; /* width of text currently in buffer */
int outwds; /* number of words in buffer, init = 0 */
int lpr; /* output to printer, init = FALSE */
int outesc; /* number of escape char on this line */
char outbuf[MAXLINE];/* output of filled text */
} co;
static void bold (char *p0, char *p1, int size);
static void center (char *p);
static void expand (char *p0, char c, char *s);
static void justcntr (char *p, char *q, int *limit);
static void justleft (char *p, char *q, int limit);
static void justrite (char *p, char *q, int limit);
static void leadbl (char *p);
static void puttl (char *p, int *lim, int pgno);
static void putwrd (char *wrdbuf);
static void spread (char *p, int outp, int nextra, int outwds,int escapes);
static void underl (char *p0, char *p1, int size);
static int width (char *s);
static void do_mc (char *p);
/*
* text
* main text processing
*
* Pre: <line> contains the line we wish to process (and print)
* it is MAXLINE characters long, including the
* terminating NULL character.
*
* Post:
*/
void
text (char *line) {
register int i;
char wrdbuf[MAXLINE];
/*
* skip over leading blanks if in fill mode. we indent later.
* since leadbl does a robrk, do it if in .nf mode
*/
if (dc.fill == YES) {
if (*line == ' ' || *line == '\n' || *line == '\r') {
/* note that leadbl shifts <line> left as necessary */
leadbl (line);
}
} else {
robrk ();
}
/*
* expand escape sequences from <line> into <wrdbuf>
*/
expesc (line, wrdbuf, MAXLINE);
/*
* test for how to output
*/
if (dc.ulval > 0) {
/*
* underline (.ul)
*
* Because of the way underlining is handled,
* MAXLINE should be declared to be three times
* larger than the longest expected input line
* for underlining. Since many of the character
* buffers use this parameter, a lot of memory
* can be allocated when it may not really be
* needed. A MAXLINE of 180 would allow about
* 60 characters in the output line to be
* underlined (remember that only alphanumerics
* get underlined - no spaces or punctuation).
*/
underl (line, wrdbuf, MAXLINE);
--dc.ulval;
}
if (dc.cuval > 0) {
/*
* continuous underline (.cu)
*/
underl (line, wrdbuf, MAXLINE);
--dc.cuval;
}
if (dc.boval > 0) {
/*
* bold (.bo)
*/
bold (line, wrdbuf, MAXLINE);
--dc.boval;
}
if (dc.ceval > 0) {
/*
* centered (.ce)
*/
center (line);
do_mc (line);
put (line);
--dc.ceval;
} else if ((*line == '\r' || *line == '\n') && dc.fill == NO) {
/*
* all blank line
*/
do_mc (line);
put (line);
} else if (dc.fill == NO) {
/*
* unfilled (.nf)
*/
do_mc (line);
put (line);
} else {
/*
* anything else...
*
* init escape char counter for this line...
*/
/* co.outesc = 0;*/
/*
* get a word and put it out. increment ptr to the next
* word.
*/
while ((i = getwrd (line, wrdbuf)) > 0) {
/* co.outesc += countesc (wrdbuf);*/
putwrd (wrdbuf);
line += i;
}
}
}
/*
* bold
* insert bold face text (by overstriking)
*/
static void
bold (register char *p0, register char *p1, int size)
{
register int i;
register int j;
j = 0;
for (i = 0; (p0[i] != '\n') && (j < size - 1); ++i)
{
if (isalpha (p0[i]) || isdigit (p0[i]))
{
p1[j++] = p0[i];
p1[j++] = '\b';
}
p1[j++] = p0[i];
}
p1[j++] = '\n';
p1[j] = EOS;
while (*p1 != EOS)
*p0++ = *p1++;
*p0 = EOS;
}
/*
* center
* center a line by setting tival
*/
static void
center (register char *p) {
int t;
t = (dc.rmval + dc.tival - width (p)) >> 1;
dc.tival = MAX(t, 0);
}
/*
* expand
* expand title buffer to include character string
*/
static void
expand (register char *p0, char c, register char *s) {
register char *p;
register char *q;
register char *r;
char tmp[MAXLINE];
p = p0;
q = tmp;
while (*p != EOS) {
if (*p == c) {
r = s;
while (*r != EOS) {
*q++ = *r++;
}
} else {
*q++ = *p;
}
++p;
}
*q = EOS;
strcpy (p0, tmp); /* copy it back */
}
/*
* justcntr
* center title text into print buffer
*/
static void
justcntr (register char *p, char *q, int *limit) {
register int len;
len = width (p);
q = &q[(limit[RIGHT] + limit[LEFT] - len) >> 1];
while (*p != EOS) {
*q++ = *p++;
}
}
/*
* justleft
* left justify title text into print buffer
*/
static void
justleft (register char *p, char *q, int limit) {
q = &q[limit];
while (*p != EOS) {
*q++ = *p++;
}
}
/*
* justrite
* right justify title text into print buffer
*/
static void
justrite (register char *p, char *q, int limit) {
register int len;
len = width (p);
q = &q[limit - len];
while (*p != EOS) {
*q++ = *p++;
}
}
/*
* leadbl
* delete leading blanks, set tival
*
* REVIEWED
*/
static void
leadbl (register char *p) {
register char *q;
register int i;
/*
* end current line and reset co struct
*/
robrk ();
/*
* skip spaces
*/
for (i = 0; p[i] == ' ' || p[i] == '\t'; ++i);
/*
* if not end of line, reset current temp indent
*/
if (p[i] != '\n' && p[i] != '\r') {
dc.tival = i;
}
/*
* shift string
*/
q = &p[i];
#ifdef DEBUG
i = 0;
#endif
while (*q) {
#ifdef DEBUG
i++;
ASSERT(i<MAXLINE, ("leadbl buffer overflow\n"));
#endif
*p++ = *q++;
}
*p = '\0';
}
/*
* pfoot
* put out page footer
*/
void
pfoot (void) {
if (dc.prflg == TRUE) {
skip (pg.m3val);
if (pg.m4val > 0) {
if ((pg.curpag % 2) == 0) {
puttl (pg.efoot, pg.eflim, pg.curpag);
} else {
puttl (pg.ofoot, pg.oflim, pg.curpag);
}
skip (pg.m4val - 1);
}
}
}
/*
* phead
* put out page header
*/
void
phead (void) {
pg.curpag = pg.newpag;
if (pg.curpag >= pg.frstpg && pg.curpag <= pg.lastpg) {
dc.prflg = TRUE;
} else {
dc.prflg = FALSE;
}
++pg.newpag;
set_ireg ("%", pg.newpag, 0);
if (dc.prflg == TRUE) {
if (pg.m1val > 0) {
skip (pg.m1val - 1);
if ((pg.curpag % 2) == 0) {
puttl (pg.ehead, pg.ehlim, pg.curpag);
} else {
puttl (pg.ohead, pg.ohlim, pg.curpag);
}
}
skip (pg.m2val);
}
/*
* initialize lineno for the next page
*/
pg.lineno = pg.m1val + pg.m2val + 1;
set_ireg ("ln", pg.lineno, 0);
}
/*
* puttl
* put out title or footer
*/
static void
puttl (register char *p, int *lim, int pgno) {
register int i;
char pn[8];
char t[MAXLINE];
char h[MAXLINE];
char delim;
itoda (pgno, pn, 6);
for (i = 0; i < MAXLINE; ++i) {
h[i] = ' ';
}
delim = *p++;
p = getfield (p, t, delim);
expand (t, dc.pgchr, pn);
justleft (t, h, lim[LEFT]);
p = getfield (p, t, delim);
expand (t, dc.pgchr, pn);
justcntr (t, h, lim);
p = getfield (p, t, delim);
expand (t, dc.pgchr, pn);
justrite (t, h, lim[RIGHT]);
for (i = MAXLINE - 4; h[i] == ' '; --i) {
h[i] = EOS;
}
h[++i] = '\n';
h[++i] = '\r';
h[++i] = EOS;
if (strlen (h) > 2) {
for (i = 0; i < pg.offset; ++i) {
PRCHAR2(' ', out_stream);
}
}
putlin (h, out_stream);
}
/*
* putwrd
* put word in output buffer
*/
static void
putwrd (register char *wrdbuf) {
register char *p0;
register char *p1;
int w;
int last;
int llval;
int nextra;
/*
* check if this word puts us over the limit
*/
w = width (wrdbuf);
last = strlen (wrdbuf) + co.outp;
llval = dc.rmval - dc.tival;
/* if (((co.outp > 0) && ((co.outw + w) > llval))*/
co.outesc += countesc (wrdbuf);
if (((co.outp > 0) && ((co.outw + w - co.outesc) > llval))
||(last > MAXLINE)) {
/*
* last word exceeds limit so prepare to break line, print
* it, and reset outbuf.
*/
last -= co.outp;
if (dc.juval == YES) {
nextra = llval - co.outw + 1;
/*
* Do not take in the escape char of the
* word that didn't fit on this line anymore
*/
co.outesc -= countesc (wrdbuf);
/*
* Check whether last word was end of
* sentence and modify counts so that
* it is right justified.
*/
if (co.outbuf[co.outp - 2] == ' ') {
--co.outp;
++nextra;
}
#ifdef OLD_WAY
spread (co.outbuf, co.outp - 1, nextra, co.outwds, co.outesc);
if ((nextra > 0) && (co.outwds > 1)) {
co.outp += (nextra - 1);
}
#if 0
if (co.outesc > 0) {
co.outp += co.outesc;
}
#endif /* 0 */
#else /* OLD_WAY */
spread (co.outbuf, co.outp - 1, nextra, co.outwds, co.outesc);
if ((nextra + co.outesc > 0) && (co.outwds > 1)) {
co.outp += (nextra + co.outesc - 1);
}
#endif
}
/*
* break line, output it, and reset all co members. reset
* esc count.
*/
robrk ();
co.outesc = countesc (wrdbuf);
}
/*
* copy the current word to the out buffer which may have been
* reset
*/
p0 = wrdbuf;
p1 = co.outbuf + co.outp;
while (*p0 != EOS) {
*p1++ = *p0++;
}
co.outp = last;
co.outbuf[co.outp++] = ' ';
co.outw += w + 1;
co.outwds += 1;
}
/*
* skip
* skips the number of lines specified by n.
*/
void
skip (register int n) {
register int i;
register int j;
if (dc.prflg == TRUE && n > 0) {
for (i = 0; i < n; ++i) {
/*
* handle blank line with changebar
*/
if (mc_ing == TRUE) {
for (j = 0; j < pg.offset; ++j) {
PRCHAR2(' ', out_stream);
}
for (j = 0; j < dc.rmval; ++j) {
PRCHAR2(' ', out_stream);
}
for (j = 0; j < mc_space; j++) {
PRCHAR2(' ', out_stream);
}
PRCHAR(mc_char, out_stream);
}
PRCHAR2('\n', out_stream);
#if 0
/* gdr: not required */
prchar ('\r', out_stream);
#endif
}
}
}
/*
* spread
* spread words to justify right margin
*/
static void
spread (register char *p, int outp, int nextra, int outwds, int escapes) {
register int i;
register int j;
register int nb;
register int ne;
register int nholes;
/*
* quick sanity check...
*/
#ifdef OLDWAY
if ((nextra <= 0) || (outwds <= 1)) {
return;
}
#else
if ((nextra + escapes < 1) || (outwds < 2)) {
return;
}
#endif
/*
* set up for the spread and do it...
*/
dc.sprdir = ~dc.sprdir;
#ifdef OLD_WAY
ne = nextra;
#else
ne = nextra + escapes;
#endif
nholes = outwds - 1; /* holes between words */
i = outp - 1; /* last non-blank character */
j = MIN(MAXLINE - 3, i + ne); /* leave room for CR,LF,EOS */
#if 0
j += escapes;
if (p[i-1] == 27) {
j += 2;
}
j = MIN(j, MAXLINE - 3);
#endif
while (i < j) {
p[j] = p[i];
if (p[i] == ' ') {
if (dc.sprdir == 0) {
nb = (ne - 1) / nholes + 1;
} else {
nb = ne / nholes;
}
ne -= nb;
--nholes;
for (; nb > 0; --nb) {
--j;
p[j] = ' ';
}
}
--i;
--j;
}
}
/*
* strkovr
* split overstrikes (backspaces) into seperate buffer
*/
int
strkovr (char *p, char *q) {
register char *pp;
int bsflg;
bsflg = FALSE;
pp = p;
while (*p != EOS) {
*q = ' ';
*pp = *p;
++p;
if (*p == '\b') {
if (*pp >= ' ' && *pp <= '~') {
bsflg = TRUE;
*q = *pp;
++p;
*pp = *p;
++p;
}
}
++q;
++pp;
}
*q++ = NEWLINE;
*q = *pp = EOS;
return bsflg;
}
/*
* underl
* underline a line
*/
static void
underl (register char *p0, register char *p1, int size) {
register int i;
register int j;
j = 0;
for (i = 0; (p0[i] != '\n') && (j < size - 1); ++i) {
if (p0[i] >= ' ' &&
p0[i] <= '~' &&
(isalpha (p0[i]) || isdigit (p0[i]) || dc.cuval > 0))
{
p1[j++] = '_';
p1[j++] = '\b';
}
p1[j++] = p0[i];
}
p1[j++] = '\n';
p1[j] = EOS;
while (*p1 != EOS) {
*p0++ = *p1++;
}
*p0 = EOS;
}
/*
* width
* compute width of character string
*/
static int
width (char *s) {
register int w;
w = 0;
for (; *s != '\0'; s++) {
ASSERT((*s != '\n' && *s != '\r'), ("\n"));
if ((*s >= 32) && (*s < 127)) {
++w;
} else if (*s == '\b' || *s == 128) {
--w;
}
/* ignore high-bit chars and other control chars */
}
return w;
}
/*
* do_mc
* add margin char (change bar) for .nf and .ce lines.
*
* filled lines handled in robrk(). blank lines (.sp) handled in skip().
* note: robrk() calls this routine, too.
*/
static void
do_mc (char *p) {
register char *ps;
register int nspaces;
register int i;
register int has_cr;
register int has_lf;
int len;
int nesc;
if (mc_ing == FALSE) {
return;
}
len = strlen (p);
/*
* get to the end...
*/
ps = p;
while (*ps) {
ps++;
}
/*
* check for cr and lf
*/
ps--;
has_lf = 0;
has_cr = 0;
while (ps >= p && (*ps == '\r' || *ps == '\n')) {
if (*ps == '\n') {
has_lf++;
} else {
has_cr++;
}
len--;
ps--;
}
if (has_lf < has_cr) {
has_lf = has_cr;
} else if (has_cr < has_lf) {
has_cr = has_lf;
}
/*
* remove any trailing blanks here
*/
while (ps >= p && *ps == ' ') {
ps--;
len--;
}
*++ps = EOS;
/*
* add trailing spaces for short lines. count escapes, subtract
* from len. use rmval for rigth margin (minus tival which is
* added later in put).
*/
nesc = countesc (p);
len -= nesc;
nspaces = dc.rmval - dc.tival - len;
for (i = 0; i < nspaces; i++, ps++) {
*ps = ' ';
}
/*
* add the bar...
*/
for (i = 0; i < mc_space; i++, ps++) {
*ps = ' ';
}
*ps++ = mc_char;
/*
* replace cr, lf, and EOS
*/
while (has_lf--) {
*ps++ = '\r';
*ps++ = '\n';
}
*ps = EOS;
return;
}
void
initOutbuf (void) {
co.outp = 0;
co.outw = 0;
co.outwds = 0;
co.lpr = FALSE;
co.outesc = 0;
memset(co.outbuf, EOS, MAXLINE);
#if 0
for (i = 0; i < MAXLINE; ++i) {
co.outbuf[i] = EOS;
}
#endif
}
/*
* robrk
*
* End current filled line. References and modifies globals:
* co
*/
void
robrk (void) {
if (co.outp > 0) {
/*
* handle margin char (change bar) here for all filled lines
*/
ASSERT(MAXLINE - co.outp > 2, ("output buffer overrun: >%d:%d:%d<\n",
MAXLINE, co.outp, MAXLINE- co.outp));
co.outbuf[co.outp] = '\r';
co.outbuf[co.outp+1] = '\n';
co.outbuf[co.outp+2] = EOS;
do_mc (co.outbuf);
put (co.outbuf);
}
co.outp = 0;
co.outw = 0;
co.outwds = 0;
co.outesc = 0;
}
void
setPrinting(int val) {
co.lpr = val;
}