/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

#include "IniFile.h"
#include "stdio.h"
#include "string.h"
#include <ctype.h>

static char defaultIniFile[] = "Jonah.ini";
#ifdef _WIN32
static char defaultIniFileDir[] = "C:\\"; 
// Root directory of C: probably isn't the best place for INI-files, but 
// it's not clear what the best place is.  We should really rely on our
// caller to provide a place.
#else
static char defaultIniFileDir[] = "/etc/";
#endif


static char * copyOf(const char * s) {
  if (s == NULL) return NULL;
  char * sc = (char *)malloc(strlen(s)+1);
  strcpy(sc, s);
  return sc;
}

IniFile::IniFile(const char * fname, bool writeable) {

  FILE * f;
  char line[1024];
  char * ptr;
  char * ptr1;
  char * ptr2;
  int i;
  int sectionIndex;
  StringList * currentSection = NULL;

  fnam = NULL;
  modifiable = writeable;
  modified = false;
  
  if (fname == NULL) {
    strcpy(line, defaultIniFileDir);
    strcat(line, defaultIniFile);
  } else if ( (fname[0] != '/') && (fname[0] != '\\') && ( !isalpha(fname[0]) || (fname[1] != ':'))) {
// The filename we've been given is a relative one, so prepend the default directory
    strcpy(line, defaultIniFileDir);
    strcat(line, fname);
  } else {
    strcpy(line, fname);
  };
// Now process the file to munge both '/' and '\' characters to the current directory seperator
  for (i=0; line[i] != 0; i++) if ((line[i] == '/') || (line[i] == '\\')) line[i] = DIR_SEP_CHARACTER;

  f = fopen(line, "r");
  if (f == NULL) return;
  
  fnam = (char *)malloc(strlen(line)+1);
  strcpy(fnam, line);

  sectionIndex = -1;

  sections.DuplicatesAllowed = false;
  sections.Sorted = false;
  
  while (fgets(line, sizeof(line), f) != NULL) {
    if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0;
    ptr = line;
    while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
    if (*ptr == 0) continue;
    if (*ptr == '[') {
// New section...
      ptr1 = ++ptr;
      while ((*ptr1 != 0) && (*ptr1 != ']')) ptr1++;
      *ptr1 = 0;
      if ((sectionIndex = sections.IndexOf(ptr)) < 0) sectionIndex = sections.AddItem(ptr);

      if ((currentSection = (StringList*)sections.Object(sectionIndex)) == NULL) {
        currentSection = new StringList;
        currentSection->DuplicatesAllowed = false;
        currentSection->Sorted = false;
        sections.SetObject(sectionIndex, currentSection);
      };
      currentSection = (StringList *)sections.Object(sectionIndex);

    } else {
      ptr1 = ptr;
      while ((*ptr1 != 0) && (*ptr1 != '=')) ptr1++;
      if (*ptr1 == 0) continue;
      
      ptr2 = ptr1++;
      *ptr2-- = 0;
      while ((ptr2 > ptr) && ((*ptr2 == ' ')  || (*ptr2 == '\t'))) *ptr2-- = 0;

      ptr2 = ptr1;
      while (*ptr2 != 0) *ptr2++;
      ptr2--;
      while ((ptr2 > ptr1) && ((*ptr2 == ' ')  || (*ptr2 == '\t'))) *ptr2-- = 0;
      while ((*ptr1 == '\t') || (*ptr1 == ' ')) ptr1++;

// Now ptr and ptr1 point to the trimmed key and value respectively.

      if (currentSection == NULL) {
// We're in the "global section"
        if ((sectionIndex = sections.IndexOf("[noname]")) < 0) sectionIndex = sections.AddItem("[noname]");
        
        if ((currentSection = (StringList *)sections.Object(sectionIndex)) == NULL) {
          currentSection = new StringList;
          currentSection->DuplicatesAllowed = false;
          currentSection->Sorted = false;
          sections.SetObject(sectionIndex, currentSection);
        };
      };
      currentSection->AddItem(ptr, copyOf(ptr1));
    };
  };
  fclose(f);
}

IniFile::IniFile(const char * fname) {
  IniFile(fname, false);
}

