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