/*****************************************************************************
 *
 *  xdbx - X Window System interface to the dbx debugger
 *
 *  Copyright 1989 The University of Texas at Austin
 *  Copyright 1990 Microelectronics and Computer Technology Corporation
 *
 *  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 and that
 *  both that copyright notice and this permission notice appear in
 *  supporting documentation, and that the name of The University of Texas
 *  and Microelectronics and Computer Technology Corporation (MCC) not be 
 *  used in advertising or publicity pertaining to distribution of
 *  the software without specific, written prior permission.  The
 *  University of Texas and MCC makes no representations about the 
 *  suitability of this software for any purpose.  It is provided "as is" 
 *  without express or implied warranty.
 *
 *  THE UNIVERSITY OF TEXAS AND MCC DISCLAIMS ALL WARRANTIES WITH REGARD TO
 *  THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 *  FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF TEXAS OR MCC BE LIABLE FOR
 *  ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 *  RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 *  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 *  CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *  Author:  	Po Cheung
 *  Created:   	March 10, 1989
 * 
 *****************************************************************************
 * 
 *  xxgdb - X Window System interface to the gdb debugger
 *  
 * 	Copyright 1990,1993 Thomson Consumer Electronics, Inc.
 *  
 *  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 and that
 *  both that copyright notice and this permission notice appear in
 *  supporting documentation, and that the name of Thomson Consumer
 *  Electronics (TCE) not be used in advertising or publicity pertaining
 *  to distribution of the software without specific, written prior
 *  permission.  TCE makes no representations about the suitability of
 *  this software for any purpose.  It is provided "as is" without express
 *  or implied warranty.
 *
 *  TCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 *  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
 *  SHALL TCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES
 *  OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 *  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 *  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 *  SOFTWARE.
 *
 *  Adaptation to GDB:  Pierre Willard
 *  XXGDB Created:   	December, 1990
 *
 *****************************************************************************/

/*  dataDpy.c:
 *
 *   Provide graphical display of C pointers and structures.
 *
 *   BuildLinePos():	Construct an array indexing the character position of 
 *			each line.
 *   PositionToLine():	Return the character position of a given line.   
 *   SelectPointer():	Action proc for double click on a pointer value,
 *   CreateDataPopup():	Create a popup to display the object pointed to by a
 *			pointer.
 *   UpdateDataPopup(): Update an unused popupshell to display data.
 *   AppendList():	Append a popup to the list.
 *   DeleteList():	Delete a popup from the list.
 *   pop_down():	pop down the popup and free storage.
 *   DestroyDataPopup():event handler for destroying a popup, call DeleteList()
 *			and pop_down() (CRL mod 25)
 *   MovePopup():	Position the popup.
 *   print_handler():	Action handler for displaying pointers and structures.
 */

/*
 * 01FEB94: bugs fixed (klamer)
 *		xxgdb does not allow the graphically displaying of members of parents
 *   		from a class.
 *		xxgdb does not allow the graphically displaying of data through a
 *		   reference.
 */

#include <string.h>
#include 	"global.h"
#include 	"regex.h"
#include 	"datadpy.h"

#define		MAXLEVELS	20	/* max level of indentation */
#ifdef GDB
#define		INDENT		2	/* # of spaces for each indentation */
#else
#define		INDENT		8	/* # of spaces for each indentation */
#endif /* GDB */
#define 	EMPTY           0
#define 	UNUSED          1
#define 	USED            2
#define 	LEFT_MARGIN	10
#define 	SCROLLBAR_WIDTH	15


static DataDpyRec	**dataDpyTable;
static int		dataDpyTableSize = 0;
static DataDpyRec	*Parent = NULL;
static DataDpyList	*TopParentList = NULL;
static int		font_height, font_width;

#ifdef OBSOLETE
static void		DestroyDataPopup();
#else
/* CRL mod 25 4/12/91 GWC - changed label widget to command widget in popups */
static void		DestroyDataCallback();
#endif

/* 
 *  Build an array which gives the starting text position of each line.
 *  Very similar to the routine in source.c.
 */
