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