/*
 *	nup.c	- (2up modified version)
 *
 *	version 1.3 (August 5, 1994)
 *	Copyright (C) 1992, 1993, 1994
 *	Kazuhiro Kazama (kazama@square.ntt.jp)
 *      NTT Basic Research Laboratories
 *	All rights reserved.
 */
#define	version	"1.4"

#include	<stdio.h>
#include	<string.h>

#define	TRUE	1
#define	FALSE	0

#define	BB	"%%BoundingBox:"
#define	OPR	"%%Orientation: Portrait"
#define	OLS	"%%Orientation: Landscape"
#define	DPS	"%%DocumentPaperSizes:"
#define	EC	"%%EndComments"

#define	EPL	"%%EndProlog"

#define	BSU	"%%BeginSetup"
#define	ESU	"%%EndSetup"
#define	BDM	"%%BeginDocument:"
#define	EDM	"%%EndDocument"

#define	PAGE	"%%Page:"
#define	TR	"%%Trailer"
#define	PAGES	"%%Pages:"

#define	Default		-1
#define Letter		0
#define Legal		1
#define Tabloid		2
#define B4		3
#define B5		4
#define A5		5
#define A4		6
#define A3		7

struct {
    char	*size;		/* paper size */
    char	*command;	/* paper command */
    int		width;		/* paper width */
    int		height;		/* paper height */
} paperinfo[] = {
    {"Letter",	"@letter",	612, 792},
    {"Legal",	"@legal",	612, 1008},
    {"Tabloid",	"@tabloid",	792, 1224},
    {"B4",	"@b4", 		709, 1001},
    {"B5",	"@b5",		499, 709},
    {"A5",	"@a5",		420, 595},
    {"A4",	"@a4",		595, 842},
    {"A3",	"@a3",		842, 1191},
    };

char	*program;
char	line[BUFSIZ];

int	paper = Default;
int	origpaper = Default;

/* default settings */
int	pagespersheet = 2;
char	shrink = TRUE;
int	drawline = FALSE;
int	verbose = FALSE;

main(argc, argv)
int	argc;
char	*argv[];
{
    char	*p;

    program = *argv;
    while (--argc > 0) {
	argv++;
	if (argv[0][0] == '-')
	    switch(argv[0][1]) {
	    case 'n':
		shrink = FALSE;
		break;
	    case '2':
		pagespersheet = 2;
		break;
	    case '4':
		pagespersheet = 4;
		break;
	    case '8':
		pagespersheet = 8;
		break;
	    case 'l':
		drawline = TRUE;
		break;
	    case 'p':
		argc--;
		argv++;
		p = *argv;
		while (*p)
		    if (isupper(*p))
			*p++ = tolower(*p);
		    else
			*p++;

		if (strcmp(*argv, "letter") == 0)
		    paper = Letter;
		else if (strcmp(*argv, "legal") == 0)
		    paper = Legal;
		else if (strcmp(*argv, "tabloid") == 0)
		    paper = Tabloid;
		else if (strcmp(*argv, "b4") == 0)
		    paper = B4;
		else if (strcmp(*argv, "b5") == 0)
		    paper = B5;
		else if (strcmp(*argv, "a5") == 0)
		    paper = A5;
		else if (strcmp(*argv, "a4") == 0)
		    paper = A4;
		else if (strcmp(*argv, "a3") == 0)
		    paper = A3;
		else {
		    fprintf(stderr, "%s: illegal papersize %s\n", program, *argv);
		    usage();
		    exit(1);
		}
		break;
	    case 'v':
		verbose = TRUE;
		break;
	    default:
		fprintf(stderr, "%s: illegal option %s\n", program, *argv);
		usage();
		exit(1);
	    }
	else {
	    usage();
	    exit(1);
	}
    }
    if (verbose) {
	fprintf(stderr, "%s ver.%s\n", program, version);
	fprintf(stderr, "%d pages per sheet", pagespersheet);
	fprintf(stderr, ", %s paper", (paper == -1 ? "Default"
				       : paperinfo[paper].size));
	fprintf(stderr, ", line[%s]", (drawline?"on":"off"));
	fprintf(stderr, ", shrink[%s]\n", (shrink?"on":"off"));
    }
    if (!printprologue() || !printscript())
	exit(1);
    exit(0);
}

printprologue()
{
    if (!printheader())
	return FALSE;
    while (fgets(line, BUFSIZ, stdin) != NULL) {
	if (strncmp(line, EPL, sizeof(EPL)-1) == 0) {
	    printprocset();
	    fputs(line, stdout);
	    return TRUE;
	} else
	    fputs(line, stdout);
    }
    return FALSE;
}

