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
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.
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.
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.
24 ///////////////////////////////////////////////////////////////////////////
26 ///////////////////////////////////////////////////////////////////////////////
30 // Contents: telnet main program
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
40 // 02.Apr.1995 igor.milavec@uni-lj.si
43 ///////////////////////////////////////////////////////////////////////////////
50 int telCommandLine (Telnet
&MyConnection
);
53 HANDLE hConsole
= GetStdHandle(STD_INPUT_HANDLE
);
54 INPUT_RECORD InputRecord
;
58 WaitForSingleObject( hConsole
, INFINITE
);
59 if (!ReadConsoleInput(hConsole
, &InputRecord
, 1, &dwInput
)){
63 if (InputRecord
.EventType
== KEY_EVENT
&&
64 InputRecord
.Event
.KeyEvent
.bKeyDown
)
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
) {
72 HANDLE hConsole
= GetStdHandle(STD_INPUT_HANDLE
);
73 unsigned int current
=0, cursor
=0, iEraseLength
=0, i
;
78 INPUT_RECORD InputRecord
;
84 if(!ini
.get_input_redir()) {
88 WaitForSingleObject( hConsole
, INFINITE
);
89 if (!ReadConsoleInput(hConsole
, &InputRecord
, 1, &dwInput
)){
94 if (InputRecord
.EventType
== KEY_EVENT
&&
95 InputRecord
.Event
.KeyEvent
.bKeyDown
) {
97 if(InputRecord
.Event
.KeyEvent
.dwControlKeyState
&
98 (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) {
100 switch(InputRecord
.Event
.KeyEvent
.wVirtualKeyCode
) {
101 case 'D': // Thomas Briggs 8/11/98
107 case 'U': // Paul Brannan 8/11/98
116 switch (InputRecord
.Event
.KeyEvent
.wVirtualKeyCode
) {
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);
126 current
= strlen(buf
);
132 // crn@ozemail.com.au
133 if (cmdhist
!= NULL
) {
134 if (cmdhist
->next
!= NULL
) {
135 cmdhist
= cmdhist
->next
;
136 strncpy(buf
, cmdhist
->cmd
, 79);
138 strncpy(buf
, "", 79);
140 current
= strlen(buf
);
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
)) {
150 for (j
= cursor
; j
<= current
; j
++)
151 if (buf
[j
+1] == ' ' || (j
+1)==current
)
153 for (k
= ++j
; k
<= current
; k
++)
154 if (buf
[k
] != ' ' || k
== current
) {
155 cursor
= k
== current
? --k
: k
;
162 case VK_LEFT
: //crn@ozemail.com.au (added ctrl+arrow)
164 if(InputRecord
.Event
.KeyEvent
.dwControlKeyState
&
165 (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) {
167 for (j
= cursor
; j
>= 0; j
--)
170 for (k
= --j
; k
>= 0; k
--)
171 if (buf
[k
] == ' ' || k
== 0) {
172 cursor
= !k
? k
: ++k
;
180 if (cursor
>0) cursor
= 0;
184 if (cursor
<current
) cursor
= current
;
188 if (current
> 0 && current
> cursor
) {
189 strcpy(&buf
[cursor
],&buf
[cursor
+1]);
193 for (i
= 0; i
< current
+strlen("telnet>")+1 ;i
++)
200 strcpy(&buf
[cursor
-1],&buf
[cursor
]);
205 for (i
= 0; i
< current
+strlen("telnet>")+1 ;i
++)
212 chr
= InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
217 if (current
>= length
-1){
221 if ( isprint (chr
) ){
222 strncpy(temp1
,&buf
[cursor
],79);
223 strncpy(&buf
[cursor
+1],temp1
,79-(cursor
+1));
231 if (MustRefresh
== 1)
234 for (i
= 0; i
<= iEraseLength
;i
++)
236 printit("\rtelnet>");
238 iEraseLength
= strlen(buf
);
239 for (i
= 0; i
< current
-cursor
; i
++)
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");
252 strncpy(cmdhist
->cmd
, buf
, 79);
253 cmdhist
->next
= NULL
;
254 cmdhist
->prev
= NULL
;
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");
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
;
268 while (cmdhist
->next
)
269 cmdhist
= cmdhist
->next
;
274 WaitForSingleObject( hConsole
, INFINITE
);
277 GetConsoleMode(hConsole
, &OldMode
);
278 SetConsoleMode(hConsole
,
279 OldMode
&~ (ENABLE_ECHO_INPUT
| ENABLE_LINE_INPUT
) );
280 while (ReadFile(hConsole
, &chr
, 1, &dwInput
, NULL
)) {
286 if (chr
== '\b' && current
> 0) {
290 if (current
>= length
-1){
293 if ( isprint (chr
) ){
300 SetConsoleMode(hConsole
, OldMode
);
305 // AVS ** for fix bug in command 'keys load keymapname' without file
306 // static char keyfile[MAX_PATH*2];
308 int main(int ArgC
, char* ArgV
[]) {
310 CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo
;
311 GetConsoleScreenBufferInfo(
312 GetStdHandle(STD_OUTPUT_HANDLE
),
313 &ConsoleScreenBufferInfo
317 char startdir
[MAX_PATH
*2];
318 char exename
[MAX_PATH
];
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
));
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
));
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
334 // end the string after the last '\' to get rid of the file name
335 strcpy(exename
, k
+1);
339 printm(0, FALSE
, MSG_COPYRIGHT
);
340 printm(0, FALSE
, MSG_COPYRIGHT_1
);
342 // set up the ini class
343 ini
.init(startdir
, exename
);
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();
351 while(telCommandLine(MyConnection
));
354 if(MyConnection
.Open(szHost
, strPort
) == TNPROMPT
) {
357 telCommandLine(MyConnection
);
361 //// (Paul Brannan 5/14/98)
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.
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
374 SetConsoleTextAttribute(
375 GetStdHandle(STD_OUTPUT_HANDLE
), // handle of console screen buffer
376 ConsoleScreenBufferInfo
.wAttributes
// text and background colors
379 // Restore the original console title
380 // ("Pedro A. Aranda Gutiérrez" <paag@coppi.tid.es>)
381 SetConsoleTitle(ConsoleTitle
);
393 OPEN
= __FIRST_COMMAND
,
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'
403 SET
, // Paul Brannan 5/30/98
406 FASTQUIT
, // Thomas Briggs 8/11/98
407 CMD_HISTORY
, // crn@ozemail.com.au
408 CLEAR_HISTORY
, // crn@ozemail.com.au
410 ALIASES
, // Paul Brannan 1/1/99
412 __COMMAND_LIST_SIZE
// must be last
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
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"
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
}
453 // a maximal count of parms
454 #define MAX_PARM_COUNT 3
455 #define MAX_TOKEN_COUNT (MAX_PARM_COUNT+2)
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;
464 for ( i
= 0; i
< minM
; i
++ ) if ( cmd
[i
] != token
[i
] ) return 0;
466 for ( i
= minM
; i
< tokenLen
; i
++ ) if ( cmd
[i
] != token
[i
] ) return 0;
471 static void printUsage(int cmd
) {
472 if ( cmdList
[cmd
].usage
!= NULL
) {
473 printit(cmdList
[cmd
].usage
);
476 if ( cmdList
[cmd
].isSubCmd
>= 0 ) {
477 printUsage(cmdList
[cmd
].isSubCmd
);
480 printm(0, FALSE
, MSG_BADUSAGE
);
483 int tokenizeCommand(char* szCommand
, int& argc
, char** argv
) {
484 char* tokens
[MAX_TOKEN_COUNT
];
488 if(!szCommand
|| !*szCommand
) return EMPTY_LINE
;
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
++) {
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
500 if(*p
== 0 || *p
== ' ' || *p
== '\t') {
501 tokens
[args
] = token_start
;
503 if(args
>= MAX_TOKEN_COUNT
) break; // Break if too many args
509 // while ( (p = strtok((args?NULL:szCommand), " \t")) != NULL && args < MAX_TOKEN_COUNT ) {
514 if ( !args
) return EMPTY_LINE
;
520 int tokenLen
= strlen(tokens
[args
]);
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
) {
528 if ( cmdList
[i
].haveSubCmd
&& curCmd
== cmdList
[i
].isSubCmd
) {
535 if ( curCmd
== cmdList
[i
].isSubCmd
) {
545 if ( curCmd
< 0 ) return INVALID_CMD
;
551 for ( int i
= 0; i
<argc
; i
++ ) {
552 argv
[i
] = tokens
[i
+args
+1];
558 int telCommandLine (Telnet
&MyConnection
){
559 #define HISTLENGTH 25
561 char* Parms
[MAX_PARM_COUNT
];
564 char *extitle
, *newtitle
;
565 struct cmdHistory
*cmdhist
;
568 // printit("\n"); // crn@ozemail.com.au 14/12/98
570 // printit("\n"); // Paul Brannan 5/25/98
572 cmdhist
= cfgets (szCommand
, 79, cmdhist
);
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;
580 if(MyConnection
.Resume() == TNPROMPT
) {
587 printm(0, FALSE
, MSG_INVCMD
);
591 retval
= MyConnection
.Open(Parms
[0], "23");
593 retval
= MyConnection
.Open(Parms
[0], Parms
[1]);
594 if(retval
!= TNNOCON
&& retval
!= TNPROMPT
) return 1;
595 if(retval
== TNPROMPT
) printit("\n");
598 MyConnection
.Close();
600 case FASTQUIT
: // Thomas Briggs 8/11/98
602 MyConnection
.Close();
607 printm(0, FALSE
, MSG_HELP
);
608 printm(0, FALSE
, MSG_HELP_1
);
610 // case KEYS: we should never get it
613 // Ioannou : changed to ini.get_keyfile()
614 if(MyConnection
.LoadKeyMap( ini
.get_keyfile(), Parms
[0]) != 1)
615 printit("Error loading keymap.\n");
618 if(MyConnection
.LoadKeyMap( Parms
[1], Parms
[0]) != 1)
619 printit("Error loading keymap.\n");
622 MyConnection
.DisplayKeyMap();
625 MyConnection
.SwitchKeyMap(atoi(Parms
[0]));
628 // Paul Brannan 5/30/98
631 printit("Available groups:\n"); // Print out groups
632 ini
.print_groups(); // (Paul Brannan 9/3/98)
634 ini
.print_vars(Parms
[0]);
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);
643 case SUSPEND
: // Thomas Briggs 8/11/98
645 // remind the user we're suspended -crn@ozemail.com.au 15/12/98
646 extitle
= new char[128];
647 GetConsoleTitle (extitle
, 128);
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
);
655 if (getenv("comspec") == NULL
) {
656 switch (GetWin32Version()) {
657 case 2: // 'cmd' is faster than 'command' in NT -crn@ozemail.com.au
665 system(getenv("comspec"));
668 if(ini
.get_set_title()) SetConsoleTitle (extitle
);
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");
680 printf ("\t%s\n", cmdhist
->cmd
);
682 if (cmdhist
->next
!= NULL
)
683 cmdhist
= cmdhist
->next
;
688 printf ("No command history available.\n");
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
;
702 printf ("Command history cleared.\n");
704 printf ("No command history available.\n");
706 case ALIASES
: // Paul Brannan 1/1/99
711 printm(0, FALSE
, MSG_INVCMD
);