90d84995958a77c74119f61e8a219b195ef6c7c1
[samba.git] / source3 / printing / lpq_parse.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    lpq parsing routines
5    Copyright (C) Andrew Tridgell 2000
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 extern int DEBUGLEVEL;
24
25
26 static char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
27                               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
28
29
30 /*******************************************************************
31 process time fields
32 ********************************************************************/
33 static time_t EntryTime(fstring tok[], int ptr, int count, int minimum)
34 {
35   time_t jobtime,jobtime1;
36
37   jobtime = time(NULL);         /* default case: take current time */
38   if (count >= minimum) {
39     struct tm *t;
40     int i, day, hour, min, sec;
41     char   *c;
42
43     for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */
44     if (i<12) {
45       t = localtime(&jobtime);
46       day = atoi(tok[ptr+1]);
47       c=(char *)(tok[ptr+2]);
48       *(c+2)=0;
49       hour = atoi(c);
50       *(c+5)=0;
51       min = atoi(c+3);
52       if(*(c+6) != 0)sec = atoi(c+6);
53       else  sec=0;
54
55       if ((t->tm_mon < i)||
56           ((t->tm_mon == i)&&
57            ((t->tm_mday < day)||
58             ((t->tm_mday == day)&&
59              (t->tm_hour*60+t->tm_min < hour*60+min)))))
60         t->tm_year--;           /* last year's print job */
61
62       t->tm_mon = i;
63       t->tm_mday = day;
64       t->tm_hour = hour;
65       t->tm_min = min;
66       t->tm_sec = sec;
67       jobtime1 = mktime(t);
68       if (jobtime1 != (time_t)-1)
69         jobtime = jobtime1;
70     }
71   }
72   return jobtime;
73 }
74
75
76 /****************************************************************************
77 parse a lpq line
78
79 here is an example of lpq output under bsd
80
81 Warning: no daemon present
82 Rank   Owner      Job  Files                                 Total Size
83 1st    tridge     148  README                                8096 bytes
84
85 here is an example of lpq output under osf/1
86
87 Warning: no daemon present
88 Rank   Pri Owner      Job  Files                             Total Size
89 1st    0   tridge     148  README                            8096 bytes
90
91
92 <allan@umich.edu> June 30, 1998.
93 Modified to handle file names with spaces, like the parse_lpq_lprng code
94 further below.
95 ****************************************************************************/
96 static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first)
97 {
98 #ifdef  OSF1
99 #define RANKTOK 0
100 #define PRIOTOK 1
101 #define USERTOK 2
102 #define JOBTOK  3
103 #define FILETOK 4
104 #define TOTALTOK (count - 2)
105 #define NTOK    6
106 #define MAXTOK  128
107 #else   /* OSF1 */
108 #define RANKTOK 0
109 #define USERTOK 1
110 #define JOBTOK  2
111 #define FILETOK 3
112 #define TOTALTOK (count - 2)
113 #define NTOK    5
114 #define MAXTOK  128
115 #endif  /* OSF1 */
116
117   char *tok[MAXTOK];
118   int  count = 0;
119   pstring line2;
120
121   pstrcpy(line2,line);
122
123 #ifdef  OSF1
124   {
125     size_t length;
126     length = strlen(line2);
127     if (line2[length-3] == ':')
128       return(False);
129   }
130 #endif  /* OSF1 */
131
132   tok[0] = strtok(line2," \t");
133   count++;
134
135   while (((tok[count] = strtok(NULL," \t")) != NULL) && (count < MAXTOK)) {
136     count++;
137   }
138
139   /* we must get at least NTOK tokens */
140   if (count < NTOK)
141     return(False);
142
143   /* the Job and Total columns must be integer */
144   if (!isdigit((int)*tok[JOBTOK]) || !isdigit((int)*tok[TOTALTOK])) return(False);
145
146   buf->job = atoi(tok[JOBTOK]);
147   buf->size = atoi(tok[TOTALTOK]);
148   buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED;
149   buf->time = time(NULL);
150   StrnCpy(buf->user,tok[USERTOK],sizeof(buf->user)-1);
151   StrnCpy(buf->file,tok[FILETOK],sizeof(buf->file)-1);
152
153   if ((FILETOK + 1) != TOTALTOK) {
154     int bufsize;
155     int i;
156
157     bufsize = sizeof(buf->file) - strlen(buf->file) - 1;
158
159     for (i = (FILETOK + 1); i < TOTALTOK; i++) {
160       safe_strcat(buf->file," ",bufsize);
161       safe_strcat(buf->file,tok[i],bufsize - 1);
162       bufsize = sizeof(buf->file) - strlen(buf->file) - 1;
163       if (bufsize <= 0) {
164         break;
165       }
166     }
167     /* Ensure null termination. */
168     buf->file[sizeof(buf->file)-1] = '\0';
169   }
170
171 #ifdef PRIOTOK
172   buf->priority = atoi(tok[PRIOTOK]);
173 #else
174   buf->priority = 1;
175 #endif
176   return(True);
177 }
178
179 /*
180 <magnus@hum.auc.dk>
181 LPRng_time modifies the current date by inserting the hour and minute from
182 the lpq output.  The lpq time looks like "23:15:07"
183
184 <allan@umich.edu> June 30, 1998.
185 Modified to work with the re-written parse_lpq_lprng routine.
186
187 <J.P.M.v.Itegem@tue.nl> Dec 17,1999
188 Modified to work with lprng 3.16
189 With lprng 3.16 The lpq time looks like
190                        "23:15:07"
191                        "23:15:07.100"
192                        "1999-12-16-23:15:07"
193                        "1999-12-16-23:15:07.100"
194
195 */
196 static time_t LPRng_time(char *time_string)
197 {
198         time_t jobtime;
199         struct tm t;
200
201         jobtime = time(NULL);         /* default case: take current time */
202         t = *localtime(&jobtime);
203
204         if ( atoi(time_string) < 24 ){
205                 t.tm_hour = atoi(time_string);
206                 t.tm_min = atoi(time_string+3);
207                 t.tm_sec = atoi(time_string+6);
208         } else {
209                 t.tm_year = atoi(time_string)-1900;
210                 t.tm_mon = atoi(time_string+5)-1;
211                 t.tm_mday = atoi(time_string+8);
212                 t.tm_hour = atoi(time_string+11);
213                 t.tm_min = atoi(time_string+14);
214                 t.tm_sec = atoi(time_string+17);
215         }    
216         jobtime = mktime(&t);
217
218         return jobtime;
219 }
220
221
222 /****************************************************************************
223   parse a lprng lpq line
224   <allan@umich.edu> June 30, 1998.
225   Re-wrote this to handle file names with spaces, multiple file names on one
226   lpq line, etc;
227 ****************************************************************************/
228 static BOOL parse_lpq_lprng(char *line,print_queue_struct *buf,BOOL first)
229 {
230 #define LPRNG_RANKTOK   0
231 #define LPRNG_USERTOK   1
232 #define LPRNG_PRIOTOK   2
233 #define LPRNG_JOBTOK    3
234 #define LPRNG_FILETOK   4
235 #define LPRNG_TOTALTOK  (num_tok - 2)
236 #define LPRNG_TIMETOK   (num_tok - 1)
237 #define LPRNG_NTOK      7
238 #define LPRNG_MAXTOK    128 /* PFMA just to keep us from running away. */
239
240   fstring tokarr[LPRNG_MAXTOK];
241   char *cptr;
242   int  num_tok = 0;
243   pstring line2;
244
245   pstrcpy(line2,line);
246   cptr = line2;
247   while(next_token( &cptr, tokarr[num_tok], " \t", sizeof(fstring)) && (num_tok < LPRNG_MAXTOK))
248     num_tok++;
249
250   /* We must get at least LPRNG_NTOK tokens. */
251   if (num_tok < LPRNG_NTOK) {
252     return(False);
253   }
254
255   if (!isdigit((int)*tokarr[LPRNG_JOBTOK]) || !isdigit((int)*tokarr[LPRNG_TOTALTOK])) {
256     return(False);
257   }
258
259   buf->job  = atoi(tokarr[LPRNG_JOBTOK]);
260   buf->size = atoi(tokarr[LPRNG_TOTALTOK]);
261
262   if (strequal(tokarr[LPRNG_RANKTOK],"active")) {
263     buf->status = LPQ_PRINTING;
264   } else if (isdigit((int)*tokarr[LPRNG_RANKTOK])) {
265     buf->status = LPQ_QUEUED;
266   } else {
267     buf->status = LPQ_PAUSED;
268   }
269
270   buf->priority = *tokarr[LPRNG_PRIOTOK] -'A';
271
272   buf->time = LPRng_time(tokarr[LPRNG_TIMETOK]);
273
274   StrnCpy(buf->user,tokarr[LPRNG_USERTOK],sizeof(buf->user)-1);
275
276   /* The '@hostname' prevents windows from displaying the printing icon
277    * for the current user on the taskbar.  Plop in a null.
278    */
279
280   if ((cptr = strchr_m(buf->user,'@')) != NULL) {
281     *cptr = '\0';
282   }
283
284   StrnCpy(buf->file,tokarr[LPRNG_FILETOK],sizeof(buf->file)-1);
285
286   if ((LPRNG_FILETOK + 1) != LPRNG_TOTALTOK) {
287     int bufsize;
288     int i;
289
290     bufsize = sizeof(buf->file) - strlen(buf->file) - 1;
291
292     for (i = (LPRNG_FILETOK + 1); i < LPRNG_TOTALTOK; i++) {
293       safe_strcat(buf->file," ",bufsize);
294       safe_strcat(buf->file,tokarr[i],bufsize - 1);
295       bufsize = sizeof(buf->file) - strlen(buf->file) - 1;
296       if (bufsize <= 0) {
297         break;
298       }
299     }
300     /* Ensure null termination. */
301     buf->file[sizeof(buf->file)-1] = '\0';
302   }
303
304   return(True);
305 }
306
307
308
309 /*******************************************************************
310 parse lpq on an aix system
311
312 Queue   Dev   Status    Job Files              User         PP %   Blks  Cp Rnk
313 ------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
314 lazer   lazer READY
315 lazer   lazer RUNNING   537 6297doc.A          kvintus@IE    0 10  2445   1   1
316               QUEUED    538 C.ps               root@IEDVB           124   1   2
317               QUEUED    539 E.ps               root@IEDVB            28   1   3
318               QUEUED    540 L.ps               root@IEDVB           172   1   4
319               QUEUED    541 P.ps               root@IEDVB            22   1   5
320 ********************************************************************/
321 static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
322 {
323   fstring tok[11];
324   int count=0;
325
326   /* handle the case of "(standard input)" as a filename */
327   pstring_sub(line,"standard input","STDIN");
328   all_string_sub(line,"(","\"",0);
329   all_string_sub(line,")","\"",0);
330
331   for (count=0; 
332        count<10 && 
333                next_token(&line,tok[count],NULL, sizeof(tok[count])); 
334        count++) ;
335
336   /* we must get 6 tokens */
337   if (count < 10)
338   {
339       if ((count == 7) && ((strcmp(tok[0],"QUEUED") == 0) || (strcmp(tok[0],"HELD") == 0)))
340       {
341           /* the 2nd and 5th columns must be integer */
342           if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4])) return(False);
343           buf->size = atoi(tok[4]) * 1024;
344           /* if the fname contains a space then use STDIN */
345           if (strchr_m(tok[2],' '))
346             fstrcpy(tok[2],"STDIN");
347
348           /* only take the last part of the filename */
349           {
350             fstring tmp;
351             char *p = strrchr_m(tok[2],'/');
352             if (p)
353               {
354                 fstrcpy(tmp,p+1);
355                 fstrcpy(tok[2],tmp);
356               }
357           }
358
359
360           buf->job = atoi(tok[1]);
361           buf->status = strequal(tok[0],"HELD")?LPQ_PAUSED:LPQ_QUEUED;
362           buf->priority = 0;
363           buf->time = time(NULL);
364           StrnCpy(buf->user,tok[3],sizeof(buf->user)-1);
365           StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
366       }
367       else
368       {
369           DEBUG(6,("parse_lpq_aix count=%d\n", count));
370           return(False);
371       }
372   }
373   else
374   {
375       /* the 4th and 9th columns must be integer */
376       if (!isdigit((int)*tok[3]) || !isdigit((int)*tok[8])) return(False);
377       buf->size = atoi(tok[8]) * 1024;
378       /* if the fname contains a space then use STDIN */
379       if (strchr_m(tok[4],' '))
380         fstrcpy(tok[4],"STDIN");
381
382       /* only take the last part of the filename */
383       {
384         fstring tmp;
385         char *p = strrchr_m(tok[4],'/');
386         if (p)
387           {
388             fstrcpy(tmp,p+1);
389             fstrcpy(tok[4],tmp);
390           }
391       }
392
393
394       buf->job = atoi(tok[3]);
395       buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
396       buf->priority = 0;
397       buf->time = time(NULL);
398       StrnCpy(buf->user,tok[5],sizeof(buf->user)-1);
399       StrnCpy(buf->file,tok[4],sizeof(buf->file)-1);
400   }
401
402
403   return(True);
404 }
405
406
407 /****************************************************************************
408 parse a lpq line
409 here is an example of lpq output under hpux; note there's no space after -o !
410 $> lpstat -oljplus
411 ljplus-2153         user           priority 0  Jan 19 08:14 on ljplus
412       util.c                                  125697 bytes
413       server.c                                110712 bytes
414 ljplus-2154         user           priority 0  Jan 19 08:14 from client
415       (standard input)                          7551 bytes
416 ****************************************************************************/
417 static BOOL parse_lpq_hpux(char * line, print_queue_struct *buf, BOOL first)
418 {
419   /* must read two lines to process, therefore keep some values static */
420   static BOOL header_line_ok=False, base_prio_reset=False;
421   static fstring jobuser;
422   static int jobid;
423   static int jobprio;
424   static time_t jobtime;
425   static int jobstat=LPQ_QUEUED;
426   /* to store minimum priority to print, lpstat command should be invoked
427      with -p option first, to work */
428   static int base_prio;
429  
430   int count;
431   char htab = '\011';  
432   fstring tok[12];
433
434   /* If a line begins with a horizontal TAB, it is a subline type */
435   
436   if (line[0] == htab) { /* subline */
437     /* check if it contains the base priority */
438     if (!strncmp(line,"\tfence priority : ",18)) {
439        base_prio=atoi(&line[18]);
440        DEBUG(4, ("fence priority set at %d\n", base_prio));
441     }
442     if (!header_line_ok) return (False); /* incorrect header line */
443     /* handle the case of "(standard input)" as a filename */
444     pstring_sub(line,"standard input","STDIN");
445     all_string_sub(line,"(","\"",0);
446     all_string_sub(line,")","\"",0);
447     
448     for (count=0; count<2 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
449     /* we must get 2 tokens */
450     if (count < 2) return(False);
451     
452     /* the 2nd column must be integer */
453     if (!isdigit((int)*tok[1])) return(False);
454     
455     /* if the fname contains a space then use STDIN */
456     if (strchr_m(tok[0],' '))
457       fstrcpy(tok[0],"STDIN");
458     
459     buf->size = atoi(tok[1]);
460     StrnCpy(buf->file,tok[0],sizeof(buf->file)-1);
461     
462     /* fill things from header line */
463     buf->time = jobtime;
464     buf->job = jobid;
465     buf->status = jobstat;
466     buf->priority = jobprio;
467     StrnCpy(buf->user,jobuser,sizeof(buf->user)-1);
468     
469     return(True);
470   }
471   else { /* header line */
472     header_line_ok=False; /* reset it */
473     if (first) {
474        if (!base_prio_reset) {
475           base_prio=0; /* reset it */
476           base_prio_reset=True;
477        }
478     }
479     else if (base_prio) base_prio_reset=False;
480     
481     /* handle the dash in the job id */
482     pstring_sub(line,"-"," ");
483     
484     for (count=0; count<12 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
485       
486     /* we must get 8 tokens */
487     if (count < 8) return(False);
488     
489     /* first token must be printer name (cannot check ?) */
490     /* the 2nd, 5th & 7th column must be integer */
491     if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4]) || !isdigit((int)*tok[6])) return(False);
492     jobid = atoi(tok[1]);
493     StrnCpy(jobuser,tok[2],sizeof(buf->user)-1);
494     jobprio = atoi(tok[4]);
495     
496     /* process time */
497     jobtime=EntryTime(tok, 5, count, 8);
498     if (jobprio < base_prio) {
499        jobstat = LPQ_PAUSED;
500        DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat));
501     }
502     else {
503        jobstat = LPQ_QUEUED;
504        if ((count >8) && (((strequal(tok[8],"on")) ||
505                            ((strequal(tok[8],"from")) && 
506                             ((count > 10)&&(strequal(tok[10],"on")))))))
507          jobstat = LPQ_PRINTING;
508     }
509     
510     header_line_ok=True; /* information is correct */
511     return(False); /* need subline info to include into queuelist */
512   }
513 }
514
515
516 /****************************************************************************
517 parse a lpstat line
518
519 here is an example of "lpstat -o dcslw" output under sysv
520
521 dcslw-896               tridge            4712   Dec 20 10:30:30 on dcslw
522 dcslw-897               tridge            4712   Dec 20 10:30:30 being held
523
524 ****************************************************************************/
525 static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first)
526 {
527   fstring tok[9];
528   int count=0;
529   char *p;
530
531   /* 
532    * Handle the dash in the job id, but make sure that we skip over
533    * the printer name in case we have a dash in that.
534    * Patch from Dom.Mitchell@palmerharvey.co.uk.
535    */
536
537   /*
538    * Move to the first space.
539    */
540   for (p = line ; !isspace(*p) && *p; p++)
541     ;
542
543   /*
544    * Back up until the last '-' character or
545    * start of line.
546    */
547   for (; (p >= line) && (*p != '-'); p--)
548     ;
549
550   if((p >= line) && (*p == '-'))
551     *p = ' ';
552
553   for (count=0; count<9 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++)
554     ;
555
556   /* we must get 7 tokens */
557   if (count < 7)
558     return(False);
559
560   /* the 2nd and 4th, 6th columns must be integer */
561   if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[3]))
562     return(False);
563   if (!isdigit((int)*tok[5]))
564     return(False);
565
566   /* if the user contains a ! then trim the first part of it */  
567   if ((p=strchr_m(tok[2],'!'))) {
568       fstring tmp;
569       fstrcpy(tmp,p+1);
570       fstrcpy(tok[2],tmp);
571   }
572
573   buf->job = atoi(tok[1]);
574   buf->size = atoi(tok[3]);
575   if (count > 7 && strequal(tok[7],"on"))
576     buf->status = LPQ_PRINTING;
577   else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
578     buf->status = LPQ_PAUSED;
579   else
580     buf->status = LPQ_QUEUED;
581   buf->priority = 0;
582   buf->time = EntryTime(tok, 4, count, 7);
583   StrnCpy(buf->user,tok[2],sizeof(buf->user)-1);
584   StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
585   return(True);
586 }
587
588 /****************************************************************************
589 parse a lpq line
590
591 here is an example of lpq output under qnx
592 Spooler: /qnx/spooler, on node 1
593 Printer: txt        (ready) 
594 0000:     root  [job #1    ]   active 1146 bytes        /etc/profile
595 0001:     root  [job #2    ]    ready 2378 bytes        /etc/install
596 0002:     root  [job #3    ]    ready 1146 bytes        -- standard input --
597 ****************************************************************************/
598 static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
599 {
600   fstring tok[7];
601   int count=0;
602
603   DEBUG(4,("antes [%s]\n", line));
604
605   /* handle the case of "-- standard input --" as a filename */
606   pstring_sub(line,"standard input","STDIN");
607   DEBUG(4,("despues [%s]\n", line));
608   all_string_sub(line,"-- ","\"",0);
609   all_string_sub(line," --","\"",0);
610   DEBUG(4,("despues 1 [%s]\n", line));
611
612   pstring_sub(line,"[job #","");
613   pstring_sub(line,"]","");
614   DEBUG(4,("despues 2 [%s]\n", line));
615
616   
617   
618   for (count=0; count<7 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
619
620   /* we must get 7 tokens */
621   if (count < 7)
622     return(False);
623
624   /* the 3rd and 5th columns must be integer */
625   if (!isdigit((int)*tok[2]) || !isdigit((int)*tok[4])) return(False);
626
627   /* only take the last part of the filename */
628   {
629     fstring tmp;
630     char *p = strrchr_m(tok[6],'/');
631     if (p)
632       {
633         fstrcpy(tmp,p+1);
634         fstrcpy(tok[6],tmp);
635       }
636   }
637         
638
639   buf->job = atoi(tok[2]);
640   buf->size = atoi(tok[4]);
641   buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED;
642   buf->priority = 0;
643   buf->time = time(NULL);
644   StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
645   StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
646   return(True);
647 }
648
649
650 /****************************************************************************
651   parse a lpq line for the plp printing system
652   Bertrand Wallrich <Bertrand.Wallrich@loria.fr>
653
654 redone by tridge. Here is a sample queue:
655
656 Local  Printer 'lp2' (fjall):
657   Printing (started at Jun 15 13:33:58, attempt 1).
658     Rank Owner       Pr Opt  Job Host        Files           Size     Date
659   active tridge      X  -    6   fjall       /etc/hosts      739      Jun 15 13:33
660      3rd tridge      X  -    7   fjall       /etc/hosts      739      Jun 15 13:33
661
662 ****************************************************************************/
663 static BOOL parse_lpq_plp(char *line,print_queue_struct *buf,BOOL first)
664 {
665   fstring tok[11];
666   int count=0;
667
668   /* handle the case of "(standard input)" as a filename */
669   pstring_sub(line,"stdin","STDIN");
670   all_string_sub(line,"(","\"",0);
671   all_string_sub(line,")","\"",0);
672   
673   for (count=0; count<11 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
674
675   /* we must get 11 tokens */
676   if (count < 11)
677     return(False);
678
679   /* the first must be "active" or begin with an integer */
680   if (strcmp(tok[0],"active") && !isdigit((int)tok[0][0]))
681     return(False);
682
683   /* the 5th and 8th must be integer */
684   if (!isdigit((int)*tok[4]) || !isdigit((int)*tok[7])) 
685     return(False);
686
687   /* if the fname contains a space then use STDIN */
688   if (strchr_m(tok[6],' '))
689     fstrcpy(tok[6],"STDIN");
690
691   /* only take the last part of the filename */
692   {
693     fstring tmp;
694     char *p = strrchr_m(tok[6],'/');
695     if (p)
696       {
697         fstrcpy(tmp,p+1);
698         fstrcpy(tok[6],tmp);
699       }
700   }
701
702
703   buf->job = atoi(tok[4]);
704
705   buf->size = atoi(tok[7]);
706   if (strchr_m(tok[7],'K'))
707     buf->size *= 1024;
708   if (strchr_m(tok[7],'M'))
709     buf->size *= 1024*1024;
710
711   buf->status = strequal(tok[0],"active")?LPQ_PRINTING:LPQ_QUEUED;
712   buf->priority = 0;
713   buf->time = time(NULL);
714   StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
715   StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
716   return(True);
717 }
718
719 /****************************************************************************
720 parse a qstat line
721
722 here is an example of "qstat -l -d qms" output under softq
723
724 Queue qms: 2 jobs; daemon active (313); enabled; accepting;
725  job-ID   submission-time     pri     size owner      title 
726 205980: H 98/03/09 13:04:05     0    15733 stephenf   chap1.ps
727 206086:>  98/03/12 17:24:40     0      659 chris      -
728 206087:   98/03/12 17:24:45     0     4876 chris      -
729 Total:      21268 bytes in queue
730
731
732 ****************************************************************************/
733 static BOOL parse_lpq_softq(char *line,print_queue_struct *buf,BOOL first)
734 {
735   fstring tok[10];
736   int count=0;
737
738   /* mung all the ":"s to spaces*/
739   pstring_sub(line,":"," ");
740   
741   for (count=0; count<10 && next_token(&line,tok[count],NULL,sizeof(tok[count])); count++) ;
742
743   /* we must get 9 tokens */
744   if (count < 9)
745     return(False);
746
747   /* the 1st and 7th columns must be integer */
748   if (!isdigit((int)*tok[0]) || !isdigit((int)*tok[6]))  return(False);
749   /* if the 2nd column is either '>' or 'H' then the 7th and 8th must be
750    * integer, else it's the 6th and 7th that must be
751    */
752   if (*tok[1] == 'H' || *tok[1] == '>')
753     {
754       if (!isdigit((int)*tok[7]))
755         return(False);
756       buf->status = *tok[1] == '>' ? LPQ_PRINTING : LPQ_PAUSED;
757       count = 1;
758     }
759   else
760     {
761       if (!isdigit((int)*tok[5]))
762         return(False);
763       buf->status = LPQ_QUEUED;
764       count = 0;
765     }
766         
767
768   buf->job = atoi(tok[0]);
769   buf->size = atoi(tok[count+6]);
770   buf->priority = atoi(tok[count+5]);
771   StrnCpy(buf->user,tok[count+7],sizeof(buf->user)-1);
772   StrnCpy(buf->file,tok[count+8],sizeof(buf->file)-1);
773   buf->time = time(NULL);               /* default case: take current time */
774   {
775     time_t jobtime;
776     struct tm *t;
777
778     t = localtime(&buf->time);
779     t->tm_mday = atoi(tok[count+2]+6);
780     t->tm_mon  = atoi(tok[count+2]+3);
781     switch (*tok[count+2])
782     {
783     case 7: case 8: case 9: t->tm_year = atoi(tok[count+2]); break;
784     default:                t->tm_year = atoi(tok[count+2]); break;
785     }
786
787     t->tm_hour = atoi(tok[count+3]);
788     t->tm_min = atoi(tok[count+4]);
789     t->tm_sec = atoi(tok[count+5]);
790     jobtime = mktime(t);
791     if (jobtime != (time_t)-1)
792       buf->time = jobtime; 
793   }
794
795   return(True);
796 }
797
798 /*******************************************************************
799 parse lpq on an NT system
800
801                          Windows 2000 LPD Server
802                               Printer \\10.0.0.2\NP17PCL (Paused)
803
804 Owner       Status         Jobname          Job-Id    Size   Pages  Priority
805 ----------------------------------------------------------------------------
806 root (9.99. Printing  /usr/lib/rhs/rhs-pr      3       625      0      1
807 root (9.99. Paused    /usr/lib/rhs/rhs-pr      4       625      0      1
808 jmcd        Waiting   Re: Samba Open Sour     26     32476      1      1
809
810 ********************************************************************/
811 static BOOL parse_lpq_nt(char *line,print_queue_struct *buf,BOOL first)
812 {
813 #define LPRNT_OWNSIZ 11
814 #define LPRNT_STATSIZ 9
815 #define LPRNT_JOBSIZ 19
816 #define LPRNT_IDSIZ 6
817 #define LPRNT_SIZSIZ 9
818   typedef struct 
819   {
820     char owner[LPRNT_OWNSIZ];
821     char space1;
822     char status[LPRNT_STATSIZ];
823     char space2;
824     char jobname[LPRNT_JOBSIZ];
825     char space3;
826     char jobid[LPRNT_IDSIZ];
827     char space4;
828     char size[LPRNT_SIZSIZ];
829     char terminator;
830   } nt_lpq_line;
831
832   nt_lpq_line parse_line;
833 #define LPRNT_PRINTING "Printing"
834 #define LPRNT_WAITING "Waiting"
835 #define LPRNT_PAUSED "Paused"
836
837   memset(&parse_line, '\0', sizeof(parse_line));
838   strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
839
840   if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
841     return(False);
842
843   /* Just want the first word in the owner field - the username */
844   if (strchr_m(parse_line.owner, ' '))
845     *(strchr_m(parse_line.owner, ' ')) = '\0';
846   else
847     parse_line.space1 = '\0';
848
849   /* Make sure we have an owner */
850   if (!strlen(parse_line.owner))
851     return(False);
852
853   /* Make sure the status is valid */
854   parse_line.space2 = '\0';
855   trim_string(parse_line.status, NULL, " ");
856   if (!strequal(parse_line.status, LPRNT_PRINTING) &&
857       !strequal(parse_line.status, LPRNT_PAUSED) &&
858       !strequal(parse_line.status, LPRNT_WAITING))
859     return(False);
860   
861   parse_line.space3 = '\0';
862   trim_string(parse_line.jobname, NULL, " ");
863
864   buf->job = atoi(parse_line.jobid);
865   buf->priority = 0;
866   buf->size = atoi(parse_line.size);
867   buf->time = time(NULL);
868   StrnCpy(buf->user, parse_line.owner, sizeof(buf->user)-1);
869   StrnCpy(buf->file, parse_line.jobname, sizeof(buf->file)-1);
870   if (strequal(parse_line.status, LPRNT_PRINTING))
871     buf->status = LPQ_PRINTING;
872   else if (strequal(parse_line.status, LPRNT_PAUSED))
873     buf->status = LPQ_PAUSED;
874   else
875     buf->status = LPQ_QUEUED;
876
877   return(True);
878 }
879
880 /*******************************************************************
881 parse lpq on an OS2 system
882
883 JobID  File Name          Rank      Size        Status          Comment       
884 -----  ---------------    ------    --------    ------------    ------------  
885     3  Control                 1          68    Queued          root@psflinu  
886     4  /etc/motd               2       11666    Queued          root@psflinu  
887
888 ********************************************************************/
889 static BOOL parse_lpq_os2(char *line,print_queue_struct *buf,BOOL first)
890 {
891 #define LPROS2_IDSIZ 5
892 #define LPROS2_JOBSIZ 15
893 #define LPROS2_SIZSIZ 8
894 #define LPROS2_STATSIZ 12
895 #define LPROS2_OWNSIZ 12
896   typedef struct 
897   {
898     char jobid[LPROS2_IDSIZ];
899     char space1[2];
900     char jobname[LPROS2_JOBSIZ];
901     char space2[14];
902     char size[LPROS2_SIZSIZ];
903     char space3[4];
904     char status[LPROS2_STATSIZ];
905     char space4[4];
906     char owner[LPROS2_OWNSIZ];
907     char terminator;
908   } os2_lpq_line;
909
910   os2_lpq_line parse_line;
911 #define LPROS2_PRINTING "Printing"
912 #define LPROS2_WAITING "Queued"
913 #define LPROS2_PAUSED "Paused"
914
915   memset(&parse_line, '\0', sizeof(parse_line));
916   strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
917
918   if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
919     return(False);
920
921   /* Get the jobid */
922   buf->job = atoi(parse_line.jobid);
923
924   /* Get the job name */
925   parse_line.space2[0] = '\0';
926   trim_string(parse_line.jobname, NULL, " ");
927   StrnCpy(buf->file, parse_line.jobname, sizeof(buf->file)-1);
928
929   buf->priority = 0;
930   buf->size = atoi(parse_line.size);
931   buf->time = time(NULL);
932
933   /* Make sure we have an owner */
934   if (!strlen(parse_line.owner))
935     return(False);
936
937   /* Make sure we have a valid status */
938   parse_line.space4[0] = '\0';
939   trim_string(parse_line.status, NULL, " ");
940   if (!strequal(parse_line.status, LPROS2_PRINTING) &&
941       !strequal(parse_line.status, LPROS2_PAUSED) &&
942       !strequal(parse_line.status, LPROS2_WAITING))
943     return(False);
944
945   StrnCpy(buf->user, parse_line.owner, sizeof(buf->user)-1);
946   if (strequal(parse_line.status, LPROS2_PRINTING))
947     buf->status = LPQ_PRINTING;
948   else if (strequal(parse_line.status, LPROS2_PAUSED))
949     buf->status = LPQ_PAUSED;
950   else
951     buf->status = LPQ_QUEUED;
952
953   return(True);
954 }
955
956 static char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
957 static char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
958 static char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
959
960 #ifdef DEVELOPER
961
962 /****************************************************************************
963 parse a vlp line
964 ****************************************************************************/
965 static BOOL parse_lpq_vlp(char *line,print_queue_struct *buf,BOOL first)
966 {
967         int toknum = 0;
968         fstring tok;
969
970         /* First line is printer status */
971
972         if (!isdigit(line[0])) return False;
973
974         /* Parse a print job entry */
975
976         while(next_token(&line, tok, NULL, sizeof(fstring))) {
977                 switch (toknum) {
978                 case 0:
979                         buf->job = atoi(tok);
980                         break;
981                 case 1:
982                         buf->size = atoi(tok);
983                         break;
984                 case 2:
985                         buf->status = atoi(tok);
986                         break;
987                 case 3:
988                         buf->time = atoi(tok);
989                         break;
990                 case 4:
991                         fstrcpy(buf->user, tok);
992                         break;
993                 case 5:
994                         fstrcpy(buf->file, tok);
995                         break;
996                 }
997                 toknum++;
998         }
999
1000         return True;
1001 }
1002
1003 #endif /* DEVELOPER */
1004
1005 /****************************************************************************
1006 parse a lpq line. Choose printing style
1007 ****************************************************************************/
1008 BOOL parse_lpq_entry(int snum,char *line,
1009                      print_queue_struct *buf,
1010                      print_status_struct *status,BOOL first)
1011 {
1012   BOOL ret;
1013
1014   switch (lp_printing(snum))
1015     {
1016     case PRINT_SYSV:
1017       ret = parse_lpq_sysv(line,buf,first);
1018       break;
1019     case PRINT_AIX:      
1020       ret = parse_lpq_aix(line,buf,first);
1021       break;
1022     case PRINT_HPUX:
1023       ret = parse_lpq_hpux(line,buf,first);
1024       break;
1025     case PRINT_QNX:
1026       ret = parse_lpq_qnx(line,buf,first);
1027       break;
1028     case PRINT_LPRNG:
1029       ret = parse_lpq_lprng(line,buf,first);
1030       break;
1031     case PRINT_PLP:
1032       ret = parse_lpq_plp(line,buf,first);
1033       break;
1034     case PRINT_SOFTQ:
1035       ret = parse_lpq_softq(line,buf,first);
1036       break;
1037     case PRINT_LPRNT:
1038       ret = parse_lpq_nt(line,buf,first);
1039       break;
1040     case PRINT_LPROS2:
1041       ret = parse_lpq_os2(line,buf,first);
1042       break;
1043 #ifdef DEVELOPER
1044     case PRINT_VLP:
1045     case PRINT_TEST:
1046             ret = parse_lpq_vlp(line,buf,first);
1047             break;
1048 #endif /* DEVELOPER */
1049     default:
1050       ret = parse_lpq_bsd(line,buf,first);
1051       break;
1052     }
1053
1054   /* We don't want the newline in the status message. */
1055   {
1056     char *p = strchr_m(line,'\n');
1057     if (p) *p = 0;
1058   }
1059
1060   /* in the LPRNG case, we skip lines starting by a space.*/
1061   if (line && !ret && (lp_printing(snum)==PRINT_LPRNG) )
1062   {
1063         if (line[0]==' ')
1064                 return ret;
1065   }
1066
1067
1068   if (status && !ret)
1069     {
1070       /* a few simple checks to see if the line might be a
1071          printer status line: 
1072          handle them so that most severe condition is shown */
1073       int i;
1074       strlower(line);
1075       
1076       switch (status->status) {
1077       case LPSTAT_OK:
1078         for (i=0; stat0_strings[i]; i++)
1079           if (strstr(line,stat0_strings[i])) {
1080             StrnCpy(status->message,line,sizeof(status->message)-1);
1081             status->status=LPSTAT_OK;
1082             return ret;
1083           }
1084       case LPSTAT_STOPPED:
1085         for (i=0; stat1_strings[i]; i++)
1086           if (strstr(line,stat1_strings[i])) {
1087             StrnCpy(status->message,line,sizeof(status->message)-1);
1088             status->status=LPSTAT_STOPPED;
1089             return ret;
1090           }
1091       case LPSTAT_ERROR:
1092         for (i=0; stat2_strings[i]; i++)
1093           if (strstr(line,stat2_strings[i])) {
1094             StrnCpy(status->message,line,sizeof(status->message)-1);
1095             status->status=LPSTAT_ERROR;
1096             return ret;
1097           }
1098         break;
1099       }
1100     }
1101
1102   return(ret);
1103 }