d930dc1189ad1623728a9a414ff73f7b578020e3
[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 #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 }