[AOLSERVER-COMMITS] knutil/src atomicint.cpp, NONE, 1.1 atomicint.il, NONE, 1.1 counter.cpp, NONE, 1.1 kncircularmapping.cpp, NONE, 1.1 knconsistenthash.cpp, NONE, 1.1 kncrashhandler.cpp, NONE, 1.1 kncrypt.cpp, NONE, 1.1 knerror.cpp, NONE, 1.1 knhash.cpp, NONE, 1.1 knlog.cpp, NONE, 1.1 knmarkuputil.cpp, NONE, 1.1 knmemory.cpp, NONE, 1.1 knmutex.cpp, NONE, 1.1 knnetwork.cpp, NONE, 1.1 knnewdelete.cpp, NONE, 1.1 knpattern.cpp, NONE, 1.1 knperfstatistics.cpp, NONE, 1.1 knregex.cpp, NONE, 1.1 knrwlock.cpp, NONE, 1.1 knrwlogginglock.cpp, NONE, 1.1 knset.cpp, NONE, 1.1 knsleep.cpp, NONE, 1.1 knstring.cpp, NONE, 1.1 knstringtokenizer.cpp, NONE, 1.1 knsysutility_unix.cpp, NONE, 1.1 knsysutility_win32.cpp, NONE, 1.1 kntclerrors.cpp, NONE, 1.1 kntclfile.cpp, NONE, 1.1 kntclinterp.cpp, NONE, 1.1 kntemplateuri.cpp, NONE, 1.1 kntime.cpp, NONE, 1.1 knurl.cpp, NONE, 1.1 mtrand.cpp, NONE, 1.1 randomnumber.cpp, NONE, 1.1 shahash.cpp, NONE, 1.1 uuidgenerator.cpp, NONE, 1.1

12 views
Skip to first unread message

cob...@users.sourceforge.net

unread,
May 15, 2008, 8:24:36 PM5/15/08
to AOLSER...@listserv.aol.com
Update of /cvsroot/aolserver/knutil/src
In directory sc8-pr-cvs11.sourceforge.net:/tmp/cvs-serv6393/src

Added Files:
atomicint.cpp atomicint.il counter.cpp kncircularmapping.cpp
knconsistenthash.cpp kncrashhandler.cpp kncrypt.cpp
knerror.cpp knhash.cpp knlog.cpp knmarkuputil.cpp knmemory.cpp
knmutex.cpp knnetwork.cpp knnewdelete.cpp knpattern.cpp
knperfstatistics.cpp knregex.cpp knrwlock.cpp
knrwlogginglock.cpp knset.cpp knsleep.cpp knstring.cpp
knstringtokenizer.cpp knsysutility_unix.cpp
knsysutility_win32.cpp kntclerrors.cpp kntclfile.cpp
kntclinterp.cpp kntemplateuri.cpp kntime.cpp knurl.cpp
mtrand.cpp randomnumber.cpp shahash.cpp uuidgenerator.cpp
Log Message:
Added this library to the AOLServer.com repository. It contains a number of C/C++ utilities, some of which integrate into AOLServer, and many of which standalone. A portion of the library is required for the knregistration module, which will be checked in next.

I believe this compiles fine in "native" AOLServer, but it has not been tested recently.

--- NEW FILE: atomicint.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/atomicint.h"

#ifndef WIN32
#include <unistd.h>
#endif

namespace
{

#ifdef WIN32
int getNumberOfProcessors()
{
SYSTEM_INFO si;

memset(&si, 0, sizeof(si));
GetSystemInfo(&si);

return si.dwNumberOfProcessors;
}
#else
int getNumberOfProcessors()
{
return sysconf(_SC_NPROCESSORS_ONLN);
}
#endif /* WIN32 */

/*
* Assume that the number of processors remains static during process
* lifetime. This may be dicey if the superuser is using/abusing tools
* such as Solaris's psradm.
*/
const int gNumberOfProcessors = getNumberOfProcessors();

#ifdef WIN32
#pragma warning(disable : 4035)

/*
* Return with the result in EAX.
*/
inline int atomicAdd(int *pcnt, int cnt)
{
if (gNumberOfProcessors == 1)
{
__asm
{
mov ecx, pcnt
mov eax, cnt
xadd dword ptr [ecx], eax
add eax, cnt
}
}
else
{
__asm
{
mov ecx, pcnt
mov eax, cnt
lock xadd dword ptr [ecx], eax
add eax, cnt
}
}
}

#pragma warning(default : 4035)

inline bool atomicCompareAndSwap(int *pcnt, int oval, int val)
{
unsigned char c;

if (gNumberOfProcessors == 1)
{
__asm
{
mov ecx, pcnt
mov eax, oval
mov edx, val
cmpxchg dword ptr [ecx], edx
sete c
}
}
else
{
__asm
{
mov ecx, pcnt
mov eax, oval
mov edx, val
lock cmpxchg dword ptr [ecx], edx
sete c
}
}

return c != 0;
}

inline bool atomicDecrementAndTest(int *pcnt)
{
unsigned char c;

if (gNumberOfProcessors == 1)
{
__asm
{
mov ecx, pcnt
dec dword ptr [ecx]
sete c
}
}
else
{
__asm
{
mov ecx, pcnt
lock dec dword ptr [ecx]
sete c
}
}

return c != 0;
}

inline bool atomicDoubleCompareAndSwap(int64_t *pcnt, int64_t oval, int64_t val)
{
unsigned char c;

if (gNumberOfProcessors == 1)
{
__asm
{
mov esi, pcnt
mov edx, dword ptr [oval + 4]
mov eax, dword ptr [oval]
mov ecx, dword ptr [val + 4]
mov ebx, dword ptr [val]
cmpxchg8b qword ptr [esi]
sete c
}
}
else
{
__asm
{
mov esi, pcnt
mov edx, dword ptr [oval + 4]
mov eax, dword ptr [oval]
mov ecx, dword ptr [val + 4]
mov ebx, dword ptr [val]
lock cmpxchg8b qword ptr [esi]
sete c
}
}

return c != 0;
}

inline void atomicIncrement(int *pcnt)
{
if (gNumberOfProcessors == 1)
{
__asm
{
mov ecx, pcnt
inc dword ptr [ecx]
}
}
else
{
__asm
{
mov ecx, pcnt
lock inc dword ptr [ecx]
}
}
}

inline void atomicReadBarrier()
{
/*
* A read barrier is only necessary on a multiprocessor box.
*/
if (gNumberOfProcessors != 1)
{
__asm
{
lock add dword ptr [esp], 0
}
}
}

inline void atomicWriteBarrier()
{
/*
* Intel doesn't reorder writes.
*/
}
#elif __linux
inline int atomicAdd(int *pcnt, int cnt)
{
int rc;

if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"xaddl %0,%2"
: "=r" (rc)
: "0" (cnt), "m" (*pcnt)
: "memory");
}
else
{
__asm__ __volatile__(
"lock xaddl %0,%2"
: "=r" (rc)
: "0" (cnt), "m" (*pcnt)
: "memory");
}

return rc + cnt;
}

inline bool atomicCompareAndSwap(int *pcnt, int oval, int val)
{
unsigned char c;

if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"cmpxchgl %2,%1; sete %0"
: "=q" (c), "=m" (*pcnt)
: "r" (val), "1" (*pcnt), "a" (oval));
}
else
{
__asm__ __volatile__(
"lock cmpxchgl %2,%1; sete %0"
: "=q" (c), "=m" (*pcnt)
: "r" (val), "1" (*pcnt), "a" (oval));
}

return c != 0;
}

inline bool atomicDecrementAndTest(int *pcnt)
{
unsigned char c;

if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"decl %1; sete %0"
: "=q" (c)
: "m" (*pcnt)
: "memory");
}
else
{
__asm__ __volatile__(
"lock decl %1; sete %0"
: "=q" (c)
: "m" (*pcnt)
: "memory" );
}

return c != 0;
}

inline bool atomicDoubleCompareAndSwap(int64_t *pcnt, int64_t oval, int64_t val)
{
unsigned char c;

if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"cmpxchg8b %1; sete %0"
: "=q" (c), "=m" (*pcnt)
: "1" (*pcnt),
"A" (oval),
"c" (static_cast<int>(val >> 32)),
"b" (static_cast<int>(val)));
}
else
{
__asm__ __volatile__(
"lock cmpxchg8b %1; sete %0"
: "=q" (c), "=m" (*pcnt)
: "1" (*pcnt),
"A" (oval),
"c" (static_cast<int>(val >> 32)),
"b" (static_cast<int>(val)));
}

return c != 0;
}

inline void atomicIncrement(int *pcnt)
{
if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"incl %0"
: "+m" (*pcnt)
);
}
else
{
__asm__ __volatile__(
"lock incl %0"
: "+m" (*pcnt)
);
}
}

inline void atomicReadBarrier()
{
if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
""
:
:
: "memory");
}
else
{
int x = 0, y = 1;

__asm__ __volatile__(
"xchgl %0,%1"
: "=r" (x)
: "m" (y), "0" (x)
: "memory");
}
}

inline void atomicWriteBarrier()
{
__asm__ __volatile__(
""
:
:
: "memory");
}

#elif __sun

extern "C" int atomicAdd(int *, int);
extern "C" bool atomicCompareAndSwap(int *, int, int);
extern "C" bool atomicDoubleCompareAndSwap(int64_t *, int64_t *, int64_t *);
extern "C" void atomicReadBarrier();
extern "C" void atomicWriteBarrier();

inline bool atomicDecrementAndTest(int *pcnt)
{
return atomicAdd(pcnt, -1) == 0;
}

inline bool atomicDoubleCompareAndSwap(int64_t *pcnt, int64_t oval, int64_t val)
{
return atomicDoubleCompareAndSwap(pcnt, &oval, &val);
}

inline void atomicIncrement(int *pcnt)
{
atomicAdd(pcnt, 1);
}
#endif /* WIN32 */

} //namespace


int AtomicInt::operator+=(int cnt)
{
return atomicAdd(&m_cnt, cnt);
}

int AtomicInt::operator-=(int cnt)
{
return atomicAdd(&m_cnt, -cnt);
}

int AtomicInt::operator++()
{
return atomicAdd(&m_cnt, 1);
}

int AtomicInt::operator++(int)
{
return atomicAdd(&m_cnt, 1) - 1;
}

int AtomicInt::operator--()
{
return atomicAdd(&m_cnt, -1);
}

int AtomicInt::operator--(int)
{
return atomicAdd(&m_cnt, -1) + 1;
}

bool AtomicInt::decrementAndTest()
{
return atomicDecrementAndTest(&m_cnt);
}

void AtomicInt::increment()
{
atomicIncrement(&m_cnt);
}

--- NEW FILE: atomicint.il ---
.inline atomicAdd,8
1:
ld [%o0],%o2
add %o2,%o1,%o3
cas [%o0],%o2,%o3
cmp %o2,%o3
bne,pn %icc,1b
nop
add %o2,%o1,%o0
.end

.inline atomicCompareAndSwap,12
cas [%o0],%o1,%o2
xor %o0,%o0,%o0
cmp %o1,%o2
bne,pn %icc,2f
nop
add %o0,1,%o0
2:
.end

.inline atomicDoubleCompareAndSwap,12
ldx [%o1],%o3
ldx [%o2],%o4
casx [%o0],%o3,%o4
xor %o0,%o0,%o0
cmp %o3,%o4
bne,pn %xcc,3f
nop
add %o0,1,%o0
3:
.end

.inline atomicReadBarrier,0
membar #LoadLoad
.end

.inline atomicWriteBarrier,0
membar #StoreStore
.end

--- NEW FILE: counter.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/counter.h"

Counter::Counter(int c) : count(c)
{
}

Counter::~Counter()
{
}

--- NEW FILE: kncircularmapping.cpp ---
#include <math.h>
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/

#include <sstream>
#include <algorithm>

#include "knexportlibraryknutilmodule.h"
#include "knutil/knerror.h"
#include "knutil/knlog.h"
#include "knutil/kncircularmapping.h"

//--------------------------------------------------------------------------
// Bucket Class
//--------------------------------------------------------------------------

class Bucket
{
public:

Bucket(const std::string& bucketName, double weight) : m_name(bucketName), m_weight(weight)
{
}

std::string m_name;
double m_weight;

std::vector<KnCircularMapping::PointT> m_replicaPoints;
};


bool BucketComp(const Bucket* lhs, const Bucket* rhs)
{
return lhs->m_name < rhs->m_name;
}

bool insertSorted(std::vector<Bucket*>& v, Bucket* value)
{
std::pair<std::vector<Bucket*>::iterator, std::vector<Bucket*>::iterator> bounds;

bounds = equal_range(v.begin(), v.end(), value, BucketComp);

if(bounds.first == bounds.second)
{
if(bounds.first == v.end())
{
v.push_back(value);
return true;
}

v.insert(bounds.second, value);
return true;
}

return false;
}

//--------------------------------------------------------------------------
// ReplicaPoint Class
//--------------------------------------------------------------------------

class ReplicaPoint
{
public:

ReplicaPoint(KnCircularMapping::PointT& point) : m_point(point)
{
}

~ReplicaPoint()
{
}

Bucket* getFirstBucket()
{
if(!m_replicas.size())
{
KnLog(KnLogError, "KnConsistentHash: replica point with no replicas.");

return NULL;
}

Bucket* bucket = *m_replicas.begin();

return bucket;
}

bool addReplica(Bucket* bucket) // keep them sorted , return true if conflict
{
if(!m_replicas.size())
{
m_replicas.push_back(bucket);
return false;
}

bool result = insertSorted(m_replicas, bucket);

if(!result)
{
KnLog(KnLogError, "KnConsistentHash: Bucket with same ID already present at replica");
}

KnLog(KnLogWarning, "KnConsistentHash: conflict when adding bucket %s replica at point %u.", bucket->m_name.c_str(), m_point);

return true;
}

KnCircularMapping::PointT m_point;

std::vector<Bucket*> m_replicas; // should be just one or there are conflicts
};


//--------------------------------------------------------------------------
// KnCircularMapping
//--------------------------------------------------------------------------

KnCircularMapping::KnCircularMapping()
{
m_totalWeight = 0;
}

KnCircularMapping::~KnCircularMapping()
{
removeAllBuckets();
}

bool KnCircularMapping::addBucket(const char* bucketName, std::vector<PointT>& replicas, double weight)
{
if (weight <= 0)
return false;

if (m_buckets.find(bucketName) != m_buckets.end())
return false;

Bucket* bucket = new Bucket(bucketName, weight);
m_buckets[bucketName] = bucket;

ReplicaPoint* rp;

bucket->m_replicaPoints = replicas;

for (int i = 0; i < replicas.size(); i++)
{
rp = findOrAddReplicaPoint(replicas[i]);
rp->addReplica(bucket);
}

m_totalWeight += weight;

return true;
}

bool KnCircularMapping::removeBucket(const char* bucketName)
{
BucketMapIterator it = m_buckets.find(bucketName);
if (it == m_buckets.end())
return false;

Bucket* bucket = it->second;
m_buckets.erase(it);

double weight = bucket->m_weight;
m_totalWeight -= weight;

for (int i = 0; i < bucket->m_replicaPoints.size(); i++)
{
if(!removeReplicaPoint(bucket->m_replicaPoints[i], bucket))
{
KnLog(KnLogError, "KnConsistentHash: could not remove replica point %u for bucket %s.", bucket->m_replicaPoints[i] ,bucket->m_name.c_str());
}
}

SAFE_DELETE(bucket);

return true;
}

ReplicaPoint* KnCircularMapping::findOrAddReplicaPoint(PointT point)
{
ReplicaMapRange bounds;
ReplicaPoint* result;

bounds = m_replicaMap.equal_range(point);

if(bounds.first == bounds.second)
{
// not found

result = new ReplicaPoint(point);

if(bounds.first == m_replicaMap.end())
{
m_replicaMap[point] = result;
return result;
}

m_replicaMap.insert(bounds.second, std::pair<const PointT, ReplicaPoint*>(point, result));
return result;
}

return (*bounds.first).second;
}

ReplicaPoint* KnCircularMapping::findReplicaPoint(PointT point)
{
ReplicaMapIterator it = m_replicaMap.find(point);

if(it == m_replicaMap.end())
return NULL;

return it->second;
}

bool KnCircularMapping::removeReplicaPoint(PointT point, Bucket* bucket)
{
ReplicaMapRange bounds;

bounds = m_replicaMap.equal_range(point);

if(bounds.first == bounds.second)
return false;

ReplicaPoint* rp = (*bounds.first).second;

std::vector<Bucket*>& replicas = rp->m_replicas;

std::vector<Bucket*>::iterator it = find(replicas.begin(), replicas.end(), bucket);

if(it == replicas.end())
return false;

replicas.erase(it);

if(!replicas.size())
{
m_replicaMap.erase(bounds.first);
SAFE_DELETE(rp);
}

return true;
}

Bucket* KnCircularMapping::findBucketForPoint(PointT point)
{
if (m_replicaMap.empty() || m_buckets.empty())
return NULL;

if (m_buckets.size() == 1)
return (*m_replicaMap.begin()).second->getFirstBucket();

ReplicaMapRange bounds;

bounds = m_replicaMap.equal_range(point);

if(bounds.first == bounds.second)
{
// not found

if(bounds.first == m_replicaMap.end())
{
// point is after all replicas => assign to first replica

return (*m_replicaMap.begin()).second->getFirstBucket();
}

// assign to greater replica
return (*bounds.second).second->getFirstBucket();
}

// assign to this replica as it has the same point value
return (*bounds.first).second->getFirstBucket();
}

void KnCircularMapping::removeAllBuckets()
{
ReplicaMapIterator it;
for(it = m_replicaMap.begin(); it != m_replicaMap.end(); it++)
SAFE_DELETE(it->second);
m_replicaMap.clear();

BucketMapIterator bmi = m_buckets.begin();
for(; bmi != m_buckets.end(); bmi++)
SAFE_DELETE(bmi->second);
m_buckets.clear();
}

double KnCircularMapping::getWeight(const std::string& bucketName)
{
BucketMapIterator it = m_buckets.find(bucketName.c_str());

if(it != m_buckets.end())
return it->second->m_weight;

return -1;
}

double KnCircularMapping::getTotalWeight()
{
return m_totalWeight;
}

bool KnCircularMapping::existsBucket(const std::string& bucketName)
{
return m_buckets.find(bucketName.c_str()) != m_buckets.end();
}

std::string KnCircularMapping::assignPointToBucket(PointT point)
{
Bucket* bucket = findBucketForPoint(point);

if(!bucket)
return "";

return bucket->m_name;
}

int KnCircularMapping::bucketCount()
{
return m_buckets.size();
}

--- NEW FILE: knconsistenthash.cpp ---
#include <math.h>
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/

#include <sstream>
#include <algorithm>
#include <vector>

#include "knexportlibraryknutilmodule.h"
#include "knutil/knerror.h"
#include "knutil/knlog.h"
#include "knutil/knhash.h"
#include "knutil/mtrand.h"
#include "knutil/knconsistenthash.h"

//--------------------------------------------------------------------------
// KnConsistentHash
//--------------------------------------------------------------------------

KnConsistentHash::KnConsistentHash(int replicasPerBucket, KnCircularMapping::PointT pointInterval)
{
m_replicasPerBucket = replicasPerBucket;
m_pointInterval = pointInterval;
}

KnConsistentHash::~KnConsistentHash()
{
}

KnConsistentHash::IDHash KnConsistentHash::computeHash(const char* identifier)
{
return hash32(identifier, strlen(identifier), 1492);
}

KnCircularMapping::PointT KnConsistentHash::computePointForResource(const char* identifier)
{
MTRand_int32 randGen( computeHash(identifier) );

return randGen() % m_pointInterval;
}

bool KnConsistentHash::addBucket(const char* bucketName, double weight)
{
return addBucketUnsafe(bucketName, computeHash(bucketName), weight);
}

bool KnConsistentHash::addBucket(const char* bucketName, IDHash bucketHash, double weight)
{
KnRWLock::KnWriteLock wlock(m_bucketLock);

return addBucketUnsafe(bucketName, bucketHash, weight);
}

bool KnConsistentHash::addBucketUnsafe(const char* bucketName, IDHash bucketHash, double weight)
{
if (weight < 0.000001)
return m_mapping.removeBucket(bucketName);

if(m_mapping.existsBucket(bucketName))
{
// Bucket already exists => remove before adding
m_mapping.removeBucket(bucketName);
}

MTRand_int32 randGen(bucketHash);
KnCircularMapping::PointT point;

std::vector<KnCircularMapping::PointT> replicas;
int replicaCount = floor(weight * m_replicasPerBucket);
replicas.reserve(replicaCount);

for (int i = 0; i < replicaCount; i++)
{
point = randGen() % m_pointInterval;

replicas.push_back(point);
}

return m_mapping.addBucket(bucketName, replicas, weight);
}

bool KnConsistentHash::removeBucket(const char* bucketName)
{
KnRWLock::KnWriteLock wlock(m_bucketLock);

return m_mapping.removeBucket(bucketName);
}

void KnConsistentHash::removeAllBuckets()
{
KnRWLock::KnWriteLock wlock(m_bucketLock);

m_mapping.removeAllBuckets();
}

