This commit was generated by cvs2svn to compensate for changes in r4,
[samba.git] / source3 / printing / printing.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    printing routines
5    Copyright (C) Andrew Tridgell 1992-1995
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 #include "loadparm.h"
24 extern int DEBUGLEVEL;
25 extern connection_struct Connections[];
26 extern files_struct Files[];
27
28 static BOOL * lpq_cache_reset=NULL;
29
30 static int check_lpq_cache(int snum) {
31   static int lpq_caches=0;
32   
33   if (lpq_caches <= snum) {
34       BOOL * p;
35       p = (BOOL *) Realloc(lpq_cache_reset,(snum+1)*sizeof(BOOL));
36       if (p) {
37          lpq_cache_reset=p;
38          lpq_caches = snum+1;
39       }
40   }
41   return lpq_caches;
42 }
43
44 void lpq_reset(int snum)
45 {
46   if (check_lpq_cache(snum) > snum) lpq_cache_reset[snum]=True;
47 }
48
49
50 /****************************************************************************
51 Build the print command in the supplied buffer. This means getting the
52 print command for the service and inserting the printer name and the
53 print file name. Return NULL on error, else the passed buffer pointer.
54 ****************************************************************************/
55 static char *build_print_command(int cnum, char *command, char *syscmd, char *filename1)
56 {
57   int snum = SNUM(cnum);
58   char *tstr;
59   pstring filename;
60   
61   /* get the print command for the service. */
62   tstr = command;
63   if (!syscmd || !tstr) {
64     DEBUG(0,("No print command for service `%s'\n", SERVICE(snum)));
65     return (NULL);
66   }
67
68   /* copy the command into the buffer for extensive meddling. */
69   StrnCpy(syscmd, tstr, sizeof(pstring) - 1);
70   
71   /* look for "%s" in the string. If there is no %s, we cannot print. */   
72   if (!strstr(syscmd, "%s") && !strstr(syscmd, "%f")) {
73     DEBUG(2,("WARNING! No placeholder for the filename in the print command for service %s!\n", SERVICE(snum)));
74   }
75   
76   if (strstr(syscmd,"%s")) {
77     int iOffset = strstr(syscmd, "%s") - syscmd;
78     
79     /* construct the full path for the filename, shouldn't be necessary unless
80        the subshell causes a "cd" to be executed.
81        Only use the full path if there isn't a / preceding the %s */
82     if (iOffset==0 || syscmd[iOffset-1] != '/') {
83       StrnCpy(filename,Connections[cnum].connectpath,sizeof(filename)-1);
84       trim_string(filename,"","/");
85       strcat(filename,"/");
86       strcat(filename,filename1);
87     }
88     else
89       strcpy(filename,filename1);
90     
91     string_sub(syscmd, "%s", filename);
92   }
93   
94   string_sub(syscmd, "%f", filename1);
95   
96   /* Does the service have a printername? If not, make a fake and empty    */
97   /* printer name. That way a %p is treated sanely if no printer */
98   /* name was specified to replace it. This eventuality is logged.         */
99   tstr = PRINTERNAME(snum);
100   if (tstr == NULL || tstr[0] == '\0') {
101     DEBUG(3,( "No printer name - using %s.\n", SERVICE(snum)));
102     tstr = SERVICE(snum);
103   }
104   
105   string_sub(syscmd, "%p", tstr);
106   
107   standard_sub(cnum,syscmd);
108   
109   return (syscmd);
110 }
111
112
113 /****************************************************************************
114 print a file - called on closing the file
115 ****************************************************************************/
116 void print_file(int fnum)
117 {
118   pstring syscmd;
119   int cnum = Files[fnum].cnum;
120   int snum=SNUM(cnum);
121   char *tempstr;
122
123   *syscmd = 0;
124
125   if (file_size(Files[fnum].name) <= 0) {
126     DEBUG(3,("Discarding null print job %s\n",Files[fnum].name));
127     sys_unlink(Files[fnum].name);
128     return;
129   }
130
131   tempstr = build_print_command(cnum, PRINTCOMMAND(snum), syscmd, Files[fnum].name);
132   if (tempstr != NULL)
133     {
134       int ret = smbrun(syscmd,NULL);
135       DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
136     }
137   else
138     DEBUG(0,("Null print command?\n"));
139   
140   lpq_reset(snum);
141 }
142
143 static char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
144                               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
145
146
147 /*******************************************************************
148 process time fields
149 ********************************************************************/
150 static time_t EntryTime(string tok[], int ptr, int count, int minimum)
151 {
152   time_t jobtime;
153
154   jobtime = time(NULL);         /* default case: take current time */
155   if (count >= minimum) {
156     struct tm *t;
157     int i, day, hour, min, sec;
158     char   *c;
159
160     for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */
161     if (i<12) {
162       t = localtime(&jobtime);
163       day = atoi(tok[ptr+1]);
164       c=(char *)(tok[ptr+2]);
165       *(c+2)=0;
166       hour = atoi(c);
167       *(c+5)=0;
168       min = atoi(c+3);
169       if(*(c+6) != 0)sec = atoi(c+6);
170       else  sec=0;
171
172       if ((t->tm_mon < i)||
173           ((t->tm_mon == i)&&
174            ((t->tm_mday < day)||
175             ((t->tm_mday == day)&&
176              (t->tm_hour*60+t->tm_min < hour*60+min)))))
177         t->tm_year--;           /* last year's print job */
178
179       t->tm_mon = i;
180       t->tm_mday = day;
181       t->tm_hour = hour;
182       t->tm_min = min;
183       t->tm_sec = sec;
184       jobtime = mktime(t);
185     }
186   }
187   return jobtime;
188 }
189
190
191 /****************************************************************************
192 parse a lpq line
193
194 here is an example of lpq output under bsd
195
196 Warning: no daemon present
197 Rank   Owner      Job  Files                                 Total Size
198 1st    tridge     148  README                                8096 bytes
199
200 here is an example of lpq output under osf/1
201
202 Warning: no daemon present
203 Rank   Pri Owner      Job  Files                             Total Size
204 1st    0   tridge     148  README                            8096 bytes
205 ****************************************************************************/
206 static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first)
207 {
208 #ifdef  OSF1
209 #define RANKTOK 0
210 #define PRIOTOK 1
211 #define USERTOK 2
212 #define JOBTOK  3
213 #define FILETOK 4
214 #define TOTALTOK 5
215 #define NTOK    6
216 #else   /* OSF1 */
217 #define RANKTOK 0
218 #define USERTOK 1
219 #define JOBTOK  2
220 #define FILETOK 3
221 #define TOTALTOK 4
222 #define NTOK    5
223 #endif  /* OSF1 */
224
225   string tok[NTOK];
226   int count=0;
227
228 #ifdef  OSF1
229   int length;
230   length = strlen(line);
231   if (line[length-3] == ':')
232         return(False);
233 #endif  /* OSF1 */
234
235   /* handle the case of "(standard input)" as a filename */
236   string_sub(line,"standard input","STDIN");
237   string_sub(line,"(","\"");
238   string_sub(line,")","\"");
239   
240   for (count=0; count<NTOK && next_token(&line,tok[count],NULL); count++) ;
241
242   /* we must get NTOK tokens */
243   if (count < NTOK)
244     return(False);
245
246   /* the Job and Total columns must be integer */
247   if (!isdigit(*tok[JOBTOK]) || !isdigit(*tok[TOTALTOK])) return(False);
248
249   /* if the fname contains a space then use STDIN */
250   if (strchr(tok[FILETOK],' '))
251     strcpy(tok[FILETOK],"STDIN");
252
253   /* only take the last part of the filename */
254   {
255     string tmp;
256     char *p = strrchr(tok[FILETOK],'/');
257     if (p)
258       {
259         strcpy(tmp,p+1);
260         strcpy(tok[FILETOK],tmp);
261       }
262   }
263         
264
265   buf->job = atoi(tok[JOBTOK]);
266   buf->size = atoi(tok[TOTALTOK]);
267   buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED;
268   buf->time = time(NULL);
269   StrnCpy(buf->user,tok[USERTOK],sizeof(buf->user)-1);
270   StrnCpy(buf->file,tok[FILETOK],sizeof(buf->file)-1);
271 #ifdef PRIOTOK
272   buf->priority = atoi(tok[PRIOTOK]);
273 #else
274   buf->priority = 1;
275 #endif
276   return(True);
277 }
278
279
280
281 /*******************************************************************
282 parse lpq on an aix system
283
284 Queue   Dev   Status    Job Files              User         PP %   Blks  Cp Rnk
285 ------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
286 lazer   lazer READY
287 lazer   lazer RUNNING   537 6297doc.A          kvintus@IE    0 10  2445   1   1
288               QUEUED    538 C.ps               root@IEDVB           124   1   2
289               QUEUED    539 E.ps               root@IEDVB            28   1   3
290               QUEUED    540 L.ps               root@IEDVB           172   1   4
291               QUEUED    541 P.ps               root@IEDVB            22   1   5
292 ********************************************************************/
293 static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
294 {
295   string tok[11];
296   int count=0;
297
298   /* handle the case of "(standard input)" as a filename */
299   string_sub(line,"standard input","STDIN");
300   string_sub(line,"(","\"");
301   string_sub(line,")","\"");
302
303   for (count=0; count<10 && next_token(&line,tok[count],NULL); count++) ;
304
305   /* we must get 6 tokens */
306   if (count < 10)
307   {
308       if ((count == 7) && (strcmp(tok[0],"QUEUED") == 0))
309       {
310           /* the 2nd and 5th columns must be integer */
311           if (!isdigit(*tok[1]) || !isdigit(*tok[4])) return(False);
312           buf->size = atoi(tok[4]) * 1024;
313           /* if the fname contains a space then use STDIN */
314           if (strchr(tok[2],' '))
315             strcpy(tok[2],"STDIN");
316
317           /* only take the last part of the filename */
318           {
319             string tmp;
320             char *p = strrchr(tok[2],'/');
321             if (p)
322               {
323                 strcpy(tmp,p+1);
324                 strcpy(tok[2],tmp);
325               }
326           }
327
328
329           buf->job = atoi(tok[1]);
330           buf->status = LPQ_QUEUED;
331           buf->priority = 0;
332           buf->time = time(NULL);
333           StrnCpy(buf->user,tok[3],sizeof(buf->user)-1);
334           StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
335       }
336       else
337       {
338           DEBUG(6,("parse_lpq_aix count=%d\n", count));
339           return(False);
340       }
341   }
342   else
343   {
344       /* the 4th and 9th columns must be integer */
345       if (!isdigit(*tok[3]) || !isdigit(*tok[8])) return(False);
346       buf->size = atoi(tok[8]) * 1024;
347       /* if the fname contains a space then use STDIN */
348       if (strchr(tok[4],' '))
349         strcpy(tok[4],"STDIN");
350
351       /* only take the last part of the filename */
352       {
353         string tmp;
354         char *p = strrchr(tok[4],'/');
355         if (p)
356           {
357             strcpy(tmp,p+1);
358             strcpy(tok[4],tmp);
359           }
360       }
361
362
363       buf->job = atoi(tok[3]);
364       buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
365       buf->priority = 0;
366       buf->time = time(NULL);
367       StrnCpy(buf->user,tok[5],sizeof(buf->user)-1);
368       StrnCpy(buf->file,tok[4],sizeof(buf->file)-1);
369   }
370
371
372   return(True);
373 }
374
375
376 /****************************************************************************
377 parse a lpq line
378 here is an example of lpq output under hpux; note there's no space after -o !
379 $> lpstat -oljplus
380 ljplus-2153         user           priority 0  Jan 19 08:14 on ljplus
381       util.c                                  125697 bytes
382       server.c                                110712 bytes
383 ljplus-2154         user           priority 0  Jan 19 08:14 from client
384       (standard input)                          7551 bytes
385 ****************************************************************************/
386 static BOOL parse_lpq_hpux(char * line, print_queue_struct *buf, BOOL first)
387 {
388   /* must read two lines to process, therefore keep some values static */
389   static BOOL header_line_ok=False, base_prio_reset=False;
390   static string jobuser;
391   static int jobid;
392   static int jobprio;
393   static time_t jobtime;
394   static int jobstat=LPQ_QUEUED;
395   /* to store minimum priority to print, lpstat command should be invoked
396      with -p option first, to work */
397   static int base_prio;
398  
399   int count;
400   char TAB = '\011';  
401   string tok[12];
402
403   /* If a line begins with a horizontal TAB, it is a subline type */
404   
405   if (line[0] == TAB) { /* subline */
406     /* check if it contains the base priority */
407     if (!strncmp(line,"\tfence priority : ",18)) {
408        base_prio=atoi(&line[18]);
409        DEBUG(4, ("fence priority set at %d\n", base_prio));
410     }
411     if (!header_line_ok) return (False); /* incorrect header line */
412     /* handle the case of "(standard input)" as a filename */
413     string_sub(line,"standard input","STDIN");
414     string_sub(line,"(","\"");
415     string_sub(line,")","\"");
416     
417     for (count=0; count<2 && next_token(&line,tok[count],NULL); count++) ;
418     /* we must get 2 tokens */
419     if (count < 2) return(False);
420     
421     /* the 2nd column must be integer */
422     if (!isdigit(*tok[1])) return(False);
423     
424     /* if the fname contains a space then use STDIN */
425     if (strchr(tok[0],' '))
426       strcpy(tok[0],"STDIN");
427     
428     buf->size = atoi(tok[1]);
429     StrnCpy(buf->file,tok[0],sizeof(buf->file)-1);
430     
431     /* fill things from header line */
432     buf->time = jobtime;
433     buf->job = jobid;
434     buf->status = jobstat;
435     buf->priority = jobprio;
436     StrnCpy(buf->user,jobuser,sizeof(buf->user)-1);
437     
438     return(True);
439   }
440   else { /* header line */
441     header_line_ok=False; /* reset it */
442     if (first) {
443        if (!base_prio_reset) {
444           base_prio=0; /* reset it */
445           base_prio_reset=True;
446        }
447     }
448     else if (base_prio) base_prio_reset=False;
449     
450     /* handle the dash in the job id */
451     string_sub(line,"-"," ");
452     
453     for (count=0; count<12 && next_token(&line,tok[count],NULL); count++) ;
454       
455     /* we must get 8 tokens */
456     if (count < 8) return(False);
457     
458     /* first token must be printer name (cannot check ?) */
459     /* the 2nd, 5th & 7th column must be integer */
460     if (!isdigit(*tok[1]) || !isdigit(*tok[4]) || !isdigit(*tok[6])) return(False);
461     jobid = atoi(tok[1]);
462     StrnCpy(jobuser,tok[2],sizeof(buf->user)-1);
463     jobprio = atoi(tok[4]);
464     
465     /* process time */
466     jobtime=EntryTime(tok, 5, count, 8);
467     if (jobprio < base_prio) {
468        jobstat = LPQ_PAUSED;
469        DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat));
470     }
471     else {
472        jobstat = LPQ_QUEUED;
473        if ((count >8) && (((strequal(tok[8],"on")) ||
474                            ((strequal(tok[8],"from")) && 
475                             ((count > 10)&&(strequal(tok[10],"on")))))))
476          jobstat = LPQ_PRINTING;
477     }
478     
479     header_line_ok=True; /* information is correct */
480     return(False); /* need subline info to include into queuelist */
481   }
482 }
483
484
485 /****************************************************************************
486 parse a lpq line
487
488 here is an example of "lpstat -o dcslw" output under sysv
489
490 dcslw-896               tridge            4712   Dec 20 10:30:30 on dcslw
491 dcslw-897               tridge            4712   Dec 20 10:30:30 being held
492
493 ****************************************************************************/
494 static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first)
495 {
496   string tok[9];
497   int count=0;
498   char *p;
499
500   /* handle the dash in the job id */
501   string_sub(line,"-"," ");
502   
503   for (count=0; count<9 && next_token(&line,tok[count],NULL); count++) ;
504
505   /* we must get 7 tokens */
506   if (count < 7)
507     return(False);
508
509   /* the 2nd and 4th, 6th columns must be integer */
510   if (!isdigit(*tok[1]) || !isdigit(*tok[3])) return(False);
511   if (!isdigit(*tok[5])) return(False);
512
513   /* if the user contains a ! then trim the first part of it */  
514   if ((p=strchr(tok[2],'!')))
515     {
516       string tmp;
517       strcpy(tmp,p+1);
518       strcpy(tok[2],tmp);
519     }
520     
521
522   buf->job = atoi(tok[1]);
523   buf->size = atoi(tok[3]);
524   if (count > 7 && strequal(tok[7],"on"))
525     buf->status = LPQ_PRINTING;
526   else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
527     buf->status = LPQ_PAUSED;
528   else
529     buf->status = LPQ_QUEUED;
530   buf->priority = 0;
531   buf->time = EntryTime(tok, 4, count, 7);
532   StrnCpy(buf->user,tok[2],sizeof(buf->user)-1);
533   StrnCpy(buf->file,tok[2],sizeof(buf->file)-1);
534   return(True);
535 }
536
537 /****************************************************************************
538 parse a lpq line
539
540 here is an example of lpq output under qnx
541 Spooler: /qnx/spooler, on node 1
542 Printer: txt        (ready) 
543 0000:     root  [job #1    ]   active 1146 bytes        /etc/profile
544 0001:     root  [job #2    ]    ready 2378 bytes        /etc/install
545 0002:     root  [job #3    ]    ready 1146 bytes        -- standard input --
546 ****************************************************************************/
547 static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
548 {
549   string tok[7];
550   int count=0;
551
552   DEBUG(0,("antes [%s]\n", line));
553
554   /* handle the case of "-- standard input --" as a filename */
555   string_sub(line,"standard input","STDIN");
556   DEBUG(0,("despues [%s]\n", line));
557   string_sub(line,"-- ","\"");
558   string_sub(line," --","\"");
559   DEBUG(0,("despues 1 [%s]\n", line));
560
561   string_sub(line,"[job #","");
562   string_sub(line,"]","");
563   DEBUG(0,("despues 2 [%s]\n", line));
564
565   
566   
567   for (count=0; count<7 && next_token(&line,tok[count],NULL); count++) ;
568
569   /* we must get 7 tokens */
570   if (count < 7)
571     return(False);
572
573   /* the 3rd and 5th columns must be integer */
574   if (!isdigit(*tok[2]) || !isdigit(*tok[4])) return(False);
575
576   /* only take the last part of the filename */
577   {
578     string tmp;
579     char *p = strrchr(tok[6],'/');
580     if (p)
581       {
582         strcpy(tmp,p+1);
583         strcpy(tok[6],tmp);
584       }
585   }
586         
587
588   buf->job = atoi(tok[2]);
589   buf->size = atoi(tok[4]);
590   buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED;
591   buf->priority = 0;
592   buf->time = time(NULL);
593   StrnCpy(buf->user,tok[1],sizeof(buf->user)-1);
594   StrnCpy(buf->file,tok[6],sizeof(buf->file)-1);
595   return(True);
596 }
597
598
599
600 char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
601 char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
602 char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
603
604 /****************************************************************************
605 parse a lpq line. Choose printing style
606 ****************************************************************************/
607 static BOOL parse_lpq_entry(int snum,char *line,
608                             print_queue_struct *buf,
609                             print_status_struct *status,BOOL first)
610 {
611   BOOL ret;
612
613   switch (lp_printing())
614     {
615     case PRINT_SYSV:
616       ret = parse_lpq_sysv(line,buf,first);
617       break;
618     case PRINT_AIX:      
619       ret = parse_lpq_aix(line,buf,first);
620       break;
621     case PRINT_HPUX:
622       ret = parse_lpq_hpux(line,buf,first);
623       break;
624     case PRINT_QNX:
625       ret = parse_lpq_qnx(line,buf,first);
626       break;
627     default:
628       ret = parse_lpq_bsd(line,buf,first);
629       break;
630     }
631
632 #ifdef LPQ_GUEST_TO_USER
633   if (ret) {
634     extern pstring sesssetup_user;
635     /* change guest entries to the current logged in user to make
636        them appear deletable to windows */
637     if (sesssetup_user[0] && strequal(buf->user,lp_guestaccount(snum)))
638       strcpy(buf->user,sesssetup_user);
639   }
640 #endif
641
642   if (status && !ret)
643     {
644       /* a few simple checks to see if the line might be a
645          printer status line: 
646          handle them so that most severe condition is shown */
647       int i;
648       strlower(line);
649       
650       switch (status->status) {
651       case LPSTAT_OK:
652         for (i=0; stat0_strings[i]; i++)
653           if (strstr(line,stat0_strings[i])) {
654             StrnCpy(status->message,line,sizeof(status->message)-1);
655             status->status=LPSTAT_OK;
656           }
657       case LPSTAT_STOPPED:
658         for (i=0; stat1_strings[i]; i++)
659           if (strstr(line,stat1_strings[i])) {
660             StrnCpy(status->message,line,sizeof(status->message)-1);
661             status->status=LPSTAT_STOPPED;
662           }
663       case LPSTAT_ERROR:
664         for (i=0; stat2_strings[i]; i++)
665           if (strstr(line,stat2_strings[i])) {
666             StrnCpy(status->message,line,sizeof(status->message)-1);
667             status->status=LPSTAT_ERROR;
668           }
669         break;
670       }
671     }
672
673   return(ret);
674 }
675
676 /****************************************************************************
677 get a printer queue
678 ****************************************************************************/
679 int get_printqueue(int snum,int cnum,print_queue_struct **queue,
680                    print_status_struct *status)
681 {
682   char *lpq_command = lp_lpqcommand(snum);
683   char *printername = PRINTERNAME(snum);
684   int ret=0,count=0;
685   pstring syscmd;
686   fstring outfile;
687   pstring line;
688   FILE *f;
689   struct stat sbuf;
690   BOOL dorun=True;
691   int cachetime = lp_lpqcachetime();
692   int lfd = -1;
693
694   *line = 0;
695   check_lpq_cache(snum);
696   
697   if (!printername || !*printername)
698     {
699       DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
700             lp_servicename(snum),snum));
701       printername = lp_servicename(snum);
702     }
703     
704   if (!lpq_command || !(*lpq_command))
705     {
706       DEBUG(5,("No lpq command\n"));
707       return(0);
708     }
709     
710   strcpy(syscmd,lpq_command);
711   string_sub(syscmd,"%p",printername);
712
713   standard_sub(cnum,syscmd);
714
715   sprintf(outfile,"/tmp/lpq.%08x",str_checksum(syscmd));
716   
717   if (!lpq_cache_reset[snum] && cachetime && !stat(outfile,&sbuf)) 
718     {
719       if (time(NULL) - sbuf.st_mtime < cachetime) {
720         DEBUG(3,("Using cached lpq output\n"));
721         dorun = False;
722       }
723
724       if (dorun) {
725         lfd = file_lock(outfile,LPQ_LOCK_TIMEOUT);
726         if (lfd<0 || 
727             (!fstat(lfd,&sbuf) && (time(NULL) - sbuf.st_mtime)<cachetime)) {
728           DEBUG(3,("Using cached lpq output\n"));
729           dorun = False;
730           file_unlock(lfd); lfd = -1;
731         }
732       }
733     }
734
735   if (dorun) {
736     ret = smbrun(syscmd,outfile);
737     DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
738   }
739
740   lpq_cache_reset[snum] = False;
741
742   f = fopen(outfile,"r");
743   if (!f) {
744     if (lfd >= 0) file_unlock(lfd);
745     return(0);
746   }
747
748   if (status) {
749     strcpy(status->message,"");
750     status->status = LPSTAT_OK;
751   }
752       
753   while (fgets(line,sizeof(pstring),f))
754     {
755       DEBUG(6,("QUEUE2: %s\n",line));
756
757       *queue = Realloc(*queue,sizeof(print_queue_struct)*(count+1));
758       if (! *queue)
759         {
760           count = 0;
761           break;
762         }
763
764       bzero((char *)&(*queue)[count],sizeof(**queue));
765           
766       /* parse it */
767       if (!parse_lpq_entry(snum,line,&(*queue)[count],status,count==0))
768         continue;
769           
770       count++;
771     }         
772
773   fclose(f);
774
775   if (lfd >= 0) file_unlock(lfd);
776
777   if (!cachetime) 
778     unlink(outfile);
779   else
780     chmod(outfile,0666);
781   return(count);
782 }
783
784
785 /****************************************************************************
786 delete a printer queue entry
787 ****************************************************************************/
788 void del_printqueue(int cnum,int snum,int jobid)
789 {
790   char *lprm_command = lp_lprmcommand(snum);
791   char *printername = PRINTERNAME(snum);
792   pstring syscmd;
793   char jobstr[20];
794   int ret;
795
796   if (!printername || !*printername)
797     {
798       DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
799             lp_servicename(snum),snum));
800       printername = lp_servicename(snum);
801     }
802     
803   if (!lprm_command || !(*lprm_command))
804     {
805       DEBUG(5,("No lprm command\n"));
806       return;
807     }
808     
809   sprintf(jobstr,"%d",jobid);
810
811   strcpy(syscmd,lprm_command);
812   string_sub(syscmd,"%p",printername);
813   string_sub(syscmd,"%j",jobstr);
814   standard_sub(cnum,syscmd);
815
816   ret = smbrun(syscmd,NULL);
817   DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));  
818   lpq_reset(snum); /* queue has changed */
819 }
820
821 /****************************************************************************
822 change status of a printer queue entry
823 ****************************************************************************/
824 void status_printjob(int cnum,int snum,int jobid,int status)
825 {
826   char *lpstatus_command = 
827     (status==LPQ_PAUSED?lp_lppausecommand(snum):lp_lpresumecommand(snum));
828   char *printername = PRINTERNAME(snum);
829   pstring syscmd;
830   char jobstr[20];
831   int ret;
832
833   if (!printername || !*printername)
834     {
835       DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n",
836             lp_servicename(snum),snum));
837       printername = lp_servicename(snum);
838     }
839     
840   if (!lpstatus_command || !(*lpstatus_command))
841     {
842       DEBUG(5,("No lpstatus command to %s job\n",
843                (status==LPQ_PAUSED?"pause":"resume")));
844       return;
845     }
846     
847   sprintf(jobstr,"%d",jobid);
848
849   strcpy(syscmd,lpstatus_command);
850   string_sub(syscmd,"%p",printername);
851   string_sub(syscmd,"%j",jobstr);
852   standard_sub(cnum,syscmd);
853
854   ret = smbrun(syscmd,NULL);
855   DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));  
856   lpq_reset(snum); /* queue has changed */
857 }
858
859