]> git.sur5r.net Git - glabels/blob - glabels2/src/merge-text.c
New document merge API.
[glabels] / glabels2 / src / merge-text.c
1 /*
2  *  (GLABELS) Label and Business Card Creation program for GNOME
3  *
4  *  merge_text.c:  text-file merge backend module
5  *
6  *  Copyright (C) 2001  Jim Evins <evins@snaught.com>.
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include <config.h>
24
25 #include <stdio.h>
26
27 #include "merge-text.h"
28
29 #include "debug.h"
30
31 #define LINE_BUF_LEN 1024
32
33 /*===========================================*/
34 /* Private types                             */
35 /*===========================================*/
36
37 struct _glMergeTextPrivate {
38         gchar             delim;
39         FILE             *fp;
40 };
41
42 enum {
43         LAST_SIGNAL
44 };
45
46 enum {
47         ARG_0,
48         ARG_DELIM,
49 };
50
51 /*===========================================*/
52 /* Private globals                           */
53 /*===========================================*/
54
55 static glMergeClass *parent_class = NULL;
56
57 static guint signals[LAST_SIGNAL] = {0};
58
59
60 /*===========================================*/
61 /* Local function prototypes                 */
62 /*===========================================*/
63
64 static void           gl_merge_text_class_init     (glMergeTextClass *klass);
65 static void           gl_merge_text_instance_init  (glMergeText      *object);
66 static void           gl_merge_text_finalize       (GObject          *object);
67
68 static void           gl_merge_text_set_property   (GObject          *object,
69                                                     guint             param_id,
70                                                     const GValue     *value,
71                                                     GParamSpec       *pspec);
72
73 static void           gl_merge_text_get_property   (GObject          *object,
74                                                     guint             param_id,
75                                                     GValue           *value,
76                                                     GParamSpec       *pspec);
77
78 static GList         *gl_merge_text_get_key_list   (glMerge          *merge);
79 static void           gl_merge_text_open           (glMerge          *merge);
80 static void           gl_merge_text_close          (glMerge          *merge);
81 static glMergeRecord *gl_merge_text_get_record     (glMerge          *merge);
82 static void           gl_merge_text_copy           (glMerge          *dst_merge,
83                                                     glMerge          *src_merge);
84
85 static GList         *split_fields                 (gchar            *line,
86                                                     gchar             delim);
87 static void           free_fields                  (GList           **fields);
88
89 \f
90 /*****************************************************************************/
91 /* Boilerplate object stuff.                                                 */
92 /*****************************************************************************/
93 GType
94 gl_merge_text_get_type (void)
95 {
96         static GType type = 0;
97
98         if (!type) {
99                 GTypeInfo info = {
100                         sizeof (glMergeTextClass),
101                         NULL,
102                         NULL,
103                         (GClassInitFunc) gl_merge_text_class_init,
104                         NULL,
105                         NULL,
106                         sizeof (glMergeText),
107                         0,
108                         (GInstanceInitFunc) gl_merge_text_instance_init,
109                 };
110
111                 type = g_type_register_static (GL_TYPE_MERGE,
112                                                "glMergeText", &info, 0);
113         }
114
115         return type;
116 }
117
118 static void
119 gl_merge_text_class_init (glMergeTextClass *klass)
120 {
121         GObjectClass *object_class = (GObjectClass *) klass;
122         glMergeClass *merge_class  = (glMergeClass *) klass;
123
124         gl_debug (DEBUG_MERGE, "START");
125
126         parent_class = g_type_class_peek_parent (klass);
127
128         object_class->set_property = gl_merge_text_set_property;
129         object_class->get_property = gl_merge_text_get_property;
130
131         g_object_class_install_property
132                 (object_class,
133                  ARG_DELIM,
134                  g_param_spec_char ("delim", NULL, NULL,
135                                     0, 0x7F, ',',
136                                     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
137
138         object_class->finalize = gl_merge_text_finalize;
139
140         merge_class->get_key_list = gl_merge_text_get_key_list;
141         merge_class->open         = gl_merge_text_open;
142         merge_class->close        = gl_merge_text_close;
143         merge_class->get_record   = gl_merge_text_get_record;
144         merge_class->copy         = gl_merge_text_copy;
145
146         gl_debug (DEBUG_MERGE, "END");
147 }
148
149 static void
150 gl_merge_text_instance_init (glMergeText *merge_text)
151 {
152         gl_debug (DEBUG_MERGE, "START");
153
154         merge_text->private = g_new0 (glMergeTextPrivate, 1);
155
156         gl_debug (DEBUG_MERGE, "END");
157 }
158
159 static void
160 gl_merge_text_finalize (GObject *object)
161 {
162         gl_debug (DEBUG_MERGE, "START");
163
164         g_return_if_fail (object && GL_IS_MERGE_TEXT (object));
165
166         G_OBJECT_CLASS (parent_class)->finalize (object);
167
168         gl_debug (DEBUG_MERGE, "END");
169 }
170
171 /*--------------------------------------------------------------------------*/
172 /* Set argument.                                                            */
173 /*--------------------------------------------------------------------------*/
174 static void
175 gl_merge_text_set_property (GObject      *object,
176                             guint         param_id,
177                             const GValue *value,
178                             GParamSpec   *pspec)
179 {
180         glMergeText *merge_text;
181
182         merge_text = GL_MERGE_TEXT (object);
183
184         switch (param_id) {
185
186         case ARG_DELIM:
187                 merge_text->private->delim = g_value_get_char (value);
188                 gl_debug (DEBUG_MERGE, "ARG \"delim\" = \"%c\"",
189                           merge_text->private->delim);
190                 break;
191
192         default:
193                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
194                 break;
195
196         }
197
198 }
199
200 /*--------------------------------------------------------------------------*/
201 /* Get argument.                                                            */
202 /*--------------------------------------------------------------------------*/
203 static void
204 gl_merge_text_get_property (GObject     *object,
205                             guint        param_id,
206                             GValue      *value,
207                             GParamSpec  *pspec)
208 {
209         glMergeText *merge_text;
210
211         merge_text = GL_MERGE_TEXT (object);
212
213         switch (param_id) {
214
215         case ARG_DELIM:
216                 g_value_set_char (value, merge_text->private->delim);
217                 break;
218
219         default:
220                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
221                 break;
222
223         }
224
225 }
226
227 /*--------------------------------------------------------------------------*/
228 /* Get key list.                                                            */
229 /*--------------------------------------------------------------------------*/
230 static GList *
231 gl_merge_text_get_key_list (glMerge *merge)
232 {
233         glMergeText *merge_text;
234         gchar       *src;
235         FILE        *fp;
236         gchar        delim;
237         gchar        line[LINE_BUF_LEN];
238         GList       *fields, *p;
239         gint         i_field, n_fields, n_fields_max = 0;
240         GList       *key_list;
241         
242         /* Fields are simply column numbers. */
243         /* FIXME:  the key_list should probably be cached, and only re-evaluated */
244         /*         if the source has changed. */
245
246         merge_text = GL_MERGE_TEXT (merge);
247
248         src = gl_merge_get_src (merge);
249         delim = merge_text->private->delim;
250
251         fp = fopen (src, "r");
252         if ( fp == NULL ) {
253                 return NULL;
254         }
255
256         while ( fgets (line, LINE_BUF_LEN, fp) != NULL ) {
257
258                 if (TRUE /* TODO: skip blank lines or comments */ ) {
259                         g_strchomp (line);
260                         fields = split_fields (line, delim);
261                         n_fields = 0;
262                         for (p=fields; p != NULL; p=p->next) {
263                                 n_fields++;
264                         }
265                         free_fields (&fields);
266                         if ( n_fields > n_fields_max ) n_fields = n_fields_max;
267                 }
268
269         }
270
271         key_list = NULL;
272         for (i_field=1; i_field <= n_fields; i_field++) {
273                 key_list = g_list_append (key_list, g_strdup_printf ("%d", i_field));
274         }
275
276         return key_list;
277 }
278
279 /*--------------------------------------------------------------------------*/
280 /* Open merge source.                                                       */
281 /*--------------------------------------------------------------------------*/
282 static void
283 gl_merge_text_open (glMerge *merge)
284 {
285         glMergeText *merge_text;
286         gchar       *src;
287
288         merge_text = GL_MERGE_TEXT (merge);
289
290         src = gl_merge_get_src (merge);
291
292         if (src != NULL) {
293                 merge_text->private->fp = fopen (src, "r");
294         }
295
296         g_free (src);
297 }
298
299 /*--------------------------------------------------------------------------*/
300 /* Close merge source.                                                      */
301 /*--------------------------------------------------------------------------*/
302 static void
303 gl_merge_text_close (glMerge *merge)
304 {
305         glMergeText *merge_text;
306
307         merge_text = GL_MERGE_TEXT (merge);
308
309         if (merge_text->private->fp != NULL) {
310
311                 fclose (merge_text->private->fp);
312                 merge_text->private->fp = NULL;
313
314         }
315 }
316
317 /*--------------------------------------------------------------------------*/
318 /* Get next record from merge source, NULL if no records left (i.e EOF)     */
319 /*--------------------------------------------------------------------------*/
320 static glMergeRecord *
321 gl_merge_text_get_record (glMerge *merge)
322 {
323         glMergeText   *merge_text;
324         gchar          delim;
325         FILE          *fp;
326         gchar          line[LINE_BUF_LEN];
327         glMergeRecord *record = NULL;
328         GList         *fields, *p;
329         gint           i_field;
330         glMergeField  *field;
331
332         merge_text = GL_MERGE_TEXT (merge);
333
334         delim = merge_text->private->delim;
335         fp    = merge_text->private->fp;
336
337         if (fp == NULL) {
338                 return NULL;
339         }
340                
341         while ( fgets (line, LINE_BUF_LEN, fp) != NULL ) {
342
343                 if (TRUE /* TODO: skip blank lines or comments */ ) {
344                         g_strchomp (line);
345                         record = g_new0 (glMergeRecord, 1);
346                         record->select_flag = TRUE;
347                         fields = split_fields (line, delim);
348                         i_field = 1;
349                         for (p=fields; p != NULL; p=p->next) {
350                                 
351                                 field = g_new0 (glMergeField, 1);
352                                 field->key = g_strdup_printf ("%d", i_field++);
353                                 field->value = g_strdup (p->data);
354
355                                 record->field_list =
356                                         g_list_append (record->field_list, field);
357                         }
358                         free_fields (&fields);
359                         return record;
360                 }
361
362         }
363
364         return NULL;
365 }
366
367 /*---------------------------------------------------------------------------*/
368 /* Copy merge_text specific fields.                                          */
369 /*---------------------------------------------------------------------------*/
370 static void
371 gl_merge_text_copy (glMerge *dst_merge,
372                     glMerge *src_merge)
373 {
374         glMergeText *dst_merge_text;
375         glMergeText *src_merge_text;
376
377         dst_merge_text = GL_MERGE_TEXT (dst_merge);
378         src_merge_text = GL_MERGE_TEXT (src_merge);
379
380         dst_merge_text->private->delim = src_merge_text->private->delim;
381 }
382
383 /*---------------------------------------------------------------------------*/
384 /* PRIVATE.  Split out fields by delimiter while decoding things like "\n".  */
385 /*---------------------------------------------------------------------------*/
386 static GList * split_fields ( gchar *line,
387                               gchar delim )
388 {
389         GList *list = NULL;
390         GString *string;
391         gchar *c;
392         enum { NORMAL, ESCAPED } state;
393
394         g_return_val_if_fail (line != NULL, NULL);
395
396         state = NORMAL;
397         string = g_string_new( "" );
398         for ( c=line; *c!=0; c++ ) {
399
400                 switch (state) {
401
402                 case NORMAL:
403                         if ( *c == '\\' ) {
404                                 state = ESCAPED;
405                         } else if ( *c != delim ) {
406                                 string = g_string_append_c (string, *c);
407                         } else {
408                                 list = g_list_append (list,
409                                                       g_strdup (string->str));
410                                 string = g_string_assign( string, "" );
411                         }
412                         break;
413
414                 case ESCAPED:
415                         switch (*c) {
416                         case 'n':
417                                 string = g_string_append_c (string, '\n');
418                                 break;
419                         case 't':
420                                 string = g_string_append_c (string, '\t');
421                                 break;
422                         default:
423                                 string = g_string_append_c (string, *c);
424                                 break;
425                         }
426                         state = NORMAL;
427                         break;
428
429                 default:
430                         g_assert_not_reached();
431                         break;
432                 }
433
434         }
435         list = g_list_append( list, g_strdup(string->str) );
436         g_string_free( string, TRUE );
437
438         return list;
439 }
440
441 /*---------------------------------------------------------------------------*/
442 /* Free list of fields.                                                      */
443 /*---------------------------------------------------------------------------*/
444 void
445 free_fields (GList ** list)
446 {
447         GList *p;
448
449         for (p = *list; p != NULL; p = p->next) {
450                 g_free (p->data);
451                 p->data = NULL;
452         }
453
454         g_list_free (*list);
455         *list = NULL;
456 }
457