+
+ var fileSize int
+
+ err := overwriteFile(absFilename, func(file io.Writer) (e error) {
+ if len(b.lines) == 0 {
+ return
+ }
+
+ // end of line
+ var eol []byte
+
+ if b.Settings["fileformat"] == "dos" {
+ eol = []byte{'\r', '\n'}
+ } else {
+ eol = []byte{'\n'}
+ }
+
+ // write lines
+ if fileSize, e = file.Write(b.lines[0].data); e != nil {
+ return
+ }
+
+ for _, l := range b.lines[1:] {
+ if _, e = file.Write(eol); e != nil {
+ return
+ }
+
+ if _, e = file.Write(l.data); e != nil {
+ return
+ }
+
+ fileSize += len(eol) + len(l.data)
+ }
+
+ return
+ })
+
+ if err != nil {
+ return err
+ }
+
+ if !b.Settings["fastdirty"].(bool) {
+ if fileSize > LargeFileThreshold {
+ // For large files 'fastdirty' needs to be on
+ b.Settings["fastdirty"] = true
+ } else {
+ calcHash(b, &b.origHash)
+ }
+ }
+
+ b.Path = filename
+ b.IsModified = false
+ return b.Serialize()
+}
+
+// overwriteFile opens the given file for writing, truncating if one exists, and then calls
+// the supplied function with the file as io.Writer object, also making sure the file is
+// closed afterwards.
+func overwriteFile(name string, fn func(io.Writer) error) (err error) {
+ var file *os.File
+
+ if file, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
+ return
+ }
+
+ defer func() {
+ if e := file.Close(); e != nil && err == nil {
+ err = e
+ }
+ }()
+
+ w := bufio.NewWriter(file)
+
+ if err = fn(w); err != nil {
+ return
+ }
+
+ err = w.Flush()
+ return
+}
+
+// calcHash calculates md5 hash of all lines in the buffer
+func calcHash(b *Buffer, out *[md5.Size]byte) {
+ h := md5.New()
+
+ if len(b.lines) > 0 {
+ h.Write(b.lines[0].data)
+
+ for _, l := range b.lines[1:] {
+ h.Write([]byte{'\n'})
+ h.Write(l.data)
+ }
+ }
+
+ h.Sum((*out)[:0])