@@ -1647,7 +1647,10 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
16471647 var res2 = setFileAttributesA (filename, res)
16481648 if res2 == - 1 'i32 : raiseOSError (osLastError (), $ (filename, permissions))
16491649
1650- proc copyFile * (source, dest: string ) {.rtl , extern : " nos$1" ,
1650+ proc createSymlink * (src, dest: string ) {.tags : [WriteIOEffect ], noWeirdTarget .}
1651+ proc expandSymlink * (symlinkPath: string ): string {.tags : [ReadIOEffect ], noWeirdTarget .}
1652+
1653+ proc copyFile * (source, dest: string , followSymlinks: bool = true ) {.rtl , extern : " nos$1" ,
16511654 tags : [ReadIOEffect , WriteIOEffect ], noWeirdTarget .} =
16521655 # # Copies a file from `source` to `dest`, where `dest.parentDir` must exist.
16531656 # #
@@ -1668,11 +1671,11 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
16681671 # # will be preserved and the content overwritten.
16691672 # #
16701673 # # See also:
1671- # # * `copyDir proc <#copyDir,string,string>`_
1674+ # # * `copyDir proc <#copyDir,string,string,bool >`_
16721675 # # * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
16731676 # # * `tryRemoveFile proc <#tryRemoveFile,string>`_
16741677 # # * `removeFile proc <#removeFile,string>`_
1675- # # * `moveFile proc <#moveFile,string,string>`_
1678+ # # * `moveFile proc <#moveFile,string,string,bool >`_
16761679
16771680 when defined (Windows ):
16781681 when useWinUnicode:
@@ -1682,34 +1685,37 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
16821685 else :
16831686 if copyFileA (source, dest, 0 'i32 ) == 0 'i32 : raiseOSError (osLastError (), $ (source, dest))
16841687 else :
1685- # generic version of copyFile which works for any platform:
1686- const bufSize = 8000 # better for memory manager
1687- var d, s: File
1688- if not open (s, source): raiseOSError (osLastError (), source)
1689- if not open (d, dest, fmWrite):
1688+ if not followSymlinks and source.checkSymlink:
1689+ createSymlink (expandSymlink (source), dest)
1690+ else :
1691+ # generic version of copyFile which works for any platform:
1692+ const bufSize = 8000 # better for memory manager
1693+ var d, s: File
1694+ if not open (s, source): raiseOSError (osLastError (), source)
1695+ if not open (d, dest, fmWrite):
1696+ close (s)
1697+ raiseOSError (osLastError (), dest)
1698+ var buf = alloc (bufSize)
1699+ while true :
1700+ var bytesread = readBuffer (s, buf, bufSize)
1701+ if bytesread > 0 :
1702+ var byteswritten = writeBuffer (d, buf, bytesread)
1703+ if bytesread != byteswritten:
1704+ dealloc (buf)
1705+ close (s)
1706+ close (d)
1707+ raiseOSError (osLastError (), dest)
1708+ if bytesread != bufSize: break
1709+ dealloc (buf)
16901710 close (s)
1691- raiseOSError (osLastError (), dest)
1692- var buf = alloc (bufSize)
1693- while true :
1694- var bytesread = readBuffer (s, buf, bufSize)
1695- if bytesread > 0 :
1696- var byteswritten = writeBuffer (d, buf, bytesread)
1697- if bytesread != byteswritten:
1698- dealloc (buf)
1699- close (s)
1700- close (d)
1701- raiseOSError (osLastError (), dest)
1702- if bytesread != bufSize: break
1703- dealloc (buf)
1704- close (s)
1705- flushFile (d)
1706- close (d)
1707-
1708- proc copyFileToDir * (source, dir: string ) {.noWeirdTarget , since : (1 ,3 ,7 ).} =
1711+ flushFile (d)
1712+ close (d)
1713+
1714+ proc copyFileToDir * (source, dir: string , followSymlinks: bool = true ) {.noWeirdTarget , since : (1 ,3 ,7 ).} =
17091715 # # Copies a file `source` into directory `dir`, which must exist.
17101716 if dir.len == 0 : # treating "" as "." is error prone
17111717 raise newException (ValueError , " dest is empty" )
1712- copyFile (source, dir / source.lastPathPart)
1718+ copyFile (source, dir / source.lastPathPart, followSymlinks = followSymlinks )
17131719
17141720when not declared (ENOENT ) and not defined (Windows ):
17151721 when NoFakeVars :
@@ -1739,10 +1745,10 @@ proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirE
17391745 # # On Windows, ignores the read-only attribute.
17401746 # #
17411747 # # See also:
1742- # # * `copyFile proc <#copyFile,string,string>`_
1748+ # # * `copyFile proc <#copyFile,string,string,bool >`_
17431749 # # * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
17441750 # # * `removeFile proc <#removeFile,string>`_
1745- # # * `moveFile proc <#moveFile,string,string>`_
1751+ # # * `moveFile proc <#moveFile,string,string,bool >`_
17461752 result = true
17471753 when defined (Windows ):
17481754 when useWinUnicode:
@@ -1772,10 +1778,10 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], n
17721778 # #
17731779 # # See also:
17741780 # # * `removeDir proc <#removeDir,string>`_
1775- # # * `copyFile proc <#copyFile,string,string>`_
1781+ # # * `copyFile proc <#copyFile,string,string,bool >`_
17761782 # # * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
17771783 # # * `tryRemoveFile proc <#tryRemoveFile,string>`_
1778- # # * `moveFile proc <#moveFile,string,string>`_
1784+ # # * `moveFile proc <#moveFile,string,string,bool >`_
17791785 if not tryRemoveFile (file):
17801786 raiseOSError (osLastError (), file)
17811787
@@ -1802,7 +1808,7 @@ proc tryMoveFSObject(source, dest: string): bool {.noWeirdTarget.} =
18021808 raiseOSError (err, $ (source, dest, strerror (errno)))
18031809 return true
18041810
1805- proc moveFile * (source, dest: string ) {.rtl , extern : " nos$1" ,
1811+ proc moveFile * (source, dest: string , followSymlinks: bool = true ) {.rtl , extern : " nos$1" ,
18061812 tags : [ReadIOEffect , WriteIOEffect ], noWeirdTarget .} =
18071813 # # Moves a file from `source` to `dest`.
18081814 # #
@@ -1812,16 +1818,16 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
18121818 # # Can be used to `rename files`:idx:.
18131819 # #
18141820 # # See also:
1815- # # * `moveDir proc <#moveDir,string,string>`_
1816- # # * `copyFile proc <#copyFile,string,string>`_
1821+ # # * `moveDir proc <#moveDir,string,string,bool >`_
1822+ # # * `copyFile proc <#copyFile,string,string,bool >`_
18171823 # # * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
18181824 # # * `removeFile proc <#removeFile,string>`_
18191825 # # * `tryRemoveFile proc <#tryRemoveFile,string>`_
18201826
18211827 if not tryMoveFSObject (source, dest):
18221828 when not defined (windows):
18231829 # Fallback to copy & del
1824- copyFile (source, dest)
1830+ copyFile (source, dest, followSymlinks = followSymlinks )
18251831 try :
18261832 removeFile (source)
18271833 except :
@@ -2223,9 +2229,9 @@ proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [
22232229 # # * `removeFile proc <#removeFile,string>`_
22242230 # # * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
22252231 # # * `createDir proc <#createDir,string>`_
2226- # # * `copyDir proc <#copyDir,string,string>`_
2227- # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
2228- # # * `moveDir proc <#moveDir,string,string>`_
2232+ # # * `copyDir proc <#copyDir,string,string,bool >`_
2233+ # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string,bool >`_
2234+ # # * `moveDir proc <#moveDir,string,string,bool >`_
22292235 for kind, path in walkDir (dir, checkDir = checkDir):
22302236 case kind
22312237 of pcFile, pcLinkToFile, pcLinkToDir: removeFile (path)
@@ -2290,9 +2296,9 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
22902296 # # See also:
22912297 # # * `removeDir proc <#removeDir,string>`_
22922298 # # * `createDir proc <#createDir,string>`_
2293- # # * `copyDir proc <#copyDir,string,string>`_
2294- # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
2295- # # * `moveDir proc <#moveDir,string,string>`_
2299+ # # * `copyDir proc <#copyDir,string,string,bool >`_
2300+ # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string,bool >`_
2301+ # # * `moveDir proc <#moveDir,string,string,bool >`_
22962302 result = not rawCreateDir (dir)
22972303 if result :
22982304 # path already exists - need to check that it is indeed a directory
@@ -2312,9 +2318,9 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1",
23122318 # # See also:
23132319 # # * `removeDir proc <#removeDir,string>`_
23142320 # # * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
2315- # # * `copyDir proc <#copyDir,string,string>`_
2316- # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
2317- # # * `moveDir proc <#moveDir,string,string>`_
2321+ # # * `copyDir proc <#copyDir,string,string,bool >`_
2322+ # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string,bool >`_
2323+ # # * `moveDir proc <#moveDir,string,string,bool >`_
23182324 var omitNext = false
23192325 when doslikeFileSystem:
23202326 omitNext = isAbsolute (dir)
@@ -2330,7 +2336,7 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1",
23302336 dir[^ 1 ] notin {DirSep , AltSep }:
23312337 discard existsOrCreateDir (dir)
23322338
2333- proc copyDir * (source, dest: string ) {.rtl , extern : " nos$1" ,
2339+ proc copyDir * (source, dest: string , followSymlinks: bool = true ) {.rtl , extern : " nos$1" ,
23342340 tags : [WriteIOEffect , ReadIOEffect ], benign , noWeirdTarget .} =
23352341 # # Copies a directory from `source` to `dest`.
23362342 # #
@@ -2341,46 +2347,47 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
23412347 # #
23422348 # # On other platforms created files and directories will inherit the
23432349 # # default permissions of a newly created file/directory for the user.
2344- # # Use `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
2350+ # # Use `copyDirWithPermissions proc <#copyDirWithPermissions,string,string,bool >`_
23452351 # # to preserve attributes recursively on these platforms.
23462352 # #
23472353 # # See also:
2348- # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
2349- # # * `copyFile proc <#copyFile,string,string>`_
2354+ # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string,bool >`_
2355+ # # * `copyFile proc <#copyFile,string,string,bool >`_
23502356 # # * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
23512357 # # * `removeDir proc <#removeDir,string>`_
23522358 # # * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
23532359 # # * `createDir proc <#createDir,string>`_
2354- # # * `moveDir proc <#moveDir,string,string>`_
2360+ # # * `moveDir proc <#moveDir,string,string,bool >`_
23552361 createDir (dest)
23562362 for kind, path in walkDir (source):
23572363 var noSource = splitPath (path).tail
23582364 case kind
23592365 of pcFile:
2360- copyFile (path, dest / noSource)
2366+ copyFile (path, dest / noSource, followSymlinks = followSymlinks )
23612367 of pcDir:
2362- copyDir (path, dest / noSource)
2368+ copyDir (path, dest / noSource, followSymlinks = followSymlinks )
23632369 else : discard
23642370
2365- proc moveDir * (source, dest: string ) {.tags : [ReadIOEffect , WriteIOEffect ], noWeirdTarget .} =
2371+ proc moveDir * (source, dest: string , followSymlinks: bool = true ) {.tags : [ReadIOEffect , WriteIOEffect ],
2372+ noWeirdTarget .} =
23662373 # # Moves a directory from `source` to `dest`.
23672374 # #
23682375 # # If this fails, `OSError` is raised.
23692376 # #
23702377 # # See also:
2371- # # * `moveFile proc <#moveFile,string,string>`_
2372- # # * `copyDir proc <#copyDir,string,string>`_
2373- # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
2378+ # # * `moveFile proc <#moveFile,string,string,bool >`_
2379+ # # * `copyDir proc <#copyDir,string,string,bool >`_
2380+ # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string,bool >`_
23742381 # # * `removeDir proc <#removeDir,string>`_
23752382 # # * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
23762383 # # * `createDir proc <#createDir,string>`_
23772384 if not tryMoveFSObject (source, dest):
23782385 when not defined (windows):
23792386 # Fallback to copy & del
2380- copyDir (source, dest)
2387+ copyDir (source, dest, followSymlinks = followSymlinks )
23812388 removeDir (source)
23822389
2383- proc createSymlink * (src, dest: string ) {.noWeirdTarget .} =
2390+ proc createSymlink * (src, dest: string ) {.tags : [ WriteIOEffect ], noWeirdTarget .} =
23842391 # # Create a symbolic link at `dest` which points to the item specified
23852392 # # by `src`. On most operating systems, will fail if a link already exists.
23862393 # #
@@ -2431,7 +2438,7 @@ proc createHardlink*(src, dest: string) {.noWeirdTarget.} =
24312438 raiseOSError (osLastError (), $ (src, dest))
24322439
24332440proc copyFileWithPermissions * (source, dest: string ,
2434- ignorePermissionErrors = true ) {.noWeirdTarget .} =
2441+ ignorePermissionErrors = true , followSymlinks = true ) {.noWeirdTarget .} =
24352442 # # Copies a file from `source` to `dest` preserving file permissions.
24362443 # #
24372444 # # This is a wrapper proc around `copyFile <#copyFile,string,string>`_,
@@ -2450,12 +2457,12 @@ proc copyFileWithPermissions*(source, dest: string,
24502457 # #
24512458 # # See also:
24522459 # # * `copyFile proc <#copyFile,string,string>`_
2453- # # * `copyDir proc <#copyDir,string,string>`_
2460+ # # * `copyDir proc <#copyDir,string,string,bool >`_
24542461 # # * `tryRemoveFile proc <#tryRemoveFile,string>`_
24552462 # # * `removeFile proc <#removeFile,string>`_
2456- # # * `moveFile proc <#moveFile,string,string>`_
2457- # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_
2458- copyFile (source, dest)
2463+ # # * `moveFile proc <#moveFile,string,string,bool >`_
2464+ # # * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string,bool >`_
2465+ copyFile (source, dest, followSymlinks = followSymlinks )
24592466 when not defined (Windows ):
24602467 try :
24612468 setFilePermissions (dest, getFilePermissions (source))
@@ -2464,17 +2471,17 @@ proc copyFileWithPermissions*(source, dest: string,
24642471 raise
24652472
24662473proc copyDirWithPermissions * (source, dest: string ,
2467- ignorePermissionErrors = true ) {.rtl , extern : " nos$1" ,
2474+ ignorePermissionErrors = true , followSymlinks = true ) {.rtl , extern : " nos$1" ,
24682475 tags : [WriteIOEffect , ReadIOEffect ], benign , noWeirdTarget .} =
24692476 # # Copies a directory from `source` to `dest` preserving file permissions.
24702477 # #
24712478 # # If this fails, `OSError` is raised. This is a wrapper proc around `copyDir
2472- # # <#copyDir,string,string>`_ and `copyFileWithPermissions
2479+ # # <#copyDir,string,string,bool >`_ and `copyFileWithPermissions
24732480 # # <#copyFileWithPermissions,string,string>`_ procs
24742481 # # on non-Windows platforms.
24752482 # #
24762483 # # On Windows this proc is just a wrapper for `copyDir proc
2477- # # <#copyDir,string,string>`_ since that proc already copies attributes.
2484+ # # <#copyDir,string,string,bool >`_ since that proc already copies attributes.
24782485 # #
24792486 # # On non-Windows systems permissions are copied after the file or directory
24802487 # # itself has been copied, which won't happen atomically and could lead to a
@@ -2483,11 +2490,11 @@ proc copyDirWithPermissions*(source, dest: string,
24832490 # # `OSError`.
24842491 # #
24852492 # # See also:
2486- # # * `copyDir proc <#copyDir,string,string>`_
2493+ # # * `copyDir proc <#copyDir,string,string,bool >`_
24872494 # # * `copyFile proc <#copyFile,string,string>`_
24882495 # # * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_
24892496 # # * `removeDir proc <#removeDir,string>`_
2490- # # * `moveDir proc <#moveDir,string,string>`_
2497+ # # * `moveDir proc <#moveDir,string,string,bool >`_
24912498 # # * `existsOrCreateDir proc <#existsOrCreateDir,string>`_
24922499 # # * `createDir proc <#createDir,string>`_
24932500 createDir (dest)
@@ -2501,9 +2508,9 @@ proc copyDirWithPermissions*(source, dest: string,
25012508 var noSource = splitPath (path).tail
25022509 case kind
25032510 of pcFile:
2504- copyFileWithPermissions (path, dest / noSource, ignorePermissionErrors)
2511+ copyFileWithPermissions (path, dest / noSource, ignorePermissionErrors, followSymlinks = followSymlinks )
25052512 of pcDir:
2506- copyDirWithPermissions (path, dest / noSource, ignorePermissionErrors)
2513+ copyDirWithPermissions (path, dest / noSource, ignorePermissionErrors, followSymlinks = followSymlinks )
25072514 else : discard
25082515
25092516proc inclFilePermissions * (filename: string ,
@@ -2524,7 +2531,7 @@ proc exclFilePermissions*(filename: string,
25242531 # # setFilePermissions(filename, getFilePermissions(filename)-permissions)
25252532 setFilePermissions (filename, getFilePermissions (filename)- permissions)
25262533
2527- proc expandSymlink * (symlinkPath: string ): string {.noWeirdTarget .} =
2534+ proc expandSymlink * (symlinkPath: string ): string {.tags : [ ReadIOEffect ], noWeirdTarget .} =
25282535 # # Returns a string representing the path to which the symbolic link points.
25292536 # #
25302537 # # On Windows this is a noop, ``symlinkPath`` is simply returned.
0 commit comments