Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 *
3
 * postdaisy - PostScript translator for Diablo 1640 files.
4
 *
5
 * A program that translates Diablo 1640 files into PostScript. Absolutely nothing
6
 * is guaranteed. Quite a few things haven't been implemented, and what's been
7
 * done isn't well tested. Most of the documentation used to write this program
8
 * was taken from the 'Diablo Emulator' section of a recent Imagen manual.
9
 *
10
 * Some of document comments that are generated may not be right. Most of the test
11
 * files I used produced a trailing blank page. I've put a check in formfeed() that
12
 * won't print the last page if it doesn't contain any text, but PAGES comments may
13
 * not be right. The DOCUMENTFONTS comment will also be wrong if auto underline or
14
 * bold printing have been turned on by escape commands.
15
 *
16
 * The brute force approach used to implement horizontal and vertical tabs leaves
17
 * much to be desired, and may not work for very small initial hmi and vmi values.
18
 * At the very least I should have used malloc() to get space for the two tabstop
19
 * arrays after hmi and vmi are known!
20
 *
21
 * Reverse printing mode hasn't been tested at all, but what's here should be
22
 * close even though it's not efficient.
23
 *
24
 * The PostScript prologue is copied from *prologue before any of the input files
25
 * are translated. The program expects that the following PostScript procedures
26
 * are defined in that file:
27
 *
28
 *	setup
29
 *
30
 *	  mark ... setup -
31
 *
32
 *	    Handles special initialization stuff that depends on how this program
33
 *	    was called. Expects to find a mark followed by key/value pairs on the
34
 *	    stack. The def operator is applied to each pair up to the mark, then
35
 *	    the default state is set up.
36
 *
37
 *	pagesetup
38
 *
39
 *	  page pagesetup -
40
 *
41
 *	    Does whatever is needed to set things up for the next page. Expects to
42
 *	    find the current page number on the stack.
43
 *
44
 *	t
45
 *
46
 *	  mark str1 x1 str2 x2 ... strn xn y hmi t mark
47
 *
48
 *	    Handles all the text on the stack. Characters in the strings are
49
 *	    printed using hmi as the character advance, and all strings are at
50
 *	    vertical position y. Each string is begins at the horizontal position
51
 *	    that preceeds it.
52
 *
53
 *	f
54
 *
55
 *	  font f -
56
 *
57
 *	    Use font f, where f is the full PostScript font name. Only used when
58
 *	    we switch to auto underline (Courier-Italic) or bold (Courier-Bold)
59
 *	    printing.
60
 *
61
 *	done
62
 *
63
 *	  done
64
 *
65
 *	    Makes sure the last page is printed. Only needed when we're printing
66
 *	    more than one page on each sheet of paper.
67
 *
68
 * Many default values, like the magnification and orientation, are defined in 
69
 * the prologue, which is where they belong. If they're changed (by options), an
70
 * appropriate definition is made after the prologue is added to the output file.
71
 * The -P option passes arbitrary PostScript through to the output file. Among
72
 * other things it can be used to set (or change) values that can't be accessed by
73
 * other options.
74
 *
75
 */
76
 
77
#include <stdio.h>
78
#include <signal.h>
79
#include <ctype.h>
80
#ifdef plan9
81
#define	isascii(c)	((unsigned char)(c)<=0177)
82
#endif
83
#include <sys/types.h>
84
#include <fcntl.h>
85
 
86
#include "comments.h"			/* PostScript file structuring comments */
87
#include "gen.h"			/* general purpose definitions */
88
#include "path.h"			/* for the prologue */
89
#include "ext.h"			/* external variable declarations */
90
#include "postdaisy.h"			/* a few special definitions */
91
 
92
char	*optnames = "a:c:f:h:l:m:n:o:p:r:s:v:x:y:A:C:E:J:L:P:DI";
93
 
94
char	*prologue = POSTDAISY;		/* default PostScript prologue */
95
char	*formfile = FORMFILE;		/* stuff for multiple pages per sheet */
96
 
97
int	formsperpage = 1;		/* page images on each piece of paper */
98
int	copies = 1;			/* and this many copies of each sheet */
99
 
100
char	htabstops[COLUMNS];		/* horizontal */
101
char	vtabstops[ROWS];		/* and vertical tabs */
102
 
103
int	res = RES;			/* input file resolution - sort of */
104
 
105
int	hmi = HMI;			/* horizontal motion index - 1/120 inch */
106
int	vmi = VMI;			/* vertical motion index - 1/48 inch */
107
int	ohmi = HMI;			/* original hmi */
108
int	ovmi = VMI;			/* and vmi - for tabs and char size */
109
 
