#include <stdio.h> /* * filter to optimize the output of nroff when it is * directed to a QUME spint micro-3 printer. * * GJE and ADF * */ #define MAXPAGE 10000 /* max chars on a page */ #define CR 4 /* a carriage return takes a CR'th of the time of one character */ #define BS 1.8 /* backspaces take BS * longer than spaces */ #define CRLF 020 #define ECHO 010 #define FORWARDS 1 #define BACKWARDS 0 #define VT 013 #define IN_PLOT 1 #define OUT_PLOT 0 #define PLOT_IN_CHAR 03 #define PLOT_OUT_CHAR 02 #define QUME 0 #define HUPI 60 #define VUPI 48 /* vertical units per inch */ #define PLDEF 12 /* default page length */ #define QUMEWIDTH 15 * HUPI #define DECWRITER 1 struct { char c; int pos; } linebuf[500]; int device QUME; FILE *termin; int singlesheet; int hpos = 0, vpos = 0; int nchars; int xpr; /* the co-ordinate increment for printable characters */ int xnonpr, ynonpr; /* the co-ordinate increment for non-printables */ int mode; int intended_mode; int tab_len; int char_width, char_hight, page_length; int pitch 12; int lpi 6; int length 0; int ttyold[3], ttynew[3]; char obuf[BUFSIZ]; long page[MAXPAGE]; main( ac, av ) int ac; char **av; { int cmp(), intr(); FILE *fopen(); register i, j; int pagenumber; /* set the pitch from name of invocation */ if( av[0][3] == '0' ) pitch = 10; av++; ac--; singlesheet = 0; while( ac-- ) { if( av[0][0] == '-' ) { switch( av[0][1] ) { case 'p': pitch = atoi( &av[0][2] ); break; case 'i': lpi = atoi( &av[0][2] ); break; case 's': if(device == QUME) singlesheet = ((singlesheet = atoi(&av[0][2])) < 1 ? 1: singlesheet); 1; break; case 'd': device = DECWRITER; break; case 'l': length = atoi( &av[0][2] ); break; default: errexit( "usage: qf [-pn] [-in] [-ln]\n" ); } } else errexit( "usage: qf [-pn] [-in] [-ln]\n" ); av++; } /* test pitch */ if( (pitch > HUPI) || (pitch <= 0) ) errexit( "Unreasonable pitch\n" ); /* test lines per inch */ char_width = HUPI/pitch; if( (lpi > VUPI) || (lpi <= 0) ) errexit( "Unreasonable number of lines per inch\n" ); char_hight = VUPI/lpi; page_length = ( length ? VUPI*length : VUPI*PLDEF ); tab_len = 8*char_width; if(singlesheet) termin = fopen( "/dev/tty", "r" ); /* clean exit on signals */ signal( 2, intr ); signal( 3, intr ); /* have to compensate for the fact that nroffs stty will fail */ if( gtty(1, ttyold) != -1 ) { ttynew[0] = ttyold[0]; ttynew[1] = ttyold[1]; ttynew[2] = ttyold[2] & ~(CRLF|ECHO); if(stty( 1, ttynew ) < 0) errexit("Cant set Qume to suitable mode\n"); } else if( device == QUME ) fprintf(stderr, "Warning: need control of Qume for proper results\n" ); /* buffered output */ setbuf( stdout, obuf ); /* Better be sure we know what state the qume is in to start with. */ if(device == QUME) { mode = OUT_PLOT; intended_mode = OUT_PLOT; putchar(PLOT_OUT_CHAR); } putchar('\r'); /* initialize variables based on known state of Qume */ xnonpr = xpr = char_width; ynonpr = char_hight; pagenumber = 0; while( !feof(stdin) ) { inpage(); qsort( page, nchars, sizeof(page[0]), cmp ); outpage(); pagenumber++; if(singlesheet!=0)if((pagenumber%singlesheet)==0) { gettobottom(); fflush(stdout); fgets("dummy buffer", sizeof("dummy buffer"), termin); } } /* better get to bottom of last page */ gettobottom(); /* get him out of plot mode */ intended_mode = OUT_PLOT; putch('\r'); fflush(stdout); stty( 1, ttyold ); } intr() { signal( 2, 1 ); errexit("\002\r\n"); } errexit(s) char *s; { stty( 1, ttyold ); while(*s) write(1,s++,1); /* exit without flushing */ _exit(1); } /* put out a character and set appropriate variables to describe the position of the Qume */ putch( c ) register char c; { register xs, ys; register n; static qume_width = QUMEWIDTH; if( ( intended_mode == IN_PLOT ) && !( mode == IN_PLOT ) ) { putchar( PLOT_IN_CHAR ); mode = IN_PLOT; } else if( ( intended_mode == OUT_PLOT ) && !( mode == OUT_PLOT ) ) { putchar( PLOT_OUT_CHAR ); mode = OUT_PLOT; } xs = ( mode == IN_PLOT ? 1 : char_width ); ys = ( mode == IN_PLOT ? 1 : char_hight ); switch( c ) { case '\r': hpos = 0; break; case ' ': hpos =+ xs; if(hpos > qume_width) errexit("You have asked the Qume to print past the end of its carriage\n"); break; case '\b': hpos =- xs; if(hpos < 0) errexit("You have asked the Qume to backspace past its left margin\n"); break; case '\n': vpos =+ ys; break; case VT: vpos =- ys; break; default: hpos =+ ( mode == IN_PLOT ? 0 : char_width ); break; } putchar( c ); } /* collect a page of text */ inpage() { register char c; long l; static int ivpos = 0; static int ihpos = 0; static int vp = 0; static int hv = 0; static int lastvpos = 0; static int lasthpos = 0; nchars = 0; while( ivpos < page_length ) { c = getchar(); if( feof(stdin) ) return(-1); switch( c ) { case PLOT_IN_CHAR: xpr = 0; xnonpr = ynonpr = 1; break; case PLOT_OUT_CHAR: xnonpr = xpr = char_width; ynonpr = char_hight; break; case ' ': ihpos =+ xnonpr; break; case '\t': break; case '\b': ihpos =- xnonpr; if( ihpos < 0 ) ihpos = 0; break; case '\n': ivpos =+ ynonpr; break; case VT: ivpos =- ynonpr; if( ivpos < 0 ) errexit( "print not contained on one page\n" ); break; case '\r': ihpos = 0; lasthpos = 0; hv = 0; break; default: /* pack the character and its co-ordinates in a long */ if(device==QUME) { vp = ivpos; hv = ihpos; } else { hv =+ (abs(ihpos-lasthpos) > char_width/2) ? (((ihpos-lasthpos)/char_width) * char_width): 0; vp =+ ((abs(ivpos-lastvpos) > char_hight/4 )? (( (ivpos-lastvpos)/(char_hight/2)) * char_hight) : 0); } l = vp; l = ( l << 12 ) | hv; l = ( l << 8 ) | c; page[nchars++] = l; if( nchars >= MAXPAGE ) errexit("too many chars this page ??\n"); lastvpos = ivpos; lasthpos = ihpos; ihpos =+ xpr; break; } } ivpos =- page_length; lastvpos =- page_length; vp = 0; } /* output a page */ outpage() { register nch = 0; char c; int best; int left, right; int x, y; int method; int temp; int hwm; while(nch != nchars) { decode( nch++, &x, &y, &c ); linebuf[0].c = c; linebuf[0].pos = x; hwm = 0; /* gather the characters on the same line */ while((decode(nch, &x, &temp, &c) == y) && (nch < nchars)) { nch++; linebuf[++hwm].c = c; linebuf[hwm].pos = x; } /* This is the bit that decides whether we should print the line forwards or backwards and whether we should do a CR first */ if(device == QUME) { left = linebuf[0].pos; right = linebuf[hwm].pos; method = BACKWARDS; if( hpos >= right ) best = (hpos - left) * BS; else best = (right - left) * BS + (right - hpos); if( (temp = ((hpos-left) * BS + (right-left))) < best ) { best = temp; method = FORWARDS; } if( (right + (hpos / CR))< best ) { intended_mode = OUT_PLOT; putch('\r'); method = FORWARDS; } repeat( OUT_PLOT, '\n', ( y-vpos )/ char_hight ); if( y != vpos ) repeat( IN_PLOT, '\n', y-vpos ); if( method == FORWARDS ) outline( 0, hwm, 1 ); else outline( hwm, 0, -1 ); } else { /* no point optimising for a decwriter */ repeat(OUT_PLOT, '\n', (y-vpos)/char_hight ); putch('\r'); for(temp=0; temp<=hwm; temp++) { if( linebuf[temp].pos == hpos ) putch('\b'); else repeat(OUT_PLOT, ' ', (linebuf[temp].pos - hpos )/char_width - 1); putch(linebuf[temp].c); } } } if( device == DECWRITER ) { putch('\r'); vpos = 0; fputs( "\n\n---------------------------------------------------------\r\n", stdout ); } else vpos =- page_length; } /* put out "n" of "c" in the given mode */ repeat( plotype, c, n ) int plotype; register char c; register n; { if( n == 0 ) return; intended_mode = plotype; /* should never happen */ if( n < 0 ) { fprintf( stderr, "**** %d %c\n", n, c ); return; } while( n-- ) putch( c ); } /* long comparison */ cmp( l1, l2 ) long *l1, *l2; { if( *l1 < *l2 ) return( -1 ); if( *l1 == *l2 ) return( 0 ); return( 1 ); } /* * get the character and co-odinates out of the long which * can be found in page[where] */ decode( where, x, y, c ) int where; int *x, *y; char *c; { *y = (page[where] >> 20) & 07777; *x = (page[where] >> 8) & 07777; *c = page[where] & 0377; return(*y); } /* * optimally output a line * the line is in the array "linebuf" * and is indexed from "start" to "end" in increments of "step" */ outline(start, end, step) int start, end, step; { register int i; register char c; register int col; int count; int ti; int blflag; for( i=start; i!=end+step; ) { col = linebuf[i].pos; blflag = 0; if( col != hpos ) { if( col > hpos ) { if( col-hpos < char_width ) repeat( IN_PLOT, ' ', col -hpos ); else { /* we move to one blank of where we should be because the qume spaces before it prints a character. */ repeat( OUT_PLOT, ' ', ( col-hpos )/ char_width - 1); if( col != (hpos + char_width) ) repeat( IN_PLOT, ' ', col-hpos - char_width); blflag = 1; } } else { repeat( OUT_PLOT, '\b', (hpos-col)/char_width); if( hpos != col ) repeat( IN_PLOT, '\b', hpos-col ); intended_mode = IN_PLOT; } } else intended_mode = IN_PLOT; /* over-strike optimisation */ /* if there is more than one character with the same co-ordinate, go into plot-mode and stuff them all out. First we gather up the characters. */ count = 1; ti = i; while( ( col == (linebuf[ti+step].pos) ) && (ti != end ) ) { count++; ti =+ step; } if( count > 1 ) { if( blflag ) { /* expecting a blank to be produced but we are going to go in plot mode so we better compensate for the fact that a character does not generate a blank in plot mode. */ intended_mode = OUT_PLOT; putch( ' ' ); } intended_mode = IN_PLOT; while( count-- ) { /* dump the overstruck characters advance i to empty linebuf */ putch( linebuf[i].c ); i =+ step; } } else { if( blflag ) { /* a blank is required so we better print the character in plot mode. */ intended_mode = OUT_PLOT; } putch( linebuf[i].c ); i =+ step; } } } gettobottom() { vpos =% page_length; repeat( OUT_PLOT, '\n', (-vpos)/char_hight ); if(device==QUME) if(vpos!=page_length) repeat( IN_PLOT, '\n', -vpos ); }