]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/cups-driverd.c
Merge changes from CUPS 1.4svn-r7626.
[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>
5eb9da71 39#include <cups/ppd-private.h>
ef416fc2 40
41
b94498cf 42/*
43 * Constants...
44 */
45
3d8365b8 46#define PPD_SYNC 0x50504434 /* Sync word for ppds.dat (PPD4) */
b94498cf 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
3d8365b8 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
ac884b6a 57static const char * const ppd_types[] = /* ppd-type values */
3d8365b8 58{
59 "postscript",
60 "pdf",
61 "raster",
62 "fax",
63 "unknown"
64};
65
b94498cf 66
ef416fc2 67/*
68 * PPD information structures...
69 */
70
71typedef struct /**** PPD record ****/
72{
73 time_t mtime; /* Modification time */
74 size_t size; /* Size in bytes */
3d8365b8 75 int model_number; /* cupsModelNumber */
76 int type; /* ppd-type */
b94498cf 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 */
ef416fc2 84 make[128], /* Manufacturer */
f899b121 85 make_and_model[128], /* NickName/ModelName */
bd7854cb 86 device_id[128]; /* IEEE 1284 Device ID */
b94498cf 87} ppd_rec_t;
ef416fc2 88
89typedef 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
100int NumPPDs, /* Number of PPD files */
101 SortedPPDs, /* Number of sorted PPD files */
102 AllocPPDs; /* Number of allocated entries */
103ppd_info_t *PPDs; /* PPD file info */
104int ChangedPPD; /* Did we change the PPD database? */
105
106
107/*
108 * Local functions...
109 */
110
b94498cf 111static ppd_info_t *add_ppd(const char *name, const char *language,
f899b121 112 const char *make, const char *make_and_model,
113 const char *device_id, const char *product,
b94498cf 114 const char *psversion, time_t mtime,
3d8365b8 115 size_t size, int model_number, int type);
b94498cf 116static int cat_ppd(const char *name, int request_id);
bd7854cb 117static int compare_names(const ppd_info_t *p0,
118 const ppd_info_t *p1);
119static int compare_ppds(const ppd_info_t *p0,
120 const ppd_info_t *p1);
b94498cf 121static void free_array(cups_array_t *a);
bd7854cb 122static int list_ppds(int request_id, int limit, const char *opt);
123static int load_drivers(void);
b94498cf 124static int load_ppds(const char *d, const char *p, int descend);
ef416fc2 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
135int /* O - Exit code */
136main(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"))
b94498cf 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])));
ef416fc2 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);
b94498cf 152 fputs("Usage: cups-driverd get request_id ppd-name\n", stderr);
ef416fc2 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
bd7854cb 163static ppd_info_t * /* O - PPD */
ef416fc2 164add_ppd(const char *name, /* I - PPD name */
b94498cf 165 const char *language, /* I - LanguageVersion */
ef416fc2 166 const char *make, /* I - Manufacturer */
f899b121 167 const char *make_and_model, /* I - NickName/ModelName */
89d46774 168 const char *device_id, /* I - 1284DeviceID */
f899b121 169 const char *product, /* I - Product */
b94498cf 170 const char *psversion, /* I - PSVersion */
ef416fc2 171 time_t mtime, /* I - Modification time */
3d8365b8 172 size_t size, /* I - File size */
173 int model_number, /* I - Model number */
174 int type) /* I - Driver type */
ef416fc2 175{
176 ppd_info_t *ppd; /* PPD */
b86bc4cf 177 char *recommended; /* Foomatic driver string */
ef416fc2 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 {
b94498cf 199 fprintf(stderr,
200 "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
ef416fc2 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
3d8365b8 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;
ef416fc2 222
223 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
b94498cf 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]));
ef416fc2 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));
bd7854cb 232 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
ef416fc2 233
b86bc4cf 234 /*
235 * Strip confusing (and often wrong) "recommended" suffix added by
236 * Foomatic drivers...
237 */
238
b94498cf 239 if ((recommended = strstr(ppd->record.make_and_model,
240 " (recommended)")) != NULL)
b86bc4cf 241 *recommended = '\0';
242
ef416fc2 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
bd7854cb 255static int /* O - Exit code */
b94498cf 256cat_ppd(const char *name, /* I - PPD name */
257 int request_id) /* I - Request ID for response? */
ef416fc2 258{
259 char scheme[256], /* Scheme from PPD name */
260 *sptr; /* Pointer into scheme */
261 char line[1024]; /* Line/filename */
b94498cf 262 char message[2048]; /* status-message */
ef416fc2 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
09a101d6 287 if (request_id > 0)
288 puts("Content-Type: application/ipp\n");
b94498cf 289
ef416fc2 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 */
c934a06c 297 char *argv[4]; /* Arguments for program */
ef416fc2 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));
b94498cf 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
ef416fc2 327 return (1);
328 }
329
330 /*
331 * Yes, let it cat the PPD file...
332 */
333
b94498cf 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
c934a06c
MS
344 argv[0] = scheme;
345 argv[1] = (char *)"cat";
346 argv[2] = (char *)name;
347 argv[3] = NULL;
348
349 if (cupsdExec(line, argv))
ef416fc2 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 */
5eb9da71
MS
1066 psversion[256], /* PSVersion */
1067 temp[512]; /* Temporary make and model */
3d8365b8 1068 int model_number, /* cupsModelNumber */
1069 type; /* ppd-type */
b94498cf 1070 cups_array_t *products, /* Product array */
1071 *psversions, /* PSVersion array */
1072 *cups_languages; /* cupsLanguages array */
ef416fc2 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 {
b94498cf 1082 { "chinese", "zh" },
839a51c8 1083 { "czech", "cs" },
ef416fc2 1084 { "danish", "da" },
1085 { "dutch", "nl" },
1086 { "english", "en" },
1087 { "finnish", "fi" },
1088 { "french", "fr" },
1089 { "german", "de" },
1090 { "greek", "el" },
839a51c8 1091 { "hungarian", "hu" },
ef416fc2 1092 { "italian", "it" },
7594b224 1093 { "japanese", "ja" },
bc44d920 1094 { "korean", "ko" },
ef416fc2 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 {
839a51c8
MS
1108 if (errno != ENOENT)
1109 fprintf(stderr,
1110 "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
1111 d, strerror(errno));
1112
ef416fc2 1113 return (0);
1114 }
1115
1116 while ((dent = cupsDirRead(dir)) != NULL)
1117 {
bd7854cb 1118 /*
1119 * Skip files/directories starting with "."...
1120 */
1121
1122 if (dent->filename[0] == '.')
1123 continue;
1124
ef416fc2 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
b94498cf 1142 if (descend)
1143 if (!load_ppds(filename, name, 1))
1144 {
1145 cupsDirClose(dir);
1146 return (1);
1147 }
ef416fc2 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
b94498cf 1203 cups_languages = cupsArrayNew(NULL, NULL);
1204 products = cupsArrayNew(NULL, NULL);
1205 psversions = cupsArrayNew(NULL, NULL);
f899b121 1206
80ca4592 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");
3d8365b8 1213 model_number = 0;
1214 type = PPD_TYPE_POSTSCRIPT;
ef416fc2 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);
80ca4592 1222 else if (!strncmp(line, "*LanguageEncoding:", 18))
1223 sscanf(line, "%*[^:]:%63s", lang_encoding);
ef416fc2 1224 else if (!strncmp(line, "*LanguageVersion:", 17))
80ca4592 1225 sscanf(line, "%*[^:]:%63s", lang_version);
ef416fc2 1226 else if (!strncmp(line, "*NickName:", 10))
1227 sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
89d46774 1228 else if (!strncasecmp(line, "*1284DeviceID:", 14))
bd7854cb 1229 sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
3d8365b8 1230 else if (!strncmp(line, "*Product:", 9))
f899b121 1231 {
5eb9da71
MS
1232 if (sscanf(line, "%*[^\"]\"(%255[^)]", product) == 1)
1233 cupsArrayAdd(products, strdup(product));
f899b121 1234 }
3d8365b8 1235 else if (!strncmp(line, "*PSVersion:", 11))
b94498cf 1236 {
1237 sscanf(line, "%*[^\"]\"%255[^\"]", psversion);
1238 cupsArrayAdd(psversions, strdup(psversion));
1239 }
3d8365b8 1240 else if (!strncmp(line, "*cupsLanguages:", 15))
b94498cf 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 }
3d8365b8 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 }
a0f6818e 1275 else if (!strncmp(line, "*cupsFilter:", 12) && type == PPD_TYPE_POSTSCRIPT)
3d8365b8 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;
3d8365b8 1281 }
1282 else if (!strncmp(line, "*cupsModelNumber:", 17))
1283 sscanf(line, "*cupsModelNumber:%d", &model_number);
ef416fc2 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
b94498cf 1291 if ((model_name[0] || nick_name[0]) && cupsArrayCount(products) > 0 &&
1292 cupsArrayCount(psversions) > 0)
ef416fc2 1293 break;
1294 }
ef416fc2 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])
80ca4592 1308 cupsCharsetToUTF8((cups_utf8_t *)make_model, nick_name,
1309 sizeof(make_model), _ppdGetEncoding(lang_encoding));
ef416fc2 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
b94498cf 1316 if (!make_model[0] || cupsArrayCount(products) == 0 ||
1317 cupsArrayCount(psversions) == 0)
f899b121 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
b94498cf 1330 if (cupsArrayCount(psversions) == 0)
1331 fprintf(stderr, "WARNING: Missing PSVersion in %s!\n", filename);
f899b121 1332
b94498cf 1333 free_array(products);
1334 free_array(psversions);
1335 free_array(cups_languages);
f899b121 1336
1337 continue;
1338 }
ef416fc2 1339
3d8365b8 1340 if (model_name[0])
1341 cupsArrayAdd(products, strdup(model_name));
1342
ef416fc2 1343 /*
5eb9da71 1344 * Normalize the make and model string...
ef416fc2 1345 */
1346
1347 while (isspace(manufacturer[0] & 255))
1348 _cups_strcpy(manufacturer, manufacturer + 1);
1349
5eb9da71
MS
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
ef416fc2 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';
ef416fc2 1380 else
1381 strcpy(manufacturer, "Other");
ef416fc2 1382 }
1383 else if (!strncasecmp(manufacturer, "LHAG", 4) ||
1384 !strncasecmp(manufacturer, "linotype", 8))
1385 strcpy(manufacturer, "LHAG");
5eb9da71
MS
1386 else if (!strncasecmp(manufacturer, "Hewlett", 7))
1387 strcpy(manufacturer, "HP");
ef416fc2 1388
1389 /*
80ca4592 1390 * Fix the lang_version as needed...
ef416fc2 1391 */
1392
80ca4592 1393 if ((ptr = strchr(lang_version, '-')) != NULL)
ef416fc2 1394 *ptr++ = '\0';
80ca4592 1395 else if ((ptr = strchr(lang_version, '_')) != NULL)
ef416fc2 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 ++)
80ca4592 1417 if (!strcasecmp(languages[i].version, lang_version))
ef416fc2 1418 break;
1419
1420 if (i < (int)(sizeof(languages) / sizeof(languages[0])))
1421 {
1422 /*
1423 * Found a known language...
1424 */
1425
80ca4592 1426 snprintf(lang_version, sizeof(lang_version), "%s%s",
1427 languages[i].language, country);
ef416fc2 1428 }
1429 else
1430 {
1431 /*
1432 * Unknown language; use "xx"...
1433 */
1434
80ca4592 1435 strcpy(lang_version, "xx");
ef416fc2 1436 }
1437
1438 /*
5eb9da71 1439 * Record the PPD file...
ef416fc2 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
b94498cf 1452 ppd = add_ppd(name, lang_version, manufacturer, make_model, device_id,
1453 (char *)cupsArrayFirst(products),
1454 (char *)cupsArrayFirst(psversions),
3d8365b8 1455 dent->fileinfo.st_mtime, dent->fileinfo.st_size,
1456 model_number, type);
b94498cf 1457
1458 if (!ppd)
ef416fc2 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
3d8365b8 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;
ef416fc2 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));
b94498cf 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]));
bd7854cb 1490 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
ef416fc2 1491 }
1492
f899b121 1493 /*
b94498cf 1494 * Add remaining products, versions, and languages...
f899b121 1495 */
1496
b94498cf 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]));
f899b121 1508
b94498cf 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);
f899b121 1522
ef416fc2 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
bd7854cb 1536static int /* O - 1 on success, 0 on failure */
ef416fc2 1537load_drivers(void)
1538{
b94498cf 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 */
ef416fc2 1543 char drivers[1024]; /* Location of driver programs */
c934a06c
MS
1544 int pid; /* Process ID for driver program */
1545 cups_file_t *fp; /* Pipe to driver program */
ef416fc2 1546 cups_dir_t *dir; /* Directory pointer */
1547 cups_dentry_t *dent; /* Directory entry */
c934a06c
MS
1548 char *argv[3], /* Arguments for command */
1549 filename[1024], /* Name of driver */
ef416fc2 1550 line[2048], /* Line from driver */
1551 name[512], /* ppd-name */
ef416fc2 1552 make[128], /* ppd-make */
b94498cf 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 */
3d8365b8 1557 psversion[128], /* ppd-psversion */
1558 type_str[128]; /* ppd-type */
1559 int type; /* PPD type */
b94498cf 1560 ppd_info_t *ppd; /* Newly added PPD */
ef416fc2 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 "
839a51c8
MS
1575 "\"%s\": %s\n",
1576 drivers, strerror(errno));
ef416fc2 1577 return (0);
1578 }
1579
1580 /*
1581 * Loop through all of the device drivers...
1582 */
1583
c934a06c
MS
1584 argv[1] = (char *)"list";
1585 argv[2] = NULL;
1586
ef416fc2 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
c934a06c
MS
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)
ef416fc2 1604 {
c934a06c 1605 while (cupsFileGets(fp, line, sizeof(line)))
ef416fc2 1606 {
1607 /*
1608 * Each line is of the form:
1609 *
f899b121 1610 * "ppd-name" ppd-natural-language "ppd-make" "ppd-make-and-model" \
b94498cf 1611 * "ppd-device-id" "ppd-product" "ppd-psversion"
ef416fc2 1612 */
1613
bd7854cb 1614 device_id[0] = '\0';
f899b121 1615 product[0] = '\0';
b94498cf 1616 psversion[0] = '\0';
3d8365b8 1617 strcpy(type_str, "postscript");
bd7854cb 1618
1619 if (sscanf(line, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\""
b94498cf 1620 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
3d8365b8 1621 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
1622 "%*[ \t]\"%127[^\"]\"",
b94498cf 1623 name, languages, make, make_and_model,
3d8365b8 1624 device_id, product, psversion, type_str) < 4)
ef416fc2 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
b94498cf 1643 if ((start = strchr(languages, ',')) != NULL)
1644 *start++ = '\0';
1645
3d8365b8 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 {
c934a06c
MS
1654 fprintf(stderr,
1655 "ERROR: [cups-driverd] Bad ppd-type \"%s\" ignored!\n",
3d8365b8 1656 type_str);
1657 type = PPD_TYPE_UNKNOWN;
1658 }
1659
b94498cf 1660 ppd = add_ppd(name, languages, make, make_and_model, device_id,
3d8365b8 1661 product, psversion, 0, 0, 0, type);
b94498cf 1662
1663 if (!ppd)
ef416fc2 1664 {
1665 cupsDirClose(dir);
c934a06c 1666 cupsFileClose(fp);
ef416fc2 1667 return (0);
1668 }
1669
b94498cf 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
ef416fc2 1686 fprintf(stderr, "DEBUG: [cups-driverd] Added dynamic PPD \"%s\"...\n",
1687 name);
1688 }
1689 }
1690
c934a06c 1691 cupsFileClose(fp);
ef416fc2 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/*
db1f069b 1705 * End of "$Id: cups-driverd.c 6762 2007-08-02 18:05:03Z mike $".
ef416fc2 1706 */