/*
 * @DEC_COPYRIGHT@
 */
/*
 * HISTORY
 * $Log: msg.c,v $
 * Revision 1.1  1995/03/10  21:55:36  bourquard
 * original OSF dxdiff sources
 *
 * Revision 1.1.2.2  1994/04/08  19:08:35  Susan_Ng
 * 	code for generic messaging facility
 * 	[1994/03/31  22:00:07  Susan_Ng]
 *
 * $EndLog$
 */
 /* #module msg "v1.0"
 * 
 * 	THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND  MAY
 * 	BE USED OR COPIED ONLY IN ACCORDANCE WITH THE TERMS
 * 	OF SUCH LICENSE.
 *
 * 	COPYRIGHT (c) 1989, 1990, 1991 BY DIGITAL EQUIPMENT
 * 	CORPORATION.  ALL RIGHTS RESERVED.
 *
 *
 * AUTHOR:
 *	DHIREN M. PATEL
 *
 * MODULE:
 *	msg
 *
 * ABSTRACT:
 *	This file contains code which provides a generic messaging
 *	facility. A feature of the facilty is to allow "parameterized"
 *	messages - i.e. messages that have content that is determined
 *	at run-time. A message can have MSG_MAX_PARAMETERS parameters
 *	and they are in the format "%n". For example, here is sample
 *	message:
 *		The %1 in Spain, falls mainly on the %2."
 *	A developer using this facility only needs to make two
 *	function calls to put up a message box. The first call would
 *	be to function Msg_messageparams() with the strings to replace
 *	the parameters. The second function call (Msg_putmessage) puts up a
 *	dialog box. This function dynamically allocates widgets and
 *	keeps track of them, thus allowing creating as many instances
 *	of message boxes as needed.
 *
 * RESTRICTIONS:
 *      The parameters in any message are assumed to be ASCII
 *      strings only. This is because the code converts the entire
 *      message to a simple string, replaces the parameters, and then
 *      converts it back to a compound string for use in Motif message
 *      dialogs. This WILL work with Asian strings but ONLY if
 *      the parameters are ASCII (not ISO-LATIN1). The following
 *      is a precise description of these terms:
 *
 *      Reference to ASCII character set means bytes (ie, eight bits)
 *      whose MSB is NOT set - i.e. the code ranges from 0x00 through 0x7F.
 *      The ASCII characters are those used in normal English
 *      language. ISO Latin-1 character includes ASCII and 8-bit
 *      characters which are used for European languages (i.e. chars
 *      with acute, circumflex, grave accent, etc.). The code ranges
 *      from 0x00 through 0xFF. A 8-bit Latin-1 character refers to a
 *      Latin-1 character with MSB set such as French, German
 *      character. Reference to a 7-bit character is a Latin-1
 *      character with MSB not set such as used in English.
 *
 *      A DEC-Kanji character is a 2-byte character with MSB of the first byte
 *      set. The MSB of the second byte can be set or reset. Hence it
 *      is easy to tell a Kanji/8-bit Lantin-1 character from an ASCII
 *      character simply by looking at the MSB. A kanji string may have
 *      ASCII characters embedded in it.
 *
 *      The routine msg_substituteparams() substitutes the "%n" tokens in a
 *      string with data in global storage. The "%n" strings are
 *      messages in uil files and must be restricted to ASCII strings.
 *      Their existence in any other character set will result in
 *      wrong parsing and subsequently unexpected results.
 *
 *      Conversion of a the final message character string to a compound
 *      string is done using DXmCvtFCtoCS which checks the MSB of each byte
 *      and correctly converts the text string to a compound string.
 *
 *      As long as %n's in strings are restricted to ASCII characters, code in
 *      this file will work with any character set (ISO Latin-1,
 *      kanji, etc.).
 *
 *      The logical improvements to these routines is to
 *      require that all strings in the UIL file be compound strings
 *      and parse out each individial segment and reconstruct
 *      the final compound string using existing segments and
 *      ISO-LATIN1 parameters.
 *
 *
 * REVISION HISTORY:
 *       7-mar-94 - dmp - module created
 ***************************************************************************/