static void BuildLinePos(dataDpy)
DataDpyRec *dataDpy;
{
    char *p;
    int	 line, nlines;
    int	 max=0;

    nlines = MAX(1, dataDpy->buflen/CHARS_PER_LINE);
    dataDpy->linepos = (XawTextPosition *)
                    XtMalloc ((nlines+2) * sizeof(XawTextPosition));
    p = dataDpy->buf;
    line = 0;
    dataDpy->linepos[line++] = 0;
    dataDpy->linepos[line++] = 0;
    while (*p) {
        if (*p++ == '\n') {
            if (line == nlines) {       /* buffer full, need more memory */
                dataDpy->linepos = (XawTextPosition *)XtRealloc( (void*)dataDpy->linepos,
                          (nlines + ADD_LINES) * sizeof(XawTextPosition));
                nlines += ADD_LINES;
            }
            dataDpy->linepos[line] = p - dataDpy->buf;
	    AssignMax(max, dataDpy->linepos[line] - dataDpy->linepos[line-1]);
	    line++;
        }
    }
    dataDpy->numlines = line - 2;
    dataDpy->maxLineLength = max;
    /* shrink to min size */
    dataDpy->linepos = (XawTextPosition *) XtRealloc     
                        ((void*)dataDpy->linepos, line * sizeof(XawTextPosition));
}

/*
 *  Return the line number for the specified text position.
 */
static int PositionToLine(dataDpy, pos)
DataDpyRec *dataDpy;
XawTextPosition pos;
{
    int	 line;

    if (dataDpy && pos >= 0) {
    	for (line = 1; pos >= dataDpy->linepos[line]; line++);
    	return (line-1);
    }
    else
	return (0);
}

/* ARGSUSED */
/*  
 *  Called by double click of pointer button.
 *  If the selected text is a valid pointer, this routine parses the data 
 *  output to obtain the full qualified name of the pointer, and asks
 *  dbx to print the value of the object the pointer is pointing to.
 */
static void SelectPointer(w, event, params, num_params)
    Widget w;
    XEvent *event;
    String *params;
    Cardinal *num_params;
{
#ifndef GDB
    struct re_registers regs;
	char *field[MAXLEVELS];
	int line, n, r, level, newlevel;
	char		name[LINESIZ];
#endif
    XawTextPosition 	left, right;
    char		*selection, *p;
    DataDpyRec		*dataDpy;
    int			fromLine;
    int			i, nbytes;
    char		command[LINESIZ];

    /* Find out which data display output does the selection belong to */
    dataDpy = NULL;
    for (i=0; dataDpyTable[i]; i++)
	if ((Widget) w == (Widget) dataDpyTable[i]->dataDpyWindow) {
	    dataDpy = dataDpyTable[i];
	    Parent = dataDpy;
	    break;
	}
    if (!dataDpy) return;

    /* Get the selection and check if it's a pointer value, 0x???? */
    selection = XFetchBytes(display, &nbytes);
    if (re_match(dataPattern[D_POINTER].buf, selection, strlen(selection), 0, 0)
	< 0) {
	Parent = NULL;
	return;
    }

    /* Parse the output to get the fully qualified name of the pointer */
    XawTextGetSelectionPos(w, &left, &right);
    fromLine = PositionToLine(dataDpy, left);
    p = dataDpy->buf + dataDpy->linepos[fromLine];

#ifdef GDB	/* (PW)  we now use a new parser which should work better,
	in particular for arrays */
	{
	char *parse_gdb_print();
	char *newcommand;
	
    newcommand = parse_gdb_print(command, dataDpy->buf, dataDpy->buf + left );
    if (*newcommand)
    	{
		if (strchr(selection, '@'))
		{	
			char	*p = strchr(newcommand, '*');
			if (p != 0)
				*p = ' ';
		}
		PopupMode = True;
    	query_gdb(newcommand, PARSE_ON | ECHO_OFF | FILTER_OFF);
    	}
    }
#else	/* not GDB */

    if (re_match(dataPattern[D_FIELD].buf, p, strlen(p), 0, &regs) >= 0) {
	r = dataPattern[D_FIELD].reg_token[TK_POINTER];
	if (strncmp(selection, p+regs.start[r], regs.end[r]-regs.start[r]))
	    return;
	r = dataPattern[D_FIELD].reg_token[TK_INDENT];
	level = regs.end[r]/INDENT;
	field[level+1] = NULL;

	r = dataPattern[D_FIELD].reg_token[TK_FIELD];
	n = regs.end[r] - regs.start[r];
	field[level] = (char *) XtMalloc ((n+1) * sizeof(char));
	strncpy(field[level], p+regs.start[r], n);
	field[level][n] = '\0';

	for (line = fromLine-1; line > 0; line--) {
	    p = dataDpy->buf + dataDpy->linepos[line];
	    if (re_match(dataPattern[D_STRUCT].buf, p, strlen(p), 0, &regs)>=0){
		r = dataPattern[D_STRUCT].reg_token[TK_INDENT];
		newlevel = regs.end[r]/INDENT;
		if (newlevel == level-1) {
		    level--;
		    r = dataPattern[D_STRUCT].reg_token[TK_FIELD];
		    n = regs.end[r] - regs.start[r];
		    field[level] = (char *) XtMalloc ((n+1) * sizeof(char));
		    strncpy(field[level], p+regs.start[r], n);
		    field[level][n] = '\0';
		}
	    }
	}
	if (*field[0] == '*' && field[1])
	    sprintf(name, "(%s)", field[0]+1);
	else
	    strcpy(name, field[0]);
	    
	for (i=1; field[i]; i++) {
	    strcat(name, ".");
	    strcat(name, field[i]);
	}
	sprintf(command, "print *(%s)\n", name);
	PopupMode = True;
#ifdef GDB
    query_gdb(command, PARSE_ON | ECHO_OFF | FILTER_OFF);
#else
	query_dbx(command);
#endif	/* GDB */
    }
#endif /* GDB */
}

    
/*
 *  Create a data display with a label.
 *  The popupshell has a form widget which consists of a label and a text
 *  widget.
 */
