On Tue, May 16, 2000 at 10:12:43AM +1000, Rodos wrote:
| I am using Pine for my email now, moving off Netscape Communicator.
| Things are going really well. One of the few remaining problems I have is
| invoking links to come up in netscape.
| 
| I have my helper app set as
| /usr/bin/netscape -remote 'openURL(_URL_)'
| which works fine.

You'll find netscape-remote a little quicker. Source attached. It's
really just the -remote code from the Netscape source taken out and
made into a small app by JZ. Smaller and faster.

| Problem is that if the URL has a comma in it, like
| http://www.pcworld.com/pcwtoday/article/0,1510,16704,00.html
| it raises an error because the openURL has an optional second argument
| which is sperated by a comma. Netscapet gets confused because of the
| commas in the URL. I have tried a number of ways to escape it but can't.

Translate the commas to %2C (their hex exquivalent).

I use a shell script:
        http://www.zip.com.au/~cs/scripts/urlshow
as a wrapper for netscape-remote to do this. That particular script is
overkill for you, but the bits you want are in it and can be ripped out
and put in a much smaller script:
        
        # escape commas for OpenURL() call
        case "$url" in
            *,*)        url=`echo "$url"|sed 's/,/%2C/g'` ;;
        esac

The dicking with "case" is a performance hack to save to cost of the
sed if there aren't any commas.

Cheers,
-- 
Cameron Simpson, DoD#743        [EMAIL PROTECTED]    http://www.zip.com.au/~cs/

[Alain] had been looking at his dashboard, and had not seen me, so I
ran into him. - Jean Alesi on his qualifying prang at Imola '93
/* -*- Mode:C; tab-width: 8 -*-
 * remote.c --- remote control of Netscape Navigator for Unix.
 * version 1.1.3, for Netscape Navigator 1.1 and newer.
 *
 * Copyright � 1996 Netscape Communications Corporation, all rights reserved.
 * Created: Jamie Zawinski <[EMAIL PROTECTED]>, 24-Dec-94.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or 
 * implied warranty.
 *
 * To compile:
 *
 *    cc -o netscape-remote remote.c -DSTANDALONE -lXmu -lX11
 *
 * To use:
 *
 *    netscape-remote -help
 *
 * Documentation for the protocol which this code implements may be found at:
 *
 *    http://home.netscape.com/newsref/std/x-remote.html
 *
 * Bugs and commentary to [EMAIL PROTECTED]
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/WinUtil.h>    /* for XmuClientWindow() */


/* vroot.h is a header file which lets a client get along with `virtual root'
   window managers like swm, tvtwm, olvwm, etc.  If you don't have this header
   file, you can find it at "http://home.netscape.com/newsref/std/vroot.h".
   If you don't care about supporting virtual root window managers, you can
   comment this line out.
 */
#include "vroot.h"


#ifdef STANDALONE
 static const char *progname = 0;
 static const char *expected_mozilla_version = "1.1";
#else  /* !STANDALONE */
 extern const char *progname;
 extern const char *expected_mozilla_version;
#endif /* !STANDALONE */

#define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
#define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
#define MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
#define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
static Atom XA_MOZILLA_VERSION  = 0;
static Atom XA_MOZILLA_LOCK     = 0;
static Atom XA_MOZILLA_COMMAND  = 0;
static Atom XA_MOZILLA_RESPONSE = 0;

static void
mozilla_remote_init_atoms (Display *dpy)
{
  if (! XA_MOZILLA_VERSION)
    XA_MOZILLA_VERSION = XInternAtom (dpy, MOZILLA_VERSION_PROP, False);
  if (! XA_MOZILLA_LOCK)
    XA_MOZILLA_LOCK = XInternAtom (dpy, MOZILLA_LOCK_PROP, False);
  if (! XA_MOZILLA_COMMAND)
    XA_MOZILLA_COMMAND = XInternAtom (dpy, MOZILLA_COMMAND_PROP, False);
  if (! XA_MOZILLA_RESPONSE)
    XA_MOZILLA_RESPONSE = XInternAtom (dpy, MOZILLA_RESPONSE_PROP, False);
}

