A simple .ini file interface.
Dependents: Smart-WiFly-WebServer SignalGenerator WattEye X10Svr
IniManager.cpp
- Committer:
- WiredHome
- Date:
- 2013-09-01
- Revision:
- 1:1e2ee9bbee40
- Parent:
- 0:ae5bf432c249
- Child:
- 2:c63a794c1fee
File content as of revision 1:1e2ee9bbee40:
// 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) {
// helps recover if the system crashed before it could swap in the new file
INFO("*** repairing\r\n");
fclose(repair);
int i;
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
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