static void dndGetSelection(Window owner, Atom property)
{
  unsigned long remaining;
  unsigned char *data= 0;
  Atom actual;
  int format;
  unsigned long count;

  if (Success != XGetWindowProperty(stDisplay, owner, property, 0, 65536, 1, AnyPropertyType,
				    &actual, &format, &count, &remaining, &data))
    fprintf(stderr, "dndGetSelection: XGetWindowProperty failed\n");
  else if (remaining)
    /* a little violent perhaps */
    fprintf(stderr, "dndGetSelection: XGetWindowProperty has more than 64K (why?)\n");
  else
    {
      char *tokens= (char *)data;
      char *item;
      while ((item= strtok(tokens, "\n\r")))
	{
	  fdebugf((stderr, "  got URI <%s>\n", item));
	  if (!strncmp(item, "file:", 5)) /*** xxx BOGUS -- just while image is broken ***/
	    addDropFile(item);
	  tokens= 0; /* strtok is weird.  this ensures more tokens, not less. */
	}
      generateSqueakDropEventIfDroppedFiles();
      fdebugf((stderr, "  uxDropFileCount = %d\n", uxDropFileCount));
    }
  XFree(data);
}
static enum XdndState dndInEnter(enum XdndState state, XClientMessageEvent *evt)
{
  fdebugf((stderr, "Receive XdndEnter (input)\n"));
  if (xdndEnter_version(evt) < 3)
    {
      fprintf(stderr, "  xdnd: protocol version %ld not supported\n", xdndEnter_version(evt));
      return state;
    }
  xdndSourceWindow= xdndEnter_sourceWindow(evt);
  dndGetTypeList(evt);

  fdebugf((stderr, "  dndEnter target: 0x%lx source: 0x%lx\n", evt->window, xdndSourceWindow));
  return XdndStateEntered;
}
static void display_dndOutSend (char *bytes, int nbytes)
{
  XEvent notify;
  XSelectionEvent *res= &notify.xselection;
  Atom targetProperty= ((None == xdndOutRequestEvent.property)
			? xdndOutRequestEvent.target
			: xdndOutRequestEvent.property);

  res->type	  = SelectionNotify;
  res->display	  = xdndOutRequestEvent.display;
  res->requestor  = xdndOutRequestEvent.requestor;
  res->selection  = xdndOutRequestEvent.selection;
  res->target	  = xdndOutRequestEvent.target;
  res->time	  = xdndOutRequestEvent.time;
  res->send_event = True;
  res->property	  = targetProperty; /* override later if error */

  XChangeProperty(stDisplay, res->requestor,
		  targetProperty, xdndOutRequestEvent.target,
		  8, PropModeReplace,
		  (unsigned char *)bytes,
		  nbytes);

  XSendEvent(stDisplay, res->requestor, False, 0, &notify);
  fdebugf((stderr, "Send data for %s (output) requestor: 0x%lx\n",
           XGetAtomName(stDisplay, res->target), res->requestor));
}
static void *tryLoadModule(char *in, char *name)
{
  char path[PATH_MAX], *out= path;
  void *handle= 0;
  int c;
  while ((c= *in++) && ':' != c) {	/* copy next plugin path to path[] */
    switch (c) {
    case '%':
      if ('n' == *in || 'N' == *in) {	/* replace %n with name of plugin */
	++in;
	strcpy(out, name);
	out += strlen(name);
	continue;
      }
      if ('%' == *in) {
	++in;
	*out++= '%';
	continue;
      }
      /* fall through... */
    default:
      *out++= c;
      continue;
    }
  }
  sprintf(out, "/" MODULE_PREFIX "%s" MODULE_SUFFIX, name);
  handle= dlopen(path, RTLD_NOW | RTLD_GLOBAL);
  fdebugf((stderr, "tryLoading(%s) = %p\n", path, handle));
  if (!handle) {
    struct stat buf;
    if ((0 == stat(path, &buf)) && ! S_ISDIR(buf.st_mode))
      fprintf(stderr, "%s\n", dlerror());
  }
  return handle;
}
static sqInt display_dndOutStart(char *types, int ntypes)
{
  int pos, i;
  int typesSize= 0;

  if (xdndOutTypes != 0)
    {
      free(xdndOutTypes);
      xdndOutTypes= 0;
    }

  for (pos= 0; pos < ntypes; pos += strlen(types + pos) + 1)
    typesSize++;

  if (typesSize > 3) return 0; /* Supported types are up to 3 now */

  xdndOutTypes= xmalloc(sizeof(Atom) * (typesSize + 1));
  xdndOutTypes[typesSize]= None;

  for (pos= 0, i= 0; pos < ntypes; pos += strlen(types + pos) + 1, i++)
    xdndOutTypes[i]= XInternAtom(stDisplay, types + pos, False);

  for (i= 0; i < typesSize; i++)
    fdebugf((stderr, "dndOutStart: %s\n", XGetAtomName(stDisplay, xdndOutTypes[i])));
  dndHandleEvent(DndOutStart, 0);

  return 1;
}
/* A finished message is received.
 */
