Logo Search packages:      
Sourcecode: vbaexpress version File versions  Download package

lcfg.c

/*
 *   This file is part of VBA Express.
 *
 *   Copyright (c) 2005-2006 Achraf cherti <achrafcherti@gmail.com>
 * 
 *   VBA Express is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   VBA Express is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with VBA Express; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/**************************************
 * chargement et sauvegarde des
 * fichiers ini
 *
 * Licence: GPL
 *
 * Auteur: Achraf cherti
 * Email:  achrafcherti@gmail.com
 *************************************/

//notes: begin>end alors aucune section... ne rien faire

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lcfg.h"

#define BINARY_HEADER "$$$LCFGBH$$$\n$$$A.C$$$\n$$$beGIn$$$\n"

static char *trim(char *str);
static LCFG_ELEMENT *add_element(LCFG *lcfg);
static char *lineinput(char *str, FILE* file);
static int traiter_line(LCFG_ELEMENT *e, char uncomment_var,char *line, char comment, char equal);
static char *premier(char *line,char c);
static int str_equal(const char *str1, const char *str2);
static int search_var(LCFG *lcfg, const char *var);

static int lcfg_save_binary_as(LCFG *lcfg, const char *filename);
static int lcfg_save_text_as(LCFG *lcfg, const char *filename);
static int lcfg_load_text(LCFG *lcfg, const char *filename);
static int lcfg_load_binary(LCFG *lcfg, const char *filename);

// *** ajout ***
static int lcfg_add(LCFG *lcfg, char type, const char *var, const char *value, const char *comment);

// *** Variables ***
static char default_comment='#'; //Le commentaire par défaut est...
static char default_equal='=';
static char default_autoindent=1;
static char default_guillemet=1;  //par défaut lors de l'ajout variable il y a guillemet

static size_t lcfg_fwrite(void *ptr, size_t size, size_t nmemb, FILE *file);
static size_t lcfg_fread(void *ptr, size_t size, size_t nmemb, FILE *file);
static void memcrypt(char *mem, size_t size);

// ** cryptage **
static const char *default_password=""; // mot de passe pour cryptage
static size_t pass_len=0;
static char _uncomment_var=0;
// des fichiers de configuration binaires...

/*****************************************
 * déclare globalement que tous les
 * ouvertures de fichiers config
 * prochaine vont décommenter les 
 * variables commentées d'avance.
 *****************************************/
void lcfg_set_uncomment_var(char enabled)
{
      _uncomment_var = enabled?1:0;
}

/*****************************************
 * changer le commentaire d'une variable
 * qui existe déjà.
 *
 * comment n'as pas le droit d'être null
 *
 * ret:
 * LCFG_OK
 * LCFG_E_NEXIST
 * LCFG_E_OUTMEM
 * LCFG_E_SECTION
 *****************************************/
int lcfg_var_comment(LCFG *lcfg, const char *var, const char *comment)
{
      int i;
      
      if(lcfg->end>=0 && lcfg->begin>lcfg->end) return LCFG_E_SECTION;
      i = search_var(lcfg,var);
      if(i==-1) return LCFG_E_NEXIST;
      
      {
            char *_comment=strdup(comment);
            if(!_comment) return LCFG_E_OUTMEM;

            //Mets le commentaire
            free(lcfg->el[i].comment);
            lcfg->el[i].comment=_comment;
      }
      
      return LCFG_OK;
}


/*****************************************
 * like ex. mais comment=0
 *****************************************/
int lcfg_var_value(LCFG *lcfg, const char *var, const char *value)
{
      return lcfg_var_value_ex(lcfg,var,value,0);
}

/*****************************************
 * Changer le contenu d'une 
 * variable 
 * si elle n'existe pas, elle est 
 * ajoutée dans "begin".
 *
 * comment peut être null
 * comment n'est mis que si la 
 * variable est ajoutée (quand elle
 * n'existe pas dans lcfg)
 *
 * ret:
 * ----
 * LCFG_OK
 * LCFG_E_OUTMEM
 * LCFG_E_SECTION
 *****************************************/