static Window
mozilla_remote_find_window (Display *dpy)
{
  int i;
  Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
  Window root2, parent, *kids;
  unsigned int nkids;
  Window result = 0;
  Window tenative = 0;
  unsigned char *tenative_version = 0;

  if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
    {
      fprintf (stderr, "%s: XQueryTree failed on display %s\n", progname,
               DisplayString (dpy));
      exit (2);
    }

  /* root != root2 is possible with virtual root WMs. */

  if (! (kids && nkids))
    {
      fprintf (stderr, "%s: root window has no children on display %s\n",
               progname, DisplayString (dpy));
      exit (2);
    }

  for (i = nkids-1; i >= 0; i--)
    {
      Atom type;
      int format;
      unsigned long nitems, bytesafter;
      unsigned char *version = 0;
      Window w = XmuClientWindow (dpy, kids[i]);
      int status = XGetWindowProperty (dpy, w, XA_MOZILLA_VERSION,
                                       0, (65536 / sizeof (long)),
                                       False, XA_STRING,
                                       &type, &format, &nitems, &bytesafter,
                                       &version);
      if (! version)
        continue;
      if (strcmp ((char *) version, expected_mozilla_version) &&
          !tenative)
        {
          tenative = w;
          tenative_version = version;
          continue;
        }
      XFree (version);
      if (status == Success && type != None)
        {
          result = w;
          break;
        }
    }

  if (result && tenative)
    {
      /*
      fprintf (stderr,
               "%s: warning: both version %s (0x%x) and version\n"
               "\t%s (0x%x) are running.  Using version %s.\n",
               progname, tenative_version, (unsigned int) tenative,
               expected_mozilla_version, (unsigned int) result,
               expected_mozilla_version);
      */
      XFree (tenative_version);
      return result;
    }
  else if (tenative)
    {
      fprintf (stderr,
               "%s: warning: expected version %s but found version\n"
               "\t%s (0x%x) instead.\n",
               progname, expected_mozilla_version,
               tenative_version, (unsigned int) tenative);
      XFree (tenative_version);
      return tenative;
    }
  else if (result)
    {
      return result;
    }
  else
    {
      fprintf (stderr, "%s: not running on display %s\n", progname,
               DisplayString (dpy));
      exit (1);
    }
}

static void
mozilla_remote_check_window (Display *dpy, Window window)
{
  Atom type;
  int format;
  unsigned long nitems, bytesafter;
  unsigned char *version = 0;
  int status = XGetWindowProperty (dpy, window, XA_MOZILLA_VERSION,
                                   0, (65536 / sizeof (long)),
                                   False, XA_STRING,
                                   &type, &format, &nitems, &bytesafter,
                                   &version);
  if (status != Success || !version)
    {
      fprintf (stderr, "%s: window 0x%x is not a Netscape window.\n",
               progname, (unsigned int) window);
      exit (6);
    }
  else if (strcmp ((char *) version, expected_mozilla_version))
    {
      fprintf (stderr,
               "%s: warning: window 0x%x is Netscape version %s;\n"
               "\texpected version %s.\n",
               progname, (unsigned int) window,
               version, expected_mozilla_version);
    }
  XFree (version);
}


static char *lock_data = 0;

