Tiny INI parser

nini.c 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. ///
  2. /// nini - Nano INI parser
  3. ///
  4. /// Written by MightyPork, 2018
  5. /// MIT license
  6. ///
  7. #include "nini.h"
  8. enum nini_state {
  9. NINI_IDLE,
  10. NINI_SECTION,
  11. NINI_KEY,
  12. NINI_VALUE,
  13. NINI_COMMENT,
  14. };
  15. static struct {
  16. uint8_t section_i;
  17. char section[INI_KEY_MAX];
  18. uint8_t key_i;
  19. char key[INI_KEY_MAX];
  20. uint8_t value_i;
  21. char value[INI_VALUE_MAX];
  22. bool val_last_space;
  23. IniParserCallback cb;
  24. void *userdata;
  25. enum nini_state state;
  26. } nini;
  27. void ini_parse_begin(IniParserCallback callback, void *userData)
  28. {
  29. ini_parse_reset();
  30. nini.cb = callback;
  31. nini.userdata = userData;
  32. }
  33. void ini_parse(const char *data, size_t len)
  34. {
  35. for (; len > 0; len--) {
  36. char c = *data++;
  37. if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
  38. if (nini.state != NINI_VALUE && nini.state != NINI_COMMENT)
  39. continue;
  40. }
  41. switch (nini.state) {
  42. case NINI_IDLE:
  43. if (c == '[') {
  44. nini.state = NINI_SECTION;
  45. nini.section_i = 0;
  46. }
  47. else if (c == '#') {
  48. nini.state = NINI_COMMENT;
  49. }
  50. else {
  51. nini.state = NINI_KEY;
  52. nini.key_i = 0;
  53. nini.value_i = 0;
  54. nini.val_last_space = false;
  55. nini.key[nini.key_i++] = c;
  56. }
  57. break;
  58. case NINI_COMMENT:
  59. if (c == '\n' || c == '\r') {
  60. nini.state = NINI_IDLE;
  61. }
  62. break;
  63. case NINI_SECTION:
  64. if (c == ']') {
  65. nini.section[nini.section_i] = 0;
  66. nini.state = NINI_COMMENT; // discard to EOL
  67. break;
  68. }
  69. else if (nini.section_i < INI_KEY_MAX - 1) {
  70. nini.section[nini.section_i++] = c;
  71. }
  72. break;
  73. case NINI_KEY:
  74. if (c == '=') {
  75. nini.key[nini.key_i] = 0;
  76. nini.state = NINI_VALUE;
  77. }
  78. else if (nini.key_i < INI_KEY_MAX - 1) {
  79. nini.key[nini.key_i++] = c;
  80. }
  81. break;
  82. case NINI_VALUE:
  83. switch (c) {
  84. case ' ':
  85. case '\t':
  86. if (nini.value_i) nini.val_last_space = true;
  87. break;
  88. case '\r':
  89. case '\n':
  90. nini.value[nini.value_i] = 0;
  91. nini.state = NINI_IDLE;
  92. nini.cb(nini.section, nini.key, nini.value, nini.userdata);
  93. break;
  94. default:
  95. if (nini.val_last_space && nini.value_i < INI_VALUE_MAX - 1) {
  96. nini.value[nini.value_i++] = ' ';
  97. }
  98. if (nini.value_i < INI_VALUE_MAX - 1) {
  99. nini.value[nini.value_i++] = c;
  100. }
  101. nini.val_last_space = false;
  102. }
  103. }
  104. }
  105. }
  106. void *ini_parse_end(void)
  107. {
  108. if (nini.state == NINI_VALUE) {
  109. nini.value[nini.value_i] = 0;
  110. nini.state = NINI_IDLE;
  111. nini.cb(nini.section, nini.key, nini.value, nini.userdata);
  112. }
  113. return nini.userdata;
  114. }
  115. void ini_parse_file(const char *text, size_t len, IniParserCallback callback, void *userData)
  116. {
  117. ini_parse_begin(callback, userData);
  118. ini_parse(text, len);
  119. ini_parse_end();
  120. }
  121. void ini_parse_reset(void)
  122. {
  123. nini.state = NINI_IDLE;
  124. }