Annotation of libaitcfg/src/parse.c, revision 1.22.4.2
1.2 misho 1: /*************************************************************************
2: * (C) 2008 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
3: * by Michael Pounov <misho@openbsd-bg.org>
4: *
5: * $Author: misho $
1.22.4.2! misho 6: * $Id: parse.c,v 1.22.4.1 2026/08/04 18:51:43 misho Exp $
1.2 misho 7: *
1.6 misho 8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
1.22.4.1 misho 15: Copyright 2004 - 2026
1.6 misho 16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
1.1 misho 46: #include "global.h"
47:
48:
1.7 misho 49: /*
1.22.4.1 misho 50: * cfg_ReadConfig() - Read file and add new item at config root
1.7 misho 51: *
52: * @f = File resource
53: * @cfg = Config root
1.22.4.1 misho 54: * @lvl = Level of include recursion
1.7 misho 55: * return: -1 error or 0 ok
56: */
1.9 misho 57: int
1.22.4.1 misho 58: cfg_ReadConfig(FILE *f, cfg_root_t * __restrict cfg, int lvl)
1.3 misho 59: {
1.21 misho 60: char line[BUFSIZ], origin[BUFSIZ], chkattr[STRSIZ];
1.7 misho 61: struct tagCfg *av = NULL;
62: int flg = 0;
63: char *psAttr, *psVal, szSection[STRSIZ] = { 0 };
1.18 misho 64: FILE *ff;
1.3 misho 65:
1.11 misho 66: if (!f || !cfg) {
67: cfg_SetErr(EINVAL, "Invalid parameter(s)");
68: return -1;
69: }
70:
1.7 misho 71: while (!feof(f)) {
72: memset(line, 0, sizeof line);
1.20 misho 73: if (!fgets(line, sizeof(line) - 1, f))
74: break;
1.7 misho 75: #ifdef SUPPORT_USER_EOF
76: /* check for user end-of-file */
77: if (line[0] == '.' && line[1] == '\n')
78: break;
79: #endif
80: if (!(psAttr = strpbrk(line, "\r\n"))) {
1.22 misho 81: if (feof(f) && strlen(line) > 0) {
82: /* last line without end of line */
83: strlcpy(origin, line, sizeof origin);
84: str_Trim(line);
85: } else {
86: /* skip line, too long */
87: continue;
88: }
1.7 misho 89: } else {
90: *psAttr = 0;
1.17 misho 91: strlcpy(origin, line, sizeof origin);
1.12 misho 92: str_Trim(line);
1.7 misho 93: }
1.3 misho 94:
1.7 misho 95: if (flg) {
96: /* continues line */
97: if (!av)
98: continue;
99: else
100: psAttr = line + strlen(line) - 1;
101: if (*psAttr == '\\')
102: *psAttr = 0;
103: else
104: flg = 0;
105: /* concat line to value */
106: AIT_SET_STRCAT(&av->cfg_val, line);
1.10 misho 107: if (!flg && AIT_ADDR(&av->cfg_val))
1.21 misho 108: av->cfg_quoted += str_Unquot((char*) AIT_GET_STR(&av->cfg_val));
1.18 misho 109:
1.22.4.1 misho 110: if (lvl > 0) {
111: /* read include file */
112: if (!flg && AIT_ADDR(&av->cfg_val) &&
113: *AIT_GET_STR(&av->cfg_attr) == '%' &&
114: !strcmp(AIT_GET_STR(&av->cfg_attr), "%include")) {
115: ff = fopen(AIT_GET_STR(&av->cfg_val), "r");
116: if (ff) {
117: cfg_ReadConfig(ff, cfg, lvl - 1);
118: fclose(ff);
119: } else
120: EDEBUG(ELWIX_DEBUG_LOG, "Error:: Can't open %s file",
121: AIT_GET_STR(&av->cfg_val));
122: }
1.18 misho 123: }
1.7 misho 124: continue;
125: }
1.3 misho 126:
1.21 misho 127: /* check for duplicated element */
128: if (*line != '#' && *line != ';' && *line != '/' && *line != '%' &&
129: (psAttr = strchr(line, '='))) {
130: strncpy(chkattr, line, psAttr - line);
131: chkattr[psAttr - line] = 0;
132: str_RTrim(chkattr);
133: if (cfg_findAttribute(cfg, szSection, chkattr))
134: cfg_unsetAttribute(cfg, szSection, chkattr);
135: }
136:
1.7 misho 137: /* *NEW PAIR* alloc new pair element */
1.12 misho 138: av = e_malloc(sizeof(struct tagCfg));
1.7 misho 139: if (!av) {
1.12 misho 140: cfg_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
1.7 misho 141: return -1;
142: } else {
143: memset(av, 0, sizeof(struct tagCfg));
144: CFG_RC_LOCK(cfg);
1.14 misho 145: TAILQ_INSERT_TAIL(cfg, av, cfg_next);
1.7 misho 146: CFG_RC_UNLOCK(cfg);
1.3 misho 147: }
1.7 misho 148:
1.18 misho 149: /* check for comment or empty line */
150: if (!*line || *line == '#' || *line == ';') {
151: AIT_SET_STR(&av->cfg_val, line);
152: continue;
153: }
154:
1.7 misho 155: /* check for continues line */
1.8 misho 156: psAttr = line + (*line ? strlen(line) : 1) - 1;
1.7 misho 157: if (*psAttr == '\\') {
158: *psAttr = 0;
159: flg = 1;
1.3 misho 160: }
161:
1.7 misho 162: /* section */
163: if (*line == '[') {
164: psAttr = line + strlen(line) - 1;
165: if (*psAttr == ']') {
166: *psAttr = 0;
167: flg = 0;
168: strlcpy(szSection, line + 1, sizeof szSection);
1.13 misho 169: AIT_SET_STR(&av->cfg_sec, line);
1.7 misho 170: } else
1.21 misho 171: EDEBUG(ELWIX_DEBUG_LOG, "Ignore section '%s' ... not found ']'", line);
1.7 misho 172: continue;
173: }
174: /* parse pair */
175: if (!(psAttr = strchr(line, '='))) {
1.17 misho 176: AIT_SET_STR(&av->cfg_val, origin);
1.21 misho 177: EDEBUG(ELWIX_DEBUG_LOG, "Ignore a/v '%s' ... not found '='", line);
1.7 misho 178: continue;
1.3 misho 179: } else {
1.7 misho 180: *psAttr = 0;
181: psVal = psAttr + 1;
182: psAttr = line;
183: }
184:
185: /* if exists, added section name to element */
186: if (*szSection) {
187: AIT_SET_STR(&av->cfg_sec, szSection);
188: AIT_KEY(&av->cfg_sec) = crcFletcher16(AIT_GET_LIKE(&av->cfg_sec, u_short*),
1.12 misho 189: E_ALIGN(AIT_LEN(&av->cfg_sec) - 1, 2) / 2);
1.3 misho 190: }
191:
1.12 misho 192: str_RTrim(psAttr);
193: str_LTrim(psVal);
1.7 misho 194: if (!flg)
1.21 misho 195: av->cfg_quoted += str_Unquot(psVal);
1.7 misho 196: AIT_SET_STR(&av->cfg_val, psVal);
197: AIT_SET_STR(&av->cfg_attr, psAttr);
198: AIT_KEY(&av->cfg_attr) = crcFletcher16(AIT_GET_LIKE(&av->cfg_attr, u_short*),
1.12 misho 199: E_ALIGN(AIT_LEN(&av->cfg_attr) - 1, 2) / 2);
1.7 misho 200:
201: CFG_RC_LOCK(cfg);
202: RB_INSERT(tagRC, cfg, av);
203: CFG_RC_UNLOCK(cfg);
1.18 misho 204:
1.22.4.1 misho 205: if (lvl > 0) {
206: /* read include file */
207: if (!flg && *AIT_GET_STR(&av->cfg_attr) == '%' &&
208: !strcmp(AIT_GET_STR(&av->cfg_attr), "%include")) {
209: ff = fopen(AIT_GET_STR(&av->cfg_val), "r");
210: if (ff) {
211: cfg_ReadConfig(ff, cfg, lvl - 1);
212: fclose(ff);
213: } else
214: EDEBUG(ELWIX_DEBUG_LOG, "Error:: Can't open %s file",
215: AIT_GET_STR(&av->cfg_val));
216: }
1.18 misho 217: }
1.3 misho 218: }
219:
220: return 0;
221: }
222:
1.1 misho 223: /*
1.7 misho 224: * cfgWriteConfig() - Write config from memory
225: *
226: * @f = File handle
227: * @cfg = Config root
228: * @whitespace = Additional whitespace characters to file
229: * return: -1 error or 0 ok
230: */
231: int
232: cfgWriteConfig(FILE *f, cfg_root_t * __restrict cfg, int whitespace)
1.1 misho 233: {
1.7 misho 234: struct tagCfg *av;
1.13 misho 235: char line[BUFSIZ] = { 0 }, szSection[STRSIZ] = { [0 ... STRSIZ - 1] = 0 };
1.1 misho 236:
1.11 misho 237: if (!f || !cfg) {
238: cfg_SetErr(EINVAL, "Invalid parameter(s)");
239: return -1;
240: }
241:
1.7 misho 242: CFG_RC_LOCK(cfg);
1.14 misho 243: RB_FOREACH(av, tagRC, cfg) {
1.13 misho 244: /* empty lines or comment */
245: if (AIT_ISEMPTY(&av->cfg_attr)) {
246: if (AIT_ISEMPTY(&av->cfg_val))
247: continue;
248: strlcpy(line, AIT_GET_STR(&av->cfg_val), sizeof line);
249: goto skip_sec;
250: }
251:
252: /* section [] */
1.10 misho 253: if (!AIT_ISEMPTY(&av->cfg_sec) && AIT_ADDR(&av->cfg_sec) &&
1.14 misho 254: strcmp(AIT_GET_STRZ(&av->cfg_sec), szSection)) {
1.7 misho 255: strlcpy(szSection, AIT_GET_STR(&av->cfg_sec), sizeof szSection);
1.21 misho 256: if (!cfg_Write(f, "[%s]\n", AIT_GET_STR(&av->cfg_sec))) {
1.7 misho 257: LOGERR;
258: CFG_RC_UNLOCK(cfg);
259: return -1;
1.1 misho 260: }
1.13 misho 261: } else if (AIT_ISEMPTY(&av->cfg_sec) && *szSection) {
1.7 misho 262: memset(szSection, 0, sizeof szSection);
1.21 misho 263: if (!cfg_Write(f, "[]\n")) {
1.1 misho 264: LOGERR;
1.7 misho 265: CFG_RC_UNLOCK(cfg);
1.1 misho 266: return -1;
267: }
1.7 misho 268: }
269:
270: /* build line */
271: memset(line, 0, sizeof line);
1.11 misho 272: if (!AIT_ISEMPTY(&av->cfg_attr) && AIT_TYPE(&av->cfg_attr) == string) {
273: strlcpy(line, AIT_GET_STRZ(&av->cfg_attr), sizeof line);
1.7 misho 274: if (whitespace)
275: strlcat(line, " = ", sizeof line);
276: else
277: strlcat(line, "=", sizeof line);
278: }
1.21 misho 279: if (!AIT_ISEMPTY(&av->cfg_val) && AIT_TYPE(&av->cfg_val) == string) {
280: if (av->cfg_quoted)
281: strlcat(line, "\"", sizeof line);
1.11 misho 282: strlcat(line, AIT_GET_STRZ(&av->cfg_val), sizeof line);
1.21 misho 283: if (av->cfg_quoted)
284: strlcat(line, "\"", sizeof line);
285: }
1.13 misho 286: skip_sec:
1.7 misho 287: /* write */
288: if (!cfg_Write(f, "%s\n", line)) {
289: LOGERR;
290: CFG_RC_UNLOCK(cfg);
291: return -1;
1.1 misho 292: }
293: }
1.7 misho 294: CFG_RC_UNLOCK(cfg);
295:
1.1 misho 296: return 0;
297: }
298:
299: /*
1.19 misho 300: * cfgWriteConfigRaw() - Write config from memory by list
301: *
302: * @f = File handle
303: * @cfg = Config root
304: * @whitespace = Additional whitespace characters to file
305: * return: -1 error or 0 ok
306: */
307: int
308: cfgWriteConfigRaw(FILE *f, cfg_root_t * __restrict cfg, int whitespace)
309: {
310: struct tagCfg *av, *nxt;
311: char line[BUFSIZ] = { 0 }, szSection[STRSIZ] = { [0 ... STRSIZ - 1] = 0 };
312:
313: if (!f || !cfg) {
314: cfg_SetErr(EINVAL, "Invalid parameter(s)");
315: return -1;
316: }
317:
318: CFG_RC_LOCK(cfg);
319: TAILQ_FOREACH_SAFE(av, cfg, cfg_next, nxt) {
320: /* empty lines or comment */
321: if (AIT_ISEMPTY(&av->cfg_attr)) {
322: if (AIT_ISEMPTY(&av->cfg_val))
323: continue;
324: strlcpy(line, AIT_GET_STR(&av->cfg_val), sizeof line);
325: goto skip_sec;
326: }
327:
328: /* section [] */
329: if (!AIT_ISEMPTY(&av->cfg_sec) && AIT_ADDR(&av->cfg_sec) &&
330: strcmp(AIT_GET_STRZ(&av->cfg_sec), szSection)) {
331: strlcpy(szSection, AIT_GET_STR(&av->cfg_sec), sizeof szSection);
1.21 misho 332: if (!cfg_Write(f, "[%s]\n", AIT_GET_STR(&av->cfg_sec))) {
1.19 misho 333: LOGERR;
334: CFG_RC_UNLOCK(cfg);
335: return -1;
336: }
337: } else if (AIT_ISEMPTY(&av->cfg_sec) && *szSection) {
338: memset(szSection, 0, sizeof szSection);
1.21 misho 339: if (!cfg_Write(f, "[]\n")) {
1.19 misho 340: LOGERR;
341: CFG_RC_UNLOCK(cfg);
342: return -1;
343: }
344: }
345:
346: /* build line */
347: memset(line, 0, sizeof line);
348: if (!AIT_ISEMPTY(&av->cfg_attr) && AIT_TYPE(&av->cfg_attr) == string) {
349: strlcpy(line, AIT_GET_STRZ(&av->cfg_attr), sizeof line);
350: if (whitespace)
351: strlcat(line, " = ", sizeof line);
352: else
353: strlcat(line, "=", sizeof line);
354: }
1.21 misho 355: if (!AIT_ISEMPTY(&av->cfg_val) && AIT_TYPE(&av->cfg_val) == string) {
356: if (av->cfg_quoted)
357: strlcat(line, "\"", sizeof line);
1.19 misho 358: strlcat(line, AIT_GET_STRZ(&av->cfg_val), sizeof line);
1.21 misho 359: if (av->cfg_quoted)
360: strlcat(line, "\"", sizeof line);
361: }
1.19 misho 362: skip_sec:
363: /* write */
364: if (!cfg_Write(f, "%s\n", line)) {
365: LOGERR;
366: CFG_RC_UNLOCK(cfg);
367: return -1;
368: }
369: }
370: CFG_RC_UNLOCK(cfg);
371:
372: return 0;
373: }
374: /*
1.7 misho 375: * cfgConcatConfig() - Concat two configs into one
376: *
377: * @cfg = Config root
378: * @add_cfg = Concated config will be destroy after merge
379: * return: -1 error or 0 ok
380: */
381: int
382: cfgConcatConfig(cfg_root_t * __restrict cfg, cfg_root_t * __restrict add_cfg)
1.1 misho 383: {
1.7 misho 384: struct tagCfg *item;
1.3 misho 385:
1.7 misho 386: if (!cfg || !add_cfg)
387: return -1;
1.3 misho 388:
1.7 misho 389: CFG_RC_LOCK(add_cfg);
390: CFG_RC_LOCK(cfg);
1.3 misho 391:
1.14 misho 392: /* concat lists & red-black trees */
393: TAILQ_FOREACH(item, add_cfg, cfg_next) {
394: TAILQ_INSERT_TAIL(cfg, item, cfg_next);
1.7 misho 395: RB_INSERT(tagRC, cfg, item);
1.14 misho 396: }
1.3 misho 397:
1.7 misho 398: CFG_RC_UNLOCK(cfg);
1.3 misho 399:
1.14 misho 400: TAILQ_INIT(add_cfg);
401: RB_INIT(add_cfg);
1.11 misho 402: CFG_RC_UNLOCK(add_cfg);
1.7 misho 403: pthread_mutex_destroy(&add_cfg->rc_mtx);
404: return 0;
1.3 misho 405: }
1.1 misho 406:
1.3 misho 407: /*
1.7 misho 408: * cfgMergeConfig() - Marge two list in one cfg and destroy add_cfg
409: *
410: * @cfg = Config root of main list
411: * @add_cfg = Merged config will be destroy after merge
412: * return: -1 error or 0 ok
413: */
414: int
415: cfgMergeConfig(cfg_root_t * __restrict cfg, cfg_root_t * __restrict add_cfg)
1.3 misho 416: {
1.10 misho 417: struct tagCfg *item, *merge, *add_next, *next;
1.3 misho 418: int flg;
1.1 misho 419:
1.3 misho 420: if (!cfg || !add_cfg)
1.1 misho 421: return -1;
422:
1.7 misho 423: CFG_RC_LOCK(add_cfg);
424: CFG_RC_LOCK(cfg);
1.10 misho 425:
426: /* merge lists */
1.14 misho 427: TAILQ_FOREACH_SAFE(item, add_cfg, cfg_next, add_next) {
1.7 misho 428: flg = 0;
1.14 misho 429: TAILQ_FOREACH_SAFE(merge, cfg, cfg_next, next) {
1.7 misho 430: if (AIT_ISEMPTY(&merge->cfg_sec) && AIT_ISEMPTY(&item->cfg_sec)) {
1.3 misho 431: flg = 1;
432: break;
433: }
1.7 misho 434: if (!AIT_ISEMPTY(&merge->cfg_sec) && !AIT_ISEMPTY(&item->cfg_sec) &&
1.10 misho 435: AIT_ADDR(&merge->cfg_sec) && AIT_ADDR(&item->cfg_sec) &&
1.7 misho 436: !strcmp(AIT_GET_STR(&merge->cfg_sec), AIT_GET_STR(&item->cfg_sec))) {
1.21 misho 437: flg = -1;
1.3 misho 438: break;
1.1 misho 439: }
440: }
1.3 misho 441:
1.21 misho 442: switch (flg) {
443: case -1:
444: continue; /* skip duplicated element */
445: case 1:
446: TAILQ_INSERT_AFTER(cfg, merge, item, cfg_next);
447: break;
448: case 0:
449: TAILQ_INSERT_TAIL(cfg, item, cfg_next);
450: break;
451: }
1.10 misho 452: RB_INSERT(tagRC, cfg, item);
1.1 misho 453: }
1.10 misho 454:
1.7 misho 455: CFG_RC_UNLOCK(cfg);
1.1 misho 456:
1.14 misho 457: TAILQ_INIT(add_cfg);
458: RB_INIT(add_cfg);
1.11 misho 459: CFG_RC_UNLOCK(add_cfg);
1.7 misho 460: pthread_mutex_destroy(&add_cfg->rc_mtx);
1.1 misho 461: return 0;
462: }
1.9 misho 463:
464: /*
465: * cfgReadLines() - Read custom lines and add new item at config root
466: *
467: * @f = File resource
468: * @delim = Custom delimiter, if =NULL default is '='
469: * @end = Custom user end of file, if =NULL default is EOF
470: * @cfg = Config root
471: * return: -1 error or 0 ok
472: */
473: int
474: cfgReadLines(FILE *f, const char *delim, const char *end, cfg_root_t * __restrict cfg)
475: {
476: char line[BUFSIZ];
477: struct tagCfg *d, *av = NULL;
1.11 misho 478: char *p, *psSec, *psAttr, *psVal;
479:
480: if (!cfg)
481: return -1;
482: if (!delim)
483: delim = ATR_LINES_DELIM;
1.9 misho 484:
485: while (!feof(f)) {
1.11 misho 486: psSec = psAttr = psVal = NULL;
1.9 misho 487: memset(line, 0, sizeof line);
1.20 misho 488: if (!fgets(line, sizeof(line) - 1, f))
489: break;
1.9 misho 490: /* check for user end-of-file */
491: if (strspn(line, end))
492: break;
493:
494: if (!(psAttr = strpbrk(line, "\r\n"))) {
495: /* skip line, too long */
496: continue;
497: } else {
498: *psAttr = 0;
1.12 misho 499: str_Trim(line);
1.9 misho 500: if (!*line)
501: continue;
502: }
503:
1.12 misho 504: if (!av_MakeExt(line, delim, &p, &psVal))
1.9 misho 505: continue;
506: else {
1.12 misho 507: str_RTrim(p);
508: str_LTrim(psVal);
1.9 misho 509: }
1.12 misho 510: if (!av_MakeExt(p, SEC_LINES_DELIM, &psSec, &psAttr))
1.11 misho 511: psAttr = p;
1.9 misho 512:
1.21 misho 513: /* check for duplicated element */
514: if (psAttr && cfg_findAttribute(cfg, psSec, psAttr))
515: cfg_unsetAttribute(cfg, psSec, psAttr);
516:
1.9 misho 517: /* *NEW PAIR* alloc new pair element */
1.12 misho 518: av = e_malloc(sizeof(struct tagCfg));
1.9 misho 519: if (!av) {
520: LOGERR;
521: return -1;
522: } else
523: memset(av, 0, sizeof(struct tagCfg));
524:
1.11 misho 525: if (psSec) {
526: AIT_SET_STR(&av->cfg_sec, psSec);
527: AIT_KEY(&av->cfg_sec) = crcFletcher16(AIT_GET_LIKE(&av->cfg_sec, u_short*),
1.12 misho 528: E_ALIGN(AIT_LEN(&av->cfg_sec) - 1, 2) / 2);
1.11 misho 529: }
1.21 misho 530: if (psVal) {
531: av->cfg_quoted = str_Unquot(psVal);
1.9 misho 532: AIT_SET_STR(&av->cfg_val, psVal);
1.21 misho 533: }
1.9 misho 534: AIT_SET_STR(&av->cfg_attr, psAttr);
535: AIT_KEY(&av->cfg_attr) = crcFletcher16(AIT_GET_LIKE(&av->cfg_attr, u_short*),
1.12 misho 536: E_ALIGN(AIT_LEN(&av->cfg_attr) - 1, 2) / 2);
1.9 misho 537:
538: CFG_RC_LOCK(cfg);
539: /* find & delete duplicates */
540: if ((d = RB_FIND(tagRC, cfg, av))) {
541: RB_REMOVE(tagRC, cfg, d);
1.14 misho 542: TAILQ_REMOVE(cfg, d, cfg_next);
1.9 misho 543:
544: AIT_FREE_VAL(&d->cfg_val);
545: AIT_FREE_VAL(&d->cfg_attr);
546: AIT_FREE_VAL(&d->cfg_sec);
1.12 misho 547: e_free(d);
1.9 misho 548: }
549:
1.14 misho 550: TAILQ_INSERT_TAIL(cfg, av, cfg_next);
1.9 misho 551: RB_INSERT(tagRC, cfg, av);
552: CFG_RC_UNLOCK(cfg);
553: }
554:
555: return 0;
556: }
557:
1.11 misho 558: /*
559: * cfgWriteLines() - Write custom lines and export data to variable
560: *
561: * @f = File resource
562: * @delim = Custom delimiter, if =NULL default is '='
563: * @eol = End of line string, if =NULL default is "\n"
564: * @section = Export only section, if =NULL default is all
565: * @cfg = Config root
1.12 misho 566: * return: =NULL error or !=NULL exported data, must be free after use with ait_freeVar()
1.11 misho 567: */
568: ait_val_t *
569: cfgWriteLines(FILE *f, const char *delim, const char *eol, const char *section, cfg_root_t * __restrict cfg)
570: {
571: ait_val_t *v = NULL;
572: struct tagCfg *av;
573:
574: if (!cfg)
575: return NULL;
576: if (!delim)
577: delim = ATR_LINES_DELIM;
578: if (!eol)
579: eol = EOL_LINES_DELIM;
1.12 misho 580: if (!(v = ait_allocVar())) {
581: cfg_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
1.11 misho 582: return NULL;
583: } else
584: AIT_INIT_VAL2(v, string);
585:
1.14 misho 586: TAILQ_FOREACH(av, cfg, cfg_next) {
1.11 misho 587: if (section) {
588: if (!AIT_ISEMPTY(&av->cfg_sec) && *section)
589: continue;
1.14 misho 590: if (strcmp(section, AIT_GET_STRZ(&av->cfg_sec)))
1.11 misho 591: continue;
592: }
593:
594: if (!AIT_ISEMPTY(&av->cfg_sec)) {
595: AIT_SET_STRCAT(v, AIT_GET_STR(&av->cfg_sec));
596: AIT_SET_STRCAT(v, SEC_LINES_DELIM);
597: }
1.16 misho 598: if (!AIT_ISEMPTY(&av->cfg_attr)) {
599: AIT_SET_STRCAT(v, AIT_GET_STR(&av->cfg_attr));
600: AIT_SET_STRCAT(v, delim);
601: }
1.21 misho 602: if (!AIT_ISEMPTY(&av->cfg_val)) {
603: if (av->cfg_quoted)
604: AIT_SET_STRCAT(v, "\"");
1.11 misho 605: AIT_SET_STRCAT(v, AIT_GET_STR(&av->cfg_val));
1.21 misho 606: if (av->cfg_quoted)
607: AIT_SET_STRCAT(v, "\"");
608: }
1.11 misho 609: AIT_SET_STRCAT(v, eol);
610: }
611:
1.22.4.2! misho 612: if (f && AIT_GET_STR(v))
1.11 misho 613: fputs(AIT_GET_STR(v), f);
614: return v;
615: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>