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