/* Convert a Usenix Face to PostScript. */

/* This program outputs things in the following order:
 *
 * the prolog
 * anything defined with -D
 * defines "count" to be the number of faces to follow.
 * start
 *  data for the first face
 * face
 *  data for the second face, if any
 * face
 * finish
 */

#ifdef __MSDOS__
char absolute_format[] = "/faces/%s.ps";  /* full pathname to the prologs */
#else
char absolute_format[] = "./%s.ps";  /* full pathname to the prologs */
#endif

char version[] = "Version 1, patchlevel 1";

#ifdef __TURBOC__
#include <stdlib.h>
#else
#include <malloc.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <string.h>

char *program;				/* the name of this program */

/* getopt stuff */
extern int optind;			/* index of which argument is next */
extern char *optarg;			/* pointer to argument of this option */

char *prolog_name = NULL;		/* the name of the prolog file */

int height, width, depth;		/* as per PicData: */
int iheight, iwidth, idepth;		/* as per Image: */
char *defines;				/* accumulated -D options */

void doprolog();
void doface();

do_define(arg)
char *arg;
{
	char *equals = strchr(arg, '=');
	static char define_string[] = "/%s %s def\n";

	if (!equals) {
		fprintf(stderr, "%s: \"%s\" is missing an equals sign",
		    program, arg);
		exit(1);
	}
	defines = realloc(defines, strlen(defines) + strlen(define_string)
	    + strlen(arg) + 1);
	if (!defines) {
		fprintf(stderr, "%s: ran out of memory on \"%s\"\n",
		    program, arg);
		exit(1);
	}
	*equals++ = '\0';
	sprintf(defines + strlen(defines), define_string, arg, equals);
}

char *
longgets(fd)
FILE *fd;
{
	int length = 256;
	char *cp = malloc(length);
	int slen = 0;

	if (!cp) {
		fprintf(stderr, "%s: ran out of memory\n", program);
		exit(1);
	}
	slen = 0;
	*cp = '\0';
	while (fgets(cp + slen, length - slen, fd) != NULL) {
		slen = strlen(cp);
		if (cp[slen-1] == '\n')
			break;
		cp = realloc(cp, length *= 2);
		if (!cp) {
			fprintf(stderr, "%s: ran out of memory\n", program);
			exit(1);
		}
	}
	return cp;
}

main(argc,argv)
int argc;
char *argv[];
{
	int option;			/* the option letter */

#ifdef __MSDOS__
	char *progend;
	program = max(strrchr(argv[0], '\\'),strrchr(argv[0], '/'));
	progend = strchr(program, '.');
	if (progend)
		*progend = '\0';
	program++;
#else
	program = strrchr(argv[0], '/');
	if (program)
		program++;
	else
		program = argv[0];
#endif

	if (setvbuf(stdout, 0, _IOFBF, 4096)) {
		fprintf(stderr, "%s: ran out of memory on setvbuf to stdout\n",
		    program);
		exit(1);
	}

	defines = strdup("");

	while((option=getopt(argc, argv, "p:D:")) != EOF) switch(option){
	case 'p':
		prolog_name = optarg;
		break;
	case 'D':
		do_define(optarg);
		break;
	default:
usage:
		fprintf(stderr, "usage: %s [-pD] [face-file(s)...]", program);
		exit(1);
	}

	/* The default prolog name is the same name as the program.  This lets
	 * us link the filename to different prologs under Unix.  MS-LOSS loses.
	 */
	if (!prolog_name)
		prolog_name = program;

	doprolog(prolog_name);

	/* now that we've output the prolog, output the -D defines, before
	 * we do "start".
	 */
	fputs(defines, stdout);
	free(defines);

	/* now output the count and the "start"
	 */
	printf("/count %d def\nstart\n", argc - optind);

	/* process the remaining arguments (if any)
	 */
	while (optind < argc) {
		doface(argv[optind++]);
	}
	printf("finish\n%%%%EndTrailer\n");
}