110
int	hpos = 0;			/* current horizontal */
111
int	vpos = 0;			/* and vertical position */
112
 
113
int	lastx = -1;			/* printer's last horizontal */
114
int	lasty = -1;			/* and vertical position */
115
int	lasthmi = -1;			/* hmi for current text strings */
116
 
117
int	lastc = -1;			/* last printed character */
118
int	prevx = -1;			/* at this position */
119
 
120
int	leftmargin = LEFTMARGIN;	/* page margins */
121
int	rightmargin = RIGHTMARGIN;
122
int	topmargin = TOPMARGIN;
123
int	bottommargin = BOTTOMMARGIN;
124
 
125
int	stringcount = 0;		/* number of strings on the stack */
126
int	stringstart = 1;		/* column where current one starts */
127
int	advance = 1;			/* -1 if in backward print mode */
128
 
129
int	lfiscr = OFF;			/* line feed implies carriage return */
130
int	crislf = OFF;			/* carriage return implies line feed */
131
 
132
int	linespp = 0;			/* lines per page if it's positive */
133
int	markedpage = FALSE;		/* helps prevent trailing blank page */
134
int	page = 0;			/* page we're working on */
135
int	printed = 0;			/* printed this many pages */
136
 
137
Fontmap	fontmap[] = FONTMAP;		/* for translating font names */
138
char	*fontname = "Courier";		/* use this PostScript font */
139
int	shadowprint = OFF;		/* automatic bold printing if ON */
140
 
141
FILE	*fp_in;				/* read from this file */
142
FILE	*fp_out = stdout;		/* and write stuff here */
143
FILE	*fp_acct = NULL;		/* for accounting data */
144
 
145
/*****************************************************************************/
146
 
147
main(agc, agv)
148
 
149
    int		agc;
150
    char	*agv[];
151
 
152
{
153
 
154
/*
155
 *
156
 * A simple program that translates Diablo 1640 files into PostScript. Nothing
157
 * is guaranteed - the program not well tested and doesn't implement everything.
158
 *
159
 */
160
 
161
    argc = agc;				/* other routines may want them */
162
    argv = agv;
163
 
164
    prog_name = argv[0];		/* really just for error messages */
165
 
166
    init_signals();			/* sets up interrupt handling */
167
    header();				/* PostScript header comments */
168
    options();				/* handle the command line options */
169
    setup();				/* for PostScript */
170
    arguments();			/* followed by each input file */
171
    done();				/* print the last page etc. */
172
    account();				/* job accounting data */
173
 
174
    exit(x_stat);			/* not much could be wrong */
175
 
176
}   /* End of main */
177
 
178
/*****************************************************************************/
179
 
180
init_signals()
181
 
182
{
183
 
184
/*
185
 *
186
 * Makes sure we handle interrupts.
187
 *
188
 */
189
 
190
    if ( signal(SIGINT, interrupt) == SIG_IGN )  {
191
	signal(SIGINT, SIG_IGN);
192
	signal(SIGQUIT, SIG_IGN);
193
	signal(SIGHUP, SIG_IGN);
194
    } else {
195
	signal(SIGHUP, interrupt);
196
	signal(SIGQUIT, interrupt);
197
    }   /* End else */
198
 
199
    signal(SIGTERM, interrupt);
200
 
201
}   /* End of init_signals */
202
 
203
/*****************************************************************************/
204
 
205
header()
206
 
207
{
208
 
209
    int		ch;			/* return value from getopt() */
210
    int		old_optind = optind;	/* for restoring optind - should be 1 */
211
 
212
/*
213
 *
214
 * Scans the option list looking for things, like the prologue file, that we need
215
 * right away but could be changed from the default. Doing things this way is an
216
 * attempt to conform to Adobe's latest file structuring conventions. In particular
217
 * they now say there should be nothing executed in the prologue, and they have
218
 * added two new comments that delimit global initialization calls. Once we know
219
 * where things really are we write out the job header, follow it by the prologue,
220
 * and then add the ENDPROLOG and BEGINSETUP comments.
221
 *
222
 */
223
 
224
    while ( (ch = getopt(argc, argv, optnames)) != EOF )
225
	if ( ch == 'L' )
226
	    prologue = optarg;
227
	else if ( ch == '?' )
228
	    error(FATAL, "");
229
 
230
    optind = old_optind;		/* get ready for option scanning */
231
 
232
    fprintf(stdout, "%s", CONFORMING);
233
    fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
234
    fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
235
    fprintf(stdout, "%s %s\n", PAGES, ATEND);
236
    fprintf(stdout, "%s", ENDCOMMENTS);
237
 
238
    if ( cat(prologue) == FALSE )
239
	error(FATAL, "can't read %s", prologue);
240
 
241
    if ( DOROUND )
242
	cat(ROUNDPAGE);
243
 
244
    fprintf(stdout, "%s", ENDPROLOG);
245
    fprintf(stdout, "%s", BEGINSETUP);
246
    fprintf(stdout, "mark\n");
247
 
248
}   /* End of header */
249
 
