1 /***************************************************************************
2 params.cpp -- configuration parameters management
3 -------------------
4 created : Fri Aug 13 22:27:57 CEST 1999
5 copyright : (C) 1999-2014 by Eric Espie, Bernhard Wymann
6 email : torcs@free.fr
7 version : $Id: params.cpp,v 1.30.2.26 2014/05/23 08:38:31 berniw Exp $
8 ***************************************************************************/
9
10 /***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19 /** @file
20 Parameter handling API
21 @author Bernhard Wymann, Eric Espie
22 @version $Id: params.cpp,v 1.30.2.26 2014/05/23 08:38:31 berniw Exp $
23 */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <sys/stat.h>
28 #ifndef _WIN32
29 #include <unistd.h>
30 #endif
31 #include <math.h>
32
33 #include <xmlparse.h>
34 #ifdef WIN32
35 #include <windows.h>
36 #endif
37 #include <tgf.h>
38 #include <assert.h>
39 #include <portability.h>
40
41
42 #define LINE_SZ 1024
43 #define PARAM_CREATE 0x01
44
45 #define P_NUM 0
46 #define P_STR 1
47
48 /** @brief Structure to hold linked list of within options */
49 struct within
50 {
51 char *val; /**< Value of within option */
52 GF_TAILQ_ENTRY (struct within) linkWithin; /**< Link to next entry */
53 };
54
55 GF_TAILQ_HEAD (withinHead, struct within);
56
57
58
59 /** Parameter header structure, a parameter can either carry a numeric or a string value,
60 * numeric value is constraint by min and max, string value by options in within
61 * @see GfParmUnit2SI
62 */
63 struct param
64 {
65 char *name; /**< Name of the parameter */
66 char *fullName; /**< Name of the parameter including the full section name ('/' separated) */
67 char *value; /**< String value of the parameter */
68 tdble valnum; /**< Numeric value of the parameter */
69 int type; /**< Type, either @ref P_NUM or @ref P_STR */
70 char *unit; /**< Unit, see @ref GfParmUnit2SI for supported units */
71 tdble min; /**< Minimum for numeric value */
72 tdble max; /**< Maximum for numeric value */
73 struct withinHead withinList; /**< Linked list containing the options for legal string values */
74 GF_TAILQ_ENTRY (struct param) linkParam; /**< Next parameter in the same section */
75 };
76
77 GF_TAILQ_HEAD (paramHead, struct param);
78 struct section;
79 GF_TAILQ_HEAD (sectionHead, struct section);
80
81
82 /** Section header structure */
83 struct section
84 {
85 char *fullName; /**< Name of the section including full path ('/' separated) */
86 struct paramHead paramList; /**< List of the parameters in this section */
87 GF_TAILQ_ENTRY (struct section) linkSection; /**< Next section at the same level */
88 struct sectionHead subSectionList; /**< List of sub-sections (linked by linkSection) */
89 struct section *curSubSection; /**< Current subsection, for iterations, see @ref GfParmListSeekFirst and @ref GfParmListSeekNext */
90 struct section *parent; /**< Parent section */
91 };
92
93
94 #define PARM_MAGIC 0x20030815
95
96 /** Configuration header structure */
97 struct parmHeader
98 {
99 char *filename; /**< Name of the configuration file */
100 char *name; /**< Name of the data */
101 char *dtd; /**< Optional DTD location */
102 char *header; /**< Optional header (comment, xsl...) */
103 int refcount; /**< Use counter (number of conf handle) */
104 struct section *rootSection; /**< List of sections at the first level */
105 void *paramHash; /**< Hash table for parameter access */
106 void *sectionHash; /**< Hash table for section access */
107 };
108
109 #define PARM_HANDLE_FLAG_PRIVATE 0x01
110 #define PARM_HANDLE_FLAG_PARSE_ERROR 0x02
111
112
113 /** Ouput control structure used for serializing parameter set into XML*/
114 struct parmOutput
115 {
116 int state; /**< Internal state */
117 struct section *curSection; /**< Current section */
118 struct param *curParam; /**< Current parameter */
119 char *filename; /**< Name of the output file */
120 int indent; /**< Keep track of indentation */
121 };
122
123
124 /** Parameter set handle structure, multiple handles can reference the same parameter set */
125 struct parmHandle
126 {
127 int magic; /**< Magic number (to detect wrong type casts and such) */
128 struct parmHeader *conf; /**< Header of the parameter set */
129 int flag; /**< Flag (@ref PARM_HANDLE_FLAG_PARSE_ERROR, @ref PARM_HANDLE_FLAG_PRIVATE) */
130 XML_Parser parser; /**< Parser */
131 struct section *curSection; /**< Current section, for iterations, see @ref GfParmListSeekFirst and @ref GfParmListSeekNext */
132 struct parmOutput outCtrl; /**< Ouput control structure used for serializing parameter set into XML */
133 GF_TAILQ_ENTRY (struct parmHandle) linkHandle; /**< Next configuration handle */
134 };
135
136
137 GF_TAILQ_HEAD (parmHead, struct parmHandle);
138
139 static struct parmHead parmHandleList;
140
141 static char *getFullName(const char *sectionName, const char *paramName);
142 static struct param *getParamByName (struct parmHeader *conf, const char *sectionName, const char *paramName, int flag);
143 static void removeParamByName (struct parmHeader *conf, const char *sectionName, const char *paramName);
144 static void removeParam (struct parmHeader *conf, struct section *section, struct param *param);
145 static struct param *addParam (struct parmHeader *conf, struct section *section, const char *paramName, const char *value);
146 static void removeSection (struct parmHeader *conf, struct section *section);
147 static struct section *addSection (struct parmHeader *conf, const char *sectionName);
148 static void parmClean (struct parmHeader *conf);
149 static void parmReleaseHandle (struct parmHandle *parmHandle);
150 static void parmReleaseHeader (struct parmHeader *conf);
151 static struct section *getParent (struct parmHeader *conf, const char *sectionName);
152 static void cleanUnusedSection (struct parmHeader *conf, struct section *section);
153
154
155 /** @brief Parameter set library API initialization, set up parameter set handle cache.
156 * @ingroup conf
157 */
GfParmInit(void)158 void GfParmInit (void)
159 {
160 GF_TAILQ_INIT (&parmHandleList);
161 }
162
163
164 /** @brief Parameter set library API shutdown, removes parameter set handle cache.
165 * @ingroup conf
166 */
GfParmShutdown(void)167 void GfParmShutdown (void)
168 {
169 struct parmHandle *parmHandle;
170
171 while ((parmHandle = GF_TAILQ_FIRST (&parmHandleList)) != GF_TAILQ_END (&parmHandleList)) {
172 parmReleaseHandle (parmHandle);
173 }
174 }
175
176
177 /** @brief Helper function to get the full name of a parameter (full name: "sectionName/paramName").
178 *
179 * @ingroup paramshelper
180 * @param[in] sectionName name of the section containing the parameter
181 * @param[in] paramName name of the parameter
182 * @return string
183 * <br>NULL on error
184 * @note Heap memory is allocated for the return value, so the caller is responsible for releasing the
185 * memory of the returned string.
186 */
getFullName(const char * sectionName,const char * paramName)187 static char *getFullName (const char *sectionName, const char *paramName)
188 {
189 char *fullName;
190 unsigned long len = strlen (sectionName) + strlen (paramName) + 2;
191
192 fullName = (char *) malloc(strlen (sectionName) + strlen (paramName) + 2);
193 if (!fullName) {
194 GfError ("getFullName: malloc (%lu) failed", len);
195 return NULL;
196 }
197 snprintf(fullName, len, "%s/%s", sectionName, paramName);
198
199 return fullName;
200 }
201
202
203 /** @brief Helper function to get (or create) a parameter by name.
204 *
205 * @ingroup paramshelper
206 * @param[in,out] conf parameter set header
207 * @param[in] sectionName name of the section containing the parameter
208 * @param[in] paramName name of the parameter
209 * @param[in] flag if in flag the @ref PARAM_CREATE bit is set the parameter gets created if it is not found
210 * @return param
211 * <br>NULL on error or not found
212 */
getParamByName(struct parmHeader * conf,const char * sectionName,const char * paramName,int flag)213 static struct param* getParamByName(struct parmHeader *conf, const char *sectionName, const char *paramName, int flag)
214 {
215 char *fullName;
216 struct param *param;
217 struct section *section;
218
219 fullName = getFullName (sectionName, paramName);
220 if (!fullName) {
221 GfError ("getParamByName: getFullName failed\n");
222 return NULL;
223 }
224
225 param = (struct param *)GfHashGetStr (conf->paramHash, fullName);
226 free (fullName);
227 if (param || ((flag & PARAM_CREATE) == 0)) {
228 return param;
229 }
230
231 /* Parameter not found CREATE it */
232 section = (struct section *)GfHashGetStr (conf->sectionHash, sectionName);
233 if (!section) {
234 section = addSection (conf, sectionName);
235 if (!section) {
236 GfError ("getParamByName: addSection failed\n");
237 return NULL;
238 }
239 }
240 param = addParam (conf, section, paramName, "");
241
242 return param;
243 }
244
245
246 /** @brief Helper function to remove a parameter with given name @e paramName.
247 *
248 * @ingroup paramshelper
249 * @param[in,out] conf parameter set header
250 * @param[in] sectionName name of the section containing the parameter
251 * @param[in] paramName name of the parameter
252 * @note @ref cleanUnusedSection is called after removing the parameter.
253 */
removeParamByName(struct parmHeader * conf,const char * sectionName,const char * paramName)254 static void removeParamByName(struct parmHeader *conf, const char *sectionName, const char *paramName)
255 {
256 char *fullName;
257 struct param *param;
258 struct section *section;
259
260 section = (struct section *)GfHashGetStr (conf->sectionHash, sectionName);
261 if (!section) {
262 return;
263 }
264
265 fullName = getFullName (sectionName, paramName);
266 if (!fullName) {
267 GfError("removeParamByName: getFullName failed\n");
268 return;
269 }
270
271 param = (struct param *)GfHashGetStr (conf->paramHash, fullName);
272 freez (fullName);
273 if (param) {
274 removeParam(conf, section, param);
275 }
276
277 cleanUnusedSection(conf, section);
278 }
279
280
281 /** @brief Helper function to clean up unused (empty) sections starting with given @e section.
282 *
283 * @ingroup paramshelper
284 * @param[in,out] conf parameter set header
285 * @param[in] section section to start up cleaning
286 * @note A section is unused if it does not contain subsections or elements, or if the fullName
287 * property is empty. If the given section is in use nothing is changed. If the section is
288 * unused it gets cleaned up and the process continues with the parent section (could now be
289 * empty as well).
290 */
cleanUnusedSection(struct parmHeader * conf,struct section * section)291 static void cleanUnusedSection(struct parmHeader *conf, struct section *section)
292 {
293 struct section *parent;
294
295 if (
296 !section->fullName ||
297 (!GF_TAILQ_FIRST (&(section->paramList)) && !GF_TAILQ_FIRST (&(section->subSectionList)))
298 ) {
299 parent = section->parent;
300 removeSection (conf, section);
301 if (parent) {
302 /* check if the parent is unused */
303 cleanUnusedSection (conf, parent);
304 }
305 }
306 }
307
308
309 /** @brief Helper function to remove given parameter.
310 *
311 * @ingroup paramshelper
312 * @param[in,out] conf parameter set header
313 * @param[in,out] section section to remove parameter from
314 * @param[in] param parameter to remove
315 */
removeParam(struct parmHeader * conf,struct section * section,struct param * param)316 static void removeParam(struct parmHeader *conf, struct section *section, struct param *param)
317 {
318 GfHashRemStr (conf->paramHash, param->fullName);
319 GF_TAILQ_REMOVE (&(section->paramList), param, linkParam);
320
321 struct within *within;
322 while ((within = GF_TAILQ_FIRST (¶m->withinList)) != GF_TAILQ_END (¶m->withinList)) {
323 GF_TAILQ_REMOVE (¶m->withinList, within, linkWithin);
324 freez(within->val);
325 free(within);
326 }
327
328 freez (param->name);
329 freez (param->fullName);
330 freez (param->value);
331 freez (param->unit);
332 freez (param);
333 }
334
335
336 /** @brief Helper function to add parameter, does not check for duplicated name.
337 *
338 * @ingroup paramshelper
339 * @param[in,out] conf parameter set header
340 * @param[in,out] section section to add parameter to
341 * @param[in] paramName parameter name
342 * @param[in] value value of parameter
343 * @return param
344 * <br>NULL on error
345 */
addParam(struct parmHeader * conf,struct section * section,const char * paramName,const char * value)346 static struct param *addParam(struct parmHeader *conf, struct section *section, const char *paramName, const char *value)
347 {
348 char *fullName;
349 struct param *param = NULL;
350 char *tmpVal = NULL;
351 const unsigned long len = sizeof (struct param);
352
353 tmpVal = strdup (value);
354 if (!tmpVal) {
355 GfError ("addParam: strdup (%s) failed\n", value);
356 goto bailout;
357 }
358
359 param = (struct param *) calloc (1, len);
360 if (!param) {
361 GfError ("addParam: calloc (1, %lu) failed\n", len);
362 goto bailout;
363 }
364
365 param->name = strdup (paramName);
366 if (!param->name) {
367 GfError ("addParam: strdup (%s) failed\n", paramName);
368 goto bailout;
369 }
370
371 fullName = getFullName (section->fullName, paramName);
372 if (!fullName) {
373 GfError ("addParam: getFullName failed\n");
374 goto bailout;
375 }
376
377 param->fullName = fullName;
378 if (GfHashAddStr (conf->paramHash, param->fullName, param)) {
379 goto bailout;
380 }
381
382 GF_TAILQ_INIT (&(param->withinList));
383
384 /* Attach to section */
385 GF_TAILQ_INSERT_TAIL (&(section->paramList), param, linkParam);
386
387 freez (param->value);
388 param->value = tmpVal;
389
390 return param;
391
392 bailout:
393 if (param) {
394 freez (param->name);
395 freez (param->fullName);
396 freez (param->value);
397 free (param);
398 }
399 freez (tmpVal);
400
401 return NULL;
402 }
403
404
405 /** @brief Helper function to remove a section and its contents (subsections, elements).
406 *
407 * @ingroup paramshelper
408 * @param[in,out] conf parameter set header
409 * @param[in] section section to remove
410 */
removeSection(struct parmHeader * conf,struct section * section)411 static void removeSection(struct parmHeader *conf, struct section *section)
412 {
413 struct param *param;
414 struct section *subSection;
415
416 while ((subSection = GF_TAILQ_FIRST (&(section->subSectionList))) != NULL) {
417 removeSection (conf, subSection);
418 }
419
420 if (section->fullName) {
421 /* not the root section */
422 GfHashRemStr (conf->sectionHash, section->fullName);
423 GF_TAILQ_REMOVE (&(section->parent->subSectionList), section, linkSection);
424 while ((param = GF_TAILQ_FIRST (&(section->paramList))) != GF_TAILQ_END (&(section->paramList))) {
425 removeParam (conf, section, param);
426 }
427 freez (section->fullName);
428 }
429 freez (section);
430 }
431
432
433 /** @brief Helper function to get (or create if not found) parent section of section given in @e sectionName.
434 *
435 * @ingroup paramshelper
436 * @param[in] conf parameter set header
437 * @param[in] sectionName name of the section
438 * @return section
439 * <br>NULL on error
440 */
getParent(struct parmHeader * conf,const char * sectionName)441 static struct section *getParent(struct parmHeader *conf, const char *sectionName)
442 {
443 struct section *section;
444 char *tmpName;
445 char *s;
446
447 tmpName = strdup (sectionName);
448 if (!tmpName) {
449 GfError ("getParent: strdup (\"%s\") failed\n", sectionName);
450 return NULL;
451 }
452
453 s = strrchr (tmpName, '/');
454 if (s) {
455 *s = '\0';
456 section = (struct section *)GfHashGetStr (conf->sectionHash, tmpName);
457 if (section) {
458 goto end;
459 }
460 section = addSection (conf, tmpName);
461 goto end;
462 } else {
463 section = conf->rootSection;
464 goto end;
465 }
466
467 end:
468 free (tmpName);
469 return section;
470 }
471
472
473 /** @brief Helper function to add a section to a parameter set.
474 *
475 * @ingroup paramshelper
476 * @param[in,out] conf parameter set header
477 * @param[in] sectionName section name
478 * @return section on success
479 * <br>NULL on error
480 */
addSection(struct parmHeader * conf,const char * sectionName)481 static struct section *addSection(struct parmHeader *conf, const char *sectionName)
482 {
483 struct section *section;
484 struct section *parent;
485 const unsigned long len = sizeof (struct section);
486
487 if (GfHashGetStr (conf->sectionHash, sectionName)) {
488 GfError ("addSection: duplicate section [%s]\n", sectionName);
489 return NULL;
490 }
491
492 parent = getParent(conf, sectionName);
493 if (!parent) {
494 GfError ("addSection: Problem with getParent for section [%s]\n", sectionName);
495 return NULL;
496 }
497
498 section = (struct section *) calloc (1, len);
499 if (!section) {
500 GfError ("addSection: calloc (1, %lu) failed\n", len);
501 return NULL;
502 }
503
504 section->fullName = strdup(sectionName);
505 if (!section->fullName) {
506 GfError ("addSection: strdup (%s) failed\n", sectionName);
507 goto bailout;
508 }
509
510 if (GfHashAddStr (conf->sectionHash, sectionName, section)) {
511 GfError ("addSection: GfHashAddStr failed\n");
512 goto bailout;
513 }
514
515 /* no more bailout call */
516 section->parent = parent;
517 GF_TAILQ_INIT (&(section->paramList));
518 GF_TAILQ_INIT (&(section->subSectionList));
519 GF_TAILQ_INSERT_TAIL (&(parent->subSectionList), section, linkSection);
520
521 return section;
522
523 bailout:
524 freez (section->fullName);
525 freez (section);
526 return NULL;
527 }
528
529
530 /** @brief Helper function for looking up parameter sets in the cache.
531 *
532 * @ingroup paramshelper
533 * @param file name of the file to look up
534 * @param mode opening mode is a mask of:
535 * <br>#GFPARM_RMODE_STD if the parameter set is already loaded and not private return
536 * a handle pointing to the existing parameter set (default)
537 * <br>#GFPARM_RMODE_REREAD re-read the parameters file
538 * <br>#GFPARM_RMODE_CREAT if the parameters file does not exist return a handle
539 * pointing to an empty parameter set (does not create a file on disk,
540 * this is done using @ref GfParmWriteFile).
541 * <br>#GFPARM_RMODE_PRIVATE mark handle as private
542 * @return handle to parameter set
543 * <br>0 if not in cache
544 * @see GfParmReadFile
545 */
getSharedHeader(const char * file,int mode)546 static struct parmHeader *getSharedHeader(const char *file, int mode)
547 {
548 struct parmHeader *conf = NULL;
549 struct parmHandle *parmHandle;
550
551 /* Search for existing conf */
552 if ((mode & GFPARM_RMODE_PRIVATE) == 0) {
553 for (
554 parmHandle = GF_TAILQ_FIRST (&parmHandleList);
555 parmHandle != GF_TAILQ_END (&parmHandleList);
556 parmHandle = GF_TAILQ_NEXT (parmHandle, linkHandle)
557 ) {
558 if ((parmHandle->flag & PARM_HANDLE_FLAG_PRIVATE) == 0) {
559 conf = parmHandle->conf;
560 if (!strcmp(conf->filename, file)) {
561 if (mode & GFPARM_RMODE_REREAD) {
562 parmClean (conf);
563 }
564 conf->refcount++;
565 return conf;
566 }
567 }
568 }
569 }
570
571 return NULL;
572 }
573
574
575 /** @brief Helper function to create header for parameter set handle.
576 *
577 * @ingroup paramshelper
578 * @param[in] file filename
579 * @return parmHeader in case of success
580 * <br>NULL on error
581 */
createParmHeader(const char * file)582 static struct parmHeader* createParmHeader (const char *file)
583 {
584 struct parmHeader *conf = NULL;
585 const unsigned long parmheadersize = sizeof (struct parmHeader);
586 const unsigned long sectionsize = sizeof (struct section);
587
588 conf = (struct parmHeader *) calloc (1, parmheadersize);
589 if (!conf) {
590 GfError ("gfParmReadFile: calloc (1, %lu) failed\n", parmheadersize);
591 return NULL;
592 }
593
594 conf->refcount = 1;
595
596 conf->rootSection = (struct section *) calloc (1, sectionsize);
597 if (!conf->rootSection) {
598 GfError ("gfParmReadFile: calloc (1, %lu) failed\n", sectionsize);
599 goto bailout;
600 }
601
602 GF_TAILQ_INIT (&(conf->rootSection->paramList));
603 GF_TAILQ_INIT (&(conf->rootSection->subSectionList));
604
605 conf->paramHash = GfHashCreate (GF_HASH_TYPE_STR);
606 if (!conf->paramHash) {
607 GfError ("gfParmReadFile: GfHashCreate (paramHash) failed\n");
608 goto bailout;
609 }
610
611 conf->sectionHash = GfHashCreate (GF_HASH_TYPE_STR);
612 if (!conf->sectionHash) {
613 GfError ("gfParmReadFile: GfHashCreate (sectionHash) failed\n");
614 goto bailout;
615 }
616
617 conf->filename = strdup (file);
618 if (!conf->filename) {
619 GfError ("gfParmReadFile: strdup (%s) failed\n", file);
620 goto bailout;
621 }
622
623 return conf;
624
625 bailout:
626 freez (conf->rootSection);
627 if (conf->paramHash) {
628 GfHashRelease (conf->paramHash, NULL);
629 }
630
631 if (conf->sectionHash) {
632 GfHashRelease (conf->sectionHash, NULL);
633 }
634
635 freez (conf->filename);
636 freez (conf);
637
638 return NULL;
639 }
640
641
642 /** @brief Helper function to add "within" options to parameter @e curParam.
643 *
644 * @ingroup paramshelper
645 * @param[in] curParam parameter to add "within" option
646 * @param[in] s1 option string
647 */
addWithin(struct param * curParam,char * s1)648 static void addWithin (struct param *curParam, char *s1)
649 {
650 struct within *curWithin;
651
652 if (!s1 || ! strlen (s1)) {
653 return;
654 }
655
656 curWithin = (struct within *) calloc (1, sizeof (struct within));
657 curWithin->val = strdup (s1);
658 GF_TAILQ_INSERT_TAIL (&(curParam->withinList), curWithin, linkWithin);
659 }
660
661
myStrcmp(const void * s1,const void * s2)662 static int myStrcmp(const void *s1, const void * s2)
663 {
664 return strcmp((const char *)s1, (const char *)s2);
665 }
666
667
668 /** @brief Helper function to parse number.
669 *
670 * @ingroup paramshelper
671 * @param[in] str number as string
672 * @return number
673 */
getValNumFromStr(const char * str)674 static tdble getValNumFromStr (const char *str)
675 {
676 tdble val;
677
678 if (!str || !strlen (str)) {
679 return 0.0;
680 }
681
682 if (strncmp (str, "0x", 2) == 0) {
683 return (tdble)strtol(str, NULL, 0);
684 }
685
686 sscanf (str, "%g", &val);
687 return val;
688 }
689
690
691 /** @brief Helper function to process opening XML elements.
692 *
693 * @ingroup paramshelper
694 * @param[in,out] userData handle to parameter set to read data into
695 * @param[in] name name of the current XML element
696 * @param[in] atts attributes of the XML element
697 */
xmlStartElement(void * userData,const char * name,const char ** atts)698 static void xmlStartElement (void *userData , const char *name, const char **atts)
699 {
700 struct parmHandle *parmHandle = (struct parmHandle *)userData;
701 struct parmHeader *conf = parmHandle->conf;
702 struct param *curParam;
703
704 int nAtts;
705 int len;
706 const char **p;
707 const char *s1, *s2;
708 char *fullName;
709 const char *shortName;
710 const char *val;
711 const char *min;
712 const char *max;
713 const char *unit;
714 char *within;
715 char *sa, *sb;
716
717 if (parmHandle->flag & PARM_HANDLE_FLAG_PARSE_ERROR) {
718 // parse error occured, ignore.
719 return;
720 }
721
722 p = atts;
723 while (*p) {
724 ++p;
725 }
726
727 nAtts = (p - atts) >> 1;
728 if (nAtts > 1) {
729 qsort ((void *)atts, nAtts, sizeof(char *) * 2, myStrcmp);
730 }
731
732 if (!strcmp(name, "params")) {
733
734 parmHandle->curSection = conf->rootSection;
735 parmHandle->curSection->fullName = strdup ("");
736
737 if (!parmHandle->curSection->fullName) {
738 GfError ("xmlStartElement: strdup (\"\") failed\n");
739 goto bailout;
740 }
741
742 while (*atts) {
743 s1 = *atts++;
744 s2 = *atts++;
745 if (!strcmp(s1, "name")) {
746 FREEZ (conf->name);
747 conf->name = strdup(s2);
748 if (!conf->name) {
749 GfError ("xmlStartElement: strdup (\"%s\") failed\n", s2);
750 goto bailout;
751 }
752 break;
753 }
754 }
755
756 if (!conf->name) {
757 GfOut ("xmlStartElement: Syntax error, missing \"name\" field in params definition\n");
758 goto bailout;
759 }
760
761 } else if (!strcmp(name, "section")) {
762
763 if (!parmHandle->curSection) {
764 GfError ("xmlStartElement: Syntax error, missing \"params\" tag\n");
765 goto bailout;
766 }
767
768 shortName = NULL;
769
770 while (*atts) {
771 s1 = *atts++;
772 s2 = *atts++;
773 if (!strcmp(s1, "name")) {
774 shortName = s2;
775 break;
776 }
777 }
778
779 if (!shortName) {
780 GfError ("xmlStartElement: Syntax error, missing \"name\" field in section definition\n");
781 goto bailout;
782 }
783
784 if (strlen(parmHandle->curSection->fullName)) {
785 len = strlen (shortName) + strlen (parmHandle->curSection->fullName) + 2;
786 fullName = (char *) malloc (len);
787 if (!fullName) {
788 GfError ("xmlStartElement: malloc (%d) failed\n", len);
789 goto bailout;
790 }
791 snprintf (fullName, len, "%s/%s", parmHandle->curSection->fullName, shortName);
792 } else {
793 fullName = strdup (shortName);
794 }
795
796 parmHandle->curSection = addSection(conf, fullName);
797 free(fullName);
798
799 if (!parmHandle->curSection) {
800 GfError ("xmlStartElement: addSection failed\n");
801 goto bailout;
802 }
803
804 } else if (!strcmp(name, "attnum")) {
805
806 if ((!parmHandle->curSection) || (!strlen (parmHandle->curSection->fullName))) {
807 GfError ("xmlStartElement: Syntax error, missing \"section\" tag\n");
808 goto bailout;
809 }
810
811 shortName = NULL;
812 val = NULL;
813 min = max = unit = NULL;
814
815 while (*atts) {
816 s1 = *atts++;
817 s2 = *atts++;
818 if (!strcmp(s1, "name")) {
819 shortName = s2;
820 } else if (!strcmp(s1, "val")) {
821 val = s2;
822 } else if (!strcmp(s1, "min")) {
823 min = s2;
824 } else if (!strcmp(s1, "max")) {
825 max = s2;
826 } else if (!strcmp(s1, "unit")) {
827 unit = s2;
828 }
829 }
830
831 if (!shortName) {
832 GfError ("xmlStartElement: Syntax error, missing \"name\" field in %s definition\n", name);
833 goto bailout;
834 }
835
836 if (!val) {
837 GfError ("xmlStartElement: Syntax error, missing \"val\" field in %s definition\n", name);
838 goto bailout;
839 }
840
841 if (!min) {
842 min = val;
843 }
844
845 if (!max) {
846 max = val;
847 }
848
849 curParam = addParam (conf, parmHandle->curSection, shortName, val);
850 if (!curParam) {
851 GfError ("xmlStartElement: addParam failed\n");
852 goto bailout;
853 }
854
855 curParam->type = P_NUM;
856 curParam->valnum = getValNumFromStr (val);
857 curParam->min = getValNumFromStr (min);
858 curParam->max = getValNumFromStr (max);
859
860 if (curParam->min > curParam->valnum) {
861 curParam->min = curParam->valnum;
862 }
863
864 if (curParam->max < curParam->valnum) {
865 curParam->max = curParam->valnum;
866 }
867
868 if (unit) {
869 curParam->unit = strdup (unit);
870 curParam->valnum = GfParmUnit2SI ((char*)unit, curParam->valnum);
871 curParam->min = GfParmUnit2SI ((char*)unit, curParam->min);
872 curParam->max = GfParmUnit2SI ((char*)unit, curParam->max);
873 }
874
875 } else if (!strcmp(name, "attstr")) {
876
877 if ((!parmHandle->curSection) || (!strlen (parmHandle->curSection->fullName))) {
878 GfError ("xmlStartElement: Syntax error, missing \"section\" tag\n");
879 goto bailout;
880 }
881
882 shortName = NULL;
883 val = NULL;
884 within = NULL;
885
886 while (*atts) {
887 s1 = *atts++;
888 s2 = *atts++;
889 if (!strcmp(s1, "name")) {
890 shortName = s2;
891 } else if (!strcmp(s1, "val")) {
892 val = s2;
893 } else if (!strcmp(s1, "in")) {
894 within = (char *)s2;
895 }
896 }
897
898 if (!shortName) {
899 GfError ("xmlStartElement: Syntax error, missing \"name\" field in %s definition\n", name);
900 goto bailout;
901 }
902
903 if (!val) {
904 GfError ("xmlStartElement: Syntax error, missing \"val\" field in %s definition\n", name);
905 goto bailout;
906 }
907
908 curParam = addParam (conf, parmHandle->curSection, shortName, val);
909 if (!curParam) {
910 GfError ("xmlStartElement: addParam failed\n");
911 goto bailout;
912 }
913
914 curParam->type = P_STR;
915 if (within) {
916 sa = within;
917 sb = strchr (sa, ',');
918 while (sb) {
919 *sb = 0;
920 addWithin (curParam, sa);
921 sa = sb + 1;
922 sb = strchr (sa, ',');
923 }
924 addWithin (curParam, sa);
925 }
926
927 }
928
929 return;
930
931 bailout:
932 parmHandle->flag |= PARM_HANDLE_FLAG_PARSE_ERROR;
933 return;
934 }
935
936
937 /** @brief Helper function to process closing XML elements.
938 *
939 * @ingroup paramshelper
940 * @param[in,out] userData handle to parameter set to read data into
941 * @param[in] name name of the current XML element
942 */
xmlEndElement(void * userData,const XML_Char * name)943 static void xmlEndElement (void *userData, const XML_Char *name)
944 {
945 struct parmHandle *parmHandle = (struct parmHandle *)userData;
946
947 if (parmHandle->flag & PARM_HANDLE_FLAG_PARSE_ERROR) {
948 /* parse error occured, ignore */
949 return;
950 }
951
952 if (!strcmp(name, "section")) {
953 if ((!parmHandle->curSection) || (!parmHandle->curSection->parent)) {
954 GfError ("xmlEndElement: Syntax error in \"%s\"\n", name);
955 return;
956 }
957 parmHandle->curSection = parmHandle->curSection->parent;
958 }
959 }
960
961
962 /** @brief Helper function to handle external XML entities (XML referencing over multiple files/URI's).
963 *
964 * @ingroup paramshelper
965 * @param[in] mainparser parent XML parser
966 * @param[in] openEntityNames space separated list of names of entities that are open for the parse of this entity
967 * @param[in] base unused (base path for resolving system id)
968 * @param[in] systemId path to external entity (SYSTEM in XML)
969 * @param[in] publicId unused (public identifier of external entity, PUBLIC in XML)
970 * @return 1 ok
971 * <br>0 error
972 */
xmlExternalEntityRefHandler(XML_Parser mainparser,const XML_Char * openEntityNames,const XML_Char * base,const XML_Char * systemId,const XML_Char * publicId)973 static int xmlExternalEntityRefHandler(
974 XML_Parser mainparser,
975 const XML_Char *openEntityNames,
976 const XML_Char *base,
977 const XML_Char *systemId,
978 const XML_Char *publicId)
979 {
980 FILE *in;
981 char buf[BUFSIZ];
982 XML_Parser parser;
983 int done;
984 char fin[LINE_SZ];
985 char *s;
986 struct parmHandle *parmHandle;
987 struct parmHeader *conf;
988
989 parmHandle = (struct parmHandle *)XML_GetUserData (mainparser);
990 conf = parmHandle->conf;
991
992 parser = XML_ExternalEntityParserCreate (mainparser, openEntityNames, (const XML_Char *)NULL);
993
994 if (systemId[0] == '/') {
995 strncpy (fin, systemId, sizeof (fin));
996 fin[LINE_SZ - 1] = 0;
997 } else {
998 /* relative path */
999 strncpy (fin, conf->filename, sizeof (fin));
1000 fin[LINE_SZ - 1] = 0;
1001 s = strrchr (fin, '/');
1002 if (s) {
1003 s++;
1004 } else {
1005 s = fin;
1006 }
1007 strncpy (s, systemId, sizeof (fin) - (s - fin));
1008 fin[LINE_SZ - 1] = 0;
1009 }
1010
1011 in = fopen (fin, "r");
1012 if (in == NULL) {
1013 perror (fin);
1014 GfError ("GfReadParmFile: file %s has pb\n", systemId);
1015 return 0;
1016 }
1017
1018 XML_SetElementHandler (parser, xmlStartElement, xmlEndElement);
1019 do {
1020 size_t len = fread (buf, 1, sizeof(buf), in);
1021 done = len < sizeof (buf);
1022 if (!XML_Parse (parser, buf, len, done)) {
1023 GfError ("file: %s -> %s at line %d\n",
1024 systemId,
1025 XML_ErrorString(XML_GetErrorCode(parser)),
1026 XML_GetCurrentLineNumber(parser));
1027 fclose (in);
1028 return 0;
1029 }
1030 } while (!done);
1031
1032 XML_ParserFree (parser);
1033 fclose(in);
1034
1035 return 1; /* ok (0 for failure) */
1036 }
1037
1038
1039 /** @brief Helper function to parse one line of XML.
1040 *
1041 * @ingroup paramshelper
1042 * @param[in,out] parmHandle parameter set handle
1043 * @param[in] buf line to parse
1044 * @param[in] len buffer size
1045 * @param[in] done this was the last slice, no more input available
1046 * @return 0 ok
1047 * <br>1 error
1048 */
parseXml(struct parmHandle * parmHandle,char * buf,int len,int done)1049 static int parseXml(struct parmHandle *parmHandle, char *buf, int len, int done)
1050 {
1051 if (!XML_Parse(parmHandle->parser, buf, len, done)) {
1052 GfError ("parseXml: %s at line %d\n",
1053 (char*)XML_ErrorString (XML_GetErrorCode (parmHandle->parser)),
1054 XML_GetCurrentLineNumber (parmHandle->parser));
1055 return 1;
1056 }
1057
1058 if (done) {
1059 XML_ParserFree(parmHandle->parser);
1060 parmHandle->parser = 0;
1061 }
1062
1063 return 0;
1064 }
1065
1066
1067 /** @brief Helper function to set up XML parser in @e parmHandle.
1068 *
1069 * @ingroup paramshelper
1070 * @param[in] parmHandle parameter set handle
1071 */
parserXmlInit(struct parmHandle * parmHandle)1072 static int parserXmlInit (struct parmHandle *parmHandle)
1073 {
1074 parmHandle->parser = XML_ParserCreate((XML_Char*)NULL);
1075 XML_SetElementHandler(parmHandle->parser, xmlStartElement, xmlEndElement);
1076 XML_SetExternalEntityRefHandler(parmHandle->parser, xmlExternalEntityRefHandler);
1077 XML_SetUserData(parmHandle->parser, parmHandle);
1078
1079 return 0;
1080 }
1081
1082
1083 /** @brief Read parameter set from memory buffer and return handle to parameter set.
1084 *
1085 * @ingroup paramsfile
1086 * @param[in] buffer buffer to read the configuration from
1087 * @return handle to the parameter set
1088 * <br>0 if Error
1089 * @see GfParmWriteBuf
1090 */
GfParmReadBuf(char * buffer)1091 void* GfParmReadBuf (char *buffer)
1092 {
1093 struct parmHeader *conf;
1094 struct parmHandle *parmHandle = NULL;
1095 const unsigned long parmhandlesize = sizeof (struct parmHandle);
1096
1097 /* Conf Header creation */
1098 conf = createParmHeader ("");
1099 if (!conf) {
1100 GfError ("gfParmReadBuf: conf header creation failed\n");
1101 goto bailout;
1102 }
1103
1104 /* Handle creation */
1105 parmHandle = (struct parmHandle *) calloc (1, parmhandlesize);
1106 if (!parmHandle) {
1107 GfError ("gfParmReadBuf: calloc (1, %lu) failed\n", parmhandlesize);
1108 goto bailout;
1109 }
1110
1111 parmHandle->magic = PARM_MAGIC;
1112 parmHandle->conf = conf;
1113 parmHandle->flag = PARM_HANDLE_FLAG_PRIVATE;
1114
1115 /* Parsers Initialization */
1116 if (parserXmlInit (parmHandle)) {
1117 GfError ("gfParmReadBuf: parserInit failed\n");
1118 goto bailout;
1119 }
1120
1121 /* Parameters reading in buffer */
1122 if (parseXml (parmHandle, buffer, strlen (buffer), 1)) {
1123 GfError ("gfParmReadBuf: Parse failed for buffer\n");
1124 goto bailout;
1125 }
1126
1127 GF_TAILQ_INSERT_HEAD (&parmHandleList, parmHandle, linkHandle);
1128
1129 return parmHandle;
1130
1131 bailout:
1132 freez (parmHandle);
1133 if (conf) {
1134 parmReleaseHeader (conf);
1135 }
1136
1137 return NULL;
1138 }
1139
1140
1141 /** @brief Read parameter set from file and return handle to parameter set.
1142 *
1143 * @ingroup paramsfile
1144 * @param[in] file name of the file to read
1145 * @param mode opening mode is a mask of:
1146 * <br>#GFPARM_RMODE_STD if the parameter set is already loaded and not private return
1147 * a handle pointing to the existing parameter set (default)
1148 * <br>#GFPARM_RMODE_REREAD re-read the parameters file
1149 * <br>#GFPARM_RMODE_CREAT if the parameters file does not exist return a handle
1150 * pointing to an empty parameter set (does not create a file on disk,
1151 * this is done using @ref GfParmWriteFile).
1152 * <br>#GFPARM_RMODE_PRIVATE mark handle as private
1153 * @return handle to parameter set
1154 * <br>0 if error
1155 * @see GfParmWriteFile
1156 */
GfParmReadFile(const char * file,int mode)1157 void* GfParmReadFile(const char *file, int mode)
1158 {
1159 FILE *in = NULL;
1160 struct parmHeader *conf;
1161 struct parmHandle *parmHandle = NULL;
1162 char buf[LINE_SZ];
1163 int len;
1164 int done;
1165 const unsigned long parmHandleSize = sizeof (struct parmHandle);
1166
1167 /* search for an already openned header & clean the conf if necessary */
1168 conf = getSharedHeader (file, mode);
1169
1170 /* Conf Header creation */
1171 if (conf == NULL) {
1172 conf = createParmHeader (file);
1173 if (!conf) {
1174 GfError ("gfParmReadFile: conf header creation failed\n");
1175 goto bailout;
1176 }
1177 mode |= GFPARM_RMODE_REREAD;
1178 }
1179
1180 /* Handle creation */
1181 parmHandle = (struct parmHandle *) calloc (1, parmHandleSize);
1182 if (!parmHandle) {
1183 GfError ("gfParmReadFile: calloc (1, %lu) failed\n", parmHandleSize);
1184 goto bailout;
1185 }
1186
1187 parmHandle->magic = PARM_MAGIC;
1188 parmHandle->conf = conf;
1189 if (mode & GFPARM_RMODE_PRIVATE) {
1190 parmHandle->flag = PARM_HANDLE_FLAG_PRIVATE;
1191 }
1192
1193 /* File opening */
1194 if (mode & GFPARM_RMODE_REREAD) {
1195 in = fopen (file, "r");
1196 if (!in && ((mode & GFPARM_RMODE_CREAT) == 0)) {
1197 GfOut ("gfParmReadFile: fopen \"%s\" failed\n", file);
1198 goto bailout;
1199 }
1200
1201 if (in) {
1202 /* Parsers Initialization */
1203 if (parserXmlInit (parmHandle)) {
1204 GfError ("gfParmReadBuf: parserInit failed for file \"%s\"\n", file);
1205 goto bailout;
1206 }
1207 /* Parameters reading */
1208 do {
1209 len = fread (buf, 1, sizeof(buf), in);
1210 done = len < (int)sizeof(buf);
1211 if (parseXml (parmHandle, buf, len, done)) {
1212 GfError ("gfParmReadFile: Parse failed in file \"%s\"\n", file);
1213 goto bailout;
1214 }
1215 if (parmHandle->flag & PARM_HANDLE_FLAG_PARSE_ERROR) {
1216 /* parse error occured, ignore */
1217 GfError ("gfParmReadFile: Parse failed in file \"%s\"\n", file);
1218 goto bailout;
1219 }
1220 } while (!done);
1221
1222 fclose (in);
1223 in = NULL;
1224 }
1225 }
1226
1227 GF_TAILQ_INSERT_HEAD (&parmHandleList, parmHandle, linkHandle);
1228
1229 GfOut ("GfParmReadFile: Opening \"%s\" (%p)\n", file, parmHandle);
1230
1231 return parmHandle;
1232
1233 bailout:
1234 if (in) {
1235 fclose (in);
1236 }
1237 freez (parmHandle);
1238 if (conf) {
1239 parmReleaseHeader (conf);
1240 }
1241
1242 return NULL;
1243 }
1244
1245
1246 /** @brief Helper function to convert the input line given in @e val into proper XML notation, the output goes into @e buf.
1247 *
1248 * @ingroup paramshelper
1249 * @param[in,out] buf buffer for the processed line
1250 * @param[in] BUFSIZE buffer size
1251 * @param[in] val input line
1252 * @return pointer to given buffer @e buf
1253 */
handleEntities(char * buf,const int BUFSIZE,const char * val)1254 static char* handleEntities(char *buf, const int BUFSIZE, const char* val)
1255 {
1256 int i = 0;
1257 int len = strlen(val);
1258 const char *replacement;
1259 char *pos = buf;
1260 int rlen;
1261
1262 for (i = 0; i < len; i++) {
1263 switch (val[i]) {
1264 case '<':
1265 replacement = "<"; break;
1266 case '>':
1267 replacement = ">"; break;
1268 case '&':
1269 replacement = "&"; break;
1270 case '\'':
1271 replacement = "'"; break;
1272 case '"':
1273 replacement = """; break;
1274 default:
1275 replacement = 0;
1276 }
1277
1278 if (replacement == 0) {
1279 replacement = &val[i];
1280 rlen = 1;
1281 } else {
1282 rlen = strlen(replacement);
1283 }
1284
1285 if (pos-buf < BUFSIZE - rlen) {
1286 memcpy(pos, replacement, rlen*sizeof(char));
1287 pos += rlen;
1288 } else {
1289 GfError("handleEntities: buffer too small to convert %s", val);
1290 break;
1291 }
1292 }
1293
1294 *pos = '\0';
1295
1296 return buf;
1297 }
1298
1299
1300 /** @brief Helper function for indentation in the XML.
1301 *
1302 * @ingroup paramshelper
1303 * @param[in,out] buf buffer for the result
1304 * @param[in] BUFSIZE buffer size
1305 * @param[in] blanks number of blanks to write
1306 */
createIndent(char * buf,const int BUFSIZE,const int blanks)1307 static void createIndent(char *buf, const int BUFSIZE, const int blanks)
1308 {
1309 int pos = 0;
1310 while ((pos < BUFSIZE - 1) && (pos < blanks)) {
1311 *buf++ = ' ';
1312 pos++;
1313 }
1314 *buf = '\0';
1315 }
1316
1317
1318 /** @brief Helper function to support the serialization into the XML of the "within" attribute.
1319 *
1320 * @ingroup paramshelper
1321 * @param[in,out] buf buffer for the result
1322 * @param[in] BUFSIZE buffer size
1323 * @param[in] head head of the list with the within options
1324 */
createIn(char * buf,const int BUFSIZE,withinHead * head)1325 static void createIn(char *buf, const int BUFSIZE, withinHead* head)
1326 {
1327 const char* s = " in=\"";
1328 struct within* curWithin = GF_TAILQ_FIRST(head);
1329 int pos = 0;
1330 bool separator = false;
1331 *buf = '\0'; // Terminate for empty content
1332
1333 while (curWithin != 0) {
1334 int len = strlen(s);
1335 if (pos < BUFSIZE - len - 1) {
1336 memcpy(buf, s, len*sizeof(char));
1337 buf += len;
1338 pos += len;
1339 } else {
1340 break;
1341 }
1342
1343 if (separator) {
1344 curWithin = GF_TAILQ_NEXT(curWithin, linkWithin);
1345 if (curWithin != 0) {
1346 s = ",";
1347 separator = false;
1348 }
1349 } else {
1350 s = curWithin->val;
1351 separator = true;
1352 }
1353 }
1354
1355 // Just terminate if we have written something
1356 if (pos > 0) {
1357 memcpy(buf, "\"", 2*sizeof(char));
1358 }
1359 }
1360
1361
1362 /** @brief Helper function to output one line of XML generated from the given parameter set.
1363 *
1364 * The parameter set handle @e parmHandle keeps track of the progress internally.
1365 *
1366 * @ingroup paramshelper
1367 * @param[in,out] parmHandle parameter set handle
1368 * @param[in,out] buffer buffer for the line
1369 * @param[in] size buffer size
1370 * @return 1 more lines available
1371 * <br>0 done
1372 */
xmlGetOuputLine(struct parmHandle * parmHandle,char * buffer,int size)1373 static int xmlGetOuputLine(struct parmHandle *parmHandle, char *buffer, int size)
1374 {
1375 struct parmOutput *outCtrl = &(parmHandle->outCtrl);
1376 struct parmHeader *conf = parmHandle->conf;
1377 struct section *curSection;
1378 struct param *curParam;
1379 char *s;
1380 const int BUFSIZE = 1024;
1381 char buf[BUFSIZE];
1382 const int INDENTSIZE = 1024;
1383 char indent[INDENTSIZE];
1384 const int INSIZE = 1024;
1385 char in[INSIZE];
1386 const int NUMVALUE = 1024;
1387 char numvalue[NUMVALUE];
1388
1389 while (1) {
1390 switch (outCtrl->state) {
1391 case 0:
1392 snprintf (buffer, size, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1393 outCtrl->indent = 0;
1394 outCtrl->state = 1;
1395 return 1;
1396
1397 case 1:
1398 if (conf->dtd == NULL) {
1399 conf->dtd = strdup("params.dtd");
1400 }
1401 if (conf->header == NULL) {
1402 conf->header = strdup("");
1403 }
1404 snprintf (buffer, size, "<!DOCTYPE params SYSTEM \"%s\">\n%s\n", conf->dtd, conf->header);
1405 outCtrl->indent = 0;
1406 outCtrl->state = 2;
1407 return 1;
1408
1409 case 2: /* Start Params */
1410 outCtrl->curSection = parmHandle->conf->rootSection;
1411 snprintf (buffer, size, "\n<params name=\"%s\">\n", parmHandle->conf->name);
1412 curSection = GF_TAILQ_FIRST (&(outCtrl->curSection->subSectionList));
1413 if (curSection) {
1414 outCtrl->curSection = curSection;
1415 outCtrl->indent += 2;
1416 outCtrl->state = 4;
1417 } else {
1418 outCtrl->state = 3;
1419 }
1420 return 1;
1421
1422 case 3: /* End Params */
1423 snprintf (buffer, size, "</params>\n");
1424 outCtrl->state = 9;
1425 return 1;
1426
1427 case 4: /* Parse section attributes list */
1428 outCtrl->curParam = GF_TAILQ_FIRST (&(outCtrl->curSection->paramList));
1429 s = strrchr (outCtrl->curSection->fullName, '/');
1430 if (!s) {
1431 s = outCtrl->curSection->fullName;
1432 } else {
1433 s++;
1434 }
1435
1436 createIndent(indent, INDENTSIZE, outCtrl->indent);
1437 handleEntities(buf, BUFSIZE, s);
1438 snprintf(buffer, size, "%s<section name=\"%s\">\n", indent, buf);
1439
1440 outCtrl->indent += 2;
1441 outCtrl->state = 5;
1442 return 1;
1443
1444 case 5: /* Parse one attribute */
1445 if (!outCtrl->curParam) {
1446 outCtrl->state = 6;
1447 break;
1448 }
1449
1450 curParam = outCtrl->curParam;
1451 if (curParam->type == P_STR) {
1452 createIndent(indent, INDENTSIZE, outCtrl->indent);
1453 createIn(in, INSIZE, &(curParam->withinList));
1454 handleEntities(buf, BUFSIZE, curParam->value);
1455 snprintf(buffer, size, "%s<attstr name=\"%s\"%s val=\"%s\"/>\n", indent, curParam->name, in, buf);
1456
1457 outCtrl->curParam = GF_TAILQ_NEXT (curParam, linkParam);
1458 return 1;
1459 } else {
1460 if (curParam->unit) {
1461 if ((curParam->min != curParam->valnum) || (curParam->max != curParam->valnum)) {
1462 snprintf(numvalue, NUMVALUE, " min=\"%g\" max=\"%g\" unit=\"%s\" val=\"%g\"/>\n",
1463 GfParmSI2Unit (curParam->unit, curParam->min),
1464 GfParmSI2Unit (curParam->unit, curParam->max),
1465 curParam->unit,
1466 GfParmSI2Unit (curParam->unit, curParam->valnum)
1467 );
1468 } else {
1469 snprintf(numvalue, NUMVALUE, " unit=\"%s\" val=\"%g\"/>\n",
1470 curParam->unit,
1471 GfParmSI2Unit (curParam->unit, curParam->valnum)
1472 );
1473 }
1474 } else {
1475 if ((curParam->min != curParam->valnum) || (curParam->max != curParam->valnum)) {
1476 snprintf (numvalue, NUMVALUE, " min=\"%g\" max=\"%g\" val=\"%g\"/>\n",
1477 curParam->min,
1478 curParam->max,
1479 curParam->valnum
1480 );
1481 } else {
1482 snprintf (numvalue, NUMVALUE, " val=\"%g\"/>\n", curParam->valnum);
1483 }
1484 }
1485
1486 createIndent(indent, INDENTSIZE, outCtrl->indent);
1487 snprintf (buffer, size, "%s<attnum name=\"%s\"%s", indent, curParam->name, numvalue);
1488
1489 outCtrl->curParam = GF_TAILQ_NEXT (curParam, linkParam);
1490 return 1;
1491 }
1492
1493 case 6: /* Parse sub-section list */
1494 curSection = GF_TAILQ_FIRST (&(outCtrl->curSection->subSectionList));
1495 if (curSection) {
1496 outCtrl->curSection = curSection;
1497 outCtrl->state = 4;
1498 break;
1499 }
1500 outCtrl->state = 7;
1501 break;
1502
1503 case 7: /* End Section */
1504 outCtrl->indent -= 2;
1505 createIndent(indent, INDENTSIZE, outCtrl->indent);
1506 snprintf (buffer, size, "%s</section>\n\n", indent);
1507 outCtrl->state = 8;
1508 return 1;
1509
1510 case 8: /* Parse next section at the same level */
1511 curSection = GF_TAILQ_NEXT (outCtrl->curSection, linkSection);
1512 if (curSection) {
1513 outCtrl->curSection = curSection;
1514 outCtrl->state = 4;
1515 break;
1516 }
1517 curSection = outCtrl->curSection->parent;
1518 outCtrl->indent -= 2;
1519 if (curSection->parent) {
1520 outCtrl->curSection = curSection;
1521 createIndent(indent, INDENTSIZE, outCtrl->indent);
1522 snprintf (buffer, size, "%s</section>\n\n", indent);
1523 return 1;
1524 }
1525 outCtrl->state = 3;
1526 break;
1527 case 9:
1528 return 0;
1529 }
1530 }
1531 }
1532
1533
1534 /** @brief Write a parameter set into a memory buffer.
1535 *
1536 * @ingroup paramsfile
1537 * @param[in] handle parameter set handle
1538 * @param[in,out] buf buffer to write the configuration to (must be big enough)
1539 * @param[in] size buffer size
1540 * @return 0 if ok
1541 * <br>1 if error
1542 */
GfParmWriteBuf(void * handle,char * buf,int size)1543 int GfParmWriteBuf(void *handle, char *buf, int size)
1544 {
1545 struct parmHandle *parmHandle = (struct parmHandle *)handle;
1546 char line[LINE_SZ];
1547 int len;
1548 int curSize;
1549 char *s;
1550
1551 if (parmHandle->magic != PARM_MAGIC) {
1552 GfFatal ("gfParmWriteBuf: bad handle (%p)\n", parmHandle);
1553 return 1;
1554 }
1555
1556 parmHandle->outCtrl.state = 0;
1557 parmHandle->outCtrl.curSection = NULL;
1558 parmHandle->outCtrl.curParam = NULL;
1559 curSize = size;
1560 s = buf;
1561
1562 while (curSize && xmlGetOuputLine (parmHandle, line, sizeof (line))) {
1563 len = strlen (line);
1564 if (len > curSize) {
1565 len = curSize;
1566 }
1567 memcpy (s, line, len);
1568 s += len;
1569 curSize -= len;
1570 }
1571 buf [size - 1] = 0;
1572
1573 return 0;
1574 }
1575
1576
1577 /** @brief Set the dtd path and header.
1578 *
1579 * @ingroup paramsfile
1580 * @param parmHandle parameter set handle
1581 * @param dtd optional dtd path
1582 * @param header optional header
1583 */
GfParmSetDTD(void * parmHandle,char * dtd,char * header)1584 void GfParmSetDTD(void *parmHandle, char *dtd, char*header)
1585 {
1586 struct parmHandle *handle = (struct parmHandle *)parmHandle;
1587 struct parmHeader *conf = handle->conf;
1588
1589 if (dtd) {
1590 FREEZ(conf->dtd);
1591 conf->dtd = strdup(dtd);
1592 }
1593
1594 if (header) {
1595 FREEZ(conf->header);
1596 conf->header = strdup(header);
1597 }
1598 }
1599
1600
1601 /** @brief Write parameter set into file.
1602 *
1603 * @ingroup paramsfile
1604 * @param[in] file if NULL the internally stored filename is used, if not NULL the given filename is used (it is not stored in the handle)
1605 * @param[in,out] parmHandle parameter set handle
1606 * @param[in] name if NULL the internally stored name is used, if not NULL the given name is used and stored in the handle
1607 * @return 0 if ok
1608 * <br>1 if Error
1609 */
GfParmWriteFile(const char * file,void * parmHandle,const char * name)1610 int GfParmWriteFile(const char *file, void *parmHandle, const char *name)
1611 {
1612 struct parmHandle *handle = (struct parmHandle *)parmHandle;
1613 struct parmHeader *conf = handle->conf;
1614 char line[LINE_SZ];
1615 FILE *fout;
1616
1617 if (handle->magic != PARM_MAGIC) {
1618 GfFatal ("gfParmWriteFile: bad handle (%p)\n", parmHandle);
1619 return 1;
1620 }
1621
1622 if (!file) {
1623 file = conf->filename;
1624 if (!file) {
1625 GfError ("gfParmWriteFile: bad file name\n");
1626 return 1;
1627 }
1628 }
1629
1630 fout = fopen (file, "wb");
1631 if (!fout) {
1632 GfError ("gfParmSetStr: fopen (%s, \"wb\") failed\n", file);
1633 return 1;
1634 }
1635
1636 if (name) {
1637 FREEZ (conf->name);
1638 conf->name = strdup (name);
1639 }
1640
1641 handle->outCtrl.state = 0;
1642 handle->outCtrl.curSection = NULL;
1643 handle->outCtrl.curParam = NULL;
1644
1645 while (xmlGetOuputLine (handle, line, sizeof (line))) {
1646 fputs (line, fout);
1647 }
1648
1649 GfOut ("GfParmWriteFile: %s file written\n", file);
1650
1651 fclose (fout);
1652
1653 return 0;
1654 }
1655
1656
1657 /** @brief Create directory for parameter set handle if it does not yet exist.
1658 *
1659 * @ingroup paramsfile
1660 * @param[in] file if NULL the internally stored path is used, if not NULL the given path is used (it is not stored in the handle)
1661 * @param[in,out] parmHandle parameter set handle
1662 * @return 0 if ok
1663 * <br>1 if Error
1664 */
GfParmCreateDirectory(const char * file,void * parmHandle)1665 int GfParmCreateDirectory(const char *file, void *parmHandle)
1666 {
1667 struct parmHandle *handle = (struct parmHandle *)parmHandle;
1668 struct parmHeader *conf = handle->conf;
1669
1670 if (handle->magic != PARM_MAGIC) {
1671 GfFatal ("GfParmCreateDirectory: bad handle (%p)\n", parmHandle);
1672 return 1;
1673 }
1674
1675 if (!file) {
1676 file = conf->filename;
1677 if (!file) {
1678 GfError ("GfParmCreateDirectory: bad file name\n");
1679 return 1;
1680 }
1681 }
1682
1683 if (GfCreateDirForFile(file) != GF_DIR_CREATED) {
1684 return 1;
1685 }
1686
1687 return 0;
1688 }
1689
1690
1691 /** @brief Remove a parameter from a parameter set.
1692 *
1693 * @ingroup paramsdata
1694 * @param parmHandle parameter set handle
1695 * @param sectionName parameter section name
1696 * @param paramName parameter name
1697 */
GfParmRemove(void * parmHandle,char * sectionName,char * paramName)1698 void GfParmRemove(void *parmHandle, char *sectionName, char *paramName)
1699 {
1700 struct parmHandle *handle = (struct parmHandle *)parmHandle;
1701 struct parmHeader *conf;
1702
1703 conf = handle->conf;
1704
1705 if (handle->magic != PARM_MAGIC) {
1706 GfFatal ("gfParmRemove: bad handle (%p)\n", parmHandle);
1707 return;
1708 }
1709
1710 removeParamByName(conf, sectionName, paramName);
1711 }
1712
1713
1714 /** @brief Helper function to release the parameter set content.
1715 *
1716 * @ingroup paramshelper
1717 * @param[in] conf parameter set header
1718 */
parmClean(struct parmHeader * conf)1719 static void parmClean(struct parmHeader *conf)
1720 {
1721 struct section *section;
1722
1723 while ((section = GF_TAILQ_FIRST (&(conf->rootSection->subSectionList))) !=
1724 GF_TAILQ_END (&(conf->rootSection->subSectionList)))
1725 {
1726 removeSection (conf, section);
1727 }
1728 }
1729
1730
1731 /** @brief Clean all the parameters of a parameter set.
1732 *
1733 * Removes all contained parameters in the parameter set, the
1734 * @e parmHande remains valid.
1735 *
1736 * @ingroup paramsfile
1737 * @param[in,out] parmHandle parameter set handle
1738 * @return 0 if OK
1739 * <br>-1 if Error
1740 */
GfParmClean(void * parmHandle)1741 void GfParmClean(void *parmHandle)
1742 {
1743 struct parmHandle *handle = (struct parmHandle *)parmHandle;
1744 struct parmHeader *conf;
1745
1746 conf = handle->conf;
1747
1748 if (handle->magic != PARM_MAGIC) {
1749 GfFatal ("gfParmClean: bad handle (%p)\n", parmHandle);
1750 return;
1751 }
1752
1753 parmClean (conf);
1754 }
1755
1756
1757 /** @brief Helper function to release the parameter set if the reference counter is 0.
1758 *
1759 * @ingroup paramshelper
1760 * @param[in] conf parameter set header
1761 * @see GfParmReleaseHandle
1762 * @see GfParmWriteFile
1763 * @see GfParmReadFile
1764 */
parmReleaseHeader(struct parmHeader * conf)1765 static void parmReleaseHeader(struct parmHeader *conf)
1766 {
1767 conf->refcount--;
1768 if (conf->refcount > 0) {
1769 return;
1770 }
1771
1772 GfOut ("parmReleaseHeader: refcount null free \"%s\"\n", conf->filename);
1773
1774 parmClean (conf);
1775
1776 freez (conf->filename);
1777 if (conf->paramHash) {
1778 GfHashRelease (conf->paramHash, NULL);
1779 }
1780
1781 if (conf->sectionHash) {
1782 GfHashRelease (conf->sectionHash, NULL);
1783 }
1784
1785 freez (conf->rootSection->fullName);
1786 freez (conf->rootSection);
1787 freez (conf->dtd);
1788 freez (conf->name);
1789 freez (conf->header);
1790 freez (conf);
1791 }
1792
1793
1794 /** @brief Helper function to release the handle and eventually the referenced parameter set (if the reference counter falls to 0).
1795 *
1796 * @ingroup paramshelper
1797 * @param[in] parmHandle parameter set handle
1798 * @see GfParmReleaseHandle
1799 * @see GfParmWriteFile
1800 * @see GfParmReadFile
1801 */
parmReleaseHandle(struct parmHandle * parmHandle)1802 static void parmReleaseHandle(struct parmHandle *parmHandle)
1803 {
1804 struct parmHeader *conf = parmHandle->conf;
1805
1806 GfOut ("parmReleaseHandle: release \"%s\" (%p)\n", conf->filename, parmHandle);
1807
1808 GF_TAILQ_REMOVE (&parmHandleList, parmHandle, linkHandle);
1809 parmHandle->magic = 0;
1810 freez (parmHandle);
1811
1812 parmReleaseHeader(conf);
1813 }
1814
1815
1816 /** @brief Release given parameter set handle @e parmHandle.
1817 *
1818 * Releases the parameter set handle and eventally the parameter set which it
1819 * refers to.
1820 *
1821 * The parameter sets are internally reused and reference counted, so if the
1822 * parameter set has still a reference counter greater than 0, just the reference
1823 * counter is decremented, if it reaches 0, the whole parameter set is deleted from
1824 * memory.
1825 *
1826 * The parameter file is not written on release, write operations are done explicitely
1827 * with @ref GfParmWriteFile.
1828 *
1829 * @ingroup paramsfile
1830 * @param[in] parmHandle parameter set handle
1831 * @see GfParmWriteFile
1832 * @see GfParmReadFile
1833 */
GfParmReleaseHandle(void * parmHandle)1834 void GfParmReleaseHandle(void *parmHandle)
1835 {
1836 struct parmHandle *handle = (struct parmHandle *)parmHandle;
1837
1838 if (handle->magic != PARM_MAGIC) {
1839 GfFatal ("gfParmReleaseHandle: bad handle (%p)\n", parmHandle);
1840 return;
1841 }
1842
1843 parmReleaseHandle(handle);
1844 }
1845
1846
1847 /** @brief Support function to multiply or divide @e dest with unit conversion factor.
1848 *
1849 * This function is used by @ref GfParmUnit2SI and @ref GfParmSI2Unit. The given unit string
1850 * gets split up and processed unit by unit with evalUnit.
1851 *
1852 * @ingroup paramshelper
1853 * @param[in] unit unit name from dest
1854 * @param[in,out] dest pointer to value to convert
1855 * @param flg multiply (0) or divide (otherwise)
1856 * @see GfParmUnit2SI
1857 * @see GfParmSI2Unit
1858 */
evalUnit(char * unit,tdble * dest,int flg)1859 static void evalUnit(char *unit, tdble *dest, int flg)
1860 {
1861 tdble coeff = 1.0;
1862
1863 if (strcmp(unit, "m") == 0) return;
1864 if (strcmp(unit, "kg") == 0) return;
1865 if (strcmp(unit, "s") == 0) return;
1866 if (strcmp(unit, "rad") == 0) return;
1867 if (strcmp(unit, "Pa") == 0) return;
1868
1869 if ((strcmp(unit, "feet") == 0) || (strcmp(unit, "ft") == 0)) {
1870 coeff = 0.304801f; /* m */
1871 } else if (strcmp(unit, "deg") == 0) {
1872 coeff = (float) (M_PI/180.0); /* rad */
1873 } else if ((strcmp(unit, "h") == 0) || (strcmp(unit, "hour") == 0) || (strcmp(unit, "hours") == 0)) {
1874 coeff = 3600.0; /* s */
1875 } else if ((strcmp(unit, "day") == 0) || (strcmp(unit, "days") == 0)) {
1876 coeff = 24*3600.0; /* s */
1877 } else if (strcmp(unit, "km") == 0) {
1878 coeff = 1000.0; /* m */
1879 } else if (strcmp(unit, "mm") == 0) {
1880 coeff = 0.001f; /* m */
1881 } else if (strcmp(unit, "cm") == 0) {
1882 coeff = 0.01f; /* m */
1883 } else if ((strcmp(unit, "in") == 0) || (strcmp(unit, "inch") == 0) || (strcmp(unit, "inches") == 0)) {
1884 coeff = 0.0254f; /* m */
1885 } else if ((strcmp(unit, "lbs") == 0) || (strcmp(unit, "lb") == 0)) {
1886 coeff = 0.45359237f; /* kg */
1887 } else if (strcmp(unit, "lbf") == 0) {
1888 coeff = 0.45359237f*G; /* N (kg*m/s^2) */
1889 } else if ((strcmp(unit, "slug") == 0) || (strcmp(unit, "slugs") == 0)) {
1890 coeff = 14.59484546f; /* kg */
1891 } else if (strcmp(unit, "kPa") == 0) {
1892 coeff = 1000.0; /* Pa */
1893 } else if (strcmp(unit, "MPa") == 0) {
1894 coeff = 1000000.0; /* Pa */
1895 } else if ((strcmp(unit, "PSI") == 0) || (strcmp(unit, "psi") == 0)){
1896 coeff = 6894.76f; /* Pa */
1897 } else if ((strcmp(unit, "rpm") == 0) || (strcmp(unit, "RPM") == 0)) {
1898 coeff = 0.104719755f; /* rad/s */
1899 } else if ((strcmp(unit, "percent") == 0) || (strcmp(unit, "%") == 0)) {
1900 coeff = 0.01f;
1901 } else if ((strcmp(unit, "mph") == 0) || (strcmp(unit, "MPH") == 0)) {
1902 coeff = 0.44704f; /* m/s */
1903 }
1904
1905 if (flg) {
1906 *dest /= coeff;
1907 } else {
1908 *dest *= coeff;
1909 }
1910
1911 return;
1912 }
1913
1914
1915 /** @brief Convert a value given in unit to SI.
1916 *
1917 * The units can be combined with "/" (divide), "." (multiply) and "2" (square), e.g. "lbf/in", "N.m", "kg.m/s2".
1918 *
1919 * @ingroup paramsdata
1920 * @param[in] unit unit name from val
1921 * @param[in] val value in unit
1922 * @return the value converted to SI
1923 * @note The supported units are:
1924 * <br><ul>
1925 * <li><b>feet</b> or <b>ft</b> converted to <b>m</b></li>
1926 * <li><b>inches</b>,<b>inch</b> or <b>in</b> converted to <b>m</b></li>
1927 * <li><b>km</b> converted to <b>m</b></li>
1928 * <li><b>cm</b> converted to <b>m</b></li>
1929 * <li><b>mm</b> converted to <b>m</b></li>
1930 * <li><b>lbs</b> converted to <b>kg</b></li>
1931 * <li><b>slug</b> or <b>slugs</b> converted to <b>kg</b></li>
1932 * <li><b>h</b>,<b>hour</b> or <b>hours</b> converted to <b>s</b></li>
1933 * <li><b>day</b> or <b>days</b> converted to <b>s</b></li>
1934 * <li><b>kPa</b> or <b>MPa</b> converted to <b>Pa</b></li>
1935 * <li><b>PSI</b> or <b>psi</b> converted to <b>Pa</b></li>
1936 * <li><b>deg</b> converted to <b>rad</b></li>
1937 * <li><b>rpm</b> or <b>RPM</b> converted to <b>rad/s</b></li>
1938 * <li><b>percent</b> or <b>%</b> divided by <b>100</b></li>
1939 * <li><b>lbf</b> converted to <b>N</b></li>
1940 * </ul>
1941 *
1942 * @see GfParmSI2Unit
1943 */
GfParmUnit2SI(const char * unit,tdble val)1944 tdble GfParmUnit2SI(const char *unit, tdble val)
1945 {
1946 char buf[256];
1947 int idx;
1948 const char *s;
1949 int inv;
1950 tdble dest = val;
1951
1952 if ((unit == NULL) || (strlen(unit) == 0)) return dest;
1953
1954 s = unit;
1955 buf[0] = 0;
1956 inv = 0;
1957 idx = 0;
1958
1959 while (*s != 0) {
1960 switch (*s) {
1961 case '.':
1962 evalUnit(buf, &dest, inv);
1963 buf[0] = 0;
1964 idx = 0;
1965 break;
1966 case '/':
1967 evalUnit(buf, &dest, inv);
1968 buf[0] = 0;
1969 idx = 0;
1970 inv = 1;
1971 break;
1972 case '2':
1973 evalUnit(buf, &dest, inv);
1974 evalUnit(buf, &dest, inv);
1975 buf[0] = 0;
1976 idx = 0;
1977 break;
1978 default:
1979 buf[idx++] = *s;
1980 buf[idx] = 0;
1981 break;
1982 }
1983 s++;
1984 }
1985
1986 evalUnit(buf, &dest, inv);
1987 return dest;
1988 }
1989
1990
1991 /** @brief Convert a value from SI to given unit.
1992 * @ingroup paramsdata
1993 * @param[in] unit unit name to convert to
1994 * @param[in] val value in SI units to be converted to unit
1995 * @return converted value in unit
1996 * @see GfParmUnit2SI
1997 */
GfParmSI2Unit(const char * unit,tdble val)1998 tdble GfParmSI2Unit(const char *unit, tdble val)
1999 {
2000 char buf[256];
2001 int idx;
2002 const char *s;
2003 int inv;
2004 tdble dest = val;
2005
2006 if ((unit == NULL) || (strlen(unit) == 0)) return dest;
2007
2008 s = unit;
2009 buf[0] = 0;
2010 inv = 1;
2011 idx = 0;
2012
2013 while (*s != 0) {
2014 switch (*s) {
2015 case '.':
2016 evalUnit(buf, &dest, inv);
2017 buf[0] = 0;
2018 idx = 0;
2019 break;
2020 case '/':
2021 evalUnit(buf, &dest, inv);
2022 buf[0] = 0;
2023 idx = 0;
2024 inv = 0;
2025 break;
2026 case '2':
2027 evalUnit(buf, &dest, inv);
2028 evalUnit(buf, &dest, inv);
2029 buf[0] = 0;
2030 idx = 0;
2031 break;
2032 default:
2033 buf[idx++] = *s;
2034 buf[idx] = 0;
2035 break;
2036 }
2037 s++;
2038 }
2039
2040 evalUnit(buf, &dest, inv);
2041 return dest;
2042 }
2043
2044
2045
2046 /** @brief Get the name property of the parameter set @e handle.
2047 *
2048 * @ingroup paramsdata
2049 * @param[in] handle parameter set handle
2050 * @return Name
2051 * <br>NULL if failed or not set
2052 * @note The pointer returned is for immediate use, if you plan
2053 * to keep the value for a long time, it is necessary to
2054 * copy the string, because manipulating the handle will
2055 * produce an incoherent pointer.
2056 */
GfParmGetName(void * handle)2057 char* GfParmGetName(void *handle)
2058 {
2059 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2060 struct parmHeader *conf = parmHandle->conf;
2061
2062 if (parmHandle->magic != PARM_MAGIC) {
2063 GfFatal ("GfParmGetName: bad handle (%p)\n", parmHandle);
2064 return NULL;
2065 }
2066
2067 return conf->name;
2068 }
2069
2070
2071 /** @brief Get the filename property of the parameter set @e handle.
2072 *
2073 * @ingroup paramsfile
2074 * @param[in] handle parameter set handle
2075 * @return File name
2076 * <br>NULL if failed or not set
2077 * @note The pointer returned is for immediate use, if you plan
2078 * to keep the value for a long time, it is necessary to
2079 * copy the string, because manipulating the handle will
2080 * produce an incoherent pointer.
2081 */
GfParmGetFileName(void * handle)2082 char* GfParmGetFileName(void *handle)
2083 {
2084 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2085 struct parmHeader *conf = parmHandle->conf;
2086
2087 if (parmHandle->magic != PARM_MAGIC) {
2088 GfFatal ("GfParmGetFileName: bad handle (%p)\n", parmHandle);
2089 return NULL;
2090 }
2091
2092 return conf->filename;
2093 }
2094
2095
2096 /** @brief Count the number of subsections in a section in the parameter set @e handle.
2097 *
2098 * A subsection can have any name and structure, any section element enclosed by the section given in
2099 * the @e path is a subsection.
2100 *
2101 * @ingroup paramslist
2102 * @param[in] handle parameter set handle
2103 * @param[in] path path of the section containing the subsections to count
2104 * @return element count
2105 */
GfParmGetEltNb(void * handle,const char * path)2106 int GfParmGetEltNb(void *handle, const char *path)
2107 {
2108 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2109 struct parmHeader *conf = parmHandle->conf;
2110 struct section *section;
2111 int count;
2112
2113 if (parmHandle->magic != PARM_MAGIC) {
2114 GfFatal ("GfParmGetEltNb: bad handle (%p)\n", parmHandle);
2115 return 0;
2116 }
2117
2118 section = (struct section *)GfHashGetStr (conf->sectionHash, path);
2119 if (!section) {
2120 return 0;
2121 }
2122
2123 count = 0;
2124 section = GF_TAILQ_FIRST (&(section->subSectionList));
2125 while (section) {
2126 count++;
2127 section = GF_TAILQ_NEXT (section, linkSection);
2128 }
2129
2130 return count;
2131 }
2132
2133
2134
2135 /** @brief Go the the first subsection element in the parameter set @e handle.
2136 *
2137 * A subsection can have any name and structure, any section element enclosed by the section given in
2138 * the @e path is a subsection.
2139 *
2140 * @ingroup paramslist
2141 * @param[in,out] handle parameter set handle, interation state is internally stored
2142 * @param[in] path path of the section containing the subsections to iterate through
2143 * @return 0 Ok
2144 * <br>-1 Failed
2145 * @see GfParmListSeekNext
2146 * @see GfParmListGetCurEltName
2147 */
GfParmListSeekFirst(void * handle,const char * path)2148 int GfParmListSeekFirst(void *handle, const char *path)
2149 {
2150 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2151 struct parmHeader *conf = parmHandle->conf;
2152 struct section *section;
2153
2154 if (parmHandle->magic != PARM_MAGIC) {
2155 GfFatal ("GfParmListSeekFirst: bad handle (%p)\n", parmHandle);
2156 return -1;
2157 }
2158
2159 section = (struct section *)GfHashGetStr (conf->sectionHash, path);
2160 if (!section) {
2161 return -1;
2162 }
2163
2164 section->curSubSection = GF_TAILQ_FIRST (&(section->subSectionList));
2165
2166 return 0;
2167 }
2168
2169
2170 /** @brief Go the the next subsection element in the parameter set @e handle.
2171 *
2172 * A subsection can have any name and structure, any section element enclosed by the section given in
2173 * the @e path is a subsection.
2174 *
2175 * @ingroup paramslist
2176 * @param[in,out] handle parameter set handle, interation state is internally stored
2177 * @param[in] path path of the section containing the subsections to iterate through
2178 * @return 0 Ok
2179 * <br>1 End of list reached
2180 * <br>-1 Failed
2181 * @see GfParmListSeekFirst
2182 * @see GfParmListGetCurEltName
2183 */
GfParmListSeekNext(void * handle,const char * path)2184 int GfParmListSeekNext(void *handle, const char *path)
2185 {
2186 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2187 struct parmHeader *conf = parmHandle->conf;
2188 struct section *section;
2189
2190 if (parmHandle->magic != PARM_MAGIC) {
2191 GfFatal ("GfParmListSeekNext: bad handle (%p)\n", parmHandle);
2192 return -1;
2193 }
2194
2195 section = (struct section *)GfHashGetStr (conf->sectionHash, path);
2196 if ((!section) || (!section->curSubSection)) {
2197 return -1;
2198 }
2199
2200 section->curSubSection = GF_TAILQ_NEXT (section->curSubSection, linkSection);
2201
2202 if (section->curSubSection) {
2203 return 0;
2204 }
2205
2206 return 1;
2207 }
2208
2209
2210 /** @brief Remove all the subsections in a section in the parameter set @e handle.
2211 *
2212 * A subsection can have any name and structure, any section element enclosed by the section given in
2213 * the @e path is a subsection.
2214 *
2215 * @ingroup paramslist
2216 * @param[in,out] handle parameter set handle
2217 * @param[in] path path of the section containing the subsections to remove
2218 * @return 0 Ok
2219 * <br>-1 Error
2220 */
GfParmListClean(void * handle,const char * path)2221 int GfParmListClean(void *handle, const char *path)
2222 {
2223 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2224 struct parmHeader *conf = parmHandle->conf;
2225 struct section *listSection;
2226 struct section *section;
2227
2228 if (parmHandle->magic != PARM_MAGIC) {
2229 GfFatal ("GfParmListSeekNext: bad handle (%p)\n", parmHandle);
2230 return -1;
2231 }
2232
2233 listSection = (struct section *)GfHashGetStr (conf->sectionHash, path);
2234 if (!listSection) {
2235 GfOut ("GfParmListClean: \"%s\" not found\n", path);
2236 return -1;
2237 }
2238
2239 while ((section = GF_TAILQ_FIRST (&(listSection->subSectionList))) != NULL) {
2240 removeSection (conf, section);
2241 }
2242
2243 return 0;
2244 }
2245
2246
2247 /** @brief Get current subsection name of the parameter set @e handle during subsection iteration.
2248 *
2249 * The internal state of the parameter set @e handle must point to a current subsection,
2250 * this is done using @ref GfParmListSeekFirst or @ref GfParmListSeekNext. This call
2251 * returns the name of the current subsection.
2252 *
2253 * A subsection can have any name and structure, any section element enclosed by the section given in
2254 * the @e path is a subsection.
2255 *
2256 * @ingroup paramslist
2257 * @param[in] handle parameter set handle
2258 * @param[in] path path of the section used to iterate subsections
2259 * @return Name of the current subsection
2260 * <br>NULL if failed
2261 * @note The pointer returned is for immediate use, if you plan
2262 * to keep the value for a long time, it is necessary to
2263 * copy the string, because removing the section will
2264 * produce an incoherent pointer.
2265 * @see GfParmListSeekFirst
2266 * @see GfParmListSeekNext
2267 */
GfParmListGetCurEltName(void * handle,const char * path)2268 char* GfParmListGetCurEltName(void *handle, const char *path)
2269 {
2270 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2271 struct parmHeader *conf = parmHandle->conf;
2272 struct section *section;
2273 char *s;
2274
2275 if (parmHandle->magic != PARM_MAGIC) {
2276 GfFatal ("GfParmListGetCurEltName: bad handle (%p)\n", parmHandle);
2277 return NULL;
2278 }
2279
2280 section = (struct section *)GfHashGetStr (conf->sectionHash, path);
2281 if ((!section) || (!section->curSubSection)) {
2282 return NULL;
2283 }
2284
2285 s = strrchr(section->curSubSection->fullName, '/');
2286 if (s) {
2287 s++;
2288 return s;
2289 }
2290
2291 return section->curSubSection->fullName;
2292 }
2293
2294
2295 /** @brief Get a string parameter from the parameter set @e handle.
2296 *
2297 * If the parameter does not yet exist the given default is returned.
2298 *
2299 * @ingroup paramsdata
2300 * @param[in] parmHandle parameter set handle
2301 * @param[in] path path of the parameter
2302 * @param[in] key parameter key name
2303 * @param[in] deflt default string
2304 * @return parameter value
2305 * <br>deflt if error or not found
2306 * @note The pointer returned is for immediate use, if you plan
2307 * to keep the value for a long time, it is necessary to
2308 * copy the string, because removing the attribute will
2309 * produce an incoherent pointer.
2310 */
GfParmGetStr(void * parmHandle,const char * path,const char * key,const char * deflt)2311 const char* GfParmGetStr(void *parmHandle, const char *path, const char *key, const char *deflt)
2312 {
2313 struct param *param;
2314 struct parmHandle *handle = (struct parmHandle *)parmHandle;
2315 struct parmHeader *conf = handle->conf;
2316
2317 if (handle->magic != PARM_MAGIC) {
2318 GfFatal ("gfParmGetStr: bad handle (%p)\n", parmHandle);
2319 return deflt;
2320 }
2321
2322 param = getParamByName (conf, path, key, 0);
2323 if (!param || !(param->value) || !strlen (param->value) || (param->type != P_STR)) {
2324 return deflt;
2325 }
2326
2327 return param->value;
2328 }
2329
2330
2331 /** @brief Get a string parameter from the parameter set @e handle based on subsection iteration.
2332 *
2333 * The internal state of the parameter set @e handle must point to a current subsection,
2334 * this is done using @ref GfParmListSeekFirst or @ref GfParmListSeekNext. If the parameter
2335 * does not yet exist the given default is returned.
2336 *
2337 * A subsection can have any name and structure, any section element enclosed by the section given in
2338 * the @e path is a subsection.
2339 *
2340 * @ingroup paramslist
2341 * @param[in] handle parameter set handle
2342 * @param[in] path path of the section used to iterate subsections
2343 * @param[in] key parameter key name
2344 * @param[in] deflt default string
2345 * @return parameter value
2346 * <br>deflt if error or not found
2347 * @note The pointer returned is for immediate use, if you plan
2348 * to keep the value for a long time, it is necessary to
2349 * copy the string, because removing the attribute will
2350 * produce an incoherent pointer.
2351 * @see GfParmListSeekFirst
2352 * @see GfParmListSeekNext
2353 */
GfParmGetCurStr(void * handle,const char * path,const char * key,const char * deflt)2354 const char* GfParmGetCurStr(void *handle, const char *path, const char *key, const char *deflt)
2355 {
2356 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2357 struct parmHeader *conf = parmHandle->conf;
2358 struct section *section;
2359 struct param *param;
2360
2361 if (parmHandle->magic != PARM_MAGIC) {
2362 GfFatal ("GfParmGetCurStr: bad handle (%p)\n", parmHandle);
2363 return deflt;
2364 }
2365
2366 section = (struct section *)GfHashGetStr (conf->sectionHash, path);
2367 if ((!section) || (!section->curSubSection)) {
2368 return deflt;
2369 }
2370
2371 param = getParamByName (conf, section->curSubSection->fullName, key, 0);
2372 if (!param || !(param->value) || !strlen (param->value) || (param->type != P_STR)) {
2373 return deflt;
2374 }
2375
2376 return param->value;
2377 }
2378
2379
2380 /** @brief Get a numerical parameter from the parameter set @e handle.
2381 *
2382 * If the parameter does not exist the given default value is returned without unit conversion.
2383 *
2384 * @ingroup paramsdata
2385 * @param[in] handle parameter set handle
2386 * @param[in] path path of the parameter
2387 * @param[in] key parameter key name
2388 * @param[in] unit unit to convert the result to (NULL if SI is desired)
2389 * @param[in] deflt default value
2390 * @return parameter value
2391 */
GfParmGetNum(void * handle,const char * path,const char * key,const char * unit,tdble deflt)2392 tdble GfParmGetNum(void *handle, const char *path, const char *key, const char *unit, tdble deflt)
2393 {
2394 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2395 struct parmHeader *conf = parmHandle->conf;
2396 struct param *param;
2397
2398 if (parmHandle->magic != PARM_MAGIC) {
2399 GfFatal ("GfParmGetNum: bad handle (%p)\n", parmHandle);
2400 return deflt;
2401 }
2402
2403 param = getParamByName (conf, path, key, 0);
2404 if (!param || (param->type != P_NUM)) {
2405 return deflt;
2406 }
2407
2408 if (unit) {
2409 return GfParmSI2Unit(unit, param->valnum);
2410 }
2411
2412 return param->valnum;
2413 }
2414
2415
2416 /** @brief Get a numerical parameter from the parameter set @e handle based on subsection iteration.
2417 *
2418 * The internal state of the parameter set @e handle must point to a current subsection,
2419 * this is done using @ref GfParmListSeekFirst or @ref GfParmListSeekNext. If the parameter
2420 * does not exist the given default value is returned without unit conversion.
2421 *
2422 * A subsection can have any name and structure, any section element enclosed by the section given in
2423 * the @e path is a subsection.
2424 *
2425 * @ingroup paramslist
2426 * @param[in] handle parameter set handle
2427 * @param[in] path path of the section used to iterate subsections
2428 * @param[in] key parameter key name
2429 * @param[in] unit unit to convert the result to (NULL if SI is desired)
2430 * @param[in] deflt default value
2431 * @return parameter value
2432 * @see GfParmListSeekFirst
2433 * @see GfParmListSeekNext
2434 */
GfParmGetCurNum(void * handle,const char * path,const char * key,const char * unit,tdble deflt)2435 tdble GfParmGetCurNum(void *handle, const char *path, const char *key, const char *unit, tdble deflt)
2436 {
2437 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2438 struct parmHeader *conf = parmHandle->conf;
2439 struct section *section;
2440 struct param *param;
2441
2442 if (parmHandle->magic != PARM_MAGIC) {
2443 GfFatal ("GfParmGetCurNum: bad handle (%p)\n", parmHandle);
2444 return deflt;
2445 }
2446
2447 section = (struct section *)GfHashGetStr (conf->sectionHash, path);
2448 if ((!section) || (!section->curSubSection)) {
2449 return deflt;
2450 }
2451
2452 param = getParamByName(conf, section->curSubSection->fullName, key, 0);
2453 if (!param || (param->type != P_NUM)) {
2454 return deflt;
2455 }
2456
2457 if (unit) {
2458 return GfParmSI2Unit(unit, param->valnum);
2459 }
2460
2461 return param->valnum;
2462 }
2463
2464
2465 /** @brief Set a string parameter in the parameter set @e handle.
2466 *
2467 * If the parameter does not yet exist it is created. The within constraint is not checked.
2468 *
2469 * @ingroup paramsdata
2470 * @param[in,out] handle parameter set handle
2471 * @param[in] path path of the parameter
2472 * @param[in] key parameter key name
2473 * @param[in] val value (NULL or empty string to remove the parameter)
2474 * @return 0 ok
2475 * <br>-1 error
2476 */
GfParmSetStr(void * handle,const char * path,const char * key,const char * val)2477 int GfParmSetStr(void *handle, const char *path, const char *key, const char *val)
2478 {
2479 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2480 struct parmHeader *conf = parmHandle->conf;
2481 struct param *param;
2482
2483 if (parmHandle->magic != PARM_MAGIC) {
2484 GfFatal ("GfParmSetStr: bad handle (%p)\n", parmHandle);
2485 return -1;
2486 }
2487
2488 if (!val || !strlen (val)) {
2489 /* Remove the entry */
2490 removeParamByName (conf, path, key);
2491 return 0;
2492 }
2493
2494 param = getParamByName (conf, path, key, PARAM_CREATE);
2495 if (!param) {
2496 return -1;
2497 }
2498
2499 param->type = P_STR;
2500 freez (param->value);
2501 param->value = strdup (val);
2502
2503 if (!param->value) {
2504 GfError ("gfParmSetStr: strdup (%s) failed\n", val);
2505 removeParamByName (conf, path, key);
2506 return -1;
2507 }
2508
2509 return 0;
2510 }
2511
2512
2513 /** @brief Set a string parameter in the parameter set @e handle based on subsection iteration.
2514 *
2515 * The internal state of the parameter set @e handle must point to a current subsection,
2516 * this is done using @ref GfParmListSeekFirst or @ref GfParmListSeekNext. If the parameter
2517 * does not yet exist it is created. The within constraint is not checked.
2518 *
2519 * A subsection can have any name and structure, any section element enclosed by the section given in
2520 * the @e path is a subsection.
2521 *
2522 * @ingroup paramslist
2523 * @param[in,out] handle parameter set handle
2524 * @param[in] path path of the section used to iterate subsections
2525 * @param[in] key parameter key name
2526 * @param[in] val value (NULL or empty string to remove the parameter)
2527 * @return 0 ok
2528 * <br>-1 error
2529 * @see GfParmListSeekFirst
2530 * @see GfParmListSeekNext
2531 */
GfParmSetCurStr(void * handle,const char * path,const char * key,const char * val)2532 int GfParmSetCurStr(void *handle, const char *path, const char *key, const char *val)
2533 {
2534 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2535 struct parmHeader *conf = parmHandle->conf;
2536 struct section *section;
2537 struct param *param;
2538
2539 if (parmHandle->magic != PARM_MAGIC) {
2540 GfFatal ("GfParmSetCurStr: bad handle (%p)\n", parmHandle);
2541 return -1;
2542 }
2543
2544 section = (struct section *)GfHashGetStr (conf->sectionHash, path);
2545 if ((!section) || (!section->curSubSection)) {
2546 return -1;
2547 }
2548
2549 if (!val || !strlen (val)) {
2550 /* Remove the entry */
2551 removeParamByName (conf, section->curSubSection->fullName, key);
2552 return 0;
2553 }
2554
2555 param = getParamByName (conf, section->curSubSection->fullName, key, PARAM_CREATE);
2556 if (!param) {
2557 return -1;
2558 }
2559
2560 param->type = P_STR;
2561 freez (param->value);
2562 param->value = strdup (val);
2563 if (!param->value) {
2564 GfError ("gfParmSetStr: strdup (%s) failed\n", val);
2565 removeParamByName (conf, section->curSubSection->fullName, key);
2566 return -1;
2567 }
2568
2569 return 0;
2570 }
2571
2572
2573 /** @brief Set a numerical parameter in the parameter set @e handle.
2574 *
2575 * If the parameter does not yet exist it is created. The value is assigned to the value, min and max.
2576 *
2577 * @ingroup paramsdata
2578 * @param[in,out] handle parameter set handle
2579 * @param[in] path path of the parameter
2580 * @param[in] key parameter key name
2581 * @param[in] unit unit to convert the result to (NULL if SI desired)
2582 * @param[in] val value to set
2583 * @return 0 ok
2584 * <br>-1 error
2585 */
GfParmSetNum(void * handle,const char * path,const char * key,const char * unit,tdble val)2586 int GfParmSetNum(void *handle, const char *path, const char *key, const char *unit, tdble val)
2587 {
2588 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2589 struct parmHeader *conf = parmHandle->conf;
2590 struct param *param;
2591
2592 if (parmHandle->magic != PARM_MAGIC) {
2593 GfFatal ("GfParmSetNum: bad handle (%p)\n", parmHandle);
2594 return -1;
2595 }
2596
2597 param = getParamByName (conf, path, key, PARAM_CREATE);
2598 if (!param) {
2599 return -11;
2600 }
2601
2602 param->type = P_NUM;
2603 FREEZ (param->unit);
2604 if (unit) {
2605 param->unit = strdup (unit);
2606 }
2607
2608 val = GfParmUnit2SI (unit, val);
2609 param->valnum = val;
2610 param->min = val;
2611 param->max = val;
2612
2613 return 0;
2614 }
2615
2616
2617 /** @brief Set a numerical parameter in the parameter set @e handle including min and max.
2618 * @ingroup paramsdata
2619 * @param[in,out] handle parameter set handle
2620 * @param[in] path path of the parameter
2621 * @param[in] key parameter key name
2622 * @param[in] unit unit to convert the result to (NULL if SI desired)
2623 * @param[in] val value to set
2624 * @param[in] min min value to set
2625 * @param[in] max max value to set
2626 * @return 0 ok
2627 * <br>-1 error
2628 */
GfParmSetNumEx(void * handle,const char * path,const char * key,const char * unit,tdble val,tdble min,tdble max)2629 int GfParmSetNumEx(void *handle, const char *path, const char *key, const char *unit, tdble val, tdble min, tdble max)
2630 {
2631 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2632 struct parmHeader *conf = parmHandle->conf;
2633 struct param *param;
2634
2635 if (parmHandle->magic != PARM_MAGIC) {
2636 GfFatal ("GfParmSetNumEx: bad handle (%p)\n", parmHandle);
2637 return -1;
2638 }
2639
2640 param = getParamByName (conf, path, key, PARAM_CREATE);
2641 if (!param) {
2642 return -1;
2643 }
2644
2645 param->type = P_NUM;
2646 FREEZ (param->unit);
2647 if (unit) {
2648 param->unit = strdup (unit);
2649 }
2650
2651 param->valnum = GfParmUnit2SI (unit, val);
2652 param->min = GfParmUnit2SI (unit, min);
2653 param->max = GfParmUnit2SI (unit, max);
2654
2655 return 0;
2656 }
2657
2658
2659 /** @brief Set a numerical parameter in the parameter set @e handle based on subsection iteration.
2660 *
2661 * The internal state of the parameter set @e handle must point to a current subsection,
2662 * this is done using @ref GfParmListSeekFirst or @ref GfParmListSeekNext. If the parameter
2663 * does not yet exist it is created. The value is assigned to the value, min and max.
2664 *
2665 * A subsection can have any name and structure, any section element enclosed by the section given in
2666 * the @e path is a subsection.
2667 *
2668 * @ingroup paramslist
2669 * @param[in,out] handle parameter set handle
2670 * @param[in] path path of the section used to iterate subsections
2671 * @param[in] key parameter key name
2672 * @param[in] unit unit to convert the result to (NULL if SI is desired)
2673 * @param[in] val value to set
2674 * @return 0 ok
2675 * <br>-1 error
2676 * @see GfParmListSeekFirst
2677 * @see GfParmListSeekNext
2678 */
GfParmSetCurNum(void * handle,const char * path,const char * key,const char * unit,tdble val)2679 int GfParmSetCurNum(void *handle, const char *path, const char *key, const char *unit, tdble val)
2680 {
2681 struct parmHandle *parmHandle = (struct parmHandle *)handle;
2682 struct parmHeader *conf = parmHandle->conf;
2683 struct section *section;
2684 struct param *param;
2685
2686 if (parmHandle->magic != PARM_MAGIC) {
2687 GfFatal ("GfParmSetCurNum: bad handle (%p)\n", parmHandle);
2688 return -1;
2689 }
2690
2691 section = (struct section *)GfHashGetStr (conf->sectionHash, path);
2692 if ((!section) || (!section->curSubSection)) {
2693 return -1;
2694 }
2695
2696 param = getParamByName(conf, section->curSubSection->fullName, key, PARAM_CREATE);
2697 if (!param) {
2698 return -1;
2699 }
2700
2701 param->type = P_NUM;
2702 FREEZ (param->unit);
2703 if (unit) {
2704 param->unit = strdup (unit);
2705 }
2706
2707 val = GfParmUnit2SI (unit, val);
2708 param->valnum = val;
2709 param->min = val;
2710 param->max = val;
2711
2712 return 0;
2713 }
2714
2715
2716
2717 /** @brief Check the values in the parameter set @e tgt against the min/max/within definitions in the @e ref parameter set.
2718 *
2719 * @ingroup paramsfile
2720 * @param[in] ref reference parameter set handle for check (min/max/within)
2721 * @param[in] tgt target parameter set handle for check (values)
2722 * @return 0 All checked values are ok
2723 * <br>-1 Some values are out of bounds
2724 * @note Only the parameters present in both sets, @e tgt and @e ref, are tested.
2725 * Min/max/within values eventually present in @e tgt are not checked.
2726 * @see GfParmMergeHandles
2727 */
GfParmCheckHandle(void * ref,void * tgt)2728 int GfParmCheckHandle(void *ref, void *tgt)
2729 {
2730 struct parmHandle *parmHandleRef = (struct parmHandle *)ref;
2731 struct parmHandle *parmHandle = (struct parmHandle *)tgt;
2732 struct parmHeader *confRef = parmHandleRef->conf;
2733 struct parmHeader *conf = parmHandle->conf;
2734 struct section *curSectionRef;
2735 struct section *nextSectionRef;
2736 struct param *curParamRef;
2737 struct param *curParam;
2738 struct within *curWithinRef;
2739 int found;
2740 int error = 0;
2741
2742 if ((parmHandleRef->magic != PARM_MAGIC) || (parmHandle->magic != PARM_MAGIC)) {
2743 GfFatal ("GfParmCheckHandle: bad handle (%p)\n", parmHandle);
2744 return -1;
2745 }
2746
2747 /* Traverse all the reference tree */
2748 curSectionRef = GF_TAILQ_FIRST (&(confRef->rootSection->subSectionList));
2749 while (curSectionRef) {
2750 curParamRef = GF_TAILQ_FIRST (&(curSectionRef->paramList));
2751 while (curParamRef) {
2752 /* compare params */
2753 curParam = getParamByName (conf, curSectionRef->fullName, curParamRef->name, 0);
2754 if (curParam) {
2755 if (curParamRef->type != curParam->type) {
2756 GfError("GfParmCheckHandle: type mismatch for parameter \"%s\" in (\"%s\" - \"%s\")\n",
2757 curParamRef->fullName, conf->name, conf->filename);
2758 error = -1;
2759 } else if (curParamRef->type == P_NUM) {
2760 if ((curParam->valnum < curParamRef->min) || (curParam->valnum > curParamRef->max)) {
2761 GfError("GfParmCheckHandle: parameter \"%s\" out of bounds: min:%g max:%g val:%g in (\"%s\" - \"%s\")\n",
2762 curParamRef->fullName, curParamRef->min, curParamRef->max, curParam->valnum, conf->name, conf->filename);
2763 }
2764 } else {
2765 curWithinRef = GF_TAILQ_FIRST (&(curParamRef->withinList));
2766 found = 0;
2767 while (!found && curWithinRef) {
2768 if (!strcmp (curWithinRef->val, curParam->value)) {
2769 found = 1;
2770 } else {
2771 curWithinRef = GF_TAILQ_NEXT (curWithinRef, linkWithin);
2772 }
2773 }
2774 if (!found && strcmp (curParamRef->value, curParam->value)) {
2775 GfError("GfParmCheckHandle: parameter \"%s\" value:\"%s\" not allowed in (\"%s\" - \"%s\")\n",
2776 curParamRef->fullName, curParam->value, conf->name, conf->filename);
2777 }
2778 }
2779 }
2780 curParamRef = GF_TAILQ_NEXT (curParamRef, linkParam);
2781 }
2782
2783 nextSectionRef = GF_TAILQ_NEXT (curSectionRef, linkSection);
2784 while (!nextSectionRef) {
2785 nextSectionRef = curSectionRef->parent;
2786 if (!nextSectionRef) {
2787 /* Reached the root */
2788 break;
2789 }
2790 curSectionRef = nextSectionRef;
2791 nextSectionRef = GF_TAILQ_NEXT (curSectionRef, linkSection);
2792 }
2793 curSectionRef = nextSectionRef;
2794 }
2795
2796 return error;
2797 }
2798
2799
2800 /** @brief Helper function to merge a parameter into a parameter set.
2801 *
2802 * If the parameter @e param already exists in @e paramHandle, the values are overwritten with the values from @e param.
2803 * If the parameter @e param does not yet exist in @e paramHandle, it gets created. The value and restrictions (min, max, within)
2804 * in @e param are checked against the restrictions given by @e parmRef and adjusted if required.
2805 *
2806 * @ingroup paramshelper
2807 * @param[in,out] parmHandle parameter set handle
2808 * @param[in] path path to the parameter
2809 * @param[in] paramRef reference parameter for min/max boundaries or string set restrictions
2810 * @param[in] param parameter
2811 * @see GfParmMergeHandles
2812 *
2813 */
insertParamMerge(struct parmHandle * parmHandle,char * path,struct param * paramRef,struct param * param)2814 static void insertParamMerge(struct parmHandle *parmHandle, char *path, struct param *paramRef, struct param *param)
2815 {
2816 struct parmHeader *conf = parmHandle->conf;
2817 struct param *paramNew;
2818 struct within *withinRef;
2819 struct within *within;
2820 tdble num;
2821 char *str;
2822
2823 paramNew = getParamByName (conf, path, param->name, PARAM_CREATE);
2824 if (!paramNew) {
2825 return;
2826 }
2827
2828 if (param->type == P_NUM) {
2829 paramNew->type = P_NUM;
2830 FREEZ (paramNew->unit);
2831 if (param->unit) {
2832 paramNew->unit = strdup (param->unit);
2833 }
2834
2835 if (param->min < paramRef->min) {
2836 num = paramRef->min;
2837 } else {
2838 num = param->min;
2839 }
2840 paramNew->min = num;
2841
2842 if (param->max > paramRef->max) {
2843 num = paramRef->max;
2844 } else {
2845 num = param->max;
2846 }
2847 paramNew->max = num;
2848 num = param->valnum;
2849
2850 if (num < paramNew->min) {
2851 num = paramNew->min;
2852 }
2853
2854 if (num > paramNew->max) {
2855 num = paramNew->max;
2856 }
2857 paramNew->valnum = num;
2858 } else {
2859 paramNew->type = P_STR;
2860 FREEZ (paramNew->value);
2861 within = GF_TAILQ_FIRST (&(param->withinList));
2862
2863 while (within) {
2864 withinRef = GF_TAILQ_FIRST (&(paramRef->withinList));
2865 while (withinRef) {
2866 if (!strcmp (withinRef->val, within->val)) {
2867 addWithin (paramNew, within->val);
2868 break;
2869 }
2870 withinRef = GF_TAILQ_NEXT (withinRef, linkWithin);
2871 }
2872 within = GF_TAILQ_NEXT (within, linkWithin);
2873 }
2874 str = NULL;
2875 withinRef = GF_TAILQ_FIRST (&(paramRef->withinList));
2876
2877 while (withinRef) {
2878 if (!strcmp (withinRef->val, param->value)) {
2879 str = param->value;
2880 break;
2881 }
2882 withinRef = GF_TAILQ_NEXT (withinRef, linkWithin);
2883 }
2884
2885 if (!str) {
2886 str = paramRef->value;
2887 }
2888
2889 paramNew->value = strdup (str);
2890 }
2891 }
2892
2893
2894 /** @brief Helper function to insert a parameter into a parameter set.
2895 *
2896 * If the parameter @e param already exists in @e paramHandle, the values are overwritten with the values from @e param.
2897 * If the parameter @e param does not yet exist in @e paramHandle, it gets created.
2898 *
2899 * @ingroup paramshelper
2900 * @param[in,out] parmHandle parameter set handle
2901 * @param[in] path path to the parameter
2902 * @param[in] param parameter
2903 * @see GfParmMergeHandles
2904 *
2905 */
insertParam(struct parmHandle * parmHandle,char * path,struct param * param)2906 static void insertParam(struct parmHandle *parmHandle, char *path, struct param *param)
2907 {
2908 struct parmHeader *conf = parmHandle->conf;
2909 struct param *paramNew;
2910 struct within *within;
2911
2912 paramNew = getParamByName (conf, path, param->name, PARAM_CREATE);
2913 if (!paramNew) {
2914 return;
2915 }
2916
2917 if (param->type == P_NUM) {
2918 paramNew->type = P_NUM;
2919 FREEZ (paramNew->unit);
2920 if (param->unit) {
2921 paramNew->unit = strdup (param->unit);
2922 }
2923 paramNew->valnum = param->valnum;
2924 paramNew->min = param->min;
2925 paramNew->max = param->max;
2926 } else {
2927 paramNew->type = P_STR;
2928 FREEZ (paramNew->value);
2929 paramNew->value = strdup (param->value);
2930 within = GF_TAILQ_FIRST (&(param->withinList));
2931 while (within) {
2932 addWithin (paramNew, within->val);
2933 within = GF_TAILQ_NEXT (within, linkWithin);
2934 }
2935 }
2936 }
2937
2938
2939 /** @brief Merge two parameter sets into a new one, either containing parameters from @e ref, @e tgt or from both sets, the @e ref and @e tgt sets are not changed.
2940 *
2941 * Used to create a new parameter set from two exising ones, e.g. like the car category and car. If #GFPARM_MMODE_SRC
2942 * mode is used, all parameterers from @e ref will exist in the new parameter set. If #GFPARM_MMODE_DST
2943 * mode is used, all parameterers from @e tgt will exist in the new parameter set. You can combine #GFPARM_MMODE_SRC and
2944 * #GFPARM_MMODE_DST to get all parameters from both sets into the new set.
2945 *
2946 * The parameter value is taken from the @e tgt set if the parameter exists in the @e tgt set. If a parameter exists in
2947 * both sets (@e ref and @e tgt) and has different min/max values, then the greater min value and the smaller max value
2948 * is selected, so with combining parameters it is only possible to shrink the possible range. If the parameter value
2949 * does not fit the new min/max range it is adjusted.
2950 *
2951 * @ingroup paramsfile
2952 * @param[in] ref reference parameter set handle for merge
2953 * @param[in] tgt target parameter set handle for merge
2954 * @param[in] mode merge mode, can be any combination (binary or opearator, "|") of:
2955 * <br>#GFPARM_MMODE_SRC Use parameters from @e ref and modify parameters existing in @e tgt with @e tgt
2956 * <br>#GFPARM_MMODE_DST Use parameters from @e tgt and verify parameters existing in @e ref against @e ref
2957 * <br>#GFPARM_MMODE_RELSRC Release @e ref handle after the merge
2958 * <br>#GFPARM_MMODE_RELDST Release @e tgt handle after the merge
2959 * @return The new handle containing the merge
2960 * @see GfParmCheckHandle
2961 */
GfParmMergeHandles(void * ref,void * tgt,int mode)2962 void *GfParmMergeHandles(void *ref, void *tgt, int mode)
2963 {
2964 struct parmHandle *parmHandleRef = (struct parmHandle *)ref;
2965 struct parmHandle *parmHandleTgt = (struct parmHandle *)tgt;
2966 struct parmHandle *parmHandleOut;
2967 struct parmHeader *confRef = parmHandleRef->conf;
2968 struct parmHeader *confTgt = parmHandleTgt->conf;
2969 struct parmHeader *confOut;
2970 struct section *curSectionRef;
2971 struct section *nextSectionRef;
2972 struct section *curSectionTgt;
2973 struct section *nextSectionTgt;
2974 struct param *curParamRef;
2975 struct param *curParamTgt;
2976 const unsigned long parmHandleSize = sizeof (struct parmHandle);
2977
2978 GfOut ("Merging \"%s\" and \"%s\" (%s - %s)\n", confRef->filename, confTgt->filename, ((mode & GFPARM_MMODE_SRC) ? "SRC" : ""), ((mode & GFPARM_MMODE_DST) ? "DST" : ""));
2979
2980 if (parmHandleRef->magic != PARM_MAGIC) {
2981 GfFatal ("GfParmMergeHandles: bad handle (%p)\n", parmHandleRef);
2982 return NULL;
2983 }
2984 if (parmHandleTgt->magic != PARM_MAGIC) {
2985 GfFatal ("GfParmMergeHandles: bad handle (%p)\n", parmHandleTgt);
2986 return NULL;
2987 }
2988
2989 /* Conf Header creation */
2990 confOut = createParmHeader ("");
2991 if (!confOut) {
2992 GfError ("gfParmReadBuf: conf header creation failed\n");
2993 return NULL;
2994 }
2995
2996 /* Handle creation */
2997 parmHandleOut = (struct parmHandle *) calloc (1, parmHandleSize);
2998 if (!parmHandleOut) {
2999 GfError ("gfParmReadBuf: calloc (1, %lu) failed\n", parmHandleSize);
3000 parmReleaseHeader (confOut);
3001 return NULL;
3002 }
3003
3004 parmHandleOut->magic = PARM_MAGIC;
3005 parmHandleOut->conf = confOut;
3006 parmHandleOut->flag = PARM_HANDLE_FLAG_PRIVATE;
3007
3008 if (mode & GFPARM_MMODE_SRC) {
3009 /* Traverse all the reference tree */
3010 curSectionRef = GF_TAILQ_FIRST (&(confRef->rootSection->subSectionList));
3011 while (curSectionRef) {
3012 curParamRef = GF_TAILQ_FIRST (&(curSectionRef->paramList));
3013 while (curParamRef) {
3014 /* compare params */
3015 curParamTgt = getParamByName (confTgt, curSectionRef->fullName, curParamRef->name, 0);
3016 if (curParamTgt) {
3017 insertParamMerge (parmHandleOut, curSectionRef->fullName, curParamRef, curParamTgt);
3018 } else {
3019 insertParam (parmHandleOut, curSectionRef->fullName, curParamRef);
3020 }
3021 curParamRef = GF_TAILQ_NEXT (curParamRef, linkParam);
3022 }
3023 nextSectionRef = GF_TAILQ_FIRST (&(curSectionRef->subSectionList));
3024 if (nextSectionRef) {
3025 curSectionRef = nextSectionRef;
3026 } else {
3027 nextSectionRef = GF_TAILQ_NEXT (curSectionRef, linkSection);
3028 while (!nextSectionRef) {
3029 nextSectionRef = curSectionRef->parent;
3030 if (!nextSectionRef) {
3031 /* Reached the root */
3032 break;
3033 }
3034 curSectionRef = nextSectionRef;
3035 nextSectionRef = GF_TAILQ_NEXT (curSectionRef, linkSection);
3036 }
3037 curSectionRef = nextSectionRef;
3038 }
3039 }
3040 }
3041
3042 if (mode & GFPARM_MMODE_DST) {
3043 /* Traverse all the target tree */
3044 curSectionTgt = GF_TAILQ_FIRST (&(confTgt->rootSection->subSectionList));
3045 while (curSectionTgt) {
3046 curParamTgt = GF_TAILQ_FIRST (&(curSectionTgt->paramList));
3047 while (curParamTgt) {
3048 /* compare params */
3049 curParamRef = getParamByName (confRef, curSectionTgt->fullName, curParamTgt->name, 0);
3050 if (curParamRef) {
3051 insertParamMerge (parmHandleOut, curSectionTgt->fullName, curParamRef, curParamTgt);
3052 } else {
3053 insertParam (parmHandleOut, curSectionTgt->fullName, curParamTgt);
3054 }
3055 curParamTgt = GF_TAILQ_NEXT (curParamTgt, linkParam);
3056 }
3057
3058 nextSectionTgt = GF_TAILQ_FIRST (&(curSectionTgt->subSectionList));
3059 if (nextSectionTgt) {
3060 curSectionTgt = nextSectionTgt;
3061 } else {
3062 nextSectionTgt = GF_TAILQ_NEXT (curSectionTgt, linkSection);
3063 while (!nextSectionTgt) {
3064 nextSectionTgt = curSectionTgt->parent;
3065 if (!nextSectionTgt) {
3066 /* Reached the root */
3067 break;
3068 }
3069 curSectionTgt = nextSectionTgt;
3070 nextSectionTgt = GF_TAILQ_NEXT (curSectionTgt, linkSection);
3071 }
3072 curSectionTgt = nextSectionTgt;
3073 }
3074 }
3075 }
3076
3077 if (mode & GFPARM_MMODE_RELSRC) {
3078 GfParmReleaseHandle(ref);
3079 }
3080
3081 if (mode & GFPARM_MMODE_RELDST) {
3082 GfParmReleaseHandle(tgt);
3083 }
3084
3085 GF_TAILQ_INSERT_HEAD (&parmHandleList, parmHandleOut, linkHandle);
3086
3087 return (void*)parmHandleOut;
3088 }
3089
3090
3091 /** @brief Get the min and max of a numerical parameter from the parameter set @e handle.
3092 * @ingroup paramsdata
3093 * @param[in] handle parameter set handle
3094 * @param[in] path path of the parameter
3095 * @param[in] key parameter key name
3096 * @param[out] min Receives the min value
3097 * @param[out] max Receives the max value
3098 * @return 0 Ok
3099 * <br>-1 Parameter does not exist
3100 */
GfParmGetNumBoundaries(void * handle,const char * path,const char * key,tdble * min,tdble * max)3101 int GfParmGetNumBoundaries(void *handle, const char *path, const char *key, tdble *min, tdble *max)
3102 {
3103 struct parmHandle *parmHandle = (struct parmHandle *)handle;
3104 struct parmHeader *conf = parmHandle->conf;
3105 struct param *param;
3106
3107 if (parmHandle->magic != PARM_MAGIC) {
3108 GfFatal ("GfParmGetNumBoundaries: bad handle (%p)\n", parmHandle);
3109 return -1;
3110 }
3111
3112 param = getParamByName (conf, path, key, 0);
3113 if (!param || (param->type != P_NUM)) {
3114 return -1;
3115 }
3116
3117 *min = param->min;
3118 *max = param->max;
3119
3120 return 0;
3121 }
3122
3123
3124