/* $Id: ApplCtx.c,v 1.7 1991/06/26 07:02:00 altenhof Exp $ */
 
#define NEED_RESOURCES
#define NEED_EVENTS
 
#include "Xlibint.h"
#include "XmuXlibint.h"
#include "alt.XmuX.h"
#include "Xatom.h"
 
#define MAX_IDX MAXSOCKS
 
#define TOPLEVELID( idx , i ) \
    ( _muxApplCtx[ idx ]->toplevels[ i ].window )
 
#define TOPLEVELMASK( idx , i ) \
    ( _muxApplCtx[ idx ]->toplevels[ i ].mux_mask )
 
#ifndef XmuXMask
#define XmuXMask ( PropertyChangeMask )
#endif
 
/*
 * The next two data structures describe what we call the "application
 * context". In the XmuXlib environment, application means one client
 * connection to the server (i.e. an "X application" may define more than one
 * "XmuX application"). What we duplicate (or multiplex) are such XmuX
 * applications. The "appplication context" describes some properties that
 * are "shared" among the associated (client and mux'ed) server connections:
 * Whether we are in ChalkpassMode or AnarchyMode, which connection has the
 * chalk... In the older version this information was stored in the client's
 * server info. These structures are private to this file !
 */
 
/*
 * This data structure is used to store the names of the associated hosts
 * Will be used to pass the chalk and to keep the MUX_HOSTS property uptodate
 */
 
typedef struct _MUXHostInfo {
 
  int fd;			/* the socket number for this host */
  char *hostname;		/* the host name */
  struct _MUXHostInfo *next;
 
} MUXHostInfo;
 
/*****************************************************************
 * The "application context" :
 *   Contents :
 *     - communication mode ( Anarchy / Chalkpassing )
 *     - chalk holder connection ( if we're in ChalkPassMode )
 *     - the client fd (socket) this context belongs to
 *     - queue length of the "must do" event queue
 *     - pointers to the head and the tail of the "must do" event queue
 *     - the number and
 *     - the array of top-level window infos
 *     - pointer to the host list
 *
 * The "must do" event queue holds those events that trigger some
 * XmuX-specific work,e.g. we "must multiplex" this application or we "must
 * pass the chalk". Top-level infos store the XmuX-specific event masks for
 * top-level windows: we "add" PropertyChangeMask to all top-level windows to
 * solicit notification about changes of the XMUX_MUX property.
 *****************************************************************/
 
typedef struct _MUXApplCtx {
 
  int commMode, ChalkHolder, client_fd, qlen;
  _XQEvent *head, *tail;
  int ntoplevels;
  MUXToplevelInfo *toplevels;
  MUXHostInfo *hosts;
 
} MUXApplCtx, *MUXApplCtxPtr;
 
/* private array storing pointers to application contexts */
static MUXApplCtxPtr *_muxApplCtx;
 
/* what's the max index of the _muxApplCtx array */
static int _lastApplIdx;
 
/* pointer to unused queue events */
static _XQEvent *_XmuXqfree;
 
/*
 * next two routines handle "applications indices",i.e. indices in the
 * application context array.
 */
 
static void
SetApplicationIndex (dpy, idx)
  Display *dpy;
  int idx;
{
 
#ifdef DEBUG
  CheckDisplay (dpy, "SetApplicationIndex");
#endif
 
  if (idx > _lastApplIdx)
    XmuXErrorF ("SetApplCtx: invalid index (%d > %d)!\n",
		idx, _lastApplIdx);
  else
    ConnectionTranslation[dpy->fd]->index = idx;
}
 
static int
GetApplicationIndex (dpy)
  Display *dpy;
{
  register int appl_idx;
 
#ifdef DEBUG
  CheckDisplay (dpy, "GetApplicationIndex");
#endif
 
  appl_idx = ConnectionTranslation[dpy->fd]->index;
 
#ifdef DEBUG
  if (appl_idx < 0 || appl_idx > _lastApplIdx)
    XmuXFatalError ("GetApplIdx: index out of range!\n");
#endif
 
  return (appl_idx);
}
 
