]> arthur.barton.de Git - netatalk.git/blob - etc/papd/lp.c
implemented config.h
[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         if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
358             syslog( LOG_ERR, "lp_open fdopen: %m" );
359             spoolerror( out, NULL );
360             return( -1 );
361         }
362     }
363     lp.lp_flags |= LP_OPEN;
364
365     return( 0 );
366 }
367
368 lp_close()
369 {
370     if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
371         return;
372     }
373     fclose( lp.lp_stream );
374     lp.lp_stream = NULL;
375     lp.lp_flags &= ~LP_OPEN;
376     return;
377 }
378
379 lp_write( buf, len )
380     char        *buf;
381     int         len;
382 {
383     if (( lp.lp_flags & LP_OPEN ) == 0 ) {
384         return( -1 );
385     }
386
387     if ( fwrite( buf, 1, len, lp.lp_stream ) != len ) {
388         syslog( LOG_ERR, "lp_write: %m" );
389         abort();
390     }
391     return( 0 );
392 }
393
394 lp_cancel()
395 {
396     char        name[ MAXPATHLEN ];
397     char        letter;
398
399     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
400         return;
401     }
402
403     if ( lp.lp_flags & LP_OPEN ) {
404         lp_close();
405     }
406
407     for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
408         sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
409         if ( unlink( name ) < 0 ) {
410             syslog( LOG_ERR, "lp_cancel unlink %s: %m", name );
411         }
412     }
413
414     return;
415 }
416
417 /*
418  * Create printcap control file, signal printer.  Errors here should
419  * remove queue files.
420  *
421  * XXX piped?
422  */
423 lp_print()
424 {
425     char                buf[ MAXPATHLEN ];
426     char                tfname[ MAXPATHLEN ];
427     char                cfname[ MAXPATHLEN ];
428     char                letter;
429     int                 fd, n, s;
430     FILE                *cfile;
431
432     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
433         return;
434     }
435     lp_close();
436
437     if ( printer->p_flags & P_SPOOLED ) {
438         sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
439         if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
440             syslog( LOG_ERR, "lp_print %s: %m", tfname );
441             return;
442         }
443         if (( cfile = fdopen( fd, "w" )) == NULL ) {
444             syslog( LOG_ERR, "lp_print %s: %m", tfname );
445             return;
446         }
447         fprintf( cfile, "H%s\n", hostname );    /* XXX lp_host? */
448
449         if ( lp.lp_person ) {
450             fprintf( cfile, "P%s\n", lp.lp_person );
451         } else {
452             fprintf( cfile, "P%s\n", printer->p_operator );
453         }
454
455         if ( lp.lp_job && *lp.lp_job ) {
456             fprintf( cfile, "J%s\n", lp.lp_job );
457             fprintf( cfile, "T%s\n", lp.lp_job );
458         } else {
459             fprintf( cfile, "JMac Job\n" );
460             fprintf( cfile, "TMac Job\n" );
461         }
462
463         fprintf( cfile, "C%s\n", hostname );    /* XXX lp_host? */
464
465         if ( lp.lp_person ) {
466             fprintf( cfile, "L%s\n", lp.lp_person );
467         } else {
468             fprintf( cfile, "L%s\n", printer->p_operator );
469         }
470
471         for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
472             fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
473             fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
474         }
475
476         if ( lp.lp_job && *lp.lp_job ) {
477             fprintf( cfile, "N%s\n", lp.lp_job );
478         } else {
479             fprintf( cfile, "NMac Job\n" );
480         }
481         fclose( cfile );
482
483         sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
484         if ( link( tfname, cfname ) < 0 ) {
485             syslog( LOG_ERR, "lp_print can't link %s to %s: %m", cfname,
486                     tfname );
487             return;
488         }
489         unlink( tfname );
490
491         if (( s = lp_conn_unix()) < 0 ) {
492             syslog( LOG_ERR, "lp_print: lp_conn_unix: %m" );
493             return;
494         }
495
496         sprintf( buf, "\1%s\n", printer->p_printer );
497         n = strlen( buf );
498         if ( write( s, buf, n ) != n ) {
499             syslog( LOG_ERR, "lp_print write: %m" );
500             return;
501         }
502         if ( read( s, buf, 1 ) != 1 ) {
503             syslog( LOG_ERR, "lp_print read: %m" );
504             return;
505         }
506
507         lp_disconn_unix( s );
508
509         if ( buf[ 0 ] != '\0' ) {
510             syslog( LOG_ERR, "lp_print lpd said %c: %m", buf[ 0 ] );
511             return;
512         }
513     }
514     syslog( LOG_INFO, "lp_print queued" );
515     return;
516 }
517
518 lp_disconn_unix( fd )
519 {
520     return( close( fd ));
521 }
522
523 lp_conn_unix()
524 {
525     int                 s;
526     struct sockaddr_un  saun;
527
528     if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
529         syslog( LOG_ERR, "lp_conn_unix socket: %m" );
530         return( -1 );
531     }
532     bzero( &saun, sizeof( struct sockaddr_un ));
533     saun.sun_family = AF_UNIX;
534     strcpy( saun.sun_path, _PATH_DEVPRINTER );
535     if ( connect( s, (struct sockaddr *)&saun,
536             strlen( saun.sun_path ) + 2 ) < 0 ) {
537         syslog( LOG_ERR, "lp_conn_unix connect %s: %m", saun.sun_path );
538         close( s );
539         return( -1 );
540     }
541
542     return( s );
543 }
544
545 lp_disconn_inet( fd )
546 {
547     return( close( fd ));
548 }
549
550 lp_conn_inet()
551 {
552     int                 privfd, port = IPPORT_RESERVED - 1;
553     struct sockaddr_in  sin;
554     struct servent      *sp;
555     struct hostent      *hp;
556
557     if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
558         syslog( LOG_ERR, "printer/tcp: unknown service\n" );
559         return( -1 );
560     }
561
562     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
563         syslog( LOG_ERR, "gethostname: %m" );
564         exit( 1 );
565     }
566
567     if (( hp = gethostbyname( hostname )) == NULL ) {
568         syslog( LOG_ERR, "%s: unknown host\n", hostname );
569         return( -1 );
570     }
571
572     if (( privfd = rresvport( &port )) < 0 ) {
573         syslog( LOG_ERR, "lp_connect: socket: %m" );
574         close( privfd );
575         return( -1 );
576     }
577
578     bzero( &sin, sizeof( struct sockaddr_in ));
579     sin.sin_family = AF_INET;
580 /*    sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
581     bcopy( hp->h_addr, &sin.sin_addr, hp->h_length );
582     sin.sin_port = sp->s_port;
583
584     if ( connect( privfd, (struct sockaddr *)&sin,
585             sizeof( struct sockaddr_in )) < 0 ) {
586         syslog( LOG_ERR, "lp_connect: %m" );
587         close( privfd );
588         return( -1 );
589     }
590
591     return( privfd );
592 }
593
594 lp_rmjob( job )
595     int         job;
596 {
597     char        buf[ 1024 ];
598     int         n, s;
599
600     if (( s = lp_conn_inet()) < 0 ) {
601         syslog( LOG_ERR, "lp_rmjob: %m" );
602         return( -1 );
603     }
604
605     if ( lp.lp_person == NULL ) {
606         return( -1 );
607     }
608
609     sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
610     n = strlen( buf );
611     if ( write( s, buf, n ) != n ) {
612         syslog( LOG_ERR, "lp_rmjob write: %m" );
613         lp_disconn_inet( s );
614         return( -1 );
615     }
616     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
617         syslog( LOG_DEBUG, "read %.*s", n, buf );
618     }
619
620     lp_disconn_inet( s );
621     return( 0 );
622 }
623
624 char    *kw_rank = "Rank";
625 char    *kw_active = "active";
626
627 char    *tag_rank = "rank: ";
628 char    *tag_owner = "owner: ";
629 char    *tag_job = "job: ";
630 char    *tag_files = "files: ";
631 char    *tag_size = "size: ";
632 char    *tag_status = "status: ";
633
634 lp_queue( out )
635     struct papfile      *out;
636 {
637     char                        buf[ 1024 ], *start, *stop, *p, *q;
638     int                         linelength, crlflength;
639     static struct papfile       pf;
640     int                         n, len, s;
641         
642     if (( s = lp_conn_unix()) < 0 ) {
643         syslog( LOG_ERR, "lp_queue: %m" );
644         return( -1 );
645     }
646
647     sprintf( buf, "\3%s\n", printer->p_printer );
648     n = strlen( buf );
649     if ( write( s, buf, n ) != n ) {
650         syslog( LOG_ERR, "lp_queue write: %m" );
651         lp_disconn_unix( s );
652         return( -1 );
653     }
654     pf.pf_state = PF_BOT;
655
656     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
657         append( &pf, buf, n );
658     }
659
660     for (;;) {
661         if ( markline( &pf, &start, &linelength, &crlflength ) > 0 ) {
662             /* parse */
663             stop = start + linelength;
664             for ( p = start; p < stop; p++ ) {
665                 if ( *p == ' ' || *p == '\t' ) {
666                     break;
667                 }
668             }
669             if ( p >= stop ) {
670                 CONSUME( &pf , linelength + crlflength);
671                 continue;
672             }
673
674             /*
675              * Keys: "Rank", a number, "active"
676              * Anything else is status.
677              */
678             len = p - start;
679             if ( len == strlen( kw_rank ) &&
680                     strncmp( kw_rank, start, len ) == 0 ) {
681                 CONSUME( &pf, linelength + crlflength );
682                 continue;
683             }
684             if (( len == strlen( kw_active ) &&
685                     strncmp( kw_active, start, len ) == 0 ) ||
686                     isdigit( *start )) {                /* a job line */
687                 append( out, tag_rank, strlen( tag_rank ));
688                 append( out, start, p - start );
689                 append( out, "\n", 1 );
690
691                 for ( ; p < stop; p++ ) {
692                     if ( *p != ' ' && *p != '\t' ) {
693                         break;
694                     }
695                 }
696                 for ( q = p; p < stop; p++ ) {
697                     if ( *p == ' ' || *p == '\t' ) {
698                         break;
699                     }
700                 }
701                 if ( p >= stop ) {
702                     append( out, ".\n", 2 );
703                     CONSUME( &pf, linelength + crlflength );
704                     continue;
705                 }
706                 append( out, tag_owner, strlen( tag_owner ));
707                 append( out, q, p - q );
708                 append( out, "\n", 1 );
709
710                 for ( ; p < stop; p++ ) {
711                     if ( *p != ' ' && *p != '\t' ) {
712                         break;
713                     }
714                 }
715                 for ( q = p; p < stop; p++ ) {
716                     if ( *p == ' ' || *p == '\t' ) {
717                         break;
718                     }
719                 }
720                 if ( p >= stop ) {
721                     append( out, ".\n", 2 );
722                     CONSUME( &pf , linelength + crlflength );
723                     continue;
724                 }
725                 append( out, tag_job, strlen( tag_job ));
726                 append( out, q, p - q );
727                 append( out, "\n", 1 );
728
729                 for ( ; p < stop; p++ ) {
730                     if ( *p != ' ' && *p != '\t' ) {
731                         break;
732                     }
733                 }
734                 for ( q = p, p = stop; p > q; p-- ) {
735                     if ( *p == ' ' || *p == '\t' ) {
736                         break;
737                     }
738                 }
739                 for ( ; p > q; p-- ) {
740                     if ( *p != ' ' && *p != '\t' ) {
741                         break;
742                     }
743                 }
744                 for ( ; p > q; p-- ) {
745                     if ( *p == ' ' || *p == '\t' ) {
746                         break;
747                     }
748                 }
749                 if ( p <= q ) {
750                     append( out, ".\n", 2 );
751                     CONSUME( &pf, linelength + crlflength );
752                     continue;
753                 }
754                 append( out, tag_files, strlen( tag_files ));
755                 append( out, q, p - q );
756                 append( out, "\n", 1 );
757
758                 for ( ; p < stop; p++ ) {
759                     if ( *p != ' ' && *p != '\t' ) {
760                         break;
761                     }
762                 }
763                 append( out, tag_size, strlen( tag_size ));
764                 append( out, p, stop - p );
765                 append( out, "\n.\n", 3 );
766
767                 CONSUME( &pf, linelength + crlflength );
768                 continue;
769             }
770
771             /* status */
772             append( out, tag_status, strlen( tag_status ));
773             append( out, start, linelength );
774             append( out, "\n.\n", 3 );
775
776             CONSUME( &pf, linelength + crlflength );
777         } else {
778             append( out, "*\n", 2 );
779             lp_disconn_unix( s );
780             return( 0 );
781         }
782     }
783 }