]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/cups-driverd.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / cups-driverd.c
index cbe2ebd32140120526255fdc3eb7a1ea41fb7217..1a7a3931daee6e48d1741f68100283e3c203e000 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id$"
+ * "$Id: cups-driverd.c 6508 2007-05-03 20:07:14Z mike $"
  *
  *   PPD/driver support for the Common UNIX Printing System (CUPS).
  *
@@ -7,7 +7,7 @@
  *   in CUPS_DATADIR/model and dynamically generated PPD files using
  *   the driver helper programs in CUPS_SERVERBIN/driver.
  *
- *   Copyright 1997-2006 by Easy Software Products.
+ *   Copyright 1997-2007 by Easy Software Products.
  *
  *   These coded instructions, statements, and computer programs are the
  *   property of Easy Software Products and are protected by Federal
@@ -32,6 +32,7 @@
  *   cat_ppd()       - Copy a PPD file to stdout.
  *   compare_names() - Compare PPD filenames for sorting.
  *   compare_ppds()  - Compare PPD file make and model names for sorting.
+ *   free_array()    - Free an array of strings.
  *   list_ppds()     - List PPD files.
  *   load_ppds()     - Load PPD files recursively.
  *   load_drivers()  - Load driver-generated PPD files.
 extern cups_encoding_t _ppdGetEncoding(const char *name);
 
 
+/*
+ * Constants...
+ */
+
+#define PPD_SYNC       0x50504433      /* Sync word for ppds.dat (PPD3) */
+#define PPD_MAX_LANG   32              /* Maximum languages */
+#define PPD_MAX_PROD   8               /* Maximum products */
+#define PPD_MAX_VERS   8               /* Maximum versions */
+
+
 /*
  * PPD information structures...
  */