int lcfg_var_value_ex(LCFG *lcfg, const char *var, const char *value, const char *comment)
{
      int i;
      
      if(lcfg->end>=0 && lcfg->begin>lcfg->end) return LCFG_E_SECTION;
      i = search_var(lcfg,var);

      //si trouvé
      if(i!=-1) {
            char *_value = strdup(value);
            if(!_value) return LCFG_E_OUTMEM;
            free(lcfg->el[i].value);
            lcfg->el[i].value=_value; //mets la nouvelle valeur
            //petite modification si la variable est commentée... la décommenter :-)
            if(lcfg->el[i].type==LCFG_T_COMMENT_VAR) lcfg->el[i].type=LCFG_T_VAR;
            return LCFG_OK;
      }
      // S'il n'a pas trouvé...
      // alloquer pour ajouter nouveau
      // dans begin...
      else {
            LCFG_ELEMENT *save=lcfg->el;
            lcfg->el = (LCFG_ELEMENT *)realloc(lcfg->el, sizeof(LCFG_ELEMENT)*(lcfg->s_el+1));
            //outmem
            if(!lcfg->el) {
                  lcfg->el = save;
                  return LCFG_E_OUTMEM;
            }

            //Allocation des données
            {
                  char *_value   = strdup(value);
                  char *_var     = strdup(var);
                  char *_comment = (comment)?strdup(comment):0;
                  int begin,end;
                  if(!_value || !_var || (comment && !_comment)) {
                        //note: free ignore NULL
                        free(_var);
                        free(_value);
                        free(_comment);
                        return LCFG_E_OUTMEM;
                  }

                  //chercher la position ou mettre la nouvelle
                  //var:
                  //    * après toutes les LINE
                  //    * Avant toutes les var
                  //
                  // ne mets jamais la var à la fin. Si c'est
                  // le cas elle est dans l'avant dernière
                  // ligne.
                  //
                  //mets begin et end
                  end = (lcfg->end==-1)?lcfg->s_el-1:lcfg->end;
                  begin=lcfg->begin; //cherche après BEGIN
                  for(i=lcfg->begin;i<=end;i++) {
                        //TODO: mettre qu'il mette var
                        //après lignes de commentaires # avancée
                        //pour cela créer nouveau type
                        //de commentaires ligne:
                        //LCFG_T_COMMENT
                        if(lcfg->el[i].type==LCFG_T_VAR) break;

                        //pour qu'il se mette après tous les commentaires
                        //je l'ai mis après pour qu'il le mette
                        //avant une variable ;)
                        begin=i;
                  }
                  //toujours si begin=end alors begin=avant dernière
                  if(begin==end) {
                        begin=end-1;
                        if(begin<lcfg->begin) 
                              begin=lcfg->begin;
                  }
                  
                  //free une place
                  memmove(lcfg->el+begin+1,lcfg->el+begin,(lcfg->s_el - begin)*sizeof(LCFG_ELEMENT));

                  //mets les valeurs dans cette place
                  lcfg->el[begin].type      = LCFG_T_VAR;
                  lcfg->el[begin].value     = _value;
                  lcfg->el[begin].var       = _var;
                  lcfg->el[begin].comment   = _comment;
                  lcfg->el[begin].guillemet = default_guillemet;

                  //Quelques mises à jour
                  if(lcfg->end>=0) lcfg->end++;
                  lcfg->s_el++;
            }
      }
      return LCFG_OK;
}

/*****************************************
 * pour la création manuelle d'un 
 * fichier de configuration...
 *****************************************/
void lcfg_new(LCFG *lcfg, char binary)
{
      memset(lcfg,0,sizeof(LCFG));
      lcfg->default_comment=default_comment; //Le commentaire par défaut
      lcfg->default_equal=default_equal; //Le commentaire par défaut
      lcfg->end=-1; // déterminer automatiquement...
      lcfg->autoindent=default_autoindent;
      lcfg->binary=binary;
      lcfg->uncomment_var=_uncomment_var;
}

/***************************
 * Ajoute une ligne
 * vide...
 *
 * ret: 
 * LCFG_E_OUTMEM
 * LCFG_OK
 **************************/
int lcfg_a_void(LCFG *lcfg)
{
      return lcfg_add(lcfg,LCFG_T_LINE,"\n",0,0);
}

/***************************
 * ADD COMMENT
 *
 * Ajoute un comment
 * à la fin.
 **************************/
int lcfg_a_comment(LCFG *lcfg, const char *comment)
{
      int n;
      
      char *var=(char *)malloc(strlen(comment)+3); // 3= # + \n + z
      if(!var) return LCFG_E_OUTMEM;
      
      var[0]=lcfg->default_comment;
      strcpy(var+1,comment);
      strcat(var+1,"\n");

      n=lcfg_add(lcfg,LCFG_T_LINE,var,0,0);
      
      free(var);
      return n;
}

/***************************
 * ADD SECTION 
 *
 * Ajoute une section
 * à la fin...
 **************************/
int lcfg_a_section(LCFG *lcfg, const char *section)
{ return lcfg_a_section_ex(lcfg,section,0); }

int lcfg_a_section_ex(LCFG *lcfg, const char *section, const char *comment)
{
return lcfg_add(lcfg,LCFG_T_SECTION,section,0,comment);
}

/***************************
 * Ajout de variable
 * à la fin...
 ***************************/
int lcfg_a_var(LCFG *lcfg, const char *var, const char *value)
{
      return lcfg_a_var_ex(lcfg,var,value,0);
}

int lcfg_a_var_ex(LCFG *lcfg, const char *var, const char *value, const char *comment)
{
      return lcfg_add(lcfg,LCFG_T_VAR,var,value,comment);
}

static int lcfg_add(LCFG *lcfg, char type, const char *var, const char *value, const char *comment)
{
      LCFG_ELEMENT *e=add_element(lcfg);
      int i;
      if(!e) return LCFG_E_OUTMEM; //pas assez de mémoire...
      
      // positionne i à la fin de l'élément!
      // à noter que le dernier élément est vidé avec add_element
      i = lcfg->s_el-1;

      //mets ce qu'il y a à mettre
      lcfg->el[i].type = type;

      //var
      lcfg->el[i].var = (!var)?(char *)malloc(1):(char *)malloc(strlen(var)+1);
      if(!lcfg->el[i].var) { lcfg->s_el--; return LCFG_E_OUTMEM; }
      if(!var) lcfg->el[i].var[0]=0; else strcpy(lcfg->el[i].var,var); 

      // value
      lcfg->el[i].value = (!value)?(char *)malloc(1):(char *)malloc(strlen(value)+1); 
      if(!lcfg->el[i].value) { free(lcfg->el[i].var); lcfg->s_el--; return LCFG_E_OUTMEM; }
      if(!value) lcfg->el[i].value[0]=0; else strcpy(lcfg->el[i].value,value); 

      //comment
      if(comment) {
            lcfg->el[i].comment = (!comment)?(char *)malloc(1):(char *)malloc(strlen(comment)+1); 
            if(!lcfg->el[i].comment) { free(lcfg->el[i].value); free(lcfg->el[i].var); lcfg->s_el--; return LCFG_E_OUTMEM; }
            if(!comment) lcfg->el[i].comment[0]=0; else strcpy(lcfg->el[i].comment,comment); 
      }

      //guillemet
      lcfg->el[i].guillemet = default_guillemet;

      return LCFG_OK; 
}


