#ifndef LINT
static char *rcs_header =
	"$Header: psdvi.c,v 1.5 87/11/27 16:39:25 elwell Exp $";
#endif

/*
 * psdvi -- convert TeX DVI files to PostScript
 *
 * Written by Clayton M. Elwell, The Ohio State University
 *
 * Edit History:
 *
 *	$Log:	psdvi.c,v $
 * Revision 1.5  87/11/27  16:39:25  elwell
 * Split out PostScript prologue into separate file
 * 
 * Revision 1.4  87/09/17  22:13:53  elwell
 * Cleaned up PostScript prologue
 * 
 * Revision 1.3  87/07/30  12:21:43  elwell
 * Fixed bug in font scaling (TeX points vs. PostScript points)
 * 
 * Revision 1.2  87/07/29  11:23:02  elwell
 * Added support for oblique (slanted) fonts.  The PostScript
 * macro that implements this was written by L.A. Carr (as far
 * as I can tell).
 * 
 * Revision 1.1  87/07/14  19:11:07  elwell
 * Initial revision
 * 
 * 
 */

#include <stdio.h>
char *malloc();
#ifdef BSD
#include <pwd.h>
#include <strings.h>
#endif

char profile[80] = "/usr/local/lib/ps/psdvi.pro";

#define  SETC_000    0
#define  SETC_127  127
#define  SET1      128
#define  SET2      129
#define  SET3      130
#define  SET4      131
#define  SET_RULE  132
#define  PUT1      133
#define  PUT2      134
#define  PUT3      135
#define  PUT4      136
#define  PUT_RULE  137
#define  NOP       138
#define  BOP       139
#define  EOP       140
#define  PUSH      141
#define  POP       142
#define  RIGHT1    143
#define  RIGHT2    144
#define  RIGHT3    145
#define  RIGHT4    146
#define  W0        147
#define  W1        148
#define  W2        149
#define  W3        150
#define  W4        151
#define  X0        152
#define  X1        153
#define  X2        154
#define  X3        155
#define  X4        156
#define  DOWN1     157
#define  DOWN2     158
#define  DOWN3     159
#define  DOWN4     160
#define  Y0        161
#define  Y1        162
#define  Y2        163
#define  Y3        164
#define  Y4        165
#define  Z0        166
#define  Z1        167
#define  Z2        168
#define  Z3        169
#define  Z4        170
#define  FONT_00   171
#define  FONT_63   234
#define  FNT1      235
#define  FNT2      236
#define  FNT3      237
#define  FNT4      238
#define  XXX1      239
#define  XXX2      240
#define  XXX3      241
#define  XXX4      242
#define  FNT_DEF1  243
#define  FNT_DEF2  244
#define  FNT_DEF3  245
#define  FNT_DEF4  246
#define  PRE       247
#define  POST      248
#define  POST_POST 249

char *program_name;

FILE *dvi_file;

open_dvi_file(s)
char *s;
{
    dvi_file = fopen(s, "r");
    if (!dvi_file) error("Unable to open DVI file %s", s);
}

#define	set_dvi_file_pos(i)	fseek(dvi_file, (long) (i), 0)
#define get_dvi_file_pos()	ftell(dvi_file)

get_byte()		/* get a byte from the DVI file, unsigned */
{
    register int i;

    if ((i = getc(dvi_file)) == EOF)
	error("unexpected end of file on DVI file");
    return i & 255;
}

signed_byte()
{
    register int i;
    i = get_byte();
    if (i > 127) i -= 256;
    return i;
}

get_two_bytes()
{
    register int c1, c2;

    c1 = get_byte(); c2 = get_byte();
    return c1 * 256 + c2;
}

signed_pair()
{
    register int c1, c2;

    c1 = signed_byte(); c2 = get_byte();
    return c1 * 256 + c2;
}

get_three_bytes()
{
    register int c1, c2 ,c3;
    c1 = get_byte(); c2 = get_byte(); c3 = get_byte();
    return (c1 * 256 + c2) * 256 + c3;
}

get_signed_trio()
{
    register int c1, c2, c3;
    c1 = signed_byte(); c2 = get_byte(); c3 = get_byte();
    return (c1 * 256 + c2) * 256 + c3;
}

