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