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 ///////////////////////////////////////////////////////////////////////////////
47 int telCommandLine (Telnet
&MyConnection
);
50 HANDLE hConsole
= GetStdHandle(STD_INPUT_HANDLE
);
51 INPUT_RECORD InputRecord
;
55 WaitForSingleObject( hConsole
, INFINITE
);
56 if (!ReadConsoleInput(hConsole
, &InputRecord
, 1, &dwInput
)){
60 if (InputRecord
.EventType
== KEY_EVENT
&&
61 InputRecord
.Event
.KeyEvent
.bKeyDown
)
66 //char * cfgets ( char * buf, unsigned int length, char pszHistory[][80], int iHistLength){
67 struct cmdHistory
* cfgets (char *buf
, unsigned int length
, struct cmdHistory
*cmdhist
) {
69 HANDLE hConsole
= GetStdHandle(STD_INPUT_HANDLE
);
70 unsigned int current
=0, cursor
=0, iEraseLength
=0, i
;
75 INPUT_RECORD InputRecord
;
81 if(!ini
.get_input_redir()) {
85 WaitForSingleObject( hConsole
, INFINITE
);
86 if (!ReadConsoleInput(hConsole
, &InputRecord
, 1, &dwInput
)){
91 if (InputRecord
.EventType
== KEY_EVENT
&&
92 InputRecord
.Event
.KeyEvent
.bKeyDown
) {
94 if(InputRecord
.Event
.KeyEvent
.dwControlKeyState
&
95 (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) {
97 switch(InputRecord
.Event
.KeyEvent
.wVirtualKeyCode
) {
98 case 'D': // Thomas Briggs 8/11/98
104 case 'U': // Paul Brannan 8/11/98
113 switch (InputRecord
.Event
.KeyEvent
.wVirtualKeyCode
) {
115 // crn@ozemail.com.au
116 if (cmdhist
!= NULL
) {
117 if (!strcmp(buf
, ""))
118 strncpy(buf
, cmdhist
->cmd
, 79);
119 else if (cmdhist
->prev
!= NULL
) {
120 cmdhist
= cmdhist
->prev
;
121 strncpy(buf
, cmdhist
->cmd
, 79);
123 current
= strlen(buf
);
129 // crn@ozemail.com.au
130 if (cmdhist
!= NULL
) {
131 if (cmdhist
->next
!= NULL
) {
132 cmdhist
= cmdhist
->next
;
133 strncpy(buf
, cmdhist
->cmd
, 79);
135 strncpy(buf
, "", 79);
137 current
= strlen(buf
);
142 case VK_RIGHT
: //crn@ozemail.com.au (added ctrl+arrow)
143 if (cursor
< current
) {
144 if (InputRecord
.Event
.KeyEvent
.dwControlKeyState
&
145 (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) {
147 for (j
= cursor
; j
<= current
; j
++)
148 if (buf
[j
+1] == ' ' || (j
+1)==current
)
150 for (k
= ++j
; k
<= current
; k
++)
151 if (buf
[k
] != ' ' || k
== current
) {
152 cursor
= k
== current
? --k
: k
;
160 case VK_LEFT
: //crn@ozemail.com.au (added ctrl+arrow)
162 if(InputRecord
.Event
.KeyEvent
.dwControlKeyState
&
163 (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) {
165 for (j
= cursor
; j
>= 0; j
--)
168 for (k
= --j
; k
>= 0; k
--)
169 if (buf
[k
] == ' ' || k
== 0) {
170 cursor
= !k
? k
: ++k
;
179 if (cursor
>0) cursor
= 0;
183 if (cursor
<current
) cursor
= current
;
187 if (current
> 0 && current
> cursor
) {
188 strcpy(&buf
[cursor
],&buf
[cursor
+1]);
192 for (i
= 0; i
< current
+strlen("telnet>")+1 ;i
++)
199 strcpy(&buf
[cursor
-1],&buf
[cursor
]);
204 for (i
= 0; i
< current
+strlen("telnet>")+1 ;i
++)
211 chr
= InputRecord
.Event
.KeyEvent
.uChar
.AsciiChar
;
216 if (current
>= length
-1){
220 if ( isprint (chr
) ){
221 strncpy(temp1
,&buf
[cursor
],79);
222 strncpy(&buf
[cursor
+1],temp1
,79-(cursor
+1));
230 if (MustRefresh
== 1)
233 for (i
= 0; i
<= iEraseLength
;i
++)
235 printit("\rtelnet>");
237 iEraseLength
= strlen(buf
);
238 for (i
= 0; i
< current
-cursor
; i
++)
244 if (strcmp(buf
, "")) {
245 if (cmdhist
== NULL
) {
246 cmdhist
= new struct cmdHistory
;
247 if (cmdhist
== NULL
) {
248 printit ("\nUnable to allocate memory for history buffer -- use the \"flush\" command to clear the buffer.\n");
251 strncpy(cmdhist
->cmd
, buf
, 79);
252 cmdhist
->next
= NULL
;
253 cmdhist
->prev
= NULL
;
255 while (cmdhist
->next
!= NULL
) // move to the end of the list
256 cmdhist
= cmdhist
->next
;
257 cmdhist
->next
= new struct cmdHistory
;
258 if (cmdhist
->next
== NULL
) {
259 printit ("\nUnable to allocate memory for history buffer -- use the \"flush\" command to clear the buffer.\n");
262 cmdhist
->next
->prev
= cmdhist
; // previous is where we are now
263 cmdhist
= cmdhist
->next
;
264 strncpy(cmdhist
->cmd
, buf
, 79);
265 cmdhist
->next
= NULL
;
267 while (cmdhist
->next
)
268 cmdhist
= cmdhist
->next
;
273 WaitForSingleObject( hConsole
, INFINITE
);
276 GetConsoleMode(hConsole
, &OldMode
);
277 SetConsoleMode(hConsole
,
278 OldMode
&~ (ENABLE_ECHO_INPUT
| ENABLE_LINE_INPUT
) );
279 while (ReadFile(hConsole
, &chr
, 1, &dwInput
, NULL
)) {
285 if (chr
== '\b' && current
> 0) {
289 if (current
>= length
-1){
292 if ( isprint (chr
) ){
299 SetConsoleMode(hConsole
, OldMode
);
304 // AVS ** for fix bug in command 'keys load keymapname' without file
305 // static char keyfile[MAX_PATH*2];
307 int main(int ArgC
, char* ArgV
[]) {
309 CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo
;
310 GetConsoleScreenBufferInfo(
311 GetStdHandle(STD_OUTPUT_HANDLE
),
312 &ConsoleScreenBufferInfo
316 char startdir
[MAX_PATH
*2];
317 char exename
[MAX_PATH
];
319 // strncpy(startdir, ArgV[0],MAX_PATH);
320 // This should be more accurate than using argv[0] (Paul Brannan 9/16/98)
321 GetModuleFileName(NULL
, startdir
, sizeof(startdir
));
323 // Get the current console title so it can be set later
324 // ("Pedro A. Aranda Gutiérrez" <paag@coppi.tid.es>)
325 TCHAR ConsoleTitle
[255];
326 GetConsoleTitle(ConsoleTitle
, sizeof(ConsoleTitle
));
328 k
= strrchr(startdir
, '\\');
329 if (k
== NULL
){ // if the \ character is not found...
330 strcpy(exename
, startdir
);
331 strcpy(startdir
,""); // set the path to nothing
333 // end the string after the last '\' to get rid of the file name
334 strcpy(exename
, k
+1);
338 printm(0, FALSE
, MSG_COPYRIGHT
);
339 printm(0, FALSE
, MSG_COPYRIGHT_1
);
341 // set up the ini class
342 ini
.init(startdir
, exename
);
344 // Process the command line arguments and connect to a host if necessary
345 if(ini
.Process_Params(ArgC
, ArgV
)) {
346 const char *szHost
= ini
.get_host();
347 const char *strPort
= ini
.get_port();
350 while(telCommandLine(MyConnection
));
353 if(MyConnection
.Open(szHost
, strPort
) == TNPROMPT
) {
356 telCommandLine(MyConnection
);
360 //// (Paul Brannan 5/14/98)
362 if(ini
.get_term_width() != -1 || ini
.get_term_height() != -1) {
363 SetConsoleScreenBufferSize(
364 GetStdHandle(STD_OUTPUT_HANDLE
), // handle of console screen buffer
365 ConsoleScreenBufferInfo
.dwSize
// new size in character rows and cols.
367 SetConsoleWindowInfo(
368 GetStdHandle(STD_OUTPUT_HANDLE
), // handle of console screen buffer
369 TRUE
, // coordinate type flag
370 &ConsoleScreenBufferInfo
.srWindow
// address of new window rectangle
373 SetConsoleTextAttribute(
374 GetStdHandle(STD_OUTPUT_HANDLE
), // handle of console screen buffer
375 ConsoleScreenBufferInfo
.wAttributes
// text and background colors
378 // Restore the original console title
379 // ("Pedro A. Aranda Gutiérrez" <paag@coppi.tid.es>)
380 SetConsoleTitle(ConsoleTitle
);
392 OPEN
= __FIRST_COMMAND
,
397 HELP2
, // there is way for synonims
398 K_LOAD
, // subcommand of 'keys'
399 K_SWITCH
, // subcommand of 'keys'
400 K_DISPLAY
, // subcommand of 'keys'
402 SET
, // Paul Brannan 5/30/98
405 FASTQUIT
, // Thomas Briggs 8/11/98
406 CMD_HISTORY
, // crn@ozemail.com.au
407 CLEAR_HISTORY
, // crn@ozemail.com.au
409 ALIASES
, // Paul Brannan 1/1/99
411 __COMMAND_LIST_SIZE
// must be last
416 const char* cmd
; // command
417 int minLen
, // minimal length for match
418 minParms
, // minimal count of parms
419 maxParms
; // maximal -/- (negative disables)
420 int isSubCmd
, // is a subcommand - number of wich command
421 haveSubCmd
; // have subcommands? 0 or 1
422 const char* usage
; // text of usage
425 command cmdList
[__COMMAND_LIST_SIZE
] = {
426 {"open", 1, 1, 2, -1, 0, "o[pen] host [port]\n"},
427 {"close", 2, 0, 0, -1, 0, NULL
},
428 {"keys", 2, 1, 3, -1, 1, "ke[ys] l[oad] keymapname [file]\n"
430 "ke[ys] s[witch] number\n"},
431 // Ioannou : i change it to q, to be more compatible with unix telnet
432 {"quit", 1, 0, 0, -1, 0, NULL
}, // must type it exactly
433 {"?", 1, 0, 0, -1, 0, NULL
},
434 {"help", 1, 0, 0, -1, 0, NULL
},
435 {"load", 1, 1, 2, KEYS
, 0, NULL
},
436 {"switch", 1, 1, 1, KEYS
, 0, NULL
},
437 {"display", 1, 0, 0, KEYS
, 0, NULL
},
438 // Paul Brannan 5/30/98
439 {"set", 3, 0, 2, -1, 0, "set will display available groups.\n"
440 "set groupname will display all variables/values in a group.\n"
441 "set [variable [value]] will set variable to value.\n"},
442 // Thomas Briggs 8/11/98
443 {"z", 1, 0, 0, -1, 0, "suspend telnet\n"},
444 {"\04", 1, 0, 0, -1, 0, NULL
},
445 // crn@ozemail.com.au
446 {"history", 2, 0, 0, -1, 0, "show command history"},
447 {"flush", 2, 0, 0, -1, 0, "flush history buffer"},
448 // Paul Brannan 1/1/99
449 {"aliases", 5, 0, 0, -1, 0, NULL
}
452 // a maximal count of parms
453 #define MAX_PARM_COUNT 3
454 #define MAX_TOKEN_COUNT (MAX_PARM_COUNT+2)
456 static int cmdMatch(const char* cmd
, const char* token
, int tokenLen
, int minM
) {
457 if ( tokenLen
< minM
) return 0;
458 // The (unsigned) gets rid of a compiler warning (Paul Brannan 5/25/98)
459 if ( (unsigned)tokenLen
> strlen(cmd
) ) return 0;
460 if ( strcmp(cmd
,token
) == 0 ) return 1;
463 for ( i
= 0; i
< minM
; i
++ ) if ( cmd
[i
] != token
[i
] ) return 0;
465 for ( i
= minM
; i
< tokenLen
; i
++ ) if ( cmd
[i
] != token
[i
] ) return 0;
470 static void printUsage(int cmd
) {
471 if ( cmdList
[cmd
].usage
!= NULL
) {
472 printit(cmdList
[cmd
].usage
);
475 if ( cmdList
[cmd
].isSubCmd
>= 0 ) {
476 printUsage(cmdList
[cmd
].isSubCmd
);
479 printm(0, FALSE
, MSG_BADUSAGE
);
482 int tokenizeCommand(char* szCommand
, int& argc
, char** argv
) {
483 char* tokens
[MAX_TOKEN_COUNT
];
487 if(!szCommand
|| !*szCommand
) return EMPTY_LINE
;
489 // Removed strtok to handle tokens with spaces; this is handled with
490 // quotes. (Paul Brannan 3/18/99)
491 char *token_start
= szCommand
;
492 for(p
= szCommand
;; p
++) {
495 for(p
++; *p
!= '\"' && *p
!= 0; p
++); // Find the next quote
496 if(*p
!= 0) strcpy(p
, p
+ 1); // Remove quote#2
497 strcpy(tmp
, tmp
+ 1); // Remove quote#1
499 if(*p
== 0 || *p
== ' ' || *p
== '\t') {
500 tokens
[args
] = token_start
;
502 if(args
>= MAX_TOKEN_COUNT
) break; // Break if too many args
508 // while ( (p = strtok((args?NULL:szCommand), " \t")) != NULL && args < MAX_TOKEN_COUNT ) {
513 if ( !args
) return EMPTY_LINE
;
519 int tokenLen
= strlen(tokens
[args
]);
521 for ( int i
= 0; i
<__COMMAND_LIST_SIZE
; i
++ ) {
522 if ( cmdMatch(cmdList
[i
].cmd
, tokens
[args
], tokenLen
, cmdList
[i
].minLen
) ) {
523 if (argc
< cmdList
[i
].minParms
|| argc
> cmdList
[i
].maxParms
) {
527 if ( cmdList
[i
].haveSubCmd
&& curCmd
== cmdList
[i
].isSubCmd
) {
534 if ( curCmd
== cmdList
[i
].isSubCmd
) {
544 if ( curCmd
< 0 ) return INVALID_CMD
;
550 for ( int i
= 0; i
<argc
; i
++ ) {
551 argv
[i
] = tokens
[i
+args
+1];
557 int telCommandLine (Telnet
&MyConnection
){
558 #define HISTLENGTH 25
560 char* Parms
[MAX_PARM_COUNT
];
563 char *extitle
, *newtitle
;
564 struct cmdHistory
*cmdhist
;
567 // printit("\n"); // crn@ozemail.com.au 14/12/98
569 // printit("\n"); // Paul Brannan 5/25/98
571 cmdhist
= cfgets (szCommand
, 79, cmdhist
);
574 strlwr(szCommand
); // convert command line to lower
575 // i = sscanf(szCommand,"%80s %80s %80s %80s", szCmd, szArg1, szArg2, szArg3);
576 switch ( tokenizeCommand(szCommand
, i
, Parms
) ) {
577 case BAD_USAGE
: break;
579 if(MyConnection
.Resume() == TNPROMPT
) {
586 printm(0, FALSE
, MSG_INVCMD
);
590 retval
= MyConnection
.Open(Parms
[0], "23");
592 retval
= MyConnection
.Open(Parms
[0], Parms
[1]);
593 if(retval
!= TNNOCON
&& retval
!= TNPROMPT
) return 1;
594 if(retval
== TNPROMPT
) printit("\n");
597 MyConnection
.Close();
599 case FASTQUIT
: // Thomas Briggs 8/11/98
601 MyConnection
.Close();
606 printm(0, FALSE
, MSG_HELP
);
607 printm(0, FALSE
, MSG_HELP_1
);
609 // case KEYS: we should never get it
612 // Ioannou : changed to ini.get_keyfile()
613 if(MyConnection
.LoadKeyMap( ini
.get_keyfile(), Parms
[0]) != 1)
614 printit("Error loading keymap.\n");
617 if(MyConnection
.LoadKeyMap( Parms
[1], Parms
[0]) != 1)
618 printit("Error loading keymap.\n");
621 MyConnection
.DisplayKeyMap();
624 MyConnection
.SwitchKeyMap(atoi(Parms
[0]));
627 // Paul Brannan 5/30/98
630 printit("Available groups:\n"); // Print out groups
631 ini
.print_groups(); // (Paul Brannan 9/3/98)
633 ini
.print_vars(Parms
[0]);
635 ini
.set_value(Parms
[0], Parms
[1]);
636 // FIX ME !!! Ioannou: here we must call the parser routine for
637 // wrap line, not the ini.set_value
638 // something like Parser.ConLineWrap(Wrap_Line);
642 case SUSPEND
: // Thomas Briggs 8/11/98
644 // remind the user we're suspended -crn@ozemail.com.au 15/12/98
645 extitle
= new char[128];
646 GetConsoleTitle (extitle
, 128);
648 newtitle
= new char[128+sizeof("[suspended]")];
649 strcpy(newtitle
, extitle
);
650 strncat(newtitle
, "[suspended]", 128+sizeof("[suspended]"));
651 if(ini
.get_set_title()) SetConsoleTitle (newtitle
);
654 if (getenv("comspec") == NULL
) {
655 switch (GetWin32Version()) {
656 case 2: // 'cmd' is faster than 'command' in NT -crn@ozemail.com.au
664 system(getenv("comspec"));
667 if(ini
.get_set_title()) SetConsoleTitle (extitle
);
673 case CMD_HISTORY
: //crn@ozemail.com.au
674 if (cmdhist
!= NULL
) {
675 while (cmdhist
->prev
!= NULL
)
676 cmdhist
= cmdhist
->prev
; //rewind
677 printf ("Command history:\n");
679 printf ("\t%s\n", cmdhist
->cmd
);
681 if (cmdhist
->next
!= NULL
)
682 cmdhist
= cmdhist
->next
;
687 printf ("No command history available.\n");
691 case CLEAR_HISTORY
: //crn@ozemail.com.au
692 if (cmdhist
!= NULL
) {
693 while (cmdhist
->next
!= NULL
)
694 cmdhist
= cmdhist
->next
; //fast forward
695 while (cmdhist
->prev
!= NULL
) {
696 cmdhist
= cmdhist
->prev
;
697 delete cmdhist
->next
;
701 printf ("Command history cleared.\n");
703 printf ("No command history available.\n");
705 case ALIASES
: // Paul Brannan 1/1/99
710 printm(0, FALSE
, MSG_INVCMD
);