218 lines
4.5 KiB
Go
218 lines
4.5 KiB
Go
package browsh
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/url"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
|
|
// TCell seems to be one of the best projects in any language for handling terminal
|
|
// standards across the major OSs.
|
|
"github.com/gdamore/tcell"
|
|
|
|
"github.com/go-errors/errors"
|
|
"github.com/spf13/pflag"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
var (
|
|
logo = `
|
|
//// ////
|
|
/ / / /
|
|
// //
|
|
// // ,,,,,,,,
|
|
//////// ..,,,,,,,,,
|
|
// // .., ,,, .,.
|
|
//////// .., ,,,,,..
|
|
//////// ..,,,,,,,,,
|
|
//////// ...........
|
|
//////////
|
|
****///////////////////
|
|
********///////////////
|
|
***********************`
|
|
// IsTesting is used in tests, so it needs to be exported
|
|
IsTesting = false
|
|
IsHTTPServerMode = false
|
|
logfile string
|
|
_ = pflag.Bool("version", false, "Output current Browsh version")
|
|
)
|
|
|
|
func setupLogging() {
|
|
out := os.Stderr
|
|
if *isDebug {
|
|
dir, err := os.Getwd()
|
|
if err != nil {
|
|
Shutdown(err)
|
|
}
|
|
logfile = fmt.Sprintf(filepath.Join(dir, "debug.log"))
|
|
if out, err = os.OpenFile(logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644); err != nil {
|
|
Shutdown(err)
|
|
}
|
|
}
|
|
slog.SetDefault(slog.New(slog.NewTextHandler(out, nil)))
|
|
}
|
|
|
|
// Initialise browsh
|
|
func Initialise() {
|
|
if IsTesting {
|
|
*isDebug = true
|
|
}
|
|
setupLogging()
|
|
loadConfig()
|
|
}
|
|
|
|
// Shutdown tries its best to cleanly shutdown browsh and the associated browser
|
|
func Shutdown(err error) {
|
|
msg := "shutting down"
|
|
var e *errors.Error
|
|
if errors.As(err, &e) {
|
|
slog.Error(msg, "errorStack", e.ErrorStack())
|
|
} else {
|
|
slog.Error(msg, "error", err)
|
|
}
|
|
if screen != nil {
|
|
screen.Fini()
|
|
}
|
|
exitCode := 0
|
|
if !errors.Is(err, errNormalExit) {
|
|
exitCode = 1
|
|
}
|
|
os.Exit(exitCode)
|
|
}
|
|
|
|
func Log(message string) {
|
|
}
|
|
|
|
func saveScreenshot(base64String string) {
|
|
dec, err := base64.StdEncoding.DecodeString(base64String)
|
|
if err != nil {
|
|
Shutdown(err)
|
|
}
|
|
file, err := os.CreateTemp("", "browsh-screenshot")
|
|
if err != nil {
|
|
Shutdown(err)
|
|
}
|
|
defer file.Close()
|
|
if _, err := file.Write(dec); err != nil {
|
|
Shutdown(err)
|
|
}
|
|
if err := file.Sync(); err != nil {
|
|
Shutdown(err)
|
|
}
|
|
fullPath := file.Name() + ".jpg"
|
|
if err := os.Rename(file.Name(), fullPath); err != nil {
|
|
Shutdown(err)
|
|
}
|
|
message := "Screenshot saved to " + fullPath
|
|
sendMessageToWebExtension("/status," + message)
|
|
}
|
|
|
|
// Shell provides nice and easy shell commands
|
|
func Shell(command string) string {
|
|
parts := strings.Fields(command)
|
|
head := parts[0]
|
|
parts = parts[1:]
|
|
out, err := exec.Command(head, parts...).CombinedOutput()
|
|
if err != nil {
|
|
err := fmt.Errorf(
|
|
"Browsh tried to run `%s` but failed with: %s, err: %w",
|
|
command,
|
|
string(out),
|
|
err,
|
|
)
|
|
Shutdown(err)
|
|
}
|
|
return strings.TrimSpace(string(out))
|
|
}
|
|
|
|
// TTYStart starts Browsh
|
|
func TTYStart(injectedScreen tcell.Screen) {
|
|
screen = injectedScreen
|
|
setupTcell()
|
|
writeString(1, 0, logo, tcell.StyleDefault)
|
|
writeString(
|
|
0,
|
|
15,
|
|
"Starting Browsh v"+browshVersion+", the modern text-based web browser.",
|
|
tcell.StyleDefault,
|
|
)
|
|
StartFirefox()
|
|
slog.Info("Starting Browsh CLI client")
|
|
go readStdin()
|
|
startWebSocketServer()
|
|
}
|
|
|
|
func toInt(char string) int {
|
|
i, err := strconv.ParseInt(char, 10, 16)
|
|
if err != nil {
|
|
Shutdown(err)
|
|
}
|
|
return int(i)
|
|
}
|
|
|
|
func toInt32(char string) int32 {
|
|
i, err := strconv.ParseInt(char, 10, 32)
|
|
if err != nil {
|
|
Shutdown(err)
|
|
}
|
|
return int32(i)
|
|
}
|
|
|
|
func ttyEntry() {
|
|
// Hack to force true colours
|
|
// Follow: https://github.com/gdamore/tcell/pull/183
|
|
if runtime.GOOS != "windows" {
|
|
// On windows this generates a "character set not supported" error. The error comes
|
|
// from tcell.
|
|
os.Setenv("TERM", "xterm-truecolor")
|
|
}
|
|
realScreen, err := tcell.NewScreen()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
TTYStart(realScreen)
|
|
}
|
|
|
|
// MainEntry decides between running Browsh as a CLI app or as an HTTP web server
|
|
func MainEntry() {
|
|
pflag.Parse()
|
|
// validURL contains array of valid user inputted links.
|
|
var validURL []string
|
|
if pflag.NArg() != 0 {
|
|
for i := 0; i < len(pflag.Args()); i++ {
|
|
u, _ := url.ParseRequestURI(pflag.Args()[i])
|
|
if u != nil {
|
|
validURL = append(validURL, pflag.Args()[i])
|
|
}
|
|
}
|
|
}
|
|
viper.SetDefault("validURL", validURL)
|
|
Initialise()
|
|
|
|
// Print version if asked and exit
|
|
if viper.GetBool("version") || viper.GetBool("v") {
|
|
println(browshVersion)
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Print name if asked and exit
|
|
if viper.GetBool("name") || viper.GetBool("n") {
|
|
println("Browsh")
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Decide whether to run in http-server-mode or CLI app
|
|
if viper.GetBool("http-server-mode") {
|
|
HTTPServerStart()
|
|
} else {
|
|
ttyEntry()
|
|
}
|
|
}
|