/* TN5250 - An implementation of the 5250 telnet protocol.
 * Copyright (C) 1997 Michael Madore
 * 
 * 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"

#define	MAX_LINESZ	103	/* Maximum macro line size */
#define CR		0x0D
#define LF		0x0A
#define	MAX_SPECKEY	12	/* Maximum special key name size */

struct _MacroKey {
   int	km_code ;
   char	km_str[MAX_SPECKEY] ;
} ;

typedef struct _MacroKey MacroKey ;

/* Table based on terminal.h */
static const MacroKey MKey[] = {
	{K_ENTER ,	"ENTER" },
	{K_NEWLINE ,	"NEWLINE" },
	{K_TAB ,	"TAB" },
	{K_BACKTAB ,	"BACKTAB" },
	{K_F1 ,		"F1" },
	{K_F2 ,		"F2" },
	{K_F3 ,		"F3" },
	{K_F4 ,		"F4" },
	{K_F5 ,		"F5" },
	{K_F6 ,		"F6" },
	{K_F7 ,		"F7" },
	{K_F8 ,		"F8" },
	{K_F9 ,		"F9" },
	{K_F10 ,	"F10" },
	{K_F11 ,	"F11" },
	{K_F12 ,	"F12" },
	{K_F13 ,	"F13" },
	{K_F14 ,	"F14" },
	{K_F15 ,	"F15" },
	{K_F16 ,	"F16" },
	{K_F17 ,	"F17" },
	{K_F18 ,	"F18" },
	{K_F19 ,	"F19" },
	{K_F20 ,	"F20" },
	{K_F21 ,	"F21" },
	{K_F22 ,	"F22" },
	{K_F23 ,	"F23" },
	{K_F24 ,	"F24" },
	{K_LEFT ,	"LEFT" },
	{K_RIGHT ,	"RIGHT" },
	{K_UP ,		"UP" },
	{K_DOWN ,	"DOWN" },
	{K_ROLLDN ,	"ROLLDN" },
	{K_ROLLUP ,	"ROLLUP" },
	{K_BACKSPACE ,	"BACKSPACE" },
	{K_HOME ,	"HOME" },
	{K_END ,	"END" },
	{K_INSERT ,	"INSERT" },
	{K_DELETE ,	"DELETE" },
	{K_RESET ,	"RESET" },
	{K_PRINT ,	"PRINT" },
	{K_HELP ,	"HELP" },
	{K_SYSREQ ,	"SYSREQ" },
	{K_CLEAR ,	"CLEAR" },
	{K_REFRESH ,	"REFRESH" },
	{K_FIELDEXIT ,	"FIELDEXIT" },
	{K_TESTREQ ,	"TESTREQ" },
	{K_TOGGLE ,	"TOGGLE" },
	{K_ERASE ,	"ERASE" },
	{K_ATTENTION ,	"ATTENTION" },
	{K_DUPLICATE ,	"DUPLICATE" },
	{K_FIELDMINUS ,	"FIELDMINUS" },
	{K_FIELDPLUS ,	"FIELDPLUS" },
	{K_PREVWORD ,	"PREVWORD" },
	{K_NEXTWORD ,	"NEXTWORD" },
	{K_FIELDHOME ,	"FIELDHOME" },
	{K_EXEC ,	"EXEC" },
	{K_MEMO ,	"MEMO" },
	{K_COPY_TEXT ,	"COPY_TEXT" },
	{K_PASTE_TEXT ,	"PASTE_TEXT" },
	{0 ,		""}
} ;

char PState[12] ;		/* Printable state */

/*
 * Clean trailing CRs. Returns line size
 */
int macro_buffer_clean (char *Buff)
{
   int i ;

   i = strlen(Buff) - 1 ;
   while ((i >= 0) && ((Buff[i] == CR) || (Buff[i] == LF)))
      Buff[i--] = 0 ;

   return (i+1) ;
}

/*
 * Return macro number
 */
int macro_isnewmacro (char *Buff)
{
   int i,Num ;

   Num = 0 ;
   if ((Buff[0] == '[') && (Buff[1] == 'M'))
   {
      i = 2 ;
      while (isdigit(Buff[i]))
      {
         Num = (Num * 10) + Buff[i] - '0' ;
         i++ ;
      }
      if (Buff[i] != ']')
         Num = 0 ;
   }

   return (Num) ;
}

/*
 * Determines the macro size
 */
int macro_macrosize (int *Mac)
{
   int i ;

   i = 0 ;
   if (Mac != NULL)
      while (*Mac != 0)
      {
         i++ ;
         Mac++ ;
      }

   return (i) ;
}

