SA-MP/server/amx/amxcons.c
2024-06-22 19:51:34 +08:00

1108 lines
28 KiB
C

/* Console output module (terminal I/O) for the Pawn AMX
*
* Since some of these routines go further than those of standard C, they
* cannot always be implemented with portable C functions. In other words,
* these routines must be ported to other environments.
*
* Copyright (c) ITB CompuPhase, 1997-2005
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxcons.c 3363 2005-07-23 09:03:29Z 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 <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
#include <conio.h>
#include <malloc.h>
#endif
#if defined USE_CURSES
#include <curses.h>
#endif
#include "amx.h"
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
#include <windows.h>
#endif
#if defined _UNICODE
# include <tchar.h>
#elif !defined __T
typedef char TCHAR;
# define __T(string) string
# define _tcschr strchr
# define _tcscpy strcpy
# define _tcsdup strdup
# define _tcslen strlen
# define _stprintf sprintf
#endif
#if defined __MSDOS__
#define EOL_CHAR '\r'
#endif
#if defined __WIN32__ || defined _WIN32 || defined WIN32
#define EOL_CHAR '\r'
#endif
#if !defined EOL_CHAR
/* if not a "known" operating system, assume Linux/Unix */
#define EOL_CHAR '\n'
#endif
#if defined AMX_TERMINAL
/* required functions are implemented elsewhere */
int amx_putstr(TCHAR *);
int amx_putchar(int);
int amx_fflush(void);
int amx_getch(void);
TCHAR *amx_gets(TCHAR *,int);
int amx_termctl(int,int);
void amx_clrscr(void);
void amx_clreol(void);
void 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
/* 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)
int amx_termctl(int code,int value)
{
switch (code) {
case 0: /* query terminal support */
return 1;
case 1: /* switch "auto-wrap" on or off */
if (value)
amx_putstr("\033[?7h"); /* enable "auto-wrap" */
else
amx_putstr("\033[?7l"); /* disable "auto-wrap" */
return 1;
#if 0
/* next to swapping buffers, more information should be saved and swapped,
* such as the cursor position and the current terminal attributes
*/
case 2: /* swap console buffers */
amx_fflush();
if (value==1) {
amx_putstr("\033[?47h");
} else {
amx_putstr("\033[?47l");
} /* if */
amx_fflush();
return 1;
#endif
case 3: /* set bold/highlighted font */
return 0;
default:
return 0;
} /* switch */
}
void amx_clrscr(void)
{
amx_putstr("\033[2J");
amx_fflush(); /* pump through the terminal codes */
}
void amx_clreol(void)
{
amx_putstr("\033[K");
amx_fflush(); /* pump through the terminal codes */
}
void 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 */
}
void amx_wherexy(int *x,int *y)
{
int val,i;
char str[10];
assert(x!=NULL && y!=NULL);
amx_putstr("\033[6n");
amx_fflush();
while (amx_getch()!='\033')
/* nothing */;
val=amx_getch();
assert(val=='[');
for (i=0; i<8 && (val=amx_getch())!=';'; i++)
str[i]=(char)val;
str[i]='\0';
*y=atoi(str);
for (i=0; i<8 && (val=amx_getch())!='R'; i++)
str[i]=(char)val;
str[i]='\0';
*x=atoi(str);
#if defined ANSITERM
val=amx_getch();
assert(val=='\r'); /* ANSI driver adds CR to the end of the command */
#endif
}
unsigned int amx_setattr(int foregr,int backgr,int highlight)
{
static short current=(0 << 8) | 7;
short prev = current;
char str[30];
if (foregr>=0) {
_stprintf(str,"\x1b[%dm",foregr+30);
amx_putstr(str);
current=(current & 0xff00) | (foregr & 0x0f);
} /* if */
if (backgr>=0) {
_stprintf(str,"\x1b[%dm",backgr+40);
amx_putstr(str);
current=(current & 0x00ff) | ((backgr & 0x0f) << 8);
} /* if */
if (highlight>=0) {
_stprintf(str,"\x1b[%dm",highlight);
amx_putstr(str);
current=(current & 0x7fff) | ((highlight & 0x01) << 15);
} /* if */
return prev;
}
void amx_console(int columns, int lines, int flags)
{
char str[30];
(void)flags;
/* There is no ANSI code (or VT100/VT220) to set the size of the console
* (indeed, the terminal was that of the alphanumeric display). In xterm (a
* terminal emulator) we can set the terminal size though, and most
* terminals that in use today are in fact emulators.
* Putty understands this code too, by many others do not.
*/
sprintf(str,"\033[8;%d;%dt",lines,columns);
amx_putstr(str);
amx_fflush();
}
#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)
int amx_termctl(int code,int value)
{
switch (code) {
case 0: /* query terminal support */
return 1;
case 1: { /* switch auto-wrap on/off */
/* only works in Windows 2000/XP */
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
DWORD Flags=ENABLE_PROCESSED_OUTPUT;
if (value)
Flags |= ENABLE_WRAP_AT_EOL_OUTPUT;
SetConsoleMode(hConsole,Flags);
return 1;
} /* case */
/* case 2: */ /* create/switch to another console */
/* case 3: */ /* set emphasized font */
/* case 4: */ /* query whether a terminal is "open" */
default:
return 0;
} /* switch */
}
void amx_clrscr(void)
{
COORD coordScreen={0,0};
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
amx_fflush(); /* make sure the I/O buffer is empty */
GetConsoleScreenBufferInfo(hConsole,&csbi);
dwConSize=csbi.dwSize.X*csbi.dwSize.Y;
FillConsoleOutputCharacter(hConsole,' ',dwConSize,coordScreen,&cCharsWritten);
FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,coordScreen, &cCharsWritten);
SetConsoleCursorPosition(hConsole,coordScreen);
}
void amx_clreol(void)
{
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
amx_fflush(); /* make sure all output is written */
GetConsoleScreenBufferInfo(hConsole,&csbi);
dwConSize=csbi.dwSize.X - csbi.dwCursorPosition.X;
FillConsoleOutputCharacter(hConsole,' ',dwConSize,csbi.dwCursorPosition,&cCharsWritten);
FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,csbi.dwCursorPosition,&cCharsWritten);
}
void amx_gotoxy(int x,int y)
{
COORD point;
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsole, &csbi);
if (x<=0 || x>csbi.dwSize.X || y<=0 || y>csbi.dwSize.Y)
return;
amx_fflush(); /* make sure all output is written */
point.X=(short)(x-1);
point.Y=(short)(y-1);
SetConsoleCursorPosition(hConsole,point);
}
void amx_wherexy(int *x,int *y)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
amx_fflush(); /* make sure all output is written */
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
if (x!=NULL)
*x=csbi.dwCursorPosition.X+1;
if (y!=NULL)
*y=csbi.dwCursorPosition.Y+1;
}
unsigned int amx_setattr(int foregr,int backgr,int highlight)
{
static int ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
CONSOLE_SCREEN_BUFFER_INFO csbi;
int f,b,h,prev;
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
amx_fflush(); /* make sure all output is written */
GetConsoleScreenBufferInfo(hConsole,&csbi);
f=csbi.wAttributes & 0x07;
b=(csbi.wAttributes >> 4) & 0x0f;
h=(csbi.wAttributes & 0x08) ? 1 : 0;
prev=(b << 8) | f | (h << 15);
if (foregr>=0 && foregr<8)
f=ansi_colours[foregr];
if (backgr>=0 && backgr<8)
b=ansi_colours[backgr];
if (highlight>=0)
h=highlight!=0;
SetConsoleTextAttribute(hConsole, (WORD)((b << 4) | f | (h << 3)));
return prev;
}
void amx_console(int columns, int lines, int flags)
{
SMALL_RECT rect;
COORD dwSize;
(void)flags;
dwSize.X=(short)columns;
dwSize.Y=(short)lines;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),dwSize);
rect.Left=0;
rect.Top=0;
rect.Right=(short)(columns-1);
rect.Bottom=(short)(lines-1);
SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE),TRUE,&rect);
}
#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_termsup(c,v) (0)
#define amx_console(c,l,f) (void)(0)
#else
/* assume a streaming terminal; limited features (no colour, no cursor
* control)
*/
#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)(0)
#define amx_wherexy(x,y) (*(x)=*(y)=0)
#define amx_setattr(c,b,h) (0)
#define amx_termsup(c,v) (0)
#define amx_console(c,l,f) (void)(0)
#endif
#if !defined AMX_TERMINAL && (defined __WIN32__ || defined _WIN32 || defined WIN32)
void CreateConsole(void)
{ static int createdconsole=0;
if (!createdconsole) {
AllocConsole();
createdconsole=1;
} /* if */
}
#elif defined(USE_CURSES)
// The Mac OS X build variant uses curses.
void CreateConsole(void)
{ static int createdconsole=0;
if (!createdconsole) {
initscr();
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
createdconsole=1;
} /* if */
}
#else
#define CreateConsole()
#endif
static int printstring(AMX *amx,cell *cstr,cell *params,int num);
enum {
SV_DECIMAL,
SV_HEX
};
static TCHAR *reverse(TCHAR *string,int stop)
{
int start=0;
TCHAR temp;
/* swap the string */
stop--; /* avoid swapping the '\0' byte to the first position */
while (stop - start > 0) {
temp = string[start];
string[start] = string[stop];
string[stop] = temp;
start++;
stop--;
} /* while */
return string;
}
/* Converts an integral value to a string, with optional padding with spaces or
* zeros.
* The "format" must be decimal or hexadecimal
* The number is right-aligned in the field with the size of the absolute value
* of the "width" parameter.
* If the width value is positive, the string is padded with spaces; if it is
* negative, it is padded with zeros.
*/
static TCHAR *strval(TCHAR buffer[], long value, int format, int width)
{
int start, stop;
TCHAR temp;
start = stop = 0;
if (format == SV_DECIMAL) {
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);
} else {
/* hexadecimal */
unsigned long v = (unsigned long)value; /* copy to unsigned value for shifting */
do {
buffer[stop] = (TCHAR)((v & 0x0f) + __T('0'));
if (buffer[stop] > __T('9'))
buffer[stop] += (TCHAR)(__T('A') - __T('0') - 10);
v >>= 4;
stop++;
} while (v != 0);
} /* if */
/* pad to given width */
if (width < 0) {
temp = __T('0');
width = -width;
} else {
temp = __T(' ');
} /* if */
while (stop < width)
buffer[stop++] = temp;
buffer[stop] = __T('\0');
/* swap the string, and we are done */
reverse(buffer+start,stop-start);
return buffer;
}
#if defined FIXEDPOINT
#define FIXEDMULT 1000
#define FIXEDDIGITS 3
static TCHAR *formatfixed(TCHAR *string,cell value,char align,int width,char decpoint,int digits)
{
int i, len;
cell ipart,v;
TCHAR vsign=__T('\0');
/* make the value positive (but keep the sign) */
if (value<0) {
value=-value;
vsign='-';
} /* if */
/* "prepare" the value so that when it is truncated to the requested
* number of digits, the result is rounded towards the dropped digits
*/
v=FIXEDMULT/2;
for (i=0; i<digits; i++)
v/=10;
value+=v;
/* get the integer part and remove it from the value */
ipart=value/FIXEDMULT;
value-=FIXEDMULT*ipart;
assert(ipart>=0);
assert(value>=0);
/* truncate the fractional part to the requested number of digits */
for (i=FIXEDDIGITS; i>digits; i--)
value/=10;
string[0]='\0';
/* add sign */
i=_tcslen(string);
string[i]=vsign;
string[i+1]='\0';
/* add integer part */
strval(string+_tcslen(string),(long)ipart,SV_DECIMAL,0);
/* add fractional part */
if (digits>0) {
i=_tcslen(string);
string[i]=decpoint;
strval(string+i+1,(long)value,SV_DECIMAL,-digits);
} /* if */
len=_tcslen(string);
if (len<width) {
/* pad to the requested width */
for (i=len; i<width; i++)
string[i]=__T(' ');
string[i]=__T('\0');
/* optionally move the padding to the beginning of the string, using the handwaving algorithm */
if (align!='-') {
assert(i==(int)_tcslen(string));
assert(i>=len);
reverse(string,len);
reverse(string+len,i-len);
reverse(string,i);
} /* if */
} /* if */
return string;
}
#endif
static int dochar(AMX *amx,TCHAR ch,cell param,char sign,char decpoint,int width,int digits)
{
cell *cptr;
TCHAR buffer[40];
#if defined FLOATPOINT
TCHAR formatstring[40];
#endif
#if !defined FIXEDPOINT && !defined FLOATPOINT
(void)decpoint;
(void)digits;
#endif
switch (ch) {
case '%':
amx_putchar(ch);
return 0;
case 'c':
amx_GetAddr(amx,param,&cptr);
width--; /* single character itself has a with of 1 */
if (sign!='-')
while (width-->0)
amx_putchar(__T(' '));
amx_putchar((TCHAR)*cptr);
while (width-->0)
amx_putchar(__T(' '));
return 1;
case 'd': {
cell value;
int length=1;
amx_GetAddr(amx,param,&cptr);
value=*cptr;
if (value<0 || sign=='+') {
length++;
value=-value;
} /* if */
while (value>=10) {
length++;
value/=10;
} /* while */
width-=length;
if (sign!='-')
while (width-->0)
amx_putchar(__T(' '));
strval(buffer,*cptr,SV_DECIMAL,0);
amx_putstr(buffer);
while (width-->0)
amx_putchar(__T(' '));
return 1;
} /* case */
#if defined FLOATPOINT
case 'f': /* 32-bit floating point number */
case 'r': /* if floating point is enabled, %r == %f */
/* build a format string */
_tcscpy(formatstring,__T("%"));
if (sign!='\0')
_stprintf(formatstring+_tcslen(formatstring),__T("%c"),sign);
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);
amx_putstr(buffer);
return 1;
#endif
#if defined FIXEDPOINT
#define FIXEDMULT 1000
case 'q': /* 32-bit fixed point number */
#if !defined FLOATPOINT
case 'r': /* if fixed point is enabled, and floating point is not, %r == %q */
#endif
amx_GetAddr(amx,param,&cptr);
/* format the number */
formatfixed(buffer,*cptr,sign,width,decpoint,digits);
assert(_tcslen(buffer)<sizeof buffer);
amx_putstr(buffer);
return 1;
#endif
#if !defined FLOATPOINT && !defined FIXEDPOINT
case 'f':
case 'q':
case 'r':
amx_putstr(__T("(no rational number support)"));
return 0; /* flag this as an error */
#endif
case 's':
amx_GetAddr(amx,param,&cptr);
printstring(amx,cptr,NULL,0);
return 1;
case 'x': {
ucell value;
int length=1;
amx_GetAddr(amx,param,&cptr);
value=*(ucell*)cptr;
while (value>=0x10) {
length++;
value>>=4;
} /* while */
width-=length;
if (sign!='-')
while (width-->0)
amx_putchar(__T(' '));
strval(buffer,(long)*cptr,SV_HEX,0);
amx_putstr(buffer);
while (width-->0)
amx_putchar(__T(' '));
return 1;
} /* case */
} /* switch */
/* error in the string format, try to repair */
amx_putchar(ch);
return 0;
}
enum {
FMT_NONE, /* not in format state; accept '%' */
FMT_START, /* found '%', accept '+', ' ' (START), digit (WIDTH), '.' (DECIM) 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) */
};
static int formatstate(TCHAR c,int *state,char *sign,char *decpoint,int *width,int *digits)
{
switch (*state) {
case FMT_NONE:
if (c=='%') {
*state=FMT_START;
*sign='\0';
*decpoint='.';
*width=0;
*digits=3;
} else {
return -1; /* print a single character */
} /* if */
break;
case FMT_START:
if (c=='+' || c==' ') {
*sign=' ';
} else if (c>='0' && c<='9') {
*width=(int)(c-'0');
*state=FMT_WIDTH;
} else if (c=='.' || c==',') {
*decpoint=c;
*digits=0;
*state=FMT_DECIM;
} else {
return 1; /* print formatted character */
} /* if */
break;
case FMT_WIDTH:
if (c>='0' && c<='9') {
*width=*width*10+(int)(c-'0');
} else if (c=='.' || c==',') {
*decpoint=c;
*digits=0;
*state=FMT_DECIM;
} else {
return 1; /* print formatted character */
} /* if */
break;
case FMT_DECIM:
if (c>='0' && c<='9') {
*digits=*digits*10+(int)(c-'0');
} else {
return 1; /* print formatted character */
} /* if */
break;
} /* switch */
return 0;
}
static int printstring(AMX *amx,cell *cstr,cell *params,int num)
{
int i,paramidx=0;
int fmtstate=FMT_NONE,width,digits;
char sign,decpoint;
/* if no placeholders appear, we can use a quicker routine */
if (params==NULL) {
TCHAR cache[100];
int idx=0;
for (i=0; cstr[i]!=0; i++) {
assert(idx<sizeof cache);
cache[idx++]=(TCHAR)cstr[i];
if (idx==sizeof cache - 1) {
cache[idx]='\0';
amx_putstr(cache);
idx=0;
} /* if */
} /* for */
if (idx>0) {
cache[idx]='\0';
amx_putstr(cache);
} /* if */
} else {
/* unpacked */
for (i=0; cstr[i]!=0; i++) {
switch (formatstate((TCHAR)cstr[i],&fmtstate,&sign,&decpoint,&width,&digits)) {
case -1:
amx_putchar((TCHAR)cstr[i]);
break;
case 1:
assert(params!=NULL);
paramidx+=dochar(amx,(TCHAR)cstr[i],params[paramidx],sign,decpoint,width,digits);
fmtstate=FMT_NONE;
break;
case 0:
if (params==NULL || paramidx>=num) /* insufficient parameters passed */
amx_RaiseError(amx, AMX_ERR_NATIVE);
break;
} /* switch */
} /* for */
} /* if (params==NULL) */
return paramidx;
}
static cell AMX_NATIVE_CALL n_print(AMX *amx,cell *params)
{
cell *cstr;
int oldcolours;
CreateConsole();
/* set the new colours */
oldcolours=amx_setattr((int)params[2],(int)params[3],(int)params[4]);
amx_GetAddr(amx,params[1],&cstr);
printstring(amx,cstr,NULL,0);
/* reset the colours */
(void)amx_setattr(oldcolours & 0xff,(oldcolours >> 8) & 0x7f,(oldcolours >> 15) & 0x01);
amx_fflush();
return 0;
}
static cell AMX_NATIVE_CALL n_printf(AMX *amx,cell *params)
{
cell *cstr;
CreateConsole();
amx_GetAddr(amx,params[1],&cstr);
printstring(amx,cstr,params+2,(int)(params[0]/sizeof(cell))-1);
amx_fflush();
return 0;
}
/* getchar(bool:echo=true) */
static cell AMX_NATIVE_CALL n_getchar(AMX *amx,cell *params)
{
int c;
(void)amx;
CreateConsole();
c=amx_getch();
if (params[1]) {
#if defined(SUPPRESS_ECHO)
/* For Mac OS X, non-Curses, don't echo the character */
#else
amx_putchar((TCHAR)c);
amx_fflush();
#endif
} /* if */
return c;
}
/* getstring(string[], size=sizeof string, bool:pack=false) */
static cell AMX_NATIVE_CALL n_getstring(AMX *amx,cell *params)
{
int c,chars,max;
TCHAR *str;
cell *cptr;
CreateConsole();
max=(int)params[2];
if (max<=0)
return 0;
if (params[3])
max*=sizeof(cell);
chars=0;
str=(TCHAR *)alloca(max*sizeof(TCHAR));
if (str!=NULL) {
c=amx_getch();
while (c!=EOF && c!=EOL_CHAR && chars<max-1) {
str[chars++]=(TCHAR)c;
#if defined(SUPPRESS_ECHO)
/* For Mac OS X, non-Curses, don't echo the character */
#else
amx_putchar((TCHAR)c);
amx_fflush();
#endif
if (chars<max-1)
c=amx_getch();
} /* while */
if (c==EOL_CHAR)
amx_putchar('\n');
assert(chars<max);
str[chars]='\0';
amx_GetAddr(amx,params[1],&cptr);
amx_SetString(cptr,(char*)str,(int)params[3],sizeof(TCHAR)>1,max);
} /* if */
return chars;
}
static void acceptchar(int c,int *num)
{
switch (c) {
case '\b':
amx_putchar('\b');
*num-=1;
#if defined amx_putchar && (defined __BORLANDC__ || defined __WATCOMC__)
/* the backspace key does not erase the
* character, so do this explicitly */
amx_putchar(' '); /* erase */
amx_putchar('\b'); /* go back */
#endif
break;
case EOL_CHAR:
amx_putchar('\n');
*num+=1;
break;
default:
#if defined(SUPPRESS_ECHO)
/* For Mac OS X, non-Curses, don't echo the character */
#else
amx_putchar((TCHAR)c);
#endif
*num+=1;
} /* switch */
amx_fflush();
}
static int inlist(AMX *amx,int c,cell *params,int num)
{
int i, key;
for (i=0; i<num; i++) {
if (i==0) {
/* first key is passed by value, others are passed by reference */
key = (int)params[i];
} else {
cell *cptr;
amx_GetAddr(amx,params[i],&cptr);
key=(int)*cptr;
} /* if */
if (c==key || c==-key)
return key;
} /* for */
return 0;
}
static cell AMX_NATIVE_CALL n_getvalue(AMX *amx,cell *params)
{
cell value;
int base,sign,c,d;
int chars,n;
CreateConsole();
base=(int)params[1];
if (base<2 || base>36)
return 0;
chars=0;
value=0;
sign=1; /* to avoid a compiler warning (Microsoft Visual C/C++ 6.0) */
c=amx_getch();
while (c!=EOF) {
/* check for sign (if any) */
if (chars==0) {
if (c=='-') {
sign=-1;
acceptchar(c,&chars);
c=amx_getch();
} else {
sign=1;
} /* if */
} /* if */
/* check end of input */
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS
if (c=='\n' && inlist(amx,'\r',params+2,(int)params[0]/sizeof(cell)-1)!=0)
c='\r';
#endif
if ((chars>1 || chars>0 && sign>0)
&& (n=inlist(amx,c,params+2,(int)params[0]/sizeof(cell)-1))!=0)
{
if (n>0)
acceptchar(c,&chars);
break;
} /* if */
/* get value */
d=base; /* by default, do not accept the character */
if (c>='0' && c<='9') {
d=c-'0';
} else if (c>='a' && c<='z') {
d=c-'a'+10;
} else if (c>='A' && c<='Z') {
d=c-'A'+10;
} else if (c=='\b') {
if (chars>0) {
value/=base;
acceptchar(c,&chars);
} /* if */
} /* if */
if (d<base) {
acceptchar(c,&chars);
value=value*base + d;
} /* if */
c=amx_getch();
} /* while */
return sign*value;
}
static cell AMX_NATIVE_CALL n_clrscr(AMX *amx,cell *params)
{
(void)amx;
(void)params;
CreateConsole();
amx_clrscr();
return 0;
}
static cell AMX_NATIVE_CALL n_clreol(AMX *amx,cell *params)
{
(void)amx;
(void)params;
CreateConsole();
amx_clreol();
return 0;
}
static cell AMX_NATIVE_CALL n_gotoxy(AMX *amx,cell *params)
{
(void)amx;
CreateConsole();
amx_gotoxy((int)params[1],(int)params[2]);
return 0;
}
static cell AMX_NATIVE_CALL n_wherexy(AMX *amx,cell *params)
{
cell *px,*py;
int x,y;
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;
return 0;
}
static cell AMX_NATIVE_CALL n_setattr(AMX *amx,cell *params)
{
(void)amx;
CreateConsole();
(void)amx_setattr((int)params[1],(int)params[2],(int)params[3]);
return 0;
}
static cell AMX_NATIVE_CALL n_consctrl(AMX *amx,cell *params)
{
(void)amx;
CreateConsole();
(void)amx_termctl((int)params[1],(int)params[2]);
return 0;
}
static cell AMX_NATIVE_CALL n_console(AMX *amx,cell *params)
{
(void)amx;
CreateConsole();
amx_console((int)params[1],(int)params[2],(int)params[3]);
return 0;
}
#if !defined AMXCONSOLE_NOIDLE
static AMX_DEBUG PrevIdle = NULL;
static int idxKeyPressed = -1;
static int AMXAPI amx_ConsoleIdle(AMX *amx)
{
int err=0, key;
assert(idxKeyPressed >= 0);
if (PrevIdle != NULL)
PrevIdle(amx);
if (kbhit()) {
key = getch();
amx_Push(amx, key);
err = amx_Exec(amx, NULL, idxKeyPressed);
while (err == AMX_ERR_SLEEP)
err = amx_Exec(amx, NULL, AMX_EXEC_CONT);
} /* if */
return err;
}
#endif
#if defined __cplusplus
extern "C"
#endif
const AMX_NATIVE_INFO console_Natives[] = {
{ "getchar", n_getchar },
{ "getstring", n_getstring },
{ "getvalue", n_getvalue },
{ "print", n_print },
{ "printf", n_printf },
{ "clrscr", n_clrscr },
{ "clreol", n_clreol },
{ "gotoxy", n_gotoxy },
{ "wherexy", n_wherexy },
{ "setattr", n_setattr },
{ "console", n_console },
{ "consctrl", n_consctrl },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_ConsoleInit(AMX *amx)
{
#if !defined AMXCONSOLE_NOIDLE
/* see whether there is an @keypressed() function */
if (amx_FindPublic(amx, "@keypressed", &idxKeyPressed) == AMX_ERR_NONE) {
if (amx_GetUserData(amx, AMX_USERTAG('I','d','l','e'), (void**)&PrevIdle) != AMX_ERR_NONE)
PrevIdle = NULL;
amx_SetUserData(amx, AMX_USERTAG('I','d','l','e'), amx_ConsoleIdle);
} /* if */
#endif
return amx_Register(amx, console_Natives, -1);
}
int AMXEXPORT amx_ConsoleCleanup(AMX *amx)
{
(void)amx;
#if !defined AMXCONSOLE_NOIDLE
PrevIdle = NULL;
#endif
return AMX_ERR_NONE;
}