Logo Search packages:      
Sourcecode: deb-gview version File versions

dvarchive.c

/********************************************************************
 *            dvarchive.c
 *
 *  Tue Aug  1 00:50:52 2006
 *  Copyright  2006-2008  Neil Williams
 *  linux@codehelp.co.uk
 * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk>
 *******************************************************************/
/*
    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/>.
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
#include <ar.h>
#include <archive.h>
#include <archive_entry.h>
#include <zlib.h>
#include "dvpreview.h"
#include "callbacks.h"
#include "support.h"
#include "interface.h"
#include "dvarchive.h"

#define BLOCKSIZE 10240
#define MAXGZ 1024*512*sizeof(gint)

typedef enum
{
      DV_AR_FAIL = -1,
      DV_OK,
      DV_ERR_NO_FILE,
      DV_ERR_FILE_READ,
      DV_ERR_VERSION,
      DV_ERR_CORRUPT_FILE,
      DV_ERR_NOT_DPKG,
      DV_ERR_COMPRESS,
} DVError;

/* details of the files within the .deb */
typedef struct
{
      gint64 size;
      gpointer content;
      gboolean compressed;
      GtkWidget *parent;
}DVFileData;

struct DV_s
{
      const gchar *filename;
      gpointer readbuffer;
      gchar *copyright;
      GHashTable *control_files;
      GHashTable *data_files;
      struct archive *control_tar;
      struct archive *data_tar;
      struct ar_hdr *current;
      gulong skip;
      GFile * ghandle;
      GInputStream * stream;
      GtkWidget *parent;
      DVError error;
      gboolean store_set;
      GList * subwindows;
      const gchar * ar_message; /* error string from archive/ar_hdr */
      gchar * selected_file;
      gchar * current_path;
      /** indicates if the GFile is a URI or a path */
      gboolean needs_uri;
};

enum compression_type
{ CAT, GZ, BZ2 };

DVContents*
dv_create (void)
{
      DVContents *deb;

      deb = g_new0 (DVContents, 1);
      deb->control_files = g_hash_table_new (g_str_hash, g_str_equal);
      deb->data_files = g_hash_table_new (g_str_hash, g_str_equal);
      deb->subwindows = NULL;
      return deb;
}

void
dv_set_needs_uri (DVContents * deb, gboolean val)
{
      g_return_if_fail (deb);
      deb->needs_uri = val;
}

gboolean
dv_get_needs_uri (DVContents * deb)
{
      /* default value is FALSE and the toolbar callbacks expect FALSE */
      return deb->needs_uri;
}

static void
dv_new_window (GFile * deb)
{
      DVContents * context;
      GtkWindow *window1;
      gchar * filename;

      g_return_if_fail (deb);
      context = dv_create();
      window1 = GTK_WINDOW(create_deb_gview_window ());
      g_return_if_fail (window1);
      g_object_set_data (G_OBJECT(window1), DVCONTENTS, context);
      set_dv_parent (GTK_WIDGET(window1));
      context->ghandle = deb;
      filename = g_file_get_path (deb);
      /* set the working path for the new window from the .changes file */
      dv_set_working_path (context, filename);
      set_deb_file (deb, context);
      gtk_widget_show (GTK_WIDGET(window1));
      dv_add_child (window1);
}

void
dv_parse_changes (GFile * cfile, DVContents * deb)
{
      gchar **changes_file;
      gchar * debname, *debline, *contents, *chgpath, *path;
      guint c;
      GFile * parent;
      GError * result;
      gsize changes_size;
      gboolean files;

      g_return_if_fail (cfile);
      result = NULL;
      files = FALSE;
      g_file_load_contents (cfile, NULL, &contents, &changes_size, 
            NULL, &result);
      if (result)
      {
            g_warning ("Unable to parse .changes file: %s", result->message);
            g_clear_error (&result);
            return;
      }
      path = g_file_get_parse_name (cfile);
      chgpath = NULL;
      parent = g_file_get_parent (cfile);
      chgpath = g_file_get_uri (parent);
      changes_file = g_strsplit (contents, "\n", -1);
      for (c = 0; c < g_strv_length(changes_file); c++)
      {
            // handle new format .changes files
            if (g_str_has_prefix (changes_file[c], "Checksums"))
                  files = FALSE;
            if (g_str_has_prefix (changes_file[c], "Files"))
                  files = TRUE;
            if (!files)
                  continue;
            debline = NULL;
            debname = NULL;
            debline = g_strrstr (changes_file[c], ".deb");
            if (debline)
            {
                  GFile * debfile;
                  gchar * partial;

                  /* g_str is safe because this line comes from inside the
                  .changes file, not from a path or filename */
                  if (!g_str_has_suffix (debline, ".deb"))
                        continue;
                  debfile = NULL;
                  partial = g_strstrip (g_strrstr (changes_file[c], " "));
                  debname = g_strconcat(chgpath, "/", partial, NULL);
                  debfile = g_file_new_for_uri (debname);
                  if (g_str_has_suffix (debname, ".deb"))
                        dv_new_window (debfile);
            }
      }
}

