Logo Search packages:      
Sourcecode: langupdate version File versions

langupdate.cc

Go to the documentation of this file.
/***************************************************************************
 *            
 *
 *  Sun Nov 12 23:01:13 2006
 *  Copyright 2006-2009 Neil Williams <codehelp@debian.org>
 ****************************************************************************/
/*
    This package is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**   @file   langupdate.cc
      @brief  Parse the cache output
      @author Neil Williams <codehelp@debian.org>
*/
/** @{
*/

#include "config.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <locale.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <apt-pkg/configuration.h>
#include "langupdate.h"
#include "aptcache.h"
#define GNU_WARRANTY "Copyright (C) 2006-2009 Neil Williams <codehelp@debian.org>\n" \
"This is free software; see the source for copying conditions.  There is NO\n" \
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
#define DPKG_II "^Package: (.*)$"
#define GREX_EMPTY GRegexMatchFlags(0)
#define LOCALEGEN "/etc/locale.gen"
#define STATUS_CHK   "Status"

/** @todo put the static variables into a context struct? */
00049 static guint c = 0;
static const gchar * sourceslist = "/var/lib/"PACKAGE"/sources.list";
static GList * installed = NULL;
static GList * targets  =  NULL;
static GList * purged = NULL;
static GHashTable  * choices = NULL;
/** \brief List of packages that are not from Emdebian
 
 In order for langupdate to be safe in use in Debian, langupdate must
 not attempt to install an Emdebian TDeb when the package installed is
 from Debian as a file conflict will occur. (Emdebian TDebs do not have
 "Replaces:" for the corresponding binary packages.)
 
 Even though the sysadmin can manually delete files from /usr/share/locale,
 it is not safe to assume that this has happened for the specific files
 in the TDeb. Calculating the conflict would require looking up the Source:
 package in the installed package check.
 
 Currently, langupdate takes the safer option that if the relevant binary
 package is not from Emdebian (with an Emdebian version string), then the
 TDeb should not be considered.
 
 The list of packages that fall into this category is held in not_ours.

*/
00074 static GList * not_ours = NULL;
static GHashTable * orphans = NULL;
static gchar * suite_codename = NULL;
static gchar * user_suite = NULL;
static gchar * mirror = NULL;
static gint verbose = 0;
static gboolean dry_run = false;
/** \brief Keep temporary files around for debugging

Rather than a runtime option, this needs to be switched
at compile time. Set true if debug mode is required. */
00085 static gboolean debug = false;

static gboolean show_version = false;
static gboolean purge_mode = false;
static gchar * purge_names = NULL;
static GOptionEntry entries[] = 
{
      { "dry-run", 'n', 0, G_OPTION_ARG_NONE, &dry_run, 
            _("Only show the cache data, do not install"), 0 },
      { "mirror", 'm', 0, G_OPTION_ARG_STRING, &mirror,
            _("Use the specified TDeb mirror instead of the Emdebian default."), 
      /* Translators: mirror_name is to be translated - it expresses
      what the user needs to pass to this option, a name of a mirror. */
            _("mirror_name")},
      { "autoremove", 'r', 0, G_OPTION_ARG_NONE, &purge_mode,
            _("Remove TDebs for unsupported locales."), 0},
      { "verbose", 'v', 0, G_OPTION_ARG_INT, &verbose, _("Specify verbose level [0-4]"),
      /* Translators: integer is to be translated - it expresses
      what the user needs to pass to this option, an integer number. */
            _("integer") },
      { "suite", 0, 0, G_OPTION_ARG_STRING, &user_suite, 
            _("Specify the apt suite name [default unstable]"), _("codename") },
      { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, _("Show version information "
            "and exit."), 0},
      { NULL }
};

/** \brief debug handling

 @bug TDebs are not architecture-independent anymore.
 
In theory, apt should not care about the architecture because the translations
are Architecture: all. In practice, it appears that apt is not expecting an 
entire repository of architecture-independent packages. The langupdate cache
needs to force apt to look for architecture-dependent packages or the
langupdate cache is likely to be empty.

*/

