877 lines
12 KiB
C
877 lines
12 KiB
C
/*
|
|
* low.c - misc low-level functions for 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
|
|
* - Adapted to have it recognize also VT100 escape codes (e.g. ESC[7m)
|
|
* by Wim 'Blue Baron' van Dorst (wsincc@tuerc3.urc.tue.nl)
|
|
*/
|
|
|
|
#undef NRO_MAIN /* extern globals */
|
|
|
|
#include <stdio.h>
|
|
#include "nroff.h"
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* atod */
|
|
/*------------------------------*/
|
|
atod (c)
|
|
char c;
|
|
{
|
|
|
|
/*
|
|
* convert ascii character to decimal.
|
|
*/
|
|
|
|
return (((c < '0') || (c > '9')) ? -1 : c - '0');
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* robrk */
|
|
/*------------------------------*/
|
|
robrk ()
|
|
{
|
|
|
|
/*
|
|
* end current filled line
|
|
*/
|
|
|
|
if (co.outp > 0)
|
|
{
|
|
#ifdef GEMDOS
|
|
co.outbuf[co.outp] = '\r';
|
|
co.outbuf[co.outp+1] = '\n';
|
|
co.outbuf[co.outp+2] = EOS;
|
|
#else
|
|
co.outbuf[co.outp] = '\n';
|
|
co.outbuf[co.outp+1] = EOS;
|
|
co.outbuf[co.outp+2] = EOS;
|
|
#endif
|
|
put (co.outbuf);
|
|
}
|
|
co.outp = 0;
|
|
co.outw = 0;
|
|
co.outwds = 0;
|
|
co.outesc = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* ctod */
|
|
/*------------------------------*/
|
|
ctod (p)
|
|
register char *p;
|
|
{
|
|
|
|
/*
|
|
* convert string to decimal.
|
|
* processes only positive values.
|
|
*/
|
|
|
|
register int val;
|
|
register int d;
|
|
|
|
val = 0;
|
|
while (*p != EOS)
|
|
{
|
|
d = atod (*p);
|
|
p++;
|
|
if (d == -1)
|
|
return (val);
|
|
val = 10 * val + d;
|
|
}
|
|
return (val);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* inptobu */
|
|
/*------------------------------*/
|
|
inptobu (ps)
|
|
char *ps;
|
|
{
|
|
|
|
/*
|
|
* convert input units to b.u.
|
|
*/
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* butochar */
|
|
/*------------------------------*/
|
|
butochar (ps)
|
|
char *ps;
|
|
{
|
|
|
|
/*
|
|
* convert b.u. to char spaces
|
|
*/
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* skipbl */
|
|
/*------------------------------*/
|
|
char *skipbl (p)
|
|
register char *p;
|
|
{
|
|
|
|
/*
|
|
* skip blanks and tabs in character buffer. return ptr to first
|
|
* non-space or non-tab char. this could mean EOS or \r or \n.
|
|
* also increments the arg ptr (side effect).
|
|
*/
|
|
|
|
while ((*p != EOS) && (*p == ' ' || *p == '\t'))
|
|
++p;
|
|
return (p);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* skipwd */
|
|
/*------------------------------*/
|
|
char *skipwd (p)
|
|
register char *p;
|
|
{
|
|
|
|
/*
|
|
* skip over word and punctuation. anything but space,\t,\r,\n, and EOS
|
|
* is skipped. return ptr to the first of these found. also increments
|
|
* the arg ptr (side effect).
|
|
*/
|
|
|
|
while (*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' && *p != EOS)
|
|
++p;
|
|
return (p);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* space */
|
|
/*------------------------------*/
|
|
space (n)
|
|
int n;
|
|
{
|
|
|
|
/*
|
|
* space vertically n lines. this does header and footer also.
|
|
*/
|
|
|
|
robrk ();
|
|
if (pg.lineno > pg.bottom)
|
|
return;
|
|
if (pg.lineno == 0)
|
|
phead ();
|
|
skip (min (n, pg.bottom + 1 - pg.lineno));
|
|
pg.lineno += n;
|
|
set_ireg ("ln", pg.lineno, 0);
|
|
if (pg.lineno > pg.bottom)
|
|
pfoot ();
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* getfield */
|
|
/*------------------------------*/
|
|
char *getfield (p, q, delim)
|
|
register char *p;
|
|
register char *q;
|
|
char delim;
|
|
{
|
|
|
|
/*
|
|
* get field from title
|
|
*/
|
|
|
|
while (*p != delim && *p != '\r' && *p != '\n' && *p != EOS)
|
|
{
|
|
*q++ = *p++;
|
|
}
|
|
*q = EOS;
|
|
if (*p == delim)
|
|
++p;
|
|
return (p);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* getwrd */
|
|
/*------------------------------*/
|
|
getwrd (p0, p1)
|
|
register char *p0;
|
|
register char *p1;
|
|
{
|
|
|
|
/*
|
|
* get non-blank word from p0 into p1.
|
|
* return number of characters processed.
|
|
*/
|
|
|
|
register int i;
|
|
register char *p;
|
|
char c;
|
|
|
|
/*
|
|
* init counter...
|
|
*/
|
|
i = 0;
|
|
|
|
|
|
/*
|
|
* skip leading whitespace
|
|
*/
|
|
while (*p0 && (*p0 == ' ' || *p0 == '\t'))
|
|
{
|
|
++i;
|
|
++p0;
|
|
}
|
|
|
|
|
|
/*
|
|
* set ptr and start to look for end of word
|
|
*/
|
|
p = p0;
|
|
while (*p0 != ' ' && *p0 != EOS && *p0 != '\t')
|
|
{
|
|
if (*p0 == '\n' || *p0 == '\r')
|
|
break;
|
|
*p1 = *p0++;
|
|
++p1;
|
|
++i;
|
|
}
|
|
|
|
c = *(p1 - 1);
|
|
if (c == '"')
|
|
c = *(p1 - 2);
|
|
if (c == '?' || c == '!')
|
|
{
|
|
*p1++ = ' ';
|
|
++i;
|
|
}
|
|
if (c == '.' && (*p0 == '\n' || *p0 == '\r' || islower (*p)))
|
|
{
|
|
*p1++ = ' ';
|
|
++i;
|
|
}
|
|
*p1 = EOS;
|
|
|
|
return (i);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* countesc */
|
|
/*------------------------------*/
|
|
|
|
#define ESC 27
|
|
|
|
countesc (p)
|
|
register char *p;
|
|
{
|
|
|
|
/*
|
|
* count escape sequence characters in given null-terminated
|
|
* string
|
|
*/
|
|
|
|
register char *pp;
|
|
register int num;
|
|
|
|
pp = p;
|
|
num = 0;
|
|
|
|
while (*pp != EOS)
|
|
{
|
|
if (*pp == ESC)
|
|
{
|
|
/*
|
|
* count escape char (vt52 and vt100)
|
|
*/
|
|
switch (*(pp+1))
|
|
{
|
|
case 'A': /* ESC-a */
|
|
case 'B':
|
|
case 'C':
|
|
case 'D':
|
|
case 'E':
|
|
case 'H':
|
|
case 'I':
|
|
case 'J':
|
|
case 'K':
|
|
case 'L':
|
|
case 'M':
|
|
case 'd':
|
|
case 'e':
|
|
case 'f':
|
|
case 'j':
|
|
case 'k':
|
|
case 'l':
|
|
case 'o':
|
|
case 'p':
|
|
case 'q':
|
|
case 'v':
|
|
case 'w':
|
|
num += 2;
|
|
break;
|
|
case 'b': /* ESC-a-b */
|
|
case 'c':
|
|
num += 3;
|
|
break;
|
|
case 'Y': /* ESC-a-b-c */
|
|
case '[': /* Esc [ 7 m */
|
|
num += 4;
|
|
break;
|
|
default:
|
|
num += 1;
|
|
break;
|
|
}
|
|
}
|
|
pp++;
|
|
}
|
|
|
|
return (num);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* itoda */
|
|
/*------------------------------*/
|
|
itoda (value, p, size)
|
|
int value;
|
|
register char *p;
|
|
register int size;
|
|
{
|
|
|
|
/*
|
|
* convert integer to decimal ascii string
|
|
*/
|
|
|
|
register int i;
|
|
register int j;
|
|
register int k;
|
|
register int aval;
|
|
char c[20];
|
|
|
|
aval = abs (value);
|
|
c[0] = EOS;
|
|
i = 1;
|
|
do
|
|
{
|
|
c[i++] = (aval % 10) + '0';
|
|
aval /= 10;
|
|
|
|
} while (aval > 0 && i <= size);
|
|
|
|
if (value < 0 && i <= size)
|
|
c[i++] = '-';
|
|
for (j = 0; j < i; ++j)
|
|
*p++ = c[i - j - 1];
|
|
|
|
return (i);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* itoROMAN */
|
|
/*------------------------------*/
|
|
itoROMAN (value, p, size)
|
|
int value;
|
|
register char *p;
|
|
register int size;
|
|
{
|
|
|
|
/*
|
|
* convert integer to upper roman. must be positive
|
|
*/
|
|
|
|
register int i;
|
|
register int j;
|
|
register int k;
|
|
register int aval;
|
|
char c[100];
|
|
int rem;
|
|
|
|
aval = abs (value);
|
|
c[0] = EOS;
|
|
i = 1;
|
|
|
|
/*
|
|
* trivial case:
|
|
*/
|
|
if (aval == 0)
|
|
{
|
|
c[i++] = '0';
|
|
goto done_100;
|
|
}
|
|
|
|
/*
|
|
* temporarily mod 100...
|
|
*/
|
|
aval = aval % 100;
|
|
|
|
if (aval > 0)
|
|
{
|
|
/*
|
|
* build backward
|
|
*
|
|
* | I| 1
|
|
* | II| 2
|
|
* | III| 3
|
|
* | VI| 4
|
|
* | V| 5
|
|
* | IV| 6
|
|
* | IIV| 7
|
|
* | IIIV| 8
|
|
* | XI| 9
|
|
* | X| 0
|
|
* | IX| 11
|
|
* | IIX| 12
|
|
*/
|
|
if ((aval % 5 == 0) && (aval % 10 != 0))/* 5 */
|
|
c[i++] = 'V';
|
|
else
|
|
{
|
|
rem = aval % 10;
|
|
if (rem == 9) /* 9 */
|
|
{
|
|
c[i++] = 'X';
|
|
c[i++] = 'I';
|
|
}
|
|
else if (rem == 8) /* 8 */
|
|
{
|
|
c[i++] = 'I';
|
|
c[i++] = 'I';
|
|
c[i++] = 'I';
|
|
c[i++] = 'V';
|
|
}
|
|
else if (rem == 7) /* 7 */
|
|
{
|
|
c[i++] = 'I';
|
|
c[i++] = 'I';
|
|
c[i++] = 'V';
|
|
}
|
|
else if (rem == 6) /* 6 */
|
|
{
|
|
c[i++] = 'I';
|
|
c[i++] = 'V';
|
|
}
|
|
else if (rem == 4) /* 4 */
|
|
{
|
|
c[i++] = 'V';
|
|
c[i++] = 'I';
|
|
}
|
|
else /* 3,2,1 */
|
|
{
|
|
for (j = 0; j < rem; j++)
|
|
c[i++] = 'I';
|
|
}
|
|
}
|
|
|
|
aval /= 10;
|
|
if (aval == 0)
|
|
goto done_100;
|
|
|
|
rem = aval % 10;
|
|
if (rem == 4)
|
|
{
|
|
c[i++] = 'L';
|
|
c[i++] = 'X';
|
|
}
|
|
else if (rem == 5)
|
|
{
|
|
c[i++] = 'L';
|
|
}
|
|
else if (rem < 4)
|
|
{
|
|
for (j = 0; j < rem; j++)
|
|
c[i++] = 'X';
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < rem - 5; j++)
|
|
c[i++] = 'X';
|
|
c[i++] = 'L';
|
|
}
|
|
}
|
|
|
|
|
|
done_100:
|
|
/*
|
|
* divide by 100 (they are done) and temp mod by another 10
|
|
*/
|
|
aval = abs (value);
|
|
aval /= 100;
|
|
|
|
if (aval > 0)
|
|
{
|
|
rem = aval % 10;
|
|
if (rem == 4)
|
|
{
|
|
c[i++] = 'D';
|
|
c[i++] = 'C';
|
|
}
|
|
if (rem == 5)
|
|
{
|
|
c[i++] = 'D';
|
|
}
|
|
else if (rem < 4)
|
|
{
|
|
for (j = 0; j < rem; j++)
|
|
c[i++] = 'C';
|
|
}
|
|
else if (rem == 9)
|
|
{
|
|
c[i++] = 'M';
|
|
c[i++] = 'C';
|
|
}
|
|
else if (rem < 9)
|
|
{
|
|
for (j = 0; j < rem - 5; j++)
|
|
c[i++] = 'C';
|
|
c[i++] = 'D';
|
|
}
|
|
}
|
|
|
|
|
|
aval /= 10;
|
|
|
|
if (aval > 0)
|
|
{
|
|
rem = aval % 10;
|
|
if (rem < 4)
|
|
{
|
|
for (j = 0; j < rem; j++)
|
|
c[i++] = 'M';
|
|
}
|
|
}
|
|
|
|
|
|
if (value < 0)
|
|
c[i++] = '-';
|
|
|
|
for (j = 0; j < i; ++j)
|
|
*p++ = c[i - j - 1];
|
|
|
|
return (i);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* itoroman */
|
|
/*------------------------------*/
|
|
itoroman (value, p, size)
|
|
int value;
|
|
char *p;
|
|
int size;
|
|
{
|
|
|
|
/*
|
|
* convert integer to lower roman
|
|
*/
|
|
|
|
register int i;
|
|
register int len;
|
|
register int aval;
|
|
char c[100];
|
|
|
|
c[0] = EOS;
|
|
len = itoROMAN (value, c, size);
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
p[i] = c[i];
|
|
if (isalpha (p[i]))
|
|
p[i] = tolower (c[i]);
|
|
}
|
|
|
|
return (len);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* itoLETTER */
|
|
/*------------------------------*/
|
|
itoLETTER (value, p, size)
|
|
int value;
|
|
register char *p;
|
|
register int size;
|
|
{
|
|
|
|
/*
|
|
* convert integer to upper letter value: 0,A,B,C,...,AA,AB,AC,...
|
|
*/
|
|
|
|
register int i;
|
|
register int j;
|
|
register int k;
|
|
register int aval;
|
|
int rem;
|
|
char c[20];
|
|
|
|
aval = abs (value);
|
|
c[0] = EOS;
|
|
i = 1;
|
|
|
|
/*
|
|
* 1 based:
|
|
*
|
|
* 0 0
|
|
* 1 A
|
|
* 25 Z
|
|
* 26 AA
|
|
* 51 AZ
|
|
* 52 AAA
|
|
* ...
|
|
*/
|
|
if (aval == 0)
|
|
c[i++] = '0';
|
|
else if (aval < 27)
|
|
{
|
|
c[i++] = aval - 1 + 'A';
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
c[i++] = ((aval - 1) % 26) + 'A';
|
|
aval = (aval - 1) / 26;
|
|
|
|
} while (aval > 0 && i <= size);
|
|
}
|
|
|
|
if (value < 0 && i <= size)
|
|
c[i++] = '-';
|
|
|
|
for (j = 0; j < i; ++j)
|
|
*p++ = c[i - j - 1];
|
|
|
|
return (i);
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* itoletter */
|
|
/*------------------------------*/
|
|
itoletter (value, p, size)
|
|
int value;
|
|
register char *p;
|
|
register int size;
|
|
{
|
|
|
|
/*
|
|
* convert integer to upper letter value: 0,a,b,c,...,aa,ab,ac,...
|
|
*/
|
|
|
|
register int i;
|
|
register int j;
|
|
register int k;
|
|
register int aval;
|
|
char c[20];
|
|
int rem;
|
|
|
|
aval = abs (value);
|
|
c[0] = EOS;
|
|
i = 1;
|
|
|
|
/*
|
|
* 1 based:
|
|
*
|
|
* 0 0
|
|
* 1 A
|
|
* 25 Z
|
|
* 26 AA
|
|
* 51 AZ
|
|
* 52 AAA
|
|
* ...
|
|
*/
|
|
if (aval == 0)
|
|
c[i++] = '0';
|
|
else if (aval < 27)
|
|
{
|
|
c[i++] = aval - 1 + 'a';
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
c[i++] = ((aval - 1) % 26) + 'a';
|
|
aval = (aval - 1) / 26;
|
|
|
|
} while (aval > 0 && i <= size);
|
|
}
|
|
|
|
if (value < 0 && i <= size)
|
|
c[i++] = '-';
|
|
|
|
for (j = 0; j < i; ++j)
|
|
*p++ = c[i - j - 1];
|
|
|
|
return (i);
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* min */
|
|
/*------------------------------*/
|
|
|
|
#ifdef min
|
|
#undef min
|
|
#endif
|
|
|
|
min (v1, v2)
|
|
register int v1;
|
|
register int v2;
|
|
{
|
|
|
|
/*
|
|
* find minimum of two integer ONLY
|
|
*/
|
|
|
|
return ((v1 < v2) ? v1 : v2);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* max */
|
|
/*------------------------------*/
|
|
|
|
#ifdef max
|
|
#undef max
|
|
#endif
|
|
|
|
max (v1, v2)
|
|
register int v1;
|
|
register int v2;
|
|
{
|
|
|
|
/*
|
|
* find maximum of two integers ONLY
|
|
*/
|
|
|
|
return ((v1 > v2) ? v1 : v2);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* err_exit */
|
|
/*------------------------------*/
|
|
err_exit (code)
|
|
{
|
|
|
|
/*
|
|
* exit cleanly on fatal error (close files, etc). also handles normal
|
|
* exit.
|
|
*/
|
|
|
|
if (err_stream != stderr && err_stream != (FILE *) 0)
|
|
{
|
|
/*
|
|
* not going to stderr (-o file)
|
|
*/
|
|
fflush (err_stream);
|
|
fclose (err_stream);
|
|
}
|
|
if (debugging && dbg_stream != stderr && dbg_stream != (FILE *) 0)
|
|
{
|
|
fflush (dbg_stream);
|
|
fclose (dbg_stream);
|
|
}
|
|
if (out_stream != stdout && out_stream != (FILE *) 0)
|
|
{
|
|
/*
|
|
* not going to stdout (-l)
|
|
*/
|
|
fflush (out_stream);
|
|
fclose (out_stream);
|
|
}
|
|
|
|
if (hold_screen)
|
|
{
|
|
wait_for_char ();
|
|
}
|
|
|
|
exit (code);
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------*/
|
|
/* wait_for_char */
|
|
/*------------------------------*/
|
|
#ifdef GEMDOS
|
|
#include <osbind.h>
|
|
#endif
|
|
|
|
wait_for_char ()
|
|
{
|
|
#ifdef GEMDOS
|
|
printf ("enter any key..."); fflush (stdout);
|
|
Cconin ();
|
|
#endif
|
|
}
|
|
|
|
|
|
|