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> 2012
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_configobj.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 : /* Maximum number of separators supported. Do not make it less than 3. */
44 : #define MAX_SEP_LEN 3
45 :
46 : /* Arrays of stings */
47 20 : static char **get_str_cfg_array(struct value_obj *vo,
48 : int include,
49 : const char *sep,
50 : int *size,
51 : int *error)
52 : {
53 20 : char *copy = NULL;
54 20 : char *dest = NULL;
55 : char locsep[MAX_SEP_LEN + 1];
56 : uint32_t lensep;
57 : const char *buff;
58 20 : uint32_t count = 0;
59 20 : uint32_t len = 0;
60 : uint32_t resume_len;
61 : char **array;
62 : const char *start;
63 : char *start_array;
64 : uint32_t i, j;
65 : uint32_t dlen;
66 :
67 : TRACE_FLOW_ENTRY();
68 :
69 : /* Do we have the vo ? */
70 20 : if (vo == NULL) {
71 : TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
72 0 : if (error) *error = EINVAL;
73 : return NULL;
74 : }
75 :
76 : /* Get value and length - no error checking as we checked it above
77 : * and there is no other reson the function could to fail.
78 : */
79 20 : value_get_concatenated(vo, &buff);
80 20 : value_get_concatenated_len(vo, &dlen);
81 :
82 : /* Handle the separators */
83 20 : if (sep == NULL) {
84 0 : locsep[0] = ',';
85 0 : locsep[1] = '\0';
86 0 : lensep = 2;
87 : }
88 : else {
89 20 : strncpy(locsep, sep, MAX_SEP_LEN);
90 20 : locsep[MAX_SEP_LEN] = '\0';
91 20 : lensep = strlen(locsep) + 1;
92 : }
93 :
94 : /* Allocate memory for the copy of the string */
95 : TRACE_INFO_NUMBER("Length to allocate is :", dlen);
96 : /* Always reserve one more byte
97 : * for the case when the string consist of delimeters */
98 20 : copy = malloc(dlen + 1);
99 20 : if (copy == NULL) {
100 : TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
101 0 : if (error) *error = ENOMEM;
102 : return NULL;
103 : }
104 :
105 : /* Suppress warning */
106 20 : start = buff;
107 :
108 : /* Loop through the string */
109 20 : dest = copy;
110 319 : for(i = 0; i < dlen; i++) {
111 638 : for(j = 0; j < lensep; j++) {
112 715 : if(buff[i] == locsep[j]) {
113 : /* If we found one of the separators trim spaces around */
114 : resume_len = len;
115 101 : while (len > 0) {
116 48 : if (isspace(start[len - 1])) len--;
117 : else break;
118 : }
119 : TRACE_INFO_STRING("Current:", start);
120 : TRACE_INFO_NUMBER("Length:", len);
121 77 : if (len > 0) {
122 : /* Save block aside */
123 24 : memcpy(dest, start, len);
124 24 : count++;
125 24 : dest += len;
126 24 : *dest = '\0';
127 24 : dest++;
128 : }
129 53 : else if(include) {
130 24 : count++;
131 24 : *dest = '\0';
132 24 : dest++;
133 : }
134 :
135 : /* Move forward and trim spaces if any */
136 77 : start += resume_len + 1;
137 77 : i++;
138 : TRACE_INFO_STRING("Other pointer :", buff + i);
139 327 : while ((i < dlen) && (isspace(*start))) {
140 173 : i++;
141 173 : start++;
142 : }
143 77 : len = -1; /* Len will be increased in the loop */
144 77 : i--; /* i will be increased so we need to step back */
145 : TRACE_INFO_STRING("Remaining buffer after triming spaces:",
146 : start);
147 77 : break;
148 : }
149 : }
150 299 : len++;
151 : }
152 :
153 : /* Save last segment */
154 : TRACE_INFO_STRING("Current:", start);
155 : TRACE_INFO_NUMBER("Length:", len);
156 20 : if (len > 0) {
157 : /* Save block aside */
158 2 : memcpy(dest, start, len);
159 2 : count++;
160 2 : dest += len;
161 2 : *dest = '\0';
162 : }
163 18 : else if(include && dlen && count) {
164 : TRACE_INFO_NUMBER("Include :", include);
165 : TRACE_INFO_NUMBER("dlen :", dlen);
166 : TRACE_INFO_NUMBER("Count :", count);
167 7 : count++;
168 7 : *dest = '\0';
169 : }
170 :
171 : /* Now we know how many items are there in the list */
172 20 : array = malloc((count + 1) * sizeof(char *));
173 20 : if (array == NULL) {
174 0 : free(copy);
175 : TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
176 0 : if (error) *error = ENOMEM;
177 : return NULL;
178 : }
179 :
180 : /* Loop again to fill in the pointers */
181 : start_array = copy;
182 57 : for (i = 0; i < count; i++) {
183 : TRACE_INFO_STRING("Token :", start_array);
184 : TRACE_INFO_NUMBER("Item :", i);
185 57 : array[i] = start_array;
186 : /* Move to next item */
187 57 : while(*start_array) start_array++;
188 57 : start_array++;
189 : }
190 20 : array[count] = NULL;
191 :
192 20 : if (error) *error = EOK;
193 20 : if (size) *size = count;
194 : /* If count is 0 the copy needs to be freed */
195 20 : if (count == 0) free(copy);
196 :
197 : TRACE_FLOW_EXIT();
198 20 : return array;
199 : }
200 :
201 : /* Get array of strings from item eliminating empty tokens */
202 11 : char **ini_get_string_config_array(struct value_obj *vo,
203 : const char *sep, int *size, int *error)
204 : {
205 : TRACE_FLOW_ENTRY();
206 11 : return get_str_cfg_array(vo, EXCLUDE_EMPTY, sep, size, error);
207 : }
208 : /* Get array of strings from item preserving empty tokens */
209 9 : char **ini_get_raw_string_config_array(struct value_obj *vo,
210 : const char *sep, int *size, int *error)
211 : {
212 : TRACE_FLOW_ENTRY();
213 9 : return get_str_cfg_array(vo, INCLUDE_EMPTY, sep, size, error);
214 : }
215 :
216 : /* Special function to free string config array */
217 20 : void ini_free_string_config_array(char **str_config)
218 : {
219 : TRACE_FLOW_ENTRY();
220 :
221 20 : if (str_config != NULL) {
222 20 : if (*str_config != NULL) free(*str_config);
223 20 : free(str_config);
224 : }
225 :
226 : TRACE_FLOW_EXIT();
227 20 : }
228 :
229 : /* Get an array of long values.
230 : * NOTE: For now I leave just one function that returns numeric arrays.
231 : * In future if we need other numeric types we can change it to do strtoll
232 : * internally and wrap it for backward compatibility.
233 : */
234 1 : long *ini_get_long_config_array(struct value_obj *vo, int *size, int *error)
235 : {
236 : const char *str;
237 : char *endptr;
238 1 : long val = 0;
239 : long *array;
240 1 : uint32_t count = 0;
241 : int err;
242 : uint32_t dlen;
243 :
244 : TRACE_FLOW_ENTRY();
245 :
246 : /* Do we have the vo ? */
247 1 : if (vo == NULL) {
248 : TRACE_ERROR_NUMBER("Invalid value object argument.", EINVAL);
249 0 : if (error) *error = EINVAL;
250 : return NULL;
251 : }
252 :
253 : /* Do we have the size ? */
254 1 : if (size == NULL) {
255 : TRACE_ERROR_NUMBER("Invalid size argument.", EINVAL);
256 0 : if (error) *error = EINVAL;
257 : return NULL;
258 : }
259 :
260 : /* Get value and length - no error checking as we checked it above
261 : * and there is no other reson the function could to fail.
262 : */
263 1 : value_get_concatenated(vo, &str);
264 1 : value_get_concatenated_len(vo, &dlen);
265 :
266 : /* Assume that we have maximum number of different numbers */
267 1 : array = (long *)malloc(sizeof(long) * dlen/2);
268 1 : if (array == NULL) {
269 : TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
270 0 : if (error) *error = ENOMEM;
271 : return NULL;
272 : }
273 :
274 : /* Now parse the string */
275 7 : while (*str) {
276 :
277 6 : errno = 0;
278 6 : val = strtol(str, &endptr, 10);
279 6 : err = errno;
280 :
281 6 : if (err) {
282 : TRACE_ERROR_NUMBER("Conversion failed", err);
283 0 : free(array);
284 0 : if (error) *error = err;
285 : return NULL;
286 : }
287 :
288 6 : if (endptr == str) {
289 : TRACE_ERROR_NUMBER("Nothing processed", EIO);
290 0 : free(array);
291 0 : if (error) *error = EIO;
292 : return NULL;
293 : }
294 :
295 : /* Save value */
296 6 : array[count] = val;
297 6 : count++;
298 : /* Are we done? */
299 6 : if (*endptr == 0) break;
300 : /* Advance to the next valid number */
301 19 : for (str = endptr; *str; str++) {
302 18 : if (isdigit(*str) || (*str == '-') || (*str == '+')) break;
303 : }
304 : }
305 :
306 1 : *size = count;
307 1 : if (error) *error = EOK;
308 :
309 : TRACE_FLOW_EXIT();
310 1 : return array;
311 :
312 : }
313 :
314 : /* Get an array of double values */
315 1 : double *ini_get_double_config_array(struct value_obj *vo, int *size, int *error)
316 : {
317 : const char *str;
318 : char *endptr;
319 1 : double val = 0;
320 : double *array;
321 1 : int count = 0;
322 : struct lconv *loc;
323 : uint32_t dlen;
324 :
325 : TRACE_FLOW_ENTRY();
326 :
327 : /* Do we have the vo ? */
328 1 : if (vo == NULL) {
329 : TRACE_ERROR_NUMBER("Invalid value object argument.", EINVAL);
330 0 : if (error) *error = EINVAL;
331 : return NULL;
332 : }
333 :
334 : /* Do we have the size ? */
335 1 : if (size == NULL) {
336 : TRACE_ERROR_NUMBER("Invalid size argument.", EINVAL);
337 0 : if (error) *error = EINVAL;
338 : return NULL;
339 : }
340 :
341 : /* Get value and length - no error checking as we checked it above
342 : * and there is no other reson the function could to fail.
343 : */
344 1 : value_get_concatenated(vo, &str);
345 1 : value_get_concatenated_len(vo, &dlen);
346 :
347 :
348 : /* Assume that we have maximum number of different numbers */
349 1 : array = (double *)malloc(sizeof(double) * dlen/2);
350 1 : if (array == NULL) {
351 : TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
352 0 : if (error) *error = ENOMEM;
353 : return NULL;
354 : }
355 :
356 : /* Get locale information so that we can check for decimal point character.
357 : * Based on the man pages it is unclear if this is an allocated memory or not.
358 : * Seems like it is a static thread or process local structure so
359 : * I will not try to free it after use.
360 : */
361 1 : loc = localeconv();
362 :
363 : /* Now parse the string */
364 8 : while (*str) {
365 : TRACE_INFO_STRING("String to convert",str);
366 6 : errno = 0;
367 6 : val = strtod(str, &endptr);
368 6 : if ((errno == ERANGE) ||
369 6 : ((errno != 0) && (val == 0)) ||
370 6 : (endptr == str)) {
371 : TRACE_ERROR_NUMBER("Conversion failed", EIO);
372 0 : free(array);
373 0 : if (error) *error = EIO;
374 : return NULL;
375 : }
376 : /* Save value */
377 6 : array[count] = val;
378 6 : count++;
379 : /* Are we done? */
380 6 : if (*endptr == 0) break;
381 : TRACE_INFO_STRING("End pointer after conversion",endptr);
382 : /* Advance to the next valid number */
383 20 : for (str = endptr; *str; str++) {
384 35 : if (isdigit(*str) || (*str == '-') || (*str == '+') ||
385 : /* It is ok to do this since the string is null terminated */
386 19 : ((*str == *(loc->decimal_point)) && isdigit(str[1]))) break;
387 : }
388 : }
389 :
390 1 : *size = count;
391 1 : if (error) *error = EOK;
392 :
393 : TRACE_FLOW_EXIT();
394 1 : return array;
395 :
396 : }
397 :
398 : /* Special function to free long config array */
399 1 : void ini_free_long_config_array(long *array)
400 : {
401 1 : free(array);
402 1 : }
403 :
404 : /* Special function to free double config array */
405 1 : void ini_free_double_config_array(double *array)
406 : {
407 1 : free(array);
408 1 : }
|