diff --git a/.golangci.yaml b/.golangci.yaml index e8ecf0ee..fcf9fd96 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,7 +1,23 @@ version: "2" linters: - disable: - - errcheck + settings: + errcheck: + exclude-functions: + - (*github.com/fsnotify/fsnotify.Watcher).Close + - (*net.TCPConn).CloseWrite + - (*net.UnixConn).CloseWrite + - (*os.File).Close + - (*os.File).Write + - (*text/tabwriter.Writer).Flush + - (io.Closer).Close + - (io.Writer).Write + - (net.Conn).Close + - (net.Listener).Close + - fmt.Fprintf + - fmt.Fprintln + - io.WriteString + - os.Setenv + - syscall.Close issues: max-issues-per-linter: 0 # no limit max-same-issues: 0 # no limit diff --git a/app.go b/app.go index 8471aebd..87b85dc5 100644 --- a/app.go +++ b/app.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "cmp" "errors" "fmt" "io" @@ -129,7 +130,10 @@ func loadFiles() (clipboard clipboard, err error) { s := bufio.NewScanner(files) - s.Scan() + if !s.Scan() { + err = fmt.Errorf("scanning file list: %w", cmp.Or(s.Err(), io.EOF)) + return + } switch s.Text() { case "copy": @@ -168,18 +172,23 @@ func saveFiles(clipboard clipboard) error { log.Printf("saving files: %v", clipboard.paths) + var clipboardModeStr string if clipboard.mode == clipboardCopy { - fmt.Fprintln(files, "copy") + clipboardModeStr = "copy" } else { - fmt.Fprintln(files, "move") + clipboardModeStr = "move" + } + if _, err := fmt.Fprintln(files, clipboardModeStr); err != nil { + return fmt.Errorf("write clipboard mode to file: %w", err) } for _, path := range clipboard.paths { - fmt.Fprintln(files, path) + if _, err := fmt.Fprintln(files, path); err != nil { + return fmt.Errorf("write path to file: %w", err) + } } - files.Sync() - return nil + return files.Sync() } func (app *app) readHistory() error { diff --git a/client.go b/client.go index 8e725dc4..08ba8c87 100644 --- a/client.go +++ b/client.go @@ -128,7 +128,9 @@ func readExpr() <-chan expr { c, err = net.Dial(gSocketProt, gSocketPath) } - fmt.Fprintf(c, "conn %d\n", gClientID) + if _, err := fmt.Fprintf(c, "conn %d\n", gClientID); err != nil { + log.Fatalf("registering with server: %s", err) + } ch <- &callExpr{"sync", nil, 1} ch <- &callExpr{"on-init", nil, 1} @@ -143,12 +145,11 @@ func readExpr() <-chan expr { // running `$lf -remote "query $id "`. if word, rest := splitWord(s.Text()); word == "query" { gState.mutex.Lock() - state, ok := gState.data[rest] + state := gState.data[rest] gState.mutex.Unlock() - if ok { - fmt.Fprint(c, state) + if _, err := fmt.Fprintln(c, state); err != nil { + log.Fatalf("sending response to server: %s", err) } - fmt.Fprintln(c, "") } else { p := newParser(strings.NewReader(s.Text())) if p.parse() { @@ -157,6 +158,10 @@ func readExpr() <-chan expr { } } + if err := s.Err(); err != nil { + log.Printf("reading from server: %s", err) + } + c.Close() }() @@ -169,17 +174,20 @@ func remote(cmd string) error { return fmt.Errorf("dialing to send server: %w", err) } - fmt.Fprintln(c, cmd) + if _, err := fmt.Fprintln(c, cmd); err != nil { + return fmt.Errorf("sending command to server: %w", err) + } // XXX: Standard net.Conn interface does not include a CloseWrite method // but net.UnixConn and net.TCPConn implement it so the following should be // safe as long as we do not use other types of connections. We need // CloseWrite to notify the server that this is not a persistent connection // and it should be closed after the response. - if v, ok := c.(interface { - CloseWrite() error - }); ok { - v.CloseWrite() + switch c := c.(type) { + case *net.TCPConn: + c.CloseWrite() + case *net.UnixConn: + c.CloseWrite() } // The most straightforward way to write the response to stdout would be diff --git a/copy.go b/copy.go index d9ad945f..835fa938 100644 --- a/copy.go +++ b/copy.go @@ -54,10 +54,11 @@ func copySize(srcs []string) (int64, error) { return total, nil } -func copyFile(src, dst string, preserve []string, info os.FileInfo, nums chan int64) error { +func copyFile(src, dst string, preserve []string, info os.FileInfo, nums chan<- int64, errs chan<- error) { r, err := os.Open(src) if err != nil { - return err + errs <- err + return } defer r.Close() @@ -67,30 +68,38 @@ func copyFile(src, dst string, preserve []string, info os.FileInfo, nums chan in } w, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, dstMode) if err != nil { - return err + errs <- err + return } if _, err := io.Copy(NewProgressWriter(w, nums), r); err != nil { + errs <- err w.Close() - os.Remove(dst) - return err + if err = os.Remove(dst); err != nil { + errs <- err + } + return } if err := w.Close(); err != nil { - os.Remove(dst) - return err + errs <- err + if err = os.Remove(dst); err != nil { + errs <- err + } + return } if slices.Contains(preserve, "timestamps") { atime := times.Get(info).AccessTime() mtime := info.ModTime() if err := os.Chtimes(dst, atime, mtime); err != nil { - os.Remove(dst) - return err + errs <- err + if err = os.Remove(dst); err != nil { + errs <- err + } + return } } - - return nil } func copyAll(srcs []string, dstDir string, preserve []string) (nums chan int64, errs chan error) { @@ -119,7 +128,7 @@ func copyAll(srcs []string, dstDir string, preserve []string) (nums chan int64, dst = newPath } - filepath.Walk(src, func(path string, info os.FileInfo, err error) error { + err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error { if err != nil { errs <- fmt.Errorf("walk: %w", err) return nil @@ -153,12 +162,13 @@ func copyAll(srcs []string, dstDir string, preserve []string) (nums chan int64, } nums <- info.Size() default: - if err := copyFile(path, newPath, preserve, info, nums); err != nil { - errs <- fmt.Errorf("copy: %w", err) - } + copyFile(path, newPath, preserve, info, nums, errs) } return nil }) + if err != nil { + errs <- fmt.Errorf("walk: %w", err) + } } for path, info := range dirInfos { diff --git a/eval.go b/eval.go index 1bc605c1..43d49325 100644 --- a/eval.go +++ b/eval.go @@ -747,8 +747,9 @@ func resetIncCmd(app *app) { } else if gOpts.incfilter && app.ui.cmdPrefix == "filter: " { dir := app.nav.currDir() old := dir.ind - app.nav.setFilter(app.nav.prevFilter) - if old != dir.ind { + if err := app.nav.setFilter(app.nav.prevFilter); err != nil { + log.Printf("reset filter: %s", err) + } else if old != dir.ind { app.ui.loadFile(app, true) } } diff --git a/main.go b/main.go index e47f033d..d50015f6 100644 --- a/main.go +++ b/main.go @@ -180,7 +180,9 @@ func checkServer() { if _, err := os.Stat(gSocketPath); os.IsNotExist(err) { startServer() } else if _, err := net.Dial(gSocketProt, gSocketPath); err != nil { - os.Remove(gSocketPath) + if err := os.Remove(gSocketPath); err != nil { + log.Print(err) + } startServer() } } else { @@ -344,7 +346,9 @@ func main() { log.Fatalf("remote command: %s", err) } case *serverMode: - os.Chdir(gUser.HomeDir) + if err := os.Chdir(gUser.HomeDir); err != nil { + log.Print(err) + } serve() default: gSingleMode = *singleMode diff --git a/misc.go b/misc.go index b95b1895..e782fc4b 100644 --- a/misc.go +++ b/misc.go @@ -234,7 +234,7 @@ func readArrays(r io.Reader, minCols, maxCols int) ([][]string, error) { arrays = append(arrays, arr) } - return arrays, nil + return arrays, s.Err() } func readPairs(r io.Reader) ([][]string, error) { diff --git a/nav.go b/nav.go index 8a32e3a2..46aefd65 100644 --- a/nav.go +++ b/nav.go @@ -646,14 +646,18 @@ func (nav *nav) addJumpList() { func (nav *nav) cdJumpListPrev() { if nav.jumpListInd > 0 { nav.jumpListInd-- - nav.cd(nav.jumpList[nav.jumpListInd]) + if err := nav.cd(nav.jumpList[nav.jumpListInd]); err != nil { + log.Print(err) + } } } func (nav *nav) cdJumpListNext() { if nav.jumpListInd < len(nav.jumpList)-1 { nav.jumpListInd++ - nav.cd(nav.jumpList[nav.jumpListInd]) + if err := nav.cd(nav.jumpList[nav.jumpListInd]); err != nil { + log.Print(err) + } } } diff --git a/server.go b/server.go index 721cec01..0055656d 100644 --- a/server.go +++ b/server.go @@ -111,12 +111,16 @@ Loop: word2, rest2 := splitWord(rest) id, err := strconv.Atoi(word2) if err != nil { - for _, c := range gConnList { - fmt.Fprintln(c, rest) + for id, c2 := range gConnList { + if _, err := fmt.Fprintln(c2, rest); err != nil { + echoerrf(c, "failed to send command to client %v: %s", id, err) + } } } else { if c2, ok := gConnList[id]; ok { - fmt.Fprintln(c2, rest2) + if _, err := fmt.Fprintln(c2, rest2); err != nil { + echoerrf(c, "failed to send command to client %v: %s", id, err) + } } else { echoerr(c, "listen: send: no such client id is connected") } @@ -138,10 +142,18 @@ Loop: echoerr(c, "listen: query: no such client id is connected") break } - fmt.Fprintln(c2, "query "+rest2) + if _, err := fmt.Fprintln(c2, "query "+rest2); err != nil { + echoerrf(c, "failed to send query to client %v: %s", id, err) + break + } s2 := bufio.NewScanner(c2) for s2.Scan() && s2.Text() != "" { - fmt.Fprintln(c, s2.Text()) + if _, err := fmt.Fprintln(c, s2.Text()); err != nil { + log.Printf("failed to forward query response from client %v: %s", id, err) + } + } + if s2.Err() != nil { + echoerrf(c, "failed to read query response from client %v: %s", id, s2.Err()) } case "quit": if len(gConnList) == 0 { diff --git a/ui.go b/ui.go index 75fea9b3..15519138 100644 --- a/ui.go +++ b/ui.go @@ -1820,10 +1820,16 @@ func anyKey() { if err != nil { panic(err) } - defer term.Restore(int(os.Stdin.Fd()), oldState) + defer func() { + if err := term.Restore(int(os.Stdin.Fd()), oldState); err != nil { + panic(err) + } + }() b := make([]byte, 8) - os.Stdin.Read(b) + if _, err := os.Stdin.Read(b); err != nil { + log.Printf("Failed to read key press: %s", err) + } } func listMatches(screen tcell.Screen, matches []compMatch, selectedInd int) (string, *menuSelect) { diff --git a/watch.go b/watch.go index 9aa7768d..6c4c1ba3 100644 --- a/watch.go +++ b/watch.go @@ -71,7 +71,9 @@ func (watch *watch) add(path string) { // ignore /dev since write updates to /dev/tty causes high cpu usage if path != "/dev" { - watch.watcher.Add(path) + if err := watch.watcher.Add(path); err != nil { + log.Printf("watch path %s: %s", path, err) + } } }