
/*
**  VARS.C
**
**	Variable Support for [X]DME
**
**  (C)Copyright 1992 by Bernd Noll for null/zero-soft
**  All Rights Reserved
**
**  RCS Header: $Id: vars.c,v 1.65 92/11/09 12:47:10 b_noll Exp $
**
**  compiling with
**  "#define PATCH_NULL"
**  "#define PATCH_VARS"
**  "#define PATCH_FLAGS"
**  causes that module to be used (and var.c not to be used)
**
*!***************************************************************************
*!
*!  VARIABLES - OVERVIEW
*!
*!
*!  the vars-module shall support access to different types of
*!  variables
*!  which are currently "special" vars/flags, "global" vars/flags,
*!  "textlocal" vars/flags, "macrolocal" vars and "env" vars
*!  also keybindings and menuitems are accessible via variables
*!  in some versions of xdme also rexxclips, rexxvars, and macro-bodies
*!  might be accessible via the variable interface, but that feature
*!  has been dropped due to less feedback
**  (there are some other types used for DME, but not for XDME yet)
*!
*!
*!  WARNING:
*!	as this module is written very close
*!	to DME's internal hierachy, it will be object of major changes,
**	as soon as Packages are introduced as a new concept for management
**	of macros, menues, keys, flags and variables
*!	in fact, that means, that funcionality is dropped as soon, as
*!	the C-Interpreter is available
*!
*! some of the different VARIABLE command are following ...
*!
**
**
**  GetTypedVar / SetTypedVar / VAR_...types/prefixes
**
**  the "Highlevel" interface to most other variable modules,
**  which should most times be used in Programming -
**  Cleanest Possiblility of Access to Variables of any Type.
**
**  We have put all variable functions, which do not need access
**  to certain structures (rexx.../argstack...), together in that
**  module and try to export only a very small interface, which
**  should allow information hiding
**
**  this module should completely cover the "old" module var.c
**  so I put var.c into '#ifndef PATCH_VARS'
**
*****************************************************************
**
**  Implementor's note
**
**	The module looks pretty ugly,
**	I am not ready documenting and porting all
**	functions which had been written for DME
**	into XDME partly as many differences have occurred
**	partly as I have started designing a Packages-concept
**
**  XDME NOTE - the flags - routines are not used yet for XDME
*/

/**************************************
		Includes
**************************************/
#include "defs.h"


/**************************************
	   Internal Prototypes
**************************************/
Prototype void	    init_variables  (void);
Prototype char *    GetTypedVar     (char *, int *);
Prototype void	    SetTypedVar     (char *, char *, int);
extern	  void	    SetDMEVar	    (char *, char *);   /* Proto'd for access from scanf.c */
extern	  void	    SetTextVar	    (char *, char *);
extern	  void	    SetDEnv	    (char *, char *);
extern	  char *    GetDMEVar	    (char *);
extern	  char *    GetTextVar	    (char *);
extern	  char *    GetEnvVar	    (char *);
extern	  char *    GetDEnv	    (char *);
Prototype void	    do_set	    (void);
Prototype void	    do_unset	    (void);
Prototype void	    do_setenv	    (void);
Prototype void	    do_unsetenv     (void);
Prototype void	    do_settvar	    (void);
Prototype void	    do_unsettvar    (void);
Prototype int	    is_tflagset     (int);              /* Proto'd for access from control.c */
Prototype int	    is_gflagset     (int);              /* Proto'd for access from control.c */
extern	  char *    GetTextFlag     (char *);
extern	  char *    GetGlobalFlag   (char *);
extern	  char	    SetAnyFlag	    (char *, char *);
extern	  char	    SetGlobalFlag   (char *, char *);
extern	  char	    SetTextFlag     (char *, char *);
Prototype void	    do_flag	    (void);
Prototype void	    do_toggleflag   (void);
Prototype char *    getvar	    (char *);


/**************************************
	    Internal Variables
**************************************/
#ifndef VAR_NUM
#define VAR_NUM 8
#endif

#ifndef SIGN_LOCAL_FLAG
# define SIGN_LOCAL_FLAG 't'
#endif

#ifdef DBASE
# define SList	  DmeBase.SList
# define VarsTree DmeBase.SList
#else
APTR	VarsTree = NULL;
#endif






/*
**  init_variables()
**	initialize the "global" variables structures
**	call that function from main or make it '__autoinit'
*/

