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 (&param->withinList)) != GF_TAILQ_END (&param->withinList)) {
323 		GF_TAILQ_REMOVE (&param->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 = "&lt;"; break;
1266 			case '>':
1267 				replacement = "&gt;"; break;
1268 			case '&':
1269 				replacement = "&amp;"; break;
1270 			case '\'':
1271 				replacement = "&apos;"; break;
1272 			case '"':
1273 				replacement = "&quot;"; 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