@@ -61,13 +72,17 @@ typedef struct                              /**** PPD record ****/
 {
   time_t       mtime;                  /* Modification time */
   size_t       size;                   /* Size in bytes */
-  char         name[512 - sizeof(time_t) - sizeof(size_t)],
-                                       /* PPD name */
-               natural_language[128],  /* Natural language(s) */
+  char         name[512],              /* PPD name */
+               languages[PPD_MAX_LANG][6],
+                                       /* LanguageVersion/cupsLanguages */
+               products[PPD_MAX_PROD][128],
+                                       /* Product strings */
+               psversions[PPD_MAX_VERS][32],
+                                       /* PSVersion strings */
                make[128],              /* Manufacturer */
-               make_and_model[128],    /* Make and model */
+               make_and_model[128],    /* NickName/ModelName */
                device_id[128];         /* IEEE 1284 Device ID */
-} ppd_rec_t;
+} ppd_rec_t; 
 
 typedef struct                         /**** In-memory record ****/
 {
@@ -91,17 +106,20 @@ int                ChangedPPD;             /* Did we change the PPD database? */
  * Local functions...
  */
 
-static ppd_info_t      *add_ppd(const char *name, const char *natural_language,
-                        const char *make, const char *make_and_model,
-                        const char *device_id, time_t mtime, size_t size);
-static int             cat_ppd(const char *name);
+static ppd_info_t      *add_ppd(const char *name, const char *language,
+                                const char *make, const char *make_and_model,
+                                const char *device_id, const char *product,
+                                const char *psversion, time_t mtime,
+                                size_t size);
+static int             cat_ppd(const char *name, int request_id);
 static int             compare_names(const ppd_info_t *p0,
                                      const ppd_info_t *p1);
 static int             compare_ppds(const ppd_info_t *p0,
                                     const ppd_info_t *p1);
+static void            free_array(cups_array_t *a);
 static int             list_ppds(int request_id, int limit, const char *opt);
 static int             load_drivers(void);
-static int             load_ppds(const char *d, const char *p);
+static int             load_ppds(const char *d, const char *p, int descend);
 
 
 /*
@@ -121,12 +139,15 @@ main(int  argc,                           /* I - Number of command-line args */
   */
 
   if (argc == 3 && !strcmp(argv[1], "cat"))
-    return (cat_ppd(argv[2]));
+    return (cat_ppd(argv[2], 0));
+  else if (argc == 4 && !strcmp(argv[1], "get"))
+    return (cat_ppd(argv[3], atoi(argv[2])));
   else if (argc == 5 && !strcmp(argv[1], "list"))
     return (list_ppds(atoi(argv[2]), atoi(argv[3]), argv[4]));
   else
   {
     fputs("Usage: cups-driverd cat ppd-name\n", stderr);
+    fputs("Usage: cups-driverd get request_id ppd-name\n", stderr);
     fputs("Usage: cups-driverd list request_id limit options\n", stderr);
     return (1);
   }
@@ -139,14 +160,17 @@ main(int  argc,                           /* I - Number of command-line args */
 
 static ppd_info_t *                    /* O - PPD */
 add_ppd(const char *name,              /* I - PPD name */
-        const char *natural_language,  /* I - Language(s) */
+        const char *language,          /* I - LanguageVersion */
         const char *make,              /* I - Manufacturer */
-       const char *make_and_model,     /* I - NickName */
+       const char *make_and_model,     /* I - NickName/ModelName */
        const char *device_id,          /* I - 1284DeviceID */
+       const char *product,            /* I - Product */
+       const char *psversion,          /* I - PSVersion */
         time_t     mtime,              /* I - Modification time */
        size_t     size)                /* I - File size */
 {
   ppd_info_t   *ppd;                   /* PPD */
+  char         *recommended;           /* Foomatic driver string */
 
 
  /*
@@ -168,7 +192,8 @@ add_ppd(const char *name,           /* I - PPD name */
 
     if (ppd == NULL)
     {
-      fprintf(stderr, "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
+      fprintf(stderr,
+              "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
              AllocPPDs);
       return (NULL);
     }
@@ -190,13 +215,25 @@ add_ppd(const char *name,         /* I - PPD name */
   ppd->record.size  = size;
 
   strlcpy(ppd->record.name, name, sizeof(ppd->record.name));
-  strlcpy(ppd->record.natural_language, natural_language,
-          sizeof(ppd->record.natural_language));
+  strlcpy(ppd->record.languages[0], language,
+          sizeof(ppd->record.languages[0]));
+  strlcpy(ppd->record.products[0], product, sizeof(ppd->record.products[0]));
+  strlcpy(ppd->record.psversions[0], psversion,
+          sizeof(ppd->record.psversions[0]));
   strlcpy(ppd->record.make, make, sizeof(ppd->record.make));
   strlcpy(ppd->record.make_and_model, make_and_model,
           sizeof(ppd->record.make_and_model));
   strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
 
+ /*
+  * Strip confusing (and often wrong) "recommended" suffix added by
+  * Foomatic drivers...
+  */
+
+  if ((recommended = strstr(ppd->record.make_and_model,
+                            " (recommended)")) != NULL)
+    *recommended = '\0';
+
  /*
   * Return the new PPD pointer...
   */
@@ -210,11 +247,13 @@ add_ppd(const char *name,         /* I - PPD name */
  */
 
 static int                             /* O - Exit code */
-cat_ppd(const char *name)              /* I - PPD name */
+cat_ppd(const char *name,              /* I - PPD name */
+        int        request_id)         /* I - Request ID for response? */
 {
   char         scheme[256],            /* Scheme from PPD name */
                *sptr;                  /* Pointer into scheme */
   char         line[1024];             /* Line/filename */
+  char         message[2048];          /* status-message */
 
 
  /*
@@ -239,6 +278,8 @@ cat_ppd(const char *name)           /* I - PPD name */
   else
     scheme[0] = '\0';
 
+  puts("Content-Type: application/ipp\n");
+
   if (scheme[0])
   {
    /*
@@ -260,6 +301,21 @@ cat_ppd(const char *name)          /* I - PPD name */
 
       fprintf(stderr, "ERROR: [cups-driverd] Unable to access \"%s\" - %s\n",
               line, strerror(errno));
+
+      if (request_id > 0)
+      {
+        snprintf(message, sizeof(message), "Unable to access \"%s\" - %s",
+                line, strerror(errno));
+
+       cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
+       cupsdSendIPPGroup(IPP_TAG_OPERATION);
+       cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
+       cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
+                          "en-US");
+        cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
+        cupsdSendIPPTrailer();
+      }
+
       return (1);
     }
 
@@ -267,6 +323,16 @@ cat_ppd(const char *name)          /* I - PPD name */
     * Yes, let it cat the PPD file...
     */
 
+    if (request_id)
+    {
+      cupsdSendIPPHeader(IPP_OK, request_id);
+      cupsdSendIPPGroup(IPP_TAG_OPERATION);
+      cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
+      cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
+                        "en-US");
+      cupsdSendIPPTrailer();
+    }
+
     if (execl(line, scheme, "cat", name, (char *)NULL))
     {
      /*
@@ -295,6 +361,20 @@ cat_ppd(const char *name)          /* I - PPD name */
       */
 
       fprintf(stderr, "ERROR: [cups-driverd] Bad PPD name \"%s\"!\n", name);
+
+      if (request_id)
+      {
+       snprintf(message, sizeof(message), "Bad PPD name \"%s\"!", name);
+
+       cupsdSendIPPHeader(IPP_OK, request_id);
+       cupsdSendIPPGroup(IPP_TAG_OPERATION);
+       cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
+       cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
+                          "en-US");
+        cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
+       cupsdSendIPPTrailer();
+      }
+
       return (1);
     }
 
@@ -302,17 +382,85 @@ cat_ppd(const char *name)         /* I - PPD name */
     * Try opening the file...
     */
 
-    if ((datadir = getenv("CUPS_DATADIR")) == NULL)
-      datadir = CUPS_DATADIR;
+#ifdef __APPLE__
+    if (!strncmp(name, "System/Library/Printers/PPDs/Contents/Resources/", 48) ||
+        !strncmp(name, "Library/Printers/PPDs/Contents/Resources/", 41))
+    {
+     /*
+      * Map ppd-name to Mac OS X standard locations...
+      */
+
+      snprintf(line, sizeof(line), "/%s", name);
+    }
+    else
+
+#elif defined(__linux)
+    if (!strncmp(name, "lsb/usr/", 8))
+    {
+     /*
+      * Map ppd-name to LSB standard /usr/share/ppd location...
+      */
+
+      snprintf(line, sizeof(line), "/usr/share/ppd/%s", name + 8);
+    }
+    else if (!strncmp(name, "lsb/opt/", 8))
+    {
+     /*
+      * Map ppd-name to LSB standard /opt/share/ppd location...
+      */
+
+      snprintf(line, sizeof(line), "/opt/share/ppd/%s", name + 8);
+    }
+    else if (!strncmp(name, "lsb/local/", 10))
+    {
+     /*
+      * Map ppd-name to LSB standard /usr/local/share/ppd location...
+      */
+
+      snprintf(line, sizeof(line), "/usr/local/share/ppd/%s", name + 10);
+    }
+    else
+
+#endif /* __APPLE__ */
+    {
+      if ((datadir = getenv("CUPS_DATADIR")) == NULL)
+       datadir = CUPS_DATADIR;
+
+      snprintf(line, sizeof(line), "%s/model/%s", datadir, name);
+    }
 
-    snprintf(line, sizeof(line), "%s/model/%s", datadir, name);
     if ((fp = cupsFileOpen(line, "r")) == NULL)
     {
       fprintf(stderr, "ERROR: [cups-driverd] Unable to open \"%s\" - %s\n",
               line, strerror(errno));
+
+      if (request_id)
+      {
+       snprintf(message, sizeof(message), "Unable to open \"%s\" - %s",
+                line, strerror(errno));
+
+       cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
+       cupsdSendIPPGroup(IPP_TAG_OPERATION);
+       cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
+       cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
+                          "en-US");
+        cupsdSendIPPString(IPP_TAG_TEXT, "status-message", message);
+       cupsdSendIPPTrailer();
+      }
+
       return (1);
     }
 
+    if (request_id)
+    {
+      cupsdSendIPPHeader(IPP_OK, request_id);
+      cupsdSendIPPGroup(IPP_TAG_OPERATION);
+      cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
+      cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
+                         "en-US");
+      cupsdSendIPPTrailer();
+    }
+
    /*
     * Now copy the file to stdout...
     */
@@ -364,8 +512,27 @@ compare_ppds(const ppd_info_t *p0, /* I - First PPD file */
                                      p1->record.make_and_model)) != 0)
     return (diff);
   else