void
dv_archive_clear (DVContents *deb)
{
      g_return_if_fail (deb); 
      if (deb->control_files)
      {
            g_hash_table_destroy (deb->control_files);
            deb->control_files = g_hash_table_new (g_str_hash, g_str_equal);
      }
      if (deb->data_files)
      {
            g_hash_table_destroy (deb->data_files);
            deb->data_files = g_hash_table_new (g_str_hash, g_str_equal);
      }
      if (deb->copyright)
            g_free (deb->copyright);
}

void
dv_archive_free (DVContents *deb)
{
      if (!deb)
            return;
      if (deb->control_files)
            g_hash_table_destroy (deb->control_files);
      if (deb->data_files)
            g_hash_table_destroy (deb->data_files);
      if (deb->copyright)
            g_free (deb->copyright);
      if (deb->ghandle)
            g_object_unref (deb->ghandle);
      g_free (deb);
      preview_shutdown ();
      deb = NULL;
}

GtkWidget *
dv_get_parent (DVContents *deb)
{
      g_return_val_if_fail (deb, NULL);
      return deb->parent;
}

void
set_dv_parent (GtkWidget * widget)
{
      DVContents *deb;

      g_return_if_fail (widget);
      deb = (DVContents*) lookup_widget (widget, DVCONTENTS);
      g_return_if_fail (deb);
      deb->parent = widget;
}

const gchar *
dv_get_selected_file (DVContents * deb)
{
      return deb->selected_file;
}

gchar *
dv_get_working_path (DVContents * deb)
{
      g_return_val_if_fail (deb, NULL);
      return deb->current_path;
}

void
dv_set_working_path (DVContents * deb, gchar * path)
{
      g_return_if_fail (deb);
      if (!path)
            return;
      g_free (deb->current_path);
      deb->current_path = g_strdup (path);
}

static void
dv_show_error (DVError dve, GtkWidget * widget)
{
      GtkDialog *dialog;
      GtkWidget *parent;
      gboolean fatal;
      gchar *fmt;
      DVContents *deb;

      g_return_if_fail (widget);
      fatal = FALSE;
      fmt = NULL;
      deb = (DVContents*) lookup_widget (widget, DVCONTENTS);
      parent = (!widget) ? deb->parent : widget;
      switch (dve)
      {
            case DV_OK:
                  return;
            case DV_ERR_NO_FILE:
            {
                  fmt = _("Unable to locate package file, "
                        "the file may be empty.");
                  break;
            }
            case DV_AR_FAIL :
            case DV_ERR_FILE_READ:
            {
                  fmt = _("Failed to read package file.");
                  break;
            }
            case DV_ERR_VERSION:
            {
                  fmt = _("Unsupported archive version.");
                  break;
            }
            case DV_ERR_CORRUPT_FILE :
            {
                  fmt = _("Possible corrupted file.");
                  break;
            }
            case DV_ERR_NOT_DPKG :
            {
                  fmt = _("Not a debian binary archive.");
                  break;
            }
            case DV_ERR_COMPRESS :
            {
                  fmt = _("Unable to decompress file within package.");
                  break;
            }
      }
      if (deb->ar_message)
            fmt = g_strconcat (fmt, "\n", deb->ar_message, NULL);
      dialog = GTK_DIALOG (gtk_message_dialog_new (GTK_WINDOW(parent),
                  GTK_DIALOG_DESTROY_WITH_PARENT,     GTK_MESSAGE_ERROR,
                  GTK_BUTTONS_CLOSE, _("Error loading file '%s'. %s"),
                  deb->filename, fmt));
      deb->ar_message = NULL;
      gtk_dialog_run (dialog);
      gtk_widget_destroy (GTK_WIDGET (dialog));
      if (fatal)
      {
            dv_archive_free (deb);
            gtk_main_quit ();
      }
}

