Logo Search packages:      
Sourcecode: langupdate version File versions  Download package

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 STATUS_CHK   "Status"

/** @todo put the static variables into a context struct? */
00048 static guint c = 0;
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.

*/
00072 static GList * not_ours = NULL;
static GHashTable * orphans = NULL;
static gchar * suite_codename = NULL;
static gchar * user_suite = NULL;
static gchar * mirror = NULL;
static gchar * prefix = NULL;
static gint verbose = 0;
static gboolean dry_run = false;
static gboolean unpack_mode = 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 = true;
static gchar * user_arch = NULL;
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") },
      { "prefix", 'p', G_OPTION_FLAG_FILENAME, G_OPTION_ARG_FILENAME, &prefix,
            _("Operate inside a sub directory."), _("directory") },
      { "arch", 'a', 0, G_OPTION_ARG_STRING, &user_arch,
            _("Override query architecture - requires -p|--prefix"),
            _("architecture") },
      { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, 
            _("Show version information and exit."), 0},
      { NULL }
};

static gchar *
get_locale_gen (gchar * prefix)
{
      return g_build_filename (prefix, G_DIR_SEPARATOR_S,
                  "etc", "locale.gen", NULL);
}

static gchar *
get_sources_list (gchar * prefix)
{
      return g_build_filename (prefix, G_DIR_SEPARATOR_S,
                  "var", "lib", PACKAGE, "sources.list", NULL);
}

static gchar *
get_dpkg_file (gchar * prefix)
{
      return g_build_filename (prefix, G_DIR_SEPARATOR_S,
                  "var", "lib", "dpkg", "status", NULL);
}

/** \brief Configuration string for apt

Provides the main mechanism to separate the tdeb cache from 
 the apt cache with support for the prefix.

*/
gchar * 
00146 lu_get_aptstring (void)
{
      gchar * arch, * ret, * download;

      if (g_strcmp0(prefix, ""))
      {
            // prefix in use, other options become available.
            // if user_arch is set, set arch and set unpack mode.
            arch = (user_arch) ? g_strdup(user_arch) : g_strdup(HOST_CPU);
            // if prefix is set, set download
            download = g_strdup (" -o APT::Get::Download-Only=true ");
            unpack_mode = true;
      }
      else
            download = g_strdup ("");
      arch = (user_arch and (g_strcmp0(prefix, "") != 0)) ? 
            g_strdup(user_arch) : g_strdup(HOST_CPU);
      ret = g_strconcat (" -o APT::Architecture=", arch, download,
            " -o APT::Get::List-Cleanup=off -o Apt::Install-Recommends=false "
            "-o Dir=", prefix, G_DIR_SEPARATOR_S, "/var/lib/"PACKAGE" ",
            "-o Dir::Etc=", prefix, G_DIR_SEPARATOR_S, "/var/lib/"PACKAGE" ",
            "-o Dir::Etc::SourceList=sources.list ",
            "-o Dir::State::Status=", prefix, G_DIR_SEPARATOR_S, 
            "/var/lib/"PACKAGE"/status ", "-o Dir::State=", suite_codename,
            " -o Dir::Cache=", suite_codename, NULL);
      g_free (arch);
      g_free (download);
      return ret;
}