static void
mozilla_remote_obtain_lock (Display *dpy, Window window)
{
  Bool locked = False;
  Bool waited = False;

  if (! lock_data)
    {
      lock_data = (char *) malloc (255);
      sprintf (lock_data, "pid%d@", getpid ());
      if (gethostname (lock_data + strlen (lock_data), 100))
        {
          perror ("gethostname");
          exit (-1);
        }
    }

  do
    {
      int result;
      Atom actual_type;
      int actual_format;
      unsigned long nitems, bytes_after;
      unsigned char *data = 0;

      XGrabServer (dpy);   /* ################################# DANGER! */

      result = XGetWindowProperty (dpy, window, XA_MOZILLA_LOCK,
                                   0, (65536 / sizeof (long)),
                                   False, /* don't delete */
                                   XA_STRING,
                                   &actual_type, &actual_format,
                                   &nitems, &bytes_after,
                                   &data);
      if (result != Success || actual_type == None)
        {
          /* It's not now locked - lock it. */
#ifdef DEBUG_PROPS
          fprintf (stderr, "%s: (writing " MOZILLA_LOCK_PROP
                   " \"%s\" to 0x%x)\n",
                   progname, lock_data, (unsigned int) window);
#endif
          XChangeProperty (dpy, window, XA_MOZILLA_LOCK, XA_STRING, 8,
                           PropModeReplace, (unsigned char *) lock_data,
                           strlen (lock_data));
          locked = True;
        }

      XUngrabServer (dpy); /* ################################# danger over */
      XSync (dpy, False);

      if (! locked)
        {
          /* We tried to grab the lock this time, and failed because someone
             else is holding it already.  So, wait for a PropertyDelete event
             to come in, and try again. */

          fprintf (stderr, "%s: window 0x%x is locked by %s; waiting...\n",
                   progname, (unsigned int) window, data);
          waited = True;

          while (1)
            {
              XEvent event;
              XNextEvent (dpy, &event);
              if (event.xany.type == DestroyNotify &&
                  event.xdestroywindow.window == window)
                {
                  fprintf (stderr, "%s: window 0x%x unexpectedly destroyed.\n",
                           progname, (unsigned int) window);
                  exit (6);
                }
              else if (event.xany.type == PropertyNotify &&
                       event.xproperty.state == PropertyDelete &&
                       event.xproperty.window == window &&
                       event.xproperty.atom == XA_MOZILLA_LOCK)
                {
                  /* Ok!  Someone deleted their lock, so now we can try
                     again. */
#ifdef DEBUG_PROPS
                  fprintf (stderr, "%s: (0x%x unlocked, trying again...)\n",
                           progname, (unsigned int) window);
#endif
                  break;
                }
            }
        }
      if (data)
        XFree (data);
    }
  while (! locked);

  if (waited)
    fprintf (stderr, "%s: obtained lock.\n", progname);
}


static void
mozilla_remote_free_lock (Display *dpy, Window window)
{
  int result;
  Atom actual_type;
  int actual_format;
  unsigned long nitems, bytes_after;
  unsigned char *data = 0;

#ifdef DEBUG_PROPS
          fprintf (stderr, "%s: (deleting " MOZILLA_LOCK_PROP
                   " \"%s\" from 0x%x)\n",
                   progname, lock_data, (unsigned int) window);
#endif

  result = XGetWindowProperty (dpy, window, XA_MOZILLA_LOCK,
                               0, (65536 / sizeof (long)),
                               True, /* atomic delete after */
                               XA_STRING,
                               &actual_type, &actual_format,
                               &nitems, &bytes_after,
                               &data);
  if (result != Success)
    {
      fprintf (stderr, "%s: unable to read and delete " MOZILLA_LOCK_PROP
               " property\n",
               progname);
      return;
    }
  else if (!data || !*data)
    {
      fprintf (stderr, "%s: invalid data on " MOZILLA_LOCK_PROP
               " of window 0x%x.\n",
               progname, (unsigned int) window);
      return;
    }
  else if (strcmp ((char *) data, lock_data))
    {
      fprintf (stderr, "%s: " MOZILLA_LOCK_PROP
               " was stolen!  Expected \"%s\", saw \"%s\"!\n",
               progname, lock_data, data);
      return;
    }

  if (data)
    XFree (data);
}


