A simple .ini file interface.
Dependents: Smart-WiFly-WebServer SignalGenerator WattEye X10Svr
IniManager.cpp
- Committer:
- WiredHome
- Date:
- 2014-03-16
- Revision:
- 4:70042853d43b
- Parent:
- 3:64fcaf06b012
- Child:
- 5:bfeb0882bd82
File content as of revision 4:70042853d43b:
// Simple INI file manager. // #ifdef WIN32 #include "string.h" #include "stdlib.h" #include "stdio.h" #else #include "mbed.h" #endif #include "IniManager.h" //#define DEBUG //Debug is disabled by default #if (defined(DEBUG) && !defined(TARGET_LPC11U24)) #define DBG(x, ...) std::printf("[DBG INI%4d] "x"\r\n", __LINE__, ##__VA_ARGS__); #define WARN(x, ...) std::printf("[WRN INI%4d] "x"\r\n", __LINE__, ##__VA_ARGS__); #define ERR(x, ...) std::printf("[ERR INI%4d] "x"\r\n", __LINE__, ##__VA_ARGS__); #define INFO(x, ...) std::printf("[INF INI%4d] "x"\r\n", __LINE__, ##__VA_ARGS__); #else #define DBG(x, ...) #define WARN(x, ...) #define ERR(x, ...) #define INFO(x, ...) #endif INI::INI(const char * file) : iniFile(0) { if (file) { iniFile = (char *)malloc(strlen(file)+1); if (iniFile) strcpy(iniFile, file); } } 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(); INFO("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'; INFO("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'; INFO("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) { WARN("*** 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) { int i; i = i; // suppress warning about i not used when !DEBUG // helps recover if the system crashed before it could swap in the new file INFO("*** repairing\r\n"); fclose(repair); i = remove(bakFile); // remove an old .bak INFO("remove(%s) returned %d\r\n", bakFile, i); i = Rename(iniFile, bakFile); // move the existing .ini to .bak INFO("rename(%s,%s) returned %d\r\n", iniFile, bakFile, i); i = Rename(newFile, iniFile); // move the new .new to .ini INFO("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(); INFO("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); INFO("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); INFO("write: %s=%s\r\n", key, value); fileChanged = true; } found = true; } inSection = false; // write old record fprintf(fo, "%s\r\n", buf); INFO("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); INFO("write: %s\r\n", buf); } } } } INFO("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); INFO("write: [%s]\r\n", section); } fprintf(fo, "%s=%s\r\n", key, value); INFO("write: %s=%s\r\n", key, value); fileChanged = true; } found = true; } INFO("close %s\r\n", newFile); fclose(fo); } if (fileChanged) { INFO("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 #ifdef RTOS_H Thread::wait(1000); #else wait(1); #endif } 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