Consolidate quoting style in printed strings.
[pspp] / src / data / gnumeric-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009, 2010 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
18
19 #include <config.h>
20
21 #include <libpspp/message.h>
22 #include <libpspp/misc.h>
23
24 #include "minmax.h"
25
26 #include "gettext.h"
27 #define _(msgid) gettext (msgid)
28 #define N_(msgid) (msgid)
29
30
31 #if !GNM_SUPPORT
32
33 struct casereader *
34 gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
35 {
36   msg (ME, _("Support for Gnumeric files was not compiled into this installation of PSPP"));
37
38   return NULL;
39 }
40
41 #else
42
43 #include <data/casereader-provider.h>
44 #include <errno.h>
45 #include <libpspp/str.h>
46 #include <libpspp/i18n.h>
47 #include <data/dictionary.h>
48 #include <data/variable.h>
49 #include <xalloc.h>
50
51 #include <errno.h>
52 #include <libxml/xmlreader.h>
53 #include <zlib.h>
54 #include <stdbool.h>
55
56 #include <data/case.h>
57 #include <data/value.h>
58
59 #include "gnumeric-reader.h"
60 #include <data/identifier.h>
61 #include <assert.h>
62
63 /* Default width of string variables. */
64 #define GNUMERIC_DEFAULT_WIDTH 8
65
66 static void gnm_file_casereader_destroy (struct casereader *, void *);
67
68 static struct ccase *gnm_file_casereader_read (struct casereader *, void *);
69
70 static const struct casereader_class gnm_file_casereader_class =
71   {
72     gnm_file_casereader_read,
73     gnm_file_casereader_destroy,
74     NULL,
75     NULL,
76   };
77
78 /* Convert a string, which is an integer encoded in base26
79    IE, A=0, B=1, ... Z=25 to the integer it represents.
80    ... except that in this scheme, digits with an exponent
81    greater than 1 are implicitly incremented by 1, so
82    AA  = 0 + 1*26, AB = 1 + 1*26,
83    ABC = 2 + 2*26 + 1*26^2 ....
84 */
85 static int
86 pseudo_base26 (const char *str)
87 {
88   int i;
89   int multiplier = 1;
90   int result = 0;
91   int len = strlen (str);
92
93   for ( i = len - 1 ; i >= 0; --i)
94     {
95       int mantissa = (str[i] - 'A');
96
97       if ( mantissa < 0 || mantissa > 25 )
98         return -1;
99
100       if ( i != len - 1)
101         mantissa++;
102
103       result += mantissa * multiplier;
104
105       multiplier *= 26;
106     }
107
108   return result;
109 }
110
111
112
113 /* Convert a cell reference in the form "A1:B2", to
114    integers.  A1 means column zero, row zero.
115    B1 means column 1 row 0. AA1 means column 26, row 0.
116 */
117 static bool
118 convert_cell_ref (const char *ref,
119                   int *col0, int *row0,
120                   int *coli, int *rowi)
121 {
122   char startcol[5];
123   char stopcol [5];
124
125   int startrow;
126   int stoprow;
127
128   int n = sscanf (ref, "%4[a-zA-Z]%d:%4[a-zA-Z]%d",
129               startcol, &startrow,
130               stopcol, &stoprow);
131   if ( n != 4)
132     return false;
133
134   str_uppercase (startcol);
135   *col0 = pseudo_base26 (startcol);
136   str_uppercase (stopcol);
137   *coli = pseudo_base26 (stopcol);
138   *row0 = startrow - 1;
139   *rowi = stoprow - 1 ;
140
141   return true;
142 }
143
144
145 enum reader_state
146   {
147     STATE_INIT = 0,        /* Initial state */
148     STATE_SHEET_START,     /* Found the start of a sheet */
149     STATE_SHEET_NAME,      /* Found the sheet name */
150     STATE_MAXROW,
151     STATE_SHEET_FOUND,     /* Found the sheet that we actually want */
152     STATE_CELLS_START,     /* Found the start of the cell array */
153     STATE_CELL             /* Found a cell */
154   };
155
156
157 struct gnumeric_reader
158 {
159   xmlTextReaderPtr xtr;
160
161   enum reader_state state;
162   int row;
163   int col;
164   int node_type;
165   int sheet_index;
166
167
168   const xmlChar *target_sheet;
169   int target_sheet_index;
170
171   int start_row;
172   int start_col;
173   int stop_row;
174   int stop_col;
175
176   struct caseproto *proto;
177   struct dictionary *dict;
178   struct ccase *first_case;
179   bool used_first_case;
180 };
181
182 static void process_node (struct gnumeric_reader *r);
183
184 #define _xml(X) (const xmlChar *)(X)
185
186 #define _xmlchar_to_int(X) atoi((const char *)X)
187
188 static void
189 gnm_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
190 {
191   struct gnumeric_reader *r = r_;
192   if ( r == NULL)
193         return ;
194
195   if ( r->xtr)
196     xmlFreeTextReader (r->xtr);
197
198   if ( ! r->used_first_case )
199     case_unref (r->first_case);
200
201   caseproto_unref (r->proto);
202
203   free (r);
204 }
205
206 static void
207 process_node (struct gnumeric_reader *r)
208 {
209   xmlChar *name = xmlTextReaderName (r->xtr);
210   if (name == NULL)
211     name = xmlStrdup (_xml ("--"));
212
213
214   r->node_type = xmlTextReaderNodeType (r->xtr);
215
216   switch ( r->state)
217     {
218     case STATE_INIT:
219       if (0 == xmlStrcasecmp (name, _xml("gnm:Sheet")) &&
220           XML_READER_TYPE_ELEMENT  == r->node_type)
221         {
222           r->state = STATE_SHEET_START;
223         }
224       break;
225     case STATE_SHEET_START:
226       if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
227           XML_READER_TYPE_ELEMENT  == r->node_type)
228         {
229           r->state = STATE_SHEET_NAME;
230         }
231       else if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
232                XML_READER_TYPE_END_ELEMENT  == r->node_type)
233         {
234           r->state = STATE_INIT;
235         }
236       break;
237     case STATE_SHEET_NAME:
238       if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
239           XML_READER_TYPE_END_ELEMENT  == r->node_type)
240         {
241           r->state = STATE_SHEET_START;
242         }
243       else if (XML_READER_TYPE_TEXT == r->node_type)
244         {
245           ++r->sheet_index;
246           if ( r->target_sheet != NULL)
247             {
248               xmlChar *value = xmlTextReaderValue (r->xtr);
249               if ( 0 == xmlStrcmp (value, r->target_sheet))
250                 r->state = STATE_SHEET_FOUND;
251               free (value);
252             }
253           else if (r->target_sheet_index == r->sheet_index)
254             {
255               r->state = STATE_SHEET_FOUND;
256             }
257         }
258       break;
259     case STATE_SHEET_FOUND:
260       if (0 == xmlStrcasecmp (name, _xml("gnm:Cells"))  &&
261           XML_READER_TYPE_ELEMENT  == r->node_type)
262         {
263           if (! xmlTextReaderIsEmptyElement (r->xtr))
264             r->state = STATE_CELLS_START;
265         }
266       else if (0 == xmlStrcasecmp (name, _xml("gnm:MaxRow"))  &&
267           XML_READER_TYPE_ELEMENT  == r->node_type)
268         {
269           r->state = STATE_MAXROW;
270         }
271       else if (0 == xmlStrcasecmp (name, _xml("gnm:Sheet"))  &&
272           XML_READER_TYPE_END_ELEMENT  == r->node_type)
273         {
274           r->state = STATE_INIT;
275         }
276       break;
277     case STATE_MAXROW:
278       if (0 == xmlStrcasecmp (name, _xml("gnm:MaxRow"))  &&
279           XML_READER_TYPE_END_ELEMENT  == r->node_type)
280         {
281           r->state = STATE_SHEET_FOUND;
282         }
283     case STATE_CELLS_START:
284       if (0 == xmlStrcasecmp (name, _xml ("gnm:Cell"))  &&
285           XML_READER_TYPE_ELEMENT  == r->node_type)
286         {
287           xmlChar *attr = NULL;
288           r->state = STATE_CELL;
289
290           attr = xmlTextReaderGetAttribute (r->xtr, _xml ("Col"));
291           r->col =  _xmlchar_to_int (attr);
292           free (attr);
293
294           attr = xmlTextReaderGetAttribute (r->xtr, _xml ("Row"));
295           r->row = _xmlchar_to_int (attr);
296           free (attr);
297         }
298       else if (0 == xmlStrcasecmp (name, _xml("gnm:Cells"))  &&
299                XML_READER_TYPE_END_ELEMENT  == r->node_type)
300         r->state = STATE_SHEET_NAME;
301
302       break;
303     case STATE_CELL:
304       if (0 == xmlStrcasecmp (name, _xml("gnm:Cell"))  &&
305                               XML_READER_TYPE_END_ELEMENT  == r->node_type)
306         r->state = STATE_CELLS_START;
307       break;
308     default:
309       break;
310     };
311
312   xmlFree (name);
313 }
314
315
316 /*
317    Sets the VAR of case C, to the value corresponding to the xml string XV
318  */
319 static void
320 convert_xml_string_to_value (struct ccase *c, const struct variable *var,
321                              const xmlChar *xv)
322 {
323   union value *v = case_data_rw (c, var);
324
325   if (xv == NULL)
326     value_set_missing (v, var_get_width (var));
327   else if ( var_is_alpha (var))
328     value_copy_str_rpad (v, var_get_width (var), xv, ' ');
329   else
330     {
331       const char *text = (const char *) xv;
332       char *endptr;
333
334       errno = 0;
335       v->f = strtod (text, &endptr);
336       if ( errno != 0 || endptr == text)
337         v->f = SYSMIS;
338     }
339 }
340
341 struct var_spec
342 {
343   char *name;
344   int width;
345   xmlChar *first_value;
346 };
347
348 struct casereader *
349 gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
350 {
351   unsigned long int vstart = 0;
352   int ret;
353   casenumber n_cases = CASENUMBER_MAX;
354   int i;
355   struct var_spec *var_spec = NULL;
356   int n_var_specs = 0;
357
358   struct gnumeric_reader *r = NULL;
359
360   gzFile gz = gzopen (gri->file_name, "r");
361
362   if ( NULL == gz)
363     {
364       msg (ME, _("Error opening `%s' for reading as a Gnumeric file: %s."),
365            gri->file_name, strerror (errno));
366
367       goto error;
368     }
369
370   r = xzalloc (sizeof *r);
371
372   r->xtr = xmlReaderForIO ((xmlInputReadCallback) gzread, gzclose, gz,
373                            NULL, NULL, 0);
374
375   if ( r->xtr == NULL)
376     goto error;
377
378   if ( gri->cell_range )
379     {
380       if ( ! convert_cell_ref (gri->cell_range,
381                                &r->start_col, &r->start_row,
382                                &r->stop_col, &r->stop_row))
383         {
384           msg (SE, _("Invalid cell range `%s'"),
385                gri->cell_range);
386           goto error;
387         }
388     }
389   else
390     {
391       r->start_col = 0;
392       r->start_row = 0;
393       r->stop_col = -1;
394       r->stop_row = -1;
395     }
396
397   r->state = STATE_INIT;
398   r->target_sheet = BAD_CAST gri->sheet_name;
399   r->target_sheet_index = gri->sheet_index;
400   r->row = r->col = -1;
401   r->sheet_index = 0;
402
403   /* Advance to the start of the cells for the target sheet */
404   while ( (r->state != STATE_CELL || r->row < r->start_row )
405           && 1 == (ret = xmlTextReaderRead (r->xtr)))
406     {
407       xmlChar *value ;
408       process_node (r);
409       value = xmlTextReaderValue (r->xtr);
410
411       if ( r->state == STATE_MAXROW  && r->node_type == XML_READER_TYPE_TEXT)
412         {
413           n_cases = 1 + _xmlchar_to_int (value) ;
414         }
415       free (value);
416     }
417
418
419   /* If a range has been given, then  use that to calculate the number
420      of cases */
421   if ( gri->cell_range)
422     {
423       n_cases = MIN (n_cases, r->stop_row - r->start_row + 1);
424     }
425
426   if ( gri->read_names )
427     {
428       r->start_row++;
429       n_cases --;
430     }
431
432   /* Read in the first row of cells,
433      including the headers if read_names was set */
434   while (
435          (( r->state == STATE_CELLS_START && r->row <= r->start_row) || r->state == STATE_CELL )
436          && (ret = xmlTextReaderRead (r->xtr))
437          )
438     {
439       int idx;
440       process_node (r);
441
442       if ( r->row > r->start_row ) break;
443
444       if ( r->col < r->start_col ||
445            (r->stop_col != -1 && r->col > r->stop_col))
446         continue;
447
448       idx = r->col - r->start_col;
449
450       if ( idx  >= n_var_specs )
451         {
452           n_var_specs =  idx + 1 ;
453           var_spec = xrealloc (var_spec, sizeof (*var_spec) * n_var_specs);
454           var_spec [idx].name = NULL;
455           var_spec [idx].width = -1;
456           var_spec [idx].first_value = NULL;
457         }
458
459       if ( r->node_type == XML_READER_TYPE_TEXT )
460         {
461           xmlChar *value = xmlTextReaderValue (r->xtr);
462           const char *text  = (const char *) value;
463
464           if ( r->row < r->start_row)
465             {
466               if ( gri->read_names )
467                 {
468                   var_spec [idx].name = xstrdup (text);
469                 }
470             }
471           else
472             {
473               var_spec [idx].first_value = xmlStrdup (value);
474
475               if (-1 ==  var_spec [idx].width )
476                 var_spec [idx].width = (gri->asw == -1) ?
477                   ROUND_UP (strlen(text), GNUMERIC_DEFAULT_WIDTH) : gri->asw;
478             }
479
480           free (value);
481         }
482       else if ( r->node_type == XML_READER_TYPE_ELEMENT
483                 && r->state == STATE_CELL)
484         {
485           if ( r->row == r->start_row )
486             {
487               xmlChar *attr =
488                 xmlTextReaderGetAttribute (r->xtr, _xml ("ValueType"));
489
490               if ( NULL == attr || 60 !=  _xmlchar_to_int (attr))
491                 var_spec [idx].width = 0;
492
493               free (attr);
494             }
495         }
496     }
497
498
499   /* Create the dictionary and populate it */
500   *dict = r->dict = dict_create ();
501
502   dict_set_encoding (r->dict, (const char *) xmlTextReaderConstEncoding (r->xtr));
503
504   for (i = 0 ; i < n_var_specs ; ++i )
505     {
506       char name[VAR_NAME_LEN + 1];
507
508       /* Probably no data exists for this variable, so allocate a
509          default width */
510       if ( var_spec[i].width == -1 )
511         var_spec[i].width = GNUMERIC_DEFAULT_WIDTH;
512
513       if  ( ! dict_make_unique_var_name (r->dict, var_spec[i].name,
514                                          &vstart, name))
515         {
516           msg (ME, _("Cannot create variable name from %s"), var_spec[i].name);
517           goto error;
518         }
519
520       dict_create_var (r->dict, name, var_spec[i].width);
521     }
522
523   /* Create the first case, and cache it */
524   r->used_first_case = false;
525
526   if ( n_var_specs ==  0 )
527     {
528       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
529            gri->file_name);
530       goto error;
531     }
532
533   r->proto = caseproto_ref (dict_get_proto (r->dict));
534   r->first_case = case_create (r->proto);
535   case_set_missing (r->first_case);
536
537   for ( i = 0 ; i < n_var_specs ; ++i )
538     {
539       const struct variable *var = dict_get_var (r->dict, i);
540
541       convert_xml_string_to_value (r->first_case, var,
542                                    var_spec[i].first_value);
543     }
544
545   for ( i = 0 ; i < n_var_specs ; ++i )
546     {
547       free (var_spec[i].first_value);
548       free (var_spec[i].name);
549     }
550
551   free (var_spec);
552
553   return casereader_create_sequential
554     (NULL,
555      r->proto,
556      n_cases,
557      &gnm_file_casereader_class, r);
558
559
560  error:
561   for ( i = 0 ; i < n_var_specs ; ++i )
562     {
563       free (var_spec[i].first_value);
564       free (var_spec[i].name);
565     }
566
567   free (var_spec);
568   dict_destroy (*dict);
569
570   gnm_file_casereader_destroy (NULL, r);
571
572   return NULL;
573 };
574
575
576 /* Reads and returns one case from READER's file.  Returns a null
577    pointer on failure. */
578 static struct ccase *
579 gnm_file_casereader_read (struct casereader *reader UNUSED, void *r_)
580 {
581   struct ccase *c;
582   int ret = 0;
583
584   struct gnumeric_reader *r = r_;
585   int current_row = r->row;
586
587   if ( !r->used_first_case )
588     {
589       r->used_first_case = true;
590       return r->first_case;
591     }
592
593   c = case_create (r->proto);
594   case_set_missing (c);
595
596   while ((r->state == STATE_CELL || r->state == STATE_CELLS_START )
597          && r->row == current_row && (ret = xmlTextReaderRead (r->xtr)))
598     {
599       process_node (r);
600
601       if ( r->col < r->start_col || (r->stop_col != -1 &&
602                                      r->col > r->stop_col))
603         continue;
604
605       if ( r->col - r->start_col >= caseproto_get_n_widths (r->proto))
606         continue;
607
608       if ( r->stop_row != -1 && r->row > r->stop_row)
609         break;
610
611       if ( r->node_type == XML_READER_TYPE_TEXT )
612         {
613           xmlChar *value = xmlTextReaderValue (r->xtr);
614
615           const int idx = r->col - r->start_col;
616
617           const struct variable *var = dict_get_var (r->dict, idx);
618
619           convert_xml_string_to_value (c, var, value);
620
621           free (value);
622         }
623
624     }
625
626   if (ret == 1)
627     return c;
628   else
629     {
630       case_unref (c);
631       return NULL;
632     }
633 }
634
635
636 #endif /* GNM_SUPPORT */