]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/cups-driverd.c
Import CUPS 1.4svn-r7356.
[thirdparty/cups.git] / scheduler / cups-driverd.c
CommitLineData
ef416fc2 1/*
db1f069b 2 * "$Id: cups-driverd.c 6762 2007-08-02 18:05:03Z mike $"
ef416fc2 3 *
4 * PPD/driver support for the Common UNIX Printing System (CUPS).
5 *
6 * This program handles listing and installing both static PPD files
7 * in CUPS_DATADIR/model and dynamically generated PPD files using
8 * the driver helper programs in CUPS_SERVERBIN/driver.
9 *
91c84a35 10 * Copyright 2007-2008 by Apple Inc.
f899b121 11 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 12 *
13 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 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.
b94498cf 26 * free_array() - Free an array of strings.
ef416fc2 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>
80ca4592 38#include <cups/transcode.h>
39
40
41/*
42 * Private PPD functions...
43 */
44
45extern cups_encoding_t _ppdGetEncoding(const char *name);
ef416fc2 46
47
b94498cf 48/*
49 * Constants...
50 */
51
3d8365b8 52#define PPD_SYNC 0x50504434 /* Sync word for ppds.dat (PPD4) */
b94498cf 53#define PPD_MAX_LANG 32 /* Maximum languages */
54#define PPD_MAX_PROD 8 /* Maximum products */
55#define PPD_MAX_VERS 8 /* Maximum versions */
56
3d8365b8 57#define PPD_TYPE_POSTSCRIPT 0 /* PostScript PPD */
58#define PPD_TYPE_PDF 1 /* PDF PPD */
59#define PPD_TYPE_RASTER 2 /* CUPS raster PPD */
60#define PPD_TYPE_FAX 3 /* Facsimile/MFD PPD */
61#define PPD_TYPE_UNKNOWN 4 /* Other/hybrid PPD */
62
ac884b6a 63static const char * const ppd_types[] = /* ppd-type values */
3d8365b8 64{
65 "postscript",
66 "pdf",
67 "raster",
68 "fax",
69 "unknown"
70};
71
b94498cf 72
ef416fc2 73/*
74 * PPD information structures...
75 */
76
77typedef struct /**** PPD record ****/
78{
79 time_t mtime; /* Modification time */
80 size_t size; /* Size in bytes */
3d8365b8 81 int model_number; /* cupsModelNumber */
82 int type; /* ppd-type */
b94498cf 83 char name[512], /* PPD name */
84 languages[PPD_MAX_LANG][6],
85 /* LanguageVersion/cupsLanguages */
86 products[PPD_MAX_PROD][128],
87 /* Product strings */
88 psversions[PPD_MAX_VERS][32],
89 /* PSVersion strings */
ef416fc2 90 make[128], /* Manufacturer */
f899b121 91 make_and_model[128], /* NickName/ModelName */
bd7854cb 92 device_id[128]; /* IEEE 1284 Device ID */
b94498cf 93} ppd_rec_t;
ef416fc2 94
95typedef struct /**** In-memory record ****/
96{
97 int found; /* 1 if PPD is found */
98 ppd_rec_t record; /* PPDs.dat record */
99} ppd_info_t;
100
101
102/*
103 * Globals...
104 */
105
106int NumPPDs, /* Number of PPD files */
107 SortedPPDs, /* Number of sorted PPD files */
108 AllocPPDs; /* Number of allocated entries */
109ppd_info_t *PPDs; /* PPD file info */
110int ChangedPPD; /* Did we change the PPD database? */
111
112
113/*
114 * Local functions...
115 */
116
b94498cf 117static ppd_info_t *add_ppd(const char *name, const char *language,
f899b121 118 const char *make, const char *make_and_model,
119 const char *device_id, const char *product,
b94498cf 120 const char *psversion, time_t mtime,
3d8365b8 121 size_t size, int model_number, int type);
b94498cf 122static int cat_ppd(const char *name, int request_id);
bd7854cb 123static int compare_names(const ppd_info_t *p0,
124 const ppd_info_t *p1);
125static int compare_ppds(const ppd_info_t *p0,
126 const ppd_info_t *p1);
b94498cf 127static void free_array(cups_array_t *a);
bd7854cb 128static int list_ppds(int request_id, int limit, const char *opt);
129static int load_drivers(void);
b94498cf 130static int load_ppds(const char *d, const char *p, int descend);
ef416fc2 131
132
133/*
134 * 'main()' - Scan for drivers and return an IPP response.
135 *
136 * Usage:
137 *
138 * cups-driverd request_id limit options
139 */
140
141int /* O - Exit code */
142main(int argc, /* I - Number of command-line args */
143 char *argv[]) /* I - Command-line arguments */
144{
145 /*
146 * Install or list PPDs...
147 */
148
149 if (argc == 3 && !strcmp(argv[1], "cat"))
b94498cf 150 return (cat_ppd(argv[2], 0));
151 else if (argc == 4 && !strcmp(argv[1], "get"))
152 return (cat_ppd(argv[3], atoi(argv[2])));
ef416fc2 153 else if (argc == 5 && !strcmp(argv[1], "list"))
154 return (list_ppds(atoi(argv[2]), atoi(argv[3]), argv[4]));
155 else
156 {
157 fputs("Usage: cups-driverd cat ppd-name\n", stderr);
b94498cf 158 fputs("Usage: cups-driverd get request_id ppd-name\n", stderr);
ef416fc2 159 fputs("Usage: cups-driverd list request_id limit options\n", stderr);
160 return (1);
161 }
162}
163
164
165/*
166 * 'add_ppd()' - Add a PPD file.
167 */
168
bd7854cb 169static ppd_info_t * /* O - PPD */
ef416fc2 170add_ppd(const char *name, /* I - PPD name */
b94498cf 171 const char *language, /* I - LanguageVersion */
ef416fc2 172 const char *make, /* I - Manufacturer */
f899b121 173 const char *make_and_model, /* I - NickName/ModelName */
89d46774 174 const char *device_id, /* I - 1284DeviceID */
f899b121 175 const char *product, /* I - Product */
b94498cf 176 const char *psversion, /* I - PSVersion */
ef416fc2 177 time_t mtime, /* I - Modification time */
3d8365b8 178 size_t size, /* I - File size */
179 int model_number, /* I - Model number */
180 int type) /* I - Driver type */
ef416fc2 181{
182 ppd_info_t *ppd; /* PPD */
b86bc4cf 183 char *recommended; /* Foomatic driver string */
ef416fc2 184
185
186 /*
187 * Add a new PPD file...
188 */
189
190 if (NumPPDs >= AllocPPDs)
191 {
192 /*
193 * Allocate (more) memory for the PPD files...
194 */
195
196 AllocPPDs += 128;
197
198 if (!PPDs)
199 ppd = malloc(sizeof(ppd_info_t) * AllocPPDs);
200 else
201 ppd = realloc(PPDs, sizeof(ppd_info_t) * AllocPPDs);
202
203 if (ppd == NULL)
204 {
b94498cf 205 fprintf(stderr,
206 "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
ef416fc2 207 AllocPPDs);
208 return (NULL);
209 }
210
211 PPDs = ppd;
212 }
213
214 ppd = PPDs + NumPPDs;
215 NumPPDs ++;
216
217 /*
218 * Zero-out the PPD data and copy the values over...
219 */
220
221 memset(ppd, 0, sizeof(ppd_info_t));
222
3d8365b8 223 ppd->found = 1;
224 ppd->record.mtime = mtime;
225 ppd->record.size = size;
226 ppd->record.model_number = model_number;
227 ppd->record.type = type;
ef416fc2 228
229 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
b94498cf 230 strlcpy(ppd->record.languages[0], language,
231 sizeof(ppd->record.languages[0]));
232 strlcpy(ppd->record.products[0], product, sizeof(ppd->record.products[0]));
233 strlcpy(ppd->record.psversions[0], psversion,
234 sizeof(ppd->record.psversions[0]));
ef416fc2 235 strlcpy(ppd->record.make, make, sizeof(ppd->record.make));
236 strlcpy(ppd->record.make_and_model, make_and_model,
237 sizeof(ppd->record.make_and_model));
bd7854cb 238 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
ef416fc2 239
b86bc4cf 240 /*
241 * Strip confusing (and often wrong) "recommended" suffix added by
242 * Foomatic drivers...
243 */
244
b94498cf 245 if ((recommended = strstr(ppd->record.make_and_model,
246 " (recommended)")) != NULL)
b86bc4cf 247 *recommended = '\0';
248
ef416fc2 249 /*
250 * Return the new PPD pointer...
251 */
252
253 return (ppd);
254}
255
256
257/*
258 * 'cat_ppd()' - Copy a PPD file to stdout.
259 */
260
bd7854cb 261static int /* O - Exit code */
b94498cf 262cat_ppd(const char *name, /* I - PPD name */
263 int request_id) /* I - Request ID for response? */
ef416fc2 264{
265 char scheme[256], /* Scheme from PPD name */
266 *sptr; /* Pointer into scheme */
267 char line[1024]; /* Line/filename */
b94498cf 268 char message[2048]; /* status-message */
ef416fc2 269
270
271 /*
272 * Figure out if this is a static or dynamic PPD file...
273 */
274
275 strlcpy(scheme, name, sizeof(scheme));
276 if ((sptr = strchr(scheme, ':')) != NULL)
277 {
278 *sptr = '\0';
279
280 if (!strcmp(scheme, "file"))
281 {
282 /*
283 * "file:name" == "name"...
284 */
285
286 name += 5;
287 scheme[0] = '\0';
288 }
289 }
290 else
291 scheme[0] = '\0';
292
09a101d6 293 if (request_id > 0)
294 puts("Content-Type: application/ipp\n");
b94498cf 295
ef416fc2 296 if (scheme[0])
297 {
298 /*
299 * Dynamic PPD, see if we have a driver program to support it...
300 */
301
302 const char *serverbin; /* CUPS_SERVERBIN env var */
303
304
305 if ((serverbin = getenv("CUPS_SERVERBIN")) == NULL)
306 serverbin = CUPS_SERVERBIN;
307
308 snprintf(line, sizeof(line), "%s/driver/%s", serverbin, scheme);
309 if (access(line, X_OK))
310 {
311 /*
312 * File does not exist or is not executable...
313 */
314
315 fprintf(stderr, "ERROR: [cups-driverd] Unable to access \"%s\" - %s\n",
316 line, strerror(errno));
b94498cf 317
318 if (request_id > 0)
319 {
320 snprintf(message, sizeof(message), "Unable to access \"%s\" - %s",
321 line, strerror(errno));
322
323 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
324 cupsdSendIPPGroup(IPP_TAG_OPERATION);
325 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
326 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
327 "en-US");
328 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
329 cupsdSendIPPTrailer();
330 }
331
ef416fc2 332 return (1);
333 }
334
335 /*
336 * Yes, let it cat the PPD file...
337 */
338
b94498cf 339 if (request_id)
340 {
341 cupsdSendIPPHeader(IPP_OK, request_id);
342 cupsdSendIPPGroup(IPP_TAG_OPERATION);
343 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
344 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
345 "en-US");
346 cupsdSendIPPTrailer();
347 }
348
ef416fc2 349 if (execl(line, scheme, "cat", name, (char *)NULL))
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);
b94498cf 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
ef416fc2 391 return (1);
392 }
393
394 /*
395 * Try opening the file...
396 */
397
b94498cf 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 }
ef416fc2 444
ef416fc2 445 if ((fp = cupsFileOpen(line, "r")) == NULL)
446 {
447 fprintf(stderr, "ERROR: [cups-driverd] Unable to open \"%s\" - %s\n",
448 line, strerror(errno));
b94498cf 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
ef416fc2 464 return (1);
465 }
466
b94498cf 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
ef416fc2 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
bd7854cb 499static int /* O - Result of comparison */
ef416fc2 500compare_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
bd7854cb 511static int /* O - Result of comparison */
ef416fc2 512compare_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
bd7854cb 517
ef416fc2 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
b94498cf 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
537static void
538free_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);
ef416fc2 549}
550
551
552/*
553 * 'list_ppds()' - List PPD files.
554 */
555
bd7854cb 556static int /* O - Exit code */
ef416fc2 557list_ppds(int request_id, /* I - Request ID */
558 int limit, /* I - Limit */
559 const char *opt) /* I - Option argument */
560{
b94498cf 561 int i, j; /* Looping vars */
ef416fc2 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 */
b94498cf 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 */
3d8365b8 577 *model_number_str, /* ppd-model-number option */
b94498cf 578 *product, /* ppd-product option */
3d8365b8 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 */
b94498cf 584 device_id_len, /* Length of ppd-device-id */
3d8365b8 585 send_device_id, /* Send ppd-device-id? */
b94498cf 586 send_make, /* Send ppd-make? */
587 send_make_and_model, /* Send ppd-make-and-model? */
3d8365b8 588 send_model_number, /* Send ppd-model-number? */
b94498cf 589 send_name, /* Send ppd-name? */
3d8365b8 590 send_natural_language, /* Send ppd-natural-language? */
b94498cf 591 send_product, /* Send ppd-product? */
592 send_psversion, /* Send ppd-psversion? */
3d8365b8 593 send_type, /* Send ppd-type? */
b94498cf 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);
ef416fc2 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);
b94498cf 614 if ((fp = cupsFileOpen(filename, "r")) != NULL)
ef416fc2 615 {
616 /*
b94498cf 617 * See if we have the right sync word...
ef416fc2 618 */
619
b94498cf 620 unsigned ppdsync; /* Sync word */
ef416fc2 621
b94498cf 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)
ef416fc2 629 {
b94498cf 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
ef416fc2 639 {
b94498cf 640 AllocPPDs = NumPPDs;
ef416fc2 641
b94498cf 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 }
ef416fc2 647
b94498cf 648 fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
649 filename, NumPPDs);
650 }
ef416fc2 651 }
ef416fc2 652
b94498cf 653 cupsFileClose(fp);
654 }
655
ef416fc2 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);
b94498cf 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
568fa3fa
MS
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);
b94498cf 693#endif /* __APPLE__ */
ef416fc2 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 {
b94498cf 729 unsigned ppdsync = PPD_SYNC; /* Sync word */
730
731
732 cupsFileWrite(fp, (char *)&ppdsync, sizeof(ppdsync));
733
ef416fc2 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
3d8365b8 759 add_ppd("raw", "en", "Raw", "Raw Queue", "", "", "", 0, 0, 0,
760 PPD_TYPE_UNKNOWN);
ef416fc2 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
3d8365b8 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);
b94498cf 784
785 if (make_and_model)
786 mam_len = strlen(make_and_model);
787 else
788 mam_len = 0;
ef416fc2 789
b94498cf 790 if (device_id)
791 device_id_len = strlen(device_id);
792 else
793 device_id_len = 0;
794
3d8365b8 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 }
bc44d920 815 else
816 type = 0;
3d8365b8 817
b94498cf 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);
3d8365b8 833 if (model_number_str)
834 fprintf(stderr, "DEBUG: [cups-driverd] ppd-model-number=\"%s\"\n",
835 model_number_str);
b94498cf 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);
3d8365b8 842 if (type_str)
843 fprintf(stderr, "DEBUG: [cups-driverd] ppd-type=\"%s\"\n", type_str);
ef416fc2 844
845 if (!requested || strstr(requested, "all"))
846 {
847 send_name = 1;
848 send_make = 1;
849 send_make_and_model = 1;
3d8365b8 850 send_model_number = 1;
ef416fc2 851 send_natural_language = 1;
bd7854cb 852 send_device_id = 1;
f899b121 853 send_product = 1;
b94498cf 854 send_psversion = 1;
3d8365b8 855 send_type = 1;
ef416fc2 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;
3d8365b8 864 send_model_number = strstr(requested, "ppd-model-number") != NULL;
ef416fc2 865 send_natural_language = strstr(requested, "ppd-natural-language") != NULL;
bd7854cb 866 send_device_id = strstr(requested, "ppd-device-id") != NULL;
f899b121 867 send_product = strstr(requested, "ppd-product") != NULL;
b94498cf 868 send_psversion = strstr(requested, "ppd-psversion") != NULL;
3d8365b8 869 send_type = strstr(requested, "ppd-type") != NULL;
ef416fc2 870 }
871
872 puts("Content-Type: application/ipp\n");
873
b94498cf 874 sent_header = 0;
ef416fc2 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 ++)
b94498cf 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)
ef416fc2 892 {
b94498cf 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
3d8365b8 909 if (model_number_str && ppd->record.model_number != model_number)
910 continue;
911
b94498cf 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;
ef416fc2 918
b94498cf 919 if (j >= PPD_MAX_PROD || !ppd->record.products[j][0])
920 continue;
921 }
ef416fc2 922
b94498cf 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;
ef416fc2 929
b94498cf 930 if (j >= PPD_MAX_VERS || !ppd->record.psversions[j][0])
931 continue;
932 }
ef416fc2 933
3d8365b8 934 if (type_str && ppd->record.type != type)
935 continue;
936
b94498cf 937 /*
938 * Send this PPD...
939 */
ef416fc2 940
b94498cf 941 if (!sent_header)
942 {
943 sent_header = 1;
ef416fc2 944
b94498cf 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 }
ef416fc2 950
b94498cf 951 fprintf(stderr, "DEBUG: [cups-driverd] Sending %s (%s)...\n",
952 ppd->record.name, ppd->record.make_and_model);
ef416fc2 953
b94498cf 954 count --;
bd7854cb 955
b94498cf 956 cupsdSendIPPGroup(IPP_TAG_PRINTER);
f899b121 957
b94498cf 958 if (send_name)
959 cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name);
ef416fc2 960
b94498cf 961 if (send_natural_language)
962 {
963 cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language",
964 ppd->record.languages[0]);
ef416fc2 965
b94498cf 966 for (j = 1; j < PPD_MAX_LANG && ppd->record.languages[j][0]; j ++)
967 cupsdSendIPPString(IPP_TAG_LANGUAGE, "", ppd->record.languages[j]);
968 }
ef416fc2 969
b94498cf 970 if (send_make)
971 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make);
ef416fc2 972
b94498cf 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
3d8365b8 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
b94498cf 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 --;
ef416fc2 1023 }
b94498cf 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 }
ef416fc2 1033
1034 cupsdSendIPPTrailer();
1035
1036 return (0);
1037}
1038
1039
1040/*
1041 * 'load_ppds()' - Load PPD files recursively.
1042 */
1043
bd7854cb 1044static int /* O - 1 on success, 0 on failure */
ef416fc2 1045load_ppds(const char *d, /* I - Actual directory */
b94498cf 1046 const char *p, /* I - Virtual path in name */
1047 int descend) /* I - Descend into directories? */
ef416fc2 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 */
80ca4592 1057 lang_version[64], /* PPD LanguageVersion */
1058 lang_encoding[64], /* PPD LanguageEncoding */
ef416fc2 1059 country[64], /* Country code */
1060 manufacturer[256], /* Manufacturer */
1061 make_model[256], /* Make and Model */
1062 model_name[256], /* ModelName */
bd7854cb 1063 nick_name[256], /* NickName */
f899b121 1064 device_id[256], /* 1284DeviceID */
b94498cf 1065 product[256], /* Product */
1066 psversion[256]; /* PSVersion */
3d8365b8 1067 int model_number, /* cupsModelNumber */
1068 type; /* ppd-type */
b94498cf 1069 cups_array_t *products, /* Product array */
1070 *psversions, /* PSVersion array */
1071 *cups_languages; /* cupsLanguages array */
ef416fc2 1072 ppd_info_t *ppd, /* New PPD file */
1073 key; /* Search key */
1074 int new_ppd; /* Is this a new PPD? */
1075 struct /* LanguageVersion translation table */
1076 {
1077 const char *version, /* LanguageVersion string */
1078 *language; /* Language code */
1079 } languages[] =
1080 {
b94498cf 1081 { "chinese", "zh" },
ef416fc2 1082 { "danish", "da" },
1083 { "dutch", "nl" },
1084 { "english", "en" },
1085 { "finnish", "fi" },
1086 { "french", "fr" },
1087 { "german", "de" },
1088 { "greek", "el" },
1089 { "italian", "it" },
7594b224 1090 { "japanese", "ja" },
bc44d920 1091 { "korean", "ko" },
ef416fc2 1092 { "norwegian", "no" },
1093 { "polish", "pl" },
1094 { "portuguese", "pt" },
1095 { "russian", "ru" },
1096 { "slovak", "sk" },
1097 { "spanish", "es" },
1098 { "swedish", "sv" },
1099 { "turkish", "tr" }
1100 };
1101
1102
1103 if ((dir = cupsDirOpen(d)) == NULL)
1104 {
b94498cf 1105 fprintf(stderr,
1106 "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
ef416fc2 1107 d, strerror(errno));
1108 return (0);
1109 }
1110
1111 while ((dent = cupsDirRead(dir)) != NULL)
1112 {
bd7854cb 1113 /*
1114 * Skip files/directories starting with "."...
1115 */
1116
1117 if (dent->filename[0] == '.')
1118 continue;
1119
ef416fc2 1120 /*
1121 * See if this is a file...
1122 */
1123
1124 snprintf(filename, sizeof(filename), "%s/%s", d, dent->filename);
1125
1126 if (p[0])
1127 snprintf(name, sizeof(name), "%s/%s", p, dent->filename);
1128 else
1129 strlcpy(name, dent->filename, sizeof(name));
1130
1131 if (S_ISDIR(dent->fileinfo.st_mode))
1132 {
1133 /*
1134 * Do subdirectory...
1135 */
1136
b94498cf 1137 if (descend)
1138 if (!load_ppds(filename, name, 1))
1139 {
1140 cupsDirClose(dir);
1141 return (1);
1142 }
ef416fc2 1143
1144 continue;
1145 }
1146
1147 /*
1148 * See if this file has been scanned before...
1149 */
1150
1151 if (SortedPPDs > 0)
1152 {
1153 strcpy(key.record.name, name);
1154
1155 ppd = bsearch(&key, PPDs, SortedPPDs, sizeof(ppd_info_t),
1156 (int (*)(const void *, const void *))compare_names);
1157
1158 if (ppd &&
1159 ppd->record.size == dent->fileinfo.st_size &&
1160 ppd->record.mtime == dent->fileinfo.st_mtime)
1161 {
1162 ppd->found = 1;
1163 continue;
1164 }
1165 }
1166 else
1167 ppd = NULL;
1168
1169 /*
1170 * No, file is new/changed, so re-scan it...
1171 */
1172
1173 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1174 continue;
1175
1176 /*
1177 * Now see if this is a PPD file...
1178 */
1179
1180 line[0] = '\0';
1181 cupsFileGets(fp, line, sizeof(line));
1182
1183 if (strncmp(line, "*PPD-Adobe:", 11))
1184 {
1185 /*
1186 * Nope, close the file and continue...
1187 */
1188
1189 cupsFileClose(fp);
1190
1191 continue;
1192 }
1193
1194 /*
1195 * Now read until we get the NickName field...
1196 */
1197
b94498cf 1198 cups_languages = cupsArrayNew(NULL, NULL);
1199 products = cupsArrayNew(NULL, NULL);
1200 psversions = cupsArrayNew(NULL, NULL);
f899b121 1201
80ca4592 1202 model_name[0] = '\0';
1203 nick_name[0] = '\0';
1204 manufacturer[0] = '\0';
1205 device_id[0] = '\0';
1206 lang_encoding[0] = '\0';
1207 strcpy(lang_version, "en");
3d8365b8 1208 model_number = 0;
1209 type = PPD_TYPE_POSTSCRIPT;
ef416fc2 1210
1211 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
1212 {
1213 if (!strncmp(line, "*Manufacturer:", 14))
1214 sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
1215 else if (!strncmp(line, "*ModelName:", 11))
1216 sscanf(line, "%*[^\"]\"%127[^\"]", model_name);
80ca4592 1217 else if (!strncmp(line, "*LanguageEncoding:", 18))
1218 sscanf(line, "%*[^:]:%63s", lang_encoding);
ef416fc2 1219 else if (!strncmp(line, "*LanguageVersion:", 17))
80ca4592 1220 sscanf(line, "%*[^:]:%63s", lang_version);
ef416fc2 1221 else if (!strncmp(line, "*NickName:", 10))
1222 sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
89d46774 1223 else if (!strncasecmp(line, "*1284DeviceID:", 14))
bd7854cb 1224 sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
3d8365b8 1225 else if (!strncmp(line, "*Product:", 9))
f899b121 1226 {
1227 sscanf(line, "%*[^\"]\"(%255[^)]", product);
1228 cupsArrayAdd(products, strdup(product));
1229 }
3d8365b8 1230 else if (!strncmp(line, "*PSVersion:", 11))
b94498cf 1231 {
1232 sscanf(line, "%*[^\"]\"%255[^\"]", psversion);
1233 cupsArrayAdd(psversions, strdup(psversion));
1234 }
3d8365b8 1235 else if (!strncmp(line, "*cupsLanguages:", 15))
b94498cf 1236 {
1237 char *start; /* Start of language */
1238
1239
1240 for (start = line + 15; *start && isspace(*start & 255); start ++);
1241
1242 if (*start++ == '\"')
1243 {
1244 while (*start)
1245 {
1246 for (ptr = start + 1;
1247 *ptr && *ptr != '\"' && !isspace(*ptr & 255);
1248 ptr ++);
1249
1250 if (*ptr)
1251 {
1252 *ptr++ = '\0';
1253
1254 while (isspace(*ptr & 255))
1255 *ptr++ = '\0';
1256 }
1257
1258 cupsArrayAdd(cups_languages, strdup(start));
1259 start = ptr;
1260 }
1261 }
1262 }
3d8365b8 1263 else if (!strncmp(line, "*cupsFax:", 9))
1264 {
1265 for (ptr = line + 9; isspace(*ptr & 255); ptr ++);
1266
1267 if (!strncasecmp(ptr, "true", 4))
1268 type = PPD_TYPE_FAX;
1269 }
1270 else if (!strncmp(line, "*cupsFilter:", 12) &&
1271 (type == PPD_TYPE_POSTSCRIPT || type == PPD_TYPE_UNKNOWN))
1272 {
1273 if (strstr(line + 12, "application/vnd.cups-raster"))
1274 type = PPD_TYPE_RASTER;
1275 else if (strstr(line + 12, "application/vnd.cups-pdf"))
1276 type = PPD_TYPE_PDF;
1277 else
1278 type = PPD_TYPE_UNKNOWN;
1279 }
1280 else if (!strncmp(line, "*cupsModelNumber:", 17))
1281 sscanf(line, "*cupsModelNumber:%d", &model_number);
ef416fc2 1282 else if (!strncmp(line, "*OpenUI", 7))
1283 {
1284 /*
1285 * Stop early if we have a NickName or ModelName attributes
1286 * before the first OpenUI...
1287 */
1288
b94498cf 1289 if ((model_name[0] || nick_name[0]) && cupsArrayCount(products) > 0 &&
1290 cupsArrayCount(psversions) > 0)
ef416fc2 1291 break;
1292 }
ef416fc2 1293 }
1294
1295 /*
1296 * Close the file...
1297 */
1298
1299 cupsFileClose(fp);
1300
1301 /*
1302 * See if we got all of the required info...
1303 */
1304
1305 if (nick_name[0])
80ca4592 1306 cupsCharsetToUTF8((cups_utf8_t *)make_model, nick_name,
1307 sizeof(make_model), _ppdGetEncoding(lang_encoding));
ef416fc2 1308 else
1309 strcpy(make_model, model_name);
1310
1311 while (isspace(make_model[0] & 255))
1312 _cups_strcpy(make_model, make_model + 1);
1313
b94498cf 1314 if (!make_model[0] || cupsArrayCount(products) == 0 ||
1315 cupsArrayCount(psversions) == 0)
f899b121 1316 {
1317 /*
1318 * We don't have all the info needed, so skip this file...
1319 */
1320
1321 if (!make_model[0])
1322 fprintf(stderr, "WARNING: Missing NickName and ModelName in %s!\n",
1323 filename);
1324
1325 if (cupsArrayCount(products) == 0)
1326 fprintf(stderr, "WARNING: Missing Product in %s!\n", filename);
1327
b94498cf 1328 if (cupsArrayCount(psversions) == 0)
1329 fprintf(stderr, "WARNING: Missing PSVersion in %s!\n", filename);
f899b121 1330
b94498cf 1331 free_array(products);
1332 free_array(psversions);
1333 free_array(cups_languages);
f899b121 1334
1335 continue;
1336 }
ef416fc2 1337
3d8365b8 1338 if (model_name[0])
1339 cupsArrayAdd(products, strdup(model_name));
1340
ef416fc2 1341 /*
1342 * See if we got a manufacturer...
1343 */
1344
1345 while (isspace(manufacturer[0] & 255))
1346 _cups_strcpy(manufacturer, manufacturer + 1);
1347
1348 if (!manufacturer[0] || !strcmp(manufacturer, "ESP"))
1349 {
1350 /*
1351 * Nope, copy the first part of the make and model then...
1352 */
1353
1354 strlcpy(manufacturer, make_model, sizeof(manufacturer));
1355
1356 /*
1357 * Truncate at the first space, dash, or slash, or make the
1358 * manufacturer "Other"...
1359 */
1360
1361 for (ptr = manufacturer; *ptr; ptr ++)
1362 if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
1363 break;
1364
1365 if (*ptr && ptr > manufacturer)
1366 *ptr = '\0';
1367 else if (!strncasecmp(manufacturer, "agfa", 4))
1368 strcpy(manufacturer, "AGFA");
1369 else if (!strncasecmp(manufacturer, "herk", 4) ||
1370 !strncasecmp(manufacturer, "linotype", 8))
1371 strcpy(manufacturer, "LHAG");
1372 else
1373 strcpy(manufacturer, "Other");
1374
1375 /*
1376 * Hack for various vendors...
1377 */
1378
1379 if (!strcasecmp(manufacturer, "XPrint"))
1380 strcpy(manufacturer, "Xerox");
1381 else if (!strcasecmp(manufacturer, "Eastman"))
1382 strcpy(manufacturer, "Kodak");
1383 else if (!strcasecmp(manufacturer, "laserwriter"))
1384 strcpy(manufacturer, "Apple");
1385 else if (!strcasecmp(manufacturer, "colorpoint"))
1386 strcpy(manufacturer, "Seiko");
1387 else if (!strcasecmp(manufacturer, "fiery"))
1388 strcpy(manufacturer, "EFI");
1389 else if (!strcasecmp(manufacturer, "ps") ||
1390 !strcasecmp(manufacturer, "colorpass"))
1391 strcpy(manufacturer, "Canon");
1392 else if (!strncasecmp(manufacturer, "primera", 7))
1393 strcpy(manufacturer, "Fargo");
1394 else if (!strcasecmp(manufacturer, "designjet"))
1395 strcpy(manufacturer, "HP");
1396 }
1397 else if (!strncasecmp(manufacturer, "LHAG", 4) ||
1398 !strncasecmp(manufacturer, "linotype", 8))
1399 strcpy(manufacturer, "LHAG");
1400
1401 /*
80ca4592 1402 * Fix the lang_version as needed...
ef416fc2 1403 */
1404
80ca4592 1405 if ((ptr = strchr(lang_version, '-')) != NULL)
ef416fc2 1406 *ptr++ = '\0';
80ca4592 1407 else if ((ptr = strchr(lang_version, '_')) != NULL)
ef416fc2 1408 *ptr++ = '\0';
1409
1410 if (ptr)
1411 {
1412 /*
1413 * Setup the country suffix...
1414 */
1415
1416 country[0] = '_';
1417 _cups_strcpy(country + 1, ptr);
1418 }
1419 else
1420 {
1421 /*
1422 * No country suffix...
1423 */
1424
1425 country[0] = '\0';
1426 }
1427
1428 for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
80ca4592 1429 if (!strcasecmp(languages[i].version, lang_version))
ef416fc2 1430 break;
1431
1432 if (i < (int)(sizeof(languages) / sizeof(languages[0])))
1433 {
1434 /*
1435 * Found a known language...
1436 */
1437
80ca4592 1438 snprintf(lang_version, sizeof(lang_version), "%s%s",
1439 languages[i].language, country);
ef416fc2 1440 }
1441 else
1442 {
1443 /*
1444 * Unknown language; use "xx"...
1445 */
1446
80ca4592 1447 strcpy(lang_version, "xx");
ef416fc2 1448 }
1449
1450 /*
1451 * Add the PPD file...
1452 */
1453
1454 new_ppd = !ppd;
1455
1456 if (new_ppd)
1457 {
1458 /*
1459 * Add new PPD file...
1460 */
1461
1462 fprintf(stderr, "DEBUG: [cups-driverd] Adding ppd \"%s\"...\n", name);
1463
b94498cf 1464 ppd = add_ppd(name, lang_version, manufacturer, make_model, device_id,
1465 (char *)cupsArrayFirst(products),
1466 (char *)cupsArrayFirst(psversions),
3d8365b8 1467 dent->fileinfo.st_mtime, dent->fileinfo.st_size,
1468 model_number, type);
b94498cf 1469
1470 if (!ppd)
ef416fc2 1471 {
1472 cupsDirClose(dir);
1473 return (0);
1474 }
1475 }
1476 else
1477 {
1478 /*
1479 * Update existing record...
1480 */
1481
1482 fprintf(stderr, "DEBUG: [cups-driverd] Updating ppd \"%s\"...\n", name);
1483
1484 memset(ppd, 0, sizeof(ppd_info_t));
1485
3d8365b8 1486 ppd->found = 1;
1487 ppd->record.mtime = dent->fileinfo.st_mtime;
1488 ppd->record.size = dent->fileinfo.st_size;
1489 ppd->record.model_number = model_number;
1490 ppd->record.type = type;
ef416fc2 1491
1492 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
1493 strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make));
1494 strlcpy(ppd->record.make_and_model, make_model,
1495 sizeof(ppd->record.make_and_model));
b94498cf 1496 strlcpy(ppd->record.languages[0], lang_version,
1497 sizeof(ppd->record.languages[0]));
1498 strlcpy(ppd->record.products[0], (char *)cupsArrayFirst(products),
1499 sizeof(ppd->record.products[0]));
1500 strlcpy(ppd->record.psversions[0], (char *)cupsArrayFirst(psversions),
1501 sizeof(ppd->record.psversions[0]));
bd7854cb 1502 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
ef416fc2 1503 }
1504
f899b121 1505 /*
b94498cf 1506 * Add remaining products, versions, and languages...
f899b121 1507 */
1508
b94498cf 1509 for (i = 1;
1510 i < PPD_MAX_PROD && (ptr = (char *)cupsArrayNext(products)) != NULL;
1511 i ++)
1512 strlcpy(ppd->record.products[i], ptr,
1513 sizeof(ppd->record.products[0]));
1514
1515 for (i = 1;
1516 i < PPD_MAX_VERS && (ptr = (char *)cupsArrayNext(psversions)) != NULL;
1517 i ++)
1518 strlcpy(ppd->record.psversions[i], ptr,
1519 sizeof(ppd->record.psversions[0]));
f899b121 1520
b94498cf 1521 for (i = 1, ptr = (char *)cupsArrayFirst(cups_languages);
1522 i < PPD_MAX_LANG && ptr;
1523 i ++, ptr = (char *)cupsArrayNext(cups_languages))
1524 strlcpy(ppd->record.languages[i], ptr,
1525 sizeof(ppd->record.languages[0]));
1526
1527 /*
1528 * Free products, versions, and languages...
1529 */
1530
1531 free_array(cups_languages);
1532 free_array(products);
1533 free_array(psversions);
f899b121 1534
ef416fc2 1535 ChangedPPD = 1;
1536 }
1537
1538 cupsDirClose(dir);
1539
1540 return (1);
1541}
1542
1543
1544/*
1545 * 'load_drivers()' - Load driver-generated PPD files.
1546 */
1547
bd7854cb 1548static int /* O - 1 on success, 0 on failure */
ef416fc2 1549load_drivers(void)
1550{
b94498cf 1551 int i; /* Looping var */
1552 char *start, /* Start of value */
1553 *ptr; /* Pointer into string */
1554 const char *server_bin; /* CUPS_SERVERBIN env variable */
ef416fc2 1555 char drivers[1024]; /* Location of driver programs */
1556 FILE *fp; /* Pipe to driver program */
1557 cups_dir_t *dir; /* Directory pointer */
1558 cups_dentry_t *dent; /* Directory entry */
1559 char filename[1024], /* Name of driver */
1560 line[2048], /* Line from driver */
1561 name[512], /* ppd-name */
ef416fc2 1562 make[128], /* ppd-make */
b94498cf 1563 make_and_model[128], /* ppd-make-and-model */
1564 device_id[128], /* ppd-device-id */
1565 languages[128], /* ppd-natural-language */
1566 product[128], /* ppd-product */
3d8365b8 1567 psversion[128], /* ppd-psversion */
1568 type_str[128]; /* ppd-type */
1569 int type; /* PPD type */
b94498cf 1570 ppd_info_t *ppd; /* Newly added PPD */
ef416fc2 1571
1572
1573 /*
1574 * Try opening the driver directory...
1575 */
1576
1577 if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
1578 server_bin = CUPS_SERVERBIN;
1579
1580 snprintf(drivers, sizeof(drivers), "%s/driver", server_bin);
1581
1582 if ((dir = cupsDirOpen(drivers)) == NULL)
1583 {
1584 fprintf(stderr, "ERROR: [cups-driverd] Unable to open driver directory "
1585 "\"%s\": %s\n",
1586 drivers, strerror(errno));
1587 return (0);
1588 }
1589
1590 /*
1591 * Loop through all of the device drivers...
1592 */
1593
1594 while ((dent = cupsDirRead(dir)) != NULL)
1595 {
1596 /*
1597 * Only look at executable files...
1598 */
1599
1600 if (!(dent->fileinfo.st_mode & 0111) || !S_ISREG(dent->fileinfo.st_mode))
1601 continue;
1602
1603 /*
1604 * Run the driver with no arguments and collect the output...
1605 */
1606
bd7854cb 1607 snprintf(filename, sizeof(filename), "%s/%s list", drivers, dent->filename);
ef416fc2 1608 if ((fp = popen(filename, "r")) != NULL)
1609 {
1610 while (fgets(line, sizeof(line), fp) != NULL)
1611 {
1612 /*
1613 * Each line is of the form:
1614 *
f899b121 1615 * "ppd-name" ppd-natural-language "ppd-make" "ppd-make-and-model" \
b94498cf 1616 * "ppd-device-id" "ppd-product" "ppd-psversion"
ef416fc2 1617 */
1618
bd7854cb 1619 device_id[0] = '\0';
f899b121 1620 product[0] = '\0';
b94498cf 1621 psversion[0] = '\0';
3d8365b8 1622 strcpy(type_str, "postscript");
bd7854cb 1623
1624 if (sscanf(line, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\""
b94498cf 1625 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
3d8365b8 1626 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
1627 "%*[ \t]\"%127[^\"]\"",
b94498cf 1628 name, languages, make, make_and_model,
3d8365b8 1629 device_id, product, psversion, type_str) < 4)
ef416fc2 1630 {
1631 /*
1632 * Bad format; strip trailing newline and write an error message.
1633 */
1634
1635 if (line[strlen(line) - 1] == '\n')
1636 line[strlen(line) - 1] = '\0';
1637
1638 fprintf(stderr, "ERROR: [cups-driverd] Bad line from \"%s\": %s\n",
1639 dent->filename, line);
1640 break;
1641 }
1642 else
1643 {
1644 /*
1645 * Add the device to the array of available devices...
1646 */
1647
b94498cf 1648 if ((start = strchr(languages, ',')) != NULL)
1649 *start++ = '\0';
1650
3d8365b8 1651 for (type = 0;
1652 type < (int)(sizeof(ppd_types) / sizeof(ppd_types[0]));
1653 type ++)
1654 if (!strcmp(type_str, ppd_types[type]))
1655 break;
1656
1657 if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0])))
1658 {
1659 fprintf(stderr, "ERROR: [cups-driverd] Bad ppd-type \"%s\" ignored!\n",
1660 type_str);
1661 type = PPD_TYPE_UNKNOWN;
1662 }
1663
b94498cf 1664 ppd = add_ppd(name, languages, make, make_and_model, device_id,
3d8365b8 1665 product, psversion, 0, 0, 0, type);
b94498cf 1666
1667 if (!ppd)
ef416fc2 1668 {
1669 cupsDirClose(dir);
91c84a35 1670 pclose(fp);
ef416fc2 1671 return (0);
1672 }
1673
b94498cf 1674 if (start && *start)
1675 {
1676 for (i = 1; i < PPD_MAX_LANG && *start; i ++)
1677 {
1678 if ((ptr = strchr(start, ',')) != NULL)
1679 *ptr++ = '\0';
1680 else
1681 ptr = start + strlen(start);
1682
1683 strlcpy(ppd->record.languages[i], start,
1684 sizeof(ppd->record.languages[0]));
1685
1686 start = ptr;
1687 }
1688 }
1689
ef416fc2 1690 fprintf(stderr, "DEBUG: [cups-driverd] Added dynamic PPD \"%s\"...\n",
1691 name);
1692 }
1693 }
1694
1695 pclose(fp);
1696 }
1697 else
1698 fprintf(stderr, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n",
1699 filename, strerror(errno));
1700 }
1701
1702 cupsDirClose(dir);
1703
1704 return (1);
1705}
1706
1707
1708/*
db1f069b 1709 * End of "$Id: cups-driverd.c 6762 2007-08-02 18:05:03Z mike $".
ef416fc2 1710 */