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 ///////////////////////////////////////////////////////////////////////////////
49 int telCommandLine (Telnet
&MyConnection
);
52 HANDLE hConsole
= GetStdHandle(STD_INPUT_HANDLE
);
53 INPUT_RECORD InputRecord
;
57 WaitForSingleObject( hConsole
, INFINITE
);
58 if (!ReadConsoleInput(hConsole
, &InputRecord
, 1, &dwInput
)){
62 if (InputRecord
.EventType
== KEY_EVENT
&&
63 InputRecord
.Event
.KeyEvent
.bKeyDown
)
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
) {
71 HANDLE hConsole
= GetStdHandle(STD_INPUT_HANDLE
);
72 unsigned int current
=0, cursor
=0, iEraseLength
=0, i
;
77 INPUT_RECORD InputRecord
;
83 if(!ini
.get_input_redir()) {
87 WaitForSingleObject( hConsole
, INFINITE
);
88 if (!ReadConsoleInput(hConsole
, &InputRecord
, 1, &dwInput
)){
93 if (InputRecord
.EventType
== KEY_EVENT
&&
94 InputRecord
.Event
.KeyEvent
.bKeyDown
) {
96 if(InputRecord
.Event
.KeyEvent
.dwControlKeyState
&
97 (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) {
99 switch(InputRecord
.Event
.KeyEvent
.wVirtualKeyCode
) {
100 case 'D': // Thomas Briggs 8/11/98
106 case 'U': // Paul Brannan 8/11/98
115 switch (InputRecord
.Event
.KeyEvent
.wVirtualKeyCode
) {
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);
125 current
= strlen(buf
);
131 // crn@ozemail.com.au
132 if (cmdhist
!= NULL
) {
133 if (cmdhist
->next
!= NULL
) {
134 cmdhist
= cmdhist
->next
;
135 strncpy(buf
, cmdhist
->cmd
, 79);
137 strncpy(buf
, "", 79);
139 current
= strlen(buf
);
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
)) {
149 for (j
= cursor
; j
<= current
; j
++)
150 if (buf
[j
+1] == ' ' || (j
+1)==current
)
152 for (k
= ++j
; k
<= current
; k
++)
153 if (buf
[k
] != ' ' || k
== current
) {
154 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
;
181 if (cursor
>0) cursor
= 0;
185 if (cursor
<current
) cursor
= current
;
189 if (current
> 0 && current
> cursor
) {
190 strcpy(&buf
[cursor
],&buf
[cursor
+1]);
194 for (i
= 0; i
< current
+strlen("telnet>")+1 ;i
++)
201 strcpy(&buf
[cursor
-1],&buf
[cursor
]);
206 for (i
= 0; i
< current
+strlen("telnet>")+1 ;i
++)
213 chr
= InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
218 if (current
>= length
-1){
222 if ( isprint (chr
) ){
223 strncpy(temp1
,&buf
[cursor
],79);
224 strncpy(&buf
[cursor
+1],temp1
,79-(cursor
+1));
232 if (MustRefresh
== 1)
235 for (i
= 0; i
<= iEraseLength
;i
++)
237 printit("\rtelnet>");
239 iEraseLength
= strlen(buf
);
240 for (i
= 0; i
< current
-cursor
; i
++)
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");
253 strncpy(cmdhist
->cmd
, buf
, 79);
254 cmdhist
->next
= NULL
;
255 cmdhist
->prev
= NULL
;
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");
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
;
269 while (cmdhist
->next
)
270 cmdhist
= cmdhist
->next
;
275 WaitForSingleObject( hConsole
, INFINITE
);
278 GetConsoleMode(hConsole
, &OldMode
);
279 SetConsoleMode(hConsole
,
280 OldMode
&~ (ENABLE_ECHO_INPUT
| ENABLE_LINE_INPUT
) );
281 while (ReadFile(hConsole
, &chr
, 1, &dwInput
, NULL
)) {
287 if (chr
== '\b' && current
> 0) {
291 if (current
>= length
-1){
294 if ( isprint (chr
) ){
301 SetConsoleMode(hConsole
, OldMode
);
306 // AVS ** for fix bug in command 'keys load keymapname' without file
307 // static char keyfile[MAX_PATH*2];
309 int main(int ArgC
, char* ArgV
[]) {
311 CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo
;
312 GetConsoleScreenBufferInfo(
313 GetStdHandle(STD_OUTPUT_HANDLE
),
314 &ConsoleScreenBufferInfo
318 char startdir
[MAX_PATH
*2];
319 char exename
[MAX_PATH
];
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
));
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
));
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
335 // end the string after the last '\' to get rid of the file name
336 strcpy(exename
, k
+1);
340 printm(0, FALSE
, MSG_COPYRIGHT
);
341 printm(0, FALSE
, MSG_COPYRIGHT_1
);
343 // set up the ini class
344 ini
.init(startdir
, exename
);
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();
352 while(telCommandLine(MyConnection
));
355 if(MyConnection
.Open(szHost
, strPort
) == TNPROMPT
) {
358 telCommandLine(MyConnection
);
362 //// (Paul Brannan 5/14/98)
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.
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
375 SetConsoleTextAttribute(
376 GetStdHandle(STD_OUTPUT_HANDLE
), // handle of console screen buffer
377 ConsoleScreenBufferInfo
.wAttributes
// text and background colors
380 // Restore the original console title
381 // ("Pedro A. Aranda Gutiérrez" <paag@coppi.tid.es>)
382 SetConsoleTitle(ConsoleTitle
);
394 OPEN
= __FIRST_COMMAND
,
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'
404 SET
, // Paul Brannan 5/30/98
407 FASTQUIT
, // Thomas Briggs 8/11/98
408 CMD_HISTORY
, // crn@ozemail.com.au
409 CLEAR_HISTORY
, // crn@ozemail.com.au
411 ALIASES
, // Paul Brannan 1/1/99
413 __COMMAND_LIST_SIZE
// must be last
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
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"
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
}
454 // a maximal count of parms
455 #define MAX_PARM_COUNT 3
456 #define MAX_TOKEN_COUNT (MAX_PARM_COUNT+2)
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;
465 for ( i
= 0; i
< minM
; i
++ ) if ( cmd
[i
] != token
[i
] ) return 0;
467 for ( i
= minM
; i
< tokenLen
; i
++ ) if ( cmd
[i
] != token
[i
] ) return 0;
472 static void printUsage(int cmd
) {
473 if ( cmdList
[cmd
].usage
!= NULL
) {
474 printit(cmdList
[cmd
].usage
);
477 if ( cmdList
[cmd
].isSubCmd
>= 0 ) {
478 printUsage(cmdList
[cmd
].isSubCmd
);
481 printm(0, FALSE
, MSG_BADUSAGE
);
484 int tokenizeCommand(char* szCommand
, int& argc
, char** argv
) {
485 char* tokens
[MAX_TOKEN_COUNT
];
489 if(!szCommand
|| !*szCommand
) return EMPTY_LINE
;
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
++) {
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
501 if(*p
== 0 || *p
== ' ' || *p
== '\t') {
502 tokens
[args
] = token_start
;
504 if(args
>= MAX_TOKEN_COUNT
) break; // Break if too many args
510 // while ( (p = strtok((args?NULL:szCommand), " \t")) != NULL && args < MAX_TOKEN_COUNT ) {
515 if ( !args
) return EMPTY_LINE
;
521 int tokenLen
= strlen(tokens
[args
]);
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
) {
529 if ( cmdList
[i
].haveSubCmd
&& curCmd
== cmdList
[i
].isSubCmd
) {
536 if ( curCmd
== cmdList
[i
].isSubCmd
) {
546 if ( curCmd
< 0 ) return INVALID_CMD
;
552 for ( int i
= 0; i
<argc
; i
++ ) {
553 argv
[i
] = tokens
[i
+args
+1];
559 int telCommandLine (Telnet
&MyConnection
){
560 #define HISTLENGTH 25
562 char* Parms
[MAX_PARM_COUNT
];
565 char *extitle
, *newtitle
;
566 struct cmdHistory
*cmdhist
;
569 // printit("\n"); // crn@ozemail.com.au 14/12/98
571 // printit("\n"); // Paul Brannan 5/25/98
573 cmdhist
= cfgets (szCommand
, 79, cmdhist
);
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;
581 if(MyConnection
.Resume() == TNPROMPT
) {
588 printm(0, FALSE
, MSG_INVCMD
);
592 retval
= MyConnection
.Open(Parms
[0], "23");
594 retval
= MyConnection
.Open(Parms
[0], Parms
[1]);
595 if(retval
!= TNNOCON
&& retval
!= TNPROMPT
) return 1;
596 if(retval
== TNPROMPT
) printit("\n");
599 MyConnection
.Close();
601 case FASTQUIT
: // Thomas Briggs 8/11/98
603 MyConnection
.Close();
608 printm(0, FALSE
, MSG_HELP
);
609 printm(0, FALSE
, MSG_HELP_1
);
611 // case KEYS: we should never get it
614 // Ioannou : changed to ini.get_keyfile()
615 if(MyConnection
.LoadKeyMap( ini
.get_keyfile(), Parms
[0]) != 1)
616 printit("Error loading keymap.\n");
619 if(MyConnection
.LoadKeyMap( Parms
[1], Parms
[0]) != 1)
620 printit("Error loading keymap.\n");
623 MyConnection
.DisplayKeyMap();
626 MyConnection
.SwitchKeyMap(atoi(Parms
[0]));
629 // Paul Brannan 5/30/98
632 printit("Available groups:\n"); // Print out groups
633 ini
.print_groups(); // (Paul Brannan 9/3/98)
635 ini
.print_vars(Parms
[0]);
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);
644 case SUSPEND
: // Thomas Briggs 8/11/98
646 // remind the user we're suspended -crn@ozemail.com.au 15/12/98
647 extitle
= new char[128];
648 GetConsoleTitle (extitle
, 128);
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
);
656 if (getenv("comspec") == NULL
) {
657 switch (GetWin32Version()) {
658 case 2: // 'cmd' is faster than 'command' in NT -crn@ozemail.com.au
666 system(getenv("comspec"));
669 if(ini
.get_set_title()) SetConsoleTitle (extitle
);
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");
681 printf ("\t%s\n", cmdhist
->cmd
);
683 if (cmdhist
->next
!= NULL
)
684 cmdhist
= cmdhist
->next
;
689 printf ("No command history available.\n");
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
;
703 printf ("Command history cleared.\n");
705 printf ("No command history available.\n");
707 case ALIASES
: // Paul Brannan 1/1/99
712 printm(0, FALSE
, MSG_INVCMD
);