-    return (strcasecmp(p0->record.natural_language,
-                       p1->record.natural_language));
+    return (strcasecmp(p0->record.languages[0],
+                       p1->record.languages[0]));
+}
+
+
+/*
+ * 'free_array()' - Free an array of strings.
+ */
+
+static void
+free_array(cups_array_t *a)            /* I - Array to free */
+{
+  char *ptr;                           /* Pointer to string */
+
+
+  for (ptr = (char *)cupsArrayFirst(a);
+       ptr;
+       ptr = (char *)cupsArrayNext(a))
+    free(ptr);
+
+  cupsArrayDelete(a);
 }
 
 
@@ -378,7 +545,7 @@ list_ppds(int        request_id,    /* I - Request ID */
           int        limit,            /* I - Limit */
          const char *opt)              /* I - Option argument */
 {
-  int          i;                      /* Looping var */
+  int          i, j;                   /* Looping vars */
   int          count;                  /* Number of PPDs to send */
   ppd_info_t   *ppd;                   /* Current PPD file */
   cups_file_t  *fp;                    /* ppds.dat file */
@@ -390,16 +557,27 @@ list_ppds(int        request_id,  /* I - Request ID */
   int          num_options;            /* Number of options */
   cups_option_t        *options;               /* Options */
   const char   *requested,             /* requested-attributes option */
-               *make;                  /* ppd-make option */
-  int          send_natural_language,  /* Send ppd-natural-language attribute? */
-               send_make,              /* Send ppd-make attribute? */
-               send_make_and_model,    /* Send ppd-make-and-model attribute? */
-               send_name,              /* Send ppd-name attribute? */
-               send_device_id;         /* Send ppd-device-id attribute? */
-
-
-  fprintf(stderr, "DEBUG2: [cups-driverd] list_ppds(request_id=%d, limit=%d, opt=\"%s\"\n",
-          request_id, limit, opt);
+               *device_id,             /* ppd-device-id option */
+               *language,              /* ppd-natural-language option */
+               *make,                  /* ppd-make option */
+               *make_and_model,        /* ppd-make-and-model option */
+               *product,               /* ppd-product option */
+               *psversion;             /* ppd-psversion option */
+  int          mam_len,                /* Length of ppd-make-and-model */
+               device_id_len,          /* Length of ppd-device-id */
+               send_natural_language,  /* Send ppd-natural-language? */
+               send_make,              /* Send ppd-make? */
+               send_make_and_model,    /* Send ppd-make-and-model? */
+               send_name,              /* Send ppd-name? */
+               send_device_id,         /* Send ppd-device-id? */
+               send_product,           /* Send ppd-product? */
+               send_psversion,         /* Send ppd-psversion? */
+               sent_header;            /* Sent the IPP header? */
+
+
+  fprintf(stderr,
+          "DEBUG2: [cups-driverd] list_ppds(request_id=%d, limit=%d, "
+          "opt=\"%s\"\n", request_id, limit, opt);
 
  /*
   * See if we a PPD database file...
@@ -414,45 +592,48 @@ list_ppds(int        request_id,  /* I - Request ID */
     cups_cachedir = CUPS_CACHEDIR;
 
   snprintf(filename, sizeof(filename), "%s/ppds.dat", cups_cachedir);
-  if (!stat(filename, &fileinfo) &&
-      (fileinfo.st_size % sizeof(ppd_rec_t)) == 0 &&
-      (NumPPDs = fileinfo.st_size / sizeof(ppd_rec_t)) > 0)
+  if ((fp = cupsFileOpen(filename, "r")) != NULL)
   {
    /*
-    * We have a ppds.dat file, so read it!
+    * See if we have the right sync word...
     */
 
-    AllocPPDs = NumPPDs;
+    unsigned ppdsync;                  /* Sync word */
 
-    if ((PPDs = malloc(sizeof(ppd_info_t) * NumPPDs)) == NULL)
+    if (cupsFileRead(fp, (char *)&ppdsync, sizeof(ppdsync))
+            == sizeof(ppdsync) &&
+        ppdsync == PPD_SYNC &&
+        !stat(filename, &fileinfo) &&
+       ((fileinfo.st_size - sizeof(ppdsync)) % sizeof(ppd_rec_t)) == 0 &&
+       (NumPPDs = (fileinfo.st_size - sizeof(ppdsync)) /
+                  sizeof(ppd_rec_t)) > 0)
     {
-      fprintf(stderr,
-              "ERROR: [cups-driverd] Unable to allocate memory for %d "
-             "PPD files!\n", NumPPDs);
-      NumPPDs   = 0;
-      AllocPPDs = 0;
-    }
-    else if ((fp = cupsFileOpen(filename, "r")) != NULL)
-    {
-      for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
+     /*
+      * We have a ppds.dat file, so read it!
+      */
+
+      if ((PPDs = malloc(sizeof(ppd_info_t) * NumPPDs)) == NULL)
+       fprintf(stderr,
+               "ERROR: [cups-driverd] Unable to allocate memory for %d "
+               "PPD files!\n", NumPPDs);
+      else
       {
-        cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
-       ppd->found = 0;
-      }
+        AllocPPDs = NumPPDs;
 
-      cupsFileClose(fp);
+       for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
+       {
+         cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
+         ppd->found = 0;
+       }
 
-      fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
-              filename, NumPPDs);
-    }
-    else
-    {
-      fprintf(stderr, "ERROR: [cups-driverd] Unable to read \"%s\" - %s\n", filename,
-              strerror(errno));
-      NumPPDs = 0;
+       fprintf(stderr, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
+               filename, NumPPDs);
+      }
     }
-  }
 
