New module to control Web browsers; see the documentation for
more information.
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
new file mode 100644
index 0000000..5a4a80f
--- /dev/null
+++ b/Lib/webbrowser.py
@@ -0,0 +1,229 @@
+"""Remote-control interfaces to some browsers."""
+
+import os
+import sys
+
+
+PROCESS_CREATION_DELAY = 4
+
+
+class Error(Exception):
+    pass
+
+
+_browsers = {}
+
+def register(name, klass, instance=None):
+    """Register a browser connector and, optionally, connection."""
+    _browsers[name.lower()] = [klass, instance]
+
+
+def get(name=None):
+    """Retrieve a connection to a browser by type name, or the default
+    browser."""
+    name = name or DEFAULT_BROWSER
+    try:
+        L = _browsers[name.lower()]
+    except KeyError:
+        raise ValueError, "unknown browser type: " + `name`
+    if L[1] is None:
+        L[1] = L[0]()
+    return L[1]
+
+
+# Please note: the following definition hides a builtin function.
+
+def open(url, new=0):
+    get().open(url, new)
+
+
+def open_new(url):
+    get().open_new(url)
+
+
+def _iscommand(cmd):
+    """Return true if cmd can be found on the executable search path."""
+    path = os.environ.get("PATH")
+    if not path:
+        return 0
+    for d in path.split(os.pathsep):
+        exe = os.path.join(d, cmd)
+        if os.path.isfile(exe):
+            return 1
+    return 0
+
+
+class CommandLineBrowser:
+    _browsers = []
+    if os.environ.get("DISPLAY"):
+        _browsers.extend([
+            ("netscape", "netscape %s >/dev/null &"),
+            ("mosaic", "mosaic %s >/dev/null &"),
+            ])
+    _browsers.extend([
+        ("lynx", "lynx %s"),
+        ("w3m", "w3m %s"),
+        ])
+
+    def open(self, url, new=0):
+        for exe, cmd in self._browsers:
+            if _iscommand(exe):
+                os.system(cmd % url)
+                return
+        raise Error("could not locate runnable browser")
+
+    def open_new(self, url):
+        self.open(url)
+
+register("command-line", CommandLineBrowser)
+
+
+class Netscape:
+    autoRaise = 1
+
+    def _remote(self, action):
+        raise_opt = ("-noraise", "-raise")[self.autoRaise]
+        cmd = "netscape %s -remote '%s' >/dev/null 2>&1" % (raise_opt, action)
+        rc = os.system(cmd)
+        if rc:
+            import time
+            os.system("netscape -no-about-splash &")
+            time.sleep(PROCESS_CREATION_DELAY)
+            rc = os.system(cmd)
+        return not rc
+
+    def open(self, url, new=0):
+        if new:
+            self.open_new(url)
+        else:
+            self._remote("openURL(%s)" % url)
+
+    def open_new(self, url):
+        self._remote("openURL(%s, new-window)" % url)
+
+register("netscape", Netscape)
+
+
+class Konquerer:
+    """Controller for the KDE File Manager (kfm, or Konquerer).
+
+    See http://developer.kde.org/documentation/other/kfmclient.html
+    for more information on the Konquerer remote-control interface.
+
+    """
+    def _remote(self, action):
+        cmd = "kfmclient %s >/dev/null 2>&1" % action
+        rc = os.system(cmd)
+        if rc:
+            import time
+            os.system("kfm -d &")
+            time.sleep(PROCESS_CREATION_DELAY)
+            rc = os.system(cmd)
+        return not rc
+
+    def open(self, url, new=1):
+	# XXX currently I know no way to prevent KFM from opening a new win.
+        self.open_new(url)
+
+    def open_new(self, url):
+        self._remote("openURL %s" % url)
+
+register("kfm", Konquerer)
+
+
+class Grail:
+    # There should be a way to maintain a connection to Grail, but the
+    # Grail remote control protocol doesn't really allow that at this
+    # point.  It probably never will!
+
+    def _find_grail_rc(self):
+        import glob
+        import pwd
+        import socket
+        import tempfile
+        tempdir = os.path.join(tempfile.gettempdir(), ".grail-unix")
+        user = pwd.getpwuid(_os.getuid())[0]
+        filename = os.path.join(tempdir, user + "-*")
+        maybes = glob.glob(filename)
+        if not maybes:
+            return None
+        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        for fn in maybes:
+            # need to PING each one until we find one that's live
+            try:
+                s.connect(fn)
+            except socket.error:
+                # no good; attempt to clean it out, but don't fail:
+                try:
+                    os.unlink(fn)
+                except IOError:
+                    pass
+            else:
+                return s
+
+    def _remote(self, action):
+        s = self._find_grail_rc()
+        if not s:
+            return 0
+        s.send(action)
+        s.close()
+        return 1
+
+    def open(self, url, new=0):
+        if new:
+            self.open_new(url)
+        else:
+            self._remote("LOAD " + url)
+
+    def open_new(self, url):
+        self._remote("LOADNEW " + url)
+
+register("grail", Grail)
+
+
+class WindowsDefault:
+    def open(self, url, new=0):
+        import win32api, win32con
+        win32api.ShellExecute(0, "open", url, None, ".",
+                              win32con.SW_SHOWNORMAL)
+
+    def open_new(self, url):
+        self.open(url)
+
+
+DEFAULT_BROWSER = "command-line"
+
+if sys.platform[:3] == "win":
+    del _browsers["kfm"]
+    register("windows-default", WindowsDefault)
+    DEFAULT_BROWSER = "windows-default"
+elif os.environ.get("DISPLAY"):
+    if os.environ.get("KDEDIR"):
+        DEFAULT_BROWSER = "kfm"
+    elif _iscommand("netscape"):
+        DEFAULT_BROWSER = "netscape"
+
+# If the $BROWSER environment variable is set and true, let that be
+# the name of the browser to use:
+#
+DEFAULT_BROWSER = os.environ.get("BROWSER") or DEFAULT_BROWSER
+
+
+# Now try to support the MacOS world.  This is the only supported
+# controller on that platform, so don't mess with the default!
+
+try:
+    import ic
+except ImportError:
+    pass
+else:
+    class InternetConfig:
+        def open(self, url, new=0):
+            ic.launcurl(url)
+
+        def open_new(self, url):
+            self.open(url)
+
+    _browsers.clear()
+    register("internet-config", InternetConfig)
+    DEFAULT_BROWSER = "internet-config"