std::string KnConsistentHash::assignPointToBucket(KnCircularMapping::PointT point)
{
KnRWLock::KnReadLock rlock(m_bucketLock);

return m_mapping.assignPointToBucket(point);
}

std::string KnConsistentHash::assignResourceToBucket(const char* identifier)
{
KnRWLock::KnReadLock rlock(m_bucketLock);

return m_mapping.assignPointToBucket( computePointForResource(identifier) );
}


int KnConsistentHash::bucketCount()
{
return m_mapping.bucketCount();
}

bool KnConsistentHash::addBucketsFromSet(const KnSet& bucketSet)
{
if(bucketSet.size() == 0)
return true;

KnRWLock::KnWriteLock wlock(m_bucketLock);

return addBucketsFromSetUnsafe(bucketSet);
}

bool KnConsistentHash::setBucketsFromSet(const KnSet& bucketSet)
{
if(bucketSet.size() == 0)
return true;

KnRWLock::KnWriteLock wlock(m_bucketLock);

m_mapping.removeAllBuckets();

return addBucketsFromSetUnsafe(bucketSet);
}

bool KnConsistentHash::addBucketsFromSetUnsafe(const KnSet& bucketSet)
{
if(bucketSet.size() == 0)
return true;

for (size_t ix = 0; ix < bucketSet.size(); ++ix)
{
KnSetElem entry = bucketSet[ix];
KnString bucketName = entry.m_key;

if(entry.m_value == "")
{
// A delete bucket request
m_mapping.removeBucket(bucketName.c_str());
continue;
}

double weight = entry.m_value.toDouble();

addBucketUnsafe(bucketName.c_str(), computeHash(bucketName.c_str()), weight);
}

return true;
}

double KnConsistentHash::getTotalWeight()
{
return m_mapping.getTotalWeight();
}

--- NEW FILE: kncrashhandler.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/kncrashhandler.h"
#include "knutil/knlog.h"
#include "knutil/knmemory.h"
#include "knutil/knmutex.h"

#include "string"


namespace {
KnMutex gCrashHandlerMutex;
std::string gDumpLocation; // For occasions when we change it.
bool gDumpAtAll = true; // Avoid denial of service by running out of disk (Customer Support called for the default to be false for this reason).
}


#ifdef WIN32

#define _MSJDBG_H
#include "BugslayerUtil.h"

namespace {
// From http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/minidumpwritedump.asp:
// DumpType
// [in] The type of information to be generated. This parameter can be
// one or more of the values from the MINIDUMP_TYPE enumeration.
unsigned int gMiniDumpType = static_cast<unsigned int>(MiniDumpNormal);
char* gMiniDumpFile = "log/liveserver.dmp";
}

LONG __stdcall KnCrashHandler( EXCEPTION_POINTERS * pExPtrs )
{
// Count on the BugSlayer's OutputDebugString (annoying) if
// we're out of stack; if we're a service, we'll have hung
// before getting here. Unfortunately, it's not as rare for us as
// for many other programs, since we're multithreaded (and have therefore
// declared a stack size), but it's rare enough to have worked out OK for a while.
// When we get hangs or crashes on Windows with no stack trace or minidump, then
// we're probably looking at exactly that case. Try increasing the stack size
// in knrouter.tcl.
if ( EXCEPTION_STACK_OVERFLOW != pExPtrs->ExceptionRecord->ExceptionCode )
{
// None of the BugSlayer routines are thread-safe. OTOH, it's hard
// to bring myself to use another mutex at this point, since we deadlock
// so often for mutex problems. Doing it anyway.
KnMutex::KnLock lock(gCrashHandlerMutex);

KnLog(KnLogBug,GetFaultReason(pExPtrs));
KnLog(KnLogNotice,GetRegisterString(pExPtrs));

// Want the ns_fatal call to have all the info about minidump so it's easy
// to see in the Monitor Alarms view.
char *miniDumpIntro = "No .dmp generated:";
char *miniDumpExtra = "";

char *miniDumpFile = gDumpLocation.length() ? const_cast<char*>(gDumpLocation.c_str()) : gMiniDumpFile;

if (!gDumpAtAll)
{
miniDumpExtra = "disabled. See Configuration: Memory in the KSA";
}
else if (IsMiniDumpFunctionAvailable())
{
BSUMDRET problems = CreateCurrentProcessMiniDump(
static_cast<MINIDUMP_TYPE>(gMiniDumpType),
miniDumpFile,
GetCurrentThreadId(),
pExPtrs);
switch (problems)
{
case eDUMP_SUCCEEDED:
miniDumpIntro = "See minidump file";
miniDumpExtra = miniDumpFile;
break;
case eDBGHELP_NOT_FOUND:
miniDumpExtra = "dbghelp.dll not found.";
break;
case eDBGHELP_MISSING_EXPORTS:
miniDumpExtra = "dbghelp.dll missing functionality.";
break;
case eBAD_PARAM:
miniDumpExtra = "Bad parameter.";
break;
case eOPEN_DUMP_FAILED:
miniDumpIntro = "Could not open minidump file";
miniDumpExtra = miniDumpFile;
// No guidance from Mr. Robbins on why in this case.
break;
case eMINIDUMPWRITEDUMP_FAILED:
KnLog(KnLogNotice,"Couldn't write dump; GetLastError returns %d",GetLastError());
miniDumpExtra = "Dump writing failed.";
break;
case eDEATH_ERROR:
miniDumpExtra = "Minidump thread could not be started.";
break;
case eINVALID_ERROR:
miniDumpExtra = "Bad bugslayer code";
break;
}
}
else
{
miniDumpExtra = "Minidump facility not available.";
}

KnLog(KnLogNotice,"Stack trace:");

DWORD dwOpts = GSTSO_SRCLINE | GSTSO_SYMBOL | GSTSO_PARAMS;
for (
LPCSTR pStackMessage =GetFirstStackTraceString( dwOpts, pExPtrs ) ;
0 != pStackMessage ;
pStackMessage = GetNextStackTraceString( dwOpts, pExPtrs )
)
{
KnLog(KnLogNotice,pStackMessage);
}

KnMemory::logStatistics( KnLogNotice ) ;

KnLog(KnLogNotice,"knownow: Memory available: %lu",KnMemory::getMemoryAvail());

Ns_Fatal("Exception shuts down server immediately. %s %s", miniDumpIntro, miniDumpExtra);
}

return EXCEPTION_EXECUTE_HANDLER ;
};


// This routine should be called from DLLMAIN, but so far, we're doing
// OK just installing it from nskn. Since we're not limiting ourselves
// to just one dll, it works pretty nicely, without requiring changes in
// AOLServer. If we ever integrate any DLL that sets its own in this way,
// though, we will run into some conflicts, at which point we'll have to
// BE_NICE_TO_OUR_MODULE_WRITERS and get this called from each of our .dlls.
//
// See Robbins; I'm using "Debugging Applications" (the VS 6 - era book)
//
// Separated KnSetCrashHandler from KnSetCrashInfo for two reasons (one
// very minor)
//
// (1) didn't want to change an existing API [very minor, it's only called
// in one place]
//
// (2) want it called *before* we try to do anything across dll boundaries
// from nskn.
//
// OTOH, since it's going to be set before we even _can_ look at the defauls
// from the nskn ns_section, we've set up the defaults here so we do get a
// stack trace dump during that short time. That shouldn't be enough data
// to cause a serious denial of service -- and is a huge win for debugging. /*
bool KnSetCrashHandler()
{
if (! SetCrashHandlerFilter( KnCrashHandler ) ) {
KnLog(KnLogError,"Could not set crash handler.\n");
return false ;
}

#if BE_NICE_TO_OUR_MODULE_WRITERS
/* Get the handle for this DLL or module */
HMODULE hMyModule = GetModuleHandle(NULL) ;

if (! AddCrashHandlerLimitModule( hMyModule )) {
KnLog(KnLogError,"Could not limit crash handling\n");
return false ;
}
#endif

return true;
}

#else

#ifdef __sun
#else
#include "google/coredumper.h"
#endif

namespace {
char* gCoreFile = "log/liveserver.core";
}


bool KnDumpCore()
{
if (gDumpAtAll)
{
const char *coreFile = gDumpLocation.length() ? gDumpLocation.c_str() : gCoreFile;
#ifdef __sun
KnLog(KnLogNotice,"Core dumping currently not supported on Solaris.");
#else
return 0 == WriteCoreDump(coreFile);
#endif
}

return true;
}

bool KnSetCrashHandler()
{
return true;
}

#endif


bool KnSetCrashInfo(const char* dumpLocation, const char* dumpLevel)
{
if (dumpLocation != NULL)
gDumpLocation = dumpLocation;

std::string level(dumpLevel);
if (level[0] == '0')
{
gDumpAtAll = false;
}
#ifdef WIN32
else if (level[0] == '1')
{
gDumpAtAll = true;
gMiniDumpType = MiniDumpNormal; // Just the stacks, ma'am
}
else if (level[0] == '2')
{
gDumpAtAll = true;
gMiniDumpType = MiniDumpWithDataSegs;
}
else if (level[0] == '3')
{
gDumpAtAll = true;
gMiniDumpType = MiniDumpWithFullMemory | MiniDumpWithHandleData ; // Hope this also contains Full Data
}
else
{
KnLog(KnLogNotice,"Unrecognized dump level '%s'; will set to 'just the stacks' level",level.c_str());
gDumpAtAll = true;
gMiniDumpType = MiniDumpNormal;
}
#else
else if (level[0] == '1')
{
gDumpAtAll = true;
}
else
{
KnLog(KnLogNotice,"Unrecognized dump level '%s'; setting dump level to 'true'",level.c_str());
gDumpAtAll = true;
}
#endif

return true;
}

// We wrote our own so we always get crash dumps the way we want 'em
// (and get the assertion in the log)
void KnAssert(char *exp, char *file, unsigned int line)
{
KnLog(KnLogBug,"%s @ %s:%u", exp, file, line);
// I wanted something reasonably portable without working too hard, so here's the best I could do on short
// notice. All attempts do it with kill (and any portable fault) were
// caught by nsd and didn't give core dumps.
char *p = 0;
++p;
strcpy(p,"assertion failed");
}

--- NEW FILE: kncrypt.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/kncrypt.h"


KnError KnCrypt(KnString &buf, const KnString &key, const KnString &salt)
{
char tmp[NS_ENCRYPT_BUFSIZE];

/*
* Copy the raw byte string into a KnString.
*/
buf = Ns_Encrypt(const_cast<char*>(key.c_str()), const_cast<char*>(salt.c_str()), tmp);

return KnErrorSuccess;
}

--- NEW FILE: knerror.cpp ---
/* Copyright 2004-2005 KnowNow, Inc */
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knerror.h"
#include "knutil/knmutex.h"
#include "knutil/knstring.h"
#include "knutil/knlog.h"
#include "knutil/simpletraits.h"
#include "knutil/simplemap.h"

namespace {

class KnErrorSingleton {
public:
static KnErrorSingleton& instance();

const KnString* get(KnError errEnum)
{
unsigned int err = static_cast<unsigned int>(errEnum);
const KnString *val = m_map.get(err);
if (0 == val) {
err &= 0xFF00;
val = m_map.get(err);
}

if (0 == val) {
KnString *unknown = new KnString("Unregistered error ");
unknown->printf("%u",err);
m_map.put(err,unknown);
val = m_map.get(err);
}
if (0 == val) {
Ns_Fatal("knerror: Can't map error %d",err);
}
return val;
}

private:
// Not implemented
KnErrorSingleton(const KnErrorSingleton &);
KnErrorSingleton &operator=(const KnErrorSingleton &);

// Implemented
KnErrorSingleton() ;

SimpleMap<unsigned int,const KnString *> m_map;
static KnErrorSingleton *m_instance ;
static KnMutex m_lock;
};

KnErrorSingleton* KnErrorSingleton::m_instance = 0 ;
KnMutex KnErrorSingleton::m_lock ;

KnErrorSingleton&
KnErrorSingleton::instance( void )
{
if ( 0 == m_instance )
{
KnMutex::KnLock guard(m_lock) ;
if ( 0 == m_instance )
{
m_instance = new KnErrorSingleton ;
if (0 == m_instance)
{
KnLog(KnLogFatal,"knerror: Can't allocate instance");
}
}
}
return *m_instance ;
}

KnErrorSingleton::KnErrorSingleton()
{
m_map.put(KnErrorSuccess, &KnStringConstants::success);
m_map.put(KnErrorUnauthorized, &KnStringConstants::unauthorized);
m_map.put(KnErrorForbidden, &KnStringConstants::forbidden);
m_map.put(KnErrorNotFound, &KnStringConstants::notspacefound);
m_map.put(KnErrorFailure, &KnStringConstants::failed);
/*
m_map.put(KnErrorNotFound_Event, new KnString("event not found"));
m_map.put(KnErrorNotFound_Route, new KnString("route not found"));
m_map.put(KnErrorNotFound_Topic, new KnString("topic not found"));
m_map.put(KnErrorNotFound_Journal, new KnString("journal not found"));
m_map.put(KnErrorNotFound_Parent, new KnString("parent topic not found"));
m_map.put(KnErrorNotFound_SubTopic, new KnString("subtopic not found"));
m_map.put(KnErrorNotFound_User, new KnString("user not found"));
m_map.put(KnErrorNotFound_Group, new KnString("group not found"));
*/
}

} // Namespace

const KnString& KnError_AsString( const KnError err )
{
return *KnErrorSingleton::instance().get(err);
}
const int KnError_AsHTTP(const KnError err) {
return err >> 8;
}

#if 0
KnError KnError_Register(unsigned int err, KnString string)
{
if (err > 65500 || err < 256) {
return KnError_OutOfRange;
}
return KnErrorSingleton::instance().put(err,string);
}
#endif

--- NEW FILE: knhash.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knhash.h"

#define ind32(value, bits) \
(static_cast<uint32_t>(value) << (bits))

#define ind64(value, bits) \
(static_cast<uint64_t>(value) << (bits))

#define mix32(a, b, c) \
{ \
a -= b; a -= c; a ^= (c >> 13); \
b -= c; b -= a; b ^= (a << 8); \
c -= a; c -= b; c ^= (b >> 13); \
a -= b; a -= c; a ^= (c >> 12); \
b -= c; b -= a; b ^= (a << 16); \
c -= a; c -= b; c ^= (b >> 5); \
a -= b; a -= c; a ^= (c >> 3); \
b -= c; b -= a; b ^= (a << 10); \
c -= a; c -= b; c ^= (b >> 15); \
}

#define mix64(a, b, c) \
{ \
a -= b; a -= c; a ^= (c >> 43); \
b -= c; b -= a; b ^= (a << 9); \
c -= a; c -= b; c ^= (b >> 8); \
a -= b; a -= c; a ^= (c >> 38); \
b -= c; b -= a; b ^= (a << 23); \
c -= a; c -= b; c ^= (b >> 5); \
a -= b; a -= c; a ^= (c >> 35); \
b -= c; b -= a; b ^= (a << 49); \
c -= a; c -= b; c ^= (b >> 11); \
a -= b; a -= c; a ^= (c >> 12); \
b -= c; b -= a; b ^= (a << 18); \
c -= a; c -= b; c ^= (b >> 22); \
}

extern "C"
{

uint32_t hash32(const void *buf, uint32_t len, uint32_t seed)
{
const uint8_t *p = static_cast<const uint8_t *>(buf);

uint32_t a = 0x9E3779B9;
uint32_t b = 0x9E3779B9;
uint32_t c = seed;
uint32_t d = len;

for (; d >= 12; d -= 12, p += 12)
{
a += ind32(p[ 0], 0) + ind32(p[ 1], 8) + ind32(p[ 2], 16) + ind32(p[ 3], 24);
b += ind32(p[ 4], 0) + ind32(p[ 5], 8) + ind32(p[ 6], 16) + ind32(p[ 7], 24);
c += ind32(p[ 8], 0) + ind32(p[ 9], 8) + ind32(p[10], 16) + ind32(p[11], 24);

mix32(a, b, c);
}

c += len;

/*
* Duff's Device lives! The first byte of c is reserved for the length.
*/
switch (d)
{
case 11: c += ind32(p[10], 24);
case 10: c += ind32(p[ 9], 16);
case 9: c += ind32(p[ 8], 8);
case 8: b += ind32(p[ 7], 24);
case 7: b += ind32(p[ 6], 16);
case 6: b += ind32(p[ 5], 8);
case 5: b += ind32(p[ 4], 0);
case 4: a += ind32(p[ 3], 24);
case 3: a += ind32(p[ 2], 16);
case 2: a += ind32(p[ 1], 8);
case 1: a += ind32(p[ 0], 0);
}

mix32(a, b, c);

return c;
}

uint64_t hash64(const void *buf, uint64_t len, uint64_t seed)
{
const uint8_t *p = static_cast<const uint8_t *>(buf);

uint64_t a = seed;
uint64_t b = seed;
uint64_t c = 0x9E3779B97F4A7C13ULL;
uint64_t d = len;

for (; d >= 24; d -= 24, p += 24)
{
a += ind64(p[ 0], 0) + ind64(p[ 1], 8) + ind64(p[ 2], 16) + ind64(p[ 3], 24) +
ind64(p[ 4], 32) + ind64(p[ 5], 40) + ind64(p[ 6], 48) + ind64(p[ 7], 56);
b += ind64(p[ 8], 0) + ind64(p[ 9], 8) + ind64(p[10], 16) + ind64(p[11], 24) +
ind64(p[12], 32) + ind64(p[13], 40) + ind64(p[14], 48) + ind64(p[15], 56);
c += ind64(p[16], 0) + ind64(p[17], 8) + ind64(p[18], 16) + ind64(p[19], 24) +
ind64(p[20], 32) + ind64(p[21], 40) + ind64(p[22], 48) + ind64(p[23], 56);

mix64(a, b, c);
}

c += len;

/*
* Duff's Device lives! The first byte of c is reserved for the length.
*/
switch (d)
{
case 23: c += ind64(p[22], 56);
case 22: c += ind64(p[21], 48);
case 21: c += ind64(p[20], 40);
case 20: c += ind64(p[19], 32);
case 19: c += ind64(p[18], 24);
case 18: c += ind64(p[17], 16);
case 17: c += ind64(p[16], 8);
case 16: b += ind64(p[15], 56);
case 15: b += ind64(p[14], 48);
case 14: b += ind64(p[13], 40);
case 13: b += ind64(p[12], 32);
case 12: b += ind64(p[11], 24);
case 11: b += ind64(p[10], 16);
case 10: b += ind64(p[ 9], 8);
case 9: b += ind64(p[ 8], 0);
case 8: a += ind64(p[ 7], 56);
case 7: a += ind64(p[ 6], 48);
case 6: a += ind64(p[ 5], 40);
case 5: a += ind64(p[ 4], 32);
case 4: a += ind64(p[ 3], 24);
case 3: a += ind64(p[ 2], 16);
case 2: a += ind64(p[ 1], 8);
case 1: a += ind64(p[ 0], 0);
}

mix64(a, b, c);

return c;
}

} // extern "C"

--- NEW FILE: knlog.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/kntypes.h"
#include "knutil/knlog.h"
#include <cstdarg>


KnError KnLog(KnLogSeverity severity, const KnString &msg)
{
/*
* Reuse existing interface.
*/
return KnLog(severity, "%.*s", (int)msg.length(), msg.c_str());
}

KnError KnLog(KnLogSeverity severity, const char *fmt, ...)
{
va_list ap;

/*
* Grab the arguments off the stack.
*/
va_start(ap, fmt);

/*
* Note the C-style cast for converting between unrelated enums.
*/
Ns_ServerLog(Ns_LogSeverity(severity), const_cast<char*>(fmt), &ap);

/*
* Restore the stack.
*/
va_end(ap);

return KnErrorSuccess;
}

--- NEW FILE: knmarkuputil.cpp ---
//////////////////////////////////////////////////////
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
// knmarkuputil.cpp
//
// (c)2002-2006, KnowNow, Inc. All Rights Reserved.

#include "knexportlibraryknutilmodule.h"
#include "knutil/knmarkuputil.h"
#include "knutil/knlog.h"

template class EXPORT_LIBRARY_KNUTIL SimpleMap<char*, char*>;


KnMarkupUtil::KnMarkupUtil()
{
buildHtlmEntity();
buildHtlmTags();
}

KnMarkupUtil::~KnMarkupUtil()
{
}

