printing.c: Bug fix for lpng reporting.
[samba.git] / source3 / printing / pcap.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    printcap parsing
5    Copyright (C) Karl Auer 1993-1997
6
7    Re-working by Martin Kiff, 1994
8    
9    Re-written again by Andrew Tridgell
10
11    Modified for SVID support by Norm Jacobs, 1997
12    
13    This program is free software; you can redistribute it and/or modify
14    it under the terms of the GNU General Public License as published by
15    the Free Software Foundation; either version 2 of the License, or
16    (at your option) any later version.
17    
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21    GNU General Public License for more details.
22    
23    You should have received a copy of the GNU General Public License
24    along with this program; if not, write to the Free Software
25    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 */
27
28 /*
29  *  Parse printcap file.
30  *
31  *  This module does exactly one thing - it looks into the printcap file
32  *  and tells callers if a specified string appears as a printer name.
33  *
34  *  The way this module looks at the printcap file is very simplistic.
35  *  Only the local printcap file is inspected (no searching of NIS
36  *  databases etc).
37  *
38  *  There are assumed to be one or more printer names per record, held
39  *  as a set of sub-fields separated by vertical bar symbols ('|') in the
40  *  first field of the record. The field separator is assumed to be a colon
41  *  ':' and the record separator a newline.
42  * 
43  *  Lines ending with a backspace '\' are assumed to flag that the following
44  *  line is a continuation line so that a set of lines can be read as one
45  *  printcap entry.
46  *
47  *  A line stating with a hash '#' is assumed to be a comment and is ignored
48  *  Comments are discarded before the record is strung together from the
49  *  set of continuation lines.
50  *
51  *  Opening a pipe for "lpc status" and reading that would probably 
52  *  be pretty effective. Code to do this already exists in the freely
53  *  distributable PCNFS server code.
54  *
55  *  Modified to call SVID/XPG4 support if printcap name is set to "lpstat"
56  *  in smb.conf under Solaris.
57  */
58
59 #include "includes.h"
60
61 #include "smb.h"
62
63 extern int DEBUGLEVEL;
64
65 #ifdef AIX
66 /*  ******************************************
67      Extend for AIX system and qconfig file
68        from 'boulard@univ-rennes1.fr
69     ****************************************** */
70 static int strlocate(char *xpLine,char *xpS)
71 {
72         int iS,iL,i,iRet;
73         char *p;
74         iS = strlen(xpS);
75         iL = strlen(xpLine);
76
77         iRet = 0;
78         p = xpLine;
79         while (iL >= iS)
80         {
81                 if (strncmp(p,xpS,iS) == 0) {iRet =1;break;};
82                 p++;
83                 iL--;
84         }
85         /*DEBUG(3,(" strlocate %s in line '%s',ret=%d\n",xpS,xpLine,iRet));*/
86         
87         return(iRet);
88 }
89         
90         
91 /* ******************************************************************* */
92 /* *    Scan qconfig and search all virtual printer (device printer) * */
93 /* ******************************************************************* */
94 static void ScanQconfig_fn(char *psz,void (*fn)())
95 {
96         int iLg,iEtat;
97         FILE *pfile;
98         char *line,*p;
99         pstring name,comment;
100         line  = NULL;
101         *name = 0;
102         *comment = 0;
103
104         if ((pfile = fopen(psz, "r")) == NULL)
105         {
106               DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
107               return;
108         }
109
110         iEtat = 0;
111         /* scan qconfig file for searching <printername>:       */
112         for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
113         {
114                 if (*line == '*' || *line == 0)
115                 continue;
116                 switch (iEtat)
117                 {
118                         case 0: /* locate an entry */
119                          if (*line == '\t' || *line == ' ') continue;
120                          if ((p=strchr(line,':')))
121                          {
122                                 *p = '\0';
123                                 p = strtok(line,":");
124                                 if (strcmp(p,"bsh")!=0)
125                                   {
126                                     strcpy(name,p);
127                                     iEtat = 1;
128                                     continue;
129                                   }
130                          }
131                          break;
132                         case 1: /* scanning device stanza */
133                          if (*line == '*' || *line == 0) continue;
134                          if (*line != '\t' && *line != ' ')
135                          {
136                            /* name is found without stanza device  */
137                            /* probably a good printer ???               */
138                            fn(name,comment);
139                            iEtat = 0;
140                            continue;
141                           }
142                         
143                           if (strlocate(line,"backend"))
144                           {
145                                 /* it's a device, not a virtual printer*/
146                                 iEtat = 0;
147                           }
148                           else if (strlocate(line,"device"))
149                           {
150                                 /* it's a good virtual printer */
151                                 fn(name,comment);
152                                 iEtat = 0;
153                                 continue;
154                           }
155                           break;
156                 }
157         }
158         fclose(pfile);
159 }
160
161 /* Scan qconfig file and locate de printername */
162
163 static BOOL ScanQconfig(char *psz,char *pszPrintername)
164 {
165         int iLg,iEtat;
166         FILE *pfile;
167         char *pName;
168         char *line;
169
170         pName = NULL;
171         line  = NULL;
172         if ((pszPrintername!= NULL) && ((iLg = strlen(pszPrintername)) > 0))
173          pName = malloc(iLg+10);
174         if (pName == NULL)
175         {
176                 DEBUG(0,(" Unable to allocate memory for printer %s\n",pszPrintername));
177                 return(False);
178         }
179         if ((pfile = fopen(psz, "r")) == NULL)
180         {
181               DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
182               free(pName);
183               return(False);
184         }
185         sprintf(pName,"%s:",pszPrintername);
186         iLg = strlen(pName);
187         /*DEBUG(3,( " Looking for entry %s\n",pName));*/
188         iEtat = 0;
189         /* scan qconfig file for searching <printername>:       */
190         for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
191         {
192                 if (*line == '*' || *line == 0)
193                 continue;
194                 switch (iEtat)
195                 {
196                         case 0: /* scanning entry */
197                          if (strncmp(line,pName,iLg) == 0)
198                          {
199                                 iEtat = 1;
200                                 continue;
201                          }
202                          break;
203                         case 1: /* scanning device stanza */
204                          if (*line == '*' || *line == 0) continue;
205                          if (*line != '\t' && *line != ' ')
206                          {
207                            /* name is found without stanza device  */
208                            /* probably a good printer ???               */
209                            free (line);
210                            free(pName);
211                            fclose(pfile);
212                            return(True);
213                           }
214                         
215                           if (strlocate(line,"backend"))
216                           {
217                                 /* it's a device, not a virtual printer*/
218                                 iEtat = 0;
219                           }
220                           else if (strlocate(line,"device"))
221                           {
222                                 /* it's a good virtual printer */
223                                 free (line);
224                                 free(pName);
225                                 fclose(pfile);
226                                 return(True);
227                           }
228                           break;
229                 }
230         }
231         free (pName);
232         fclose(pfile);
233         return(False);
234 }
235
236 #endif
237 /***************************************************************************
238 Scan printcap file pszPrintcapname for a printer called pszPrintername. 
239 Return True if found, else False. Returns False on error, too, after logging 
240 the error at level 0. For generality, the printcap name may be passed - if
241 passed as NULL, the configuration will be queried for the name.
242 ***************************************************************************/
243 BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname)
244 {
245   char *line=NULL;
246   char *psz;
247   char *p,*q;
248   FILE *pfile;
249
250   if (pszPrintername == NULL || pszPrintername[0] == '\0')
251     {
252       DEBUG(0,( "Attempt to locate null printername! Internal error?\n"));
253       return(False);
254     }
255
256   /* only go looking if no printcap name supplied */
257   if ((psz = pszPrintcapname) == NULL || psz[0] == '\0')
258     if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
259       {
260         DEBUG(0,( "No printcap file name configured!\n"));
261         return(False);
262       }
263
264 #ifdef SYSV
265     if (strequal(psz, "lpstat"))
266        return (sysv_printername_ok(pszPrintername));
267 #endif
268
269 #ifdef AIX
270   if (strlocate(psz,"/qconfig"))
271      return(ScanQconfig(psz,pszPrintername));
272 #endif
273
274   if ((pfile = fopen(psz, "r")) == NULL)
275     {
276       DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
277       return(False);
278     }
279
280   for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
281     {
282       if (*line == '#' || *line == 0)
283         continue;
284
285       /* now we have a real printer line - cut it off at the first : */      
286       p = strchr(line,':');
287       if (p) *p = 0;
288       
289       /* now just check if the name is in the list */
290       /* NOTE: I avoid strtok as the fn calling this one may be using it */
291       for (p=line; p; p=q)
292         {
293           if ((q = strchr(p,'|'))) *q++ = 0;
294
295           if (strequal(p,pszPrintername))
296             {
297               /* normalise the case */
298               strcpy(pszPrintername,p);
299               free(line);
300               fclose(pfile);
301               return(True);           
302             }
303           p = q;
304         }
305     }
306
307   fclose(pfile);
308   return(False);
309 }
310
311
312 /***************************************************************************
313 run a function on each printer name in the printcap file. The function is 
314 passed the primary name and the comment (if possible)
315 ***************************************************************************/
316 void pcap_printer_fn(void (*fn)())
317 {
318   pstring name,comment;
319   char *line;
320   char *psz;
321   char *p,*q;
322   FILE *pfile;
323
324   /* only go looking if no printcap name supplied */
325   if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
326     {
327       DEBUG(0,( "No printcap file name configured!\n"));
328       return;
329     }
330
331 #ifdef SYSV
332     if (strequal(psz, "lpstat")) {
333       sysv_printer_fn(fn);
334       return;
335     }
336 #endif
337
338 #ifdef AIX
339   if (strlocate(psz,"/qconfig"))
340   {
341         ScanQconfig_fn(psz,fn);
342      return;
343   }
344 #endif
345
346   if ((pfile = fopen(psz, "r")) == NULL)
347     {
348       DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
349       return;
350     }
351
352   for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line))
353     {
354       if (*line == '#' || *line == 0)
355         continue;
356
357       /* now we have a real printer line - cut it off at the first : */      
358       p = strchr(line,':');
359       if (p) *p = 0;
360       
361       /* now find the most likely printer name and comment 
362        this is pure guesswork, but it's better than nothing */
363       *name = 0;
364       *comment = 0;
365       for (p=line; p; p=q)
366         {
367           BOOL has_punctuation;
368           if ((q = strchr(p,'|'))) *q++ = 0;
369
370           has_punctuation = (strchr(p,' ') || strchr(p,'(') || strchr(p,')'));
371
372           if (strlen(p)>strlen(comment) && has_punctuation)
373             {
374               StrnCpy(comment,p,sizeof(comment)-1);
375               continue;
376             }
377
378           if (strlen(p) <= 8 && strlen(p)>strlen(name) && !has_punctuation)
379             {
380               if (!*comment) pstrcpy(comment,name);
381               pstrcpy(name,p);
382               continue;
383             }
384
385           if (!strchr(comment,' ') && 
386               strlen(p) > strlen(comment))
387             {
388               StrnCpy(comment,p,sizeof(comment)-1);
389               continue;
390             }
391         }
392
393       comment[60] = 0;
394       name[8] = 0;
395
396       if (*name)
397         fn(name,comment);
398     }
399   fclose(pfile);
400 }