This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[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 const 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   fstrcpy(buf->fs_user,tok[USERTOK]);
149   fstrcpy(buf->fs_file,tok[FILETOK]);
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   fstrcpy(buf->fs_user,tokarr[LPRNG_USERTOK]);
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   fstrcpy(buf->fs_file,tokarr[LPRNG_FILETOK]);
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           fstrcpy(buf->fs_user,tok[3]);
357           fstrcpy(buf->fs_file,tok[2]);
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       fstrcpy(buf->fs_user,tok[5]);
391       fstrcpy(buf->fs_file,tok[4]);
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     fstrcpy(buf->fs_file,tok[0]);
453     
454     /* fill things from header line */
455     buf->time = jobtime;
456     buf->job = jobid;
457     buf->status = jobstat;
458     buf->priority = jobprio;
459     fstrcpy(buf->fs_user,jobuser);
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     fstrcpy(jobuser,tok[2]);
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   fstrcpy(buf->fs_user,tok[2]);
577   fstrcpy(buf->fs_file,tok[2]);
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 = line;
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   fstrcpy(buf->fs_user,tok[1]);
637   fstrcpy(buf->fs_file,tok[6]);
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   fstrcpy(buf->fs_user,tok[1]);
708   fstrcpy(buf->fs_file,tok[6]);
709   return(True);
710 }
711
712 /*******************************************************************
713 parse lpq on an NT system
714
715                          Windows 2000 LPD Server
716                               Printer \\10.0.0.2\NP17PCL (Paused)
717
718 Owner       Status         Jobname          Job-Id    Size   Pages  Priority
719 ----------------------------------------------------------------------------
720 root (9.99. Printing  /usr/lib/rhs/rhs-pr      3       625      0      1
721 root (9.99. Paused    /usr/lib/rhs/rhs-pr      4       625      0      1
722 jmcd        Waiting   Re: Samba Open Sour     26     32476      1      1
723
724 ********************************************************************/
725 static BOOL parse_lpq_nt(char *line,print_queue_struct *buf,BOOL first)
726 {
727 #define LPRNT_OWNSIZ 11
728 #define LPRNT_STATSIZ 9
729 #define LPRNT_JOBSIZ 19
730 #define LPRNT_IDSIZ 6
731 #define LPRNT_SIZSIZ 9
732   typedef struct 
733   {
734     char owner[LPRNT_OWNSIZ];
735     char space1;
736     char status[LPRNT_STATSIZ];
737     char space2;
738     char jobname[LPRNT_JOBSIZ];
739     char space3;
740     char jobid[LPRNT_IDSIZ];
741     char space4;
742     char size[LPRNT_SIZSIZ];
743     char terminator;
744   } nt_lpq_line;
745
746   nt_lpq_line parse_line;
747 #define LPRNT_PRINTING "Printing"
748 #define LPRNT_WAITING "Waiting"
749 #define LPRNT_PAUSED "Paused"
750
751   memset(&parse_line, '\0', sizeof(parse_line));
752   strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
753
754   if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
755     return(False);
756
757   /* Just want the first word in the owner field - the username */
758   if (strchr_m(parse_line.owner, ' '))
759     *(strchr_m(parse_line.owner, ' ')) = '\0';
760   else
761     parse_line.space1 = '\0';
762
763   /* Make sure we have an owner */
764   if (!strlen(parse_line.owner))
765     return(False);
766
767   /* Make sure the status is valid */
768   parse_line.space2 = '\0';
769   trim_string(parse_line.status, NULL, " ");
770   if (!strequal(parse_line.status, LPRNT_PRINTING) &&
771       !strequal(parse_line.status, LPRNT_PAUSED) &&
772       !strequal(parse_line.status, LPRNT_WAITING))
773     return(False);
774   
775   parse_line.space3 = '\0';
776   trim_string(parse_line.jobname, NULL, " ");
777
778   buf->job = atoi(parse_line.jobid);
779   buf->priority = 0;
780   buf->size = atoi(parse_line.size);
781   buf->time = time(NULL);
782   fstrcpy(buf->fs_user, parse_line.owner);
783   fstrcpy(buf->fs_file, parse_line.jobname);
784   if (strequal(parse_line.status, LPRNT_PRINTING))
785     buf->status = LPQ_PRINTING;
786   else if (strequal(parse_line.status, LPRNT_PAUSED))
787     buf->status = LPQ_PAUSED;
788   else
789     buf->status = LPQ_QUEUED;
790
791   return(True);
792 }
793
794 /*******************************************************************
795 parse lpq on an OS2 system
796
797 JobID  File Name          Rank      Size        Status          Comment       
798 -----  ---------------    ------    --------    ------------    ------------  
799     3  Control                 1          68    Queued          root@psflinu  
800     4  /etc/motd               2       11666    Queued          root@psflinu  
801
802 ********************************************************************/
803 static BOOL parse_lpq_os2(char *line,print_queue_struct *buf,BOOL first)
804 {
805 #define LPROS2_IDSIZ 5
806 #define LPROS2_JOBSIZ 15
807 #define LPROS2_SIZSIZ 8
808 #define LPROS2_STATSIZ 12
809 #define LPROS2_OWNSIZ 12
810   typedef struct 
811   {
812     char jobid[LPROS2_IDSIZ];
813     char space1[2];
814     char jobname[LPROS2_JOBSIZ];
815     char space2[14];
816     char size[LPROS2_SIZSIZ];
817     char space3[4];
818     char status[LPROS2_STATSIZ];
819     char space4[4];
820     char owner[LPROS2_OWNSIZ];
821     char terminator;
822   } os2_lpq_line;
823
824   os2_lpq_line parse_line;
825 #define LPROS2_PRINTING "Printing"
826 #define LPROS2_WAITING "Queued"
827 #define LPROS2_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   /* Get the jobid */
836   buf->job = atoi(parse_line.jobid);
837
838   /* Get the job name */
839   parse_line.space2[0] = '\0';
840   trim_string(parse_line.jobname, NULL, " ");
841   fstrcpy(buf->fs_file, parse_line.jobname);
842
843   buf->priority = 0;
844   buf->size = atoi(parse_line.size);
845   buf->time = time(NULL);
846
847   /* Make sure we have an owner */
848   if (!strlen(parse_line.owner))
849     return(False);
850
851   /* Make sure we have a valid status */
852   parse_line.space4[0] = '\0';
853   trim_string(parse_line.status, NULL, " ");
854   if (!strequal(parse_line.status, LPROS2_PRINTING) &&
855       !strequal(parse_line.status, LPROS2_PAUSED) &&
856       !strequal(parse_line.status, LPROS2_WAITING))
857     return(False);
858
859   fstrcpy(buf->fs_user, parse_line.owner);
860   if (strequal(parse_line.status, LPROS2_PRINTING))
861     buf->status = LPQ_PRINTING;
862   else if (strequal(parse_line.status, LPROS2_PAUSED))
863     buf->status = LPQ_PAUSED;
864   else
865     buf->status = LPQ_QUEUED;
866
867   return(True);
868 }
869
870 static const char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
871 static const char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
872 static const char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
873
874 #ifdef DEVELOPER
875
876 /****************************************************************************
877 parse a vlp line
878 ****************************************************************************/
879 static BOOL parse_lpq_vlp(char *line,print_queue_struct *buf,BOOL first)
880 {
881         int toknum = 0;
882         fstring tok;
883         const char *cline = line;
884
885         /* First line is printer status */
886
887         if (!isdigit(line[0])) return False;
888
889         /* Parse a print job entry */
890
891         while(next_token(&cline, tok, NULL, sizeof(fstring))) {
892                 switch (toknum) {
893                 case 0:
894                         buf->job = atoi(tok);
895                         break;
896                 case 1:
897                         buf->size = atoi(tok);
898                         break;
899                 case 2:
900                         buf->status = atoi(tok);
901                         break;
902                 case 3:
903                         buf->time = atoi(tok);
904                         break;
905                 case 4:
906                         fstrcpy(buf->fs_user, tok);
907                         break;
908                 case 5:
909                         fstrcpy(buf->fs_file, tok);
910                         break;
911                 }
912                 toknum++;
913         }
914
915         return True;
916 }
917
918 #endif /* DEVELOPER */
919
920 /****************************************************************************
921 parse a lpq line. Choose printing style
922 ****************************************************************************/
923
924 BOOL parse_lpq_entry(int snum,char *line,
925                      print_queue_struct *buf,
926                      print_status_struct *status,BOOL first)
927 {
928   BOOL ret;
929
930   switch (lp_printing(snum))
931     {
932     case PRINT_SYSV:
933       ret = parse_lpq_sysv(line,buf,first);
934       break;
935     case PRINT_AIX:      
936       ret = parse_lpq_aix(line,buf,first);
937       break;
938     case PRINT_HPUX:
939       ret = parse_lpq_hpux(line,buf,first);
940       break;
941     case PRINT_QNX:
942       ret = parse_lpq_qnx(line,buf,first);
943       break;
944     case PRINT_LPRNG:
945       ret = parse_lpq_lprng(line,buf,first);
946       break;
947     case PRINT_PLP:
948       ret = parse_lpq_plp(line,buf,first);
949       break;
950     case PRINT_LPRNT:
951       ret = parse_lpq_nt(line,buf,first);
952       break;
953     case PRINT_LPROS2:
954       ret = parse_lpq_os2(line,buf,first);
955       break;
956 #ifdef DEVELOPER
957     case PRINT_VLP:
958     case PRINT_TEST:
959             ret = parse_lpq_vlp(line,buf,first);
960             break;
961 #endif /* DEVELOPER */
962     default:
963       ret = parse_lpq_bsd(line,buf,first);
964       break;
965     }
966
967   /* We don't want the newline in the status message. */
968   {
969     char *p = strchr_m(line,'\n');
970     if (p) *p = 0;
971   }
972
973   /* in the LPRNG case, we skip lines starting by a space.*/
974   if (line && !ret && (lp_printing(snum)==PRINT_LPRNG) )
975   {
976         if (line[0]==' ')
977                 return ret;
978   }
979
980
981   if (status && !ret)
982     {
983       /* a few simple checks to see if the line might be a
984          printer status line: 
985          handle them so that most severe condition is shown */
986       int i;
987       strlower(line);
988       
989       switch (status->status) {
990       case LPSTAT_OK:
991         for (i=0; stat0_strings[i]; i++)
992           if (strstr(line,stat0_strings[i])) {
993                   fstrcpy(status->message,line);
994                   status->status=LPSTAT_OK;
995                   return ret;
996           }
997       case LPSTAT_STOPPED:
998         for (i=0; stat1_strings[i]; i++)
999           if (strstr(line,stat1_strings[i])) {
1000                   fstrcpy(status->message,line);
1001                   status->status=LPSTAT_STOPPED;
1002                   return ret;
1003           }
1004       case LPSTAT_ERROR:
1005         for (i=0; stat2_strings[i]; i++)
1006           if (strstr(line,stat2_strings[i])) {
1007                   fstrcpy(status->message,line);
1008                   status->status=LPSTAT_ERROR;
1009                   return ret;
1010           }
1011         break;
1012       }
1013     }
1014
1015   return(ret);
1016 }