void KnMarkupUtil::buildHtlmEntity()
{
// http://www.w3schools.com/tags/ref_entities.asp
// http://www.utoronto.ca/webdocs/HTMLdocs/NewHTML/iso_table.html

entityMap.put("#32"," ");
entityMap.put("#0x0020"," ");
entityMap.put("nbsp"," ");

entityMap.put("#33","!");
entityMap.put("#0x0021","!");

entityMap.put("#34","\"");
entityMap.put("#0x0022","\"");
entityMap.put("quot","\"");

entityMap.put("#35","#");
entityMap.put("#0x0023","#");

entityMap.put("#36","$");
entityMap.put("#0x0024","$");

entityMap.put("#37","%");
entityMap.put("#0x0025","%");

entityMap.put("#38","&");
entityMap.put("#0x0026","&");
entityMap.put("amp","&");

entityMap.put("#39","'");
entityMap.put("#0x0027","'");

entityMap.put("#40","(");
entityMap.put("#0x0028","(");

entityMap.put("#41",")");
entityMap.put("#0x0029",")");

entityMap.put("#42","*");
entityMap.put("#0x002a","*");

entityMap.put("#43","+");
entityMap.put("#0x002b","+");

entityMap.put("#44",",");
entityMap.put("#0x002c",",");

entityMap.put("#45","-");
entityMap.put("#0x002d","-");

entityMap.put("#46",".");
entityMap.put("#0x002e",".");

entityMap.put("#47","/");
entityMap.put("#0x002f","/");

entityMap.put("#48","0");
entityMap.put("#0x0030","0");

entityMap.put("#49","1");
entityMap.put("#0x0031","1");

entityMap.put("#50","2");
entityMap.put("#0x0032","2");

entityMap.put("#51","3");
entityMap.put("#0x0033","3");

entityMap.put("#52","4");
entityMap.put("#0x0034","4");

entityMap.put("#53","5");
entityMap.put("#0x0035","5");

entityMap.put("#54","6");
entityMap.put("#0x0036","6");

entityMap.put("#55","7");
entityMap.put("#0x0037","7");

entityMap.put("#56","8");
entityMap.put("#0x0038","8");

entityMap.put("#57","9");
entityMap.put("#0x0039","9");

entityMap.put("#58",":");
entityMap.put("#0x003a",":");

entityMap.put("#59",";");
entityMap.put("#0x003b",";");

entityMap.put("#60","<");
entityMap.put("#0x003c","<");
entityMap.put("lt","<");

entityMap.put("#61","=");
entityMap.put("#0x003d","=");

entityMap.put("#62",">");
entityMap.put("#0x003e",">");
entityMap.put("gt",">");

entityMap.put("#63","?");
entityMap.put("#0x003f","?");

entityMap.put("#64","@");
entityMap.put("#0x0040","@");

entityMap.put("#65","A");
entityMap.put("#0x0041","A");

entityMap.put("#66","B");
entityMap.put("#0x0042","B");

entityMap.put("#67","C");
entityMap.put("#0x0043","C");

entityMap.put("#68","D");
entityMap.put("#0x0044","D");

entityMap.put("#69","E");
entityMap.put("#0x0045","E");

entityMap.put("#70","F");
entityMap.put("#0x0046","F");

entityMap.put("#71","G");
entityMap.put("#0x0047","G");

entityMap.put("#72","H");
entityMap.put("#0x0048","H");

entityMap.put("#73","I");
entityMap.put("#0x0049","I");

entityMap.put("#74","J");
entityMap.put("#0x004a","J");

entityMap.put("#75","K");
entityMap.put("#0x004b","K");

entityMap.put("#76","L");
entityMap.put("#0x004c","L");

entityMap.put("#77","M");
entityMap.put("#0x004d","M");

entityMap.put("#78","N");
entityMap.put("#0x004e","N");

entityMap.put("#79","O");
entityMap.put("#0x004f","O");

entityMap.put("#80","P");
entityMap.put("#0x0050","P");

entityMap.put("#81","Q");
entityMap.put("#0x0051","Q");

entityMap.put("#82","R");
entityMap.put("#0x0052","R");

entityMap.put("#83","S");
entityMap.put("#0x0053","S");

entityMap.put("#84","T");
entityMap.put("#0x0054","T");

entityMap.put("#85","U");
entityMap.put("#0x0055","U");

entityMap.put("#86","V");
entityMap.put("#0x0056","V");

entityMap.put("#87","W");
entityMap.put("#0x0057","W");

entityMap.put("#88","X");
entityMap.put("#0x0058","X");

entityMap.put("#89","Y");
entityMap.put("#0x0059","Y");

entityMap.put("#90","Z");
entityMap.put("#0x005a","Z");

entityMap.put("#91","[");
entityMap.put("#0x005b","[");

entityMap.put("#92","\\");
entityMap.put("#0x005c","\\");

entityMap.put("#93","]");
entityMap.put("#0x005d","]");

entityMap.put("#94","^");
entityMap.put("#0x005e","^");

entityMap.put("#95","_");
entityMap.put("#0x005e","_");

entityMap.put("#96","`");
entityMap.put("#0x0060","`");

entityMap.put("#97","a");
entityMap.put("#0x0061","a");

entityMap.put("#98","b");
entityMap.put("#0x0062","b");

entityMap.put("#99","c");
entityMap.put("#0x0063","c");

entityMap.put("#100","d");
entityMap.put("#0x0064","d");

entityMap.put("#101","e");
entityMap.put("#0x0065","e");

entityMap.put("#102","f");
entityMap.put("#0x0066","f");

entityMap.put("#103","g");
entityMap.put("#0x0067","g");

entityMap.put("#104","h");
entityMap.put("#0x0068","h");

entityMap.put("#105","i");
entityMap.put("#0x0069","i");

entityMap.put("#106","j");
entityMap.put("#0x006a","j");

entityMap.put("#107","k");
entityMap.put("#0x006b","k");

entityMap.put("#108","l");
entityMap.put("#0x006c","l");

entityMap.put("#109","m");
entityMap.put("#0x006d","m");

entityMap.put("#110","n");
entityMap.put("#0x006e","n");

entityMap.put("#111","o");
entityMap.put("#0x006f","o");

entityMap.put("#112","p");
entityMap.put("#0x0070","p");

entityMap.put("#113","q");
entityMap.put("#0x0071","q");

entityMap.put("#114","r");
entityMap.put("#0x0072","r");

entityMap.put("#115","s");
entityMap.put("#0x0073","s");

entityMap.put("#116","t");
entityMap.put("#0x0074","t");

entityMap.put("#117","u");
entityMap.put("#0x0075","u");

entityMap.put("#118","v");
entityMap.put("#0x0076","v");

entityMap.put("#119","w");
entityMap.put("#0x0077","w");

entityMap.put("#120","x");
entityMap.put("#0x0078","x");

entityMap.put("#121","y");
entityMap.put("#0x0079","y");

entityMap.put("#122","z");
entityMap.put("#0x007a","z");

entityMap.put("#123","{");
entityMap.put("#0x007b","{");

entityMap.put("#124","|");
entityMap.put("#0x007c","|");

entityMap.put("#125","}");
entityMap.put("#0x007d","}");

entityMap.put("#126","~");
entityMap.put("#0x007e","~");

// ---------------------------- END ASCII -----------------------------

entityMap.put("#160"," ");
entityMap.put("#0x00a0"," ");
entityMap.put("nbsp"," ");

entityMap.put("#162","¢");
entityMap.put("#0x00a2","¢");
entityMap.put("cent","¢");

entityMap.put("#163","£");
entityMap.put("#0x00a3","£");
entityMap.put("pound","£");

entityMap.put("#164","¤");
entityMap.put("#0x00a4","¤");
entityMap.put("curren","¤");

entityMap.put("#165","Â¥");
entityMap.put("#0x00a5","Â¥");
entityMap.put("yen","Â¥");

entityMap.put("#171","«");
entityMap.put("#0x00ab","«");
entityMap.put("laquo","«");

entityMap.put("#187","»");
entityMap.put("#0x00bb","»");
entityMap.put("raquo","»");
}

void KnMarkupUtil::buildHtlmTags()
{
tagMap.put("br","\n"); // HTML <br>
tagMap.put("br/","\n"); // XHTML <br/>
}


void KnMarkupUtil::stripTags(const KnString& source, KnString& result)
{
std::string temp;
for (const char * p = source.c_str(); *p != 0; p++)
{
if (*p == '<')
for (p++; *p != 0 && *p != '>'; p++);
else
temp.append(p,1);
}

result = temp.c_str();
}

bool KnMarkupUtil::expandTagsAndStrip(const KnString& source, KnString& result)
{
std::string temp;
bool ok = true;

for (const char * p = source.c_str(); *p != 0; p++)
{
if (*p == '<')
{
std::string key;
for (p++; *p != 0 && *p != '>'; p++)
{
key.append(p,1);
}

if(*p == '>') // we found a valid entity, search it in the tag map
{
char* value = tagMap.get((char *const)key.c_str());
if(value != NULL)
{
temp.append(value, 1);
}
else
{
// just ignore it, strip it
/*
ok = false;
temp->append("<",1);
temp->append(key.c_str(), key.length());
temp->append(">",1);
//KnLog(KnLogDev,"Tag:'%s' not recognized, it will not expand", key.c_str());
*/
}
}
else // we got \0
{
if (key.length() > 0)
{
temp.append("<",1);
temp.append(key.c_str(), key.length());
KnLog(KnLogDebug,"Tag:'%s' not '>' terminated, it will not expand", key.c_str());
}
result = temp.c_str();
return false;
}

}
else
{
temp.append(p,1);
}
}

result = temp.c_str();
return ok;
}

bool KnMarkupUtil::expandHtmlEntities(const KnString& source, KnString& result)
{
std::string temp;
bool ok = true;

for (const char * p = source.c_str(); *p != 0; p++)
{
if (*p == '&')
{
std::string key;
for (p++; *p != 0 && *p != ';'; p++)
{
key.append(p,1);
}

if(*p == ';') // we found a valid entity, search it in the entity map
{
toLowerCase(key);
char* value = entityMap.get((char *const)key.c_str());
if(value != NULL)
{
temp.append(value, 1);
}
else
{
ok = false;
temp.append("&",1);
temp.append(key.c_str(), key.length());
temp.append(";",1);
KnLog(KnLogDebug,"Entity:'%s' not recognized, it will not expand", key.c_str());
}
}
else // we got \0
{
if(key.length() > 0)
{
temp.append("&",1);
temp.append(key.c_str(), key.length());
KnLog(KnLogDebug,"Entity:'%s' not ';' terminated, it will not expand", key.c_str());
}

result = temp.c_str();
return false;
}
}
else
{
temp.append(p,1);
}
}

result = temp.c_str();
return ok;
}

bool KnMarkupUtil::expandTagsAndEntities(const KnString& source, KnString& result)
{
bool ok1, ok2;
KnString temp;

ok1 = expandTagsAndStrip(source, temp);
ok2 = expandHtmlEntities(temp, result);

return ok1 && ok2;
}

bool KnMarkupUtil::stripTagsAndExpandEntities(const KnString& source, KnString& result)
{
bool ok;
KnString temp;

stripTags(source, temp);
ok = expandHtmlEntities(temp, result);

return ok;
}

/*
bool KnMarkupUtil::renderXmlHtml(const KnString* source, KnString* result)
{
XALAN_USING_STD(istrstream)
XALAN_USING_STD(ostrstream)

int theResult = -1;

KnString theXSLTSource("<?xml version=\"1.0\" encoding=\"utf-8\"?> \
<!DOCTYPE xsl:stylesheet [<!ENTITY nbsp \"&#160;\">]> \
<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\"> \
<xsl:output method=\"text\"> \
</xsl:output> \
</xsl:stylesheet>");

KnString theXMLSource("<strip>");
theXMLSource.append(source->c_str(), source->length());
theXMLSource.append("</strip>", 8);

XalanTransformer theXalanTransformer;

istrstream theXMLStream(theXMLSource.c_str(), theXMLSource.length());
istrstream theXSLStream(theXSLTSource.c_str(), theXSLTSource.length());

ostrstream theOutputStream;

XALAN_USING_XALAN(XSLTInputSource)

XSLTInputSource xmlSource(theXMLStream);
XSLTInputSource xslSource(theXSLStream);

// Do the transform.
theResult = theXalanTransformer.transform(xmlSource, xslSource, theOutputStream);

if (theResult != 0)
{
KnLog(KnLogError, "StreamTransform Error: %s\n" ,theXalanTransformer.getLastError());
return false;
}
else
{
KnString strout(theOutputStream.str(), theOutputStream.pcount());
KnString* temp = &strout;
stripTags(temp, result);
return true;
}
}
*/

--- NEW FILE: knmemory.cpp ---
/*
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
* Copyright (c) 2003 KnowNow, Inc., All Rights Reserved
*/
#include "knexportlibraryknutilmodule.h"
#include "knutil/kntypes.h"
#include "knutil/atomicint.h"
#include "knutil/knmemory.h"

#ifdef WIN32
#include <winbase.h>
#else
#include <sys/resource.h>
#include <unistd.h>
#endif


/**
* File containing C++ wrappers for the AOLServer allocation & free
* routines.
*
* Also has a very elementary leak detector: it counts allocations
* rather than using a boolean to determine whether to print its trace
* message. Because it should be atomic, this
* will slow down the memory allocation time of the router. Let's
* hope that's not where our performance bottlenecks are.
*
* If it is, we'll wrap this in some preprocessor magic.
*/

EXPORT_LIBRARY_KNUTIL AtomicInt nAllocs = 0;

void KnMemory::increment() { ++nAllocs; }
void KnMemory::decrement() { --nAllocs; }

size_t
KnMemory::getBalance()
{
return nAllocs ;
}

void
KnMemory::logStatistics( KnLogSeverity severity )
{
KnLog(severity,"Allocations that were not deleted: %lu",
static_cast<unsigned long>(nAllocs));
}

size_t
KnMemory::getMemoryAvail()
{
size_t nAvail = 1234567890; // Suspicious value

#ifdef WIN32
// Assumes all of the system is available to us.

MEMORYSTATUS status ;

GlobalMemoryStatus (&status);

nAvail = status.dwAvailVirtual;

#elif defined(__linux)
// Two approaches could be used. We could try the whole system
// approach like Windows (querying /proc/meminfo), but because the
// Linux 2.6 vm system keeps the vmsize as close to the physical
// resources as possible, this doesn't work [shows the memory as always
// over-used].
//
// Instead, we need to use /proc/<pid>/stat and try to work with what
// it returns.

static bool bProcStatWorks = true ;

pid_t pid = getpid();

KnString statFile;
statFile.printf("/proc/%d/stat",pid);

if (bProcStatWorks)
{
FILE *pFile = fopen(statFile.c_str(),"r");

if (!pFile)
{
KnLog(KnLogNotice,"KnMemory: Can't determine memory available: Can't read /proc/meminfo");
bProcStatWorks = false ;
}
else
{
char procStatBuf[ BUFSIZ ] ;

if (NULL != fgets(procStatBuf,BUFSIZ,pFile))
{
unsigned long vsize ;
unsigned long rlim ;

int fields =
sscanf(
procStatBuf,
"%*d "
"(%*[^)]) " // Spaces are allowed in commands
"%*c "
"%*d "

"%*d "
"%*d "
"%*d "
"%*d "

"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "

"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*d " // "%*ld "

"%*d " // "%*ld "
"%*d " // "%*ld "
"%*d " // "%*ld "
"%*d " // "%*ld "

"%*d " // "%*ld "
"%*u " // "%*lu "
"%lu "
"%*d " // "%*ld "

"%lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "

"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "

"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "

"%*u " // "%*lu "
"%*d "
"%*d"
// ,pid
// ,comm
// ,state
// ,ppid

// ,pgrp
// ,session
// ,tty_nr
// ,tpgid

// ,flags
// ,minflt
// ,cminflt
// ,majflt

// ,cmajflt
// ,utime
// ,stime
// ,cutime

// ,cstime
// ,priority
// ,nice
// ,0

// ,itrealvalue
// ,starttime
,&vsize
// ,rss

,&rlim
// ,startcode
// ,endcode
// ,startstack

// ,kstkesp
// ,kstkeip
// ,signal
// ,blocked

// ,sigignore
// ,sigcatch
// ,wchan
// ,nswap

// ,cnswap
// ,exit_signal
// ,processor
);

if (2 == fields)
{
// I just don't believe that we really work above the signed/unsigned
// line yet, so I'm going forcing the rlim down to that limit
// artificially. If it turns out we give specious or ugly
// warnings and run just fine at 3G of a 32-bit space, remove
// this if.
if (rlim > (ULONG_MAX / 2))
{
rlim = ULONG_MAX / 2;
}
nAvail = rlim - vsize;
}
else
{
bProcStatWorks = false;
KnLog(KnLogNotice,"KnMemory: Can't determine memory available: sscanf of line from '%s' returned %d fields from '%s'",
statFile.c_str(),
fields,
procStatBuf
);
}
}
else
{
bProcStatWorks = false;
KnLog(KnLogNotice,"KnMemory: Can't determine memory available: no information in '%s'",statFile.c_str());
}
fclose(pFile);
}
}
#else
// It'd be nice if getrusage actually returned something
// comparable to RLIMIT_DATA. It doesn't; I though ru_idrss might
// work, but (a) no OS we support sets that correctly anyway, and
// (b) it's measured in pages, not bytes, so things get too complicated
// too fast. Let's hope the old assumption that heap is "last" in
// address space works.
struct rlimit rlim ;

if (0 == getrlimit( RLIMIT_DATA, &rlim ) )
{
if (rlim.rlim_cur == RLIM_INFINITY)
{
static bool bWarned = false ;
if (!bWarned)
{
KnLog(KnLogNotice,"getMemoryAvail: RLIMIT_DATA is infinity?!");
bWarned = true ;
}
}

// c_cast<unsigned long>(void*)
nAvail = rlim.rlim_cur - (rlim_t)(sbrk(0));
}
#endif

return nAvail ;
}

--- NEW FILE: knmutex.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knmutex.h"

/*
* Need C cast to convert from void ** to Ns_Mutex *.
*/

KnMutex::KnMutex() :
m_impl(0)
{
Ns_MutexInit((Ns_Mutex *)&m_impl);
}

KnMutex::~KnMutex()
{
Ns_MutexDestroy((Ns_Mutex *)&m_impl);
}

bool KnMutex::lock()
{
Ns_MutexLock((Ns_Mutex *)&m_impl);

return true;
}

bool KnMutex::trylock()
{
return Ns_MutexTryLock((Ns_Mutex *)&m_impl) == NS_OK;
}

bool KnMutex::unlock()
{
Ns_MutexUnlock((Ns_Mutex *)&m_impl);

return true;
}

KnMutex::KnLock::KnLock(KnMutex &mutex, bool blocking) :
m_mutex(mutex),
m_owner(blocking ? m_mutex.lock() : m_mutex.trylock())
{
}

KnMutex::KnLock::~KnLock()
{
if (m_owner)
{
m_mutex.unlock();
}
}

bool KnMutex::KnLock::owner() const
{
return m_owner;
}

--- NEW FILE: knnetwork.cpp ---
/* Copyright 2008 KnowNow, Inc */
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
[...1505 lines suppressed...]

KnError
KnNetwork::formatTopicLocation(
KnString& location,
const KnString& urlSpace
) {
return KnNetworkImpl::instance()->formatTopicLocation(location,urlSpace);
}

KnError
KnNetwork::formatNonNetworkRelativeRef(KnString& location,size_t inputPfxLen)
{
return KnNetworkImpl::instance()->formatNonNetworkRelativeRef(location,inputPfxLen);
}

KnError
KnNetwork::formatTopicRelativeRef(KnString& location,size_t inputPfxLen)
{
return KnNetworkImpl::instance()->formatTopicRelativeRef(location,inputPfxLen);
}

--- NEW FILE: knnewdelete.cpp ---
#define KNMODULE knutil
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knnewdelete.h"

--- NEW FILE: knpattern.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/

#include "knutil/knpattern.h"

// ---------------------------------------------------------
// KnMatcher
// ---------------------------------------------------------

KnMatcher::KnMatcher(const char* string)
{
m_obj = Tcl_NewStringObj((char*)string, strlen(string));
}

KnMatcher::~KnMatcher()
{
Tcl_DecrRefCount(m_obj);
}

Tcl_Obj* KnMatcher::getTclObj()
{
return m_obj;
}


// ---------------------------------------------------------
// KnPattern
// ---------------------------------------------------------

KnPattern::KnPattern()
{
}

KnPattern::KnPattern( const KnString& name )
{
m_name = name;
}

bool
KnPattern::setRegexPattern(const KnString& pattern)
{
return setRegexPattern(pattern.c_str());
}

bool
KnPattern::setRegexPattern(const char* pattern)
{
m_regexPattern = pattern;
return m_regex.setRegexPattern(pattern);
}

const KnString&
KnPattern::getName() const
{
return m_name;
}

const KnString&
KnPattern::getRegexPattern() const
{
return m_regexPattern;
}

const KnRegEx&
KnPattern::getRegexMatcher() const
{
return m_regex;
}

bool
KnPattern::isValidRegex() const
{
return m_regex.isValid();
}

bool
KnPattern::match(const char* string) const
{
return m_regex.match(string);
}

bool
KnPattern::match(KnMatcher& matchObj) const
{
return m_regex.match(matchObj.getTclObj());
}

int KnPattern::getSubCount() const
{
return 1; //TODO determinte the number of subexpressions
}

bool KnPattern::matchSubs(const char *str, KnSet& result)
{
bool ok = m_regex.matchSubs(str);

if(!ok)
return false;

char* mstart;
int mlen;

KnString name;
std::string matched;
int subsCount = getSubCount();
for(int i = 0; i < subsCount; i++)
{
m_regex.getSubRange(0, mstart, mlen);

matched = "";
if(mlen)
matched.append(mstart,mlen);

name.printf("%d",i);

result.put(name, matched.c_str());
}

return true;
}

