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