/*
 * Reads a special key from macro text
 */
int macro_specialkey (char *Buff, int * Pt)
{
   int i,j ;

   if (Buff[*Pt] == '[')
   {
      i = 1 ;
      while ((Buff[*Pt+i] != 0) && (Buff[*Pt+i] != ']') && (i <= MAX_SPECKEY))
         i++ ;

      if (Buff[*Pt+i] == ']')
      {
         j = 0 ;
         while ((MKey[j].km_code != 0) && 
                (strncmp(MKey[j].km_str,&Buff[*Pt+1],i-1) != 0))
            j++ ;
         if (MKey[j].km_code != 0)
         {
            *Pt += i ;
            return (MKey[j].km_code) ;
         }
      }
   }
   return (0) ;
}

/*
 * Add the line contents to macro
 * TODO : add an escape character
 * TODO : resize macro buffer when special keys are transcoded
 */
void macro_addline (int **PDest, char *Buff, int Sz)
{
   int i,j,key ;
   int *Buffer ;
   int NSz, OSz ;

   if (*PDest == NULL)
   {
      Buffer = (int *)malloc((Sz+1)*sizeof(int));
      OSz = 0 ;
   }
   else
   {
      OSz = macro_macrosize(*PDest) ;
      NSz = OSz + Sz + 1 ;
      Buffer = *PDest ;
      Buffer = (int *)realloc(*PDest,NSz*sizeof(int)) ;
   }

   if (Buffer != NULL)
   {
      *PDest = Buffer ;

      i = j = 0 ;
      while (Buff[j] != 0)
      {
         if ((key = macro_specialkey (Buff,&j)) > 0)
            Buffer[OSz+i] = key ;	/* maybe should resize buffer here */
         else
            Buffer[OSz+i] = (int)Buff[j] ;
         i++ ;
         j++ ;
      }

      Buffer[OSz+i] = 0 ;
   }
}

/*
 * Load the macro definitions file
 */
char macro_loadfile (Tn5250Macro *Macro)
{
   FILE *MFile ;
   int Sz,Num,CurMacro ;
   char Buffer[MAX_LINESZ] ;

   if (Macro->fname != NULL)
   {
      if ((MFile = fopen (Macro->fname,"rt")) != NULL)
      {
         CurMacro = 0 ;
         while (fgets(Buffer,MAX_LINESZ,MFile) != NULL)
         {
            Sz = macro_buffer_clean (Buffer) ;
            if ((Num=macro_isnewmacro(Buffer)) > 0)
            {
               if (Num <= 24)
                  CurMacro = Num ;
            }
            else
               if ((CurMacro > 0) && (Sz > 0))
                  macro_addline (&Macro->BuffM[CurMacro-1],Buffer,Sz) ;
         }
         fclose (MFile) ;
      }
      return (1) ;
   }

   return (0) ;
}

/*
 * Clear macros in memory
 */
void macro_clearmem (Tn5250Macro *Macro)
{
   int i ;

   for (i=0;i < 24;i++)
      if (Macro->BuffM[i] != NULL)
      {
         free (Macro->BuffM[i]) ;
         Macro->BuffM[i] = NULL ;
      }
}

#ifdef __WIN32__
/*
 * Get the macro file name
 *    Win32 version -- Looks for a file in the same dir as the .exe
 */
char *macro_filename (Tn5250Display *Dsp)
{
#define PATHSIZE 1024
   LPTSTR apppath;
   LPTSTR dir, fname;
   DWORD len;
   LPTSTR lpMsgBuf;
   const char *cnf ;

   apppath = malloc(PATHSIZE+1);

   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 (NULL);
   }

   if (strrchr(apppath, '\\')) {
        len = strrchr(apppath, '\\') - apppath;
        apppath[len+1] = '\0';
   }

   dir = malloc(strlen(apppath) + 15);

   strcpy(dir, apppath);
   strcat(dir, "tn5250macros");
   free(apppath);

   fname = dir ;
   if ((cnf = tn5250_config_get (Dsp->config,"macros")) != NULL)
   {
      fname = (char *)malloc (strlen(cnf)+1) ;
      if (fname != NULL)
      {
         memcpy (fname,cnf,strlen(cnf)+1) ;
         free (dir) ;
      }
   }

   return (fname);
}

#else

/*
 * Get the macro file name
 */
