]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
CUPS-Get-Driver now provides much better driver matching based on the
authormike <mike@7a7537e8-13f0-0310-91df-b6672ffda945>
Wed, 8 Oct 2008 21:07:45 +0000 (21:07 +0000)
committermike <mike@7a7537e8-13f0-0310-91df-b6672ffda945>
Wed, 8 Oct 2008 21:07:45 +0000 (21:07 +0000)
IEEE-1284 device ID and make/model strings (STR #2707)

Fix banner filter destination type.

systemv/lpinfo.c:
    - main(): Add support for --device-id, --language, --make-and-model,
      --product, and --timeout options.
    - show_devices(): Use timeout option.
    - show_models(): Use device_id, language, make_model, and product
      strings when listing drivers.

cgi-bin/admin.c:
    - do_am_printer(): Pass make and model to CUPS-Get-PPDs to get a list
      sorted by relevance to the current device.

scheduler/cups-driverd.cxx:
    - ppd_info_t: Add matches field.
    - compare_matches(): Added.
    - list_ppds(): Use regex to match make-and-model and device-id data,
      and create an array of matches sorted by score and make/model.
    - regex_device_id(): Added.
    - regex_string(): Added.

git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@8029 7a7537e8-13f0-0310-91df-b6672ffda945

CHANGES.txt
cgi-bin/admin.c
conf/mime.convs.in
man/lpinfo.man
scheduler/cups-driverd.cxx
systemv/lpinfo.c

index 28f51230c5d092ff6b2ef4537b3424a3dd53b482..67afdc144755a535da148744bef70d6f54ef2440 100644 (file)
@@ -1,9 +1,11 @@
-CHANGES.txt - 2008-10-06
+CHANGES.txt - 2008-10-08
 ------------------------
 
 CHANGES IN CUPS V1.4b1
 
        - Documentation updates (STR #2567)
+       - CUPS-Get-Driver now provides much better driver matching based
+         on the IEEE-1284 device ID and make/model strings (STR #2707)
        - Now support the cupsSNMPSupplies keyword to control whether
          the network backends query the SNMP Printer MIB for supply
          levels.
index 068406f01cf46619974f3566b11876645b7743c1..3e3042a63be6e524dd80ff7eaa89b49f0e0b4925 100644 (file)
@@ -32,7 +32,6 @@
  *   do_set_sharing()          - Set printer-is-shared value.
  *   get_option_value()        - Return the value of an option.
  *   get_points()              - Get a value in points.
- *   match_string()            - Return the number of matching characters.
  */
 
 /*
@@ -84,7 +83,6 @@ static void   do_set_sharing(http_t *http);
 static char    *get_option_value(ppd_file_t *ppd, const char *name,
                                  char *buffer, size_t bufsize);
 static double  get_points(double number, const char *uval);
-static int     match_string(const char *a, const char *b);
 
 
 /*
@@ -1116,8 +1114,14 @@ do_am_printer(http_t *http,              /* I - HTTP connection */
     if ((var = cgiGetVariable("CURRENT_MAKE")) == NULL)
       var = cgiGetVariable("PPD_MAKE");
     if (var)
+    {
       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
                    "ppd-make", NULL, var);
+
+      if ((var = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL)
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
+                    "ppd-make-and-model", NULL, var);
+    }
     else
       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                    "requested-attributes", NULL, "ppd-make");
@@ -1167,47 +1171,9 @@ do_am_printer(http_t *http,              /* I - HTTP connection */
        * Let the user choose a model...
        */
 
-        const char     *make_model;    /* Current make/model string */
-
-
-        if ((make_model = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL)
-       {
-        /*
-         * Scan for "close" matches...
-         */
-
-          int          match,          /* Current match */
-                       best_match,     /* Best match so far */
-                       count;          /* Number of drivers */
-         const char    *best,          /* Best matching string */
-                       *current;       /* Current string */
-
-
-          count = cgiGetSize("PPD_MAKE_AND_MODEL");
-
-         for (i = 0, best_match = 0, best = NULL; i < count; i ++)
-         {
-           current = cgiGetArray("PPD_MAKE_AND_MODEL", i);
-           match   = match_string(make_model, current);
-
-           if (match > best_match)
-           {
-             best_match = match;
-             best       = current;
-           }
-         }
-
-          if (best_match > strlen(var))
-         {
-          /*
-           * Found a match longer than the make...
-           */
-
-            cgiSetVariable("CURRENT_MAKE_AND_MODEL", best);
-         }
-       }
-
         cgiStartHTML(title);
+       cgiSetVariable("CURRENT_MAKE_AND_MODEL",
+                      cgiGetArray("PPD_MAKE_AND_MODEL", 0));
        cgiCopyTemplateLang("choose-model.tmpl");
         cgiEndHTML();
       }
@@ -4196,55 +4162,6 @@ get_points(double     number,            /* I - Original number */
 }
 
 
-/*
- * 'match_string()' - Return the number of matching characters.
- */
-
-static int                             /* O - Number of matching characters */
-match_string(const char *a,            /* I - First string */
-             const char *b)            /* I - Second string */
-{
-  int  count;                          /* Number of matching characters */
-
-
- /*
-  * Loop through both strings until we hit the end of either or we find
-  * a non-matching character.  For the purposes of comparison, we ignore
-  * whitespace and do a case-insensitive comparison so that we have a
-  * better chance of finding a match...
-  */
-
-  for (count = 0; *a && *b; a++, b++, count ++)
-  {
-   /*
-    * Skip leading whitespace characters...
-    */
-
-    while (isspace(*a & 255))
-      a ++;
-
-    while (isspace(*b & 255))
-      b ++;
-
-   /*
-    * Break out if we run out of characters...
-    */
-
-    if (!*a || !*b)
-      break;
-
-   /*
-    * Do a case-insensitive comparison of the next two chars...
-    */
-
-    if (tolower(*a & 255) != tolower(*b & 255))
-      break;
-  }
-
-  return (count);
-}
-
-    
 /*
  * End of "$Id$".
  */
index 8c98e9e4bb81941f9271b10e33748c895390bbaa..38ed3f40f8145759f8d79326e3c954bf1fd3c590 100644 (file)
@@ -62,7 +62,7 @@ image/x-xbitmap                       application/vnd.cups-postscript 66      imagetops
 image/x-xpixmap                        application/vnd.cups-postscript 66      imagetops
 #image/x-xwindowdump           application/vnd.cups-postscript 66      imagetops
 image/x-sun-raster             application/vnd.cups-postscript 66      imagetops
-application/vnd.cups-banner    application/vnd.cups-postscript 33      bannertops
+application/vnd.cups-banner    application/postscript          33      bannertops
 
 ########################################################################
 #
index 5e930095806968e551f073b51d7b29dd37bd04d7..4267ce54e58ac7de1bb325a4ec4cc786b8679232 100644 (file)
@@ -3,7 +3,7 @@
 .\"
 .\"   lpinfo man page for the Common UNIX Printing System (CUPS).
 .\"
-.\"   Copyright 2007 by Apple Inc.
+.\"   Copyright 2007-2008 by Apple Inc.
 .\"   Copyright 1997-2006 by Easy Software Products.
 .\"
 .\"   These coded instructions, statements, and computer programs are the
@@ -12,7 +12,7 @@
 .\"   which should have been included with this file.  If this file is
 .\"   file is missing or damaged, see the license at "http://www.cups.org/".
 .\"
-.TH lpinfo 8 "Common UNIX Printing System" "12 February 2006" "Apple Inc."
+.TH lpinfo 8 "Common UNIX Printing System" "8 October 2008" "Apple Inc."
 .SH NAME
 lpinfo \- show available devices or drivers
 .SH SYNOPSIS
@@ -21,14 +21,24 @@ lpinfo \- show available devices or drivers
 .I username
 ] [ -h
 .I server[:port]
-] [ -l ] -m
+] [ -l ] [ --device-id
+.I device-id-string
+] [ --language
+.I locale
+] [ --make-and-model
+.I name
+] [ --product
+.I name
+] -m
 .br
 .B lpinfo
 [ -E ] [ -U
 .I username
 ] [ -h
 .I server[:port]
-] [ -l ] -v
+] [ -l ] [ --timeout
+.I seconds
+] -v
 .SH DESCRIPTION
 \fIlpinfo\fR lists the available devices or drivers known to the
 CUPS server. The first form (\fI-m\fR) lists the available
