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