better tests
[pspp] / src / data / ods-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2011, 2012, 2013, 2016, 2020, 2021 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 + r->rsd.col_span));
913               memset (var_spec + n_var_specs,
914                       0,
915                       (idx + r->rsd.col_span - n_var_specs) * sizeof (*var_spec));
916
917               var_spec [idx].name = NULL;
918               n_var_specs = idx + 1;
919             }
920
921           for (int x = 0; x < r->rsd.col_span; ++x)
922           {
923             var_spec [idx - x].firstval.type = xmlStrdup (type);
924             var_spec [idx - x].firstval.text = xmlTextReaderValue (r->rsd.xtr);
925             var_spec [idx - x].firstval.value = xmlStrdup (val_string);
926           }
927
928           free (val_string);
929           free (type);
930         }
931     }
932
933   /* Create the dictionary and populate it */
934   r->spreadsheet.dict = dict_create (
935     CHAR_CAST (const char *, xmlTextReaderConstEncoding (r->rsd.xtr)));
936
937   for (i = 0; i < n_var_specs ; ++i)
938     {
939       struct fmt_spec fmt;
940       struct variable *var = NULL;
941       char *name = dict_make_unique_var_name (r->spreadsheet.dict, var_spec[i].name, &vstart);
942       int width  = xmv_to_width (&var_spec[i].firstval, opts->asw);
943       dict_create_var (r->spreadsheet.dict, name, width);
944       free (name);
945
946       var = dict_get_var (r->spreadsheet.dict, i);
947
948       if (0 == xmlStrcmp (var_spec[i].firstval.type, _xml("date")))
949         {
950           fmt.type = FMT_DATE;
951           fmt.d = 0;
952           fmt.w = 20;
953         }
954       else
955         fmt = fmt_default_for_width (width);
956
957       var_set_both_formats (var, &fmt);
958     }
959
960   if (n_var_specs ==  0)
961     {
962       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
963            spreadsheet->file_name);
964       goto error;
965     }
966
967   /* Create the first case, and cache it */
968   r->spreadsheet.proto = caseproto_ref (dict_get_proto (r->spreadsheet.dict));
969   r->spreadsheet.first_case = case_create (r->spreadsheet.proto);
970   case_set_missing (r->spreadsheet.first_case);
971
972   for (i = 0 ; i < n_var_specs; ++i)
973     {
974       const struct variable *var = dict_get_var (r->spreadsheet.dict, i);
975
976       convert_xml_to_value (r->spreadsheet.first_case, var,  &var_spec[i].firstval,
977                             r->rsd.col - n_var_specs + i,
978                             r->rsd.row - 1);
979     }
980
981   /* Read in the first row of data */
982   while (1 == xmlTextReaderRead (r->rsd.xtr))
983     {
984       process_node (r, &r->rsd);
985
986       if (r->rsd.state == STATE_ROW)
987         break;
988     }
989
990
991   for (i = 0 ; i < n_var_specs ; ++i)
992     {
993       free (var_spec[i].firstval.type);
994       free (var_spec[i].firstval.value);
995       free (var_spec[i].firstval.text);
996       free (var_spec[i].name);
997     }
998
999   free (var_spec);
1000
1001
1002   return casereader_create_sequential
1003     (NULL,
1004      r->spreadsheet.proto,
1005      n_cases,
1006      &ods_file_casereader_class, r);
1007
1008  error:
1009
1010   for (i = 0 ; i < n_var_specs ; ++i)
1011     {
1012       free (var_spec[i].firstval.type);
1013       free (var_spec[i].firstval.value);
1014       free (var_spec[i].firstval.text);
1015       free (var_spec[i].name);
1016     }
1017
1018   free (var_spec);
1019
1020   ods_file_casereader_destroy (NULL, r);
1021
1022   return NULL;
1023 }
1024
1025
1026 /* Reads and returns one case from READER's file.  Returns a null
1027    pointer on failure. */
1028 static struct ccase *
1029 ods_file_casereader_read (struct casereader *reader UNUSED, void *r_)
1030 {
1031   struct ccase *c = NULL;
1032   struct ods_reader *r = r_;
1033
1034   xmlChar *val_string = NULL;
1035   xmlChar *type = NULL;
1036
1037   if (!r->spreadsheet.used_first_case)
1038     {
1039       r->spreadsheet.used_first_case = true;
1040       return r->spreadsheet.first_case;
1041     }
1042
1043
1044   /* Advance to the start of a row. (If there is one) */
1045   while (r->rsd.state != STATE_ROW
1046          && 1 == xmlTextReaderRead (r->rsd.xtr)
1047         )
1048     {
1049       process_node (r, &r->rsd);
1050     }
1051
1052
1053   if (! reading_target_sheet (r, &r->rsd)
1054        ||  r->rsd.state < STATE_TABLE
1055        ||  (r->spreadsheet.stop_row != -1 && r->rsd.row > r->spreadsheet.stop_row + 1)
1056 )
1057     {
1058       return NULL;
1059     }
1060
1061   c = case_create (r->spreadsheet.proto);
1062   case_set_missing (c);
1063
1064   while (1 == xmlTextReaderRead (r->rsd.xtr))
1065     {
1066       process_node (r, &r->rsd);
1067
1068       if (r->spreadsheet.stop_row != -1 && r->rsd.row > r->spreadsheet.stop_row + 1)
1069         break;
1070
1071       if (r->rsd.state == STATE_CELL &&
1072            r->rsd.node_type == XML_READER_TYPE_ELEMENT)
1073         {
1074           type = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value-type"));
1075           val_string = xmlTextReaderGetAttribute (r->rsd.xtr, _xml ("office:value"));
1076         }
1077
1078       if (r->rsd.state == STATE_CELL_CONTENT &&
1079            r->rsd.node_type == XML_READER_TYPE_TEXT)
1080         {
1081           int col;
1082           struct xml_value *xmv = XZALLOC (struct xml_value);
1083           xmv->text = xmlTextReaderValue (r->rsd.xtr);
1084           xmv->value = val_string;
1085           val_string = NULL;
1086           xmv->type = type;
1087           type = NULL;
1088
1089           for (col = 0; col < r->rsd.col_span; ++col)
1090             {
1091               const struct variable *var;
1092               const int idx = r->rsd.col - col - r->spreadsheet.start_col - 1;
1093               if (idx < 0)
1094                 continue;
1095               if (r->spreadsheet.stop_col != -1 && idx > r->spreadsheet.stop_col - r->spreadsheet.start_col)
1096                 break;
1097               if (idx >= dict_get_n_vars (r->spreadsheet.dict))
1098                 break;
1099
1100               var = dict_get_var (r->spreadsheet.dict, idx);
1101               convert_xml_to_value (c, var, xmv, idx + r->spreadsheet.start_col, r->rsd.row - 1);
1102             }
1103
1104           xmlFree (xmv->text);
1105           xmlFree (xmv->value);
1106           xmlFree (xmv->type);
1107           free (xmv);
1108         }
1109       if (r->rsd.state <= STATE_TABLE)
1110         break;
1111     }
1112
1113   xmlFree (type);
1114   xmlFree (val_string);
1115
1116   return c;
1117 }
1118
1119 static bool
1120 init_reader (struct ods_reader *r, bool report_errors,
1121              struct state_data *state)
1122 {
1123   struct spreadsheet *s = SPREADSHEET_CAST (r);
1124
1125   if (state)
1126     {
1127       struct zip_member *content;
1128       char *error = zip_member_open (r->zreader, "content.xml", &content);
1129       if (content == NULL)
1130         {
1131           free (error);
1132           return NULL;
1133         }
1134
1135       xmlTextReaderPtr xtr = xmlReaderForIO (xml_reader_for_zip_member, NULL, content, NULL, NULL,
1136                                              report_errors
1137                                              ? 0
1138                                              : (XML_PARSE_NOERROR | XML_PARSE_NOWARNING));
1139
1140       if (xtr == NULL)
1141         return false;
1142
1143      *state = (struct state_data) { .xtr = xtr,
1144                                     .zm = content,
1145                                     .state = STATE_INIT };
1146      if (report_errors)
1147        xmlTextReaderSetErrorHandler (xtr, ods_error_handler, r);
1148   }
1149
1150   strcpy (s->type, "ODS");
1151   s->destroy = ods_destroy;
1152   s->make_reader = ods_make_reader;
1153   s->get_sheet_name = ods_get_sheet_name;
1154   s->get_sheet_range = ods_get_sheet_range;
1155   s->get_sheet_n_sheets = ods_get_sheet_n_sheets;
1156   s->get_sheet_n_rows = ods_get_sheet_n_rows;
1157   s->get_sheet_n_columns = ods_get_sheet_n_columns;
1158   s->get_sheet_cell = ods_get_sheet_cell;
1159
1160   return true;
1161 }
1162
1163 struct spreadsheet *
1164 ods_probe (const char *filename, bool report_errors)
1165 {
1166   struct ods_reader *r = XZALLOC (struct ods_reader);
1167
1168   struct zip_reader *zr;
1169   char *error = zip_reader_create (filename, &zr);
1170   if (error)
1171     {
1172       if (report_errors)
1173         {
1174           msg (ME, _("Cannot open %s as a OpenDocument file: %s"),
1175                filename, error);
1176         }
1177       free (error);
1178       free (r);
1179       return NULL;
1180     }
1181
1182   r->zreader = zr;
1183   r->spreadsheet.ref_cnt = 1;
1184   hmap_init (&r->cache);
1185
1186   if (!init_reader (r, report_errors, NULL))
1187     goto error;
1188
1189   r->n_sheets = -1;
1190   r->n_allocated_sheets = 0;
1191   r->spreadsheet.sheets = NULL;
1192
1193   r->spreadsheet.file_name = strdup (filename);
1194   return &r->spreadsheet;
1195
1196  error:
1197   zip_reader_unref (r->zreader);
1198   free (r);
1199   return NULL;
1200 }