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 { 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 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 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 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 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 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