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