char *macro_filename (Tn5250Display *Dsp)
{
   struct passwd *pwent;
   char *dir, *fname;
   const char *cnf ;

   pwent = getpwuid (getuid ());
   if (pwent == NULL)
      return (NULL) ;

   dir = (char *)malloc (strlen (pwent->pw_dir) + 16);
   if (dir == NULL)
      return (NULL) ;

   strcpy (dir, pwent->pw_dir);
   strcat (dir, "/.tn5250macros");

   fname = dir ;
   if ((cnf = tn5250_config_get (Dsp->config,"macros")) != NULL)
   {
      fname = (char *)malloc (strlen(cnf)+1) ;
      if (fname != NULL)
      {
         memcpy (fname,cnf,strlen(cnf)+1) ;
         free (dir) ;
      }
   }

   return (fname) ;
}

#endif

/****f* lib5250/tn5250_macro_init
 * NAME
 *    tn5250_macro_init
 * SYNOPSIS
 *    ret = tn5250_macro_init
 * INPUTS
 *    None
 * DESCRIPTION
 *    Macro system initialization
 *****/
Tn5250Macro *tn5250_macro_init()
{
   Tn5250Macro *This;
   int i ;

   This = tn5250_new(Tn5250Macro, 1);
   if (This == NULL)
      return NULL;

   This->RState = 0 ;
   This->EState = 0 ;
   This->TleBuff = 0 ;
   for (i=0;i < 24; i++)
      This->BuffM[i] = NULL ;

   return This;
}

/*
 * Write one macro to file
 */
void macro_write (int Num, int *Buff, FILE *MF)
{
   int i,j,Sz ;

   fprintf (MF,"[M%02i]\n",Num) ;

   i = Sz = 0 ;
   while (Buff[i] != 0)
   {
      j = 0 ;
      while ((MKey[j].km_code != 0) && (MKey[j].km_code != Buff[i]))
         j++ ;
      if (MKey[j].km_code == 0)
      {
         if (Sz + 1 > MAX_LINESZ-3)
         {
            fprintf (MF,"\n") ;
            Sz = 0 ;
         }
         fprintf (MF,"%c",(char)Buff[i]) ;
         Sz++ ;
      }
      else
      {
         if (Sz + strlen(MKey[j].km_str) + 2 > MAX_LINESZ-3)
         {
            fprintf (MF,"\n") ;
            Sz = 0 ;
         }
         fprintf (MF,"[%s]",MKey[j].km_str) ;
         Sz += strlen(MKey[j].km_str) + 2 ;
      }

      i++ ;
   }

   fprintf (MF,"\n\n") ;
}

/*
 * Save the macro definitions file
 */
char macro_savefile (Tn5250Macro *Macro)
{
   FILE *MFile ;
   int i;

   if (Macro->fname != NULL)
   {
      if ((MFile = fopen (Macro->fname,"wt")) != NULL)
      {
         for (i=0;i<24;i++)
         if (Macro->BuffM[i] != NULL)
            macro_write (i+1,Macro->BuffM[i],MFile) ;
   
         fclose (MFile) ;
      }
      return (1) ;
   }
   return (0) ;
}

/****f* lib5250/tn5250_macro_exit
 * NAME
 *    tn5250_macro_exit
 * SYNOPSIS
 *    tn5250_macro_exit (This)
 * INPUTS
 *    Tn5250Macro *      This       - 
 * DESCRIPTION
 *    Macro system termination
 *****/
void tn5250_macro_exit(Tn5250Macro * This)
{
   int i ;

   if (This != NULL)
   {
      /* macro_savefile (This) ; */

      if (This->fname != NULL)
         free (This->fname) ;

      for (i=0;i<24;i++)
	 free (This->BuffM[i]) ;
      free (This) ;
   }
}

/****f* lib5250/tn5250_macro_attach
 * NAME
 *    tn5250_macro_attach
 * SYNOPSIS
 *    tn5250_macro_attach (This);
 * INPUTS
 *    Tn5250Display *      This       - The display to attach to
 *    Tn5250Macro *       Macro     - The macro object to attach
 * DESCRIPTION
 *    Attach the macro system to display structure
 * TODO
 *    macro_detach ?
 *****/
int tn5250_macro_attach (Tn5250Display *This, Tn5250Macro *Macro)
{
   if ((This->macro == NULL) && (Macro != NULL))
   {
      Macro->fname = macro_filename(This) ;

      if (Macro->fname == NULL)
         TN5250_LOG (("Macro: fname NULL\n")) ;
      else
         TN5250_LOG (("Macro: fname=%s\n",Macro->fname)) ;

      /* macro_loadfile (Macro) ; */

      This->macro = Macro ;
      return (1) ;
   }
   return 0;
}

