You've already forked pgbackrest
							
							
				mirror of
				https://github.com/pgbackrest/pgbackrest.git
				synced 2025-10-30 23:37:45 +02:00 
			
		
		
		
	This header does not contain a type but is used to define types so this seems like a better location.
		
			
				
	
	
		
			433 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			433 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***********************************************************************************************************************************
 | |
| Ini Handler
 | |
| ***********************************************************************************************************************************/
 | |
| #include "build.auto.h"
 | |
| 
 | |
| #include <stdarg.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| 
 | |
| #include "common/debug.h"
 | |
| #include "common/memContext.h"
 | |
| #include "common/log.h"
 | |
| #include "common/ini.h"
 | |
| #include "common/type/keyValue.h"
 | |
| #include "common/type/object.h"
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Object type
 | |
| ***********************************************************************************************************************************/
 | |
| struct Ini
 | |
| {
 | |
|     MemContext *memContext;                                         // Context that contains the ini
 | |
|     KeyValue *store;                                                // Key value store that contains the ini data
 | |
| };
 | |
| 
 | |
| OBJECT_DEFINE_MOVE(INI);
 | |
| OBJECT_DEFINE_FREE(INI);
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Create a new Ini object
 | |
| ***********************************************************************************************************************************/
 | |
| Ini *
 | |
| iniNew(void)
 | |
