[gnome-control-center] printers: Improve search for PPDs for new devices



commit 8164c8f7f9327e26d501ffeb103ede772165666f
Author: Marek Kasik <mkasik redhat com>
Date:   Fri May 6 12:31:42 2011 +0200

    printers: Improve search for PPDs for new devices
    
    Improve logic for selecting PPD from available PPDs.
    Try to find the best PPD if there is not an exact match
    (rhbz#691808, rhbz#691701).

 panels/printers/pp-new-printer-dialog.c |   30 +-
 panels/printers/pp-utils.c              | 1094 +++++++++++++++++++++++++++++--
 panels/printers/pp-utils.h              |   20 +-
 3 files changed, 1065 insertions(+), 79 deletions(-)
---
diff --git a/panels/printers/pp-new-printer-dialog.c b/panels/printers/pp-new-printer-dialog.c
index d556d0c..1f1882b 100644
--- a/panels/printers/pp-new-printer-dialog.c
+++ b/panels/printers/pp-new-printer-dialog.c
@@ -360,12 +360,19 @@ devices_get_cb (GObject      *source_object,
                   if (pp->devices[i].device_id)
                     {
                       name = get_tag_value (pp->devices[i].device_id, "mdl");
-                      name = g_strcanon (name, ALLOWED_CHARACTERS, '-');
+                      if (!name)
+                        name = get_tag_value (pp->devices[i].device_id, "model");
+
+                      if (name)
+                        name = g_strcanon (name, ALLOWED_CHARACTERS, '-');
                     }
-                  else if (pp->devices[i].device_info)
+
+                  if (!name &&
+                      pp->devices[i].device_info)
                     {
                       name = g_strdup (pp->devices[i].device_info);
-                      name = g_strcanon (name, ALLOWED_CHARACTERS, '-');
+                      if (name)
+                        name = g_strcanon (name, ALLOWED_CHARACTERS, '-');
                     }
 
                   pp->devices[i].display_name = name;
@@ -812,7 +819,7 @@ new_printer_add_button_cb (GtkButton *button,
   GtkWidget          *treeview;
   gboolean            success = FALSE;
   gchar              *device_name = NULL;
-  gchar              *ppd_name = NULL;
+  PPDName            *ppd_name = NULL;
   gint                device_id = -1;
   gint                device_type = -1;
   gint                i, j, k;
@@ -929,7 +936,7 @@ new_printer_add_button_cb (GtkButton *button,
                            pp->devices[device_id].device_uri,
                            pp->devices[device_id].device_location);
 
-          if (ppd_name == NULL)
+          if (ppd_name == NULL || ppd_name->ppd_match_level < PPD_EXACT_MATCH)
             {
               /* Try PackageKit to install printer driver */
               DBusGProxy *proxy;
@@ -969,6 +976,12 @@ new_printer_add_button_cb (GtkButton *button,
 
                   g_clear_error (&error);
 
+                  if (ppd_name)
+                    {
+                      g_free (ppd_name->ppd_name);
+                      g_free (ppd_name);
+                    }
+
                   /* Search CUPS for driver */
                   ppd_name = get_ppd_name (pp->devices[device_id].device_class,
                                pp->devices[device_id].device_id,
@@ -982,7 +995,7 @@ new_printer_add_button_cb (GtkButton *button,
             }
 
           /* Add the new printer */
-          if (ppd_name)
+          if (ppd_name && ppd_name->ppd_name)
             {
               DBusGProxy *proxy;
               GError     *error = NULL;
@@ -997,7 +1010,7 @@ new_printer_add_button_cb (GtkButton *button,
                   dbus_g_proxy_call (proxy, "PrinterAdd", &error,
                                      G_TYPE_STRING, pp->devices[device_id].display_name,
                                      G_TYPE_STRING, pp->devices[device_id].device_uri,
-                                     G_TYPE_STRING, ppd_name,
+                                     G_TYPE_STRING, ppd_name->ppd_name,
                                      G_TYPE_STRING, pp->devices[device_id].device_info,
                                      G_TYPE_STRING, pp->devices[device_id].device_location,
                                      G_TYPE_INVALID,
@@ -1021,6 +1034,9 @@ new_printer_add_button_cb (GtkButton *button,
 
                   g_object_unref (proxy);
                 }
+
+              g_free (ppd_name->ppd_name);
+              g_free (ppd_name);
             }
         }
 
diff --git a/panels/printers/pp-utils.c b/panels/printers/pp-utils.c
index d274a74..8dbe04f 100644
--- a/panels/printers/pp-utils.c
+++ b/panels/printers/pp-utils.c
@@ -68,20 +68,733 @@ gchar *get_tag_value (const gchar *tag_string, const gchar *tag_name)
 {
   gchar **tag_string_splitted = NULL;
   gchar  *tag_value = NULL;
-  gint    tag_name_length = strlen (tag_name);
+  gint    tag_name_length;
   gint    i;
 
-  tag_string_splitted = g_strsplit (tag_string, ";", 0);
-  for (i = 0; i < g_strv_length (tag_string_splitted); i++)
-    if (g_ascii_strncasecmp (tag_string_splitted[i], tag_name, tag_name_length) == 0)
-      if (strlen (tag_string_splitted[i]) > tag_name_length + 1)
-        tag_value = g_strdup (tag_string_splitted[i] + tag_name_length + 1);
-  g_strfreev (tag_string_splitted);
+  if (tag_string && tag_name)
+    {
+      tag_name_length = strlen (tag_name);
+      tag_string_splitted = g_strsplit (tag_string, ";", 0);
+      if (tag_string_splitted)
+        {
+          for (i = 0; i < g_strv_length (tag_string_splitted); i++)
+            if (g_ascii_strncasecmp (tag_string_splitted[i], tag_name, tag_name_length) == 0)
+              if (strlen (tag_string_splitted[i]) > tag_name_length + 1)
+                tag_value = g_strdup (tag_string_splitted[i] + tag_name_length + 1);
+
+          g_strfreev (tag_string_splitted);
+        }
+    }
 
   return tag_value;
 }
 
-gchar *
+
+typedef struct
+{
+  gchar *ppd_name;
+  gchar *ppd_device_id;
+  gchar *ppd_product;
+  gchar *ppd_make_and_model;
+
+  gchar *driver_type;
+
+  gchar *mfg;
+  gchar *mdl;
+  gint   match_level;
+  gint   preference_value;
+} PPDItem;
+
+
+static void
+ppd_item_free (PPDItem *item)
+{
+  if (item)
+    {
+      g_free (item->ppd_name);
+      g_free (item->ppd_device_id);
+      g_free (item->ppd_product);
+      g_free (item->ppd_make_and_model);
+      g_free (item->driver_type);
+      g_free (item->mfg);
+      g_free (item->mdl);
+    }
+}
+
+static PPDItem *
+ppd_item_copy (PPDItem *item)
+{
+  PPDItem *result = NULL;
+
+  result = g_new0 (PPDItem, 1);
+  if (item && result)
+    {
+      result->ppd_name = g_strdup (item->ppd_name);
+      result->ppd_device_id = g_strdup (item->ppd_device_id);
+      result->ppd_product = g_strdup (item->ppd_product);
+      result->ppd_make_and_model = g_strdup (item->ppd_make_and_model);
+      result->driver_type = g_strdup (item->driver_type);
+      result->mfg = g_strdup (item->mfg);
+      result->mdl = g_strdup (item->mdl);
+      result->match_level = item->match_level;
+      result->preference_value = item->preference_value;
+    }
+
+  return result;
+}
+
+
+/*
+ * Make deep copy of given const string array.
+ */
+static gchar **
+strvdup (const gchar * const x[])
+{
+  gint i, length = 0;
+  gchar **result;
+
+  for (length = 0; x && x[length]; length++);
+
+  length++;
+
+  result = g_new0 (gchar *, length);
+  for (i = 0; i < length; i++)
+    result[i] = g_strdup (x[i]);
+
+  return result;
+}
+
+/*
+ * Normalize given string so that it is lowercase, doesn't
+ * have trailing or leading whitespaces and digits doesn't
+ * neighbour with alphabetic.
+ * (see cupshelpers/ppds.py from system-config-printer)
+ */
+static gchar *
+normalize (const gchar *input_string)
+{
+  gchar *tmp = NULL;
+  gchar *res = NULL;
+  gchar *result = NULL;
+  gint   i, j = 0, k = -1;
+
+  if (input_string)
+    {
+      tmp = g_strstrip (g_ascii_strdown (input_string, -1));
+      if (tmp)
+        {
+          res = g_new (gchar, 2 * strlen (tmp));
+
+          for (i = 0; i < strlen (tmp); i++)
+            {
+              if ((g_ascii_isalpha (tmp[i]) && k >= 0 && g_ascii_isdigit (res[k])) ||
+                  (g_ascii_isdigit (tmp[i]) && k >= 0 && g_ascii_isalpha (res[k])))
+                {
+                  res[j] = ' ';
+                  k = j++;
+                  res[j] = tmp[i];
+                  k = j++;
+                }
+              else
+                {
+                  if (g_ascii_isspace (tmp[i]) || !g_ascii_isalnum (tmp[i]))
+                    {
+                      if (!(k >= 0 && res[k] == ' '))
+                        {
+                          res[j] = ' ';
+                          k = j++;
+                        }
+                    }
+                  else
+                    {
+                      res[j] = tmp[i];
+                      k = j++;
+                    }
+                }
+            }
+
+          res[j] = '\0';
+
+          result = g_strdup (res);
+          g_free (tmp);
+          g_free (res);
+        }
+    }
+
+  return result;
+}
+
+
+/*
+ * Find out type of the given printer driver.
+ * (see xml/preferreddrivers.xml from system-config-printer)
+ */
+static gchar *
+get_driver_type (gchar *ppd_name,
+                 gchar *ppd_device_id,
+                 gchar *ppd_make_and_model,
+                 gchar *ppd_product,
+                 gint   match_level)
+{
+  gchar *tmp = NULL;
+
+  if (match_level == PPD_GENERIC_MATCH)
+    {
+      if (ppd_name && g_regex_match_simple ("(foomatic(-db-compressed-ppds)?|ijsgutenprint.*):", ppd_name, 0, 0))
+        {
+          tmp = get_tag_value ("DRV", ppd_device_id);
+          if (tmp && g_regex_match_simple (".*,?R1", tmp, 0, 0))
+            return g_strdup ("generic-foomatic-recommended");
+        }
+    }
+
+  if (match_level == PPD_GENERIC_MATCH || match_level == PPD_NO_MATCH)
+    {
+      if (ppd_name && g_regex_match_simple ("(foomatic(-db-compressed-ppds)?|ijsgutenprint.*):Generic-ESC_P", ppd_name, 0, 0))
+        return g_strdup ("generic-escp");
+
+      if (ppd_name && g_regex_match_simple ("drv:///sample.drv/epson(9|24).ppd", ppd_name, 0, 0))
+        return g_strdup ("generic-escp");
+
+      if (g_strcmp0 (ppd_make_and_model, "Generic PostScript Printer") == 0)
+        return g_strdup ("generic-postscript");
+
+      if (g_strcmp0 (ppd_make_and_model, "Generic PCL 6 Printer") == 0)
+        return g_strdup ("generic-pcl6");
+
+      if (g_strcmp0 (ppd_make_and_model, "Generic PCL 5e Printer") == 0)
+        return g_strdup ("generic-pcl5e");
+
+      if (g_strcmp0 (ppd_make_and_model, "Generic PCL 5 Printer") == 0)
+        return g_strdup ("generic-pcl5");
+
+      if (g_strcmp0 (ppd_make_and_model, "Generic PCL Laser Printer") == 0)
+        return g_strdup ("generic-pcl");
+
+      return g_strdup ("generic");
+    }
+
+
+  if (ppd_name && g_regex_match_simple ("drv:///sample.drv/", ppd_name, 0, 0))
+    return g_strdup ("cups");
+
+  if (ppd_product && g_regex_match_simple (".*Ghostscript", ppd_product, 0, 0))
+    return g_strdup ("ghostscript");
+
+  if (ppd_name && g_regex_match_simple ("gutenprint.*:.*/simple|.*-gutenprint.*\\.sim", ppd_name, 0, 0))
+    return g_strdup ("gutenprint-simplified");
+
+  if (ppd_name && g_regex_match_simple ("gutenprint.*:|.*-gutenprint", ppd_name, 0, 0))
+    return g_strdup ("gutenprint-expert");
+
+  if (ppd_make_and_model && g_regex_match_simple (".* Foomatic/hpijs", ppd_make_and_model, 0, 0))
+    {
+      tmp = get_tag_value ("DRV", ppd_device_id);
+      if (tmp && g_regex_match_simple (",?R1", tmp, 0, 0))
+        return g_strdup ("foomatic-recommended-hpijs");
+    }
+
+  if (ppd_make_and_model && g_regex_match_simple (".* Foomatic/hpijs", ppd_make_and_model, 0, 0))
+    return g_strdup ("foomatic-hpijs");
+
+  if (ppd_name && g_regex_match_simple ("foomatic(-db-compressed-ppds)?:", ppd_name, 0, 0) &&
+      ppd_make_and_model && g_regex_match_simple (".* Postscript", ppd_make_and_model, 0, 0))
+    {
+      tmp = get_tag_value ("DRV", ppd_device_id);
+      if (tmp && g_regex_match_simple (".*,?R1", tmp, 0, 0))
+        return g_strdup ("foomatic-recommended-postscript");
+    }
+
+  if (ppd_name && g_regex_match_simple ("foomatic(-db-compressed-ppds)?:.*-Postscript", ppd_name, 0, 0))
+    return g_strdup ("foomatic-postscript");
+
+  if (ppd_make_and_model && g_regex_match_simple (".* Foomatic/pxlmono", ppd_make_and_model, 0, 0))
+    return g_strdup ("foomatic-pxlmono");
+
+  if (ppd_name && g_regex_match_simple ("(foomatic(-db-compressed-ppds)?|ijsgutenprint.*):", ppd_name, 0, 0))
+    {
+      tmp = get_tag_value ("DRV", ppd_device_id);
+      if (tmp && g_regex_match_simple (".*,?R1", tmp, 0, 0))
+        return g_strdup ("foomatic-recommended-non-postscript");
+    }
+
+  if (ppd_name && g_regex_match_simple ("(foomatic(-db-compressed-ppds)?|ijsgutenprint.*):.*-gutenprint", ppd_name, 0, 0))
+    return g_strdup ("foomatic-gutenprint");
+
+  if (ppd_name && g_regex_match_simple ("(foomatic(-db-compressed-ppds)?|ijsgutenprint.*):", ppd_name, 0, 0))
+    return g_strdup ("foomatic");
+
+  if (ppd_name && g_regex_match_simple ("drv:///(hp/)?hpcups.drv/|.*-hpcups", ppd_name, 0, 0) &&
+      ppd_make_and_model && g_regex_match_simple (".* plugin", ppd_make_and_model, 0, 0))
+    return g_strdup ("hpcups-plugin");
+
+  if (ppd_name && g_regex_match_simple ("drv:///(hp/)?hpcups.drv/|.*-hpcups", ppd_name, 0, 0))
+    return g_strdup ("hpcups");
+
+  if (ppd_name && g_regex_match_simple ("drv:///(hp/)?hpijs.drv/|.*-hpijs", ppd_name, 0, 0) &&
+      ppd_make_and_model && g_regex_match_simple (".* plugin", ppd_make_and_model, 0, 0))
+    return g_strdup ("hpijs-plugin");
+
+  if (ppd_name && g_regex_match_simple ("drv:///(hp/)?hpijs.drv/|.*-hpijs", ppd_name, 0, 0))
+    return g_strdup ("hpijs");
+
+  if (ppd_name && g_regex_match_simple (".*splix", ppd_name, 0, 0))
+    return g_strdup ("splix");
+
+  if (ppd_name && g_regex_match_simple (".*turboprint", ppd_name, 0, 0))
+    return g_strdup ("turboprint");
+
+  if (ppd_name && g_regex_match_simple (".*/(Ricoh|Lanier|Gestetner|InfoPrint|Infotech|Savin|NRG)/PS/", ppd_name, 0, 0))
+    return g_strdup ("manufacturer-ricoh-postscript");
+
+  if (ppd_name && g_regex_match_simple (".*/(Ricoh|Lanier|Gestetner|InfoPrint|Infotech|Savin|NRG)/PXL/", ppd_name, 0, 0))
+    return g_strdup ("manufacturer-ricoh-pxl");
+
+  if (match_level == PPD_EXACT_CMD_MATCH)
+    return g_strdup ("manufacturer-cmd");
+
+  return g_strdup ("manufacturer");
+}
+
+
+/*
+ * Return preference value. The most preferred driver has the lowest value.
+ * If the value is higher or equal to 1000 then try to avoid installation
+ * of this driver. If it is higher or equal to 2000 then don't install
+ * this driver.
+ * (see xml/preferreddrivers.xml from system-config-printer)
+ */
+static gint
+get_driver_preference (PPDItem *item)
+{
+  gchar   *tmp1 = NULL;
+  gchar   *tmp2 = NULL;
+  gint     result = 0;
+
+  if (item && item->ppd_make_and_model &&
+      g_regex_match_simple ("Brother HL-2030", item->ppd_make_and_model, 0, 0))
+    {
+      tmp1 = get_tag_value ("MFG", item->ppd_device_id);
+      tmp2 = get_tag_value ("MDL", item->ppd_device_id);
+
+      if (tmp1 && g_regex_match_simple ("Brother", tmp1, 0, 0) &&
+          tmp2 && g_regex_match_simple ("HL-2030", tmp2, 0, 0))
+        {
+          if (item->driver_type && g_regex_match_simple ("gutenprint*", item->driver_type, 0, 0))
+            return result + 2000;
+          else
+            return result;
+        }
+    }
+  result++;
+
+  if (item && item->ppd_make_and_model &&
+      g_regex_match_simple ("(Ricoh|Lanier|Gestetner|InfoPrint|Infotech|Savin|NRG) ", item->ppd_make_and_model, 0, 0))
+    {
+      tmp1 = get_tag_value ("MFG", item->ppd_device_id);
+
+      if (tmp1 && g_regex_match_simple ("(Ricoh|Lanier|Gestetner|InfoPrint|Infotech|Savin|NRG)", tmp1, 0, 0) &&
+          ((g_strcmp0 (item->driver_type, "manufacturer-ricoh-postscript") == 0) ||
+           (g_strcmp0 (item->driver_type, "manufacturer-ricoh-pxl") == 0)))
+        return result;
+    }
+  result++;
+
+  if (item && item->ppd_make_and_model &&
+      g_regex_match_simple ("Xerox 6250DP", item->ppd_make_and_model, 0, 0))
+    {
+      tmp1 = get_tag_value ("MFG", item->ppd_device_id);
+      tmp2 = get_tag_value ("MDL", item->ppd_device_id);
+
+      if (tmp1 && g_regex_match_simple ("Xerox", tmp1, 0, 0) &&
+          tmp2 && g_regex_match_simple ("6250DP", tmp2, 0, 0))
+        {
+          if (item->driver_type && g_regex_match_simple ("gutenprint*", item->driver_type, 0, 0))
+            return result + 1000;
+          else
+            return result;
+        }
+    }
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "manufacturer-cmd") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "foomatic-recommended-hpijs") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "foomatic-recommended-non-postscript") == 0)
+    return result;
+  result++;
+
+  if (item && item->driver_type &&
+      g_regex_match_simple ("manufacturer*", item->driver_type, 0, 0))
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "foomatic-recommended-postscript") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "foomatic-postscript") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "hpcups") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "hpijs") == 0)
+    return result;
+  result++;
+
+  if (item && item->ppd_make_and_model &&
+      g_regex_match_simple ("(HP|Hewlett-Packard) ", item->ppd_make_and_model, 0, 0))
+    {
+      tmp1 = get_tag_value ("MFG", item->ppd_device_id);
+
+      if (tmp1 && g_regex_match_simple ("HP|Hewlett-Packard", tmp1, 0, 0) &&
+          g_strcmp0 (item->driver_type, "foomatic-hpijs") == 0)
+        return result;
+    }
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "gutenprint-simplified") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "gutenprint-expert") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "foomatic-hpijs") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "foomatic-gutenprint") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "foomatic") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "cups") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "generic-postscript") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "generic-foomatic-recommended") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "generic-pcl6") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "generic-pcl5c") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "generic-pcl5e") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "generic-pcl5") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "generic-pcl") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "foomatic-pxlmono") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "generic-escp") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "ghostscript") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "generic") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "hpcups-plugin") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "hpijs-plugin") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "splix") == 0)
+    return result;
+  result++;
+
+  if (item && g_strcmp0 (item->driver_type, "turboprint") == 0)
+    return result;
+  result++;
+
+  return result;
+}
+
+
+/*
+ * Compare driver types according to preference order.
+ * The most preferred driver is the lowest one.
+ * (see xml/preferreddrivers.xml from system-config-printer)
+ */
+static gint
+preference_value_cmp (gconstpointer a,
+                      gconstpointer b)
+{
+  PPDItem *c = (PPDItem *) a;
+  PPDItem *d = (PPDItem *) b;
+
+  if (c == NULL && d == NULL)
+    return 0;
+  else if (c == NULL)
+    return -1;
+  else if (d == NULL)
+    return 1;
+
+  if (c->preference_value < d->preference_value)
+    return -1;
+  else if (c->preference_value > d->preference_value)
+    return 1;
+  else
+    return 0;
+}
+
+
+/* Compare PPDItems a and b according to normalized model name */
+static gint
+item_cmp (gconstpointer a,
+          gconstpointer b)
+{
+  PPDItem *c = (PPDItem *) a;
+  PPDItem *d = (PPDItem *) b;
+  glong   a_number;
+  glong   b_number;
+  gchar  *a_normalized;
+  gchar  *b_normalized;
+  gchar **av = NULL;
+  gchar **bv = NULL;
+  gint    a_length = 0;
+  gint    b_length = 0;
+  gint    min_length;
+  gint    result = 0;
+  gint    i;
+
+  if (c && d)
+    {
+      a_normalized = normalize (c->mdl);
+      b_normalized = normalize (d->mdl);
+
+      if (a_normalized)
+        av = g_strsplit (a_normalized, " ", 0);
+
+      if (b_normalized)
+        bv = g_strsplit (b_normalized, " ", 0);
+
+      if (av)
+        a_length = g_strv_length (av);
+
+      if (bv)
+        b_length = g_strv_length (bv);
+
+      min_length = a_length < b_length ? a_length : b_length;
+
+      for (i = 0; i < min_length; i++)
+        {
+          if (g_ascii_isdigit (av[i][0]) && g_ascii_isdigit (bv[i][0]))
+            {
+              a_number = atol (av[i]);
+              b_number = atol (bv[i]);
+              if (a_number < b_number)
+                {
+                  result = -1;
+                  goto out;
+                }
+              else if (a_number > b_number)
+                {
+                  result = 1;
+                  goto out;
+                }
+            }
+          else if (g_ascii_isdigit (av[i][0]) && !g_ascii_isdigit (bv[i][0]))
+            {
+              result = -1;
+              goto out;
+            }
+          else if (!g_ascii_isdigit (av[i][0]) && g_ascii_isdigit (bv[i][0]))
+            {
+              result = 1;
+              goto out;
+            }
+          else
+            {
+              if (g_strcmp0 (av[i], bv[i]) != 0)
+                {
+                  result = g_strcmp0 (av[i], bv[i]);
+                  goto out;
+                }
+            }
+        }
+
+      if (a_length < b_length)
+        result = -1;
+      else if (a_length > b_length)
+        result = 1;
+    }
+
+out:
+  if (av)
+    g_strfreev (av);
+
+  if (bv)
+    g_strfreev (bv);
+
+  g_free (a_normalized);
+  g_free (b_normalized);
+
+  return result;
+}
+
+
+static gint
+get_prefix_length (gchar *a, gchar *b)
+{
+  gint a_length;
+  gint b_length;
+  gint min_length;
+  gint i;
+
+  if (a && b)
+    {
+      a_length = strlen (a);
+      b_length = strlen (b);
+      min_length = a_length < b_length ? a_length : b_length;
+
+      for (i = 0; i < min_length; i++)
+        {
+          if (a[i] != b[i])
+            return i;
+        }
+      return min_length;
+    }
+
+  return 0;
+}
+
+
+/*
+ * Append best matching ppds from list "ppds" to list "list"
+ * according to model name "model". Return the resulting list.
+ */
+static GList *
+append_best_ppds (GList *list,
+                  GList *ppds,
+                  gchar *model)
+{
+  PPDItem *item;
+  PPDItem *tmp_item;
+  PPDItem *best_item = NULL;
+  gchar   *mdl_normalized;
+  gchar   *mdl;
+  gchar   *tmp;
+  GList   *local_ppds;
+  GList   *actual_item;
+  GList   *candidates = NULL;
+  GList   *tmp_list = NULL;
+  GList   *result = NULL;
+  gint     best_prefix_length = -1;
+  gint     prefix_length;
+
+  result = list;
+
+  if (model)
+    {
+      mdl = g_ascii_strdown (model, -1);
+      if (g_str_has_suffix (mdl, " series"))
+        {
+          tmp = g_strndup (mdl, strlen (mdl) - 7);
+          g_free (mdl);
+          mdl = tmp;
+        }
+
+      mdl_normalized = normalize (mdl);
+
+      item = g_new0 (PPDItem, 1);
+      item->ppd_device_id = g_strdup_printf ("mdl:%s;", mdl);
+      item->mdl = mdl_normalized;
+
+      local_ppds = g_list_copy (ppds);
+      local_ppds = g_list_append (local_ppds, item);
+      local_ppds = g_list_sort (local_ppds, item_cmp);
+
+      actual_item = g_list_find (local_ppds, item);
+      if (actual_item)
+        {
+          if (actual_item->prev)
+            candidates = g_list_append (candidates, actual_item->prev->data);
+          if (actual_item->next)
+            candidates = g_list_append (candidates, actual_item->next->data);
+        }
+
+      for (tmp_list = candidates; tmp_list; tmp_list = tmp_list->next)
+        {
+          tmp_item = (PPDItem *) tmp_list->data;
+
+          prefix_length = get_prefix_length (tmp_item->mdl, mdl_normalized);
+          if (prefix_length > best_prefix_length)
+            {
+              best_prefix_length = prefix_length;
+              best_item = tmp_item;
+            }
+        }
+
+      if (best_item && best_prefix_length > strlen (mdl_normalized) / 2)
+        {
+          if (best_prefix_length == strlen (mdl_normalized))
+            best_item->match_level = PPD_EXACT_MATCH;
+          else
+            best_item->match_level = PPD_CLOSE_MATCH;
+
+          result = g_list_append (result, ppd_item_copy (best_item));
+        }
+      else
+        {
+          /* TODO the last resort (see _findBestMatchPPDs() in ppds.py) */
+        }
+
+      g_list_free (candidates);
+      g_list_free (local_ppds);
+
+      g_free (item->ppd_device_id);
+      g_free (item);
+      g_free (mdl);
+      g_free (mdl_normalized);
+    }
+
+  return result;
+}
+
+/*
+ * Return the best matching driver name
+ * for device described by given parameters.
+ */
+PPDName *
 get_ppd_name (gchar *device_class,
               gchar *device_id,
               gchar *device_info,
@@ -89,104 +802,343 @@ get_ppd_name (gchar *device_class,
               gchar *device_uri,
               gchar *device_location)
 {
-  http_t *http = NULL;
-  ipp_t  *request = NULL;
-  ipp_t  *response = NULL;
-  gchar  *mfg = NULL;
-  gchar  *mdl = NULL;
-  gchar  *result = NULL;
+  ipp_attribute_t *attr = NULL;
+  const gchar     *hp_equivalents[] = {"hp", "hewlett packard", NULL};
+  const gchar     *kyocera_equivalents[] = {"kyocera", "kyocera mita", NULL};
+  const gchar     *toshiba_equivalents[] = {"toshiba", "toshiba tec corp.", NULL};
+  const gchar     *lexmark_equivalents[] = {"lexmark", "lexmark international", NULL};
+  gboolean         ppd_exact_match_found = FALSE;
+  PPDItem         *item;
+  PPDName         *result = NULL;
+  http_t          *http = NULL;
+  ipp_t           *request = NULL;
+  ipp_t           *response = NULL;
+  GList           *tmp_list;
+  GList           *tmp_list2;
+  GList           *mdls = NULL;
+  GList           *list = NULL;
+  gchar           *mfg_normalized = NULL;
+  gchar           *mdl_normalized = NULL;
+  gchar           *eq_normalized = NULL;
+  gchar           *mfg = NULL;
+  gchar           *mdl = NULL;
+  gchar           *tmp = NULL;
+  gchar           *ppd_device_id;
+  gchar           *ppd_make_and_model;
+  gchar           *ppd_name;
+  gchar           *ppd_product;
+  gchar          **equivalents = NULL;
+  gint             i;
 
   mfg = get_tag_value (device_id, "mfg");
+  if (!mfg)
+    mfg = get_tag_value (device_id, "manufacturer");
+
   mdl = get_tag_value (device_id, "mdl");
+  if (!mdl)
+    mdl = get_tag_value (device_id, "model");
+
+  mfg_normalized = normalize (mfg);
+  mdl_normalized = normalize (mdl);
+
+  if (mfg_normalized && mfg)
+    {
+      if (g_str_has_prefix (mfg_normalized, "hewlett") ||
+          g_str_has_prefix (mfg_normalized, "hp"))
+        equivalents = strvdup (hp_equivalents);
+
+      if (g_str_has_prefix (mfg_normalized, "kyocera"))
+        equivalents = strvdup (kyocera_equivalents);
+
+      if (g_str_has_prefix (mfg_normalized, "toshiba"))
+        equivalents = strvdup (toshiba_equivalents);
+
+      if (g_str_has_prefix (mfg_normalized, "lexmark"))
+        equivalents = strvdup (lexmark_equivalents);
+
+      if (equivalents == NULL)
+        {
+          equivalents = g_new0 (gchar *, 2);
+          equivalents[0] = g_strdup (mfg);
+        }
+    }
 
   http = httpConnectEncrypt (cupsServer (),
                              ippPort (),
                              cupsEncryption ());
 
+  /* Find usable drivers for given device */
   if (http)
     {
-      request = ippNewRequest (CUPS_GET_PPDS);
+      /* Try exact match according to device-id */
       if (device_id)
-        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-device-id",
-                     NULL, device_id);
-      else if (mfg)
-        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make",
-                     NULL, mfg);
-      response = cupsDoRequest (http, request, "/");
-
-      if (response)
         {
-          ipp_attribute_t *attr = NULL;
-          const char      *ppd_device_id;
-          const char      *ppd_make_model;
-          const char      *ppd_make;
-          const char      *ppd_name;
-          gchar           *ppd_mfg;
-          gchar           *ppd_mdl;
+          request = ippNewRequest (CUPS_GET_PPDS);
+          ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
+                        "ppd-device-id", NULL, device_id);
+          response = cupsDoRequest (http, request, "/");
 
-          for (attr = response->attrs; attr != NULL; attr = attr->next)
+          if (response &&
+              response->request.status.status_code <= IPP_OK_CONFLICT)
             {
-              while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
-                attr = attr->next;
+              for (attr = response->attrs; attr != NULL; attr = attr->next)
+                {
+                  while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+                    attr = attr->next;
 
-              if (attr == NULL)
-                break;
+                  if (attr == NULL)
+                    break;
 
-              ppd_device_id  = "NONE";
-              ppd_make_model = NULL;
-              ppd_make       = NULL;
-              ppd_name       = NULL;
-              ppd_mfg        = NULL;
-              ppd_mdl        = NULL;
+                  ppd_device_id = NULL;
+                  ppd_make_and_model = NULL;
+                  ppd_name = NULL;
+                  ppd_product = NULL;
 
-              while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
-                {
-                  if (!strcmp(attr->name, "ppd-device-id") &&
-                      attr->value_tag == IPP_TAG_TEXT)
-                    ppd_device_id = 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;
-                  else if (!strcmp(attr->name, "ppd-make") &&
-                           attr->value_tag == IPP_TAG_TEXT)
-                    ppd_make = attr->values[0].string.text;
-                  else if (!strcmp(attr->name, "ppd-make-and-model") &&
-                           attr->value_tag == IPP_TAG_TEXT)
-                    ppd_make_model = attr->values[0].string.text;
-
-                  attr = attr->next;
+                  while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+                    {
+                      if (g_strcmp0 (attr->name, "ppd-device-id") == 0 &&
+                          attr->value_tag == IPP_TAG_TEXT)
+                        ppd_device_id = attr->values[0].string.text;
+                      else if (g_strcmp0 (attr->name, "ppd-make-and-model") == 0 &&
+                               attr->value_tag == IPP_TAG_TEXT)
+                        ppd_make_and_model = attr->values[0].string.text;
+                      else if (g_strcmp0 (attr->name, "ppd-name") == 0 &&
+                               attr->value_tag == IPP_TAG_NAME)
+                        ppd_name = attr->values[0].string.text;
+                      else if (g_strcmp0 (attr->name, "ppd-product") == 0 &&
+                               attr->value_tag == IPP_TAG_TEXT)
+                        ppd_product = attr->values[0].string.text;
+
+                      attr = attr->next;
+                    }
+
+                  if (ppd_device_id && ppd_name)
+                    {
+                      item = g_new0 (PPDItem, 1);
+                      item->ppd_name = g_strdup (ppd_name);
+                      item->ppd_device_id = g_strdup (ppd_device_id);
+                      item->ppd_make_and_model = g_strdup (ppd_make_and_model);
+                      item->ppd_product = g_strdup (ppd_product);
+
+                      tmp = get_tag_value (ppd_device_id, "mfg");
+                      if (!tmp)
+                        tmp = get_tag_value (ppd_device_id, "manufacturer");
+                      item->mfg = normalize (tmp);
+                      g_free (tmp);
+
+                      tmp = get_tag_value (ppd_device_id, "mdl");
+                      if (!tmp)
+                        tmp = get_tag_value (ppd_device_id, "model");
+                      item->mdl = normalize (tmp);
+                      g_free (tmp);
+
+                      item->match_level = PPD_EXACT_CMD_MATCH;
+                      ppd_exact_match_found = TRUE;
+                      list = g_list_append (list, item);
+                    }
+
+                  if (attr == NULL)
+                    break;
                 }
+            }
+
+          if (response)
+            ippDelete(response);
+        }
 
-              if (mfg && mdl && !result)
+      /* Try match according to manufacturer and model fields */
+      if (!ppd_exact_match_found && mfg_normalized && mdl_normalized)
+        {
+          request = ippNewRequest (CUPS_GET_PPDS);
+          response = cupsDoRequest (http, request, "/");
+
+          if (response &&
+              response->request.status.status_code <= IPP_OK_CONFLICT)
+            {
+              for (i = 0; equivalents && equivalents[i]; i++)
                 {
-                  if (ppd_device_id)
+                  eq_normalized = normalize (equivalents[i]);
+                  for (attr = response->attrs; attr != NULL; attr = attr->next)
                     {
-                      ppd_mfg = get_tag_value (ppd_device_id, "mfg");
+                      while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+                        attr = attr->next;
+
+                      if (attr == NULL)
+                        break;
+
+                      ppd_device_id = NULL;
+                      ppd_make_and_model = NULL;
+                      ppd_name = NULL;
+                      ppd_product = NULL;
 
-                      if (ppd_mfg && g_ascii_strcasecmp (ppd_mfg, mfg) == 0)
+                      while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
                         {
-                          ppd_mdl = get_tag_value (ppd_device_id, "mdl");
+                          if (g_strcmp0 (attr->name, "ppd-device-id") == 0 &&
+                              attr->value_tag == IPP_TAG_TEXT)
+                            ppd_device_id = attr->values[0].string.text;
+                          else if (g_strcmp0 (attr->name, "ppd-make-and-model") == 0 &&
+                                   attr->value_tag == IPP_TAG_TEXT)
+                            ppd_make_and_model = attr->values[0].string.text;
+                          else if (g_strcmp0 (attr->name, "ppd-name") == 0 &&
+                                   attr->value_tag == IPP_TAG_NAME)
+                            ppd_name = attr->values[0].string.text;
+                          else if (g_strcmp0 (attr->name, "ppd-product") == 0 &&
+                                   attr->value_tag == IPP_TAG_TEXT)
+                            ppd_product = attr->values[0].string.text;
+
+                          attr = attr->next;
+                        }
+
+                      if (ppd_device_id && ppd_name)
+                        {
+                          item = g_new0 (PPDItem, 1);
+                          item->ppd_name = g_strdup (ppd_name);
+                          item->ppd_device_id = g_strdup (ppd_device_id);
+                          item->ppd_make_and_model = g_strdup (ppd_make_and_model);
+                          item->ppd_product = g_strdup (ppd_product);
+
+                          tmp = get_tag_value (ppd_device_id, "mfg");
+                          if (!tmp)
+                            tmp = get_tag_value (ppd_device_id, "manufacturer");
+                          item->mfg = normalize (tmp);
+                          g_free (tmp);
+
+                          tmp = get_tag_value (ppd_device_id, "mdl");
+                          if (!tmp)
+                            tmp = get_tag_value (ppd_device_id, "model");
+                          item->mdl = normalize (tmp);
+                          g_free (tmp);
+
+                          if (item->mdl && item->mfg &&
+                              g_ascii_strcasecmp (item->mdl, mdl_normalized) == 0 &&
+                              g_ascii_strcasecmp (item->mfg, eq_normalized) == 0)
+                            {
+                              item->match_level = PPD_EXACT_MATCH;
+                              ppd_exact_match_found = TRUE;
+                            }
 
-                          if (ppd_mdl && g_ascii_strcasecmp (ppd_mdl, mdl) == 0)
+                          if (item->match_level == PPD_EXACT_MATCH)
+                            list = g_list_append (list, item);
+                          else if (item->mfg &&
+                                   g_ascii_strcasecmp (item->mfg, eq_normalized) == 0)
+                            mdls = g_list_append (mdls, item);
+                          else
                             {
-                              result = g_strdup (ppd_name);
-                              g_free (ppd_mdl);
+                              ppd_item_free (item);
+                              g_free (item);
                             }
-                          g_free (ppd_mfg);
                         }
+
+                      if (attr == NULL)
+                        break;
                     }
+
+                  g_free (eq_normalized);
                 }
+            }
+
+          if (response)
+            ippDelete(response);
+        }
+
+      httpClose (http);
+    }
+
+  if (list == NULL)
+    list = append_best_ppds (list, mdls, mdl);
+
+  /* Find out driver types for all listed drivers and set their preference values */
+  for (tmp_list = list; tmp_list; tmp_list = tmp_list->next)
+    {
+      item = (PPDItem *) tmp_list->data;
+      if (item)
+        {
+          item->driver_type = get_driver_type (item->ppd_name,
+                                               item->ppd_device_id,
+                                               item->ppd_make_and_model,
+                                               item->ppd_product,
+                                               item->match_level);
+          item->preference_value = get_driver_preference (item);
+        }
+    }
+
+  /* Sort driver list according to preference value */
+  list = g_list_sort (list, preference_value_cmp);
+
+  /* Split blacklisted drivers to tmp_list */
+  for (tmp_list = list; tmp_list; tmp_list = tmp_list->next)
+    {
+      item = (PPDItem *) tmp_list->data;
+      if (item && item->preference_value >= 2000)
+        break;
+    }
+
+  /* Free tmp_list */
+  if (tmp_list)
+    {
+      if (tmp_list->prev)
+        tmp_list->prev->next = NULL;
+      else
+        list = NULL;
 
-              if (attr == NULL)
+      tmp_list->prev = NULL;
+      for (tmp_list2 = tmp_list; tmp_list2; tmp_list2 = tmp_list2->next)
+        {
+          item = (PPDItem *) tmp_list2->data;
+          ppd_item_free (item);
+          g_free (item);
+        }
+
+      g_list_free (tmp_list);
+    }
+
+  /* Free driver list and set the best one */
+  if (list)
+    {
+      item = (PPDItem *) list->data;
+      if (item)
+        {
+          result = g_new0 (PPDName, 1);
+          result->ppd_name = g_strdup (item->ppd_name);
+          result->ppd_match_level = item->match_level;
+          switch (item->match_level)
+            {
+              case PPD_GENERIC_MATCH:
+              case PPD_CLOSE_MATCH:
+                g_warning ("Found PPD does not match given device exactly!");
+                break;
+              default:
                 break;
             }
-          ippDelete(response);
         }
-      httpClose (http);
+
+      for (tmp_list = list; tmp_list; tmp_list = tmp_list->next)
+        {
+          item = (PPDItem *) tmp_list->data;
+          ppd_item_free (item);
+          g_free (item);
+        }
+
+      g_list_free (list);
+    }
+
+  if (mdls)
+    {
+      for (tmp_list = mdls; tmp_list; tmp_list = tmp_list->next)
+        {
+          item = (PPDItem *) tmp_list->data;
+          ppd_item_free (item);
+          g_free (item);
+        }
+      g_list_free (mdls);
     }
 
   g_free (mfg);
   g_free (mdl);
