2 * (GLABELS) Label and Business Card Creation program for GNOME
4 * merge_text.c: text-file merge backend module
6 * Copyright (C) 2001 Jim Evins <evins@snaught.com>.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "merge-text.h"
31 #define LINE_BUF_LEN 1024
33 /*===========================================*/
35 /*===========================================*/
37 struct _glMergeTextPrivate {
51 /*===========================================*/
53 /*===========================================*/
55 static glMergeClass *parent_class = NULL;
58 /*===========================================*/
59 /* Local function prototypes */
60 /*===========================================*/
62 static void gl_merge_text_class_init (glMergeTextClass *klass);
63 static void gl_merge_text_instance_init (glMergeText *object);
64 static void gl_merge_text_finalize (GObject *object);
66 static void gl_merge_text_set_property (GObject *object,
71 static void gl_merge_text_get_property (GObject *object,
76 static GList *gl_merge_text_get_key_list (glMerge *merge);
77 static gchar *gl_merge_text_get_primary_key (glMerge *merge);
78 static void gl_merge_text_open (glMerge *merge);
79 static void gl_merge_text_close (glMerge *merge);
80 static glMergeRecord *gl_merge_text_get_record (glMerge *merge);
81 static void gl_merge_text_copy (glMerge *dst_merge,
84 static GList *parse_line (FILE *fp,
86 static void free_fields (GList **fields);
89 /*****************************************************************************/
90 /* Boilerplate object stuff. */
91 /*****************************************************************************/
93 gl_merge_text_get_type (void)
95 static GType type = 0;
98 static const GTypeInfo info = {
99 sizeof (glMergeTextClass),
102 (GClassInitFunc) gl_merge_text_class_init,
105 sizeof (glMergeText),
107 (GInstanceInitFunc) gl_merge_text_instance_init,
111 type = g_type_register_static (GL_TYPE_MERGE,
112 "glMergeText", &info, 0);
119 gl_merge_text_class_init (glMergeTextClass *klass)
121 GObjectClass *object_class = (GObjectClass *) klass;
122 glMergeClass *merge_class = (glMergeClass *) klass;
124 gl_debug (DEBUG_MERGE, "START");
126 parent_class = g_type_class_peek_parent (klass);
128 object_class->set_property = gl_merge_text_set_property;
129 object_class->get_property = gl_merge_text_get_property;
131 g_object_class_install_property
134 g_param_spec_char ("delim", NULL, NULL,
136 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
138 object_class->finalize = gl_merge_text_finalize;
140 merge_class->get_key_list = gl_merge_text_get_key_list;
141 merge_class->get_primary_key = gl_merge_text_get_primary_key;
142 merge_class->open = gl_merge_text_open;
143 merge_class->close = gl_merge_text_close;
144 merge_class->get_record = gl_merge_text_get_record;
145 merge_class->copy = gl_merge_text_copy;
147 gl_debug (DEBUG_MERGE, "END");
151 gl_merge_text_instance_init (glMergeText *merge_text)
153 gl_debug (DEBUG_MERGE, "START");
155 merge_text->private = g_new0 (glMergeTextPrivate, 1);
157 gl_debug (DEBUG_MERGE, "END");
161 gl_merge_text_finalize (GObject *object)
163 gl_debug (DEBUG_MERGE, "START");
165 g_return_if_fail (object && GL_IS_MERGE_TEXT (object));
167 G_OBJECT_CLASS (parent_class)->finalize (object);
169 gl_debug (DEBUG_MERGE, "END");
172 /*--------------------------------------------------------------------------*/
174 /*--------------------------------------------------------------------------*/
176 gl_merge_text_set_property (GObject *object,
181 glMergeText *merge_text;
183 merge_text = GL_MERGE_TEXT (object);
188 merge_text->private->delim = g_value_get_char (value);
189 gl_debug (DEBUG_MERGE, "ARG \"delim\" = \"%c\"",
190 merge_text->private->delim);
194 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
201 /*--------------------------------------------------------------------------*/
203 /*--------------------------------------------------------------------------*/
205 gl_merge_text_get_property (GObject *object,
210 glMergeText *merge_text;
212 merge_text = GL_MERGE_TEXT (object);
217 g_value_set_char (value, merge_text->private->delim);
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
228 /*--------------------------------------------------------------------------*/
230 /*--------------------------------------------------------------------------*/
232 gl_merge_text_get_key_list (glMerge *merge)
234 glMergeText *merge_text;
235 GList *record_list, *p_rec;
236 glMergeRecord *record;
238 gint i_field, n_fields, n_fields_max = 0;
241 /* Field keys are simply column numbers. */
243 gl_debug (DEBUG_MERGE, "BEGIN");
245 merge_text = GL_MERGE_TEXT (merge);
247 record_list = (GList *)gl_merge_get_record_list (merge);
249 for ( p_rec=record_list; p_rec!=NULL; p_rec=p_rec->next ) {
250 record = (glMergeRecord *)p_rec->data;
253 for ( p_field=record->field_list; p_field!=NULL; p_field=p_field->next ) {
256 if ( n_fields > n_fields_max ) n_fields_max = n_fields;
260 for (i_field=1; i_field <= n_fields_max; i_field++) {
261 key_list = g_list_append (key_list, g_strdup_printf ("%d", i_field));
265 gl_debug (DEBUG_MERGE, "END");
270 /*--------------------------------------------------------------------------*/
271 /* Get "primary" key. */
272 /*--------------------------------------------------------------------------*/
274 gl_merge_text_get_primary_key (glMerge *merge)
276 /* For now, let's always assume the first column is the primary key. */
277 return g_strdup ("1");
280 /*--------------------------------------------------------------------------*/
281 /* Open merge source. */
282 /*--------------------------------------------------------------------------*/
284 gl_merge_text_open (glMerge *merge)
286 glMergeText *merge_text;
289 merge_text = GL_MERGE_TEXT (merge);
291 src = gl_merge_get_src (merge);
294 merge_text->private->fp = fopen (src, "r");
300 /*--------------------------------------------------------------------------*/
301 /* Close merge source. */
302 /*--------------------------------------------------------------------------*/
304 gl_merge_text_close (glMerge *merge)
306 glMergeText *merge_text;
308 merge_text = GL_MERGE_TEXT (merge);
310 if (merge_text->private->fp != NULL) {
312 fclose (merge_text->private->fp);
313 merge_text->private->fp = NULL;
318 /*--------------------------------------------------------------------------*/
319 /* Get next record from merge source, NULL if no records left (i.e EOF) */
320 /*--------------------------------------------------------------------------*/
321 static glMergeRecord *
322 gl_merge_text_get_record (glMerge *merge)
324 glMergeText *merge_text;
327 glMergeRecord *record;
332 merge_text = GL_MERGE_TEXT (merge);
334 delim = merge_text->private->delim;
335 fp = merge_text->private->fp;
341 fields = parse_line (fp, delim);
342 if ( fields == NULL ) {
346 record = g_new0 (glMergeRecord, 1);
347 record->select_flag = TRUE;
349 for (p=fields; p != NULL; p=p->next) {
351 field = g_new0 (glMergeField, 1);
352 field->key = g_strdup_printf ("%d", i_field++);
353 #ifndef CSV_ALWAYS_UTF8
354 field->value = g_locale_to_utf8 (p->data, -1, NULL, NULL, NULL);
356 field->value = g_strdup (p->data);
359 record->field_list = g_list_append (record->field_list, field);
361 free_fields (&fields);
366 /*---------------------------------------------------------------------------*/
367 /* Copy merge_text specific fields. */
368 /*---------------------------------------------------------------------------*/
370 gl_merge_text_copy (glMerge *dst_merge,
373 glMergeText *dst_merge_text;
374 glMergeText *src_merge_text;
376 dst_merge_text = GL_MERGE_TEXT (dst_merge);
377 src_merge_text = GL_MERGE_TEXT (src_merge);
379 dst_merge_text->private->delim = src_merge_text->private->delim;
382 /*---------------------------------------------------------------------------*/
383 /* PRIVATE. Parse line (quoted values may span multiple lines). */
384 /*---------------------------------------------------------------------------*/
386 parse_line (FILE *fp,
392 enum { BEGIN, NORMAL, NORMAL_ESCAPED,
393 QUOTED, QUOTED_ESCAPED, QUOTED_QUOTE1,
397 string = g_string_new( "" );
398 while ( state != DONE ) {
406 state = NORMAL_ESCAPED;
416 /* treat as one empty field. */
417 list = g_list_append (list,
426 string = g_string_append_c (string, c);
428 list = g_list_append (list,
429 g_strdup (string->str));
430 string = g_string_assign( string, "" );
440 state = NORMAL_ESCAPED;
450 list = g_list_append (list,
451 g_strdup (string->str));
456 string = g_string_append_c (string, c);
458 list = g_list_append (list,
459 g_strdup (string->str));
460 string = g_string_assign( string, "" );
469 string = g_string_append_c (string, '\n');
473 string = g_string_append_c (string, '\t');
477 /* Strip CR, stay ESCAPED. */
483 string = g_string_append_c (string, c);
492 state = QUOTED_ESCAPED;
495 state = QUOTED_QUOTE1;
501 /* File ended mid way through quoted item */
502 list = g_list_append (list,
503 g_strdup (string->str));
507 string = g_string_append_c (string, c);
515 string = g_string_append_c (string, '\n');
519 string = g_string_append_c (string, '\t');
523 /* Strip CR, stay ESCAPED. */
526 /* File ended mid way through quoted item */
527 list = g_list_append (list,
528 g_strdup (string->str));
532 string = g_string_append_c (string, c);
541 /* insert quotes in string, stay quoted. */
542 string = g_string_append_c (string, c);
546 /* Strip CR, return to NORMAL. */
551 /* line or file ended after quoted item */
552 list = g_list_append (list,
553 g_strdup (string->str));
558 string = g_string_append_c (string, c);
560 list = g_list_append (list,
561 g_strdup (string->str));
562 string = g_string_assign( string, "" );
570 g_assert_not_reached();
575 g_string_free( string, TRUE );
580 /*---------------------------------------------------------------------------*/
581 /* Free list of fields. */
582 /*---------------------------------------------------------------------------*/
584 free_fields (GList ** list)
588 for (p = *list; p != NULL; p = p->next) {