// ---------------------------------------------------------
// KnUrlPattern
// ---------------------------------------------------------

namespace {
const KnString AnyPart("[^ ]*");
const KnString AnyUrlComponent("[^/ ]*");
const KnString AndSubTree("(?:/[^ ]*){0,1}");
const KnString Anywhere("(?:(?:/[^ ]*/)|/)");
const KnString SubTreeOnly("/[^ ]*");

static const char hexdigits[22] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'
};
};


KnUrlPattern::KnUrlPattern( const KnString& name)
: KnPattern(name),
m_filecards(0),
m_pathcards(0),
m_leaders(0),
m_trailers(0),
m_subtopics(0)
{
}

KnUrlPattern::KnUrlPattern( const KnString& name, const KnString& urlPattern)
: KnPattern(name),
m_filecards(0),
m_pathcards(0),
m_leaders(0),
m_trailers(0),
m_subtopics(0)
{
makePathRegex(m_regexPattern,urlPattern);
setRegexPattern(m_regexPattern.c_str());
}

// Regex building routines
// -----------------------

// We convert DOS glob expressions (on non-URL parts) and WS-Topics
// FullTopicExpressions (sans alternation) into a regular expression.

// Escape characters that aren't part of the regexes we're building
bool
KnUrlPattern::escape(KnString& putItHere, char aChar)
{
switch(aChar)
{
case '\\':
case '*':
case '{':
case '}':
case '[':
case ']':
case '(':
case ')':
case '.':
case '^':
case '$':
case '?':
putItHere += "\\";
break;
default:
break;
}
putItHere.append(&aChar,1);
return true;
}

// For parts of the pattern that can't take '**', treat it as a pair of '*'
// (which is identical to one *)
//
// Doesn't count against leaders & trailers.
bool
KnUrlPattern::makeNoPathRegex(KnString& putItHere, const KnString& part)
{
bool bOK = true ;
bool bEscape = false ;
int ix = 0;
for (; ix < (int)part.length() ; ++ix)
{
if (part[ix] == '\\')
{
bEscape = true ;
continue ;
}
else if (!bEscape && part[ix] == '*')
{
putItHere += AnyPart;
m_filecards++;
}
else
{
bOK = escape(putItHere,part[ix]); // Not sure this is right
}
bEscape = false ;
}
if (ix == 0)
{
putItHere += AnyPart ;
m_filecards++;
}

#if 0
//KNPDBG(KnLog(KnLogDev,"knperm: Escaping %s is %s",part.c_str(),putItHere.c_str()));
#endif
return true ;
}


// Pattern taken from WS-Topics, Draft 1.2 of 6/17/2004, lines 446 - 477
// Only oddity is '//.'; that document specifies that this at the end of
// a string means AndSubtree. So we go out of our way to delete the . if
// it's at the end of the string.

// Getting the counts right takes an embedded state machine. Idea is:
// foreach component
// if (no wildcards yet) { ++leaders, ++trailers }
// else if (component has a wildcard) {++<wildcardtype>, trailers = 0}
// else { ++trailers }
// we recognize a component when we reach a '/' other than at the
// beginning of the parse, and at the end of the parse, presuming
// the last thing was not a /.
bool
KnUrlPattern::makePathRegex(KnString& putItHere, const KnString& part)
{
enum { normal, oneslash, twoslash, withdot } state = normal ;
bool bOK = true ;
KnUrlPattern::ComponentCounter counter(*this) ;

int ix = 0 ;
bool subTopicCounted = false;
for (; ix < (int)part.length() ; ++ix)
{
switch (state)
{
default: // Just to make compilers happy
case normal:
switch(part[ix])
{
case '/':
if (ix != 0)
{
// ends a component, so count it.
counter.countComponent() ;
}
state = oneslash;
subTopicCounted = false;
break ;
case '*':
counter.countFileCard() ;
putItHere += AnyUrlComponent ;
break ;
default:
if (!subTopicCounted)
{
counter.countSubTopics();
subTopicCounted = true;
}
bOK = escape(putItHere,part[ix]);
}
break ;

case oneslash:
switch(part[ix])
{
case '/':
// It's definitely a wildcard, now to recognize it.
counter.countPathCard() ;
state = twoslash;
break;
case '*':
// This is the case foo/*..., so we're in a component
// and want a filecard. And we're back in normal state.
//
// So what happens with foo/*//. ? Should mean there
// must be one complete component; I think it does.
counter.countFileCard() ;
putItHere += "/";
putItHere += AnyUrlComponent ;
state = normal ;
break ;
default:
putItHere += "/" ;
bOK = escape(putItHere,part[ix]);
state = normal ;
break ;
}
break ;

case twoslash:
switch(part[ix])
{
case '.':
putItHere += AndSubTree ;
state = withdot;
break;
case '*':
putItHere += SubTreeOnly ;
counter.countComponent() ;
state = normal ;
break ;
default:
putItHere += Anywhere;
bOK = escape(putItHere,part[ix]);
counter.countComponent() ;
state = normal ;
break ;
}
break ;

case withdot:
// Well, we're still in the string, so include the .
putItHere += ".";
bOK = escape(putItHere,part[ix]);
// Didn't know whether to count even the first slash till now.
counter.countComponent() ;
state = normal;
break ;
}
}
switch (state)
{
case withdot: // Trash the dot; we already output the pattern.
case normal:
counter.countComponent();
break;
case oneslash:
putItHere += "/";
break;
case twoslash:
putItHere += AndSubTree ;
break ;
}

if (ix == 0)
{
putItHere += AndSubTree ;
counter.countPathCard() ;
}

#if 0
//KNPDBG(KnLog(KnLogDev,"knperm: Escaping %s is %s",part.c_str(),putItHere.c_str()));
#endif
return true;
}

// 1. Number of subtopics (more first)
// 2. Number of wildcards (fewest first)
// 3. Number of file wildcards vs. path wildcards (files beat paths)
// 4. Number of trailing non-path-wildcard components (more are less)
// 5. Number of leading non-path-wildcard components (more are less)
// 6. Alphabetic order of the pattern name.
// 7. Alphabetic order of the components (the regex)
bool
KnUrlPattern::lessSpecificThan(const KnUrlPattern& rhs) const
{
//KnLog(KnLogDev,"knperm: Comparing %s to %s (%s <? %s)",m_name.c_str(), rhs.m_name.c_str(), m_regexPattern.c_str(), rhs.m_regexPattern.c_str());

// more are less
int less = (rhs.m_subtopics - m_subtopics);
if (less == 0) {
less = (m_filecards + m_pathcards)-(rhs.m_filecards + rhs.m_pathcards); // fewer are less

if (less == 0)
{
less = rhs.m_filecards - m_filecards; // more are less

if (less == 0)
{
less = rhs.m_trailers - m_trailers; // more are less

if (less == 0)
{
less = rhs.m_leaders - m_leaders; // more are less

if (less == 0)
{
if (m_name == rhs.m_name)
{
less = (m_regexPattern < rhs.m_regexPattern) ? -1 : +1;
//KNPDBG(KnLog(KnLogDev,"knperm: Regex alphabetic decided %d", less));
}
else
{
less = (m_name < rhs.m_name) ? -1 : +1;
//KNPDBG(KnLog(KnLogDev,"knperm: Pattern name alphabetics decided %d", less));
}
}
else
{
//KNPDBG(KnLog(KnLogDev,"knperm: Comparison on leaders decided %d",less));
}
}
else
{
//KNPDBG(KnLog(KnLogDev,"knperm: Comparison on trailers decided %d",less));
}
}
else
{
///KNPDBG(KnLog(KnLogDev,"knperm: Comparison on filecards decided %d",less));
}
}
else
{
//KNPDBG(KnLog(KnLogDev,"knperm: Comparison on total wildcards decided %d",less));
}
}
else
{
//KNPDBG(KnLog(KnLogDev,"knperm: Comparison on total subtopics decided %d",less));
}

return less < 0;
}
--- NEW FILE: knperfstatistics.cpp ---
/* Copyright 2004 KnowNow, Inc., Sunnyvale CA 94089 USA. All Rights Reserved */
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knset.h"
#include "knutil/knstring.h"
#include "knutil/knperfstatistics.h"

// ---------------------------------------------------------------
// KnPerfStatistic
// ---------------------------------------------------------------
KnPerfStatistic *KnPerfStatistic::allStatistics_[ALLSTATISTICSMAX];
int KnPerfStatistic::allStatisticsCount_ = 0;
KnString KnPerfStatistic::systemName_;

namespace {
// This rolls over every 24.27 259 259 ... days; it's the largest
// decimal milliseconds that will work nicely in 2^31 bits. We tried
// just relying on roll-over truncating and staying positive, but that
// didn't work. I suspect the better idea is to do everything unsigned,
// but this solution stays inside the file (no change in API). We should change the
// clients to use perftimer instead of long in case we need to move to, e.g.,
// usec timings.
inline perftimer perftimerNormalize( perftimer secs, perftimer msecs ) {
return ( (secs & 0x001FFFFF) * 1000) + msecs ;
};
inline perftimer perftimerNow() {
perftimer perftime;
#ifdef WIN32
_timeb timerData;
_ftime(&timerData);
perftime = perftimerNormalize(timerData.time,timerData.millitm);
#else
struct timeval timerData;
gettimeofday(&timerData,0);
timerData.tv_usec /= 1000; // milliseconds
perftime = perftimerNormalize(timerData.tv_sec,timerData.tv_usec);
#endif
return perftime;
}
inline perftimer perftimerRoll(perftimer value) {
// Presume our compilers are bright enough to figure this out
if (value < 0)
{
value += 0x7FFFFFFF;
++value;
}
return value;
}
}

int KnPerfStatistic::gCategoryPerformance = 60000;
int KnPerfStatistic::gCategoryPerformanceInSeconds = 1200000;
bool KnPerfStatistic::gLogAssertPerformanceOutOfScale = false;

bool KnPerfStatistic::gStatisticsRoll = false;
int KnPerfStatistic::gMaxBackupStatistics = 100;
int KnPerfStatistic::gClampStatisticsRoll = (1000 * 60 * 60 * 24 * 7); // 1 week

KnPerfStatistic::KnPerfStatistic(const KnString & name,
const KnString& displayName,
KnCategoryType cat) :
knId_(name),
displayName_(displayName),
value_(0),
category_(cat),
total_(0L),
min_(LONG_MAX),
max_(0L),
unit_(0),
histogram_(0),
start_(0L)
{
switch(cat)
{
case KnCategory_Performance:
histogram_ = new Histogram(0, gCategoryPerformance); // Allow up to a minute without asserting
insert(this);
break;

case KnCategory_PerformanceInSeconds:
histogram_ = new Histogram(0, gCategoryPerformanceInSeconds); // Easier to explain 20 minutes than 1000 seconds
insert(this);
break;

default:
KN_ASSERT(!"knperfstatistic: Unregistered statistics category. See KnPerfStatistic::KnPerfStatistic");
break;
}
}

void
KnPerfStatistic::configure(int a, int b)
{
gCategoryPerformance = a;
gCategoryPerformanceInSeconds = b;
}

void
KnPerfStatistic::configureStatisticsRoll(bool roll, int backups, int clamp)
{
gStatisticsRoll = roll;
gMaxBackupStatistics = backups;
gClampStatisticsRoll = clamp * 1000; // clamp is in seconds
}

void
KnPerfStatistic::setAssertPerformanceOutOfScale(bool value)
{
gLogAssertPerformanceOutOfScale = value;
}

bool
KnPerfStatistic::getAssertPerformanceOutOfScale()
{
return gLogAssertPerformanceOutOfScale;
}

KnPerfStatistic::~KnPerfStatistic()
{
if (histogram_)
delete histogram_;
histogram_ = 0;
}

KnError
KnPerfStatistic::incr( perftimer amount )
{
KnLog(KnLogDebug,"knstatistics: Adjusting %s by %ld",knId_.c_str(),amount);
value_ += amount;
if (value_ > max_)
max_ = value_;
if (value_ < min_)
min_ = value_;

return KnErrorSuccess ;
}

KnError
KnPerfStatistic::set(perftimer amount)
{
KnLog(KnLogDebug,"knstatistics: Adjusting %s to %ld",knId_.c_str(),amount);
value_ = amount;
if (value_ > max_)
max_ = value_;
if (value_ < min_)
min_ = value_;
return KnErrorSuccess;
}

perftimer
KnPerfStatistic::begin()
{
start_ = perftimerNow();
#ifdef PERFSTATISTICS_DEBUG
KnLog(KnLogDebug,"knstatistics: start timer %s @ %ld",knId_.c_str(),start_);
#endif
return start_ ;
}

static perftimer nextTimeInterval = 1000 * 60 * 1;// one minute to start
static perftimer nextTimeToDump = 0;
#define MAX_TIMEINTERVAL (1000 * 60 * 60 * 24 * 7) // 1 week
// each time we write the stats, take perftimerer to write out the next one
#define MULTIPLY_TIMEINTERVAL 2

// This is going to screw up every 24.xx days at present. Need to adjust
// the diff correctly on rollover, and to set up the next dump interval to
// work as it passes. The assertion should stay in, since it's part of
// debugging whether the *client* is keeping its state properly across threads.
perftimer
KnPerfStatistic::end(const perftimer start)
{
perftimer end_ = perftimerNow();
#ifdef PERFSTATISTICS_DEBUG
KnLog(KnLogDebug,"ReC: end timer %s @ %ld (start_ %ld, start %ld)",
id_.c_str(),
end_,
start_,
start
);
#endif

perftimer diff = perftimerRoll((start == 0) ? end_ - start_ : end_ - start);

KN_ASSERT(diff >= 0 && "Time must not go backward");

++value_;
total_ += diff;
if (diff < min_)
min_ = diff;
if (diff > max_)
max_ = diff;
histogram_->addInt(diff);
if (nextTimeToDump == 0)
nextTimeToDump = end_ + nextTimeInterval;
else if (nextTimeToDump <= end_)
{
nextTimeInterval = nextTimeInterval * MULTIPLY_TIMEINTERVAL;
if (nextTimeInterval > gClampStatisticsRoll)
nextTimeInterval = gClampStatisticsRoll;
nextTimeToDump = perftimerRoll(end_ + nextTimeInterval);
writeToFile();
}

return diff;
}

int KnPerfStatistic::find(KnString name)
{
int lo=0;
int hi=allStatisticsCount_;
while (lo < hi) {
const int mi = (lo + hi) / 2;
if (name <= allStatistics_[mi]->displayName_)
hi = mi;
else
lo = mi+1;
}
return lo;
}

void KnPerfStatistic::insert(KnPerfStatistic *statistic)
{
statistic->id_ = allStatisticsCount_;
if (allStatisticsCount_ >= ALLSTATISTICSMAX)
KnLog(KnLogError,"KnPerfStatistic::insert: ALLSTATISTICSMAX needs to be increased");
else
{
int i = find(statistic->displayName_);
for (int j = allStatisticsCount_; j > i; --j)
allStatistics_[j] = allStatistics_[j-1];
allStatistics_[i] = statistic;
allStatisticsCount_++;
}
}

void KnPerfStatistic::setSystemName(KnString systemName)
{
systemName_ = systemName;
}

void KnPerfStatistic::writeToFile(FILE *fp)
{
fprintf(fp, "<system>");
fprintf(fp, "<servername>");
fprintf(fp, "%s", systemName_.c_str());
fprintf(fp, "</servername>");
fprintf(fp, "<currenttime>");

char timeBuf[100];
Ns_LogTime(timeBuf);

fprintf(fp, "%s", timeBuf);
fprintf(fp, "</currenttime>\n");
fprintf(fp, "<performances>");

for (int i = 0; i < allStatisticsCount_; ++i)
{
fprintf(fp,
"<performance>\n"
"<name>%s</name>"
"<count>%d</count>"
"<min>%ld</min>"
"<max>%ld</max>"
"<total>" NS_INT_64_FORMAT_STRING "</total>"
"<avg>%ld</avg>"
"<percentile-10>%d</percentile-10>"
"<percentile-90>%d</percentile-90>"
,
allStatistics_[i]->displayName_.c_str(),
(int)allStatistics_[i]->value_,
allStatistics_[i]->min_,
allStatistics_[i]->max_,
allStatistics_[i]->total_,
(long)(allStatistics_[i]->total_/((int)allStatistics_[i]->value_>0?(int)allStatistics_[i]->value_:1)),
allStatistics_[i]->histogram_->percentile(10),
allStatistics_[i]->histogram_->percentile(90));
#ifdef dumpHistogram
allStatistics_[i]->histogram_->writeToFile(fp);
#endif
fprintf(fp, "</performance>\n");
}

fprintf(fp, "</performances></system>");
}

void KnPerfStatistic::writeToFile()
{
const char * defaultLogFile = "statistics.xml";
char* statisticsFile;
FILE *fd;
Ns_DString ds;

// get the statistics file path
Ns_DStringInit(&ds);
Ns_HomePath(&ds, "log", defaultLogFile, NULL);
statisticsFile = Ns_DStringExport(&ds);

// write the file
fd = fopen(statisticsFile, "w");
if (fd == NULL)
Ns_Log(Error, "log: failed to open statistics file '%s': '%s'", statisticsFile, strerror(errno));
else
{
writeToFile(fd);
fclose(fd);
}

// roll the file if so
if(gStatisticsRoll)
{
int status = NS_ERROR;
Ns_Log( Notice, "log: scheduled statistic roll started" );
if (statisticsFile != NULL)
{
if (access(statisticsFile, F_OK) == 0)
{
#ifdef TRUNCATE_DONT_ROLL
char * statisticFile000 = ns_malloc(strlen(statisticsFile) + 5);
sprintf(statisticFile000, "%s.000", statisticsFile);
#ifdef WIN32
CopyFile(statisticsFile,statisticFile000,0);
#endif
ns_free(statisticFile000);
status = Ns_RollFileCommon(statisticsFile, gMaxBackupStatistics,1);
#else
status = Ns_RollFile(statisticsFile, gMaxBackupStatistics);
#endif

#ifdef TRUNCATE_DONT_ROLL
status = truncate(statisticsFile,0);
if (status != 0)
fprintf(stderr,"unexpected error truncating statistic %d",status);
#endif
}
else
{
Ns_Log( Notice, "log: no file '%s' to roll (%s)",
statisticsFile, strerror(errno));
}
}

if (NS_OK == status)
{
Ns_Log(Notice, "log: statistic rolled successfully");
}
else
{
Ns_Log(Error, "log: statistic roll failed");
}
}

ns_free(statisticsFile);
}

Histogram::Histogram(const int low,const int high): low_(low), range_(high-low)
{
for (int i = 0; i < RESOLUTION; ++i)
frequency_[i] = 0;
};

void Histogram::addInt(const int value, const long weight)
{
int outOfRange = 0;
{
KnMutex::KnLock knlock( mutex_ );
int bucket = (value-low_)*RESOLUTION/range_;
if (bucket < 0)
{
bucket = 0;
outOfRange = -1;
}
if (bucket >= RESOLUTION)
{
bucket = RESOLUTION-1;
outOfRange = +1;
}
frequency_[bucket] += weight;
}
if (0 != outOfRange)
{
if (outOfRange < 0)
{
KnPerfStatistic::writeToFile();
KnLog(KnLogDebug,"Performance measurement out of range for category (below minimum)");
if(KnPerfStatistic::getAssertPerformanceOutOfScale())
KN_ASSERT_PERFORMANCE(!"Performance measurement out of range for category (below minimum)");
}
else
{
KnPerfStatistic::writeToFile();
KnLog(KnLogDebug,"Performance measurement out of range for category (above maximum)");
if(KnPerfStatistic::getAssertPerformanceOutOfScale())
KN_ASSERT_PERFORMANCE(!"Performance measurement out of range for category (above maximum)");
}
}
}

void Histogram::reset()
{
KnMutex::KnLock knlock( mutex_ );
for (int i = 0; i < RESOLUTION; i++)
frequency_[i] = 0;
}
void Histogram::setRange(const int low, const int high)
{
reset();
KnMutex::KnLock knlock( mutex_ ) ;
low_= low;
range_ = high - low;
}
int Histogram::percentile(const int percent)
{
KnMutex::KnLock knlock( mutex_ ) ;
perftimer total=0;

int i;
for (i = 0; i < RESOLUTION; i++)
total += frequency_[i];
total = total * percent / 100;
for (i = 0; i < RESOLUTION; i++)
if ((total -= frequency_[i]) < 0)
break;
return low_ + i * range_/RESOLUTION;
}

void Histogram::toString(KnString &result) const
{
result.printf("\n<histogram><low>%u</low><range>%u</range><high>%u</high><values>",low_,range_,low_ + range_);
// find largest value to normalize display
// display bars as text lines
bool inGap = false;
for (int i = 0; i < RESOLUTION; i++)
{
// print three dots for ranges with no data
if (frequency_[i] == 0)
{
if (inGap)
continue;
else
{
//result += "\n...";
inGap = true;
continue;
}
}
inGap = false;
int x1 = low_ + (range_ / RESOLUTION) * i;
int x2 = low_ + (range_ / RESOLUTION) * (i + 1);
KnString tmp;
tmp.printf("\n<value><id>%d</id><low>%d</low><high>%d</high><cnt>%ld</cnt></value>",i,x1,x2,frequency_[i]);
result += tmp;

}
result += "\n</values></histogram>";
}

