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
;
163 case VK_LEFT
: //crn@ozemail.com.au (added ctrl+arrow)
165 if(InputRecord
.Event
.KeyEvent
.dwControlKeyState
&
166 (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) {
168 for (j
= cursor
; j
>= 0; j
--)
171 for (k
= --j
; k
>= 0; k
--)
172 if (buf
[k
] == ' ' || k
== 0) {
173 cursor
= !k
? k
: ++k
;
182 if (cursor
>0) cursor
= 0;
186 if (cursor
<current
) cursor
= current
;
190 if (current
> 0 && current
> cursor
) {
191 strcpy(&buf
[cursor
],&buf
[cursor
+1]);
195 for (i
= 0; i
< current
+strlen("telnet>")+1 ;i
++)
202 strcpy(&buf
[cursor
-1],&buf
[cursor
]);
207 for (i
= 0; i
< current
+strlen("telnet>")+1 ;i
++)
214 chr
= InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
219 if (current
>= length
-1){
223 if ( isprint (chr
) ){
224 strncpy(temp1
,&buf
[cursor
],79);
225 strncpy(&buf
[cursor
+1],temp1
,79-(cursor
+1));
233 if (MustRefresh
== 1)
236 for (i
= 0; i
<= iEraseLength
;i
++)
238 printit("\rtelnet>");
240 iEraseLength
= strlen(buf
);
241 for (i
= 0; i
< current
-cursor
; i
++)
247 if (strcmp(buf
, "")) {
248 if (cmdhist
== NULL
) {
249 cmdhist
= new struct cmdHistory
;
250 if (cmdhist
== NULL
) {
251 printit ("\nUnable to allocate memory for history buffer -- use the \"flush\" command to clear the buffer.\n");
254 strncpy(cmdhist
->cmd
, buf
, 79);
255 cmdhist
->next
= NULL
;
256 cmdhist
->prev
= NULL
;
258 while (cmdhist
->next
!= NULL
) // move to the end of the list
259 cmdhist
= cmdhist
->next
;
260 cmdhist
->next
= new struct cmdHistory
;
261 if (cmdhist
->next
== NULL
) {
262 printit ("\nUnable to allocate memory for history buffer -- use the \"flush\" command to clear the buffer.\n");
265 cmdhist
->next
->prev
= cmdhist
; // previous is where we are now
266 cmdhist
= cmdhist
->next
;
267 strncpy(cmdhist
->cmd
, buf
, 79);
268 cmdhist
->next
= NULL
;
270 while (cmdhist
->next
)
271 cmdhist
= cmdhist
->next
;
276 WaitForSingleObject( hConsole
, INFINITE
);
279 GetConsoleMode(hConsole
, &OldMode
);
280 SetConsoleMode(hConsole
,
281 OldMode
&~ (ENABLE_ECHO_INPUT
| ENABLE_LINE_INPUT
) );
282 while (ReadFile(hConsole
, &chr
, 1, &dwInput
, NULL
)) {
288 if (chr
== '\b' && current
> 0) {
292 if (current
>= length
-1){
295 if ( isprint (chr
) ){
302 SetConsoleMode(hConsole
, OldMode
);
307 // AVS ** for fix bug in command 'keys load keymapname' without file
308 // static char keyfile[MAX_PATH*2];
310 int main(int ArgC
, char* ArgV
[]) {
312 CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo
;
313 GetConsoleScreenBufferInfo(
314 GetStdHandle(STD_OUTPUT_HANDLE
),
315 &ConsoleScreenBufferInfo
319 char startdir
[MAX_PATH
*2];
320 char exename
[MAX_PATH
];
322 // strncpy(startdir, ArgV[0],MAX_PATH);
323 // This should be more accurate than using argv[0] (Paul Brannan 9/16/98)
324 GetModuleFileName(NULL
, startdir
, sizeof(startdir
));
326 // Get the current console title so it can be set later
327 // ("Pedro A. Aranda Gutiérrez" <paag@coppi.tid.es>)
328 TCHAR ConsoleTitle
[255];
329 GetConsoleTitle(ConsoleTitle
, sizeof(ConsoleTitle
));
331 k
= strrchr(startdir
, '\\');
332 if (k
== NULL
){ // if the \ character is not found...
333 strcpy(exename
, startdir
);
334 strcpy(startdir
,""); // set the path to nothing
336 // end the string after the last '\' to get rid of the file name
337 strcpy(exename
, k
+1);
341 printm(0, FALSE
, MSG_COPYRIGHT
);
342 printm(0, FALSE
, MSG_COPYRIGHT_1
);
344 // set up the ini class
345 ini
.init(startdir
, exename
);
347 // Process the command line arguments and connect to a host if necessary
348 if(ini
.Process_Params(ArgC
, ArgV
)) {
349 const char *szHost
= ini
.get_host();
350 const char *strPort
= ini
.get_port();
353 while(telCommandLine(MyConnection
));
356 if(MyConnection
.Open(szHost
, strPort
) == TNPROMPT
) {
359 telCommandLine(MyConnection
);
363 //// (Paul Brannan 5/14/98)
365 if(ini
.get_term_width() != -1 || ini
.get_term_height() != -1) {
366 SetConsoleScreenBufferSize(
367 GetStdHandle(STD_OUTPUT_HANDLE
), // handle of console screen buffer
368 ConsoleScreenBufferInfo
.dwSize
// new size in character rows and cols.
370 SetConsoleWindowInfo(
371 GetStdHandle(STD_OUTPUT_HANDLE
), // handle of console screen buffer
372 TRUE
, // coordinate type flag
373 &ConsoleScreenBufferInfo
.srWindow
// address of new window rectangle
376 SetConsoleTextAttribute(
377 GetStdHandle(STD_OUTPUT_HANDLE
), // handle of console screen buffer
378 ConsoleScreenBufferInfo
.wAttributes
// text and background colors
381 // Restore the original console title
382 // ("Pedro A. Aranda Gutiérrez" <paag@coppi.tid.es>)
383 SetConsoleTitle(ConsoleTitle
);
395 OPEN
= __FIRST_COMMAND
,
400 HELP2
, // there is way for synonims
401 K_LOAD
, // subcommand of 'keys'
402 K_SWITCH
, // subcommand of 'keys'
403 K_DISPLAY
, // subcommand of 'keys'
405 SET
, // Paul Brannan 5/30/98
408 FASTQUIT
, // Thomas Briggs 8/11/98
409 CMD_HISTORY
, // crn@ozemail.com.au
410 CLEAR_HISTORY
, // crn@ozemail.com.au
412 ALIASES
, // Paul Brannan 1/1/99
414 __COMMAND_LIST_SIZE
// must be last
419 const char* cmd
; // command
420 int minLen
, // minimal length for match
421 minParms
, // minimal count of parms
422 maxParms
; // maximal -/- (negative disables)
423 int isSubCmd
, // is a subcommand - number of wich command
424 haveSubCmd
; // have subcommands? 0 or 1
425 const char* usage
; // text of usage
428 command cmdList
[__COMMAND_LIST_SIZE
] = {
429 {"open", 1, 1, 2, -1, 0, "o[pen] host [port]\n"},
430 {"close", 2, 0, 0, -1, 0, NULL
},
431 {"keys", 2, 1, 3, -1, 1, "ke[ys] l[oad] keymapname [file]\n"
433 "ke[ys] s[witch] number\n"},
434 // Ioannou : i change it to q, to be more compatible with unix telnet
435 {"quit", 1, 0, 0, -1, 0, NULL
}, // must type it exactly
436 {"?", 1, 0, 0, -1, 0, NULL
},
437 {"help", 1, 0, 0, -1, 0, NULL
},
438 {"load", 1, 1, 2, KEYS
, 0, NULL
},
439 {"switch", 1, 1, 1, KEYS
, 0, NULL
},
440 {"display", 1, 0, 0, KEYS
, 0, NULL
},
441 // Paul Brannan 5/30/98
442 {"set", 3, 0, 2, -1, 0, "set will display available groups.\n"
443 "set groupname will display all variables/values in a group.\n"
444 "set [variable [value]] will set variable to value.\n"},
445 // Thomas Briggs 8/11/98
446 {"z", 1, 0, 0, -1, 0, "suspend telnet\n"},
447 {"\04", 1, 0, 0, -1, 0, NULL
},
448 // crn@ozemail.com.au
449 {"history", 2, 0, 0, -1, 0, "show command history"},
450 {"flush", 2, 0, 0, -1, 0, "flush history buffer"},
451 // Paul Brannan 1/1/99
452 {"aliases", 5, 0, 0, -1, 0, NULL
}
455 // a maximal count of parms
456 #define MAX_PARM_COUNT 3
457 #define MAX_TOKEN_COUNT (MAX_PARM_COUNT+2)
459 static int cmdMatch(const char* cmd
, const char* token
, int tokenLen
, int minM
) {
460 if ( tokenLen
< minM
) return 0;
461 // The (unsigned) gets rid of a compiler warning (Paul Brannan 5/25/98)
462 if ( (unsigned)tokenLen
> strlen(cmd
) ) return 0;
463 if ( strcmp(cmd
,token
) == 0 ) return 1;
466 for ( i
= 0; i
< minM
; i
++ ) if ( cmd
[i
] != token
[i
] ) return 0;
468 for ( i
= minM
; i
< tokenLen
; i
++ ) if ( cmd
[i
] != token
[i
] ) return 0;
473 static void printUsage(int cmd
) {
474 if ( cmdList
[cmd
].usage
!= NULL
) {
475 printit(cmdList
[cmd
].usage
);
478 if ( cmdList
[cmd
].isSubCmd
>= 0 ) {
479 printUsage(cmdList
[cmd
].isSubCmd
);
482 printm(0, FALSE
, MSG_BADUSAGE
);
485 int tokenizeCommand(char* szCommand
, int& argc
, char** argv
) {
486 char* tokens
[MAX_TOKEN_COUNT
];
490 if(!szCommand
|| !*szCommand
) return EMPTY_LINE
;
492 // Removed strtok to handle tokens with spaces; this is handled with
493 // quotes. (Paul Brannan 3/18/99)
494 char *token_start
= szCommand
;
495 for(p
= szCommand
;; p
++) {
498 for(p
++; *p
!= '\"' && *p
!= 0; p
++); // Find the next quote
499 if(*p
!= 0) strcpy(p
, p
+ 1); // Remove quote#2
500 strcpy(tmp
, tmp
+ 1); // Remove quote#1
502 if(*p
== 0 || *p
== ' ' || *p
== '\t') {
503 tokens
[args
] = token_start
;
505 if(args
>= MAX_TOKEN_COUNT
) break; // Break if too many args
511 // while ( (p = strtok((args?NULL:szCommand), " \t")) != NULL && args < MAX_TOKEN_COUNT ) {
516 if ( !args
) return EMPTY_LINE
;
522 int tokenLen
= strlen(tokens
[args
]);
524 for ( int i
= 0; i
<__COMMAND_LIST_SIZE
; i
++ ) {
525 if ( cmdMatch(cmdList
[i
].cmd
, tokens
[args
], tokenLen
, cmdList
[i
].minLen
) ) {
526 if (argc
< cmdList
[i
].minParms
|| argc
> cmdList
[i
].maxParms
) {
530 if ( cmdList
[i
].haveSubCmd
&& curCmd
== cmdList
[i
].isSubCmd
) {
537 if ( curCmd
== cmdList
[i
].isSubCmd
) {
547 if ( curCmd
< 0 ) return INVALID_CMD
;
553 for ( int i
= 0; i
<argc
; i
++ ) {
554 argv
[i
] = tokens
[i
+args
+1];
560 int telCommandLine (Telnet
&MyConnection
){
561 #define HISTLENGTH 25
563 char* Parms
[MAX_PARM_COUNT
];
566 char *extitle
, *newtitle
;
567 struct cmdHistory
*cmdhist
;
570 // printit("\n"); // crn@ozemail.com.au 14/12/98
572 // printit("\n"); // Paul Brannan 5/25/98
574 cmdhist
= cfgets (szCommand
, 79, cmdhist
);
577 strlwr(szCommand
); // convert command line to lower
578 // i = sscanf(szCommand,"%80s %80s %80s %80s", szCmd, szArg1, szArg2, szArg3);
579 switch ( tokenizeCommand(szCommand
, i
, Parms
) ) {
580 case BAD_USAGE
: break;
582 if(MyConnection
.Resume() == TNPROMPT
) {
589 printm(0, FALSE
, MSG_INVCMD
);
593 retval
= MyConnection
.Open(Parms
[0], "23");
595 retval
= MyConnection
.Open(Parms
[0], Parms
[1]);
596 if(retval
!= TNNOCON
&& retval
!= TNPROMPT
) return 1;
597 if(retval
== TNPROMPT
) printit("\n");
600 MyConnection
.Close();
602 case FASTQUIT
: // Thomas Briggs 8/11/98
604 MyConnection
.Close();
609 printm(0, FALSE
, MSG_HELP
);
610 printm(0, FALSE
, MSG_HELP_1
);
612 // case KEYS: we should never get it
615 // Ioannou : changed to ini.get_keyfile()
616 if(MyConnection
.LoadKeyMap( ini
.get_keyfile(), Parms
[0]) != 1)
617 printit("Error loading keymap.\n");
620 if(MyConnection
.LoadKeyMap( Parms
[1], Parms
[0]) != 1)
621 printit("Error loading keymap.\n");
624 MyConnection
.DisplayKeyMap();
627 MyConnection
.SwitchKeyMap(atoi(Parms
[0]));
630 // Paul Brannan 5/30/98
633 printit("Available groups:\n"); // Print out groups
634 ini
.print_groups(); // (Paul Brannan 9/3/98)
636 ini
.print_vars(Parms
[0]);
638 ini
.set_value(Parms
[0], Parms
[1]);
639 // FIX ME !!! Ioannou: here we must call the parser routine for
640 // wrap line, not the ini.set_value
641 // something like Parser.ConLineWrap(Wrap_Line);
645 case SUSPEND
: // Thomas Briggs 8/11/98
647 // remind the user we're suspended -crn@ozemail.com.au 15/12/98
648 extitle
= new char[128];
649 GetConsoleTitle (extitle
, 128);
651 newtitle
= new char[128+sizeof("[suspended]")];
652 strcpy(newtitle
, extitle
);
653 strncat(newtitle
, "[suspended]", 128+sizeof("[suspended]"));
654 if(ini
.get_set_title()) SetConsoleTitle (newtitle
);
657 if (getenv("comspec") == NULL
) {
658 switch (GetWin32Version()) {
659 case 2: // 'cmd' is faster than 'command' in NT -crn@ozemail.com.au
667 system(getenv("comspec"));
670 if(ini
.get_set_title()) SetConsoleTitle (extitle
);
676 case CMD_HISTORY
: //crn@ozemail.com.au
677 if (cmdhist
!= NULL
) {
678 while (cmdhist
->prev
!= NULL
)
679 cmdhist
= cmdhist
->prev
; //rewind
680 printf ("Command history:\n");
682 printf ("\t%s\n", cmdhist
->cmd
);
684 if (cmdhist
->next
!= NULL
)
685 cmdhist
= cmdhist
->next
;
690 printf ("No command history available.\n");
694 case CLEAR_HISTORY
: //crn@ozemail.com.au
695 if (cmdhist
!= NULL
) {
696 while (cmdhist
->next
!= NULL
)
697 cmdhist
= cmdhist
->next
; //fast forward
698 while (cmdhist
->prev
!= NULL
) {
699 cmdhist
= cmdhist
->prev
;
700 delete cmdhist
->next
;
704 printf ("Command history cleared.\n");
706 printf ("No command history available.\n");
708 case ALIASES
: // Paul Brannan 1/1/99
713 printm(0, FALSE
, MSG_INVCMD
);