static void CreateDataPopup(dataDpy, label)
DataDpyRec *dataDpy;
char	   *label;
{
    Arg         args[MAXARGS];
    Cardinal    n;
    Dimension	dataDpyHeight, dataDpyWidth;
    XFontStruct	*text_font;

    static XtActionsRec datadpy_actions[] = {
        {"SelectPointer", (XtActionProc) SelectPointer},
        {NULL, NULL}
    };

    static String translations = "#override \n\
        <Btn1Down>:     SelectStart() SelectWord() SelectPointer() \n\
        <Btn1Up>:       SelectEnd() \n\
    ";

    n = 0;
    dataDpy->popupshell = XtCreatePopupShell("Data Popup", 
	transientShellWidgetClass, toplevel, args, n);

    n = 0;
    XtSetArg(args[n], XtNdefaultDistance, 0);                           n++;
    dataDpy->popup = XtCreateManagedWidget("popup", formWidgetClass,
	dataDpy->popupshell, args, n);

    /* Create the label */
    n = 0;
    XtSetArg(args[n], XtNtop, (XtArgVal) XawChainTop);                  n++;
    XtSetArg(args[n], XtNbottom, (XtArgVal) XawChainTop);               n++;
    XtSetArg(args[n], XtNright, (XtArgVal) XawChainRight);              n++;
    XtSetArg(args[n], XtNleft, (XtArgVal) XawChainLeft);               	n++;
    XtSetArg(args[n], XtNlabel, (XtArgVal) label);                     	n++;
    XtSetArg(args[n], XtNresize, (XtArgVal) False);                    	n++;
    XtSetArg(args[n], XtNjustify, (XtArgVal) XtJustifyCenter);         	n++;

#ifdef OBSOLETE
    dataDpy->label = XtCreateManagedWidget("label", labelWidgetClass, 
	dataDpy->popup, args, n);
/*	GWC says it is better to use ButtonReleaseMask instead of ButtonPressMask.*/
	XtAddEventHandler(dataDpy->label, (EventMask) ButtonPressMask, False, 
	DestroyDataPopup, dataDpy);
#else
/* CRL mod 25 4/12/91 GWC - changed label widget to command widget in
popups */
	dataDpy->label = XtCreateManagedWidget("command", commandWidgetClass, 
  		dataDpy->popup, args, n);
    XtAddCallback(dataDpy->label, XtNcallback, DestroyDataCallback, dataDpy);
#endif

    /* Create the text window */
    n = 0;
    XtSetArg(args[n], XtNfromVert, (XtArgVal) dataDpy->label);          n++;
    XtSetArg(args[n], XtNtop, (XtArgVal) XawChainTop);                  n++;
    XtSetArg(args[n], XtNbottom, (XtArgVal) XawChainBottom);            n++;
    XtSetArg(args[n], XtNright, (XtArgVal) XawChainRight);              n++;
    XtSetArg(args[n], XtNleft, (XtArgVal) XawChainLeft);               	n++;

    XtSetArg(args[n], XtNleftMargin, (XtArgVal) LEFT_MARGIN); 		n++;
    XtSetArg(args[n], XtNuseStringInPlace, (XtArgVal) True); 		n++;
    XtSetArg(args[n], XtNstring, (XtArgVal) dataDpy->buf);            	n++;
    XtSetArg(args[n], XtNlength, (XtArgVal) dataDpy->buflen);  		n++;
    XtSetArg(args[n], XtNeditType, (XtArgVal) XawtextRead);             n++;
    XtSetArg(args[n], XtNscrollHorizontal, XawtextScrollWhenNeeded);	n++;
    XtSetArg(args[n], XtNscrollVertical, XawtextScrollWhenNeeded);	n++;
    XtSetArg(args[n], XtNtranslations, XtParseTranslationTable(translations));
                                                                        n++;
    dataDpy->dataDpyWindow = XtCreateManagedWidget("dataDpyWindow", 
	asciiTextWidgetClass, dataDpy->popup, args, n);
    XtAppAddActions(app_context, datadpy_actions, XtNumber(datadpy_actions));

    /* Get the text font */
    n = 0;
    XtSetArg(args[n], XtNfont, &text_font);                  		n++;
    XtGetValues(dataDpy->dataDpyWindow, args, n);

    /* Estimate the size of the text widget, dataDpyWindow, with the number
       of lines and the maximum length of a line.  Assume fixed font width.
    */
    font_height = text_font->ascent + text_font->descent;
    font_width = text_font->max_bounds.width;
    dataDpyHeight = dataDpy->numlines * font_height + 5;
    dataDpyWidth = dataDpy->maxLineLength * font_width + LEFT_MARGIN;
    if (dataDpyHeight > app_resources.dataDpyMaxHeight)
	dataDpyWidth += SCROLLBAR_WIDTH;
	
#if 1	/*(PW)17DEC90 : bug ! */
#define 	SCROLLBAR_HEIGHT	15
    if (dataDpyWidth > app_resources.dataDpyMaxWidth)
	dataDpyHeight += SCROLLBAR_HEIGHT;
#endif

    AssignMin(dataDpyHeight, app_resources.dataDpyMaxHeight);
    AssignMin(dataDpyWidth, app_resources.dataDpyMaxWidth);
		
    n = 0;
    XtSetArg(args[n], XtNheight, (XtArgVal) dataDpyHeight); 		n++;
    XtSetArg(args[n], XtNwidth, (XtArgVal) dataDpyWidth); 		n++;
    XtSetValues(dataDpy->dataDpyWindow, args, n);

    n = 0;
    XtSetArg(args[n], XtNwidth, (XtArgVal) dataDpyWidth); 		n++;
    XtSetValues(dataDpy->label, args, n);
}

