utils.c revision 352bf44e
1/*
2 *
3Copyright 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 */
25
26#include <X11/Intrinsic.h>
27#include <X11/Xutil.h>
28#include <X11/Xos.h>
29#include <X11/Shell.h>
30#include <X11/StringDefs.h>
31
32#include <X11/Xaw/Cardinals.h>
33#include <X11/Xaw/Dialog.h>
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <X11/Xmu/Error.h>
38
39#include "editresP.h"
40
41static WNode * FindWidgetFromWindowGivenNode ( WNode * node, Window win );
42static WidgetResources * ParseResources ( GetResourcesInfo * info,
43					  char **error );
44static int CompareResourceEntries ( const void *e1,
45				    const void *e2 );
46static void AddResource ( ResourceInfo * res_info,
47			  WidgetResourceInfo * resource );
48static void FreeResources ( WidgetResources * resources );
49
50
51/*	Function Name: SetMessage(w, str)
52 *	Description: shows the message to the user.
53 *	Arguments: w - a label widget to show the message in.
54 *                 str - the string to show.
55 *	Returns: none.
56 */
57
58void
59SetMessage(Widget w, String str)
60{
61    Arg args[1];
62
63    XtSetArg(args[0], XtNlabel, str);
64    XtSetValues(w, args, ONE);
65}
66
67/*	Function Name: GetAllStrings
68 *	Description: Returns a list of strings that have been broken up by
69 *                   the character specified.
70 *	Arguments: in - the string to parse.
71 *                 sep - the separator character.
72 *                 out - the strings to send out.
73 *                 num - the number of strings in out.
74 *	Returns: none
75 */
76
77void
78GetAllStrings(char *in, char sep, char ***out, int *num)
79{
80    int size, i;
81    char * ptr;
82
83    if (*in == sep)		/* jump over first char if it is the sep. */
84	in++;
85
86    /*
87     * count the number of strings.
88     */
89
90    for (*num = 1, ptr = in; (ptr = strchr(ptr, sep)) != NULL; (*num)++)
91	ptr++;
92
93/*
94 * Create Enough space for pointers and string.
95 */
96
97    size = (sizeof(char *) * *num) + (sizeof(char) * (strlen(in) + 1));
98    *out = (char **) XtMalloc( (Cardinal) size);
99
100    ptr = (char *) (*out + *num);
101    strcpy(ptr, in);
102
103/*
104 * Change all `sep' characters to '\0' and stuff the pointer into
105 * the next pointer slot.
106 */
107
108    i = 1;
109    (*out)[0] = ptr;
110    while (TRUE) {
111	if ((ptr = strchr(ptr, sep)) == NULL)
112	    break;
113
114	*ptr++ = '\0';
115	(*out)[i++] = ptr;
116    }
117
118/*
119 * If last string is empty then strip it off.
120 */
121
122    if ( *((*out)[i - 1]) == '\0' )
123	(*num)--;
124}
125
126/*	Function Name: AddString
127 *	Description: Mallocs and strcats the string onto the end of
128 *                   the given string.
129 *	Arguments: str - string to add on to.
130 *                 add - string to add.
131 *	Returns: none.
132 */
133
134void
135AddString(char ** str, const char *add)
136{
137    int len_str, len_add;
138    char * ptr;
139
140    len_str = ((*str) ? strlen(*str) : 0);
141    len_add = strlen(add);
142
143    *str = XtRealloc(*str, sizeof(char) * (len_str + len_add + 1));
144    ptr = *str + len_str;
145    strcpy(ptr, add);
146}
147
148/*	Function Name: FindNode
149 *	Description: Finds a node give the top node, and a node id number.
150 *	Arguments: top_node - the top node.
151 *                 id - the node id.
152 *	Returns: node.
153 */
154
155WNode *
156FindNode(WNode *top_node, unsigned long *ids, Cardinal number)
157{
158    Cardinal i, j;
159    WNode *node;
160
161    if (top_node == NULL)
162	return(NULL);
163
164    if (ids[0] != top_node->id)
165	return(NULL);
166
167    for (node = top_node, i = 1 ; i < number; i++) {
168	Boolean found_it = FALSE;
169
170	for (j = 0; j < node->num_children; j++) {
171	    if (node->children[j]->id == ids[i]) {
172		node = node->children[j];
173		found_it = TRUE;
174		break;
175	    }
176	}
177	if (!found_it)
178	    return(NULL);
179    }
180    return(node);
181}
182
183/*	Function Name: FindWidgetFromWindow
184 *	Description: finds a widget in the current tree given its window id.
185 *	Arguments: tree_info - information about this tree.
186 *                 win - window to search for.
187 *	Returns: node - the node corrosponding to this widget.
188 */
189
190WNode *
191FindWidgetFromWindow(TreeInfo *tree_info, Window win)
192{
193    if (tree_info == NULL)
194	return(NULL);
195
196    return(FindWidgetFromWindowGivenNode(tree_info->top_node, win));
197}
198
199/*	Function Name: FindWidgetFromWindowGivenNode
200 *	Description: finds a widget in the current tree given its window id.
201 *	Arguments: node - current node.
202 *                 win - window to search for.
203 *	Returns: node - the node corrosponding to this widget.
204 */
205
206static WNode *
207FindWidgetFromWindowGivenNode(WNode *node, Window win)
208{
209    Cardinal i;
210    WNode * ret_node;
211
212    if (node->window == win)
213	return(node);
214
215    for (i = 0; i < node->num_children; i++) {
216	ret_node = FindWidgetFromWindowGivenNode(node->children[i], win);
217	if (ret_node != NULL)
218	    return(ret_node);
219    }
220    return(NULL);
221}
222
223/*	Function Name: HandleXErrors
224 *	Description: Handles error codes from the server.
225 *	Arguments: display - the display.
226 *                 error - error information.
227 *	Returns: none.
228 */
229
230/* ARGSUSED */
231int
232HandleXErrors(Display *display, XErrorEvent *error)
233{
234    if (error->serial != global_serial_num) {
235	(*global_old_error_handler) (display, error);
236	return(0);
237    }
238
239    if (error->error_code == BadWindow)
240	global_error_code = NO_WINDOW;
241    else {
242	if (XmuPrintDefaultErrorMessage(display, error, stderr) != 0)
243	    exit(1);
244    }
245    return(0);
246}
247
248/*	Function Name: _DumpTreeToFile
249 *	Description: Dumps the widget tree to a file
250 *	Arguments: w - a random widget in the application on the
251 *                     currently active display
252 *                 tree_ptr - pointer to the widget tree info.
253 *                 filename - name of the file.
254 *	Returns: none.
255 */
256
257/* ARGSUSED */
258
259void
260_DumpTreeToFile(Widget w, XtPointer tree_ptr, XtPointer filename)
261{
262    TreeInfo * tree_info = (TreeInfo *) tree_ptr;
263    FILE * fp;
264
265    if (tree_info == NULL) {
266	SetMessage(global_screen_data.info_label,
267		   res_labels[17]);
268	return;
269    }
270
271    if ( (fp = fopen((char *)filename, "w")) == NULL ) {
272	char buf[BUFSIZ];
273
274	snprintf(buf, sizeof(buf), res_labels[24], (char *)filename);
275	SetMessage(global_screen_data.info_label, buf);
276	return;
277    }
278
279    PerformTreeToFileDump(tree_info->top_node, 0, fp);
280    fclose(fp);
281}
282
283/************************************************************
284 *
285 * The file dialog boxes are handled with this code.
286 *
287 * It automatically calls the function specified when the
288 * user selects okay, or hits <CR>.
289 *
290 * A translation is required in the app-defaults file.
291 *
292 ************************************************************/
293
294/*	Function Name: _PopupFileDialog
295 *	Description: Puts up a dialog box to get the filename.
296 *	Arguments: str - message.
297 *                 default_value - the default value of the filename;
298 *                 func - function to call when filename has been entered.
299 *                 data - generic data to pass to func.
300 *	Returns: none
301 */
302
303static XContext file_dialog_context = None;
304
305typedef struct _FileDialogInfo {
306    XtCallbackProc func;
307    XtPointer data;
308} FileDialogInfo;
309
310void
311_PopupFileDialog(Widget w, String str, String default_value,
312		 XtCallbackProc func, XtPointer data)
313{
314    FileDialogInfo * file_info;
315    Widget shell, dialog;
316    Arg args[2];
317    Cardinal num_args;
318
319    if (file_dialog_context == None)
320	file_dialog_context = XUniqueContext();
321
322    shell = XtCreatePopupShell("fileDialog", transientShellWidgetClass, w,
323			       NULL, ZERO);
324
325    num_args = 0;
326    XtSetArg(args[num_args], XtNlabel, str); num_args++;
327    XtSetArg(args[num_args], XtNvalue, default_value); num_args++;
328    dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
329				   shell, args, num_args);
330
331    file_info = XtNew(FileDialogInfo);
332
333    file_info->func = func;
334    file_info->data = data;
335
336    if  (XSaveContext(XtDisplay(dialog), (Window) dialog, file_dialog_context,
337		      (XPointer) file_info) != 0) {
338	SetMessage(global_screen_data.info_label,
339	    "Error while trying to save Context\nAborting file dialog popup.");
340	XtDestroyWidget(shell);
341	return;
342    }
343
344    XawDialogAddButton(dialog, "okay", _PopdownFileDialog, (XtPointer) TRUE);
345    XawDialogAddButton(dialog, "cancel", _PopdownFileDialog,(XtPointer) FALSE);
346
347    PopupCentered(NULL, shell, XtGrabNone);
348}
349
350/*	Function Name: PopupCentered
351 *	Description: Pops up the window specified under the location passed
352 *                   in the event, or under the cursor.
353 *	Arguments: event - the event that we should use.
354 *                 w - widget to popup.
355 *                 mode - mode to pop it up in.
356 *	Returns: none
357 */
358
359void
360PopupCentered(XEvent *event, Widget w, XtGrabKind mode)
361{
362    Boolean get_from_cursor = FALSE;
363    Arg args[3];
364    Cardinal num_args;
365    Dimension width, height, b_width;
366    int x, y, max_x, max_y;
367
368    XtRealizeWidget(w);
369
370    if (event == NULL)
371	get_from_cursor = TRUE;
372    else {
373	switch (event->type) {
374	case ButtonPress:
375	case ButtonRelease:
376	    x = event->xbutton.x_root;
377	    y = event->xbutton.y_root;
378	    break;
379	case KeyPress:
380	case KeyRelease:
381	    x = event->xkey.x_root;
382	    y = event->xkey.y_root;
383	    break;
384	default:
385	    get_from_cursor = TRUE;
386	    break;
387	}
388    }
389
390    if (get_from_cursor) {
391	Window root, child;
392	int win_x, win_y;
393	unsigned int mask;
394
395	XQueryPointer(XtDisplay(w), XtWindow(w),
396		      &root, &child, &x, &y, &win_x, &win_y, &mask);
397    }
398
399    num_args = 0;
400    XtSetArg(args[num_args], XtNwidth, &width); num_args++;
401    XtSetArg(args[num_args], XtNheight, &height); num_args++;
402    XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
403    XtGetValues(w, args, num_args);
404
405    width += 2 * b_width;
406    height += 2 * b_width;
407
408    x -= ((int) width/2);
409    if (x < 0)
410	x = 0;
411    if ( x > (max_x = (int) (XtScreen(w)->width - width)) )
412	x = max_x;
413
414    y -= ( (Position) height/2 );
415    if (y < 0)
416	y = 0;
417    if ( y > (max_y = (int) (XtScreen(w)->height - height)) )
418	y = max_y;
419
420    num_args = 0;
421    XtSetArg(args[num_args], XtNx, x); num_args++;
422    XtSetArg(args[num_args], XtNy, y); num_args++;
423    XtSetValues(w, args, num_args);
424
425    XtPopup(w, mode);
426}
427
428/*	Function Name: _PopdownFileDialog
429 *	Description: Destroys the file dialog, and calls the correct function.
430 *	Arguments:  w - a child of the dialog widget.
431 *                  client_data - TRUE if command was successful.
432 *                  junk - ** UNUSED **.
433 *	Returns: none.
434 */
435
436/* ARGSUSED */
437
438void
439_PopdownFileDialog(Widget w, XtPointer client_data, XtPointer junk)
440{
441    Widget dialog = XtParent(w);
442    XPointer file_info_ptr;
443    FileDialogInfo * file_info;
444
445    if (XFindContext(XtDisplay(dialog), (Window) dialog, file_dialog_context,
446		     &file_info_ptr) == XCNOENT) {
447	SetMessage(global_screen_data.info_label,
448		   "Error while trying to find Context\nAborting...");
449    }
450
451    (void) XDeleteContext(XtDisplay(dialog), (Window)dialog,
452			  file_dialog_context);
453
454    file_info = (FileDialogInfo *) file_info_ptr;
455
456    if ( ((Boolean)(long) client_data) == TRUE ) {
457	String filename = XawDialogGetValueString(dialog);
458
459	(*file_info->func)(w, file_info->data, filename); /* call handler */
460    }
461
462    XtFree( (XtPointer) file_info); /* Free data. */
463
464    XtPopdown(XtParent(dialog));
465    XtDestroyWidget(XtParent(dialog)); /* Remove file dialog. */
466}
467
468/************************************************************
469 *
470 * Functions for dealing with the Resource Box.
471 *
472 ************************************************************/
473
474/*    Function Name: GetNamesAndClasses
475 *    Description: Gets a list of names and classes for this widget.
476 *    Arguments: node - this widget's node.
477 *                 names, classes - list of names and classes. ** RETURNED **
478 *    Returns: none.
479 */
480
481void
482GetNamesAndClasses(WNode *node, char ***names, char ***classes)
483{
484    int i, total_widgets;
485    WNode * temp = node;
486
487    for (total_widgets = 1 ; temp->parent != NULL ;
488       total_widgets++, temp = temp->parent) {}
489
490    *names = (char **) XtMalloc(sizeof(char *) * (total_widgets + 1));
491    *classes = (char **) XtMalloc(sizeof(char *) * (total_widgets + 1));
492
493    (*names)[total_widgets] = (*classes)[total_widgets] = NULL;
494
495    for ( i = (total_widgets - 1); i >= 0 ; node = node->parent, i--) {
496      (*names)[i] = node->name;
497      (*classes)[i] = node->class;
498    }
499}
500
501/*	Function Name: HandleGetResources
502 *	Description: Gets the resources.
503 *	Arguments: event - the information from the client.
504 *	Returns: an error message to display.
505 */
506
507char *
508HandleGetResources(Event *event)
509{
510    GetResourcesEvent * get_event = (GetResourcesEvent *) event;
511    char buf[BUFSIZ], * errors = NULL;
512    int i;
513    WNode * node;
514
515    for (i = 0; i < (int)get_event->num_entries; i++) {
516	node = FindNode(global_tree_info->top_node,
517			get_event->info[i].widgets.ids,
518			get_event->info[i].widgets.num_widgets);
519
520	if (node == NULL) {
521	    snprintf(buf, sizeof(buf), res_labels[16]);
522	    AddString(&errors, buf);
523	    continue;
524	}
525
526	if (node->resources != NULL)
527	    FreeResources(node->resources);
528
529	if (!get_event->info[i].error) {
530	    node->resources = ParseResources(get_event->info + i, &errors);
531	    CreateResourceBox(node, &errors);
532	}
533	else {
534	    AddString(&errors, get_event->info[i].message);
535	    AddString(&errors, "\n");
536	}
537    }
538
539    return(errors);
540}
541
542/*	Function Name: CreateResourceBox
543 *	Description: Creates a resource box for the widget specified.
544 *	Arguments: node - the node of the widget in question.
545 *                 errors - an error string.
546 *	Returns: none.
547 */
548
549void
550CreateResourceBox(WNode *node, char **errors)
551{
552    WidgetResources * resources = node->resources;
553    char ** names, ** cons_names;
554    int i;
555
556    if (global_resource_box_up) {
557	AddString(errors, res_labels[34]);
558	return;
559    }
560    else
561	global_resource_box_up = TRUE;
562
563    if (resources->num_normal > 0) {
564	names = (char **) XtMalloc(sizeof(char *) *
565				   (resources->num_normal + 1));
566	for (i = 0 ; i < resources->num_normal ; i++)
567	    names[i] = resources->normal[i].name;
568	names[i] = NULL;
569    }
570    else
571	names = NULL;
572
573    if (resources->num_constraint > 0) {
574	cons_names = (char **) XtMalloc(sizeof(char *) *
575					(resources->num_constraint + 1));
576
577	for (i = 0 ; i < resources->num_constraint ; i++)
578	    cons_names[i] = resources->constraint[i].name;
579	cons_names[i] = NULL;
580    }
581    else
582	cons_names = NULL;
583
584    CreateResourceBoxWidgets(node, names, cons_names);
585}
586
587/*	Function Name: ParseResources
588 *	Description: Parses the resource values returned from the client
589 *                   into a resources structure.
590 *	Arguments: info - info about a widget's resources.
591 *                 error - where to place error info.
592 *	Returns: The resource information.
593 */
594
595static WidgetResources *
596ParseResources(GetResourcesInfo *info, char **error)
597{
598    WidgetResources * resources;
599    WidgetResourceInfo * normal;
600    int i;
601
602    resources = (WidgetResources *) XtMalloc(sizeof(WidgetResources));
603
604    /*
605     * Allocate enough space for both the normal and constraint resources,
606     * then add the normal resources from the top, and the constraint resources
607     * from the bottom.  This assures that enough memory is allocated, and
608     * that there is no overlap.
609     */
610
611    resources->normal = (WidgetResourceInfo *)
612	            XtMalloc(sizeof(WidgetResourceInfo) * info->num_resources);
613
614    normal = resources->normal;
615    resources->constraint = resources->normal + info->num_resources - 1;
616
617    resources->num_constraint = resources->num_normal = 0;
618
619    for (i = 0; i < (int)info->num_resources; i++) {
620	switch((int) info->res_info[i].res_type) {
621	case NormalResource:
622	    resources->num_normal++;
623	    AddResource(info->res_info + i, normal++);
624	    break;
625	case ConstraintResource:
626	    resources->num_constraint++;
627	    AddResource(info->res_info + i, resources->constraint--);
628	    break;
629	default:
630	    {
631		char buf[BUFSIZ];
632		snprintf(buf, sizeof(buf), "Unknown resource type %d\n",
633                         info->res_info[i].res_type);
634		AddString(error, buf);
635	    }
636	    break;
637	}
638    }
639
640    /*
641     * Sort the resources alphabetically.
642     */
643
644    qsort(resources->normal, resources->num_normal,
645	  sizeof(WidgetResourceInfo), CompareResourceEntries);
646
647    if (resources->num_constraint > 0) {
648	resources->constraint++;
649	qsort(resources->constraint, resources->num_constraint,
650	      sizeof(WidgetResourceInfo), CompareResourceEntries);
651    }
652    else
653	resources->constraint = NULL;
654
655    return(resources);
656}
657
658/*	Function Name: CompareResourceEntries
659 *	Description: Compares two resource entries.
660 *	Arguments: e1, e2 - the entries to compare.
661 *	Returns: an integer >, < or = 0.
662 */
663
664static int
665CompareResourceEntries(const void *e1, const void *e2)
666{
667    return (strcmp(((WidgetResourceInfo *)e1)->name,
668		   ((WidgetResourceInfo *)e2)->name));
669}
670
671/*	Function Name: AddResource
672 *	Description: Parses the resource string a stuffs in individual
673 *                   parts into the resource info struct.
674 *	Arguments: res_info - the resource info from the event.
675 *                 resource - location to stuff the resource into.
676 *	Returns: none.
677 */
678
679static void
680AddResource(ResourceInfo *res_info, WidgetResourceInfo *resource)
681{
682    resource->name = res_info->name;
683    res_info->name = NULL;	/* Keeps it from being deallocated. */
684    resource->class = res_info->class;
685    res_info->class = NULL;	/* Keeps it from being deallocated. */
686    resource->type = res_info->type;
687    res_info->type = NULL;	/* Keeps it from being deallocated. */
688}
689
690
691/*	Function Name: FreeResources
692 *	Description: frees the resource information.
693 *	Arguments: resources.
694 *	Returns: none.
695 */
696
697static void
698FreeResources(WidgetResources *resources)
699{
700    int i;
701
702    if (resources->num_normal > 0) {
703	for (i = 0; i < resources->num_normal; i++) {
704	    XtFree(resources->normal[i].name);
705	    XtFree(resources->normal[i].class);
706	    XtFree(resources->normal[i].type);
707	}
708	XFree((char *)resources->normal);
709    }
710
711    if (resources->num_constraint > 0) {
712	for (i = 0; i < resources->num_constraint; i++) {
713	    XtFree(resources->constraint[i].name);
714	    XtFree(resources->constraint[i].class);
715	    XtFree(resources->constraint[i].type);
716	}
717	XFree((char *)resources->constraint);
718    }
719
720    XFree((char *)resources);
721}
722
723
724/*	Function Name: CheckDatabase
725 *	Description: Checks to see if the node is in the database.
726 *	Arguments: db - the db to check
727 *                 names, classes - names and classes, represented as quarks.
728 *	Returns: True if this entry is found.
729 */
730
731Boolean
732CheckDatabase(XrmDatabase db, XrmQuarkList names, XrmQuarkList classes)
733{
734    XrmRepresentation junk;
735    XrmValue garbage;
736
737    return(XrmQGetResource(db, names, classes, &junk, &garbage));
738}
739
740/*	Function Name: Quarkify
741 *	Description: Quarkifies the string list specified.
742 *	Arguments: list - list of strings to quarkify
743 *                 ptr - an additional string to quarkify.
744 *	Returns: none.
745 */
746
747XrmQuarkList
748Quarkify(char **list, const char *ptr)
749{
750    int i;
751    char ** tlist;
752    XrmQuarkList quarks, tquarks;
753
754    for (i = 0, tlist = list; *tlist != NULL; tlist++, i++) {}
755    if (ptr != NULL)
756	i++;
757    i++;			/* leave space for NULLQUARK */
758
759    quarks = (XrmQuarkList) XtMalloc(sizeof(XrmQuark) * i);
760
761    for (tlist = list, tquarks = quarks; *tlist != NULL; tlist++, tquarks++)
762	*tquarks = XrmStringToQuark(*tlist);
763
764    if (ptr != NULL)
765	*tquarks++ = XrmStringToQuark(ptr);
766
767    *tquarks = NULLQUARK;
768    return(quarks);
769}
770
771/*	Function Name: ExecuteOverAllNodes
772 *	Description: Executes the given function over all nodes.
773 *	Arguments: top_node - top node of the tree.
774 *                 func - the function to execute.
775 *                 data - a data pointer to pass to the function.
776 *	Returns: none
777 */
778
779void
780ExecuteOverAllNodes(WNode *top_node, void (*func)(WNode *, XtPointer),
781		    XtPointer data)
782{
783    Cardinal i;
784
785    (*func)(top_node, data);
786
787    for (i = 0; i < top_node->num_children; i++)
788	ExecuteOverAllNodes(top_node->children[i], func, data);
789}
790
791/*	Function Name: InsertWidgetFromNode
792 *	Description: Inserts the widget info for this widget represented
793 *                   by this node.
794 *	Arguments: stream - the stream to insert it info into.
795 *                 none - the widget node to insert.
796 *	Returns: none
797 */
798
799void
800InsertWidgetFromNode(ProtocolStream *stream, WNode *node)
801{
802    WNode *temp;
803    unsigned long * widget_list;
804    register int i, num_widgets;
805
806    for (temp = node, i = 0; temp != NULL; temp = temp->parent, i++) {}
807
808    num_widgets = i;
809    widget_list = (unsigned long *)
810	          XtMalloc(sizeof(unsigned long) * num_widgets);
811
812    /*
813     * Put the widgets into the list.
814     * Make sure that they are inserted in the list from parent -> child.
815     */
816
817    for (i--, temp = node; temp != NULL; temp = temp->parent, i--)
818	widget_list[i] = temp->id;
819
820    _XEditResPut16(stream, num_widgets);	/* insert number of widgets. */
821    for (i = 0; i < num_widgets; i++) 	/* insert Widgets themselves. */
822	_XEditResPut32(stream, widget_list[i]);
823
824    XtFree((char *)widget_list);
825}
826
827/*	Function Name: GetFailureMesssage
828 *	Description: returns the message returned from a failed request.
829 *	Arguments: stream - the protocol stream containing the message.
830 *	Returns: message to show.
831 */
832
833char *
834GetFailureMessage(ProtocolStream *stream)
835{
836    char * return_str;
837
838    if (_XEditResGetString8(stream, &return_str))
839	return(return_str);
840
841    return(XtNewString(res_labels[35]));
842}
843
844/*	Function Name: ProtocolFailure
845 *	Description: Gets the version of the protocol the client is
846 *                   willing to speak.
847 *	Arguments: stream - the protocol stream containing the message.
848 *	Returns: message to show.
849 */
850
851char *
852ProtocolFailure(ProtocolStream *stream)
853{
854    char buf[BUFSIZ];
855    unsigned char version;
856    const char* old_version_string;
857
858    if (!_XEditResGet8(stream, &version))
859	return(XtNewString(res_labels[35]));
860
861    switch ((int)version) {
862    case PROTOCOL_VERSION_ONE_POINT_ZERO: old_version_string = "1.0"; break;
863    default: old_version_string = "1.0";
864    }
865
866    snprintf(buf, sizeof(buf), res_labels[36],
867             CURRENT_PROTOCOL_VERSION_STRING, old_version_string);
868    return(XtNewString(buf));
869}
870
871