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