/***************************************************************************
 *                                                                         *
 *   CFGAPI.C                                                              *
 *                                                                         *
 *   Copyright (c) 1997      Galacticomm, Inc.     All Rights Reserved.    *
 *                                                                         *
 *   Configuration buffer management functions.                            *
 *                                                                         *
 *                                            - J. Alvrus   5/29/97        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"

#define FILREV "$Revision: 3 $"

CHAR *                             /*   start of sect or NULL if not found */
findSect(                          /* find a section in a config buffer    */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *src);                  /*   configuration buffer               */

CHAR *                             /*   line containing section name       */
findSectName(                      /* find a section in a config buffer    */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *src);                  /*   configuration buffer               */

CHAR *                             /*   start of value or NULL if not found*/
findItem(                          /* find item in a configuration section */
const CHAR *itemName,              /*   item name                          */
const CHAR *src);                  /*   start of configuration section     */

CHAR *                             /*   line containing item               */
findItemName(                      /* find item in a configuration section */
const CHAR *itemName,              /*   item name                          */
const CHAR *src);                  /*   start of configuration section     */

GBOOL                              /*   returns FALSE if not enough room   */
addSect(                           /* add a section name                   */
const CHAR *sectName,              /*   section name (NULL for default)    */
CHAR *buf,                         /*   configuration buffer               */
size_t bufSiz);                    /*   size of configuration buffer       */

VOID
delSect(                           /* delete a configuration section       */
const CHAR *sectName,              /*   section name (NULL for default)    */
CHAR *buf);                        /*   configuration buffer               */

VOID
delItem(                           /* delete a configuration item          */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *itemName,              /*   item name                          */
CHAR *buf);                        /*   configuration buffer               */

CHAR *                             /*   ptr to start of next section       */
nextSect(                          /* find next section name in config buf */
const CHAR *str);                  /*   buffer to search                   */

#define nextItem(s) nextLine((s),"\n")

CHAR *                             /*   returns ptr to destination         */
cfgGetStr(                         /* get string from configuration buffer */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *itemName,              /*   item name (NULL for all)           */
const CHAR *dftVal,                /*   default value                      */
CHAR *dst,                         /*   destination buffer                 */
size_t dstSiz,                     /*   size of destination buffer         */
const CHAR *src)                   /*   configuration buffer               */
{
     size_t len;
     const CHAR *endp;

     ASSERT(sectName == NULL || strchr(sectName,']') == NULL);
     ASSERT(itemName == NULL || strchr(itemName,'=') == NULL);
     ASSERT(dftVal != NULL);
     ASSERT(dst != NULL);
     ASSERT(src != NULL);
     if ((src=findSect(sectName,src)) != NULL) {
          if (NULSTR(itemName)) {
               itemName=NULL;
          }
          if (itemName == NULL || (src=findItem(itemName,src)) != NULL) {
               endp=itemName == NULL ? nextSect(src) : nextItem(src);
               len=(endp == NULL ? strlen(src) : (size_t)(endp-src))+1;
               stlcpy(dst,src,min(dstSiz,len));
               return(unpad(dst));
          }
     }
     return(stlcpy(dst,dftVal,dstSiz));
}

