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