diff --git a/cmd/syncthing/monitor.go b/cmd/syncthing/monitor.go index af64be32..d48b4506 100644 --- a/cmd/syncthing/monitor.go +++ b/cmd/syncthing/monitor.go @@ -22,10 +22,13 @@ import ( "os/exec" "os/signal" "path/filepath" + "runtime" "strings" "sync" "syscall" "time" + + "github.com/syncthing/syncthing/internal/osutil" ) var ( @@ -45,16 +48,29 @@ func monitorMain() { l.SetPrefix("[monitor] ") var err error - var dst io.Writer + var dst io.Writer = os.Stdout - if logFile == "" { - dst = os.Stdout - } else { - dst, err = os.Create(logFile) + if logFile != "" { + var fileDst io.Writer + + fileDst, err = os.Create(logFile) if err != nil { l.Fatalln("log file:", err) } - l.Infof(`Log output directed to file "%s"`, logFile) + + if runtime.GOOS == "windows" { + // Translate line breaks to Windows standard + fileDst = osutil.ReplacingWriter{ + Writer: fileDst, + From: '\n', + To: []byte{'\r', '\n'}, + } + } + + // Log to both stdout and file. + dst = io.MultiWriter(dst, fileDst) + + l.Infof(`Log output saved to file "%s"`, logFile) } args := os.Args diff --git a/internal/osutil/replacingwriter.go b/internal/osutil/replacingwriter.go new file mode 100644 index 00000000..4eac5908 --- /dev/null +++ b/internal/osutil/replacingwriter.go @@ -0,0 +1,57 @@ +// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file). +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +package osutil + +import ( + "bytes" + "io" +) + +type ReplacingWriter struct { + Writer io.Writer + From byte + To []byte +} + +func (w ReplacingWriter) Write(bs []byte) (int, error) { + var n, written int + var err error + + newlineIdx := bytes.IndexByte(bs, w.From) + for newlineIdx >= 0 { + n, err = w.Writer.Write(bs[:newlineIdx]) + written += n + if err != nil { + break + } + if len(w.To) > 0 { + n, err := w.Writer.Write(w.To) + if n == len(w.To) { + written++ + } + if err != nil { + break + } + } + bs = bs[newlineIdx+1:] + newlineIdx = bytes.IndexByte(bs, w.From) + } + + n, err = w.Writer.Write(bs) + written += n + + return written, err +} diff --git a/internal/osutil/replacingwriter_test.go b/internal/osutil/replacingwriter_test.go new file mode 100644 index 00000000..c2332063 --- /dev/null +++ b/internal/osutil/replacingwriter_test.go @@ -0,0 +1,53 @@ +// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file). +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +package osutil + +import ( + "bytes" + "fmt" + "testing" +) + +var testcases = []struct { + from byte + to []byte + a, b string +}{ + {'\n', []byte{'\r', '\n'}, "", ""}, + {'\n', []byte{'\r', '\n'}, "foo", "foo"}, + {'\n', []byte{'\r', '\n'}, "foo\n", "foo\r\n"}, + {'\n', []byte{'\r', '\n'}, "foo\nbar", "foo\r\nbar"}, + {'\n', []byte{'\r', '\n'}, "foo\nbar\nbaz", "foo\r\nbar\r\nbaz"}, + {'\n', []byte{'\r', '\n'}, "\nbar", "\r\nbar"}, + {'o', []byte{'x', 'l', 'r'}, "\nfoo", "\nfxlrxlr"}, + {'o', nil, "\nfoo", "\nf"}, + {'f', []byte{}, "\nfoo", "\noo"}, +} + +func TestReplacingWriter(t *testing.T) { + for _, tc := range testcases { + var buf bytes.Buffer + w := ReplacingWriter{ + Writer: &buf, + From: tc.from, + To: tc.to, + } + fmt.Fprint(w, tc.a) + if buf.String() != tc.b { + t.Errorf("%q != %q", buf.String(), tc.b) + } + } +}