/* Include files
*/
#include <Xm/Xm.h>
#include <Xm/MessageB.h>
#include <Mrm/MrmPublic.h>
#include <DXm/DECspecific.h>
#include <stdio.h>
#include <string.h>
#include <varargs.h>

#include "msg_proto.h"	/* Function proto's */

#define MSG_MAX_MESSAGE_PARAMETER	9

static char *message_parameters[MSG_MAX_MESSAGE_PARAMETER];

#define SUCCESS	1 
#define FAILURE	0


void Msg_messageparams(va_alist)
va_dcl
/*
 * FUNCTION:
 *	Saves the parameters to be used by msg_substituteparams call.
 * INPUTS:
 *	va_alist - variable list of string arguments with the first
 *		   argument being the number of following arguments.
 * RESULTS:
 *	None.
 * NOTES:
 *	Caller's strings are copied into a global.  Caller may free 
 *	immediately after call to msg_messageparam().  A zero argument 
 *	indicates that a parameter is not specified and will not be 
 *	modified, allowing for multiple calls to msg_messageparam.
 */
{
    va_list va;
    int i;
    int number_parameters;
    char *parameter;

    va_start(va);

    number_parameters = va_arg(va, int);
    for (i = 0 ; i < number_parameters ; i++)
       if (parameter = va_arg(va, char *))
          {
           if (message_parameters[i])
              free((caddr_t)message_parameters[i]);
	   message_parameters[i] = (char *)malloc(strlen(parameter) +1);
	   strcpy(message_parameters[i], parameter);
          }

    va_end(va);

}		/* end routine Msg_messageparams */



char *msg_substituteparams 
#ifdef MSG_PROTOTYPE_ON	/* prototypes */
	(Display *dpy, MrmHierarchy msg_hierarchy, char *uil_identifier)
#else			/* no prototypes */
	(dpy, msg_hierarchy, uil_identifier)
	Display 	*dpy;
	MrmHierarchy	msg_hierarchy;
	char		*uil_identifier;
#endif			/* end ifdef MSG_PROTOTYPE_ON */
/*
 * FUNCTION:
 *	dp:
 *	This routine fetches the char string corresponding to the char string
 *	"uil_identifier" and substitutes parameters in a message string, and
 *	returns that string. Parameter substitution is performed on strings 
 *	taken from char array "message_parameters" and they replace strings of 
 *	the form %n, where n is a number from 1 to 9.
 * INPUTS:
 *	 uil_identifier -- string specifying a uil index.
 * RESULTS:
 *	pointer to null-terminated string resulting from substitution.
 *	The caller is responsible for deallocating this string.
 * NOTES:
 */
{
 char *out_message;
 int parameter;
 int position;
 char param_string[3]; /* Current parameter identifier. %1 through %9  */
 char *temp_string;    /* Work buffer for parameterizing algorithm  */
 int temp_len, temp_newlen;
 char *pos_ptr;

    if(Get_UIL_Value (uil_identifier, &out_message, dpy, msg_hierarchy) 
		!= SUCCESS)
	return (char *)NULL;
                              
/* Scan the message string for strings %A thru %(A+MSG_MAX_MESSAGE_PARAMETER).
 * These are replaced by the strings in the message_parameters array.
 * temp_string is used to store the string past the %n param being replaced 
 * and appended later to out_message to construct the final message string.
 * temp_string must be reallocated if the string past %n param becomes longer
 * than the memory currently allocated for it. temp_len and temp_newlen are
 * used to manage the length of temp_string. [cde_bugs #4036]
 */
    if (strstr(out_message, "%") != NULL)
    {
	temp_len = strlen(out_message) + 1;
	temp_string = (char *)malloc(temp_len);
	strcpy(param_string, "%0");
	for (parameter = 0 ; parameter < MSG_MAX_MESSAGE_PARAMETER ; 
	     parameter++)
	{
	    param_string[1]++;
	    if (message_parameters[parameter])
	    {
		/* Replace all the parameters of this # in the message */
		while ((pos_ptr = strstr(out_message, param_string)) != NULL)
		{
/* pos_ptr now points to the first occurence of param_string */
		    position = pos_ptr - out_message;
/* We want to store the part of out_message which extends beyond 
 * param_string (%n). So allocate the memory needed to hold it. 
 */
		    temp_newlen = strlen(pos_ptr+2) + 1;
		    if (temp_newlen > temp_len)	/* Need to realloc? */
		    {		                /* Yes */
			temp_string = (char *)realloc(temp_string, temp_newlen);
			temp_len = temp_newlen;
		    }
		    strcpy(temp_string, (pos_ptr+2));
		    out_message = (char *)realloc(out_message, 
					(position + 
					strlen(message_parameters[parameter]) +
					strlen(temp_string) + 1));
		    strcpy(&out_message[position],
			   message_parameters[parameter]);
		    strcat(out_message, temp_string);
		}
	    }
	}

/* Clear the global array of message parameters.
 */
	free((char *)temp_string);
	for (parameter = 0 ; parameter < MSG_MAX_MESSAGE_PARAMETER ; 
	     parameter++)
	    if (message_parameters[parameter])
	    {
		free((char *)message_parameters[parameter]);
		message_parameters[parameter] = (char *)0;
	    }
    }
    
    return (out_message);
}		 /* end routine msg_substituteparams */