/****f* lib5250/tn5250_macro_rstate
 * NAME
 *    tn5250_macro_rstate
 * SYNOPSIS
 *    tn5250_macro_rstate (This);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 * DESCRIPTION
 *    Returns the current macro recording state
 *****/
char tn5250_macro_rstate (Tn5250Display *This)
{
	if (This->macro != NULL)
		return (This->macro->RState) ;
	return 0;
}

/****f* lib5250/tn5250_macro_startdef
 * NAME
 *    tn5250_macro_startdef
 * SYNOPSIS
 *    tn5250_macro_startdef (This);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 * DESCRIPTION
 *    Starts a macro definition
 *****/
char tn5250_macro_startdef (Tn5250Display *This)
{
	if (This->macro != NULL)
	{
	    This->macro->RState = 1 ;
	    This->macro->FctnKey = 0 ;
	    This->macro->TleBuff = MACRO_BUFSIZE ;
	    return (1) ;
	}
	return (0) ;
}

/****f* lib5250/tn5250_macro_enddef
 * NAME
 *    tn5250_macro_enddef
 * SYNOPSIS
 *    tn5250_macro_enddef (This);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 * DESCRIPTION
 *    Ends a macro definition
 *****/
void tn5250_macro_enddef (Tn5250Display *This)
{
   int NumMacro ;
   int *Buffer ;

	if (This->macro != NULL)
        {
	    if (This->macro->RState > 1)
	    {
	       NumMacro = This->macro->FctnKey - K_F1 ;
	       if (This->macro->TleBuff > 0)
	       {
	          This->macro->BuffM[NumMacro][This->macro->TleBuff++] = 0 ;

	          Buffer = (int *)realloc(This->macro->BuffM[NumMacro],
				This->macro->TleBuff*sizeof(int)) ;
	          if (Buffer != NULL)
	             This->macro->BuffM[NumMacro] = Buffer ;
	       }
	       else
	       {
	          free (This->macro->BuffM[NumMacro]) ;
	          This->macro->BuffM[NumMacro] = NULL ;
	       }

               macro_savefile (This->macro) ;
	    }
	    This->macro->RState = 0 ;
        }
}

/****f* lib5250/tn5250_macro_recfunct
 * NAME
 *    tn5250_macro_recfunct
 * SYNOPSIS
 *    tn5250_macro_recfunct (This,key);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 *    int 		   key     - function key received
 * DESCRIPTION
 *    Receives a function key. Return True if macro definition key
 *****/
char tn5250_macro_recfunct (Tn5250Display *This, int key)
{
   int *Buffer ;
   int NumMacro ;

	if ((This->macro != NULL) && (This->macro->RState == 1))
	{
	       Buffer = (int *)malloc((MACRO_BUFSIZE+1)*sizeof(int));
	       if (Buffer != NULL)
	       {
		  This->macro->RState = 2 ;
	          This->macro->FctnKey = key ;
		  NumMacro = key - K_F1 ;
		  if ((NumMacro >= 0) && (NumMacro < 24))
		  {
                     macro_clearmem (This->macro) ;
                     macro_loadfile (This->macro) ;

		     if (This->macro->BuffM[NumMacro] != NULL)
			free (This->macro->BuffM[NumMacro]) ;
		     This->macro->BuffM[NumMacro] = Buffer ;
		     This->macro->TleBuff = 0 ;
		     return (1) ;
		  }
		  else
		     free (Buffer) ;
	       }
	}
	return (0) ;
}

/****f* lib5250/tn5250_macro_reckey
 * NAME
 *    tn5250_macro_reckey
 * SYNOPSIS
 *    tn5250_macro_reckey (This,key);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 *    int 		   key     - key received
 * DESCRIPTION
 *    Receives a key.
 *****/
void tn5250_macro_reckey (Tn5250Display *This, int key)
{
   int NumMacro ;

	if ((This->macro != NULL) && (This->macro->RState == 2) &&
	    (key != K_MEMO))
	{
	    NumMacro = This->macro->FctnKey - K_F1 ;
	    if (This->macro->TleBuff < MACRO_BUFSIZE)
	       This->macro->BuffM[NumMacro][This->macro->TleBuff++] = key ;
	}
}

/****f* lib5250/tn5250_macro_printstate
 * NAME
 *    tn5250_macro_printstate
 * SYNOPSIS
 *    tn5250_macro_printstate (This,key);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 * DESCRIPTION
 *    Returns a printable macro state (always 11 char long)
 *****/
