Y_Less c565a5d1df Revert "Merge branch 'master' of github.com:pawn-lang/compiler into HEAD"
This reverts commit 5400511d8616964ddf2c78bb65e72dfc23fa3a25, reversing
changes made to 2e15e645e687bc0ad4a210963de0bfadc56d48fc.
2018-10-30 22:51:21 +01:00

403 lines
11 KiB
C

/* Script Arguments support module for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2005-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxargs.c 3649 2006-10-12 13:13:57Z thiadmer $
*/
#if defined _UNICODE || defined __UNICODE__ || defined UNICODE
# if !defined UNICODE /* for Windows */
# define UNICODE
# endif
# if !defined _UNICODE /* for C library */
# define _UNICODE
# endif
#endif
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
#include <malloc.h>
#endif
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
#include <windows.h>
#endif
#include "osdefs.h"
#include "amx.h"
#if defined _UNICODE
# include <tchar.h>
#elif !defined __T
typedef char TCHAR;
# define __T(string) string
# define _istdigit isdigit
# define _tgetenv getenv
# define _tcscat strcat
# define _tcschr strchr
# define _tcscpy strcpy
# define _tcsdup strdup
# define _tcslen strlen
# define _tcsncmp strncmp
# define _tcspbrk strpbrk
# define _tcsrchr strrchr
# define _tcstol strtol
#endif
#if !defined AMXARGS_COLON
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
/* A ':' is also a separator for filenames (the disk drive identifier), and
* therefore it is better not to use it as an name/value seperator for
* command line argiments as well. So, by default, the library uses only
* the '=' as the name/value separator.
*/
#define AMXARGS_COLON 0
#else
#define AMXARGS_COLON 1
#endif
#endif
#if !defined AMXARGS_SKIPARG
/* The first option may be the name of the script (this is common if the
* host application takes the name of the script as the first parameter).
* This can optionally be ignored.
*/
#define AMXARGS_SKIPARG 0
#endif
static const TCHAR *tokenize(const TCHAR *string, int index, int *length);
static const TCHAR *cmdline = NULL;
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[1024]; /* some arbitrary maximum */
#endif
const TCHAR *ptr;
int skip = 0;
if (cmdline == NULL) {
#if defined __WIN32__ || defined _WIN32 || defined WIN32
cmdline = GetCommandLine();
skip++;
#elif defined _Windows || defined __MSDOS__
#if defined _Windows
unsigned short _psp = GetCurrentPDB();
#endif
char _far *cmd = (char _far *)MK_FP(_psp, 128);
unsigned char length = (unsigned char)*cmd++;
assert(length < 128);
assert(length < sizeof cmdbuffer);
memcpy(cmdbuffer, cmd, length);
cmdbuffer[length] = '\0';
if ((cmd == strchr(cmdbuffer, '\r')) != NULL)
*cmd = '\0'; /* also erase \r after the last option (if any) */
cmdline = cmdbuffer;
#elif defined LINUX
/* Options in /proc/<pid>/cmdline are delimited with '\0' characters
* rather than spaces.
*/
FILE *fp;
size_t fsize;
sprintf(cmdbuffer, "/proc/%d/cmdline", getpid());
if ((fp = fopen(cmdbuffer, "r")) != NULL) {
char *ptr;
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (fsize >= sizeof cmdbuffer)
fsize = sizeof cmdbuffer - 1;
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 = ' ';
cmdline = cmdbuffer;
skip++;
} /* if */
#else
#error Platform not supported
#endif
/* skip leading white space */
while (*cmdline <= __T(' ') && *cmdline != __T('\0'))
cmdline++;
#if AMXARGS_SKIPARG
skip++;
#endif
/* skip the first option(s), because it is the name of the host program
* and the name of the script
*/
if ((ptr = tokenize(cmdline, skip, NULL)) != NULL)
cmdline = ptr;
else
cmdline = _tcschr(cmdline, __T('\0'));
} /* if */
return cmdline;
}
static const TCHAR *tokenize(const TCHAR *string, int index, int *length)
{
const TCHAR *start = string;
TCHAR endchar;
assert(index >= 0);
assert(start != NULL);
while (*start == __T(' ') || *start == __T('\t'))
start++;
if (*start == __T('\0'))
return NULL;
if (*start == __T('"'))
endchar = *start++;
else
endchar = __T(' ');
while (index > 0 && start != NULL) {
start = _tcschr(start, endchar);
if (start != NULL) {
assert(*start == endchar);
if (endchar != __T(' '))
start++;
while (*start == __T(' ') || *start == __T('\t'))
start++;
if (*start == __T('"'))
endchar = *start++;
else
endchar = __T(' ');
} /* if */
index--;
} /* while */
if (start != NULL && length != NULL) {
const TCHAR *end;
if ((end = _tcschr(start, endchar)) == NULL)
end = _tcschr(start, __T('\0'));
assert(end != NULL);
*length = (int)(end - start);
} /* if */
return start;
}
static const TCHAR *matcharg(const TCHAR *key, int skip, int *length)
{
const TCHAR *cmdline = rawcmdline();
int index, optlen, keylen;
const TCHAR *option, *vptr;
keylen = (key != NULL) ? _tcslen(key) : 0;
index = 0;
while ((option = tokenize(cmdline, index, length)) != NULL) {
/* check for a colon or an equal sign (':' or '=') */
vptr = _tcschr(option, __T('='));
#if AMXARGS_COLON
if (vptr == NULL || (int)(vptr - option) > *length)
vptr = _tcschr(option, __T(':'));
#endif
if (vptr != NULL && (int)(vptr - option) > *length)
vptr = NULL;
optlen = (vptr != NULL) ? (int)(vptr - option) : 0;
if (keylen == 0 && vptr == NULL
|| keylen > 0 && keylen == optlen && _tcsncmp(option, key, optlen) == 0)
{
if (vptr != NULL)
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);
if (skip-- == 0)
break;
} /* if */
index++;
} /* while */
return option;
}
/* bool: argindex(index, value[], maxlength=sizeof value, bool:pack=false)
* returns true if the option was found and false on error or if the parameter "index" is out of range
*/
static cell AMX_NATIVE_CALL n_argindex(AMX *amx, const cell *params)
{
const TCHAR *cmdline = rawcmdline();
const TCHAR *option;
int length, max;
TCHAR *str;
cell *cptr;
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 */
if ((option = tokenize(cmdline, params[1], &length)) == NULL) {
/* option not found, return an empty string */
*cptr = 0;
return 0;
} /* if */
if (params[4])
max *= sizeof(cell);
if (max > length + 1)
max = length + 1;
str = (TCHAR *)alloca(max*sizeof(TCHAR));
if (str == NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
memcpy(str, option, (max - 1) * sizeof(TCHAR));
str[max - 1] = __T('\0');
amx_SetString(cptr, (char*)str, (int)params[4], sizeof(TCHAR)>1, max);
return 1;
}
/* bool: argstr(index=0, const option[]="", value[]="", maxlength=sizeof value, bool:pack=false)
* returns true if the option was found and false otherwise
*/
static cell AMX_NATIVE_CALL n_argstr(AMX *amx, const cell *params)
{
const TCHAR *option, *key;
int length, max;
TCHAR *str;
cell *cptr;
max = (int)params[4];
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 */
option = matcharg(key, (int)params[1], &length);
if (option == NULL)
return 0; /* option not found */
/* check whether we must write the value of the option at all; in case the
* size is one cell and that cell is already zero, we do not write anything
* back
*/
assert(params[4] > 0);
if (params[4] > 1 || *cptr != 0) {
if (params[5])
max *= sizeof(cell);
if (max > length + 1)
max = length + 1;
str = (TCHAR *)alloca(max*sizeof(TCHAR));
if (str == NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
memcpy(str, option, (max - 1) * sizeof(TCHAR));
str[max - 1] = __T('\0');
amx_SetString(cptr, (char*)str, (int)params[5], sizeof(TCHAR)>1, max);
} /* if */
return 1;
}
/* bool: argvalue(index=0, const option[]="", &value=cellmin)
* returns true if the option was found and false otherwise
*/
static cell AMX_NATIVE_CALL n_argvalue(AMX *amx, const cell *params)
{
const TCHAR *option, *key;
int length;
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 */
option = matcharg(key, (int)params[1], &length);
if (option == NULL)
return 0;
/* check whether we must write the value of the option at all */
if (length > 0 && (_istdigit(*option) || *option == __T('-')))
*cptr = _tcstol(option, NULL, 10);
return 1;
}
/* argcount() */
static cell AMX_NATIVE_CALL n_argcount(AMX *amx, const cell *params)
{
const TCHAR *cmdline = rawcmdline();
cell count = 0;
while (tokenize(cmdline, count, NULL) != NULL)
count++;
(void)amx;
(void)params;
return count;
}
#if defined __cplusplus
extern "C"
#endif
const AMX_NATIVE_INFO args_Natives[] = {
{ "argcount", n_argcount },
{ "argindex", n_argindex },
{ "argstr", n_argstr },
{ "argvalue", n_argvalue },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_ArgsInit(AMX *amx)
{
return amx_Register(amx, args_Natives, -1);
}
int AMXEXPORT amx_ArgsCleanup(AMX *amx)
{
(void)amx;
return AMX_ERR_NONE;
}
/* A host application calls this function to set the command line for the
* script. If the host does not do this, the library will use the global
* options for the application (provided that it can find these). The buffer
* 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)
{
cmdline = cmd;
return AMX_ERR_NONE;
}