void
set_deb_file (GFile * gfile, DVContents *deb)
{
      gchar * filename;
      g_return_if_fail (deb);
      if (!gfile)
      {
            dv_show_error (DV_ERR_NO_FILE, deb->parent);
            return;
      }
      if (!deb->ghandle)
            deb->ghandle = gfile;
      filename = g_file_get_uri (deb->ghandle);
      if ((!g_str_has_suffix(filename, ".deb")) &&
            (!g_str_has_suffix(filename, ".udeb")) &&
            (!g_str_has_suffix(filename, ".tdeb")))
            return;
      deb->filename = filename;
      deb->ghandle = gfile;
}

static gint
dv_archive_open (struct archive *a, gpointer client_data)
{
      DVContents *d = (DVContents*)client_data;
      gchar *size;

      g_return_val_if_fail (d, ARCHIVE_FATAL);
      g_return_val_if_fail (d->readbuffer == NULL, ARCHIVE_FATAL);
      /* deb->pkg already open */
      d->skip = 0;
      size = g_strndup (d->current->ar_size, 10);
      d->skip = g_ascii_strtod (size, NULL);
      g_free (size);
      if (d->skip == 0)
            return ARCHIVE_FATAL;
      d->readbuffer = g_strnfill (d->skip, ' ');
      if( d->readbuffer == NULL )
      {
            archive_set_error(a, ENOMEM, _("Out of memory."));
            d->ar_message = archive_error_string (a);
            return ARCHIVE_FATAL;
      }
      return ARCHIVE_OK;
}

static ssize_t
dv_archive_read (struct archive *a, gpointer client_data, gconstpointer *buffer)
{
      gsize bytesread;
      GError * result;

      DVContents *d = (DVContents*)client_data;
      g_return_val_if_fail (d, DV_AR_FAIL);
      result = NULL;
      *buffer = d->readbuffer;
      g_return_val_if_fail (d->readbuffer != NULL, DV_AR_FAIL);
      if (d->skip == 0)
            return ARCHIVE_OK;
      if (g_input_stream_is_closed (d->stream))
            return DV_AR_FAIL;
      bytesread = g_input_stream_read (d->stream, d->readbuffer, d->skip, NULL, &result);
      if (result)
      {
            archive_set_error (a, errno, _("Error reading from file: %m"));
            d->ar_message = archive_error_string (a);
            dv_show_error (DV_ERR_FILE_READ, d->parent);
            g_clear_error (&result);
            return DV_AR_FAIL;
      }
      if (bytesread == 0)
      {
            archive_set_error (a, EIO, _("Unexpected end of file."));
            d->ar_message = archive_error_string (a);
            dv_show_error (DV_ERR_FILE_READ, d->parent);
            g_clear_error (&result);
            return DV_AR_FAIL;
      }
      d->skip -= bytesread;
      return bytesread;
}

/* A .deb is two archives within an archive so do NOT
close the FILE at this stage! */
static gint
dv_archive_close (struct archive *r __attribute__ ((unused)), gpointer client_data)
{
      DVContents *d = (DVContents*)client_data;

      g_free(d->readbuffer);
      d->readbuffer = NULL;
      return ARCHIVE_OK;
}

/* from dpkg extract.c */
static gulong
parseheaderlength (const gchar *inh, size_t len)
{
      gchar lintbuf[15];
      gulong r;
      gchar *endp;

      if (memchr (inh, 0, len))
            return 0;
      g_assert (sizeof (lintbuf) > len);
      memcpy (lintbuf, inh, len);
      lintbuf[len] = ' ';
      *strchr (lintbuf, ' ') = 0;
      r = strtoul (lintbuf, &endp, 10);
      if (*endp)
            return 0;
      return r;
}