GBOOL                              /*   returns TRUE if able to set        */
cfgSetStr(                         /* set string in configuration buffer   */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *itemName,              /*   item name (NULL for all)           */
const CHAR *value,                 /*   string to add (NULL to delete)     */
CHAR *buf,                         /*   configuration buffer               */
size_t bufSiz)                     /*   size of configuration buffer       */
{
     CHAR *sectStart,*sectEnd,*itemStart,*itemEnd;
     size_t bufLen,newItemLen,oldItemLen;

     ASSERT(sectName == NULL || strchr(sectName,']') == NULL);
     ASSERT(itemName == NULL || strchr(itemName,'=') == NULL);
     ASSERT(buf != NULL);
     ASSERT(strlen(buf) < bufSiz);
     if (NULSTR(sectName)) {
          sectName=NULL;
     }
     if (NULSTR(itemName)) {
          itemName=NULL;
     }
     if (NULSTR(value)) {
          if (itemName == NULL) {
               delSect(sectName,buf);
          }
          else {
               delItem(sectName,itemName,buf);
          }
          return(TRUE);
     }
     if (sectName == NULL) {
          sectStart=buf;
     }
     else if ((sectStart=findSectName(sectName,buf)) == NULL) {
          if (!addSect(sectName,buf,bufSiz)) {
               return(FALSE);
          }
          sectStart=buf+strlen(buf);
     }
     else if ((sectStart=nextItem(sectStart)) == NULL) {
          if ((bufLen=strlen(buf)) >= bufSiz-1) {
               return(FALSE);
          }
          sectStart=buf+bufLen;
          *sectStart++='\n';
          *sectStart='\0';
     }
     sectEnd=nextSect(sectStart);
     if (itemName == NULL) {
          itemStart=sectStart;
          itemEnd=sectEnd;
          newItemLen=strlen(value);
          if (value[newItemLen-1] != '\n') {
               ++newItemLen;
          }
     }
     else {
          newItemLen=strlen(itemName)+1+strlen(value)+1;
          if ((itemStart=findItemName(itemName,sectStart)) == NULL) {
               itemStart=itemEnd=sectEnd;
          }
          else if ((itemEnd=nextItem(itemStart)) == NULL) {
               itemEnd=itemStart+strlen(itemStart);
          }
     }
     oldItemLen=(size_t)(itemEnd-itemStart);
     if (bufSiz <= (strlen(buf)-oldItemLen)+newItemLen) {
          return(FALSE);
     }
     strmove(itemStart+newItemLen,itemEnd);
     if (itemName == NULL) {
          memcpy(itemStart,value,newItemLen-1);
          itemStart[newItemLen-1]='\n';
     }
     else {
          itemStart=stpcpy(itemStart,itemName);
          *itemStart++='=';
          itemStart=stpcpy(itemStart,value);
          *itemStart='\n';
     }
     return(TRUE);
}

GBOOL                              /*   returns TRUE if able to set        */
cfgMerge(                          /* merge two configuration buffers      */
CHAR *dst,                         /*   destination config buf (added to)  */
const CHAR *src,                   /*   source config (to be added)        */
size_t dstSiz)                     /*   size of destination buffer         */
{
     CHAR *cp,*curlin,*nxtlin,*sect,*item,*tmpsrc;

     curlin=tmpsrc=strdup(src);
     unpad(tmpsrc);
     sect=NULL;
     do {
          if ((nxtlin=nextItem(curlin)) != NULL) {
               *(nxtlin-1)='\0';
          }
          if (*curlin == '[') {
               sect=skpwht(curlin);
               if ((cp=strchr(sect+1,']')) == NULL) {
                    free(tmpsrc);
                    return(FALSE);
               }
               *cp='\0';
               unpad(sect);
          }
          else {
               item=skpwht(curlin);
               if ((cp=strchr(item+1,'=')) == NULL) {
                    free(tmpsrc);
                    return(FALSE);
               }
               *cp++='\0';
               unpad(item);
               cp=unpad(skpwht(cp));
               if (!cfgSetStr(sect,item,cp,dst,dstSiz)) {
                    free(tmpsrc);
                    return(FALSE);
               }
          }
     } while ((curlin=nxtlin) != NULL);
     free(tmpsrc);
     return(TRUE);
}

CHAR *                             /*   start of sect or NULL if not found */
findSect(                          /* find a section in a config buffer    */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *src)                   /*   configuration buffer               */
{
     const CHAR *tmps;

     if (!NULSTR(sectName)) {
          if ((src=findSectName(sectName,src)) != NULL) {
               if ((tmps=nextItem(src)) == NULL) {
                    src+=strlen(src);
               }
               else {
                    src=tmps;
               }
          }
     }
     return((CHAR *)src);
}