/*****************************************
 * CRYPTAGE
 *****************************************/
static void memcrypt(char *mem, size_t size)
{
      size_t i;
      size_t carac=0; //carac actuel ds le pws
      
      for(i=0;i<size;i++) {
            mem[i] = mem[i]^default_password[carac];
            carac++;
            if(carac>=pass_len) carac=0;
      }
}

// fread avec cryptage
static size_t lcfg_fread(void *ptr, size_t size, size_t nmemb, FILE *file)
{
      size_t n;
      n = fread(ptr,size,nmemb,file);
      memcrypt((char *)ptr,nmemb*size);
      return n;
}

// fwrite avec cryptage
static size_t lcfg_fwrite(void *ptr, size_t size, size_t nmemb, FILE *file)
{
      size_t n;
      memcrypt((char *)ptr,nmemb*size);
      n=fwrite(ptr,size,nmemb,file);
      memcrypt((char *)ptr,nmemb*size);
      return n;
}

/*****************************************
 * Change: equal, comment
 *****************************************/
void lcfg_set_guillemet(int enabled)
{ if(enabled) default_guillemet=1; else default_guillemet=0; }
void lcfg_set_password(const char *password) 
{ default_password=password; pass_len=strlen(password); }
void lcfg_set_autoindent(int enabled)
{ if(enabled) default_autoindent=1; else default_autoindent=0; }
void lcfg_set_equal(char equal)
{ default_equal=equal; }
void lcfg_set_comment(char comment)
{ default_comment=comment; }

/*****************************************
 * str_equal
 * si deux chaines sont égales...
 *
 * 0 si pas égales.
 ****************************************/
static int str_equal(const char *str1, const char *str2)
{
      if(!strcasecmp(str1,str2)) return 1;
      return 0;
}

/*****************************************
 * Change de section!
 *
 * si la section n'existe pas
 * elle est ajoutée à la fin
 *
 * si dernière ligne <> espace vide
 * alors il mets un retour à
 * la ligne avant section
 * pour une bonne mise en page
 * du fichier de configuration.
 *
 * ret:
 * LCFG_OK
 * LCFG_E_OUTMEM
 *****************************************/
int lcfg_set_section(LCFG *lcfg, const char *section)
{
      int i,j;
      int fin=lcfg->end;
      
      // Si null alors cela veut dire englober le tout!
      if(!section) {
            lcfg->begin=0;
            lcfg->end=-1;
            return 0;
      }
      
      // si fin<0 ou fin>=dernier element alors correction ;)
      if(fin<0 || fin>=lcfg->s_el) fin=lcfg->s_el-1;

      // parcours à la recherche de la section... 
      for(i=0;i<=fin;i++) {
            if(lcfg->el[i].type!=LCFG_T_SECTION) continue;

            if(str_equal(section,lcfg->el[i].var))
            {
                  // cherche la fin de la section
                  lcfg->begin=i+1;
                  lcfg->end=-1; // met -1 on ne sait jamais (il pourrais ne pas la trouver la fin !)
                  // recherche la fin de la section...
                  for(j=i+1;j<=fin;j++) {
                        if(lcfg->el[j].type==LCFG_T_SECTION) {
                              lcfg->end=j-1; // et voilà !
                              return 0; // et fin, sans erreurs !
                        }
                  }
                  // ne change pas end parceque de toute façon 
                  // elle indique la fin (-1)
                  return 0; //pas d'erreur car trouvé!
            }
      }

      //la section n'existe pas...
      {
            int ret;
            //si la n'est pas ligne<>var ou section ajouter
            //un petit \n pour un bon indent du fichier
            //de configuration
            // TODO: Voir pq il n'ajoute jamais \n à la fin
            //       type!=LCFG_T_LINE ?
            //if(lcfg->s_el>0 && lcfg->el[lcfg->s_el-1].type!=LCFG_T_LINE) {
                  ret = lcfg_a_void(lcfg);
                  if(ret) {
                        lcfg->begin=1; //rendre impossible l'ajout var
                        lcfg->end=0;
                        return ret;
                  }
            //}
            
            //ajouter la section
            ret = lcfg_a_section(lcfg,section);
            if(ret) { 
                  lcfg->begin=1; //rendre impossible l'ajout var
                  lcfg->end=0;
                  return ret;
            }

            //maintenant mets les positions
            lcfg->begin=lcfg->s_el; //on le connait car c la fin ;)
            lcfg->end=-1; //fin!
      }
      
      return LCFG_OK; //tout est ok
}