/*
 *  Instead of creating a new popupshell, this routine uses an already
 *  existing popupshell for data display.
 *  It changes the label, calculates the size of the popupshell,
 *  and sets the source of the text window to that of the new data.
 */
static void UpdateDataPopup(dataDpy, label)
DataDpyRec *dataDpy;
char	   *label;
{
    Arg args[MAXARGS];
    Cardinal n;
    Dimension	popupHeight, popupWidth, dataDpyHeight, dataDpyWidth,
		labelHeight, labelBorderWidth, dataDpyBorderWidth;

    /* Update the label */
    n = 0;
    XtSetArg(args[n], XtNlabel, (XtArgVal) label);                     	n++;
    XtSetValues(dataDpy->label, args, n);

    /* Calculate the size of popupshell */
    dataDpyHeight = dataDpy->numlines * font_height + 5;
    dataDpyWidth = dataDpy->maxLineLength * font_width + 2*10;
    
#if 1	/*(PW)18DEC90 : bug ! */
    if (dataDpyHeight > app_resources.dataDpyMaxHeight)
	dataDpyWidth += SCROLLBAR_WIDTH;
	
    if (dataDpyWidth > app_resources.dataDpyMaxWidth)
	dataDpyHeight += SCROLLBAR_HEIGHT;
#endif

    AssignMin(dataDpyHeight, app_resources.dataDpyMaxHeight);
    AssignMin(dataDpyWidth, app_resources.dataDpyMaxWidth);

    n = 0;
    XtSetArg(args[n], XtNheight, (XtArgVal) &labelHeight); 		n++;
    XtSetArg(args[n], XtNborderWidth, (XtArgVal) &labelBorderWidth); 	n++;
    XtGetValues(dataDpy->label, args, n);
    n = 0;
    XtSetArg(args[n], XtNborderWidth, (XtArgVal) &dataDpyBorderWidth); 	n++;
    XtGetValues(dataDpy->dataDpyWindow, args, n);

    popupHeight = dataDpyHeight + labelHeight + 2*labelBorderWidth +
		  2*dataDpyBorderWidth;
    popupWidth = dataDpyWidth;
    
    n = 0;
    XtSetArg(args[n], XtNheight, (XtArgVal) popupHeight); 		n++;
    XtSetArg(args[n], XtNwidth, (XtArgVal) popupWidth);			n++;
    XtSetValues(dataDpy->popupshell, args, n);

    /* Set the text source */
    n = 0;
    XtSetArg(args[n], XtNstring, (XtArgVal) dataDpy->buf);		n++;
    XtSetArg(args[n], XtNlength, (XtArgVal) dataDpy->buflen);           n++;
    XawTextSetSource(dataDpy->dataDpyWindow, 
		     XtCreateWidget("textsrc", asciiSrcObjectClass, 
				    dataDpy->dataDpyWindow, args, n),
		     0);
}