IniFile::IniFile(bool writeable, const char * fname) {
  IniFile(fname, false);
}

IniFile::~IniFile() {
  unsigned sectionCount;
  unsigned keyCount;
  unsigned i;
  unsigned j;
  char * key;
  StringList * currentSection = NULL;

  if (modified && modifiable) {
    writeFile();  
  };

  sectionCount = sections.Count();
  for (i=0; i<sectionCount; i++) {
    if ((currentSection = (StringList *)sections.Object(i)) != NULL) {
      keyCount = currentSection->Count();
      for (j=0; j<keyCount; j++) if ((key = (char *)currentSection->Object(j)) != NULL) delete key;
      delete currentSection;
    };
  };
}


// The readXXX members return true if the value was read from the
// file, false if the default value was used.
bool IniFile::readString(const char * theSection, 
                         const char * key,
                         char * buffer,
                         size_t bufferLength,
                         const char * defaultValue) {
  int sectionIndex;
  int keyIndex;
  StringList * section;

  if ((sectionIndex = sections.IndexOf(theSection)) < 0) {
    if (defaultValue) {
      strncpy(buffer, defaultValue, bufferLength);
      buffer[bufferLength-1] = 0;
    } else buffer[0] = 0;
    return false;
  };

  if ((section = (StringList *)sections.Object(sectionIndex)) == NULL) {
    if (defaultValue) {
      strncpy(buffer, defaultValue, bufferLength);
      buffer[bufferLength-1] = 0;
    } else buffer[0] = 0;
    return false;
  };

  if ((keyIndex = section->IndexOf(key)) < 0) {
    if (defaultValue) {
      strncpy(buffer, defaultValue, bufferLength);
      buffer[bufferLength-1] = 0;
    } else buffer[0] = 0;
    return false;
  };

  strncpy(buffer, (char *)section->Object(keyIndex), bufferLength);
  buffer[bufferLength-1] = 0;
  return true;
}

bool IniFile::readInteger(const char * theSection,
                          const char * key,
                          int & value,
                          int defaultValue) {
  int sectionIndex;
  int keyIndex;
  StringList * section;

  if ((sectionIndex = sections.IndexOf(theSection)) < 0) {
    value = defaultValue;
    return false;
  };

  if ((section = (StringList *)sections.Object(sectionIndex)) == NULL) {
    value = defaultValue;
    return false;
  };

  if ((keyIndex = section->IndexOf(key)) < 0) {
    value = defaultValue;
    return false;
  };

  if (sscanf((char *)section->Object(keyIndex), "%d", &value) != 1) {
    value = defaultValue;
    return false;
  };

  return true;

}

bool IniFile::readBoolean(const char * theSection,
                          const char * key,
                          bool & value,
                          bool defaultValue) {
  int sectionIndex;
  int keyIndex;
  StringList * section;
  char * ptr;
  
  if ((sectionIndex = sections.IndexOf(theSection)) < 0) {
    value = defaultValue;
    return false;
  };

  if ((section = (StringList *)sections.Object(sectionIndex)) == NULL) {
    value = defaultValue;
    return false;
  };

  if ((keyIndex = section->IndexOf(key)) < 0) {
    value = defaultValue;
    return false;
  };

  ptr = (char *)section->Object(keyIndex);
  
  if ((*ptr == 't') || (*ptr == 'T') || (*ptr == '1') || (*ptr == 'y') || (*ptr == 'Y')) {
    value = true;
    return true;
  } else if ((*ptr == 'f') || (*ptr == 'F') || (*ptr == '0') || (*ptr == 'n') || (*ptr == 'N')) {
    value = false;
    return true;
  };

  value = defaultValue;
  return false;
  return true;

}

// The writeXXX members return false if the IniFile was
// not marked modifiable when it was created.
bool IniFile::writeString(const char * theSection,
                          const char * key,
                          const char * value) {
  int sectionIndex;
  int keyIndex;
  StringList * section;

  if (!modifiable) return false;

  if ((sectionIndex = sections.IndexOf(theSection)) < 0) sectionIndex = sections.AddItem(theSection);  

  if ((section = (StringList *)sections.Object(sectionIndex)) == NULL) {
    section = new StringList;
    section->DuplicatesAllowed = false;
    section->Sorted = false;
    sections.SetObject(sectionIndex, section);
  };

  if ((keyIndex = section->IndexOf(key)) >= 0) {
    free(section->Object(keyIndex));
  };
  section->AddItem(key, copyOf(value));
  modified = true;
  return true;
}