00124 #define APTARCHSTR " -o APT::Architecture="

/** \brief Configuration string for apt

Provides the main mechanism to separate the tdeb cache from the apt cache.

 \note options that specify the suite are deliberately omitted.

*/
00133 #define APTCONFIGSTR "-o APT::Get::List-Cleanup=off " \
 "-o Apt::Install-Recommends=false " \
 "-o Dir=/var/lib/"PACKAGE" " \
 "-o Dir::Etc=/var/lib/"PACKAGE" " \
 "-o Dir::Etc::SourceList=sources.list " \
 "-o Dir::State::Status=/var/lib/"PACKAGE"/status "

/** @brief Prepare a config string for apt */
gchar * 
00142 lu_get_aptstring (void)
{
      return g_strconcat (APTARCHSTR, HOST_CPU, " ", APTCONFIGSTR, "-o Dir::State=",
            suite_codename, " -o Dir::Cache=", suite_codename, NULL);
}

/** @brief install each available package name

 \todo run all install operations in one call - need
 to create it here and pass back.
*/
static void
00154 output (gpointer key, gpointer data)
{
      gchar * name, * hdl, * hdl_err, * config_str, * cmd, * func;
      gint err_status = 0;
      GError * gerr = NULL;

      c++;
      g_print ("%s: %s %s\n", PACKAGE, _("installing:"), (gchar*)key);
      name = NULL;
      config_str = lu_get_aptstring();
      if (dry_run)
      {
            cmd = g_strdup("apt-cache");
            func = g_strdup("show");
      }
      else
      {
            cmd = g_strdup ("apt-get");
            func = g_strdup("install -y");
      }
      if ((key) && (strcmp ((gchar*)key, "")))
            name = g_strdup_printf ( "%s %s %s %s", cmd, config_str, func, (gchar*)key);
      g_free (config_str);
      if (verbose >= 4)
            g_print ("%s: %s %s\n", PACKAGE, _("running apt command: "), name);
      g_spawn_command_line_sync (name, &hdl, &hdl_err, &err_status, &gerr);
      g_free (name);
      g_free (cmd);
      g_free (func);
      if (gerr) 
      {
            /// \todo rationalise error output.
            fprintf (stdout, _("apt returned an error:%s\n"), hdl_err);
            fprintf (stdout, _("status code:%d\n"), err_status);
            fprintf (stdout, "%s\n", gerr->message);
            fprintf (stdout, "%s\n", hdl);
            g_clear_error (&gerr);
            g_free (hdl_err);
            return;
      }
      if (dry_run)
      {
            fprintf (stdout, "%s\n", hdl);
            g_free (hdl);
      }
}

/** @brief simple string comparison routine for a GList */
static gint
00203 lu_check_pkg_name (gconstpointer a, gconstpointer b)
{
      gchar * test, * avail;
      test =  (gchar*)a;
      avail = (gchar*)b;
      return g_strcmp0 (test, avail);
}

/** \brief Parses list of all installed packages

Store a list of existing target packages
by skipping those already installed.

@param name full package name from dpkg status
@param src  source package for name, from the apt cache
@param locale the locale the TDeb needs to supply.

*/
static gchar *
00222 lu_parse_installed (const gchar* name, const gchar * src, const gchar * locale)
{
      GList * match;
      gchar *target, * lang;

      if (!g_strcmp0 (locale, ""))
            return NULL;
      match = NULL;
      /* generate the full locale-lang suffix */
      lang = g_strconcat ("-", LOCALE_SUFFIX, "-", locale, NULL);
      target = g_strconcat (src, lang, NULL);
      if ((g_str_has_suffix (name, lang)) or 
            (!g_strcmp0(target, name)))
      {
            /* pkg is already a suitable locale package, skip it. */
            installed = g_list_prepend (installed, target);
            /* in case the target occurs later in the list */
            targets = g_list_remove (targets, target);
            return NULL;
      }
      match = g_list_find_custom (installed, target, lu_check_pkg_name);
      if (g_list_length (match) != 0)
            targets = g_list_prepend (targets, target);
      g_free (lang);
      return target;
}

