2
* $Id: psf.c,v 1.8 2002/05/23 15:58:55 rufustfirefly Exp $
4
* Copyright (c) 1990,1995 Regents of The University of Michigan.
5
* All Rights Reserved. See COPYRIGHT.
7
* PostScript Filter, psf.
9
* Handles both PostScript files and text files. Files with the
10
* '%!' PostScript header are sent directly to the printer,
11
* unmodified. Text files are first converted to PostScript,
12
* then sent. Printers may be directly attached or on an AppleTalk
13
* network. Other media are possible. Currently, psf invokes
14
* pap to send files to AppleTalk-ed printers. Replace the pap*
15
* variables to use another program for communication. psf only
16
* converts plain-text. If called as "tf" or "df", psf will invoke
17
* a troff or dvi to PostScript converter.
22
#endif /* HAVE_CONFIG_H */
28
#endif /* HAVE_UNISTD_H */
31
/* POSIX.1 sys/wait.h check */
32
#include <sys/types.h>
33
#ifdef HAVE_SYS_WAIT_H
35
#endif /* HAVE_SYS_WAIT_H */
37
#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
38
#endif /* ! WEXITSTATUS */
40
#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
41
#endif /* ! WIFEXITED */
45
#include <atalk/paths.h>
53
/* Forward Declarations */
54
int pexecv(char *path, char *argv[]);
58
char psapath[] = _PATH_PSA;
59
char *psaargv[] = { "psa", 0, 0, 0, 0 };
62
* If we're not doing accounting, we just call pap as below.
63
* If we are doing accounting, we call pap twice. The first time,
64
* we call it with "-E" in arg 2, pagecount.ps in arg 3, and "-" in
65
* arg 4. The second time, we call it with "-c" in arg 2, pagecount.ps
66
* in arg 3, and 0 in arg 4.
68
char pappath[] = _PATH_PAP;
69
char *papargv[] = { "pap", "-sstatus", 0, 0, 0, 0, 0, 0 };
71
char revpath[] = _PATH_PSORDER;
72
char *revargv[] = { "psorder", "-d", 0 };
74
char *filtargv[] = { 0, 0, 0 };
76
char inbuf[ 1024 * 8 ];
79
FILE *acctfile = NULL;
81
int width = 80, length = 66, indent = 0;
82
char *prog, *name, *host;
90
{ 80, 66, 8.5, 11.0 }, /* US Letter */
91
{ 80, 70, 8.27, 11.69 }, /* A4 */
98
int c, rc, children = 0;
100
int psafileno, multiconn = 0, waitidle = 0, waitidle2 = 0;
104
extern int optind, opterr;
107
if (( prog = rindex( av[ 0 ], '/' )) == NULL ) {
113
openlog( prog, LOG_PID );
115
openlog( prog, LOG_PID, LOG_LPR );
118
while (( c = getopt( ac, av, "P:C:D:F:L:J:x:y:n:h:w:l:i:c" )) != EOF ) {
129
width = atoi( optarg );
132
* Some version of lpd pass 0 for the page width.
137
#endif /* ZEROWIDTH */
141
length = atoi( optarg );
145
indent = atoi( optarg );
148
case 'c' : /* Print control chars */
162
syslog( LOG_ERR, "bad option: %c", c );
167
if ( ac - optind > 1 ) {
168
syslog( LOG_ERR, "Too many arguments" );
172
if ( index( prog, 'w' )) {
175
if ( index( prog, 'W' )) {
178
if ( index( prog, 'm' )) {
183
syslog( LOG_INFO, "starting for %s", name ? name : "?" );
186
if (( inlen = read( 0, inbuf, sizeof( inbuf ))) < 0 ) {
187
syslog( LOG_ERR, "read: %s", strerror(errno) );
190
if ( inlen == 0 ) { /* nothing to be done */
191
syslog( LOG_INFO, "done" );
196
* If we've been given an accounting file, start the accounting
200
/* build arguments */
201
psaargv[ 1 ] = av[ optind ];
204
if (( c = pexecv( psapath, psaargv )) < 0 ) {
205
syslog( LOG_ERR, "%s: %s", psapath, strerror(errno) );
209
syslog( LOG_INFO, "accounting with psa[%d]", c );
213
* Check prog's name to decide what programs to execute.
215
if ( strstr( prog, "pap" ) != NULL ) {
216
if ( optind < ac ) { /* accounting */
219
psafileno = getdtablesize();
221
dup2( 1, psafileno );
227
papargv[ 5 ] = _PATH_PAGECOUNT;
230
} else if ( waitidle ) {
234
papargv[ 5 ] = _PATH_PAGECOUNT;
240
papargv[ 4 ] = _PATH_PAGECOUNT;
246
* This is how it should be done.
249
papargv[ 3 ] = _PATH_PAGECOUNT;
251
papargv[ 5 ] = _PATH_PAGECOUNT;
261
if (( c = pexecv( pappath, papargv )) < 0 ) {
262
syslog( LOG_ERR, "%s: %s", pappath, strerror(errno) );
266
syslog( LOG_INFO, "sending to pap[%d]", c );
270
* Might be a good idea to have both a "forw" and a "rev", so that
271
* reversed documents can be reordered for the printing device.
273
if ( strstr( prog, "rev" ) != NULL ) {
274
if (( c = pexecv( revpath, revargv )) < 0 ) {
275
syslog( LOG_ERR, "%s: %s", revpath, strerror(errno) );
278
syslog( LOG_INFO, "sending to rev[%d]", c );
283
* Invoke an external (script) filter to produce PostScript from
286
if ( *prog != 'i' && *prog != 'o' && *( prog + 1 ) == 'f' ) {
287
filtargv[ 0 ] = filtargv[ 1 ] = prog;
288
if (( c = pexecv( _PATH_PSFILTER, filtargv )) < 0 ) {
289
syslog( LOG_ERR, "%s: %s", _PATH_PSFILTER, strerror(errno) );
292
syslog( LOG_INFO, "external filter[%d]", c );
294
rc = copyio(); /* external filter */
296
if ( inlen >= 2 && inbuf[ 0 ] == '%' && inbuf[ 1 ] == '!' ) {
297
syslog( LOG_INFO, "PostScript" );
298
rc = copyio(); /* PostScript */
299
} else if ( inlen >= 2 && inbuf[ 0 ] == '\033' && inbuf[ 1 ] == '%' ) {
300
syslog( LOG_INFO, "PostScript w/PJL" );
301
rc = copyio(); /* PostScript */
303
syslog( LOG_INFO, "straight text" );
304
rc = textps(); /* straight text */
309
if ( strstr( prog, "pap" ) != NULL && optind < ac && multiconn ) {
310
dup2( psafileno, 1 );
315
papargv[ 4 ] = _PATH_PAGECOUNT;
317
} else if ( waitidle ) {
319
papargv[ 4 ] = _PATH_PAGECOUNT;
322
papargv[ 3 ] = _PATH_PAGECOUNT;
326
if (( c = pexecv( pappath, papargv )) < 0 ) {
327
syslog( LOG_ERR, "%s: %s", pappath, strerror(errno) );
331
syslog( LOG_INFO, "pagecount with pap[%d]", c );
339
if (( c = wait3( &status, 0, 0 )) < 0 ) {
340
syslog( LOG_ERR, "wait3: %s", strerror(errno) );
343
if ( WIFEXITED( status )) {
345
#define WEXITSTATUS(x) ((x).w_status)
346
#endif /* WEXITSTATUS */
347
if ( WEXITSTATUS( status ) != 0 ) {
348
syslog( LOG_ERR, "%d died with %d", c, WEXITSTATUS( status ));
349
exit( WEXITSTATUS( status ));
351
syslog( LOG_INFO, "%d done", c );
355
syslog( LOG_ERR, "%d died badly", c );
361
syslog( LOG_INFO, "pausing" );
362
kill( getpid(), SIGSTOP );
363
syslog( LOG_INFO, "restarting" );
367
syslog( LOG_INFO, "done" );
373
/* implement the FSM needed to do the suspend. Note that
374
* the last characters will be \031\001 so don't worry
375
* Fun things: 1. \031\001 should not be written to output device
376
* 2. The \031 can be last char of one read, \001 first of next
377
* - we need to write \031 if not followed by \001
386
* First, \031 and \001 *must* be the last things in the buffer
387
* (\001 can be the first thing in the next buffer). There's no
388
* need to scan any of the intervening bytes. Second, if there's
389
* more input, the escape sequence was bogus, and we should keep
394
if ( inbuf[ 0 ] == '\001' ) {
398
if ( write( 1, "\031", 1 ) != 1 ) {
399
syslog( LOG_ERR, "write: %s", strerror(errno) );
405
if ( inbuf[ 0 ] == '\031' ) {
411
if ( write( 1, "\031", 1 ) != 1 ) {
412
syslog( LOG_ERR, "write: %s", strerror(errno) );
417
if ( inbuf[ inlen - 2 ] == '\031' &&
418
inbuf[ inlen - 1 ] == '\001' ) {
420
} else if ( inbuf[ inlen - 1 ] == '\031' ) {
426
if (( inlen > 0 ) && ( write( 1, inbuf, inlen ) != inlen )) {
427
syslog( LOG_ERR, "write: %s", strerror(errno) );
433
} while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
440
if ( select( 1, &fdset, NULL, NULL, &tv ) != 0 ) {
441
if (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 ) {
448
syslog( LOG_ERR, "read: %s", strerror(errno) );
453
if ( write( 1, "\031", 1 ) != 1 ) {
454
syslog( LOG_ERR, "write: %s", strerror(errno) );
457
} else if ( ctl == 2 ) {
463
char *font = "Courier";
467
/GSV save def % global VM\n\
469
/SV save def % save vmstate\n\
470
dup /H exch def % save font height\n\
471
exch findfont exch scalefont setfont % select font\n\
472
( ) stringwidth pop /W exch def % save font width\n\
473
0.5 sub 72 mul /CY exch def % save start Y\n\
474
pop 0.5 add 72 mul /CX exch def % save start X\n\
475
CX CY moveto % make current point\n\
478
/NL { CX CY H sub dup /CY exch def moveto } bind def\n\
479
/CR { CX CY moveto } bind def\n\
480
/B { W neg 0 rmoveto}bind def\n\
481
/T { W mul 0 rmoveto}bind def\n\
482
/EP { SV restore showpage } bind def\n\
487
struct papersize papersize;
488
int state = 0, line = 0, col = 0, npages = 0, rc, i;
491
#define elements(x) (sizeof(x)/sizeof((x)[0]))
492
for ( i = 0; i < elements( papersizes ); i++ ) {
493
if ( width == papersizes[ 0 ].width &&
494
length == papersizes[ 0 ].length ) {
495
papersize = papersizes[ i ];
499
if ( i >= elements( papersizes )) {
500
papersize = papersizes[ 0 ]; /* default */
503
#define ST_AVAIL (1<<0)
504
#define ST_CONTROL (1<<1)
505
#define ST_PAGE (1<<2)
507
* convert text lines to postscript.
508
* A grungy little state machine. If I was more creative, I could
509
* probably think of a better way of doing this...
515
if (( state & ST_PAGE ) == 0 && *p != '\031' && *p != '\001' ) {
517
printf( "%%!PS-Adobe-2.0\n%%%%Pages: (atend)\n" );
518
printf( "%%%%DocumentFonts: %s\n", font );
521
/* output postscript prologue: */
522
if ( write( 1, pspro, sizeof( pspro ) - 1 ) !=
523
sizeof( pspro ) - 1 ) {
524
syslog( LOG_ERR, "write prologue: %s", strerror(errno) );
527
if ( name && host ) {
528
printf( "statusdict /jobname (%s@%s) put\n", name,
533
printf( "%%%%Page: ? %d\n", ++npages );
534
printf( "%d %f %f /%s %d SP\n", indent,
535
papersize.win, papersize.lin, font, point );
538
if ( state & ST_CONTROL && *p != '\001' ) {
539
/* It is a very bad thing to toss a job because it contains
540
* unprintable characters. Instead, we will convert them to
541
* question marks. This is adapted from a solution described
542
* by Werner Eugster <eugster@giub.unibe.ch> on his ApplePrint
543
* webpage (http://www.giub.unibe.ch/~eugster/appleprint.html).
545
* Note that this is rather ugly code. The same change is
546
* applied identically at two different locations in this file.
547
* It would be better someday to combine the two.
551
"unprintable character (0x%x) converted to ?!\n",
553
putchar( '?' ); /* Replace unprintable char with a question mark. */
555
printf( "\\%o", (unsigned char)031 );
557
state &= ~ST_CONTROL;
562
case '\n' : /* end of line */
563
if ( state & ST_AVAIL ) {
570
if ( line >= length ) {
577
case '\r' : /* carriage return (for overtyping) */
578
if ( state & ST_AVAIL ) {
585
case '\f' : /* form feed */
586
if ( state & ST_AVAIL ) {
596
case '\b' : /* backspace */
597
/* show line, back up one character */
598
if ( state & ST_AVAIL ) {
606
case '\t' : /* tab */
607
if ( state & ST_AVAIL ) {
611
printf( "%d T\n", 8 - ( col % 8 ));
612
col += 8 - ( col % 8 );
616
* beginning of lpr control sequence
622
case '\001' : /* lpr control sequence */
623
if ( state & ST_CONTROL ) {
632
if (( state & ST_AVAIL ) == 0 ) {
640
if (( state & ST_AVAIL ) == 0 ) {
644
if ( !isascii( *p ) || !isprint( *p )) {
647
"unprintable character (0x%x) converted to ?!\n",
649
putchar( '?' ); /* Replace unprintable char with a question mark. */
651
printf( "\\%o", (unsigned char)*p );
661
} while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
663
syslog( LOG_ERR, "read: %s", strerror(errno) );
669
if ( state & ST_AVAIL ) {
674
if ( state & ST_PAGE ) {
680
printf( "%%%%Trailer\nGSV restore\n%%%%Pages: %d\n%%%%EOF\n", npages );
688
* Interface to pipe and exec, for starting children in pipelines.
690
* Manipulates file descriptors 0, 1, and 2, such that the new child
691
* is reading from the parent's output.
693
int pexecv( path, argv )
698
if ( pipe( fd ) < 0 ) {
702
switch ( c = fork()) {
708
if ( close( fd[ 1 ] ) < 0 ) {
711
if ( dup2( fd[ 0 ], 0 ) < 0 ) {
714
if ( close( fd[ 0 ] ) < 0 ) {
722
if ( close( fd[ 0 ] ) < 0 ) {
725
if ( dup2( fd[ 1 ], 1 ) < 0 ) {
728
if ( close( fd[ 1 ] ) < 0 ) {