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