static gboolean
start_sources_list (void)
{
      gchar * lines;
      GError * err = NULL;

      lines = g_strconcat ("deb http://www.emdebian.org/grip/ ",
                   suite_codename, " main\n", 
                  "deb-src http://www.emdebian.org/grip/ ",
                   suite_codename, " main\n", NULL);
      g_file_set_contents (sourceslist, lines, -1, &err);
      if (err)
      {
            g_print ("%s: %s", PACKAGE, err->message);
            return false;
      }
      if (verbose >= 2)
      {
            g_print ("%s: %s\n", PACKAGE, _("Adding temporary apt sources lines:"));
            g_print ("%s: %s %s %s\n", PACKAGE, "deb http://www.emdebian.org/grip/",
                         suite_codename, "main");
            g_print ("%s: %s %s %s\n", PACKAGE, "deb-src http://www.emdebian.org/grip/",
                         suite_codename, "main");
      }
      return true;
}

/** \brief write the sources list 
 
Need to correlate the binaries from dpkg_list with the
source packages but that means having the system Cache, not the locale
one. What we need from this is to pass in libglib2.0-0 and
get back glib2.0 (which is why apt-cache showsrc worked).
So added the main sources to the langupdate sources list.

*/
static gboolean
00286 append_sourceslist (const gchar * lang)
{
      FILE * fout = NULL;

      fout = fopen (sourceslist, "a");
      if (!fout)
      {
            fout = fopen (sourceslist, "w+");
      }
      if (!fout)
      {
            g_warning (_("failed to create sources list: '%s'"), sourceslist);
            return false;
      }
      if ((mirror) && (!g_str_has_prefix(mirror, "http://www.emdebian.org/locale"))
                  && (!g_str_has_prefix(mirror, "http://buildd.emdebian.org/locale")))
      {
            g_print ("%s: %s %s\n%s: %s %s\n", PACKAGE, _("Default Emdebian mirror:"),
                  "http://www.emdebian.org/locale/", PACKAGE,
                  _("Using specified mirror: "), mirror);
            fprintf (fout, "deb %s %s %s\n", mirror, suite_codename, lang);
            fprintf (fout, "deb-src %s %s %s\n", mirror, suite_codename, lang);
            if (verbose >= 3)
            {
                  g_print ("%s: %s\n", PACKAGE, _("Adding temporary apt sources lines:"));
                  g_print ("%s: %s %s %s %s\n", PACKAGE,
                        "deb", mirror, suite_codename, lang);
                  g_print ("%s: %s %s %s %s \n", PACKAGE,
                        "deb-src", mirror, suite_codename, lang);
            }
      }
      else
      {
            if (verbose >= 2)
            {
                  g_print ("%s: %s %s\n", PACKAGE, _("Using default Emdebian mirror:"),
                        "http://www.emdebian.org/locale/");
            }
            fprintf (fout, "deb http://www.emdebian.org/locale/ %s %s\n",
                  suite_codename, lang);
            fprintf (fout, "deb-src http://www.emdebian.org/locale/ %s %s\n",
                  suite_codename, lang);
            if (verbose >= 3)
            {
                  g_print ("%s: %s\n", PACKAGE, _("Adding temporary apt sources lines:"));
                  g_print ("%s: %s %s %s\n", PACKAGE, "deb http://www.emdebian.org/locale/",
                        suite_codename, lang);
                  g_print ("%s: %s %s %s\n", PACKAGE, "deb-src http://www.emdebian.org/locale/",
                        suite_codename, lang);
            }
      }
      fclose (fout);
      return true;
}

