[gnome-software/wip/temp/ubuntu-xenial-rebased: 40/326] Show updates. Fix coding style



commit 8a4a5c2d72f2caabb6c968dc03841e0108428c00
Author: Robert Ancell <robert ancell canonical com>
Date:   Fri Feb 5 15:51:36 2016 +1300

    Show updates. Fix coding style

 src/plugins/gs-plugin-apt.c |  251 +++++++++++++++++++++++++++++++++++++------
 1 files changed, 217 insertions(+), 34 deletions(-)
---
diff --git a/src/plugins/gs-plugin-apt.c b/src/plugins/gs-plugin-apt.c
index d6b6ef1..a338ad1 100644
--- a/src/plugins/gs-plugin-apt.c
+++ b/src/plugins/gs-plugin-apt.c
@@ -23,9 +23,12 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
 #include <gs-plugin.h>
 
 struct GsPluginPrivate {
+       gboolean cache_loaded;
+       GList *dpkg_cache;
 };
 
 const gchar *
@@ -59,6 +62,119 @@ find_app (GList **list, const gchar *source)
        return NULL;
 }
 
+// Ordering of symbols in dpkg ~, 0-9, A-Z, a-z, everything else (ASCII ordering)
+static int
+order (int c)
+{
+       if (isdigit(c))
+               return 0;
+       else if (isalpha(c))
+               return c;
+       else if (c == '~')
+               return -1;
+       else if (c)
+               return c + 256;
+       else
+               return 0;
+}
+
+// Check if this is a valid dpkg character ('-' terminates version for revision)
+static gboolean
+valid_char (char c)
+{
+       return c != '\0' && c != '-';
+}
+
+static int
+compare_version (const char *v0, const char *v1)
+{
+       while (valid_char (*v0) || valid_char (*v1))
+       {
+               int first_diff = 0;
+
+               // Compare non-digits based on ordering rules
+               while ((valid_char (*v0) && !isdigit (*v0)) || (valid_char (*v1) && !isdigit (*v1))) {
+                       int ac = order (*v0);
+                       int bc = order (*v1);
+
+                       if (ac != bc)
+                               return ac - bc;
+
+                       v0++;
+                       v1++;
+               }
+
+               // Skip leading zeroes
+               while (*v0 == '0')
+                       v0++;
+               while (*v1 == '0')
+                       v1++;
+
+               // Compare numbers - longest wins, otherwise compare next digit
+               while (isdigit (*v0) && isdigit (*v1)) {
+                       if (first_diff == 0)
+                               first_diff = *v0 - *v1;
+                       v0++;
+                       v1++;
+               }
+               if (isdigit (*v0))
+                       return 1;
+               if (isdigit (*v1))
+                       return -1;
+               if (first_diff)
+                       return first_diff;
+       }
+
+       return 0;
+}
+
+// Get the epoch, i.e. 1:2.3-4 -> '1'
+static int
+get_epoch (const gchar *version)
+{
+       if (strchr (version, ':') == NULL)
+               return 0;
+       return atoi (version);
+}
+
+// Get the version after the epoch, i.e. 1:2.3-4 -> '2.3'
+static const gchar *
+get_version (const gchar *version)
+{
+       const gchar *v = strchr (version, ':');
+       return v ? v : version;
+}
+
+// Get the debian revision, i.e. 1:2.3-4 -> '4'
+static const gchar *
+get_revision (const gchar *version)
+{
+       const gchar *v = strchr (version, '-');
+       return v ? v + 1: version + strlen (version);
+}
+
+static int
+compare_dpkg_version (const gchar *v0, const gchar *v1)
+{
+       int r;
+
+       r = get_epoch (v0) - get_epoch (v1);
+       if (r != 0)
+               return r;
+
+       r = compare_version (get_version (v0), get_version (v1));
+       if (r != 0)
+               return r;
+
+       return compare_version (get_revision (v0), get_revision (v1));
+}
+
+static gboolean
+version_newer (const gchar *v0, const gchar *v1)
+{
+       return v0 ? compare_dpkg_version (v0, v1) < 0 : TRUE;
+}
+
 static void
 parse_package_info (const gchar *info, GsPluginRefineFlags flags, GList **list, gboolean mark_available)
 {
@@ -79,31 +195,34 @@ parse_package_info (const gchar *info, GsPluginRefineFlags flags, GList **list,
                }
 
                // Skip other fields until we find an app we know
-               if (!app)
+               if (app == NULL)
                        continue;
 
                if (g_str_has_prefix (lines[i], status_prefix)) {
                        if (g_str_has_suffix (lines[i] + strlen (status_prefix), " installed"))
                                gs_app_set_state (app, AS_APP_STATE_INSTALLED);
-               } else if (g_str_has_prefix (lines[i], installed_size_prefix)) {
-                       if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE) != 0 && gs_app_get_size (app) == 0)
+               } else if (g_str_has_prefix (lines[i], installed_size_prefix) && (flags & 
GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE) != 0) {
+                       if (gs_app_get_size (app) == 0)
                                gs_app_set_size (app, atoi (lines[i] + strlen (installed_size_prefix)) * 
1024);
-               } else if (g_str_has_prefix (lines[i], version_prefix)) {
-                       // FIXME: apt-cache contains multiple versions - pick the best
-                       if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION) != 0 && gs_app_get_version (app) 
== NULL)
-                               gs_app_set_version (app, lines[i] + strlen (version_prefix));
+               } else if (g_str_has_prefix (lines[i], version_prefix) && (flags & 
GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION) != 0) {
+                       const gchar *version = lines[i] + strlen (version_prefix);
+
+                       if (gs_app_get_version (app) == NULL)
+                               gs_app_set_version (app, version);
+                       if (gs_app_get_state (app) == AS_APP_STATE_INSTALLED && version_newer 
(gs_app_get_update_version (app), version))
+                               gs_app_set_update_version (app, version);
                }
        }
 
        g_strfreev (lines);
 }
 
