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