webbrowser.py revision ea6b4187cf78ce1ac260c1620b1d4c0fd069beba
190f5ba538bf40bcf4fd41049c7bf4296d3ffc9c7Benjamin Peterson#! /usr/bin/env python3
20a8c29be4b5314d4bf72cb391ab078f7483cf3f7Ka-Ping Yee"""Interfaces for launching and remotely controlling Web browsers."""
3992d4a3e6e67a05b85350820157028a61d1f22cfGuido van Rossum# Maintained by Georg Brandl.
4c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
5ee763951dc56d4cdf0d39a12214bc1342fc3d93aFred Drakeimport io
6c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drakeimport os
7d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossumimport shlex
8c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drakeimport sys
9e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlimport stat
1023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlimport subprocess
1123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlimport time
12c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
13e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl__all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
1440fc16059f04ee8fda0b5956cc4883eb21ca8f8cSkip Montanaro
15c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drakeclass Error(Exception):
16c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake    pass
17c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
18658cba6706eb4a2ad8b3e235cf0db9fe1c8e9e6bTim Peters_browsers = {}          # Dictionary of available browser controllers
19658cba6706eb4a2ad8b3e235cf0db9fe1c8e9e6bTim Peters_tryorder = []          # Preference order of available browsers
20c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
21e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandldef register(name, klass, instance=None, update_tryorder=1):
22c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake    """Register a browser connector and, optionally, connection."""
23c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake    _browsers[name.lower()] = [klass, instance]
24e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if update_tryorder > 0:
25e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        _tryorder.append(name)
26e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    elif update_tryorder < 0:
27e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        _tryorder.insert(0, name)
28c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
29f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymonddef get(using=None):
30f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond    """Return a browser launcher instance appropriate for the environment."""
3110ff706e2788a7c7ef9f8ea0108a5ede625fedadRaymond Hettinger    if using is not None:
32f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond        alternatives = [using]
33f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond    else:
34f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond        alternatives = _tryorder
35f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond    for browser in alternatives:
36bac788a3cd348203a5fdabba52e5faf65bf35c5eRaymond Hettinger        if '%s' in browser:
3723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # User gave us a command line, split it into name and args
38d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            browser = shlex.split(browser)
39d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            if browser[-1] == '&':
40d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                return BackgroundBrowser(browser[:-1])
41d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            else:
42d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                return GenericBrowser(browser)
43f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond        else:
44e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            # User gave us a browser name or path.
45f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake            try:
46f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake                command = _browsers[browser.lower()]
47f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake            except KeyError:
48f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake                command = _synthesize(browser)
49e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if command[1] is not None:
50f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond                return command[1]
51e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            elif command[0] is not None:
52e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                return command[0]()
53f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond    raise Error("could not locate runnable browser")
54c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
55c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake# Please note: the following definition hides a builtin function.
56e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# It is recommended one does "import webbrowser" and uses webbrowser.open(url)
57e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# instead of "from webbrowser import *".
58c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
59e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalottidef open(url, new=0, autoraise=True):
60e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    for name in _tryorder:
61e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        browser = get(name)
62e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if browser.open(url, new, autoraise):
63e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            return True
64e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    return False
65c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
663f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drakedef open_new(url):
67e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    return open(url, 1)
68c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
69e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandldef open_new_tab(url):
70e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    return open(url, 2)
71f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
72e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
73e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandldef _synthesize(browser, update_tryorder=1):
74f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    """Attempt to synthesize a controller base on existing controllers.
75f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
76f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    This is useful to create a controller when a user specifies a path to
77f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    an entry in the BROWSER environment variable -- we can copy a general
78f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    controller to operate using a specific installation of the desired
79f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    browser in this way.
80f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
81f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    If we can't create a controller in this way, or if there is no
82f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    executable for the requested browser, return [None, None].
83f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
84f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    """
85e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    cmd = browser.split()[0]
86e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if not _iscommand(cmd):
87f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        return [None, None]
88e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    name = os.path.basename(cmd)
89f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    try:
90f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        command = _browsers[name.lower()]
91f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    except KeyError:
92f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        return [None, None]
93f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    # now attempt to clone to fit the new name:
94f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    controller = command[1]
95f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    if controller and name.lower() == controller.basename:
96f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        import copy
97f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        controller = copy.copy(controller)
98f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        controller.name = browser
99f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        controller.basename = os.path.basename(browser)
100e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        register(browser, None, controller, update_tryorder)
101f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        return [None, controller]
102118aa5337c745dece455f4782799c83b9e3a7d4fAndrew M. Kuchling    return [None, None]
103f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
1043f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
105e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif sys.platform[:3] == "win":
106e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def _isexecutable(cmd):
107e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        cmd = cmd.lower()
1080e3f591aeeef9ed715f8770320f4c4c7332a8794Thomas Wouters        if os.path.isfile(cmd) and cmd.endswith((".exe", ".bat")):
109e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            return True
110e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        for ext in ".exe", ".bat":
111e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if os.path.isfile(cmd + ext):
112e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                return True
113e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return False
114e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlelse:
115e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def _isexecutable(cmd):
116e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if os.path.isfile(cmd):
117e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            mode = os.stat(cmd)[stat.ST_MODE]
118e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH:
119e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                return True
120e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return False
121e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
1223f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drakedef _iscommand(cmd):
123e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Return True if cmd is executable or can be found on the executable
124e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    search path."""
125e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _isexecutable(cmd):
126e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return True
1273f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    path = os.environ.get("PATH")
1283f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    if not path:
129bc0e9108261693b6278687f4fb4709ff76c2e543Tim Peters        return False
1303f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    for d in path.split(os.pathsep):
1313f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        exe = os.path.join(d, cmd)
132e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if _isexecutable(exe):
133bc0e9108261693b6278687f4fb4709ff76c2e543Tim Peters            return True
134bc0e9108261693b6278687f4fb4709ff76c2e543Tim Peters    return False
1353f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
1363f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
137e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# General parent classes
138e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
139e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass BaseBrowser(object):
14023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    """Parent class for all browsers. Do not use directly."""
141887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
14223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    args = ['%s']
143887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
144e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def __init__(self, name=""):
145e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        self.name = name
146b980113a8df699797b837f103ac6d2049a214551Georg Brandl        self.basename = name
147536cf99536bce562cfcb44a856fac1c84b9de4c3Tim Peters
148e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalotti    def open(self, url, new=0, autoraise=True):
149196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz        raise NotImplementedError
150196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz
151e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def open_new(self, url):
152e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return self.open(url, 1)
153e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
154e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def open_new_tab(self, url):
155e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return self.open(url, 2)
156e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
1573f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
158e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass GenericBrowser(BaseBrowser):
159e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Class for all browsers started with a command
160e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl       and without remote functionality."""
1613f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
16223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    def __init__(self, name):
1633172c5d263eeffff1e89d03d79be3ccc1d60fbdeGuido van Rossum        if isinstance(name, str):
16423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            self.name = name
165992d4a3e6e67a05b85350820157028a61d1f22cfGuido van Rossum            self.args = ["%s"]
16623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
16723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # name should be a list with arguments
16823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            self.name = name[0]
16923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            self.args = name[1:]
170b980113a8df699797b837f103ac6d2049a214551Georg Brandl        self.basename = os.path.basename(self.name)
1713f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
172e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalotti    def open(self, url, new=0, autoraise=True):
173887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters        cmdline = [self.name] + [arg.replace("%s", url)
17423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 for arg in self.args]
17523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
17689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            if sys.platform[:3] == 'win':
17789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                p = subprocess.Popen(cmdline)
17889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            else:
17989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                p = subprocess.Popen(cmdline, close_fds=True)
18023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return not p.wait()
18123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
18223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return False
18323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
18423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
18523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlclass BackgroundBrowser(GenericBrowser):
18623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    """Class for all browsers which are to be started in the
18723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl       background."""
18823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
189e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalotti    def open(self, url, new=0, autoraise=True):
19023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        cmdline = [self.name] + [arg.replace("%s", url)
19123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 for arg in self.args]
19223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
19389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            if sys.platform[:3] == 'win':
19489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                p = subprocess.Popen(cmdline)
19589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            else:
19689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                setsid = getattr(os, 'setsid', None)
19789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                if not setsid:
19889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                    setsid = getattr(os, 'setpgrp', None)
19989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid)
20023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return (p.poll() is None)
20123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
20223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return False
2033f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
2043f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
205e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass UnixBrowser(BaseBrowser):
206e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Parent class for all Unix browsers with remote functionality."""
2073f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
208e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    raise_opts = None
20923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['%action', '%s']
210e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    remote_action = None
211e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    remote_action_newwin = None
212e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    remote_action_newtab = None
21323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = False
21423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    redirect_stdout = True
21523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
21623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    def _invoke(self, args, remote, autoraise):
21723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        raise_opt = []
21823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if remote and self.raise_opts:
21923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # use autoraise argument only for remote invocation
220e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalotti            autoraise = int(autoraise)
22123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            opt = self.raise_opts[autoraise]
22223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if opt: raise_opt = [opt]
22323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
22423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        cmdline = [self.name] + raise_opt + args
225887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
22623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if remote or self.background:
227bc2ce57203e1f355004ec95953eae9dec780ef9aAmaury Forgeot d'Arc            inout = io.open(os.devnull, "r+")
22823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
22923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # for TTY browsers, we need stdin/out
23023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            inout = None
23123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
23223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                             stdout=(self.redirect_stdout and inout or None),
2338f7724f9a4ec57d689a436a064e2e047b2ad0d97Gregory P. Smith                             stderr=inout, start_new_session=True)
23423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if remote:
2351beea5b7c181d9e7279dd58bc5fbee52b549e528Ezio Melotti            # wait five seconds. If the subprocess is not finished, the
23623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # remote invocation has (hopefully) started a new instance.
23723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            time.sleep(1)
23823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            rc = p.poll()
23923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if rc is None:
24023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                time.sleep(4)
24123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                rc = p.poll()
24223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                if rc is None:
24323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                    return True
24423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # if remote call failed, open() will try direct invocation
24523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return not rc
24623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        elif self.background:
24723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if p.poll() is None:
24823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                return True
24923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            else:
25023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                return False
25123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
25223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return not p.wait()
2533f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
254e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalotti    def open(self, url, new=0, autoraise=True):
255e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if new == 0:
256e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            action = self.remote_action
257e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        elif new == 1:
258e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            action = self.remote_action_newwin
259e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        elif new == 2:
260e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if self.remote_action_newtab is None:
261e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                action = self.remote_action_newwin
262e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            else:
263e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                action = self.remote_action_newtab
2643f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        else:
26523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            raise Error("Bad 'new' parameter to open(); " +
26623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                        "expected 0, 1, or 2, got %s" % new)
267887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
26823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        args = [arg.replace("%s", url).replace("%action", action)
26923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                for arg in self.remote_args]
27023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        success = self._invoke(args, True, autoraise)
27123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if not success:
27223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # remote invocation failed, try straight way
27323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            args = [arg.replace("%s", url) for arg in self.args]
27423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return self._invoke(args, False, False)
27523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
27623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return True
2773f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
2783f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
279e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass Mozilla(UnixBrowser):
280e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Launcher class for Mozilla/Netscape browsers."""
2813f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
28223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    raise_opts = ["-noraise", "-raise"]
28323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['-remote', 'openURL(%s%action)']
28423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action = ""
28523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newwin = ",new-window"
28623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newtab = ",new-tab"
28723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = True
2888dd28eb973a1c072448d961a6fb6a8a12cc3c950Neal Norwitz
289e8f244305ef4f257f6999b69601f4316b31faa5eGeorg BrandlNetscape = Mozilla
2908dd28eb973a1c072448d961a6fb6a8a12cc3c950Neal Norwitz
2918dd28eb973a1c072448d961a6fb6a8a12cc3c950Neal Norwitz
292e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass Galeon(UnixBrowser):
293e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Launcher class for Galeon/Epiphany browsers."""
294e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
29523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    raise_opts = ["-noraise", ""]
29623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['%action', '%s']
29723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action = "-n"
29823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newwin = "-w"
29923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = True
300e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
301e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
302ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaranclass Chrome(UnixBrowser):
303ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran    "Launcher class for Google Chrome browser."
304ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran
305ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran    remote_args = ['%action', '%s']
306ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran    remote_action = ""
307ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran    remote_action_newwin = "--new-window"
308ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran    remote_action_newtab = ""
309ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran    background = True
310ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran
311ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil KumaranChromium = Chrome
312ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran
313ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran
31423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlclass Opera(UnixBrowser):
31523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    "Launcher class for Opera browser."
3163f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
317dad532f7def9917707d9705fcd9201413e42ef75Terry Reedy    raise_opts = ["-noraise", ""]
31823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['-remote', 'openURL(%s%action)']
31923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action = ""
32023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newwin = ",new-window"
32123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newtab = ",new-page"
32223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = True
3233f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
324e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
32523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlclass Elinks(UnixBrowser):
32623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    "Launcher class for Elinks browsers."
327e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
32823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['-remote', 'openURL(%s%action)']
32923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action = ""
33023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newwin = ",new-window"
33123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newtab = ",new-tab"
33223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = False
3333f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
33423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    # elinks doesn't like its stdout to be redirected -
33523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    # it uses redirected stdout as a signal to do -dump
33623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    redirect_stdout = False
3373f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
3383f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
33923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlclass Konqueror(BaseBrowser):
34023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    """Controller for the KDE File Manager (kfm, or Konqueror).
341e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
34223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    See the output of ``kfmclient --commands``
34323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    for more information on the Konqueror remote-control interface.
34423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    """
345e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
346e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalotti    def open(self, url, new=0, autoraise=True):
34723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        # XXX Currently I know no way to prevent KFM from opening a new win.
34823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if new == 2:
34923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            action = "newTab"
35023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
35123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            action = "openURL"
352887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
3532b2b44dc2c2b95569e0e59c6dd4b12148ffba598Amaury Forgeot d'Arc        devnull = io.open(os.devnull, "r+")
35423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        # if possible, put browser in separate process group, so
35523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        # keyboard interrupts don't affect browser as well as Python
35623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        setsid = getattr(os, 'setsid', None)
35723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if not setsid:
35823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            setsid = getattr(os, 'setpgrp', None)
359887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
36023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
36123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            p = subprocess.Popen(["kfmclient", action, url],
36223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 close_fds=True, stdin=devnull,
36323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 stdout=devnull, stderr=devnull)
36423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
36523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # fall through to next variant
36623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            pass
36723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
36823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            p.wait()
36923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # kfmclient's return code unfortunately has no meaning as it seems
37023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return True
371e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
37223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
37323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            p = subprocess.Popen(["konqueror", "--silent", url],
37423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 close_fds=True, stdin=devnull,
37523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 stdout=devnull, stderr=devnull,
37623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 preexec_fn=setsid)
37723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
37823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # fall through to next variant
37923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            pass
38023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
38123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if p.poll() is None:
38223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                # Should be running now.
38323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                return True
384887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
38523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
38623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            p = subprocess.Popen(["kfm", "-d", url],
38723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 close_fds=True, stdin=devnull,
38823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 stdout=devnull, stderr=devnull,
38923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 preexec_fn=setsid)
39023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
39123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return False
39223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
39323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return (p.poll() is None)
394e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
395e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
396e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass Grail(BaseBrowser):
3973f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    # There should be a way to maintain a connection to Grail, but the
3983f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    # Grail remote control protocol doesn't really allow that at this
39923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    # point.  It probably never will!
4003f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    def _find_grail_rc(self):
4013f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        import glob
4023f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        import pwd
4033f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        import socket
4043f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        import tempfile
4053f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        tempdir = os.path.join(tempfile.gettempdir(),
4063f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                               ".grail-unix")
40716623fe3e606efdedead8ccccaf7ef583e1be97fFred Drake        user = pwd.getpwuid(os.getuid())[0]
4083f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        filename = os.path.join(tempdir, user + "-*")
4093f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        maybes = glob.glob(filename)
4103f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        if not maybes:
4113f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            return None
4123f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
4133f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        for fn in maybes:
4143f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            # need to PING each one until we find one that's live
4153f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            try:
4163f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                s.connect(fn)
4173f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            except socket.error:
4183f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                # no good; attempt to clean it out, but don't fail:
4193f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                try:
4203f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                    os.unlink(fn)
4213f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                except IOError:
4223f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                    pass
4233f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            else:
4243f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                return s
4253f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
4263f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    def _remote(self, action):
4273f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        s = self._find_grail_rc()
4283f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        if not s:
4293f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            return 0
4303f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        s.send(action)
4313f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        s.close()
4323f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        return 1
4333f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
434e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalotti    def open(self, url, new=0, autoraise=True):
4353f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        if new:
436e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            ok = self._remote("LOADNEW " + url)
4373f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        else:
438e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            ok = self._remote("LOAD " + url)
439e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return ok
4403f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
441c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
442658cba6706eb4a2ad8b3e235cf0db9fe1c8e9e6bTim Peters#
443f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# Platform support for Unix
444f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond#
445c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
446e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# These are the right tests because all these Unix browsers require either
447e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# a console terminal or an X display to run.
448e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
449196f733d935bd51f8674c9761420ce990694f33aNeal Norwitzdef register_X_browsers():
450d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
451d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # The default GNOME browser
452d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"):
453d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum        register("gnome-open", None, BackgroundBrowser("gnome-open"))
454d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
455d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # The default KDE browser
456d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"):
457d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum        register("kfmclient", Konqueror, Konqueror("kfmclient"))
458d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
459d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # The Mozilla/Netscape browsers
4604a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl    for browser in ("mozilla-firefox", "firefox",
4614a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl                    "mozilla-firebird", "firebird",
462477c8d5e70240744d24631b18341ad892c8a8e1cThomas Wouters                    "seamonkey", "mozilla", "netscape"):
4634a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl        if _iscommand(browser):
4644a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl            register(browser, None, Mozilla(browser))
4654a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl
466e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Konqueror/kfm, the KDE browser.
467b980113a8df699797b837f103ac6d2049a214551Georg Brandl    if _iscommand("kfm"):
468b980113a8df699797b837f103ac6d2049a214551Georg Brandl        register("kfm", Konqueror, Konqueror("kfm"))
469b980113a8df699797b837f103ac6d2049a214551Georg Brandl    elif _iscommand("konqueror"):
470b980113a8df699797b837f103ac6d2049a214551Georg Brandl        register("konqueror", Konqueror, Konqueror("konqueror"))
471e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
472e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Gnome's Galeon and Epiphany
473e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    for browser in ("galeon", "epiphany"):
474e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if _iscommand(browser):
475e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            register(browser, None, Galeon(browser))
476e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
477e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Skipstone, another Gtk/Mozilla based browser
478e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("skipstone"):
47923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("skipstone", None, BackgroundBrowser("skipstone"))
480e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
481ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran    # Google Chrome/Chromium browsers
482ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran    for browser in ("google-chrome", "chrome", "chromium", "chromium-browser"):
483ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran        if _iscommand(browser):
484ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran            register(browser, None, Chrome(browser))
485ea6b4187cf78ce1ac260c1620b1d4c0fd069bebaSenthil Kumaran
486e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Opera, quite popular
487e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("opera"):
488e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        register("opera", None, Opera("opera"))
489e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
490e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Next, Mosaic -- old but still in use.
491e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("mosaic"):
49223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("mosaic", None, BackgroundBrowser("mosaic"))
493e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
494e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Grail, the Python browser. Does anybody still use it?
495e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("grail"):
496e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        register("grail", Grail, None)
497e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
498196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz# Prefer X browsers if present
499196f733d935bd51f8674c9761420ce990694f33aNeal Norwitzif os.environ.get("DISPLAY"):
500196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz    register_X_browsers()
501196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz
502e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# Also try console browsers
503e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif os.environ.get("TERM"):
504e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
505e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("links"):
50623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("links", None, GenericBrowser("links"))
507e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("elinks"):
508e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        register("elinks", None, Elinks("elinks"))
509e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
510e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("lynx"):
51123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("lynx", None, GenericBrowser("lynx"))
512e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # The w3m browser <http://w3m.sourceforge.net/>
513e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("w3m"):
51423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("w3m", None, GenericBrowser("w3m"))
5153f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
516f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond#
517f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# Platform support for Windows
518f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond#
519c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
520f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymondif sys.platform[:3] == "win":
521e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    class WindowsDefault(BaseBrowser):
522e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalotti        def open(self, url, new=0, autoraise=True):
523d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            try:
524d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                os.startfile(url)
525d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            except WindowsError:
526d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                # [Error 22] No application is associated with the specified
527d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                # file for this operation: '<URL>'
528d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                return False
529d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            else:
530d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                return True
531e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
532e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _tryorder = []
533e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _browsers = {}
534d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
535d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # First try to use the default Windows browser
536d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    register("windows-default", WindowsDefault)
537d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
538d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # Detect some common Windows browsers, fallback to IE
539d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
540d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                            "Internet Explorer\\IEXPLORE.EXE")
541477c8d5e70240744d24631b18341ad892c8a8e1cThomas Wouters    for browser in ("firefox", "firebird", "seamonkey", "mozilla",
542d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                    "netscape", "opera", iexplore):
543e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if _iscommand(browser):
54423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            register(browser, None, BackgroundBrowser(browser))
545c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
546c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake#
547f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# Platform support for MacOS
548f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond#
549c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
550e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif sys.platform == 'darwin':
551e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Adapted from patch submitted to SourceForge by Steven J. Burr
552e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    class MacOSX(BaseBrowser):
553e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        """Launcher class for Aqua browsers on Mac OS X
554e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
555e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        Optionally specify a browser name on instantiation.  Note that this
556e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        will not work for Aqua browsers if the user has moved the application
557e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        package after installation.
558e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
559e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        If no browser is specified, the default browser, as specified in the
560e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        Internet System Preferences panel, will be used.
561e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        """
562e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        def __init__(self, name):
563e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            self.name = name
564e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
565e223eb8477c2d3241641a8a3e266a28d6dcd0c0eAlexandre Vassalotti        def open(self, url, new=0, autoraise=True):
566e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            assert "'" not in url
56723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # hack for local urls
56823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if not ':' in url:
56923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                url = 'file:'+url
570887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
571e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            # new must be 0 or 1
572e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            new = int(bool(new))
573e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if self.name == "default":
574e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                # User called open, open_new or get without a browser parameter
5751cb179e93fb0f698fdb5f215b3864c578d910d9aGeorg Brandl                script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
576e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            else:
577e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                # User called get and chose a browser
578e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                if self.name == "OmniWeb":
579e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                    toWindow = ""
580e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                else:
581e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                    # Include toWindow parameter of OpenURL command for browsers
582e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                    # that support it.  0 == new window; -1 == existing
583e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                    toWindow = "toWindow %d" % (new - 1)
5841cb179e93fb0f698fdb5f215b3864c578d910d9aGeorg Brandl                cmd = 'OpenURL "%s"' % url.replace('"', '%22')
585e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                script = '''tell application "%s"
586e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                                activate
587e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                                %s %s
588e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                            end tell''' % (self.name, cmd, toWindow)
589e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            # Open pipe to AppleScript through osascript command
590e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            osapipe = os.popen("osascript", "w")
591e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if osapipe is None:
592e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                return False
593e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            # Write script to osascript's stdin
594e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            osapipe.write(script)
595e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            rc = osapipe.close()
596e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            return not rc
597e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
5984d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren    class MacOSXOSAScript(BaseBrowser):
5994d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren        def __init__(self, name):
6004d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren            self._name = name
6014d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren
6024d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren        def open(self, url, new=0, autoraise=True):
6034d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren            if self._name == 'default':
6044d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren                script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
6054d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren            else:
6064d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren                script = '''
6074d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren                   tell application "%s"
6084d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren                       activate
6094d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren                       open location "%s"
6104d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren                   end
6114d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren                   '''%(self._name, url.replace('"', '%22'))
6124d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren
6134d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren            osapipe = os.popen("osascript", "w")
6144d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren            if osapipe is None:
6154d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren                return False
6164d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren
6174d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren            osapipe.write(script)
6184d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren            rc = osapipe.close()
6194d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren            return not rc
6204d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren
6214d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren
622e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Don't clear _tryorder or _browsers since OS X can use above Unix support
623e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # (but we prefer using the OS X specific stuff)
6244d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren    register("safari", None, MacOSXOSAScript('safari'), -1)
6254d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren    register("firefox", None, MacOSXOSAScript('firefox'), -1)
6264d39f6e09a5c0a0e09eb51d678bacd1adaa3f2caRonald Oussoren    register("MacOSX", None, MacOSXOSAScript('default'), -1)
627e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
628f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond
6293a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis#
6303a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis# Platform support for OS/2
6313a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis#
6323a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis
633e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif sys.platform[:3] == "os2" and _iscommand("netscape"):
634e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _tryorder = []
635e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _browsers = {}
6363a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis    register("os2netscape", None,
63723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl             GenericBrowser(["start", "netscape", "%s"]), -1)
638e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
6393a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis
640f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# OK, now that we know what the default preference orders for each
641f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# platform are, allow user to override them with the BROWSER variable.
64254f0222547b1e92cd018ef132307a6f793dc9505Raymond Hettingerif "BROWSER" in os.environ:
643e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _userchoices = os.environ["BROWSER"].split(os.pathsep)
644e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _userchoices.reverse()
645e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
646e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Treat choices in same way as if passed into get() but do register
647e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # and prepend to _tryorder
648e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    for cmdline in _userchoices:
649e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if cmdline != '':
6508719ad5ddefadbc08b56a0af91515f050c89c678Benjamin Peterson            cmd = _synthesize(cmdline, -1)
6518719ad5ddefadbc08b56a0af91515f050c89c678Benjamin Peterson            if cmd[1] is None:
6528719ad5ddefadbc08b56a0af91515f050c89c678Benjamin Peterson                register(cmdline, None, GenericBrowser(cmdline), -1)
653e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    cmdline = None # to make del work if _userchoices was empty
654e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    del cmdline
655e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    del _userchoices
656e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
657cdab3bf7eb0810fcda21be065868f3da330779a1Skip Montanaro# what to do if _tryorder is now empty?
658e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
659e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
660e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandldef main():
661e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    import getopt
662e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    usage = """Usage: %s [-n | -t] url
663e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    -n: open new window
664e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    -t: open new tab""" % sys.argv[0]
665e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    try:
666e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        opts, args = getopt.getopt(sys.argv[1:], 'ntd')
667b940e113bf90ff71b0ef57414ea2beea9d2a4bc0Guido van Rossum    except getopt.error as msg:
668be19ed77ddb047e02fe94d142181062af6d99dccGuido van Rossum        print(msg, file=sys.stderr)
669be19ed77ddb047e02fe94d142181062af6d99dccGuido van Rossum        print(usage, file=sys.stderr)
670e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        sys.exit(1)
671e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    new_win = 0
672e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    for o, a in opts:
673e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if o == '-n': new_win = 1
674e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        elif o == '-t': new_win = 2
675b053cd8f40dd19985b16f50661640dcefb69888fGuido van Rossum    if len(args) != 1:
676be19ed77ddb047e02fe94d142181062af6d99dccGuido van Rossum        print(usage, file=sys.stderr)
677e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        sys.exit(1)
678e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
679e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    url = args[0]
680e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    open(url, new_win)
681e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
682be19ed77ddb047e02fe94d142181062af6d99dccGuido van Rossum    print("\a")
68323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
684e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif __name__ == "__main__":
685e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    main()
686