static int
mozilla_remote_command (Display *dpy, Window window, const char *command,
                        Bool raise_p)
{
  int result;
  Bool done = False;
  char *new_command = 0;

  /* The -noraise option is implemented by passing a "noraise" argument
     to each command to which it should apply.
   */
  if (! raise_p)
    {
      char *close;
      new_command = (char *) malloc (strlen (command) + 20);
      strcpy (new_command, command);
      close = strrchr (new_command, ')');
      if (close)
        strcpy (close, ", noraise)");
      else
        strcat (new_command, "(noraise)");
      command = new_command;
    }

#ifdef DEBUG_PROPS
  fprintf (stderr, "%s: (writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n",
           progname, command, (unsigned int) window);
#endif

  XChangeProperty (dpy, window, XA_MOZILLA_COMMAND, XA_STRING, 8,
                   PropModeReplace, (unsigned char *) command,
                   strlen (command));

  while (!done)
    {
      XEvent event;
      XNextEvent (dpy, &event);
      if (event.xany.type == DestroyNotify &&
          event.xdestroywindow.window == window)
        {
          /* Print to warn user...*/
          fprintf (stderr, "%s: window 0x%x was destroyed.\n",
                   progname, (unsigned int) window);
          result = 6;
          goto DONE;
        }
      else if (event.xany.type == PropertyNotify &&
               event.xproperty.state == PropertyNewValue &&
               event.xproperty.window == window &&
               event.xproperty.atom == XA_MOZILLA_RESPONSE)
        {
          Atom actual_type;
          int actual_format;
          unsigned long nitems, bytes_after;
          unsigned char *data = 0;

          result = XGetWindowProperty (dpy, window, XA_MOZILLA_RESPONSE,
                                       0, (65536 / sizeof (long)),
                                       True, /* atomic delete after */
                                       XA_STRING,
                                       &actual_type, &actual_format,
                                       &nitems, &bytes_after,
                                       &data);
#ifdef DEBUG_PROPS
          if (result == Success && data && *data)
            {
              fprintf (stderr, "%s: (server sent " MOZILLA_RESPONSE_PROP
                       " \"%s\" to 0x%x.)\n",
                       progname, data, (unsigned int) window);
            }
#endif

          if (result != Success)
            {
              fprintf (stderr, "%s: failed reading " MOZILLA_RESPONSE_PROP
                       " from window 0x%0x.\n",
                       progname, (unsigned int) window);
              result = 6;
              done = True;
            }
          else if (!data || strlen((char *) data) < 5)
            {
              fprintf (stderr, "%s: invalid data on " MOZILLA_RESPONSE_PROP
                       " property of window 0x%0x.\n",
                       progname, (unsigned int) window);
              result = 6;
              done = True;
            }
          else if (*data == '1')        /* positive preliminary reply */
            {
              fprintf (stderr, "%s: %s\n", progname, data + 4);
              /* keep going */
              done = False;
            }
#if 1
          else if (!strncmp ((char *)data, "200", 3)) /* positive completion */
            {
              result = 0;
              done = True;
            }
#endif
          else if (*data == '2')                /* positive completion */
            {
              fprintf (stderr, "%s: %s\n", progname, data + 4);
              result = 0;
              done = True;
            }
          else if (*data == '3')        /* positive intermediate reply */
            {
              fprintf (stderr, "%s: internal error: "
                       "server wants more information?  (%s)\n",
                       progname, data);
              result = 3;
              done = True;
            }
          else if (*data == '4' ||      /* transient negative completion */
                   *data == '5')        /* permanent negative completion */
            {
              fprintf (stderr, "%s: %s\n", progname, data + 4);
              result = (*data - '0');
              done = True;
            }
          else
            {
              fprintf (stderr,
                       "%s: unrecognised " MOZILLA_RESPONSE_PROP
                       " from window 0x%x: %s\n",
                       progname, (unsigned int) window, data);
              result = 6;
              done = True;
            }

          if (data)
            XFree (data);
        }
#ifdef DEBUG_PROPS
      else if (event.xany.type == PropertyNotify &&
               event.xproperty.window == window &&
               event.xproperty.state == PropertyDelete &&
               event.xproperty.atom == XA_MOZILLA_COMMAND)
        {
          fprintf (stderr, "%s: (server 0x%x has accepted "
                   MOZILLA_COMMAND_PROP ".)\n",
                   progname, (unsigned int) window);
        }
#endif /* DEBUG_PROPS */
    }

 DONE:

  if (new_command)
    free (new_command);

  return result;
}

int
mozilla_remote_commands (Display *dpy, Window window, char **commands)
{
  Bool raise_p = True;
  int status = 0;
  mozilla_remote_init_atoms (dpy);

  if (window == 0)
    window = mozilla_remote_find_window (dpy);
  else
    mozilla_remote_check_window (dpy, window);

  XSelectInput (dpy, window, (PropertyChangeMask|StructureNotifyMask));

  mozilla_remote_obtain_lock (dpy, window);

  while (*commands)
    {
      if (!strcmp (*commands, "-raise"))
        raise_p = True;
      else if (!strcmp (*commands, "-noraise"))
        raise_p = False;
      else
        status = mozilla_remote_command (dpy, window, *commands, raise_p);

      if (status != 0)
        break;
      commands++;
    }

  /* When status = 6, it means the window has been destroyed */
  /* It is invalid to free the lock when window is destroyed. */

  if ( status != 6 )
  mozilla_remote_free_lock (dpy, window);

  return status;
}


