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