2016-08-13 12:49:04 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
2018-06-05 19:11:20 +00:00
|
|
|
"net"
|
2016-08-13 12:49:04 +00:00
|
|
|
"os"
|
2016-09-06 20:05:18 +00:00
|
|
|
"path/filepath"
|
2020-09-17 21:50:45 +00:00
|
|
|
"reflect"
|
2018-02-17 16:22:40 +00:00
|
|
|
"runtime"
|
2016-09-01 20:49:56 +00:00
|
|
|
"runtime/pprof"
|
2019-03-17 16:52:34 +00:00
|
|
|
"strconv"
|
2021-01-06 22:10:54 +00:00
|
|
|
"strings"
|
2016-08-13 12:49:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2018-07-31 21:21:55 +00:00
|
|
|
envPath = os.Getenv("PATH")
|
2018-07-28 13:52:54 +00:00
|
|
|
envLevel = os.Getenv("LF_LEVEL")
|
2016-08-13 12:49:04 +00:00
|
|
|
)
|
|
|
|
|
2021-01-06 22:10:54 +00:00
|
|
|
type arrayFlag []string
|
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
var (
|
2021-05-19 20:38:05 +00:00
|
|
|
gSingleMode bool
|
2016-12-17 21:47:37 +00:00
|
|
|
gClientID int
|
2017-08-06 08:05:46 +00:00
|
|
|
gHostname string
|
2016-08-14 12:45:24 +00:00
|
|
|
gLastDirPath string
|
2016-08-13 12:49:04 +00:00
|
|
|
gSelectionPath string
|
2017-07-20 22:26:36 +00:00
|
|
|
gSocketProt string
|
2016-08-13 12:49:04 +00:00
|
|
|
gSocketPath string
|
|
|
|
gLogPath string
|
|
|
|
gServerLogPath string
|
2019-02-10 16:48:49 +00:00
|
|
|
gSelect string
|
2021-04-13 20:29:26 +00:00
|
|
|
gConfigPath string
|
2021-01-06 22:10:54 +00:00
|
|
|
gCommands arrayFlag
|
2018-03-22 17:42:15 +00:00
|
|
|
gVersion string
|
2016-08-13 12:49:04 +00:00
|
|
|
)
|
|
|
|
|
2021-01-06 22:10:54 +00:00
|
|
|
func (a *arrayFlag) Set(v string) error {
|
|
|
|
*a = append(*a, v)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *arrayFlag) String() string {
|
|
|
|
return strings.Join(*a, ", ")
|
|
|
|
}
|
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
func init() {
|
2017-11-19 18:55:13 +00:00
|
|
|
h, err := os.Hostname()
|
2017-08-06 08:05:46 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("hostname: %s", err)
|
2016-11-05 21:08:51 +00:00
|
|
|
}
|
2017-11-19 18:55:13 +00:00
|
|
|
gHostname = h
|
2018-07-28 13:52:54 +00:00
|
|
|
|
|
|
|
if envLevel == "" {
|
|
|
|
envLevel = "0"
|
|
|
|
}
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2019-03-17 16:52:34 +00:00
|
|
|
func exportEnvVars() {
|
|
|
|
os.Setenv("id", strconv.Itoa(gClientID))
|
|
|
|
|
|
|
|
os.Setenv("OPENER", envOpener)
|
|
|
|
os.Setenv("EDITOR", envEditor)
|
|
|
|
os.Setenv("PAGER", envPager)
|
|
|
|
os.Setenv("SHELL", envShell)
|
|
|
|
|
2021-04-13 20:48:49 +00:00
|
|
|
dir, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
|
|
|
}
|
|
|
|
os.Setenv("OLDPWD", dir)
|
|
|
|
|
2019-03-17 16:52:34 +00:00
|
|
|
level, err := strconv.Atoi(envLevel)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("reading lf level: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
level++
|
|
|
|
|
|
|
|
os.Setenv("LF_LEVEL", strconv.Itoa(level))
|
|
|
|
}
|
|
|
|
|
2020-09-17 21:50:45 +00:00
|
|
|
// used by exportOpts below
|
|
|
|
func fieldToString(field reflect.Value) string {
|
|
|
|
kind := field.Kind()
|
|
|
|
var value string
|
|
|
|
|
|
|
|
switch kind {
|
|
|
|
case reflect.Int:
|
|
|
|
value = strconv.Itoa(int(field.Int()))
|
|
|
|
case reflect.Bool:
|
|
|
|
value = strconv.FormatBool(field.Bool())
|
|
|
|
case reflect.Slice:
|
|
|
|
for i := 0; i < field.Len(); i++ {
|
|
|
|
element := field.Index(i)
|
|
|
|
|
|
|
|
if i == 0 {
|
|
|
|
value = fieldToString(element)
|
|
|
|
} else {
|
|
|
|
value += ":" + fieldToString(element)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
value = field.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
|
|
|
func exportOpts() {
|
|
|
|
e := reflect.ValueOf(&gOpts).Elem()
|
|
|
|
|
|
|
|
for i := 0; i < e.NumField(); i++ {
|
|
|
|
// Get name and prefix it with lf_
|
|
|
|
name := e.Type().Field(i).Name
|
|
|
|
name = fmt.Sprintf("lf_%s", name)
|
|
|
|
|
|
|
|
// Skip maps
|
|
|
|
if name == "lf_keys" || name == "lf_cmdkeys" || name == "lf_cmds" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get string representation of the value
|
|
|
|
if name == "lf_sortType" {
|
|
|
|
var sortby string
|
|
|
|
|
|
|
|
switch gOpts.sortType.method {
|
|
|
|
case naturalSort:
|
|
|
|
sortby = "natural"
|
|
|
|
case nameSort:
|
|
|
|
sortby = "name"
|
|
|
|
case sizeSort:
|
|
|
|
sortby = "size"
|
|
|
|
case timeSort:
|
|
|
|
sortby = "time"
|
|
|
|
case ctimeSort:
|
|
|
|
sortby = "ctime"
|
|
|
|
case atimeSort:
|
|
|
|
sortby = "atime"
|
|
|
|
case extSort:
|
|
|
|
sortby = "ext"
|
|
|
|
}
|
|
|
|
|
|
|
|
os.Setenv("lf_sortby", sortby)
|
|
|
|
|
|
|
|
reverse := strconv.FormatBool(gOpts.sortType.option&reverseSort != 0)
|
|
|
|
os.Setenv("lf_reverse", reverse)
|
|
|
|
|
|
|
|
hidden := strconv.FormatBool(gOpts.sortType.option&hiddenSort != 0)
|
|
|
|
os.Setenv("lf_hidden", hidden)
|
|
|
|
|
|
|
|
dirfirst := strconv.FormatBool(gOpts.sortType.option&dirfirstSort != 0)
|
|
|
|
os.Setenv("lf_dirfirst", dirfirst)
|
|
|
|
} else {
|
|
|
|
field := e.Field(i)
|
|
|
|
value := fieldToString(field)
|
|
|
|
|
|
|
|
os.Setenv(name, value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
func startServer() {
|
2018-07-31 21:21:55 +00:00
|
|
|
cmd := detachedCommand(os.Args[0], "-server")
|
2016-08-17 20:28:42 +00:00
|
|
|
if err := cmd.Start(); err != nil {
|
2016-08-17 20:06:45 +00:00
|
|
|
log.Printf("starting server: %s", err)
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-05 19:11:20 +00:00
|
|
|
func checkServer() {
|
|
|
|
if gSocketProt == "unix" {
|
|
|
|
if _, err := os.Stat(gSocketPath); os.IsNotExist(err) {
|
|
|
|
startServer()
|
|
|
|
} else if _, err := net.Dial(gSocketProt, gSocketPath); err != nil {
|
|
|
|
os.Remove(gSocketPath)
|
|
|
|
startServer()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if _, err := net.Dial(gSocketProt, gSocketPath); err != nil {
|
|
|
|
startServer()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
func main() {
|
2017-11-19 18:55:13 +00:00
|
|
|
showDoc := flag.Bool(
|
|
|
|
"doc",
|
|
|
|
false,
|
|
|
|
"show documentation")
|
|
|
|
|
2018-03-22 17:42:15 +00:00
|
|
|
showVersion := flag.Bool(
|
|
|
|
"version",
|
|
|
|
false,
|
|
|
|
"show version")
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
serverMode := flag.Bool(
|
|
|
|
"server",
|
|
|
|
false,
|
|
|
|
"start server (automatic)")
|
|
|
|
|
2021-05-19 20:38:05 +00:00
|
|
|
singleMode := flag.Bool(
|
|
|
|
"single",
|
|
|
|
false,
|
|
|
|
"start a client without server")
|
|
|
|
|
2021-04-13 20:29:26 +00:00
|
|
|
remoteCmd := flag.String(
|
|
|
|
"remote",
|
|
|
|
"",
|
|
|
|
"send remote command to server")
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
cpuprofile := flag.String(
|
|
|
|
"cpuprofile",
|
|
|
|
"",
|
2018-02-17 16:22:40 +00:00
|
|
|
"path to the file to write the CPU profile")
|
|
|
|
|
|
|
|
memprofile := flag.String(
|
|
|
|
"memprofile",
|
|
|
|
"",
|
|
|
|
"path to the file to write the memory profile")
|
2017-11-19 18:55:13 +00:00
|
|
|
|
|
|
|
flag.StringVar(&gLastDirPath,
|
|
|
|
"last-dir-path",
|
|
|
|
"",
|
|
|
|
"path to the file to write the last dir on exit (to use for cd)")
|
|
|
|
|
|
|
|
flag.StringVar(&gSelectionPath,
|
|
|
|
"selection-path",
|
|
|
|
"",
|
|
|
|
"path to the file to write selected files on open (to use as open file dialog)")
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2021-04-13 20:29:26 +00:00
|
|
|
flag.StringVar(&gConfigPath,
|
2021-03-09 11:35:50 +00:00
|
|
|
"config",
|
|
|
|
"",
|
2021-04-13 20:29:26 +00:00
|
|
|
"path to the config file (instead of the usual paths)")
|
2021-03-09 11:35:50 +00:00
|
|
|
|
2021-01-06 22:10:54 +00:00
|
|
|
flag.Var(&gCommands,
|
2019-02-06 11:56:06 +00:00
|
|
|
"command",
|
|
|
|
"command to execute on client initialization")
|
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
flag.Parse()
|
|
|
|
|
2017-08-06 15:26:38 +00:00
|
|
|
gSocketProt = gDefaultSocketProt
|
|
|
|
gSocketPath = gDefaultSocketPath
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
if *cpuprofile != "" {
|
|
|
|
f, err := os.Create(*cpuprofile)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("could not create CPU profile: %s", err)
|
|
|
|
}
|
|
|
|
if err := pprof.StartCPUProfile(f); err != nil {
|
|
|
|
log.Fatalf("could not start CPU profile: %s", err)
|
|
|
|
}
|
|
|
|
defer pprof.StopCPUProfile()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case *showDoc:
|
2016-09-15 14:08:05 +00:00
|
|
|
fmt.Print(genDocString)
|
2018-03-22 17:42:15 +00:00
|
|
|
case *showVersion:
|
|
|
|
fmt.Println(gVersion)
|
2017-11-19 18:55:13 +00:00
|
|
|
case *remoteCmd != "":
|
2019-02-26 18:27:04 +00:00
|
|
|
if err := remote(*remoteCmd); err != nil {
|
2016-12-19 18:28:57 +00:00
|
|
|
log.Fatalf("remote command: %s", err)
|
|
|
|
}
|
2017-11-19 18:55:13 +00:00
|
|
|
case *serverMode:
|
2020-09-09 11:08:52 +00:00
|
|
|
os.Chdir(gUser.HomeDir)
|
2017-08-06 08:05:46 +00:00
|
|
|
gServerLogPath = filepath.Join(os.TempDir(), fmt.Sprintf("lf.%s.server.log", gUser.Username))
|
2016-08-13 12:49:04 +00:00
|
|
|
serve()
|
2017-11-19 18:55:13 +00:00
|
|
|
default:
|
2021-05-19 20:38:05 +00:00
|
|
|
gSingleMode = *singleMode
|
|
|
|
|
|
|
|
if !gSingleMode {
|
|
|
|
checkServer()
|
|
|
|
}
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2021-01-02 22:58:01 +00:00
|
|
|
gClientID = os.Getpid()
|
2017-08-06 08:05:46 +00:00
|
|
|
gLogPath = filepath.Join(os.TempDir(), fmt.Sprintf("lf.%s.%d.log", gUser.Username, gClientID))
|
2018-07-30 17:56:57 +00:00
|
|
|
switch flag.NArg() {
|
|
|
|
case 0:
|
|
|
|
_, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
case 1:
|
2019-02-10 16:48:49 +00:00
|
|
|
gSelect = flag.Arg(0)
|
2018-07-30 17:56:57 +00:00
|
|
|
default:
|
2019-02-10 16:48:49 +00:00
|
|
|
fmt.Fprintf(os.Stderr, "only single file or directory is allowed\n")
|
2018-07-30 17:56:57 +00:00
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
|
2019-03-31 16:45:11 +00:00
|
|
|
exportEnvVars()
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
run()
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
2018-02-17 16:22:40 +00:00
|
|
|
|
|
|
|
if *memprofile != "" {
|
|
|
|
f, err := os.Create(*memprofile)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("could not create memory profile: ", err)
|
|
|
|
}
|
|
|
|
runtime.GC()
|
|
|
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
|
|
|
log.Fatal("could not write memory profile: ", err)
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
}
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|