1.12.42
[gnumeric.git] / plugins / dif / dif.c
blob7ae75585e5ae39e4160b40800a598b6537314605
1 /*
2 * dif.c: read/write sheets using a DIF encoding.
4 * Authors:
5 * Kevin Handy <kth@srv.net>
6 * Zbigniew Chyla <cyba@gnome.pl>
8 * Based on ff-csv code.
9 */
10 #include <gnumeric-config.h>
11 #include <glib/gi18n-lib.h>
12 #include <gnumeric.h>
13 #include <libgnumeric.h>
15 #include <cell.h>
16 #include <sheet.h>
17 #include <value.h>
18 #include <numbers.h>
19 #include <gutils.h>
20 #include <goffice/goffice.h>
21 #include <workbook-view.h>
22 #include <workbook.h>
23 #include <gnm-plugin.h>
25 #include <gsf/gsf-input-textline.h>
26 #include <gsf/gsf-output.h>
27 #include <gsf/gsf-utils.h>
28 #include <string.h>
29 #include <stdlib.h>
31 GNM_PLUGIN_MODULE_HEADER;
33 #define N_INPUT_LINES_BETWEEN_UPDATES 50
35 void dif_file_open (GOFileOpener const *fo, GOIOContext *io_context,
36 WorkbookView *wbv, GsfInput *input);
37 void dif_file_save (GOFileSaver const *fs, GOIOContext *io_context,
38 WorkbookView const *wbv, GsfOutput *out);
40 typedef struct {
41 GOIOContext *io_context;
43 GsfInputTextline *input;
44 gint line_no;
45 gsize line_len;
46 gchar *line;
48 Sheet *sheet;
50 GIConv converter;
51 } DifInputContext;
54 static DifInputContext *
55 dif_input_context_new (GOIOContext *io_context, Workbook *wb, GsfInput *input)
57 DifInputContext *ctxt = NULL;
59 ctxt = g_new (DifInputContext, 1);
60 ctxt->io_context = io_context;
62 ctxt->input = (GsfInputTextline *) gsf_input_textline_new (input);
64 ctxt->line_no = 1;
65 ctxt->line = NULL;
66 ctxt->sheet = workbook_sheet_add (wb, -1, GNM_DEFAULT_COLS, GNM_DEFAULT_ROWS);
67 ctxt->converter = g_iconv_open ("UTF-8", "ISO-8859-1");
69 go_io_progress_message (io_context, _("Reading file..."));
71 return ctxt;
74 static void
75 dif_input_context_destroy (DifInputContext *ctxt)
77 go_io_progress_unset (ctxt->io_context);
78 g_object_unref (ctxt->input); ctxt->input = NULL;
79 gsf_iconv_close (ctxt->converter);
80 g_free (ctxt->line);
81 g_free (ctxt);
84 static gboolean
85 dif_get_line (DifInputContext *ctxt)
87 char *raw;
89 if (NULL == (raw = gsf_input_textline_ascii_gets (ctxt->input)))
90 return FALSE;
92 g_free (ctxt->line);
93 ctxt->line = g_convert_with_iconv (raw, -1, ctxt->converter,
94 NULL, &ctxt->line_len, NULL);
96 ctxt->line_no++;
97 return ctxt->line != NULL;
101 * Raturns FALSE on EOF.
103 static gboolean
104 dif_parse_header (DifInputContext *ctxt)
106 while (1) {
107 gchar *topic = NULL, *num_line = NULL, *str_line = NULL;
108 size_t str_line_len;
109 int res = -1;
111 if (!dif_get_line (ctxt)) {
112 res = FALSE;
113 goto out;
115 topic = g_strdup (ctxt->line);
117 if (!dif_get_line (ctxt)) {
118 res = FALSE;
119 goto out;
121 num_line = g_strdup (ctxt->line);
123 if (!dif_get_line (ctxt)) {
124 res = FALSE;
125 goto out;
127 str_line = g_strdup (ctxt->line);
128 str_line_len = strlen (str_line);
130 if (strcmp (topic, "TABLE") == 0) {
131 gchar *name;
133 if (str_line_len > 2 && str_line[0] == '"' && str_line[str_line_len - 1] == '"') {
134 str_line[str_line_len - 1] = '\0';
135 name = str_line + 1;
136 } else {
137 name = str_line;
139 if (name[0] != '\0') {
140 /* FIXME - rename the sheet */
142 } else if (strcmp (topic, "DATA") == 0) {
143 res = TRUE;
147 * Other "standard" header entry items are:
148 * SIZE, LABEL, UNITS, TUPLES, VECTORS, COMMENT, MINORSTART,
149 * TRUELENGTH, PERIODICITY, DISPLAYUNITS
152 out:
153 g_free (topic);
154 g_free (num_line);
155 g_free (str_line);
156 if (res >= 0)
157 return res;
162 * Raturns FALSE on EOF.
164 static gboolean
165 dif_parse_data (DifInputContext *ctxt)
167 gboolean too_many_rows = FALSE, too_many_columns = FALSE;
168 gint row = -1, col = 0;
169 gint val_type;
170 GnmCell *cell;
171 gchar *msg;
173 while (1) {
174 if (!dif_get_line (ctxt))
175 return FALSE;
177 val_type = atoi (ctxt->line);
178 if (val_type == 0) {
179 gchar const *comma = strchr (ctxt->line, ',');
180 if (comma == NULL)
181 go_io_warning (ctxt->io_context,
182 _("Syntax error at line %d. Ignoring."),
183 ctxt->line_no);
184 else if (col > gnm_sheet_get_max_cols (ctxt->sheet)) {
185 too_many_columns = TRUE;
186 break;
187 } else {
188 gnm_float num = gnm_strto (comma+1, NULL);
189 GnmValue *v = NULL;
191 if (!dif_get_line (ctxt))
192 return FALSE;
194 if (0 == strcmp (ctxt->line, "V")) { /* V value */
195 v = value_new_float (num);
196 } else if (0 == strcmp (ctxt->line, "NA")) { /* NA not available res must be O */
197 v = value_new_error_NA (NULL);
198 } else if (0 == strcmp (ctxt->line, "TRUE")) { /* TRUE bool T res must be 1 */
199 v = value_new_bool (TRUE);
200 } else if (0 == strcmp (ctxt->line, "FALSE")) { /* FALSE bool F res must be O */
201 v = value_new_bool (TRUE);
202 } else if (0 == strcmp (ctxt->line, "ERROR")) { /* ERROR err res must be O */
203 go_io_warning (ctxt->io_context,
204 _("Unknown value type '%s' at line %d. Ignoring."),
205 ctxt->line, ctxt->line_no);
208 if (NULL != v) {
209 cell = sheet_cell_fetch (ctxt->sheet, col, row);
210 gnm_cell_set_value (cell, v);
212 col++;
214 } else if (val_type == 1) {
215 if (!dif_get_line (ctxt))
216 return FALSE;
217 if (col > gnm_sheet_get_max_cols (ctxt->sheet)) {
218 too_many_columns = TRUE;
219 continue;
221 cell = sheet_cell_fetch (ctxt->sheet, col, row);
222 if (ctxt->line_len >= 2 &&
223 ctxt->line[0] == '"' && ctxt->line[ctxt->line_len - 1] == '"') {
224 ctxt->line[ctxt->line_len - 1] = '\0';
225 gnm_cell_set_text (cell, ctxt->line + 1);
226 } else
227 gnm_cell_set_text (cell, ctxt->line);
228 col++;
229 } else if (val_type == -1) {
230 if (!dif_get_line (ctxt))
231 return FALSE;
232 if (strcmp (ctxt->line, "BOT") == 0) {
233 col = 0;
234 row++;
235 if (row > gnm_sheet_get_max_rows (ctxt->sheet)) {
236 too_many_rows = TRUE;
237 break;
239 } else if (strcmp (ctxt->line, "EOD") == 0) {
240 break;
241 } else {
242 msg = g_strdup_printf (
243 _("Unknown data value \"%s\" at line %d. Ignoring."),
244 ctxt->line, ctxt->line_no);
245 g_warning ("%s", msg);
246 g_free (msg);
248 } else {
249 msg = g_strdup_printf (
250 _("Unknown value type %d at line %d. Ignoring."),
251 val_type, ctxt->line_no);
252 g_warning ("%s", msg);
253 g_free (msg);
254 (void) dif_get_line (ctxt);
258 if (too_many_rows) {
259 g_warning (_("DIF file has more than the maximum number of rows %d. "
260 "Ignoring remaining rows."), gnm_sheet_get_max_rows (ctxt->sheet));
262 if (too_many_columns) {
263 g_warning (_("DIF file has more than the maximum number of columns %d. "
264 "Ignoring remaining columns."), gnm_sheet_get_max_cols (ctxt->sheet));
267 return TRUE;
270 static void
271 dif_parse_sheet (DifInputContext *ctxt)
273 GnmLocale *locale = gnm_push_C_locale ();
275 if (!dif_parse_header (ctxt)) {
276 go_io_error_info_set (ctxt->io_context, go_error_info_new_printf (
277 _("Unexpected end of file at line %d while reading header."),
278 ctxt->line_no));
279 } else if (!dif_parse_data(ctxt)) {
280 go_io_error_info_set (ctxt->io_context, go_error_info_new_printf (
281 _("Unexpected end of file at line %d while reading data."),
282 ctxt->line_no));
285 gnm_pop_C_locale (locale);
288 void
289 dif_file_open (GOFileOpener const *fo, GOIOContext *io_context,
290 WorkbookView *wbv, GsfInput *input)
292 Workbook *wb = wb_view_get_workbook (wbv);
293 DifInputContext *ctxt = dif_input_context_new (io_context, wb, input);
295 workbook_set_saveinfo (wb, GO_FILE_FL_MANUAL_REMEMBER,
296 go_file_saver_for_id ("Gnumeric_dif:dif"));
297 if (ctxt != NULL) {
298 dif_parse_sheet (ctxt);
299 if (go_io_error_occurred (io_context))
300 go_io_error_push (io_context,
301 go_error_info_new_str (_("Error while reading DIF file.")));
302 dif_input_context_destroy (ctxt);
303 } else if (!go_io_error_occurred (io_context))
304 go_io_error_unknown (io_context);
308 * Write _current_ sheet of the workbook to a DIF format file
310 void
311 dif_file_save (GOFileSaver const *fs, GOIOContext *io_context,
312 WorkbookView const *wbv, GsfOutput *out)
314 GnmLocale *locale;
315 Sheet *sheet;
316 GnmRange r;
317 gint row, col;
318 gboolean ok = TRUE;
320 sheet = wb_view_cur_sheet (wbv);
321 if (sheet == NULL) {
322 go_io_error_string (io_context, _("Cannot get default sheet."));
323 return;
326 r = sheet_get_extent (sheet, FALSE, TRUE);
328 /* Write out the standard headers */
329 gsf_output_puts (out, "TABLE\n" "0,1\n" "\"GNUMERIC\"\n");
330 gsf_output_printf (out, "VECTORS\n" "0,%d\n" "\"\"\n", r.end.col+1);
331 gsf_output_printf (out, "TUPLES\n" "0,%d\n" "\"\"\n", r.end.row+1);
332 gsf_output_puts (out, "DATA\n" "0,0\n" "\"\"\n");
334 locale = gnm_push_C_locale ();
336 /* Process all cells */
337 for (row = r.start.row; ok && row <= r.end.row; row++) {
338 gsf_output_puts (out, "-1,0\n" "BOT\n");
339 for (col = r.start.col; col <= r.end.col; col++) {
340 GnmCell *cell = sheet_cell_get (sheet, col, row);
341 if (gnm_cell_is_empty (cell)) {
342 gsf_output_puts(out, "1,0\n" "\"\"\n");
343 } else if (VALUE_IS_BOOLEAN (cell->value)) {
344 if (value_get_as_checked_bool (cell->value))
345 gsf_output_puts(out, "0,1\n" "TRUE\n");
346 else
347 gsf_output_puts(out, "0,0\n" "FALSE\n");
348 } else if (VALUE_IS_ERROR (cell->value)) {
349 if (value_error_classify (cell->value) == GNM_ERROR_NA)
350 gsf_output_puts(out, "0,0\n" "NA\n");
351 else
352 gsf_output_puts(out, "0,0\n" "ERROR\n");
353 } else if (VALUE_IS_FLOAT (cell->value))
354 gsf_output_printf (out, "0,%" GNM_FORMAT_g "\n" "V\n",
355 value_get_as_float (cell->value));
356 else {
357 gchar *str = gnm_cell_get_rendered_text (cell);
358 ok = gsf_output_printf (out,
359 "1,0\n" "\"%s\"\n",
360 str);
361 g_free (str);
366 gsf_output_puts (out, "-1,0\n" "EOD\n");
368 gnm_pop_C_locale (locale);
370 if (!ok)
371 go_io_error_string (io_context, _("Error while saving DIF file."));