/*****************************************
 * chercher une variable dans une lint
 *
 * section=0 cela veut dire variable sans
 * prendre les sections en compte.
 *
 * var=0
 *
 * cela veut dire chercher une section.
 *
 * debut=0 c'est le début
 * fin=-1  la fin (calcul auto)
 *
 * search_var trim automatiquement var
 * si espace ou tab il y a.
 ****************************************/
static int search_var(LCFG *lcfg, const char *var)
{
      int i;
      int debut=lcfg->begin,fin=lcfg->end;

      // corrections sur début et fin...
      if(debut<0) debut=0;
      if(fin<0 || fin>=lcfg->s_el) fin=lcfg->s_el-1;
      if(debut>fin) return -1; //pas trouvé d'avance!
      
      // recherche...
      for(i=debut;i<=fin;i++) {
            if(lcfg->el[i].type!=LCFG_T_VAR && (lcfg->uncomment_var && lcfg->el[i].type!=LCFG_T_COMMENT_VAR)) continue;
            if(str_equal(var,lcfg->el[i].var))
                  return i;
      }

      return -1; //err
}

/**************************************
 * Lecture numérique
 * 
 * retourne 0 si variable non trouvée.
 **************************************/
int lcfg_get_double(LCFG *lcfg, const char *var, double *out)
{
      char *s = lcfg_get_value(lcfg,var);
      if(!s) return 0;
      *out = atof(s);
      return 1;
}

int lcfg_get_long(LCFG *lcfg, const char *var, long *out)
{
      char *s = lcfg_get_value(lcfg,var);
      if(!s) return 0;
      *out = atol(s);
      return 1;
}

int lcfg_get_int(LCFG *lcfg, const char *var, int *out)
{
      char *s = lcfg_get_value(lcfg,var);
      if(!s) return 0;
      *out = atoi(s);
      return 1;
}

/**************************************
 * Retourne un pointeur vers value
 * correspondant à var.
 *
 * NULL si pas trouvé.
 **************************************/
char *lcfg_get_value(LCFG *lcfg, const char *var)
{
      int i = search_var(lcfg,var);
      if(lcfg->el[i].type==LCFG_T_COMMENT_VAR) return 0;
      if(i>=0) return lcfg->el[i].value;
      return 0;
}

/**************************************
 * unload
 * sauvegarde et désallocation.
 *************************************/
void lcfg_free(LCFG *lcfg)
{
      int i;
      if(!lcfg->el) {
            memset(lcfg,0,sizeof(LCFG));
            return;
      }
      for(i=0;i<lcfg->s_el;i++) {
            LCFG_ELEMENT *e=&lcfg->el[i];
            
            // si var alloquée
            if(e->var) free(e->var);

            // si value allquée
            if(e->value) free(e->value);

            //comment...
            if(e->comment) free(e->comment);

            //le surplus... (utilisé dans T_COMMENT_VAR) pour remettre le contenu comme il était au tout début
            if(e->extra) free(e->extra);
      }
      
      free(lcfg->el);

      memset(lcfg,0,sizeof(LCFG));
      return;
}

/**************************************
 * Forcer un chargement!
 **************************************/
int lcfg_load_ex(LCFG *lcfg, const char *filename, char binary)
{
      if(binary) 
            return lcfg_load_binary(lcfg,filename);
      else
            return lcfg_load_text(lcfg,filename);
}

/**************************************
 * charger un ini!
 **************************************/
int lcfg_load(LCFG *lcfg, const char *filename)
{
      char *header;
      size_t len=strlen(BINARY_HEADER);
      char binary=0;
      FILE *file;
      
      // Détection binaire/ascii
      // charge header
      file = fopen(filename,"rb");
      if(!file) return LCFG_E_FOPEN; 
      header=(char *)malloc(len); //sans z
      if(!header) { fclose(file); return LCFG_E_OUTMEM; }
      if(lcfg_fread(header,len,1,file)>=1) { 
            fclose(file); //pas la peine de gérer cette erreur...
            if(strncmp(header,BINARY_HEADER,len)==0) 
                  binary=1;
      }
      free(header);

      // ouverture
      return (binary)?
            lcfg_load_binary(lcfg,filename)
            :
            lcfg_load_text(lcfg,filename);
}

