#!mkcmd
comment "$Id: txt2ps.m,v 3.8 2008/11/13 02:12:42 ksb Exp $"
#
# $Compile: %b %o -mMkcmd %f && %b %o prog.c && %b %o -mRename %f
# $Mkcmd: ${mkcmd-mkcmd} %f
# $Rename: mv prog %F && rm -f prog.[ch]

from '<errno.h>'
from '<fcntl.h>'
from '<unistd.h>'
from '<stdlib.h>'
from '"machine.h"'
require "std_help.m" "std_version.m"


%i
static char rcsid[] =
	"$Id: txt2ps.m,v 3.8 2008/11/13 02:12:42 ksb Exp $";
%%

double "b" {		# excepts a floating point value and scales it
	parameter "bmargin"
	help "set bottom margin to %p inches"
	user "iBottom = %n * 720;"
}
boolean "F" {
	local named "bForce"
	init "FALSE"
	help "force conversion to PostScript"
}
double "g"	{
	parameter "gwidth"
	help "set gutter for numbers to %p inches (implies -n)"
	user "iGutter *= 720;"
}
boolean "L" {
	named "bLandscape"
	init "FALSE"
	help "print in landscape mode"
}
int "l" {
	named "iPerPage"
	init "OFF_PAGE"
	parameter "lines"
	help "number of lines per page"
}
double "m" {		# excepts a floating point value and scales it
	parameter "lmargin"
	help "set left margin to %p inches"
	user "iLeft = %n * 720;"
}
boolean "n" {
	named "bNumber"
	init "FALSE"
	help "print line numbers"
}
char* "P" {
	named "pcProject"
	parameter "name"
	help  "project name for header"
}
double "p" {		# excepts a floating point value and scales it
	parameter "psize"
	help "use point size %p"
	user "iPtsize = %n * 10;"
}
double "t" {		# excepts a floating point value and scales it
	parameter "tmargin"
	help "set top margin to %p inches"
	user "iTop = %n * 720;"
}
boolean "v" {
	named "bVerbose"
	help "describe settings being used"
}
int "w" {
	named "iMaxCol"
	parameter "width"
	help "wrap lines at %p characters"
}

after {
	named "After"
}
zero {
	update "Process(\"-\", %rFn);"
}
every {
	parameter "files"
	help "files to be converted"
	update "Process(%a, %rFn);"
}

%i
#define HEIGHT		7920	/* 11 inch */
#define WIDTH		6120	/* 8.5 inch */
#define POINTSIZE	 100	/* 10 point */
#define BOTTOMMARGIN	 240	/* 1/3 inch */
#define LEFTMARGIN	 360	/* 1/2 inch */
#define TOPMARGIN	 360	/* 1/2 inch */

int	bBPdone,		/* has BP been done */
	bCont = FALSE,		/* continuation line? */
	iErrs = 0,		/* number of errs encountered */
	iBottom = OFF_PAGE,
	iGutter = 180,		/* .25 inch gutter */
	iLeft = OFF_PAGE,
	iTop = OFF_PAGE,
	iPtsize = OFF_PAGE,
	iCol,			/* current column */
	iPage,			/* current page number */
	iTopLine,		/* position of top line */
	iVspace,		/* vertical spacing */
	iYpos;			/* current vertical position */
%%

%c
/*
 *	txt2ps - Convert ASCII text to printable PostScript
 *
 *	Jeff W. Stewart, Purdue University Computing Center
 */

static void
StartPage()
{
	if (!bBPdone) {
		++iPage;
		printf("%%%%Page: %d %d\nBP\n", iPage, iPage);
		bBPdone = TRUE;
	}
}

static void
LineFeed(newpage)
int newpage;
{
	if (bNumber) {
		if (0 == iCol) {
			puts("(");
		}
		printf(") %6.1f %c\n", iYpos/10.0, bCont ? 'P':'N');
		iCol = 0;
	} else if (iCol > 0) {
		printf(") %6.1f P\n", iYpos/10.0);
		iCol = 0;
	}
	iYpos -= iVspace;
	if (iYpos < iBottom || newpage) {
		StartPage();
		puts("EP");
		bBPdone = FALSE;
		iYpos = iTopLine;
	}
}

static void
AddChar(c)
int c;
{
	if (iMaxCol > 0 && iCol >= iMaxCol) {
		LineFeed(FALSE);
		bCont = TRUE;
		iCol = 0;
	}
	StartPage();
	if (iCol == 0) {	/* beginning of line */
		putchar('(');
	}
	if (c == '(' || c == ')' || c == '\\') {
		putchar('\\');	/* escape the character */
	}
	putchar(c);
	++iCol;
}

