/*
 *	device.c -- cawf(1) output device support functions
 */

/*
 *	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.
 */

#include "cawf.h"
#include <ctype.h>

_PROTOTYPE(static unsigned char *Convstr,(char *s, int *len));
_PROTOTYPE(static int Convfont,(char *nm, char *s, char **fn,
	unsigned char **fi));

#ifndef	UNIX
#define	strcasecmp	strcmpi
#endif



/*
 * Convstr(s, len) - convert a string
 */

static unsigned char *
Convstr(s, len)
	char *s;			/* input string */
	int *len;			/* length of result */
{
	int c;				/* character assembly */
	unsigned char *cp;		/* temporary character pointer */
	char *em;			/* error message */
	int i;				/* temporary index */
	int l;				/* length */
	unsigned char *r;		/* result string */
/*
 * Make space for the result.
 */
	if ((r = (unsigned char *)malloc(strlen((char *)s) + 1)) == NULL) {
		(void) fprintf(stderr, "%s: out of string space at %s\n",
			Pname, s);
		return(NULL);
	}
/*
 * Copy the input string to the result, processing '\\' escapes.
 */
	for (cp = r, l = 0; *s;) {
		switch (*s) {

		case '\\':
			s++;
			if (*s >= '0' && *s <= '7') {
		/*
		 * '\xxx' -- octal form
		 */
				for (c = i = 0; i < 3; i++, s++) {
					if (*s < '0' || *s > '7') {
						em = "non-octal char";
bad_string:
						(void) fprintf(stderr,
							"%s: %s : %s\n",
							Pname, em, (char *)r);
						return(NULL);
					}
					c = (c << 3) + *s - '0';
				}
				if (c > 0377) {
					em = "octal char > 0377";
					goto bad_string;
				}
				*cp++ = c;
				l++;
			} else if (*s == 'x') {
		/*
		 * '\xyy' -- hexadecimal form
		 */
				s++;
				for (c = i = 0; i < 2; i++, s++) {
#if	defined(__STDC__)
					if ( ! isalpha(*s) && ! isdigit(*s))
#else
					if ( ! isascii(*s) && ! isalpha(*s)
					&&   ! isdigit(*s))
#endif
					{
non_hex_char:
						em = "non-hex char";
						goto bad_string;
					}
					c = c << 4;
					if (*s >= '0' && *s <= '9')
						c += *s - '0';
					else if ((*s >= 'a' && *s <= 'f')
					     ||  (*s >= 'A' && *s <= 'F'))
						c += *s + 10 -
						     (isupper(*s) ? 'A' : 'a');
					else
						goto non_hex_char;
				}
				*cp++ = (unsigned char)c;
				l++;
			} else if (*s == 'E' || *s == 'e') {
		/*
		 * '\E' or '\e' -- ESCape
		 */
				*cp++ = ESC;
				l++;
				s++;
			} else if (*s == '\0') {
				em = "no char after \\";
				goto bad_string;
			} else {
		/*
		 * escaped character (for some reason)
		 */
				*cp++ = *s++;
				l++;
			}
			break;
	/*
	 * Copy a "normal" character.
	 */
		default:
			*cp++ = *s++;
			l++;
		}
	}
	*cp = '\0';
	*len = l;
	return(r);
}


/*
 * Convfont(nm, s, fn, fi) - convert a font for a device
 */

static int
Convfont(nm, s, fn, fi)
	char *nm;			/* output device name */
	char *s;			/* font definition string */
	char **fn;			/* font name address */
	unsigned char **fi;		/* initialization string address */
{
	char *cp;			/* temporary character pointer */
	int len;			/* length */
/*
 * Get the font name, allocate space for it and allocate space for
 * a font structure.
 */
	if ((cp = strchr(s, '=')) == NULL) {
		(void) fprintf(stderr, "%s: bad %s font line format: %s\n",
			Pname, nm, s);
		return(0);
	}
	if ((*fn = (char *)malloc(cp - s + 1)) == NULL) {
		(void) fprintf(stderr, "%s: no space for %s font name %s\n",
			Pname, nm, s);
		return(0);
	}
	(void) strncpy(*fn, s, cp - s);
	(*fn)[cp - s] = '\0';
/*
 * Assmble the font initialization string.
 */
	if ((*fi = Convstr(cp + 1, &len)) == NULL)
		return(0);
	return(len);
}


/*
 * Defdev() - define the output device
 */

