diff options
Diffstat (limited to 'gpr/source/lib/xmp_core/XMP_LibUtils.cpp')
-rw-r--r-- | gpr/source/lib/xmp_core/XMP_LibUtils.cpp | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/gpr/source/lib/xmp_core/XMP_LibUtils.cpp b/gpr/source/lib/xmp_core/XMP_LibUtils.cpp new file mode 100644 index 0000000..507294e --- /dev/null +++ b/gpr/source/lib/xmp_core/XMP_LibUtils.cpp @@ -0,0 +1,705 @@ +// ================================================================================================= +// Copyright 2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" + +#include "XMP_LibUtils.hpp" + +#include "UnicodeInlines.incl_cpp" + +#include <cstdio> +#include <cstring> + +// ================================================================================================= + +#ifndef TraceThreadLocks + #define TraceThreadLocks 0 +#endif + +// ------------------------------------------------------------------------------------------------- + +extern "C" bool Initialize_LibUtils() +{ + return true; +} + +// ------------------------------------------------------------------------------------------------- + +extern "C" void Terminate_LibUtils(){ + // Nothing to do. +} + +// ================================================================================================= +// Error notifications +// ================================================================================================= + +bool GenericErrorCallback::CheckLimitAndSeverity ( XMP_ErrorSeverity severity ) const +{ + + if ( this->limit == 0 ) return true; // Always notify if the limit is zero. + if ( severity < this->topSeverity ) return false; // Don't notify, don't count. + + if ( severity > this->topSeverity ) { + this->topSeverity = severity; + this->notifications = 0; + } + + this->notifications += 1; + return (this->notifications <= this->limit); + +} // GenericErrorCallback::CheckLimitAndSeverity + +// ================================================================================================= + +void GenericErrorCallback::NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath /*= 0 */ ) const +{ + bool notifyClient = CanNotify() && !error.IsNotified(); + bool returnAndRecover (severity == kXMPErrSev_Recoverable); + + if ( notifyClient ) { + error.SetNotified(); + notifyClient = CheckLimitAndSeverity ( severity ); + if ( notifyClient ) { + returnAndRecover &= ClientCallbackWrapper( filePath, severity, error.GetID(), error.GetErrMsg() ); + } + } + + if ( ! returnAndRecover ) XMP_Error_Throw ( error ); + +} + +// ================================================================================================= +// Thread synchronization locks +// ================================================================================================= + +XMP_ReadWriteLock::XMP_ReadWriteLock() : beingWritten(false) +{ + #if XMP_DebugBuild && HaveAtomicIncrDecr + this->lockCount = 0; + // Atomic counter must be 32 or 64 bits and naturally aligned. + size_t counterSize = sizeof ( XMP_AtomicCounter ); + size_t counterOffset = XMP_OffsetOf ( XMP_ReadWriteLock, lockCount ); + XMP_Assert ( (counterSize == 4) || (counterSize == 8) ); // Counter must be 32 or 64 bits. + XMP_Assert ( (counterOffset & (counterSize-1)) == 0 ); // Counter must be naturally aligned. + #endif + XMP_BasicRWLock_Initialize ( this->lock ); + #if TraceThreadLocks + fprintf ( stderr, "Created lock %.8X\n", this ); + #endif +} + +// --------------------------------------------------------------------------------------------- + +XMP_ReadWriteLock::~XMP_ReadWriteLock() +{ + #if TraceThreadLocks + fprintf ( stderr, "Deleting lock %.8X\n", this ); + #endif + #if XMP_DebugBuild && HaveAtomicIncrDecr + XMP_Assert ( this->lockCount == 0 ); + #endif + XMP_BasicRWLock_Terminate ( this->lock ); +} + +// --------------------------------------------------------------------------------------------- + +void XMP_ReadWriteLock::Acquire ( bool forWriting ) +{ + #if TraceThreadLocks + fprintf ( stderr, "Acquiring lock %.8X for %s, count %d%s\n", + this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") ); + #endif + + if ( forWriting ) { + XMP_BasicRWLock_AcquireForWrite ( this->lock ); + #if XMP_DebugBuild && HaveAtomicIncrDecr + XMP_Assert ( this->lockCount == 0 ); + #endif + } else { + XMP_BasicRWLock_AcquireForRead ( this->lock ); + XMP_Assert ( ! this->beingWritten ); + } + #if XMP_DebugBuild && HaveAtomicIncrDecr + XMP_AtomicIncrement ( this->lockCount ); + #endif + this->beingWritten = forWriting; + + #if TraceThreadLocks + fprintf ( stderr, "Acquired lock %.8X for %s, count %d%s\n", + this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") ); + #endif +} + +// --------------------------------------------------------------------------------------------- + +void XMP_ReadWriteLock::Release() +{ + #if TraceThreadLocks + fprintf ( stderr, "Releasing lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") ); + #endif + + #if XMP_DebugBuild && HaveAtomicIncrDecr + XMP_Assert ( this->lockCount > 0 ); + XMP_AtomicDecrement ( this->lockCount ); // ! Do these before unlocking, that might release a waiting thread. + #endif + bool forWriting = this->beingWritten; + this->beingWritten = false; + + if ( forWriting ) { + XMP_BasicRWLock_ReleaseFromWrite ( this->lock ); + } else { + XMP_BasicRWLock_ReleaseFromRead ( this->lock ); + } + + #if TraceThreadLocks + fprintf ( stderr, "Released lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") ); + #endif +} + +// ================================================================================================= + +#if UseHomeGrownLock + + #if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild + + // ----------------------------------------------------------------------------------------- + + // About pthread mutexes and conditions: + // + // The mutex protecting the condition must be locked before waiting for the condition. A + // thread can wait for a condition to be signaled by calling the pthread_cond_wait + // subroutine. The subroutine atomically unlocks the mutex and blocks the calling thread + // until the condition is signaled. When the call returns, the mutex is locked again. + + #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); } + #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); } + + #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); } + #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); } + + #define InitializeBasicQueue(queue) { int err = pthread_cond_init ( &queue, 0 ); XMP_Enforce ( err == 0 ); } + #define TerminateBasicQueue(queue) { int err = pthread_cond_destroy ( &queue ); XMP_Enforce ( err == 0 ); } + + #define WaitOnBasicQueue(queue,mutex) { int err = pthread_cond_wait ( &queue, &mutex ); XMP_Enforce ( err == 0 ); } + #define ReleaseOneBasicQueue(queue) { int err = pthread_cond_signal ( &queue ); XMP_Enforce ( err == 0 ); } + #define ReleaseAllBasicQueue(queue) { int err = pthread_cond_broadcast ( &queue ); XMP_Enforce ( err == 0 ); } + + // ----------------------------------------------------------------------------------------- + + #elif XMP_WinBuild + + // ----------------------------------------------------------------------------------------- + + #define InitializeBasicMutex(mutex) { InitializeCriticalSection ( &mutex ); } + #define TerminateBasicMutex(mutex) { DeleteCriticalSection ( &mutex ); } + + #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); } + #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); } + + #if ! BuildLocksForWinXP + + // About Win32 condition variables (not on XP): + // + // Condition variables enable threads to atomically release a lock and enter the + // sleeping state. They can be used with critical sections or slim reader/writer (SRW) + // locks. Condition variables support operations that "wake one" or "wake all" waiting + // threads. After a thread is woken, it re-acquires the lock it released when the thread + // entered the sleeping state. + + #define InitializeBasicQueue(queue) { InitializeConditionVariable ( &queue ); } + #define TerminateBasicQueue(queue) /* Do nothing. */ + + #define WaitOnBasicQueue(queue,mutex) \ + { BOOL ok = SleepConditionVariableCS ( &queue, &mutex, INFINITE /* timeout */ ); XMP_Enforce ( ok ); } + + #define ReleaseOneBasicQueue(queue) { WakeConditionVariable ( &queue ); } + #define ReleaseAllBasicQueue(queue) { WakeAllConditionVariable ( &queue ); } + + #else + + // Need to create our own queue for Windows XP. This is not a general queue, it depends + // on the usage inside XMP_HomeGrownLock where the queueMutex guarantees that the + // queueing operations are done single threaded. + + #define InitializeBasicQueue(queue) /* Do nothing. */ + #define TerminateBasicQueue(queue) /* Do nothing. */ + + #define WaitOnBasicQueue(queue,mutex) { queue.Wait ( mutex ); } + #define ReleaseOneBasicQueue(queue) { queue.ReleaseOne(); } + #define ReleaseAllBasicQueue(queue) { queue.ReleaseAll(); } + + // ------------------------------------------------------------------------------------- + + XMP_WinXP_HGQueue::XMP_WinXP_HGQueue() : queueEvent(0), waitCount(0), releaseAll(false) + { + this->queueEvent = CreateEvent ( NULL, FALSE, TRUE, NULL ); // Auto reset, initially clear. + XMP_Enforce ( this->queueEvent != 0 ); + } + + // ------------------------------------------------------------------------------------- + + XMP_WinXP_HGQueue::~XMP_WinXP_HGQueue() + { + CloseHandle ( this->queueEvent ); + } + + // ------------------------------------------------------------------------------------- + + void XMP_WinXP_HGQueue::Wait ( XMP_BasicMutex & queueMutex ) + { + ++this->waitCount; // ! Does not need atomic increment, protected by queue mutex. + ReleaseBasicMutex ( queueMutex ); + DWORD status = WaitForSingleObject ( this->queueEvent, INFINITE ); + if ( status != WAIT_OBJECT_0 ) XMP_Throw ( "Failure from WaitForSingleObject", kXMPErr_ExternalFailure ); + AcquireBasicMutex ( queueMutex ); + --this->waitCount; // ! Does not need atomic decrement, protected by queue mutex. + + if ( this->releaseAll ) { + if ( this->waitCount == 0 ) { + this->releaseAll = false; + } else { + BOOL ok = SetEvent ( this->queueEvent ); + if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure ); + } + } + } + + // ------------------------------------------------------------------------------------- + + void XMP_WinXP_HGQueue::ReleaseOne() + { + XMP_Assert ( ! this->releaseAll ); + BOOL ok = SetEvent ( this->queueEvent ); + if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure ); + } + + // ------------------------------------------------------------------------------------- + + void XMP_WinXP_HGQueue::ReleaseAll() + { + this->releaseAll = true; + BOOL ok = SetEvent ( this->queueEvent ); + if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure ); + } + + #endif + + // ----------------------------------------------------------------------------------------- + + #endif + + // ============================================================================================= + + XMP_HomeGrownLock::XMP_HomeGrownLock() : lockCount(0), readersWaiting(0), writersWaiting(0), beingWritten(false) + { + InitializeBasicMutex ( this->queueMutex ); + InitializeBasicQueue ( this->writerQueue ); + InitializeBasicQueue ( this->readerQueue ); + } + + // ============================================================================================= + + XMP_HomeGrownLock::~XMP_HomeGrownLock() + { + TerminateBasicMutex ( this->queueMutex ); + TerminateBasicQueue ( this->writerQueue ); + TerminateBasicQueue ( this->readerQueue ); + } + + // ============================================================================================= + + void XMP_HomeGrownLock::AcquireForRead() + { + XMP_AutoMutex autoMutex ( &this->queueMutex ); + + ++this->readersWaiting; // ! Does not need atomic increment, protected by queue mutex. + while ( (this->beingWritten) || (this->writersWaiting > 0) ) { + // Don't allow more readers if writers are waiting. + WaitOnBasicQueue ( this->readerQueue, this->queueMutex ); + } + --this->readersWaiting; // ! Does not need atomic decrement, protected by queue mutex. + XMP_Assert ( ! this->beingWritten ); + + ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex. + } + + // ============================================================================================= + + void XMP_HomeGrownLock::AcquireForWrite() + { + XMP_AutoMutex autoMutex ( &this->queueMutex ); + + ++this->writersWaiting; // ! Does not need atomic increment, protected by queue mutex. + while ( this->lockCount > 0 ) { + WaitOnBasicQueue ( this->writerQueue, this->queueMutex ); + } + --this->writersWaiting; // ! Does not need atomic decrement, protected by queue mutex. + XMP_Assert ( (! this->beingWritten) && (this->lockCount == 0) ); + + ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex. + this->beingWritten = true; + } + + // ============================================================================================= + + void XMP_HomeGrownLock::ReleaseFromRead() + { + XMP_AutoMutex autoMutex ( &this->queueMutex ); + + XMP_Assert ( (! this->beingWritten) && (this->lockCount > 0) ); + --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex. + + if ( this->writersWaiting > 0 ) { + ReleaseOneBasicQueue ( this->writerQueue ); + } else if ( this->readersWaiting > 0 ) { + ReleaseAllBasicQueue ( this->readerQueue ); + } + + } + + // ============================================================================================= + + void XMP_HomeGrownLock::ReleaseFromWrite() + { + XMP_AutoMutex autoMutex ( &this->queueMutex ); + + XMP_Assert ( this->beingWritten && (this->lockCount == 1) ); + --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex. + this->beingWritten = false; + + if ( this->writersWaiting > 0 ) { + ReleaseOneBasicQueue ( this->writerQueue ); + } else if ( this->readersWaiting > 0 ) { + ReleaseAllBasicQueue ( this->readerQueue ); + } + } + + // ============================================================================================= + +#endif + +// ================================================================================================= +// Data structure dumping utilities +// ================================ + +void +DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon ) +{ + + char buffer [20]; + bool prevNormal; + XMP_Status status = 0; + + XMP_StringPtr spanStart, spanEnd; + XMP_StringPtr valueEnd = &value[0] + value.size(); + + spanStart = &value[0]; + while ( spanStart < valueEnd ) { + + // Output the next span of regular characters. + for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) { + if ( *spanEnd > 0x7F ) break; + if ( (*spanEnd < 0x20) && (*spanEnd != kTab) && (*spanEnd != kLF) ) break; + } + if ( spanStart != spanEnd ) status = (*outProc) ( refCon, spanStart, (XMP_StringLen)(spanEnd-spanStart) ); + if ( status != 0 ) break; + spanStart = spanEnd; + + // Output the next span of irregular characters. + prevNormal = true; + for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) { + if ( ((0x20 <= *spanEnd) && (*spanEnd <= 0x7F)) || (*spanEnd == kTab) || (*spanEnd == kLF) ) break; + char space = ' '; + if ( prevNormal ) space = '<'; + status = (*outProc) ( refCon, &space, 1 ); + if ( status != 0 ) break; + OutProcHexByte ( *spanEnd ); + prevNormal = false; + } + if ( ! prevNormal ) { + status = (*outProc) ( refCon, ">", 1 ); + if ( status != 0 ) return; + } + spanStart = spanEnd; + + } + +} // DumpClearString + +// ------------------------------------------------------------------------------------------------- + +static void +DumpStringMap ( const XMP_StringMap & map, XMP_StringPtr label, XMP_TextOutputProc outProc, void * refCon ) +{ + XMP_cStringMapPos currPos; + XMP_cStringMapPos endPos = map.end(); + + size_t maxLen = 0; + for ( currPos = map.begin(); currPos != endPos; ++currPos ) { + size_t currLen = currPos->first.size(); + if ( currLen > maxLen ) maxLen = currLen; + } + + OutProcNewline(); + OutProcLiteral ( label ); + OutProcNewline(); + + for ( currPos = map.begin(); currPos != endPos; ++currPos ) { + OutProcNChars ( " ", 2 ); + DumpClearString ( currPos->first, outProc, refCon ); + OutProcPadding ( maxLen - currPos->first.size() ); + OutProcNChars ( " => ", 4 ); + DumpClearString ( currPos->second, outProc, refCon ); + OutProcNewline(); + } + +} // DumpStringMap + +// ================================================================================================= +// Namespace Tables +// ================================================================================================= + +XMP_NamespaceTable::XMP_NamespaceTable ( const XMP_NamespaceTable & presets ) +{ + XMP_AutoLock presetLock ( &presets.lock, kXMP_ReadLock ); + + this->uriToPrefixMap = presets.uriToPrefixMap; + this->prefixToURIMap = presets.prefixToURIMap; + +} // XMP_NamespaceTable::XMP_NamespaceTable + +// ================================================================================================= + +bool XMP_NamespaceTable::Define ( XMP_StringPtr _uri, XMP_StringPtr _suggPrefix, + XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) +{ + XMP_AutoLock tableLock ( &this->lock, kXMP_WriteLock ); + bool prefixMatches = false; + + XMP_Assert ( (_uri != 0) && (*_uri != 0) && (_suggPrefix != 0) && (*_suggPrefix != 0) ); + + XMP_VarString uri ( _uri ); + XMP_VarString suggPrefix ( _suggPrefix ); + if ( suggPrefix[suggPrefix.size()-1] != ':' ) suggPrefix += ':'; + VerifySimpleXMLName ( _suggPrefix, _suggPrefix+suggPrefix.size()-1 ); // Exclude the colon. + + XMP_StringMapPos uriPos = this->uriToPrefixMap.find ( uri ); + + if ( uriPos == this->uriToPrefixMap.end() ) { + + // The URI is not yet registered, make sure we use a unique prefix. + + XMP_VarString uniqPrefix ( suggPrefix ); + int suffix = 0; + char buffer [32]; // AUDIT: Plenty of room for the "_%d_" suffix. + + while ( true ) { + if ( this->prefixToURIMap.find ( uniqPrefix ) == this->prefixToURIMap.end() ) break; + ++suffix; + snprintf ( buffer, sizeof(buffer), "_%d_:", suffix ); // AUDIT: Using sizeof for snprintf length is safe. + uniqPrefix = suggPrefix; + uniqPrefix.erase ( uniqPrefix.size()-1 ); // ! Remove the trailing ':'. + uniqPrefix += buffer; + } + + // Add the new namespace to both maps. + + XMP_StringPair newNS ( uri, uniqPrefix ); + uriPos = this->uriToPrefixMap.insert ( this->uriToPrefixMap.end(), newNS ); + + newNS.first.swap ( newNS.second ); + (void) this->prefixToURIMap.insert ( this->prefixToURIMap.end(), newNS ); + + } + + // Return the actual prefix and see if it matches the suggested prefix. + + if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str(); + if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size(); + + prefixMatches = ( uriPos->second == suggPrefix ); + return prefixMatches; + +} // XMP_NamespaceTable::Define + +// ================================================================================================= + +bool XMP_NamespaceTable::GetPrefix ( XMP_StringPtr _uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const +{ + XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock ); + bool found = false; + + XMP_Assert ( (_uri != 0) && (*_uri != 0) ); + + XMP_VarString uri ( _uri ); + XMP_cStringMapPos uriPos = this->uriToPrefixMap.find ( uri ); + + if ( uriPos != this->uriToPrefixMap.end() ) { + if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str(); + if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size(); + found = true; + } + + return found; + +} // XMP_NamespaceTable::GetPrefix + +// ================================================================================================= + +bool XMP_NamespaceTable::GetURI ( XMP_StringPtr _prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const +{ + XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock ); + + bool found = false; + + XMP_Assert ( (_prefix != 0) && (*_prefix != 0) ); + + XMP_VarString prefix ( _prefix ); + if ( prefix[prefix.size()-1] != ':' ) prefix += ':'; + XMP_cStringMapPos prefixPos = this->prefixToURIMap.find ( prefix ); + + if ( prefixPos != this->prefixToURIMap.end() ) { + if ( uriPtr != 0 ) *uriPtr = prefixPos->second.c_str(); + if ( uriLen != 0 ) *uriLen = (XMP_StringLen)prefixPos->second.size(); + found = true; + } + + return found; + +} // XMP_NamespaceTable::GetURI + +// ================================================================================================= + +void XMP_NamespaceTable::Dump ( XMP_TextOutputProc outProc, void * refCon ) const +{ + XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock ); + + XMP_cStringMapPos p2uEnd = this->prefixToURIMap.end(); // ! Move up to avoid gcc complaints. + XMP_cStringMapPos u2pEnd = this->uriToPrefixMap.end(); + + DumpStringMap ( this->prefixToURIMap, "Dumping namespace prefix to URI map", outProc, refCon ); + + if ( this->prefixToURIMap.size() != this->uriToPrefixMap.size() ) { + OutProcLiteral ( "** bad namespace map sizes **" ); + XMP_Throw ( "Fatal namespace map problem", kXMPErr_InternalFailure ); + } + + for ( XMP_cStringMapPos nsLeft = this->prefixToURIMap.begin(); nsLeft != p2uEnd; ++nsLeft ) { + + XMP_cStringMapPos nsOther = this->uriToPrefixMap.find ( nsLeft->second ); + if ( (nsOther == u2pEnd) || (nsLeft != this->prefixToURIMap.find ( nsOther->second )) ) { + OutProcLiteral ( " ** bad namespace URI ** " ); + DumpClearString ( nsLeft->second, outProc, refCon ); + break; + } + + for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != p2uEnd; ++nsRight ) { + if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+! + if ( nsLeft->second == nsRight->second ) { + OutProcLiteral ( " ** duplicate namespace URI ** " ); + DumpClearString ( nsLeft->second, outProc, refCon ); + break; + } + } + + } + + for ( XMP_cStringMapPos nsLeft = this->uriToPrefixMap.begin(); nsLeft != u2pEnd; ++nsLeft ) { + + XMP_cStringMapPos nsOther = this->prefixToURIMap.find ( nsLeft->second ); + if ( (nsOther == p2uEnd) || (nsLeft != this->uriToPrefixMap.find ( nsOther->second )) ) { + OutProcLiteral ( " ** bad namespace prefix ** " ); + DumpClearString ( nsLeft->second, outProc, refCon ); + break; + } + + for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != u2pEnd; ++nsRight ) { + if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+! + if ( nsLeft->second == nsRight->second ) { + OutProcLiteral ( " ** duplicate namespace prefix ** " ); + DumpClearString ( nsLeft->second, outProc, refCon ); + break; + } + } + + } + +} // XMP_NamespaceTable::Dump + +// ================================================================================================= +static XMP_Bool matchdigit ( XMP_StringPtr text ) { + if ( *text >= '0' && *text <= '9' ) + return true; + return false; +} + +static XMP_Bool matchUpperCase ( XMP_StringPtr text ) { + if ( *text >= 'A' && *text <= 'Z' ) + return true; + return false; +} + +static XMP_Bool matchLowerCase ( XMP_StringPtr text ) { + if ( *text >= 'a' && *text <= 'z' ) + return true; + return false; +} + +/* matchhere: search for regexp at beginning of text */ +static XMP_Bool matchhere ( XMP_StringPtr regexp, XMP_StringPtr text ) { + if ( regexp[0] == '\0' ) + return true; + if ( regexp[0] == '\\' ) { + if ( regexp[1] == 'd' ) { + if ( matchdigit(text) ) + return matchhere ( regexp+2, text+1 ); + else + return false; + } + else if ( regexp[1] == 'W' ) { + if ( matchUpperCase(text) ) + return matchhere ( regexp+2, text+1 ); + else + return false; + } + else if ( regexp[1] == 'w' ) { + if ( matchLowerCase(text) ) + return matchhere ( regexp+2, text+1 ); + else + return false; + } + } + + if ( regexp[0] == '$' && regexp[1] == '\0' ) + return *text == '\0'; + + if ( *text != '\0' && regexp[0] == *text ) + return matchhere ( regexp+1, text+1 ); + return 0; +} + +/* match: search for regexp anywhere in text */ +static XMP_Bool match ( XMP_StringPtr regexp, XMP_StringPtr text ) { + if ( regexp[0] == '^' ) + return matchhere ( regexp+1, text ); + do { /* must look even if string is empty */ + if ( matchhere ( regexp, text ) ) + return true; + } while ( *text++ != '\0' ); + return false; +} + +XMP_Bool XMP_RegExp::Match ( XMP_StringPtr s ) +{ + if ( regExpStr.size() == 0 ) + return true; + if ( s == NULL ) + return false; + return match ( this->regExpStr.c_str(), s ); +} +// ================================================================================================= |