void Histogram::writeToFile(FILE *fp) const
{
fprintf(fp, "\n<histogram><low>%u</low><range>%u</range><high>%u</high><values>",low_,range_,low_ + range_);
// find largest value to normalize display
// display bars as text lines
bool inGap = false;
for (int i = 0; i < RESOLUTION; i++)
{
// print three dots for ranges with no data
if (frequency_[i] == 0)
{
if (inGap)
continue;
else
{
//result += "\n...";
inGap = true;
continue;
}
}
inGap = false;
int x1 = low_ + (range_ / RESOLUTION) * i;
int x2 = low_ + (range_ / RESOLUTION) * (i + 1);
fprintf(fp, "\n<value><id>%d</id><low>%d</low><high>%d</high><cnt>%ld</cnt></value>",i,x1,x2,frequency_[i]);
}
fprintf(fp, "\n</values></histogram>");
}

--- NEW FILE: knregex.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knregex.h"
#include "knutil/knlog.h"


KnRegEx::KnRegEx() :
m_regex(NULL),
m_patObj(NULL)
{
}

KnRegEx::KnRegEx(const KnString &expr, bool caseSensitive) :
m_regex(NULL),
m_patObj(NULL)
{
setRegexPattern(expr.c_str(), caseSensitive);
}

KnRegEx::KnRegEx(const char *expr, bool caseSensitive) :
m_regex(NULL),
m_patObj(NULL)
{
setRegexPattern(expr,caseSensitive);
}

void KnRegEx::clear()
{
if(m_patObj)
Tcl_DecrRefCount(m_patObj);
}

bool KnRegEx::setRegexPattern(const KnString& expr, bool caseSensitive)
{
return setRegexPattern(expr.c_str(), caseSensitive);
}

bool KnRegEx::setRegexPattern(const char *expr, bool caseSensitive)
{
clear();

constructRegEx(expr, caseSensitive);

if(m_regex == NULL)
{
KnLog(KnLogDebug, "KnRegEx: Failed to compile Tcl_RegExp");
return false;
}

return true;
}

KnRegEx::~KnRegEx()
{
clear();
}

void KnRegEx::constructRegEx(const char* regex, bool caseSensitive)
{
m_patObj = Tcl_NewStringObj((char*)regex, strlen(regex));

int cflags = TCL_REG_ADVANCED;
if (caseSensitive == false)
cflags |= TCL_REG_NOCASE;

m_regex = Tcl_GetRegExpFromObj(NULL, m_patObj, cflags);
}

bool KnRegEx::isValid() const
{
return m_regex != NULL;
}

bool KnRegEx::match(const KnString &str) const
{
return match(str.c_str());
}

bool KnRegEx::match(Tcl_Obj* matchObj) const
{
if(!isValid())
return false;

int retCode = Tcl_RegExpExecObj(NULL, m_regex, matchObj, 0, 0, 0);

return retCode == 1;
}

bool KnRegEx::match(const char *str) const
{
if(!isValid())
return false;

Tcl_Obj* objMatchPtr = Tcl_NewStringObj((char*)str, strlen(str));
int retCode = Tcl_RegExpExecObj(NULL, m_regex, objMatchPtr, 0, 0, 0);
Tcl_DecrRefCount(objMatchPtr);

return retCode == 1;
}

bool KnRegEx::matchSubs(const char *str)
{
if(!isValid())
return false;

return Tcl_RegExpExec(NULL, m_regex, (char*)str, (char*)str) == 1;
}


bool KnRegEx::find(const char *str, char*& matchStart, int& matchLen)
{
if(!isValid())
return false;

int len = strlen(str);

Tcl_Obj* objMatchPtr = Tcl_NewStringObj((char*)str, len);
Tcl_RegExpExecObj(NULL, m_regex, objMatchPtr, 0, 1, TCL_REG_NOTBOL|TCL_REG_NOTEOL);
Tcl_DecrRefCount(objMatchPtr);

if(!getSubRange( 0, matchStart, matchLen))
return false;

return true;
}

bool KnRegEx::matchPrefix(const char *str, int& matchLen)
{
if(!isValid())
return false;

char* matchStart = NULL;
int len = strlen(str);
std::string toMatch;

// add a ^ at the beginning if not found
if((len != 0) || (str[0] != '^'))
{
toMatch += '^';
toMatch += str;
}else
toMatch = str;

Tcl_Obj* objMatchPtr = Tcl_NewStringObj((char*)toMatch.c_str(), toMatch.length());
int retCode = Tcl_RegExpExecObj(NULL, m_regex, objMatchPtr, 0, 1, TCL_REG_NOTEOL);
Tcl_DecrRefCount(objMatchPtr);

if(!getSubRange( 0, matchStart, matchLen))
return false;

return retCode == 1;
}

bool KnRegEx::matchSuffix(const char *str, int& matchLen)
{
if(!isValid())
return false;

char* matchStart = NULL;
int len = strlen(str);
std::string toMatch;

// add a $ at the end if not found
if((len != 0) || (str[len-1] != '$'))
{
toMatch += str;
toMatch += '$';
}else
toMatch = str;

Tcl_Obj* objMatchPtr = Tcl_NewStringObj((char*)toMatch.c_str(), toMatch.length());
int retCode = Tcl_RegExpExecObj(NULL, m_regex, objMatchPtr, 0, 1, TCL_REG_NOTBOL);
Tcl_DecrRefCount(objMatchPtr);

if(!getSubRange( 0, matchStart, matchLen))
return false;

return retCode == 1;
}

bool KnRegEx::getSubRange( int subIndex, char*& matchStart, int& matchLen) const
{
char* startPtr;
char* endPtr;

Tcl_RegExpRange(m_regex, subIndex, &startPtr, &endPtr);

if((startPtr == NULL)||(endPtr == NULL))
{
matchStart = NULL;
matchLen = 0;
return false;
}

matchStart = startPtr;
matchLen = endPtr - startPtr;

return true;
}

--- NEW FILE: knrwlock.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knrwlock.h"
#include "knutil/knperfstatistics.h"
#include "nsthread.h"

PERFORMANCE_STATISTICS_DECLARE_WITH_CATEGORY(gReadLockTime,KnPerfStatistic::KnCategory_PerformanceInSeconds);
PERFORMANCE_STATISTICS_DECLARE_WITH_CATEGORY(gWriteLockTime,KnPerfStatistic::KnCategory_PerformanceInSeconds);

/*
* Need C cast to convert from void ** to Ns_RWLock *.
*/

KnRWLock::KnRWLock() :
m_impl(0)
{
Ns_RWLockInit((Ns_RWLock *)&m_impl);
}

KnRWLock::~KnRWLock()
{
Ns_RWLockDestroy((Ns_RWLock *)&m_impl);
}

bool KnRWLock::lock(bool write)
{
(write) ? Ns_RWLockWrLock((Ns_RWLock *)&m_impl) :
Ns_RWLockRdLock((Ns_RWLock *)&m_impl);

return true;
}

bool KnRWLock::unlock()
{
Ns_RWLockUnlock((Ns_RWLock *)&m_impl);

return true;
}

KnRWLock::KnReadLock::KnReadLock(KnRWLock &lock) :
m_lock(lock)
{
perftimer readlock_time = PERFORMANCE_STATISTICS_BEGIN(gReadLockTime);

m_lock.lock();

PERFORMANCE_STATISTICS_END_WITH_TIME(gReadLockTime, readlock_time);

}

KnRWLock::KnReadLock::~KnReadLock()
{
m_lock.unlock();
}

KnRWLock::KnWriteLock::KnWriteLock(KnRWLock &lock) :
m_lock(lock)
{
perftimer writelock_time = PERFORMANCE_STATISTICS_BEGIN(gWriteLockTime);

m_lock.lock(true);

PERFORMANCE_STATISTICS_END_WITH_TIME(gWriteLockTime, writelock_time);

}

KnRWLock::KnWriteLock::~KnWriteLock()
{
m_lock.unlock();
}

--- NEW FILE: knrwlogginglock.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knrwlogginglock.h"

struct KnRWLoggingLock::KnLoggingLockImpl {
Ns_RWLock m_nslock ;
KnString m_name;
bool m_locking;
bool m_logging;
KnLogSeverity m_level;

KnLoggingLockImpl(
const char *name,
bool locking,
bool logging,
KnLogSeverity level
) :
m_nslock(0),
m_name(name),
m_locking(locking),
m_logging(logging),
m_level(level)
{
Ns_RWLockInit(&m_nslock);
if (m_name.length() == 0)
{
m_name.printf("%p",this);
}
if (m_logging)
{
KnLog(m_level,"KnRWLock: Created lock '%s'",m_name.c_str());
}
};

~KnLoggingLockImpl()
{
if (m_logging)
{
KnLog(m_level,"KnRWLock: Destroyed lock '%s'",m_name.c_str());
}
Ns_RWLockDestroy(&m_nslock);
};

bool lock(bool write)
{
if (m_locking)
{
if (m_logging)
KnLog(m_level,"KnRWLock: Acquiring '%s' for %s", m_name.c_str(),
write ? "write" : "read");

(write) ? Ns_RWLockWrLock(&m_nslock) :
Ns_RWLockRdLock(&m_nslock);

if (m_logging)
KnLog(m_level,"KnRWLock: Acquired '%s' for %s", m_name.c_str(),
write ? "write" : "read");
}
else if (m_logging)
{
KnLog(m_level,"KnRWLock: Ignoring %s lock of %s",write ? "write" : "read",
m_name.c_str());
}

return true;
};

bool unlock(bool write)
{
if (m_locking)
{
if (m_logging)
KnLog(m_level,"KnRWLock: Releasing '%s' for %s", m_name.c_str(),
write ? "write" : "read");

Ns_RWLockUnlock(&m_nslock);

if (m_logging)
KnLog(m_level,"KnRWLock: Released '%s' for %s", m_name.c_str(),
write ? "write" : "read");
}
else if (m_logging)
{
KnLog(m_level,"KnRWLock: Ignoring %s unlock of %s",write?"write":"read",
m_name.c_str());
}
return true;
};
};

#ifdef WIN32
#pragma warning ( disable : 4710 ) // VC 6 won't inline the constructor call
#endif

KnRWLoggingLock::KnRWLoggingLock(
const char *name, bool locking, bool logging, KnLogSeverity level) :
m_impl( 0 )
{
m_impl = new KnRWLoggingLock::KnLoggingLockImpl( name, locking, logging, level );
}

KnRWLoggingLock::~KnRWLoggingLock()
{
delete m_impl ;
m_impl = 0;
}

bool KnRWLoggingLock::setLogging(bool to)
{
bool from = m_impl->m_logging ;
m_impl->m_logging = to ;
return from ;
}

bool KnRWLoggingLock::setLocking(bool to)
{
bool from = m_impl->m_locking ;
m_impl->m_locking = to ;
return from ;
}

bool KnRWLoggingLock::setLevel(KnLogSeverity level)
{
m_impl->m_level = level ;
return true ;
}

bool KnRWLoggingLock::lock(bool write)
{
return m_impl->lock(write);
}

bool KnRWLoggingLock::unlock(bool write)
{
return m_impl->unlock(write);
}

KnRWLoggingLock::KnReadLock::KnReadLock(KnRWLoggingLock &lock) :
m_lock(lock)
{
m_lock.lock();
}

KnRWLoggingLock::KnReadLock::~KnReadLock()
{
m_lock.unlock();
}

KnRWLoggingLock::KnWriteLock::KnWriteLock(KnRWLoggingLock &lock) :
m_lock(lock)
{
m_lock.lock(true);
}

KnRWLoggingLock::KnWriteLock::~KnWriteLock()
{
m_lock.unlock(true);
}

--- NEW FILE: knset.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knset.h"

template union EXPORT_LIBRARY_KNUTIL SimpleBufferSlow<KnSetElem *>;

template class EXPORT_LIBRARY_KNUTIL SimpleVector<KnSetElem *>;
template class EXPORT_LIBRARY_KNUTIL SimpleSyncVector<KnSetElem *>;

template class EXPORT_LIBRARY_KNUTIL KnSetT<SimpleVectorSetElem>; //KnSet;
template class EXPORT_LIBRARY_KNUTIL KnSetT<SimpleSyncVectorSetElem>; //KnSyncSet;


void KnSetAssign(KnSyncSet& setSync, const KnSet& set)
{
setSync.clear();

for (size_t i = 0; i < set.size(); ++i)
{
setSync.put(set[i].m_key, set[i].m_value);
}
}

void KnSetAssign(KnSet& set, const KnSyncSet& setSync)
{
set.clear();

for (size_t i = 0; i < setSync.size(); ++i)
{
set.put(setSync[i].m_key, setSync[i].m_value);
}
}

--- NEW FILE: knsleep.cpp ---
/**
* Copyright (c) 2001 KnowNow, Inc. All rights reserved.
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/

#include "knexportlibraryknutilmodule.h"
#include "knutil/knsleep.h"

#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif

void KnSleep(long milliseconds) {
#ifdef WIN32
Sleep(milliseconds);
#else
if (milliseconds > 1000) {
sleep(milliseconds / 1000);
}
else
{
usleep(milliseconds * 1000);
}
#endif
}

--- NEW FILE: knstring.cpp ---
#include "string.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
[...1426 lines suppressed...]
break;
}

if(s)
ns_free(s);
}


void truncateFromRight( std::string& str, int len )
{
if (len > 0)
{
len = str.length() - len;
if (len < 0)
len = 0;

str.resize(len);
}
}


--- NEW FILE: knstringtokenizer.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knstringtokenizer.h"

KnStringTokenizer::KnStringTokenizer(const KnString &str, bool skip) :
m_str(str),
m_delim(" \f\n\r\t\v"),
m_pos(0),
m_skip(skip)
{
}

KnStringTokenizer::KnStringTokenizer(const KnString &str,
const KnString &delim,
bool skip) :
m_str(str),
m_delim(delim),
m_pos(0),
m_skip(skip)
{
}

KnStringTokenizer::KnStringTokenizer(const KnString &str,
const char *delim,
bool skip) :
m_str(str),
m_delim(delim),
m_pos(0),
m_skip(skip)
{
}

KnStringTokenizer::KnStringTokenizer(const KnStringTokenizer &tk) :
m_str(tk.m_str),
m_delim(tk.m_delim),
m_pos(tk.m_pos),
m_skip(tk.m_skip)
{
}

KnStringTokenizer::~KnStringTokenizer()
{
}

bool KnStringTokenizer::next(KnString &token)
{
token = "";

if (m_pos >= m_str.length())
return false;

if (m_delim.indexOf(m_str[m_pos]) != -1)
{
++m_pos;

if (!m_skip)
{
token.copy(m_str.c_str() + (m_pos - 1), 1);
return true;
}

for (;;)
{
if (m_pos >= m_str.length())
return false;

if (m_delim.indexOf(m_str[m_pos]) == -1)
break;

++m_pos;
}
}

int pos = m_pos;

for (;;)
{
++m_pos;

if (m_pos >= m_str.length() || m_delim.indexOf(m_str[m_pos]) != -1)
break;
}

token.copy(m_str.c_str() + pos, m_pos - pos);

return true;
}

bool KnStringTokenizer::next(KnString &token, const KnString &delim)
{
m_delim = delim;

return next(token);
}

bool KnStringTokenizer::next(KnString &token, const char *delim)
{
m_delim = delim;

return next(token);
}

--- NEW FILE: knsysutility_unix.cpp ---
/**
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
* Copyright 2000 - 2004 KnowNow, Inc., Sunnyvale, CA. All rights reserved.
*/

#include "knexportlibraryknutilmodule.h"
#include "knutil/knset.h"
#include "knutil/knsysutility.h"

#ifndef _WINDOWS

#include <stdlib.h>

#define HAVE_SETENV

bool KnSetEnvVar(const char *envvar, const char *value)
{
#if defined(HAVE_SETENV)

if (0 > setenv(envvar, value, 1))
return false;
return true;

#elif defined(HAVE_PUTENV)

size_t elen = strlen(envvar);
size_t vlen = strlen(value);
char *env = malloc(elen + vlen + 2);
char *p = env + elen;

memcpy(env, envvar, elen);
*p++ = '=';
memcpy(p, value, vlen);
p[vlen] = '\0';

if (0 > putenv(env))
return false;
return true;

#else
return false;
#endif
}
#endif


--- NEW FILE: knsysutility_win32.cpp ---
/**
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
* Copyright 2000 - 2004 KnowNow, Inc., Sunnyvale, CA. All rights reserved.
*/

#include "knexportlibraryknutilmodule.h"
#include "knutil/knset.h"
#include "knutil/knsysutility.h"
#include "knkillprocess_win32.h"
#include <process.h>

namespace
{
void quoteArgs( KnString &args )
{
if ( args.length() <= 0 )
return;

std::string tmp;
std::string dargs;

int from = 0;
int delim = args.indexOf( ',' );

for (;;) /* MS compiler warning-free way to write while(true) */
{
if ( delim == -1 )
delim = args.length();

dargs += "\"";
tmp.assign( args.c_str() + from, delim - from );
dargs += tmp;
dargs += "\"";

if ( delim == args.length() )
{
break;
}

dargs += " ";
from = delim + 1;
delim = args.indexOf( ',', from );
}

args = dargs.c_str();
} // quoteArgs

} // namespace

int
KnProcessStart( const KnString &exec, KnString *args,
Ns_Set *env, int *pid,
bool restart, bool nullio )
{
bool ok ;

// executable name has to be provided
if ( 0 == exec.length() )
return -1 ;

KnString dir = exec ;
dir.truncateFromRight( dir.length() - dir.lastIndexOf( '/' ) - 1 );

STARTUPINFO si ;
PROCESS_INFORMATION pi ;

/*
* Setup STARTUPINFO with stdin, stdout, and stderr.
*/
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);

if (nullio)
{
si.dwFlags = STARTF_USESTDHANDLES;
FILE *fnul = fopen("NUL", "w");
si.hStdOutput = (HANDLE) _get_osfhandle(_fileno(fnul));
si.hStdError = (HANDLE) _get_osfhandle(_fileno(fnul));
}

if ( !args && !env )
{
// 0 != syntax suppresses compiler warning about int->bool performance
ok = (0 != CreateProcess( exec.c_str(), NULL, NULL, NULL,
true, 0,
NULL, dir.c_str(),
&si, &pi ));
}
else
{
// place quotes around the arguments in case the full path names include
// spaces.
if ( args && !restart )
{
quoteArgs( *args );
}

std::string cmdline;
cmdline += "\"";
cmdline += exec.c_str();
cmdline += "\" ";
cmdline += (*args).c_str();

KnSet restore;
if ( env )
{
for ( int i = 0; i < Ns_SetSize( env ); ++i )
{
char buffer[2050] ;
int result;
result = GetEnvironmentVariable( Ns_SetKey( env, i ), (LPSTR)&buffer, sizeof(buffer) );
if (result != 0 && result < sizeof(buffer))
restore.put( Ns_SetKey( env, i ), buffer );
else
restore.put( Ns_SetKey( env, i ), NULL );

if ( !SetEnvironmentVariable( Ns_SetKey( env, i ),
Ns_SetValue( env, i )))
{
KnLog( KnLogWarning, "KnProcessStart: could not set "
"environment variable '%s' to '%s'",
Ns_SetKey( env, i ), Ns_SetValue( env, i ) ) ;
}
}
}

char *strcmdline = static_cast<char *>(ns_malloc(cmdline.length() + 1));
memcpy(strcmdline, cmdline.c_str(), cmdline.length()+1);
// 0 != syntax suppresses compiler warning about int->bool performance
ok = (0 != CreateProcess( NULL,
strcmdline,
NULL, NULL,
true, 0,
NULL,
dir.c_str(), &si, &pi )) ;
ns_free(strcmdline);

// Now restore the envs for the parent process
if ( env )
{
for ( size_t i = 0 ; i < restore.size() ; ++i )
{
if ( !SetEnvironmentVariable( restore[i].m_key.c_str(),
restore[i].m_value.c_str() ))
KnLog( KnLogWarning, "KnProcessStart: could not restore "
"environment variable '%s'", restore[i].m_key.c_str()) ;
}
}
}

CloseHandle(si.hStdInput);
CloseHandle(si.hStdOutput);

if ( !ok )
{
KnLog( KnLogDebug, "KnProcessStart failed to start process '%s':%u",
exec.c_str(), GetLastError() ) ;
return -1 ;
}

CloseHandle(pi.hThread);

KnLog( KnLogDebug, "KnProcessStart started process '%s' with pid %d",
exec.c_str(), pi.dwProcessId ) ;

if (pid)
*pid = pi.dwProcessId ;

return ( int) pi.hProcess ;
}

bool
KnProcessStop( const int phandle, int *exit )
{
if ( !TerminateProcess( (HANDLE)phandle, (DWORD)-1 ) )
{
KnLog( KnLogDebug, "KnProcessStop: failed to stop process: %u",
GetLastError() ) ;
return false ;
}

if (exit)
*exit = (int)-1 ;

return true ;
}

