]> arthur.barton.de Git - netatalk.git/blob - etc/papd/lp.c
Fix papd so it will spool to a BSD lpr queue and change the owner of the file to...
[netatalk.git] / etc / papd / lp.c
1 /*
2  * Copyright (c) 1990,1994 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  *
5  * Portions:
6  * Copyright (c) 1983 Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by the University of
20  *      California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37
38 /*
39  * Interface to lpr system.
40  */
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #include <sys/param.h>
47 #include <sys/syslog.h>
48 #include <sys/time.h>
49 #include <sys/socket.h>
50 #include <sys/stat.h>
51 #if defined( sun ) && defined( __svr4__ )
52 #include </usr/ucbinclude/sys/file.h>
53 #else sun __svr4__
54 #include <sys/file.h>
55 #endif sun __svr4__
56 #include <sys/un.h>
57 #include <netinet/in.h>
58 #undef s_net
59 #include <netatalk/at.h>
60 #include <atalk/atp.h>
61 #include <atalk/paths.h>
62
63 #ifdef ABS_PRINT
64 #include <math.h>
65 #endif ABS_PRINT
66
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <netdb.h>
71 #include <fcntl.h>
72 #include <pwd.h>
73
74 #include "printer.h"
75 #include "file.h"
76
77 char    hostname[ MAXHOSTNAMELEN ];
78
79 extern struct sockaddr_at *sat;
80
81 /* initialize printing interface */
82 int     lp_init();
83 /* cancel current job */
84 int     lp_cancel();
85 /* print current job */
86 int     lp_print();
87
88 /* open a file for spooling */
89 int     lp_open();
90 /* open a buffer to the current open file */
91 int     lp_write();
92 /* close current spooling file */
93 int     lp_close();
94
95 struct lp {
96     int                 lp_flags;
97     FILE                *lp_stream;
98     int                 lp_seq;
99     char                lp_letter;
100     char                *lp_person;
101     char                *lp_host;
102     char                *lp_job;
103 } lp;
104 #define LP_INIT         (1<<0)
105 #define LP_OPEN         (1<<1)
106 #define LP_PIPE         (1<<2)
107 #define LP_CONNECT      (1<<3)
108 #define LP_QUEUE        (1<<4)
109
110 lp_person( person )
111     char        *person;
112 {
113     if ( lp.lp_person != NULL ) {
114         free( lp.lp_person );
115     }
116     if (( lp.lp_person = (char *)malloc( strlen( person ) + 1 )) == NULL ) {
117         syslog( LOG_ERR, "malloc: %m" );
118         exit( 1 );
119     }
120     strcpy( lp.lp_person, person );
121 }
122
123 #ifdef ABS_PRINT
124 lp_pagecost()
125 {
126     char        cost[ 22 ];
127     char        balance[ 22 ];
128     int         err;
129
130     if ( lp.lp_person == NULL ) {
131         return( -1 );
132     }
133     err = ABS_canprint( lp.lp_person, printer->p_role, printer->p_srvid,
134             cost, balance );
135     printer->p_pagecost = floor( atof( cost ) * 10000.0 );
136     printer->p_balance = atof( balance ) + atof( cost );
137     return( err < 0 ? -1 : 0 );
138 }
139 #endif ABS_PRINT
140
141 lp_host( host )
142     char        *host;
143 {
144     if ( lp.lp_host != NULL ) {
145         free( lp.lp_host );
146     }
147     if (( lp.lp_host = (char *)malloc( strlen( host ) + 1 )) == NULL ) {
148         syslog( LOG_ERR, "malloc: %m" );
149         exit( 1 );
150     }
151     strcpy( lp.lp_host, host );
152 }
153
154 lp_job( job )
155     char        *job;
156 {
157     char        *p, *q;
158
159     if ( lp.lp_job != NULL ) {
160         free( lp.lp_job );
161     }
162     if (( lp.lp_job = (char *)malloc( strlen( job ) + 1 )) == NULL ) {
163         syslog( LOG_ERR, "malloc: %m" );
164         exit( 1 );
165     }
166     for ( p = job, q = lp.lp_job; *p != '\0'; p++, q++ ) {
167         if ( !isascii( *p ) || !isprint( *p ) || *p == '\\' ) {
168             *q = '.';
169         } else {
170             *q = *p;
171         }
172     }
173     *q = '\0';
174 }
175
176 lp_init( out, sat )
177     struct papfile      *out;
178     struct sockaddr_at  *sat;
179 {
180     int         fd, n, len;
181     char        *cp, buf[ BUFSIZ ];
182     struct stat st;
183     int         authenticated = 0;
184 #ifdef ABS_PRINT
185     char        cost[ 22 ];
186     char        balance[ 22 ];
187 #endif ABS_PRINT
188 #if defined( CAPDIR ) || defined( USE_CAP )
189     char        username[32];
190     int         addr_net, addr_node;
191     FILE        *cap_file;
192     struct stat cap_st;
193     char        addr_filename[256];
194 #endif /* CAPDIR */
195
196     if ( printer->p_flags & P_AUTH ) {
197         authenticated = 0;
198 #ifdef CAPDIR
199         if ( printer->p_flags & P_AUTH_CAP ) {
200             addr_net = ntohs( sat->sat_addr.s_net );
201             addr_node  = sat->sat_addr.s_node;
202             sprintf(addr_filename, "%s/net%d.%dnode%d", CAPDIR, addr_net/256, addr_net%256, addr_node);
203             if (stat(addr_filename, &cap_st) == 0) {
204                 if ((cap_file = fopen(addr_filename, "r")) != NULL) {
205                     if (fscanf(cap_file, "%s", username) != EOF) {
206                         if (getpwnam(username) != NULL ) {
207                             syslog(LOG_INFO, "CAP authenticated %s", username);
208                             lp_person(username);
209                             authenticated = 1;
210                         } else {
211                             syslog(LOG_INFO, "CAP error: invalid username: '%s'", username);
212                         }
213                     } else {
214                         syslog(LOG_INFO, "CAP error: could not read username");
215                     }
216                 } else {
217                     syslog(LOG_INFO, "CAP error: %m");
218                 }
219             } else {
220                 syslog(LOG_INFO, "CAP error: %m");
221             }
222         }
223 #endif /* CAPDIR */
224
225         if ( printer->p_flags & P_AUTH_PSSP ) {
226             if ( lp.lp_person != NULL ) {
227                 authenticated = 1;
228             }
229         }
230
231         if ( authenticated == 0 ) {
232             syslog( LOG_ERR, "lp_init: must authenticate" );
233             spoolerror( out, "Authentication required." );
234             return( -1 );
235         }
236
237 #ifdef ABS_PRINT
238         if (( printer->p_flags & P_ACCOUNT ) && printer->p_pagecost > 0 &&
239                 ! ABS_canprint( lp.lp_person, printer->p_role,
240                 printer->p_srvid, cost, balance )) {
241             syslog( LOG_ERR, "lp_init: no ABS funds" );
242             spoolerror( out, "No ABS funds available." );
243             return( -1 );
244         }
245 #endif ABS_PRINT
246     }
247
248     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
249         syslog( LOG_ERR, "gethostname: %m" );
250         exit( 1 );
251     }
252
253     if ( lp.lp_flags & LP_INIT ) {
254         syslog( LOG_ERR, "lp_init: already inited, die!" );
255         abort();
256     }
257
258     lp.lp_flags = 0;
259     lp.lp_stream = NULL;
260     lp.lp_letter = 'A';
261
262     if ( printer->p_flags & P_SPOOLED ) {
263         /* check if queuing is enabled: mode & 010 on lock file */
264         if ( stat( printer->p_lock, &st ) < 0 ) {
265             syslog( LOG_ERR, "lp_init: %s: %m", printer->p_lock );
266             spoolerror( out, NULL );
267             return( -1 );
268         }
269         if ( st.st_mode & 010 ) {
270             syslog( LOG_INFO, "lp_init: queuing is disabled" );
271             spoolerror( out, "Queuing is disabled." );
272             return( -1 );
273         }
274
275         if (( fd = open( ".seq", O_RDWR|O_CREAT, 0661 )) < 0 ) {
276             syslog( LOG_ERR, "lp_init: can't create .seq" );
277             spoolerror( out, NULL );
278             return( -1 );
279         }
280
281         if ( flock( fd, LOCK_EX ) < 0 ) {
282             syslog( LOG_ERR, "lp_init: can't lock .seq" );
283             spoolerror( out, NULL );
284             return( -1 );
285         }
286
287         n = 0;
288         if (( len = read( fd, buf, sizeof( buf ))) < 0 ) {
289             syslog( LOG_ERR, "lp_init read: %m" );
290             spoolerror( out, NULL );
291             return( -1 );
292         }
293         if ( len > 0 ) {
294             for ( cp = buf; len; len--, cp++ ) {
295                 if ( *cp < '0' || *cp > '9' ) {
296                     break;
297                 }
298                 n = n * 10 + ( *cp - '0' );
299             }
300         }
301         lp.lp_seq = n;
302
303         n = ( n + 1 ) % 1000;
304         sprintf( buf, "%03d\n", n );
305         lseek( fd, 0L, 0 );
306         write( fd, buf, strlen( buf ));
307         close( fd );
308     } else {
309         lp.lp_flags |= LP_PIPE;
310         lp.lp_seq = getpid();
311     }
312
313     lp.lp_flags |= LP_INIT;
314     return( 0 );
315 }
316
317 lp_open( out, sat )
318     struct papfile      *out;
319     struct sockaddr_at  *sat;
320 {
321     char        name[ MAXPATHLEN ];
322     int         fd;
323     struct passwd       *pwent;
324
325     if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out, sat ) != 0 ) {
326         return( -1 );
327     }
328     if ( lp.lp_flags & LP_OPEN ) {
329         syslog( LOG_ERR, "lp_open already open" );
330         abort();
331     }
332
333     if ( lp.lp_flags & LP_PIPE ) {
334         /* go right to program */
335         if (lp.lp_person != NULL) {
336             if((pwent = getpwnam(lp.lp_person)) != NULL) {
337                 if(setreuid(pwent->pw_uid, pwent->pw_uid) != 0) {
338                     syslog(LOG_INFO, "setreuid error: %m");
339                 }
340             } else {
341                 syslog(LOG_INFO, "Error getting username (%s)", lp.lp_person);
342             }
343         }
344         if (( lp.lp_stream = popen( printer->p_printer, "w" )) == NULL ) {
345             syslog( LOG_ERR, "lp_open popen %s: %m", printer->p_printer );
346             spoolerror( out, NULL );
347             return( -1 );
348         }
349     } else {
350         sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname );
351
352         if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) {
353             syslog( LOG_ERR, "lp_open %s: %m", name );
354             spoolerror( out, NULL );
355             return( -1 );
356         }
357
358         if (lp.lp_person != NULL) {
359             if ((pwent = getpwnam(lp.lp_person)) == NULL) {
360                 syslog(LOG_ERR, "getpwnam %s: no such user", lp.lp_person);
361                 spoolerror( out, NULL );
362                 return( -1 );
363             }
364         } else {
365             if ((pwent = getpwnam(printer->p_operator)) == NULL) {
366                 syslog(LOG_ERR, "getpwnam %s: no such user", printer->p_operator);
367                 spoolerror( out, NULL );
368                 return( -1 );
369             }
370         }
371
372         if (fchown(fd, pwent->pw_uid, -1) < 0) {
373             syslog(LOG_ERR, "chown %s %s: %m", pwent->pw_name, name);
374             spoolerror( out, NULL );
375             return( -1 );
376         }
377
378         if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
379             syslog( LOG_ERR, "lp_open fdopen: %m" );
380             spoolerror( out, NULL );
381             return( -1 );
382         }
383     }
384     lp.lp_flags |= LP_OPEN;
385
386     return( 0 );
387 }
388
389 lp_close()
390 {
391     if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
392         return;
393     }
394     fclose( lp.lp_stream );
395     lp.lp_stream = NULL;
396     lp.lp_flags &= ~LP_OPEN;
397     return;
398 }
399
400 lp_write( buf, len )
401     char        *buf;
402     int         len;
403 {
404     if (( lp.lp_flags & LP_OPEN ) == 0 ) {
405         return( -1 );
406     }
407
408     if ( fwrite( buf, 1, len, lp.lp_stream ) != len ) {
409         syslog( LOG_ERR, "lp_write: %m" );
410         abort();
411     }
412     return( 0 );
413 }
414
415 lp_cancel()
416 {
417     char        name[ MAXPATHLEN ];
418     char        letter;
419
420     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
421         return;
422     }
423
424     if ( lp.lp_flags & LP_OPEN ) {
425         lp_close();
426     }
427
428     for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
429         sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
430         if ( unlink( name ) < 0 ) {
431             syslog( LOG_ERR, "lp_cancel unlink %s: %m", name );
432         }
433     }
434
435     return;
436 }
437
438 /*
439  * Create printcap control file, signal printer.  Errors here should
440  * remove queue files.
441  *
442  * XXX piped?
443  */
444 lp_print()
445 {
446     char                buf[ MAXPATHLEN ];
447     char                tfname[ MAXPATHLEN ];
448     char                cfname[ MAXPATHLEN ];
449     char                letter;
450     int                 fd, n, s;
451     FILE                *cfile;
452
453     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
454         return;
455     }
456     lp_close();
457
458     if ( printer->p_flags & P_SPOOLED ) {
459         sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
460         if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
461             syslog( LOG_ERR, "lp_print %s: %m", tfname );
462             return;
463         }
464         if (( cfile = fdopen( fd, "w" )) == NULL ) {
465             syslog( LOG_ERR, "lp_print %s: %m", tfname );
466             return;
467         }
468         fprintf( cfile, "H%s\n", hostname );    /* XXX lp_host? */
469
470         if ( lp.lp_person ) {
471             fprintf( cfile, "P%s\n", lp.lp_person );
472         } else {
473             fprintf( cfile, "P%s\n", printer->p_operator );
474         }
475
476         if ( lp.lp_job && *lp.lp_job ) {
477             fprintf( cfile, "J%s\n", lp.lp_job );
478             fprintf( cfile, "T%s\n", lp.lp_job );
479         } else {
480             fprintf( cfile, "JMac Job\n" );
481             fprintf( cfile, "TMac Job\n" );
482         }
483
484         fprintf( cfile, "C%s\n", hostname );    /* XXX lp_host? */
485
486         if ( lp.lp_person ) {
487             fprintf( cfile, "L%s\n", lp.lp_person );
488         } else {
489             fprintf( cfile, "L%s\n", printer->p_operator );
490         }
491
492         for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
493             fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
494             fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
495         }
496
497         if ( lp.lp_job && *lp.lp_job ) {
498             fprintf( cfile, "N%s\n", lp.lp_job );
499         } else {
500             fprintf( cfile, "NMac Job\n" );
501         }
502         fclose( cfile );
503
504         sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
505         if ( link( tfname, cfname ) < 0 ) {
506             syslog( LOG_ERR, "lp_print can't link %s to %s: %m", cfname,
507                     tfname );
508             return;
509         }
510         unlink( tfname );
511
512         if (( s = lp_conn_unix()) < 0 ) {
513             syslog( LOG_ERR, "lp_print: lp_conn_unix: %m" );
514             return;
515         }
516
517         sprintf( buf, "\1%s\n", printer->p_printer );
518         n = strlen( buf );
519         if ( write( s, buf, n ) != n ) {
520             syslog( LOG_ERR, "lp_print write: %m" );
521             return;
522         }
523         if ( read( s, buf, 1 ) != 1 ) {
524             syslog( LOG_ERR, "lp_print read: %m" );
525             return;
526         }
527
528         lp_disconn_unix( s );
529
530         if ( buf[ 0 ] != '\0' ) {
531             syslog( LOG_ERR, "lp_print lpd said %c: %m", buf[ 0 ] );
532             return;
533         }
534     }
535     syslog( LOG_INFO, "lp_print queued" );
536     return;
537 }
538
539 lp_disconn_unix( fd )
540 {
541     return( close( fd ));
542 }
543
544 lp_conn_unix()
545 {
546     int                 s;
547     struct sockaddr_un  saun;
548
549     if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
550         syslog( LOG_ERR, "lp_conn_unix socket: %m" );
551         return( -1 );
552     }
553     bzero( &saun, sizeof( struct sockaddr_un ));
554     saun.sun_family = AF_UNIX;
555     strcpy( saun.sun_path, _PATH_DEVPRINTER );
556     if ( connect( s, (struct sockaddr *)&saun,
557             strlen( saun.sun_path ) + 2 ) < 0 ) {
558         syslog( LOG_ERR, "lp_conn_unix connect %s: %m", saun.sun_path );
559         close( s );
560         return( -1 );
561     }
562
563     return( s );
564 }
565
566 lp_disconn_inet( fd )
567 {
568     return( close( fd ));
569 }
570
571 lp_conn_inet()
572 {
573     int                 privfd, port = IPPORT_RESERVED - 1;
574     struct sockaddr_in  sin;
575     struct servent      *sp;
576     struct hostent      *hp;
577
578     if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
579         syslog( LOG_ERR, "printer/tcp: unknown service\n" );
580         return( -1 );
581     }
582
583     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
584         syslog( LOG_ERR, "gethostname: %m" );
585         exit( 1 );
586     }
587
588     if (( hp = gethostbyname( hostname )) == NULL ) {
589         syslog( LOG_ERR, "%s: unknown host\n", hostname );
590         return( -1 );
591     }
592
593     if (( privfd = rresvport( &port )) < 0 ) {
594         syslog( LOG_ERR, "lp_connect: socket: %m" );
595         close( privfd );
596         return( -1 );
597     }
598
599     bzero( &sin, sizeof( struct sockaddr_in ));
600     sin.sin_family = AF_INET;
601 /*    sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
602     bcopy( hp->h_addr, &sin.sin_addr, hp->h_length );
603     sin.sin_port = sp->s_port;
604
605     if ( connect( privfd, (struct sockaddr *)&sin,
606             sizeof( struct sockaddr_in )) < 0 ) {
607         syslog( LOG_ERR, "lp_connect: %m" );
608         close( privfd );
609         return( -1 );
610     }
611
612     return( privfd );
613 }
614
615 lp_rmjob( job )
616     int         job;
617 {
618     char        buf[ 1024 ];
619     int         n, s;
620
621     if (( s = lp_conn_inet()) < 0 ) {
622         syslog( LOG_ERR, "lp_rmjob: %m" );
623         return( -1 );
624     }
625
626     if ( lp.lp_person == NULL ) {
627         return( -1 );
628     }
629
630     sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
631     n = strlen( buf );
632     if ( write( s, buf, n ) != n ) {
633         syslog( LOG_ERR, "lp_rmjob write: %m" );
634         lp_disconn_inet( s );
635         return( -1 );
636     }
637     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
638         syslog( LOG_DEBUG, "read %.*s", n, buf );
639     }
640
641     lp_disconn_inet( s );
642     return( 0 );
643 }
644
645 char    *kw_rank = "Rank";
646 char    *kw_active = "active";
647
648 char    *tag_rank = "rank: ";
649 char    *tag_owner = "owner: ";
650 char    *tag_job = "job: ";
651 char    *tag_files = "files: ";
652 char    *tag_size = "size: ";
653 char    *tag_status = "status: ";
654
655 lp_queue( out )
656     struct papfile      *out;
657 {
658     char                        buf[ 1024 ], *start, *stop, *p, *q;
659     int                         linelength, crlflength;
660     static struct papfile       pf;
661     int                         n, len, s;
662         
663     if (( s = lp_conn_unix()) < 0 ) {
664         syslog( LOG_ERR, "lp_queue: %m" );
665         return( -1 );
666     }
667
668     sprintf( buf, "\3%s\n", printer->p_printer );
669     n = strlen( buf );
670     if ( write( s, buf, n ) != n ) {
671         syslog( LOG_ERR, "lp_queue write: %m" );
672         lp_disconn_unix( s );
673         return( -1 );
674     }
675     pf.pf_state = PF_BOT;
676
677     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
678         append( &pf, buf, n );
679     }
680
681     for (;;) {
682         if ( markline( &pf, &start, &linelength, &crlflength ) > 0 ) {
683             /* parse */
684             stop = start + linelength;
685             for ( p = start; p < stop; p++ ) {
686                 if ( *p == ' ' || *p == '\t' ) {
687                     break;
688                 }
689             }
690             if ( p >= stop ) {
691                 CONSUME( &pf , linelength + crlflength);
692                 continue;
693             }
694
695             /*
696              * Keys: "Rank", a number, "active"
697              * Anything else is status.
698              */
699             len = p - start;
700             if ( len == strlen( kw_rank ) &&
701                     strncmp( kw_rank, start, len ) == 0 ) {
702                 CONSUME( &pf, linelength + crlflength );
703                 continue;
704             }
705             if (( len == strlen( kw_active ) &&
706                     strncmp( kw_active, start, len ) == 0 ) ||
707                     isdigit( *start )) {                /* a job line */
708                 append( out, tag_rank, strlen( tag_rank ));
709                 append( out, start, p - start );
710                 append( out, "\n", 1 );
711
712                 for ( ; p < stop; p++ ) {
713                     if ( *p != ' ' && *p != '\t' ) {
714                         break;
715                     }
716                 }
717                 for ( q = p; p < stop; p++ ) {
718                     if ( *p == ' ' || *p == '\t' ) {
719                         break;
720                     }
721                 }
722                 if ( p >= stop ) {
723                     append( out, ".\n", 2 );
724                     CONSUME( &pf, linelength + crlflength );
725                     continue;
726                 }
727                 append( out, tag_owner, strlen( tag_owner ));
728                 append( out, q, p - q );
729                 append( out, "\n", 1 );
730
731                 for ( ; p < stop; p++ ) {
732                     if ( *p != ' ' && *p != '\t' ) {
733                         break;
734                     }
735                 }
736                 for ( q = p; p < stop; p++ ) {
737                     if ( *p == ' ' || *p == '\t' ) {
738                         break;
739                     }
740                 }
741                 if ( p >= stop ) {
742                     append( out, ".\n", 2 );
743                     CONSUME( &pf , linelength + crlflength );
744                     continue;
745                 }
746                 append( out, tag_job, strlen( tag_job ));
747                 append( out, q, p - q );
748                 append( out, "\n", 1 );
749
750                 for ( ; p < stop; p++ ) {
751                     if ( *p != ' ' && *p != '\t' ) {
752                         break;
753                     }
754                 }
755                 for ( q = p, p = stop; p > q; p-- ) {
756                     if ( *p == ' ' || *p == '\t' ) {
757                         break;
758                     }
759                 }
760                 for ( ; p > q; p-- ) {
761                     if ( *p != ' ' && *p != '\t' ) {
762                         break;
763                     }
764                 }
765                 for ( ; p > q; p-- ) {
766                     if ( *p == ' ' || *p == '\t' ) {
767                         break;
768                     }
769                 }
770                 if ( p <= q ) {
771                     append( out, ".\n", 2 );
772                     CONSUME( &pf, linelength + crlflength );
773                     continue;
774                 }
775                 append( out, tag_files, strlen( tag_files ));
776                 append( out, q, p - q );
777                 append( out, "\n", 1 );
778
779                 for ( ; p < stop; p++ ) {
780                     if ( *p != ' ' && *p != '\t' ) {
781                         break;
782                     }
783                 }
784                 append( out, tag_size, strlen( tag_size ));
785                 append( out, p, stop - p );
786                 append( out, "\n.\n", 3 );
787
788                 CONSUME( &pf, linelength + crlflength );
789                 continue;
790             }
791
792             /* status */
793             append( out, tag_status, strlen( tag_status ));
794             append( out, start, linelength );
795             append( out, "\n.\n", 3 );
796
797             CONSUME( &pf, linelength + crlflength );
798         } else {
799             append( out, "*\n", 2 );
800             lp_disconn_unix( s );
801             return( 0 );
802         }
803     }
804 }