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