bool
KnProcessStop( const KnString &exec, int *exit )
{
CKillProcess helper;

KnString exe( exec.c_str()
+ exec.lastIndexOf( '/' ) + 1 ) ;

if ( !exe.has( "." ) )
{
exe += ".exe" ;
}

if ( !helper.KillProcess( exe.c_str(), exit ) )
{
KnLog( KnLogError, "KnProcessStop: failed to stop process '%s': %u",
exe.c_str(), GetLastError() ) ;
return false ;
}
if (exit)
*exit = (int)-1 ;

return true ;
}

void*
KnProcessFind( const KnString &procname )
{
DWORD dwId;
CKillProcess helper;

//KnString name( procname.c_str() + procname.lastIndexOf( '/' ) ) ;

return helper.FindProcess( procname.c_str(), dwId );
}

bool KnSetEnvVar(const char *envvar, const char *value )
{
if (!SetEnvironmentVariableA(envvar, value))
return false;

return true;
}

--- NEW FILE: kntclerrors.cpp ---
/**
* Copyright (c) 2005 KnowNow, Inc. All rights reserved.
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/

#include "knexportlibraryknutilmodule.h"
#include "knutil/knstring.h"
#include "knutil/knerror.h"
#include "knutil/kntclerrors.h"

/*
* This is all about putting that "500" in front of the message. We try
* to be consistent about returning a HTTP-style code when throwing errors.
*
* However, the rest of TCL is not so nice about it, so any TCL "catch" which
* expects to return information to clients should recognize whether a
* code is present and prepend one if not.
*
* Note that this version of WrongNumArgs does *not* do any dictionary lookup,
* unless Tcl_GetString has more magic in it than I think it does. Certainly,
* the Tcl_WrongNumArgs source has special code for that lookup that requires
* knowledge of Tcl interpreter internals.
*/
int
KnTcl_WrongNumArgs(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], const char *message) {
int ix;

std::string output(KNTCL_ERROR_WRONG_NUM_ARGS " Wrong # args: should be \"");
for (ix = 0; ix < objc; ++ix) {
if (ix > 0) {
output += " ";
}
output += Tcl_GetString(objv[ix]);
}
if (ix > 0) {
output += " ";
}
output += message ;
output += "\"";

Tcl_AppendResult(interp,output.c_str(),0);
return TCL_ERROR;
}

int /* Tcl_Error */
KnTcl_BadArg(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], const char *message) {
int ix;

std::string output(KNTCL_ERROR_BAD_ARG " Invalid argument to ");
if (objc > 0) {
output += "\"";
for (ix = 0; ix < objc; ++ix) {
if (ix > 0) {
output += " ";
}
output += Tcl_GetString(objv[ix]);
}
output += "\"";
}
output += ": ";
output += message ;

Tcl_AppendResult(interp,output.c_str(),0);
return TCL_ERROR;
}

int
KnTcl_Error(Tcl_Interp *interp, KnError status)
{
KnString output;

output.printf("%d %s",
KnError_AsHTTP( status ),
KnError_AsString(status).c_str());

Tcl_Obj *value = Tcl_NewStringObj(const_cast<char*>(output.c_str()),output.length());
Tcl_SetObjResult(interp,value);

return (KnErrorSuccess == status) ? TCL_OK : TCL_ERROR ;
}

--- NEW FILE: kntclfile.cpp ---
/* Copyright 2004 KnowNow, Inc., Sunnyvale CA 94089 USA All Rights Reserved */
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/

#include "knexportlibraryknutilmodule.h"
#include "knutil/kntclfile.h"
#include "knutil/knlog.h"
#include <sys/stat.h>

#ifndef S_ISREG
#define S_ISREG(m) ((m)&_S_IFREG)
#endif

// This belongs in a filter-writing general location in KnowNow

// For security purposes, we may want to move from using Ns_Tcl allocation
// and deallocation routines to directly using the Tcl routines,
// since the Ns_ routines add commands to the namespace
// that we may not want exposed.

KnTclFile::KnTclFile(KnString &file, KnTclInterp* interp) :
interp_(interp),
filename_(file),
valid_(true),
destroy(false)
{
// Initialize an interpreter so we can run the given file
// to read the file as pure Unicode.
struct stat statBuf;

if (stat(filename_.c_str(), &statBuf) != 0)
{
KnLog(KnLogWarning, "kntclfile: failed to access '%s': '%s'",
filename_.c_str(), strerror(errno));
valid_ = false;
}
else if (!S_ISREG(statBuf.st_mode))
{
KnLog(KnLogWarning, "kntclfile: failed to access '%s': not an ordinary filename_.c_str()",
filename_.c_str());
valid_ = false;
}
else if (access(filename_.c_str(), R_OK) != 0)
{
KnLog(KnLogWarning, "kntclfile: failed to access '%s': '%s'",
filename_.c_str(), strerror(errno));
valid_ = false;
}
else if (!interp_)
{
interp_ = new KnTclInterp((const char*)NULL);
destroy = true;
valid_ = (interp_ != 0);
}
}

KnError
KnTclFile::run(KnString& file)
{
if (!valid_)
{
KnLog(KnLogBug, "kntclfile: Attempt to run invalid file");
return KnErrorFailure ;
}
if (!interp_)
{
KnLog(KnLogBug, "kntclfile: Attempt to run unallocated interpreter");
return KnErrorFailure ;
}

if (Ns_CheckStack() == NS_BREAK) {
KnLog(KnLogError,
"KnTclFile: stack grown too large; not running '%s'",
filename_.c_str());
return KnErrorFailure;
}

return interp_->evalFile(filename_, file);
}

KnTclFile::~KnTclFile()
{
if (destroy)
delete interp_;
}

bool
KnTclFile::isValid()
{
return valid_;
}

--- NEW FILE: kntclinterp.cpp ---
// (c) Copyright 2004 KnowNow, Inc
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
//
// Provides a class that wraps the Tcl_Interp structure in a way that allows
// us to know when we should call Ns_TclDeAllocateInterp();

#include "knexportlibraryknutilmodule.h"
#include "knutil/kntclinterp.h"

// Implements reference counting in TCL variables in order to
// make sure we don't de-allocate a TclInterp that's still in use.
// Can't use C++ easily, since we're passed the Tcl_Interp* as an input
// parameter in some cases.
//
// We could potentially use a dictionary in C++ for that, but
// TCL is perfectly capable of storing our information for us. May want
// to put it in its own namespace for cleanliness reasons.
//
// Since AOLServer assures us that each Tcl_Interp is only used by one
// thread, we don't have to do any locking here.
//
// I couldn't easily implement it in terms of either KnSmartRef or
// RefCount; accordingly, it's given its own implementation here, but
// I'm sure a better way to build these things -- I just
// don't know what it is yet. -- ReC 4/1/2005

// The whole implementation is in the header file.

// Should namespace this in TCL, but don't know how;
// got 'parent namespace doesn't exist'.
char * KnTclInterp::REFCOUNT_TCLVAR = "kn_interp_depth";
int KnTclInterp::REFCOUNT_VARFLAGS = TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY ;


KnError
KnTclInterp::createObjCommand(CONST char* name, Tcl_ObjCmdProc* proc, void* data, Tcl_CmdDeleteProc* delProc)
{
Tcl_CreateObjCommand(m_pointer, name, proc, data, delProc);
return KnErrorSuccess;
}

KnError
KnTclInterp::evalFile(KnString& filename, KnString& file)
{
file.reset();

int ret = Tcl_EvalFile(m_pointer, const_cast<char*>(filename.c_str()));
if (ret == TCL_OK)
{
int len;
file = Tcl_GetStringFromObj(Tcl_GetObjResult(m_pointer), &len);
return KnErrorSuccess;
}
else
{
const char *result = Tcl_GetStringResult(m_pointer);
KnLog(KnLogError, "kntclinterp: Script %s failed (returned %d) @ line %d: '%s'",
filename.c_str(), ret, m_pointer->errorLine, result);
return KnErrorFailure;
}
}

bool
KnTclInterp::getValue(Tcl_Obj*& valueObj, int& value) {
valueObj = Tcl_GetVar2Ex(m_pointer,REFCOUNT_TCLVAR,NULL,REFCOUNT_VARFLAGS);
if (!valueObj) {
value = 0 ;
valueObj = Tcl_NewIntObj(value);
return (valueObj != 0);
} else if (TCL_OK != Tcl_GetIntFromObj(m_pointer,valueObj,&value)) {
KnLog(KnLogBug,"KnTclInterp: invalid reference count object: %s in interp %p",
Tcl_GetStringResult(m_pointer),
m_pointer);
return false ;
}
return true ;
}

bool
KnTclInterp::setValue(Tcl_Obj*& valueObj, int value) {
// It'd be nice to only do this once, but we're not worrying about
// speed right now:
Tcl_Obj *refcount_tclvar_obj = Tcl_NewStringObj(REFCOUNT_TCLVAR,strlen(REFCOUNT_TCLVAR));
Tcl_SetIntObj(valueObj,value);

bool success = (0 != Tcl_ObjSetVar2(m_pointer,refcount_tclvar_obj,NULL,valueObj,REFCOUNT_VARFLAGS));
if (!success) {
KnLog(KnLogError,
"KnTclInterp: could not set %s to %d in interp %p: %s",
REFCOUNT_TCLVAR,value,m_pointer,
Tcl_GetStringResult(m_pointer));
}
Tcl_DecrRefCount(refcount_tclvar_obj);
return success ;
}

KnError
KnTclInterp::setGlobalVariable(const KnString varName, const KnString value) const
{
Tcl_Obj *varNameObj = Tcl_NewStringObj(const_cast<char*>(varName.c_str()),varName.length());
Tcl_Obj *valueObj = Tcl_NewStringObj(const_cast<char*>(value.c_str()),value.length());

bool success = (0 != Tcl_ObjSetVar2(m_pointer,varNameObj,NULL,valueObj,TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG ));
if (!success) {
KnLog(KnLogError,
"KnTclInterp: could not set %s to %s in interp %p: %s",
varName.c_str(),value.c_str(),m_pointer,
Tcl_GetStringResult(m_pointer));
Tcl_DecrRefCount(valueObj);
}
Tcl_DecrRefCount(varNameObj);
return success ? KnErrorSuccess : KnErrorFailure ;
}

KnError
KnTclInterp::getGlobalVariable(KnString varName, KnString& value) const
{
Tcl_Obj *valueObj = Tcl_GetVar2Ex(m_pointer, const_cast<char*>(varName.c_str()),NULL,TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG );
char *valuePtr = 0;
int len;

if (!valueObj) {
return KnErrorNotFound ;
}
else if (0 == (valuePtr = Tcl_GetStringFromObj(valueObj,&len))) {
KnLog(KnLogBug,"KnTclInterp: invalid reference count object: %s in interp %p",
Tcl_GetStringResult(m_pointer),
m_pointer);
return KnErrorFailure ;
}
else {
value.copy(valuePtr,len);
return KnErrorSuccess;
}
}

KnTclInterp::KnTclInterp(Tcl_Interp* pointer)
: m_amOwner(pointer ? false : true ),
m_pointer(pointer ? pointer : Ns_TclAllocateInterp(NULL))
{
increment() ;
if (0 == Tcl_SetVar(m_pointer,"kn_interp_depth","1",TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY))
{
KnLog(KnLogWarning,"kntclinterp: could not set reference count: %s",
Tcl_GetStringResult(m_pointer));
}
}

KnTclInterp::KnTclInterp(const char* server)
: m_amOwner(true),
m_pointer(Ns_TclAllocateInterp(const_cast<char*>(server)))
{
increment() ;
if (0 == Tcl_SetVar(m_pointer,"kn_interp_depth","1",TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY))
{
KnLog(KnLogWarning,"kntclinterp: could not set reference count: %s",
Tcl_GetStringResult(m_pointer));
}
}

KnTclInterp::~KnTclInterp()
{
decrement();
}

int KnTclInterp::increment(int count)
{
assert(isValid());
int value = -1;
Tcl_Obj* valueObj;
if (getValue(valueObj,value)) {
value += count;
setValue(valueObj,value);
}
//KnLog(KnLogDev,"KnTclInterp: incremented count of %p to %d",m_pointer,value);
return (value);
}

int KnTclInterp::decrement(int count)
{
assert(isValid());
int value = -1;
Tcl_Obj* valueObj;
if (getValue(valueObj,value)) {
value -= count;
if (value <= 0 && m_amOwner) {
Ns_TclDeAllocateInterp(m_pointer);
//KnLog(KnLogDev,"KnTclInterp: deallocated %p based on value %d",m_pointer,value);
m_pointer = 0 ;
} else {
setValue(valueObj,value);
//KnLog(KnLogDev,"KnTclInterp: decremented count of %p to %d",m_pointer,value);
}
}
return (value);
}

bool KnTclInterp::isValid() const { return (0 != m_pointer); }
Tcl_Interp* KnTclInterp::operator->() { assert(isValid()); return m_pointer ; }
const Tcl_Interp* KnTclInterp::operator->() const { assert(isValid());return m_pointer;}
Tcl_Interp& KnTclInterp::operator*() { assert(isValid());return *m_pointer; }
const Tcl_Interp& KnTclInterp::operator*() const { assert(isValid());return *m_pointer; }


KnError KnTclInterp::executeBooleanCommand(KnString& tclCommand, bool& value)
{
KnLog(KnLogDebug,"KnTclInterp: Evaluating boolean command '%s'", tclCommand.c_str());

if (Ns_CheckStack() == NS_BREAK)
{
KnLog(KnLogError, "KnTclInterp: stack grown too large; not executing '%s'", tclCommand.c_str());
return KnErrorFailure;
}

Ns_DString tmpbuf;
Ns_DStringInit(&tmpbuf);
Ns_DStringNAppend(&tmpbuf, const_cast<char*>(tclCommand.c_str()), tclCommand.length());
int status = Tcl_EvalEx(m_pointer, Ns_DStringValue(&tmpbuf), Ns_DStringLength(&tmpbuf), TCL_EVAL_DIRECT);
Ns_DStringFree(&tmpbuf);

if (status != TCL_OK) {

KnLog(KnLogError,"KnTclInterp: Couldn't evaluate '%s': %s",tclCommand.c_str(), Tcl_GetStringResult(m_pointer));
Ns_TclLogError(m_pointer);
return KnErrorFailure ;
}

Tcl_Obj* result = Tcl_GetObjResult(m_pointer);
if (!result) {

KnLog(KnLogWarning,"KnTclInterp: tcl expression '%s' did not return a usable result; not changing value", tclCommand.c_str());

} else {
int intValue ;

if (TCL_OK != Tcl_GetBooleanFromObj(m_pointer,result,&intValue)) {

KnLog(KnLogError,"KnTclInterp: couldn't convert result '%s' to boolean value value",Tcl_GetStringResult(m_pointer));
return KnErrorFailure;
}

KnLog(KnLogDebug, "KnTclInterp: boolean return %d", intValue);
value = (intValue != 0);
}

KnLog(KnLogDebug,"KnTclInterp: executed boolean command successffully");

return KnErrorSuccess ;
}

--- NEW FILE: kntemplateuri.cpp ---

/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knlog.h"
#include "knutil/kntemplateuri.h"

class KnTempItemDesc
{
public:

KnTempItemDesc(const char* name, int templatePosition, int regexIndex) :
name(name), templatePosition(templatePosition)
{
startRegexIndex = stopRegexIndex = regexIndex;
}

std::string name;
int templatePosition;
int startRegexIndex;
int stopRegexIndex;
};

KnTemplateURI::KnTemplateURI(const KnString& name, const KnString& expr)
: KnUrlPattern(name)
{
m_template = expr;

m_regexPattern += "^";
m_validTemplate = makePathRegex(m_regexPattern,expr);
m_regexPattern += "$";

if(m_validTemplate)
{
setRegexPattern(m_regexPattern.c_str());
m_validTemplate = m_validTemplate && isValidRegex();
}
}

KnTemplateURI::~KnTemplateURI()
{
for(int i = 0; i < m_items.size(); i++)
{
SAFE_DELETE(m_items[i]);
}
}

int KnTemplateURI::getSubCount() const
{
return m_items.size();
}

bool KnTemplateURI::isValidTemplate() const
{
return m_validTemplate;
}

bool KnTemplateURI::matchSubs(const char *str, KnSet& result)
{
if(!isValidTemplate())
{
KnLog(KnLogDev, "KnTemplateURI: invalid template [%s]",str);
return false;
}

result.clear();

int subCount = getSubCount();

if(!subCount)
return m_regex.match(str);

KnMutex::KnLock lock(m_regexSubsMutex);

if(!m_regex.matchSubs(str))
return false;

char* mstart;
int mlen;

KnTempItemDesc* item;

std::string matched;

for(int i = 0; i < subCount; i++)
{
item = m_items[i];

for(int index = item->startRegexIndex; index <= item->stopRegexIndex; index++)
{
m_regex.getSubRange(index, mstart, mlen);
if(mlen)
break;
}

matched = "";
if(mlen)
matched.append(mstart,mlen);

result.put(item->name.c_str(), matched.c_str());
}

return true;
}

bool KnTemplateURI::replaceInto(KnSet& set, const char* expr, std::string& result, bool removeMatched)
{
int len = strlen(expr);

bool inside = false;
std::string name;

for(int i = 0; i < len; i++)
{
switch (expr[i])
{
case '{':
{
if(inside)
return false;
inside = true;

name = "";

break;
}

case '}':
{
if(!inside)
return false;
inside = false;

const KnString* value = set.get(name.c_str());
if(!value)
return false;

result += value->c_str();

if(removeMatched)
set.remove(name.c_str());

break;
}

default:
if(inside)
{
name += expr[i];
}else
{
result += expr[i];
}
}
}

return !inside;
}

bool KnTemplateURI::escape( std::string& putItHere, char aChar)
{
switch(aChar)
{
case '\\':
case '*':
case '[':
case ']':
case '(':
case ')':
case '{':
case '}':
case '.':
case '^':
case '$':
case '?':
putItHere += "\\";
break;
default:
break;
}
putItHere.append(&aChar,1);
return true;
}

// replace ( with (?: not to interfere with subexpression matching
std::string replacePars(std::string& in)
{
std::string result;
result.reserve(in.length());

for(int i = 0; i < in.length(); i++)
if(in[i] == '(')
result += "(?:";
else
result += in[i];

return result;
}

namespace {
const std::string AnyPart(".*");
const std::string AnyUrlComponent("[^/]*");
const std::string AndSubTree("(?:/.*){0,1}");
const std::string Anywhere("(?:(?:/.*/)|/)");
const std::string SubTreeOnly("/.*");
};

// Pattern taken from WS-Topics, Draft 1.2 of 6/17/2004, lines 446 - 477
// Only oddity is '//.'; that document specifies that this at the end of
// a string means AndSubtree. So we go out of our way to delete the . if
// it's at the end of the string.

// Getting the counts right takes an embedded state machine. Idea is:
// foreach component
// if (no wildcards yet) { ++leaders, ++trailers }
// else if (component has a wildcard) {++<wildcardtype>, trailers = 0}
// else { ++trailers }
// we recognize a component when we reach a '/' other than at the
// beginning of the parse, and at the end of the parse, presuming
// the last thing was not a /.
typedef enum { State_normal, State_oneslash, State_twoslash, State_withdot, State_inside} ParseState;

void addTemplate(std::string& here, KnTempItemDesc* item, int& subIndex, bool subtreeOnly = false)
{
std::string op;
std::string arg;
std::string var;
int firstpos;
int lastpos;
int len = item->name.length();
subIndex++;

for(firstpos = 0; firstpos < len; firstpos++)
if(item->name[firstpos] == '|')
break;

for(lastpos = len-1; lastpos >= 0; lastpos--)
if(item->name[lastpos] == '|')
break;

if((firstpos == len))
{
here += '(';

if(subtreeOnly)
here += SubTreeOnly;
else
here += AnyUrlComponent;

here += ')';
return;
}

op = item->name.substr( 0, firstpos);
arg = item->name.substr( firstpos+1, lastpos-firstpos-1);
var = item->name.substr( lastpos+1, len-lastpos);

if(op == "-prefix")
{
here += "(?:";

here += "(?:";
here += arg;

here += "(";

if(subtreeOnly)
here += SubTreeOnly;
else
here += AnyUrlComponent;

here += ")";

here += ")";

here += "|";

here += "(";

if(subtreeOnly)
here += SubTreeOnly;
else
here += AnyUrlComponent;

here += ")";

here += ")";

item->name = var;

item->startRegexIndex = subIndex-1;
item->stopRegexIndex = subIndex;
subIndex++;

return;
}

if(op == "-regex")
{
here += "(";
here += replacePars(arg);
here += ")";

item->name = var;

return;
}

here += '(';

if(subtreeOnly)
here += SubTreeOnly;
else
here += AnyUrlComponent;

here += ')';
}