250
/*****************************************************************************/
251
 
252
options()
253
 
254
{
255
 
256
    int		ch;			/* return value from getopt() */
257
    int		n;			/* for CR and LF modes */
258
 
259
/*
260
 *
261
 * Reads and processes the command line options. Added the -P option so arbitrary
262
 * PostScript code can be passed through. Expect it could be useful for changing
263
 * definitions in the prologue for which options have not been defined.
264
 *
265
 * Although any PostScript font can be used, things will only work for constant
266
 * width fonts.
267
 *
268
 */
269
 
270
    while ( (ch = getopt(argc, argv, optnames)) != EOF )  {
271
	switch ( ch )  {
272
	    case 'a':			/* aspect ratio */
273
		    fprintf(stdout, "/aspectratio %s def\n", optarg);
274
		    break;
275
 
276
	    case 'c':			/* copies */
277
		    copies = atoi(optarg);
278
		    fprintf(stdout, "/#copies %s store\n", optarg);
279
		    break;
280
 
281
	    case 'f':			/* use this PostScript font */
282
		    fontname = get_font(optarg);
283
		    fprintf(stdout, "/font /%s def\n", fontname);
284
		    break;
285
 
286
	    case 'h':			/* default character spacing */
287
		    ohmi = hmi = atoi(optarg) * HSCALE;
288
		    fprintf(stdout, "/hmi %s def\n", optarg);
289
		    break;
290
 
291
	    case 'l':			/* lines per page */
292
		    linespp = atoi(optarg);
293
		    break;
294
 
295
	    case 'm':			/* magnification */
296
		    fprintf(stdout, "/magnification %s def\n", optarg);
297
		    break;
298
 
299
	    case 'n':			/* forms per page */
300
		    formsperpage = atoi(optarg);
301
		    fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
302
		    fprintf(stdout, "/formsperpage %s def\n", optarg);
303
		    break;
304
 
305
	    case 'o':			/* output page list */
306
		    out_list(optarg);
307
		    break;
308
 
309
	    case 'p':			/* landscape or portrait mode */
310
		    if ( *optarg == 'l' )
311
			fprintf(stdout, "/landscape true def\n");
312
		    else fprintf(stdout, "/landscape false def\n");
313
		    break;
314
 
315
	    case 'r':			/* set CR and LF modes */
316
		    n = atoi(optarg);
317
		    if ( n & 01 )
318
			lfiscr = ON;
319
		    else lfiscr = OFF;
320
		    if ( n & 02 )
321
			crislf = ON;
322
		    else crislf = OFF;
323
		    break;
324
 
325
	    case 's':			/* point size */
326
		    fprintf(stdout, "/pointsize %s def\n", optarg);
327
		    break;
328
 
329
	    case 'v':			/* default line spacing */
330
		    ovmi = vmi = atoi(optarg) * VSCALE;
331
		    break;
332
 
333
	    case 'x':			/* shift things horizontally */
334
		    fprintf(stdout, "/xoffset %s def\n", optarg);
335
		    break;
336
 
337
	    case 'y':			/* and vertically on the page */
338
		    fprintf(stdout, "/yoffset %s def\n", optarg);
339
		    break;
340
 
341
	    case 'A':			/* force job accounting */
342
	    case 'J':
343
		    if ( (fp_acct = fopen(optarg, "a")) == NULL )
344
			error(FATAL, "can't open accounting file %s", optarg);
345
		    break;
346
 
347
	    case 'C':			/* copy file straight to output */
348
		    if ( cat(optarg) == FALSE )
349
			error(FATAL, "can't read %s", optarg);
350
		    break;
351
 
352
	    case 'E':			/* text font encoding */
353
		    fontencoding = optarg;
354
		    break;
355
 
356
	    case 'L':			/* PostScript prologue file */
357
		    prologue = optarg;
358
		    break;
359
 
360
	    case 'P':			/* PostScript pass through */
361
		    fprintf(stdout, "%s\n", optarg);
362
		    break;
363
 
364
	    case 'R':			/* special global or page level request */
365
		    saverequest(optarg);
366
		    break;
367
 
368
	    case 'D':			/* debug flag */
369
		    debug = ON;
370
		    break;
371
 
372
	    case 'I':			/* ignore FATAL errors */
373
		    ignore = ON;
374
		    break;
375
 
376
	    case '?':			/* don't understand the option */
377
		    error(FATAL, "");
378
		    break;
379
 
380
	    default:			/* don't know what to do for ch */
381
		    error(FATAL, "missing case for option %c\n", ch);
382
		    break;
383
	}   /* End switch */
384
    }   /* End while */
385
 
386
    argc -= optind;			/* get ready for non-option args */
387
    argv += optind;
388
 
389
}   /* End of options */
390
 
