1 /*
2 INI LIBRARY
3
4 Value interpretation functions for arrays of values
5 and corresponding memory cleanup functions.
6
7 Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
8
9 INI Library is free software: you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 INI Library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with INI Library. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "config.h"
24 #include <stdio.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <locale.h>
30 #include "trace.h"
31 #include "collection.h"
32 #include "collection_tools.h"
33 #include "ini_defines.h"
34 #include "ini_config.h"
35
36 /*
37 * Internal contants to indicate how
38 * to process the lists of strings.
39 */
40 #define EXCLUDE_EMPTY 0
41 #define INCLUDE_EMPTY 1
42
43 /* Arrays of stings */
get_str_cfg_array(struct collection_item * item,int include,const char * sep,int * size,int * error)44 static char **get_str_cfg_array(struct collection_item *item,
45 int include,
46 const char *sep,
47 int *size,
48 int *error)
49 {
50 char *copy = NULL;
51 char *dest = NULL;
52 char locsep[4];
53 int lensep;
54 char *buff;
55 int count = 0;
56 int len = 0;
57 int resume_len;
58 char **array;
59 char *start;
60 int i, j;
61 int dlen;
62
63 TRACE_FLOW_STRING("get_str_cfg_array", "Entry");
64
65 /* Do we have the item ? */
66 if ((item == NULL) ||
67 (col_get_item_type(item) != COL_TYPE_STRING)) {
68 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
69 if (error) *error = EINVAL;
70 return NULL;
71 }
72
73 /* Handle the separators */
74 if (sep == NULL) {
75 locsep[0] = ',';
76 locsep[1] = '\0';
77 lensep = 2;
78 }
79 else {
80 strncpy(locsep, sep, 3);
81 locsep[3] = '\0';
82 lensep = strlen(locsep) + 1;
83 }
84
85 /* Allocate memory for the copy of the string */
86 copy = malloc(col_get_item_length(item));
87 if (copy == NULL) {
88 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
89 if (error) *error = ENOMEM;
90 return NULL;
91 }
92
93 /* Loop through the string */
94 dest = copy;
95 buff = col_get_item_data(item);
96 start = buff;
97 dlen = col_get_item_length(item);
98 for(i = 0; i < dlen; i++) {
99 for(j = 0; j < lensep; j++) {
100 if(buff[i] == locsep[j]) {
101 /* If we found one of the separators trim spaces around */
102 resume_len = len;
103 while (len > 0) {
104 if (isspace(start[len - 1])) len--;
105 else break;
106 }
107 TRACE_INFO_STRING("Current:", start);
108 TRACE_INFO_NUMBER("Length:", len);
109 if (len > 0) {
110 /* Save block aside */
111 memcpy(dest, start, len);
112 count++;
113 dest += len;
114 *dest = '\0';
115 dest++;
116 }
117 else if(include) {
118 count++;
119 *dest = '\0';
120 dest++;
121 }
122 if (locsep[j] == '\0') break; /* We are done */
123
124 /* Move forward and trim spaces if any */
125 start += resume_len + 1;
126 i++;
127 TRACE_INFO_STRING("Other pointer :", buff + i);
128 while ((i < dlen) && (isspace(*start))) {
129 i++;
130 start++;
131 }
132 len = -1; /* Len will be increased in the loop */
133 i--; /* i will be increas so we need to step back */
134 TRACE_INFO_STRING("Remaining buffer after triming spaces:", start);
135 break;
136 }
137 }
138 len++;
139 }
140
141 /* Now we know how many items are there in the list */
142 array = malloc((count + 1) * sizeof(char *));
143 if (array == NULL) {
144 free(copy);
145 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
146 if (error) *error = ENOMEM;
147 return NULL;
148 }
149
150 /* Loop again to fill in the pointers */
151 start = copy;
152 for (i = 0; i < count; i++) {
153 TRACE_INFO_STRING("Token :", start);
154 TRACE_INFO_NUMBER("Item :", i);
155 array[i] = start;
156 /* Move to next item */
157 while(*start) start++;
158 start++;
159 }
160 array[count] = NULL;
161
162 if (error) *error = EOK;
163 if (size) *size = count;
164 /* If count is 0 the copy needs to be freed */
165 if (count == 0) free(copy);
166 TRACE_FLOW_STRING("get_str_cfg_array", "Exit");
167 return array;
168 }
169
170 /* Get array of strings from item eliminating empty tokens */
get_string_config_array(struct collection_item * item,const char * sep,int * size,int * error)171 char **get_string_config_array(struct collection_item *item,
172 const char *sep, int *size, int *error)
173 {
174 TRACE_FLOW_STRING("get_string_config_array", "Called.");
175 return get_str_cfg_array(item, EXCLUDE_EMPTY, sep, size, error);
176 }
177 /* Get array of strings from item preserving empty tokens */
get_raw_string_config_array(struct collection_item * item,const char * sep,int * size,int * error)178 char **get_raw_string_config_array(struct collection_item *item,
179 const char *sep, int *size, int *error)
180 {
181 TRACE_FLOW_STRING("get_raw_string_config_array", "Called.");
182 return get_str_cfg_array(item, INCLUDE_EMPTY, sep, size, error);
183 }
184
185 /* Special function to free string config array */
free_string_config_array(char ** str_config)186 void free_string_config_array(char **str_config)
187 {
188 TRACE_FLOW_STRING("free_string_config_array", "Entry");
189
190 if (str_config != NULL) {
191 if (*str_config != NULL) free(*str_config);
192 free(str_config);
193 }
194
195 TRACE_FLOW_STRING("free_string_config_array", "Exit");
196 }
197
198 /* Get an array of long values.
199 * NOTE: For now I leave just one function that returns numeric arrays.
200 * In future if we need other numeric types we can change it to do strtoll
201 * internally and wrap it for backward compatibility.
202 */
get_long_config_array(struct collection_item * item,int * size,int * error)203 long *get_long_config_array(struct collection_item *item, int *size, int *error)
204 {
205 const char *str;
206 char *endptr;
207 long val = 0;
208 long *array;
209 int count = 0;
210 int err;
211
212 TRACE_FLOW_STRING("get_long_config_array", "Entry");
213
214 /* Do we have the item ? */
215 if ((item == NULL) ||
216 (col_get_item_type(item) != COL_TYPE_STRING) ||
217 (size == NULL)) {
218 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
219 if (error) *error = EINVAL;
220 return NULL;
221 }
222
223 /* Assume that we have maximum number of different numbers */
224 array = (long *)malloc(sizeof(long) * col_get_item_length(item)/2);
225 if (array == NULL) {
226 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
227 if (error) *error = ENOMEM;
228 return NULL;
229 }
230
231 /* Now parse the string */
232 str = (const char *)col_get_item_data(item);
233 while (*str) {
234
235 errno = 0;
236 val = strtol(str, &endptr, 10);
237 err = errno;
238
239 if (err) {
240 TRACE_ERROR_NUMBER("Conversion failed", err);
241 free(array);
242 if (error) *error = err;
243 return NULL;
244 }
245
246 if (endptr == str) {
247 TRACE_ERROR_NUMBER("Nothing processed", EIO);
248 free(array);
249 if (error) *error = EIO;
250 return NULL;
251 }
252
253 /* Save value */
254 array[count] = val;
255 count++;
256 /* Are we done? */
257 if (*endptr == 0) break;
258 /* Advance to the next valid number */
259 for (str = endptr; *str; str++) {
260 if (isdigit(*str) || (*str == '-') || (*str == '+')) break;
261 }
262 }
263
264 *size = count;
265 if (error) *error = EOK;
266
267 TRACE_FLOW_NUMBER("get_long_config_value returning", val);
268 return array;
269
270 }
271
272 /* Get an array of double values */
get_double_config_array(struct collection_item * item,int * size,int * error)273 double *get_double_config_array(struct collection_item *item, int *size, int *error)
274 {
275 const char *str;
276 char *endptr;
277 double val = 0;
278 double *array;
279 int count = 0;
280 struct lconv *loc;
281
282 TRACE_FLOW_STRING("get_double_config_array", "Entry");
283
284 /* Do we have the item ? */
285 if ((item == NULL) ||
286 (col_get_item_type(item) != COL_TYPE_STRING) ||
287 (size == NULL)) {
288 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
289 if (error) *error = EINVAL;
290 return NULL;
291 }
292
293 /* Assume that we have maximum number of different numbers */
294 array = (double *)malloc(sizeof(double) * col_get_item_length(item)/2);
295 if (array == NULL) {
296 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
297 if (error) *error = ENOMEM;
298 return NULL;
299 }
300
301 /* Get locale information so that we can check for decimal point character.
302 * Based on the man pages it is unclear if this is an allocated memory or not.
303 * Seems like it is a static thread or process local structure so
304 * I will not try to free it after use.
305 */
306 loc = localeconv();
307
308 /* Now parse the string */
309 str = (const char *)col_get_item_data(item);
310 while (*str) {
311 TRACE_INFO_STRING("String to convert",str);
312 errno = 0;
313 val = strtod(str, &endptr);
314 if ((errno == ERANGE) ||
315 ((errno != 0) && (val == 0)) ||
316 (endptr == str)) {
317 TRACE_ERROR_NUMBER("Conversion failed", EIO);
318 free(array);
319 if (error) *error = EIO;
320 return NULL;
321 }
322 /* Save value */
323 array[count] = val;
324 count++;
325 /* Are we done? */
326 if (*endptr == 0) break;
327 TRACE_INFO_STRING("End pointer after conversion",endptr);
328 /* Advance to the next valid number */
329 for (str = endptr; *str; str++) {
330 if (isdigit(*str) || (*str == '-') || (*str == '+') ||
331 /* It is ok to do this since the string is null terminated */
332 ((*str == *(loc->decimal_point)) && isdigit(str[1]))) break;
333 }
334 }
335
336 *size = count;
337 if (error) *error = EOK;
338
339 TRACE_FLOW_NUMBER("get_double_config_value returning", val);
340 return array;
341
342 }
343
344
345 /* Special function to free long config array */
free_long_config_array(long * array)346 void free_long_config_array(long *array)
347 {
348 if (array != NULL) free(array);
349 }
350
351 /* Special function to free double config array */
free_double_config_array(double * array)352 void free_double_config_array(double *array)
353 {
354 if (array != NULL) free(array);
355 }
356