-gboolean
-gs_plugin_refine (GsPlugin *plugin,
-                 GList **list,
-                 GsPluginRefineFlags flags,
-                 GCancellable *cancellable,
-                 GError **error)
+static gboolean
+refine (GsPlugin *plugin,
+        GList **list,
+        GsPluginRefineFlags flags,
+        GCancellable *cancellable,
+        GError **error)
 {
        GList *link;
        GPtrArray *dpkg_argv_array, *cache_argv_array;
@@ -139,7 +258,7 @@ gs_plugin_refine (GsPlugin *plugin,
                const gchar *source;
 
                source = gs_app_get_source_default (app);
-               if (!source)
+               if (source == NULL)
                        continue;
 
                known_apps = TRUE;
@@ -166,19 +285,26 @@ gs_plugin_refine (GsPlugin *plugin,
 }
 
 gboolean
-gs_plugin_add_installed (GsPlugin *plugin,
-                        GList **list,
-                        GCancellable *cancellable,
-                        GError **error)
+gs_plugin_refine (GsPlugin *plugin,
+                 GList **list,
+                 GsPluginRefineFlags flags,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+       // NOTE: Had to put into a static function so can be called from inside plugin - not sure why
+       return refine (plugin, list, flags, cancellable, error);
+}
+
+static gchar **
+get_installed (GError **error)
 {
-       g_autofree gchar *output = NULL;
+       g_autofree gchar *dpkg_stdout = NULL, *dpkg_stderr = NULL;
        gint exit_status;
-       gchar **lines = NULL;
+       gchar *argv[3] = { (gchar *) "dpkg", (gchar *) "--get-selections", NULL }, **lines;
        int i;
+       GPtrArray *array;
 
-       g_printerr ("APT: gs_plugin_add_installed\n");
-
-       if (!g_spawn_command_line_sync ("dpkg --get-selections", &output, NULL, &exit_status, error))
+       if (!g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &dpkg_stdout, &dpkg_stderr, 
&exit_status, error))
                return FALSE;
        if (exit_status != EXIT_SUCCESS) {
                g_set_error (error,
@@ -188,14 +314,15 @@ gs_plugin_add_installed (GsPlugin *plugin,
                return FALSE;
        }
 
-       lines = g_strsplit (output, "\n", -1);
+       array = g_ptr_array_new ();
+       lines = g_strsplit (dpkg_stdout, "\n", -1);
        for (i = 0; lines[i]; i++) {
                g_autoptr(GsApp) app = NULL;
-               gchar *id, *status, *c;
+               gchar *status, *c;
 
                // Line is the form <name>\t<status> - find the status and only use installed packages
                status = strrchr (lines[i], '\t');
-               if (!status)
+               if (status == NULL)
                        continue;
                status++;
                if (strcmp (status, "install") != 0)
@@ -204,14 +331,38 @@ gs_plugin_add_installed (GsPlugin *plugin,
                // Split out name
                c = strchr (lines[i], '\t');
                *c = '\0';
-               id = lines[i];
+               g_ptr_array_add (array, (gpointer) g_strdup (lines[i]));
+       }
+       g_strfreev (lines);
+       g_ptr_array_add (array, NULL);
 
-               app = gs_app_new (id);
-               gs_app_add_source (app, id);
+       return (gchar **) g_ptr_array_free (array, FALSE);
+}
+
+gboolean
+gs_plugin_add_installed (GsPlugin *plugin,
+                        GList **list,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+       gchar **installed;
+       int i;
+
+       //g_printerr ("APT: gs_plugin_add_installed\n");
+
+       installed = get_installed (error);
+       if (installed == NULL)
+               return FALSE;
+
+       for (i = 0; installed[i] != NULL; i++) {
+               g_autoptr(GsApp) app = NULL;
+
+               app = gs_app_new (installed[i]);
+               gs_app_add_source (app, installed[i]);
                gs_app_set_state (app, AS_APP_STATE_INSTALLED);
                gs_plugin_add_app (list, app);
        }
-       g_strfreev (lines);
+       g_strfreev (installed);
 
        return TRUE;
 }
@@ -291,7 +442,7 @@ aptd_transaction (const gchar *method, GsApp *app, GError **error)
                                              -1,
                                              NULL,
                                              error);
-       if (!result)
+       if (result == NULL)
                return FALSE;
        g_variant_get (result, "(s)", &transaction_path);
        g_variant_unref (result);
@@ -332,7 +483,7 @@ aptd_transaction (const gchar *method, GsApp *app, GError **error)
                                              -1,
                                              NULL,
                                              error);
-       if (!result)
+       if (result == NULL)
                return FALSE;
 
        g_main_loop_run (loop);
@@ -358,7 +509,7 @@ gs_plugin_app_install (GsPlugin *plugin,
 {
        //g_printerr ("APT: gs_plugin_app_install\n");
 
-       if (!gs_app_get_source_default (app))
+       if (gs_app_get_source_default (app) == NULL)
                return TRUE;
 
        gs_app_set_state (app, AS_APP_STATE_INSTALLING);
@@ -380,7 +531,7 @@ gs_plugin_app_remove (GsPlugin *plugin,
 {
        //g_printerr ("APT: gs_plugin_app_remove\n");
 
-       if (!gs_app_get_source_default (app))
+       if (gs_app_get_source_default (app) == NULL)
                return TRUE;
 
        gs_app_set_state (app, AS_APP_STATE_REMOVING);
@@ -408,3 +559,35 @@ gs_plugin_refresh (GsPlugin *plugin,
 
        return aptd_transaction ("UpdateCache", NULL, error);
 }
+
+gboolean
+gs_plugin_add_updates (GsPlugin *plugin,
+                       GList **list,
+                       GCancellable *cancellable,
+                       GError **error)
+{
+       GList *installed = NULL, *link;
+
+       g_printerr ("APT: gs_plugin_add_updates\n");
+
+       // Get the version of everything installed
+       // FIXME: Checks all the packages we don't have appstream data for (so inefficient)
+       if (!gs_plugin_add_installed (plugin, &installed, NULL, error) ||
+           !refine (plugin, &installed, GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION, NULL, error)) {
+               g_list_free_full (installed, g_object_unref);
+               return FALSE;
+       }
+
+       for (link = installed; link; link = link->next) {
+               GsApp *app = GS_APP (link->data);
+               const gchar *v0, *v1;
+
+               v0 = gs_app_get_version (app);
+               v1 = gs_app_get_update_version (app);
+               if (v0 != NULL && v1 != NULL && version_newer (v0, v1))
+                       gs_plugin_add_app (list, app);
+       }
+       g_list_free_full (installed, g_object_unref);
+
+       return TRUE;
+}


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