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