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