static gboolean
prepare_contents (GFile * gfile, DVContents * deb)
{
      gsize bytesread, header, seek;
      GError * result;
      GFileInputStream * filestream;
      struct ar_hdr debian_binary;
      struct ar_hdr controlgz;
      struct ar_hdr datagz;
      gint memberlen, error_num;
      gulong bytes_left;
      gchar * versionbuf, * vers_cmp, *infobuf, * filename;

      g_return_val_if_fail (deb, FALSE);
      result = NULL;
      deb->ghandle = gfile;
      filestream = g_file_read (deb->ghandle, NULL, &result);
      if (result)
      {
            filename = g_file_get_parse_name (deb->ghandle);
            g_warning ("Error: %s : %s", filename, result->message);
            dv_show_error (DV_ERR_FILE_READ, deb->parent);
            g_free (filename);
            g_clear_error (&result);
            return FALSE;
      }
      deb->stream = (GInputStream*)filestream;
      if (g_input_stream_is_closed (deb->stream))
            return FALSE;
      versionbuf = g_new0 (gchar, strlen(ARMAG) + 1);
      bytesread = g_input_stream_read (deb->stream, versionbuf, strlen(ARMAG), NULL, &result);
      if (result)
      {
            g_warning (result->message);
            g_clear_error (&result);
            return FALSE;
      }
      vers_cmp = g_strndup (versionbuf, strlen(ARMAG));
      g_free (versionbuf);
      if (strcmp(vers_cmp, ARMAG))
      {
            dv_show_error (DV_ERR_VERSION, deb->parent);
            g_input_stream_close (deb->stream, NULL, &result);
            if (result)
            {
                  g_warning (result->message);
                  g_clear_error (&result);
            }
            return FALSE;
      }
      header = sizeof(struct ar_hdr);
      bytesread = g_input_stream_read (deb->stream, &debian_binary, header, NULL, &result);
      if (memcmp (debian_binary.ar_name, "debian-binary   ",
                  sizeof (debian_binary.ar_name)) &&
            memcmp (debian_binary.ar_name, "debian-binary/   ",
                  sizeof (debian_binary.ar_name)))
      {
            dv_show_error (DV_ERR_NOT_DPKG, deb->parent);
            g_input_stream_close (deb->stream, NULL, &result);
            if (result)
            {
                  g_warning (result->message);
                  g_clear_error (&result);
            }
            return FALSE;
      }
      if (result)
      {
            g_warning (result->message);
            g_clear_error (&result);
      }
      memberlen = parseheaderlength (debian_binary.ar_size, 
            sizeof (debian_binary.ar_size));
      if (!memberlen)
            dv_show_error (DV_ERR_CORRUPT_FILE, deb->parent);
      header = memberlen + 1;
      infobuf = g_new0(gchar, header);
      bytesread = g_input_stream_read (deb->stream, infobuf, memberlen + (memberlen & 1), NULL, &result);
      if (result)
      {
            g_warning (result->message);
            g_clear_error (&result);
      }
      g_free (infobuf);
      header = sizeof(struct ar_hdr);
      bytesread = g_input_stream_read (deb->stream, &controlgz, sizeof (struct ar_hdr), NULL, &result);
      if (result)
      {
            g_warning (result->message);
            g_clear_error (&result);
      }
      deb->current = &controlgz;
      deb->control_tar = archive_read_new ();
      archive_read_support_compression_gzip (deb->control_tar);
      archive_read_support_format_tar (deb->control_tar);
      error_num = ARCHIVE_OK;
      error_num = archive_read_open (deb->control_tar, deb,
            dv_archive_open, dv_archive_read, dv_archive_close);
      if (error_num != ARCHIVE_OK)
      {
            dv_show_error (DV_ERR_CORRUPT_FILE, deb->parent);
            deb->ar_message = archive_error_string (deb->control_tar);
            g_input_stream_close (deb->stream, NULL, &result);
            if (result)
            {
                  g_warning (result->message);
                  g_clear_error (&result);
            }
            return FALSE;
      }
      else
      {
            struct archive_entry *entry;
            gchar *locate;
            gpointer content;
            gint64 size;
            DVFileData *dvfd;

            while (archive_read_next_header (deb->control_tar, &entry) == ARCHIVE_OK)
            {
                  size = archive_entry_size (entry);
                  locate = g_strdup (archive_entry_pathname (entry));
                  content = g_new0(gpointer, size);
                  archive_read_data (deb->control_tar, content, size);
                  dvfd = g_new0 (DVFileData, 1);
                  dvfd->size = size;
                  if(S_ISREG(archive_entry_mode(entry)))
                        dvfd->content = content;
                  dvfd->compressed = FALSE;
                  g_hash_table_insert (deb->control_files, locate, dvfd);
                  if ((0 == strcmp (locate, "./control")) ||
                        (0 == strcmp (locate, "control")))
                  {
                        GtkWidget *display_area;
                        GtkTextBuffer *buffer;

                        /* todo: pattern match Package: Version: for
                        title of window.*/
                        display_area = lookup_widget (deb->parent, "display");
                        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(display_area));
                        gtk_text_buffer_set_text (buffer, (gchar*)content, -1);
                        gtk_text_view_set_buffer (GTK_TEXT_VIEW (display_area),     buffer);
                  }
                  archive_read_data_skip (deb->control_tar);
            }
      }
      dv_archive_close (deb->control_tar, deb);
      bytes_left = strtoul(controlgz.ar_size,NULL,10);
      /* if the size of control.tar.gz is odd,
      seek one byte to the start of data.tar.gz */
      seek = (bytes_left & 1);
      if (seek > 0)
      {
            g_input_stream_skip (deb->stream, seek, NULL, &result);
            if (result) {
                  dv_show_error (DV_ERR_CORRUPT_FILE, deb->parent);
                  deb->ar_message = archive_error_string (deb->control_tar);
                  g_input_stream_close (deb->stream, NULL, &result);
                  g_warning (result->message);
                  g_clear_error (&result);
                  return FALSE;
            }
      }
      header = sizeof(struct ar_hdr);
      bytesread = g_input_stream_read (deb->stream, &datagz, sizeof(struct ar_hdr), NULL, &result);
      if (result)
      {
            g_warning ("Data tarball read error: %s", result->message);
            g_clear_error (&result);
      }
      deb->current = &datagz;
      deb->data_tar = archive_read_new ();
      archive_read_support_compression_gzip (deb->data_tar);
      archive_read_support_format_tar (deb->data_tar);
      error_num = ARCHIVE_OK;
      error_num = archive_read_open (deb->data_tar, deb,
            dv_archive_open, dv_archive_read, dv_archive_close);
      if (error_num != ARCHIVE_OK)
      {
            dv_show_error (DV_ERR_CORRUPT_FILE, deb->parent);
            deb->ar_message = archive_error_string (deb->data_tar);
            g_input_stream_close (deb->stream, NULL, &result);
            if (result)
            {
                  g_warning (result->message);
                  g_clear_error (&result);
            }
      }
      else
      {
            struct archive_entry *entry;
            gpointer *datacontent;
            gint64 size;
            gchar *locate;
            DVFileData *dvfd;

            while (archive_read_next_header (deb->data_tar, &entry) == ARCHIVE_OK)
            {
                  size = archive_entry_size (entry);
                  datacontent = g_new0 (gpointer, size);
                  archive_read_data (deb->data_tar, datacontent, size);
                  /* locate is the name of the .gz file */
                  locate = g_strdup (archive_entry_pathname (entry));
                  dvfd = g_new0 (DVFileData, 1);
                  dvfd->size = size;
                  if (g_str_has_suffix (locate, ".gz"))
                  {
                        gchar *temp_file;
                        gint err;
                        gzFile t;
                        gpointer h;
                        gint f = g_file_open_tmp ("dvXXXXXX", &temp_file, NULL);
                        g_file_set_contents (temp_file, (gchar*)datacontent, size, NULL);
                        close (f);
                        h = g_new0 (gpointer, MAXGZ);
                        t = gzopen (temp_file, "rb");
                        err = gzread (t, h, MAXGZ);
                        if (err <= -1)
                        {
                              deb->ar_message = locate;
                              dv_show_error (DV_ERR_COMPRESS, deb->parent);
                        }
                        gzclose (t);
                        dvfd->content = h;
                        g_remove (temp_file);
                  }
                  else
                  {
                        if (g_str_has_suffix (locate, "copyright"))
                        {
                              g_hash_table_insert (deb->control_files, "./copyright", dvfd);
                              deb->copyright = g_strdup ((gchar*)datacontent);
                        }
                        dvfd->content = datacontent;
                  }
                  g_hash_table_insert (deb->data_files, locate, dvfd);
                  archive_read_data_skip (deb->data_tar);
            }
            archive_read_finish (deb->data_tar);
      }
      g_input_stream_close (deb->stream, NULL, &result);
      if (result)
      {
            g_warning (result->message);
            g_clear_error (&result);
      }
      return TRUE;
}

