|
1 #! /usr/bin/env python |
|
2 """Interfaces for launching and remotely controlling Web browsers.""" |
|
3 # Maintained by Georg Brandl. |
|
4 |
|
5 import os |
|
6 import shlex |
|
7 import sys |
|
8 import stat |
|
9 import subprocess |
|
10 import time |
|
11 |
|
12 __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] |
|
13 |
|
14 class Error(Exception): |
|
15 pass |
|
16 |
|
17 _browsers = {} # Dictionary of available browser controllers |
|
18 _tryorder = [] # Preference order of available browsers |
|
19 |
|
20 def register(name, klass, instance=None, update_tryorder=1): |
|
21 """Register a browser connector and, optionally, connection.""" |
|
22 _browsers[name.lower()] = [klass, instance] |
|
23 if update_tryorder > 0: |
|
24 _tryorder.append(name) |
|
25 elif update_tryorder < 0: |
|
26 _tryorder.insert(0, name) |
|
27 |
|
28 def get(using=None): |
|
29 """Return a browser launcher instance appropriate for the environment.""" |
|
30 if using is not None: |
|
31 alternatives = [using] |
|
32 else: |
|
33 alternatives = _tryorder |
|
34 for browser in alternatives: |
|
35 if '%s' in browser: |
|
36 # User gave us a command line, split it into name and args |
|
37 browser = shlex.split(browser) |
|
38 if browser[-1] == '&': |
|
39 return BackgroundBrowser(browser[:-1]) |
|
40 else: |
|
41 return GenericBrowser(browser) |
|
42 else: |
|
43 # User gave us a browser name or path. |
|
44 try: |
|
45 command = _browsers[browser.lower()] |
|
46 except KeyError: |
|
47 command = _synthesize(browser) |
|
48 if command[1] is not None: |
|
49 return command[1] |
|
50 elif command[0] is not None: |
|
51 return command[0]() |
|
52 raise Error("could not locate runnable browser") |
|
53 |
|
54 # Please note: the following definition hides a builtin function. |
|
55 # It is recommended one does "import webbrowser" and uses webbrowser.open(url) |
|
56 # instead of "from webbrowser import *". |
|
57 |
|
58 def open(url, new=0, autoraise=1): |
|
59 for name in _tryorder: |
|
60 browser = get(name) |
|
61 if browser.open(url, new, autoraise): |
|
62 return True |
|
63 return False |
|
64 |
|
65 def open_new(url): |
|
66 return open(url, 1) |
|
67 |
|
68 def open_new_tab(url): |
|
69 return open(url, 2) |
|
70 |
|
71 |
|
72 def _synthesize(browser, update_tryorder=1): |
|
73 """Attempt to synthesize a controller base on existing controllers. |
|
74 |
|
75 This is useful to create a controller when a user specifies a path to |
|
76 an entry in the BROWSER environment variable -- we can copy a general |
|
77 controller to operate using a specific installation of the desired |
|
78 browser in this way. |
|
79 |
|
80 If we can't create a controller in this way, or if there is no |
|
81 executable for the requested browser, return [None, None]. |
|
82 |
|
83 """ |
|
84 cmd = browser.split()[0] |
|
85 if not _iscommand(cmd): |
|
86 return [None, None] |
|
87 name = os.path.basename(cmd) |
|
88 try: |
|
89 command = _browsers[name.lower()] |
|
90 except KeyError: |
|
91 return [None, None] |
|
92 # now attempt to clone to fit the new name: |
|
93 controller = command[1] |
|
94 if controller and name.lower() == controller.basename: |
|
95 import copy |
|
96 controller = copy.copy(controller) |
|
97 controller.name = browser |
|
98 controller.basename = os.path.basename(browser) |
|
99 register(browser, None, controller, update_tryorder) |
|
100 return [None, controller] |
|
101 return [None, None] |
|
102 |
|
103 |
|
104 if sys.platform[:3] == "win": |
|
105 def _isexecutable(cmd): |
|
106 cmd = cmd.lower() |
|
107 if os.path.isfile(cmd) and cmd.endswith((".exe", ".bat")): |
|
108 return True |
|
109 for ext in ".exe", ".bat": |
|
110 if os.path.isfile(cmd + ext): |
|
111 return True |
|
112 return False |
|
113 else: |
|
114 def _isexecutable(cmd): |
|
115 if os.path.isfile(cmd): |
|
116 mode = os.stat(cmd)[stat.ST_MODE] |
|
117 if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH: |
|
118 return True |
|
119 return False |
|
120 |
|
121 def _iscommand(cmd): |
|
122 """Return True if cmd is executable or can be found on the executable |
|
123 search path.""" |
|
124 if _isexecutable(cmd): |
|
125 return True |
|
126 path = os.environ.get("PATH") |
|
127 if not path: |
|
128 return False |
|
129 for d in path.split(os.pathsep): |
|
130 exe = os.path.join(d, cmd) |
|
131 if _isexecutable(exe): |
|
132 return True |
|
133 return False |
|
134 |
|
135 |
|
136 # General parent classes |
|
137 |
|
138 class BaseBrowser(object): |
|
139 """Parent class for all browsers. Do not use directly.""" |
|
140 |
|
141 args = ['%s'] |
|
142 |
|
143 def __init__(self, name=""): |
|
144 self.name = name |
|
145 self.basename = name |
|
146 |
|
147 def open(self, url, new=0, autoraise=1): |
|
148 raise NotImplementedError |
|
149 |
|
150 def open_new(self, url): |
|
151 return self.open(url, 1) |
|
152 |
|
153 def open_new_tab(self, url): |
|
154 return self.open(url, 2) |
|
155 |
|
156 |
|
157 class GenericBrowser(BaseBrowser): |
|
158 """Class for all browsers started with a command |
|
159 and without remote functionality.""" |
|
160 |
|
161 def __init__(self, name): |
|
162 if isinstance(name, basestring): |
|
163 self.name = name |
|
164 self.args = ["%s"] |
|
165 else: |
|
166 # name should be a list with arguments |
|
167 self.name = name[0] |
|
168 self.args = name[1:] |
|
169 self.basename = os.path.basename(self.name) |
|
170 |
|
171 def open(self, url, new=0, autoraise=1): |
|
172 cmdline = [self.name] + [arg.replace("%s", url) |
|
173 for arg in self.args] |
|
174 try: |
|
175 if sys.platform[:3] == 'win': |
|
176 p = subprocess.Popen(cmdline) |
|
177 else: |
|
178 p = subprocess.Popen(cmdline, close_fds=True) |
|
179 return not p.wait() |
|
180 except OSError: |
|
181 return False |
|
182 |
|
183 |
|
184 class BackgroundBrowser(GenericBrowser): |
|
185 """Class for all browsers which are to be started in the |
|
186 background.""" |
|
187 |
|
188 def open(self, url, new=0, autoraise=1): |
|
189 cmdline = [self.name] + [arg.replace("%s", url) |
|
190 for arg in self.args] |
|
191 try: |
|
192 if sys.platform[:3] == 'win': |
|
193 p = subprocess.Popen(cmdline) |
|
194 else: |
|
195 setsid = getattr(os, 'setsid', None) |
|
196 if not setsid: |
|
197 setsid = getattr(os, 'setpgrp', None) |
|
198 p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid) |
|
199 return (p.poll() is None) |
|
200 except OSError: |
|
201 return False |
|
202 |
|
203 |
|
204 class UnixBrowser(BaseBrowser): |
|
205 """Parent class for all Unix browsers with remote functionality.""" |
|
206 |
|
207 raise_opts = None |
|
208 remote_args = ['%action', '%s'] |
|
209 remote_action = None |
|
210 remote_action_newwin = None |
|
211 remote_action_newtab = None |
|
212 background = False |
|
213 redirect_stdout = True |
|
214 |
|
215 def _invoke(self, args, remote, autoraise): |
|
216 raise_opt = [] |
|
217 if remote and self.raise_opts: |
|
218 # use autoraise argument only for remote invocation |
|
219 autoraise = int(bool(autoraise)) |
|
220 opt = self.raise_opts[autoraise] |
|
221 if opt: raise_opt = [opt] |
|
222 |
|
223 cmdline = [self.name] + raise_opt + args |
|
224 |
|
225 if remote or self.background: |
|
226 inout = file(os.devnull, "r+") |
|
227 else: |
|
228 # for TTY browsers, we need stdin/out |
|
229 inout = None |
|
230 # if possible, put browser in separate process group, so |
|
231 # keyboard interrupts don't affect browser as well as Python |
|
232 setsid = getattr(os, 'setsid', None) |
|
233 if not setsid: |
|
234 setsid = getattr(os, 'setpgrp', None) |
|
235 |
|
236 p = subprocess.Popen(cmdline, close_fds=True, stdin=inout, |
|
237 stdout=(self.redirect_stdout and inout or None), |
|
238 stderr=inout, preexec_fn=setsid) |
|
239 if remote: |
|
240 # wait five secons. If the subprocess is not finished, the |
|
241 # remote invocation has (hopefully) started a new instance. |
|
242 time.sleep(1) |
|
243 rc = p.poll() |
|
244 if rc is None: |
|
245 time.sleep(4) |
|
246 rc = p.poll() |
|
247 if rc is None: |
|
248 return True |
|
249 # if remote call failed, open() will try direct invocation |
|
250 return not rc |
|
251 elif self.background: |
|
252 if p.poll() is None: |
|
253 return True |
|
254 else: |
|
255 return False |
|
256 else: |
|
257 return not p.wait() |
|
258 |
|
259 def open(self, url, new=0, autoraise=1): |
|
260 if new == 0: |
|
261 action = self.remote_action |
|
262 elif new == 1: |
|
263 action = self.remote_action_newwin |
|
264 elif new == 2: |
|
265 if self.remote_action_newtab is None: |
|
266 action = self.remote_action_newwin |
|
267 else: |
|
268 action = self.remote_action_newtab |
|
269 else: |
|
270 raise Error("Bad 'new' parameter to open(); " + |
|
271 "expected 0, 1, or 2, got %s" % new) |
|
272 |
|
273 args = [arg.replace("%s", url).replace("%action", action) |
|
274 for arg in self.remote_args] |
|
275 success = self._invoke(args, True, autoraise) |
|
276 if not success: |
|
277 # remote invocation failed, try straight way |
|
278 args = [arg.replace("%s", url) for arg in self.args] |
|
279 return self._invoke(args, False, False) |
|
280 else: |
|
281 return True |
|
282 |
|
283 |
|
284 class Mozilla(UnixBrowser): |
|
285 """Launcher class for Mozilla/Netscape browsers.""" |
|
286 |
|
287 raise_opts = ["-noraise", "-raise"] |
|
288 |
|
289 remote_args = ['-remote', 'openURL(%s%action)'] |
|
290 remote_action = "" |
|
291 remote_action_newwin = ",new-window" |
|
292 remote_action_newtab = ",new-tab" |
|
293 |
|
294 background = True |
|
295 |
|
296 Netscape = Mozilla |
|
297 |
|
298 |
|
299 class Galeon(UnixBrowser): |
|
300 """Launcher class for Galeon/Epiphany browsers.""" |
|
301 |
|
302 raise_opts = ["-noraise", ""] |
|
303 remote_args = ['%action', '%s'] |
|
304 remote_action = "-n" |
|
305 remote_action_newwin = "-w" |
|
306 |
|
307 background = True |
|
308 |
|
309 |
|
310 class Opera(UnixBrowser): |
|
311 "Launcher class for Opera browser." |
|
312 |
|
313 raise_opts = ["", "-raise"] |
|
314 |
|
315 remote_args = ['-remote', 'openURL(%s%action)'] |
|
316 remote_action = "" |
|
317 remote_action_newwin = ",new-window" |
|
318 remote_action_newtab = ",new-page" |
|
319 background = True |
|
320 |
|
321 |
|
322 class Elinks(UnixBrowser): |
|
323 "Launcher class for Elinks browsers." |
|
324 |
|
325 remote_args = ['-remote', 'openURL(%s%action)'] |
|
326 remote_action = "" |
|
327 remote_action_newwin = ",new-window" |
|
328 remote_action_newtab = ",new-tab" |
|
329 background = False |
|
330 |
|
331 # elinks doesn't like its stdout to be redirected - |
|
332 # it uses redirected stdout as a signal to do -dump |
|
333 redirect_stdout = False |
|
334 |
|
335 |
|
336 class Konqueror(BaseBrowser): |
|
337 """Controller for the KDE File Manager (kfm, or Konqueror). |
|
338 |
|
339 See the output of ``kfmclient --commands`` |
|
340 for more information on the Konqueror remote-control interface. |
|
341 """ |
|
342 |
|
343 def open(self, url, new=0, autoraise=1): |
|
344 # XXX Currently I know no way to prevent KFM from opening a new win. |
|
345 if new == 2: |
|
346 action = "newTab" |
|
347 else: |
|
348 action = "openURL" |
|
349 |
|
350 devnull = file(os.devnull, "r+") |
|
351 # if possible, put browser in separate process group, so |
|
352 # keyboard interrupts don't affect browser as well as Python |
|
353 setsid = getattr(os, 'setsid', None) |
|
354 if not setsid: |
|
355 setsid = getattr(os, 'setpgrp', None) |
|
356 |
|
357 try: |
|
358 p = subprocess.Popen(["kfmclient", action, url], |
|
359 close_fds=True, stdin=devnull, |
|
360 stdout=devnull, stderr=devnull) |
|
361 except OSError: |
|
362 # fall through to next variant |
|
363 pass |
|
364 else: |
|
365 p.wait() |
|
366 # kfmclient's return code unfortunately has no meaning as it seems |
|
367 return True |
|
368 |
|
369 try: |
|
370 p = subprocess.Popen(["konqueror", "--silent", url], |
|
371 close_fds=True, stdin=devnull, |
|
372 stdout=devnull, stderr=devnull, |
|
373 preexec_fn=setsid) |
|
374 except OSError: |
|
375 # fall through to next variant |
|
376 pass |
|
377 else: |
|
378 if p.poll() is None: |
|
379 # Should be running now. |
|
380 return True |
|
381 |
|
382 try: |
|
383 p = subprocess.Popen(["kfm", "-d", url], |
|
384 close_fds=True, stdin=devnull, |
|
385 stdout=devnull, stderr=devnull, |
|
386 preexec_fn=setsid) |
|
387 except OSError: |
|
388 return False |
|
389 else: |
|
390 return (p.poll() is None) |
|
391 |
|
392 |
|
393 class Grail(BaseBrowser): |
|
394 # There should be a way to maintain a connection to Grail, but the |
|
395 # Grail remote control protocol doesn't really allow that at this |
|
396 # point. It probably never will! |
|
397 def _find_grail_rc(self): |
|
398 import glob |
|
399 import pwd |
|
400 import socket |
|
401 import tempfile |
|
402 tempdir = os.path.join(tempfile.gettempdir(), |
|
403 ".grail-unix") |
|
404 user = pwd.getpwuid(os.getuid())[0] |
|
405 filename = os.path.join(tempdir, user + "-*") |
|
406 maybes = glob.glob(filename) |
|
407 if not maybes: |
|
408 return None |
|
409 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
|
410 for fn in maybes: |
|
411 # need to PING each one until we find one that's live |
|
412 try: |
|
413 s.connect(fn) |
|
414 except socket.error: |
|
415 # no good; attempt to clean it out, but don't fail: |
|
416 try: |
|
417 os.unlink(fn) |
|
418 except IOError: |
|
419 pass |
|
420 else: |
|
421 return s |
|
422 |
|
423 def _remote(self, action): |
|
424 s = self._find_grail_rc() |
|
425 if not s: |
|
426 return 0 |
|
427 s.send(action) |
|
428 s.close() |
|
429 return 1 |
|
430 |
|
431 def open(self, url, new=0, autoraise=1): |
|
432 if new: |
|
433 ok = self._remote("LOADNEW " + url) |
|
434 else: |
|
435 ok = self._remote("LOAD " + url) |
|
436 return ok |
|
437 |
|
438 |
|
439 # |
|
440 # Platform support for Unix |
|
441 # |
|
442 |
|
443 # These are the right tests because all these Unix browsers require either |
|
444 # a console terminal or an X display to run. |
|
445 |
|
446 def register_X_browsers(): |
|
447 |
|
448 # The default GNOME browser |
|
449 if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"): |
|
450 register("gnome-open", None, BackgroundBrowser("gnome-open")) |
|
451 |
|
452 # The default KDE browser |
|
453 if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"): |
|
454 register("kfmclient", Konqueror, Konqueror("kfmclient")) |
|
455 |
|
456 # The Mozilla/Netscape browsers |
|
457 for browser in ("mozilla-firefox", "firefox", |
|
458 "mozilla-firebird", "firebird", |
|
459 "seamonkey", "mozilla", "netscape"): |
|
460 if _iscommand(browser): |
|
461 register(browser, None, Mozilla(browser)) |
|
462 |
|
463 # Konqueror/kfm, the KDE browser. |
|
464 if _iscommand("kfm"): |
|
465 register("kfm", Konqueror, Konqueror("kfm")) |
|
466 elif _iscommand("konqueror"): |
|
467 register("konqueror", Konqueror, Konqueror("konqueror")) |
|
468 |
|
469 # Gnome's Galeon and Epiphany |
|
470 for browser in ("galeon", "epiphany"): |
|
471 if _iscommand(browser): |
|
472 register(browser, None, Galeon(browser)) |
|
473 |
|
474 # Skipstone, another Gtk/Mozilla based browser |
|
475 if _iscommand("skipstone"): |
|
476 register("skipstone", None, BackgroundBrowser("skipstone")) |
|
477 |
|
478 # Opera, quite popular |
|
479 if _iscommand("opera"): |
|
480 register("opera", None, Opera("opera")) |
|
481 |
|
482 # Next, Mosaic -- old but still in use. |
|
483 if _iscommand("mosaic"): |
|
484 register("mosaic", None, BackgroundBrowser("mosaic")) |
|
485 |
|
486 # Grail, the Python browser. Does anybody still use it? |
|
487 if _iscommand("grail"): |
|
488 register("grail", Grail, None) |
|
489 |
|
490 # Prefer X browsers if present |
|
491 if os.environ.get("DISPLAY"): |
|
492 register_X_browsers() |
|
493 |
|
494 # Also try console browsers |
|
495 if os.environ.get("TERM"): |
|
496 # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/> |
|
497 if _iscommand("links"): |
|
498 register("links", None, GenericBrowser("links")) |
|
499 if _iscommand("elinks"): |
|
500 register("elinks", None, Elinks("elinks")) |
|
501 # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/> |
|
502 if _iscommand("lynx"): |
|
503 register("lynx", None, GenericBrowser("lynx")) |
|
504 # The w3m browser <http://w3m.sourceforge.net/> |
|
505 if _iscommand("w3m"): |
|
506 register("w3m", None, GenericBrowser("w3m")) |
|
507 |
|
508 # |
|
509 # Platform support for Windows |
|
510 # |
|
511 |
|
512 if sys.platform[:3] == "win": |
|
513 class WindowsDefault(BaseBrowser): |
|
514 def open(self, url, new=0, autoraise=1): |
|
515 try: |
|
516 os.startfile(url) |
|
517 except WindowsError: |
|
518 # [Error 22] No application is associated with the specified |
|
519 # file for this operation: '<URL>' |
|
520 return False |
|
521 else: |
|
522 return True |
|
523 |
|
524 _tryorder = [] |
|
525 _browsers = {} |
|
526 |
|
527 # First try to use the default Windows browser |
|
528 register("windows-default", WindowsDefault) |
|
529 |
|
530 # Detect some common Windows browsers, fallback to IE |
|
531 iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"), |
|
532 "Internet Explorer\\IEXPLORE.EXE") |
|
533 for browser in ("firefox", "firebird", "seamonkey", "mozilla", |
|
534 "netscape", "opera", iexplore): |
|
535 if _iscommand(browser): |
|
536 register(browser, None, BackgroundBrowser(browser)) |
|
537 |
|
538 # |
|
539 # Platform support for MacOS |
|
540 # |
|
541 |
|
542 try: |
|
543 import ic |
|
544 except ImportError: |
|
545 pass |
|
546 else: |
|
547 class InternetConfig(BaseBrowser): |
|
548 def open(self, url, new=0, autoraise=1): |
|
549 ic.launchurl(url) |
|
550 return True # Any way to get status? |
|
551 |
|
552 register("internet-config", InternetConfig, update_tryorder=-1) |
|
553 |
|
554 if sys.platform == 'darwin': |
|
555 # Adapted from patch submitted to SourceForge by Steven J. Burr |
|
556 class MacOSX(BaseBrowser): |
|
557 """Launcher class for Aqua browsers on Mac OS X |
|
558 |
|
559 Optionally specify a browser name on instantiation. Note that this |
|
560 will not work for Aqua browsers if the user has moved the application |
|
561 package after installation. |
|
562 |
|
563 If no browser is specified, the default browser, as specified in the |
|
564 Internet System Preferences panel, will be used. |
|
565 """ |
|
566 def __init__(self, name): |
|
567 self.name = name |
|
568 |
|
569 def open(self, url, new=0, autoraise=1): |
|
570 assert "'" not in url |
|
571 # hack for local urls |
|
572 if not ':' in url: |
|
573 url = 'file:'+url |
|
574 |
|
575 # new must be 0 or 1 |
|
576 new = int(bool(new)) |
|
577 if self.name == "default": |
|
578 # User called open, open_new or get without a browser parameter |
|
579 script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser |
|
580 else: |
|
581 # User called get and chose a browser |
|
582 if self.name == "OmniWeb": |
|
583 toWindow = "" |
|
584 else: |
|
585 # Include toWindow parameter of OpenURL command for browsers |
|
586 # that support it. 0 == new window; -1 == existing |
|
587 toWindow = "toWindow %d" % (new - 1) |
|
588 cmd = 'OpenURL "%s"' % url.replace('"', '%22') |
|
589 script = '''tell application "%s" |
|
590 activate |
|
591 %s %s |
|
592 end tell''' % (self.name, cmd, toWindow) |
|
593 # Open pipe to AppleScript through osascript command |
|
594 osapipe = os.popen("osascript", "w") |
|
595 if osapipe is None: |
|
596 return False |
|
597 # Write script to osascript's stdin |
|
598 osapipe.write(script) |
|
599 rc = osapipe.close() |
|
600 return not rc |
|
601 |
|
602 # Don't clear _tryorder or _browsers since OS X can use above Unix support |
|
603 # (but we prefer using the OS X specific stuff) |
|
604 register("MacOSX", None, MacOSX('default'), -1) |
|
605 |
|
606 |
|
607 # |
|
608 # Platform support for OS/2 |
|
609 # |
|
610 |
|
611 if sys.platform[:3] == "os2" and _iscommand("netscape"): |
|
612 _tryorder = [] |
|
613 _browsers = {} |
|
614 register("os2netscape", None, |
|
615 GenericBrowser(["start", "netscape", "%s"]), -1) |
|
616 |
|
617 |
|
618 # OK, now that we know what the default preference orders for each |
|
619 # platform are, allow user to override them with the BROWSER variable. |
|
620 if "BROWSER" in os.environ: |
|
621 _userchoices = os.environ["BROWSER"].split(os.pathsep) |
|
622 _userchoices.reverse() |
|
623 |
|
624 # Treat choices in same way as if passed into get() but do register |
|
625 # and prepend to _tryorder |
|
626 for cmdline in _userchoices: |
|
627 if cmdline != '': |
|
628 _synthesize(cmdline, -1) |
|
629 cmdline = None # to make del work if _userchoices was empty |
|
630 del cmdline |
|
631 del _userchoices |
|
632 |
|
633 # what to do if _tryorder is now empty? |
|
634 |
|
635 |
|
636 def main(): |
|
637 import getopt |
|
638 usage = """Usage: %s [-n | -t] url |
|
639 -n: open new window |
|
640 -t: open new tab""" % sys.argv[0] |
|
641 try: |
|
642 opts, args = getopt.getopt(sys.argv[1:], 'ntd') |
|
643 except getopt.error, msg: |
|
644 print >>sys.stderr, msg |
|
645 print >>sys.stderr, usage |
|
646 sys.exit(1) |
|
647 new_win = 0 |
|
648 for o, a in opts: |
|
649 if o == '-n': new_win = 1 |
|
650 elif o == '-t': new_win = 2 |
|
651 if len(args) <> 1: |
|
652 print >>sys.stderr, usage |
|
653 sys.exit(1) |
|
654 |
|
655 url = args[0] |
|
656 open(url, new_win) |
|
657 |
|
658 print "\a" |
|
659 |
|
660 if __name__ == "__main__": |
|
661 main() |