printheader()
{
    char *s;

    while (fgets(line, BUFSIZ, stdin) != NULL) {
	if (strncmp(line, EC, sizeof(EC)-1) == 0) {
	    fputs(line, stdout);
	    return TRUE;
	} else if ((pagespersheet == 2 || pagespersheet == 8)
		   && strncmp(line, OPR, strlen(OPR)-1) == 0)
	    printf("%%%%Orientation: Landscape\n");
	else if ((pagespersheet == 2 || pagespersheet == 8)
		 && strncmp(line, OLS, strlen(OLS)-1) == 0)
	    printf("%%%%Orientation: Portrait\n");
	else if (paper != Default && strncmp(line, BB, sizeof(BB)-1) == 0)
	    printf("%%%%BoundingBox: 0 0 %d %d\n",
		   paperinfo[paper].width, paperinfo[paper].height);
	else if (paper != Default && strncmp(line, DPS, sizeof(DPS)-1) == 0) {
	    for (s = line + sizeof(DPS) - 1; *s == ' '; s++);
	    if (strncmp(s, "Letter", 6) == 0)
		origpaper = Letter;
	    else if (strncmp(s, "Legal", 5) == 0)
		origpaper = Legal;
	    else if (strncmp(s, "Tabloid", 7) == 0)
		origpaper = Tabloid;
	    else if (strncmp(s, "B4", 2) == 0)
		origpaper = B4;
	    else if (strncmp(s, "B5", 2) == 0)
		origpaper = B5;
	    else if (strncmp(s, "A5", 2) == 0)
		origpaper = A5;
	    else if (strncmp(s, "A4", 2) == 0)
		origpaper = A4;
	    else if (strncmp(s, "A3", 2) == 0)
		origpaper = A3;
	    else {
		fprintf(stderr, "%s: unknown paper size\n", program);
		return FALSE;
	    }
	    printf("%%%%DocumentPaperSizes: %s\n", paperinfo[paper].size);
	} else
	    fputs(line, stdout);
    }
    return FALSE;
}

printscript()
{
    int count = 0, page = 0, setup = TRUE;
    
    while (fgets(line, BUFSIZ, stdin) != NULL) {
	if (strncmp(line, PAGE, sizeof(PAGE)-1) == 0) {
	    if (++count % pagespersheet == 1) {
		if (count != 1)
		    printf("EP0\n");
		printf("%%%%Page: %i %i\nBP0\n", ++page, page);
	    }
	} else if (strncmp(line, TR, sizeof(TR)-1) == 0) {
	    printf("EP0\n%%%%Trailer\n");
	} else if (strncmp(line, PAGES, sizeof(PAGES)-1) == 0)
	    printf("%%%%Pages: %i\n", page);
	else if (strncmp(line, BDM, sizeof(BDM)-1) == 0) {
	    fputs(line, stdout);  
	    if (!skipdocument())
		return FALSE;
	} else if (paper != Default && setup
		   && strncmp(line, BSU, sizeof(BSU)-1) == 0) {
	    fputs(line, stdout);  
	    if (!printsetup())
		return FALSE;
	    setup = FALSE;
	} else
	    fputs(line, stdout);
    }
    return TRUE;
}

printsetup()
{
    while (fgets(line, BUFSIZ, stdin) != NULL) {
	if (strncmp(line, ESU, sizeof(ESU)-1) == 0) {
	    fputs(line, stdout);
	    return TRUE;
	} else if (strncmp(line, paperinfo[origpaper].command,
			   sizeof(paperinfo[origpaper].command)-1) == 0)
	    printf("%s\n", paperinfo[paper].command);
	else
	    fputs(line, stdout);
    }
    return FALSE;
}

skipdocument()
{
    int	indoc = 1;

    while (fgets(line, BUFSIZ, stdin) != NULL) {
	fputs(line, stdout);
	if (strncmp(line, EDM, sizeof(EDM)-1) == 0 && --indoc == 0)
	    return TRUE;
	else if (strncmp(line, BDM, sizeof(BDM)-1) == 0)
	    indoc++;
    }
    return FALSE;
}