static void
Header(pcName)
char *pcName;
{
	static char acRotate[] = "90 rotate 0 -612 translate";

	puts("%!");
	puts("%%Creator: txt2ps");
	puts("%%DocumentFonts: Courier");
	puts("%%Pages: (atend)");
	puts("%%EndComments");
	printf("/LM %5.1f def\n", iLeft/10.0);
	printf("/NM %4.1f def\n", (iLeft-iGutter)/10.0);
	puts("/rshow {dup stringwidth pop neg 0 rmoveto show} def");
	printf("/NumFont /Courier findfont %4.1f scalefont def \n",
		iPtsize/10.0-3);
	puts("/N {/NUM NUM 1 add def");
	puts("  First 0 eq { /First NUM def }if");
	puts("  dup NM exch moveto");
	puts("  gsave");
	puts("    NumFont setfont NUM 10 string cvs rshow");
	puts("  grestore");
	puts("  LM exch moveto show} def");
	puts("/P {");
	puts("  First 0 eq { /First NUM def }if");
	puts("  LM exch moveto show");
	puts("}def");
	puts("/NUM 0 def");
	puts("/BP {");
	puts("  /First 0 def");
	if (bLandscape) {
		puts(acRotate);
	}
	puts("}def");
	puts("/EP {");
	if ((char*)0 != pcProject) {
		puts("  gsave");
		printf("  /Times-Bold findfont %4.1f scalefont setfont\n",
			iPtsize/10.0+2);
		printf("  (%s) %s %4.1f moveto show\n",
			pcProject, bNumber ? "NM":"LM", iTopLine/10.0+18);
		printf("  /Times-Roman findfont %4.1f scalefont setfont\n",
			iPtsize/10.0+2);
		if (bNumber) {
			printf("  (%s) %4.1f %4.1f moveto rshow\n",
				pcName, 6.25*72, iTopLine/10.0+18);
			printf("  %4.1f %4.1f moveto First 10 string cvs rshow ( -) show\n",
				7.0*72, iTopLine/10.0+18);
			puts("  ( 00000) stringwidth rmoveto NUM 10 string cvs rshow");
		} else {
			printf("  (%s) %4.1f %4.1f moveto rshow\n",
				pcName, 7.0*72, iTopLine/10.0+18);
		}
		puts("  grestore");
	}
	puts("  showpage");
	puts("}def");
	printf("/Courier findfont %4.1f scalefont setfont\n", iPtsize/10.0);
	puts("%%EndProlog");
	bBPdone = FALSE;
	iCol = iPage = 0;
	iYpos = iTopLine;
}

static void
After(int dummy)		/*ARGSUSED*/
{
	if (OFF_PAGE == iBottom) {
		iBottom = BOTTOMMARGIN;
	}
	if (OFF_PAGE == iLeft) {
		iLeft = LEFTMARGIN;
	}
	if (OFF_PAGE == iPtsize) {
		iPtsize = POINTSIZE;
	}
	if (OFF_PAGE == iTop) {
		iTop = TOPMARGIN;
	}
	if (bLandscape) {
		iTopLine = WIDTH - iTop - iPtsize;
	} else {
		iTopLine = HEIGHT - iTop - iPtsize;
	}
	if (OFF_PAGE == iPerPage) {
		iPerPage = (iTopLine - iBottom) / (iPtsize * 1.1) + 1;
	}
	if (iPerPage > 1) {
		iVspace = (iTopLine - iBottom) / (iPerPage - 1);
	} else {
		iVspace = iPtsize * 1.1;	/* for 1 line per page */
	}
	if (bVerbose) {
		fprintf(stderr, "%s Mode\n", (bLandscape) ? "Landscape" : "Portrait");
		fprintf(stderr, "%d Lines per page\n", iPerPage);
		fprintf(stderr, "Point Size = %4.2f on %4.2f spacing\n", iPtsize/10.0, iVspace/10.0);
		fprintf(stderr, "Left Margin = %5.3f\n", iLeft/720.0);
		fprintf(stderr, "Top Margin = %5.3f   Bottom Margin = %5.3f\n", iTop/720.0, iBottom/720.0);
	}
}

static void
Process(pcPath, bConvert)
char *pcPath;
int bConvert;
{
	int fd;
	int i, nc;
	char acBuf[BUFSIZ];

	if (strcmp(pcPath, "-") == 0) {
		fd = fileno(stdin);
	} else {
		fd = open(pcPath, O_RDONLY, 0644);
	}
	if (fd < 0) {
		fprintf(stderr, "%s: open %s: %s\n", progname, pcPath, strerror(errno));
		++iErrs;
		return;
	}
	/* convert or copy */
	nc = read(fd, acBuf, sizeof(acBuf));
	if (nc < 2 || acBuf[0] != '%' || acBuf[1] != '!') {
		bConvert = TRUE;
	}
	if (bConvert) {
		char c;

		Header(pcPath);
		while (nc > 0) {
			for (i=0; i<nc; ++i) {
				switch (c = acBuf[i]) {
				case '\t':
					do {
						AddChar(' ');
					} while ((iCol % 8) != 0);
					break;

				case '\r':
					break;

				case '\n':
					LineFeed(FALSE);
					bCont = FALSE;
					break;

				case '\f':
					/* ignore leading blank pages */
					if(iPage > 0 || iCol > 0) {
						LineFeed(TRUE);
						bCont = FALSE;
					}
					break;

				default:
					AddChar(c);
				}
			}
			nc = read(fd, acBuf, sizeof(acBuf));
		}
		if (iCol > 0) {
			LineFeed(FALSE);
			bCont = FALSE;
		}
		if (bBPdone) {
			puts("EP");	/* end final page */
		}
		printf("%%%%Trailer\n%%%%Pages: %d\n%%%%EOF\n", iPage);
		fflush(stdout);
	} else {
		while (nc > 0) {
			if (write(fileno(stdout), acBuf, nc) != nc) {
				fprintf(stderr, "%s: write: %d: %s\n", progname, fileno(stdout), strerror(errno));
				exit(127);
			}
			nc = read(fd, acBuf, sizeof(acBuf));
		}
		if (6 != write(fileno(stdout), "%%EOF\n", 6)) {
			fprintf(stderr, "%s: write: %d: %s\n", progname, fileno(stdout), strerror(errno));
			exit(127);
		}
	}
	if (nc < 0) {
		fprintf(stderr, "%s: read: %s: %s\n", progname, pcPath, strerror(errno));
	}
	if (fileno(stdin) != fd) {
		(void) close(fd);
	}
}
