Line data Source code
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 */
44 6 : 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 6 : char *copy = NULL;
51 6 : char *dest = NULL;
52 : char locsep[4];
53 : int lensep;
54 : char *buff;
55 6 : int count = 0;
56 6 : 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 12 : if ((item == NULL) ||
67 6 : (col_get_item_type(item) != COL_TYPE_STRING)) {
68 : TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
69 0 : if (error) *error = EINVAL;
70 : return NULL;
71 : }
72 :
73 : /* Handle the separators */
74 6 : if (sep == NULL) {
75 0 : locsep[0] = ',';
76 0 : locsep[1] = '\0';
77 0 : lensep = 2;
78 : }
79 : else {
80 6 : strncpy(locsep, sep, 3);
81 6 : locsep[3] = '\0';
82 6 : lensep = strlen(locsep) + 1;
83 : }
84 :
85 : /* Allocate memory for the copy of the string */
86 6 : copy = malloc(col_get_item_length(item));
87 6 : if (copy == NULL) {
88 : TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
89 0 : if (error) *error = ENOMEM;
90 : return NULL;
91 : }
92 :
93 : /* Loop through the string */
94 6 : dest = copy;
95 6 : buff = col_get_item_data(item);
96 6 : start = buff;
97 6 : dlen = col_get_item_length(item);
98 177 : for(i = 0; i < dlen; i++) {
99 270 : for(j = 0; j < lensep; j++) {
100 309 : if(buff[i] == locsep[j]) {
101 : /* If we found one of the separators trim spaces around */
102 : resume_len = len;
103 55 : while (len > 0) {
104 28 : if (isspace(start[len - 1])) len--;
105 : else break;
106 : }
107 : TRACE_INFO_STRING("Current:", start);
108 : TRACE_INFO_NUMBER("Length:", len);
109 39 : if (len > 0) {
110 : /* Save block aside */
111 12 : memcpy(dest, start, len);
112 12 : count++;
113 12 : dest += len;
114 12 : *dest = '\0';
115 12 : dest++;
116 : }
117 27 : else if(include) {
118 10 : count++;
119 10 : *dest = '\0';
120 10 : dest++;
121 : }
122 39 : if (locsep[j] == '\0') break; /* We are done */
123 :
124 : /* Move forward and trim spaces if any */
125 33 : start += resume_len + 1;
126 33 : i++;
127 : TRACE_INFO_STRING("Other pointer :", buff + i);
128 163 : while ((i < dlen) && (isspace(*start))) {
129 97 : i++;
130 97 : start++;
131 : }
132 33 : len = -1; /* Len will be increased in the loop */
133 33 : i--; /* i will be increas so we need to step back */
134 : TRACE_INFO_STRING("Remaining buffer after triming spaces:", start);
135 33 : break;
136 : }
137 : }
138 171 : len++;
139 : }
140 :
141 : /* Now we know how many items are there in the list */
142 6 : array = malloc((count + 1) * sizeof(char *));
143 6 : if (array == NULL) {
144 0 : free(copy);
145 : TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
146 0 : if (error) *error = ENOMEM;
147 : return NULL;
148 : }
149 :
150 : /* Loop again to fill in the pointers */
151 : start = copy;
152 22 : for (i = 0; i < count; i++) {
153 : TRACE_INFO_STRING("Token :", start);
154 : TRACE_INFO_NUMBER("Item :", i);
155 22 : array[i] = start;
156 : /* Move to next item */
157 22 : while(*start) start++;
158 22 : start++;
159 : }
160 6 : array[count] = NULL;
161 :
162 6 : if (error) *error = EOK;
163 6 : if (size) *size = count;
164 : /* If count is 0 the copy needs to be freed */
165 6 : if (count == 0) free(copy);
166 : TRACE_FLOW_STRING("get_str_cfg_array", "Exit");
167 6 : return array;
168 : }
169 :
170 : /* Get array of strings from item eliminating empty tokens */
171 4 : 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 4 : return get_str_cfg_array(item, EXCLUDE_EMPTY, sep, size, error);
176 : }
177 : /* Get array of strings from item preserving empty tokens */
178 2 : 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 2 : return get_str_cfg_array(item, INCLUDE_EMPTY, sep, size, error);
183 : }
184 :
185 : /* Special function to free string config array */
186 6 : void free_string_config_array(char **str_config)
187 : {
188 : TRACE_FLOW_STRING("free_string_config_array", "Entry");
189 :
190 6 : if (str_config != NULL) {
191 6 : if (*str_config != NULL) free(*str_config);
192 6 : free(str_config);
193 : }
194 :
195 : TRACE_FLOW_STRING("free_string_config_array", "Exit");
196 6 : }
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 : */
203 1 : long *get_long_config_array(struct collection_item *item, int *size, int *error)
204 : {
205 : const char *str;
206 : char *endptr;
207 1 : long val = 0;
208 : long *array;
209 1 : int count = 0;
210 : int err;
211 :
212 : TRACE_FLOW_STRING("get_long_config_array", "Entry");
213 :
214 : /* Do we have the item ? */
215 2 : if ((item == NULL) ||
216 2 : (col_get_item_type(item) != COL_TYPE_STRING) ||
217 : (size == NULL)) {
218 : TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
219 0 : if (error) *error = EINVAL;
220 : return NULL;
221 : }
222 :
223 : /* Assume that we have maximum number of different numbers */
224 1 : array = (long *)malloc(sizeof(long) * col_get_item_length(item)/2);
225 1 : if (array == NULL) {
226 : TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
227 0 : if (error) *error = ENOMEM;
228 : return NULL;
229 : }
230 :
231 : /* Now parse the string */
232 1 : str = (const char *)col_get_item_data(item);
233 8 : while (*str) {
234 :
235 6 : errno = 0;
236 6 : val = strtol(str, &endptr, 10);
237 6 : err = errno;
238 :
239 6 : if (err) {
240 : TRACE_ERROR_NUMBER("Conversion failed", err);
241 0 : free(array);
242 0 : if (error) *error = err;
243 : return NULL;
244 : }
245 :
246 6 : if (endptr == str) {
247 : TRACE_ERROR_NUMBER("Nothing processed", EIO);
248 0 : free(array);
249 0 : if (error) *error = EIO;
250 : return NULL;
251 : }
252 :
253 : /* Save value */
254 6 : array[count] = val;
255 6 : count++;
256 : /* Are we done? */
257 6 : if (*endptr == 0) break;
258 : /* Advance to the next valid number */
259 13 : for (str = endptr; *str; str++) {
260 18 : if (isdigit(*str) || (*str == '-') || (*str == '+')) break;
261 : }
262 : }
263 :
264 1 : *size = count;
265 1 : if (error) *error = EOK;
266 :
267 : TRACE_FLOW_NUMBER("get_long_config_value returning", val);
268 1 : return array;
269 :
270 : }
271 :
272 : /* Get an array of double values */
273 1 : double *get_double_config_array(struct collection_item *item, int *size, int *error)
274 : {
275 : const char *str;
276 : char *endptr;
277 1 : double val = 0;
278 : double *array;
279 1 : 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 2 : if ((item == NULL) ||
286 2 : (col_get_item_type(item) != COL_TYPE_STRING) ||
287 : (size == NULL)) {
288 : TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
289 0 : if (error) *error = EINVAL;
290 : return NULL;
291 : }
292 :
293 : /* Assume that we have maximum number of different numbers */
294 1 : array = (double *)malloc(sizeof(double) * col_get_item_length(item)/2);
295 1 : if (array == NULL) {
296 : TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
297 0 : 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 1 : loc = localeconv();
307 :
308 : /* Now parse the string */
309 1 : str = (const char *)col_get_item_data(item);
310 8 : while (*str) {
311 : TRACE_INFO_STRING("String to convert",str);
312 6 : errno = 0;
313 6 : val = strtod(str, &endptr);
314 6 : if ((errno == ERANGE) ||
315 6 : ((errno != 0) && (val == 0)) ||
316 6 : (endptr == str)) {
317 : TRACE_ERROR_NUMBER("Conversion failed", EIO);
318 0 : free(array);
319 0 : if (error) *error = EIO;
320 : return NULL;
321 : }
322 : /* Save value */
323 6 : array[count] = val;
324 6 : count++;
325 : /* Are we done? */
326 6 : if (*endptr == 0) break;
327 : TRACE_INFO_STRING("End pointer after conversion",endptr);
328 : /* Advance to the next valid number */
329 14 : for (str = endptr; *str; str++) {
330 35 : if (isdigit(*str) || (*str == '-') || (*str == '+') ||
331 : /* It is ok to do this since the string is null terminated */
332 19 : ((*str == *(loc->decimal_point)) && isdigit(str[1]))) break;
333 : }
334 : }
335 :
336 1 : *size = count;
337 1 : if (error) *error = EOK;
338 :
339 : TRACE_FLOW_NUMBER("get_double_config_value returning", val);
340 1 : return array;
341 :
342 : }
343 :
344 :
345 : /* Special function to free long config array */
346 1 : void free_long_config_array(long *array)
347 : {
348 1 : if (array != NULL) free(array);
349 1 : }
350 :
351 : /* Special function to free double config array */
352 1 : void free_double_config_array(double *array)
353 : {
354 1 : if (array != NULL) free(array);
355 1 : }
|