static void
add_control_files (gpointer key, gpointer value, gpointer user_data)
{
      DVIter *dvi = (DVIter *) user_data;
      gchar *path = (gchar *) key;
      DVFileData *dvfd = (DVFileData*)value;
      gchar *size_str;

      if (g_str_has_suffix(path, "/"))
            return;
      size_str = g_strdup_printf ("%" G_GINT64_FORMAT, dvfd->size);
      gtk_tree_store_append (dvi->store, dvi->child_iter, dvi->parent_iter);
      gtk_tree_store_set (dvi->store, dvi->child_iter, PKG_COLUMN,
            g_filename_to_utf8 ((gchar*)key, -1, NULL, NULL, NULL),
            SIZE_COLUMN, size_str, LOCATION_COLUMN,
            g_filename_to_utf8 ((gchar*)key, -1, NULL, NULL, NULL), -1);
}

static void
add_data_files (gpointer key, gpointer value, gpointer user_data)
{
      DVIter *dvi = (DVIter *) user_data;
      gchar *path = (gchar *) key;
      gchar *file = g_path_get_basename (path);
      DVFileData *dvfd = (DVFileData*)value;
      gchar *size_str;

      if (g_str_has_suffix(path, "/"))
            return;
      size_str = g_strdup_printf ("%" G_GINT64_FORMAT, dvfd->size);
      gtk_tree_store_append (dvi->store, dvi->child_iter, dvi->parent_iter);
      gtk_tree_store_set (dvi->store, dvi->child_iter, PKG_COLUMN,
            g_filename_to_utf8 (file, -1, NULL, NULL, NULL),
            SIZE_COLUMN, size_str, LOCATION_COLUMN,
            g_filename_to_utf8 (path, -1, NULL, NULL, NULL), -1);
}

