Various modules solving daily C developer tasks
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.

290 lines
5.9 KiB

  1. /******************************************************************************
  2. *
  3. * Copyright (c) 2017-2019 by Löwenware Ltd
  4. * Please, refer LICENSE file for legal information
  5. *
  6. ******************************************************************************/
  7. /**
  8. * @file config.c
  9. * @author Ilja Kartašov <ik@lowenware.com>
  10. * @brief
  11. *
  12. * @see https://lowenware.com/
  13. */
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <errno.h>
  18. #include <ctype.h>
  19. #include <stdbool.h>
  20. #include <cStuff/file.h>
  21. #include <cStuff/config.h>
  22. static const char
  23. m_mixed_line_indent[] = "Line indent contains tabs and spaces"
  24. , m_incomplete_key[] = "Incomplete key"
  25. , m_parser_error[] = "Parser error"
  26. , m_indent_error[] = "Indent error"
  27. , m_out_of_memory[] = "Out of memory"
  28. ;
  29. struct config_path {
  30. char *key;
  31. int size;
  32. int indent;
  33. int level;
  34. bool is_node;
  35. };
  36. struct config_ctx {
  37. struct cstuff_config cfg;
  38. struct config_path path;
  39. CStuffConfigCallback callback;
  40. void *u_ptr;
  41. };
  42. static int
  43. config_get_indent(char *line)
  44. {
  45. char chr = 0, c;
  46. int result = 0;
  47. while ((c = *line) != '\0') {
  48. if (c == ' ' || c == '\t') {
  49. if (!chr) {
  50. chr = c;
  51. }
  52. if (chr == c) {
  53. result++;
  54. } else {
  55. /* mixed indent */
  56. return -1;
  57. }
  58. } else {
  59. break;
  60. }
  61. line++;
  62. }
  63. return result;
  64. }
  65. static int
  66. config_read_line(char *line, ssize_t length, struct cstuff_config *cfg)
  67. {
  68. const char *err = NULL;
  69. CStuffConfigEvent evt = CSTUFF_CONFIG_NONE;
  70. ssize_t i = 0;
  71. int k_len = 0, v_len = 0, indent = 0;
  72. char c, *key = NULL, *val = NULL;
  73. cfg->line_num++;
  74. fprintf(stderr, "cfg:%d ", cfg->line_num);
  75. if ((indent = config_get_indent(line)) != -1) {
  76. fprintf(stderr, "indent=%d ", indent);
  77. key = &line[indent];
  78. for (i = indent; i < length; i++) {
  79. c = line[i];
  80. if (isalnum(c) || c == '_' || c == '-') {
  81. k_len++;
  82. continue;
  83. }
  84. break;
  85. }
  86. while (i < length) {
  87. switch ((c = line[i++])) {
  88. case ':':
  89. evt = CSTUFF_CONFIG_NODE;
  90. fprintf(stderr, "node! ");
  91. continue;
  92. case '=':
  93. evt = CSTUFF_CONFIG_PAIR;
  94. fprintf(stderr, "pair! ");
  95. continue;
  96. case ' ':
  97. case '\t':
  98. case '\n':
  99. continue;
  100. default:
  101. break;
  102. }
  103. break;
  104. }
  105. if (evt == CSTUFF_CONFIG_NODE) {
  106. if (line[i] && line[i] != ';') {
  107. evt = CSTUFF_CONFIG_ERROR;
  108. err = m_incomplete_key;
  109. }
  110. } else if (evt == CSTUFF_CONFIG_PAIR) {
  111. val = &line[i - 1];
  112. while (i < length) {
  113. switch (line[i++]) {
  114. case ';':
  115. case '\n':
  116. break;
  117. case ' ':
  118. case '\t':
  119. continue;
  120. default:
  121. v_len = (int) (&line[i] - val) - 1;
  122. continue;
  123. }
  124. break;
  125. }
  126. } else {
  127. evt = CSTUFF_CONFIG_ERROR;
  128. err = m_parser_error;
  129. }
  130. } else {
  131. evt = CSTUFF_CONFIG_ERROR;
  132. err = m_mixed_line_indent;
  133. }
  134. /* Key ! */
  135. if (key)
  136. key[k_len] = 0;
  137. fprintf(stderr, "\n");
  138. switch ((cfg->evt = evt)) {
  139. case CSTUFF_CONFIG_PAIR:
  140. cfg->data.pair.key = key;
  141. cfg->data.pair.val = val;
  142. cfg->data.pair.val_len = v_len;
  143. cfg->data.pair.key_len = k_len;
  144. val[v_len] = 0;
  145. return indent;
  146. case CSTUFF_CONFIG_NODE:
  147. cfg->data.node.name = key;
  148. cfg->data.node.name_len = k_len;
  149. return indent;
  150. case CSTUFF_CONFIG_ERROR:
  151. default:
  152. cfg->data.error.char_num = i;
  153. cfg->data.error.line_txt = line;
  154. cfg->data.error.err_txt = err;
  155. return -1;
  156. }
  157. }
  158. static void
  159. config_set_path_key(struct config_ctx *ctx, int line_indent, bool is_node)
  160. {
  161. struct config_path *path = &ctx->path;
  162. const char *key = ctx->cfg.data.pair.key;
  163. char *ptr;
  164. int i, len, level, k_len = ctx->cfg.data.pair.key_len;
  165. if (line_indent) {
  166. if (path->indent) {
  167. level = line_indent / path->indent;
  168. } else {
  169. level = 1;
  170. path->indent = line_indent;
  171. }
  172. } else {
  173. path->indent = 0;
  174. level = 0;
  175. }
  176. if (level > path->level && (!path->is_node ||
  177. level - path->level != path->indent)) {
  178. fprintf(stderr, "cfg:%d ((%d > %d && !%d) || %d - %d != %d)\n",
  179. ctx->cfg.line_num, level,
  180. path->level, path->is_node & 0xFF, level, path->level, path->indent);
  181. ctx->cfg.evt = CSTUFF_CONFIG_ERROR;
  182. ctx->cfg.data.error.line_txt = NULL;
  183. ctx->cfg.data.error.err_txt = m_indent_error;
  184. ctx->cfg.data.error.char_num = 0;
  185. return;
  186. }
  187. ptr = ctx->path.key;
  188. for (i = 0; i < level; i++) {
  189. int l = strcspn(ptr, ".");
  190. ptr += l;
  191. fprintf(stderr, "l = %d\n", l);
  192. }
  193. len = (ptr - ctx->path.key) + 1 + k_len + 1;
  194. if (len > ctx->path.size) {
  195. char *new_key;
  196. if (!(new_key = realloc(ctx->path.key, len))) {
  197. ctx->cfg.evt = CSTUFF_CONFIG_ERROR;
  198. ctx->cfg.data.error.line_txt = NULL;
  199. ctx->cfg.data.error.err_txt = m_out_of_memory;
  200. ctx->cfg.data.error.char_num = 0;
  201. return;
  202. }
  203. ctx->path.key = new_key;
  204. ctx->path.size = len;
  205. }
  206. if (level) {
  207. *(ptr++) = '.';
  208. } else {
  209. *ptr = 0;
  210. }
  211. strcpy(ptr, key);
  212. path->level = level;
  213. path->is_node = is_node;
  214. ctx->cfg.data.pair.key = ctx->path.key;
  215. }
  216. static int
  217. config_get_line(char *line, ssize_t length, void *p_ctx)
  218. {
  219. int result;
  220. struct config_ctx *ctx = (struct config_ctx *)p_ctx;
  221. memset(&ctx->cfg.data, 0, sizeof (ctx->cfg.data));
  222. if ((result = config_read_line(line, length, &ctx->cfg)) != -1) {
  223. bool is_node = (ctx->cfg.evt == CSTUFF_CONFIG_NODE);
  224. /* result == indent */
  225. config_set_path_key(ctx, result, is_node);
  226. } else {
  227. fprintf(stderr, "cfg: read line failed %d\n", result);
  228. }
  229. result = ctx->callback(&ctx->cfg, ctx->u_ptr);
  230. return result;
  231. }
  232. CStuffRetcode
  233. cstuff_config_parse(const char *file, CStuffConfigCallback callback, void *u_ptr)
  234. {
  235. CStuffRetcode rc;
  236. struct config_ctx ctx;
  237. memset(&ctx.cfg, 0, sizeof (ctx.cfg));
  238. memset(&ctx.path, 0, sizeof (ctx.path));
  239. if (!(ctx.path.key = malloc(CSTUFF_CONFIG_PATH_SIZE))) {
  240. return CSTUFF_MALLOC_ERROR;
  241. } else {
  242. ctx.path.size = CSTUFF_CONFIG_PATH_SIZE;
  243. }
  244. ctx.u_ptr = u_ptr;
  245. ctx.callback = callback;
  246. rc = cstuff_file_get_lines(file, config_get_line, (void *)&ctx);
  247. if (rc) {
  248. fprintf(stderr, "cfg: get lines failed %d (%s)\n", rc, strerror(errno));
  249. }
  250. if (ctx.path.key)
  251. free(ctx.path.key);
  252. return rc;
  253. }