/* application context initialisation routine */
void
XmuXInitApplCtx ()
{
  register int i;
 
  _XmuXqfree = NULL;
  _lastApplIdx = MAX_IDX;
  _muxApplCtx =
    (MUXApplCtxPtr *) Xmalloc (_lastApplIdx * sizeof (MUXApplCtxPtr));
  if (_muxApplCtx == (MUXApplCtxPtr *) NULL)
    XmuXFatalError ("InitApplCtx: Can't allocate!\n");
 
  for (i = 0; i < _lastApplIdx; i++)
    _muxApplCtx[i] = (MUXApplCtxPtr) NULL;
}
 
/*
 * XmuXCreateApplCtx - creates a new application context and fills it with
 *                     default values
 */
 
void
XmuXCreateApplCtx (server)
  MUXServerPtr server;
{
  register int curr_idx;
 
  /* find a free slot */
  curr_idx = 0;
  while (_muxApplCtx[curr_idx] != (MUXApplCtxPtr) NULL) {
    if (++curr_idx > _lastApplIdx) {
      /* out of bounds; realloc array */
      _muxApplCtx = (MUXApplCtxPtr *)
	Xrealloc ((char *) _muxApplCtx,
		  curr_idx * sizeof (MUXApplCtxPtr));
 
      if (_muxApplCtx == (MUXApplCtxPtr *) NULL)
	XmuXFatalError ("CreateApplCtx: Can't realloc!\n");
 
      _lastApplIdx = curr_idx;
      break;
    }
  }
 
  /* now, curr_idx points to a free slot */
  _muxApplCtx[curr_idx] =
    (MUXApplCtxPtr) Xmalloc (sizeof (MUXApplCtx));
  /* malloc failed ? */
  if (_muxApplCtx[curr_idx] == (MUXApplCtxPtr) NULL)
    XmuXFatalError ("CreateApplCtx: Can't alloc!\n");
 
  /* establish links it to the appropriate server info */
  server->index = curr_idx;
  _muxApplCtx[curr_idx]->ChalkHolder =
    _muxApplCtx[curr_idx]->client_fd = server->xdpy->fd;
 
  _muxApplCtx[curr_idx]->commMode = ChalkMode;
  _muxApplCtx[curr_idx]->qlen = 0;
  _muxApplCtx[curr_idx]->head =
    _muxApplCtx[curr_idx]->tail = NULL;
  _muxApplCtx[curr_idx]->ntoplevels = 0;
  _muxApplCtx[curr_idx]->toplevels = NULL;
  _muxApplCtx[curr_idx]->hosts = NULL;
 
}
 
/*
 * XmuXFreeApplCtx -- free the application context dpy points to
 * We create application ctx for mux'ed connections too (although we don't
 * need them), because we don't want two versions of XOpenDisplay, and
 * immediately free it by calling this routine
 */
 
void
XmuXFreeApplCtx (dpy)
  Display *dpy;
{
  register int appl_idx, i;
  void XmuXFreeQ (), XmuXFreeHosts ();
  register MUXApplCtxPtr appl_ctx;
 
  appl_idx = GetApplicationIndex (dpy);
  appl_ctx = _muxApplCtx[appl_idx];
 
  SetApplicationIndex (dpy, -1);
  /* free everything */
  XmuXFreeHosts (appl_ctx->hosts);
  XmuXFreeQ (appl_ctx->head);
  if (appl_ctx->ntoplevels > 0)
    Xfree ((char *) appl_ctx->toplevels);
 
  _muxApplCtx[appl_idx] = (MUXApplCtxPtr) NULL;
 
}
 
/*
 * XmuXSetApplCtx -- link dpy1 to dpy2's application context
 */
 
void
XmuXSetApplCtx (dpy1, dpy2)
  Display *dpy1, *dpy2;
{
  SetApplicationIndex (dpy1, GetApplicationIndex (dpy2));
}
 
/* routines dealing with communication mode / chalk passing */
 
