/* tn5250 -- an implentation of the 5250 telnet protocol.
* Copyright (C) 2000 Jason M. Felice
*
* 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 of the License, 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 program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "tn5250-private.h"
#define MAX_PREFIX_STACK 50
static void tn5250_config_str_destroy (Tn5250ConfigStr * This);
static Tn5250ConfigStr *tn5250_config_str_new (const char *name,
const char *value);
static Tn5250ConfigStr *tn5250_config_get_str (Tn5250Config * This,
const char *name);
static void tn5250_config_replace_vars (char *buf, int maxlen);
static void tn5250_config_replacedata (const char *from, const char *to,
char *line, int maxlen);
#ifdef WIN32
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#endif
/*** Tn5250ConfigStr ***/
static void
tn5250_config_str_destroy (Tn5250ConfigStr * This)
{
if (This->name != NULL)
free (This->name);
if (This->value != NULL)
free (This->value);
free (This);
return;
}
static Tn5250ConfigStr *
tn5250_config_str_new (const char *name, const char *value)
{
Tn5250ConfigStr *This = tn5250_new (Tn5250ConfigStr, 1);
if (This == NULL)
return NULL;
This->name = (char *) malloc (strlen (name) + 1);
if (This->name == NULL)
{
free (This);
return NULL;
}
strcpy (This->name, name);
This->value = (char *) malloc (strlen (value) + 1);
if (This->value == NULL)
{
free (This->name);
free (This);
return NULL;
}
strcpy (This->value, value);
return This;
}
/*** Tn5250Config ***/
Tn5250Config *
tn5250_config_new ()
{
Tn5250Config *This = tn5250_new (Tn5250Config, 1);
if (This == NULL)
return NULL;
This->ref = 1;
This->vars = NULL;
return This;
}
Tn5250Config *
tn5250_config_ref (Tn5250Config * This)
{
This->ref++;
return This;
}
void
tn5250_config_unref (Tn5250Config * This)
{
if (--This->ref == 0)
{
Tn5250ConfigStr *iter, *next;
/* Destroy all vars. */
if ((iter = This->vars) != NULL)
{
do
{
next = iter->next;
tn5250_config_str_destroy (iter);
iter = next;
}
while (iter != This->vars);
}
free (This);
}
return;
}
int
tn5250_config_load (Tn5250Config * This, const char *filename)
{
FILE *f;
char buf[128];
char *scan;
int len;
int prefix_stack_size = 0;
char *prefix_stack[MAX_PREFIX_STACK];
char *list[100];
int done;
int curitem;
int loop;
int name_len, i;
char *name;
/* It is not an error for a config file not to exist. */
if ((f = fopen (filename, "r")) == NULL)
return errno == ENOENT ? 0 : -1;
while (fgets (buf, sizeof (buf) - 1, f))
{
buf[sizeof (buf) - 1] = '\0';
if (strchr (buf, '\n'))
*strchr (buf, '\n') = '\0';
tn5250_config_replace_vars (buf, sizeof (buf));
scan = buf;
while (*scan && isspace (*scan))
scan++;
if (*scan == '#' || *scan == '\0')
continue;
if (*scan == '+')
{
scan++;
while (*scan && isspace (*scan))
scan++;
len = strlen (scan) - 1;
while (len > 0 && isspace (scan[len]))
scan[len--] = '\0';
for (i = 0, name_len = len + 3; i < prefix_stack_size; i++)
{
name_len += strlen (prefix_stack[i]) + 1;
}
if ((name = (char *) malloc (name_len)) == NULL)
goto config_error;
name[0] = '\0';
for (i = 0; i < prefix_stack_size; i++)
{
strcat (name, prefix_stack[i]);
strcat (name, ".");
}
strcat (name, scan);
tn5250_config_set (This, name, "1");
free (name);
}
else if (*scan == '-')
{
scan++;
while (*scan && isspace (*scan))
scan++;
len = strlen (scan) - 1;
while (len > 0 && isspace (scan[len]))
scan[len--] = '\0';
for (i = 0, name_len = len + 3; i < prefix_stack_size; i++)
{
name_len += strlen (prefix_stack[i]) + 1;
}
if ((name = (char *) malloc (name_len)) == NULL)
goto config_error;
name[0] = '\0';
for (i = 0; i < prefix_stack_size; i++)
{
strcat (name, prefix_stack[i]);
strcat (name, ".");
}
strcat (name, scan);
tn5250_config_set (This, name, "0");
free (name);
}
else if (strchr (scan, '='))
{
/* Set item. */
len = 0;
while (scan[len] && !isspace (scan[len]) && scan[len] != '=')
len++;
if (len == 0)
goto config_error; /* Missing variable name. */
for (i = 0, name_len = len + 3; i < prefix_stack_size; i++)
{
name_len += strlen (prefix_stack[i]) + 1;
}
if ((name = (char *) malloc (name_len)) == NULL)
goto config_error;
name[0] = '\0';
for (i = 0; i < prefix_stack_size; i++)
{
strcat (name, prefix_stack[i]);
strcat (name, ".");
}
name_len = strlen (name) + len;
memcpy (name + strlen (name), scan, len);
name[name_len] = '\0';
scan += len;
while (*scan && isspace (*scan))
scan++;
if (*scan != '=')
{
free (name);
goto config_error;
}
scan++;
while (*scan && isspace (*scan))
scan++;
if (*scan == '[')
{
done = 0;
curitem = 0;
while (!done)
{
fgets (buf, sizeof (buf) - 1, f);
buf[sizeof (buf) - 1] = '\0';
if (strchr (buf, '\n'))
*strchr (buf, '\n') = '\0';
scan = buf;
while (*scan && isspace (*scan))
scan++;
if (*scan == ']')
{
done = 1;
}
else
{
list[curitem] = malloc (strlen (scan) + 1);
strcpy (list[curitem], scan);
curitem++;
}
}
for (loop = 0; loop < curitem; loop++)
{
printf ("%s\n", list[loop]);
}
}
else
{
len = strlen (scan) - 1;
while (len > 0 && isspace (scan[len]))
scan[len--] = '\0';
tn5250_config_set (This, name, scan);
free (name);
}
}
else if (strchr (scan, '{'))
{
/* Push level. */
len = 0;
while (scan[len] && !isspace (scan[len]) && scan[len] != '{')
len++;
if (len == 0)
goto config_error; /* Missing section name. */
TN5250_ASSERT (prefix_stack_size < MAX_PREFIX_STACK);
prefix_stack[prefix_stack_size] = (char *) malloc (len + 1);
TN5250_ASSERT (prefix_stack[prefix_stack_size] != NULL);
memcpy (prefix_stack[prefix_stack_size], scan, len);
prefix_stack[prefix_stack_size][len] = '\0';
prefix_stack_size++;
}
else if (*scan == '}')
{
/* Pop level. */
scan++;
while (*scan && isspace (*scan))
scan++;
if (*scan != '#' && *scan != '\0')
goto config_error; /* Garbage after '}' */
TN5250_ASSERT (prefix_stack_size > 0);
prefix_stack_size--;
if (prefix_stack[prefix_stack_size] != NULL)
free (prefix_stack[prefix_stack_size]);
}
else
goto config_error; /* Garbage line. */
}
if (prefix_stack_size != 0)
goto config_error;
fclose (f);
return 0;
config_error:
while (prefix_stack_size > 0)
{
prefix_stack_size--;
if (prefix_stack[prefix_stack_size] != NULL)
free (prefix_stack[prefix_stack_size]);
}
fclose (f);
return -1;
}
#ifdef __WIN32__
/*
* Load default configuration files.
* Win32 version -- Looks for a file in the same dir as the .exe
* file called "tn5250rc".
*/
int
tn5250_config_load_default (Tn5250Config * This)
{
#define PATHSIZE 1024
LPTSTR apppath;
LPTSTR dir;
DWORD rc;
DWORD len;
LPTSTR lpMsgBuf;
apppath = malloc (PATHSIZE + 1);
TN5250_ASSERT (apppath != NULL);
if (GetModuleFileName (NULL, apppath, PATHSIZE) < 1)
{
FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (),
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), lpMsgBuf, 0,
NULL);
TN5250_LOG (("GetModuleFileName Error: %s\n", lpMsgBuf));
MessageBox (NULL, lpMsgBuf, "TN5250", MB_OK);
LocalFree (lpMsgBuf);
return -1;
}
if (strrchr (apppath, '\\'))
{
len = strrchr (apppath, '\\') - apppath;
apppath[len + 1] = '\0';
}
dir = malloc (strlen (apppath) + 15);
TN5250_ASSERT (apppath != NULL);
strcpy (dir, apppath);
strcat (dir, "tn5250rc");
free (apppath);
TN5250_LOG (("Config File = %s\n", dir));
rc = tn5250_config_load (This, dir);
free (dir);
return rc;
}
#else
/*
* Load default configuration files.
*/
int
tn5250_config_load_default (Tn5250Config * This)
{
struct passwd *pwent;
char *dir;
int ec;
if (tn5250_config_load (This, SYSCONFDIR "/tn5250rc") == -1)
{
perror (SYSCONFDIR "/tn5250rc");
return -1;
}
pwent = getpwuid (getuid ());
if (pwent == NULL)
{
perror ("getpwuid");
return -1;
}
dir = (char *) malloc (strlen (pwent->pw_dir) + 12);
if (dir == NULL)
{
perror ("malloc");
return -1;
}
strcpy (dir, pwent->pw_dir);
strcat (dir, "/.tn5250rc");
if ((ec = tn5250_config_load (This, dir)) == -1)
perror (dir);
free (dir);
return ec;
}
#endif
int
tn5250_config_parse_argv (Tn5250Config * This, int argc, char **argv)
{
int argn = 1;
/* FIXME: Scan and promote entries first, then parse individual
* settings. This is so that we can use a particular session but
* override one of it's settings. */
while (argn < argc)
{
if (argv[argn][0] == '+')
{
/* Set boolean option. */
char *opt = argv[argn] + 1;
tn5250_config_set (This, opt, "1");
}
else if (argv[argn][0] == '-')
{
/* Clear boolean option. */
char *opt = argv[argn] + 1;
tn5250_config_set (This, opt, "0");
}
else if (strchr (argv[argn], '='))
{
/* Set string option. */
char *opt;
char *val = strchr (argv[argn], '=') + 1;
opt = (char *) malloc (strchr (argv[argn], '=') - argv[argn] + 3);
if (opt == NULL)
return -1; /* FIXME: Produce error message. */
memcpy (opt, argv[argn], strchr (argv[argn], '=') - argv[argn] + 1);
*strchr (opt, '=') = '\0';
tn5250_config_set (This, opt, val);
}
else
{
/* Should be profile name/connect URL. */
tn5250_config_set (This, "host", argv[argn]);
tn5250_config_promote (This, argv[argn]);
}
argn++;
}
return 0;
}
const char *
tn5250_config_get (Tn5250Config * This, const char *name)
{
Tn5250ConfigStr *str = tn5250_config_get_str (This, name);
return (str == NULL ? NULL : str->value);
}
int
tn5250_config_get_bool (Tn5250Config * This, const char *name)
{
const char *v = tn5250_config_get (This, name);
return (v == NULL ? 0 :
!(!strcmp (v, "off")
|| !strcmp (v, "no")
|| !strcmp (v, "0") || !strcmp (v, "false")));
}
int
tn5250_config_get_int (Tn5250Config * This, const char *name)
{
const char *v = tn5250_config_get (This, name);
if (v == NULL)
{
return 0;
}
else
{
return (atoi (v));
}
}
void
tn5250_config_set (Tn5250Config * This, const char *name, const char *value)
{
Tn5250ConfigStr *str = tn5250_config_get_str (This, name);
if (str != NULL)
{
if (str->value != NULL)
free (str->value);
str->value = (char *) malloc (strlen (value) + 1);
TN5250_ASSERT (str->value != NULL);
strcpy (str->value, value);
return;
}
str = tn5250_config_str_new (name, value);
if (This->vars == NULL)
This->vars = str->next = str->prev = str;
else
{
str->next = This->vars;
str->prev = This->vars->prev;
str->next->prev = str;
str->prev->next = str;
}
}
void
tn5250_config_unset (Tn5250Config * This, const char *name)
{
Tn5250ConfigStr *str;
if ((str = tn5250_config_get_str (This, name)) == NULL)
return; /* Not found */
if (This->vars == str)
This->vars = This->vars->next;
if (This->vars == str)
This->vars = NULL;
else
{
str->next->prev = str->prev;
str->prev->next = str->next;
}
tn5250_config_str_destroy (str);
}
/*
* Copy variables prefixed with `prefix' to variables without `prefix'.
*/
void
tn5250_config_promote (Tn5250Config * This, const char *prefix)
{
Tn5250ConfigStr *iter;
if ((iter = This->vars) == NULL)
return;
do
{
if (strlen (prefix) <= strlen (iter->name) + 2
&& !memcmp (iter->name, prefix, strlen (prefix))
&& iter->name[strlen (prefix)] == '.')
{
tn5250_config_set (This, iter->name + strlen (prefix) + 1,
iter->value);
}
iter = iter->next;
}
while (iter != This->vars);
}
static Tn5250ConfigStr *
tn5250_config_get_str (Tn5250Config * This, const char *name)
{
Tn5250ConfigStr *iter;
if ((iter = This->vars) == NULL)
return NULL; /* No vars */
do
{
if (!strcmp (iter->name, name))
return iter;
iter = iter->next;
}
while (iter != This->vars);
return NULL; /* Not found */
}
/****f* lib5250/tn5250_config_replace_vars
* NAME
* tn5250_config_replace_vars
* SYNOPSIS
* tn5250_config_replace_vars (buf, 128);
* INPUTS
* char * buf -
* int maxlen -
* DESCRIPTION
* This searches for special "replacement variables" in the
* config file entries and converts them to their "real values".
* (At this time, the only var is "$loginname$".)
*****/
#define USER_NAME_MAX 50
static void
tn5250_config_replace_vars (char *buf, int maxlen)
{
#ifdef WIN32
{
DWORD len;
char usrnam[USER_NAME_MAX + 1];
len = USER_NAME_MAX;
if (GetUserName (usrnam, &len) != 0)
{
tn5250_config_replacedata ("$loginname$", usrnam, buf, maxlen);
}
}
#else
{
struct passwd *pwent;
pwent = getpwuid (getuid ());
if (pwent != NULL)
{
tn5250_config_replacedata ("$loginname$", pwent->pw_name, buf,
maxlen);
}
}
#endif
return;
}
/****f* lib5250/tn5250_config_replacedata
* NAME
* tn5250_config_replacedata
* SYNOPSIS
* tn5250_config_replacedata ("$loginname$", "fred", line, sizeof(line));
* INPUTS
* const char * from -
* const char * to -
* char * line -
* int maxlen -
* DESCRIPTION
* This will replace the first occurrance of the "from" string with
* the "to" string in a line of text. The from and to do not have to
* be the same length.
*****/
static void
tn5250_config_replacedata (const char *from, const char *to,
char *line, int maxlen)
{
char *p;
int len;
char *before, *after;
if ((p = strstr (line, from)) != NULL)
{
if (p <= line)
{
before = malloc (1);
*before = '\0';
}
else
{
len = p - line;
before = malloc (len + 1);
memcpy (before, line, len);
before[len] = '\0';
}
p += strlen (from);
if (strlen (p) < 1)
{
after = malloc (1);
*after = '\0';
}
else
{
len = strlen (p);
after = malloc (len + 1);
memcpy (after, p, len);
after[len] = '\0';
}
snprintf (line, maxlen - 1, "%s%s%s", before, to, after);
free (before);
free (after);
}
}
/* vi:set sts=3 sw=3: */
syntax highlighted by Code2HTML, v. 0.9.1