diff --git a/src/Makefile.in b/src/Makefile.in index 59142ce27..0a77f26d9 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -429,7 +429,7 @@ common/type/list.o: common/type/list.c build.auto.h common/assert.h common/debug common/type/mcv.o: common/type/mcv.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/stackTrace.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/mcv.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h $(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c common/type/mcv.c -o common/type/mcv.o -common/type/string.o: common/type/string.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/logLevel.h common/macro.h common/memContext.h common/stackTrace.h common/type/buffer.h common/type/convert.h common/type/string.h +common/type/string.o: common/type/string.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/logLevel.h common/macro.h common/memContext.h common/stackTrace.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h $(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c common/type/string.c -o common/type/string.o common/type/stringList.o: common/type/stringList.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/logLevel.h common/memContext.h common/stackTrace.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h diff --git a/src/common/type/string.c b/src/common/type/string.c index e56a08473..1e634ac53 100644 --- a/src/common/type/string.c +++ b/src/common/type/string.c @@ -13,6 +13,7 @@ String Handler #include "common/macro.h" #include "common/memContext.h" #include "common/type/string.h" +#include "common/type/stringList.h" /*********************************************************************************************************************************** Constant strings that are generally useful @@ -21,6 +22,7 @@ STRING_EXTERN(BRACKETL_STR, "["); STRING_EXTERN(BRACKETR_STR, "]"); STRING_EXTERN(CR_STR, "\r"); STRING_EXTERN(DOT_STR, "."); +STRING_EXTERN(DOTDOT_STR, ".."); STRING_EXTERN(EMPTY_STR, ""); STRING_EXTERN(EQ_STR, "="); STRING_EXTERN(FALSE_STR, "false"); @@ -594,6 +596,85 @@ strPath(const String *this) end - this->buffer <= 1 ? (size_t)(end - this->buffer) : (size_t)(end - this->buffer - 1))); } +/*********************************************************************************************************************************** +Combine with a base path to get an absolute path +***********************************************************************************************************************************/ +String * +strPathAbsolute(const String *this, const String *base) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(STRING, this); + FUNCTION_TEST_PARAM(STRING, base); + FUNCTION_TEST_END(); + + ASSERT(this != NULL); + + String *result = NULL; + + // Path is already absolute so just return it + if (strBeginsWith(this, FSLASH_STR)) + { + result = strDup(this); + } + // Else we'll need to construct the absolute path. You would hope we could use realpath() here but it is so broken in the + // Posix spec that is seems best avoided. + else + { + ASSERT(base != NULL); + + // Base must be absolute to start + if (!strBeginsWith(base, FSLASH_STR)) + THROW_FMT(AssertError, "base path '%s' is not absolute", strPtr(base)); + + MEM_CONTEXT_TEMP_BEGIN() + { + StringList *baseList = strLstNewSplit(base, FSLASH_STR); + StringList *pathList = strLstNewSplit(this, FSLASH_STR); + + while (strLstSize(pathList) > 0) + { + const String *pathPart = strLstGet(pathList, 0); + + if (strSize(pathPart) == 0) + THROW_FMT(AssertError, "'%s' is not a valid relative path", strPtr(this)); + + if (strEq(pathPart, DOTDOT_STR)) + { + const String *basePart = strLstGet(baseList, strLstSize(baseList) - 1); + + if (strSize(basePart) == 0) + { + THROW_FMT( + AssertError, "relative path '%s' goes back too far in base path '%s'", strPtr(this), strPtr(base)); + } + + strLstRemoveIdx(baseList, strLstSize(baseList) - 1); + } + else + strLstAdd(baseList, pathPart); + + strLstRemoveIdx(pathList, 0); + } + + memContextSwitch(MEM_CONTEXT_OLD()); + + if (strLstSize(baseList) == 1) + result = strDup(FSLASH_STR); + else + result = strLstJoin(baseList, "/"); + + memContextSwitch(MEM_CONTEXT_TEMP()); + } + MEM_CONTEXT_TEMP_END(); + } + + // There should not be any stray .. or // in the final result + if (strstr(strPtr(result), "/..") != NULL || strstr(strPtr(result), "//") != NULL) + THROW_FMT(AssertError, "result path '%s' is not absolute", strPtr(result)); + + FUNCTION_TEST_RETURN(result); +} + /*********************************************************************************************************************************** Return string ptr ***********************************************************************************************************************************/ diff --git a/src/common/type/string.h b/src/common/type/string.h index ba61f5fc0..d9ff1263c 100644 --- a/src/common/type/string.h +++ b/src/common/type/string.h @@ -54,6 +54,7 @@ String *strFirstLower(String *this); String *strUpper(String *this); String *strLower(String *this); String *strPath(const String *this); +String *strPathAbsolute(const String *this, const String *base); const char *strPtr(const String *this); String *strQuote(const String *this, const String *quote); String *strQuoteZ(const String *this, const char *quote); @@ -121,6 +122,7 @@ STRING_DECLARE(BRACKETL_STR); STRING_DECLARE(BRACKETR_STR); STRING_DECLARE(CR_STR); STRING_DECLARE(DOT_STR); +STRING_DECLARE(DOTDOT_STR); STRING_DECLARE(EMPTY_STR); STRING_DECLARE(EQ_STR); STRING_DECLARE(FALSE_STR); diff --git a/src/common/type/stringList.c b/src/common/type/stringList.c index b48a853b0..e7ae45264 100644 --- a/src/common/type/stringList.c +++ b/src/common/type/stringList.c @@ -628,6 +628,19 @@ strLstRemove(StringList *this, const String *item) FUNCTION_TEST_RETURN(lstRemove((List *)this, &item)); } +StringList * +strLstRemoveIdx(StringList *this, unsigned int listIdx) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(STRING_LIST, this); + FUNCTION_TEST_PARAM(UINT, listIdx); + FUNCTION_TEST_END(); + + ASSERT(this != NULL); + + FUNCTION_TEST_RETURN((StringList *)lstRemoveIdx((List *)this, listIdx)); +} + /*********************************************************************************************************************************** Wrapper for lstSize() ***********************************************************************************************************************************/ diff --git a/src/common/type/stringList.h b/src/common/type/stringList.h index c221db83b..bf8fc1eb6 100644 --- a/src/common/type/stringList.h +++ b/src/common/type/stringList.h @@ -43,6 +43,7 @@ StringList *strLstMergeAnti(const StringList *this, const StringList *anti); StringList *strLstMove(StringList *this, MemContext *parentNew); const char **strLstPtr(const StringList *this); bool strLstRemove(StringList *this, const String *item); +StringList *strLstRemoveIdx(StringList *this, unsigned int listIdx); unsigned int strLstSize(const StringList *this); StringList *strLstSort(StringList *this, SortOrder sortOrder); diff --git a/test/src/module/common/typeStringTest.c b/test/src/module/common/typeStringTest.c index c39b5fdbe..65a9df394 100644 --- a/test/src/module/common/typeStringTest.c +++ b/test/src/module/common/typeStringTest.c @@ -61,7 +61,7 @@ testRun(void) } // ***************************************************************************************************************************** - if (testBegin("strBase() and strPath()")) + if (testBegin("strBase(), strPath(), and strPathAbsolute()")) { TEST_RESULT_STR(strPtr(strBase(STRDEF(""))), "", "empty string"); TEST_RESULT_STR(strPtr(strBase(STRDEF("/"))), "", "/ only"); @@ -72,6 +72,17 @@ testRun(void) TEST_RESULT_STR(strPtr(strPath(STRDEF("/"))), "/", "/ only"); TEST_RESULT_STR(strPtr(strPath(STRDEF("/file"))), "/", "root path"); TEST_RESULT_STR(strPtr(strPath(STRDEF("/dir1/dir2/file"))), "/dir1/dir2", "subdirectory file"); + + TEST_ERROR(strPathAbsolute(STRDEF("/.."), NULL), AssertError, "result path '/..' is not absolute"); + TEST_ERROR(strPathAbsolute(STRDEF("//"), NULL), AssertError, "result path '//' is not absolute"); + TEST_ERROR(strPathAbsolute(STRDEF(".."), STRDEF("path1")), AssertError, "base path 'path1' is not absolute"); + TEST_ERROR( + strPathAbsolute(STRDEF(".."), STRDEF("/")), AssertError, "relative path '..' goes back too far in base path '/'"); + TEST_ERROR(strPathAbsolute(STRDEF("path1/"), STRDEF("/")), AssertError, "'path1/' is not a valid relative path"); + TEST_RESULT_STR_Z(strPathAbsolute(STRDEF("/"), NULL), "/", "path is already absolute"); + TEST_RESULT_STR_Z(strPathAbsolute(STRDEF(".."), STRDEF("/path1")), "/", "simple relative path"); + TEST_RESULT_STR_Z( + strPathAbsolute(STRDEF("../path2/../path3"), STRDEF("/base1/base2")), "/base1/path3", "complex relative path"); } // *****************************************************************************************************************************