bool KnTemplateURI::makePathRegex(KnString& result, const KnString& part)
{
bool countTemplates = true;
ParseState state = State_normal;
ParseState lastState = State_normal;

bool bOK = true;
KnUrlPattern::ComponentCounter counter(*this);

std::string putItHere = result.c_str();

int itemIndex = 1;
KnTempItemDesc* currentItem = NULL;

bool needPar = false;

int i = 0 ;
bool subTopicCounted = false;
char nextChar;
for (; i < (int)part.length() ; ++i)
{
nextChar = part[i];

if(nextChar == '{')
{
if(state == State_inside)
return false;
lastState = state;
state = State_inside;

currentItem = new KnTempItemDesc("", i, itemIndex);

continue;
}else
if(nextChar == '}')
{
if(state != State_inside)
return false;
state = lastState;

m_items.push_back(currentItem);

needPar = true;
nextChar = '*';
}

switch (state)
{
case State_inside:

currentItem->name += m_template[i];

break;
default:
case State_normal:
switch(nextChar)
{
case '/':
if (i != 0)
{
// ends a component, so count it.
counter.countComponent();
}
state = State_oneslash;
subTopicCounted = false;
break ;
case '*':
if(needPar)
{
addTemplate(putItHere, currentItem, itemIndex);

if(countTemplates)
counter.countFileCard();
}else
{
putItHere += AnyUrlComponent;
counter.countFileCard();
}
break ;
default:
if (!subTopicCounted)
{
counter.countSubTopics();
subTopicCounted = true;
}
bOK = escape(putItHere,nextChar);
}
break ;

case State_oneslash:
switch(nextChar)
{
case '/':
// It's definitely a wildcard, now to recognize it.
counter.countPathCard();
state = State_twoslash;
break;
case '*':
// This is the case foo/*..., so we're in a component
// and want a filecard. And we're back in State_normal state.
//
// So what happens with foo/*//. ? Should mean there
// must be one complete component; I think it does.
putItHere += "/";

if(needPar)
{
addTemplate(putItHere, currentItem, itemIndex);

if(countTemplates)
counter.countFileCard();
}else
{
putItHere += AnyUrlComponent;
counter.countFileCard();
}

state = State_normal ;
break ;
default:
putItHere += "/";
bOK = escape(putItHere,nextChar);
state = State_normal;
break;
}
break;

case State_twoslash:
switch(nextChar)
{
case '.':
putItHere += AndSubTree;
state = State_withdot;
break;
case '*':

if(needPar)
{
addTemplate(putItHere, currentItem, itemIndex, true);

if(countTemplates)
counter.countComponent();
}else
{
putItHere += SubTreeOnly;
counter.countComponent();
}

state = State_normal;

break ;
default:
putItHere += Anywhere;
bOK = escape(putItHere,nextChar);
counter.countComponent();
state = State_normal;
break;
}
break ;

case State_withdot:
// Well, we're still in the string, so include the .
putItHere += ".";
bOK = escape(putItHere,nextChar);
// Didn't know whether to count even the first slash till now.
counter.countComponent() ;
state = State_normal;
break ;
}
}
switch (state)
{
case State_withdot: // Trash the dot; we already output the pattern.
case State_normal:
counter.countComponent();
break;
case State_oneslash:
putItHere += "/";
break;
case State_twoslash:
putItHere += AndSubTree;
break;
}

if (i == 0)
{
putItHere += AndSubTree;
counter.countPathCard();
}

result = putItHere.c_str();

return state != State_inside;
}

--- NEW FILE: kntime.cpp ---
/*
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
* Copyright 2003 Knownow, Inc.
*/

#include "knexportlibraryknutilmodule.h"
#include "knutil/kntime.h"
#include "knutil/knlog.h"

#ifdef WIN32
#include "windows.h"
#else
#include "sys/time.h"
#endif

#ifdef WIN32
#pragma warning ( disable : 4710 ) // KnTimeImpl won't inline
#endif

#define TIMEVAL_RESOLUTION (1000000)

/**
* KnTime is implemented using the handle/body or "pimpl" idiom. That
* enables us to change the underlying implementation without requiring
* clients to recompile.
*/

struct KnTime::KnTimeImpl {
explicit KnTimeImpl() :
m_delta(false)
{
setToNow();
};

explicit KnTimeImpl( const KnString& relativeTime, KnTimeDefault dir, bool isDelta ):
m_delta(isDelta)
{
if (!m_delta)
{
setToNow();
}
else
{
m_tv.tv_sec = 0UL;
m_tv.tv_usec = 0UL;
}

setString();

if (relativeTime.startsWith("+"))
{
parseRelative( relativeTime.c_str()+1, KNTIME_FUTURE );
}
else if (relativeTime.startsWith("-"))
{
parseRelative( relativeTime.c_str()+1, KNTIME_PAST );
}
else if (relativeTime.startsWith("@"))
{
parseRelative( relativeTime.c_str()+1, KNTIME_ABSOLUTE );
}
else
{
parseRelative( relativeTime.c_str(), dir );
}
};

const KnString& asString() const {
setString() ;
return m_string ;
};

const char * c_str() const {
setString() ;
return m_string.c_str();
};

KnTime_t asTime_T() const {
return static_cast<KnTime_t>(m_tv.tv_sec);
};

double asFloat() const {
double ret = double(m_tv.tv_sec) + ((double)m_tv.tv_usec / TIMEVAL_RESOLUTION);
return ret ;
};

struct timeval asTimeVal() const {
return m_tv ;
};

KnTimeImpl& operator+=( const struct timeval& other )
{
KnLog(KnLogDebug,"kntime: Adding %lu.%lu to %lu.%lu",other.tv_sec,other.tv_usec, m_tv.tv_sec, m_tv.tv_usec);
m_tv.tv_sec += other.tv_sec ;
m_tv.tv_usec += other.tv_usec ;
if (m_tv.tv_usec >= TIMEVAL_RESOLUTION)
{
++(m_tv.tv_sec);
m_tv.tv_usec -= TIMEVAL_RESOLUTION;
}
return *this;
}

KnTimeImpl& operator-=( const struct timeval& other )
{
m_tv.tv_sec -= other.tv_sec ;
m_tv.tv_usec -= other.tv_usec ;
if (m_tv.tv_usec < 0)
{
--(m_tv.tv_sec);
m_tv.tv_usec += TIMEVAL_RESOLUTION;
if (m_tv.tv_usec < 0)
{
KnLog(KnLogBug,"KnTime: failed adjustment for underflow [%ld.%ld - %ld.%ld]?!",
m_tv.tv_sec,m_tv.tv_usec,other.tv_sec,other.tv_usec);
}
}
return *this;
}

KnTimeImpl& operator+=( const KnTimeImpl& other )
{
if (!(m_delta || other.m_delta))
{
KnLog(KnLogBug,"kntime: Adding two absolutes!");
}
return *this += other.m_tv;
}

bool operator<( const KnTimeImpl& other) const
{
return (m_tv.tv_sec < other.m_tv.tv_sec) ||
((m_tv.tv_sec == other.m_tv.tv_sec) && (m_tv.tv_usec < other.m_tv.tv_usec));
}

bool operator<=( const KnTimeImpl& other) const
{
return (*this < other) || (*this == other);
}

bool operator>( const KnTimeImpl& other) const
{
return !(*this <= other);
}
bool operator>=( const KnTimeImpl& other) const
{
return !(*this < other);
}

bool operator==( const KnTimeImpl& other ) const
{
return (m_tv.tv_sec == other.m_tv.tv_sec && m_tv.tv_usec == other.m_tv.tv_usec);
}
bool operator==( KnTime_t other ) const
{
return (static_cast<KnTime_t>(m_tv.tv_sec) == other);
}

KnTimeImpl& operator=( const KnTimeImpl& other )
{
m_delta = other.m_delta;
m_tv.tv_sec = other.m_tv.tv_sec ;
m_tv.tv_usec = other.m_tv.tv_usec ;
return *this ;
}

void setToNow()
{
#ifdef WIN32

#pragma warning ( disable: 4244 ) // "Possible loss of data" in 64->32 conversions.

// This code assumes filetime resolution is finer than
// timeval resolution.

#define FILETIME_RESOLUTION (10*1000*1000) // 100 nsec interval expressed as ticks/second

static ULARGE_INTEGER epochTime;

if (epochTime.QuadPart == 0UL)
{
FILETIME epochTemp;
SYSTEMTIME unix;
unix.wYear = 1970;
unix.wMonth = 1;
unix.wDay = 1;
unix.wHour = 0;
unix.wMinute = 0;
unix.wSecond = 0;
unix.wMilliseconds = 0;

if (!SystemTimeToFileTime(&unix, &epochTemp))
{
KnLog(KnLogBug,"kntime: could not reference to 1970");
}
epochTime.LowPart = epochTemp.dwLowDateTime;
epochTime.HighPart = epochTemp.dwHighDateTime;
}

FILETIME myTime;
GetSystemTimeAsFileTime( &myTime );

ULARGE_INTEGER temp;
temp.LowPart = myTime.dwLowDateTime;
temp.HighPart = myTime.dwHighDateTime;

temp.QuadPart -= epochTime.QuadPart ;

m_tv.tv_sec = temp.QuadPart / FILETIME_RESOLUTION;
m_tv.tv_usec = (temp.QuadPart % FILETIME_RESOLUTION) /
(FILETIME_RESOLUTION / TIMEVAL_RESOLUTION) ;

#if 0
KnLog(KnLogDebug,"KnTime: At time(0) %ld got m_tv %ld.%ld",
time(0),m_tv.tv_sec,m_tv.tv_usec);
#endif

#else

gettimeofday( &m_tv, 0 ) ;

#endif // WIN32
}

private:
void setString() const
{
m_string.reset();
m_string.printf("%ld.%6.6ld",m_tv.tv_sec, m_tv.tv_usec);
};
bool parseAbsolute( struct timeval& tv, const char *pStr )
{
const char* pDot = strchr(pStr,'.');

if ( pDot != 0 )
{
KnString high( pStr, pDot - pStr);
KnString low( pDot+1);

tv.tv_sec = high.toLong();
tv.tv_usec = low.toLong();
}
else
{
KnString sec( pStr );
tv.tv_usec = 0;
tv.tv_sec = sec.toLong();
}
return true ;
};

// Set *this to the time you want to be relative to before calling
// this function.
void parseRelative(const char *pStr, KnTimeDefault dir )
{

if (KnStringConstants::infinity.equalsIgnoreCase(pStr))
{
switch (dir)
{
case KNTIME_ABSOLUTE:
case KNTIME_FUTURE:
m_tv.tv_sec = LONG_MAX ;
m_tv.tv_usec = LONG_MAX ;
break ;
case KNTIME_PAST:
m_tv.tv_sec = 0UL ;
m_tv.tv_usec = 0UL ;
break ;
}
}
else if (KnStringConstants::now.equalsIgnoreCase(pStr))
{
if (m_delta)
{
KnLog(KnLogBug,"kntime: Setting relative time to now?!");
}
setToNow();
}
else
{
struct timeval temp ;
parseAbsolute( temp, pStr );
switch (dir)
{
case KNTIME_ABSOLUTE:
m_tv = temp ;
break ;
case KNTIME_FUTURE:
*this += temp;
break ;
case KNTIME_PAST:
*this -= temp;
break ;
}
}
}

mutable KnString m_string;
struct timeval m_tv;
bool m_delta;
};


KnTime::KnTime()
: pImpl( new KnTime::KnTimeImpl )
{}

KnTime::KnTime( const KnString &timestring, KnTimeDefault bDflt, bool bDelta )
: pImpl( new KnTime::KnTimeImpl( timestring, bDflt, bDelta ) )
{}

KnTime::KnTime( const KnTime& other )
: pImpl( new KnTime::KnTimeImpl )
{
*pImpl = *(other.pImpl);
}

KnTime::~KnTime()
{
if (pImpl) delete( pImpl ) ;
pImpl = 0;
}

const KnString&
KnTime::asString() const
{
return pImpl->asString();
}

const char *
KnTime::c_str() const
{
return pImpl->c_str();
}

KnTime_t
KnTime::asTime_T() const
{
return pImpl->asTime_T();
}

double
KnTime::asFloat() const
{
return pImpl->asFloat();
}

struct timeval
KnTime::asTimeVal() const
{
return pImpl->asTimeVal();
}

KnTime& KnTime::operator+=( const KnTime& other )
{
*pImpl += *(other.pImpl);
return *this;
}

bool KnTime::operator<( const KnTime& other) const
{
return *pImpl < *(other.pImpl);
}

bool KnTime::operator<=( const KnTime& other) const
{
return *pImpl <= *(other.pImpl);
}

bool KnTime::operator>( const KnTime& other) const
{
return *pImpl > *(other.pImpl);
}

bool KnTime::operator>=( const KnTime& other) const
{
return *pImpl >= *(other.pImpl);
}

bool KnTime::operator==( const KnTime& other) const
{
return *pImpl == *(other.pImpl);
}

bool KnTime::operator==( const KnTime_t other) const
{
return *pImpl == other;
}

bool KnTime::operator!=( const KnTime& other) const
{
return !(*pImpl == *(other.pImpl));
}

bool KnTime::operator!=( const KnTime_t other) const
{
return !(*pImpl == other);
}

KnTime& KnTime::operator=( const KnTime& other )
{
*pImpl = *(other.pImpl);
return *this ;
}

void KnTime::setToNow()
{
pImpl->setToNow();
}

EXPORT_LIBRARY_KNUTIL
KnTime operator+( const KnTime& lhs, const KnTime& rhs )
{
return KnTime(lhs) += rhs;
};

--- NEW FILE: knurl.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knurl.h"
#include <sstream>

// This class might well be extended to:
//
// Reference-count the URL for various storage strategies.
//
// Provide the last file component (for AOL-style pattern matching).
// (actually, the tail part should be separated out (from ? on)),
// A direct access routine for the last filecomponent would be nice)

KnUrl::KnUrl(const KnString &url)
: m_parsed(false)
{
m_string = new char[url.length()+1];
memset(m_string, 0, url.length()+1);
memcpy(m_string, url.c_str(), url.length());
}

KnUrl::KnUrl(const char *url)
: m_parsed(false)
{
m_string = new char[strlen(url)+1];
memset(m_string, 0, strlen(url)+1);
memcpy(m_string, url, strlen(url));
}

KnUrl::KnUrl( const char *scheme
, const char *user
, const char *password
, const char *host
, const int port
, const char *path
, const char * /* query */
, const char * /* fragment */
) :
m_string(0),
m_parsed(true),
m_scheme(scheme),
m_user(user),
m_password(password),
m_host(host),
m_port(),
m_path(path)
{
m_port.printf("%d",port);
std::string reverse(path);
int pos = m_path.lastIndexOf('/');
if (pos >= 0) {
m_basename += &(m_path.c_str()[pos + 1]);
} else {
m_basename = m_path;
}
}

KnUrl::~KnUrl()
{
SAFE_DELETE_ARRAY(m_string);
}

std::string
KnUrl::get_root() const {
if (!m_parsed) { parse(); }
if (!isAbsolute()) { return ""; };

std::string value(m_scheme.c_str());
value.append("://");
value.append(m_host.c_str());
#if 0
KnLog(KnLogDev,"knurl: parsed host part is '%s'",m_host.c_str());
#endif

// m_port is 0-length if you parse http://foo/fum . OTOH, it's
// got values like 80 & 443 in it sometimes, too. Maybe the parse
// should take care of that....
if (m_port.length() &&
!((m_scheme == KnStringConstants::http && m_port == "80") ||
(m_scheme == KnStringConstants::https && m_port == "443"))) {
value.append(":");
value.append(m_port.c_str());
}
#if 0
KnLog(KnLogDev,"knurl: reassembled root is '%s'",value.c_str());
#endif
return value;
}

void
KnUrl::parse() const
{
char *protocol, *host, *port, *path, *tail;

if (!m_string)
return;

Ns_ParseUrl(m_string,&protocol,&host,&port,&path,&tail);

m_host = host ;
m_port = port ;
m_scheme = protocol ;
m_path = "/";
m_path += path ;
m_basename = tail ;
m_path += tail ;

// Ns_ParseUrl won't find the user & password components if provided
// In fact, it'll take 'foo:fum@scum:30" and give us foo in m_host,
// and fum@scum:30 in port. Almost tempted me to rewrite without it.
int indexAt = m_port.indexOf('@');
if (indexAt >= 0)
{
m_user = m_host ;
m_host.reset();
m_password.reset();
m_password.printf("%.*s",indexAt,m_port.c_str());
int indexColon = m_port.indexOf(':',indexAt);

if (indexColon >= 0)
{
m_host.printf("%.*s",indexColon - indexAt - 1, m_port.c_str() + indexAt + 1);

KnString tmp(m_port);
m_port.reset() ; // Should be redundant.
m_port += tmp.c_str() + indexColon + 1;
}
else
{
m_host.printf("%s",m_port.c_str() + indexAt + 1);
m_port.reset();
}
}
#if 0
KnLog(KnLogDev,"knurl: parsed '%s' to protocol '%s', user/pw '%s/%s', location '%s', port '%s', path '%s'",
url,
m_protocol.c_str(),
m_user.c_str(),
m_password.c_str(),
m_host.c_str(),
m_port.c_str(),
m_path.c_str()
);
#endif

m_parsed = true ;
}

void
KnUrl::encode(std::string& buffer, const char *str)
{
Ns_DString tmp;
Ns_DStringInit(&tmp);

Ns_EncodeUrl(&tmp, const_cast<char*>(str));
buffer += tmp.string;

Ns_DStringFree(&tmp);
}

void
KnUrl::encode(KnString &buffer, const KnString &name)
{
Ns_DString tmp;
Ns_DStringInit(&tmp);

Ns_EncodeUrl(&tmp, const_cast<char*>(name.c_str()));
buffer += tmp.string;

Ns_DStringFree(&tmp);
}

void
KnUrl::encode(std::string& buffer, const KnString& name)
{
Ns_DString tmp;
Ns_DStringInit(&tmp);

Ns_EncodeUrl(&tmp, const_cast<char*>(name.c_str()));
buffer += tmp.string;

Ns_DStringFree(&tmp);
}

// Presume caller knows whether to put ? on or not depending
// on context.
void
KnUrl::encode(std::string& buf, const KnSet& set)
{
for (size_t ix = 0; ix < set.size(); ++ix)
{
if (ix)
{
buf += "&";
}
encode(buf, set[ix].m_key);
encode(buf += "=",set[ix].m_value);
}
}

// And now for NsSet. These should just be free functions, I think
void
KnUrl::encode(std::string& buf, const Ns_Set* set)
{
for (int jx = 0; jx < Ns_SetSize(set); ++jx)
{
if (jx)
{
buf += "&";
}
encode(buf, Ns_SetKey(set,jx));
encode(buf += "=",Ns_SetValue(set,jx));
}
}


--- NEW FILE: mtrand.cpp ---
#include "knexportlibraryknutilmodule.h"
#include "knutil/mtrand.h"

// mtrand.cpp
//
// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. The names of its contributors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Any feedback is very welcome.
// http://www.math.keio.ac.jp/matumoto/emt.html
// email: matu...@math.keio.ac.jp
//
// Feedback about the C++ port should be sent to Jasper Bedaux,
// see http://www.bedaux.net/mtrand/ for e-mail address and info.

// non-inline function definitions and static member definitions cannot
// reside in header file because of the risk of multiple declarations

// initialization of static private members
unsigned long MTRand_int32::state[n] = {0x0UL};
int MTRand_int32::p = 0;
bool MTRand_int32::init = false;

void MTRand_int32::gen_state() { // generate new state vector
for (int i = 0; i < (n - m); ++i)
state[i] = state[i + m] ^ twiddle(state[i], state[i + 1]);
for (int i = n - m; i < (n - 1); ++i)
state[i] = state[i + m - n] ^ twiddle(state[i], state[i + 1]);
state[n - 1] = state[m - 1] ^ twiddle(state[n - 1], state[0]);
p = 0; // reset position
}

void MTRand_int32::seed(unsigned long s) { // init by 32 bit seed
state[0] = s & 0xFFFFFFFFUL; // for > 32 bit machines
for (int i = 1; i < n; ++i) {
state[i] = 1812433253UL * (state[i - 1] ^ (state[i - 1] >> 30)) + i;
// see Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier
// in the previous versions, MSBs of the seed affect only MSBs of the array state
// 2002/01/09 modified by Makoto Matsumoto
state[i] &= 0xFFFFFFFFUL; // for > 32 bit machines
}
p = n; // force gen_state() to be called for next random number
}

void MTRand_int32::seed(const unsigned long* array, int size) { // init by array
seed(19650218UL);
int i = 1, j = 0;
for (int k = ((n > size) ? n : size); k; --k) {
state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1664525UL))
+ array[j] + j; // non linear
state[i] &= 0xFFFFFFFFUL; // for > 32 bit machines
++j; j %= size;
if ((++i) == n) { state[0] = state[n - 1]; i = 1; }
}
for (int k = n - 1; k; --k) {
state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1566083941UL)) - i;
state[i] &= 0xFFFFFFFFUL; // for > 32 bit machines
if ((++i) == n) { state[0] = state[n - 1]; i = 1; }
}
state[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array
p = n; // force gen_state() to be called for next random number
}

--- NEW FILE: randomnumber.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/randomnumber.h"

