Skip to content

Commit 8a22546

Browse files
author
jakedt
committed
Add a CpWithArgs method which allows for running cp with a variety of switches. Add a switch to preserve the timestamps of the file being copied.
1 parent df2954c commit 8a22546

File tree

1 file changed

+81
-103
lines changed

1 file changed

+81
-103
lines changed

fileutils.go

Lines changed: 81 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ import (
1111
"path/filepath"
1212
)
1313

14+
// List of arguments which can be passed to the CpWithArgs method
15+
type CpArgs struct {
16+
Recursive bool
17+
PreserveLinks bool
18+
PreserveTimestamps bool
19+
}
20+
1421
// ChmodR is like `chmod -R`
1522
func ChmodR(name string, mode os.FileMode) error {
1623
return filepath.Walk(name, func(path string, info os.FileInfo, err error) error {
@@ -33,7 +40,7 @@ func ChownR(path string, uid, gid int) error {
3340

3441
// Cp is like `cp`
3542
func Cp(src, dest string) (err error) {
36-
return cpFollowLinks(src, dest)
43+
return CpWithArgs(src, dest, CpArgs{})
3744
}
3845

3946
func cpSymlink(src, dest string) (err error) {
@@ -47,133 +54,104 @@ func cpSymlink(src, dest string) (err error) {
4754
}
4855

4956
func cpFollowLinks(src, dest string) (err error) {
50-
// get info on src
51-
si, err := os.Lstat(src)
52-
if err != nil {
53-
return
54-
}
55-
56-
//open src
57-
in, err := os.Open(src)
58-
if err != nil {
59-
return
60-
}
61-
defer in.Close()
62-
63-
//create dest
64-
out, err := os.Create(dest)
65-
if err != nil {
66-
return
67-
}
68-
defer func() {
69-
cerr := out.Close()
70-
if err == nil {
71-
err = cerr
72-
}
73-
}()
74-
75-
//copy to dest from source
76-
if _, err = io.Copy(out, in); err != nil {
77-
return
78-
}
79-
80-
if err = out.Chmod(si.Mode()); err != nil {
81-
return
82-
}
83-
84-
//sync dest to disk
85-
err = out.Sync()
86-
87-
return
57+
return CpWithArgs(src, dest, CpArgs{})
8858
}
8959

9060
func cpPreserveLinks(src, dest string) (err error) {
91-
// get info on src
92-
si, err := os.Lstat(src)
93-
if err != nil {
94-
return
95-
}
61+
return CpWithArgs(src, dest, CpArgs{PreserveLinks: true})
62+
}
9663

97-
// handle symlinks
98-
if !si.Mode().IsRegular() {
99-
return cpSymlink(src, dest)
100-
}
64+
/*
65+
CpR is like `cp -R`
66+
*/
67+
func CpR(source, dest string) (err error) {
68+
return CpWithArgs(source, dest, CpArgs{Recursive: true})
69+
}
10170

102-
//open source
103-
in, err := os.Open(src)
71+
func CpWithArgs(source, dest string, args CpArgs) (err error) {
72+
sourceInfo, err := os.Stat(source)
10473
if err != nil {
10574
return
10675
}
107-
defer in.Close()
10876

109-
//create dest
110-
out, err := os.Create(dest)
111-
if err != nil {
112-
return
113-
}
114-
defer func() {
115-
cerr := out.Close()
116-
if err == nil {
117-
err = cerr
77+
if sourceInfo.IsDir() {
78+
// Handle the dir case
79+
if !args.Recursive {
80+
return errors.New("source is a directory")
11881
}
119-
}()
12082

121-
//copy to dest from source
122-
if _, err = io.Copy(out, in); err != nil {
123-
return
124-
}
83+
// ensure dest dir does not already exist
84+
if _, err = os.Open(dest); !os.IsNotExist(err) {
85+
return errors.New("destination already exists")
86+
}
12587

126-
if err = out.Chmod(si.Mode()); err != nil {
127-
return
128-
}
88+
// create dest dir
89+
if err = os.MkdirAll(dest, sourceInfo.Mode()); err != nil {
90+
return
91+
}
12992

130-
//sync dest to disk
131-
err = out.Sync()
93+
files, err := ioutil.ReadDir(source)
94+
if err != nil {
95+
return err
96+
}
13297

133-
return
134-
}
98+
for _, file := range files {
99+
sourceFilePath := fmt.Sprintf("%s/%s", source, file.Name())
100+
destFilePath := fmt.Sprintf("%s/%s", dest, file.Name())
135101

136-
/*
137-
CpR is like `cp -R`
138-
*/
139-
func CpR(source, dest string) (err error) {
140-
// get properties of source dir
141-
sourceInfo, err := os.Stat(source)
142-
if err != nil {
143-
return
144-
}
102+
if err = CpWithArgs(sourceFilePath, destFilePath, args); err != nil {
103+
return err
104+
}
105+
}
106+
} else {
107+
// Handle the file case
108+
si, err := os.Lstat(source)
109+
if err != nil {
110+
return err
111+
}
145112

146-
if !sourceInfo.IsDir() {
147-
return errors.New("source is not a directory")
148-
}
113+
if args.PreserveLinks && !si.Mode().IsRegular() {
114+
return cpSymlink(source, dest)
115+
}
149116

150-
// ensure dest dir does not already exist
151-
if _, err = os.Open(dest); !os.IsNotExist(err) {
152-
return errors.New("destination already exists")
153-
}
117+
//open source
118+
in, err := os.Open(source)
119+
if err != nil {
120+
return err
121+
}
122+
defer in.Close()
154123

155-
// create dest dir
156-
if err = os.MkdirAll(dest, sourceInfo.Mode()); err != nil {
157-
return
158-
}
124+
//create dest
125+
out, err := os.Create(dest)
126+
if err != nil {
127+
return err
128+
}
129+
defer func() {
130+
cerr := out.Close()
131+
if err == nil {
132+
err = cerr
133+
}
134+
}()
159135

160-
files, err := ioutil.ReadDir(source)
136+
//copy to dest from source
137+
if _, err = io.Copy(out, in); err != nil {
138+
return err
139+
}
161140

162-
for _, file := range files {
163-
sourceFilePath := fmt.Sprintf("%s/%s", source, file.Name())
164-
destFilePath := fmt.Sprintf("%s/%s", dest, file.Name())
141+
if err = out.Chmod(si.Mode()); err != nil {
142+
return err
143+
}
165144

166-
if file.IsDir() {
167-
if err = CpR(sourceFilePath, destFilePath); err != nil {
168-
return
169-
}
170-
} else {
171-
if err = cpPreserveLinks(sourceFilePath, destFilePath); err != nil {
172-
return
145+
if args.PreserveTimestamps {
146+
if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
147+
return err
173148
}
174149
}
175150

151+
//sync dest to disk
152+
err = out.Sync()
176153
}
154+
177155
return
178156
}
179157

0 commit comments

Comments
 (0)