/** \brief checks the user specified suite against the supported list

Sets the user specified suite if supported, otherwise sets the
Emdebian default (unstable). If the user also specifies a
particular mirror, allow whatever value the user specifies for the
suite.

*/
static void
00350 lu_check_supported_suites(gchar * user_suite)
{
      gchar * allowed, * match;

      /* if user specifies a mirror, all bets are off. */
      if (mirror)
            return;
      match = NULL;
      /* Translators: used with the --suite option to confirm
      the suite codename entered by the user. */
      g_print ("%s: %s%s\n", PACKAGE, _("checking Emdebian support for: "), user_suite);
      allowed = g_strdup("unstable sid testing lenny stable squeeze experimental");
      match = g_strrstr_len (allowed, strlen(allowed), user_suite);
      if (match)
            suite_codename = g_strdup(user_suite);
      else
      {
            /* Translators - the %s strings are the same value. */
            g_warning (_("Suite '%s' is not supported by Emdebian.\n"
                  "Use the mirror option to specify a repository that can\n"
                  "provide the '%s' suite"), user_suite, user_suite);
            g_print ("\n%s: %s", PACKAGE, _("Using the default Emdebian suite: unstable.\n"));
            suite_codename = g_strdup("unstable");
      }
      g_free (user_suite);
      g_free (allowed);
}

static void
remove_cache (const gchar * file, const gchar * user_suite)
{
      gchar * path;

      g_return_if_fail (file);
      g_return_if_fail (user_suite);
      path = g_strconcat ("/var/lib/", PACKAGE, "/", user_suite, "/", file, NULL);
      g_unlink (path);
      g_free (path);
}

static void
00391 run_apt_clean (const gchar * user_suite)
{
      gchar * config_str, * hdl, * hdl_err, * name;
      GError * gerr = NULL;
      gint err_status = 0;

      g_return_if_fail (user_suite);
      config_str = lu_get_aptstring();
      name = g_strdup_printf ( "%s %s %s", "apt-get", config_str, "clean");
      g_spawn_command_line_sync (name, &hdl, &hdl_err, &err_status, &gerr);
      if (gerr) 
      {
            /// \todo rationalise error output.
            g_printerr (_("apt returned an error:%s\n"), hdl_err);
            g_printerr (_("status code:%d\n"), err_status);
            g_printerr ("%s\n", gerr->message);
            g_clear_error (&gerr);
            g_free (hdl_err);
            return;
      }
}

static void
remove_lists (gchar * suite)
{
      gchar * path, *file;
      const gchar * list;
      GDir * binlists;

      g_return_if_fail (suite);
      path = g_strconcat ("/var/lib/",PACKAGE,"/", suite, "/lists/", NULL);
      binlists = g_dir_open (path, 0, NULL);
      if (binlists)
      {
            while ((list = g_dir_read_name (binlists)))
            {
                  file = g_strconcat (path, list, NULL);
                  /* the partial directory would fail anyway, so skip */
                  if (g_strcmp0(list, "partial"))
                        g_unlink (file);
                  g_free (file);
            }
            g_dir_close (binlists);
      }
      g_free (path);
}

/** \brief strip extra components from a language variable */
static gboolean
00440 lu_strip_components (gchar * line, gchar ** code, gchar ** root)
{
      gchar * list;
      gchar ** tok;
      GError * gerr;
      GRegex * patt;

      g_return_val_if_fail (line, FALSE);
      gerr = NULL;
      list = g_ascii_strdown (line, -1);
      tok = g_strsplit (list, ".", -1);
      g_free (list);
      list = g_strdup (tok[0]);
      g_strfreev (tok);
      tok = g_strsplit (list, " ", -1);
      g_free (list);
      list = g_strdup (tok[0]);
      g_strfreev (tok);
      patt = g_regex_new ("_", G_REGEX_OPTIMIZE, GREX_EMPTY, &gerr);
      if (gerr)
      {
            g_warning (gerr->message);
            g_clear_error (&gerr);
            return FALSE;
      }
      gerr = NULL;
      list = g_regex_replace (patt, list, -1, 0, "-", GREX_EMPTY, &gerr);
      if (gerr)
      {
            g_warning (gerr->message);
            g_clear_error (&gerr);
            return FALSE;
      }
      tok = g_strsplit (list, "-", -1);
      *root = g_strdup(tok[0]);
      g_strfreev (tok);
      *code = g_strdup (list);
      g_free (list);
      return TRUE;
}

static void
lu_purge_orphans (gpointer key, gpointer value, gpointer data)
{
      gchar * names;

      names = g_strconcat (purge_names, " ", (gchar*)key, NULL);
      g_free (purge_names);
      purge_names = g_strdup(names);
      g_free (names);
}