391
/*****************************************************************************/
392
 
393
char *get_font(name)
394
 
395
    char	*name;			/* name the user asked for */
396
 
397
{
398
 
399
    int		i;			/* for looking through fontmap[] */
400
 
401
/*
402
 *
403
 * Called from options() to map a user's font name into a legal PostScript name.
404
 * If the lookup fails *name is returned to the caller. That should let you choose
405
 * any PostScript font, although things will only work well for constant width
406
 * fonts.
407
 *
408
 */
409
 
410
    for ( i = 0; fontmap[i].name != NULL; i++ )
411
	if ( strcmp(name, fontmap[i].name) == 0 )
412
	    return(fontmap[i].val);
413
 
414
    return(name);
415
 
416
}   /* End of get_font */
417
 
418
/*****************************************************************************/
419
 
420
setup()
421
 
422
{
423
 
424
/*
425
 *
426
 * Handles things that must be done after the options are read but before the
427
 * input files are processed.
428
 *
429
 */
430
 
431
    writerequest(0, stdout);		/* global requests eg. manual feed */
432
    setencoding(fontencoding);
433
    fprintf(stdout, "setup\n");
434
 
435
    if ( formsperpage > 1 )  {
436
	if ( cat(formfile) == FALSE )
437
	    error(FATAL, "can't read %s", formfile);
438
	fprintf(stdout, "%d setupforms\n", formsperpage);
439
    }	/* End if */
440
 
441
    fprintf(stdout, "%s", ENDSETUP);
442
 
443
}   /* End of setup */
444
 
445
/*****************************************************************************/
446
 
447
arguments()
448
 
449
{
450
 
451
/*
452
 *
453
 * Makes sure all the non-option command line arguments are processed. If we get
454
 * here and there aren't any arguments left, or if '-' is one of the input files
455
 * we'll process stdin.
456
 *
457
 */
458
 
459
    fp_in = stdin;
460
 
461
    if ( argc < 1 )
462
	text();
463
    else {				/* at least one argument is left */
464
	while ( argc > 0 )  {
465
	    if ( strcmp(*argv, "-") == 0 )
466
		fp_in = stdin;
467
	    else if ( (fp_in = fopen(*argv, "r")) == NULL )
468
		error(FATAL, "can't open %s", *argv);
469
	    text();
470
	    if ( fp_in != stdin )
471
		fclose(fp_in);
472
	    argc--;
473
	    argv++;
474
	}   /* End while */
475
    }   /* End else */
476
 
477
}   /* End of arguments */
478
 
479
/*****************************************************************************/
480
 
481
done()
482
 
483
{
484
 
485
/*
486
 *
487
 * Finished with all the input files, so mark the end of the pages, make sure the
488
 * last page is printed, and restore the initial environment.
489
 *
490
 */
491
 
492
    fprintf(stdout, "%s", TRAILER);
493
    fprintf(stdout, "done\n");
494
    fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
495
    fprintf(stdout, "%s %d\n", PAGES, printed);
496
 
497
}   /* End of done */
498
 
499
/*****************************************************************************/
500
 
501
account()
502
 
503
{
504
 
505
/*
506
 *
507
 * Writes an accounting record to *fp_acct provided it's not NULL. Accounting
508
 * is requested using the -A or -J options.
509
 *
510
 */
511
 
512
    if ( fp_acct != NULL )
513
	fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
514
 
515
}   /* End of account */
516
 
517
/*****************************************************************************/
518
 
519
text()
520
 
