"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-output-csv.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-output-csv.c: a GsfOutput to write .csv style files.
4  *
5  * Copyright (C) 2005-2006 Morten Welinder (terra@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21
22 #include <gsf-config.h>
23 #include <gsf/gsf-output-csv.h>
24 #include <gsf/gsf-output-impl.h>
25 #include <gsf/gsf-impl-utils.h>
26 #include <gsf/gsf-utils.h>
27 #include <glib.h>
28 #include <string.h>
29
30 static GObjectClass *parent_class;
31
32 enum {
33         PROP_0,
34         PROP_SINK,
35         PROP_QUOTE,
36         PROP_QUOTING_MODE,
37         PROP_QUOTING_TRIGGERS,
38         PROP_QUOTING_ON_WHITESPACE,
39         PROP_EOL,
40         PROP_SEPARATOR
41 };
42
43 /*
44  * We really need to add a data member to the structure, but that would be
45  * an ABI change.  For now, fake it.
46  */
47 #define HACK_QUOTING_ON_WHITESPACE "hack-quoting-on-whitespace"
48
49 static void
50 gsf_output_csv_finalize (GObject *obj)
51 {
52         GsfOutputCsv *csv = (GsfOutputCsv *)obj;
53
54         if (csv->sink != NULL)
55                 g_object_unref (G_OBJECT (csv->sink));
56         g_free (csv->quote);
57         g_free (csv->quoting_triggers);
58         g_free (csv->eol);
59         g_free (csv->separator);
60         g_string_free (csv->buf, TRUE);
61
62         parent_class->finalize (obj);
63 }
64
65 static gboolean
66 gsf_output_csv_write (GsfOutput *output,
67                       size_t num_bytes, guint8 const *data)
68 {
69         GsfOutputCsv *csv = GSF_OUTPUT_CSV (output);
70         return gsf_output_write (csv->sink, num_bytes, data);
71 }
72
73 static gboolean
74 gsf_output_csv_seek (GsfOutput *output,
75                      gsf_off_t offset,
76                      GSeekType whence)
77 {
78         GsfOutputCsv *csv = GSF_OUTPUT_CSV (output);
79         return gsf_output_seek (csv->sink, offset, whence);
80 }
81
82 static gboolean
83 gsf_output_csv_close (G_GNUC_UNUSED GsfOutput *output)
84 {
85         return TRUE;
86 }
87
88 gboolean
89 gsf_output_csv_write_field (GsfOutputCsv *csv, char const *field, size_t len)
90 {
91         gboolean quote;
92         gboolean ok;
93         char const *end;
94
95         g_return_val_if_fail (GSF_IS_OUTPUT_CSV (csv), FALSE);
96         g_return_val_if_fail (field != NULL, FALSE);
97
98         if (len == (size_t)-1)
99                 len = strlen (field);
100         end = field + len;
101
102         if (csv->fields_on_line && csv->separator_len)
103                 g_string_append_len (csv->buf,
104                                      csv->separator,
105                                      csv->separator_len);
106         csv->fields_on_line = TRUE;
107
108         switch (csv->quoting_mode) {
109         default:
110         case GSF_OUTPUT_CSV_QUOTING_MODE_NEVER:
111                 quote = FALSE;
112                 break;
113         case GSF_OUTPUT_CSV_QUOTING_MODE_ALWAYS:
114                 quote = TRUE;
115                 break;
116         case GSF_OUTPUT_CSV_QUOTING_MODE_AUTO: {
117                 char const *p = field;
118                 quote = FALSE;
119                 while (p < end) {
120                         gunichar c = g_utf8_get_char (p);
121                         if (g_utf8_strchr (csv->quoting_triggers, -1, c)) {
122                                 quote = TRUE;
123                                 break;
124                         }
125                         p = g_utf8_next_char (p);
126                 }
127
128                 if (!quote && *field &&
129                     (g_unichar_isspace (g_utf8_get_char (field)) ||
130                      g_unichar_isspace (g_utf8_get_char (g_utf8_prev_char (p)))) &&
131                     g_object_get_data (G_OBJECT (csv),
132                                        HACK_QUOTING_ON_WHITESPACE) != NULL)
133                         quote = TRUE;
134                 break;
135         }
136         }
137
138         if (quote && csv->quote_len > 0) {
139                 g_string_append_len (csv->buf,
140                                      csv->quote,
141                                      csv->quote_len);
142
143                 while (field < end) {
144                         gunichar c = g_utf8_get_char (field);
145                         if (g_utf8_strchr (csv->quote, -1, c))
146                                 g_string_append_len (csv->buf,
147                                                      csv->quote,
148                                                      csv->quote_len);
149                         g_string_append_unichar (csv->buf, c);
150                         field = g_utf8_next_char (field);
151                 }
152
153                 g_string_append_len (csv->buf,
154                                      csv->quote,
155                                      csv->quote_len);
156         } else
157                 g_string_append_len (csv->buf, field, len);
158
159         ok = gsf_output_write (csv->sink, csv->buf->len, csv->buf->str);
160
161         g_string_truncate (csv->buf, 0);
162
163         return ok;
164 }
165
166 /* ------------------------------------------------------------------------- */
167
168 gboolean
169 gsf_output_csv_write_eol (GsfOutputCsv *csv)
170 {
171         g_return_val_if_fail (GSF_IS_OUTPUT_CSV (csv), FALSE);
172
173         csv->fields_on_line = FALSE;
174         return gsf_output_write (csv->sink, csv->eol_len, csv->eol);
175 }
176
177 /* ------------------------------------------------------------------------- */
178
179 GType
180 gsf_output_csv_quoting_mode_get_type (void)
181 {
182         static GType gsf_output_csv_quoting_mode_type = 0;
183
184         if (gsf_output_csv_quoting_mode_type == 0) {
185                 static GEnumValue const values[] = {
186                 { GSF_OUTPUT_CSV_QUOTING_MODE_NEVER,  (char *)"GSF_OUTPUT_CSV_QUOTING_MODE_NEVER",  (char *)"never" },
187                 { GSF_OUTPUT_CSV_QUOTING_MODE_AUTO,   (char *)"GSF_OUTPUT_CSV_QUOTING_MODE_AUTO",   (char *)"auto" },
188                 { GSF_OUTPUT_CSV_QUOTING_MODE_ALWAYS, (char *)"GSF_OUTPUT_CSV_QUOTING_MODE_ALWAYS", (char *)"always" },
189                 { 0, NULL, NULL }
190                 };
191                 gsf_output_csv_quoting_mode_type = g_enum_register_static (
192                         g_intern_static_string ("GsfOutputCsvQuotingMode"), values);
193         }
194
195         return gsf_output_csv_quoting_mode_type;
196 }
197
198 /* ------------------------------------------------------------------------- */
199
200 static void
201 gsf_output_csv_init (GObject *obj)
202 {
203         GsfOutputCsv *csv = (GsfOutputCsv *)obj;
204         csv->quoting_triggers = g_strdup ("");
205         csv->eol = g_strdup ("\n");
206         csv->eol_len = strlen (csv->eol);
207         csv->buf = g_string_new (NULL);
208         g_object_set_data (obj,
209                            HACK_QUOTING_ON_WHITESPACE,
210                            GINT_TO_POINTER (1));
211 }
212
213 static void
214 gsf_output_csv_get_property (GObject     *object,
215                              guint        property_id,
216                              GValue      *value,
217                              GParamSpec  *pspec)
218 {
219         GsfOutputCsv *csv = (GsfOutputCsv *)object;
220
221         switch (property_id) {
222         case PROP_SINK:
223                 g_value_set_object (value, csv->sink);
224                 break;
225         case PROP_QUOTE:
226                 g_value_set_string (value, csv->quote);
227                 break;
228         case PROP_QUOTING_MODE:
229                 g_value_set_enum (value, csv->quoting_mode);
230                 break;
231         case PROP_QUOTING_TRIGGERS:
232                 g_value_set_string (value, csv->quoting_triggers);
233                 break;
234         case PROP_QUOTING_ON_WHITESPACE: {
235                 gboolean qow = g_object_get_data
236                         (object, HACK_QUOTING_ON_WHITESPACE) != NULL;
237                 g_value_set_boolean (value, qow);
238                 break;
239         }
240         case PROP_EOL:
241                 g_value_set_string (value, csv->eol);
242                 break;
243         case PROP_SEPARATOR:
244                 g_value_set_string (value, csv->separator);
245                 break;
246         default:
247                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
248                 break;
249         }
250 }
251
252 static void
253 gsf_output_csv_set_sink (GsfOutputCsv *csv, GsfOutput *sink)
254 {
255         if (sink)
256                 g_object_ref (sink);
257         if (csv->sink)
258                 g_object_unref (csv->sink);
259         csv->sink = sink;
260 }
261
262 static void
263 gsf_output_csv_set_property (GObject      *object,
264                              guint         property_id,
265                              GValue const *value,
266                              GParamSpec   *pspec)
267 {
268         GsfOutputCsv *csv = (GsfOutputCsv *)object;
269         char *scopy;
270
271         switch (property_id) {
272         case PROP_SINK:
273                 gsf_output_csv_set_sink (csv, g_value_get_object (value));
274                 break;
275         case PROP_QUOTE:
276                 scopy = g_strdup (g_value_get_string (value));
277                 g_free (csv->quote);
278                 csv->quote = scopy;
279                 csv->quote_len = scopy ? strlen (scopy) : 0;
280                 break;
281         case PROP_QUOTING_MODE:
282                 csv->quoting_mode = g_value_get_enum (value);
283                 break;
284         case PROP_QUOTING_TRIGGERS:
285                 scopy = g_strdup (g_value_get_string (value));
286                 g_free (csv->quoting_triggers);
287                 csv->quoting_triggers = scopy ? scopy : g_strdup ("");
288                 if (*csv->quoting_triggers)
289                         csv->quoting_mode = GSF_OUTPUT_CSV_QUOTING_MODE_AUTO;
290                 break;
291         case PROP_QUOTING_ON_WHITESPACE: {
292                 gboolean qow = g_value_get_boolean (value);
293                 g_object_set_data (object,
294                                    HACK_QUOTING_ON_WHITESPACE,
295                                    GINT_TO_POINTER (qow));
296                 break;
297         }
298         case PROP_EOL:
299                 scopy = g_strdup (g_value_get_string (value));
300                 g_free (csv->eol);
301                 csv->eol = scopy ? scopy : g_strdup ("");
302                 csv->eol_len = strlen (csv->eol);
303                 break;
304         case PROP_SEPARATOR:
305                 scopy = g_strdup (g_value_get_string (value));
306                 g_free (csv->separator);
307                 csv->separator = scopy;
308                 csv->separator_len = scopy ? strlen (scopy) : 0;
309                 break;
310         default:
311                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
312                 break;
313         }
314 }
315
316 static void
317 gsf_output_csv_class_init (GObjectClass *gobject_class)
318 {
319         GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
320
321         gobject_class->finalize     = gsf_output_csv_finalize;
322         gobject_class->set_property = gsf_output_csv_set_property;
323         gobject_class->get_property = gsf_output_csv_get_property;
324         output_class->Write         = gsf_output_csv_write;
325         output_class->Seek          = gsf_output_csv_seek;
326         output_class->Close         = gsf_output_csv_close;
327
328         g_object_class_install_property
329                 (gobject_class,
330                  PROP_SINK,
331                  g_param_spec_object ("sink", "Sink",
332                                       "Where the compressed data is written.",
333                                       GSF_OUTPUT_TYPE,
334                                       GSF_PARAM_STATIC |
335                                       G_PARAM_READWRITE));
336         g_object_class_install_property
337                 (gobject_class,
338                  PROP_QUOTE,
339                  g_param_spec_string ("quote", "Quote",
340                                       "The string used for quoting fields.",
341                                       "\"",
342                                       GSF_PARAM_STATIC |
343                                       G_PARAM_CONSTRUCT |
344                                       G_PARAM_READWRITE));
345         g_object_class_install_property
346                 (gobject_class,
347                  PROP_QUOTING_MODE,
348                  g_param_spec_enum ("quoting-mode",
349                                     "Quoting Mode",
350                                     "When to quote fields.",
351                                     GSF_OUTPUT_CSV_QUOTING_MODE_TYPE,
352                                     GSF_OUTPUT_CSV_QUOTING_MODE_NEVER,
353                                     GSF_PARAM_STATIC |
354                                     G_PARAM_CONSTRUCT |
355                                     G_PARAM_READWRITE));
356         g_object_class_install_property
357                 (gobject_class,
358                  PROP_QUOTING_TRIGGERS,
359                  g_param_spec_string ("quoting-triggers", "Quoting Triggers",
360                                       "Characters that cause field quoting.",
361                                       NULL,
362                                       GSF_PARAM_STATIC |
363                                       G_PARAM_READWRITE));
364         g_object_class_install_property
365                 (gobject_class,
366                  PROP_QUOTING_ON_WHITESPACE,
367                  g_param_spec_boolean ("quoting-on-whitespace",
368                                        "Quoting On Whitespace",
369                                        "Does initial or terminal whitespace force quoting?",
370                                        TRUE,
371                                        GSF_PARAM_STATIC |
372                                        G_PARAM_READWRITE));
373         g_object_class_install_property
374                 (gobject_class,
375                  PROP_SEPARATOR,
376                  g_param_spec_string ("separator", "Separator",
377                                       "The field separator.",
378                                       ",",
379                                       GSF_PARAM_STATIC |
380                                       G_PARAM_CONSTRUCT |
381                                       G_PARAM_READWRITE));
382         g_object_class_install_property
383                 (gobject_class,
384                  PROP_EOL,
385                  g_param_spec_string ("eol", "eol",
386                                       "The end-of-line marker.",
387                                       "\n",
388                                       GSF_PARAM_STATIC |
389                                       G_PARAM_CONSTRUCT |
390                                       G_PARAM_READWRITE));
391
392         parent_class = g_type_class_peek_parent (gobject_class);
393 }
394
395 GSF_CLASS (GsfOutputCsv, gsf_output_csv,
396            gsf_output_csv_class_init, gsf_output_csv_init,
397            GSF_OUTPUT_TYPE)
398