Define simple configuration on INI files.
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

213 рядки
6.2 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(I32, int32_t);
  107. typedef bool INI_ConfigLoadParamsStringValidator(const char*);
  108. void INI_ConfigEnsureValidString(INI_ConfigItem* item, const char* buffer) {
  109. if (item->validator) {
  110. INI_ConfigLoadParamsStringValidator* validator = item->validator;
  111. if (validator(buffer)) {
  112. memcpy(item->dest, buffer, item->fns.size);
  113. return;
  114. }
  115. memcpy(item->dest, item->default_value, item->fns.size);
  116. return;
  117. }
  118. memcpy(item->dest, buffer, item->fns.size);
  119. }
  120. void INI_ConfigLoadString(INI_ConfigItem* item, const char* config_path) {
  121. char buffer[item->fns.size];
  122. ini_gets(item->section, item->key, item->default_value, buffer, (int32_t) item->fns.size, config_path);
  123. INI_ConfigEnsureValidString(item, buffer);
  124. }
  125. INI_ConfigSaveItemResult INI_ConfigSaveString(INI_ConfigItem* item, const char* config_path) {
  126. const char* dest = (const char*) item->dest;
  127. if (item->validator) {
  128. INI_ConfigLoadParamsStringValidator* validator = item->validator;
  129. if (!validator(dest)) {
  130. dest = (const char*) item->default_value;
  131. }
  132. }
  133. if (!ini_puts(item->section, item->key, dest, config_path)) {
  134. return INI_CONFIG_SAVE_ITEM_ERROR;
  135. }
  136. return INI_CONFIG_SAVE_ITEM_OK;
  137. }
  138. void INI_ConfigOverrideString(INI_ConfigItem* item, uint8_t argc, const char* argv[]) {
  139. if (!item->cmdline_option) {
  140. return;
  141. }
  142. const char* cmdline_buffer;
  143. if ((cmdline_buffer = INI_ConfigGetCommandlineOption(argc, argv, item->cmdline_option))) {
  144. INI_ConfigEnsureValidString(item, cmdline_buffer);
  145. }
  146. }
  147. void INI_ConfigLoad(INI_ConfigItem item[], const char* config_path) {
  148. uint8_t i;
  149. for (i = 0; item[i].fns.size > 0; i += 1) {
  150. if (!item[i].fns.load) {
  151. continue;
  152. }
  153. item[i].fns.load(&item[i], config_path);
  154. }
  155. }
  156. INI_ConfigSaveResult INI_ConfigSave(INI_ConfigItem item[], const char* config_path) {
  157. uint8_t i;
  158. int32_t problems = 0;
  159. for (i = 0; item[i].fns.size > 0; i += 1) {
  160. int32_t result = item[i].fns.save ? item[i].fns.save(&item[i], config_path) : 0;
  161. if (result < 0) {
  162. problems |= (1 << (int32_t) i);
  163. }
  164. }
  165. return -problems;
  166. }
  167. void INI_ConfigOverride(INI_ConfigItem item[], uint8_t argc, const char* argv[]) {
  168. uint8_t i;
  169. for (i = 0; item[i].fns.size > 0; i += 1) {
  170. if (!item[i].fns.override) {
  171. continue;
  172. }
  173. item[i].fns.override(&item[i], argc, argv);
  174. // TODO specify command-line arg external from config item?
  175. }
  176. }
  177. INI_ConfigInitializeResult INI_ConfigInitialize(INI_ConfigItem item[], const char* config_path, uint8_t argc, const char* argv[]) {
  178. INI_ConfigLoad(item, config_path);
  179. INI_ConfigSaveResult save_result = INI_ConfigSave(item, config_path);
  180. if (save_result < 0) {
  181. //INI_LogError("config", "Sync failed! Result: %u", save_result);
  182. return INI_CONFIG_INITIALIZE_RESULT_ERROR;
  183. }
  184. INI_ConfigOverride(item, argc, argv);
  185. if (save_result > 0) {
  186. //INI_LogWarn(false, "config", "Sync encountered issues. Result: %u", save_result);
  187. return INI_CONFIG_INITIALIZE_RESULT_WARNING;
  188. }
  189. //INI_LogInfo(INI_LOG_CATEGORY_GLOBAL, "config", "Sync successful.");
  190. return INI_CONFIG_INITIALIZE_RESULT_OK;
  191. }