static enum XdndState dndOutFinished(enum XdndState state, XClientMessageEvent *evt)
{
  fdebugf((stderr, "Receive XdndFinished (output) source: 0x%lx target: 0x%lx\n",
           DndWindow, xdndFinished_targetWindow(evt)));
  xdndOutTarget= None;
  return XdndStateIdle;
}
/* Track the current mouse position.
 */
static enum XdndState dndOutMotion(enum XdndState state, XMotionEvent *evt)
{
  Window currentWindow= None;
  int versionReturn= 0;

  if ((XdndStateOutTracking != state) && (XdndStateOutAccepted != state)) return state;

  currentWindow= dndAwareWindow(evt->root, evt->root, &versionReturn);
  if (DndWindow == currentWindow) /* Cursor is on myself */
    {
      xdndOutTarget= None;
      return XdndStateOutTracking;
    }
  
  updateCursor(XdndStateOutAccepted == state);

  if ((XdndVersion > versionReturn)	/* Target's version is too low. */
      || (None == currentWindow))	/* I can't find XdndAware window. */
    {
      xdndOutTarget= None;
      return XdndStateOutTracking;
    }
  
  fdebugf((stderr, "Receive MotionNotify (output) root: 0x%lx awareWindow: 0x%lx\n", evt->root, currentWindow));
  if (currentWindow != xdndOutTarget)
    {
      sendLeave(xdndOutTarget, DndWindow);
      sendEnter(currentWindow, DndWindow);
    }

  sendPosition(currentWindow, DndWindow, evt->x_root, evt->y_root, evt->time);
  xdndOutTarget= currentWindow;

