   C functions to read/write ini files
  Hi everybodyI've completed two functions to "read from" and "write to" ini files. Since I've noticed other people looked for similar things in this forum I thought I'd share them with you. :-)here's the code...// // Two C functions to read from and write to ini-files.//// All code was written by Roman Hoegg, 2006 (romanh_at_gmx_dot_ch )// You may use this code for whatever you like//// Of course, it would be cool if you would send me an email if the// code was helpful to you. (but you don't _have_ to do that.)//// (the code works in windows. Don't know about other OS'...)//// Places where I found "inspiration" and help:// http://www.cl.uni-heidelberg.de/kurs/ws02/prog2/html/page029.html// http://www.thescripts.com/forum/threadnav214876-1-10.html// and of course the gamedev.net forums!!!// thanks!!!//// The test.ini file could look something like this:// // [Section1]// ;lines that start with ";" are considered comments and are skipped// [Section2]// ;a blank space (" ") is expected before and after "="// Entry1 = some value// Entry2 = some other value// Entry3 = yet another value// [Section3]// [Section4]// [Section5]// [Section6]// #include <stdio.h>#include <stdlib.h>#include <string.h>#define INI_FILENAME "test.ini" // name of the ini file#define MAX_LENGTH_OF_LINE 100  // maximum length of a linechar *readFromINI(char *iniFile, char *section, char *entry){  char buffer[MAX_LENGTH_OF_LINE];  // = holds the content of current line  char *foundSection;  // = picking out the section name  char *foundEntry;  // = picking out the section name  int sectionLength = 0;  int entryLength = 0;  bool sectionFound = false;  FILE *cfgFile=fopen(iniFile, "r"); //open the file  if (!cfgFile) { // if file doesn't exist do something...    // insert error routine here...    return 0;  }   //go through the lines of the ini  while (fgets(buffer, MAX_LENGTH_OF_LINE, cfgFile))  {    if (buffer[0]==';') {continue;} // skip comments    if(sectionFound == false)    { foundSection = buffer; while (*foundSection && *foundSection!='[') {foundSection++;} if (*foundSection!='[') {continue;} *foundSection++ = 0; // delete "["  while (*foundSection && *foundSection!=']') {foundSection++; sectionLength++;} if (*foundSection!=']') {continue;} *foundSection++ = 0; // delete "]"  while (*foundSection) {foundSection++;}  *--foundSection = 0;  for(int i = sectionLength+1; i > 0; i--){foundSection--;} sectionLength = 0;  if (strcmp(foundSection, section) == 0) // compare the char arrays  {    sectionFound = true; // found the section we were looking for    continue;  }    } // = end if sectionFound == false    if(sectionFound == true)    { if (buffer[0]=='[') {sectionFound = false; continue;} foundEntry = buffer; while (*foundEntry && *foundEntry!='=') {foundEntry++;} if (*foundEntry!='=') {continue;} *foundEntry-- = 0; // delete " " before the "=" *foundEntry++ = 0; // delete " " before the "=" *foundEntry++ = 0; // delete "=" *foundEntry++ = 0; // delete " " after the "=" // Skip to end of string and delete lf (=line feed char) while (*foundEntry) {foundEntry++; entryLength++;} *--foundEntry = 0; // move back to the beginning of the value of the entry for(int i = entryLength-1; i > 0; i--){foundEntry--;} entryLength = 0; if(strcmp(buffer, entry) == 0) // found the entry we were looking for {   char *returnValue = new char[MAX_LENGTH_OF_LINE];   strcpy( returnValue, foundEntry );   fclose (cfgFile);   return returnValue; }    } // = end if sectionFound == true  } // = end of skipping through the lines}int writeToINI(char *filename, char *section, char *entry, char *value){  char buffer[MAX_LENGTH_OF_LINE];  // = holds the content of current line  char *foundSection;  // = picking out the section name  char *foundEntry;  // = picking out the section name  int sectionLength = 0;  int entryLength = 0;  bool sectionFound = false;  int sectionline = 0;  int entryline = 0;  int lastline = 0;  bool entryfound = false;  int linecounter=0;  FILE *cfgFile=fopen(filename, "r"); //open the file  if (!cfgFile) { // if file doesn't exist do something...    // insert error routine here...    return 0;  }   //go through the lines one by one  while (fgets(buffer, MAX_LENGTH_OF_LINE, cfgFile))  {    linecounter++;    if (buffer[0]==';') {continue;} // skip comments    if(sectionFound == false)    { foundSection = buffer; while (*foundSection && *foundSection!='[') {foundSection++;} if (*foundSection!='[') {continue;} *foundSection++ = 0; // delete "[" while (*foundSection && *foundSection!=']') {foundSection++; sectionLength++;} if (*foundSection!=']') {continue;} *foundSection++ = 0; // delete "]" while (*foundSection) {foundSection++;}  *--foundSection = 0; for(int i = sectionLength+1; i > 0; i--){foundSection--;} sectionLength = 0; if (strcmp(foundSection, section) == 0) // compare the char arrays  {   sectionFound = true; // found the section we were looking for   sectionline = linecounter; // store the location of the section   continue;  }    } // = end if sectionFound == false    if(sectionFound == true)    {    if (buffer[0]=='[') {sectionFound = false; continue;} foundEntry = buffer; while (*foundEntry && *foundEntry!='=') {foundEntry++;} if (*foundEntry!='=') {continue;} *foundEntry-- = 0; // delete " " before the "=" *foundEntry++ = 0; // delete " " before the "=" *foundEntry++ = 0; // delete "=" *foundEntry++ = 0; // delete " " after the "=" // Skip to end of string and delete lf (=line feed char) while (*foundEntry) {foundEntry++; entryLength++;} *--foundEntry = 0; // move back to the beginning of the value of the entry for(int i = entryLength-1; i > 0; i--){foundEntry--;} entryLength = 0; if(strcmp(buffer, entry) == 0) {   entryfound = true; // found the entry we were looking for   entryline = linecounter; // store the location of the entry }    } // = end if sectionFound == true  } // = end of skipping through the lines  fclose (cfgFile);  lastline = linecounter; //store the total number of lines// ok, now we know where the line is we want to replace// so let's replace it!  linecounter = 0;  FILE* in_file = NULL;  FILE* out_file = NULL;  char* temporary_file_name = NULL;  char line[128];  in_file = fopen(filename, "r"); // open the file  if(in_file == NULL){ // if the file doesn't exist, do something...  //insert error routine here...  return 1;  }  temporary_file_name = tmpnam(NULL);  out_file = fopen(temporary_file_name, "w");  while(fgets(line, sizeof(line), in_file)) // go through the lines  {    linecounter++;    if(sectionline > 0 && entryline > 0) //section and entry already exist    { if(linecounter == entryline) {   strcpy(line, entry);   strcat(line, " = ");   strcat(line, value);   strcat(line, "\n"); }    }    if(sectionline > 0 && entryline == 0) //section exists but entry doesn't    { if(linecounter == sectionline) { strcat(line, "\n"); //needed just in cast there is no line break strcat(line, entry); strcat(line, " = "); strcat(line, value); strcat(line, "\n"); }    } if(sectionline == 0) //section does not yet exist    { if(linecounter == lastline) // add the new section to the end of the file {   strcat(line, "\n[");   //needed just in cast there is no line break   strcat(line, section);   strcat(line, "]\n");   strcat(line, entry);   strcat(line, " = ");   strcat(line, value);   strcat(line, "\n"); } }    fputs(line, out_file);  } // end of going through the lines  fclose(in_file);  fflush(out_file);  fclose(out_file);  remove(INI_FILENAME);  rename(temporary_file_name, INI_FILENAME);  return 0;  }int main(void){  // an example of how to call the functions...  char *value = readFromINI(INI_FILENAME, "Section2", "Entry2");  printf("returned value from readFromINI: %s\n", value);  free(value);  writeToINI(INI_FILENAME, "Section2", "Entry2", "I've change this value! Weehee!!!");  getchar ();  return 0;}[/SOURCE]and here's an example ini file... (test.ini)[SOURCE][Section1];lines that start with ";" are considered comments and are skipped[Section2];a blank space (" ") is expected before and after "="Entry1 = some valueEntry2 = some other valueEntry3 = yet another value[Section3][Section4][Section5][Section6][/SOURCE]Feel free to use it for whatever you want. If you have suggestions for improvements please let me know!RomanH[Edited by - RomanH on June 23, 2006 5:41:04 AM]
  Stirling effort. Just two questions:What happens if a blank line is read by the first function? All that pointer manipulation looks a bit dangerous at first glance.If it reads a line longer than MAX_LINE_LENGTH, should it not then skip the rest of the characters on the line before it reads the next one? (If it does, and I've missed it, I apologise in advance).Don't mean to nitpick. Just get a bit scared of some horrible crashes if the input file does not match the expected format [smile].   ;  > All that pointer manipulation looks > a bit dangerous at first glance.Don't hesitate to nitpick! :-) That's one of the reasons I've put the code here. It can defininitely be improved by from some form of peer review. All I know about pointers I read in books and on the net. My knowledge about them is therefore, well, a little patchy.Nevertheless, I've just tried it with the following test.ini (and there were no problems at all.)[Section1];lines that start with ";" are considered comments and are skipped[Section2];a blank space (" ") is expected before and after "="Entry0 = some value which is very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very longEntry1 = some valueEntry2 = some other valueEntry3 = yet another value[Section3][Section4][Section5][Section6]Empty lines (= lines only consisting of "\n") are considered lines without "[" or ";" at the beginning and without "=" somewhere in the middle. They are therefore simply ignored.Currently I only plan to use these functions on ini-files I've created myself and therefore apply to my standard. But of course, some further testing and improving has to be done. :-)RomanH   ;  Groovy then. My advance apology applies.Only other thing you *might* want to consider is, from a performance point of view, reading the file into memory and parsing it there. Also, if you did this when writing the file as well, it would not be necessary to use a temporary filename any more.This is really a performance vs space trade off though and I believe that the WinAPI functions for reading and writing INI files work in a similar way to yours.   