/** @brief install each available package name

 \todo run all install operations in one call - need
 to create it here and pass back.
*/
static void
00182 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) and (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_print (hdl);
      g_free (name);
      g_free (cmd);
      g_free (func);
      if (gerr)
      {
            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);
            return;
      }
      if (unpack_mode)
      {
            GDir * tdebs;
            gchar * dir, * arch, *path, * system;
            const gchar * filename;
            gint i;

            arch = (user_arch) ? g_strdup(user_arch) : g_strdup(HOST_CPU);
            /* TDebs have no maintainer scripts, just use dpkg -x dir/
             This puts the files in the right place but doesn't alter
             dpkg data. (i.e. the TDeb does not get configured).
            */
            dir = g_build_filename (prefix, G_DIR_SEPARATOR_S, "var","lib",
                        PACKAGE, suite_codename, "archives", NULL);
            tdebs = g_dir_open (dir, 0, &gerr);
            if (gerr) 
            {
                  g_critical ("%s: %s", PACKAGE,_("Failed to open directory"));
                  g_clear_error (&gerr);
                  return;
            }
            filename = g_dir_read_name (tdebs);
            path = g_build_filename (dir, filename, NULL);
            /* find the right TDeb */
            while (filename and (!g_str_has_prefix(filename, (gchar*)key)))
            {
                  g_free (path);
                  filename = g_dir_read_name (tdebs);
                  path = g_build_filename (dir, filename, NULL);
            }
            if (verbose >= 2)
                  g_print (_("%s: Extracting TDeb '%s' into '%s'\n"), 
                  PACKAGE, filename, prefix);
            name = g_strdup_printf ("dpkg-deb --extract %s %s", path, prefix);
            g_spawn_command_line_sync (name, &hdl, &hdl_err, &err_status, &gerr);
            g_print (hdl);
            if (gerr)
            {
                  fprintf (stdout, _("dpkg-deb 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);
            }
            system = g_build_filename (prefix, 
                              "var", "cache", "apt", "archives", filename, NULL);
            g_print ("%s: %s: '%s'", PACKAGE, 
                        _("Copying unpacked TDeb to apt cache"), system);
            i = g_rename (path, system);
            if (i)
            {
                  g_print ("%s: %s", PACKAGE, 
                        _("Unable to move the unpacked TDeb, deleting instead."));
                  g_unlink (path);
            }
            g_dir_close (tdebs);
            g_free (dir);
            g_free (name);
            g_free (path);
      }
}

/** @brief simple string comparison routine for a GList */
static gint
00293 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 *
00312 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, *srclist;
      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);
      srclist = get_sources_list (prefix);
      g_file_set_contents (srclist, lines, -1, &err);
      if (err)
      {
            g_print ("%s: %s\n", 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");
      }
      g_free (srclist);
      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
00378 append_sourceslist (const gchar * lang)
{
      gchar * srclist;
      FILE * fout = NULL;

      srclist = get_sources_list (prefix);
      fout = fopen (srclist, "a");
      if (!fout)
      {
            fout = fopen (srclist, "w+");
      }
      if (!fout)
      {
            g_warning ("%s: %s: '%s'", PACKAGE, 
                  _("Failed to create sources list"), srclist);
            return false;
      }
      if ((mirror) and (!g_str_has_prefix(mirror, "http://www.emdebian.org/locale"))
                  and (!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);
      g_free (srclist);
      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
00446 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_build_filename (prefix, G_DIR_SEPARATOR_S, "var", "lib",
                        PACKAGE, user_suite, file, NULL);
      g_unlink (path);
      g_free (path);
}

static void
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);
      g_print (hdl);
      if (gerr) 
      {
            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_build_filename (prefix, G_DIR_SEPARATOR_S, "var", "lib", PACKAGE,
                  suite, "lists", NULL);
      binlists = g_dir_open (path, 0, NULL);
      if (binlists)
      {
            while ((list = g_dir_read_name (binlists)))
            {
                  file = g_build_filename (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);
      /* now remove any lists in the partial directory */
      path = g_build_filename (prefix, G_DIR_SEPARATOR_S, "var", "lib", PACKAGE,
                  suite, "lists", "partial", NULL);
      binlists = g_dir_open (path, 0, NULL);
      if (binlists)
      {
            while ((list = g_dir_read_name (binlists)))
            {
                  file = g_build_filename (path, list, NULL);
                  g_unlink (file);
                  g_free (file);
            }
            g_dir_close (binlists);
      }
      g_free (path);
}

/** \brief strip extra components from a language variable */
static gboolean
00553 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, * pkg_prefix;

      match = g_strdup ((gchar*)data);
      match = g_strstrip (match);
      pkg_prefix = g_strconcat (match, "-", LOCALE_SUFFIX, "-", NULL);
      if (g_str_has_prefix((gchar*)key, pkg_prefix))
      {
            if (choices)
            {
                  if (verbose >= 1)
                  {
                        g_print ("%s: %s '%s'\n", PACKAGE,
                              _("Skipping Debian package"), (gchar*)data);
                  }
                  g_free (match);
                  g_free (pkg_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 (pkg_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
00719 main (gint argc, gchar *argv[])
{
      GError * em_gerr;
      gchar * dpkg, *code, *lgen, * log, * root, * name, *src, *version, *pkg,
       *gen, *srclist, *dpkgfile;
      const gchar * const * locale_v;
      GList * dpkg_list, * p, *locales;
      GHashTable * locale_table, * dpkg_table;
      gchar ** status_list, ** gen_list;
      guint mcount, 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 (!prefix)
            prefix = g_strdup("");
      else
      {
            gchar * ours;
            ours = g_build_filename (prefix, G_DIR_SEPARATOR_S, "var/lib/"PACKAGE"/", NULL);
            if (!g_file_test (ours, G_FILE_TEST_IS_DIR))
            {
                  gint i;
                  i = g_mkdir_with_parents (ours, 0755);
                  if (i != 0)
                  {
                        /* Translators: First string is the package name,
                         second and third are a user-specified directory. */
                        g_error (_("Unable to create %s data using prefix '%s'.\n"
                              "Do you have permission to write to '%s'?"),
                              PACKAGE, prefix, prefix);
                        return -1;
                  }
            }
      }
      if (dry_run and not 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
      gen = get_locale_gen (prefix);
      if (!g_file_get_contents (gen, &lgen, NULL, &em_gerr))
      {
            g_critical (em_gerr->message);
            g_message ("%s: %s '%s'", PACKAGE,
                  _("Unable to open the locale.gen configuration file."), gen);
            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 %s\n", PACKAGE, prefix,
                              _("/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 */

      srclist = get_sources_list (prefix);
      g_return_val_if_fail (apt_init (srclist, suite_codename, prefix, verbose), -1);
      dpkgfile = get_dpkg_file (prefix);
      if (!g_file_get_contents (dpkgfile, &dpkg, NULL, &em_gerr))
      {
            g_critical (em_gerr->message);
            g_message ("%s: %s '%s'", PACKAGE,
                  _("Unable to open the dpkg status file."), dpkgfile);
            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 * pkg_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.
                        */
                        pkg_prefix = g_strconcat (src, "-", LOCALE_SUFFIX, "-", NULL);
                        if ((!g_str_has_prefix(name, pkg_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);
                        g_free (pkg_prefix);
                        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)
      {
            g_print ("%s: %s: '%s'.\n", PACKAGE,
                  _("Cleaning up apt lists"), suite_codename);
            run_apt_clean (suite_codename);
            remove_cache ("srcpkgcache.bin", suite_codename);
            remove_cache ("pkgcache.bin", suite_codename);
            remove_lists (suite_codename);
            g_unlink (srclist);
      }
      log = g_build_filename (prefix, G_DIR_SEPARATOR_S, "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);
      g_free (srclist);
      if (!debug)
            lu_clear_caches ();
      return 0;
}

/** @} */

Generated by  Doxygen 1.6.0   Back to index