Skip to content

Commit

Permalink
Fix nasa#981, implement better filename parser routine
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jphickey committed Feb 5, 2021
1 parent 365c095 commit 0881336
Show file tree
Hide file tree
Showing 10 changed files with 372 additions and 14 deletions.
13 changes: 8 additions & 5 deletions cmake/sample_defs/cpu1_cfe_es_startup.scr
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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.

1 change: 1 addition & 0 deletions cmake/target/inc/target_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
5 changes: 5 additions & 0 deletions cmake/target/src/target_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
{
/*
Expand Down Expand Up @@ -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,
Expand Down
34 changes: 28 additions & 6 deletions fsw/cfe-core/src/es/cfe_es_apps.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,25 @@
#include <string.h> /* memset() */
#include <fcntl.h>


#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
Expand Down Expand Up @@ -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;

/*
Expand All @@ -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 */
Expand All @@ -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)
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);
}

/*
Expand Down
162 changes: 162 additions & 0 deletions fsw/cfe-core/src/fs/cfe_fs_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions fsw/cfe-core/src/inc/cfe_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 10 additions & 3 deletions fsw/cfe-core/unit-test/es_UT.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading

0 comments on commit 0881336

Please # to comment.