home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
cawf407.zip
/
src
/
cawf.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-12-28
|
15KB
|
658 lines
/*
* cawf - a C version of Henry Spencer's awf(1), the Amazingly
* Workable (text) Formatter
*
* V. Abell, Purdue University Computing Center
*/
/*
* Copyright (c) 1991 Purdue University Research Foundation,
* West Lafayette, Indiana 47907. All rights reserved.
*
* Written by Victor A. Abell <abe@mace.cc.purdue.edu>, Purdue
* University Computing Center. Not derived from licensed software;
* derived from awf(1) by Henry Spencer of the University of Toronto.
*
* Permission is granted to anyone to use this software for any
* purpose on any computer system, and to alter it and redistribute
* it freely, subject to the following restrictions:
*
* 1. The author is not responsible for any consequences of use of
* this software, even if they arise from flaws in it.
*
* 2. The origin of this software must not be misrepresented, either
* by explicit claim or by omission. Credits must appear in the
* documentation.
*
* 3. Altered versions must be plainly marked as such, and must not
* be misrepresented as being the original software. Credits must
* appear in the documentation.
*
* 4. This notice may not be removed or altered.
*/
static char Version[] = "4.07";
#include "cawf.h"
#include <time.h>
#include <sys/stat.h>
#ifndef UNIX
#include <io.h>
#include <process.h>
#include <string.h>
#include <sys\types.h>
#include <sys\stat.h>
#endif
static char *Month[] =
{ "January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December" };
main(argc, argv)
int argc;
char *argv[];
{
char dt[32]; /* date for td and DY strings */
char *ep; /* environment pointer */
int fff = 0; /* final form feed status */
char *files[MAXFILES]; /* file names */
int help = 0; /* help status */
int i; /* temporary index */
size_t l; /* length */
char *lib = CAWFLIB; /* library path */
int libl; /* library path length */
struct tm *lt; /* local time structure pointer */
int mac = 0; /* macro specification status */
int nf = 0; /* number of files */
char *np; /* name pointer */
int pc; /* prolog count */
struct stat sbuf; /* stat buffer */
time_t tm; /* system time of day */
/*
* Save program name.
*/
if ((Pname = strrchr(argv[0], '\\')) != NULL)
Pname++;
else if ((Pname = strrchr(argv[0], '/')) != NULL)
Pname++;
else
Pname = argv[0];
#ifdef __EMX__
/*
* Perform OS2 initialization:
*
* expand response files;
* do file name glob'bing
*/
_response(&argc, &argv);
_wildcard(&argc, &argv);
#endif /* __EMX__ */
/*
* Set error file stream pointer.
*/
Efs = stderr;
/*
* Get library name.
*/
if ((np = getenv("CAWFLIB")) != NULL)
lib = np;
libl = strlen(lib);
/*
* Get device file name.
*/
for (ep = getenv("TERM");; ep = NULL) {
if (ep == NULL || *ep == '\0')
ep = "dumb";
l = libl + 1 + strlen(ep) + strlen(".dev") + 1;
if ((np = malloc(l)) == NULL)
Error(FATAL, NOLINE,
" no string space for device file: ", ep);
(void) sprintf(np, "%s/%s.dev", lib, ep);
if (stat(np, &sbuf) == 0)
break;
if (strcmp(ep, "dumb") == 0)
Error(FATAL, NOLINE, " no dumb.dev file in ", lib);
(void) free(np);
}
files[nf++] = np;
/*
* Get common text file name.
*/
l = libl + 1 + strlen("common") + 1;
if ((np = malloc(l)) == NULL)
Error(FATAL, NOLINE, " no string space for common file name",
NULL);
(void) sprintf(np, "%s/common", lib);
files[nf++] = np;
/*
* Process options.
*/
while ((i = getopt(argc, argv, "c:d:ef:hm:n:o:")) != EOF) {
switch (i) {
/*
* -c c
*
* Set the device configuration file path to c.
*/
case 'c':
Devconf = optarg;
break;
/*
* -d d
*
* Set the output device name to d.
*
* The default output device name is NORMAL -- i.e., a device that
* does bold face with backspace and overprinting and italic face with
* underscore. NORMAL is usually a terminal device.
*
* There is a built-in device, named ANSI, that does bold face with
* the ANSI shadow mode and italic face with the ANSI underscore mode.
* ANSI is normally a terminal device that supports the ANSI shadow
* and underscore modes.
*
* There is a built-in output device, named NONE, that does nothing
* at all for the bold or italic faces. This is usually a terminal
* device.
*
* All other device names must match a stanza in the device
* configuration file.
*/
case 'd':
Device = optarg;
break;
/*
* -e -- eject: issue final form feed
*/
case 'e':
fff = 1;
break;
/*
* -f f
*
* Set f as the font name for the output device (from the device
* configuration file).
*/
case 'f':
Devfont = optarg;
break;
/*
* -h -- display help (usage)
*/
case 'h':
help = 1;
break;
/*
* -m m
*
* Set the macro file name to m.
*
* Special support is provided for -man, -me and -ms.
*/
case 'm':
if (mac) {
Error(WARN, NOLINE,
"multiple macro file declaration",
NULL);
break;
}
l = libl + 2 + strlen(optarg) + strlen(".mac") + 1;
if ((np = malloc(l)) == NULL)
Error(FATAL, NOLINE, " no string space for ",
argv[1]);
(void) sprintf(np, "%s/m%s.mac", lib, optarg);
files[nf++] = np;
if (strcmp(optarg, "an") == 0)
Marg = MANMACROS;
else if (strcmp(optarg, "e") == 0)
Marg = MEMACROS;
else if (strcmp(optarg, "s") == 0)
Marg = MSMACROS;
mac++;
break;
/*
* -n n
*
* Set the starting page number to n.
*/
case 'n':
Thispg = atoi(optarg);
break;
/*
* -o o
*
* Set the pages to print list from the string o.
*
* Entries are separated by commas. A range is specified as n-m.
* A starting range of the form -m has one for a starting value,
* and a range of the form n- has an ending value equivalent to
* the end of the document. For example:
*
* -5,8,10-
*
* specifies the printing of pages 1 through 5, 8, and 10 through
* the end of the document.
*/
case 'o':
if (!AsmPgRange(optarg))
Error(WARN, NOLINE, "invalid page list: ",
optarg);
break;
/*
* Option not recognized by getopt().
*/
case '?':
Err = 1;
}
}
if (Defdev())
Err++;
if (help || Err) {
(void) fprintf(stderr,
"%s %s usage: [-c c] [-d d] [-e] [-f f] [-h] [-m m]\n",
Pname, Version);
(void) fprintf(stderr, "\t[-n n] [-o o] file...\n");
(void) fprintf(stderr,
"\t-c c c is the device configuration file path\n");
(void) fprintf(stderr,
"\t (default = %s/%s)\n", CAWFLIB, DEVCONFIG);
(void) fprintf(stderr,
"\t-d d d is the output device name\n");
(void) fprintf(stderr,
"\t (default = NORMAL, using \\b for bold and italic)\n");
(void) fprintf(stderr,
"\t (built-ins = ANSI, NONE and NORMAL)\n");
(void) fprintf(stderr,
"\t-e issue eject after last page\n");
(void) fprintf(stderr,
"\t-f f f is the output device font name\n");
(void) fprintf(stderr,
"\t-h display help (this output)\n");
(void) fprintf(stderr,
"\t-m m m is the macro file name\n");
(void) fprintf(stderr,
"\t-n n n is the starting page number\n");
(void) fprintf(stderr,
"\t-o o o specifies output pages and page ranges\n");
(void) fprintf(stderr,
"\tfile ... source file names\n");
exit(Err);
}
if (mac == 0) {
/*
* No macros - enable Bold, Italic and Roman fonts.
*/
for (i = 0; Fcode[i].nm; i++) {
switch (Fcode[i].nm) {
case 'B':
case 'I':
case 'R':
Fcode[i].status = '1';
}
}
}
/*
* Add user-supplied file names.
*/
pc = nf;
if (optind >= argc) {
files[nf++] = NULL; /* STDIN */
} else {
while (optind < argc) {
if (nf >= MAXFILES)
Error(WARN, NOLINE, " too many files at ",
argv[optind]);
files[nf++] = argv[optind++];
}
}
/*
* Make sure all input files are accessible.
*/
for (i = 0; i < nf; i++) {
if (files[i] != NULL) {
if (stat(files[i], &sbuf) != 0)
Error(WARN, NOLINE, " can't find ", files[i]);
}
}
if (Err)
exit(1);
/*
* Miscellaneous initialization.
*/
for (i = 0; ; i++) {
if (Pat[i].re == NULL)
break;
if ((Pat[i].pat = regcomp(Pat[i].re)) == NULL)
Error(WARN, NOLINE, Pat[i].re, " regcomp failure");
}
if ((i = Findscale((int)'n', 0.0, 0)) < 0)
Error(WARN, NOLINE, " can't find Scale['n']", NULL);
Scalen = Scale[i].val;
if ((i = Findscale((int)'u', 0.0, 0)) < 0)
Error(WARN, NOLINE, " can't find Scale['u']", NULL);
Scaleu = Scale[i].val;
if ((i = Findscale((int)'v', 0.0, 0)) < 0)
Error(WARN, NOLINE, " can't find Scale['v']", NULL);
Scalev = Scale[i].val;
(void) Findstr((unsigned char *)"CH", (unsigned char *)"= % -", 1);
Cont = Newstr((unsigned char *)" ");
Contlen = 1;
if ((Trtbl = (unsigned char *)malloc(256)) == NULL)
Error(WARN, NOLINE, " can't allocate translate table space",
NULL);
else {
*Trtbl = ' ';
for (i = 1; i < 256; i++)
Trtbl[i] = (unsigned char) i;
}
if (Err)
exit(1);
/*
* Here begins pass1 of awf - reading input lines and expanding macros.
*/
/*
* Output prolog.
*/
if (Fstr.i)
Stringput(Fstr.i, Fstr.il);
Macro((unsigned char *)".^x");
Macro((unsigned char *)".^b");
Macro((unsigned char *)".^# 1 <prolog>");
/*
* Put today's date in the -me td string or the -ms DY string.
*/
if (Marg == MEMACROS || Marg == MSMACROS) {
tm = time(NULL);
lt = localtime(&tm);
(void) sprintf(dt, "%s %d, %d",
Month[lt->tm_mon], lt->tm_mday, lt->tm_year + 1900);
Findstr(
(Marg == MEMACROS) ? (unsigned char *)"td"
: (unsigned char *)"DY",
(unsigned char *)dt, 1);
}
/*
* Read input files.
*/
for (i = 0; i < nf; i++) {
if (files[i] == NULL) {
np = "stdin";
Ifs = stdin;
} else {
#ifdef UNIX
if ((Ifs = fopen(files[i], "r")) == NULL)
#else
if ((Ifs = fopen(files[i], "rt")) == NULL)
#endif
Error(FATAL, NOLINE, " can't open ", files[i]);
np = files[i];
}
if (i >= pc) {
(void) sprintf((char *)Line, ".^# 1 %s", np);
Macro(Line);
NR = 0;
}
Fsp = 0;
do {
while (fgets((char *)Line, MAXLINE, Ifs) != NULL) {
NR++;
if ((np = strrchr((char *)Line, '\n')) != NULL)
*np = '\0';
else
Line[MAXLINE-1] = '\0';
Macro(Line);
}
if (i >= pc)
Macro((unsigned char *)".^e");
if (Ifs != stdin)
(void) fclose(Ifs);
if (Fsp > 0) {
Free(&Inname);
Inname = Inn_stk[Fsp-1];
NR = NR_stk[Fsp-1];
Ifs = Ifs_stk[Fsp-1];
}
} while (Fsp-- > 0);
}
Macro(NULL);
if (fff)
Charput("\f");
return(Err);
}
/*
* AsmPg(pa, pb) - assemble an ASCII page number
*/
char *
AsmPg(pa, pb)
char *pa; /* page number in ASCII */
int *pb; /* page number in binary */
{
for (*pb = 0; *pa; pa++) {
if (*pa < '0' || *pa > '9')
return(pa);
*pb = (*pb * 10) + (int)(*pa - '0');
}
return(pa);
}
/*
* AsmPgRange(s) - assemble a page range
*/
int
AsmPgRange(s)
char *s; /* page range string */
{
char *cp, *tbuf;
int pg, pl, pu;
struct pgrange *pr;
if (s == NULL || (pl = strlen(s)) == 0)
return(1);
/*
* Remove spaces from the range string.
*/
if ((tbuf = (char *)malloc(pl + 1)) == NULL)
Error(FATAL, NOLINE, " no string space for page range", NULL);
for (cp = tbuf; *s; *s++) {
if (*s != ' ')
*cp++ = *s;
}
*cp = '\0';
/*
* Parse the range string.
*/
for (cp = tbuf; *cp;) {
/*
* Get the starting value.
* A missing initial range value defaults to one.
*/
if (*cp == '-')
pg = 1;
else
cp = AsmPg(cp, &pg);
/*
* Base further processing on the range terminator character.
*/
switch (*cp) {
case ',': /* single page number */
case '\0':
pl = pu = pg;
break;
case '-': /* page number range */
cp++;
pl = pg;
/*
* Assemble the terminating page number. A missing
* number defaults to page 1,000,000 -- the end of
* the document.
*/
if (!*cp) {
pu = 10000;
break;
}
cp = AsmPg(cp, &pg);
pu = pg;
break;
default:
return(0);
}
/*
* Check the page numbers.
*/
if (pl < 0 || pu < 0 || pl > pu)
return(0);
if (*cp) {
if (*cp != ',')
return(0);
cp++;
}
/*
* Allocate, fill in, and connect a page range structure.
*/
if ((pr = (struct pgrange *)malloc(sizeof(struct pgrange)))
== NULL) {
Error(FATAL, NOLINE, " no space for page range entry",
NULL);
}
pr->l = pl;
pr->u = pu;
pr->next = PgRange;
PgRange = pr;
}
return(1);
}
/*
* Macro(inp) - process a possible macro statement
* pass non-macros and macros alike to pass 2
*/
void
Macro(inp)
unsigned char *inp; /* possible macro statement pointer */
{
unsigned char c[2]; /* characters */
int endm; /* end of macro status */
FILE *fs; /* temporary file stream */
int req; /* request character status */
unsigned char *s1, *s2; /* temporary string pointers */
if (inp == NULL) {
Pass2(NULL);
return;
}
req = (*inp == '.' || *inp == '\'') ? 1 : 0;
/*
* Check for file name designator.
*/
if (req && inp[1] == '^' && inp[2] == '#') {
Free(&Inname);
Inname = Field(3, inp, 1);
F = NULL;
Pass2(inp);
return;
}
/*
* Check for source command - "^[.']so".
*/
if (req && inp[1] == 's' && inp[2] == 'o') {
if ((s1 = Field(2, inp, 1)) == NULL) {
Error(WARN, LINE, " no file specified", NULL);
return;
}
if ((fs = fopen((char *)s1, "r")) == NULL) {
Error(WARN, LINE, " can't open", NULL);
return;
}
if (Fsp >= MAXFSTK) {
(void) fclose(fs);
Error(WARN, LINE, " nesting too deep", NULL);
return;
}
Ifs_stk[Fsp] = Ifs;
Ifs = fs;
Inn_stk[Fsp] = Inname;
Inname = F;
F = NULL;
NR_stk[Fsp++] = NR;
NR = 0;
return;
}
/*
* Check for start of macro definition.
*/
if (req && inp[1] == 'd' && inp[2] == 'e') {
if (inp[3] != ' ' || inp[4] == '\0') {
Error(WARN, LINE, " illegal macro definition", NULL);
return;
}
c[0] = inp[4];
c[1] = inp[5];
Curmx = Findmacro(c, 1);
return;
}
/*
* Check for macro text. Remove double backslashes.
*/
if (req && (inp[1] == '\0' || (inp[2] == '\0' && inp[0] == inp[1])))
endm = 1;
else
endm = 0;
if (Curmx >= 0 && !endm) {
if (Mtx >= MAXMTXT)
Error(FATAL, LINE, " out of macro text space", NULL);
if ((s1 = (unsigned char *)strchr((char *)inp, '\\')) == NULL)
Macrotxt[Mtx] = Newstr(inp);
else {
for (s1 = Pass1ln, s2 = inp;; s1++) {
if ((*s1 = *s2++) == '\0')
break;
if (*s1 == '\\' && *s2 == '\\')
s2++;
}
Macrotxt[Mtx] = Newstr(Pass1ln);
}
if (Macrotab[Curmx].bx == -1)
Macrotab[Curmx].bx = Mtx;
Mtx++;
Macrotab[Curmx].ct++;
return;
}
/*
* Check for end of macro.
*/
if (Curmx >= 0 && endm) {
Curmx = -1;
(void) sprintf((char *)Pass1ln, ".^# %d %s", NR, Inname);
Pass2(Pass1ln);
return;
}
/*
* Check for conditionals and macro expansions.
*/
if (req
&& (Findmacro(inp+1, 0) != -1 || regexec(Pat[0].pat, inp))) {
Expand(inp);
return;
}
/*
* None of the above: forward the line.
*/
Pass2(inp);
}