@@ -52,6 +62,28 @@ Selects an alternate server.
 -l
 .br
 Shows a "long" listing of devices or drivers.
+.TP 5
+--device-id device-id-string
+.br
+Specifies the IEEE-1284 device ID to match when listing drivers with the
+\fI-m\fR option.
+.TP 5
+--language locale
+.br
+Specifies the language to match when listing drivers with the \fI-m\fR option.
+.TP 5
+--make-and-model name
+.br
+Specifies the make and model to match when listing drivers with the \fI-m\fR
+option.
+.TP 5
+--product name
+.br
+Specifies the product to match when listing drivers with the \fI-m\fR option.
+.TP 5
+--timeout seconds
+.br
+Specifies the timeout when listing devices with the \fI-v\fR option.
 .SH COMPATIBILITY
 The \fIlpinfo\fR command is unique to CUPS.
 .SH SEE ALSO
@@ -59,7 +91,7 @@ The \fIlpinfo\fR command is unique to CUPS.
 .br
 http://localhost:631/help
 .SH COPYRIGHT
-Copyright 2007 by Apple Inc.
+Copyright 2007-2008 by Apple Inc.
 .\"
 .\" End of "$Id$".
 .\"
index 7742abc9304c7bfa6063d6819b52332e3e92f19f..3ee2974103ec58e4e8ad751a655ee008587270b2 100644 (file)
  *
  * Contents:
  *
