2006-05-21 18:11:05 -05:00
|
|
|
/*
|
2016-01-02 13:51:52 -08:00
|
|
|
* mooedit-fileops.cpp
|
2005-06-22 18:20:32 +00:00
|
|
|
*
|
2016-01-02 13:51:52 -08:00
|
|
|
* Copyright (C) 2004-2016 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
|
2014-11-30 15:44:42 -08:00
|
|
|
* Copyright (C) 2014 by Ulrich Eckhardt <ulrich.eckhardt@base-42.de>
|
2005-06-22 18:20:32 +00:00
|
|
|
*
|
2008-09-05 17:20:50 -05:00
|
|
|
* This file is part of medit. medit is free software; you can
|
|
|
|
* redistribute it and/or modify it under the terms of the
|
|
|
|
* GNU Lesser General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2.1 of the License,
|
|
|
|
* or (at your option) any later version.
|
2005-06-22 18:20:32 +00:00
|
|
|
*
|
2008-09-05 17:20:50 -05:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with medit. If not, see <http://www.gnu.org/licenses/>.
|
2005-06-22 18:20:32 +00:00
|
|
|
*/
|
|
|
|
|
2006-06-13 00:58:44 -05:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2005-06-22 18:20:32 +00:00
|
|
|
#include "mooedit/mooedit-private.h"
|
2010-11-23 21:54:39 -08:00
|
|
|
#include "mooedit/mooeditor-impl.h"
|
2008-09-07 00:15:07 -05:00
|
|
|
#include "mooedit/mooedit-fileops.h"
|
2005-06-22 18:20:32 +00:00
|
|
|
#include "mooedit/mooeditdialogs.h"
|
2005-10-13 14:08:18 +00:00
|
|
|
#include "mooedit/mootextbuffer.h"
|
2007-02-27 22:43:51 -06:00
|
|
|
#include "mooedit/mooeditprefs.h"
|
2008-08-31 01:40:29 -05:00
|
|
|
#include "mooutils/moofileicon.h"
|
2005-10-13 14:08:18 +00:00
|
|
|
#include "mooutils/moofilewatch.h"
|
2006-12-17 10:58:17 -06:00
|
|
|
#include "mooutils/mooencodings.h"
|
2006-12-31 04:53:45 -06:00
|
|
|
#include "mooutils/mooi18n.h"
|
2008-09-07 00:15:07 -05:00
|
|
|
#include "mooutils/mootype-macros.h"
|
2011-01-14 02:37:04 -08:00
|
|
|
#include "mooutils/mooutils.h"
|
2010-08-13 21:18:59 -07:00
|
|
|
#include "mooutils/mooutils-fs.h"
|
2010-11-07 03:32:15 -08:00
|
|
|
#include "mooutils/moocompat.h"
|
2005-06-22 18:20:32 +00:00
|
|
|
#include <string.h>
|
2005-11-10 07:15:16 +00:00
|
|
|
#include <sys/types.h>
|
2006-12-22 02:16:50 -06:00
|
|
|
#include <fcntl.h>
|
2006-06-13 00:58:44 -05:00
|
|
|
#ifdef HAVE_UNISTD_H
|
2005-11-10 07:15:16 +00:00
|
|
|
#include <unistd.h>
|
2006-06-13 00:58:44 -05:00
|
|
|
#endif
|
2005-11-10 07:15:16 +00:00
|
|
|
#include <stdio.h>
|
2005-11-30 16:46:02 +00:00
|
|
|
|
2015-12-25 18:07:33 -08:00
|
|
|
#include <mooglib/moo-glib.h>
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
#include <list>
|
|
|
|
#include <moocpp/strutils.h>
|
|
|
|
using namespace moo;
|
|
|
|
|
2007-02-27 22:43:51 -06:00
|
|
|
#define ENCODING_LOCALE "LOCALE"
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2008-09-07 00:15:07 -05:00
|
|
|
MOO_DEFINE_QUARK (MooEditFileErrorQuark, _moo_edit_file_error_quark)
|
|
|
|
|
2010-12-08 01:11:51 -08:00
|
|
|
static GSList *UNTITLED = NULL;
|
|
|
|
static GHashTable *UNTITLED_NO = NULL;
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static void block_buffer_signals (Edit& edit);
|
|
|
|
static void unblock_buffer_signals (Edit& edit);
|
|
|
|
static void check_file_status (Edit& edit);
|
|
|
|
static void file_modified_on_disk (Edit& edit);
|
|
|
|
static void file_deleted (Edit& edit);
|
|
|
|
static void add_status (Edit& edit,
|
|
|
|
MooEditStatus s);
|
|
|
|
|
|
|
|
static void moo_edit_load_text (Edit& edit,
|
|
|
|
const g::File& file,
|
|
|
|
const char* encoding,
|
|
|
|
const char* text);
|
|
|
|
static bool moo_edit_reload_local (Edit& edit,
|
|
|
|
const char* encoding,
|
|
|
|
GError** error);
|
|
|
|
static bool moo_edit_save_local (Edit& edit,
|
|
|
|
const g::File& file,
|
|
|
|
const char* encoding,
|
|
|
|
MooEditSaveFlags flags,
|
|
|
|
GError** error);
|
|
|
|
static bool moo_edit_save_copy_local (Edit& edit,
|
|
|
|
const g::File& file,
|
|
|
|
const char* encoding,
|
|
|
|
MooEditSaveFlags flags,
|
|
|
|
GError** error);
|
|
|
|
static void _moo_edit_start_file_watch (Edit& edit);
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
static char *moo_convert_file_data_to_utf8 (const char *data,
|
|
|
|
gsize len,
|
|
|
|
const char *encoding,
|
|
|
|
const char *cached_encoding,
|
2016-01-03 05:13:00 -08:00
|
|
|
gstr& /*out*/ used_enc);
|
2016-01-04 03:56:42 -08:00
|
|
|
static bool encoding_needs_bom_save (const char *enc,
|
2012-08-11 15:02:55 -07:00
|
|
|
const char **enc_no_bom,
|
|
|
|
const char **bom,
|
|
|
|
gsize *bom_len);
|
2016-01-04 03:56:42 -08:00
|
|
|
static bool encoding_is_utf8 (const char *encoding);
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static bool check_regular (const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
GError** error);
|
2012-08-11 15:02:55 -07:00
|
|
|
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
bool _moo_is_file_error_cancelled(GError *error)
|
2011-01-26 01:09:17 -08:00
|
|
|
{
|
|
|
|
return error && error->domain == MOO_EDIT_FILE_ERROR &&
|
|
|
|
error->code == MOO_EDIT_FILE_ERROR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-17 10:58:17 -06:00
|
|
|
static const char *
|
|
|
|
normalize_encoding (const char *encoding,
|
2016-01-01 22:25:53 -08:00
|
|
|
bool for_save)
|
2005-09-02 23:27:25 +00:00
|
|
|
{
|
2007-07-12 00:53:34 -05:00
|
|
|
if (!encoding || !encoding[0] || !strcmp (encoding, MOO_ENCODING_AUTO))
|
2006-12-17 10:58:17 -06:00
|
|
|
encoding = for_save ? MOO_ENCODING_UTF8 : NULL;
|
|
|
|
return encoding;
|
2005-09-02 23:27:25 +00:00
|
|
|
}
|
|
|
|
|
2016-01-03 05:13:00 -08:00
|
|
|
static gstr
|
|
|
|
normalize_encoding (const gstr& encoding,
|
|
|
|
bool for_save)
|
2016-01-01 22:25:53 -08:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
if (encoding.empty() || encoding == MOO_ENCODING_AUTO)
|
2016-01-03 05:13:00 -08:00
|
|
|
return for_save ? gstr::wrap_literal(MOO_ENCODING_UTF8) : gstr();
|
2016-01-01 22:25:53 -08:00
|
|
|
else
|
2016-01-03 05:13:00 -08:00
|
|
|
return encoding.borrow();
|
2016-01-01 22:25:53 -08:00
|
|
|
}
|
|
|
|
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
bool _moo_edit_file_is_new(const g::File& file)
|
2010-12-12 02:29:20 -08:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
gstr filename = file.get_path();
|
|
|
|
g_return_val_if_fail(!filename.empty(), FALSE);
|
|
|
|
return !g_file_test (filename, G_FILE_TEST_EXISTS);
|
2010-12-12 02:29:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
static gboolean
|
2016-01-04 09:56:33 -08:00
|
|
|
load_file_contents (const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
char** data,
|
|
|
|
gsize* data_len,
|
|
|
|
GError** error)
|
2005-09-02 23:27:25 +00:00
|
|
|
{
|
2016-01-01 22:25:53 -08:00
|
|
|
if (!check_regular (file, error))
|
2012-08-11 15:02:55 -07:00
|
|
|
return FALSE;
|
2012-08-11 01:24:22 -07:00
|
|
|
|
2016-01-02 07:09:54 -08:00
|
|
|
auto path = file.get_path();
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (path.empty())
|
2012-08-11 15:02:55 -07:00
|
|
|
{
|
|
|
|
g_set_error (error, MOO_EDIT_FILE_ERROR,
|
|
|
|
MOO_EDIT_FILE_ERROR_NOT_IMPLEMENTED,
|
|
|
|
"Loading remote files is not implemented");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2016-01-02 07:09:54 -08:00
|
|
|
return g_file_get_contents (path, data, data_len, error);
|
2012-08-11 15:02:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
2016-01-04 03:56:42 -08:00
|
|
|
convert_file_data_to_utf8_with_prompt (const char* data,
|
|
|
|
gsize data_len,
|
2016-01-04 09:56:33 -08:00
|
|
|
const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* encoding,
|
|
|
|
const char* cached_encoding,
|
|
|
|
/*out*/ gstr& used_encoding)
|
2012-08-11 15:02:55 -07:00
|
|
|
{
|
|
|
|
char *text_utf8 = NULL;
|
2016-01-01 22:25:53 -08:00
|
|
|
|
|
|
|
used_encoding.reset();
|
2011-01-26 01:09:17 -08:00
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
MooEditTryEncodingResponse response;
|
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
text_utf8 = moo_convert_file_data_to_utf8 (data, data_len, encoding, cached_encoding, /*out*/ used_encoding);
|
2011-01-26 01:09:17 -08:00
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
if (text_utf8)
|
2011-01-26 01:09:17 -08:00
|
|
|
break;
|
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
used_encoding.reset();
|
|
|
|
response = _moo_edit_try_encoding_dialog (file, encoding, /*out*/ used_encoding);
|
2011-01-26 01:09:17 -08:00
|
|
|
|
|
|
|
switch (response)
|
|
|
|
{
|
|
|
|
case MOO_EDIT_TRY_ENCODING_RESPONSE_CANCEL:
|
2016-01-01 22:25:53 -08:00
|
|
|
used_encoding.reset();
|
2011-01-26 01:09:17 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MOO_EDIT_TRY_ENCODING_RESPONSE_TRY_ANOTHER:
|
2016-01-01 22:25:53 -08:00
|
|
|
g_assert(!used_encoding.empty());
|
2011-01-26 01:09:17 -08:00
|
|
|
break;
|
|
|
|
}
|
2005-09-03 22:48:05 +00:00
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
if (used_encoding.empty())
|
2011-01-26 01:09:17 -08:00
|
|
|
break;
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
encoding = normalize_encoding (used_encoding, false);
|
2011-01-26 01:09:17 -08:00
|
|
|
cached_encoding = NULL;
|
|
|
|
}
|
2006-06-23 03:42:08 -05:00
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
used_encoding.ensure_not_borrowed();
|
2012-08-11 15:02:55 -07:00
|
|
|
|
|
|
|
return text_utf8;
|
|
|
|
}
|
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
bool
|
2016-01-04 09:56:33 -08:00
|
|
|
_moo_edit_load_file (Edit& edit,
|
|
|
|
const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
const gstr& init_encoding,
|
|
|
|
const gstr& init_cached_encoding,
|
|
|
|
GError** error)
|
2012-08-11 15:02:55 -07:00
|
|
|
{
|
2016-01-01 22:25:53 -08:00
|
|
|
bool result = false;
|
2012-08-11 15:02:55 -07:00
|
|
|
GError *error_here = NULL;
|
|
|
|
char *data = NULL;
|
|
|
|
gsize data_len = 0;
|
|
|
|
char *data_utf8 = NULL;
|
2016-01-03 05:13:00 -08:00
|
|
|
gstr used_encoding;
|
2012-08-11 15:02:55 -07:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_return_error_if_fail(!edit._is_busy());
|
2012-08-11 15:02:55 -07:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
gstr encoding = normalize_encoding(init_encoding, false);
|
2016-01-03 05:13:00 -08:00
|
|
|
gstr cached_encoding;
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!init_cached_encoding.empty())
|
|
|
|
cached_encoding = normalize_encoding(init_cached_encoding, false);
|
2012-08-11 15:02:55 -07:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!load_file_contents(file, &data, &data_len, &error_here))
|
2012-08-11 15:02:55 -07:00
|
|
|
goto done;
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
data_utf8 = convert_file_data_to_utf8_with_prompt(data, data_len, file, encoding, cached_encoding, /*out*/ used_encoding);
|
2012-08-11 15:02:55 -07:00
|
|
|
|
|
|
|
if (data_utf8 == NULL)
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
error_here = g_error_new(MOO_EDIT_FILE_ERROR,
|
|
|
|
MOO_EDIT_FILE_ERROR_CANCELLED,
|
|
|
|
"Cancelled");
|
2012-08-11 15:02:55 -07:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_edit_load_text(edit, file, used_encoding, data_utf8);
|
2012-08-11 15:02:55 -07:00
|
|
|
result = TRUE;
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (!result)
|
2016-01-04 03:56:42 -08:00
|
|
|
edit._stop_file_watch();
|
2012-08-11 15:02:55 -07:00
|
|
|
|
2006-06-23 03:42:08 -05:00
|
|
|
if (error_here)
|
2016-01-04 03:56:42 -08:00
|
|
|
g_propagate_error(error, error_here);
|
2005-09-03 22:48:05 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
g_free(data_utf8);
|
|
|
|
g_free(data);
|
2005-09-03 22:48:05 +00:00
|
|
|
return result;
|
2005-09-02 23:27:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
bool
|
2016-01-04 09:56:33 -08:00
|
|
|
_moo_edit_reload_file(Edit edit,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* encoding,
|
|
|
|
GError** error)
|
2005-09-02 23:27:25 +00:00
|
|
|
{
|
2006-06-23 03:42:08 -05:00
|
|
|
GError *error_here = NULL;
|
2016-01-04 03:56:42 -08:00
|
|
|
bool result = moo_edit_reload_local(edit, encoding, &error_here);
|
2006-06-23 03:42:08 -05:00
|
|
|
|
|
|
|
if (error_here)
|
2016-01-04 03:56:42 -08:00
|
|
|
g_propagate_error(error, error_here);
|
2006-06-23 03:42:08 -05:00
|
|
|
|
|
|
|
return result;
|
2005-09-02 23:27:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
bool _moo_edit_save_file(Edit& edit,
|
|
|
|
const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* encoding,
|
|
|
|
MooEditSaveFlags flags,
|
|
|
|
GError** error)
|
2005-09-02 23:27:25 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
gstr encoding_copy = gstr::make_copy(normalize_encoding(encoding, true));
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
GError *error_here = NULL;
|
|
|
|
bool result = moo_edit_save_local(edit, file, encoding_copy, flags, &error_here);
|
2006-06-23 03:42:08 -05:00
|
|
|
|
|
|
|
if (error_here)
|
2016-01-04 03:56:42 -08:00
|
|
|
g_propagate_error(error, error_here);
|
2005-09-03 22:48:05 +00:00
|
|
|
|
|
|
|
return result;
|
2005-09-02 23:27:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
bool _moo_edit_save_file_copy(Edit edit,
|
|
|
|
const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* encoding,
|
|
|
|
MooEditSaveFlags flags,
|
|
|
|
GError** error)
|
2005-11-02 04:41:09 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
gstr encoding_copy = gstr::make_copy(normalize_encoding(encoding, true));
|
|
|
|
return moo_edit_save_copy_local(edit, file, encoding_copy, flags, error);
|
2005-11-02 04:41:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-17 10:58:17 -06:00
|
|
|
static void
|
|
|
|
set_encoding_error (GError **error)
|
|
|
|
{
|
|
|
|
GError *tmp_error = NULL;
|
|
|
|
g_set_error (&tmp_error, MOO_EDIT_FILE_ERROR,
|
|
|
|
MOO_EDIT_FILE_ERROR_ENCODING,
|
|
|
|
"%s", *error ? (*error)->message : "ERROR");
|
|
|
|
g_clear_error (error);
|
|
|
|
g_propagate_error (error, tmp_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-20 23:02:34 -08:00
|
|
|
/**
|
|
|
|
* moo_edit_get_line_end_type:
|
|
|
|
**/
|
2009-12-06 13:54:27 -08:00
|
|
|
MooLineEndType
|
2009-12-13 16:16:39 -08:00
|
|
|
moo_edit_get_line_end_type (MooEdit *edit)
|
2009-12-06 13:54:27 -08:00
|
|
|
{
|
2011-01-22 21:55:40 -08:00
|
|
|
g_return_val_if_fail (MOO_IS_EDIT (edit), MOO_LE_NATIVE);
|
2010-09-04 04:25:07 -07:00
|
|
|
if (edit->priv->line_end_type == MOO_LE_NONE)
|
2011-01-22 21:55:40 -08:00
|
|
|
return MOO_LE_NATIVE;
|
2010-09-04 04:25:07 -07:00
|
|
|
else
|
|
|
|
return edit->priv->line_end_type;
|
2009-12-06 13:54:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-01-04 09:56:33 -08:00
|
|
|
moo_edit_set_line_end_type_full (Edit edit,
|
2016-01-04 03:56:42 -08:00
|
|
|
MooLineEndType le,
|
|
|
|
bool quiet)
|
2009-12-06 13:54:27 -08:00
|
|
|
{
|
2011-01-14 02:37:04 -08:00
|
|
|
g_return_if_fail (le > 0);
|
2009-12-06 13:54:27 -08:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (edit.get_priv().line_end_type != le)
|
2009-12-06 13:54:27 -08:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
edit.get_priv().line_end_type = le;
|
2009-12-06 13:54:27 -08:00
|
|
|
if (!quiet)
|
2016-01-04 03:56:42 -08:00
|
|
|
edit.notify("line-end-type");
|
2009-12-06 13:54:27 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-20 23:02:34 -08:00
|
|
|
/**
|
|
|
|
* moo_edit_set_line_end_type:
|
|
|
|
**/
|
2009-12-06 13:54:27 -08:00
|
|
|
void
|
2009-12-13 16:16:39 -08:00
|
|
|
moo_edit_set_line_end_type (MooEdit *edit,
|
|
|
|
MooLineEndType le)
|
2009-12-06 13:54:27 -08:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
g_return_if_fail(MOO_IS_EDIT(edit));
|
|
|
|
moo_edit_set_line_end_type_full(*edit, le, false);
|
2009-12-06 13:54:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-02 23:27:25 +00:00
|
|
|
/***************************************************************************/
|
|
|
|
/* File loading
|
|
|
|
*/
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static void do_load_text(Edit& edit,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* text);
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2016-01-03 05:13:00 -08:00
|
|
|
static std::list<gstr>
|
2007-04-10 02:20:06 -05:00
|
|
|
get_encodings (void)
|
|
|
|
{
|
|
|
|
const char *encodings;
|
2007-02-27 22:43:51 -06:00
|
|
|
char **raw, **p;
|
|
|
|
|
|
|
|
encodings = moo_prefs_get_string (moo_edit_setting (MOO_EDIT_PREFS_ENCODINGS));
|
|
|
|
|
2007-04-10 02:20:06 -05:00
|
|
|
if (!encodings || !encodings[0])
|
|
|
|
encodings = _moo_get_default_encodings ();
|
2007-02-27 22:43:51 -06:00
|
|
|
|
2016-01-03 05:13:00 -08:00
|
|
|
std::list<gstr> result;
|
2007-02-27 22:43:51 -06:00
|
|
|
raw = g_strsplit (encodings, ",", 0);
|
|
|
|
|
|
|
|
for (p = raw; p && *p; ++p)
|
|
|
|
{
|
|
|
|
const char *enc;
|
|
|
|
|
|
|
|
if (!g_ascii_strcasecmp (*p, ENCODING_LOCALE))
|
|
|
|
{
|
|
|
|
if (g_get_charset (&enc))
|
|
|
|
enc = "UTF-8";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
enc = *p;
|
|
|
|
}
|
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
if (!any_of(result, [enc](const char* s) { return g_ascii_strcasecmp(s, enc) == 0; }))
|
2016-01-03 05:13:00 -08:00
|
|
|
result.emplace_back(gstr::make_copy(enc));
|
2007-02-27 22:43:51 -06:00
|
|
|
}
|
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
if (result.empty())
|
2007-02-27 22:43:51 -06:00
|
|
|
{
|
2011-01-14 02:37:04 -08:00
|
|
|
g_critical ("oops");
|
2016-01-03 05:13:00 -08:00
|
|
|
result.emplace_back(gstr::wrap_literal("UTF-8"));
|
2007-02-27 22:43:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
g_strfreev (raw);
|
2016-01-01 22:25:53 -08:00
|
|
|
return result;
|
2007-02-27 22:43:51 -06:00
|
|
|
}
|
|
|
|
|
2005-09-16 23:45:55 +00:00
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static bool check_regular (const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
GError** error)
|
2008-09-15 03:59:22 -05:00
|
|
|
{
|
|
|
|
GFileInfo *info;
|
|
|
|
GFileType type;
|
|
|
|
gboolean retval = TRUE;
|
|
|
|
|
2016-01-02 07:09:54 -08:00
|
|
|
if (!file.is_native())
|
2008-09-15 03:59:22 -05:00
|
|
|
return TRUE;
|
|
|
|
|
2016-01-02 07:09:54 -08:00
|
|
|
if (!(info = file.query_info(G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, nullptr, nullptr)))
|
2008-09-15 04:25:51 -05:00
|
|
|
return TRUE;
|
2008-09-15 03:59:22 -05:00
|
|
|
|
|
|
|
type = g_file_info_get_file_type (info);
|
|
|
|
if (type != G_FILE_TYPE_REGULAR && type != G_FILE_TYPE_UNKNOWN)
|
|
|
|
{
|
|
|
|
g_set_error (error, MOO_EDIT_FILE_ERROR,
|
|
|
|
MOO_EDIT_FILE_ERROR_FAILED,
|
|
|
|
"%s", D_("Not a regular file", "glib20"));
|
|
|
|
retval = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref (info);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
static void
|
2016-01-04 09:56:33 -08:00
|
|
|
moo_edit_load_text (Edit& edit,
|
|
|
|
const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* encoding,
|
|
|
|
const char* text)
|
2005-09-16 23:45:55 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
MooEditPrivate& priv = edit.get_priv();
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
bool undo = !moo_edit_is_empty(&edit);
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
GtkTextBuffer *buffer = moo_edit_get_buffer(&edit);
|
2005-09-06 16:21:05 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
block_buffer_signals(edit);
|
2005-10-13 14:08:18 +00:00
|
|
|
|
2005-06-22 18:20:32 +00:00
|
|
|
if (undo)
|
2016-01-04 03:56:42 -08:00
|
|
|
gtk_text_buffer_begin_user_action(buffer);
|
2005-06-22 18:20:32 +00:00
|
|
|
else
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_text_buffer_begin_non_undoable_action(MOO_TEXT_BUFFER(buffer));
|
2005-11-15 05:56:39 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_text_buffer_begin_non_interactive_action(MOO_TEXT_BUFFER(buffer));
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
MooLineEndType saved_le = priv.line_end_type;
|
2009-12-06 13:54:27 -08:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
gboolean enable_highlight;
|
|
|
|
buffer = moo_edit_get_buffer(&edit);
|
|
|
|
gtk_text_buffer_set_text(buffer, "", 0);
|
|
|
|
g_object_get(buffer, "highlight-syntax", &enable_highlight, (char*) 0);
|
|
|
|
g_object_set(buffer, "highlight-syntax", FALSE, (char*) 0);
|
|
|
|
do_load_text(edit, text);
|
|
|
|
g_object_set(buffer, "highlight-syntax", enable_highlight, (char*) 0);
|
2006-06-18 03:38:28 -05:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
unblock_buffer_signals(edit);
|
2005-06-22 18:20:32 +00:00
|
|
|
|
|
|
|
if (undo)
|
2016-01-04 03:56:42 -08:00
|
|
|
gtk_text_buffer_end_user_action(buffer);
|
2005-06-22 18:20:32 +00:00
|
|
|
else
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_text_buffer_end_non_undoable_action(MOO_TEXT_BUFFER(buffer));
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_text_buffer_end_non_interactive_action(MOO_TEXT_BUFFER(buffer));
|
2012-08-11 15:02:55 -07:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
GtkTextIter start;
|
|
|
|
gtk_text_buffer_get_start_iter(buffer, &start);
|
|
|
|
gtk_text_buffer_place_cursor(buffer, &start);
|
|
|
|
priv.status = (MooEditStatus) 0;
|
|
|
|
moo_edit_set_modified(&edit, false);
|
|
|
|
edit._set_file(&file, encoding);
|
|
|
|
if (priv.line_end_type != saved_le)
|
|
|
|
edit.notify("line-end-type");
|
|
|
|
_moo_edit_start_file_watch(edit);
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static void do_load_text(Edit& edit, const char* text)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2010-02-27 22:37:18 -08:00
|
|
|
MooLineEndType le = MOO_LE_NONE;
|
2012-08-11 14:15:37 -07:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
GString *strbuf = g_string_new(NULL);
|
|
|
|
GtkTextBuffer *buffer = moo_edit_get_buffer(&edit);
|
2012-08-11 14:15:37 -07:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
MooLineReader lr;
|
|
|
|
moo_line_reader_init(&lr, text, -1);
|
2012-08-11 15:02:55 -07:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
bool mixed_le = false;
|
|
|
|
const char *line;
|
|
|
|
gsize line_len;
|
|
|
|
gsize line_term_len;
|
|
|
|
while ((line = moo_line_reader_get_line(&lr, &line_len, &line_term_len)) != NULL)
|
2005-09-02 23:27:25 +00:00
|
|
|
{
|
2006-04-05 13:55:48 -05:00
|
|
|
gboolean insert_line_term = FALSE;
|
2010-02-01 22:44:03 -08:00
|
|
|
MooLineEndType le_here = MOO_LE_NONE;
|
2012-08-11 15:02:55 -07:00
|
|
|
gsize copy_len = line_len;
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
if (line_term_len != 0)
|
2006-06-23 03:42:08 -05:00
|
|
|
{
|
2012-08-11 15:02:55 -07:00
|
|
|
const char *line_term = line + line_len;
|
2005-09-16 23:45:55 +00:00
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
insert_line_term = TRUE;
|
2011-01-26 01:14:33 -08:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (line_term_len == 1 && !strncmp(line_term, "\r", line_term_len))
|
2006-04-05 13:55:48 -05:00
|
|
|
{
|
2012-08-11 15:02:55 -07:00
|
|
|
le_here = MOO_LE_MAC;
|
2006-04-05 13:55:48 -05:00
|
|
|
}
|
2016-01-04 03:56:42 -08:00
|
|
|
else if (line_term_len == 1 && !strncmp(line_term, "\n", line_term_len))
|
2012-08-11 01:24:22 -07:00
|
|
|
{
|
2012-08-11 15:02:55 -07:00
|
|
|
le_here = MOO_LE_UNIX;
|
2012-08-11 01:24:22 -07:00
|
|
|
}
|
2016-01-04 03:56:42 -08:00
|
|
|
else if (line_term_len == 2 && !strncmp(line_term, "\r\n", line_term_len))
|
2006-04-28 19:39:30 -05:00
|
|
|
{
|
2012-08-11 15:02:55 -07:00
|
|
|
le_here = MOO_LE_WIN32;
|
2006-04-28 19:39:30 -05:00
|
|
|
}
|
2016-01-04 03:56:42 -08:00
|
|
|
else if (line_term_len == 3 && !strncmp("\xe2\x80\xa9", line_term, line_term_len))
|
2012-08-11 01:24:22 -07:00
|
|
|
{
|
2012-08-11 15:02:55 -07:00
|
|
|
insert_line_term = FALSE;
|
|
|
|
copy_len += line_term_len;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
g_critical("oops");
|
2012-08-11 15:02:55 -07:00
|
|
|
copy_len += line_term_len;
|
2012-08-11 01:24:22 -07:00
|
|
|
}
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
if (le_here)
|
|
|
|
{
|
|
|
|
if (mixed_le || (le && le != le_here))
|
|
|
|
mixed_le = TRUE;
|
|
|
|
else
|
|
|
|
le = le_here;
|
|
|
|
}
|
2012-08-11 14:15:37 -07:00
|
|
|
}
|
2012-08-11 01:24:22 -07:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
g_string_append_len(strbuf, line, copy_len);
|
2012-08-11 15:02:55 -07:00
|
|
|
|
|
|
|
if (insert_line_term)
|
2016-01-04 03:56:42 -08:00
|
|
|
g_string_append_c(strbuf, '\n');
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
gtk_text_buffer_insert_at_cursor(buffer, strbuf->str, (int) strbuf->len);
|
2006-04-28 19:39:30 -05:00
|
|
|
|
2011-01-22 21:55:40 -08:00
|
|
|
if (mixed_le)
|
|
|
|
le = MOO_LE_NATIVE;
|
|
|
|
|
|
|
|
if (le != MOO_LE_NONE)
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_edit_set_line_end_type_full(edit, le, TRUE);
|
2006-04-05 13:55:48 -05:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
g_string_free(strbuf, TRUE);
|
2006-06-18 03:38:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-03 06:47:53 +00:00
|
|
|
/* XXX */
|
2016-01-04 09:56:33 -08:00
|
|
|
static bool moo_edit_reload_local(Edit& edit,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* encoding,
|
|
|
|
GError** error)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 09:56:33 -08:00
|
|
|
g::FilePtr file = wrap_new(moo_edit_get_file(&edit));
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_return_error_if_fail(file != nullptr);
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
auto& priv = edit.get_priv();
|
|
|
|
gboolean result = _moo_edit_load_file(edit, *file,
|
|
|
|
encoding ? gstr::make_borrowed(encoding) : priv.encoding.borrow(),
|
|
|
|
NULL,
|
|
|
|
error);
|
2005-10-13 14:08:18 +00:00
|
|
|
|
|
|
|
if (result)
|
2005-09-02 23:27:25 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
priv.status = (MooEditStatus) 0;
|
|
|
|
moo_edit_set_modified(&edit, false);
|
|
|
|
_moo_edit_start_file_watch(edit);
|
|
|
|
g_clear_error(error);
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
2005-10-13 14:08:18 +00:00
|
|
|
|
|
|
|
return result;
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-02 23:27:25 +00:00
|
|
|
/***************************************************************************/
|
|
|
|
/* File saving
|
|
|
|
*/
|
|
|
|
|
2009-12-06 13:54:27 -08:00
|
|
|
static char *
|
2015-07-11 14:32:02 -07:00
|
|
|
get_contents_with_fixed_line_end (GtkTextBuffer *buffer, const char *le, gsize le_len)
|
2006-12-17 10:58:17 -06:00
|
|
|
{
|
2008-09-15 04:25:51 -05:00
|
|
|
GtkTextIter line_start;
|
|
|
|
GString *contents;
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2008-09-15 04:25:51 -05:00
|
|
|
contents = g_string_new (NULL);
|
2005-09-06 16:21:05 +00:00
|
|
|
gtk_text_buffer_get_start_iter (buffer, &line_start);
|
2005-09-02 23:27:25 +00:00
|
|
|
|
|
|
|
do
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2005-09-02 23:27:25 +00:00
|
|
|
GtkTextIter line_end = line_start;
|
|
|
|
|
|
|
|
if (!gtk_text_iter_ends_line (&line_start))
|
|
|
|
{
|
|
|
|
char *line;
|
2008-09-14 04:05:58 -05:00
|
|
|
gsize len;
|
2005-09-02 23:27:25 +00:00
|
|
|
|
|
|
|
gtk_text_iter_forward_to_line_end (&line_end);
|
2009-12-06 13:54:27 -08:00
|
|
|
line = gtk_text_buffer_get_text (buffer, &line_start, &line_end, TRUE);
|
2008-09-14 04:05:58 -05:00
|
|
|
len = strlen (line);
|
|
|
|
|
2008-09-15 04:25:51 -05:00
|
|
|
g_string_append_len (contents, line, len);
|
2008-11-19 19:41:13 -06:00
|
|
|
|
|
|
|
g_free (line);
|
2008-09-15 04:25:51 -05:00
|
|
|
}
|
2008-09-14 04:05:58 -05:00
|
|
|
|
2008-09-15 04:25:51 -05:00
|
|
|
if (!gtk_text_iter_is_end (&line_end))
|
|
|
|
g_string_append_len (contents, le, le_len);
|
|
|
|
}
|
|
|
|
while (gtk_text_iter_forward_line (&line_start));
|
2008-09-14 04:05:58 -05:00
|
|
|
|
2009-12-06 13:54:27 -08:00
|
|
|
return g_string_free (contents, FALSE);
|
|
|
|
}
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
static gstr get_contents(MooEdit& edit)
|
2009-12-06 13:54:27 -08:00
|
|
|
{
|
2009-12-30 23:57:27 -08:00
|
|
|
const char *le = "\n";
|
|
|
|
gsize le_len = 1;
|
2009-12-06 13:54:27 -08:00
|
|
|
GtkTextBuffer *buffer;
|
2009-12-30 23:57:27 -08:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
switch (moo_edit_get_line_end_type(&edit))
|
2009-12-06 13:54:27 -08:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
case MOO_LE_UNIX:
|
|
|
|
le = "\n";
|
|
|
|
le_len = 1;
|
|
|
|
break;
|
|
|
|
case MOO_LE_WIN32:
|
|
|
|
le = "\r\n";
|
|
|
|
le_len = 2;
|
|
|
|
break;
|
|
|
|
case MOO_LE_MAC:
|
|
|
|
le = "\r";
|
|
|
|
le_len = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
moo_assert_not_reached();
|
2009-12-06 13:54:27 -08:00
|
|
|
}
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
buffer = moo_edit_get_buffer(&edit);
|
|
|
|
return gstr::wrap_new(get_contents_with_fixed_line_end(buffer, le, le_len));
|
2008-09-15 04:25:51 -05:00
|
|
|
}
|
2008-09-14 04:05:58 -05:00
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static bool do_write(const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* data1,
|
|
|
|
gsize len1,
|
|
|
|
const char* data2,
|
|
|
|
gsize len2,
|
|
|
|
MooEditSaveFlags flags,
|
|
|
|
GError** error)
|
2008-09-15 04:25:51 -05:00
|
|
|
{
|
|
|
|
MooFileWriter *writer;
|
|
|
|
MooFileWriterFlags writer_flags;
|
|
|
|
gboolean success = FALSE;
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2010-02-01 22:44:03 -08:00
|
|
|
writer_flags = (flags & MOO_EDIT_SAVE_BACKUP) ? MOO_FILE_WRITER_SAVE_BACKUP : (MooFileWriterFlags) 0;
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2016-01-04 10:24:03 -08:00
|
|
|
if ((writer = moo_file_writer_new_for_file (file.nc_gobj(), writer_flags, error)))
|
2008-09-15 04:25:51 -05:00
|
|
|
{
|
2012-08-11 15:02:55 -07:00
|
|
|
success = TRUE;
|
|
|
|
if (success && len1 > 0)
|
|
|
|
success = moo_file_writer_write (writer, data1, len1);
|
|
|
|
if (success && len2 > 0)
|
|
|
|
success = moo_file_writer_write (writer, data2, len2);
|
|
|
|
if (success)
|
|
|
|
success = moo_file_writer_close (writer, error);
|
2008-09-15 04:25:51 -05:00
|
|
|
}
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2008-09-15 04:25:51 -05:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
static bool
|
2016-01-04 09:56:33 -08:00
|
|
|
do_save_local(Edit& edit,
|
|
|
|
const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* encoding,
|
|
|
|
MooEditSaveFlags flags,
|
|
|
|
GError** error)
|
2008-09-15 04:25:51 -05:00
|
|
|
{
|
|
|
|
const char *to_save;
|
|
|
|
gsize to_save_size;
|
|
|
|
GError *encoding_error = NULL;
|
2012-08-11 15:02:55 -07:00
|
|
|
const char *enc_no_bom = NULL;
|
|
|
|
const char *bom = NULL;
|
|
|
|
gsize bom_len = 0;
|
2008-09-15 04:25:51 -05:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
gstr utf8_contents = get_contents(edit);
|
|
|
|
moo_release_assert(utf8_contents != nullptr);
|
2008-09-15 04:25:51 -05:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (encoding_needs_bom_save(encoding, &enc_no_bom, &bom, &bom_len))
|
2012-08-11 15:02:55 -07:00
|
|
|
encoding = enc_no_bom;
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (encoding && encoding_is_utf8(encoding))
|
2008-09-15 04:25:51 -05:00
|
|
|
encoding = NULL;
|
|
|
|
|
|
|
|
if (encoding)
|
|
|
|
{
|
|
|
|
gsize bytes_read;
|
|
|
|
gsize bytes_written;
|
2016-01-04 03:56:42 -08:00
|
|
|
gstr encoded = gstr::wrap_new(g_convert(utf8_contents, -1,
|
|
|
|
encoding, "UTF-8",
|
|
|
|
&bytes_read, &bytes_written,
|
|
|
|
&encoding_error));
|
2008-09-15 04:25:51 -05:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!encoded.is_null())
|
2008-09-15 04:25:51 -05:00
|
|
|
{
|
|
|
|
to_save = encoded;
|
|
|
|
to_save_size = bytes_written;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
g_propagate_error(error, encoding_error);
|
|
|
|
set_encoding_error(error);
|
|
|
|
return false;
|
2005-09-02 23:27:25 +00:00
|
|
|
}
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
2008-09-15 04:25:51 -05:00
|
|
|
else
|
|
|
|
{
|
2009-12-06 13:54:27 -08:00
|
|
|
to_save = utf8_contents;
|
2016-01-04 03:56:42 -08:00
|
|
|
to_save_size = strlen(utf8_contents);
|
2008-09-15 04:25:51 -05:00
|
|
|
}
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!do_write(file, bom, bom_len, to_save, to_save_size, flags, error))
|
|
|
|
return false;
|
2015-12-29 19:42:36 -08:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
return true;
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
static bool
|
2016-01-04 09:56:33 -08:00
|
|
|
moo_edit_save_local(Edit& edit,
|
|
|
|
const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* encoding,
|
|
|
|
MooEditSaveFlags flags,
|
|
|
|
GError** error)
|
2008-09-15 04:25:51 -05:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!do_save_local(edit, file, encoding, flags, error))
|
2011-01-06 02:30:55 -08:00
|
|
|
return FALSE;
|
2008-09-15 04:25:51 -05:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
edit.get_priv().status = (MooEditStatus) 0;
|
|
|
|
edit._set_file(&file, encoding);
|
|
|
|
moo_edit_set_modified(&edit, false);
|
2011-01-06 02:30:55 -08:00
|
|
|
_moo_edit_start_file_watch (edit);
|
|
|
|
return TRUE;
|
2008-09-15 04:25:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static bool moo_edit_save_copy_local(Edit& edit,
|
|
|
|
const g::File& file,
|
2016-01-04 03:56:42 -08:00
|
|
|
const char* encoding,
|
|
|
|
MooEditSaveFlags flags,
|
|
|
|
GError** error)
|
2008-09-15 04:25:51 -05:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
return do_save_local(edit, file, encoding, flags, error);
|
2008-09-15 04:25:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-02 23:27:25 +00:00
|
|
|
/***************************************************************************/
|
|
|
|
/* Aux stuff
|
|
|
|
*/
|
|
|
|
|
2006-12-10 23:35:40 -06:00
|
|
|
static void
|
2016-01-04 09:56:33 -08:00
|
|
|
block_buffer_signals(Edit& edit)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
g_signal_handler_block (moo_edit_get_buffer (&edit), edit.get_priv().modified_changed_handler_id);
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-10 23:35:40 -06:00
|
|
|
static void
|
2016-01-04 09:56:33 -08:00
|
|
|
unblock_buffer_signals(Edit& edit)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
g_signal_handler_unblock(moo_edit_get_buffer(&edit), edit.get_priv().modified_changed_handler_id);
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-13 14:08:18 +00:00
|
|
|
static void
|
2016-01-04 03:56:42 -08:00
|
|
|
file_watch_callback(G_GNUC_UNUSED MooFileWatch& watch,
|
|
|
|
MooFileEvent* event,
|
|
|
|
gpointer data)
|
2005-10-13 14:08:18 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
g_return_if_fail(MOO_IS_EDIT(data));
|
2005-10-13 14:08:18 +00:00
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
Edit edit = *MOO_EDIT(data);
|
2016-01-04 03:56:42 -08:00
|
|
|
auto& priv = edit.get_priv();
|
|
|
|
|
|
|
|
g_return_if_fail(event->monitor_id == priv.file_monitor_id);
|
|
|
|
g_return_if_fail(!priv.filename.empty());
|
|
|
|
g_return_if_fail(!(priv.status & MOO_EDIT_STATUS_CHANGED_ON_DISK));
|
2005-10-13 14:08:18 +00:00
|
|
|
|
|
|
|
switch (event->code)
|
|
|
|
{
|
2006-12-08 14:55:46 -06:00
|
|
|
case MOO_FILE_EVENT_CHANGED:
|
2016-01-04 03:56:42 -08:00
|
|
|
priv.modified_on_disk = TRUE;
|
2005-10-13 14:08:18 +00:00
|
|
|
break;
|
|
|
|
|
2006-12-08 14:55:46 -06:00
|
|
|
case MOO_FILE_EVENT_DELETED:
|
2016-01-04 03:56:42 -08:00
|
|
|
priv.deleted_from_disk = TRUE;
|
|
|
|
priv.file_monitor_id = 0;
|
2005-10-13 14:08:18 +00:00
|
|
|
break;
|
|
|
|
|
2006-12-08 14:55:46 -06:00
|
|
|
case MOO_FILE_EVENT_ERROR:
|
|
|
|
/* XXX and what to do now? */
|
|
|
|
break;
|
2006-12-15 21:12:31 -06:00
|
|
|
|
|
|
|
case MOO_FILE_EVENT_CREATED:
|
2011-01-14 02:37:04 -08:00
|
|
|
g_critical ("oops");
|
2006-12-15 21:12:31 -06:00
|
|
|
break;
|
2005-10-13 14:08:18 +00:00
|
|
|
}
|
|
|
|
|
2010-12-08 01:14:07 -08:00
|
|
|
check_file_status (edit);
|
2005-10-13 14:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static void _moo_edit_start_file_watch(Edit& edit)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
auto& priv = edit.get_priv();
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
MooFileWatch *watch = _moo_editor_get_file_watch(priv.editor);
|
|
|
|
g_return_if_fail(watch != NULL);
|
2005-10-13 14:08:18 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (priv.file_monitor_id)
|
|
|
|
watch->cancel_monitor(priv.file_monitor_id);
|
|
|
|
priv.file_monitor_id = 0;
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
g_return_if_fail((priv.status & MOO_EDIT_STATUS_CHANGED_ON_DISK) == 0);
|
|
|
|
g_return_if_fail(!priv.filename.empty());
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
GError *error = nullptr;
|
|
|
|
priv.file_monitor_id =
|
|
|
|
watch->create_monitor(priv.filename,
|
|
|
|
file_watch_callback,
|
|
|
|
&edit, NULL, &error);
|
2005-10-13 14:08:18 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!priv.file_monitor_id)
|
2005-10-13 14:08:18 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
g_warning("could not start watch for '%s': %s",
|
|
|
|
priv.filename, moo_error_message(error));
|
|
|
|
g_error_free(error);
|
2005-10-13 14:08:18 +00:00
|
|
|
return;
|
|
|
|
}
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
void Edit::_stop_file_watch()
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
auto& priv = get_priv();
|
|
|
|
MooFileWatch *watch = _moo_editor_get_file_watch (priv.editor);
|
2011-01-14 02:37:04 -08:00
|
|
|
g_return_if_fail (watch != NULL);
|
2005-09-02 23:27:25 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (priv.file_monitor_id)
|
|
|
|
watch->cancel_monitor (priv.file_monitor_id);
|
|
|
|
priv.file_monitor_id = 0;
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static void check_file_status(Edit& edit)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
auto& priv = edit.get_priv();
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
g_return_if_fail(!priv.filename.empty());
|
|
|
|
g_return_if_fail(!(priv.status & MOO_EDIT_STATUS_CHANGED_ON_DISK));
|
|
|
|
|
|
|
|
if (priv.deleted_from_disk)
|
|
|
|
file_deleted(edit);
|
|
|
|
else if (priv.modified_on_disk)
|
|
|
|
file_modified_on_disk(edit);
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
2010-12-08 01:11:51 -08:00
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static void file_modified_on_disk(Edit& edit)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
auto& priv = edit.get_priv();
|
|
|
|
|
|
|
|
g_return_if_fail (!priv.filename.empty());
|
2014-11-15 01:16:21 +01:00
|
|
|
|
2014-11-16 16:03:19 +01:00
|
|
|
if (moo_prefs_get_bool (moo_edit_setting (MOO_EDIT_PREFS_AUTO_SYNC)))
|
2014-11-15 01:16:21 +01:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_edit_reload(&edit, NULL, NULL);
|
2014-11-15 01:16:21 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
priv.modified_on_disk = FALSE;
|
|
|
|
priv.deleted_from_disk = FALSE;
|
|
|
|
edit._stop_file_watch ();
|
|
|
|
add_status(edit, MOO_EDIT_STATUS_MODIFIED_ON_DISK);
|
2014-11-15 01:16:21 +01:00
|
|
|
}
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
2010-12-08 01:11:51 -08:00
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static void file_deleted (Edit& edit)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
auto& priv = edit.get_priv();
|
2014-11-15 01:16:21 +01:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
g_return_if_fail(!priv.filename.empty());
|
|
|
|
|
|
|
|
if (moo_prefs_get_bool(moo_edit_setting(MOO_EDIT_PREFS_AUTO_SYNC)))
|
2014-11-15 01:16:21 +01:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_edit_close(&edit);
|
2014-11-15 01:16:21 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
priv.modified_on_disk = FALSE;
|
|
|
|
priv.deleted_from_disk = FALSE;
|
|
|
|
edit._stop_file_watch();
|
|
|
|
add_status(edit, MOO_EDIT_STATUS_DELETED);
|
2014-11-15 01:16:21 +01:00
|
|
|
}
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static void add_status(Edit& edit, MooEditStatus s)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
edit.get_priv().status |= s;
|
|
|
|
edit.signal_emit_by_name("doc-status-changed", NULL);
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
void Edit::_remove_untitled(const Edit& doc)
|
2005-09-03 22:48:05 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
gpointer n = g_hash_table_lookup (UNTITLED_NO, &doc);
|
2010-12-08 01:11:51 -08:00
|
|
|
|
|
|
|
if (n)
|
2005-09-03 22:48:05 +00:00
|
|
|
{
|
2010-12-08 01:11:51 -08:00
|
|
|
UNTITLED = g_slist_remove (UNTITLED, n);
|
2016-01-04 03:56:42 -08:00
|
|
|
g_hash_table_remove (UNTITLED_NO, &doc);
|
2010-12-08 01:11:51 -08:00
|
|
|
}
|
|
|
|
}
|
2010-11-26 02:51:10 -08:00
|
|
|
|
2005-09-03 22:48:05 +00:00
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static int add_untitled(Edit& edit)
|
2010-12-08 01:11:51 -08:00
|
|
|
{
|
|
|
|
int n;
|
2010-11-26 02:51:10 -08:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!(n = GPOINTER_TO_INT(g_hash_table_lookup(UNTITLED_NO, &edit))))
|
2010-12-08 01:11:51 -08:00
|
|
|
{
|
|
|
|
for (n = 1; ; ++n)
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!g_slist_find(UNTITLED, GINT_TO_POINTER(n)))
|
2010-12-08 01:11:51 -08:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
UNTITLED = g_slist_prepend(UNTITLED, GINT_TO_POINTER(n));
|
2010-12-08 01:11:51 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-11-26 02:51:10 -08:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
g_hash_table_insert(UNTITLED_NO, &edit, GINT_TO_POINTER(n));
|
2005-09-03 22:48:05 +00:00
|
|
|
}
|
|
|
|
|
2010-12-08 01:11:51 -08:00
|
|
|
return n;
|
2005-09-03 22:48:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
static gstr moo_file_get_display_basename(const g::File& file)
|
2008-09-15 03:59:22 -05:00
|
|
|
{
|
|
|
|
const char *slash;
|
2008-09-16 20:13:30 -05:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
gstr name = moo_file_get_display_name(file);
|
|
|
|
g_return_val_if_fail(!name.empty(), NULL);
|
2008-09-16 20:13:30 -05:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
slash = strrchr(name, '/');
|
2008-09-15 03:59:22 -05:00
|
|
|
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
const char *backslash = strrchr(name, '\\');
|
2008-09-15 03:59:22 -05:00
|
|
|
if (backslash && (!slash || backslash > slash))
|
|
|
|
slash = backslash;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (slash)
|
2016-01-04 03:56:42 -08:00
|
|
|
memmove(name.get_mutable(), slash + 1, strlen(slash + 1) + 1);
|
2008-09-16 20:13:30 -05:00
|
|
|
|
|
|
|
return name;
|
2008-09-15 03:59:22 -05:00
|
|
|
}
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
static gstr normalize_filename_for_comparison(const char *filename)
|
2010-04-11 18:49:15 -07:00
|
|
|
{
|
2016-01-03 05:13:00 -08:00
|
|
|
g_return_val_if_fail(filename != NULL, NULL);
|
|
|
|
|
2010-04-11 18:49:15 -07:00
|
|
|
#ifdef __WIN32__
|
|
|
|
/* XXX */
|
2016-01-03 05:13:00 -08:00
|
|
|
gstr tmp = gstr::wrap_new(g_utf8_normalize(filename, -1, G_NORMALIZE_ALL_COMPOSE));
|
|
|
|
return gstr::wrap_new(g_utf8_strdown(tmp, -1));
|
2010-04-11 18:49:15 -07:00
|
|
|
#else
|
2016-01-03 05:13:00 -08:00
|
|
|
return gstr::wrap_new(g_strdup(filename));
|
2010-04-11 18:49:15 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
gstr Edit::_get_normalized_name(const g::File& file)
|
2010-04-11 18:49:15 -07:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
gstr tmp = file.get_path();
|
2010-12-12 02:29:20 -08:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!tmp.empty())
|
2010-12-12 02:29:20 -08:00
|
|
|
{
|
2016-01-03 05:13:00 -08:00
|
|
|
const auto& tmp2 = gstr::wrap_new(_moo_normalize_file_path(tmp));
|
2016-01-04 03:56:42 -08:00
|
|
|
return normalize_filename_for_comparison(tmp2);
|
2010-12-12 02:29:20 -08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
tmp = file.get_uri();
|
|
|
|
g_return_val_if_fail(!tmp.empty(), nullptr);
|
|
|
|
return normalize_filename_for_comparison(tmp);
|
2010-12-12 02:29:20 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-04 09:56:33 -08:00
|
|
|
void Edit::_set_file(gobj_raw_ptr<const GFile> file,
|
|
|
|
const char* encoding)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2010-12-08 01:11:51 -08:00
|
|
|
if (!UNTITLED_NO)
|
2016-01-04 03:56:42 -08:00
|
|
|
UNTITLED_NO = g_hash_table_new(g_direct_hash, g_direct_equal);
|
|
|
|
|
|
|
|
auto& priv = get_priv();
|
2010-12-08 01:11:51 -08:00
|
|
|
|
2005-09-02 23:27:25 +00:00
|
|
|
if (!file)
|
2005-06-22 18:20:32 +00:00
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
int n = add_untitled(*this);
|
2005-09-03 22:48:05 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
priv.file = nullptr;
|
|
|
|
priv.filename = nullptr;
|
|
|
|
priv.norm_name = nullptr;
|
2005-09-03 22:48:05 +00:00
|
|
|
|
|
|
|
if (n == 1)
|
2016-01-04 03:56:42 -08:00
|
|
|
priv.display_filename.copy(_("Untitled"));
|
2005-09-03 22:48:05 +00:00
|
|
|
else
|
2016-01-04 03:56:42 -08:00
|
|
|
priv.display_filename.take(g_strdup_printf(_("Untitled %d"), n));
|
2005-09-03 22:48:05 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
priv.display_basename.copy(priv.display_filename);
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
2005-09-02 23:27:25 +00:00
|
|
|
else
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
_remove_untitled(*this);
|
|
|
|
priv.file = file->dup();
|
|
|
|
priv.filename = file->get_path();
|
|
|
|
priv.norm_name = _get_normalized_name(*file);
|
|
|
|
priv.display_filename.take(moo_file_get_display_name(*file));
|
|
|
|
priv.display_basename.take(moo_file_get_display_basename(*file));
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
|
|
|
|
2007-05-02 00:37:36 -05:00
|
|
|
if (!encoding)
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_edit_set_encoding(gobj(), _moo_edit_get_default_encoding());
|
2007-11-26 00:13:17 -06:00
|
|
|
else
|
2016-01-04 03:56:42 -08:00
|
|
|
moo_edit_set_encoding(gobj(), encoding);
|
2005-06-22 18:20:32 +00:00
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
signal_emit_by_name("filename-changed", nullptr);
|
|
|
|
_status_changed();
|
|
|
|
_queue_recheck_config();
|
2005-06-22 18:20:32 +00:00
|
|
|
}
|
2005-07-31 04:28:41 +00:00
|
|
|
|
|
|
|
|
2007-07-07 07:34:41 -05:00
|
|
|
GdkPixbuf *
|
|
|
|
_moo_edit_get_icon (MooEdit *doc,
|
|
|
|
GtkWidget *widget,
|
|
|
|
GtkIconSize size)
|
|
|
|
{
|
2016-01-04 03:56:42 -08:00
|
|
|
if (!doc->priv->filename.empty())
|
2010-11-23 21:54:39 -08:00
|
|
|
return moo_get_icon_for_path (doc->priv->filename, widget, size);
|
2008-09-15 03:59:22 -05:00
|
|
|
else if (doc->priv->file)
|
2010-11-23 21:54:39 -08:00
|
|
|
return moo_get_icon_for_path (doc->priv->display_basename, widget, size);
|
2008-09-15 03:59:22 -05:00
|
|
|
else
|
2010-11-23 21:54:39 -08:00
|
|
|
return moo_get_icon_for_path (NULL, widget, size);
|
2007-07-07 07:34:41 -05:00
|
|
|
}
|
2012-08-11 15:02:55 -07:00
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
*
|
|
|
|
* Character encoding conversion
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define BOM_UTF8 "\xEF\xBB\xBF"
|
|
|
|
#define BOM_UTF8_LEN 3
|
|
|
|
#define BOM_UTF16_LE "\xFF\xFE"
|
|
|
|
#define BOM_UTF16_BE "\xFE\xFF"
|
|
|
|
#define BOM_UTF16_LEN 2
|
|
|
|
#define BOM_UTF32_LE "\xFF\xFE\x00\x00"
|
|
|
|
#define BOM_UTF32_BE "\x00\x00\xFE\xFF"
|
|
|
|
#define BOM_UTF32_LEN 4
|
|
|
|
|
|
|
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
|
|
#define BOM_UTF16 BOM_UTF16_LE
|
|
|
|
#define BOM_UTF32 BOM_UTF32_LE
|
|
|
|
#else
|
|
|
|
#define BOM_UTF16 BOM_UTF16_BE
|
|
|
|
#define BOM_UTF32 BOM_UTF32_BE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static char *
|
|
|
|
try_convert_to_utf8_from_utf8 (const char *data,
|
|
|
|
gsize len)
|
|
|
|
{
|
|
|
|
const char *invalid;
|
|
|
|
gboolean valid_utf8;
|
2013-04-22 01:02:44 -07:00
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
// g_print ("try_convert_to_utf8_from_utf8()\n");
|
|
|
|
|
|
|
|
if (len >= BOM_UTF8_LEN && memcmp (data, BOM_UTF8, BOM_UTF8_LEN) == 0)
|
|
|
|
{
|
|
|
|
data += BOM_UTF8_LEN;
|
|
|
|
len -= BOM_UTF8_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
valid_utf8 = g_utf8_validate (data, len, &invalid);
|
|
|
|
|
|
|
|
// allow trailing zero byte
|
|
|
|
if (!valid_utf8 && invalid + 1 == data + len && *invalid == 0)
|
|
|
|
valid_utf8 = TRUE;
|
|
|
|
|
|
|
|
return valid_utf8 ? g_strdup (data) : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
encoding_needs_bom_load (const char *enc,
|
|
|
|
gboolean *bom_optional,
|
|
|
|
const char **enc_no_bom,
|
|
|
|
const char **bom,
|
|
|
|
gsize *bom_len)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *enc_bom;
|
|
|
|
const char *enc_no_bom;
|
|
|
|
const char *bom;
|
|
|
|
gsize bom_len;
|
|
|
|
gboolean optional;
|
|
|
|
} encs[] = {
|
|
|
|
{ "UTF-8-BOM", "UTF-8", BOM_UTF8, BOM_UTF8_LEN, FALSE },
|
|
|
|
{ "UTF-16", "UTF-16", BOM_UTF16, BOM_UTF16_LEN, TRUE },
|
|
|
|
{ "UTF-16LE-BOM", "UTF-16LE", BOM_UTF16_LE, BOM_UTF16_LEN, FALSE },
|
|
|
|
{ "UTF-16BE-BOM", "UTF-16BE", BOM_UTF16_BE, BOM_UTF16_LEN, FALSE },
|
|
|
|
{ "UTF-32", "UTF-32", BOM_UTF32, BOM_UTF32_LEN, TRUE },
|
|
|
|
{ "UTF-32LE-BOM", "UTF-32LE", BOM_UTF32_LE, BOM_UTF32_LEN, FALSE },
|
|
|
|
{ "UTF-32BE-BOM", "UTF-32BE", BOM_UTF32_BE, BOM_UTF32_LEN, FALSE },
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (encs); ++i)
|
|
|
|
{
|
|
|
|
if (!g_ascii_strcasecmp (enc, encs[i].enc_bom))
|
|
|
|
{
|
|
|
|
*enc_no_bom = encs[i].enc_no_bom;
|
|
|
|
*bom = encs[i].bom;
|
|
|
|
*bom_len = encs[i].bom_len;
|
|
|
|
*bom_optional = encs[i].optional;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
static bool
|
2012-08-11 15:02:55 -07:00
|
|
|
encoding_needs_bom_save (const char *enc,
|
|
|
|
const char **enc_no_bom,
|
|
|
|
const char **bom,
|
|
|
|
gsize *bom_len)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *enc_bom;
|
|
|
|
const char *enc_no_bom;
|
|
|
|
const char *bom;
|
|
|
|
gsize bom_len;
|
|
|
|
} encs[] = {
|
|
|
|
{ "UTF-8-BOM", "UTF-8", BOM_UTF8, BOM_UTF8_LEN },
|
|
|
|
{ "UTF-16LE-BOM", "UTF-16LE", BOM_UTF16_LE, BOM_UTF16_LEN },
|
|
|
|
{ "UTF-16BE-BOM", "UTF-16BE", BOM_UTF16_BE, BOM_UTF16_LEN },
|
|
|
|
{ "UTF-32LE-BOM", "UTF-32LE", BOM_UTF32_LE, BOM_UTF32_LEN },
|
|
|
|
{ "UTF-32BE-BOM", "UTF-32BE", BOM_UTF32_BE, BOM_UTF32_LEN },
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (encs); ++i)
|
|
|
|
{
|
|
|
|
if (!g_ascii_strcasecmp (enc, encs[i].enc_bom))
|
|
|
|
{
|
|
|
|
*enc_no_bom = encs[i].enc_no_bom;
|
|
|
|
*bom = encs[i].bom;
|
|
|
|
*bom_len = encs[i].bom_len;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
try_convert_to_utf8_from_non_utf8_encoding (const char *data,
|
|
|
|
gsize len,
|
|
|
|
const char *enc)
|
|
|
|
{
|
|
|
|
const char *enc_no_bom = NULL;
|
|
|
|
const char *bom = NULL;
|
|
|
|
gsize bom_len = 0;
|
|
|
|
gsize bytes_read = 0;
|
|
|
|
gsize bytes_written = 0;
|
|
|
|
char *result = NULL;
|
|
|
|
gsize result_len = 0;
|
|
|
|
gboolean bom_optional = FALSE;
|
|
|
|
|
|
|
|
// g_print ("try_convert_to_utf8_from_non_utf8_encoding(%s)\n",
|
|
|
|
// enc ? enc : "<null>");
|
|
|
|
|
|
|
|
if (encoding_needs_bom_load (enc, &bom_optional, &enc_no_bom, &bom, &bom_len))
|
|
|
|
{
|
|
|
|
if (len < bom_len || memcmp (bom, data, bom_len) != 0)
|
|
|
|
{
|
|
|
|
if (!bom_optional)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
data += bom_len;
|
|
|
|
len -= bom_len;
|
|
|
|
enc = enc_no_bom;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encoding_is_utf8 (enc))
|
|
|
|
return try_convert_to_utf8_from_utf8 (data, len);
|
|
|
|
|
|
|
|
result = g_convert (data, len, "UTF-8", enc, &bytes_read, &bytes_written, NULL);
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (bytes_read < len)
|
|
|
|
{
|
|
|
|
g_free (result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
result_len = strlen (result);
|
|
|
|
|
|
|
|
// ignore trailing zero
|
|
|
|
if (bytes_written == result_len + 1)
|
|
|
|
bytes_written -= 1;
|
|
|
|
|
|
|
|
if (result_len < bytes_written)
|
|
|
|
{
|
|
|
|
g_free (result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
try_convert_to_utf8_from_encoding (const char *data,
|
|
|
|
gsize len,
|
|
|
|
const char *enc)
|
|
|
|
{
|
|
|
|
if (encoding_is_utf8 (enc))
|
|
|
|
return try_convert_to_utf8_from_utf8 (data, len);
|
|
|
|
else
|
|
|
|
return try_convert_to_utf8_from_non_utf8_encoding (data, len, enc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
data_has_bom (const char *data,
|
|
|
|
gsize len,
|
|
|
|
const char **bom_enc)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *enc;
|
|
|
|
const char *bom;
|
|
|
|
gsize bom_len;
|
|
|
|
} encs[] = {
|
2013-05-11 22:09:08 -07:00
|
|
|
{ "UTF-8-BOM", BOM_UTF8, BOM_UTF8_LEN },
|
|
|
|
{ "UTF-16-BOM", BOM_UTF16, BOM_UTF16_LEN },
|
|
|
|
{ "UTF-32-BOM", BOM_UTF32, BOM_UTF32_LEN },
|
2012-08-11 15:02:55 -07:00
|
|
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
|
|
{ "UTF-16BE-BOM", BOM_UTF16_BE, BOM_UTF16_LEN },
|
|
|
|
{ "UTF-32BE-BOM", BOM_UTF32_BE, BOM_UTF32_LEN },
|
|
|
|
#else
|
|
|
|
{ "UTF-16LE-BOM", BOM_UTF16_LE, BOM_UTF16_LEN },
|
|
|
|
{ "UTF-32LE-BOM", BOM_UTF32_LE, BOM_UTF32_LEN },
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (encs); ++i)
|
|
|
|
{
|
|
|
|
const char *bom = encs[i].bom;
|
|
|
|
gsize bom_len = encs[i].bom_len;
|
|
|
|
|
|
|
|
if (len >= bom_len && memcmp (data, bom, bom_len) == 0)
|
|
|
|
{
|
|
|
|
*bom_enc = encs[i].enc;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
moo_convert_file_data_to_utf8 (const char *data,
|
|
|
|
gsize len,
|
|
|
|
const char *encoding,
|
|
|
|
const char *cached_encoding,
|
2016-01-03 05:13:00 -08:00
|
|
|
gstr& used_enc)
|
2012-08-11 15:02:55 -07:00
|
|
|
{
|
|
|
|
char *result = NULL;
|
|
|
|
const char *bom_enc = NULL;
|
2013-04-22 01:02:44 -07:00
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
// g_print ("moo_convert_file_data_to_utf8(%s, %s)\n",
|
|
|
|
// encoding ? encoding : "<null>",
|
|
|
|
// cached_encoding ? cached_encoding : "<null>");
|
2013-04-22 01:02:44 -07:00
|
|
|
|
2012-08-11 15:02:55 -07:00
|
|
|
if (!encoding && data_has_bom (data, len, &bom_enc))
|
|
|
|
{
|
|
|
|
encoding = bom_enc;
|
|
|
|
result = try_convert_to_utf8_from_encoding (data, len, encoding);
|
|
|
|
}
|
|
|
|
else if (!encoding)
|
|
|
|
{
|
2016-01-03 05:13:00 -08:00
|
|
|
std::list<gstr> encodings = get_encodings ();
|
2012-08-11 15:02:55 -07:00
|
|
|
|
|
|
|
if (cached_encoding)
|
2016-01-03 05:13:00 -08:00
|
|
|
encodings.push_front(gstr::make_borrowed(cached_encoding));
|
2012-08-11 15:02:55 -07:00
|
|
|
|
2016-01-01 22:25:53 -08:00
|
|
|
for (auto& enc: encodings)
|
2012-08-11 15:02:55 -07:00
|
|
|
{
|
|
|
|
result = try_convert_to_utf8_from_encoding (data, len, enc);
|
|
|
|
|
|
|
|
if (result != NULL)
|
|
|
|
{
|
2016-01-01 22:25:53 -08:00
|
|
|
used_enc = std::move(enc);
|
2012-08-11 15:02:55 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = try_convert_to_utf8_from_encoding (data, len, encoding);
|
2016-01-03 05:13:00 -08:00
|
|
|
used_enc = gstr::make_borrowed(encoding);
|
2012-08-11 15:02:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-01-04 03:56:42 -08:00
|
|
|
static bool
|
2012-08-11 15:02:55 -07:00
|
|
|
encoding_is_utf8 (const char *encoding)
|
|
|
|
{
|
|
|
|
return !g_ascii_strcasecmp (encoding, "UTF-8") ||
|
|
|
|
!g_ascii_strcasecmp (encoding, "UTF8");
|
|
|
|
}
|