/* * 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 #include #include #include #ifdef __GNO__ #include #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: is the number to convert *

is the output buffer * is the number of bytes in the output buffer * * Post:

contains an ascii representation of * * 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 /* * Wait for a character */ void wait_for_char (void) { printf ("enter any key..."); fflush (stdout); Cconin (); } #endif