signed_quad()
{
    int i;
    register int c1, c2, c3, c4;
    c1 = signed_byte(); c2 = get_byte(); c3 = get_byte(); c4 = get_byte();
    i = ((c1*256+c2)*256+c3)*256+c4;
    return i;
}

get_dvi_str(l, s)
int l;
char *s;
{
    int i;

    for (i = 0; i < l; i++) s[i] = get_byte();
    s[i] = 0;
}

include_file(s)
char *s;
{
    FILE *fp;
    int c;

    if (!(fp = fopen(s, "r"))) return -1;
    while ((c = getc(fp)) != EOF) putchar(c);
    fclose(fp);
    return 0;
}

double mag;

write_preamble_comment()
{
    register int byte1, byte2;
#ifdef BSD
    struct passwd *pw;
    char h[80];
#endif

    byte1 = get_byte(); byte2 = get_byte();
    if (byte1 != PRE || byte2 != 2) error ("input file is not a DVI file");
    set_dvi_file_pos(14);
    printf("%%%%Creator: ");
    byte1 = get_byte();
    while (byte1--) putchar(get_byte());
    putchar('\n');
#ifdef BSD
    pw = getpwuid(getuid());
    gethostname(h, 80);
    if (index(pw->pw_gecos, ','))
    	*(index(pw->pw_gecos, ',')) = 0;
    printf("%%%%For: %s@%s (%s)\n", pw->pw_name, h, pw->pw_gecos);
#endif
}

scmp(s1, s2)
char **s1;
char **s2;
{
    return strcmp(*s1, *s2);
}

int last_bop_pointer;
double conv, true_conv, mag;

load_postamble_and_fonts()
{
    int pos, op, dvi_mag, numerator, denominator, max_v, max_h, max_s,
	total_pages, p;
    int scaled, design;
    int waste, length;
    char str[200];
    char *ftab[500];
    int nfonts, i;

    /* find postamble */

    fseek(dvi_file, -5L, 2);	/* find end of file - 5 */
    pos = get_dvi_file_pos();
    for (op = get_byte(); op == 223;) {
	pos--; set_dvi_file_pos(pos); op = get_byte();
    }

    set_dvi_file_pos(pos - 4);
    pos = signed_quad();

    /* load postamble proper */

    set_dvi_file_pos(pos + 1);		/* just past the POST command */
    last_bop_pointer = signed_quad();
    numerator = signed_quad();
    denominator = signed_quad();
    dvi_mag = signed_quad();
    mag = ((double) dvi_mag) / 1000.0;

    conv = (((double) numerator)/254000.0)*(72.0/((double) denominator));
    true_conv = conv;
    conv = true_conv * mag;

    max_v = signed_quad();
    max_h = signed_quad();
    max_s = get_two_bytes();

    total_pages = get_two_bytes();
 
    pos = get_dvi_file_pos();

    printf("%%%%DocumentFonts:");
    nfonts = 0;
    for (op = get_byte(); op != POST_POST; op = get_byte()) {
	if (op >= FNT_DEF1 && op <= FNT_DEF4) {
	    p = first_par(op);
	    signed_quad();
	    scaled = signed_quad();
	    design = signed_quad();
	    waste = get_byte(); length = get_byte() + waste;
	    get_dvi_str(length, str);
	    for (i = 0; i < nfonts; i++)
		if (!strcmp(str, ftab[i])) break;
	    if (i == nfonts) {
		ftab[i] = malloc(strlen(str)+1);
		strcpy(ftab[i], str);
		nfonts++;
	    }
	}
    }
    qsort(ftab, nfonts, sizeof(char *), scmp);
    puts(ftab[0]);
    for (i = 1; i < nfonts; i++) {
	printf("%%%%+%s\n", ftab[i]);
	free(ftab[i]);
    }

    set_dvi_file_pos(pos);

    printf("%%%%Pages: %d\n%%%%EndComments\n", total_pages);

    include_file(profile);

    for (op = get_byte(); op != POST_POST; op = get_byte()) {
	if (op >= FNT_DEF1 && op <= FNT_DEF4) {
	    p = first_par(op);
	    signed_quad();
	    scaled = signed_quad();
	    design = signed_quad();
	    waste = get_byte(); length = get_byte() + waste;
	    get_dvi_str(length, str);
	    /* notice correction factor between TeX and PostScript points */
	    printf("/f%d { %g /%s tf } def\n", p,
		   (((double)scaled)/((double)design))*mag*0.996264010, str);
	}
    }

    puts("%%EndProlog");
}