void
dv_archive_get_control (DVIter * dvi, DVContents *deb)
{
      g_return_if_fail (deb);
      g_hash_table_foreach (deb->control_files, add_control_files, dvi);
}

void
dv_archive_get_data (DVIter * dvi, DVContents *deb)
{
      g_return_if_fail (deb);
      g_hash_table_foreach (deb->data_files, add_data_files, dvi);
}

static void
columns_reset (gpointer value, gpointer user_data)
{
      guint cols;
      GtkTreeView *treeview;
      GtkTreeViewColumn *column;

      column = (GtkTreeViewColumn*)value;
      treeview = (GtkTreeView*)user_data;
      gtk_tree_view_column_clear (column);
      cols = gtk_tree_view_remove_column (treeview, column);
}

static gint
size_compare (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
{
      gint result;
      gchar *str_a, *str_b;
      guint64 sz_a, sz_b;

      result = 0;
      gtk_tree_model_get(model, a, SIZE_COLUMN, &str_a, -1);
      gtk_tree_model_get(model, b, SIZE_COLUMN, &str_b, -1);
      if (!str_a)
            return -1;
      if (!str_b)
            return 1;
      sz_a = g_ascii_strtoull (str_a, NULL, 10);
      sz_b = g_ascii_strtoull (str_b, NULL, 10);
      if(sz_a != sz_b)
            result = (sz_a > sz_b) ? 1 : -1;
      g_free(str_a);
      g_free(str_b);
      return result;
}

static void 
tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data)
{
      GtkTreeIter iter;
      GtkTreeModel *model;
      DVContents *deb;

      deb = (DVContents*) data;
      g_return_if_fail (deb);

      if (gtk_tree_selection_get_selected (selection, &model, &iter))
      {
            if (deb->selected_file)
                  g_free (deb->selected_file);
            gtk_tree_model_get (model, &iter, LOCATION_COLUMN, 
                  &deb->selected_file, -1);
      }
}

static void
dv_update_title (const gchar * filename, DVContents * deb)
{
      const gchar * current;
      gchar * newstr;

      current = _("Debian package file viewer");
      newstr = g_strconcat (filename, " - ", current, NULL);
      gtk_window_set_title (GTK_WINDOW(deb->parent), newstr);
}