521
{
522
 
523
    int		ch;			/* next input character */
524
 
525
/*
526
 *
527
 * Translates the next input file into PostScript. The redirect(-1) call forces
528
 * the initial output to go to /dev/null - so the stuff formfeed() does at the
529
 * end of each page doesn't go to stdout.
530
 *
531
 */
532
 
533
    redirect(-1);			/* get ready for the first page */
534
    formfeed();				/* force PAGE comment etc. */
535
    inittabs();
536
 
537
    while ( (ch = getc(fp_in)) != EOF )
538
	switch ( ch )  {
539
	    case '\010':		/* backspace */
540
		    backspace();
541
		    break;
542
 
543
	    case '\011':		/* horizontal tab */
544
		    htab();
545
		    break;
546
 
547
	    case '\012':		/* new line */
548
		    linefeed();
549
		    break;
550
 
551
	    case '\013':		/* vertical tab */
552
		    vtab();
553
		    break;
554
 
555
	    case '\014':		/* form feed */
556
		    formfeed();
557
		    break;
558
 
559
	    case '\015':		/* carriage return */
560
		    carriage();
561
		    break;
562
 
563
	    case '\016':		/* extended character set - SO */
564
		    break;
565
 
566
	    case '\017':		/* extended character set - SI */
567
		    break;
568
 
569
	    case '\031':		/* next char from supplementary set */
570
		    break;
571
 
572
	    case '\033':		/* 2 or 3 byte escape sequence */
573
		    escape();
574
		    break;
575
 
576
	    default:
577
		    oput(ch);
578
		    break;
579
	}   /* End switch */
580
 
581
    formfeed();				/* next file starts on a new page? */
582
 
583
}   /* End of text */
584
 
585
/*****************************************************************************/
586
 
587
inittabs()
588
 
589
{
590
 
591
    int		i;			/* loop index */
592
 
593
/*
594
 *
595
 * Initializes the horizontal and vertical tab arrays. The way tabs are handled is
596
 * quite inefficient and may not work for all initial hmi or vmi values.
597
 *
598
 */
599
 
600
    for ( i = 0; i < COLUMNS; i++ )
601
	htabstops[i] = ((i % 8) == 0) ? ON : OFF;
602
 
603
    for ( i = 0; i < ROWS; i++ )
604
	vtabstops[i] = ((i * ovmi) > BOTTOMMARGIN) ? ON : OFF;
605
 
606
}   /* End of inittabs */
607
 
608
/*****************************************************************************/
609
 
610
cleartabs()
611
 
612
{
613
 
614
    int		i;			/* loop index */
615
 
616
/*
617
 *
618
 * Clears all horizontal and vertical tab stops.
619
 *
620
 */
621
 
622
    for ( i = 0; i < ROWS; i++ )
623
	htabstops[i] = OFF;
624
 
625
    for ( i = 0; i < COLUMNS; i++ )
626
	vtabstops[i] = OFF;
627
 
628
}   /* End of cleartabs */
629
 
630
/*****************************************************************************/
631
 
632
formfeed()
633
 
634
{
635
 
636
/*
637
 *
638
 * Called whenever we've finished with the last page and want to get ready for the
639
 * next one. Also used at the beginning and end of each input file, so we have to
640
 * be careful about what's done. I've added a simple test before the showpage that
641
 * should eliminate the extra blank page that was put out at the end of many jobs,
642
 * but the PAGES comments may be wrong.
643
 *
644
 */
645
 
646
    if ( fp_out == stdout )		/* count the last page */
647
	printed++;
648
 
649
    endline();				/* print the last line */
650
 
651
    fprintf(fp_out, "cleartomark\n");
652
    if ( feof(fp_in) == 0 || markedpage == TRUE )
653
	fprintf(fp_out, "showpage\n");
654
    fprintf(fp_out, "saveobj restore\n");
655
    fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
656
 
657
    if ( ungetc(getc(fp_in), fp_in) == EOF )
658
	redirect(-1);
659
    else redirect(++page);
660
 
661
    fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
662
    fprintf(fp_out, "/saveobj save def\n");
663
    fprintf(fp_out, "mark\n");
664
    writerequest(printed+1, fp_out);
665
    fprintf(fp_out, "%d pagesetup\n", printed+1);
666
 
667
    vgoto(topmargin);
668
    hgoto(leftmargin);
669
 
670
    markedpage = FALSE;
671
 
672
}   /* End of formfeed */
673
 
674
/*****************************************************************************/
675
 
676
linefeed()
677
 
678
{
679
 
680
    int		line = 0;		/* current line - based on ovmi */
681
 
682
/*
683
 *
684
 * Adjust our current vertical position. If we've passed the bottom of the page
685
 * or exceeded the number of lines per page, print it and go to the upper left
686
 * corner of the next page. This routine is also called from carriage() if crislf
687
 * is ON.
688
 *
689
 */
690
 
691
    vmot(vmi);
692
 
693
    if ( lfiscr == ON )
694
	hgoto(leftmargin);
695
 
696
    if ( linespp > 0 )			/* means something so see where we are */
697
	line = vpos / ovmi + 1;
698
 
699
    if ( vpos > bottommargin || line > linespp )
700
	formfeed();
701
 
702
}   /* End of linefeed */
703
 
704
/*****************************************************************************/
705
 
706
carriage()
707
 