first_par(o)
int o;
{
    if (o >= SETC_000 && o <= SETC_127) return (o - SETC_000);
    if (o >= FONT_00  && o <= FONT_63)  return (o - FONT_00);
    switch (o) {
	case SET1: case PUT1: case FNT1: case XXX1: case FNT_DEF1:
		return get_byte();
	case SET2: case PUT2: case FNT2: case XXX2: case FNT_DEF2:
		return get_two_bytes();
	case SET3: case PUT3: case FNT3: case XXX3: case FNT_DEF3:
		return get_three_bytes();
	case RIGHT1: case W1: case X1: case DOWN1: case Y1: case Z1:
		return signed_byte();
	case RIGHT2: case W2: case X2: case DOWN2: case Y2: case Z2:
		return signed_pair();
	case RIGHT3: case W3: case X3: case DOWN3: case Y3: case Z3:
		return get_signed_trio();
	case SET4: case SET_RULE: case PUT4: case PUT_RULE: case RIGHT4:
	case W4: case X4: case DOWN4: case Y4: case Z4: case FNT4:
	case XXX4: case FNT_DEF4:
		return signed_quad();
	case W0:
		return 0;
	case X0:
		return 0;
	case Y0:
		return 0;
	case Z0:
		return 0;
	case NOP: case BOP: case EOP: case PUSH: case POP: case PRE:
	case POST: case POST_POST:
	default:
		return 0;
    }
}

char cur_name[200];

int first_bop_pointer;

find_starting_page()
{
    int p, found, counts_match, i;

    found = 0;
    for (p = last_bop_pointer; p >= 0; p = signed_quad()) {
	set_dvi_file_pos(p + 1);	/* move to byte after BOP */
	/* match counts against starting page spec -- ignore for now */
	for (i = 0; i < 10; i++)
	    signed_quad();
	first_bop_pointer = p;
	found = 1;
    }
    if (!found) error ("Starting page number could not be found");
}

int cur_font;