  return state;
}
static char *uri2string(const char *uri)
{
  size_t len= strlen(uri);
  char *string= (char *)xmalloc(len + 3);
  /* whoever wrote the URL stuff in the the image was too damn stupid to understand file URIs */
  if (!strncmp(uri, "file:", 5))
    {
      char *in= string, *out= string;
      strncpy(string, /* chop file:///absolute/path to /absolute/path */
	      uri + (uri[5] == '/' && uri[6] == '/' && uri[7] == '/' ? 7 : 5),
	      len);
      while (*in)
	if ((in[0] == '%') && isxdigit(in[1]) && isxdigit(in[2]))
	  {
	    *out++= hexValue(in[1]) * 16 + hexValue(in[2]);
	    in += 3;
	  }
	else
	  *out++= *in++;
      *out= '\0';
    }
  else
    {
      strncpy(string, uri, len+1);
    }
  fdebugf((stderr, "  uri2string: <%s>\n", string));
  return string;
}
static void dndGetTypeList(XClientMessageEvent *evt)
{
  xdndWillAccept= 0;
  isUrlList= 0;

  if (xdndEnter_hasThreeTypes(evt))
    {
      fdebugf((stderr, "  3 types\n"));
      updateInTypes((Atom *) xdndEnter_targets(evt), 3);
    }
  else
    {
      Atom type;
      int format;
      unsigned long count, remaining;
      unsigned char *data= 0;

      XGetWindowProperty(stDisplay, xdndSourceWindow, XdndTypeList, 0, 0x8000000L, False, XA_ATOM,
			 &type, &format, &count, &remaining, &data);

      if ((type != XA_ATOM) || (format != 32) || (count == 0) || !data)
	{
	  if (data) XFree(data);
	  fprintf(stderr, "XGetWindowProperty failed in xdndGetTypeList\n");
	  return;
	}

      updateInTypes((Atom *) data, count);
      XFree(data);
      fdebugf((stderr, "  %ld types\n", count));
    }

  /* We only accept filenames (MIME type "text/uri-list"). */
  {
    int i;
    for (i= 0;  xdndInTypes[i];  ++i)
      {
	fdebugf((stderr, "  type %d == %ld %s\n", i, xdndInTypes[i], XGetAtomName(stDisplay, xdndInTypes[i])));
	if (XdndTextUriList == xdndInTypes[i])
	  {
	    isUrlList= 1;
	    xdndWillAccept= 1;
	  }
      }
  }
  xdndWillAccept= 1;
}
static enum XdndState dndOutInitialize(enum XdndState state)
{
  fdebugf((stderr, "Internal signal DndOutStart (output)\n"));
  memset(&xdndOutRequestEvent, 0, sizeof(xdndOutRequestEvent));
  XSetSelectionOwner(stDisplay, XdndSelection, DndWindow, CurrentTime);
  updateCursor(-1);
  return XdndStateOutTracking;
}
static enum XdndState dndInFinished(enum XdndState state)
{
  fdebugf((stderr, "Internal signal DndInFinished (input)\n"));
  dndSendFinished();
  recordDragEvent(DragLeave, 1);
  dndInDestroyTypes();
  return XdndStateIdle;
}
static void sendDrop(Window target, Window source, Time timestamp)
{
  long data[5]= { 0, 0, 0, 0, 0 };
  data[2]= timestamp;
  fdebugf((stderr, "Send XdndDrop (output) source: 0x%lx target: 0x%lx\n", source, target));

  sendClientMessage(data, source, target, XdndDrop);
}
/*  Free the module with the associated handle.  Answer 0 on error (do
 *  NOT fail the primitive!).
*/
sqInt ioFreeModule(void *moduleHandle)
{
  if (dlclose(moduleHandle))
    {
      fdebugf((stderr, "ioFreeModule(%d): %s\n", moduleHandle, dlerror()));
      return 0;
    }
  return 1;
}
/* drastically simplified case of dndInDrop that leaves out the 8 step dance
 * (see http://www.newplanetsoftware.com/xdnd/).  Instead grab the fileName in
 * the XdndSqueakLaunchDrop property and send an ack message.
 */
enum XdndState
dndInLaunchDrop(XClientMessageEvent *evt)
{
	Atom actualType;
	int actualFormat;
	unsigned long nitems, bytesAfter;
	unsigned char *fileName;
	unsigned int mask;

	fdebugf((stderr, " dndInLaunchDrop <%d> (%d)\n", evt->message_type, XdndSqueakLaunchDrop));
	XGetWindowProperty(stDisplay, xdndDrop_sourceWindow(evt),
			   XdndSqueakLaunchDrop,
			   0, 0x8000000L, False, XA_ATOM,
			   &actualType, &actualFormat, &nitems,
			   &bytesAfter, &fileName);

	if (nitems > 0) {
		int i;
		fdebugf((stderr, " got launch drop <%s>\n", fileName));
		/* The convention is that we free the previous uxDropFileNames
		 * and zero uxDropFileCount /before/ each dnd interchange,
		 * which saves having to rely on consumption by the image in the
		 * right order.  But it means that if we want multiple launch
		 * drops we're going to have to send them in one go, e.g. by
		 * concatenating a set of null-terminated names.  Too lazy now.
		 * But, but, but.  This convention means multiple launch drops
		 * can smash previous ones.  Broken.  Needs more thought.
		 */
		initDropFileNames();
		addDropFile(fileName);
		generateSqueakDropEventIfDroppedFiles();
		for (i = 0; i < numLaunchDrops; i++)
			if (!launchDrops[i].fileName)
				break;
		if (i >= numLaunchDrops) {
			i = numLaunchDrops;
			launchDrops = xrealloc(launchDrops,
									++numLaunchDrops * sizeof(*launchDrops));
		}
		launchDrops[i].fileName = fileName;
		launchDrops[i].sourceWindow = xdndDrop_sourceWindow(evt);
	}
}
static enum XdndState dndInSelectionNotify(enum XdndState state, XSelectionEvent *evt)
{
  fdebugf((stderr, "Receive SelectionNotify (input)\n"));
  if (evt->property != XdndSelectionAtom) return state;

