/*
 * macros.c --
 *
 * Defines and retrieves macros
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[]="$Header: /ufs/repository/magic/macros/macros.c,v 1.7 2006/02/08 03:47:08 fang Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>		/* for toupper() */
#ifdef XLIB
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#endif
#include "misc/magic.h"
#include "utils/utils.h"
#include "utils/malloc.h"
#include "macros/macros.h"
#include "textio/textio.h"



/* Define the start of the macro table */

macrodef *mactable = NULL;


/*---------------------------------------------------------
 * MacroDefine:
 *	This procedure defines a macro.
 *
 * Results:	None.
 *
 * Side Effects:
 *	The string passed is copied and considered to be the
 *	macro definition for the character.
 *---------------------------------------------------------
 */

void
MacroDefine(xc, str, imacro)
int xc;			/* full (X11) keycode of macro with modifiers */
char *str;		/* ...and the string to be attached to it */
bool imacro;		/* is this an interactive macro? */
{
    macrodef *newmacro, *cmacro;

    /* If a macro exists, delete the old string and redefine it */

    for (cmacro = mactable; cmacro != NULL; cmacro = cmacro->nextmacro)
    {
	if (cmacro->keyvalue == xc)
	{
	    newmacro = cmacro;
	    break;
	}
    }
    if (cmacro == NULL)
    {
	MALLOC(macrodef *, newmacro, sizeof(macrodef));
	newmacro->keyvalue = xc;
	newmacro->nextmacro = mactable;
	newmacro->macrodef = (char *)NULL;
	mactable = newmacro;
    }
    newmacro->interactive = imacro;
    newmacro->macrodef = StrDup(&(newmacro->macrodef), str);
}


/*---------------------------------------------------------
 * MacroRetrieve:
 *	This procedure retrieves a macro.
 *
 * Results:
 *	A pointer to a new Malloc'ed string is returned.
 *	This structure should be freed when the caller is
 *	done with it.
 *
 * Side Effects:
 *	None.
 *---------------------------------------------------------
 */

char *
MacroRetrieve(xc, iReturn)
int xc;			/* the extended name of the macro */
bool *iReturn;		/* TRUE if macro is interactive */
{
    macrodef *cmacro;

    for (cmacro = mactable; cmacro != NULL; cmacro = cmacro->nextmacro)
    {
	if (cmacro->keyvalue == xc)
	{
	    if (iReturn != NULL)
		*iReturn = cmacro->interactive;
	    return ( StrDup( (char **) NULL, cmacro->macrodef) );
	}
    }
    return (char *)NULL;
}


/*---------------------------------------------------------
 * MacroDelete:
 *	This procedure deletes a macro.
 *
 * Results:	None.
 *
 * Side Effects:
 *	The string that defines a macro is deleted.  This means
 *	that if anybody still has a pointer to that string
 *	they are in trouble.
 *---------------------------------------------------------
 */

void
MacroDelete(xc)
int xc;			/* the extended name of the macro */
{
    macrodef *cmacro, *lmacro = NULL;

    for (cmacro = mactable; cmacro != NULL; cmacro = cmacro->nextmacro)
    {
	if (cmacro->keyvalue == xc)
	{
	    if (lmacro != NULL)
		lmacro->nextmacro = cmacro->nextmacro;
	    else
		mactable = cmacro->nextmacro;
	    FREE(cmacro->macrodef);
	    FREE((char*) cmacro);
	    break;
	}
	lmacro = cmacro;
    }
}


/*
 * ----------------------------------------------------------------------------
 * MacroName --
 * 	Convert an extended keycode to a string.
 *
 * Results:
 *	The string.
 *
 * Side effects:
 *	The string is malloc'ed from memory, and must be free'd later.
 * ----------------------------------------------------------------------------
 */

char *
MacroName(xc)
int xc;
{
    char *vis;
    static char hex[17] = "0123456789ABCDEF";

#ifdef XLIB
    char *str;
    extern Display *grXdpy;
    KeySym ks = xc & 0xffff;
    int kmod = xc >> 16;

    str = NULL;
    if (grXdpy != NULL) {
        ks = xc & 0xffff;
	if (ks != NoSymbol) str = XKeysymToString(ks);
    }
    if (str != NULL)
    {
	vis = (char *) mallocMagic( sizeof(char) * (strlen(str) + 32) );
	vis[0] = '\0';
	if (kmod & Mod1Mask) strcat(vis, "Meta_");
	if (kmod & ControlMask) strcat(vis, "Control_");
	if (kmod & LockMask) strcat(vis, "Capslock_");
	if (kmod & ShiftMask) strcat(vis, "Shift_");
	strcat(vis, "XK_");
	strcat(vis, str);
	return (vis);
    }
#endif

    vis = (char *) mallocMagic( sizeof(char) * 6 );
    if (xc < (int)' ')
    {
	vis[0] = '^';
	vis[1] = (char)xc + '@';
	vis[2] = '\0';
    }
    else if (xc == 0x7F)
    {
	vis[0] = '<';
	vis[1] = 'd';
	vis[2] = 'e';
	vis[3] = 'l';
	vis[4] = '>';
	vis[5] = '\0';
    }
    else if (xc < 0x80)
    {
	vis[0] = (char)xc;
	vis[1] = '\0';
    }
    else
    {
	vis = (char *) mallocMagic( sizeof(char) * 8 );
	vis[0] = '0';
	vis[1] = 'x';
#ifdef XLIB
	vis[2] = hex[ (kmod & 0xf)];
#else
	vis[2] = '0';
#endif
	vis[3] = hex[ (xc & 0x0f000) >> 12];
	vis[4] = hex[ (xc & 0x00f00) >>  8];
	vis[5] = hex[ (xc & 0x000f0) >>  4];
	vis[6] = hex[ (xc & 0x0000f)      ];
	vis[7] = '\0';
    }
    return(vis);
}


