gno/usr.bin/nroff/low.c

930 lines
15 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
* - Heavily modified by Devin Reade to avoid memory trashing bugs.
*
* $Id: low.c,v 1.2 1997/03/20 06:40:50 gdr Exp $
*/
#ifdef __ORCAC__
segment "low_______";
#pragma noroot
#pragma optimize 79
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef __GNO__
#include <err.h>
#else
#include "unix/err.h"
#endif
#ifdef sparc
#include "unix/sunos.h"
#endif
#include "nroff.h"
/* convert ascii character to decimal */
#define ATOD(c) (((c) < '0') || ((c) > '9')) ? -1 : ((c) - '0')
#if 0 /* not currently used */
static void inptobu (char *); /* convert input units to b.u. */
static void butochar (char *); /* convert b.u. to char spaces */
#endif
/*
* ctod
*
* convert string to decimal. processes only positive values.
* this takes a constant like "1", "1.0i", etc.
*
* Returns converted value (including zero) on success, zero on failure.
*/
int
ctod (char *p) {
long val;
int d;
char *pp = p;
char *ptmp;
int rside = 0;
int lside = 0;
int has_rside = 0;
int has_lside = 0;
if (*p == EOS) {
return 0;
}
ptmp = skipwd (pp);
pp = --ptmp;
switch (*pp) {
case 'i':
case 'c':
val = 0L;
while (*p != EOS && isdigit (*p)) {
has_lside++;
lside = ATOD(*p);
p++;
if (lside == -1) {
break;
}
val = 10L * val + (long) lside;
}
lside = (int) val;
if (*p == '.') {
p++;
val = 0L;
while (*p != EOS && isdigit (*p)) {
has_rside++;
rside = ATOD(*p);
p++;
if (rside == -1) {
break;
}
val = 10L * val + (long) rside;
if (has_rside > 2) { /* more than enough */
break;
}
}
rside = (int) val;
}
/*
* now put it together. 1.0i -> 240, 1.50i -> 360, etc.
*/
val = 0L;
if (has_lside) {
val = (long) lside * BU_INCH;
}
switch (has_rside) {
case 1:
val = val + ((long) rside * BU_INCH / 10L);
break;
case 2:
val = val + ((long) rside * BU_INCH / 100L);
break;
case 3:
val = val + ((long) rside * BU_INCH / 1000L);
break;
default:
break;
}
if (*pp == 'c') {
val = (val * BU_CM) / BU_INCH;
}
/*
* for now we convert to basic char size, 1 em...
*/
val = val / BU_EM;
break;
case 'P':
case 'm':
case 'n':
case 'p':
case 'u':
case 'v':
val = 0L;
while (*p != EOS) {
d = ATOD(*p);
p++;
if (d == -1)
break;
val = 10L * val + (long) d;
}
switch (*pp) {
case 'P':
val = val * BU_PICA;
break;
case 'p':
val = val * BU_POINT;
break;
case 'u':
val = val * BU_BU;
break;
case 'm':
val = val * BU_EM;
break;
case 'n':
val = val * BU_EN;
break;
case 'v':
val = val * BU_EM;
break;
}
/*
* for now we convert to basic char size, 1 em...
*/
val = val / BU_EM;
break;
default:
/*
* this is the default behavior. it SHOULD make things
* compatible with the old way...
*/
val = 0L;
while (*p != EOS) {
d = ATOD(*p);
p++;
if (d == -1) {
break;
}
val = 10L * val + (long) d;
}
break;
}
return (int) val;
}
#if 0 /* not currently used */
/*
* inptobu
*
* convert input units to b.u.
*/
static void
inptobu (char *ps)
{
return;
}
/*
* butochar
*
* convert b.u. to char spaces
*/
static void
butochar (char *ps)
{
return;
}
#endif /* 0 */
/*
* skipbl
*
* 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.
*/
char *
skipbl (register char *p)
{
while ((*p != EOS) && (*p == ' ' || *p == '\t')) {
p++;
}
return (p);
}
/*
* skipwd
*
* skip over word and punctuation. anything but space,\t,\r,\n, and EOS
* is skipped. return ptr to the first of these found.
*/
char *
skipwd (register char *p) {
while (*p != EOS && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n') {
p++;
}
return (p);
}
/*
* nroffSpace
*
* space vertically n lines. this does header and footer also.
*/
void
nroffSpace (int n) {
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
* get field from title
*/
char *
getfield (register char *p, register char *q, char delim) {
while (*p != delim && *p != '\r' && *p != '\n' && *p != EOS) {
*q++ = *p++;
}
*q = EOS;
if (*p == delim) {
++p;
}
return p;
}
/*
* getwrd
*
* get non-blank word from p0 into p1.
* return number of characters processed.
*/
int
getwrd (register char *src, register char *dest) {
#if 0 /* gdr changes */
char *orgsrc, *orgdest;
orgsrc = src;
orgdest = dest;
/*
* skip leading whitespace
*/
while (*src == ' ' || *src == '\t') {
src++;
}
/* find end of word */
while (*src != '\0' && !isspace(*src)) {
*dest++ = *src++;
}
/* I don't understand what this is supposed to achieve */
#ifdef WHATTHEFUCK
assert (dest - orgdest >= 1); /* gdr */
c = *(dest - 1);
if (c == '"') {
asssert(dest - orgdest >= 2); /* gdr */
c = *(dest - 2);
}
if (c == '?' || c == '!') {
*dest++ = ' ';
}
if (c == '.' && (*src == '\n' || *src == '\r' || islower (*p))) {
*dest++ = ' ';
}
#endif /* WHATTHEFUCK */
*dest = EOS;
return src - orgsrc;
#elif 1
char c;
register int i;
register char *p;
char *orgsrc, *orgdest;
orgsrc = src;
orgdest = dest;
/*
* init counter...
*/
i = 0;
/*
* skip leading whitespace
*/
while (*src == ' ' || *src == '\t') {
++i;
++src;
}
/*
* set ptr and start to look for end of word
*/
p = src;
while (*src != ' ' && *src != EOS && *src != '\t') {
if (*src == '\n' || *src == '\r') {
break;
}
*dest = *src++;
++dest;
++i;
}
if (dest > orgdest) {
c = *(dest - 1);
if (c == '"' && (dest > orgdest + 1)) {
c = *(dest - 2);
}
if (c == '?' || c == '!') {
*dest++ = ' ';
++i;
}
if (c == '.' && (*src == '\n' || *src == '\r' || islower (*p))) {
*dest++ = ' ';
++i;
}
}
*dest = EOS;
return (i);
#else
char c;
register int i;
register char *p;
char *orgsrc, *orgdest;
orgsrc = src;
orgdest = dest;
/*
* init counter...
*/
i = 0;
/*
* skip leading whitespace
*/
while (*src == ' ' || *src == '\t') {
++i;
++src;
}
/*
* set ptr and start to look for end of word
*/
p = src;
while (*src != ' ' && *src != EOS && *src != '\t') {
if (*src == '\n' || *src == '\r') {
break;
}
*dest = *src++;
++dest;
++i;
}
if (dest > orgdest) {
c = *(dest - 1);
if (c == '"') {
ASSERT(dest > orgdest + 1,
("%s:%d: array indexing error", __FILE__, __LINE__));
c = *(dest - 2);
}
if (c == '?' || c == '!') {
*dest++ = ' ';
++i;
}
if (c == '.' && (*src == '\n' || *src == '\r' || islower (*p))) {
*dest++ = ' ';
++i;
}
}
*dest = EOS;
return (i);
#endif
}
/*
* countesc
* count atari escape sequence characters in given null-terminated
* string
*/
#define ESC 27
int
countesc (register char *p) {
register char *pp;
register int num;
pp = p;
num = 0;
while (*pp != EOS) {
if (*pp == ESC) {
/*
* count escape char (atari-specific, vt52)
* generally only p,q,b,and c will show up...
*/
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
* convert integer to decimal ascii string
*
* Pre: <value> is the number to convert
* <p> is the output buffer
* <size> is the number of bytes in the output buffer
*
* Post: <p> contains an ascii representation of <value>
*
* Returns:
*/
int
itoda (int value, register char *p, register int size) {
#if 0
int len;
/*
* buffer must be big enough for representation of largest integer,
* plus 1 byte for sign, plus 1 byte for terminator. On a 32-bit
* machine this is 10+1+1 == 12 bytes.
*/
#define MAX_INT_LENGTH 15
char buffer[MAX_INT_LENGTH];
len = sprintf(buffer, "%d", value);
if (len == EOF) {
errx(1, "itoda failed with EOF");
}
if (len >= size) {
errx(1, "%s:%d: buffer overflow", __FILE__, __LINE__);
}
strncpy(p, buffer, len);
return len;
#else
int i, j, 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;
#endif
}
/*
* itoROMAN
* convert integer to upper roman. must be positive
*/
int
itoROMAN (int value, register char *p, register int size) {
register int i;
register int j;
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
* convert integer to lower roman
*/
int
itoroman (int value, char *p, int size)
{
register int i;
register int len;
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
* convert integer to upper letter value: 0,A,B,C,...,AA,AB,AC,...
*/
int
itoLETTER (int value, register char *p, register int size) {
register int i;
register int j;
register int aval;
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
* convert integer to upper letter value: 0,a,b,c,...,aa,ab,ac,...
*/
int
itoletter (int value, register char *p, register int size) {
register int i;
register int j;
register int aval;
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;
}
#if 0 /* min, max not needed */
/*
* min
* find minimum of two integer ONLY
*/
#ifdef min
#undef min
#endif
int
min (register int v1, register int v2) {
return ((v1 < v2) ? v1 : v2);
}
/*
* max
* find maximum of two integers ONLY
*/
#ifdef max
#undef max
#endif
int
max (register int v1, register int v2) {
return ((v1 > v2) ? v1 : v2);
}
#endif /* 0 */
/*
* err_exit
* exit cleanly on fatal error (close files, etc). also handles normal
* exit.
*/
void
err_exit (int code) {
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);
}
#ifdef GEMDOS
if (hold_screen) {
wait_for_char();
}
#endif
exit(code);
}
#ifdef GEMDOS
#include <osbind.h>
/*
* Wait for a character
*/
void
wait_for_char (void) {
printf ("enter any key..."); fflush (stdout);
Cconin ();
}
#endif