]> arthur.barton.de Git - netatalk.git/blob - etc/psf/psf.c
autoconf POSIX.1 sys/wait.h check
[netatalk.git] / etc / psf / psf.c
1 /*
2  * $Id: psf.c,v 1.5 2001-09-06 19:04:40 rufustfirefly Exp $
3  *
4  * Copyright (c) 1990,1995 Regents of The University of Michigan.
5  * All Rights Reserved. See COPYRIGHT.
6  *
7  * PostScript Filter, psf.
8  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif /* HAVE_CONFIG_H */
23
24 #define FUCKED
25
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif /* HAVE_UNISTD_H */
29 #include <sys/time.h>
30
31 /* POSIX.1 sys/wait.h check */
32 #include <sys/types.h>
33 #ifdef HAVE_SYS_WAIT_H
34 #include <sys/wait.h>
35 #endif /* HAVE_SYS_WAIT_H */
36 #ifndef WEXITSTATUS
37 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
38 #endif /* ! WEXITSTATUS */
39 #ifndef WIFEXITED
40 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
41 #endif /* ! WIFEXITED */
42
43 #include <sys/file.h>
44 #include <sys/syslog.h>
45 #include <atalk/paths.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <ctype.h>
50 #include <signal.h>
51
52 /* Forward Declarations */
53 int pexecv(char *path, char *argv[]);
54 int copyio();
55 int textps();
56
57 char            psapath[] = _PATH_PSA;
58 char            *psaargv[] = { "psa", 0, 0, 0, 0 };
59
60 /*
61  * If we're not doing accounting, we just call pap as below.
62  * If we are doing accounting, we call pap twice.  The first time,
63  * we call it with "-E" in arg 2, pagecount.ps in arg 3, and "-" in
64  * arg 4.  The second time, we call it with "-c" in arg 2, pagecount.ps
65  * in arg 3, and 0 in arg 4.
66  */
67 char            pappath[] = _PATH_PAP;
68 char            *papargv[] = { "pap", "-sstatus", 0, 0, 0, 0, 0, 0 };
69
70 char            revpath[] = _PATH_PSORDER;
71 char            *revargv[] = { "psorder", "-d", 0 };
72
73 char            *filtargv[] = { 0, 0, 0 };
74
75 char            inbuf[ 1024 * 8 ];
76 int             inlen;
77
78 FILE            *acctfile = NULL;
79 int             literal;
80 int             width = 80, length = 66, indent = 0;
81 char            *prog, *name, *host;
82
83 struct papersize {
84     int         width;
85     int         length;
86    float        win;
87     float       lin;
88 } papersizes[] = {
89     { 80, 66, 8.5, 11.0 },                      /* US Letter */
90     { 80, 70, 8.27, 11.69 },                    /* A4 */
91 };
92
93 int main( ac, av ) 
94     int         ac;
95     char        **av;
96 {
97     int                 c, rc, children = 0;
98 #ifdef FUCKED
99     int                 psafileno, multiconn = 0, waitidle = 0, waitidle2 = 0;
100 #endif /* FUCKED */
101     int                 status;
102     extern char         *optarg;
103     extern int          optind, opterr;
104
105     opterr = 0;
106     if (( prog = rindex( av[ 0 ], '/' )) == NULL ) {
107         prog = av[ 0 ];
108     } else {
109         prog++;
110     }
111 #ifdef ultrix
112     openlog( prog, LOG_PID );
113 #else /* ultrix */
114     openlog( prog, LOG_PID, LOG_LPR );
115 #endif /* ultrix */
116
117     while (( c = getopt( ac, av, "P:C:D:F:L:J:x:y:n:h:w:l:i:c" )) != EOF ) {
118         switch ( c ) {
119         case 'n' :
120             name = optarg;
121             break;
122
123         case 'h' :
124             host = optarg;
125             break;
126
127         case 'w' :
128             width = atoi( optarg );
129 #ifdef ZEROWIDTH
130             /*
131              * Some version of lpd pass 0 for the page width.
132              */
133             if ( width == 0 ) {
134                 width = 80;
135             }
136 #endif /* ZEROWIDTH */
137             break;
138
139         case 'l' :
140             length = atoi( optarg );
141             break;
142
143         case 'i' :
144             indent = atoi( optarg );
145             break;
146
147         case 'c' :      /* Print control chars */
148             literal++;
149             break;
150
151         case 'F' :
152         case 'L' :
153         case 'J' :
154         case 'P' :
155         case 'x' :
156         case 'y' :
157             break;
158         
159 #ifdef notdef
160         default :
161             syslog( LOG_ERR, "bad option: %c", c );
162             exit( 2 );
163 #endif /* notdef */
164         }
165     }
166     if ( ac - optind > 1 ) {
167         syslog( LOG_ERR, "Too many arguments" );
168         exit( 2 );
169     }
170 #ifdef FUCKED
171     if ( index( prog, 'w' )) {
172         waitidle++;
173     }
174     if ( index( prog, 'W' )) {
175         waitidle2++;
176     }
177     if ( index( prog, 'm' )) {
178         multiconn++;
179     }
180 #endif /* FUCKED */
181
182     syslog( LOG_INFO, "starting for %s", name ? name : "?" );
183
184 restart:
185     if (( inlen = read( 0, inbuf, sizeof( inbuf ))) < 0 ) {
186         syslog( LOG_ERR, "read: %m" );
187         exit( 1 );
188     }
189     if ( inlen == 0 ) { /* nothing to be done */
190         syslog( LOG_INFO, "done" );
191         exit( 0 );
192     }
193
194     /*
195      * If we've been given an accounting file, start the accounting
196      * process.
197      */
198     if ( optind < ac ) {
199         /* build arguments */
200         psaargv[ 1 ] = av[ optind ];
201         psaargv[ 2 ] = name;
202         psaargv[ 3 ] = host;
203         if (( c = pexecv( psapath, psaargv )) < 0 ) {
204             syslog( LOG_ERR, "%s: %m", psapath );
205             exit( 2 );
206         }
207         children++;
208         syslog( LOG_INFO, "accounting with psa[%d]", c );
209     }
210
211     /*
212      * Check prog's name to decide what programs to execute.
213      */
214     if ( strstr( prog, "pap" ) != NULL ) {
215         if ( optind < ac ) {    /* accounting */
216 #ifdef FUCKED
217             if ( multiconn ) {
218                 psafileno = getdtablesize();
219                 psafileno--;
220                 dup2( 1, psafileno );
221
222                 if ( waitidle2 ) {
223                     papargv[ 2 ] = "-W";
224                     papargv[ 3 ] = "-c";
225                     papargv[ 4 ] = "-E";
226                     papargv[ 5 ] = _PATH_PAGECOUNT;
227                     papargv[ 6 ] = "-";
228                     papargv[ 7 ] = 0;
229                 } else if ( waitidle ) {
230                     papargv[ 2 ] = "-w";
231                     papargv[ 3 ] = "-c";
232                     papargv[ 4 ] = "-E";
233                     papargv[ 5 ] = _PATH_PAGECOUNT;
234                     papargv[ 6 ] = "-";
235                     papargv[ 7 ] = 0;
236                 } else {
237                     papargv[ 2 ] = "-c";
238                     papargv[ 3 ] = "-E";
239                     papargv[ 4 ] = _PATH_PAGECOUNT;
240                     papargv[ 5 ] = "-";
241                     papargv[ 6 ] = 0;
242                 }
243             } else {
244                 /*
245                  * This is how it should be done.
246                  */
247                 papargv[ 2 ] = "-c";
248                 papargv[ 3 ] = _PATH_PAGECOUNT;
249                 papargv[ 4 ] = "-";
250                 papargv[ 5 ] = _PATH_PAGECOUNT;
251                 papargv[ 6 ] = 0;
252             }
253 #endif /* FUCKED */
254         } else {
255             papargv[ 2 ] = "-c";
256             papargv[ 3 ] = "-E";
257             papargv[ 4 ] = 0;
258         }
259
260         if (( c = pexecv( pappath, papargv )) < 0 ) {
261             syslog( LOG_ERR, "%s: %m", pappath );
262             exit( 2 );
263         }
264         children++;
265         syslog( LOG_INFO, "sending to pap[%d]", c );
266     }
267
268     /*
269      * Might be a good idea to have both a "forw" and a "rev", so that
270      * reversed documents can be reordered for the printing device.
271      */
272     if ( strstr( prog, "rev" ) != NULL ) {
273         if (( c = pexecv( revpath, revargv )) < 0 ) {
274             syslog( LOG_ERR, "%s: %m", revpath );
275             exit( 2 );
276         }
277         syslog( LOG_INFO, "sending to rev[%d]", c );
278         children++;
279     }
280
281     /*
282      * Invoke an external (script) filter to produce PostScript from
283      * non-text input.
284      */
285     if ( *prog != 'i' && *prog != 'o' && *( prog + 1 ) == 'f' ) {
286         filtargv[ 0 ] = filtargv[ 1 ] = prog;
287         if (( c = pexecv( _PATH_PSFILTER, filtargv )) < 0 ) {
288             syslog( LOG_ERR, "%s: %m", _PATH_PSFILTER );
289             exit( 2 );
290         }
291         syslog( LOG_INFO, "external filter[%d]", c );
292         children++;
293         rc = copyio();          /* external filter */
294     } else {
295         if ( inlen >= 2 && inbuf[ 0 ] == '%' && inbuf[ 1 ] == '!' ) {
296             syslog( LOG_INFO, "PostScript" );
297             rc = copyio();      /* PostScript */
298         } else if ( inlen >= 2 && inbuf[ 0 ] == '\033' && inbuf[ 1 ] == '%' ) {
299             syslog( LOG_INFO, "PostScript w/PJL" );
300             rc = copyio();      /* PostScript */
301         } else {
302             syslog( LOG_INFO, "straight text" );
303             rc = textps();      /* straight text */
304         }
305     }
306
307 #ifdef FUCKED
308     if ( strstr( prog, "pap" ) != NULL && optind < ac && multiconn ) {
309         dup2( psafileno, 1 );
310         close( psafileno );
311         papargv[ 2 ] = "-c";
312         if ( waitidle2 ) {
313             papargv[ 3 ] = "-W";
314             papargv[ 4 ] = _PATH_PAGECOUNT;
315             papargv[ 5 ] = 0;
316         } else if ( waitidle ) {
317             papargv[ 3 ] = "-w";
318             papargv[ 4 ] = _PATH_PAGECOUNT;
319             papargv[ 5 ] = 0;
320         } else {
321             papargv[ 3 ] = _PATH_PAGECOUNT;
322             papargv[ 4 ] = 0;
323         }
324
325         if (( c = pexecv( pappath, papargv )) < 0 ) {
326             syslog( LOG_ERR, "%s: %m", pappath );
327             exit( 2 );
328         }
329         children++;
330         syslog( LOG_INFO, "pagecount with pap[%d]", c );
331     }
332 #endif /* FUCKED */
333
334     if ( children ) {
335         close( 1 );
336     }
337     while ( children ) {
338         if (( c = wait3( &status, 0, 0 )) < 0 ) {
339             syslog( LOG_ERR, "wait3: %m" );
340             exit( 1 );
341         }
342         if ( WIFEXITED( status )) {
343 #ifndef WEXITSTATUS
344 #define WEXITSTATUS(x)  ((x).w_status)
345 #endif /* WEXITSTATUS */
346             if ( WEXITSTATUS( status ) != 0 ) {
347                 syslog( LOG_ERR, "%d died with %d", c, WEXITSTATUS( status ));
348                 exit( WEXITSTATUS( status ));
349             } else {
350                 syslog( LOG_INFO, "%d done", c );
351                 children--;
352             }
353         } else {
354             syslog( LOG_ERR, "%d died badly", c );
355             exit( 1 );
356         }
357     }
358
359     if ( rc == 3 ) {
360         syslog( LOG_INFO, "pausing" );
361         kill( getpid(), SIGSTOP );
362         syslog( LOG_INFO, "restarting" );
363         goto restart;
364     }
365
366     syslog( LOG_INFO, "done" );
367     exit( rc );
368 }
369
370 int copyio()
371 {
372     /* implement the FSM needed to do the suspend. Note that
373      * the last characters will be \031\001 so don't worry
374      * Fun things: 1. \031\001 should not be written to output device
375      *  2. The \031 can be last char of one read, \001 first of next
376      *      - we need to write \031 if not followed by \001
377      */
378     struct timeval      tv;
379     fd_set              fdset;
380     int                 ctl = 0;
381
382 notdone:
383     do {
384         /*
385          * First, \031 and \001 *must* be the last things in the buffer
386          * (\001 can be the first thing in the next buffer).  There's no
387          * need to scan any of the intervening bytes.  Second, if there's
388          * more input, the escape sequence was bogus, and we should keep
389          * reading.
390          */
391         if ( inlen == 1 ) {
392             if ( ctl == 1 ) {
393                 if ( inbuf[ 0 ] == '\001' ) {
394                     ctl = 2;
395                     break;
396                 }
397                 if ( write( 1, "\031", 1 ) != 1 ) {
398                     syslog( LOG_ERR, "write: %m" );
399                     return( 1 );
400                 }
401                 ctl = 0;
402             }
403
404             if ( inbuf[ 0 ] == '\031' ) {
405                 ctl = 1;
406             }
407
408         } else {
409             if ( ctl == 1 ) {
410                 if ( write( 1, "\031", 1 ) != 1 ) {
411                     syslog( LOG_ERR, "write: %m" );
412                     return( 1 );
413                 }
414             }
415             ctl = 0;
416             if ( inbuf[ inlen - 2 ] == '\031' &&
417                     inbuf[ inlen - 1 ] == '\001' ) {
418                 ctl = 2;
419             } else if ( inbuf[ inlen - 1 ] == '\031' ) {
420                 ctl = 1;
421             }
422         }
423
424         inlen -= ctl;
425         if (( inlen > 0 ) && ( write( 1, inbuf, inlen ) != inlen )) {
426             syslog( LOG_ERR, "write: %m" );
427             return( 1 );
428         }
429         if ( ctl == 2 ) {
430             break;
431         }
432     } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
433
434     if ( ctl == 2 ) {
435         tv.tv_sec = 2;
436         tv.tv_usec = 0;
437         FD_ZERO( &fdset );
438         FD_SET( 0, &fdset );
439         if ( select( 1, &fdset, NULL, NULL, &tv ) != 0 ) {
440             if (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 ) {
441                 goto notdone;
442             }
443         }
444     }
445
446     if ( inlen < 0 ) {
447         syslog( LOG_ERR, "read: %m" );
448         return( 1 );
449     }
450
451     if ( ctl == 1 ) {
452         if ( write( 1, "\031", 1 ) != 1 ) {
453             syslog( LOG_ERR, "write: %m" );
454             return( 1 );
455         }
456     } else if ( ctl == 2 ) {
457         return( 3 );
458     }
459     return( 0 );
460 }
461
462 char            *font = "Courier";
463 int             point = 11;
464
465 char            pspro[] = "\
466 /GSV save def                                           % global VM\n\
467 /SP {\n\
468         /SV save def                                    % save vmstate\n\
469         dup /H exch def                                 % save font height\n\
470         exch findfont exch scalefont setfont            % select font\n\
471         ( ) stringwidth pop /W exch def                 % save font width\n\
472         0.5 sub 72 mul /CY exch def                     % save start Y\n\
473         pop 0.5 add 72 mul /CX exch def                 % save start X\n\
474         CX CY moveto                                    % make current point\n\
475 } bind def\n\
476 /S /show load def\n\
477 /NL { CX CY H sub dup /CY exch def moveto } bind def\n\
478 /CR { CX CY moveto } bind def\n\
479 /B { W neg 0 rmoveto}bind def\n\
480 /T { W mul 0 rmoveto}bind def\n\
481 /EP { SV restore showpage } bind def\n\
482 %%EndProlog\n";
483
484 int textps()
485 {
486     struct papersize    papersize;
487     int                 state = 0, line = 0, col = 0, npages = 0, rc, i;
488     char                *p, *end;
489
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 ];
495             break;
496         }
497     }
498     if ( i >= elements( papersizes )) {
499         papersize = papersizes[ 0 ];            /* default */
500     }
501
502 #define ST_AVAIL                (1<<0)
503 #define ST_CONTROL              (1<<1)
504 #define ST_PAGE                 (1<<2)
505     /*
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...
509      */
510     do {
511         p = inbuf;
512         end = inbuf + inlen;
513         while ( p < end ) {
514             if (( state & ST_PAGE ) == 0 && *p != '\031' && *p != '\001' ) {
515                 if ( npages == 0 ) {
516                     printf( "%%!PS-Adobe-2.0\n%%%%Pages: (atend)\n" );
517                     printf( "%%%%DocumentFonts: %s\n", font );
518                     fflush( stdout );
519
520                     /* output postscript prologue: */
521                     if ( write( 1, pspro, sizeof( pspro ) - 1 ) !=
522                             sizeof( pspro ) - 1 ) {
523                         syslog( LOG_ERR, "write prologue: %m" );
524                         return( 1 );
525                     }
526                     if ( name && host ) {
527                         printf( "statusdict /jobname (%s@%s) put\n", name,
528                                 host );
529                     }
530                 }
531
532                 printf( "%%%%Page: ? %d\n", ++npages );
533                 printf( "%d %f %f /%s %d SP\n", indent,
534                         papersize.win, papersize.lin, font, point );
535                 state |= ST_PAGE;
536             }
537             if ( state & ST_CONTROL && *p != '\001' ) {
538                 if ( !literal ) {
539                     fprintf( stderr, "unprintable character (0x%x)!\n",
540                             (unsigned char)031 );
541                     return( 2 );        /* Toss job */
542                 }
543                 printf( "\\%o", (unsigned char)031 );
544                 state &= ~ST_CONTROL;
545                 col++;
546             }
547
548             switch ( *p ) {
549             case '\n' :         /* end of line */
550                 if ( state & ST_AVAIL ) {
551                     printf( ")S\n" );
552                     state &= ~ST_AVAIL;
553                 }
554                 printf( "NL\n" );
555                 line++;
556                 col = 0;
557                 if ( line >= length ) {
558                     printf( "EP\n" );
559                     state &= ~ST_PAGE;
560                     line = 0;
561                 }
562                 break;
563
564             case '\r' :         /* carriage return (for overtyping) */
565                 if ( state & ST_AVAIL ) {
566                     printf( ")S CR\n" );
567                     state &= ~ST_AVAIL;
568                 }
569                 col = 0;
570                 break;
571
572             case '\f' :         /* form feed */
573                 if ( state & ST_AVAIL ) {
574                     printf( ")S\n" );
575                     state &= ~ST_AVAIL;
576                 }
577                 printf( "EP\n" );
578                 state &= ~ST_PAGE;
579                 line = 0;
580                 col = 0;
581                 break;
582
583             case '\b' :         /* backspace */
584                 /* show line, back up one character */
585                 if ( state & ST_AVAIL ) {
586                     printf( ")S\n" );
587                     state &= ~ST_AVAIL;
588                 }
589                 printf( "B\n" );
590                 col--;
591                 break;
592
593             case '\t' :         /* tab */
594                 if ( state & ST_AVAIL ) {
595                     printf( ")S\n" );
596                     state &= ~ST_AVAIL;
597                 }
598                 printf( "%d T\n", 8 - ( col % 8 ));
599                 col += 8 - ( col % 8 );
600                 break;
601
602             /*
603              * beginning of lpr control sequence
604              */
605             case '\031' :
606                 state |= ST_CONTROL;
607                 break;
608
609             case '\001' :       /* lpr control sequence */
610                 if ( state & ST_CONTROL ) {
611                     rc = 3;
612                     goto out;
613                 }
614                 /* FALLTHROUGH */
615
616             case '\\' :
617             case ')' :
618             case '(' :
619                 if (( state & ST_AVAIL ) == 0 ) {
620                     printf( "(" );
621                     state |= ST_AVAIL;
622                 }
623                 putchar( '\\' );
624                 /* FALLTHROUGH */
625
626             default :
627                 if (( state & ST_AVAIL ) == 0 ) {
628                     printf( "(" );
629                     state |= ST_AVAIL;
630                 }
631                 if ( !isascii( *p ) || !isprint( *p )) {
632                     if ( !literal ) {
633                         fprintf( stderr, "unprintable character (0x%x)!\n",
634                                 (unsigned char)*p );
635                         return( 2 );    /* Toss job */
636                     }
637                     printf( "\\%o", (unsigned char)*p );
638                 } else {
639                     putchar( *p );
640                 }
641                 col++;
642                 break;
643             }
644         p++;
645         }
646     } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
647     if ( inlen < 0 ) {
648         syslog( LOG_ERR, "read: %m" );
649         return( 1 );
650     }
651     rc = 0;
652
653 out:
654     if ( state & ST_AVAIL ) {
655         printf( ")S\n" );
656         state &= ~ST_AVAIL;
657     }
658
659     if ( state & ST_PAGE ) {
660         printf( "EP\n" );
661         state &= ~ST_PAGE;
662     }
663
664     if ( npages > 0 ) {
665         printf( "%%%%Trailer\nGSV restore\n%%%%Pages: %d\n%%%%EOF\n", npages );
666         fflush( stdout );
667     }
668
669     return( rc );
670 }
671
672 /*
673  * Interface to pipe and exec, for starting children in pipelines.
674  *
675  * Manipulates file descriptors 0, 1, and 2, such that the new child
676  * is reading from the parent's output.
677  */
678 int pexecv( path, argv )
679     char        *path, *argv[];
680 {
681     int         fd[ 2 ], c;
682
683     if ( pipe( fd ) < 0 ) {
684         return( -1 );
685     }
686
687     switch ( c = fork()) {
688     case -1 :
689         return( -1 );
690         /* NOTREACHED */
691
692     case 0 :
693         if ( close( fd[ 1 ] ) < 0 ) {
694             return( -1 );
695         }
696         if ( dup2( fd[ 0 ], 0 ) < 0 ) {
697             return( -1 );
698         }
699         if ( close( fd[ 0 ] ) < 0 ) {
700             return( -1 );
701         }
702         execv( path, argv );
703         return( -1 );
704         /* NOTREACHED */
705
706     default :
707         if ( close( fd[ 0 ] ) < 0 ) {
708             return( -1 );
709         }
710         if ( dup2( fd[ 1 ], 1 ) < 0 ) {
711             return( -1 );
712         }
713         if ( close( fd[ 1 ] ) < 0 ) {
714             return( -1 );
715         }
716         return( c );
717     }
718 }