//------------------------------------------------------------------------------ // Desc: Shared utility routines // // Tabs: 3 // // Copyright (c) 1997-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: sharutil.cpp 3129 2006-01-25 11:46:17 -0700 (Wed, 25 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "sharutil.h" FSTATIC RCODE propertyExists( char * pszProperty, char * pszBuffer, char ** ppszValue); FSTATIC RCODE _flmWrapperFunc( F_Thread * pThread); /******************************************************************** Desc: Parses command-line parameters *********************************************************************/ void flmUtilParseParams( char * pszCommandBuffer, FLMINT iMaxArgs, FLMINT * iArgcRV, char ** ppArgvRV) { FLMINT iArgC = 0; for (;;) { /* Strip off leading white space. */ while ((*pszCommandBuffer == ' ') || (*pszCommandBuffer == '\t')) pszCommandBuffer++; if (!(*pszCommandBuffer)) break; if ((*pszCommandBuffer == '"') || (*pszCommandBuffer == '\'')) { char cQuoteChar = *pszCommandBuffer; pszCommandBuffer++; ppArgvRV [iArgC] = pszCommandBuffer; iArgC++; while ((*pszCommandBuffer) && (*pszCommandBuffer != cQuoteChar)) pszCommandBuffer++; if (*pszCommandBuffer) *pszCommandBuffer++ = 0; } else { ppArgvRV [iArgC] = pszCommandBuffer; iArgC++; while ((*pszCommandBuffer) && (*pszCommandBuffer != ' ') && (*pszCommandBuffer != '\t')) pszCommandBuffer++; if (*pszCommandBuffer) *pszCommandBuffer++ = 0; } /* Quit if we have reached the maximum allowable number of arguments. */ if (iArgC == iMaxArgs) break; } *iArgcRV = iArgC; } /**************************************************************************** Name: FlmVector::setElementAt Desc: a vector set item operation. ****************************************************************************/ #define FLMVECTOR_START_AMOUNT 16 #define FLMVECTOR_GROW_AMOUNT 2 RCODE FlmVector::setElementAt( void * pData, FLMUINT uiIndex) { RCODE rc = NE_XFLM_OK; if ( !m_pElementArray) { TEST_RC( rc = f_calloc( sizeof( void*) * FLMVECTOR_START_AMOUNT, &m_pElementArray)); m_uiArraySize = FLMVECTOR_START_AMOUNT; } if ( uiIndex >= m_uiArraySize) { TEST_RC( rc = f_recalloc( sizeof( void*) * m_uiArraySize * FLMVECTOR_GROW_AMOUNT, &m_pElementArray)); m_uiArraySize *= FLMVECTOR_GROW_AMOUNT; } m_pElementArray[ uiIndex] = pData; Exit: return rc; } /**************************************************************************** Name: FlmVector::getElementAt Desc: a vector get item operation ****************************************************************************/ void * FlmVector::getElementAt( FLMUINT uiIndex) { //if you hit this you are indexing into the vector out of bounds. //unlike a real array, we can catch this here! oh joy! flmAssert ( uiIndex < m_uiArraySize); return m_pElementArray[ uiIndex]; } /**************************************************************************** Name: FlmStringAcc::appendCHAR Desc: append a char (or the same char many times) to the string ****************************************************************************/ RCODE FlmStringAcc::appendCHAR( char ucChar, FLMUINT uiHowMany) { RCODE rc = NE_XFLM_OK; if ( uiHowMany == 1) { FLMBYTE szStr[ 2]; szStr[ 0] = ucChar; szStr[ 1] = 0; rc = this->appendTEXT( (const FLMBYTE*)szStr); } else { FLMBYTE * pszStr; if( RC_BAD( rc = f_alloc( uiHowMany + 1, &pszStr))) { goto Exit; } f_memset( pszStr, ucChar, uiHowMany); pszStr[ uiHowMany] = 0; rc = this->appendTEXT( pszStr); f_free( &pszStr); } Exit: return rc; } /**************************************************************************** Name: FlmStringAcc::appendTEXT Desc: appending text to the accumulator safely. all other methods in the class funnel through this one, as this one contains the logic for making sure storage requirements are met. ****************************************************************************/ RCODE FlmStringAcc::appendTEXT( const FLMBYTE * pszVal) { RCODE rc = NE_XFLM_OK; FLMUINT uiIncomingStrLen; FLMUINT uiStrLen; //be forgiving if they pass in a NULL if ( !pszVal) { goto Exit; } //also be forgiving if they pass a 0-length string else if ( (uiIncomingStrLen = f_strlen( pszVal)) == 0) { goto Exit; } //compute total size we need to store the new total if ( m_bQuickBufActive || m_pszVal) { uiStrLen = uiIncomingStrLen + m_uiValStrLen; } else { uiStrLen = uiIncomingStrLen; } //just use small buffer if it's small enough if ( uiStrLen < FSA_QUICKBUF_BUFFER_SIZE) { f_strcat( m_szQuickBuf, pszVal); m_bQuickBufActive = TRUE; } //we are exceeding the quickbuf size, so get the bytes from the heap else { //ensure storage requirements are met (and then some) if ( m_pszVal == NULL) { FLMUINT uiNewBytes = (uiStrLen+1) * 4; if ( RC_OK ( rc = f_alloc( (FLMUINT)(sizeof( FLMBYTE) * uiNewBytes), &m_pszVal))) { m_uiBytesAllocatedForPszVal = uiNewBytes; m_pszVal[ 0] = 0; } else { goto Exit; } } else if ( (m_uiBytesAllocatedForPszVal-1) < uiStrLen) { FLMUINT uiNewBytes = (uiStrLen+1) * 4; if ( RC_OK( rc = f_realloc( (FLMUINT)(sizeof( FLMBYTE) * uiNewBytes), &m_pszVal))) { m_uiBytesAllocatedForPszVal = uiNewBytes; } else { goto Exit; } } //if transitioning from quick buf to heap buf, we need to //transfer over the quick buf contents and unset the flag if ( m_bQuickBufActive) { m_bQuickBufActive = FALSE; f_strcpy( m_pszVal, m_szQuickBuf); //no need to zero out m_szQuickBuf because it will never //be used again, unless a clear() is issued, in which //case it will be zeroed out then. } //copy over the string f_strcat( m_pszVal, pszVal); } m_uiValStrLen = uiStrLen; Exit: return rc; } /**************************************************************************** Desc: printf into the FlmStringAcc ****************************************************************************/ RCODE FlmStringAcc::printf( const char * pszFormatString, ...) { f_va_list args; char * pDestStr = NULL; const size_t iSize = 4096; RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = f_alloc( iSize, &pDestStr))) { goto Exit; } f_va_start( args, pszFormatString); f_vsprintf( pDestStr, pszFormatString, &args); f_va_end( args); this->clear(); TEST_RC( rc = this->appendTEXT( (FLMBYTE *)pDestStr)); Exit: if ( pDestStr) { f_free( &pDestStr); } return rc; } /**************************************************************************** Desc: formatted appender like sprintf ****************************************************************************/ RCODE FlmStringAcc::appendf( const char * pszFormatString, ...) { f_va_list args; char * pDestStr = NULL; const size_t iSize = 4096; RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = f_alloc( iSize, &pDestStr))) { goto Exit; } f_va_start( args, pszFormatString); f_vsprintf( pDestStr, pszFormatString, &args); f_va_end( args); TEST_RC( rc = this->appendTEXT( (FLMBYTE *)pDestStr)); Exit: if ( pDestStr) { f_free( &pDestStr); } return rc; } /**************************************************************************** Desc: Constructor *****************************************************************************/ FlmContext::FlmContext() { m_szCurrDir[ 0] = '\0'; m_hMutex = F_MUTEX_NULL; m_bIsSetup = FALSE; } /**************************************************************************** Desc: Destructor *****************************************************************************/ FlmContext::~FlmContext( void) { if( m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); m_hMutex = F_MUTEX_NULL; } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmContext::setup( FLMBOOL bShared) { RCODE rc = NE_XFLM_OK; if( bShared) { if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } } m_bIsSetup = TRUE; Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmContext::setCurrDir( FLMBYTE * pszCurrDir) { RCODE rc = NE_XFLM_OK; flmAssert( m_bIsSetup); lock(); f_strcpy( m_szCurrDir, pszCurrDir); unlock(); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmContext::getCurrDir( FLMBYTE * pszCurrDir) { RCODE rc = NE_XFLM_OK; flmAssert( m_bIsSetup); lock(); f_strcpy( pszCurrDir, m_szCurrDir); unlock(); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ void FlmContext::lock( void) { flmAssert( m_bIsSetup); if( m_hMutex != F_MUTEX_NULL) { f_mutexLock( m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ void FlmContext::unlock( void) { flmAssert( m_bIsSetup); if( m_hMutex != F_MUTEX_NULL) { f_mutexUnlock( m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ FlmThreadContext::FlmThreadContext( void) { m_pScreen = NULL; m_pWindow = NULL; m_bShutdown = FALSE; m_pLocalContext = NULL; m_pSharedContext = NULL; m_pNext = NULL; m_pPrev = NULL; m_uiID = 0; m_hMutex = F_MUTEX_NULL; m_pThrdFunc = NULL; m_pvAppData = NULL; m_pThread = NULL; m_bFuncExited = FALSE; } /**************************************************************************** Desc: *****************************************************************************/ FlmThreadContext::~FlmThreadContext( void) { // Free the local context if( m_pLocalContext) { m_pLocalContext->Release(); } // Destroy the semaphore if( m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ void FlmThreadContext::lock( void) { if( m_hMutex != F_MUTEX_NULL) { f_mutexLock( m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ void FlmThreadContext::unlock( void) { if( m_hMutex != F_MUTEX_NULL) { f_mutexUnlock( m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmThreadContext::setup( FlmSharedContext * pSharedContext, char * pszThreadName, THREAD_FUNC_p pFunc, void * pvAppData) { RCODE rc = NE_XFLM_OK; flmAssert( pSharedContext != NULL); m_pSharedContext = pSharedContext; m_pThrdFunc = pFunc; m_pvAppData = pvAppData; if( (m_pLocalContext = f_new FlmContext) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } if( pszThreadName && f_strlen( pszThreadName) <= MAX_THREAD_NAME_LEN) { f_strcpy( m_szName, pszThreadName); } else { f_sprintf( m_szName, "flmGenericThread"); } if( RC_BAD( rc = m_pLocalContext->setup( FALSE))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ void FlmThreadContext::getName( char * pszName, FLMBOOL bLocked) { if( !bLocked) { lock(); } f_strcpy( pszName, m_szName); if( !bLocked) { unlock(); } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmThreadContext::execute( void) { flmAssert( m_pThrdFunc != NULL); m_FuncRC = m_pThrdFunc( this, m_pvAppData); return m_FuncRC; } /**************************************************************************** Desc: *****************************************************************************/ void FlmThreadContext::shutdown() { m_bShutdown = TRUE; } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmThreadContext::exec( void) { flmAssert( m_pThrdFunc != NULL); return( (RCODE)(m_pThrdFunc( this, m_pvAppData))); } /**************************************************************************** Desc: *****************************************************************************/ FlmSharedContext::FlmSharedContext( void) { m_pParentContext = NULL; m_pFtxInfo = NULL; m_pThreadList = NULL; m_bLocalShutdownFlag = FALSE; m_pbShutdownFlag = &m_bLocalShutdownFlag; m_hSem = F_SEM_NULL; m_uiNextProcID = 1; m_bPrivateShare = FALSE; } /**************************************************************************** Desc: *****************************************************************************/ FlmSharedContext::~FlmSharedContext( void) { // Clean up the thread list shutdown(); // Free the ESem if( m_hSem != F_SEM_NULL) { f_semDestroy( &m_hSem); } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::init( FlmSharedContext * pSharedContext, FTX_INFO * pFtxInfo) { RCODE rc = NE_XFLM_OK; // Initialize the base class if( RC_BAD( rc = FlmContext::setup( TRUE))) { goto Exit; } m_pFtxInfo = pFtxInfo; if( RC_BAD( rc = f_semCreate( &m_hSem))) { goto Exit; } m_pParentContext = pSharedContext; Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ void FlmSharedContext::shutdown( void) { FLMBOOL bLocked = FALSE; *m_pbShutdownFlag = TRUE; for( ;;) { lock(); bLocked = TRUE; if( m_pThreadList) { m_pThreadList->shutdown(); } else { break; } unlock(); bLocked = FALSE; (void)f_semWait( m_hSem, 1000); } if( bLocked) { unlock(); } } /**************************************************************************** Desc: *****************************************************************************/ void FlmSharedContext::wait( void) { FLMBOOL bLocked = FALSE; for( ;;) { lock(); bLocked = TRUE; if( !m_pThreadList) { break; } unlock(); bLocked = FALSE; (void)f_semWait( m_hSem, 1000); } if( bLocked) { unlock(); } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::spawn( FlmThreadContext * pThread, FLMUINT * puiThreadID) { char szName[ MAX_THREAD_NAME_LEN + 1]; RCODE rc = NE_XFLM_OK; registerThread( pThread); pThread->getName( szName); if( RC_BAD( rc = f_threadCreate( NULL, _flmWrapperFunc, szName, FLM_DEFAULT_THREAD_GROUP, 0, pThread))) { goto Exit; } if( puiThreadID) { *puiThreadID = pThread->getID(); } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::spawn( char * pszThreadName, THREAD_FUNC_p pFunc, void * pvUserData, FLMUINT * puiThreadID) { FlmThreadContext * pThread; RCODE rc = NE_XFLM_OK; if( (pThread = f_new FlmThreadContext) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pThread->setup( this, pszThreadName, pFunc, pvUserData))) { goto Exit; } if( RC_BAD( rc = spawn( pThread, puiThreadID))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::registerThread( FlmThreadContext * pThread) { RCODE rc = NE_XFLM_OK; lock(); pThread->setNext( m_pThreadList); if( m_pThreadList) { m_pThreadList->setPrev( pThread); } m_pThreadList = pThread; pThread->setID( m_uiNextProcID++); unlock(); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::deregisterThread( FlmThreadContext * pThread) { FlmThreadContext * pTmpThrd; RCODE rc = NE_XFLM_OK; lock(); pTmpThrd = m_pThreadList; while( pTmpThrd) { if( pTmpThrd == pThread) { if( pTmpThrd->getPrev()) { pTmpThrd->getPrev()->setNext( pTmpThrd->getNext()); } if( pTmpThrd->getNext()) { pTmpThrd->getNext()->setPrev( pTmpThrd->getPrev()); } if( pTmpThrd == m_pThreadList) { m_pThreadList = pTmpThrd->getNext(); } pTmpThrd->Release(); break; } pTmpThrd = pTmpThrd->getNext(); } f_semSignal( m_hSem); unlock(); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::killThread( FLMUINT uiThreadID, FLMUINT uiMaxWait) { FlmThreadContext * pThread; FLMUINT uiStartTime; RCODE rc = NE_XFLM_OK; lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { pThread->shutdown(); break; } pThread = pThread->getNext(); } unlock(); // Wait for the thread to exit uiStartTime = FLM_GET_TIMER(); FLM_SECS_TO_TIMER_UNITS( uiMaxWait, uiMaxWait); for( ;;) { (void)f_semWait( m_hSem, 200); lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { break; } pThread = pThread->getNext(); } unlock(); if( !pThread) { break; } if( uiMaxWait) { if( FLM_GET_TIMER() - uiStartTime >= uiMaxWait) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } } } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::setFocus( FLMUINT uiThreadID) { FlmThreadContext * pThread; lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { if( pThread->getScreen()) { FTXScreenDisplay( pThread->getScreen()); } break; } pThread = pThread->getNext(); } unlock(); return( NE_XFLM_OK); } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FlmSharedContext::isThreadTerminating( FLMUINT uiThreadID) { FLMBOOL bTerminating = FALSE; FlmThreadContext * pThread; lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { if( pThread->getShutdownFlag()) { bTerminating = TRUE; } break; } pThread = pThread->getNext(); } unlock(); return( bTerminating); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::getThread( FLMUINT uiThreadID, FlmThreadContext ** ppThread) { FlmThreadContext * pThread; lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { if( ppThread) { *ppThread = pThread; } break; } pThread = pThread->getNext(); } unlock(); return( ((pThread != NULL) ? NE_XFLM_OK : RC_SET( NE_XFLM_NOT_FOUND))); } /**************************************************************************** Desc: *****************************************************************************/ RCODE _flmWrapperFunc( F_Thread * pFlmThread) { FlmThreadContext * pThread = (FlmThreadContext *)pFlmThread->getParm1(); FlmSharedContext * pSharedContext = pThread->getSharedContext(); pThread->setFlmThread( pFlmThread); if( RC_BAD( pThread->execute())) { goto Exit; } Exit: pThread->setFuncExited(); pThread->setFlmThread( NULL); // Unlink the thread from the shared context pSharedContext->deregisterThread( pThread); return( NE_XFLM_OK); } /**************************************************************************** Desc: callback to use to output a line ****************************************************************************/ void utilOutputLine( char * pszData, void * pvUserData) { FTX_WINDOW * pMainWindow = (FTX_WINDOW*)pvUserData; FLMUINT uiBack, uiFore; FTXWinGetBackFore( pMainWindow, &uiBack, &uiFore); FTXWinCPrintf( pMainWindow, uiBack, uiFore, "%s\n", pszData); } /**************************************************************************** Name: utilPressAnyKey Desc: callback to serve as a 'pager' function when the Usage: help is too long to fit on one screen. ****************************************************************************/ void utilPressAnyKey( char * pszMessage, void * pvUserData) { FTX_WINDOW * pMainWindow = (FTX_WINDOW*)pvUserData; FLMUINT uiChar; FLMUINT uiBack, uiFore; FTXWinGetBackFore( pMainWindow, &uiBack, &uiFore); FTXWinCPrintf( pMainWindow, uiBack, uiFore, pszMessage); while ( FTXWinTestKB( pMainWindow) != FTXRC_SUCCESS) { f_sleep( 100); //don't hog the cpu } FTXWinCPrintf( pMainWindow, uiBack, uiFore, "\r "); FTXWinCPrintf( pMainWindow, uiBack, uiFore, "\r"); FTXWinInputChar( pMainWindow, &uiChar); } /**************************************************************************** Name: utilInitWindow Desc: routine to startup the TUI ****************************************************************************/ RCODE utilInitWindow( char * pszTitle, FLMUINT * puiScreenRows, FTX_INFO ** ppFtxInfo, FTX_WINDOW ** ppMainWindow, FLMBOOL * pbShutdown) { FTX_SCREEN * pScreen = NULL; FTX_WINDOW * pTitleWin = NULL; FLMUINT uiCols; int iResCode = 0; if( FTXInit( pszTitle, (FLMBYTE)80, (FLMBYTE)50, WPS_BLUE, WPS_WHITE, NULL, NULL, ppFtxInfo) != FTXRC_SUCCESS) { iResCode = 1; goto Exit; } FTXSetShutdownFlag( *ppFtxInfo, pbShutdown); if( FTXScreenInit( *ppFtxInfo, pszTitle, &pScreen) != FTXRC_SUCCESS) { iResCode = 1; goto Exit; } if( FTXScreenGetSize( pScreen, &uiCols, puiScreenRows) != FTXRC_SUCCESS) { iResCode = 1; goto Exit; } if ( FTXScreenInitStandardWindows( pScreen, WPS_RED, WPS_WHITE, WPS_BLUE, WPS_WHITE, FALSE, FALSE, pszTitle, &pTitleWin, ppMainWindow) != FTXRC_SUCCESS) { iResCode = 1; goto Exit; } Exit: return (RCODE)iResCode; } /**************************************************************************** Name: utilShutdownWindow Desc: routine to shutdown the TUI ****************************************************************************/ void utilShutdownWindow( FTX_INFO * pFtxInfo) { FTXFree( &pFtxInfo); } /**************************************************************************** Desc: read the contents of the argument file into the ppszReturnString buffer ****************************************************************************/ RCODE fileToString( char * pszFile, char ** ppszReturnString) { char * pszBuffer = NULL; IF_FileHdl * pFileHdl = NULL; F_FileSystem fileSystem; FLMUINT64 ui64FileSize = 0; FLMUINT uiBytesRead = 0; RCODE rc = NE_XFLM_OK; if (RC_BAD(rc = fileSystem.Open( pszFile, XFLM_IO_RDONLY, &pFileHdl))) { goto Exit; } if (RC_BAD( rc = pFileHdl->Size( &ui64FileSize))) { goto Exit; } if( ui64FileSize == 0) { goto Exit; } if( RC_BAD( rc = f_alloc( (FLMUINT)(ui64FileSize + 1), &pszBuffer))) { goto Exit; } if (RC_BAD( rc = pFileHdl->Read( 0, (FLMUINT)ui64FileSize, pszBuffer, &uiBytesRead))) { goto Exit; } flmAssert( ui64FileSize == uiBytesRead); pszBuffer[ ui64FileSize] = 0; Exit: if(pFileHdl) { pFileHdl->Close(); pFileHdl->Release(); } if ( RC_BAD( rc) && pszBuffer) { f_free( &pszBuffer); } else if ( RC_OK( rc)) { *ppszReturnString = pszBuffer; } return( rc); } /**************************************************************************** Desc: allocate a copy of the arg string and return it out ****************************************************************************/ char * getStringClone( char * pszSrcStr) { char * pszReturnVal = NULL; if( RC_BAD( f_alloc( f_strlen( pszSrcStr) + 1, &pszReturnVal))) { goto Exit; } f_strcpy( pszReturnVal, pszSrcStr); Exit: return pszReturnVal; } /**************************************************************************** Desc: fill a buffer with the current (or given) time ****************************************************************************/ FLMUINT utilGetTimeString( char * pszOutString, FLMUINT uiBufferSize, FLMUINT uiInSeconds) //default param to use user-supplied time { F_TMSTAMP timeStamp; FLMUINT uiSeconds; if ( uiInSeconds != 0) { f_timeSecondsToDate( uiInSeconds, &timeStamp); } else { f_timeGetTimeStamp( &timeStamp); } f_timeDateToSeconds( &timeStamp, &uiSeconds); char szTemp[ 256]; f_sprintf( szTemp, "%4u-%02u-%02u %02u:%02u:%02u", (unsigned)timeStamp.year, (unsigned)(timeStamp.month + 1), (unsigned)timeStamp.day, (unsigned)timeStamp.hour, (unsigned)timeStamp.minute, (unsigned)timeStamp.second); f_strncpy( pszOutString, szTemp, uiBufferSize - 1); pszOutString[ uiBufferSize-1] = 0; return uiSeconds; } #define UTIL_PROP_DELIMITER '!' FSTATIC RCODE propertyExists( char * pszProperty, char * pszBuffer, char ** ppszValue) { flmAssert( pszProperty); FlmStringAcc acc; RCODE rc = NE_XFLM_OK; //returns only memory errors *ppszValue = NULL; if ( !pszBuffer) { goto Exit; } else { char * pszValue; acc.appendf( "%s%c", pszProperty, UTIL_PROP_DELIMITER); if ( (pszValue = (char *)f_strstr( pszBuffer, pszProperty)) != NULL) { pszValue = (char *)(1 + f_strchr( pszValue, UTIL_PROP_DELIMITER)); *ppszValue = getStringClone( pszValue); if ( !*ppszValue) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } ((FLMBYTE*)(f_strchr( *ppszValue, '\n')))[ 0] = 0; } else { goto Exit; } } Exit: return rc; } RCODE utilWriteProperty( char * pszFile, char * pszProp, char * pszValue) { RCODE rc = NE_XFLM_OK; char * pszContents = NULL; char * pszExistingProperty; FlmStringAcc newContents; F_FileSystem fileSys; //can't have newlines in the props or values flmAssert( !f_strchr( pszProp, '\n')); flmAssert( !f_strchr( pszValue, '\n')); if ( RC_BAD( fileSys.Exists( pszFile))) { //add trailing newline TEST_RC( rc = f_filecpy( pszFile, "")); } if ( RC_BAD( fileToString( pszFile, &pszContents))) { goto Exit; } //propertyExists returns out a new TEST_RC( rc = propertyExists( pszProp, pszContents, &pszExistingProperty)); if ( !pszExistingProperty) { newContents.appendf( "%s%c%s\n", pszProp, UTIL_PROP_DELIMITER, pszValue); newContents.appendTEXT( (FLMBYTE *)pszContents); } else { f_free( &pszExistingProperty); pszExistingProperty = NULL; FLMUINT uiProps = 0; //write out nulls in place of the "\n"'s throughout the contents char * pszNuller = pszContents; for( ;;) { pszNuller = (char *)f_strchr( pszNuller, '\n'); if ( pszNuller) { pszNuller[ 0] = 0; pszNuller++; uiProps++; } else { break; } } char * pszNextLine = pszContents; char * pszNextProp; char * pszNextVal; char * pszBang; while ( uiProps--) { pszBang = (char *)f_strchr( pszNextLine, UTIL_PROP_DELIMITER); flmAssert( pszBang); //better have a UTIL_PROP_DELIMITER in it pszBang[ 0] = 0; pszNextProp = pszNextLine; pszNextVal = pszBang + 1; if ( !(STREQ( pszNextProp, pszProp))) { pszBang[ 0] = UTIL_PROP_DELIMITER; newContents.appendTEXT( (FLMBYTE *)pszNextLine); newContents.appendCHAR( '\n'); } else { newContents.appendf( "%s%c%s\n", pszNextProp, UTIL_PROP_DELIMITER, pszValue); pszBang[ 0] = UTIL_PROP_DELIMITER; } pszNextLine = pszNextLine + f_strlen( pszNextLine) + 1; } } rc = f_filecpy( pszFile, newContents.getTEXT()); Exit: if ( pszContents) { f_free( &pszContents); } return rc; } RCODE utilReadProperty( char * pszFile, char * pszProp, FlmStringAcc * pAcc) { RCODE rc = NE_XFLM_OK; F_FileSystem fileSys; char * pszContents = NULL; char * pszValue = NULL; if ( RC_BAD( fileSys.Exists( pszFile))) { //be nice here. simply don't append anything into FlmStringAcc goto Exit; } if ( RC_BAD( fileToString( pszFile, &pszContents))) { goto Exit; } TEST_RC( rc = propertyExists( pszProp, pszContents, &pszValue)); TEST_RC( rc = pAcc->appendTEXT( (FLMBYTE *)pszValue)); Exit: if ( pszValue) { f_free( &pszValue); } if ( pszContents) { f_free( &pszContents); } return rc; } void scramble( F_RandomGenerator * pRandGen, FLMUINT * puiArray, FLMUINT uiNumElems) { FLMUINT uiLoop; FLMUINT uiTmp; FLMUINT uiIndex; for( uiLoop = 0; uiLoop < uiNumElems; uiLoop++) { uiIndex = pRandGen->randomChoice( 0, uiNumElems - 1); f_swap( puiArray[uiLoop], puiArray[uiIndex], uiTmp); } }