Removed global_myworkgroup, global_myname, global_myscope. Added liberal
[jra/samba/.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   const char *cptr;
236   char *ptr;
237   int  num_tok = 0;
238
239   cptr = line;
240   while(next_token( &cptr, tokarr[num_tok], " \t", sizeof(fstring)) && (num_tok < LPRNG_MAXTOK))
241     num_tok++;
242
243   /* We must get at least LPRNG_NTOK tokens. */
244   if (num_tok < LPRNG_NTOK) {
245     return(False);
246   }
247
248   if (!isdigit((int)*tokarr[LPRNG_JOBTOK]) || !isdigit((int)*tokarr[LPRNG_TOTALTOK])) {
249     return(False);
250   }
251
252   buf->job  = atoi(tokarr[LPRNG_JOBTOK]);
253   buf->size = atoi(tokarr[LPRNG_TOTALTOK]);
254
255   if (strequal(tokarr[LPRNG_RANKTOK],"active")) {
256     buf->status = LPQ_PRINTING;
257   } else if (strequal(tokarr[LPRNG_RANKTOK],"done")) {
258     buf->status = LPQ_PRINTED;
259   } else if (isdigit((int)*tokarr[LPRNG_RANKTOK])) {
260     buf->status = LPQ_QUEUED;
261   } else {
262     buf->status = LPQ_PAUSED;
263   }
264
265   buf->priority = *tokarr[LPRNG_PRIOTOK] -'A';
266
267   buf->time = LPRng_time(tokarr[LPRNG_TIMETOK]);
268
269   StrnCpy(buf->fs_user,tokarr[LPRNG_USERTOK],sizeof(buf->fs_user)-1);
270
271   /* The '@hostname' prevents windows from displaying the printing icon
272    * for the current user on the taskbar.  Plop in a null.
273    */
274
275   if ((ptr = strchr_m(buf->fs_user,'@')) != NULL) {
276     *ptr = '\0';
277   }
278
279   StrnCpy(buf->fs_file,tokarr[LPRNG_FILETOK],sizeof(buf->fs_file)-1);
280
281   if ((LPRNG_FILETOK + 1) != LPRNG_TOTALTOK) {
282     int i;
283
284     for (i = (LPRNG_FILETOK + 1); i < LPRNG_TOTALTOK; i++) {
285       /* FIXME: Using fstrcat rather than other means is a bit
286        * inefficient; this might be a problem for enormous queues with
287        * many fields. */
288       fstrcat(buf->fs_file, " ");
289       fstrcat(buf->fs_file, tokarr[i]);
290     }
291     /* Ensure null termination. */
292     fstrterminate(buf->fs_file);
293   }
294
295   return(True);
296 }
297
298
299
300 /*******************************************************************
301 parse lpq on an aix system
302
303 Queue   Dev   Status    Job Files              User         PP %   Blks  Cp Rnk
304 ------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
305 lazer   lazer READY
306 lazer   lazer RUNNING   537 6297doc.A          kvintus@IE    0 10  2445   1   1
307               QUEUED    538 C.ps               root@IEDVB           124   1   2
308               QUEUED    539 E.ps               root@IEDVB            28   1   3
309               QUEUED    540 L.ps               root@IEDVB           172   1   4
310               QUEUED    541 P.ps               root@IEDVB            22   1   5
311 ********************************************************************/
312 static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
313 {
314   fstring tok[11];
315   int count=0;
316   const char *cline = line;
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(&cline,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   int count;
422   char htab = '\011';  
423   const char *cline = line;
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(&cline,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(&cline,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   const char *cline = line;
523
524   /* 
525    * Handle the dash in the job id, but make sure that we skip over
526    * the printer name in case we have a dash in that.
527    * Patch from Dom.Mitchell@palmerharvey.co.uk.
528    */
529
530   /*
531    * Move to the first space.
532    */
533   for (p = line ; !isspace(*p) && *p; p++)
534     ;
535
536   /*
537    * Back up until the last '-' character or
538    * start of line.
539    */
540   for (; (p >= line) && (*p != '-'); p--)
541     ;
542
543   if((p >= line) && (*p == '-'))
544     *p = ' ';
545
546   for (count=0; count<9 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++)
547     ;
548
549   /* we must get 7 tokens */
550   if (count < 7)
551     return(False);
552
553   /* the 2nd and 4th, 6th columns must be integer */
554   if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[3]))
555     return(False);
556   if (!isdigit((int)*tok[5]))
557     return(False);
558
559   /* if the user contains a ! then trim the first part of it */  
560   if ((p=strchr_m(tok[2],'!'))) {
561       fstring tmp;
562       fstrcpy(tmp,p+1);
563       fstrcpy(tok[2],tmp);
564   }
565
566   buf->job = atoi(tok[1]);
567   buf->size = atoi(tok[3]);
568   if (count > 7 && strequal(tok[7],"on"))
569     buf->status = LPQ_PRINTING;
570   else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
571     buf->status = LPQ_PAUSED;
572   else
573     buf->status = LPQ_QUEUED;
574   buf->priority = 0;
575   buf->time = EntryTime(tok, 4, count, 7);
576   StrnCpy(buf->fs_user,tok[2],sizeof(buf->fs_user)-1);
577   StrnCpy(buf->fs_file,tok[2],sizeof(buf->fs_file)-1);
578   return(True);
579 }
580
581 /****************************************************************************
582 parse a lpq line
583
584 here is an example of lpq output under qnx
585 Spooler: /qnx/spooler, on node 1
586 Printer: txt        (ready) 
587 0000:     root  [job #1    ]   active 1146 bytes        /etc/profile
588 0001:     root  [job #2    ]    ready 2378 bytes        /etc/install
589 0002:     root  [job #3    ]    ready 1146 bytes        -- standard input --
590 ****************************************************************************/
591 static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
592 {
593   fstring tok[7];
594   int count=0;
595   const char *cline;
596
597   DEBUG(4,("antes [%s]\n", line));
598
599   /* handle the case of "-- standard input --" as a filename */
600   string_sub(line,"standard input","STDIN",0);
601   DEBUG(4,("despues [%s]\n", line));
602   all_string_sub(line,"-- ","\"",0);
603   all_string_sub(line," --","\"",0);
604   DEBUG(4,("despues 1 [%s]\n", line));
605
606   string_sub(line,"[job #","",0);
607   string_sub(line,"]","",0);
608   DEBUG(4,("despues 2 [%s]\n", line));
609
610   for (count=0; count<7 && next_token(&cline,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   const char *cline = line;
660
661   /* handle the case of "(standard input)" as a filename */
662   string_sub(line,"stdin","STDIN",0);
663   all_string_sub(line,"(","\"",0);
664   all_string_sub(line,")","\"",0);
665   
666   for (count=0; count<11 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
667
668   /* we must get 11 tokens */
669   if (count < 11)
670     return(False);
671
672   /* the first must be "active" or begin with an integer */
673   if (strcmp(tok[0],"active") && !isdigit((int)tok[0][0]))
674     return(False);
675
676   /* the 5th and 8th must be integer */
677   if (!isdigit((int)*tok[4]) || !isdigit((int)*tok[7])) 
678     return(False);
679
680   /* if the fname contains a space then use STDIN */
681   if (strchr_m(tok[6],' '))
682     fstrcpy(tok[6],"STDIN");
683
684   /* only take the last part of the filename */
685   {
686     fstring tmp;
687     char *p = strrchr_m(tok[6],'/');
688     if (p)
689       {
690         fstrcpy(tmp,p+1);
691         fstrcpy(tok[6],tmp);
692       }
693   }
694
695
696   buf->job = atoi(tok[4]);
697
698   buf->size = atoi(tok[7]);
699   if (strchr_m(tok[7],'K'))
700     buf->size *= 1024;
701   if (strchr_m(tok[7],'M'))
702     buf->size *= 1024*1024;
703
704   buf->status = strequal(tok[0],"active")?LPQ_PRINTING:LPQ_QUEUED;
705   buf->priority = 0;
706   buf->time = time(NULL);
707   StrnCpy(buf->fs_user,tok[1],sizeof(buf->fs_user)-1);
708   StrnCpy(buf->fs_file,tok[6],sizeof(buf->fs_file)-1);
709   return(True);
710 }
711
712 /****************************************************************************
713 parse a qstat line
714
715 here is an example of "qstat -l -d qms" output under softq
716
717 Queue qms: 2 jobs; daemon active (313); enabled; accepting;
718  job-ID   submission-time     pri     size owner      title 
719 205980: H 98/03/09 13:04:05     0    15733 stephenf   chap1.ps
720 206086:>  98/03/12 17:24:40     0      659 chris      -
721 206087:   98/03/12 17:24:45     0     4876 chris      -
722 Total:      21268 bytes in queue
723
724
725 ****************************************************************************/
726 static BOOL parse_lpq_softq(char *line,print_queue_struct *buf,BOOL first)
727 {
728   fstring tok[10];
729   int count=0;
730   const char *cline = line;
731
732   /* mung all the ":"s to spaces*/
733   string_sub(line,":"," ",0);
734   
735   for (count=0; count<10 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
736
737   /* we must get 9 tokens */
738   if (count < 9)
739     return(False);
740
741   /* the 1st and 7th columns must be integer */
742   if (!isdigit((int)*tok[0]) || !isdigit((int)*tok[6]))  return(False);
743   /* if the 2nd column is either '>' or 'H' then the 7th and 8th must be
744    * integer, else it's the 6th and 7th that must be
745    */
746   if (*tok[1] == 'H' || *tok[1] == '>')
747     {
748       if (!isdigit((int)*tok[7]))
749         return(False);
750       buf->status = *tok[1] == '>' ? LPQ_PRINTING : LPQ_PAUSED;
751       count = 1;
752     }
753   else
754     {
755       if (!isdigit((int)*tok[5]))
756         return(False);
757       buf->status = LPQ_QUEUED;
758       count = 0;
759     }
760         
761
762   buf->job = atoi(tok[0]);
763   buf->size = atoi(tok[count+6]);
764   buf->priority = atoi(tok[count+5]);
765   StrnCpy(buf->fs_user,tok[count+7],sizeof(buf->fs_user)-1);
766   StrnCpy(buf->fs_file,tok[count+8],sizeof(buf->fs_file)-1);
767   buf->time = time(NULL);               /* default case: take current time */
768   {
769     time_t jobtime;
770     struct tm *t;
771
772     t = localtime(&buf->time);
773     t->tm_mday = atoi(tok[count+2]+6);
774     t->tm_mon  = atoi(tok[count+2]+3);
775     switch (*tok[count+2])
776     {
777     case 7: case 8: case 9: t->tm_year = atoi(tok[count+2]); break;
778     default:                t->tm_year = atoi(tok[count+2]); break;
779     }
780
781     t->tm_hour = atoi(tok[count+3]);
782     t->tm_min = atoi(tok[count+4]);
783     t->tm_sec = atoi(tok[count+5]);
784     jobtime = mktime(t);
785     if (jobtime != (time_t)-1)
786       buf->time = jobtime; 
787   }
788
789   return(True);
790 }
791
792 /*******************************************************************
793 parse lpq on an NT system
794
795                          Windows 2000 LPD Server
796                               Printer \\10.0.0.2\NP17PCL (Paused)
797
798 Owner       Status         Jobname          Job-Id    Size   Pages  Priority
799 ----------------------------------------------------------------------------
800 root (9.99. Printing  /usr/lib/rhs/rhs-pr      3       625      0      1
801 root (9.99. Paused    /usr/lib/rhs/rhs-pr      4       625      0      1
802 jmcd        Waiting   Re: Samba Open Sour     26     32476      1      1
803
804 ********************************************************************/
805 static BOOL parse_lpq_nt(char *line,print_queue_struct *buf,BOOL first)
806 {
807 #define LPRNT_OWNSIZ 11
808 #define LPRNT_STATSIZ 9
809 #define LPRNT_JOBSIZ 19
810 #define LPRNT_IDSIZ 6
811 #define LPRNT_SIZSIZ 9
812   typedef struct 
813   {
814     char owner[LPRNT_OWNSIZ];
815     char space1;
816     char status[LPRNT_STATSIZ];
817     char space2;
818     char jobname[LPRNT_JOBSIZ];
819     char space3;
820     char jobid[LPRNT_IDSIZ];
821     char space4;
822     char size[LPRNT_SIZSIZ];
823     char terminator;
824   } nt_lpq_line;
825
826   nt_lpq_line parse_line;
827 #define LPRNT_PRINTING "Printing"
828 #define LPRNT_WAITING "Waiting"
829 #define LPRNT_PAUSED "Paused"
830
831   memset(&parse_line, '\0', sizeof(parse_line));
832   strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
833
834   if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
835     return(False);
836
837   /* Just want the first word in the owner field - the username */
838   if (strchr_m(parse_line.owner, ' '))
839     *(strchr_m(parse_line.owner, ' ')) = '\0';
840   else
841     parse_line.space1 = '\0';
842
843   /* Make sure we have an owner */
844   if (!strlen(parse_line.owner))
845     return(False);
846
847   /* Make sure the status is valid */
848   parse_line.space2 = '\0';
849   trim_string(parse_line.status, NULL, " ");
850   if (!strequal(parse_line.status, LPRNT_PRINTING) &&
851       !strequal(parse_line.status, LPRNT_PAUSED) &&
852       !strequal(parse_line.status, LPRNT_WAITING))
853     return(False);
854   
855   parse_line.space3 = '\0';
856   trim_string(parse_line.jobname, NULL, " ");
857
858   buf->job = atoi(parse_line.jobid);
859   buf->priority = 0;
860   buf->size = atoi(parse_line.size);
861   buf->time = time(NULL);
862   StrnCpy(buf->fs_user, parse_line.owner, sizeof(buf->fs_user)-1);
863   StrnCpy(buf->fs_file, parse_line.jobname, sizeof(buf->fs_file)-1);
864   if (strequal(parse_line.status, LPRNT_PRINTING))
865     buf->status = LPQ_PRINTING;
866   else if (strequal(parse_line.status, LPRNT_PAUSED))
867     buf->status = LPQ_PAUSED;
868   else
869     buf->status = LPQ_QUEUED;
870
871   return(True);
872 }
873
874 /*******************************************************************
875 parse lpq on an OS2 system
876
877 JobID  File Name          Rank      Size        Status          Comment       
878 -----  ---------------    ------    --------    ------------    ------------  
879     3  Control                 1          68    Queued          root@psflinu  
880     4  /etc/motd               2       11666    Queued          root@psflinu  
881
882 ********************************************************************/
883 static BOOL parse_lpq_os2(char *line,print_queue_struct *buf,BOOL first)
884 {
885 #define LPROS2_IDSIZ 5
886 #define LPROS2_JOBSIZ 15
887 #define LPROS2_SIZSIZ 8
888 #define LPROS2_STATSIZ 12
889 #define LPROS2_OWNSIZ 12
890   typedef struct 
891   {
892     char jobid[LPROS2_IDSIZ];
893     char space1[2];
894     char jobname[LPROS2_JOBSIZ];
895     char space2[14];
896     char size[LPROS2_SIZSIZ];
897     char space3[4];
898     char status[LPROS2_STATSIZ];
899     char space4[4];
900     char owner[LPROS2_OWNSIZ];
901     char terminator;
902   } os2_lpq_line;
903
904   os2_lpq_line parse_line;
905 #define LPROS2_PRINTING "Printing"
906 #define LPROS2_WAITING "Queued"
907 #define LPROS2_PAUSED "Paused"
908
909   memset(&parse_line, '\0', sizeof(parse_line));
910   strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
911
912   if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
913     return(False);
914
915   /* Get the jobid */
916   buf->job = atoi(parse_line.jobid);
917
918   /* Get the job name */
919   parse_line.space2[0] = '\0';
920   trim_string(parse_line.jobname, NULL, " ");
921   StrnCpy(buf->fs_file, parse_line.jobname, sizeof(buf->fs_file)-1);
922
923   buf->priority = 0;
924   buf->size = atoi(parse_line.size);
925   buf->time = time(NULL);
926
927   /* Make sure we have an owner */
928   if (!strlen(parse_line.owner))
929     return(False);
930
931   /* Make sure we have a valid status */
932   parse_line.space4[0] = '\0';
933   trim_string(parse_line.status, NULL, " ");
934   if (!strequal(parse_line.status, LPROS2_PRINTING) &&
935       !strequal(parse_line.status, LPROS2_PAUSED) &&
936       !strequal(parse_line.status, LPROS2_WAITING))
937     return(False);
938
939   StrnCpy(buf->fs_user, parse_line.owner, sizeof(buf->fs_user)-1);
940   if (strequal(parse_line.status, LPROS2_PRINTING))
941     buf->status = LPQ_PRINTING;
942   else if (strequal(parse_line.status, LPROS2_PAUSED))
943     buf->status = LPQ_PAUSED;
944   else
945     buf->status = LPQ_QUEUED;
946
947   return(True);
948 }
949
950 static char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
951 static char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
952 static char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
953
954 #ifdef DEVELOPER
955
956 /****************************************************************************
957 parse a vlp line
958 ****************************************************************************/
959 static BOOL parse_lpq_vlp(char *line,print_queue_struct *buf,BOOL first)
960 {
961         int toknum = 0;
962         fstring tok;
963         const char *cline = line;
964
965         /* First line is printer status */
966
967         if (!isdigit(line[0])) return False;
968
969         /* Parse a print job entry */
970
971         while(next_token(&cline, tok, NULL, sizeof(fstring))) {
972                 switch (toknum) {
973                 case 0:
974                         buf->job = atoi(tok);
975                         break;
976                 case 1:
977                         buf->size = atoi(tok);
978                         break;
979                 case 2:
980                         buf->status = atoi(tok);
981                         break;
982                 case 3:
983                         buf->time = atoi(tok);
984                         break;
985                 case 4:
986                         fstrcpy(buf->fs_user, tok);
987                         break;
988                 case 5:
989                         fstrcpy(buf->fs_file, tok);
990                         break;
991                 }
992                 toknum++;
993         }
994
995         return True;
996 }
997
998 #endif /* DEVELOPER */
999
1000 /****************************************************************************
1001 parse a lpq line. Choose printing style
1002 ****************************************************************************/
1003
1004 BOOL parse_lpq_entry(int snum,char *line,
1005                      print_queue_struct *buf,
1006                      print_status_struct *status,BOOL first)
1007 {
1008   BOOL ret;
1009
1010   switch (lp_printing(snum))
1011     {
1012     case PRINT_SYSV:
1013       ret = parse_lpq_sysv(line,buf,first);
1014       break;
1015     case PRINT_AIX:      
1016       ret = parse_lpq_aix(line,buf,first);
1017       break;
1018     case PRINT_HPUX:
1019       ret = parse_lpq_hpux(line,buf,first);
1020       break;
1021     case PRINT_QNX:
1022       ret = parse_lpq_qnx(line,buf,first);
1023       break;
1024     case PRINT_LPRNG:
1025       ret = parse_lpq_lprng(line,buf,first);
1026       break;
1027     case PRINT_PLP:
1028       ret = parse_lpq_plp(line,buf,first);
1029       break;
1030     case PRINT_SOFTQ:
1031       ret = parse_lpq_softq(line,buf,first);
1032       break;
1033     case PRINT_LPRNT:
1034       ret = parse_lpq_nt(line,buf,first);
1035       break;
1036     case PRINT_LPROS2:
1037       ret = parse_lpq_os2(line,buf,first);
1038       break;
1039 #ifdef DEVELOPER
1040     case PRINT_VLP:
1041     case PRINT_TEST:
1042             ret = parse_lpq_vlp(line,buf,first);
1043             break;
1044 #endif /* DEVELOPER */
1045     default:
1046       ret = parse_lpq_bsd(line,buf,first);
1047       break;
1048     }
1049
1050   /* We don't want the newline in the status message. */
1051   {
1052     char *p = strchr_m(line,'\n');
1053     if (p) *p = 0;
1054   }
1055
1056   /* in the LPRNG case, we skip lines starting by a space.*/
1057   if (line && !ret && (lp_printing(snum)==PRINT_LPRNG) )
1058   {
1059         if (line[0]==' ')
1060                 return ret;
1061   }
1062
1063
1064   if (status && !ret)
1065     {
1066       /* a few simple checks to see if the line might be a
1067          printer status line: 
1068          handle them so that most severe condition is shown */
1069       int i;
1070       strlower(line);
1071       
1072       switch (status->status) {
1073       case LPSTAT_OK:
1074         for (i=0; stat0_strings[i]; i++)
1075           if (strstr(line,stat0_strings[i])) {
1076             StrnCpy(status->message,line,sizeof(status->message)-1);
1077             status->status=LPSTAT_OK;
1078             return ret;
1079           }
1080       case LPSTAT_STOPPED:
1081         for (i=0; stat1_strings[i]; i++)
1082           if (strstr(line,stat1_strings[i])) {
1083             StrnCpy(status->message,line,sizeof(status->message)-1);
1084             status->status=LPSTAT_STOPPED;
1085             return ret;
1086           }
1087       case LPSTAT_ERROR:
1088         for (i=0; stat2_strings[i]; i++)
1089           if (strstr(line,stat2_strings[i])) {
1090             StrnCpy(status->message,line,sizeof(status->message)-1);
1091             status->status=LPSTAT_ERROR;
1092             return ret;
1093           }
1094         break;
1095       }
1096     }
1097
1098   return(ret);
1099 }