mirror of
https://github.com/dashr9230/SA-MP.git
synced 2024-12-22 14:37:29 +08:00
792 lines
22 KiB
C
792 lines
22 KiB
C
/* String functions for the Pawn Abstract Machine
|
|
*
|
|
* Copyright (c) ITB CompuPhase, 2005
|
|
*
|
|
* This software is provided "as-is", without any express or implied warranty.
|
|
* In no event will the authors be held liable for any damages arising from
|
|
* the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software in
|
|
* a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*
|
|
* Version: $Id: amxstring.c 3363 2005-07-23 09:03:29Z thiadmer $
|
|
*/
|
|
#include <ctype.h>
|
|
#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 CHARBITS (8*sizeof(char))
|
|
|
|
/* 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; i+=sizeof(cell)) {
|
|
*dest=c | ((*source >> (offs*CHARBITS)) & mask);
|
|
c=(*source << ((sizeof(cell)-offs)*CHARBITS)) & ~mask;
|
|
dest++;
|
|
source++;
|
|
} /* for */
|
|
/* add the final cell, but only if there is not already a terminating zero
|
|
* in dest
|
|
*/
|
|
if ((*(dest-1) & 0xff)!=0) {
|
|
c &= ~0xff; /* force a terminating zero, for security */
|
|
*dest=c;
|
|
} /* if */
|
|
} 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
|
|
c=tolower((int)c);
|
|
#endif
|
|
} /* if */
|
|
return c;
|
|
}
|
|
|
|
static int verify_addr(AMX *amx,cell addr)
|
|
{
|
|
int err;
|
|
cell *cdest;
|
|
|
|
err=amx_GetAddr(amx,addr,&cdest);
|
|
if (err!=AMX_ERR_NONE)
|
|
amx_RaiseError(amx,err);
|
|
return err;
|
|
}
|
|
|
|
/* strlen(const string[])
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strlen(AMX *amx,cell *params)
|
|
{
|
|
cell *cptr;
|
|
int len = 0;
|
|
|
|
if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE)
|
|
amx_StrLen(cptr,&len);
|
|
return len;
|
|
}
|
|
|
|
/* strpack(dest[], const source[], maxlength=sizeof dest)
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strpack(AMX *amx,cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
int len,needed,err;
|
|
size_t lastaddr;
|
|
|
|
/* calculate number of cells needed for (packed) destination */
|
|
amx_GetAddr(amx,params[2],&csrc);
|
|
amx_StrLen(csrc,&len);
|
|
if ((unsigned)len>params[3]*sizeof(cell)-1)
|
|
len=params[3]*sizeof(cell)-1;
|
|
needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */
|
|
assert(needed>0);
|
|
lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1);
|
|
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
|
|
return amx_RaiseError(amx,AMX_ERR_NATIVE);
|
|
|
|
amx_GetAddr(amx,params[1],&cdest);
|
|
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,cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
int len,err;
|
|
size_t lastaddr;
|
|
|
|
/* calculate number of cells needed for (unpacked) destination */
|
|
amx_GetAddr(amx,params[2],&csrc);
|
|
amx_StrLen(csrc,&len);
|
|
assert(len>=0);
|
|
if (len>=params[3])
|
|
len=params[3]-1;
|
|
lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1);
|
|
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
|
|
return amx_RaiseError(amx,AMX_ERR_NATIVE);
|
|
|
|
amx_GetAddr(amx,params[1],&cdest);
|
|
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,cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
int len,len2,needed;
|
|
int packed,err;
|
|
size_t lastaddr;
|
|
|
|
/* calculate number of cells needed for (packed) destination */
|
|
amx_GetAddr(amx,params[2],&csrc);
|
|
amx_GetAddr(amx,params[1],&cdest);
|
|
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;
|
|
needed=(len+len2+sizeof(cell))/sizeof(cell); /* # of cells needed */
|
|
assert(needed>0);
|
|
lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1);
|
|
} else {
|
|
if (len+len2>params[3]-1)
|
|
len=params[3]-len2-1;
|
|
lastaddr=(size_t)(params[1]+sizeof(cell)*(len+len2+1)-1);
|
|
} /* if */
|
|
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
|
|
return amx_RaiseError(amx,AMX_ERR_NATIVE);
|
|
|
|
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;
|
|
}
|
|
|
|
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,cell *params)
|
|
{
|
|
cell *cstr1,*cstr2;
|
|
int len1,len2,len;
|
|
cell result;
|
|
|
|
amx_GetAddr(amx,params[1],&cstr1);
|
|
amx_GetAddr(amx,params[2],&cstr2);
|
|
|
|
/* 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 0;
|
|
|
|
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,cell *params)
|
|
{
|
|
cell *cstr,*csub;
|
|
int lenstr,lensub,offs;
|
|
cell c,f;
|
|
|
|
amx_GetAddr(amx,params[1],&cstr);
|
|
amx_GetAddr(amx,params[2],&csub);
|
|
|
|
/* 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,cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
int len,needed,err;
|
|
int soffs,doffs;
|
|
size_t lastaddr;
|
|
unsigned char *ptr;
|
|
unsigned char c;
|
|
|
|
/* calculate number of cells needed for (packed) destination */
|
|
amx_GetAddr(amx,params[2],&csrc);
|
|
amx_GetAddr(amx,params[1],&cdest);
|
|
amx_StrLen(csrc,&len);
|
|
|
|
/* clamp the start/end parameters */
|
|
if (params[3]<0)
|
|
params[3]=0;
|
|
else if (params[3]>len)
|
|
params[3]=len;
|
|
if (params[4]<params[3])
|
|
params[4]=params[3];
|
|
else if (params[4]>len)
|
|
params[4]=len;
|
|
else if (params[3]>params[4])
|
|
params[3]=params[4];
|
|
|
|
len=params[4]-params[3];
|
|
if ((ucell)*csrc>UNPACKEDMAX) {
|
|
if ((unsigned)len>params[5]*sizeof(cell)-1)
|
|
len=params[5]*sizeof(cell)-1;
|
|
needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */
|
|
assert(needed>0);
|
|
lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1);
|
|
} else {
|
|
if (len>params[5]-1)
|
|
len=params[5]-1;
|
|
lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1);
|
|
} /* if */
|
|
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
|
|
return amx_RaiseError(amx,AMX_ERR_NATIVE);
|
|
|
|
if ((ucell)*csrc>UNPACKEDMAX) {
|
|
/* first align the source to a cell boundary */
|
|
for (doffs=0,soffs=(int)params[3]; (soffs & (sizeof(cell)-1))!=0 && len>1; soffs++,doffs++,len--) {
|
|
ptr=packedptr(csrc,soffs);
|
|
c=*ptr;
|
|
ptr=packedptr(cdest,doffs);
|
|
*ptr=c;
|
|
} /* for */
|
|
err=amx_StrPack(cdest,csrc+soffs/sizeof(cell),len,doffs);
|
|
} else {
|
|
err=amx_StrUnpack(cdest,csrc+params[3],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,cell *params)
|
|
{
|
|
cell *cstr;
|
|
int index,offs,length;
|
|
unsigned char *ptr;
|
|
unsigned char c;
|
|
|
|
/* calculate number of cells needed for (packed) destination */
|
|
amx_GetAddr(amx,params[1],&cstr);
|
|
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,cell *params)
|
|
{
|
|
cell *cstr,*csub;
|
|
int index,lenstr,lensub,count;
|
|
int needed;
|
|
size_t lastaddr;
|
|
unsigned char *ptr;
|
|
cell c;
|
|
|
|
/* calculate number of cells needed for (packed) destination */
|
|
amx_GetAddr(amx,params[1],&cstr);
|
|
amx_GetAddr(amx,params[2],&csub);
|
|
amx_StrLen(cstr,&lenstr);
|
|
amx_StrLen(csub,&lensub);
|
|
index=(int)params[3];
|
|
if (index>lenstr)
|
|
return amx_RaiseError(amx,AMX_ERR_NATIVE);
|
|
|
|
if (((ucell)*cstr>UNPACKEDMAX)) {
|
|
needed=(lenstr+lensub+sizeof(cell))/sizeof(cell); /* # of cells needed */
|
|
assert(needed>0);
|
|
lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1);
|
|
} else {
|
|
lastaddr=(size_t)(params[1]+sizeof(cell)*(lenstr+lensub+1)-1);
|
|
} /* if */
|
|
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
|
|
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[])
|
|
*/
|
|
static cell AMX_NATIVE_CALL n_strval(AMX *amx,cell *params)
|
|
{
|
|
char str[50],*ptr;
|
|
cell *cstr,result;
|
|
int len,negate=0;
|
|
|
|
amx_GetAddr(amx,params[1],&cstr);
|
|
amx_StrLen(cstr,&len);
|
|
if (len>=50) {
|
|
//amx_RaiseError(amx,AMX_ERR_NATIVE);
|
|
return 0;
|
|
} /* if */
|
|
amx_GetString(str,cstr,0,sizeof str);
|
|
ptr=str;
|
|
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,cell *params)
|
|
{
|
|
char str[50];
|
|
cell value,mult;
|
|
cell *cstr;
|
|
int len,result,negate=0;
|
|
|
|
/* find out how many digits are needed */
|
|
mult=10;
|
|
len=1;
|
|
value=params[2];
|
|
if (value<0) {
|
|
negate=1;
|
|
len++;
|
|
value=-value;
|
|
} /* if */
|
|
while (value>=mult) {
|
|
len++;
|
|
mult*=10;
|
|
} /* while */
|
|
|
|
/* 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]='-';
|
|
amx_GetAddr(amx,params[1],&cstr);
|
|
amx_SetString(cstr,str,params[3],0,UNLIMITED);
|
|
return result;
|
|
}
|
|
|
|
/* ispacked(const string[]) */
|
|
static cell AMX_NATIVE_CALL n_ispacked(AMX *amx,cell *params)
|
|
{
|
|
cell *cstr;
|
|
amx_GetAddr(amx,params[1],&cstr);
|
|
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];
|
|
|
|
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,cell *params)
|
|
{
|
|
cell *cstr;
|
|
unsigned char dst[BITMASK+2];
|
|
char src[BITMASK+BITMASK/3+2];
|
|
int len;
|
|
size_t size;
|
|
|
|
/* get the source */
|
|
amx_GetAddr(amx,params[2],&cstr);
|
|
amx_GetString(src,cstr,0,sizeof src);
|
|
/* decode */
|
|
len=uudecode(dst,src);
|
|
/* store */
|
|
amx_GetAddr(amx,params[1],&cstr);
|
|
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,cell *params)
|
|
{
|
|
cell *cstr;
|
|
unsigned char src[BITMASK+2];
|
|
char dst[BITMASK+BITMASK/3+2];
|
|
|
|
/* get the source */
|
|
amx_GetAddr(amx,params[2],&cstr);
|
|
/* encode (and check for errors) */
|
|
if (uuencode(dst,src,params[3])) {
|
|
if (params[4]>0) {
|
|
amx_GetAddr(amx,params[1],&cstr);
|
|
*cstr=0;
|
|
} /* if */
|
|
return 0;
|
|
} /* if */
|
|
/* always add a \n */
|
|
assert(strlen(dst)+1<sizeof dst);
|
|
strcat(dst,"\n");
|
|
/* store */
|
|
amx_GetAddr(amx,params[1],&cstr);
|
|
amx_SetString(cstr,dst,1,0,params[4]);
|
|
return (((params[3]+2)/3) << 2)+2;
|
|
}
|
|
|
|
/* 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,cell *params)
|
|
{
|
|
cell *cdest,*csrc;
|
|
unsigned char *pdest,*psrc;
|
|
|
|
if (params[3]<0 || params[4]<0 || (params[3]+params[4])>params[5]*(int)sizeof(cell))
|
|
return 0;
|
|
amx_GetAddr(amx,params[1],&cdest);
|
|
amx_GetAddr(amx,params[2],&csrc);
|
|
pdest=(unsigned char*)cdest+params[3];
|
|
psrc=(unsigned char*)csrc;
|
|
memmove(pdest,psrc,params[4]);
|
|
return 1;
|
|
}
|
|
|
|
#if defined __cplusplus
|
|
extern "C"
|
|
#endif
|
|
const AMX_NATIVE_INFO string_Natives[] = {
|
|
{ "ispacked", n_ispacked },
|
|
{ "memcpy", n_memcpy },
|
|
{ "strcat", n_strcat },
|
|
{ "strcmp", n_strcmp },
|
|
{ "strdel", n_strdel },
|
|
{ "strfind", n_strfind },
|
|
{ "strins", n_strins },
|
|
{ "strlen", n_strlen },
|
|
{ "strmid", n_strmid },
|
|
{ "strpack", n_strpack },
|
|
{ "strunpack", n_strunpack },
|
|
{ "strval", n_strval },
|
|
{ "uudecode", n_uudecode },
|
|
{ "uuencode", n_uuencode },
|
|
{ "valstr", n_valstr },
|
|
{ NULL, NULL } /* terminator */
|
|
};
|
|
|
|
int AMXEXPORT amx_StringInit(AMX *amx)
|
|
{
|
|
return amx_Register(amx, string_Natives, -1);
|
|
}
|
|
|
|
int AMXEXPORT amx_StringCleanup(AMX *amx)
|
|
{
|
|
(void)amx;
|
|
return AMX_ERR_NONE;
|
|
}
|