@@ -231,29 +231,48 @@ pub fn rename(src string, dst string) ! {
231231 }
232232}
233233
234- // cp copies files or folders from `src` to `dst`.
235- pub fn cp (src string , dst string ) ! {
234+ @[params]
235+ pub struct CopyParams {
236+ pub :
237+ fail_if_exists bool
238+ }
239+
240+ // cp copies the file src to the file or directory dst. If dst specifies a directory, the file will be copied into dst
241+ // using the base filename from src. If dst specifies a file that already exists, it will be replaced by
242+ // default. Can be overridden to fail by setting fail_if_exists: true
243+ pub fn cp (src string , dst string , config CopyParams) ! {
236244 $if windows {
237245 w_src := src.replace ('/' , '\\ ' )
238- w_dst := dst.replace ('/' , '\\ ' )
239- if C.CopyFile (w_src.to_wide (), w_dst.to_wide (), false ) == 0 {
246+ mut w_dst := dst.replace ('/' , '\\ ' )
247+ if is_dir (w_dst) {
248+ w_dst = join_path_single (w_dst, file_name (w_src))
249+ }
250+ if C.CopyFile (w_src.to_wide (), w_dst.to_wide (), config.fail_if_exists) == 0 {
240251 // we must save error immediately, or it will be overwritten by other API function calls.
241252 code := int (C.GetLastError ())
242253 return error_win32 (
243- msg: 'failed to copy ${src} to ${dst} '
254+ msg: 'cp: failed to copy ${src} to ${dst} '
244255 code: code
245256 )
246257 }
247258 } $else {
259+ mut w_dst := dst
260+ if is_dir (dst) {
261+ w_dst = join_path_single (w_dst, file_name (src))
262+ }
248263 fp_from := C.open (& char (src.str), C.O_RDONLY, 0 )
249264 if fp_from < 0 { // Check if file opened
250- return error_with_code ('cp: failed to open ${src} ' , int (fp_from))
265+ return error_with_code ('cp: failed to open ${src} for reading' , int (fp_from))
266+ }
267+ mode_flags := C.S_IWUSR | C.S_IRUSR
268+ mut open_flags := C.O_WRONLY | C.O_CREAT | C.O_TRUNC
269+ if config.fail_if_exists {
270+ open_flags |= C.O_EXCL
251271 }
252- fp_to := C.open (& char (dst .str), C.O_WRONLY | C.O_CREAT | C.O_TRUNC, C.S_IWUSR | C.S_IRUSR )
272+ fp_to := C.open (& char (w_dst .str), open_flags, mode_flags )
253273 if fp_to < 0 { // Check if file opened (permissions problems ...)
254274 C.close (fp_from)
255- return error_with_code ('cp (permission): failed to write to ${dst} (fp_to: ${fp_to} )' ,
256- int (fp_to))
275+ return error_with_code ('cp: failed to open ${w_dst} for writing' , int (fp_to))
257276 }
258277 // TODO: use defer{} to close files in case of error or return.
259278 // Currently there is a C-Error when building.
@@ -267,14 +286,14 @@ pub fn cp(src string, dst string) ! {
267286 if C.write (fp_to, & buf[0 ], count) < 0 {
268287 C.close (fp_to)
269288 C.close (fp_from)
270- return error_with_code ('cp: failed to write to ${dst } ' , int (- 1 ))
289+ return error_with_code ('cp: failed to write to ${w_dst } ' , int (- 1 ))
271290 }
272291 }
273292 from_attr := stat (src)!
274- if C.chmod (& char (dst .str), from_attr.mode) < 0 {
293+ if C.chmod (& char (w_dst .str), from_attr.mode) < 0 {
275294 C.close (fp_to)
276295 C.close (fp_from)
277- return error_with_code ('failed to set permissions for ${dst } ' , int (- 1 ))
296+ return error_with_code ('failed to set permissions for ${w_dst } ' , int (- 1 ))
278297 }
279298 C.close (fp_to)
280299 C.close (fp_from)
0 commit comments