A simple .ini file interface.
Dependents: Smart-WiFly-WebServer SignalGenerator WattEye X10Svr
Diff: IniManager.cpp
- Revision:
- 0:ae5bf432c249
- Child:
- 1:1e2ee9bbee40
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IniManager.cpp Mon Aug 12 22:57:54 2013 +0000 @@ -0,0 +1,361 @@ +// Simple INI file manager. +// +#ifdef WIN32 +#include "string.h" +#include "stdlib.h" +#include "stdio.h" +#else +#include "mbed.h" +#endif + +#include "IniManager.h" + +INI::INI(const char * file) + : iniFile(0) +{ + if (file) { + iniFile = (char *)malloc(strlen(file)+1); + if (iniFile) + strcpy(iniFile, file); + } + FILE * fo = fopen("/local/test.txt", "wt"); + if (fo) { + printf("Writing to /local/test.txt\r\n"); + fprintf(fo, "This is three - %d\r\n", 3); + fclose(fo); + } +} + + +INI::~INI(void) +{ + if (iniFile) + free(iniFile); +} + + +bool INI::ReadString(const char * section, const char * key, char * buffer, size_t bufferSize, const char * defaultString) +{ + bool found = false; + if (!iniFile) + return found; + CrashRecover(); + printf("ReadString from %s\r\n", iniFile); + FILE * fp = fopen(iniFile,"rt"); + if (fp) { + char buf[INTERNAL_BUF_SIZE]; + bool inSection = (section == NULL) ? true : false; + + while(fgets(buf, sizeof(buf), fp)) { + int x = strlen(buf) - 1; // remove trailing \r\n combinations + while (x >= 0 && buf[x] < ' ') + buf[x--] = '\0'; + printf("read in [%s]\r\n", buf); + if (inSection && buf[0] != '[') { + char * eq = strchr(buf, '='); + if (eq) { + *eq++ = '\0'; + if ( (strcmp(buf,key) == 0) && (strlen(eq) <= bufferSize) ) { + strcpy(buffer, eq); + memset(buf, 0, INTERNAL_BUF_SIZE); // secure the memory space + found = true; + break; + } + } + } else { + if (buf[0] == '[') { + char * br = strchr(buf, ']'); + inSection = false; + if (br) { + *br = '\0'; + if (strcmp(buf+1, section) == 0) + inSection = true; + } + } + } + } + fclose(fp); + } + if (!found && defaultString != NULL && *defaultString) { + strncpy(buffer, defaultString, bufferSize); + buffer[bufferSize-1] = '\0'; + printf("sub %s.\r\n", buffer); + found = true; + } + return found; +} + +bool INI::CrashRecover() +{ + char * newFile = (char *)malloc(strlen(iniFile)+1); + char * bakFile = (char *)malloc(strlen(iniFile)+1); + + if (newFile && bakFile) { + printf("*** CrashRecover\r\n"); + strcpy(bakFile, iniFile); + strcpy(newFile, iniFile); + strcpy(bakFile + strlen(bakFile) - 4, ".bak"); + strcpy(newFile + strlen(newFile) - 4, ".new"); + + FILE * repair = fopen(newFile, "rt"); + if (repair) { + // helps recover if the system crashed before it could swap in the new file + printf("*** repairing\r\n"); + fclose(repair); + int i; + i = remove(bakFile); // remove an old .bak + printf("remove(%s) returned %d\r\n", bakFile, i); + i = Rename(iniFile, bakFile); // move the existing .ini to .bak + printf("rename(%s,%s) returned %d\r\n", iniFile, bakFile, i); + i = Rename(newFile, iniFile); // move the new .new to .ini + printf("rename(%s,%s) returned %d\r\n", newFile, iniFile, i); + } + } + free(newFile); + free(bakFile); + return true; +} + +// Create the new version as .new +// once complete, if something actually changed, then rename the .ini to .bak and rename the .new to .ini +// once complete, if nothing actually changed, then delete the .new +// +bool INI::WriteString(const char * section, const char * key, char * value) +{ + bool found = false; + bool fileChanged = false; + + if (!iniFile || (value != NULL && strlen(value) > INTERNAL_BUF_SIZE)) + return found; + + char * newFile = (char *)malloc(strlen(iniFile)+1); + char * bakFile = (char *)malloc(strlen(iniFile)+1); + if (!newFile) + return found; // no memory + if (!bakFile) { + free(newFile); + return found; + } + strcpy(bakFile, iniFile); + strcpy(newFile, iniFile); + strcpy(bakFile + strlen(bakFile) - 4, ".bak"); + strcpy(newFile + strlen(newFile) - 4, ".new"); + + CrashRecover(); + + printf("Opening [%s] and [%s]\r\n", iniFile, newFile); + FILE * fi = fopen(iniFile, "rt"); + FILE * fo = fopen(newFile, "wt"); + if (fo) { + char buf[INTERNAL_BUF_SIZE]; + bool inSection = (section == NULL) ? true : false; + + if (fi) { + while(fgets(buf, sizeof(buf), fi)) { + // if not inSection, copy across + // if inSection and not key, copy across + // if InSection and key, write new value (or skip if value is null) + int x = strlen(buf) - 1; // remove trailing \r\n combinations + while (x >= 0 && buf[x] < ' ') + buf[x--] = '\0'; + if (inSection && buf[0] != '[') { + char * eq = strchr(buf, '='); + if (eq) { + *eq++ = '\0'; + if (strcmp(buf,key) == 0) { + if (value != NULL && strcmp(eq, value) != 0) { + // replace the old record + if (value != NULL) { + fprintf(fo, "%s=%s\n", key, value); + printf("write: %s=%s\r\n", key, value); + } + } + fileChanged = true; + inSection = false; + found = true; + } else { + // write old record + fprintf(fo, "%s=%s\n", buf, eq); + printf("write: %s=%s\r\n", buf, eq); + } + } else { + // what to do with unknown record(s)? + // fprintf(fo, "%s\n", buf); // eliminate them + } + } else { + if (buf[0] == '[') { + char * br = strchr(buf, ']'); + if (inSection) { // found next section while in good section + // Append new record to desired section + if (value != NULL) { + fprintf(fo, "%s=%s\r\n", key, value); + printf("write: %s=%s\r\n", key, value); + fileChanged = true; + } + found = true; + } + inSection = false; + // write old record + fprintf(fo, "%s\r\n", buf); + printf("write: %s\r\n", buf); + if (br) { + *br = '\0'; + if (strcmp(buf+1, section) == 0) + inSection = true; + } + } else { + // copy unaltered records across + if (buf[0]) { + fprintf(fo, "%s\r\n", buf); + printf("write: %s\r\n", buf); + } + } + } + } + printf("close %s\r\n", iniFile); + fclose(fi); + } + if (!found) { + // No old file, just create it now + if (value != NULL) { + if (!inSection) { + fprintf(fo, "[%s]\r\n", section); + printf("write: [%s]\r\n", section); + } + fprintf(fo, "%s=%s\r\n", key, value); + printf("write: %s=%s\r\n", key, value); + fileChanged = true; + } + found = true; + } + printf("close %s\r\n", newFile); + fclose(fo); + } + if (fileChanged) { + printf("remove bak, rename ini to bak, rename new to ini\r\n"); + remove(bakFile); // remove an old .bak + Rename(iniFile, bakFile); // move the existing .ini to .bak + Rename(newFile, iniFile); // move the new .new to .ini + wait(1); + } + free(newFile); + free(bakFile); + return found; +} + + +//*********************************************************** +// Private version that also works with local file system +// Returns -1 = error; 0 = success +//*********************************************************** +int INI::Rename(const char *oldfname, const char *newfname) +{ + int retval = 0; + int ch; + + FILE *fpold = fopen(oldfname, "r"); // src file + FILE *fpnew = fopen(newfname, "w"); // dest file + + while (1) { // Copy src to dest + ch = fgetc(fpold); // until src EOF read. + if (ch == EOF) break; + fputc(ch, fpnew); + } + + fclose(fpnew); + fclose(fpold); + + fpnew = fopen(newfname, "r"); // Reopen dest to insure + if(fpnew == NULL) { // that it was created. + retval = (-1); // Return Error. + } else { + fclose(fpnew); + remove(oldfname); // Remove original file. + retval = (0); // Return Success. + } + return (retval); +} + +//*********************************************************** +// Private version that also works with local file system +// Returns -1 = error; 0 = success +//*********************************************************** +int INI::Copy(const char *src, const char *dst) +{ + int retval = 0; + int ch; + + FILE *fpsrc = fopen(src, "r"); // src file + FILE *fpdst = fopen(dst, "w"); // dest file + + while (1) { // Copy src to dest + ch = fgetc(fpsrc); // until src EOF read. + if (ch == EOF) break; + fputc(ch, fpdst); + } + fclose(fpsrc); + fclose(fpdst); + + fpdst = fopen(dst, "r"); // Reopen dest to insure + if(fpdst == NULL) { // that it was created. + retval = (-1); // Return error. + } else { + fclose(fpdst); + retval = (0); // Return success. + } + return (retval); +} + + +#if 0 +// Test code for basic regression testing +// +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#include "INI.h" + +#define TESTFILE "test.ini" + +int main(int argc, char * argv[]) +{ + FILE * fp; + char buffer[100]; + INI ini(TESTFILE); + + // Start testing + _unlink(TESTFILE); + assert(ini.ReadString("Section 1", "Name 1", buffer, sizeof(buffer)) == false); + + fp = fopen(TESTFILE, "wt"); + assert(fp); + fprintf(fp, "[Section 1]\n"); + fprintf(fp, "Name 1=Value 1\n"); + fprintf(fp, "Name 2=Value 2\n"); + fprintf(fp, "\n"); + fprintf(fp, "[Section 2]\n"); + fprintf(fp, "Name 1=Value 2\n"); + fprintf(fp, "Name 2=Value 2\n"); + fprintf(fp, "Name 3=Value 3\n"); + fprintf(fp, "\n"); + fclose(fp); + + assert(ini.ReadString("Section 2", "Name 2", buffer, sizeof(buffer)) == true); + assert(strcmp("Value 2", buffer) == 0); + + assert(ini.ReadString("Section 3", "Name", buffer, sizeof(buffer)) == false); + assert(ini.ReadString("Section 1", "Name 3", buffer, sizeof(buffer)) == false); + + assert(ini.WriteString("Section 1", "Name 4", "Value 4") == true); + assert(ini.ReadString("Section 1", "Name 2", buffer, sizeof(buffer)) == true); + assert(ini.ReadString("Section 1", "Name 3", buffer, sizeof(buffer)) == false); + assert(ini.ReadString("Section 1", "Name 4", buffer, sizeof(buffer)) == true); + assert(strcmp("Value 4", buffer) == 0); + + assert(ini.WriteString("Section 1", "Name 4", NULL) == true); + assert(ini.ReadString("Section 1", "Name 4", buffer, sizeof(buffer)) == false); + + return 0; +} +#endif