]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-driverd.cxx
Merge changes from CUPS 1.4svn-r8454.
[thirdparty/cups.git] / scheduler / cups-driverd.cxx
1 /*
2 * "$Id$"
3 *
4 * PPD/driver support for the Common UNIX Printing System (CUPS).
5 *
6 * This program handles listing and installing both static PPD files
7 * in CUPS_DATADIR/model and dynamically generated PPD files using
8 * the driver helper programs in CUPS_SERVERBIN/driver.
9 *
10 * Copyright 2007-2009 by Apple Inc.
11 * Copyright 1997-2007 by Easy Software Products.
12 *
13 * These coded instructions, statements, and computer programs are the
14 * property of Apple Inc. and are protected by Federal copyright
15 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
16 * which should have been included with this file. If this file is
17 * file is missing or damaged, see the license at "http://www.cups.org/".
18 *
19 * Contents:
20 *
21 * main() - Scan for drivers and return an IPP response.
22 * add_ppd() - Add a PPD file.
23 * cat_drv() - Generate a PPD from a driver info file.
24 * cat_ppd() - Copy a PPD file to stdout.
25 * copy_static() - Copy a static PPD file to stdout.
26 * compare_matches() - Compare PPD match scores for sorting.
27 * compare_names() - Compare PPD filenames for sorting.
28 * compare_ppds() - Compare PPD file make and model names for sorting.
29 * dump_ppds_dat() - Dump the contents of the ppds.dat file.
30 * free_array() - Free an array of strings.
31 * list_ppds() - List PPD files.
32 * load_ppds() - Load PPD files recursively.
33 * load_drv() - Load the PPDs from a driver information file.
34 * load_drivers() - Load driver-generated PPD files.
35 * load_ppds_dat() - Load the ppds.dat file.
36 * regex_device_id() - Compile a regular expression based on the 1284 device
37 * ID.
38 * regex_string() - Construct a regular expression to compare a simple
39 * string.
40 */
41
42 /*
43 * Include necessary headers...
44 */
45
46 #include "util.h"
47 #include <cups/dir.h>
48 #include <cups/transcode.h>
49 #include <cups/ppd-private.h>
50 #include <ppdc/ppdc.h>
51 #include <regex.h>
52
53
54 /*
55 * Constants...
56 */
57
58 #define PPD_SYNC 0x50504436 /* Sync word for ppds.dat (PPD6) */
59 #define PPD_MAX_LANG 32 /* Maximum languages */
60 #define PPD_MAX_PROD 8 /* Maximum products */
61 #define PPD_MAX_VERS 8 /* Maximum versions */
62
63 #define PPD_TYPE_POSTSCRIPT 0 /* PostScript PPD */
64 #define PPD_TYPE_PDF 1 /* PDF PPD */
65 #define PPD_TYPE_RASTER 2 /* CUPS raster PPD */
66 #define PPD_TYPE_FAX 3 /* Facsimile/MFD PPD */
67 #define PPD_TYPE_UNKNOWN 4 /* Other/hybrid PPD */
68 #define PPD_TYPE_DRV 5 /* Driver info file */
69
70 static const char * const ppd_types[] = /* ppd-type values */
71 {
72 "postscript",
73 "pdf",
74 "raster",
75 "fax",
76 "unknown"
77 };
78
79
80 /*
81 * PPD information structures...
82 */
83
84 typedef struct /**** PPD record ****/
85 {
86 time_t mtime; /* Modification time */
87 off_t size; /* Size in bytes */
88 int model_number; /* cupsModelNumber */
89 int type; /* ppd-type */
90 char filename[512], /* Filename */
91 name[512], /* PPD name */
92 languages[PPD_MAX_LANG][6],
93 /* LanguageVersion/cupsLanguages */
94 products[PPD_MAX_PROD][128],
95 /* Product strings */
96 psversions[PPD_MAX_VERS][32],
97 /* PSVersion strings */
98 make[128], /* Manufacturer */
99 make_and_model[128], /* NickName/ModelName */
100 device_id[256], /* IEEE 1284 Device ID */
101 scheme[128]; /* PPD scheme */
102 } ppd_rec_t;
103
104 typedef struct /**** In-memory record ****/
105 {
106 int found; /* 1 if PPD is found */
107 int matches; /* Match count */
108 ppd_rec_t record; /* PPDs.dat record */
109 } ppd_info_t;
110
111
112 /*
113 * Globals...
114 */
115
116 cups_array_t *PPDsByName = NULL, /* PPD files sorted by filename and name */
117 *PPDsByMakeModel = NULL;/* PPD files sorted by make and model */
118 int ChangedPPD; /* Did we change the PPD database? */
119
120
121 /*
122 * Local functions...
123 */
124
125 static ppd_info_t *add_ppd(const char *filename, const char *name,
126 const char *language, const char *make,
127 const char *make_and_model,
128 const char *device_id, const char *product,
129 const char *psversion, time_t mtime,
130 size_t size, int model_number, int type,
131 const char *scheme);
132 static int cat_drv(const char *name, int request_id);
133 static int cat_ppd(const char *name, int request_id);
134 static int cat_static(const char *name, int request_id);
135 static int compare_matches(const ppd_info_t *p0,
136 const ppd_info_t *p1);
137 static int compare_names(const ppd_info_t *p0,
138 const ppd_info_t *p1);
139 static int compare_ppds(const ppd_info_t *p0,
140 const ppd_info_t *p1);
141 static int dump_ppds_dat(void);
142 static void free_array(cups_array_t *a);
143 static int list_ppds(int request_id, int limit, const char *opt);
144 static int load_drivers(cups_array_t *include,
145 cups_array_t *exclude);
146 static int load_drv(const char *filename, const char *name,
147 cups_file_t *fp, time_t mtime, off_t size);
148 static int load_ppds(const char *d, const char *p, int descend);
149 static void load_ppds_dat(char *filename, size_t filesize,
150 int verbose);
151 static regex_t *regex_device_id(const char *device_id);
152 static regex_t *regex_string(const char *s);
153
154
155 /*
156 * 'main()' - Scan for drivers and return an IPP response.
157 *
158 * Usage:
159 *
160 * cups-driverd request_id limit options
161 */
162
163 int /* O - Exit code */
164 main(int argc, /* I - Number of command-line args */
165 char *argv[]) /* I - Command-line arguments */
166 {
167 /*
168 * Install or list PPDs...
169 */
170
171 if (argc == 3 && !strcmp(argv[1], "cat"))
172 return (cat_ppd(argv[2], 0));
173 else if (argc == 2 && !strcmp(argv[1], "dump"))
174 return (dump_ppds_dat());
175 else if (argc == 4 && !strcmp(argv[1], "get"))
176 return (cat_ppd(argv[3], atoi(argv[2])));
177 else if (argc == 5 && !strcmp(argv[1], "list"))
178 return (list_ppds(atoi(argv[2]), atoi(argv[3]), argv[4]));
179 else
180 {
181 fputs("Usage: cups-driverd cat ppd-name\n", stderr);
182 fputs("Usage: cups-driverd dump\n", stderr);
183 fputs("Usage: cups-driverd get request_id ppd-name\n", stderr);
184 fputs("Usage: cups-driverd list request_id limit options\n", stderr);
185 return (1);
186 }
187 }
188
189
190 /*
191 * 'add_ppd()' - Add a PPD file.
192 */
193
194 static ppd_info_t * /* O - PPD */
195 add_ppd(const char *filename, /* I - PPD filename */
196 const char *name, /* I - PPD name */
197 const char *language, /* I - LanguageVersion */
198 const char *make, /* I - Manufacturer */
199 const char *make_and_model, /* I - NickName/ModelName */
200 const char *device_id, /* I - 1284DeviceID */
201 const char *product, /* I - Product */
202 const char *psversion, /* I - PSVersion */
203 time_t mtime, /* I - Modification time */
204 size_t size, /* I - File size */
205 int model_number, /* I - Model number */
206 int type, /* I - Driver type */
207 const char *scheme) /* I - PPD scheme */
208 {
209 ppd_info_t *ppd; /* PPD */
210 char *recommended; /* Foomatic driver string */
211
212
213 /*
214 * Add a new PPD file...
215 */
216
217 if ((ppd = (ppd_info_t *)calloc(1, sizeof(ppd_info_t))) == NULL)
218 {
219 fprintf(stderr,
220 "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
221 cupsArrayCount(PPDsByName));
222 return (NULL);
223 }
224
225 /*
226 * Zero-out the PPD data and copy the values over...
227 */
228
229 ppd->found = 1;
230 ppd->record.mtime = mtime;
231 ppd->record.size = size;
232 ppd->record.model_number = model_number;
233 ppd->record.type = type;
234
235 strlcpy(ppd->record.filename, filename, sizeof(ppd->record.filename));
236 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
237 strlcpy(ppd->record.languages[0], language,
238 sizeof(ppd->record.languages[0]));
239 strlcpy(ppd->record.products[0], product, sizeof(ppd->record.products[0]));
240 strlcpy(ppd->record.psversions[0], psversion,
241 sizeof(ppd->record.psversions[0]));
242 strlcpy(ppd->record.make, make, sizeof(ppd->record.make));
243 strlcpy(ppd->record.make_and_model, make_and_model,
244 sizeof(ppd->record.make_and_model));
245 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
246 strlcpy(ppd->record.scheme, scheme, sizeof(ppd->record.scheme));
247
248 /*
249 * Strip confusing (and often wrong) "recommended" suffix added by
250 * Foomatic drivers...
251 */
252
253 if ((recommended = strstr(ppd->record.make_and_model,
254 " (recommended)")) != NULL)
255 *recommended = '\0';
256
257 /*
258 * Add the PPD to the PPD arrays...
259 */
260
261 cupsArrayAdd(PPDsByName, ppd);
262 cupsArrayAdd(PPDsByMakeModel, ppd);
263
264 /*
265 * Return the new PPD pointer...
266 */
267
268 return (ppd);
269 }
270
271
272 /*
273 * 'cat_drv()' - Generate a PPD from a driver info file.
274 */
275
276 static int /* O - Exit code */
277 cat_drv(const char *name, /* I - PPD name */
278 int request_id) /* I - Request ID for response? */
279 {
280 const char *datadir; // CUPS_DATADIR env var
281 ppdcSource *src; // PPD source file data
282 ppdcDriver *d; // Current driver
283 cups_file_t *out; // Stdout via CUPS file API
284 char message[2048], // status-message
285 filename[1024], // Full path to .drv file(s)
286 scheme[32], // URI scheme ("drv")
287 userpass[256], // User/password info (unused)
288 host[2], // Hostname (unused)
289 resource[1024], // Resource path (/dir/to/filename.drv)
290 *pc_file_name; // Filename portion of URI
291 int port; // Port number (unused)
292
293
294 // Determine where CUPS has installed the data files...
295 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
296 datadir = CUPS_DATADIR;
297
298 // Pull out the
299 if (httpSeparateURI(HTTP_URI_CODING_ALL, name, scheme, sizeof(scheme),
300 userpass, sizeof(userpass), host, sizeof(host), &port,
301 resource, sizeof(resource)) < HTTP_URI_OK ||
302 strstr(resource, "../") ||
303 (pc_file_name = strrchr(resource, '/')) == NULL ||
304 pc_file_name == resource)
305 {
306 fprintf(stderr, "ERROR: Bad PPD name \"%s\"!\n", name);
307
308 if (request_id)
309 {
310 snprintf(message, sizeof(message), "Bad PPD name \"%s\"!", name);
311
312 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
313 cupsdSendIPPGroup(IPP_TAG_OPERATION);
314 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
315 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
316 "en-US");
317 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
318 cupsdSendIPPTrailer();
319 }
320
321 return (1);
322 }
323
324 *pc_file_name++ = '\0';
325
326 #ifdef __APPLE__
327 if (!strncmp(resource, "/Library/Printers/PPDs.drv/", 27))
328 strlcpy(filename, resource, sizeof(filename));
329 else
330 #endif // __APPLE__
331 {
332 snprintf(filename, sizeof(filename), "%s/drv%s", datadir, resource);
333 if (access(filename, 0))
334 snprintf(filename, sizeof(filename), "%s/model%s", datadir, resource);
335 }
336
337 src = new ppdcSource(filename);
338
339 for (d = (ppdcDriver *)src->drivers->first();
340 d;
341 d = (ppdcDriver *)src->drivers->next())
342 if (!strcmp(pc_file_name, d->pc_file_name->value) ||
343 (d->file_name && !strcmp(pc_file_name, d->file_name->value)))
344 break;
345
346 if (d)
347 {
348 ppdcArray *locales; // Locale names
349 ppdcCatalog *catalog; // Message catalog in .drv file
350
351
352 fprintf(stderr, "DEBUG2: [cups-driverd] %d locales defined in \"%s\"...\n",
353 src->po_files->count, filename);
354
355 locales = new ppdcArray();
356 for (catalog = (ppdcCatalog *)src->po_files->first();
357 catalog;
358 catalog = (ppdcCatalog *)src->po_files->next())
359 {
360 fprintf(stderr, "DEBUG2: [cups-driverd] Adding locale \"%s\"...\n",
361 catalog->locale->value);
362 locales->add(catalog->locale);
363 }
364
365 if (request_id)
366 {
367 cupsdSendIPPHeader(IPP_OK, request_id);
368 cupsdSendIPPGroup(IPP_TAG_OPERATION);
369 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
370 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
371 "en-US");
372 cupsdSendIPPTrailer();
373 fflush(stdout);
374 }
375
376 out = cupsFileStdout();
377 d->write_ppd_file(out, NULL, locales, src, PPDC_LFONLY);
378 cupsFileClose(out);
379
380 locales->release();
381 }
382 else
383 {
384 fprintf(stderr, "ERROR: PPD \"%s\" not found!\n", name);
385
386 if (request_id)
387 {
388 snprintf(message, sizeof(message), "PPD \"%s\" not found!", name);
389
390 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
391 cupsdSendIPPGroup(IPP_TAG_OPERATION);
392 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
393 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
394 "en-US");
395 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
396 cupsdSendIPPTrailer();
397 }
398 }
399
400 src->release();
401
402 return (!d);
403 }
404
405
406 /*
407 * 'cat_ppd()' - Copy a PPD file to stdout.
408 */
409
410 static int /* O - Exit code */
411 cat_ppd(const char *name, /* I - PPD name */
412 int request_id) /* I - Request ID for response? */
413 {
414 char scheme[256], /* Scheme from PPD name */
415 *sptr, /* Pointer into scheme */
416 line[1024], /* Line/filename */
417 message[2048]; /* status-message */
418
419
420 /*
421 * Figure out if this is a static or dynamic PPD file...
422 */
423
424 strlcpy(scheme, name, sizeof(scheme));
425 if ((sptr = strchr(scheme, ':')) != NULL)
426 {
427 *sptr = '\0';
428
429 if (!strcmp(scheme, "file"))
430 {
431 /*
432 * "file:name" == "name"...
433 */
434
435 name += 5;
436 scheme[0] = '\0';
437 }
438 }
439 else
440 scheme[0] = '\0';
441
442 if (request_id > 0)
443 puts("Content-Type: application/ipp\n");
444
445 if (!scheme[0])
446 return (cat_static(name, request_id));
447 else if (!strcmp(scheme, "drv"))
448 return (cat_drv(name, request_id));
449 else
450 {
451 /*
452 * Dynamic PPD, see if we have a driver program to support it...
453 */
454
455 const char *serverbin; /* CUPS_SERVERBIN env var */
456 char *argv[4]; /* Arguments for program */
457
458
459 if ((serverbin = getenv("CUPS_SERVERBIN")) == NULL)
460 serverbin = CUPS_SERVERBIN;
461
462 snprintf(line, sizeof(line), "%s/driver/%s", serverbin, scheme);
463 if (access(line, X_OK))
464 {
465 /*
466 * File does not exist or is not executable...
467 */
468
469 fprintf(stderr, "ERROR: [cups-driverd] Unable to access \"%s\" - %s\n",
470 line, strerror(errno));
471
472 if (request_id > 0)
473 {
474 snprintf(message, sizeof(message), "Unable to access \"%s\" - %s",
475 line, strerror(errno));
476
477 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
478 cupsdSendIPPGroup(IPP_TAG_OPERATION);
479 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
480 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
481 "en-US");
482 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
483 cupsdSendIPPTrailer();
484 }
485
486 return (1);
487 }
488
489 /*
490 * Yes, let it cat the PPD file...
491 */
492
493 if (request_id)
494 {
495 cupsdSendIPPHeader(IPP_OK, request_id);
496 cupsdSendIPPGroup(IPP_TAG_OPERATION);
497 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
498 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
499 "en-US");
500 cupsdSendIPPTrailer();
501 }
502
503 argv[0] = scheme;
504 argv[1] = (char *)"cat";
505 argv[2] = (char *)name;
506 argv[3] = NULL;
507
508 if (cupsdExec(line, argv))
509 {
510 /*
511 * Unable to execute driver...
512 */
513
514 fprintf(stderr, "ERROR: [cups-driverd] Unable to execute \"%s\" - %s\n",
515 line, strerror(errno));
516 return (1);
517 }
518 }
519
520 /*
521 * Return with no errors...
522 */
523
524 return (0);
525 }
526
527
528 /*
529 * 'copy_static()' - Copy a static PPD file to stdout.
530 */
531
532 static int /* O - Exit code */
533 cat_static(const char *name, /* I - PPD name */
534 int request_id) /* I - Request ID for response? */
535 {
536 cups_file_t *fp; /* PPD file */
537 const char *datadir; /* CUPS_DATADIR env var */
538 char line[1024], /* Line/filename */
539 message[2048]; /* status-message */
540
541
542 if (name[0] == '/' || strstr(name, "../") || strstr(name, "/.."))
543 {
544 /*
545 * Bad name...
546 */
547
548 fprintf(stderr, "ERROR: [cups-driverd] Bad PPD name \"%s\"!\n", name);
549
550 if (request_id)
551 {
552 snprintf(message, sizeof(message), "Bad PPD name \"%s\"!", name);
553
554 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
555 cupsdSendIPPGroup(IPP_TAG_OPERATION);
556 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
557 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
558 "en-US");
559 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
560 cupsdSendIPPTrailer();
561 }
562
563 return (1);
564 }
565
566 /*
567 * Try opening the file...
568 */
569
570 #ifdef __APPLE__
571 if (!strncmp(name, "System/Library/Printers/PPDs/Contents/Resources/", 48) ||
572 !strncmp(name, "Library/Printers/PPDs/Contents/Resources/", 41))
573 {
574 /*
575 * Map ppd-name to Mac OS X standard locations...
576 */
577
578 snprintf(line, sizeof(line), "/%s", name);
579 }
580 else
581
582 #elif defined(__linux)
583 if (!strncmp(name, "lsb/usr/", 8))
584 {
585 /*
586 * Map ppd-name to LSB standard /usr/share/ppd location...
587 */
588
589 snprintf(line, sizeof(line), "/usr/share/ppd/%s", name + 8);
590 }
591 else if (!strncmp(name, "lsb/opt/", 8))
592 {
593 /*
594 * Map ppd-name to LSB standard /opt/share/ppd location...
595 */
596
597 snprintf(line, sizeof(line), "/opt/share/ppd/%s", name + 8);
598 }
599 else if (!strncmp(name, "lsb/local/", 10))
600 {
601 /*
602 * Map ppd-name to LSB standard /usr/local/share/ppd location...
603 */
604
605 snprintf(line, sizeof(line), "/usr/local/share/ppd/%s", name + 10);
606 }
607 else
608
609 #endif /* __APPLE__ */
610 {
611 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
612 datadir = CUPS_DATADIR;
613
614 snprintf(line, sizeof(line), "%s/model/%s", datadir, name);
615 }
616
617 if ((fp = cupsFileOpen(line, "r")) == NULL)
618 {
619 fprintf(stderr, "ERROR: [cups-driverd] Unable to open \"%s\" - %s\n",
620 line, strerror(errno));
621
622 if (request_id)
623 {
624 snprintf(message, sizeof(message), "Unable to open \"%s\" - %s",
625 line, strerror(errno));
626
627 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
628 cupsdSendIPPGroup(IPP_TAG_OPERATION);
629 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
630 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
631 "en-US");
632 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
633 cupsdSendIPPTrailer();
634 }
635
636 return (1);
637 }
638
639 if (request_id)
640 {
641 cupsdSendIPPHeader(IPP_OK, request_id);
642 cupsdSendIPPGroup(IPP_TAG_OPERATION);
643 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
644 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
645 "en-US");
646 cupsdSendIPPTrailer();
647 }
648
649 /*
650 * Now copy the file to stdout...
651 */
652
653 while (cupsFileGets(fp, line, sizeof(line)))
654 puts(line);
655
656 cupsFileClose(fp);
657
658 return (0);
659 }
660
661
662 /*
663 * 'compare_matches()' - Compare PPD match scores for sorting.
664 */
665
666 static int
667 compare_matches(const ppd_info_t *p0, /* I - First PPD */
668 const ppd_info_t *p1) /* I - Second PPD */
669 {
670 if (p1->matches != p0->matches)
671 return (p1->matches - p0->matches);
672 else
673 return (cupsdCompareNames(p0->record.make_and_model,
674 p1->record.make_and_model));
675 }
676
677
678 /*
679 * 'compare_names()' - Compare PPD filenames for sorting.
680 */
681
682 static int /* O - Result of comparison */
683 compare_names(const ppd_info_t *p0, /* I - First PPD file */
684 const ppd_info_t *p1) /* I - Second PPD file */
685 {
686 int diff; /* Difference between strings */
687
688
689 if ((diff = strcmp(p0->record.filename, p1->record.filename)) != 0)
690 return (diff);
691 else
692 return (strcmp(p0->record.name, p1->record.name));
693 }
694
695
696 /*
697 * 'compare_ppds()' - Compare PPD file make and model names for sorting.
698 */
699
700 static int /* O - Result of comparison */
701 compare_ppds(const ppd_info_t *p0, /* I - First PPD file */
702 const ppd_info_t *p1) /* I - Second PPD file */
703 {
704 int diff; /* Difference between strings */
705
706
707 /*
708 * First compare manufacturers...
709 */
710
711 if ((diff = strcasecmp(p0->record.make, p1->record.make)) != 0)
712 return (diff);
713 else if ((diff = cupsdCompareNames(p0->record.make_and_model,
714 p1->record.make_and_model)) != 0)
715 return (diff);
716 else
717 return (strcmp(p0->record.languages[0], p1->record.languages[0]));
718 }
719
720
721 /*
722 * 'dump_ppds_dat()' - Dump the contents of the ppds.dat file.
723 */
724
725 static int /* O - Exit status */
726 dump_ppds_dat(void)
727 {
728 char filename[1024]; /* ppds.dat filename */
729 ppd_info_t *ppd; /* Current PPD */
730
731
732 /*
733 * See if we a PPD database file...
734 */
735
736 load_ppds_dat(filename, sizeof(filename), 0);
737
738 puts("mtime,size,model_number,type,filename,name,languages0,products0,"
739 "psversions0,make,make_and_model,device_id,scheme");
740 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByName);
741 ppd;
742 ppd = (ppd_info_t *)cupsArrayNext(PPDsByName))
743 printf("%d,%ld,%d,%d,\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\","
744 "\"%s\",\"%s\"\n",
745 (int)ppd->record.mtime, (long)ppd->record.size,
746 ppd->record.model_number, ppd->record.type, ppd->record.filename,
747 ppd->record.name, ppd->record.languages[0], ppd->record.products[0],
748 ppd->record.psversions[0], ppd->record.make,
749 ppd->record.make_and_model, ppd->record.device_id,
750 ppd->record.scheme);
751
752 return (0);
753 }
754
755
756 /*
757 * 'free_array()' - Free an array of strings.
758 */
759
760 static void
761 free_array(cups_array_t *a) /* I - Array to free */
762 {
763 char *ptr; /* Pointer to string */
764
765
766 for (ptr = (char *)cupsArrayFirst(a);
767 ptr;
768 ptr = (char *)cupsArrayNext(a))
769 free(ptr);
770
771 cupsArrayDelete(a);
772 }
773
774
775 /*
776 * 'list_ppds()' - List PPD files.
777 */
778
779 static int /* O - Exit code */
780 list_ppds(int request_id, /* I - Request ID */
781 int limit, /* I - Limit */
782 const char *opt) /* I - Option argument */
783 {
784 int i; /* Looping vars */
785 int count; /* Number of PPDs to send */
786 ppd_info_t *ppd; /* Current PPD file */
787 cups_file_t *fp; /* ppds.dat file */
788 char filename[1024], /* ppds.dat filename */
789 model[1024]; /* Model directory */
790 const char *cups_datadir; /* CUPS_DATADIR environment variable */
791 int num_options; /* Number of options */
792 cups_option_t *options; /* Options */
793 cups_array_t *requested, /* requested-attributes values */
794 *include, /* PPD schemes to include */
795 *exclude; /* PPD schemes to exclude */
796 const char *device_id, /* ppd-device-id option */
797 *language, /* ppd-natural-language option */
798 *make, /* ppd-make option */
799 *make_and_model, /* ppd-make-and-model option */
800 *model_number_str, /* ppd-model-number option */
801 *product, /* ppd-product option */
802 *psversion, /* ppd-psversion option */
803 *type_str; /* ppd-type option */
804 int model_number, /* ppd-model-number value */
805 type, /* ppd-type value */
806 make_and_model_len, /* Length of ppd-make-and-model */
807 product_len, /* Length of ppd-product */
808 send_device_id, /* Send ppd-device-id? */
809 send_make, /* Send ppd-make? */
810 send_make_and_model, /* Send ppd-make-and-model? */
811 send_model_number, /* Send ppd-model-number? */
812 send_name, /* Send ppd-name? */
813 send_natural_language, /* Send ppd-natural-language? */
814 send_product, /* Send ppd-product? */
815 send_psversion, /* Send ppd-psversion? */
816 send_type, /* Send ppd-type? */
817 sent_header; /* Sent the IPP header? */
818 regex_t *device_id_re, /* Regular expression for matching device ID */
819 *make_and_model_re; /* Regular expression for matching make and model */
820 regmatch_t re_matches[6]; /* Regular expression matches */
821 cups_array_t *matches; /* Matching PPDs */
822
823
824 fprintf(stderr,
825 "DEBUG2: [cups-driverd] list_ppds(request_id=%d, limit=%d, "
826 "opt=\"%s\"\n", request_id, limit, opt);
827
828 /*
829 * See if we a PPD database file...
830 */
831
832 load_ppds_dat(filename, sizeof(filename), 1);
833
834 /*
835 * Load all PPDs in the specified directory and below...
836 */
837
838 if ((cups_datadir = getenv("CUPS_DATADIR")) == NULL)
839 cups_datadir = CUPS_DATADIR;
840
841 snprintf(model, sizeof(model), "%s/model", cups_datadir);
842 load_ppds(model, "", 1);
843
844 snprintf(model, sizeof(model), "%s/drv", cups_datadir);
845 load_ppds(model, "", 1);
846
847 #ifdef __APPLE__
848 /*
849 * Load PPDs from standard Mac OS X locations...
850 */
851
852 load_ppds("/Library/Printers/PPDs/Contents/Resources",
853 "Library/Printers/PPDs/Contents/Resources", 0);
854 load_ppds("/Library/Printers/PPDs/Contents/Resources/en.lproj",
855 "Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
856 load_ppds("/System/Library/Printers/PPDs/Contents/Resources",
857 "System/Library/Printers/PPDs/Contents/Resources", 0);
858 load_ppds("/System/Library/Printers/PPDs/Contents/Resources/en.lproj",
859 "System/Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
860
861 #elif defined(__linux)
862 /*
863 * Load PPDs from LSB-defined locations...
864 */
865
866 if (!access("/usr/local/share/ppd", 0))
867 load_ppds("/usr/local/share/ppd", "lsb/local", 1);
868 if (!access("/usr/share/ppd", 0))
869 load_ppds("/usr/share/ppd", "lsb/usr", 1);
870 if (!access("/opt/share/ppd", 0))
871 load_ppds("/opt/share/ppd", "lsb/opt", 1);
872 #endif /* __APPLE__ */
873
874 /*
875 * Cull PPD files that are no longer present...
876 */
877
878 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByName);
879 ppd;
880 ppd = (ppd_info_t *)cupsArrayNext(PPDsByName))
881 if (!ppd->found)
882 {
883 /*
884 * Remove this PPD file from the list...
885 */
886
887 cupsArrayRemove(PPDsByName, ppd);
888 cupsArrayRemove(PPDsByMakeModel, ppd);
889 free(ppd);
890
891 ChangedPPD = 1;
892 }
893
894 /*
895 * Write the new ppds.dat file...
896 */
897
898 fprintf(stderr, "DEBUG: [cups-driverd] ChangedPPD=%d\n", ChangedPPD);
899
900 if (ChangedPPD)
901 {
902 if ((fp = cupsFileOpen(filename, "w")) != NULL)
903 {
904 unsigned ppdsync = PPD_SYNC; /* Sync word */
905
906
907 cupsFileWrite(fp, (char *)&ppdsync, sizeof(ppdsync));
908
909 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByName);
910 ppd;
911 ppd = (ppd_info_t *)cupsArrayNext(PPDsByName))
912 cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
913
914 cupsFileClose(fp);
915
916 fprintf(stderr, "INFO: [cups-driverd] Wrote \"%s\", %d PPDs...\n",
917 filename, cupsArrayCount(PPDsByName));
918 }
919 else
920 fprintf(stderr, "ERROR: [cups-driverd] Unable to write \"%s\" - %s\n",
921 filename, strerror(errno));
922 }
923 else
924 fputs("INFO: [cups-driverd] No new or changed PPDs...\n", stderr);
925
926 /*
927 * Scan for dynamic PPD files...
928 */
929
930 num_options = cupsParseOptions(opt, 0, &options);
931 exclude = cupsdCreateStringsArray(cupsGetOption("exclude-schemes",
932 num_options, options));
933 include = cupsdCreateStringsArray(cupsGetOption("include-schemes",
934 num_options, options));
935
936 load_drivers(include, exclude);
937
938 /*
939 * Add the raw driver...
940 */
941
942 add_ppd("", "raw", "en", "Raw", "Raw Queue", "", "", "", 0, 0, 0,
943 PPD_TYPE_UNKNOWN, "raw");
944
945 /*
946 * Send IPP attributes...
947 */
948
949 requested = cupsdCreateStringsArray(
950 cupsGetOption("requested-attributes", num_options,
951 options));
952 device_id = cupsGetOption("ppd-device-id", num_options, options);
953 language = cupsGetOption("ppd-natural-language", num_options, options);
954 make = cupsGetOption("ppd-make", num_options, options);
955 make_and_model = cupsGetOption("ppd-make-and-model", num_options, options);
956 model_number_str = cupsGetOption("ppd-model-number", num_options, options);
957 product = cupsGetOption("ppd-product", num_options, options);
958 psversion = cupsGetOption("ppd-psversion", num_options, options);
959 type_str = cupsGetOption("ppd-type", num_options, options);
960
961 if (make_and_model)
962 make_and_model_len = strlen(make_and_model);
963 else
964 make_and_model_len = 0;
965
966 if (product)
967 product_len = strlen(product);
968 else
969 product_len = 0;
970
971 if (model_number_str)
972 model_number = atoi(model_number_str);
973 else
974 model_number = 0;
975
976 if (type_str)
977 {
978 for (type = 0;
979 type < (int)(sizeof(ppd_types) / sizeof(ppd_types[0]));
980 type ++)
981 if (!strcmp(type_str, ppd_types[type]))
982 break;
983
984 if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0])))
985 {
986 fprintf(stderr, "ERROR: [cups-driverd] Bad ppd-type=\"%s\" ignored!\n",
987 type_str);
988 type_str = NULL;
989 }
990 }
991 else
992 type = 0;
993
994 for (i = 0; i < num_options; i ++)
995 fprintf(stderr, "DEBUG2: [cups-driverd] %s=\"%s\"\n", options[i].name,
996 options[i].value);
997
998 if (!requested || cupsArrayFind(requested, (void *)"all") != NULL)
999 {
1000 send_name = 1;
1001 send_make = 1;
1002 send_make_and_model = 1;
1003 send_model_number = 1;
1004 send_natural_language = 1;
1005 send_device_id = 1;
1006 send_product = 1;
1007 send_psversion = 1;
1008 send_type = 1;
1009 }
1010 else
1011 {
1012 send_name = cupsArrayFind(requested,
1013 (void *)"ppd-name") != NULL;
1014 send_make = cupsArrayFind(requested,
1015 (void *)"ppd-make") != NULL;
1016 send_make_and_model = cupsArrayFind(requested,
1017 (void *)"ppd-make-and-model") != NULL;
1018 send_model_number = cupsArrayFind(requested,
1019 (void *)"ppd-model-number") != NULL;
1020 send_natural_language = cupsArrayFind(requested,
1021 (void *)"ppd-natural-language") != NULL;
1022 send_device_id = cupsArrayFind(requested,
1023 (void *)"ppd-device-id") != NULL;
1024 send_product = cupsArrayFind(requested,
1025 (void *)"ppd-product") != NULL;
1026 send_psversion = cupsArrayFind(requested,
1027 (void *)"ppd-psversion") != NULL;
1028 send_type = cupsArrayFind(requested,
1029 (void *)"ppd-type") != NULL;
1030 }
1031
1032 /*
1033 * Send the content type header to the scheduler; request_id can only be
1034 * 0 when run manually since the scheduler enforces the IPP requirement for
1035 * a request ID from 1 to 2^31-1...
1036 */
1037
1038 if (request_id > 0)
1039 puts("Content-Type: application/ipp\n");
1040
1041 sent_header = 0;
1042
1043 if (limit <= 0 || limit > cupsArrayCount(PPDsByMakeModel))
1044 count = cupsArrayCount(PPDsByMakeModel);
1045 else
1046 count = limit;
1047
1048 if (device_id || language || make || make_and_model || model_number_str ||
1049 product)
1050 {
1051 matches = cupsArrayNew((cups_array_func_t)compare_matches, NULL);
1052
1053 if (device_id)
1054 device_id_re = regex_device_id(device_id);
1055 else
1056 device_id_re = NULL;
1057
1058 if (make_and_model)
1059 make_and_model_re = regex_string(make_and_model);
1060 else
1061 make_and_model_re = NULL;
1062
1063 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
1064 ppd;
1065 ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
1066 {
1067 /*
1068 * Filter PPDs based on make, model, product, language, model number,
1069 * and/or device ID using the "matches" score value. An exact match
1070 * for product, make-and-model, or device-id adds 3 to the score.
1071 * Partial matches for make-and-model yield 1 or 2 points, and matches
1072 * for the make and language add a single point. Results are then sorted
1073 * by score, highest score first.
1074 */
1075
1076 if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
1077 ppd->record.type >= PPD_TYPE_DRV)
1078 continue;
1079
1080 if (cupsArrayFind(exclude, ppd->record.scheme) ||
1081 (include && !cupsArrayFind(include, ppd->record.scheme)))
1082 continue;
1083
1084 ppd->matches = 0;
1085
1086 if (device_id_re &&
1087 !regexec(device_id_re, ppd->record.device_id,
1088 (int)(sizeof(re_matches) / sizeof(re_matches[0])),
1089 re_matches, 0))
1090 {
1091 /*
1092 * Add the number of matching values from the device ID - it will be
1093 * at least 2 (manufacturer and model), and as much as 3 (command set).
1094 */
1095
1096 for (i = 1; i < (int)(sizeof(re_matches) / sizeof(re_matches[0])); i ++)
1097 if (re_matches[i].rm_so >= 0)
1098 ppd->matches ++;
1099 }
1100
1101 if (language)
1102 {
1103 for (i = 0; i < PPD_MAX_LANG; i ++)
1104 if (!ppd->record.languages[i][0])
1105 break;
1106 else if (!strcmp(ppd->record.languages[i], language))
1107 {
1108 ppd->matches ++;
1109 break;
1110 }
1111 }
1112
1113 if (make && !strcasecmp(ppd->record.make, make))
1114 ppd->matches ++;
1115
1116 if (make_and_model_re &&
1117 !regexec(make_and_model_re, ppd->record.make_and_model,
1118 (int)(sizeof(re_matches) / sizeof(re_matches[0])),
1119 re_matches, 0))
1120 {
1121 // See how much of the make-and-model string we matched...
1122 if (re_matches[0].rm_so == 0)
1123 {
1124 if (re_matches[0].rm_eo == make_and_model_len)
1125 ppd->matches += 3; // Exact match
1126 else
1127 ppd->matches += 2; // Prefix match
1128 }
1129 else
1130 ppd->matches ++; // Infix match
1131 }
1132
1133 if (model_number_str && ppd->record.model_number == model_number)
1134 ppd->matches ++;
1135
1136 if (product)
1137 {
1138 for (i = 0; i < PPD_MAX_PROD; i ++)
1139 if (!ppd->record.products[i][0])
1140 break;
1141 else if (!strcasecmp(ppd->record.products[i], product))
1142 {
1143 ppd->matches += 3;
1144 break;
1145 }
1146 }
1147
1148 if (psversion)
1149 {
1150 for (i = 0; i < PPD_MAX_VERS; i ++)
1151 if (!ppd->record.psversions[i][0])
1152 break;
1153 else if (!strcasecmp(ppd->record.psversions[i], psversion))
1154 {
1155 ppd->matches ++;
1156 break;
1157 }
1158 }
1159
1160 if (type_str && ppd->record.type == type)
1161 ppd->matches ++;
1162
1163 if (ppd->matches)
1164 {
1165 fprintf(stderr, "DEBUG2: [cups-driverd] %s matches with score %d!\n",
1166 ppd->record.name, ppd->matches);
1167 cupsArrayAdd(matches, ppd);
1168 }
1169 }
1170 }
1171 else if (include || exclude)
1172 {
1173 matches = cupsArrayNew((cups_array_func_t)compare_ppds, NULL);
1174
1175 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
1176 ppd;
1177 ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
1178 {
1179 /*
1180 * Filter PPDs based on the include/exclude lists.
1181 */
1182
1183 if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
1184 ppd->record.type >= PPD_TYPE_DRV)
1185 continue;
1186
1187 if (cupsArrayFind(exclude, ppd->record.scheme) ||
1188 (include && !cupsArrayFind(include, ppd->record.scheme)))
1189 continue;
1190
1191 cupsArrayAdd(matches, ppd);
1192 }
1193 }
1194 else
1195 matches = PPDsByMakeModel;
1196
1197 for (ppd = (ppd_info_t *)cupsArrayFirst(matches);
1198 count > 0 && ppd;
1199 ppd = (ppd_info_t *)cupsArrayNext(matches))
1200 {
1201 /*
1202 * Skip invalid PPDs...
1203 */
1204
1205 if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
1206 ppd->record.type >= PPD_TYPE_DRV)
1207 continue;
1208
1209 /*
1210 * Send this PPD...
1211 */
1212
1213 if (!sent_header)
1214 {
1215 sent_header = 1;
1216
1217 cupsdSendIPPHeader(IPP_OK, request_id);
1218 cupsdSendIPPGroup(IPP_TAG_OPERATION);
1219 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
1220 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
1221 "en-US");
1222 }
1223
1224 fprintf(stderr, "DEBUG2: [cups-driverd] Sending %s (%s)...\n",
1225 ppd->record.name, ppd->record.make_and_model);
1226
1227 count --;
1228
1229 cupsdSendIPPGroup(IPP_TAG_PRINTER);
1230
1231 if (send_name)
1232 cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name);
1233
1234 if (send_natural_language)
1235 {
1236 cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language",
1237 ppd->record.languages[0]);
1238
1239 for (i = 1; i < PPD_MAX_LANG && ppd->record.languages[i][0]; i ++)
1240 cupsdSendIPPString(IPP_TAG_LANGUAGE, "", ppd->record.languages[i]);
1241 }
1242
1243 if (send_make)
1244 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make);
1245
1246 if (send_make_and_model)
1247 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make-and-model",
1248 ppd->record.make_and_model);
1249
1250 if (send_device_id)
1251 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-device-id",
1252 ppd->record.device_id);
1253
1254 if (send_product)
1255 {
1256 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-product",
1257 ppd->record.products[0]);
1258
1259 for (i = 1; i < PPD_MAX_PROD && ppd->record.products[i][0]; i ++)
1260 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.products[i]);
1261 }
1262
1263 if (send_psversion)
1264 {
1265 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-psversion",
1266 ppd->record.psversions[0]);
1267
1268 for (i = 1; i < PPD_MAX_VERS && ppd->record.psversions[i][0]; i ++)
1269 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.psversions[i]);
1270 }
1271
1272 if (send_type)
1273 cupsdSendIPPString(IPP_TAG_KEYWORD, "ppd-type",
1274 ppd_types[ppd->record.type]);
1275
1276 if (send_model_number)
1277 cupsdSendIPPInteger(IPP_TAG_INTEGER, "ppd-model-number",
1278 ppd->record.model_number);
1279
1280 /*
1281 * If we have only requested the ppd-make attribute, then skip
1282 * the remaining PPDs with this make...
1283 */
1284
1285 if (cupsArrayFind(requested, (void *)"ppd-make") &&
1286 cupsArrayCount(requested) == 1)
1287 {
1288 const char *this_make; /* This ppd-make */
1289
1290
1291 for (this_make = ppd->record.make,
1292 ppd = (ppd_info_t *)cupsArrayNext(matches);
1293 ppd;
1294 ppd = (ppd_info_t *)cupsArrayNext(matches))
1295 if (strcasecmp(this_make, ppd->record.make))
1296 break;
1297
1298 cupsArrayPrev(matches);
1299 }
1300 }
1301
1302 if (!sent_header)
1303 {
1304 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
1305 cupsdSendIPPGroup(IPP_TAG_OPERATION);
1306 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
1307 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
1308 }
1309
1310 cupsdSendIPPTrailer();
1311
1312 return (0);
1313 }
1314
1315
1316 /*
1317 * 'load_ppds()' - Load PPD files recursively.
1318 */
1319
1320 static int /* O - 1 on success, 0 on failure */
1321 load_ppds(const char *d, /* I - Actual directory */
1322 const char *p, /* I - Virtual path in name */
1323 int descend) /* I - Descend into directories? */
1324 {
1325 int i; /* Looping var */
1326 cups_file_t *fp; /* Pointer to file */
1327 cups_dir_t *dir; /* Directory pointer */
1328 cups_dentry_t *dent; /* Directory entry */
1329 char filename[1024], /* Name of PPD or directory */
1330 line[256], /* Line from backend */
1331 *ptr, /* Pointer into name */
1332 name[128], /* Name of PPD file */
1333 lang_version[64], /* PPD LanguageVersion */
1334 lang_encoding[64], /* PPD LanguageEncoding */
1335 country[64], /* Country code */
1336 manufacturer[256], /* Manufacturer */
1337 make_model[256], /* Make and Model */
1338 model_name[256], /* ModelName */
1339 nick_name[256], /* NickName */
1340 device_id[256], /* 1284DeviceID */
1341 product[256], /* Product */
1342 psversion[256], /* PSVersion */
1343 temp[512]; /* Temporary make and model */
1344 int model_number, /* cupsModelNumber */
1345 type; /* ppd-type */
1346 cups_array_t *products, /* Product array */
1347 *psversions, /* PSVersion array */
1348 *cups_languages; /* cupsLanguages array */
1349 ppd_info_t *ppd, /* New PPD file */
1350 key; /* Search key */
1351 int new_ppd; /* Is this a new PPD? */
1352 struct /* LanguageVersion translation table */
1353 {
1354 const char *version, /* LanguageVersion string */
1355 *language; /* Language code */
1356 } languages[] =
1357 {
1358 { "chinese", "zh" },
1359 { "czech", "cs" },
1360 { "danish", "da" },
1361 { "dutch", "nl" },
1362 { "english", "en" },
1363 { "finnish", "fi" },
1364 { "french", "fr" },
1365 { "german", "de" },
1366 { "greek", "el" },
1367 { "hungarian", "hu" },
1368 { "italian", "it" },
1369 { "japanese", "ja" },
1370 { "korean", "ko" },
1371 { "norwegian", "no" },
1372 { "polish", "pl" },
1373 { "portuguese", "pt" },
1374 { "russian", "ru" },
1375 { "simplified chinese", "zh_CN" },
1376 { "slovak", "sk" },
1377 { "spanish", "es" },
1378 { "swedish", "sv" },
1379 { "traditional chinese", "zh_TW" },
1380 { "turkish", "tr" }
1381 };
1382
1383
1384 fprintf(stderr, "DEBUG: [cups-driverd] Loading \"%s\"...\n", d);
1385
1386 if ((dir = cupsDirOpen(d)) == NULL)
1387 {
1388 if (errno != ENOENT)
1389 fprintf(stderr,
1390 "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
1391 d, strerror(errno));
1392
1393 return (0);
1394 }
1395
1396 while ((dent = cupsDirRead(dir)) != NULL)
1397 {
1398 /*
1399 * Skip files/directories starting with "."...
1400 */
1401
1402 if (dent->filename[0] == '.')
1403 continue;
1404
1405 /*
1406 * See if this is a file...
1407 */
1408
1409 snprintf(filename, sizeof(filename), "%s/%s", d, dent->filename);
1410
1411 if (p[0])
1412 snprintf(name, sizeof(name), "%s/%s", p, dent->filename);
1413 else
1414 strlcpy(name, dent->filename, sizeof(name));
1415
1416 if (S_ISDIR(dent->fileinfo.st_mode))
1417 {
1418 /*
1419 * Do subdirectory...
1420 */
1421
1422 if (descend)
1423 if (!load_ppds(filename, name, 1))
1424 {
1425 cupsDirClose(dir);
1426 return (1);
1427 }
1428
1429 continue;
1430 }
1431
1432 /*
1433 * See if this file has been scanned before...
1434 */
1435
1436 strcpy(key.record.filename, name);
1437 strcpy(key.record.name, name);
1438
1439 ppd = (ppd_info_t *)cupsArrayFind(PPDsByName, &key);
1440
1441 if (ppd &&
1442 ppd->record.size == dent->fileinfo.st_size &&
1443 ppd->record.mtime == dent->fileinfo.st_mtime)
1444 {
1445 do
1446 {
1447 ppd->found = 1;
1448 }
1449 while ((ppd = (ppd_info_t *)cupsArrayNext(PPDsByName)) != NULL &&
1450 !strcmp(ppd->record.filename, name));
1451
1452 continue;
1453 }
1454
1455 /*
1456 * No, file is new/changed, so re-scan it...
1457 */
1458
1459 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1460 continue;
1461
1462 /*
1463 * Now see if this is a PPD file...
1464 */
1465
1466 line[0] = '\0';
1467 cupsFileGets(fp, line, sizeof(line));
1468
1469 if (strncmp(line, "*PPD-Adobe:", 11))
1470 {
1471 /*
1472 * Nope, treat it as a driver information file...
1473 */
1474
1475 load_drv(filename, name, fp, dent->fileinfo.st_mtime,
1476 dent->fileinfo.st_size);
1477 continue;
1478 }
1479
1480 /*
1481 * Now read until we get the NickName field...
1482 */
1483
1484 cups_languages = cupsArrayNew(NULL, NULL);
1485 products = cupsArrayNew(NULL, NULL);
1486 psversions = cupsArrayNew(NULL, NULL);
1487
1488 model_name[0] = '\0';
1489 nick_name[0] = '\0';
1490 manufacturer[0] = '\0';
1491 device_id[0] = '\0';
1492 lang_encoding[0] = '\0';
1493 strcpy(lang_version, "en");
1494 model_number = 0;
1495 type = PPD_TYPE_POSTSCRIPT;
1496
1497 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
1498 {
1499 if (!strncmp(line, "*Manufacturer:", 14))
1500 sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
1501 else if (!strncmp(line, "*ModelName:", 11))
1502 sscanf(line, "%*[^\"]\"%127[^\"]", model_name);
1503 else if (!strncmp(line, "*LanguageEncoding:", 18))
1504 sscanf(line, "%*[^:]:%63s", lang_encoding);
1505 else if (!strncmp(line, "*LanguageVersion:", 17))
1506 sscanf(line, "%*[^:]:%63s", lang_version);
1507 else if (!strncmp(line, "*NickName:", 10))
1508 sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
1509 else if (!strncasecmp(line, "*1284DeviceID:", 14))
1510 {
1511 sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
1512
1513 // Make sure device ID ends with a semicolon...
1514 if (device_id[0] && device_id[strlen(device_id) - 1] != ';')
1515 strlcat(device_id, ";", sizeof(device_id));
1516 }
1517 else if (!strncmp(line, "*Product:", 9))
1518 {
1519 if (sscanf(line, "%*[^\"]\"(%255[^\"]", product) == 1)
1520 {
1521 /*
1522 * Make sure the value ends with a right parenthesis - can't stop at
1523 * the first right paren since the product name may contain escaped
1524 * parenthesis...
1525 */
1526
1527 ptr = product + strlen(product) - 1;
1528 if (ptr > product && *ptr == ')')
1529 {
1530 /*
1531 * Yes, ends with a parenthesis, so remove it from the end and
1532 * add the product to the list...
1533 */
1534
1535 *ptr = '\0';
1536 cupsArrayAdd(products, strdup(product));
1537 }
1538 }
1539 }
1540 else if (!strncmp(line, "*PSVersion:", 11))
1541 {
1542 sscanf(line, "%*[^\"]\"%255[^\"]", psversion);
1543 cupsArrayAdd(psversions, strdup(psversion));
1544 }
1545 else if (!strncmp(line, "*cupsLanguages:", 15))
1546 {
1547 char *start; /* Start of language */
1548
1549
1550 for (start = line + 15; *start && isspace(*start & 255); start ++);
1551
1552 if (*start++ == '\"')
1553 {
1554 while (*start)
1555 {
1556 for (ptr = start + 1;
1557 *ptr && *ptr != '\"' && !isspace(*ptr & 255);
1558 ptr ++);
1559
1560 if (*ptr)
1561 {
1562 *ptr++ = '\0';
1563
1564 while (isspace(*ptr & 255))
1565 *ptr++ = '\0';
1566 }
1567
1568 cupsArrayAdd(cups_languages, strdup(start));
1569 start = ptr;
1570 }
1571 }
1572 }
1573 else if (!strncmp(line, "*cupsFax:", 9))
1574 {
1575 for (ptr = line + 9; isspace(*ptr & 255); ptr ++);
1576
1577 if (!strncasecmp(ptr, "true", 4))
1578 type = PPD_TYPE_FAX;
1579 }
1580 else if (!strncmp(line, "*cupsFilter:", 12) && type == PPD_TYPE_POSTSCRIPT)
1581 {
1582 if (strstr(line + 12, "application/vnd.cups-raster"))
1583 type = PPD_TYPE_RASTER;
1584 else if (strstr(line + 12, "application/vnd.cups-pdf"))
1585 type = PPD_TYPE_PDF;
1586 }
1587 else if (!strncmp(line, "*cupsModelNumber:", 17))
1588 sscanf(line, "*cupsModelNumber:%d", &model_number);
1589 else if (!strncmp(line, "*OpenUI", 7))
1590 {
1591 /*
1592 * Stop early if we have a NickName or ModelName attributes
1593 * before the first OpenUI...
1594 */
1595
1596 if ((model_name[0] || nick_name[0]) && cupsArrayCount(products) > 0 &&
1597 cupsArrayCount(psversions) > 0)
1598 break;
1599 }
1600 }
1601
1602 /*
1603 * Close the file...
1604 */
1605
1606 cupsFileClose(fp);
1607
1608 /*
1609 * See if we got all of the required info...
1610 */
1611
1612 if (nick_name[0])
1613 cupsCharsetToUTF8((cups_utf8_t *)make_model, nick_name,
1614 sizeof(make_model), _ppdGetEncoding(lang_encoding));
1615 else
1616 strcpy(make_model, model_name);
1617
1618 while (isspace(make_model[0] & 255))
1619 _cups_strcpy(make_model, make_model + 1);
1620
1621 if (!make_model[0] || cupsArrayCount(products) == 0 ||
1622 cupsArrayCount(psversions) == 0)
1623 {
1624 /*
1625 * We don't have all the info needed, so skip this file...
1626 */
1627
1628 if (!make_model[0])
1629 fprintf(stderr, "WARNING: Missing NickName and ModelName in %s!\n",
1630 filename);
1631
1632 if (cupsArrayCount(products) == 0)
1633 fprintf(stderr, "WARNING: Missing Product in %s!\n", filename);
1634
1635 if (cupsArrayCount(psversions) == 0)
1636 fprintf(stderr, "WARNING: Missing PSVersion in %s!\n", filename);
1637
1638 free_array(products);
1639 free_array(psversions);
1640 free_array(cups_languages);
1641
1642 continue;
1643 }
1644
1645 if (model_name[0])
1646 cupsArrayAdd(products, strdup(model_name));
1647
1648 /*
1649 * Normalize the make and model string...
1650 */
1651
1652 while (isspace(manufacturer[0] & 255))
1653 _cups_strcpy(manufacturer, manufacturer + 1);
1654
1655 if (!strncasecmp(make_model, manufacturer, strlen(manufacturer)))
1656 strlcpy(temp, make_model, sizeof(temp));
1657 else
1658 snprintf(temp, sizeof(temp), "%s %s", manufacturer, make_model);
1659
1660 _ppdNormalizeMakeAndModel(temp, make_model, sizeof(make_model));
1661
1662 /*
1663 * See if we got a manufacturer...
1664 */
1665
1666 if (!manufacturer[0] || !strcmp(manufacturer, "ESP"))
1667 {
1668 /*
1669 * Nope, copy the first part of the make and model then...
1670 */
1671
1672 strlcpy(manufacturer, make_model, sizeof(manufacturer));
1673
1674 /*
1675 * Truncate at the first space, dash, or slash, or make the
1676 * manufacturer "Other"...
1677 */
1678
1679 for (ptr = manufacturer; *ptr; ptr ++)
1680 if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
1681 break;
1682
1683 if (*ptr && ptr > manufacturer)
1684 *ptr = '\0';
1685 else
1686 strcpy(manufacturer, "Other");
1687 }
1688 else if (!strncasecmp(manufacturer, "LHAG", 4) ||
1689 !strncasecmp(manufacturer, "linotype", 8))
1690 strcpy(manufacturer, "LHAG");
1691 else if (!strncasecmp(manufacturer, "Hewlett", 7))
1692 strcpy(manufacturer, "HP");
1693
1694 /*
1695 * Fix the lang_version as needed...
1696 */
1697
1698 if ((ptr = strchr(lang_version, '-')) != NULL)
1699 *ptr++ = '\0';
1700 else if ((ptr = strchr(lang_version, '_')) != NULL)
1701 *ptr++ = '\0';
1702
1703 if (ptr)
1704 {
1705 /*
1706 * Setup the country suffix...
1707 */
1708
1709 country[0] = '_';
1710 _cups_strcpy(country + 1, ptr);
1711 }
1712 else
1713 {
1714 /*
1715 * No country suffix...
1716 */
1717
1718 country[0] = '\0';
1719 }
1720
1721 for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
1722 if (!strcasecmp(languages[i].version, lang_version))
1723 break;
1724
1725 if (i < (int)(sizeof(languages) / sizeof(languages[0])))
1726 {
1727 /*
1728 * Found a known language...
1729 */
1730
1731 snprintf(lang_version, sizeof(lang_version), "%s%s",
1732 languages[i].language, country);
1733 }
1734 else
1735 {
1736 /*
1737 * Unknown language; use "xx"...
1738 */
1739
1740 strcpy(lang_version, "xx");
1741 }
1742
1743 /*
1744 * Record the PPD file...
1745 */
1746
1747 new_ppd = !ppd;
1748
1749 if (new_ppd)
1750 {
1751 /*
1752 * Add new PPD file...
1753 */
1754
1755 fprintf(stderr, "DEBUG2: [cups-driverd] Adding ppd \"%s\"...\n", name);
1756
1757 ppd = add_ppd(name, name, lang_version, manufacturer, make_model,
1758 device_id, (char *)cupsArrayFirst(products),
1759 (char *)cupsArrayFirst(psversions),
1760 dent->fileinfo.st_mtime, dent->fileinfo.st_size,
1761 model_number, type, "file");
1762
1763 if (!ppd)
1764 {
1765 cupsDirClose(dir);
1766 return (0);
1767 }
1768 }
1769 else
1770 {
1771 /*
1772 * Update existing record...
1773 */
1774
1775 fprintf(stderr, "DEBUG2: [cups-driverd] Updating ppd \"%s\"...\n", name);
1776
1777 memset(ppd, 0, sizeof(ppd_info_t));
1778
1779 ppd->found = 1;
1780 ppd->record.mtime = dent->fileinfo.st_mtime;
1781 ppd->record.size = dent->fileinfo.st_size;
1782 ppd->record.model_number = model_number;
1783 ppd->record.type = type;
1784
1785 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
1786 strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make));
1787 strlcpy(ppd->record.make_and_model, make_model,
1788 sizeof(ppd->record.make_and_model));
1789 strlcpy(ppd->record.languages[0], lang_version,
1790 sizeof(ppd->record.languages[0]));
1791 strlcpy(ppd->record.products[0], (char *)cupsArrayFirst(products),
1792 sizeof(ppd->record.products[0]));
1793 strlcpy(ppd->record.psversions[0], (char *)cupsArrayFirst(psversions),
1794 sizeof(ppd->record.psversions[0]));
1795 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
1796 }
1797
1798 /*
1799 * Add remaining products, versions, and languages...
1800 */
1801
1802 for (i = 1;
1803 i < PPD_MAX_PROD && (ptr = (char *)cupsArrayNext(products)) != NULL;
1804 i ++)
1805 strlcpy(ppd->record.products[i], ptr,
1806 sizeof(ppd->record.products[0]));
1807
1808 for (i = 1;
1809 i < PPD_MAX_VERS && (ptr = (char *)cupsArrayNext(psversions)) != NULL;
1810 i ++)
1811 strlcpy(ppd->record.psversions[i], ptr,
1812 sizeof(ppd->record.psversions[0]));
1813
1814 for (i = 1, ptr = (char *)cupsArrayFirst(cups_languages);
1815 i < PPD_MAX_LANG && ptr;
1816 i ++, ptr = (char *)cupsArrayNext(cups_languages))
1817 strlcpy(ppd->record.languages[i], ptr,
1818 sizeof(ppd->record.languages[0]));
1819
1820 /*
1821 * Free products, versions, and languages...
1822 */
1823
1824 free_array(cups_languages);
1825 free_array(products);
1826 free_array(psversions);
1827
1828 ChangedPPD = 1;
1829 }
1830
1831 cupsDirClose(dir);
1832
1833 return (1);
1834 }
1835
1836
1837 /*
1838 * 'load_drv()' - Load the PPDs from a driver information file.
1839 */
1840
1841 static int /* O - 1 on success, 0 on failure */
1842 load_drv(const char *filename, /* I - Actual filename */
1843 const char *name, /* I - Name to the rest of the world */
1844 cups_file_t *fp, /* I - File to read from */
1845 time_t mtime, /* I - Mod time of driver info file */
1846 off_t size) /* I - Size of driver info file */
1847 {
1848 ppdcSource *src; // Driver information file
1849 ppdcDriver *d; // Current driver
1850 ppdcAttr *device_id, // 1284DeviceID attribute
1851 *product, // Current product value
1852 *ps_version, // PSVersion attribute
1853 *cups_fax, // cupsFax attribute
1854 *nick_name; // NickName attribute
1855 ppdcFilter *filter; // Current filter
1856 bool product_found; // Found product?
1857 char uri[1024], // Driver URI
1858 make_model[1024]; // Make and model
1859 int type; // Driver type
1860
1861
1862 /*
1863 * Load the driver info file...
1864 */
1865
1866 src = new ppdcSource(filename, fp);
1867
1868 if (src->drivers->count == 0)
1869 {
1870 fprintf(stderr,
1871 "ERROR: [cups-driverd] Bad driver information file \"%s\"!\n",
1872 filename);
1873 src->release();
1874 return (0);
1875 }
1876
1877 /*
1878 * Add a dummy entry for the file...
1879 */
1880
1881 add_ppd(filename, filename, "", "", "", "", "", "", mtime, size, 0,
1882 PPD_TYPE_DRV, "drv");
1883 ChangedPPD = 1;
1884
1885 /*
1886 * Then the drivers in the file...
1887 */
1888
1889 for (d = (ppdcDriver *)src->drivers->first();
1890 d;
1891 d = (ppdcDriver *)src->drivers->next())
1892 {
1893 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "drv", "", "", 0,
1894 "/%s/%s", name,
1895 d->file_name ? d->file_name->value :
1896 d->pc_file_name->value);
1897
1898 device_id = d->find_attr("1284DeviceID", NULL);
1899 ps_version = d->find_attr("PSVersion", NULL);
1900 nick_name = d->find_attr("NickName", NULL);
1901
1902 if (nick_name)
1903 strlcpy(make_model, nick_name->value->value, sizeof(make_model));
1904 else if (strncasecmp(d->model_name->value, d->manufacturer->value,
1905 strlen(d->manufacturer->value)))
1906 snprintf(make_model, sizeof(make_model), "%s %s, %s",
1907 d->manufacturer->value, d->model_name->value,
1908 d->version->value);
1909 else
1910 snprintf(make_model, sizeof(make_model), "%s, %s", d->model_name->value,
1911 d->version->value);
1912
1913 if ((cups_fax = d->find_attr("cupsFax", NULL)) != NULL &&
1914 !strcasecmp(cups_fax->value->value, "true"))
1915 type = PPD_TYPE_FAX;
1916 else if (d->type == PPDC_DRIVER_PS)
1917 type = PPD_TYPE_POSTSCRIPT;
1918 else if (d->type != PPDC_DRIVER_CUSTOM)
1919 type = PPD_TYPE_RASTER;
1920 else
1921 {
1922 for (filter = (ppdcFilter *)d->filters->first(),
1923 type = PPD_TYPE_POSTSCRIPT;
1924 filter;
1925 filter = (ppdcFilter *)d->filters->next())
1926 if (strcasecmp(filter->mime_type->value, "application/vnd.cups-raster"))
1927 type = PPD_TYPE_RASTER;
1928 else if (strcasecmp(filter->mime_type->value,
1929 "application/vnd.cups-pdf"))
1930 type = PPD_TYPE_PDF;
1931 }
1932
1933 for (product = (ppdcAttr *)d->attrs->first(), product_found = false;
1934 product;
1935 product = (ppdcAttr *)d->attrs->next())
1936 if (!strcmp(product->name->value, "Product"))
1937 {
1938 product_found = true;
1939
1940 add_ppd(filename, uri, "en", d->manufacturer->value, make_model,
1941 device_id ? device_id->value->value : "",
1942 product->value->value,
1943 ps_version ? ps_version->value->value : "(3010) 0",
1944 mtime, size, d->model_number, type, "drv");
1945 }
1946
1947 if (!product_found)
1948 add_ppd(filename, uri, "en", d->manufacturer->value, make_model,
1949 device_id ? device_id->value->value : "",
1950 d->model_name->value,
1951 ps_version ? ps_version->value->value : "(3010) 0",
1952 mtime, size, d->model_number, type, "drv");
1953 }
1954
1955 src->release();
1956
1957 return (1);
1958 }
1959
1960
1961 /*
1962 * 'load_drivers()' - Load driver-generated PPD files.
1963 */
1964
1965 static int /* O - 1 on success, 0 on failure */
1966 load_drivers(cups_array_t *include, /* I - Drivers to include */
1967 cups_array_t *exclude) /* I - Drivers to exclude */
1968 {
1969 int i; /* Looping var */
1970 char *start, /* Start of value */
1971 *ptr; /* Pointer into string */
1972 const char *server_bin, /* CUPS_SERVERBIN env variable */
1973 *scheme, /* Scheme for this driver */
1974 *scheme_end; /* Pointer to end of scheme */
1975 char drivers[1024]; /* Location of driver programs */
1976 int pid; /* Process ID for driver program */
1977 cups_file_t *fp; /* Pipe to driver program */
1978 cups_dir_t *dir; /* Directory pointer */
1979 cups_dentry_t *dent; /* Directory entry */
1980 char *argv[3], /* Arguments for command */
1981 filename[1024], /* Name of driver */
1982 line[2048], /* Line from driver */
1983 name[512], /* ppd-name */
1984 make[128], /* ppd-make */
1985 make_and_model[128], /* ppd-make-and-model */
1986 device_id[128], /* ppd-device-id */
1987 languages[128], /* ppd-natural-language */
1988 product[128], /* ppd-product */
1989 psversion[128], /* ppd-psversion */
1990 type_str[128]; /* ppd-type */
1991 int type; /* PPD type */
1992 ppd_info_t *ppd; /* Newly added PPD */
1993
1994
1995 /*
1996 * Try opening the driver directory...
1997 */
1998
1999 if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
2000 server_bin = CUPS_SERVERBIN;
2001
2002 snprintf(drivers, sizeof(drivers), "%s/driver", server_bin);
2003
2004 if ((dir = cupsDirOpen(drivers)) == NULL)
2005 {
2006 fprintf(stderr, "ERROR: [cups-driverd] Unable to open driver directory "
2007 "\"%s\": %s\n",
2008 drivers, strerror(errno));
2009 return (0);
2010 }
2011
2012 /*
2013 * Loop through all of the device drivers...
2014 */
2015
2016 argv[1] = (char *)"list";
2017 argv[2] = NULL;
2018
2019 while ((dent = cupsDirRead(dir)) != NULL)
2020 {
2021 /*
2022 * Only look at executable files...
2023 */
2024
2025 if (!(dent->fileinfo.st_mode & 0111) || !S_ISREG(dent->fileinfo.st_mode))
2026 continue;
2027
2028 /*
2029 * Include/exclude specific drivers...
2030 */
2031
2032 if (exclude)
2033 {
2034 /*
2035 * Look for "scheme" or "scheme*" (prefix match), and skip any matches.
2036 */
2037
2038 for (scheme = (char *)cupsArrayFirst(exclude);
2039 scheme;
2040 scheme = (char *)cupsArrayNext(exclude))
2041 {
2042 fprintf(stderr, "DEBUG: [cups-driverd] Exclude \"%s\" with \"%s\"?\n",
2043 dent->filename, scheme);
2044 scheme_end = scheme + strlen(scheme) - 1;
2045
2046 if ((scheme_end > scheme && *scheme_end == '*' &&
2047 !strncmp(scheme, dent->filename, scheme_end - scheme)) ||
2048 !strcmp(scheme, dent->filename))
2049 {
2050 fputs("DEBUG: [cups-driverd] Yes, exclude!\n", stderr);
2051 break;
2052 }
2053 }
2054
2055 if (scheme)
2056 continue;
2057 }
2058
2059 if (include)
2060 {
2061 /*
2062 * Look for "scheme" or "scheme*" (prefix match), and skip any non-matches.
2063 */
2064
2065 for (scheme = (char *)cupsArrayFirst(include);
2066 scheme;
2067 scheme = (char *)cupsArrayNext(include))
2068 {
2069 fprintf(stderr, "DEBUG: [cups-driverd] Include \"%s\" with \"%s\"?\n",
2070 dent->filename, scheme);
2071 scheme_end = scheme + strlen(scheme) - 1;
2072
2073 if ((scheme_end > scheme && *scheme_end == '*' &&
2074 !strncmp(scheme, dent->filename, scheme_end - scheme)) ||
2075 !strcmp(scheme, dent->filename))
2076 {
2077 fputs("DEBUG: [cups-driverd] Yes, include!\n", stderr);
2078 break;
2079 }
2080 }
2081
2082 if (!scheme)
2083 continue;
2084 }
2085 else
2086 scheme = dent->filename;
2087
2088 /*
2089 * Run the driver with no arguments and collect the output...
2090 */
2091
2092 argv[0] = dent->filename;
2093 snprintf(filename, sizeof(filename), "%s/%s", drivers, dent->filename);
2094
2095 if ((fp = cupsdPipeCommand(&pid, filename, argv, 0)) != NULL)
2096 {
2097 while (cupsFileGets(fp, line, sizeof(line)))
2098 {
2099 /*
2100 * Each line is of the form:
2101 *
2102 * "ppd-name" ppd-natural-language "ppd-make" "ppd-make-and-model" \
2103 * "ppd-device-id" "ppd-product" "ppd-psversion"
2104 */
2105
2106 device_id[0] = '\0';
2107 product[0] = '\0';
2108 psversion[0] = '\0';
2109 strcpy(type_str, "postscript");
2110
2111 if (sscanf(line, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\""
2112 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
2113 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
2114 "%*[ \t]\"%127[^\"]\"",
2115 name, languages, make, make_and_model,
2116 device_id, product, psversion, type_str) < 4)
2117 {
2118 /*
2119 * Bad format; strip trailing newline and write an error message.
2120 */
2121
2122 if (line[strlen(line) - 1] == '\n')
2123 line[strlen(line) - 1] = '\0';
2124
2125 fprintf(stderr, "ERROR: [cups-driverd] Bad line from \"%s\": %s\n",
2126 dent->filename, line);
2127 break;
2128 }
2129 else
2130 {
2131 /*
2132 * Add the device to the array of available devices...
2133 */
2134
2135 if ((start = strchr(languages, ',')) != NULL)
2136 *start++ = '\0';
2137
2138 for (type = 0;
2139 type < (int)(sizeof(ppd_types) / sizeof(ppd_types[0]));
2140 type ++)
2141 if (!strcmp(type_str, ppd_types[type]))
2142 break;
2143
2144 if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0])))
2145 {
2146 fprintf(stderr,
2147 "ERROR: [cups-driverd] Bad ppd-type \"%s\" ignored!\n",
2148 type_str);
2149 type = PPD_TYPE_UNKNOWN;
2150 }
2151
2152 ppd = add_ppd(filename, name, languages, make, make_and_model,
2153 device_id, product, psversion, 0, 0, 0, type, scheme);
2154
2155 if (!ppd)
2156 {
2157 cupsDirClose(dir);
2158 cupsFileClose(fp);
2159 return (0);
2160 }
2161
2162 if (start && *start)
2163 {
2164 for (i = 1; i < PPD_MAX_LANG && *start; i ++)
2165 {
2166 if ((ptr = strchr(start, ',')) != NULL)
2167 *ptr++ = '\0';
2168 else
2169 ptr = start + strlen(start);
2170
2171 strlcpy(ppd->record.languages[i], start,
2172 sizeof(ppd->record.languages[0]));
2173
2174 start = ptr;
2175 }
2176 }
2177
2178 fprintf(stderr, "DEBUG2: [cups-driverd] Added dynamic PPD \"%s\"...\n",
2179 name);
2180 }
2181 }
2182
2183 cupsFileClose(fp);
2184 }
2185 else
2186 fprintf(stderr, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n",
2187 filename, strerror(errno));
2188 }
2189
2190 cupsDirClose(dir);
2191
2192 return (1);
2193 }
2194
2195
2196 /*
2197 * 'load_ppds_dat()' - Load the ppds.dat file.
2198 */
2199
2200 static void
2201 load_ppds_dat(char *filename, /* I - Filename buffer */
2202 size_t filesize, /* I - Size of filename buffer */
2203 int verbose) /* I - Be verbose? */
2204 {
2205 ppd_info_t *ppd; /* Current PPD file */
2206 cups_file_t *fp; /* ppds.dat file */
2207 struct stat fileinfo; /* ppds.dat information */
2208 const char *cups_cachedir; /* CUPS_CACHEDIR environment variable */
2209
2210
2211 PPDsByName = cupsArrayNew((cups_array_func_t)compare_names, NULL);
2212 PPDsByMakeModel = cupsArrayNew((cups_array_func_t)compare_ppds, NULL);
2213 ChangedPPD = 0;
2214
2215 if ((cups_cachedir = getenv("CUPS_CACHEDIR")) == NULL)
2216 cups_cachedir = CUPS_CACHEDIR;
2217
2218 snprintf(filename, filesize, "%s/ppds.dat", cups_cachedir);
2219 if ((fp = cupsFileOpen(filename, "r")) != NULL)
2220 {
2221 /*
2222 * See if we have the right sync word...
2223 */
2224
2225 unsigned ppdsync; /* Sync word */
2226 int num_ppds; /* Number of PPDs */
2227
2228
2229 if (cupsFileRead(fp, (char *)&ppdsync, sizeof(ppdsync))
2230 == sizeof(ppdsync) &&
2231 ppdsync == PPD_SYNC &&
2232 !stat(filename, &fileinfo) &&
2233 ((fileinfo.st_size - sizeof(ppdsync)) % sizeof(ppd_rec_t)) == 0 &&
2234 (num_ppds = (fileinfo.st_size - sizeof(ppdsync)) /
2235 sizeof(ppd_rec_t)) > 0)
2236 {
2237 /*
2238 * We have a ppds.dat file, so read it!
2239 */
2240
2241 for (; num_ppds > 0; num_ppds --)
2242 {
2243 if ((ppd = (ppd_info_t *)calloc(1, sizeof(ppd_info_t))) == NULL)
2244 {
2245 if (verbose)
2246 fputs("ERROR: [cups-driverd] Unable to allocate memory for PPD!\n",
2247 stderr);
2248 exit(1);
2249 }
2250
2251 if (cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t)) > 0)
2252 {
2253 cupsArrayAdd(PPDsByName, ppd);
2254 cupsArrayAdd(PPDsByMakeModel, ppd);
2255 }
2256 else
2257 {
2258 free(ppd);
2259 break;
2260 }
2261 }
2262
2263 if (verbose)
2264 fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
2265 filename, cupsArrayCount(PPDsByName));
2266 }
2267
2268 cupsFileClose(fp);
2269 }
2270 }
2271
2272
2273 /*
2274 * 'regex_device_id()' - Compile a regular expression based on the 1284 device
2275 * ID.
2276 */
2277
2278 static regex_t * /* O - Regular expression */
2279 regex_device_id(const char *device_id) /* I - IEEE-1284 device ID */
2280 {
2281 char res[2048], /* Regular expression string */
2282 *ptr; /* Pointer into string */
2283 regex_t *re; /* Regular expression */
2284 int cmd; /* Command set string? */
2285
2286
2287 fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id(\"%s\")\n", device_id);
2288
2289 /*
2290 * Scan the device ID string and insert class, command set, manufacturer, and
2291 * model attributes to match. We assume that the device ID in the PPD and the
2292 * device ID reported by the device itself use the same attribute names and
2293 * order of attributes.
2294 */
2295
2296 ptr = res;
2297
2298 while (*device_id && ptr < (res + sizeof(res) - 6))
2299 {
2300 cmd = !strncasecmp(device_id, "COMMAND SET:", 12) ||
2301 !strncasecmp(device_id, "CMD:", 4);
2302
2303 if (cmd || !strncasecmp(device_id, "MANUFACTURER:", 13) ||
2304 !strncasecmp(device_id, "MFG:", 4) ||
2305 !strncasecmp(device_id, "MFR:", 4) ||
2306 !strncasecmp(device_id, "MODEL:", 6) ||
2307 !strncasecmp(device_id, "MDL:", 4))
2308 {
2309 if (ptr > res)
2310 {
2311 *ptr++ = '.';
2312 *ptr++ = '*';
2313 }
2314
2315 *ptr++ = '(';
2316
2317 while (*device_id && *device_id != ';' && ptr < (res + sizeof(res) - 4))
2318 {
2319 if (strchr("[]{}().*\\|", *device_id))
2320 *ptr++ = '\\';
2321 *ptr++ = *device_id++;
2322 }
2323
2324 if (*device_id == ';' || !*device_id)
2325 *ptr++ = ';';
2326 *ptr++ = ')';
2327 if (cmd)
2328 *ptr++ = '?';
2329 }
2330 else if ((device_id = strchr(device_id, ';')) == NULL)
2331 break;
2332 else
2333 device_id ++;
2334 }
2335
2336 *ptr = '\0';
2337
2338 fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id: \"%s\"\n", res);
2339
2340 /*
2341 * Compile the regular expression and return...
2342 */
2343
2344 if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
2345 {
2346 if (!regcomp(re, res, REG_EXTENDED | REG_ICASE))
2347 {
2348 fputs("DEBUG: [cups-driverd] regex_device_id: OK\n", stderr);
2349 return (re);
2350 }
2351
2352 free(re);
2353 }
2354
2355 return (NULL);
2356 }
2357
2358
2359 /*
2360 * 'regex_string()' - Construct a regular expression to compare a simple string.
2361 */
2362
2363 static regex_t * /* O - Regular expression */
2364 regex_string(const char *s) /* I - String to compare */
2365 {
2366 char res[2048], /* Regular expression string */
2367 *ptr; /* Pointer into string */
2368 regex_t *re; /* Regular expression */
2369
2370
2371 fprintf(stderr, "DEBUG: [cups-driverd] regex_string(\"%s\")\n", s);
2372
2373 /*
2374 * Convert the string to a regular expression, escaping special characters
2375 * as needed.
2376 */
2377
2378 ptr = res;
2379
2380 while (*s && ptr < (res + sizeof(res) - 2))
2381 {
2382 if (strchr("[]{}().*\\", *s))
2383 *ptr++ = '\\';
2384
2385 *ptr++ = *s++;
2386 }
2387
2388 *ptr = '\0';
2389
2390 fprintf(stderr, "DEBUG: [cups-driverd] regex_string: \"%s\"\n", res);
2391
2392 /*
2393 * Create a case-insensitive regular expression...
2394 */
2395
2396 if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
2397 {
2398 if (!regcomp(re, res, REG_ICASE))
2399 {
2400 fputs("DEBUG: [cups-driverd] regex_string: OK\n", stderr);
2401 return (re);
2402 }
2403
2404 free(re);
2405 }
2406
2407 return (NULL);
2408 }
2409
2410
2411 /*
2412 * End of "$Id$".
2413 */