int
Defdev()
{
	unsigned char *fi = NULL;	/* last font initialization string */
	char *fn = NULL;		/* font name */
	int fd = 0;			/* found-device flag */
	FILE *fs;			/* file stream */
	int err = 0;			/* errror count */
	int i;				/* temporary index */
	int len;			/* length */
	char line[MAXLINE];		/* line buffer */
	char *p;			/* output device configuration file */
	char *s;			/* temporary string pointer */
/*
 * Check for the built-in devices, ANSI, NONE or NORMAL (default).
 */
	Fstr.b = Fstr.i = Fstr.it = Fstr.r = NULL;
	Fstr.bl = Fstr.il = Fstr.itl = Fstr.rl = 0;
	if (Device == NULL || strcasecmp(Device, "normal") == 0) {
		Fontctl = 0;
check_font:
		if (Devfont) {
			(void) fprintf(stderr,
				"%s: font %s for device %s illegal\n",
				Pname, Devfont, Device ? Device : "NORMAL");
			return(1);
		}
		return(0);
	}
	Fontctl = 1;
	if (strcasecmp(Device, "ansi") == 0) {
		Fstr.b = Newstr((unsigned char *)"x[1m");
		Fstr.it = Newstr((unsigned char *)"x[4m");
		Fstr.r = Newstr((unsigned char *)"x[0m");
		Fstr.b[0] = Fstr.it[0] = Fstr.r[0] = ESC;
		Fstr.bl = Fstr.itl = Fstr.rl = 4;
		goto check_font;
	}
	if (strcasecmp(Device, "none") == 0)
		goto check_font;
/*
 * If a device configuration file path is supplied, use it.
 */
	if (Devconf)
		p = Devconf;
	else {

	/*
	 * Use the CAWFLIB environment if it is defined.
	 */
		if ((p = getenv("CAWFLIB")) == NULL)	
			p = CAWFLIB;
		len = strlen(p) + 1 + strlen(DEVCONFIG) + 1;
		if ((s = (char *)malloc(len)) == NULL) {
			(void) fprintf(stderr, "%s: no space for %s name\n",
				Pname, DEVCONFIG);
			return(1);
		}
		(void) sprintf(s, "%s/%s", p, DEVCONFIG);
		p = s;
	}
/*
 * Open the configuration file.
 */
#ifdef	UNIX
	if ((fs = fopen(p, "r")) == NULL)
#else
	if ((fs = fopen(p, "rt")) == NULL)
#endif
	{
		(void) fprintf(stderr, "%s: can't open config file: %s\n",
			Pname, p);
		return(1);
	}
	*line = ' ';
/*
 * Look for a device definition line -- a line that begins with a name.
 */
	while ( ! feof(fs)) {
		if (*line == '\t' || *line == '#' || *line == ' ') {
			(void) fgets(line, MAXLINE, fs);
			continue;
		}
		if ((s = strrchr(line, '\n')) != NULL)
			*s = '\0';
		else
			line[MAXLINE-1] = '\0';
	/*
	 * Match device name.
	 */
		if (strcmp(Device, line) != 0) {
			(void) fgets(line, MAXLINE, fs);
			continue;
		}
		fd = 1;
	/*
	 * Read the parameter lines for the device.
	 */
		while (fgets(line, MAXLINE, fs) != NULL) {
			if (*line == ' ') {
				for (i = 1; line[i] == ' '; i++)
					;
			} else if (*line == '\t')
				i = 1;
			else
				break;
#if	defined(__STDC__)
			if ( ! isalpha(line[i])
#else
			if ( ! isascii(line[i]) || ! isalpha(line[i])
#endif
			||   line[i+1] != '=')
				break;
			if ((s = strrchr(line, '\n')) != NULL)
				*s = '\0';
			else
				line[MAXLINE-1] = '\0';
			switch (line[i]) {
		/*
		 * \tb=<bolding_string>
		 */
			case 'b':
				if (Fstr.b != NULL) {
				    (void) fprintf(stderr,
					"%s: dup bold for %s in %s: %s\n",
					Pname, Device, p, line);
					(void) free(Fstr.b);
					Fstr.b = NULL;
				}
				if ((Fstr.b = Convstr(&line[i+2], &Fstr.bl))
				== NULL)
					err++;
				break;
		/*
		 * \ti=<italicization_string>
		 */
			case 'i':
				if (Fstr.it != NULL) {
				    (void) fprintf(stderr,
					"%s: dup italic for %s in %s: %s\n",
					Pname, Device, p, line);
					(void) free(Fstr.it);
					Fstr.it = NULL;
				}
				if ((Fstr.it = Convstr(&line[i+2], &Fstr.itl))
				== NULL)
					err++;
				break;
		/*
		 * \tr=<return_to_Roman_string>
		 */
			case 'r':
				if (Fstr.r != NULL) {
				    (void) fprintf(stderr,
					"%s: dup roman for %s in %s: %s\n",
					Pname, Device, p, line);
					(void) free(Fstr.r);
					Fstr.r = NULL;
				}
				if ((Fstr.r = Convstr(&line[i+2], &Fstr.rl))
				== NULL)
					err++;
				break;
		/*
		 * \tf=<font_name>=<font_initialization_string>
		 */
			case 'f':
				if ( ! Devfont || Fstr.i)
					break;
				if ((i = Convfont(Device, &line[i+2], &fn, &fi))
				< 0)
					err++;
				else if (fn && strcmp(Devfont, fn) == 0) {
					Fstr.i = fi;
					Fstr.il = i;
					fi = NULL;
				}
				if (fn) {
					(void) free(fn);
					fn = NULL;
				}
				if (fi) {
					(void) free((char *)fi);
					fi = NULL;
				}
				break;
		/*
		 * ????
		 */
			default:
				(void) fprintf(stderr,
					"%s: unknown device %s line: %s\n",
					Pname, Device, line);
				err++;
			}
		}
		break;
	}
	(void) fclose(fs);
	if (err)
		return(1);
/*
 * See if the device stanza was located and the font exists.
 */
	if ( ! fd) {
		(void) fprintf(stderr, "%s: can't find device %s in %s\n",
			Pname, Device, p);
		return(1);
	}
	if (Devfont && ! Fstr.i) {
		(void) fprintf(stderr,
			"%s: font %s for device %s not found in %s\n",
			Pname, Devfont, Device, p);
		return(1);
	}
	return(0);
}
