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