1 ///////////////////////////////////////////////////////////////////////////////
2 //Telnet Win32 : an ANSI telnet client.
3 //Copyright (C) 1998-2000 Paul Brannan
4 //Copyright (C) 1998 I.Ioannou
5 //Copyright (C) 1997 Brad Johnson
6 //
7 //This program is free software; you can redistribute it and/or
8 //modify it under the terms of the GNU General Public License
9 //as published by the Free Software Foundation; either version 2
10 //of the License, or (at your option) any later version.
11 //
12 //This program is distributed in the hope that it will be useful,
13 //but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //GNU General Public License for more details.
16 //
17 //You should have received a copy of the GNU General Public License
18 //along with this program; if not, write to the Free Software
19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 //I.Ioannou
22 //roryt@hol.gr
23 //
24 ///////////////////////////////////////////////////////////////////////////
25 
26 /////////////////////////////////////////////////////////
27 //     Class TMapLoader - Key/Character Mappings       //
28 //                      - Loads from telnet.cfg        //
29 //     originally part of KeyTrans.cpp                 //
30 /////////////////////////////////////////////////////////
31 
32 #include "precomp.h"
33 
34 // It's probably a good idea to turn off the "identifier was truncated" warning
35 // in MSVC (Paul Brannan 5/25/98)
36 #ifdef _MSC_VER
37 #pragma warning(disable: 4786)
38 #endif
39 
40 // AVS
41 // skip inline comments, empty lines
getline(istream & i,char * buf,int size)42 static char * getline(istream& i, char* buf, int size){
43 
44 	int len = 0;
45 
46 	while (1) {
47 		memset(buf,0,size);
48 		if (i.eof()) break;
49 		i.getline(buf,size,'\n');
50 
51 		while (buf[len]) {
52 			if ( /*(buf[len]>=0) &&*/ buf[len]< ' ' ) buf[len] = ' ';
53 			len++;
54 		};
55 		len = 0;
56 
57 		// not so fast, but work ;)
58 		while ( buf[len] ) {
59             if ( (buf[len] == ' ') && (buf[len+1] == ' ')) {
60 				memmove(buf+len, buf+len+1, strlen(buf+len));
61             } else len++;
62 		};
63 
64 		if (buf[0] == ' ') memmove(buf, buf+1, size-1);
65 
66 		// empty or comment
67 		if ((buf[0]==0)||(buf[0]==';')) continue;
68 
69 		len = 0; // look for comment like this one
70         while (buf[len])
71         {
72             if ((buf[len] == '/') && (buf[len + 1] == '/')) buf[len] = 0;
73             else len++;
74 
75             if (len && (buf[len - 1] == ' ')) {
76                 len--;
77                 buf[len] = 0;
78             };
79         }
80         // in case for comment like this one (in line just a comment)
81         if (buf[0] == 0) continue;
82 
83         break;
84 	};
85 	return (buf);
86 };
87 
88 //AVS
89 // use string as FIFO queue for lines
getline(string & str,char * buf,size_t sz)90 static int getline(string&str, char* buf, size_t sz) {
91 
92 	if ( !str.length() ) return 0;
93 	const char * p = strchr(str.c_str(),'\n');
94 	unsigned int len; // Changed to unsigned (Paul Brannan 6/23/98)
95 	if ( p==NULL )
96 		len = str.length();
97 	else
98 		len = p - str.c_str();
99 
100 	len = len<sz?len:sz-1;
101 
102 	strncpy(buf,str.c_str(), len);
103 	buf[len]=0;
104 	// DJGPP also uses erase rather than remove (Paul Brannan 6/23/98)
105 #ifndef __BORLANDC__
106 	str.erase(0, len + 1);
107 #else
108 	str.remove(0,len+1);
109 #endif
110 	return 1;
111 };
112 
113 //AVS
114 // parse \nnn and \Xhh
getbyte(const char * str)115 static int getbyte(const char*str) {
116 	unsigned char retval = 0;
117 	int base = 10;
118 	int readed = 0;
119 
120 	if ( (*str == 'x') || (*str == 'X') ) {
121 		base = 16;
122 		readed++;
123 	};
124 
125 	while (readed != 3 && str[readed]) {
126 		unsigned char ch = toupper(str[readed]);
127 		if ( isdigit(ch) ) {
128 			retval = retval*base + (ch -'0');
129 		} else if (base == 16 && ch >= 'A' && ch <= 'F') {
130 			retval = retval*base + (ch-'A'+10);
131 		} else {
132 			return -1;
133 		};
134 		readed++;
135 	};
136 	// Ioannou: If we discard the 0x00 we can't undefine a key !!!
137 	//  if ( retval == 0 ) {
138 	//     return -1;
139 	//  };
140 	return retval;
141 };
142 
143 //AVS
144 // a little optimization
Fix_ControlKeyState(char * Next_Token)145 DWORD Fix_ControlKeyState(char * Next_Token) {
146 	if (stricmp(Next_Token, "RIGHT_ALT" ) == 0) return RIGHT_ALT_PRESSED;
147 	if (stricmp(Next_Token, "LEFT_ALT"  ) == 0) return LEFT_ALT_PRESSED;
148 	if (stricmp(Next_Token, "RIGHT_CTRL") == 0) return RIGHT_CTRL_PRESSED;
149 	if (stricmp(Next_Token, "LEFT_CTRL" ) == 0) return LEFT_CTRL_PRESSED;
150 	if (stricmp(Next_Token, "SHIFT"     ) == 0) return SHIFT_PRESSED;
151 	if (stricmp(Next_Token, "NUMLOCK"   ) == 0) return NUMLOCK_ON;
152 	if (stricmp(Next_Token, "SCROLLLOCK") == 0) return SCROLLLOCK_ON;
153 	if (stricmp(Next_Token, "CAPSLOCK"  ) == 0) return CAPSLOCK_ON;
154 	if (stricmp(Next_Token, "ENHANCED"  ) == 0) return ENHANCED_KEY;
155 
156 	// Paul Brannan 5/27/98
157 	if (stricmp(Next_Token, "APP_KEY"   ) == 0) return APP_KEY;
158 	// Paul Brannan 6/28/98
159 	if (stricmp(Next_Token, "APP2_KEY"  ) == 0) return APP2_KEY;
160 	// Paul Brannan 8/28/98
161 	if (stricmp(Next_Token, "APP3_KEY"  ) == 0) return APP3_KEY;
162 	// Paul Brannan 12/9/98
163 	if (stricmp(Next_Token, "APP4_KEY"  ) == 0) return APP4_KEY;
164 
165 	return 0;
166 }
167 
168 
169 // AVS
170 // rewrited to suppert \xhh notation, a little optimized
Fix_Tok(char * tok)171 char* Fix_Tok(char * tok) {
172 	static char s[256];
173 	int i,j,n;
174 
175 	// setmem is nonstandard; memset is standard (Paul Brannan 5/25/98)
176 	memset(s, 0, 256);
177 	//  setmem(s, 256, 0);
178 	i = j = n = 0;
179 	if ( tok != NULL ) {
180 		for ( ; tok[i] != 0; ) {
181 			switch ( tok[i] ) {
182 			case '\\' :
183 				switch ( tok[i+1] ) {
184 				case '\\':
185 					s[j++] = '\\';
186 					i += 2;
187 					break;
188 				default:
189 					n = getbyte(tok+i+1);
190 					if ( n < 0 )
191 						s[j++] = tok[i++];
192 					else {
193 						s[j++]=n;
194 						i += 4;
195 					} ;
196 					break;
197 				};
198 				break;
199 				case '^' :
200                     if ( tok[i+1] >= '@' ) {
201 						s[j++] = tok[i+1] - '@';
202 						i += 2;
203 						break;
204                     }
205 				default  :
206                     s[j++] = tok[i++];
207 			}
208 		}
209 	}
210 	return s;
211 };
212 
213 // AVS
214 // perform 'normalization' for lines like [some text], and some checks
215 // maybe it will be done faster - but no time for it
normalizeSplitter(string & buf)216 int normalizeSplitter(string& buf) {
217     if ( buf.length() <= 2 ) return 0;
218     if ( buf[0] == '[' && buf[buf.length()-1] == ']' ) {
219 		while ( buf[1] == ' ' )
220 			// DJGPP also uses erase rather than remove (Paul Brannan 6/23/98)
221 #ifndef __BORLANDC__
222 			buf.erase(1, 1);
223 #else
224 			buf.remove(1,1);
225 #endif
226 		while ( buf[buf.length()-2] == ' ' )
227 			// Paul Brannan 6/23/98
228 #ifndef __BORLANDC__
229 			buf.erase(buf.length()-2,1);
230 #else
231 			buf.remove(buf.length()-2,1);
232 #endif
233 		return 1;
234     }
235     return 0;
236 };
237 
238 // AVS
239 // looking for part in string array, see Load(..) for more info
LookForPart(stringArray & sa,const char * partType,const char * partName)240 int TMapLoader::LookForPart(stringArray& sa, const char* partType, const char* partName) {
241     if ( !sa.IsEmpty() ) {
242 		string cmpbuf("[");
243 		cmpbuf += partType;
244 		cmpbuf += " ";
245 		cmpbuf += partName;
246 		cmpbuf += "]";
247 		normalizeSplitter(cmpbuf); // if no parttype, [global] for example
248 		int max = sa.GetItemsInContainer();
249 		for ( int i = 0; i<max; i++ )
250 			// I found some strange behavior if strnicmp was used here
251             if (strnicmp(cmpbuf.c_str(),sa[i].c_str(),cmpbuf.length()) == 0)
252 				return i;
253     };
254     return INT_MAX;
255 };
256 
257 // AVS
258 // load globals to 'globals'
259 // in buf must be a [global] part of input file
LoadGlobal(string & buf)260 int TMapLoader::LoadGlobal(string& buf) {
261 
262 	char wbuf[128];
263 	while ( buf.length() ) {
264 		wbuf[0]=0;
265 		if (!getline(buf,wbuf,sizeof(wbuf))) break;
266 		if ( wbuf[0]==0 ) break;
267 		char* Name = strtok(wbuf, TOKEN_DELIMITERS);
268 		if ( stricmp(Name, "[global]")==0 ) continue;
269 
270 		char* Value = strtok(NULL, TOKEN_DELIMITERS);
271 		if ( Value == NULL ) {
272 			//              cerr << "[global] -> no value for " << Name << endl;
273 			printm(0, FALSE, MSG_KEYNOVAL, Name);
274 			continue;
275 		};
276 		int val = atoi(Value);
277 		if ( val > 0 && val <= 0xff ) {
278 			if ( !KeyTrans.AddGlobalDef(val, Name)) return 0;
279 		}
280 		else {
281 			//             cerr << "[global] -> bad value for " << Name << endl;
282 			printm(0, FALSE, MSG_KEYBADVAL, Name);
283 			continue;
284 		};
285 	};
286 	return 1;
287 };
288 
289 // AVS
290 // perform parsing of strings like 'VK_CODE shifts text'
291 // returns text on success
ParseKeyDef(const char * buf,WORD & vk_code,DWORD & control)292 char* TMapLoader::ParseKeyDef(const char* buf, WORD& vk_code, DWORD& control) {
293 	char wbuf[256];
294 	strcpy(wbuf,buf);
295 	char* ptr = strtok(wbuf, TOKEN_DELIMITERS);
296 	if ( ptr == NULL ) return NULL;
297 
298 	int i = KeyTrans.LookOnGlobal(ptr);
299 	if ( i == INT_MAX ) return NULL;
300 
301 	vk_code = KeyTrans.GetGlobalCode(i);
302 
303 	control = 0;
304 	DWORD st;
305 	while (1) {
306 		ptr = strtok(NULL, TOKEN_DELIMITERS);
307 		if ((ptr == NULL) || ((st = Fix_ControlKeyState(ptr)) == 0)) break;
308 		control |= st;
309 	};
310 
311 	if ( ptr == NULL ) return NULL;
312 
313 	return Fix_Tok(ptr);
314 };
315 
316 // AVS
317 // load keymap to current map
318 // be aware - buf must passed by value, its destroyed
LoadKeyMap(string buf)319 int TMapLoader::LoadKeyMap(string buf) {
320 
321 	char wbuf[128];
322 	WORD vk_code;
323 	DWORD control;
324 	int i;
325 
326 	// Paul Brannan Feb. 22, 1999
327 	strcpy(wbuf, "VK_");
328 	wbuf[4] = 0;
329 	wbuf[3] = ini.get_escape_key();
330 	i = KeyTrans.LookOnGlobal(wbuf);
331 	if (i != INT_MAX) {
332 		KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), RIGHT_ALT_PRESSED, TN_ESCAPE);
333 		KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), LEFT_ALT_PRESSED, TN_ESCAPE);
334 	}
335 	wbuf[3] = ini.get_scrollback_key();
336 	i = KeyTrans.LookOnGlobal(wbuf);
337 	if (i != INT_MAX) {
338 		KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), RIGHT_ALT_PRESSED, TN_SCROLLBACK);
339 		KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), LEFT_ALT_PRESSED, TN_SCROLLBACK);
340 	}
341 	wbuf[3] = ini.get_dial_key();
342 	i = KeyTrans.LookOnGlobal(wbuf);
343 	if (i != INT_MAX) {
344 		KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), RIGHT_ALT_PRESSED, TN_DIAL);
345 		KeyTrans.AddKeyDef(KeyTrans.GetGlobalCode(i), LEFT_ALT_PRESSED, TN_DIAL);
346 	}
347 	KeyTrans.AddKeyDef(VK_INSERT, SHIFT_PRESSED, TN_PASTE);
348 
349 	while ( buf.length() ) {
350 		wbuf[0] = 0;
351 		if (!getline(buf,wbuf,sizeof(wbuf))) break;
352 		if ( wbuf[0]==0 ) break;
353 		if ( strnicmp(wbuf,"[keymap",7)==0 ) continue;
354 
355 		char * keydef = ParseKeyDef(wbuf,vk_code,control);
356 
357 		if ( keydef != NULL ) {
358 
359 			// Check to see if keydef is a "special" code (Paul Brannan 3/29/00)
360 			if(!strnicmp(keydef, "\\tn_escape", strlen("\\tn_escape"))) {
361 				if(!KeyTrans.AddKeyDef(vk_code, control, TN_ESCAPE)) return 0;
362 			} else if(!strnicmp(keydef, "\\tn_scrollback", strlen("\\tn_scrollback"))) {
363 				if(!KeyTrans.AddKeyDef(vk_code, control, TN_SCROLLBACK)) return 0;
364 			} else if(!strnicmp(keydef, "\\tn_dial", strlen("\\tn_dial"))) {
365 				if(!KeyTrans.AddKeyDef(vk_code, control, TN_DIAL)) return 0;
366 			} else if(!strnicmp(keydef, "\\tn_paste", strlen("\\tn_paste"))) {
367 				if(!KeyTrans.AddKeyDef(vk_code, control, TN_PASTE)) return 0;
368 			} else if(!strnicmp(keydef, "\\tn_null", strlen("\\tn_null"))) {
369 				if(!KeyTrans.AddKeyDef(vk_code, control, TN_NULL)) return 0;
370 			} else if(!strnicmp(keydef, "\\tn_cr", strlen("\\tn_cr"))) {
371 				if(!KeyTrans.AddKeyDef(vk_code, control, TN_CR)) return 0;
372 			} else if(!strnicmp(keydef, "\\tn_crlf", strlen("\\tn_crlf"))) {
373 				if(!KeyTrans.AddKeyDef(vk_code, control, TN_CRLF)) return 0;
374 			} else
375 				if(!KeyTrans.AddKeyDef(vk_code,control,keydef)) return 0;
376 				// else DeleteKeyDef() ???? - I'm not sure...
377 		}
378 	};
379 
380 	return 1;
381 };
382 
383 // AVS
384 // load [charmap ...] part to xlat
LoadCharMap(string buf)385 int TMapLoader::LoadCharMap(string buf) {
386 	char wbuf[128];
387 	char charmapname[128];
388 	charmapname[0] = 0;
389 
390 	//        xlat.init(); now it done by KeyTranslator::Load()
391 
392 	while ( buf.length() ) {
393 		wbuf[0]=0;
394 		if (!getline(buf,wbuf,sizeof(wbuf))) break;
395 		if ( wbuf[0]==0 ) break;
396 		if ( strnicmp(wbuf,"[charmap",8)==0 ) {
397 			strcpy(charmapname,wbuf);
398 			continue;
399 		};
400 		char * host = strtok(wbuf, " ");
401 		char * console = strtok(NULL, " ");
402 
403 		int bHost;
404 		int bConsole;
405 
406 		if ( host == NULL || console == NULL ) {
407 			//              cerr << charmapname << " -> Bad structure" << endl;
408 			printm(0, FALSE, MSG_KEYBADSTRUCT, charmapname);
409 			return 0;
410 		};
411 		if ( strlen(host) > 1 && host[0] == '\\' )
412 			bHost = getbyte(host+1);
413 		else
414 			bHost = (unsigned char)host[0];
415 
416 		if ( strlen(console) > 1 && console[0] == '\\' )
417 			bConsole = getbyte(console+1);
418 		else
419 			bConsole = (unsigned char)console[0];
420 
421 		if ( bHost <= 0 || bConsole <= 0 ) {
422 			//              cerr << charmapname << " -> Bad chars? "
423 			//                   << host << " -> " << console << endl;
424 			printm(0, FALSE, MSG_KEYBADCHARS, charmapname, host, console);
425 			return 0;
426 		};
427 		// xlat.table[bHost] = bConsole;
428 		Charmap.modmap(bHost, 'B', bConsole);
429 	};
430 	return (Charmap.enabled = 1);
431 };
432 
433 // AVS
434 // ignore long comment [comment] ... [end comment]
435 // recursive!
getLongComment(istream & is,char * wbuf,size_t sz)436 int getLongComment(istream& is, char* wbuf, size_t sz) {
437 
438 	int bufLen;
439     while ( is ) {
440 		wbuf[0] = 0;
441 		getline(is, wbuf, sz);
442 		if ( wbuf[0]==0 ) return 1;
443 		bufLen = strlen(wbuf);
444 		if ( wbuf[0] == '[' && wbuf[bufLen-1] == ']' ) {
445 			string temps(wbuf);
446 
447 			if (!normalizeSplitter(temps)) {
448 				//           cerr << "Unexpected line '" << temps << "'\n";
449 				printm(0, FALSE, MSG_KEYUNEXPLINE, temps.c_str());
450 				return 0;
451 			};
452 			if ( stricmp(temps.c_str(),"[comment]") == 0 ) {
453 				// do recursive call
454 				if ( !getLongComment(is, wbuf, sz) ) return 0;
455 				continue;
456 			};
457 			if ( stricmp(temps.c_str(),"[end comment]") == 0 ) return 1;
458 		};
459     };
460 	// we get a warning if we don't put a return here (Paul Brannan 5/25/98)
461 	return 0;
462 };
463 
464 // AVS
465 // completelly rewrited to support new conceptions
Load(const char * filename,const char * szActiveEmul)466 int TMapLoader::Load(const char * filename, const char * szActiveEmul) {
467 	char buf[256];
468 	int bufLen;
469 
470 	ifstream inpfile(filename);
471 	KeyTrans.DeleteAllDefs();
472 	Charmap.init();
473 
474 	// it is an array for store [...] ... [end ...] parts from file
475 	stringArray SA(0,0,sizeof(string));
476 	int AllOk = 0;
477 
478 	while ( inpfile ) {
479 
480 		getline(inpfile, buf, 255);
481 		bufLen = strlen(buf);
482 		if ( !bufLen ) continue;
483 
484 		if ( buf[0] == '[' && buf[bufLen-1] == ']' ) {
485 			// is a part splitter [...]
486 			string temps(buf);
487 
488 			if (!normalizeSplitter(temps)) {
489 				printm(0, FALSE, MSG_KEYUNEXPLINE, temps.c_str());
490 				AllOk = 0;
491 				break;
492 			};
493 			// if a comment
494 			if ( stricmp(temps.c_str(),"[comment]") == 0 ) {
495 #ifdef KEYDEBUG
496 				printit(temps.c_str());
497 #endif
498 				if ( !getLongComment(inpfile, buf, sizeof(buf)) ) {
499 					printm(0, FALSE, MSG_KEYUNEXPEOF);
500 					break;
501 				};
502 #ifdef KEYDEBUG
503 				printit("\r          \r");
504 #endif
505 				continue;
506 			};
507 
508 
509 			string back = temps;
510 			// prepare line for make it as [end ...]
511 			// and check it
512 			if ( strnicmp(back.c_str(), "[global]", 8) == 0 ) {} // do nothing
513 			else if ( strnicmp(back.c_str(), "[keymap", 7) == 0 ) {
514 				// DJGPP also uses erase rather than remove (Paul Brannan 6/23/98)
515 #ifndef __BORLANDC__
516 				back.erase(7);
517 #else
518 				back.remove(7);
519 #endif
520 				back += "]";
521 			}
522 			else if ( strnicmp(back.c_str(), "[charmap", 8) == 0 ) {
523 				// Paul Brannan 6/23/98
524 #ifndef __BORLANDC__
525 				back.erase(8);
526 #else
527 				back.remove(8);
528 #endif
529 				back += "]";
530 			}
531 			else if ( strnicmp(back.c_str(), "[config", 7) == 0 ) {
532 				// Paul Brannan 6/23/98
533 #ifndef __BORLANDC__
534 				back.erase(7);
535 #else
536 				back.remove(7);
537 #endif
538 				back += "]";
539 			}
540 			else {
541 				//        cerr << "Unexpected token " << back << endl;
542 				printm(0, FALSE, MSG_KEYUNEXPTOK, back.c_str());
543 				break;
544 			};
545 
546 			back.insert(1,"END "); // now it looks like [END ...]
547 #ifdef KEYDEBUG
548 			printit(temps.c_str());
549 #endif
550 
551 			int ok = 0;
552 			// fetch it to temps
553 			while ( 1 ) {
554 				getline(inpfile, buf, sizeof(buf));
555 				bufLen = strlen(buf);
556 				if ( !bufLen ) break;
557 				if ( buf[0] == '[' && buf[bufLen-1] == ']' ) {
558 					string t(buf);
559 					if ( !normalizeSplitter(t) ) break;
560 
561 					if ( stricmp(t.c_str(),back.c_str()) == 0 ) {
562 						ok = 1;
563 						break;
564 					};
565 
566 					// AVS 31.12.97 fix [comment] block inside another block
567 					if ( stricmp(t.c_str(),"[comment]") == 0 &&
568 						getLongComment(inpfile, buf, sizeof(buf)) ) continue;
569 
570 					break;
571 				};
572 				temps += "\n";
573 				temps += buf;
574 			};
575 			if ( !ok ) {
576 				//         cerr << "Unexpected end of file or token" << endl;
577 				printm(0, FALSE, MSG_KEYUNEXP);
578 				AllOk = 0;
579 				break;
580 			};
581 #ifdef KEYDEBUG
582 			printit("\r                                                                      \r");
583 #endif
584 			AllOk = SA.Add(temps);
585 			if ( !AllOk ) break;
586 		} else {
587 			//       cerr << "Unexpected line '" << buf << "'\n";
588 			printm(0, FALSE, MSG_KEYUNEXPLINE, buf);
589 			AllOk = 0;
590 			break;
591 		};
592 	};
593 
594 	inpfile.close();
595 
596 	if ( !AllOk ) return 0;
597 
598 	// now all file are in SA, comments are stripped
599 
600 	int i = LookForPart(SA, "global", "");
601 	if ( i == INT_MAX ) {
602 		//     cerr << "No [GLOBAL] definition!" << endl;
603 		printm(0, FALSE, MSG_KEYNOGLOBAL);
604 		return 0;
605 	};
606 	if ( !LoadGlobal(SA[i]) ) {
607 		return 0;
608 	};
609 
610 	// look for need configuration
611 	i = LookForPart(SA, "config", szActiveEmul);
612 	if ( i == INT_MAX ) {
613 		//     cerr << "No [CONFIG " << szActiveEmul << "]\n";
614 		printm(0, FALSE, MSG_KEYNOCONFIG, szActiveEmul);
615 		return 0;
616 	};
617 	//  cerr << "use configuration: " << szActiveEmul << endl;
618 	printm(0, FALSE, MSG_KEYUSECONFIG, szActiveEmul);
619 	BOOL hadKeys = FALSE;
620 
621 	string config = SA[i];
622 	// parse it
623 	while ( config.length() ) {
624 		buf[0] = 0;
625 		getline(config,buf,sizeof(buf));
626 		bufLen = strlen(buf);
627 		if ( !bufLen || (buf[0] == '[' && buf[bufLen-1] == ']') ) continue;
628 		if ( strnicmp(buf,"keymap",6) == 0 ) {
629 			string orig(buf);
630 			printit("\t"); printit(buf); printit("\n");
631 			char * mapdef = strtok(buf,":");
632 			char * switchKey = strtok(NULL,"\n");
633 
634 			if ( !KeyTrans.mapArray.IsEmpty() && switchKey == NULL ) {
635 				//            cerr << "no switch Key for '" << mapdef
636 				//                 << "'" << endl;
637 				printm(0, FALSE, MSG_KEYNOSWKEY, mapdef);
638 				break;
639 			};
640 			if ( KeyTrans.mapArray.IsEmpty() ) {
641 				if ( switchKey != NULL ) { // create default keymap
642 					//               cerr << "You cannot define switch key for default keymap -> ignored"
643 					//                    << endl;
644 					printm(0, FALSE, MSG_KEYCANNOTDEF);
645 				};
646 				TKeyDef empty;
647 				KeyTrans.mapArray.Add(KeyMap(string(mapdef)));
648 				KeyTrans.switchMap(empty); // set it as current keymap
649 				KeyTrans.mainKeyMap = KeyTrans.currentKeyMap;
650 			}
651 			else {
652 				string keydef(switchKey);
653 				keydef += " !*!*!*"; // just for check
654 				WORD vk_code;
655 				DWORD control;
656 				switchKey = ParseKeyDef(keydef.c_str(),vk_code,control);
657 				if ( switchKey != NULL ) {
658 					TKeyDef swi(NULL,control,vk_code);
659 					if ( KeyTrans.switchMap(swi) > 0 ) {
660 						//                  cerr << "Duplicate switching key\n";
661 						printm(0, FALSE, MSG_KEYDUPSWKEY);
662 						break;
663 					};
664 					KeyTrans.mapArray.Add(KeyMap(swi, orig));
665 					KeyTrans.switchMap(swi); // set it as current keymap
666 				}
667 			};
668 			mapdef+=7; // 'keymap '
669 			// now load defined keymaps to current
670 			while ((mapdef != NULL)&&
671 				(mapdef = strtok(mapdef,TOKEN_DELIMITERS)) != NULL ) {
672 				i = LookForPart(SA,"keymap",mapdef);
673 				if ( i == INT_MAX ) {
674 					//               cerr << "Unknown KEYMAP " << mapdef << endl;
675 					printm(0, FALSE, MSG_KEYUNKNOWNMAP, mapdef);
676 				} else {
677 					mapdef = strtok(NULL,"\n"); // strtok is used in LoadKeyMap
678 					// so - save pointer!
679 					hadKeys = LoadKeyMap(SA[i]); // load it
680 				};
681 			};
682 
683 		}
684 		else if ( strnicmp(buf,"charmap",7) == 0 ) {
685 			printit("\t"); printit(buf); printit("\n");
686 			char * mapdef = buf + 8;// 'charmap '
687 			int SuccesLoaded = 0;
688 			// now load defined charmaps to current
689 			while ((mapdef != NULL)&&
690 				(mapdef = strtok(mapdef,TOKEN_DELIMITERS)) != NULL ) {
691 				i = LookForPart(SA,"charmap",mapdef);
692 				if ( i == INT_MAX ) {
693 					//               cerr << "Unknown KEYMAP " << mapdef << endl;
694 					printm(0, FALSE, MSG_KEYUNKNOWNMAP, mapdef);
695 				} else {
696 					mapdef = strtok(NULL,"\n"); // strtok is used in LoadKeyMap
697 					// so - save pointer!
698 					if (LoadCharMap(SA[i])) // load it
699 						SuccesLoaded++;
700 				};
701 			};
702 			if (!SuccesLoaded) {
703 				//            cerr << "No charmaps loaded\n";
704 				printm(0, FALSE, MSG_KEYNOCHARMAPS);
705 				Charmap.init();
706 			};
707 			/*         strtok(buf," ");
708 
709 			  char* name = strtok(NULL," ");
710 			  if ( name == NULL ) {
711 			  cerr << "No name for CHARMAP" << endl;
712 			  } else {
713 			  i = LookForPart(SA,"charmap", name);
714 			  if ( i == INT_MAX ) {
715 			  cerr << "Unknown CHARMAP " << name << endl;
716 			  } else {
717 			  LoadCharMap(SA[i]);
718 			  };
719 			  };
720 			*/
721 		}
722 		else {
723 			//        cerr << "unexpected token in " << szActiveEmul << endl;
724 			printm(0, FALSE, MSG_KEYUNEXPTOKIN, szActiveEmul);
725 		}
726 	}
727 
728 	if ( hadKeys) {
729 		TKeyDef empty;
730 		KeyTrans.switchMap(empty); // switch to default
731 		KeyTrans.mainKeyMap = KeyTrans.currentKeyMap; // save it's number
732 		//     cerr << "There are " << (KeyTrans.mapArray.GetItemsInContainer()) << " maps\n";
733 		char s[12]; // good enough for a long int (32-bit)
734 		itoa(KeyTrans.mapArray.GetItemsInContainer(), s, 10);
735 		printm(0, FALSE, MSG_KEYNUMMAPS, s);
736 		return 1;
737 	};
738 	return 0;
739 }
740 
Display()741 void TMapLoader::Display() {
742 
743     int max = KeyTrans.mapArray.GetItemsInContainer();
744     if (max == 0) {
745        printm(0, FALSE, MSG_KEYNOKEYMAPS);
746        return;
747     };
748     for ( int i = 0; i < max; i++ ) {
749        char buf[20];
750        itoa(i,buf,10);
751        printit("\t");
752        // Ioannou : we can show the current
753        if (KeyTrans.currentKeyMap == i)
754          printit("*");
755        else
756          printit(" ");
757        strcat(buf," ");
758        printit(buf);
759        char * msg = new char [KeyTrans.mapArray[i].orig.length()+1];
760        strcpy(msg,KeyTrans.mapArray[i].orig.c_str());
761        printit(msg);
762        delete[] msg;
763        printit("\n");
764     };
765 };
766