/* 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-2011 * * 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: amxcons.c 4523 2011-06-21 15:03:47Z 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 #include #include #include #include #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ #define HAVE_CONIO #include #include #endif #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 #if defined __WIN32__ || defined _WIN32 || defined WIN32 #include #endif #if defined _UNICODE # include #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 _tprintf printf #endif #include "amxcons.h" #if defined AMX_TERMINAL #define EOL_CHAR '\r' #endif #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ #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_STRING_LIB #if defined AMX_TERMINAL /* required functions are implemented elsewhere */ int amx_putstr(const 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); 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); 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) { int attribs=A_NORMAL; if (highlight>0) attribs=(attribs & ~A_NORMAL) | A_STANDOUT; attrset(attribs); //??? 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" */ #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) { 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 */ } 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) { 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'; if (y!=NULL) *y=atoi(str); for (i=0; i<8 && (val=amx_getch())!='R'; i++) str[i]=(char)val; str[i]='\0'; 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 */ #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(); } 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) _tprintf("%s",(s)) #define amx_putchar(c) _puttchar(c) #define amx_fflush() fflush(stdout) #define amx_getch() getch() #define amx_gets(s,n) _fgetts(s,n,stdin) #define amx_kbhit() kbhit() 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); } int 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 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) { 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); } 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); } #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_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),(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) void CreateConsole(void) { static int createdconsole=0; if (!createdconsole) { AllocConsole(); createdconsole=1; } /* if */ } #elif defined CURSES && CURSES != 0 // The Mac OS X build variant uses curses. void CreateConsole(void) { static int createdconsole=0; if (!createdconsole) { curseswin=initscr(); cbreak(); noecho(); nonl(); scrollok(curseswin,TRUE); intrflush(curseswin,FALSE); keypad(curseswin,TRUE); createdconsole=1; } /* if */ } #else #define CreateConsole() #endif static int cons_putstr(void *dest,const TCHAR *str) { (void)dest; return amx_putstr(str); } static int cons_putchar(void *dest,TCHAR ch) { (void)dest; return amx_putchar(ch); } #endif /* AMX_STRING_LIB */ 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 *amx_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,TCHAR align,int width,TCHAR decpoint,int digits,TCHAR filler) { int i, len; cell ipart,v; TCHAR vsign=__T('\0'); /* make the value positive (but keep the sign) */ if (value<0) { value=-value; vsign=__T('-'); } /* if */ /* "prepare" the value so that when it is truncated to the requested * number of digits, the result is rounded towards the dropped digits */ assert(digits=0); assert(value>=0); /* truncate the fractional part to the requested number of digits */ for (i=FIXEDDIGITS; i>digits; i--) value/=10; string[0]=__T('\0'); /* add sign */ i=_tcslen(string); string[i]=vsign; string[i+1]=__T('\0'); /* add integer part */ amx_strval(string+_tcslen(string),(long)ipart,SV_DECIMAL,0); /* add fractional part */ if (digits>0) { i=_tcslen(string); string[i]=decpoint; amx_strval(string+i+1,(long)value,SV_DECIMAL,-digits); } /* if */ len=_tcslen(string); if (len=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,TCHAR sign,TCHAR decpoint,int width,int digits,TCHAR filler, int (*f_putstr)(void*,const TCHAR *),int (*f_putchar)(void*,TCHAR),void *user) { cell *cptr; TCHAR buffer[40]; #if defined FLOATPOINT TCHAR formatstring[40]; #endif #if !defined FIXEDPOINT && !defined FLOATPOINT (void)decpoint; #endif assert(f_putstr!=NULL); assert(f_putchar!=NULL); switch (ch) { case __T('c'): cptr=amx_Address(amx,param); width--; /* single character itself has a with of 1 */ if (sign!=__T('-')) while (width-->0) f_putchar(user,filler); f_putchar(user,(TCHAR)*cptr); while (width-->0) f_putchar(user,filler); return 1; case __T('d'): { cell value; int length=1; cptr=amx_Address(amx,param); value=*cptr; if (value<0 || sign==__T('+')) length++; if (value<0) value=-value; while (value>=10) { length++; value/=10; } /* while */ width-=length; if (sign!=__T('-')) while (width-->0) f_putchar(user,filler); amx_strval(buffer,*cptr,SV_DECIMAL,0); if (sign==__T('+') && *cptr>=0) f_putchar(user,sign); f_putstr(user,buffer); while (width-->0) f_putchar(user,filler); return 1; } /* case */ #if defined FLOATPOINT case __T('f'): /* 32-bit floating point number */ case __T('r'): /* if floating point is enabled, %r == %f */ /* build a format string */ if (digits==INT_MAX) digits=5; else if (digits>25) digits=25; _tcscpy(formatstring,__T("%")); if (sign!=__T('\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); 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 #if defined FIXEDPOINT #define FIXEDMULT 1000 case __T('q'): /* 32-bit fixed point number */ #if !defined FLOATPOINT case __T('r'): /* if fixed point is enabled, and floating point is not, %r == %q */ #endif cptr=amx_Address(amx,param); /* format the number */ if (digits==INT_MAX) digits=3; else if (digits>25) digits=25; formatfixed(buffer,*cptr,sign,width,decpoint,digits,filler); assert(_tcslen(buffer)=0x10) { length++; value>>=4; } /* while */ width-=length; if (sign!=__T('-')) while (width-->0) f_putchar(user,filler); amx_strval(buffer,(long)*cptr,SV_HEX,0); f_putstr(user,buffer); while (width-->0) f_putchar(user,filler); return 1; } /* case */ } /* switch */ /* error in the string format, try to repair */ f_putchar(user,ch); return 0; } enum { FMT_NONE, /* not in format state; accept '%' */ 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) */ }; static int formatstate(TCHAR c,int *state,TCHAR *sign,TCHAR *decpoint,int *width,int *digits,TCHAR *filler) { assert(state!=NULL && sign!=NULL && decpoint!=NULL && width!=NULL && digits!=NULL && filler!=NULL); switch (*state) { case FMT_NONE: if (c==__T('%')) { *state=FMT_START; *sign=__T('\0'); *decpoint=__T('.'); *width=0; *digits=INT_MAX; *filler=__T(' '); } else { return -1; /* print a single character */ } /* if */ break; case FMT_START: if (c==__T('+') || c==__T('-')) { *sign=c; } else if (c==__T('0')) { *filler=c; } else if (c>=__T('1') && c<=__T('9')) { *width=(int)(c-__T('0')); *state=FMT_WIDTH; } else if (c==__T('.') || c==__T(',')) { *decpoint=c; *digits=0; *state=FMT_DECIM; } else if (c==__T('%')) { *state=FMT_NONE; return -1; /* print literal '%' */ } else { return 1; /* print formatted character */ } /* if */ break; case FMT_WIDTH: if (c>=__T('0') && c<=__T('9')) { *width=*width*10+(int)(c-__T('0')); } else if (c==__T('.') || c==__T(',')) { *decpoint=c; *digits=0; *state=FMT_DECIM; } else { 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 */ } /* if */ break; } /* switch */ return 0; } int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info) { int i,paramidx=0; int fmtstate=FMT_NONE,width,digits; TCHAR sign,decpoint,filler; int (*f_putstr)(void*,const TCHAR *); int (*f_putchar)(void*,TCHAR); void *user; int skip,length; if (info!=NULL) { f_putstr=info->f_putstr; f_putchar=info->f_putchar; user=info->user; skip=info->skip; length=info->length; } else { f_putstr=NULL; f_putchar=NULL; user=NULL; skip=0; length=INT_MAX; } /* if */ #if !defined AMX_STRING_LIB if (f_putstr==NULL) f_putstr=cons_putstr; if (f_putchar==NULL) f_putchar=cons_putchar; #else assert(f_putstr!=NULL && f_putchar!=NULL); #endif /* if no placeholders appear, we can use a quicker routine */ if (info==NULL || info->params==NULL) { TCHAR cache[100]; int idx=0; 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); if (c==0) break; if (skip>0) { skip--; /* skip a number of characters */ } else { if (length--<=0) break; /* print up to a certain length */ assert(idx0) continue; assert(idx0) { cache[idx]=__T('\0'); f_putstr(user,cache); } /* if */ } else { /* 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); if (c==0) break; switch (formatstate(c,&fmtstate,&sign,&decpoint,&width,&digits,&filler)) { case -1: f_putchar(user,c); 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++; j=(j+sizeof(cell)-sizeof(char)) % sizeof(cell); } /* for */ } else { /* the string is unpacked */ for (i=0; cstr[i]!=0; i++) { switch (formatstate((TCHAR)cstr[i],&fmtstate,&sign,&decpoint,&width,&digits,&filler)) { case -1: f_putchar(user,(TCHAR)cstr[i]); 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 */ } /* if (info==NULL || info->params==NULL) */ return paramidx; } #if !defined AMX_STRING_LIB #if defined AMX_ALTPRINT /* print(const string[], start=0, end=cellmax) */ static cell AMX_NATIVE_CALL n_print(AMX *amx,const cell *params) { cell *cstr; AMX_FMTINFO info; memset(&info,0,sizeof info); info.skip= ((size_t)params[0]>=2*sizeof(cell)) ? (int)params[2] : 0; info.length= ((size_t)params[0]>=3*sizeof(cell)) ? (int)(params[3]-info.skip) : INT_MAX; CreateConsole(); cstr=amx_Address(amx,params[1]); amx_printstring(amx,cstr,&info); amx_fflush(); return 0; } #else /* print(const string[], foreground=-1, background=-1, highlight=-1) */ static cell AMX_NATIVE_CALL n_print(AMX *amx,const cell *params) { cell *cstr; int oldcolours; CreateConsole(); /* set the new colours */ oldcolours=amx_setattr((int)params[2],(int)params[3],(int)params[4]); cstr=amx_Address(amx,params[1]); amx_printstring(amx,cstr,NULL); /* reset the colours */ (void)amx_setattr(oldcolours & 0xff,(oldcolours >> 8) & 0x7f,(oldcolours >> 15) & 0x01); amx_fflush(); return 0; } #endif static cell AMX_NATIVE_CALL n_printf(AMX *amx,const cell *params) { cell *cstr; AMX_FMTINFO info; memset(&info,0,sizeof info); info.params=params+2; info.numparams=(int)(params[0]/sizeof(cell))-1; info.skip=0; info.length=INT_MAX; CreateConsole(); cstr=amx_Address(amx,params[1]); amx_printstring(amx,cstr,&info); amx_fflush(); return 0; } /* getchar(bool:echo=true) */ static cell AMX_NATIVE_CALL n_getchar(AMX *amx,const 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,const cell *params) { int c,chars,max; cell *cptr; (void)amx; CreateConsole(); chars=0; 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 */ 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,const cell *params,int num) { int i, key; (void)amx; for (i=0; i36) 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 EOL_CHAR!='\r' if (c==EOL_CHAR && 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 */ #if EOL_CHAR!='\r' if (c=='\r') c=EOL_CHAR; #endif /* 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= 0); if (PrevIdle != NULL) PrevIdle(amx, Exec); if (amx_kbhit()) { key = amx_getch(); amx_Push(amx, key); err = Exec(amx, NULL, idxKeyPressed); while (err == AMX_ERR_SLEEP) err = 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 AMXAPI 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'), (void*)amx_ConsoleIdle); } /* if */ #endif return amx_Register(amx, console_Natives, -1); } int AMXEXPORT AMXAPI amx_ConsoleCleanup(AMX *amx) { (void)amx; #if !defined AMXCONSOLE_NOIDLE PrevIdle = NULL; #endif return AMX_ERR_NONE; } #endif /* AMX_STRING_LIB */