bool IniFile::writeInteger(const char * theSection,
                           const char * key,
                           int value) {
  int sectionIndex;
  int keyIndex;
  StringList * section;
  char lbuf[10];

  if (!modifiable) return false;

  sprintf(lbuf, "%d", value);

  if ((sectionIndex = sections.IndexOf(theSection)) < 0) sectionIndex = sections.AddItem(theSection);  

  if ((section = (StringList *)sections.Object(sectionIndex)) == NULL) {
    section = new StringList;
    section->DuplicatesAllowed = false;
    section->Sorted = false;
    sections.SetObject(sectionIndex, section);
  };

  if ((keyIndex = section->IndexOf(key)) >= 0) {
    free(section->Object(keyIndex));
  };
  section->AddItem(key, copyOf(lbuf));
  modified = true;
  return true;
}

bool IniFile::writeBoolean(const char * theSection,
                           const char * key,
                           bool value) {
  
  int sectionIndex;
  int keyIndex;
  StringList * section;
  char lbuf[2];

  if (!modifiable) return false;

  if (value) lbuf[0] = 'Y';
  else lbuf[0] = 'N';
  lbuf[1] = 0;

  if ((sectionIndex = sections.IndexOf(theSection)) < 0) sectionIndex = sections.AddItem(theSection);  

  if ((section = (StringList *)sections.Object(sectionIndex)) == NULL) {
    section = new StringList;
    section->DuplicatesAllowed = false;
    section->Sorted = false;
    sections.SetObject(sectionIndex, section);
  };

  if ((keyIndex = section->IndexOf(key)) >= 0) {
    free(section->Object(keyIndex));
  };
  section->AddItem(key, copyOf(lbuf));
  modified = true;
  return true;
}

// getSections returns a list of all the sections in the file.
void IniFile::getSections(StringList & sl) {
  unsigned i;
  unsigned sectionCount;
  sl.empty();
  sectionCount = sections.Count();
  for (i=0; i<sectionCount; i++) {
    sl.AddItem(sections[i]);
  };
}


// getKeys returns a list of all the keys in the specified section.
bool IniFile::getKeys(const char * theSection,
                      StringList & sl) {
  unsigned sectionIndex;
  StringList * section;
  unsigned i;
  unsigned keyCount;

  sl.empty();

  if ((sectionIndex = sections.IndexOf(theSection)) < 0) return false;
  section = (StringList *)sections.Object(sectionIndex);
  if (section == NULL) return false;

  keyCount = section->Count();
  for (i=0; i<keyCount; i++) {
    sl.AddItem((*section)[i]);
  };

  return true;
}

// WriteFile returns true if the file was written succesfully.
// WriteFile is automatically called from the IniFile destructor
// if there are un-written changes.
bool IniFile::writeFile(void) {
  unsigned sectionCount;
  unsigned keyCount;
  unsigned sectionIndex;
  unsigned keyIndex;
  const char * sName;
  StringList * section;
  const char * key;
  const char * value;
  FILE * f;

  if (!modifiable) return false;
  if (fnam == NULL) return false;
  if (!modified) return true;
  if ((f = fopen(fnam, "w")) == NULL) return false;

  sectionCount = sections.Count();

  for (sectionIndex = 0;
       sectionIndex < sectionCount;
       sectionIndex++) {
    sName = sections[sectionIndex];
    if (strcmp(sName, "[noname]") != 0) fprintf(f, "[%s]\n", sName);
    if ((section = (StringList *)sections.Object(sectionIndex)) != NULL) {
      keyCount = section->Count();
      for (keyIndex = 0;
           keyIndex < keyCount;
           keyIndex++) {
        key = (*section)[keyIndex];
        value = (const char *)section->Object(keyIndex);
        if (value == NULL) fprintf(f, "%s=\n", key);
        else fprintf(f, "%s=%s\n", key, value);
      };
    };
  };
  fclose(f);
  return true;
}