708
{
709
 
710
/*
711
 *
712
 * Handles carriage return character. If crislf is ON we'll generate a line feed
713
 * every time we get a carriage return character.
714
 *
715
 */
716
 
717
    if ( shadowprint == ON )		/* back to normal mode */
718
	changefont(fontname);
719
 
720
    advance = 1;
721
    shadowprint = OFF;
722
 
723
    hgoto(leftmargin);
724
 
725
    if ( crislf == ON )
726
	linefeed();
727
 
728
}   /* End of carriage */
729
 
730
/*****************************************************************************/
731
 
732
htab()
733
 
734
{
735
 
736
    int		col;			/* 'column' we'll be at next */
737
    int		i;			/* loop index */
738
 
739
/*
740
 *
741
 * Tries to figure out where the next tab stop is. Wasn't positive about this
742
 * one, since hmi can change. I'll assume columns are determined by the original
743
 * value of hmi. That fixes them on the page, which seems to make more sense than
744
 * letting them float all over the place.
745
 *
746
 */
747
 
748
    endline();
749
 
750
    col = hpos/ohmi + 1;
751
    for ( i = col; i < ROWS; i++ )
752
	if ( htabstops[i] == ON )  {
753
	    col = i;
754
	    break;
755
	}   /* End if */
756
 
757
    hgoto(col * ohmi);
758
    lastx = hpos;
759
 
760
}   /* End of htab */
761
 
762
/*****************************************************************************/
763
 
764
vtab()
765
 
766
{
767
 
768
    int		line;			/* line we'll be at next */
769
    int		i;			/* loop index */
770
 
771
/*
772
 *
773
 * Looks for the next vertical tab stop in the vtabstops[] array and moves to that
774
 * line. If we don't find a tab we'll just move down one line - shouldn't happen.
775
 *
776
 */
777
 
778
    endline();
779
 
780
    line = vpos/ovmi + 1;
781
    for ( i = line; i < COLUMNS; i++ )
782
	if ( vtabstops[i] == ON )  {
783
	    line = i;
784
	    break;
785
	}   /* End if */
786
 
787
    vgoto(line * ovmi);
788
 
789
}   /* End of vtab */
790
 
791
/*****************************************************************************/
792
 
793
backspace()
794
 
795
{
796
 
797
/*
798
 *
799
 * Moves backwards a distance equal to the current value of hmi, but don't go
800
 * past the left margin.
801
 *
802
 */
803
 
804
    endline();
805
 
806
    if ( hpos - leftmargin >= hmi )
807
	hmot(-hmi);
808
    else hgoto(leftmargin);		/* maybe just ignore the backspace?? */
809
 
810
    lastx = hpos;
811
 
812
}   /* End of backspace */
813
 
814
/*****************************************************************************/
815
 
816
escape()
817
 
