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