]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-driverd.c
Merge CUPS 1.4svn-r7319.
[thirdparty/cups.git] / scheduler / cups-driverd.c
1 /*
2 * "$Id: cups-driverd.c 6762 2007-08-02 18:05:03Z mike $"
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_ppd() - Copy a PPD file to stdout.
24 * compare_names() - Compare PPD filenames for sorting.
25 * compare_ppds() - Compare PPD file make and model names for sorting.
26 * free_array() - Free an array of strings.
27 * list_ppds() - List PPD files.
28 * load_ppds() - Load PPD files recursively.
29 * load_drivers() - Load driver-generated PPD files.
30 */
31
32 /*
33 * Include necessary headers...
34 */
35
36 #include "util.h"
37 #include <cups/dir.h>
38 #include <cups/transcode.h>
39
40
41 /*
42 * Private PPD functions...
43 */
44
45 extern cups_encoding_t _ppdGetEncoding(const char *name);
46
47
48 /*
49 * Constants...
50 */
51
52 #define PPD_SYNC 0x50504434 /* Sync word for ppds.dat (PPD4) */
53 #define PPD_MAX_LANG 32 /* Maximum languages */
54 #define PPD_MAX_PROD 8 /* Maximum products */
55 #define PPD_MAX_VERS 8 /* Maximum versions */
56
57 #define PPD_TYPE_POSTSCRIPT 0 /* PostScript PPD */
58 #define PPD_TYPE_PDF 1 /* PDF PPD */
59 #define PPD_TYPE_RASTER 2 /* CUPS raster PPD */
60 #define PPD_TYPE_FAX 3 /* Facsimile/MFD PPD */
61 #define PPD_TYPE_UNKNOWN 4 /* Other/hybrid PPD */
62
63 static const char * const ppd_types[] = /* ppd-type values */
64 {
65 "postscript",
66 "pdf",
67 "raster",
68 "fax",
69 "unknown"
70 };
71
72
73 /*
74 * PPD information structures...
75 */
76
77 typedef struct /**** PPD record ****/
78 {
79 time_t mtime; /* Modification time */
80 size_t size; /* Size in bytes */
81 int model_number; /* cupsModelNumber */
82 int type; /* ppd-type */
83 char name[512], /* PPD name */
84 languages[PPD_MAX_LANG][6],
85 /* LanguageVersion/cupsLanguages */
86 products[PPD_MAX_PROD][128],
87 /* Product strings */
88 psversions[PPD_MAX_VERS][32],
89 /* PSVersion strings */
90 make[128], /* Manufacturer */
91 make_and_model[128], /* NickName/ModelName */
92 device_id[128]; /* IEEE 1284 Device ID */
93 } ppd_rec_t;
94
95 typedef struct /**** In-memory record ****/
96 {
97 int found; /* 1 if PPD is found */
98 ppd_rec_t record; /* PPDs.dat record */
99 } ppd_info_t;
100
101
102 /*
103 * Globals...
104 */
105
106 int NumPPDs, /* Number of PPD files */
107 SortedPPDs, /* Number of sorted PPD files */
108 AllocPPDs; /* Number of allocated entries */
109 ppd_info_t *PPDs; /* PPD file info */
110 int ChangedPPD; /* Did we change the PPD database? */
111
112
113 /*
114 * Local functions...
115 */
116
117 static ppd_info_t *add_ppd(const char *name, const char *language,
118 const char *make, const char *make_and_model,
119 const char *device_id, const char *product,
120 const char *psversion, time_t mtime,
121 size_t size, int model_number, int type);
122 static int cat_ppd(const char *name, int request_id);
123 static int compare_names(const ppd_info_t *p0,
124 const ppd_info_t *p1);
125 static int compare_ppds(const ppd_info_t *p0,
126 const ppd_info_t *p1);
127 static void free_array(cups_array_t *a);
128 static int list_ppds(int request_id, int limit, const char *opt);
129 static int load_drivers(void);
130 static int load_ppds(const char *d, const char *p, int descend);
131
132
133 /*
134 * 'main()' - Scan for drivers and return an IPP response.
135 *
136 * Usage:
137 *
138 * cups-driverd request_id limit options
139 */
140
141 int /* O - Exit code */
142 main(int argc, /* I - Number of command-line args */
143 char *argv[]) /* I - Command-line arguments */
144 {
145 /*
146 * Install or list PPDs...
147 */
148
149 if (argc == 3 && !strcmp(argv[1], "cat"))
150 return (cat_ppd(argv[2], 0));
151 else if (argc == 4 && !strcmp(argv[1], "get"))
152 return (cat_ppd(argv[3], atoi(argv[2])));
153 else if (argc == 5 && !strcmp(argv[1], "list"))
154 return (list_ppds(atoi(argv[2]), atoi(argv[3]), argv[4]));
155 else
156 {
157 fputs("Usage: cups-driverd cat ppd-name\n", stderr);
158 fputs("Usage: cups-driverd get request_id ppd-name\n", stderr);
159 fputs("Usage: cups-driverd list request_id limit options\n", stderr);
160 return (1);
161 }
162 }
163
164
165 /*
166 * 'add_ppd()' - Add a PPD file.
167 */
168
169 static ppd_info_t * /* O - PPD */
170 add_ppd(const char *name, /* I - PPD name */
171 const char *language, /* I - LanguageVersion */
172 const char *make, /* I - Manufacturer */
173 const char *make_and_model, /* I - NickName/ModelName */
174 const char *device_id, /* I - 1284DeviceID */
175 const char *product, /* I - Product */
176 const char *psversion, /* I - PSVersion */
177 time_t mtime, /* I - Modification time */
178 size_t size, /* I - File size */
179 int model_number, /* I - Model number */
180 int type) /* I - Driver type */
181 {
182 ppd_info_t *ppd; /* PPD */
183 char *recommended; /* Foomatic driver string */
184
185
186 /*
187 * Add a new PPD file...
188 */
189
190 if (NumPPDs >= AllocPPDs)
191 {
192 /*
193 * Allocate (more) memory for the PPD files...
194 */
195
196 AllocPPDs += 128;
197
198 if (!PPDs)
199 ppd = malloc(sizeof(ppd_info_t) * AllocPPDs);
200 else
201 ppd = realloc(PPDs, sizeof(ppd_info_t) * AllocPPDs);
202
203 if (ppd == NULL)
204 {
205 fprintf(stderr,
206 "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
207 AllocPPDs);
208 return (NULL);
209 }
210
211 PPDs = ppd;
212 }
213
214 ppd = PPDs + NumPPDs;
215 NumPPDs ++;
216
217 /*
218 * Zero-out the PPD data and copy the values over...
219 */
220
221 memset(ppd, 0, sizeof(ppd_info_t));
222
223 ppd->found = 1;
224 ppd->record.mtime = mtime;
225 ppd->record.size = size;
226 ppd->record.model_number = model_number;
227 ppd->record.type = type;
228
229 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
230 strlcpy(ppd->record.languages[0], language,
231 sizeof(ppd->record.languages[0]));
232 strlcpy(ppd->record.products[0], product, sizeof(ppd->record.products[0]));
233 strlcpy(ppd->record.psversions[0], psversion,
234 sizeof(ppd->record.psversions[0]));
235 strlcpy(ppd->record.make, make, sizeof(ppd->record.make));
236 strlcpy(ppd->record.make_and_model, make_and_model,
237 sizeof(ppd->record.make_and_model));
238 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
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 * Return the new PPD pointer...
251 */
252
253 return (ppd);
254 }
255
256
257 /*
258 * 'cat_ppd()' - Copy a PPD file to stdout.
259 */
260
261 static int /* O - Exit code */
262 cat_ppd(const char *name, /* I - PPD name */
263 int request_id) /* I - Request ID for response? */
264 {
265 char scheme[256], /* Scheme from PPD name */
266 *sptr; /* Pointer into scheme */
267 char line[1024]; /* Line/filename */
268 char message[2048]; /* status-message */
269
270
271 /*
272 * Figure out if this is a static or dynamic PPD file...
273 */
274
275 strlcpy(scheme, name, sizeof(scheme));
276 if ((sptr = strchr(scheme, ':')) != NULL)
277 {
278 *sptr = '\0';
279
280 if (!strcmp(scheme, "file"))
281 {
282 /*
283 * "file:name" == "name"...
284 */
285
286 name += 5;
287 scheme[0] = '\0';
288 }
289 }
290 else
291 scheme[0] = '\0';
292
293 if (request_id > 0)
294 puts("Content-Type: application/ipp\n");
295
296 if (scheme[0])
297 {
298 /*
299 * Dynamic PPD, see if we have a driver program to support it...
300 */
301
302 const char *serverbin; /* CUPS_SERVERBIN env var */
303
304
305 if ((serverbin = getenv("CUPS_SERVERBIN")) == NULL)
306 serverbin = CUPS_SERVERBIN;
307
308 snprintf(line, sizeof(line), "%s/driver/%s", serverbin, scheme);
309 if (access(line, X_OK))
310 {
311 /*
312 * File does not exist or is not executable...
313 */
314
315 fprintf(stderr, "ERROR: [cups-driverd] Unable to access \"%s\" - %s\n",
316 line, strerror(errno));
317
318 if (request_id > 0)
319 {
320 snprintf(message, sizeof(message), "Unable to access \"%s\" - %s",
321 line, strerror(errno));
322
323 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
324 cupsdSendIPPGroup(IPP_TAG_OPERATION);
325 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
326 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
327 "en-US");
328 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
329 cupsdSendIPPTrailer();
330 }
331
332 return (1);
333 }
334
335 /*
336 * Yes, let it cat the PPD file...
337 */
338
339 if (request_id)
340 {
341 cupsdSendIPPHeader(IPP_OK, request_id);
342 cupsdSendIPPGroup(IPP_TAG_OPERATION);
343 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
344 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
345 "en-US");
346 cupsdSendIPPTrailer();
347 }
348
349 if (execl(line, scheme, "cat", name, (char *)NULL))
350 {
351 /*
352 * Unable to execute driver...
353 */
354
355 fprintf(stderr, "ERROR: [cups-driverd] Unable to execute \"%s\" - %s\n",
356 line, strerror(errno));
357 return (1);
358 }
359 }
360 else
361 {
362 /*
363 * Static PPD, see if we have a valid path and it exists...
364 */
365
366 cups_file_t *fp; /* PPD file */
367 const char *datadir; /* CUPS_DATADIR env var */
368
369
370 if (name[0] == '/' || strstr(name, "../") || strstr(name, "/.."))
371 {
372 /*
373 * Bad name...
374 */
375
376 fprintf(stderr, "ERROR: [cups-driverd] Bad PPD name \"%s\"!\n", name);
377
378 if (request_id)
379 {
380 snprintf(message, sizeof(message), "Bad PPD name \"%s\"!", name);
381
382 cupsdSendIPPHeader(IPP_OK, 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 return (1);
392 }
393
394 /*
395 * Try opening the file...
396 */
397
398 #ifdef __APPLE__
399 if (!strncmp(name, "System/Library/Printers/PPDs/Contents/Resources/", 48) ||
400 !strncmp(name, "Library/Printers/PPDs/Contents/Resources/", 41))
401 {
402 /*
403 * Map ppd-name to Mac OS X standard locations...
404 */
405
406 snprintf(line, sizeof(line), "/%s", name);
407 }
408 else
409
410 #elif defined(__linux)
411 if (!strncmp(name, "lsb/usr/", 8))
412 {
413 /*
414 * Map ppd-name to LSB standard /usr/share/ppd location...
415 */
416
417 snprintf(line, sizeof(line), "/usr/share/ppd/%s", name + 8);
418 }
419 else if (!strncmp(name, "lsb/opt/", 8))
420 {
421 /*
422 * Map ppd-name to LSB standard /opt/share/ppd location...
423 */
424
425 snprintf(line, sizeof(line), "/opt/share/ppd/%s", name + 8);
426 }
427 else if (!strncmp(name, "lsb/local/", 10))
428 {
429 /*
430 * Map ppd-name to LSB standard /usr/local/share/ppd location...
431 */
432
433 snprintf(line, sizeof(line), "/usr/local/share/ppd/%s", name + 10);
434 }
435 else
436
437 #endif /* __APPLE__ */
438 {
439 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
440 datadir = CUPS_DATADIR;
441
442 snprintf(line, sizeof(line), "%s/model/%s", datadir, name);
443 }
444
445 if ((fp = cupsFileOpen(line, "r")) == NULL)
446 {
447 fprintf(stderr, "ERROR: [cups-driverd] Unable to open \"%s\" - %s\n",
448 line, strerror(errno));
449
450 if (request_id)
451 {
452 snprintf(message, sizeof(message), "Unable to open \"%s\" - %s",
453 line, strerror(errno));
454
455 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
456 cupsdSendIPPGroup(IPP_TAG_OPERATION);
457 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
458 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
459 "en-US");
460 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
461 cupsdSendIPPTrailer();
462 }
463
464 return (1);
465 }
466
467 if (request_id)
468 {
469 cupsdSendIPPHeader(IPP_OK, 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 cupsdSendIPPTrailer();
475 }
476
477 /*
478 * Now copy the file to stdout...
479 */
480
481 while (cupsFileGets(fp, line, sizeof(line)))
482 puts(line);
483
484 cupsFileClose(fp);
485 }
486
487 /*
488 * Return with no errors...
489 */
490
491 return (0);
492 }
493
494
495 /*
496 * 'compare_names()' - Compare PPD filenames for sorting.
497 */
498
499 static int /* O - Result of comparison */
500 compare_names(const ppd_info_t *p0, /* I - First PPD file */
501 const ppd_info_t *p1) /* I - Second PPD file */
502 {
503 return (strcasecmp(p0->record.name, p1->record.name));
504 }
505
506
507 /*
508 * 'compare_ppds()' - Compare PPD file make and model names for sorting.
509 */
510
511 static int /* O - Result of comparison */
512 compare_ppds(const ppd_info_t *p0, /* I - First PPD file */
513 const ppd_info_t *p1) /* I - Second PPD file */
514 {
515 int diff; /* Difference between strings */
516
517
518 /*
519 * First compare manufacturers...
520 */
521
522 if ((diff = strcasecmp(p0->record.make, p1->record.make)) != 0)
523 return (diff);
524 else if ((diff = cupsdCompareNames(p0->record.make_and_model,
525 p1->record.make_and_model)) != 0)
526 return (diff);
527 else
528 return (strcasecmp(p0->record.languages[0],
529 p1->record.languages[0]));
530 }
531
532
533 /*
534 * 'free_array()' - Free an array of strings.
535 */
536
537 static void
538 free_array(cups_array_t *a) /* I - Array to free */
539 {
540 char *ptr; /* Pointer to string */
541
542
543 for (ptr = (char *)cupsArrayFirst(a);
544 ptr;
545 ptr = (char *)cupsArrayNext(a))
546 free(ptr);
547
548 cupsArrayDelete(a);
549 }
550
551
552 /*
553 * 'list_ppds()' - List PPD files.
554 */
555
556 static int /* O - Exit code */
557 list_ppds(int request_id, /* I - Request ID */
558 int limit, /* I - Limit */
559 const char *opt) /* I - Option argument */
560 {
561 int i, j; /* Looping vars */
562 int count; /* Number of PPDs to send */
563 ppd_info_t *ppd; /* Current PPD file */
564 cups_file_t *fp; /* ppds.dat file */
565 struct stat fileinfo; /* ppds.dat information */
566 char filename[1024], /* ppds.dat filename */
567 model[1024]; /* Model directory */
568 const char *cups_cachedir; /* CUPS_CACHEDIR environment variable */
569 const char *cups_datadir; /* CUPS_DATADIR environment variable */
570 int num_options; /* Number of options */
571 cups_option_t *options; /* Options */
572 const char *requested, /* requested-attributes option */
573 *device_id, /* ppd-device-id option */
574 *language, /* ppd-natural-language option */
575 *make, /* ppd-make option */
576 *make_and_model, /* ppd-make-and-model option */
577 *model_number_str, /* ppd-model-number option */
578 *product, /* ppd-product option */
579 *psversion, /* ppd-psversion option */
580 *type_str; /* ppd-type option */
581 int model_number, /* ppd-model-number value */
582 type, /* ppd-type value */
583 mam_len, /* Length of ppd-make-and-model */
584 device_id_len, /* Length of ppd-device-id */
585 send_device_id, /* Send ppd-device-id? */
586 send_make, /* Send ppd-make? */
587 send_make_and_model, /* Send ppd-make-and-model? */
588 send_model_number, /* Send ppd-model-number? */
589 send_name, /* Send ppd-name? */
590 send_natural_language, /* Send ppd-natural-language? */
591 send_product, /* Send ppd-product? */
592 send_psversion, /* Send ppd-psversion? */
593 send_type, /* Send ppd-type? */
594 sent_header; /* Sent the IPP header? */
595
596
597 fprintf(stderr,
598 "DEBUG2: [cups-driverd] list_ppds(request_id=%d, limit=%d, "
599 "opt=\"%s\"\n", request_id, limit, opt);
600
601 /*
602 * See if we a PPD database file...
603 */
604
605 NumPPDs = 0;
606 AllocPPDs = 0;
607 PPDs = (ppd_info_t *)NULL;
608 ChangedPPD = 0;
609
610 if ((cups_cachedir = getenv("CUPS_CACHEDIR")) == NULL)
611 cups_cachedir = CUPS_CACHEDIR;
612
613 snprintf(filename, sizeof(filename), "%s/ppds.dat", cups_cachedir);
614 if ((fp = cupsFileOpen(filename, "r")) != NULL)
615 {
616 /*
617 * See if we have the right sync word...
618 */
619
620 unsigned ppdsync; /* Sync word */
621
622 if (cupsFileRead(fp, (char *)&ppdsync, sizeof(ppdsync))
623 == sizeof(ppdsync) &&
624 ppdsync == PPD_SYNC &&
625 !stat(filename, &fileinfo) &&
626 ((fileinfo.st_size - sizeof(ppdsync)) % sizeof(ppd_rec_t)) == 0 &&
627 (NumPPDs = (fileinfo.st_size - sizeof(ppdsync)) /
628 sizeof(ppd_rec_t)) > 0)
629 {
630 /*
631 * We have a ppds.dat file, so read it!
632 */
633
634 if ((PPDs = malloc(sizeof(ppd_info_t) * NumPPDs)) == NULL)
635 fprintf(stderr,
636 "ERROR: [cups-driverd] Unable to allocate memory for %d "
637 "PPD files!\n", NumPPDs);
638 else
639 {
640 AllocPPDs = NumPPDs;
641
642 for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
643 {
644 cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
645 ppd->found = 0;
646 }
647
648 fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
649 filename, NumPPDs);
650 }
651 }
652
653 cupsFileClose(fp);
654 }
655
656 /*
657 * Load all PPDs in the specified directory and below...
658 */
659
660 SortedPPDs = NumPPDs;
661
662 if ((cups_datadir = getenv("CUPS_DATADIR")) == NULL)
663 cups_datadir = CUPS_DATADIR;
664
665 snprintf(model, sizeof(model), "%s/model", cups_datadir);
666 load_ppds(model, "", 1);
667
668 #ifdef __APPLE__
669 /*
670 * Load PPDs from standard Mac OS X locations...
671 */
672
673 load_ppds("/Library/Printers/PPDs/Contents/Resources",
674 "Library/Printers/PPDs/Contents/Resources", 0);
675 load_ppds("/Library/Printers/PPDs/Contents/Resources/en.lproj",
676 "Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
677 load_ppds("/System/Library/Printers/PPDs/Contents/Resources",
678 "System/Library/Printers/PPDs/Contents/Resources", 0);
679 load_ppds("/System/Library/Printers/PPDs/Contents/Resources/en.lproj",
680 "System/Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
681
682 #elif defined(__linux)
683 /*
684 * Load PPDs from LSB-defined locations...
685 */
686
687 load_ppds("/usr/local/share/ppd", "lsb/local", 1);
688 load_ppds("/usr/share/ppd", "lsb/usr", 1);
689 load_ppds("/opt/share/ppd", "lsb/opt", 1);
690 #endif /* __APPLE__ */
691
692 /*
693 * Cull PPD files that are no longer present...
694 */
695
696 for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
697 if (!ppd->found)
698 {
699 /*
700 * Remove this PPD file from the list...
701 */
702
703 if (i > 1)
704 memmove(ppd, ppd + 1, (i - 1) * sizeof(ppd_info_t));
705
706 NumPPDs --;
707 ppd --;
708 }
709
710 /*
711 * Sort the PPDs by name...
712 */
713
714 if (NumPPDs > 1)
715 qsort(PPDs, NumPPDs, sizeof(ppd_info_t),
716 (int (*)(const void *, const void *))compare_names);
717
718 /*
719 * Write the new ppds.dat file...
720 */
721
722 if (ChangedPPD)
723 {
724 if ((fp = cupsFileOpen(filename, "w")) != NULL)
725 {
726 unsigned ppdsync = PPD_SYNC; /* Sync word */
727
728
729 cupsFileWrite(fp, (char *)&ppdsync, sizeof(ppdsync));
730
731 for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
732 cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
733
734 cupsFileClose(fp);
735
736 fprintf(stderr, "INFO: [cups-driverd] Wrote \"%s\", %d PPDs...\n",
737 filename, NumPPDs);
738 }
739 else
740 fprintf(stderr, "ERROR: [cups-driverd] Unable to write \"%s\" - %s\n",
741 filename, strerror(errno));
742 }
743 else
744 fputs("INFO: [cups-driverd] No new or changed PPDs...\n", stderr);
745
746 /*
747 * Scan for dynamic PPD files...
748 */
749
750 load_drivers();
751
752 /*
753 * Add the raw driver...
754 */
755
756 add_ppd("raw", "en", "Raw", "Raw Queue", "", "", "", 0, 0, 0,
757 PPD_TYPE_UNKNOWN);
758
759 /*
760 * Sort the PPDs by make and model...
761 */
762
763 if (NumPPDs > 1)
764 qsort(PPDs, NumPPDs, sizeof(ppd_info_t),
765 (int (*)(const void *, const void *))compare_ppds);
766
767 /*
768 * Send IPP attributes...
769 */
770
771 num_options = cupsParseOptions(opt, 0, &options);
772 requested = cupsGetOption("requested-attributes", num_options, options);
773 device_id = cupsGetOption("ppd-device-id", num_options, options);
774 language = cupsGetOption("ppd-natural-language", num_options, options);
775 make = cupsGetOption("ppd-make", num_options, options);
776 make_and_model = cupsGetOption("ppd-make-and-model", num_options, options);
777 model_number_str = cupsGetOption("ppd-model-number", num_options, options);
778 product = cupsGetOption("ppd-product", num_options, options);
779 psversion = cupsGetOption("ppd-psversion", num_options, options);
780 type_str = cupsGetOption("ppd-type", num_options, options);
781
782 if (make_and_model)
783 mam_len = strlen(make_and_model);
784 else
785 mam_len = 0;
786
787 if (device_id)
788 device_id_len = strlen(device_id);
789 else
790 device_id_len = 0;
791
792 if (model_number_str)
793 model_number = atoi(model_number_str);
794 else
795 model_number = 0;
796
797 if (type_str)
798 {
799 for (type = 0;
800 type < (int)(sizeof(ppd_types) / sizeof(ppd_types[0]));
801 type ++)
802 if (!strcmp(type_str, ppd_types[type]))
803 break;
804
805 if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0])))
806 {
807 fprintf(stderr, "ERROR: [cups-driverd] Bad ppd-type=\"%s\" ignored!\n",
808 type_str);
809 type_str = NULL;
810 }
811 }
812 else
813 type = 0;
814
815 if (requested)
816 fprintf(stderr, "DEBUG: [cups-driverd] requested-attributes=\"%s\"\n",
817 requested);
818 if (device_id)
819 fprintf(stderr, "DEBUG: [cups-driverd] ppd-device-id=\"%s\"\n",
820 device_id);
821 if (language)
822 fprintf(stderr, "DEBUG: [cups-driverd] ppd-natural-language=\"%s\"\n",
823 language);
824 if (make)
825 fprintf(stderr, "DEBUG: [cups-driverd] ppd-make=\"%s\"\n",
826 make);
827 if (make_and_model)
828 fprintf(stderr, "DEBUG: [cups-driverd] ppd-make-and-model=\"%s\"\n",
829 make_and_model);
830 if (model_number_str)
831 fprintf(stderr, "DEBUG: [cups-driverd] ppd-model-number=\"%s\"\n",
832 model_number_str);
833 if (product)
834 fprintf(stderr, "DEBUG: [cups-driverd] ppd-product=\"%s\"\n",
835 product);
836 if (psversion)
837 fprintf(stderr, "DEBUG: [cups-driverd] ppd-psversion=\"%s\"\n",
838 psversion);
839 if (type_str)
840 fprintf(stderr, "DEBUG: [cups-driverd] ppd-type=\"%s\"\n", type_str);
841
842 if (!requested || strstr(requested, "all"))
843 {
844 send_name = 1;
845 send_make = 1;
846 send_make_and_model = 1;
847 send_model_number = 1;
848 send_natural_language = 1;
849 send_device_id = 1;
850 send_product = 1;
851 send_psversion = 1;
852 send_type = 1;
853 }
854 else
855 {
856 send_name = strstr(requested, "ppd-name") != NULL;
857 send_make = strstr(requested, "ppd-make,") != NULL ||
858 strstr(requested, ",ppd-make") != NULL ||
859 !strcmp(requested, "ppd-make");
860 send_make_and_model = strstr(requested, "ppd-make-and-model") != NULL;
861 send_model_number = strstr(requested, "ppd-model-number") != NULL;
862 send_natural_language = strstr(requested, "ppd-natural-language") != NULL;
863 send_device_id = strstr(requested, "ppd-device-id") != NULL;
864 send_product = strstr(requested, "ppd-product") != NULL;
865 send_psversion = strstr(requested, "ppd-psversion") != NULL;
866 send_type = strstr(requested, "ppd-type") != NULL;
867 }
868
869 puts("Content-Type: application/ipp\n");
870
871 sent_header = 0;
872
873 if (limit <= 0 || limit > NumPPDs)
874 count = NumPPDs;
875 else
876 count = limit;
877
878 for (i = NumPPDs, ppd = PPDs; count > 0 && i > 0; i --, ppd ++)
879 {
880 /*
881 * Filter PPDs based on make, model, or device ID...
882 */
883
884 if (device_id && strncasecmp(ppd->record.device_id, device_id,
885 device_id_len))
886 continue; /* TODO: implement smart compare */
887
888 if (language)
889 {
890 for (j = 0; j < PPD_MAX_LANG; j ++)
891 if (!ppd->record.languages[j][0] ||
892 !strcasecmp(ppd->record.languages[j], language))
893 break;
894
895 if (j >= PPD_MAX_LANG || !ppd->record.languages[j][0])
896 continue;
897 }
898
899 if (make && strcasecmp(ppd->record.make, make))
900 continue;
901
902 if (make_and_model && strncasecmp(ppd->record.make_and_model,
903 make_and_model, mam_len))
904 continue;
905
906 if (model_number_str && ppd->record.model_number != model_number)
907 continue;
908
909 if (product)
910 {
911 for (j = 0; j < PPD_MAX_PROD; j ++)
912 if (!ppd->record.products[j][0] ||
913 !strcasecmp(ppd->record.products[j], product))
914 break;
915
916 if (j >= PPD_MAX_PROD || !ppd->record.products[j][0])
917 continue;
918 }
919
920 if (psversion)
921 {
922 for (j = 0; j < PPD_MAX_VERS; j ++)
923 if (!ppd->record.psversions[j][0] ||
924 !strcasecmp(ppd->record.psversions[j], psversion))
925 break;
926
927 if (j >= PPD_MAX_VERS || !ppd->record.psversions[j][0])
928 continue;
929 }
930
931 if (type_str && ppd->record.type != type)
932 continue;
933
934 /*
935 * Send this PPD...
936 */
937
938 if (!sent_header)
939 {
940 sent_header = 1;
941
942 cupsdSendIPPHeader(IPP_OK, request_id);
943 cupsdSendIPPGroup(IPP_TAG_OPERATION);
944 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
945 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
946 }
947
948 fprintf(stderr, "DEBUG: [cups-driverd] Sending %s (%s)...\n",
949 ppd->record.name, ppd->record.make_and_model);
950
951 count --;
952
953 cupsdSendIPPGroup(IPP_TAG_PRINTER);
954
955 if (send_name)
956 cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name);
957
958 if (send_natural_language)
959 {
960 cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language",
961 ppd->record.languages[0]);
962
963 for (j = 1; j < PPD_MAX_LANG && ppd->record.languages[j][0]; j ++)
964 cupsdSendIPPString(IPP_TAG_LANGUAGE, "", ppd->record.languages[j]);
965 }
966
967 if (send_make)
968 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make);
969
970 if (send_make_and_model)
971 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make-and-model",
972 ppd->record.make_and_model);
973
974 if (send_device_id)
975 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-device-id",
976 ppd->record.device_id);
977
978 if (send_product)
979 {
980 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-product",
981 ppd->record.products[0]);
982
983 for (j = 1; j < PPD_MAX_PROD && ppd->record.products[j][0]; j ++)
984 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.products[j]);
985 }
986
987 if (send_psversion)
988 {
989 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-psversion",
990 ppd->record.psversions[0]);
991
992 for (j = 1; j < PPD_MAX_VERS && ppd->record.psversions[j][0]; j ++)
993 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.psversions[j]);
994 }
995
996 if (send_type)
997 cupsdSendIPPString(IPP_TAG_KEYWORD, "ppd-type",
998 ppd_types[ppd->record.type]);
999
1000 if (send_model_number)
1001 cupsdSendIPPInteger(IPP_TAG_INTEGER, "ppd-model-number",
1002 ppd->record.model_number);
1003
1004 /*
1005 * If we have only requested the ppd-make attribute, then skip
1006 * the remaining PPDs with this make...
1007 */
1008
1009 if (requested && !strcmp(requested, "ppd-make"))
1010 {
1011 const char *this_make; /* This ppd-make */
1012
1013
1014 for (this_make = ppd->record.make, i --, ppd ++; i > 0; i --, ppd ++)
1015 if (strcasecmp(this_make, ppd->record.make))
1016 break;
1017
1018 i ++;
1019 ppd --;
1020 }
1021 }
1022
1023 if (!sent_header)
1024 {
1025 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
1026 cupsdSendIPPGroup(IPP_TAG_OPERATION);
1027 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
1028 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
1029 }
1030
1031 cupsdSendIPPTrailer();
1032
1033 return (0);
1034 }
1035
1036
1037 /*
1038 * 'load_ppds()' - Load PPD files recursively.
1039 */
1040
1041 static int /* O - 1 on success, 0 on failure */
1042 load_ppds(const char *d, /* I - Actual directory */
1043 const char *p, /* I - Virtual path in name */
1044 int descend) /* I - Descend into directories? */
1045 {
1046 int i; /* Looping var */
1047 cups_file_t *fp; /* Pointer to file */
1048 cups_dir_t *dir; /* Directory pointer */
1049 cups_dentry_t *dent; /* Directory entry */
1050 char filename[1024], /* Name of PPD or directory */
1051 line[256], /* Line from backend */
1052 *ptr, /* Pointer into name */
1053 name[128], /* Name of PPD file */
1054 lang_version[64], /* PPD LanguageVersion */
1055 lang_encoding[64], /* PPD LanguageEncoding */
1056 country[64], /* Country code */
1057 manufacturer[256], /* Manufacturer */
1058 make_model[256], /* Make and Model */
1059 model_name[256], /* ModelName */
1060 nick_name[256], /* NickName */
1061 device_id[256], /* 1284DeviceID */
1062 product[256], /* Product */
1063 psversion[256]; /* PSVersion */
1064 int model_number, /* cupsModelNumber */
1065 type; /* ppd-type */
1066 cups_array_t *products, /* Product array */
1067 *psversions, /* PSVersion array */
1068 *cups_languages; /* cupsLanguages array */
1069 ppd_info_t *ppd, /* New PPD file */
1070 key; /* Search key */
1071 int new_ppd; /* Is this a new PPD? */
1072 struct /* LanguageVersion translation table */
1073 {
1074 const char *version, /* LanguageVersion string */
1075 *language; /* Language code */
1076 } languages[] =
1077 {
1078 { "chinese", "zh" },
1079 { "danish", "da" },
1080 { "dutch", "nl" },
1081 { "english", "en" },
1082 { "finnish", "fi" },
1083 { "french", "fr" },
1084 { "german", "de" },
1085 { "greek", "el" },
1086 { "italian", "it" },
1087 { "japanese", "ja" },
1088 { "korean", "ko" },
1089 { "norwegian", "no" },
1090 { "polish", "pl" },
1091 { "portuguese", "pt" },
1092 { "russian", "ru" },
1093 { "slovak", "sk" },
1094 { "spanish", "es" },
1095 { "swedish", "sv" },
1096 { "turkish", "tr" }
1097 };
1098
1099
1100 if ((dir = cupsDirOpen(d)) == NULL)
1101 {
1102 fprintf(stderr,
1103 "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
1104 d, strerror(errno));
1105 return (0);
1106 }
1107
1108 while ((dent = cupsDirRead(dir)) != NULL)
1109 {
1110 /*
1111 * Skip files/directories starting with "."...
1112 */
1113
1114 if (dent->filename[0] == '.')
1115 continue;
1116
1117 /*
1118 * See if this is a file...
1119 */
1120
1121 snprintf(filename, sizeof(filename), "%s/%s", d, dent->filename);
1122
1123 if (p[0])
1124 snprintf(name, sizeof(name), "%s/%s", p, dent->filename);
1125 else
1126 strlcpy(name, dent->filename, sizeof(name));
1127
1128 if (S_ISDIR(dent->fileinfo.st_mode))
1129 {
1130 /*
1131 * Do subdirectory...
1132 */
1133
1134 if (descend)
1135 if (!load_ppds(filename, name, 1))
1136 {
1137 cupsDirClose(dir);
1138 return (1);
1139 }
1140
1141 continue;
1142 }
1143
1144 /*
1145 * See if this file has been scanned before...
1146 */
1147
1148 if (SortedPPDs > 0)
1149 {
1150 strcpy(key.record.name, name);
1151
1152 ppd = bsearch(&key, PPDs, SortedPPDs, sizeof(ppd_info_t),
1153 (int (*)(const void *, const void *))compare_names);
1154
1155 if (ppd &&
1156 ppd->record.size == dent->fileinfo.st_size &&
1157 ppd->record.mtime == dent->fileinfo.st_mtime)
1158 {
1159 ppd->found = 1;
1160 continue;
1161 }
1162 }
1163 else
1164 ppd = NULL;
1165
1166 /*
1167 * No, file is new/changed, so re-scan it...
1168 */
1169
1170 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1171 continue;
1172
1173 /*
1174 * Now see if this is a PPD file...
1175 */
1176
1177 line[0] = '\0';
1178 cupsFileGets(fp, line, sizeof(line));
1179
1180 if (strncmp(line, "*PPD-Adobe:", 11))
1181 {
1182 /*
1183 * Nope, close the file and continue...
1184 */
1185
1186 cupsFileClose(fp);
1187
1188 continue;
1189 }
1190
1191 /*
1192 * Now read until we get the NickName field...
1193 */
1194
1195 cups_languages = cupsArrayNew(NULL, NULL);
1196 products = cupsArrayNew(NULL, NULL);
1197 psversions = cupsArrayNew(NULL, NULL);
1198
1199 model_name[0] = '\0';
1200 nick_name[0] = '\0';
1201 manufacturer[0] = '\0';
1202 device_id[0] = '\0';
1203 lang_encoding[0] = '\0';
1204 strcpy(lang_version, "en");
1205 model_number = 0;
1206 type = PPD_TYPE_POSTSCRIPT;
1207
1208 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
1209 {
1210 if (!strncmp(line, "*Manufacturer:", 14))
1211 sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
1212 else if (!strncmp(line, "*ModelName:", 11))
1213 sscanf(line, "%*[^\"]\"%127[^\"]", model_name);
1214 else if (!strncmp(line, "*LanguageEncoding:", 18))
1215 sscanf(line, "%*[^:]:%63s", lang_encoding);
1216 else if (!strncmp(line, "*LanguageVersion:", 17))
1217 sscanf(line, "%*[^:]:%63s", lang_version);
1218 else if (!strncmp(line, "*NickName:", 10))
1219 sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
1220 else if (!strncasecmp(line, "*1284DeviceID:", 14))
1221 sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
1222 else if (!strncmp(line, "*Product:", 9))
1223 {
1224 sscanf(line, "%*[^\"]\"(%255[^)]", product);
1225 cupsArrayAdd(products, strdup(product));
1226 }
1227 else if (!strncmp(line, "*PSVersion:", 11))
1228 {
1229 sscanf(line, "%*[^\"]\"%255[^\"]", psversion);
1230 cupsArrayAdd(psversions, strdup(psversion));
1231 }
1232 else if (!strncmp(line, "*cupsLanguages:", 15))
1233 {
1234 char *start; /* Start of language */
1235
1236
1237 for (start = line + 15; *start && isspace(*start & 255); start ++);
1238
1239 if (*start++ == '\"')
1240 {
1241 while (*start)
1242 {
1243 for (ptr = start + 1;
1244 *ptr && *ptr != '\"' && !isspace(*ptr & 255);
1245 ptr ++);
1246
1247 if (*ptr)
1248 {
1249 *ptr++ = '\0';
1250
1251 while (isspace(*ptr & 255))
1252 *ptr++ = '\0';
1253 }
1254
1255 cupsArrayAdd(cups_languages, strdup(start));
1256 start = ptr;
1257 }
1258 }
1259 }
1260 else if (!strncmp(line, "*cupsFax:", 9))
1261 {
1262 for (ptr = line + 9; isspace(*ptr & 255); ptr ++);
1263
1264 if (!strncasecmp(ptr, "true", 4))
1265 type = PPD_TYPE_FAX;
1266 }
1267 else if (!strncmp(line, "*cupsFilter:", 12) &&
1268 (type == PPD_TYPE_POSTSCRIPT || type == PPD_TYPE_UNKNOWN))
1269 {
1270 if (strstr(line + 12, "application/vnd.cups-raster"))
1271 type = PPD_TYPE_RASTER;
1272 else if (strstr(line + 12, "application/vnd.cups-pdf"))
1273 type = PPD_TYPE_PDF;
1274 else
1275 type = PPD_TYPE_UNKNOWN;
1276 }
1277 else if (!strncmp(line, "*cupsModelNumber:", 17))
1278 sscanf(line, "*cupsModelNumber:%d", &model_number);
1279 else if (!strncmp(line, "*OpenUI", 7))
1280 {
1281 /*
1282 * Stop early if we have a NickName or ModelName attributes
1283 * before the first OpenUI...
1284 */
1285
1286 if ((model_name[0] || nick_name[0]) && cupsArrayCount(products) > 0 &&
1287 cupsArrayCount(psversions) > 0)
1288 break;
1289 }
1290 }
1291
1292 /*
1293 * Close the file...
1294 */
1295
1296 cupsFileClose(fp);
1297
1298 /*
1299 * See if we got all of the required info...
1300 */
1301
1302 if (nick_name[0])
1303 cupsCharsetToUTF8((cups_utf8_t *)make_model, nick_name,
1304 sizeof(make_model), _ppdGetEncoding(lang_encoding));
1305 else
1306 strcpy(make_model, model_name);
1307
1308 while (isspace(make_model[0] & 255))
1309 _cups_strcpy(make_model, make_model + 1);
1310
1311 if (!make_model[0] || cupsArrayCount(products) == 0 ||
1312 cupsArrayCount(psversions) == 0)
1313 {
1314 /*
1315 * We don't have all the info needed, so skip this file...
1316 */
1317
1318 if (!make_model[0])
1319 fprintf(stderr, "WARNING: Missing NickName and ModelName in %s!\n",
1320 filename);
1321
1322 if (cupsArrayCount(products) == 0)
1323 fprintf(stderr, "WARNING: Missing Product in %s!\n", filename);
1324
1325 if (cupsArrayCount(psversions) == 0)
1326 fprintf(stderr, "WARNING: Missing PSVersion in %s!\n", filename);
1327
1328 free_array(products);
1329 free_array(psversions);
1330 free_array(cups_languages);
1331
1332 continue;
1333 }
1334
1335 if (model_name[0])
1336 cupsArrayAdd(products, strdup(model_name));
1337
1338 /*
1339 * See if we got a manufacturer...
1340 */
1341
1342 while (isspace(manufacturer[0] & 255))
1343 _cups_strcpy(manufacturer, manufacturer + 1);
1344
1345 if (!manufacturer[0] || !strcmp(manufacturer, "ESP"))
1346 {
1347 /*
1348 * Nope, copy the first part of the make and model then...
1349 */
1350
1351 strlcpy(manufacturer, make_model, sizeof(manufacturer));
1352
1353 /*
1354 * Truncate at the first space, dash, or slash, or make the
1355 * manufacturer "Other"...
1356 */
1357
1358 for (ptr = manufacturer; *ptr; ptr ++)
1359 if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
1360 break;
1361
1362 if (*ptr && ptr > manufacturer)
1363 *ptr = '\0';
1364 else if (!strncasecmp(manufacturer, "agfa", 4))
1365 strcpy(manufacturer, "AGFA");
1366 else if (!strncasecmp(manufacturer, "herk", 4) ||
1367 !strncasecmp(manufacturer, "linotype", 8))
1368 strcpy(manufacturer, "LHAG");
1369 else
1370 strcpy(manufacturer, "Other");
1371
1372 /*
1373 * Hack for various vendors...
1374 */
1375
1376 if (!strcasecmp(manufacturer, "XPrint"))
1377 strcpy(manufacturer, "Xerox");
1378 else if (!strcasecmp(manufacturer, "Eastman"))
1379 strcpy(manufacturer, "Kodak");
1380 else if (!strcasecmp(manufacturer, "laserwriter"))
1381 strcpy(manufacturer, "Apple");
1382 else if (!strcasecmp(manufacturer, "colorpoint"))
1383 strcpy(manufacturer, "Seiko");
1384 else if (!strcasecmp(manufacturer, "fiery"))
1385 strcpy(manufacturer, "EFI");
1386 else if (!strcasecmp(manufacturer, "ps") ||
1387 !strcasecmp(manufacturer, "colorpass"))
1388 strcpy(manufacturer, "Canon");
1389 else if (!strncasecmp(manufacturer, "primera", 7))
1390 strcpy(manufacturer, "Fargo");
1391 else if (!strcasecmp(manufacturer, "designjet"))
1392 strcpy(manufacturer, "HP");
1393 }
1394 else if (!strncasecmp(manufacturer, "LHAG", 4) ||
1395 !strncasecmp(manufacturer, "linotype", 8))
1396 strcpy(manufacturer, "LHAG");
1397
1398 /*
1399 * Fix the lang_version as needed...
1400 */
1401
1402 if ((ptr = strchr(lang_version, '-')) != NULL)
1403 *ptr++ = '\0';
1404 else if ((ptr = strchr(lang_version, '_')) != NULL)
1405 *ptr++ = '\0';
1406
1407 if (ptr)
1408 {
1409 /*
1410 * Setup the country suffix...
1411 */
1412
1413 country[0] = '_';
1414 _cups_strcpy(country + 1, ptr);
1415 }
1416 else
1417 {
1418 /*
1419 * No country suffix...
1420 */
1421
1422 country[0] = '\0';
1423 }
1424
1425 for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
1426 if (!strcasecmp(languages[i].version, lang_version))
1427 break;
1428
1429 if (i < (int)(sizeof(languages) / sizeof(languages[0])))
1430 {
1431 /*
1432 * Found a known language...
1433 */
1434
1435 snprintf(lang_version, sizeof(lang_version), "%s%s",
1436 languages[i].language, country);
1437 }
1438 else
1439 {
1440 /*
1441 * Unknown language; use "xx"...
1442 */
1443
1444 strcpy(lang_version, "xx");
1445 }
1446
1447 /*
1448 * Add the PPD file...
1449 */
1450
1451 new_ppd = !ppd;
1452
1453 if (new_ppd)
1454 {
1455 /*
1456 * Add new PPD file...
1457 */
1458
1459 fprintf(stderr, "DEBUG: [cups-driverd] Adding ppd \"%s\"...\n", name);
1460
1461 ppd = add_ppd(name, lang_version, manufacturer, make_model, device_id,
1462 (char *)cupsArrayFirst(products),
1463 (char *)cupsArrayFirst(psversions),
1464 dent->fileinfo.st_mtime, dent->fileinfo.st_size,
1465 model_number, type);
1466
1467 if (!ppd)
1468 {
1469 cupsDirClose(dir);
1470 return (0);
1471 }
1472 }
1473 else
1474 {
1475 /*
1476 * Update existing record...
1477 */
1478
1479 fprintf(stderr, "DEBUG: [cups-driverd] Updating ppd \"%s\"...\n", name);
1480
1481 memset(ppd, 0, sizeof(ppd_info_t));
1482
1483 ppd->found = 1;
1484 ppd->record.mtime = dent->fileinfo.st_mtime;
1485 ppd->record.size = dent->fileinfo.st_size;
1486 ppd->record.model_number = model_number;
1487 ppd->record.type = type;
1488
1489 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
1490 strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make));
1491 strlcpy(ppd->record.make_and_model, make_model,
1492 sizeof(ppd->record.make_and_model));
1493 strlcpy(ppd->record.languages[0], lang_version,
1494 sizeof(ppd->record.languages[0]));
1495 strlcpy(ppd->record.products[0], (char *)cupsArrayFirst(products),
1496 sizeof(ppd->record.products[0]));
1497 strlcpy(ppd->record.psversions[0], (char *)cupsArrayFirst(psversions),
1498 sizeof(ppd->record.psversions[0]));
1499 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
1500 }
1501
1502 /*
1503 * Add remaining products, versions, and languages...
1504 */
1505
1506 for (i = 1;
1507 i < PPD_MAX_PROD && (ptr = (char *)cupsArrayNext(products)) != NULL;
1508 i ++)
1509 strlcpy(ppd->record.products[i], ptr,
1510 sizeof(ppd->record.products[0]));
1511
1512 for (i = 1;
1513 i < PPD_MAX_VERS && (ptr = (char *)cupsArrayNext(psversions)) != NULL;
1514 i ++)
1515 strlcpy(ppd->record.psversions[i], ptr,
1516 sizeof(ppd->record.psversions[0]));
1517
1518 for (i = 1, ptr = (char *)cupsArrayFirst(cups_languages);
1519 i < PPD_MAX_LANG && ptr;
1520 i ++, ptr = (char *)cupsArrayNext(cups_languages))
1521 strlcpy(ppd->record.languages[i], ptr,
1522 sizeof(ppd->record.languages[0]));
1523
1524 /*
1525 * Free products, versions, and languages...
1526 */
1527
1528 free_array(cups_languages);
1529 free_array(products);
1530 free_array(psversions);
1531
1532 ChangedPPD = 1;
1533 }
1534
1535 cupsDirClose(dir);
1536
1537 return (1);
1538 }
1539
1540
1541 /*
1542 * 'load_drivers()' - Load driver-generated PPD files.
1543 */
1544
1545 static int /* O - 1 on success, 0 on failure */
1546 load_drivers(void)
1547 {
1548 int i; /* Looping var */
1549 char *start, /* Start of value */
1550 *ptr; /* Pointer into string */
1551 const char *server_bin; /* CUPS_SERVERBIN env variable */
1552 char drivers[1024]; /* Location of driver programs */
1553 FILE *fp; /* Pipe to driver program */
1554 cups_dir_t *dir; /* Directory pointer */
1555 cups_dentry_t *dent; /* Directory entry */
1556 char filename[1024], /* Name of driver */
1557 line[2048], /* Line from driver */
1558 name[512], /* ppd-name */
1559 make[128], /* ppd-make */
1560 make_and_model[128], /* ppd-make-and-model */
1561 device_id[128], /* ppd-device-id */
1562 languages[128], /* ppd-natural-language */
1563 product[128], /* ppd-product */
1564 psversion[128], /* ppd-psversion */
1565 type_str[128]; /* ppd-type */
1566 int type; /* PPD type */
1567 ppd_info_t *ppd; /* Newly added PPD */
1568
1569
1570 /*
1571 * Try opening the driver directory...
1572 */
1573
1574 if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
1575 server_bin = CUPS_SERVERBIN;
1576
1577 snprintf(drivers, sizeof(drivers), "%s/driver", server_bin);
1578
1579 if ((dir = cupsDirOpen(drivers)) == NULL)
1580 {
1581 fprintf(stderr, "ERROR: [cups-driverd] Unable to open driver directory "
1582 "\"%s\": %s\n",
1583 drivers, strerror(errno));
1584 return (0);
1585 }
1586
1587 /*
1588 * Loop through all of the device drivers...
1589 */
1590
1591 while ((dent = cupsDirRead(dir)) != NULL)
1592 {
1593 /*
1594 * Only look at executable files...
1595 */
1596
1597 if (!(dent->fileinfo.st_mode & 0111) || !S_ISREG(dent->fileinfo.st_mode))
1598 continue;
1599
1600 /*
1601 * Run the driver with no arguments and collect the output...
1602 */
1603
1604 snprintf(filename, sizeof(filename), "%s/%s list", drivers, dent->filename);
1605 if ((fp = popen(filename, "r")) != NULL)
1606 {
1607 while (fgets(line, sizeof(line), fp) != NULL)
1608 {
1609 /*
1610 * Each line is of the form:
1611 *
1612 * "ppd-name" ppd-natural-language "ppd-make" "ppd-make-and-model" \
1613 * "ppd-device-id" "ppd-product" "ppd-psversion"
1614 */
1615
1616 device_id[0] = '\0';
1617 product[0] = '\0';
1618 psversion[0] = '\0';
1619 strcpy(type_str, "postscript");
1620
1621 if (sscanf(line, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\""
1622 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
1623 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
1624 "%*[ \t]\"%127[^\"]\"",
1625 name, languages, make, make_and_model,
1626 device_id, product, psversion, type_str) < 4)
1627 {
1628 /*
1629 * Bad format; strip trailing newline and write an error message.
1630 */
1631
1632 if (line[strlen(line) - 1] == '\n')
1633 line[strlen(line) - 1] = '\0';
1634
1635 fprintf(stderr, "ERROR: [cups-driverd] Bad line from \"%s\": %s\n",
1636 dent->filename, line);
1637 break;
1638 }
1639 else
1640 {
1641 /*
1642 * Add the device to the array of available devices...
1643 */
1644
1645 if ((start = strchr(languages, ',')) != NULL)
1646 *start++ = '\0';
1647
1648 for (type = 0;
1649 type < (int)(sizeof(ppd_types) / sizeof(ppd_types[0]));
1650 type ++)
1651 if (!strcmp(type_str, ppd_types[type]))
1652 break;
1653
1654 if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0])))
1655 {
1656 fprintf(stderr, "ERROR: [cups-driverd] Bad ppd-type \"%s\" ignored!\n",
1657 type_str);
1658 type = PPD_TYPE_UNKNOWN;
1659 }
1660
1661 ppd = add_ppd(name, languages, make, make_and_model, device_id,
1662 product, psversion, 0, 0, 0, type);
1663
1664 if (!ppd)
1665 {
1666 cupsDirClose(dir);
1667 pclose(fp);
1668 return (0);
1669 }
1670
1671 if (start && *start)
1672 {
1673 for (i = 1; i < PPD_MAX_LANG && *start; i ++)
1674 {
1675 if ((ptr = strchr(start, ',')) != NULL)
1676 *ptr++ = '\0';
1677 else
1678 ptr = start + strlen(start);
1679
1680 strlcpy(ppd->record.languages[i], start,
1681 sizeof(ppd->record.languages[0]));
1682
1683 start = ptr;
1684 }
1685 }
1686
1687 fprintf(stderr, "DEBUG: [cups-driverd] Added dynamic PPD \"%s\"...\n",
1688 name);
1689 }
1690 }
1691
1692 pclose(fp);
1693 }
1694 else
1695 fprintf(stderr, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n",
1696 filename, strerror(errno));
1697 }
1698
1699 cupsDirClose(dir);
1700
1701 return (1);
1702 }
1703
1704
1705 /*
1706 * End of "$Id: cups-driverd.c 6762 2007-08-02 18:05:03Z mike $".
1707 */