489 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			489 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/*
 | 
						|
 *	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.0";
 | 
						|
 | 
						|
#include "cawf.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
 | 
						|
 | 
						|
 | 
						|
main(argc, argv)
 | 
						|
	int     argc;
 | 
						|
	char    *argv[];
 | 
						|
{
 | 
						|
	char *ep;               	/* environment pointer */
 | 
						|
	int fff = 0;			/* final form feed status */
 | 
						|
	char **files;			/* 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 */
 | 
						|
	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 */
 | 
						|
/*
 | 
						|
 * Save program name.
 | 
						|
 */
 | 
						|
	if ((Pname = strrchr(argv[0], '\\')) != NULL)
 | 
						|
		Pname++;
 | 
						|
	else if ((Pname = strrchr(argv[0], '/')) != NULL)
 | 
						|
		Pname++;
 | 
						|
	else
 | 
						|
		Pname = argv[0];
 | 
						|
/*
 | 
						|
 * 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);
 | 
						|
	}
 | 
						|
	if ((files = malloc((argc + 2) * sizeof(files[0]))) == NULL)
 | 
						|
		Error(FATAL, NOLINE, " no space for file list",
 | 
						|
			NULL);
 | 
						|
	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:")) != EOF) {
 | 
						|
		switch (i) {
 | 
						|
	/*
 | 
						|
	 * -c<device_configuration_file_path>>
 | 
						|
	 */
 | 
						|
		case 'c':
 | 
						|
			Devconf = optarg;
 | 
						|
			break;
 | 
						|
	/*
 | 
						|
	 * -d<output_device_name> -- define output device name
 | 
						|
	 *
 | 
						|
	 * 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<output_device_font_name> -- define font name for the output
 | 
						|
	 *				  device (from device configuration
 | 
						|
	 *				  file)
 | 
						|
	 */
 | 
						|
		case 'f':
 | 
						|
			Devfont = optarg;
 | 
						|
			break;
 | 
						|
	/*
 | 
						|
	 * -h -- display help (usage)
 | 
						|
	 */
 | 
						|
		case 'h':
 | 
						|
			help = 1;
 | 
						|
			break;
 | 
						|
	/*
 | 
						|
	 * -m<macro_file_name>
 | 
						|
	 *
 | 
						|
	 *  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, "s") == 0
 | 
						|
			     ||  strcmp(optarg, "e") == 0)
 | 
						|
				Marg = MSMACROS;
 | 
						|
			mac++;
 | 
						|
			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>] file...\n",
 | 
						|
		Pname, Version);
 | 
						|
	  (void) fprintf(stderr,
 | 
						|
	    "\t-c<c>     <c> is the device configuration file path\n");
 | 
						|
	  (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<m> is the macro file name\n");
 | 
						|
	  (void) fprintf(stderr,
 | 
						|
	    "\tfile ...  source file names\n");
 | 
						|
	  exit(Err);
 | 
						|
	}
 | 
						|
	if (mac == 0) {
 | 
						|
 | 
						|
	    /*
 | 
						|
	     * No macroes - enable Bold, Italic, Roman and Courier fonts.
 | 
						|
	     */
 | 
						|
		for (i = 0; Fcode[i].nm; i++) {
 | 
						|
			switch (Fcode[i].nm) {
 | 
						|
			case 'B':
 | 
						|
			case 'I':
 | 
						|
			case 'R':
 | 
						|
			case 'C':
 | 
						|
				Fcode[i].status = '1';
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
/*
 | 
						|
 * Add user-supplied file names.
 | 
						|
 */
 | 
						|
	pc = nf;
 | 
						|
	if (optind >= argc) {
 | 
						|
		files[nf++] = NULL;       /* STDIN */
 | 
						|
	} else {
 | 
						|
		while (optind < argc)
 | 
						|
			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) {
 | 
						|
		for (i = 0; i < Fstr.il; i++) {
 | 
						|
			Charput((int)Fstr.i[i]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	Macro((unsigned char *)".^x");
 | 
						|
	Macro((unsigned char *)".^b");
 | 
						|
	Macro((unsigned char *)".^# 1 <prolog>");
 | 
						|
/*
 | 
						|
 * Read input files.
 | 
						|
 */
 | 
						|
	for (i = 0; i < nf; i++) {
 | 
						|
		Dowarn = (i >= pc);
 | 
						|
		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((int)'\f');
 | 
						|
	exit(Err);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * 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 i, j, k;                    /* temporary indexes */
 | 
						|
	int mx;                         /* Macrotab[] index */
 | 
						|
	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 ignore.
 | 
						|
  */
 | 
						|
	if (req && inp[1] == 'i' && inp[2] == 'g') {
 | 
						|
		while (fgets((char *)inp, MAXLINE, Ifs) != NULL) {
 | 
						|
			NR++;
 | 
						|
			if (inp[0] == '.' && inp[1] == '.') break;
 | 
						|
		}
 | 
						|
		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
 | 
						|
	&&  (((mx = Findmacro(inp+1, 0)) != -1) || regexec(Pat[0].pat, inp))) {
 | 
						|
		Expand(inp);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
/*
 | 
						|
 * None of the above: forward the line.
 | 
						|
 */
 | 
						|
	Pass2(inp);
 | 
						|
}
 |