//========================
// ouverture en binaire!
//========================
#define mread(ptr,sz) n=lcfg_fread(ptr,sz,1,file); if(sz!=0) ntest(1);
#define ntest(num) if(n<num) { lcfg_free(lcfg); fclose(file); return LCFG_E_FREAD; }
#define quit(ret) { lcfg_free(lcfg); fclose(file); return ret;  }
static int lcfg_load_binary(LCFG *lcfg, const char *filename)
{
      FILE *file;
      char *header;
      size_t len=strlen(BINARY_HEADER);
      size_t n;
      int i;
      char c; //comment enabled

      // ouverture
      file = fopen(filename,"rb");
      if(!file) return LCFG_E_FOPEN;
      
      // header
      header = (char *)malloc(len);
      if(lcfg_fread(header,len,1,file)<1) 
            {free(header); fclose(file); return LCFG_E_FFORMAT;}
      if(strncmp(header,BINARY_HEADER,len)!=0) {
            free(header);
            fclose(file);
            return LCFG_E_FFORMAT;
      }
      free(header);

      // charge quelques trucs
      lcfg_new(lcfg,1); 
      n=lcfg_fread(&lcfg->default_comment,sizeof(lcfg->default_comment),1,file);
      n+=lcfg_fread(&lcfg->default_equal,sizeof(lcfg->default_equal),1,file);
      n+=lcfg_fread(&lcfg->autoindent,sizeof(lcfg->autoindent),1,file);
      n+=lcfg_fread(&lcfg->s_el,sizeof(lcfg->s_el),1,file);
      if(n<4) {fclose(file); return LCFG_E_FREAD;}

      // malloque el[]
      lcfg->el = (LCFG_ELEMENT *)malloc(sizeof(LCFG_ELEMENT)*lcfg->s_el);
      if(!lcfg->el) { fclose(file); return LCFG_E_OUTMEM; }
      memset(lcfg->el,0,sizeof(LCFG_ELEMENT)*lcfg->s_el);

      // charge tt ls éléments
      for(i=0;i<lcfg->s_el;i++) {
            mread(&lcfg->el[i].type,1);

            switch(lcfg->el[i].type) {
                  case LCFG_T_VAR:
                        // Len
                        mread(&len,sizeof(len)); 
                        //Var
                        if(!(lcfg->el[i].var = (char *)malloc(len+1))) {quit(LCFG_E_OUTMEM);}
                        mread(lcfg->el[i].var,len); 
                        lcfg->el[i].var[len]=0;
                        
                        // Len
                        mread(&len,sizeof(len)); 
                        //Value
                        if(!(lcfg->el[i].value = (char *)malloc(len+1))) {quit(LCFG_E_OUTMEM);}
                        mread(lcfg->el[i].value,len); 
                        lcfg->el[i].value[len]=0;

                        // guillemet
                        mread(&lcfg->el[i].guillemet,1); 
                        
                        // comment enabled
                        mread(&c,1); 

                        if(c) {
                              // COMMENT
                              // Len
                              mread(&len,sizeof(len)); 
                              //Value
                              if(!(lcfg->el[i].comment = (char *)malloc(len+1))) 
                              {quit(LCFG_E_OUTMEM);}
                              mread(lcfg->el[i].comment,len); 
                              lcfg->el[i].comment[len]=0;
                        }

                        break;

                  case LCFG_T_SECTION:
                        // Len
                        mread(&len,sizeof(len)); 
                        //Var
                        if(!(lcfg->el[i].var = (char *)malloc(len+1))) {quit(LCFG_E_OUTMEM);}
                        mread(lcfg->el[i].var,len); 
                        lcfg->el[i].var[len]=0;

                        // comment enabled ??
                        mread(&c,1); 
                        if(c) {
                              // alors ok charger un comment !!
                              // Len
                              mread(&len,sizeof(size_t)); 
                              //Value
                              if(!(lcfg->el[i].comment = (char *)malloc(len+1))) {quit(LCFG_E_OUTMEM);}
                              mread(lcfg->el[i].comment,len); 
                              lcfg->el[i].comment[len]=0;
                        }

                        break;

                  case LCFG_T_LINE:
                        // Len
                        mread(&len,sizeof(len)); 
                        //Value
                        if(!(lcfg->el[i].var = (char *)malloc(len+1))) {quit(LCFG_E_OUTMEM);}
                        mread(lcfg->el[i].var,len); 
                        lcfg->el[i].var[len]=0;
                        
                        break;

                  default:
                        quit(LCFG_E_FFORMAT);
            }
      }

      if(fclose(file)) return LCFG_E_FCLOSE; 
      return LCFG_OK;
}

// chargement d'un fichier de configuration en format
// de texte...
static int lcfg_load_text(LCFG *lcfg, const char *filename)
{
      char *ptr=0;
      LCFG_ELEMENT *el;
      
      // ouverture
      FILE *file = fopen(filename,"r");
      if(!file) return LCFG_E_FOPEN;

      // init 
      lcfg_new(lcfg,0);

      // chargement de l'ini
      while(!feof(file)) 
      {
            // lit une ligne
            ptr = lineinput(ptr,file);
            if(!ptr) { //pas assez de mémire
                  fclose(file); lcfg_free(lcfg);
                  return LCFG_E_OUTMEM;
            }

            // Ajouter un élément
            el = add_element(lcfg);
            if(!el) {
                  fclose(file); free(ptr); lcfg_free(lcfg);
                  return LCFG_E_OUTMEM;
            }

            // mets line dans el selon si c'est une variable ou une
            // ligne normale
            if(traiter_line(el,lcfg->uncomment_var,ptr,lcfg->default_comment,lcfg->default_equal)) {
                  fclose(file); free(ptr); lcfg_free(lcfg);
                  return LCFG_E_OUTMEM;
            }
      }

      // mets le pointeur à 0
      // if(ptr) car: il se pourrait que le fichier soit
      // vide et que la boucle reçoit un break avant 
      // d'appeler lineinput.
      if(ptr) free(ptr);

      // ici traite l'erreor d'fclose aussi!
      if(fclose(file)) {
            lcfg_free(lcfg);
            return LCFG_E_FCLOSE;
      }
      
      return LCFG_OK;

}

/**************************************
 * cherche si c est le premier carac-
 * tere (sans compter les espace et
 * tab).
 *
 * '        # salut'
 *
 * par exemple là # est le premier.
 **************************************/