static gboolean
lu_skip_not_ours (gpointer key, gpointer value, gpointer data)
{
      gchar * match, * prefix;

      match = g_strdup ((gchar*)data);
      match = g_strstrip (match);
      prefix = g_strconcat (match, "-", LOCALE_SUFFIX, "-", NULL);
      if (g_str_has_prefix((gchar*)key, prefix))
      {
            if (choices)
            {
                  if (verbose >= 1)
                  {
                        g_print ("%s: %s '%s'\n", PACKAGE,
                              _("Skipping Debian package"), (gchar*)data);
                  }
                  g_free (match);
                  g_free (prefix);
                  return TRUE;
            }
      }
/*    else
      {
            if (verbose >= 2)
            {
                  g_print ("%s: %s: %s (%s)\n", PACKAGE,
                               _("Not an Emdebian package"), match, version);
                  g_print ("%s: %s: %s\n", PACKAGE,
                               _("Not an Emdebian package"), match);
            }
      }*/
      g_free (match);
      g_free (prefix);
      return FALSE;
}

static gchar *
lu_parse_control (const gchar * control_line)
{
      gchar * name;
      gchar ** pkg_name;

      if (!control_line)
            return NULL;
      /* need a copy so that g_strstrip can work in place */
      name = g_strdup (control_line);
      name = g_strstrip (name);
      pkg_name = g_strsplit (name, " ", -1);
      if (g_strv_length(pkg_name) < 2)
            return NULL;
      g_free (name);
      name = g_strdup (pkg_name[1]);
      if (!name)
            return NULL;
      name = g_strstrip (name);
      return name;
}

static void
lu_parse_dpkg (gpointer key, gpointer value, gpointer data)
{
      gboolean exists;
      gchar * pkg, * version;
      GList * done = NULL;

      pkg = g_strdup((gchar*)key);
      version = (gchar*)value;
      if (!pkg)
            return;
      pkg = g_strstrip (pkg);
      exists = aptcache_lookup (pkg);
      if (!exists)
            return;

      /* if the package was removed, don't bother checking 
      the installed status */
      done = g_list_find_custom (purged, pkg, lu_check_pkg_name);
      if ((done) and (!purge_mode))
            return;

      if (g_strcmp0(version, NULL))
      {
            g_hash_table_insert (choices, pkg, pkg);
            return;
      }

      done = g_list_find_custom (installed, pkg, lu_check_pkg_name);
      if (!done)
      {
            g_hash_table_insert (choices, pkg, pkg);
            return;
      }

      if (g_strcmp0(version, NULL))
      {
            g_hash_table_insert (choices, pkg, pkg);
            return;
      }

      if (lu_check_pkg_version (pkg, version) != 0)
      {
            g_hash_table_insert (choices, pkg, pkg);
            return;
      }
      if (verbose >= 1)
            g_print ("%s: %s %s (%s)\n", PACKAGE, 
                  _("TDeb is already installed."), pkg, version);
}

