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