static char *premier(char *str, char c)
{
      while(!(*str==c || *str==0)) {
            if(*str!=' ' && *str!='\t') return 0; // rien à part espaces!
            str++;
      }
      if(*str!=c) str=0;
      return str;
}

/**************************************
 * fonction qui teste si c'est une
 * variable ou une ligne normale
 * et enfin mets le résultat dans e
 * qui est soit une ligne soit une
 * variable avec sa valeur.
 *
 * nonz pour erreur de mémoire.
 *
 * comment contient le caractère 
 * ascii du commentaire.
 * Par exemple: '#'.
 **************************************/
static int traiter_line(LCFG_ELEMENT *e, char uncomment_var, char *line, char comment, char equal)
{
      char *diese, *egal;
      char *s;
      char *crochet,*crochet2;
      char *g1,*g2;
      
      memset(e,0,sizeof(LCFG_ELEMENT));

      diese = premier(line,comment);
      egal  = index(line,equal);
      crochet = premier(line,'[');
      crochet2 = rindex(line,']');

      // Si c'est une variable = valeur
      // ou encore #variable=valeur (varible commentée...)
      if((!diese && egal) || (diese && uncomment_var && egal>diese)) {
            // init type
            if(diese) {
                  e->type = LCFG_T_COMMENT_VAR; //variable commentée :-)
                  //sauvegarder le contenu pour qu'il remette ça 
                  //comme ce qui était avant
                  e->extra=strdup(line);
                  if(!e->extra) return LCFG_E_OUTMEM;
                  //fait comme si la diese n'existait pas :-)
                  line=diese+1;
                  egal = index(line,equal);
            }
            else
                  e->type = LCFG_T_VAR;

            // Enlever égal
            *egal=0;
            
            //init var
            s = trim(line); //prends var
            e->var=strdup(s);
            if(!e->var) return LCFG_E_OUTMEM;

            //enleve \n ds value
            s=rindex(egal+1,'\n'); if(s) *s=0;

            // Si il y a des guillemets
            s=egal+1; // value dans s
            
            // Dans le cas de var = "salut" # pipi roro
            g1 = premier(s,'\"');
            if(g1) {
                  g2 = index(g1+1,'\"');
                  if(!g2) {
                        goto normal;
                  }
                  *g2=0; // enlève g2

                  s=g2+1; // pour goto normal
                  // le contenu de sera restoré à la fin
                  // dans if(g1) ...

                  e->guillemet = 1; //guillemets !

                  // cherche le commentaire...
                  goto normal;
            }
            // dans le cas normal: var = joumla zouina ! # popo
            else {
                  normal:
                  // enleve le commentaire normalement
                  // la dernière #...
                  diese = index(s,comment);
                  if(diese) {
                        *diese=0;
                        e->comment=strdup(diese+1);
                        if(!e->comment) return LCFG_E_OUTMEM;
                  }
                  s=trim(s);
            }

            // cette dernière if() sert pour la compatibilité
            // avec le goto.
            if(g1) 
                  s=g1+1;
            else
                  s=trim(egal+1);

            //init value
            e->value=strdup(s);
            if(!e->value) return LCFG_E_OUTMEM;
      }
      // Section!
      else if(crochet && crochet2) {
            crochet2 = rindex(line,']');
            if(crochet2) {
                  *crochet2=0;
                  crochet++;
                  s = trim(crochet);

                  // init type
                  e->type = LCFG_T_SECTION;
                  
                  // var
                  e->var = strdup(s);
                  if(!e->var) return LCFG_E_OUTMEM;
            }
      }
      // Les autres choses
      else {
            e->type=LCFG_T_LINE;
            e->var =strdup(line);
            if(!e->var) return LCFG_E_OUTMEM;
      }

      return LCFG_OK;
}

/**************************************
 * charger une ligne entière
 * d'un fichier.
 *
 * n'enlève pas \n
 **************************************
 * str doit être soit null
 * soit pointeur malloc.
 * la fonction retourne une version
 * realloquée de str contenant
 * la ligne entière.
 **************************************
 * NULL si pas assez de mémoire...
 * str est désalloqué automatiquement.
 **************************************/
#define BUFFER_SIZE 1024
static char *lineinput(char *str, FILE* file)
{
      char buffer[BUFFER_SIZE];
      char *save;
      size_t len;

      // Vide str...
      str=(char *)realloc(save=str,1);
      if(!str) {
            free(save);
            return 0;
      }
      str[0]=0;
      
      //init
      len=0;

      // Commence le chargement de la ligne
      while(!(feof(file))) {
            buffer[0]=0;
            (char *)fgets(buffer,BUFFER_SIZE,file);
            len=+strlen(buffer);
            
            // Ajoute la chaine
            str = (char *)realloc(save=str,len+1);
            if(!str) { free(save); return 0; }
            strcat(str,buffer);
            
            // quitter si derniere
            if(len>0 && buffer[len-1]=='\n') break;
      }
      
      return str;
}
#undef BUFFER_SIZE


/********************************************
 * Ajoute un élément dans lcfg
 * et retourne le pointeur vers cet élément.
 * 
 * s'il n'est pas possible de l'ajouter
 * il retourne 0.
 ********************************************/