+    cupsFileClose(fp);
+  }
+  
  /*
   * Load all PPDs in the specified directory and below...
   */
@@ -463,7 +644,31 @@ list_ppds(int        request_id,   /* I - Request ID */
     cups_datadir = CUPS_DATADIR;
 
   snprintf(model, sizeof(model), "%s/model", cups_datadir);
-  load_ppds(model, "");
+  load_ppds(model, "", 1);
+
+#ifdef __APPLE__
+ /*
+  * Load PPDs from standard Mac OS X locations...
+  */
+
+  load_ppds("/Library/Printers/PPDs/Contents/Resources",
+            "Library/Printers/PPDs/Contents/Resources", 0);
+  load_ppds("/Library/Printers/PPDs/Contents/Resources/en.lproj",
+            "Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
+  load_ppds("/System/Library/Printers/PPDs/Contents/Resources",
+            "System/Library/Printers/PPDs/Contents/Resources", 0);
+  load_ppds("/System/Library/Printers/PPDs/Contents/Resources/en.lproj",
+            "System/Library/Printers/PPDs/Contents/Resources/en.lproj", 0);
+
+#elif defined(__linux)
+ /*
+  * Load PPDs from LSB-defined locations...
+  */
+
+  load_ppds("/usr/local/share/ppds", "lsb/local", 1);
+  load_ppds("/usr/share/ppds", "lsb/usr", 1);
+  load_ppds("/opt/share/ppds", "lsb/opt", 1);
+#endif /* __APPLE__ */
 
  /*
   * Cull PPD files that are no longer present...
@@ -499,6 +704,11 @@ list_ppds(int        request_id,   /* I - Request ID */
   {
     if ((fp = cupsFileOpen(filename, "w")) != NULL)
     {
+      unsigned ppdsync = PPD_SYNC;     /* Sync word */
+
+
+      cupsFileWrite(fp, (char *)&ppdsync, sizeof(ppdsync));
+
       for (i = NumPPDs, ppd = PPDs; i > 0; i --, ppd ++)
        cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
 
@@ -524,7 +734,7 @@ list_ppds(int        request_id,    /* I - Request ID */
   * Add the raw driver...
   */
 
-  add_ppd("raw", "en", "Raw", "Raw Queue", "", 0, 0);
+  add_ppd("raw", "en", "Raw", "Raw Queue", "", "", "", 0, 0);
 
  /*
   * Sort the PPDs by make and model...
@@ -538,12 +748,46 @@ list_ppds(int        request_id,  /* I - Request ID */
   * Send IPP attributes...
   */
 
-  num_options = cupsParseOptions(opt, 0, &options);
-  requested   = cupsGetOption("requested-attributes", num_options, options);
-  make        = cupsGetOption("ppd-make", num_options, options);
+  num_options    = cupsParseOptions(opt, 0, &options);
+  requested      = cupsGetOption("requested-attributes", num_options, options);
+  device_id      = cupsGetOption("ppd-device-id", num_options, options);
+  language       = cupsGetOption("ppd-natural-language", num_options, options);
+  make           = cupsGetOption("ppd-make", num_options, options);
+  make_and_model = cupsGetOption("ppd-make-and-model", num_options, options);
+  product        = cupsGetOption("ppd-product", num_options, options);
+  psversion      = cupsGetOption("ppd-psversion", num_options, options);
+
+  if (make_and_model)
+    mam_len = strlen(make_and_model);
+  else
+    mam_len = 0;
 
-  fprintf(stderr, "DEBUG: [cups-driverd] requested=\"%s\"\n",
-          requested ? requested : "(nil)");
+  if (device_id)
+    device_id_len = strlen(device_id);
+  else
+    device_id_len = 0;
+
+  if (requested)
+    fprintf(stderr, "DEBUG: [cups-driverd] requested-attributes=\"%s\"\n",
+           requested);
+  if (device_id)
+    fprintf(stderr, "DEBUG: [cups-driverd] ppd-device-id=\"%s\"\n",
+           device_id);
+  if (language)
+    fprintf(stderr, "DEBUG: [cups-driverd] ppd-natural-language=\"%s\"\n",
+           language);
+  if (make)
+    fprintf(stderr, "DEBUG: [cups-driverd] ppd-make=\"%s\"\n",
+           make);
+  if (make_and_model)
+    fprintf(stderr, "DEBUG: [cups-driverd] ppd-make-and-model=\"%s\"\n",
+           make_and_model);
+  if (product)
+    fprintf(stderr, "DEBUG: [cups-driverd] ppd-product=\"%s\"\n",
+           product);
+  if (psversion)
+    fprintf(stderr, "DEBUG: [cups-driverd] ppd-psversion=\"%s\"\n",
+           psversion);
 
   if (!requested || strstr(requested, "all"))
   {
@@ -552,6 +796,8 @@ list_ppds(int        request_id,    /* I - Request ID */
     send_make_and_model   = 1;
     send_natural_language = 1;
     send_device_id        = 1;
+    send_product          = 1;
+    send_psversion        = 1;
   }
   else
   {
@@ -562,14 +808,13 @@ list_ppds(int        request_id,  /* I - Request ID */
     send_make_and_model   = strstr(requested, "ppd-make-and-model") != NULL;
     send_natural_language = strstr(requested, "ppd-natural-language") != NULL;
     send_device_id        = strstr(requested, "ppd-device-id") != NULL;
+    send_product          = strstr(requested, "ppd-product") != NULL;
+    send_psversion        = strstr(requested, "ppd-psversion") != NULL;
   }
 
   puts("Content-Type: application/ipp\n");
 
-  cupsdSendIPPHeader(IPP_OK, request_id);
-  cupsdSendIPPGroup(IPP_TAG_OPERATION);
-  cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
-  cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
+  sent_header = 0;
 
   if (limit <= 0 || limit > NumPPDs)
     count = NumPPDs;
@@ -577,56 +822,144 @@ list_ppds(int        request_id, /* I - Request ID */
     count = limit;
 
   for (i = NumPPDs, ppd = PPDs; count > 0 && i > 0; i --, ppd ++)
-    if (!make || !strcasecmp(ppd->record.make, make))
+  {
+   /*
+    * Filter PPDs based on make, model, or device ID...
+    */
+
+    if (device_id && strncasecmp(ppd->record.device_id, device_id,
+                                 device_id_len))
+      continue;                                /* TODO: implement smart compare */
+
+    if (language)
     {
-     /*
-      * Send this PPD...
-      */
+      for (j = 0; j < PPD_MAX_LANG; j ++)
+       if (!ppd->record.languages[j][0] ||
+           !strcasecmp(ppd->record.languages[j], language))
+         break;
 
-      fprintf(stderr, "DEBUG: [cups-driverd] Sending %s (%s)...\n",
-              ppd->record.name, ppd->record.make_and_model);
+      if (j >= PPD_MAX_LANG || !ppd->record.languages[j][0])
+       continue;
+    }
+
+    if (make && strcasecmp(ppd->record.make, make))
+      continue;
 
-      count --;
+    if (make_and_model && strncasecmp(ppd->record.make_and_model,
+                                      make_and_model, mam_len))
+      continue;
+
+    if (product)
+    {
+      for (j = 0; j < PPD_MAX_PROD; j ++)
+       if (!ppd->record.products[j][0] ||
+           !strcasecmp(ppd->record.products[j], product))
+         break;
 
-      cupsdSendIPPGroup(IPP_TAG_PRINTER);
+      if (j >= PPD_MAX_PROD || !ppd->record.products[j][0])
+       continue;
+    }
 
-      if (send_name)
-        cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name);
+    if (psversion)
+    {
+      for (j = 0; j < PPD_MAX_VERS; j ++)
+       if (!ppd->record.psversions[j][0] ||
+           !strcasecmp(ppd->record.psversions[j], psversion))
+         break;
 
-      if (send_natural_language)
-        cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language",
-                          ppd->record.natural_language);
+      if (j >= PPD_MAX_VERS || !ppd->record.psversions[j][0])
+       continue;
+    }
 
-      if (send_make)
-        cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make);
+   /*
+    * Send this PPD...
+    */
 
-      if (send_make_and_model)
-        cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make-and-model",
-                          ppd->record.make_and_model);
+    if (!sent_header)
+    {
+      sent_header = 1;
 
-      if (send_device_id)
-        cupsdSendIPPString(IPP_TAG_TEXT, "ppd-device-id",
-                          ppd->record.device_id);
+      cupsdSendIPPHeader(IPP_OK, request_id);
+      cupsdSendIPPGroup(IPP_TAG_OPERATION);
+      cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
+      cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
+    }
 
-     /*
-      * If we have only requested the ppd-make attribute, then skip
-      * the remaining PPDs with this make...
-      */
+    fprintf(stderr, "DEBUG: [cups-driverd] Sending %s (%s)...\n",
+           ppd->record.name, ppd->record.make_and_model);
 
-      if (requested && !strcmp(requested, "ppd-make"))
-      {
-        const char     *this_make;     /* This ppd-make */
+    count --;
 
+    cupsdSendIPPGroup(IPP_TAG_PRINTER);
 
-        for (this_make = ppd->record.make, i --, ppd ++; i > 0; i --, ppd ++)
-         if (strcasecmp(this_make, ppd->record.make))
-           break;
+    if (send_name)
+      cupsdSendIPPString(IPP_TAG_NAME, "ppd-name", ppd->record.name);
 
-        i ++;
-       ppd --;
-      }
+    if (send_natural_language)
+    {
+      cupsdSendIPPString(IPP_TAG_LANGUAGE, "ppd-natural-language",
+                        ppd->record.languages[0]);
+
+      for (j = 1; j < PPD_MAX_LANG && ppd->record.languages[j][0]; j ++)
+       cupsdSendIPPString(IPP_TAG_LANGUAGE, "", ppd->record.languages[j]);
+    }
+
+    if (send_make)
+      cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make", ppd->record.make);
+
+    if (send_make_and_model)
+      cupsdSendIPPString(IPP_TAG_TEXT, "ppd-make-and-model",
+                        ppd->record.make_and_model);
+
+    if (send_device_id)
+      cupsdSendIPPString(IPP_TAG_TEXT, "ppd-device-id",
+                        ppd->record.device_id);
+
+    if (send_product)
+    {
+      cupsdSendIPPString(IPP_TAG_TEXT, "ppd-product",
+                        ppd->record.products[0]);
+
+      for (j = 1; j < PPD_MAX_PROD && ppd->record.products[j][0]; j ++)
+       cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.products[j]);
+    }
+
+    if (send_psversion)
+    {
+      cupsdSendIPPString(IPP_TAG_TEXT, "ppd-psversion",
+                        ppd->record.psversions[0]);
+
+      for (j = 1; j < PPD_MAX_VERS && ppd->record.psversions[j][0]; j ++)
+       cupsdSendIPPString(IPP_TAG_TEXT, "", ppd->record.psversions[j]);
     }
 
+   /*
+    * If we have only requested the ppd-make attribute, then skip
+    * the remaining PPDs with this make...
+    */
+
+    if (requested && !strcmp(requested, "ppd-make"))
+    {
+      const char       *this_make;     /* This ppd-make */
+
+
+      for (this_make = ppd->record.make, i --, ppd ++; i > 0; i --, ppd ++)
+       if (strcasecmp(this_make, ppd->record.make))
+         break;
+
+      i ++;
+      ppd --;
+    }
+  }
+
+  if (!sent_header)
+  {
+    cupsdSendIPPHeader(IPP_NOT_FOUND, request_id);
+    cupsdSendIPPGroup(IPP_TAG_OPERATION);
+    cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
+    cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
+  }
+
   cupsdSendIPPTrailer();
 
   return (0);
@@ -639,7 +972,8 @@ list_ppds(int        request_id,    /* I - Request ID */
 
 static int                             /* O - 1 on success, 0 on failure */
 load_ppds(const char *d,               /* I - Actual directory */
-          const char *p)               /* I - Virtual path in name */
+          const char *p,               /* I - Virtual path in name */
+         int        descend)           /* I - Descend into directories? */
 {
   int          i;                      /* Looping var */
   cups_file_t  *fp;                    /* Pointer to file */
@@ -656,7 +990,12 @@ load_ppds(const char *d,           /* I - Actual directory */
                make_model[256],        /* Make and Model */
                model_name[256],        /* ModelName */
                nick_name[256],         /* NickName */
-               device_id[256];         /* 1284DeviceID */
+               device_id[256],         /* 1284DeviceID */
+               product[256],           /* Product */
+               psversion[256];         /* PSVersion */
+  cups_array_t *products,              /* Product array */
+               *psversions,            /* PSVersion array */
+               *cups_languages;        /* cupsLanguages array */
   ppd_info_t   *ppd,                   /* New PPD file */
                key;                    /* Search key */
   int          new_ppd;                /* Is this a new PPD? */
@@ -666,7 +1005,7 @@ load_ppds(const char *d,           /* I - Actual directory */
                *language;              /* Language code */
   }            languages[] =
   {
-    { "chinese",       "cn" },
+    { "chinese",       "zh" },
     { "danish",                "da" },
     { "dutch",         "nl" },
     { "english",       "en" },
@@ -675,7 +1014,7 @@ load_ppds(const char *d,           /* I - Actual directory */
     { "german",                "de" },
     { "greek",         "el" },
     { "italian",       "it" },
-    { "japanese",      "jp" },
+    { "japanese",      "ja" },
     { "norwegian",     "no" },
     { "polish",                "pl" },
     { "portuguese",    "pt" },
@@ -689,7 +1028,8 @@ load_ppds(const char *d,           /* I - Actual directory */
 
   if ((dir = cupsDirOpen(d)) == NULL)
   {
-    fprintf(stderr, "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
+    fprintf(stderr,
+            "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
             d, strerror(errno));
     return (0);
   }
@@ -720,11 +1060,12 @@ load_ppds(const char *d,         /* I - Actual directory */
       * Do subdirectory...
       */
 
-      if (!load_ppds(filename, name))
-      {
-        cupsDirClose(dir);
-        return (1);
-      }
+      if (descend)
+       if (!load_ppds(filename, name, 1))
+       {
+         cupsDirClose(dir);
+         return (1);
+       }
 
       continue;
     }
@@ -780,6 +1121,10 @@ load_ppds(const char *d,          /* I - Actual directory */
     * Now read until we get the NickName field...
     */
 
+    cups_languages = cupsArrayNew(NULL, NULL);
+    products       = cupsArrayNew(NULL, NULL);
+    psversions     = cupsArrayNew(NULL, NULL);
+
     model_name[0]    = '\0';
     nick_name[0]     = '\0';
     manufacturer[0]  = '\0';
@@ -801,6 +1146,44 @@ load_ppds(const char *d,          /* I - Actual directory */
        sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
       else if (!strncasecmp(line, "*1284DeviceID:", 14))
        sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
+      else if (!strncasecmp(line, "*Product:", 9))
+      {
+       sscanf(line, "%*[^\"]\"(%255[^)]", product);
+       cupsArrayAdd(products, strdup(product));
+      }
+      else if (!strncasecmp(line, "*PSVersion:", 11))
+      {
+       sscanf(line, "%*[^\"]\"%255[^\"]", psversion);
+       cupsArrayAdd(psversions, strdup(psversion));
+      }
+      else if (!strncasecmp(line, "*cupsLanguages:", 15))
+      {
+        char   *start;                 /* Start of language */
+
+
+        for (start = line + 15; *start && isspace(*start & 255); start ++);
+
+       if (*start++ == '\"')
+       {
+         while (*start)
+         {
+           for (ptr = start + 1;
+                *ptr && *ptr != '\"' && !isspace(*ptr & 255);
+                ptr ++);
+
+            if (*ptr)
+           {
+             *ptr++ = '\0';
+
+             while (isspace(*ptr & 255))
+               *ptr++ = '\0';
+            }
+
+            cupsArrayAdd(cups_languages, strdup(start));
+           start = ptr;
+         }
+       }
+      }
       else if (!strncmp(line, "*OpenUI", 7))
       {
        /*
@@ -808,7 +1191,8 @@ load_ppds(const char *d,           /* I - Actual directory */
        * before the first OpenUI...
        */
 
-        if (model_name[0] || nick_name[0])
+        if ((model_name[0] || nick_name[0]) && cupsArrayCount(products) > 0 &&
+           cupsArrayCount(psversions) > 0)
          break;
       }
     }
@@ -832,8 +1216,29 @@ load_ppds(const char *d,          /* I - Actual directory */
     while (isspace(make_model[0] & 255))
       _cups_strcpy(make_model, make_model + 1);
 
-    if (!make_model[0])
-      continue;        /* Nope... */
+    if (!make_model[0] || cupsArrayCount(products) == 0 ||
+        cupsArrayCount(psversions) == 0)
+    {
+     /*
+      * We don't have all the info needed, so skip this file...
+      */
+
+      if (!make_model[0])
+        fprintf(stderr, "WARNING: Missing NickName and ModelName in %s!\n",
+               filename);
+
+      if (cupsArrayCount(products) == 0)
+        fprintf(stderr, "WARNING: Missing Product in %s!\n", filename);
+
+      if (cupsArrayCount(psversions) == 0)
+        fprintf(stderr, "WARNING: Missing PSVersion in %s!\n", filename);
+
+      free_array(products);
+      free_array(psversions);
+      free_array(cups_languages);
+
+      continue;
+    }
 
    /*
     * See if we got a manufacturer...
@@ -958,8 +1363,12 @@ load_ppds(const char *d,          /* I - Actual directory */
 
       fprintf(stderr, "DEBUG: [cups-driverd] Adding ppd \"%s\"...\n", name);
 
-      if (!add_ppd(name, lang_version, manufacturer, make_model, device_id,
-                   dent->fileinfo.st_mtime, dent->fileinfo.st_size))
+      ppd = add_ppd(name, lang_version, manufacturer, make_model, device_id,
+                    (char *)cupsArrayFirst(products),
+                    (char *)cupsArrayFirst(psversions),
+                    dent->fileinfo.st_mtime, dent->fileinfo.st_size);
+
+      if (!ppd)
       {
         cupsDirClose(dir);
        return (0);
@@ -983,11 +1392,45 @@ load_ppds(const char *d,         /* I - Actual directory */
       strlcpy(ppd->record.make, manufacturer, sizeof(ppd->record.make));
       strlcpy(ppd->record.make_and_model, make_model,
               sizeof(ppd->record.make_and_model));
-      strlcpy(ppd->record.natural_language, lang_version,
-              sizeof(ppd->record.natural_language));
+      strlcpy(ppd->record.languages[0], lang_version,
+              sizeof(ppd->record.languages[0]));
+      strlcpy(ppd->record.products[0], (char *)cupsArrayFirst(products),
+              sizeof(ppd->record.products[0]));
+      strlcpy(ppd->record.psversions[0], (char *)cupsArrayFirst(psversions),
+              sizeof(ppd->record.psversions[0]));
       strlcpy(ppd->record.device_id, device_id, sizeof(ppd->record.device_id));
     }
 
+   /*
+    * Add remaining products, versions, and languages...
+    */
+
+    for (i = 1;
+         i < PPD_MAX_PROD && (ptr = (char *)cupsArrayNext(products)) != NULL;
+        i ++)
+      strlcpy(ppd->record.products[i], ptr,
+              sizeof(ppd->record.products[0]));
+
+    for (i = 1;
+         i < PPD_MAX_VERS && (ptr = (char *)cupsArrayNext(psversions)) != NULL;
+        i ++)
+      strlcpy(ppd->record.psversions[i], ptr,
+              sizeof(ppd->record.psversions[0]));
+
+    for (i = 1, ptr = (char *)cupsArrayFirst(cups_languages);
+         i < PPD_MAX_LANG && ptr;
+        i ++, ptr = (char *)cupsArrayNext(cups_languages))
+      strlcpy(ppd->record.languages[i], ptr,
+              sizeof(ppd->record.languages[0]));
+
+   /*
+    * Free products, versions, and languages...
+    */
+
+    free_array(cups_languages);
+    free_array(products);
+    free_array(psversions);
+
     ChangedPPD = 1;
   }
 
@@ -1004,7 +1447,10 @@ load_ppds(const char *d,         /* I - Actual directory */
 static int                             /* O - 1 on success, 0 on failure */
 load_drivers(void)
 {
-  const char   *server_bin;            /* CUPS_SERVERBIN environment variable */
+  int          i;                      /* Looping var */
+  char         *start,                 /* Start of value */
+               *ptr;                   /* Pointer into string */
+  const char   *server_bin;            /* CUPS_SERVERBIN env variable */
   char         drivers[1024];          /* Location of driver programs */
   FILE         *fp;                    /* Pipe to driver program */
   cups_dir_t   *dir;                   /* Directory pointer */
@@ -1012,10 +1458,13 @@ load_drivers(void)
   char         filename[1024],         /* Name of driver */
                line[2048],             /* Line from driver */
                name[512],              /* ppd-name */
-               natural_language[128],  /* ppd-natural-language */
                make[128],              /* ppd-make */
-               make_and_model[256],    /* ppd-make-and-model */
-               device_id[256];         /* ppd-device-id */
+               make_and_model[128],    /* ppd-make-and-model */
+               device_id[128],         /* ppd-device-id */
+               languages[128],         /* ppd-natural-language */
+               product[128],           /* ppd-product */
+               psversion[128];         /* ppd-psversion */
+  ppd_info_t   *ppd;                   /* Newly added PPD */
 
 
  /*
@@ -1060,15 +1509,19 @@ load_drivers(void)
        /*
         * Each line is of the form:
        *
-       *   \"ppd-name\" ppd-natural-language "ppd-make" "ppd-make-and-model"
+       *   "ppd-name" ppd-natural-language "ppd-make" "ppd-make-and-model" \
+       *       "ppd-device-id" "ppd-product" "ppd-psversion"
        */
 
         device_id[0] = '\0';
+       product[0]   = '\0';
+       psversion[0] = '\0';
 
         if (sscanf(line, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\""
-                        "%*[ \t]\"%256[^\"]\"%*[ \t]\"%256[^\"]\"",
-                  name, natural_language, make, make_and_model,
-                  device_id) < 4)
+                        "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\""
+                        "%*[ \t]\"%127[^\"]\"%*[ \t]\"%127[^\"]\"",
+                  name, languages, make, make_and_model,
+                  device_id, product, psversion) < 4)
         {
         /*
          * Bad format; strip trailing newline and write an error message.
@@ -1087,13 +1540,34 @@ load_drivers(void)
          * Add the device to the array of available devices...
          */
 
-          if (!add_ppd(name, natural_language, make, make_and_model, device_id,
-                      0, 0))
+          if ((start = strchr(languages, ',')) != NULL)
+           *start++ = '\0';
+
+          ppd = add_ppd(name, languages, make, make_and_model, device_id,
+                       product, psversion, 0, 0);
+
+          if (!ppd)
          {
             cupsDirClose(dir);
            return (0);
          }
 
+          if (start && *start)
+         {
+           for (i = 1; i < PPD_MAX_LANG && *start; i ++)
+           {
+             if ((ptr = strchr(start, ',')) != NULL)
+               *ptr++ = '\0';
+             else
+               ptr = start + strlen(start);
+
+              strlcpy(ppd->record.languages[i], start,
+                     sizeof(ppd->record.languages[0]));
+
+             start = ptr;
+           }
+          }
+
           fprintf(stderr, "DEBUG: [cups-driverd] Added dynamic PPD \"%s\"...\n",
                  name);
        }
@@ -1113,5 +1587,5 @@ load_drivers(void)
 
 
 /*
- * End of "$Id$".
+ * End of "$Id: cups-driverd.c 6508 2007-05-03 20:07:14Z mike $".
  */