| {
 | |
|     FUNCTION_TEST_VOID();
 | |
| 
 | |
|     Ini *this = NULL;
 | |
| 
 | |
|     MEM_CONTEXT_NEW_BEGIN("Ini")
 | |
|     {
 | |
|         this = memNew(sizeof(Ini));
 | |
| 
 | |
|         *this = (Ini)
 | |
|         {
 | |
|             .memContext = MEM_CONTEXT_NEW(),
 | |
|             .store = kvNew(),
 | |
|         };
 | |
|     }
 | |
|     MEM_CONTEXT_NEW_END();
 | |
| 
 | |
|     FUNCTION_TEST_RETURN(this);
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Internal function to get an ini value
 | |
| ***********************************************************************************************************************************/
 | |
| static const Variant *
 | |
| iniGetInternal(const Ini *this, const String *section, const String *key, bool required)
 | |
| {
 | |
|     FUNCTION_TEST_BEGIN();
 | |
|         FUNCTION_TEST_PARAM(INI, this);
 | |
|         FUNCTION_TEST_PARAM(STRING, section);
 | |
|         FUNCTION_TEST_PARAM(STRING, key);
 | |
|         FUNCTION_TEST_PARAM(BOOL, required);
 | |
|     FUNCTION_TEST_END();
 | |
| 
 | |
|     ASSERT(this != NULL);
 | |
|     ASSERT(section != NULL);
 | |
|     ASSERT(key != NULL);
 | |
| 
 | |
|     const Variant *result = NULL;
 | |
| 
 | |
|     // Get the section
 | |
|     KeyValue *sectionKv = varKv(kvGet(this->store, VARSTR(section)));
 | |
| 
 | |
|     // Section must exist to get the value
 | |
|     if (sectionKv != NULL)
 | |
|         result = kvGet(sectionKv, VARSTR(key));
 | |
| 
 | |
|     // If value is null and required then error
 | |
|     if (result == NULL && required)
 | |
|         THROW_FMT(FormatError, "section '%s', key '%s' does not exist", strPtr(section), strPtr(key));
 | |
| 
 | |
|     FUNCTION_TEST_RETURN(result);
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Get an ini value -- error if it does not exist
 | |
| ***********************************************************************************************************************************/
 | |
| const String *
 | |
| iniGet(const Ini *this, const String *section, const String *key)
 | |
| {
 | |
|     FUNCTION_TEST_BEGIN();
 | |
|         FUNCTION_TEST_PARAM(INI, this);
 | |
|         FUNCTION_TEST_PARAM(STRING, section);
 | |
|         FUNCTION_TEST_PARAM(STRING, key);
 | |
|     FUNCTION_TEST_END();
 | |
| 
 | |
|     ASSERT(this != NULL);
 | |
|     ASSERT(section != NULL);
 | |
|     ASSERT(key != NULL);
 | |
| 
 | |
|     // Get the value
 | |
|     const Variant *result = iniGetInternal(this, section, key, true);
 | |
| 
 | |
|     FUNCTION_TEST_RETURN(varStr(result));
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Get an ini value -- if it does not exist then return specified default
 | |
| ***********************************************************************************************************************************/
 | |
| const String *
 | |
| iniGetDefault(const Ini *this, const String *section, const String *key, const String *defaultValue)
 | |
| {
 | |
|     FUNCTION_TEST_BEGIN();
 | |
|         FUNCTION_TEST_PARAM(INI, this);
 | |
|         FUNCTION_TEST_PARAM(STRING, section);
 | |
|         FUNCTION_TEST_PARAM(STRING, key);
 | |
|         FUNCTION_TEST_PARAM(STRING, defaultValue);
 | |
|     FUNCTION_TEST_END();
 | |
| 
 | |
|     ASSERT(this != NULL);
 | |
|     ASSERT(section != NULL);
 | |
|     ASSERT(key != NULL);
 | |
| 
 | |
|     // Get the value
 | |
|     const Variant *result = iniGetInternal(this, section, key, false);
 | |
| 
 | |
|     FUNCTION_TEST_RETURN(result == NULL ? defaultValue : varStr(result));
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Internal function to get an ini value list
 | |
| ***********************************************************************************************************************************/
 | |
| StringList *
 | |
| iniGetList(const Ini *this, const String *section, const String *key)
 | |
| {
 | |
|     FUNCTION_TEST_BEGIN();
 | |
|         FUNCTION_TEST_PARAM(INI, this);
 | |
|         FUNCTION_TEST_PARAM(STRING, section);
 | |
|         FUNCTION_TEST_PARAM(STRING, key);
 | |
|     FUNCTION_TEST_END();
 | |
| 
 | |
|     ASSERT(this != NULL);
 | |
|     ASSERT(section != NULL);
 | |
|     ASSERT(key != NULL);
 | |
| 
 | |
|     // Get the value
 | |
|     const Variant *result = iniGetInternal(this, section, key, false);
 | |
| 
 | |
|     FUNCTION_TEST_RETURN(result == NULL ? NULL : strLstNewVarLst(varVarLst(result)));
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Internal function to get an ini value list
 | |
| ***********************************************************************************************************************************/
 | |
| bool
 | |
| iniSectionKeyIsList(const Ini *this, const String *section, const String *key)
 | |
| {
 | |
|     FUNCTION_TEST_BEGIN();
 | |
|         FUNCTION_TEST_PARAM(INI, this);
 | |
|         FUNCTION_TEST_PARAM(STRING, section);
 | |
|         FUNCTION_TEST_PARAM(STRING, key);
 | |
|     FUNCTION_TEST_END();
 | |
| 
 | |
|     ASSERT(this != NULL);
 | |
|     ASSERT(section != NULL);
 | |
|     ASSERT(key != NULL);
 | |
| 
 | |
|     // Get the value
 | |
|     const Variant *result = iniGetInternal(this, section, key, true);
 | |
| 
 | |
|     FUNCTION_TEST_RETURN(varType(result) == varTypeVariantList);
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Get a list of keys for a section
 | |
| ***********************************************************************************************************************************/
 | |
| StringList *
 | |
| iniSectionKeyList(const Ini *this, const String *section)
 | |
| {
 | |
|     FUNCTION_TEST_BEGIN();
 | |
|         FUNCTION_TEST_PARAM(INI, this);
 | |
|         FUNCTION_TEST_PARAM(STRING, section);
 | |
|     FUNCTION_TEST_END();
 | |
| 
 | |
|     ASSERT(this != NULL);
 | |
|     ASSERT(section != NULL);
 | |
| 
 | |
|     StringList *result = NULL;
 | |
| 
 | |
|     MEM_CONTEXT_TEMP_BEGIN()
 | |
|     {
 | |
|         // Get the section
 | |
|         KeyValue *sectionKv = varKv(kvGet(this->store, VARSTR(section)));
 | |
| 
 | |
|         // Return key list if the section exists
 | |
|         if (sectionKv != NULL)
 | |
|             result = strLstNewVarLst(kvKeyList(sectionKv));
 | |
|         // Otherwise return an empty list
 | |
|         else
 | |
|             result = strLstNew();
 | |
| 
 | |
|         strLstMove(result, memContextPrior());
 | |
|     }
 | |
|     MEM_CONTEXT_TEMP_END();
 | |
| 
 | |
|     FUNCTION_TEST_RETURN(result);
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Get a list of sections
 | |
| ***********************************************************************************************************************************/
 | |
| StringList *
 | |
| iniSectionList(const Ini *this)
 | |
| {
 | |
|     FUNCTION_TEST_BEGIN();
 | |
|         FUNCTION_TEST_PARAM(INI, this);
 | |
|     FUNCTION_TEST_END();
 | |
| 
 | |
|     ASSERT(this != NULL);
 | |
| 
 | |
|     StringList *result = NULL;
 | |
| 
 | |
|     MEM_CONTEXT_TEMP_BEGIN()
 | |
|     {
 | |
|         // Get the sections from the keyList
 | |
|         result = strLstNewVarLst(kvKeyList(this->store));
 | |
| 
 | |
|         strLstMove(result, memContextPrior());
 | |
|     }
 | |
|     MEM_CONTEXT_TEMP_END();
 | |
| 
 | |
|     FUNCTION_TEST_RETURN(result);
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Parse ini from a string
 | |
| ***********************************************************************************************************************************/
 | |
| void
 | |
| iniParse(Ini *this, const String *content)
 | |
| {
 | |
|     FUNCTION_TEST_BEGIN();
 | |
|         FUNCTION_TEST_PARAM(INI, this);
 | |
|         FUNCTION_TEST_PARAM(STRING, content);
 | |
|     FUNCTION_TEST_END();
 | |
| 
 | |
|     ASSERT(this != NULL);
 | |
| 
 | |
|     MEM_CONTEXT_BEGIN(this->memContext)
 | |
|     {
 | |
|         kvFree(this->store);
 | |
|         this->store = kvNew();
 | |
| 
 | |
|         if (content != NULL)
 | |
|         {
 | |
|             MEM_CONTEXT_TEMP_BEGIN()
 | |
|             {
 | |
|                 // Track the current section
 | |
|                 String *section = NULL;
 | |
| 
 | |
|                 // Split the content into lines and loop
 | |
|                 StringList *lines = strLstNewSplitZ(content, "\n");
 | |
| 
 | |
|                 for (unsigned int lineIdx = 0; lineIdx < strLstSize(lines); lineIdx++)
 | |
|                 {
 | |
|                     // Get next line
 | |
|                     const String *line = strTrim(strLstGet(lines, lineIdx));
 | |
|                     const char *linePtr = strPtr(line);
 | |
| 
 | |
|                     // Only interested in lines that are not blank or comments
 | |
|                     if (strSize(line) > 0 && linePtr[0] != '#')
 | |
|                     {
 | |
|                         // Looks like this line is a section
 | |
|                         if (linePtr[0] == '[')
 | |
|                         {
 | |
|                             // Make sure the section ends with ]
 | |
|                             if (linePtr[strSize(line) - 1] != ']')
 | |
|                                 THROW_FMT(FormatError, "ini section should end with ] at line %u: %s", lineIdx + 1, linePtr);
 | |
| 
 | |
|                             // Assign section
 | |
|                             section = strNewN(linePtr + 1, strSize(line) - 2);
 | |
|                         }
 | |
|                         // Else it should be a key/value
 | |
|                         else
 | |
|                         {
 | |
|                             if (section == NULL)
 | |
|                                 THROW_FMT(FormatError, "key/value found outside of section at line %u: %s", lineIdx + 1, linePtr);
 | |
| 
 | |
|                             // Find the =
 | |
|                             const char *lineEqual = strstr(linePtr, "=");
 | |
| 
 | |
|                             if (lineEqual == NULL)
 | |
|                                 THROW_FMT(FormatError, "missing '=' in key/value at line %u: %s", lineIdx + 1, linePtr);
 | |
| 
 | |
|                             // Extract the key
 | |
|                             String *key = strTrim(strNewN(linePtr, (size_t)(lineEqual - linePtr)));
 | |
| 
 | |
|                             if (strSize(key) == 0)
 | |
|                                 THROW_FMT(FormatError, "key is zero-length at line %u: %s", lineIdx++, linePtr);
 | |
| 
 | |
|                             // Store the section/key/value
 | |
|                             iniSet(this, section, key, strTrim(strNew(lineEqual + 1)));
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             MEM_CONTEXT_TEMP_END();
 | |
|         }
 | |
|     }
 | |
|     MEM_CONTEXT_END();
 | |
| 
 | |
|     FUNCTION_TEST_RETURN_VOID();
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Set an ini value
 | |
| ***********************************************************************************************************************************/
 | |
| void
 | |
| iniSet(Ini *this, const String *section, const String *key, const String *value)
 | |
| {
 | |
|     FUNCTION_TEST_BEGIN();
 | |
|         FUNCTION_TEST_PARAM(INI, this);
 | |
|         FUNCTION_TEST_PARAM(STRING, section);
 | |
|         FUNCTION_TEST_PARAM(STRING, key);
 | |
|         FUNCTION_TEST_PARAM(STRING, value);
 | |
|     FUNCTION_TEST_END();
 | |
| 
 | |
|     ASSERT(this != NULL);
 | |
|     ASSERT(section != NULL);
 | |
|     ASSERT(key != NULL);
 | |
|     ASSERT(value != NULL);
 | |
| 
 | |
|     MEM_CONTEXT_TEMP_BEGIN()
 | |
|     {
 | |
|         const Variant *sectionKey = VARSTR(section);
 | |
|         KeyValue *sectionKv = varKv(kvGet(this->store, sectionKey));
 | |
| 
 | |
|         if (sectionKv == NULL)
 | |
|             sectionKv = kvPutKv(this->store, sectionKey);
 | |
| 
 | |
|         kvAdd(sectionKv, VARSTR(key), VARSTR(value));
 | |
|     }
 | |
|     MEM_CONTEXT_TEMP_END();
 | |
| 
 | |
|     FUNCTION_TEST_RETURN_VOID();
 | |
| }
 | |
| 
 | |
| /***********************************************************************************************************************************
 | |
| Load an ini file and return data to a callback
 | |
| ***********************************************************************************************************************************/
 | |
| void
 | |
| iniLoad(
 | |
|     IoRead *read, void (*callbackFunction)(void *data, const String *section, const String *key, const String *value),
 | |
|     void *callbackData)
 | |
| {
 | |
|     FUNCTION_LOG_BEGIN(logLevelDebug);
 | |
|         FUNCTION_LOG_PARAM(IO_READ, read);
 | |
|         FUNCTION_LOG_PARAM(FUNCTIONP, callbackFunction);
 | |
|         FUNCTION_LOG_PARAM_P(VOID, callbackData);
 | |
|     FUNCTION_LOG_END();
 | |
| 
 | |
|     ASSERT(read != NULL);
 | |
| 
 | |
|     MEM_CONTEXT_TEMP_BEGIN()
 | |
|     {
 | |
|         // Track the current section
 | |
|         String *section = NULL;
 | |
| 
 | |
|         // Keep track of the line number for error reporting
 | |
|         unsigned int lineIdx = 0;
 | |
| 
 | |
|         MEM_CONTEXT_TEMP_RESET_BEGIN()
 | |
|         {
 | |
|             ioReadOpen(read);
 | |
| 
 | |
|             do
 | |
|             {
 | |
|                 const String *line = strTrim(ioReadLineParam(read, true));
 | |
|                 const char *linePtr = strPtr(line);
 | |
| 
 | |
|                 // Only interested in lines that are not blank or comments
 | |
|                 if (strSize(line) > 0 && linePtr[0] != '#')
 | |
|                 {
 | |
|                     // Looks like this line is a section
 | |
|                     if (linePtr[0] == '[')
 | |
|                     {
 | |
|                         // Make sure the section ends with ]
 | |
|                         if (linePtr[strSize(line) - 1] != ']')
 | |
|                             THROW_FMT(FormatError, "ini section should end with ] at line %u: %s", lineIdx + 1, linePtr);
 | |
| 
 | |
|                         // Assign section
 | |
|                         MEM_CONTEXT_PRIOR_BEGIN()
 | |
|                         {
 | |
|                             section = strNewN(linePtr + 1, strSize(line) - 2);
 | |
|                         }
 | |
|                         MEM_CONTEXT_PRIOR_END();
 | |
|                     }
 | |
|                     // Else it should be a key/value
 | |
|                     else
 | |
|                     {
 | |
|                         if (section == NULL)
 | |
|                             THROW_FMT(FormatError, "key/value found outside of section at line %u: %s", lineIdx + 1, linePtr);
 | |
| 
 | |
|                         // Find the =
 | |
|                         const char *lineEqual = strstr(linePtr, "=");
 | |
| 
 | |
|                         if (lineEqual == NULL)
 | |
|                             THROW_FMT(FormatError, "missing '=' in key/value at line %u: %s", lineIdx + 1, linePtr);
 | |
| 
 | |
|                         // Extract the key
 | |
|                         String *key = strTrim(strNewN(linePtr, (size_t)(lineEqual - linePtr)));
 | |
| 
 | |
|                         if (strSize(key) == 0)
 | |
|                             THROW_FMT(FormatError, "key is zero-length at line %u: %s", lineIdx++, linePtr);
 | |
| 
 | |
|                         // Callback with the section/key/value
 | |
|                         callbackFunction(callbackData, section, key, strTrim(strNew(lineEqual + 1)));
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 lineIdx++;
 | |
|                 MEM_CONTEXT_TEMP_RESET(1000);
 | |
|             }
 | |
|             while (!ioReadEof(read));
 | |
|         }
 | |
|         MEM_CONTEXT_TEMP_END();
 | |
|     }
 | |
|     MEM_CONTEXT_TEMP_END();
 | |
| 
 | |
|     FUNCTION_LOG_RETURN_VOID();
 | |
| }
 |