/*
 * XmuXCommunicationMode -- Anarchy or ChalkPass ?
 */
int
XmuXCommunicationMode (dpy)
  Display *dpy;
{
 
  return (_muxApplCtx[GetApplicationIndex (dpy)]->commMode);
}
 
/*
 * XmuXChalkHolder -- which connection has the chalk?  If we're in
 * anarchy mode, we'll return the calling dpy's fd
 */
 
int
XmuXChalkHolder (dpy)
  Display *dpy;
{
  register int appl_idx;
 
  appl_idx = GetApplicationIndex (dpy);
 
  if (_muxApplCtx[appl_idx]->commMode == AnarchyMode)
    /* calling dpy is "chalk holder" */
    return (dpy->fd);
  else
    return (_muxApplCtx[appl_idx]->ChalkHolder);
}
 
/*
 * XmuXSetChalkHolder -- register calling dpy as the new chalk holder
 * Returns the connection number of the new chalk holder, or -1 if we
 * are in anarchy mode
 */
 
int
XmuXSetChalkHolder (dpy, index)
  Display *dpy;
  unsigned int index;
{
  register int i, idx;
  register MUXHostInfo *host;
 
#ifdef DEBUG
  CheckDisplay (dpy, "ApplSetChalkHolder");
#endif
 
  idx = GetApplicationIndex (dpy);
  if (_muxApplCtx[idx]->commMode == AnarchyMode)
    return (-1);
  else {
    for (i = 0, host = _muxApplCtx[idx]->hosts;
	 i < index && host != NULL;
	 i++, host = host->next)
      /* just advance */
      ;
    if (host != NULL) {
      _muxApplCtx[idx]->ChalkHolder = host->fd;
      /* change the toplevel property */
      if (!IsClientDisplay (dpy))
	dpy = XmuXPrimaryDisplayFromDisplay (dpy);
      for (i = 0; i < _muxApplCtx[idx]->ntoplevels; i++)
	XChangeProperty (dpy,
			 TOPLEVELID (idx, i),
			 XmuXServerAtom (dpy, _MUX_CHALKHOLDER),
			 XA_STRING, 8, PropModeReplace,
			 host->hostname,
			 strlen (host->hostname) + 1
	  );
      return (host->fd);
    }
    else
      return (-1);
  }
}
 
/* routines handling the "must do" event queue */
 
/*
 * XmuXQLength
 */
 
int
XmuXQLength (dpy)
  Display *dpy;
{
  return (_muxApplCtx[GetApplicationIndex (dpy)]->qlen);
}
 
/*
 * XmuXEnq -- enqueue an event in application's "must do" event queue
 * Really enqueues a duplicate of the event.
 */
 
void
XmuXEnq (dpy, event)
  Display *dpy;
  XEvent *event;
{
  register _XQEvent *qevent;
  register int idx;
 
  idx = GetApplicationIndex (dpy, False);
 
  /* code is stolen from _XEnq */
 
  /* NOSTRICT */
  if (qevent = _XmuXqfree) {
    /* if _XmuXqfree is non-NULL do this, else malloc a new one. */
    _XmuXqfree = qevent->next;
  }
  else if ((qevent =
	    (_XQEvent *) Xmalloc ((unsigned) sizeof (_XQEvent))) == NULL) {
    /* Malloc call failed! */
    errno = ENOMEM;
    (*_XIOErrorFunction) (dpy);
  }
  qevent->next = NULL;
 
  /* copy event values */
  bcopy ((char *) event, (char *) &qevent->event, sizeof (XEvent));
 
  /* enque it */
  if (_muxApplCtx[idx]->tail)
    _muxApplCtx[idx]->tail->next = qevent;
  else
    _muxApplCtx[idx]->head = qevent;
 
  _muxApplCtx[idx]->tail = qevent;
  _muxApplCtx[idx]->qlen++;
}
 
/*
 * XmuXDeq - reverse of XmuXEnq
 */
 