/*
 *  Append dataDpy to a DataDpyList pointed to by head.
 */
static void AppendList(head, dataDpy)
DataDpyList **head;
DataDpyRec  *dataDpy;
{
    DataDpyList	*p, *q, *r;

    p = (DataDpyList *) XtNew (DataDpyList);
    p->dataDpy = dataDpy;
    p->next = NULL;
    q = *head;
    if (!q)
	*head = p;
    else {
	while ((r = q->next))
	    q = r;
	q->next = p;
    }
}

/*
 *  Removes a dataDpy from its parent's list of children.
 */
static void DeleteList(head, dataDpy)
DataDpyList **head;
DataDpyRec  *dataDpy;
{
    DataDpyList *p, *q;

    if ((p = *head)) {
	if (p->dataDpy == dataDpy)
	    *head = p->next;
	else {
	    for (q = p->next; q && q->dataDpy != dataDpy;) {
		p = q; 
		q = p->next;
	    }
	    if (q) p->next = q->next;
	}
    }
}
	    
/*
 *  Pop down a dataDpy and all its descendants, freeing storage and
 *  reinitializing fields.
 */
static void pop_down(dataDpy)
DataDpyRec *dataDpy;
{
    DataDpyList *p, *q;

    XtPopdown(dataDpy->popupshell);
    XtFree((void*)dataDpy->linepos);
    XtFree(dataDpy->buf);
    dataDpy->buf = NULL;
    dataDpy->buflen = 0;
    dataDpy->linepos = NULL;
    dataDpy->state = UNUSED; 
    dataDpy->parent = NULL; 
    for (p = dataDpy->childlist; p;) {
	pop_down(p->dataDpy);
	q = p;
	p = p->next;
	XtFree((void*)q);
    }
    dataDpy->childlist = NULL;
}

/*
 *  Invoked by a ButtonPress event on the label of a data display to
 *  pop down itself and its descendants.
 */
/* ARGSUSED */

#ifdef OBSOLETE
static void DestroyDataPopup(w, dataDpy, event)
    Widget w;
    DataDpyRec *dataDpy;
    XEvent *event;
