diff --git a/source/amx/amx.h b/source/amx/amx.h index 022d7510..0de16d4e 100644 --- a/source/amx/amx.h +++ b/source/amx/amx.h @@ -27,54 +27,63 @@ #include /* for size_t */ #include +#if (defined __linux || defined __linux__) && !defined __LINUX__ + #define __LINUX__ +#endif #if defined FREEBSD && !defined __FreeBSD__ #define __FreeBSD__ #endif -#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ +#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ #include #endif +#if defined __GNUC__ + #define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +#endif + +#if !defined HAVE_STDINT_H + #if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) \ + || defined __GNUC__ || defined __LCC__ || defined __DMC__ \ + || (defined __WATCOMC__ && __WATCOMC__ >= 1200) + #define HAVE_STDINT_H 1 + #endif +#endif +#if !defined HAVE_INTTYPES_H + #if defined __FreeBSD__ || defined __APPLE__ + #define HAVE_INTTYPES_H 1 + #endif +#endif #if defined HAVE_STDINT_H #include +#elif defined HAVE_INTTYPES_H + #include #else - #if defined __LCC__ || defined __DMC__ || defined LINUX || (defined __WATCOMC__ && __WATCOMC__ >= 1200) - #if defined HAVE_INTTYPES_H - #include - #else - #include - #endif - #elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L - /* The ISO C99 defines the int16_t and int_32t types. If the compiler got - * here, these types are probably undefined. - */ - #if defined __MACH__ - #include - typedef unsigned short int uint16_t; - typedef unsigned long int uint32_t; - #elif defined __FreeBSD__ - #include + #if defined __MACH__ + #include + #endif + typedef short int int16_t; + typedef unsigned short int uint16_t; + #if defined SN_TARGET_PS2 + typedef int int32_t; + typedef unsigned int uint32_t; + #else + typedef long int int32_t; + typedef unsigned long int uint32_t; + #endif + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + #define HAVE_I64 + #endif + #if !defined _INTPTR_T_DEFINED + #if defined _LP64 || defined WIN64 || defined _WIN64 + typedef __int64 intptr_t; #else - typedef short int int16_t; - typedef unsigned short int uint16_t; - #if defined SN_TARGET_PS2 - typedef int int32_t; - typedef unsigned int uint32_t; - #else - typedef long int int32_t; - typedef unsigned long int uint32_t; - #endif - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; - #define HAVE_I64 - #elif defined __GNUC__ - typedef long long int64_t; - typedef unsigned long long uint64_t; - #define HAVE_I64 - #endif + typedef int32_t intptr_t; #endif #endif - #define HAVE_STDINT_H #endif #if defined _LP64 || defined WIN64 || defined _WIN64 #if !defined __64BIT__ @@ -82,8 +91,17 @@ #endif #endif -#if HAVE_ALLOCA_H +#if !defined HAVE_ALLOCA_H + #if defined __GNUC__ || defined __LCC__ || defined __DMC__ || defined __ARMCC_VERSION + #define HAVE_ALLOCA_H 1 + #elif defined __WATCOMC__ && __WATCOMC__ >= 1200 + #define HAVE_ALLOCA_H 1 + #endif +#endif +#if defined HAVE_ALLOCA_H && HAVE_ALLOCA_H #include +#elif defined __BORLANDC__ + #include #endif #if defined __WIN32__ || defined _WIN32 || defined WIN32 /* || defined __MSDOS__ */ #if !defined alloca @@ -94,14 +112,21 @@ #if !defined arraysize #define arraysize(array) (sizeof(array) / sizeof((array)[0])) #endif + #if !defined assert_static - /* see "Compile-Time Assertions" by Ralf Holly, - * C/C++ Users Journal, November 2004 - */ - #define assert_static(e) \ - do { \ - enum { assert_static__ = 1/(e) }; \ - } while (0) + #if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112) || GCC_VERSION >= 40600 + #define assert_static(test) _Static_assert(test, "assert") + #else + /* see "Compile-Time Assertions" by Greg Miller, + * (with modifications to port it to C) + */ + #define _ASSERT_STATIC_SYMBOL_INNER(line) __ASSERT_STATIC_ ## line + #define _ASSERT_STATIC_SYMBOL(line) _ASSERT_STATIC_SYMBOL_INNER(line) + #define assert_static(test) \ + do { \ + typedef char _ASSERT_STATIC_SYMBOL(__LINE__)[ ((test) ? 1 : -1) ]; \ + } while (0) + #endif #endif #ifdef __cplusplus @@ -192,9 +217,14 @@ typedef int (AMXAPI *AMX_IDLE)(struct tagAMX *amx, int AMXAPI Exec(struct tagAMX #endif #if defined _MSC_VER - #pragma warning(disable:4103) /* disable warning message 4103 that complains - * about pragma pack in a header file */ #pragma warning(disable:4100) /* "'%$S' : unreferenced formal parameter" */ + #pragma warning(disable:4103) /* disable warning message 4103 that complains about pragma pack in a header file */ + #pragma warning(disable:4127) /* "conditional expression is constant" (needed for static_assert) */ + #pragma warning(disable:4996) /* POSIX name is deprecated */ +#elif defined __GNUC__ +#elif defined __clang__ + #pragma GCC diagnostic ignored "-Wlogical-op-parentheses" + #pragma GCC diagnostic ignored "-Wbitwise-op-parentheses" #endif /* Some compilers do not support the #pragma align, which should be fine. Some @@ -307,12 +337,15 @@ typedef struct tagAMX_HEADER { int32_t nametable PACKED; /* name table */ } AMX_HEADER; +#define AMX_MAGIC_16 0xf1e2 +#define AMX_MAGIC_32 0xf1e0 +#define AMX_MAGIC_64 0xf1e1 #if PAWN_CELL_SIZE==16 - #define AMX_MAGIC 0xf1e2 + #define AMX_MAGIC AMX_MAGIC_16 #elif PAWN_CELL_SIZE==32 - #define AMX_MAGIC 0xf1e0 + #define AMX_MAGIC AMX_MAGIC_32 #elif PAWN_CELL_SIZE==64 - #define AMX_MAGIC 0xf1e1 + #define AMX_MAGIC AMX_MAGIC_64 #endif enum { @@ -377,19 +410,51 @@ enum { #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ #define amx_ctof(c) ( * ((double*)&c) ) /* cell to float */ #else - #error Unsupported cell size + // amx_ftoc() and amx_ctof() cannot be used +#endif + +/* when a pointer cannot be stored in a cell, cells that hold relocated + * addresses need to be expanded + */ +#if defined __64BIT__ && PAWN_CELL_SIZE<64 + #define CELLMASK (((int64_t)1 << PAWN_CELL_SIZE) - 1) + #define amx_Address(amx,addr) \ + (cell*)(((int64_t)((amx)->data ? (amx)->data : (amx)->code) & ~CELLMASK) | ((int64_t)(addr) & CELLMASK)) +#elif defined __32BIT__ && PAWN_CELL_SIZE<32 + #define CELLMASK ((1L << PAWN_CELL_SIZE) - 1) + #define amx_Address(amx,addr) \ + (cell*)(((int32_t)((amx)->data ? (amx)->data : (amx)->code) & ~CELLMASK) | ((int32_t)(addr) & CELLMASK)) +#else + #define amx_Address(amx,addr) \ + (cell*)(((int32_t)((amx)->data ? (amx)->data : (amx)->base+(int)((AMX_HEADER *)(amx)->base)->dat)) + ((int32_t)(addr))) #endif -#define amx_StrParam(amx,param,result) \ +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L + /* C99: use variable-length arrays */ + #define amx_StrParam_Type(amx,param,result,type) \ + int result##_length_; \ + amx_StrLen(amx_Address(amx,param),&result##_length_); \ + char result##_vla_[(result##_length_+1)*sizeof(*(result))]; \ + (result)=(type)result##_vla_; \ + amx_GetString((char*)(result),amx_Address(amx,param), \ + sizeof(*(result))>1,result##_length_+1) + #define amx_StrParam(amx,param,result) \ + amx_StrParam_Type(amx,param,result,void*) +#else + /* macro using alloca() */ + #define amx_StrParam_Type(amx,param,result,type) \ do { \ - cell *amx_cstr_; int amx_length_; \ - amx_GetAddr((amx), (param), &amx_cstr_); \ - amx_StrLen(amx_cstr_, &amx_length_); \ - if (amx_length_ > 0 && \ - ((result) = (void*)alloca((amx_length_ + 1) * sizeof(*(result)))) != NULL) \ - amx_GetString((char*)(result), amx_cstr_, sizeof(*(result))>1, amx_length_ + 1); \ + int result##_length_; \ + amx_StrLen(amx_Address(amx,param),&result##_length_); \ + if (result##_length_>0 && \ + ((result)=(type)alloca((result##_length_+1)*sizeof(*(result))))!=NULL) \ + amx_GetString((char*)(result),amx_Address(amx,param), \ + sizeof(*(result))>1,result##_length_+1); \ else (result) = NULL; \ } while (0) + #define amx_StrParam(amx,param,result) \ + amx_StrParam_Type(amx,param,result,void*) +#endif uint16_t * AMXAPI amx_Align16(uint16_t *v); uint32_t * AMXAPI amx_Align32(uint32_t *v); @@ -439,11 +504,24 @@ int AMXAPI amx_UTF8Len(const cell *cstr, int *length); int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); #if PAWN_CELL_SIZE==16 - #define amx_AlignCell(v) amx_Align16(v) + void amx_Swap16(uint16_t *v); +#endif +#if PAWN_CELL_SIZE==32 + void amx_Swap32(uint32_t *v); +#endif +#if PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined INT64_MAX || defined HAVE_I64) + void amx_Swap64(uint64_t *v); +#endif + +#if PAWN_CELL_SIZE==16 + #define amx_AlignCell(v) amx_Align16((uint16_t*)(v)) + #define amx_SwapCell(v) amx_Swap16((uint16_t*)(v)) #elif PAWN_CELL_SIZE==32 - #define amx_AlignCell(v) amx_Align32(v) -#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) - #define amx_AlignCell(v) amx_Align64(v) + #define amx_AlignCell(v) amx_Align32((uint32_t*)(v)) + #define amx_SwapCell(v) amx_Swap32((uint32_t*)(v)) +#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined INT64_MAX || defined HAVE_I64) + #define amx_AlignCell(v) amx_Align64((uint64_t*)(v)) + #define amx_SwapCell(v) amx_Swap64((uint64_t*)(v)) #else #error Unsupported cell size #endif @@ -452,7 +530,7 @@ int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); amx_Register((amx), amx_NativeInfo((name),(func)), 1); #if !defined AMX_NO_ALIGN - #if defined LINUX || defined __FreeBSD__ + #if defined __LINUX__ || defined __FreeBSD__ || defined __APPLE__ #pragma pack() /* reset default packing */ #elif defined MACOS && defined __MWERKS__ #pragma options align=reset diff --git a/source/amx/amxFile.rc b/source/amx/amxFile.rc index d9e128eb..fa379979 100644 --- a/source/amx/amxFile.rc +++ b/source/amx/amxFile.rc @@ -10,15 +10,15 @@ * All strings MUST have an explicit \0. See the Windows SDK documentation * for details on version information and the VERSIONINFO structure. */ -#define VERSION 1 -#define REVISION 1 +#define VERSION 4 +#define REVISION 0 #define BUILD 0 -#define VERSIONSTR "1.1.0\0" +#define VERSIONSTR "4.0.0\0" #define VERSIONNAME "amxFile.dll\0" #define VERSIONDESCRIPTION "Pawn AMX: File I/O support\0" #define VERSIONCOMPANYNAME "ITB CompuPhase\0" #define VERSIONPRODUCTNAME "amxFile\0" -#define VERSIONCOPYRIGHT "Copyright \251 2004-2006 ITB CompuPhase\0" +#define VERSIONCOPYRIGHT "Copyright \251 2004-2016 ITB CompuPhase\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VERSION, REVISION, BUILD, 0 diff --git a/source/amx/amxFloat.rc b/source/amx/amxFloat.rc index 339af34f..205f9dbb 100644 --- a/source/amx/amxFloat.rc +++ b/source/amx/amxFloat.rc @@ -10,15 +10,15 @@ * All strings MUST have an explicit \0. See the Windows SDK documentation * for details on version information and the VERSIONINFO structure. */ -#define VERSION 1 +#define VERSION 4 #define REVISION 0 #define BUILD 0 -#define VERSIONSTR "1.0.0\0" +#define VERSIONSTR "4.0.0\0" #define VERSIONNAME "amxFloat.dll\0" #define VERSIONDESCRIPTION "Pawn AMX: Floating Point support\0" #define VERSIONCOMPANYNAME "ITB CompuPhase\0" #define VERSIONPRODUCTNAME "amxFloat\0" -#define VERSIONCOPYRIGHT "Copyright \251 2003-2006 ITB CompuPhase\0" +#define VERSIONCOPYRIGHT "Copyright \251 2003-2013 ITB CompuPhase\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VERSION, REVISION, BUILD, 0 diff --git a/source/amx/amxargs.c b/source/amx/amxargs.c index b556b699..c73c7446 100644 --- a/source/amx/amxargs.c +++ b/source/amx/amxargs.c @@ -1,24 +1,20 @@ /* Script Arguments support module for the Pawn Abstract Machine * - * Copyright (c) ITB CompuPhase, 2005-2006 + * Copyright (c) ITB CompuPhase, 2005-2016 * - * 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. + * 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 * - * 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: + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * 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: amxargs.c 3649 2006-10-12 13:13:57Z thiadmer $ + * Version: $Id: amxargs.c 5588 2016-10-25 11:13:28Z $ */ #if defined _UNICODE || defined __UNICODE__ || defined UNICODE # if !defined UNICODE /* for Windows */ @@ -35,13 +31,16 @@ #include #include #include -#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ +#include "osdefs.h" +#if defined __WIN32__ || defined __MSDOS__ #include #endif -#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows +#if defined __WIN32__ || defined _Windows #include #endif -#include "osdefs.h" +#if defined __GNUC__ || defined __clang__ + #include +#endif #include "amx.h" #if defined _UNICODE @@ -91,8 +90,8 @@ static const TCHAR *rawcmdline(void) { #if defined __WIN32__ || defined _WIN32 || defined WIN32 #elif defined _Windows || defined __MSDOS__ - static char cmdbuffer[128]; - #elif defined LINUX + static char cmdbuffer[128]; /* DOS & Windows 3.1 are never in Unicode mode */ + #elif defined __LINUX__ static char cmdbuffer[1024]; /* some arbitrary maximum */ #endif const TCHAR *ptr; @@ -115,7 +114,7 @@ static const TCHAR *rawcmdline(void) if ((cmd == strchr(cmdbuffer, '\r')) != NULL) *cmd = '\0'; /* also erase \r after the last option (if any) */ cmdline = cmdbuffer; - #elif defined LINUX + #elif defined __LINUX__ /* Options in /proc//cmdline are delimited with '\0' characters * rather than spaces. */ @@ -132,7 +131,6 @@ static const TCHAR *rawcmdline(void) fread(cmdbuffer, 1, fsize, fp); fclose(fp); cmdbuffer[fsize] = '\0'; /* terminate with double-zero */ - // ??? convert to Unicode /* convert '\0' characters to spaces, for uniform parsing */ for (ptr = cmdbuffer; *ptr != ' '; ptr = strchr(ptr, '\0') + 1) *ptr = ' '; @@ -140,7 +138,10 @@ static const TCHAR *rawcmdline(void) skip++; } /* if */ #else - #error Platform not supported + /* no mechanism for determining the commandline, so it + * must be supplied with amx_ArgsSetCmdLine() instead. + */ + ptr = ""; #endif /* skip leading white space */ @@ -208,7 +209,7 @@ static const TCHAR *matcharg(const TCHAR *key, int skip, int *length) int index, optlen, keylen; const TCHAR *option, *vptr; - keylen = (key != NULL) ? _tcslen(key) : 0; + keylen = (key != NULL) ? (int)_tcslen(key) : 0; index = 0; while ((option = tokenize(cmdline, index, length)) != NULL) { /* check for a colon or an equal sign (':' or '=') */ @@ -227,7 +228,7 @@ static const TCHAR *matcharg(const TCHAR *key, int skip, int *length) optlen++; /* if ':' or '=' was found, skip it too */ option += optlen; /* point behind option */ *length -= optlen; /* length of the value, not of the option */ - assert(length >= 0); + assert(*length >= 0); if (skip-- == 0) break; } /* if */ @@ -251,11 +252,7 @@ static cell AMX_NATIVE_CALL n_argindex(AMX *amx, const cell *params) max = (int)params[3]; if (max <= 0) return 0; - amx_GetAddr(amx, params[2], &cptr); - if (cptr == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ + cptr = amx_Address(amx, params[2]); if ((option = tokenize(cmdline, params[1], &length)) == NULL) { /* option not found, return an empty string */ @@ -293,11 +290,7 @@ static cell AMX_NATIVE_CALL n_argstr(AMX *amx, const cell *params) if (max <= 0) return 0; amx_StrParam(amx, params[2], key); - amx_GetAddr(amx, params[3], &cptr); - if (cptr == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ + cptr = amx_Address(amx, params[3]); option = matcharg(key, (int)params[1], &length); if (option == NULL) @@ -336,11 +329,7 @@ static cell AMX_NATIVE_CALL n_argvalue(AMX *amx, const cell *params) cell *cptr; amx_StrParam(amx, params[2], key); - amx_GetAddr(amx, params[3], &cptr); - if (cptr == NULL) { - amx_RaiseError(amx, AMX_ERR_NATIVE); - return 0; - } /* if */ + cptr = amx_Address(amx, params[3]); option = matcharg(key, (int)params[1], &length); if (option == NULL) @@ -377,12 +366,12 @@ const AMX_NATIVE_INFO args_Natives[] = { { NULL, NULL } /* terminator */ }; -int AMXEXPORT amx_ArgsInit(AMX *amx) +int AMXEXPORT AMXAPI amx_ArgsInit(AMX *amx) { return amx_Register(amx, args_Natives, -1); } -int AMXEXPORT amx_ArgsCleanup(AMX *amx) +int AMXEXPORT AMXAPI amx_ArgsCleanup(AMX *amx) { (void)amx; return AMX_ERR_NONE; @@ -394,7 +383,7 @@ int AMXEXPORT amx_ArgsCleanup(AMX *amx) * that is passed in to this function is NOT copied, so it may not be freed * after the call. */ -int AMXEXPORT amx_ArgsSetCmdLine(const TCHAR *cmd) +int AMXEXPORT AMXAPI amx_ArgsSetCmdLine(const TCHAR *cmd) { cmdline = cmd; return AMX_ERR_NONE; diff --git a/source/amx/amxargs.def b/source/amx/amxargs.def new file mode 100644 index 00000000..5ec007af --- /dev/null +++ b/source/amx/amxargs.def @@ -0,0 +1,6 @@ +NAME amxArgs +DESCRIPTION 'Pawn AMX: Script Arguments support' + +EXPORTS + amx_ArgsInit + amx_ArgsCleanup diff --git a/source/amx/amxargs.rc b/source/amx/amxargs.rc index 95f76736..6c8fcd8a 100644 --- a/source/amx/amxargs.rc +++ b/source/amx/amxargs.rc @@ -10,15 +10,15 @@ * All strings MUST have an explicit \0. See the Windows SDK documentation * for details on version information and the VERSIONINFO structure. */ -#define VERSION 1 +#define VERSION 4 #define REVISION 0 #define BUILD 0 -#define VERSIONSTR "1.0.0\0" +#define VERSIONSTR "4.0.0\0" #define VERSIONNAME "amxArgs.dll\0" #define VERSIONDESCRIPTION "Pawn AMX: Script Arguments support\0" #define VERSIONCOMPANYNAME "ITB CompuPhase\0" #define VERSIONPRODUCTNAME "amxArgs\0" -#define VERSIONCOPYRIGHT "Copyright \251 2005-2006 ITB CompuPhase\0" +#define VERSIONCOPYRIGHT "Copyright \251 2005-2016 ITB CompuPhase\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VERSION, REVISION, BUILD, 0 diff --git a/source/amx/amxcons.c b/source/amx/amxcons.c index b4e36596..b1d9a7a1 100644 --- a/source/amx/amxcons.c +++ b/source/amx/amxcons.c @@ -4,25 +4,21 @@ * cannot always be implemented with portable C functions. In other words, * these routines must be ported to other environments. * - * Copyright (c) ITB CompuPhase, 1997-2006 + * Copyright (c) ITB CompuPhase, 1997-2016 * - * 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. + * 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 * - * 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: + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * 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: amxcons.c 3649 2006-10-12 13:13:57Z thiadmer $ + * Version: $Id: amxcons.c 5587 2016-10-25 09:59:46Z $ */ #if defined _UNICODE || defined __UNICODE__ || defined UNICODE @@ -40,13 +36,26 @@ #include #include #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ + #define HAVE_CONIO #include #include #endif -#if defined USE_CURSES +#if defined USE_CURSES || defined HAVE_CURSES_H #include + #if !defined CURSES + #define CURSES 1 + #endif +#endif +#include "osdefs.h" +#if defined __ECOS__ + /* eCos puts include files in cyg/package_name */ + #include + #include + #include + #include +#else + #include "amx.h" #endif -#include "amx.h" #if defined __WIN32__ || defined _WIN32 || defined WIN32 #include #endif @@ -56,18 +65,21 @@ #elif !defined __T typedef char TCHAR; # define __T(string) string +# define _fgetts fgets +# define _puttchar putchar +# define _stprintf sprintf # define _tcschr strchr # define _tcscpy strcpy # define _tcsdup strdup # define _tcslen strlen -# define _stprintf sprintf +# define _tprintf printf #endif #include "amxcons.h" -#if defined __MSDOS__ +#if defined AMX_TERMINAL #define EOL_CHAR '\r' #endif -#if defined __WIN32__ || defined _WIN32 || defined WIN32 +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ #define EOL_CHAR '\r' #endif #if !defined EOL_CHAR @@ -87,17 +99,118 @@ int amx_termctl(int,int); void amx_clrscr(void); void amx_clreol(void); - void amx_gotoxy(int x,int y); + int amx_gotoxy(int x,int y); void amx_wherexy(int *x,int *y); unsigned int amx_setattr(int foregr,int backgr,int highlight); void amx_console(int columns, int lines, int flags); -#elif defined VT100 || defined LINUX || defined ANSITERM + void amx_viewsize(int *width,int *height); + int amx_kbhit(void); +#elif defined CURSES && CURSES != 0 + /* Use the "curses" library to implement the console */ + static WINDOW *curseswin; + #define amx_putstr(s) printw("%s",(s)) + #define amx_putchar(c) addch(c) + #define amx_fflush() refresh() + #define amx_getch() getch() + #define amx_gets(s,n) getnstr((s),(n)) + #define amx_clrscr() clear() + #define amx_clreol() clrtoeol() + #define amx_gotoxy(x,y) move((y)-1,(x)-1) + #define amx_console(c,l,f) ((void)(c),(void)(l),(void)(f)) + unsigned int amx_setattr(int foregr,int backgr,int highlight) + { + if (highlight>0) + attron(A_STANDOUT); + else + attroff(A_STANDOUT); + //??? in future, also handle colours + } + void CreateConsole(void); + int amx_kbhit(void) + { + int result; + CreateConsole(); + nodelay(curseswin,TRUE); /* enter non-blocking state */ + result=getch(); /* read key (if any) */ + nodelay(curseswin,FALSE); /* leave non-blocking state */ + if (result!=ERR) + ungetch(result); /* a key is waiting, push it back */ + return (result==ERR) ? 0 : 1; + } + int amx_termctl(int code,int value) + { + switch (code) { + case 0: /* query terminal support */ + return 1; + /* case 1: */ /* switch auto-wrap on/off (not supported in curses!) */ + /* case 2: */ /* create/switch to another console */ + case 3: /* set emphasized font */ + if (value) + attron(A_BOLD); + else + attroff(A_BOLD); + return 1; + /* case 4: */ /* query whether a terminal is "open" */ + default: + return 0; + } /* switch */ + } + void amx_wherexy(int *x,int *y) + { + int row,col; + getyx(curseswin,row,col); + if (x!=NULL) + *x=col+1; + if (y!=NULL) + *y=row+1; + } + void amx_viewsize(int *width,int *height) + { + int row,col; + getmaxyx(curseswin,row,col); + if (width!=NULL) + *width=col; + if (height!=NULL) + *height=row; + } +#elif defined VT100 || defined __LINUX__ || defined ANSITERM || defined __ECOS__ /* ANSI/VT100 terminal, or shell emulating "xterm" */ - #define amx_putstr(s) printf("%s",(s)) - #define amx_putchar(c) putchar(c) - #define amx_fflush() fflush(stdout) - #define amx_getch() getch() - #define amx_gets(s,n) fgets(s,n,stdin) + #if defined __ECOS__ + #define AMXCONSOLE_NOIDLE + #endif + + #if CYGPKG_PAWN_AMXCONSOLE_DIAG==1 + /* eCos has basically two ways to make simple exchanges with a terminal: + * - with the diag_*() functions (no input provided!) + * - with f*() functions (fprintf(),fputs(), etc). + */ + #define amx_fflush() + + static int amx_putstr(TCHAR *s) + { + diag_write_string(s); + return 1; + } + static int amx_putchar(TCHAR c) + { + diag_write_char(c); + return c; + } + static char amx_getch(void) + { + char c=-1; + HAL_DIAG_READ_CHAR(c); + return c; + } + #else + + #define amx_putstr(s) fputs((s),stdout) + #define amx_putchar(c) putchar(c) + #define amx_fflush() fflush(stdout) + #define amx_getch() getch() + #define amx_gets(s,n) fgets(s,n,stdin) + #define amx_kbhit() kbhit() + #endif int amx_termctl(int code,int value) { @@ -144,12 +257,13 @@ amx_putstr("\033[K"); amx_fflush(); /* pump through the terminal codes */ } - void amx_gotoxy(int x,int y) + int amx_gotoxy(int x,int y) { char str[30]; _stprintf(str,"\033[%d;%dH",y,x); amx_putstr(str); amx_fflush(); /* pump through the terminal codes */ + return 1; } void amx_wherexy(int *x,int *y) { @@ -166,11 +280,13 @@ for (i=0; i<8 && (val=amx_getch())!=';'; i++) str[i]=(char)val; str[i]='\0'; - *y=atoi(str); + if (y!=NULL) + *y=atoi(str); for (i=0; i<8 && (val=amx_getch())!='R'; i++) str[i]=(char)val; str[i]='\0'; - *x=atoi(str); + if (x!=NULL) + *x=atoi(str); #if defined ANSITERM val=amx_getch(); assert(val=='\r'); /* ANSI driver adds CR to the end of the command */ @@ -214,13 +330,20 @@ amx_putstr(str); amx_fflush(); } + void amx_viewsize(int *width,int *height) + { + /* a trick to get the size of the terminal is to position the cursor far + * away and then read it back + */ + amx_gotoxy(999,999); + amx_wherexy(width,height); + } #elif defined __WIN32__ || defined _WIN32 || defined WIN32 /* Win32 console */ - #define amx_putstr(s) printf("%s",(s)) - #define amx_putchar(c) putchar(c) - #define amx_fflush() fflush(stdout) - #define amx_getch() getch() - #define amx_gets(s,n) fgets(s,n,stdin) + #define amx_putstr(s) _tprintf("%s",(s)) + #define amx_putchar(c) _puttchar(c) + #define amx_fflush() fflush(stdout) + #define amx_gets(s,n) _fgetts(s,n,stdin) int amx_termctl(int code,int value) { @@ -231,10 +354,13 @@ case 1: { /* switch auto-wrap on/off */ /* only works in Windows 2000/XP */ HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); - DWORD Flags=ENABLE_PROCESSED_OUTPUT; + DWORD mode; + GetConsoleMode(hConsole,&mode); if (value) - Flags |= ENABLE_WRAP_AT_EOL_OUTPUT; - SetConsoleMode(hConsole,Flags); + mode |= ENABLE_WRAP_AT_EOL_OUTPUT; + else + mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT; + SetConsoleMode(hConsole,mode); return 1; } /* case */ @@ -273,7 +399,7 @@ FillConsoleOutputCharacter(hConsole,' ',dwConSize,csbi.dwCursorPosition,&cCharsWritten); FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,csbi.dwCursorPosition,&cCharsWritten); } - void amx_gotoxy(int x,int y) + int amx_gotoxy(int x,int y) { COORD point; CONSOLE_SCREEN_BUFFER_INFO csbi; @@ -281,11 +407,12 @@ GetConsoleScreenBufferInfo(hConsole, &csbi); if (x<=0 || x>csbi.dwSize.X || y<=0 || y>csbi.dwSize.Y) - return; + return 0; amx_fflush(); /* make sure all output is written */ point.X=(short)(x-1); point.Y=(short)(y-1); SetConsoleCursorPosition(hConsole,point); + return 1; } void amx_wherexy(int *x,int *y) { @@ -323,30 +450,58 @@ { SMALL_RECT rect; COORD dwSize; + HANDLE hConsole; (void)flags; dwSize.X=(short)columns; dwSize.Y=(short)lines; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),dwSize); + hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleScreenBufferSize(hConsole,dwSize); rect.Left=0; rect.Top=0; rect.Right=(short)(columns-1); rect.Bottom=(short)(lines-1); - SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE),TRUE,&rect); + SetConsoleWindowInfo(hConsole,TRUE,&rect); + } + void amx_viewsize(int *width,int *height) + { + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&csbi); + if (width!=NULL) + *width=(int)csbi.dwSize.X; + if (height!=NULL) + *height=(int)(csbi.srWindow.Bottom-csbi.srWindow.Top+1); + } + int amx_getch(void) + { + TCHAR ch; + DWORD count,mode; + HANDLE hConsole=GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hConsole,&mode); + SetConsoleMode(hConsole,mode & ~(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT)); + while (ReadFile(hConsole,&ch,1,&count,NULL) && count==0) + /* nothing */; + SetConsoleMode(hConsole,mode); + if (count>0) + return ch; + return EOF; + } + int amx_kbhit(void) + { + DWORD count=0; + HANDLE hConsole; + hConsole=GetStdHandle(STD_INPUT_HANDLE); + if (GetFileType(hConsole)==FILE_TYPE_PIPE) { + PeekNamedPipe(hConsole,NULL,0,NULL,&count,NULL); + } else { + INPUT_RECORD rec; + while (PeekConsoleInput(hConsole,&rec,1,&count)) { + if (count==0 || (rec.EventType==KEY_EVENT && rec.Event.KeyEvent.bKeyDown)) + break; + ReadConsoleInput(hConsole,&rec,1,&count); + } + } + return (count>0); } -#elif defined USE_CURSES - /* Use the "curses" library to implement the console */ - #define amx_putstr(s) printw("%s",(s)) - #define amx_putchar(c) addch(c) - #define amx_fflush() (0) - #define amx_getch() getch() - #define amx_gets(s,n) getnstr(s,n) - #define amx_clrscr() clear() - #define amx_clreol() clrtoeol() - #define amx_gotoxy(x,y) (void)(0) - #define amx_wherexy(x,y) (*(x)=*(y)=0) - #define amx_setattr(c,b,h) (0) - #define amx_termctl(c,v) (0) - #define amx_console(c,l,f) (void)(0) #else /* assume a streaming terminal; limited features (no colour, no cursor * control) @@ -354,15 +509,22 @@ #define amx_putstr(s) printf("%s",(s)) #define amx_putchar(c) putchar(c) #define amx_fflush() fflush(stdout) - #define amx_getch() getch() #define amx_gets(s,n) fgets(s,n,stdin) #define amx_clrscr() (void)(0) #define amx_clreol() (void)(0) - #define amx_gotoxy(x,y) ((void)(x),(void)(y)) + #define amx_gotoxy(x,y) ((void)(x),(void)(y),(0)) #define amx_wherexy(x,y) (*(x)=*(y)=0) #define amx_setattr(c,b,h) ((void)(c),(void)(b),(void)(h),(0)) #define amx_termctl(c,v) ((void)(c),(void)(v),(0)) #define amx_console(c,l,f) ((void)(c),(void)(l),(void)(f)) + #define amx_viewsize (*(x)=80,*(y)=25) + #if defined HAVE_CONIO + #define amx_getch() getch() + #define amx_kbhit() kbhit() + #else + #define amx_getch() getchar() + #define amx_kbhit() (0) + #endif #endif #if !defined AMX_TERMINAL && (defined __WIN32__ || defined _WIN32 || defined WIN32) @@ -373,20 +535,22 @@ createdconsole=1; } /* if */ } -#elif defined USE_CURSES +#elif defined CURSES && CURSES != 0 // The Mac OS X build variant uses curses. void CreateConsole(void) { static int createdconsole=0; if (!createdconsole) { - initscr(); + curseswin=initscr(); + if (has_colors()) + start_color(); cbreak(); noecho(); nonl(); - intrflush(stdscr, FALSE); - keypad(stdscr, TRUE); - - createdconsole=1; - } /* if */ + scrollok(curseswin,TRUE); + intrflush(curseswin,FALSE); + keypad(curseswin,TRUE); + createdconsole=1; + } /* if */ } #else #define CreateConsole() @@ -446,12 +610,19 @@ static TCHAR *amx_strval(TCHAR buffer[], long value, int format, int width) if (value < 0) { buffer[0] = __T('-'); start = stop = 1; - value = -value; - } /* if */ - do { - buffer[stop++] = (TCHAR)((value % 10) + __T('0')); - value /= 10; - } while (value > 0); + do { + temp = (TCHAR)(value % 10); + if (temp > 0) + temp = (TCHAR)(temp - 10); + buffer[stop++] = (TCHAR)(__T('0') - temp); + value /= 10; + } while (value != 0); + } else { + do { + buffer[stop++] = (TCHAR)((value % 10) + __T('0')); + value /= 10; + } while (value != 0); + } } else { /* hexadecimal */ unsigned long v = (unsigned long)value; /* copy to unsigned value for shifting */ @@ -519,7 +690,7 @@ static TCHAR *formatfixed(TCHAR *string,cell value,TCHAR align,int width,TCHAR d string[0]=__T('\0'); /* add sign */ - i=_tcslen(string); + i=(int)_tcslen(string); string[i]=vsign; string[i+1]=__T('\0'); @@ -528,12 +699,12 @@ static TCHAR *formatfixed(TCHAR *string,cell value,TCHAR align,int width,TCHAR d /* add fractional part */ if (digits>0) { - i=_tcslen(string); + i=(int)_tcslen(string); string[i]=decpoint; amx_strval(string+i+1,(long)value,SV_DECIMAL,-digits); } /* if */ - len=_tcslen(string); + len=(int)_tcslen(string); if (len0) @@ -588,7 +755,7 @@ static int dochar(AMX *amx,TCHAR ch,cell param,TCHAR sign,TCHAR decpoint,int wid case __T('d'): { cell value; int length=1; - amx_GetAddr(amx,param,&cptr); + cptr=amx_Address(amx,param); value=*cptr; if (value<0 || sign==__T('+')) length++; @@ -625,9 +792,17 @@ static int dochar(AMX *amx,TCHAR ch,cell param,TCHAR sign,TCHAR decpoint,int wid if (width>0) _stprintf(formatstring+_tcslen(formatstring),__T("%d"),width); _stprintf(formatstring+_tcslen(formatstring),__T(".%df"),digits); - /* ??? decimal comma? */ - amx_GetAddr(amx,param,&cptr); - _stprintf(buffer,formatstring,*(float*)cptr); + cptr=amx_Address(amx,param); + #if PAWN_CELL_SIZE == 64 + _stprintf(buffer,formatstring,*(double*)cptr); + #else + _stprintf(buffer,formatstring,*(float*)cptr); + #endif + if (decpoint==__T(',')) { + TCHAR *ptr=_tcschr(buffer,__T('.')); + if (ptr!=NULL) + *ptr=__T(','); + } /* if */ f_putstr(user,buffer); return 1; #endif @@ -638,7 +813,7 @@ static int dochar(AMX *amx,TCHAR ch,cell param,TCHAR sign,TCHAR decpoint,int wid #if !defined FLOATPOINT case __T('r'): /* if fixed point is enabled, and floating point is not, %r == %q */ #endif - amx_GetAddr(amx,param,&cptr); + cptr=amx_Address(amx,param); /* format the number */ if (digits==INT_MAX) digits=3; @@ -665,7 +840,7 @@ static int dochar(AMX *amx,TCHAR ch,cell param,TCHAR sign,TCHAR decpoint,int wid info.f_putstr=f_putstr; info.f_putchar=f_putchar; info.user=user; - amx_GetAddr(amx,param,&cptr); + cptr=amx_Address(amx,param); amx_printstring(amx,cptr,&info); return 1; } /* case */ @@ -673,7 +848,7 @@ static int dochar(AMX *amx,TCHAR ch,cell param,TCHAR sign,TCHAR decpoint,int wid case __T('x'): { ucell value; int length=1; - amx_GetAddr(amx,param,&cptr); + cptr=amx_Address(amx,param); value=*(ucell*)cptr; while (value>=0x10) { length++; @@ -698,7 +873,7 @@ static int dochar(AMX *amx,TCHAR ch,cell param,TCHAR sign,TCHAR decpoint,int wid enum { FMT_NONE, /* not in format state; accept '%' */ - FMT_START, /* found '%', accept '+', '-' (START), '0' (filler; START), digit (WIDTH), '.' (DECIM) or format letter (done) */ + FMT_START, /* found '%', accept '+', '-' (START), '0' (filler; START), digit (WIDTH), '.' (DECIM), or '%' or format letter (done) */ FMT_WIDTH, /* found digit after '%' or sign, accept digit (WIDTH), '.' (DECIM) or format letter (done) */ FMT_DECIM, /* found digit after '.', accept accept digit (DECIM) or format letter (done) */ }; @@ -731,8 +906,11 @@ static int formatstate(TCHAR c,int *state,TCHAR *sign,TCHAR *decpoint,int *width *decpoint=c; *digits=0; *state=FMT_DECIM; + } else if (c==__T('%')) { + *state=FMT_NONE; + return -1; /* print literal '%' */ } else { - return 1; /* print formatted character */ + return 1; /* print formatted character */ } /* if */ break; case FMT_WIDTH: @@ -743,14 +921,14 @@ static int formatstate(TCHAR c,int *state,TCHAR *sign,TCHAR *decpoint,int *width *digits=0; *state=FMT_DECIM; } else { - return 1; /* print formatted character */ + return 1; /* print formatted character */ } /* if */ break; case FMT_DECIM: if (c>=__T('0') && c<=__T('9')) { *digits=*digits*10+(int)(c-__T('0')); } else { - return 1; /* print formatted character */ + return 1; /* print formatted character */ } /* if */ break; } /* switch */ @@ -798,11 +976,10 @@ int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info) if ((ucell)*cstr>UNPACKEDMAX) { int j=sizeof(cell)-sizeof(char); - char c; /* the string is packed */ i=0; for ( ; ; ) { - c=(char)((ucell)cstr[i] >> 8*j); + char c=(char)((ucell)cstr[i] >> 8*j); if (c==0) break; if (skip>0) { @@ -846,28 +1023,29 @@ int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info) /* check whether this is a packed string */ if ((ucell)*cstr>UNPACKEDMAX) { int j=sizeof(cell)-sizeof(char); - char c; /* the string is packed */ i=0; for ( ; ; ) { - c=(char)((ucell)cstr[i] >> 8*j); + char c=(char)((ucell)cstr[i] >> 8*j); if (c==0) break; switch (formatstate(c,&fmtstate,&sign,&decpoint,&width,&digits,&filler)) { case -1: f_putchar(user,c); break; - case 1: - assert(info!=NULL && info->params!=NULL); - paramidx+=dochar(amx,c,info->params[paramidx],sign,decpoint,width,digits,filler, - f_putstr,f_putchar,user); - fmtstate=FMT_NONE; - break; case 0: + break; + case 1: assert(info!=NULL && info->params!=NULL); if (paramidx>=info->numparams) /* insufficient parameters passed */ amx_RaiseError(amx, AMX_ERR_NATIVE); + else + paramidx+=dochar(amx,c,info->params[paramidx],sign,decpoint,width,digits,filler, + f_putstr,f_putchar,user); + fmtstate=FMT_NONE; break; + default: + assert(0); } /* switch */ if (j==0) i++; @@ -880,17 +1058,19 @@ int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info) case -1: f_putchar(user,(TCHAR)cstr[i]); break; - case 1: - assert(info!=NULL && info->params!=NULL); - paramidx+=dochar(amx,(TCHAR)cstr[i],info->params[paramidx],sign,decpoint,width,digits,filler, - f_putstr,f_putchar,user); - fmtstate=FMT_NONE; - break; case 0: + break; + case 1: assert(info!=NULL && info->params!=NULL); if (paramidx>=info->numparams) /* insufficient parameters passed */ amx_RaiseError(amx, AMX_ERR_NATIVE); + else + paramidx+=dochar(amx,(TCHAR)cstr[i],info->params[paramidx],sign,decpoint,width,digits,filler, + f_putstr,f_putchar,user); + fmtstate=FMT_NONE; break; + default: + assert(0); } /* switch */ } /* for */ } /* if */ @@ -914,7 +1094,7 @@ static cell AMX_NATIVE_CALL n_print(AMX *amx,const cell *params) info.length= ((size_t)params[0]>=3*sizeof(cell)) ? (int)(params[3]-info.skip) : INT_MAX; CreateConsole(); - amx_GetAddr(amx,params[1],&cstr); + cstr=amx_Address(amx,params[1]); amx_printstring(amx,cstr,&info); amx_fflush(); return 0; @@ -931,7 +1111,7 @@ static cell AMX_NATIVE_CALL n_print(AMX *amx,const cell *params) /* set the new colours */ oldcolours=amx_setattr((int)params[2],(int)params[3],(int)params[4]); - amx_GetAddr(amx,params[1],&cstr); + cstr=amx_Address(amx,params[1]); amx_printstring(amx,cstr,NULL); /* reset the colours */ @@ -953,7 +1133,7 @@ static cell AMX_NATIVE_CALL n_printf(AMX *amx,const cell *params) info.length=INT_MAX; CreateConsole(); - amx_GetAddr(amx,params[1],&cstr); + cstr=amx_Address(amx,params[1]); amx_printstring(amx,cstr,&info); amx_fflush(); return 0; @@ -982,18 +1162,20 @@ static cell AMX_NATIVE_CALL n_getchar(AMX *amx,const cell *params) static cell AMX_NATIVE_CALL n_getstring(AMX *amx,const cell *params) { int c,chars,max; - TCHAR *str; cell *cptr; + (void)amx; CreateConsole(); - max=(int)params[2]; - if (max<=0) - return 0; - chars=0; - - str=(TCHAR *)alloca(max*sizeof(TCHAR)); - if (str!=NULL) { + max=(int)params[2]; + if (max>0) { + #if __STDC_VERSION__ >= 199901L + TCHAR str[max]; /* use C99 feature if available */ + #else + TCHAR *str=(TCHAR *)alloca(max*sizeof(TCHAR)); + if (str==NULL) + return chars; + #endif c=amx_getch(); while (c!=EOF && c!=EOL_CHAR && chars1,max); } /* if */ @@ -1052,13 +1234,14 @@ static int inlist(AMX *amx,int c,const cell *params,int num) { int i, key; + (void)amx; for (i=0; i1 || chars>0 && sign>0) @@ -1107,6 +1290,10 @@ static cell AMX_NATIVE_CALL n_getvalue(AMX *amx,const cell *params) acceptchar(c,&chars); break; } /* if */ + #if EOL_CHAR!='\r' + if (c=='\r') + c=EOL_CHAR; + #endif /* get value */ d=base; /* by default, do not accept the character */ @@ -1153,8 +1340,7 @@ static cell AMX_NATIVE_CALL n_gotoxy(AMX *amx,const cell *params) { (void)amx; CreateConsole(); - amx_gotoxy((int)params[1],(int)params[2]); - return 0; + return amx_gotoxy((int)params[1],(int)params[2]); } static cell AMX_NATIVE_CALL n_wherexy(AMX *amx,const cell *params) @@ -1162,12 +1348,13 @@ static cell AMX_NATIVE_CALL n_wherexy(AMX *amx,const cell *params) cell *px,*py; int x,y; + (void)amx; CreateConsole(); amx_wherexy(&x,&y); - amx_GetAddr(amx,params[1],&px); - amx_GetAddr(amx,params[2],&py); - if (px!=NULL) *px=x; - if (py!=NULL) *py=y; + px=amx_Address(amx,params[1]); + py=amx_Address(amx,params[2]); + *px=x; + *py=y; return 0; } @@ -1183,8 +1370,7 @@ static cell AMX_NATIVE_CALL n_consctrl(AMX *amx,const cell *params) { (void)amx; CreateConsole(); - (void)amx_termctl((int)params[1],(int)params[2]); - return 0; + return amx_termctl((int)params[1],(int)params[2]); } static cell AMX_NATIVE_CALL n_console(AMX *amx,const cell *params) @@ -1209,8 +1395,8 @@ static int AMXAPI amx_ConsoleIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int)) if (PrevIdle != NULL) PrevIdle(amx, Exec); - if (kbhit()) { - key = getch(); + if (amx_kbhit()) { + key = amx_getch(); amx_Push(amx, key); err = Exec(amx, NULL, idxKeyPressed); while (err == AMX_ERR_SLEEP) @@ -1240,7 +1426,7 @@ const AMX_NATIVE_INFO console_Natives[] = { { NULL, NULL } /* terminator */ }; -int AMXEXPORT amx_ConsoleInit(AMX *amx) +int AMXEXPORT AMXAPI amx_ConsoleInit(AMX *amx) { #if !defined AMXCONSOLE_NOIDLE /* see whether there is an @keypressed() function */ @@ -1254,7 +1440,7 @@ int AMXEXPORT amx_ConsoleInit(AMX *amx) return amx_Register(amx, console_Natives, -1); } -int AMXEXPORT amx_ConsoleCleanup(AMX *amx) +int AMXEXPORT AMXAPI amx_ConsoleCleanup(AMX *amx) { (void)amx; #if !defined AMXCONSOLE_NOIDLE diff --git a/source/amx/amxcore.c b/source/amx/amxcore.c index e1c14a21..0c83f064 100644 --- a/source/amx/amxcore.c +++ b/source/amx/amxcore.c @@ -1,24 +1,20 @@ /* Core module for the Pawn AMX * - * Copyright (c) ITB CompuPhase, 1997-2006 + * Copyright (c) ITB CompuPhase, 1997-2016 * - * 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. + * 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 * - * 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: + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * 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: amxcore.c 3657 2006-10-24 20:09:50Z thiadmer $ + * Version: $Id: amxcore.c 5504 2016-05-15 13:42:30Z $ */ #if defined _UNICODE || defined __UNICODE__ || defined UNICODE # if !defined UNICODE /* for Windows */ @@ -33,7 +29,14 @@ #include #include #include -#include "amx.h" +#include "osdefs.h" +#if defined __ECOS__ + /* eCos puts include files in cyg/package_name */ + #include + #define stricmp(a,b) strcasecmp(a,b) +#else + #include "amx.h" +#endif #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows #include #endif @@ -195,18 +198,10 @@ static cell AMX_NATIVE_CALL funcidx(AMX *amx,const cell *params) { char name[64]; cell *cstr; - int index,err,len; - - amx_GetAddr(amx,params[1],&cstr); - - /* verify string length */ - amx_StrLen(cstr,&len); - if (len>=64) { - amx_RaiseError(amx,AMX_ERR_NATIVE); - return 0; - } /* if */ + int index,err; - amx_GetString(name,cstr,0,UNLIMITED); + cstr=amx_Address(amx,params[1]); + amx_GetString(name,cstr,0,sizeof name); err=amx_FindPublic(amx,name,&index); if (err!=AMX_ERR_NONE) index=-1; /* this is not considered a fatal error */ @@ -276,9 +271,9 @@ static cell AMX_NATIVE_CALL swapchars(AMX *amx,const cell *params) static cell AMX_NATIVE_CALL core_tolower(AMX *amx,const cell *params) { (void)amx; - #if defined __WIN32__ || defined _WIN32 || defined WIN32 + #if (defined __WIN32__ || defined _WIN32 || defined WIN32) && !defined _WIN64 return (cell)CharLower((LPTSTR)params[1]); - #elif defined _Windows + #elif defined _Windows && !defined _WIN64 return (cell)AnsiLower((LPSTR)params[1]); #else if ((unsigned)(params[1]-'A')<26u) @@ -290,9 +285,9 @@ static cell AMX_NATIVE_CALL core_tolower(AMX *amx,const cell *params) static cell AMX_NATIVE_CALL core_toupper(AMX *amx,const cell *params) { (void)amx; - #if defined __WIN32__ || defined _WIN32 || defined WIN32 + #if (defined __WIN32__ || defined _WIN32 || defined WIN32) && !defined _WIN64 return (cell)CharUpper((LPTSTR)params[1]); - #elif defined _Windows + #elif defined _Windows && !defined _WIN64 return (cell)AnsiUpper((LPSTR)params[1]); #else if ((unsigned)(params[1]-'a')<26u) @@ -333,44 +328,31 @@ static char *MakePackedString(cell *cptr) amx_StrLen(cptr,&len); dest=(char *)malloc(len+sizeof(cell)); - amx_GetString(dest,cptr,0,UNLIMITED); + amx_GetString(dest,cptr,0,len+sizeof(cell)); return dest; } -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; -} - +/* getproperty(id=0, const name[]="", value=cellmin, string[]="", size=sizeof string) */ static cell AMX_NATIVE_CALL getproperty(AMX *amx,const cell *params) { cell *cstr; char *name; proplist *item; - amx_GetAddr(amx,params[2],&cstr); + (void)amx; + cstr=amx_Address(amx,params[2]); name=MakePackedString(cstr); item=list_finditem(&proproot,params[1],name,params[3],NULL); /* if list_finditem() found the value, store the name */ if (item!=NULL && item->value==params[3] && strlen(name)==0) { - int needed=(strlen(item->name)+sizeof(cell)-1)/sizeof(cell); /* # of cells needed */ - if (verify_addr(amx,(cell)(params[4]+needed))!=AMX_ERR_NONE) { - free(name); - return 0; - } /* if */ - amx_GetAddr(amx,params[4],&cstr); - amx_SetString(cstr,item->name,1,0,UNLIMITED); + cstr=amx_Address(amx,params[4]); + amx_SetString(cstr,item->name,1,0,params[5]); } /* if */ free(name); return (item!=NULL) ? item->value : 0; } +/* setproperty(id=0, const name[]="", value=cellmin, const string[]="") */ static cell AMX_NATIVE_CALL setproperty(AMX *amx,const cell *params) { cell prev=0; @@ -378,7 +360,7 @@ static cell AMX_NATIVE_CALL setproperty(AMX *amx,const cell *params) char *name; proplist *item; - amx_GetAddr(amx,params[2],&cstr); + cstr=amx_Address(amx,params[2]); name=MakePackedString(cstr); item=list_finditem(&proproot,params[1],name,params[3],NULL); if (item==NULL) @@ -389,7 +371,7 @@ static cell AMX_NATIVE_CALL setproperty(AMX *amx,const cell *params) prev=item->value; if (strlen(name)==0) { free(name); - amx_GetAddr(amx,params[4],&cstr); + cstr=amx_Address(amx,params[4]); name=MakePackedString(cstr); } /* if */ list_setitem(item,params[1],name,params[3]); @@ -398,6 +380,7 @@ static cell AMX_NATIVE_CALL setproperty(AMX *amx,const cell *params) return prev; } +/* deleteproperty(id=0, const name[]="", value=cellmin) */ static cell AMX_NATIVE_CALL delproperty(AMX *amx,const cell *params) { cell prev=0; @@ -405,7 +388,8 @@ static cell AMX_NATIVE_CALL delproperty(AMX *amx,const cell *params) char *name; proplist *item,*pred; - amx_GetAddr(amx,params[2],&cstr); + (void)amx; + cstr=amx_Address(amx,params[2]); name=MakePackedString(cstr); item=list_finditem(&proproot,params[1],name,params[3],&pred); if (item!=NULL) { @@ -416,13 +400,15 @@ static cell AMX_NATIVE_CALL delproperty(AMX *amx,const cell *params) return prev; } +/* existproperty(id=0, const name[]="", value=cellmin) */ static cell AMX_NATIVE_CALL existproperty(AMX *amx,const cell *params) { cell *cstr; char *name; proplist *item; - amx_GetAddr(amx,params[2],&cstr); + (void)amx; + cstr=amx_Address(amx,params[2]); name=MakePackedString(cstr); item=list_finditem(&proproot,params[1],name,params[3],NULL); free(name); @@ -498,12 +484,12 @@ const AMX_NATIVE_INFO core_Natives[] = { { NULL, NULL } /* terminator */ }; -int AMXEXPORT amx_CoreInit(AMX *amx) +int AMXEXPORT AMXAPI amx_CoreInit(AMX *amx) { return amx_Register(amx, core_Natives, -1); } -int AMXEXPORT amx_CoreCleanup(AMX *amx) +int AMXEXPORT AMXAPI amx_CoreCleanup(AMX *amx) { (void)amx; #if !defined AMX_NOPROPLIST diff --git a/source/amx/amxfile.c b/source/amx/amxfile.c index 4dab381b..012ddf56 100644 --- a/source/amx/amxfile.c +++ b/source/amx/amxfile.c @@ -1,24 +1,20 @@ /* Text file I/O module for the Pawn Abstract Machine * - * Copyright (c) ITB CompuPhase, 2003-2006 + * Copyright (c) ITB CompuPhase, 2003-2016 * - * 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. + * 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 * - * 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: + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * 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: amxfile.c 3660 2006-11-05 13:05:09Z thiadmer $ + * Version: $Id: amxfile.c 5588 2016-10-25 11:13:28Z $ */ #if defined _UNICODE || defined __UNICODE__ || defined UNICODE # if !defined UNICODE /* for Windows */ @@ -29,54 +25,117 @@ # endif #endif +#include #include #include #include #include -#include -#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ - #include +#include +#include +#include +#include "osdefs.h" +#if (defined __linux || defined __linux__) && !defined __LINUX__ + #define __LINUX__ +#endif +#if defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#endif +#if defined __BORLANDC__ + #include +#endif +#if defined __BORLANDC__ || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS || defined __APPLE__ + #include +#else + #include +#endif +#if defined __WIN32__ || defined __MSDOS__ #include #endif -#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows - #include +#if defined __WATCOMC__ || defined _MSC_VER + #include #endif -#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS +#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS || defined __APPLE__ #include +#else + #include +#endif +#if defined __GNUC__ || defined __clang__ + #include #endif -#include "osdefs.h" #include "amx.h" +#if defined __WIN32__ || defined _Windows + #include +#endif #include "fpattern.c" #if !defined AMXFILE_VAR #define AMXFILE_VAR "AMXFILE" -#elif AMXFILE_VAR=="" - #undef AMXFILE_VAR +#endif + +#if !defined sizearray + #define sizearray(a) (sizeof(a)/sizeof((a)[0])) #endif #if defined _UNICODE -# include + #include #elif !defined __T typedef char TCHAR; -# define __T(string) string -# define _tfopen fopen -# define _tgetenv getenv -# define _tfputs fputs -# define _tcscat strcat -# define _tcschr strchr -# define _tcscpy strcpy -# define _tcsdup strdup -# define _tcslen strlen -# define _tcsncpy strncpy -# define _tcspbrk strpbrk -# define _tcsrchr strrchr + #define __T(string) string + #define _tchmod chmod + #define _tcscat strcat + #define _tcschr strchr + #define _tcscmp strcmp + #define _tcscpy strcpy + #define _tcsdup strdup + #define _tcslen strlen + #define _tcsncpy strncpy + #define _tcsnicmp strnicmp + #define _tcspbrk strpbrk + #define _tcsrchr strrchr + #define _tcstol strtol + #define _tfopen fopen + #define _tfputs fputs + #define _tgetenv getenv + #define _tremove remove + #define _trename rename + #if defined __APPLE__ + #define _tmkdir mkdir + #define _trmdir rmdir + #define _tstat stat + #define _tutime utime + #else + #define _tmkdir _mkdir + #define _trmdir _rmdir + #define _tstat _stat + #define _tstat64 _stat64 + #define _tutime _utime + #endif + #if defined __APPLE__ + #define t_stat stat + #elif defined __WIN32__ + #if defined __WATCOMC__ + #define t_stat __stat + #else + #define t_stat _stat + #endif + #else + #define t_stat stat + #endif #endif - -#if !defined UNUSED_PARAM - #define UNUSED_PARAM(p) ((void)(p)) +#if !defined t_stat + #if defined _tstat + #define t_stat _tstat + #else + #define t_stat stat + #endif +#endif +#if !defined S_ISDIR + #define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0) #endif +#include "minIni.c" + enum filemode { io_read, /* file must exist */ io_write, /* creates a new file */ @@ -303,13 +362,13 @@ static size_t fgets_char(FILE *fp, char *string, size_t max) return index; } -#if defined __WIN32__ || defined _WIN32 || defined WIN32 +#if (defined __WIN32__ || defined _WIN32 || defined WIN32) && _MSC_VER < 1500 #if defined _UNICODE wchar_t *_wgetenv(wchar_t *name) { static wchar_t buffer[_MAX_PATH]; buffer[0]=L'\0'; - GetEnvironmentVariable(name,buffer,sizeof buffer/sizeof(wchar_t)); + GetEnvironmentVariable(name,buffer,sizearray(buffer)); return buffer[0]!=L'\0' ? buffer : NULL; } #else @@ -317,7 +376,7 @@ char *getenv(const char *name) { static char buffer[_MAX_PATH]; buffer[0]='\0'; - GetEnvironmentVariable(name,buffer,sizeof buffer); + GetEnvironmentVariable(name,buffer,sizearray(buffer)); return buffer[0]!='\0' ? buffer : NULL; } #endif @@ -428,6 +487,7 @@ static cell AMX_NATIVE_CALL n_fopen(AMX *amx, const cell *params) TCHAR *name,fullname[_MAX_PATH]; FILE *f = NULL; + (void)amx; altattrib=NULL; switch (params[2] & 0x7fff) { case io_read: @@ -449,7 +509,7 @@ static cell AMX_NATIVE_CALL n_fopen(AMX *amx, const cell *params) /* get the filename */ amx_StrParam(amx,params[1],name); - if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL) { + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { f=_tfopen(fullname,attrib); if (f==NULL && altattrib!=NULL) f=_tfopen(fullname,altattrib); @@ -460,19 +520,20 @@ static cell AMX_NATIVE_CALL n_fopen(AMX *amx, const cell *params) /* fclose(File: handle) */ static cell AMX_NATIVE_CALL n_fclose(AMX *amx, const cell *params) { - UNUSED_PARAM(amx); + (void)amx; return fclose((FILE*)params[1]) == 0; } /* fwrite(File: handle, const string[]) */ static cell AMX_NATIVE_CALL n_fwrite(AMX *amx, const cell *params) { - int r = 0; + size_t r = 0; cell *cptr; char *str; int len; - amx_GetAddr(amx,params[2],&cptr); + (void)amx; + cptr=amx_Address(amx,params[2]); amx_StrLen(cptr,&len); if (len==0) return 0; @@ -487,13 +548,14 @@ static cell AMX_NATIVE_CALL n_fwrite(AMX *amx, const cell *params) /* the string is unpacked, write it as UTF-8 */ r=fputs_cell((FILE*)params[1],cptr,1); } /* if */ - return r; + return (cell)r; } /* fread(File: handle, string[], size=sizeof string, bool:pack=false) */ static cell AMX_NATIVE_CALL n_fread(AMX *amx, const cell *params) { - int chars,max; + size_t chars; + int max; char *str; cell *cptr; @@ -503,7 +565,7 @@ static cell AMX_NATIVE_CALL n_fread(AMX *amx, const cell *params) if (params[4]) max*=sizeof(cell); - amx_GetAddr(amx,params[2],&cptr); + cptr=amx_Address(amx,params[2]); str=(char *)alloca(max); if (str==NULL || cptr==NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); @@ -513,15 +575,15 @@ static cell AMX_NATIVE_CALL n_fread(AMX *amx, const cell *params) if (params[4]) { /* store as packed string, read an ASCII/ANSI string */ chars=fgets_char((FILE*)params[1],str,max); - assert(chars> 8) ^ ulCRCTable[(ulCRC & 0xFF) ^ *sBuf++]; + + return ulCRC; +} + +/* filecrc(const name[]) */ +static cell AMX_NATIVE_CALL n_filecrc(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + FILE *fp; + unsigned char buffer[256]; + unsigned long ulCRC = 0xffffffff; + size_t numread; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL + && (fp=_tfopen(fullname,"rb"))!=NULL) + { + do { + numread=fread(buffer,sizeof(unsigned char),sizeof buffer,fp); + ulCRC=PartialCRC(ulCRC,buffer,(unsigned long)numread); + } while(numread==sizeof buffer); + fclose(fp); + } /* if */ + return(ulCRC ^ 0xffffffff); +} + + +const TCHAR default_ini_name[] = "config.ini"; + +/* readcfg(const filename[]="", const section[]="", const key[], value[], size=sizeof value, const defvalue[]="", bool:packed=false) */ +static cell AMX_NATIVE_CALL n_readcfg(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key,*defvalue; + TCHAR *buffer; + int size,result=0; + cell *cptr; + + size=(int)params[5]; + if (size<=0) + return 0; + if (params[7]) + size*=sizeof(cell); + + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + amx_StrParam(amx,params[3],key); + amx_StrParam(amx,params[6],defvalue); + + cptr=amx_Address(amx,params[4]); + buffer=(char *)alloca(size); + if (buffer==NULL || cptr==NULL) { + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + result=ini_gets(section,key,defvalue,buffer,size,fullname); + amx_SetString(cptr,buffer,params[7],0,size); + } /* if */ + return result; +} + +/* readcfgvalue(const filename[]="", const section[]="", const key[], defvalue=0) */ +static cell AMX_NATIVE_CALL n_readcfgvalue(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key; + long result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + amx_StrParam(amx,params[3],key); + result=ini_getl(section,key,(long)params[4],fullname); + } /* if */ + return result; +} + +/* writecfg(const filename[]="", const section[]="", const key[], const value[]) */ +static cell AMX_NATIVE_CALL n_writecfg(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key,*value; + int result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + amx_StrParam(amx,params[3],key); + amx_StrParam(amx,params[4],value); + result=ini_puts(section,key,value,fullname); + } /* if */ + return result; +} + +/* writecfgvalue(const filename[]="", const section[]="", const key[], value) */ +static cell AMX_NATIVE_CALL n_writecfgvalue(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key; + int result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + amx_StrParam(amx,params[3],key); + result=ini_putl(section,key,(long)params[4],fullname); + } /* if */ + return result; +} + +/* deletecfg(const filename[]="", const section[]="", const key[]="") */ +static cell AMX_NATIVE_CALL n_deletecfg(AMX *amx, const cell *params) +{ + TCHAR *name,fullname[_MAX_PATH]=""; + TCHAR *section,*key; + int result=0; + + (void)amx; + amx_StrParam(amx,params[1],name); + if (name!=NULL && *name=='\0') + name=(TCHAR*)default_ini_name; + if (name!=NULL && completename(fullname,name,sizearray(fullname))!=NULL) { + amx_StrParam(amx,params[2],section); + if (section!=NULL && *section=='\0') + section=NULL; + amx_StrParam(amx,params[3],key); + if (key!=NULL && *key=='\0') + key=NULL; + result=ini_puts(section,key,NULL,fullname); + } /* if */ + return result; +} #if defined __cplusplus extern "C" #endif AMX_NATIVE_INFO file_Natives[] = { - { "fopen", n_fopen }, - { "fclose", n_fclose }, - { "fwrite", n_fwrite }, - { "fread", n_fread }, - { "fputchar", n_fputchar }, - { "fgetchar", n_fgetchar }, - { "fblockwrite", n_fblockwrite }, - { "fblockread", n_fblockread }, - { "ftemp", n_ftemp }, - { "fseek", n_fseek }, - { "flength", n_flength }, - { "fremove", n_fremove }, - { "fexist", n_fexist }, - { "fmatch", n_fmatch }, + { "fopen", n_fopen }, + { "fclose", n_fclose }, + { "fwrite", n_fwrite }, + { "fread", n_fread }, + { "fputchar", n_fputchar }, + { "fgetchar", n_fgetchar }, + { "fblockwrite", n_fblockwrite }, + { "fblockread", n_fblockread }, + { "ftemp", n_ftemp }, + { "fseek", n_fseek }, + { "flength", n_flength }, + { "fremove", n_fremove }, + { "fcopy", n_fcopy }, + { "frename", n_frename }, + { "fexist", n_fexist }, + { "fmatch", n_fmatch }, + { "fstat", n_fstat }, + { "fattrib", n_fattrib }, + { "filecrc", n_filecrc }, + { "fcreatedir", n_fcreatedir }, + { "readcfg", n_readcfg }, + { "readcfgvalue", n_readcfgvalue }, + { "writecfg", n_writecfg }, + { "writecfgvalue",n_writecfgvalue }, + { "deletecfg", n_deletecfg }, { NULL, NULL } /* terminator */ }; -int AMXEXPORT amx_FileInit(AMX *amx) +int AMXEXPORT AMXAPI amx_FileInit(AMX *amx) { return amx_Register(amx, file_Natives, -1); } -int AMXEXPORT amx_FileCleanup(AMX *amx) +int AMXEXPORT AMXAPI amx_FileCleanup(AMX *amx) { - UNUSED_PARAM(amx); + (void)amx; return AMX_ERR_NONE; } diff --git a/source/amx/float.c b/source/amx/amxfloat.c similarity index 91% rename from source/amx/float.c rename to source/amx/amxfloat.c index 02cb4f65..d22ff623 100644 --- a/source/amx/float.c +++ b/source/amx/amxfloat.c @@ -44,7 +44,7 @@ static cell AMX_NATIVE_CALL n_float(AMX *amx,const cell *params) { /* * params[0] = number of bytes - * params[1] = long value to convert to a float + * params[1] = integer value to convert to a float */ REAL fValue; @@ -73,7 +73,7 @@ static cell AMX_NATIVE_CALL n_strfloat(AMX *amx,const cell *params) assert(params[0]/sizeof(cell)==1); /* Get the real address of the string. */ - amx_GetAddr(amx,params[1],&pString); + pString=amx_Address(amx,params[1]); /* Find out how long the string is in characters. */ amx_StrLen(pString, &nLen); @@ -162,20 +162,20 @@ static cell AMX_NATIVE_CALL n_floatround(AMX *amx,const cell *params) /* * params[0] = number of bytes * params[1] = float operand - * params[2] = Type of rounding (long) + * params[2] = Type of rounding (integer) */ REAL fA = amx_ctof(params[1]); (void)amx; switch (params[2]) { - case 1: /* round downwards (truncate) */ + case 1: /* round downwards */ fA = (REAL)(floor((double)fA)); break; case 2: /* round upwards */ fA = (REAL)(ceil((double)fA)); break; - case 3: /* round towards zero */ + case 3: /* round towards zero (truncate) */ if ( fA>=0.0 ) fA = (REAL)(floor((double)fA)); else @@ -186,7 +186,7 @@ static cell AMX_NATIVE_CALL n_floatround(AMX *amx,const cell *params) break; } - return (long)fA; + return (cell)fA; } /******************************************************************/ @@ -283,7 +283,7 @@ static cell AMX_NATIVE_CALL n_floatsin(AMX *amx,const cell *params) */ REAL fA = amx_ctof(params[1]); fA = ToRadians(fA, params[2]); - fA = sin(fA); + fA = (float)sin(fA); (void)amx; return amx_ftoc(fA); } @@ -298,7 +298,7 @@ static cell AMX_NATIVE_CALL n_floatcos(AMX *amx,const cell *params) */ REAL fA = amx_ctof(params[1]); fA = ToRadians(fA, params[2]); - fA = cos(fA); + fA = (float)cos(fA); (void)amx; return amx_ftoc(fA); } @@ -313,7 +313,7 @@ static cell AMX_NATIVE_CALL n_floattan(AMX *amx,const cell *params) */ REAL fA = amx_ctof(params[1]); fA = ToRadians(fA, params[2]); - fA = tan(fA); + fA = (float)tan(fA); (void)amx; return amx_ftoc(fA); } @@ -327,6 +327,22 @@ static cell AMX_NATIVE_CALL n_floatabs(AMX *amx,const cell *params) return amx_ftoc(fA); } +/******************************************************************/ +/* return the integer part of a real value, truncated + * Return integer part of float, truncated (same as floatround + * with mode 3) + */ +static cell AMX_NATIVE_CALL n_floatint(AMX *amx,const cell *params) +{ + REAL fA = amx_ctof(params[1]); + if ( fA>=0.0 ) + fA = (REAL)(floor((double)fA)); + else + fA = (REAL)(ceil((double)fA)); + (void)amx; + return (cell)fA; +} + #if defined __cplusplus extern "C" #endif @@ -347,15 +363,16 @@ const AMX_NATIVE_INFO float_Natives[] = { { "floatcos", n_floatcos }, { "floattan", n_floattan }, { "floatabs", n_floatabs }, + { "floatint", n_floatint }, // also add user-defined operator "=" { NULL, NULL } /* terminator */ }; -int AMXEXPORT amx_FloatInit(AMX *amx) +int AMXEXPORT AMXAPI amx_FloatInit(AMX *amx) { return amx_Register(amx,float_Natives,-1); } -int AMXEXPORT amx_FloatCleanup(AMX *amx) +int AMXEXPORT AMXAPI amx_FloatCleanup(AMX *amx) { (void)amx; return AMX_ERR_NONE; diff --git a/source/amx/amxstring.c b/source/amx/amxstring.c index 8651e602..45927dfa 100644 --- a/source/amx/amxstring.c +++ b/source/amx/amxstring.c @@ -1,24 +1,20 @@ /* String functions for the Pawn Abstract Machine * - * Copyright (c) ITB CompuPhase, 2005-2006 + * Copyright (c) ITB CompuPhase, 2005-2016 * - * 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. + * 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 * - * 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: + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * 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 3656 2006-10-24 07:20:26Z thiadmer $ + * Version: $Id: amxstring.c 5514 2016-05-20 14:26:51Z $ */ #include #include @@ -51,11 +47,14 @@ #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 + * len the number of characters (bytes) to copy, excluding the zero terminator * offs the offset in dest, in characters (bytes) */ static int amx_StrPack(cell *dest,cell *source,int len,int offs) @@ -126,6 +125,10 @@ static int amx_StrPack(cell *dest,cell *source,int len,int offs) return AMX_ERR_NONE; } +/* dest the destination buffer, this must be aligned to a cell edge + * source the source buffer, it must point to the start of a cell + * len the number of cells to copy, excluding the zero terminator + */ static int amx_StrUnpack(cell *dest,cell *source,int len) { /* len excludes the terminating '\0' byte */ @@ -179,17 +182,6 @@ static cell extractchar(cell *string,int index,int mklower) 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,const cell *params) @@ -197,8 +189,9 @@ static cell AMX_NATIVE_CALL n_strlen(AMX *amx,const cell *params) cell *cptr; int len = 0; - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - amx_StrLen(cptr,&len); + (void)(amx); + cptr=amx_Address(amx,params[1]); + amx_StrLen(cptr,&len); return len; } @@ -207,21 +200,14 @@ static cell AMX_NATIVE_CALL n_strlen(AMX *amx,const cell *params) static cell AMX_NATIVE_CALL n_strpack(AMX *amx,const cell *params) { cell *cdest,*csrc; - int len,needed,err; - size_t lastaddr; + int len,err; - /* calculate number of cells needed for (packed) destination */ - amx_GetAddr(amx,params[2],&csrc); + csrc=amx_Address(amx,params[2]); 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); + cdest=amx_Address(amx,params[1]); err=amx_StrPack(cdest,csrc,len,0); if (err!=AMX_ERR_NONE) return amx_RaiseError(amx,err); @@ -235,19 +221,14 @@ static cell AMX_NATIVE_CALL n_strunpack(AMX *amx,const 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); + csrc=amx_Address(amx,params[2]); 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); + cdest=amx_Address(amx,params[1]); err=amx_StrUnpack(cdest,csrc,len); if (err!=AMX_ERR_NONE) return amx_RaiseError(amx,err); @@ -262,29 +243,22 @@ static cell AMX_NATIVE_CALL n_strunpack(AMX *amx,const cell *params) static cell AMX_NATIVE_CALL n_strcat(AMX *amx,const cell *params) { cell *cdest,*csrc; - int len,len2,needed; + int len,len2; 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); + 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; - 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); @@ -299,6 +273,37 @@ static cell AMX_NATIVE_CALL n_strcat(AMX *amx,const cell *params) 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; @@ -327,8 +332,9 @@ static cell AMX_NATIVE_CALL n_strcmp(AMX *amx,const cell *params) int len1,len2,len; cell result; - amx_GetAddr(amx,params[1],&cstr1); - amx_GetAddr(amx,params[2],&cstr2); + (void)(amx); + cstr1=amx_Address(amx,params[1]); + cstr2=amx_Address(amx,params[2]); /* get the maximum length to compare */ amx_StrLen(cstr1,&len1); @@ -338,12 +344,16 @@ static cell AMX_NATIVE_CALL n_strcmp(AMX *amx,const cell *params) 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; + if (len==0) { + if (params[4]==0 || len1==len2) + result=0; + else + result=(len1UNPACKEDMAX) { 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 */ @@ -458,8 +462,9 @@ static cell AMX_NATIVE_CALL n_strdel(AMX *amx,const cell *params) unsigned char *ptr; unsigned char c; + (void)(amx); /* calculate number of cells needed for (packed) destination */ - amx_GetAddr(amx,params[1],&cstr); + cstr=amx_Address(amx,params[1]); amx_StrLen(cstr,&length); index=(int)params[2]; offs=(int)params[3]-index; @@ -494,34 +499,28 @@ static cell AMX_NATIVE_CALL n_strdel(AMX *amx,const cell *params) static cell AMX_NATIVE_CALL n_strins(AMX *amx,const cell *params) { cell *cstr,*csub; - int index,lenstr,lensub,count; - int needed; - size_t lastaddr; + int index,lenstr,lensub,maxlen,count; 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); + 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) + maxlen=(int)params[4]; + if ((ucell)*cstr>UNPACKEDMAX) + maxlen*=sizeof(cell); + maxlen-=1; + if (index>lenstr || index>maxlen) 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) { + if (lenstr==0) { /* current string is empty (and the insertion point is zero), just make a copy */ assert(index==0); + if (lensub>maxlen) + lensub=maxlen; if ((ucell)*csub>UNPACKEDMAX) amx_StrPack(cstr,csub,lensub,0); else @@ -529,9 +528,12 @@ static cell AMX_NATIVE_CALL n_strins(AMX *amx,const cell *params) return 1; } /* if */ - if (((ucell)*cstr>UNPACKEDMAX)) { + lenstr+=lensub; /* length after insertion */ + if (lenstr>=maxlen) + lenstr=maxlen-1; + if ((ucell)*cstr>UNPACKEDMAX) { /* make room for the new characters */ - for (count=lenstr+lensub; count>index; count--) { + for (count=lenstr; count>index; count--) { ptr=packedptr(cstr,count-lensub); c=*ptr; ptr=packedptr(cstr,count); @@ -545,7 +547,7 @@ static cell AMX_NATIVE_CALL n_strins(AMX *amx,const cell *params) } /* for */ } else { /* make room for the new characters */ - for (count=lenstr+lensub; count>index; count--) + for (count=lenstr; count>index; count--) cstr[count]=cstr[count-lensub]; /* copy in the new characters */ for (count=0; count=2*sizeof(cell)) offset=params[2]; @@ -577,16 +580,22 @@ static cell AMX_NATIVE_CALL n_strval(AMX *amx,const cell *params) offset=len-1; /* skip a number of cells */ - while (offset>=(int)sizeof(cell)) { - cstr++; - offset-=sizeof(cell); - len-=sizeof(cell); - } /* while */ - - if (len>=(int)sizeof str) { - amx_RaiseError(amx,AMX_ERR_NATIVE); - return 0; + 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; @@ -612,12 +621,12 @@ static cell AMX_NATIVE_CALL n_strval(AMX *amx,const cell *params) static cell AMX_NATIVE_CALL n_valstr(AMX *amx,const cell *params) { TCHAR str[50]; - cell value,mult; + cell value,temp; cell *cstr; int len,result,negate=0; + (void)(amx); /* find out how many digits are needed */ - mult=10; len=1; value=params[2]; if (value<0) { @@ -625,10 +634,9 @@ static cell AMX_NATIVE_CALL n_valstr(AMX *amx,const cell *params) len++; value=-value; } /* if */ - while (value>=mult) { + for (temp=value; temp>=10; temp/=10) len++; - mult*=10; - } /* while */ + assert(len<=sizearray(str)); /* put in the string */ result=len; @@ -639,16 +647,16 @@ static cell AMX_NATIVE_CALL n_valstr(AMX *amx,const cell *params) } /* while */ if (negate) str[0]='-'; - amx_GetAddr(amx,params[1],&cstr); - amx_SetString(cstr,str,params[3],sizeof(TCHAR)>1,UNLIMITED); + 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_GetAddr(amx,params[1],&cstr); + cell *cstr=amx_Address(amx,params[1]); + (void)(amx); return *cstr>=UNPACKEDMAX; } @@ -727,13 +735,14 @@ static cell AMX_NATIVE_CALL n_uudecode(AMX *amx,const cell *params) int len; size_t size; + (void)(amx); /* get the source */ - amx_GetAddr(amx,params[2],&cstr); + cstr=amx_Address(amx,params[2]); amx_GetString(src,cstr,0,sizeof src); /* decode */ len=uudecode(dst,src); /* store */ - amx_GetAddr(amx,params[1],&cstr); + cstr=amx_Address(amx,params[1]); size=len; if (size>params[3]*sizeof(cell)) size=params[3]*sizeof(cell); @@ -754,12 +763,14 @@ static cell AMX_NATIVE_CALL n_uuencode(AMX *amx,const cell *params) unsigned char src[BITMASK+2]; char dst[BITMASK+BITMASK/3+2]; + (void)(amx); /* get the source */ - amx_GetAddr(amx,params[2],&cstr); + 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) { - amx_GetAddr(amx,params[1],&cstr); + cstr=amx_Address(amx,params[1]); *cstr=0; } /* if */ return 0; @@ -768,11 +779,142 @@ static cell AMX_NATIVE_CALL n_uuencode(AMX *amx,const cell *params) assert(strlen(dst)+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' && destlenlength) { /* 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" @@ -785,10 +927,11 @@ 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; - amx_GetAddr(amx,params[1],&cdest); - amx_GetAddr(amx,params[2],&csrc); + 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]); @@ -805,7 +948,7 @@ static cell AMX_NATIVE_CALL n_memcpy(AMX *amx,const cell *params) static int str_putchar(void *dest,TCHAR ch) { - int len=_tcslen((TCHAR*)dest); + int len=(int)_tcslen((TCHAR*)dest); if (len1,(int)params[2]); return 1; #endif @@ -856,6 +999,7 @@ const AMX_NATIVE_INFO string_Natives[] = { { "memcpy", n_memcpy }, { "strcat", n_strcat }, { "strcmp", n_strcmp }, + { "strcopy", n_strcopy }, { "strdel", n_strdel }, { "strfind", n_strfind }, { "strformat", n_strformat }, @@ -867,16 +1011,18 @@ const AMX_NATIVE_INFO string_Natives[] = { { "strval", n_strval }, { "uudecode", n_uudecode }, { "uuencode", n_uuencode }, + { "urldecode", n_urldecode }, + { "urlencode", n_urlencode }, { "valstr", n_valstr }, { NULL, NULL } /* terminator */ }; -int AMXEXPORT amx_StringInit(AMX *amx) +int AMXEXPORT AMXAPI amx_StringInit(AMX *amx) { return amx_Register(amx, string_Natives, -1); } -int AMXEXPORT amx_StringCleanup(AMX *amx) +int AMXEXPORT AMXAPI amx_StringCleanup(AMX *amx) { (void)amx; return AMX_ERR_NONE; diff --git a/source/amx/amxstring.rc b/source/amx/amxstring.rc index 7f4a41e3..2b06eb19 100644 --- a/source/amx/amxstring.rc +++ b/source/amx/amxstring.rc @@ -10,15 +10,15 @@ * All strings MUST have an explicit \0. See the Windows SDK documentation * for details on version information and the VERSIONINFO structure. */ -#define VERSION 1 -#define REVISION 1 +#define VERSION 4 +#define REVISION 0 #define BUILD 0 -#define VERSIONSTR "1.1.0\0" +#define VERSIONSTR "4.0.0\0" #define VERSIONNAME "amxString.dll\0" #define VERSIONDESCRIPTION "Pawn AMX: String manipulation routines\0" #define VERSIONCOMPANYNAME "ITB CompuPhase\0" #define VERSIONPRODUCTNAME "amxString\0" -#define VERSIONCOPYRIGHT "Copyright \251 2005-2006 ITB CompuPhase\0" +#define VERSIONCOPYRIGHT "Copyright \251 2005-2016 ITB CompuPhase\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VERSION, REVISION, BUILD, 0 diff --git a/source/amx/amxtime.c b/source/amx/amxtime.c index 03408551..4fe79ce7 100644 --- a/source/amx/amxtime.c +++ b/source/amx/amxtime.c @@ -1,35 +1,44 @@ /* Date/time module for the Pawn Abstract Machine * - * Copyright (c) ITB CompuPhase, 2001-2006 + * Copyright (c) ITB CompuPhase, 2001-2016 * - * 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. + * 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 * - * 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: + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. + * 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: amxtime.c 3649 2006-10-12 13:13:57Z thiadmer $ + * Version: $Id: amxtime.c 5588 2016-10-25 11:13:28Z $ */ #include #include #include "amx.h" -#if defined __WIN32__ || defined _WIN32 +#if defined __WIN32__ || defined _WIN32 || defined _Windows #include #include #endif +#if defined __GNUC__ || defined __clang__ + #include +#endif +#if defined __clang__ + /* ignore this warning, because fixing the macro would make it depend on + two's-complement arithmetic */ + #pragma GCC diagnostic ignored "-Wshift-negative-value" +#endif #define CELLMIN (-1 << (8*sizeof(cell) - 1)) +#define SECONDS_PER_MINUTE 60 +#define SECONDS_PER_HOUR 3600 +#define SECONDS_PER_DAY 86400 +#define SECONDS_PER_YEAR 31556952 /* based on 365.2425 days per year */ + #if !defined CLOCKS_PER_SEC #define CLOCKS_PER_SEC CLK_TCK #endif @@ -65,52 +74,168 @@ static unsigned long gettimestamp(void) #if defined __WIN32__ || defined _WIN32 || defined WIN32 value=timeGetTime(); /* this value is already in milliseconds */ + #elif defined __linux || defined __linux__ || defined __LINUX__ || defined __APPLE__ + struct timeval tv; + gettimeofday(&tv, NULL); + value = ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); #else - value=(cell)clock(); - #if CLOCKS_PER_SEC!=1000 + value=clock(); + #if CLOCKS_PER_SEC<1000 /* convert to milliseconds */ - value=(cell)((1000L * (value+CLOCKS_PER_SEC/2)) / CLOCKS_PER_SEC); + value=(cell)((1000L * value) / CLOCKS_PER_SEC); + #elif CLOCKS_PER_SEC>1000 + /* convert to milliseconds */ + value=(cell)(value/(CLOCKS_PER_SEC/1000)); #endif #endif return value; } -/* settime(hour, minute, second) - * Always returns 0 - */ -static cell AMX_NATIVE_CALL n_settime(AMX *amx, const cell *params) +void stamp2datetime(unsigned long sec1970, + int *year, int *month, int *day, + int *hour, int *minute, int *second) +{ + int days, seconds; + + /* find the year */ + assert(year!=NULL); + for (*year = 1970; ; *year += 1) { + days = 365 + ((*year & 0x03) == 0); /* clumsy "leap-year" routine, fails for 2100 */ + seconds = days * SECONDS_PER_DAY; + if ((unsigned long)seconds > sec1970) + break; + sec1970 -= seconds; + } /* if */ + + /* find the month */ + assert(month!=NULL); + for (*month = 1; ; *month += 1) { + days = monthdays[*month - 1]; + seconds = days * SECONDS_PER_DAY; + if ((unsigned long)seconds > sec1970) + break; + sec1970 -= seconds; + } /* if */ + + /* find the day */ + assert(day!=NULL); + for (*day = 1; sec1970 >= SECONDS_PER_DAY; *day += 1) + sec1970 -= SECONDS_PER_DAY; + + /* find the hour */ + assert(hour!=NULL); + for (*hour = 0; sec1970 >= SECONDS_PER_HOUR; *hour += 1) + sec1970 -= SECONDS_PER_HOUR; + + /* find the minute */ + assert(minute!=NULL); + for (*minute = 0; sec1970 >= SECONDS_PER_MINUTE; *minute += 1) + sec1970 -= SECONDS_PER_MINUTE; + + /* remainder is the number of seconds */ + assert(second!=NULL); + *second = (int)sec1970; +} + +static void settime(cell hour,cell minute,cell second) { #if defined __WIN32__ || defined _WIN32 || defined WIN32 SYSTEMTIME systim; GetLocalTime(&systim); - if (params[1]!=CELLMIN) - systim.wHour=(WORD)wrap((int)params[1],0,23); - if (params[2]!=CELLMIN) - systim.wMinute=(WORD)wrap((int)params[2],0,59); - if (params[3]!=CELLMIN) - systim.wSecond=(WORD)wrap((int)params[3],0,59); + if (hour!=CELLMIN) + systim.wHour=(WORD)wrap((int)hour,0,23); + if (minute!=CELLMIN) + systim.wMinute=(WORD)wrap((int)minute,0,59); + if (second!=CELLMIN) + systim.wSecond=(WORD)wrap((int)second,0,59); SetLocalTime(&systim); #else /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you - * must have "root" permission to call stime() + * must have "root" permission to call stime(); many POSIX systems will + * have settimeofday() instead */ time_t sec1970; struct tm gtm; + #if defined __APPLE__ /* also valid for other POSIX systems */ + struct timeval tv; + #endif - (void)amx; time(&sec1970); gtm=*localtime(&sec1970); - if (params[1]!=CELLMIN) - gtm.tm_hour=wrap((int)params[1],0,23); - if (params[2]!=CELLMIN) - gtm.tm_min=wrap((int)params[2],0,59); - if (params[3]!=CELLMIN) - gtm.tm_sec=wrap((int)params[3],0,59); + if (hour!=CELLMIN) + gtm.tm_hour=wrap((int)hour,0,23); + if (minute!=CELLMIN) + gtm.tm_min=wrap((int)minute,0,59); + if (second!=CELLMIN) + gtm.tm_sec=wrap((int)second,0,59); sec1970=mktime(>m); - stime(&sec1970); + #if defined __APPLE__ /* also valid for other POSIX systems */ + tv.tv_sec = sec1970; + tv.tv_usec = 0; + settimeofday(&tv, 0); + #else + stime(&sec1970); + #endif #endif +} + +static void setdate(cell year,cell month,cell day) +{ + int maxday; + + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + SYSTEMTIME systim; + + GetLocalTime(&systim); + if (year!=CELLMIN) + systim.wYear=(WORD)wrap((int)year,1970,2099); + if (month!=CELLMIN) + systim.wMonth=(WORD)wrap((int)month,1,12); + maxday=monthdays[systim.wMonth - 1]; + if (systim.wMonth==2 && ((systim.wYear % 4)==0 && ((systim.wYear % 100)!=0 || (systim.wYear % 400)==0))) + maxday++; + if (day!=CELLMIN) + systim.wDay=(WORD)wrap((int)day,1,maxday); + SetLocalTime(&systim); + #else + /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you + * must have "root" permission to call stime(); many POSIX systems will + * have settimeofday() instead + */ + time_t sec1970; + struct tm gtm; + #if defined __APPLE__ /* also valid for other POSIX systems */ + struct timeval tv; + #endif + + time(&sec1970); + gtm=*localtime(&sec1970); + if (year!=CELLMIN) + gtm.tm_year=year-1900; + if (month!=CELLMIN) + gtm.tm_mon=month-1; + if (day!=CELLMIN) + gtm.tm_mday=day; + sec1970=mktime(>m); + #if defined __APPLE__ /* also valid for other POSIX systems */ + tv.tv_sec = sec1970; + tv.tv_usec = 0; + settimeofday(&tv, 0); + #else + stime(&sec1970); + #endif + #endif +} + + +/* settime(hour, minute, second) + * Always returns 0 + */ +static cell AMX_NATIVE_CALL n_settime(AMX *amx, const cell *params) +{ (void)amx; + settime(params[1],params[2],params[3]); return 0; } @@ -132,17 +257,17 @@ static cell AMX_NATIVE_CALL n_gettime(AMX *amx, const cell *params) * library; in that case gmtime() and localtime() return the same value */ gtm=*localtime(&sec1970); - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_hour; - if (amx_GetAddr(amx,params[2],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_min; - if (amx_GetAddr(amx,params[3],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_sec; + cptr=amx_Address(amx,params[1]); + *cptr=gtm.tm_hour; + cptr=amx_Address(amx,params[2]); + *cptr=gtm.tm_min; + cptr=amx_Address(amx,params[3]); + *cptr=gtm.tm_sec; /* the time() function returns the number of seconds since January 1 1970 * in Universal Coordinated Time (the successor to Greenwich Mean Time) */ - return sec1970; + return (cell)sec1970; } /* setdate(year, month, day) @@ -150,42 +275,8 @@ static cell AMX_NATIVE_CALL n_gettime(AMX *amx, const cell *params) */ static cell AMX_NATIVE_CALL n_setdate(AMX *amx, const cell *params) { - int maxday; - - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - SYSTEMTIME systim; - - GetLocalTime(&systim); - if (params[1]!=CELLMIN) - systim.wYear=(WORD)wrap((int)params[1],1970,2099); - if (params[2]!=CELLMIN) - systim.wMonth=(WORD)wrap((int)params[2],1,12); - maxday=monthdays[systim.wMonth - 1]; - if (systim.wMonth==2 && ((systim.wYear % 4)==0 && ((systim.wYear % 100)!=0 || (systim.wYear % 400)==0))) - maxday++; - if (params[3]!=CELLMIN) - systim.wDay=(WORD)wrap((int)params[3],1,maxday); - SetLocalTime(&systim); - #else - /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you - * must have "root" permission to call stime() - */ - time_t sec1970; - struct tm gtm; - - (void)amx; - time(&sec1970); - gtm=*localtime(&sec1970); - if (params[1]!=CELLMIN) - gtm.tm_year=params[1]-1900; - if (params[2]!=CELLMIN) - gtm.tm_mon=params[2]-1; - if (params[3]!=CELLMIN) - gtm.tm_mday=params[3]; - sec1970=mktime(>m); - stime(&sec1970); - #endif (void)amx; + setdate(params[1],params[2],params[3]); return 0; } @@ -204,12 +295,12 @@ static cell AMX_NATIVE_CALL n_getdate(AMX *amx, const cell *params) time(&sec1970); gtm=*localtime(&sec1970); - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_year+1900; - if (amx_GetAddr(amx,params[2],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_mon+1; - if (amx_GetAddr(amx,params[3],&cptr)==AMX_ERR_NONE) - *cptr=gtm.tm_mday; + cptr=amx_Address(amx,params[1]); + *cptr=gtm.tm_year+1900; + cptr=amx_Address(amx,params[2]); + *cptr=gtm.tm_mon+1; + cptr=amx_Address(amx,params[3]); + *cptr=gtm.tm_mday; return gtm.tm_yday+1; } @@ -225,12 +316,11 @@ static cell AMX_NATIVE_CALL n_tickcount(AMX *amx, const cell *params) assert(params[0]==(int)sizeof(cell)); INIT_TIMER(); + cptr=amx_Address(amx,params[1]); #if defined __WIN32__ || defined _WIN32 || defined WIN32 - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=1000; /* granularity = 1 ms */ + *cptr=1000; /* granularity = 1 ms */ #else - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=(cell)CLOCKS_PER_SEC; /* in Unix/Linux, this is often 100 */ + *cptr=(cell)CLOCKS_PER_SEC; /* in Unix/Linux, this is often 100 */ #endif return gettimestamp() & 0x7fffffff; } @@ -275,13 +365,59 @@ static cell AMX_NATIVE_CALL n_gettimer(AMX *amx, const cell *params) cell *cptr; assert(params[0]==(int)(2*sizeof(cell))); - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=timelimit; - if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) - *cptr=timerepeat; + cptr=amx_Address(amx,params[1]); + *cptr=timelimit; + cptr=amx_Address(amx,params[2]); + *cptr=timerepeat; return timelimit>0; } +/* settimestamp(seconds1970) sets the date and time from a single parameter: the + * number of seconds since 1 January 1970. + */ +static cell AMX_NATIVE_CALL n_settimestamp(AMX *amx, const cell *params) +{ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + int year, month, day, hour, minute, second; + + stamp2datetime(params[1], + &year, &month, &day, + &hour, &minute, &second); + setdate(year, month, day); + settime(hour, minute, second); + #else + /* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you + * must have "root" permission to call stime(); many POSIX systems will + * have settimeofday() instead + */ + #if defined __APPLE__ /* also valid for other POSIX systems */ + struct timeval tv; + tv.tv_sec = params[1]; + tv.tv_usec = 0; + settimeofday(&tv, 0); + #else + time_t sec1970=(time_t)params[1]; + stime(&sec1970); + #endif + #endif + (void)amx; + + return 0; +} + +/* cvttimestamp(seconds1970, &year, &month, &day, &hour, &minute, &second) + */ +static cell AMX_NATIVE_CALL n_cvttimestamp(AMX *amx, const cell *params) +{ + int year, month, day, hour, minute, second; + + (void)amx; + stamp2datetime(params[1], + &year, &month, &day, + &hour, &minute, &second); + return 0; +} + #if !defined AMXTIME_NOIDLE static AMX_IDLE PrevIdle = NULL; @@ -315,18 +451,20 @@ static int AMXAPI amx_TimeIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int)) extern "C" #endif const AMX_NATIVE_INFO time_Natives[] = { - { "gettime", n_gettime }, - { "settime", n_settime }, - { "getdate", n_getdate }, - { "setdate", n_setdate }, - { "tickcount", n_tickcount }, - { "settimer", n_settimer }, - { "gettimer", n_gettimer }, - { "delay", n_delay }, + { "gettime", n_gettime }, + { "settime", n_settime }, + { "getdate", n_getdate }, + { "setdate", n_setdate }, + { "tickcount", n_tickcount }, + { "settimer", n_settimer }, + { "gettimer", n_gettimer }, + { "delay", n_delay }, + { "settimestamp", n_settimestamp }, + { "cvttimestamp", n_cvttimestamp }, { NULL, NULL } /* terminator */ }; -int AMXEXPORT amx_TimeInit(AMX *amx) +int AMXEXPORT AMXAPI amx_TimeInit(AMX *amx) { #if !defined AMXTIME_NOIDLE /* see whether there is a @timer() function */ @@ -340,7 +478,7 @@ int AMXEXPORT amx_TimeInit(AMX *amx) return amx_Register(amx, time_Natives, -1); } -int AMXEXPORT amx_TimeCleanup(AMX *amx) +int AMXEXPORT AMXAPI amx_TimeCleanup(AMX *amx) { (void)amx; #if !defined AMXTIME_NOIDLE diff --git a/source/amx/amxtime.rc b/source/amx/amxtime.rc index 1d5a521f..0836e9d4 100644 --- a/source/amx/amxtime.rc +++ b/source/amx/amxtime.rc @@ -10,15 +10,15 @@ * All strings MUST have an explicit \0. See the Windows SDK documentation * for details on version information and the VERSIONINFO structure. */ -#define VERSION 1 -#define REVISION 1 +#define VERSION 4 +#define REVISION 0 #define BUILD 0 -#define VERSIONSTR "1.2.0\0" +#define VERSIONSTR "4.0.0\0" #define VERSIONNAME "amxTime.dll\0" #define VERSIONDESCRIPTION "Pawn AMX: time routines\0" #define VERSIONCOMPANYNAME "ITB CompuPhase\0" #define VERSIONPRODUCTNAME "amxTime\0" -#define VERSIONCOPYRIGHT "Copyright \251 2005-2006 ITB CompuPhase\0" +#define VERSIONCOPYRIGHT "Copyright \251 2005-2016 ITB CompuPhase\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VERSION, REVISION, BUILD, 0 diff --git a/source/amx/minGlue.h b/source/amx/minGlue.h new file mode 100644 index 00000000..36dd5e23 --- /dev/null +++ b/source/amx/minGlue.h @@ -0,0 +1,31 @@ +/* Glue functions for the minIni library, based on the C/C++ stdio library + * + * Or better said: this file contains macros that maps the function interface + * used by minIni to the standard C/C++ file I/O functions. + * + * By CompuPhase, 2008-2014 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + */ + +/* map required file I/O types and functions to the standard C library */ +#include + +#define INI_FILETYPE FILE* +#define ini_openread(filename,file) ((*(file) = fopen((filename),"rb")) != NULL) +#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"wb")) != NULL) +#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),"r+b")) != NULL) +#define ini_close(file) (fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (rename((source), (dest)) == 0) +#define ini_remove(filename) (remove(filename) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = ftell(*(file))) +#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0) + +/* for floating-point support, define additional types and functions */ +#define INI_REAL float +#define ini_ftoa(string,value) sprintf((string),"%f",(value)) +#define ini_atof(string) (INI_REAL)strtod((string),NULL) diff --git a/source/amx/minIni.c b/source/amx/minIni.c new file mode 100644 index 00000000..2ca0aaa7 --- /dev/null +++ b/source/amx/minIni.c @@ -0,0 +1,878 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * These routines are in part based on the article "Multiplatform .INI Files" + * by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal. + * + * Copyright (c) CompuPhase, 2008-2017 + * + * 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: minIni.c 5690 2017-06-08 14:04:08Z thiadmer $ + */ + +#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#define MININI_IMPLEMENTATION +#include "minIni.h" +#if defined NDEBUG + #if defined assert + #undef assert + #endif + #define assert(e) +#else + #include +#endif + +#if !defined __T || defined INI_ANSIONLY + #include + #include + #include + #define TCHAR char + #define __T(s) s + #define _tcscat strcat + #define _tcschr strchr + #define _tcscmp strcmp + #define _tcscpy strcpy + #define _tcsicmp stricmp + #define _tcslen strlen + #define _tcsncmp strncmp + #define _tcsnicmp strnicmp + #define _tcsrchr strrchr + #define _tcstol strtol + #define _tcstod strtod + #define _totupper toupper + #define _stprintf sprintf + #define _tfgets fgets + #define _tfputs fputs + #define _tfopen fopen + #define _tremove remove + #define _trename rename +#endif + +#if defined __linux || defined __linux__ + #define __LINUX__ +#elif defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#elif defined(_MSC_VER) + #pragma warning(disable: 4996) /* for Microsoft Visual C/C++ */ +#endif +#if !defined strnicmp && !defined PORTABLE_STRNICMP + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ + #define strnicmp strncasecmp + #endif +#endif +#if !defined _totupper + #include + #define _totupper toupper +#endif + +#if !defined INI_LINETERM + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ + #define INI_LINETERM __T("\n") + #else + #define INI_LINETERM __T("\r\n") + #endif +#endif +#if !defined INI_FILETYPE + #error Missing definition for INI_FILETYPE. +#endif + +#if !defined sizearray + #define sizearray(a) (sizeof(a) / sizeof((a)[0])) +#endif + +enum quote_option { + QUOTE_NONE, + QUOTE_ENQUOTE, + QUOTE_DEQUOTE, +}; + +#if defined PORTABLE_STRNICMP +int strnicmp(const TCHAR *s1, const TCHAR *s2, size_t n) +{ + while (n-- != 0 && (*s1 || *s2)) { + register int c1, c2; + c1 = *s1++; + if ('a' <= c1 && c1 <= 'z') + c1 += ('A' - 'a'); + c2 = *s2++; + if ('a' <= c2 && c2 <= 'z') + c2 += ('A' - 'a'); + if (c1 != c2) + return c1 - c2; + } /* while */ + return 0; +} +#endif /* PORTABLE_STRNICMP */ + +static TCHAR *skipleading(const TCHAR *str) +{ + assert(str != NULL); + while ('\0' < *str && *str <= ' ') + str++; + return (TCHAR *)str; +} + +static TCHAR *skiptrailing(const TCHAR *str, const TCHAR *base) +{ + assert(str != NULL); + assert(base != NULL); + while (str > base && '\0' < *(str-1) && *(str-1) <= ' ') + str--; + return (TCHAR *)str; +} + +static TCHAR *striptrailing(TCHAR *str) +{ + TCHAR *ptr = skiptrailing(_tcschr(str, '\0'), str); + assert(ptr != NULL); + *ptr = '\0'; + return str; +} + +static TCHAR *save_strncpy(TCHAR *dest, const TCHAR *source, size_t maxlen, enum quote_option option) +{ + size_t d, s; + + assert(maxlen>0); + assert(dest <= source || dest >= source + maxlen); + if (option == QUOTE_ENQUOTE && maxlen < 3) + option = QUOTE_NONE; /* cannot store two quotes and a terminating zero in less than 3 characters */ + + switch (option) { + case QUOTE_NONE: + for (d = 0; d < maxlen - 1 && source[d] != '\0'; d++) + dest[d] = source[d]; + assert(d < maxlen); + dest[d] = '\0'; + break; + case QUOTE_ENQUOTE: + d = 0; + dest[d++] = '"'; + for (s = 0; source[s] != '\0' && d < maxlen - 2; s++, d++) { + if (source[s] == '"') { + if (d >= maxlen - 3) + break; /* no space to store the escape character plus the one that follows it */ + dest[d++] = '\\'; + } /* if */ + dest[d] = source[s]; + } /* for */ + dest[d++] = '"'; + dest[d] = '\0'; + break; + case QUOTE_DEQUOTE: + for (d = s = 0; source[s] != '\0' && d < maxlen - 1; s++, d++) { + if ((source[s] == '"' || source[s] == '\\') && source[s + 1] == '"') + s++; + dest[d] = source[s]; + } /* for */ + dest[d] = '\0'; + break; + default: + assert(0); + } /* switch */ + + return dest; +} + +static TCHAR *cleanstring(TCHAR *string, enum quote_option *quotes) +{ + int isstring; + TCHAR *ep; + + assert(string != NULL); + assert(quotes != NULL); + + /* Remove a trailing comment */ + isstring = 0; + for (ep = string; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) { + if (*ep == '"') { + if (*(ep + 1) == '"') + ep++; /* skip "" (both quotes) */ + else + isstring = !isstring; /* single quote, toggle isstring */ + } else if (*ep == '\\' && *(ep + 1) == '"') { + ep++; /* skip \" (both quotes */ + } /* if */ + } /* for */ + assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#')); + *ep = '\0'; /* terminate at a comment */ + striptrailing(string); + /* Remove double quotes surrounding a value */ + *quotes = QUOTE_NONE; + if (*string == '"' && (ep = _tcschr(string, '\0')) != NULL && *(ep - 1) == '"') { + string++; + *--ep = '\0'; + *quotes = QUOTE_DEQUOTE; /* this is a string, so remove escaped characters */ + } /* if */ + return string; +} + +static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key, + int idxSection, int idxKey, TCHAR *Buffer, int BufferSize, + INI_FILEPOS *mark) +{ + TCHAR *sp, *ep; + int len, idx; + enum quote_option quotes; + TCHAR LocalBuffer[INI_BUFFERSIZE]; + + assert(fp != NULL); + /* Move through file 1 line at a time until a section is matched or EOF. If + * parameter Section is NULL, only look at keys above the first section. If + * idxSection is postive, copy the relevant section name. + */ + len = (Section != NULL) ? (int)_tcslen(Section) : 0; + if (len > 0 || idxSection >= 0) { + assert(Section != NULL); + idx = -1; + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp)) + return 0; + sp = skipleading(LocalBuffer); + ep = _tcsrchr(sp, ']'); + } while (*sp != '[' || ep == NULL || (((int)(ep-sp-1) != len || _tcsnicmp(sp+1,Section,len) != 0) && ++idx != idxSection)); + if (idxSection >= 0) { + if (idx == idxSection) { + assert(ep != NULL); + assert(*ep == ']'); + *ep = '\0'; + save_strncpy(Buffer, sp + 1, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more section found */ + } /* if */ + } /* if */ + + /* Now that the section has been found, find the entry. + * Stop searching upon leaving the section's area. + */ + assert(Key != NULL || idxKey >= 0); + len = (Key != NULL) ? (int)_tcslen(Key) : 0; + idx = -1; + do { + if (mark != NULL) + ini_tell(fp, mark); /* optionally keep the mark to the start of the line */ + if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[') + return 0; + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + } while (*sp == ';' || *sp == '#' || ep == NULL + || ((len == 0 || (int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey)); + if (idxKey >= 0) { + if (idx == idxKey) { + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + *ep = '\0'; + striptrailing(sp); + save_strncpy(Buffer, sp, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more key found (in this section) */ + } /* if */ + + /* Copy up to BufferSize chars to buffer */ + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + sp = skipleading(ep + 1); + sp = cleanstring(sp, "es); /* Remove a trailing comment */ + save_strncpy(Buffer, sp, BufferSize, quotes); + return 1; +} + +/** ini_gets() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue default string in the event of a failed read + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_gets(const TCHAR *Section, const TCHAR *Key, const TCHAR *DefValue, + TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || Key == NULL) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, Key, -1, -1, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + save_strncpy(Buffer, (DefValue != NULL) ? DefValue : __T(""), BufferSize, QUOTE_NONE); + return (int)_tcslen(Buffer); +} + +/** ini_getl() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue the default value in the event of a failed read + * \param Filename the name of the .ini file to read from + * + * \return the value located at Key + */ +long ini_getl(const TCHAR *Section, const TCHAR *Key, long DefValue, const TCHAR *Filename) +{ + TCHAR LocalBuffer[64]; + int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); + return (len == 0) ? DefValue + : ((len >= 2 && _totupper((int)LocalBuffer[1]) == 'X') ? _tcstol(LocalBuffer, NULL, 16) + : _tcstol(LocalBuffer, NULL, 10)); +} + +#if defined INI_REAL +/** ini_getf() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue the default value in the event of a failed read + * \param Filename the name of the .ini file to read from + * + * \return the value located at Key + */ +INI_REAL ini_getf(const TCHAR *Section, const TCHAR *Key, INI_REAL DefValue, const TCHAR *Filename) +{ + TCHAR LocalBuffer[64]; + int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); + return (len == 0) ? DefValue : ini_atof(LocalBuffer); +} +#endif + +/** ini_getbool() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue default value in the event of a failed read; it should + * zero (0) or one (1). + * \param Filename the name and full path of the .ini file to read from + * + * A true boolean is found if one of the following is matched: + * - A string starting with 'y' or 'Y' + * - A string starting with 't' or 'T' + * - A string starting with '1' + * + * A false boolean is found if one of the following is matched: + * - A string starting with 'n' or 'N' + * - A string starting with 'f' or 'F' + * - A string starting with '0' + * + * \return the true/false flag as interpreted at Key + */ +int ini_getbool(const TCHAR *Section, const TCHAR *Key, int DefValue, const TCHAR *Filename) +{ + TCHAR LocalBuffer[2] = __T(""); + int ret; + + ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); + LocalBuffer[0] = (TCHAR)_totupper((int)LocalBuffer[0]); + if (LocalBuffer[0] == 'Y' || LocalBuffer[0] == '1' || LocalBuffer[0] == 'T') + ret = 1; + else if (LocalBuffer[0] == 'N' || LocalBuffer[0] == '0' || LocalBuffer[0] == 'F') + ret = 0; + else + ret = DefValue; + + return(ret); +} + +/** ini_getsection() + * \param idx the zero-based sequence number of the section to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getsection(int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, NULL, NULL, idx, -1, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return (int)_tcslen(Buffer); +} + +/** ini_getkey() + * \param Section the name of the section to browse through, or NULL to + * browse through the keys outside any section + * \param idx the zero-based sequence number of the key to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getkey(const TCHAR *Section, int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, NULL, -1, idx, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return (int)_tcslen(Buffer); +} + + +#if !defined INI_NOBROWSE +/** ini_browse() + * \param Callback a pointer to a function that will be called for every + * setting in the INI file. + * \param UserData arbitrary data, which the function passes on the the + * \c Callback function + * \param Filename the name and full path of the .ini file to read from + * + * \return 1 on success, 0 on failure (INI file not found) + * + * \note The \c Callback function must return 1 to continue + * browsing through the INI file, or 0 to stop. Even when the + * callback stops the browsing, this function will return 1 + * (for success). + */ +int ini_browse(INI_CALLBACK Callback, const void *UserData, const TCHAR *Filename) +{ + TCHAR LocalBuffer[INI_BUFFERSIZE]; + int lenSec, lenKey; + enum quote_option quotes; + INI_FILETYPE fp; + + if (Callback == NULL) + return 0; + if (!ini_openread(Filename, &fp)) + return 0; + + LocalBuffer[0] = '\0'; /* copy an empty section in the buffer */ + lenSec = (int)_tcslen(LocalBuffer) + 1; + for ( ;; ) { + TCHAR *sp, *ep; + if (!ini_read(LocalBuffer + lenSec, INI_BUFFERSIZE - lenSec, &fp)) + break; + sp = skipleading(LocalBuffer + lenSec); + /* ignore empty strings and comments */ + if (*sp == '\0' || *sp == ';' || *sp == '#') + continue; + /* see whether we reached a new section */ + ep = _tcsrchr(sp, ']'); + if (*sp == '[' && ep != NULL) { + *ep = '\0'; + save_strncpy(LocalBuffer, sp + 1, INI_BUFFERSIZE, QUOTE_NONE); + lenSec = (int)_tcslen(LocalBuffer) + 1; + continue; + } /* if */ + /* not a new section, test for a key/value pair */ + ep = _tcschr(sp, '='); /* test for the equal sign or colon */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + if (ep == NULL) + continue; /* invalid line, ignore */ + *ep++ = '\0'; /* split the key from the value */ + striptrailing(sp); + save_strncpy(LocalBuffer + lenSec, sp, INI_BUFFERSIZE - lenSec, QUOTE_NONE); + lenKey = (int)_tcslen(LocalBuffer + lenSec) + 1; + /* clean up the value */ + sp = skipleading(ep); + sp = cleanstring(sp, "es); /* Remove a trailing comment */ + save_strncpy(LocalBuffer + lenSec + lenKey, sp, INI_BUFFERSIZE - lenSec - lenKey, quotes); + /* call the callback */ + if (!Callback(LocalBuffer, LocalBuffer + lenSec, LocalBuffer + lenSec + lenKey, UserData)) + break; + } /* for */ + + (void)ini_close(&fp); + return 1; +} +#endif /* INI_NOBROWSE */ + +#if ! defined INI_READONLY +static void ini_tempname(TCHAR *dest, const TCHAR *source, int maxlength) +{ + TCHAR *p; + + save_strncpy(dest, source, maxlength, QUOTE_NONE); + p = _tcsrchr(dest, '\0'); + assert(p != NULL); + *(p - 1) = '~'; +} + +static enum quote_option check_enquote(const TCHAR *Value) +{ + const TCHAR *p; + + /* run through the value, if it has trailing spaces, or '"', ';' or '#' + * characters, enquote it + */ + assert(Value != NULL); + for (p = Value; *p != '\0' && *p != '"' && *p != ';' && *p != '#'; p++) + /* nothing */; + return (*p != '\0' || (p > Value && *(p - 1) == ' ')) ? QUOTE_ENQUOTE : QUOTE_NONE; +} + +static void writesection(TCHAR *LocalBuffer, const TCHAR *Section, INI_FILETYPE *fp) +{ + if (Section != NULL && _tcslen(Section) > 0) { + TCHAR *p; + LocalBuffer[0] = '['; + save_strncpy(LocalBuffer + 1, Section, INI_BUFFERSIZE - 4, QUOTE_NONE); /* -1 for '[', -1 for ']', -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = ']'; + _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + if (fp != NULL) + (void)ini_write(LocalBuffer, fp); + } /* if */ +} + +static void writekey(TCHAR *LocalBuffer, const TCHAR *Key, const TCHAR *Value, INI_FILETYPE *fp) +{ + TCHAR *p; + enum quote_option option = check_enquote(Value); + save_strncpy(LocalBuffer, Key, INI_BUFFERSIZE - 3, QUOTE_NONE); /* -1 for '=', -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = '='; + save_strncpy(p, Value, INI_BUFFERSIZE - (p - LocalBuffer) - 2, option); /* -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + if (fp != NULL) + (void)ini_write(LocalBuffer, fp); +} + +static int cache_accum(const TCHAR *string, int *size, int max) +{ + int len = (int)_tcslen(string); + if (*size + len >= max) + return 0; + *size += len; + return 1; +} + +static int cache_flush(TCHAR *buffer, int *size, + INI_FILETYPE *rfp, INI_FILETYPE *wfp, INI_FILEPOS *mark) +{ + int terminator_len = (int)_tcslen(INI_LINETERM); + int pos = 0; + + (void)ini_seek(rfp, mark); + assert(buffer != NULL); + buffer[0] = '\0'; + assert(size != NULL); + assert(*size <= INI_BUFFERSIZE); + while (pos < *size) { + (void)ini_read(buffer + pos, INI_BUFFERSIZE - pos, rfp); + while (pos < *size && buffer[pos] != '\0') + pos++; /* cannot use _tcslen() because buffer may not be zero-terminated */ + } /* while */ + if (buffer[0] != '\0') { + assert(pos > 0 && pos <= INI_BUFFERSIZE); + if (pos == INI_BUFFERSIZE) + pos--; + buffer[pos] = '\0'; /* force zero-termination (may be left unterminated in the above while loop) */ + (void)ini_write(buffer, wfp); + } + ini_tell(rfp, mark); /* update mark */ + *size = 0; + /* return whether the buffer ended with a line termination */ + return (pos > terminator_len) && (_tcscmp(buffer + pos - terminator_len, INI_LINETERM) == 0); +} + +static int close_rename(INI_FILETYPE *rfp, INI_FILETYPE *wfp, const TCHAR *filename, TCHAR *buffer) +{ + (void)ini_close(rfp); + (void)ini_close(wfp); + (void)ini_remove(filename); + (void)ini_tempname(buffer, filename, INI_BUFFERSIZE); + (void)ini_rename(buffer, filename); + return 1; +} + +/** ini_puts() + * \param Section the name of the section to write the string in + * \param Key the name of the entry to write, or NULL to erase all keys in the section + * \param Value a pointer to the buffer the string, or NULL to erase the key + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename) +{ + INI_FILETYPE rfp; + INI_FILETYPE wfp; + INI_FILEPOS mark; + INI_FILEPOS head, tail; + TCHAR *sp, *ep; + TCHAR LocalBuffer[INI_BUFFERSIZE]; + int len, match, flag, cachelen; + + assert(Filename != NULL); + if (!ini_openread(Filename, &rfp)) { + /* If the .ini file doesn't exist, make a new file */ + if (Key != NULL && Value != NULL) { + if (!ini_openwrite(Filename, &wfp)) + return 0; + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + (void)ini_close(&wfp); + } /* if */ + return 1; + } /* if */ + + /* If parameters Key and Value are valid (so this is not an "erase" request) + * and the setting already exists, there are two short-cuts to avoid rewriting + * the INI file. + */ + if (Key != NULL && Value != NULL) { + ini_tell(&rfp, &mark); + match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), &head); + if (match) { + /* if the current setting is identical to the one to write, there is + * nothing to do. + */ + if (_tcscmp(LocalBuffer,Value) == 0) { + (void)ini_close(&rfp); + return 1; + } /* if */ + /* if the new setting has the same length as the current setting, and the + * glue file permits file read/write access, we can modify in place. + */ + #if defined ini_openrewrite + /* we already have the start of the (raw) line, get the end too */ + ini_tell(&rfp, &tail); + /* create new buffer (without writing it to file) */ + writekey(LocalBuffer, Key, Value, NULL); + if (_tcslen(LocalBuffer) == (size_t)(tail - head)) { + /* length matches, close the file & re-open for read/write, then + * write at the correct position + */ + (void)ini_close(&rfp); + if (!ini_openrewrite(Filename, &wfp)) + return 0; + (void)ini_seek(&wfp, &head); + (void)ini_write(LocalBuffer, &wfp); + (void)ini_close(&wfp); + return 1; + } /* if */ + #endif + } /* if */ + /* key not found, or different value & length -> proceed (but rewind the + * input file first) + */ + (void)ini_seek(&rfp, &mark); + } /* if */ + + /* Get a temporary file name to copy to. Use the existing name, but with + * the last character set to a '~'. + */ + ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); + if (!ini_openwrite(LocalBuffer, &wfp)) { + (void)ini_close(&rfp); + return 0; + } /* if */ + (void)ini_tell(&rfp, &mark); + cachelen = 0; + + /* Move through the file one line at a time until a section is + * matched or until EOF. Copy to temp file as it is read. + */ + len = (Section != NULL) ? (int)_tcslen(Section) : 0; + if (len > 0) { + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* Failed to find section, so add one to the end */ + flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key!=NULL && Value!=NULL) { + if (!flag) + (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ + } /* if */ + /* Copy the line from source to dest, but not if this is the section that + * we are looking for and this section must be removed + */ + sp = skipleading(LocalBuffer); + ep = _tcsrchr(sp, ']'); + match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,Section,len) == 0); + if (!match || Key != NULL) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* if */ + } while (!match); + } /* if */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + /* when deleting a section, the section head that was just found has not been + * copied to the output file, but because this line was not "accumulated" in + * the cache, the position in the input file was reset to the point just + * before the section; this must now be skipped (again) + */ + if (Key == NULL) { + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + (void)ini_tell(&rfp, &mark); + } /* if */ + + /* Now that the section has been found, find the entry. Stop searching + * upon leaving the section's area. Copy the file as it is read + * and create an entry if one is not found. + */ + len = (Key != NULL) ? (int)_tcslen(Key) : 0; + for( ;; ) { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* EOF without an entry so make one */ + flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key!=NULL && Value!=NULL) { + if (!flag) + (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ + } /* if */ + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + match = (ep != NULL && len > 0 && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0); + if ((Key != NULL && match) || *sp == '[') + break; /* found the key, or found a new section */ + /* copy other keys in the section */ + if (Key == NULL) { + (void)ini_tell(&rfp, &mark); /* we are deleting the entire section, so update the read position */ + } else { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* if */ + } /* for */ + /* the key was found, or we just dropped on the next section (meaning that it + * wasn't found); in both cases we need to write the key, but in the latter + * case, we also need to write the line starting the new section after writing + * the key + */ + flag = (*sp == '['); + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key != NULL && Value != NULL) + writekey(LocalBuffer, Key, Value, &wfp); + /* cache_flush() reset the "read pointer" to the start of the line with the + * previous key or the new section; read it again (because writekey() destroyed + * the buffer) + */ + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + if (flag) { + /* the new section heading needs to be copied to the output file */ + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } else { + /* forget the old key line */ + (void)ini_tell(&rfp, &mark); + } /* if */ + /* Copy the rest of the INI file */ + while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* while */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ +} + +/* Ansi C "itoa" based on Kernighan & Ritchie's "Ansi C" book. */ +#define ABS(v) ((v) < 0 ? -(v) : (v)) + +static void strreverse(TCHAR *str) +{ + int i, j; + for (i = 0, j = (int)_tcslen(str) - 1; i < j; i++, j--) { + TCHAR t = str[i]; + str[i] = str[j]; + str[j] = t; + } /* for */ +} + +static void long2str(long value, TCHAR *str) +{ + int i = 0; + long sign = value; + + /* generate digits in reverse order */ + do { + int n = (int)(value % 10); /* get next lowest digit */ + str[i++] = (TCHAR)(ABS(n) + '0'); /* handle case of negative digit */ + } while (value /= 10); /* delete the lowest digit */ + if (sign < 0) + str[i++] = '-'; + str[i] = '\0'; + + strreverse(str); +} + +/** ini_putl() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write + * \param Value the value to write + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putl(const TCHAR *Section, const TCHAR *Key, long Value, const TCHAR *Filename) +{ + TCHAR LocalBuffer[32]; + long2str(Value, LocalBuffer); + return ini_puts(Section, Key, LocalBuffer, Filename); +} + +#if defined INI_REAL +/** ini_putf() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write + * \param Value the value to write + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putf(const TCHAR *Section, const TCHAR *Key, INI_REAL Value, const TCHAR *Filename) +{ + TCHAR LocalBuffer[64]; + ini_ftoa(LocalBuffer, Value); + return ini_puts(Section, Key, LocalBuffer, Filename); +} +#endif /* INI_REAL */ +#endif /* !INI_READONLY */ diff --git a/source/amx/minIni.h b/source/amx/minIni.h new file mode 100644 index 00000000..6ce11de3 --- /dev/null +++ b/source/amx/minIni.h @@ -0,0 +1,152 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * Copyright (c) CompuPhase, 2008-2012 + * + * 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: minIni.h 5181 2015-01-21 09:44:28Z thiadmer $ + */ +#ifndef MININI_H +#define MININI_H + +#include "minGlue.h" + +#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY + #include + #define mTCHAR TCHAR +#else + /* force TCHAR to be "char", but only for minIni */ + #define mTCHAR char +#endif + +#if !defined INI_BUFFERSIZE + #define INI_BUFFERSIZE 512 +#endif + +#if defined __cplusplus + extern "C" { +#endif + +int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const mTCHAR *Filename); +long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const mTCHAR *Filename); +int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); +int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); +int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); + +#if defined INI_REAL +INI_REAL ini_getf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL DefValue, const mTCHAR *Filename); +#endif + +#if !defined INI_READONLY +int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const mTCHAR *Filename); +int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const mTCHAR *Filename); +#if defined INI_REAL +int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const mTCHAR *Filename); +#endif +#endif /* INI_READONLY */ + +#if !defined INI_NOBROWSE +typedef int (*INI_CALLBACK)(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const void *UserData); +int ini_browse(INI_CALLBACK Callback, const void *UserData, const mTCHAR *Filename); +#endif /* INI_NOBROWSE */ + +#if defined __cplusplus + } +#endif + + +#if defined __cplusplus + +#if defined __WXWINDOWS__ + #include "wxMinIni.h" +#else + #include + + /* The C++ class in minIni.h was contributed by Steven Van Ingelgem. */ + class minIni + { + public: + minIni(const std::string& filename) : iniFilename(filename) + { } + + bool getbool(const std::string& Section, const std::string& Key, bool DefValue=false) const + { return ini_getbool(Section.c_str(), Key.c_str(), int(DefValue), iniFilename.c_str()) != 0; } + + long getl(const std::string& Section, const std::string& Key, long DefValue=0) const + { return ini_getl(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); } + + int geti(const std::string& Section, const std::string& Key, int DefValue=0) const + { return static_cast(this->getl(Section, Key, long(DefValue))); } + + std::string gets(const std::string& Section, const std::string& Key, const std::string& DefValue="") const + { + char buffer[INI_BUFFERSIZE]; + ini_gets(Section.c_str(), Key.c_str(), DefValue.c_str(), buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + + std::string getsection(int idx) const + { + char buffer[INI_BUFFERSIZE]; + ini_getsection(idx, buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + + std::string getkey(const std::string& Section, int idx) const + { + char buffer[INI_BUFFERSIZE]; + ini_getkey(Section.c_str(), idx, buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + +#if defined INI_REAL + INI_REAL getf(const std::string& Section, const std::string& Key, INI_REAL DefValue=0) const + { return ini_getf(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); } +#endif + +#if ! defined INI_READONLY + bool put(const std::string& Section, const std::string& Key, long Value) const + { return ini_putl(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } + + bool put(const std::string& Section, const std::string& Key, int Value) const + { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } + + bool put(const std::string& Section, const std::string& Key, bool Value) const + { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } + + bool put(const std::string& Section, const std::string& Key, const std::string& Value) const + { return ini_puts(Section.c_str(), Key.c_str(), Value.c_str(), iniFilename.c_str()) != 0; } + + bool put(const std::string& Section, const std::string& Key, const char* Value) const + { return ini_puts(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } + +#if defined INI_REAL + bool put(const std::string& Section, const std::string& Key, INI_REAL Value) const + { return ini_putf(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } +#endif + + bool del(const std::string& Section, const std::string& Key) const + { return ini_puts(Section.c_str(), Key.c_str(), 0, iniFilename.c_str()) != 0; } + + bool del(const std::string& Section) const + { return ini_puts(Section.c_str(), 0, 0, iniFilename.c_str()) != 0; } +#endif + + private: + std::string iniFilename; + }; + +#endif /* __WXWINDOWS__ */ +#endif /* __cplusplus */ + +#endif /* MININI_H */