A simple .ini file interface.

Dependents:   Smart-WiFly-WebServer SignalGenerator WattEye X10Svr

Files at this revision

API Documentation at this revision

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