config.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. #define LOG_TAG "bt_osi_config"
  14. #include "esp_system.h"
  15. #include "nvs_flash.h"
  16. #include "nvs.h"
  17. #include <ctype.h>
  18. #include <errno.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include "bt_common.h"
  23. #include "osi/allocator.h"
  24. #include "osi/config.h"
  25. #include "osi/list.h"
  26. #define CONFIG_FILE_MAX_SIZE (1536)//1.5k
  27. #define CONFIG_FILE_DEFAULE_LENGTH (2048)
  28. #define CONFIG_KEY "bt_cfg_key"
  29. typedef struct {
  30. char *key;
  31. char *value;
  32. } entry_t;
  33. typedef struct {
  34. char *name;
  35. list_t *entries;
  36. } section_t;
  37. struct config_t {
  38. list_t *sections;
  39. };
  40. // Empty definition; this type is aliased to list_node_t.
  41. struct config_section_iter_t {};
  42. static void config_parse(nvs_handle_t fp, config_t *config);
  43. static section_t *section_new(const char *name);
  44. static void section_free(void *ptr);
  45. static section_t *section_find(const config_t *config, const char *section);
  46. static entry_t *entry_new(const char *key, const char *value);
  47. static void entry_free(void *ptr);
  48. static entry_t *entry_find(const config_t *config, const char *section, const char *key);
  49. config_t *config_new_empty(void)
  50. {
  51. config_t *config = osi_calloc(sizeof(config_t));
  52. if (!config) {
  53. OSI_TRACE_ERROR("%s unable to allocate memory for config_t.\n", __func__);
  54. goto error;
  55. }
  56. config->sections = list_new(section_free);
  57. if (!config->sections) {
  58. OSI_TRACE_ERROR("%s unable to allocate list for sections.\n", __func__);
  59. goto error;
  60. }
  61. return config;
  62. error:;
  63. config_free(config);
  64. return NULL;
  65. }
  66. config_t *config_new(const char *filename)
  67. {
  68. assert(filename != NULL);
  69. config_t *config = config_new_empty();
  70. if (!config) {
  71. return NULL;
  72. }
  73. esp_err_t err;
  74. nvs_handle_t fp;
  75. err = nvs_open(filename, NVS_READWRITE, &fp);
  76. if (err != ESP_OK) {
  77. if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
  78. OSI_TRACE_ERROR("%s: NVS not initialized. "
  79. "Call nvs_flash_init before initializing bluetooth.", __func__);
  80. } else {
  81. OSI_TRACE_ERROR("%s unable to open NVS namespace '%s'\n", __func__, filename);
  82. }
  83. config_free(config);
  84. return NULL;
  85. }
  86. config_parse(fp, config);
  87. nvs_close(fp);
  88. return config;
  89. }
  90. void config_free(config_t *config)
  91. {
  92. if (!config) {
  93. return;
  94. }
  95. list_free(config->sections);
  96. osi_free(config);
  97. }
  98. bool config_has_section(const config_t *config, const char *section)
  99. {
  100. assert(config != NULL);
  101. assert(section != NULL);
  102. return (section_find(config, section) != NULL);
  103. }
  104. bool config_has_key(const config_t *config, const char *section, const char *key)
  105. {
  106. assert(config != NULL);
  107. assert(section != NULL);
  108. assert(key != NULL);
  109. return (entry_find(config, section, key) != NULL);
  110. }
  111. bool config_has_key_in_section(config_t *config, const char *key, char *key_value)
  112. {
  113. OSI_TRACE_DEBUG("key = %s, value = %s", key, key_value);
  114. for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
  115. const section_t *section = (const section_t *)list_node(node);
  116. for (const list_node_t *node = list_begin(section->entries); node != list_end(section->entries); node = list_next(node)) {
  117. entry_t *entry = list_node(node);
  118. OSI_TRACE_DEBUG("entry->key = %s, entry->value = %s", entry->key, entry->value);
  119. if (!strcmp(entry->key, key) && !strcmp(entry->value, key_value)) {
  120. OSI_TRACE_DEBUG("%s, the irk aready in the flash.", __func__);
  121. return true;
  122. }
  123. }
  124. }
  125. return false;
  126. }
  127. int config_get_int(const config_t *config, const char *section, const char *key, int def_value)
  128. {
  129. assert(config != NULL);
  130. assert(section != NULL);
  131. assert(key != NULL);
  132. entry_t *entry = entry_find(config, section, key);
  133. if (!entry) {
  134. return def_value;
  135. }
  136. char *endptr;
  137. int ret = strtol(entry->value, &endptr, 0);
  138. return (*endptr == '\0') ? ret : def_value;
  139. }
  140. bool config_get_bool(const config_t *config, const char *section, const char *key, bool def_value)
  141. {
  142. assert(config != NULL);
  143. assert(section != NULL);
  144. assert(key != NULL);
  145. entry_t *entry = entry_find(config, section, key);
  146. if (!entry) {
  147. return def_value;
  148. }
  149. if (!strcmp(entry->value, "true")) {
  150. return true;
  151. }
  152. if (!strcmp(entry->value, "false")) {
  153. return false;
  154. }
  155. return def_value;
  156. }
  157. const char *config_get_string(const config_t *config, const char *section, const char *key, const char *def_value)
  158. {
  159. assert(config != NULL);
  160. assert(section != NULL);
  161. assert(key != NULL);
  162. entry_t *entry = entry_find(config, section, key);
  163. if (!entry) {
  164. return def_value;
  165. }
  166. return entry->value;
  167. }
  168. void config_set_int(config_t *config, const char *section, const char *key, int value)
  169. {
  170. assert(config != NULL);
  171. assert(section != NULL);
  172. assert(key != NULL);
  173. char value_str[32] = { 0 };
  174. sprintf(value_str, "%d", value);
  175. config_set_string(config, section, key, value_str, false);
  176. }
  177. void config_set_bool(config_t *config, const char *section, const char *key, bool value)
  178. {
  179. assert(config != NULL);
  180. assert(section != NULL);
  181. assert(key != NULL);
  182. config_set_string(config, section, key, value ? "true" : "false", false);
  183. }
  184. void config_set_string(config_t *config, const char *section, const char *key, const char *value, bool insert_back)
  185. {
  186. section_t *sec = section_find(config, section);
  187. if (!sec) {
  188. sec = section_new(section);
  189. if (insert_back) {
  190. list_append(config->sections, sec);
  191. } else {
  192. list_prepend(config->sections, sec);
  193. }
  194. }
  195. for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
  196. entry_t *entry = list_node(node);
  197. if (!strcmp(entry->key, key)) {
  198. osi_free(entry->value);
  199. entry->value = osi_strdup(value);
  200. return;
  201. }
  202. }
  203. entry_t *entry = entry_new(key, value);
  204. list_append(sec->entries, entry);
  205. }
  206. bool config_remove_section(config_t *config, const char *section)
  207. {
  208. assert(config != NULL);
  209. assert(section != NULL);
  210. section_t *sec = section_find(config, section);
  211. if (!sec) {
  212. return false;
  213. }
  214. return list_remove(config->sections, sec);
  215. }
  216. bool config_remove_key(config_t *config, const char *section, const char *key)
  217. {
  218. assert(config != NULL);
  219. assert(section != NULL);
  220. assert(key != NULL);
  221. bool ret;
  222. section_t *sec = section_find(config, section);
  223. entry_t *entry = entry_find(config, section, key);
  224. if (!sec || !entry) {
  225. return false;
  226. }
  227. ret = list_remove(sec->entries, entry);
  228. if (list_length(sec->entries) == 0) {
  229. OSI_TRACE_DEBUG("%s remove section name:%s",__func__, section);
  230. ret &= config_remove_section(config, section);
  231. }
  232. return ret;
  233. }
  234. const config_section_node_t *config_section_begin(const config_t *config)
  235. {
  236. assert(config != NULL);
  237. return (const config_section_node_t *)list_begin(config->sections);
  238. }
  239. const config_section_node_t *config_section_end(const config_t *config)
  240. {
  241. assert(config != NULL);
  242. return (const config_section_node_t *)list_end(config->sections);
  243. }
  244. const config_section_node_t *config_section_next(const config_section_node_t *node)
  245. {
  246. assert(node != NULL);
  247. return (const config_section_node_t *)list_next((const list_node_t *)node);
  248. }
  249. const char *config_section_name(const config_section_node_t *node)
  250. {
  251. assert(node != NULL);
  252. const list_node_t *lnode = (const list_node_t *)node;
  253. const section_t *section = (const section_t *)list_node(lnode);
  254. return section->name;
  255. }
  256. static int get_config_size(const config_t *config)
  257. {
  258. assert(config != NULL);
  259. int w_len = 0, total_size = 0;
  260. for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
  261. const section_t *section = (const section_t *)list_node(node);
  262. w_len = strlen(section->name) + strlen("[]\n");// format "[section->name]\n"
  263. total_size += w_len;
  264. for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) {
  265. const entry_t *entry = (const entry_t *)list_node(enode);
  266. w_len = strlen(entry->key) + strlen(entry->value) + strlen(" = \n");// format "entry->key = entry->value\n"
  267. total_size += w_len;
  268. }
  269. // Only add a separating newline if there are more sections.
  270. if (list_next(node) != list_end(config->sections)) {
  271. total_size ++; //'\n'
  272. } else {
  273. break;
  274. }
  275. }
  276. total_size ++; //'\0'
  277. return total_size;
  278. }
  279. static int get_config_size_from_flash(nvs_handle_t fp)
  280. {
  281. assert(fp != 0);
  282. esp_err_t err;
  283. const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i))
  284. char *keyname = osi_calloc(keyname_bufsz);
  285. if (!keyname){
  286. OSI_TRACE_ERROR("%s, malloc error\n", __func__);
  287. return 0;
  288. }
  289. size_t length = CONFIG_FILE_DEFAULE_LENGTH;
  290. size_t total_length = 0;
  291. uint16_t i = 0;
  292. snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0);
  293. err = nvs_get_blob(fp, keyname, NULL, &length);
  294. if (err == ESP_ERR_NVS_NOT_FOUND) {
  295. osi_free(keyname);
  296. return 0;
  297. }
  298. if (err != ESP_OK) {
  299. OSI_TRACE_ERROR("%s, error %d\n", __func__, err);
  300. osi_free(keyname);
  301. return 0;
  302. }
  303. total_length += length;
  304. while (length == CONFIG_FILE_MAX_SIZE) {
  305. length = CONFIG_FILE_DEFAULE_LENGTH;
  306. snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, ++i);
  307. err = nvs_get_blob(fp, keyname, NULL, &length);
  308. if (err == ESP_ERR_NVS_NOT_FOUND) {
  309. break;
  310. }
  311. if (err != ESP_OK) {
  312. OSI_TRACE_ERROR("%s, error %d\n", __func__, err);
  313. osi_free(keyname);
  314. return 0;
  315. }
  316. total_length += length;
  317. }
  318. osi_free(keyname);
  319. return total_length;
  320. }
  321. bool config_save(const config_t *config, const char *filename)
  322. {
  323. assert(config != NULL);
  324. assert(filename != NULL);
  325. assert(*filename != '\0');
  326. esp_err_t err;
  327. int err_code = 0;
  328. nvs_handle_t fp;
  329. char *line = osi_calloc(1024);
  330. const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i))
  331. char *keyname = osi_calloc(keyname_bufsz);
  332. int config_size = get_config_size(config);
  333. char *buf = osi_calloc(config_size);
  334. if (!line || !buf || !keyname) {
  335. err_code |= 0x01;
  336. goto error;
  337. }
  338. err = nvs_open(filename, NVS_READWRITE, &fp);
  339. if (err != ESP_OK) {
  340. if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
  341. OSI_TRACE_ERROR("%s: NVS not initialized. "
  342. "Call nvs_flash_init before initializing bluetooth.", __func__);
  343. }
  344. err_code |= 0x02;
  345. goto error;
  346. }
  347. int w_cnt, w_cnt_total = 0;
  348. for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
  349. const section_t *section = (const section_t *)list_node(node);
  350. w_cnt = snprintf(line, 1024, "[%s]\n", section->name);
  351. if(w_cnt < 0) {
  352. OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt);
  353. err_code |= 0x10;
  354. goto error;
  355. }
  356. if(w_cnt_total + w_cnt > config_size) {
  357. OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size (config_size = %d).", __func__, (w_cnt + w_cnt_total), config_size);
  358. err_code |= 0x20;
  359. goto error;
  360. }
  361. OSI_TRACE_DEBUG("section name: %s, w_cnt + w_cnt_total = %d\n", section->name, w_cnt + w_cnt_total);
  362. memcpy(buf + w_cnt_total, line, w_cnt);
  363. w_cnt_total += w_cnt;
  364. for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) {
  365. const entry_t *entry = (const entry_t *)list_node(enode);
  366. OSI_TRACE_DEBUG("(key, val): (%s, %s)\n", entry->key, entry->value);
  367. w_cnt = snprintf(line, 1024, "%s = %s\n", entry->key, entry->value);
  368. if(w_cnt < 0) {
  369. OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt);
  370. err_code |= 0x10;
  371. goto error;
  372. }
  373. if(w_cnt_total + w_cnt > config_size) {
  374. OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size.(config_size = %d)", __func__, (w_cnt + w_cnt_total), config_size);
  375. err_code |= 0x20;
  376. goto error;
  377. }
  378. OSI_TRACE_DEBUG("%s, w_cnt + w_cnt_total = %d", __func__, w_cnt + w_cnt_total);
  379. memcpy(buf + w_cnt_total, line, w_cnt);
  380. w_cnt_total += w_cnt;
  381. }
  382. // Only add a separating newline if there are more sections.
  383. if (list_next(node) != list_end(config->sections)) {
  384. buf[w_cnt_total] = '\n';
  385. w_cnt_total += 1;
  386. } else {
  387. break;
  388. }
  389. }
  390. buf[w_cnt_total] = '\0';
  391. if (w_cnt_total < CONFIG_FILE_MAX_SIZE) {
  392. snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0);
  393. err = nvs_set_blob(fp, keyname, buf, w_cnt_total);
  394. if (err != ESP_OK) {
  395. nvs_close(fp);
  396. err_code |= 0x04;
  397. goto error;
  398. }
  399. }else {
  400. int count = (w_cnt_total / CONFIG_FILE_MAX_SIZE);
  401. assert(count <= 0xFF);
  402. for (uint8_t i = 0; i <= count; i++)
  403. {
  404. snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, i);
  405. if (i == count) {
  406. err = nvs_set_blob(fp, keyname, buf + i*CONFIG_FILE_MAX_SIZE, w_cnt_total - i*CONFIG_FILE_MAX_SIZE);
  407. OSI_TRACE_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, w_cnt_total - i*CONFIG_FILE_MAX_SIZE);
  408. }else {
  409. err = nvs_set_blob(fp, keyname, buf + i*CONFIG_FILE_MAX_SIZE, CONFIG_FILE_MAX_SIZE);
  410. OSI_TRACE_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, CONFIG_FILE_MAX_SIZE);
  411. }
  412. if (err != ESP_OK) {
  413. nvs_close(fp);
  414. err_code |= 0x04;
  415. goto error;
  416. }
  417. }
  418. }
  419. err = nvs_commit(fp);
  420. if (err != ESP_OK) {
  421. nvs_close(fp);
  422. err_code |= 0x08;
  423. goto error;
  424. }
  425. nvs_close(fp);
  426. osi_free(line);
  427. osi_free(buf);
  428. osi_free(keyname);
  429. return true;
  430. error:
  431. if (buf) {
  432. osi_free(buf);
  433. }
  434. if (line) {
  435. osi_free(line);
  436. }
  437. if (keyname) {
  438. osi_free(keyname);
  439. }
  440. if (err_code) {
  441. OSI_TRACE_ERROR("%s, err_code: 0x%x\n", __func__, err_code);
  442. }
  443. return false;
  444. }
  445. static char *trim(char *str)
  446. {
  447. while (isspace((unsigned char)(*str))) {
  448. ++str;
  449. }
  450. if (!*str) {
  451. return str;
  452. }
  453. char *end_str = str + strlen(str) - 1;
  454. while (end_str > str && isspace((unsigned char)(*end_str))) {
  455. --end_str;
  456. }
  457. end_str[1] = '\0';
  458. return str;
  459. }
  460. static void config_parse(nvs_handle_t fp, config_t *config)
  461. {
  462. assert(fp != 0);
  463. assert(config != NULL);
  464. esp_err_t err;
  465. int line_num = 0;
  466. int err_code = 0;
  467. uint16_t i = 0;
  468. size_t length = CONFIG_FILE_DEFAULE_LENGTH;
  469. size_t total_length = 0;
  470. char *line = osi_calloc(1024);
  471. char *section = osi_calloc(1024);
  472. const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i))
  473. char *keyname = osi_calloc(keyname_bufsz);
  474. int buf_size = get_config_size_from_flash(fp);
  475. char *buf = osi_calloc(buf_size);
  476. if(buf_size == 0) { //First use nvs
  477. goto error;
  478. }
  479. if (!line || !section || !buf || !keyname) {
  480. err_code |= 0x01;
  481. goto error;
  482. }
  483. snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0);
  484. err = nvs_get_blob(fp, keyname, buf, &length);
  485. if (err == ESP_ERR_NVS_NOT_FOUND) {
  486. goto error;
  487. }
  488. if (err != ESP_OK) {
  489. err_code |= 0x02;
  490. goto error;
  491. }
  492. total_length += length;
  493. while (length == CONFIG_FILE_MAX_SIZE) {
  494. length = CONFIG_FILE_DEFAULE_LENGTH;
  495. snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, ++i);
  496. err = nvs_get_blob(fp, keyname, buf + CONFIG_FILE_MAX_SIZE * i, &length);
  497. if (err == ESP_ERR_NVS_NOT_FOUND) {
  498. break;
  499. }
  500. if (err != ESP_OK) {
  501. err_code |= 0x02;
  502. goto error;
  503. }
  504. total_length += length;
  505. }
  506. char *p_line_end;
  507. char *p_line_bgn = buf;
  508. strcpy(section, CONFIG_DEFAULT_SECTION);
  509. while ( (p_line_bgn < buf + total_length - 1) && (p_line_end = strchr(p_line_bgn, '\n'))) {
  510. // get one line
  511. int line_len = p_line_end - p_line_bgn;
  512. if (line_len > 1023) {
  513. OSI_TRACE_WARNING("%s exceed max line length on line %d.\n", __func__, line_num);
  514. break;
  515. }
  516. memcpy(line, p_line_bgn, line_len);
  517. line[line_len] = '\0';
  518. p_line_bgn = p_line_end + 1;
  519. char *line_ptr = trim(line);
  520. ++line_num;
  521. // Skip blank and comment lines.
  522. if (*line_ptr == '\0' || *line_ptr == '#') {
  523. continue;
  524. }
  525. if (*line_ptr == '[') {
  526. size_t len = strlen(line_ptr);
  527. if (line_ptr[len - 1] != ']') {
  528. OSI_TRACE_WARNING("%s unterminated section name on line %d.\n", __func__, line_num);
  529. continue;
  530. }
  531. strncpy(section, line_ptr + 1, len - 2);
  532. section[len - 2] = '\0';
  533. } else {
  534. char *split = strchr(line_ptr, '=');
  535. if (!split) {
  536. OSI_TRACE_DEBUG("%s no key/value separator found on line %d.\n", __func__, line_num);
  537. continue;
  538. }
  539. *split = '\0';
  540. config_set_string(config, section, trim(line_ptr), trim(split + 1), true);
  541. }
  542. }
  543. error:
  544. if (buf) {
  545. osi_free(buf);
  546. }
  547. if (line) {
  548. osi_free(line);
  549. }
  550. if (section) {
  551. osi_free(section);
  552. }
  553. if (keyname) {
  554. osi_free(keyname);
  555. }
  556. if (err_code) {
  557. OSI_TRACE_ERROR("%s returned with err code: %d\n", __func__, err_code);
  558. }
  559. }
  560. static section_t *section_new(const char *name)
  561. {
  562. section_t *section = osi_calloc(sizeof(section_t));
  563. if (!section) {
  564. return NULL;
  565. }
  566. section->name = osi_strdup(name);
  567. section->entries = list_new(entry_free);
  568. return section;
  569. }
  570. static void section_free(void *ptr)
  571. {
  572. if (!ptr) {
  573. return;
  574. }
  575. section_t *section = ptr;
  576. osi_free(section->name);
  577. list_free(section->entries);
  578. osi_free(section);
  579. }
  580. static section_t *section_find(const config_t *config, const char *section)
  581. {
  582. for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
  583. section_t *sec = list_node(node);
  584. if (!strcmp(sec->name, section)) {
  585. return sec;
  586. }
  587. }
  588. return NULL;
  589. }
  590. static entry_t *entry_new(const char *key, const char *value)
  591. {
  592. entry_t *entry = osi_calloc(sizeof(entry_t));
  593. if (!entry) {
  594. return NULL;
  595. }
  596. entry->key = osi_strdup(key);
  597. entry->value = osi_strdup(value);
  598. return entry;
  599. }
  600. static void entry_free(void *ptr)
  601. {
  602. if (!ptr) {
  603. return;
  604. }
  605. entry_t *entry = ptr;
  606. osi_free(entry->key);
  607. osi_free(entry->value);
  608. osi_free(entry);
  609. }
  610. static entry_t *entry_find(const config_t *config, const char *section, const char *key)
  611. {
  612. section_t *sec = section_find(config, section);
  613. if (!sec) {
  614. return NULL;
  615. }
  616. for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) {
  617. entry_t *entry = list_node(node);
  618. if (!strcmp(entry->key, key)) {
  619. return entry;
  620. }
  621. }
  622. return NULL;
  623. }