294b93cec5d3f5190d5bddde902ea6e0c0d273d5
[pspp] / src / data / spreadsheet-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009, 2010, 2011, 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 "spreadsheet-reader.h"
20
21 #include <libpspp/assertion.h>
22 #include "gnumeric-reader.h"
23 #include "ods-reader.h"
24
25 #include <libpspp/str.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <gl/xalloc.h>
29 #include <gl/c-xvasprintf.h>
30 #include <stdlib.h>
31
32
33 void 
34 spreadsheet_destroy (struct spreadsheet *s)
35 {
36   switch (s->type)
37     {
38 #ifdef ODF_READ_SUPPORT
39     case SPREADSHEET_ODS:
40       ods_destroy (s);
41       break;
42 #endif
43 #ifdef GNM_READ_SUPPORT
44     case SPREADSHEET_GNUMERIC:
45       gnumeric_destroy (s);
46       break;
47 #endif
48     default:
49       NOT_REACHED ();
50       break;
51     }
52 }
53
54
55 struct casereader * 
56 spreadsheet_make_reader (struct spreadsheet *s UNUSED,
57                          const struct spreadsheet_read_options *opts UNUSED)
58 {
59 #ifdef ODS_READ_SUPPORT
60   if ( s->type == SPREADSHEET_ODS)
61     return ods_make_reader (s, opts);
62 #endif
63 #ifdef GNM_READ_SUPPORT
64   if ( s->type == SPREADSHEET_GNUMERIC)
65     return gnumeric_make_reader (s, opts);
66 #endif
67
68   return NULL;
69 }
70
71 const char * 
72 spreadsheet_get_sheet_name (struct spreadsheet *s, int n)
73 {
74 #ifdef ODF_READ_SUPPORT
75   if ( s->type == SPREADSHEET_ODS)
76     return ods_get_sheet_name (s, n);
77 #endif
78 #ifdef GNM_READ_SUPPORT
79   if ( s->type == SPREADSHEET_GNUMERIC)
80     return gnumeric_get_sheet_name (s, n);
81 #endif
82
83   return NULL;
84 }
85
86 char * 
87 spreadsheet_get_sheet_range (struct spreadsheet *s, int n)
88 {
89 #ifdef ODF_READ_SUPPORT
90   if ( s->type == SPREADSHEET_ODS)
91     return ods_get_sheet_range (s, n);
92 #endif
93
94 #ifdef GNM_READ_SUPPORT
95   if ( s->type == SPREADSHEET_GNUMERIC)
96     return gnumeric_get_sheet_range (s, n);
97 #endif
98
99   return NULL;
100 }
101
102
103 #define RADIX 26
104
105 static void
106 reverse (char *s, int len)
107 {
108   int i;
109   for (i = 0; i < len / 2; ++i)
110     {
111       char tmp = s[len - i - 1];
112       s[len - i -1] = s[i];
113       s[i] = tmp;
114     }
115 }
116
117
118 /* Convert a string, which is an integer encoded in base26
119    IE, A=0, B=1, ... Z=25 to the integer it represents.
120    ... except that in this scheme, digits with an exponent
121    greater than 1 are implicitly incremented by 1, so
122    AA  = 0 + 1*26, AB = 1 + 1*26,
123    ABC = 2 + 2*26 + 1*26^2 ....
124 */
125 int
126 ps26_to_int (const char *str)
127 {
128   int i;
129   int multiplier = 1;
130   int result = 0;
131   int len = strlen (str);
132
133   for (i = len - 1 ; i >= 0; --i)
134     {
135       int mantissa = (str[i] - 'A');
136
137       assert (mantissa >= 0);
138       assert (mantissa < RADIX);
139
140       if (i != len - 1)
141         mantissa++;
142
143       result += mantissa * multiplier;
144       multiplier *= RADIX;
145     }
146
147   return result;
148 }
149
150 char *
151 int_to_ps26 (int i)
152 {
153   char *ret = NULL;
154
155   int lower = 0;
156   long long int base = RADIX;
157   int exp = 1;
158
159   assert (i >= 0);
160
161   while (i > lower + base - 1)
162     {
163       lower += base;
164       base *= RADIX;      
165       assert (base > 0);
166       exp++;
167     }
168
169   i -= lower;
170   i += base;
171
172   ret = xmalloc (exp + 1);
173
174   exp = 0;
175   do
176     {
177       ret[exp++] = (i % RADIX) + 'A';
178       i /= RADIX;
179     }
180   while (i > 1);
181
182   ret[exp]='\0';
183
184   reverse (ret, exp);
185   return ret;
186 }
187
188
189 char *
190 create_cell_ref (int col0, int row0)
191 {
192   char *cs0 ;
193   char *s ;
194
195   if ( col0 < 0) return NULL;
196   if ( row0 < 0) return NULL;
197
198   cs0 =  int_to_ps26 (col0);
199   s =  c_xasprintf ("%s%d", cs0, row0 + 1);
200
201   free (cs0);
202
203   return s;
204 }
205
206 char *
207 create_cell_range (int col0, int row0, int coli, int rowi)
208 {
209   char *s0 = create_cell_ref (col0, row0);
210   char *si = create_cell_ref (coli, rowi);
211
212   char *s =  c_xasprintf ("%s:%s", s0, si);
213
214   free (s0);
215   free (si);
216
217   return s;
218 }
219
220
221 /* Convert a cell reference in the form "A1:B2", to
222    integers.  A1 means column zero, row zero.
223    B1 means column 1 row 0. AA1 means column 26, row 0.
224 */
225 bool
226 convert_cell_ref (const char *ref,
227                   int *col0, int *row0,
228                   int *coli, int *rowi)
229 {
230   char startcol[5];
231   char stopcol [5];
232
233   int startrow;
234   int stoprow;
235
236   int n = sscanf (ref, "%4[a-zA-Z]%d:%4[a-zA-Z]%d",
237               startcol, &startrow,
238               stopcol, &stoprow);
239   if ( n != 4)
240     return false;
241
242   str_uppercase (startcol);
243   *col0 = ps26_to_int (startcol);
244   str_uppercase (stopcol);
245   *coli = ps26_to_int (stopcol);
246   *row0 = startrow - 1;
247   *rowi = stoprow - 1 ;
248
249   return true;
250 }
251