818
{
819
 
820
    int		ch;			/* control character */
821
 
822
/*
823
 *
824
 * Handles special codes that are expected to follow an escape character. The
825
 * initial escape character is followed by one or two bytes.
826
 *
827
 */
828
 
829
    switch ( ch = getc(fp_in) ) {
830
	case 'T':			/* top margin */
831
		topmargin = vpos;
832
		break;
833
 
834
	case 'L':			/* bottom margin */
835
		bottommargin = vpos;
836
		break;
837
 
838
	case 'C':			/* clear top and bottom margins */
839
		bottommargin = BOTTOMMARGIN;
840
		topmargin = TOPMARGIN;
841
		break;
842
 
843
	case '9':			/* left margin */
844
		leftmargin = hpos;
845
		break;
846
 
847
	case '0':			/* right margin */
848
		rightmargin = hpos;
849
		break;
850
 
851
	case '1':			/* set horizontal tab */
852
		htabstops[hpos/ohmi] = ON;
853
		break;
854
 
855
	case '8':			/* clear horizontal tab at hpos */
856
		htabstops[hpos/ohmi] = OFF;
857
		break;
858
 
859
	case '-':			/* set vertical tab */
860
		vtabstops[vpos/ovmi] = ON;
861
		break;
862
 
863
	case '2':			/* clear all tabs */
864
		cleartabs();
865
		break;
866
 
867
	case '\014':			/* set lines per page */
868
		linespp = getc(fp_in);
869
		break;
870
 
871
	case '\037':			/* set hmi to next byte minus 1 */
872
		hmi = HSCALE * (getc(fp_in) - 1);
873
		break;
874
 
875
	case 'S':			/* reset hmi to default */
876
		hmi = ohmi;
877
		break;
878
 
879
	case '\011':			/* move to column given by next byte */
880
		hgoto((getc(fp_in)-1) * ohmi);
881
		break;
882
 
883
	case '?':			/* do carriage return after line feed */
884
		lfiscr = ON;
885
		break;
886
 
887
	case '!':			/* don't generate carriage return */
888
		lfiscr = OFF;
889
		break;
890
 
891
	case '5':			/* forward print mode */
892
		advance = 1;
893
		break;
894
 
895
	case '6':			/* backward print mode */
896
		advance = -1;
897
		break;
898
 
899
	case '\036':			/* set vmi to next byte minus 1 */
900
		vmi = VSCALE * (getc(fp_in) - 1);
901
		break;
902
 
903
	case '\013':			/* move to line given by next byte */
904
		vgoto((getc(fp_in)-1) * ovmi);
905
		break;
906
 
907
	case 'U':			/* positive half line feed */
908
		vmot(vmi/2);
909
		break;
910
 
911
	case 'D':			/* negative half line feed */
912
		vmot(-vmi/2);
913
		break;
914
 
915
	case '\012':			/* negative line feed */
916
		vmot(-vmi);
917
		break;
918
 
919
	case '\015':			/* clear all margins */
920
		bottommargin = BOTTOMMARGIN;
921
		topmargin = TOPMARGIN;
922
		leftmargin = BOTTOMMARGIN;
923
		rightmargin = RIGHTMARGIN;
924
		break;
925
 
926
	case 'E':			/* auto underscore - use italic font */
927
		changefont("/Courier-Oblique");
928
		break;
929
 
930
	case 'R':			/* disable auto underscore */
931
		changefont(fontname);
932
		break;
933
 
934
	case 'O':			/* bold/shadow printing */
935
	case 'W':
936
		changefont("/Courier-Bold");
937
		shadowprint = ON;
938
		break;
939
 
940
	case '&':			/* disable bold printing */
941
		changefont(fontname);
942
		shadowprint = OFF;
943
		break;
944
 
945
	case '/':			/* ignored 2 byte escapes */
946
	case '\\':
947
	case '<':
948
	case '>':
949
	case '%':
950
	case '=':
951
	case '.':
952
	case '4':
953
	case 'A':
954
	case 'B':
955
	case 'M':
956
	case 'N':
957
	case 'P':
958
	case 'Q':
959
	case 'X':
960
	case '\010':
961
		break;
962
 
963
	case ',':			/* ignored 3 byte escapes */
964
	case '\016':
965
	case '\021':
966
		getc(fp_in);
967
		break;
968
 
969
	case '3':			/* graphics mode - should quit! */
970
	case '7':
971
	case 'G':
972
	case 'V':
973
	case 'Y':
974
	case 'Z':
975
		error(FATAL, "graphics mode is not implemented");
976
		break;
977
 
978
	default:
979
		error(FATAL, "missing case for escape o%o\n", ch);
980
		break;
981
    }	/* End switch */
982
 
983
}   /* End of escape */
984
 
985
/*****************************************************************************/
986
 
987
vmot(n)
988
 
989
    int		n;			/* move this far vertically */
990
 
991
{
992
 
993
/*
994
 *
995
 * Move vertically n units from where we are.
996
 *
997
 */
998
 
999
    vpos += n;
1000
 
1001
}   /* End of vmot */
1002
 
1003
/*****************************************************************************/
1004
 
1005
vgoto(n)
1006
 
1007
    int		n;			/* new vertical position */
1008
 
1009
{
1010
 
1011
/*
1012
 *
1013
 * Moves to absolute vertical position n.
1014
 *
1015
 */
1016
 
1017
    vpos = n;
1018
 
1019
}   /* End of vgoto */
1020
 
1021
/*****************************************************************************/
1022
 
1023
hmot(n)
1024
 
1025
    int		n;			/* move this horizontally */
1026
 
1027
{
1028
 
1029
/*
1030
 *
1031
 * Moves horizontally n units from our current position.
1032
 *
1033
 */
1034
 
1035
    hpos += n * advance;
1036
 
1037
    if ( hpos < leftmargin )
1038
	hpos = leftmargin;
1039
 
1040
}   /* End of hmot */
1041
 
1042
/*****************************************************************************/
1043
 
1044
hgoto(n)
1045
 
1046
    int		n;			/* go to this horizontal position */
1047
 
1048
{
1049
 
1050
/*
1051
 *
1052
 * Moves to absolute horizontal position n.
1053
 *
1054
 */
1055
 
1056
    hpos = n;
1057
 
1058
}   /* End of hgoto */
1059
 
1060
/*****************************************************************************/
1061
 
1062
changefont(name)
1063
 
1064
    char	*name;
1065
 