printprocset()
{
    printf("%%%%BeginProcSet: nup 1 3\n");
    printf("%% dvi2ps-j patch file for n-up printing\n");
    printf("%% version 1.3 8/5/1994\n");
    printf("%% Copyright (C) 1992, 1993, 1994\n");
    printf("%% Kazuhiro Kazama (kazama@square.ntt.jp)\n");
    printf("%% NTT Basic Research Laboratories \n");
    printf("%% All rights reserved.\n");
    printf("TeXDict begin\n");
    printf("    /shrink %s def\n", (shrink?"true":"false"));
    printf("    /drawline %s def\n", (drawline?"true":"false"));
    printf("    /pagespersheet %d def\n", pagespersheet);
    printf("    /papertype %d def\n", (paper==origpaper?Default:origpaper));
    printf("    /START[\n");
    printf("	{\n");
    printf("	    /realheight paperheight def\n");
    printf("	    /realwidth paperwidth def\n");
    printf("	    papertype -1 ne{\n");
    printf("		/paperheight heightarray papertype get def\n");
    printf("		/paperwidth widtharray papertype get def\n");
    printf("	    }if\n");
    printf("	    /pagescale\n");
    printf("	    shrink {\n");
    printf("		paperheight paperwidth div\n");
    printf("		pagespersheet 2 eq{\n");
    printf("		    realwidth 2 mul realheight div gt{\n");
    printf("			realwidth paperheight\n");
    printf("		    }{\n");
    printf("			realheight 2 div paperwidth\n");
    printf("		    }ifelse\n");
    printf("		}{\n");
    printf("		    pagespersheet 4 eq{\n");
    printf("			realheight realwidth div gt{\n");
    printf("			    realheight 2 div paperheight\n");
    printf("			}{\n");
    printf("			    realwidth 2 div paperwidth\n");
    printf("			}ifelse\n");
    printf("		    }{\n");
    printf("			realwidth 2 mul realheight div gt{\n");
    printf("			    realwidth 2 div paperheight\n");
    printf("			}{\n");
    printf("			    realheight 4 div paperwidth\n");
    printf("			}ifelse\n");
    printf("		    }ifelse\n");
    printf("		}ifelse\n");
    printf("		div\n");
    printf("	    }{1}ifelse def\n");
    printf("	}bind cvlit aload pop\n");
    printf("	/START load cvlit aload pop\n");
    printf("    ]cvx def\n");
    printf("	/BP0{/pagecount 0 def}def\n");
    printf("    /BP[\n");
    printf("	{\n");
    printf("	    gsave pagespersheet 2 eq{\n");
    printf("		[[[90 1 0][90 1 .5]][[-90 0 1][-90 0 .5]]]\n");
    printf("	    }{\n");
    printf("		pagespersheet 4 eq{\n");
    printf("		    [[[0 0 .5][0 .5 .5][0 0 0][0 .5 0]]\n");
    printf("		    [[0 0 0][0 0 .5][0 .5 0][0 .5 .5]]]\n");
    printf("		}{\n");
    printf("		    [[[90 .5 0][90 .5 .25][90 .5 .5][90 .5 .75]\n");
    printf("		    [90 1 0][90 1 .25][90 1 .5][90 1 .75]]\n");
    printf("		    [[-90 0 1][-90 .5 1][-90 0 .75][-90 .5 .75]\n");
    printf("		    [-90 0 .5][-90 .5 .5][-90 0 .25][-90 .5 .25]]]\n");
    printf("		}ifelse\n");
    printf("	    }ifelse\n");
    printf("            landscape{1}{0}ifelse get pagecount get aload pop\n");
    printf("            realheight mul exch realwidth mul exch translate\n");
    printf("            rotate pagescale dup scale\n");
    printf("		drawline {\n");
    printf("		    gsave .33 setlinewidth\n");
    printf("		    newpath 0 0 moveto paperwidth dup 0 rlineto\n");
    printf("		    0 paperheight rlineto -1 mul 0 rlineto\n");
    printf("		    closepath stroke grestore\n");
    printf("		}if\n");
    printf("	    }cvlit aload pop\n");
    printf("        /BP load cvlit aload pop\n");
    printf("	{pagespersheet 4 ne{/landscape landscape not def}if\n");
    printf("    }cvlit aload pop]cvx def\n");
    printf("	/EP0{showpage}def\n");
    printf("    /EP{\n");
    printf("	SaveImage restore\n");
    printf("	userdict/eop-hook known{eop-hook}if\n");
    printf("	grestore/pagecount pagecount 1 add def\n");
    printf("    }bind def\n");
    printf("    TeXDict /@beginspecial known{/@beginspecial[\n");
    printf("	/@beginspecial load cvlit aload pop\n");
    printf("	{pagescale dup scale}cvlit aload pop\n");
    printf("    ]cvx def}if\n");
    printf("end\n");
    printf("%%%%EndProcSet\n");
}

usage()
{
    fprintf(stderr, "usage: %s [-2|4|8] [-l] [-n] [-p paper]\n", program);
}