#define ind(mm, x) \
(*(reinterpret_cast<uint32_t *>( \
reinterpret_cast<uint8_t *>(mm) + ((x) & ((RANDSIZ - 1) << 2)))))

#define mix(a, b, c, d, e, f, g, h) \
{ \
a ^= b << 11; d += a; b += c; \
b ^= c >> 2; e += b; c += d; \
c ^= d << 8; f += c; d += e; \
d ^= e >> 16; g += d; e += f; \
e ^= f << 10; h += e; f += g; \
f ^= g >> 4; a += f; g += h; \
g ^= h << 8; b += g; h += a; \
h ^= a >> 9; c += h; a += b; \
}

#define rngstep(mix, a, b, mm, m, m2, r, x) \
{ \
x = *m; \
a = (a ^ (mix)) + *(m2++); \
*(m++) = y = ind(mm, x) + a + b; \
*(r++) = b = ind(mm, y >> RANDSIZL) + x; \
}

RandomNumber::RandomNumber(const uint32_t *seed, uint32_t len)
{
memset(m_randrsl, 0, sizeof(m_randrsl));

if (len > RANDSIZ)
len = RANDSIZ;

if (seed != 0)
memcpy(m_randrsl, seed, len * sizeof(*seed));

randinit();
}

RandomNumber::~RandomNumber()
{
}

uint32_t RandomNumber::rand()
{
if (m_randcnt-- == 0)
{
isaac();
m_randcnt = RANDSIZ - 1;
}

return m_randrsl[m_randcnt];
}

void RandomNumber::isaac()
{
uint32_t a = m_randa;
uint32_t b = m_randb + (++m_randc);
uint32_t x = 0;
uint32_t y = 0;
uint32_t *m = m_randmem;
uint32_t *m2 = m_randmem + (RANDSIZ / 2);
uint32_t *mm = m_randmem;
uint32_t *r = m_randrsl;

while (m < m_randmem + (RANDSIZ / 2))
{
rngstep(a << 13, a, b, mm, m, m2, r, x);
rngstep(a >> 6, a, b, mm, m, m2, r, x);
rngstep(a << 2, a, b, mm, m, m2, r, x);
rngstep(a >> 16, a, b, mm, m, m2, r, x);
}

m2 = mm;

while (m2 < m_randmem + (RANDSIZ / 2))
{
rngstep(a << 13, a, b, mm, m, m2, r, x);
rngstep(a >> 6, a, b, mm, m, m2, r, x);
rngstep(a << 2, a, b, mm, m, m2, r, x);
rngstep(a >> 16, a, b, mm, m, m2, r, x);
}

m_randa = a;
m_randb = b;
}

void RandomNumber::randinit()
{
uint32_t a = 0x9e3779b9;
uint32_t b = 0x9e3779b9;
uint32_t c = 0x9e3779b9;
uint32_t d = 0x9e3779b9;
uint32_t e = 0x9e3779b9;
uint32_t f = 0x9e3779b9;
uint32_t g = 0x9e3779b9;
uint32_t h = 0x9e3779b9;

m_randa = 0;
m_randb = 0;
m_randc = 0;

for (uint32_t i = 0; i < 4; ++i)
{
mix(a, b, c, d, e, f, g, h);
}

for (uint32_t j = 0; j < RANDSIZ; j += 8)
{
a += m_randrsl[j ]; b += m_randrsl[j + 1]; c += m_randrsl[j + 2]; d += m_randrsl[j + 3];
e += m_randrsl[j + 4]; f += m_randrsl[j + 5]; g += m_randrsl[j + 6]; h += m_randrsl[j + 7];

mix(a, b, c, d, e, f, g, h);

m_randmem[j ] = a; m_randmem[j + 1] = b; m_randmem[j + 2] = c; m_randmem[j + 3] = d;
m_randmem[j + 4] = e; m_randmem[j + 5] = f; m_randmem[j + 6] = g; m_randmem[j + 7] = h;
}

for (uint32_t k = 0; k < RANDSIZ; k += 8)
{
a += m_randmem[k ]; b += m_randmem[k + 1]; c += m_randmem[k + 2]; d += m_randmem[k + 3];
e += m_randmem[k + 4]; f += m_randmem[k + 5]; g += m_randmem[k + 6]; h += m_randmem[k + 7];

mix(a, b, c, d, e, f, g, h);

m_randmem[k ] = a; m_randmem[k + 1] = b; m_randmem[k + 2] = c; m_randmem[k + 3] = d;
m_randmem[k + 4] = e; m_randmem[k + 5] = f; m_randmem[k + 6] = g; m_randmem[k + 7] = h;
}

isaac();
m_randcnt = RANDSIZ;
}

--- NEW FILE: shahash.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/shahash.h"

#ifdef WIN32
#define SHA_ROTATE(a, n) _lrotl(a, n)
#else
#define SHA_ROTATE(a, n) ((a << n) | (a >> (32 - n)))
#endif /* WIN32 */

#if defined(__linux) && defined(__i386)
#define SHA_UPDATE(f, i) \
{ \
f = block[i & 0xF] ^ block[(i + 2) & 0xF] ^ block[(i + 8) & 0xF] ^ \
block[(i + 13) & 0xF]; \
block[i & 0xF] = f = SHA_ROTATE(f, 1); \
}
#else
#define SHA_UPDATE(f, i) \
{ \
f = block[i & 0xF] ^ block[(i + 2) & 0xF] ^ block[(i + 8) & 0xF] ^ \
block[(i + 13) & 0xF]; \
f = block[i & 0xF] = SHA_ROTATE(f, 1); \
}
#endif /* __linux */

#define SHA_00_15(i, a, b, c, d, e, f) \
{ \
f = block[i] + e + 0x5A827999 + SHA_ROTATE(a, 5) + (((c ^ d) & b) ^ d); \
b = SHA_ROTATE(b, 30); \
}

#define SHA_16_19(i, a, b, c, d, e, f) \
{ \
SHA_UPDATE(f, i); \
f += e + 0x5A827999 + SHA_ROTATE(a, 5) + (((c ^ d) & b) ^ d); \
b = SHA_ROTATE(b, 30); \
}

#define SHA_20_39(i, a, b, c, d, e, f) \
{ \
SHA_UPDATE(f, i); \
f += e + 0x6ED9EBA1 + SHA_ROTATE(a, 5) + (b ^ c ^ d); \
b = SHA_ROTATE(b, 30); \
}

#define SHA_40_59(i, a, b, c, d, e, f) \
{ \
SHA_UPDATE(f, i); \
f += e + 0x8F1BBCDC + SHA_ROTATE(a, 5) + ((b & c) | ((b | c) & d)); \
b = SHA_ROTATE(b, 30); \
}

#define SHA_60_79(i, a, b, c, d, e, f) \
{ \
SHA_UPDATE(f, i); \
f = block[i & 0xF] + e + 0xCA62C1D6 + SHA_ROTATE(a, 5) + (b ^ c ^ d); \
b = SHA_ROTATE(b, 30); \
}

ShaHash::ShaHash()
{
m_digest[0] = 0x67452301;
m_digest[1] = 0xEFCDAB89;
m_digest[2] = 0x98BADCFE;
m_digest[3] = 0x10325476;
m_digest[4] = 0xC3D2E1F0;

// Didn't used to initialize, but Purify complains
// and I can't see how initializing it can hurt. -- ReC 03/02/04
for (int ix = 0; ix < BlockWords;++ix)
{
m_block[ix] = 0;
}

m_count[0] = 0;
m_count[1] = 0;

m_index = 0;
}

ShaHash::~ShaHash()
{
}

void ShaHash::transform(uint32_t *digest, uint32_t *block)
{
uint32_t a = digest[0];
uint32_t b = digest[1];
uint32_t c = digest[2];
uint32_t d = digest[3];
uint32_t e = digest[4];
uint32_t t = 0;

SHA_00_15( 0, a, b, c, d, e, t);
SHA_00_15( 1, t, a, b, c, d, e);
SHA_00_15( 2, e, t, a, b, c, d);
SHA_00_15( 3, d, e, t, a, b, c);
SHA_00_15( 4, c, d, e, t, a, b);
SHA_00_15( 5, b, c, d, e, t, a);
SHA_00_15( 6, a, b, c, d, e, t);
SHA_00_15( 7, t, a, b, c, d, e);
SHA_00_15( 8, e, t, a, b, c, d);
SHA_00_15( 9, d, e, t, a, b, c);
SHA_00_15(10, c, d, e, t, a, b);
SHA_00_15(11, b, c, d, e, t, a);
SHA_00_15(12, a, b, c, d, e, t);
SHA_00_15(13, t, a, b, c, d, e);
SHA_00_15(14, e, t, a, b, c, d);
SHA_00_15(15, d, e, t, a, b, c);
SHA_16_19(16, c, d, e, t, a, b);
SHA_16_19(17, b, c, d, e, t, a);
SHA_16_19(18, a, b, c, d, e, t);
SHA_16_19(19, t, a, b, c, d, e);

SHA_20_39(20, e, t, a, b, c, d);
SHA_20_39(21, d, e, t, a, b, c);
SHA_20_39(22, c, d, e, t, a, b);
SHA_20_39(23, b, c, d, e, t, a);
SHA_20_39(24, a, b, c, d, e, t);
SHA_20_39(25, t, a, b, c, d, e);
SHA_20_39(26, e, t, a, b, c, d);
SHA_20_39(27, d, e, t, a, b, c);
SHA_20_39(28, c, d, e, t, a, b);
SHA_20_39(29, b, c, d, e, t, a);
SHA_20_39(30, a, b, c, d, e, t);
SHA_20_39(31, t, a, b, c, d, e);
SHA_20_39(32, e, t, a, b, c, d);
SHA_20_39(33, d, e, t, a, b, c);
SHA_20_39(34, c, d, e, t, a, b);
SHA_20_39(35, b, c, d, e, t, a);
SHA_20_39(36, a, b, c, d, e, t);
SHA_20_39(37, t, a, b, c, d, e);
SHA_20_39(38, e, t, a, b, c, d);
SHA_20_39(39, d, e, t, a, b, c);

SHA_40_59(40, c, d, e, t, a, b);
SHA_40_59(41, b, c, d, e, t, a);
SHA_40_59(42, a, b, c, d, e, t);
SHA_40_59(43, t, a, b, c, d, e);
SHA_40_59(44, e, t, a, b, c, d);
SHA_40_59(45, d, e, t, a, b, c);
SHA_40_59(46, c, d, e, t, a, b);
SHA_40_59(47, b, c, d, e, t, a);
SHA_40_59(48, a, b, c, d, e, t);
SHA_40_59(49, t, a, b, c, d, e);
SHA_40_59(50, e, t, a, b, c, d);
SHA_40_59(51, d, e, t, a, b, c);
SHA_40_59(52, c, d, e, t, a, b);
SHA_40_59(53, b, c, d, e, t, a);
SHA_40_59(54, a, b, c, d, e, t);
SHA_40_59(55, t, a, b, c, d, e);
SHA_40_59(56, e, t, a, b, c, d);
SHA_40_59(57, d, e, t, a, b, c);
SHA_40_59(58, c, d, e, t, a, b);
SHA_40_59(59, b, c, d, e, t, a);

SHA_60_79(60, a, b, c, d, e, t);
SHA_60_79(61, t, a, b, c, d, e);
SHA_60_79(62, e, t, a, b, c, d);
SHA_60_79(63, d, e, t, a, b, c);
SHA_60_79(64, c, d, e, t, a, b);
SHA_60_79(65, b, c, d, e, t, a);
SHA_60_79(66, a, b, c, d, e, t);
SHA_60_79(67, t, a, b, c, d, e);
SHA_60_79(68, e, t, a, b, c, d);
SHA_60_79(69, d, e, t, a, b, c);
SHA_60_79(70, c, d, e, t, a, b);
SHA_60_79(71, b, c, d, e, t, a);
SHA_60_79(72, a, b, c, d, e, t);
SHA_60_79(73, t, a, b, c, d, e);
SHA_60_79(74, e, t, a, b, c, d);
SHA_60_79(75, d, e, t, a, b, c);
SHA_60_79(76, c, d, e, t, a, b);
SHA_60_79(77, b, c, d, e, t, a);
SHA_60_79(78, a, b, c, d, e, t);
SHA_60_79(79, t, a, b, c, d, e);

digest[0] += e;
digest[1] += t;
digest[2] += a;
digest[3] += b;
digest[4] += c;
}

void ShaHash::final(uint8_t *digest)
{
uint32_t *p = m_block;
uint32_t i = m_index >> 2;
uint32_t l = p[i];

switch (m_index & 0x3)
{
case 0: l = 0x80000000; break;
case 1: l |= 0x00800000; break;
case 2: l |= 0x00008000; break;
case 3: l |= 0x00000080; break;
}

p[i++] = l;

if (m_index >= 56)
{
for (; i < BlockWords; ++i)
{
p[i] = 0;
}

transform(m_digest, p);
i = 0;
}

for (; i < BlockWords - 2; ++i)
{
p[i] = 0;
}

p[BlockWords - 2] = m_count[1];
p[BlockWords - 1] = m_count[0];

transform(m_digest, p);

uint32_t a = m_digest[0];
uint32_t b = m_digest[1];
uint32_t c = m_digest[2];
uint32_t d = m_digest[3];
uint32_t e = m_digest[4];

digest[ 0] = static_cast<uint8_t>(a >> 24);
digest[ 1] = static_cast<uint8_t>(a >> 16);
digest[ 2] = static_cast<uint8_t>(a >> 8);
digest[ 3] = static_cast<uint8_t>(a);
digest[ 4] = static_cast<uint8_t>(b >> 24);
digest[ 5] = static_cast<uint8_t>(b >> 16);
digest[ 6] = static_cast<uint8_t>(b >> 8);
digest[ 7] = static_cast<uint8_t>(b);
digest[ 8] = static_cast<uint8_t>(c >> 24);
digest[ 9] = static_cast<uint8_t>(c >> 16);
digest[10] = static_cast<uint8_t>(c >> 8);
digest[11] = static_cast<uint8_t>(c);
digest[12] = static_cast<uint8_t>(d >> 24);
digest[13] = static_cast<uint8_t>(d >> 16);
digest[14] = static_cast<uint8_t>(d >> 8);
digest[15] = static_cast<uint8_t>(d);
digest[16] = static_cast<uint8_t>(e >> 24);
digest[17] = static_cast<uint8_t>(e >> 16);
digest[18] = static_cast<uint8_t>(e >> 8);
digest[19] = static_cast<uint8_t>(e);

m_index = 0;
}

void ShaHash::update(const void *buf, uint32_t len)
{
if (len == 0)
return;

uint32_t l = m_count[0] + (len << 3);

if (l < m_count[0])
++m_count[1];

m_count[1] += len >> 29;
m_count[0] = l;

const uint8_t *data = static_cast<const uint8_t *>(buf);

if (m_index > 0)
{
uint32_t *p = m_block;
uint32_t sc = m_index & 0x3;
uint32_t sw = m_index >> 2;

if (m_index + len >= BlockLen)
{
l = p[sw];

switch (sc)
{
case 0: l = static_cast<uint32_t>(*data++) << 24;
case 1: l |= static_cast<uint32_t>(*data++) << 16;
case 2: l |= static_cast<uint32_t>(*data++) << 8;
case 3: l |= static_cast<uint32_t>(*data++);
}

p[sw++] = l;

for (; sw < BlockWords; ++sw, data += 4)
{
p[sw] = ((static_cast<uint32_t>(data[0]) << 24) |
(static_cast<uint32_t>(data[1]) << 16) |
(static_cast<uint32_t>(data[2]) << 8) |
(static_cast<uint32_t>(data[3])));
}

len -= BlockLen - m_index;
transform(m_digest, p);
m_index = 0;
}
else
{
m_index += len;

if (sc + len < 4)
{
l = p[sw];

switch (sc)
{
case 0:
l = static_cast<uint32_t>(*data++) << 24;
if (--len == 0) break;
case 1:
l |= static_cast<uint32_t>(*data++) << 16;
if (--len == 0) break;
case 2:
l |= static_cast<uint32_t>(*data++) << 8;
}

p[sw] = l;
}
else
{
uint32_t ec = m_index & 0x3;
uint32_t ew = m_index >> 2;

l = p[sw];

switch (sc)
{
case 0: l = static_cast<uint32_t>(*data++) << 24;
case 1: l |= static_cast<uint32_t>(*data++) << 16;
case 2: l |= static_cast<uint32_t>(*data++) << 8;
case 3: l |= static_cast<uint32_t>(*data++);
}

p[sw++] = l;

for (; sw < ew; ++sw, data += 4)
{
p[sw] = ((static_cast<uint32_t>(data[0]) << 24) |
(static_cast<uint32_t>(data[1]) << 16) |
(static_cast<uint32_t>(data[2]) << 8) |
(static_cast<uint32_t>(data[3])));
}

if (ec > 0)
{
l = 0;
data += ec;

switch (ec)
{
case 3: l = static_cast<uint32_t>(*--data) << 8;
case 2: l |= static_cast<uint32_t>(*--data) << 16;
case 1: l |= static_cast<uint32_t>(*--data) << 24;
}

p[sw] = l;
}
}

return;
}
}

uint32_t *p = m_block;

while (len >= BlockLen)
{
for (uint32_t sw = BlockWords / 4; sw > 0; --sw, data += 16)
{
*p++ = ((static_cast<uint32_t>(data[ 0]) << 24) |
(static_cast<uint32_t>(data[ 1]) << 16) |
(static_cast<uint32_t>(data[ 2]) << 8) |
(static_cast<uint32_t>(data[ 3])));

*p++ = ((static_cast<uint32_t>(data[ 4]) << 24) |
(static_cast<uint32_t>(data[ 5]) << 16) |
(static_cast<uint32_t>(data[ 6]) << 8) |
(static_cast<uint32_t>(data[ 7])));

*p++ = ((static_cast<uint32_t>(data[ 8]) << 24) |
(static_cast<uint32_t>(data[ 9]) << 16) |
(static_cast<uint32_t>(data[10]) << 8) |
(static_cast<uint32_t>(data[11])));

*p++ = ((static_cast<uint32_t>(data[12]) << 24) |
(static_cast<uint32_t>(data[13]) << 16) |
(static_cast<uint32_t>(data[14]) << 8) |
(static_cast<uint32_t>(data[15])));
}

p = m_block;
transform(m_digest, p);
len -= BlockLen;
}

m_index = len;

uint32_t ec = len & 0x3;
uint32_t ew = len >> 2;
uint32_t sw = 0;

for (; sw < ew; ++sw, data += 4)
{
p[sw] = ((static_cast<uint32_t>(data[0]) << 24) |
(static_cast<uint32_t>(data[1]) << 16) |
(static_cast<uint32_t>(data[2]) << 8) |
(static_cast<uint32_t>(data[3])));
}

l = 0;
data += ec;

switch (ec)
{
case 3: l = static_cast<uint32_t>(*--data) << 8;
case 2: l |= static_cast<uint32_t>(*--data) << 16;
case 1: l |= static_cast<uint32_t>(*--data) << 24;
}

p[sw] = l;
}

--- NEW FILE: uuidgenerator.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/uuidgenerator.h"

#ifdef WIN32
#include <rpc.h>

bool UuidGenerator::next(KnString &kns)
{
/*
* Create the uuid in binary form.
*/
UUID uuid;

if (UuidCreate(&uuid) != RPC_S_OK)
return false;

/*
* Convert from binary into a string.
*/
uint8_t *str;

if (UuidToString(&uuid, &str) != RPC_S_OK)
return false;

/*
* Append to the given string.
*/
kns += reinterpret_cast<char *>(str);

/*
* Dispose of temporary memory _not from nsd_.
*/
RpcStringFree(&str);

return true;
}
#else
bool UuidGenerator::next(KnString &kns)
{
/*
* File descriptor to the crytographic device.
*/
static int fd = open("/dev/urandom", O_RDONLY);

/*
* A uuid consists of 16 octets.
*/
uint8_t oc[16];

/*
* Loop until all octets have been read.
*/
for (size_t n = 0; n < sizeof(oc);)
{
ssize_t len = read(fd, &oc[n], sizeof(oc) - n);

if (len > 0)
{
n += len;
}
else if (len == 0 || (len < 0 && errno != EINTR))
{
return false;
}
}

/*
* Unpack octets.
*/
uint32_t time_low = ((static_cast<uint32_t>(oc[0]) << 24) |
(static_cast<uint32_t>(oc[1]) << 16) |
(static_cast<uint32_t>(oc[2]) << 8) |
(static_cast<uint32_t>(oc[3])));

uint16_t time_mid = ((static_cast<uint16_t>(oc[4]) << 8) |
(static_cast<uint16_t>(oc[5])));

uint16_t time_hi = ((static_cast<uint16_t>(oc[6]) << 8) |
(static_cast<uint16_t>(oc[7])));

/*
* Set version information.
*/
kns.printf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
time_low, time_mid, ((time_hi & 0x0FFF) | 0x4000),
((oc[8] & 0x3F) | 0x80), oc[9], oc[10], oc[11],
oc[12], oc[13], oc[14], oc[15]);

return true;
}
#endif


Reply all
Reply to author
Forward
0 new messages