void
init_variables (void)
{
    VarsTree = NULL;
} /* init_variables */






/***************************************************************
**
**  Access to the different kinds of Named Variables
**
***************************************************************/



/**************************************
**  Handling of DME "global" Variables
** (if we are using Packages "global" might be the wrong name)
**************************************/

char *
GetDMEVar (char * name)
{
    return (GetVarFromTree(&VarsTree, name));
} /* GetDMEVar */



void
SetDMEVar (char * name, char * value)
{
    SetVarIntoTree (&VarsTree, name, value);
} /* SetDMEVar */



/*
*! >SET name value
*! >UNSET name
*!
*!  set a std DME variable to a new value or drop it and its contents
*!  if the name is only a number, set the according dme std flag
*!
*!  NOTE that if packages are ready, SET should default to PVars/PFlags,
*!	not to GVars/GFlags
*!
*/

void
do_set (void)
{
    if (!SetGlobalFlag(av[1], av[2]))
	SetDMEVar (av[1],av[2]);
} /* do_set */



void
do_unset (void)
{
    DelVarFromTree (&VarsTree, av[1]);
} /* do_unset */






/**************************************
**  Handling of Text Local Variables
**************************************/

char *
GetTextVar (char * name)
{
    return (GetVarFromTree (&Ep->textvars, name));
} /* GetTextVar */



void
SetTextVar (char * name, char * value)
{
    SetVarIntoTree (&Ep->textvars, name, value);
} /* SetTextVar */



/*
*! >SETTVAR name value
*! >UNSETTVAR name
*!
*!  set a text local variable to a new value or drop it and its contents
*!  if the name is only a "t"number, set the according text local flag
*!
*/

void
do_settvar (void)
{
    if (!SetTextFlag(av[1], av[2]))
	SetTextVar (av[1],av[2]);
} /* do_settvar */



void
do_unsettvar (void)
{
    DelVarFromTree (&Ep->textvars, av[1]);
} /* do_unsettvar */






/***************************************************************
**
**  Access to Environment variables
**
***************************************************************/



char *
GetDEnv (char * ename)
{
    long   envLock = Lock("env:", SHARED_LOCK);
    char * str	   = NULL;

    if (envLock)
    {
	long  oldLock = CurrentDir (envLock);
	FILE * fi     = fopen (ename, "r");
	long   siz;

	if (fi)
	{
	    fseek (fi, 0L, 2);
	    siz = ftell (fi);
	    fseek (fi, 0L, 0);

	    if (siz > 0 && (str = malloc (siz + 1)))
	    {
		fread (str, siz, 1, fi);
		str[siz] = 0;
	    } /* if malloced */
	    fclose (fi);
	} /* if opened */
	UnLock (CurrentDir (oldLock));
    } /* if locked */
    return (str);
} /* GetDEnv */



void
SetDEnv (char * ename, char * econt)
{
    long envLock = Lock ("env:", SHARED_LOCK);

    if (envLock)
    {
	long   oldLock = CurrentDir (envLock);
	FILE * fi      = fopen (ename, "w");

	if (fi)
	{
	    fwrite (econt, strlen(econt), 1, fi);
	    fclose (fi);
	} /* if opened */
	UnLock (CurrentDir(oldLock));
    } /* if locked */
} /* SetDEnv */



char *
GetEnvVar(char * name)
{
    char * str;

    mountrequest(0);
    str = GetDEnv(name);
    mountrequest(1);
    return(str);
} /* GetEnvVar */



/*
*! >SETENV name value
*! >UNSETENV name
*!
*!  set an Environment variable to a new value or drop it and its contents
*/

void do_setenv (void)
{
    SetDEnv ((char *)av[1], (char *)av[2]);
} /* do_setenv */



void do_unsetenv (void)
{
    char * tmp = malloc(4+1+strlen(av[1]));

    if (tmp)
    {
	strcpy(tmp, "ENV:");
	strcat(tmp, av[1]);

	mountrequest (0);
	DeleteFile ((STRPTR)tmp);
	mountrequest (1);

	free (tmp);
    } /* if */
} /* do_unsetenv */






/***************************************************************
**
**  Access to the different kinds of Flags
**
***************************************************************/


/**************************************
**  Handling of TEXT Local Flags
**************************************/

int is_tflagset(int num)
{
    return (IsFlagSet(Ep->textflags, num, 32));
} /* is_tflagset */