#else
/* CRL mod 25 4/12/91 GWC - changed label widget to command widget */
static void DestroyDataCallback(w, dataDpy, call_data)
    Widget w;
    DataDpyRec *dataDpy;
    caddr_t call_data;
#endif
{
    if (!dataDpy->parent)
    	DeleteList(&TopParentList, dataDpy);
    else
    	DeleteList(&dataDpy->parent->childlist, dataDpy);
    pop_down(dataDpy);
}

/*
 *  Position the data display on the screen to reflect the parent-child
 *  relationship.
 */
static void MovePopup(dataDpy)
DataDpyRec *dataDpy;
{
    Arg         args[MAXARGS];
    Cardinal    n;
    Screen	*screen;
    int		popupHeight, popupWidth, screenHeight, screenWidth;
    Position	x, y;
    Dimension	dataDpyWidth, dataDpyHeight, dataDpyBorderWidth, 
		labelHeight, labelBorderWidth, width, height, borderWidth;
    DataDpyList	*p, *q;

    Parent = NULL;
    if (!dataDpy->parent)
	p = TopParentList;
    else
	p = dataDpy->parent->childlist;

    /*  Look for its previous sibling  */
    for (q = p->next; q && q->dataDpy != dataDpy;) {
	p = q;
	q = q->next;
    }
    /*  If a sibling exists, place the new popup right next to it  */
    if (q) {
	n = 0;
	XtSetArg(args[n], XtNwidth, (XtArgVal) &width);             n++;
	XtSetArg(args[n], XtNborderWidth, (XtArgVal) &borderWidth);	n++;
	XtGetValues(p->dataDpy->popupshell, args, n);
	XtTranslateCoords(p->dataDpy->popupshell, 0, 0, &x, &y);
	x += width;
	y -= borderWidth;
    }
    else {	/* no siblings */
	/*  this is the very first popup  */
	if (!dataDpy->parent) {
	    x = 0;
	    y = 0;
	}
	/*  place it under its parent  */
	else {
	    n = 0;
	    XtSetArg(args[n], XtNheight, (XtArgVal) &height);		n++;
	    XtGetValues(dataDpy->parent->popupshell, args, n);
	    XtTranslateCoords(dataDpy->parent->popupshell, 30, (Position)height,
		 &x, &y);
	}
    }

    /* Make sure the popup does not go outside of the screen */
    n = 0;
    XtSetArg(args[n], XtNwidth, (XtArgVal) &dataDpyWidth);             	n++;
    XtSetArg(args[n], XtNheight, (XtArgVal) &dataDpyHeight);           	n++;
    XtSetArg(args[n], XtNborderWidth, (XtArgVal) &dataDpyBorderWidth); 	n++;
    XtGetValues(dataDpy->dataDpyWindow, args, n);

    n = 0;
    XtSetArg(args[n], XtNheight, (XtArgVal) &labelHeight); 		n++;
    XtSetArg(args[n], XtNborderWidth, (XtArgVal) &labelBorderWidth); 	n++;
    XtGetValues(dataDpy->label, args, n);

    popupHeight = dataDpyHeight + labelHeight + 2*labelBorderWidth +
		  2*dataDpyBorderWidth;
    popupWidth = dataDpyWidth;

    screen = XtScreen(toplevel);
    screenHeight = XHeightOfScreen(screen);
    screenWidth = XWidthOfScreen(screen);

    if (x + popupWidth > screenWidth && y + popupHeight > screenHeight) {
	x = screenWidth - popupWidth;
	y = screenHeight - popupHeight;
    }
    else if (x + popupWidth > screenWidth)
	x = screenWidth - popupWidth;
    else if (y + popupHeight > screenHeight)
	y = screenHeight - popupHeight;

    n = 0;
    XtSetArg(args[n], XtNx, x);						n++;
    XtSetArg(args[n], XtNy, y);						n++;
    XtSetValues(dataDpy->popupshell, args, n);
}

/*
 *  Handler procedure called by parse().
 *  The main function to popup a data display.
 */
