A simple .ini file interface.
Dependents: Smart-WiFly-WebServer SignalGenerator WattEye X10Svr
Revision 18:282ed56d983b, committed 2016-12-28
- Comitter:
- WiredHome
- Date:
- Wed Dec 28 00:21:01 2016 +0000
- Parent:
- 15:3fc2b87a234d
- Parent:
- 17:01c0ee144433
- Child:
- 19:8f394a5f3758
- Commit message:
- Revised the ReadString and WriteString to return a failure code, rather than a boolean. This made the APIs incompatible, but this can be selected at construction or when setting the file, in order to be backward compatible.
Changed in this revision
| IniManager.cpp | Show annotated file Show diff for this revision Revisions of this file |
| IniManager.h | Show annotated file Show diff for this revision Revisions of this file |
--- a/IniManager.cpp Sun Dec 11 14:05:17 2016 +0000
+++ b/IniManager.cpp Wed Dec 28 00:21:01 2016 +0000
@@ -31,10 +31,24 @@
#define INFO(x, ...)
#endif
-INI::INI(const char * file)
+// 2 versions, to translate new return values to old format
+// return RetXLate[new value][version]
+INI::INI_Return RetXLate[INI::INI_INTERNAL_ERROR+1][2] = {
+// Ver1, Ver2 return values.
+ INI::INI_V1_SUCCESS, INI::INI_SUCCESS, /// Success - operation succeeded
+ INI::INI_V1_FAIL, INI::INI_NO_FILE_SPEC, /// Fail - no file was specified
+ INI::INI_V1_FAIL, INI::INI_FILE_NOT_FOUND, /// Fail - ini file not found, or failed to open
+ INI::INI_V1_FAIL, INI::INI_SECTION_NOT_FOUND, /// Fail - section not found
+ INI::INI_V1_FAIL, INI::INI_KEY_NOT_FOUND, /// Fail - key not found
+ INI::INI_V1_FAIL, INI::INI_BUF_TOO_SMALL, /// Fail - buffer to small for value
+ INI::INI_V1_FAIL, INI::INI_INTERNAL_ERROR /// Fail - internal error - can't alloc buffers
+};
+
+INI::INI(const char * file, int Version)
: iniFile(0)
{
SetFile(file);
+ version = (Version == 2) ? 1 : 0; // Version 1 or 2 is return value index 0 or 1
}
@@ -161,9 +175,10 @@
}
-bool INI::SetFile(const char * file)
+bool INI::SetFile(const char * file, int Version)
{
- INFO("SetFile(%s)", file);
+ INFO("SetFile(%s,%d)", file, Version);
+ version = (Version == 2) ? 1 : 0; // Version 1 or 2 is return value index 0 or 1
if (file) {
if (iniFile)
swFree(iniFile);
@@ -181,18 +196,22 @@
return false;
}
-bool INI::ReadString(const char * section, const char * key, char * buffer, size_t bufferSize, const char * defaultString)
+INI::INI_Return INI::ReadString(const char * section, const char * key, char * buffer, size_t bufferSize, const char * defaultString)
{
+ INI_Return retVal;
bool found = false;
+
if (!iniFile)
- return found;
+ return RetXLate[INI_NO_FILE_SPEC][version];
CleanUp();
INFO("ReadString from %s", iniFile);
FILE * fp = fopen(iniFile,"rt");
- if (fp) {
+ if (!fp) {
+ return RetXLate[INI_FILE_NOT_FOUND][version];
+ } else {
char buf[INTERNAL_BUF_SIZE];
bool inSection = (section == NULL) ? true : false;
-
+ retVal = RetXLate[INI_SECTION_NOT_FOUND][version]; // assume we won't find the section, until we do.
while(fgets(buf, sizeof(buf), fp)) {
int x = strlen(buf) - 1; // remove trailing \r\n combinations
while (x >= 0 && buf[x] < ' ')
@@ -205,21 +224,32 @@
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;
+ if (strcmp(buf,key) == 0) { // Found the key of interest
+ if (strlen(eq) < bufferSize) {
+ strcpy(buffer, eq);
+ memset(buf, 0, INTERNAL_BUF_SIZE); // secure the memory space
+ found = true;
+ retVal = RetXLate[INI_SUCCESS][version];
+ } else {
+ retVal = RetXLate[INI_BUF_TOO_SMALL][version];
+ }
break;
}
}
} else {
if (buf[0] == '[') {
char * br = strchr(buf, ']');
- inSection = false;
- if (br) {
- *br = '\0';
- if (strcmp(buf+1, section) == 0)
- inSection = true;
+ if (inSection) { // we were in the section of interest and just hit the next section...
+ break;
+ } else {
+ inSection = false;
+ if (br) {
+ *br = '\0';
+ if (strcmp(buf+1, section) == 0) {
+ inSection = true;
+ retVal = RetXLate[INI_KEY_NOT_FOUND][version]; // assume we won't find the key, until we do
+ }
+ }
}
}
}
@@ -227,14 +257,17 @@
fclose(fp);
}
if (!found && defaultString != NULL && *defaultString) {
- strncpy(buffer, defaultString, bufferSize);
- buffer[bufferSize-1] = '\0';
- INFO(" sub %s.", buffer);
- found = true;
+ if (strlen(defaultString) < bufferSize) {
+ strcpy(buffer, defaultString);
+ retVal = RetXLate[INI_SUCCESS][version];
+ } else {
+ retVal = RetXLate[INI_BUF_TOO_SMALL][version];
+ }
}
- return found;
+ return retVal;
}
+
long int INI::ReadLongInt(const char * section, const char * key, long int defaultValue)
{
char localBuf[16];
@@ -282,28 +315,42 @@
// 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, const char * value)
+INI::INI_Return INI::WriteString(const char * section, const char * key, const char * value, int len)
{
bool found = false;
bool fileChanged = false;
-
+ INI_Return retVal;
+
+ if (len == -1)
+ len = strlen(value);
INFO("WriteString(%s,%s,%s)", section, key, value);
- if (!iniFile || (value != NULL && strlen(value) > INTERNAL_BUF_SIZE))
- return found;
+ if (!iniFile)
+ return RetXLate[INI_NO_FILE_SPEC][version];
+
+ if (strlen(value) > INTERNAL_BUF_SIZE)
+ return RetXLate[INI_INTERNAL_ERROR][version];
char * newFile = (char *)swMalloc(strlen(iniFile)+1);
+ if (!newFile)
+ return RetXLate[INI_INTERNAL_ERROR][version]; // no memory
char * bakFile = (char *)swMalloc(strlen(iniFile)+1);
- if (!newFile)
- return found; // no memory
if (!bakFile) {
swFree(newFile);
- return found;
+ return RetXLate[INI_INTERNAL_ERROR][version];
}
+ char * valBuf = (char *)swMalloc(len+1);
+ if (!valBuf) {
+ swFree(bakFile);
+ swFree(newFile);
+ return RetXLate[INI_INTERNAL_ERROR][version];
+ }
+
strcpy(bakFile, iniFile);
strcpy(newFile, iniFile);
strcpy(bakFile + strlen(bakFile) - 4, ".bak");
strcpy(newFile + strlen(newFile) - 4, ".new");
-
+ strncpy(valBuf, value, len);
+ valBuf[len] = '\0';
CleanUp();
INFO(" Opening [%s] and [%s]", iniFile, newFile);
@@ -328,20 +375,22 @@
if (eq) {
*eq++ = '\0';
if (strcmp(buf,key) == 0) {
- if (value != NULL && strcmp(eq, value) != 0) {
+ // delete, or replace the old record
+ if (valBuf != NULL && strcmp(eq, valBuf) != 0) {
// replace the old record
- if (value != NULL) {
- fprintf(fo, "%s=%s\n", key, value);
- printf("write: %s=%s\r\n", key, value);
- INFO(" write: %s=%s", key, value);
+ if (valBuf != NULL) {
+ fprintf(fo, "%s=%s\r\n", key, valBuf);
+ printf("write: %s=%s\r\n", key, valBuf);
+ INFO(" write: %s=%s", key, valBuf);
}
}
+ retVal = RetXLate[INI_SUCCESS][version];
fileChanged = true;
inSection = false;
found = true;
} else {
// write old record
- fprintf(fo, "%s=%s\n", buf, eq);
+ fprintf(fo, "%s=%s\r\n", buf, eq);
INFO(" write: %s=%s", buf, eq);
}
} else {
@@ -353,12 +402,13 @@
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", key, value);
+ if (valBuf != NULL) {
+ fprintf(fo, "%s=%s\r\n", key, valBuf);
+ INFO(" write: %s=%s", key, valBuf);
fileChanged = true;
}
found = true;
+ retVal = RetXLate[INI_SUCCESS][version];
}
inSection = false;
// write old record
@@ -385,21 +435,23 @@
}
if (!found) {
// No old file, just create it now
- if (value != NULL) {
+ if (valBuf != NULL) {
if (!inSection) {
- fprintf(fo, "[%s]\r\n", section);
+ fprintf(fo, "\r\n[%s]\r\n", section);
INFO(" write: [%s]", section);
}
- fprintf(fo, "%s=%s\r\n", key, value);
- INFO(" write: %s=%s", key, value);
+ fprintf(fo, "%s=%s\r\n", key, valBuf);
+ INFO(" write: %s=%s", key, valBuf);
fileChanged = true;
}
found = true;
+ retVal = RetXLate[INI_SUCCESS][version];
}
INFO(" close %s", newFile);
fclose(fo);
} else {
ERR("*** Failed to open %s", newFile);
+ retVal = RetXLate[INI_FILE_NOT_FOUND][version];
}
if (fileChanged) {
INFO(" File changed: remove bak, rename ini to bak, rename new to ini");
@@ -416,9 +468,10 @@
#endif
INFO(" d");
}
+ swFree(valBuf);
swFree(newFile);
swFree(bakFile);
- return found;
+ return retVal;
}
@@ -480,6 +533,27 @@
}
+const char * INI::GetReturnMessage(INI_Return retVal) {
+ if (version == 0) {
+ switch (retVal) {
+ default:
+ case INI_V1_FAIL: return "INI Fail";
+ case INI_V1_SUCCESS: return "INI Success";
+ }
+ } else {
+ switch (retVal) {
+ case INI_SUCCESS: return "INI Success - operation succeeded";
+ case INI_NO_FILE_SPEC: return "INI Fail - no file was specified";
+ case INI_FILE_NOT_FOUND: return "INI Fail - ini file not found, or failed to open";
+ case INI_SECTION_NOT_FOUND: return "INI Fail - section not found";
+ case INI_KEY_NOT_FOUND: return "INI Fail - key not found";
+ case INI_BUF_TOO_SMALL: return "INI Fail - buffer to small for value";
+ case INI_INTERNAL_ERROR: return "INI Fail - internal error - can't malloc";
+ default: return "INI Fail - Code Unknown";
+ }
+ }
+}
+
#if 0
// Test code for basic regression testing
//
@@ -534,3 +608,4 @@
#endif
+
--- a/IniManager.h Sun Dec 11 14:05:17 2016 +0000
+++ b/IniManager.h Wed Dec 28 00:21:01 2016 +0000
@@ -4,13 +4,16 @@
#define INTERNAL_BUF_SIZE 250
-/** A simple ini file manager.
+
+/** A simple INI file manager - Version 2.
*
* This is a simple ini file manager intended for low duty cycle usage.
*
* It follows an old "Windows" style of ini file format with section, key, and value.
-* This version only operates on strings at this time.
*
+* @note An API change offers DIFFERENT AND INCOMPATIBLE return values from the
+* WriteString and the ReadString APIs.
+*
* As a "simple" ini file manager, this version does not cache anything internally.
* This comes at the "cost" that each write transaction will read and replace the
* ini file. Read transactions will open and scan the file.
@@ -29,6 +32,30 @@
class INI
{
public:
+
+ /** Return values
+ *
+ * Functions may return a status code as follows. Where the API supports
+ * a default, and on a Fail code, that value will be returned, if it
+ * fits in the available buffer.
+ *
+ * @note Version 1 returned only a success or failure value from the ReadString
+ * and the WriteString APIs. Version 2 returns incompatible and different
+ * values. The selection of version 1 vs. version 2 is made in either
+ * the constructor, or in the SetFile API.
+ */
+ typedef enum {
+ INI_V1_FAIL = 0, ///< Version 1 return value - Fail
+ INI_V1_SUCCESS = 1, ///< Version 1 return value - Success
+ INI_SUCCESS = 0, ///< Success - operation succeeded
+ INI_NO_FILE_SPEC, ///< Fail - no file was specified
+ INI_FILE_NOT_FOUND, ///< Fail - ini file not found, or failed to open
+ INI_SECTION_NOT_FOUND, ///< Fail - section not found
+ INI_KEY_NOT_FOUND, ///< Fail - key not found
+ INI_BUF_TOO_SMALL, ///< Fail - buffer to small for value
+ INI_INTERNAL_ERROR ///< Fail - internal error - can't alloc buffers
+ } INI_Return;
+
/** Constructor for an INI file interface.
*
* Constructor for an INI file interface.
@@ -36,8 +63,11 @@
* @param[in] file is the filename to manage. Memory is allocated to hold
* a private copy of the filename. Be sure that this parameter
* has the right path prefix based on what file system you have.
+ * @param[in] Version is an optional parameter that defines whether
+ * the return value of the ReadString and WriteString APIs
+ * are version 1 or version 2 compatible. The default is version 1.
*/
- INI(const char * file = NULL);
+ INI(const char * file = NULL, int Version = 1);
/** destructor for the ini manager.
*
@@ -63,9 +93,12 @@
* API can be used.
*
* @param[in] file is the filename to manage.
+ * @param[in] Version is an optional parameter that defines whether
+ * the return value of the ReadString and WriteString APIs
+ * are version 1 or version 2 compatible. The default is version 1.
* @returns true if success, false if memory could not be allocated.
*/
- bool SetFile(const char * file);
+ bool SetFile(const char * file, int Version = 1);
/** get the filename in use
*
@@ -83,11 +116,17 @@
* @param[out] buffer is the caller provided buffer for this method to put the string into.
* @param[in] bufferSize is the caller provided declaration of the available space.
* @param[in] defaultString is an optional parameter that sets the buffer if the section/key is not found.
+ * if defaultString is NULL (or omitted), and if the item cannot be found,
+ * it will return INI_KEY_NOT_FOUND.
*
- * @return true if the section, key, and value are found AND the value will fit in the buffer
- * in which case it is written into the buffer; false otherwise.
+ * @return INI_SUCCESS if the file, section, key, and value are found, and it fits into the specified buffer.
+ * @return INI_NO_FILE_SPEC if the ini file was never set.
+ * @return INI_FILE_NOT_FOUND if the ini file was specified, but cannot be found as specified.
+ * @return INI_SECTION_NOT_FOUND if the section was not found.
+ * @return INI_KEY_NOT_FOUND if the key was not found.
+ * @return INI_BUF_TOO_SMALL if everything was found, but it could not fit into the specified buffer.
*/
- bool ReadString(const char * section, const char * key, char * buffer, size_t bufferSize, const char * defaultString = NULL);
+ INI_Return ReadString(const char * section, const char * key, char * buffer, size_t bufferSize, const char * defaultString = NULL);
/** Read a long integer from the ini file - if it exists.
*
@@ -98,7 +137,7 @@
* @param[in] key is the name of the key to search.
* @param[in] defaultValue is the default value to return if the entry is not found.
*
- * @return the value read, or the defaultVaule.
+ * @return the value read, or the defaultVaule; no failure code is returned.
*/
long int ReadLongInt(const char * section, const char * key, long int defaultValue);
@@ -110,10 +149,17 @@
* @param[in] key is the name of the key to search.
* @param[in] buffer is the caller provided buffer containing the string to write. If
* buffer is NULL, then any existing entry is removed.
+ * @param[in] len is the number of characters to write, if specified. If not specified,
+ * the length of the buffer defines the length to write.
*
- * @return true if the write was successful; false otherwise.
+ * @return INI_SUCCESS if the file, section, key, and value are found, and it fits into the specified buffer.
+ * @return INI_NO_FILE_SPEC if the ini file was never set.
+ * @return INI_FILE_NOT_FOUND if the ini file was specified, but cannot be found as specified.
+ * @return INI_SECTION_NOT_FOUND if the section was not found.
+ * @return INI_KEY_NOT_FOUND if the key was not found.
+ * @return INI_BUF_TOO_SMALL if everything was found, but it could not fit into the specified buffer.
*/
- bool WriteString(const char * section, const char * key, const char * buffer);
+ INI_Return WriteString(const char * section, const char * key, const char * buffer, int len = -1);
/** Get Section, or Next Section name
@@ -143,9 +189,21 @@
bool GetNextKey(const char * Section, const char * after, char * buffer, size_t bufferSize);
+ /** Get the text message for an error return value from ReadString and WriteString.
+ *
+ * @param[in] retVal is the return value from either the ReadString or the WriteString
+ * APIs.
+ * @returns a pointer to a string which describes the return value.
+ */
+ const char * GetReturnMessage(INI_Return retVal);
+
private:
char * iniFile;
+ /** Version of the return values
+ */
+ int version;
+
/** Cleanup temporary files.
*
* This will attempt to clean up any temporary files. This can happen