- *   main()          - Scan for drivers and return an IPP response.
- *   add_ppd()       - Add a PPD file.
- *   cat_drv()       - Generate a PPD from a driver info file.
- *   cat_ppd()       - Copy a PPD file to stdout.
- *   copy_static()   - Copy a static 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_drv()      - Load the PPDs from a driver information file.
- *   load_drivers()  - Load driver-generated PPD files.
+ *   main()            - Scan for drivers and return an IPP response.
+ *   add_ppd()         - Add a PPD file.
+ *   cat_drv()         - Generate a PPD from a driver info file.
+ *   cat_ppd()         - Copy a PPD file to stdout.
+ *   copy_static()     - Copy a static PPD file to stdout.
+ *   compare_matches() - Compare PPD match scores for sorting.
+ *   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_drv()        - Load the PPDs from a driver information file.
+ *   load_drivers()    - Load driver-generated PPD files.
+ *   regex_device_id() - Compile a regular expression based on the 1284 device
+ *                       ID.
+ *   regex_string()    - Construct a regular expression to compare a simple
+ *                       string.
  */
 
 /*
@@ -41,6 +46,7 @@
 #include <cups/transcode.h>
 #include <cups/ppd-private.h>
 #include <ppdc/ppdc.h>
+#include <regex.h>
 
 
 /*
@@ -95,6 +101,7 @@ typedef struct                               /**** PPD record ****/
 typedef struct                         /**** In-memory record ****/
 {
   int          found;                  /* 1 if PPD is found */
+  int          matches;                /* Match count */
   ppd_rec_t    record;                 /* PPDs.dat record */
 } ppd_info_t;
 