  dndGetSelection(evt->requestor, evt->property);
  dndSendFinished();
  recordDragEvent(DragLeave, 1);
  return XdndStateIdle;
}
static enum XdndState dndInPosition(enum XdndState state, XClientMessageEvent *evt)
{
  /*fdebugf((stderr, "Receive XdndPosition (input)\n"));*/

  if (xdndSourceWindow != xdndPosition_sourceWindow(evt))
    {
      fdebugf((stderr, "dndInPosition: wrong source window\n"));
      return XdndStateIdle;
    }

  getMousePosition();

  if ((state != XdndStateEntered) && (state != XdndStateTracking))
    {
      fdebugf((stderr, "dndInPosition: wrong state\n"));
      return XdndStateIdle;
    }
  
  if ((state == XdndStateEntered) && xdndWillAccept)
    recordDragEvent(DragEnter, 1);
  
  if (xdndWillAccept)
    {
      Atom action= xdndPosition_action(evt);
      /*fdebugf((stderr, "  dndInPosition: action = %ld %s\n", action, XGetAtomName(stDisplay, action)));*/
      xdndWillAccept= (action == XdndActionMove) | (action == XdndActionCopy)
	|             (action == XdndActionLink) | (action == XdndActionAsk);
    }

  if (xdndWillAccept)
    {
      /*fdebugf((stderr, "  dndInPosition: accepting\n"));*/
      dndSendStatus(1, XdndActionCopy);
      recordDragEvent(DragMove, 1);
    }
  else /* won't accept */
    {
      /*fdebugf((stderr, "  dndInPosition: not accepting\n"));*/
      dndSendStatus(0, XdndActionPrivate);
    }
  return XdndStateTracking;
}
/*  Attempt to load the shared library named by the concatenation of prefix,
 *  moduleName and suffix.  Answer the new module entry, or 0 if the shared
 *  library could not be loaded.
 */
static void *tryLoading(char *dirName, char *moduleName)
{
  static char *prefixes[]= { "", "lib", 0 };
  static char *suffixes[]= { "", ".so", ".dylib", 0 };
  void        *handle= 0;
  char	     **prefix= 0, **suffix= 0;

  for (prefix= prefixes;  *prefix;  ++prefix)
    for (suffix= suffixes;  *suffix;  ++suffix)
      {
	char        libName[NAME_MAX + 32];	/* headroom for prefix/suffix */
	struct stat buf;
	int         err;
	sprintf(libName, "%s%s%s%s", dirName, *prefix, moduleName, *suffix);
	if ((err= stat(libName, &buf)))
	  fdebugf((stderr, "cannot read: %s\n", libName));
	else
	  {
	    if (S_ISDIR(buf.st_mode))
	      fdebugf((stderr, "ignoring directory: %s\n", libName));
	    else
	      {
		fdebugf((stderr, "tryLoading %s\n", libName));
		handle= dlopen(libName, RTLD_NOW | RTLD_GLOBAL);
		if (handle == 0)
		  {
		    /*if ((!err) && !(sqIgnorePluginErrors))*/
		      fprintf(stderr, "ioLoadModule(%s):\n  %s\n", libName, dlerror());
		  }
		else
		  {
#	           if DEBUG
		    printf("squeak: loaded plugin `%s'\n", libName);
#	           endif
		    return handle;
		  }
	      }
	  }
      }
  return 0;
}
enum XdndState dndInDrop(enum XdndState state, XClientMessageEvent *evt)
{
  fdebugf((stderr, "Receive XdndDrop (input)\n"));

  /* If there is "text/url-list" in xdndInTypes, the selection is
   * processed only in DropFilesEvent. But if none (file count == 0),
   * the selection is handled ClipboardExtendedPlugin.
   */
  if (isUrlList == 0)
    {
      fdebugf((stderr, "  dndInDrop: no url list\n"));
      recordDragEvent(DragDrop, 0);
      return state;
    }
  dndInDestroyTypes();

