[gitg] Improved charset conversion (fixes #623466)
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg] Improved charset conversion (fixes #623466)
- Date: Sat, 23 Oct 2010 16:41:39 +0000 (UTC)
commit 2d95ef73eddbae1e5d7f6eae20552cc91cf9bcd6
Author: Jesse van den Kieboom <jessevdk gnome org>
Date: Sat Oct 23 18:41:26 2010 +0200
Improved charset conversion (fixes #623466)
This adds better support for charset conversion and preservation of
line endings. Most code was copied from gedit (GeditEncoding and
GeditSmartCharsetConverter)
configure.ac | 14 +-
gitg/gitg-commit-view.c | 45 ++--
libgitg/Makefile.am | 48 ++--
libgitg/gitg-encodings.c | 547 ++++++++++++++++++++++++++++++++
libgitg/gitg-encodings.h | 72 +++++
libgitg/gitg-runner.c | 330 ++++++++++++++-----
libgitg/gitg-runner.h | 5 +
libgitg/gitg-smart-charset-converter.c | 427 +++++++++++++++++++++++++
libgitg/gitg-smart-charset-converter.h | 76 +++++
9 files changed, 1427 insertions(+), 137 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 4a1638f..5aa6cd5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -64,14 +64,16 @@ PKG_CHECK_EXISTS([gtk+-3.0 >= 2.90],
GTKSOURCEVIEW_REQUIRED_VERSION=2.8.0
])
+GLIB_REQUIRED_VERSION=2.24
+
PKG_CHECK_MODULES(GITG, [
$GTK_REQUIRED >= $GTK_REQUIRED_VERSION
- gthread-2.0
- glib-2.0
- gobject-2.0
- gmodule-2.0
- gio-2.0
- gio-unix-2.0
+ gthread-2.0 >= $GLIB_REQUIRED_VERSION
+ glib-2.0 >= $GLIB_REQUIRED_VERSION
+ gobject-2.0 >= $GLIB_REQUIRED_VERSION
+ gmodule-2.0 >= $GLIB_REQUIRED_VERSION
+ gio-2.0 >= $GLIB_REQUIRED_VERSION
+ gio-unix-2.0 >= $GLIB_REQUIRED_VERSION
])
PKG_CHECK_MODULES(PACKAGE, [
diff --git a/gitg/gitg-commit-view.c b/gitg/gitg-commit-view.c
index 276d79e..38bc044 100644
--- a/gitg/gitg-commit-view.c
+++ b/gitg/gitg-commit-view.c
@@ -253,7 +253,6 @@ on_changes_update (GitgRunner *runner, gchar **buffer, GitgCommitView *view)
}
gtk_text_buffer_insert (buf, &iter, line, -1);
- gtk_text_buffer_insert (buf, &iter, "\n", -1);
}
if (gtk_source_buffer_get_language (GTK_SOURCE_BUFFER(buf)) == NULL)
@@ -380,8 +379,8 @@ get_selected_files(GtkTreeView *tree_view,
}
static gboolean
-check_selection(GtkTreeView *tree_view,
- GtkTreeIter *iter,
+check_selection(GtkTreeView *tree_view,
+ GtkTreeIter *iter,
GitgCommitView *view)
{
if (view->priv->update_id)
@@ -769,10 +768,10 @@ line_patch_contents (GitgCommitView *view,
line_type == old_type || is_patch_line)
{
/* copy context */
+ gtk_text_iter_forward_line (&line_end);
text = gtk_text_buffer_get_text (buffer, &start, &line_end, TRUE);
g_string_append (patch, text);
- g_string_append_c (patch, '\n');
if (!is_patch_line || line_type == GITG_DIFF_LINE_TYPE_REMOVE)
{
@@ -1298,7 +1297,7 @@ on_tree_view_unstaged_drag_data_received(GtkWidget *widget,
}
static void
-initialize_dnd(GitgCommitView *view,
+initialize_dnd(GitgCommitView *view,
GtkTreeView *tree_view,
GCallback drag_data_received)
{
@@ -1661,10 +1660,12 @@ gitg_commit_view_init (GitgCommitView *self)
self->priv = GITG_COMMIT_VIEW_GET_PRIVATE (self);
self->priv->runner = gitg_runner_new (10000);
+ gitg_runner_set_preserve_line_endings (self->priv->runner, TRUE);
+
self->priv->hand = gdk_cursor_new (GDK_HAND1);
}
-void
+void
gitg_commit_view_set_repository(GitgCommitView *view, GitgRepository *repository)
{
g_return_if_fail(GITG_IS_COMMIT_VIEW(view));
@@ -1983,7 +1984,7 @@ show_error(GitgCommitView *view, gchar const *error)
gtk_widget_destroy(dlg);
}
-static void
+static void
on_commit_clicked(GtkButton *button, GitgCommitView *view)
{
if (!gitg_commit_has_changes(view->priv->commit))
@@ -2025,7 +2026,7 @@ on_commit_clicked(GtkButton *button, GitgCommitView *view)
g_free(comment);
}
-static void
+static void
on_context_value_changed(GtkHScale *scale, GitgCommitView *view)
{
view->priv->context_size = (gint)gtk_range_get_value(GTK_RANGE(scale));
@@ -2088,16 +2089,16 @@ popup_unstaged_menu(GitgCommitView *view, GdkEventButton *event)
}
else
{
- gtk_menu_popup(GTK_MENU(wd), NULL, NULL,
- gitg_utils_menu_position_under_tree_view,
- view->priv->tree_view_staged, 0,
+ gtk_menu_popup(GTK_MENU(wd), NULL, NULL,
+ gitg_utils_menu_position_under_tree_view,
+ view->priv->tree_view_staged, 0,
gtk_get_current_event_time());
}
return TRUE;
}
-static gboolean
+static gboolean
popup_staged_menu(GitgCommitView *view, GdkEventButton *event)
{
if (!set_staged_popup_status(view))
@@ -2113,9 +2114,9 @@ popup_staged_menu(GitgCommitView *view, GdkEventButton *event)
}
else
{
- gtk_menu_popup(GTK_MENU(wd), NULL, NULL,
- gitg_utils_menu_position_under_tree_view,
- view->priv->tree_view_unstaged, 0,
+ gtk_menu_popup(GTK_MENU(wd), NULL, NULL,
+ gitg_utils_menu_position_under_tree_view,
+ view->priv->tree_view_unstaged, 0,
gtk_get_current_event_time());
}
@@ -2123,19 +2124,19 @@ popup_staged_menu(GitgCommitView *view, GdkEventButton *event)
}
-static gboolean
+static gboolean
on_unstaged_popup_menu(GtkWidget *widget, GitgCommitView *view)
{
return popup_unstaged_menu(view, NULL);
}
-static gboolean
+static gboolean
on_staged_popup_menu(GtkWidget *widget, GitgCommitView *view)
{
return popup_staged_menu(view, NULL);
}
-static void
+static void
on_stage_changes(GtkAction *action, GitgCommitView *view)
{
if (view->priv->context_type == CONTEXT_TYPE_FILE)
@@ -2197,7 +2198,7 @@ do_revert_changes(GitgCommitView *view)
show_error(view, _("Revert fail"));
}
-static void
+static void
on_revert_changes(GtkAction *action, GitgCommitView *view)
{
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))),
@@ -2207,7 +2208,7 @@ on_revert_changes(GtkAction *action, GitgCommitView *view)
"%s",
_("Are you sure you want to revert these changes?"));
- gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s",
+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s",
_("Reverting changes is permanent and cannot be undone"));
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -2252,7 +2253,7 @@ on_edit_file (GtkAction *action, GitgCommitView *view)
g_list_free (files);
}
-static void
+static void
on_ignore_file (GtkAction *action, GitgCommitView *view)
{
GList *files = NULL;
@@ -2300,7 +2301,7 @@ create_context_menu_item (GitgCommitView *view, gchar const *action)
return gtk_action_create_menu_item (ac);
}
-static void
+static void
on_changes_view_popup_menu (GtkTextView *textview, GtkMenu *menu, GitgCommitView *view)
{
gboolean is_hunk;
diff --git a/libgitg/Makefile.am b/libgitg/Makefile.am
index 0ec1f44..cc0c2e8 100644
--- a/libgitg/Makefile.am
+++ b/libgitg/Makefile.am
@@ -32,28 +32,32 @@ INST_H_FILES = \
gitg-revision.h \
gitg-runner.h
-NOINST_H_FILES = \
- gitg-convert.h \
- gitg-debug.h \
- gitg-i18n.h \
- gitg-lanes.h
-
-C_FILES = \
- $(BUILT_SOURCES) \
- gitg-changed-file.c \
- gitg-color.c \
- gitg-commit.c \
- gitg-config.c \
- gitg-convert.c \
- gitg-debug.c \
- gitg-hash.c \
- gitg-i18n.c \
- gitg-lane.c \
- gitg-lanes.c \
- gitg-ref.c \
- gitg-repository.c \
- gitg-revision.c \
- gitg-runner.c
+NOINST_H_FILES = \
+ gitg-convert.h \
+ gitg-debug.h \
+ gitg-i18n.h \
+ gitg-lanes.h \
+ gitg-smart-charset-converter.h \
+ gitg-encodings.h
+
+C_FILES = \
+ $(BUILT_SOURCES) \
+ gitg-changed-file.c \
+ gitg-color.c \
+ gitg-commit.c \
+ gitg-config.c \
+ gitg-convert.c \
+ gitg-debug.c \
+ gitg-hash.c \
+ gitg-i18n.c \
+ gitg-lane.c \
+ gitg-lanes.c \
+ gitg-ref.c \
+ gitg-repository.c \
+ gitg-revision.c \
+ gitg-runner.c \
+ gitg-smart-charset-converter.c \
+ gitg-encodings.c
ENUM_H_FILES = \
gitg-changed-file.h
diff --git a/libgitg/gitg-encodings.c b/libgitg/gitg-encodings.c
new file mode 100644
index 0000000..48c8988
--- /dev/null
+++ b/libgitg/gitg-encodings.c
@@ -0,0 +1,547 @@
+/*
+ * This file was copied from gedit-encodings.c
+ *
+ * gedit-encodings.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002-2005 Paolo Maggi
+ *
+ * This program 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ *
+ * $Id$
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include "gitg-encodings.h"
+
+
+struct _GitgEncoding
+{
+ gint index;
+ const gchar *charset;
+ const gchar *name;
+};
+
+G_DEFINE_BOXED_TYPE (GitgEncoding, gitg_encoding,
+ gitg_encoding_copy,
+ gitg_encoding_free)
+
+/*
+ * The original versions of the following tables are taken from profterm
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ */
+
+typedef enum
+{
+
+ GITG_ENCODING_ISO_8859_1,
+ GITG_ENCODING_ISO_8859_2,
+ GITG_ENCODING_ISO_8859_3,
+ GITG_ENCODING_ISO_8859_4,
+ GITG_ENCODING_ISO_8859_5,
+ GITG_ENCODING_ISO_8859_6,
+ GITG_ENCODING_ISO_8859_7,
+ GITG_ENCODING_ISO_8859_8,
+ GITG_ENCODING_ISO_8859_9,
+ GITG_ENCODING_ISO_8859_10,
+ GITG_ENCODING_ISO_8859_13,
+ GITG_ENCODING_ISO_8859_14,
+ GITG_ENCODING_ISO_8859_15,
+ GITG_ENCODING_ISO_8859_16,
+
+ GITG_ENCODING_UTF_7,
+ GITG_ENCODING_UTF_16,
+ GITG_ENCODING_UTF_16_BE,
+ GITG_ENCODING_UTF_16_LE,
+ GITG_ENCODING_UTF_32,
+ GITG_ENCODING_UCS_2,
+ GITG_ENCODING_UCS_4,
+
+ GITG_ENCODING_ARMSCII_8,
+ GITG_ENCODING_BIG5,
+ GITG_ENCODING_BIG5_HKSCS,
+ GITG_ENCODING_CP_866,
+
+ GITG_ENCODING_EUC_JP,
+ GITG_ENCODING_EUC_JP_MS,
+ GITG_ENCODING_CP932,
+ GITG_ENCODING_EUC_KR,
+ GITG_ENCODING_EUC_TW,
+
+ GITG_ENCODING_GB18030,
+ GITG_ENCODING_GB2312,
+ GITG_ENCODING_GBK,
+ GITG_ENCODING_GEOSTD8,
+
+ GITG_ENCODING_IBM_850,
+ GITG_ENCODING_IBM_852,
+ GITG_ENCODING_IBM_855,
+ GITG_ENCODING_IBM_857,
+ GITG_ENCODING_IBM_862,
+ GITG_ENCODING_IBM_864,
+
+ GITG_ENCODING_ISO_2022_JP,
+ GITG_ENCODING_ISO_2022_KR,
+ GITG_ENCODING_ISO_IR_111,
+ GITG_ENCODING_JOHAB,
+ GITG_ENCODING_KOI8_R,
+ GITG_ENCODING_KOI8__R,
+ GITG_ENCODING_KOI8_U,
+
+ GITG_ENCODING_SHIFT_JIS,
+ GITG_ENCODING_TCVN,
+ GITG_ENCODING_TIS_620,
+ GITG_ENCODING_UHC,
+ GITG_ENCODING_VISCII,
+
+ GITG_ENCODING_WINDOWS_1250,
+ GITG_ENCODING_WINDOWS_1251,
+ GITG_ENCODING_WINDOWS_1252,
+ GITG_ENCODING_WINDOWS_1253,
+ GITG_ENCODING_WINDOWS_1254,
+ GITG_ENCODING_WINDOWS_1255,
+ GITG_ENCODING_WINDOWS_1256,
+ GITG_ENCODING_WINDOWS_1257,
+ GITG_ENCODING_WINDOWS_1258,
+
+ GITG_ENCODING_LAST,
+
+ GITG_ENCODING_UTF_8,
+ GITG_ENCODING_UNKNOWN
+
+} GitgEncodingIndex;
+
+static const GitgEncoding utf8_encoding = {
+ GITG_ENCODING_UTF_8,
+ "UTF-8",
+ N_("Unicode")
+};
+
+/* initialized in gitg_encoding_lazy_init() */
+static GitgEncoding unknown_encoding = {
+ GITG_ENCODING_UNKNOWN,
+ NULL,
+ NULL
+};
+
+static const GitgEncoding encodings [] = {
+
+ { GITG_ENCODING_ISO_8859_1,
+ "ISO-8859-1", N_("Western") },
+ { GITG_ENCODING_ISO_8859_2,
+ "ISO-8859-2", N_("Central European") },
+ { GITG_ENCODING_ISO_8859_3,
+ "ISO-8859-3", N_("South European") },
+ { GITG_ENCODING_ISO_8859_4,
+ "ISO-8859-4", N_("Baltic") },
+ { GITG_ENCODING_ISO_8859_5,
+ "ISO-8859-5", N_("Cyrillic") },
+ { GITG_ENCODING_ISO_8859_6,
+ "ISO-8859-6", N_("Arabic") },
+ { GITG_ENCODING_ISO_8859_7,
+ "ISO-8859-7", N_("Greek") },
+ { GITG_ENCODING_ISO_8859_8,
+ "ISO-8859-8", N_("Hebrew Visual") },
+ { GITG_ENCODING_ISO_8859_9,
+ "ISO-8859-9", N_("Turkish") },
+ { GITG_ENCODING_ISO_8859_10,
+ "ISO-8859-10", N_("Nordic") },
+ { GITG_ENCODING_ISO_8859_13,
+ "ISO-8859-13", N_("Baltic") },
+ { GITG_ENCODING_ISO_8859_14,
+ "ISO-8859-14", N_("Celtic") },
+ { GITG_ENCODING_ISO_8859_15,
+ "ISO-8859-15", N_("Western") },
+ { GITG_ENCODING_ISO_8859_16,
+ "ISO-8859-16", N_("Romanian") },
+
+ { GITG_ENCODING_UTF_7,
+ "UTF-7", N_("Unicode") },
+ { GITG_ENCODING_UTF_16,
+ "UTF-16", N_("Unicode") },
+ { GITG_ENCODING_UTF_16_BE,
+ "UTF-16BE", N_("Unicode") },
+ { GITG_ENCODING_UTF_16_LE,
+ "UTF-16LE", N_("Unicode") },
+ { GITG_ENCODING_UTF_32,
+ "UTF-32", N_("Unicode") },
+ { GITG_ENCODING_UCS_2,
+ "UCS-2", N_("Unicode") },
+ { GITG_ENCODING_UCS_4,
+ "UCS-4", N_("Unicode") },
+
+ { GITG_ENCODING_ARMSCII_8,
+ "ARMSCII-8", N_("Armenian") },
+ { GITG_ENCODING_BIG5,
+ "BIG5", N_("Chinese Traditional") },
+ { GITG_ENCODING_BIG5_HKSCS,
+ "BIG5-HKSCS", N_("Chinese Traditional") },
+ { GITG_ENCODING_CP_866,
+ "CP866", N_("Cyrillic/Russian") },
+
+ { GITG_ENCODING_EUC_JP,
+ "EUC-JP", N_("Japanese") },
+ { GITG_ENCODING_EUC_JP_MS,
+ "EUC-JP-MS", N_("Japanese") },
+ { GITG_ENCODING_CP932,
+ "CP932", N_("Japanese") },
+
+ { GITG_ENCODING_EUC_KR,
+ "EUC-KR", N_("Korean") },
+ { GITG_ENCODING_EUC_TW,
+ "EUC-TW", N_("Chinese Traditional") },
+
+ { GITG_ENCODING_GB18030,
+ "GB18030", N_("Chinese Simplified") },
+ { GITG_ENCODING_GB2312,
+ "GB2312", N_("Chinese Simplified") },
+ { GITG_ENCODING_GBK,
+ "GBK", N_("Chinese Simplified") },
+ { GITG_ENCODING_GEOSTD8,
+ "GEORGIAN-ACADEMY", N_("Georgian") }, /* FIXME GEOSTD8 ? */
+
+ { GITG_ENCODING_IBM_850,
+ "IBM850", N_("Western") },
+ { GITG_ENCODING_IBM_852,
+ "IBM852", N_("Central European") },
+ { GITG_ENCODING_IBM_855,
+ "IBM855", N_("Cyrillic") },
+ { GITG_ENCODING_IBM_857,
+ "IBM857", N_("Turkish") },
+ { GITG_ENCODING_IBM_862,
+ "IBM862", N_("Hebrew") },
+ { GITG_ENCODING_IBM_864,
+ "IBM864", N_("Arabic") },
+
+ { GITG_ENCODING_ISO_2022_JP,
+ "ISO-2022-JP", N_("Japanese") },
+ { GITG_ENCODING_ISO_2022_KR,
+ "ISO-2022-KR", N_("Korean") },
+ { GITG_ENCODING_ISO_IR_111,
+ "ISO-IR-111", N_("Cyrillic") },
+ { GITG_ENCODING_JOHAB,
+ "JOHAB", N_("Korean") },
+ { GITG_ENCODING_KOI8_R,
+ "KOI8R", N_("Cyrillic") },
+ { GITG_ENCODING_KOI8__R,
+ "KOI8-R", N_("Cyrillic") },
+ { GITG_ENCODING_KOI8_U,
+ "KOI8U", N_("Cyrillic/Ukrainian") },
+
+ { GITG_ENCODING_SHIFT_JIS,
+ "SHIFT_JIS", N_("Japanese") },
+ { GITG_ENCODING_TCVN,
+ "TCVN", N_("Vietnamese") },
+ { GITG_ENCODING_TIS_620,
+ "TIS-620", N_("Thai") },
+ { GITG_ENCODING_UHC,
+ "UHC", N_("Korean") },
+ { GITG_ENCODING_VISCII,
+ "VISCII", N_("Vietnamese") },
+
+ { GITG_ENCODING_WINDOWS_1250,
+ "WINDOWS-1250", N_("Central European") },
+ { GITG_ENCODING_WINDOWS_1251,
+ "WINDOWS-1251", N_("Cyrillic") },
+ { GITG_ENCODING_WINDOWS_1252,
+ "WINDOWS-1252", N_("Western") },
+ { GITG_ENCODING_WINDOWS_1253,
+ "WINDOWS-1253", N_("Greek") },
+ { GITG_ENCODING_WINDOWS_1254,
+ "WINDOWS-1254", N_("Turkish") },
+ { GITG_ENCODING_WINDOWS_1255,
+ "WINDOWS-1255", N_("Hebrew") },
+ { GITG_ENCODING_WINDOWS_1256,
+ "WINDOWS-1256", N_("Arabic") },
+ { GITG_ENCODING_WINDOWS_1257,
+ "WINDOWS-1257", N_("Baltic") },
+ { GITG_ENCODING_WINDOWS_1258,
+ "WINDOWS-1258", N_("Vietnamese") }
+};
+
+static void
+gitg_encoding_lazy_init (void)
+{
+ static gboolean initialized = FALSE;
+ const gchar *locale_charset;
+
+ if (initialized)
+ return;
+
+ if (g_get_charset (&locale_charset) == FALSE)
+ {
+ unknown_encoding.charset = g_strdup (locale_charset);
+ }
+
+ initialized = TRUE;
+}
+
+const GitgEncoding *
+gitg_encoding_get_from_charset (const gchar *charset)
+{
+ gint i;
+
+ g_return_val_if_fail (charset != NULL, NULL);
+
+ gitg_encoding_lazy_init ();
+
+ if (charset == NULL)
+ return NULL;
+
+ if (g_ascii_strcasecmp (charset, "UTF-8") == 0)
+ return gitg_encoding_get_utf8 ();
+
+ i = 0;
+ while (i < GITG_ENCODING_LAST)
+ {
+ if (g_ascii_strcasecmp (charset, encodings[i].charset) == 0)
+ return &encodings[i];
+
+ ++i;
+ }
+
+ if (unknown_encoding.charset != NULL)
+ {
+ if (g_ascii_strcasecmp (charset, unknown_encoding.charset) == 0)
+ return &unknown_encoding;
+ }
+
+ return NULL;
+}
+
+GSList *
+gitg_encoding_get_candidates (void)
+{
+ static GSList *ret = NULL;
+
+ if (ret == NULL)
+ {
+ ret = g_slist_prepend (ret,
+ (gpointer)gitg_encoding_get_from_index (GITG_ENCODING_WINDOWS_1250));
+
+ ret = g_slist_prepend (ret,
+ (gpointer)gitg_encoding_get_from_index (GITG_ENCODING_ISO_8859_1));
+
+ ret = g_slist_prepend (ret,
+ (gpointer)gitg_encoding_get_current ());
+ }
+
+ return ret;
+}
+
+const GitgEncoding *
+gitg_encoding_get_from_index (gint idx)
+{
+ g_return_val_if_fail (idx >= 0, NULL);
+
+ if (idx >= GITG_ENCODING_LAST)
+ return NULL;
+
+ gitg_encoding_lazy_init ();
+
+ return &encodings[idx];
+}
+
+const GitgEncoding *
+gitg_encoding_get_utf8 (void)
+{
+ gitg_encoding_lazy_init ();
+
+ return &utf8_encoding;
+}
+
+const GitgEncoding *
+gitg_encoding_get_current (void)
+{
+ static gboolean initialized = FALSE;
+ static const GitgEncoding *locale_encoding = NULL;
+
+ const gchar *locale_charset;
+
+ gitg_encoding_lazy_init ();
+
+ if (initialized != FALSE)
+ return locale_encoding;
+
+ if (g_get_charset (&locale_charset) == FALSE)
+ {
+ g_return_val_if_fail (locale_charset != NULL, &utf8_encoding);
+
+ locale_encoding = gitg_encoding_get_from_charset (locale_charset);
+ }
+ else
+ {
+ locale_encoding = &utf8_encoding;
+ }
+
+ if (locale_encoding == NULL)
+ {
+ locale_encoding = &unknown_encoding;
+ }
+
+ g_return_val_if_fail (locale_encoding != NULL, NULL);
+
+ initialized = TRUE;
+
+ return locale_encoding;
+}
+
+gchar *
+gitg_encoding_to_string (const GitgEncoding* enc)
+{
+ g_return_val_if_fail (enc != NULL, NULL);
+
+ gitg_encoding_lazy_init ();
+
+ g_return_val_if_fail (enc->charset != NULL, NULL);
+
+ if (enc->name != NULL)
+ {
+ return g_strdup_printf ("%s (%s)", _(enc->name), enc->charset);
+ }
+ else
+ {
+ if (g_ascii_strcasecmp (enc->charset, "ANSI_X3.4-1968") == 0)
+ return g_strdup_printf ("US-ASCII (%s)", enc->charset);
+ else
+ return g_strdup (enc->charset);
+ }
+}
+
+const gchar *
+gitg_encoding_get_charset (const GitgEncoding* enc)
+{
+ g_return_val_if_fail (enc != NULL, NULL);
+
+ gitg_encoding_lazy_init ();
+
+ g_return_val_if_fail (enc->charset != NULL, NULL);
+
+ return enc->charset;
+}
+
+const gchar *
+gitg_encoding_get_name (const GitgEncoding* enc)
+{
+ g_return_val_if_fail (enc != NULL, NULL);
+
+ gitg_encoding_lazy_init ();
+
+ return (enc->name == NULL) ? _("Unknown") : _(enc->name);
+}
+
+/* These are to make language bindings happy. Since Encodings are
+ * const, copy() just returns the same pointer and fres() doesn't
+ * do nothing */
+
+GitgEncoding *
+gitg_encoding_copy (const GitgEncoding *enc)
+{
+ g_return_val_if_fail (enc != NULL, NULL);
+
+ return (GitgEncoding *) enc;
+}
+
+void
+gitg_encoding_free (GitgEncoding *enc)
+{
+ g_return_if_fail (enc != NULL);
+}
+
+static gboolean
+data_exists (GSList *list,
+ const gpointer data)
+{
+ while (list != NULL)
+ {
+ if (list->data == data)
+ return TRUE;
+
+ list = g_slist_next (list);
+ }
+
+ return FALSE;
+}
+
+GSList *
+_gitg_encoding_strv_to_list (const gchar * const *enc_str)
+{
+ GSList *res = NULL;
+ gchar **p;
+ const GitgEncoding *enc;
+
+ for (p = (gchar **)enc_str; p != NULL && *p != NULL; p++)
+ {
+ const gchar *charset = *p;
+
+ if (strcmp (charset, "CURRENT") == 0)
+ g_get_charset (&charset);
+
+ g_return_val_if_fail (charset != NULL, NULL);
+ enc = gitg_encoding_get_from_charset (charset);
+
+ if (enc != NULL)
+ {
+ if (!data_exists (res, (gpointer)enc))
+ res = g_slist_prepend (res, (gpointer)enc);
+
+ }
+ }
+
+ return g_slist_reverse (res);
+}
+
+gchar **
+_gitg_encoding_list_to_strv (const GSList *enc_list)
+{
+ GSList *l;
+ GPtrArray *array;
+
+ array = g_ptr_array_sized_new (g_slist_length ((GSList *)enc_list) + 1);
+
+ for (l = (GSList *)enc_list; l != NULL; l = g_slist_next (l))
+ {
+ const GitgEncoding *enc;
+ const gchar *charset;
+
+ enc = (const GitgEncoding *)l->data;
+
+ charset = gitg_encoding_get_charset (enc);
+ g_return_val_if_fail (charset != NULL, NULL);
+
+ g_ptr_array_add (array, g_strdup (charset));
+ }
+
+ g_ptr_array_add (array, NULL);
+
+ return (gchar **)g_ptr_array_free (array, FALSE);
+}
+
+/* ex:ts=8:noet: */
diff --git a/libgitg/gitg-encodings.h b/libgitg/gitg-encodings.h
new file mode 100644
index 0000000..1c7070e
--- /dev/null
+++ b/libgitg/gitg-encodings.h
@@ -0,0 +1,72 @@
+/*
+ * Copied from gedit-encodings.h
+ *
+ *
+ * gedit-encodings.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002-2005 Paolo Maggi
+ *
+ * This program 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ *
+ * $Id$
+ */
+
+#ifndef __GITG_ENCODINGS_H__
+#define __GITG_ENCODINGS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GitgEncoding GitgEncoding;
+
+#define GITG_TYPE_ENCODING (gitg_encoding_get_type ())
+
+GType gitg_encoding_get_type (void) G_GNUC_CONST;
+
+const GitgEncoding *gitg_encoding_get_from_charset (const gchar *charset);
+const GitgEncoding *gitg_encoding_get_from_index (gint index);
+
+gchar *gitg_encoding_to_string (const GitgEncoding *enc);
+
+const gchar *gitg_encoding_get_name (const GitgEncoding *enc);
+const gchar *gitg_encoding_get_charset (const GitgEncoding *enc);
+
+const GitgEncoding *gitg_encoding_get_utf8 (void);
+const GitgEncoding *gitg_encoding_get_current (void);
+
+GSList *gitg_encoding_get_candidates (void);
+
+/* These should not be used, they are just to make python bindings happy */
+GitgEncoding *gitg_encoding_copy (const GitgEncoding *enc);
+void gitg_encoding_free (GitgEncoding *enc);
+
+GSList *_gitg_encoding_strv_to_list (const gchar * const *enc_str);
+gchar **_gitg_encoding_list_to_strv (const GSList *enc);
+
+G_END_DECLS
+
+#endif /* __GITG_ENCODINGS_H__ */
+
+/* ex:ts=8:noet: */
diff --git a/libgitg/gitg-runner.c b/libgitg/gitg-runner.c
index 22e0198..0b4371e 100644
--- a/libgitg/gitg-runner.c
+++ b/libgitg/gitg-runner.c
@@ -23,6 +23,7 @@
#include "gitg-convert.h"
#include "gitg-debug.h"
#include "gitg-runner.h"
+#include "gitg-smart-charset-converter.h"
#include <string.h>
#include <sys/types.h>
@@ -53,7 +54,8 @@ enum
PROP_0,
PROP_BUFFER_SIZE,
- PROP_SYNCHRONIZED
+ PROP_SYNCHRONIZED,
+ PROP_PRESERVE_LINE_ENDINGS
};
struct _GitgRunnerPrivate
@@ -62,15 +64,19 @@ struct _GitgRunnerPrivate
GInputStream *input_stream;
GOutputStream *output_stream;
GCancellable *cancellable;
- gboolean synchronized;
guint buffer_size;
- gchar *buffer;
gchar *read_buffer;
gchar **lines;
gchar **environment;
+ gchar *rest_buffer;
+ gssize rest_buffer_size;
+
gint exit_status;
+
+ guint synchronized : 1;
+ guint preserve_line_endings : 1;
};
G_DEFINE_TYPE (GitgRunner, gitg_runner, G_TYPE_OBJECT)
@@ -82,7 +88,8 @@ typedef struct
} AsyncData;
static AsyncData *
-async_data_new (GitgRunner *runner, GCancellable *cancellable)
+async_data_new (GitgRunner *runner,
+ GCancellable *cancellable)
{
AsyncData *data = g_slice_new (AsyncData);
data->runner = runner;
@@ -112,7 +119,9 @@ gitg_runner_error_quark (void)
}
static void
-runner_io_exit (GPid pid, gint status, GitgRunner *runner)
+runner_io_exit (GPid pid,
+ gint status,
+ GitgRunner *runner)
{
g_spawn_close_pid (pid);
@@ -152,7 +161,7 @@ gitg_runner_finalize (GObject *object)
g_slice_free1 (sizeof (gchar *) * (runner->priv->buffer_size + 1), runner->priv->lines);
/* Remove line buffer */
- g_free (runner->priv->buffer);
+ g_free (runner->priv->rest_buffer);
g_strfreev (runner->priv->environment);
g_object_unref (runner->priv->cancellable);
@@ -161,7 +170,10 @@ gitg_runner_finalize (GObject *object)
}
static void
-gitg_runner_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+gitg_runner_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
GitgRunner *runner = GITG_RUNNER (object);
@@ -173,6 +185,9 @@ gitg_runner_get_property (GObject *object, guint prop_id, GValue *value, GParamS
case PROP_SYNCHRONIZED:
g_value_set_boolean (value, runner->priv->synchronized);
break;
+ case PROP_PRESERVE_LINE_ENDINGS:
+ g_value_set_boolean (value, runner->priv->preserve_line_endings);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -180,7 +195,8 @@ gitg_runner_get_property (GObject *object, guint prop_id, GValue *value, GParamS
}
static void
-set_buffer_size (GitgRunner *runner, guint buffer_size)
+set_buffer_size (GitgRunner *runner,
+ guint buffer_size)
{
runner->priv->buffer_size = buffer_size;
runner->priv->lines = g_slice_alloc (sizeof (gchar *) * (runner->priv->buffer_size + 1));
@@ -190,7 +206,10 @@ set_buffer_size (GitgRunner *runner, guint buffer_size)
}
static void
-gitg_runner_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+gitg_runner_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
GitgRunner *runner = GITG_RUNNER (object);
@@ -202,6 +221,9 @@ gitg_runner_set_property (GObject *object, guint prop_id, const GValue *value, G
case PROP_SYNCHRONIZED:
runner->priv->synchronized = g_value_get_boolean (value);
break;
+ case PROP_PRESERVE_LINE_ENDINGS:
+ runner->priv->preserve_line_endings = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -273,6 +295,14 @@ gitg_runner_class_init (GitgRunnerClass *klass)
G_TYPE_BOOLEAN);
g_type_class_add_private (object_class, sizeof (GitgRunnerPrivate));
+
+ g_object_class_install_property (object_class,
+ PROP_PRESERVE_LINE_ENDINGS,
+ g_param_spec_boolean ("preserve-line-endings",
+ "Preserve Line Endings",
+ "preserve line endings",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
}
static void
@@ -309,72 +339,143 @@ gitg_runner_new_synchronized (guint buffer_size)
NULL));
}
+void
+gitg_runner_set_preserve_line_endings (GitgRunner *runner,
+ gboolean preserve_line_endings)
+{
+ g_return_if_fail (GITG_IS_RUNNER (runner));
+
+ runner->priv->preserve_line_endings = preserve_line_endings;
+ g_object_notify (G_OBJECT (runner), "preserve-line-endings");
+}
+
+gboolean
+gitg_runner_get_preserve_line_endings (GitgRunner *runner)
+{
+ g_return_val_if_fail (GITG_IS_RUNNER (runner), FALSE);
+
+ return runner->priv->preserve_line_endings;
+}
+
static gchar *
-gitg_strnchr (gchar *ptr, gssize size, gchar find)
+find_newline (gchar *ptr,
+ gchar *end,
+ gchar **line_end)
{
- while (size-- > 0)
+
+ while (ptr < end)
{
- if (*ptr++ == find)
+ gunichar c;
+
+ c = g_utf8_get_char (ptr);
+
+ if (c == '\n')
+ {
+ /* That's it */
+ *line_end = g_utf8_next_char (ptr);
+ return ptr;
+ }
+ else if (c == '\r')
{
- return ptr - 1;
+ gchar *next;
+
+ next = g_utf8_next_char (ptr);
+
+ if (next < end)
+ {
+ gunichar n = g_utf8_get_char (next);
+
+ if (n == '\n')
+ {
+ /* Consume both! */
+ *line_end = g_utf8_next_char (next);
+ return ptr;
+ }
+ else
+ {
+ /* That's it! */
+ *line_end = next;
+ return ptr;
+ }
+ }
+ else
+ {
+ /* Need to save it, it might come later... */
+ break;
+ }
}
+
+ ptr = g_utf8_next_char (ptr);
}
return NULL;
}
static void
-parse_lines (GitgRunner *runner, gchar *buffer, gssize size)
+parse_lines (GitgRunner *runner,
+ gchar *buffer,
+ gssize size)
{
- gchar *ptr = buffer;
+ gchar *ptr;
gchar *newline = NULL;
gint i = 0;
+ gchar *all;
+ gchar *end;
free_lines (runner);
- while ((newline = gitg_strnchr (ptr, size, '\n')))
+ if (runner->priv->rest_buffer_size > 0)
{
- gssize linesize = newline - ptr;
- size -= linesize + 1;
- *newline = '\0';
+ GString *str = g_string_sized_new (runner->priv->rest_buffer_size + size);
- if (runner->priv->buffer)
- {
- gchar *buffered = g_strconcat (runner->priv->buffer, ptr, NULL);
- g_free (runner->priv->buffer);
- runner->priv->buffer = NULL;
+ g_string_append_len (str, runner->priv->rest_buffer, runner->priv->rest_buffer_size);
+ g_string_append_len (str, buffer, size);
- runner->priv->lines[i++] = gitg_convert_utf8 (buffered, -1);
- g_free (buffered);
- }
- else
- {
- runner->priv->lines[i++] = gitg_convert_utf8 (ptr, linesize);
- }
+ all = g_string_free (str, FALSE);
+ size += runner->priv->rest_buffer_size;
- ptr += linesize + 1;
+ g_free (runner->priv->rest_buffer);
+ runner->priv->rest_buffer = NULL;
+ runner->priv->rest_buffer_size = 0;
}
-
- if (size)
+ else
{
- gchar *tmp;
+ all = buffer;
+ }
+
+ ptr = all;
- if (runner->priv->buffer != NULL)
+ gchar *line_end;
+ end = ptr + size;
+
+ while ((newline = find_newline (ptr, end, &line_end)))
+ {
+ if (runner->priv->preserve_line_endings)
{
- tmp = g_strconcat (runner->priv->buffer, ptr, NULL);
+ runner->priv->lines[i++] = g_strndup (ptr, line_end - ptr);
}
else
{
- tmp = g_strndup (ptr, size);
+ runner->priv->lines[i++] = g_strndup (ptr, newline - ptr);
}
- g_free (runner->priv->buffer);
- runner->priv->buffer = tmp;
+ ptr = line_end;
+ }
+
+ if (ptr < end)
+ {
+ runner->priv->rest_buffer_size = end - ptr;
+ runner->priv->rest_buffer = g_strndup (ptr, runner->priv->rest_buffer_size);
}
runner->priv->lines[i] = NULL;
g_signal_emit (runner, runner_signals[UPDATE], 0, runner->priv->lines);
+
+ if (all != buffer)
+ {
+ g_free (all);
+ }
}
static void
@@ -394,16 +495,41 @@ close_streams (GitgRunner *runner)
runner->priv->input_stream = NULL;
}
- g_free (runner->priv->buffer);
- runner->priv->buffer = NULL;
+ g_free (runner->priv->rest_buffer);
+ runner->priv->rest_buffer = NULL;
+ runner->priv->rest_buffer_size = 0;
+}
+
+static void
+emit_rest (GitgRunner *runner)
+{
+ if (runner->priv->rest_buffer_size > 0)
+ {
+ if (!runner->priv->preserve_line_endings &&
+ runner->priv->rest_buffer[runner->priv->rest_buffer_size - 1] == '\r')
+ {
+ runner->priv->rest_buffer[runner->priv->rest_buffer_size - 1] = '\0';
+ }
+
+ gchar *b[] = {runner->priv->rest_buffer, NULL};
+
+ g_signal_emit (runner, runner_signals[UPDATE], 0, b);
+ }
}
static gboolean
-run_sync (GitgRunner *runner, gchar const *input, GError **error)
+run_sync (GitgRunner *runner,
+ gchar const *input,
+ GError **error)
{
if (input)
{
- if (!g_output_stream_write_all (runner->priv->output_stream, input, strlen (input), NULL, NULL, error))
+ if (!g_output_stream_write_all (runner->priv->output_stream,
+ input,
+ strlen (input),
+ NULL,
+ NULL,
+ error))
{
runner_io_exit (runner->priv->pid, 1, runner);
close_streams (runner);
@@ -419,7 +545,12 @@ run_sync (GitgRunner *runner, gchar const *input, GError **error)
while (read == runner->priv->buffer_size)
{
- if (!g_input_stream_read_all (runner->priv->input_stream, runner->priv->read_buffer, runner->priv->buffer_size, &read, NULL, error))
+ if (!g_input_stream_read_all (runner->priv->input_stream,
+ runner->priv->read_buffer,
+ runner->priv->buffer_size,
+ &read,
+ NULL,
+ error))
{
runner_io_exit (runner->priv->pid, 1, runner);
close_streams (runner);
@@ -432,8 +563,7 @@ run_sync (GitgRunner *runner, gchar const *input, GError **error)
parse_lines (runner, runner->priv->read_buffer, read);
}
- gchar *b[] = {runner->priv->buffer, NULL};
- g_signal_emit (runner, runner_signals[UPDATE], 0, b);
+ emit_rest (runner);
gint status = 0;
waitpid (runner->priv->pid, &status, 0);
@@ -468,7 +598,9 @@ async_failed (AsyncData *data)
static void start_reading (GitgRunner *runner, AsyncData *data);
static void
-read_output_ready (GInputStream *stream, GAsyncResult *result, AsyncData *data)
+read_output_ready (GInputStream *stream,
+ GAsyncResult *result,
+ AsyncData *data)
{
GError *error = NULL;
@@ -503,14 +635,7 @@ read_output_ready (GInputStream *stream, GAsyncResult *result, AsyncData *data)
if (read == 0)
{
/* End */
- gchar *converted = gitg_convert_utf8 (data->runner->priv->buffer,
- -1);
-
- gchar *b[] = {converted, NULL};
-
- g_signal_emit (data->runner, runner_signals[UPDATE], 0, b);
-
- g_free (converted);
+ emit_rest (data->runner);
gint status = 0;
waitpid (data->runner->priv->pid, &status, 0);
@@ -518,14 +643,19 @@ read_output_ready (GInputStream *stream, GAsyncResult *result, AsyncData *data)
runner_io_exit (data->runner->priv->pid, status, data->runner);
close_streams (data->runner);
- g_signal_emit (data->runner, runner_signals[END_LOADING], 0, FALSE);
+ g_signal_emit (data->runner,
+ runner_signals[END_LOADING],
+ 0,
+ FALSE);
async_data_free (data);
}
else
{
data->runner->priv->read_buffer[read] = '\0';
- parse_lines (data->runner, data->runner->priv->read_buffer, read);
+ parse_lines (data->runner,
+ data->runner->priv->read_buffer,
+ read);
if (g_cancellable_is_cancelled (data->cancellable))
{
@@ -539,9 +669,16 @@ read_output_ready (GInputStream *stream, GAsyncResult *result, AsyncData *data)
}
static void
-start_reading (GitgRunner *runner, AsyncData *data)
+start_reading (GitgRunner *runner,
+ AsyncData *data)
{
- g_input_stream_read_async (runner->priv->input_stream, runner->priv->read_buffer, runner->priv->buffer_size, G_PRIORITY_DEFAULT, runner->priv->cancellable, (GAsyncReadyCallback)read_output_ready, data);
+ g_input_stream_read_async (runner->priv->input_stream,
+ runner->priv->read_buffer,
+ runner->priv->buffer_size,
+ G_PRIORITY_DEFAULT,
+ runner->priv->cancellable,
+ (GAsyncReadyCallback)read_output_ready,
+ data);
}
static void
@@ -572,11 +709,11 @@ write_input_ready (GOutputStream *stream, GAsyncResult *result, AsyncData *data)
}
static gboolean
-gitg_runner_run_streams (GitgRunner *runner,
- GInputStream *input_stream,
- GOutputStream *output_stream,
- gchar const *input,
- GError **error)
+gitg_runner_run_streams (GitgRunner *runner,
+ GInputStream *input_stream,
+ GOutputStream *output_stream,
+ gchar const *input,
+ GError **error)
{
gitg_runner_cancel (runner);
@@ -587,7 +724,14 @@ gitg_runner_run_streams (GitgRunner *runner,
if (input_stream)
{
- runner->priv->input_stream = g_object_ref (input_stream);
+ GitgSmartCharsetConverter *smart;
+
+ smart = gitg_smart_charset_converter_new (gitg_encoding_get_candidates ());
+
+ runner->priv->input_stream = g_converter_input_stream_new (input_stream,
+ G_CONVERTER (smart));
+
+ g_object_unref (smart);
}
/* Emit begin-loading signal */
@@ -621,11 +765,11 @@ gitg_runner_run_streams (GitgRunner *runner,
}
gboolean
-gitg_runner_run_with_arguments (GitgRunner *runner,
- GFile *work_tree,
+gitg_runner_run_with_arguments (GitgRunner *runner,
+ GFile *work_tree,
gchar const **argv,
- gchar const *input,
- GError **error)
+ gchar const *input,
+ GError **error)
{
g_return_val_if_fail (GITG_IS_RUNNER (runner), FALSE);
@@ -663,42 +807,45 @@ gitg_runner_run_with_arguments (GitgRunner *runner,
return FALSE;
}
- GInputStream *input_stream = NULL;
GOutputStream *output_stream = NULL;
+ GInputStream *input_stream;
if (input)
{
- output_stream = G_OUTPUT_STREAM (g_unix_output_stream_new (stdinf, TRUE));
+ output_stream = G_OUTPUT_STREAM (g_unix_output_stream_new (stdinf,
+ TRUE));
}
input_stream = G_INPUT_STREAM (g_unix_input_stream_new (stdoutf, TRUE));
- ret = gitg_runner_run_streams (runner, input_stream, output_stream, input, error);
+
+ ret = gitg_runner_run_streams (runner,
+ input_stream,
+ output_stream,
+ input,
+ error);
if (output_stream)
{
g_object_unref (output_stream);
}
- if (input_stream)
- {
- g_object_unref (input_stream);
- }
+ g_object_unref (input_stream);
return ret;
}
gboolean
-gitg_runner_run (GitgRunner *runner,
+gitg_runner_run (GitgRunner *runner,
gchar const **argv,
- GError **error)
+ GError **error)
{
return gitg_runner_run_with_arguments (runner, NULL, argv, NULL, error);
}
gboolean
-gitg_runner_run_stream (GitgRunner *runner,
- GInputStream *stream,
- GError **error)
+gitg_runner_run_stream (GitgRunner *runner,
+ GInputStream *stream,
+ GError **error)
{
return gitg_runner_run_streams (runner, stream, NULL, NULL, error);
}
@@ -711,7 +858,9 @@ gitg_runner_get_buffer_size (GitgRunner *runner)
}
static void
-dummy_cb (GPid pid, gint status, gpointer data)
+dummy_cb (GPid pid,
+ gint status,
+ gpointer data)
{
}
@@ -756,7 +905,8 @@ gitg_runner_get_exit_status (GitgRunner *runner)
}
void
-gitg_runner_set_environment (GitgRunner *runner, gchar const **environment)
+gitg_runner_set_environment (GitgRunner *runner,
+ gchar const **environment)
{
g_return_if_fail (GITG_IS_RUNNER (runner));
@@ -783,7 +933,9 @@ gitg_runner_set_environment (GitgRunner *runner, gchar const **environment)
}
void
-gitg_runner_add_environment (GitgRunner *runner, gchar const *key, gchar const *value)
+gitg_runner_add_environment (GitgRunner *runner,
+ gchar const *key,
+ gchar const *value)
{
g_return_if_fail (GITG_IS_RUNNER (runner));
g_return_if_fail (key != NULL);
@@ -794,11 +946,15 @@ gitg_runner_add_environment (GitgRunner *runner, gchar const *key, gchar const *
gchar **all = g_listenv ();
gint i = 0;
- runner->priv->environment = g_malloc (sizeof (gchar *) * (g_strv_length (all) + 1));
+ runner->priv->environment = g_malloc (sizeof (gchar *) *
+ (g_strv_length (all) + 1));
while (all && all[i])
{
- runner->priv->environment[i] = g_strconcat (all[i], "=", g_getenv (all[i]), NULL);
+ runner->priv->environment[i] = g_strconcat (all[i],
+ "=",
+ g_getenv (all[i]),
+ NULL);
++i;
}
diff --git a/libgitg/gitg-runner.h b/libgitg/gitg-runner.h
index 300919a..589ffe1 100644
--- a/libgitg/gitg-runner.h
+++ b/libgitg/gitg-runner.h
@@ -91,6 +91,11 @@ void gitg_runner_cancel (GitgRunner *runner);
void gitg_runner_set_environment (GitgRunner *runner, gchar const **environment);
void gitg_runner_add_environment (GitgRunner *runner, gchar const *key, gchar const *value);
+void gitg_runner_set_preserve_line_endings (GitgRunner *runner,
+ gboolean preserve_line_endings);
+
+gboolean gitg_runner_get_preserve_line_endings (GitgRunner *runner);
+
GQuark gitg_runner_error_quark (void);
G_END_DECLS
diff --git a/libgitg/gitg-smart-charset-converter.c b/libgitg/gitg-smart-charset-converter.c
new file mode 100644
index 0000000..29cbad2
--- /dev/null
+++ b/libgitg/gitg-smart-charset-converter.c
@@ -0,0 +1,427 @@
+/*
+ * gedit-smart-charset-converter.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2009 - Ignacio Casal Quinteiro
+ *
+ * gedit 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gedit 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 gedit; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include "gitg-smart-charset-converter.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+#define GITG_SMART_CHARSET_CONVERTER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GITG_TYPE_SMART_CHARSET_CONVERTER, GitgSmartCharsetConverterPrivate))
+
+struct _GitgSmartCharsetConverterPrivate
+{
+ GCharsetConverter *charset_conv;
+
+ GSList *encodings;
+ GSList *current_encoding;
+
+ guint is_utf8 : 1;
+ guint use_first : 1;
+};
+
+static void gitg_smart_charset_converter_iface_init (GConverterIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GitgSmartCharsetConverter, gitg_smart_charset_converter,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
+ gitg_smart_charset_converter_iface_init))
+
+GQuark
+gitg_charset_conversion_error_quark (void)
+{
+ static GQuark ret = 0;
+
+ if (G_UNLIKELY (ret == 0))
+ {
+ ret = g_quark_from_static_string ("GitgCharsetConversionError");
+ }
+
+ return ret;
+}
+
+static void
+gitg_smart_charset_converter_finalize (GObject *object)
+{
+ GitgSmartCharsetConverter *smart = GITG_SMART_CHARSET_CONVERTER (object);
+
+ g_slist_free (smart->priv->encodings);
+
+ G_OBJECT_CLASS (gitg_smart_charset_converter_parent_class)->finalize (object);
+}
+
+static void
+gitg_smart_charset_converter_dispose (GObject *object)
+{
+ GitgSmartCharsetConverter *smart = GITG_SMART_CHARSET_CONVERTER (object);
+
+ if (smart->priv->charset_conv != NULL)
+ {
+ g_object_unref (smart->priv->charset_conv);
+ smart->priv->charset_conv = NULL;
+ }
+
+ G_OBJECT_CLASS (gitg_smart_charset_converter_parent_class)->dispose (object);
+}
+
+static void
+gitg_smart_charset_converter_class_init (GitgSmartCharsetConverterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gitg_smart_charset_converter_finalize;
+ object_class->dispose = gitg_smart_charset_converter_dispose;
+
+ g_type_class_add_private (object_class, sizeof (GitgSmartCharsetConverterPrivate));
+}
+
+static void
+gitg_smart_charset_converter_init (GitgSmartCharsetConverter *smart)
+{
+ smart->priv = GITG_SMART_CHARSET_CONVERTER_GET_PRIVATE (smart);
+
+ smart->priv->charset_conv = NULL;
+ smart->priv->encodings = NULL;
+ smart->priv->current_encoding = NULL;
+ smart->priv->is_utf8 = FALSE;
+ smart->priv->use_first = FALSE;
+}
+
+static const GitgEncoding *
+get_encoding (GitgSmartCharsetConverter *smart)
+{
+ if (smart->priv->current_encoding == NULL)
+ {
+ smart->priv->current_encoding = smart->priv->encodings;
+ }
+ else
+ {
+ smart->priv->current_encoding = g_slist_next (smart->priv->current_encoding);
+ }
+
+ if (smart->priv->current_encoding != NULL)
+ return (const GitgEncoding *)smart->priv->current_encoding->data;
+
+#if 0
+ FIXME: uncomment this when using fallback
+ /* If we tried all encodings, we return the first encoding */
+ smart->priv->use_first = TRUE;
+ smart->priv->current_encoding = smart->priv->encodings;
+
+ return (const GitgEncoding *)smart->priv->current_encoding->data;
+#endif
+ return NULL;
+}
+
+static gboolean
+try_convert (GCharsetConverter *converter,
+ const void *inbuf,
+ gsize inbuf_size)
+{
+ GError *err;
+ gsize bytes_read, nread;
+ gsize bytes_written, nwritten;
+ GConverterResult res;
+ gchar *out;
+ gboolean ret;
+ gsize out_size;
+
+ if (inbuf == NULL || inbuf_size == 0)
+ {
+ return FALSE;
+ }
+
+ err = NULL;
+ nread = 0;
+ nwritten = 0;
+ out_size = inbuf_size * 4;
+ out = g_malloc (out_size);
+
+ do
+ {
+ res = g_converter_convert (G_CONVERTER (converter),
+ (gchar *)inbuf + nread,
+ inbuf_size - nread,
+ (gchar *)out + nwritten,
+ out_size - nwritten,
+ G_CONVERTER_INPUT_AT_END,
+ &bytes_read,
+ &bytes_written,
+ &err);
+
+ nread += bytes_read;
+ nwritten += bytes_written;
+ } while (res != G_CONVERTER_FINISHED && res != G_CONVERTER_ERROR && err == NULL);
+
+ if (err != NULL)
+ {
+ if (err->code == G_CONVERT_ERROR_PARTIAL_INPUT)
+ {
+ /* FIXME We can get partial input while guessing the
+ encoding because we just take some amount of text
+ to guess from. */
+ ret = TRUE;
+ }
+ else
+ {
+ ret = FALSE;
+ }
+
+ g_error_free (err);
+ }
+ else
+ {
+ ret = TRUE;
+ }
+
+ /* FIXME: Check the remainder? */
+ if (ret == TRUE && !g_utf8_validate (out, nwritten, NULL))
+ {
+ ret = FALSE;
+ }
+
+ g_free (out);
+
+ return ret;
+}
+
+static GCharsetConverter *
+guess_encoding (GitgSmartCharsetConverter *smart,
+ const void *inbuf,
+ gsize inbuf_size)
+{
+ GCharsetConverter *conv = NULL;
+
+ if (inbuf == NULL || inbuf_size == 0)
+ {
+ smart->priv->is_utf8 = TRUE;
+ return NULL;
+ }
+
+ if (smart->priv->encodings != NULL &&
+ smart->priv->encodings->next == NULL)
+ {
+ smart->priv->use_first = TRUE;
+ }
+
+ /* We just check the first block */
+ while (TRUE)
+ {
+ const GitgEncoding *enc;
+
+ if (conv != NULL)
+ {
+ g_object_unref (conv);
+ conv = NULL;
+ }
+
+ /* We get an encoding from the list */
+ enc = get_encoding (smart);
+
+ /* if it is NULL we didn't guess anything */
+ if (enc == NULL)
+ {
+ break;
+ }
+
+ if (enc == gitg_encoding_get_utf8 ())
+ {
+ gsize remainder;
+ const gchar *end;
+
+ if (g_utf8_validate (inbuf, inbuf_size, &end) ||
+ smart->priv->use_first)
+ {
+ smart->priv->is_utf8 = TRUE;
+ break;
+ }
+
+ /* Check if the end is less than one char */
+ remainder = inbuf_size - (end - (gchar *)inbuf);
+ if (remainder < 6)
+ {
+ smart->priv->is_utf8 = TRUE;
+ break;
+ }
+
+ continue;
+ }
+
+ conv = g_charset_converter_new ("UTF-8",
+ gitg_encoding_get_charset (enc),
+ NULL);
+
+ /* If we tried all encodings we use the first one */
+ if (smart->priv->use_first)
+ {
+ break;
+ }
+
+ /* Try to convert */
+ if (try_convert (conv, inbuf, inbuf_size))
+ {
+ break;
+ }
+ }
+
+ if (conv != NULL)
+ {
+ g_converter_reset (G_CONVERTER (conv));
+
+ /* FIXME: uncomment this when we want to use the fallback
+ g_charset_converter_set_use_fallback (conv, TRUE);*/
+ }
+
+ return conv;
+}
+
+static GConverterResult
+gitg_smart_charset_converter_convert (GConverter *converter,
+ const void *inbuf,
+ gsize inbuf_size,
+ void *outbuf,
+ gsize outbuf_size,
+ GConverterFlags flags,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ GitgSmartCharsetConverter *smart = GITG_SMART_CHARSET_CONVERTER (converter);
+
+ /* Guess the encoding if we didn't make it yet */
+ if (smart->priv->charset_conv == NULL &&
+ !smart->priv->is_utf8)
+ {
+ smart->priv->charset_conv = guess_encoding (smart, inbuf, inbuf_size);
+
+ /* If we still have the previous case is that we didn't guess
+ anything */
+ if (smart->priv->charset_conv == NULL &&
+ !smart->priv->is_utf8)
+ {
+ g_set_error_literal (error, GITG_CHARSET_CONVERSION_ERROR,
+ GITG_CHARSET_CONVERSION_ERROR_ENCODING_AUTO_DETECTION_FAILED,
+ _("It is not possible to detect the encoding automatically"));
+
+ return G_CONVERTER_ERROR;
+ }
+ }
+
+ /* Now if the encoding is utf8 just redirect the input to the output */
+ if (smart->priv->is_utf8)
+ {
+ gsize size;
+ GConverterResult ret;
+
+ size = MIN (inbuf_size, outbuf_size);
+
+ memcpy (outbuf, inbuf, size);
+ *bytes_read = size;
+ *bytes_written = size;
+
+ ret = G_CONVERTER_CONVERTED;
+
+ if (flags & G_CONVERTER_INPUT_AT_END)
+ ret = G_CONVERTER_FINISHED;
+ else if (flags & G_CONVERTER_FLUSH)
+ ret = G_CONVERTER_FLUSHED;
+
+ return ret;
+ }
+
+ /* If we reached here is because we need to convert the text so, we
+ convert it with the charset converter */
+ return g_converter_convert (G_CONVERTER (smart->priv->charset_conv),
+ inbuf,
+ inbuf_size,
+ outbuf,
+ outbuf_size,
+ flags,
+ bytes_read,
+ bytes_written,
+ error);
+}
+
+static void
+gitg_smart_charset_converter_reset (GConverter *converter)
+{
+ GitgSmartCharsetConverter *smart = GITG_SMART_CHARSET_CONVERTER (converter);
+
+ smart->priv->current_encoding = NULL;
+ smart->priv->is_utf8 = FALSE;
+
+ if (smart->priv->charset_conv != NULL)
+ {
+ g_object_unref (smart->priv->charset_conv);
+ smart->priv->charset_conv = NULL;
+ }
+}
+
+static void
+gitg_smart_charset_converter_iface_init (GConverterIface *iface)
+{
+ iface->convert = gitg_smart_charset_converter_convert;
+ iface->reset = gitg_smart_charset_converter_reset;
+}
+
+GitgSmartCharsetConverter *
+gitg_smart_charset_converter_new (GSList *candidate_encodings)
+{
+ GitgSmartCharsetConverter *smart;
+
+ g_return_val_if_fail (candidate_encodings != NULL, NULL);
+
+ smart = g_object_new (GITG_TYPE_SMART_CHARSET_CONVERTER, NULL);
+
+ smart->priv->encodings = g_slist_copy (candidate_encodings);
+
+ return smart;
+}
+
+const GitgEncoding *
+gitg_smart_charset_converter_get_guessed (GitgSmartCharsetConverter *smart)
+{
+ g_return_val_if_fail (GITG_IS_SMART_CHARSET_CONVERTER (smart), NULL);
+
+ if (smart->priv->current_encoding != NULL)
+ {
+ return (const GitgEncoding *)smart->priv->current_encoding->data;
+ }
+ else if (smart->priv->is_utf8)
+ {
+ return gitg_encoding_get_utf8 ();
+ }
+
+ return NULL;
+}
+
+guint
+gitg_smart_charset_converter_get_num_fallbacks (GitgSmartCharsetConverter *smart)
+{
+ g_return_val_if_fail (GITG_IS_SMART_CHARSET_CONVERTER (smart), FALSE);
+
+ if (smart->priv->charset_conv == NULL)
+ return FALSE;
+
+ return g_charset_converter_get_num_fallbacks (smart->priv->charset_conv) != 0;
+}
+
+/* ex:ts=8:noet: */
diff --git a/libgitg/gitg-smart-charset-converter.h b/libgitg/gitg-smart-charset-converter.h
new file mode 100644
index 0000000..64d7aff
--- /dev/null
+++ b/libgitg/gitg-smart-charset-converter.h
@@ -0,0 +1,76 @@
+/*
+ * gedit-smart-charset-converter.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2009 - Ignacio Casal Quinteiro
+ *
+ * gedit 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gedit 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 gedit; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#ifndef __GITG_SMART_CHARSET_CONVERTER_H__
+#define __GITG_SMART_CHARSET_CONVERTER_H__
+
+#include <glib-object.h>
+
+#include "gitg-encodings.h"
+
+G_BEGIN_DECLS
+
+#define GITG_TYPE_SMART_CHARSET_CONVERTER (gitg_smart_charset_converter_get_type ())
+#define GITG_SMART_CHARSET_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GITG_TYPE_SMART_CHARSET_CONVERTER, GitgSmartCharsetConverter))
+#define GITG_SMART_CHARSET_CONVERTER_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GITG_TYPE_SMART_CHARSET_CONVERTER, GitgSmartCharsetConverter const))
+#define GITG_SMART_CHARSET_CONVERTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GITG_TYPE_SMART_CHARSET_CONVERTER, GitgSmartCharsetConverterClass))
+#define GITG_IS_SMART_CHARSET_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GITG_TYPE_SMART_CHARSET_CONVERTER))
+#define GITG_IS_SMART_CHARSET_CONVERTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GITG_TYPE_SMART_CHARSET_CONVERTER))
+#define GITG_SMART_CHARSET_CONVERTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GITG_TYPE_SMART_CHARSET_CONVERTER, GitgSmartCharsetConverterClass))
+
+typedef struct _GitgSmartCharsetConverter GitgSmartCharsetConverter;
+typedef struct _GitgSmartCharsetConverterClass GitgSmartCharsetConverterClass;
+typedef struct _GitgSmartCharsetConverterPrivate GitgSmartCharsetConverterPrivate;
+
+#define GITG_CHARSET_CONVERSION_ERROR (gitg_charset_conversion_error_quark ())
+
+typedef enum
+{
+ GITG_CHARSET_CONVERSION_ERROR_ENCODING_AUTO_DETECTION_FAILED
+} GitgCharserConversionError;
+
+struct _GitgSmartCharsetConverter
+{
+ GObject parent;
+
+ GitgSmartCharsetConverterPrivate *priv;
+};
+
+struct _GitgSmartCharsetConverterClass
+{
+ GObjectClass parent_class;
+};
+
+GType gitg_smart_charset_converter_get_type (void) G_GNUC_CONST;
+GQuark gitg_charset_conversion_error_quark (void);
+
+GitgSmartCharsetConverter *gitg_smart_charset_converter_new (GSList *candidate_encodings);
+
+const GitgEncoding *gitg_smart_charset_converter_get_guessed (GitgSmartCharsetConverter *smart);
+
+guint gitg_smart_charset_converter_get_num_fallbacks(GitgSmartCharsetConverter *smart);
+
+G_END_DECLS
+
+#endif /* __GITG_SMART_CHARSET_CONVERTER_H__ */
+
+/* ex:ts=8:noet: */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]