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