  if (xdndSourceWindow != xdndDrop_sourceWindow(evt))
    {
      fdebugf((stderr, "  dndInDrop: wrong source window\n"));
    }
  else if (xdndWillAccept)
    {
      Window owner;
      fdebugf((stderr, "  dndInDrop: converting selection\n"));
      if (!(owner= XGetSelectionOwner(stDisplay, XdndSelection)))
	fprintf(stderr, "  dndInDrop: XGetSelectionOwner failed\n");
      else
	XConvertSelection(stDisplay, XdndSelection, XdndTextUriList, XdndSelectionAtom, stWindow, xdndDrop_time(evt));
      initDropFileNames();
    }
  else
    {
      fdebugf((stderr, "  dndInDrop: refusing selection -- finishing\n"));
    }

  dndSendFinished();
  recordDragEvent(DragLeave, 1);

  return XdndStateIdle;
}
void *ioLoadModule(char *pluginName)
{
  char  path[PATH_MAX];
  char *dir= squeakPlugins;
  void *handle= 0;

  if ((0 == pluginName) || ('\0' == pluginName[0])) {	/* find module in main program */
    handle= dlopen(0, RTLD_NOW | RTLD_GLOBAL);
    if (handle == 0) {
      fprintf(stderr, "ioLoadModule(<intrinsic>): %s\n", dlerror());
    }
    else {
      fdebugf((stderr, "loaded: <intrinsic>\n"));
    }
    return handle;
  }

  /* try loading {pluginPaths}/MODULE_PREFIX<name>MODULE_SUFFIX */

  while (*dir) {
    if ((handle= tryLoadModule(dir, pluginName)))
      return handle;
    while (*dir && ':' != *dir++)
      ;
  }

  /* try dlopen()ing LIBRARY_PREFIX<name>LIBRARY_SUFFIX searching only the default locations modulo LD_LIBRARY_PATH et al */

# if defined(HAVE_SNPRINTF)
  snprintf(path, sizeof(path), "%s%s%s", LIBRARY_PREFIX, pluginName, LIBRARY_SUFFIX);
# else
  sprintf(path, "%s%s%s", LIBRARY_PREFIX, pluginName, LIBRARY_SUFFIX);
# endif

  handle= dlopen(path, RTLD_NOW | RTLD_GLOBAL);
  fdebugf((stderr, "ioLoadModule(%s) = %p\n", path, handle));

  return handle;
}
/* The mouse button was released.
*/
static enum XdndState dndOutRelease(enum XdndState state, XButtonEvent *evt)
{
  if (XdndStateIdle == state) return XdndStateIdle;
  fdebugf((stderr, "Receive ButtonRelease (output) window: 0x%lx\n", evt->window));

  if (XdndStateOutAccepted == state)
    {
      sendDrop(xdndOutTarget, DndWindow, evt->time);
      return XdndStateOutAccepted;
    }
  sendLeave(xdndOutTarget, DndWindow);
  return XdndStateIdle;
}
/* Another application is requesting the selection.
*/
static enum XdndState dndOutSelectionRequest(enum XdndState state, XSelectionRequestEvent *req)
{
  fdebugf((stderr, "Receive SelectionRequest for %s (output) owner: 0x%lx : requestor: 0x%lx\n",
           XGetAtomName(stDisplay, req->target), req->owner, req->requestor));
  if (XdndStateOutAccepted != state)
    {
      /*printf("%i is not expected in SelectionRequest\n", state);*/
      return state;
    }
  memcpy(&xdndOutRequestEvent, req, sizeof(xdndOutRequestEvent));
  recordDragEvent(DragRequest, 1);
  return state;
}
/*  Find a function in a loaded module.  Answer 0 if not found (do NOT
 *  fail the primitive!).
 */