static int Get_UIL_Value 
#ifdef MSG_PROTOTYPE_ON	/* prototypes */
	(char *uil_index, char **buffer, Display *dpy, 
					MrmHierarchy msg_hierarchy)
#else			/* no prototypes */
	(uil_index, buffer, dpy, msg_hierarchy)
	char		*uil_index;
	char		**buffer;
	Display 	*dpy;
	MrmHierarchy	msg_hierarchy;
#endif    		/* end ifdef MSG_PROTOTYPE_ON */
/*
 * Function:
 *	Get the value in the UID file indexed by the index string. The routine
 *	allocates the required memory (*buffer). The user must free this string
 *	when done using it.
 *
 * Inputs:
 *	index string, pointer to a buffer, display pointer, hierarchy id.
 *
 * Outputs:
 *	Success or failure code.
 *
 * Notes:
 */
{
    int ret_status = SUCCESS;
    MrmCode data_type;
    XtPointer value;
    char  *tmp;
    long  byte_count;
    long  status;

    if (MrmFetchLiteral (msg_hierarchy, uil_index, dpy, &value,
                         &data_type) != MrmSUCCESS) 
    {
	switch(MrmFetchLiteral (msg_hierarchy, uil_index, 
						dpy, &value, &data_type))
	{
	    case MrmBAD_HIERARCHY:
		printf("Msg: the hierarchy id is invalid.\n");
		break;
	    case MrmNOT_FOUND:
		printf("Msg: the literal was not found in the UIL file.\n");
		break;
	    case MrmWRONG_TYPE:
	    /* dp: the types of values that can be stored in uid files
	     *	   are defined in <Mrm/MrmPublic.h>.
	     */
		printf("Msg: The caller tried to fetch a literal not \
					supported by MrmFetchLiteral.\n");
		break;
	    case MrmFAILURE: 
		printf("Msg: MrmFetchLiteral failed.\n");
		break;
	}
	ret_status = FAILURE;
    }		/* end if MrmFetchLiteral failed */
    else	/* MrmFetchLiteral Succeeded */
    {
	if (data_type == MrmRtypeChar8)		/* a null-terminated string */
	{
	    *buffer = (char *)malloc(strlen(value) + 1);
	    strcpy (*buffer, value);
	    free ((char*)value);
	    value = NULL;
	}
	else if (data_type == MrmRtypeCString)	/* a compound string */
	{
/* dp:	The value returned by DXmCvtCStoFC (currently text format)
 *	should be freed only using XtFree.
 *	Since this is supposed to be a generic routine, we don't
 *	know how the user is going to free the string. Hence we copy
 *	the string a a char * and free "tmp" right here.
 */
	    tmp = (char *) DXmCvtCStoFC((XmString)value, &byte_count, &status);
	    if (status != DXmCvtStatusOK)
	    {
		ret_status = FAILURE;
		return(ret_status);
	    }
	    *buffer = (char *)malloc(byte_count + 1);
	    strcpy (*buffer, tmp);
	    XtFree((char *)tmp);
	    free ((char*)value);
	    value = NULL;
	}
    }
    return (ret_status);
}		/* end routine Get_UIL_Value */


void
dialog_ok_cb
#ifdef MSG_PROTOTYPE_ON	/* prototypes */
	(Widget w, XtPointer client_data)
