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