1package main 2 3import ( 4 "flag" 5 "fmt" 6 "log" 7 "net" 8 "os" 9 "path/filepath" 10 "reflect" 11 "runtime" 12 "runtime/pprof" 13 "strconv" 14 "strings" 15) 16 17var ( 18 envPath = os.Getenv("PATH") 19 envLevel = os.Getenv("LF_LEVEL") 20) 21 22type arrayFlag []string 23 24var ( 25 gSingleMode bool 26 gClientID int 27 gHostname string 28 gLastDirPath string 29 gSelectionPath string 30 gSocketProt string 31 gSocketPath string 32 gLogPath string 33 gServerLogPath string 34 gSelect string 35 gConfigPath string 36 gCommands arrayFlag 37 gVersion string 38) 39 40func (a *arrayFlag) Set(v string) error { 41 *a = append(*a, v) 42 return nil 43} 44 45func (a *arrayFlag) String() string { 46 return strings.Join(*a, ", ") 47} 48 49func init() { 50 h, err := os.Hostname() 51 if err != nil { 52 log.Printf("hostname: %s", err) 53 } 54 gHostname = h 55 56 if envLevel == "" { 57 envLevel = "0" 58 } 59} 60 61func exportEnvVars() { 62 os.Setenv("id", strconv.Itoa(gClientID)) 63 64 os.Setenv("OPENER", envOpener) 65 os.Setenv("EDITOR", envEditor) 66 os.Setenv("PAGER", envPager) 67 os.Setenv("SHELL", envShell) 68 69 dir, err := os.Getwd() 70 if err != nil { 71 fmt.Fprintf(os.Stderr, "%s\n", err) 72 } 73 os.Setenv("OLDPWD", dir) 74 75 level, err := strconv.Atoi(envLevel) 76 if err != nil { 77 log.Printf("reading lf level: %s", err) 78 } 79 80 level++ 81 82 os.Setenv("LF_LEVEL", strconv.Itoa(level)) 83} 84 85// used by exportOpts below 86func fieldToString(field reflect.Value) string { 87 kind := field.Kind() 88 var value string 89 90 switch kind { 91 case reflect.Int: 92 value = strconv.Itoa(int(field.Int())) 93 case reflect.Bool: 94 value = strconv.FormatBool(field.Bool()) 95 case reflect.Slice: 96 for i := 0; i < field.Len(); i++ { 97 element := field.Index(i) 98 99 if i == 0 { 100 value = fieldToString(element) 101 } else { 102 value += ":" + fieldToString(element) 103 } 104 } 105 default: 106 value = field.String() 107 } 108 109 return value 110} 111 112func exportOpts() { 113 e := reflect.ValueOf(&gOpts).Elem() 114 115 for i := 0; i < e.NumField(); i++ { 116 // Get name and prefix it with lf_ 117 name := e.Type().Field(i).Name 118 name = fmt.Sprintf("lf_%s", name) 119 120 // Skip maps 121 if name == "lf_keys" || name == "lf_cmdkeys" || name == "lf_cmds" { 122 continue 123 } 124 125 // Get string representation of the value 126 if name == "lf_sortType" { 127 var sortby string 128 129 switch gOpts.sortType.method { 130 case naturalSort: 131 sortby = "natural" 132 case nameSort: 133 sortby = "name" 134 case sizeSort: 135 sortby = "size" 136 case timeSort: 137 sortby = "time" 138 case ctimeSort: 139 sortby = "ctime" 140 case atimeSort: 141 sortby = "atime" 142 case extSort: 143 sortby = "ext" 144 } 145 146 os.Setenv("lf_sortby", sortby) 147 148 reverse := strconv.FormatBool(gOpts.sortType.option&reverseSort != 0) 149 os.Setenv("lf_reverse", reverse) 150 151 hidden := strconv.FormatBool(gOpts.sortType.option&hiddenSort != 0) 152 os.Setenv("lf_hidden", hidden) 153 154 dirfirst := strconv.FormatBool(gOpts.sortType.option&dirfirstSort != 0) 155 os.Setenv("lf_dirfirst", dirfirst) 156 } else { 157 field := e.Field(i) 158 value := fieldToString(field) 159 160 os.Setenv(name, value) 161 } 162 } 163} 164 165func startServer() { 166 cmd := detachedCommand(os.Args[0], "-server") 167 if err := cmd.Start(); err != nil { 168 log.Printf("starting server: %s", err) 169 } 170} 171 172func checkServer() { 173 if gSocketProt == "unix" { 174 if _, err := os.Stat(gSocketPath); os.IsNotExist(err) { 175 startServer() 176 } else if _, err := net.Dial(gSocketProt, gSocketPath); err != nil { 177 os.Remove(gSocketPath) 178 startServer() 179 } 180 } else { 181 if _, err := net.Dial(gSocketProt, gSocketPath); err != nil { 182 startServer() 183 } 184 } 185} 186 187func main() { 188 showDoc := flag.Bool( 189 "doc", 190 false, 191 "show documentation") 192 193 showVersion := flag.Bool( 194 "version", 195 false, 196 "show version") 197 198 serverMode := flag.Bool( 199 "server", 200 false, 201 "start server (automatic)") 202 203 singleMode := flag.Bool( 204 "single", 205 false, 206 "start a client without server") 207 208 remoteCmd := flag.String( 209 "remote", 210 "", 211 "send remote command to server") 212 213 cpuprofile := flag.String( 214 "cpuprofile", 215 "", 216 "path to the file to write the CPU profile") 217 218 memprofile := flag.String( 219 "memprofile", 220 "", 221 "path to the file to write the memory profile") 222 223 flag.StringVar(&gLastDirPath, 224 "last-dir-path", 225 "", 226 "path to the file to write the last dir on exit (to use for cd)") 227 228 flag.StringVar(&gSelectionPath, 229 "selection-path", 230 "", 231 "path to the file to write selected files on open (to use as open file dialog)") 232 233 flag.StringVar(&gConfigPath, 234 "config", 235 "", 236 "path to the config file (instead of the usual paths)") 237 238 flag.Var(&gCommands, 239 "command", 240 "command to execute on client initialization") 241 242 flag.Parse() 243 244 gSocketProt = gDefaultSocketProt 245 gSocketPath = gDefaultSocketPath 246 247 if *cpuprofile != "" { 248 f, err := os.Create(*cpuprofile) 249 if err != nil { 250 log.Fatalf("could not create CPU profile: %s", err) 251 } 252 if err := pprof.StartCPUProfile(f); err != nil { 253 log.Fatalf("could not start CPU profile: %s", err) 254 } 255 defer pprof.StopCPUProfile() 256 } 257 258 switch { 259 case *showDoc: 260 fmt.Print(genDocString) 261 case *showVersion: 262 fmt.Println(gVersion) 263 case *remoteCmd != "": 264 if err := remote(*remoteCmd); err != nil { 265 log.Fatalf("remote command: %s", err) 266 } 267 case *serverMode: 268 os.Chdir(gUser.HomeDir) 269 gServerLogPath = filepath.Join(os.TempDir(), fmt.Sprintf("lf.%s.server.log", gUser.Username)) 270 serve() 271 default: 272 gSingleMode = *singleMode 273 274 if !gSingleMode { 275 checkServer() 276 } 277 278 gClientID = os.Getpid() 279 gLogPath = filepath.Join(os.TempDir(), fmt.Sprintf("lf.%s.%d.log", gUser.Username, gClientID)) 280 switch flag.NArg() { 281 case 0: 282 _, err := os.Getwd() 283 if err != nil { 284 fmt.Fprintf(os.Stderr, "%s\n", err) 285 os.Exit(2) 286 } 287 case 1: 288 gSelect = flag.Arg(0) 289 default: 290 fmt.Fprintf(os.Stderr, "only single file or directory is allowed\n") 291 os.Exit(2) 292 } 293 294 exportEnvVars() 295 296 run() 297 } 298 299 if *memprofile != "" { 300 f, err := os.Create(*memprofile) 301 if err != nil { 302 log.Fatal("could not create memory profile: ", err) 303 } 304 runtime.GC() 305 if err := pprof.WriteHeapProfile(f); err != nil { 306 log.Fatal("could not write memory profile: ", err) 307 } 308 f.Close() 309 } 310} 311