Define simple configuration on INI files.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.4 KiB

  1. #include "ini-config.h"
  2. const char* INI_ConfigGetCommandlineOption(uint8_t argc, const char* argv[], const char* val) {
  3. size_t n = strlen(val);
  4. int c = argc;
  5. while (--c > 0) {
  6. if (!strncmp(argv[c], val, n)) {
  7. if (!*(argv[c] + n) && c < argc - 1) {
  8. /* coverity treats unchecked argv as "tainted" */
  9. if (!argv[c + 1] || strlen(argv[c + 1]) > 1024)
  10. return NULL;
  11. return argv[c + 1];
  12. }
  13. if (argv[c][n] == '=')
  14. return &argv[c][n + 1];
  15. return argv[c] + n;
  16. }
  17. }
  18. return NULL;
  19. }
  20. #define INI_CONFIG_REGISTER_INT_TYPE(ID, T) \
  21. typedef bool INI_ConfigValidate##ID(T); \
  22. \
  23. typedef T INI_ConfigDeserialize##ID(const char*); \
  24. \
  25. typedef void INI_ConfigSerialize##ID(T, const char[128]); \
  26. \
  27. void INI_ConfigEnsureValid##ID(INI_ConfigItem* item, T raw_value, T default_value) { \
  28. T* dest = item->dest; \
  29. if (item->validator) { \
  30. INI_ConfigValidate##ID* validate = item->validator; \
  31. if (validate(raw_value)) { \
  32. *dest = raw_value; \
  33. return; \
  34. } \
  35. *dest = default_value; \
  36. return; \
  37. } \
  38. *dest = raw_value; \
  39. } \
  40. \
  41. void INI_ConfigLoad##ID(INI_ConfigItem* item, const char* config_path) { \
  42. static T raw_value; \
  43. static T default_value; \
  44. default_value = *((T*) item->default_value); \
  45. if (item->transformer.deserialize && item->transformer.serialize) { \
  46. INI_ConfigDeserialize##ID* deserialize = item->transformer.deserialize; \
  47. INI_ConfigSerialize##ID* serialize = item->transformer.serialize; \
  48. const char serialized_default_value[128]; \
  49. if (default_value) { \
  50. serialize(default_value, serialized_default_value); \
  51. } \
  52. char buffer[128]; \
  53. ini_gets(item->section, item->key, serialized_default_value, buffer, 128, config_path); \
  54. raw_value = deserialize(buffer); \
  55. } else { \
  56. raw_value = ini_getl(item->section, item->key, default_value, config_path); \
  57. } \
  58. INI_ConfigEnsureValid##ID(item, raw_value, default_value); \
  59. } \
  60. \
  61. INI_ConfigSaveItemResult INI_ConfigSave##ID(INI_ConfigItem* item, const char* config_path) { \
  62. T dest = *((T*) item->dest); \
  63. if (item->validator) { \
  64. INI_ConfigValidate##ID* validate = item->validator; \
  65. if (!validate(dest)) { \
  66. dest = *((const T*) item->default_value); \
  67. } \
  68. } \
  69. \
  70. if (item->transformer.deserialize && item->transformer.serialize) { \
  71. INI_ConfigSerialize##ID* serialize = item->transformer.serialize; \
  72. const char serialized_value[128]; \
  73. serialize(dest, serialized_value); \
  74. if (!ini_puts(item->section, item->key, serialized_value, config_path)) { \
  75. return -1; \
  76. } \
  77. return 0; \
  78. } \
  79. \
  80. if (!ini_putl(item->section, item->key, dest, config_path)) { \
  81. return -1; \
  82. } \
  83. \
  84. return 0; \
  85. } \
  86. \
  87. void INI_ConfigOverride##ID(INI_ConfigItem* item, uint8_t argc, const char* argv[]) { \
  88. if (!item->cmdline_option) { \
  89. return; \
  90. } \
  91. const char* cmdline_buffer; \
  92. char* rest_of_string; \
  93. static T dest; \
  94. static T config_value; \
  95. config_value = *((T*) item->dest); \
  96. if ((cmdline_buffer = INI_ConfigGetCommandlineOption(argc, argv, item->cmdline_option))) { \
  97. dest = strtol(cmdline_buffer, &rest_of_string, 10); \
  98. if (strcmp(cmdline_buffer, rest_of_string) != 0) { \
  99. INI_ConfigEnsureValid##ID(item, dest, config_value); \
  100. return; \
  101. } \
  102. } \
  103. }
  104. INI_CONFIG_REGISTER_INT_TYPE(U8, uint8_t);
  105. INI_CONFIG_REGISTER_INT_TYPE(U16, uint16_t);
  106. INI_CONFIG_REGISTER_INT_TYPE(U32, uint32_t);
  107. INI_CONFIG_REGISTER_INT_TYPE(I8, int8_t);
  108. INI_CONFIG_REGISTER_INT_TYPE(I16, int16_t);
  109. INI_CONFIG_REGISTER_INT_TYPE(I32, int32_t);
  110. typedef bool INI_ConfigLoadParamsStringValidator(const char*);
  111. void INI_ConfigEnsureValidString(INI_ConfigItem* item, const char* buffer) {
  112. if (item->validator) {
  113. INI_ConfigLoadParamsStringValidator* validator = item->validator;
  114. if (validator(buffer)) {
  115. memcpy(item->dest, buffer, item->type.size);
  116. return;
  117. }
  118. memcpy(item->dest, item->default_value, item->type.size);
  119. return;
  120. }
  121. memcpy(item->dest, buffer, item->type.size);
  122. }
  123. void INI_ConfigLoadString(INI_ConfigItem* item, const char* config_path) {
  124. char buffer[item->type.size];
  125. ini_gets(item->section, item->key, item->default_value, buffer, (int32_t) item->type.size, config_path);
  126. INI_ConfigEnsureValidString(item, buffer);
  127. }
  128. INI_ConfigSaveItemResult INI_ConfigSaveString(INI_ConfigItem* item, const char* config_path) {
  129. const char* dest = (const char*) item->dest;
  130. if (item->validator) {
  131. INI_ConfigLoadParamsStringValidator* validator = item->validator;
  132. if (!validator(dest)) {
  133. dest = (const char*) item->default_value;
  134. }
  135. }
  136. if (!ini_puts(item->section, item->key, dest, config_path)) {
  137. return INI_CONFIG_SAVE_ITEM_ERROR;
  138. }
  139. return INI_CONFIG_SAVE_ITEM_OK;
  140. }
  141. void INI_ConfigOverrideString(INI_ConfigItem* item, uint8_t argc, const char* argv[]) {
  142. if (!item->cmdline_option) {
  143. return;
  144. }
  145. const char* cmdline_buffer;
  146. if ((cmdline_buffer = INI_ConfigGetCommandlineOption(argc, argv, item->cmdline_option))) {
  147. INI_ConfigEnsureValidString(item, cmdline_buffer);
  148. }
  149. }
  150. void INI_ConfigLoad(INI_ConfigItem item[], const char* config_path) {
  151. uint8_t i;
  152. for (i = 0; item[i].type.size > 0; i += 1) {
  153. if (!item[i].type.load) {
  154. continue;
  155. }
  156. item[i].type.load(&item[i], config_path);
  157. }
  158. }
  159. INI_ConfigSaveResult INI_ConfigSave(INI_ConfigItem item[], const char* config_path) {
  160. uint8_t i;
  161. int32_t problems = 0;
  162. for (i = 0; item[i].type.size > 0; i += 1) {
  163. int32_t result = item[i].type.save ? item[i].type.save(&item[i], config_path) : 0;
  164. if (result < 0) {
  165. problems |= (1 << (int32_t) i);
  166. }
  167. }
  168. return -problems;
  169. }
  170. void INI_ConfigOverride(INI_ConfigItem item[], uint8_t argc, const char* argv[]) {
  171. uint8_t i;
  172. for (i = 0; item[i].type.size > 0; i += 1) {
  173. if (!item[i].type.override) {
  174. continue;
  175. }
  176. item[i].type.override(&item[i], argc, argv);
  177. // TODO specify command-line arg external from config item?
  178. }
  179. }
  180. INI_ConfigInitializeResult INI_ConfigInitialize(INI_ConfigItem item[], const char* config_path, uint8_t argc, const char* argv[]) {
  181. INI_ConfigLoad(item, config_path);
  182. INI_ConfigSaveResult save_result = INI_ConfigSave(item, config_path);
  183. if (save_result < 0) {
  184. //INI_LogError("config", "Sync failed! Result: %u", save_result);
  185. return INI_CONFIG_INITIALIZE_RESULT_ERROR;
  186. }
  187. INI_ConfigOverride(item, argc, argv);
  188. if (save_result > 0) {
  189. //INI_LogWarn(false, "config", "Sync encountered issues. Result: %u", save_result);
  190. return INI_CONFIG_INITIALIZE_RESULT_WARNING;
  191. }
  192. //INI_LogInfo(INI_LOG_CATEGORY_GLOBAL, "config", "Sync successful.");
  193. return INI_CONFIG_INITIALIZE_RESULT_OK;
  194. }