A simple .ini file interface.

Dependents:   Smart-WiFly-WebServer SignalGenerator WattEye X10Svr

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