/* receives new deb from toolbar selection. */
void
open_deb (DVContents * deb)
{
      GtkWidget *treeview;
      GtkTreeStore *store;
      GtkTreeModel *model;
      GtkTreeSortable * sort;
      GtkTreeIter parent_iter, orig_iter;
      GtkTreeIter deb_iter, tar_iter, file_iter;
      GtkCellRenderer *renderer;
      GtkTreeViewColumn *column;
      GtkTreeSelection *select;
      DVIter dvi;
      GFileInfo * ginfo;
      GError * result;
      gchar * filename;

      result = NULL;
      g_return_if_fail (deb);
      filename = g_file_get_parse_name (deb->ghandle);
      if (g_file_query_exists (deb->ghandle, NULL))
      {
            ginfo = g_file_query_info (deb->ghandle, G_FILE_ATTRIBUTE_STANDARD_SIZE,
                  G_FILE_QUERY_INFO_NONE, NULL, &result);
            if (result)
            {
                  g_warning (result->message);
                  g_clear_error (&result);
            }
            if (0 == g_file_info_get_attribute_uint64 (ginfo,
                  G_FILE_ATTRIBUTE_STANDARD_SIZE))
            {
                  dv_show_error (DV_ERR_NO_FILE, deb->parent);
                  return;
            }
      }
      if (!prepare_contents (deb->ghandle, deb))
            return;
      dv_update_title(filename, deb);
      treeview = lookup_widget (deb->parent, "treeview");
      g_return_if_fail (treeview);
      model = gtk_tree_view_get_model (GTK_TREE_VIEW(treeview));
      if (deb->store_set)
      {
            GList *columns = NULL;
            store = GTK_TREE_STORE (model);
            sort = GTK_TREE_SORTABLE(store);
            gtk_tree_store_clear (store);
            columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(treeview));
            g_list_foreach (columns, columns_reset, treeview);
      }
      else
      {
            store = gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING, 
                  G_TYPE_STRING, G_TYPE_STRING);
            sort = GTK_TREE_SORTABLE(store);
            gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (sort));
            deb->store_set = TRUE;
      }
      gtk_tree_store_append (store, &parent_iter, NULL);
      gtk_tree_store_set (store, &parent_iter, PKG_COLUMN, "name",
            SIZE_COLUMN, "size", LOCATION_COLUMN, filename, -1);
      renderer = gtk_cell_renderer_text_new ();
      column = gtk_tree_view_column_new_with_attributes
            (_("Package"), renderer, "text", PKG_COLUMN, NULL);
      gtk_tree_sortable_set_sort_column_id (sort, PKG_COLUMN, GTK_SORT_ASCENDING);
      gtk_tree_view_column_set_sort_column_id (column, PKG_COLUMN);
      gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
      column = gtk_tree_view_column_new_with_attributes
            (_("Size"), renderer, "text", SIZE_COLUMN, NULL);
      gtk_tree_view_column_set_sort_column_id (column, SIZE_COLUMN);
      gtk_tree_sortable_set_sort_func (sort, SIZE_COLUMN, size_compare, NULL, NULL);
      gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
      column = gtk_tree_view_column_new_with_attributes
            (_("Location"), renderer, "text", LOCATION_COLUMN, NULL);
      gtk_tree_view_column_set_sort_column_id (column, LOCATION_COLUMN);
      gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
      gtk_tree_store_append (store, &deb_iter, &parent_iter);
      gtk_tree_store_set (store, &deb_iter, PKG_COLUMN,
            _("Debian data"), SIZE_COLUMN, NULL, LOCATION_COLUMN, NULL, -1);
      dvi.store = store;
      dvi.child_iter = &file_iter;
      dvi.parent_iter = &deb_iter;
      dv_archive_get_control (&dvi, deb);

      gtk_tree_store_append (store, &orig_iter, &parent_iter);
      gtk_tree_store_set (store, &orig_iter, PKG_COLUMN,
            _("Upstream data"), SIZE_COLUMN, "", LOCATION_COLUMN, "", -1);
      dvi.store = store;
      dvi.child_iter = &tar_iter;
      dvi.parent_iter = &orig_iter;
      dv_archive_get_data (&dvi, deb);
      gtk_tree_view_expand_all (GTK_TREE_VIEW(treeview));
      select = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
      gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
      g_signal_connect (G_OBJECT (select), "changed", 
            G_CALLBACK (tree_selection_changed_cb), deb);
      gtk_widget_show_all (treeview);
}

