943 lines
23 KiB
C++
943 lines
23 KiB
C++
|
|
// +-------------------------------------------------------------------------+
|
||
|
|
// | Script Plus Plus vers. 0.3.72 |
|
||
|
|
// | Copyright (c) Andrey V. Stolyarov <croco at croco dot net> 2003--2023 |
|
||
|
|
// | ----------------------------------------------------------------------- |
|
||
|
|
// | This is free software. Permission is granted to everyone to use, copy |
|
||
|
|
// | or modify this software under the terms and conditions of |
|
||
|
|
// | GNU LESSER GENERAL PUBLIC LICENSE, v. 2.1 |
|
||
|
|
// | as published by Free Software Foundation (see the file LGPL.txt) |
|
||
|
|
// | |
|
||
|
|
// | Please visit http://www.croco.net/software/scriptpp to get a fresh copy |
|
||
|
|
// | ----------------------------------------------------------------------- |
|
||
|
|
// | This code is provided strictly and exclusively on the "AS IS" basis. |
|
||
|
|
// | !!! THERE IS NO WARRANTY OF ANY KIND, NEITHER EXPRESSED NOR IMPLIED !!! |
|
||
|
|
// +-------------------------------------------------------------------------+
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <stddef.h>
|
||
|
|
|
||
|
|
#include "scrvar.hpp"
|
||
|
|
|
||
|
|
|
||
|
|
enum { size_of_memblock_header = sizeof(int) * 2 };
|
||
|
|
|
||
|
|
|
||
|
|
static ScriptVariableImplementation TheEmptyString = { 1, 0, 0, "" };
|
||
|
|
|
||
|
|
|
||
|
|
ScriptVariable::ScriptVariable()
|
||
|
|
: p(0)
|
||
|
|
{
|
||
|
|
Assign(&TheEmptyString);
|
||
|
|
}
|
||
|
|
|
||
|
|
#if 0
|
||
|
|
ScriptVariable::ScriptVariable(int len)
|
||
|
|
: p(0)
|
||
|
|
{
|
||
|
|
if(len != -1) {
|
||
|
|
Create(len);
|
||
|
|
p->buf[0] = 0;
|
||
|
|
p->len_cached = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
ScriptVariable::ScriptVariable(const char *s, int len)
|
||
|
|
: p(0)
|
||
|
|
{
|
||
|
|
if(s && len != -1) {
|
||
|
|
Create(len);
|
||
|
|
memcpy(p->buf, s, len);
|
||
|
|
// p->buf[len] = 0; // no need for this, done by Create()
|
||
|
|
p->len_cached = len;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::ScriptVariable(const char *str)
|
||
|
|
: p(0)
|
||
|
|
{
|
||
|
|
if(str) {
|
||
|
|
int len = strlen(str);
|
||
|
|
Create(len);
|
||
|
|
memcpy(p->buf, str, len);
|
||
|
|
p->len_cached = len;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::ScriptVariable(const ScriptVariable& other)
|
||
|
|
: p(0)
|
||
|
|
{
|
||
|
|
Assign(other.p);
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::~ScriptVariable()
|
||
|
|
{
|
||
|
|
Unlink();
|
||
|
|
}
|
||
|
|
|
||
|
|
void ScriptVariable::MakeRoom(int len)
|
||
|
|
{
|
||
|
|
if(!p) {
|
||
|
|
Create(len);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if(p->maxlen >= len)
|
||
|
|
return;
|
||
|
|
ScriptVariableInv res;
|
||
|
|
res.Create(len);
|
||
|
|
memcpy(res.p->buf, p->buf, len+1);
|
||
|
|
Assign(res.p);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ScriptVariable::Unlink()
|
||
|
|
{
|
||
|
|
if(p) {
|
||
|
|
if(--(p->refcount)<=0)
|
||
|
|
free(p);
|
||
|
|
p = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void ScriptVariable::Assign(ScriptVariableImplementation *q)
|
||
|
|
{
|
||
|
|
Unlink();
|
||
|
|
p = q;
|
||
|
|
if(p)
|
||
|
|
p->refcount++;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ScriptVariable::Create(int len)
|
||
|
|
{
|
||
|
|
Unlink();
|
||
|
|
int efflen = 16;
|
||
|
|
int hdrsize = offsetof(ScriptVariableImplementation, buf);
|
||
|
|
int minsize = hdrsize + len + 1;
|
||
|
|
while(efflen < minsize)
|
||
|
|
efflen *= 2;
|
||
|
|
p = reinterpret_cast<ScriptVariableImplementation*>(malloc(efflen));
|
||
|
|
p->refcount = 1;
|
||
|
|
p->maxlen = efflen - hdrsize - 1;
|
||
|
|
p->len_cached = -1;
|
||
|
|
p->buf[len] = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ScriptVariable::EnsureOwnCopy()
|
||
|
|
{
|
||
|
|
if(p && p->refcount > 1) {
|
||
|
|
int len = Length();
|
||
|
|
ScriptVariableImplementation *tmp = p;
|
||
|
|
Create(len);
|
||
|
|
memcpy(p->buf, tmp->buf, len);
|
||
|
|
p->len_cached = len;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int ScriptVariable::Length() const
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return 0;
|
||
|
|
if(p->len_cached == -1)
|
||
|
|
p->len_cached = strlen(p->buf);
|
||
|
|
return p->len_cached;
|
||
|
|
}
|
||
|
|
|
||
|
|
const char *ScriptVariable::c_str() const
|
||
|
|
{
|
||
|
|
return p ? p->buf : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
char ScriptVariable::operator[](int i) const
|
||
|
|
{
|
||
|
|
return p ? p->buf[i] : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
char& ScriptVariable::operator[](int i)
|
||
|
|
{
|
||
|
|
if(p)
|
||
|
|
EnsureOwnCopy();
|
||
|
|
else
|
||
|
|
Create(i+1);
|
||
|
|
p->len_cached = -1;
|
||
|
|
// because our caller can truncate the string assigning zero
|
||
|
|
return p->buf[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
////////////////////////////////////////
|
||
|
|
|
||
|
|
ScriptVariable ScriptVariable::operator+(const char *o2) const
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return *this;
|
||
|
|
int len1 = Length();
|
||
|
|
int len2 = strlen(o2);
|
||
|
|
int newlen = len1 + len2;
|
||
|
|
ScriptVariableInv res;
|
||
|
|
res.Create(newlen);
|
||
|
|
memcpy(res.p->buf, p->buf, len1);
|
||
|
|
memcpy(res.p->buf+len1, o2, len2);
|
||
|
|
res.p->buf[newlen] = 0;
|
||
|
|
res.p->len_cached = newlen;
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable& ScriptVariable::operator+=(const char *o2)
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return *this;
|
||
|
|
int len1 = Length();
|
||
|
|
int len2 = strlen(o2);
|
||
|
|
int newlen = len1 + len2;
|
||
|
|
if(p && p->refcount == 1 && p->maxlen >= newlen) {
|
||
|
|
// in this special case we can just copy the second string in
|
||
|
|
memcpy(p->buf+len1, o2, len2);
|
||
|
|
p->buf[newlen] = 0;
|
||
|
|
p->len_cached = newlen;
|
||
|
|
} else {
|
||
|
|
// we have to create another one, so let's just make it sum...
|
||
|
|
ScriptVariable res(*this + o2);
|
||
|
|
Assign(res.p);
|
||
|
|
}
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable& ScriptVariable::operator=(const char *o2)
|
||
|
|
{
|
||
|
|
int len2 = strlen(o2);
|
||
|
|
Create(len2);
|
||
|
|
memcpy(p->buf, o2, len2 + 1);
|
||
|
|
p->len_cached = len2;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
///////////////////////////
|
||
|
|
|
||
|
|
ScriptVariable ScriptVariable::operator+(char c) const
|
||
|
|
{
|
||
|
|
// if(!p) return *this; // not needed, the subseq. call will do
|
||
|
|
char bb[2];
|
||
|
|
bb[0]=c; bb[1]=0;
|
||
|
|
return (*this)+bb;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable& ScriptVariable::operator+=(char c)
|
||
|
|
{
|
||
|
|
// if(!p) return *this; // not needed, the subseq. call will do
|
||
|
|
char bb[2];
|
||
|
|
bb[0]=c; bb[1]=0;
|
||
|
|
return (*this)+=bb;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable& ScriptVariable::operator=(char c)
|
||
|
|
{
|
||
|
|
char bb[2];
|
||
|
|
bb[0]=c; bb[1]=0;
|
||
|
|
return (*this)=bb;
|
||
|
|
}
|
||
|
|
|
||
|
|
///////////////////////////////////////////
|
||
|
|
|
||
|
|
ScriptVariable ScriptVariable::operator+(const ScriptVariable &o2) const
|
||
|
|
{
|
||
|
|
if(!o2.p)
|
||
|
|
return o2; // it is invalid, return the invalid str...
|
||
|
|
return (*this) + o2.p->buf;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable& ScriptVariable::operator+=(const ScriptVariable &o2)
|
||
|
|
{
|
||
|
|
if(!o2.p)
|
||
|
|
Invalidate(); // it was invalid, we're now invalid, too
|
||
|
|
else
|
||
|
|
(*this) += o2.p->buf;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable& ScriptVariable::operator=(const ScriptVariable &o2)
|
||
|
|
{
|
||
|
|
Assign(o2.p);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
int ScriptVariable::Strcmp(const ScriptVariable &o2) const
|
||
|
|
{
|
||
|
|
if(!p || !o2.p) { // well, let's think Invalid is lesser than everything
|
||
|
|
if(!p && !o2.p)
|
||
|
|
return 0; // both are invalid
|
||
|
|
if(!p)
|
||
|
|
return -1; // the first is invalid, the second is not
|
||
|
|
return 1; // the first is valid
|
||
|
|
}
|
||
|
|
return strcmp(p->buf, o2.p->buf);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::HasPrefix(const char *pr) const
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return false;
|
||
|
|
for(const char *qq = p->buf; *pr; pr++, qq++)
|
||
|
|
if(*pr!=*qq)
|
||
|
|
return false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::HasPrefix(const ScriptVariable& prefix) const
|
||
|
|
{
|
||
|
|
return HasPrefix(prefix.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::HasSuffix(const char *suf) const
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return false;
|
||
|
|
int suflen = strlen(suf);
|
||
|
|
for(const char *qq = p->buf + Length() - suflen; *suf; suf++, qq++)
|
||
|
|
if(*suf!=*qq)
|
||
|
|
return false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::HasSuffix(const ScriptVariable& suffix) const
|
||
|
|
{
|
||
|
|
if(!p || !suffix.p)
|
||
|
|
return false;
|
||
|
|
int l = suffix.Length();
|
||
|
|
return (*const_cast<ScriptVariable*>(this)).Range(-l, l).Get() == suffix;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable& ScriptVariable::Trim(const char *spaces)
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return *this;
|
||
|
|
(*this) = Whole().Trim(spaces).Get();
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
const ScriptVariable& ScriptVariable::Toupper()
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return *this;
|
||
|
|
EnsureOwnCopy();
|
||
|
|
int len = Length();
|
||
|
|
for(int i=0; i<len; i++) {
|
||
|
|
char c = p->buf[i];
|
||
|
|
if(c >= 'a' && c <= 'z')
|
||
|
|
p->buf[i] -= ('a' - 'A');
|
||
|
|
}
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
const ScriptVariable& ScriptVariable::Tolower()
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return *this;
|
||
|
|
EnsureOwnCopy();
|
||
|
|
int len = Length();
|
||
|
|
for(int i=0; i<len; i++) {
|
||
|
|
char c = p->buf[i];
|
||
|
|
if(c >= 'A' && c <= 'Z')
|
||
|
|
p->buf[i] += ('a' - 'A');
|
||
|
|
}
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring::
|
||
|
|
Substring(ScriptVariable &a_master, int a_pos, int a_len)
|
||
|
|
{
|
||
|
|
master = &a_master;
|
||
|
|
if(master->IsValid()) {
|
||
|
|
pos = a_pos>=0 ? a_pos : (master->Length() + a_pos);
|
||
|
|
if(pos<0)
|
||
|
|
pos = 0;
|
||
|
|
len = a_len;
|
||
|
|
if(len == -1)
|
||
|
|
len = master->Length() - pos;
|
||
|
|
if(pos+len > master->p->maxlen)
|
||
|
|
len = master->p->maxlen - pos;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void ScriptVariable::Substring::Erase()
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return;
|
||
|
|
master->EnsureOwnCopy();
|
||
|
|
// Isn't the string really shorter than it is needed?
|
||
|
|
for(int i = pos; i < pos+len; i++) {
|
||
|
|
if(!master->p->buf[i]) { // it is; just truncate it
|
||
|
|
master->p->buf[pos] = 0;
|
||
|
|
master->p->len_cached = -1;
|
||
|
|
len = 0;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for(char *p = master->p->buf + pos; (*p = *(p+len)); p++)
|
||
|
|
;
|
||
|
|
master->p->len_cached = -1;
|
||
|
|
len = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void ScriptVariable::Substring::Replace(const char *what)
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return;
|
||
|
|
master->EnsureOwnCopy();
|
||
|
|
int mlen = master->Length();
|
||
|
|
if(pos+len > mlen)
|
||
|
|
len = mlen - pos;
|
||
|
|
int whatlen = strlen(what);
|
||
|
|
if(whatlen > len) { // first move the rest forward
|
||
|
|
// do we have enough room for it?
|
||
|
|
int delta = whatlen - len;
|
||
|
|
if(mlen+delta <= master->p->maxlen) {
|
||
|
|
// yes, we've got enough room
|
||
|
|
memmove(master->p->buf + pos + whatlen,
|
||
|
|
master->p->buf + pos + len,
|
||
|
|
mlen - pos - len + 1);
|
||
|
|
master->p->buf[mlen+delta] = 0; // sanity
|
||
|
|
master->p->len_cached = -1;
|
||
|
|
} else {
|
||
|
|
// not enough room, don't bother moving
|
||
|
|
ScriptVariableInv tmp;
|
||
|
|
tmp.Create(mlen+delta);
|
||
|
|
memmove(tmp.p->buf, master->p->buf, pos);
|
||
|
|
memmove(tmp.p->buf+whatlen, master->p->buf+len,
|
||
|
|
mlen - pos - len + 1);
|
||
|
|
tmp.p->len_cached = -1;
|
||
|
|
(*master) = tmp;
|
||
|
|
}
|
||
|
|
} else
|
||
|
|
if(whatlen < len) { // first move the rest backward
|
||
|
|
memmove(master->p->buf + pos + whatlen,
|
||
|
|
master->p->buf + pos + len,
|
||
|
|
mlen - pos - len + 1);
|
||
|
|
master->p->len_cached = -1;
|
||
|
|
}
|
||
|
|
// now just replace
|
||
|
|
memcpy(master->p->buf + pos, what, whatlen);
|
||
|
|
master->p->len_cached = -1;
|
||
|
|
len = whatlen;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable ScriptVariable::Substring::Get() const
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return ScriptVariableInv();
|
||
|
|
int llen = len>=0 ? len : master->Length()-pos;
|
||
|
|
if(llen<=0)
|
||
|
|
return ScriptVariable("");
|
||
|
|
ScriptVariableInv res;
|
||
|
|
res.Create(llen);
|
||
|
|
memcpy(res.p->buf, master->p->buf + pos, llen);
|
||
|
|
res.p->buf[llen] = 0;
|
||
|
|
res.p->len_cached = llen;
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring ScriptVariable::Substring::Before() const
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return Substring(); // invalid
|
||
|
|
return Substring(*master, 0, pos);
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring ScriptVariable::Substring::After() const
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return Substring(); // invalid
|
||
|
|
return Substring(*master, pos + len, master->Length() - pos - len);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::Substring::FetchWord(Substring &word, const char *spaces)
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return false;
|
||
|
|
char *p = master->p->buf + pos;
|
||
|
|
char *q = p;
|
||
|
|
while(*p && strchr(spaces, *p))
|
||
|
|
p++;
|
||
|
|
if(!*p)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
word.master = master;
|
||
|
|
word.pos = pos + (p-q); // skip spaces
|
||
|
|
|
||
|
|
q = p;
|
||
|
|
while(*p && !strchr(spaces, *p))
|
||
|
|
p++;
|
||
|
|
word.len = p-q;
|
||
|
|
|
||
|
|
int fetchedlen = word.pos + word.len - pos;
|
||
|
|
pos += fetchedlen;
|
||
|
|
len -= fetchedlen;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::Substring::
|
||
|
|
FetchToken(Substring &token,
|
||
|
|
const char *delimiters,
|
||
|
|
const char *trim_spaces)
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return false;
|
||
|
|
if(len<0) // this means nothing left in the string
|
||
|
|
return false;
|
||
|
|
// else, at least one token exists
|
||
|
|
char *p = master->p->buf + pos;
|
||
|
|
token.master = master;
|
||
|
|
token.pos = pos;
|
||
|
|
|
||
|
|
char *q = p;
|
||
|
|
|
||
|
|
while(*p && !strchr(delimiters, *p))
|
||
|
|
p++;
|
||
|
|
|
||
|
|
if(*p) {
|
||
|
|
// delimiter found; there will be another token...
|
||
|
|
token.len = (p-q);
|
||
|
|
pos += p-q+1;
|
||
|
|
len -= p-q+1;
|
||
|
|
} else {
|
||
|
|
// no delimiters; this will be the last and the only token
|
||
|
|
token.len = len;
|
||
|
|
pos += len;
|
||
|
|
len = -1; // no more tokens!
|
||
|
|
}
|
||
|
|
|
||
|
|
if(trim_spaces && *trim_spaces)
|
||
|
|
token.Trim(trim_spaces);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring
|
||
|
|
ScriptVariable::Substring::FetchWord(const char *spaces)
|
||
|
|
{
|
||
|
|
Substring ret;
|
||
|
|
FetchWord(ret, spaces);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring ScriptVariable::Substring::
|
||
|
|
FetchToken(const char *delimiters, const char *trim_spaces)
|
||
|
|
{
|
||
|
|
Substring ret;
|
||
|
|
FetchToken(ret, delimiters, trim_spaces);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring& ScriptVariable::Substring::Move(int d)
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return *this;
|
||
|
|
pos+=d;
|
||
|
|
if(pos < 0) pos = 0;
|
||
|
|
if(pos >= master->p->maxlen)
|
||
|
|
pos = master->p->maxlen-1;
|
||
|
|
if(pos+len > master->p->maxlen)
|
||
|
|
len = master->p->maxlen - pos;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring& ScriptVariable::Substring::Resize(int d)
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return *this;
|
||
|
|
len+=d;
|
||
|
|
if(len<0)
|
||
|
|
len = 0;
|
||
|
|
else
|
||
|
|
if(pos+len > master->p->maxlen)
|
||
|
|
len = master->p->maxlen - pos;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring& ScriptVariable::Substring::ExtendToBegin()
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return *this;
|
||
|
|
len += pos;
|
||
|
|
pos = 0;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring& ScriptVariable::Substring::ExtendToEnd()
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return *this;
|
||
|
|
len = master->Length() - pos;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring& ScriptVariable::Substring::SetLength(int nl)
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return *this;
|
||
|
|
int maxlen = master->Length() - pos;
|
||
|
|
// for negative nl, we just make the substring as long as possible
|
||
|
|
len = (nl >= 0 && nl < maxlen) ? nl : maxlen;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring&
|
||
|
|
ScriptVariable::Substring::Trim(const char *spaces)
|
||
|
|
{
|
||
|
|
if(IsInvalid())
|
||
|
|
return *this;
|
||
|
|
char *p = master->p->buf + pos;
|
||
|
|
char *q = p;
|
||
|
|
while(*p && strchr(spaces, *p))
|
||
|
|
p++;
|
||
|
|
pos+=(p-q);
|
||
|
|
len-=(p-q);
|
||
|
|
q=p;
|
||
|
|
p+=len-1;
|
||
|
|
while(p>q && strchr(spaces, *p))
|
||
|
|
p--;
|
||
|
|
len = p-q+1;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring ScriptVariable::Strchr(int c)
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return ScriptVariable::Substring(); // invalid
|
||
|
|
const char *t = strchr(c_str(), c);
|
||
|
|
if(!t)
|
||
|
|
return ScriptVariable::Substring(); // invalid
|
||
|
|
return ScriptVariable::Substring(*this, t-c_str(), 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring ScriptVariable::Strrchr(int c)
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return ScriptVariable::Substring(); // invalid
|
||
|
|
const char *t = strrchr(c_str(), c);
|
||
|
|
if(!t)
|
||
|
|
return ScriptVariable::Substring(); // invalid
|
||
|
|
return ScriptVariable::Substring(*this, t-c_str(), 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring ScriptVariable::Strstr(const char *str)
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return ScriptVariable::Substring(); // invalid
|
||
|
|
const char *t = strstr(c_str(), str);
|
||
|
|
if(!t)
|
||
|
|
return ScriptVariable::Substring(); // invalid
|
||
|
|
return ScriptVariable::Substring(*this, t-c_str(), strlen(str));
|
||
|
|
}
|
||
|
|
|
||
|
|
ScriptVariable::Substring ScriptVariable::Strrstr(const char *str)
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return ScriptVariable::Substring(); // invalid
|
||
|
|
const char *t = strstr(c_str(), str);
|
||
|
|
if(!t)
|
||
|
|
return ScriptVariable::Substring(); // invalid
|
||
|
|
const char *p;
|
||
|
|
while((p = strstr(t+1, str)))
|
||
|
|
t = p;
|
||
|
|
return ScriptVariable::Substring(*this, t-c_str(), strlen(str));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
bool ScriptVariable::Iterator::NextWord(const char *spaces)
|
||
|
|
{
|
||
|
|
if(!master->IsValid())
|
||
|
|
return false;
|
||
|
|
pos += len; // begin from the next position
|
||
|
|
char *p = master->p->buf + pos;
|
||
|
|
char *q = p;
|
||
|
|
while(*p && strchr(spaces, *p))
|
||
|
|
p++;
|
||
|
|
if(!*p)
|
||
|
|
return false;
|
||
|
|
pos += p - q;
|
||
|
|
q = p;
|
||
|
|
while(*p && !strchr(spaces, *p))
|
||
|
|
p++;
|
||
|
|
len = p - q;
|
||
|
|
just_started = false; // this is just to keep the things consistent
|
||
|
|
// NextWord doesn't really use the just_started variable
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::Iterator::NextToken(
|
||
|
|
const char *delimiters,
|
||
|
|
const char *trim_spaces)
|
||
|
|
{
|
||
|
|
if(!master->IsValid())
|
||
|
|
return false;
|
||
|
|
char *p, *q;
|
||
|
|
if(!just_started) { // not the starting situation
|
||
|
|
// we need first to find the delimiter that finishes the
|
||
|
|
// current token, and only in this case
|
||
|
|
pos += len; // begin from the next position
|
||
|
|
p = master->p->buf + pos;
|
||
|
|
q = p;
|
||
|
|
while(*p && !strchr(delimiters, *p))
|
||
|
|
p++;
|
||
|
|
if(!*p)
|
||
|
|
return false; // that was the last token
|
||
|
|
p++;
|
||
|
|
pos += p - q; // now pos is the next position after the delimiter
|
||
|
|
} else {
|
||
|
|
p = master->p->buf; // begin from the beginning
|
||
|
|
just_started = false;
|
||
|
|
}
|
||
|
|
q = p;
|
||
|
|
while(*p && !strchr(delimiters, *p))
|
||
|
|
p++;
|
||
|
|
p--;
|
||
|
|
// q points at the begin and p points at the end
|
||
|
|
if(trim_spaces && *trim_spaces) {
|
||
|
|
// need trimming
|
||
|
|
while(q<=p && strchr(trim_spaces, *q))
|
||
|
|
q++;
|
||
|
|
if(q<=p) while(strchr(trim_spaces, *p))
|
||
|
|
p--;
|
||
|
|
// if everything gets trim away, then p is actually less than q
|
||
|
|
}
|
||
|
|
pos = q - master->p->buf;
|
||
|
|
len = p - q + 1;
|
||
|
|
//return (len + pos > 0); // to prevent infinite loop on, say, empty string
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
bool ScriptVariable::Iterator::PrevWord(const char *spaces)
|
||
|
|
{
|
||
|
|
if(!master->IsValid())
|
||
|
|
return false;
|
||
|
|
if(just_started)
|
||
|
|
pos = master->Length();
|
||
|
|
pos -= 1; // begin from the previous position
|
||
|
|
char *limit = master->p->buf;
|
||
|
|
char *p = limit + pos;
|
||
|
|
while(p>=limit && strchr(spaces, *p))
|
||
|
|
p--;
|
||
|
|
if(p<limit)
|
||
|
|
return false;
|
||
|
|
char *q = p;
|
||
|
|
while(p>=limit && !strchr(spaces, *p))
|
||
|
|
p--;
|
||
|
|
pos = p - limit + 1;
|
||
|
|
len = q - p;
|
||
|
|
just_started = false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::Iterator::PrevToken(
|
||
|
|
const char *delimiters,
|
||
|
|
const char *trim_spaces)
|
||
|
|
{
|
||
|
|
if(!master->IsValid())
|
||
|
|
return false;
|
||
|
|
char *p, *q;
|
||
|
|
char *limit = master->p->buf;
|
||
|
|
if(!just_started) { // not the starting situation
|
||
|
|
// we need first to find the delimiter that starts the
|
||
|
|
// current token, and only in this case
|
||
|
|
pos -= 1; // begin from the previous position
|
||
|
|
p = limit + pos;
|
||
|
|
q = p;
|
||
|
|
while(p>=limit && !strchr(delimiters, *p))
|
||
|
|
p--;
|
||
|
|
if(p<limit)
|
||
|
|
return false; // that was the first token
|
||
|
|
p--; // jump over the delimiter
|
||
|
|
pos -= p - q; // now pos is the next position before the delimiter
|
||
|
|
} else {
|
||
|
|
p = limit + master->Length() - 1; // begin from the end
|
||
|
|
just_started = false;
|
||
|
|
}
|
||
|
|
q = p;
|
||
|
|
while(p>=limit && !strchr(delimiters, *p))
|
||
|
|
p--;
|
||
|
|
p++;
|
||
|
|
// q points at the end and p points at the begin
|
||
|
|
if(trim_spaces && *trim_spaces) {
|
||
|
|
// need trimming
|
||
|
|
while(q>=p && strchr(trim_spaces, *q))
|
||
|
|
q--;
|
||
|
|
while(q>p && strchr(trim_spaces, *p))
|
||
|
|
p++;
|
||
|
|
// if everything gets trim away, then q is actually less than p
|
||
|
|
}
|
||
|
|
pos = p - limit;
|
||
|
|
len = q - p + 1;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
bool ScriptVariable::GetLong(long &l, int radix) const
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return false;
|
||
|
|
long ret;
|
||
|
|
char *err;
|
||
|
|
char *q = p->buf;
|
||
|
|
ret = strtol(q, &err, radix);
|
||
|
|
if(*q != '\0' && *err == '\0') {
|
||
|
|
l = ret;
|
||
|
|
return true;
|
||
|
|
} else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::GetLongLong(long long &l, int radix) const
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return false;
|
||
|
|
long long ret;
|
||
|
|
char *err;
|
||
|
|
char *q = p->buf;
|
||
|
|
ret = strtoll(q, &err, radix);
|
||
|
|
if(*q != '\0' && *err == '\0') {
|
||
|
|
l = ret;
|
||
|
|
return true;
|
||
|
|
} else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::GetDouble(double &d) const
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return false;
|
||
|
|
double ret;
|
||
|
|
char *err;
|
||
|
|
char *q = p->buf;
|
||
|
|
ret = strtod(q, &err);
|
||
|
|
if(*q != '\0' && *err == '\0') {
|
||
|
|
d = ret;
|
||
|
|
return true;
|
||
|
|
} else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ScriptVariable::GetRational(long &n, long &m) const
|
||
|
|
{
|
||
|
|
if(!p)
|
||
|
|
return false;
|
||
|
|
long ret;
|
||
|
|
char *err;
|
||
|
|
char *q = p->buf;
|
||
|
|
ret = strtol(q, &err, 10);
|
||
|
|
if(*q == '\0' || (*err != '\0' && *err != '.'))
|
||
|
|
return false;
|
||
|
|
if(*err=='\0' || *(err+1)=='\0') {
|
||
|
|
// no decimal dot or nothing right after the decimal dot
|
||
|
|
n = ret;
|
||
|
|
m = 1;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
// decimal dot found
|
||
|
|
q = err+1;
|
||
|
|
long ret2 = strtol(q, &err, 10);
|
||
|
|
if(*err != '\0' || ret2<=0)
|
||
|
|
return false;
|
||
|
|
long x = 10;
|
||
|
|
while(x<=ret2)
|
||
|
|
x*=10;
|
||
|
|
n = ret * x + ret2;
|
||
|
|
m = x;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
template <class Int, int maxbuf>
|
||
|
|
const char *scriptpp_signed_to_str(Int i, const char *minstr)
|
||
|
|
{
|
||
|
|
if(i == 0)
|
||
|
|
return "0";
|
||
|
|
static char buf[maxbuf];
|
||
|
|
int idx = sizeof(buf)-1;
|
||
|
|
buf[idx] = 0;
|
||
|
|
bool negative = (i < 0);
|
||
|
|
if(negative)
|
||
|
|
i = -i;
|
||
|
|
if(i <= 0) // it's the minimum for the size
|
||
|
|
return minstr;
|
||
|
|
while(i) {
|
||
|
|
idx--;
|
||
|
|
buf[idx] = i % 10 + '0';
|
||
|
|
i /= 10;
|
||
|
|
}
|
||
|
|
if(negative) {
|
||
|
|
idx--;
|
||
|
|
buf[idx] = '-';
|
||
|
|
}
|
||
|
|
return buf + idx;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class Int, int maxbuf>
|
||
|
|
const char *scriptpp_unsigned_to_str(Int i)
|
||
|
|
{
|
||
|
|
if(i == 0)
|
||
|
|
return "0";
|
||
|
|
static char buf[maxbuf];
|
||
|
|
int idx = sizeof(buf)-1;
|
||
|
|
buf[idx] = 0;
|
||
|
|
while(i) {
|
||
|
|
idx--;
|
||
|
|
buf[idx] = i % 10 + '0';
|
||
|
|
i /= 10;
|
||
|
|
}
|
||
|
|
return buf + idx;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
The following code assumes there are only 32-bit and 64-bit
|
||
|
|
architectures, and that int is always 32-bit, long long is
|
||
|
|
always 64-bit and long may be either the same as int or the
|
||
|
|
same as long long.
|
||
|
|
|
||
|
|
On 16-bit platforms, this code will not work correctly for
|
||
|
|
minimal signed values of int and long.
|
||
|
|
|
||
|
|
Should the world change one day so long long becomes 128 bit,
|
||
|
|
this code will fail as well, but such a change will mean the
|
||
|
|
world failed as a whole.
|
||
|
|
*/
|
||
|
|
|
||
|
|
static const char min_i32_str[] = "-2147483648";
|
||
|
|
static const char min_i64_str[] = "-9223372036854775808";
|
||
|
|
static const char *min_long_str =
|
||
|
|
sizeof(int) == sizeof(long) ? min_i32_str : min_i64_str;
|
||
|
|
|
||
|
|
ScriptNumber::ScriptNumber(short int i)
|
||
|
|
: ScriptVariable(scriptpp_signed_to_str<int, 16>(i, min_i32_str)) {}
|
||
|
|
ScriptNumber::ScriptNumber(unsigned short int i)
|
||
|
|
: ScriptVariable(scriptpp_unsigned_to_str<unsigned int, 16>(i)) {}
|
||
|
|
|
||
|
|
ScriptNumber::ScriptNumber(int i)
|
||
|
|
: ScriptVariable(scriptpp_signed_to_str<int, 16>(i, min_i32_str)) {}
|
||
|
|
ScriptNumber::ScriptNumber(unsigned int i)
|
||
|
|
: ScriptVariable(scriptpp_unsigned_to_str<unsigned int, 16>(i)) {}
|
||
|
|
|
||
|
|
ScriptNumber::ScriptNumber(long i)
|
||
|
|
: ScriptVariable(scriptpp_signed_to_str<long, 32>(i, min_long_str)) {}
|
||
|
|
ScriptNumber::ScriptNumber(unsigned long int i)
|
||
|
|
: ScriptVariable(scriptpp_unsigned_to_str<unsigned long, 32>(i)) {}
|
||
|
|
|
||
|
|
ScriptNumber::ScriptNumber(long long int i)
|
||
|
|
: ScriptVariable(scriptpp_signed_to_str<long long, 32>(i, min_i64_str)) {}
|
||
|
|
ScriptNumber::ScriptNumber(unsigned long long int i)
|
||
|
|
: ScriptVariable(scriptpp_unsigned_to_str<unsigned long long, 32>(i)) {}
|