3705 lines
86 KiB
C++
3705 lines
86 KiB
C++
#include "gb-include.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include "iana_charset.h"
|
|
#include "Unicode.h"
|
|
#include "SafeBuf.h"
|
|
#include "Words.h"
|
|
#include "Sections.h"
|
|
|
|
//static uint32_t utf8Decode ( char *p, char **next = NULL );
|
|
|
|
// // table for decoding utf8...says how many bytes in the character
|
|
// // based on value of first byte. 0 is an illegal value
|
|
// static int bytes_in_code[] = {
|
|
// 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
// 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
// 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
// 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
// 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
// 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0
|
|
// };
|
|
|
|
SafeBuf::SafeBuf(int32_t initSize, char *label ) {
|
|
if(initSize <= 0) initSize = 1;
|
|
m_capacity = initSize;
|
|
m_length = 0;
|
|
m_label = label;
|
|
m_buf = (char*)mrealloc(NULL, 0, m_capacity, m_label );
|
|
if(!m_buf) m_capacity = 0;
|
|
m_usingStack = false;
|
|
m_encoding = csUTF8;
|
|
}
|
|
|
|
void SafeBuf::constructor ( ) {
|
|
m_capacity = 0;
|
|
m_length = 0;
|
|
m_buf = NULL;
|
|
m_usingStack = false;
|
|
m_encoding = csUTF8;
|
|
m_label = NULL;
|
|
}
|
|
|
|
SafeBuf::SafeBuf() {
|
|
m_capacity = 0;
|
|
m_length = 0;
|
|
m_buf = NULL;
|
|
m_usingStack = false;
|
|
m_encoding = csUTF8;
|
|
m_label = NULL;
|
|
}
|
|
|
|
void SafeBuf::setLabel ( char *label ) {
|
|
m_label = label;
|
|
}
|
|
|
|
SafeBuf::SafeBuf(char* stackBuf, int32_t cap, char* label) {
|
|
m_usingStack = true;
|
|
m_capacity = cap;
|
|
m_buf = stackBuf;
|
|
m_length = 0;
|
|
m_encoding = csUTF8;
|
|
m_label = label;
|
|
}
|
|
|
|
SafeBuf::SafeBuf(char *heapBuf, int32_t bufMax, int32_t bytesInUse, bool ownData) {
|
|
// . If we don't own the data, treat it like a stack buffer
|
|
// so we won't attempt to free or realloc it.
|
|
// . If you already have data in this buffer, make sure
|
|
// to explicitly update length.
|
|
m_usingStack = !ownData;
|
|
m_capacity = bufMax;
|
|
m_buf = heapBuf;
|
|
m_length = bytesInUse;
|
|
m_encoding = csUTF8;
|
|
}
|
|
|
|
SafeBuf::~SafeBuf() {
|
|
destructor();
|
|
}
|
|
|
|
void SafeBuf::destructor() {
|
|
if(!m_usingStack && m_buf)
|
|
mfree(m_buf, m_capacity, "SafeBuf");
|
|
m_buf = NULL;
|
|
}
|
|
|
|
bool SafeBuf::setBuf(char *newBuf, int32_t bufMax, int32_t bytesInUse, bool ownData,
|
|
int16_t encoding ){
|
|
// . Passing in a null or a capacity smaller than the
|
|
// used portion of the buffer is pointless, we have
|
|
// more reliable functions for emptying a buffer.
|
|
if ( !newBuf || bufMax < bytesInUse || bytesInUse < 0 )
|
|
return false;
|
|
if ( !m_usingStack && m_buf )
|
|
mfree( m_buf, m_capacity, "SafeBuf" );
|
|
// . If we don't own the data, treat it like a stack buffer
|
|
// so we won't attempt to free or realloc it.
|
|
m_usingStack = !ownData;
|
|
m_buf = newBuf;
|
|
m_capacity = bufMax;
|
|
m_length = bytesInUse;
|
|
m_encoding = csUTF8;
|
|
if ( encoding > 0 ) m_encoding = encoding;
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::yieldBuf( char **bufPtr, int32_t *bufCapacity, int32_t *bytesInUse,
|
|
bool *ownData, int16_t *encoding ) {
|
|
// Set the references to our data.
|
|
if ( !bufPtr || !bufCapacity || ! bytesInUse || !ownData )
|
|
return false;
|
|
*bufPtr = m_buf;
|
|
*bufCapacity = m_capacity;
|
|
*bytesInUse = m_length;
|
|
*ownData = !m_usingStack;
|
|
if ( encoding ) {
|
|
*encoding = m_encoding;
|
|
}
|
|
// Clear out our data.
|
|
m_buf = NULL;
|
|
m_capacity = 0;
|
|
m_length = 0;
|
|
m_usingStack = false;
|
|
m_encoding = csUTF8;
|
|
return true;
|
|
}
|
|
|
|
void SafeBuf::purge() {
|
|
if ( !m_usingStack && m_buf )
|
|
mfree( m_buf, m_capacity, "SafeBuf" );
|
|
m_buf = NULL;
|
|
m_capacity = 0;
|
|
m_length = 0;
|
|
m_usingStack = false;
|
|
m_encoding = csUTF8;
|
|
}
|
|
|
|
bool SafeBuf::safePrintf(char *formatString , ...) {
|
|
va_list ap;
|
|
va_start ( ap, formatString);
|
|
int32_t tmp = vsnprintf ( m_buf + m_length, m_capacity - m_length,
|
|
formatString , ap );
|
|
va_end(ap);
|
|
if(tmp + m_length +1>= m_capacity) {
|
|
// +1 some space for a silent \0 at the end
|
|
if(!reserve(m_capacity + tmp + 1)) return false;
|
|
|
|
va_start ( ap, formatString);
|
|
tmp = vsnprintf ( m_buf + m_length, m_capacity - m_length,
|
|
formatString , ap );
|
|
va_end(ap);
|
|
}
|
|
m_length += tmp;
|
|
// this should not hurt anything
|
|
m_buf[m_length] = '\0';
|
|
return true;
|
|
}
|
|
|
|
|
|
bool SafeBuf::safeMemcpy(char *s, int32_t len) {
|
|
// put a silent \0 at the end
|
|
//int32_t tmp = len + m_length+1;
|
|
//if(tmp >= m_capacity ) {
|
|
if ( m_length + len > m_capacity ) {
|
|
if ( ! reserve(m_length+len) ) return false;
|
|
}
|
|
gbmemcpy(m_buf + m_length, s, len);
|
|
m_length = m_length + len; // tmp-1;
|
|
// this should not hurt anything
|
|
//m_buf[m_length] = '\0';
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::safeMemcpy_nospaces(char *s, int32_t len) {
|
|
// put a silent \0 at the end
|
|
int32_t tmp = len + m_length+1;
|
|
if(tmp >= m_capacity ) {
|
|
if ( ! reserve(tmp) ) return false;
|
|
}
|
|
for ( int32_t i = 0 ; i < len ; i++ ) {
|
|
if ( is_wspace_a(s[i]) ) continue;
|
|
m_buf[m_length++] = s[i];
|
|
}
|
|
//gbmemcpy(m_buf + m_length, s, len);
|
|
//m_length = tmp-1;
|
|
// this should not hurt anything
|
|
m_buf[m_length] = '\0';
|
|
return true;
|
|
}
|
|
|
|
#include "Words.h"
|
|
|
|
bool SafeBuf::safeMemcpy ( Words *w , int32_t a , int32_t b ) {
|
|
char *p = w->m_words[a];
|
|
char *pend = w->m_words[b-1] + w->m_wordLens[b-1];
|
|
return safeMemcpy ( p , pend - p );
|
|
}
|
|
|
|
char* SafeBuf::pushStr (char* str, uint32_t len) {
|
|
int32_t initLen = m_length;
|
|
bool status = safeMemcpy ( str , len );
|
|
status &= nullTerm();
|
|
m_length++; //count the null so it isn't overwritten
|
|
if(!status) return NULL;
|
|
return m_buf + initLen;
|
|
}
|
|
|
|
bool SafeBuf::pushPtr ( void *ptr ) {
|
|
if ( m_length + (int32_t)sizeof(char *) > m_capacity )
|
|
if(!reserve(sizeof(char *)))//2*m_capacity + 1))
|
|
return false;
|
|
*(char **)(m_buf+m_length) = (char *)ptr;
|
|
m_length += sizeof(char *);
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::pushLong ( int32_t i) {
|
|
if ( m_length + 4 > m_capacity )
|
|
if(!reserve(4))//2*m_capacity + 1))
|
|
return false;
|
|
*(int32_t *)(m_buf+m_length) = i;
|
|
m_length += 4;
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::pushLongLong ( int64_t i) {
|
|
if ( m_length + 8 > m_capacity && ! reserve(8) )
|
|
return false;
|
|
*(int64_t *)(m_buf+m_length) = i;
|
|
m_length += 8;
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::pushFloat ( float i) {
|
|
if ( m_length + 4 > m_capacity )
|
|
if(!reserve(4))
|
|
return false;
|
|
*(float *)(m_buf+m_length) = i;
|
|
m_length += 4;
|
|
return true;
|
|
}
|
|
|
|
// hack off trailing 0's
|
|
bool SafeBuf::printFloatPretty ( float f ) {
|
|
|
|
if ( m_length + 40 > m_capacity && ! reserve(40) )
|
|
return false;
|
|
|
|
char *p = m_buf + m_length;
|
|
|
|
int len = sprintf(p,"%f",f);
|
|
|
|
// need a decimal point to truncate trailing 0's
|
|
char *ss = strstr(p,".");
|
|
if ( ! ss ) {
|
|
m_length += len;
|
|
return true;
|
|
}
|
|
|
|
// hack off trailing zeros
|
|
char *e = p + len - 1;
|
|
int plen = len;
|
|
|
|
for ( ; e > p ; e-- ) {
|
|
if ( e[0] == '.' ) {
|
|
//e[0] = '\0';
|
|
plen--;
|
|
break;
|
|
}
|
|
if ( e[0] != '0' ) break;
|
|
//e[0] = '\0';
|
|
plen--;
|
|
}
|
|
|
|
m_length += plen;//e - p;
|
|
p[plen] = '\0';
|
|
return true;
|
|
}
|
|
|
|
|
|
bool SafeBuf::pushDouble ( double i) {
|
|
if ( m_length + (int32_t)sizeof(double) > m_capacity )
|
|
if(!reserve(sizeof(double)))
|
|
return false;
|
|
*(double *)(m_buf+m_length) = i;
|
|
m_length += sizeof(double);
|
|
return true;
|
|
}
|
|
|
|
int32_t SafeBuf::popLong ( ) {
|
|
if ( m_length < 4 ) { char *xx=NULL;*xx=0; }
|
|
int32_t ret = *(int32_t *)(m_buf+m_length-4);
|
|
m_length -= 4;
|
|
return ret;
|
|
}
|
|
|
|
float SafeBuf::popFloat ( ) {
|
|
if ( m_length < 4 ) { char *xx=NULL;*xx=0; }
|
|
float ret = *(float *)(m_buf+m_length-4);
|
|
m_length -= 4;
|
|
return ret;
|
|
}
|
|
|
|
int32_t SafeBuf::pad(const char ch, const int32_t len) {
|
|
for(int32_t i = 0; i < len; ++i)
|
|
pushChar(ch);
|
|
return len;
|
|
}
|
|
|
|
bool SafeBuf::cat(SafeBuf& c) {
|
|
return safeMemcpy(c.getBufStart(), c.length());
|
|
}
|
|
|
|
// returns false with g_errno set on error
|
|
bool SafeBuf::cat2 ( SafeBuf& c,
|
|
char *tagFilter1 ,
|
|
char *tagFilter2 ) {
|
|
|
|
//SafeBuf tmp;
|
|
// reserve 1MB to avoid excessive reallocs
|
|
//if ( ! tmp.reserve(1000000) ) return -1;
|
|
|
|
int32_t tlen1 = gbstrlen(tagFilter1);
|
|
int32_t tlen2 = gbstrlen(tagFilter2);
|
|
// parse our buffer up into words and sections
|
|
char *p = c.m_buf;
|
|
// ensure c.m_buf is NULL terminated so we do not overflow!
|
|
//if ( c.m_buf[c.m_length-1]
|
|
|
|
loop:
|
|
// scan it
|
|
for ( ; *p ; p++ ) {
|
|
// match?
|
|
if ( p[0] != tagFilter1[0] ) continue;
|
|
if ( p[1] != tagFilter1[1] ) continue;
|
|
if ( p[2] != tagFilter1[2] ) continue;
|
|
if ( p[3] != tagFilter1[3] ) continue;
|
|
if ( strncmp(p,tagFilter1,tlen1) ) continue;
|
|
// ok, got one!
|
|
break;
|
|
}
|
|
|
|
// no more?
|
|
if ( ! *p ) return true;
|
|
|
|
// scan for the end tag now
|
|
char *e = p + 1;
|
|
for ( ; *e ; e++ ) {
|
|
// match?
|
|
if ( e[0] != tagFilter2[0] ) continue;
|
|
if ( e[1] != tagFilter2[1] ) continue;
|
|
if ( e[2] != tagFilter2[2] ) continue;
|
|
if ( e[3] != tagFilter2[3] ) continue;
|
|
if ( strncmp(e,tagFilter2,tlen2) ) continue;
|
|
break;
|
|
}
|
|
|
|
// unmatched tag?
|
|
if ( ! *e ) { char *xx=NULL;*xx=0; }
|
|
|
|
// concatenate it to our buffer
|
|
if ( ! safeMemcpy ( p , e - p ) ) return false;
|
|
|
|
// go to next tag
|
|
p++;
|
|
|
|
// do more
|
|
goto loop;
|
|
}
|
|
|
|
|
|
bool SafeBuf::advance ( int32_t i ) {
|
|
if ( ! reserve ( i ) ) return false;
|
|
m_length += i;
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::reserve(int32_t i , char *label, bool clearIt ) {
|
|
|
|
// if we don't already have a label and they provided one, use it
|
|
if ( ! m_label ) {
|
|
if ( label ) m_label = label;
|
|
else m_label = "SafeBuf";
|
|
}
|
|
|
|
if(m_length + i > m_capacity) {
|
|
char *tmpBuf = m_buf;
|
|
int32_t tmpCap = m_capacity;
|
|
if(m_usingStack) {
|
|
m_buf = NULL;
|
|
m_capacity += i;
|
|
//if(m_capacity < 8) m_capacity = 8;
|
|
m_buf = (char*)mrealloc(m_buf, 0, m_capacity,m_label);
|
|
if(!m_buf) {
|
|
m_buf = tmpBuf;
|
|
log("safebuf: failed to reserve %" INT32 " bytes",
|
|
m_capacity);
|
|
m_capacity = tmpCap;
|
|
return false;
|
|
}
|
|
log(LOG_DEBUG, "query: safebuf switching to heap: %" INT32 "",
|
|
m_capacity);
|
|
gbmemcpy(m_buf, tmpBuf, m_length);
|
|
// reset to 0's?
|
|
if ( clearIt ) {
|
|
int32_t clearSize = m_capacity - tmpCap;
|
|
memset(m_buf+tmpCap,0,clearSize);
|
|
}
|
|
m_usingStack = false;
|
|
return true;
|
|
}
|
|
m_capacity += i;
|
|
//if(m_capacity < 8) m_capacity = 8;
|
|
m_buf = (char*)mrealloc(m_buf, tmpCap, m_capacity,m_label);
|
|
if(!m_buf) {
|
|
m_buf = tmpBuf;
|
|
log("safebuf: failed to realloc %" INT32 " bytes",m_capacity);
|
|
m_capacity = tmpCap;
|
|
return false;
|
|
}
|
|
// reset to 0's?
|
|
if ( clearIt ) {
|
|
int32_t clearSize = m_capacity - tmpCap;
|
|
memset(m_buf+tmpCap,0,clearSize);
|
|
}
|
|
log(LOG_DEBUG, "query: resize safebuf %" INT32 " to %" INT32 "",
|
|
tmpCap, m_capacity);
|
|
}
|
|
// reset to 0's?
|
|
//if ( ! clearIt ) return true;
|
|
//int32_t clearSize = m_capacity - m_length;
|
|
//memset(m_buf+m_length,0,clearSize);
|
|
return true;
|
|
}
|
|
|
|
|
|
//reserve this many bytes, if we need to alloc, we double the
|
|
//buffer size.
|
|
bool SafeBuf::reserve2x(int32_t i, char *label) {
|
|
//watch out for overflow!
|
|
if((m_capacity << 1) + i < m_capacity) return false;
|
|
if(i + m_length >= m_capacity)
|
|
return reserve(m_capacity + i,label);
|
|
else return true;
|
|
}
|
|
|
|
int32_t SafeBuf::saveToFile ( char *dir , char *filename ) {
|
|
char buf[1024];
|
|
snprintf(buf,1024,"%s/%s",dir,filename);
|
|
return dumpToFile ( buf );
|
|
}
|
|
|
|
int32_t SafeBuf::save ( char *fullFilename ) {
|
|
return dumpToFile ( fullFilename );
|
|
}
|
|
|
|
int32_t SafeBuf::dumpToFile(char *filename ) {
|
|
retry22:
|
|
int32_t fd = open ( filename , O_CREAT | O_WRONLY | O_TRUNC ,
|
|
getFileCreationFlags() );
|
|
//S_IRUSR |S_IWUSR |S_IRGRP |S_IWGRP| S_IROTH );
|
|
if ( fd < 0 ) {
|
|
// valgrind
|
|
if ( errno == EINTR ) goto retry22;
|
|
log("safebuf: Failed to open %s for writing: %s",
|
|
filename,mstrerror(errno));
|
|
return -1;
|
|
}
|
|
//logf(LOG_DEBUG, "test: safebuf %" INT32 " bytes written to %s",m_length,
|
|
// filename);
|
|
retry23:
|
|
int32_t bytes = write(fd, (char*)m_buf, m_length) ;
|
|
if ( bytes != m_length ) {
|
|
// valgrind
|
|
if ( bytes <= 0 && errno == EINTR ) goto retry23;
|
|
logf(LOG_DEBUG,"test: safebuf bad write %" INT32 " != %" INT32 ": %s",
|
|
bytes,m_length,mstrerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
return m_length;
|
|
}
|
|
|
|
// return -1 on error
|
|
int32_t SafeBuf::safeSave (char *filename ) {
|
|
retry22:
|
|
|
|
// first write to tmp file
|
|
char tmp[1024];
|
|
SafeBuf fn(tmp,1024);
|
|
fn.safePrintf( "%s.saving",filename );
|
|
|
|
int32_t fd = open ( fn.getBufStart() ,
|
|
O_CREAT | O_WRONLY | O_TRUNC ,
|
|
getFileCreationFlags() );
|
|
// S_IRUSR |S_IWUSR |S_IRGRP |S_IWGRP| S_IROTH );
|
|
if ( fd < 0 ) {
|
|
// valgrind
|
|
if ( errno == EINTR ) goto retry22;
|
|
log("safebuf: Failed to open %s for writing: %s",
|
|
fn.getBufStart(), mstrerror(errno));
|
|
return -1;
|
|
}
|
|
//logf(LOG_DEBUG, "test: safebuf %" INT32 " bytes written to %s",m_length,
|
|
// filename);
|
|
retry23:
|
|
int32_t bytes = write(fd, (char*)m_buf, m_length) ;
|
|
if ( bytes != m_length ) {
|
|
// valgrind
|
|
if ( bytes <= 0 && errno == EINTR ) goto retry23;
|
|
logf(LOG_DEBUG,"test: safebuf bad write %" INT32 " != %" INT32 ": %s",
|
|
bytes,m_length,mstrerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
|
|
// remove original file before replacing it
|
|
if ( access (filename , F_OK ) == 0) unlink ( filename );
|
|
|
|
// now move it to the actual filename
|
|
//int32_t status = ::rename ( fn.getBufStart() , filename );
|
|
int32_t status = ::link ( fn.getBufStart() , filename );
|
|
|
|
// note it
|
|
//log("sb: saved %s safely",filename);
|
|
|
|
if ( status == -1 ) {
|
|
//g_errno = errno;
|
|
log("sb: safeBuf::safeSave::link: %s",mstrerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return m_length;
|
|
}
|
|
|
|
|
|
int32_t SafeBuf::fillFromFile(char *dir,char *filename,char *label) {
|
|
m_label = label;
|
|
char buf[1024];
|
|
if ( dir ) snprintf(buf,1024,"%s/%s",dir,filename);
|
|
else snprintf(buf,1024,"%s",filename);
|
|
return fillFromFile ( buf );
|
|
}
|
|
|
|
char *SafeBuf::getNextLine ( char *p ) {
|
|
// skip till \n
|
|
for ( ; *p ; p++ )
|
|
if ( *p == '\n' ) break;
|
|
if ( *p == '\n' ) p++;
|
|
if ( *p == '\0' ) return NULL;
|
|
return p;
|
|
}
|
|
|
|
// returns -1 on error
|
|
int32_t SafeBuf::catFile(char *filename) {
|
|
SafeBuf sb2;
|
|
if ( sb2.fillFromFile(filename) < 0 ) return -1;
|
|
// add 1 for a null
|
|
if ( ! reserve ( sb2.length() + 1 ) ) return -1;
|
|
cat ( sb2 );
|
|
return length();
|
|
}
|
|
|
|
|
|
// returns -1 on error
|
|
int32_t SafeBuf::fillFromFile(char *filename) {
|
|
struct stat results;
|
|
if (stat(filename, &results) != 0) {
|
|
// An error occurred
|
|
log(LOG_DEBUG, "query: Failed to open %s for reading: ",
|
|
filename);
|
|
// 0 means does not exist or file is empty
|
|
return 0;
|
|
}
|
|
|
|
// The size of the file in bytes is in
|
|
// results.st_size
|
|
reserve(results.st_size+1);
|
|
|
|
retry:
|
|
int32_t fd = open ( filename , O_RDONLY , getFileCreationFlags() );
|
|
// S_IRUSR |S_IWUSR |S_IRGRP |S_IWGRP| S_IROTH );
|
|
if ( ! fd ) {
|
|
// valgrind
|
|
if ( errno == EINTR ) goto retry;
|
|
log(LOG_DEBUG, "query: Failed to open %s for reading: ",
|
|
filename);
|
|
// -1 means there was a read error of some sorts
|
|
return -1;//false;
|
|
}
|
|
retry2:
|
|
int32_t numRead = read(fd, m_buf+m_length, results.st_size);
|
|
// valgrind
|
|
if ( numRead<0 && errno == EINTR ) goto retry2;
|
|
close(fd);
|
|
// add a \0 for good meaure
|
|
if ( numRead >= 0 ) {
|
|
m_length += numRead;
|
|
m_buf[m_length] = '\0';
|
|
}
|
|
|
|
return numRead;
|
|
}
|
|
|
|
// safely print the string, converting Latin1 to Utf8
|
|
bool SafeBuf::safeLatin1ToUtf8(char *s, int32_t len) {
|
|
// check how far we're going to grow
|
|
int32_t tmp = len + m_length;
|
|
for ( int32_t i = 0; i < len; i++ ) {
|
|
unsigned char c = (unsigned char)s[i];
|
|
// check if this expands to 2 chars
|
|
if ( c >= 0x80 ) tmp++;
|
|
//windows-1252 extensions
|
|
//if (c >= 130 && c < 160) tmp++;
|
|
}
|
|
// make sure we have room
|
|
if ( tmp >= m_capacity ) {
|
|
int32_t expanded = tmp - (len + m_length);
|
|
if (!reserve( m_capacity + len + expanded))
|
|
return false;
|
|
}
|
|
// convert it over
|
|
char *p = m_buf + m_length;
|
|
/*
|
|
#if 0
|
|
for (int32_t i = 0; i < len; i++) {
|
|
unsigned char c = (unsigned char)s[i];
|
|
if (c < 0x80) {
|
|
*p = c;
|
|
p++;
|
|
}
|
|
else {
|
|
*p = (char)(0xc0 | (c >> 6 & 0x1f));
|
|
p++;
|
|
*p = (char)(0x80 | (c & 0x3f));
|
|
p++;
|
|
}
|
|
}
|
|
#else
|
|
*/
|
|
// use the iconv function
|
|
p += latin1ToUtf8(p, m_capacity-m_length, s, len);
|
|
//#endif
|
|
|
|
// set the new length
|
|
m_length = p - m_buf;
|
|
return true;
|
|
}
|
|
|
|
// safely print the string, converting Utf8 to Latin1
|
|
/*
|
|
bool SafeBuf::safeUtf8ToLatin1(char *s, int32_t len) {
|
|
// JAB: const-ness for the optimizer
|
|
char *next = NULL;
|
|
const char *current = s;
|
|
int32_t count = 0;
|
|
while (current && current <(s+len)) {
|
|
utf8Decode(current,&next);
|
|
// increment past this utf8 char
|
|
current = next;
|
|
count++;
|
|
}
|
|
// make sure we have room
|
|
if ( m_length + count >= m_capacity ) {
|
|
if (!reserve( 2 * m_capacity + count))
|
|
return false;
|
|
}
|
|
// convert it over
|
|
char *p = m_buf + m_length;
|
|
current = s;
|
|
while (current && current <(s+len)){
|
|
// get the decoded char
|
|
int32_t c32 = utf8Decode(current,&next);
|
|
// if it's under 256, print it, otherwise print ?
|
|
if ( c32 < 256 ) *p = (char)c32;
|
|
else *p = '?';
|
|
// increment past this utf8 char
|
|
current = next;
|
|
p++;
|
|
}
|
|
// set the new length
|
|
m_length += count;
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
// a special replace
|
|
bool SafeBuf::insert ( SafeBuf *c , int32_t insertPos ) {
|
|
return safeReplace ( c->getBufStart() ,
|
|
c->length() ,
|
|
insertPos ,
|
|
0 );
|
|
}
|
|
|
|
bool SafeBuf::insert ( char *s , int32_t insertPos ) {
|
|
return safeReplace ( s ,
|
|
gbstrlen(s) ,
|
|
insertPos ,
|
|
0 );
|
|
}
|
|
|
|
bool SafeBuf::insert2 ( char *s , int32_t slen , int32_t insertPos ) {
|
|
return safeReplace ( s ,
|
|
slen ,
|
|
insertPos ,
|
|
0 );
|
|
}
|
|
|
|
bool SafeBuf::replace ( char *src , char *dst ) {
|
|
int32_t len1 = gbstrlen(src);
|
|
int32_t len2 = gbstrlen(dst);
|
|
if ( len1 != len2 ) {
|
|
int32_t niceness = 0;
|
|
return safeReplace2 ( src , len1,
|
|
dst , len2,
|
|
niceness ,
|
|
0 );
|
|
}
|
|
for ( char *p = strstr ( m_buf , src ) ; p ; p = strstr(p+len1,src ) )
|
|
gbmemcpy ( p , dst , len2 );
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::removeChunk1 ( char *p , int32_t len ) {
|
|
int32_t off = p - m_buf;
|
|
return removeChunk2 ( off , len );
|
|
}
|
|
|
|
bool SafeBuf::removeChunk2 ( int32_t pos , int32_t len ) {
|
|
if ( len == 0 ) return true;
|
|
char *dst = m_buf + pos;
|
|
char *src = m_buf + pos + len;
|
|
int32_t moveLen = m_buf + m_length - src;
|
|
memmove(dst, src, moveLen);
|
|
m_length -= len;
|
|
m_buf[m_length] = '\0';
|
|
return true;
|
|
}
|
|
|
|
|
|
// replace string at "pos/replaceLen" with s/len
|
|
bool SafeBuf::safeReplace ( char *s, int32_t len, int32_t pos, int32_t replaceLen ) {
|
|
// make sure we have room
|
|
int32_t diff = len - replaceLen;
|
|
// add in one for the silent terminating \0
|
|
int32_t newLen = m_length + diff ;
|
|
if ( newLen+1 > m_capacity ) {
|
|
if ( !reserve ( 2 * newLen + 1 ) )
|
|
return false;
|
|
}
|
|
// shift memory over
|
|
if ( diff != 0 ) {
|
|
char *src = m_buf + (pos + replaceLen);
|
|
char *dst = src + diff;
|
|
int32_t movelen = m_length - (pos + replaceLen);
|
|
memmove(dst, src, movelen);
|
|
}
|
|
// replace
|
|
char *p = m_buf + pos;
|
|
gbmemcpy(p, s, len);
|
|
m_length = newLen;
|
|
// silent terminating \0
|
|
m_buf[m_length] = '\0';
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::safeReplace3 ( char *s, char *t , int32_t niceness ) {
|
|
if ( ! safeReplace2 ( s , gbstrlen(s) ,
|
|
t , gbstrlen(t) ,
|
|
niceness ) )
|
|
return false;
|
|
if ( ! nullTerm() )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// return false and set g_errno on error
|
|
bool SafeBuf::safeReplace2 ( char *s, int32_t slen,
|
|
char *t , int32_t tlen ,
|
|
int32_t niceness ,
|
|
int32_t startOff ) {
|
|
|
|
char *pend2 = m_buf + m_length - slen + 1;
|
|
int32_t count = 0;
|
|
for ( char *p = m_buf + startOff ; p < pend2 ; p++ ) {
|
|
// breathe
|
|
QUICKPOLL(niceness);
|
|
// search
|
|
if ( p[0] != s[0] ) continue;
|
|
// compare 2nd char
|
|
if ( slen >= 2 && p[1] != s[1] ) continue;
|
|
// check all chars now
|
|
if ( slen >= 3 && strncmp(p,s,slen) ) continue;
|
|
// count them
|
|
count++;
|
|
}
|
|
|
|
// nothing to replace? all done then
|
|
if ( count == 0 ) return true;
|
|
|
|
int32_t extra = (tlen - slen) * count;
|
|
// allocate new space
|
|
int32_t need = m_length + extra;
|
|
// make a new safebuf to copy into
|
|
char *bbb = (char *)mmalloc(need,"saferplc");
|
|
if ( ! bbb ) return false;
|
|
// do it
|
|
char *dst = bbb;
|
|
char *pend = m_buf + m_length;
|
|
// scan all
|
|
for ( char *p = m_buf ; p < pend ; p++ , dst++ ) {
|
|
// assume not a match
|
|
*dst = *p;
|
|
// breathe
|
|
QUICKPOLL(niceness);
|
|
// search
|
|
if ( p[0] != s[0] ) continue;
|
|
// must be big enough
|
|
if ( p + slen > pend ) continue;
|
|
// compare 2nd char
|
|
if ( slen >= 2 && p[1] != s[1] ) continue;
|
|
// check all chars now
|
|
if ( slen >= 3 && strncmp(p,s,slen) ) continue;
|
|
// undo copy
|
|
gbmemcpy ( dst , t , tlen );
|
|
// advance for that
|
|
dst += tlen - 1;
|
|
p += slen - 1;
|
|
}
|
|
// clear us
|
|
purge();
|
|
// now this is our new junk
|
|
bool status = safeMemcpy ( bbb , dst - bbb );
|
|
// clear what we had
|
|
mfree ( bbb , need , "saferplc");
|
|
return status;
|
|
}
|
|
|
|
|
|
bool SafeBuf::copyToken(char* s) {
|
|
char* p = s;
|
|
while(*p && !isspace(*p)) p++;
|
|
return safeMemcpy(s, (p - s));
|
|
}
|
|
|
|
|
|
bool SafeBuf::setEncoding(int16_t cs) {
|
|
//only support utf8 and latin1 encoding for now
|
|
if ((cs != csUTF8) &&
|
|
(cs != csISOLatin1)){
|
|
m_encoding = csUTF8; //default
|
|
return false;
|
|
}
|
|
m_encoding = cs;
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::utf8Encode2(char *s, int32_t len, bool encodeHTML,int32_t niceness) {
|
|
int32_t tmp = m_length;
|
|
if ( m_encoding == csUTF8 ) {
|
|
if (! safeMemcpy(s,len)) return false;
|
|
}
|
|
//else if ( m_encoding == csISOLatin1 ) {
|
|
// if (! safeUtf8ToLatin1(s,len)) return false;
|
|
//}
|
|
else
|
|
return false;
|
|
if (!encodeHTML) return true;
|
|
return htmlEncode(m_length-tmp,niceness);
|
|
}
|
|
|
|
|
|
|
|
bool SafeBuf::utf32Encode(UChar32* codePoints, int32_t cpLen) {
|
|
if(m_encoding != csUTF8) return safePrintf("FIXME %s:%i", __FILE__, __LINE__);
|
|
|
|
int32_t need = 0;
|
|
for(int32_t i = 0; i < cpLen;i++) need += utf8Size(codePoints[i]);
|
|
if(!reserve(need)) return false;
|
|
|
|
for(int32_t i = 0; i < cpLen;i++) {
|
|
m_length += ::utf8Encode(codePoints[i], m_buf + m_length);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
bool SafeBuf::utf32Encode(UChar32 c) {
|
|
if(!reserve2x(8)) return false;
|
|
|
|
if ( m_encoding == csUTF8 ) {
|
|
m_length += ::utf8Encode(c, m_buf + m_length);
|
|
return true;
|
|
}
|
|
if ( m_encoding == csISOLatin1 ) {
|
|
if(c < 128) {
|
|
*(m_buf + m_length++) = (char)c;
|
|
}
|
|
else {
|
|
*(m_buf + m_length++) = '?';
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
*/
|
|
|
|
bool SafeBuf::latin1Encode(char *s, int32_t len, bool encodeHTML,int32_t niceness) {
|
|
int32_t tmp = m_length;
|
|
switch(m_encoding) {
|
|
case csUTF8:
|
|
if (! safeLatin1ToUtf8(s,len)) return false;
|
|
break;
|
|
case csISOLatin1:
|
|
if (! safeMemcpy(s,len)) return false;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if (!encodeHTML) return true;
|
|
return htmlEncode(m_length-tmp,niceness);
|
|
}
|
|
|
|
/*
|
|
bool SafeBuf::utf16Encode(UChar *s, int32_t len, bool encodeHTML){
|
|
int32_t used=0;
|
|
// string could be up to 4 bytes per character
|
|
if (!reserve( len*4))
|
|
return false;
|
|
|
|
switch(m_encoding) {
|
|
case csUTF8:
|
|
used = utf16ToUtf8(m_buf+m_length,
|
|
m_capacity-m_length,
|
|
s,len);
|
|
m_length += used;
|
|
break;
|
|
case csISOLatin1:
|
|
used = utf16ToLatin1(m_buf+m_length,
|
|
m_capacity-m_length,
|
|
s,len);
|
|
m_length += used;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if (!encodeHTML) return (used > 0);
|
|
return htmlEncode(used);
|
|
}
|
|
*/
|
|
|
|
bool SafeBuf::utf8CdataEncode(char *s, int32_t len) {
|
|
int32_t len1 = m_length;
|
|
bool r;
|
|
if ( m_encoding == csUTF8 )
|
|
r = safeMemcpy(s,len);
|
|
//else if ( m_encoding == csISOLatin1 )
|
|
// r = safeUtf8ToLatin1(s,len);
|
|
else
|
|
return false;
|
|
if ( !r ) return false;
|
|
// check the written section for bad characters
|
|
int32_t p = len1;
|
|
while ( p < m_length-2 ) {
|
|
if ( m_buf[p]==']' && m_buf[p+1]==']' && m_buf[p+2]=='>') {
|
|
// rewrite the > as >
|
|
safeReplace(">", 3, p+2, 1);
|
|
}
|
|
p++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
bool SafeBuf::latin1CdataEncode(char *s, int32_t len) {
|
|
int32_t len1 = m_length;
|
|
bool r;
|
|
switch(m_encoding) {
|
|
case csUTF8:
|
|
r = safeLatin1ToUtf8(s,len);
|
|
break;
|
|
case csISOLatin1:
|
|
r = safeMemcpy(s,len);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if ( !r ) return false;
|
|
// check the written section for bad characters
|
|
int32_t p = len1;
|
|
while ( p < m_length-2 ) {
|
|
if ( m_buf[p]==']' && m_buf[p+1]==']' && m_buf[p+2]=='>') {
|
|
// rewrite the > as >
|
|
safeReplace(">", 3, p+2, 1);
|
|
}
|
|
p++;
|
|
}
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
bool SafeBuf::utf16CdataEncode(UChar *s, int32_t len){
|
|
int32_t len1 = m_length;
|
|
int32_t used;
|
|
bool r;
|
|
|
|
// string could be up to 4 bytes per character
|
|
if (!reserve( len*4))
|
|
return false;
|
|
|
|
switch(m_encoding) {
|
|
case csUTF8:
|
|
used = utf16ToUtf8(m_buf+m_length,
|
|
m_capacity-m_length,
|
|
s,len);
|
|
m_length += used;
|
|
r = (used > 0);
|
|
break;
|
|
case csISOLatin1:
|
|
used = utf16ToLatin1(m_buf+m_length,
|
|
m_capacity-m_length,
|
|
s,len);
|
|
m_length += used;
|
|
r = (used > 0);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if ( !r ) return false;
|
|
// check the written section for bad characters
|
|
int32_t p = len1;
|
|
while ( p < m_length-2 ) {
|
|
if ( m_buf[p]==']' && m_buf[p+1]==']' && m_buf[p+2]=='>') {
|
|
// rewrite the > as >
|
|
safeReplace(">", 3, p+2, 1);
|
|
}
|
|
p++;
|
|
}
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
bool SafeBuf::latin1HtmlEncode(char *s, int32_t len,int32_t niceness) {
|
|
int32_t len1 = m_length;
|
|
bool r;
|
|
switch(m_encoding) {
|
|
case csUTF8:
|
|
r = safeLatin1ToUtf8(s,len);
|
|
break;
|
|
case csISOLatin1:
|
|
r = safeMemcpy(s,len);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if ( !r ) return false;
|
|
// check the written section for bad characters
|
|
int32_t p = len1;
|
|
while ( p < m_length ) {
|
|
QUICKPOLL(niceness);
|
|
if ( m_buf[p]=='>' ) {
|
|
// rewrite the > as >
|
|
safeReplace(">", 4, p, 1);
|
|
}
|
|
else if ( m_buf[p]=='<' ) {
|
|
// rewrite the < as <
|
|
safeReplace("<", 4, p, 1);
|
|
}
|
|
else if ( m_buf[p]=='&' ) {
|
|
// rewrite the & as &
|
|
safeReplace("&", 5, p, 1);
|
|
}
|
|
p++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
bool SafeBuf::utf16HtmlEncode(UChar *s, int32_t len){
|
|
int32_t len1 = m_length;
|
|
int32_t used;
|
|
bool r;
|
|
switch(m_encoding) {
|
|
case csUTF8:
|
|
used = utf16ToUtf8(m_buf+m_length,
|
|
m_capacity-m_length,
|
|
s,len);
|
|
m_length += used;
|
|
r = (used > 0);
|
|
break;
|
|
case csISOLatin1:
|
|
used = utf16ToLatin1(m_buf+m_length,
|
|
m_capacity-m_length,
|
|
s,len);
|
|
m_length += used;
|
|
r = (used > 0);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if ( !r ) return false;
|
|
// check the written section for bad characters
|
|
int32_t p = len1;
|
|
while ( p < m_length ) {
|
|
if ( m_buf[p]=='>' ) {
|
|
// rewrite the > as >
|
|
safeReplace(">", 4, p, 1);
|
|
}
|
|
else if ( m_buf[p]=='<' ) {
|
|
// rewrite the < as <
|
|
safeReplace("<", 4, p, 1);
|
|
}
|
|
else if ( m_buf[p]=='&' ) {
|
|
// rewrite the & as &
|
|
safeReplace("&", 5, p, 1);
|
|
}
|
|
p++;
|
|
}
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
bool SafeBuf::cdataEncode ( char *s ) {
|
|
return safeCdataMemcpy(s,gbstrlen(s));
|
|
}
|
|
|
|
bool SafeBuf::cdataEncode ( char *s , int32_t len ) {
|
|
return safeCdataMemcpy(s,len);
|
|
}
|
|
|
|
|
|
bool SafeBuf::safeCdataMemcpy ( char *s, int32_t len ) {
|
|
int32_t len1 = m_length;
|
|
if ( !safeMemcpy(s,len) )
|
|
return false;
|
|
// check the written section for bad characters
|
|
int32_t p = len1;
|
|
while ( p < m_length-2 ) {
|
|
if ( m_buf[p]==']' && m_buf[p+1]==']' && m_buf[p+2]=='>') {
|
|
// rewrite the > as >
|
|
safeReplace(">", 3, p+2, 1);
|
|
}
|
|
p++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::htmlEncode(char *s, int32_t lenArg, bool encodePoundSign ,
|
|
int32_t niceness , int32_t truncateLen ) {
|
|
//bool convertUtf8CharsToEntity ) {
|
|
// . we assume we are encoding into utf8
|
|
// . sanity check
|
|
if ( m_encoding == csUTF16 ) { char *xx = NULL; *xx = 0; }
|
|
|
|
// the new truncation logic
|
|
int32_t len = lenArg;
|
|
bool truncate = false;
|
|
int32_t extra = 0;
|
|
if ( truncateLen > 0 && lenArg > truncateLen ) {
|
|
len = truncateLen;
|
|
truncate = true;
|
|
extra = 4;
|
|
}
|
|
|
|
// alloc some space if we need to. add a byte for NULL termination.
|
|
if( m_length+len+1+extra>=m_capacity&&!reserve(m_capacity+len+1+extra))
|
|
return false;
|
|
// tmp vars
|
|
char *t = m_buf + m_length;
|
|
char *tend = m_buf + m_capacity;
|
|
// scan through all
|
|
char *send = s + len;
|
|
|
|
|
|
|
|
for ( ; s < send ; s++ ) {
|
|
// breathe
|
|
QUICKPOLL ( niceness );
|
|
// ensure we have enough room
|
|
if ( t + 12 >= tend ) {
|
|
// save progress
|
|
int32_t written = t - m_buf;
|
|
if ( ! reserve (m_capacity + 100) ) return false;
|
|
// these might have changed, so set them again
|
|
t = m_buf + written;
|
|
tend = m_buf + m_capacity;
|
|
}
|
|
// this is only used to encode stuff for the validation
|
|
// routine (see XmlDoc::validateOutput). like storing the
|
|
// event tile or description ultimately unto a checkboxspan.*
|
|
// txt file, so don't mess with these punct chars either
|
|
|
|
// convert it?
|
|
if ( *s == '"' ) {
|
|
*t++ = '&';
|
|
*t++ = '#';
|
|
*t++ = '3';
|
|
*t++ = '4';
|
|
*t++ = ';';
|
|
continue;
|
|
}
|
|
if ( *s == '<' ) {
|
|
*t++ = '&';
|
|
*t++ = 'l';
|
|
*t++ = 't';
|
|
*t++ = ';';
|
|
continue;
|
|
}
|
|
if ( *s == '>' ) {
|
|
*t++ = '&';
|
|
*t++ = 'g';
|
|
*t++ = 't';
|
|
*t++ = ';';
|
|
continue;
|
|
}
|
|
if ( *s == '&' ) { // && ! convertUtf8CharsToEntity ) {
|
|
*t++ = '&';
|
|
*t++ = 'a';
|
|
*t++ = 'm';
|
|
*t++ = 'p';
|
|
*t++ = ';';
|
|
continue;
|
|
}
|
|
if ( *s == '#' && encodePoundSign ) {
|
|
*t++ = '&';
|
|
*t++ = '#';
|
|
*t++ = '0';
|
|
*t++ = '3';
|
|
*t++ = '5';
|
|
*t++ = ';';
|
|
continue;
|
|
}
|
|
// our own specially decoded entities!
|
|
if ( *s == '+' && s[1]=='!' && s[2]=='-' ) {
|
|
*t++ = '&';
|
|
*t++ = 'l';
|
|
*t++ = 't';
|
|
*t++ = ';';
|
|
s += 2;
|
|
continue;
|
|
}
|
|
// our own specially decoded entities!
|
|
if ( *s == '-' && s[1]=='!' && s[2]=='+' ) {
|
|
*t++ = '&';
|
|
*t++ = 'g';
|
|
*t++ = 't';
|
|
*t++ = ';';
|
|
s += 2;
|
|
continue;
|
|
}
|
|
*t++ = *s;
|
|
}
|
|
|
|
if ( truncate ) {
|
|
gbmemcpy ( t , "... " , 4 );
|
|
t += 4;
|
|
}
|
|
|
|
*t = '\0';
|
|
// update the used buf length
|
|
m_length = t - m_buf ;
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
|
|
bool SafeBuf::javascriptEncode(char *s, int32_t len ) {
|
|
// . we assume we are encoding into utf8
|
|
// . sanity check
|
|
if ( m_encoding == csUTF16 ) { char *xx = NULL; *xx = 0; }
|
|
// alloc some space if we need to. add a byte for NULL termination.
|
|
if(m_length+len+1>=m_capacity && !reserve(m_capacity+len))return false;
|
|
// tmp vars
|
|
char *t = m_buf + m_length;
|
|
char *tend = m_buf + m_capacity;
|
|
// scan through all
|
|
char *send = s + len;
|
|
for ( ; s < send ; s++ ) {
|
|
// ensure we have enough room
|
|
if ( t + 12 >= tend ) {
|
|
// save progress
|
|
int32_t written = t - m_buf;
|
|
if ( ! reserve (m_capacity + 100) ) return false;
|
|
// these might have changed, so set them again
|
|
t = m_buf + written;
|
|
tend = m_buf + m_capacity;
|
|
}
|
|
// this is only used to encode stuff for the validation
|
|
// routine (see XmlDoc::validateOutput). like storing the
|
|
// event tile or description ultimately unto a checkboxspan.*
|
|
// txt file, so don't mess with these punct chars either
|
|
|
|
|
|
// javascript converts all spaces to plusses when it passes
|
|
// the content to the web server so if we have a plus
|
|
// and we care about it, we gotta convert it otherwise it
|
|
// gets converted back to a space
|
|
if ( *s == '+' ) {
|
|
*t++ = ' ';
|
|
continue;
|
|
}
|
|
|
|
// i don't think we need to encode these but because
|
|
// the checkboxspans.* files already had them encoded
|
|
// let's keep encoding < and >
|
|
if ( *s == '<' ) {
|
|
*t++ = '&';
|
|
*t++ = 'l';
|
|
*t++ = 't';
|
|
*t++ = ';';
|
|
continue;
|
|
}
|
|
if ( *s == '>' ) {
|
|
*t++ = '&';
|
|
*t++ = 'g';
|
|
*t++ = 't';
|
|
*t++ = ';';
|
|
continue;
|
|
}
|
|
|
|
// i guess the javascript encodes these when the checkbox
|
|
// is clicked, so let's keep them encoded too
|
|
if ( *s == '&' ) {
|
|
*t++ = '&';
|
|
*t++ = 'a';
|
|
*t++ = 'm';
|
|
*t++ = 'p';
|
|
*t++ = ';';
|
|
continue;
|
|
}
|
|
|
|
// our own specially decoded entities!
|
|
if ( *s == '+' && s[1]=='!' && s[2]=='-' ) {
|
|
*t++ = '&';
|
|
*t++ = 'l';
|
|
*t++ = 't';
|
|
*t++ = ';';
|
|
s += 2;
|
|
continue;
|
|
}
|
|
// our own specially decoded entities!
|
|
if ( *s == '-' && s[1]=='!' && s[2]=='+' ) {
|
|
*t++ = '&';
|
|
*t++ = 'g';
|
|
*t++ = 't';
|
|
*t++ = ';';
|
|
s += 2;
|
|
continue;
|
|
}
|
|
// if s is utf8, convert it?
|
|
// . this is now an official HACK for Address and
|
|
// Events::print() to remove the utf8 chars so that
|
|
// the senddiv() javascript doesn't convert them to
|
|
// %uxxxx format in the url because we can't convert them
|
|
// back for XmlDoc::getDivId()!!
|
|
if ( getUtf8CharSize(s)>1 ) {
|
|
//! is_ascii ( *s ) ) {
|
|
//*t++ = '&';
|
|
//*t++ = '#';
|
|
//*t++ = 'x';
|
|
*t++ = 'u';
|
|
// loop over the bytes
|
|
int32_t numBytes = getUtf8CharSize(s);
|
|
// sanity
|
|
if ( numBytes >= 5 ) { char *xx=NULL;*xx=0; }
|
|
// write out each bytes as two chars each
|
|
for ( int32_t k = 0 ; k < numBytes ; k++ , s++ ) {
|
|
// store hex digit
|
|
unsigned char v = ((unsigned char)*s)/16 ;
|
|
if ( v < 10 ) v += '0';
|
|
else v += 'A' - 10;
|
|
*t++ = v;
|
|
// store second hex digit
|
|
v = ((unsigned char)*s) & 0x0f ;
|
|
if ( v < 10 ) v += '0';
|
|
else v += 'A' - 10;
|
|
*t++ = v;
|
|
}
|
|
// backup for next char
|
|
s--;
|
|
continue;
|
|
}
|
|
|
|
*t++ = *s;
|
|
}
|
|
*t = '\0';
|
|
// update the used buf length
|
|
m_length = t - m_buf ;
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
|
|
bool SafeBuf::htmlEncode(char *s) {
|
|
return htmlEncode(s,gbstrlen(s),true,0);
|
|
}
|
|
|
|
// scan the last "len" characters for entities to encode
|
|
bool SafeBuf::htmlEncode(int32_t len, int32_t niceness ){
|
|
for (int32_t i = m_length-len; i < m_length ; i++){
|
|
|
|
QUICKPOLL ( niceness );
|
|
|
|
if ( m_buf[i] == '"' ) {
|
|
if (!safeReplace(""", 4, i, 1))
|
|
return false;
|
|
continue;
|
|
}
|
|
if ( m_buf[i] == '<' ) {
|
|
if (!safeReplace("<", 4, i, 1))
|
|
return false;
|
|
continue;
|
|
}
|
|
if ( m_buf[i] == '>' ) {
|
|
if (!safeReplace(">", 4, i, 1))
|
|
return false;
|
|
continue;
|
|
}
|
|
if ( m_buf[i] == '&' ) {
|
|
if (!safeReplace("&", 5, i, 1))
|
|
return false;
|
|
continue;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// a static buffer for speed
|
|
static char s_ut[256];
|
|
static bool s_init23 = false;
|
|
|
|
void initTable ( ) {
|
|
if ( s_init23 ) return;
|
|
s_init23 = true;
|
|
for ( int32_t c = 0 ; c <= 255 ; c++ ) {
|
|
// assume we must encode it
|
|
s_ut[c] = 1;
|
|
if ( ! is_ascii ( (unsigned char)c ) ) continue;
|
|
if ( c == ' ' ) continue;
|
|
if ( c == '&' ) continue;
|
|
if ( c == '"' ) continue;
|
|
if ( c == '+' ) continue;
|
|
if ( c == '%' ) continue;
|
|
if ( c == '#' ) continue;
|
|
if ( c == '<' ) continue;
|
|
if ( c == '>' ) continue;
|
|
if ( c == '?' ) continue;
|
|
if ( c == ':' ) continue;
|
|
if ( c == '/' ) continue;
|
|
// no need to encode it!
|
|
s_ut[c] = 0;
|
|
}
|
|
}
|
|
|
|
// url encode the whole buffer
|
|
bool SafeBuf::urlEncodeAllBuf ( bool spaceToPlus ) {
|
|
// this makes things faster
|
|
if ( ! s_init23 ) initTable();
|
|
// how many chars do we need?
|
|
char *bs = m_buf;
|
|
int32_t size = 0;
|
|
int32_t need = 0;
|
|
for ( ; *bs ; bs++ ) {
|
|
// one char is one byte
|
|
size++;
|
|
// skip if no encoding required
|
|
if ( s_ut[(unsigned char)*bs] == 0 ) continue;
|
|
// spaces are ok, just a single + byte
|
|
if ( *bs == ' ' && spaceToPlus ) continue;
|
|
// will need to do %ff type of thing
|
|
need += 2;
|
|
}
|
|
// incorporate abse size
|
|
need += size;
|
|
// null term
|
|
need++;
|
|
// ensure enough room
|
|
if ( ! reserve ( need ) ) return false;
|
|
// reset s in case got reallocated
|
|
char *src = m_buf + size - 1;
|
|
char *dst = m_buf + need - 1;
|
|
// null term
|
|
*dst-- = '\0';
|
|
// start off encoding backwards!
|
|
for ( ; src >= m_buf ; src-- ) {
|
|
// copy one byte if no encoding required
|
|
if ( s_ut[(unsigned char)*src] == 0 ) {
|
|
*dst = *src;
|
|
dst--;
|
|
continue;
|
|
}
|
|
// ok, need to encode it
|
|
if ( *src == ' ' && spaceToPlus ) { *dst-- = '+'; continue; }
|
|
unsigned char v;
|
|
// store second hex digit
|
|
v = ((unsigned char)*src) & 0x0f ;
|
|
if ( v < 10 ) v += '0';
|
|
else v += 'A' - 10;
|
|
*dst-- = v;
|
|
// store first hex digit
|
|
v = ((unsigned char)*src)/16 ;
|
|
if ( v < 10 ) v += '0';
|
|
else v += 'A' - 10;
|
|
*dst-- = v;
|
|
*dst-- = '%';
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// used by PageEvents.cpp to escape out the single quotes in javascript
|
|
// strings...
|
|
bool SafeBuf::escapeJS (char *s , int32_t slen ) {
|
|
if ( ! reserve ( slen + 1 ) ) return false;
|
|
char *send = s + slen;
|
|
for ( ; s < send ; s++ ) {
|
|
// encode the damn thing
|
|
if ( *s == '\'' ) safeMemcpy("\\\'",2 );
|
|
// i dunno why i need " here... but \\\" does not work!
|
|
// i think because the html parser messes up on it before
|
|
// passing it on to the javascript parser.
|
|
else if ( *s == '\"' ) safeMemcpy(""",6 );
|
|
else if ( *s == '\\' ) safeMemcpy("\\\\",2 );
|
|
else pushChar ( *s );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//s is a url, make it safe for printing to html
|
|
bool SafeBuf::urlEncode (char *s , int32_t slen,
|
|
bool requestPath ,
|
|
bool encodeApostrophes ) {
|
|
// this makes things faster
|
|
if ( ! s_init23 ) initTable();
|
|
|
|
char *send = s + slen;
|
|
for ( ; s < send ; s++ ) {
|
|
if ( *s == '\0' && requestPath ) {
|
|
pushChar(*s);
|
|
continue;
|
|
}
|
|
if ( *s == '\'' && encodeApostrophes ) {
|
|
safeMemcpy("%27",3);
|
|
continue;
|
|
}
|
|
// encode if not fit for display
|
|
//if ( ! is_ascii ( *s ) ) goto encode;
|
|
// skip if no encoding required
|
|
if ( s_ut[(unsigned char)*s] == 0 ) {
|
|
pushChar(*s);
|
|
continue;
|
|
}
|
|
// special case
|
|
if ( *s == '?' && requestPath ) {
|
|
pushChar(*s);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
switch ( *s ) {
|
|
case ' ': goto encode;
|
|
case '&': goto encode;
|
|
case '"': goto encode;
|
|
case '+': goto encode;
|
|
case '%': goto encode;
|
|
case '#': goto encode;
|
|
case ':': goto encode;
|
|
case '/': goto encode;
|
|
// encoding < and > are more for displaying on an
|
|
// html page than sending to an http server
|
|
case '>': goto encode;
|
|
case '<': goto encode;
|
|
case '?': if ( requestPath ) break;
|
|
goto encode;
|
|
}
|
|
// otherwise, no need to encode
|
|
pushChar(*s);
|
|
continue;
|
|
encode:
|
|
*/
|
|
// space to +
|
|
if ( *s == ' ' ) { pushChar('+'); continue; }
|
|
pushChar('%');
|
|
// store first hex digit
|
|
unsigned char v = ((unsigned char)*s)/16 ;
|
|
if ( v < 10 ) v += '0';
|
|
else v += 'A' - 10;
|
|
pushChar(v);
|
|
// store second hex digit
|
|
v = ((unsigned char)*s) & 0x0f ;
|
|
if ( v < 10 ) v += '0';
|
|
else v += 'A' - 10;
|
|
pushChar(v);
|
|
}
|
|
nullTerm();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool SafeBuf::dequote ( char *t , int32_t tlen ) {
|
|
if ( tlen == 0 ) return true;
|
|
char *tend = t + tlen;
|
|
for ( ; t < tend; t++ ) {
|
|
if ( *t == '"' ) {
|
|
safeMemcpy(""", 5);
|
|
continue;
|
|
}
|
|
safeMemcpy(t, 1);
|
|
}
|
|
// there may be more things added to m_buf later, so do
|
|
// not null terminate this here...
|
|
//char null = '\0';
|
|
//safeMemcpy(&null, 1);
|
|
return true;
|
|
}
|
|
|
|
|
|
void SafeBuf::detachBuf() {
|
|
m_capacity = 0;
|
|
m_length = 0;
|
|
m_buf = NULL;
|
|
m_usingStack = false;
|
|
}
|
|
|
|
// set buffer from another safebuf, stealing it
|
|
bool SafeBuf::stealBuf ( SafeBuf *sb ) {
|
|
// free what we got!
|
|
purge();
|
|
m_capacity = sb->m_capacity;
|
|
m_length = sb->m_length;
|
|
m_buf = sb->m_buf;
|
|
m_usingStack = sb->m_usingStack;
|
|
m_encoding = sb->m_encoding;
|
|
// clear his ptrs
|
|
sb->m_buf = NULL;
|
|
sb->m_capacity = 0;
|
|
sb->m_length = 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool SafeBuf::operator += (uint64_t i) {
|
|
return safeMemcpy((char*)&i, sizeof(uint64_t));
|
|
}
|
|
|
|
bool SafeBuf::operator += (int64_t i) {
|
|
return safeMemcpy((char*)&i, sizeof(int64_t));
|
|
}
|
|
|
|
// bool SafeBuf::operator += (int32_t i) {
|
|
// return safeMemcpy((char*)&i, sizeof(int32_t));
|
|
// }
|
|
|
|
// bool SafeBuf::operator += (uint32_t i) {
|
|
// return safeMemcpy((char*)&i, sizeof(uint32_t));
|
|
// }
|
|
|
|
bool SafeBuf::operator += (float i) {
|
|
return safeMemcpy((char*)&i, sizeof(float));
|
|
}
|
|
|
|
bool SafeBuf::operator += (double i) {
|
|
return safeMemcpy((char*)&i, sizeof(double));
|
|
}
|
|
|
|
bool SafeBuf::operator += (char c) {
|
|
return safeMemcpy((char*)&c, sizeof(char));
|
|
}
|
|
|
|
bool SafeBuf::operator += (uint32_t i) {
|
|
return safeMemcpy((char*)&i, sizeof(uint32_t));
|
|
}
|
|
|
|
bool SafeBuf::operator += (uint16_t i) {
|
|
return safeMemcpy((char*)&i, sizeof(uint16_t));
|
|
}
|
|
|
|
bool SafeBuf::operator += (uint8_t i) {
|
|
return safeMemcpy((char*)&i, sizeof(uint8_t));
|
|
}
|
|
|
|
|
|
// bool SafeBuf::operator += (double i) {
|
|
// return safeMemcpy((char*)&i, sizeof(double));
|
|
// }
|
|
|
|
char& SafeBuf::operator[](int32_t i) {
|
|
return m_buf[i];
|
|
}
|
|
|
|
// for decoding Utf8
|
|
// uint32_t utf8Decode ( char *p, char **next = NULL ) {
|
|
// int num_bytes = bytes_in_code[*p];
|
|
// if (!num_bytes){
|
|
// // ill-formed byte sequence
|
|
// // lets just return an invalid character and go on to the next
|
|
// if (next) *next = p+1;
|
|
// return (uint32_t)0xffffffff;
|
|
// }
|
|
// if (next){
|
|
// *next = p + num_bytes;
|
|
// }
|
|
// switch(num_bytes){
|
|
// case 1:
|
|
// return (uint32_t)*p;
|
|
// case 2:
|
|
// return (uint32_t)((*p & 0x1f)<<6 |
|
|
// (*(p+1) & 0x3f));
|
|
// case 3:
|
|
// return (uint32_t)((*p & 0x0f)<<12 |
|
|
// (*(p+1) & 0x3f)<<6 |
|
|
// (*(p+2) & 0x3f));
|
|
// case 4:
|
|
// return (uint32_t)((*p & 0x07)<<18 |
|
|
// (*(p+1) & 0x3f)<<12 |
|
|
// (*(p+2) & 0x3f)<<6 |
|
|
// (*(p+3) & 0x3f));
|
|
// default:
|
|
// return (uint32_t) -1;
|
|
// };
|
|
// }
|
|
|
|
|
|
bool SafeBuf::printKey(char* key, char ks) {
|
|
switch (ks) {
|
|
case 12:
|
|
safePrintf("%016" XINT64 "%08" XINT32 "",
|
|
*(int64_t*)(key+(sizeof(int32_t))), *(int32_t*)key);
|
|
break;
|
|
case 16:
|
|
safePrintf("%016" XINT64 "%016" XINT64 " ",
|
|
*(int64_t*)(key+(sizeof(int64_t))),
|
|
*(int64_t *)key);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// . filter out tags and line breaks and extra spaces or whatever
|
|
// . used for printing sentences from XmlDoc::printEventSentences()
|
|
bool SafeBuf::safePrintFilterTagsAndLines ( char *p , int32_t plen ,
|
|
bool oneWordPerLine ) {
|
|
|
|
// prealloc -- also for terminating \0
|
|
if ( ! reserve ( plen + 1 ) ) return false;
|
|
|
|
// write into here
|
|
char *dst = m_buf + m_length;
|
|
|
|
char *pend = p + plen;
|
|
char size;
|
|
|
|
// set this to true so we do not lead with a space
|
|
bool lastWasSpace = true;
|
|
for ( ; p < pend ; p += size ) {
|
|
// get size
|
|
size = getUtf8CharSize(p);
|
|
// if a tag, then skip till '>' (assume no comment
|
|
// tags are in here!)
|
|
if ( *p == '<' ) {
|
|
// count # of lost chars
|
|
int32_t count = 0;
|
|
//char *pstart = p;
|
|
for ( ; p < pend && *p != '>' ; p++ ) count++;
|
|
// add a space to represent the tag
|
|
if ( oneWordPerLine ) *dst++ = '\n';
|
|
else *dst++ = ' ';
|
|
lastWasSpace = true;
|
|
continue;
|
|
}
|
|
// if we are space, write a simple space
|
|
if ( is_wspace_utf8 ( p ) ) {
|
|
// no back to back spaces
|
|
if ( lastWasSpace ) continue;
|
|
if ( oneWordPerLine ) *dst++ = '\n';
|
|
else *dst++ = ' ';
|
|
lastWasSpace = true;
|
|
continue;
|
|
}
|
|
// punct to space
|
|
if ( oneWordPerLine &&
|
|
is_punct_utf8 ( p ) ) {
|
|
// no back to back spaces
|
|
if ( lastWasSpace ) continue;
|
|
*dst++ = '\n';
|
|
lastWasSpace = true;
|
|
continue;
|
|
}
|
|
|
|
// not a space
|
|
lastWasSpace = false;
|
|
// . copy the char if not a tag char
|
|
// . if only 1 byte, done
|
|
if ( size == 1 ) { *dst++ = *p; continue; }
|
|
// might consist of multiple bytes in utf8
|
|
gbmemcpy ( dst , p , size );
|
|
dst += size;
|
|
}
|
|
// sanity scan
|
|
//char *end = m_buf + m_length;
|
|
//for ( char *s = m_buf ; s < end ; s++ )
|
|
// if ( *s == '<' )
|
|
// log("hey");
|
|
|
|
// update length now
|
|
m_length = dst - m_buf;
|
|
// this should not hurt anything
|
|
m_buf[m_length] = '\0';
|
|
// null term for printing
|
|
//m_buf[m_length++] = '\0';
|
|
return true;
|
|
}
|
|
|
|
void SafeBuf::filterTags ( ) {
|
|
// write into here
|
|
char *dst = m_buf ;
|
|
char *p = m_buf;
|
|
char *pend = m_buf + m_length;
|
|
char size;
|
|
// set this to true so we do not lead with a space
|
|
bool lastWasSpace = true;
|
|
for ( ; p < pend ; p += size ) {
|
|
// get size
|
|
size = getUtf8CharSize(p);
|
|
// if a tag, then skip till '>' (assume no comment
|
|
// tags are in here!)
|
|
if ( *p == '<' ) {
|
|
// count # of lost chars
|
|
int32_t count = 0;
|
|
//char *pstart = p;
|
|
for ( ; p < pend && *p != '>' ; p++ ) count++;
|
|
// no back to back spaces
|
|
if ( lastWasSpace ) continue;
|
|
// add a space to represent the tag
|
|
*dst++ = ' ';
|
|
lastWasSpace = true;
|
|
continue;
|
|
}
|
|
// if we are space, write a simple space
|
|
if ( is_wspace_utf8 ( p ) ) {
|
|
// no back to back spaces
|
|
if ( lastWasSpace ) continue;
|
|
*dst++ = ' ';
|
|
lastWasSpace = true;
|
|
continue;
|
|
}
|
|
// not a space
|
|
lastWasSpace = false;
|
|
// . copy the char if not a tag char
|
|
// . if only 1 byte, done
|
|
if ( size == 1 ) { *dst++ = *p; continue; }
|
|
// might consist of multiple bytes in utf8
|
|
gbmemcpy ( dst , p , size );
|
|
dst += size;
|
|
}
|
|
// update length now
|
|
m_length = dst - m_buf;
|
|
// this should not hurt anything
|
|
m_buf[m_length] = '\0';
|
|
}
|
|
|
|
void SafeBuf::filterQuotes ( ) {
|
|
char *p = m_buf;
|
|
char *pend = m_buf + m_length;
|
|
char size;
|
|
for ( ; p < pend ; p += size ) {
|
|
// get size
|
|
size = getUtf8CharSize(p);
|
|
// if a tag, then skip till '>' (assume no comment
|
|
// tags are in here!)
|
|
if ( *p != '\"' ) continue;
|
|
// filter?
|
|
*p = ' ';
|
|
}
|
|
}
|
|
|
|
#include "Tagdb.h"
|
|
|
|
// if safebuf is a buffer of Tags from Tagdb.cpp
|
|
Tag *SafeBuf::addTag2 ( char *mysite ,
|
|
char *tagname ,
|
|
int32_t now ,
|
|
char *user ,
|
|
int32_t ip ,
|
|
int32_t val ,
|
|
char rdbId ) {
|
|
char buf[64];
|
|
sprintf(buf,"%" INT32 "",val);
|
|
int32_t dsize = gbstrlen(buf) + 1;
|
|
return addTag ( mysite,tagname,now,user,ip,buf,dsize,rdbId,true);
|
|
}
|
|
|
|
// if safebuf is a buffer of Tags from Tagdb.cpp
|
|
Tag *SafeBuf::addTag3 ( char *mysite ,
|
|
char *tagname ,
|
|
int32_t now ,
|
|
char *user ,
|
|
int32_t ip ,
|
|
char *data ,
|
|
char rdbId ) {
|
|
int32_t dsize = gbstrlen(data) + 1;
|
|
return addTag ( mysite,tagname,now,user,ip,data,dsize,rdbId,true);
|
|
}
|
|
|
|
Tag *SafeBuf::addTag ( char *mysite ,
|
|
char *tagname ,
|
|
int32_t now ,
|
|
char *user ,
|
|
int32_t ip ,
|
|
char *data ,
|
|
int32_t dsize ,
|
|
char rdbId ,
|
|
bool pushRdbId ) {
|
|
int32_t need = dsize + 32 + sizeof(Tag);
|
|
if ( user ) need += gbstrlen(user);
|
|
if ( mysite ) need += gbstrlen(mysite);
|
|
if ( ! reserve ( need ) ) return NULL;
|
|
if ( pushRdbId && ! pushChar(rdbId) ) return NULL;
|
|
Tag *tag = (Tag *)getBuf();
|
|
tag->set(mysite,tagname,now,user,ip,data,dsize);
|
|
incrementLength ( tag->getRecSize() );
|
|
if ( tag->getRecSize() > need ) { char *xx=NULL;*xx=0; }
|
|
return tag;
|
|
}
|
|
|
|
bool SafeBuf::addTag ( Tag *tag ) {
|
|
int32_t recSize = tag->getSize();
|
|
//tag->setDataSize();
|
|
if ( tag->m_recDataSize <= 16 ) {
|
|
// note it
|
|
return log("safebuf: encountered corrupted tag datasize=%" INT32 ".",
|
|
tag->m_recDataSize);
|
|
//char *xx=NULL;*xx=0; }
|
|
}
|
|
return safeMemcpy ( (char *)tag , recSize );
|
|
}
|
|
|
|
bool SafeBuf::htmlEncodeXmlTags ( char *s , int32_t slen , int32_t niceness ) {
|
|
// count the xml tags so we know how much buf to allocated
|
|
char *p = s;
|
|
char *pend = s + slen;
|
|
int32_t count = 0;
|
|
for ( ; p < pend ; p++ ) {
|
|
QUICKPOLL(niceness);
|
|
if ( *p == '<' ) count += 6; // assume '>' too!
|
|
if ( *p == '\n' ) count += 3; // <br>
|
|
if ( *p == '\t' ) count += 17; //
|
|
}
|
|
// include \0
|
|
int32_t need = slen + 1;
|
|
// then < has a > and each is expanded to < or >
|
|
need += count ;
|
|
// reserve that
|
|
if ( ! reserve ( need ) ) return false;
|
|
// point to dst buf
|
|
char *dst = m_buf;
|
|
bool inXmlTag = false;
|
|
// copy over now
|
|
p = s;
|
|
for ( ; p < pend ; p++ ) {
|
|
QUICKPOLL(niceness);
|
|
// make > into > then break out
|
|
if ( inXmlTag && *p == '>' ) {
|
|
gbmemcpy ( dst , ">", 4 );
|
|
dst += 4;
|
|
inXmlTag = false;
|
|
continue;
|
|
}
|
|
// translate \n to br for xml docs
|
|
if ( *p == '\n' ) {
|
|
gbmemcpy ( dst , "<br>", 4 );
|
|
dst += 4;
|
|
continue;
|
|
}
|
|
// translate \n to br for xml docs
|
|
if ( *p == '\t' ) {
|
|
gbmemcpy ( dst , " ", 18 );
|
|
dst += 18;
|
|
continue;
|
|
}
|
|
// copy over if nothing special
|
|
if ( *p != '<' ) {
|
|
*dst++ = *p;
|
|
continue;
|
|
}
|
|
// get tag id (will be 0 for </>)
|
|
char *tagName;
|
|
if ( p[1] == '/' ) tagName = p + 2;
|
|
else tagName = p + 1;
|
|
NodeType *nt;
|
|
getTagId ( tagName , &nt );
|
|
// keep html tags
|
|
if ( nt && ! nt->m_isXmlTag ) {
|
|
*dst++ = '<';
|
|
continue;
|
|
}
|
|
// ok, it's an xml tag so use <
|
|
gbmemcpy ( dst, "<", 4 );
|
|
dst += 4;
|
|
inXmlTag = true;
|
|
}
|
|
// store \0 but do not count towards m_length
|
|
*dst = '\0';
|
|
// update length
|
|
m_length = dst - m_buf;
|
|
return true;
|
|
}
|
|
|
|
// this puts a \0 at the end but does not update m_length for the \0
|
|
bool SafeBuf::safeStrcpy ( char *s ) {
|
|
if ( ! s ) return true;
|
|
int32_t slen = gbstrlen(s);
|
|
if ( ! reserve ( slen+1 ) ) return false;
|
|
if ( ! safeMemcpy(s,slen) ) return false;
|
|
nullTerm();
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::truncateLongWords ( char *s , int32_t srcLen , int32_t minmax ) {
|
|
// ensure enough room
|
|
if ( ! reserve ( srcLen * 2 + 50 ) ) return false;
|
|
// start of where we write into
|
|
char *dstart = m_buf + m_length;
|
|
int32_t nospacecount = 0;
|
|
char *src = s;
|
|
char *srcEnd = s + srcLen;
|
|
char *dst = getBuf();
|
|
char size;
|
|
int32_t scan = 10;
|
|
bool lastWasSpace = true;
|
|
for ( ; src < srcEnd ; src += size ) {
|
|
size = getUtf8CharSize(src);
|
|
// copy full tags
|
|
if ( *src == '<' ) {
|
|
for ( ; *src && *src != '>' ; ) *dst++ = *src++;
|
|
nospacecount = 0;
|
|
// copy the remaining '>' using loop logic
|
|
if ( *src == '>' ) src--;
|
|
continue;
|
|
}
|
|
// a space?
|
|
if ( is_wspace_a(*src) ) {
|
|
nospacecount = 0;
|
|
// make all spaces now plain space so PageSubmit.cpp
|
|
// can fill in <textarea> tags with the event
|
|
// description...
|
|
if ( ! lastWasSpace ) *dst++ = ' ';//*src;
|
|
lastWasSpace = true;
|
|
continue;
|
|
}
|
|
// NEW! any punct mark is a good breaking point
|
|
if ( ! is_alnum_utf8(src) )
|
|
nospacecount = -1;
|
|
// reset this
|
|
lastWasSpace = false;
|
|
// truncate if overdue for a space
|
|
if ( nospacecount >= minmax ) { // 25 ) {
|
|
// go forward up to 5 more characters looking
|
|
// for a non-alnum to break on
|
|
char *x = src;
|
|
char *xend = src + scan;
|
|
char xsize;
|
|
for ( ; *x && x < xend ; x += xsize ) {
|
|
xsize = getUtf8CharSize(x);
|
|
if ( ! is_alnum_utf8(x) ) break;
|
|
}
|
|
|
|
// bail on truncation if completing it would be int16_ter
|
|
// than adding the ellipsis
|
|
if ( *x && x < xend ) {
|
|
// scan for next space
|
|
char *w = x;
|
|
char *wmax = x + 3;
|
|
for ( ; w<wmax && *w && !is_wspace_a(*w); w++);
|
|
// ok bail!
|
|
if ( w<wmax ) { nospacecount=0; goto resume;}
|
|
}
|
|
|
|
|
|
// if we had a nice break point at "src" or int16_tly
|
|
// thereafter, then break right after that!
|
|
if ( *x && x < xend ) {
|
|
gbmemcpy ( dst , src , x - src );
|
|
dst += x - src;
|
|
src = x;
|
|
}
|
|
|
|
|
|
gbmemcpy ( dst , "... " , 4 );
|
|
dst += 4;
|
|
// skip until space or tag
|
|
for ( ; *src ; src++ ) {
|
|
if ( *src == '<' )
|
|
break;
|
|
if ( is_wspace_a(*src) )
|
|
break;
|
|
}
|
|
// if could not find < or space, stop then i guess
|
|
if ( ! *src ) break;
|
|
// start printing there again
|
|
src--;
|
|
// its a size of 1
|
|
size = 1;
|
|
// we added a "... " which has a space
|
|
nospacecount = 0;
|
|
continue;
|
|
}
|
|
resume:
|
|
// a nonspace i guess
|
|
nospacecount++;
|
|
// copy the char
|
|
if ( size == 1 ) {
|
|
*dst++ = *src;
|
|
continue;
|
|
}
|
|
gbmemcpy ( dst , src , size );
|
|
dst += size;
|
|
}
|
|
// remove trailing space in case we ended in "... "
|
|
if ( dst > dstart && dst[-1] == ' ' ) dst--;
|
|
// sanity check
|
|
char *end = m_buf + m_capacity;
|
|
if ( dst+1 > end ) { char *xx=NULL;*xx=0; }
|
|
// update length
|
|
m_length = dst - m_buf;
|
|
// a safe null term
|
|
*dst = '\0';
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::queryFilter ( char *s , int32_t slen ) {
|
|
|
|
// include terminating \0
|
|
if ( ! reserve ( slen + 1 ) ) return false;
|
|
|
|
// copy over
|
|
char *src = s;
|
|
char *srcEnd = s + slen;
|
|
char *dst = getBuf();
|
|
|
|
for ( ; src < srcEnd ; ) {
|
|
// assume good
|
|
*dst = *src;
|
|
// remove parens
|
|
if ( *src == '(' ) *dst = ' ';
|
|
if ( *src == ')' ) *dst = ' ';
|
|
if ( *src == '\"' ) *dst = ' ';
|
|
if ( *src == '\'' ) *dst = ' ';
|
|
if ( *src == '|' ) *dst = ' ';
|
|
if ( *src == ':' ) *dst = ' ';
|
|
// remove bool operators
|
|
if ( *src == 'A' &&
|
|
// must have a space before it
|
|
(src == s || is_wspace_a(src[-1]) ) &&
|
|
// and match a boolean operator
|
|
strncmp(src,"AND",3) &&
|
|
// and after it
|
|
(is_wspace_a(src[3]) || !src[3]) ) {
|
|
// nuke it
|
|
src += 3;
|
|
continue;
|
|
}
|
|
// remove NOT operator
|
|
if ( *src == 'N' &&
|
|
// must have a space before it
|
|
(src == s || is_wspace_a(src[-1]) ) &&
|
|
// and match a boolean operator
|
|
strncmp(src,"NOT",3) &&
|
|
// and after it
|
|
(is_wspace_a(src[3]) || !src[3]) ) {
|
|
// nuke it
|
|
src += 3;
|
|
continue;
|
|
}
|
|
// remove UOR operator
|
|
if ( *src == 'U' &&
|
|
// must have a space before it
|
|
(src == s || is_wspace_a(src[-1]) ) &&
|
|
// and match a boolean operator
|
|
strncmp(src,"UOR",3) &&
|
|
// and after it
|
|
(is_wspace_a(src[3]) || !src[3]) ) {
|
|
// nuke it
|
|
src += 3;
|
|
continue;
|
|
}
|
|
// remove OR operator
|
|
if ( *src == 'O' &&
|
|
// must have a space before it
|
|
(src == s || is_wspace_a(src[-1]) ) &&
|
|
// and match a boolean operator
|
|
strncmp(src,"OR",2) &&
|
|
// and after it
|
|
(is_wspace_a(src[2]) || !src[2]) ) {
|
|
// nuke it
|
|
src += 2;
|
|
continue;
|
|
}
|
|
dst++;
|
|
src++;
|
|
}
|
|
*dst = '\0';
|
|
// update length
|
|
m_length = dst - m_buf;
|
|
return true;
|
|
}
|
|
|
|
// . some email services like hotmail and gmail remove our style tags so
|
|
// that they do not conflict with their own, so let's automatically
|
|
// inline the style tags here.
|
|
// . store the new creation into "dst"
|
|
bool SafeBuf::inlineStyleTags ( ) {
|
|
|
|
SafeBuf dst;
|
|
|
|
HashTableX stab;
|
|
stab.set ( 4,8,128,NULL,0,false,0,"stytab");
|
|
|
|
// map the class names in style tags to an inline style
|
|
char *s = m_buf;
|
|
for ( ; *s ; s++ ) {
|
|
// look for style tag
|
|
if ( s[0] != '<' ) continue;
|
|
if ( to_lower_a(s[1]) != 's' ) continue;
|
|
if ( to_lower_a(s[2]) != 't' ) continue;
|
|
if ( to_lower_a(s[3]) != 'y' ) continue;
|
|
if ( to_lower_a(s[4]) != 'l' ) continue;
|
|
if ( to_lower_a(s[5]) != 'e' ) continue;
|
|
// skip over tag
|
|
for ( ; *s && *s != '>' ; s++ );
|
|
// skip over '>'
|
|
s++;
|
|
// get the next line of style
|
|
getline:
|
|
// skip white space
|
|
for ( ; is_wspace_a(*s) ; s++ );
|
|
// </style>? end our line parsing then and look for
|
|
// more <style> tags in the for loop.
|
|
if ( s[0] == '<' &&
|
|
s[1] == '/' &&
|
|
to_lower_a(s[2]) == 's' &&
|
|
to_lower_a(s[3]) == 't' &&
|
|
to_lower_a(s[4]) == 'y' &&
|
|
to_lower_a(s[5]) == 'l' &&
|
|
to_lower_a(s[6]) == 'e' )
|
|
continue;
|
|
// get tag name
|
|
char *tagName = s;
|
|
// skip till '.' or '{' or space
|
|
for ( ; *s && *s!='{' && *s!='.' && ! is_wspace_a(*s) ; s++ );
|
|
// set length then
|
|
int32_t tagNameLen = s - tagName;
|
|
// set to NULL if empty just to be consistent
|
|
if ( tagNameLen == 0 ) tagName = NULL;
|
|
// assume none
|
|
char *className = NULL;
|
|
int32_t classNameLen = 0;
|
|
// if period set class
|
|
if ( *s == '.' ) {
|
|
// skip period
|
|
s++;
|
|
// set class
|
|
className = s;
|
|
// skip till non-alnum i guess. could be '{'
|
|
for ( ; is_alnum_a(*s) ; s++ );
|
|
// set len
|
|
classNameLen = s - className;
|
|
}
|
|
// skip til '{'
|
|
for ( ; *s && *s != '{' ; s++ );
|
|
// sanity!
|
|
if ( *s != '{' )
|
|
// return error!
|
|
return log("oops");
|
|
// skip over that
|
|
s++;
|
|
// skip spaces
|
|
for ( ; is_wspace_a(*s) ; s++ );
|
|
// that's the style
|
|
char *style = s;
|
|
// find last '}'
|
|
for ( ; *s && *s != '}' ; s++ );
|
|
// must be there
|
|
if ( *s != '}' )
|
|
return log("oops2");
|
|
// mark the end
|
|
int32_t styleLen = s - style;
|
|
// hash the tag name and class
|
|
int32_t h32 = hash32 ( tagName , tagNameLen );
|
|
h32 = hash32 ( className , classNameLen , h32 );
|
|
// point that to our style and length
|
|
uint64_t val = (uint64_t)style;
|
|
val <<= 32;
|
|
val |= (uint32_t)styleLen;
|
|
// and store it
|
|
stab.addKey ( &h32 , &val );
|
|
// skip over '}'
|
|
s++;
|
|
// get the next line of style
|
|
goto getline;
|
|
}
|
|
|
|
// ok, now copy over byte by byte into the "dst" buf and
|
|
// check each tag to see if it has a style in "stab". if it
|
|
// does then insert the style! if it already has a style tag
|
|
// then pre-pend to that i guess.
|
|
s = m_buf;
|
|
|
|
for ( ; *s ; s++ ) {
|
|
// skip if not tag
|
|
if ( *s != '<' ) {
|
|
dst.pushChar ( *s );
|
|
continue;
|
|
}
|
|
// save it
|
|
char *top = s;
|
|
// skip '<'
|
|
s++;
|
|
// get tag name
|
|
char *tagName = s;
|
|
// find end of it. can have a hyphen i guess.
|
|
for ( ; is_alnum_a(*s) || *s=='-' ; s++ );
|
|
// set length
|
|
int32_t tagNameLen = s - tagName;
|
|
// set bookmark
|
|
char *afterName = s;
|
|
// assume no class
|
|
char *className = NULL;
|
|
int32_t classNameLen = 0;
|
|
// look for "class"
|
|
for ( ; *s && *s != '>' && s[1] !='>' ; s++ ) {
|
|
// need " class"
|
|
if ( ! is_wspace_a(s[0]) ) continue;
|
|
if ( to_lower_a(s[1]) != 'c' ) continue;
|
|
if ( to_lower_a(s[2]) != 'l' ) continue;
|
|
if ( to_lower_a(s[3]) != 'a' ) continue;
|
|
if ( to_lower_a(s[4]) != 's' ) continue;
|
|
if ( to_lower_a(s[5]) != 's' ) continue;
|
|
// skip it
|
|
s += 6;
|
|
// skip spaces
|
|
for ( ; is_wspace_a(*s) ; s++ );
|
|
// need equal. if *s == '>' the s[1] above should catch
|
|
if ( *s != '=' ) continue;
|
|
// skip equal
|
|
s++;
|
|
// skip spaces
|
|
for ( ; is_wspace_a(*s) ; s++ );
|
|
// a quote?
|
|
if ( *s == '\"' ) s++;
|
|
// that's the name then
|
|
className = s;
|
|
// find end of class name
|
|
for ( ; is_alnum_a(*s) || *s == '-' ; s++ );
|
|
// set that
|
|
classNameLen = s - className;
|
|
break;
|
|
}
|
|
// find the style tag name if it's already in there
|
|
s = afterName;
|
|
char *stylePos = NULL;
|
|
for ( ; *s && *s != '>' && s[1] !='>' ; s++ ) {
|
|
// need " class"
|
|
if ( ! is_wspace_a(s[0]) ) continue;
|
|
if ( to_lower_a(s[1]) != 's' ) continue;
|
|
if ( to_lower_a(s[2]) != 't' ) continue;
|
|
if ( to_lower_a(s[3]) != 'y' ) continue;
|
|
if ( to_lower_a(s[4]) != 'l' ) continue;
|
|
if ( to_lower_a(s[5]) != 'e' ) continue;
|
|
// skip it
|
|
s += 6;
|
|
// skip spaces
|
|
for ( ; is_wspace_a(*s) ; s++ );
|
|
// need equal. if *s == '>' the s[1] above should catch
|
|
if ( *s != '=' ) continue;
|
|
// skip equal
|
|
s++;
|
|
// skip spaces
|
|
for ( ; is_wspace_a(*s) ; s++ );
|
|
// a quote?
|
|
if ( *s == '\"' ) s++;
|
|
// that's the style stuff then
|
|
stylePos = s;
|
|
break;
|
|
}
|
|
|
|
// hash the tag name and class
|
|
int32_t h32 = hash32 ( tagName , tagNameLen );
|
|
h32 = hash32 ( className , classNameLen , h32 );
|
|
// try getting the slot
|
|
int32_t slot = stab.getSlot(&h32);
|
|
// if that hash didn't work try a hash with just the
|
|
// class name!
|
|
if ( slot < 0 ) {
|
|
h32 = hash32 ( NULL , 0 , 0 );
|
|
h32 = hash32 ( className , classNameLen , h32 );
|
|
slot = stab.getSlot(&h32);
|
|
}
|
|
// if still no go, give up
|
|
if ( slot < 0 ) {
|
|
// revert for copy
|
|
s = top;
|
|
// copy
|
|
dst.pushChar(*s);
|
|
// and copy more!
|
|
continue;
|
|
}
|
|
// get it
|
|
uint64_t val ;
|
|
val = *(uint64_t *)stab.getValueFromSlot ( slot );
|
|
// extract the style
|
|
char *style = (char *)(val>>32);
|
|
int32_t styleLen = (int32_t)(val & 0xffffffff);
|
|
// inject style into style position if there
|
|
if ( stylePos ) {
|
|
// copy over up til that
|
|
dst.safeMemcpy ( top , stylePos - top );
|
|
// inject it
|
|
dst.safeMemcpy ( style , styleLen );
|
|
// if our style did not end on a semicolon! bug!
|
|
if ( style[styleLen-1] != ';' )
|
|
dst.pushChar(';');
|
|
// then finish it up
|
|
s = stylePos-1;
|
|
continue;
|
|
}
|
|
// otherwise, add our own style attribute then
|
|
dst.safeMemcpy ( top , afterName - top );
|
|
// add a space and the attribute
|
|
dst.safePrintf(" style=\"");
|
|
// the style from our table
|
|
dst.safeMemcpy ( style , styleLen );
|
|
// close it up
|
|
dst.safePrintf("\" ");
|
|
// do rest of tag
|
|
s = afterName-1;
|
|
}
|
|
|
|
// clear us
|
|
reset();
|
|
|
|
// copy over
|
|
return safeMemcpy ( &dst );
|
|
}
|
|
|
|
// fix email issues. the period on the line by itself is the signal
|
|
// to terminate the email.
|
|
bool SafeBuf::fixIsolatedPeriods ( ) {
|
|
|
|
char *s = getBufStart();
|
|
char *start = s;
|
|
|
|
for ( ; *s ; s++ ) {
|
|
if ( *s != '.' ) continue;
|
|
if ( s > start && s[-1] != '\n' ) continue;
|
|
|
|
char *p = s;
|
|
// check following spaces
|
|
bool fixIt = false;
|
|
for ( ; *p ; p++ ) {
|
|
if ( *p == '\n' ) { fixIt = true; break; }
|
|
// i guess ". \n" is also a signal...
|
|
if ( is_wspace_a(*p) ) continue;
|
|
break;
|
|
}
|
|
// map it to hyphen
|
|
if ( fixIt ) *s = '-';
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// convert json to xml if it is in json
|
|
bool SafeBuf::convertJSONtoXML ( int32_t niceness , int32_t startConvertPos ) {
|
|
|
|
char *src = m_buf + startConvertPos;
|
|
|
|
// return if not json
|
|
if ( strncmp ( src , "{\"data\":[" , 9 ) ) return true;
|
|
|
|
// store here
|
|
SafeBuf dbuf;
|
|
if ( ! dbuf.reserve ( m_length * 2 ) ) return false;
|
|
char *dst = dbuf.m_buf;
|
|
|
|
// copy over the mime verbatim
|
|
gbmemcpy ( dst , m_buf , startConvertPos );
|
|
dst += startConvertPos;
|
|
|
|
// skip data header, should point to { then
|
|
src += 9;
|
|
|
|
int32_t level = 0;
|
|
|
|
bool startEvent = false;
|
|
|
|
for ( ; *src ; ) {
|
|
// breathe
|
|
QUICKPOLL(niceness);
|
|
// a / indicates literal
|
|
if ( *src == '\\' ) {
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
// unicode!
|
|
if ( src[-1] == 'u' ) {
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
}
|
|
continue;
|
|
}
|
|
// control character?
|
|
if ( *src == '{' ) {
|
|
if ( level == 0 ) {
|
|
startEvent = true;
|
|
gbmemcpy(dst,"<event>\n",8);
|
|
dst += 8;
|
|
}
|
|
level++;
|
|
src++;
|
|
*dst++ = '\n';
|
|
continue;
|
|
}
|
|
// control character?
|
|
if ( *src == '}' ) {
|
|
level--;
|
|
if ( level == 0 ) {
|
|
gbmemcpy(dst,"</event>\n",9);
|
|
dst += 9;
|
|
}
|
|
src++;
|
|
*dst++ = '\n';
|
|
continue;
|
|
}
|
|
|
|
// a quote indicates field name
|
|
if ( *src == '\"' ) {
|
|
// find the end quote
|
|
char *eq = src + 1;
|
|
for ( ; *eq && *eq != '\"' ; eq++ );
|
|
// must be a colon!
|
|
char *colon = eq + 1;
|
|
if ( *colon != ':' ) {
|
|
log("safebuf: no colon!");
|
|
return false;
|
|
}
|
|
// if it has compound data, just ignore it
|
|
if ( colon[1] == '[' ) {
|
|
src = colon + 2;
|
|
continue;
|
|
}
|
|
// make xml name then
|
|
*dst++ = '<';
|
|
char *name = src+1;
|
|
int32_t nlen = eq - name;
|
|
// massive hack. convert json "id" to "eid"
|
|
if ( nlen == 2 &&
|
|
name[0] == 'i' &&
|
|
name[1] == 'd' &&
|
|
startEvent ) {
|
|
startEvent = false;
|
|
name = "eid";
|
|
nlen = 3;
|
|
}
|
|
// name mappings
|
|
if ( nlen == 7 &&
|
|
name[0] == 'p' &&
|
|
!strncmp(name,"picture",7)) {
|
|
name = "pic_square";
|
|
nlen = 10;
|
|
}
|
|
|
|
gbmemcpy ( dst , name , nlen );
|
|
dst += nlen;
|
|
*dst++ = '>';
|
|
|
|
// if after colon is '{' then just bail!
|
|
// our data value is compound. unfortunately we will
|
|
// not get our ending tag then!
|
|
if ( colon[1] == '{' ) {
|
|
src = &colon[1];
|
|
continue;
|
|
}
|
|
|
|
// skip colon
|
|
char *val = colon + 1;
|
|
// should be a quote after that! unless its
|
|
// a numeric value. skip quote if there
|
|
bool hadQuote = (val[0] == '\"');
|
|
if ( hadQuote ) val++;
|
|
// find end of quote for data value now
|
|
char *vend = val;
|
|
for ( ; *vend ; vend++ ) {
|
|
// skip escape sequences. like \\\"
|
|
if ( *vend == '\\' ) {
|
|
vend++;
|
|
continue;
|
|
}
|
|
// stop if quote
|
|
if ( *vend == '\"' ) break;
|
|
// if not in quotes, a comma,etc breaks it!
|
|
if ( ! hadQuote &&
|
|
( *vend == ',' ||
|
|
*vend == '}' ||
|
|
is_wspace_a(*vend) )
|
|
)
|
|
break;
|
|
}
|
|
// ok, that's the value
|
|
int32_t vlen = vend - val;
|
|
gbmemcpy ( dst , val , vlen );
|
|
dst += vlen;
|
|
// name end tag
|
|
gbmemcpy ( dst , "</" , 2 );
|
|
dst += 2;
|
|
gbmemcpy ( dst , name , nlen );
|
|
dst += nlen;
|
|
*dst++ = '>';
|
|
*dst++ = '\n';
|
|
// skip ahead then. will be a , } etc.
|
|
src = vend;
|
|
// skip quote
|
|
if ( *src == '\"' ) src++;
|
|
if ( *src == ',' ) src++;
|
|
continue;
|
|
}
|
|
|
|
// just skip it i guess, it's a comma or space
|
|
src++;
|
|
}
|
|
|
|
|
|
*dst = '\0';
|
|
dbuf.m_length = dst - dbuf.m_buf;
|
|
|
|
// purge ourselves
|
|
purge();
|
|
|
|
// and steal dbuf's m_buf
|
|
m_buf = dbuf.m_buf;
|
|
m_length = dbuf.m_length;
|
|
m_capacity = dbuf.m_capacity;
|
|
m_usingStack = dbuf.m_usingStack;
|
|
|
|
// detach from dbuf so he does not free it
|
|
dbuf.detachBuf();
|
|
|
|
|
|
// now unescape crappy json
|
|
if ( ! decodeJSON(niceness) ) return false;
|
|
|
|
// check it out
|
|
//log("fbspider: got xml from json reply: %s",m_buf);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::decodeJSON ( int32_t niceness ) {
|
|
|
|
// count how many \u's we got
|
|
int32_t need = 0;
|
|
char *p = m_buf;
|
|
for ( ; *p ; p++ )
|
|
// for the 'x' and the ';'
|
|
if ( *p == '\\' && p[1] == 'u' ) need += 2;
|
|
|
|
// reserve a little extra if we need it
|
|
SafeBuf dbuf;
|
|
dbuf.reserve ( need + m_length + 1);
|
|
|
|
char *src = m_buf;
|
|
char *dst = dbuf.m_buf;
|
|
for ( ; *src ; ) {
|
|
if ( *src == '\\' ) {
|
|
// \n?
|
|
if ( src[1] == 'n' ) {
|
|
*dst++ = '\n';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
if ( src[1] == 'r' ) {
|
|
*dst++ = '\r';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
// utf8? if not, just skip the slash
|
|
if ( src[1] != 'u' ) { src++; continue; }
|
|
// otherwise, decode. can do in place like this...
|
|
*dst++ = '&';
|
|
*dst++ = '#';
|
|
*dst++ = 'x';
|
|
// skip over /u
|
|
src += 2;
|
|
if ( *src ) *dst++ = *src++;
|
|
if ( *src ) *dst++ = *src++;
|
|
if ( *src ) *dst++ = *src++;
|
|
if ( *src ) *dst++ = *src++;
|
|
*dst++ = ';';
|
|
continue;
|
|
}
|
|
*dst++ = *src++;
|
|
}
|
|
*dst = '\0';
|
|
dbuf.m_length = dst - dbuf.m_buf;
|
|
|
|
// purge ourselves
|
|
purge();
|
|
|
|
// and steal dbuf's m_buf
|
|
m_buf = dbuf.m_buf;
|
|
m_length = dbuf.m_length;
|
|
m_capacity = dbuf.m_capacity;
|
|
m_usingStack = dbuf.m_usingStack;
|
|
|
|
// detach from dbuf so he does not free it
|
|
dbuf.detachBuf();
|
|
|
|
return true;
|
|
}
|
|
|
|
// . this should support the case when the src and dst buffers are the same!
|
|
// so decodeJSONToUtf8() function below will work
|
|
// . this is used by xmldoc.cpp to PARTIALLY decode a json buf so we do not
|
|
// index letters in escapes like \n \r \f \t \uxxxx \\ \/
|
|
// . SO we do keep \"
|
|
// . so when indexing a doc we set decodeAll to FALSE, but if you want to
|
|
// decode quotation marks as well then set decodeAll to TRUE!
|
|
bool SafeBuf::safeDecodeJSONToUtf8 ( char *json,
|
|
int32_t jsonLen,
|
|
int32_t niceness ) {
|
|
|
|
// how much space to reserve for the copy?
|
|
int32_t need = jsonLen;
|
|
|
|
// count how many \u's we got
|
|
char *p = json;//m_buf;
|
|
char *pend = json + jsonLen;
|
|
for ( ; p < pend ; p++ )
|
|
// for the 'x' and the ';'
|
|
if ( *p == '\\' && p[1] == 'u' ) need += 2;
|
|
|
|
// reserve a little extra if we need it
|
|
//SafeBuf dbuf;
|
|
if ( ! reserve ( need + 1) ) return false;
|
|
|
|
char *src = json;//m_buf;
|
|
char *srcEnd = json + jsonLen;
|
|
|
|
char *dst = m_buf + m_length;
|
|
|
|
for ( ; src < srcEnd ; ) {
|
|
QUICKPOLL(niceness);
|
|
if ( *src == '\\' ) {
|
|
// \n? (from json.org homepage)
|
|
if ( src[1] == 'n' ) {
|
|
*dst++ = '\n';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
if ( src[1] == 'r' ) {
|
|
*dst++ = '\r';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
if ( src[1] == 't' ) {
|
|
*dst++ = '\t';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
if ( src[1] == 'b' ) {
|
|
*dst++ = '\b';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
if ( src[1] == 'f' ) {
|
|
*dst++ = '\f';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
// a "\\" is an encoded backslash
|
|
if ( src[1] == '\\' ) {
|
|
*dst++ = '\\';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
// a "\/" is an encoded forward slash
|
|
if ( src[1] == '/' ) {
|
|
*dst++ = '/';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
// we do not decode quotation marks when indexing
|
|
// the doc so we can preserve json names/value pair
|
|
// information for indexing purposes. however,
|
|
// Title.cpp DOES want to decode quotations.
|
|
if ( src[1] == '\"' ) { // && decodeAll ) {
|
|
*dst++ = '\"';
|
|
src += 2;
|
|
continue;
|
|
}
|
|
// utf8? if not, just skip the slash
|
|
if ( src[1] != 'u' ) {
|
|
// no, keep the slash so if we have /"
|
|
// we do not convert to just "
|
|
*dst++ = '\\';
|
|
src++;
|
|
continue;
|
|
}
|
|
// otherwise, decode. can do in place like this...
|
|
char *p = src + 2;
|
|
// skip the /ug or /ugg or /uggg or /ugggg in its
|
|
// entirety i guess... to avoid infinite loop
|
|
if ( ! is_hex(p[0]) ) { src +=2; continue;}
|
|
if ( ! is_hex(p[1]) ) { src +=3; continue;}
|
|
if ( ! is_hex(p[2]) ) { src +=4; continue;}
|
|
if ( ! is_hex(p[3]) ) { src +=5; continue;}
|
|
// TODO: support surrogate pairs in utf16?
|
|
UChar32 uc = 0;
|
|
// store the 16-bit number in lower 16 bits of uc...
|
|
hexToBin ( p , 2 , ((char *)&uc)+1 );
|
|
hexToBin ( p+2 , 2 , ((char *)&uc)+0 );
|
|
//buf[2] = '\0';
|
|
int32_t size = ::utf8Encode ( (UChar32)uc , (char *)dst );
|
|
// a quote??? not allowed in json!
|
|
if ( size == 1 && dst[0] == '\"' ) {
|
|
size = 2;
|
|
dst[0] = '\\';
|
|
dst[1] = '\"';
|
|
}
|
|
//int16_t = ahextoint16_t ( p );
|
|
dst += size;
|
|
// skip over /u and 4 digits
|
|
src += 6;
|
|
continue;
|
|
}
|
|
*dst++ = *src++;
|
|
}
|
|
*dst = '\0';
|
|
// update our length
|
|
m_length = dst - m_buf;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
bool SafeBuf::decodeJSONToUtf8 ( int32_t niceness ) {
|
|
|
|
//char *x = strstr(m_buf,"Chief European");
|
|
//if ( x )
|
|
// log("hey");
|
|
|
|
int32_t saved = m_length;
|
|
// reset for calling the function below
|
|
m_length = 0;
|
|
|
|
return safeDecodeJSONToUtf8 ( m_buf , saved , niceness );
|
|
}
|
|
*/
|
|
|
|
// . REALLY just a print vanity function. makes json output prettier
|
|
//
|
|
// . after converting JSON to utf8 above we sometimes want to go back.
|
|
// . just print that out. encode \n's and \r's back to \\n \\r
|
|
// and backslash to a \\ ... etc.
|
|
// . but if they originally had a \u<backslash> encoding and we decoded
|
|
// it to a backslash, here it will be re-encoded as (double backslash)
|
|
// . like wise if that originally had a \u<quote> encoding we should
|
|
// have decoded it as a \"!
|
|
// . this does not need to be super fast because it will be used for
|
|
// showing cached pages or dumping out the json objects from a crawl for
|
|
// diffbot
|
|
// . really we could leave the newlines decoded etc, but it is prettier
|
|
// for printing
|
|
/*
|
|
bool SafeBuf::safeStrcpyPrettyJSON ( char *decodedJson ) {
|
|
// how much space do we need?
|
|
// each single byte \t char for instance will need 2 bytes
|
|
int32_t need = gbstrlen(decodedJson) * 2 + 1;
|
|
if ( ! reserve ( need ) ) return false;
|
|
// scan and copy
|
|
char *src = decodedJson;
|
|
// concatenate to what's already there
|
|
char *dst = m_buf + m_length;
|
|
for ( ; *src ; src++ ) {
|
|
|
|
if ( *src == '\t' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = 't';
|
|
continue;
|
|
}
|
|
if ( *src == '\n' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = 'n';
|
|
continue;
|
|
}
|
|
if ( *src == '\r' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = 'r';
|
|
continue;
|
|
}
|
|
if ( *src == '\f' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = 'f';
|
|
continue;
|
|
}
|
|
// do not decode the \ if it's in \" because we did not
|
|
// decode the \" back into " above because we can't afford
|
|
// to lose that info and for indexing purposes it does not
|
|
// index extra crap (like the 'n' in \n),
|
|
// so it's ok to not decode it.
|
|
if ( *src == '\\' && src[1] != '\"' && src[1] !='/' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = '\\';
|
|
continue;
|
|
}
|
|
// mdw: why was this commented out?
|
|
// we converted '\/' above to a single / so we must undo here
|
|
if ( *src == '/' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = '/';
|
|
continue;
|
|
}
|
|
|
|
*dst++ = *src;
|
|
|
|
}
|
|
// null term
|
|
*dst = '\0';
|
|
|
|
m_length = dst - m_buf;
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
bool SafeBuf::jsonEncode ( char *src , int32_t srcLen ) {
|
|
char c = src[srcLen];
|
|
src[srcLen] = 0;
|
|
bool status = jsonEncode ( src );
|
|
src[srcLen] = c;
|
|
return status;
|
|
}
|
|
|
|
// encode into json
|
|
bool SafeBuf::safeUtf8ToJSON ( char *utf8 ) {
|
|
|
|
if ( ! utf8 ) return true;
|
|
|
|
// how much space do we need?
|
|
// each single byte \t char for instance will need 2 bytes
|
|
int32_t need = gbstrlen(utf8) * 2 + 1;
|
|
if ( ! reserve ( need ) ) return false;
|
|
// scan and copy
|
|
char *src = utf8;
|
|
// concatenate to what's already there
|
|
char *dst = m_buf + m_length;
|
|
for ( ; *src ; src++ ) {
|
|
if ( *src == '\"' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = '\"';
|
|
continue;
|
|
}
|
|
if ( *src == '\t' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = 't';
|
|
continue;
|
|
}
|
|
if ( *src == '\n' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = 'n';
|
|
continue;
|
|
}
|
|
if ( *src == '\r' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = 'r';
|
|
continue;
|
|
}
|
|
if ( *src == '\f' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = 'f';
|
|
continue;
|
|
}
|
|
if ( *src == '\\' ) {
|
|
*dst++ = '\\';
|
|
*dst++ = '\\';
|
|
continue;
|
|
}
|
|
//if ( *src == '\/' ) {
|
|
// *dst++ = '\\';
|
|
// *dst++ = '/';
|
|
// continue;
|
|
//}
|
|
|
|
*dst++ = *src;
|
|
|
|
}
|
|
// null term
|
|
*dst = '\0';
|
|
|
|
m_length = dst - m_buf;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool SafeBuf::linkify ( int32_t niceness , int32_t startPos ) {
|
|
|
|
// reserve a little extra in anticipation
|
|
SafeBuf dbuf;
|
|
int32_t avail = 3000;
|
|
dbuf.reserve ( m_length + avail + 1 ); // +1 for \0
|
|
int32_t tally = 0;
|
|
|
|
char *src = m_buf;
|
|
char *dst = dbuf.m_buf;
|
|
|
|
// copy header over
|
|
gbmemcpy ( dst , src , startPos );
|
|
// update them
|
|
src += startPos;
|
|
dst += startPos;
|
|
|
|
bool inTag = 0;
|
|
for ( ; *src ; ) {
|
|
if ( *src == '<' ) inTag = true;
|
|
if ( *src == '>' ) inTag = false;
|
|
// copy over
|
|
*dst++ = *src++;
|
|
// must not be in a tag already!
|
|
if ( inTag ) continue;
|
|
// if not http:// or https:// skip it
|
|
if ( to_lower_a(src[-1]) != 'h' ) continue;
|
|
if ( to_lower_a(src[ 0]) != 't' ) continue;
|
|
if ( to_lower_a(src[ 1]) != 't' ) continue;
|
|
if ( to_lower_a(src[ 2]) != 'p' ) continue;
|
|
char *p = src+3;
|
|
if ( to_lower_a(*p) == 's' ) p++;
|
|
if ( *p != ':' ) continue;
|
|
p++;
|
|
if ( *p != '/' ) continue;
|
|
p++;
|
|
if ( *p != '/' ) continue;
|
|
p++;
|
|
// ok, undo the writing of 'h' above
|
|
src--;
|
|
dst--;
|
|
// get link size then
|
|
char *start = src;
|
|
// stop at first space
|
|
char *end = start;
|
|
for ( ; *end && !is_wspace_a(*end) ; end++ );
|
|
// the link size
|
|
int32_t size = end - start;
|
|
if ( size > 3000 ) {
|
|
// damn, too big, forget it!
|
|
src++;
|
|
dst++;
|
|
continue;
|
|
}
|
|
// tally it
|
|
int32_t need = 24 + size + 2 + 4;
|
|
tally += need;
|
|
if ( tally >= avail ) {
|
|
// damn, too big, forget it!
|
|
src++;
|
|
dst++;
|
|
continue;
|
|
}
|
|
// then store the link
|
|
gbmemcpy ( dst , "<a target=_parent href=\"", 24 );
|
|
dst += 24;
|
|
gbmemcpy ( dst , start , size );
|
|
dst += size;
|
|
gbmemcpy ( dst, "\">" , 2);
|
|
dst += 2;
|
|
// print the link text
|
|
gbmemcpy ( dst , start , size );
|
|
dst += size;
|
|
// the ending anchor
|
|
gbmemcpy ( dst , "</a>" , 4 );
|
|
dst += 4;
|
|
// and skip the src ahead now too, after the link
|
|
src += size;
|
|
}
|
|
|
|
*dst = '\0';
|
|
dbuf.m_length = dst - dbuf.m_buf;
|
|
|
|
// purge ourselves
|
|
purge();
|
|
|
|
// and steal dbuf's m_buf
|
|
m_buf = dbuf.m_buf;
|
|
m_length = dbuf.m_length;
|
|
m_capacity = dbuf.m_capacity;
|
|
m_usingStack = dbuf.m_usingStack;
|
|
|
|
// detach from dbuf so he does not free it
|
|
dbuf.detachBuf();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
bool SafeBuf::brify ( char *s , int32_t slen , int32_t niceness ) {
|
|
// count the xml tags so we know how much buf to allocated
|
|
char *p = s;
|
|
char *pend = s + slen;
|
|
int32_t count = 0;
|
|
for ( ; p < pend ; p++ ) {
|
|
QUICKPOLL(niceness);
|
|
if ( *p == '<' ) count += 6; // assume '>' too!
|
|
if ( *p == '\n' ) count += 3; // <br>
|
|
if ( *p == '\t' ) count += 17; //
|
|
}
|
|
// include \0
|
|
int32_t need = slen + 1;
|
|
// then < has a > and each is expanded to < or >
|
|
need += count ;
|
|
// reserve that
|
|
if ( ! reserve ( need ) ) return false;
|
|
// point to dst buf
|
|
char *dst = m_buf;
|
|
bool inXmlTag = false;
|
|
// copy over now
|
|
p = s;
|
|
for ( ; p < pend ; p++ ) {
|
|
QUICKPOLL(niceness);
|
|
// make > into > then break out
|
|
if ( inXmlTag && *p == '>' ) {
|
|
gbmemcpy ( dst , ">", 4 );
|
|
dst += 4;
|
|
inXmlTag = false;
|
|
continue;
|
|
}
|
|
// translate \n to br for xml docs
|
|
if ( *p == '\n' ) {
|
|
gbmemcpy ( dst , "<br>", 4 );
|
|
dst += 4;
|
|
continue;
|
|
}
|
|
// translate \n to br for xml docs
|
|
if ( *p == '\t' ) {
|
|
gbmemcpy ( dst , " ", 18 );
|
|
dst += 18;
|
|
continue;
|
|
}
|
|
// copy over if nothing special
|
|
if ( *p != '<' ) {
|
|
*dst++ = *p;
|
|
continue;
|
|
}
|
|
// get tag id (will be 0 for </>)
|
|
char *tagName;
|
|
if ( p[1] == '/' ) tagName = p + 2;
|
|
else tagName = p + 1;
|
|
NodeType *nt;
|
|
getTagId ( tagName , &nt );
|
|
// keep html tags
|
|
if ( nt && ! nt->m_isXmlTag ) {
|
|
*dst++ = '<';
|
|
continue;
|
|
}
|
|
// ok, it's an xml tag so use <
|
|
gbmemcpy ( dst, "<", 4 );
|
|
dst += 4;
|
|
inXmlTag = true;
|
|
}
|
|
// store \0 but do not count towards m_length
|
|
*dst = '\0';
|
|
// update length
|
|
m_length = dst - m_buf;
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
bool SafeBuf::brify2 ( char *s , int32_t cols , char *sep , bool isHtml ) {
|
|
return brify ( s, gbstrlen(s), 0 , cols , sep , isHtml );
|
|
}
|
|
|
|
bool SafeBuf::brify ( char *s ,
|
|
int32_t slen ,
|
|
int32_t niceness ,
|
|
int32_t maxCharsPerLine ,
|
|
char *sep ,
|
|
bool isHtml ) {
|
|
// count the xml tags so we know how much buf to allocated
|
|
char *p = s;
|
|
char *pend = s + slen;
|
|
//int32_t count = 0;
|
|
char cs;
|
|
int32_t brSizes = 0;
|
|
bool lastRound = false;
|
|
int32_t col = 0;
|
|
char *pstart = s;
|
|
char *breakPoint = NULL;
|
|
bool inTag = false;
|
|
int32_t sepLen = gbstrlen(sep);
|
|
bool forced = false;
|
|
|
|
redo:
|
|
|
|
for ( ; p < pend ; p += cs ) {
|
|
QUICKPOLL(niceness);
|
|
cs = getUtf8CharSize(p);
|
|
// do not inc count if in a tag
|
|
if ( inTag ) {
|
|
if ( *p == '>' ) inTag = false;
|
|
continue;
|
|
}
|
|
if ( *p == '<' && isHtml ) {
|
|
inTag = true;
|
|
continue;
|
|
}
|
|
col++;
|
|
if ( is_wspace_utf8(p) ) {
|
|
// reset?
|
|
if ( ! isHtml && *p == '\n' ) {
|
|
forced = true;
|
|
breakPoint = p;
|
|
goto forceBreak;
|
|
}
|
|
// apostrophe exceptions
|
|
//if ( *p == '\'' ) continue;
|
|
// break AFTER this punct
|
|
breakPoint = p;
|
|
continue;
|
|
}
|
|
if ( col < maxCharsPerLine ) continue;
|
|
|
|
forceBreak:
|
|
// now add the break point i guess
|
|
// if none, gotta break here for sure!!!
|
|
if ( ! breakPoint ) breakPoint = p;
|
|
// count that
|
|
brSizes += sepLen;//4;
|
|
// print only for last round
|
|
if ( lastRound ) {
|
|
// . print up to that
|
|
// . this includes the \n if forced is true
|
|
safeMemcpy ( pstart , breakPoint - pstart + 1 );
|
|
// then br
|
|
//if ( forced ) pushChar('\n');
|
|
if ( ! forced )
|
|
safeMemcpy ( sep , sepLen ) ; // "<br>"
|
|
forced = false;
|
|
}
|
|
// start right after breakpoint for next line
|
|
p = breakPoint;
|
|
cs = getUtf8CharSize(p);
|
|
pstart = p + cs;
|
|
// nuke it
|
|
breakPoint = NULL;
|
|
col = 0;
|
|
continue;
|
|
}
|
|
|
|
// print out the last line which never hit the maxCharsPerLine barrier
|
|
if ( lastRound && p - pstart ) {
|
|
// print up to that
|
|
safeMemcpy ( pstart , p - pstart );
|
|
// then br
|
|
//safeMemcpy ( "<br>" , 4 );
|
|
}
|
|
|
|
if ( lastRound ) return true;
|
|
|
|
// alloc that space. return false with g_errno set on error
|
|
if ( brSizes && ! reserve ( brSizes ) ) return false;
|
|
|
|
// reset ptrs
|
|
p = s;
|
|
pstart = s;
|
|
col = 0;
|
|
breakPoint = NULL;
|
|
|
|
// now do it again but for real!
|
|
lastRound = true;
|
|
forced = false;
|
|
goto redo;
|
|
return true;
|
|
}
|
|
|
|
#include "XmlDoc.h"
|
|
|
|
// . these use zlib
|
|
// . return false with g_errno set on error
|
|
bool SafeBuf::compress() {
|
|
// how much space do we need, worst case?
|
|
int32_t need = ((int64_t)length() * 1001LL) / 1000LL + 13 + 12;
|
|
// alloc new buf then
|
|
char *newBuf = (char *)mmalloc(need,"sbcomp");
|
|
if ( ! newBuf ) return false;
|
|
// save it
|
|
int32_t origLen = length();
|
|
// store it here
|
|
char *dest = newBuf + 4 ;
|
|
int32_t destLen = need - 4;
|
|
// do it
|
|
int32_t err = gbcompress ( (unsigned char *)dest ,
|
|
(uint32_t *)&destLen,
|
|
(unsigned char *)m_buf ,
|
|
(uint32_t) m_length );
|
|
// error?
|
|
if ( err != Z_OK ) {
|
|
log("sbuf: error compressing!");
|
|
return false;
|
|
}
|
|
// now free old buf
|
|
purge();
|
|
// sanity
|
|
if ( destLen > need - 4 ) { char *xx=NULL;*xx=0; }
|
|
// store length here too now
|
|
*(int32_t *)newBuf = origLen;
|
|
// . and set to new buf
|
|
// . the first 4 bytes is uncompressed size of the compressed data
|
|
// so we can send over network
|
|
setBuf ( newBuf ,
|
|
need, // buffer max size
|
|
4 + destLen, // bytes used, "m_length"
|
|
true , // owndata?
|
|
0 ); // encoding
|
|
return true;
|
|
}
|
|
|
|
|
|
// these use zlib
|
|
bool SafeBuf::uncompress() {
|
|
// how much space do we need, worst case?
|
|
int32_t need = *(int32_t *)m_buf;
|
|
// alloc new buf then
|
|
char *newBuf = (char *)mmalloc(need,"sbcomp");
|
|
if ( ! newBuf ) return false;
|
|
// the first 4 bytes are the uncompressed size, "need"
|
|
int32_t avail = need;
|
|
// do it
|
|
int err = gbuncompress ( (unsigned char *)newBuf,
|
|
(uint32_t *)&avail ,
|
|
(unsigned char *)m_buf+4,
|
|
(uint32_t)m_length-4 );
|
|
if ( err != Z_OK ) {
|
|
log("sbuf: gbuncompress: error!");
|
|
return false;
|
|
}
|
|
// sanity
|
|
if ( avail != need ) { char *xx=NULL;*xx=0; }
|
|
// now free old buf
|
|
purge();
|
|
// and set to new buf
|
|
setBuf ( newBuf ,
|
|
need , // buffer max size
|
|
need , // bytes in use
|
|
true , // owndata?
|
|
0 ); // encoding
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::safeTruncateEllipsis ( char *src , int32_t maxLen ) {
|
|
int32_t srcLen = gbstrlen(src);
|
|
return safeTruncateEllipsis ( src , srcLen , maxLen );
|
|
}
|
|
|
|
bool SafeBuf::safeTruncateEllipsis ( char *src , int32_t srcLen , int32_t maxLen ) {
|
|
int32_t printLen = srcLen;
|
|
if ( printLen > maxLen ) printLen = maxLen;
|
|
if ( ! safeMemcpy ( src , printLen ) )
|
|
return false;
|
|
if ( srcLen < maxLen ) {
|
|
if ( ! nullTerm() ) return false;
|
|
return true;
|
|
}
|
|
if ( ! safeMemcpy("...",3) )
|
|
return false;
|
|
if ( ! nullTerm() )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
#include "sort.h"
|
|
|
|
static int int32_tcmp4 ( const void *a, const void *b ) {
|
|
if ( *(int32_t *)a > *(int32_t *)b ) return 1; // swap
|
|
if ( *(int32_t *)a < *(int32_t *)b ) return -1;
|
|
return 0;
|
|
}
|
|
|
|
void SafeBuf::sortLongs( int32_t niceness ) {
|
|
int32_t np = m_length / 4;
|
|
gbqsort ( m_buf , np , 4 , int32_tcmp4 , niceness );
|
|
}
|
|
|
|
bool SafeBuf::htmlDecode ( char *src,
|
|
int32_t srcLen,
|
|
bool doSpecial ,
|
|
int32_t niceness ) {
|
|
// in case we were in use
|
|
purge();
|
|
// make sure we have enough room
|
|
if ( ! reserve ( srcLen + 1 ) ) return false;
|
|
// . just decode buffer into our m_buf that we reserved
|
|
// . it puts a \0 at the end and returns the LENGTH of the string
|
|
// it put into m_buf, not including the \0
|
|
int32_t newLen = ::htmlDecode ( m_buf ,
|
|
src,
|
|
srcLen,
|
|
false ,
|
|
niceness );
|
|
// assign that length then
|
|
m_length = newLen;
|
|
// good to go
|
|
return true;
|
|
}
|
|
|
|
void SafeBuf::replaceChar ( char src , char dst ) {
|
|
char *px = m_buf;
|
|
char *pxEnd = m_buf + m_length;
|
|
for ( ; px < pxEnd ; px++ ) if ( *px == src ) *px = dst;
|
|
}
|
|
|
|
|
|
// encode a double quote char to two double quote chars
|
|
bool SafeBuf::csvEncode ( char *s , int32_t len , int32_t niceness ) {
|
|
|
|
if ( ! s ) return true;
|
|
|
|
// assume all chars are double quotes and will have to be encoded
|
|
int32_t need = len * 2 + 1;
|
|
if ( ! reserve ( need ) ) return false;
|
|
|
|
// tmp vars
|
|
char *dst = m_buf + m_length;
|
|
//char *dstEnd = m_buf + m_capacity;
|
|
|
|
// scan through all
|
|
char *send = s + len;
|
|
for ( ; s < send ; s++ ) {
|
|
// breathe
|
|
QUICKPOLL ( niceness );
|
|
// convert it?
|
|
if ( *s == '\"' ) {
|
|
*dst++ = '\"';
|
|
*dst++ = '\"';
|
|
continue;
|
|
}
|
|
//if ( *s == '\\' ) {
|
|
// *dst++ = '\\';
|
|
// *dst++ = '\\';
|
|
// continue;
|
|
//}
|
|
*dst++ = *s;
|
|
}
|
|
|
|
m_length += dst - (m_buf + m_length);
|
|
|
|
nullTerm();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::base64Encode ( char *sx , int32_t len , int32_t niceness ) {
|
|
|
|
unsigned char *s = (unsigned char *)sx;
|
|
|
|
if ( ! s ) return true;
|
|
|
|
// assume all chars are double quotes and will have to be encoded
|
|
int32_t need = len * 2 + 1 +3; // +3 for = padding
|
|
|
|
if ( ! reserve ( need ) ) return false;
|
|
|
|
// tmp vars
|
|
char *dst = m_buf + m_length;
|
|
|
|
int32_t round = 0;
|
|
|
|
// the table of 64 entities
|
|
static char tab[] = {
|
|
'A','B','C','D','E','F','G','H','I','J','K','L','M',
|
|
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
|
|
'a','b','c','d','e','f','g','h','i','j','k','l','m',
|
|
'n','o','p','q','r','s','t','u','v','w','x','y','z',
|
|
'0','1','2','3','4','5','6','7','8','9','+','/'
|
|
};
|
|
|
|
unsigned char val;
|
|
// scan through all
|
|
unsigned char *send = s + len;
|
|
for ( ; s < send ; ) {
|
|
// breathe
|
|
QUICKPOLL ( niceness );
|
|
|
|
unsigned char c1 = s[0];
|
|
unsigned char c2 = 0;
|
|
//unsigned char c3 = 0;
|
|
|
|
if ( s+1 < send ) c2 = s[1];
|
|
else c2 = 0;
|
|
|
|
if ( round == 0 ) {
|
|
val = c1 >>2;
|
|
}
|
|
else if ( round == 1 ) {
|
|
val = (c1 & 0x03) << 4;
|
|
val |= c2 >> 4;
|
|
// time for this
|
|
s++;
|
|
}
|
|
else if ( round == 2 ) {
|
|
val = ((c1 & 0x0f) << 2);
|
|
val |= ((c2 & 0xc0) >> 6);
|
|
s++;
|
|
}
|
|
else if ( round == 3 ) {
|
|
val = (c1 & 0x3f);
|
|
s++;
|
|
}
|
|
// add '0'
|
|
*dst = tab[val];
|
|
// point to next char
|
|
dst++;
|
|
// keep going if more left
|
|
if ( s < send ) {
|
|
// repeat every 4 cycles since it is aligned then
|
|
if ( ++round == 4 ) round = 0;
|
|
continue;
|
|
}
|
|
// if we are done do padding
|
|
if ( round == 0 ) {
|
|
*dst++ = '=';
|
|
}
|
|
if ( round == 1 ) {
|
|
*dst++ = '=';
|
|
*dst++ = '=';
|
|
}
|
|
if ( round == 2 ) {
|
|
*dst++ = '=';
|
|
}
|
|
|
|
|
|
}
|
|
|
|
m_length += dst - (m_buf + m_length);
|
|
|
|
nullTerm();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::base64Encode( char *s ) {
|
|
return base64Encode(s,gbstrlen(s));
|
|
}
|
|
|
|
bool SafeBuf::base64Decode ( char *src , int32_t srcLen , int32_t niceness ) {
|
|
|
|
// make the map
|
|
static unsigned char s_bmap[256];
|
|
static bool s_init = false;
|
|
if ( ! s_init ) {
|
|
s_init = true;
|
|
memset ( s_bmap , 0 , 256 );
|
|
unsigned char val = 0;
|
|
for ( unsigned char c = 'A' ; c <= 'Z'; c++ )
|
|
s_bmap[c] = val++;
|
|
for ( unsigned char c = 'a' ; c <= 'z'; c++ )
|
|
s_bmap[c] = val++;
|
|
for ( unsigned char c = '0' ; c <= '9'; c++ )
|
|
s_bmap[c] = val++;
|
|
if ( val != 62 ) { char *xx=NULL;*xx=0; }
|
|
s_bmap[(unsigned char)'+'] = 62;
|
|
s_bmap[(unsigned char)'/'] = 63;
|
|
}
|
|
|
|
// reserve twice as much space i guess
|
|
if ( ! reserve ( srcLen * 2 + 1 ) ) return false;
|
|
|
|
// leave room for \0
|
|
char *dst = getBuf();
|
|
char *dstEnd = getBufEnd(); // dst + dstSize - 5;
|
|
nullTerm();
|
|
unsigned char *p = (unsigned char *)src;
|
|
unsigned char val;
|
|
for ( ; ; ) {
|
|
QUICKPOLL(niceness);
|
|
if ( *p ) {val = s_bmap[*p]; p++; } else val = 0;
|
|
// copy 6 bits
|
|
*dst <<= 6;
|
|
*dst |= val;
|
|
if ( *p ) {val = s_bmap[*p]; p++; } else val = 0;
|
|
// copy 2 bits
|
|
*dst <<= 2;
|
|
*dst |= (val>>4);
|
|
dst++;
|
|
// copy 4 bits
|
|
*dst = val & 0xf;
|
|
if ( *p ) {val = s_bmap[*p]; p++; } else val = 0;
|
|
// copy 4 bits
|
|
*dst <<= 4;
|
|
*dst |= (val>>2);
|
|
dst++;
|
|
// copy 2 bits
|
|
*dst = (val&0x3);
|
|
if ( *p ) {val = s_bmap[*p]; p++; } else val = 0;
|
|
// copy 6 bits
|
|
*dst <<= 6;
|
|
*dst |= val;
|
|
dst++;
|
|
// sanity
|
|
if ( dst >= dstEnd ) {
|
|
log("safebuf: bas64decode breach");
|
|
//char *xx=NULL;*xx=0;
|
|
*dst = '\0';
|
|
return false;
|
|
}
|
|
if ( ! *p ) break;
|
|
}
|
|
// update
|
|
m_length = dst - m_buf;
|
|
// null term just in case
|
|
//dst[1] = '\0';
|
|
nullTerm();
|
|
return true;
|
|
}
|
|
|
|
|
|
// "ts" is a delta-t in seconds
|
|
bool SafeBuf::printTimeAgo ( int32_t ago , int32_t now , bool int16_thand ) {
|
|
// Jul 23, 1971
|
|
if ( ! reserve2x(200) ) return false;
|
|
// for printing
|
|
int32_t secs = 1000;
|
|
int32_t mins = 1000;
|
|
int32_t hrs = 1000;
|
|
int32_t days ;
|
|
if ( ago > 0 ) {
|
|
secs = (int32_t)((ago)/1);
|
|
mins = (int32_t)((ago)/60);
|
|
hrs = (int32_t)((ago)/3600);
|
|
days = (int32_t)((ago)/(3600*24));
|
|
if ( mins < 0 ) mins = 0;
|
|
if ( hrs < 0 ) hrs = 0;
|
|
if ( days < 0 ) days = 0;
|
|
}
|
|
bool printed = false;
|
|
// print the time ago
|
|
if ( int16_thand ) {
|
|
if ( mins==0 ) safePrintf("%" INT32 " secs ago",secs);
|
|
else if ( mins ==1)safePrintf("%" INT32 " min ago",mins);
|
|
else if (mins<60)safePrintf ( "%" INT32 " mins ago",mins);
|
|
else if ( hrs == 1 )safePrintf ( "%" INT32 " hr ago",hrs);
|
|
else if ( hrs < 24 )safePrintf ( "%" INT32 " hrs ago",hrs);
|
|
else if ( days == 1 )safePrintf ( "%" INT32 " day ago",days);
|
|
else if (days< 7 )safePrintf ( "%" INT32 " days ago",days);
|
|
printed = true;
|
|
}
|
|
else {
|
|
if ( mins==0 ) safePrintf("%" INT32 " seconds ago",secs);
|
|
else if ( mins ==1)safePrintf("%" INT32 " minute ago",mins);
|
|
else if (mins<60)safePrintf ( "%" INT32 " minutes ago",mins);
|
|
else if ( hrs == 1 )safePrintf ( "%" INT32 " hour ago",hrs);
|
|
else if ( hrs < 24 )safePrintf ( "%" INT32 " hours ago",hrs);
|
|
else if ( days == 1 )safePrintf ( "%" INT32 " day ago",days);
|
|
else if (days< 7 )safePrintf ( "%" INT32 " days ago",days);
|
|
printed = true;
|
|
}
|
|
// do not show if more than 1 wk old! we want to seem as
|
|
// fresh as possible
|
|
|
|
if ( ! printed && ago > 0 ) { // && si->m_isMasterAdmin ) {
|
|
int32_t ts = now - ago;
|
|
struct tm *timeStruct = localtime ( (time_t *)&ts );
|
|
char tmp[100];
|
|
strftime(tmp,100,"%b %d %Y",timeStruct);
|
|
safeStrcpy(tmp);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SafeBuf::hasDigits() {
|
|
if ( m_length <= 0 ) return false;
|
|
for ( int32_t i = 0 ; i < m_length ; i++ )
|
|
if ( is_digit(m_buf[i]) ) return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
int32_t SafeBuf::indexOf(char c) {
|
|
char* p = m_buf;
|
|
char* pend = m_buf + m_length;
|
|
while (p < pend && *p != c) p++;
|
|
if (p == pend) return -1;
|
|
return p - m_buf;
|
|
}
|