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