//------------------------------------------------------------------------------ // 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 _flmWrapperFunc( IF_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( (const char *)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, (const char *)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, (const char *)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; FLMSIZET 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; FLMSIZET 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( (char *)m_szCurrDir, (const char *)pszCurrDir); unlock(); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmContext::getCurrDir( FLMBYTE * pszCurrDir) { RCODE rc = NE_XFLM_OK; flmAssert( m_bIsSetup); lock(); f_strcpy( (char *)pszCurrDir, (const char *)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, const 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_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) { RCODE rc = NE_XFLM_OK; // Initialize the base class if( RC_BAD( rc = FlmContext::setup( TRUE))) { goto Exit; } 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) { RCODE rc = NE_XFLM_OK; char szName[ MAX_THREAD_NAME_LEN + 1]; IF_ThreadMgr * pThreadMgr = NULL; registerThread( pThread); pThread->getName( szName); if( RC_BAD( rc = FlmGetThreadMgr( &pThreadMgr))) { goto Exit; } if( RC_BAD( rc = pThreadMgr->createThread( NULL, _flmWrapperFunc, szName, 0, 0, pThread))) { goto Exit; } if( puiThreadID) { *puiThreadID = pThread->getID(); } Exit: if( pThreadMgr) { pThreadMgr->Release(); } 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(); uiMaxWait = FLM_SECS_TO_TIMER_UNITS( 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( IF_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; eColorType 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; eColorType uiBack, uiFore; FTXWinGetBackFore( pMainWindow, &uiBack, &uiFore); FTXWinCPrintf( pMainWindow, uiBack, uiFore, pszMessage); while( RC_BAD( FTXWinTestKB( pMainWindow))) { f_sleep( 100); } 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_WINDOW ** ppMainWindow, FLMBOOL * pbShutdown) { FTX_SCREEN * pScreen = NULL; FTX_WINDOW * pTitleWin = NULL; FLMUINT uiCols; int iResCode = 0; if( RC_BAD( FTXInit( pszTitle, (FLMBYTE)80, (FLMBYTE)50, FLM_BLUE, FLM_WHITE, NULL, NULL))) { iResCode = 1; goto Exit; } FTXSetShutdownFlag( pbShutdown); if( RC_BAD( FTXScreenInit( pszTitle, &pScreen))) { iResCode = 1; goto Exit; } if( RC_BAD( FTXScreenGetSize( pScreen, &uiCols, puiScreenRows))) { iResCode = 1; goto Exit; } if( RC_BAD( FTXScreenInitStandardWindows( pScreen, FLM_RED, FLM_WHITE, FLM_BLUE, FLM_WHITE, FALSE, FALSE, pszTitle, &pTitleWin, ppMainWindow))) { iResCode = 1; goto Exit; } Exit: return (RCODE)iResCode; } /**************************************************************************** Name: utilShutdownWindow Desc: routine to shutdown the TUI ****************************************************************************/ void utilShutdownWindow() { FTXExit(); }