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