#ifdef STANDALONE

static void
usage (void)
{
  fprintf (stderr, "usage: %s [ options ... ]\n\
       where options include:\n\
\n\
       -help                     to show this message.\n\
       -display <dpy>            to specify the X server to use.\n\
       -remote <remote-command>  to execute a command in an already-running\n\
                                 Netscape process.  See the manual for a\n\
                                 list of valid commands.\n\
       -id <window-id>           the id of an X window to which the -remote\n\
                                 commands should be sent; if unspecified,\n\
                                 the first window found will be used.\n\
       -raise                    whether following -remote commands should\n\
                                 cause the window to raise itself to the top\n\
                                 (this is the default.)\n\
       -noraise                  the opposite of -raise: following -remote\n\
                                 commands will not auto-raise the window.\n\
",
           progname);
}


void
main (int argc, char **argv)
{
  Display *dpy;
  char *dpy_string = 0;
  char **remote_commands = 0;
  int remote_command_count = 0;
  int remote_command_size = 0;
  unsigned long remote_window = 0;
  Bool sync_p = False;
  int i;

  progname = strrchr (argv[0], '/');
  if (progname)
    progname++;
  else
    progname = argv[0];

  /* Hack the -help and -version arguments before opening the display. */
  for (i = 1; i < argc; i++)
    {
      if (!strcasecmp (argv [i], "-h") ||
          !strcasecmp (argv [i], "-help"))
        {
          usage ();
          exit (0);
        }
      else if (!strcmp (argv [i], "-d") ||
               !strcmp (argv [i], "-dpy") ||
               !strcmp (argv [i], "-disp") ||
               !strcmp (argv [i], "-display"))
        {
          i++;
          dpy_string = argv [i];
        }
      else if (!strcmp (argv [i], "-sync") ||
               !strcmp (argv [i], "-synchronize"))
        {
          sync_p = True;
        }
      else if (!strcmp (argv [i], "-remote"))
        {
          if (remote_command_count == remote_command_size)
            {
              remote_command_size += 20;
              remote_commands =
                (remote_commands
                 ? realloc (remote_commands,
                            remote_command_size * sizeof (char *))
                 : calloc (remote_command_size, sizeof (char *)));
            }
          i++;
          if (!argv[i] || *argv[i] == '-' || *argv[i] == 0)
            {
              fprintf (stderr, "%s: invalid `-remote' option \"%s\"\n",
                       progname, argv[i] ? argv[i] : "");
              usage ();
              exit (-1);
            }
          remote_commands [remote_command_count++] = argv[i];
        }
      else if (!strcmp (argv [i], "-raise") ||
               !strcmp (argv [i], "-noraise"))
        {
          char *r = argv [i];
          if (remote_command_count == remote_command_size)
            {
              remote_command_size += 20;
              remote_commands =
                (remote_commands
                 ? realloc (remote_commands,
                            remote_command_size * sizeof (char *))
                 : calloc (remote_command_size, sizeof (char *)));
            }
          remote_commands [remote_command_count++] = r;
        }
      else if (!strcmp (argv [i], "-id"))
        {
          char c;
          if (remote_command_count > 0)
            {
              fprintf (stderr,
                "%s: the `-id' option must preceed all `-remote' options.\n",
                       progname);
              usage ();
              exit (-1);
            }
          else if (remote_window != 0)
            {
              fprintf (stderr, "%s: only one `-id' option may be used.\n",
                       progname);
              usage ();
              exit (-1);
            }
          i++;
          if (argv[i] &&
              1 == sscanf (argv[i], " %ld %c", &remote_window, &c))
            ;
          else if (argv[i] &&
                   1 == sscanf (argv[i], " 0x%lx %c", &remote_window, &c))
            ;
          else
            {
              fprintf (stderr, "%s: invalid `-id' option \"%s\"\n",
                       progname, argv[i] ? argv[i] : "");
              usage ();
              exit (-1);
            }
        }
    }

  dpy = XOpenDisplay (dpy_string);
  if (! dpy)
    exit (-1);

  if (sync_p)
    XSynchronize (dpy, True);

  exit (mozilla_remote_commands (dpy, (Window) remote_window,
                                 remote_commands));
}

#endif /* STANDALONE */

Reply via email to