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