@@ -121,6 +128,8 @@ static ppd_info_t   *add_ppd(const char *filename, const char *name,
 static int             cat_drv(const char *name, int request_id);
 static int             cat_ppd(const char *name, int request_id);
 static int             cat_static(const char *name, int request_id);
+static int             compare_matches(const ppd_info_t *p0,
+                                       const ppd_info_t *p1);
 static int             compare_names(const ppd_info_t *p0,
                                      const ppd_info_t *p1);
 static int             compare_ppds(const ppd_info_t *p0,
@@ -131,6 +140,8 @@ static int          load_drivers(void);
 static int             load_drv(const char *filename, const char *name,
                                 cups_file_t *fp, time_t mtime, off_t size);
 static int             load_ppds(const char *d, const char *p, int descend);
+static regex_t         *regex_device_id(const char *device_id);
+static regex_t         *regex_string(const char *s);
 
 
 /*
@@ -325,7 +336,7 @@ cat_drv(const char *name,           /* I - PPD name */
     ppdcCatalog        *catalog;               // Message catalog in .drv file
 
 
-    fprintf(stderr, "DEBUG: %d locales defined in \"%s\"...\n",
+    fprintf(stderr, "DEBUG: [cups-driverd] %d locales defined in \"%s\"...\n",
             src->po_files->count, filename);
 
     locales = new ppdcArray();
@@ -333,7 +344,7 @@ cat_drv(const char *name,           /* I - PPD name */
          catalog;
         catalog = (ppdcCatalog *)src->po_files->next())
     {
-      fprintf(stderr, "DEBUG: Adding locale \"%s\"...\n",
+      fprintf(stderr, "DEBUG: [cups-driverd] Adding locale \"%s\"...\n",
               catalog->locale->value);
       locales->add(catalog->locale);
     }
@@ -635,6 +646,22 @@ cat_static(const char *name,               /* I - PPD name */
 }
 
 
+/*
+ * 'compare_matches()' - Compare PPD match scores for sorting.
+ */
+
+static int
+compare_matches(const ppd_info_t *p0,  /* I - First PPD */
+                const ppd_info_t *p1)  /* I - Second PPD */
+{
+  if (p1->matches != p0->matches)
+    return (p1->matches - p0->matches);
+  else
+    return (cupsdCompareNames(p1->record.make_and_model,
+                             p0->record.make_and_model));
+}
+
+
 /*
  * 'compare_names()' - Compare PPD filenames for sorting.
  */
@@ -729,8 +756,8 @@ list_ppds(int        request_id,    /* I - Request ID */
                *type_str;              /* ppd-type option */
   int          model_number,           /* ppd-model-number value */
                type,                   /* ppd-type value */
-               mam_len,                /* Length of ppd-make-and-model */
-               device_id_len,          /* Length of ppd-device-id */
+               make_and_model_len,     /* Length of ppd-make-and-model */
+               product_len,            /* Length of ppd-product */
                send_device_id,         /* Send ppd-device-id? */
                send_make,              /* Send ppd-make? */
                send_make_and_model,    /* Send ppd-make-and-model? */
@@ -741,6 +768,10 @@ list_ppds(int        request_id,   /* I - Request ID */
                send_psversion,         /* Send ppd-psversion? */
                send_type,              /* Send ppd-type? */
                sent_header;            /* Sent the IPP header? */
+  regex_t      *device_id_re,          /* Regular expression for matching device ID */
+               *make_and_model_re;     /* Regular expression for matching make and model */
+  regmatch_t   re_matches[6];          /* Regular expression matches */
+  cups_array_t *matches;               /* Matching PPDs */
 
 
   fprintf(stderr,
@@ -926,14 +957,14 @@ list_ppds(int        request_id,  /* I - Request ID */
   type_str         = cupsGetOption("ppd-type", num_options, options);
 
   if (make_and_model)
-    mam_len = strlen(make_and_model);
+    make_and_model_len = strlen(make_and_model);
   else
-    mam_len = 0;
+    make_and_model_len = 0;
 
-  if (device_id)
-    device_id_len = strlen(device_id);
+  if (product)
+    product_len = strlen(product);
   else
-    device_id_len = 0;
+    product_len = 0;
 
   if (model_number_str)
     model_number = atoi(model_number_str);
@@ -1021,66 +1052,135 @@ list_ppds(int        request_id,       /* I - Request ID */
   else
     count = limit;
 
-  for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
-       count > 0 && ppd;
-       ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
+  if (device_id || language || make || make_and_model || model_number_str ||
+      product)
   {
-   /*
-    * Filter PPDs based on make, model, or device ID...
-    */
+    matches = cupsArrayNew((cups_array_func_t)compare_matches, NULL);
 
-    if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
-        ppd->record.type >= PPD_TYPE_DRV)
-      continue;
+    if (device_id)
+      device_id_re = regex_device_id(device_id);
+    else
+      device_id_re = NULL;
 
-    if (device_id && strncasecmp(ppd->record.device_id, device_id,
-                                 device_id_len))
-      continue;                                /* TODO: implement smart compare */
+    if (make_and_model)
+      make_and_model_re = regex_string(make_and_model);
+    else
+      make_and_model_re = NULL;
 
-    if (language)
+    for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
+        ppd;
+        ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
     {
-      for (i = 0; i < PPD_MAX_LANG; i ++)
-       if (!ppd->record.languages[i][0] ||
-           !strcasecmp(ppd->record.languages[i], language))
-         break;
+     /*
+      * Filter PPDs based on make, model, product, language, model number,
+      * and/or device ID using the "matches" score value.  An exact match
+      * for product, make-and-model, or device-id adds 3 to the score.
+      * Partial matches for make-and-model yield 1 or 2 points, and matches
+      * for the make and language add a single point.  Results are then sorted
+      * by score, highest score first.
+      */
 
-      if (i >= PPD_MAX_LANG || !ppd->record.languages[i][0])
+      if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
+         ppd->record.type >= PPD_TYPE_DRV)
        continue;
-    }
 
-    if (make && strcasecmp(ppd->record.make, make))
-      continue;
+      ppd->matches = 0;
 
-    if (make_and_model && strncasecmp(ppd->record.make_and_model,
-                                      make_and_model, mam_len))
-      continue;
+      if (device_id_re &&
+         !regexec(device_id_re, ppd->record.device_id,
+                   (int)(sizeof(re_matches) / sizeof(re_matches[0])),
+                  re_matches, 0))
+      {
+       /*
+        * Add the number of matching values from the device ID - it will be
+       * at least 2 (manufacturer and model), and as much as 3 (command set).
+       */
 
-    if (model_number_str && ppd->record.model_number != model_number)
-      continue;
+        for (i = 1; i < (int)(sizeof(re_matches) / sizeof(re_matches[0])); i ++)
+         if (re_matches[i].rm_so >= 0)
+           ppd->matches ++;
+      }
 
-    if (product)
-    {
-      for (i = 0; i < PPD_MAX_PROD; i ++)
-       if (!ppd->record.products[i][0] ||
-           !strcasecmp(ppd->record.products[i], product))
-         break;
+      if (language)
+      {
+       for (i = 0; i < PPD_MAX_LANG; i ++)
+         if (!ppd->record.languages[i][0] ||
+             !strcasecmp(ppd->record.languages[i], language))
+         {
+           ppd->matches ++;
+           break;
+         }
+      }
 
-      if (i >= PPD_MAX_PROD || !ppd->record.products[i][0])
-       continue;
-    }
+      if (make && !strcasecmp(ppd->record.make, make))
+        ppd->matches ++;
 
-    if (psversion)
-    {
-      for (i = 0; i < PPD_MAX_VERS; i ++)
-       if (!ppd->record.psversions[i][0] ||
-           !strcasecmp(ppd->record.psversions[i], psversion))
-         break;
+      if (make_and_model_re &&
+          !regexec(make_and_model_re, ppd->record.make_and_model,
+                  (int)(sizeof(re_matches) / sizeof(re_matches[0])),
+                  re_matches, 0))
+      {
+       // See how much of the make-and-model string we matched...
+       if (re_matches[0].rm_so == 0)
+       {
+         if (re_matches[0].rm_eo == make_and_model_len)
+           ppd->matches += 3;          // Exact match
+         else
+           ppd->matches += 2;          // Prefix match
+       }
+       else
+         ppd->matches ++;              // Infix match
+      }
 
-      if (i >= PPD_MAX_VERS || !ppd->record.psversions[i][0])
-       continue;
+      if (model_number_str && ppd->record.model_number == model_number)
+        ppd->matches ++;
+
+      if (product)
+      {
+       for (i = 0; i < PPD_MAX_PROD; i ++)
+         if (!ppd->record.products[i][0] ||
+             !strcasecmp(ppd->record.products[i], product))
+         {
+           ppd->matches += 3;
+           break;
+         }
+      }
+
+      if (psversion)
+      {
+       for (i = 0; i < PPD_MAX_VERS; i ++)
+         if (!ppd->record.psversions[i][0] ||
+             !strcasecmp(ppd->record.psversions[i], psversion))
+         {
+           ppd->matches ++;
+           break;
+         }
+      }
+
+      if (type_str && ppd->record.type == type)
+        ppd->matches ++;
+
+      if (ppd->matches)
+      {
+        fprintf(stderr, "DEBUG: [cups-driverd] %s matches with score %d!\n",
+               ppd->record.name, ppd->matches);
+        cupsArrayAdd(matches, ppd);
+      }
     }
+  }
+  else
+    matches = PPDsByMakeModel;
 
-    if (type_str && ppd->record.type != type)
+  for (ppd = (ppd_info_t *)cupsArrayFirst(matches);
+       count > 0 && ppd;
+       ppd = (ppd_info_t *)cupsArrayNext(matches))
+  {
+   /*
+    * Skip invalid PPDs...
+    */
+
+    if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
+        ppd->record.type >= PPD_TYPE_DRV)
       continue;
 
    /*
@@ -1094,7 +1194,8 @@ list_ppds(int        request_id,  /* I - 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");
+      cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
+                         "en-US");
     }
 
     fprintf(stderr, "DEBUG: [cups-driverd] Sending %s (%s)...\n",
@@ -1164,13 +1265,13 @@ list_ppds(int        request_id,        /* I - Request ID */
 
 
       for (this_make = ppd->record.make,
-               ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel);
+               ppd = (ppd_info_t *)cupsArrayNext(matches);
           ppd;
-          ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
+          ppd = (ppd_info_t *)cupsArrayNext(matches))
        if (strcasecmp(this_make, ppd->record.make))
          break;
 
-      cupsArrayPrev(PPDsByMakeModel);
+      cupsArrayPrev(matches);
     }
   }
 
@@ -1382,7 +1483,13 @@ load_ppds(const char *d,         /* I - Actual directory */
       else if (!strncmp(line, "*NickName:", 10))
        sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
       else if (!strncasecmp(line, "*1284DeviceID:", 14))
+      {
        sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
+
+        // Make sure device ID ends with a semicolon...
+       if (device_id[0] && device_id[strlen(device_id) - 1] != ';')
+         strlcat(device_id, ";", sizeof(device_id));
+      }
       else if (!strncmp(line, "*Product:", 9))
       {
        if (sscanf(line, "%*[^\"]\"(%255[^)]", product) == 1)
@@ -1980,6 +2087,144 @@ load_drivers(void)
 }
 
 
+/*
+ * 'regex_device_id()' - Compile a regular expression based on the 1284 device
+ *                       ID.
+ */
+
+static regex_t *                       /* O - Regular expression */
+regex_device_id(const char *device_id) /* I - IEEE-1284 device ID */
+{
+  char         res[2048],              /* Regular expression string */
+               *ptr;                   /* Pointer into string */
+  regex_t      *re;                    /* Regular expression */
+  int          cmd;                    /* Command set string? */
+
+
+  fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id(\"%s\")\n", device_id);
+
+ /*
+  * Scan the device ID string and insert class, command set, manufacturer, and
+  * model attributes to match.  We assume that the device ID in the PPD and the
+  * device ID reported by the device itself use the same attribute names and
+  * order of attributes.
+  */
+
+  ptr = res;
+
+  while (*device_id && ptr < (res + sizeof(res) - 6))
+  {
+    cmd = !strncasecmp(device_id, "COMMAND SET:", 12) ||
+          !strncasecmp(device_id, "CMD:", 4);
+
+    if (cmd || !strncasecmp(device_id, "MANUFACTURER:", 13) ||
+        !strncasecmp(device_id, "MFG:", 4) ||
+        !strncasecmp(device_id, "MFR:", 4) ||
+        !strncasecmp(device_id, "MODEL:", 6) ||
+        !strncasecmp(device_id, "MDL:", 4))
+    {
+      if (ptr > res)
+      {
+        *ptr++ = '.';
+       *ptr++ = '*';
+      }
+
+      *ptr++ = '(';
+
+      while (*device_id && *device_id != ';' && ptr < (res + sizeof(res) - 4))
+      {
+        if (strchr("[]{}().*\\|", *device_id))
+         *ptr++ = '\\';
+       *ptr++ = *device_id++;
+      }
+
+      if (*device_id == ';' || !*device_id)
+        *ptr++ = ';';
+      *ptr++ = ')';
+      if (cmd)
+        *ptr++ = '?';
+    }
+    else if ((device_id = strchr(device_id, ';')) == NULL)
+      break;
+    else
+      device_id ++;
+  }
+
+  *ptr = '\0';
+
+  fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id: \"%s\"\n", res);
+
+ /*
+  * Compile the regular expression and return...
+  */
+
+  if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
+  {
+    if (!regcomp(re, res, REG_EXTENDED | REG_ICASE))
+    {
+      fputs("DEBUG: [cups-driverd] regex_device_id: OK\n", stderr);
+      return (re);
+    }
+
+    free(re);
+  }
+
+  return (NULL);
+}
+
+
+/*
+ * 'regex_string()' - Construct a regular expression to compare a simple string.
+ */
+
+static regex_t *                       /* O - Regular expression */
+regex_string(const char *s)            /* I - String to compare */
+{
+  char         res[2048],              /* Regular expression string */
+               *ptr;                   /* Pointer into string */
+  regex_t      *re;                    /* Regular expression */
+
+
+  fprintf(stderr, "DEBUG: [cups-driverd] regex_string(\"%s\")\n", s);
+
+ /*
+  * Convert the string to a regular expression, escaping special characters
+  * as needed.
+  */
+
+  ptr = res;
+
+  while (*s && ptr < (res + sizeof(res) - 2))
+  {
+    if (strchr("[]{}().*\\", *s))
+      *ptr++ = '\\';
+
+    *ptr++ = *s++;
+  }
+
+  *ptr = '\0';
+
+  fprintf(stderr, "DEBUG: [cups-driverd] regex_string: \"%s\"\n", res);
+
+ /*
+  * Create a case-insensitive regular expression...
+  */
+
+  if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
+  {
+    if (!regcomp(re, res, REG_ICASE))
+    {
+      fputs("DEBUG: [cups-driverd] regex_string: OK\n", stderr);
+      return (re);
+    }
+
+    free(re);
+  }
+
+  return (NULL);
+}
+
+
 /*
  * End of "$Id$".
  */
index d0906db8df37789cad41a5acb9e6fa7e7e2fbe4e..9ed1f7e1641b8f94b5ad576035e5009a1070ff4f 100644 (file)
@@ -42,8 +42,10 @@ static void  device_cb(const char *device_clas, const char *device_id,
                          const char *device_make_and_model,
                          const char *device_uri, const char *device_location,
                          void *user_data);
-static int     show_devices(http_t *, int);
-static int     show_models(http_t *, int);
+static int     show_devices(http_t *http, int long_status, int timeout);
+static int     show_models(http_t *http, int long_status,
+                           const char *device_id, const char *language,
+                           const char *make_model, const char *product);
 
 
 /*
@@ -57,12 +59,22 @@ main(int  argc,                             /* I - Number of command-line arguments */
   int          i;                      /* Looping var */
   http_t       *http;                  /* Connection to server */
   int          long_status;            /* Long listing? */
+  const char   *device_id,             /* 1284 device ID */
+               *language,              /* Language */
+               *make_model,            /* Make and model */
+               *product;               /* Product */
+  int          timeout;                /* Device timeout */
 
 
   _cupsSetLocale(argv);
 
   http        = NULL;
   long_status = 0;
+  device_id   = NULL;
+  language    = NULL;
+  make_model  = NULL;
+  product     = NULL;
+  timeout     = CUPS_TIMEOUT_DEFAULT;
 
   for (i = 1; i < argc; i ++)
     if (argv[i][0] == '-')
@@ -81,6 +93,30 @@ main(int  argc,                              /* I - Number of command-line arguments */
 #endif /* HAVE_SSL */
            break;
 
+        case 'h' : /* Connect to host */
+           if (http)
+           {
+             httpClose(http);
+             http = NULL;
+           }
+
+           if (argv[i][2] != '\0')
+             cupsSetServer(argv[i] + 2);
+           else
+           {
+             i ++;
+
+             if (i >= argc)
+             {
+               _cupsLangPuts(stderr,
+                             _("Error: need hostname after \'-h\' option!\n"));
+               return (1);
+              }
+
+             cupsSetServer(argv[i]);
+           }
+           break;
+
         case 'l' : /* Show long listing */
            long_status = 1;
            break;
@@ -100,7 +136,8 @@ main(int  argc,                             /* I - Number of command-line arguments */
              }
             }
 
-            if (show_models(http, long_status))
+            if (show_models(http, long_status, device_id, language, make_model,
+                           product))
              return (1);
            break;
            
@@ -119,31 +156,101 @@ main(int  argc,                          /* I - Number of command-line arguments */
              }
             }
 
-            if (show_devices(http, long_status))
+            if (show_devices(http, long_status, timeout))
              return (1);
            break;
 
-        case 'h' : /* Connect to host */
-           if (http)
+        case '-' : /* --something */
+            if (!strcmp(argv[i], "--device-id"))
            {
-             httpClose(http);
-             http = NULL;
-           }
+             i ++;
 
-           if (argv[i][2] != '\0')
-             cupsSetServer(argv[i] + 2);
-           else
+             if (i < argc)
+               device_id = argv[i];
+             else
+             {
+               _cupsLangPuts(stderr,
+                             _("lpinfo: Expected 1284 device ID string "
+                               "after --device-id!\n"));
+               return (1);
+             }
+           }
+           else if (!strncmp(argv[i], "--device-id=", 12) && argv[i][12])
+           {
+             device_id = argv[i] + 12;
+           }
+            else if (!strcmp(argv[i], "--language"))
            {
              i ++;
-
-             if (i >= argc)
+             if (i < argc)
+               language = argv[i];
+             else
              {
-               _cupsLangPuts(stderr,
-                             _("Error: need hostname after \'-h\' option!\n"));
+               _cupsLangPuts(stderr,
+                             _("lpinfo: Expected language after "
+                               "--language!\n"));
                return (1);
-              }
-
-             cupsSetServer(argv[i]);
+             }
+           }
+           else if (!strncmp(argv[i], "--language=", 11) && argv[i][11])
+           {
+             language = argv[i] + 11;
+           }
+            else if (!strcmp(argv[i], "--make-and-model"))
+           {
+             i ++;
+             if (i < argc)
+               make_model= argv[i];
+             else
+             {
+               _cupsLangPuts(stderr,
+                             _("lpinfo: Expected make and model after "
+                               "--make-and-model!\n"));
+               return (1);
+             }
+           }
+           else if (!strncmp(argv[i], "--make-and-model=", 17) && argv[i][17])
+           {
+             make_model = argv[i] + 17;
+           }
+            else if (!strcmp(argv[i], "--product"))
+           {
+             i ++;
+             if (i < argc)
+               product = argv[i];
+             else
+             {
+               _cupsLangPuts(stderr,
+                             _("lpinfo: Expected product string after "
+                               "--product!\n"));
+               return (1);
+             }
+           }
+           else if (!strncmp(argv[i], "--product=", 10) && argv[i][10])
+           {
+             product = argv[i] + 10;
+           }
+            else if (!strcmp(argv[i], "--timeout"))
+           {
+             i ++;
+             if (i < argc)
+               timeout = atoi(argv[i]);
+             else
+             {
+               _cupsLangPuts(stderr,
+                             _("lpinfo: Expected timeout after --timeout!\n"));
+               return (1);
+             }
+           }
+           else if (!strncmp(argv[i], "--timeout=", 10) && argv[i][10])
+           {
+             timeout = atoi(argv[i] + 10);
+           }
+           else
+           {
+             _cupsLangPrintf(stderr, _("lpinfo: Unknown option \'%s\'!\n"),
+                             argv[i]);
+             return (1);
            }
            break;
 
@@ -209,9 +316,10 @@ device_cb(
 
 static int                             /* O - 0 on success, 1 on failure */
 show_devices(http_t *http,             /* I - HTTP connection to server */
-             int    long_status)       /* I - Long status report? */
+             int    long_status,       /* I - Long status report? */
+            int    timeout)            /* I - Timeout */
 {
-  if (cupsGetDevices(http, CUPS_TIMEOUT_DEFAULT, CUPS_EXCLUDE_NONE, device_cb,
+  if (cupsGetDevices(http, timeout, CUPS_EXCLUDE_NONE, device_cb,
                      &long_status) != IPP_OK)
   {
     _cupsLangPrintf(stderr, "lpinfo: %s\n", cupsLastErrorString());
@@ -227,15 +335,19 @@ show_devices(http_t *http,                /* I - HTTP connection to server */
  */
 
 static int                             /* O - 0 on success, 1 on failure */
-show_models(http_t *http,              /* I - HTTP connection to server */
-            int    long_status)                /* I - Long status report? */
+show_models(http_t     *http,          /* I - HTTP connection to server */
+            int        long_status,    /* I - Long status report? */
+           const char *device_id,      /* I - 1284 device ID */
+           const char *language,       /* I - Language */
+           const char *make_model,     /* I - Make and model */
+           const char *product)        /* I - Product */
 {
   ipp_t                *request,               /* IPP Request */
                *response;              /* IPP Response */
   ipp_attribute_t *attr;               /* Current attribute */
   const char   *ppd_device_id,         /* Pointer to ppd-device-id */
                *ppd_language,          /* Pointer to ppd-natural-language */
-               *ppd_make,              /* Pointer to ppd-make-and-model */
+               *ppd_make_model,        /* Pointer to ppd-make-and-model */
                *ppd_name;              /* Pointer to ppd-name */
 
 
@@ -243,15 +355,24 @@ show_models(http_t *http,         /* I - HTTP connection to server */
     return (1);
 
  /*
-  * Build a CUPS_GET_PPDS request, which requires the following
-  * attributes:
-  *
-  *    attributes-charset
-  *    attributes-natural-language
+  * Build a CUPS_GET_PPDS request...
   */
 
   request = ippNewRequest(CUPS_GET_PPDS);
 
+  if (device_id)
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-device-id",
+                 NULL, device_id);
+  if (language)
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "ppd-language",
+                 NULL, language);
+  if (make_model)
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make-and-model",
+                 NULL, make_model);
+  if (product)
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-product",
+                 NULL, product);
+
  /*
   * Do the request and get back a response...
   */
@@ -285,10 +406,10 @@ show_models(http_t *http,         /* I - HTTP connection to server */
       * Pull the needed attributes from this PPD...
       */
 
-      ppd_device_id = "NONE";
-      ppd_language  = NULL;
-      ppd_make      = NULL;
-      ppd_name      = NULL;
+      ppd_device_id  = "NONE";
+      ppd_language   = NULL;
+      ppd_make_model = NULL;
+      ppd_name       = NULL;
 
       while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
       {
@@ -300,7 +421,7 @@ show_models(http_t *http,           /* I - HTTP connection to server */
          ppd_language = attr->values[0].string.text;
         else if (!strcmp(attr->name, "ppd-make-and-model") &&
                 attr->value_tag == IPP_TAG_TEXT)
-         ppd_make = attr->values[0].string.text;
+         ppd_make_model = attr->values[0].string.text;
         else if (!strcmp(attr->name, "ppd-name") &&
                 attr->value_tag == IPP_TAG_NAME)
          ppd_name = attr->values[0].string.text;
@@ -312,7 +433,7 @@ show_models(http_t *http,           /* I - HTTP connection to server */
       * See if we have everything needed...
       */
 
-      if (ppd_language == NULL || ppd_make == NULL || ppd_name == NULL)
+      if (ppd_language == NULL || ppd_make_model == NULL || ppd_name == NULL)
       {
         if (attr == NULL)
          break;
@@ -331,10 +452,10 @@ show_models(http_t *http,         /* I - HTTP connection to server */
                          "        natural_language = %s\n"
                          "        make-and-model = %s\n"
                          "        device-id = %s\n"),
-                       ppd_name, ppd_language, ppd_make, ppd_device_id);
+                       ppd_name, ppd_language, ppd_make_model, ppd_device_id);
       }
       else
-        _cupsLangPrintf(stdout, "%s %s\n", ppd_name, ppd_make);
+        _cupsLangPrintf(stdout, "%s %s\n", ppd_name, ppd_make_model);
 
       if (attr == NULL)
         break;