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