void *ioFindExternalFunctionIn(char *lookupName, void *moduleHandle)
{
  void *fn= dlsym(moduleHandle, lookupName);
  fdebugf((stderr, "ioFindExternalFunctionIn(%s, %p) = %p\n", lookupName, moduleHandle, fn));

  if ((fn == 0) && (!sqIgnorePluginErrors)
      && strcmp(lookupName, "initialiseModule")
      && strcmp(lookupName, "shutdownModule")
      && strcmp(lookupName, "setInterpreter")
      && strcmp(lookupName, "getModuleName"))
    fprintf(stderr, "ioFindExternalFunctionIn(%s, %p):\n  %s\n", lookupName, moduleHandle, dlerror());

  return fn;
}
static void *tryLoadingPath(char *varName, char *pluginName)
{
  char *path= getenv(varName);
  void *handle= 0;

  if (path)
    {
      char pbuf[MAXPATHLEN];
      fdebugf((stderr, "try %s=%s\n", varName, path));
      strncpy(pbuf, path, sizeof(pbuf));
      pbuf[sizeof(pbuf) - 1]= '\0';
      for (path= strtok(pbuf, ":");
	   path != 0;
	   path= strtok(0, ":"))
	{
	  char buf[MAXPATHLEN];
	  sprintf(buf, "%s/", path);
	  fdebugf((stderr, "  path dir = %s\n", buf));
	  if ((handle= tryLoading(buf, pluginName)) != 0)
	    break;
	}
    }
  return handle;
}
static void dndSendFinished(void)
{
    XClientMessageEvent evt;
    memset(&evt, 0, sizeof(evt));

    evt.type	     = ClientMessage;
    evt.display	     = stDisplay;
    evt.window	     = xdndSourceWindow;
    evt.message_type = XdndFinished;
    evt.format	     = 32;

    xdndFinished_targetWindow(&evt)= DndWindow;
    XSendEvent(stDisplay, xdndSourceWindow, 0, 0, (XEvent *)&evt);

    fdebugf((stderr, "dndSendFinished target: 0x%lx source: 0x%lx\n", DndWindow, xdndSourceWindow));
}
/* Change cursor
 * TODO: The cursor should be controlled by the image, so it should be removed finally.
 *
 * state = -1 : Cursor is on Squeak window.
 * state =  0 : Target window doesn't accept.
 * state =  1 : Target window accepts.
 */
static void updateCursor(int state)
{
  static int lastCursor= -1;

  if (lastCursor == state) return;
  fdebugf((stderr, "Cursor change (output) previous: %i new: %i\n", lastCursor, state));
  if (1 == state)
    {
      Cursor cursor;
      cursor= XCreateFontCursor(stDisplay, 90);
      XDefineCursor(stDisplay, stWindow, cursor);
    }
  else
    XDefineCursor(stDisplay, stWindow, None);
  
  lastCursor= state;
}
/* A status message to know accept or not is received.
 */
static enum XdndState dndOutStatus(enum XdndState state, XClientMessageEvent *evt)
{
  long *ldata= evt->data.l;
  fdebugf((stderr, "Receive XdndStatus (output) status: 0x%lx target: 0x%lx\n", ldata[1], ldata[0]));

  if ((XdndStateOutTracking != state) && (XdndStateOutAccepted != state))
    {
      /*printf("%i is not expected in XdndStatus\n", state);*/
      sendLeave(ldata[0], DndWindow);
      return state;
    }
  
  if (xdndOutTarget != ldata[0]) return state;

  if (ldata[1] && 0x1UL)
    return XdndStateOutAccepted;
  else
    return XdndStateOutTracking;
}
static void sendEnter(Window target, Window source)
{
  long data[5]= { 0, 0, 0, 0, 0 };
  data[1] |= 0x0UL; /* just three data types */
  data[1] |= XdndVersion << 24; /* version num */

  if (0 != xdndOutTypes)
    {
      data[2]= xdndOutTypes[0];
      if (None != xdndOutTypes[1])
	{
	  data[3]= xdndOutTypes[1];
	  if (None != xdndOutTypes[2])
	    {
	      data[4]= xdndOutTypes[1];
	    }
	}
    }
  fdebugf((stderr, "Send XdndEnter (output) source: 0x%lx target: 0x%lx\n", source, target));
  sendClientMessage(data, source, target, XdndEnter);
}
/*  Find and load the named module.  Answer 0 if not found (do NOT fail
 *  the primitive!).
 */