char SetTextFlag(char * name, char *  value)
{
    return (SetFlag(Ep->textflags, name, value, 32, "t"));
} /* SetTextFlag */



/*
 *  GetTextFlag: get a variable of a text's internal flags
 */

char * GetTextFlag (char * name)
{
    return (GetFlag(Ep->textflags, name, 32, "t"));
} /* GetTextFlag */





#ifdef DBASE
# define tg DmeBase.gflags
#else
char tg[MAXTOGGLE/8]; /* test purposes */
#endif /* DBASE */

/**************************************
**  Handling of DME Global Flags
**************************************/

int is_gflagset (int num)
{
    return(IsFlagSet(tg, num, MAXTOGGLE));
} /* is_gflagset */



/*
 *  GetGlobalFlag : get a variable from dme's flag-list
 */

char SetGlobalFlag(char * name, char * value)
{
    return(SetFlag(tg, name, value, MAXTOGGLE, "")); /* "g" */
} /* SetGlobalFlag */



char * GetGlobalFlag (char * name)
{
    return(GetFlag(tg, name, MAXTOGGLE, ""));   /* "g" */
} /* GetGlobalFlag */






/**************************************
**  Interface to come around with all types of flags
**************************************/

char
SetAnyFlag(char * name, char * qual)
{
    if (name && qual)
    {
	if (is_number(name))
	{
	    return(SetGlobalFlag(name, qual));

	/* } if (!is_digit(name[1]))
	{	       // Commented out for XDME */
	//     return(SetSpecialFlag(name, qual));  /* Commented out for XDME */

	} else if (name[0] == SIGN_LOCAL_FLAG)
	{
	    return(SetTextFlag(name, qual));

	} else
	{
	    /* abort(); */
	    return(0);
	} /* if */
    } else
    {
	abort(0);
    } /* if */
} /* SetAnyFlag */



/*
*! >FLAG flagname on/off/toggle/switch/0/1/set/reset/true/false
*!
*!  set any flag that is controlled by DME
*!  with flagname is number, "t"number or specialflagname
*!
*/

void
do_flag (void)
{
    if (!SetAnyFlag(av[1],av[2]))
	abort2();
} /* do_flag */



/*
*! >[[RE]SET]TOGGLE flagname
*!
*!  modify any flag, that is controlled by DME
*!  with flagname is number, "i"number or specialflag
*!
*/

void
do_toggleflag (void)
{
    SetAnyFlag(av[1], av[0]);
} /* do_toggleflag */



/***************************************************************
**
**  Interface to be used for external functions and commands
**
***************************************************************/



/*
** the different types of "variables",
** which seem to be possible to be recognized
**
** these definitions must be visible to all modules
*/

// #define VAR_NEX 0  /* not existing variable */
// #define VAR_FPK 1  /* explicite access to another ("foreign") package */
// #define VAR_SF  2  /* dme special flags */
// #define VAR_SI  3  /* dme special integer variable */
// #define VAR_SV  4  /* dme special vars scanf/filename/... */
// #define VAR_MF  5  /* dme macro   flag */
// #define VAR_TF  6  /* dme text    flag */
// #define VAR_PF  7  /* dme package flag */
// #define VAR_GF  8  /* dme global  flag */
// #define VAR_TV  9  /* dme text    variable */
// #define VAR_MV  10 /* dme macro   variable */
// #define VAR_PV  11 /* dme package variable */
// #define VAR_GV  12 /* dme global  variable */
// #define VAR_ARG 13 /* dme macro   parameter */
// #define VAR_ENV 14 /* CBM env: variable */
// #define VAR_SH  15 /* CBM local shell-var		    (FUTURE) */
// #define VAR_CLP 16 /* rexx cliplist - entry */
// #define VAR_RXX 17 /* rexx variable via RVI */
// #define VAR_RXF 18 /* rexx result of functioncall */
// #define VAR_MAP 19 /* dme package key-mapping */
// #define VAR_MEN 20 /* dme package menu */
// #define VAR_MNX 21 /* Arp shell-var			    (FUTURE) */

// #define VAR_DME VAR_GV /* alias */

#define VF_COP	1  /* duplicate the result */
#define VF_PAW	2  /* Prefix AlWays: dont use without Prefix (FUTURE) */


