r16173 - gnucash/branches/csv-import - Initial commit of csv-import.
Benjamin Sperisen
lasindi at cvs.gnucash.org
Sat Jun 16 21:23:41 EDT 2007
Author: lasindi
Date: 2007-06-16 21:23:40 -0400 (Sat, 16 Jun 2007)
New Revision: 16173
Trac: http://svn.gnucash.org/trac/changeset/16173
Added:
gnucash/branches/csv-import/lib/stf/
gnucash/branches/csv-import/lib/stf/Makefile.am
gnucash/branches/csv-import/lib/stf/README
gnucash/branches/csv-import/lib/stf/stf-parse.c
gnucash/branches/csv-import/lib/stf/stf-parse.h
gnucash/branches/csv-import/src/import-export/csv/Makefile.am
gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c
gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.h
gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c
gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h
gnucash/branches/csv-import/src/import-export/csv/gnc-csv-preview-dialog.glade
gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv-ui.xml
gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv.c
gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv.h
gnucash/branches/csv-import/src/import-export/csv/gncmod-csv-import.c
Removed:
gnucash/branches/csv-import/src/import-export/csv/gnc-csv2glist.c
gnucash/branches/csv-import/src/import-export/csv/gnc-csv2glist.h
Modified:
gnucash/branches/csv-import/configure.in
gnucash/branches/csv-import/lib/Makefile.am
gnucash/branches/csv-import/src/bin/gnucash-bin.c
gnucash/branches/csv-import/src/import-export/Makefile.am
Log:
Initial commit of csv-import.
Rudimentary preview and configuration works, but with minimal error
checking and lots of bugs.
Modified: gnucash/branches/csv-import/configure.in
===================================================================
--- gnucash/branches/csv-import/configure.in 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/configure.in 2007-06-17 01:23:40 UTC (rev 16173)
@@ -1424,6 +1424,7 @@
lib/libqof/qof/Makefile
lib/libqof/backend/Makefile
lib/libqof/backend/file/Makefile
+ lib/stf/Makefile
packaging/Makefile
packaging/win32/Makefile
packaging/win32/gnucash.iss
@@ -1477,6 +1478,7 @@
src/import-export/schemas/Makefile
src/import-export/ofx/Makefile
src/import-export/ofx/test/Makefile
+ src/import-export/csv/Makefile
src/import-export/log-replay/Makefile
src/import-export/hbci/Makefile
src/import-export/hbci/glade/Makefile
Modified: gnucash/branches/csv-import/lib/Makefile.am
===================================================================
--- gnucash/branches/csv-import/lib/Makefile.am 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/lib/Makefile.am 2007-06-17 01:23:40 UTC (rev 16173)
@@ -1,5 +1,5 @@
-SUBDIRS = libc glib28 guile-www srfi
-DIST_SUBDIRS = libc glib28 guile-www srfi libqof
+SUBDIRS = libc glib28 guile-www srfi stf
+DIST_SUBDIRS = libc glib28 guile-www srfi libqof stf
if USE_LIBQOF
SUBDIRS += libqof
Added: gnucash/branches/csv-import/lib/stf/Makefile.am
===================================================================
--- gnucash/branches/csv-import/lib/stf/Makefile.am 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/lib/stf/Makefile.am 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,13 @@
+noinst_LTLIBRARIES = libgnc-stf.la
+
+REALSRCS = stf-parse.c
+REALHDRS = stf-parse.h
+
+libgnc_stf_la_SOURCES = ${REALSRCS}
+noinst_HEADERS = ${REALHDRS}
+
+libgnc_stf_la_LDFLAGS = $(pkg-config --libs libgoffice-0.3)
+
+AM_CFLAGS = $(GOFFICE_CFLAGS)
+
+EXTRA_DIST = $(REALSRCS) $(REALHDRS)
Added: gnucash/branches/csv-import/lib/stf/README
===================================================================
--- gnucash/branches/csv-import/lib/stf/README 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/lib/stf/README 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,2 @@
+This consists of code taken from Gnumeric's Structured Text Format
+parser. It is used for the CSV/Fixed-Width file importer.
Added: gnucash/branches/csv-import/lib/stf/stf-parse.c
===================================================================
--- gnucash/branches/csv-import/lib/stf/stf-parse.c 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/lib/stf/stf-parse.c 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,1411 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * stf-parse.c : Structured Text Format parser. (STF)
+ * A general purpose engine for parsing data
+ * in CSV and Fixed width format.
+ *
+ *
+ * Copyright (C) Almer. S. Tigelaar.
+ * EMail: almer1 at dds.nl or almer-t at bigfoot.com
+ *
+ * Copyright (C) 2003 Andreas J. Guelzow <aguelzow at taliesin.ca>
+ * Copyright (C) 2003 Morten Welinder <terra at gnome.org>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define GETTEXT_PACKAGE gnumeric
+
+#include <glib/gi18n-lib.h>
+/* #include "gnumeric.h" */
+#include "stf-parse.h"
+
+/* #include "workbook.h" */
+/* #include "cell.h" */
+/* #include "sheet.h" */
+/* #include "clipboard.h" */
+/* #include "sheet-style.h" */
+/* #include "value.h" */
+/* #include "mstyle.h" */
+/* #include "number-match.h" */
+/* #include "gutils.h" */
+/* #include "parse-util.h" */
+#include <goffice/utils/go-glib-extras.h>
+#include <goffice/utils/go-locale.h>
+#include <goffice/utils/go-format.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <locale.h>
+#include <string.h>
+#include <math.h>
+
+#define SETUP_LOCALE_SWITCH char *oldlocale = NULL
+
+#define START_LOCALE_SWITCH if (parseoptions->locale) {\
+oldlocale = g_strdup(go_setlocale (LC_ALL, NULL)); \
+go_setlocale(LC_ALL, parseoptions->locale);}
+
+#define END_LOCALE_SWITCH if (oldlocale) {\
+go_setlocale(LC_ALL, oldlocale);\
+g_free (oldlocale);}
+
+/* Source_t struct, used for interchanging parsing information between the low level parse functions */
+typedef struct {
+ GStringChunk *chunk;
+ char const *position; /* Indicates the current position within data */
+
+ /* Used internally for fixed width parsing */
+ int splitpos; /* Indicates current position in splitpositions array */
+ int linepos; /* Position on the current line */
+} Source_t;
+
+/* Struct used for autodiscovery */
+typedef struct {
+ int start;
+ int stop;
+} AutoDiscovery_t;
+
+/*
+ * Some silly dude make the length field an unsigned int. C just does
+ * not deal very well with that.
+ */
+static inline int
+my_garray_len (GArray const *a)
+{
+ return (int)a->len;
+}
+
+static inline int
+my_gptrarray_len (GPtrArray const *a)
+{
+ return (int)a->len;
+}
+
+static int
+compare_terminator (char const *s, StfParseOptions_t *parseoptions)
+{
+ guchar const *us = (guchar const *)s;
+ GSList *l;
+
+ if (*us > parseoptions->compiled_terminator.max ||
+ *us < parseoptions->compiled_terminator.min)
+ return 0;
+
+ for (l = parseoptions->terminator; l; l = l->next) {
+ char const *term = l->data;
+ char const *d = s;
+
+ while (*term) {
+ if (*d != *term)
+ goto next;
+ term++;
+ d++;
+ }
+ return d - s;
+
+ next:
+ ;
+ }
+ return 0;
+}
+
+
+/*******************************************************************************************************
+ * STF PARSE OPTIONS : StfParseOptions related
+ *******************************************************************************************************/
+
+/**
+ * stf_parse_options_new:
+ *
+ * This will return a new StfParseOptions_t struct.
+ * The struct should, after being used, freed with stf_parse_options_free.
+ **/
+StfParseOptions_t *
+stf_parse_options_new (void)
+{
+ StfParseOptions_t* parseoptions = g_new0 (StfParseOptions_t, 1);
+
+ parseoptions->parsetype = PARSE_TYPE_NOTSET;
+
+ parseoptions->terminator = NULL;
+ stf_parse_options_add_line_terminator (parseoptions, "\r\n");
+ stf_parse_options_add_line_terminator (parseoptions, "\n");
+ stf_parse_options_add_line_terminator (parseoptions, "\r");
+
+ parseoptions->trim_spaces = (TRIM_TYPE_RIGHT | TRIM_TYPE_LEFT);
+ parseoptions->locale = NULL;
+
+ parseoptions->splitpositions = NULL;
+ stf_parse_options_fixed_splitpositions_clear (parseoptions);
+
+ parseoptions->stringindicator = '"';
+ parseoptions->indicator_2x_is_single = TRUE;
+ parseoptions->duplicates = FALSE;
+ parseoptions->trim_seps = FALSE;
+
+ parseoptions->sep.str = NULL;
+ parseoptions->sep.chr = NULL;
+
+ parseoptions->col_import_array = NULL;
+ parseoptions->col_import_array_len = 0;
+ parseoptions->formats = NULL;
+
+ parseoptions->cols_exceeded = FALSE;
+
+ return parseoptions;
+}
+
+/**
+ * stf_parse_options_free:
+ *
+ * will free @parseoptions, note that this will not free the splitpositions
+ * member (GArray) of the struct, the caller is responsible for that.
+ **/
+void
+stf_parse_options_free (StfParseOptions_t *parseoptions)
+{
+ g_return_if_fail (parseoptions != NULL);
+
+ g_free (parseoptions->col_import_array);
+ g_free (parseoptions->locale);
+ g_free (parseoptions->sep.chr);
+
+ if (parseoptions->sep.str) {
+ GSList *l;
+
+ for (l = parseoptions->sep.str; l != NULL; l = l->next)
+ g_free ((char *) l->data);
+ g_slist_free (parseoptions->sep.str);
+ }
+
+ g_array_free (parseoptions->splitpositions, TRUE);
+
+ stf_parse_options_clear_line_terminator (parseoptions);
+
+ if (parseoptions->formats) {
+ unsigned int ui;
+ GPtrArray *formats = parseoptions->formats;
+
+ for (ui = 0; ui < formats->len; ui++)
+ go_format_unref (g_ptr_array_index (formats, ui));
+ g_ptr_array_free (formats, TRUE);
+ parseoptions->formats = NULL;
+ }
+
+ g_free (parseoptions);
+}
+
+void
+stf_parse_options_set_type (StfParseOptions_t *parseoptions, StfParseType_t const parsetype)
+{
+ g_return_if_fail (parseoptions != NULL);
+ g_return_if_fail (parsetype == PARSE_TYPE_CSV || parsetype == PARSE_TYPE_FIXED);
+
+ parseoptions->parsetype = parsetype;
+}
+
+static gint
+long_string_first (gchar const *a, gchar const *b)
+{
+ /* This actually is UTF-8 safe. */
+ return strlen (b) - strlen (a);
+}
+
+static void
+compile_terminators (StfParseOptions_t *parseoptions)
+{
+ GSList *l;
+ GO_SLIST_SORT (parseoptions->terminator, (GCompareFunc)long_string_first);
+
+ parseoptions->compiled_terminator.min = 255;
+ parseoptions->compiled_terminator.max = 0;
+ for (l = parseoptions->terminator; l; l = l->next) {
+ const guchar *term = l->data;
+ parseoptions->compiled_terminator.min =
+ MIN (parseoptions->compiled_terminator.min, *term);
+ parseoptions->compiled_terminator.max =
+ MAX (parseoptions->compiled_terminator.max, *term);
+ }
+}
+
+/**
+ * stf_parse_options_add_line_terminator:
+ *
+ * This will add to the line terminators, in both the Fixed width and CSV delimited importers
+ * this indicates the end of a row.
+ *
+ **/
+void
+stf_parse_options_add_line_terminator (StfParseOptions_t *parseoptions, char const *terminator)
+{
+ g_return_if_fail (parseoptions != NULL);
+ g_return_if_fail (terminator != NULL && *terminator != 0);
+
+ GO_SLIST_PREPEND (parseoptions->terminator, g_strdup (terminator));
+ compile_terminators (parseoptions);
+}
+
+/**
+ * stf_parse_options_clear_line_terminator:
+ *
+ * This will clear the line terminator, in both the Fixed width and CSV delimited importers
+ * this indicates the end of a row.
+ *
+ **/
+void
+stf_parse_options_clear_line_terminator (StfParseOptions_t *parseoptions)
+{
+ g_return_if_fail (parseoptions != NULL);
+
+ go_slist_free_custom (parseoptions->terminator, g_free);
+ parseoptions->terminator = NULL;
+ compile_terminators (parseoptions);
+}
+
+/**
+ * stf_parse_options_set_trim_spaces:
+ *
+ * If enabled will trim spaces in every parsed field on left and/or right
+ * sides.
+ **/
+void
+stf_parse_options_set_trim_spaces (StfParseOptions_t *parseoptions, StfTrimType_t const trim_spaces)
+{
+ g_return_if_fail (parseoptions != NULL);
+
+ parseoptions->trim_spaces = trim_spaces;
+}
+
+/**
+ * stf_parse_options_csv_set_separators:
+ *
+ * A copy is made of the parameters.
+ **/
+void
+stf_parse_options_csv_set_separators (StfParseOptions_t *parseoptions, char const *character,
+ GSList const *string)
+{
+ g_return_if_fail (parseoptions != NULL);
+
+ g_free (parseoptions->sep.chr);
+ parseoptions->sep.chr = g_strdup (character);
+
+ go_slist_free_custom (parseoptions->sep.str, g_free);
+ parseoptions->sep.str = go_slist_map (string, (GOMapFunc)g_strdup);
+}
+
+void
+stf_parse_options_csv_set_stringindicator (StfParseOptions_t *parseoptions, gunichar const stringindicator)
+{
+ g_return_if_fail (parseoptions != NULL);
+ g_return_if_fail (stringindicator != '\0');
+
+ parseoptions->stringindicator = stringindicator;
+}
+
+/**
+ * stf_parse_options_csv_set_indicator_2x_is_single:
+ * @indic_2x : a boolean value indicating whether we want to see two
+ * adjacent string indicators as a single string indicator
+ * that is part of the cell, rather than a terminator.
+ **/
+void
+stf_parse_options_csv_set_indicator_2x_is_single (StfParseOptions_t *parseoptions,
+ gboolean const indic_2x)
+{
+ g_return_if_fail (parseoptions != NULL);
+
+ parseoptions->indicator_2x_is_single = indic_2x;
+}
+
+/**
+ * stf_parse_options_csv_set_duplicates:
+ * @duplicates : a boolean value indicating whether we want to see two
+ * separators right behind each other as one
+ **/
+void
+stf_parse_options_csv_set_duplicates (StfParseOptions_t *parseoptions, gboolean const duplicates)
+{
+ g_return_if_fail (parseoptions != NULL);
+
+ parseoptions->duplicates = duplicates;
+}
+
+/**
+ * stf_parse_options_csv_set_trim_seps:
+ * @trim_seps : a boolean value indicating whether we want to ignore
+ * separators at the beginning of lines
+ **/
+void
+stf_parse_options_csv_set_trim_seps (StfParseOptions_t *parseoptions, gboolean const trim_seps)
+{
+ g_return_if_fail (parseoptions != NULL);
+
+ parseoptions->trim_seps = trim_seps;
+}
+
+/**
+ * stf_parse_options_fixed_splitpositions_clear:
+ *
+ * This will clear the splitpositions (== points on which a line is split)
+ **/
+void
+stf_parse_options_fixed_splitpositions_clear (StfParseOptions_t *parseoptions)
+{
+ int minus_one = -1;
+ g_return_if_fail (parseoptions != NULL);
+
+ if (parseoptions->splitpositions)
+ g_array_free (parseoptions->splitpositions, TRUE);
+ parseoptions->splitpositions = g_array_new (FALSE, FALSE, sizeof (int));
+
+ g_array_append_val (parseoptions->splitpositions, minus_one);
+}
+
+/**
+ * stf_parse_options_fixed_splitpositions_add:
+ *
+ * @position will be added to the splitpositions.
+ **/
+void
+stf_parse_options_fixed_splitpositions_add (StfParseOptions_t *parseoptions, int position)
+{
+ unsigned int ui;
+
+ g_return_if_fail (parseoptions != NULL);
+ g_return_if_fail (position >= 0);
+
+ for (ui = 0; ui < parseoptions->splitpositions->len - 1; ui++) {
+ int here = g_array_index (parseoptions->splitpositions, int, ui);
+ if (position == here)
+ return;
+ if (position < here)
+ break;
+ }
+
+ g_array_insert_val (parseoptions->splitpositions, ui, position);
+}
+
+void
+stf_parse_options_fixed_splitpositions_remove (StfParseOptions_t *parseoptions, int position)
+{
+ unsigned int ui;
+
+ g_return_if_fail (parseoptions != NULL);
+ g_return_if_fail (position >= 0);
+
+ for (ui = 0; ui < parseoptions->splitpositions->len - 1; ui++) {
+ int here = g_array_index (parseoptions->splitpositions, int, ui);
+ if (position == here)
+ g_array_remove_index (parseoptions->splitpositions, ui);
+ if (position <= here)
+ return;
+ }
+}
+
+int
+stf_parse_options_fixed_splitpositions_count (StfParseOptions_t *parseoptions)
+{
+ return parseoptions->splitpositions->len;
+}
+
+int
+stf_parse_options_fixed_splitpositions_nth (StfParseOptions_t *parseoptions, int n)
+{
+ return g_array_index (parseoptions->splitpositions, int, n);
+}
+
+
+/**
+ * stf_parse_options_valid:
+ * @parseoptions : an import options struct
+ *
+ * Checks if @parseoptions is correctly filled
+ *
+ * returns : TRUE if it is correctly filled, FALSE otherwise.
+ **/
+static gboolean
+stf_parse_options_valid (StfParseOptions_t *parseoptions)
+{
+ g_return_val_if_fail (parseoptions != NULL, FALSE);
+
+ if (parseoptions->parsetype == PARSE_TYPE_CSV) {
+ if (parseoptions->stringindicator == '\0') {
+ g_warning ("STF: Cannot have \\0 as string indicator");
+ return FALSE;
+ }
+
+ } else if (parseoptions->parsetype == PARSE_TYPE_FIXED) {
+ if (!parseoptions->splitpositions) {
+ g_warning ("STF: No splitpositions in struct");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*******************************************************************************************************
+ * STF PARSE : The actual routines that do the 'trick'
+ *******************************************************************************************************/
+
+static void
+trim_spaces_inplace (char *field, StfParseOptions_t const *parseoptions)
+{
+ if (!field) return;
+
+ if (parseoptions->trim_spaces & TRIM_TYPE_LEFT) {
+ char *s = field;
+
+ while (g_unichar_isspace (g_utf8_get_char (s)))
+ s = g_utf8_next_char (s);
+
+ if (s != field)
+ strcpy (field, s);
+ }
+
+ if (parseoptions->trim_spaces & TRIM_TYPE_RIGHT) {
+ char *s = field + strlen (field);
+
+ while (field != s) {
+ s = g_utf8_prev_char (s);
+ if (!g_unichar_isspace (g_utf8_get_char (s)))
+ break;
+ *s = 0;
+ }
+ }
+}
+
+/**
+ * stf_parse_csv_is_separator:
+ *
+ * returns NULL if @character is not a separator, a pointer to the character
+ * after the separator otherwise.
+ **/
+static char const *
+stf_parse_csv_is_separator (char const *character, char const *chr, GSList const *str)
+{
+ g_return_val_if_fail (character != NULL, NULL);
+
+ if (*character == 0)
+ return NULL;
+
+ if (str) {
+ GSList const *l;
+
+ for (l = str; l != NULL; l = l->next) {
+ char const *s = l->data;
+ char const *r;
+ glong cnt;
+ glong const len = g_utf8_strlen (s, -1);
+
+ /* Don't compare past the end of the buffer! */
+ for (r = character, cnt = 0; cnt < len; cnt++, r = g_utf8_next_char (r))
+ if (*r == '\0')
+ break;
+
+ if ((cnt == len) && (memcmp (character, s, len) == 0))
+ return g_utf8_offset_to_pointer (character, len);
+ }
+ }
+
+ if (chr && g_utf8_strchr (chr, -1,
+ g_utf8_get_char (character)))
+ return g_utf8_next_char(character);
+
+ return NULL;
+}
+
+/*
+ * stf_parse_eat_separators:
+ *
+ * skip over leading separators
+ *
+ */
+
+static void
+stf_parse_eat_separators (Source_t *src, StfParseOptions_t *parseoptions)
+{
+ char const *cur, *next;
+
+ g_return_if_fail (src != NULL);
+ g_return_if_fail (parseoptions != NULL);
+
+ cur = src->position;
+
+ if (*cur == '\0' || compare_terminator (cur, parseoptions))
+ return;
+ while ((next = stf_parse_csv_is_separator (cur, parseoptions->sep.chr, parseoptions->sep.str)))
+ cur = next;
+ src->position = cur;
+ return;
+}
+
+
+typedef enum {
+ STF_CELL_ERROR,
+ STF_CELL_EOF,
+ STF_CELL_EOL,
+ STF_CELL_FIELD_NO_SEP,
+ STF_CELL_FIELD_SEP,
+} StfParseCellRes;
+
+static StfParseCellRes
+stf_parse_csv_cell (GString *text, Source_t *src, StfParseOptions_t *parseoptions)
+{
+ char const *cur;
+ gboolean saw_sep = FALSE;
+
+ g_return_val_if_fail (src != NULL, STF_CELL_ERROR);
+ g_return_val_if_fail (parseoptions != NULL, STF_CELL_ERROR);
+
+ cur = src->position;
+ g_return_val_if_fail (cur != NULL, STF_CELL_ERROR);
+
+ /* Skip whitespace, but stop at line terminators. */
+ while (1) {
+ int term_len;
+
+ if (*cur == 0) {
+ src->position = cur;
+ return STF_CELL_EOF;
+ }
+
+ term_len = compare_terminator (cur, parseoptions);
+ if (term_len) {
+ src->position = cur + term_len;
+ return STF_CELL_EOL;
+ }
+
+ if ((parseoptions->trim_spaces & TRIM_TYPE_LEFT) == 0)
+ break;
+
+ if (stf_parse_csv_is_separator (cur, parseoptions->sep.chr,
+ parseoptions->sep.str))
+ break;
+
+ if (!g_unichar_isspace (g_utf8_get_char (cur)))
+ break;
+ cur = g_utf8_next_char (cur);
+ }
+
+ if (g_utf8_get_char (cur) == parseoptions->stringindicator) {
+ cur = g_utf8_next_char (cur);
+ while (*cur) {
+ gunichar uc = g_utf8_get_char (cur);
+ cur = g_utf8_next_char (cur);
+
+ if (uc == parseoptions->stringindicator) {
+ if (parseoptions->indicator_2x_is_single &&
+ g_utf8_get_char (cur) == parseoptions->stringindicator)
+ cur = g_utf8_next_char (cur);
+ else {
+ /* "field content"dropped-garbage, */
+ while (*cur && !compare_terminator (cur, parseoptions)) {
+ char const *post = stf_parse_csv_is_separator
+ (cur, parseoptions->sep.chr, parseoptions->sep.str);
+ if (post) {
+ cur = post;
+ saw_sep = TRUE;
+ break;
+ }
+ cur = g_utf8_next_char (cur);
+ }
+ break;
+ }
+ }
+
+ g_string_append_unichar (text, uc);
+ }
+
+ /* We silently allow a missing terminating quote. */
+ } else {
+ /* Unquoted field. */
+
+ while (*cur && !compare_terminator (cur, parseoptions)) {
+
+ char const *post = stf_parse_csv_is_separator
+ (cur, parseoptions->sep.chr, parseoptions->sep.str);
+ if (post) {
+ cur = post;
+ saw_sep = TRUE;
+ break;
+ }
+
+ g_string_append_unichar (text, g_utf8_get_char (cur));
+ cur = g_utf8_next_char (cur);
+ }
+
+ if (parseoptions->trim_spaces & TRIM_TYPE_RIGHT) {
+ while (text->len) {
+ const char *last = g_utf8_prev_char (text->str + text->len);
+ if (!g_unichar_isspace (g_utf8_get_char (last)))
+ break;
+ g_string_truncate (text, last - text->str);
+ }
+ }
+ }
+
+ src->position = cur;
+
+ if (saw_sep && parseoptions->duplicates)
+ stf_parse_eat_separators (src, parseoptions);
+
+ return saw_sep ? STF_CELL_FIELD_SEP : STF_CELL_FIELD_NO_SEP;
+}
+
+/**
+ * stf_parse_csv_line:
+ *
+ * This will parse one line from the current @src->position.
+ * NOTE: The calling routine is responsible for freeing the result.
+ *
+ * returns : a GPtrArray of char*'s
+ **/
+static GPtrArray *
+stf_parse_csv_line (Source_t *src, StfParseOptions_t *parseoptions)
+{
+ GPtrArray *line;
+ gboolean cont = FALSE;
+
+ g_return_val_if_fail (src != NULL, NULL);
+ g_return_val_if_fail (parseoptions != NULL, NULL);
+
+ line = g_ptr_array_new ();
+ if (parseoptions->trim_seps)
+ stf_parse_eat_separators (src, parseoptions);
+
+ while (1) {
+ GString *text = g_string_sized_new (30);
+ StfParseCellRes res =
+ stf_parse_csv_cell (text, src, parseoptions);
+ trim_spaces_inplace (text->str, parseoptions);
+ switch (res) {
+ case STF_CELL_FIELD_NO_SEP:
+ g_ptr_array_add (line, g_string_free (text, FALSE));
+ cont = FALSE;
+ break;
+
+ case STF_CELL_FIELD_SEP:
+ g_ptr_array_add (line, g_string_free (text, FALSE));
+ cont = TRUE; /* Make sure we see one more field. */
+ break;
+
+ default:
+ if (cont)
+ g_ptr_array_add (line, g_string_free (text, FALSE));
+ else
+ g_string_free (text, TRUE);
+ return line;
+ }
+ }
+}
+
+/**
+ * stf_parse_fixed_cell:
+ *
+ * returns a pointer to the parsed cell contents.
+ **/
+static char *
+stf_parse_fixed_cell (Source_t *src, StfParseOptions_t *parseoptions)
+{
+ char *res;
+ char const *cur;
+ int splitval;
+
+ g_return_val_if_fail (src != NULL, NULL);
+ g_return_val_if_fail (parseoptions != NULL, NULL);
+
+ cur = src->position;
+
+ if (src->splitpos < my_garray_len (parseoptions->splitpositions))
+ splitval = (int) g_array_index (parseoptions->splitpositions, int, src->splitpos);
+ else
+ splitval = -1;
+
+ while (*cur != 0 && !compare_terminator (cur, parseoptions) && splitval != src->linepos) {
+ src->linepos++;
+ cur = g_utf8_next_char (cur);
+ }
+
+ res = g_string_chunk_insert_len (src->chunk,
+ src->position,
+ cur - src->position);
+
+ src->position = cur;
+
+ return res;
+}
+
+/**
+ * stf_parse_fixed_line:
+ *
+ * This will parse one line from the current @src->position.
+ * It will return a GPtrArray with the cell contents as strings.
+
+ * NOTE: The calling routine is responsible for freeing result.
+ **/
+static GPtrArray *
+stf_parse_fixed_line (Source_t *src, StfParseOptions_t *parseoptions)
+{
+ GPtrArray *line;
+
+ g_return_val_if_fail (src != NULL, NULL);
+ g_return_val_if_fail (parseoptions != NULL, NULL);
+
+ src->linepos = 0;
+ src->splitpos = 0;
+
+ line = g_ptr_array_new ();
+ while (*src->position != '\0' && !compare_terminator (src->position, parseoptions)) {
+ char *field = stf_parse_fixed_cell (src, parseoptions);
+
+ trim_spaces_inplace (field, parseoptions);
+ g_ptr_array_add (line, field);
+
+ src->splitpos++;
+ }
+
+ return line;
+}
+
+void
+stf_parse_general_free (GPtrArray *lines)
+{
+ unsigned lineno;
+ for (lineno = 0; lineno < lines->len; lineno++) {
+ GPtrArray *line = g_ptr_array_index (lines, lineno);
+ /* Fields are not free here. */
+ g_ptr_array_free (line, TRUE);
+ }
+ g_ptr_array_free (lines, TRUE);
+}
+
+
+/**
+ * stf_parse_general:
+ *
+ * Returns a GPtrArray of lines, where each line is itself a
+ * GPtrArray of strings.
+ *
+ * The caller must free this entire structure, for example by calling
+ * stf_parse_general_free.
+ **/
+GPtrArray *
+stf_parse_general (StfParseOptions_t *parseoptions,
+ GStringChunk *lines_chunk,
+ char const *data, char const *data_end)
+{
+ GPtrArray *lines;
+ Source_t src;
+ int row;
+
+ g_return_val_if_fail (parseoptions != NULL, NULL);
+ g_return_val_if_fail (data != NULL, NULL);
+ g_return_val_if_fail (data_end != NULL, NULL);
+ g_return_val_if_fail (stf_parse_options_valid (parseoptions), NULL);
+ g_return_val_if_fail (g_utf8_validate (data, -1, NULL), NULL);
+
+ src.chunk = lines_chunk;
+ src.position = data;
+ row = 0;
+
+ lines = g_ptr_array_new ();
+ while (*src.position != '\0' && src.position < data_end) {
+ GPtrArray *line;
+
+ line = parseoptions->parsetype == PARSE_TYPE_CSV
+ ? stf_parse_csv_line (&src, parseoptions)
+ : stf_parse_fixed_line (&src, parseoptions);
+
+ g_ptr_array_add (lines, line);
+ if (parseoptions->parsetype != PARSE_TYPE_CSV)
+ src.position += compare_terminator (src.position, parseoptions);
+
+ if (++row == SHEET_MAX_ROWS)
+ break;
+ }
+
+ return lines;
+}
+
+GPtrArray *
+stf_parse_lines (StfParseOptions_t *parseoptions,
+ GStringChunk *lines_chunk,
+ char const *data,
+ int maxlines, gboolean with_lineno)
+{
+ GPtrArray *lines;
+ int lineno = 1;
+
+ g_return_val_if_fail (data != NULL, NULL);
+
+ lines = g_ptr_array_new ();
+ while (*data) {
+ char const *data0 = data;
+ GPtrArray *line = g_ptr_array_new ();
+
+ if (with_lineno) {
+ char buf[4 * sizeof (int)];
+ sprintf (buf, "%d", lineno);
+ g_ptr_array_add (line,
+ g_string_chunk_insert (lines_chunk, buf));
+ }
+
+ while (1) {
+ int termlen = compare_terminator (data, parseoptions);
+ if (termlen > 0 || *data == 0) {
+ g_ptr_array_add (line,
+ g_string_chunk_insert_len (lines_chunk,
+ data0,
+ data - data0));
+ data += termlen;
+ break;
+ } else
+ data = g_utf8_next_char (data);
+ }
+
+ g_ptr_array_add (lines, line);
+
+ lineno++;
+ if (lineno >= maxlines)
+ break;
+ }
+ return lines;
+}
+
+char const *
+stf_parse_find_line (StfParseOptions_t *parseoptions,
+ char const *data,
+ int line)
+{
+ while (line > 0) {
+ int termlen = compare_terminator (data, parseoptions);
+ if (termlen > 0) {
+ data += termlen;
+ line--;
+ } else if (*data == 0) {
+ return data;
+ } else {
+ data = g_utf8_next_char (data);
+ }
+ }
+ return data;
+}
+
+
+/**
+ * stf_parse_options_fixed_autodiscover:
+ * @parseoptions: a Parse options struct.
+ * @data_lines : The number of lines to look at in @data.
+ * @data : The actual data.
+ *
+ * Automatically try to discover columns in the text to be parsed.
+ * We ignore empty lines (only containing parseoptions->terminator)
+ *
+ * FIXME: This is so extremely ugly that I am too tired to rewrite it right now.
+ * Think hard of a better more flexible solution...
+ **/
+void
+stf_parse_options_fixed_autodiscover (StfParseOptions_t *parseoptions,
+ char const *data, char const *data_end)
+{
+ char const *iterator = data;
+ GSList *list = NULL;
+ GSList *list_start = NULL;
+ int lines = 0;
+ int effective_lines = 0;
+ int max_line_length = 0;
+ int *line_begin_hits = NULL;
+ int *line_end_hits = NULL;
+ int i;
+
+ stf_parse_options_fixed_splitpositions_clear (parseoptions);
+
+ /*
+ * First take a look at all possible white space combinations
+ */
+ while (*iterator && iterator < data_end) {
+ gboolean begin_recorded = FALSE;
+ AutoDiscovery_t *disc = NULL;
+ int position = 0;
+ int termlen = 0;
+
+ while (*iterator && (termlen = compare_terminator (iterator, parseoptions)) == 0) {
+ if (!begin_recorded && *iterator == ' ') {
+ disc = g_new0 (AutoDiscovery_t, 1);
+
+ disc->start = position;
+
+ begin_recorded = TRUE;
+ } else if (begin_recorded && *iterator != ' ') {
+ disc->stop = position;
+ list = g_slist_prepend (list, disc);
+
+ begin_recorded = FALSE;
+ disc = NULL;
+ }
+
+ position++;
+ iterator++;
+ }
+
+ if (position > max_line_length)
+ max_line_length = position;
+
+ /*
+ * If there are excess spaces at the end of
+ * the line : ignore them
+ */
+ g_free (disc);
+
+ /*
+ * Hop over the terminator
+ */
+ iterator += termlen;
+
+ if (position != 0)
+ effective_lines++;
+
+ lines++;
+ }
+
+ list = g_slist_reverse (list);
+ list_start = list;
+
+ /*
+ * Kewl stuff :
+ * Look at the number of hits at each line position
+ * if the number of hits equals the number of lines
+ * we can be pretty sure this is the start or end
+ * of a column, we filter out empty columns
+ * later
+ */
+ line_begin_hits = g_new0 (int, max_line_length + 1);
+ line_end_hits = g_new0 (int, max_line_length + 1);
+
+ while (list) {
+ AutoDiscovery_t *disc = list->data;
+
+ line_begin_hits[disc->start]++;
+ line_end_hits[disc->stop]++;
+
+ g_free (disc);
+
+ list = g_slist_next (list);
+ }
+ g_slist_free (list_start);
+
+ for (i = 0; i < max_line_length + 1; i++)
+ if (line_begin_hits[i] == effective_lines || line_end_hits[i] == effective_lines)
+ stf_parse_options_fixed_splitpositions_add (parseoptions, i);
+
+ /*
+ * Do some corrections to the initial columns
+ * detected here, we obviously don't need to
+ * do this if there are no columns at all.
+ */
+ if (my_garray_len (parseoptions->splitpositions) > 0) {
+ /*
+ * Try to find columns that look like :
+ *
+ * Example 100
+ * Example2 9
+ *
+ * (In other words : Columns with left & right justification with
+ * a minimum of 2 spaces in the middle)
+ * Split these columns in 2
+ */
+
+ for (i = 0; i < my_garray_len (parseoptions->splitpositions) - 1; i++) {
+ int begin = g_array_index (parseoptions->splitpositions, int, i);
+ int end = g_array_index (parseoptions->splitpositions, int, i + 1);
+ int num_spaces = -1;
+ int spaces_start = 0;
+ gboolean right_aligned = TRUE;
+ gboolean left_aligned = TRUE;
+ gboolean has_2_spaces = TRUE;
+
+ iterator = data;
+ lines = 0;
+ while (*iterator && iterator < data_end) {
+ gboolean trigger = FALSE;
+ gboolean space_trigger = FALSE;
+ int pos = 0;
+
+ num_spaces = -1;
+ spaces_start = 0;
+ while (*iterator && !compare_terminator (iterator, parseoptions)) {
+ if (pos == begin) {
+ if (*iterator == ' ')
+ left_aligned = FALSE;
+
+ trigger = TRUE;
+ } else if (pos == end - 1) {
+ if (*iterator == ' ')
+ right_aligned = FALSE;
+
+ trigger = FALSE;
+ }
+
+ if (trigger || pos == end - 1) {
+ if (!space_trigger && *iterator == ' ') {
+ space_trigger = TRUE;
+ spaces_start = pos;
+ } else if (space_trigger && *iterator != ' ') {
+ space_trigger = FALSE;
+ num_spaces = pos - spaces_start;
+ }
+ }
+
+ iterator++;
+ pos++;
+ }
+
+ if (num_spaces < 2)
+ has_2_spaces = FALSE;
+
+ if (*iterator)
+ iterator++;
+
+ lines++;
+ }
+
+ /*
+ * If this column meets all the criteria
+ * split it into two at the last measured
+ * spaces_start + num_spaces
+ */
+ if (has_2_spaces && right_aligned && left_aligned) {
+ int val = (((spaces_start + num_spaces) - spaces_start) / 2) + spaces_start;
+
+ g_array_insert_val (parseoptions->splitpositions, i + 1, val);
+
+ /*
+ * Skip over the inserted column
+ */
+ i++;
+ }
+ }
+
+ /*
+ * Remove empty columns here if needed
+ */
+ for (i = 0; i < my_garray_len (parseoptions->splitpositions) - 1; i++) {
+ int begin = g_array_index (parseoptions->splitpositions, int, i);
+ int end = g_array_index (parseoptions->splitpositions, int, i + 1);
+ gboolean only_spaces = TRUE;
+
+ iterator = data;
+ lines = 0;
+ while (*iterator && iterator < data_end) {
+ gboolean trigger = FALSE;
+ int pos = 0;
+
+ while (*iterator && !compare_terminator (iterator, parseoptions)) {
+ if (pos == begin)
+ trigger = TRUE;
+ else if (pos == end)
+ trigger = FALSE;
+
+ if (trigger) {
+ if (*iterator != ' ')
+ only_spaces = FALSE;
+ }
+
+ iterator++;
+ pos++;
+ }
+
+ if (*iterator)
+ iterator++;
+
+ lines++;
+ }
+
+ /*
+ * The column only contains spaces
+ * remove it
+ */
+ if (only_spaces) {
+ g_array_remove_index (parseoptions->splitpositions, i);
+
+ /*
+ * We HAVE to make sure that the next column (end) also
+ * gets checked out. If we don't decrease "i" here, we
+ * will skip over it as the indexes shift down after
+ * the removal
+ */
+ i--;
+ }
+ }
+ }
+
+ g_free (line_begin_hits);
+ g_free (line_end_hits);
+}
+
+/*******************************************************************************************************
+ * STF PARSE HL: high-level functions that dump the raw data returned by the low-level parsing
+ * functions into something meaningful (== application specific)
+ *******************************************************************************************************/
+
+/* gboolean */
+/* stf_parse_sheet (StfParseOptions_t *parseoptions, */
+/* char const *data, char const *data_end, */
+/* Sheet *sheet, int start_col, int start_row) */
+/* { */
+/* int row, col; */
+/* unsigned int lrow, lcol; */
+/* GODateConventions const *date_conv; */
+/* GStringChunk *lines_chunk; */
+/* GPtrArray *lines, *line; */
+
+/* SETUP_LOCALE_SWITCH; */
+
+/* g_return_val_if_fail (parseoptions != NULL, FALSE); */
+/* g_return_val_if_fail (data != NULL, FALSE); */
+/* g_return_val_if_fail (IS_SHEET (sheet), FALSE); */
+
+/* START_LOCALE_SWITCH; */
+
+/* date_conv = workbook_date_conv (sheet->workbook); */
+
+/* if (!data_end) */
+/* data_end = data + strlen (data); */
+/* lines_chunk = g_string_chunk_new (100 * 1024); */
+/* lines = stf_parse_general (parseoptions, lines_chunk, data, data_end); */
+/* if (lines == NULL) */
+/* return FALSE; */
+/* for (row = start_row, lrow = 0; lrow < lines->len ; row++, lrow++) { */
+/* col = start_col; */
+/* line = g_ptr_array_index (lines, lrow); */
+
+/* for (lcol = 0; lcol < line->len; lcol++) */
+/* if (parseoptions->col_import_array == NULL || */
+/* parseoptions->col_import_array_len <= lcol || */
+/* parseoptions->col_import_array[lcol]) { */
+/* if (col >= SHEET_MAX_COLS) { */
+/* if (!parseoptions->cols_exceeded) { */
+/* g_warning (_("There are more columns of data than " */
+/* "there is room for in the sheet. Extra " */
+/* "columns will be ignored.")); */
+/* parseoptions->cols_exceeded = TRUE; */
+/* } */
+/* } else { */
+/* char const *text = g_ptr_array_index (line, lcol); */
+/* if (text && *text) */
+/* gnm_cell_set_text ( */
+/* sheet_cell_fetch (sheet, col, row), */
+/* text); */
+/* } */
+/* col++; */
+/* } */
+/* } */
+
+/* stf_parse_general_free (lines); */
+/* g_string_chunk_free (lines_chunk); */
+/* END_LOCALE_SWITCH; */
+/* return TRUE; */
+/* } */
+
+/* GnmCellRegion * */
+/* stf_parse_region (StfParseOptions_t *parseoptions, char const *data, char const *data_end, */
+/* Workbook const *wb) */
+/* { */
+/* static GODateConventions const default_conv = {FALSE}; */
+/* GODateConventions const *date_conv = wb ? workbook_date_conv (wb) : &default_conv; */
+
+/* GnmCellRegion *cr; */
+/* unsigned int row, colhigh = 0; */
+/* char *text; */
+/* GStringChunk *lines_chunk; */
+/* GPtrArray *lines; */
+/* GnmCellCopy *cc; */
+/* GOFormat *fmt; */
+/* GnmValue *v; */
+
+/* SETUP_LOCALE_SWITCH; */
+
+/* g_return_val_if_fail (parseoptions != NULL, NULL); */
+/* g_return_val_if_fail (data != NULL, NULL); */
+
+/* START_LOCALE_SWITCH; */
+
+/* cr = cellregion_new (NULL); */
+
+/* if (!data_end) */
+/* data_end = data + strlen (data); */
+/* lines_chunk = g_string_chunk_new (100 * 1024); */
+/* lines = stf_parse_general (parseoptions, lines_chunk, data, data_end); */
+/* for (row = 0; row < lines->len; row++) { */
+/* GPtrArray *line = g_ptr_array_index (lines, row); */
+/* unsigned int col, targetcol = 0; */
+/* for (col = 0; col < line->len; col++) { */
+/* if (parseoptions->col_import_array == NULL || */
+/* parseoptions->col_import_array_len <= col || */
+/* parseoptions->col_import_array[col]) { */
+/* if (NULL != (text = g_ptr_array_index (line, col))) { */
+/* fmt = g_ptr_array_index ( */
+/* parseoptions->formats, col); */
+/* if (NULL == (v = format_match (text, fmt, date_conv))) */
+/* v = value_new_string (text); */
+
+/* cc = gnm_cell_copy_new (cr, targetcol, row); */
+/* cc->val = v; */
+/* cc->texpr = NULL; */
+/* targetcol++; */
+/* if (targetcol > colhigh) */
+/* colhigh = targetcol; */
+/* } */
+/* } */
+/* } */
+/* } */
+/* stf_parse_general_free (lines); */
+/* g_string_chunk_free (lines_chunk); */
+
+/* END_LOCALE_SWITCH; */
+
+/* cr->cols = (colhigh > 0) ? colhigh : 1; */
+/* cr->rows = row; */
+
+/* return cr; */
+/* } */
+
+static int
+int_sort (void const *a, void const *b)
+{
+ return *(int const *)a - *(int const *)b;
+}
+
+static int
+count_character (GPtrArray *lines, gunichar c, double quantile)
+{
+ int *counts, res;
+ unsigned int lno, cno;
+
+ if (lines->len == 0)
+ return 0;
+
+ counts = g_new (int, lines->len);
+ for (lno = cno = 0; lno < lines->len; lno++) {
+ int count = 0;
+ GPtrArray *boxline = g_ptr_array_index (lines, lno);
+ char const *line = g_ptr_array_index (boxline, 0);
+
+ /* Ignore empty lines. */
+ if (*line == 0)
+ continue;
+
+ while (*line) {
+ if (g_utf8_get_char (line) == c)
+ count++;
+ line = g_utf8_next_char (line);
+ }
+
+ counts[cno++] = count;
+ }
+
+ if (cno == 0)
+ res = 0;
+ else {
+ unsigned int qi = (unsigned int)ceil (quantile * cno);
+ qsort (counts, cno, sizeof (counts[0]), int_sort);
+ if (qi == cno)
+ qi--;
+ res = counts[qi];
+ }
+
+ g_free (counts);
+
+ return res;
+}
+
+
+StfParseOptions_t *
+stf_parse_options_guess (char const *data)
+{
+ StfParseOptions_t *res;
+ GStringChunk *lines_chunk;
+ GPtrArray *lines;
+ int tabcount;
+ int sepcount;
+ gunichar sepchar = go_locale_get_arg_sep ();
+
+ g_return_val_if_fail (data != NULL, NULL);
+
+ res = stf_parse_options_new ();
+ lines_chunk = g_string_chunk_new (100 * 1024);
+ lines = stf_parse_lines (res, lines_chunk, data, SHEET_MAX_ROWS, FALSE);
+
+ tabcount = count_character (lines, '\t', 0.2);
+ sepcount = count_character (lines, sepchar, 0.2);
+
+ /* At least one tab per line and enough to separate every
+ would-be sepchars. */
+ if (tabcount >= 1 && tabcount >= sepcount - 1)
+ stf_parse_options_csv_set_separators (res, "\t", NULL);
+ else {
+ gunichar c;
+
+ /*
+ * Try a few more or less likely characters and pick the first
+ * one that occurs on at least half the lines.
+ *
+ * The order is mostly random, although ' ' and '!' which
+ * could very easily occur in text are put last.
+ */
+ if (count_character (lines, (c = sepchar), 0.5) > 0 ||
+ count_character (lines, (c = go_locale_get_col_sep ()), 0.5) > 0 ||
+ count_character (lines, (c = ':'), 0.5) > 0 ||
+ count_character (lines, (c = ','), 0.5) > 0 ||
+ count_character (lines, (c = ';'), 0.5) > 0 ||
+ count_character (lines, (c = '|'), 0.5) > 0 ||
+ count_character (lines, (c = '!'), 0.5) > 0 ||
+ count_character (lines, (c = ' '), 0.5) > 0) {
+ char sep[7];
+ sep[g_unichar_to_utf8 (c, sep)] = 0;
+ if (c == ' ')
+ strcat (sep, "\t");
+ stf_parse_options_csv_set_separators (res, sep, NULL);
+ }
+ }
+
+ if (1) {
+ /* Separated */
+ gboolean dups =
+ res->sep.chr &&
+ strchr (res->sep.chr, ' ') != NULL;
+ gboolean trim =
+ res->sep.chr &&
+ strchr (res->sep.chr, ' ') != NULL;
+
+ stf_parse_options_set_type (res, PARSE_TYPE_CSV);
+ stf_parse_options_set_trim_spaces (res, TRIM_TYPE_LEFT | TRIM_TYPE_RIGHT);
+ stf_parse_options_csv_set_indicator_2x_is_single (res, TRUE);
+ stf_parse_options_csv_set_duplicates (res, dups);
+ stf_parse_options_csv_set_trim_seps (res, trim);
+
+ stf_parse_options_csv_set_stringindicator (res, '"');
+ } else {
+ /* Fixed-width */
+ }
+
+ stf_parse_general_free (lines);
+ g_string_chunk_free (lines_chunk);
+
+ return res;
+}
Added: gnucash/branches/csv-import/lib/stf/stf-parse.h
===================================================================
--- gnucash/branches/csv-import/lib/stf/stf-parse.h 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/lib/stf/stf-parse.h 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,112 @@
+#ifndef STF_PARSE_H
+#define STF_PARSE_H
+
+#include <glib.h>
+
+#define SHEET_MAX_ROWS (16*16*16*16) /* 0, 1, ... */
+#define SHEET_MAX_COLS (4*4*4*4) /* 0, 1, ... */
+
+typedef enum {
+ PARSE_TYPE_NOTSET = 1 << 0,
+ PARSE_TYPE_CSV = 1 << 1,
+ PARSE_TYPE_FIXED = 1 << 2
+} StfParseType_t;
+
+/* Additive. */
+typedef enum {
+ TRIM_TYPE_NEVER = 0,
+ TRIM_TYPE_LEFT = 1 << 0,
+ TRIM_TYPE_RIGHT = 2 << 1
+} StfTrimType_t;
+
+typedef struct {
+ StfParseType_t parsetype; /* The type of import to do */
+ StfTrimType_t trim_spaces; /* Trim spaces in fields? */
+
+ GSList * terminator; /* Line terminators */
+ char * locale;
+
+ struct {
+ guchar min, max;
+ } compiled_terminator;
+
+ /* CSV related */
+ struct {
+ GSList *str;
+ char *chr;
+ } sep;
+ gunichar stringindicator; /* String indicator */
+ gboolean indicator_2x_is_single;/* 2 quote chars form a single non-terminating quote */
+ gboolean duplicates; /* See two text separators as one? */
+ gboolean trim_seps; /* Ignore initial seps. */
+
+ /* Fixed width related */
+ GArray *splitpositions; /* Positions where text will be split vertically */
+
+ int rowcount; /* Number of rows parsed */
+ int colcount; /* Number of columns parsed */
+ gboolean *col_import_array; /* 0/1 array indicating */
+ /* which cols to import */
+ unsigned int col_import_array_len;
+ GPtrArray *formats ; /* Contains GnmFormat *s */
+ gboolean cols_exceeded; /* This is set to TRUE if */
+ /* we tried to import more than */
+ /* SHEET_MAX_COLS columns */
+} StfParseOptions_t;
+
+/* CREATION/DESTRUCTION of stf options struct */
+
+StfParseOptions_t *stf_parse_options_new (void);
+void stf_parse_options_free (StfParseOptions_t *parseoptions);
+
+StfParseOptions_t *stf_parse_options_guess (char const *data);
+
+/* MANIPULATION of stf options struct */
+
+void stf_parse_options_set_type (StfParseOptions_t *parseoptions,
+ StfParseType_t const parsetype);
+void stf_parse_options_clear_line_terminator (StfParseOptions_t *parseoptions);
+void stf_parse_options_add_line_terminator (StfParseOptions_t *parseoptions,
+ char const *terminator);
+void stf_parse_options_set_trim_spaces (StfParseOptions_t *parseoptions,
+ StfTrimType_t const trim_spaces);
+void stf_parse_options_csv_set_separators (StfParseOptions_t *parseoptions,
+ char const *character, GSList const *string);
+void stf_parse_options_csv_set_stringindicator (StfParseOptions_t *parseoptions,
+ gunichar const stringindicator);
+void stf_parse_options_csv_set_indicator_2x_is_single (StfParseOptions_t *parseoptions,
+ gboolean const indic_2x);
+void stf_parse_options_csv_set_duplicates (StfParseOptions_t *parseoptions,
+ gboolean const duplicates);
+void stf_parse_options_csv_set_trim_seps (StfParseOptions_t *parseoptions,
+ gboolean const trim_seps);
+void stf_parse_options_fixed_splitpositions_clear (StfParseOptions_t *parseoptions);
+void stf_parse_options_fixed_splitpositions_add (StfParseOptions_t *parseoptions,
+ int position);
+void stf_parse_options_fixed_splitpositions_remove (StfParseOptions_t *parseoptions,
+ int position);
+int stf_parse_options_fixed_splitpositions_count (StfParseOptions_t *parseoptions);
+int stf_parse_options_fixed_splitpositions_nth (StfParseOptions_t *parseoptions, int n);
+
+/* USING the stf structs to actually do some parsing, these are the lower-level functions and utility functions */
+
+GPtrArray *stf_parse_general (StfParseOptions_t *parseoptions,
+ GStringChunk *lines_chunk,
+ char const *data,
+ char const *data_end);
+void stf_parse_general_free (GPtrArray *lines);
+GPtrArray *stf_parse_lines (StfParseOptions_t *parseoptions,
+ GStringChunk *lines_chunk,
+ char const *data,
+ int maxlines,
+ gboolean with_lineno);
+
+void stf_parse_options_fixed_autodiscover (StfParseOptions_t *parseoptions,
+ char const *data,
+ char const *data_end);
+
+char const *stf_parse_find_line (StfParseOptions_t *parseoptions,
+ char const *data,
+ int line);
+
+#endif
Modified: gnucash/branches/csv-import/src/bin/gnucash-bin.c
===================================================================
--- gnucash/branches/csv-import/src/bin/gnucash-bin.c 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/bin/gnucash-bin.c 2007-06-17 01:23:40 UTC (rev 16173)
@@ -337,6 +337,7 @@
{ "gnucash/register/register-gnome", 0, FALSE },
{ "gnucash/import-export/qif-import", 0, FALSE },
{ "gnucash/import-export/ofx", 0, TRUE },
+ { "gnucash/import-export/csv", 0, TRUE },
{ "gnucash/import-export/log-replay", 0, TRUE },
{ "gnucash/import-export/hbci", 0, TRUE },
{ "gnucash/report/report-system", 0, FALSE },
Modified: gnucash/branches/csv-import/src/import-export/Makefile.am
===================================================================
--- gnucash/branches/csv-import/src/import-export/Makefile.am 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/Makefile.am 2007-06-17 01:23:40 UTC (rev 16173)
@@ -1,7 +1,7 @@
SUBDIRS = . schemas qif qif-import \
- ${OFX_DIR} ${HBCI_DIR} log-replay test
+ ${OFX_DIR} ${HBCI_DIR} log-replay test csv
DIST_SUBDIRS = schemas qif qif-import qif-io-core \
- ofx hbci log-replay test
+ ofx hbci log-replay test csv
pkglib_LTLIBRARIES=libgncmod-generic-import.la
Added: gnucash/branches/csv-import/src/import-export/csv/Makefile.am
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/Makefile.am 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/Makefile.am 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,57 @@
+SUBDIRS = .
+
+pkglib_LTLIBRARIES=libgncmod-csv.la
+
+libgncmod_csv_la_SOURCES = \
+ gncmod-csv-import.c \
+ gnc-plugin-csv.c \
+ gnc-csv-import.c \
+ gnc-csv-model.c
+
+noinst_HEADERS = \
+ gnc-plugin-csv.h \
+ gnc-csv-import.h \
+ gnc-csv-model.h
+
+libgncmod_csv_la_LDFLAGS = -avoid-version $(pkg-config --libs libgoffice-0.3)
+
+libgncmod_csv_la_LIBADD = \
+ ${top_builddir}/src/import-export/libgncmod-generic-import.la \
+ ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \
+ ${top_builddir}/src/app-utils/libgncmod-app-utils.la \
+ ${top_builddir}/src/engine/libgncmod-engine.la \
+ ${top_builddir}/src/core-utils/libgnc-core-utils.la \
+ ${top_builddir}/src/gnc-module/libgnc-module.la \
+ ${top_builddir}/lib/stf/libgnc-stf.la \
+ ${QOF_LIBS} \
+ ${GLIB_LIBS}
+
+AM_CFLAGS = \
+ -I${top_srcdir}/src \
+ -I${top_srcdir}/src/core-utils \
+ -I${top_srcdir}/src/engine \
+ -I${top_srcdir}/src/gnc-module \
+ -I${top_srcdir}/src/app-utils \
+ -I${top_srcdir}/src/gnome \
+ -I${top_srcdir}/src/gnome-utils \
+ -I${top_srcdir}/src/import-export \
+ -I${top_srcdir}/lib \
+ ${GNOME_CFLAGS} \
+ ${GTKHTML_CFLAGS} \
+ ${GLADE_CFLAGS} \
+ ${GUILE_INCS} \
+ ${QOF_CFLAGS} \
+ ${GLIB_CFLAGS} \
+ $(GOFFICE_CFLAGS)
+
+uidir = $(GNC_UI_DIR)
+ui_DATA = \
+ gnc-plugin-csv-ui.xml
+
+gladedir = ${GNC_GLADE_DIR}
+glade_DATA = \
+ gnc-csv-preview-dialog.glade
+
+EXTRA_DIST = $(ui_DATA)
+
+INCLUDES = -DG_LOG_DOMAIN=\"gnc.import.csv\"
Added: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,422 @@
+/*******************************************************************\
+ * 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 at gnu.org *
+\********************************************************************/
+/** @addtogroup Import_Export
+ @{ */
+/** @internal
+ @file gnc-csv-import.c
+ @brief Csv import module code
+ @author Copyright (c) 2007 Benny Sperisen <lasindi at gmail.com>
+*/
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <glib/gi18n.h>
+#include <goffice/gtk/go-charmap-sel.h>
+
+#include "import-account-matcher.h"
+#include "import-main-matcher.h"
+
+#include "gnc-file.h"
+#include "gnc-book.h"
+#include "gnc-ui-util.h"
+#include "gnc-glib-utils.h"
+#include "dialog-utils.h"
+
+#include "gnc-csv-import.h"
+#include "gnc-csv-model.h"
+
+#include <stdio.h> /* Get rid of this */
+#include <stdlib.h> /* Get rid of this */
+
+#define GCONF_SECTION "dialogs/import/csv"
+
+static QofLogModule log_module = GNC_MOD_IMPORT;
+
+/* TODO Comment */
+
+enum SEP_BUTTON_TYPES {SEP_SPACE, SEP_TAB, SEP_COMMA, SEP_COLON, SEP_SEMICOLON, SEP_HYPHEN,
+ SEP_NUM_OF_TYPES};
+
+
+typedef struct
+{
+ GncCsvParseData* parse_data;
+ GladeXML* xml;
+ GtkDialog* dialog;
+ GtkTreeView* treeview;
+ gboolean approved;
+ GtkCheckButton* sep_buttons[SEP_NUM_OF_TYPES];
+ GtkCheckButton* custom_cbutton;
+ GtkEntry* custom_entry;
+} GncCsvPreview;
+
+static void gnc_csv_preview_treeview(GncCsvPreview* preview, gboolean notEmpty);
+
+static void sep_button_clicked(GtkCheckButton* widget, GncCsvPreview* preview)
+{
+ char* sep_chars[] = {" ", "\t", ",", ":", ";", "-"};
+ GSList* separators = NULL;
+ int i;
+ for(i = 0; i < SEP_NUM_OF_TYPES; i++)
+ {
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(preview->sep_buttons[i])))
+ separators = g_slist_append(separators, sep_chars[i]);
+ }
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(preview->custom_cbutton)))
+ {
+ char* custom_sep = (char*)gtk_entry_get_text(preview->custom_entry);
+ if(custom_sep[0] != '\0')
+ separators = g_slist_append(separators, custom_sep);
+ }
+
+ stf_parse_options_csv_set_separators(preview->parse_data->options, NULL, separators);
+ g_slist_free(separators);
+ /* TODO Handle error */
+ gnc_csv_parse(preview->parse_data, FALSE, NULL);
+ gnc_csv_preview_treeview(preview, TRUE);
+}
+
+static void encoding_selected(GOCharmapSel* selector, const char* enc,
+ GncCsvPreview* preview)
+{
+ static gboolean second_call = FALSE;
+ if(second_call)
+ {
+ GError* error = NULL;
+ printf("Encoding selected! %p %p %s\n", selector, preview, enc);
+ /* TODO Handle errors */
+ gnc_csv_convert_enc(preview->parse_data, enc);
+ gnc_csv_parse(preview->parse_data, FALSE, &error);
+ gnc_csv_preview_treeview(preview, TRUE);
+ second_call = FALSE;
+ }
+ else
+ {
+ second_call = TRUE;
+ }
+}
+
+static void ok_button_clicked(GtkWidget* widget, GncCsvPreview* preview)
+{
+ preview->approved = TRUE;
+ gtk_widget_hide((GtkWidget*)(preview->dialog));
+}
+
+static void cancel_button_clicked(GtkWidget* widget, GncCsvPreview* preview)
+{
+ gtk_widget_hide((GtkWidget*)(preview->dialog));
+}
+
+static GncCsvPreview* gnc_csv_new_preview()
+{
+ GncCsvPreview* preview = g_malloc(sizeof(GncCsvPreview));
+ GtkWidget *ok_button, *cancel_button;
+ char* sep_button_names[] = {"space_cbutton",
+ "tab_cbutton",
+ "comma_cbutton",
+ "colon_cbutton",
+ "semicolon_cbutton",
+ "hyphen_cbutton"};
+ int i;
+ GtkWidget *encselector = go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8);
+ GtkTable* enctable;
+ preview->xml = gnc_glade_xml_new("gnc-csv-preview-dialog.glade", "dialog");
+ printf("xml is %p\n", preview->xml);
+ preview->dialog = (GtkDialog*)(glade_xml_get_widget(preview->xml, "dialog"));
+
+ for(i = 0; i < SEP_NUM_OF_TYPES; i++)
+ {
+ preview->sep_buttons[i]
+ = (GtkCheckButton*)glade_xml_get_widget(preview->xml, sep_button_names[i]);
+ g_signal_connect(G_OBJECT(preview->sep_buttons[i]), "toggled",
+ G_CALLBACK(sep_button_clicked), (gpointer)preview);
+ }
+
+ preview->custom_cbutton
+ = (GtkCheckButton*)glade_xml_get_widget(preview->xml, "custom_cbutton");
+ g_signal_connect(G_OBJECT(preview->custom_cbutton), "clicked",
+ G_CALLBACK(sep_button_clicked), (gpointer)preview);
+
+ preview->custom_entry = (GtkEntry*)glade_xml_get_widget(preview->xml, "custom_entry");
+ g_signal_connect(G_OBJECT(preview->custom_entry), "changed",
+ G_CALLBACK(sep_button_clicked), (gpointer)preview);
+
+ enctable = GTK_TABLE(glade_xml_get_widget(preview->xml, "enctable"));
+ gtk_table_attach_defaults(enctable, encselector, 1, 2, 0, 1);
+ gtk_widget_show_all(GTK_WIDGET(enctable));
+ g_signal_connect(G_OBJECT(encselector), "charmap_changed",
+ G_CALLBACK(encoding_selected), (gpointer)preview);
+
+ ok_button = glade_xml_get_widget(preview->xml, "ok_button");
+ g_signal_connect(G_OBJECT(ok_button), "clicked",
+ G_CALLBACK(ok_button_clicked), (gpointer)preview);
+
+ cancel_button = glade_xml_get_widget(preview->xml, "cancel_button");
+ g_signal_connect(G_OBJECT(cancel_button), "clicked",
+ G_CALLBACK(cancel_button_clicked), (gpointer)preview);
+
+ preview->treeview = (GtkTreeView*)(glade_xml_get_widget(preview->xml, "treeview"));
+ printf("treeview is %p \n", preview->treeview);
+
+ preview->approved = FALSE;
+ return preview;
+}
+
+
+static void gnc_csv_preview_free(GncCsvPreview* preview)
+{
+ g_object_unref(preview->xml);
+ g_free(preview);
+}
+
+static void gnc_csv_preview_treeview(GncCsvPreview* preview, gboolean notEmpty)
+{
+ GtkListStore* store;
+ GtkTreeIter iter;
+ int i, j, ncols = preview->parse_data->column_types->len;
+ GType* types = g_malloc(ncols * sizeof(GType));
+ for(i = 0; i < ncols; i++)
+ types[i] = G_TYPE_STRING;
+ store = gtk_list_store_newv(ncols, types);
+
+ /* Clear out any exisiting columns. */
+ if(notEmpty)
+ {
+ int size;
+ do
+ {
+ GtkTreeViewColumn* col = gtk_tree_view_get_column(preview->treeview, 0);
+ size = gtk_tree_view_remove_column(preview->treeview, col);
+ } while(size);
+ }
+
+ /* TODO free types */
+
+ for(i = 0; i < preview->parse_data->orig_lines->len; i++)
+ {
+ gtk_list_store_append(store, &iter);
+ for(j = 0; j < ((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->len; j++)
+ {
+ gtk_list_store_set(store, &iter, j,
+ ((GPtrArray*)(preview->parse_data->orig_lines->pdata[i]))->pdata[j],
+ -1);
+ }
+ }
+
+ for(i = 0; i < ncols; i++)
+ {
+ GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(preview->treeview,
+ -1, "", renderer, "text", i, NULL);
+ }
+
+ gtk_tree_view_set_model(preview->treeview, GTK_TREE_MODEL(store));
+ g_object_unref(GTK_TREE_MODEL(store));
+}
+
+static int gnc_csv_preview(GncCsvPreview* preview, GncCsvParseData* parse_data)
+{
+ preview->parse_data = parse_data;
+ gnc_csv_preview_treeview(preview, FALSE);
+ gtk_dialog_run(GTK_DIALOG(preview->dialog));
+
+ if(preview->approved)
+ return 0;
+ else
+ return 1;
+}
+
+typedef struct
+{
+ GncCsvParseData* parse_data;
+ char approved;
+ char* encoding;
+} EncodingDialog;
+
+static void encoding_dialog_selected(GOCharmapSel* selector, const char* enc,
+ EncodingDialog* encdialog)
+{
+ static gboolean second_call = FALSE;
+ if(second_call)
+ {
+ printf("set encoding %s\n", enc);
+ encdialog->encoding = (char*)enc;
+ second_call = FALSE;
+ }
+ else
+ {
+ second_call = TRUE;
+ }
+}
+
+static void enc_ok_button_clicked(GtkWidget* button, EncodingDialog* encdialog)
+{
+ printf("OK!\n");
+ printf("using dialog encoding %s\n", encdialog->encoding);
+ if(!gnc_csv_convert_enc(encdialog->parse_data, encdialog->encoding))
+ encdialog->approved = 1;
+}
+
+static void enc_cancel_button_clicked(GtkWidget* button, EncodingDialog* encdialog)
+{
+ printf("cancel!\n");
+ encdialog->approved = -1;
+}
+
+static int gnc_csv_encoding_dialog(GncCsvParseData* parse_data)
+{
+ EncodingDialog* encdialog = g_malloc(sizeof(encdialog));
+ GladeXML* xml;
+ GtkWidget *ok_button, *cancel_button;
+ GOCharmapSel* encselector = (GOCharmapSel*)go_charmap_sel_new(GO_CHARMAP_SEL_TO_UTF8);
+ GtkVBox* vbox;
+ GtkDialog* dialog;
+ encdialog->parse_data = parse_data;
+ encdialog->encoding = (char*)go_charmap_sel_get_encoding(encselector);
+ encdialog->approved = 0;
+ xml = gnc_glade_xml_new("preview-dialog.glade", "encdialog");
+ dialog = (GtkDialog*)(glade_xml_get_widget(xml, "encdialog"));
+
+ vbox = GTK_VBOX(glade_xml_get_widget(xml, "encvbox"));
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(encselector), TRUE, TRUE, 5);
+ gtk_widget_show_all(GTK_WIDGET(vbox));
+ g_signal_connect(G_OBJECT(encselector), "charmap_changed",
+ G_CALLBACK(encoding_dialog_selected), (gpointer)encdialog);
+
+ ok_button = glade_xml_get_widget(xml, "enc_ok_button");
+ g_signal_connect(G_OBJECT(ok_button), "clicked",
+ G_CALLBACK(enc_ok_button_clicked), (gpointer)encdialog);
+
+ cancel_button = glade_xml_get_widget(xml, "enc_cancel_button");
+ g_signal_connect(G_OBJECT(cancel_button), "clicked",
+ G_CALLBACK(enc_cancel_button_clicked), (gpointer)encdialog);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+
+ g_object_unref(xml);
+ /* TODO Free stuff */
+
+ return encdialog->approved;
+}
+
+/* The function that actually imports a CSV/Fixed-Width file. */
+/* TODO Comment this function. */
+void gnc_file_csv_import(void)
+{
+ const char *selected_filename;
+ char *default_dir;
+ GNCImportMainMatcher* gnc_csv_importer_gui = NULL;
+
+ default_dir = gnc_get_default_directory(GCONF_SECTION);
+
+ selected_filename = gnc_file_dialog(_("Select an CSV/Fixed-Width file to import"),
+ NULL,
+ default_dir,
+ GNC_FILE_DIALOG_IMPORT);
+ g_free(default_dir);
+
+ if(selected_filename!=NULL)
+ {
+ Account* account;
+ int i;
+ GncCsvParseData* parse_data;
+ GError* error = NULL;
+ GList *transactions;
+ GncCsvPreview* preview;
+
+ /* Remember the directory as the default. */
+ default_dir = g_path_get_dirname(selected_filename);
+ gnc_set_default_directory(GCONF_SECTION, default_dir);
+ g_free(default_dir);
+
+ /* TODO Check for errors */
+
+ parse_data = gnc_csv_new_parse_data();
+ if(gnc_csv_load_file(parse_data, selected_filename, &error))
+ {
+ if(error->code == GNC_CSV_ENCODING_ERR)
+ {
+ int result;
+ do
+ {
+ result = gnc_csv_encoding_dialog(parse_data);
+ } while(result == 0);
+ if(result == -1)
+ {
+ printf("Canceled!\n");
+ }
+ else
+ {
+ printf("Success!\n");
+ }
+ }
+ else /* GNC_CSV_FILE_OPEN_ERR */
+ {
+ /* TODO Do real error handling */
+ printf("Couldn't open file\n");
+ }
+ }
+ if(gnc_csv_parse(parse_data, TRUE, &error))
+ {
+ /* TODO real error handling */
+ printf("Error in parsing: %s\n", error->message);
+ }
+
+ parse_data->column_types->data[0] = GNC_CSV_DATE;
+ parse_data->column_types->data[1] = GNC_CSV_DESCRIPTION;
+ parse_data->column_types->data[2] = GNC_CSV_AMOUNT;
+ printf("1ctype 1: %d\n", parse_data->column_types->data[0]);
+
+ preview = gnc_csv_new_preview();
+ if(gnc_csv_preview(preview, parse_data))
+ {
+ gnc_csv_preview_free(preview);
+ gnc_csv_parse_data_free(parse_data);
+ return;
+ }
+
+ /* TODO Move this after the parsing code. */
+ account = gnc_import_select_account(NULL, NULL, 1, NULL, NULL, 0, NULL, NULL);
+ printf("2ctype 1: %d\n", parse_data->column_types->data[0]);
+
+ gnc_parse_to_trans(parse_data, account);
+ printf("3ctype 1: %d\n", parse_data->column_types->data[0]);
+
+ /* Create the Generic transaction importer GUI. */
+ gnc_csv_importer_gui = gnc_gen_trans_list_new(NULL, NULL, FALSE, 42);
+ printf("4ctype 1: %d\n", parse_data->column_types->data[0]);
+
+ transactions = parse_data->transactions;
+ while(transactions != NULL)
+ {
+ gnc_gen_trans_list_add_trans(gnc_csv_importer_gui,
+ (Transaction*)(transactions->data));
+ transactions = g_list_next(transactions);
+ }
+ gnc_gen_trans_list_run(gnc_csv_importer_gui);
+
+ gnc_csv_preview_free(preview);
+ gnc_csv_parse_data_free(parse_data);
+ }
+}
+
+
+
+/** @} */
Added: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.h
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.h 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.h 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,33 @@
+/********************************************************************\
+ * 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 at gnu.org *
+\********************************************************************/
+ /** @file
+ @brief Csv import module interface
+ *
+ gnc-csv-import.h
+ @author Copyright (c) 2007 Benny Sperisen <lasindi at gmail.com>
+ */
+#ifndef CSV_IMPORT_H
+#define CSV_IMPORT_H
+
+/** The gnc_file_csv_import() will let the user select a
+ * CSV/Fixed-Width file to open, select an account to import it to,
+ * and import the transactions into the account. It also allows the
+ * user to configure how the file is parsed. */
+void gnc_file_csv_import (void);
+#endif
Added: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,300 @@
+#include "gnc-csv-model.h"
+
+#include "gnc-book.h"
+
+#include <goffice/utils/go-glib-extras.h>
+
+#include <string.h>
+#include <sys/time.h>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <stdio.h> /* Get rid of this */
+
+/* Returns a set of sensible defaults for parsing CSV files. */
+static StfParseOptions_t* default_parse_options(void)
+{
+ StfParseOptions_t* options = stf_parse_options_new();
+ stf_parse_options_set_type(options, PARSE_TYPE_CSV);
+ stf_parse_options_csv_set_separators(options, ",", NULL);
+ return options;
+}
+
+/* TODO This will be replaced by something more sophisticated. */
+time_t parse_date(const char* date_str);
+
+time_t parse_date(const char* date_str)
+{
+ struct tm retvalue;
+ char mstr[3], dstr[3], ystr[3];
+ strncpy(mstr, date_str, 2);
+ strncpy(dstr, date_str + 3, 2);
+ strncpy(ystr, date_str + 6, 2);
+ mstr[2] = dstr[2] = ystr[2] = '\0';
+ retvalue.tm_mon = atoi(mstr) - 1;
+ retvalue.tm_mday = atoi(dstr);
+ retvalue.tm_year = atoi(ystr);
+ if(retvalue.tm_year < 10)
+ retvalue.tm_year += 100;
+ retvalue.tm_hour = 0;
+ retvalue.tm_min = 0;
+ retvalue.tm_sec = 1;
+ retvalue.tm_isdst = -1;
+ return mktime(&retvalue);
+}
+
+/* TODO Comment. */
+GncCsvStr file_to_string(const char* filename, GError** error)
+{
+ /* The file descriptor for opening the file, a flag indicating
+ * whether the file actually exists, the length of the file */
+ int fd, exists, length, max_cols = 0, i;
+
+ struct stat buf; /* Used to find file size */
+
+ /* What we want to return. */
+ GncCsvStr file_str;
+
+ /* Make sure filename is meaningful. */
+ if(filename == NULL)
+ {
+ g_set_error(error, 0, 0, "Received NULL filename.");
+ file_str.begin = file_str.end = NULL;
+ return file_str;
+ }
+
+ exists = stat(filename, &buf);
+ /* Make sure the file exists. */
+ if(exists == -1)
+ {
+ g_set_error(error, 0, 0, "File %s does not exist.", filename);
+ file_str.begin = file_str.end = NULL;
+ return file_str;
+ }
+
+ /* Now we can get the length of the file out of buf. */
+ length = (int)(buf.st_size);
+
+ /* Put the file's contents into a string starting at data_begin. */
+ fd = open(filename, O_RDONLY);
+ file_str.begin = mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0);
+ file_str.end = file_str.begin + length - 1; /* Point data_end to the end. */
+ close(fd);
+
+ return file_str;
+}
+
+/* TODO Comment */
+GncCsvParseData* gnc_csv_new_parse_data(void)
+{
+ GncCsvParseData* parse_data = g_malloc(sizeof(GncCsvParseData));
+ parse_data->encoding = "UTF-8";
+ parse_data->raw_str.begin = parse_data->raw_str.end
+ = parse_data->file_str.begin = parse_data->file_str.end = NULL;
+ parse_data->orig_lines = NULL;
+ parse_data->column_types = NULL;
+ parse_data->error_lines = parse_data->transactions = NULL;
+ parse_data->options = default_parse_options();
+ return parse_data;
+}
+
+/* TODO Comment */
+void gnc_csv_parse_data_free(GncCsvParseData* parse_data)
+{
+ if(parse_data->raw_str.begin != NULL)
+ munmap(parse_data->raw_str.begin,
+ parse_data->raw_str.end - parse_data->raw_str.begin);
+ if(parse_data->file_str.begin != NULL)
+ g_free(parse_data->file_str.begin);
+ if(parse_data->orig_lines != NULL)
+ g_ptr_array_free(parse_data->orig_lines, TRUE);
+ if(parse_data->options != NULL)
+ stf_parse_options_free(parse_data->options);
+ if(parse_data->column_types != NULL)
+ g_array_free(parse_data->column_types, TRUE);
+ if(parse_data->error_lines != NULL)
+ g_list_free(parse_data->error_lines);
+ /* TODO Find out if there's a potential memory leak here. */
+ if(parse_data->transactions != NULL)
+ g_list_free(parse_data->transactions);
+ g_free(parse_data);
+}
+
+/* TODO Comment */
+int gnc_csv_convert_enc(GncCsvParseData* parse_data, const char* enc)
+{
+ GError* error;
+ gsize bytes_read, bytes_written;
+ if(parse_data->file_str.begin != NULL)
+ {
+ g_free(parse_data->file_str.begin);
+ }
+ parse_data->file_str.begin = g_convert(parse_data->raw_str.begin,
+ parse_data->raw_str.end - parse_data->raw_str.begin,
+ "UTF-8", enc, &bytes_read,
+ &bytes_written, &error);
+ printf("using %s got %p\n", enc, parse_data->file_str.begin);
+ if(parse_data->file_str.begin == NULL)
+ {
+ return 1;
+ }
+ parse_data->file_str.end = parse_data->file_str.begin + bytes_written;
+ parse_data->encoding = (gchar*)enc;
+ return 0;
+}
+
+/* TODO Comment */
+int gnc_csv_load_file(GncCsvParseData* parse_data, const char* filename,
+ GError** error)
+{
+ const char* guess_enc;
+ parse_data->raw_str = file_to_string(filename, error);
+ if(parse_data->raw_str.begin == NULL)
+ {
+ g_set_error(error, 0, GNC_CSV_FILE_OPEN_ERR, "File opening failed.");
+ return 1;
+ }
+ guess_enc = go_guess_encoding((const char*)(parse_data->raw_str.begin),
+ (size_t)(parse_data->raw_str.end - parse_data->raw_str.begin),
+ "UTF-8", NULL);
+ printf("Guessed %s\n", guess_enc);
+ /* TODO Handle error */
+ gnc_csv_convert_enc(parse_data, guess_enc);
+ if(parse_data->file_str.begin == NULL)
+ {
+ g_set_error(error, 0, GNC_CSV_ENCODING_ERR, "Encoding conversion failed.");
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* TODO Comment. */
+/* TODO Should we use 0 for domain and code in errors? */
+int gnc_csv_parse(GncCsvParseData* parse_data, gboolean guessColTypes, GError** error)
+{
+ GStringChunk* chunk; /* TODO Find out exactly what this is. */
+ int i, max_cols = 0;
+
+ /* Do the actual parsing. */
+ /* TODO: This size might have to change ... because I'm not exactly
+ * sure what it's for. ... */
+ chunk = g_string_chunk_new(100);
+ parse_data->orig_lines = stf_parse_general(parse_data->options, chunk,
+ parse_data->file_str.begin,
+ parse_data->file_str.end);
+ g_string_chunk_free(chunk);
+ if(parse_data->orig_lines == NULL) /* If it failed, generate an error message. */
+ {
+ g_set_error(error, 0, 0, "Parsing failed.");
+ return 1;
+ }
+
+ /* Now that we have data, let's update max_cols. */
+ for(i = 0; i < parse_data->orig_lines->len; i++)
+ {
+ if(max_cols < ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len)
+ max_cols = ((GPtrArray*)(parse_data->orig_lines->pdata[i]))->len;
+ }
+ printf("max_cols %d\n", max_cols);
+
+ if(guessColTypes)
+ {
+ if(parse_data->column_types != NULL)
+ g_array_free(parse_data->column_types, TRUE);
+ parse_data->column_types = g_array_sized_new(FALSE, FALSE, sizeof(int),
+ max_cols);
+ g_array_set_size(parse_data->column_types, max_cols);
+ for(i = 0; i < parse_data->column_types->len; i++)
+ {
+ parse_data->column_types->data[i] = GNC_CSV_NONE;
+ }
+ }
+ else
+ {
+ int i = parse_data->column_types->len;
+ g_array_set_size(parse_data->column_types, max_cols);
+ for(; i < parse_data->column_types->len; i++)
+ {
+ parse_data->column_types->data[i] = GNC_CSV_NONE;
+ }
+ }
+
+ return 0;
+}
+
+/* TODO Comment. */
+int gnc_parse_to_trans(GncCsvParseData* parse_data, Account* account)
+{
+ int i, j;
+ GArray* column_types = parse_data->column_types;
+ GNCBook* book = gnc_account_get_book(account);
+
+ if(parse_data->error_lines != NULL)
+ {
+ g_list_free(parse_data->error_lines);
+ parse_data->error_lines = NULL;
+ }
+ if(parse_data->transactions != NULL)
+ {
+ g_list_free(parse_data->transactions);
+ parse_data->transactions = NULL;
+ }
+
+ for(i = 0; i < parse_data->orig_lines->len; i++)
+ {
+ Transaction* trans = xaccMallocTransaction(book);
+ gboolean noErrors = TRUE;
+ GPtrArray* line = parse_data->orig_lines->pdata[i];
+ time_t date;
+ const char* description;
+ gnc_numeric amount;
+ Split* split;
+ xaccTransBeginEdit(trans);
+ for(j = column_types->len - 1; j >= 0; j--)
+ {
+ if(column_types->data[j] != GNC_CSV_NONE)
+ {
+ /* If this line is too short, it goes in errors list. */
+ if(j >= line->len)
+ {
+ parse_data->error_lines = g_list_append(parse_data->error_lines,
+ line);
+ xaccTransDestroy(trans);
+ noErrors = FALSE;
+ break;
+ }
+ /* Affect the transaction appropriately. */
+ if(column_types->data[j] == GNC_CSV_DATE)
+ {
+ date = parse_date(line->pdata[j]);
+ xaccTransSetDatePostedSecs(trans, date);
+ }
+ else if(column_types->data[j] == GNC_CSV_DESCRIPTION)
+ {
+ description = line->pdata[j];
+ xaccTransSetDescription(trans, description);
+ }
+ else if(column_types->data[j] == GNC_CSV_AMOUNT)
+ {
+ amount = double_to_gnc_numeric(atof(line->pdata[j]), 1,
+ GNC_RND_ROUND);
+ split = xaccMallocSplit(book);
+ xaccSplitSetAccount(split, account);
+ xaccSplitSetParent(split, trans);
+ xaccSplitSetAmount(split, amount);
+ xaccSplitSetValue(split, amount);
+ xaccSplitSetAction(split, "Deposit");
+ }
+ }
+ }
+ if(noErrors)
+ parse_data->transactions = g_list_append(parse_data->transactions, trans);
+ }
+ return 0;
+}
Added: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,59 @@
+#ifndef GNC_CSV_MODEL_H
+#define GNC_CSV_MODEL_H
+
+#include "config.h"
+
+#include "Account.h"
+#include "Transaction.h"
+
+#include "stf/stf-parse.h"
+
+/* TODO Comment. */
+enum GncCsvColumnType {GNC_CSV_NONE,
+ GNC_CSV_DATE,
+ GNC_CSV_DESCRIPTION,
+ GNC_CSV_AMOUNT};
+
+enum GncCsvErrorType {GNC_CSV_FILE_OPEN_ERR,
+ GNC_CSV_ENCODING_ERR};
+
+typedef struct
+{
+ char* begin;
+ char* end;
+} GncCsvStr;
+
+typedef struct
+{
+ int line_no;
+ Transaction* trans;
+} GncCsvTransLine;
+
+typedef struct
+{
+ gchar* encoding;
+ GncCsvStr raw_str;
+ GncCsvStr file_str;
+ GPtrArray* orig_lines;
+ StfParseOptions_t* options;
+ GArray* column_types;
+ GList* error_lines;
+ GList* transactions;
+} GncCsvParseData;
+
+GncCsvParseData* gnc_csv_new_parse_data(void);
+
+void gnc_csv_parse_data_free(GncCsvParseData* parse_data);
+
+int gnc_csv_convert_enc(GncCsvParseData* parse_data, const char* enc);
+
+int gnc_csv_load_file(GncCsvParseData* parse_data, const char* filename,
+ GError** error);
+
+int gnc_csv_parse(GncCsvParseData* parse_data, gboolean guessColTypes, GError** error);
+
+int gnc_parse_to_trans(GncCsvParseData* parse_data, Account* account);
+
+GncCsvStr file_to_string(const char* filename, GError** error);
+
+#endif
Added: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-preview-dialog.glade
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-preview-dialog.glade 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-preview-dialog.glade 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,463 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.2.0 on Sat Jun 16 19:20:02 2007 by lasindi at pi-->
+<glade-interface>
+ <widget class="GtkDialog" id="dialog">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Import CSV/Fixed-Width File</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkTable" id="enctable">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ <property name="y_padding">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ <property name="y_padding">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkRadioButton" id="radiobutton1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Separated</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="radiobutton3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Fixed-Width</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">radiobutton1</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Data type: </property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator4">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator5">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ <property name="y_padding">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Encoding: </property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="left_padding">12</property>
+ <child>
+ <widget class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="column_spacing">3</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <widget class="GtkCheckButton" id="space_cbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Space</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="tab_cbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Tab</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="comma_cbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Comma (,)</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="colon_cbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Colon (:)</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="semicolon_cbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Semicolon (;)</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="hyphen_cbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Hyphen (-)</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="custom_cbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Custom</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="custom_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Separators</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator6">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">3</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="typehbox">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTreeView" id="treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="headers_clickable">True</property>
+ <property name="enable_grid_lines">GTK_TREE_VIEW_GRID_LINES_BOTH</property>
+ </widget>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-ok</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkDialog" id="encdialog">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">5</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkVBox" id="encvbox">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkLabel" id="enclabel">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="enc_cancel_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="enc_ok_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-ok</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
Deleted: gnucash/branches/csv-import/src/import-export/csv/gnc-csv2glist.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv2glist.c 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv2glist.c 2007-06-17 01:23:40 UTC (rev 16173)
@@ -1,187 +0,0 @@
-
-/*
- Copyright 2004 Kevin dot Hammack at comcast dot 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301 USA
-
-
- */
-
-//#include "config.h"
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <fcntl.h>
-
-//#include "file-utils.h"
-
-#define TEST_CSV
-
-/*
- <warlord> Yea, just returning a GList would be fine.. Or a GList of
- GLists, one per row..
-*/
-
-
-/*
- split_csv_list - Take a string, split on commas into a GList.
- Tricky part: honor quotes.
- Quotes aren't stripped.
- This would have been both easier and cleaner in scheme, and / or
- using regular expressions.
- I was thinking small and fast when I wrote it. :-P
-*/
-
-static GList *
-split_csv_line(char *line) {
- GList *csvlist = NULL;
- gchar *begin;
- gchar *current;
- gchar *cell;
- gchar quote=0;
- gboolean eol = FALSE;
-
- current = line;
- begin = current;
-
- while (!eol) {
-
- if (quote &&
- (*current == quote) && (*(current-1) != '\\') &&
- (current != begin)) {
-
- quote = 0;
- }
- else if (!quote && (*current == '"')) { quote = '"'; }
- else if (!quote && (*current == '\'')) { quote = '\''; }
-
- if (!quote && (*current == ',')) { *current = 0; }
-
- if (*current == '\n') {
- *current = 0;
- eol = TRUE;
- }
-
- if (*current == 0) {
- cell = g_strdup( begin );
- csvlist = g_list_prepend(csvlist, cell);
- current++;
- begin = current;
- quote = 0;
- }
- else {
- current++;
- }
-
- }
-
- return g_list_reverse(csvlist);
-}
-
-
-#if 1
-gint64
-gnc_getline (gchar **line, FILE *file)
-{
- char str[BUFSIZ];
- gint64 len;
- GString *gs = g_string_new("");
-
- if (!line || !file) return 0;
-
- while (fgets(str, sizeof(str), file) != NULL) {
- g_string_append(gs, str);
-
- len = strlen(str);
- if (str[len-1] == '\n')
- break;
- }
-
- len = gs->len;
- *line = g_string_free(gs, FALSE);
- return len;
-}
-#endif
-
-
-GList *
-gnc_csv_parse (FILE *handle)
-{
- GList *csvlists = NULL;
- ssize_t bytes_read;
- char *line;
-
- while (bytes_read = gnc_getline (&line, handle) > 0) {
- csvlists = g_list_prepend(csvlists, split_csv_line(line));
- g_free(line);
- }
-
- return g_list_reverse(csvlists);
-}
-
-
-
-#ifdef TEST_CSV
-
-static void print_glist_rec (GList *list) {
-
-}
-// print_glist_rec(list);
-
-static void print_glist(GList *list, gpointer dummy) {
-
- printf("%d: (", g_list_length(list));
-
- while (list != NULL) {
- if ((list->data != NULL) && ( *((char *) list->data) != 0)) {
- printf( "%s", list->data) ;
- }
- else {
- printf( "\"\"" );
- }
- list = list->next;
- if (list) {
- printf(" ");
- }
- }
-
- printf(")\n");
-}
-
-int main (int argc, char **argv) {
-
- FILE *fp;
- int result;
- GList *parsed_csv;
- GList *current;
- int dummy = 1;
-
- if (argc < 2) {
- printf("usage:\n\tcsv2glist fname.csv\n");
- }
-
- fp = g_fopen (argv[1], "r");
- if (fp == NULL) return 1;
-
- parsed_csv = gnc_csv_parse(fp);
-
- g_list_foreach(parsed_csv, (GFunc)print_glist, &dummy);
-
-}
-
-#endif
Deleted: gnucash/branches/csv-import/src/import-export/csv/gnc-csv2glist.h
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv2glist.h 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv2glist.h 2007-06-17 01:23:40 UTC (rev 16173)
@@ -1,39 +0,0 @@
-/*
- * gnc-csv2glist.h -- Parse Comma Separated Variable files
- *
- * Created by: Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2004 Derek Atkins <derek at ihtfp.com>
- *
- * 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 at gnu.org
- */
-
-#ifndef GNC_IMPORT_CSV_2GLIST_H
-#define GNC_IMPORT_CSV_2GLIST_H
-
-/**
- * gnc_csv_parse -- parse a Comma Separated Variable FILE into
- * a list of lists of values.
- *
- * Args: file : the input file to read
- * Returns : a list of lists of strings. Both lists and all strings
- * must be g_free()'d by the caller.
- */
-
-GList * gnc_csv_parse (FILE *file);
-
-#endif
Added: gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv-ui.xml
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv-ui.xml 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv-ui.xml 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,11 @@
+<ui>
+ <menubar>
+ <menu name="File" action="FileAction">
+ <menu name="FileImport" action="FileImportAction">
+ <placeholder name="FileImportPlaceholder">
+ <menuitem name="FileCsvImport" action="CsvImportAction"/>
+ </placeholder>
+ </menu>
+ </menu>
+ </menubar>
+</ui>
Added: gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv.c 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv.c 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,160 @@
+/*
+ * gnc-plugin-csv.c --
+ * Copyright (C) 2003 David Hampton <hampton at employees.org>
+ *
+ * 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 at gnu.org
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "gnc-plugin-csv.h"
+#include "gnc-plugin-manager.h"
+
+#include "gnc-csv-import.h"
+
+static void gnc_plugin_csv_class_init (GncPluginCsvClass *klass);
+static void gnc_plugin_csv_init (GncPluginCsv *plugin);
+static void gnc_plugin_csv_finalize (GObject *object);
+
+/* Command callbacks */
+static void gnc_plugin_csv_cmd_import (GtkAction *action, GncMainWindowActionData *data);
+
+
+#define PLUGIN_ACTIONS_NAME "gnc-plugin-csv-actions"
+#define PLUGIN_UI_FILENAME "gnc-plugin-csv-ui.xml"
+
+static GtkActionEntry gnc_plugin_actions [] = {
+ { "CsvImportAction", GTK_STOCK_CONVERT, N_("Import _CSV/Fixed-Width..."), NULL,
+ N_(" a CSV/Fixed-Width file"),
+ G_CALLBACK (gnc_plugin_csv_cmd_import) },
+};
+static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions);
+
+typedef struct GncPluginCsvPrivate
+{
+ gpointer dummy;
+} GncPluginCsvPrivate;
+
+#define GNC_PLUGIN_CSV_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_PLUGIN_CSV, GncPluginCsvPrivate))
+
+static GObjectClass *parent_class = NULL;
+
+GType
+gnc_plugin_csv_get_type (void)
+{
+ static GType gnc_plugin_csv_type = 0;
+
+ if (gnc_plugin_csv_type == 0) {
+ static const GTypeInfo our_info = {
+ sizeof (GncPluginCsvClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gnc_plugin_csv_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GncPluginCsv),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnc_plugin_csv_init,
+ };
+
+ gnc_plugin_csv_type = g_type_register_static (GNC_TYPE_PLUGIN,
+ "GncPluginCsv",
+ &our_info, 0);
+ }
+
+ return gnc_plugin_csv_type;
+}
+
+GncPlugin *
+gnc_plugin_csv_new (void)
+{
+ return GNC_PLUGIN (g_object_new (GNC_TYPE_PLUGIN_CSV, NULL));
+}
+
+static void
+gnc_plugin_csv_class_init (GncPluginCsvClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GncPluginClass *plugin_class = GNC_PLUGIN_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->finalize = gnc_plugin_csv_finalize;
+
+ /* plugin info */
+ plugin_class->plugin_name = GNC_PLUGIN_CSV_NAME;
+
+ /* widget addition/removal */
+ plugin_class->actions_name = PLUGIN_ACTIONS_NAME;
+ plugin_class->actions = gnc_plugin_actions;
+ plugin_class->n_actions = gnc_plugin_n_actions;
+ plugin_class->ui_filename = PLUGIN_UI_FILENAME;
+
+ g_type_class_add_private(klass, sizeof(GncPluginCsvPrivate));
+}
+
+static void
+gnc_plugin_csv_init (GncPluginCsv *plugin)
+{
+}
+
+static void
+gnc_plugin_csv_finalize (GObject *object)
+{
+ GncPluginCsv *plugin;
+ GncPluginCsvPrivate *priv;
+
+ g_return_if_fail (GNC_IS_PLUGIN_CSV (object));
+
+ plugin = GNC_PLUGIN_CSV (object);
+ priv = GNC_PLUGIN_CSV_GET_PRIVATE(plugin);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/************************************************************
+ * Plugin Function Implementation *
+ ************************************************************/
+
+/************************************************************
+ * Command Callbacks *
+ ************************************************************/
+
+static void
+gnc_plugin_csv_cmd_import (GtkAction *action,
+ GncMainWindowActionData *data)
+{
+ gnc_file_csv_import();
+}
+
+
+/************************************************************
+ * Plugin Bootstrapping *
+ ************************************************************/
+
+void
+gnc_plugin_csv_create_plugin (void)
+{
+ GncPlugin *plugin = gnc_plugin_csv_new ();
+
+ gnc_plugin_manager_add_plugin (gnc_plugin_manager_get (), plugin);
+}
Added: gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv.h
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv.h 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-plugin-csv.h 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,60 @@
+/*
+ * gnc-plugin-csv.h --
+ * Copyright (C) 2003 David Hampton <hampton at employees.org>
+ *
+ * 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 at gnu.org
+ */
+
+#ifndef __GNC_PLUGIN_CSV_H
+#define __GNC_PLUGIN_CSV_H
+
+#include <gtk/gtkwindow.h>
+
+#include "gnc-plugin.h"
+
+G_BEGIN_DECLS
+
+/* type macros */
+#define GNC_TYPE_PLUGIN_CSV (gnc_plugin_csv_get_type ())
+#define GNC_PLUGIN_CSV(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_PLUGIN_CSV, GncPluginCsv))
+#define GNC_PLUGIN_CSV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_PLUGIN_CSV, GncPluginCsvClass))
+#define GNC_IS_PLUGIN_CSV(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_PLUGIN_CSV))
+#define GNC_IS_PLUGIN_CSV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNC_TYPE_PLUGIN_CSV))
+#define GNC_PLUGIN_CSV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_PLUGIN_CSV, GncPluginCsvClass))
+
+#define GNC_PLUGIN_CSV_NAME "gnc-plugin-csv"
+
+/* typedefs & structures */
+typedef struct {
+ GncPlugin gnc_plugin;
+} GncPluginCsv;
+
+typedef struct {
+ GncPluginClass gnc_plugin;
+} GncPluginCsvClass;
+
+/* function prototypes */
+GType gnc_plugin_csv_get_type (void);
+
+GncPlugin *gnc_plugin_csv_new (void);
+
+void gnc_plugin_csv_create_plugin (void);
+
+G_END_DECLS
+
+#endif /* __GNC_PLUGIN_CSV_H */
Added: gnucash/branches/csv-import/src/import-export/csv/gncmod-csv-import.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gncmod-csv-import.c 2007-06-16 18:43:31 UTC (rev 16172)
+++ gnucash/branches/csv-import/src/import-export/csv/gncmod-csv-import.c 2007-06-17 01:23:40 UTC (rev 16173)
@@ -0,0 +1,91 @@
+/********************************************************************\
+ * 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 at gnu.org *
+\********************************************************************/
+/** @addtogroup Import_Export
+ @{ */
+ /**@internal
+ @file gncmod-csv-import.c
+ @brief module definition/initialization for the csv importer
+ @author Copyright (c) 2002 Benoit Grégoire bock at step.polymtl.ca
+ */
+#include "config.h"
+
+#include <gmodule.h>
+
+#include "gnc-module.h"
+#include "gnc-module-api.h"
+#include "gnc-plugin-csv.h"
+
+GNC_MODULE_API_DECL(libgncmod_csv)
+
+/* version of the gnc module system interface we require */
+int libgncmod_csv_gnc_module_system_interface = 0;
+
+/* module versioning uses libtool semantics. */
+int libgncmod_csv_gnc_module_current = 0;
+int libgncmod_csv_gnc_module_revision = 0;
+int libgncmod_csv_gnc_module_age = 0;
+
+//static GNCModule bus_core;
+//static GNCModule file;
+
+
+char *
+libgncmod_csv_gnc_module_path(void)
+{
+ return g_strdup("gnucash/import-export/csv");
+}
+
+char *
+libgncmod_csv_gnc_module_description(void)
+{
+ return g_strdup("Gnome GUI and C code for CSV importer using libcsv");
+}
+
+int
+libgncmod_csv_gnc_module_init(int refcount)
+{
+ if(!gnc_module_load("gnucash/engine", 0))
+ {
+ return FALSE;
+ }
+ if(!gnc_module_load("gnucash/app-utils", 0))
+ {
+ return FALSE;
+ }
+ if(!gnc_module_load("gnucash/gnome-utils", 0))
+ {
+ return FALSE;
+ }
+ if(!gnc_module_load("gnucash/import-export", 0))
+ {
+ return FALSE;
+ }
+
+ /* Add menu items with C callbacks */
+ gnc_plugin_csv_create_plugin();
+
+ return TRUE;
+}
+
+int
+libgncmod_csv_gnc_module_end(int refcount)
+{
+ return TRUE;
+}
+/** @}*/
More information about the gnucash-changes
mailing list