// -*- mode:objc -*- // $Id: VT100Terminal.m,v 1.99 2006/03/03 22:30:47 ujwal Exp $ // /* ** VT100Terminal.m ** ** Copyright (c) 2002, 2003 ** ** Author: Fabian, Ujwal S. Setlur ** Initial code by Kiichi Kusama ** ** Project: iTerm ** ** Description: Implements the model class VT100 terminal. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #import #import #import #import #import #define DEBUG_ALLOC 0 /* Traditional Chinese (Big5) 1st 0xa1-0xfe 2nd 0x40-0x7e || 0xa1-0xfe Simplifed Chinese (EUC_CN) 1st 0x81-0xfe 2nd 0x40-0x7e || 0x80-0xfe */ #define UNKNOWN ('#') @implementation VT100Terminal #define iscontrol(c) ((c) <= 0x1f) #define iseuccn(c) ((c) >= 0x81 && (c) <= 0xfe) #define isbig5(c) ((c) >= 0xa1 && (c) <= 0xfe) #define issjiskanji(c) (((c) >= 0x81 && (c) <= 0x9f) || \ ((c) >= 0xe0 && (c) <= 0xef)) #define iseuckr(c) ((c) >= 0xa1 && (c) <= 0xfe) #define isGBEncoding(e) ((e)==0x80000019||(e)==0x80000421|| \ (e)==0x80000631||(e)==0x80000632|| \ (e)==0x80000930) #define isBig5Encoding(e) ((e)==0x80000002||(e)==0x80000423|| \ (e)==0x80000931||(e)==0x80000a03|| \ (e)==0x80000a06) #define isJPEncoding(e) ((e)==0x80000001||(e)==0x8||(e)==0x15) #define isSJISEncoding(e) ((e)==0x80000628||(e)==0x80000a01) #define isKREncoding(e) ((e)==0x80000422||(e)==0x80000003|| \ (e)==0x80000840||(e)==0x80000940) #define ESC 0x1b #define DEL 0x7f #define CURSOR_SET_DOWN "\033OB" #define CURSOR_SET_UP "\033OA" #define CURSOR_SET_RIGHT "\033OC" #define CURSOR_SET_LEFT "\033OD" #define CURSOR_RESET_DOWN "\033[B" #define CURSOR_RESET_UP "\033[A" #define CURSOR_RESET_RIGHT "\033[C" #define CURSOR_RESET_LEFT "\033[D" #define CURSOR_MOD_DOWN "\033[%dB" #define CURSOR_MOD_UP "\033[%dA" #define CURSOR_MOD_RIGHT "\033[%dC" #define CURSOR_MOD_LEFT "\033[%dD" #define KEY_INSERT "\033[2~" #define KEY_PAGE_UP "\033[5~" #define KEY_PAGE_DOWN "\033[6~" #define KEY_HOME "\033[1~" #define KEY_END "\033[4~" #define KEY_DEL "\033[3~" #define KEY_BACKSPACE "\010" #define KEY_PF1 "\033OP" #define KEY_PF2 "\033OQ" #define KEY_PF3 "\033OR" #define KEY_PF4 "\033OS" #define ALT_KP_0 "\033Op" #define ALT_KP_1 "\033Oq" #define ALT_KP_2 "\033Or" #define ALT_KP_3 "\033Os" #define ALT_KP_4 "\033Ot" #define ALT_KP_5 "\033Ou" #define ALT_KP_6 "\033Ov" #define ALT_KP_7 "\033Ow" #define ALT_KP_8 "\033Ox" #define ALT_KP_9 "\033Oy" #define ALT_KP_MINUS "\033Om" #define ALT_KP_PLUS "\033Ol" #define ALT_KP_PERIOD "\033On" #define ALT_KP_ENTER "\033OM" #define KEY_FUNCTION_FORMAT "\033[%d~" #define REPORT_POSITION "\033[%d;%dR" #define REPORT_STATUS "\033[0n" // Device Attribute : VT100 with Advanced Video Option #define REPORT_WHATAREYOU "\033[?1;2c" // Secondary Device Attribute: VT100 #define REPORT_SDA "\033[1;0;0c" #define REPORT_VT52 "\033/Z" #define MOUSE_REPORT_FORMAT "\033[M%c%c%c" #define conststr_sizeof(n) ((sizeof(n)) - 1) typedef struct { int p[VT100CSIPARAM_MAX]; int count; int cmd; BOOL question; int modifier; } CSIParam; // functions static BOOL isCSI(unsigned char *, size_t); static BOOL isXTERM(unsigned char *, size_t); static BOOL isString(unsigned char *, NSStringEncoding); static size_t getCSIParam(unsigned char *, size_t, CSIParam *, VT100Screen *); static VT100TCC decode_csi(unsigned char *, size_t, size_t *,VT100Screen *); static VT100TCC decode_xterm(unsigned char *, size_t, size_t *,NSStringEncoding); static VT100TCC decode_other(unsigned char *, size_t, size_t *); static VT100TCC decode_control(unsigned char *, size_t, size_t *,NSStringEncoding,VT100Screen *); static int utf8_reqbyte(unsigned char); static VT100TCC decode_utf8(unsigned char *, size_t, size_t *); static VT100TCC decode_euccn(unsigned char *, size_t, size_t *); static VT100TCC decode_big5(unsigned char *,size_t, size_t *); static VT100TCC decode_string(unsigned char *, size_t, size_t *, NSStringEncoding); static BOOL isCSI(unsigned char *code, size_t len) { if (len >= 2 && code[0] == ESC && (code[1] == '[')) return YES; return NO; } static BOOL isXTERM(unsigned char *code, size_t len) { if (len >= 2 && code[0] == ESC && (code[1] == ']')) return YES; return NO; } static BOOL isString(unsigned char *code, NSStringEncoding encoding) { BOOL result = NO; // NSLog(@"%@",[NSString localizedNameOfStringEncoding:encoding]); if (isascii(*code)) { result = YES; } else if (encoding== NSUTF8StringEncoding) { if (*code >= 0x80) result = YES; } else if (isGBEncoding(encoding)) { if (iseuccn(*code)) result = YES; } else if (isBig5Encoding(encoding)) { if (isbig5(*code)) result = YES; } else if (isJPEncoding(encoding)) { if (*code ==0x8e || *code==0x8f|| (*code>=0xa1&&*code<=0xfe)) result = YES; } else if (isSJISEncoding(encoding)) { if (*code >= 0x80) result = YES; } else if (isKREncoding(encoding)) { if (iseuckr(*code)) result = YES; } else if (*code>=0x20) { result = YES; } return result; } static size_t getCSIParam(unsigned char *datap, size_t datalen, CSIParam *param, VT100Screen *SCREEN) { int i; BOOL unrecognized=NO; unsigned char *orgp = datap; BOOL readNumericParameter = NO; NSCParameterAssert(datap != NULL); NSCParameterAssert(datalen >= 2); NSCParameterAssert(param != NULL); param->count = 0; param->cmd = 0; for (i = 0; i < VT100CSIPARAM_MAX; ++i ) param->p[i] = -1; NSCParameterAssert(datap[0] == ESC); NSCParameterAssert(datap[1] == '['); datap += 2; datalen -= 2; if (datalen > 0 && *datap == '?') { param->question = YES; datap ++; datalen --; } // check for secondsry device attribute modifier else if (datalen > 0 && *datap == '>') { param->modifier = '>'; param->question = NO; datap++; datalen--; } else param->question = NO; while (datalen > 0) { if (isdigit(*datap)) { int n = *datap - '0'; datap++; datalen--; while (datalen > 0 && isdigit(*datap)) { n = n * 10 + *datap - '0'; datap++; datalen--; } //if (param->count == 0 ) //param->count = 1; //param->p[param->count - 1] = n; if(param->count < VT100CSIPARAM_MAX) param->p[param->count] = n; // increment the parameter count param->count++; // set the numeric parameter flag readNumericParameter = YES; } else if (*datap == ';') { datap++; datalen--; // If we got an implied (blank) parameter, increment the parameter count again if(readNumericParameter == NO) param->count++; // reset the parameter flag readNumericParameter = NO; if (param->count >= VT100CSIPARAM_MAX) { // broken //param->cmd = 0xff; unrecognized=YES; //break; } } else if (isalpha(*datap)||*datap=='@') { datalen--; param->cmd = unrecognized?0xff:*datap; datap++; break; } else if (*datap=='\'') { datap++; datalen--; switch (*datap) { case 'z': case '|': case 'w': NSLog(@"Unsupported locator sequence"); param->cmd=0xff; datap++; datalen--; break; default: NSLog(@"Unrecognized locator sequence"); datap++; datalen--; param->cmd=0xff; break; } break; } else if (*datap=='&') { datap++; datalen--; switch (*datap) { case 'w': NSLog(@"Unsupported locator sequence"); param->cmd=0xff; datap++; datalen--; break; default: NSLog(@"Unrecognized locator sequence"); datap++; datalen--; param->cmd=0xff; break; } break; } else { switch (*datap) { case VT100CC_ENQ: break; case VT100CC_BEL: [SCREEN activateBell]; break; case VT100CC_BS: [SCREEN backSpace]; break; case VT100CC_HT: [SCREEN setTab]; break; case VT100CC_LF: case VT100CC_VT: case VT100CC_FF: [SCREEN setNewLine]; break; case VT100CC_CR: [SCREEN cursorToX:1 Y:[SCREEN cursorY]]; break; case VT100CC_SO: break; case VT100CC_SI: break; case VT100CC_DC1: break; case VT100CC_DC3: break; case VT100CC_CAN: case VT100CC_SUB: break; case VT100CC_DEL: [SCREEN deleteCharacters:1];break; default: NSLog(@"Unrecognized escape sequence: %c (0x%x)", *datap, *datap); param->cmd=0xff; unrecognized=YES; break; } if(unrecognized == NO) { datalen--; datap++; } } if (unrecognized) break; } return datap - orgp; } #define SET_PARAM_DEFAULT(pm,n,d) \ (((pm).p[(n)] = (pm).p[(n)] < 0 ? (d):(pm).p[(n)]), \ ((pm).count = (pm).count > (n) + 1 ? (pm).count : (n) + 1 )) static VT100TCC decode_csi(unsigned char *datap, size_t datalen, size_t *rmlen,VT100Screen *SCREEN) { VT100TCC result; CSIParam param; size_t paramlen; int i; paramlen = getCSIParam(datap, datalen, ¶m, SCREEN); // Check for unkown if(param.cmd == 0xff) { result.type = VT100_UNKNOWNCHAR; *rmlen = paramlen; } // process else if (paramlen > 0 && param.cmd > 0) { if (!param.question) { switch (param.cmd) { case 'D': // Cursor Backward result.type = VT100CSI_CUB; SET_PARAM_DEFAULT(param, 0, 1); break; case 'B': // Cursor Down result.type = VT100CSI_CUD; SET_PARAM_DEFAULT(param, 0, 1); break; case 'C': // Cursor Forward result.type = VT100CSI_CUF; SET_PARAM_DEFAULT(param, 0, 1); break; case 'A': // Cursor Up result.type = VT100CSI_CUU; SET_PARAM_DEFAULT(param, 0, 1); break; case 'H': result.type = VT100CSI_CUP; SET_PARAM_DEFAULT(param, 0, 1); SET_PARAM_DEFAULT(param, 1, 1); break; case 'c': result.type = VT100CSI_DA; SET_PARAM_DEFAULT(param, 0, 0); break; case 'q': result.type = VT100CSI_DECLL; SET_PARAM_DEFAULT(param, 0, 0); break; case 'x': if (param.count == 1) result.type = VT100CSI_DECREQTPARM; else result.type = VT100CSI_DECREPTPARM; break; case 'r': result.type = VT100CSI_DECSTBM; SET_PARAM_DEFAULT(param, 0, 1); SET_PARAM_DEFAULT(param, 1, [SCREEN height]); break; case 'y': if (param.count == 2) result.type = VT100CSI_DECTST; else { NSLog(@"1: Unknown token %c", param.cmd); result.type = VT100_NOTSUPPORT; } break; case 'n': result.type = VT100CSI_DSR; SET_PARAM_DEFAULT(param, 0, 0); break; case 'J': result.type = VT100CSI_ED; SET_PARAM_DEFAULT(param, 0, 0); break; case 'K': result.type = VT100CSI_EL; SET_PARAM_DEFAULT(param, 0, 0); break; case 'f': result.type = VT100CSI_HVP; SET_PARAM_DEFAULT(param, 0, 1); SET_PARAM_DEFAULT(param, 1, 1); break; case 'l': result.type = VT100CSI_RM; break; case 'm': result.type = VT100CSI_SGR; for (i = 0; i < param.count; ++i) { SET_PARAM_DEFAULT(param, i, 0); // NSLog(@"m[%d]=%d",i,param.p[i]); } break; case 'h': result.type = VT100CSI_SM; break; case 'g': result.type = VT100CSI_TBC; SET_PARAM_DEFAULT(param, 0, 0); break; // these are xterm controls case '@': result.type = XTERMCC_INSBLNK; SET_PARAM_DEFAULT(param,0,1); break; case 'L': result.type = XTERMCC_INSLN; SET_PARAM_DEFAULT(param,0,1); break; case 'P': result.type = XTERMCC_DELCH; SET_PARAM_DEFAULT(param,0,1); break; case 'M': result.type = XTERMCC_DELLN; SET_PARAM_DEFAULT(param,0,1); break; // ANSI case 'G': result.type = ANSICSI_CHA; SET_PARAM_DEFAULT(param,0,1); break; case 'd': result.type = ANSICSI_VPA; SET_PARAM_DEFAULT(param,0,1); break; case 'e': result.type = ANSICSI_VPR; SET_PARAM_DEFAULT(param,0,1); break; case 'X': result.type = ANSICSI_ECH; SET_PARAM_DEFAULT(param,0,1); break; case 'i': result.type = ANSICSI_PRINT; SET_PARAM_DEFAULT(param,0,0); break; default: NSLog(@"2: Unknown token (%c); %s", param.cmd, datap); result.type = VT100_NOTSUPPORT; break; } } else { switch (param.cmd) { case 'h': // Dec private mode set result.type = VT100CSI_DECSET; SET_PARAM_DEFAULT(param, 0, 0); break; case 'l': // Dec private mode reset result.type = VT100CSI_DECRST; SET_PARAM_DEFAULT(param, 0, 0); break; default: NSLog(@"3: Unknown token %c", param.cmd); result.type = VT100_NOTSUPPORT; break; } } // copy CSI parameter for (i = 0; i < VT100CSIPARAM_MAX; ++i) result.u.csi.p[i] = param.p[i]; result.u.csi.count = param.count; result.u.csi.question = param.question; result.u.csi.modifier = param.modifier; *rmlen = paramlen; } else { result.type = VT100_WAIT; } return result; } static VT100TCC decode_xterm(unsigned char *datap, size_t datalen, size_t *rmlen, NSStringEncoding enc) { int mode=0; VT100TCC result; NSData *data; BOOL unrecognized=NO; char s[1024]={0}, *c=nil; NSCParameterAssert(datap != NULL); NSCParameterAssert(datalen >= 2); NSCParameterAssert(datap[0] == ESC); NSCParameterAssert(datap[1] == ']'); datap += 2; datalen -= 2; *rmlen=2; while (isdigit(*datap)) { int n = *datap++ - '0'; datalen--; (*rmlen)++; while (datalen > 0 && isdigit(*datap)) { n = n * 10 + *datap - '0'; (*rmlen)++; datap++; datalen--; } mode=n; } if (datalen>0) { if (*datap != ';') { unrecognized=YES; } else { c=s; datalen--; datap++; (*rmlen)++; while (*datap!=0x007&&c-s0) { *c=*datap; datalen--; datap++; (*rmlen)++; c++; } if (*datap!=0x007) { if (datalen>0) unrecognized=YES; else { *rmlen=0; } } else { *datap++; datalen--; (*rmlen)++; } } } else { *rmlen=0; } if (unrecognized||!(*rmlen)) { result.type = VT100_WAIT; NSLog(@"invalid: %d",*rmlen); } else { data = [NSData dataWithBytes:s length:c-s]; result.u.string = [[[NSString alloc] initWithData:data encoding:enc] autorelease]; switch (mode) { case 0: result.type = XTERMCC_WINICON_TITLE; break; case 1: result.type = XTERMCC_ICON_TITLE; break; case 2: default: result.type = XTERMCC_WIN_TITLE; break; } // NSLog(@"result: %d[%@],%d",result.type,result.u.string,*rmlen); } return result; } static VT100TCC decode_other(unsigned char *datap, size_t datalen, size_t *rmlen) { VT100TCC result; int c1, c2, c3; NSCParameterAssert(datap[0] == ESC); NSCParameterAssert(datalen > 1); c1 = (datalen >= 2 ? datap[1]: -1); c2 = (datalen >= 3 ? datap[2]: -1); c3 = (datalen >= 4 ? datap[3]: -1); switch (c1) { case '#': if (c2 < 0) { result.type = VT100_WAIT; } else { switch (c2) { case '8': result.type=VT100CSI_DECALN; break; default: NSLog(@"4: Unknown token ESC # %c", c2); result.type = VT100_NOTSUPPORT; } *rmlen = 3; } break; case '=': result.type = VT100CSI_DECKPAM; *rmlen = 2; break; case '>': result.type = VT100CSI_DECKPNM; *rmlen = 2; break; case '<': result.type = STRICT_ANSI_MODE; *rmlen = 2; break; case '(': if (c2 < 0) { result.type = VT100_WAIT; } else { result.type = VT100CSI_SCS0; result.u.code=c2; *rmlen = 3; } break; case ')': if (c2 < 0) { result.type = VT100_WAIT; } else { result.type = VT100CSI_SCS1; result.u.code=c2; *rmlen = 3; } break; case '*': if (c2 < 0) { result.type = VT100_WAIT; } else { result.type = VT100CSI_SCS2; result.u.code=c2; *rmlen = 3; } break; case '+': if (c2 < 0) { result.type = VT100_WAIT; } else { result.type = VT100CSI_SCS3; result.u.code=c2; *rmlen = 3; } break; case '8': result.type = VT100CSI_DECRC; *rmlen = 2; break; case '7': result.type = VT100CSI_DECSC; *rmlen = 2; break; case 'D': result.type = VT100CSI_IND; *rmlen = 2; break; case 'E': result.type = VT100CSI_NEL; *rmlen = 2; break; case 'H': result.type = VT100CSI_HTS; *rmlen = 2; break; case 'M': result.type = VT100CSI_RI; *rmlen = 2; break; case 'Z': result.type = VT100CSI_DECID; *rmlen = 2; break; case 'c': result.type = VT100CSI_RIS; *rmlen = 2; break; default: NSLog(@"5: Unknown token %c(%x)", c1, c1); result.type = VT100_NOTSUPPORT; *rmlen = 2; break; } return result; } static VT100TCC decode_control(unsigned char *datap, size_t datalen, size_t *rmlen, NSStringEncoding enc, VT100Screen *SCREEN) { VT100TCC result; if (isCSI(datap, datalen)) { result = decode_csi(datap, datalen, rmlen, SCREEN); } else if (isXTERM(datap,datalen)) { result = decode_xterm(datap,datalen,rmlen,enc); } else { NSCParameterAssert(datalen > 0); switch ( *datap ) { case VT100CC_NULL: result.type = VT100_SKIP; *rmlen = 0; while (datalen > 0 && *datap == '\0') { ++datap; --datalen; ++ *rmlen; } break; case VT100CC_ESC: if (datalen == 1) { result.type = VT100_WAIT; } else { result = decode_other(datap, datalen, rmlen); } break; default: result.type = *datap; *rmlen = 1; break; } } return result; } static int utf8_reqbyte(unsigned char f) { int result; if (isascii(f)) result = 1; else if ((f & 0xe0) == 0xc0) result = 2; else if ((f & 0xf0) == 0xe0) result = 3; else if ((f & 0xf8) == 0xf0) result = 4; else if ((f & 0xfc) == 0xf8) result = 5; else if ((f & 0xfe) == 0xfc) result = 6; else result = 0; return result; } static VT100TCC decode_utf8(unsigned char *datap, size_t datalen , size_t *rmlen) { VT100TCC result; unsigned char *p = datap; size_t len = datalen; int reqbyte; while (len > 0) { if (*p>=0x80) { reqbyte = utf8_reqbyte(*datap); if (reqbyte > 0) { if (len >= reqbyte) { p += reqbyte; len -= reqbyte; } else break; } else { NSLog(@"unknown code in UTF8: %d(%c)",*p,*p); *p=UNKNOWN; p++; len--; } } else if (*p>= 0x20 && *p <= 0x7f) { ++p; --len; } else break; } if (len == datalen) { *rmlen = 0; result.type = VT100_WAIT; } else { *rmlen = datalen - len; result.type = VT100_STRING; } return result; } static VT100TCC decode_euccn(unsigned char *datap, size_t datalen, size_t *rmlen) { VT100TCC result; unsigned char *p = datap; size_t len = datalen; while (len > 0) { if (iseuccn(*p)&&len>1) { if ((*(p+1)>=0x40&&*(p+1)<=0x7e)||*(p+1)>=0x80&&*(p+1)<=0xfe) { p += 2; len -= 2; } else { *p=UNKNOWN; p++; len--; } } else if (*p>= 0x20 && *p <= 0x7f) { ++p; --len; } else break; } if (len == datalen) { *rmlen = 0; result.type = VT100_WAIT; } else { *rmlen = datalen - len; result.type = VT100_STRING; } return result; } static VT100TCC decode_big5(unsigned char *datap, size_t datalen, size_t *rmlen) { VT100TCC result; unsigned char *p = datap; size_t len = datalen; while (len > 0) { if (isbig5(*p)&&len>1) { if ((*(p+1)>=0x40&&*(p+1)<=0x7e)||*(p+1)>=0xa1&&*(p+1)<=0xfe) { p += 2; len -= 2; } else { *p=UNKNOWN; p++; len--; } } else if (*p>= 0x20 && *p <= 0x7f) { ++p; --len; } else break; } if (len == datalen) { *rmlen = 0; result.type = VT100_WAIT; } else { *rmlen = datalen - len; result.type = VT100_STRING; } return result; } static VT100TCC decode_euc_jp(unsigned char *datap, size_t datalen , size_t *rmlen) { VT100TCC result; unsigned char *p = datap; size_t len = datalen; while (len > 0) { if (len > 1 && *p == 0x8e) { p += 2; len -= 2; } else if (len > 2 && *p == 0x8f ) { p += 3; len -= 3; } else if (len > 1 && *p >= 0xa1 && *p <= 0xfe ) { p += 2; len -= 2; } else if (*p>= 0x20 && *p <= 0x7f) { ++p; --len; } else break; } if (len == datalen) { *rmlen = 0; result.type = VT100_WAIT; } else { *rmlen = datalen - len; result.type = VT100_STRING; } return result; } static VT100TCC decode_sjis(unsigned char *datap, size_t datalen , size_t *rmlen) { VT100TCC result; unsigned char *p = datap; size_t len = datalen; while (len > 0) { if (issjiskanji(*p)&&len>1) { p += 2; len -= 2; } else if (*p>=0x20) { p++; len--; } else break; } if (len == datalen) { *rmlen = 0; result.type = VT100_WAIT; } else { *rmlen = datalen - len; result.type = VT100_STRING; } return result; } static VT100TCC decode_euckr(unsigned char *datap, size_t datalen, size_t *rmlen) { VT100TCC result; unsigned char *p = datap; size_t len = datalen; while (len > 0) { if (iseuckr(*p)&&len>1) { p += 2; len -= 2; } else if (*p>= 0x20 && *p <= 0x7f) { ++p; --len; } else break; } if (len == datalen) { *rmlen = 0; result.type = VT100_WAIT; } else { *rmlen = datalen - len; result.type = VT100_STRING; } return result; } static VT100TCC decode_other_enc(unsigned char *datap, size_t datalen, size_t *rmlen) { VT100TCC result; unsigned char *p = datap; size_t len = datalen; while (len > 0) { if (*p>=0x20) { p++; len--; } else break; } if (len == datalen) { *rmlen = 0; result.type = VT100_WAIT; } else { *rmlen = datalen - len; result.type = VT100_STRING; } return result; } static VT100TCC decode_string(unsigned char *datap, size_t datalen, size_t *rmlen, NSStringEncoding encoding) { VT100TCC result; *rmlen = 0; result.type = VT100_UNKNOWNCHAR; result.u.code = datap[0]; // NSLog(@"data: %@",[NSData dataWithBytes:datap length:datalen]); if (encoding == NSUTF8StringEncoding) { result = decode_utf8(datap, datalen, rmlen); } else if (isGBEncoding(encoding)) { // NSLog(@"Chinese-GB!"); result = decode_euccn(datap, datalen, rmlen); } else if (isBig5Encoding(encoding)) { result = decode_big5(datap, datalen, rmlen); } else if (isJPEncoding(encoding)) { // NSLog(@"decoding euc-jp"); result = decode_euc_jp(datap, datalen, rmlen); } else if (isSJISEncoding(encoding)) { // NSLog(@"decoding j-jis"); result = decode_sjis(datap, datalen, rmlen); } else if (isKREncoding(encoding)) { // NSLog(@"decoding korean"); result = decode_euckr(datap, datalen, rmlen); } else { // NSLog(@"%s(%d):decode_string()-support character encoding(%@d)", // __FILE__, __LINE__, [NSString localizedNameOfStringEncoding:encoding]); result = decode_other_enc(datap, datalen, rmlen); } if (result.type != VT100_WAIT) { /*data = [NSData dataWithBytes:datap length:*rmlen]; result.u.string = [[[NSString alloc] initWithData:data encoding:encoding] autorelease]; */ result.u.string =[[[NSString alloc] initWithBytes:datap length:*rmlen encoding:encoding] autorelease]; if (result.u.string==nil) { /*int i; NSLog(@"Null:%@",data); for(i=0;i<*rmlen;i++) datap[i]=UNKNOWN; result.u.string = [[[NSString alloc] initWithCString:datap length:*rmlen] autorelease];*/ NSLog(@"Null(%d bytes)",*rmlen); *rmlen = 0; result.type = VT100_UNKNOWNCHAR; result.u.code = datap[0]; } } return result; } + (void)initialize { } - (id)init:(PseudoTerminal *) parent { #if DEBUG_ALLOC NSLog(@"%s: 0x%x", __PRETTY_FUNCTION__, self); #endif if ([super init] == nil) return nil; ENCODING = NSASCIIStringEncoding; STREAM = [[NSMutableData alloc] init]; streamLock = [[NSLock alloc] init]; LINE_MODE = NO; CURSOR_MODE = NO; COLUMN_MODE = NO; SCROLL_MODE = NO; SCREEN_MODE = NO; ORIGIN_MODE = NO; WRAPAROUND_MODE = YES; AUTOREPEAT_MODE = NO; INTERLACE_MODE = NO; KEYPAD_MODE = NO; INSERT_MODE = NO; saveCHARSET=CHARSET = NO; XON = YES; bold=blink=reversed=under=0; saveBold=saveBlink=saveReversed=saveUnder = 0; highlight = saveHighlight = NO; FG_COLORCODE = DEFAULT_FG_COLOR_CODE; BG_COLORCODE = DEFAULT_BG_COLOR_CODE; MOUSE_MODE = MOUSE_REPORTING_NONE; TRACE = NO; strictAnsiMode = NO; allowColumnMode = YES; streamOffset = 0; numLock = YES; PARENT=parent; return self; } - (void)dealloc { #if DEBUG_ALLOC NSLog(@"%s: 0x%x", __PRETTY_FUNCTION__, self); #endif [STREAM release]; [streamLock unlock]; [streamLock release]; [super dealloc]; } - (BOOL)trace { return TRACE; } - (void)setTrace:(BOOL)flag { TRACE = flag; } - (BOOL)strictAnsiMode { return (strictAnsiMode); } - (void)setStrictAnsiMode: (BOOL)flag { strictAnsiMode = flag; } - (BOOL)allowColumnMode { return (allowColumnMode); } - (void)setAllowColumnMode: (BOOL)flag { allowColumnMode = flag; } - (NSStringEncoding)encoding { return ENCODING; } - (void)setEncoding:(NSStringEncoding)encoding { ENCODING = encoding; } - (void)cleanStream { [streamLock lock]; [STREAM autorelease]; STREAM = [[NSMutableData data] retain]; [streamLock unlock]; } - (void)putStreamData:(char *)data length: (int)length { [streamLock lock]; if([STREAM length] == 0) streamOffset = 0; [STREAM appendBytes:data length:length]; [streamLock unlock]; } - (VT100TCC)getNextToken { unsigned char *datap; size_t datalen; VT100TCC result; // acquire lock [streamLock lock]; #if 0 NSLog(@"buffer data = %@", STREAM); #endif // get our current position in the stream datap = (unsigned char *)[STREAM bytes] + streamOffset; datalen = (size_t)[STREAM length] - streamOffset; if (datalen == 0) { result.type = VT100CC_NULL; result.length = 0; // We are done with this stream. Get rid of it and allocate a new one // to avoid allowing this to grow too big. streamOffset = 0; [STREAM release]; STREAM = nil; STREAM = [[NSMutableData alloc] init]; } else { size_t rmlen = 0; if (iscontrol(datap[0])) { result = decode_control(datap, datalen, &rmlen, ENCODING, SCREEN); } else { if (isString(datap,ENCODING)) { result = decode_string(datap, datalen, &rmlen, ENCODING); if(result.type != VT100_WAIT && rmlen == 0) { result.type = VT100_UNKNOWNCHAR; result.u.code = datap[0]; rmlen = 1; } } else { result.type = VT100_UNKNOWNCHAR; result.u.code = datap[0]; rmlen = 1; } } result.length = rmlen; result.position = datap; if (rmlen > 0) { NSParameterAssert(datalen - rmlen >= 0); if (TRACE && result.type == VT100_UNKNOWNCHAR) { // NSLog(@"INPUT-BUFFER %@, read %d byte, type %d", // STREAM, rmlen, result.type); } // mark our current position in the stream streamOffset += rmlen; } } [self _setMode:result]; [self _setCharAttr:result]; // release lock [streamLock unlock]; return result; } - (NSData *)keyArrowUp:(unsigned int)modflag { int mod=0; static char buf[20]; if ((modflag&NSControlKeyMask) && (modflag&NSShiftKeyMask)) mod=6; else if (modflag&NSControlKeyMask) mod=5; else if (modflag&NSShiftKeyMask) mod=2; if (mod) { sprintf(buf,CURSOR_MOD_UP,mod); return [NSData dataWithBytes:buf length:strlen(buf)]; } else { if (CURSOR_MODE) return [NSData dataWithBytes:CURSOR_SET_UP length:conststr_sizeof(CURSOR_SET_UP)]; else return [NSData dataWithBytes:CURSOR_RESET_UP length:conststr_sizeof(CURSOR_RESET_UP)]; } } - (NSData *)keyArrowDown:(unsigned int)modflag { int mod=0; static char buf[20]; if ((modflag&NSControlKeyMask) && (modflag&NSShiftKeyMask)) mod=6; else if (modflag&NSControlKeyMask) mod=5; else if (modflag&NSShiftKeyMask) mod=2; if (mod) { sprintf(buf,CURSOR_MOD_DOWN,mod); return [NSData dataWithBytes:buf length:strlen(buf)]; } else { if (CURSOR_MODE) return [NSData dataWithBytes:CURSOR_SET_DOWN length:conststr_sizeof(CURSOR_SET_DOWN)]; else return [NSData dataWithBytes:CURSOR_RESET_DOWN length:conststr_sizeof(CURSOR_RESET_DOWN)]; } } - (NSData *)keyArrowLeft:(unsigned int)modflag { int mod=0; static char buf[20]; if ((modflag&NSControlKeyMask) && (modflag&NSShiftKeyMask)) mod=6; else if (modflag&NSControlKeyMask) mod=5; else if (modflag&NSShiftKeyMask) mod=2; if (mod) { sprintf(buf,CURSOR_MOD_LEFT,mod); return [NSData dataWithBytes:buf length:strlen(buf)]; } else { if (CURSOR_MODE) return [NSData dataWithBytes:CURSOR_SET_LEFT length:conststr_sizeof(CURSOR_SET_LEFT)]; else return [NSData dataWithBytes:CURSOR_RESET_LEFT length:conststr_sizeof(CURSOR_RESET_LEFT)]; } } - (NSData *)keyArrowRight:(unsigned int)modflag { int mod=0; static char buf[20]; if ((modflag&NSControlKeyMask) && (modflag&NSShiftKeyMask)) mod=6; else if (modflag&NSControlKeyMask) mod=5; else if (modflag&NSShiftKeyMask) mod=2; if (mod) { sprintf(buf,CURSOR_MOD_RIGHT,mod); return [NSData dataWithBytes:buf length:strlen(buf)]; } else { if (CURSOR_MODE) return [NSData dataWithBytes:CURSOR_SET_RIGHT length:conststr_sizeof(CURSOR_SET_RIGHT)]; else return [NSData dataWithBytes:CURSOR_RESET_RIGHT length:conststr_sizeof(CURSOR_RESET_RIGHT)]; } } - (NSData *)keyInsert { return [NSData dataWithBytes:KEY_INSERT length:conststr_sizeof(KEY_INSERT)]; } - (NSData *)keyHome { return [NSData dataWithBytes:KEY_HOME length:conststr_sizeof(KEY_HOME)]; } - (NSData *)keyDelete { // unsigned char del = 0x7f; // return [NSData dataWithBytes:&del length:1]; return [NSData dataWithBytes:KEY_DEL length:conststr_sizeof(KEY_DEL)]; } - (NSData *)keyBackspace { return [NSData dataWithBytes:KEY_BACKSPACE length:conststr_sizeof(KEY_BACKSPACE)]; } - (NSData *)keyEnd { return [NSData dataWithBytes:KEY_END length:conststr_sizeof(KEY_END)]; } - (NSData *)keyPageUp { return [NSData dataWithBytes:KEY_PAGE_UP length:conststr_sizeof(KEY_PAGE_UP)]; } - (NSData *)keyPageDown { return [NSData dataWithBytes:KEY_PAGE_DOWN length:conststr_sizeof(KEY_PAGE_DOWN)]; } // Reference: http://www.utexas.edu/cc/faqs/unix/VT200-function-keys.html // http://www.cs.utk.edu/~shuford/terminal/misc_old_terminals_news.txt - (NSData *)keyFunction:(int)no { char str[256]; size_t len; if (no <= 5) { sprintf(str, KEY_FUNCTION_FORMAT, no + 10); } else if (no <= 10) sprintf(str, KEY_FUNCTION_FORMAT, no + 11); else if (no <= 14) sprintf(str, KEY_FUNCTION_FORMAT, no + 12); else if (no <= 16) sprintf(str, KEY_FUNCTION_FORMAT, no + 13); else if (no <= 20) sprintf(str, KEY_FUNCTION_FORMAT, no + 14); else str[0] = 0; len = strlen(str); return [NSData dataWithBytes:str length:len]; } - (NSData *)keyPFn: (int) n { NSData *theData; switch (n) { case 4: theData = [NSData dataWithBytes:KEY_PF4 length:conststr_sizeof(KEY_PF4)]; break; case 3: theData = [NSData dataWithBytes:KEY_PF3 length:conststr_sizeof(KEY_PF3)]; break; case 2: theData = [NSData dataWithBytes:KEY_PF2 length:conststr_sizeof(KEY_PF2)]; break; case 1: default: theData = [NSData dataWithBytes:KEY_PF1 length:conststr_sizeof(KEY_PF1)]; break; } return (theData); } - (NSData *) keypadData: (unichar) unicode keystr: (NSString *) keystr { NSData *theData = nil; // numeric keypad mode if(![self keypadMode]) return ([keystr dataUsingEncoding:NSUTF8StringEncoding]); // alternate keypad mode switch (unicode) { case '0': theData = [NSData dataWithBytes:ALT_KP_0 length:conststr_sizeof(ALT_KP_0)]; break; case '1': theData = [NSData dataWithBytes:ALT_KP_1 length:conststr_sizeof(ALT_KP_1)]; break; case '2': theData = [NSData dataWithBytes:ALT_KP_2 length:conststr_sizeof(ALT_KP_2)]; break; case '3': theData = [NSData dataWithBytes:ALT_KP_3 length:conststr_sizeof(ALT_KP_3)]; break; case '4': theData = [NSData dataWithBytes:ALT_KP_4 length:conststr_sizeof(ALT_KP_4)]; break; case '5': theData = [NSData dataWithBytes:ALT_KP_5 length:conststr_sizeof(ALT_KP_5)]; break; case '6': theData = [NSData dataWithBytes:ALT_KP_6 length:conststr_sizeof(ALT_KP_6)]; break; case '7': theData = [NSData dataWithBytes:ALT_KP_7 length:conststr_sizeof(ALT_KP_7)]; break; case '8': theData = [NSData dataWithBytes:ALT_KP_8 length:conststr_sizeof(ALT_KP_8)]; break; case '9': theData = [NSData dataWithBytes:ALT_KP_9 length:conststr_sizeof(ALT_KP_9)]; break; case '-': theData = [NSData dataWithBytes:ALT_KP_MINUS length:conststr_sizeof(ALT_KP_MINUS)]; break; case '+': theData = [NSData dataWithBytes:ALT_KP_PLUS length:conststr_sizeof(ALT_KP_PLUS)]; break; case '.': theData = [NSData dataWithBytes:ALT_KP_PERIOD length:conststr_sizeof(ALT_KP_PERIOD)]; break; case 0x03: theData = [NSData dataWithBytes:ALT_KP_ENTER length:conststr_sizeof(ALT_KP_ENTER)]; break; default: theData = [keystr dataUsingEncoding:NSUTF8StringEncoding]; break; } return (theData); } - (NSData *)mousePress: (int)button withModifiers: (unsigned int)modflag atX: (int)x Y: (int)y { static char buf[7]; char cb; cb = button % 3; if (button > 3) cb += 64; if (modflag & NSControlKeyMask) cb += 16; if (modflag & NSShiftKeyMask) cb += 4; if (modflag & NSAlternateKeyMask) cb += 8; sprintf(buf, MOUSE_REPORT_FORMAT, 32 + cb, 32 + x + 1, 32 + y + 1); return [NSData dataWithBytes: buf length: strlen(buf)]; } - (NSData *)mouseReleaseAtX: (int)x Y: (int)y { static char buf[7]; sprintf(buf, MOUSE_REPORT_FORMAT, 32 + 3, 32 + x + 1, 32 + y + 1); return [NSData dataWithBytes: buf length: strlen(buf)]; } - (NSData *)mouseMotion: (int)button withModifiers: (unsigned int)modflag atX: (int)x Y: (int)y { static char buf[7]; char cb; cb = button % 3; if (button > 3) cb += 64; if (modflag & NSControlKeyMask) cb += 16; if (modflag & NSShiftKeyMask) cb += 4; if (modflag & NSAlternateKeyMask) cb += 8; sprintf(buf, MOUSE_REPORT_FORMAT, 32 + 32 + cb, 32 + x + 1, 32 + y + 1); return [NSData dataWithBytes: buf length: strlen(buf)]; } - (BOOL)lineMode { return LINE_MODE; } - (BOOL)cursorMode { return CURSOR_MODE; } - (BOOL)columnMode { return COLUMN_MODE; } - (BOOL)scrollMode { return SCROLL_MODE; } - (BOOL)screenMode { return SCREEN_MODE; } - (BOOL)originMode { return ORIGIN_MODE; } - (BOOL)wraparoundMode { return WRAPAROUND_MODE; } - (BOOL)autorepeatMode { return AUTOREPEAT_MODE; } - (BOOL)interlaceMode { return INTERLACE_MODE; } - (BOOL)keypadMode { return KEYPAD_MODE; } - (BOOL)insertMode { return INSERT_MODE; } - (BOOL) xon { return XON; } - (int) charset { return CHARSET; } - (mouseMode) mouseMode { return MOUSE_MODE; } - (int)foregroundColorCode { return (reversed?BG_COLORCODE:FG_COLORCODE+(highlight?1:bold)*8)+bold*BOLD_MASK+under*UNDER_MASK+blink*BLINK_MASK; } - (int)backgroundColorCode { return (reversed?FG_COLORCODE:BG_COLORCODE); } - (NSData *)reportActivePositionWithX:(int)x Y:(int)y { char buf[64]; snprintf(buf, sizeof(buf), REPORT_POSITION, y, x); return [NSData dataWithBytes:buf length:strlen(buf)]; } - (NSData *)reportStatus { return [NSData dataWithBytes:REPORT_STATUS length:conststr_sizeof(REPORT_STATUS)]; } - (NSData *)reportDeviceAttribute { return [NSData dataWithBytes:REPORT_WHATAREYOU length:conststr_sizeof(REPORT_WHATAREYOU)]; } - (NSData *)reportSecondaryDeviceAttribute { return [NSData dataWithBytes:REPORT_SDA length:conststr_sizeof(REPORT_SDA)]; } - (void)_setMode:(VT100TCC)token { BOOL mode; switch (token.type) { case VT100CSI_DECSET: case VT100CSI_DECRST: mode=(token.type == VT100CSI_DECSET); switch (token.u.csi.p[0]) { case 20: LINE_MODE = mode; break; case 1: CURSOR_MODE = mode; break; case 2: ANSI_MODE = mode; break; case 3: COLUMN_MODE = mode; break; case 4: SCROLL_MODE = mode; break; case 5: SCREEN_MODE = mode; [SCREEN setDirty]; break; case 6: ORIGIN_MODE = mode; break; case 7: WRAPAROUND_MODE = mode; break; case 8: AUTOREPEAT_MODE = mode; break; case 9: INTERLACE_MODE = mode; break; case 40: allowColumnMode = mode; break; case 47: if(mode) [SCREEN saveBuffer]; else [SCREEN restoreBuffer]; break; // alternate screen buffer mode case 1000: /* case 1001: */ /* MOUSE_REPORTING_HILITE not implemented yet */ case 1002: case 1003: if (mode) MOUSE_MODE = token.u.csi.p[0] - 1000; else MOUSE_MODE = MOUSE_REPORTING_NONE; break; } break; case VT100CSI_SM: case VT100CSI_RM: mode=(token.type == VT100CSI_SM); switch (token.u.csi.p[0]) { case 4: INSERT_MODE = mode; break; } break; case VT100CSI_DECKPAM: KEYPAD_MODE = YES; break; case VT100CSI_DECKPNM: KEYPAD_MODE = NO; break; case VT100CC_SI: CHARSET = 0; break; case VT100CC_SO: CHARSET = 1; break; case VT100CC_DC1: XON = YES; break; case VT100CC_DC3: XON = NO; break; case VT100CSI_DECRC: bold=saveBold; under=saveUnder; blink=saveBlink; reversed=saveReversed; highlight = saveHighlight; CHARSET=saveCHARSET; break; case VT100CSI_DECSC: saveBold=bold; saveUnder=under; saveBlink=blink; saveReversed=reversed; saveHighlight=highlight; saveCHARSET=CHARSET; break; } } - (void)_setCharAttr:(VT100TCC)token { if (token.type == VT100CSI_SGR) { if (token.u.csi.count == 0) { // all attribute off bold=under=blink=reversed=highlight=0; FG_COLORCODE = DEFAULT_FG_COLOR_CODE; BG_COLORCODE = DEFAULT_BG_COLOR_CODE; } else { int i; for (i = 0; i < token.u.csi.count; ++i) { int n = token.u.csi.p[i]; switch (n) { case VT100CHARATTR_ALLOFF: // all attribute off bold=under=blink=reversed=highlight=0; FG_COLORCODE = DEFAULT_FG_COLOR_CODE; BG_COLORCODE = DEFAULT_BG_COLOR_CODE; break; case VT100CHARATTR_BOLD: bold=1; break; case VT100CHARATTR_NORMAL: bold=0; break; case VT100CHARATTR_UNDER: under=1; break; case VT100CHARATTR_NOT_UNDER: under=0; break; case VT100CHARATTR_BLINK: blink=1; break; case VT100CHARATTR_STEADY: blink=0; break; case VT100CHARATTR_REVERSE: reversed=1; break; case VT100CHARATTR_POSITIVE: reversed=0; break; case VT100CHARATTR_FG_DEFAULT: FG_COLORCODE = DEFAULT_FG_COLOR_CODE; break; case VT100CHARATTR_BG_DEFAULT: BG_COLORCODE = DEFAULT_BG_COLOR_CODE; break; default: // 8 color support if (n>=VT100CHARATTR_FG_BLACK&&n<=VT100CHARATTR_FG_WHITE) { FG_COLORCODE = n - VT100CHARATTR_FG_BASE - COLORCODE_BLACK; highlight = 0; } else if (n>=VT100CHARATTR_BG_BLACK&&n<=VT100CHARATTR_BG_WHITE) { BG_COLORCODE = n - VT100CHARATTR_BG_BASE - COLORCODE_BLACK; highlight = 0; } // 16 color support if (n>=VT100CHARATTR_FG_HI_BLACK&&n<=VT100CHARATTR_FG_HI_WHITE) { FG_COLORCODE = n - VT100CHARATTR_FG_HI_BASE - COLORCODE_BLACK; highlight = 1; } else if (n>=VT100CHARATTR_BG_HI_BLACK&&n<=VT100CHARATTR_BG_HI_WHITE) { BG_COLORCODE = n - VT100CHARATTR_BG_HI_BASE - COLORCODE_BLACK; highlight = 1; } } } } } } - (void) setScreen:(VT100Screen*) sc { SCREEN=sc; } @end