138 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			138 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Package webbrowser provides a simple API for opening web pages on your
 | |
| // default browser.
 | |
| package webbrowser
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net/url"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 	"runtime"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	ErrCantOpenBrowser = errors.New("webbrowser: can't open browser")
 | |
| 	ErrNoCandidates    = errors.New("webbrowser: no browser candidate found for your OS")
 | |
| )
 | |
| 
 | |
| // Candidates contains a list of registered `Browser`s that will be tried with Open.
 | |
| var Candidates []Browser
 | |
| 
 | |
| type Browser interface {
 | |
| 	// Command returns a ready to be used Cmd that will open an URL.
 | |
| 	Command(string) (*exec.Cmd, error)
 | |
| 	// Open tries to open a URL in your default browser. NOTE: This may cause
 | |
| 	// your program to hang until the browser process is closed in some OSes,
 | |
| 	// see https://github.com/toqueteos/webbrowser/issues/4.
 | |
| 	Open(string) error
 | |
| }
 | |
| 
 | |
| // Open tries to open a URL in your default browser ensuring you have a display
 | |
| // set up and not running this from SSH. NOTE: This may cause your program to
 | |
| // hang until the browser process is closed in some OSes, see
 | |
| // https://github.com/toqueteos/webbrowser/issues/4.
 | |
| func Open(s string) (err error) {
 | |
| 	if len(Candidates) == 0 {
 | |
| 		return ErrNoCandidates
 | |
| 	}
 | |
| 
 | |
| 	// Try to determine if there's a display available (only linux) and we
 | |
| 	// aren't on a terminal (all but windows).
 | |
| 	switch runtime.GOOS {
 | |
| 	case "linux":
 | |
| 		// No display, no need to open a browser. Lynx users **MAY** have
 | |
| 		// something to say about this.
 | |
| 		if os.Getenv("DISPLAY") == "" {
 | |
| 			return fmt.Errorf("webbrowser: tried to open %q, no screen found", s)
 | |
| 		}
 | |
| 		fallthrough
 | |
| 	case "darwin":
 | |
| 		// Check SSH env vars.
 | |
| 		if os.Getenv("SSH_CLIENT") != "" || os.Getenv("SSH_TTY") != "" {
 | |
| 			return fmt.Errorf("webbrowser: tried to open %q, but you are running a shell session", s)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Try all candidates
 | |
| 	for _, candidate := range Candidates {
 | |
| 		err := candidate.Open(s)
 | |
| 		if err == nil {
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ErrCantOpenBrowser
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	// Register the default Browser for current OS, if it exists.
 | |
| 	if os, ok := osCommand[runtime.GOOS]; ok {
 | |
| 		Candidates = append(Candidates, browserCommand{os.cmd, os.args})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	osCommand = map[string]*browserCommand{
 | |
| 		"android": &browserCommand{"xdg-open", nil},
 | |
| 		"darwin":  &browserCommand{"open", nil},
 | |
| 		"freebsd": &browserCommand{"xdg-open", nil},
 | |
| 		"linux":   &browserCommand{"xdg-open", nil},
 | |
| 		"netbsd":  &browserCommand{"xdg-open", nil},
 | |
| 		"openbsd": &browserCommand{"xdg-open", nil}, // It may be open instead
 | |
| 		"windows": &browserCommand{"cmd", []string{"/c", "start"}},
 | |
| 	}
 | |
| 	winSchemes = [3]string{"https", "http", "file"}
 | |
| )
 | |
| 
 | |
| type browserCommand struct {
 | |
| 	cmd  string
 | |
| 	args []string
 | |
| }
 | |
| 
 | |
| func (b browserCommand) Command(s string) (*exec.Cmd, error) {
 | |
| 	u, err := url.Parse(s)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	validUrl := ensureValidURL(u)
 | |
| 
 | |
| 	b.args = append(b.args, validUrl)
 | |
| 
 | |
| 	return exec.Command(b.cmd, b.args...), nil
 | |
| }
 | |
| 
 | |
| func (b browserCommand) Open(s string) error {
 | |
| 	cmd, err := b.Command(s)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	return cmd.Run()
 | |
| }
 | |
| 
 | |
| func ensureScheme(u *url.URL) {
 | |
| 	for _, s := range winSchemes {
 | |
| 		if u.Scheme == s {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	u.Scheme = "http"
 | |
| }
 | |
| 
 | |
| func ensureValidURL(u *url.URL) string {
 | |
| 	// Enforce a scheme (windows requires scheme to be set to work properly).
 | |
| 	ensureScheme(u)
 | |
| 	s := u.String()
 | |
| 
 | |
| 	// Escape characters not allowed by cmd/bash
 | |
| 	switch runtime.GOOS {
 | |
| 	case "windows":
 | |
| 		s = strings.Replace(s, "&", `^&`, -1)
 | |
| 	}
 | |
| 
 | |
| 	return s
 | |
| }
 |