2 * $Id: psf.c,v 1.11 2009-10-14 01:38:28 didg 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 static char psapath[] = _PATH_PSA;
59 static 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 static char pappath[] = _PATH_PAP;
69 static char *papargv[] = { "pap", "-sstatus", 0, 0, 0, 0, 0, 0 };
71 static char revpath[] = _PATH_PSORDER;
72 static char *revargv[] = { "psorder", "-d", 0 };
74 static char *filtargv[] = { 0, 0, 0 };
76 static char inbuf[ 1024 * 8 ];
79 static FILE *acctfile = NULL;
81 static int width = 80, length = 66, indent = 0;
82 static char *prog, *name, *host;
84 static struct papersize {
90 { 80, 66, 8.5, 11.0 }, /* US Letter */
91 { 80, 70, 8.27, 11.69 }, /* A4 */
94 int main( int ac, char **av)
96 int c, rc, children = 0;
98 int psafileno = 0, multiconn = 0, waitidle = 0, waitidle2 = 0;
102 extern int optind, opterr;
105 if (( prog = rindex( av[ 0 ], '/' )) == NULL ) {
111 openlog( prog, LOG_PID );
113 openlog( prog, LOG_PID, LOG_LPR );
116 while (( c = getopt( ac, av, "P:C:D:F:L:J:x:y:n:h:w:l:i:c" )) != EOF ) {
127 width = atoi( optarg );
130 * Some version of lpd pass 0 for the page width.
135 #endif /* ZEROWIDTH */
139 length = atoi( optarg );
143 indent = atoi( optarg );
146 case 'c' : /* Print control chars */
160 syslog( LOG_ERR, "bad option: %c", c );
165 if ( ac - optind > 1 ) {
166 syslog( LOG_ERR, "Too many arguments" );
170 if ( index( prog, 'w' )) {
173 if ( index( prog, 'W' )) {
176 if ( index( prog, 'm' )) {
181 syslog( LOG_INFO, "starting for %s", name ? name : "?" );
184 if (( inlen = read( 0, inbuf, sizeof( inbuf ))) < 0 ) {
185 syslog( LOG_ERR, "read: %s", strerror(errno) );
188 if ( inlen == 0 ) { /* nothing to be done */
189 syslog( LOG_INFO, "done" );
194 * If we've been given an accounting file, start the accounting
198 /* build arguments */
199 psaargv[ 1 ] = av[ optind ];
202 if (( c = pexecv( psapath, psaargv )) < 0 ) {
203 syslog( LOG_ERR, "%s: %s", psapath, strerror(errno) );
207 syslog( LOG_INFO, "accounting with psa[%d]", c );
211 * Check prog's name to decide what programs to execute.
213 if ( strstr( prog, "pap" ) != NULL ) {
214 if ( optind < ac ) { /* accounting */
217 psafileno = getdtablesize();
219 dup2( 1, psafileno );
225 papargv[ 5 ] = _PATH_PAGECOUNT;
228 } else if ( waitidle ) {
232 papargv[ 5 ] = _PATH_PAGECOUNT;
238 papargv[ 4 ] = _PATH_PAGECOUNT;
244 * This is how it should be done.
247 papargv[ 3 ] = _PATH_PAGECOUNT;
249 papargv[ 5 ] = _PATH_PAGECOUNT;
259 if (( c = pexecv( pappath, papargv )) < 0 ) {
260 syslog( LOG_ERR, "%s: %s", pappath, strerror(errno) );
264 syslog( LOG_INFO, "sending to pap[%d]", c );
268 * Might be a good idea to have both a "forw" and a "rev", so that
269 * reversed documents can be reordered for the printing device.
271 if ( strstr( prog, "rev" ) != NULL ) {
272 if (( c = pexecv( revpath, revargv )) < 0 ) {
273 syslog( LOG_ERR, "%s: %s", revpath, strerror(errno) );
276 syslog( LOG_INFO, "sending to rev[%d]", c );
281 * Invoke an external (script) filter to produce PostScript from
284 if ( *prog != 'i' && *prog != 'o' && *( prog + 1 ) == 'f' ) {
285 filtargv[ 0 ] = filtargv[ 1 ] = prog;
286 if (( c = pexecv( _PATH_PSFILTER, filtargv )) < 0 ) {
287 syslog( LOG_ERR, "%s: %s", _PATH_PSFILTER, strerror(errno) );
290 syslog( LOG_INFO, "external filter[%d]", c );
292 rc = copyio(); /* external filter */
294 if ( inlen >= 2 && inbuf[ 0 ] == '%' && inbuf[ 1 ] == '!' ) {
295 syslog( LOG_INFO, "PostScript" );
296 rc = copyio(); /* PostScript */
297 } else if ( inlen >= 2 && inbuf[ 0 ] == '\033' && inbuf[ 1 ] == '%' ) {
298 syslog( LOG_INFO, "PostScript w/PJL" );
299 rc = copyio(); /* PostScript */
301 syslog( LOG_INFO, "straight text" );
302 rc = textps(); /* straight text */
307 if ( strstr( prog, "pap" ) != NULL && optind < ac && multiconn ) {
308 dup2( psafileno, 1 );
313 papargv[ 4 ] = _PATH_PAGECOUNT;
315 } else if ( waitidle ) {
317 papargv[ 4 ] = _PATH_PAGECOUNT;
320 papargv[ 3 ] = _PATH_PAGECOUNT;
324 if (( c = pexecv( pappath, papargv )) < 0 ) {
325 syslog( LOG_ERR, "%s: %s", pappath, strerror(errno) );
329 syslog( LOG_INFO, "pagecount with pap[%d]", c );
337 if (( c = wait3( &status, 0, 0 )) < 0 ) {
338 syslog( LOG_ERR, "wait3: %s", strerror(errno) );
341 if ( WIFEXITED( status )) {
343 #define WEXITSTATUS(x) ((x).w_status)
344 #endif /* WEXITSTATUS */
345 if ( WEXITSTATUS( status ) != 0 ) {
346 syslog( LOG_ERR, "%d died with %d", c, WEXITSTATUS( status ));
347 exit( WEXITSTATUS( status ));
349 syslog( LOG_INFO, "%d done", c );
353 syslog( LOG_ERR, "%d died badly", c );
359 syslog( LOG_INFO, "pausing" );
360 kill( getpid(), SIGSTOP );
361 syslog( LOG_INFO, "restarting" );
365 syslog( LOG_INFO, "done" );
371 /* implement the FSM needed to do the suspend. Note that
372 * the last characters will be \031\001 so don't worry
373 * Fun things: 1. \031\001 should not be written to output device
374 * 2. The \031 can be last char of one read, \001 first of next
375 * - we need to write \031 if not followed by \001
384 * First, \031 and \001 *must* be the last things in the buffer
385 * (\001 can be the first thing in the next buffer). There's no
386 * need to scan any of the intervening bytes. Second, if there's
387 * more input, the escape sequence was bogus, and we should keep
392 if ( inbuf[ 0 ] == '\001' ) {
396 if ( write( 1, "\031", 1 ) != 1 ) {
397 syslog( LOG_ERR, "write: %s", strerror(errno) );
403 if ( inbuf[ 0 ] == '\031' ) {
409 if ( write( 1, "\031", 1 ) != 1 ) {
410 syslog( LOG_ERR, "write: %s", strerror(errno) );
415 if ( inbuf[ inlen - 2 ] == '\031' &&
416 inbuf[ inlen - 1 ] == '\001' ) {
418 } else if ( inbuf[ inlen - 1 ] == '\031' ) {
424 if (( inlen > 0 ) && ( write( 1, inbuf, inlen ) != inlen )) {
425 syslog( LOG_ERR, "write: %s", strerror(errno) );
431 } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
438 if ( select( 1, &fdset, NULL, NULL, &tv ) != 0 ) {
439 if (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 ) {
446 syslog( LOG_ERR, "read: %s", strerror(errno) );
451 if ( write( 1, "\031", 1 ) != 1 ) {
452 syslog( LOG_ERR, "write: %s", strerror(errno) );
455 } else if ( ctl == 2 ) {
461 static char *font = "Courier";
462 static int point = 11;
464 static char pspro[] = "\
465 /GSV save def % global VM\n\
467 /SV save def % save vmstate\n\
468 dup /H exch def % save font height\n\
469 exch findfont exch scalefont setfont % select font\n\
470 ( ) stringwidth pop /W exch def % save font width\n\
471 0.5 sub 72 mul /CY exch def % save start Y\n\
472 pop 0.5 add 72 mul /CX exch def % save start X\n\
473 CX CY moveto % make current point\n\
476 /NL { CX CY H sub dup /CY exch def moveto } bind def\n\
477 /CR { CX CY moveto } bind def\n\
478 /B { W neg 0 rmoveto}bind def\n\
479 /T { W mul 0 rmoveto}bind def\n\
480 /EP { SV restore showpage } bind def\n\
485 struct papersize papersize;
486 int state = 0, line = 0, col = 0, npages = 0, rc;
490 #define elements(x) (sizeof(x)/sizeof((x)[0]))
491 for ( i = 0; i < elements( papersizes ); i++ ) {
492 if ( width == papersizes[ 0 ].width &&
493 length == papersizes[ 0 ].length ) {
494 papersize = papersizes[ i ];
498 if ( i >= elements( papersizes )) {
499 papersize = papersizes[ 0 ]; /* default */
502 #define ST_AVAIL (1<<0)
503 #define ST_CONTROL (1<<1)
504 #define ST_PAGE (1<<2)
506 * convert text lines to postscript.
507 * A grungy little state machine. If I was more creative, I could
508 * probably think of a better way of doing this...
514 if (( state & ST_PAGE ) == 0 && *p != '\031' && *p != '\001' ) {
516 printf( "%%!PS-Adobe-2.0\n%%%%Pages: (atend)\n" );
517 printf( "%%%%DocumentFonts: %s\n", font );
520 /* output postscript prologue: */
521 if ( write( 1, pspro, sizeof( pspro ) - 1 ) !=
522 sizeof( pspro ) - 1 ) {
523 syslog( LOG_ERR, "write prologue: %s", strerror(errno) );
526 if ( name && host ) {
527 printf( "statusdict /jobname (%s@%s) put\n", name,
532 printf( "%%%%Page: ? %d\n", ++npages );
533 printf( "%d %f %f /%s %d SP\n", indent,
534 papersize.win, papersize.lin, font, point );
537 if ( state & ST_CONTROL && *p != '\001' ) {
538 /* It is a very bad thing to toss a job because it contains
539 * unprintable characters. Instead, we will convert them to
540 * question marks. This is adapted from a solution described
541 * by Werner Eugster <eugster@giub.unibe.ch> on his ApplePrint
542 * webpage (http://www.giub.unibe.ch/~eugster/appleprint.html).
544 * Note that this is rather ugly code. The same change is
545 * applied identically at two different locations in this file.
546 * It would be better someday to combine the two.
550 "unprintable character (0x%x) converted to ?!\n",
552 putchar( '?' ); /* Replace unprintable char with a question mark. */
554 printf( "\\%o", (unsigned char)031 );
556 state &= ~ST_CONTROL;
561 case '\n' : /* end of line */
562 if ( state & ST_AVAIL ) {
569 if ( line >= length ) {
576 case '\r' : /* carriage return (for overtyping) */
577 if ( state & ST_AVAIL ) {
584 case '\f' : /* form feed */
585 if ( state & ST_AVAIL ) {
595 case '\b' : /* backspace */
596 /* show line, back up one character */
597 if ( state & ST_AVAIL ) {
605 case '\t' : /* tab */
606 if ( state & ST_AVAIL ) {
610 printf( "%d T\n", 8 - ( col % 8 ));
611 col += 8 - ( col % 8 );
615 * beginning of lpr control sequence
621 case '\001' : /* lpr control sequence */
622 if ( state & ST_CONTROL ) {
631 if (( state & ST_AVAIL ) == 0 ) {
639 if (( state & ST_AVAIL ) == 0 ) {
643 if ( !isascii( *p ) || !isprint( *p )) {
646 "unprintable character (0x%x) converted to ?!\n",
648 putchar( '?' ); /* Replace unprintable char with a question mark. */
650 printf( "\\%o", (unsigned char)*p );
660 } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
662 syslog( LOG_ERR, "read: %s", strerror(errno) );
668 if ( state & ST_AVAIL ) {
673 if ( state & ST_PAGE ) {
679 printf( "%%%%Trailer\nGSV restore\n%%%%Pages: %d\n%%%%EOF\n", npages );
687 * Interface to pipe and exec, for starting children in pipelines.
689 * Manipulates file descriptors 0, 1, and 2, such that the new child
690 * is reading from the parent's output.
692 int pexecv( char *path, char *argv[])
696 if ( pipe( fd ) < 0 ) {
700 switch ( c = fork()) {
706 if ( close( fd[ 1 ] ) < 0 ) {
709 if ( dup2( fd[ 0 ], 0 ) < 0 ) {
712 if ( close( fd[ 0 ] ) < 0 ) {
720 if ( close( fd[ 0 ] ) < 0 ) {
723 if ( dup2( fd[ 1 ], 1 ) < 0 ) {
726 if ( close( fd[ 1 ] ) < 0 ) {