raconf.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. //
  2. // Athena style config parser
  3. // (would be better to have "one" implementation instead of .. 4 :)
  4. //
  5. //
  6. // Author: Florian Wilkemeyer <fw@f-ws.de>
  7. //
  8. // Copyright (c) RAthena Project (www.rathena.org) - Licensed under GNU GPL
  9. // For more information, see LICENCE in the main folder
  10. //
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include "cbasetypes.h"
  15. #include "showmsg.h"
  16. #include "db.h"
  17. #include "malloc.h"
  18. #include "raconf.h"
  19. #define SECTION_LEN 32
  20. #define VARNAME_LEN 64
  21. struct raconf {
  22. DBMap *db;
  23. };
  24. struct conf_value{
  25. int64 intval;
  26. bool bval;
  27. double floatval;
  28. size_t strval_len; // not includung \0
  29. char strval[16];
  30. };
  31. static struct conf_value *makeValue(const char *key, char *val, size_t val_len){
  32. struct conf_value *v;
  33. /* size_t sz;
  34. sz = sizeof(struct conf_value);
  35. if(val_len >= sizeof(v->strval))
  36. sz += (val_len - sizeof(v->strval) + 1);*/
  37. v = (struct conf_value*)aCalloc(1, sizeof(struct conf_value));
  38. if(v == NULL){
  39. ShowFatalError("raconf: makeValue => Out of Memory while allocating new node.\n");
  40. return NULL;
  41. }
  42. memcpy(v->strval, val, val_len);
  43. v->strval[val_len+1] = '\0';
  44. v->strval_len = val_len;
  45. // Parse boolean value:
  46. if((val_len == 4) && (strncmpi("true", val, 4) == 0))
  47. v->bval = true;
  48. else if((val_len == 3) && (strncmpi("yes", val, 3) == 0))
  49. v->bval = true;
  50. else if((val_len == 3) && (strncmpi("oui", val, 3) == 0))
  51. v->bval = true;
  52. else if((val_len == 2) && (strncmpi("si", val, 2) == 0))
  53. v->bval = true;
  54. else if((val_len == 2) && (strncmpi("ja", val, 2) == 0))
  55. v->bval = true;
  56. else if((val_len == 1) && (*val == '1'))
  57. v->bval = true;
  58. else if((val_len == 5) && (strncmpi("false", val, 5) == 0))
  59. v->bval = false;
  60. else if((val_len == 2) && (strncmpi("no", val, 2) == 0))
  61. v->bval = false;
  62. else if((val_len == 3) && (strncmpi("non", val, 3) == 0))
  63. v->bval = false;
  64. else if((val_len == 4) && (strncmpi("nein", val, 4) == 0))
  65. v->bval = false;
  66. else if((val_len == 1) && (*val == '0'))
  67. v->bval = false;
  68. else
  69. v->bval = false; // assume false.
  70. // Parse number
  71. // Supported formats:
  72. // prefix: 0x hex .
  73. // postix: h for hex
  74. // b for bin (dual)
  75. if( (val_len >= 1 && (val[val_len] == 'h')) || (val_len >= 2 && (val[0] == '0' && val[1] == 'x')) ){//HEX!
  76. if(val[val_len] == 'h'){
  77. val[val_len]= '\0';
  78. v->intval = strtoull(val, NULL, 16);
  79. val[val_len] = 'h';
  80. }else
  81. v->intval = strtoull(&val[2], NULL, 16);
  82. }else if( val_len >= 1 && (val[val_len] == 'b') ){ //BIN
  83. val[val_len] = '\0';
  84. v->intval = strtoull(val, NULL, 2);
  85. val[val_len] = 'b';
  86. }else if( *val >='0' && *val <= '9'){ // begins with normal digit, so assume its dec.
  87. // is it float?
  88. bool is_float = false;
  89. char *p;
  90. for(p = val; *p != '\0'; p++){
  91. if(*p == '.'){
  92. v->floatval = strtod(val, NULL);
  93. v->intval = (int64) v->floatval;
  94. is_float = true;
  95. break;
  96. }
  97. }
  98. if(is_float == false){
  99. v->intval = strtoull(val, NULL, 10);
  100. v->floatval = (double) v->intval;
  101. }
  102. }else{
  103. // Everything else: lets use boolean for fallback
  104. if(v->bval == true)
  105. v->intval = 1;
  106. else
  107. v->intval = 0;
  108. }
  109. return v;
  110. }//end: makeValue()
  111. static bool configParse(raconf inst, const char *fileName){
  112. FILE *fp;
  113. char line[4096];
  114. char currentSection[SECTION_LEN];
  115. char *p;
  116. char c;
  117. int linecnt;
  118. size_t linelen;
  119. size_t currentSection_len;
  120. fp = fopen(fileName, "r");
  121. if(fp == NULL){
  122. ShowError("configParse: cannot open '%s' for reading.\n", fileName);
  123. return false;
  124. }
  125. // Start with empty section:
  126. currentSection[0] = '\0';
  127. currentSection_len = 0;
  128. //
  129. linecnt = 0;
  130. while(1){
  131. linecnt++;
  132. if(fgets(line, sizeof(line), fp) != line)
  133. break;
  134. linelen = strlen(line);
  135. p = line;
  136. // Skip whitespaces from beginning (space and tab)
  137. _line_begin_skip_whities:
  138. c = *p;
  139. if(c == ' ' || c == '\t'){
  140. p++;
  141. linelen--;
  142. goto _line_begin_skip_whities;
  143. }
  144. // Remove linebreaks as (cr or lf) and whitespaces from line end!
  145. _line_end_skip_whities_and_breaks:
  146. c = p[linelen-1];
  147. if(c == '\r' || c == '\n' || c == ' ' || c == '\t'){
  148. p[--linelen] = '\0';
  149. goto _line_end_skip_whities_and_breaks;
  150. }
  151. // Empty line?
  152. // or line starts with comment (commented out)?
  153. if(linelen == 0 || (p[0] == '/' && p[1] == '/') || p[0] == ';')
  154. continue;
  155. // Variable names can contain:
  156. // A-Za-z-_.0-9
  157. //
  158. // Sections start with [ .. ] (INI Style)
  159. //
  160. c = *p;
  161. // check what we have.. :)
  162. if(c == '['){ // got section!
  163. // Got Section!
  164. // Search for ]
  165. char *start = (p+1);
  166. while(1){
  167. ++p;
  168. c = *p;
  169. if(c == '\0'){
  170. ShowError("Syntax Error: unterminated Section name in %s:%u (expected ']')\n", fileName, linecnt);
  171. fclose(fp);
  172. return false;
  173. }else if(c == ']'){ // closing backet (section name termination)
  174. if( (p - start + 1) > (sizeof(currentSection) ) ){
  175. ShowError("Syntax Error: Section name in %s:%u is too large (max Supported length: %u chars)\n", fileName, linecnt, sizeof(currentSection)-1);
  176. fclose(fp);
  177. return false;
  178. }
  179. // Set section!
  180. *p = '\0'; // add termination here.
  181. memcpy(currentSection, start, (p-start)+1 ); // we'll copy \0, too! (we replaced the ] backet with \0.)
  182. currentSection_len = (p-start);
  183. break;
  184. }else if( (c >= '0' && c <= '9') || (c == '-') || (c == ' ') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
  185. // skip .. (allowed char / specifier)
  186. continue;
  187. }else{
  188. ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Section name.\n", c, fileName, linecnt, (p-line));
  189. fclose(fp);
  190. return false;
  191. }
  192. }//endwhile: parse section name
  193. }else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
  194. // Got variable!
  195. // Search for '=' or ':' wich termiantes the name
  196. char *start = p;
  197. char *valuestart = NULL;
  198. size_t start_len;
  199. while(1){
  200. ++p;
  201. c = *p;
  202. if(c == '\0'){
  203. ShowError("Syntax Error: unterminated Variable name in %s:%u\n", fileName, linecnt);
  204. fclose(fp);
  205. return false;
  206. }else if( (c == '=') || (c == ':') ){
  207. // got name termination
  208. *p = '\0'; // Terminate it so (start) will hold the pointer to the name.
  209. break;
  210. }else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
  211. // skip .. allowed char
  212. continue;
  213. }else{
  214. ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Variable name.\n", c, fileName, linecnt, (p-line));
  215. fclose(fp);
  216. return false;
  217. }
  218. }//endwhile: parse var name
  219. start_len = (p-start);
  220. if(start_len >= VARNAME_LEN){
  221. ShowError("%s:%u Variable length exceeds limit of %u Characters.\n", fileName, linecnt, VARNAME_LEN-1);
  222. fclose(fp);
  223. return false;
  224. }else if(start_len == 0){
  225. ShowError("%s:%u Empty Variable name is not allowed.\n", fileName, linecnt);
  226. fclose(fp);
  227. return false;
  228. }
  229. valuestart = (p+1);
  230. // Skip whitespace from begin of value (tab and space)
  231. _skip_value_begin_whities:
  232. c = *valuestart;
  233. if(c == ' ' || c == '\t'){
  234. valuestart++;
  235. goto _skip_value_begin_whities;
  236. }
  237. // Scan for value termination,
  238. // wich can be \0 or comment start (// or ; (INI) )
  239. //
  240. p = valuestart;
  241. while(1){
  242. c = *p;
  243. if(c == '\0'){
  244. // Terminated by line end.
  245. break;
  246. }else if(c == '/' && p[1] == '/'){
  247. // terminated by c++ style comment.
  248. *p = '\0';
  249. break;
  250. }else if(c == ';'){
  251. // terminated by ini style comment.
  252. *p = '\0';
  253. break;
  254. }
  255. p++;
  256. }//endwhile: search var value end.
  257. // Strip whitespaces from end of value.
  258. if(valuestart != p){ // not empty!
  259. p--;
  260. _strip_value_end_whities:
  261. c = *p;
  262. if(c == ' ' || c == '\t'){
  263. *p = '\0';
  264. p--;
  265. goto _strip_value_end_whities;
  266. }
  267. p++;
  268. }
  269. // Buildin Hook:
  270. if( stricmp(start, "import") == 0){
  271. if( configParse(inst, valuestart) != true){
  272. ShowError("%s:%u - Import of '%s' failed!\n", fileName, linecnt, valuestart);
  273. }
  274. }else{
  275. // put it to db.
  276. struct conf_value *v, *o;
  277. char key[ (SECTION_LEN+VARNAME_LEN+1+1) ]; //+1 for delimiter, +1 for termination.
  278. size_t section_len;
  279. if(*currentSection == '\0'){ // empty / none
  280. strncpy(key, "<unnamed>",9);
  281. section_len = 9;
  282. }else{
  283. strncpy(key, currentSection, currentSection_len);
  284. section_len = currentSection_len;
  285. }
  286. key[section_len] = '.'; // Delim
  287. strncpy(&key[section_len+1], start, start_len);
  288. key[section_len + start_len + 1] = '\0';
  289. v = makeValue(key, valuestart, (p-valuestart) );
  290. // Try to get the old one before
  291. o = strdb_get(inst->db, key);
  292. if(o != NULL){
  293. strdb_remove(inst->db, key);
  294. aFree(o); //
  295. }
  296. strdb_put( inst->db, key, v);
  297. }
  298. }else{
  299. ShowError("Syntax Error: unexpected Character '%c' in %s:%u (offset %u)\n", c, fileName, linecnt, (p-line) );
  300. fclose(fp);
  301. return false;
  302. }
  303. }
  304. fclose(fp);
  305. return true;
  306. }//end: configParse()
  307. #define MAKEKEY(dest, section, key) { size_t section_len, key_len; \
  308. if(section == NULL || *section == '\0'){ \
  309. strncpy(dest, "<unnamed>", 9); \
  310. section_len = 9; \
  311. }else{ \
  312. section_len = strlen(section); \
  313. strncpy(dest, section, section_len); \
  314. } \
  315. \
  316. dest[section_len] = '.'; \
  317. \
  318. key_len = strlen(key); \
  319. strncpy(&dest[section_len+1], key, key_len); \
  320. dest[section_len + key_len + 1] = '\0'; \
  321. }
  322. raconf raconf_parse(const char *file_name){
  323. struct raconf *rc;
  324. rc = aCalloc(1, sizeof(struct raconf) );
  325. if(rc == NULL){
  326. ShowFatalError("raconf_parse: failed to allocate memory for new handle\n");
  327. return NULL;
  328. }
  329. rc->db = strdb_alloc(DB_OPT_BASE | DB_OPT_DUP_KEY, 98);
  330. //
  331. if(configParse(rc, file_name) != true){
  332. ShowError("Failed to Parse Configuration file '%s'\n", file_name);
  333. }
  334. return rc;
  335. }//end: raconf_parse()
  336. void raconf_destroy(raconf rc){
  337. DBIterator *iter;
  338. struct conf_value *v;
  339. // Clear all entrys in db.
  340. iter = db_iterator(rc->db);
  341. for( v = (struct conf_value*)dbi_first(iter); dbi_exists(iter); v = (struct conf_value*)dbi_next(iter) ){
  342. aFree(v);
  343. }
  344. dbi_destroy(iter);
  345. db_destroy(rc->db);
  346. aFree(rc);
  347. }//end: raconf_destroy()
  348. bool raconf_getbool(raconf rc, const char *section, const char *key, bool _default){
  349. char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
  350. struct conf_value *v;
  351. MAKEKEY(keystr, section, key);
  352. v = strdb_get(rc->db, keystr);
  353. if(v == NULL)
  354. return _default;
  355. else
  356. return v->bval;
  357. }//end: raconf_getbool()
  358. float raconf_getfloat(raconf rc,const char *section, const char *key, float _default){
  359. char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
  360. struct conf_value *v;
  361. MAKEKEY(keystr, section, key);
  362. v = strdb_get(rc->db, keystr);
  363. if(v == NULL)
  364. return _default;
  365. else
  366. return (float)v->floatval;
  367. }//end: raconf_getfloat()
  368. int64 raconf_getint(raconf rc, const char *section, const char *key, int64 _default){
  369. char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
  370. struct conf_value *v;
  371. MAKEKEY(keystr, section, key);
  372. v = strdb_get(rc->db, keystr);
  373. if(v == NULL)
  374. return _default;
  375. else
  376. return v->intval;
  377. }//end: raconf_getint()
  378. const char* raconf_getstr(raconf rc, const char *section, const char *key, const char *_default){
  379. char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
  380. struct conf_value *v;
  381. MAKEKEY(keystr, section, key);
  382. v = strdb_get(rc->db, keystr);
  383. if(v == NULL)
  384. return _default;
  385. else
  386. return v->strval;
  387. }//end: raconf_getstr()
  388. bool raconf_getboolEx(raconf rc, const char *section, const char *fallback_section, const char *key, bool _default){
  389. char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
  390. struct conf_value *v;
  391. MAKEKEY(keystr, section, key);
  392. v = strdb_get(rc->db, keystr);
  393. if(v == NULL){
  394. MAKEKEY(keystr, fallback_section, key);
  395. v = strdb_get(rc->db, keystr);
  396. if(v == NULL){
  397. return _default;
  398. }else{
  399. return v->bval;
  400. }
  401. }else{
  402. return v->bval;
  403. }
  404. }//end: raconf_getboolEx()
  405. float raconf_getfloatEx(raconf rc,const char *section, const char *fallback_section, const char *key, float _default){
  406. char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
  407. struct conf_value *v;
  408. MAKEKEY(keystr, section, key);
  409. v = strdb_get(rc->db, keystr);
  410. if(v == NULL){
  411. MAKEKEY(keystr, fallback_section, key);
  412. v = strdb_get(rc->db, keystr);
  413. if(v == NULL){
  414. return _default;
  415. }else{
  416. return (float)v->floatval;
  417. }
  418. }else{
  419. return (float)v->floatval;
  420. }
  421. }//end: raconf_getfloatEx()
  422. int64 raconf_getintEx(raconf rc, const char *section, const char *fallback_section, const char *key, int64 _default){
  423. char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
  424. struct conf_value *v;
  425. MAKEKEY(keystr, section, key);
  426. v = strdb_get(rc->db, keystr);
  427. if(v == NULL){
  428. MAKEKEY(keystr, fallback_section, key);
  429. v = strdb_get(rc->db, keystr);
  430. if(v == NULL){
  431. return _default;
  432. }else{
  433. return v->intval;
  434. }
  435. }else{
  436. return v->intval;
  437. }
  438. }//end: raconf_getintEx()
  439. const char* raconf_getstrEx(raconf rc, const char *section, const char *fallback_section, const char *key, const char *_default){
  440. char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
  441. struct conf_value *v;
  442. MAKEKEY(keystr, section, key);
  443. v = strdb_get(rc->db, keystr);
  444. if(v == NULL){
  445. MAKEKEY(keystr, fallback_section, key);
  446. v = strdb_get(rc->db, keystr);
  447. if(v == NULL){
  448. return _default;
  449. }else{
  450. return v->strval;
  451. }
  452. }else{
  453. return v->strval;
  454. }
  455. }//end: raconf_getstrEx()