mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Initial support to decode old, ambiguous data files, missing an encoding declaration, with the help of the user.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@13781 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
d9c78bd0fe
commit
14ce276877
25
ChangeLog
25
ChangeLog
@ -1,3 +1,28 @@
|
||||
2006-04-14 Andreas Köhler <andi5.py@gmx.net>
|
||||
|
||||
* lib/libqof/qof/qofbackend.h:
|
||||
* lib/libqof/qof/qofsession.c:
|
||||
* src/backend/file/gnc-backend-file.[ch]:
|
||||
* src/backend/file/io-example-account.[ch]:
|
||||
* src/backend/file/io-gncxml-v1.c:
|
||||
* src/backend/file/test/test-xml2-is-file.c: Add
|
||||
ERR_FILEIO_NO_ENCODING and test whether xml data file declares
|
||||
encoding.
|
||||
|
||||
* src/backend/file/sixtp.[ch]: Add parsing in push mode.
|
||||
|
||||
* src/backend/file/io-gncxml-v2.[ch]: Add analysis of data file to
|
||||
find ambiguous words because of missing encoding, and parsing of
|
||||
it with string substitutions.
|
||||
|
||||
* src/gnome-utils/druid-gnc-xml-import.[ch]:
|
||||
* src/gnome-utils/glade/druid-gnc-xml-import.glade: Add Code and
|
||||
GUI for a druid that enables the user to select from different
|
||||
encodings for each ambiguous word.
|
||||
|
||||
* src/gnome-utils/gnc-file.c: Hook the druid into the file loading
|
||||
process and reload the file if it was successful.
|
||||
|
||||
2006-04-14 Derek Atkins <derek@ihtfp.com>
|
||||
|
||||
* src/engine/Transaction.c:
|
||||
|
@ -116,6 +116,7 @@ typedef enum {
|
||||
ERR_FILEIO_BACKUP_ERROR, /**< couldn't make a backup of the file */
|
||||
ERR_FILEIO_WRITE_ERROR, /**< couldn't write to the file */
|
||||
ERR_FILEIO_READ_ERROR, /**< Could not open the file for reading. */
|
||||
ERR_FILEIO_NO_ENCODING, /**< file does not specify encoding */
|
||||
|
||||
/* network errors */
|
||||
ERR_NETIO_SHORT_READ = 2000, /**< not enough bytes received */
|
||||
|
@ -1113,6 +1113,7 @@ qof_session_load (QofSession *session,
|
||||
err = qof_session_get_error(session);
|
||||
if ((err != ERR_BACKEND_NO_ERR) &&
|
||||
(err != ERR_FILEIO_FILE_TOO_OLD) &&
|
||||
(err != ERR_FILEIO_NO_ENCODING) &&
|
||||
(err != ERR_SQL_DB_TOO_OLD))
|
||||
{
|
||||
/* Something broke, put back the old stuff */
|
||||
|
@ -408,8 +408,13 @@ is_gzipped_file(const gchar *name)
|
||||
static QofBookFileType
|
||||
gnc_file_be_determine_file_type(const char *path)
|
||||
{
|
||||
if (gnc_is_xml_data_file_v2(path)) {
|
||||
return GNC_BOOK_XML2_FILE;
|
||||
gboolean with_encoding;
|
||||
if (gnc_is_xml_data_file_v2(path, &with_encoding)) {
|
||||
if (with_encoding) {
|
||||
return GNC_BOOK_XML2_FILE;
|
||||
} else {
|
||||
return GNC_BOOK_XML2_FILE_NO_ENCODING;
|
||||
}
|
||||
} else if (gnc_is_xml_data_file(path)) {
|
||||
return GNC_BOOK_XML1_FILE;
|
||||
} else if (is_gzipped_file(path)) {
|
||||
@ -435,10 +440,10 @@ gnc_determine_file_type (const char *path)
|
||||
rc = stat(path, &sbuf);
|
||||
if(rc < 0) { return FALSE; }
|
||||
if (sbuf.st_size == 0) { PINFO (" empty file"); return TRUE; }
|
||||
if(gnc_is_xml_data_file_v2(path)) { return TRUE; }
|
||||
else if(gnc_is_xml_data_file(path)) { return TRUE; }
|
||||
else if(is_gzipped_file(path)) { return TRUE; }
|
||||
else if(gnc_is_bin_file(path)) { return TRUE; }
|
||||
if(gnc_is_xml_data_file_v2(path, NULL)) { return TRUE; }
|
||||
else if(gnc_is_xml_data_file(path)) { return TRUE; }
|
||||
else if(is_gzipped_file(path)) { return TRUE; }
|
||||
else if(gnc_is_bin_file(path)) { return TRUE; }
|
||||
PINFO (" %s is not a gnc file", path);
|
||||
return FALSE;
|
||||
}
|
||||
@ -873,6 +878,9 @@ gnc_file_be_load_from_file (QofBackend *bend, QofBook *book)
|
||||
if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
|
||||
break;
|
||||
|
||||
case GNC_BOOK_XML2_FILE_NO_ENCODING:
|
||||
error = ERR_FILEIO_NO_ENCODING;
|
||||
break;
|
||||
case GNC_BOOK_XML1_FILE:
|
||||
rc = qof_session_load_from_xml_file (book, be->fullpath);
|
||||
if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
|
||||
|
@ -57,6 +57,7 @@ typedef enum
|
||||
GNC_BOOK_BIN_FILE,
|
||||
GNC_BOOK_XML1_FILE,
|
||||
GNC_BOOK_XML2_FILE,
|
||||
GNC_BOOK_XML2_FILE_NO_ENCODING,
|
||||
QSF_GNC_OBJECT,
|
||||
QSF_OBJECT,
|
||||
QSF_MAP,
|
||||
|
@ -481,5 +481,5 @@ gnc_load_example_account_list(QofBook *book, const char *dirname)
|
||||
gboolean
|
||||
gnc_is_example_account_xml(const gchar *name)
|
||||
{
|
||||
return gnc_is_our_xml_file(name, GNC_ACCOUNT_STRING);
|
||||
return gnc_is_our_xml_file(name, GNC_ACCOUNT_STRING, NULL);
|
||||
}
|
||||
|
@ -51,8 +51,6 @@ GncExampleAccount *gnc_read_example_account(QofBook *book,
|
||||
const gchar *filename);
|
||||
|
||||
|
||||
gboolean gnc_is_xml_data_file_v2(const gchar *filename);
|
||||
|
||||
void gnc_free_example_account_list(GSList *list);
|
||||
GSList* gnc_load_example_account_list(QofBook *book,
|
||||
const char *dirname);
|
||||
|
@ -403,7 +403,7 @@ qof_session_load_from_xml_file(QofBook *book, const char *filename)
|
||||
gboolean
|
||||
gnc_is_xml_data_file(const gchar *filename)
|
||||
{
|
||||
return gnc_is_our_xml_file(filename, "gnc");
|
||||
return gnc_is_our_xml_file(filename, "gnc", NULL);
|
||||
}
|
||||
|
||||
/* ================================================================== */
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <zlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "gnc-engine.h"
|
||||
#include "gnc-pricedb-p.h"
|
||||
@ -646,16 +647,19 @@ gnc_sixtp_gdv2_new (
|
||||
return gd;
|
||||
}
|
||||
|
||||
gboolean
|
||||
qof_session_load_from_xml_file_v2(FileBackend *fbe, QofBook *book)
|
||||
static gboolean
|
||||
qof_session_load_from_xml_file_v2_full(
|
||||
FileBackend *fbe, QofBook *book,
|
||||
sixtp_push_handler push_handler, gpointer push_user_data)
|
||||
{
|
||||
AccountGroup *grp;
|
||||
AccountGroup *grp;
|
||||
QofBackend *be = &fbe->be;
|
||||
sixtp_gdv2 *gd;
|
||||
sixtp *top_parser;
|
||||
sixtp *main_parser;
|
||||
sixtp *book_parser;
|
||||
struct file_backend be_data;
|
||||
gboolean retval;
|
||||
|
||||
gd = gnc_sixtp_gdv2_new(book, FALSE, file_rw_feedback, be->percentage);
|
||||
|
||||
@ -717,9 +721,22 @@ qof_session_load_from_xml_file_v2(FileBackend *fbe, QofBook *book)
|
||||
xaccLogDisable ();
|
||||
xaccDisableDataScrubbing();
|
||||
|
||||
if(!gnc_xml_parse_file(top_parser, fbe->fullpath,
|
||||
generic_callback, gd, book))
|
||||
{
|
||||
if (push_handler) {
|
||||
gpointer parse_result = NULL;
|
||||
gxpf_data gpdata;
|
||||
|
||||
gpdata.cb = generic_callback;
|
||||
gpdata.parsedata = gd;
|
||||
gpdata.bookdata = book;
|
||||
|
||||
retval = sixtp_parse_push(top_parser, push_handler, push_user_data,
|
||||
NULL, &gpdata, &parse_result);
|
||||
} else {
|
||||
retval = gnc_xml_parse_file(top_parser, fbe->fullpath,
|
||||
generic_callback, gd, book);
|
||||
}
|
||||
|
||||
if (!retval) {
|
||||
sixtp_destroy(top_parser);
|
||||
xaccLogEnable ();
|
||||
xaccEnableDataScrubbing();
|
||||
@ -766,6 +783,12 @@ qof_session_load_from_xml_file_v2(FileBackend *fbe, QofBook *book)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
qof_session_load_from_xml_file_v2(FileBackend *fbe, QofBook *book)
|
||||
{
|
||||
return qof_session_load_from_xml_file_v2_full(fbe, book, NULL, NULL);
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
static void
|
||||
@ -1359,7 +1382,391 @@ gnc_book_write_accounts_to_xml_file_v2(
|
||||
|
||||
/***********************************************************************/
|
||||
gboolean
|
||||
gnc_is_xml_data_file_v2(const gchar *name)
|
||||
gnc_is_xml_data_file_v2(const gchar *name, gboolean *with_encoding)
|
||||
{
|
||||
return gnc_is_our_xml_file(name, GNC_V2_STRING);
|
||||
return gnc_is_our_xml_file(name, GNC_V2_STRING, with_encoding);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
replace_character_references(gchar *string)
|
||||
{
|
||||
gchar *cursor, *semicolon, *tail;
|
||||
glong number;
|
||||
|
||||
for (cursor = strstr(string, "&#");
|
||||
cursor && *cursor;
|
||||
cursor = strstr(cursor, "&#")) {
|
||||
semicolon = strchr(cursor, ';');
|
||||
if (semicolon && *semicolon) {
|
||||
|
||||
/* parse number */
|
||||
errno = 0;
|
||||
if (*(cursor+2) == 'x') {
|
||||
number = strtol(cursor+3, &tail, 16);
|
||||
} else {
|
||||
number = strtol(cursor+2, &tail, 10);
|
||||
}
|
||||
if (errno || tail != semicolon || number < 0 || number > 255) {
|
||||
PWARN("Illegal character reference");
|
||||
return;
|
||||
}
|
||||
|
||||
/* overwrite '&' with the specified character */
|
||||
*cursor = (gchar) number;
|
||||
cursor++;
|
||||
if (*(semicolon+1)) {
|
||||
/* move text after semicolon the the left */
|
||||
tail = g_strdup(semicolon+1);
|
||||
strcpy(cursor, tail);
|
||||
g_free(tail);
|
||||
} else {
|
||||
/* cut here */
|
||||
*cursor = '\0';
|
||||
}
|
||||
|
||||
} else {
|
||||
PWARN("Unclosed character reference");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
conv_free(conv_type *conv) {
|
||||
if (conv) {
|
||||
g_free(conv->utf8_string);
|
||||
g_free(conv);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
conv_list_free(GList *conv_list) {
|
||||
g_list_foreach(conv_list, (GFunc) conv_free, NULL);
|
||||
g_list_free(conv_list);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GQuark encoding;
|
||||
GIConv iconv;
|
||||
} iconv_item_type;
|
||||
|
||||
gint
|
||||
gnc_xml2_find_ambiguous(const gchar *filename, GList *encodings,
|
||||
GHashTable **unique, GHashTable **ambiguous,
|
||||
GList **impossible, GError **error)
|
||||
{
|
||||
GIOChannel *channel=NULL;
|
||||
GIOStatus status;
|
||||
GList *iconv_list=NULL, *conv_list=NULL, *iter;
|
||||
iconv_item_type *iconv_item=NULL, *ascii=NULL;
|
||||
const gchar *enc;
|
||||
GHashTable *processed=NULL;
|
||||
gint n_impossible = 0;
|
||||
gboolean clean_return = FALSE;
|
||||
|
||||
channel = g_io_channel_new_file(filename, "r", error);
|
||||
if (*error) {
|
||||
PWARN("Unable to open file %s", filename);
|
||||
goto cleanup_find_ambs;
|
||||
}
|
||||
|
||||
status = g_io_channel_set_encoding(channel, NULL, error);
|
||||
if (status != G_IO_STATUS_NORMAL) {
|
||||
PWARN("Error on unsetting encoding on IOChannel");
|
||||
goto cleanup_find_ambs;
|
||||
}
|
||||
|
||||
/* we need ascii */
|
||||
ascii = g_new(iconv_item_type, 1);
|
||||
ascii->encoding = g_quark_from_string("ASCII");
|
||||
ascii->iconv = g_iconv_open("UTF-8", "ASCII");
|
||||
if (ascii->iconv == (GIConv) -1) {
|
||||
PWARN("Unable to open ASCII ICONV conversion descriptor");
|
||||
goto cleanup_find_ambs;
|
||||
}
|
||||
|
||||
/* call iconv_open on encodings */
|
||||
for (iter = encodings; iter; iter = iter->next) {
|
||||
iconv_item = g_new(iconv_item_type, 1);
|
||||
iconv_item->encoding = GPOINTER_TO_UINT (iter->data);
|
||||
if (iconv_item->encoding == ascii->encoding) {
|
||||
continue;
|
||||
}
|
||||
|
||||
enc = g_quark_to_string(iconv_item->encoding);
|
||||
iconv_item->iconv = g_iconv_open("UTF-8", enc);
|
||||
if (iconv_item->iconv == (GIConv) -1) {
|
||||
PWARN("Unable to open IConv conversion descriptor for '%s'", enc);
|
||||
goto cleanup_find_ambs;
|
||||
} else {
|
||||
iconv_list = g_list_prepend(iconv_list, iconv_item);
|
||||
}
|
||||
}
|
||||
|
||||
/* prepare data containers */
|
||||
if (unique)
|
||||
*unique = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
|
||||
(GDestroyNotify) conv_free);
|
||||
if (ambiguous)
|
||||
*ambiguous = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
|
||||
(GDestroyNotify) conv_list_free);
|
||||
if (impossible)
|
||||
*impossible = NULL;
|
||||
processed = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
||||
|
||||
/* loop through lines */
|
||||
while (1) {
|
||||
gchar *line, *word, *utf8;
|
||||
gchar **word_array, **word_cursor;
|
||||
conv_type *conv;
|
||||
|
||||
status = g_io_channel_read_line(channel, &line, NULL, NULL, error);
|
||||
if (status == G_IO_STATUS_EOF) {
|
||||
break;
|
||||
}
|
||||
if (status == G_IO_STATUS_AGAIN) {
|
||||
continue;
|
||||
}
|
||||
if (status != G_IO_STATUS_NORMAL) {
|
||||
goto cleanup_find_ambs;
|
||||
}
|
||||
|
||||
g_strchomp(line);
|
||||
replace_character_references(line);
|
||||
word_array = g_strsplit_set(line, "> <", 0);
|
||||
g_free(line);
|
||||
|
||||
/* loop through words */
|
||||
for (word_cursor = word_array; *word_cursor; word_cursor++) {
|
||||
word = *word_cursor;
|
||||
if (!word)
|
||||
continue;
|
||||
|
||||
utf8 = g_convert_with_iconv(word, -1, ascii->iconv,
|
||||
NULL, NULL, error);
|
||||
if (utf8) {
|
||||
/* pure ascii */
|
||||
g_free(utf8);
|
||||
continue;
|
||||
}
|
||||
g_error_free(*error);
|
||||
*error = NULL;
|
||||
|
||||
if (g_hash_table_lookup_extended(processed, word, NULL, NULL)) {
|
||||
/* already processed */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* loop through encodings */
|
||||
conv_list = NULL;
|
||||
for (iter = iconv_list; iter; iter = iter->next) {
|
||||
iconv_item = iter->data;
|
||||
utf8 = g_convert_with_iconv(word, -1, iconv_item->iconv,
|
||||
NULL, NULL, error);
|
||||
if (utf8) {
|
||||
conv = g_new(conv_type, 1);
|
||||
conv->encoding = iconv_item->encoding;
|
||||
conv->utf8_string = utf8;
|
||||
conv_list = g_list_prepend(conv_list, conv);
|
||||
} else {
|
||||
g_error_free(*error);
|
||||
*error = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* no successful conversion */
|
||||
if (!conv_list) {
|
||||
if (impossible)
|
||||
*impossible = g_list_append(*impossible, g_strdup(word));
|
||||
n_impossible++;
|
||||
}
|
||||
|
||||
/* more than one successful conversion */
|
||||
else if (conv_list->next) {
|
||||
if (ambiguous) {
|
||||
g_hash_table_insert(*ambiguous, g_strdup(word), conv_list);
|
||||
} else {
|
||||
conv_list_free(conv_list);
|
||||
}
|
||||
}
|
||||
|
||||
/* only one successful conversion */
|
||||
else {
|
||||
if (unique) {
|
||||
g_hash_table_insert(*unique, g_strdup(word), conv);
|
||||
} else {
|
||||
conv_free(conv);
|
||||
}
|
||||
g_list_free(conv_list);
|
||||
}
|
||||
|
||||
g_hash_table_insert(processed, g_strdup(word), NULL);
|
||||
}
|
||||
g_strfreev(word_array);
|
||||
}
|
||||
|
||||
clean_return = TRUE;
|
||||
|
||||
cleanup_find_ambs:
|
||||
|
||||
if (iconv_list) {
|
||||
for (iter = iconv_list; iter; iter = iter->next) {
|
||||
if (iter->data) {
|
||||
g_iconv_close(((iconv_item_type*) iter->data)->iconv);
|
||||
g_free(iter->data);
|
||||
}
|
||||
}
|
||||
g_list_free(iconv_list);
|
||||
}
|
||||
if (processed)
|
||||
g_hash_table_destroy(processed);
|
||||
if (ascii)
|
||||
g_free(ascii);
|
||||
if (channel)
|
||||
g_io_channel_unref(channel);
|
||||
|
||||
return (clean_return) ? n_impossible : -1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
gchar *filename;
|
||||
GHashTable *subst;
|
||||
} push_data_type;
|
||||
|
||||
static void
|
||||
parse_with_subst_push_handler (xmlParserCtxtPtr xml_context,
|
||||
push_data_type *push_data)
|
||||
{
|
||||
GIOChannel *channel=NULL;
|
||||
GIOStatus status;
|
||||
GIConv ascii=(GIConv)-1;
|
||||
GString *output=NULL;
|
||||
GError *error=NULL;
|
||||
|
||||
channel = g_io_channel_new_file(push_data->filename, "r", &error);
|
||||
if (error) {
|
||||
PWARN("Unable to open file %s", push_data->filename);
|
||||
goto cleanup_push_handler;
|
||||
}
|
||||
|
||||
status = g_io_channel_set_encoding(channel, NULL, &error);
|
||||
if (status != G_IO_STATUS_NORMAL) {
|
||||
PWARN("Error on unsetting encoding on IOChannel");
|
||||
goto cleanup_push_handler;
|
||||
}
|
||||
|
||||
ascii = g_iconv_open("UTF-8", "ASCII");
|
||||
if (ascii == (GIConv) -1) {
|
||||
PWARN("Unable to open ASCII ICONV conversion descriptor");
|
||||
goto cleanup_push_handler;
|
||||
}
|
||||
|
||||
/* loop through lines */
|
||||
while (1) {
|
||||
gchar *line, *word, *repl, *utf8;
|
||||
gint pos, len;
|
||||
gchar *start, *cursor;
|
||||
|
||||
status = g_io_channel_read_line(channel, &line, NULL, NULL, &error);
|
||||
if (status == G_IO_STATUS_EOF) {
|
||||
break;
|
||||
}
|
||||
if (status == G_IO_STATUS_AGAIN) {
|
||||
continue;
|
||||
}
|
||||
if (status != G_IO_STATUS_NORMAL) {
|
||||
goto cleanup_push_handler;
|
||||
}
|
||||
|
||||
replace_character_references(line);
|
||||
output = g_string_new(line);
|
||||
g_free(line);
|
||||
|
||||
/* loop through words */
|
||||
cursor = output->str;
|
||||
pos = 0;
|
||||
while (1) {
|
||||
/* ignore delimiters */
|
||||
while (*cursor=='>' || *cursor==' ' || *cursor=='<' ||
|
||||
*cursor=='\n') {
|
||||
cursor++;
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
if (!*cursor)
|
||||
/* welcome to EOL */
|
||||
break;
|
||||
|
||||
/* search for a delimiter */
|
||||
start = cursor;
|
||||
len = 0;
|
||||
while (*cursor && *cursor!='>' && *cursor!=' ' && *cursor!='<' &&
|
||||
*cursor!='\n') {
|
||||
cursor++;
|
||||
len++;
|
||||
}
|
||||
|
||||
utf8 = g_convert_with_iconv(start, len, ascii, NULL, NULL, &error);
|
||||
|
||||
if (utf8) {
|
||||
/* pure ascii */
|
||||
g_free(utf8);
|
||||
pos += len;
|
||||
} else {
|
||||
g_error_free(error);
|
||||
error = NULL;
|
||||
|
||||
word = g_strndup(start, len);
|
||||
repl = g_hash_table_lookup(push_data->subst, word);
|
||||
g_free(word);
|
||||
if (repl) {
|
||||
/* there is a replacement */
|
||||
output = g_string_insert(g_string_erase(output, pos, len),
|
||||
pos, repl);
|
||||
pos += strlen(repl);
|
||||
cursor = output->str + pos;
|
||||
} else {
|
||||
/* there is no replacement, return immediately */
|
||||
goto cleanup_push_handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlParseChunk(xml_context, output->str, output->len, 0) != 0) {
|
||||
goto cleanup_push_handler;
|
||||
}
|
||||
}
|
||||
|
||||
/* last chunk */
|
||||
xmlParseChunk(xml_context, "", 0, 1);
|
||||
|
||||
cleanup_push_handler:
|
||||
|
||||
if (output)
|
||||
g_string_free(output, TRUE);
|
||||
if (ascii != (GIConv) -1)
|
||||
g_iconv_close(ascii);
|
||||
if (channel)
|
||||
g_io_channel_unref(channel);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gnc_xml2_parse_with_subst (FileBackend *fbe, QofBook *book, GHashTable *subst)
|
||||
{
|
||||
push_data_type *push_data;
|
||||
gboolean success;
|
||||
|
||||
push_data = g_new(push_data_type, 1);
|
||||
push_data->filename = fbe->fullpath;
|
||||
push_data->subst = subst;
|
||||
|
||||
success = qof_session_load_from_xml_file_v2_full(
|
||||
fbe, book, (sixtp_push_handler) parse_with_subst_push_handler,
|
||||
push_data);
|
||||
|
||||
if (success)
|
||||
qof_book_kvp_changed(book);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
@ -142,11 +142,57 @@ gboolean gnc_book_write_accounts_to_xml_file_v2(QofBackend * be, QofBook *book,
|
||||
/** The is_gncxml_file() routine checks to see if the first few
|
||||
* chars of the file look like gnc-xml data.
|
||||
*/
|
||||
gboolean gnc_is_xml_data_file_v2(const gchar *name);
|
||||
gboolean gnc_is_xml_data_file_v2(const gchar *name, gboolean *with_encoding);
|
||||
|
||||
/** Write a name-space declaration for the provided namespace data type
|
||||
* within the GNC XML namespace at http://www.gnucash.org/XML.
|
||||
*/
|
||||
void gnc_xml2_write_namespace_decl (FILE *out, const char *namespace);
|
||||
|
||||
|
||||
typedef struct {
|
||||
GQuark encoding;
|
||||
gchar *utf8_string;
|
||||
} conv_type;
|
||||
|
||||
/** Read a file as plain byte stream to find words that are not completely ASCII.
|
||||
* On error, @unique, @ambiguous and @impossible will be filled up to that point,
|
||||
* @error may contain an io channel error, -1 will be returned.
|
||||
*
|
||||
* @param filename Name of the file to read.
|
||||
*
|
||||
* @param encodings List of encodings to check words for, each begin one a GQuark
|
||||
* in a pointer.
|
||||
*
|
||||
* @param unique Location used for a hash table for unique solutions, if not
|
||||
* NULL. The byte sequence is the key, successful_conversion the value.
|
||||
*
|
||||
* @param ambiguous Location used for a hash table for ambiguous byte sequences,
|
||||
* if not NULL. The byte sequences is the key, a list of successful_conversions
|
||||
* the value.
|
||||
*
|
||||
* @param impossible Location used for a list for undecodable byte sequences,
|
||||
* if not NULL.
|
||||
*
|
||||
* @param error Location to return an io channel error.
|
||||
*
|
||||
* @return Size of impossible, -1 on error.
|
||||
*/
|
||||
gint gnc_xml2_find_ambiguous(
|
||||
const gchar *filename, GList *encodings, GHashTable **unique,
|
||||
GHashTable **ambiguous, GList **impossible, GError **error);
|
||||
typedef gint (*find_ambiguous_handler)(
|
||||
const gchar *filename, GList *encodings, GHashTable **unique,
|
||||
GHashTable **ambiguous, GList **impossible, GError **error);
|
||||
|
||||
/** Parse a file in push mode, but replace byte sequences in the file given a
|
||||
* hash table of substitutions
|
||||
*
|
||||
* @param subst hash table with keys and values of type gchar*
|
||||
*/
|
||||
gboolean gnc_xml2_parse_with_subst (
|
||||
FileBackend *fbe, QofBook *book, GHashTable *subst);
|
||||
typedef gboolean (*parse_with_subst_handler)(
|
||||
FileBackend *fbe, QofBook *book, GHashTable *subst);
|
||||
|
||||
#endif /* __IO_GNCXML_V2_H__ */
|
||||
|
@ -748,6 +748,52 @@ sixtp_parse_buffer(sixtp *sixtp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sixtp_parse_push (sixtp *sixtp,
|
||||
sixtp_push_handler push_handler,
|
||||
gpointer push_user_data,
|
||||
gpointer data_for_top_level,
|
||||
gpointer global_data,
|
||||
gpointer *parse_result)
|
||||
{
|
||||
sixtp_parser_context *ctxt;
|
||||
xmlParserCtxtPtr xml_context;
|
||||
|
||||
if (!push_handler) {
|
||||
PERR("No push handler specified");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(ctxt = sixtp_context_new(sixtp, global_data, data_for_top_level))) {
|
||||
PERR("sixtp_context_new returned null");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
xml_context = xmlCreatePushParserCtxt(&ctxt->handler, &ctxt->data,
|
||||
NULL, 0, NULL);
|
||||
ctxt->data.saxParserCtxt = xml_context;
|
||||
ctxt->data.bad_xml_parser = sixtp_dom_parser_new(gnc_bad_xml_end_handler,
|
||||
NULL, NULL);
|
||||
|
||||
(*push_handler)(xml_context, push_user_data);
|
||||
|
||||
sixtp_context_run_end_handler(ctxt);
|
||||
|
||||
if (ctxt->data.parsing_ok) {
|
||||
if (parse_result)
|
||||
*parse_result = ctxt->top_frame->frame_data;
|
||||
sixtp_context_destroy(ctxt);
|
||||
return TRUE;
|
||||
} else {
|
||||
if (parse_result)
|
||||
*parse_result = NULL;
|
||||
if (g_slist_length(ctxt->data.stack) > 1)
|
||||
sixtp_handle_catastrophe(&ctxt->data);
|
||||
sixtp_context_destroy(ctxt);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
static gboolean
|
||||
eat_whitespace(unsigned char **cursor)
|
||||
@ -787,7 +833,8 @@ search_for(unsigned char marker, unsigned char **cursor)
|
||||
}
|
||||
|
||||
gboolean
|
||||
gnc_is_our_xml_file(const char *filename, const char *first_tag)
|
||||
gnc_is_our_xml_file(const char *filename, const char *first_tag,
|
||||
gboolean *with_encoding)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
char first_chunk[256];
|
||||
@ -796,6 +843,10 @@ gnc_is_our_xml_file(const char *filename, const char *first_tag)
|
||||
|
||||
g_return_val_if_fail(filename, FALSE);
|
||||
g_return_val_if_fail(first_tag, FALSE);
|
||||
|
||||
if (with_encoding) {
|
||||
*with_encoding = FALSE;
|
||||
}
|
||||
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
@ -838,6 +889,18 @@ gnc_is_our_xml_file(const char *filename, const char *first_tag)
|
||||
|
||||
result = (strncmp(cursor, tag_compare, strlen(tag_compare)) == 0);
|
||||
g_free (tag_compare);
|
||||
|
||||
if (result && with_encoding) {
|
||||
*cursor = '\0';
|
||||
cursor = first_chunk;
|
||||
while (search_for('e', &cursor)) {
|
||||
if (strncmp(cursor, "ncoding=", 8) == 0) {
|
||||
*with_encoding = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
|
@ -86,6 +86,9 @@ typedef void (*sixtp_fail_handler)(gpointer data_for_children,
|
||||
gpointer *result,
|
||||
const gchar *tag);
|
||||
|
||||
typedef void (*sixtp_push_handler)(xmlParserCtxtPtr xml_context,
|
||||
gpointer user_data);
|
||||
|
||||
typedef struct sixtp
|
||||
{
|
||||
/* If you change this, don't forget to modify all the copy/etc. functions */
|
||||
@ -180,6 +183,9 @@ gboolean sixtp_parse_file(sixtp *sixtp, const char *filename,
|
||||
gboolean sixtp_parse_buffer(sixtp *sixtp, char *bufp, int bufsz,
|
||||
gpointer data_for_top_level, gpointer global_data,
|
||||
gpointer *parse_result);
|
||||
gboolean sixtp_parse_push(sixtp *sixtp, sixtp_push_handler push_handler,
|
||||
gpointer push_user_data, gpointer data_for_top_level,
|
||||
gpointer global_data, gpointer *parse_result);
|
||||
|
||||
void sixtp_set_start(sixtp *parser, sixtp_start_handler start_handler);
|
||||
void sixtp_set_before_child(sixtp *parser, sixtp_before_child_handler handler);
|
||||
@ -198,7 +204,8 @@ sixtp* sixtp_add_some_sub_parsers(sixtp *tochange, gboolean cleanup, ...);
|
||||
gboolean sixtp_add_sub_parser(sixtp *parser, const gchar* tag,
|
||||
sixtp *sub_parser);
|
||||
|
||||
gboolean gnc_is_our_xml_file(const char *filename, const char *first_tag);
|
||||
gboolean gnc_is_our_xml_file(const char *filename, const char *first_tag,
|
||||
gboolean *with_encoding);
|
||||
|
||||
|
||||
#endif /* _SIXTP_H_ */
|
||||
|
@ -22,7 +22,7 @@ main(int argc, char **argv)
|
||||
|
||||
filename = malloc(strlen(directory) + 1 + strlen(FILENAME) + 1);
|
||||
sprintf(filename, "%s/%s", directory, FILENAME);
|
||||
do_test(gnc_is_xml_data_file_v2(filename), "gnc_is_xml_data_file_v2");
|
||||
do_test(gnc_is_xml_data_file_v2(filename, NULL), "gnc_is_xml_data_file_v2");
|
||||
|
||||
print_test_results();
|
||||
exit(get_rv());
|
||||
|
@ -48,6 +48,7 @@ libgncmod_gnome_utils_la_SOURCES = \
|
||||
dialog-utils.c \
|
||||
druid-utils.c \
|
||||
druid-gconf-setup.c \
|
||||
druid-gnc-xml-import.c \
|
||||
gnc-account-sel.c \
|
||||
gnc-amount-edit.c \
|
||||
gnc-commodity-edit.c \
|
||||
@ -116,6 +117,7 @@ gncinclude_HEADERS = \
|
||||
dialog-utils.h \
|
||||
druid-utils.h \
|
||||
druid-gconf-setup.h \
|
||||
druid-gnc-xml-import.h \
|
||||
gnc-account-sel.h \
|
||||
gnc-amount-edit.h \
|
||||
gnc-commodity-edit.h \
|
||||
|
1518
src/gnome-utils/druid-gnc-xml-import.c
Normal file
1518
src/gnome-utils/druid-gnc-xml-import.c
Normal file
File diff suppressed because it is too large
Load Diff
33
src/gnome-utils/druid-gnc-xml-import.h
Normal file
33
src/gnome-utils/druid-gnc-xml-import.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* druid-gnc-xml-import.h --
|
||||
* Copyright (C) 2006 Andreas Koehler <andi5.py@gmx.net>
|
||||
*
|
||||
* 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, contact:
|
||||
*
|
||||
* Free Software Foundation Voice: +1-617-542-5942
|
||||
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||
* Boston, MA 02110-1301, USA gnu@gnu.org
|
||||
*/
|
||||
|
||||
#ifndef DRUID_GNC_XML_IMPORT_H
|
||||
#define DRUID_GNC_XML_IMPORT_H
|
||||
|
||||
#include "qof.h"
|
||||
|
||||
gboolean gnc_xml_convert_single_file (const gchar *filename);
|
||||
|
||||
/* this is NOT fully implemented */
|
||||
void gnc_xml_merge_files (void);
|
||||
|
||||
#endif /* DRUID_GNC_XML_IMPORT_H */
|
@ -6,6 +6,7 @@ glade_DATA = \
|
||||
druid-provider-multifile.glade \
|
||||
exchange-dialog.glade \
|
||||
druid-gconf-setup.glade \
|
||||
druid-gnc-xml-import.glade \
|
||||
gnc-date-format.glade \
|
||||
gnc-gui-query.glade \
|
||||
preferences.glade \
|
||||
|
1161
src/gnome-utils/glade/druid-gnc-xml-import.glade
Normal file
1161
src/gnome-utils/glade/druid-gnc-xml-import.glade
Normal file
File diff suppressed because it is too large
Load Diff
@ -29,6 +29,7 @@
|
||||
#include <g-wrap-wct.h>
|
||||
|
||||
#include "dialog-utils.h"
|
||||
#include "druid-gnc-xml-import.h"
|
||||
#include "gnc-commodity.h"
|
||||
#include "gnc-component-manager.h"
|
||||
#include "gnc-engine.h"
|
||||
@ -740,6 +741,22 @@ gnc_post_file_open (const char * filename)
|
||||
|
||||
/* check for i/o error, put up appropriate error dialog */
|
||||
io_err = qof_session_get_error (new_session);
|
||||
|
||||
if (io_err == ERR_FILEIO_NO_ENCODING) {
|
||||
qof_session_pop_error (new_session);
|
||||
if (gnc_xml_convert_single_file (newfile)) {
|
||||
/* try to load once again */
|
||||
gnc_window_show_progress(_("Reading file..."), 0.0);
|
||||
qof_session_load (new_session, gnc_window_show_progress);
|
||||
gnc_window_show_progress(NULL, -1.0);
|
||||
xaccLogEnable();
|
||||
io_err = qof_session_get_error (new_session);
|
||||
}
|
||||
else {
|
||||
io_err = ERR_FILEIO_PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
uh_oh = show_session_error (io_err, newfile, GNC_FILE_DIALOG_OPEN);
|
||||
|
||||
new_group = gnc_book_get_group (qof_session_get_book (new_session));
|
||||
|
Loading…
Reference in New Issue
Block a user