From 0881336b14f4577723bfe8f7bb44181b4d271fb3 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Fri, 5 Feb 2021 14:15:59 -0500 Subject: [PATCH] Fix #981, implement better filename parser routine Implmements a new function in FS to parse an input file name from a user. Initially applied to startup script processing. The new function produces fully-qualified output where the input may omit either a pathname or an extension. These items will be added from the specified defaults, if missing, and a complete fully-qualified filename will be output. If the input is already a fully qualified filename, then the output is the same as the input (basically a copy). This initially is used to provide better cross-platform startup script processing, where module suffix may differ across platforms. Only the "basename" of the filename needs to be specified in the startup script - everything else can come from defaults. --- cmake/sample_defs/cpu1_cfe_es_startup.scr | 13 +- cmake/target/inc/target_config.h | 1 + cmake/target/src/target_config.c | 5 + fsw/cfe-core/src/es/cfe_es_apps.c | 34 ++++- fsw/cfe-core/src/fs/cfe_fs_api.c | 162 ++++++++++++++++++++++ fsw/cfe-core/src/inc/cfe_fs.h | 30 ++++ fsw/cfe-core/unit-test/es_UT.c | 13 +- fsw/cfe-core/unit-test/fs_UT.c | 82 +++++++++++ fsw/cfe-core/unit-test/fs_UT.h | 18 +++ fsw/cfe-core/ut-stubs/ut_fs_stubs.c | 28 ++++ 10 files changed, 372 insertions(+), 14 deletions(-) diff --git a/cmake/sample_defs/cpu1_cfe_es_startup.scr b/cmake/sample_defs/cpu1_cfe_es_startup.scr index d03c0e77a..d0510376a 100644 --- a/cmake/sample_defs/cpu1_cfe_es_startup.scr +++ b/cmake/sample_defs/cpu1_cfe_es_startup.scr @@ -1,8 +1,8 @@ -CFE_LIB, /cf/sample_lib.so, SAMPLE_LIB_Init, SAMPLE_LIB, 0, 0, 0x0, 0; -CFE_APP, /cf/sample_app.so, SAMPLE_APP_Main, SAMPLE_APP, 50, 16384, 0x0, 0; -CFE_APP, /cf/ci_lab.so, CI_Lab_AppMain, CI_LAB_APP, 60, 16384, 0x0, 0; -CFE_APP, /cf/to_lab.so, TO_Lab_AppMain, TO_LAB_APP, 70, 16384, 0x0, 0; -CFE_APP, /cf/sch_lab.so, SCH_Lab_AppMain, SCH_LAB_APP, 80, 16384, 0x0, 0; +CFE_LIB, sample_lib, SAMPLE_LIB_Init, SAMPLE_LIB, 0, 0, 0x0, 0; +CFE_APP, sample_app, SAMPLE_APP_Main, SAMPLE_APP, 50, 16384, 0x0, 0; +CFE_APP, ci_lab, CI_Lab_AppMain, CI_LAB_APP, 60, 16384, 0x0, 0; +CFE_APP, to_lab, TO_Lab_AppMain, TO_LAB_APP, 70, 16384, 0x0, 0; +CFE_APP, sch_lab, SCH_Lab_AppMain, SCH_LAB_APP, 80, 16384, 0x0, 0; ! ! Startup script fields: ! 1. Object Type -- CFE_APP for an Application, or CFE_LIB for a library. @@ -27,4 +27,7 @@ CFE_APP, /cf/sch_lab.so, SCH_Lab_AppMain, SCH_LAB_APP, 80, 16384, 0x0, 0; ! vxWorks = .o ( ci.o ) ! RTEMS with S-record Loader = .s3r ( ci.s3r ) ! RTEMS with CEXP Loader = .o ( ci.o ) +! 3. The filename field (2) no longer requires a fully-qualified filename; the path and extension +! may be omitted. If omitted, the standard virtual path (/cf) and a platform-specific default +! extension will be used, which is derived from the build system. diff --git a/cmake/target/inc/target_config.h b/cmake/target/inc/target_config.h index c4af5307f..e69b817d4 100644 --- a/cmake/target/inc/target_config.h +++ b/cmake/target/inc/target_config.h @@ -144,6 +144,7 @@ typedef const struct uint16 Default_SpacecraftId; /**< Compile-time value for Spacecraft ID (mission-wide) */ const char *Default_ModuleExtension; /**< Default system extension for dynamic modules */ + const char *Default_ModulePath; /**< Default virtual path for dynamic modules */ const char *Default_CoreFilename; /**< Default file name for CFE core executable/library */ Target_CfeConfigData *CfeConfig; /**< CFE configuration sub-structure */ diff --git a/cmake/target/src/target_config.c b/cmake/target/src/target_config.c index bbf33104b..af8f57659 100644 --- a/cmake/target/src/target_config.c +++ b/cmake/target/src/target_config.c @@ -62,6 +62,10 @@ #define CFE_DEFAULT_CORE_FILENAME "" #endif +#ifndef CFE_DEFAULT_MODULE_PATH +#define CFE_DEFAULT_MODULE_PATH "/cf" +#endif + Target_CfeConfigData GLOBAL_CFE_CONFIGDATA = { /* @@ -147,6 +151,7 @@ Target_ConfigData GLOBAL_CONFIGDATA = .Default_CpuId = CFE_CPU_ID_VALUE, .Default_SpacecraftId = CFE_SPACECRAFT_ID_VALUE, .Default_ModuleExtension = CFE_DEFAULT_MODULE_EXTENSION, + .Default_ModulePath = CFE_DEFAULT_MODULE_PATH, .Default_CoreFilename = CFE_DEFAULT_CORE_FILENAME, .CfeConfig = &GLOBAL_CFE_CONFIGDATA, .PspConfig = &GLOBAL_PSP_CONFIGDATA, diff --git a/fsw/cfe-core/src/es/cfe_es_apps.c b/fsw/cfe-core/src/es/cfe_es_apps.c index 414ad3d5b..a544ebbba 100644 --- a/fsw/cfe-core/src/es/cfe_es_apps.c +++ b/fsw/cfe-core/src/es/cfe_es_apps.c @@ -49,11 +49,25 @@ #include /* memset() */ #include + +#include "target_config.h" + /* ** Defines */ #define ES_START_BUFF_SIZE 128 + +/* + * The default values to use in startup script file names. + * + * These are platform-specific, and the build system will + * put the defaults into then GLOBAL_CONFIGDATA global. + */ +#define CFE_ES_DEFAULT_SCRIPT_ENTRY_PATHNAME (GLOBAL_CONFIGDATA.Default_ModulePath) +#define CFE_ES_DEFAULT_SCRIPT_ENTRY_EXTENSION (GLOBAL_CONFIGDATA.Default_ModuleExtension) + + /* ** ** Global Variables @@ -271,7 +285,7 @@ int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens) CFE_ES_AppId_t AppId; CFE_ES_LibId_t LibId; } IdBuf; - int32 CreateStatus = CFE_ES_ERR_APP_CREATE; + int32 Status; CFE_ES_AppStartParams_t ParamBuf; /* @@ -280,7 +294,7 @@ int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens) if (NumTokens < 8) { CFE_ES_WriteToSysLog("ES Startup: Invalid ES Startup file entry: %u\n", (unsigned int)NumTokens); - return (CreateStatus); + return CFE_ES_BAD_ARGUMENT; } /* Get pointers to specific tokens that are simple strings used as-is */ @@ -292,7 +306,14 @@ int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens) * Both Libraries and Apps use File Name (1) and Symbol Name (2) fields so copy those now */ memset(&ParamBuf, 0, sizeof(ParamBuf)); - strncpy(ParamBuf.BasicInfo.FileName, TokenList[1], sizeof(ParamBuf.BasicInfo.FileName) - 1); + Status = CFE_FS_ParseInputFileName(ParamBuf.BasicInfo.FileName, TokenList[1], sizeof(ParamBuf.BasicInfo.FileName), + CFE_ES_DEFAULT_SCRIPT_ENTRY_PATHNAME, CFE_ES_DEFAULT_SCRIPT_ENTRY_EXTENSION); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Invalid ES Startup script file name: %s\n", TokenList[1]); + return Status; + } + strncpy(ParamBuf.BasicInfo.InitSymbolName, TokenList[2], sizeof(ParamBuf.BasicInfo.InitSymbolName) - 1); if (strcmp(EntryType, "CFE_APP") == 0) @@ -338,7 +359,7 @@ int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens) /* ** Now create the application */ - CreateStatus = CFE_ES_AppCreate(&IdBuf.AppId, ModuleName, &ParamBuf); + Status = CFE_ES_AppCreate(&IdBuf.AppId, ModuleName, &ParamBuf); } else if (strcmp(EntryType, "CFE_LIB") == 0) { @@ -347,14 +368,15 @@ int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens) /* ** Now load the library */ - CreateStatus = CFE_ES_LoadLibrary(&IdBuf.LibId, ModuleName, &ParamBuf.BasicInfo); + Status = CFE_ES_LoadLibrary(&IdBuf.LibId, ModuleName, &ParamBuf.BasicInfo); } else { CFE_ES_WriteToSysLog("ES Startup: Unexpected EntryType %s in startup file.\n", EntryType); + Status = CFE_ES_ERR_APP_CREATE; } - return (CreateStatus); + return (Status); } /* diff --git a/fsw/cfe-core/src/fs/cfe_fs_api.c b/fsw/cfe-core/src/fs/cfe_fs_api.c index 5b48387f9..79decea2b 100644 --- a/fsw/cfe-core/src/fs/cfe_fs_api.c +++ b/fsw/cfe-core/src/fs/cfe_fs_api.c @@ -252,6 +252,168 @@ void CFE_FS_ByteSwapUint32(uint32 *Uint32ToSwapPtr) OutPtr[3] = InPtr[0]; } /* End of CFE_FS_ByteSwapUint32() */ +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_FS_ParseInputFileName +** +** Purpose: This reads a file name from user input with extra logic to make more user friendly +** - absolute path is optional; assume default dir if missing +** - module extension is optional; append default for OS/platform if missing +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_FS_ParseInputFileName(char *OutputBuffer, const char *InputName, size_t OutputBufSize, const char *DefaultPath, const char *DefaultExtension) +{ + int32 Status; + const char *InputPtr; + const char *ComponentPtr; + size_t ComponentLen; + char ComponentTerm; + size_t OutLen; + + /* The filename consists of a pathname, filename, and extension component. */ + enum + { + PATHNAME_COMPONENT, + PATHNAME_SEPARATOR, + FILENAME_COMPONENT, + EXTENSION_SEPARATOR, + EXTENSION_COMPONENT, + END_COMPONENT + } Component; + + /* Sanity check buffer input */ + if (OutputBuffer == NULL || OutputBufSize == 0) + { + return CFE_FS_BAD_ARGUMENT; + } + + Status = CFE_SUCCESS; + OutLen = 0; + ComponentTerm = 0; + InputPtr = InputName; + + for (Component = 0; InputPtr != NULL && Component < END_COMPONENT; ++Component) + { + switch(Component) + { + case PATHNAME_COMPONENT: + /* path part ends with the last / char, which begins the filename */ + ComponentTerm = '/'; + ComponentPtr = strrchr(InputPtr, ComponentTerm); + if (ComponentPtr != NULL) + { + /* has path: use pathname from input, advance InputPtr to next part (filename) */ + ComponentLen = ComponentPtr - InputPtr; + ComponentPtr = InputPtr; + InputPtr += ComponentLen; + } + else if (DefaultPath != NULL) + { + /* no path: input is a filename - use default pathname, leave InputPtr alone */ + ComponentLen = strlen(DefaultPath); + ComponentPtr = DefaultPath; + } + else + { + /* use no pathname at all */ + ComponentLen = 0; + ComponentPtr = NULL; + } + break; + + case FILENAME_COMPONENT: + /* filename ends with a . char, which begins the extension */ + ComponentTerm = '.'; + ComponentPtr = strrchr(InputPtr, ComponentTerm); + if (ComponentPtr != NULL) + { + /* has ext: use pathname from input, advance InputPtr to next part (extension) */ + ComponentLen = ComponentPtr - InputPtr; + ComponentPtr = InputPtr; + InputPtr += ComponentLen; + } + else + { + /* no ext: use remainder of input here - then use default extension for next part */ + ComponentLen = strlen(InputPtr); + ComponentPtr = InputPtr; + InputPtr = DefaultExtension; + } + break; + + case PATHNAME_SEPARATOR: + case EXTENSION_SEPARATOR: + /* Remove duplicate terminators that may have been in the input */ + while (OutLen > 0 && OutputBuffer[OutLen-1] == ComponentTerm) + { + --OutLen; + } + + ComponentLen = 1; + ComponentPtr = &ComponentTerm; + + /* advance past any separators in input to get to the next content */ + while (*InputPtr == ComponentTerm) + { + ++InputPtr; + } + break; + + + case EXTENSION_COMPONENT: + /* Intentional fall through to default case */ + + default: + /* Just consume the rest of input - + * should already be pointing to correct data */ + ComponentTerm = 0; + ComponentLen = strlen(InputPtr); + ComponentPtr = InputPtr; + InputPtr = NULL; /* no more input */ + break; + } + + /* Confirm block plus terminator/separator will fit in remaining space */ + if ((OutLen + ComponentLen) >= OutputBufSize) + { + /* name is too long to fit in output buffer */ + Status = CFE_FS_FNAME_TOO_LONG; + break; + } + + if (ComponentLen > 0) + { + /* Append component */ + memcpy(&OutputBuffer[OutLen], ComponentPtr, ComponentLen); + OutLen += ComponentLen; + } + else if (Component == FILENAME_COMPONENT) + { + /* If the filename part is empty, that should be considered an error */ + Status = CFE_FS_INVALID_PATH; + break; + } + + } + + /* + * Always add a final terminating NUL char. + * + * Note that the loop above should never entirely fill + * buffer (length check includes extra char). + */ + OutputBuffer[OutLen] = 0; + + /* At least a filename component must have been found and a + * non-empty output must be generated (extension not necessary) */ + if (Status == CFE_SUCCESS && (Component <= FILENAME_COMPONENT || OutLen == 0)) + { + Status = CFE_FS_INVALID_PATH; + } + + return Status; +} + /* ** CFE_FS_ExtractFilenameFromPath - See API and header file for details diff --git a/fsw/cfe-core/src/inc/cfe_fs.h b/fsw/cfe-core/src/inc/cfe_fs.h index 91d4b98af..e8cb2ccd5 100644 --- a/fsw/cfe-core/src/inc/cfe_fs.h +++ b/fsw/cfe-core/src/inc/cfe_fs.h @@ -222,6 +222,36 @@ CFE_Status_t CFE_FS_SetTimestamp(osal_id_t FileDes, CFE_TIME_SysTime_t NewTimest * @{ */ +/*****************************************************************************/ +/** +** \brief Parse a filename input from the user into a local buffer +** +** \par Description +** This provides a more user friendly way to specify file names, +** using default values for the path and extension, which can +** vary from system to system. +** +** If either the pathname or extension is missing from the input, +** it will be added from defaults, with the complete fully-qualified +** filename stored in the output buffer. +** +** \par Assumptions, External Events, and Notes: +** -# The paths and filenames used here are the standard unix style +** filenames separated by "/" (path) and "." (extension) characters. +** -# The default parameters for path+extension are in CFE_GLOBALDATA +** and come from the build system depending on the platform +** +** \param[in] InputName The input filename from the user. +** \param[out] OutputBuffer Buffer to store result. +** \param[in] OutputBufSize Maximum Size of result. +** \param[in] DefaultPath Default value to use for pathname if omitted from input +** \param[in] DefaultExtension Default value to use for extension if omitted from input +** +** \return Execution status, see \ref CFEReturnCodes +** +******************************************************************************/ +int32 CFE_FS_ParseInputFileName(char *OutputBuffer, const char *InputName, size_t OutputBufSize, const char *DefaultPath, const char *DefaultExtension); + /*****************************************************************************/ /** ** \brief Extracts the filename from a unix style path and filename string. diff --git a/fsw/cfe-core/unit-test/es_UT.c b/fsw/cfe-core/unit-test/es_UT.c index 1faa23a7c..e64a912c8 100644 --- a/fsw/cfe-core/unit-test/es_UT.c +++ b/fsw/cfe-core/unit-test/es_UT.c @@ -1295,14 +1295,21 @@ void TestApps(void) CFE_ES_ParseFileEntry(TokenList, 8) == CFE_ES_ERR_APP_CREATE, "CFE_ES_ParseFileEntry", "Unknown entry type"); + + /* Test parsing the startup script with an invalid file name */ + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_ParseInputFileName), CFE_FS_INVALID_PATH); + UT_Report(__FILE__, __LINE__, + CFE_ES_ParseFileEntry(TokenList, 8) == CFE_FS_INVALID_PATH, + "CFE_ES_ParseFileEntry", + "Invalid file name"); } - /* Test parsing the startup script with an invalid file entry */ + /* Test parsing the startup script with an invalid argument passed in */ ES_ResetUnitTest(); UT_Report(__FILE__, __LINE__, - CFE_ES_ParseFileEntry(NULL, 0) == CFE_ES_ERR_APP_CREATE, + CFE_ES_ParseFileEntry(NULL, 0) == CFE_ES_BAD_ARGUMENT, "CFE_ES_ParseFileEntry", - "Invalid file entry"); + "Invalid argument"); /* Test application loading and creation with a task creation failure */ ES_ResetUnitTest(); diff --git a/fsw/cfe-core/unit-test/fs_UT.c b/fsw/cfe-core/unit-test/fs_UT.c index 729b776be..a6d011663 100644 --- a/fsw/cfe-core/unit-test/fs_UT.c +++ b/fsw/cfe-core/unit-test/fs_UT.c @@ -81,6 +81,7 @@ void UtTest_Setup(void) UT_ADD_TEST(Test_CFE_FS_SetTimestamp); UT_ADD_TEST(Test_CFE_FS_ByteSwapCFEHeader); UT_ADD_TEST(Test_CFE_FS_ByteSwapUint32); + UT_ADD_TEST(Test_CFE_FS_ParseInputFileName); UT_ADD_TEST(Test_CFE_FS_ExtractFileNameFromPath); UT_ADD_TEST(Test_CFE_FS_Private); @@ -251,6 +252,87 @@ void Test_CFE_FS_ByteSwapUint32(void) "Byte swap - successful"); } +/* +** Test CFE_FS_ParseInputFileName function +*/ +void Test_CFE_FS_ParseInputFileName(void) +{ + /* + * Test case for: + * int32 CFE_FS_ParseInputFileName(char *OutputBuffer, const char *InputName, size_t OutputBufSize, + * const char *DefaultPath, const char *DefaultExtension) + */ + + char Buffer[64]; + + /* nominal with fully-qualified input */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "/my/path/to/file.txt", sizeof(Buffer), "/dflpath", ".dflext"), CFE_SUCCESS); + UtAssert_StrCmp(Buffer, "/my/path/to/file.txt", "Fully-qualified pass through -> %s", Buffer); + + /* nominal with no path input */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "file.txt", sizeof(Buffer), "/dflpath", ".dflext"), CFE_SUCCESS); + UtAssert_StrCmp(Buffer, "/dflpath/file.txt", "No Path input -> %s", Buffer); + + /* nominal with no path input - should remove duplicate separator */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "file.txt", sizeof(Buffer), "/dflpath/", ".dflext"), CFE_SUCCESS); + UtAssert_StrCmp(Buffer, "/dflpath/file.txt", "No Path input, extra separator -> %s", Buffer); + + /* nominal with no extension input */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "/my/path/to/file", sizeof(Buffer), "/dflpath", ".dflext"), CFE_SUCCESS); + UtAssert_StrCmp(Buffer, "/my/path/to/file.dflext", "No Extension input -> %s", Buffer); + + /* nominal with no extension input, no separator (should be added anyway) */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "/my/path/to/file", sizeof(Buffer), "/dflpath", "dflext"), CFE_SUCCESS); + UtAssert_StrCmp(Buffer, "/my/path/to/file.dflext", "No Extension input, no separator -> %s", Buffer); + + /* nominal with neither path nor extension input */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "file", sizeof(Buffer), "/dflpath", ".dflext"), CFE_SUCCESS); + UtAssert_StrCmp(Buffer, "/dflpath/file.dflext", "No Path nor Extension input -> %s", Buffer); + + /* Bad arguments for buffer pointer/size */ + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(NULL, "file", sizeof(Buffer), "/dflpath", ".dflext"), CFE_FS_BAD_ARGUMENT); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "file", 0, "/dflpath/", ".dflext"), CFE_FS_BAD_ARGUMENT); + + /* Bad arguments for input */ + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, NULL, sizeof(Buffer), "/dflpath", ".dflext"), CFE_FS_INVALID_PATH); + + /* Cases where the file name itself is actually an empty string */ + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "", sizeof(Buffer), "/dflpath", ".dflext"), CFE_FS_INVALID_PATH); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "/my/path/", sizeof(Buffer), "/dflpath", ".dflext"), CFE_FS_INVALID_PATH); + + /* if the default path/extension is null it is just ignored, not an error. */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "file", sizeof(Buffer), NULL, ".dflext"), CFE_SUCCESS); + /* If no path this still adds a leading / */ + UtAssert_StrCmp(Buffer, "/file.dflext", "No Path nor default -> %s", Buffer); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "file", sizeof(Buffer), "/dflpath", NULL), CFE_SUCCESS); + UtAssert_StrCmp(Buffer, "/dflpath/file", "No Extension nor default -> %s", Buffer); + + /* test corner case for termination where result fits exactly, including NUL (should work) */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "file.txt", 18, "/dflpath", ".dflext"), CFE_SUCCESS); + UtAssert_StrCmp(Buffer, "/dflpath/file.txt", "Exact Length input -> %s", Buffer); + /* Confirm character after buffer was not touched */ + UtAssert_INT32_EQ(Buffer[18], 0x7F); + + /* test corner case for termination where result itself fits but cannot fit NUL char (should be error) */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "file.txt", 17, "/dflpath", ".dflext"), CFE_FS_FNAME_TOO_LONG); + UtAssert_INT32_EQ(Buffer[17], 0x7F); + + /* test corner case for termination where result can ONLY fit NUL char (result should be terminated) */ + memset(Buffer, 0x7F, sizeof(Buffer)); + UtAssert_INT32_EQ(CFE_FS_ParseInputFileName(Buffer, "file.txt", 1, "/dflpath", ".dflext"), CFE_FS_FNAME_TOO_LONG); + UtAssert_INT32_EQ(Buffer[0], 0); + UtAssert_INT32_EQ(Buffer[1], 0x7F); +} + /* ** Test FS API write extract file name from path function */ diff --git a/fsw/cfe-core/unit-test/fs_UT.h b/fsw/cfe-core/unit-test/fs_UT.h index 47b2573be..21612b18e 100644 --- a/fsw/cfe-core/unit-test/fs_UT.h +++ b/fsw/cfe-core/unit-test/fs_UT.h @@ -196,6 +196,24 @@ void Test_CFE_FS_ByteSwapUint32(void); ******************************************************************************/ void Test_CFE_FS_IsGzFile(void); +/*****************************************************************************/ +/** +** \brief Test FS API parse input file name function +** +** \par Description +** This function tests the parse input file name function. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \returns +** This function does not return a value. +** +** \sa #UT_InitData, #UT_Report, #CFE_FS_ParseInputFileName +** +******************************************************************************/ +void Test_CFE_FS_ParseInputFileName(void); + /*****************************************************************************/ /** ** \brief Test FS API write extract file name from path function diff --git a/fsw/cfe-core/ut-stubs/ut_fs_stubs.c b/fsw/cfe-core/ut-stubs/ut_fs_stubs.c index f3bdaf3cd..a7ca50207 100644 --- a/fsw/cfe-core/ut-stubs/ut_fs_stubs.c +++ b/fsw/cfe-core/ut-stubs/ut_fs_stubs.c @@ -199,6 +199,34 @@ int32 CFE_FS_EarlyInit(void) return status; } +/*****************************************************************************/ +/* + * Stub for CFE_FS_ParseInputFileName - see prototype for description + */ +int32 CFE_FS_ParseInputFileName(char *OutputBuffer, const char *InputName, size_t OutputBufSize, + const char *DefaultPath, const char *DefaultExtension) +{ + UT_Stub_RegisterContextGenericArg(UT_KEY(CFE_FS_ParseInputFileName), OutputBuffer); + UT_Stub_RegisterContextGenericArg(UT_KEY(CFE_FS_ParseInputFileName), InputName); + UT_Stub_RegisterContextGenericArg(UT_KEY(CFE_FS_ParseInputFileName), OutputBufSize); + UT_Stub_RegisterContextGenericArg(UT_KEY(CFE_FS_ParseInputFileName), DefaultPath); + UT_Stub_RegisterContextGenericArg(UT_KEY(CFE_FS_ParseInputFileName), DefaultExtension); + + int32 status; + + status = UT_DEFAULT_IMPL(CFE_FS_ParseInputFileName); + + /* Copy any specific output supplied by test case */ + if (status >= 0 && + UT_Stub_CopyToLocal(UT_KEY(CFE_FS_ParseInputFileName), OutputBuffer, OutputBufSize) == 0 && + OutputBufSize > 0) + { + /* Otherwise fall back to simple copy */ + strncpy(OutputBuffer, InputName, OutputBufSize); + } + + return status; +} /*****************************************************************************/ /**