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