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