1066
{
1067
 
1068
/*
1069
 *
1070
 * Changes the current font. Used to get in and out of auto underscore and bold
1071
 * printing.
1072
 *
1073
 */
1074
 
1075
    endline();
1076
    fprintf(fp_out, "%s f\n", name);
1077
 
1078
}   /* End of changefont */
1079
 
1080
/*****************************************************************************/
1081
 
1082
startline()
1083
 
1084
{
1085
 
1086
/*
1087
 *
1088
 * Called whenever we want to be certain we're ready to start pushing characters
1089
 * into an open string on the stack. If stringcount is positive we've already
1090
 * started, so there's nothing to do. The first string starts in column 1.
1091
 *
1092
 */
1093
 
1094
    if ( stringcount < 1 )  {
1095
	putc('(', fp_out);
1096
	stringstart = lastx = hpos;
1097
	lasty = vpos;
1098
	lasthmi = hmi;
1099
	lastc = -1;
1100
	prevx = -1;
1101
	stringcount = 1;
1102
    }	/* End if */
1103
 
1104
}   /* End of startline */
1105
 
1106
/*****************************************************************************/
1107
 
1108
endline()
1109
 
1110
{
1111
 
1112
/*
1113
 *
1114
 * Generates a call to the PostScript procedure that processes the text on the
1115
 * the stack - provided stringcount is positive.
1116
 *
1117
 */
1118
 
1119
    if ( stringcount > 0 )
1120
	fprintf(fp_out, ")%d %d %d t\n", stringstart, lasty, lasthmi);
1121
 
1122
    stringcount = 0;
1123
 
1124
}   /* End of endline */
1125
 
1126
/*****************************************************************************/
1127
 
1128
endstring()
1129
 
1130
{
1131
 
1132
/*
1133
 *
1134
 * Takes the string we've been working on and adds it to the output file. Called
1135
 * when we need to adjust our horizontal position before starting a new string.
1136
 * Also called from endline() when we're done with the current line.
1137
 *
1138
 */
1139
 
1140
    if ( stringcount > 0 )  {
1141
	fprintf(fp_out, ")%d(", stringstart);
1142
	lastx = stringstart = hpos;
1143
	stringcount++;
1144
    }	/* End if */
1145
 
1146
}   /* End of endstring */
1147
 
1148
/*****************************************************************************/
1149
 
1150
oput(ch)
1151
 
1152
    int		ch;			/* next output character */
1153
 
1154
{
1155
 
1156
/*
1157
 *
1158
 * Responsible for adding all printing characters from the input file to the
1159
 * open string on top of the stack. The only other characters that end up in
1160
 * that string are the quotes required for special characters. Reverse printing
1161
 * mode hasn't been tested but it should be close. hpos and lastx should disagree
1162
 * each time (except after startline() does something), and that should force a
1163
 * call to endstring() for every character.
1164
 *
1165
 */
1166
 
1167
    if ( stringcount > 100 )		/* don't put too much on the stack */
1168
	endline();
1169
 
1170
    if ( vpos != lasty )
1171
	endline();
1172
 
1173
    if ( advance == -1 )		/* for reverse printing - move first */
1174
	hmot(hmi);
1175
 
1176
    startline();
1177
 
1178
    if ( lastc != ch || hpos != prevx )  {
1179
	if ( lastx != hpos )
1180
	    endstring();
1181
 
1182
	if ( isascii(ch) && isprint(ch) ) {
1183
	    if ( ch == '\\' || ch == '(' || ch == ')' )
1184
		putc('\\', fp_out);
1185
	    putc(ch, fp_out);
1186
	} else fprintf(fp_out, "\\%.3o", ch & 0377);
1187
 
1188
	lastc = ch;
1189
	prevx = hpos;
1190
	lastx += lasthmi;
1191
    }	/* End if */
1192
 
1193
    if ( advance != -1 )
1194
	hmot(hmi);
1195
 
1196
    markedpage = TRUE;
1197
 
1198
}   /* End of oput */
1199
 
1200
/*****************************************************************************/
1201
 
1202
redirect(pg)
1203
 
1204
    int		pg;			/* next page we're printing */
1205
 
1206
{
1207
 
1208
    static FILE	*fp_null = NULL;	/* if output is turned off */
1209
 
1210
/*
1211
 *
1212
 * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
1213
 * otherwise output goes to stdout.
1214
 *
1215
 */
1216
 
1217
    if ( pg >= 0 && in_olist(pg) == ON )
1218
	fp_out = stdout;
1219
    else if ( (fp_out = fp_null) == NULL )
1220
	fp_out = fp_null = fopen("/dev/null", "w");
1221
 
1222
}   /* End of redirect */
1223
 
1224
/*****************************************************************************/
1225