#else			/* no prototypes */
	(w, client_data)
	Widget w;
	XtPointer client_data;
#endif			/* end ifdef MSG_PROTOTYPE_ON */
/*
 * Function:
 *	Callback for the dialogs put up. It destroys the dialog and 
 *	decreases the number of dialogs a user has caused by one.
 *
 * Inputs:
 *	The client data is a integer pointer in order to facilitate
 *	reducing the number of existing dialogs consistently with
 *	their destruction.
 *
 * Outputs:
 *
 * Notes:
 */
{
  int *number = (int *)client_data;

  (*number)--;	
  XtDestroyWidget(w);
  w = (Widget)NULL;
}

Cardinal Msg_putmessage
#ifdef MSG_PROTOTYPE_ON	/* prototypes */
	(Widget parent, char *uil_index, Display *dpy, 
					MrmHierarchy msg_hierarchy)
#else			/* no prototypes */
	(parent, uil_index, dpy, msg_hierarchy)
	Widget		parent;
	char		*uil_index;
	Display 	*dpy;
	MrmHierarchy	msg_hierarchy;
#endif			/* end ifdef MSG_PROTOTYPE_ON */
/*
 * FUNCTION:
 *	This routine puts up a dialog box with the the message string
 *	returned by function msg_substituteparams ().
 *	The dialog boxes are allocated dynamically as needed. Therefore,
 *	a developer can put up as many as needed. The callback destroys
 *	the widgets.
 * INPUTS:
 *	parent		-- parent of which the dialog box will be a child
 *	uil_index	-- string specifying a uil index.
 *	dpy		-- pointer to the display
 *	MrmHierarchy	-- hierarchy for the application
 * RESULTS:
 *	MrmFAILURE if msg_substitureparams returned a NULL string OR
 *		   if could not fetch the information dialog from hierarchy.
 *	MrmSUCCESS if everything went fine.
 * NOTES:
 */
{
    char		*dialog_disp_string;
    XmString		message;
    static Widget	*dialog_list;
    static int		list_size = 0;
    static int		num_of_dialogs = -1;
    MrmType		class;
    Arg			args[1];
    long		byte_count;
    long		status;

    num_of_dialogs++;

    if (!dialog_list)
    {
	dialog_list = (Widget *)malloc(sizeof(Widget) * 10);
	list_size = 10;
    }
    else if (num_of_dialogs > list_size)
    {
	list_size += 10;
	dialog_list = (Widget *)realloc(dialog_list, 
					sizeof(Widget)*(list_size));
    }

    if((dialog_disp_string = msg_substituteparams(dpy, 
					msg_hierarchy, uil_index)) == NULL)
    {
	fprintf(stderr,"can't fetch string for uil_index\n");
	return;
    }


/*  we use DXmCvtFCtoCS takes into account whether MSB of the byte
 *  is set or not and hence when this code is used with kanji,
 *  proper conversion to a XmString is done and no data is lost.
 */
    message = DXmCvtFCtoCS(dialog_disp_string, &byte_count, &status);
    if(MrmFetchWidgetOverride(msg_hierarchy, "message_box", parent, 
			NULL, NULL, 0, 
			&(dialog_list[num_of_dialogs]),  &class)
			!= MrmSUCCESS)
    {
	fprintf(stderr,"can't fetch dialog box\n");
	return;
    }

    XtAddCallback(dialog_list[num_of_dialogs], XmNokCallback, 
				(XtCallbackProc)dialog_ok_cb, &num_of_dialogs);
    XtSetArg(args[0], XmNmessageString, message);
    XtSetValues(dialog_list[num_of_dialogs], args, 1);
    XmStringFree(message);
    XtFree((char *) dialog_disp_string);
    XtUnmanageChild(XmMessageBoxGetChild(dialog_list[num_of_dialogs],
						XmDIALOG_CANCEL_BUTTON));
    XtUnmanageChild(XmMessageBoxGetChild(dialog_list[num_of_dialogs],
						XmDIALOG_HELP_BUTTON));
    XtManageChild(dialog_list[num_of_dialogs]);
    XtPopup(XtParent(dialog_list[num_of_dialogs]), XtGrabNone);
    return;
}		/* end routine Msg_putmessage */
