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