/*
 * ----------------------------------------------------------------------------
 * MacroKey:
 * 	Convert a string to an extended keycode.
 *
 * Results:
 *	An extended macro name.
 *
 * Side effects:
 *	If the display being used is not X11, a warning is printed the
 *	first time this function is called.
 * ----------------------------------------------------------------------------
 */

int
MacroKey(str, verbose)
char *str;
int *verbose;
{
    static int warn = 1;

#ifdef XLIB
    int kc=0;
    char *vis;
    extern Display *grXdpy;
    KeySym ks;
    int kmod = 0;

    *verbose = 1;
    if (grXdpy != NULL)
    {
	vis = str;
	while( (*vis) != '\0' )
	{
	    if (!strncmp(vis, "Meta_", 5))
	    {
		kmod |= Mod1Mask;
		vis += 5;
	    }
	    else if (!strncmp(vis, "Alt_", 4))
	    {
		kmod |= Mod1Mask;
		vis += 4;
	    }
	    else if (!strncmp(vis, "Control_", 8))
	    {
		kmod |= ControlMask;
		vis += 8;
	    }
	    else if (((*vis) == '^') && (*(vis + 1) != '\0'))
	    {
		kmod |= ControlMask;
		vis++;
	    }
	    else if (!strncmp(vis, "Capslock_", 9))
	    {
		kmod |= LockMask;
		vis += 9;
	    }
	    else if (!strncmp(vis, "Shift_", 6))
	    {
		kmod |= ShiftMask;
		vis += 6;
	    }
	    else break;
	}
	if (!strncmp(vis, "XK_", 3)) vis += 3;

	/* We're converting all ASCII back into X Keycodes 	   */
	/* The original macro (.magic) file format allows embedded */
	/* control characters, so we have to handle those.  For    */
	/* regular ASCII characters, the keysym = the ASCII value. */

	if ((*(vis + 1)) == '\0')
	{
	    /* single ASCII character handling */

	    char tc = *vis;
	   
	    /* Revert Control and Shift characters to ASCII	*/
	    /* unless other modifiers are present. Always make	*/
	    /* characters with Control and Shift uppercase.	*/

	    if (kmod & (ControlMask | ShiftMask))
	    {
		tc = toupper(tc);
		if (kmod & ShiftMask)
		    kc = (int)tc;
		else if (kmod & ControlMask)
		    kc = (int)(tc - 'A' + 1);

		if (!(kmod & (Mod1Mask | LockMask)))
		    if (!(kmod & ShiftMask) || !(kmod & ControlMask))
		       kmod = 0;
	    }
	    else
		kc = (int)tc;
	}
	else
	{
	    /* X11 keysym name handling */

	    ks = XStringToKeysym(vis);
	    kc = (ks != NoSymbol) ? ks & 0xffff : 0;
	}
	return (kc | (kmod << 16));
    }
#endif

    *verbose = 1;
    if (strlen(str) == 1)
    {
	return (int)str[0];
    }
    else if (strlen(str) == 2 && *str == '^')
    {
	return (int)str[1] - 'A' + 1;
    }
    if (warn)
	TxError("Extended macros are unavailable with this device type.\n");
    warn = 0;
    *verbose = 0;
    return(0);
}


/*
 * ----------------------------------------------------------------------------
 * CommandLineTranslate
 * 	Convert X11 definitions to readline controls
 *
 * Results:
 *	A converted character (integer)
 *
 * Side effects:
 *	None.
 * ----------------------------------------------------------------------------
 */

int
TranslateChar(key)
int key;
{
    int rval = key;

#ifdef XLIB
    switch (key)
    {
	case XK_Left:
	    rval = (int)'\002';	/* Ctrl-B */
	    break;
	case XK_Right:
	    rval = (int)'\006';	/* Ctrl-F */
	    break;
	case XK_Up:
	    rval = (int)'\020';	/* Ctrl-P */
	    break;
	case XK_Down:
	    rval = (int)'\016';	/* Ctrl-N */
	    break;
	case XK_BackSpace:
	case XK_Delete:
	    rval = (int)'\010';	/* Ctrl-H */
	    break;
	case XK_Home:
	    rval = (int)'\001';	/* Ctrl-A */
	    break;
	case XK_End:
	    rval = (int)'\005';	/* Ctrl-E */
	    break;
    }
#endif
    return rval;
}