typedef struct _vtype
{
    const char * name;	/* prefix */
    int     id; 	/* number for communication */
    int     len;	/* size of prefix for comparison */
    int     offset;	/* offset to cut prefix */
    int     flags;	/* flags (e.g. duplicate result ...) */
    char*   replace;	/* if prefix matches, replace w/that prefix */
    void    (*do_set)(char*,char*); /* set-function */
    char*   (*do_get)(char*);       /* get-function */
} VTYPE;


static CONST VTYPE vartypes[] =
{
#ifdef N_DEF
    {"SHVAR_",  VAR_SH , 6, 6,     0, NULL, NULL, NULL},
    {"ARP_",    VAR_MNX, 4, 4,     0, NULL, NULL, NULL},
    {"RXFUNC ", VAR_RXF, 7, 7,     0, NULL, NULL, getQ},
    {"RXSFUNC ",VAR_RXF, 8, 8,     0, NULL, NULL, getQ},
#endif
    {"SFLAG_",  VAR_SF,  6, 6,     0, NULL,     (void (*)(char*,char*))SetSpecialFlag, GetSpecialFlag},    /* we need 3 names */
    {"SINT_",   VAR_SI,  5, 5,     0, NULL,     (void (*)(char*,char*))SetSpecialInt,  GetSpecialInt},     /* for the 3 different types (or we have to put them together) */
    {"SPC_",    VAR_SV,  4, 4,     0, NULL,     (void (*)(char*,char*))SetSpecialVar,  GetSpecialVar},     /* of special vars else there are big problems */
    {"TFLAG_",  VAR_TF,  6, 6,     0, "t%s",    (void (*)(char*,char*))SetTextFlag,    GetTextFlag},
    {"GFLAG_",  VAR_GF,  6, 6,     0, NULL,     (void (*)(char*,char*))SetGlobalFlag,  GetGlobalFlag},  /* ifdef PATCH_PACK set replace to "g%s" */
    {"ARG_",    VAR_ARG, 4, 4,     0, "arg%s",  NULL,           getmacroarg},
    {"arg",     VAR_ARG, 3, 0,     0, NULL,     NULL,           getmacroarg},
    {"MVAR_",   VAR_MV,  5, 5,     0, NULL,     SetMacroVar,    getmacrovar},
    {"TVAR_",   VAR_TV,  5, 5,     0, NULL,     SetTextVar,     GetTextVar},
    {"GVAR_",   VAR_GV,  5, 5,     0, NULL,     SetDMEVar,      GetDMEVar},
    {"ENV_",    VAR_ENV, 4, 4,     0, NULL,     SetDEnv,        GetEnvVar},
#ifdef PATCH_RXCLIPS
    {"RXCLP_",  VAR_CLP, 6, 6,     0, NULL,     SetRexxClip,    GetRexxClip},
#endif /* PATCH_RXCLIPS */
#ifdef PATCH_RXVARS
    {"RXVAR_",  VAR_RXX, 6, 6,     0, NULL,     setrexxvar,     getrexxvar},
#endif /* PATCH_RXVARS */
    {"KEY_",    VAR_MAP, 4, 4,VF_COP, NULL,     (void (*)(char*,char*))mapkey,         keyspectomacro},
    {"MENU_",   VAR_MEN, 5, 5,VF_COP, NULL,     NULL,           menutomacro},
    {NULL,	VAR_NEX, 0, 0,	   0, NULL,	NULL, NULL}
};



/*
**  GetTypedVar()
**	this function is nearly the same as getvar from cmd2.c
**	major differences:
**	* it tells where it has found a variable;
**	* first we check if the search-name matches a certain
**	  type-prefix and if it does, we use that type and its
**	  result (w/out respect if result != NULL)
**	* then we check all types until we get a non-NULL result
**	  or an abortion
**	* the end of the vartypes-list means - there is no variable
**	  of that name
**	(type may be NULL)
*/