char  * tn5250_macro_printstate (Tn5250Display *This)
{
   int NumKey ;

   PState[0] = 0 ;
   if (This->macro != NULL) 
      if (This->macro->RState > 0)    /* recording state */
      {
	 if (This->macro->RState == 1)
	    sprintf (PState,"R %04i     ",MACRO_BUFSIZE-This->macro->TleBuff) ;
	 else
	 {
	    NumKey = This->macro->FctnKey - K_F1 + 1 ;
	    sprintf (PState,"R %04i  F%02i",MACRO_BUFSIZE-This->macro->TleBuff,NumKey) ;
	 }
      }
      else			      /* execution state */
      if (This->macro->EState > 0)
      {
	 if (This->macro->EState == 1)
	    sprintf (PState,"P          ") ;
	 else
	 {
	    NumKey = This->macro->FctnKey - K_F1 + 1 ;
	    sprintf (PState,"P F%02i      ",NumKey) ;
	 }
      }
   return (PState) ;
}

/****f* lib5250/tn5250_macro_estate
 * NAME
 *    tn5250_macro_estate
 * SYNOPSIS
 *    tn5250_macro_estate (This);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 * DESCRIPTION
 *    Returns the current macro execution state
 *****/
char tn5250_macro_estate (Tn5250Display *This)
{
	if (This->macro != NULL)
		return (This->macro->EState) ;
	return 0;
}

/****f* lib5250/tn5250_macro_startexec
 * NAME
 *    tn5250_macro_startexec
 * SYNOPSIS
 *    tn5250_macro_startexec (This);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 * DESCRIPTION
 *    Starts a macro execution
 *****/
char tn5250_macro_startexec (Tn5250Display *This)
{
	if (This->macro != NULL)
	{
	    This->macro->EState = 1 ;
	    This->macro->FctnKey = 0 ;
	    return (1) ;
	}
	return (0) ;
}

/****f* lib5250/tn5250_macro_endexec
 * NAME
 *    tn5250_macro_endexec
 * SYNOPSIS
 *    tn5250_macro_endexec (This);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 * DESCRIPTION
 *    Ends a macro execution
 *****/
void tn5250_macro_endexec (Tn5250Display *This)
{
	if (This->macro != NULL)
	    This->macro->EState = 0 ;
}

/****f* lib5250/tn5250_macro_execfunct
 * NAME
 *    tn5250_macro_execfunct
 * SYNOPSIS
 *    tn5250_macro_execfunct (This,key);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 *    int 		   key     - function key received
 * DESCRIPTION
 *    Receives an execution function key.
 *****/
char tn5250_macro_execfunct (Tn5250Display *This, int key)
{
   int NumMacro ;

   if ((This->macro != NULL) && (This->macro->EState == 1))
   {
      This->macro->EState = 2 ;
      This->macro->FctnKey = key ;
      NumMacro = key - K_F1 ;
      if ((NumMacro >= 0) && (NumMacro < 24))
      {
         macro_clearmem (This->macro) ;
         macro_loadfile (This->macro) ;

	 This->macro->EState = 3 ;  /* Ok to run macro */
	 This->macro->TleBuff = 0 ;
	 return (1) ;
      }
   }
   return (0) ;
}

/****f* lib5250/tn5250_macro_getkey
 * NAME
 *    tn5250_macro_execfunct
 * SYNOPSIS
 *    tn5250_macro_execfunct (This,Last);
 * INPUTS
 *    Tn5250Display *      This       - Current display
 *    char  *		   Last     - to return a "toggle indicator off"
 * DESCRIPTION
 *    Sends a key to execute
 *****/
int tn5250_macro_getkey (Tn5250Display *This, char *Last)
{
   int NumMacro ;
   int key,nkey ;

   *Last = 0 ;
   if ((This->macro != NULL) && (This->macro->EState == 3))
   {
      NumMacro = This->macro->FctnKey  - K_F1 ;
      if (This->macro->BuffM[NumMacro] != NULL)
      {
	 key = This->macro->BuffM[NumMacro][This->macro->TleBuff] ;
	 if (key != 0)
	    nkey = This->macro->BuffM[NumMacro][++This->macro->TleBuff] ;
	 
	 if ((key == 0) || (nkey == 0))
	 {
	    *Last = 1 ;
	    This->macro->EState = 0 ;
	 }

	 return (key) ;
      }
      else
      {
	 This->macro->EState = 0 ;
	 *Last = 1 ;
	 return (0) ;
      }
   }
   return (0) ;
}


syntax highlighted by Code2HTML, v. 0.9.1