void
dv_archive_preload (GtkWidget * widget)
{
      DVContents *deb;

      deb = (DVContents*) lookup_widget (widget, DVCONTENTS);
      if (deb)
      {
            if (!deb->ghandle)
                  return;
            open_deb (deb);
      }
}

gchar *
dv_get_control_content (const gchar * file, GtkWidget * widget)
{
      DVFileData *dvfd;
      DVContents *deb;

      deb = (DVContents*) lookup_widget (widget, DVCONTENTS);
      g_return_val_if_fail (deb, NULL);
      dvfd = (DVFileData*)
            g_hash_table_lookup (deb->control_files, file);
      if (!dvfd)
            return NULL;
      {
            guint s_id;
            GtkWidget *sbar;
            sbar = lookup_widget(GTK_WIDGET(widget), "dvstatusbar");
            s_id = gtk_statusbar_get_context_id (GTK_STATUSBAR(sbar), "errors");
            gtk_statusbar_pop (GTK_STATUSBAR(sbar), s_id);
      }
      return (g_utf8_validate (dvfd->content, -1, NULL)) ? dvfd->content : NULL;
}

static gchar *
dv_make_utf8 (gchar * string)
{
      gchar *value;

      if (!string)
            return NULL;
      if (g_utf8_validate (string, -1, NULL))
            return string;
      value = g_locale_to_utf8 (string, -1, NULL, NULL, NULL);
      if (!value)
      {
            value = g_convert (string, -1, "UTF-8", "ISO-8859-15", NULL, NULL, NULL);
            if (!value)
                  return NULL;
            return value;
      }
      return value;
}

gint64
dv_get_content_length (DVContents * deb)
{
      DVFileData *dvfd;
      const gchar * file;

      g_return_val_if_fail (deb, 0);
      file = dv_get_selected_file (deb);
      dvfd = (DVFileData*)
            g_hash_table_lookup (deb->data_files, file);
      if (!dvfd)
            return 0;
      return dvfd->size;
}

gpointer
dv_get_raw_content (DVContents * deb)
{
      guint s_id;
      GtkWidget *sbar;
      DVFileData *dvfd;
      const gchar * file;

      g_return_val_if_fail (deb, NULL);
      file = dv_get_selected_file (deb);
      dvfd = (DVFileData*)
            g_hash_table_lookup (deb->data_files, file);
      if (!dvfd)
            return NULL;
      sbar = lookup_widget(deb->parent, "dvstatusbar");
      s_id = gtk_statusbar_get_context_id (GTK_STATUSBAR(sbar), "errors");
      gtk_statusbar_pop (GTK_STATUSBAR(sbar), s_id);
      if (dvfd->size == 0)
      {
            gtk_statusbar_push (GTK_STATUSBAR(sbar), s_id, _("Empty file."));
            return NULL;
      }
      return (gpointer)dvfd->content;
}

gchar *
dv_get_data_content (const gchar * file, GtkWidget * widget)
{
      guint s_id;
      GtkWidget *sbar;
      gchar *check;
      DVFileData *dvfd;
      DVContents *deb;

      deb = (DVContents*) lookup_widget (widget, DVCONTENTS);
      g_return_val_if_fail (deb, NULL);
      dvfd = (DVFileData*)
            g_hash_table_lookup (deb->data_files, file);
      if (!dvfd)
            return NULL;
      sbar = lookup_widget(GTK_WIDGET(widget), "dvstatusbar");
      s_id = gtk_statusbar_get_context_id (GTK_STATUSBAR(sbar), "errors");
      gtk_statusbar_pop (GTK_STATUSBAR(sbar), s_id);
      if (dvfd->size == 0)
      {
            gtk_statusbar_push (GTK_STATUSBAR(sbar), s_id, _("Empty file."));
            return NULL;
      }
      check = dv_make_utf8((gchar*)dvfd->content);
      if (check == NULL)
      {
            gtk_statusbar_push (GTK_STATUSBAR(sbar), s_id,
                  _("Error: Failed to convert the contents of "
                  "the selected file to UTF-8."));
      }
      return check;
}

gboolean
quit_last_window (gpointer data)
{
      gtk_widget_destroy (gtk_widget_get_toplevel(GTK_WIDGET(data)));
      return FALSE;
}

Generated by  Doxygen 1.6.0   Back to index