]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/ppds.c
Now sort PPD files by make, model, and language.
[thirdparty/cups.git] / scheduler / ppds.c
1 /*
2 * "$Id: ppds.c,v 1.4 2000/02/11 05:04:14 mike Exp $"
3 *
4 * PPD scanning routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2000 by Easy Software Products.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * LoadPPDs() - Load PPD files from the specified directory...
27 * load_ppds() - Load PPD files recursively.
28 */
29
30 /*
31 * Include necessary headers...
32 */
33
34 #include "cupsd.h"
35 #include <ctype.h>
36
37 #ifdef HAVE_LIBZ
38 # include <zlib.h>
39 #endif /* HAVE_LIBZ */
40
41 #if HAVE_DIRENT_H
42 # include <dirent.h>
43 typedef struct dirent DIRENT;
44 # define NAMLEN(dirent) strlen((dirent)->d_name)
45 #else
46 # if HAVE_SYS_NDIR_H
47 # include <sys/ndir.h>
48 # endif
49 # if HAVE_SYS_DIR_H
50 # include <sys/dir.h>
51 # endif
52 # if HAVE_NDIR_H
53 # include <ndir.h>
54 # endif
55 typedef struct direct DIRENT;
56 # define NAMLEN(dirent) (dirent)->d_namlen
57 #endif
58
59
60 /*
61 * PPD information structure...
62 */
63
64 typedef struct
65 {
66 char ppd_make[128], /* Manufacturer */
67 ppd_make_and_model[256], /* Make and model */
68 ppd_name[256], /* PPD filename */
69 ppd_natural_language[16]; /* Natural language */
70 } ppd_info_t;
71
72
73 /*
74 * Local globals...
75 */
76
77 static int num_ppds, /* Number of PPD files */
78 alloc_ppds; /* Number of allocated entries */
79 static ppd_info_t *ppds; /* PPD file info */
80
81
82 /*
83 * Local functions...
84 */
85
86 static int compare_ppds(const ppd_info_t *p0, const ppd_info_t *p1);
87 static void load_ppds(const char *d, const char *p);
88
89
90 /*
91 * 'LoadPPDs()' - Load PPD files from the specified directory...
92 */
93
94 void
95 LoadPPDs(const char *d) /* I - Directory to scan... */
96 {
97 int i; /* Looping var */
98 ppd_info_t *ppd; /* Current PPD file */
99
100
101 /*
102 * Load all PPDs in the specified directory and below...
103 */
104
105 num_ppds = 0;
106 alloc_ppds = 0;
107 ppds = (ppd_info_t *)0;
108
109 load_ppds(d, "");
110
111 /*
112 * Sort the PPDs...
113 */
114
115 if (num_ppds > 1)
116 qsort(ppds, num_ppds, sizeof(ppd_info_t),
117 (int (*)(const void *, const void *))compare_ppds);
118
119 /*
120 * Create the list of PPDs...
121 */
122
123 PPDs = ippNew();
124
125 for (i = num_ppds, ppd = ppds; i > 0; i --, ppd ++)
126 {
127 if (i)
128 ippAddSeparator(PPDs);
129
130 ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_NAME,
131 "ppd-name", NULL, ppd->ppd_name);
132 ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
133 "ppd-make", NULL, ppd->ppd_make);
134 ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
135 "ppd-make-and-model", NULL, ppd->ppd_make_and_model);
136 ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
137 "ppd-natural-language", NULL, ppd->ppd_natural_language);
138 }
139
140 /*
141 * Free the memory used...
142 */
143
144 if (alloc_ppds)
145 free(ppds);
146 }
147
148
149 /*
150 * 'compare_ppds()' - Compare PPD file make and model names for sorting.
151 */
152
153 static int /* O - Result of comparison */
154 compare_ppds(const ppd_info_t *p0, /* I - First PPD file */
155 const ppd_info_t *p1) /* I - Second PPD file */
156 {
157 const char *s, /* First name */
158 *t; /* Second name */
159 int diff, /* Difference between digits */
160 digits; /* Number of digits */
161
162
163 /*
164 * First compare manufacturers...
165 */
166
167 if ((diff = strcasecmp(p0->ppd_make, p1->ppd_make)) != 0)
168 return (diff);
169
170 /*
171 * Then compare names...
172 */
173
174 s = p0->ppd_make_and_model;
175 t = p1->ppd_make_and_model;
176
177 /*
178 * Loop through both nicknames, returning only when a difference is
179 * seen. Also, compare whole numbers rather than just characters, too!
180 */
181
182 while (*s && *t)
183 {
184 if (isdigit(*s) && isdigit(*t))
185 {
186 /*
187 * Got a number; start by skipping leading 0's...
188 */
189
190 while (*s == '0')
191 s ++;
192 while (*t == '0')
193 t ++;
194
195 /*
196 * Skip equal digits...
197 */
198
199 while (isdigit(*s) && *s == *t)
200 {
201 s ++;
202 t ++;
203 }
204
205 /*
206 * Bounce out if *s and *t aren't both digits...
207 */
208
209 if (isdigit(*s) && !isdigit(*t))
210 return (1);
211 else if (!isdigit(*s) && isdigit(*t))
212 return (-1);
213 else if (!isdigit(*s) || !isdigit(*t))
214 continue;
215
216 if (*s < *t)
217 diff = -1;
218 else
219 diff = 1;
220
221 /*
222 * Figure out how many more digits there are...
223 */
224
225 digits = 0;
226 s ++;
227 t ++;
228
229 while (isdigit(*s))
230 {
231 digits ++;
232 s ++;
233 }
234
235 while (isdigit(*t))
236 {
237 digits --;
238 t ++;
239 }
240
241 /*
242 * Return if the number or value of the digits is different...
243 */
244
245 if (digits < 0)
246 return (-1);
247 else if (digits > 0)
248 return (1);
249 else if (diff)
250 return (diff);
251 }
252 else if (tolower(*s) < tolower(*t))
253 return (-1);
254 else if (tolower(*s) > tolower(*t))
255 return (1);
256 else
257 {
258 s ++;
259 t ++;
260 }
261 }
262
263 /*
264 * Return the results of the final comparison...
265 */
266
267 if (*s)
268 return (1);
269 else if (*t)
270 return (-1);
271 else
272 return (strcasecmp(p0->ppd_natural_language, p1->ppd_natural_language));
273 }
274
275
276 /*
277 * 'load_ppds()' - Load PPD files recursively.
278 */
279
280 static void
281 load_ppds(const char *d, /* I - Actual directory */
282 const char *p) /* I - Virtual path in name */
283 {
284 #ifdef HAVE_LIBZ
285 gzFile fp; /* Pointer to file */
286 #else
287 FILE *fp; /* Pointer to file */
288 #endif /* HAVE_LIBZ */
289 DIR *dir; /* Directory pointer */
290 DIRENT *dent; /* Directory entry */
291 struct stat fileinfo; /* File information */
292 char filename[1024], /* Name of backend */
293 line[1024], /* Line from backend */
294 *ptr, /* Pointer into name */
295 name[128], /* Name of PPD file */
296 language[64], /* Device class */
297 manufacturer[1024], /* Manufacturer */
298 make_model[256]; /* Make and model */
299 ppd_info_t *ppd; /* New PPD file */
300
301
302 if ((dir = opendir(d)) == NULL)
303 {
304 LogMessage(L_ERROR, "LoadPPDs: Unable to open PPD directory \"%s\": %s",
305 d, strerror(errno));
306 return;
307 }
308
309 while ((dent = readdir(dir)) != NULL)
310 {
311 /*
312 * Skip "." and ".."...
313 */
314
315 if (dent->d_name[0] == '.')
316 continue;
317
318 /*
319 * See if this is a file...
320 */
321
322 snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name);
323
324 if (p[0])
325 snprintf(name, sizeof(name), "%s/%s", p, dent->d_name);
326 else
327 strcpy(name, dent->d_name);
328
329 if (stat(filename, &fileinfo))
330 continue;
331
332 if (S_ISDIR(fileinfo.st_mode))
333 {
334 /*
335 * Do subdirectory...
336 */
337
338 load_ppds(filename, name);
339 continue;
340 }
341
342 #ifdef HAVE_LIBZ
343 if ((fp = gzopen(filename, "rb")) == NULL)
344 #else
345 if ((fp = fopen(filename, "rb")) == NULL)
346 #endif /* HAVE_LIBZ */
347 continue;
348
349 /*
350 * Yup, now see if this is a PPD file...
351 */
352
353 line[0] = '\0';
354 #ifdef HAVE_LIBZ
355 gzgets(fp, line, sizeof(line));
356 #else
357 fgets(line, sizeof(line), fp);
358 #endif /* HAVE_LIBZ */
359
360 if (strncmp(line, "*PPD-Adobe:", 11) != 0)
361 {
362 /*
363 * Nope, close the file and continue...
364 */
365
366 #ifdef HAVE_LIBZ
367 gzclose(fp);
368 #else
369 fclose(fp);
370 #endif /* HAVE_LIBZ */
371
372 continue;
373 }
374
375 /*
376 * Now read until we get the NickName field...
377 */
378
379 make_model[0] = '\0';
380 manufacturer[0] = '\0';
381 strcpy(language, "en");
382
383 #ifdef HAVE_LIBZ
384 while (gzgets(fp, line, sizeof(line)) != NULL)
385 #else
386 while (fgets(line, sizeof(line), fp) != NULL)
387 #endif /* HAVE_LIBZ */
388 {
389 if (strncmp(line, "*Manufacturer:", 14) == 0)
390 sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
391 else if (strncmp(line, "*ModelName:", 11) == 0)
392 sscanf(line, "%*[^\"]\"%127[^\"]", make_model);
393 else if (strncmp(line, "*LanguageVersion:", 17) == 0)
394 sscanf(line, "%*[^\"]\"%63[^\"]", language);
395 else if (strncmp(line, "*NickName:", 10) == 0)
396 {
397 sscanf(line, "%*[^\"]\"%255[^\"]", make_model);
398 break;
399 }
400 }
401
402 /*
403 * Close the file...
404 */
405
406 #ifdef HAVE_LIBZ
407 gzclose(fp);
408 #else
409 fclose(fp);
410 #endif /* HAVE_LIBZ */
411
412 /*
413 * See if we got all of the required info...
414 */
415
416 while (isspace(make_model[0]))
417 strcpy(make_model, make_model + 1);
418
419 if (!make_model[0])
420 continue; /* Nope... */
421
422 /*
423 * See if we got a manufacturer...
424 */
425
426 while (isspace(manufacturer[0]))
427 strcpy(manufacturer, manufacturer + 1);
428
429 if (manufacturer[0] || strcmp(manufacturer, "ESP") == 0)
430 {
431 /*
432 * Nope, copy the first part of the make and model then...
433 */
434
435 strncpy(manufacturer, make_model, sizeof(manufacturer) - 1);
436
437 /*
438 * Truncate at the first space, dash, or slash, or make the
439 * manufacturer "Other"...
440 */
441
442 for (ptr = manufacturer; *ptr; ptr ++)
443 if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
444 break;
445
446 if (*ptr && ptr > manufacturer)
447 *ptr = '\0';
448 else if (strncasecmp(manufacturer, "agfa", 4) == 0)
449 strcpy(manufacturer, "AGFA");
450 else if (strncasecmp(manufacturer, "ps-ipu", 6) == 0)
451 strcpy(manufacturer, "Canon");
452 else if (strncasecmp(manufacturer, "herk", 4) == 0)
453 strcpy(manufacturer, "Linotype");
454 else
455 strcpy(manufacturer, "Other");
456
457 /*
458 * Hack for various vendors...
459 */
460
461 if (strcasecmp(manufacturer, "XPrint") == 0)
462 strcpy(manufacturer, "Xerox");
463 else if (strcasecmp(manufacturer, "Eastman") == 0)
464 strcpy(manufacturer, "Kodak");
465 else if (strcasecmp(manufacturer, "laserwriter") == 0)
466 strcpy(manufacturer, "Apple");
467 else if (strcasecmp(manufacturer, "colorpoint") == 0)
468 strcpy(manufacturer, "Seiko");
469 else if (strcasecmp(manufacturer, "fiery") == 0)
470 strcpy(manufacturer, "EFI");
471 else if (strncasecmp(manufacturer, "primera", 7) == 0)
472 strcpy(manufacturer, "Fargo");
473 }
474
475 /*
476 * Fix the language as needed...
477 */
478
479 if (strcasecmp(language, "german") == 0)
480 strcpy(language, "de");
481 else if (strcasecmp(language, "spanish") == 0)
482 strcpy(language, "es");
483 else if (strlen(language) > 2)
484 {
485 /*
486 * en, fr, it, etc.
487 */
488
489 language[0] = tolower(language[0]);
490 language[1] = tolower(language[1]);
491 language[2] = '\0';
492 }
493
494 /*
495 * Add the PPD file...
496 */
497
498 if (num_ppds >= alloc_ppds)
499 {
500 /*
501 * Allocate (more) memory for the PPD files...
502 */
503
504 if (alloc_ppds == 0)
505 ppd = malloc(sizeof(ppd_info_t) * 32);
506 else
507 ppd = realloc(ppds, sizeof(ppd_info_t) * (alloc_ppds + 32));
508
509 if (ppd == NULL)
510 {
511 LogMessage(L_ERROR, "load_ppds: Ran out of memory for %d PPD files!",
512 alloc_ppds + 32);
513 closedir(dir);
514 return;
515 }
516
517 ppds = ppd;
518 alloc_ppds += 32;
519 }
520
521 ppd = ppds + num_ppds;
522 num_ppds ++;
523
524 memset(ppd, 0, sizeof(ppd_info_t));
525 strncpy(ppd->ppd_name, name, sizeof(ppd->ppd_name) - 1);
526 strncpy(ppd->ppd_make, manufacturer, sizeof(ppd->ppd_make) - 1);
527 strncpy(ppd->ppd_make_and_model, make_model,
528 sizeof(ppd->ppd_make_and_model) - 1);
529 strncpy(ppd->ppd_natural_language, language,
530 sizeof(ppd->ppd_natural_language) - 1);
531
532 LogMessage(L_DEBUG, "LoadPPDs: Added ppd \"%s\"...", name);
533 }
534
535 closedir(dir);
536 }
537
538
539 /*
540 * End of "$Id: ppds.c,v 1.4 2000/02/11 05:04:14 mike Exp $".
541 */