process_pages()
{
    char waste_string[200];	/* string shaped waste basket */
    int waste;			/* integer shaped waste basket */
    int beginning_of_page, pages_processed, fonts_on_page, op, p;
    int k, f, width, pxl_height, pxl_width;
    int length;
    int scaled_size, design_size;

    set_dvi_file_pos(first_bop_pointer);
    pages_processed = 0;

    for (op = get_byte(); op != POST; op = get_byte()) {
	switch (op) {
	    case BOP:
	        printf("%%%%Page: %d %d\nbop ", signed_quad(),
		       pages_processed+1);

		/* process one page */

		/* toss out extra counts and the back pointer */
		for (k = 0; k < 10; k++) signed_quad();

		for (op = get_byte(); op != EOP; op = get_byte()) {
		    /* interpret one command */
		  nxtcmd:
		    if (op >= SETC_000 && op <= SETC_127) {
			putchar('(');
			while (op <= 128) {
			    if (op == 128) {
				p = get_byte();
			        printf("\\%c%c%c", p/0100 + '0',
				       ((p/010) & 7) + '0', (p & 7)+'0');
			    } else if (op != '\\' && op != '(' && op != ')')
				putchar(op);
			    else {
				putchar('\\'); putchar(op);
			    }
			    op = get_byte();
			    switch (op) {
				case W0:
				    putchar(' '); 
				    op = get_byte();
				    break;
			    }
			}
			puts(")s");
			goto nxtcmd;
		    } else if ((op >= FONT_00 && op <= FONT_63) ||
			       (op >= FNT1 && op <= FNT4)) {
			p = first_par(op);
			printf("f%d ", p);
		    } else {
			p = first_par(op);	/* get first parameter */
			switch (op) {
			    case SET1: case SET2: case SET3: case SET4:
			        printf("(\\%c%c%c)s ", p/0100 + '0',
				       ((p/010) & 7) + '0', (p & 7)+'0');
				break;
			    case PUT1: case PUT2: case PUT3: case PUT4:
			        printf("(\\%c%c%c)p ", p/0100 + '0',
				       ((p/010) & 7) + '0', (p & 7)+'0');
				break;
			    case SET_RULE:
				/* output rule and move, height in p */
				width = signed_quad();
				printf("%g %g rl\n", conv*(double)width,
				       conv*(double)p);
				break;
			    case PUT_RULE:
				/* output rule, height in p */
				width = signed_quad();
				printf("%g %g pr\n", conv*(double)width,
				       conv*(double)p);
				break;
			    case NOP:
				break;
			    case PUSH:
				printf("S ");
				break;
			    case POP:
				printf("R ");
				break;
			    case X0: 
				printf("x0 ");
                                break;
			    case X1: case X2: case X3: case X4:
				printf("%g xs ", conv*(double)p);
				break;
			    case W0:
				printf("w0 ");
				break;
			    case W1: case W2: case W3: case W4:
				printf("%g ws ", conv*(double)p);
			        break;
			    case RIGHT1: case RIGHT2:
			    case RIGHT3: case RIGHT4:
				printf("%g r ", conv*(double)p);
				break;
			    case Z0:
				printf("z0 ");
				break;
			    case Z1: case Z2: case Z3: case Z4:
				printf("%g zs ", conv*(double)p);
				break;
			    case Y0:
				printf("y0 "); break;
			    case Y1: case Y2: case Y3: case Y4:
				printf("%g ys ", conv*(double)p); break;
			    case DOWN1: case DOWN2: case DOWN3: case DOWN4:
				printf("%g d ", conv*(double)p);
				break;
			    case FNT_DEF1: case FNT_DEF2:
			    case FNT_DEF3: case FNT_DEF4:
				signed_quad();
				scaled_size = signed_quad();
				design_size = signed_quad();
				waste = get_byte(); length = get_byte()+waste;
				get_dvi_str(length, waste_string);
/*				printf("/f%d { %g /%s tf } def\n", p,
				       ((double)scaled_size)/
				       ((double)design_size),
				       waste_string);		*/
				break;
			    case XXX1: case XXX2: case XXX3: case XXX4:
				get_dvi_str(p, waste_string);
				do_special(waste_string);
				break;
			    default:
				error("Unknown or misplaced DVI command: %d",
					op);
				break;
			}
		    }
		}

		puts("eop");			/* page separator */
		pages_processed++;
		break;
	    case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4:
		p = first_par(op);		/* skip external font no. */
		signed_quad();
		scaled_size = signed_quad();
		design_size = signed_quad();
		waste = get_byte(); length = get_byte()+waste;
		get_dvi_str(length, waste_string);
/*		printf("/f%d { %g /%s tf } def\n", p,
		       ((double)scaled_size)/
		       ((double)design_size),
		       waste_string);		*/
		break;
	}
    }
    puts("%%Trailer");
}

main(argc, argv)
int argc;
char **argv;
{
    int i;

    program_name = argv[0];
    if (argc != 2) {
	fprintf(stderr, "usage: %s dvi-file\n", argv[0]);
	exit(-1);
    }

    printf("%%!PS-Adobe-1.0\n%%%%Title: %s\n", argv[1]);
    open_dvi_file(argv[1]);
    write_preamble_comment();
    load_postamble_and_fonts();
    find_starting_page();
    process_pages();
}

error(s, a1, a2, a3, a4, a5, a6, a7, a8, a9)
char *s;
int a1, a2, a3, a4, a5, a6, a7, a8, a9;
{
    fprintf(stderr, "%s: ", program_name);
    fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9);
    putc('\n', stderr);
    exit(-1);
}

do_special(s)
char *s;
{
    FILE *fp;
    int c;

    if (s[0] != '!') { puts(s); return; }
    if (!strncmp(s, "!include", 8)) {
	if (include_file(s+9)) {
	    fprintf(stderr, "%s: WARNING: cannot include file %s\n",
		    program_name, s+9);
	    return;
	}
    } else puts(s);
}
