/* -*- Mode:C; tab-width: 8 -*- * vim:cindent:tabstop=8:shiftwidth=2:noexpandtab: */ /* * mozilla-remote.c - modified version of remote.c (see below) to add * -remotepipe option to accept input on standard input instead of on * the command line (only works for one option). (Also defined * STANDALONE by default, changed expected version to "5.0", commented * out include of "vroot.h", and changed end of main from |exit| to * |return| and changed return type to |int|. * * Modifications copyright 2003, L. David Baron. Same license as below, * of course. */ /* 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 , 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 x_cbug@netscape.com. */ #include #include #include #include #include #include #include /* 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" */ #define STANDALONE #ifdef STANDALONE static const char *progname = 0; static const char *expected_mozilla_version = "5.0"; #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 to specify the X server to use.\n\ -remote to execute a command in an already-running\n\ Netscape process. See the manual for a\n\ list of valid commands.\n\ -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); } int 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; char buf[10000]; size_t read_size, read_data_size = 1; char *read_data = 0; 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], "-remotepipe")) { 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 *))); } while ((read_size = fread(buf, 1, sizeof(buf), stdin)) > 0) { read_data_size += read_size; read_data = realloc(read_data, read_data_size); memcpy(read_data + (read_data_size - read_size - 1), buf, read_size); } read_data[read_data_size] = '\0'; remote_commands [remote_command_count++] = read_data; } 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); return mozilla_remote_commands (dpy, (Window) remote_window, remote_commands); } #endif /* STANDALONE */