@@ -24,7 +24,63 @@ import (
2424// because hashing is too slow
2525const LargeFileThreshold = 50000
2626
27- func (b * Buffer ) writeFile (file io.Writer ) (int , error ) {
27+ type wrappedFile struct {
28+ writeCloser io.WriteCloser
29+ withSudo bool
30+ screenb bool
31+ cmd * exec.Cmd
32+ sigChan chan os.Signal
33+ }
34+
35+ func openFile (name string , withSudo bool ) (wrappedFile , error ) {
36+ var err error
37+ var writeCloser io.WriteCloser
38+ var screenb bool
39+ var cmd * exec.Cmd
40+ var sigChan chan os.Signal
41+
42+ if withSudo {
43+ cmd = exec .Command (config .GlobalSettings ["sucmd" ].(string ), "dd" , "bs=4k" , "of=" + name )
44+ writeCloser , err = cmd .StdinPipe ()
45+ if err != nil {
46+ return wrappedFile {}, err
47+ }
48+
49+ sigChan = make (chan os.Signal , 1 )
50+ signal .Reset (os .Interrupt )
51+ signal .Notify (sigChan , os .Interrupt )
52+
53+ screenb = screen .TempFini ()
54+ // need to start the process now, otherwise when we flush the file
55+ // contents to its stdin it might hang because the kernel's pipe size
56+ // is too small to handle the full file contents all at once
57+ err = cmd .Start ()
58+ if err != nil {
59+ screen .TempStart (screenb )
60+
61+ signal .Notify (util .Sigterm , os .Interrupt )
62+ signal .Stop (sigChan )
63+
64+ return wrappedFile {}, err
65+ }
66+ } else {
67+ writeCloser , err = os .OpenFile (name , os .O_WRONLY | os .O_CREATE , util .FileMode )
68+ if err != nil {
69+ return wrappedFile {}, err
70+ }
71+ }
72+
73+ return wrappedFile {writeCloser , withSudo , screenb , cmd , sigChan }, nil
74+ }
75+
76+ func (wf wrappedFile ) Write (b * Buffer ) (int , error ) {
77+ enc , err := htmlindex .Get (b .Settings ["encoding" ].(string ))
78+ if err != nil {
79+ return 0 , err
80+ }
81+
82+ file := bufio .NewWriter (transform .NewWriter (wf .writeCloser , enc .NewEncoder ()))
83+
2884 b .Lock ()
2985 defer b .Unlock ()
3086
@@ -40,6 +96,14 @@ func (b *Buffer) writeFile(file io.Writer) (int, error) {
4096 eol = []byte {'\n' }
4197 }
4298
99+ if ! wf .withSudo {
100+ f := wf .writeCloser .(* os.File )
101+ err = f .Truncate (0 )
102+ if err != nil {
103+ return 0 , err
104+ }
105+ }
106+
43107 // write lines
44108 size , err := file .Write (b .lines [0 ].data )
45109 if err != nil {
@@ -55,78 +119,45 @@ func (b *Buffer) writeFile(file io.Writer) (int, error) {
55119 }
56120 size += len (eol ) + len (l .data )
57121 }
58- return size , nil
59- }
60122
61- func (b * Buffer ) overwriteFile (name string , withSudo bool ) (int , error ) {
62- enc , err := htmlindex .Get (b .Settings ["encoding" ].(string ))
63- if err != nil {
64- return 0 , err
123+ err = file .Flush ()
124+ if err == nil && ! wf .withSudo {
125+ // Call Sync() on the file to make sure the content is safely on disk.
126+ f := wf .writeCloser .(* os.File )
127+ err = f .Sync ()
65128 }
129+ return size , err
130+ }
66131
67- var writeCloser io.WriteCloser
68- var screenb bool
69- var cmd * exec.Cmd
70- var c chan os.Signal
132+ func (wf wrappedFile ) Close () error {
133+ err := wf .writeCloser .Close ()
134+ if wf .withSudo {
135+ // wait for dd to finish and restart the screen if we used sudo
136+ err := wf .cmd .Wait ()
137+ screen .TempStart (wf .screenb )
71138
72- if withSudo {
73- cmd = exec . Command ( config . GlobalSettings [ "sucmd" ].( string ), "dd" , "bs=4k" , "of=" + name )
139+ signal . Notify ( util . Sigterm , os . Interrupt )
140+ signal . Stop ( wf . sigChan )
74141
75- if writeCloser , err = cmd . StdinPipe (); err != nil {
76- return 0 , err
142+ if err != nil {
143+ return err
77144 }
145+ }
146+ return err
147+ }
78148
79- c = make (chan os.Signal , 1 )
80- signal .Reset (os .Interrupt )
81- signal .Notify (c , os .Interrupt )
82-
83- screenb = screen .TempFini ()
84- // need to start the process now, otherwise when we flush the file
85- // contents to its stdin it might hang because the kernel's pipe size
86- // is too small to handle the full file contents all at once
87- if err = cmd .Start (); err != nil {
88- screen .TempStart (screenb )
89-
90- signal .Notify (util .Sigterm , os .Interrupt )
91- signal .Stop (c )
92-
93- return 0 , err
94- }
95- } else if writeCloser , err = os .OpenFile (name , os .O_WRONLY | os .O_CREATE | os .O_TRUNC , util .FileMode ); err != nil {
149+ func (b * Buffer ) overwriteFile (name string ) (int , error ) {
150+ file , err := openFile (name , false )
151+ if err != nil {
96152 return 0 , err
97153 }
98154
99- w := bufio .NewWriter (transform .NewWriter (writeCloser , enc .NewEncoder ()))
100- size , err := b .writeFile (w )
155+ size , err := file .Write (b )
101156
102- if err2 := w .Flush (); err2 != nil && err == nil {
157+ err2 := file .Close ()
158+ if err2 != nil && err == nil {
103159 err = err2
104160 }
105- // Call Sync() on the file to make sure the content is safely on disk.
106- // Does not work with sudo as we don't have direct access to the file.
107- if ! withSudo {
108- f := writeCloser .(* os.File )
109- if err2 := f .Sync (); err2 != nil && err == nil {
110- err = err2
111- }
112- }
113- if err2 := writeCloser .Close (); err2 != nil && err == nil {
114- err = err2
115- }
116-
117- if withSudo {
118- // wait for dd to finish and restart the screen if we used sudo
119- err := cmd .Wait ()
120- screen .TempStart (screenb )
121-
122- signal .Notify (util .Sigterm , os .Interrupt )
123- signal .Stop (c )
124-
125- if err != nil {
126- return size , err
127- }
128- }
129-
130161 return size , err
131162}
132163
@@ -262,6 +293,17 @@ func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error
262293// This means that the file is not overwritten directly but by writing to the
263294// backup file first.
264295func (b * Buffer ) safeWrite (path string , withSudo bool , newFile bool ) (int , error ) {
296+ file , err := openFile (path , withSudo )
297+ if err != nil {
298+ return 0 , err
299+ }
300+
301+ defer func () {
302+ if newFile && err != nil {
303+ os .Remove (path )
304+ }
305+ }()
306+
265307 backupDir := b .backupDir ()
266308 if _ , err := os .Stat (backupDir ); err != nil {
267309 if ! errors .Is (err , fs .ErrNotExist ) {
@@ -273,18 +315,15 @@ func (b *Buffer) safeWrite(path string, withSudo bool, newFile bool) (int, error
273315 }
274316
275317 backupName := util .DetermineEscapePath (backupDir , path )
276- _ , err : = b .overwriteFile (backupName , false )
318+ _ , err = b .overwriteFile (backupName )
277319 if err != nil {
278320 os .Remove (backupName )
279321 return 0 , err
280322 }
281323
282324 b .forceKeepBackup = true
283- size , err := b . overwriteFile ( path , withSudo )
325+ size , err := file . Write ( b )
284326 if err != nil {
285- if newFile {
286- os .Remove (path )
287- }
288327 return size , err
289328 }
290329 b .forceKeepBackup = false
@@ -293,5 +332,10 @@ func (b *Buffer) safeWrite(path string, withSudo bool, newFile bool) (int, error
293332 os .Remove (backupName )
294333 }
295334
335+ err2 := file .Close ()
336+ if err2 != nil && err == nil {
337+ err = err2
338+ }
339+
296340 return size , err
297341}
0 commit comments