]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-driverd.c
9c5032246f75d49cdf0aed10f7b312f02b976d35
[thirdparty/cups.git] / scheduler / cups-driverd.c
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 1997-2005 by Easy Software Products.
11 *
12 * These coded instructions, statements, and computer programs are the
13 * property of Easy Software Products and are protected by Federal
14 * copyright law. Distribution and use rights are outlined in the file
15 * "LICENSE.txt" which should have been included with this file. If this
16 * file is missing or damaged please contact Easy Software Products
17 * at:
18 *
19 * Attn: CUPS Licensing Information
20 * Easy Software Products
21 * 44141 Airport View Drive, Suite 204
22 * Hollywood, Maryland 20636 USA
23 *
24 * Voice: (301) 373-9600
25 * EMail: cups-info@cups.org
26 * WWW: http://www.cups.org
27 *
28 * Contents:
29 *
30 */
31
32 /*
33 * Include necessary headers...
34 */
35
36 #include "util.h"
37 #include <cups/dir.h>
38
39
40 /*
41 * PPD information structures...
42 */
43
44 typedef struct /**** PPD record ****/
45 {
46 time_t mtime; /* Modification time */
47 size_t size; /* Size in bytes */
48 char name[512 - sizeof(time_t) - sizeof(size_t)],
49 /* PPD name */
50 natural_language[128], /* Natural language(s) */
51 make[128], /* Manufacturer */
52 make_and_model[256]; /* Make and model */
53 } ppd_rec_t;
54
55 typedef struct /**** In-memory record ****/
56 {
57 int found; /* 1 if PPD is found */
58 ppd_rec_t record; /* PPDs.dat record */
59 } ppd_info_t;
60
61
62 /*
63 * Globals...
64 */
65
66 int NumPPDs, /* Number of PPD files */
67 SortedPPDs, /* Number of sorted PPD files */
68 AllocPPDs; /* Number of allocated entries */
69 ppd_info_t *PPDs; /* PPD file info */
70 int ChangedPPD; /* Did we change the PPD database? */
71
72
73 /*
74 * Local functions...
75 */
76
77 ppd_info_t *add_ppd(const char *name, const char *natural_language,
78 const char *make, const char *make_and_model,
79 time_t mtime, size_t size);
80 int cat_ppd(const char *name);
81 int compare_names(const ppd_info_t *p0, const ppd_info_t *p1);
82 int compare_ppds(const ppd_info_t *p0, const ppd_info_t *p1);
83 int list_ppds(int request_id, int limit, const char *opt);
84 int load_drivers(void);
85 int load_ppds(const char *d, const char *p);
86
87
88 /*
89 * 'main()' - Scan for drivers and return an IPP response.
90 *
91 * Usage:
92 *
93 * cups-driverd request_id limit options
94 */
95
96 int /* O - Exit code */
97 main(int argc, /* I - Number of command-line args */
98 char *argv[]) /* I - Command-line arguments */
99 {
100 /*
101 * Check the command-line...
102 */
103
104 if (argc < 3 ||
105 (!strcmp(argv[1], "cat") && argc != 3) ||
106 (!strcmp(argv[1], "list") && argc != 5))
107 {
108 fputs("Usage: cups-driverd cat ppd-name\n", stderr);
109 fputs("Usage: cups-driverd list request_id limit options\n", stderr);
110 return (1);
111 }
112
113 /*
114 * Install or list PPDs...
115 */
116
117 if (!strcmp(argv[1], "cat"))
118 return (cat_ppd(argv[2]));
119 else
120 return (list_ppds(atoi(argv[2]), atoi(argv[3]), argv[4]));
121 }
122
123
124 /*
125 * 'add_ppd()' - Add a PPD file.
126 */
127
128 ppd_info_t * /* O - PPD */
129 add_ppd(const char *name, /* I - PPD name */
130 const char *natural_language, /* I - Language(s) */
131 const char *make, /* I - Manufacturer */
132 const char *make_and_model, /* I - NickName */
133 time_t mtime, /* I - Modification time */
134 size_t size) /* I - File size */
135 {
136 ppd_info_t *ppd; /* PPD */
137
138
139 /*
140 * Add a new PPD file...
141 */
142
143 if (NumPPDs >= AllocPPDs)
144 {
145 /*
146 * Allocate (more) memory for the PPD files...
147 */
148
149 AllocPPDs += 128;
150
151 if (!PPDs)
152 ppd = malloc(sizeof(ppd_info_t) * AllocPPDs);
153 else
154 ppd = realloc(PPDs, sizeof(ppd_info_t) * AllocPPDs);
155
156 if (ppd == NULL)
157 {
158 fprintf(stderr, "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
159 AllocPPDs);
160 return (NULL);
161 }
162
163 PPDs = ppd;
164 }
165
166 ppd = PPDs + NumPPDs;
167 NumPPDs ++;
168
169 /*
170 * Zero-out the PPD data and copy the values over...
171 */
172
173 memset(ppd, 0, sizeof(ppd_info_t));
174
175 ppd->found = 1;
176 ppd->record.mtime = mtime;
177 ppd->record.size = size;
178
179 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
180 strlcpy(ppd->record.natural_language, natural_language,
181 sizeof(ppd->record.natural_language));
182 strlcpy(ppd->record.make, make, sizeof(ppd->record.make));
183 strlcpy(ppd->record.make_and_model, make_and_model,
184 sizeof(ppd->record.make_and_model));
185
186 /*
187 * Return the new PPD pointer...
188 */
189
190 return (ppd);
191 }
192
193
194 /*
195 * 'cat_ppd()' - Copy a PPD file to stdout.
196 */
197
198 int /* O - Exit code */
199 cat_ppd(const char *name) /* I - PPD name */
200 {
201 return (0);
202 }
203
204
205 /*
206 * 'compare_names()' - Compare PPD filenames for sorting.
207 */
208
209 int /* O - Result of comparison */
210 compare_names(const ppd_info_t *p0, /* I - First PPD file */
211 const ppd_info_t *p1) /* I - Second PPD file */
212 {
213 return (strcasecmp(p0->record.name, p1->record.name));
214 }
215
216
217 /*
218 * 'compare_ppds()' - Compare PPD file make and model names for sorting.
219 */
220
221 int /* O - Result of comparison */
222 compare_ppds(const ppd_info_t *p0, /* I - First PPD file */
223 const ppd_info_t *p1) /* I - Second PPD file */
224 {
225 int diff; /* Difference between strings */
226
227 /*
228 * First compare manufacturers...
229 */
230
231 if ((diff = strcasecmp(p0->record.make, p1->record.make)) != 0)
232 return (diff);
233 else if ((diff = cupsdCompareNames(p0->record.make_and_model,
234 p1->record.make_and_model)) != 0)
235 return (diff);
236 else
237 return (strcasecmp(p0->record.natural_language,
238 p1->record.natural_language));
239 }
240
241
242 /*
243 * 'list_ppds()' - List PPD files.
244 */
245
246 int /* O - Exit code */
247 list_ppds(int request_id, /* I - Request ID */
248 int limit, /* I - Limit */
249 const char *opt) /* I - Option argument */
250 {
251 int i; /* Looping var */
252 int count; /* Number of PPDs to send */
253 ppd_info_t *ppd; /* Current PPD file */
254 cups_file_t *fp; /* ppds.dat file */
255 struct stat fileinfo; /* ppds.dat information */
256 char filename[1024], /* ppds.dat filename */
257 model[1024]; /* Model directory */
258 const char *cups_cachedir; /* CUPS_CACHEDIR environment variable */
259 const char *cups_datadir; /* CUPS_DATADIR environment variable */
260 int num_options; /* Number of options */
261 cups_option_t *options; /* Options */
262 const char *requested, /* requested-attributes option */
263 *make; /* ppd-make option */
264 int send_natural_language, /* Send ppd-natural-language attribute? */
265 send_make, /* Send ppd-make attribute? */
266 send_make_and_model, /* Send ppd-make-and-model attribute? */
267 send_name; /* Send ppd-name attribute? */
268
269
270 /*
271 * See if we a PPD database file...
272 */
273
274 NumPPDs = 0;
275 AllocPPDs = 0;
276 PPDs = (ppd_info_t *)NULL;
277 ChangedPPD = 0;
278
279 if ((cups_cachedir = getenv("CUPS_CACHEDIR")) == NULL)
280 cups_cachedir = CUPS_CACHEDIR;
281
282 snprintf(filename, sizeof(filename), "%s/ppds.dat", cups_cachedir);
283 if (!stat(filename, &fileinfo) &&
284 (fileinfo.st_size % sizeof(ppd_rec_t)) == 0 &&
285 (NumPPDs = fileinfo.st_size / sizeof(ppd_rec_t)) > 0)
286 {
287 /*
288 * We have a ppds.dat file, so read it!
289 */
290
291 AllocPPDs = NumPPDs;
292
293 if ((PPDs = malloc(sizeof(ppd_info_t) * NumPPDs)) == NULL)
294 {
295 fprintf(stderr, "ERROR: [cups-driverd] Unable to allocate memory for %d PPD files!\n",
296 NumPPDs);
297 NumPPDs = 0;
298 AllocPPDs = 0;
299 }
300 else if ((fp = cupsFileOpen(filename, "r")) != NULL)
301 {
302 for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
303 {
304 cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
305 ppd->found = 0;
306 }
307
308 cupsFileClose(fp);
309
310 fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
311 filename, NumPPDs);
312 }
313 else
314 {
315 fprintf(stderr, "ERROR: [cups-driverd] Unable to read \"%s\" - %s\n", filename,
316 strerror(errno));
317 NumPPDs = 0;
318 }
319 }
320
321 /*
322 * Load all PPDs in the specified directory and below...
323 */
324
325 SortedPPDs = NumPPDs;
326
327 if ((cups_datadir = getenv("CUPS_DATADIR")) == NULL)
328 cups_datadir = CUPS_DATADIR;
329
330 snprintf(model, sizeof(model), "%s/model", cups_datadir);
331 load_ppds(model, "");
332
333 /*
334 * Cull PPD files that are no longer present...
335 */
336
337 for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
338 if (!ppd->found)
339 {
340 /*
341 * Remove this PPD file from the list...
342 */
343
344 if (i > 1)
345 memmove(ppd, ppd + 1, (i - 1) * sizeof(ppd_info_t));
346
347 NumPPDs --;
348 ppd --;
349 }
350
351 /*
352 * Sort the PPDs by name...
353 */
354
355 if (NumPPDs > 1)
356 qsort(PPDs, NumPPDs, sizeof(ppd_info_t),
357 (int (*)(const void *, const void *))compare_names);
358
359 /*
360 * Write the new ppds.dat file...
361 */
362
363 if (ChangedPPD)
364 {
365 if ((fp = cupsFileOpen(filename, "w")) != NULL)
366 {
367 for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
368 cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
369
370 cupsFileClose(fp);
371
372 fprintf(stderr, "INFO: [cups-driverd] Wrote \"%s\", %d PPDs...\n",
373 filename, NumPPDs);
374 }
375 else
376 fprintf(stderr, "ERROR: [cups-driverd] Unable to write \"%s\" - %s",
377 filename, strerror(errno));
378 }
379 else
380 fputs("INFO: [cups-driverd] No new or changed PPDs...\n", stderr);
381
382 /*
383 * Scan for dynamic PPD files...
384 */
385
386 load_drivers();
387
388 /*
389 * Add the raw driver...
390 */
391
392 add_ppd("raw", "en", "Raw", "Raw Queue", 0, 0);
393
394 /*
395 * Sort the PPDs by make and model...
396 */
397
398 if (NumPPDs > 1)
399 qsort(PPDs, NumPPDs, sizeof(ppd_info_t),
400 (int (*)(const void *, const void *))compare_ppds);
401
402 /*
403 * Send IPP attributes...
404 */
405
406 num_options = cupsParseOptions(opt, 0, &options);
407 requested = cupsGetOption("requested-attributes", num_options, options);
408 make = cupsGetOption("ppd-make", num_options, options);
409
410 if (!requested || strstr(requested, "all"))
411 {
412 send_name = 1;
413 send_make = 1;
414 send_make_and_model = 1;
415 send_natural_language = 1;
416 }
417 else
418 {
419 send_name = strstr(requested, "ppd-name") != NULL;
420 send_make = strstr(requested, "ppd-make,") != NULL ||
421 strstr(requested, ",ppd-make") != NULL ||
422 !strcmp(requested, "ppd-make");
423 send_make_and_model = strstr(requested, "ppd-make-and-model") != NULL;
424 send_natural_language = strstr(requested, "ppd-natural-language") != NULL;
425 }
426
427 puts("Content-Type: application/ipp\n");
428
429 cupsdSendIPPHeader(IPP_OK, request_id);
430 cupsdSendIPPGroup(IPP_TAG_OPERATION);
431 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
432 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
433
434 if (limit <= 0 || limit > NumPPDs)
435 count = NumPPDs;
436 else
437 count = limit;
438
439 for (i = NumPPDs, ppd = PPDs; count > 0 && i > 0; i --, ppd ++)
440 if (!make || !strcasecmp(ppd->record.make, make))
441 {
442 /*
443 * Send this PPD...
444 */
445
446 count --;
447
448 cupsdSendIPPGroup(IPP_TAG_PRINTER);
449
450 if (send_name)
451 cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name);
452
453 if (send_natural_language)
454 cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language",
455 ppd->record.natural_language);
456
457 if (send_make)
458 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make);
459
460 if (send_make_and_model)
461 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make-and-model",
462 ppd->record.make_and_model);
463
464 /*
465 * If we have only requested the ppd-make attribute, then skip
466 * the remaining PPDs with this make...
467 */
468
469 if (requested && !strcmp(requested, "ppd-make"))
470 {
471 const char *this_make; /* This ppd-make */
472
473
474 for (this_make = ppd->record.make, i --, ppd ++; i > 0; i --, ppd ++)
475 if (strcasecmp(this_make, ppd->record.make))
476 break;
477
478 i ++;
479 ppd --;
480 }
481 }
482
483 cupsdSendIPPTrailer();
484
485 return (0);
486 }
487
488
489 /*
490 * 'load_ppds()' - Load PPD files recursively.
491 */
492
493 int /* O - 1 on success, 0 on failure */
494 load_ppds(const char *d, /* I - Actual directory */
495 const char *p) /* I - Virtual path in name */
496 {
497 int i; /* Looping var */
498 cups_file_t *fp; /* Pointer to file */
499 cups_dir_t *dir; /* Directory pointer */
500 cups_dentry_t *dent; /* Directory entry */
501 char filename[1024], /* Name of PPD or directory */
502 line[256], /* Line from backend */
503 *ptr, /* Pointer into name */
504 name[128], /* Name of PPD file */
505 language[64], /* PPD language version */
506 country[64], /* Country code */
507 manufacturer[256], /* Manufacturer */
508 make_model[256], /* Make and Model */
509 model_name[256], /* ModelName */
510 nick_name[256]; /* NickName */
511 ppd_info_t *ppd, /* New PPD file */
512 key; /* Search key */
513 int new_ppd; /* Is this a new PPD? */
514 struct /* LanguageVersion translation table */
515 {
516 const char *version, /* LanguageVersion string */
517 *language; /* Language code */
518 } languages[] =
519 {
520 { "chinese", "cn" },
521 { "danish", "da" },
522 { "dutch", "nl" },
523 { "english", "en" },
524 { "finnish", "fi" },
525 { "french", "fr" },
526 { "german", "de" },
527 { "greek", "el" },
528 { "italian", "it" },
529 { "japanese", "jp" },
530 { "norwegian", "no" },
531 { "polish", "pl" },
532 { "portuguese", "pt" },
533 { "russian", "ru" },
534 { "slovak", "sk" },
535 { "spanish", "es" },
536 { "swedish", "sv" },
537 { "turkish", "tr" }
538 };
539
540
541 if ((dir = cupsDirOpen(d)) == NULL)
542 {
543 fprintf(stderr, "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
544 d, strerror(errno));
545 return (0);
546 }
547
548 while ((dent = cupsDirRead(dir)) != NULL)
549 {
550 /*
551 * See if this is a file...
552 */
553
554 snprintf(filename, sizeof(filename), "%s/%s", d, dent->filename);
555
556 if (p[0])
557 snprintf(name, sizeof(name), "%s/%s", p, dent->filename);
558 else
559 strlcpy(name, dent->filename, sizeof(name));
560
561 if (S_ISDIR(dent->fileinfo.st_mode))
562 {
563 /*
564 * Do subdirectory...
565 */
566
567 if (!load_ppds(filename, name))
568 {
569 cupsDirClose(dir);
570 return (1);
571 }
572
573 continue;
574 }
575
576 /*
577 * See if this file has been scanned before...
578 */
579
580 if (SortedPPDs > 0)
581 {
582 strcpy(key.record.name, name);
583
584 ppd = bsearch(&key, PPDs, SortedPPDs, sizeof(ppd_info_t),
585 (int (*)(const void *, const void *))compare_names);
586
587 if (ppd &&
588 ppd->record.size == dent->fileinfo.st_size &&
589 ppd->record.mtime == dent->fileinfo.st_mtime)
590 {
591 ppd->found = 1;
592 continue;
593 }
594 }
595 else
596 ppd = NULL;
597
598 /*
599 * No, file is new/changed, so re-scan it...
600 */
601
602 if ((fp = cupsFileOpen(filename, "r")) == NULL)
603 continue;
604
605 /*
606 * Now see if this is a PPD file...
607 */
608
609 line[0] = '\0';
610 cupsFileGets(fp, line, sizeof(line));
611
612 if (strncmp(line, "*PPD-Adobe:", 11))
613 {
614 /*
615 * Nope, close the file and continue...
616 */
617
618 cupsFileClose(fp);
619
620 continue;
621 }
622
623 /*
624 * Now read until we get the NickName field...
625 */
626
627 model_name[0] = '\0';
628 nick_name[0] = '\0';
629 manufacturer[0] = '\0';
630 strcpy(language, "en");
631
632 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
633 {
634 if (!strncmp(line, "*Manufacturer:", 14))
635 sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
636 else if (!strncmp(line, "*ModelName:", 11))
637 sscanf(line, "%*[^\"]\"%127[^\"]", model_name);
638 else if (!strncmp(line, "*LanguageVersion:", 17))
639 sscanf(line, "%*[^:]:%63s", language);
640 else if (!strncmp(line, "*NickName:", 10))
641 sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
642 else if (!strncmp(line, "*OpenUI", 7))
643 {
644 /*
645 * Stop early if we have a NickName or ModelName attributes
646 * before the first OpenUI...
647 */
648
649 if (model_name[0] || nick_name[0])
650 break;
651 }
652
653 /*
654 * Stop early if we have both the Manufacturer and NickName
655 * attributes...
656 */
657
658 if (manufacturer[0] && nick_name[0])
659 break;
660 }
661
662 /*
663 * Close the file...
664 */
665
666 cupsFileClose(fp);
667
668 /*
669 * See if we got all of the required info...
670 */
671
672 if (nick_name[0])
673 strcpy(make_model, nick_name);
674 else
675 strcpy(make_model, model_name);
676
677 while (isspace(make_model[0] & 255))
678 cups_strcpy(make_model, make_model + 1);
679
680 if (!make_model[0])
681 continue; /* Nope... */
682
683 /*
684 * See if we got a manufacturer...
685 */
686
687 while (isspace(manufacturer[0] & 255))
688 cups_strcpy(manufacturer, manufacturer + 1);
689
690 if (!manufacturer[0] || !strcmp(manufacturer, "ESP"))
691 {
692 /*
693 * Nope, copy the first part of the make and model then...
694 */
695
696 strlcpy(manufacturer, make_model, sizeof(manufacturer));
697
698 /*
699 * Truncate at the first space, dash, or slash, or make the
700 * manufacturer "Other"...
701 */
702
703 for (ptr = manufacturer; *ptr; ptr ++)
704 if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
705 break;
706
707 if (*ptr && ptr > manufacturer)
708 *ptr = '\0';
709 else if (!strncasecmp(manufacturer, "agfa", 4))
710 strcpy(manufacturer, "AGFA");
711 else if (!strncasecmp(manufacturer, "herk", 4) ||
712 !strncasecmp(manufacturer, "linotype", 8))
713 strcpy(manufacturer, "LHAG");
714 else
715 strcpy(manufacturer, "Other");
716
717 /*
718 * Hack for various vendors...
719 */
720
721 if (!strcasecmp(manufacturer, "XPrint"))
722 strcpy(manufacturer, "Xerox");
723 else if (!strcasecmp(manufacturer, "Eastman"))
724 strcpy(manufacturer, "Kodak");
725 else if (!strcasecmp(manufacturer, "laserwriter"))
726 strcpy(manufacturer, "Apple");
727 else if (!strcasecmp(manufacturer, "colorpoint"))
728 strcpy(manufacturer, "Seiko");
729 else if (!strcasecmp(manufacturer, "fiery"))
730 strcpy(manufacturer, "EFI");
731 else if (!strcasecmp(manufacturer, "ps") ||
732 !strcasecmp(manufacturer, "colorpass"))
733 strcpy(manufacturer, "Canon");
734 else if (!strncasecmp(manufacturer, "primera", 7))
735 strcpy(manufacturer, "Fargo");
736 else if (!strcasecmp(manufacturer, "designjet"))
737 strcpy(manufacturer, "HP");
738 }
739 else if (!strncasecmp(manufacturer, "LHAG", 4) ||
740 !strncasecmp(manufacturer, "linotype", 8))
741 strcpy(manufacturer, "LHAG");
742
743 /*
744 * Fix the language as needed...
745 */
746
747 if ((ptr = strchr(language, '-')) != NULL)
748 *ptr++ = '\0';
749 else if ((ptr = strchr(language, '_')) != NULL)
750 *ptr++ = '\0';
751
752 if (ptr)
753 {
754 /*
755 * Setup the country suffix...
756 */
757
758 country[0] = '_';
759 cups_strcpy(country + 1, ptr);
760 }
761 else
762 {
763 /*
764 * No country suffix...
765 */
766
767 country[0] = '\0';
768 }
769
770 for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
771 if (!strcasecmp(languages[i].version, language))
772 break;
773
774 if (i < (int)(sizeof(languages) / sizeof(languages[0])))
775 {
776 /*
777 * Found a known language...
778 */
779
780 snprintf(language, sizeof(language), "%s%s", languages[i].language,
781 country);
782 }
783 else
784 {
785 /*
786 * Unknown language; use "xx"...
787 */
788
789 strcpy(language, "xx");
790 }
791
792 /*
793 * Add the PPD file...
794 */
795
796 new_ppd = !ppd;
797
798 if (new_ppd)
799 {
800 /*
801 * Add new PPD file...
802 */
803
804 fprintf(stderr, "DEBUG: [cups-driverd] Adding ppd \"%s\"...\n", name);
805
806 if (!add_ppd(name, language, manufacturer, make_model,
807 dent->fileinfo.st_mtime, dent->fileinfo.st_size))
808 {
809 cupsDirClose(dir);
810 return (0);
811 }
812 }
813 else
814 {
815 /*
816 * Update existing record...
817 */
818
819 fprintf(stderr, "DEBUG: [cups-driverd] Updating ppd \"%s\"...\n", name);
820
821 memset(ppd, 0, sizeof(ppd_info_t));
822
823 ppd->found = 1;
824 ppd->record.mtime = dent->fileinfo.st_mtime;
825 ppd->record.size = dent->fileinfo.st_size;
826
827 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
828 strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make));
829 strlcpy(ppd->record.make_and_model, make_model,
830 sizeof(ppd->record.make_and_model));
831 strlcpy(ppd->record.natural_language, language,
832 sizeof(ppd->record.natural_language));
833 }
834
835 ChangedPPD = 1;
836 }
837
838 cupsDirClose(dir);
839
840 return (1);
841 }
842
843
844 /*
845 * 'load_drivers()' - Load driver-generated PPD files.
846 */
847
848 int /* O - 1 on success, 0 on failure */
849 load_drivers(void)
850 {
851 const char *server_bin; /* CUPS_SERVERBIN environment variable */
852 char drivers[1024]; /* Location of driver programs */
853 FILE *fp; /* Pipe to driver program */
854 cups_dir_t *dir; /* Directory pointer */
855 cups_dentry_t *dent; /* Directory entry */
856 char filename[1024], /* Name of driver */
857 line[2048], /* Line from driver */
858 name[512], /* ppd-name */
859 natural_language[128], /* ppd-natural-language */
860 make[128], /* ppd-make */
861 make_and_model[256]; /* ppd-make-and-model */
862
863
864 /*
865 * Try opening the driver directory...
866 */
867
868 if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
869 server_bin = CUPS_SERVERBIN;
870
871 snprintf(drivers, sizeof(drivers), "%s/driver", server_bin);
872
873 if ((dir = cupsDirOpen(drivers)) == NULL)
874 {
875 fprintf(stderr, "ERROR: [cups-driverd] Unable to open driver directory \"%s\": %s",
876 drivers, strerror(errno));
877 return (0);
878 }
879
880 /*
881 * Loop through all of the device drivers...
882 */
883
884 while ((dent = cupsDirRead(dir)) != NULL)
885 {
886 /*
887 * Only look at executable files...
888 */
889
890 if (!(dent->fileinfo.st_mode & 0111) || !S_ISREG(dent->fileinfo.st_mode))
891 continue;
892
893 /*
894 * Run the driver with no arguments and collect the output...
895 */
896
897 snprintf(filename, sizeof(filename), "%s/%s", drivers, dent->filename);
898 if ((fp = popen(filename, "r")) != NULL)
899 {
900 while (fgets(line, sizeof(line), fp) != NULL)
901 {
902 /*
903 * Each line is of the form:
904 *
905 * \"ppd-name\" ppd-natural-language "ppd-make" "ppd-make-and-model"
906 */
907
908 if (sscanf(line, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\"%*[ \t]\"%256[^\"]\"",
909 name, natural_language, make, make_and_model) != 4)
910 {
911 /*
912 * Bad format; strip trailing newline and write an error message.
913 */
914
915 if (line[strlen(line) - 1] == '\n')
916 line[strlen(line) - 1] = '\0';
917
918 fprintf(stderr, "ERROR: [cups-driverd] Bad line from \"%s\": %s\n",
919 dent->filename, line);
920 break;
921 }
922 else
923 {
924 /*
925 * Add the device to the array of available devices...
926 */
927
928 if (!add_ppd(name, natural_language, make, make_and_model, 0, 0))
929 {
930 cupsDirClose(dir);
931 return (0);
932 }
933
934 fprintf(stderr, "DEBUG: [cups-driverd] Added dynamic PPD \"%s\"...\n",
935 name);
936 }
937 }
938
939 pclose(fp);
940 }
941 else
942 fprintf(stderr, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n",
943 filename, strerror(errno));
944 }
945
946 cupsDirClose(dir);
947
948 return (1);
949 }
950
951
952 /*
953 * End of "$Id$".
954 */