_XQEvent *
XmuXDeq (dpy)
  Display *dpy;
{
  register _XQEvent *qevent;
  register int idx;
 
  idx = GetApplicationIndex (dpy);
 
  if (_muxApplCtx[idx]->head == NULL)
    return (NULL);
  else {
    qevent = _muxApplCtx[idx]->head;
    _muxApplCtx[idx]->head =
      _muxApplCtx[idx]->head->next;
    _muxApplCtx[idx]->qlen--;
    if (_muxApplCtx[idx]->head == NULL)
      _muxApplCtx[idx]->tail = NULL;
    qevent->next = NULL;
    return (qevent);
  }
}
 
/*
 * XmuXFreeQEvent -- free an event by appending it to the list of
 * unused events.
 */
 
void
XmuXFreeQEvent (qevent)
  _XQEvent *qevent;
{
  qevent->next = _XmuXqfree;
  _XmuXqfree = qevent;
}
 
 
/*
 * XmuXFreeQ  - free application's queue of events
 */
 
void
XmuXFreeQ (queue)
  _XQEvent *queue;
{
  register _XQEvent *qelt = queue;
 
  while (qelt) {
    register _XQEvent *qnext = qelt->next;
    XmuXFreeQEvent (qelt);
    qelt = qnext;
  }
  queue = NULL;
  return;
}
 
/* routines dealing with top-level infos */
 
/*
 * ToplevelMask -- find and return window's mux_mask
 */
 
