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