Make data input and output take a fmt_settings structure.
[pspp] / src / data / ods-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2011, 2012, 2013, 2016, 2020 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "ods-reader.h"
20 #include "spreadsheet-reader.h"
21
22 #include <assert.h>
23 #include <stdbool.h>
24 #include <errno.h>
25 #include <libxml/xmlreader.h>
26 #include <zlib.h>
27
28 #include "data/case.h"
29 #include "data/casereader-provider.h"
30 #include "data/data-in.h"
31 #include "data/dictionary.h"
32 #include "data/format.h"
33 #include "data/identifier.h"
34 #include "data/value.h"
35 #include "data/variable.h"
36 #include "libpspp/assertion.h"
37 #include "libpspp/i18n.h"
38 #include "libpspp/message.h"
39 #include "libpspp/misc.h"
40 #include "libpspp/str.h"
41 #include "libpspp/zip-reader.h"
42 #include "libpspp/hmap.h"
43 #include "libpspp/hash-functions.h"
44
45
46 #include "gl/c-strtod.h"
47 #include "gl/minmax.h"
48 #include "gl/xalloc.h"
49
50 #include "gettext.h"
51 #define _(msgid) gettext (msgid)
52
53 /* Setting this to false can help with debugging and development.
54    Don't forget to set it back to true, or users will complain that
55    all but the smallest spreadsheets display VERY slowly.  */
56 static const bool use_cache = true;
57
58 static void ods_file_casereader_destroy (struct casereader *, void *);
59 static struct ccase *ods_file_casereader_read (struct casereader *, void *);
60
61 static const struct casereader_class ods_file_casereader_class =
62   {
63     ods_file_casereader_read,
64     ods_file_casereader_destroy,
65     NULL,
66     NULL,
67   };
68
69 enum reader_state
70   {
71     STATE_INIT = 0,        /* Initial state */
72     STATE_SPREADSHEET,     /* Found the start of the spreadsheet doc */
73     STATE_TABLE,           /* Found the sheet that we actually want */
74     STATE_ROW,             /* Found the start of the cell array */
75     STATE_CELL,            /* Found a cell */
76     STATE_CELL_CONTENT     /* Found a the text within a cell */
77   };
78
79 struct state_data
80 {
81   xmlTextReaderPtr xtr;
82   struct zip_member *zm;
83   int node_type;
84   enum reader_state state;
85   int row;
86   int col;
87   int current_sheet;
88   xmlChar *current_sheet_name;
89
90   int col_span;
91 };
92
93 static void
94 state_data_destroy (struct state_data *sd)
95 {
96   xmlFree (sd->current_sheet_name);
97   sd->current_sheet_name = NULL;
98
99   xmlFreeTextReader (sd->xtr);
100   sd->xtr = NULL;
101
102   zip_member_finish (sd->zm);
103   sd->zm = NULL;
104 }
105
106 struct ods_reader
107 {
108   struct spreadsheet spreadsheet;
109   struct zip_reader *zreader;
110
111   int target_sheet_index;
112   xmlChar *target_sheet_name;
113
114   int n_allocated_sheets;
115
116   /* The total number of sheets in the "workbook" */
117   int n_sheets;
118
119   /* State data for the reader */
120   struct state_data rsd;
121
122   struct string ods_errs;
123
124   struct string zip_errs;
125   struct hmap cache;
126 };
127
128 /* A value to be kept in the hash table for cache purposes.  */
129 struct cache_datum
130 {
131   struct hmap_node node;
132
133   /* The the number of the sheet.  */
134   int sheet;
135
136   /* The cell's row.  */
137   int row;
138
139   /* The cell's column.  */
140   int col;
141
142   /* The value of the cell.  */
143   char *value;
144 };
145
146 static int
147 xml_reader_for_zip_member (void *zm_, char *buffer, int len)
148 {
149   struct zip_member *zm = zm_;
150   return zip_member_read (zm, buffer, len);
151 }
152
153 static void
154 ods_destroy (struct spreadsheet *s)
155 {
156   struct ods_reader *r = (struct ods_reader *) s;
157
158   int i;
159
160   for (i = 0; i < r->n_allocated_sheets; ++i)
161     {
162       xmlFree (r->spreadsheet.sheets[i].name);
163     }
164
165   dict_unref (r->spreadsheet.dict);
166
167   zip_reader_destroy (r->zreader);
168   free (r->spreadsheet.sheets);
169   free (s->file_name);
170
171   struct cache_datum *cell;
172   struct cache_datum *next;
173   HMAP_FOR_EACH_SAFE (cell, next, struct cache_datum, node, &r->cache)
174     {
175       free (cell->value);
176       free (cell);
177     }
178
179   hmap_destroy (&r->cache);
180
181   free (r);
182 }
183
184 static bool
185 reading_target_sheet (const struct ods_reader *r, const struct state_data *sd)
186 {
187   if (r->target_sheet_name != NULL)
188     {
189       if (0 == xmlStrcmp (r->target_sheet_name, sd->current_sheet_name))
190         return true;
191     }
192
193   if (r->target_sheet_index == sd->current_sheet + 1)
194     return true;
195
196   return false;
197 }
198
199
200 static void process_node (struct ods_reader *or, struct state_data *r);
201
202
203 /* Initialise SD using R */
204 static bool
205 state_data_init (const struct ods_reader *r, struct state_data *sd)
206 {
207   memset (sd, 0, sizeof (*sd));
208
209   sd->zm = zip_member_open (r->zreader, "content.xml");
210
211   if (sd->zm == NULL)
212     return false;
213
214   sd->xtr =
215     xmlReaderForIO (xml_reader_for_zip_member, NULL, sd->zm, NULL, NULL,
216                     0);
217
218   if (sd->xtr == NULL)
219     return NULL;
220
221   sd->state = STATE_INIT;
222   return true;
223 }
224
225
226 static const char *
227 ods_get_sheet_name (struct spreadsheet *s, int n)
228 {
229   struct ods_reader *r = (struct ods_reader *) s;
230   struct state_data sd;
231   state_data_init (r, &sd);
232
233   while ((r->n_allocated_sheets <= n)
234          || sd.state != STATE_SPREADSHEET)
235     {
236       int ret = xmlTextReaderRead (sd.xtr);
237       if (ret != 1)
238         break;
239
240       process_node (r, &sd);
241     }
242   state_data_destroy (&sd);
243
244   return r->spreadsheet.sheets[n].name;
245 }
246
247 static char *
248 ods_get_sheet_range (struct spreadsheet *s, int n)
249 {
250   struct ods_reader *r = (struct ods_reader *) s;
251   struct state_data sd;
252   state_data_init (r, &sd);
253
254   while ((r->n_allocated_sheets <= n)
255           || (r->spreadsheet.sheets[n].last_row == -1)
256          || sd.state != STATE_SPREADSHEET)
257     {
258       int ret = xmlTextReaderRead (sd.xtr);
259       if (ret != 1)
260         break;
261
262       process_node (r, &sd);
263     }
264   state_data_destroy (&sd);
265
266   return create_cell_range (
267                           r->spreadsheet.sheets[n].first_col,
268                           r->spreadsheet.sheets[n].first_row,
269                           r->spreadsheet.sheets[n].last_col,
270                           r->spreadsheet.sheets[n].last_row);
271 }
272
273 static unsigned int
274 ods_get_sheet_n_rows (struct spreadsheet *s, int n)
275 {
276   struct ods_reader *r = (struct ods_reader *) s;
277   struct state_data sd;
278
279   if (r->n_allocated_sheets > n && r->spreadsheet.sheets[n].last_row != -1)
280     {
281       return r->spreadsheet.sheets[n].last_row + 1;
282     }
283
284   state_data_init (r, &sd);
285
286   while (1 == xmlTextReaderRead (sd.xtr))
287     {
288       process_node (r, &sd);
289     }
290
291   state_data_destroy (&sd);
292
293   return r->spreadsheet.sheets[n].last_row + 1;
294 }
295
296 static unsigned int
297 ods_get_sheet_n_columns (struct spreadsheet *s, int n)
298 {
299   struct ods_reader *r = (struct ods_reader *) s;
300   struct state_data sd;
301
302   if (r->n_allocated_sheets > n && r->spreadsheet.sheets[n].last_col != -1)
303     return r->spreadsheet.sheets[n].last_col + 1;
304
305   state_data_init (r, &sd);
306
307   while (1 == xmlTextReaderRead (sd.xtr))
308     {
309       process_node (r, &sd);
310     }
311
312   state_data_destroy (&sd);
313
314   return r->spreadsheet.sheets[n].last_col + 1;
315 }
316
317 static char *
318 ods_get_sheet_cell (struct spreadsheet *s, int n, int row, int column)
319 {
320   struct ods_reader *r = (struct ods_reader *) s;
321   struct state_data sd;
322
323   /* See if this cell is in the cache.  If it is, then use it.  */
324   if (use_cache)
325   {
326     struct cache_datum *lookup = NULL;
327     unsigned int hash = hash_int (n, 0);
328     hash = hash_int (row, hash);
329     hash = hash_int (column, hash);
330
331     HMAP_FOR_EACH_WITH_HASH (lookup, struct cache_datum, node, hash,
332                              &r->cache)
333       {
334         if (lookup->row == row && lookup->col == column
335             && lookup->sheet == n)
336           {
337             break;
338           }
339       }
340     if (lookup)
341       {
342         return lookup->value ? strdup (lookup->value) : NULL;
343       }
344   }
345
346   state_data_init (r, &sd);
347
348   char *cell_content = NULL;
349
350   int prev_col = 0;
351   int prev_row = 0;
352   while (1 == xmlTextReaderRead (sd.xtr))
353     {
354       process_node (r, &sd);
355       if (sd.row > prev_row)
356         prev_col = 0;
357
358       if (sd.state == STATE_CELL_CONTENT
359           && sd.current_sheet == n
360           && sd.node_type == XML_READER_TYPE_TEXT)
361         {
362           /*  When cell contents are encountered, copy and save it, discarding
363               any older content.  */
364           free (cell_content);
365           cell_content = CHAR_CAST (char *, xmlTextReaderValue (sd.xtr));
366         }
367       if (sd.state == STATE_ROW
368                && sd.current_sheet == n
369                && sd.node_type == XML_READER_TYPE_ELEMENT)
370         {
371           /* At the start of a row, free the cell contents and set it to NULL.  */
372           free (cell_content);
373           cell_content = NULL;
374         }
375       if (sd.state == STATE_ROW
376           && sd.current_sheet == n
377           &&
378                (sd.node_type == XML_READER_TYPE_END_ELEMENT
379                 ||
380                 xmlTextReaderIsEmptyElement (sd.xtr)))
381         {
382           if (use_cache)
383             {
384               for (int c = prev_col; c < sd.col; ++c)
385                 {
386                   /* See if this cell has already been cached ... */
387                   unsigned int hash = hash_int (sd.current_sheet, 0);
388                   hash = hash_int (sd.row - 1, hash);
389                   hash = hash_int (c, hash);
390                   struct cache_datum *probe = NULL;
391                   struct cache_datum *next;
392                   HMAP_FOR_EACH_WITH_HASH_SAFE (probe, next, struct cache_datum, node, hash,
393                                                 &r->cache)
394                     {
395                       if (probe->row == sd.row - 1 && probe->col == c
396                           && probe->sheet == sd.current_sheet)
397                         break;
398                       probe = NULL;
399                     }
400                   /* If not, then cache it.  */
401                   if (!probe)
402                     {
403                       struct cache_datum *cell_data = XMALLOC (struct cache_datum);
404                       cell_data->row = sd.row - 1;
405                       cell_data->col = c;
406                       cell_data->sheet = sd.current_sheet;
407                       cell_data->value = cell_content ? strdup (cell_content) : NULL;
408
409                       hmap_insert (&r->cache, &cell_data->node, hash);
410                     }
411                 }
412             }
413
414           if (sd.row == row + 1 && sd.col >= column + 1)
415             {
416               break;
417             }
418
419           prev_col = sd.col;
420           prev_row = sd.row;
421         }
422     }
423
424   state_data_destroy (&sd);
425   return cell_content;
426 }
427
428 static void
429 ods_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
430 {
431   struct ods_reader *r = r_;
432   if (r == NULL)
433     return ;
434
435   state_data_destroy (&r->rsd);
436
437   if (! ds_is_empty (&r->ods_errs))
438     msg (ME, "%s", ds_cstr (&r->ods_errs));
439
440   ds_destroy (&r->ods_errs);
441
442   if (r->spreadsheet.first_case && ! r->spreadsheet.used_first_case)
443     case_unref (r->spreadsheet.first_case);
444
445   caseproto_unref (r->spreadsheet.proto);
446   r->spreadsheet.proto = NULL;
447
448   xmlFree (r->target_sheet_name);
449   r->target_sheet_name = NULL;
450
451   spreadsheet_unref (&r->spreadsheet);
452 }
453
454 static void
455 process_node (struct ods_reader *or, struct state_data *r)
456 {
457   xmlChar *name = xmlTextReaderName (r->xtr);
458   if (name == NULL)
459     name = xmlStrdup (_xml ("--"));
460
461
462   r->node_type = xmlTextReaderNodeType (r->xtr);
463
464   switch (r->state)
465     {
466     case STATE_INIT:
467       if (0 == xmlStrcasecmp (name, _xml("office:spreadsheet")) &&
468           XML_READER_TYPE_ELEMENT  == r->node_type)
469         {
470           r->state = STATE_SPREADSHEET;
471           r->current_sheet = -1;
472           r->current_sheet_name = NULL;
473         }
474       break;
475     case STATE_SPREADSHEET:
476       if (0 == xmlStrcasecmp (name, _xml("table:table"))
477           &&
478           (XML_READER_TYPE_ELEMENT == r->node_type))
479         {
480           xmlFree (r->current_sheet_name);
481           r->current_sheet_name = xmlTextReaderGetAttribute (r->xtr, _xml ("table:name"));
482
483           ++r->current_sheet;
484
485           if (r->current_sheet >= or->n_allocated_sheets)
486             {
487               assert (r->current_sheet == or->n_allocated_sheets);
488               or->spreadsheet.sheets = xrealloc (or->spreadsheet.sheets, sizeof (*or->spreadsheet.sheets) * ++or->n_allocated_sheets);
489               or->spreadsheet.sheets[or->n_allocated_sheets - 1].first_col = -1;
490               or->spreadsheet.sheets[or->n_allocated_sheets - 1].last_col = -1;
491               or->spreadsheet.sheets[or->n_allocated_sheets - 1].first_row = -1;
492               or->spreadsheet.sheets[or->n_allocated_sheets - 1].last_row = -1;
493               or->spreadsheet.sheets[or->n_allocated_sheets - 1].name = CHAR_CAST (char *, xmlStrdup (r->current_sheet_name));
494             }
495           if (or->n_allocated_sheets > or->n_sheets)
496             or->n_sheets = or->n_allocated_sheets;
497
498           r->col = 0;
499           r->row = 0;
500
501           r->state = STATE_TABLE;
502         }
503       else if (0 == xmlStrcasecmp (name, _xml("office:spreadsheet")) &&
504                XML_READER_TYPE_ELEMENT  == r->node_type)
505         {
506           r->state = STATE_INIT;
507         }
508       break;
509     case STATE_TABLE:
510       if (0 == xmlStrcasecmp (name, _xml("table:table-row")) &&
511           (XML_READER_TYPE_ELEMENT  == r->node_type))
512         {
513           xmlChar *value =
514             xmlTextReaderGetAttribute (r->xtr,
515                                        _xml ("table:number-rows-repeated"));
516
517           int row_span = value ? _xmlchar_to_int (value) : 1;
518
519           r->row += row_span;
520           r->col = 0;
521
522           if (! xmlTextReaderIsEmptyElement (r->xtr))
523             r->state = STATE_ROW;
524
525           xmlFree (value);
526         }
527       else if (0 == xmlStrcasecmp (name, _xml("table:table")) &&
528                (XML_READER_TYPE_END_ELEMENT  == r->node_type))
529         {
530           r->state = STATE_SPREADSHEET;
531         }
532       break;
533     case STATE_ROW:
534       if ((0 == xmlStrcasecmp (name, _xml ("table:table-cell")))
535            &&
536            (XML_READER_TYPE_ELEMENT  == r->node_type))
537         {
538           xmlChar *value =
539             xmlTextReaderGetAttribute (r->xtr,
540                                        _xml ("table:number-columns-repeated"));
541
542           r->col_span = value ? _xmlchar_to_int (value) : 1;
543           r->col += r->col_span;
544
545           if (! xmlTextReaderIsEmptyElement (r->xtr))
546             r->state = STATE_CELL;
547
548           xmlFree (value);
549         }
550       else if ((0 == xmlStrcasecmp (name, _xml ("table:table-row")))
551                 &&
552                 (XML_READER_TYPE_END_ELEMENT  == r->node_type))
553         {
554           r->state = STATE_TABLE;
555         }
556       break;
557     case STATE_CELL:
558       if ((0 == xmlStrcasecmp (name, _xml("text:p")))
559             &&
560            (XML_READER_TYPE_ELEMENT  == r->node_type))
561         {
562           if (! xmlTextReaderIsEmptyElement (r->xtr))
563             r->state = STATE_CELL_CONTENT;
564         }
565       else if
566         ((0 == xmlStrcasecmp (name, _xml("table:table-cell")))
567           &&
568           (XML_READER_TYPE_END_ELEMENT  == r->node_type)
569         )
570         {
571           r->state = STATE_ROW;
572         }
573       break;
574     case STATE_CELL_CONTENT:
575       assert (r->current_sheet >= 0);
576       assert (r->current_sheet < or->n_allocated_sheets);
577
578       if (or->spreadsheet.sheets[r->current_sheet].first_row == -1)
579         or->spreadsheet.sheets[r->current_sheet].first_row = r->row - 1;
580
581       if (
582           (or->spreadsheet.sheets[r->current_sheet].first_col == -1)
583           ||
584           (or->spreadsheet.sheets[r->current_sheet].first_col >= r->col - 1)
585         )
586         or->spreadsheet.sheets[r->current_sheet].first_col = r->col - 1;
587
588       if (or->spreadsheet.sheets[r->current_sheet].last_row < r->row - 1)
589         or->spreadsheet.sheets[r->current_sheet].last_row = r->row - 1;
590
591       if (or->spreadsheet.sheets[r->current_sheet].last_col < r->col - 1)
592         or->spreadsheet.sheets[r->current_sheet].last_col = r->col - 1;
593
594       if (XML_READER_TYPE_END_ELEMENT  == r->node_type)
595         r->state = STATE_CELL;
596       break;
597     default:
598       NOT_REACHED ();
599       break;
600     };
601
602   xmlFree (name);
603 }
604
605 /*
606    A struct containing the parameters of a cell's value
607    parsed from the xml
608 */
609 struct xml_value
610 {
611   xmlChar *type;
612   xmlChar *value;
613   xmlChar *text;
614 };
615
616 struct var_spec
617 {
618   char *name;
619   struct xml_value firstval;
620 };
621
622
623 /* Determine the width that a xmv should probably have */
624 static int
625 xmv_to_width (const struct xml_value *xmv, int fallback)
626 {
627   int width = SPREADSHEET_DEFAULT_WIDTH;
628
629   /* Non-strings always have zero width */
630   if (xmv->type != NULL && 0 != xmlStrcmp (xmv->type, _xml("string")))
631     return 0;
632
633   if (fallback != -1)
634     return fallback;
635
636   if (xmv->value)
637     width = ROUND_UP (xmlStrlen (xmv->value),
638                       SPREADSHEET_DEFAULT_WIDTH);
639   else if (xmv->text)
640     width = ROUND_UP (xmlStrlen (xmv->text),
641                       SPREADSHEET_DEFAULT_WIDTH);
642
643   return width;
644 }
645
646 /*
647    Sets the VAR of case C, to the value corresponding to the xml data
648  */
649 static void
650 convert_xml_to_value (struct ccase *c, const struct variable *var,
651                       const struct xml_value *xmv, int col, int row)
652 {
653   union value *v = case_data_rw (c, var);
654
655   if (xmv->value == NULL && xmv->text == NULL)
656     value_set_missing (v, var_get_width (var));
657   else if (var_is_alpha (var))
658     /* Use the text field, because it seems that there is no
659        value field for strings */
660     value_copy_str_rpad (v, var_get_width (var), xmv->text, ' ');
661   else
662     {
663       const struct fmt_spec *fmt = var_get_write_format (var);
664       enum fmt_category fc  = fmt_get_category (fmt->type);
665
666       assert (fc != FMT_CAT_STRING);
667
668       if (0 == xmlStrcmp (xmv->type, _xml("float")))
669         {
670           v->f = c_strtod (CHAR_CAST (const char *, xmv->value), NULL);
671         }
672       else
673         {
674           const char *text = xmv->value ?
675             CHAR_CAST (const char *, xmv->value) : CHAR_CAST (const char *, xmv->text);
676
677           char *m = data_in (ss_cstr (text), "UTF-8", fmt->type,
678                              settings_get_fmt_settings (), v,
679                              var_get_width (var), "UTF-8");
680
681           if (m)
682             {
683               char buf [FMT_STRING_LEN_MAX + 1];
684               char *cell = create_cell_ref (col, row);
685
686               msg (MW, _("Cannot convert the value in the spreadsheet cell %s to format (%s): %s"),
687                    cell, fmt_to_string (fmt, buf), m);
688               free (cell);
689             }
690           free (m);
691         }
692     }
693 }
694
695 /* Try to find out how many sheets there are in the "workbook" */
696 static int
697 get_sheet_count (struct zip_reader *zreader)
698 {
699   xmlTextReaderPtr mxtr;
700   struct zip_member *meta = NULL;
701   meta = zip_member_open (zreader, "meta.xml");
702
703   if (meta == NULL)
704     return -1;
705
706   mxtr = xmlReaderForIO (xml_reader_for_zip_member, NULL, meta, NULL, NULL, 0);
707
708   while (1 == xmlTextReaderRead (mxtr))
709     {
710       xmlChar *name = xmlTextReaderName (mxtr);
711       if (0 == xmlStrcmp (name, _xml("meta:document-statistic")))
712         {
713           xmlChar *attr = xmlTextReaderGetAttribute (mxtr, _xml ("meta:table-count"));
714
715           if (attr != NULL)
716             {
717               int s = _xmlchar_to_int (attr);
718               xmlFreeTextReader (mxtr);
719               zip_member_finish (meta);
720               xmlFree (name);
721               xmlFree (attr);
722               return s;
723             }
724           xmlFree (attr);
725         }
726       xmlFree (name);
727     }
728
729   xmlFreeTextReader (mxtr);
730   zip_member_finish (meta);
731   return -1;
732 }
733
734 static int
735 ods_get_sheet_n_sheets (struct spreadsheet *s)
736 {
737   struct ods_reader *r = (struct ods_reader *) s;
738
739   if (r->n_sheets >= 0)
740     return r->n_sheets;
741
742   r->n_sheets = get_sheet_count (r->zreader);
743
744   return r->n_sheets;
745 }
746
747
748 static void
749 ods_error_handler (void *ctx, const char *mesg,
750                    xmlParserSeverities sev UNUSED,
751                    xmlTextReaderLocatorPtr loc)
752 {
753   struct ods_reader *r = ctx;
754
755   msg (MW, _("There was a problem whilst reading the %s file `%s' (near line %d): `%s'"),
756        "ODF",
757        r->spreadsheet.file_name,
758        xmlTextReaderLocatorLineNumber (loc),
759        mesg);
760 }
761
762
763 static bool init_reader (struct ods_reader *r, bool report_errors, struct state_data *state);
764
765 static struct casereader *
766 ods_make_reader (struct spreadsheet *spreadsheet,
767                  const struct spreadsheet_read_options *opts)
768 {
769   intf ret = 0;
770   xmlChar *type = NULL;
771   unsigned long int vstart = 0;
772   casenumber n_cases = CASENUMBER_MAX;
773   int i;
774   struct var_spec *var_spec = NULL;
775   int n_var_specs = 0;
776
777   struct ods_reader *r = (struct ods_reader *) spreadsheet;
778   xmlChar *val_string = NULL;
779
780   assert (r);
781   ds_init_empty (&r->ods_errs);
782   r = (struct ods_reader *) spreadsheet_ref (SPREADSHEET_CAST (r));
783
784   if (!init_reader (r, true, &r->rsd))
785     goto error;
786
787   r->spreadsheet.used_first_case = false;
788   r->spreadsheet.first_case = NULL;
789
790   if (opts->cell_range)
791     {
792       if (! convert_cell_ref (opts->cell_range,
793                                &r->spreadsheet.start_col, &r->spreadsheet.start_row,
794                                &r->spreadsheet.stop_col, &r->spreadsheet.stop_row))
795         {
796           msg (SE, _("Invalid cell range `%s'"),
797                opts->cell_range);
798           goto error;
799         }
800     }
801   else
802     {
803       r->spreadsheet.start_col = 0;
804       r->spreadsheet.start_row = 0;
805       r->spreadsheet.stop_col = -1;
806       r->spreadsheet.stop_row = -1;
807     }
808
809   r->target_sheet_name = xmlStrdup (BAD_CAST opts->sheet_name);
810   r->target_sheet_index = opts->sheet_index;
811
812   /* Advance to the start of the cells for the target sheet */
813   while (! reading_target_sheet (r, &r->rsd)
814           || r->rsd.state != STATE_ROW || r->rsd.row <= r->spreadsheet.start_row)
815     {
816       if (1 != (ret = xmlTextReaderRead (r->rsd.xtr)))
817            break;
818
819       process_node (r, &r->rsd);
820     }
821
822   if (ret < 1)
823     {
824       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
825            spreadsheet->file_name);
826       goto error;
827     }
828
829   if (opts->read_names)
830     {
831       while (1 == xmlTextReaderRead (r->rsd.xtr))
832         {
833           process_node (r, &r->rsd);
834
835           /* If the row is finished then stop for now */
836           if (r->rsd.state == STATE_TABLE && r->rsd.row > r->spreadsheet.start_row)
837             break;
838
839           int idx = r->rsd.col - r->spreadsheet.start_col - 1;
840
841           if (idx < 0)
842             continue;
843
844           if (r->spreadsheet.stop_col != -1 && idx > r->spreadsheet.stop_col - r->spreadsheet.start_col)
845             continue;
846
847           if (r->rsd.state == STATE_CELL_CONTENT
848               &&
849               XML_READER_TYPE_TEXT  == r->rsd.node_type)
850             {
851               xmlChar *value = xmlTextReaderValue (r->rsd.xtr);
852               if (idx >= n_var_specs)
853                 {
854                   var_spec = xrealloc (var_spec, sizeof (*var_spec) * (idx + 1));
855
856                   /* xrealloc (unlike realloc) doesn't initialise its memory to 0 */
857                   memset (var_spec + n_var_specs,
858                           0,
859                           (idx - n_var_specs + 1) * sizeof (*var_spec));
860                   n_var_specs = idx + 1;
861                 }
862               for (int i = 0; i < r->rsd.col_span; ++i)
863                 {
864                   var_spec[idx - i].firstval.text = 0;
865                   var_spec[idx - i].firstval.value = 0;
866                   var_spec[idx - i].firstval.type = 0;
867                   var_spec[idx - i].name =
868                     strdup (CHAR_CAST (const char *, value));
869                 }
870
871               xmlFree (value);
872             }
873         }
874     }
875
876   /* Read in the first row of data */
877   while (1 == xmlTextReaderRead (r->rsd.xtr))
878     {
879       int idx;
880       process_node (r, &r->rsd);
881
882       if (! reading_target_sheet (r, &r->rsd))
883         break;
884
885       /* If the row is finished then stop for now */
886       if (r->rsd.state == STATE_TABLE &&
887           r->rsd.row > r->spreadsheet.start_row + (opts->read_names ? 1 : 0))
888         break;
889
890       idx = r->rsd.col - r->spreadsheet.start_col - 1;
891       if (idx < 0)
892         continue;
893
894       if (r->spreadsheet.stop_col != -1 && idx > r->spreadsheet.stop_col - r->spreadsheet.start_col)
895         continue;
896
897       if (r->rsd.state == STATE_CELL &&
898            XML_READER_TYPE_ELEMENT  == r->rsd.node_type)
899         {
900           type = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value-type"));
901           val_string = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value"));
902         }
903
904       if (r->rsd.state == STATE_CELL_CONTENT &&
905            XML_READER_TYPE_TEXT  == r->rsd.node_type)
906         {
907           if (idx >= n_var_specs)
908             {
909               var_spec = xrealloc (var_spec, sizeof (*var_spec) * (idx + 1));
910               memset (var_spec + n_var_specs,
911                       0,
912                       (idx - n_var_specs + 1) * sizeof (*var_spec));
913
914               var_spec [idx].name = NULL;
915               n_var_specs = idx + 1;
916             }
917
918           var_spec [idx].firstval.type = type;
919           var_spec [idx].firstval.text = xmlTextReaderValue (r->rsd.xtr);
920           var_spec [idx].firstval.value = val_string;
921
922           val_string = NULL;
923           type = NULL;
924         }
925     }
926
927
928   /* Create the dictionary and populate it */
929   r->spreadsheet.dict = dict_create (
930     CHAR_CAST (const char *, xmlTextReaderConstEncoding (r->rsd.xtr)));
931
932   for (i = 0; i < n_var_specs ; ++i)
933     {
934       struct fmt_spec fmt;
935       struct variable *var = NULL;
936       char *name = dict_make_unique_var_name (r->spreadsheet.dict, var_spec[i].name, &vstart);
937       int width  = xmv_to_width (&var_spec[i].firstval, opts->asw);
938       dict_create_var (r->spreadsheet.dict, name, width);
939       free (name);
940
941       var = dict_get_var (r->spreadsheet.dict, i);
942
943       if (0 == xmlStrcmp (var_spec[i].firstval.type, _xml("date")))
944         {
945           fmt.type = FMT_DATE;
946           fmt.d = 0;
947           fmt.w = 20;
948         }
949       else
950         fmt = fmt_default_for_width (width);
951
952       var_set_both_formats (var, &fmt);
953     }
954
955   if (n_var_specs ==  0)
956     {
957       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
958            spreadsheet->file_name);
959       goto error;
960     }
961
962   /* Create the first case, and cache it */
963   r->spreadsheet.proto = caseproto_ref (dict_get_proto (r->spreadsheet.dict));
964   r->spreadsheet.first_case = case_create (r->spreadsheet.proto);
965   case_set_missing (r->spreadsheet.first_case);
966
967   for (i = 0 ; i < n_var_specs; ++i)
968     {
969       const struct variable *var = dict_get_var (r->spreadsheet.dict, i);
970
971       convert_xml_to_value (r->spreadsheet.first_case, var,  &var_spec[i].firstval,
972                             r->rsd.col - n_var_specs + i,
973                             r->rsd.row - 1);
974     }
975
976   /* Read in the first row of data */
977   while (1 == xmlTextReaderRead (r->rsd.xtr))
978     {
979       process_node (r, &r->rsd);
980
981       if (r->rsd.state == STATE_ROW)
982         break;
983     }
984
985
986   for (i = 0 ; i < n_var_specs ; ++i)
987     {
988       free (var_spec[i].firstval.type);
989       free (var_spec[i].firstval.value);
990       free (var_spec[i].firstval.text);
991       free (var_spec[i].name);
992     }
993
994   free (var_spec);
995
996
997   return casereader_create_sequential
998     (NULL,
999      r->spreadsheet.proto,
1000      n_cases,
1001      &ods_file_casereader_class, r);
1002
1003  error:
1004
1005   for (i = 0 ; i < n_var_specs ; ++i)
1006     {
1007       free (var_spec[i].firstval.type);
1008       free (var_spec[i].firstval.value);
1009       free (var_spec[i].firstval.text);
1010       free (var_spec[i].name);
1011     }
1012
1013   free (var_spec);
1014
1015   ods_file_casereader_destroy (NULL, r);
1016
1017   return NULL;
1018 }
1019
1020
1021 /* Reads and returns one case from READER's file.  Returns a null
1022    pointer on failure. */
1023 static struct ccase *
1024 ods_file_casereader_read (struct casereader *reader UNUSED, void *r_)
1025 {
1026   struct ccase *c = NULL;
1027   struct ods_reader *r = r_;
1028
1029   xmlChar *val_string = NULL;
1030   xmlChar *type = NULL;
1031
1032   if (!r->spreadsheet.used_first_case)
1033     {
1034       r->spreadsheet.used_first_case = true;
1035       return r->spreadsheet.first_case;
1036     }
1037
1038
1039   /* Advance to the start of a row. (If there is one) */
1040   while (r->rsd.state != STATE_ROW
1041          && 1 == xmlTextReaderRead (r->rsd.xtr)
1042         )
1043     {
1044       process_node (r, &r->rsd);
1045     }
1046
1047
1048   if (! reading_target_sheet (r, &r->rsd)
1049        ||  r->rsd.state < STATE_TABLE
1050        ||  (r->spreadsheet.stop_row != -1 && r->rsd.row > r->spreadsheet.stop_row + 1)
1051 )
1052     {
1053       return NULL;
1054     }
1055
1056   c = case_create (r->spreadsheet.proto);
1057   case_set_missing (c);
1058
1059   while (1 == xmlTextReaderRead (r->rsd.xtr))
1060     {
1061       process_node (r, &r->rsd);
1062
1063       if (r->spreadsheet.stop_row != -1 && r->rsd.row > r->spreadsheet.stop_row + 1)
1064         break;
1065
1066       if (r->rsd.state == STATE_CELL &&
1067            r->rsd.node_type == XML_READER_TYPE_ELEMENT)
1068         {
1069           type = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value-type"));
1070           val_string = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value"));
1071         }
1072
1073       if (r->rsd.state == STATE_CELL_CONTENT &&
1074            r->rsd.node_type == XML_READER_TYPE_TEXT)
1075         {
1076           int col;
1077           struct xml_value *xmv = xzalloc (sizeof *xmv);
1078           xmv->text = xmlTextReaderValue (r->rsd.xtr);
1079           xmv->value = val_string;
1080           val_string = NULL;
1081           xmv->type = type;
1082           type = NULL;
1083
1084           for (col = 0; col < r->rsd.col_span; ++col)
1085             {
1086               const struct variable *var;
1087               const int idx = r->rsd.col - col - r->spreadsheet.start_col - 1;
1088               if (idx < 0)
1089                 continue;
1090               if (r->spreadsheet.stop_col != -1 && idx > r->spreadsheet.stop_col - r->spreadsheet.start_col)
1091                 break;
1092               if (idx >= dict_get_var_cnt (r->spreadsheet.dict))
1093                 break;
1094
1095               var = dict_get_var (r->spreadsheet.dict, idx);
1096               convert_xml_to_value (c, var, xmv, idx + r->spreadsheet.start_col, r->rsd.row - 1);
1097             }
1098
1099           xmlFree (xmv->text);
1100           xmlFree (xmv->value);
1101           xmlFree (xmv->type);
1102           free (xmv);
1103         }
1104       if (r->rsd.state <= STATE_TABLE)
1105         break;
1106     }
1107
1108   xmlFree (type);
1109   xmlFree (val_string);
1110
1111   return c;
1112 }
1113
1114 static bool
1115 init_reader (struct ods_reader *r, bool report_errors,
1116              struct state_data *state)
1117 {
1118   struct spreadsheet *s = SPREADSHEET_CAST (r);
1119
1120   if (state)
1121     {
1122       struct zip_member *content = zip_member_open (r->zreader, "content.xml");
1123       if (content == NULL)
1124         return NULL;
1125
1126       xmlTextReaderPtr xtr = xmlReaderForIO (xml_reader_for_zip_member, NULL, content, NULL, NULL,
1127                                              report_errors
1128                                              ? 0
1129                                              : (XML_PARSE_NOERROR | XML_PARSE_NOWARNING));
1130
1131       if (xtr == NULL)
1132         return false;
1133
1134      *state = (struct state_data) { .xtr = xtr,
1135                                     .zm = content,
1136                                     .state = STATE_INIT };
1137      if (report_errors)
1138        xmlTextReaderSetErrorHandler (xtr, ods_error_handler, r);
1139   }
1140
1141   strcpy (s->type, "ODS");
1142   s->destroy = ods_destroy;
1143   s->make_reader = ods_make_reader;
1144   s->get_sheet_name = ods_get_sheet_name;
1145   s->get_sheet_range = ods_get_sheet_range;
1146   s->get_sheet_n_sheets = ods_get_sheet_n_sheets;
1147   s->get_sheet_n_rows = ods_get_sheet_n_rows;
1148   s->get_sheet_n_columns = ods_get_sheet_n_columns;
1149   s->get_sheet_cell = ods_get_sheet_cell;
1150
1151   return true;
1152 }
1153
1154 struct spreadsheet *
1155 ods_probe (const char *filename, bool report_errors)
1156 {
1157   struct ods_reader *r = xzalloc (sizeof *r);
1158   struct zip_reader *zr;
1159
1160   ds_init_empty (&r->zip_errs);
1161
1162   zr = zip_reader_create (filename, &r->zip_errs);
1163
1164   if (zr == NULL)
1165     {
1166       if (report_errors)
1167         {
1168           msg (ME, _("Cannot open %s as a OpenDocument file: %s"),
1169                filename, ds_cstr (&r->zip_errs));
1170         }
1171       ds_destroy (&r->zip_errs);
1172       free (r);
1173       return NULL;
1174     }
1175
1176   r->zreader = zr;
1177   r->spreadsheet.ref_cnt = 1;
1178   hmap_init (&r->cache);
1179
1180   if (!init_reader (r, report_errors, NULL))
1181     goto error;
1182
1183   r->n_sheets = -1;
1184   r->n_allocated_sheets = 0;
1185   r->spreadsheet.sheets = NULL;
1186
1187   r->spreadsheet.file_name = strdup (filename);
1188   return &r->spreadsheet;
1189
1190  error:
1191   ds_destroy (&r->zip_errs);
1192   zip_reader_destroy (r->zreader);
1193   free (r);
1194   return NULL;
1195 }