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