void print_handler(output)
char *output;
{
    DataDpyRec 	*dataDpy;
    int		i, j;

    if (!output) return;
    if (!PopupMode) return;
    PopupMode = False;
    XDefineCursor(display, XtWindow(toplevel), watch);
    if (Parent)
    	XDefineCursor(display, XtWindow(Parent->dataDpyWindow), watch);
    UpdateMessageWindow("Click the label to pop down the data popup",NULL);

    /* Searches the table for an unused or empty slot */
    /* (PW)17OCT91 : test i < dataDpyTableSize first (else segment violation)*/
    for (i=0; dataDpyTable && i < dataDpyTableSize && dataDpyTable[i]
    	&& dataDpyTable[i]->state == USED ; i++);
    if (i == dataDpyTableSize) {			/* Table full */
	dataDpyTableSize += ADD_SIZE;
	dataDpyTable = (DataDpyRec **) XtRealloc ((void*)dataDpyTable, 
			   dataDpyTableSize * sizeof(DataDpyRec *));
	for (j=i; j<dataDpyTableSize; j++)
	    dataDpyTable[j] = NULL;
    }

    /*  Empty slot found, allocate a data structure and initializes some
	of the fields.  */
    if (dataDpyTable[i] == NULL) {
	dataDpyTable[i] = (DataDpyRec *) XtMalloc (sizeof(DataDpyRec));
	dataDpyTable[i]->state = EMPTY;
	dataDpyTable[i]->parent = NULL;
	dataDpyTable[i]->childlist = NULL;
    }

    dataDpy = dataDpyTable[i];
    dataDpy->id = i;					/* not needed */
    dataDpy->buf = XtNewString(output);
    dataDpy->buflen = strlen(output);
    BuildLinePos(dataDpy);

    if (dataDpy->state == EMPTY)
	CreateDataPopup(dataDpy, Token.mesg);
    else if (dataDpy->state == UNUSED)
	UpdateDataPopup(dataDpy, Token.mesg);

    dataDpy->state = USED;				/* mark it used */
    if ((dataDpy->parent = Parent))
    	AppendList(&Parent->childlist, dataDpy);
    else
    	AppendList(&TopParentList, dataDpy);

    MovePopup(dataDpy);
    XtPopup(dataDpy->popupshell, XtGrabNone);
    if (dataDpy->parent)
    	XUndefineCursor(display, XtWindow(dataDpy->parent->dataDpyWindow));
    XUndefineCursor(display, XtWindow(toplevel));
}


#ifdef GDB
#define GOODCHARNAME(c)							\
			(	(((c) >='a') &&  ((c) <= 'z'))	\
			||	(((c) >='A') &&  ((c) <= 'Z'))	\
			||	(((c) >='0') &&  ((c) <= '9'))	\
			||	((c) == '_')					\
			||	((c) == '$')					\
			)

static char *result;		/* start of result buffer */
static int result_index;	/* current index in result buffer */
static char *start_txt;		/* pointer 1st char of output to parse */
static char *curr_txt;		/* current pointer in output to parse */

/*--------------------------------------------------------------------------+
|																			|
|	Store a character into the buffer.										|
|																			|
|	Note that characters are added to the buffer RIGHT TO LEFT !			|
|	This is because we parse the output from right to left.					|
|																			|
|	If the result buffer is full, we set result to "".						|
|																			|
+--------------------------------------------------------------------------*/
static void add_char(c)
char c;
{
	if (result_index == 0)	/* buffer full */
		{
		*result = 0;
		return;
		}

        if ((c == '.') && (result[result_index] == '.'))
                return;         /* To prevent $1..name for pointers in 
				   g++ parents */
	
	result_index--;
	*(result+result_index) = c;
}

/*--------------------------------------------------------------------------+
|																			|
|	Store a string into the buffer.											|
|																			|
+--------------------------------------------------------------------------*/
static void add_string(s)
char *s;
{
int nbchar;

	nbchar = strlen(s);
	
	/* copy number from last digit */
	
	while (nbchar > 0)
		add_char(*(s + (--nbchar)));
}

/*--------------------------------------------------------------------------+
|																			|
|	Store a number into the buffer.											|
|																			|
+--------------------------------------------------------------------------*/
static void add_num(number)
int number;
{
char tmpnum[128];

	sprintf(tmpnum,"%d",number);
	add_string(tmpnum);
}