void
doprolog(fn)
char *fn;
{
	FILE *fp;			/* the input file */
	char *inl;			/* points to lines from the FILE *fp. */
	char *absolute_fn;

	absolute_fn = malloc(strlen(absolute_format) + strlen(fn));
	if (!absolute_fn) {
		fprintf(stderr, "%s: ran out of memory\n", program);
		exit(1);
	}

	sprintf(absolute_fn, absolute_format, fn);

	fp = fopen(absolute_fn,"r");
	if (fp == NULL) {
		fprintf(stderr,"%s: can't open %s for input\n",
		    program, absolute_fn);
		exit (1);
	}

	if (setvbuf(fp, 0, _IOFBF, 4096)) {
		fprintf(stderr, "%s: ran out of memory on setvbuf to fp\n",
		    program);
		exit(1);
	}

	/* Copy the prolog over */
	while (inl = longgets(fp),*inl) {
		printf("%s",inl);
		free(inl);
	}

	fclose(fp);
}

void
doface(fn)
char *fn;
{
	FILE *fp;			/* the input file */
	char *inl;			/* points to lines from the FILE *fp. */
	fp = fopen(fn,"r");
	if (fp == NULL) {
		fprintf(stderr,"%s: can't open %s for input\n",
		    program, fn);
		exit (1);
	}

	if (setvbuf(fp, 0, _IOFBF, 4096)) {
		fprintf(stderr, "%s: ran out of memory on setvbuf to fp\n",
		    program);
		exit(1);
	}

	/* create a dictionary big enough for everything */
	printf("40 dict begin\n");

	/* Get the header -- exit the loop on the first blank line. */
	while((inl = longgets(fp)) && *inl != '\n'){
		char *colon = strchr(inl, ':');
		char *nl = strrchr(inl, '\n');

		/* make sure we got a newline */
		if (!nl) {
			fprintf(stderr, "%s: the following header entry has no newline:\n\t%s\n", program, inl);
			exit(1);
		}

		/* nuke the newline and any trailing spaces. */
		while (isspace(*nl))
			--nl;
		nl[1] = '\0';

		/* make sure we got a colon */
		if (!colon) {
			fprintf(stderr, "%s: the following header entry has no colon:\n\t%s\n", program, inl);
			exit(1);
		}

		/* nuke the colon and skip any trailing spaces. */
		*colon++ = '\0';
		while (isspace(*colon))
			colon++;

		/* handle PicData: and Image: specially */
		if (!strcmp(inl, "PicData")){
			if (sscanf(colon,"%d %d %d",&width,&height,&depth)
			    != 3) {
				fprintf(stderr, "%s: %s: should have three numbers following it:\"%s\"\n",
					program, inl, colon);
				exit(1);
			} else {
				printf("/width %d def\n/height %d def\n/depth %d def\n",
				       width, height, depth);
			}
		} else if (!strcmp(inl, "Image")){
			if (sscanf(colon,"%d %d %d",&iwidth,&iheight,&idepth)
			    != 3) {
				fprintf(stderr, "%s: %s: should have three numbers following it:\"%s\"\n",
					program, inl, colon);
				exit(1);
			} else {
				printf("/iwidth %d def\n/iheight %d def\n/idepth %d def\n",
				    iwidth, iheight, idepth);
			}
		} else {
			/* headers get defined as their names, quoting parens */
			printf("/%s (", inl);
			for ( ;*colon;colon++) if (*colon == '(' ||
			    *colon == ')' ||
			    *colon == '\\' ||
			    *colon < ' ')
				printf("\\%03o", *colon);
			else
				putchar(*colon);
			printf(") def\n");
		}

		free(inl);
	}

	/* we feed a NULL pointer to free iff the file is munged. */
	free(inl);

	/* we output the x ratio and y ratio in such a manner that the face
	 * is never larger than the scale that you give for it, but the
	 * aspect ratio is preserved */
	if (iwidth > iheight)
		printf("/xratio 1 def\n/yratio %f def\n",
		       (float)iheight / (float)iwidth);
	else
		printf("/xratio %f def\n/yratio 1 def\n",
		       (float)iwidth / (float)iheight);

	printf("/imagedata {<\n");

	/* Copy the image data over */
	while (inl = longgets(fp),*inl) {
		printf("%s",inl);
		free(inl);
	}

	/* finish off the imagedata, do the face, and drop the dictionary */
	printf(">} def\nface\nend\n");

	fclose(fp);
}
