1/* A general interface to the widgets of different toolkits.
2Copyright (C) 1992, 1993 Lucid, Inc.
3Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2003, 2004,
4  2005, 2006, 2007  Free Software Foundation, Inc.
5
6This file is part of the Lucid Widget Library.
7
8The Lucid Widget Library is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2, or (at your option)
11any later version.
12
13The Lucid Widget Library is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with GNU Emacs; see the file COPYING.  If not, write to
20the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21Boston, MA 02110-1301, USA.  */
22
23#ifdef NeXT
24#undef __STRICT_BSD__ /* ick */
25#endif
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31#include "../src/lisp.h"
32
33#include <sys/types.h>
34#include <stdio.h>
35#include <ctype.h>
36#include "lwlib-int.h"
37#include "lwlib-utils.h"
38#include <X11/StringDefs.h>
39
40#if defined (USE_LUCID)
41#include "lwlib-Xlw.h"
42#endif
43#if defined (USE_MOTIF)
44#include "lwlib-Xm.h"
45#else /* not USE_MOTIF */
46#if defined (USE_LUCID)
47#define USE_XAW
48#endif /* not USE_MOTIF && USE_LUCID */
49#endif
50#if defined (USE_XAW)
51#include <X11/Xaw/Paned.h>
52#include "lwlib-Xaw.h"
53#endif
54
55#if !defined (USE_LUCID) && !defined (USE_MOTIF)
56 #error  At least one of USE_LUCID or USE_MOTIF must be defined.
57#endif
58
59#ifndef max
60#define max(x, y) ((x) > (y) ? (x) : (y))
61#endif
62
63/* List of all widgets managed by the library. */
64static widget_info*
65all_widget_info = NULL;
66
67#ifdef USE_MOTIF
68char *lwlib_toolkit_type = "motif";
69#else
70char *lwlib_toolkit_type = "lucid";
71#endif
72
73static widget_value *merge_widget_value P_ ((widget_value *,
74					     widget_value *,
75					     int, int *));
76static void instantiate_widget_instance P_ ((widget_instance *));
77static int my_strcasecmp P_ ((char *, char *));
78static void safe_free_str P_ ((char *));
79static void free_widget_value_tree P_ ((widget_value *));
80static widget_value *copy_widget_value_tree P_ ((widget_value *,
81						 change_type));
82static widget_info *allocate_widget_info P_ ((char *, char *, LWLIB_ID,
83					      widget_value *,
84					      lw_callback, lw_callback,
85					      lw_callback, lw_callback));
86static void free_widget_info P_ ((widget_info *));
87static void mark_widget_destroyed P_ ((Widget, XtPointer, XtPointer));
88static widget_instance *allocate_widget_instance P_ ((widget_info *,
89						      Widget, Boolean));
90static void free_widget_instance P_ ((widget_instance *));
91static widget_info *get_widget_info P_ ((LWLIB_ID, Boolean));
92static widget_instance *get_widget_instance P_ ((Widget, Boolean));
93static widget_instance *find_instance P_ ((LWLIB_ID, Widget, Boolean));
94static Boolean safe_strcmp P_ ((char *, char *));
95static Widget name_to_widget P_ ((widget_instance *, char *));
96static void set_one_value P_ ((widget_instance *, widget_value *, Boolean));
97static void update_one_widget_instance P_ ((widget_instance *, Boolean));
98static void update_all_widget_values P_ ((widget_info *, Boolean));
99static void initialize_widget_instance P_ ((widget_instance *));
100static widget_creation_function find_in_table P_ ((char *, widget_creation_entry *));
101static Boolean dialog_spec_p P_ ((char *));
102static void destroy_one_instance P_ ((widget_instance *));
103static void lw_pop_all_widgets P_ ((LWLIB_ID, Boolean));
104static Boolean get_one_value P_ ((widget_instance *, widget_value *));
105static void show_one_widget_busy P_ ((Widget, Boolean));
106
107void
108lwlib_memset (address, value, length)
109     char *address;
110     int value;
111     size_t length;
112{
113  int i;
114
115  for (i = 0; i < length; i++)
116    address[i] = value;
117}
118
119void
120lwlib_bcopy (from, to, length)
121     char *from;
122     char *to;
123     int length;
124{
125  int i;
126
127  for (i = 0; i < length; i++)
128    to[i] = from[i];
129}
130/* utility functions for widget_instance and widget_info */
131char *
132safe_strdup (s)
133     const char *s;
134{
135  char *result;
136  if (! s) return 0;
137  result = (char *) malloc (strlen (s) + 1);
138  if (! result)
139    return 0;
140  strcpy (result, s);
141  return result;
142}
143
144/* Like strcmp but ignore differences in case.  */
145
146static int
147my_strcasecmp (s1, s2)
148     char *s1, *s2;
149{
150  while (1)
151    {
152      int c1 = *s1++;
153      int c2 = *s2++;
154      if (isupper (c1))
155	c1 = tolower (c1);
156      if (isupper (c2))
157	c2 = tolower (c2);
158      if (c1 != c2)
159	return (c1 > c2 ? 1 : -1);
160      if (c1 == 0)
161	return 0;
162    }
163}
164
165static void
166safe_free_str (s)
167     char *s;
168{
169  if (s) free (s);
170}
171
172static widget_value *widget_value_free_list = 0;
173static int malloc_cpt = 0;
174
175widget_value *
176malloc_widget_value ()
177{
178  widget_value *wv;
179  if (widget_value_free_list)
180    {
181      wv = widget_value_free_list;
182      widget_value_free_list = wv->free_list;
183      wv->free_list = 0;
184    }
185  else
186    {
187      wv = (widget_value *) malloc (sizeof (widget_value));
188      malloc_cpt++;
189    }
190  lwlib_memset ((void*) wv, 0, sizeof (widget_value));
191  return wv;
192}
193
194/* this is analogous to free().  It frees only what was allocated
195   by malloc_widget_value(), and no substructures.
196 */
197void
198free_widget_value (wv)
199     widget_value *wv;
200{
201  if (wv->free_list)
202    abort ();
203
204  if (malloc_cpt > 25)
205    {
206      /* When the number of already allocated cells is too big,
207	 We free it.  */
208      free (wv);
209      malloc_cpt--;
210    }
211  else
212    {
213      wv->free_list = widget_value_free_list;
214      widget_value_free_list = wv;
215    }
216}
217
218static void
219free_widget_value_tree (wv)
220     widget_value *wv;
221{
222  if (!wv)
223    return;
224
225  if (wv->name) free (wv->name);
226  if (wv->value) free (wv->value);
227  if (wv->key) free (wv->key);
228
229  wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
230
231  if (wv->toolkit_data && wv->free_toolkit_data)
232    {
233      XtFree (wv->toolkit_data);
234      wv->toolkit_data = (void *) 0xDEADBEEF;
235    }
236
237  if (wv->contents && (wv->contents != (widget_value*)1))
238    {
239      free_widget_value_tree (wv->contents);
240      wv->contents = (widget_value *) 0xDEADBEEF;
241    }
242  if (wv->next)
243    {
244      free_widget_value_tree (wv->next);
245      wv->next = (widget_value *) 0xDEADBEEF;
246    }
247  free_widget_value (wv);
248}
249
250static widget_value *
251copy_widget_value_tree (val, change)
252     widget_value* val;
253     change_type change;
254{
255  widget_value* copy;
256
257  if (!val)
258    return NULL;
259  if (val == (widget_value *) 1)
260    return val;
261
262  copy = malloc_widget_value ();
263  copy->name = safe_strdup (val->name);
264  copy->value = safe_strdup (val->value);
265  copy->key = safe_strdup (val->key);
266  copy->help = val->help;
267  copy->enabled = val->enabled;
268  copy->button_type = val->button_type;
269  copy->selected = val->selected;
270  copy->edited = False;
271  copy->change = change;
272  copy->this_one_change = change;
273  copy->contents = copy_widget_value_tree (val->contents, change);
274  copy->call_data = val->call_data;
275  copy->next = copy_widget_value_tree (val->next, change);
276  copy->toolkit_data = NULL;
277  copy->free_toolkit_data = False;
278  return copy;
279}
280
281static widget_info *
282allocate_widget_info (type, name, id, val, pre_activate_cb,
283		      selection_cb, post_activate_cb, highlight_cb)
284     char* type;
285     char* name;
286     LWLIB_ID id;
287     widget_value* val;
288     lw_callback pre_activate_cb;
289     lw_callback selection_cb;
290     lw_callback post_activate_cb;
291     lw_callback highlight_cb;
292{
293  widget_info* info = (widget_info*)malloc (sizeof (widget_info));
294  info->type = safe_strdup (type);
295  info->name = safe_strdup (name);
296  info->id = id;
297  info->val = copy_widget_value_tree (val, STRUCTURAL_CHANGE);
298  info->busy = False;
299  info->pre_activate_cb = pre_activate_cb;
300  info->selection_cb = selection_cb;
301  info->post_activate_cb = post_activate_cb;
302  info->highlight_cb = highlight_cb;
303  info->instances = NULL;
304
305  info->next = all_widget_info;
306  all_widget_info = info;
307
308  return info;
309}
310
311static void
312free_widget_info (info)
313     widget_info* info;
314{
315  safe_free_str (info->type);
316  safe_free_str (info->name);
317  free_widget_value_tree (info->val);
318  lwlib_memset ((void*)info, 0xDEADBEEF, sizeof (widget_info));
319  free (info);
320}
321
322static void
323mark_widget_destroyed (widget, closure, call_data)
324     Widget widget;
325     XtPointer closure;
326     XtPointer call_data;
327{
328  widget_instance* instance = (widget_instance*)closure;
329
330  /* be very conservative */
331  if (instance->widget == widget)
332    instance->widget = NULL;
333}
334
335/* The messy #ifdef PROTOTYPES here and elsewhere are prompted by a
336   flood of warnings about argument promotion from proprietary ISO C
337   compilers.  (etags still only makes one entry for each function.)  */
338static widget_instance *
339#ifdef PROTOTYPES
340allocate_widget_instance (widget_info* info, Widget parent, Boolean pop_up_p)
341#else
342allocate_widget_instance (info, parent, pop_up_p)
343     widget_info* info;
344     Widget parent;
345     Boolean pop_up_p;
346#endif
347{
348  widget_instance* instance =
349    (widget_instance*)malloc (sizeof (widget_instance));
350  bzero (instance, sizeof *instance);
351  instance->parent = parent;
352  instance->pop_up_p = pop_up_p;
353  instance->info = info;
354  instance->next = info->instances;
355  info->instances = instance;
356
357  instantiate_widget_instance (instance);
358
359  XtAddCallback (instance->widget, XtNdestroyCallback,
360		 mark_widget_destroyed, (XtPointer)instance);
361  return instance;
362}
363
364static void
365free_widget_instance (instance)
366     widget_instance* instance;
367{
368  lwlib_memset ((void*)instance, 0xDEADBEEF, sizeof (widget_instance));
369  free (instance);
370}
371
372static widget_info *
373#ifdef PROTOTYPES
374get_widget_info (LWLIB_ID id, Boolean remove_p)
375#else
376get_widget_info (id, remove_p)
377     LWLIB_ID id;
378     Boolean remove_p;
379#endif
380{
381  widget_info* info;
382  widget_info* prev;
383  for (prev = NULL, info = all_widget_info;
384       info;
385       prev = info, info = info->next)
386    if (info->id == id)
387     {
388       if (remove_p)
389	 {
390	   if (prev)
391	     prev->next = info->next;
392	   else
393	     all_widget_info = info->next;
394	 }
395      return info;
396     }
397  return NULL;
398}
399
400/* Internal function used by the library dependent implementation to get the
401   widget_value for a given widget in an instance */
402widget_info *
403lw_get_widget_info (id)
404     LWLIB_ID id;
405{
406  return get_widget_info (id, 0);
407}
408
409static widget_instance *
410#ifdef PROTOTYPES
411get_widget_instance (Widget widget, Boolean remove_p)
412#else
413get_widget_instance (widget, remove_p)
414     Widget widget;
415     Boolean remove_p;
416#endif
417{
418  widget_info* info;
419  widget_instance* instance;
420  widget_instance* prev;
421  for (info = all_widget_info; info; info = info->next)
422    for (prev = NULL, instance = info->instances;
423	 instance;
424	 prev = instance, instance = instance->next)
425      if (instance->widget == widget)
426	{
427	  if (remove_p)
428	    {
429	      if (prev)
430		prev->next = instance->next;
431	      else
432		info->instances = instance->next;
433	    }
434	  return instance;
435	}
436  return (widget_instance *) 0;
437}
438
439/* Value is a pointer to the widget_instance corresponding to
440   WIDGET, or null if WIDGET is not a lwlib widget.  */
441
442widget_instance *
443lw_get_widget_instance (widget)
444     Widget widget;
445{
446  return get_widget_instance (widget, False);
447}
448
449static widget_instance*
450#ifdef PROTOTYPES
451find_instance (LWLIB_ID id, Widget parent, Boolean pop_up_p)
452#else
453find_instance (id, parent, pop_up_p)
454     LWLIB_ID id;
455     Widget parent;
456     Boolean pop_up_p;
457#endif
458{
459  widget_info* info = get_widget_info (id, False);
460  widget_instance* instance;
461
462  if (info)
463    for (instance = info->instances; instance; instance = instance->next)
464      if (instance->parent == parent && instance->pop_up_p == pop_up_p)
465	return instance;
466
467  return NULL;
468}
469
470
471/* utility function for widget_value */
472static Boolean
473safe_strcmp (s1, s2)
474     char* s1;
475     char* s2;
476{
477  if (!!s1 ^ !!s2) return True;
478  return (s1 && s2) ? strcmp (s1, s2) : s1 ? False : !!s2;
479}
480
481
482#if 0
483# define EXPLAIN(name, oc, nc, desc, a1, a2)				\
484   printf ("Change: \"%s\"\tmax(%s=%d,%s=%d)\t%s %d %d\n",		\
485	   name,							\
486	   (oc == NO_CHANGE ? "none" :					\
487	    (oc == INVISIBLE_CHANGE ? "invisible" :			\
488	     (oc == VISIBLE_CHANGE ? "visible" :			\
489	      (oc == STRUCTURAL_CHANGE ? "structural" : "???")))),	\
490	   oc,								\
491	   (nc == NO_CHANGE ? "none" :					\
492	    (nc == INVISIBLE_CHANGE ? "invisible" :			\
493	     (nc == VISIBLE_CHANGE ? "visible" :			\
494	      (nc == STRUCTURAL_CHANGE ? "structural" : "???")))),	\
495	   nc, desc, a1, a2)
496#else
497# define EXPLAIN(name, oc, nc, desc, a1, a2)
498#endif
499
500
501static widget_value *
502merge_widget_value (val1, val2, level, change_p)
503     widget_value* val1;
504     widget_value* val2;
505     int level;
506     int *change_p;
507{
508  change_type change, this_one_change;
509  widget_value* merged_next;
510  widget_value* merged_contents;
511
512  if (!val1)
513    {
514      if (val2)
515	{
516	  *change_p = 1;
517	  return copy_widget_value_tree (val2, STRUCTURAL_CHANGE);
518	}
519      else
520	return NULL;
521    }
522  if (!val2)
523    {
524      *change_p = 1;
525      free_widget_value_tree (val1);
526      return NULL;
527    }
528
529  change = NO_CHANGE;
530
531  if (safe_strcmp (val1->name, val2->name))
532    {
533      EXPLAIN (val1->name, change, STRUCTURAL_CHANGE, "name change",
534	       val1->name, val2->name);
535      change = max (change, STRUCTURAL_CHANGE);
536      safe_free_str (val1->name);
537      val1->name = safe_strdup (val2->name);
538    }
539  if (safe_strcmp (val1->value, val2->value))
540    {
541      EXPLAIN (val1->name, change, VISIBLE_CHANGE, "value change",
542	       val1->value, val2->value);
543      change = max (change, VISIBLE_CHANGE);
544      safe_free_str (val1->value);
545      val1->value = safe_strdup (val2->value);
546    }
547  if (safe_strcmp (val1->key, val2->key))
548    {
549      EXPLAIN (val1->name, change, VISIBLE_CHANGE, "key change",
550	       val1->key, val2->key);
551      change = max (change, VISIBLE_CHANGE);
552      safe_free_str (val1->key);
553      val1->key = safe_strdup (val2->key);
554    }
555  if (! EQ (val1->help, val2->help))
556    {
557      EXPLAIN (val1->name, change, VISIBLE_CHANGE, "help change",
558	       val1->help, val2->help);
559      change = max (change, VISIBLE_CHANGE);
560      val1->help = val2->help;
561    }
562  if (val1->enabled != val2->enabled)
563    {
564      EXPLAIN (val1->name, change, VISIBLE_CHANGE, "enablement change",
565	       val1->enabled, val2->enabled);
566      change = max (change, VISIBLE_CHANGE);
567      val1->enabled = val2->enabled;
568    }
569  if (val1->button_type != val2->button_type)
570    {
571      EXPLAIN (val1->name, change, VISIBLE_CHANGE, "button type change",
572	       val1->button_type, val2->button_type);
573      change = max (change, VISIBLE_CHANGE);
574      val1->button_type = val2->button_type;
575    }
576  if (val1->selected != val2->selected)
577    {
578      EXPLAIN (val1->name, change, VISIBLE_CHANGE, "selection change",
579	       val1->selected, val2->selected);
580      change = max (change, VISIBLE_CHANGE);
581      val1->selected = val2->selected;
582    }
583  if (val1->call_data != val2->call_data)
584    {
585      EXPLAIN (val1->name, change, INVISIBLE_CHANGE, "call-data change",
586	       val1->call_data, val2->call_data);
587      change = max (change, INVISIBLE_CHANGE);
588      val1->call_data = val2->call_data;
589    }
590
591  if (level > 0)
592    {
593      merged_contents =
594	merge_widget_value (val1->contents, val2->contents, level - 1,
595			    change_p);
596
597      if (val1->contents && !merged_contents)
598	{
599	  /* This used to say INVISIBLE_CHANGE,
600	     but it is visible and vitally important when
601	     the contents of the menu bar itself are entirely deleted.
602
603	     But maybe it doesn't matter.  This fails to fix the bug.  */
604	  EXPLAIN (val1->name, change, STRUCTURAL_CHANGE, "(contents gone)",
605		   0, 0);
606	  change = max (change, STRUCTURAL_CHANGE);
607	}
608      else if (merged_contents && merged_contents->change != NO_CHANGE)
609	{
610	  EXPLAIN (val1->name, change, INVISIBLE_CHANGE, "(contents change)",
611		   0, 0);
612	  change = max (change, INVISIBLE_CHANGE);
613#if 0 /* This was replaced by the August 9 1996 change in lwlib-Xm.c.  */
614#ifdef USE_MOTIF
615	  change = max (merged_contents->change, change);
616#endif
617#endif
618	}
619
620      val1->contents = merged_contents;
621    }
622
623  this_one_change = change;
624
625  merged_next = merge_widget_value (val1->next, val2->next, level, change_p);
626
627  if (val1->next && !merged_next)
628    {
629      EXPLAIN (val1->name, change, STRUCTURAL_CHANGE, "(following gone)",
630	       0, 0);
631      change = max (change, STRUCTURAL_CHANGE);
632    }
633  else if (merged_next)
634    {
635      if (merged_next->change)
636	EXPLAIN (val1->name, change, merged_next->change, "(following change)",
637		 0, 0);
638      change = max (change, merged_next->change);
639    }
640
641  val1->next = merged_next;
642
643  val1->this_one_change = this_one_change;
644  val1->change = change;
645
646  if (change > NO_CHANGE && val1->toolkit_data)
647    {
648      *change_p = 1;
649      if (val1->free_toolkit_data)
650	XtFree (val1->toolkit_data);
651      val1->toolkit_data = NULL;
652    }
653
654  return val1;
655}
656
657
658/* modifying the widgets */
659static Widget
660name_to_widget (instance, name)
661     widget_instance* instance;
662     char* name;
663{
664  Widget widget = NULL;
665
666  if (!instance->widget)
667    return NULL;
668
669  if (!strcmp (XtName (instance->widget), name))
670    widget = instance->widget;
671  else
672    {
673      int length = strlen (name) + 2;
674      char* real_name = (char *) xmalloc (length);
675      real_name [0] = '*';
676      strcpy (real_name + 1, name);
677
678      widget = XtNameToWidget (instance->widget, real_name);
679
680      free (real_name);
681    }
682  return widget;
683}
684
685static void
686#ifdef PROTOTYPES
687set_one_value (widget_instance* instance, widget_value* val, Boolean deep_p)
688#else
689set_one_value (instance, val, deep_p)
690     widget_instance* instance;
691     widget_value* val;
692     Boolean deep_p;
693#endif
694{
695  Widget widget = name_to_widget (instance, val->name);
696
697  if (widget)
698    {
699#if defined (USE_LUCID)
700      if (lw_lucid_widget_p (instance->widget))
701	xlw_update_one_widget (instance, widget, val, deep_p);
702#endif
703#if defined (USE_MOTIF)
704      if (lw_motif_widget_p (instance->widget))
705	xm_update_one_widget (instance, widget, val, deep_p);
706#endif
707#if defined (USE_XAW)
708      if (lw_xaw_widget_p (instance->widget))
709	xaw_update_one_widget (instance, widget, val, deep_p);
710#endif
711    }
712}
713
714static void
715#ifdef PROTOTYPES
716update_one_widget_instance (widget_instance* instance, Boolean deep_p)
717#else
718update_one_widget_instance (instance, deep_p)
719     widget_instance* instance;
720     Boolean deep_p;
721#endif
722{
723  widget_value *val;
724
725  if (!instance->widget)
726    /* the widget was destroyed */
727    return;
728
729  for (val = instance->info->val; val; val = val->next)
730    if (val->change != NO_CHANGE)
731      set_one_value (instance, val, deep_p);
732}
733
734static void
735#ifdef PROTOTYPES
736update_all_widget_values (widget_info* info, Boolean deep_p)
737#else
738update_all_widget_values (info, deep_p)
739     widget_info* info;
740     Boolean deep_p;
741#endif
742{
743  widget_instance* instance;
744  widget_value* val;
745
746  for (instance = info->instances; instance; instance = instance->next)
747    update_one_widget_instance (instance, deep_p);
748
749  for (val = info->val; val; val = val->next)
750    val->change = NO_CHANGE;
751}
752
753int
754#ifdef PROTOTYPES
755lw_modify_all_widgets (LWLIB_ID id, widget_value* val, Boolean deep_p)
756#else
757lw_modify_all_widgets (id, val, deep_p)
758     LWLIB_ID id;
759     widget_value* val;
760     Boolean deep_p;
761#endif
762{
763  widget_info* info = get_widget_info (id, False);
764  widget_value* new_val;
765  widget_value* next_new_val;
766  widget_value* cur;
767  widget_value* prev;
768  widget_value* next;
769  int		found;
770  int change_p = 0;
771
772  if (!info)
773    return 0;
774
775  for (new_val = val; new_val; new_val = new_val->next)
776    {
777      next_new_val = new_val->next;
778      new_val->next = NULL;
779      found = False;
780      for (prev = NULL, cur = info->val; cur; prev = cur, cur = cur->next)
781	if (!strcmp (cur->name, new_val->name))
782	  {
783	    found = True;
784	    next = cur->next;
785	    cur->next = NULL;
786	    cur = merge_widget_value (cur, new_val, deep_p ? 1000 : 1,
787				      &change_p);
788	    if (prev)
789	      prev->next = cur ? cur : next;
790	    else
791	      info->val = cur ? cur : next;
792	    if (cur)
793	      cur->next = next;
794	    break;
795	  }
796      if (!found)
797	{
798	  /* Could not find it, add it */
799	  if (prev)
800	    prev->next = copy_widget_value_tree (new_val, STRUCTURAL_CHANGE);
801	  else
802	    info->val = copy_widget_value_tree (new_val, STRUCTURAL_CHANGE);
803	  change_p = 1;
804	}
805      new_val->next = next_new_val;
806    }
807
808  update_all_widget_values (info, deep_p);
809  return change_p;
810}
811
812
813/* creating the widgets */
814
815static void
816initialize_widget_instance (instance)
817     widget_instance* instance;
818{
819  widget_value* val;
820
821  for (val = instance->info->val; val; val = val->next)
822    val->change = STRUCTURAL_CHANGE;
823
824  update_one_widget_instance (instance, True);
825
826  for (val = instance->info->val; val; val = val->next)
827    val->change = NO_CHANGE;
828}
829
830
831static widget_creation_function
832find_in_table (type, table)
833     char* type;
834     widget_creation_entry* table;
835{
836  widget_creation_entry* cur;
837  for (cur = table; cur->type; cur++)
838    if (!my_strcasecmp (type, cur->type))
839      return cur->function;
840  return NULL;
841}
842
843static Boolean
844dialog_spec_p (name)
845     char* name;
846{
847  /* return True if name matches [EILPQeilpq][1-9][Bb] or
848     [EILPQeilpq][1-9][Bb][Rr][1-9] */
849  if (!name)
850    return False;
851
852  switch (name [0])
853    {
854    case 'E': case 'I': case 'L': case 'P': case 'Q':
855    case 'e': case 'i': case 'l': case 'p': case 'q':
856      if (name [1] >= '0' && name [1] <= '9')
857	{
858	  if (name [2] != 'B' && name [2] != 'b')
859	    return False;
860	  if (!name [3])
861	    return True;
862	  if ((name [3] == 'T' || name [3] == 't') && !name [4])
863	    return True;
864	  if ((name [3] == 'R' || name [3] == 'r')
865	      && name [4] >= '0' && name [4] <= '9' && !name [5])
866	    return True;
867	  return False;
868	}
869      else
870	return False;
871
872    default:
873      return False;
874    }
875}
876
877static void
878instantiate_widget_instance (instance)
879     widget_instance* instance;
880{
881  widget_creation_function function = NULL;
882
883#if defined (USE_LUCID)
884  if (!function)
885    function = find_in_table (instance->info->type, xlw_creation_table);
886#endif
887#if defined(USE_MOTIF)
888  if (!function)
889    function = find_in_table (instance->info->type, xm_creation_table);
890#endif
891#if defined (USE_XAW)
892  if (!function)
893    function = find_in_table (instance->info->type, xaw_creation_table);
894#endif
895
896  if (!function)
897    {
898      if (dialog_spec_p (instance->info->type))
899	{
900#if defined (USE_LUCID)
901	  /* not yet */
902#endif
903#if defined(USE_MOTIF)
904	  if (!function)
905	    function = xm_create_dialog;
906#endif
907#if defined (USE_XAW)
908	  if (!function)
909	    function = xaw_create_dialog;
910#endif
911	}
912    }
913
914  if (!function)
915    {
916      printf ("No creation function for widget type %s\n",
917	      instance->info->type);
918      abort ();
919    }
920
921  instance->widget = (*function) (instance);
922
923  if (!instance->widget)
924    abort ();
925
926  /*   XtRealizeWidget (instance->widget);*/
927}
928
929void
930lw_register_widget (type, name, id, val, pre_activate_cb,
931		    selection_cb, post_activate_cb, highlight_cb)
932     char* type;
933     char* name;
934     LWLIB_ID id;
935     widget_value* val;
936     lw_callback pre_activate_cb;
937     lw_callback selection_cb;
938     lw_callback post_activate_cb;
939     lw_callback highlight_cb;
940{
941  if (!get_widget_info (id, False))
942    allocate_widget_info (type, name, id, val, pre_activate_cb, selection_cb,
943			  post_activate_cb, highlight_cb);
944}
945
946Widget
947#ifdef PROTOTYPES
948lw_get_widget (LWLIB_ID id, Widget parent, Boolean pop_up_p)
949#else
950lw_get_widget (id, parent, pop_up_p)
951     LWLIB_ID id;
952     Widget parent;
953     Boolean pop_up_p;
954#endif
955{
956  widget_instance* instance;
957
958  instance = find_instance (id, parent, pop_up_p);
959  return instance ? instance->widget : NULL;
960}
961
962Widget
963#ifdef PROTOTYPES
964lw_make_widget (LWLIB_ID id, Widget parent, Boolean pop_up_p)
965#else
966lw_make_widget (id, parent, pop_up_p)
967     LWLIB_ID id;
968     Widget parent;
969     Boolean pop_up_p;
970#endif
971{
972  widget_instance* instance;
973  widget_info* info;
974
975  instance = find_instance (id, parent, pop_up_p);
976  if (!instance)
977    {
978      info = get_widget_info (id, False);
979      if (!info)
980	return NULL;
981      instance = allocate_widget_instance (info, parent, pop_up_p);
982      initialize_widget_instance (instance);
983    }
984  if (!instance->widget)
985    abort ();
986  return instance->widget;
987}
988
989Widget
990#ifdef PROTOTYPES
991lw_create_widget (char* type, char* name, LWLIB_ID id, widget_value* val,
992		  Widget parent, Boolean pop_up_p,
993		  lw_callback pre_activate_cb, lw_callback selection_cb,
994		  lw_callback post_activate_cb, lw_callback highlight_cb)
995#else
996lw_create_widget (type, name, id, val, parent, pop_up_p, pre_activate_cb,
997		  selection_cb, post_activate_cb, highlight_cb)
998     char* type;
999     char* name;
1000     LWLIB_ID id;
1001     widget_value* val;
1002     Widget parent;
1003     Boolean pop_up_p;
1004     lw_callback pre_activate_cb;
1005     lw_callback selection_cb;
1006     lw_callback post_activate_cb;
1007     lw_callback highlight_cb;
1008#endif
1009{
1010  lw_register_widget (type, name, id, val, pre_activate_cb, selection_cb,
1011		      post_activate_cb, highlight_cb);
1012  return lw_make_widget (id, parent, pop_up_p);
1013}
1014
1015
1016/* destroying the widgets */
1017static void
1018destroy_one_instance (instance)
1019     widget_instance* instance;
1020{
1021  /* Remove the destroy callback on the widget; that callback will try to
1022     dereference the instance object (to set its widget slot to 0, since the
1023     widget is dead.)  Since the instance is now dead, we don't have to worry
1024     about the fact that its widget is dead too.
1025
1026     This happens in the Phase2Destroy of the widget, so this callback would
1027     not have been run until arbitrarily long after the instance was freed.
1028   */
1029  if (instance->widget)
1030    XtRemoveCallback (instance->widget, XtNdestroyCallback,
1031		      mark_widget_destroyed, (XtPointer)instance);
1032
1033  if (instance->widget)
1034    {
1035      /* The else are pretty tricky here, including the empty statement
1036	 at the end because it would be very bad to destroy a widget
1037	 twice. */
1038#if defined (USE_LUCID)
1039      if (lw_lucid_widget_p (instance->widget))
1040	xlw_destroy_instance (instance);
1041      else
1042#endif
1043#if defined (USE_MOTIF)
1044      if (lw_motif_widget_p (instance->widget))
1045	xm_destroy_instance (instance);
1046      else
1047#endif
1048#if defined (USE_XAW)
1049      if (lw_xaw_widget_p (instance->widget))
1050	xaw_destroy_instance (instance);
1051      else
1052#endif
1053	/* do not remove the empty statement */
1054	;
1055    }
1056
1057  free_widget_instance (instance);
1058}
1059
1060void
1061lw_destroy_widget (w)
1062     Widget w;
1063{
1064  widget_instance* instance = get_widget_instance (w, True);
1065
1066  if (instance)
1067    {
1068      widget_info *info = instance->info;
1069      /* instance has already been removed from the list; free it */
1070      destroy_one_instance (instance);
1071      /* if there are no instances left, free the info too */
1072      if (!info->instances)
1073	lw_destroy_all_widgets (info->id);
1074    }
1075}
1076
1077void
1078lw_destroy_all_widgets (id)
1079     LWLIB_ID id;
1080{
1081  widget_info* info = get_widget_info (id, True);
1082  widget_instance* instance;
1083  widget_instance* next;
1084
1085  if (info)
1086    {
1087      for (instance = info->instances; instance; )
1088	{
1089	  next = instance->next;
1090	  destroy_one_instance (instance);
1091	  instance = next;
1092	}
1093      free_widget_info (info);
1094    }
1095}
1096
1097void
1098lw_destroy_everything ()
1099{
1100  while (all_widget_info)
1101    lw_destroy_all_widgets (all_widget_info->id);
1102}
1103
1104void
1105lw_destroy_all_pop_ups ()
1106{
1107  widget_info* info;
1108  widget_info* next;
1109  widget_instance* instance;
1110
1111  for (info = all_widget_info; info; info = next)
1112    {
1113      next = info->next;
1114      instance = info->instances;
1115      if (instance && instance->pop_up_p)
1116	lw_destroy_all_widgets (info->id);
1117    }
1118}
1119
1120#ifdef USE_MOTIF
1121extern Widget first_child (/* Widget */);	/* garbage */
1122#endif
1123
1124Widget
1125lw_raise_all_pop_up_widgets ()
1126{
1127  widget_info* info;
1128  widget_instance* instance;
1129  Widget result = NULL;
1130
1131  for (info = all_widget_info; info; info = info->next)
1132    for (instance = info->instances; instance; instance = instance->next)
1133      if (instance->pop_up_p)
1134	{
1135	  Widget widget = instance->widget;
1136	  if (widget)
1137	    {
1138	      if (XtIsManaged (widget)
1139#ifdef USE_MOTIF
1140		  /* What a complete load of crap!!!!
1141		     When a dialogShell is on the screen, it is not managed!
1142		   */
1143		  || (lw_motif_widget_p (instance->widget) &&
1144		      XtIsManaged (first_child (widget)))
1145#endif
1146		  )
1147		{
1148		  if (!result)
1149		    result = widget;
1150		  XMapRaised (XtDisplay (widget), XtWindow (widget));
1151		}
1152	    }
1153	}
1154  return result;
1155}
1156
1157static void
1158#ifdef PROTOTYPES
1159lw_pop_all_widgets (LWLIB_ID id, Boolean up)
1160#else
1161lw_pop_all_widgets (id, up)
1162     LWLIB_ID id;
1163     Boolean up;
1164#endif
1165{
1166  widget_info* info = get_widget_info (id, False);
1167  widget_instance* instance;
1168
1169  if (info)
1170    for (instance = info->instances; instance; instance = instance->next)
1171      if (instance->pop_up_p && instance->widget)
1172	{
1173#if defined (USE_LUCID)
1174	  if (lw_lucid_widget_p (instance->widget))
1175	    {
1176	      XtRealizeWidget (instance->widget);
1177	      xlw_pop_instance (instance, up);
1178	    }
1179#endif
1180#if defined (USE_MOTIF)
1181	  if (lw_motif_widget_p (instance->widget))
1182	    {
1183	      XtRealizeWidget (instance->widget);
1184	      xm_pop_instance (instance, up);
1185	    }
1186#endif
1187#if defined (USE_XAW)
1188	  if (lw_xaw_widget_p (instance->widget))
1189	    {
1190	      XtRealizeWidget (XtParent (instance->widget));
1191	      XtRealizeWidget (instance->widget);
1192	      xaw_pop_instance (instance, up);
1193	    }
1194#endif
1195	}
1196}
1197
1198void
1199lw_pop_up_all_widgets (id)
1200     LWLIB_ID id;
1201{
1202  lw_pop_all_widgets (id, True);
1203}
1204
1205void
1206lw_pop_down_all_widgets (id)
1207     LWLIB_ID id;
1208{
1209  lw_pop_all_widgets (id, False);
1210}
1211
1212void
1213lw_popup_menu (widget, event)
1214     Widget widget;
1215     XEvent *event;
1216{
1217#if defined (USE_LUCID)
1218  if (lw_lucid_widget_p (widget))
1219    xlw_popup_menu (widget, event);
1220#endif
1221#if defined (USE_MOTIF)
1222  if (lw_motif_widget_p (widget))
1223    xm_popup_menu (widget, event);
1224#endif
1225#if defined (USE_XAW)
1226  if (lw_xaw_widget_p (widget))
1227    xaw_popup_menu (widget, event);
1228#endif
1229}
1230
1231/* get the values back */
1232static Boolean
1233get_one_value (instance, val)
1234     widget_instance* instance;
1235     widget_value* val;
1236{
1237  Widget widget = name_to_widget (instance, val->name);
1238
1239  if (widget)
1240    {
1241#if defined (USE_LUCID)
1242      if (lw_lucid_widget_p (instance->widget))
1243	xlw_update_one_value (instance, widget, val);
1244#endif
1245#if defined (USE_MOTIF)
1246      if (lw_motif_widget_p (instance->widget))
1247	xm_update_one_value (instance, widget, val);
1248#endif
1249#if defined (USE_XAW)
1250      if (lw_xaw_widget_p (instance->widget))
1251	xaw_update_one_value (instance, widget, val);
1252#endif
1253      return True;
1254    }
1255  else
1256    return False;
1257}
1258
1259Boolean
1260lw_get_some_values (id, val_out)
1261     LWLIB_ID id;
1262     widget_value* val_out;
1263{
1264  widget_info* info = get_widget_info (id, False);
1265  widget_instance* instance;
1266  widget_value* val;
1267  Boolean result = False;
1268
1269  if (!info)
1270    return False;
1271
1272  instance = info->instances;
1273  if (!instance)
1274    return False;
1275
1276  for (val = val_out; val; val = val->next)
1277    if (get_one_value (instance, val))
1278      result = True;
1279
1280  return result;
1281}
1282
1283widget_value*
1284lw_get_all_values (id)
1285     LWLIB_ID id;
1286{
1287  widget_info* info = get_widget_info (id, False);
1288  widget_value* val = info->val;
1289  if (lw_get_some_values (id, val))
1290    return val;
1291  else
1292    return NULL;
1293}
1294
1295/* internal function used by the library dependent implementation to get the
1296   widget_value for a given widget in an instance */
1297widget_value*
1298lw_get_widget_value_for_widget (instance, w)
1299     widget_instance* instance;
1300     Widget w;
1301{
1302  char* name = XtName (w);
1303  widget_value* cur;
1304  for (cur = instance->info->val; cur; cur = cur->next)
1305    if (!strcmp (cur->name, name))
1306      return cur;
1307  return NULL;
1308}
1309
1310/* update other instances value when one thing changed */
1311
1312/* To forbid recursive calls */
1313static Boolean lwlib_updating;
1314
1315/* This function can be used as a an XtCallback for the widgets that get
1316  modified to update other instances of the widgets.  Closure should be the
1317  widget_instance. */
1318void
1319lw_internal_update_other_instances (widget, closure, call_data)
1320     Widget widget;
1321     XtPointer closure;
1322     XtPointer call_data;
1323{
1324  widget_instance* instance = (widget_instance*)closure;
1325  char* name = XtName (widget);
1326  widget_info* info;
1327  widget_instance* cur;
1328  widget_value* val;
1329
1330  /* Avoid possibly infinite recursion.  */
1331  if (lwlib_updating)
1332    return;
1333
1334  /* protect against the widget being destroyed */
1335  if (XtWidgetBeingDestroyedP (widget))
1336    return;
1337
1338  /* Return immediately if there are no other instances */
1339  info = instance->info;
1340  if (!info->instances->next)
1341    return;
1342
1343  lwlib_updating = True;
1344
1345  for (val = info->val; val && strcmp (val->name, name); val = val->next);
1346
1347  if (val && get_one_value (instance, val))
1348    for (cur = info->instances; cur; cur = cur->next)
1349      if (cur != instance)
1350	set_one_value (cur, val, True);
1351
1352  lwlib_updating = False;
1353}
1354
1355
1356/* get the id */
1357
1358LWLIB_ID
1359lw_get_widget_id (w)
1360     Widget w;
1361{
1362  widget_instance* instance = get_widget_instance (w, False);
1363
1364  return instance ? instance->info->id : 0;
1365}
1366
1367/* set the keyboard focus */
1368void
1369lw_set_keyboard_focus (parent, w)
1370     Widget parent;
1371     Widget w;
1372{
1373#if defined (USE_MOTIF)
1374  xm_set_keyboard_focus (parent, w);
1375#else
1376  XtSetKeyboardFocus (parent, w);
1377#endif
1378}
1379
1380/* Show busy */
1381static void
1382#ifdef PROTOTYPES
1383show_one_widget_busy (Widget w, Boolean flag)
1384#else
1385show_one_widget_busy (w, flag)
1386     Widget w;
1387     Boolean flag;
1388#endif
1389{
1390  Pixel foreground = 0;
1391  Pixel background = 1;
1392  Widget widget_to_invert = XtNameToWidget (w, "*sheet");
1393  if (!widget_to_invert)
1394    widget_to_invert = w;
1395
1396  XtVaGetValues (widget_to_invert,
1397		 XtNforeground, &foreground,
1398		 XtNbackground, &background,
1399		 NULL);
1400  XtVaSetValues (widget_to_invert,
1401		 XtNforeground, background,
1402		 XtNbackground, foreground,
1403		 NULL);
1404}
1405
1406void
1407#ifdef PROTOTYPES
1408lw_show_busy (Widget w, Boolean busy)
1409#else
1410lw_show_busy (w, busy)
1411     Widget w;
1412     Boolean busy;
1413#endif
1414{
1415  widget_instance* instance = get_widget_instance (w, False);
1416  widget_info* info;
1417  widget_instance* next;
1418
1419  if (instance)
1420    {
1421      info = instance->info;
1422      if (info->busy != busy)
1423	{
1424	  for (next = info->instances; next; next = next->next)
1425	    if (next->widget)
1426	      show_one_widget_busy (next->widget, busy);
1427	  info->busy = busy;
1428	}
1429    }
1430}
1431
1432/* This hack exists because Lucid/Athena need to execute the strange
1433   function below to support geometry management. */
1434void
1435#ifdef PROTOTYPES
1436lw_refigure_widget (Widget w, Boolean doit)
1437#else
1438lw_refigure_widget (w, doit)
1439     Widget w;
1440     Boolean doit;
1441#endif
1442{
1443#if defined (USE_XAW)
1444  XawPanedSetRefigureMode (w, doit);
1445#endif
1446#if defined (USE_MOTIF)
1447  if (doit)
1448    XtManageChild (w);
1449  else
1450    XtUnmanageChild (w);
1451#endif
1452}
1453
1454/* Toolkit independent way of determining if an event window is in the
1455   menubar. */
1456Boolean
1457lw_window_is_in_menubar (win, menubar_widget)
1458     Window win;
1459     Widget menubar_widget;
1460{
1461  return menubar_widget
1462#if defined (USE_LUCID)
1463      && XtWindow (menubar_widget) == win;
1464#endif
1465#if defined (USE_MOTIF)
1466      && ((XtWindow (menubar_widget) == win)
1467	  || (XtWindowToWidget (XtDisplay (menubar_widget), win)
1468	      && (XtParent (XtWindowToWidget (XtDisplay (menubar_widget), win))
1469		  == menubar_widget)));
1470#endif
1471}
1472
1473/* Motif hack to set the main window areas. */
1474void
1475lw_set_main_areas (parent, menubar, work_area)
1476     Widget parent;
1477     Widget menubar;
1478     Widget work_area;
1479{
1480#if defined (USE_MOTIF)
1481  xm_set_main_areas (parent, menubar, work_area);
1482#endif
1483}
1484
1485/* Manage resizing for Motif.  This disables resizing when the menubar
1486   is about to be modified. */
1487void
1488#ifdef PROTOTYPES
1489lw_allow_resizing (Widget w, Boolean flag)
1490#else
1491lw_allow_resizing (w, flag)
1492     Widget w;
1493     Boolean flag;
1494#endif
1495{
1496#if defined (USE_MOTIF)
1497  xm_manage_resizing (w, flag);
1498#endif
1499}
1500
1501
1502/* Value is non-zero if LABEL is a menu separator.  If it is, *TYPE is
1503   set to an appropriate enumerator of type enum menu_separator.
1504   MOTIF_P non-zero means map separator types not supported by Motif
1505   to similar ones that are supported.  */
1506
1507int
1508lw_separator_p (label, type, motif_p)
1509     char *label;
1510     enum menu_separator *type;
1511     int motif_p;
1512{
1513  int separator_p = 0;
1514
1515  if (strlen (label) >= 3
1516      && bcmp (label, "--:", 3) == 0)
1517    {
1518      static struct separator_table
1519      {
1520	char *name;
1521	enum menu_separator type;
1522      }
1523      separator_names[] =
1524      {
1525	{"space",		      SEPARATOR_NO_LINE},
1526	{"noLine",		      SEPARATOR_NO_LINE},
1527	{"singleLine",		      SEPARATOR_SINGLE_LINE},
1528	{"doubleLine",		      SEPARATOR_DOUBLE_LINE},
1529	{"singleDashedLine",	      SEPARATOR_SINGLE_DASHED_LINE},
1530	{"doubleDashedLine",	      SEPARATOR_DOUBLE_DASHED_LINE},
1531	{"shadowEtchedIn",	      SEPARATOR_SHADOW_ETCHED_IN},
1532	{"shadowEtchedOut",	      SEPARATOR_SHADOW_ETCHED_OUT},
1533	{"shadowEtchedInDash",	      SEPARATOR_SHADOW_ETCHED_IN_DASH},
1534	{"shadowEtchedOutDash",	      SEPARATOR_SHADOW_ETCHED_OUT_DASH},
1535	{"shadowDoubleEtchedIn",      SEPARATOR_SHADOW_DOUBLE_ETCHED_IN},
1536	{"shadowDoubleEtchedOut",     SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT},
1537	{"shadowDoubleEtchedInDash",  SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH},
1538	{"shadowDoubleEtchedOutDash", SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH},
1539	{0,0}
1540      };
1541
1542      int i;
1543
1544      label += 3;
1545      for (i = 0; separator_names[i].name; ++i)
1546	if (strcmp (label, separator_names[i].name) == 0)
1547	  {
1548	    separator_p = 1;
1549	    *type = separator_names[i].type;
1550
1551	    /* If separator type is not supported under Motif,
1552	       use a similar one.  */
1553	    if (motif_p && *type >= SEPARATOR_SHADOW_DOUBLE_ETCHED_IN)
1554	      *type -= 4;
1555	    break;
1556	  }
1557    }
1558  else if (strlen (label) > 3
1559	   && bcmp (label, "--", 2) == 0
1560	   && label[2] != '-')
1561    {
1562      /* Alternative, more Emacs-style names.  */
1563      static struct separator_table
1564      {
1565	char *name;
1566	enum menu_separator type;
1567      }
1568      separator_names[] =
1569      {
1570	{"space",			 SEPARATOR_NO_LINE},
1571	{"no-line",			 SEPARATOR_NO_LINE},
1572	{"single-line",			 SEPARATOR_SINGLE_LINE},
1573	{"double-line",			 SEPARATOR_DOUBLE_LINE},
1574	{"single-dashed-line",		 SEPARATOR_SINGLE_DASHED_LINE},
1575	{"double-dashed-line",		 SEPARATOR_DOUBLE_DASHED_LINE},
1576	{"shadow-etched-in",		 SEPARATOR_SHADOW_ETCHED_IN},
1577	{"shadow-etched-out",		 SEPARATOR_SHADOW_ETCHED_OUT},
1578	{"shadow-etched-in-dash",	 SEPARATOR_SHADOW_ETCHED_IN_DASH},
1579	{"shadow-etched-out-dash",	 SEPARATOR_SHADOW_ETCHED_OUT_DASH},
1580	{"shadow-double-etched-in",	 SEPARATOR_SHADOW_DOUBLE_ETCHED_IN},
1581	{"shadow-double-etched-out",     SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT},
1582	{"shadow-double-etched-in-dash", SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH},
1583	{"shadow-double-etched-out-dash",SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH},
1584	{0,0}
1585      };
1586
1587      int i;
1588
1589      label += 2;
1590      for (i = 0; separator_names[i].name; ++i)
1591	if (strcmp (label, separator_names[i].name) == 0)
1592	  {
1593	    separator_p = 1;
1594	    *type = separator_names[i].type;
1595
1596	    /* If separator type is not supported under Motif,
1597	       use a similar one.  */
1598	    if (motif_p && *type >= SEPARATOR_SHADOW_DOUBLE_ETCHED_IN)
1599	      *type -= 4;
1600	    break;
1601	  }
1602    }
1603  else
1604    {
1605      /* Old-style separator, maybe.  It's a separator if it contains
1606	 only dashes.  */
1607      while (*label == '-')
1608	++label;
1609      separator_p = *label == 0;
1610      *type = SEPARATOR_SHADOW_ETCHED_IN;
1611    }
1612
1613  return separator_p;
1614}
1615
1616/* arch-tag: 3d730f36-a441-4a71-9971-48ef3b5a4d9f
1617   (do not change this comment) */
1618