1 /*
2 * dif.c: read/write sheets using a DIF encoding.
3 *
4 * Authors:
5 * Kevin Handy <kth@srv.net>
6 * Zbigniew Chyla <cyba@gnome.pl>
7 *
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>
14
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>
24
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>
30
31 GNM_PLUGIN_MODULE_HEADER;
32
33 #define N_INPUT_LINES_BETWEEN_UPDATES 50
34
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);
39
40 typedef struct {
41 GOIOContext *io_context;
42
43 GsfInputTextline *input;
44 gint line_no;
45 gsize line_len;
46 gchar *line;
47
48 Sheet *sheet;
49
50 GIConv converter;
51 } DifInputContext;
52
53
54 static DifInputContext *
dif_input_context_new(GOIOContext * io_context,Workbook * wb,GsfInput * input)55 dif_input_context_new (GOIOContext *io_context, Workbook *wb, GsfInput *input)
56 {
57 DifInputContext *ctxt = NULL;
58
59 ctxt = g_new (DifInputContext, 1);
60 ctxt->io_context = io_context;
61
62 ctxt->input = (GsfInputTextline *) gsf_input_textline_new (input);
63
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");
68
69 go_io_progress_message (io_context, _("Reading file..."));
70
71 return ctxt;
72 }
73
74 static void
dif_input_context_destroy(DifInputContext * ctxt)75 dif_input_context_destroy (DifInputContext *ctxt)
76 {
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);
82 }
83
84 static gboolean
dif_get_line(DifInputContext * ctxt)85 dif_get_line (DifInputContext *ctxt)
86 {
87 char *raw;
88
89 if (NULL == (raw = gsf_input_textline_ascii_gets (ctxt->input)))
90 return FALSE;
91
92 g_free (ctxt->line);
93 ctxt->line = g_convert_with_iconv (raw, -1, ctxt->converter,
94 NULL, &ctxt->line_len, NULL);
95
96 ctxt->line_no++;
97 return ctxt->line != NULL;
98 }
99
100 /*
101 * Raturns FALSE on EOF.
102 */
103 static gboolean
dif_parse_header(DifInputContext * ctxt)104 dif_parse_header (DifInputContext *ctxt)
105 {
106 while (1) {
107 gchar *topic = NULL, *num_line = NULL, *str_line = NULL;
108 size_t str_line_len;
109 int res = -1;
110
111 if (!dif_get_line (ctxt)) {
112 res = FALSE;
113 goto out;
114 }
115 topic = g_strdup (ctxt->line);
116
117 if (!dif_get_line (ctxt)) {
118 res = FALSE;
119 goto out;
120 }
121 num_line = g_strdup (ctxt->line);
122
123 if (!dif_get_line (ctxt)) {
124 res = FALSE;
125 goto out;
126 }
127 str_line = g_strdup (ctxt->line);
128 str_line_len = strlen (str_line);
129
130 if (strcmp (topic, "TABLE") == 0) {
131 gchar *name;
132
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;
138 }
139 if (name[0] != '\0') {
140 /* FIXME - rename the sheet */
141 }
142 } else if (strcmp (topic, "DATA") == 0) {
143 res = TRUE;
144 }
145
146 /*
147 * Other "standard" header entry items are:
148 * SIZE, LABEL, UNITS, TUPLES, VECTORS, COMMENT, MINORSTART,
149 * TRUELENGTH, PERIODICITY, DISPLAYUNITS
150 */
151
152 out:
153 g_free (topic);
154 g_free (num_line);
155 g_free (str_line);
156 if (res >= 0)
157 return res;
158 }
159 }
160
161 /*
162 * Raturns FALSE on EOF.
163 */
164 static gboolean
dif_parse_data(DifInputContext * ctxt)165 dif_parse_data (DifInputContext *ctxt)
166 {
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;
172
173 while (1) {
174 if (!dif_get_line (ctxt))
175 return FALSE;
176
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;
190
191 if (!dif_get_line (ctxt))
192 return FALSE;
193
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);
206 }
207
208 if (NULL != v) {
209 cell = sheet_cell_fetch (ctxt->sheet, col, row);
210 gnm_cell_set_value (cell, v);
211 }
212 col++;
213 }
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;
220 }
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;
238 }
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);
247 }
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);
255 }
256 }
257
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));
261 }
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));
265 }
266
267 return TRUE;
268 }
269
270 static void
dif_parse_sheet(DifInputContext * ctxt)271 dif_parse_sheet (DifInputContext *ctxt)
272 {
273 GnmLocale *locale = gnm_push_C_locale ();
274
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));
283 }
284
285 gnm_pop_C_locale (locale);
286 }
287
288 void
dif_file_open(GOFileOpener const * fo,GOIOContext * io_context,WorkbookView * wbv,GsfInput * input)289 dif_file_open (GOFileOpener const *fo, GOIOContext *io_context,
290 WorkbookView *wbv, GsfInput *input)
291 {
292 Workbook *wb = wb_view_get_workbook (wbv);
293 DifInputContext *ctxt = dif_input_context_new (io_context, wb, input);
294
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);
305 }
306
307 /*
308 * Write _current_ sheet of the workbook to a DIF format file
309 */
310 void
dif_file_save(GOFileSaver const * fs,GOIOContext * io_context,WorkbookView const * wbv,GsfOutput * out)311 dif_file_save (GOFileSaver const *fs, GOIOContext *io_context,
312 WorkbookView const *wbv, GsfOutput *out)
313 {
314 GnmLocale *locale;
315 Sheet *sheet;
316 GnmRange r;
317 gint row, col;
318 gboolean ok = TRUE;
319
320 sheet = wb_view_cur_sheet (wbv);
321 if (sheet == NULL) {
322 go_io_error_string (io_context, _("Cannot get default sheet."));
323 return;
324 }
325
326 r = sheet_get_extent (sheet, FALSE, TRUE);
327
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");
333
334 locale = gnm_push_C_locale ();
335
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);
362 }
363 }
364 }
365
366 gsf_output_puts (out, "-1,0\n" "EOD\n");
367
368 gnm_pop_C_locale (locale);
369
370 if (!ok)
371 go_io_error_string (io_context, _("Error while saving DIF file."));
372 }
373