CHAR *                             /*   line containing section name       */
findSectName(                      /* find a section in a config buffer    */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *src)                   /*   configuration buffer               */
{
     size_t namlen;
     const CHAR *tmps;

     if (!NULSTR(sectName)) {
          namlen=strlen(sectName);
          do {
               if (*src == '[') {
                    tmps=skpwht(src+1);
                    if (sameto(sectName,tmps) && *skpwht(tmps+namlen) == ']') {
                         break;
                    }
               }
          } while ((src=nextItem(src)) != NULL);
     }
     return((CHAR *)src);
}

CHAR *                             /*   start of value or NULL if not found*/
findItem(                          /* find item in a configuration section */
const CHAR *itemName,              /*   item name                          */
const CHAR *src)                   /*   start of configuration section     */
{
     if ((src=findItemName(itemName,src)) != NULL) {
          src=skpwht(strchr(src+strlen(itemName),'=')+1);
     }
     return((CHAR *)src);
}

CHAR *                             /*   line containing item               */
findItemName(                      /* find item in a configuration section */
const CHAR *itemName,              /*   item name                          */
const CHAR *src)                   /*   start of configuration section     */
{
     size_t namlen;

     namlen=strlen(itemName);
     do {
          if (*src == '[') {
               return(NULL);
          }
          if (sameto(itemName,src) && *skpwht(src+namlen) == '=') {
               break;
          }
     } while ((src=nextItem(src)) != NULL);
     return((CHAR *)src);
}

GBOOL                              /*   returns FALSE if not enough room   */
addSect(                           /* add a section name                   */
const CHAR *sectName,              /*   section name (NULL for default)    */
CHAR *buf,                         /*   configuration buffer               */
size_t bufSiz)                     /*   size of configuration buffer       */
{
     CHAR *cp;
     size_t orgLen;

     if ((orgLen=strlen(buf)) == 0) {
          cp=buf;
     }
     else if (*(cp=&buf[orgLen-1]) == '\n') {
          ++cp;
     }
     else if (++orgLen < bufSiz) {
          *++cp='\n';
          *++cp='\0';
     }
     if (bufSiz <= orgLen+strlen(sectName)+3) {
          return(FALSE);
     }
     *cp++='[';
     cp=stpcpy(cp,sectName);
     *cp++=']';
     *cp++='\n';
     *cp='\0';
     return(TRUE);
}

VOID
delSect(                           /* delete a configuration section       */
const CHAR *sectName,              /*   section name (NULL for default)    */
CHAR *buf)                         /*   configuration buffer               */
{
     CHAR *scp,*ecp;

     ASSERT(buf != NULL);
     if ((scp=findSectName(sectName,buf)) != NULL) {
          ecp=nextSect(scp+1);
          strmove(scp,ecp);
     }
}

VOID
delItem(                           /* delete a configuration item          */
const CHAR *sectName,              /*   section name (NULL for default)    */
const CHAR *itemName,              /*   item name                          */
CHAR *buf)                         /*   configuration buffer               */
{
     CHAR *scp,*ecp;

     ASSERT(!NULSTR(itemName));
     ASSERT(buf != NULL);
     if ((scp=findSect(sectName,buf)) != NULL) {
          if ((scp=findItemName(itemName,scp)) != NULL) {
               if ((ecp=nextItem(scp)) == NULL) {
                    *scp='\0';
               }
               else {
                    strmove(scp,ecp);
               }
          }
     }
}

CHAR *                             /*   ptr to start of next section       */
nextSect(                          /* find next section name in config buf */
const CHAR *str)                   /*   buffer to search                   */
{
     const CHAR *cp;

     do {
          if (*str == '[') {
               return((CHAR *)str);
          }
          cp=str;
     } while ((str=nextItem(cp)) != NULL);
     return((CHAR *)(cp+strlen(cp)));
}
