Diff for /libaitcfg/src/parse.c between versions 1.1 and 1.21

version 1.1, 2008/08/28 13:17:41 version 1.21, 2023/01/23 23:27:26
Line 1 Line 1
#include "global.h"/*************************************************************************
#include "aitcfg.h"* (C) 2008 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
#include "tools.h"*  by Michael Pounov <misho@openbsd-bg.org>
 *
 * $Author$
 * $Id$
 *
 **************************************************************************
 The ELWIX and AITNET software is distributed under the following
 terms:
   
   All of the documentation and software included in the ELWIX and AITNET
   Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
   
// cfgDbg() Debug/Log operationCopyright 2004 - 2023
static inline int cfgDbg(FILE *f, char *fmt, ...)        by Michael Pounov <misho@elwix.org>.  All rights reserved.
{ 
        int ret = 0; 
        va_list lst; 
   
        va_start(lst, fmt);Redistribution and use in source and binary forms, with or without
        ret = vfprintf(f, fmt, lst);modification, are permitted provided that the following conditions
        va_end(lst);are met:
 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
 3. All advertising materials mentioning features or use of this software
    must display the following acknowledgement:
 This product includes software developed by Michael Pounov <misho@elwix.org>
 ELWIX - Embedded LightWeight unIX and its contributors.
 4. Neither the name of AITNET nor the names of its contributors
    may be used to endorse or promote products derived from this software
    without specific prior written permission.
   
        return ret;THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
}ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 SUCH DAMAGE.
 */
 #include "global.h"
   
 // -----------------------------------------  
   
 /*  /*
 * ReadConfig() Read from file and add new item to config list * cfgReadConfig() - Read file and add new item at config root
 * @f = file resource *
 * @cfg = Head list element * @f = File resource
 * return: 0 ok; -1 error:: can`t allocate memory * @cfg = Config root
*/ * return: -1 error or 0 ok
int ReadConfig(FILE *f, sl_config * __restrict cfg) */
 int
 cfgReadConfig(FILE *f, cfg_root_t * __restrict cfg)
 {  {
        u_char szLine[MAX_STR + 1];        char line[BUFSIZ], origin[BUFSIZ], chkattr[STRSIZ];
        u_char szSection[MAX_STR + 1], *psAttr, *psVal;        struct tagCfg *av = NULL;
        int pos;        int flg = 0;
        struct tagPair *av;        char *psAttr, *psVal, szSection[STRSIZ] = { 0 };
         FILE *ff;
   
        memset(szSection, 0, MAX_STR + 1);        if (!f || !cfg) {
        while (!feof(f)) {                cfg_SetErr(EINVAL, "Invalid parameter(s)");
                memset(szLine, 0, MAX_STR + 1);                return -1;
                fgets((char*) szLine, MAX_STR, f);        }
                trim(szLine); 
#ifdef __DEBUG 
                cfgDbg(stdout, "DEBUG:: RAW |%s|\n", szLine); 
#endif 
   
                // End of config        while (!feof(f)) {
                if (*szLine == '.')                memset(line, 0, sizeof line);
                 if (!fgets(line, sizeof(line) - 1, f))
                         break;                          break;
                // Comment#ifdef SUPPORT_USER_EOF
                if (*szLine == '#' || *szLine == ';' || !*szLine)                /* check for user end-of-file */
                 if (line[0] == '.' && line[1] == '\n')
                         break;
 #endif
                 if (!(psAttr = strpbrk(line, "\r\n"))) {
                         /* skip line, too long */
                         continue;                          continue;
                   } else {
                           *psAttr = 0;
                           strlcpy(origin, line, sizeof origin);
                           str_Trim(line);
                   }
   
#ifdef __DEBUG                if (flg) {
                cfgDbg(stdout, "DEBUG:: Clean |%s|\n", szLine);                        /* continues line */
#endif                        if (!av)
                                 continue;
                         else
                                 psAttr = line + strlen(line) - 1;
                         if (*psAttr == '\\')
                                 *psAttr = 0;
                         else
                                 flg = 0;
                         /* concat line to value */
                         AIT_SET_STRCAT(&av->cfg_val, line);
                         if (!flg && AIT_ADDR(&av->cfg_val))
                                 av->cfg_quoted += str_Unquot((char*) AIT_GET_STR(&av->cfg_val));
   
                // Section                        /* read include file */
                if (*szLine == '[') {                        if (!flg && AIT_ADDR(&av->cfg_val) && 
                        pos = strlen((char*) szLine) - 1;                                        *AIT_GET_STR(&av->cfg_attr) == '%' && 
                        if (szLine[pos] != ']') {                                        !strcmp(AIT_GET_STR(&av->cfg_attr), "%include")) {
                                cfgDbg(stdout, "WARNING:: Ignore section %s ... not closed breket\n", szLine);                                ff = fopen(AIT_GET_STR(&av->cfg_val), "r");
                        } else {                                if (ff) {
                                szLine[pos] = 0;                                         cfgReadConfig(ff, cfg);
                                strncpy((char*) szSection, (char*) szLine + 1, MAX_STR);                                        fclose(ff);
#ifdef __DEBUG                                } else
                                cfgDbg(stdout, "DEBUG:: Section %s\n", szSection);                                        EDEBUG(ELWIX_DEBUG_LOG, "Error:: Can't open %s file", 
#endif                                                        AIT_GET_STR(&av->cfg_val));
                         }                          }
                         continue;                          continue;
                 }                  }
   
                // Devide pairs                /* check for duplicated element */
                pos = strchr((char*) szLine, '=') ? strchr((char*) szLine, '=') - (char*) szLine : 0;                if (*line != '#' && *line != ';' && *line != '/' && *line != '%' && 
                if (!pos) {                                (psAttr = strchr(line, '='))) {
                        cfgDbg(stdout, "WARNING:: Ignore a/v %s ... format error!\n", szLine);                        strncpy(chkattr, line, psAttr - line);
                         chkattr[psAttr - line] = 0;
                         str_RTrim(chkattr);
                         if (cfg_findAttribute(cfg, szSection, chkattr))
                                 cfg_unsetAttribute(cfg, szSection, chkattr);
                 }
 
                 /* *NEW PAIR* alloc new pair element */
                 av = e_malloc(sizeof(struct tagCfg));
                 if (!av) {
                         cfg_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
                         return -1;
                 } else {
                         memset(av, 0, sizeof(struct tagCfg));
                         CFG_RC_LOCK(cfg);
                         TAILQ_INSERT_TAIL(cfg, av, cfg_next);
                         CFG_RC_UNLOCK(cfg);
                 }
 
                 /* check for comment or empty line */
                 if (!*line || *line == '#' || *line == ';') {
                         AIT_SET_STR(&av->cfg_val, line);
                         continue;                          continue;
                   }
   
                   /* check for continues line */
                   psAttr = line + (*line ? strlen(line) : 1) - 1;
                   if (*psAttr == '\\') {
                           *psAttr = 0;
                           flg = 1;
                   }
   
                   /* section */
                   if (*line == '[') {
                           psAttr = line + strlen(line) - 1;
                           if (*psAttr == ']') {
                                   *psAttr = 0; 
                                   flg = 0;
                                   strlcpy(szSection, line + 1, sizeof szSection);
                                   AIT_SET_STR(&av->cfg_sec, line);
                           } else
                                   EDEBUG(ELWIX_DEBUG_LOG, "Ignore section '%s' ... not found ']'", line);
                           continue;
                   }
                   /* parse pair */
                   if (!(psAttr = strchr(line, '='))) {
                           AIT_SET_STR(&av->cfg_val, origin);
                           EDEBUG(ELWIX_DEBUG_LOG, "Ignore a/v '%s' ... not found '='", line);
                           continue;
                 } else {                  } else {
                        av = malloc(sizeof(struct tagPair));                        *psAttr = 0;
                        if (!av) {                        psVal = psAttr + 1;
                                LOGERR;                        psAttr = line;
                                return -1;                }
                        } else {
                                // added new element                /* if exists, added section name to element */
                                av->sle_next = cfg->slh_first;                if (*szSection) {
                                cfg->slh_first = av;                        AIT_SET_STR(&av->cfg_sec, szSection);
                        }                        AIT_KEY(&av->cfg_sec) = crcFletcher16(AIT_GET_LIKE(&av->cfg_sec, u_short*), 
                        // added section name to element                                        E_ALIGN(AIT_LEN(&av->cfg_sec) - 1, 2) / 2);
                        if (*szSection) {                }
                                av->psSection = malloc(strlen((char*) szSection) + 1);
                                if (!av->psSection) {                str_RTrim(psAttr);
                                        LOGERR;                str_LTrim(psVal);
                                        free(av);                if (!flg)
                                        return -1;                        av->cfg_quoted += str_Unquot(psVal);
                                } else                AIT_SET_STR(&av->cfg_val, psVal);
                                        strcpy((char*) av->psSection, (char*) szSection);                AIT_SET_STR(&av->cfg_attr, psAttr);
                 AIT_KEY(&av->cfg_attr) = crcFletcher16(AIT_GET_LIKE(&av->cfg_attr, u_short*), 
                                 E_ALIGN(AIT_LEN(&av->cfg_attr) - 1, 2) / 2);
 
                 CFG_RC_LOCK(cfg);
                 RB_INSERT(tagRC, cfg, av);
                 CFG_RC_UNLOCK(cfg);
 
                 /* read include file */
                 if (!flg && *AIT_GET_STR(&av->cfg_attr) == '%' && 
                                 !strcmp(AIT_GET_STR(&av->cfg_attr), "%include")) {
                         ff = fopen(AIT_GET_STR(&av->cfg_val), "r");
                         if (ff) {
                                 cfgReadConfig(ff, cfg);
                                 fclose(ff);
                         } else                          } else
                                av->psSection = NULL;                                EDEBUG(ELWIX_DEBUG_LOG, "Error:: Can't open %s file", 
                                                 AIT_GET_STR(&av->cfg_val));
                 }
         }
   
                        psAttr = szLine;        return 0;
                        psVal = (szLine + pos + 1);}
                        szLine[pos] = 0;
                        rtrim(psAttr);/*
                        ltrim(psVal); * cfgWriteConfig() - Write config from memory
#ifdef __DEBUG *
                        cfgDbg(stdout, "DEBUG:: Attr(%p) ->%s size=%d Value(%p) ->%s size=%d\n",  * @f = File handle
                                        psAttr, psAttr, strlen((char*) psAttr), psVal, psVal, strlen((char*) psVal)); * @cfg = Config root
#endif * @whitespace = Additional whitespace characters to file
                        // added attribute to element * return: -1 error or 0 ok
                        av->psAttribute = malloc(strlen((char*) psAttr) + 1); */
                        if (!av->psAttribute) {int
 cfgWriteConfig(FILE *f, cfg_root_t * __restrict cfg, int whitespace)
 {
         struct tagCfg *av;
         char line[BUFSIZ] = { 0 }, szSection[STRSIZ] = { [0 ... STRSIZ - 1] = 0 };
 
         if (!f || !cfg) {
                 cfg_SetErr(EINVAL, "Invalid parameter(s)");
                 return -1;
         }
 
         CFG_RC_LOCK(cfg);
         RB_FOREACH(av, tagRC, cfg) {
                 /* empty lines or comment */
                 if (AIT_ISEMPTY(&av->cfg_attr)) {
                         if (AIT_ISEMPTY(&av->cfg_val))
                                 continue;
                         strlcpy(line, AIT_GET_STR(&av->cfg_val), sizeof line);
                         goto skip_sec;
                 }
 
                 /* section [] */
                 if (!AIT_ISEMPTY(&av->cfg_sec) && AIT_ADDR(&av->cfg_sec) && 
                                 strcmp(AIT_GET_STRZ(&av->cfg_sec), szSection)) {
                         strlcpy(szSection, AIT_GET_STR(&av->cfg_sec), sizeof szSection);
                         if (!cfg_Write(f, "[%s]\n", AIT_GET_STR(&av->cfg_sec))) {
                                 LOGERR;                                  LOGERR;
                                free(av->psSection);                                CFG_RC_UNLOCK(cfg);
                                free(av); 
                                 return -1;                                  return -1;
                        } else                        }
                                strcpy((char*) av->psAttribute, (char*) psAttr);                } else if (AIT_ISEMPTY(&av->cfg_sec) && *szSection) {
                        // added value to element                        memset(szSection, 0, sizeof szSection);
                        av->psValue = malloc(strlen((char*) psVal) + 1);                        if (!cfg_Write(f, "[]\n")) {
                        if (!av->psValue) { 
                                 LOGERR;                                  LOGERR;
                                free(av->psAttribute);                                CFG_RC_UNLOCK(cfg);
                                free(av->psSection); 
                                free(av); 
                                 return -1;                                  return -1;
                        } else                        }
                                strcpy((char*) av->psValue, (char*) psVal); 
                 }                  }
   
                   /* build line */
                   memset(line, 0, sizeof line);
                   if (!AIT_ISEMPTY(&av->cfg_attr) && AIT_TYPE(&av->cfg_attr) == string) {
                           strlcpy(line, AIT_GET_STRZ(&av->cfg_attr), sizeof line);
                           if (whitespace)
                                   strlcat(line, " = ", sizeof line);
                           else
                                   strlcat(line, "=", sizeof line);
                   }
                   if (!AIT_ISEMPTY(&av->cfg_val) && AIT_TYPE(&av->cfg_val) == string) {
                           if (av->cfg_quoted)
                                   strlcat(line, "\"", sizeof line);
                           strlcat(line, AIT_GET_STRZ(&av->cfg_val), sizeof line);
                           if (av->cfg_quoted)
                                   strlcat(line, "\"", sizeof line);
                   }
   skip_sec:
                   /* write */
                   if (!cfg_Write(f, "%s\n", line)) {
                           LOGERR;
                           CFG_RC_UNLOCK(cfg);
                           return -1;
                   }
         }          }
           CFG_RC_UNLOCK(cfg);
   
         return 0;          return 0;
 }  }
   
 /*  /*
 * WriteConfig() Write to file from items in config list * cfgWriteConfigRaw() - Write config from memory by list
 * @f = file resource *
 * @cfg = Head list element * @f = File handle
 * return: 0 ok; -1 error:: can`t write to file * @cfg = Config root
*/ * @whitespace = Additional whitespace characters to file
int WriteConfig(FILE *f, sl_config * __restrict cfg) * return: -1 error or 0 ok
  */
 int
 cfgWriteConfigRaw(FILE *f, cfg_root_t * __restrict cfg, int whitespace)
 {  {
        struct tagPair *av;        struct tagCfg *av, *nxt;
        time_t tim;        char line[BUFSIZ] = { 0 }, szSection[STRSIZ] = { [0 ... STRSIZ - 1] = 0 };
        char szTime[MAX_STR + 1]; 
        u_char szSection[MAX_STR + 1]; 
   
        bzero(szSection, MAX_STR + 1);        if (!f || !cfg) {
                cfg_SetErr(EINVAL, "Invalid parameter(s)");
        bzero(szTime, MAX_STR + 1); 
        time(&tim); 
        strftime(szTime, MAX_STR, "(UTC) %Y-%m-%d %H:%M:%S", gmtime(&tim)); 
        if (!cfgDbg(f, "## Write Config :: %s\n#\n", szTime)) { 
                LOGERR; 
                 return -1;                  return -1;
         }          }
   
        for (av = cfg->slh_first; av; av = av->sle_next) {        CFG_RC_LOCK(cfg);
                if (av->psSection && strcmp((char*) av->psSection, (char*) szSection)) {        TAILQ_FOREACH_SAFE(av, cfg, cfg_next, nxt) {
                        bzero(szSection, MAX_STR + 1);                /* empty lines or comment */
                        strcpy((char*) szSection, (char*) av->psSection);                if (AIT_ISEMPTY(&av->cfg_attr)) {
                        if (!cfgDbg(f, "\n[%s]\n", av->psSection)) {                        if (AIT_ISEMPTY(&av->cfg_val))
                                 continue;
                         strlcpy(line, AIT_GET_STR(&av->cfg_val), sizeof line);
                         goto skip_sec;
                 }
 
                 /* section [] */
                 if (!AIT_ISEMPTY(&av->cfg_sec) && AIT_ADDR(&av->cfg_sec) && 
                                 strcmp(AIT_GET_STRZ(&av->cfg_sec), szSection)) {
                         strlcpy(szSection, AIT_GET_STR(&av->cfg_sec), sizeof szSection);
                         if (!cfg_Write(f, "[%s]\n", AIT_GET_STR(&av->cfg_sec))) {
                                 LOGERR;                                  LOGERR;
                                   CFG_RC_UNLOCK(cfg);
                                 return -1;                                  return -1;
                         }                          }
                }                } else if (AIT_ISEMPTY(&av->cfg_sec) && *szSection) {
                if (!av->psSection && *szSection) {                        memset(szSection, 0, sizeof szSection);
                        bzero(szSection, MAX_STR + 1);                        if (!cfg_Write(f, "[]\n")) {
                        if (!cfgDbg(f, "\n[]\n")) { 
                                 LOGERR;                                  LOGERR;
                                   CFG_RC_UNLOCK(cfg);
                                 return -1;                                  return -1;
                         }                          }
                 }                  }
   
                if (!cfgDbg(f, "%s = %s\n", av->psAttribute, av->psValue)) {                /* build line */
                 memset(line, 0, sizeof line);
                 if (!AIT_ISEMPTY(&av->cfg_attr) && AIT_TYPE(&av->cfg_attr) == string) {
                         strlcpy(line, AIT_GET_STRZ(&av->cfg_attr), sizeof line);
                         if (whitespace)
                                 strlcat(line, " = ", sizeof line);
                         else
                                 strlcat(line, "=", sizeof line);
                 }
                 if (!AIT_ISEMPTY(&av->cfg_val) && AIT_TYPE(&av->cfg_val) == string) {
                         if (av->cfg_quoted)
                                 strlcat(line, "\"", sizeof line);
                         strlcat(line, AIT_GET_STRZ(&av->cfg_val), sizeof line);
                         if (av->cfg_quoted)
                                 strlcat(line, "\"", sizeof line);
                 }
 skip_sec:
                 /* write */
                 if (!cfg_Write(f, "%s\n", line)) {
                         LOGERR;                          LOGERR;
                           CFG_RC_UNLOCK(cfg);
                         return -1;                          return -1;
                 }                  }
         }          }
           CFG_RC_UNLOCK(cfg);
   
        bzero(szTime, MAX_STR + 1);        return 0;
        time(&tim);}
        strftime(szTime, MAX_STR, "(UTC) %Y-%m-%d %H:%M:%S", gmtime(&tim));/*
        if (!cfgDbg(f, "\n#\n## Done. :: %s\n", szTime)) { * cfgConcatConfig() - Concat two configs into one
                LOGERR; *
  * @cfg = Config root
  * @add_cfg = Concated config will be destroy after merge
  * return: -1 error or 0 ok
  */
 int
 cfgConcatConfig(cfg_root_t * __restrict cfg, cfg_root_t * __restrict add_cfg)
 {
         struct tagCfg *item;
 
         if (!cfg || !add_cfg)
                 return -1;                  return -1;
   
           CFG_RC_LOCK(add_cfg);
           CFG_RC_LOCK(cfg);
   
           /* concat lists & red-black trees */
           TAILQ_FOREACH(item, add_cfg, cfg_next) {
                   TAILQ_INSERT_TAIL(cfg, item, cfg_next);
                   RB_INSERT(tagRC, cfg, item);
         }          }
   
           CFG_RC_UNLOCK(cfg);
   
           TAILQ_INIT(add_cfg);
           RB_INIT(add_cfg);
           CFG_RC_UNLOCK(add_cfg);
           pthread_mutex_destroy(&add_cfg->rc_mtx);
         return 0;          return 0;
   }
   
   /*
    * cfgMergeConfig() - Marge two list in one cfg and destroy add_cfg
    *
    * @cfg = Config root of main list
    * @add_cfg = Merged config will be destroy after merge
    * return: -1 error or 0 ok
    */
   int
   cfgMergeConfig(cfg_root_t * __restrict cfg, cfg_root_t * __restrict add_cfg)
   {
           struct tagCfg *item, *merge, *add_next, *next;
           int flg;
   
           if (!cfg || !add_cfg)
                   return -1;
   
           CFG_RC_LOCK(add_cfg);
           CFG_RC_LOCK(cfg);
   
           /* merge lists */
           TAILQ_FOREACH_SAFE(item, add_cfg, cfg_next, add_next) {
                   flg = 0;
                   TAILQ_FOREACH_SAFE(merge, cfg, cfg_next, next) {
                           if (AIT_ISEMPTY(&merge->cfg_sec) && AIT_ISEMPTY(&item->cfg_sec)) {
                                   flg = 1;
                                   break;
                           }
                           if (!AIT_ISEMPTY(&merge->cfg_sec) && !AIT_ISEMPTY(&item->cfg_sec) && 
                                           AIT_ADDR(&merge->cfg_sec) && AIT_ADDR(&item->cfg_sec) &&
                                           !strcmp(AIT_GET_STR(&merge->cfg_sec), AIT_GET_STR(&item->cfg_sec))) {
                                   flg = -1;
                                   break;
                           }
                   }
   
                   switch (flg) {
                           case -1:
                                   continue;       /* skip duplicated element */
                           case 1:
                                   TAILQ_INSERT_AFTER(cfg, merge, item, cfg_next);
                                   break;
                           case 0:
                                   TAILQ_INSERT_TAIL(cfg, item, cfg_next);
                                   break;
                   }
                   RB_INSERT(tagRC, cfg, item);
           }
   
           CFG_RC_UNLOCK(cfg);
   
           TAILQ_INIT(add_cfg);
           RB_INIT(add_cfg);
           CFG_RC_UNLOCK(add_cfg);
           pthread_mutex_destroy(&add_cfg->rc_mtx);
           return 0;
   }
   
   /*
    * cfgReadLines() - Read custom lines and add new item at config root
    *
    * @f = File resource
    * @delim = Custom delimiter, if =NULL default is '='
    * @end = Custom user end of file, if =NULL default is EOF
    * @cfg = Config root
    * return: -1 error or 0 ok
    */
   int
   cfgReadLines(FILE *f, const char *delim, const char *end, cfg_root_t * __restrict cfg)
   {
           char line[BUFSIZ];
           struct tagCfg *d, *av = NULL;
           char *p, *psSec, *psAttr, *psVal;
   
           if (!cfg)
                   return -1;
           if (!delim)
                   delim = ATR_LINES_DELIM;
   
           while (!feof(f)) {
                   psSec = psAttr = psVal = NULL;
                   memset(line, 0, sizeof line);
                   if (!fgets(line, sizeof(line) - 1, f))
                           break;
                   /* check for user end-of-file */
                   if (strspn(line, end))
                           break;
   
                   if (!(psAttr = strpbrk(line, "\r\n"))) {
                           /* skip line, too long */
                           continue;
                   } else {
                           *psAttr = 0;
                           str_Trim(line);
                           if (!*line)
                                   continue;
                   }
   
                   if (!av_MakeExt(line, delim, &p, &psVal))
                           continue;
                   else {
                           str_RTrim(p);
                           str_LTrim(psVal);
                   }
                   if (!av_MakeExt(p, SEC_LINES_DELIM, &psSec, &psAttr))
                           psAttr = p;
   
                   /* check for duplicated element */
                   if (psAttr && cfg_findAttribute(cfg, psSec, psAttr))
                           cfg_unsetAttribute(cfg, psSec, psAttr);
   
                   /* *NEW PAIR* alloc new pair element */
                   av = e_malloc(sizeof(struct tagCfg));
                   if (!av) {
                           LOGERR;
                           return -1;
                   } else
                           memset(av, 0, sizeof(struct tagCfg));
   
                   if (psSec) {
                           AIT_SET_STR(&av->cfg_sec, psSec);
                           AIT_KEY(&av->cfg_sec) = crcFletcher16(AIT_GET_LIKE(&av->cfg_sec, u_short*), 
                                           E_ALIGN(AIT_LEN(&av->cfg_sec) - 1, 2) / 2);
                   }
                   if (psVal) {
                           av->cfg_quoted = str_Unquot(psVal);
                           AIT_SET_STR(&av->cfg_val, psVal);
                   }
                   AIT_SET_STR(&av->cfg_attr, psAttr);
                   AIT_KEY(&av->cfg_attr) = crcFletcher16(AIT_GET_LIKE(&av->cfg_attr, u_short*), 
                                   E_ALIGN(AIT_LEN(&av->cfg_attr) - 1, 2) / 2);
   
                   CFG_RC_LOCK(cfg);
                   /* find & delete duplicates */
                   if ((d = RB_FIND(tagRC, cfg, av))) {
                           RB_REMOVE(tagRC, cfg, d);
                           TAILQ_REMOVE(cfg, d, cfg_next);
   
                           AIT_FREE_VAL(&d->cfg_val);
                           AIT_FREE_VAL(&d->cfg_attr);
                           AIT_FREE_VAL(&d->cfg_sec);
                           e_free(d);
                   }
   
                   TAILQ_INSERT_TAIL(cfg, av, cfg_next);
                   RB_INSERT(tagRC, cfg, av);
                   CFG_RC_UNLOCK(cfg);
           }
   
           return 0;
   }
   
   /*
    * cfgWriteLines() - Write custom lines and export data to variable
    *
    * @f = File resource
    * @delim = Custom delimiter, if =NULL default is '='
    * @eol = End of line string, if =NULL default is "\n"
    * @section = Export only section, if =NULL default is all
    * @cfg = Config root
    * return: =NULL error or !=NULL exported data, must be free after use with ait_freeVar()
    */
   ait_val_t *
   cfgWriteLines(FILE *f, const char *delim, const char *eol, const char *section, cfg_root_t * __restrict cfg)
   {
           ait_val_t *v = NULL;
           struct tagCfg *av;
   
           if (!cfg)
                   return NULL;
           if (!delim)
                   delim = ATR_LINES_DELIM;
           if (!eol)
                   eol = EOL_LINES_DELIM;
           if (!(v = ait_allocVar())) {
                   cfg_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
                   return NULL;
           } else
                   AIT_INIT_VAL2(v, string);
   
           TAILQ_FOREACH(av, cfg, cfg_next) {
                   if (section) {
                           if (!AIT_ISEMPTY(&av->cfg_sec) && *section)
                                   continue;
                           if (strcmp(section, AIT_GET_STRZ(&av->cfg_sec)))
                                   continue;
                   }
   
                   if (!AIT_ISEMPTY(&av->cfg_sec)) {
                           AIT_SET_STRCAT(v, AIT_GET_STR(&av->cfg_sec));
                           AIT_SET_STRCAT(v, SEC_LINES_DELIM);
                   }
                   if (!AIT_ISEMPTY(&av->cfg_attr)) {
                           AIT_SET_STRCAT(v, AIT_GET_STR(&av->cfg_attr));
                           AIT_SET_STRCAT(v, delim);
                   }
                   if (!AIT_ISEMPTY(&av->cfg_val)) {
                           if (av->cfg_quoted)
                                   AIT_SET_STRCAT(v, "\"");
                           AIT_SET_STRCAT(v, AIT_GET_STR(&av->cfg_val));
                           if (av->cfg_quoted)
                                   AIT_SET_STRCAT(v, "\"");
                   }
                   AIT_SET_STRCAT(v, eol);
           }
   
           if (f)
                   fputs(AIT_GET_STR(v), f);
           return v;
 }  }

Removed from v.1.1  
changed lines
  Added in v.1.21


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>