/*##############################################################################
Copyright (C) 2011 HPCC Systems.
All rights reserved. This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
############################################################################## */
#pragma warning(disable: 4996)
#ifdef _WIN32
#include "winprocess.hpp"
#include
#endif
#include "platform.h"
#include "jmisc.hpp"
#include "jutil.hpp"
#include "jexcept.hpp"
#include "jmutex.hpp"
#include "jfile.hpp"
#include "jprop.hpp"
#ifdef _WIN32
#include // for timeGetTime
#else
#include // read()
#include
#include
#ifdef __linux__
#include
#include
#endif
#include
#include
#include
#include
#include
#include
#include "build-config.h"
#endif
static SpinLock * cvtLock;
#ifdef _WIN32
static IRandomNumberGenerator * protectedGenerator;
static CriticalSection * protectedGeneratorCs;
#endif
MODULE_INIT(INIT_PRIORITY_SYSTEM)
{
cvtLock = new SpinLock;
#ifdef _WIN32
protectedGenerator = createRandomNumberGenerator();
protectedGeneratorCs = new CriticalSection;
#endif
return true;
}
MODULE_EXIT()
{
delete cvtLock;
#ifdef _WIN32
protectedGenerator->Release();
delete protectedGeneratorCs;
#endif
}
//===========================================================================
bool safe_ecvt(size_t len, char * buffer, double value, int numDigits, int * decimal, int * sign)
{
#ifdef _WIN32
return _ecvt_s(buffer, len, value, numDigits, decimal, sign) == 0;
#elif defined(__FreeBSD__) || defined (__APPLE__)
UNIMPLEMENTED;
#else
SpinBlock block(*cvtLock);
const char * result = ecvt(value, numDigits, decimal, sign);
if (!result)
return false;
strncpy(buffer, result, len);
return true;
#endif
}
bool safe_fcvt(size_t len, char * buffer, double value, int numPlaces, int * decimal, int * sign)
{
#ifdef _WIN32
return _fcvt_s(buffer, len, value, numPlaces, decimal, sign) == 0;
#elif defined(__FreeBSD__) || defined (__APPLE__)
UNIMPLEMENTED;
#else
SpinBlock block(*cvtLock);
const char * result = fcvt(value, numPlaces, decimal, sign);
if (!result)
return false;
strncpy(buffer, result, len);
return true;
#endif
}
//===========================================================================
#ifdef _WIN32
void MilliSleep(unsigned milli)
{
Sleep(milli);
}
#else
void MilliSleep(unsigned milli)
{
if (milli) {
unsigned target = msTick()+milli;
loop {
timespec sleepTime;
if (milli>=1000)
{
sleepTime.tv_sec = milli/1000;
milli %= 1000;
}
else
sleepTime.tv_sec = 0;
sleepTime.tv_nsec = milli * 1000000;
if (nanosleep(&sleepTime, NULL)==0)
break;
if (errno!=EINTR) {
PROGLOG("MilliSleep err %d",errno);
break;
}
milli = target-msTick();
if ((int)milli<=0)
break;
}
}
else
ThreadYield(); // 0 means yield
}
#endif
long atolong_l(const char * s,int l)
{
char t[32];
memcpy(t,s,l);
t[l]=0;
return atol(t);
}
int atoi_l(const char * s,int l)
{
char t[32];
memcpy(t,s,l);
t[l]=0;
return atoi(t);
}
__int64 atoi64_l(const char * s,int l)
{
__int64 result = 0;
char sign = '+';
while (l>0 && isspace(*s))
{
l--;
s++;
}
if (l>0 && (*s == '-' || *s == '+'))
{
sign = *s;
l--;
s++;
}
while (l>0 && isdigit(*s))
{
result = 10 * result + ((*s) - '0');
l--;
s++;
}
if (sign == '-')
return -result;
else
return result;
}
#ifndef _WIN32
static char *_itoa(unsigned long n, char *str, int b, bool sign)
{
char *s = str;
if (sign)
n = -n;
do
{
byte d = n % b;
*(s++) = d+((d<10)?'0':('a'-10));
}
while ((n /= b) > 0);
if (sign)
*(s++) = '-';
*s = '\0';
// reverse
char *s2 = str;
s--;
while (s2> 4) + '0';
*target++ = (next & 15) + '0';
tlen -= 2;
}
}
//-----------------------------------------------------------------------
HINSTANCE LoadSharedObject(const char *name, bool isGlobal, bool raiseOnError)
{
#if defined(_WIN32)
UINT oldMode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
#else
// don't think anything to do here.
#endif
DynamicScopeCtx scope;
StringBuffer tmp;
if (name&&isPathSepChar(*name)&&isPathSepChar(name[1])) {
RemoteFilename rfn;
rfn.setRemotePath(name);
SocketEndpoint ep;
if (!rfn.isLocal()) {
// I guess could copy to a temporary location but currently just fail
throw MakeStringException(-1,"LoadSharedObject: %s is not a local file",name);
}
name = rfn.getLocalPath(tmp).str();
}
#if defined(_WIN32)
HINSTANCE h = LoadLibrary(name);
if (!LoadSucceeded(h))
{
int errcode = GetLastError();
StringBuffer errmsg;
formatSystemError(errmsg, errcode);
//Strip trailing newlines - makes output/errors messages cleaner
unsigned len = errmsg.length();
while (len)
{
char c = errmsg.charAt(len-1);
if ((c != '\r') && (c != '\n'))
break;
len--;
}
errmsg.setLength(len);
DBGLOG("Error loading %s: %d - %s", name, errcode, errmsg.str());
if (raiseOnError)
throw MakeStringException(0, "Error loading %s: %d - %s", name, errcode, errmsg.str());
}
#else
HINSTANCE h = dlopen((char *)name, isGlobal ? RTLD_NOW|RTLD_GLOBAL : RTLD_NOW);
if(h == NULL)
{
StringBuffer dlErrorMsg(dlerror());
DBGLOG("Error loading %s: %s", name, dlErrorMsg.str());
if (raiseOnError)
throw MakeStringException(0, "Error loading %s: %s", name, dlErrorMsg.str());
}
#endif
scope.setSoContext(h);
#if defined(_WIN32)
SetErrorMode(oldMode);
#else
// don't think anything to do here.
#endif
return h;
}
void FreeSharedObject(HINSTANCE h)
{
ExitModuleObjects(h);
#if defined(_WIN32)
FreeLibrary(h);
#else
dlclose(h);
#endif
}
bool SharedObject::load(const char * dllName, bool isGlobal, bool raiseOnError)
{
unload();
#ifdef _WIN32
UINT oldMode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
if (dllName)
{
h=LoadSharedObject(dllName, isGlobal, raiseOnError);
bRefCounted = true;
}
else
{
h=GetModuleHandle(NULL);
bRefCounted = false;
}
SetErrorMode(oldMode);
#else
h=LoadSharedObject(dllName, isGlobal, raiseOnError);
bRefCounted = true;
#endif
if (!LoadSucceeded(h))
{
h = 0;
return false;
}
return true;
}
bool SharedObject::loadCurrentExecutable()
{
unload();
#ifdef _WIN32
h=GetModuleHandle(NULL);
bRefCounted = false;
#else
h=dlopen(NULL, RTLD_NOW);
bRefCounted = true;
#endif
return true;
}
void SharedObject::unload()
{
if (h && bRefCounted) FreeSharedObject(h);
h = 0;
}
//-----------------------------------------------------------------------
/*
We use a 64 bit number for generating temporaries so that we are unlikely to get any
clashes (being paranoid). This should mean if a temporary ID is allocated 1,000,000,000
times a second, then we won't repeat until 400 years later - assuming the machine stays
alive for that long.
Using a 32 bit number we loop after about an hour if we allocated 1,000,000 a second -
not an unreasonable estimate for the future.
*/
static unique_id_t nextTemporaryId;
StringBuffer & appendUniqueId(StringBuffer & target, unique_id_t value)
{
//Just generate a temporary name from the __int64 -
//Don't care about the format, therfore use base 32 in reverse order.
while (value)
{
unsigned next = ((unsigned)value) & 31;
value = value >> 5;
if (next < 10)
target.append((char)(next+'0'));
else
target.append((char)(next+'A'-10));
}
return target;
}
unique_id_t getUniqueId()
{
return ++nextTemporaryId;
}
StringBuffer & getUniqueId(StringBuffer & target)
{
return appendUniqueId(target, ++nextTemporaryId);
}
void resetUniqueId()
{
nextTemporaryId = 0;
}
//-----------------------------------------------------------------------
#define make_numtostr(VTYPE) \
int numtostr(char *dst, VTYPE _value) \
{ \
int c; \
unsigned VTYPE value; \
if (_value<0) \
{ \
*(dst++) = '-'; \
value = (unsigned VTYPE) -_value; \
c = 1; \
} \
else \
{ \
c = 0; \
value = _value; \
} \
char temp[11], *end = temp+10; \
char *tmp = end; \
*tmp = '\0'; \
\
while (value>=10) \
{ \
unsigned VTYPE next = value / 10; \
*(--tmp) = ((char)(value-next*10))+'0'; \
value = next; \
} \
*(--tmp) = ((char)value)+'0'; \
\
int diff = (int)(end-tmp); \
int i=diff+1; \
while (i--) *(dst++) = *(tmp++); \
\
return c+diff; \
}
#define make_unumtostr(VTYPE) \
int numtostr(char *dst, unsigned VTYPE value) \
{ \
char temp[11], *end = temp+10; \
char *tmp = end; \
*tmp = '\0'; \
\
while (value>=10) \
{ \
unsigned VTYPE next = value / 10; \
*(--tmp) = ((char)(value-next*10))+'0'; \
value = next; \
} \
*(--tmp) = ((char)value)+'0'; \
\
int diff = (int)(end-tmp); \
int i=diff+1; \
while (i--) *(dst++) = *(tmp++); \
\
return diff; \
}
make_numtostr(char);
make_numtostr(short);
make_numtostr(int);
make_numtostr(long);
make_unumtostr(char);
make_unumtostr(short);
make_unumtostr(int);
make_unumtostr(long);
int numtostr(char *dst, __int64 _value)
{
int c;
unsigned __int64 value;
if (_value<0)
{
*(dst++) = '-';
value = (unsigned __int64) -_value;
c = 1;
}
else
{
value = _value;
c = 0;
}
char temp[24], *end = temp+23, *tmp = end;
*tmp = '\0';
unsigned __int32 v3 = (unsigned __int32)(value / LLC(10000000000));
unsigned __int64 vv = value - ((unsigned __int64)v3*LLC(10000000000));
unsigned __int32 v2 = (unsigned __int32)(vv / 100000);
unsigned __int32 v1 = (unsigned __int32) (vv - (v2 * 100000));
unsigned __int32 next;
while (v1>=10)
{
next = v1/10;
*(--tmp) = ((char)(v1-next*10))+'0';
v1 = next;
}
*(--tmp) = ((char)v1)+'0';
if (v2)
{
char *d = end-5;
while (d != tmp)
*(--tmp) = '0';
while (v2>=10)
{
next = v2/10;
*(--tmp) = ((char)(v2-next*10))+'0';
v2 = next;
}
*(--tmp) = ((char)v2)+'0';
}
if (v3)
{
char *d = end-10;
while (d != tmp)
*(--tmp) = '0';
while (v3>=10)
{
next = v3/10;
*(--tmp) = ((char)(v3-next*10))+'0';
v3 = next;
}
*(--tmp) = ((char)v3)+'0';
}
int diff = (int)(end-tmp);
#ifdef USEMEMCPY
memcpy(dst, tmp, diff+1);
#else
int i=diff+1;
while (i--)
{ *(dst++) = *(tmp++);
}
#endif
return c+diff;
}
int numtostr(char *dst, unsigned __int64 value)
{
char temp[24], *end = temp+23, *tmp = end;
*tmp = '\0';
unsigned __int32 v3 = (unsigned __int32)(value / LLC(10000000000));
unsigned __int64 vv = value - ((unsigned __int64)v3*LLC(10000000000));
unsigned __int32 v2 = (unsigned __int32)(vv / 100000);
unsigned __int32 v1 = (unsigned __int32) (vv - (v2 * 100000));
unsigned __int32 next;
while (v1>=10)
{
next = v1/10;
*(--tmp) = ((char)(v1-next*10))+'0';
v1 = next;
}
*(--tmp) = ((char)v1)+'0';
if (v2)
{
char *d = end-5;
while (d != tmp)
*(--tmp) = '0';
while (v2>=10)
{
next = v2/10;
*(--tmp) = ((char)(v2-next*10))+'0';
v2 = next;
}
*(--tmp) = ((char)v2)+'0';
}
if (v3)
{
char *d = end-10;
while (d != tmp)
*(--tmp) = '0';
while (v3>=10)
{
next = v3/10;
*(--tmp) = ((char)(v3-next*10))+'0';
v3 = next;
}
*(--tmp) = ((char)v3)+'0';
}
int diff = (int)(end-tmp);
#ifdef USEMEMCPY
memcpy(dst, tmp, diff+1);
#else
int i=diff+1;
while (i--)
{ *(dst++) = *(tmp++);
}
#endif
return diff;
}
class CRandom: public CInterface, public IRandomNumberGenerator
{
// from Knuth if I remember correctly
#define HISTORYSIZE 55
#define HISTORYMAX (HISTORYSIZE-1)
unsigned history[HISTORYSIZE];
unsigned ptr;
unsigned lower;
public:
IMPLEMENT_IINTERFACE;
CRandom()
{
seed((unsigned)get_cycles_now());
}
void seed(unsigned su)
{
ptr = HISTORYMAX;
lower = 23;
double s = 91648253+su;
double a = 1389796;
double m = 2147483647;
unsigned i;
for (i=0;inext() & RAND_R_MAX);
}
#if ((RAND_R_MAX & (RAND_R_MAX+1)) != 0)
#error RAND_R_MAX expected to be 2^n-1
#endif
#endif
class CShuffledIterator: public CInterface, implements IShuffledIterator
{
CRandom rand;
unsigned *seq;
unsigned idx;
unsigned num;
public:
IMPLEMENT_IINTERFACE;
CShuffledIterator(unsigned _num)
{
num = _num;
idx = 0;
seq = NULL;
}
~CShuffledIterator()
{
delete [] seq;
}
bool first()
{
if (!seq)
seq = new unsigned[num];
idx = 0;
if (!num)
return false;
unsigned i;
for (i=0;i1) {
unsigned j = rand.next()%i; // NB i is correct here
i--;
unsigned t = seq[j];
seq[j] = seq[i];
seq[i] = t;
}
return true;
}
bool isValid()
{
return idx data to be encoded
// long length -> length in bytes of this data
// IIOStream &out -> Write the result into this stream
//
void JBASE64_Encode(const void *data, long length, IIOStream &out, bool addLineBreaks/*=true*/)
{
const unsigned char *in = static_cast(data);
unsigned char one;
unsigned char two;
unsigned char three;
long i;
for(i = 0; i < length && length - i >= 3;)
{
one = *(in + i++);
two = *(in + i++);
three = *(in + i++);
// 0x30 -> 0011 0000 b
// 0x3c -> 0011 1100 b
// 0x3f -> 0011 1111 b
//
writeCharToStream(out, BASE64_enc[one >> 2]);
writeCharToStream(out, BASE64_enc[((one << 4) & 0x30) | (two >> 4)]);
writeCharToStream(out, BASE64_enc[((two << 2) & 0x3c) | (three >> 6)]);
writeCharToStream(out, BASE64_enc[three & 0x3f]);
if(addLineBreaks && (i % 54 == 0))
{
writeCharToStream(out, '\n');
}
}
switch(length - i)
{
case 2:
one = *(in + i++);
two = *(in + i++);
writeCharToStream(out, BASE64_enc[one >> 2]);
writeCharToStream(out, BASE64_enc[((one << 4) & 0x30) | (two >> 4)]);
writeCharToStream(out, BASE64_enc[(two << 2) & 0x3c]);
writeCharToStream(out, pad);
break;
case 1:
one = *(in + i++);
writeCharToStream(out, BASE64_enc[one >> 2]);
writeCharToStream(out, BASE64_enc[(one << 4) & 0x30]);
writeCharToStream(out, pad);
writeCharToStream(out, pad);
break;
}
}
// JCSMORE could have IIOStream StringBuffer adapter inplace of below.
void JBASE64_Encode(const void *data, long length, StringBuffer &out, bool addLineBreaks/*=true*/)
{
const unsigned char *in = static_cast(data);
unsigned char one;
unsigned char two;
unsigned char three;
long i;
for(i = 0; i < length && length - i >= 3;)
{
one = *(in + i++);
two = *(in + i++);
three = *(in + i++);
// 0x30 -> 0011 0000 b
// 0x3c -> 0011 1100 b
// 0x3f -> 0011 1111 b
//
out.append(BASE64_enc[one >> 2]);
out.append(BASE64_enc[((one << 4) & 0x30) | (two >> 4)]);
out.append(BASE64_enc[((two << 2) & 0x3c) | (three >> 6)]);
out.append(BASE64_enc[three & 0x3f]);
if(addLineBreaks && (i % 54 == 0))
{
out.append('\n');
}
}
switch(length - i)
{
case 2:
one = *(in + i++);
two = *(in + i++);
out.append(BASE64_enc[one >> 2]);
out.append(BASE64_enc[((one << 4) & 0x30) | (two >> 4)]);
out.append(BASE64_enc[(two << 2) & 0x3c]);
out.append(pad);
break;
case 1:
one = *(in + i++);
out.append(BASE64_enc[one >> 2]);
out.append(BASE64_enc[(one << 4) & 0x30]);
out.append(pad);
out.append(pad);
break;
}
}
//
// Decode the input in a base64 format
//
// const char *in -> The string to be decoded
// StringBuffer & out -> Decoded string here
//
StringBuffer &JBASE64_Decode(ISimpleReadStream &in, StringBuffer &out)
{
unsigned char c1, cs[3];
unsigned char &c2 = *cs;
unsigned char &c3 = *(cs+1);
unsigned char &c4 = *(cs+2);
unsigned char d1, d2, d3, d4;
for(;;)
{
if (in.read(1, &c1))
break;
if (!c1)
break;
else if (!isspace(c1))
{
in.read(3, cs);
d1 = BASE64_dec[c1];
d2 = BASE64_dec[c2];
d3 = BASE64_dec[c3];
d4 = BASE64_dec[c4];
out.append((char)((d1 << 2) | (d2 >> 4)));
if(c3 == pad)
break;
out.append((char)((d2 << 4) | (d3 >> 2)));
if(c4 == pad)
break;
out.append((char)((d3 << 6) | d4));
}
}
return out;
}
MemoryBuffer &JBASE64_Decode(ISimpleReadStream &in, MemoryBuffer &out)
{
unsigned char c1, cs[3];
unsigned char &c2 = *cs;
unsigned char &c3 = *(cs+1);
unsigned char &c4 = *(cs+2);
unsigned char d1, d2, d3, d4;
for(;;)
{
if (in.read(1, &c1) != 1)
break;
if (!c1)
break;
else if (!isspace(c1))
{
in.read(3, cs);
d1 = BASE64_dec[c1];
d2 = BASE64_dec[c2];
d3 = BASE64_dec[c3];
d4 = BASE64_dec[c4];
out.append((char)((d1 << 2) | (d2 >> 4)));
if(c3 == pad)
break;
out.append((char)((d2 << 4) | (d3 >> 2)));
if(c4 == pad)
break;
out.append((char)((d3 << 6) | d4));
}
}
return out;
}
StringBuffer &JBASE64_Decode(const char *incs, StringBuffer &out)
{
unsigned char c1, c2, c3, c4;
unsigned char d1, d2, d3, d4;
for(;;)
{
c1 = *incs++;
if (!c1)
break;
else if (!isspace(c1))
{
c2 = *incs++;
c3 = *incs++;
c4 = *incs++;
d1 = BASE64_dec[c1];
d2 = BASE64_dec[c2];
d3 = BASE64_dec[c3];
d4 = BASE64_dec[c4];
out.append((char)((d1 << 2) | (d2 >> 4)));
if(c3 == pad)
break;
out.append((char)((d2 << 4) | (d3 >> 2)));
if(c4 == pad)
break;
out.append((char)((d3 << 6) | d4));
}
}
return out;
}
MemoryBuffer &JBASE64_Decode(const char *incs, MemoryBuffer &out)
{
unsigned char c1, c2, c3, c4;
unsigned char d1, d2, d3, d4;
for(;;)
{
c1 = *incs++;
if (!c1)
break;
else if (!isspace(c1))
{
c2 = *incs++;
c3 = *incs++;
c4 = *incs++;
d1 = BASE64_dec[c1];
d2 = BASE64_dec[c2];
d3 = BASE64_dec[c3];
d4 = BASE64_dec[c4];
out.append((char)((d1 << 2) | (d2 >> 4)));
if(c3 == pad)
break;
out.append((char)((d2 << 4) | (d3 >> 2)));
if(c4 == pad)
break;
out.append((char)((d3 << 6) | d4));
}
}
return out;
}
static inline void encode5_32(const byte *in,StringBuffer &out)
{
// 5 bytes in 8 out
static const char enc[33] =
"abcdefghijklmnopqrstuvwxyz"
"234567";
out.append(enc[(in[0] >> 3)]);
out.append(enc[((in[0] & 0x07) << 2) | (in[1] >> 6)]);
out.append(enc[(in[1] >> 1) & 0x1f]);
out.append(enc[((in[1] & 0x01) << 4) | (in[2] >> 4)]);
out.append(enc[((in[2] & 0x0f) << 1) | (in[3] >> 7)]);
out.append(enc[(in[3] >> 2) & 0x1f]);
out.append(enc[((in[3] & 0x03) << 3) | (in[4] >> 5)]);
out.append(enc[in[4] & 0x1f]);
}
void JBASE32_Encode(const char *in,StringBuffer &out)
{
size32_t len = (size32_t)strlen(in);
while (len>=5) {
encode5_32((const byte *)in,out);
in += 5;
len -= 5;
}
byte rest[5];
memcpy(rest,in,len);
memset(rest+len,0,5-len);
encode5_32(rest,out);
}
static inline byte decode_32c(char c)
{
byte b = (byte)c;
if (b>=97) {
if (b<=122)
return b-97;
}
else if ((b>=50)&&(b<=55))
return b-24;
return 0;
}
void JBASE32_Decode(const char *bi,StringBuffer &out)
{
loop {
byte b[8];
for (unsigned i=0;i<8;i++)
b[i] = decode_32c(*(bi++));
byte o;
o = ((b[0] & 0x1f) << 3) | ((b[1] & 0x1c) >> 2);
if (!o) return;
out.append(o);
o = ((b[1] & 0x03) << 6) | ((b[2] & 0x1f) << 1) | ((b[3] & 0x10) >> 4);
if (!o) return;
out.append(o);
o = ((b[3] & 0x0f) << 4) | ((b[4] & 0x1e) >> 1);
if (!o) return;
out.append(o);
o = ((b[4] & 0x01) << 7) | ((b[5] & 0x1f) << 2) | ((b[6] & 0x18) >> 3);
if (!o) return;
out.append(o);
o = ((b[6] & 0x07) << 5) | (b[7] & 0x1f);
if (!o) return;
out.append(o);
}
}
void DelimToStringArray(const char *csl, StringArray &dst, const char * delim,bool deldup)
{
if (!csl)
return;
const char *s = csl;
char c;
if (!delim)
c = ',';
else if (*delim&&!delim[1])
c = *delim;
else
c = 0;
StringBuffer str;
unsigned dstlen=dst.ordinality();
loop {
while (isspace(*s))
s++;
if (!*s&&(dst.ordinality()==dstlen)) // this check is to allow trailing separators (e.g. ",," is 3 (NULL) entries) but not generate an entry for ""
break;
const char *e = s;
while (*e) {
if (c) {
if (*e==c)
break;
}
else if (strchr(delim,*e))
break;
e++;
}
str.clear().append((size32_t)(e-s),s).clip();
if (deldup) {
const char *s1 = str.str();
unsigned i;
for (i=0;i=dst.ordinality())
dst.append(s1);
}
else
dst.append(str.str());
if (!*e)
break;
s = e+1;
}
}
void CslToStringArray(const char *csl, StringArray &dst,bool deldup)
{
DelimToStringArray(csl, dst, NULL , deldup);
}
#ifdef _WIN32
unsigned msTick() { return timeGetTime(); }
unsigned usTick()
{
static __int64 freq=0;
LARGE_INTEGER v;
if (!freq) {
if (QueryPerformanceFrequency(&v))
freq=v.QuadPart;
if (!freq)
return 0;
}
if (!QueryPerformanceCounter(&v))
return 0;
return (unsigned) ((v.QuadPart*1000000)/freq); // hope dividend doesn't overflow though it might
}
#else
#ifdef CLOCK_MONOTONIC
static bool use_gettimeofday=false;
unsigned msTick()
{
if (!use_gettimeofday) {
timespec tm;
if (clock_gettime(CLOCK_MONOTONIC, &tm)>=0)
return tm.tv_sec*1000+(tm.tv_nsec/1000000);
use_gettimeofday = true;
fprintf(stderr,"clock_gettime CLOCK_MONOTONIC returns %d",errno); // don't use PROGLOG
}
struct timeval tm;
gettimeofday(&tm,NULL);
return tm.tv_sec*1000+(tm.tv_usec/1000);
}
unsigned usTick()
{
if (!use_gettimeofday) {
timespec tm;
if (clock_gettime(CLOCK_MONOTONIC, &tm)>=0)
return tm.tv_sec*1000000+(tm.tv_nsec/1000);
use_gettimeofday = true;
fprintf(stderr,"clock_gettime CLOCK_MONOTONIC returns %d",errno); // don't use PROGLOG
}
struct timeval tm;
gettimeofday(&tm,NULL);
return tm.tv_sec*1000000+tm.tv_usec;
}
#else
#warning "clock_gettime(CLOCK_MONOTONIC) not supported"
unsigned msTick()
{
struct timeval tm;
gettimeofday(&tm,NULL);
return tm.tv_sec*1000+(tm.tv_usec/1000);
}
unsigned usTick()
{
struct timeval tm;
gettimeofday(&tm,NULL);
return tm.tv_sec*1000000+tm.tv_usec;
}
#endif
#endif
int make_daemon(bool printpid)
{
#ifndef _WIN32
pid_t pid, sid;
pid = fork();
if (pid < 0) {
PrintLog("fork failed\n");
return(EXIT_FAILURE);
}
if (pid > 0) {
if (printpid) {
int status;
waitpid(pid, &status, 0);
if (WEXITSTATUS(status)!=0)
return EXIT_FAILURE;
}
exit(EXIT_SUCCESS);
}
if ((sid = setsid()) < 0) {
PrintLog("error: set sid failed\n");
return(EXIT_FAILURE);
}
umask(0);
pid = fork(); // To prevent zombies
if (pid < 0) {
PrintLog("fork failed (2)\n");
return(EXIT_FAILURE);
}
if (pid > 0) {
if (printpid)
fprintf(stdout,"%d\n",pid);
exit(EXIT_SUCCESS);
}
freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
return(EXIT_SUCCESS);
#else
return 0;
#endif
}
#ifndef _WIN32
static int exec(const char* _command)
{
const char* tok=" \t";
size32_t sz=16, count=0;
char* command = strdup(_command);
char **args=(char**)malloc(sz*sizeof(char*));
for(char *temp, *p=strtok_r(command,tok,&temp);;p=strtok_r(NULL,tok,&temp))
{
if(count>=sz)
args=(char**)realloc(args,(sz*=2)*sizeof(char*));
args[count++]=p;
if(!p)
break;
}
int ret=execv(*args,args);
free(args);
free(command);
return ret;
}
#endif
bool callExternalProgram(const char *progname, const StringBuffer &input, StringBuffer &output, StringArray *env_in)
{
#ifdef _WIN32
StringBuffer envp;
if (env_in)
{
ForEachItemIn(index, *env_in)
envp.append(env_in->item(index)).append('\0');
}
win32::ProcessPipe p(progname, envp.length() ? envp.str() : NULL);
p.Write(input.str(), input.length());
p.CloseWrite();
char buf[4096];
for(;;)
{
// Read program output
DWORD bread = (DWORD)p.Read(buf, sizeof(buf));
if(!bread)
{
break;
}
output.append(bread, buf);
}
#else
struct Pipe
{
Pipe()
{
p[0]=p[1]=-1;
if(pipe(p))
throw MakeStringException(-1,"Pipe create failed: %d",errno);
}
~Pipe()
{
if(p[0]>=0)
close(p[0]);
if(p[1]>=0)
close(p[1]);
}
int Read(void *buf, size32_t nbyte)
{
return read(p[0],buf,nbyte);
}
int Write(const void *buf, size32_t nbyte)
{
return write(p[1],buf,nbyte);
}
void SetStdout()
{
if(p[1]!=STDOUT_FILENO)
{
if(dup2(p[1],STDOUT_FILENO)<0)
throw MakeStringException(-1,"stdout failed: %d",errno);
close(p[1]);
}
}
void SetStdin()
{
if(p[0]!=STDIN_FILENO)
{
if(dup2(p[0],STDIN_FILENO)<0)
throw MakeStringException(-1,"stdin failed: %d",errno);
close(p[0]);
}
}
void CloseRead()
{
close(p[0]);
p[0]=-1;
}
void CloseWrite()
{
close(p[1]);
p[1]=-1;
}
int p[2];
} pipe1, pipe2;
struct ChildProc
{
ChildProc()
{
if((pid=fork())<0)
throw MakeStringException(-1,"Fork failed: %d",errno);
}
~ChildProc()
{
if(!inChild())
{
for(;;)
{
if(waitpid(pid,0,0)>=0)
break;
else if (errno==EINTR)
{
}
}
}
}
bool inChild()
{
return pid==0;
}
int pid;
} fchild;
if(fchild.inChild())
{
pipe1.CloseWrite();
pipe1.SetStdin();
pipe2.CloseRead();
pipe2.SetStdout();
if (env_in)
{
const char *cmd[] = { progname, (char *)0 };
const char *envp[256]={0};
ForEachItemIn(index, *env_in)
envp[index]=env_in->item(index);
if(execve(progname, (char * const *)cmd, (char * const *)envp)<0)
{
ERRLOG("Exec failed %s %d",progname,errno);
exit(EXIT_FAILURE);
}
}
else
{
if(exec(progname)<0)
{
ERRLOG("Exec failed %s %d",progname,errno);
exit(EXIT_FAILURE);
}
}
}
else
{
pipe1.CloseRead();
pipe2.CloseWrite();
const char* data=input.str();
size32_t count=input.length();
for(;count>0;)
{
size32_t w=pipe1.Write(data,count);
if(w<0 && errno!=EINTR)
throw MakeStringException(-1,"Pipe write failed: %d",errno);
data+=w;
count-=w;
}
pipe1.CloseWrite();
char buf[4096];
for(;;)
{
size32_t r=pipe2.Read(buf, sizeof(buf));
if(r>0)
{
output.append(r, buf);
}
else if(r==0)
break;
else if(errno!=EINTR)
throw MakeStringException(-1,"Pipe read failed: %d",errno);
}
}
#endif
return true;
}
//Calculate the greatest common divisor using Euclid's method
unsigned __int64 greatestCommonDivisor(unsigned __int64 left, unsigned __int64 right)
{
loop
{
if (left > right)
{
if (right == 0)
return left;
left = left % right;
}
else
{
if (left == 0)
return right;
right = right % left;
}
}
}
//In a separate module to stop optimizer removing the surrounding catch.
void doStackProbe()
{
byte local;
const volatile byte * x = (const byte *)&local;
x[-4096];
}
#ifdef _WIN32
DWORD dwTlsIndex = -1;
CriticalSection tlscrit;
void initThreadLocal(int len, void* val)
{
{
CriticalBlock b(tlscrit);
if(dwTlsIndex == -1)
{
if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
throw MakeStringException(-1, "TlsAlloc failed");
}
}
LPVOID lpvData;
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != 0)
LocalFree((HLOCAL) lpvData);
// Initialize the TLS index for this thread.
lpvData = (LPVOID) LocalAlloc(LPTR, len);
memcpy((char*)lpvData, val, len);
if (! TlsSetValue(dwTlsIndex, lpvData))
throw MakeStringException(-1, "TlsSetValue error");
}
void* getThreadLocalVal()
{
if(dwTlsIndex == -1)
return NULL;
return TlsGetValue(dwTlsIndex);
}
void clearThreadLocal()
{
if(dwTlsIndex == -1)
return;
LPVOID lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != 0)
{
LocalFree((HLOCAL) lpvData);
if (! TlsSetValue(dwTlsIndex, NULL))
throw MakeStringException(-1, "TlsSetValue error");
}
}
#else
// Key for the thread-specific buffer
static pthread_key_t buffer_key;
// Once-only initialisation of the key
static pthread_once_t buffer_key_once = PTHREAD_ONCE_INIT;
// Free the thread-specific buffer
static void buffer_destroy(void * buf)
{
if(buf)
free(buf);
}
// Allocate the key
static void buffer_key_alloc()
{
pthread_key_create(&buffer_key, buffer_destroy);
DBGLOG("buffer_key=%d", buffer_key);
}
// Allocate the thread-specific buffer
void initThreadLocal(int len, void* val)
{
pthread_once(&buffer_key_once, buffer_key_alloc);
void* valbuf = malloc(len);
memcpy(valbuf, val, len);
pthread_setspecific(buffer_key, valbuf);
}
// Return the thread-specific buffer
void* getThreadLocalVal()
{
return (char *) pthread_getspecific(buffer_key);
}
void clearThreadLocal()
{
}
#endif
StringBuffer &expandMask(StringBuffer &buf, const char *mask, unsigned p, unsigned n)
{
const char *s=mask;
if (s)
while (*s) {
char next = *(s++);
if (next=='$') {
char pc = toupper(s[0]);
if (pc&&(s[1]=='$')) {
if (pc=='P') {
buf.append(p+1);
next = 0;
s+=2;
}
else if (pc=='N') {
buf.append(n);
next = 0;
s+=2;
}
}
}
if (next)
buf.append(next);
}
return buf;
}
static const char *findExtension(const char *fn)
{
if (!fn)
return NULL;
const char *ret = strchr(fn,'.');
if (ret) {
loop {
ret++;
const char *s = strchr(ret,'.');
if (!s)
break;
ret = s;
}
}
return ret;
}
bool matchesMask(const char *fn, const char *mask, unsigned p, unsigned n)
{
StringBuffer match;
expandMask(match,mask,p,n);
return (stricmp(fn,match.str())==0);
}
bool constructMask(StringAttr &attr, const char *fn, unsigned p, unsigned n)
{
StringBuffer buf;
const char *ext = findExtension(fn);
if (!ext)
return false;
buf.append((size32_t)(ext-fn),fn).append("_$P$_of_$N$");
if (matchesMask(fn,buf.str(),p,n)) {
attr.set(buf.str());
return true;
}
return false;
}
bool deduceMask(const char *fn, bool expandN, StringAttr &mask, unsigned &pret, unsigned &nret)
{
const char *e = findExtension(fn);
if (!e)
return false;
loop {
const char *s=e;
if (*s=='_') {
s++;
unsigned p = 0;
while (isdigit(*s))
p = p*10+*(s++)-'0';
if (p&&(memcmp(s,"_of_",4)==0)) {
s += 4;
pret = p-1;
p = 0;
while (isdigit(*s))
p = p*10+*(s++)-'0';
nret = p;
if (((*s==0)||(*s=='.'))&&(p>pret)) {
StringBuffer buf;
if (expandN)
buf.append((size32_t)(e-fn),fn).append("_$P$_of_").append(p);
else
buf.append((size32_t)(e-fn),fn).append("_$P$_of_$N$");
if (*s=='.')
buf.append(s);
mask.set(buf);
return true;
}
}
}
e--;
loop {
if (e==fn)
return false;
if (*(e-1)=='.')
break;
e--;
}
}
return false;
}
//==============================================================
#ifdef _WIN32
class CWindowsAuthenticatedUser: public CInterface, implements IAuthenticatedUser
{
StringAttr name;
HANDLE usertoken;
public:
IMPLEMENT_IINTERFACE;
CWindowsAuthenticatedUser()
{
usertoken = (HANDLE)-1;
}
~CWindowsAuthenticatedUser()
{
if (usertoken != (HANDLE)-1)
CloseHandle(usertoken);
}
bool login(const char *user, const char *passwd)
{
name.clear();
if (usertoken != (HANDLE)-1)
CloseHandle(usertoken);
StringBuffer domain("");
const char *ut = strchr(user,'\\');
if (ut) {
domain.clear().append((size32_t)(ut-user),user);
user = ut+1;
}
BOOL res = LogonUser((LPTSTR)user,(LPTSTR)(domain.length()==0?NULL:domain.str()),(LPTSTR)passwd,LOGON32_LOGON_NETWORK,LOGON32_PROVIDER_DEFAULT,&usertoken);
if (res==0)
return false;
name.set(user);
return true;
}
void impersonate()
{
if (!ImpersonateLoggedOnUser(usertoken))
throw MakeOsException(GetLastError());
}
void revert()
{
RevertToSelf();
}
const char *username()
{
return name.get();
}
};
IAuthenticatedUser *createAuthenticatedUser() { return new CWindowsAuthenticatedUser; }
#elif defined(__linux__)
class CLinuxAuthenticatedUser: public CInterface, implements IAuthenticatedUser
{
StringAttr name;
uid_t uid;
gid_t gid;
uid_t saveuid;
gid_t savegid;
int saveegrplen;
gid_t *saveegrp;
public:
IMPLEMENT_IINTERFACE;
bool login(const char *user, const char *passwd)
{
name.clear();
const char *ut = strchr(user,'\\');
if (ut)
user = ut+1; // remove windows domain
struct passwd *pw;
char *epasswd;
if ((pw = getpwnam(user)) == NULL)
return false;
struct spwd *spwd = getspnam(user);
if (spwd)
epasswd = spwd->sp_pwdp;
else
epasswd = pw->pw_passwd;
if (!epasswd||!*epasswd)
return false;
if (strcmp(crypt(passwd,epasswd),epasswd)!=0)
return false;
uid = pw->pw_uid;
gid = pw->pw_gid;
name.set(pw->pw_name);
return true;
}
void impersonate()
{
saveuid = geteuid();
savegid = getegid();
setegid(gid);
seteuid(uid);
}
void revert()
{
seteuid(saveuid);
setegid(savegid);
}
const char *username()
{
return name.get();
}
};
IAuthenticatedUser *createAuthenticatedUser() { return new CLinuxAuthenticatedUser; }
#elif defined(__FreeBSD__) || defined (__APPLE__)
IAuthenticatedUser *createAuthenticatedUser() { UNIMPLEMENTED; }
#endif
extern jlib_decl void serializeAtom(MemoryBuffer & target, _ATOM name)
{
StringBuffer lower(name->str());
lower.toLowerCase();
serialize(target, lower.toCharArray());
}
extern jlib_decl _ATOM deserializeAtom(MemoryBuffer & source)
{
StringAttr text;
deserialize(source, text);
if (text)
return createAtom(text);
return NULL;
}
//==============================================================
static inline void encode3_64(byte *in,StringBuffer &out)
{
// 3 bytes in 4 out
static const char enc[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
out.append(enc[in[0]>>2]);
out.append(enc[((in[0] << 4) & 0x30) | (in[1] >> 4)]);
out.append(enc[((in[1] << 2) & 0x3c) | (in[2] >> 6)]);
out.append(enc[in[2] & 0x3f]);
}
StringBuffer &genUUID(StringBuffer &out, bool nocase)
{ // returns a 24 char UUID for nocase=false or 32 char for nocase=true
static NonReentrantSpinLock lock;
lock.enter();
// could be quicker using statics
static unsigned uuidbin[5] = {0,0,0,0,0};
if (uuidbin[0]==0) {
queryHostIP().getNetAddress(sizeof(uuidbin[0]),uuidbin);
uuidbin[1] = (unsigned)GetCurrentProcessId();
}
time_t t;
time(&t);
uuidbin[2] = (unsigned)t;
uuidbin[3] = msTick();
uuidbin[4]++;
byte *in = (byte *)uuidbin;
if (nocase) {
encode5_32(in,out);
encode5_32(in+5,out);
encode5_32(in+10,out);
encode5_32(in+15,out);
}
else {
encode3_64(in,out);
encode3_64(in+3,out);
encode3_64(in+6,out);
encode3_64(in+9,out);
byte tmp[3]; // drop two msb bytes from msec time
tmp[0] = in[12];
tmp[1] = in[13];
tmp[2] = in[16];
encode3_64(tmp,out);
encode3_64(in+17,out);
}
lock.leave();
return out;
}
//==============================================================
class jlib_decl CNameCountTable : public AtomRefTable
{
public:
CNameCountTable(bool _nocase=false) : AtomRefTable(_nocase) { }
StringBuffer &dump(StringBuffer &str)
{
SuperHashIteratorOf iter(*this);
CriticalBlock b(crit);
ForEach (iter)
{
HashKeyElement &elem = iter.query();
str.append(elem.get()).append(", count = ").append(elem.queryReferences()).newline();
}
return str;
}
};
static CNameCountTable *namedCountHT;
MODULE_INIT(INIT_PRIORITY_SYSTEM)
{
namedCountHT = new CNameCountTable;
return true;
}
MODULE_EXIT()
{
delete namedCountHT;
}
NamedCount::NamedCount()
{
ht = NULL;
}
NamedCount::~NamedCount()
{
if (ht) namedCountHT->releaseKey(ht);
}
void NamedCount::set(const char *name)
{
ht = namedCountHT->queryCreate(name);
}
StringBuffer &dumpNamedCounts(StringBuffer &str)
{
return namedCountHT->dump(str);
}
//==============================================================
// class OffsetToString
OffsetToString::OffsetToString(offset_t offset)
{
#if defined(_MSC_VER) && !defined (_POSIX_) && (__STDC__ || _INTEGRAL_MAX_BITS < 64)
// fpos_t is defined as struct (see in VC)
__int64 v = offset.lopart + (offset.hipart<<32);
m_buffer.append(v);
#else
m_buffer.append(offset);
#endif
}
/* Gentoo libc version omits these symbols which are directly */
/* referenced by some 3rd party libraries (sybase, Hasp). Until these */
/* libs get updated, provide linkable symbols within jlib for these... */
#if defined(__linux__) && (__GNUC__ >= 3)
const jlib_decl unsigned short int *__ctype_b = *__ctype_b_loc ();
const jlib_decl __int32_t *__ctype_tolower = *__ctype_tolower_loc();
const jlib_decl __int32_t *__ctype_toupper = *__ctype_toupper_loc();
// There seems to be some debate about whether these are needed
//#elif (__GNUC__ >= 3)
//const unsigned short int *__ctype_b = *__ctype_b_loc ();
//const unsigned int *__ctype_tolower = *__ctype_tolower_loc();
//const unsigned int *__ctype_toupper = *__ctype_toupper_loc();
#endif
//==============================================================
// URL Password, username handling
/*
From ftp://ftp.isi.edu/in-notes/rfc1738.txt:
http://:@:/
Some or all of the parts ":@", ":",
":", and "/" may be excluded.
The user name (and password), if present, are followed by a
commercial at-sign "@". Within the user and password field, any ":",
"@", or "/" must be encoded.
*/
jlib_decl StringBuffer& encodeUrlUseridPassword(StringBuffer& out, const char* in)
{
for (const char* p = in; *p; p++)
{
switch(*p)
{
// mentioned in rfc1738
case ':': out.append("%3A"); break;
case '@': out.append("%40"); break;
case '/': out.append("%2F"); break;
// these are not in the spec, but IE/Firefox has trouble if left un-encoded
case '%': out.append("%25"); break;
// these are not necessary: both IE and firefox handle them correctly
/*
case '&': out.append("%26"); break;
case ' ': out.append("%20"); break;
*/
default: out.append(*p); break;
}
}
return out;
}
inline bool isHexChar(char c)
{
return (c>='0' && c<='9')
|| (c>='A' && c<='F')
|| (c>='a' && c<='f');
}
int hexValue(char c)
{
return (c>='0' && c<='9') ? c-'0' :
((c>='A' && (c<='F') ? c-'A'+10 : c-'a'+10));
}
jlib_decl StringBuffer& decodeUrlUseridPassword(StringBuffer& out, const char* in)
{
for (const char* p = in; *p; p++)
{
if (*p=='%' && isHexChar(*(p+1)) && isHexChar(*(p+2)) )
{
char c1 = *(p+1), c2 = *(p+2);
int x = (hexValue(c1)<<4) + hexValue(c2);
out.appendf("%c",x);
p += 2;
}
else
out.appendf("%c",*p);
}
return out;
}
StringBuffer jlib_decl passwordInput(const char* prompt, StringBuffer& passwd)
{
#ifdef _WIN32
printf("%s", prompt);
size32_t entrylen = passwd.length();
loop {
char c = getch();
if (c == '\r')
break;
if (c == '\b') {
if (passwd.length()>entrylen) {
printf("\b \b");
passwd.setLength(passwd.length()-1);
}
}
else {
passwd.append(c);
printf("*");
}
}
printf("\n");
#else
// unfortuantely linux tty can't easily support using '*' hiding
sigset_t saved_signals;
sigset_t set_signals;
struct termios saved_term;
struct termios set_term;
FILE *term = fopen(_PATH_TTY, "w+");
if (!term)
term = stdin;
int termfd = fileno(term);
fprintf(stdout, "%s", prompt);
fflush(stdout);
sigemptyset(&set_signals);
sigaddset(&set_signals, SIGINT);
sigaddset(&set_signals, SIGTSTP);
sigprocmask(SIG_BLOCK, &set_signals, &saved_signals);
tcgetattr(termfd, &saved_term);
set_term = saved_term;
set_term.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL);
tcsetattr(termfd, TCSAFLUSH, &set_term);
char c = EOF;
int rd = ::read(termfd,&c,1);
while ((rd==1)&&(c!='\r')&&(c!='\n')&&(c!=EOF)) {
passwd.append(c);
rd = ::read(termfd,&c,1);
}
int err = (rd<0)?errno:0;
tcsetattr(termfd, TCSAFLUSH, &saved_term);
sigprocmask(SIG_SETMASK, &saved_signals, 0);
if (term!=stdin)
fclose(term);
if (err)
throw MakeOsException(err);
#endif
return passwd;
}
StringBuffer & fillConfigurationDirectoryEntry(const char *dir,const char *name, const char *component, const char *instance, StringBuffer &dirout)
{
while (*dir) {
if (*dir=='[') {
if (memicmp(dir+1,"NAME]",5)==0) {
dirout.append(name);
dir += 5;
}
else if (memicmp(dir+1,"COMPONENT]",10)==0) {
dirout.append(component);
dir += 10;
}
else if (memicmp(dir+1,"INST]",5)==0){
dirout.append(instance);
dir += 5;
}
else
dirout.append('[');
}
else
dirout.append(*dir);
dir++;
}
return dirout;
}
IPropertyTree *getHPCCenvironment(const char *confloc)
{
if (!confloc)
#ifdef _WIN32
return NULL;
#else
{
StringBuffer EnvConfPath(CONFIG_DIR);
EnvConfPath.append(PATHSEPSTR).append("environment.conf");
confloc = EnvConfPath.str();
}
#endif
Owned props = createProperties(confloc);
if (props) {
StringBuffer envfile;
if (props->getProp("environment",envfile)&&envfile.length()) {
if (!isAbsolutePath(envfile.str())) {
StringBuffer tail(envfile);
splitDirTail(confloc,envfile.clear());
addPathSepChar(envfile).append(tail);
}
Owned file = createIFile(envfile.str());
if (file) {
Owned fileio = file->open(IFOread);
if (fileio)
return createPTree(*fileio);
}
}
}
return NULL;
}
static IPropertyTree *getOSSdirTree()
{
Owned envtree = getHPCCenvironment();
if (envtree) {
IPropertyTree *ret = envtree->queryPropTree("Software/Directories");
if (ret)
return createPTreeFromIPT(ret);
}
return NULL;
}
bool getConfigurationDirectory(const IPropertyTree *dirtree,const char *category, const char *component,const char *instance, StringBuffer &dirout)
{
if (!dirtree)
dirtree = getOSSdirTree();
if (dirtree&&category&&*category) {
const char *name = dirtree->queryProp("@name");
if (name&&*name) {
StringBuffer q("Category[@name=\"");
q.append(category).append("\"]");
IPropertyTree *cat = dirtree->queryPropTree(q.str()); // assume only 1
if (cat) {
IPropertyTree *over = NULL;
if (instance&&*instance) {
q.clear().append("Override[@instance=\"").append(instance).append("\"]");
Owned it1 = cat->getElements(q.str());
ForEach(*it1) {
IPropertyTree &o1 = it1->query();
if ((!component||!*component)) {
if (!over)
over = &o1;
}
else {
const char *comp = o1.queryProp("@component");
if (!comp||!*comp) {
if (!over)
over = &o1;
}
else if (strcmp(comp,component)==0) {
over = &o1;
break;
}
}
}
}
if (!over&&component&&*component) {
q.clear().append("Override[@component=\"").append(component).append("\"]");
Owned it2 = cat->getElements(q.str());
ForEach(*it2) {
IPropertyTree &o2 = it2->query();
if ((!instance||!*instance)) {
over = &o2;
break;
}
else {
const char *inst = o2.queryProp("@instance");
if (!inst||!*inst) {
over = &o2;
break;
}
}
}
}
const char *dir = over?over->queryProp("@dir"):cat->queryProp("@dir");
if (dir&&*dir) {
fillConfigurationDirectoryEntry(dir,name,component,instance,dirout);
return true;
}
}
}
}
return false;
}
const char * matchConfigurationDirectoryEntry(const char *path,const char *mask,StringBuffer &name, StringBuffer &component, StringBuffer &instance)
{
// first check matches from (and set any values)
// only handles simple masks currently
StringBuffer var;
PointerArray val;
const char *m = mask;
const char *p = path;
loop {
char c = *m;
if (!c)
break;
m++;
StringBuffer *out=NULL;
if (c=='[') {
if (memicmp(m,"NAME]",5)==0) {
out = &name;
m += 5;
}
else if (memicmp(m,"COMPONENT]",10)==0) {
out = &component;
m += 10;
}
else if (memicmp(m,"INST]",5)==0) {
out = &instance;
m += 5;
}
}
if (out) {
StringBuffer mtail;
while (*m&&!isPathSepChar(*m)&&(*m!='['))
mtail.append(*(m++));
StringBuffer ptail;
while (*p&&!isPathSepChar(*p))
ptail.append(*(p++));
if (ptail.length()clear().append(l,ptail.str());
}
else if (c!=*(p++))
return NULL;
}
if (!*p)
return p;
if (isPathSepChar(*p))
return p+1;
return NULL;
}
bool replaceConfigurationDirectoryEntry(const char *path,const char *frommask,const char *tomask,StringBuffer &out)
{
StringBuffer name;
StringBuffer comp;
StringBuffer inst;
const char *tail = matchConfigurationDirectoryEntry(path,frommask,name,comp,inst);
if (!tail)
return false;
fillConfigurationDirectoryEntry(tomask,name,comp,inst,out);
if (*tail)
addPathSepChar(out).append(tail);
return true;
}
const char * queryCurrentProcessName()
{
static CriticalSection sect;
static StringAttr processName;
CriticalBlock block(sect);
if (processName.isEmpty())
{
#if defined(WIN32)
const char *cmdline = GetCommandLine();
if (!cmdline) return false;
StringArray argv;
DelimToStringArray(cmdline, argv, " ");
if (0 == argv.ordinality())
return "";
const char *processPath = argv.item(0);
#elif defined(__linux__)
char link[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", link, PATH_MAX);
if (len == -1)
return "";
link[len] = '\0';
const char *processPath = link;
#else
const char *processPath = NULL;
return "";
#endif
if (!processPath)
return NULL;
StringBuffer path;
const char *tail = splitDirTail(processPath, path);
if (!tail)
return NULL;
processName.set(tail);
}
return processName.sget();
}
inline bool isOctChar(char c)
{
return (c>='0' && c<'8');
}
inline int octValue(char c)
{
return c-'0';
}
int parseCommandLine(const char * cmdline, MemoryBuffer &mb, const char** &argvout)
{
mb.append((char)0);
size32_t arg[256];
int argc = 0;
arg[0] = 0;
char quotechar = 0;
loop {
char c = *(cmdline++);
switch(c) {
case ' ':
case '\t':
if (quotechar)
break;
// fall through
case 0: {
if (arg[argc]) {
while (mb.length()>arg[argc]) {
size32_t l = mb.length()-1;
const byte * b = ((const byte *)mb.bufferBase())+l;
if ((*b!=' ')&&(*b!='\t'))
break;
mb.setLength(l);
}
if (mb.length()>arg[argc]) {
mb.append((char)0);
argc++;
}
if (c) {
if (argc==256)
throw MakeStringException(-1,"parseCommandLine: too many arguments");
arg[argc] = 0;
}
}
if (c)
continue;
argvout = (const char **)mb.reserve(argc*sizeof(const char *));
for (int i=0;i