Przeglądaj źródła

lib/config, lib/ignore: Write Windows line endings (fixes #7115) (#8052)

Jakob Borg 3 lat temu
rodzic
commit
1754c93370

+ 1 - 1
build.go

@@ -1293,7 +1293,7 @@ func zipFile(out string, files []archiveFile) {
 			if err != nil {
 				log.Fatal(err)
 			}
-			bs = bytes.Replace(bs, []byte{'\n'}, []byte{'\n', '\r'}, -1)
+			bs = bytes.Replace(bs, []byte{'\n'}, []byte{'\r', '\n'}, -1)
 			fh.UncompressedSize = uint32(len(bs))
 			fh.UncompressedSize64 = uint64(len(bs))
 

+ 35 - 0
lib/config/config_test.go

@@ -594,6 +594,41 @@ func TestNewSaveLoad(t *testing.T) {
 	}
 }
 
+func TestWindowsLineEndings(t *testing.T) {
+	if runtime.GOOS != "windows" {
+		t.Skip("Windows specific")
+	}
+
+	dir, err := os.MkdirTemp("", "syncthing-test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+
+	path := filepath.Join(dir, "config.xml")
+	os.Remove(path)
+	defer os.Remove(path)
+
+	intCfg := New(device1)
+	cfg := wrap(path, intCfg, device1)
+	defer cfg.stop()
+
+	if err := cfg.Save(); err != nil {
+		t.Error(err)
+	}
+
+	bs, err := os.ReadFile(path)
+	if err != nil {
+		t.Error(err)
+	}
+
+	unixLineEndings := bytes.Count(bs, []byte("\n"))
+	windowsLineEndings := bytes.Count(bs, []byte("\r\n"))
+	if unixLineEndings == 0 || windowsLineEndings != unixLineEndings {
+		t.Error("expected there to be a non-zero number of Windows line endings")
+	}
+}
+
 func TestPrepare(t *testing.T) {
 	var cfg Configuration
 

+ 1 - 1
lib/config/wrapper.go

@@ -502,7 +502,7 @@ func (w *wrapper) Save() error {
 		return err
 	}
 
-	if err := w.cfg.WriteXML(fd); err != nil {
+	if err := w.cfg.WriteXML(osutil.LineEndingsWriter(fd)); err != nil {
 		l.Debugln("WriteXML:", err)
 		fd.Close()
 		return err

+ 2 - 1
lib/ignore/ignore.go

@@ -595,8 +595,9 @@ func WriteIgnores(filesystem fs.Filesystem, path string, content []string) error
 		return err
 	}
 
+	wr := osutil.LineEndingsWriter(fd)
 	for _, line := range content {
-		fmt.Fprintln(fd, line)
+		fmt.Fprintln(wr, line)
 	}
 
 	if err := fd.Close(); err != nil {

+ 39 - 0
lib/ignore/ignore_test.go

@@ -1192,3 +1192,42 @@ func TestEmptyPatterns(t *testing.T) {
 		}
 	}
 }
+
+func TestWindowsLineEndings(t *testing.T) {
+	if runtime.GOOS != "windows" {
+		t.Skip("Windows specific")
+	}
+
+	lines := "foo\nbar\nbaz\n"
+
+	dir, err := os.MkdirTemp("", "syncthing-test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+
+	ffs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
+	m := New(ffs)
+	if err := m.Parse(strings.NewReader(lines), ".stignore"); err != nil {
+		t.Fatal(err)
+	}
+	if err := WriteIgnores(ffs, ".stignore", m.Lines()); err != nil {
+		t.Fatal(err)
+	}
+
+	fd, err := ffs.Open(".stignore")
+	if err != nil {
+		t.Fatal(err)
+	}
+	bs, err := io.ReadAll(fd)
+	fd.Close()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	unixLineEndings := bytes.Count(bs, []byte("\n"))
+	windowsLineEndings := bytes.Count(bs, []byte("\r\n"))
+	if unixLineEndings == 0 || windowsLineEndings != unixLineEndings {
+		t.Error("expected there to be a non-zero number of Windows line endings")
+	}
+}

+ 14 - 0
lib/osutil/replacingwriter.go

@@ -9,6 +9,7 @@ package osutil
 import (
 	"bytes"
 	"io"
+	"runtime"
 )
 
 type ReplacingWriter struct {
@@ -46,3 +47,16 @@ func (w ReplacingWriter) Write(bs []byte) (int, error) {
 
 	return written, err
 }
+
+// LineEndingsWriter returns a writer that writes platform-appropriate line
+// endings. (This is a no-op on non-Windows platforms.)
+func LineEndingsWriter(w io.Writer) io.Writer {
+	if runtime.GOOS != "windows" {
+		return w
+	}
+	return &ReplacingWriter{
+		Writer: w,
+		From:   '\n',
+		To:     []byte{'\r', '\n'},
+	}
+}