char *
GetTypedVar (char * find, int *  type)
{
    char  * found	= NULL;
    int     itype	= VAR_NEX;
    VTYPE * vt;
    char    inter_abort = globalflags.Abortcommand;

    if (type)
	*type = VAR_NEX;

    if (find == NULL)
    {		/* is there a name ? */
	abort (NULL);
    } else
    if (find[0] == '\0')
    { /* is it really a name ? */
	return (NULL);
    } /* if name corrupt */

    globalflags.Abortcommand = 0;

/* if (globalflags.debug) printf("\tgetvar %s ? prefix...", find); */
    for (vt = vartypes; vt->id != VAR_NEX && globalflags.Abortcommand == 0; vt++)
    {
/* if (globalflags.debug) printf("p"); */
	if (strncmp(find, vt->name, vt->len) == 0)
	{
	    char* ptr = find+vt->offset;
	    if (vt->replace)
	    {
		sprintf(tmp_buffer, vt->replace, ptr);
		ptr = tmp_buffer;
	    } /* if prefix replacement */
	    itype = vt->id;
	    found = (*vt->do_get)(ptr);
	    if (found)
	    {
/* if (globalflags.debug) printf(" found prefix %s\n", vt->name); */
		if (vt->flags & VF_COP)
		{
		    found = strdup(found);
		} /* if dup'd */
	    } else
	    {
/* if (globalflags.debug) printf ("Aborting\n"); */
		globalflags.Abortcommand = 1;
	    } /* if res(!)=0 */
	    goto gv_quit;
	} /* if matching prefix */
    } /* for all types */

/* if (globalflags.debug) printf(" direct..."); */
    for (vt = vartypes; vt->id != VAR_NEX && globalflags.Abortcommand == 0; vt++)
    {
/* if (globalflags.debug) printf("c"); */
	if (vt->do_get)
	{
	    if (found = (*vt->do_get)(find))
	    {
/* if (globalflags.debug) printf("found type %s\n", vt->name); */
		itype = vt->id;
		if (vt->flags & VF_COP)
		{
		    found = strdup(found);
		} /* if dup'd */
		goto gv_quit;
	    } /* if res#0 */
	} /* if ex det-func */
    } /* for all types */

gv_quit:

/* if (globalflags.debug) printf("\tgetvar:prefix/err/oerr/val == %s/%d/%d/%s\n",vt->name, (int)Abortcommand, (int)inter_abort, found); */

    globalflags.Abortcommand |= inter_abort;
    if (type)
    {
	*type = itype;
    }
    return(found);
} /* GetTypedVar */



/*
**  Set a Variable
**	if it has a known type, use the associated set-command
**	else check if there is a matching prefix
**	if nothing fits - abort
*/

void
SetTypedVar (char * name, char * value, int type)
{
    VTYPE* vt;

    if ((name == NULL) || (name[0] == '\0'))
    { /* is it a name ? */
	abort2 ();
    } /* if name corrupt */

    if (type == VAR_NEX)
    {
	for (vt = vartypes; vt->id != VAR_NEX && globalflags.Abortcommand == 0; vt++)
	{
	    if (strncmp(name, vt->name, vt->len) == 0)
	    {
		char* ptr = name+vt->offset;
/* printf("setting ano type %s\n", vt->name); */
		if (vt->do_set != NULL && name[vt->offset] != 0)
		{
		    if (vt->replace)
		    {
			sprintf(tmp_buffer, vt->replace, ptr);
			ptr = tmp_buffer;
		    } /* if prefix replacement */
		    (*vt->do_set)(ptr, value);
		    return;
		} else
		{
		    abort2();
		} /* if (no) set-function */
	    } /* if matching prefix */
	} /* for all types */
	abort2();
    } /* if unknown type */

    for (vt = vartypes; vt->id != VAR_NEX; vt++)
    {
	if (vt->id == type)
	{
	    if (vt->do_set)
	    {
/* printf("setting ok type %s\n", vt->name); */
		if (strncmp(name, vt->name, vt->len) == 0)
		{
		    char* ptr = name+vt->offset;
		    if (vt->replace)
		    {
			sprintf(tmp_buffer, vt->replace, ptr);
			ptr = tmp_buffer;
		    } /* if prefix replacement */
		    (*vt->do_set)(ptr, value);
		} else
		{
		    (*vt->do_set)(name, value);
		} /* if (no) matching prefix */
		return;
	    } else
	    {
		abort2();
	    } /* if (no) set-function */
	} /* if id found */
    } /* for all types */

    abort2();
} /* SetTypedVar */



/*
**  Search
**  (1) special Variables (2) Flags (3) macro's list,
**  (4) text's list, (5) internal list, (6) enviroment,
**  (7) Rexx cliplist (8) macros.  The variable is allocated
**  with malloc().  NULL if not found.  ENV: need not exist.
*/

char * getvar (char * find)
{
    return (GetTypedVar(find,NULL));
} /* getvar */


/******************************************************************************
*****  END vars.c
******************************************************************************/
