1 ///////////////////////////////////////////////////////////////////////////////
2 //Telnet Win32 : an ANSI telnet client.
3 //Copyright (C) 1998  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 //
28 // Module:		tnmain.cpp
29 //
30 // Contents:	telnet main program
31 //
32 // Product:		telnet
33 //
34 // Revisions: August 11, 1998	Thomas Briggs <tbriggs@qmetric.com>
35 //            May 14, 1998		Paul Brannan <pbranna@clemson.edu>
36 //            5.April.1997		jbj@nounname.com
37 //            5.Dec.1996		jbj@nounname.com
38 //            Version 2.0
39 //
40 //            02.Apr.1995		igor.milavec@uni-lj.si
41 //					  Original code
42 //
43 ///////////////////////////////////////////////////////////////////////////////
44 
45 #include "precomp.h"
46 
47 #include "tnmain.h"
48 
49 int telCommandLine (Telnet &MyConnection);
50 
51 void waitforkey() {
52 	HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
53 	INPUT_RECORD InputRecord;
54 	DWORD dwInput;
55 	BOOL done = FALSE;
56 	while (!done){
57 		WaitForSingleObject( hConsole, INFINITE );
58 		if (!ReadConsoleInput(hConsole, &InputRecord, 1, &dwInput)){
59 			done = TRUE;
60 			continue;
61 		}
62 		if (InputRecord.EventType == KEY_EVENT &&
63 			InputRecord.Event.KeyEvent.bKeyDown )
64 			done = TRUE;
65 	}
66 }
67 
68 //char * cfgets ( char * buf, unsigned int length, char pszHistory[][80], int iHistLength){
69 struct cmdHistory * cfgets (char *buf, unsigned int length, struct cmdHistory *cmdhist) {
70 
71 	HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
72 	unsigned int current=0, cursor =0, iEraseLength=0, i;
73 	char chr;
74 	char temp[2];
75 	char temp1[80];
76 
77 	INPUT_RECORD InputRecord;
78 	BOOL done = FALSE;
79 
80 	temp[1] = 0;
81 	buf[0] = '\0';
82 
83 	if(!ini.get_input_redir()) {
84 		while (!done) {
85 			DWORD dwInput;
86 			int MustRefresh = 0;
87 			WaitForSingleObject( hConsole, INFINITE );
88 			if (!ReadConsoleInput(hConsole, &InputRecord, 1, &dwInput)){
89 				done = TRUE;
90 				continue;
91 			}
92 			MustRefresh = 0;
93 			if (InputRecord.EventType == KEY_EVENT &&
94 				InputRecord.Event.KeyEvent.bKeyDown ) {
95 
96 				if(InputRecord.Event.KeyEvent.dwControlKeyState &
97 					(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
98 
99 					switch(InputRecord.Event.KeyEvent.wVirtualKeyCode) {
100 					case 'D': // Thomas Briggs 8/11/98
101 						buf[0] = '\04';
102 						buf[1] = '\0';
103 						current = 1;
104 						done = true;
105 						continue;
106 					case 'U': // Paul Brannan 8/11/98
107 						buf[0] = '\0';
108 						current = 0;
109 						cursor = 0;
110 						MustRefresh = 1;
111 						break;
112 					}
113 				}
114 
115 				switch (InputRecord.Event.KeyEvent.wVirtualKeyCode) {
116 				case VK_UP:
117 					// crn@ozemail.com.au
118 					if (cmdhist != NULL) {
119 						if (!strcmp(buf, ""))
120 							strncpy(buf, cmdhist->cmd, 79);
121 						else if (cmdhist->prev != NULL) {
122 							cmdhist = cmdhist->prev;
123 							strncpy(buf, cmdhist->cmd, 79);
124 						}
125 						current = strlen(buf);
126 					}
127 					///
128 					MustRefresh = 1;
129 					break;
130 				case VK_DOWN:
131 					// crn@ozemail.com.au
132 					if (cmdhist != NULL) {
133 						if (cmdhist->next != NULL) {
134 							cmdhist = cmdhist->next;
135 							strncpy(buf, cmdhist->cmd, 79);
136 						} else {
137 							strncpy(buf, "", 79);
138 						}
139 						current = strlen(buf);
140 					}
141 					///
142 					MustRefresh = 1;
143 					break;
144 				case VK_RIGHT:		//crn@ozemail.com.au (added ctrl+arrow)
145 					if (cursor < current) {
146 						if (InputRecord.Event.KeyEvent.dwControlKeyState &
147 							(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
148 							unsigned int j, k;
149 							for (j = cursor; j <= current; j++)
150 								if (buf[j+1] == ' ' || (j+1)==current)
151 									break;
152 								for (k = ++j; k <= current; k++)
153 									if (buf[k] != ' ' || k == current) {
154 										cursor = k == current ? --k : k;
155 										break;
156 									}
157 						} else
158 							cursor++;
159 						MustRefresh = 1;
160 						break;
161 					}
162 				case VK_LEFT:		//crn@ozemail.com.au (added ctrl+arrow)
163 					if (cursor > 0) {
164 						if(InputRecord.Event.KeyEvent.dwControlKeyState &
165 							(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
166 							int j, k;
167 							for (j = cursor; j >= 0; j--)
168 								if (buf[j-1] != ' ')
169 									break;
170 								for (k = --j; k >= 0; k--)
171 									if (buf[k] == ' ' || k == 0) {
172 										cursor = !k ? k : ++k;
173 										break;
174 									}
175 						} else
176 							cursor--;
177 						MustRefresh = 1;
178 						break;
179 					}
180 				case VK_HOME:
181 					if (cursor>0) cursor = 0;
182 					MustRefresh = 1;
183 					break;
184 				case VK_END:
185 					if (cursor<current) cursor = current;
186 					MustRefresh = 1;
187 					break;
188 				case VK_DELETE:
189 					if (current > 0 && current > cursor) {
190 						strcpy(&buf[cursor],&buf[cursor+1]);
191 						current--;
192 						buf[current] = 0;
193 						printit("\r");
194 						for (i = 0; i < current+strlen("telnet>")+1 ;i++)
195 							printit(" ");
196 					}
197 					MustRefresh = 1;
198 					break;
199 				case VK_BACK:
200 					if (cursor > 0 ) {
201 						strcpy(&buf[cursor-1],&buf[cursor]);
202 						current--;
203 						cursor--;
204 						buf[current] = 0;
205 						printit("\r");
206 						for (i = 0; i < current+strlen("telnet>")+1 ;i++)
207 							printit(" ");
208 					}
209 					MustRefresh = 1;
210 					break;
211 
212 				default:
213 					chr = InputRecord.Event.KeyEvent.uChar.AsciiChar;
214 					if (chr == '\r') {
215 						done = TRUE;
216 						continue;
217 					}
218 					if (current >= length-1){
219 						done = TRUE;
220 						continue;
221 					}
222 					if ( isprint (chr) ){
223 						strncpy(temp1,&buf[cursor],79);
224 						strncpy(&buf[cursor+1],temp1,79-(cursor+1));
225 						buf[cursor++]=chr;
226 						current++;
227 						buf[current] = 0;
228 						MustRefresh = 1;
229 					}
230 					break;
231 				}
232 				if (MustRefresh == 1)
233 				{
234 					printit("\rtelnet");
235 					for (i = 0; i <= iEraseLength ;i++)
236 						printit(" ");
237 					printit("\rtelnet>");
238 					printit(buf);
239 					iEraseLength = strlen(buf);
240 					for (i = 0; i < current-cursor; i++)
241 						printit("\b");
242 				}
243 			}
244 		}
245 		buf[current] = 0;
246 		if (strcmp(buf, "")) {
247 			if (cmdhist == NULL) {
248 				cmdhist = new struct cmdHistory;
249 				if (cmdhist == NULL) {
250 					printit ("\nUnable to allocate memory for history buffer -- use the \"flush\" command to clear the buffer.\n");
251 					return cmdhist;
252 				}
253 				strncpy(cmdhist->cmd, buf, 79);
254 				cmdhist->next = NULL;
255 				cmdhist->prev = NULL;
256 			} else {
257 				while (cmdhist->next != NULL)	//  move to the end of the list
258 					cmdhist = cmdhist->next;
259 				cmdhist->next = new struct cmdHistory;
260 				if (cmdhist->next == NULL) {
261 					printit ("\nUnable to allocate memory for history buffer -- use the \"flush\" command to clear the buffer.\n");
262 					return cmdhist;
263 				}
264 				cmdhist->next->prev = cmdhist;	//  previous is where we are now
265 				cmdhist = cmdhist->next;
266 				strncpy(cmdhist->cmd, buf, 79);
267 				cmdhist->next = NULL;
268 			}
269 			while (cmdhist->next)
270 				cmdhist = cmdhist->next;
271 		}
272 		return cmdhist;
273 		///
274 	} else {
275 		WaitForSingleObject( hConsole, INFINITE );
276 		DWORD dwInput;
277 		DWORD OldMode;
278 		GetConsoleMode(hConsole, &OldMode);
279 		SetConsoleMode(hConsole,
280 			OldMode &~ (ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT) );
281 		while (ReadFile(hConsole, &chr, 1, &dwInput, NULL)) {
282 			if (chr == '\r') {
283 				temp[0] = chr;
284 				printit(&temp[0]);
285 				break;
286 			}
287 			if (chr == '\b' && current > 0) {
288 				current--;
289 				printit("\b \b");
290 			}
291 			if (current >= length-1){
292 				break;
293 			}
294 			if ( isprint (chr) ){
295 				temp[0] = chr;
296 				printit(&temp[0]);
297 				buf[current++]=chr;
298 			}
299 		}
300 		buf[current] = 0;
301 		SetConsoleMode(hConsole, OldMode);
302 		return NULL;
303 	}
304 }
305 
306 // AVS ** for fix bug in command 'keys load keymapname' without file
307 // static char keyfile[MAX_PATH*2];
308 
309 int main(int ArgC, char* ArgV[]) {
310 
311 	CONSOLE_SCREEN_BUFFER_INFO  ConsoleScreenBufferInfo;
312 	GetConsoleScreenBufferInfo(
313 		GetStdHandle(STD_OUTPUT_HANDLE),
314 		&ConsoleScreenBufferInfo
315 		);
316 
317 	char *k;
318 	char startdir[MAX_PATH*2];
319 	char exename[MAX_PATH];
320 
321 	// strncpy(startdir, ArgV[0],MAX_PATH);
322 	// This should be more accurate than using argv[0] (Paul Brannan 9/16/98)
323 	GetModuleFileName(NULL, startdir, sizeof(startdir));
324 
325 	// Get the current console title so it can be set later
326 	// ("Pedro A. Aranda Guti�rrez" <paag@coppi.tid.es>)
327 	TCHAR ConsoleTitle[255];
328 	GetConsoleTitle(ConsoleTitle, sizeof(ConsoleTitle));
329 
330 	k = strrchr(startdir, '\\');
331 	if (k == NULL){						// if the \ character is not found...
332 		strcpy(exename, startdir);
333 		strcpy(startdir,"");			// set the path to nothing
334 	} else {
335 		// end the string after the last '\' to get rid of the file name
336 		strcpy(exename, k+1);
337 		k[1] = 0;
338 	}
339 
340 	printm(0, FALSE, MSG_COPYRIGHT);
341 	printm(0, FALSE, MSG_COPYRIGHT_1);
342 
343 	// set up the ini class
344 	ini.init(startdir, exename);
345 
346 	// Process the command line arguments and connect to a host if necessary
347 	if(ini.Process_Params(ArgC, ArgV)) {
348 		const char *szHost = ini.get_host();
349 		const char *strPort = ini.get_port();
350 		if(!*szHost) {
351 			Telnet MyConnection;
352 			while(telCommandLine(MyConnection));
353 		} else {
354 			Telnet MyConnection;
355 			if(MyConnection.Open(szHost, strPort) == TNPROMPT) {
356 				// still connected
357 				printit("\n");
358 				telCommandLine(MyConnection);
359 			}
360 		}
361 	}
362 	//// (Paul Brannan 5/14/98)
363 
364 	if(ini.get_term_width() != -1 || ini.get_term_height() != -1) {
365 		SetConsoleScreenBufferSize(
366 			GetStdHandle(STD_OUTPUT_HANDLE),	// handle of console screen buffer
367 			ConsoleScreenBufferInfo.dwSize		// new size in character rows and cols.
368 			);
369 		SetConsoleWindowInfo(
370 			GetStdHandle(STD_OUTPUT_HANDLE),	// handle of console screen buffer
371 			TRUE,								// coordinate type flag
372 			&ConsoleScreenBufferInfo.srWindow 	// address of new window rectangle
373 			);
374 	}
375 	SetConsoleTextAttribute(
376 		GetStdHandle(STD_OUTPUT_HANDLE),		// handle of console screen buffer
377 		ConsoleScreenBufferInfo.wAttributes 	// text and background colors
378 		);
379 
380 	// Restore the original console title
381 	// ("Pedro A. Aranda Guti�rrez" <paag@coppi.tid.es>)
382 	SetConsoleTitle(ConsoleTitle);
383 
384 	return 0;
385 }
386 
387 // AVS
388 enum {
389 	BAD_USAGE = -3,
390 		EMPTY_LINE = -2,
391 		INVALID_CMD = -1,
392 		__FIRST_COMMAND = 0,
393 
394 		OPEN = __FIRST_COMMAND,
395 		CLOSE,
396 		KEYS,
397 		QUIT,
398 		HELP,
399 		HELP2,				// there is way for synonims
400 		K_LOAD,				// subcommand of 'keys'
401 		K_SWITCH,			// subcommand of 'keys'
402 		K_DISPLAY,			// subcommand of 'keys'
403 
404 		SET,				// Paul Brannan 5/30/98
405 
406 		SUSPEND,
407 		FASTQUIT,			// Thomas Briggs 8/11/98
408 		CMD_HISTORY,		// crn@ozemail.com.au
409 		CLEAR_HISTORY,		// crn@ozemail.com.au
410 
411 		ALIASES,			// Paul Brannan 1/1/99
412 
413 		__COMMAND_LIST_SIZE	// must be last
414 };
415 
416 
417 struct command {
418 	const char* cmd;				// command
419 	int   minLen,			// minimal length for match
420 		  minParms,			// minimal count of parms
421 		  maxParms;			// maximal -/- (negative disables)
422 	int   isSubCmd,			// is a subcommand - number of wich command
423 		  haveSubCmd;		// have subcommands? 0 or 1
424 	const char* usage;			// text of usage
425 };
426 
427 command cmdList[__COMMAND_LIST_SIZE] = {
428 	{"open",     1,	1,  2,	-1,		0,	"o[pen] host [port]\n"},
429 	{"close",    2,	0,  0,	-1,		0,	NULL},
430 	{"keys",     2,	1,  3,	-1,		1,	"ke[ys] l[oad] keymapname [file]\n"
431 										"ke[ys] d[isplay]\n"
432 										"ke[ys] s[witch] number\n"},
433 	// Ioannou : i change it to q, to be more compatible with unix telnet
434 	{"quit",     1,	0,  0,	-1,		0,	NULL}, // must type it exactly
435 	{"?",        1,	0,  0,	-1,		0,	NULL},
436 	{"help",     1,	0,  0,	-1,		0,	NULL},
437 	{"load",     1,	1,  2,	KEYS,	0,	NULL},
438 	{"switch",   1,	1,  1,	KEYS,	0,	NULL},
439 	{"display",  1,	0,  0,	KEYS,	0,	NULL},
440 	// Paul Brannan 5/30/98
441 	{"set",      3,	0,	2,	-1,		0,	"set will display available groups.\n"
442 										"set groupname will display all variables/values in a group.\n"
443 										"set [variable [value]] will set variable to value.\n"},
444 	// Thomas Briggs 8/11/98
445 	{"z",		1,	0,	0,	-1,		0,	"suspend telnet\n"},
446 	{"\04",		1,	0,	0,	-1,		0,	NULL},
447 	// crn@ozemail.com.au
448 	{"history",	2,	0,	0,	-1,	0,	"show command history"},
449 	{"flush",	2,	0,	0,	-1,	0,	"flush history buffer"},
450 	// Paul Brannan 1/1/99
451 	{"aliases",	5,	0,	0,	-1,		0,	NULL}
452 };
453 
454 // a maximal count of parms
455 #define MAX_PARM_COUNT 3
456 #define MAX_TOKEN_COUNT (MAX_PARM_COUNT+2)
457 
458 static int cmdMatch(const char* cmd, const char* token, int tokenLen, int minM) {
459     if ( tokenLen < minM ) return 0;
460 	// The (unsigned) gets rid of a compiler warning (Paul Brannan 5/25/98)
461     if ( (unsigned)tokenLen > strlen(cmd) ) return 0;
462     if ( strcmp(cmd,token) == 0 ) return 1;
463 
464     int i;
465     for ( i = 0; i < minM; i++ ) if ( cmd[i] != token[i] ) return 0;
466 
467     for ( i = minM; i < tokenLen; i++ ) if ( cmd[i] != token[i] ) return 0;
468 
469     return 1;
470 };
471 
472 static void printUsage(int cmd) {
473 	if ( cmdList[cmd].usage != NULL ) {
474 		printit(cmdList[cmd].usage);
475 		return;
476 	};
477 	if ( cmdList[cmd].isSubCmd >= 0 ) {
478 		printUsage(cmdList[cmd].isSubCmd);
479 		return;
480 	   }
481 	   printm(0, FALSE, MSG_BADUSAGE);
482 };
483 
484 int tokenizeCommand(char* szCommand, int& argc, char** argv) {
485     char* tokens[MAX_TOKEN_COUNT];
486     char* p;
487     int   args = 0;
488 
489 	if(!szCommand || !*szCommand) return EMPTY_LINE;
490 
491 	// Removed strtok to handle tokens with spaces; this is handled with
492 	// quotes.  (Paul Brannan 3/18/99)
493 	char *token_start = szCommand;
494 	for(p = szCommand;; p++) {
495 		if(*p == '\"') {
496 			char *tmp = p;
497 			for(p++; *p != '\"' && *p != 0; p++);	// Find the next quote
498 			if(*p != 0) strcpy(p, p + 1);			// Remove quote#2
499 			strcpy(tmp, tmp + 1);					// Remove quote#1
500 		}
501 		if(*p == 0 || *p == ' ' || *p == '\t') {
502 			tokens[args] = token_start;
503 			args++;
504 			if(args >= MAX_TOKEN_COUNT) break;		// Break if too many args
505 			token_start = p + 1;
506 			if(*p == 0) break;
507 			*p = 0;
508 		}
509 	}
510 	// while ( (p = strtok((args?NULL:szCommand), " \t")) != NULL && args < MAX_TOKEN_COUNT ) {
511 	// 	tokens[args] = p;
512 	// 	args++;
513 	// };
514 
515     if ( !args ) return EMPTY_LINE;
516     argc = args - 1;
517     args = 0;
518     int curCmd = -1;
519     int ok = -1;
520     while ( ok < 0 ) {
521 		int tokenLen = strlen(tokens[args]);
522 		int match = 0;
523 		for ( int i = 0; i<__COMMAND_LIST_SIZE; i++ ) {
524 			if ( cmdMatch(cmdList[i].cmd, tokens[args], tokenLen, cmdList[i].minLen) ) {
525 				if (argc < cmdList[i].minParms || argc > cmdList[i].maxParms) {
526 					printUsage(i);
527 					return BAD_USAGE;
528 				};
529 				if ( cmdList[i].haveSubCmd && curCmd == cmdList[i].isSubCmd) {
530 					curCmd = i;
531 					args++;
532 					argc--;
533 					match = 1;
534 					break;
535 				};
536 				if ( curCmd == cmdList[i].isSubCmd ) {
537 					ok = i;
538 					match = 1;
539 					break;
540 				};
541 				printUsage(i);
542 				return BAD_USAGE;
543 			};
544 		};
545 		if ( !match ) {
546 			if ( curCmd < 0 ) return INVALID_CMD;
547 			printUsage(curCmd);
548 			return -3;
549 		};
550     };
551 
552     for ( int i = 0; i<argc; i++ ) {
553         argv[i] = tokens[i+args+1];
554     };
555     return ok;
556 
557 };
558 
559 int telCommandLine (Telnet &MyConnection){
560 #define HISTLENGTH 25
561 	int i, retval;
562 	char* Parms[MAX_PARM_COUNT];
563 	char szCommand[80];
564 	int bDone = 0;
565 	char *extitle, *newtitle;
566 	struct cmdHistory *cmdhist;
567 	cmdhist = NULL;
568 
569 	// printit("\n");  // crn@ozemail.com.au 14/12/98
570 	while (!bDone){
571 		// printit("\n"); // Paul Brannan 5/25/98
572 		printit( "telnet>");
573 		cmdhist = cfgets (szCommand, 79, cmdhist);
574 		printit( "\n");
575 
576 		strlwr(szCommand);  // convert command line to lower
577 		// i = sscanf(szCommand,"%80s %80s %80s %80s", szCmd, szArg1, szArg2, szArg3);
578 		switch ( tokenizeCommand(szCommand, i, Parms) ) {
579 		case BAD_USAGE:   break;
580 		case EMPTY_LINE:
581 			if(MyConnection.Resume() == TNPROMPT) {
582 				printit("\n");
583 				break;
584 			}
585 			else
586 			 	return 1;
587 		case INVALID_CMD:
588 			printm(0, FALSE, MSG_INVCMD);
589 			break;
590 		case OPEN:
591 			if (i == 1)
592 				retval = MyConnection.Open(Parms[0], "23");
593 			else
594 				retval = MyConnection.Open(Parms[0], Parms[1]);
595 			if(retval != TNNOCON && retval != TNPROMPT) return 1;
596 			if(retval == TNPROMPT) printit("\n");
597 			break;
598 		case CLOSE:
599 			MyConnection.Close();
600 			break;
601 		case FASTQUIT: // Thomas Briggs 8/11/98
602 		case QUIT:
603 			MyConnection.Close();
604 			bDone = 1;
605 			break;
606 		case HELP:
607 		case HELP2:
608 			printm(0, FALSE, MSG_HELP);
609 			printm(0, FALSE, MSG_HELP_1);
610 			break;
611 			// case KEYS: we should never get it
612 		case K_LOAD:
613 			if ( i == 1 ) {
614 				// Ioannou : changed to ini.get_keyfile()
615 				if(MyConnection.LoadKeyMap( ini.get_keyfile(), Parms[0]) != 1)
616 					printit("Error loading keymap.\n");
617 				break;
618 			};
619 			if(MyConnection.LoadKeyMap( Parms[1], Parms[0]) != 1)
620 				printit("Error loading keymap.\n");
621 			break;
622 		case K_DISPLAY:
623 			MyConnection.DisplayKeyMap();
624 			break;
625 		case K_SWITCH:
626 			MyConnection.SwitchKeyMap(atoi(Parms[0]));
627 			break;
628 
629 			// Paul Brannan 5/30/98
630 		case SET:
631 			if(i == 0) {
632 				printit("Available groups:\n");		// Print out groups
633 				ini.print_groups();					// (Paul Brannan 9/3/98)
634 			} else if(i == 1) {
635 				ini.print_vars(Parms[0]);
636 			} else if(i >= 2) {
637 				ini.set_value(Parms[0], Parms[1]);
638 				// FIX ME !!! Ioannou: here we must call the parser routine for
639 				// wrap line, not the ini.set_value
640 				//  something like Parser.ConLineWrap(Wrap_Line);
641 			}
642 			break;
643 
644 		case SUSPEND: // Thomas Briggs 8/11/98
645 
646 			// remind the user we're suspended -crn@ozemail.com.au 15/12/98
647 			extitle = new char[128];
648 			GetConsoleTitle (extitle, 128);
649 
650 			newtitle = new char[128+sizeof("[suspended]")];
651 			strcpy(newtitle, extitle);
652 			strncat(newtitle, "[suspended]", 128+sizeof("[suspended]"));
653 			if(ini.get_set_title()) SetConsoleTitle (newtitle);
654 			delete[] newtitle;
655 
656 			if (getenv("comspec") == NULL) {
657 				switch (GetWin32Version()) {
658 				case 2:		// 'cmd' is faster than 'command' in NT -crn@ozemail.com.au
659 					system ("cmd");
660 					break;
661 				default:
662 					system ("command");
663 					break;
664 				}
665 			} else {
666 				system(getenv("comspec"));
667 			}
668 
669 			if(ini.get_set_title()) SetConsoleTitle (extitle);
670 			delete[] extitle;
671 			///
672 
673 			break;
674 
675 		case CMD_HISTORY:	//crn@ozemail.com.au
676 			if (cmdhist != NULL) {
677 				while (cmdhist->prev != NULL)
678 					cmdhist = cmdhist->prev;	//rewind
679 				printf ("Command history:\n");
680 				while (1) {
681 					printf ("\t%s\n", cmdhist->cmd);
682 
683 					if (cmdhist->next != NULL)
684 						cmdhist = cmdhist->next;
685 					else
686 						break;
687 				}
688 			} else
689 				printf ("No command history available.\n");
690 
691 			break;
692 
693 		case CLEAR_HISTORY:	//crn@ozemail.com.au
694 			if (cmdhist != NULL) {
695 				while (cmdhist->next != NULL)
696 					cmdhist = cmdhist->next;	//fast forward
697 				while (cmdhist->prev != NULL) {
698 					cmdhist = cmdhist->prev;
699 					delete cmdhist->next;
700 				}
701 				delete cmdhist;
702 				cmdhist = NULL;
703 				printf ("Command history cleared.\n");
704 			} else
705 				printf ("No command history available.\n");
706 
707 		case ALIASES: // Paul Brannan 1/1/99
708 			ini.print_aliases();
709 			break;
710 
711 		default: // paranoik
712 			printm(0, FALSE, MSG_INVCMD);
713 			break;
714 		}
715 
716 	}
717 
718 	return 0;
719 }
720