/*--------------------------------------------------------------------------+
|																			|
|	Init buffer.															|
|																			|
|	Store a NULL character (as end of string).								|
|																			|
+--------------------------------------------------------------------------*/
static void init_result(buffer,buflen)
char *buffer;
int buflen;
{
	result = buffer;
	result_index = buflen;
	add_char(0);			/* end result by null char */
}

/*--------------------------------------------------------------------------+
|																			|
|	Store the current variable or struct name.								|
|																			|
|	input :		curr_txt points to '=' character,							|
|				start_txt points to beginning of the parse string.			|
|																			|
|	output :	curr_txt points to character before 1st character of		|
|					name.													|
|																			|
|	Note : we have to test for the beginning of the parse string,			|
|	because add_name() is called also for adding the "$n" name				|
|	of the gdb output.														|
|																			|
+--------------------------------------------------------------------------*/
static void add_name ()
{
	curr_txt--;								/* point before '=' */
	while (*curr_txt == ' ') curr_txt--;	/* skip spaces */
	
	/* loop over name */
	while ((curr_txt >= start_txt) && GOODCHARNAME(*curr_txt))
		add_char(*curr_txt--);
}

/*--------------------------------------------------------------------------+
|																			|
|	Skip all previous characters until corresponding " or ' character.		|
|																			|
|	input : curr_txt points before ' or " character							|
|																			|
|	output : curr_txt points before corresponding ' or " character.			|
|																			|
+--------------------------------------------------------------------------*/
void search_char(c)
char c;
{
	while(1)
		{
		while(c != *(curr_txt--));
		
		/* make sure there is not a '\' just before */
		
		if (*curr_txt != '\\')
			return;
		}
}

/*--------------------------------------------------------------------------+
|																			|
|	Skip all previous characters until previous corresponding '{'.			|
|	All "{...}" sequences are skip.											|
|	Return the array item number (if applicable)							|
|																			|
|	input :		curr_txt points to string.									|
|																			|
|	output :	curr_txt points to character before '{'						|
|				return number of commas										|
|																			|
+--------------------------------------------------------------------------*/
static int skip_level()
{
int nbcommas;
char c;

	nbcommas = 0;
	
	while(1)
		{
		switch (c = *(curr_txt--))
			{
			case '{' :
				return nbcommas;
			
			case ',' :
				nbcommas++;
				break;
				
			case '}' :
				skip_level();
				break;
			
			case '"' :
			case '\'' :
				search_char(c);
				break;
				
			default:
				break;
			}
		}
}

/*--------------------------------------------------------------------------+
|																			|
|	Function to parse an output of a gdb print from							|
|	a pointer (0x...) and return a command line to							|
|	print *(0x...)															|
|																			|
|	input : command line pointer (LINESIZ size),							|
|			pointer print output,											|
|			pointer 0x...													|
|																			|
|	output : command line (stored RIGHT justified in commandline)			|
|																			|
|	example																	|
|																			|
|		start = "$1 = { (struct foo *) 0x1224}"								|
|		current points to 0x1224 in start,									|
|																			|
|		commandline = "print *($1)"											|
|																			|
+--------------------------------------------------------------------------*/
char *parse_gdb_print (commandline, start, current)
char *commandline;
char *start;
char *current;
{
char *begin;

	start_txt = start;		/* in static variables */
	curr_txt = current;

	begin = strchr(start,'=');	/* find '=' in "$n =" */
	
	if (!begin)
		return NULL;
		
	init_result(commandline,LINESIZ);

	add_string(")\n");
	
	while (begin <= curr_txt)
		{
		switch (*curr_txt)
			{
			case '=':
				add_name();
				
				/* stop now if we just parsed the '=' in "$n =" */
				if (curr_txt >= start_txt)
					{
					add_char('.');
					skip_level();
					}
				break;
			
			case ',':
			case '{':
				add_char(']');
				add_num(skip_level());
				add_char('[');
				break;
		
			default:
				curr_txt--;
			}
		}
		
	add_string("print *(");
	
	if (debug)
		fprintf(stderr,"datadpy=%s\n",result+result_index);
	
	return result+result_index;
}

#endif /* GDB */
