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);
 | |
| }
 | 
