Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / base / applications / network / telnet / src / tmapldr.cpp
diff --git a/base/applications/network/telnet/src/tmapldr.cpp b/base/applications/network/telnet/src/tmapldr.cpp
new file mode 100644 (file)
index 0000000..99b3b62
--- /dev/null
@@ -0,0 +1,763 @@
+///////////////////////////////////////////////////////////////////////////////
+//Telnet Win32 : an ANSI telnet client.
+//Copyright (C) 1998-2000 Paul Brannan
+//Copyright (C) 1998 I.Ioannou
+//Copyright (C) 1997 Brad Johnson
+//
+//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.
+//
+//I.Ioannou
+//roryt@hol.gr
+//
+///////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////
+//     Class TMapLoader - Key/Character Mappings       //
+//                      - Loads from telnet.cfg        //
+//     originally part of KeyTrans.cpp                 //
+/////////////////////////////////////////////////////////
+
+#include "precomp.h"
+
+// It's probably a good idea to turn off the "identifier was truncated" warning
+// in MSVC (Paul Brannan 5/25/98)
+#ifdef _MSC_VER
+#pragma warning(disable: 4786)
+#endif
+
+// AVS
+// skip inline comments, empty lines
+static char * getline(istream& i, char* buf, int size){
+
+       int len = 0;
+
+       while (1) {
+               memset(buf,0,size);
+               if (i.eof()) break;
+               i.getline(buf,size,'\n');
+
+               while (buf[len]) {
+                       if ( /*(buf[len]>=0) &&*/ buf[len]< ' ' ) buf[len] = ' ';
+                       len++;
+               };
+               len = 0;
+
+               // not so fast, but work ;)
+               while ( buf[len] ) {
+            if ( (buf[len] == ' ') && (buf[len+1] == ' ')) {
+                               memmove(buf+len, buf+len+1, strlen(buf+len));
+            } else len++;
+               };
+
+               if (buf[0] == ' ') memmove(buf, buf+1, size-1);
+
+               // empty or comment
+               if ((buf[0]==0)||(buf[0]==';')) continue;
+
+               len = 0; // look for comment like this one
+               while (buf[len])
+                       if ((buf[len] == '/') && (buf[len+1] == '/')) buf[len] = 0;
+                       else len++;
+
+                       if (len && (buf[len-1] == ' ')) {
+                len--;
+                buf[len]=0;
+                       };
+                       // in case for comment like this one (in line just a comment)
+                       if (buf[0]==0) continue;
+
+                       break;
+       };
+       return (buf);
+};
+
+//AVS
+// use string as FIFO queue for lines
+static int getline(string&str, char* buf, size_t sz) {
+
+       if ( !str.length() ) return 0;
+       const char * p = strchr(str.c_str(),'\n');
+       unsigned int len; // Changed to unsigned (Paul Brannan 6/23/98)
+       if ( p==NULL )
+               len = str.length();
+       else
+               len = p - str.c_str();
+
+       len = len<sz?len:sz-1;
+
+       strncpy(buf,str.c_str(), len);
+       buf[len]=0;
+       // DJGPP also uses erase rather than remove (Paul Brannan 6/23/98)
+#ifndef __BORLANDC__
+       str.erase(0, len + 1);
+#else
+       str.remove(0,len+1);
+#endif
+       return 1;
+};
+
+//AVS
+// parse \nnn and \Xhh
+static int getbyte(const char*str) {
+       unsigned char retval = 0;
+       int base = 10;
+       int readed = 0;
+
+       if ( (*str == 'x') || (*str == 'X') ) {
+               base = 16;
+               readed++;
+       };
+
+       while (readed != 3 && str[readed]) {
+               unsigned char ch = toupper(str[readed]);
+               if ( isdigit(ch) ) {
+                       retval = retval*base + (ch -'0');
+               } else if (base == 16 && ch >= 'A' && ch <= 'F') {
+                       retval = retval*base + (ch-'A'+10);
+               } else {
+                       return -1;
+               };
+               readed++;
+       };
+       // Ioannou: If we discard the 0x00 we can't undefine a key !!!
+       //  if ( retval == 0 ) {
+       //     return -1;
+       //  };
+       return retval;
+};
+
+//AVS
+// a little optimization
+DWORD Fix_ControlKeyState(char * Next_Token) {
+       if (stricmp(Next_Token, "RIGHT_ALT" ) == 0) return RIGHT_ALT_PRESSED;
+       if (stricmp(Next_Token, "LEFT_ALT"  ) == 0) return LEFT_ALT_PRESSED;
+       if (stricmp(Next_Token, "RIGHT_CTRL") == 0) return RIGHT_CTRL_PRESSED;
+       if (stricmp(Next_Token, "LEFT_CTRL" ) == 0) return LEFT_CTRL_PRESSED;
+       if (stricmp(Next_Token, "SHIFT"     ) == 0) return SHIFT_PRESSED;
+       if (stricmp(Next_Token, "NUMLOCK"   ) == 0) return NUMLOCK_ON;
+       if (stricmp(Next_Token, "SCROLLLOCK") == 0) return SCROLLLOCK_ON;
+       if (stricmp(Next_Token, "CAPSLOCK"  ) == 0) return CAPSLOCK_ON;
+       if (stricmp(Next_Token, "ENHANCED"  ) == 0) return ENHANCED_KEY;
+
+       // Paul Brannan 5/27/98
+       if (stricmp(Next_Token, "APP_KEY"   ) == 0) return APP_KEY;
+       // Paul Brannan 6/28/98
+       if (stricmp(Next_Token, "APP2_KEY"  ) == 0) return APP2_KEY;
+       // Paul Brannan 8/28/98
+       if (stricmp(Next_Token, "APP3_KEY"  ) == 0) return APP3_KEY;
+       // Paul Brannan 12/9/98
+       if (stricmp(Next_Token, "APP4_KEY"  ) == 0) return APP4_KEY;
+
+       return 0;
+}
+
+
+// AVS
+// rewrited to suppert \xhh notation, a little optimized
+char* Fix_Tok(char * tok) {
+       static char s[256];
+       int i,j,n;
+
+       // setmem is nonstandard; memset is standard (Paul Brannan 5/25/98)
+       memset(s, 0, 256);
+       //  setmem(s, 256, 0);
+       i = j = n = 0;
+       if ( tok != NULL ) {
+               for ( ; tok[i] != 0; ) {
+                       switch ( tok[i] ) {
+                       case '\\' :
+                               switch ( tok[i+1] ) {
+                               case '\\':
+                                       s[j++] = '\\';
+                                       i += 2;
+                                       break;
+                               default:
+                                       n = getbyte(tok+i+1);
+                                       if ( n < 0 )
+                                               s[j++] = tok[i++];
+                                       else {
+                                               s[j++]=n;
+                                               i += 4;
+                                       } ;
+                                       break;
+                               };
+                               break;
+                               case '^' :
+                    if ( tok[i+1] >= '@' ) {
+                                               s[j++] = tok[i+1] - '@';
+                                               i += 2;
+                                               break;
+                    }
+                               default  :
+                    s[j++] = tok[i++];
+                       }
+               }
+       }
+       return s;
+};
+
+// AVS
+// perform 'normalization' for lines like [some text], and some checks
+// maybe it will be done faster - but no time for it
+int normalizeSplitter(string& buf) {
+    if ( buf.length() <= 2 ) return 0;
+    if ( buf[0] == '[' && buf[buf.length()-1] == ']' ) {
+               while ( buf[1] == ' ' )
+                       // DJGPP also uses erase rather than remove (Paul Brannan 6/23/98)
+#ifndef __BORLANDC__
+                       buf.erase(1, 1);
+#else
+                       buf.remove(1,1);
+#endif
+               while ( buf[buf.length()-2] == ' ' )
+                       // Paul Brannan 6/23/98
+#ifndef __BORLANDC__
+                       buf.erase(buf.length()-2,1);
+#else
+                       buf.remove(buf.length()-2,1);
+#endif
+               return 1;
+    }
+    return 0;
+};
+
+// AVS
+// looking for part in string array, see Load(..) for more info
+int TMapLoader::LookForPart(stringArray& sa, const char* partType, const char* partName) {
+    if ( !sa.IsEmpty() ) {
+               string cmpbuf("[");
+               cmpbuf += partType;
+               cmpbuf += " ";
+               cmpbuf += partName;
+               cmpbuf += "]";
+               normalizeSplitter(cmpbuf); // if no parttype, [global] for example
+               int max = sa.GetItemsInContainer();
+               for ( int i = 0; i<max; i++ )
+                       // I found some strange behavior if strnicmp was used here
+            if (strnicmp(cmpbuf.c_str(),sa[i].c_str(),cmpbuf.length()) == 0)
+                               return i;
+    };
+    return INT_MAX;
+};
+
+// AVS
+// load globals to 'globals'
+// in buf must be a [global] part of input file
+int TMapLoader::LoadGlobal(string& buf) {
+
+       char wbuf[128];
+       while ( buf.length() ) {
+               wbuf[0]=0;
+               if (!getline(buf,wbuf,sizeof(wbuf))) break;
+               if ( wbuf[0]==0 ) break;
+               char* Name = strtok(wbuf, TOKEN_DELIMITERS);
+               if ( stricmp(Name, "[global]")==0 ) continue;
+
+               char* Value = strtok(NULL, TOKEN_DELIMITERS);
+               if ( Value == NULL ) {
+                       //              cerr << "[global] -> no value for " << Name << endl;
+                       printm(0, FALSE, MSG_KEYNOVAL, Name);
+                       continue;
+               };
+               int val = atoi(Value);
+               if ( val > 0 && val <= 0xff ) {
+                       if ( !KeyTrans.AddGlobalDef(val, Name)) return 0;
+               }
+               else {
+                       //             cerr << "[global] -> bad value for " << Name << endl;
+                       printm(0, FALSE, MSG_KEYBADVAL, Name);
+                       continue;
+               };
+       };
+       return 1;
+};
+
+// AVS
+// perform parsing of strings like 'VK_CODE shifts text'
+// returns text on success
+char* TMapLoader::ParseKeyDef(const char* buf, WORD& vk_code, DWORD& control) {
+       char wbuf[256];
+       strcpy(wbuf,buf);
+       char* ptr = strtok(wbuf, TOKEN_DELIMITERS);
+       if ( ptr == NULL ) return NULL;
+
+       int i = KeyTrans.LookOnGlobal(ptr);
+       if ( i == INT_MAX ) return NULL;
+
+       vk_code = KeyTrans.GetGlobalCode(i);
+
+       control = 0;
+       DWORD st;
+       while (1) {
+               ptr = strtok(NULL, TOKEN_DELIMITERS);
+               if ((ptr == NULL) || ((st = Fix_ControlKeyState(ptr)) == 0)) break;
+               control |= st;
+       };
+
+       if ( ptr == NULL ) return NULL;
+
+       return Fix_Tok(ptr);
+};
+
+// AVS
+// load keymap to current map
+// be aware - buf must passed by value, its destroyed
+int TMapLoader::LoadKeyMap(string buf) {
+
+       char wbuf[128];
+       WORD vk_code;
+       DWORD control;
+       int i;
+
+       // Paul Brannan Feb. 22, 1999
+       strcpy(wbuf, "VK_");
+       wbuf[4] = 0;
+       wbuf[3] = ini.get_escape_key();
+       i = KeyTrans.LookOnGlobal(wbuf);
+       if (i != INT_MAX) {
+               KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), RIGHT_ALT_PRESSED, TN_ESCAPE);
+               KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), LEFT_ALT_PRESSED, TN_ESCAPE);
+       }
+       wbuf[3] = ini.get_scrollback_key();
+       i = KeyTrans.LookOnGlobal(wbuf);
+       if (i != INT_MAX) {
+               KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), RIGHT_ALT_PRESSED, TN_SCROLLBACK);
+               KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), LEFT_ALT_PRESSED, TN_SCROLLBACK);
+       }
+       wbuf[3] = ini.get_dial_key();
+       i = KeyTrans.LookOnGlobal(wbuf);
+       if (i != INT_MAX) {
+               KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), RIGHT_ALT_PRESSED, TN_DIAL);
+               KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), LEFT_ALT_PRESSED, TN_DIAL);
+       }
+       KeyTrans.AddKeyDef(VK_INSERT, SHIFT_PRESSED, TN_PASTE);
+
+       while ( buf.length() ) {
+               wbuf[0] = 0;
+               if (!getline(buf,wbuf,sizeof(wbuf))) break;
+               if ( wbuf[0]==0 ) break;
+               if ( strnicmp(wbuf,"[keymap",7)==0 ) continue;
+
+               char * keydef = ParseKeyDef(wbuf,vk_code,control);
+
+               if ( keydef != NULL ) {
+
+                       // Check to see if keydef is a "special" code (Paul Brannan 3/29/00)
+                       if(!strnicmp(keydef, "\\tn_escape", strlen("\\tn_escape"))) {
+                               if(!KeyTrans.AddKeyDef(vk_code, control, TN_ESCAPE)) return 0;
+                       } else if(!strnicmp(keydef, "\\tn_scrollback", strlen("\\tn_scrollback"))) {
+                               if(!KeyTrans.AddKeyDef(vk_code, control, TN_SCROLLBACK)) return 0;
+                       } else if(!strnicmp(keydef, "\\tn_dial", strlen("\\tn_dial"))) {
+                               if(!KeyTrans.AddKeyDef(vk_code, control, TN_DIAL)) return 0;
+                       } else if(!strnicmp(keydef, "\\tn_paste", strlen("\\tn_paste"))) {
+                               if(!KeyTrans.AddKeyDef(vk_code, control, TN_PASTE)) return 0;
+                       } else if(!strnicmp(keydef, "\\tn_null", strlen("\\tn_null"))) {
+                               if(!KeyTrans.AddKeyDef(vk_code, control, TN_NULL)) return 0;
+                       } else if(!strnicmp(keydef, "\\tn_cr", strlen("\\tn_cr"))) {
+                               if(!KeyTrans.AddKeyDef(vk_code, control, TN_CR)) return 0;
+                       } else if(!strnicmp(keydef, "\\tn_crlf", strlen("\\tn_crlf"))) {
+                               if(!KeyTrans.AddKeyDef(vk_code, control, TN_CRLF)) return 0;
+                       } else
+                               if(!KeyTrans.AddKeyDef(vk_code,control,keydef)) return 0;
+                               // else DeleteKeyDef() ???? - I'm not sure...
+               }
+       };
+
+       return 1;
+};
+
+// AVS
+// load [charmap ...] part to xlat
+int TMapLoader::LoadCharMap(string buf) {
+       char wbuf[128];
+       char charmapname[128];
+       charmapname[0] = 0;
+
+       //        xlat.init(); now it done by KeyTranslator::Load()
+
+       while ( buf.length() ) {
+               wbuf[0]=0;
+               if (!getline(buf,wbuf,sizeof(wbuf))) break;
+               if ( wbuf[0]==0 ) break;
+               if ( strnicmp(wbuf,"[charmap",8)==0 ) {
+                       strcpy(charmapname,wbuf);
+                       continue;
+               };
+               char * host = strtok(wbuf, " ");
+               char * console = strtok(NULL, " ");
+
+               int bHost;
+               int bConsole;
+
+               if ( host == NULL || console == NULL ) {
+                       //              cerr << charmapname << " -> Bad structure" << endl;
+                       printm(0, FALSE, MSG_KEYBADSTRUCT, charmapname);
+                       return 0;
+               };
+               if ( strlen(host) > 1 && host[0] == '\\' )
+                       bHost = getbyte(host+1);
+               else
+                       bHost = (unsigned char)host[0];
+
+               if ( strlen(console) > 1 && console[0] == '\\' )
+                       bConsole = getbyte(console+1);
+               else
+                       bConsole = (unsigned char)console[0];
+
+               if ( bHost <= 0 || bConsole <= 0 ) {
+                       //              cerr << charmapname << " -> Bad chars? "
+                       //                   << host << " -> " << console << endl;
+                       printm(0, FALSE, MSG_KEYBADCHARS, charmapname, host, console);
+                       return 0;
+               };
+               // xlat.table[bHost] = bConsole;
+               Charmap.modmap(bHost, 'B', bConsole);
+       };
+       return (Charmap.enabled = 1);
+};
+
+// AVS
+// ignore long comment [comment] ... [end comment]
+// recursive!
+int getLongComment(istream& is, char* wbuf, size_t sz) {
+
+       int bufLen;
+    while ( is ) {
+               wbuf[0] = 0;
+               getline(is, wbuf, sz);
+               if ( wbuf[0]==0 ) return 1;
+               bufLen = strlen(wbuf);
+               if ( wbuf[0] == '[' && wbuf[bufLen-1] == ']' ) {
+                       string temps(wbuf);
+
+                       if (!normalizeSplitter(temps)) {
+                               //           cerr << "Unexpected line '" << temps << "'\n";
+                               printm(0, FALSE, MSG_KEYUNEXPLINE, temps.c_str());
+                               return 0;
+                       };
+                       if ( stricmp(temps.c_str(),"[comment]") == 0 ) {
+                               // do recursive call
+                               if ( !getLongComment(is, wbuf, sz) ) return 0;
+                               continue;
+                       };
+                       if ( stricmp(temps.c_str(),"[end comment]") == 0 ) return 1;
+               };
+    };
+       // we get a warning if we don't put a return here (Paul Brannan 5/25/98)
+       return 0;
+};
+
+// AVS
+// completelly rewrited to support new conceptions
+int TMapLoader::Load(const char * filename, const char * szActiveEmul) {
+       char buf[256];
+       int bufLen;
+
+       ifstream inpfile(filename);
+       KeyTrans.DeleteAllDefs();
+       Charmap.init();
+
+       // it is an array for store [...] ... [end ...] parts from file
+       stringArray SA(0,0,sizeof(string));
+       int AllOk = 0;
+
+       while ( inpfile ) {
+
+               getline(inpfile, buf, 255);
+               bufLen = strlen(buf);
+               if ( !bufLen ) continue;
+
+               if ( buf[0] == '[' && buf[bufLen-1] == ']' ) {
+                       // is a part splitter [...]
+                       string temps(buf);
+
+                       if (!normalizeSplitter(temps)) {
+                               printm(0, FALSE, MSG_KEYUNEXPLINE, temps.c_str());
+                               AllOk = 0;
+                               break;
+                       };
+                       // if a comment
+                       if ( stricmp(temps.c_str(),"[comment]") == 0 ) {
+#ifdef KEYDEBUG
+                               printit(temps.c_str());
+#endif
+                               if ( !getLongComment(inpfile, buf, sizeof(buf)) ) {
+                                       printm(0, FALSE, MSG_KEYUNEXPEOF);
+                                       break;
+                               };
+#ifdef KEYDEBUG
+                               printit("\r          \r");
+#endif
+                               continue;
+                       };
+
+
+                       string back = temps;
+                       // prepare line for make it as [end ...]
+                       // and check it
+                       if ( strnicmp(back.c_str(), "[global]", 8) == 0 ) {} // do nothing
+                       else if ( strnicmp(back.c_str(), "[keymap", 7) == 0 ) {
+                               // DJGPP also uses erase rather than remove (Paul Brannan 6/23/98)
+#ifndef __BORLANDC__
+                               back.erase(7);
+#else
+                               back.remove(7);
+#endif
+                               back += "]";
+                       }
+                       else if ( strnicmp(back.c_str(), "[charmap", 8) == 0 ) {
+                               // Paul Brannan 6/23/98
+#ifndef __BORLANDC__
+                               back.erase(8);
+#else
+                               back.remove(8);
+#endif
+                               back += "]";
+                       }
+                       else if ( strnicmp(back.c_str(), "[config", 7) == 0 ) {
+                               // Paul Brannan 6/23/98
+#ifndef __BORLANDC__
+                               back.erase(7);
+#else
+                               back.remove(7);
+#endif
+                               back += "]";
+                       }
+                       else {
+                               //        cerr << "Unexpected token " << back << endl;
+                               printm(0, FALSE, MSG_KEYUNEXPTOK, back.c_str());
+                               break;
+                       };
+
+                       back.insert(1,"END "); // now it looks like [END ...]
+#ifdef KEYDEBUG
+                       printit(temps.c_str());
+#endif
+
+                       int ok = 0;
+                       // fetch it to temps
+                       while ( 1 ) {
+                               getline(inpfile, buf, sizeof(buf));
+                               bufLen = strlen(buf);
+                               if ( !bufLen ) break;
+                               if ( buf[0] == '[' && buf[bufLen-1] == ']' ) {
+                                       string t(buf);
+                                       if ( !normalizeSplitter(t) ) break;
+
+                                       if ( stricmp(t.c_str(),back.c_str()) == 0 ) {
+                                               ok = 1;
+                                               break;
+                                       };
+
+                                       // AVS 31.12.97 fix [comment] block inside another block
+                                       if ( stricmp(t.c_str(),"[comment]") == 0 &&
+                                               getLongComment(inpfile, buf, sizeof(buf)) ) continue;
+
+                                       break;
+                               };
+                               temps += "\n";
+                               temps += buf;
+                       };
+                       if ( !ok ) {
+                               //         cerr << "Unexpected end of file or token" << endl;
+                               printm(0, FALSE, MSG_KEYUNEXP);
+                               AllOk = 0;
+                               break;
+                       };
+#ifdef KEYDEBUG
+                       printit("\r                                                                      \r");
+#endif
+                       AllOk = SA.Add(temps);
+                       if ( !AllOk ) break;
+               } else {
+                       //       cerr << "Unexpected line '" << buf << "'\n";
+                       printm(0, FALSE, MSG_KEYUNEXPLINE, buf);
+                       AllOk = 0;
+                       break;
+               };
+       };
+
+       inpfile.close();
+
+       if ( !AllOk ) return 0;
+
+       // now all file are in SA, comments are stripped
+
+       int i = LookForPart(SA, "global", "");
+       if ( i == INT_MAX ) {
+               //     cerr << "No [GLOBAL] definition!" << endl;
+               printm(0, FALSE, MSG_KEYNOGLOBAL);
+               return 0;
+       };
+       if ( !LoadGlobal(SA[i]) ) {
+               return 0;
+       };
+
+       // look for need configuration
+       i = LookForPart(SA, "config", szActiveEmul);
+       if ( i == INT_MAX ) {
+               //     cerr << "No [CONFIG " << szActiveEmul << "]\n";
+               printm(0, FALSE, MSG_KEYNOCONFIG, szActiveEmul);
+               return 0;
+       };
+       //  cerr << "use configuration: " << szActiveEmul << endl;
+       printm(0, FALSE, MSG_KEYUSECONFIG, szActiveEmul);
+       BOOL hadKeys = FALSE;
+
+       string config = SA[i];
+       // parse it
+       while ( config.length() ) {
+               buf[0] = 0;
+               getline(config,buf,sizeof(buf));
+               bufLen = strlen(buf);
+               if ( !bufLen || (buf[0] == '[' && buf[bufLen-1] == ']') ) continue;
+               if ( strnicmp(buf,"keymap",6) == 0 ) {
+                       string orig(buf);
+                       printit("\t"); printit(buf); printit("\n");
+                       char * mapdef = strtok(buf,":");
+                       char * switchKey = strtok(NULL,"\n");
+
+                       if ( !KeyTrans.mapArray.IsEmpty() && switchKey == NULL ) {
+                               //            cerr << "no switch Key for '" << mapdef
+                               //                 << "'" << endl;
+                               printm(0, FALSE, MSG_KEYNOSWKEY, mapdef);
+                               break;
+                       };
+                       if ( KeyTrans.mapArray.IsEmpty() ) {
+                               if ( switchKey != NULL ) { // create default keymap
+                                       //               cerr << "You cannot define switch key for default keymap -> ignored"
+                                       //                    << endl;
+                                       printm(0, FALSE, MSG_KEYCANNOTDEF);
+                               };
+                               TKeyDef empty;
+                               KeyTrans.mapArray.Add(KeyMap(string(mapdef)));
+                               KeyTrans.switchMap(empty); // set it as current keymap
+                               KeyTrans.mainKeyMap = KeyTrans.currentKeyMap;
+                       }
+                       else {
+                               string keydef(switchKey);
+                               keydef += " !*!*!*"; // just for check
+                               WORD vk_code;
+                               DWORD control;
+                               switchKey = ParseKeyDef(keydef.c_str(),vk_code,control);
+                               if ( switchKey != NULL ) {
+                                       TKeyDef swi(NULL,control,vk_code);
+                                       if ( KeyTrans.switchMap(swi) > 0 ) {
+                                               //                  cerr << "Duplicate switching key\n";
+                                               printm(0, FALSE, MSG_KEYDUPSWKEY);
+                                               break;
+                                       };
+                                       KeyTrans.mapArray.Add(KeyMap(swi, orig));
+                                       KeyTrans.switchMap(swi); // set it as current keymap
+                               }
+                       };
+                       mapdef+=7; // 'keymap '
+                       // now load defined keymaps to current
+                       while ((mapdef != NULL)&&
+                               (mapdef = strtok(mapdef,TOKEN_DELIMITERS)) != NULL ) {
+                               i = LookForPart(SA,"keymap",mapdef);
+                               if ( i == INT_MAX ) {
+                                       //               cerr << "Unknown KEYMAP " << mapdef << endl;
+                                       printm(0, FALSE, MSG_KEYUNKNOWNMAP, mapdef);
+                               } else {
+                                       mapdef = strtok(NULL,"\n"); // strtok is used in LoadKeyMap
+                                       // so - save pointer!
+                                       hadKeys = LoadKeyMap(SA[i]); // load it
+                               };
+                       };
+
+               }
+               else if ( strnicmp(buf,"charmap",7) == 0 ) {
+                       printit("\t"); printit(buf); printit("\n");
+                       char * mapdef = buf + 8;// 'charmap '
+                       int SuccesLoaded = 0;
+                       // now load defined charmaps to current
+                       while ((mapdef != NULL)&&
+                               (mapdef = strtok(mapdef,TOKEN_DELIMITERS)) != NULL ) {
+                               i = LookForPart(SA,"charmap",mapdef);
+                               if ( i == INT_MAX ) {
+                                       //               cerr << "Unknown KEYMAP " << mapdef << endl;
+                                       printm(0, FALSE, MSG_KEYUNKNOWNMAP, mapdef);
+                               } else {
+                                       mapdef = strtok(NULL,"\n"); // strtok is used in LoadKeyMap
+                                       // so - save pointer!
+                                       if (LoadCharMap(SA[i])) // load it
+                                               SuccesLoaded++;
+                               };
+                       };
+                       if (!SuccesLoaded) {
+                               //            cerr << "No charmaps loaded\n";
+                               printm(0, FALSE, MSG_KEYNOCHARMAPS);
+                               Charmap.init();
+                       };
+                       /*         strtok(buf," ");
+
+                         char* name = strtok(NULL," ");
+                         if ( name == NULL ) {
+                         cerr << "No name for CHARMAP" << endl;
+                         } else {
+                         i = LookForPart(SA,"charmap", name);
+                         if ( i == INT_MAX ) {
+                         cerr << "Unknown CHARMAP " << name << endl;
+                         } else {
+                         LoadCharMap(SA[i]);
+                         };
+                         };
+                       */
+               }
+               else {
+                       //        cerr << "unexpected token in " << szActiveEmul << endl;
+                       printm(0, FALSE, MSG_KEYUNEXPTOKIN, szActiveEmul);
+               }
+       }
+
+       if ( hadKeys) {
+               TKeyDef empty;
+               KeyTrans.switchMap(empty); // switch to default
+               KeyTrans.mainKeyMap = KeyTrans.currentKeyMap; // save it's number
+               //     cerr << "There are " << (KeyTrans.mapArray.GetItemsInContainer()) << " maps\n";
+               char s[12]; // good enough for a long int (32-bit)
+               itoa(KeyTrans.mapArray.GetItemsInContainer(), s, 10);
+               printm(0, FALSE, MSG_KEYNUMMAPS, s);
+               return 1;
+       };
+       return 0;
+}
+
+void TMapLoader::Display() {
+
+    int max = KeyTrans.mapArray.GetItemsInContainer();
+    if (max == 0) {
+       printm(0, FALSE, MSG_KEYNOKEYMAPS);
+       return;
+    };
+    for ( int i = 0; i < max; i++ ) {
+       char buf[20];
+       itoa(i,buf,10);
+       printit("\t");
+       // Ioannou : we can show the current
+       if (KeyTrans.currentKeyMap == i)
+         printit("*");
+       else
+         printit(" ");
+       strcat(buf," ");
+       printit(buf);
+       char * msg = new char [KeyTrans.mapArray[i].orig.length()+1];
+       strcpy(msg,KeyTrans.mapArray[i].orig.c_str());
+       printit(msg);
+       delete[] msg;
+       printit("\n");
+    };
+};