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