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