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