Define simple configuration on INI files.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

213 lignes
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. }