forked from mirror/openmw-tes3mp
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1013 lines
27 KiB
C
1013 lines
27 KiB
C
/* String functions for the Pawn Abstract Machine
|
|
*
|
|
* Copyright (c) ITB CompuPhase, 2005-2015
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
* use this file except in compliance with the License. You may obtain a copy
|
|
* of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
* Version: $Id: amxstring.c 5181 2015-01-21 09:44:28Z thiadmer $
|
|
*/
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
|
|
#include <malloc.h>
|
|
#endif
|
|
#include "osdefs.h"
|
|
#include "amx.h"
|
|
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#define MAX_FORMATSTR 256
|
|
|
|
#define CHARBITS (8*sizeof(char))
|
|
|
|
#if defined _UNICODE
|
|
# include <tchar.h>
|
|
#elif !defined __T
|
|
typedef char TCHAR;
|
|
# define __T(string) string
|
|
# define _tcscat strcat
|
|
# define _tcschr strchr
|
|
# define _tcscpy strcpy
|
|
# define _tcslen strlen
|
|
#endif
|
|
#include "amxcons.h"
|
|
|
|
#if !defined isdigit
|
|
# define isdigit(c) ((unsigned)((c)-'0')<10u)
|
|
#endif
|
|
#if !defined sizearray
|
|
# define sizearray(a) (sizeof(a) / sizeof((a)[0]))
|
|
#endif
|
|
|
|
|
|
/* dest the destination buffer; the buffer must point to the start of a cell
|
|
* source the source buffer, this must be aligned to a cell edge
|
|
* len the number of characters (bytes) to copy
|
|
* offs the offset in dest, in characters (bytes)
|
|
*/
|
|
static int amx_StrPack(cell *dest,cell *source,int len,int offs)
|
|
{
|
|
int i;
|
|
|
|
if ((ucell)*source>UNPACKEDMAX && offs%sizeof(cell)==0) {
|
|
/* source string is already packed and the destination is cell-aligned */
|
|
unsigned char* pdest=(unsigned char*)dest+offs;
|
|
i=(len+sizeof(cell)-1)/sizeof(cell);
|
|
memmove(pdest,source,i*sizeof(cell));
|
|
/* zero-terminate */
|
|
#if BYTE_ORDER==BIG_ENDIAN
|
|
pdest+=len;
|
|
for (i=len; i==len || i%sizeof(cell)!=0; i++)
|
|
*pdest++='\0';
|
|
#else
|
|
i=(len/sizeof(cell))*sizeof(cell);
|
|
pdest+=i;
|
|
len=(len==i) ? sizeof(cell) : sizeof(cell)-(len-i);
|
|
assert(len>0 && len<=sizeof(cell));
|
|
for (i=0; i<len; i++)
|
|
*pdest++='\0';
|
|
#endif
|
|
} else if ((ucell)*source>UNPACKEDMAX) {
|
|
/* source string is packed, destination is not aligned */
|
|
cell mask,c;
|
|
dest+=offs/sizeof(cell); /* increment whole number of cells */
|
|
offs%=sizeof(cell); /* get remainder */
|
|
mask=(~(ucell)0) >> (offs*CHARBITS);
|
|
c=*dest & ~mask;
|
|
for (i=0; i<len+offs+1; i+=sizeof(cell)) {
|
|
*dest=c | ((*source >> (offs*CHARBITS)) & mask);
|
|
c=(*source << ((sizeof(cell)-offs)*CHARBITS)) & ~mask;
|
|
dest++;
|
|
source++;
|
|
} /* for */
|
|
/* set the zero byte in the last cell */
|
|
mask=(~(ucell)0) >> (((offs+len)%sizeof(cell))*CHARBITS);
|
|
*(dest-1) &= ~mask;
|
|
} else {
|
|
/* source string is unpacked: pack string, from top-down */
|
|
cell c=0;
|
|
if (offs!=0) {
|
|
/* get the last cell in "dest" and mask of the characters that must be changed */
|
|
cell mask;
|
|
dest+=offs/sizeof(cell); /* increment whole number of cells */
|
|
offs%=sizeof(cell); /* get remainder */
|
|
mask=(~(ucell)0) >> (offs*CHARBITS);
|
|
c=(*dest & ~mask) >> ((sizeof(cell)-offs)*CHARBITS);
|
|
} /* if */
|
|
/* for proper alignement, add the offset to both the starting and the ending
|
|
* criterion (so that the number of iterations stays the same)
|
|
*/
|
|
assert(offs>=0 && offs<sizeof(cell));
|
|
for (i=offs; i<len+offs; i++) {
|
|
c=(c<<CHARBITS) | (*source++ & 0xff);
|
|
if (i%sizeof(cell)==sizeof(cell)-1) {
|
|
*dest++=c;
|
|
c=0;
|
|
} /* if */
|
|
} /* for */
|
|
if (i%sizeof(cell) != 0) /* store remaining packed characters */
|
|
*dest=c << (sizeof(cell)-i%sizeof(cell))*CHARBITS;
|
|
else
|
|
*dest=0; /* store full cell of zeros */
|
|
} /* if */
|
|
return AMX_ERR_NONE;
|
|
}
|
|
|
|
static int amx_StrUnpack(cell *dest,cell *source,int len)
|
|
{
|
|
/* len excludes the terminating '\0' byte */
|
|
if ((ucell)*source>UNPACKEDMAX) {
|
|
/* unpack string, from bottom up (so string can be unpacked in place) */
|
|
cell c;
|
|
int i;
|
|
for (i=len-1; i>=0; i--) {
|
|
c=source[i/sizeof(cell)] >> (sizeof(cell)-i%sizeof(cell)-1)*CHARBITS;
|
|
dest[i]=c & UCHAR_MAX;
|
|
} /* for */
|
|
dest[len]=0; /* zero-terminate */
|
|
} else {
|
|
/* source string is already unpacked */
|
|
while (len-->0)
|
|
*dest++=*source++;
|
|
*dest=0;
|
|
} /* if */
|
|
return AMX_ERR_NONE;
|
|
}
|
|
|
|
static unsigned char *packedptr(cell *string,int index)
|
|
{
|
|
unsigned char *ptr=(unsigned char *)(string+index/sizeof(cell));
|
|
#if BYTE_ORDER==BIG_ENDIAN
|
|
ptr+=index & (sizeof(cell)-1);
|
|
#else
|
|
ptr+=(sizeof(cell)-1) - (index & (sizeof(cell)-1));
|
|
#endif
|
|
return ptr;
|
|
}
|
|
|
|
static cell extractchar(cell *string,int index,int mklower)
|
|
{
|
|
cell c;
|
|
|
|
if ((ucell)*string>UNPACKEDMAX)
|
|
c=*packedptr(string,index);
|
|
else
|
|
c=string[index];
|
|
if (mklower) {
|
|
#if defined __WIN32__ || defined _WIN32 || defined WIN32
|
|
c=(cell)CharLower((LPTSTR)c);
|
|
#elif defined _Windows
|
|
c=(cell)AnsiLower((LPSTR)c);
|
|
#else
|
|
if ((unsigned int)(c-'A')<26u)
|
|
c+='a'-'A';
|
|
#endif
|
|
} /* if */
|
|
return c;
|
|
}
|
|
|
|
/* strlen(const string[])
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strlen(AMX *amx,const cell *params)
|
|
{
|
|
cell *cptr;
|
|
int len = 0;
|
|
|
|
(void)(amx);
|
|
cptr=amx_Address(amx,params[1]);
|
|
amx_StrLen(cptr,&len);
|
|
return len;
|
|
}
|
|
|
|
/* strpack(dest[], const source[], maxlength=sizeof dest)
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strpack(AMX *amx,const cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
int len,err;
|
|
|
|
csrc=amx_Address(amx,params[2]);
|
|
amx_StrLen(csrc,&len);
|
|
if ((unsigned)len>params[3]*sizeof(cell)-1)
|
|
len=params[3]*sizeof(cell)-1;
|
|
|
|
cdest=amx_Address(amx,params[1]);
|
|
err=amx_StrPack(cdest,csrc,len,0);
|
|
if (err!=AMX_ERR_NONE)
|
|
return amx_RaiseError(amx,err);
|
|
|
|
return len;
|
|
}
|
|
|
|
/* strunpack(dest[], const source[], maxlength=sizeof dest)
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strunpack(AMX *amx,const cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
int len,err;
|
|
|
|
csrc=amx_Address(amx,params[2]);
|
|
amx_StrLen(csrc,&len);
|
|
assert(len>=0);
|
|
if (len>=params[3])
|
|
len=params[3]-1;
|
|
|
|
cdest=amx_Address(amx,params[1]);
|
|
err=amx_StrUnpack(cdest,csrc,len);
|
|
if (err!=AMX_ERR_NONE)
|
|
return amx_RaiseError(amx,err);
|
|
|
|
return len;
|
|
}
|
|
|
|
/* strcat(dest[], const source[], maxlength=sizeof dest)
|
|
* packed/unpacked attribute is taken from dest[], or from source[] if dest[]
|
|
* is an empty string.
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strcat(AMX *amx,const cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
int len,len2;
|
|
int packed,err;
|
|
|
|
/* calculate number of cells needed for (packed) destination */
|
|
csrc=amx_Address(amx,params[2]);
|
|
cdest=amx_Address(amx,params[1]);
|
|
amx_StrLen(csrc,&len);
|
|
amx_StrLen(cdest,&len2);
|
|
packed=(*cdest==0) ? ((ucell)*csrc>UNPACKEDMAX) : ((ucell)*cdest>UNPACKEDMAX);
|
|
if (packed) {
|
|
if ((unsigned)(len+len2)>params[3]*sizeof(cell)-1)
|
|
len=params[3]*sizeof(cell)-len2-1;
|
|
} else {
|
|
if (len+len2>params[3]-1)
|
|
len=params[3]-len2-1;
|
|
} /* if */
|
|
|
|
if (packed) {
|
|
err=amx_StrPack(cdest,csrc,len,len2);
|
|
} else {
|
|
/* destination string must either be unpacked, or empty */
|
|
assert((ucell)*cdest<=UNPACKEDMAX || len2==0);
|
|
err=amx_StrUnpack(cdest+len2,csrc,len);
|
|
} /* if */
|
|
if (err!=AMX_ERR_NONE)
|
|
return amx_RaiseError(amx,err);
|
|
|
|
return len;
|
|
}
|
|
|
|
/* strcopy(dest[], const source[], maxlength=sizeof dest)
|
|
* packed/unpacked attribute from source[]
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strcopy(AMX *amx,const cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
int len,packed,err;
|
|
|
|
/* calculate number of cells needed for (packed) destination */
|
|
csrc=amx_Address(amx,params[2]);
|
|
cdest=amx_Address(amx,params[1]);
|
|
amx_StrLen(csrc,&len);
|
|
packed=(ucell)*csrc>UNPACKEDMAX;
|
|
if (packed) {
|
|
if ((unsigned)len>params[3]*sizeof(cell)-1)
|
|
len=params[3]*sizeof(cell)-1;
|
|
} else {
|
|
if (len>params[3]-1)
|
|
len=params[3]-1;
|
|
} /* if */
|
|
|
|
if (packed)
|
|
err=amx_StrPack(cdest,csrc,len,0);
|
|
else
|
|
err=amx_StrUnpack(cdest,csrc,len);
|
|
if (err!=AMX_ERR_NONE)
|
|
return amx_RaiseError(amx,err);
|
|
|
|
return len;
|
|
}
|
|
|
|
static int compare(cell *cstr1,cell *cstr2,int ignorecase,int length,int offs1)
|
|
{
|
|
int index;
|
|
cell c1=0,c2=0;
|
|
|
|
for (index=0; index<length; index++) {
|
|
c1=extractchar(cstr1,index+offs1,ignorecase);
|
|
c2=extractchar(cstr2,index,ignorecase);
|
|
assert(c1!=0 && c2!=0); /* string lengths are already checked, so zero-bytes should not occur */
|
|
if (c1!=c2)
|
|
break;
|
|
} /* for */
|
|
|
|
if (c1<c2)
|
|
return -1;
|
|
if (c1>c2)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* strcmp(const string1[], const string2[], bool:ignorecase=false, length=cellmax)
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strcmp(AMX *amx,const cell *params)
|
|
{
|
|
cell *cstr1,*cstr2;
|
|
int len1,len2,len;
|
|
cell result;
|
|
|
|
(void)(amx);
|
|
cstr1=amx_Address(amx,params[1]);
|
|
cstr2=amx_Address(amx,params[2]);
|
|
|
|
/* get the maximum length to compare */
|
|
amx_StrLen(cstr1,&len1);
|
|
amx_StrLen(cstr2,&len2);
|
|
len=len1;
|
|
if (len>len2)
|
|
len=len2;
|
|
if (len>params[4])
|
|
len=params[4];
|
|
if (len==0)
|
|
return (params[4]==0) ? 0 : len1-len2;
|
|
|
|
result=compare(cstr1,cstr2,params[3],len,0);
|
|
if (result==0 && len!=params[4])
|
|
result=len1-len2;
|
|
return result;
|
|
}
|
|
|
|
/* strfind(const string[], const sub[], bool:ignorecase=false, offset=0)
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strfind(AMX *amx,const cell *params)
|
|
{
|
|
cell *cstr,*csub;
|
|
int lenstr,lensub,offs;
|
|
cell c,f;
|
|
|
|
(void)(amx);
|
|
cstr=amx_Address(amx,params[1]);
|
|
csub=amx_Address(amx,params[2]);
|
|
|
|
/* get the maximum length to compare */
|
|
amx_StrLen(cstr,&lenstr);
|
|
amx_StrLen(csub,&lensub);
|
|
if (lensub==0)
|
|
return -1;
|
|
|
|
/* get the start character of the substring, for quicker searching */
|
|
f=extractchar(csub,0,params[3]);
|
|
assert(f!=0); /* string length is already checked */
|
|
|
|
for (offs=(int)params[4]; offs+lensub<=lenstr; offs++) {
|
|
/* find the initial character */
|
|
c=extractchar(csub,0,params[3]);
|
|
assert(c!=0); /* string length is already checked */
|
|
if (c!=f)
|
|
continue;
|
|
if (compare(cstr,csub,params[3],lensub,offs)==0)
|
|
return offs;
|
|
} /* for */
|
|
return -1;
|
|
}
|
|
|
|
/* strmid(dest[], const source[], start, end, maxlength=sizeof dest)
|
|
* packed/unpacked attribute is taken from source[]
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strmid(AMX *amx,const cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
int len,err;
|
|
int soffs,doffs;
|
|
unsigned char *ptr;
|
|
unsigned char c;
|
|
int start=params[3];
|
|
int end=params[4];
|
|
|
|
/* calculate number of cells needed for (packed) destination */
|
|
csrc=amx_Address(amx,params[2]);
|
|
cdest=amx_Address(amx,params[1]);
|
|
amx_StrLen(csrc,&len);
|
|
|
|
/* clamp the start/end parameters */
|
|
if (start<0)
|
|
start=0;
|
|
else if (start>len)
|
|
start=len;
|
|
if (end<start)
|
|
end=start;
|
|
else if (end>len)
|
|
end=len;
|
|
|
|
len=end-start;
|
|
if ((ucell)*csrc>UNPACKEDMAX) {
|
|
if ((unsigned)len>params[5]*sizeof(cell)-1)
|
|
len=params[5]*sizeof(cell)-1;
|
|
} else {
|
|
if (len>params[5]-1)
|
|
len=params[5]-1;
|
|
} /* if */
|
|
|
|
if ((ucell)*csrc>UNPACKEDMAX) {
|
|
/* first align the source to a cell boundary */
|
|
for (doffs=0,soffs=start; (soffs & (sizeof(cell)-1))!=0 && len>0; soffs++,doffs++,len--) {
|
|
ptr=packedptr(csrc,soffs);
|
|
c=*ptr;
|
|
ptr=packedptr(cdest,doffs);
|
|
*ptr=c;
|
|
} /* for */
|
|
if (len==0) {
|
|
/* nothing left to do, zero-terminate */
|
|
ptr=packedptr(cdest,doffs);
|
|
*ptr='\0';
|
|
err=AMX_ERR_NONE;
|
|
} else {
|
|
err=amx_StrPack(cdest,csrc+soffs/sizeof(cell),len,doffs);
|
|
} /* if */
|
|
} else {
|
|
err=amx_StrUnpack(cdest,csrc+start,len);
|
|
} /* if */
|
|
if (err!=AMX_ERR_NONE)
|
|
return amx_RaiseError(amx,err);
|
|
|
|
return len;
|
|
}
|
|
|
|
/* strdel(string[], start, end)
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strdel(AMX *amx,const cell *params)
|
|
{
|
|
cell *cstr;
|
|
int index,offs,length;
|
|
unsigned char *ptr;
|
|
unsigned char c;
|
|
|
|
(void)(amx);
|
|
/* calculate number of cells needed for (packed) destination */
|
|
cstr=amx_Address(amx,params[1]);
|
|
amx_StrLen(cstr,&length);
|
|
index=(int)params[2];
|
|
offs=(int)params[3]-index;
|
|
if (index>=length || offs<=0)
|
|
return 0;
|
|
if (index+offs>length)
|
|
offs=length-index;
|
|
|
|
index--; /* prepare for increment in the top of the loop */
|
|
if (((ucell)*cstr>UNPACKEDMAX)) {
|
|
do {
|
|
index++;
|
|
ptr=packedptr(cstr,index+offs);
|
|
c=*ptr;
|
|
ptr=packedptr(cstr,index);
|
|
*ptr=c;
|
|
} while (c!='\0');
|
|
if (index==0)
|
|
*cstr=0;
|
|
} else {
|
|
do {
|
|
index++;
|
|
cstr[index]=cstr[index+offs];
|
|
} while (cstr[index]!=0);
|
|
} /* if */
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* strins(string[], const substr[], offset, maxlength=sizeof string)
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strins(AMX *amx,const cell *params)
|
|
{
|
|
cell *cstr,*csub;
|
|
int index,lenstr,lensub,count;
|
|
unsigned char *ptr;
|
|
cell c;
|
|
|
|
/* calculate number of cells needed for (packed) destination */
|
|
cstr=amx_Address(amx,params[1]);
|
|
csub=amx_Address(amx,params[2]);
|
|
amx_StrLen(cstr,&lenstr);
|
|
amx_StrLen(csub,&lensub);
|
|
index=(int)params[3];
|
|
if (index>lenstr)
|
|
return amx_RaiseError(amx,AMX_ERR_NATIVE);
|
|
|
|
if (*cstr==0) {
|
|
/* current string is empty (and the insertion point is zero), just make a copy */
|
|
assert(index==0);
|
|
if ((ucell)*csub>UNPACKEDMAX)
|
|
amx_StrPack(cstr,csub,lensub,0);
|
|
else
|
|
amx_StrUnpack(cstr,csub,lensub);
|
|
return 1;
|
|
} /* if */
|
|
|
|
if (((ucell)*cstr>UNPACKEDMAX)) {
|
|
/* make room for the new characters */
|
|
for (count=lenstr+lensub; count>index; count--) {
|
|
ptr=packedptr(cstr,count-lensub);
|
|
c=*ptr;
|
|
ptr=packedptr(cstr,count);
|
|
*ptr=(unsigned char)c;
|
|
} /* for */
|
|
/* copy in the new characters */
|
|
for (count=0; count<lensub; count++) {
|
|
c=extractchar(csub,count,0);
|
|
ptr=packedptr(cstr,index+count);
|
|
*ptr=(unsigned char)c;
|
|
} /* for */
|
|
} else {
|
|
/* make room for the new characters */
|
|
for (count=lenstr+lensub; count>index; count--)
|
|
cstr[count]=cstr[count-lensub];
|
|
/* copy in the new characters */
|
|
for (count=0; count<lensub; count++) {
|
|
c=extractchar(csub,count,0);
|
|
cstr[index+count]=c;
|
|
} /* for */
|
|
} /* if */
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* strval(const string[], index=0)
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strval(AMX *amx,const cell *params)
|
|
{
|
|
TCHAR str[50],*ptr;
|
|
cell *cstr,result;
|
|
int len,negate=0;
|
|
int offset=0;
|
|
|
|
(void)(amx);
|
|
/* get parameters */
|
|
cstr=amx_Address(amx,params[1]);
|
|
amx_StrLen(cstr,&len);
|
|
if ((unsigned)params[0]>=2*sizeof(cell))
|
|
offset=params[2];
|
|
if (offset<0)
|
|
offset=0;
|
|
else if (offset>=len)
|
|
offset=len-1;
|
|
|
|
/* skip a number of cells */
|
|
if ((ucell)*cstr>UNPACKEDMAX) {
|
|
/* packed string */
|
|
while (offset>=(int)sizeof(cell)) {
|
|
cstr++;
|
|
offset-=sizeof(cell);
|
|
len-=sizeof(cell);
|
|
} /* while */
|
|
} else {
|
|
/* unpacked string, one character per cell */
|
|
while (offset>0) {
|
|
cstr++;
|
|
offset--;
|
|
len--;
|
|
} /* while */
|
|
} /* if */
|
|
|
|
amx_GetString(str,cstr,sizeof(TCHAR)>1,sizeof str);
|
|
assert(offset<(int)sizeof(cell) && offset>=0);
|
|
ptr=str+offset;
|
|
result=0;
|
|
while (*ptr!='\0' && *ptr<=' ')
|
|
ptr++; /* skip whitespace */
|
|
if (*ptr=='-') { /* handle sign */
|
|
negate=1;
|
|
ptr++;
|
|
} else if (*ptr=='+') {
|
|
ptr++;
|
|
} /* if */
|
|
while (isdigit(*ptr)) {
|
|
result=result*10 + (*ptr-'0');
|
|
ptr++;
|
|
} /* while */
|
|
if (negate)
|
|
result=-result;
|
|
return result;
|
|
}
|
|
|
|
/* valstr(dest[], value, bool:pack=false) */
|
|
static cell AMX_NATIVE_CALL n_valstr(AMX *amx,const cell *params)
|
|
{
|
|
TCHAR str[50];
|
|
cell value,temp;
|
|
cell *cstr;
|
|
int len,result,negate=0;
|
|
|
|
(void)(amx);
|
|
/* find out how many digits are needed */
|
|
len=1;
|
|
value=params[2];
|
|
if (value<0) {
|
|
negate=1;
|
|
len++;
|
|
value=-value;
|
|
} /* if */
|
|
for (temp=value; temp>=10; temp/=10)
|
|
len++;
|
|
assert(len<=sizearray(str));
|
|
|
|
/* put in the string */
|
|
result=len;
|
|
str[len--]='\0';
|
|
while (len>=negate) {
|
|
str[len--]=(char)((value % 10)+'0');
|
|
value/=10;
|
|
} /* while */
|
|
if (negate)
|
|
str[0]='-';
|
|
cstr=amx_Address(amx,params[1]);
|
|
amx_SetString(cstr,str,params[3],sizeof(TCHAR)>1,sizearray(str));
|
|
return result;
|
|
}
|
|
|
|
/* ispacked(const string[]) */
|
|
static cell AMX_NATIVE_CALL n_ispacked(AMX *amx,const cell *params)
|
|
{
|
|
cell *cstr=amx_Address(amx,params[1]);
|
|
(void)(amx);
|
|
return *cstr>=UNPACKEDMAX;
|
|
}
|
|
|
|
|
|
/* single character decode and encode */
|
|
#define BITMASK 0x3f
|
|
#define DEC(c) (((c) - ' ') & BITMASK)
|
|
#define ENC(c) (char)(((c) & BITMASK) == 0 ? 0x60 : ((c) & BITMASK) + ' ')
|
|
|
|
static int uudecode(unsigned char *target, char *source)
|
|
{
|
|
int len, retval;
|
|
|
|
len = DEC(*source++);
|
|
retval = len;
|
|
while (len > 0) {
|
|
if (len-- > 0)
|
|
*target++ = (unsigned char)(( DEC(source[0]) << 2 ) | ( DEC(source[1]) >> 4 ));
|
|
if (len-- > 0)
|
|
*target++ = (unsigned char)(( DEC(source[1]) << 4 ) | ( DEC(source[2]) >> 2 ));
|
|
if (len-- > 0)
|
|
*target++ = (unsigned char)(( DEC(source[2]) << 6 ) | DEC(source[3]) );
|
|
source += 4;
|
|
} /* while */
|
|
return retval;
|
|
}
|
|
|
|
static int uuencode(char *target, unsigned char *source, int length)
|
|
{
|
|
int split[4]={0,0,0,0};
|
|
|
|
if (length > BITMASK)
|
|
return 0; /* can encode up to 64 bytes */
|
|
|
|
*target++ = ENC(length);
|
|
while (length > 0) {
|
|
split[0] = source[0] >> 2; /* split first byte to char. 0 & 1 */
|
|
split[1] = source[0] << 4;
|
|
if (length > 1) {
|
|
split[1] |= source[1] >> 4; /* split 2nd byte to char. 1 & 2 */
|
|
split[2] = source[1] << 2;
|
|
if (length > 2) {
|
|
split[2] |= source[2] >> 6; /* split 3th byte to char. 2 & 3 */
|
|
split[3] = source[2];
|
|
} /* if */
|
|
} /* if */
|
|
|
|
*target++ = ENC(split[0]);
|
|
*target++ = ENC(split[1]);
|
|
if (length > 1)
|
|
*target++ = ENC(split[2]);
|
|
if (length > 2)
|
|
*target++ = ENC(split[3]);
|
|
source += 3;
|
|
length -= 3;
|
|
} /* while */
|
|
|
|
*target = '\0'; /* end string */
|
|
return 1;
|
|
}
|
|
|
|
/* uudecode(dest[], const source[], maxlength=sizeof dest)
|
|
* Returns the number of bytes (not cells) decoded; if the dest buffer is
|
|
* too small, not all bytes are stored.
|
|
* Always creates a (packed) array (not a string; the array is not
|
|
* zero-terminated).
|
|
* A buffer may be decoded "in-place"; the destination size is always smaller
|
|
* than the source size.
|
|
* Endian issues (for multi-byte values in the data stream) are not handled.
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_uudecode(AMX *amx,const cell *params)
|
|
{
|
|
cell *cstr;
|
|
unsigned char dst[BITMASK+2];
|
|
char src[BITMASK+BITMASK/3+2];
|
|
int len;
|
|
size_t size;
|
|
|
|
(void)(amx);
|
|
/* get the source */
|
|
cstr=amx_Address(amx,params[2]);
|
|
amx_GetString(src,cstr,0,sizeof src);
|
|
/* decode */
|
|
len=uudecode(dst,src);
|
|
/* store */
|
|
cstr=amx_Address(amx,params[1]);
|
|
size=len;
|
|
if (size>params[3]*sizeof(cell))
|
|
size=params[3]*sizeof(cell);
|
|
memcpy(cstr,dst,size);
|
|
return len;
|
|
}
|
|
|
|
/* uuencode(dest[], const source[], numbytes, maxlength=sizeof dest)
|
|
* Returns the number of characters encoded, excluding the zero string
|
|
* terminator; if the dest buffer is too small, not all bytes are stored.
|
|
* Always creates a packed string. This string has a newline character at the
|
|
* end. A buffer may be encoded "in-place" if the destination is large enough.
|
|
* Endian issues (for multi-byte values in the data stream) are not handled.
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_uuencode(AMX *amx,const cell *params)
|
|
{
|
|
cell *cstr;
|
|
unsigned char src[BITMASK+2];
|
|
char dst[BITMASK+BITMASK/3+2];
|
|
|
|
(void)(amx);
|
|
/* get the source */
|
|
cstr=amx_Address(amx,params[2]);
|
|
amx_GetString((char *)src,cstr,0,sizeof src);
|
|
/* encode (and check for errors) */
|
|
if (uuencode(dst,src,params[3])) {
|
|
if (params[4]>0) {
|
|
cstr=amx_Address(amx,params[1]);
|
|
*cstr=0;
|
|
} /* if */
|
|
return 0;
|
|
} /* if */
|
|
/* always add a \n */
|
|
assert(strlen(dst)+1<sizeof dst);
|
|
strcat(dst,"\n");
|
|
/* store */
|
|
cstr=amx_Address(amx,params[1]);
|
|
amx_SetString(cstr,dst,1,0,params[4]);
|
|
return (((params[3]+2)/3) << 2)+2;
|
|
}
|
|
|
|
/* urldecode(dest[], const source[], maxlength=sizeof dest, bool:pack=false)
|
|
* Returns the number of characters decoded; if the dest buffer is
|
|
* too small, not all bytes are stored.
|
|
* A buffer may be decoded "in-place"; the destination size is always
|
|
* smaller than the source size.
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_urldecode(AMX *amx,const cell *params)
|
|
{
|
|
cell *cstr;
|
|
TCHAR *str;
|
|
int idx_src=0,idx_dst=0;
|
|
|
|
/* get the source */
|
|
(void)(amx);
|
|
amx_StrParam(amx,params[2],str);
|
|
|
|
/* decode */
|
|
while (idx_src!='\0') {
|
|
assert(idx_dst<=idx_src);
|
|
if (str[idx_src]=='%') {
|
|
int p,q;
|
|
if (str[idx_src+1]>='0' && str[idx_src+1]<='9')
|
|
p=str[idx_src+1]-'0';
|
|
else if (str[idx_src+1]>='A' && str[idx_src+1]<='F')
|
|
p=str[idx_src+1]-'A'+10;
|
|
else if (str[idx_src+1]>='a' && str[idx_src+1]<='f')
|
|
p=str[idx_src+1]-'a'+10;
|
|
else
|
|
p=-1;
|
|
if (p>=0) {
|
|
if (str[idx_src+2]>='0' && str[idx_src+2]<='9')
|
|
q=str[idx_src+2]-'0';
|
|
else if (str[idx_src+2]>='A' && str[idx_src+2]<='F')
|
|
q=str[idx_src+2]-'A'+10;
|
|
else if (str[idx_src+2]>='a' && str[idx_src+2]<='f')
|
|
q=str[idx_src+2]-'a'+10;
|
|
else
|
|
q=-1;
|
|
} /* if */
|
|
if (p>=0 && q >=0) {
|
|
assert(p<=15 && q<=15);
|
|
str[idx_dst]=(TCHAR)((p<<4) | q);
|
|
idx_src+=3;
|
|
} else {
|
|
/* invalid '%xx' syntax, copy literal '%' */
|
|
str[idx_dst]=str[idx_src++];
|
|
} /* if */
|
|
} else {
|
|
str[idx_dst]=str[idx_src++];
|
|
} /* if */
|
|
idx_dst++;
|
|
} /* while */
|
|
str[idx_dst]='\0';
|
|
|
|
/* store */
|
|
cstr=amx_Address(amx,params[1]);
|
|
amx_SetString(cstr,str,1,0,params[4]); /* store as packed ot unpacked */
|
|
|
|
return idx_dst;
|
|
}
|
|
|
|
#define INVALIDURI(c) ((c)<',' \
|
|
|| (c)>'9' && (c)<'A' \
|
|
|| (c)>'Z' && (c)<'_' \
|
|
|| (c)>'_' && (c)<'a' \
|
|
|| (c)>'z' && (unsigned)(c)<0xa1)
|
|
|
|
#define TOHEX(c) (TCHAR)((c)<10 ? '0'+(c) : 'A'-10+(c))
|
|
|
|
/* urlencode(dest[], const source[], maxlength=sizeof dest, bool:pack=false)
|
|
* Returns the number of characters encoded, excluding the zero string
|
|
* terminator; if the dest buffer is too small, not all bytes are stored.
|
|
* Always creates a packed string. This string has a newline character at the
|
|
* end. A buffer may be encoded "in-place" if the destination is large enough.
|
|
* Endian issues (for multi-byte values in the data stream) are not handled.
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_urlencode(AMX *amx,const cell *params)
|
|
{
|
|
cell *cstr;
|
|
int length,destlen,count,lastwidth;
|
|
TCHAR *str;
|
|
|
|
/* allocate memory and get the source */
|
|
(void)(amx);
|
|
if ((length=(int)params[3])==0)
|
|
return 0;
|
|
if ((str = (TCHAR*)alloca(length * sizeof(TCHAR)))==NULL)
|
|
return 0;
|
|
cstr=amx_Address(amx,params[2]);
|
|
amx_GetString((char*)str, cstr, sizeof(TCHAR)>1, length);
|
|
|
|
/* run through the string and determine the new length */
|
|
destlen=1; /* space for the '\0' terminator */
|
|
lastwidth=0;
|
|
for (count=0; str[count]!='\0' && destlen<length; count++) {
|
|
if (INVALIDURI(str[count]))
|
|
lastwidth=3;
|
|
else
|
|
lastwidth=1;
|
|
destlen+=lastwidth;
|
|
} /* for */
|
|
if (destlen>length) { /* correct for overrun */
|
|
destlen-=lastwidth;
|
|
count--;
|
|
} /* if */
|
|
assert(destlen<=length);
|
|
assert(count>=0);
|
|
|
|
/* store string terminator */
|
|
assert(destlen>0);
|
|
str[--destlen]='\0';
|
|
/* convert string from end to start */
|
|
while (--count>=0) {
|
|
assert(destlen>count);
|
|
if (INVALIDURI(str[count])) {
|
|
str[--destlen]=TOHEX(str[count] & 0x0f);
|
|
str[--destlen]=TOHEX((str[count] >> 4) & 0x0f);
|
|
str[--destlen]='%';
|
|
} else {
|
|
str[--destlen]=str[count];
|
|
} /* if */
|
|
} /* while */
|
|
assert(destlen==0);
|
|
|
|
/* store the result */
|
|
cstr=amx_Address(amx,params[1]);
|
|
amx_SetString(cstr,str,1,0,params[4]); /* store as packed ot unpacked */
|
|
|
|
return (cell)strlen(str);
|
|
}
|
|
|
|
/* memcpy(dest[], const source[], index=0, numbytes, maxlength=sizeof dest)
|
|
* This function can align byte strings in cell arrays, or concatenate two
|
|
* byte strings in two arrays. The parameter "index" is a byte offset; "numbytes"
|
|
* is the number of bytes to copy. Parameter "maxlength", however, is in cells.
|
|
* This function allows copying in-place, for aligning memory buffers.
|
|
* Endian issues (for multi-byte values in the data stream) are not handled.
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_memcpy(AMX *amx,const cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
unsigned char *pdest,*psrc;
|
|
|
|
(void)(amx);
|
|
if (params[3]<0 || params[4]<0 || (params[3]+params[4])>params[5]*(int)sizeof(cell))
|
|
return 0;
|
|
cdest=amx_Address(amx,params[1]);
|
|
csrc=amx_Address(amx,params[2]);
|
|
pdest=(unsigned char*)cdest+params[3];
|
|
psrc=(unsigned char*)csrc;
|
|
memmove(pdest,psrc,params[4]);
|
|
return 1;
|
|
}
|
|
|
|
#if !defined AMX_NOSTRFMT
|
|
static int str_putstr(void *dest,const TCHAR *str)
|
|
{
|
|
if (_tcslen((TCHAR*)dest)+_tcslen(str)<MAX_FORMATSTR)
|
|
_tcscat((TCHAR*)dest,str);
|
|
return 0;
|
|
}
|
|
|
|
static int str_putchar(void *dest,TCHAR ch)
|
|
{
|
|
int len=(int)_tcslen((TCHAR*)dest);
|
|
if (len<MAX_FORMATSTR-1) {
|
|
((TCHAR*)dest)[len]=ch;
|
|
((TCHAR*)dest)[len+1]='\0';
|
|
} /* if */
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* strformat(dest[], size=sizeof dest, bool:pack=false, const format[], {Fixed,_}:...)
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strformat(AMX *amx,const cell *params)
|
|
{
|
|
#if defined AMX_NOSTRFMT
|
|
(void)amx;
|
|
(void)params;
|
|
return 0;
|
|
#else
|
|
cell *cstr;
|
|
AMX_FMTINFO info;
|
|
TCHAR output[MAX_FORMATSTR];
|
|
|
|
memset(&info,0,sizeof info);
|
|
info.params=params+5;
|
|
info.numparams=(int)(params[0]/sizeof(cell))-4;
|
|
info.skip=0;
|
|
info.length=MAX_FORMATSTR; /* max. length of the string */
|
|
info.f_putstr=str_putstr;
|
|
info.f_putchar=str_putchar;
|
|
info.user=output;
|
|
output[0] = __T('\0');
|
|
|
|
cstr=amx_Address(amx,params[4]);
|
|
amx_printstring(amx,cstr,&info);
|
|
|
|
/* store the output string */
|
|
cstr=amx_Address(amx,params[1]);
|
|
amx_SetString(cstr,(char*)output,(int)params[3],sizeof(TCHAR)>1,(int)params[2]);
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
#if defined __cplusplus
|
|
extern "C"
|
|
#endif
|
|
const AMX_NATIVE_INFO string_Natives[] = {
|
|
{ "ispacked", n_ispacked },
|
|
{ "memcpy", n_memcpy },
|
|
{ "strcat", n_strcat },
|
|
{ "strcmp", n_strcmp },
|
|
{ "strcopy", n_strcopy },
|
|
{ "strdel", n_strdel },
|
|
{ "strfind", n_strfind },
|
|
{ "strformat", n_strformat },
|
|
{ "strins", n_strins },
|
|
{ "strlen", n_strlen },
|
|
{ "strmid", n_strmid },
|
|
{ "strpack", n_strpack },
|
|
{ "strunpack", n_strunpack },
|
|
{ "strval", n_strval },
|
|
{ "uudecode", n_uudecode },
|
|
{ "uuencode", n_uuencode },
|
|
{ "urldecode", n_urldecode },
|
|
{ "urlencode", n_urlencode },
|
|
{ "valstr", n_valstr },
|
|
{ NULL, NULL } /* terminator */
|
|
};
|
|
|
|
int AMXEXPORT AMXAPI amx_StringInit(AMX *amx)
|
|
{
|
|
return amx_Register(amx, string_Natives, -1);
|
|
}
|
|
|
|
int AMXEXPORT AMXAPI amx_StringCleanup(AMX *amx)
|
|
{
|
|
(void)amx;
|
|
return AMX_ERR_NONE;
|
|
}
|