/** \brief Read the apt-cache to know what is available.
 then compare dpkg_list with apt_list
*/
gint
00606 main (gint argc, gchar *argv[])
{
      GError * em_gerr;
      gchar * dpkg, *code, *lgen, * log, * root, * name, *src, *version, *pkg;
      const gchar * const * locale_v;
      GList * dpkg_list, * p, *locales;
      GHashTable * locale_table, * dpkg_table;
      gchar ** status_list, ** gen_list;
      guint mcount, /*c,*/ l;
      gboolean pkg_changed;
      GOptionContext *context;

      /** \todo put the variables into a context struct for easier
       initialisation. */
      code = root = src = version = pkg = NULL;
      em_gerr = NULL;
      locales = dpkg_list = NULL;
      orphans = g_hash_table_new (g_str_hash, g_str_equal);
      locale_table = g_hash_table_new (g_str_hash, g_str_equal);
      dpkg_table = g_hash_table_new (g_str_hash, g_str_equal);
      choices = g_hash_table_new (g_str_hash, g_str_equal);
      l = mcount = 0;
#ifdef ENABLE_NLS
      setlocale (LC_ALL, "");
      bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
      bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
      textdomain (GETTEXT_PACKAGE);
#endif
      context = g_option_context_new (_("- updates language support for Emdebian"));
      g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
      g_option_context_set_summary (context,
            _("Apt frontend to match the Emdebian TDeb support with the installed\n"
            "packages and supported locales."));
      g_option_context_set_description (context, 
            /* Translators: the line endings need to be in roughly the same place in
            your own language, counting by characters and allowing for all breaks to
            be between words. */
            _("Emdebian TDebs provide a method for splitting all translation files out of\n"
            "packages and into separate TDeb packages, organised by the locale root and\n"
            "source package. This allows individual users to only install translations\n"
            "files for the locales supported on their own machine and only for the\n"
            "packages installed at the time.\n\n"
            "Note that this functionality is not available for Debian TDebs.\n"));
      g_option_context_parse (context, &argc, &argv, &em_gerr);
      if (show_version)
      {
            g_print ("%s (%s)\n", PACKAGE, VERSION);
            g_print ("%s", GNU_WARRANTY);
            return (0);
      }
      if (!user_suite)
            suite_codename = g_strdup("unstable");
      else
            lu_check_supported_suites(user_suite);
      if (dry_run && !purge_mode)
            g_print ("%s: %s\n", PACKAGE,
                  _("Dry run, just showing cache data for installable packages."));
      if (!start_sources_list())
            return (1);
      // parse /etc/locale.gen here
      if (!g_file_get_contents (LOCALEGEN, &lgen, NULL, &em_gerr))
      {
            g_critical (em_gerr->message);
            g_message (_("Unable to open the locale.gen configuration file."));
            g_clear_error (&em_gerr);
            return -1;
      }
      gen_list = g_strsplit (lgen, "\n", -1);
      for (mcount = 0; mcount <= g_strv_length(gen_list); mcount++)
      {
            gchar * line;

            if (!gen_list[mcount])
                  continue;
            if (g_str_has_prefix (gen_list[mcount], "#"))
                  continue;
            if (!g_strcmp0 ("", gen_list[mcount]))
                  continue;
            line = g_strdup (gen_list[mcount]);
            if (!lu_strip_components (line, &code, &root))
                  continue;
            if (!code || !root)
                  continue;
            g_free (line);
            if (!g_hash_table_lookup(locale_table, root))
            {
                  if (verbose >= 1)
                        g_print ("%s: %s %s\n", PACKAGE, 
                              _("/etc/locale.gen requires adding support for:"), code);
                  if (!append_sourceslist (root)) 
                        return -1;
                  g_hash_table_insert (locale_table, root, code);
                  if (verbose >= 1)
                        g_print ("%s: %s %s\n", PACKAGE, 
                              _("Adding support for:"), root);
            }
      }
      g_strfreev (gen_list);
      locale_v = g_get_language_names ();
      l = 0;
      while (g_strcmp0 (locale_v[l], "C") != 0)
      {
            gchar * line;

            line = g_strdup (locale_v[l]);
            code = root = NULL;
            if (!lu_strip_components (line, &code, &root))
                  continue;
            if (!code || !root)
                  continue;
            g_free (line);
            if (!g_hash_table_lookup(locale_table, root))
            {
                  if (verbose >= 1)
                        g_print ("%s: %s %s\n", PACKAGE, 
                              _("Environment needs support for:"), code);
                  if (!append_sourceslist (root)) 
                        return (-1);
                  if (verbose >= 1)
                        g_print ("%s: %s %s\n", PACKAGE, 
                              _("Adding support for:"), root);
                  g_hash_table_insert (locale_table, root, code);
            }
            else
            {
                  // don't free entries in a GHashTable
//                g_free (code);
                  g_free (root);
                  code = NULL;
                  root = NULL;
            }
            l++;
      }
      g_list_free (locales);
      locales = g_hash_table_get_values (locale_table);
      locales = g_list_concat (locales, g_hash_table_get_keys (locale_table));

      /* end of the locale identification code */

      g_return_val_if_fail (apt_init (sourceslist, suite_codename, verbose), -1);
      if (!g_file_get_contents (DPKG_FILE, &dpkg, NULL, &em_gerr))
      {
            g_critical (em_gerr->message);
            g_message (_("Unable to open the dpkg status file."));
            g_clear_error (&em_gerr);
            return -1;
      }
      status_list = g_strsplit (dpkg, "\n", -1);
      pkg_changed = FALSE;
      name = NULL;
      version = NULL;
      for (mcount = 0; mcount <= g_strv_length(status_list); mcount++)
      {
            if (!status_list[mcount])
                  continue;
            /* detect the empty line between package stanzas */
            if (!g_strcmp0 (status_list[mcount], ""))
                  pkg_changed = TRUE;
            else
                  pkg_changed = FALSE;
            /* get the status of each package in the dpkg list */

            if (g_str_has_prefix (status_list[mcount], PACKAGE_CHK))
                  name = lu_parse_control (status_list[mcount]);

            if (status_list[mcount+1] and purge_mode and 
                  g_str_has_prefix (status_list[mcount+1], STATUS_CHK))
            {
                  if (g_str_has_suffix (status_list[mcount+1], "not-installed"))
                  {
                        purged = g_list_prepend (purged, name);
                        continue;
                  }
            }
            if (g_str_has_prefix (status_list[mcount], "Version: "))
            {
                  gchar * em_chunk;
                  version = lu_parse_control (status_list[mcount]);
                  if (pkg)
                        g_hash_table_replace (dpkg_table, pkg, version);
                  /* -1 to allow for em1, em2, em3 etc. */
                  em_chunk = g_strndup (version, strlen (version) -1);
                  if ((!g_str_has_suffix (em_chunk, "em")) and (src))
                  {
                        gchar * prefix;
                        // need to add to a list that can be subtracted later.
                        // but existing TDebs also show up here due to the lack
                        // of an em1 version suffix.
                        /** \bug assuming here that no src means not Debian
                        which works for -cross packages but is still not reliable.
                        End up with this message appearing for TDebs inside Grip.
                        */
                        prefix = g_strconcat (src, "-", LOCALE_SUFFIX, "-", NULL);
                        if ((!g_str_has_prefix(name, prefix)) and
                              (verbose >= 2))
                        {
                              g_print ("%s: %s: %s (%s)\n", PACKAGE,
                                    _("Not an Emdebian package"), src, version);
                        }
                        if (!purge_mode)
                              installed = g_list_append (installed, src);
                        not_ours = g_list_prepend (not_ours, g_strdup(src));
                        g_free (em_chunk);
                        continue;
                  }
            }
            if ((!pkg_changed) and 
                  (g_str_has_prefix (status_list[mcount], PACKAGE_CHK)))
            {
                  gchar * orphaned, * l;
                  orphaned = l = NULL;
                  l = g_strconcat ("-", LOCALE_SUFFIX, "-", NULL);
                  orphaned = g_strrstr_len (name, strlen(name), l);
                  if (orphaned)
                        g_hash_table_insert (orphans, g_strdup(name), g_strdup(src));
                  orphaned = NULL;
                  g_free (l);
                  src = lu_get_sourcepkg (name);
                  for (p = locales; p != NULL; p = p->next)
                  {
                        gchar * code;
                        code = g_strdup ((gchar*)p->data);
                        if (!code)
                              continue;
                        code = g_strstrip (code);
                        if (src)
                              pkg = lu_parse_installed (name, src, code);
                        if (pkg)
                        {
                              gchar * replace;
                              replace = (gchar*)g_hash_table_lookup(dpkg_table, pkg);
                              if (replace or !g_strcmp0 ("",replace))
                              {
                                    g_hash_table_replace (dpkg_table, pkg, g_strdup(""));
                              }
                              else
                              {
                                    g_hash_table_insert (dpkg_table, pkg, g_strdup(""));
                              }
                              dpkg_list = g_list_prepend (dpkg_list, pkg);
                        }
                        g_free (code);
                  }
            }
      }
      g_strfreev (status_list);

      /* finished with the status list */

      if (verbose >= 3)
      {
            GList * dpkg_keys = NULL;
            GList * p = NULL;
            g_print ("%s choices: %d\n", "choices complete", g_hash_table_size(choices));
            g_print ("dpkg_table has %d entries.\n", g_hash_table_size(dpkg_table));
            dpkg_keys = g_hash_table_get_keys (dpkg_table);
            if (verbose >= 4)
            {
                  for (p=g_list_sort(dpkg_keys, lu_check_pkg_name); p!= NULL; p = p->next)
                  {
                        g_print ("dkg_table contains: %s\n", (gchar*)p->data);
                  }
            }
      }

      g_hash_table_foreach (dpkg_table, lu_parse_dpkg, NULL);

      for (p = not_ours; p != NULL; p = p->next)
      {
            g_hash_table_foreach_remove (choices, lu_skip_not_ours, p->data);
      }
      if (verbose >= 3)
      {
            g_print ("dpkg_table has %d entries.\n", g_hash_table_size(dpkg_table));
            g_print ("installed has %d entries.\n", g_list_length(installed));
            g_print ("not_ours has %d entries.\n", g_list_length(not_ours));
            g_print ("targets has %d entries.\n", g_list_length(targets));
            g_print ("choices has %d entries.\n", g_hash_table_size(choices));
      }
      c = g_hash_table_size (choices);
      if (!purge_mode)
      {
            GList * sorted = NULL;

            if (c == 0)
                  g_print ("%s: %s\n", PACKAGE, _("Nothing to do."));
            else
            {
                  g_print ("%s: ", PACKAGE);
                  g_print (ngettext("%d package to be installed\n",
                        "%d packages to be installed\n", c), c);
                  sorted = g_list_sort (g_hash_table_get_keys (choices), lu_check_pkg_name);
                  g_list_foreach (sorted, output, NULL);
            }
      }
      g_hash_table_destroy (locale_table);
      g_hash_table_destroy (dpkg_table);
      g_hash_table_destroy (choices);
      remove_cache ("lists/lock", suite_codename);
      remove_cache ("lock", suite_codename);
      if (!debug)
      {
            run_apt_clean (suite_codename);
            remove_cache ("srcpkgcache.bin", suite_codename);
            remove_cache ("pkgcache.bin", suite_codename);
            remove_lists (suite_codename);
            g_unlink (sourceslist);
      }
      log = g_strconcat ("/var/lib/"PACKAGE"/var/log/apt/term.log", NULL);
      g_unlink (log);
      g_free (log);
      g_list_free (locales);
      if (purge_mode)
      {
            gchar * command;

            /* to convert to a "remove all TDebs" option, whether the
             package is installed or not, switch these
             from g_hash_table_remove to g_hash_table_insert */
            for (p = targets; p != NULL; p = p->next)
            {
                  g_hash_table_remove (orphans, (gchar*)p->data);
            }
            for (p = installed; p != NULL; p = p->next)
            {
                  g_hash_table_remove (orphans, (gchar*)p->data);
            }
            purge_names = g_strdup("");
            g_hash_table_foreach (orphans, lu_purge_orphans, NULL);
            if (g_strcmp0(purge_names, ""))
            {
                  command = g_strconcat ("apt-get -y --purge remove ", purge_names, NULL);
                  if (dry_run)
                        g_print ("%s: %s '%s'\n", PACKAGE, _("Dry-run only."), command);
                  else
                        g_spawn_command_line_sync (command, NULL, NULL, NULL, &em_gerr);
                  if (em_gerr)
                  {
                        g_critical (em_gerr->message);
                        g_message (_("Unable to execute command: %s."), command);
                        g_clear_error (&em_gerr);
                  }
                  g_free (command);
            }
            else
                  g_print ("%s: %s\n", PACKAGE, _("No TDeb packages to remove."));
      }
      g_print ("\n");
      g_free (suite_codename);
      if (!debug)
            lu_clear_caches ();
      return 0;
}

/** @} */

Generated by  Doxygen 1.6.0   Back to index