static LCFG_ELEMENT *add_element(LCFG *lcfg)
{
      LCFG_ELEMENT *ptr,*save;
      lcfg->el = (LCFG_ELEMENT *)realloc(save=lcfg->el, (++lcfg->s_el)*sizeof(LCFG_ELEMENT));
      // Pas assez de mémoire...
      if(!lcfg->el) {
            lcfg->el=save;
            // je ne fais pas ici memset car
            // unload a besoin du pointeur pour le désalloquer!
            return 0; 
      }

      // mets le pointeur alloqué à 0
      ptr = lcfg->el+lcfg->s_el-1;
      memset(ptr,0,sizeof(LCFG_ELEMENT));

      // et enfin retourne le pointeur
      return ptr;
}

/********************************************
 * Trim str
 ********************************************/
static char *trim(char *str)
{
      char *s=str+strlen(str)-1;
      if(s<str) return str;

      //rtrim
      while(*s==' ' || *s=='\t') {
            *(s--)=0;
            if(s==str-1) break;
      }

      //ltrim
      while(*str!=0 && (*str==' ' || *str=='\t')) {
            str++;
      }

      return str;
}

/************************************* 
 * SAVE TEXT
 *************************************/
static int lcfg_save_text_as(LCFG *lcfg, const char *filename)
{
      FILE *file = fopen(filename, "w");
      int i; // int pour se déplacer dans lcfg->s_el
      size_t j; //pour se déplacer dans les len
      size_t len;

      size_t max_len_var=0;   //mets le plus gros len de var et de value
      size_t max_len_value=0;  
      
      if(!file) 
            return LCFG_E_FOPEN;
      
      // calcul max_len_*
      for(i=0;i<lcfg->s_el;i++) {
            if(lcfg->el[i].type==LCFG_T_VAR || (lcfg->uncomment_var && lcfg->el[i].type==LCFG_T_COMMENT_VAR)) {
                  size_t len=strlen(lcfg->el[i].var);
                  if(max_len_var<len) max_len_var=len;

                  len=strlen(lcfg->el[i].value);
                  if(max_len_value<len) max_len_value=len;
            }
      }
      
      // sauvegarde
      for(i=0;i<lcfg->s_el;i++) {
            //variable commentée?
            //il réinitialise le tout comme ce qui était au tout début
            if(lcfg->el[i].type==LCFG_T_COMMENT_VAR) {
                  len+=fprintf(file,"%s",lcfg->el[i].extra);
            }
            //variable normale
            else if(lcfg->el[i].type==LCFG_T_VAR) {
                  if(lcfg->el[i].type==LCFG_T_COMMENT_VAR) len+=fprintf(file,"%c",lcfg->default_comment);

                  // Afficher: var =
                  len=fprintf(file,"%s",lcfg->el[i].var);

                  // Affiche les espaces pour un bon affichage!
                  if(lcfg->autoindent)
                        for(j=len;j<=max_len_var+1;j++)  // +1 espaces
                              fputc(' ',file);
                  //else TODO faire espace personnalisé
                  //    fprintf(file," ");
                  
                  // Afficher '= '
                  len+=fprintf(file,"%c",lcfg->default_equal);

                  // Afficher la valeur de la variable
                  len=0; //=0 pour le commentaire
                  if(lcfg->el[i].guillemet)
                        len+=fprintf(file,"\"%s\"",lcfg->el[i].value)-1; //2="" 1=espace
                  else
                        len+=fprintf(file,"%s",lcfg->el[i].value)-1; //1=espace

                  // tout cela pour toi oh commentaire ;)
                  if(lcfg->el[i].comment)  {
                        if(lcfg->autoindent)
                              for(j=len;j<=max_len_value+4;j++) // +4 espaces
                                    fputc(' ',file);
                        else
                              fprintf(file,"\t\t\t");

                        // Affiche le commentaire, si commentaire il y a
                        fprintf(file,"%c%s",lcfg->default_comment,lcfg->el[i].comment);
                  }
                  fprintf(file,"\n");
            }
            // SECTION!
            if(lcfg->el[i].type==LCFG_T_SECTION) {
                  fprintf(file,"[ %s ]",lcfg->el[i].var);
                  
                  if(lcfg->el[i].comment) 
                        fprintf(file,"\t\t%c%s",lcfg->default_comment,lcfg->el[i].comment);
                  fprintf(file,"\n");
            }

            // si ligne normale, écrite telle qu'elle est!
            else if(lcfg->el[i].type==LCFG_T_LINE) 
                  fprintf(file,"%s",lcfg->el[i].var);

      }
      
      if(fclose(file)) return LCFG_E_FCLOSE;
      
      return LCFG_OK;
}

/************************************* 
 * SAVE BINARY
 *
 * Format de fichier:
 * ==================
 * HEADER
 * char default_comment
 * char default_equal
 * char autoindent
 * s_el (int)
 *
 * tout les éléments
 *
 * élément:
 * ========
 *    char type
 *    
 *    si VAR
 *    ======
 *    size_t len_var
 *    char *var (avec zero)
 *
 *    size_t len_value
 *    char *value (avec zero)
 *
 *    char guillemet
 *
 *    char comment_enabled
 *    si oui:
 *          size_t len_comment
 *          char *comment
 *
 *    si SECTON:
 *    ==========
 *    char *var
 *
 *    char comment_enabled
 *    si oui:
 *          size_t len_comment;
 *          char *comment
 * 
 *    Autre:
 *    ======
 *    size_t len
 *    char *var
 *
 *************************************/
