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