void *ioLoadModule(char *pluginName)
{
  void *handle= 0;

  if ((pluginName == 0) || (pluginName[0] == '\0'))
    {
      handle= dlopen(0, RTLD_NOW | RTLD_GLOBAL);
      if (handle == 0)
	fprintf(stderr, "ioLoadModule(<intrinsic>): %s\n", dlerror());
      else
	{
	  fdebugf((stderr, "loaded: <intrinsic>\n"));
	  return handle;
	}
    }

  if (squeakPlugins)
      {
	char path[NAME_MAX];
	char c, *in= squeakPlugins, *out= path;
	while ((c= *in++))
	  {
	    if (c == '%' && ((*in == 'n') || (*in == 'N')))
	      {
		++in;
		strcpy(out, pluginName);
		out+= strlen(pluginName);
	      }
	    else
	      *out++= c;
	  }
	*out= '\0';
	fdebugf((stderr, "ioLoadModule plugins = %s\n                path = %s\n",
		 squeakPlugins, path));
	if ((handle= tryLoading("", path)))
	  return handle;
	*out++= '/';
	*out= '\0';
	if ((handle= tryLoading(path, pluginName)))
	  return handle;
      }

  if ((   handle= tryLoading(    "./",			pluginName))
      || (handle= tryLoadingPath("SQUEAK_PLUGIN_PATH",	pluginName))
      || (handle= tryLoading(    VM_LIBDIR"/",		pluginName))
      || (handle= tryLoadingPath("LD_LIBRARY_PATH",	pluginName))
      || (handle= tryLoading(    "",			pluginName))
#    if defined(VM_X11DIR)
      || (handle= tryLoading(VM_X11DIR"/",		pluginName))
#    endif
      )
    return handle;

#if defined(DARWIN)
  // look in the bundle contents dir
  {
    static char *contents= 0;
    if (!contents)
      {
	char *delim;
	contents= strdup(vmPath);
	if ((delim= strrchr(contents, '/')))
	  delim[1]= '\0';
      }
    if ((handle= tryLoading(contents, pluginName)))
      return handle;
  }
  // the following is needed so that, for example, the FFI can pick up
  // things like <cdecl: 'xyz' module: 'CoreServices'>
  {
    static char *frameworks[]=
      {
	"/System/Library/Frameworks",
	"/System/Library/Frameworks/CoreServices.framework/Frameworks",
	"/System/Library/Frameworks/ApplicationServices.framework/Frameworks",
	"/System/Library/Frameworks/Carbon.framework/Frameworks",
	0
      };
    char **framework= 0;
    for (framework= frameworks;  *framework;  ++framework)
      {
	char path[NAME_MAX];
	sprintf(path, "%s/%s.framework/", *framework, pluginName);
	if ((handle= tryLoading(path, pluginName)))
	  return handle;
      }
  }
#endif /* DARWIN */

  /* finally (for VM hackers) try the pre-install build location */
  {
    char pluginDir[MAXPATHLEN];
#  ifdef HAVE_SNPRINTF
    snprintf(pluginDir, sizeof(pluginDir), "%s%s/", vmPath, pluginName);
#  else
    sprintf(pluginDir, "%s%s/", vmPath, pluginName);
#  endif
    if ((handle= tryLoading(pluginDir, pluginName)))
      return handle;
#  ifdef HAVE_SNPRINTF
    snprintf(pluginDir, sizeof(pluginDir), "%s%s/.libs/", vmPath, pluginName);
#  else
    sprintf(pluginDir, "%s%s/.libs/", vmPath, pluginName);
#  endif
    if ((handle= tryLoading(pluginDir, pluginName)))
      return handle;
  }

#if DEBUG
  fprintf(stderr, "squeak: could not load plugin `%s'\n", pluginName);
#endif
  return 0;
}
static void sendLeave(Window target, Window source)
{
  long data[5]= { 0, 0, 0, 0, 0 };
  fdebugf((stderr, "Send XdndLeave (output) source: 0x%lx target: 0x%lx\n", source, target));
  sendClientMessage(data, source, target, XdndLeave);
}
static enum XdndState dndInLeave(enum XdndState state)
{
  fdebugf((stderr, "Receive XdndLeave (input)\n"));
  recordDragEvent(DragLeave, 1);
  return XdndStateIdle;
}