+  g_free (mfg_normalized);
+  g_free (mdl_normalized);
+  if (equivalents)
+    g_strfreev (equivalents);
 
   return result;
 }
diff --git a/panels/printers/pp-utils.h b/panels/printers/pp-utils.h
index 417b73f..9e3c77f 100644
--- a/panels/printers/pp-utils.h
+++ b/panels/printers/pp-utils.h
@@ -26,6 +26,24 @@
 
 G_BEGIN_DECLS
 
+/*
+ * Match level of PPD driver.
+ */
+enum
+{
+  PPD_NO_MATCH = 0,
+  PPD_GENERIC_MATCH,
+  PPD_CLOSE_MATCH,
+  PPD_EXACT_MATCH,
+  PPD_EXACT_CMD_MATCH
+};
+
+typedef struct
+{
+  gchar *ppd_name;
+  gint   ppd_match_level;
+} PPDName;
+
 DBusGProxy *get_dbus_proxy (const gchar *name,
                             const gchar *path,
                             const gchar *iface,
@@ -34,7 +52,7 @@ DBusGProxy *get_dbus_proxy (const gchar *name,
 gchar      *get_tag_value (const gchar *tag_string,
                            const gchar *tag_name);
 
-gchar      *get_ppd_name (gchar *device_class,
+PPDName    *get_ppd_name (gchar *device_class,
                           gchar *device_id,
                           gchar *device_info,
                           gchar *device_make_and_model,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]