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