static int lcfg_save_binary_as(LCFG *lcfg, const char *filename)
{
      FILE *file = fopen(filename,"wb");
      char header[]=BINARY_HEADER;
      int i;
      size_t n=0;
      char c;
      size_t len;
      
      //TODO: Ajouter au lieu de remove un fcopy /tmp/fichier dans fichier
      //efface c mieux...
      
      // ouverture!
      if(!file) return LCFG_E_FOPEN;

      // Header
      n+=lcfg_fwrite(header,strlen(header),1,file);
      
      // Trucs de début
      n+=lcfg_fwrite(&lcfg->default_comment,sizeof(lcfg->default_comment),1,file);
      n+=lcfg_fwrite(&lcfg->default_equal,sizeof(lcfg->default_equal),1,file);
      n+=lcfg_fwrite(&lcfg->autoindent,sizeof(lcfg->autoindent),1,file);
      n+=lcfg_fwrite(&lcfg->s_el,sizeof(lcfg->s_el),1,file);
      if(n<5) { fclose(file); remove(filename); return LCFG_E_FWRITE; }

      // sauvegarde le tout!
      for(i=0;i<lcfg->s_el;i++) {
            n=lcfg_fwrite(&lcfg->el[i].type,sizeof(lcfg->el[i].type),1,file);

            switch(lcfg->el[i].type) 
            {
                  // VAR
                  case LCFG_T_VAR:
                        // save VAR
                        len=strlen(lcfg->el[i].var);
                        n+=lcfg_fwrite(&len,sizeof(len),1,file);
                        n+=lcfg_fwrite(lcfg->el[i].var,len,1,file);
                        if(len==0) n++; //bien sûre... sinon il croira qu'il
                                        //ne l'as pas lu...

                        // save VALUE
                        len=strlen(lcfg->el[i].value);
                        n+=lcfg_fwrite(&len,sizeof(len),1,file);
                        n+=lcfg_fwrite(lcfg->el[i].value,len,1,file);
                        if(len==0) n++; //bien sûre... sinon il croira qu'il
                                        //ne l'as pas lu...

                        // guillemet
                        n+=lcfg_fwrite(&lcfg->el[i].guillemet,sizeof(char),1,file);
                        
                        // save 
                        if(lcfg->el[i].comment) {
                              //comment enabled
                              c=1;
                              n+=lcfg_fwrite(&c,sizeof(c),1,file);
                              
                              // comment
                              len=strlen(lcfg->el[i].comment);
                              n+=lcfg_fwrite(&len,sizeof(len),1,file);
                              n+=lcfg_fwrite(lcfg->el[i].comment,len,1,file);
                              if(len==0) n++; //bien sûre... sinon il croira qu'il
                                        //ne l'as pas lu...

                              if(n<9) { fclose(file); remove(filename); return LCFG_E_FWRITE; }
                              //9 car il prends en compte le premier n= avant switch
                        }
                        else {
                              c=0; //comment=false
                              n+=lcfg_fwrite(&c,sizeof(c),1,file);
                              if(n<7) { fclose(file); remove(filename); return LCFG_E_FWRITE; }
                        }
                              
                        break;
                        
                  case LCFG_T_SECTION:
                        // save VAR
                        len=strlen(lcfg->el[i].var);
                        n+=lcfg_fwrite(&len,sizeof(len),1,file);
                        n+=lcfg_fwrite(lcfg->el[i].var,len,1,file);
                        if(len==0) n++;

                        //comment...
                        if(lcfg->el[i].comment) {
                              //comment_enabled
                              c=1;
                              n+=lcfg_fwrite(&c,len,1,file);

                              // comment...
                              len=strlen(lcfg->el[i].comment);
                              n+=lcfg_fwrite(&len,sizeof(len),1,file);
                              n+=lcfg_fwrite(lcfg->el[i].comment,len,1,file);
                              if(len==0) n++; //bien sûre... sinon il croira qu'il
                                              //ne l'as pas lu...
                              if(n<6) { fclose(file); remove(filename); return LCFG_E_FWRITE; }
                        }
                        else {
                              c=0;
                              n+=lcfg_fwrite(&c,len,1,file);
                              if(n<4) { fclose(file); remove(filename); return LCFG_E_FWRITE; }
                        }
                        break;

                  case LCFG_T_LINE:
                        // line !
                        len=strlen(lcfg->el[i].var);
                        n+=lcfg_fwrite(&len,sizeof(len),1,file);
                        n+=lcfg_fwrite(lcfg->el[i].var,len,1,file);
                        if(len==0) n++; //bien sûre... sinon il croira qu'il
                                        //ne l'as pas lu...
                        
                        if(n<3) { fclose(file); remove(filename); return LCFG_E_FWRITE; }
                        break;
            }
      }

      // début!
      if(fclose(file)) return LCFG_E_FCLOSE; //pas de remove ici

      return LCFG_OK;
}

/************************************* 
 * SAVE AS (générale)
 *************************************/
int lcfg_save(LCFG *lcfg, const char *filename)
{ return lcfg_save_ex(lcfg,filename,lcfg->binary); }

int lcfg_save_ex(LCFG *lcfg, const char *filename, char binary)
{
      return (binary)?
            lcfg_save_binary_as(lcfg,filename)
            :
            lcfg_save_text_as(lcfg,filename);
}


Generated by  Doxygen 1.6.0   Back to index