/*
* Copyright (C) 2001 Scott Klement
*
* This file is part of TN5250.
*
* TN5250 is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1, or (at your option)
* any later version.
*
* TN5250 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*
*/
#include "tn5250-private.h"
#include "resource.h"
Tn5250Terminal *tn5250_win32_terminal_new(HINSTANCE hInst,
HINSTANCE hPrev, int show);
Tn5250Session *sess = NULL;
Tn5250Stream *stream = NULL;
Tn5250Terminal *term = NULL;
Tn5250Display *display = NULL;
Tn5250Config *config = NULL;
Tn5250Macro *macro = NULL;
/* FIXME: This should be moved into session.[ch] or something. */
static const struct valid_term {
const char * const name;
const char * const descr;
} valid_terms[] = {
/* DBCS Terminals not yet supported.
* { "IBM-5555-C01", "DBCS color" },
* { "IBM-5555-B01", "DBCS monocrome" }, */
{ "IBM-3477-FC", "27x132 color" },
{ "IBM-3477-FG", "27x132 monochrome" },
{ "IBM-3180-2", "27x132 monochrome" },
{ "IBM-3179-2", "24x80 color" },
{ "IBM-3196-A1", "24x80 monochrome" },
{ "IBM-5292-2", "24x80 color" },
{ "IBM-5291-1", "24x80 monochrome" },
{ "IBM-5251-11", "24x80 monochrome" },
{ NULL, NULL }
};
static void clean_up_and_exit(int error);
static void usage(void);
void msgboxf(const char *fmt, ...);
int parse_cmdline(char *cmdline, char **my_argv);
BOOL CALLBACK ConnectDlgProc (HWND, UINT, WPARAM, LPARAM);
int GetParams(HWND hDlg);
int SetParams(HWND hDlg);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prev, LPSTR cmdline, int show)
{
WORD winsock_ver;
WSADATA wsadata;
char **argv = NULL;
int argc, x;
argc = parse_cmdline(cmdline, NULL);
if (argc>0) {
argv = (char **)malloc(argc * sizeof(char *));
for (x=0; x<argc; x++) argv[x]=NULL;
parse_cmdline(cmdline, argv);
}
winsock_ver = MAKEWORD(1, 1);
if (WSAStartup(winsock_ver, &wsadata)) {
MessageBox(NULL, "Unable to initialize WinSock", "TN5250",
MB_OK | MB_ICONEXCLAMATION);
return 1;
}
if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
MessageBox(NULL, "WinSock version is incompatible with 1.1",
"TN5250", MB_OK | MB_ICONEXCLAMATION);
WSACleanup();
return 1;
}
/*
* Load configuration file & command-line args
*
*/
config = tn5250_config_new ();
if (tn5250_config_load_default (config) == -1) {
tn5250_config_unref(config);
exit (1);
}
if (tn5250_config_parse_argv (config, argc, argv) == -1) {
tn5250_config_unref(config);
usage ();
}
if (tn5250_config_get (config, "help") != NULL)
usage ();
else if (tn5250_config_get (config, "version") != NULL) {
msgboxf("tn5250 %s", VERSION);
exit (0);
}
else if (tn5250_config_get (config, "host") == NULL
|| tn5250_config_get (config, "dialog") != NULL) {
if (DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG_CONNECT), NULL,
ConnectDlgProc)<1) {
exit(0);
}
}
if (tn5250_config_get (config, "font_80") == NULL) {
tn5250_config_set(config, "font_80", "Courier New");
}
#ifndef NDEBUG
if (tn5250_config_get (config, "trace"))
tn5250_log_open (tn5250_config_get (config, "trace"));
#endif
/* create connection to host */
stream = tn5250_stream_open (tn5250_config_get (config, "host"), config);
if (stream == NULL) {
msgboxf("Unable to open communications stream!");
clean_up_and_exit(errno);
}
/* set up display terminal */
display = tn5250_display_new ();
display->sign_key_hack = 0;
if (tn5250_display_config (display, config) == -1)
clean_up_and_exit(errno);
term = tn5250_win32_terminal_new(hInst, prev, show);
if (term == NULL)
clean_up_and_exit(errno);
if (tn5250_terminal_config (term, config) == -1)
clean_up_and_exit(errno);
#ifndef NDEBUG
/* wrap the terminal with the debug terminal if a debug
session is required. */
{
const char *remotehost = tn5250_config_get (config, "host");
if (strlen(remotehost) >= 6
&& !memcmp (remotehost, "debug:", 6)) {
Tn5250Terminal *dbgterm = tn5250_debug_terminal_new (term, stream);
if (dbgterm == NULL) {
tn5250_terminal_destroy (term);
clean_up_and_exit(errno);
}
term = dbgterm;
if (tn5250_terminal_config (term, config) == -1)
clean_up_and_exit(errno);
}
}
#endif
tn5250_terminal_init(term);
tn5250_display_set_terminal(display, term);
/* Create a new TN5250 session! */
sess = tn5250_session_new();
tn5250_display_set_session(display, sess);
term->conn_fd = tn5250_stream_socket_handle(stream);
tn5250_session_set_stream(sess, stream);
if (tn5250_session_config (sess, config) == -1)
clean_up_and_exit(errno);
macro = tn5250_macro_init() ;
tn5250_macro_attach (display, macro) ;
tn5250_session_main_loop(sess);
clean_up_and_exit(0);
return 0;
}
/**
* This is called when the program terminates. It cleans up the
* misc objects & reports an error if one exists.
**/
static void clean_up_and_exit(int error) {
if (macro != NULL)
tn5250_macro_exit(macro);
if (term != NULL)
tn5250_terminal_term(term);
if (sess != NULL)
tn5250_session_destroy(sess);
/* The stream as already been destroyed in tn5250_session_destroy()
* if (stream != NULL)
* tn5250_stream_destroy(stream); */
if (config != NULL)
tn5250_config_unref (config);
if (error != 0) {
char *error_note = "Could not start session:";
char *error_msg = malloc(strlen(error_note)+strlen(strerror(error))+3);
sprintf(error_msg, "%s %s", error_note, strerror(error));
MessageBox (NULL, error_msg, "tn5250", MB_OK);
free (error_msg);
}
#ifndef NDEBUG
tn5250_log_close();
#endif
exit (0);
}
static void usage(void) {
MessageBox(NULL,
"Usage: tn5250 [options] HOST[:PORT]\n"
"Options:\n"
"\t+/-ruler Draw a ruler pointing to the cursor position\n"
"\t+/-version Display the version of the emulator and exit\n"
"\t+/-pcspeaker Play beeps through the PC speaker\n"
"\tmap=NAME Character map (default is '37')\n"
"\tbeepfile=FILE Filename of a .wav file to play for beeps\n"
"\tcopymode=MODE Copy to clipboard mode: 'bitmap', 'text' or 'both'\n"
"\t+/-unix_like_copy Text is copied to the clipboard automatically\n"
"\t when it is highlighted, and text can be pasted\n"
"\t by right-clicking\n"
"\tfont_80=NAME Name of font to use in 80 col mode\n"
"\tfont_132=NAME Name of font to use in 132 col mode\n"
"\tenv.DEVNAME=NAME Use NAME as AS/400 device name(default: none)\n"
"\tenv.TERM=TYPE Emulate IBM terminal type\n"
"\ttrace=FILE Log debugging info to FILE\n"
"\t+/-ssl_verify_server Verify/don't verify the server's SSL certificate\n"
"\tssl_ca_file=FILE Use certificate authority (CA) certs from FILE\n"
"\tssl_cert_file=FILE File containing SSL certificate in PEM format to\n"
"\t use if the AS/400 requires client authentication\n"
"\tssl_pem_pass=PHRASE Passphrase to use when decrypting a PEM private\n"
"\t key. Used in conjunction with ssl_cert_file\n",
"TN5250",
MB_OK|MB_ICONERROR
);
exit (255);
}
#define MAXMSG 1024
void msgboxf(const char *fmt, ...) {
char themsg[MAXMSG+1];
va_list vl;
va_start(vl, fmt);
_vsnprintf(themsg, MAXMSG, fmt, vl);
va_end(vl);
MessageBox(NULL, themsg, "TN5250", MB_OK);
}
/*
* Windows passes all arguments to WinMain() as a single string.
* this breaks them up to look like a typical argc, argv on
* a DOS or *NIX program.
*
* Returns the number of arguments found.
* You can set my_argv to NULL if you just want a count of the arguments.
*
*/
int parse_cmdline(char *cmdline, char **my_argv) {
#define MAXARG 256
char q;
int arglen, argcnt;
char arg[MAXARG+1];
int state = 0, pos = 0;
if (my_argv!=NULL) {
my_argv[0] = malloc(MAXARG+1);
GetModuleFileName(NULL, my_argv[0], MAXARG);
}
argcnt = 1;
arglen = 0;
pos = 0;
while (cmdline[pos]!=0) {
switch (state) {
case 0:
switch (cmdline[pos]) {
case '"':
case '\'':
q = cmdline[pos];
state=1;
break;
case ' ':
if (arglen>0 && argcnt<MAXARG) {
if (my_argv!=NULL) {
my_argv[argcnt] =
(char *)malloc((arglen+1) * sizeof(char));
strcpy(my_argv[argcnt], arg);
}
argcnt++;
arg[0]=0;
arglen=0;
}
break;
default:
if (arglen<MAXARG) {
arg[arglen] = cmdline[pos];
arglen++;
arg[arglen] = '\0';
}
break;
}
break;
case 1:
if (cmdline[pos]==q)
state = 0;
else {
if (arglen<MAXARG) {
arg[arglen] = cmdline[pos];
arglen++;
arg[arglen] = '\0';
}
}
break;
}
pos++;
}
if (arglen>0 && argcnt<MAXARG) {
if (my_argv!=NULL) {
my_argv[argcnt] = (char *)malloc((arglen+1) * sizeof(char));
strcpy(my_argv[argcnt], arg);
}
argcnt++;
}
return argcnt;
#undef MAXARG
}
/*
* This is called by the Win32 DialogBox() API to help control the
* "connect" dialog which appears when you do not specify a
* host name on the command line.
*
*/
#define MAX_HOST_SIZE 255
#define MAX_DEVICE_SIZE 10
BOOL CALLBACK ConnectDlgProc (HWND hDlg, UINT message, WPARAM wParam,
LPARAM lParam) {
HWND hwndMap;
HWND hwndHost;
HWND hwndDevice;
switch (message) {
case WM_INITDIALOG:
GetParams(hDlg);
hwndMap = GetDlgItem (hDlg, IDC_EDIT_CHARMAP);
hwndHost = GetDlgItem (hDlg, IDC_EDIT_HOST);
hwndDevice = GetDlgItem (hDlg, IDC_EDIT_DEVICE);
SendMessage (hwndMap, EM_LIMITTEXT, (WPARAM)9, 0);
SendMessage (hwndDevice, EM_LIMITTEXT, (WPARAM)MAX_DEVICE_SIZE, 0);
SendMessage (hwndHost, EM_LIMITTEXT, (WPARAM)MAX_HOST_SIZE, 0);
return TRUE;
case WM_COMMAND:
switch (LOWORD (wParam)) {
case IDOK:
if (SetParams(hDlg)) {
EndDialog (hDlg, 1);
}
return TRUE;
case IDCANCEL:
EndDialog (hDlg, 0);
return TRUE;
case IDC_RADIO_80:
case IDC_RADIO_132:
CheckRadioButton (hDlg, IDC_RADIO_80, IDC_RADIO_132,
LOWORD (wParam));
return TRUE;
}
break;
}
return FALSE;
}
/*
* This is used by the "connect to" dialog box to get the default
* values that it displays in the dialog
*
*/
int GetParams(HWND hDlg) {
int ccsid;
char *work;
int x;
/* char map (CCSID) */
ccsid = 37;
if (tn5250_config_get(config, "map"))
ccsid = atoi(tn5250_config_get(config, "map"));
SetDlgItemInt (hDlg, IDC_EDIT_CHARMAP, ccsid, FALSE);
/* If any of the SSL keywords are given, then default SSL to being on */
if ( tn5250_config_get(config, "ssl_verify_server") != NULL
|| tn5250_config_get(config, "ssl_ca_file" ) != NULL
|| tn5250_config_get(config, "ssl_cert_file" ) != NULL
|| tn5250_config_get(config, "ssl_pem_pass" ) != NULL) {
CheckDlgButton(hDlg, IDC_CHECK_SSL, TRUE);
}
if ( tn5250_config_get(config, "host") != NULL) {
if (!strncmp(tn5250_config_get(config, "host"), "ssl:", 4)) {
CheckDlgButton(hDlg, IDC_CHECK_SSL, TRUE);
}
}
/* Set checkbox for verifying the SSL server if needed */
if (tn5250_config_get(config, "ssl_verify_server")) {
CheckDlgButton(hDlg, IDC_CHECK_SSLVERIFY,
tn5250_config_get_bool(config, "ssl_verify_server"));
}
/* Set unix-like-copy mode */
if (tn5250_config_get(config, "unix_like_copy")) {
CheckDlgButton(hDlg, IDC_CHECK_UNIXCOPY,
tn5250_config_get_bool(config, "unix_like_copy"));
}
/* Set terminal size */
CheckRadioButton (hDlg, IDC_RADIO_80, IDC_RADIO_132, IDC_RADIO_80);
if (tn5250_config_get(config, "env.TERM")) {
work = malloc(strlen(tn5250_config_get(config, "env.TERM"))+1);
TN5250_ASSERT(work != NULL);
strcpy(work, tn5250_config_get(config, "env.TERM"));
x = 0;
while (valid_terms[x].name != NULL) {
if (!strcmp(work, valid_terms[x].name))
if (!strncmp(valid_terms[x].descr, "27x132", 6))
CheckRadioButton (hDlg, IDC_RADIO_80, IDC_RADIO_132,
IDC_RADIO_132);
x++;
}
free(work);
}
/* set host name */
if (tn5250_config_get(config, "host")) {
if (!strncmp(tn5250_config_get(config, "host"), "ssl:", 4))
SetDlgItemText(hDlg,IDC_EDIT_HOST,tn5250_config_get(config,"host")+4);
else
SetDlgItemText(hDlg,IDC_EDIT_HOST,tn5250_config_get(config,"host"));
}
/* set device name */
if (tn5250_config_get(config, "env.DEVNAME"))
SetDlgItemText(hDlg, IDC_EDIT_DEVICE,
tn5250_config_get(config, "env.DEVNAME"));
return 0;
}
/*
* This is used by the "connect to" dialog box to set the configuration
* options for the session.
*
*/
int SetParams(HWND hDlg) {
char host[MAX_HOST_SIZE+6];
char device[MAX_DEVICE_SIZE+1];
char termtype[18];
char charmap[10];
char work[MAX_HOST_SIZE+1];
char sslpref[5];
int map;
host[0] = '\0';
if (IsDlgButtonChecked(hDlg, IDC_CHECK_SSL)==BST_CHECKED) {
strcpy(host, "ssl:");
}
if (IsDlgButtonChecked(hDlg, IDC_CHECK_SSLVERIFY)==BST_CHECKED) {
tn5250_config_set(config, "ssl_verify_server", "1");
}
if (IsDlgButtonChecked(hDlg, IDC_CHECK_UNIXCOPY)==BST_CHECKED) {
tn5250_config_set(config, "unix_like_copy", "1");
}
strcpy(termtype, "IBM-3179-2");
if (IsDlgButtonChecked(hDlg, IDC_RADIO_132)==BST_CHECKED) {
strcpy(termtype, "IBM-3477-FC");
}
tn5250_config_set(config, "env.TERM", termtype);
GetDlgItemText(hDlg, IDC_EDIT_HOST, work, MAX_HOST_SIZE);
strcat(host, work);
tn5250_config_set(config, "host", host);
GetDlgItemText(hDlg, IDC_EDIT_DEVICE, device, MAX_DEVICE_SIZE+1);
tn5250_config_set(config, "env.DEVNAME", device);
map = GetDlgItemInt(hDlg, IDC_EDIT_CHARMAP, NULL, FALSE);
sprintf(charmap, "%d", map);
tn5250_config_set(config, "map", charmap);
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1