]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/cups-driverd.cxx
Merge changes from CUPS 1.4svn-r8346.
[thirdparty/cups.git] / scheduler / cups-driverd.cxx
CommitLineData
ef416fc2 1/*
4509bb49 2 * "$Id$"
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 *
c168a833 10 * Copyright 2007-2009 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 *
58dc1933
MS
21 * main() - Scan for drivers and return an IPP response.
22 * add_ppd() - Add a PPD file.
23 * cat_drv() - Generate a PPD from a driver info file.
24 * cat_ppd() - Copy a PPD file to stdout.
25 * copy_static() - Copy a static PPD file to stdout.
26 * compare_matches() - Compare PPD match scores for sorting.
27 * compare_names() - Compare PPD filenames for sorting.
28 * compare_ppds() - Compare PPD file make and model names for sorting.
29 * free_array() - Free an array of strings.
30 * list_ppds() - List PPD files.
31 * load_ppds() - Load PPD files recursively.
32 * load_drv() - Load the PPDs from a driver information file.
33 * load_drivers() - Load driver-generated PPD files.
34 * regex_device_id() - Compile a regular expression based on the 1284 device
35 * ID.
36 * regex_string() - Construct a regular expression to compare a simple
37 * string.
ef416fc2 38 */
39
40/*
41 * Include necessary headers...
42 */
43
44#include "util.h"
45#include <cups/dir.h>
80ca4592 46#include <cups/transcode.h>
5eb9da71 47#include <cups/ppd-private.h>
4509bb49 48#include <ppdc/ppdc.h>
58dc1933 49#include <regex.h>
ef416fc2 50
51
b94498cf 52/*
53 * Constants...
54 */
55
ed6e7faf 56#define PPD_SYNC 0x50504436 /* Sync word for ppds.dat (PPD6) */
b94498cf 57#define PPD_MAX_LANG 32 /* Maximum languages */
58#define PPD_MAX_PROD 8 /* Maximum products */
59#define PPD_MAX_VERS 8 /* Maximum versions */
60
3d8365b8 61#define PPD_TYPE_POSTSCRIPT 0 /* PostScript PPD */
62#define PPD_TYPE_PDF 1 /* PDF PPD */
63#define PPD_TYPE_RASTER 2 /* CUPS raster PPD */
64#define PPD_TYPE_FAX 3 /* Facsimile/MFD PPD */
65#define PPD_TYPE_UNKNOWN 4 /* Other/hybrid PPD */
66ab9486 66#define PPD_TYPE_DRV 5 /* Driver info file */
3d8365b8 67
ac884b6a 68static const char * const ppd_types[] = /* ppd-type values */
3d8365b8 69{
70 "postscript",
71 "pdf",
72 "raster",
73 "fax",
74 "unknown"
75};
76
b94498cf 77
ef416fc2 78/*
79 * PPD information structures...
80 */
81
82typedef struct /**** PPD record ****/
83{
84 time_t mtime; /* Modification time */
749b1e90 85 off_t size; /* Size in bytes */
3d8365b8 86 int model_number; /* cupsModelNumber */
87 int type; /* ppd-type */
66ab9486
MS
88 char filename[512], /* Filename */
89 name[512], /* PPD name */
b94498cf 90 languages[PPD_MAX_LANG][6],
91 /* LanguageVersion/cupsLanguages */
92 products[PPD_MAX_PROD][128],
93 /* Product strings */
94 psversions[PPD_MAX_VERS][32],
95 /* PSVersion strings */
ef416fc2 96 make[128], /* Manufacturer */
f899b121 97 make_and_model[128], /* NickName/ModelName */
ed6e7faf
MS
98 device_id[256], /* IEEE 1284 Device ID */
99 scheme[128]; /* PPD scheme */
b94498cf 100} ppd_rec_t;
ef416fc2 101
102typedef struct /**** In-memory record ****/
103{
104 int found; /* 1 if PPD is found */
58dc1933 105 int matches; /* Match count */
ef416fc2 106 ppd_rec_t record; /* PPDs.dat record */
107} ppd_info_t;
108
109
110/*
111 * Globals...
112 */
113
66ab9486
MS
114cups_array_t *PPDsByName = NULL, /* PPD files sorted by filename and name */
115 *PPDsByMakeModel = NULL;/* PPD files sorted by make and model */
ef416fc2 116int ChangedPPD; /* Did we change the PPD database? */
117
118
119/*
120 * Local functions...
121 */
122
66ab9486
MS
123static ppd_info_t *add_ppd(const char *filename, const char *name,
124 const char *language, const char *make,
125 const char *make_and_model,
f899b121 126 const char *device_id, const char *product,
b94498cf 127 const char *psversion, time_t mtime,
ed6e7faf
MS
128 size_t size, int model_number, int type,
129 const char *scheme);
4509bb49 130static int cat_drv(const char *name, int request_id);
b94498cf 131static int cat_ppd(const char *name, int request_id);
4509bb49 132static int cat_static(const char *name, int request_id);
58dc1933
MS
133static int compare_matches(const ppd_info_t *p0,
134 const ppd_info_t *p1);
bd7854cb 135static int compare_names(const ppd_info_t *p0,
136 const ppd_info_t *p1);
137static int compare_ppds(const ppd_info_t *p0,
138 const ppd_info_t *p1);
b94498cf 139static void free_array(cups_array_t *a);
bd7854cb 140static int list_ppds(int request_id, int limit, const char *opt);
ed6e7faf
MS
141static int load_drivers(cups_array_t *include,
142 cups_array_t *exclude);
4509bb49
MS
143static int load_drv(const char *filename, const char *name,
144 cups_file_t *fp, time_t mtime, off_t size);
b94498cf 145static int load_ppds(const char *d, const char *p, int descend);
58dc1933
MS
146static regex_t *regex_device_id(const char *device_id);
147static regex_t *regex_string(const char *s);
ef416fc2 148
149
150/*
151 * 'main()' - Scan for drivers and return an IPP response.
152 *
153 * Usage:
154 *
155 * cups-driverd request_id limit options
156 */
157
158int /* O - Exit code */
159main(int argc, /* I - Number of command-line args */
160 char *argv[]) /* I - Command-line arguments */
161{
162 /*
163 * Install or list PPDs...
164 */
165
166 if (argc == 3 && !strcmp(argv[1], "cat"))
b94498cf 167 return (cat_ppd(argv[2], 0));
168 else if (argc == 4 && !strcmp(argv[1], "get"))
169 return (cat_ppd(argv[3], atoi(argv[2])));
ef416fc2 170 else if (argc == 5 && !strcmp(argv[1], "list"))
171 return (list_ppds(atoi(argv[2]), atoi(argv[3]), argv[4]));
172 else
173 {
174 fputs("Usage: cups-driverd cat ppd-name\n", stderr);
b94498cf 175 fputs("Usage: cups-driverd get request_id ppd-name\n", stderr);
ef416fc2 176 fputs("Usage: cups-driverd list request_id limit options\n", stderr);
177 return (1);
178 }
179}
180
181
182/*
183 * 'add_ppd()' - Add a PPD file.
184 */
185
bd7854cb 186static ppd_info_t * /* O - PPD */
66ab9486
MS
187add_ppd(const char *filename, /* I - PPD filename */
188 const char *name, /* I - PPD name */
b94498cf 189 const char *language, /* I - LanguageVersion */
ef416fc2 190 const char *make, /* I - Manufacturer */
f899b121 191 const char *make_and_model, /* I - NickName/ModelName */
89d46774 192 const char *device_id, /* I - 1284DeviceID */
f899b121 193 const char *product, /* I - Product */
b94498cf 194 const char *psversion, /* I - PSVersion */
ef416fc2 195 time_t mtime, /* I - Modification time */
3d8365b8 196 size_t size, /* I - File size */
197 int model_number, /* I - Model number */
ed6e7faf
MS
198 int type, /* I - Driver type */
199 const char *scheme) /* I - PPD scheme */
ef416fc2 200{
201 ppd_info_t *ppd; /* PPD */
b86bc4cf 202 char *recommended; /* Foomatic driver string */
ef416fc2 203
204
205 /*
206 * Add a new PPD file...
207 */
208
66ab9486 209 if ((ppd = (ppd_info_t *)calloc(1, sizeof(ppd_info_t))) == NULL)
ef416fc2 210 {
66ab9486
MS
211 fprintf(stderr,
212 "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
213 cupsArrayCount(PPDsByName));
214 return (NULL);
ef416fc2 215 }
216
ef416fc2 217 /*
218 * Zero-out the PPD data and copy the values over...
219 */
220
3d8365b8 221 ppd->found = 1;
222 ppd->record.mtime = mtime;
223 ppd->record.size = size;
224 ppd->record.model_number = model_number;
225 ppd->record.type = type;
ef416fc2 226
66ab9486 227 strlcpy(ppd->record.filename, filename, sizeof(ppd->record.filename));
ef416fc2 228 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
b94498cf 229 strlcpy(ppd->record.languages[0], language,
230 sizeof(ppd->record.languages[0]));
231 strlcpy(ppd->record.products[0], product, sizeof(ppd->record.products[0]));
232 strlcpy(ppd->record.psversions[0], psversion,
233 sizeof(ppd->record.psversions[0]));
ef416fc2 234 strlcpy(ppd->record.make, make, sizeof(ppd->record.make));
235 strlcpy(ppd->record.make_and_model, make_and_model,
236 sizeof(ppd->record.make_and_model));
bd7854cb 237 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
ed6e7faf 238 strlcpy(ppd->record.scheme, scheme, sizeof(ppd->record.scheme));
ef416fc2 239
b86bc4cf 240 /*
241 * Strip confusing (and often wrong) "recommended" suffix added by
242 * Foomatic drivers...
243 */
244
b94498cf 245 if ((recommended = strstr(ppd->record.make_and_model,
246 " (recommended)")) != NULL)
b86bc4cf 247 *recommended = '\0';
248
66ab9486
MS
249 /*
250 * Add the PPD to the PPD arrays...
251 */
252
253 cupsArrayAdd(PPDsByName, ppd);
254 cupsArrayAdd(PPDsByMakeModel, ppd);
255
ef416fc2 256 /*
257 * Return the new PPD pointer...
258 */
259
260 return (ppd);
261}
262
263
4509bb49
MS
264/*
265 * 'cat_drv()' - Generate a PPD from a driver info file.
266 */
267
268static int /* O - Exit code */
269cat_drv(const char *name, /* I - PPD name */
270 int request_id) /* I - Request ID for response? */
271{
272 const char *datadir; // CUPS_DATADIR env var
273 ppdcSource *src; // PPD source file data
274 ppdcDriver *d; // Current driver
275 cups_file_t *out; // Stdout via CUPS file API
276 char message[2048], // status-message
277 filename[1024], // Full path to .drv file(s)
278 scheme[32], // URI scheme ("drv")
279 userpass[256], // User/password info (unused)
280 host[2], // Hostname (unused)
281 resource[1024], // Resource path (/dir/to/filename.drv)
282 *pc_file_name; // Filename portion of URI
283 int port; // Port number (unused)
284
285
286 // Determine where CUPS has installed the data files...
287 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
288 datadir = CUPS_DATADIR;
289
290 // Pull out the
291 if (httpSeparateURI(HTTP_URI_CODING_ALL, name, scheme, sizeof(scheme),
292 userpass, sizeof(userpass), host, sizeof(host), &port,
293 resource, sizeof(resource)) < HTTP_URI_OK ||
294 strstr(resource, "../") ||
295 (pc_file_name = strrchr(resource, '/')) == NULL ||
296 pc_file_name == resource)
297 {
298 fprintf(stderr, "ERROR: Bad PPD name \"%s\"!\n", name);
299
300 if (request_id)
301 {
302 snprintf(message, sizeof(message), "Bad PPD name \"%s\"!", name);
303
304 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
305 cupsdSendIPPGroup(IPP_TAG_OPERATION);
306 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
307 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
308 "en-US");
309 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
310 cupsdSendIPPTrailer();
311 }
312
313 return (1);
314 }
315
316 *pc_file_name++ = '\0';
317
318#ifdef __APPLE__
319 if (!strncmp(resource, "/Library/Printers/PPDs.drv/", 27))
320 strlcpy(filename, resource, sizeof(filename));
321 else
322#endif // __APPLE__
323 {
324 snprintf(filename, sizeof(filename), "%s/drv%s", datadir, resource);
325 if (access(filename, 0))
326 snprintf(filename, sizeof(filename), "%s/model%s", datadir, resource);
327 }
328
329 src = new ppdcSource(filename);
330
331 for (d = (ppdcDriver *)src->drivers->first();
332 d;
333 d = (ppdcDriver *)src->drivers->next())
d1c13e16
MS
334 if (!strcmp(pc_file_name, d->pc_file_name->value) ||
335 (d->file_name && !strcmp(pc_file_name, d->file_name->value)))
4509bb49
MS
336 break;
337
338 if (d)
339 {
340 ppdcArray *locales; // Locale names
341 ppdcCatalog *catalog; // Message catalog in .drv file
342
343
58dc1933 344 fprintf(stderr, "DEBUG: [cups-driverd] %d locales defined in \"%s\"...\n",
4509bb49
MS
345 src->po_files->count, filename);
346
347 locales = new ppdcArray();
348 for (catalog = (ppdcCatalog *)src->po_files->first();
349 catalog;
350 catalog = (ppdcCatalog *)src->po_files->next())
351 {
58dc1933 352 fprintf(stderr, "DEBUG: [cups-driverd] Adding locale \"%s\"...\n",
4509bb49
MS
353 catalog->locale->value);
354 locales->add(catalog->locale);
355 }
356
357 if (request_id)
358 {
359 cupsdSendIPPHeader(IPP_OK, request_id);
360 cupsdSendIPPGroup(IPP_TAG_OPERATION);
361 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
362 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
363 "en-US");
364 cupsdSendIPPTrailer();
365 fflush(stdout);
366 }
367
368 out = cupsFileStdout();
369 d->write_ppd_file(out, NULL, locales, src, PPDC_LFONLY);
370 cupsFileClose(out);
371
e4572d57 372 locales->release();
4509bb49
MS
373 }
374 else
375 {
376 fprintf(stderr, "ERROR: PPD \"%s\" not found!\n", name);
377
378 if (request_id)
379 {
380 snprintf(message, sizeof(message), "PPD \"%s\" not found!", name);
381
382 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
383 cupsdSendIPPGroup(IPP_TAG_OPERATION);
384 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
385 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
386 "en-US");
387 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
388 cupsdSendIPPTrailer();
389 }
390 }
391
e4572d57 392 src->release();
4509bb49
MS
393
394 return (!d);
395}
396
397
ef416fc2 398/*
399 * 'cat_ppd()' - Copy a PPD file to stdout.
400 */
401
bd7854cb 402static int /* O - Exit code */
b94498cf 403cat_ppd(const char *name, /* I - PPD name */
404 int request_id) /* I - Request ID for response? */
ef416fc2 405{
406 char scheme[256], /* Scheme from PPD name */
4509bb49
MS
407 *sptr, /* Pointer into scheme */
408 line[1024], /* Line/filename */
409 message[2048]; /* status-message */
ef416fc2 410
411
412 /*
413 * Figure out if this is a static or dynamic PPD file...
414 */
415
416 strlcpy(scheme, name, sizeof(scheme));
417 if ((sptr = strchr(scheme, ':')) != NULL)
418 {
419 *sptr = '\0';
420
421 if (!strcmp(scheme, "file"))
422 {
423 /*
424 * "file:name" == "name"...
425 */
426
427 name += 5;
428 scheme[0] = '\0';
429 }
430 }
431 else
432 scheme[0] = '\0';
433
09a101d6 434 if (request_id > 0)
435 puts("Content-Type: application/ipp\n");
b94498cf 436
4509bb49
MS
437 if (!scheme[0])
438 return (cat_static(name, request_id));
439 else if (!strcmp(scheme, "drv"))
440 return (cat_drv(name, request_id));
441 else
ef416fc2 442 {
443 /*
444 * Dynamic PPD, see if we have a driver program to support it...
445 */
446
447 const char *serverbin; /* CUPS_SERVERBIN env var */
c934a06c 448 char *argv[4]; /* Arguments for program */
ef416fc2 449
450
451 if ((serverbin = getenv("CUPS_SERVERBIN")) == NULL)
452 serverbin = CUPS_SERVERBIN;
453
454 snprintf(line, sizeof(line), "%s/driver/%s", serverbin, scheme);
455 if (access(line, X_OK))
456 {
457 /*
458 * File does not exist or is not executable...
459 */
460
461 fprintf(stderr, "ERROR: [cups-driverd] Unable to access \"%s\" - %s\n",
462 line, strerror(errno));
b94498cf 463
464 if (request_id > 0)
465 {
466 snprintf(message, sizeof(message), "Unable to access \"%s\" - %s",
467 line, strerror(errno));
468
469 cupsdSendIPPHeader(IPP_NOT_FOUND, 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 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
475 cupsdSendIPPTrailer();
476 }
477
ef416fc2 478 return (1);
479 }
480
481 /*
482 * Yes, let it cat the PPD file...
483 */
484
b94498cf 485 if (request_id)
486 {
487 cupsdSendIPPHeader(IPP_OK, request_id);
488 cupsdSendIPPGroup(IPP_TAG_OPERATION);
489 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
490 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
491 "en-US");
492 cupsdSendIPPTrailer();
493 }
494
c934a06c
MS
495 argv[0] = scheme;
496 argv[1] = (char *)"cat";
497 argv[2] = (char *)name;
498 argv[3] = NULL;
499
500 if (cupsdExec(line, argv))
ef416fc2 501 {
502 /*
503 * Unable to execute driver...
504 */
505
506 fprintf(stderr, "ERROR: [cups-driverd] Unable to execute \"%s\" - %s\n",
507 line, strerror(errno));
508 return (1);
509 }
510 }
ef416fc2 511
4509bb49
MS
512 /*
513 * Return with no errors...
514 */
ef416fc2 515
4509bb49
MS
516 return (0);
517}
ef416fc2 518
b94498cf 519
4509bb49
MS
520/*
521 * 'copy_static()' - Copy a static PPD file to stdout.
522 */
b94498cf 523
4509bb49
MS
524static int /* O - Exit code */
525cat_static(const char *name, /* I - PPD name */
526 int request_id) /* I - Request ID for response? */
527{
528 cups_file_t *fp; /* PPD file */
529 const char *datadir; /* CUPS_DATADIR env var */
530 char line[1024], /* Line/filename */
531 message[2048]; /* status-message */
b94498cf 532
ef416fc2 533
4509bb49
MS
534 if (name[0] == '/' || strstr(name, "../") || strstr(name, "/.."))
535 {
ef416fc2 536 /*
4509bb49 537 * Bad name...
ef416fc2 538 */
539
4509bb49
MS
540 fprintf(stderr, "ERROR: [cups-driverd] Bad PPD name \"%s\"!\n", name);
541
542 if (request_id)
b94498cf 543 {
4509bb49 544 snprintf(message, sizeof(message), "Bad PPD name \"%s\"!", name);
b94498cf 545
4509bb49
MS
546 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
547 cupsdSendIPPGroup(IPP_TAG_OPERATION);
548 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
549 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
550 "en-US");
551 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
552 cupsdSendIPPTrailer();
b94498cf 553 }
b94498cf 554
4509bb49
MS
555 return (1);
556 }
b94498cf 557
4509bb49
MS
558 /*
559 * Try opening the file...
560 */
b94498cf 561
4509bb49
MS
562#ifdef __APPLE__
563 if (!strncmp(name, "System/Library/Printers/PPDs/Contents/Resources/", 48) ||
564 !strncmp(name, "Library/Printers/PPDs/Contents/Resources/", 41))
565 {
566 /*
567 * Map ppd-name to Mac OS X standard locations...
568 */
b94498cf 569
4509bb49
MS
570 snprintf(line, sizeof(line), "/%s", name);
571 }
572 else
b94498cf 573
4509bb49
MS
574#elif defined(__linux)
575 if (!strncmp(name, "lsb/usr/", 8))
576 {
577 /*
578 * Map ppd-name to LSB standard /usr/share/ppd location...
579 */
b94498cf 580
4509bb49
MS
581 snprintf(line, sizeof(line), "/usr/share/ppd/%s", name + 8);
582 }
583 else if (!strncmp(name, "lsb/opt/", 8))
584 {
585 /*
586 * Map ppd-name to LSB standard /opt/share/ppd location...
587 */
ef416fc2 588
4509bb49
MS
589 snprintf(line, sizeof(line), "/opt/share/ppd/%s", name + 8);
590 }
591 else if (!strncmp(name, "lsb/local/", 10))
592 {
593 /*
594 * Map ppd-name to LSB standard /usr/local/share/ppd location...
595 */
b94498cf 596
4509bb49
MS
597 snprintf(line, sizeof(line), "/usr/local/share/ppd/%s", name + 10);
598 }
599 else
b94498cf 600
4509bb49
MS
601#endif /* __APPLE__ */
602 {
603 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
604 datadir = CUPS_DATADIR;
b94498cf 605
4509bb49
MS
606 snprintf(line, sizeof(line), "%s/model/%s", datadir, name);
607 }
608
609 if ((fp = cupsFileOpen(line, "r")) == NULL)
610 {
611 fprintf(stderr, "ERROR: [cups-driverd] Unable to open \"%s\" - %s\n",
612 line, strerror(errno));
ef416fc2 613
b94498cf 614 if (request_id)
615 {
4509bb49
MS
616 snprintf(message, sizeof(message), "Unable to open \"%s\" - %s",
617 line, strerror(errno));
618
619 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
b94498cf 620 cupsdSendIPPGroup(IPP_TAG_OPERATION);
621 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
622 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
4509bb49
MS
623 "en-US");
624 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
b94498cf 625 cupsdSendIPPTrailer();
626 }
627
4509bb49
MS
628 return (1);
629 }
ef416fc2 630
4509bb49
MS
631 if (request_id)
632 {
633 cupsdSendIPPHeader(IPP_OK, request_id);
634 cupsdSendIPPGroup(IPP_TAG_OPERATION);
635 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
636 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
637 "en-US");
638 cupsdSendIPPTrailer();
ef416fc2 639 }
640
641 /*
4509bb49 642 * Now copy the file to stdout...
ef416fc2 643 */
644
4509bb49
MS
645 while (cupsFileGets(fp, line, sizeof(line)))
646 puts(line);
647
648 cupsFileClose(fp);
649
ef416fc2 650 return (0);
651}
652
653
58dc1933
MS
654/*
655 * 'compare_matches()' - Compare PPD match scores for sorting.
656 */
657
658static int
659compare_matches(const ppd_info_t *p0, /* I - First PPD */
660 const ppd_info_t *p1) /* I - Second PPD */
661{
662 if (p1->matches != p0->matches)
663 return (p1->matches - p0->matches);
664 else
e4572d57
MS
665 return (cupsdCompareNames(p0->record.make_and_model,
666 p1->record.make_and_model));
58dc1933
MS
667}
668
669
ef416fc2 670/*
671 * 'compare_names()' - Compare PPD filenames for sorting.
672 */
673
bd7854cb 674static int /* O - Result of comparison */
ef416fc2 675compare_names(const ppd_info_t *p0, /* I - First PPD file */
676 const ppd_info_t *p1) /* I - Second PPD file */
677{
66ab9486
MS
678 int diff; /* Difference between strings */
679
680
d1c13e16 681 if ((diff = strcmp(p0->record.filename, p1->record.filename)) != 0)
66ab9486
MS
682 return (diff);
683 else
d1c13e16 684 return (strcmp(p0->record.name, p1->record.name));
ef416fc2 685}
686
687
688/*
689 * 'compare_ppds()' - Compare PPD file make and model names for sorting.
690 */
691
bd7854cb 692static int /* O - Result of comparison */
ef416fc2 693compare_ppds(const ppd_info_t *p0, /* I - First PPD file */
694 const ppd_info_t *p1) /* I - Second PPD file */
695{
696 int diff; /* Difference between strings */
697
bd7854cb 698
ef416fc2 699 /*
700 * First compare manufacturers...
701 */
702
703 if ((diff = strcasecmp(p0->record.make, p1->record.make)) != 0)
704 return (diff);
705 else if ((diff = cupsdCompareNames(p0->record.make_and_model,
706 p1->record.make_and_model)) != 0)
707 return (diff);
708 else
d1c13e16 709 return (strcmp(p0->record.languages[0], p1->record.languages[0]));
b94498cf 710}
711
712
713/*
714 * 'free_array()' - Free an array of strings.
715 */
716
717static void
718free_array(cups_array_t *a) /* I - Array to free */
719{
720 char *ptr; /* Pointer to string */
721
722
723 for (ptr = (char *)cupsArrayFirst(a);
724 ptr;
725 ptr = (char *)cupsArrayNext(a))
726 free(ptr);
727
728 cupsArrayDelete(a);
ef416fc2 729}
730
731
732/*
733 * 'list_ppds()' - List PPD files.
734 */
735
bd7854cb 736static int /* O - Exit code */
ef416fc2 737list_ppds(int request_id, /* I - Request ID */
738 int limit, /* I - Limit */
739 const char *opt) /* I - Option argument */
740{
66ab9486 741 int i; /* Looping vars */
ef416fc2 742 int count; /* Number of PPDs to send */
743 ppd_info_t *ppd; /* Current PPD file */
744 cups_file_t *fp; /* ppds.dat file */
745 struct stat fileinfo; /* ppds.dat information */
746 char filename[1024], /* ppds.dat filename */
747 model[1024]; /* Model directory */
748 const char *cups_cachedir; /* CUPS_CACHEDIR environment variable */
749 const char *cups_datadir; /* CUPS_DATADIR environment variable */
750 int num_options; /* Number of options */
751 cups_option_t *options; /* Options */
ed6e7faf
MS
752 cups_array_t *requested, /* requested-attributes values */
753 *include, /* PPD schemes to include */
754 *exclude; /* PPD schemes to exclude */
755 const char *device_id, /* ppd-device-id option */
b94498cf 756 *language, /* ppd-natural-language option */
757 *make, /* ppd-make option */
758 *make_and_model, /* ppd-make-and-model option */
3d8365b8 759 *model_number_str, /* ppd-model-number option */
b94498cf 760 *product, /* ppd-product option */
3d8365b8 761 *psversion, /* ppd-psversion option */
762 *type_str; /* ppd-type option */
763 int model_number, /* ppd-model-number value */
764 type, /* ppd-type value */
58dc1933
MS
765 make_and_model_len, /* Length of ppd-make-and-model */
766 product_len, /* Length of ppd-product */
3d8365b8 767 send_device_id, /* Send ppd-device-id? */
b94498cf 768 send_make, /* Send ppd-make? */
769 send_make_and_model, /* Send ppd-make-and-model? */
3d8365b8 770 send_model_number, /* Send ppd-model-number? */
b94498cf 771 send_name, /* Send ppd-name? */
3d8365b8 772 send_natural_language, /* Send ppd-natural-language? */
b94498cf 773 send_product, /* Send ppd-product? */
774 send_psversion, /* Send ppd-psversion? */
3d8365b8 775 send_type, /* Send ppd-type? */
b94498cf 776 sent_header; /* Sent the IPP header? */
58dc1933
MS
777 regex_t *device_id_re, /* Regular expression for matching device ID */
778 *make_and_model_re; /* Regular expression for matching make and model */
779 regmatch_t re_matches[6]; /* Regular expression matches */
780 cups_array_t *matches; /* Matching PPDs */
b94498cf 781
782
783 fprintf(stderr,
784 "DEBUG2: [cups-driverd] list_ppds(request_id=%d, limit=%d, "
785 "opt=\"%s\"\n", request_id, limit, opt);
ef416fc2 786
787 /*
788 * See if we a PPD database file...
789 */
790
66ab9486
MS
791 PPDsByName = cupsArrayNew((cups_array_func_t)compare_names, NULL);
792 PPDsByMakeModel = cupsArrayNew((cups_array_func_t)compare_ppds, NULL);
793 ChangedPPD = 0;
ef416fc2 794
795 if ((cups_cachedir = getenv("CUPS_CACHEDIR")) == NULL)
796 cups_cachedir = CUPS_CACHEDIR;
797
798 snprintf(filename, sizeof(filename), "%s/ppds.dat", cups_cachedir);
b94498cf 799 if ((fp = cupsFileOpen(filename, "r")) != NULL)
ef416fc2 800 {
801 /*
b94498cf 802 * See if we have the right sync word...
ef416fc2 803 */
804
b94498cf 805 unsigned ppdsync; /* Sync word */
66ab9486
MS
806 int num_ppds; /* Number of PPDs */
807
ef416fc2 808
b94498cf 809 if (cupsFileRead(fp, (char *)&ppdsync, sizeof(ppdsync))
810 == sizeof(ppdsync) &&
811 ppdsync == PPD_SYNC &&
812 !stat(filename, &fileinfo) &&
813 ((fileinfo.st_size - sizeof(ppdsync)) % sizeof(ppd_rec_t)) == 0 &&
66ab9486
MS
814 (num_ppds = (fileinfo.st_size - sizeof(ppdsync)) /
815 sizeof(ppd_rec_t)) > 0)
ef416fc2 816 {
b94498cf 817 /*
818 * We have a ppds.dat file, so read it!
819 */
820
66ab9486 821 for (; num_ppds > 0; num_ppds --)
ef416fc2 822 {
66ab9486 823 if ((ppd = (ppd_info_t *)calloc(1, sizeof(ppd_info_t))) == NULL)
b94498cf 824 {
66ab9486
MS
825 fputs("ERROR: [cups-driverd] Unable to allocate memory for PPD!\n",
826 stderr);
827 exit(1);
b94498cf 828 }
ef416fc2 829
66ab9486
MS
830 if (cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t)) > 0)
831 {
832 cupsArrayAdd(PPDsByName, ppd);
833 cupsArrayAdd(PPDsByMakeModel, ppd);
834 }
835 else
836 {
837 free(ppd);
838 break;
839 }
b94498cf 840 }
66ab9486
MS
841
842 fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
843 filename, cupsArrayCount(PPDsByName));
ef416fc2 844 }
ef416fc2 845
b94498cf 846 cupsFileClose(fp);
847 }
848
ef416fc2 849 /*
850 * Load all PPDs in the specified directory and below...
851 */
852
ef416fc2 853 if ((cups_datadir = getenv("CUPS_DATADIR")) == NULL)
854 cups_datadir = CUPS_DATADIR;
855
856 snprintf(model, sizeof(model), "%s/model", cups_datadir);
b94498cf 857 load_ppds(model, "", 1);
858
4509bb49
MS
859 snprintf(model, sizeof(model), "%s/drv", cups_datadir);
860 load_ppds(model, "", 1);
861
b94498cf 862#ifdef __APPLE__
863 /*
864 * Load PPDs from standard Mac OS X locations...
865 */
866
867 load_ppds("/Library/Printers/PPDs/Contents/Resources",
868 "Library/Printers/PPDs/Contents/Resources", 0);
869 load_ppds("/Library/Printers/PPDs/Contents/Resources/en.lproj",
870 "Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
871 load_ppds("/System/Library/Printers/PPDs/Contents/Resources",
872 "System/Library/Printers/PPDs/Contents/Resources", 0);
873 load_ppds("/System/Library/Printers/PPDs/Contents/Resources/en.lproj",
874 "System/Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
875
876#elif defined(__linux)
877 /*
878 * Load PPDs from LSB-defined locations...
879 */
880
568fa3fa
MS
881 if (!access("/usr/local/share/ppd", 0))
882 load_ppds("/usr/local/share/ppd", "lsb/local", 1);
883 if (!access("/usr/share/ppd", 0))
884 load_ppds("/usr/share/ppd", "lsb/usr", 1);
885 if (!access("/opt/share/ppd", 0))
886 load_ppds("/opt/share/ppd", "lsb/opt", 1);
b94498cf 887#endif /* __APPLE__ */
ef416fc2 888
889 /*
890 * Cull PPD files that are no longer present...
891 */
892
66ab9486
MS
893 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByName);
894 ppd;
895 ppd = (ppd_info_t *)cupsArrayNext(PPDsByName))
ef416fc2 896 if (!ppd->found)
897 {
898 /*
899 * Remove this PPD file from the list...
900 */
901
66ab9486
MS
902 cupsArrayRemove(PPDsByName, ppd);
903 cupsArrayRemove(PPDsByMakeModel, ppd);
904 free(ppd);
ef416fc2 905 }
906
ef416fc2 907 /*
908 * Write the new ppds.dat file...
909 */
910
911 if (ChangedPPD)
912 {
913 if ((fp = cupsFileOpen(filename, "w")) != NULL)
914 {
b94498cf 915 unsigned ppdsync = PPD_SYNC; /* Sync word */
916
917
918 cupsFileWrite(fp, (char *)&ppdsync, sizeof(ppdsync));
919
66ab9486
MS
920 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByName);
921 ppd;
922 ppd = (ppd_info_t *)cupsArrayNext(PPDsByName))
ef416fc2 923 cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
924
925 cupsFileClose(fp);
926
927 fprintf(stderr, "INFO: [cups-driverd] Wrote \"%s\", %d PPDs...\n",
66ab9486 928 filename, cupsArrayCount(PPDsByName));
ef416fc2 929 }
930 else
931 fprintf(stderr, "ERROR: [cups-driverd] Unable to write \"%s\" - %s\n",
932 filename, strerror(errno));
933 }
934 else
935 fputs("INFO: [cups-driverd] No new or changed PPDs...\n", stderr);
936
937 /*
938 * Scan for dynamic PPD files...
939 */
940
ed6e7faf
MS
941 num_options = cupsParseOptions(opt, 0, &options);
942 exclude = cupsdCreateStringsArray(cupsGetOption("exclude-schemes",
943 num_options, options));
8b450588 944 include = cupsdCreateStringsArray(cupsGetOption("include-schemes",
ed6e7faf
MS
945 num_options, options));
946
8b450588
MS
947 for (i = 0; i < num_options; i ++)
948 fprintf(stderr, "DEBUG: [cups-driverd] %s=%s\n", options[i].name,
949 options[i].value);
950
ed6e7faf 951 load_drivers(include, exclude);
ef416fc2 952
953 /*
954 * Add the raw driver...
955 */
956
66ab9486 957 add_ppd("", "raw", "en", "Raw", "Raw Queue", "", "", "", 0, 0, 0,
ed6e7faf 958 PPD_TYPE_UNKNOWN, "raw");
ef416fc2 959
ef416fc2 960 /*
961 * Send IPP attributes...
962 */
963
ed6e7faf
MS
964 requested = cupsdCreateStringsArray(
965 cupsGetOption("requested-attributes", num_options,
966 options));
3d8365b8 967 device_id = cupsGetOption("ppd-device-id", num_options, options);
968 language = cupsGetOption("ppd-natural-language", num_options, options);
969 make = cupsGetOption("ppd-make", num_options, options);
970 make_and_model = cupsGetOption("ppd-make-and-model", num_options, options);
971 model_number_str = cupsGetOption("ppd-model-number", num_options, options);
972 product = cupsGetOption("ppd-product", num_options, options);
973 psversion = cupsGetOption("ppd-psversion", num_options, options);
974 type_str = cupsGetOption("ppd-type", num_options, options);
b94498cf 975
976 if (make_and_model)
58dc1933 977 make_and_model_len = strlen(make_and_model);
b94498cf 978 else
58dc1933 979 make_and_model_len = 0;
ef416fc2 980
58dc1933
MS
981 if (product)
982 product_len = strlen(product);
b94498cf 983 else
58dc1933 984 product_len = 0;
b94498cf 985
3d8365b8 986 if (model_number_str)
987 model_number = atoi(model_number_str);
988 else
989 model_number = 0;
990
991 if (type_str)
992 {
993 for (type = 0;
994 type < (int)(sizeof(ppd_types) / sizeof(ppd_types[0]));
995 type ++)
996 if (!strcmp(type_str, ppd_types[type]))
997 break;
998
999 if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0])))
1000 {
1001 fprintf(stderr, "ERROR: [cups-driverd] Bad ppd-type=\"%s\" ignored!\n",
1002 type_str);
1003 type_str = NULL;
1004 }
1005 }
bc44d920 1006 else
1007 type = 0;
3d8365b8 1008
ed6e7faf
MS
1009 for (i = 0; i < num_options; i ++)
1010 fprintf(stderr, "DEBUG: [cups-driverd] %s=\"%s\"\n", options[i].name,
1011 options[i].value);
ef416fc2 1012
ed6e7faf 1013 if (!requested || cupsArrayFind(requested, (void *)"all") != NULL)
ef416fc2 1014 {
1015 send_name = 1;
1016 send_make = 1;
1017 send_make_and_model = 1;
3d8365b8 1018 send_model_number = 1;
ef416fc2 1019 send_natural_language = 1;
bd7854cb 1020 send_device_id = 1;
f899b121 1021 send_product = 1;
b94498cf 1022 send_psversion = 1;
3d8365b8 1023 send_type = 1;
ef416fc2 1024 }
1025 else
1026 {
ed6e7faf
MS
1027 send_name = cupsArrayFind(requested,
1028 (void *)"ppd-name") != NULL;
1029 send_make = cupsArrayFind(requested,
1030 (void *)"ppd-make") != NULL;
1031 send_make_and_model = cupsArrayFind(requested,
1032 (void *)"ppd-make-and-model") != NULL;
1033 send_model_number = cupsArrayFind(requested,
1034 (void *)"ppd-model-number") != NULL;
1035 send_natural_language = cupsArrayFind(requested,
1036 (void *)"ppd-natural-language") != NULL;
1037 send_device_id = cupsArrayFind(requested,
1038 (void *)"ppd-device-id") != NULL;
1039 send_product = cupsArrayFind(requested,
1040 (void *)"ppd-product") != NULL;
1041 send_psversion = cupsArrayFind(requested,
1042 (void *)"ppd-psversion") != NULL;
1043 send_type = cupsArrayFind(requested,
1044 (void *)"ppd-type") != NULL;
ef416fc2 1045 }
1046
1047 puts("Content-Type: application/ipp\n");
1048
b94498cf 1049 sent_header = 0;
ef416fc2 1050
66ab9486
MS
1051 if (limit <= 0 || limit > cupsArrayCount(PPDsByMakeModel))
1052 count = cupsArrayCount(PPDsByMakeModel);
ef416fc2 1053 else
1054 count = limit;
1055
58dc1933 1056 if (device_id || language || make || make_and_model || model_number_str ||
8b450588 1057 product)
b94498cf 1058 {
58dc1933 1059 matches = cupsArrayNew((cups_array_func_t)compare_matches, NULL);
b94498cf 1060
58dc1933
MS
1061 if (device_id)
1062 device_id_re = regex_device_id(device_id);
1063 else
1064 device_id_re = NULL;
66ab9486 1065
58dc1933
MS
1066 if (make_and_model)
1067 make_and_model_re = regex_string(make_and_model);
1068 else
1069 make_and_model_re = NULL;
b94498cf 1070
58dc1933
MS
1071 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
1072 ppd;
1073 ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
ef416fc2 1074 {
58dc1933
MS
1075 /*
1076 * Filter PPDs based on make, model, product, language, model number,
1077 * and/or device ID using the "matches" score value. An exact match
1078 * for product, make-and-model, or device-id adds 3 to the score.
1079 * Partial matches for make-and-model yield 1 or 2 points, and matches
1080 * for the make and language add a single point. Results are then sorted
1081 * by score, highest score first.
1082 */
b94498cf 1083
58dc1933
MS
1084 if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
1085 ppd->record.type >= PPD_TYPE_DRV)
b94498cf 1086 continue;
b94498cf 1087
ed6e7faf
MS
1088 if (cupsArrayFind(exclude, ppd->record.scheme) ||
1089 (include && !cupsArrayFind(include, ppd->record.scheme)))
1090 continue;
1091
58dc1933 1092 ppd->matches = 0;
b94498cf 1093
58dc1933
MS
1094 if (device_id_re &&
1095 !regexec(device_id_re, ppd->record.device_id,
1096 (int)(sizeof(re_matches) / sizeof(re_matches[0])),
1097 re_matches, 0))
1098 {
1099 /*
1100 * Add the number of matching values from the device ID - it will be
1101 * at least 2 (manufacturer and model), and as much as 3 (command set).
1102 */
b94498cf 1103
58dc1933
MS
1104 for (i = 1; i < (int)(sizeof(re_matches) / sizeof(re_matches[0])); i ++)
1105 if (re_matches[i].rm_so >= 0)
1106 ppd->matches ++;
1107 }
3d8365b8 1108
58dc1933
MS
1109 if (language)
1110 {
1111 for (i = 0; i < PPD_MAX_LANG; i ++)
1112 if (!ppd->record.languages[i][0] ||
d1c13e16 1113 !strcmp(ppd->record.languages[i], language))
58dc1933
MS
1114 {
1115 ppd->matches ++;
1116 break;
1117 }
1118 }
ef416fc2 1119
58dc1933
MS
1120 if (make && !strcasecmp(ppd->record.make, make))
1121 ppd->matches ++;
ef416fc2 1122
58dc1933
MS
1123 if (make_and_model_re &&
1124 !regexec(make_and_model_re, ppd->record.make_and_model,
1125 (int)(sizeof(re_matches) / sizeof(re_matches[0])),
1126 re_matches, 0))
1127 {
1128 // See how much of the make-and-model string we matched...
1129 if (re_matches[0].rm_so == 0)
1130 {
1131 if (re_matches[0].rm_eo == make_and_model_len)
1132 ppd->matches += 3; // Exact match
1133 else
1134 ppd->matches += 2; // Prefix match
1135 }
1136 else
1137 ppd->matches ++; // Infix match
1138 }
ef416fc2 1139
58dc1933
MS
1140 if (model_number_str && ppd->record.model_number == model_number)
1141 ppd->matches ++;
1142
1143 if (product)
1144 {
1145 for (i = 0; i < PPD_MAX_PROD; i ++)
1146 if (!ppd->record.products[i][0] ||
1147 !strcasecmp(ppd->record.products[i], product))
1148 {
1149 ppd->matches += 3;
1150 break;
1151 }
1152 }
1153
1154 if (psversion)
1155 {
1156 for (i = 0; i < PPD_MAX_VERS; i ++)
1157 if (!ppd->record.psversions[i][0] ||
1158 !strcasecmp(ppd->record.psversions[i], psversion))
1159 {
1160 ppd->matches ++;
1161 break;
1162 }
1163 }
1164
1165 if (type_str && ppd->record.type == type)
1166 ppd->matches ++;
1167
1168 if (ppd->matches)
1169 {
1170 fprintf(stderr, "DEBUG: [cups-driverd] %s matches with score %d!\n",
1171 ppd->record.name, ppd->matches);
1172 cupsArrayAdd(matches, ppd);
1173 }
b94498cf 1174 }
58dc1933 1175 }
8b450588
MS
1176 else if (include || exclude)
1177 {
1178 matches = cupsArrayNew((cups_array_func_t)compare_ppds, NULL);
1179
1180 for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
1181 ppd;
1182 ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
1183 {
1184 /*
1185 * Filter PPDs based on the include/exclude lists.
1186 */
1187
1188 if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
1189 ppd->record.type >= PPD_TYPE_DRV)
1190 continue;
1191
1192 if (cupsArrayFind(exclude, ppd->record.scheme) ||
1193 (include && !cupsArrayFind(include, ppd->record.scheme)))
1194 continue;
1195
1196 cupsArrayAdd(matches, ppd);
1197 }
1198 }
58dc1933
MS
1199 else
1200 matches = PPDsByMakeModel;
ef416fc2 1201
58dc1933
MS
1202 for (ppd = (ppd_info_t *)cupsArrayFirst(matches);
1203 count > 0 && ppd;
1204 ppd = (ppd_info_t *)cupsArrayNext(matches))
1205 {
1206 /*
1207 * Skip invalid PPDs...
1208 */
1209
1210 if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
1211 ppd->record.type >= PPD_TYPE_DRV)
3d8365b8 1212 continue;
1213
b94498cf 1214 /*
1215 * Send this PPD...
1216 */
ef416fc2 1217
b94498cf 1218 if (!sent_header)
1219 {
1220 sent_header = 1;
ef416fc2 1221
b94498cf 1222 cupsdSendIPPHeader(IPP_OK, request_id);
1223 cupsdSendIPPGroup(IPP_TAG_OPERATION);
1224 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
58dc1933
MS
1225 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
1226 "en-US");
b94498cf 1227 }
ef416fc2 1228
b94498cf 1229 fprintf(stderr, "DEBUG: [cups-driverd] Sending %s (%s)...\n",
1230 ppd->record.name, ppd->record.make_and_model);
ef416fc2 1231
b94498cf 1232 count --;
bd7854cb 1233
b94498cf 1234 cupsdSendIPPGroup(IPP_TAG_PRINTER);
f899b121 1235
b94498cf 1236 if (send_name)
1237 cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name);
ef416fc2 1238
b94498cf 1239 if (send_natural_language)
1240 {
1241 cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language",
1242 ppd->record.languages[0]);
ef416fc2 1243
66ab9486
MS
1244 for (i = 1; i < PPD_MAX_LANG && ppd->record.languages[i][0]; i ++)
1245 cupsdSendIPPString(IPP_TAG_LANGUAGE, "", ppd->record.languages[i]);
b94498cf 1246 }
ef416fc2 1247
b94498cf 1248 if (send_make)
1249 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make);
ef416fc2 1250
b94498cf 1251 if (send_make_and_model)
1252 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make-and-model",
1253 ppd->record.make_and_model);
1254
1255 if (send_device_id)
1256 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-device-id",
1257 ppd->record.device_id);
1258
1259 if (send_product)
1260 {
1261 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-product",
1262 ppd->record.products[0]);
1263
66ab9486
MS
1264 for (i = 1; i < PPD_MAX_PROD && ppd->record.products[i][0]; i ++)
1265 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.products[i]);
b94498cf 1266 }
1267
1268 if (send_psversion)
1269 {
1270 cupsdSendIPPString(IPP_TAG_TEXT, "ppd-psversion",
1271 ppd->record.psversions[0]);
1272
66ab9486
MS
1273 for (i = 1; i < PPD_MAX_VERS && ppd->record.psversions[i][0]; i ++)
1274 cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.psversions[i]);
b94498cf 1275 }
1276
3d8365b8 1277 if (send_type)
1278 cupsdSendIPPString(IPP_TAG_KEYWORD, "ppd-type",
1279 ppd_types[ppd->record.type]);
1280
1281 if (send_model_number)
1282 cupsdSendIPPInteger(IPP_TAG_INTEGER, "ppd-model-number",
1283 ppd->record.model_number);
1284
b94498cf 1285 /*
1286 * If we have only requested the ppd-make attribute, then skip
1287 * the remaining PPDs with this make...
1288 */
1289
ed6e7faf
MS
1290 if (cupsArrayFind(requested, (void *)"ppd-make") &&
1291 cupsArrayCount(requested) == 1)
b94498cf 1292 {
1293 const char *this_make; /* This ppd-make */
1294
1295
66ab9486 1296 for (this_make = ppd->record.make,
58dc1933 1297 ppd = (ppd_info_t *)cupsArrayNext(matches);
66ab9486 1298 ppd;
58dc1933 1299 ppd = (ppd_info_t *)cupsArrayNext(matches))
b94498cf 1300 if (strcasecmp(this_make, ppd->record.make))
1301 break;
1302
58dc1933 1303 cupsArrayPrev(matches);
ef416fc2 1304 }
b94498cf 1305 }
1306
1307 if (!sent_header)
1308 {
1309 cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
1310 cupsdSendIPPGroup(IPP_TAG_OPERATION);
1311 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
1312 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
1313 }
ef416fc2 1314
1315 cupsdSendIPPTrailer();
1316
1317 return (0);
1318}
1319
1320
1321/*
1322 * 'load_ppds()' - Load PPD files recursively.
1323 */
1324
bd7854cb 1325static int /* O - 1 on success, 0 on failure */
ef416fc2 1326load_ppds(const char *d, /* I - Actual directory */
b94498cf 1327 const char *p, /* I - Virtual path in name */
1328 int descend) /* I - Descend into directories? */
ef416fc2 1329{
1330 int i; /* Looping var */
1331 cups_file_t *fp; /* Pointer to file */
1332 cups_dir_t *dir; /* Directory pointer */
1333 cups_dentry_t *dent; /* Directory entry */
1334 char filename[1024], /* Name of PPD or directory */
1335 line[256], /* Line from backend */
1336 *ptr, /* Pointer into name */
1337 name[128], /* Name of PPD file */
80ca4592 1338 lang_version[64], /* PPD LanguageVersion */
1339 lang_encoding[64], /* PPD LanguageEncoding */
ef416fc2 1340 country[64], /* Country code */
1341 manufacturer[256], /* Manufacturer */
1342 make_model[256], /* Make and Model */
1343 model_name[256], /* ModelName */
bd7854cb 1344 nick_name[256], /* NickName */
f899b121 1345 device_id[256], /* 1284DeviceID */
b94498cf 1346 product[256], /* Product */
5eb9da71
MS
1347 psversion[256], /* PSVersion */
1348 temp[512]; /* Temporary make and model */
3d8365b8 1349 int model_number, /* cupsModelNumber */
1350 type; /* ppd-type */
b94498cf 1351 cups_array_t *products, /* Product array */
1352 *psversions, /* PSVersion array */
1353 *cups_languages; /* cupsLanguages array */
ef416fc2 1354 ppd_info_t *ppd, /* New PPD file */
1355 key; /* Search key */
1356 int new_ppd; /* Is this a new PPD? */
1357 struct /* LanguageVersion translation table */
1358 {
1359 const char *version, /* LanguageVersion string */
1360 *language; /* Language code */
1361 } languages[] =
1362 {
dd1abb6b
MS
1363 { "chinese", "zh" },
1364 { "czech", "cs" },
1365 { "danish", "da" },
1366 { "dutch", "nl" },
1367 { "english", "en" },
1368 { "finnish", "fi" },
1369 { "french", "fr" },
1370 { "german", "de" },
1371 { "greek", "el" },
1372 { "hungarian", "hu" },
1373 { "italian", "it" },
1374 { "japanese", "ja" },
1375 { "korean", "ko" },
1376 { "norwegian", "no" },
1377 { "polish", "pl" },
1378 { "portuguese", "pt" },
1379 { "russian", "ru" },
1380 { "simplified chinese", "zh_CN" },
1381 { "slovak", "sk" },
1382 { "spanish", "es" },
1383 { "swedish", "sv" },
1384 { "traditional chinese", "zh_TW" },
1385 { "turkish", "tr" }
ef416fc2 1386 };
1387
1388
4509bb49
MS
1389 fprintf(stderr, "DEBUG: [cups-driverd] Loading \"%s\"...\n", d);
1390
ef416fc2 1391 if ((dir = cupsDirOpen(d)) == NULL)
1392 {
839a51c8
MS
1393 if (errno != ENOENT)
1394 fprintf(stderr,
1395 "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
1396 d, strerror(errno));
1397
ef416fc2 1398 return (0);
1399 }
1400
1401 while ((dent = cupsDirRead(dir)) != NULL)
1402 {
bd7854cb 1403 /*
1404 * Skip files/directories starting with "."...
1405 */
1406
1407 if (dent->filename[0] == '.')
1408 continue;
1409
ef416fc2 1410 /*
1411 * See if this is a file...
1412 */
1413
1414 snprintf(filename, sizeof(filename), "%s/%s", d, dent->filename);
1415
1416 if (p[0])
1417 snprintf(name, sizeof(name), "%s/%s", p, dent->filename);
1418 else
1419 strlcpy(name, dent->filename, sizeof(name));
1420
1421 if (S_ISDIR(dent->fileinfo.st_mode))
1422 {
1423 /*
1424 * Do subdirectory...
1425 */
1426
b94498cf 1427 if (descend)
1428 if (!load_ppds(filename, name, 1))
1429 {
1430 cupsDirClose(dir);
1431 return (1);
1432 }
ef416fc2 1433
1434 continue;
1435 }
1436
1437 /*
1438 * See if this file has been scanned before...
1439 */
1440
66ab9486
MS
1441 strcpy(key.record.filename, name);
1442 strcpy(key.record.name, name);
ef416fc2 1443
66ab9486 1444 ppd = (ppd_info_t *)cupsArrayFind(PPDsByName, &key);
ef416fc2 1445
66ab9486
MS
1446 if (ppd &&
1447 ppd->record.size == dent->fileinfo.st_size &&
1448 ppd->record.mtime == dent->fileinfo.st_mtime)
1449 {
1450 do
ef416fc2 1451 {
1452 ppd->found = 1;
ef416fc2 1453 }
66ab9486 1454 while ((ppd = (ppd_info_t *)cupsArrayNext(PPDsByName)) != NULL &&
d1c13e16 1455 !strcmp(ppd->record.filename, name));
66ab9486
MS
1456
1457 continue;
ef416fc2 1458 }
ef416fc2 1459
1460 /*
1461 * No, file is new/changed, so re-scan it...
1462 */
1463
1464 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1465 continue;
1466
1467 /*
1468 * Now see if this is a PPD file...
1469 */
1470
1471 line[0] = '\0';
1472 cupsFileGets(fp, line, sizeof(line));
1473
1474 if (strncmp(line, "*PPD-Adobe:", 11))
1475 {
1476 /*
4509bb49 1477 * Nope, treat it as a driver information file...
ef416fc2 1478 */
1479
4509bb49
MS
1480 load_drv(filename, name, fp, dent->fileinfo.st_mtime,
1481 dent->fileinfo.st_size);
ef416fc2 1482 continue;
1483 }
1484
1485 /*
1486 * Now read until we get the NickName field...
1487 */
1488
b94498cf 1489 cups_languages = cupsArrayNew(NULL, NULL);
1490 products = cupsArrayNew(NULL, NULL);
1491 psversions = cupsArrayNew(NULL, NULL);
f899b121 1492
80ca4592 1493 model_name[0] = '\0';
1494 nick_name[0] = '\0';
1495 manufacturer[0] = '\0';
1496 device_id[0] = '\0';
1497 lang_encoding[0] = '\0';
1498 strcpy(lang_version, "en");
3d8365b8 1499 model_number = 0;
1500 type = PPD_TYPE_POSTSCRIPT;
ef416fc2 1501
1502 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
1503 {
1504 if (!strncmp(line, "*Manufacturer:", 14))
1505 sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
1506 else if (!strncmp(line, "*ModelName:", 11))
1507 sscanf(line, "%*[^\"]\"%127[^\"]", model_name);
80ca4592 1508 else if (!strncmp(line, "*LanguageEncoding:", 18))
1509 sscanf(line, "%*[^:]:%63s", lang_encoding);
ef416fc2 1510 else if (!strncmp(line, "*LanguageVersion:", 17))
80ca4592 1511 sscanf(line, "%*[^:]:%63s", lang_version);
ef416fc2 1512 else if (!strncmp(line, "*NickName:", 10))
1513 sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
89d46774 1514 else if (!strncasecmp(line, "*1284DeviceID:", 14))
58dc1933 1515 {
bd7854cb 1516 sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
58dc1933
MS
1517
1518 // Make sure device ID ends with a semicolon...
1519 if (device_id[0] && device_id[strlen(device_id) - 1] != ';')
1520 strlcat(device_id, ";", sizeof(device_id));
1521 }
3d8365b8 1522 else if (!strncmp(line, "*Product:", 9))
f899b121 1523 {
c168a833
MS
1524 if (sscanf(line, "%*[^\"]\"(%255[^\"]", product) == 1)
1525 {
1526 /*
1527 * Make sure the value ends with a right parenthesis - can't stop at
1528 * the first right paren since the product name may contain escaped
1529 * parenthesis...
1530 */
1531
1532 ptr = product + strlen(product) - 1;
1533 if (ptr > product && *ptr == ')')
1534 {
1535 /*
1536 * Yes, ends with a parenthesis, so remove it from the end and
1537 * add the product to the list...
1538 */
1539
1540 *ptr = '\0';
1541 cupsArrayAdd(products, strdup(product));
1542 }
1543 }
f899b121 1544 }
3d8365b8 1545 else if (!strncmp(line, "*PSVersion:", 11))
b94498cf 1546 {
1547 sscanf(line, "%*[^\"]\"%255[^\"]", psversion);
1548 cupsArrayAdd(psversions, strdup(psversion));
1549 }
3d8365b8 1550 else if (!strncmp(line, "*cupsLanguages:", 15))
b94498cf 1551 {
1552 char *start; /* Start of language */
1553
1554
1555 for (start = line + 15; *start && isspace(*start & 255); start ++);
1556
1557 if (*start++ == '\"')
1558 {
1559 while (*start)
1560 {
1561 for (ptr = start + 1;
1562 *ptr && *ptr != '\"' && !isspace(*ptr & 255);
1563 ptr ++);
1564
1565 if (*ptr)
1566 {
1567 *ptr++ = '\0';
1568
1569 while (isspace(*ptr & 255))
1570 *ptr++ = '\0';
1571 }
1572
1573 cupsArrayAdd(cups_languages, strdup(start));
1574 start = ptr;
1575 }
1576 }
1577 }
3d8365b8 1578 else if (!strncmp(line, "*cupsFax:", 9))
1579 {
1580 for (ptr = line + 9; isspace(*ptr & 255); ptr ++);
1581
1582 if (!strncasecmp(ptr, "true", 4))
1583 type = PPD_TYPE_FAX;
1584 }
a0f6818e 1585 else if (!strncmp(line, "*cupsFilter:", 12) && type == PPD_TYPE_POSTSCRIPT)
3d8365b8 1586 {
1587 if (strstr(line + 12, "application/vnd.cups-raster"))
1588 type = PPD_TYPE_RASTER;
1589 else if (strstr(line + 12, "application/vnd.cups-pdf"))
1590 type = PPD_TYPE_PDF;
3d8365b8 1591 }
1592 else if (!strncmp(line, "*cupsModelNumber:", 17))
1593 sscanf(line, "*cupsModelNumber:%d", &model_number);
ef416fc2 1594 else if (!strncmp(line, "*OpenUI", 7))
1595 {
1596 /*
1597 * Stop early if we have a NickName or ModelName attributes
1598 * before the first OpenUI...
1599 */
1600
b94498cf 1601 if ((model_name[0] || nick_name[0]) && cupsArrayCount(products) > 0 &&
1602 cupsArrayCount(psversions) > 0)
ef416fc2 1603 break;
1604 }
ef416fc2 1605 }
1606
1607 /*
1608 * Close the file...
1609 */
1610
1611 cupsFileClose(fp);
1612
1613 /*
1614 * See if we got all of the required info...
1615 */
1616
1617 if (nick_name[0])
80ca4592 1618 cupsCharsetToUTF8((cups_utf8_t *)make_model, nick_name,
1619 sizeof(make_model), _ppdGetEncoding(lang_encoding));
ef416fc2 1620 else
1621 strcpy(make_model, model_name);
1622
1623 while (isspace(make_model[0] & 255))
1624 _cups_strcpy(make_model, make_model + 1);
1625
b94498cf 1626 if (!make_model[0] || cupsArrayCount(products) == 0 ||
1627 cupsArrayCount(psversions) == 0)
f899b121 1628 {
1629 /*
1630 * We don't have all the info needed, so skip this file...
1631 */
1632
1633 if (!make_model[0])
1634 fprintf(stderr, "WARNING: Missing NickName and ModelName in %s!\n",
1635 filename);
1636
1637 if (cupsArrayCount(products) == 0)
1638 fprintf(stderr, "WARNING: Missing Product in %s!\n", filename);
1639
b94498cf 1640 if (cupsArrayCount(psversions) == 0)
1641 fprintf(stderr, "WARNING: Missing PSVersion in %s!\n", filename);
f899b121 1642
b94498cf 1643 free_array(products);
1644 free_array(psversions);
1645 free_array(cups_languages);
f899b121 1646
1647 continue;
1648 }
ef416fc2 1649
3d8365b8 1650 if (model_name[0])
1651 cupsArrayAdd(products, strdup(model_name));
1652
ef416fc2 1653 /*
5eb9da71 1654 * Normalize the make and model string...
ef416fc2 1655 */
1656
1657 while (isspace(manufacturer[0] & 255))
1658 _cups_strcpy(manufacturer, manufacturer + 1);
1659
5eb9da71
MS
1660 if (!strncasecmp(make_model, manufacturer, strlen(manufacturer)))
1661 strlcpy(temp, make_model, sizeof(temp));
1662 else
1663 snprintf(temp, sizeof(temp), "%s %s", manufacturer, make_model);
1664
1665 _ppdNormalizeMakeAndModel(temp, make_model, sizeof(make_model));
1666
1667 /*
1668 * See if we got a manufacturer...
1669 */
1670
ef416fc2 1671 if (!manufacturer[0] || !strcmp(manufacturer, "ESP"))
1672 {
1673 /*
1674 * Nope, copy the first part of the make and model then...
1675 */
1676
1677 strlcpy(manufacturer, make_model, sizeof(manufacturer));
1678
1679 /*
1680 * Truncate at the first space, dash, or slash, or make the
1681 * manufacturer "Other"...
1682 */
1683
1684 for (ptr = manufacturer; *ptr; ptr ++)
1685 if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
1686 break;
1687
1688 if (*ptr && ptr > manufacturer)
1689 *ptr = '\0';
ef416fc2 1690 else
1691 strcpy(manufacturer, "Other");
ef416fc2 1692 }
1693 else if (!strncasecmp(manufacturer, "LHAG", 4) ||
1694 !strncasecmp(manufacturer, "linotype", 8))
1695 strcpy(manufacturer, "LHAG");
5eb9da71
MS
1696 else if (!strncasecmp(manufacturer, "Hewlett", 7))
1697 strcpy(manufacturer, "HP");
ef416fc2 1698
1699 /*
80ca4592 1700 * Fix the lang_version as needed...
ef416fc2 1701 */
1702
80ca4592 1703 if ((ptr = strchr(lang_version, '-')) != NULL)
ef416fc2 1704 *ptr++ = '\0';
80ca4592 1705 else if ((ptr = strchr(lang_version, '_')) != NULL)
ef416fc2 1706 *ptr++ = '\0';
1707
1708 if (ptr)
1709 {
1710 /*
1711 * Setup the country suffix...
1712 */
1713
1714 country[0] = '_';
1715 _cups_strcpy(country + 1, ptr);
1716 }
1717 else
1718 {
1719 /*
1720 * No country suffix...
1721 */
1722
1723 country[0] = '\0';
1724 }
1725
1726 for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
d1c13e16 1727 if (!strcmp(languages[i].version, lang_version))
ef416fc2 1728 break;
1729
1730 if (i < (int)(sizeof(languages) / sizeof(languages[0])))
1731 {
1732 /*
1733 * Found a known language...
1734 */
1735
80ca4592 1736 snprintf(lang_version, sizeof(lang_version), "%s%s",
1737 languages[i].language, country);
ef416fc2 1738 }
1739 else
1740 {
1741 /*
1742 * Unknown language; use "xx"...
1743 */
1744
80ca4592 1745 strcpy(lang_version, "xx");
ef416fc2 1746 }
1747
1748 /*
5eb9da71 1749 * Record the PPD file...
ef416fc2 1750 */
1751
1752 new_ppd = !ppd;
1753
1754 if (new_ppd)
1755 {
1756 /*
1757 * Add new PPD file...
1758 */
1759
1760 fprintf(stderr, "DEBUG: [cups-driverd] Adding ppd \"%s\"...\n", name);
1761
66ab9486
MS
1762 ppd = add_ppd(name, name, lang_version, manufacturer, make_model,
1763 device_id, (char *)cupsArrayFirst(products),
b94498cf 1764 (char *)cupsArrayFirst(psversions),
3d8365b8 1765 dent->fileinfo.st_mtime, dent->fileinfo.st_size,
ed6e7faf 1766 model_number, type, "file");
b94498cf 1767
1768 if (!ppd)
ef416fc2 1769 {
1770 cupsDirClose(dir);
1771 return (0);
1772 }
1773 }
1774 else
1775 {
1776 /*
1777 * Update existing record...
1778 */
1779
1780 fprintf(stderr, "DEBUG: [cups-driverd] Updating ppd \"%s\"...\n", name);
1781
1782 memset(ppd, 0, sizeof(ppd_info_t));
1783
3d8365b8 1784 ppd->found = 1;
1785 ppd->record.mtime = dent->fileinfo.st_mtime;
1786 ppd->record.size = dent->fileinfo.st_size;
1787 ppd->record.model_number = model_number;
1788 ppd->record.type = type;
ef416fc2 1789
1790 strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
1791 strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make));
1792 strlcpy(ppd->record.make_and_model, make_model,
1793 sizeof(ppd->record.make_and_model));
b94498cf 1794 strlcpy(ppd->record.languages[0], lang_version,
1795 sizeof(ppd->record.languages[0]));
1796 strlcpy(ppd->record.products[0], (char *)cupsArrayFirst(products),
1797 sizeof(ppd->record.products[0]));
1798 strlcpy(ppd->record.psversions[0], (char *)cupsArrayFirst(psversions),
1799 sizeof(ppd->record.psversions[0]));
bd7854cb 1800 strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
ef416fc2 1801 }
1802
f899b121 1803 /*
b94498cf 1804 * Add remaining products, versions, and languages...
f899b121 1805 */
1806
b94498cf 1807 for (i = 1;
1808 i < PPD_MAX_PROD && (ptr = (char *)cupsArrayNext(products)) != NULL;
1809 i ++)
1810 strlcpy(ppd->record.products[i], ptr,
1811 sizeof(ppd->record.products[0]));
1812
1813 for (i = 1;
1814 i < PPD_MAX_VERS && (ptr = (char *)cupsArrayNext(psversions)) != NULL;
1815 i ++)
1816 strlcpy(ppd->record.psversions[i], ptr,
1817 sizeof(ppd->record.psversions[0]));
f899b121 1818
b94498cf 1819 for (i = 1, ptr = (char *)cupsArrayFirst(cups_languages);
1820 i < PPD_MAX_LANG && ptr;
1821 i ++, ptr = (char *)cupsArrayNext(cups_languages))
1822 strlcpy(ppd->record.languages[i], ptr,
1823 sizeof(ppd->record.languages[0]));
1824
1825 /*
1826 * Free products, versions, and languages...
1827 */
1828
1829 free_array(cups_languages);
1830 free_array(products);
1831 free_array(psversions);
f899b121 1832
ef416fc2 1833 ChangedPPD = 1;
1834 }
1835
1836 cupsDirClose(dir);
1837
1838 return (1);
1839}
1840
1841
4509bb49
MS
1842/*
1843 * 'load_drv()' - Load the PPDs from a driver information file.
1844 */
1845
1846static int /* O - 1 on success, 0 on failure */
1847load_drv(const char *filename, /* I - Actual filename */
1848 const char *name, /* I - Name to the rest of the world */
1849 cups_file_t *fp, /* I - File to read from */
1850 time_t mtime, /* I - Mod time of driver info file */
1851 off_t size) /* I - Size of driver info file */
1852{
1853 ppdcSource *src; // Driver information file
1854 ppdcDriver *d; // Current driver
1855 ppdcAttr *device_id, // 1284DeviceID attribute
1856 *product, // Current product value
1857 *ps_version, // PSVersion attribute
1858 *cups_fax, // cupsFax attribute
1859 *nick_name; // NickName attribute
1860 ppdcFilter *filter; // Current filter
1861 bool product_found; // Found product?
1862 char uri[1024], // Driver URI
1863 make_model[1024]; // Make and model
1864 int type; // Driver type
1865
1866
66ab9486
MS
1867 /*
1868 * Load the driver info file...
1869 */
1870
4509bb49
MS
1871 src = new ppdcSource(filename, fp);
1872
1873 if (src->drivers->count == 0)
1874 {
1875 fprintf(stderr,
1876 "ERROR: [cups-driverd] Bad driver information file \"%s\"!\n",
1877 filename);
e4572d57 1878 src->release();
4509bb49
MS
1879 return (0);
1880 }
1881
66ab9486
MS
1882 /*
1883 * Add a dummy entry for the file...
1884 */
1885
1886 add_ppd(filename, filename, "", "", "", "", "", "", mtime, size, 0,
ed6e7faf 1887 PPD_TYPE_DRV, "drv");
66ab9486
MS
1888
1889 /*
1890 * Then the drivers in the file...
1891 */
1892
4509bb49
MS
1893 for (d = (ppdcDriver *)src->drivers->first();
1894 d;
1895 d = (ppdcDriver *)src->drivers->next())
1896 {
1897 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "drv", "", "", 0,
1898 "/%s/%s", name,
1899 d->file_name ? d->file_name->value :
1900 d->pc_file_name->value);
1901
1902 device_id = d->find_attr("1284DeviceID", NULL);
1903 ps_version = d->find_attr("PSVersion", NULL);
1904 nick_name = d->find_attr("NickName", NULL);
1905
1906 if (nick_name)
1907 strlcpy(make_model, nick_name->value->value, sizeof(make_model));
1908 else if (strncasecmp(d->model_name->value, d->manufacturer->value,
1909 strlen(d->manufacturer->value)))
1910 snprintf(make_model, sizeof(make_model), "%s %s, %s",
1911 d->manufacturer->value, d->model_name->value,
1912 d->version->value);
1913 else
1914 snprintf(make_model, sizeof(make_model), "%s, %s", d->model_name->value,
1915 d->version->value);
1916
1917 if ((cups_fax = d->find_attr("cupsFax", NULL)) != NULL &&
1918 !strcasecmp(cups_fax->value->value, "true"))
1919 type = PPD_TYPE_FAX;
1920 else if (d->type == PPDC_DRIVER_PS)
1921 type = PPD_TYPE_POSTSCRIPT;
1922 else if (d->type != PPDC_DRIVER_CUSTOM)
1923 type = PPD_TYPE_RASTER;
1924 else
1925 {
1926 for (filter = (ppdcFilter *)d->filters->first(),
1927 type = PPD_TYPE_POSTSCRIPT;
1928 filter;
1929 filter = (ppdcFilter *)d->filters->next())
1930 if (strcasecmp(filter->mime_type->value, "application/vnd.cups-raster"))
1931 type = PPD_TYPE_RASTER;
1932 else if (strcasecmp(filter->mime_type->value,
1933 "application/vnd.cups-pdf"))
1934 type = PPD_TYPE_PDF;
1935 }
1936
1937 for (product = (ppdcAttr *)d->attrs->first(), product_found = false;
1938 product;
1939 product = (ppdcAttr *)d->attrs->next())
1940 if (!strcmp(product->name->value, "Product"))
1941 {
1942 product_found = true;
1943
66ab9486 1944 add_ppd(filename, uri, "en", d->manufacturer->value, make_model,
4509bb49
MS
1945 device_id ? device_id->value->value : "",
1946 product->value->value,
1947 ps_version ? ps_version->value->value : "(3010) 0",
ed6e7faf 1948 mtime, size, d->model_number, type, "drv");
4509bb49
MS
1949 }
1950
1951 if (!product_found)
66ab9486 1952 add_ppd(filename, uri, "en", d->manufacturer->value, make_model,
4509bb49
MS
1953 device_id ? device_id->value->value : "",
1954 d->model_name->value,
1955 ps_version ? ps_version->value->value : "(3010) 0",
ed6e7faf 1956 mtime, size, d->model_number, type, "drv");
4509bb49
MS
1957 }
1958
e4572d57 1959 src->release();
4509bb49
MS
1960
1961 return (1);
1962}
1963
1964
ef416fc2 1965/*
1966 * 'load_drivers()' - Load driver-generated PPD files.
1967 */
1968
bd7854cb 1969static int /* O - 1 on success, 0 on failure */
ed6e7faf
MS
1970load_drivers(cups_array_t *include, /* I - Drivers to include */
1971 cups_array_t *exclude) /* I - Drivers to exclude */
ef416fc2 1972{
b94498cf 1973 int i; /* Looping var */
1974 char *start, /* Start of value */
1975 *ptr; /* Pointer into string */
8b450588
MS
1976 const char *server_bin, /* CUPS_SERVERBIN env variable */
1977 *scheme, /* Scheme for this driver */
1978 *scheme_end; /* Pointer to end of scheme */
ef416fc2 1979 char drivers[1024]; /* Location of driver programs */
c934a06c
MS
1980 int pid; /* Process ID for driver program */
1981 cups_file_t *fp; /* Pipe to driver program */
ef416fc2 1982 cups_dir_t *dir; /* Directory pointer */
1983 cups_dentry_t *dent; /* Directory entry */
c934a06c
MS
1984 char *argv[3], /* Arguments for command */
1985 filename[1024], /* Name of driver */
ef416fc2 1986 line[2048], /* Line from driver */
1987 name[512], /* ppd-name */
ef416fc2 1988 make[128], /* ppd-make */
b94498cf 1989 make_and_model[128], /* ppd-make-and-model */
1990 device_id[128], /* ppd-device-id */
1991 languages[128], /* ppd-natural-language */
1992 product[128], /* ppd-product */
3d8365b8 1993 psversion[128], /* ppd-psversion */
1994 type_str[128]; /* ppd-type */
1995 int type; /* PPD type */
b94498cf 1996 ppd_info_t *ppd; /* Newly added PPD */
ef416fc2 1997
1998
1999 /*
2000 * Try opening the driver directory...
2001 */
2002
2003 if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
2004 server_bin = CUPS_SERVERBIN;
2005
2006 snprintf(drivers, sizeof(drivers), "%s/driver", server_bin);
2007
2008 if ((dir = cupsDirOpen(drivers)) == NULL)
2009 {
2010 fprintf(stderr, "ERROR: [cups-driverd] Unable to open driver directory "
839a51c8
MS
2011 "\"%s\": %s\n",
2012 drivers, strerror(errno));
ef416fc2 2013 return (0);
2014 }
2015
2016 /*
2017 * Loop through all of the device drivers...
2018 */
2019
c934a06c
MS
2020 argv[1] = (char *)"list";
2021 argv[2] = NULL;
2022
ef416fc2 2023 while ((dent = cupsDirRead(dir)) != NULL)
2024 {
2025 /*
2026 * Only look at executable files...
2027 */
2028
2029 if (!(dent->fileinfo.st_mode & 0111) || !S_ISREG(dent->fileinfo.st_mode))
2030 continue;
2031
ed6e7faf
MS
2032 /*
2033 * Include/exclude specific drivers...
2034 */
2035
8b450588
MS
2036 if (exclude)
2037 {
2038 /*
2039 * Look for "scheme" or "scheme*" (prefix match), and skip any matches.
2040 */
2041
2042 for (scheme = (char *)cupsArrayFirst(exclude);
2043 scheme;
2044 scheme = (char *)cupsArrayNext(exclude))
2045 {
2046 fprintf(stderr, "DEBUG: [cups-driverd] Exclude \"%s\" with \"%s\"?\n",
2047 dent->filename, scheme);
2048 scheme_end = scheme + strlen(scheme) - 1;
2049
2050 if ((scheme_end > scheme && *scheme_end == '*' &&
2051 !strncmp(scheme, dent->filename, scheme_end - scheme)) ||
2052 !strcmp(scheme, dent->filename))
2053 {
2054 fputs("DEBUG: [cups-driverd] Yes, exclude!\n", stderr);
2055 break;
2056 }
2057 }
2058
2059 if (scheme)
2060 continue;
2061 }
2062
2063 if (include)
2064 {
2065 /*
2066 * Look for "scheme" or "scheme*" (prefix match), and skip any non-matches.
2067 */
2068
2069 for (scheme = (char *)cupsArrayFirst(include);
2070 scheme;
2071 scheme = (char *)cupsArrayNext(include))
2072 {
2073 fprintf(stderr, "DEBUG: [cups-driverd] Include \"%s\" with \"%s\"?\n",
2074 dent->filename, scheme);
2075 scheme_end = scheme + strlen(scheme) - 1;
2076
2077 if ((scheme_end > scheme && *scheme_end == '*' &&
2078 !strncmp(scheme, dent->filename, scheme_end - scheme)) ||
2079 !strcmp(scheme, dent->filename))
2080 {
2081 fputs("DEBUG: [cups-driverd] Yes, include!\n", stderr);
2082 break;
2083 }
2084 }
2085
2086 if (!scheme)
2087 continue;
2088 }
2089 else
2090 scheme = dent->filename;
ed6e7faf 2091
ef416fc2 2092 /*
2093 * Run the driver with no arguments and collect the output...
2094 */
2095
c934a06c
MS
2096 argv[0] = dent->filename;
2097 snprintf(filename, sizeof(filename), "%s/%s", drivers, dent->filename);
2098
2099 if ((fp = cupsdPipeCommand(&pid, filename, argv, 0)) != NULL)
ef416fc2 2100 {
c934a06c 2101 while (cupsFileGets(fp, line, sizeof(line)))
ef416fc2 2102 {
2103 /*
2104 * Each line is of the form:
2105 *
f899b121 2106 * "ppd-name" ppd-natural-language "ppd-make" "ppd-make-and-model" \
b94498cf 2107 * "ppd-device-id" "ppd-product" "ppd-psversion"
ef416fc2 2108 */
2109
bd7854cb 2110 device_id[0] = '\0';
f899b121 2111 product[0] = '\0';
b94498cf 2112 psversion[0] = '\0';
3d8365b8 2113 strcpy(type_str, "postscript");
bd7854cb 2114
2115 if (sscanf(line, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\""
b94498cf 2116 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
3d8365b8 2117 "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
2118 "%*[ \t]\"%127[^\"]\"",
b94498cf 2119 name, languages, make, make_and_model,
3d8365b8 2120 device_id, product, psversion, type_str) < 4)
ef416fc2 2121 {
2122 /*
2123 * Bad format; strip trailing newline and write an error message.
2124 */
2125
2126 if (line[strlen(line) - 1] == '\n')
2127 line[strlen(line) - 1] = '\0';
2128
2129 fprintf(stderr, "ERROR: [cups-driverd] Bad line from \"%s\": %s\n",
2130 dent->filename, line);
2131 break;
2132 }
2133 else
2134 {
2135 /*
2136 * Add the device to the array of available devices...
2137 */
2138
b94498cf 2139 if ((start = strchr(languages, ',')) != NULL)
2140 *start++ = '\0';
2141
3d8365b8 2142 for (type = 0;
2143 type < (int)(sizeof(ppd_types) / sizeof(ppd_types[0]));
2144 type ++)
2145 if (!strcmp(type_str, ppd_types[type]))
2146 break;
2147
2148 if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0])))
2149 {
c934a06c
MS
2150 fprintf(stderr,
2151 "ERROR: [cups-driverd] Bad ppd-type \"%s\" ignored!\n",
3d8365b8 2152 type_str);
2153 type = PPD_TYPE_UNKNOWN;
2154 }
2155
66ab9486 2156 ppd = add_ppd("", name, languages, make, make_and_model, device_id,
8b450588 2157 product, psversion, 0, 0, 0, type, scheme);
b94498cf 2158
2159 if (!ppd)
ef416fc2 2160 {
2161 cupsDirClose(dir);
c934a06c 2162 cupsFileClose(fp);
ef416fc2 2163 return (0);
2164 }
2165
b94498cf 2166 if (start && *start)
2167 {
2168 for (i = 1; i < PPD_MAX_LANG && *start; i ++)
2169 {
2170 if ((ptr = strchr(start, ',')) != NULL)
2171 *ptr++ = '\0';
2172 else
2173 ptr = start + strlen(start);
2174
2175 strlcpy(ppd->record.languages[i], start,
2176 sizeof(ppd->record.languages[0]));
2177
2178 start = ptr;
2179 }
2180 }
2181
ef416fc2 2182 fprintf(stderr, "DEBUG: [cups-driverd] Added dynamic PPD \"%s\"...\n",
2183 name);
2184 }
2185 }
2186
c934a06c 2187 cupsFileClose(fp);
ef416fc2 2188 }
2189 else
2190 fprintf(stderr, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n",
2191 filename, strerror(errno));
2192 }
2193
2194 cupsDirClose(dir);
2195
2196 return (1);
2197}
2198
2199
58dc1933
MS
2200/*
2201 * 'regex_device_id()' - Compile a regular expression based on the 1284 device
2202 * ID.
2203 */
2204
2205static regex_t * /* O - Regular expression */
2206regex_device_id(const char *device_id) /* I - IEEE-1284 device ID */
2207{
2208 char res[2048], /* Regular expression string */
2209 *ptr; /* Pointer into string */
2210 regex_t *re; /* Regular expression */
2211 int cmd; /* Command set string? */
2212
2213
2214 fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id(\"%s\")\n", device_id);
2215
2216 /*
2217 * Scan the device ID string and insert class, command set, manufacturer, and
2218 * model attributes to match. We assume that the device ID in the PPD and the
2219 * device ID reported by the device itself use the same attribute names and
2220 * order of attributes.
2221 */
2222
2223 ptr = res;
2224
2225 while (*device_id && ptr < (res + sizeof(res) - 6))
2226 {
2227 cmd = !strncasecmp(device_id, "COMMAND SET:", 12) ||
2228 !strncasecmp(device_id, "CMD:", 4);
2229
2230 if (cmd || !strncasecmp(device_id, "MANUFACTURER:", 13) ||
2231 !strncasecmp(device_id, "MFG:", 4) ||
2232 !strncasecmp(device_id, "MFR:", 4) ||
2233 !strncasecmp(device_id, "MODEL:", 6) ||
2234 !strncasecmp(device_id, "MDL:", 4))
2235 {
2236 if (ptr > res)
2237 {
2238 *ptr++ = '.';
2239 *ptr++ = '*';
2240 }
2241
2242 *ptr++ = '(';
2243
2244 while (*device_id && *device_id != ';' && ptr < (res + sizeof(res) - 4))
2245 {
2246 if (strchr("[]{}().*\\|", *device_id))
2247 *ptr++ = '\\';
2248 *ptr++ = *device_id++;
2249 }
2250
2251 if (*device_id == ';' || !*device_id)
2252 *ptr++ = ';';
2253 *ptr++ = ')';
2254 if (cmd)
2255 *ptr++ = '?';
2256 }
2257 else if ((device_id = strchr(device_id, ';')) == NULL)
2258 break;
2259 else
2260 device_id ++;
2261 }
2262
2263 *ptr = '\0';
2264
2265 fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id: \"%s\"\n", res);
2266
2267 /*
2268 * Compile the regular expression and return...
2269 */
2270
2271 if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
2272 {
2273 if (!regcomp(re, res, REG_EXTENDED | REG_ICASE))
2274 {
2275 fputs("DEBUG: [cups-driverd] regex_device_id: OK\n", stderr);
2276 return (re);
2277 }
2278
2279 free(re);
2280 }
2281
2282 return (NULL);
2283}
2284
2285
2286/*
2287 * 'regex_string()' - Construct a regular expression to compare a simple string.
2288 */
2289
2290static regex_t * /* O - Regular expression */
2291regex_string(const char *s) /* I - String to compare */
2292{
2293 char res[2048], /* Regular expression string */
2294 *ptr; /* Pointer into string */
2295 regex_t *re; /* Regular expression */
2296
2297
2298 fprintf(stderr, "DEBUG: [cups-driverd] regex_string(\"%s\")\n", s);
2299
2300 /*
2301 * Convert the string to a regular expression, escaping special characters
2302 * as needed.
2303 */
2304
2305 ptr = res;
2306
2307 while (*s && ptr < (res + sizeof(res) - 2))
2308 {
2309 if (strchr("[]{}().*\\", *s))
2310 *ptr++ = '\\';
2311
2312 *ptr++ = *s++;
2313 }
2314
2315 *ptr = '\0';
2316
2317 fprintf(stderr, "DEBUG: [cups-driverd] regex_string: \"%s\"\n", res);
2318
2319 /*
2320 * Create a case-insensitive regular expression...
2321 */
2322
2323 if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
2324 {
2325 if (!regcomp(re, res, REG_ICASE))
2326 {
2327 fputs("DEBUG: [cups-driverd] regex_string: OK\n", stderr);
2328 return (re);
2329 }
2330
2331 free(re);
2332 }
2333
2334 return (NULL);
2335}
2336
2337
ef416fc2 2338/*
4509bb49 2339 * End of "$Id$".
ef416fc2 2340 */