DSL
config2.cpp
1 //@AUTOHEADER@BEGIN@
2 /***********************************************************************\
3 | Drift Standard Libraries v1.01 |
4 | Copyright 2010-2023 Drift Solutions / Indy Sams |
5 | Docs and more information available at https://www.driftsolutions.dev |
6 | This file released under the 3-clause BSD license, |
7 | see included DSL.LICENSE.TXT file for details. |
8 \***********************************************************************/
9 //@AUTOHEADER@END@
10 
11 #include <drift/dslcore.h>
12 #include <drift/config.h>
13 #include <drift/config2.h>
14 #include <drift/GenLib.h>
15 #include <math.h>
16 
17 /*
18 ConfigSection::ConfigSection() {
19 }
20 */
21 
22 ConfigSection::~ConfigSection() {
23  Clear();
24 }
25 
26 /*
27 void ConfigSection::ClearSection(ConfigSection * Scan) {
28  Scan->values.clear();
29  Scan->sections.clear();
30 }
31 
32 void ConfigSection::FreeSection(ConfigSection * Scan) {
33  ClearSection(Scan);
34 };
35 */
36 
37 void ConfigSection::Clear() {
38  for (auto& x : sections) {
39  delete x.second;
40  }
41  _sections.clear();
42  for (auto& x : values) {
43  delete x.second;
44  }
45  _values.clear();
46 }
47 
48 ConfigSection * ConfigSection::GetSection(const string& name) {
49  auto x = sections.find(name);
50  if (x != sections.end()) {
51  return x->second;
52  }
53  return NULL;
54 }
55 
56 bool ConfigSection::HasValue(const string& name) const {
57  return (values.count(name) > 0);
58  //return (values.find(name) != values.end());
59 }
60 
61 /*
62 ConfigValue ConfigSection::GetValue(const string& name) {
63  auto x = values.find(name);
64  if (x != values.end()) {
65  return *x->second;
66  }
67  return ConfigValue();
68 }
69 */
70 
71 const ConfigValue * ConfigSection::GetValue(const string& name) const {
72  auto x = values.find(name);
73  if (x != values.end()) {
74  return x->second;
75  }
76  return NULL;
77 }
78 
79 bool ConfigSection::GetValue(const string& name, ConfigValue& value) const {
80  auto x = values.find(name);
81  if (x != values.end()) {
82  value = *x->second;
83  return true;
84  }
85  return false;
86 }
87 
88 void ConfigSection::SetValue(const string& name, const ConfigValue& val) {
89  auto x = values.find(name);
90  if (x != values.end()) {
91  *x->second = val;
92  } else {
93  _values[name] = new ConfigValue(val);
94  }
95 }
96 
97 bool ConfigValue::isBool(const char * buf, bool * val) {
98  if (buf == NULL || buf[0] == 0) { return false; } // null/empty string
99 
100  if (stricmp(buf, "true") == 0 || stricmp(buf, "on") == 0) {
101  if (val != NULL) { *val = true; }
102  return true;
103  }
104  if (stricmp(buf, "false") == 0 || stricmp(buf, "off") == 0) {
105  if (val != NULL) { *val = false; }
106  return true;
107  }
108 
109  return false;
110 }
111 
112 bool ConfigValue::isInt(const char * buf) {
113  if (buf == NULL || buf[0] == 0) { return false; } // null/empty string
114 
115  int64 l = atoi64(buf);
116  if (l == 0 && strcmp(buf,"0")) {
117  return false;
118  }
119  if (l == INT64_MAX && stricmp(buf, "9223372036854775807")) {
120  return false;
121  }
122  if (l == INT64_MIN && stricmp(buf, "-9223372036854775807") && stricmp(buf, "-9223372036854775808")) {
123  return false;
124  }
125 
126  //int periodCnt=0;
127  for(int i=0; buf[i] != 0; i++) {
128  if (buf[i] == '-' && i == 0 && buf[1] != 0) {
129  continue;
130  }
131  if(buf[i] < 48 || buf[i] > 57) { // not a number
132  return false;
133  }
134  }
135 
136  return true;
137 }
138 
139 bool ConfigValue::isFloat(const char * buf) {
140  if (buf == NULL || buf[0] == 0) { return false; } // null/empty string
141 
142  double l = atof(buf);
143  if (l == 0.0 && strcmp(buf,"0.0") && strcmp(buf,"0.00")) {
144  return false;
145  }
146  if (l == HUGE_VAL || l == -HUGE_VAL) {
147  return false;
148  }
149 
150  int periodCnt=0;
151  for(int i=0; buf[i] != 0; i++) {
152  if (buf[i] == '-' && i == 0 && buf[1] != 0) {
153  //Allow a negative sign in first position as long as more follows it
154  continue;
155  }
156  if (buf[i] == '.') {
157  // Allow one period
158  periodCnt++;
159  if (periodCnt == 1) { continue; }
160  return false;
161  }
162  if (buf[i] < 48 || buf[i] > 57) {
163  // Not a number
164  return false;
165  }
166  }
167 
168  return (periodCnt == 1) ? true:false;
169 }
170 
171 ConfigSection * ConfigSection::FindOrAddSection(const string& name) {
172  ConfigSection * ret = GetSection(name);
173  if (ret != NULL) {
174  return ret;
175  }
176 
177  auto * s = new ConfigSection();
178  s->name = name;
179  _sections[name] = s;
180  return s;
181 }
182 
183 bool ConfigSection::LoadFromString(const string& config, const string& fn) {
184  const char * p = config.c_str();
185  size_t line = 0;
186  return loadFromString(&p, line, fn.c_str());
187 }
188 
189 bool ConfigSection::loadFromString(const char ** pconfig, size_t& line, const char * fn) {
190  bool long_comment = false;
191  char buf[256] = { 0 };
192 
193  const char * config = *pconfig;
194  while (*config != 0) {
195  const char * eol = strchr(config, '\n');
196  if (eol != NULL) {
197  size_t len = eol - config + 1;
198  strlcpy(buf, config, len);
199  config += len;
200  } else {
201  sstrcpy(buf, config);
202  config += strlen(config);
203  }
204  line++;
205  strtrim(buf, " \t\r\n "); // first, trim the string of unwanted chars
206  if (strlen(buf) < 2) {
207  // the minimum meaningful line would be 2 chars long, specifically };
208  continue;
209  }
210 
211  str_replaceA(buf, sizeof(buf), "\t", " "); // turn tabs into spaces
212  while (strstr(buf, " ")) {
213  str_replaceA(buf, sizeof(buf), " ", " "); // turn double-spaces into spaces
214  }
215 
216  //printf("line: %s\n",buf);
217 
218  if (long_comment) {
219  if (!strcmp(buf, "*/")) {
220  long_comment = false;
221  }
222  continue;
223  }
224 
225  if (!strncmp(buf, "/*", 2)) {
226  long_comment = true;
227  continue;
228  }
229 
230  if (stristr(buf, "#include")) {
231  char * p = stristr(buf, "#include");
232  p = strchr(p, '\"');
233  if (p) {
234  p++;
235  char *q = strchr(p, '\"');
236  if (q) {
237  q[0] = 0;
238  string data;
239  if (file_get_contents(p, data)) {
240  const char * tmpc = data.c_str();
241  size_t tmpln = 0;
242  if (!loadFromString(&tmpc, tmpln, p)) {
243  printf("ERROR: Error loading #included file '%s'\n", p);
244  break;
245  }
246  } else {
247  printf("ERROR: Error reading #included file '%s'\n", p);
248  break;
249  }
250  } else {
251  printf("ERROR: Syntax is #include \"filename\"\n");
252  break;
253  }
254  } else {
255  printf("ERROR: Syntax is #include \"filename\"\n");
256  break;
257  }
258  continue;
259  }
260 
261  if (buf[0] == '#' || !strncmp(buf, "//", 2)) {
262  continue;
263  }
264 
265  char *p = (char *)&buf + (strlen(buf) - 2); // will be " {" if beginning a section
266  if (!strcmp(p, " {")) { // open a new section
267  p[0] = 0;
268  auto sub = FindOrAddSection(buf);
269  if (!sub->loadFromString(&config, line, fn)) {
270  break;
271  }
272  continue;
273  }
274 
275  if (!strcmp(buf, "};")) { // close section
276  break;
277  }
278 
279  char * value = strchr(buf, ' ');
280  if (value == NULL) {
281  continue;
282  }
283  *value++ = 0;
284  strtrim(buf);
285  strtrim(value);
286  if (buf[0] && value[0]) {
287  auto x = values.find(buf);
288  if (x != values.end()) {
289  x->second->ParseString(value);
290  } else {
291  auto tmpv = new ConfigValue();
292  tmpv->ParseString(value);
293  _values[buf] = tmpv;
294  }
295  continue;
296  }
297 
298  printf("Unrecognized line at %s:%zu -> %s\n", fn, line, buf);
299  // some unknown line here
300  }
301 
302  *pconfig = config;
303  return true;
304 }
305 
306 
307 bool ConfigSection::LoadFromFile(FILE * fp, const string& fn) {
308  if (fp == NULL) { return false; }
309 
310  fseek64(fp, 0, SEEK_END);
311  int64 len = ftell64(fp);
312  if (len <= 0) { return false; }
313  fseek64(fp, 0, SEEK_SET);
314 
315  bool ret = false;
316  char * tmp = (char *)dsl_malloc(len + 1);
317  if (fread(tmp, len, 1, fp) == 1) {
318  tmp[len] = 0;
319  ret = LoadFromString(tmp, fn);
320  }
321  dsl_free(tmp);
322  return ret;
323 }
324 
325 bool ConfigSection::LoadFromFile(const string& filename) {
326  FILE * fp = fopen(filename.c_str(), "rb");
327  if (fp == NULL) { return false; }
328  bool ret = LoadFromFile(fp, filename);
329  fclose(fp);
330  return ret;
331 }
332 
333 void ConfigSection::writeSection(stringstream& sstr, int level, bool single) const {
334  char * pref = (char *)dsl_malloc(level+1);
335  for(int i=0; i<level; i++) { pref[i] = '\t'; }
336  pref[level]=0;
337 
338  sstr << pref << name << " {\n";
339 
340  if (!single) {
341  for (auto& x : sections) {
342  x.second->writeSection(sstr, level + 1);
343  }
344  }
345 
346  for (auto& x : values) {
347  sstr << "\t" << pref << x.first << " " << x.second->AsString() << "\n";
348  };
349 
350  sstr << pref << "};\n";
351  dsl_free(pref);
352 }
353 
354 bool ConfigSection::WriteToFile(const string& filename) const {
355  FILE * fp = fopen(filename.c_str(), "wb");
356  if (fp == NULL) { return false; }
357  bool ret = WriteToFile(fp);
358  fclose(fp);
359  return ret;
360 }
361 
362 bool ConfigSection::WriteToFile(FILE * fp) const {
363  if (!fp) { return false; }
364  string str = WriteToString();
365  if (str.length() && fwrite(str.c_str(), str.length(), 1, fp) == 1) {
366  return true;
367  }
368  return false;
369 }
370 
371 string ConfigSection::WriteToString() const {
372  stringstream sstr;
373  for (auto& x : sections) {
374  x.second->writeSection(sstr, 0);
375  }
376  //writeSection(sstr, 0, single);
377  return sstr.str();
378 }
379 
380 void ConfigSection::printSection(size_t level) const {
381  char * pref = (char *)dsl_malloc(level + 2);
382  for (int i = 0; i < level; i++) { pref[i] = '\t'; }
383  pref[level] = 0;
384 
385  printf("%sSection: %s\n", pref, name.c_str());
386  strcat(pref,"\t");
387  for (auto& x : values) {
388  switch(x.second->Type) {
389  case DS_TYPE_INT:
390  printf("%s[%s] = " I64FMT "\n", pref, x.first.c_str(), x.second->Int);
391  break;
392  case DS_TYPE_FLOAT:
393  printf("%s[%s] = %f\n", pref, x.first.c_str(), x.second->Float);
394  break;
395  case DS_TYPE_STRING:
396  printf("%s[%s] = '%s'\n", pref, x.first.c_str(), x.second->sString.c_str());
397  break;
398  case DS_TYPE_BINARY:
399  printf("%s[%s] = Binary Data\n", pref, x.first.c_str());
400  break;
401  case DS_TYPE_BOOL:
402  printf("%s[%s] = %s\n", pref, x.first.c_str(), (x.second->Int > 0) ? "true" : "false");
403  break;
404  default:
405  printf("%s[%s] = Unknown Entry Type\n", pref, x.first.c_str());
406  break;
407  }
408  }
409 
410  dsl_free(pref);
411 
412  for (auto& x : sections) {
413  x.second->printSection(level+1);
414  }
415 };
416 
417 void ConfigSection::PrintConfigTree() const {
418  printSection(0);
419 }
420 
421 ConfigValue::ConfigValue() {
422  Type = DS_TYPE_UNKNOWN;
423  Int = 0;
424 }
425 ConfigValue::~ConfigValue() {
426  Reset();
427 }
428 
429 bool ConfigValue::AsBool() const {
430  //true if it is an int > 0, double >= 1.00, or the strings "true" or "on"
431  if (Type == DS_TYPE_STRING) {
432  if (stricmp(sString.c_str(), "true") == 0 || stricmp(sString.c_str(), "on") == 0) {
433  return true;
434  }
435  } else if (Type == DS_TYPE_FLOAT) {
436  return (Float >= 1.00);
437  } else if (Type == DS_TYPE_INT || Type == DS_TYPE_BOOL) {
438  return (Int > 0);
439  }
440  return false;
441 }
442 int64 ConfigValue::AsInt() const {
443  if (Type == DS_TYPE_STRING || Type == DS_TYPE_BINARY) {
444  return atoi64(sString.c_str());
445  } else if (Type == DS_TYPE_FLOAT) {
446  return (int64)Float;
447  } else if (Type == DS_TYPE_INT || Type == DS_TYPE_BOOL) {
448  return Int;
449  }
450  return 0;
451 }
452 double ConfigValue::AsFloat() const {
453  if (Type == DS_TYPE_STRING || Type == DS_TYPE_BINARY) {
454  return atof(sString.c_str());
455  } else if (Type == DS_TYPE_FLOAT) {
456  return Float;
457  } else if (Type == DS_TYPE_INT || Type == DS_TYPE_BOOL) {
458  return (double)Int;
459  }
460  return 0;
461 }
462 string ConfigValue::AsString() const {
463  if (Type == DS_TYPE_STRING || Type == DS_TYPE_BINARY) {
464  return sString;
465  } else if (Type == DS_TYPE_INT) {
466  return mprintf("%lld", Int);
467  } else if (Type == DS_TYPE_FLOAT) {
468  return mprintf("%f", Float);
469  } else if (Type == DS_TYPE_BOOL) {
470  return (Int > 0) ? "true" : "false";
471  }
472  return "";
473 }
474 
475 void ConfigValue::SetValue(const ConfigValue& val) {
476  Reset();
477  Type = val.Type;
478  if (val.Type == DS_TYPE_STRING || val.Type == DS_TYPE_BINARY) {
479  sString = val.sString;
480  } else {
481  Int = val.Int;
482  }
483 }
484 void ConfigValue::SetValue(int64 val) {
485  Reset();
486  Type = DS_TYPE_INT;
487  Int = val;
488 }
489 void ConfigValue::SetValue(double val) {
490  Reset();
491  Type = DS_TYPE_FLOAT;
492  Float = val;
493 }
494 void ConfigValue::SetValue(const char * val) {
495  Reset();
496  Type = DS_TYPE_STRING;
497  sString = val;
498 }
499 void ConfigValue::SetValue(const string& val) {
500  Reset();
501  Type = DS_TYPE_STRING;
502  sString = val;
503 }
504 void ConfigValue::SetValue(const uint8_t * val, size_t len) {
505  Reset();
506  Type = DS_TYPE_BINARY;
507  sString.assign((const char *)val, len);
508 }
509 void ConfigValue::SetValue(bool val) {
510  Reset();
511  Type = DS_TYPE_BOOL;
512  Int = val;
513 }
514 
515 void ConfigValue::ParseString(const char * value) {
516  bool tmpbool = false;
517  if (isFloat(value)) { // number
518  SetValue(atof(value));
519  } else if (isBool(value, &tmpbool)) { // bool
520  SetValue(tmpbool);
521  } else if (isInt(value)) { // number
522  SetValue((int64)atoi64(value));
523  } else { // string
524  SetValue(value);
525  }
526 }
527 
528 /*
529 void ConfigValue::Print(FILE * fp) {
530 }
531 */
532 
533 void ConfigValue::Reset() {
534  Type = DS_TYPE_UNKNOWN;
535  memset(&Int, 0, sizeof(Int));
536  sString = "";
537 }
DSL_API_CLASS string mprintf(const string fmt,...)
Definition: dsl.cpp:294
DSL_API void DSL_CC dsl_free(void *ptr)
Definition: dsl.cpp:345
DSL_API size_t DSL_CC strlcpy(char *dst, const char *src, size_t siz)
Definition: GenLib.cpp:905
DSL_API_CLASS int DSL_CC str_replaceA(char *Str, size_t BufSize, const char *FindStr, const char *ReplStr)
Simple string replacement.
Definition: GenLib.cpp:592
DSL_API int64 DSL_CC ftell64(FILE *fp)
Cross-platform 64-bit ftell.
Definition: GenLib.cpp:527
#define sstrcpy(x, y)
Definition: GenLib.h:102
DSL_API int64 DSL_CC fseek64(FILE *fp, int64 offset, int whence)
Cross-platform 64-bit fseek.
Definition: GenLib.cpp:519
DSL_API char *DSL_CC strtrim(char *buf, const char *trim="\r\n\t ", uint8 sides=TRIM_BOTH)
Definition: GenLib.cpp:294
DSL_API_CLASS bool DSL_CC file_get_contents(const string &fn, vector< uint8 > &data, int64 maxSize=INT32_MAX)
Definition: GenLib.cpp:972