Rework the spreadsheet import feature of the grapic user interface
[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",
678                          fmt->type,
679                          v,
680                          var_get_width (var),
681                          "UTF-8");
682
683           if (m)
684             {
685               char buf [FMT_STRING_LEN_MAX + 1];
686               char *cell = create_cell_ref (col, row);
687
688               msg (MW, _("Cannot convert the value in the spreadsheet cell %s to format (%s): %s"),
689                    cell, fmt_to_string (fmt, buf), m);
690               free (cell);
691             }
692           free (m);
693         }
694     }
695 }
696
697 /* Try to find out how many sheets there are in the "workbook" */
698 static int
699 get_sheet_count (struct zip_reader *zreader)
700 {
701   xmlTextReaderPtr mxtr;
702   struct zip_member *meta = NULL;
703   meta = zip_member_open (zreader, "meta.xml");
704
705   if (meta == NULL)
706     return -1;
707
708   mxtr = xmlReaderForIO (xml_reader_for_zip_member, NULL, meta, NULL, NULL, 0);
709
710   while (1 == xmlTextReaderRead (mxtr))
711     {
712       xmlChar *name = xmlTextReaderName (mxtr);
713       if (0 == xmlStrcmp (name, _xml("meta:document-statistic")))
714         {
715           xmlChar *attr = xmlTextReaderGetAttribute (mxtr, _xml ("meta:table-count"));
716
717           if (attr != NULL)
718             {
719               int s = _xmlchar_to_int (attr);
720               xmlFreeTextReader (mxtr);
721               zip_member_finish (meta);
722               xmlFree (name);
723               xmlFree (attr);
724               return s;
725             }
726           xmlFree (attr);
727         }
728       xmlFree (name);
729     }
730
731   xmlFreeTextReader (mxtr);
732   zip_member_finish (meta);
733   return -1;
734 }
735
736 static int
737 ods_get_sheet_n_sheets (struct spreadsheet *s)
738 {
739   struct ods_reader *r = (struct ods_reader *) s;
740
741   if (r->n_sheets >= 0)
742     return r->n_sheets;
743
744   r->n_sheets = get_sheet_count (r->zreader);
745
746   return r->n_sheets;
747 }
748
749
750 static void
751 ods_error_handler (void *ctx, const char *mesg,
752                    xmlParserSeverities sev UNUSED,
753                    xmlTextReaderLocatorPtr loc)
754 {
755   struct ods_reader *r = ctx;
756
757   msg (MW, _("There was a problem whilst reading the %s file `%s' (near line %d): `%s'"),
758        "ODF",
759        r->spreadsheet.file_name,
760        xmlTextReaderLocatorLineNumber (loc),
761        mesg);
762 }
763
764
765 static bool init_reader (struct ods_reader *r, bool report_errors, struct state_data *state);
766
767 static struct casereader *
768 ods_make_reader (struct spreadsheet *spreadsheet,
769                  const struct spreadsheet_read_options *opts)
770 {
771   intf ret = 0;
772   xmlChar *type = NULL;
773   unsigned long int vstart = 0;
774   casenumber n_cases = CASENUMBER_MAX;
775   int i;
776   struct var_spec *var_spec = NULL;
777   int n_var_specs = 0;
778
779   struct ods_reader *r = (struct ods_reader *) spreadsheet;
780   xmlChar *val_string = NULL;
781
782   assert (r);
783   ds_init_empty (&r->ods_errs);
784   r = (struct ods_reader *) spreadsheet_ref (SPREADSHEET_CAST (r));
785
786   if (!init_reader (r, true, &r->rsd))
787     goto error;
788
789   r->spreadsheet.used_first_case = false;
790   r->spreadsheet.first_case = NULL;
791
792   if (opts->cell_range)
793     {
794       if (! convert_cell_ref (opts->cell_range,
795                                &r->spreadsheet.start_col, &r->spreadsheet.start_row,
796                                &r->spreadsheet.stop_col, &r->spreadsheet.stop_row))
797         {
798           msg (SE, _("Invalid cell range `%s'"),
799                opts->cell_range);
800           goto error;
801         }
802     }
803   else
804     {
805       r->spreadsheet.start_col = 0;
806       r->spreadsheet.start_row = 0;
807       r->spreadsheet.stop_col = -1;
808       r->spreadsheet.stop_row = -1;
809     }
810
811   r->target_sheet_name = xmlStrdup (BAD_CAST opts->sheet_name);
812   r->target_sheet_index = opts->sheet_index;
813
814   /* Advance to the start of the cells for the target sheet */
815   while (! reading_target_sheet (r, &r->rsd)
816           || r->rsd.state != STATE_ROW || r->rsd.row <= r->spreadsheet.start_row)
817     {
818       if (1 != (ret = xmlTextReaderRead (r->rsd.xtr)))
819            break;
820
821       process_node (r, &r->rsd);
822     }
823
824   if (ret < 1)
825     {
826       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
827            spreadsheet->file_name);
828       goto error;
829     }
830
831   if (opts->read_names)
832     {
833       while (1 == xmlTextReaderRead (r->rsd.xtr))
834         {
835           process_node (r, &r->rsd);
836
837           /* If the row is finished then stop for now */
838           if (r->rsd.state == STATE_TABLE && r->rsd.row > r->spreadsheet.start_row)
839             break;
840
841           int idx = r->rsd.col - r->spreadsheet.start_col - 1;
842
843           if (idx < 0)
844             continue;
845
846           if (r->spreadsheet.stop_col != -1 && idx > r->spreadsheet.stop_col - r->spreadsheet.start_col)
847             continue;
848
849           if (r->rsd.state == STATE_CELL_CONTENT
850               &&
851               XML_READER_TYPE_TEXT  == r->rsd.node_type)
852             {
853               xmlChar *value = xmlTextReaderValue (r->rsd.xtr);
854               if (idx >= n_var_specs)
855                 {
856                   var_spec = xrealloc (var_spec, sizeof (*var_spec) * (idx + 1));
857
858                   /* xrealloc (unlike realloc) doesn't initialise its memory to 0 */
859                   memset (var_spec + n_var_specs,
860                           0,
861                           (idx - n_var_specs + 1) * sizeof (*var_spec));
862                   n_var_specs = idx + 1;
863                 }
864               for (int i = 0; i < r->rsd.col_span; ++i)
865                 {
866                   var_spec[idx - i].firstval.text = 0;
867                   var_spec[idx - i].firstval.value = 0;
868                   var_spec[idx - i].firstval.type = 0;
869                   var_spec[idx - i].name =
870                     strdup (CHAR_CAST (const char *, value));
871                 }
872
873               xmlFree (value);
874             }
875         }
876     }
877
878   /* Read in the first row of data */
879   while (1 == xmlTextReaderRead (r->rsd.xtr))
880     {
881       int idx;
882       process_node (r, &r->rsd);
883
884       if (! reading_target_sheet (r, &r->rsd))
885         break;
886
887       /* If the row is finished then stop for now */
888       if (r->rsd.state == STATE_TABLE &&
889           r->rsd.row > r->spreadsheet.start_row + (opts->read_names ? 1 : 0))
890         break;
891
892       idx = r->rsd.col - r->spreadsheet.start_col - 1;
893       if (idx < 0)
894         continue;
895
896       if (r->spreadsheet.stop_col != -1 && idx > r->spreadsheet.stop_col - r->spreadsheet.start_col)
897         continue;
898
899       if (r->rsd.state == STATE_CELL &&
900            XML_READER_TYPE_ELEMENT  == r->rsd.node_type)
901         {
902           type = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value-type"));
903           val_string = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value"));
904         }
905
906       if (r->rsd.state == STATE_CELL_CONTENT &&
907            XML_READER_TYPE_TEXT  == r->rsd.node_type)
908         {
909           if (idx >= n_var_specs)
910             {
911               var_spec = xrealloc (var_spec, sizeof (*var_spec) * (idx + 1));
912               memset (var_spec + n_var_specs,
913                       0,
914                       (idx - n_var_specs + 1) * sizeof (*var_spec));
915
916               var_spec [idx].name = NULL;
917               n_var_specs = idx + 1;
918             }
919
920           var_spec [idx].firstval.type = type;
921           var_spec [idx].firstval.text = xmlTextReaderValue (r->rsd.xtr);
922           var_spec [idx].firstval.value = val_string;
923
924           val_string = NULL;
925           type = NULL;
926         }
927     }
928
929
930   /* Create the dictionary and populate it */
931   r->spreadsheet.dict = dict_create (
932     CHAR_CAST (const char *, xmlTextReaderConstEncoding (r->rsd.xtr)));
933
934   for (i = 0; i < n_var_specs ; ++i)
935     {
936       struct fmt_spec fmt;
937       struct variable *var = NULL;
938       char *name = dict_make_unique_var_name (r->spreadsheet.dict, var_spec[i].name, &vstart);
939       int width  = xmv_to_width (&var_spec[i].firstval, opts->asw);
940       dict_create_var (r->spreadsheet.dict, name, width);
941       free (name);
942
943       var = dict_get_var (r->spreadsheet.dict, i);
944
945       if (0 == xmlStrcmp (var_spec[i].firstval.type, _xml("date")))
946         {
947           fmt.type = FMT_DATE;
948           fmt.d = 0;
949           fmt.w = 20;
950         }
951       else
952         fmt = fmt_default_for_width (width);
953
954       var_set_both_formats (var, &fmt);
955     }
956
957   if (n_var_specs ==  0)
958     {
959       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
960            spreadsheet->file_name);
961       goto error;
962     }
963
964   /* Create the first case, and cache it */
965   r->spreadsheet.proto = caseproto_ref (dict_get_proto (r->spreadsheet.dict));
966   r->spreadsheet.first_case = case_create (r->spreadsheet.proto);
967   case_set_missing (r->spreadsheet.first_case);
968
969   for (i = 0 ; i < n_var_specs; ++i)
970     {
971       const struct variable *var = dict_get_var (r->spreadsheet.dict, i);
972
973       convert_xml_to_value (r->spreadsheet.first_case, var,  &var_spec[i].firstval,
974                             r->rsd.col - n_var_specs + i,
975                             r->rsd.row - 1);
976     }
977
978   /* Read in the first row of data */
979   while (1 == xmlTextReaderRead (r->rsd.xtr))
980     {
981       process_node (r, &r->rsd);
982
983       if (r->rsd.state == STATE_ROW)
984         break;
985     }
986
987
988   for (i = 0 ; i < n_var_specs ; ++i)
989     {
990       free (var_spec[i].firstval.type);
991       free (var_spec[i].firstval.value);
992       free (var_spec[i].firstval.text);
993       free (var_spec[i].name);
994     }
995
996   free (var_spec);
997
998
999   return casereader_create_sequential
1000     (NULL,
1001      r->spreadsheet.proto,
1002      n_cases,
1003      &ods_file_casereader_class, r);
1004
1005  error:
1006
1007   for (i = 0 ; i < n_var_specs ; ++i)
1008     {
1009       free (var_spec[i].firstval.type);
1010       free (var_spec[i].firstval.value);
1011       free (var_spec[i].firstval.text);
1012       free (var_spec[i].name);
1013     }
1014
1015   free (var_spec);
1016
1017   ods_file_casereader_destroy (NULL, r);
1018
1019   return NULL;
1020 }
1021
1022
1023 /* Reads and returns one case from READER's file.  Returns a null
1024    pointer on failure. */
1025 static struct ccase *
1026 ods_file_casereader_read (struct casereader *reader UNUSED, void *r_)
1027 {
1028   struct ccase *c = NULL;
1029   struct ods_reader *r = r_;
1030
1031   xmlChar *val_string = NULL;
1032   xmlChar *type = NULL;
1033
1034   if (!r->spreadsheet.used_first_case)
1035     {
1036       r->spreadsheet.used_first_case = true;
1037       return r->spreadsheet.first_case;
1038     }
1039
1040
1041   /* Advance to the start of a row. (If there is one) */
1042   while (r->rsd.state != STATE_ROW
1043          && 1 == xmlTextReaderRead (r->rsd.xtr)
1044         )
1045     {
1046       process_node (r, &r->rsd);
1047     }
1048
1049
1050   if (! reading_target_sheet (r, &r->rsd)
1051        ||  r->rsd.state < STATE_TABLE
1052        ||  (r->spreadsheet.stop_row != -1 && r->rsd.row > r->spreadsheet.stop_row + 1)
1053 )
1054     {
1055       return NULL;
1056     }
1057
1058   c = case_create (r->spreadsheet.proto);
1059   case_set_missing (c);
1060
1061   while (1 == xmlTextReaderRead (r->rsd.xtr))
1062     {
1063       process_node (r, &r->rsd);
1064
1065       if (r->spreadsheet.stop_row != -1 && r->rsd.row > r->spreadsheet.stop_row + 1)
1066         break;
1067
1068       if (r->rsd.state == STATE_CELL &&
1069            r->rsd.node_type == XML_READER_TYPE_ELEMENT)
1070         {
1071           type = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value-type"));
1072           val_string = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value"));
1073         }
1074
1075       if (r->rsd.state == STATE_CELL_CONTENT &&
1076            r->rsd.node_type == XML_READER_TYPE_TEXT)
1077         {
1078           int col;
1079           struct xml_value *xmv = xzalloc (sizeof *xmv);
1080           xmv->text = xmlTextReaderValue (r->rsd.xtr);
1081           xmv->value = val_string;
1082           val_string = NULL;
1083           xmv->type = type;
1084           type = NULL;
1085
1086           for (col = 0; col < r->rsd.col_span; ++col)
1087             {
1088               const struct variable *var;
1089               const int idx = r->rsd.col - col - r->spreadsheet.start_col - 1;
1090               if (idx < 0)
1091                 continue;
1092               if (r->spreadsheet.stop_col != -1 && idx > r->spreadsheet.stop_col - r->spreadsheet.start_col)
1093                 break;
1094               if (idx >= dict_get_var_cnt (r->spreadsheet.dict))
1095                 break;
1096
1097               var = dict_get_var (r->spreadsheet.dict, idx);
1098               convert_xml_to_value (c, var, xmv, idx + r->spreadsheet.start_col, r->rsd.row - 1);
1099             }
1100
1101           xmlFree (xmv->text);
1102           xmlFree (xmv->value);
1103           xmlFree (xmv->type);
1104           free (xmv);
1105         }
1106       if (r->rsd.state <= STATE_TABLE)
1107         break;
1108     }
1109
1110   xmlFree (type);
1111   xmlFree (val_string);
1112
1113   return c;
1114 }
1115
1116 static bool
1117 init_reader (struct ods_reader *r, bool report_errors,
1118              struct state_data *state)
1119 {
1120   struct spreadsheet *s = SPREADSHEET_CAST (r);
1121
1122   if (state)
1123     {
1124       struct zip_member *content = zip_member_open (r->zreader, "content.xml");
1125       if (content == NULL)
1126         return NULL;
1127
1128       xmlTextReaderPtr xtr = xmlReaderForIO (xml_reader_for_zip_member, NULL, content, NULL, NULL,
1129                                              report_errors
1130                                              ? 0
1131                                              : (XML_PARSE_NOERROR | XML_PARSE_NOWARNING));
1132
1133       if (xtr == NULL)
1134         return false;
1135
1136      *state = (struct state_data) { .xtr = xtr,
1137                                     .zm = content,
1138                                     .state = STATE_INIT };
1139      if (report_errors)
1140        xmlTextReaderSetErrorHandler (xtr, ods_error_handler, r);
1141   }
1142
1143   strcpy (s->type, "ODS");
1144   s->destroy = ods_destroy;
1145   s->make_reader = ods_make_reader;
1146   s->get_sheet_name = ods_get_sheet_name;
1147   s->get_sheet_range = ods_get_sheet_range;
1148   s->get_sheet_n_sheets = ods_get_sheet_n_sheets;
1149   s->get_sheet_n_rows = ods_get_sheet_n_rows;
1150   s->get_sheet_n_columns = ods_get_sheet_n_columns;
1151   s->get_sheet_cell = ods_get_sheet_cell;
1152
1153   return true;
1154 }
1155
1156 struct spreadsheet *
1157 ods_probe (const char *filename, bool report_errors)
1158 {
1159   struct ods_reader *r = xzalloc (sizeof *r);
1160   struct zip_reader *zr;
1161
1162   ds_init_empty (&r->zip_errs);
1163
1164   zr = zip_reader_create (filename, &r->zip_errs);
1165
1166   if (zr == NULL)
1167     {
1168       if (report_errors)
1169         {
1170           msg (ME, _("Cannot open %s as a OpenDocument file: %s"),
1171                filename, ds_cstr (&r->zip_errs));
1172         }
1173       ds_destroy (&r->zip_errs);
1174       free (r);
1175       return NULL;
1176     }
1177
1178   r->zreader = zr;
1179   r->spreadsheet.ref_cnt = 1;
1180   hmap_init (&r->cache);
1181
1182   if (!init_reader (r, report_errors, NULL))
1183     goto error;
1184
1185   r->n_sheets = -1;
1186   r->n_allocated_sheets = 0;
1187   r->spreadsheet.sheets = NULL;
1188
1189   r->spreadsheet.file_name = strdup (filename);
1190   return &r->spreadsheet;
1191
1192  error:
1193   ds_destroy (&r->zip_errs);
1194   zip_reader_destroy (r->zreader);
1195   free (r);
1196   return NULL;
1197 }