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