static Mask
ToplevelMask (dpy, window)
  Display *dpy;
  Window window;
{
  register int i, appl_idx;
  appl_idx = GetApplicationIndex (dpy);
 
  for (i = 0;
       i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    if (TOPLEVELID (appl_idx, i) == window)
      return (TOPLEVELMASK (appl_idx, i));
 
  return (0L);
}
 
/*
 * XmuXIsToplevel -- Is window an top-level window?
 */
 
Bool
XmuXIsToplevel (dpy, window)
  Display *dpy;
  Window window;
{
  register int i, appl_idx;
 
  appl_idx = GetApplicationIndex (dpy);
 
  /* if called with the wrong display */
  if (!IsClientDisplay (dpy))
    window = XmuXMapID (dpy, window, FromServer);
 
  for (i = 0;
       i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    if (TOPLEVELID (appl_idx, i) == window)
      break;
 
  return (i < _muxApplCtx[appl_idx]->ntoplevels);
}
 
/*
 * XmuXAddToplevelInfo -- Insert a new info in application's top-level
 * array
 */
 
void
XmuXAddToplevelInfo (dpy, window, mux_mask)
  Display *dpy;
  Window window;
  long mux_mask;
{
  register MUXToplevelInfo *toplevels;
  register int i, appl_idx, ntops;
  void XmuXAddHosts ();
 
  appl_idx = GetApplicationIndex (dpy);
 
  toplevels = _muxApplCtx[appl_idx]->toplevels;
  ntops = _muxApplCtx[appl_idx]->ntoplevels + 1;
 
  if (!toplevels)
    /* first time */
    toplevels =
      (MUXToplevelInfo *) Xmalloc (ntops * sizeof (MUXToplevelInfo));
  else
    toplevels = (MUXToplevelInfo *)
      Xrealloc ((char *) toplevels,
		ntops * sizeof (MUXToplevelInfo));
 
  if (!toplevels)
    XmuXFatalError ("XmuXAddToplevel: Out of memory (appl %d)!\n",
		    appl_idx);
 
  toplevels[ntops - 1].window = window;
  toplevels[ntops - 1].mux_mask = mux_mask;
 
  XmuXdebug (debug_resources, "Top level 0x%lx mask=%u added\n",
	     window, mux_mask);
  _muxApplCtx[appl_idx]->toplevels = toplevels;
  _muxApplCtx[appl_idx]->ntoplevels = ntops;
 
  /* force installation of XmuX-specific properties */
  XmuXAddHosts (dpy, (char **) NULL, (int *) NULL, 0);
  XmuXSetChalkHolder (dpy, XmuXChalkHolder (dpy));
}
 
void
XmuXRemoveToplevelInfo (dpy, window)
  Display *dpy;
  Window window;
{
  register MUXToplevelInfo *toplevels;
  register int i, j, appl_idx, ntops;
 
  appl_idx = GetApplicationIndex (dpy);
 
  toplevels = _muxApplCtx[appl_idx]->toplevels;
  ntops = _muxApplCtx[appl_idx]->ntoplevels;
 
  /* find the window and mark it */
  for (i = 0; i < ntops; i++) {
    if (toplevels[i].window == window)
      toplevels[i].window = None;
  }
 
  /*
   * array will shrink --> copy in place and realloc array
   */
  for (j = 0, i = 0; i < ntops; i++)
    if (toplevels[i].window != None)
      toplevels[j++].window = toplevels[i].window;
    else
      ntops--;
  toplevels = (MUXToplevelInfo *)
    Xrealloc ((char *) toplevels,
	      ntops * sizeof (MUXToplevelInfo));
 
  _muxApplCtx[appl_idx]->toplevels = toplevels;
  _muxApplCtx[appl_idx]->ntoplevels = ntops;
}
 
/*
 * XmuXChangeToplevelInfo -- change mask of window's top-level info
 */
 
void
XmuXChangeToplevelInfo (dpy, window, mux_mask)
  Display *dpy;
  Window window;
  long mux_mask;
{
  register int i, appl_idx;
 
  appl_idx = GetApplicationIndex (dpy);
 
  for (i = 0;
       i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    /* find the window */
    if (TOPLEVELID (appl_idx, i) == window)
      break;
 
  /* error check */
  if (i == _muxApplCtx[appl_idx]->ntoplevels)
    XmuXFatalError ("XmuXChToplev: Window 0x%lx not found (# %d)!\n",
		    window, appl_idx);
  _muxApplCtx[appl_idx]->toplevels[i].mux_mask = mux_mask;
}
 
/*
 * XmuXSpecificEvent -- Used to filter XmuX-specific events.  In fact,
 * returns whether window's mux_mask contains one of the masks in XmuXMask
 * (== PropertyChangeMask). So, it should only be called in the
 * appropriate cases in _XWireToEvent (it's "context free": you do not
 * pass an event mask)!
 */
Bool
XmuXSpecificEvent (dpy, window)
  Display *dpy;
  Window window;
{
  /* just to be sure */
  if (!IsClientDisplay (dpy)) {
    window = XmuXMapID (dpy, window, FromServer);
    dpy = XmuXPrimaryDisplayFromDisplay (dpy);
  }
  return (ToplevelMask (dpy, window) & XmuXMask);
}
void
XmuXUpdateStatus (dpy, window)
 
Display *dpy;
Window window;
 
{ unsigned char *data = NULL;
  int ret_type, ret_format, ret_length, 
      bytes_after, i, idx;
 
  idx = GetApplicationIndex (dpy);
  XGetWindowProperty (dpy, 
		      XmuXMapID(dpy, window, FromClient), 
		      XmuXServerAtom (dpy, _MUX_STATUS),
		      0,	/* offset */
		      8192L,	/* data length */
		      False,	/* delete ? */
		      XA_STRING, /* prop type */
		      &ret_type,	/* returned prop type */
		      &ret_format,	/* returned prop format */
		      &ret_length,	/* returned prop length (items) */
		      &bytes_after,	/* remaining bytes */
		      &data	/* returned data */
		      );
  if (!IsClientDisplay (dpy))
    dpy = XmuXPrimaryDisplayFromDisplay (dpy);
  for (i = 0; i < _muxApplCtx[idx]->ntoplevels; i++)
    XChangeProperty (dpy,
		     TOPLEVELID (idx, i),
		     XmuXServerAtom (dpy, _MUX_STATUS),
		     XA_STRING, 8, PropModeReplace,
		     data,
		     ret_length
		     );
}
 
/* routines dealing with the host list */
 
/*
 * XmuXAddHosts -- add new host names to the list                 AND
 * change the corresponding property
 */
 
void
XmuXAddHosts (dpy, hostnames, fds, n)
  Display *dpy;
  char **hostnames;
  int *fds, n;
{
  register int i, num_hosts, prop_length, appl_idx;
  register char *prop_data, *bufptr;
  register MUXHostInfo *new_host;
 
  appl_idx = GetApplicationIndex (dpy);
  prop_length = 0;
  num_hosts = n;
  XmuXIncServers (n);
 
  /*
   * we'll change the XMUX_HOSTS property here which contains the names
   * of all displays (!) separated by semicolons don't rely on old value,
   * construct the new from application's host list
   */
  for (new_host = _muxApplCtx[appl_idx]->hosts;
       new_host != NULL; new_host = new_host->next) {
    /* watch out for the separating semicolons ! */
    prop_length += strlen (new_host->hostname) + 1;
    num_hosts++;
  }
  for (i = 0; i < n; i++) {
    int str_length;
 
    new_host = (MUXHostInfo *) Xmalloc (sizeof (MUXHostInfo));
 
    if (new_host == NULL) {
      XmuXFatalError ("XmuXAddHosts: Out of mem (new host)!\n");
    }
 
   /*****************************************************************
    if ((bufptr = SearchString (hostnames[i], '\0')) == NULL) {
      XmuXErrorF ("XmuXAddHosts: non null-terminated string!\n");
      str_length = 6;
    }
    else {
      str_length = bufptr - hostnames[i];
    }
    *****************************************************************/
 
    str_length = strlen (hostnames[i]);
 
    prop_length += str_length + 1;
    new_host->hostname = Xmalloc (str_length + 1);
 
    if (new_host->hostname == NULL) {
      XmuXFatalError ("XmuXAddHosts: Out of mem (host name)!\n");
    }
 
    strncpy (new_host->hostname, hostnames[i], str_length);
    new_host->hostname[str_length] = '\0';
 
    new_host->fd = fds[i];
 
    new_host->next = _muxApplCtx[appl_idx]->hosts;
    _muxApplCtx[appl_idx]->hosts = new_host;
  }
 
  /*
   * Let's do a little arithmetic here:
   *
   * prop_length = sum (strlen(host names)) + number of hosts;
   *
   * We need number of hosts - 1 semicolons and one terminating '\0'
   */
  if ((prop_data = Xmalloc (prop_length)) == NULL) {
    XmuXErrorF ("XmuXAddHosts : Can't update prop (out of mem)!\n");
    return;
  }
  bufptr = prop_data;
  for (new_host = _muxApplCtx[appl_idx]->hosts;
       new_host != NULL; new_host = new_host->next) {
    strcpy (bufptr, new_host->hostname);
    bufptr += strlen (new_host->hostname);
    *bufptr++ = ',';
  }
  prop_data[prop_length - 1] = '\0';
 
  /*
   * we change the property on ALL toplevel windows
   */
  if (!IsClientDisplay (dpy))
    dpy = XmuXPrimaryDisplayFromDisplay (dpy);
 
  for (i = 0; i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    XChangeProperty (dpy,
		     TOPLEVELID (appl_idx, i),
		     XmuXServerAtom (dpy, _MUX_HOSTS),
		     XA_STRING, 8, PropModeReplace, prop_data,
		     prop_length);
 
  Xfree (prop_data);
}
 
void
XmuXRemoveHosts (dpy, hostnames, n)
  Display *dpy;
  char **hostnames;
  int n;
{
  register int i, num_hosts, prop_length, appl_idx, chalk_removed;
  register char *prop_data, *bufptr;
  register MUXHostInfo *pred, *curr;
 
  appl_idx = GetApplicationIndex (dpy);
  chalk_removed = False;
  XmuXDecServers (n);
  for (i = 0; i < n; i++) {
    curr = _muxApplCtx[appl_idx]->hosts;
 
    if (curr == NULL) {
      XmuXErrorF ("XmuXRemoveHost: list empty!\n");
      return;
    }
    if (!strcmp (curr->hostname, hostnames[i])) {
      /* don't remove the original connection */
      if (_muxApplCtx[appl_idx]->hosts->fd !=
	  _muxApplCtx[appl_idx]->client_fd) {
	if (curr->fd == _muxApplCtx[appl_idx]->ChalkHolder)
	  chalk_removed = True;
	_muxApplCtx[appl_idx]->hosts =
	  _muxApplCtx[appl_idx]->hosts->next;
 
	/*
	 * NOTE: We would get in trouble, if we freed the default
	 * connection (XCloseDisplay would free the application context);
	 * but that's not allowed here !
	 */
	XCloseDisplay (MuxDisplay (curr->fd));
	Xfree (curr->hostname);
	Xfree ((char *) curr);
      }
      continue;
    }
    for (pred = curr, curr = curr->next;
	 curr != NULL;
	 pred = curr, curr = curr->next)
      if (!strcmp (curr->hostname, hostnames[i])) {
	/* don't remove the original connection */
	if (curr->fd !=
	    _muxApplCtx[appl_idx]->client_fd) {
	  if (curr->fd == _muxApplCtx[appl_idx]->ChalkHolder)
	    chalk_removed = True;
	  pred->next = curr->next;
 
	  /*
	   * NOTE: We would get in trouble, if we freed the default
	   * connection (XCloseDisplay would free the application context);
	   * but that's not allowed here !
	   */
	  XCloseDisplay (MuxDisplay (curr->fd));
	  Xfree (curr->hostname);
	  Xfree ((char *) curr);
	  continue;
	}
      }
  }
 
  prop_length = 0;
  num_hosts = 0;
 
  /*
   * We'll change the XMUX_HOSTS property here which contains the names
   * of all hosts separated by semicolons don't rely on old value,
   * construct the new from application's host list
   */
  for (curr = _muxApplCtx[appl_idx]->hosts;
       curr != NULL; curr = curr->next) {
    /* watch out for the separating semicolons ! */
    prop_length += strlen (curr->hostname) + 1;
    num_hosts++;
  }
 
  /*
   * Let's do a little arithmetic here:
   *
   * prop_length = sum(strlen(host names)) + number of hosts;
   *
   * We need number of hosts - 1 semicolons and one terminating '\0'
   */
  if ((prop_data = Xmalloc (prop_length)) == NULL) {
    XmuXErrorF ("XmuXAddHosts : Can't update prop (out of mem)!\n");
    return;
  }
  bufptr = prop_data;
  for (curr = _muxApplCtx[appl_idx]->hosts;
       curr != NULL; curr = curr->next) {
    strcpy (bufptr, curr->hostname);
    bufptr += strlen (curr->hostname);
    *bufptr++ = ',';
  }
  prop_data[prop_length - 1] = '\0';
 
  /*
   * we change the property on ALL toplevel windows
   */
  if (!IsClientDisplay (dpy))
    dpy = XmuXPrimaryDisplayFromDisplay (dpy);
 
  for (i = 0; i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    XChangeProperty (dpy,
		     TOPLEVELID (appl_idx, i),
		     XmuXServerAtom (dpy, _MUX_HOSTS),
		     XA_STRING, 8, PropModeReplace, prop_data,
		     prop_length);
 
  Xfree (prop_data);
  if (chalk_removed)
    /* switch to the default */
    XmuXSetChalkHolder (dpy, num_hosts - 1);
 
}
 
void
XmuXFreeHosts (hosts)
  MUXHostInfo *hosts;
{
  register MUXHostInfo *helt = hosts;
 
  while (helt) {
    register MUXHostInfo *hnext = helt->next;
 
    Xfree (helt->hostname);
    Xfree ((char *) helt);
    helt = hnext;
  }
  hosts = NULL;
  return;
}
 
Window
XmuXToplevel (dpy, i)
  Display *dpy;
  int i;
{
  register int appl_idx;
 
  appl_idx = GetApplicationIndex (dpy);
 
  if (i >= _muxApplCtx[appl_idx]->ntoplevels)
    return (None);
  else
    return (TOPLEVELID (appl_idx, i));
}
