@@ -926,6 +926,123 @@ pub const Dir = struct {
926
926
return self .openDir (sub_path , open_dir_options );
927
927
}
928
928
929
+ /// This function returns the canonicalized absolute pathname of
930
+ /// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this
931
+ /// `Dir` handle and returns the canonicalized absolute pathname of `pathname`
932
+ /// argument.
933
+ /// This function is not universally supported by all platforms.
934
+ /// Currently supported hosts are: Linux, macOS, and Windows.
935
+ /// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.
936
+ pub fn realpath (self : Dir , pathname : []const u8 , out_buffer : []u8 ) ! []u8 {
937
+ if (builtin .os .tag == .wasi ) {
938
+ @compileError ("realpath is unsupported in WASI" );
939
+ }
940
+ if (builtin .os .tag == .windows ) {
941
+ const pathname_w = try os .windows .sliceToPrefixedFileW (pathname );
942
+ return self .realpathW (pathname_w .span (), out_buffer );
943
+ }
944
+ const pathname_c = try os .toPosixPath (pathname );
945
+ return self .realpathZ (& pathname_c , out_buffer );
946
+ }
947
+
948
+ /// Same as `Dir.realpath` except `pathname` is null-terminated.
949
+ /// See also `Dir.realpath`, `realpathZ`.
950
+ pub fn realpathZ (self : Dir , pathname : [* :0 ]const u8 , out_buffer : []u8 ) ! []u8 {
951
+ if (builtin .os .tag == .windows ) {
952
+ const pathname_w = try os .windows .cStrToPrefixedFileW (pathname );
953
+ return self .realpathW (pathname_w .span (), out_buffer );
954
+ }
955
+
956
+ const flags = if (builtin .os .tag == .linux ) os .O_PATH | os .O_NONBLOCK | os .O_CLOEXEC else os .O_NONBLOCK | os .O_CLOEXEC ;
957
+ const fd = os .openatZ (self .fd , pathname , flags , 0 ) catch | err | switch (err ) {
958
+ error .FileLocksNotSupported = > unreachable ,
959
+ else = > | e | return e ,
960
+ };
961
+ defer os .close (fd );
962
+
963
+ // Use of MAX_PATH_BYTES here is valid as the realpath function does not
964
+ // have a variant that takes an arbitrary-size buffer.
965
+ // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
966
+ // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
967
+ // paths. musl supports passing NULL but restricts the output to PATH_MAX
968
+ // anyway.
969
+ var buffer : [MAX_PATH_BYTES ]u8 = undefined ;
970
+ const out_path = try os .getFdPath (fd , & buffer );
971
+
972
+ if (out_path .len > out_buffer .len ) {
973
+ return error .NameTooLong ;
974
+ }
975
+
976
+ mem .copy (u8 , out_buffer , out_path );
977
+
978
+ return out_buffer [0.. out_path .len ];
979
+ }
980
+
981
+ /// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 encoded.
982
+ /// See also `Dir.realpath`, `realpathW`.
983
+ pub fn realpathW (self : Dir , pathname : []const u16 , out_buffer : []u8 ) ! []u8 {
984
+ const w = os .windows ;
985
+
986
+ const access_mask = w .GENERIC_READ | w .SYNCHRONIZE ;
987
+ const share_access = w .FILE_SHARE_READ ;
988
+ const creation = w .FILE_OPEN ;
989
+ const h_file = blk : {
990
+ const res = w .OpenFile (pathname , .{
991
+ .dir = self .fd ,
992
+ .access_mask = access_mask ,
993
+ .share_access = share_access ,
994
+ .creation = creation ,
995
+ .io_mode = .blocking ,
996
+ }) catch | err | switch (err ) {
997
+ error .IsDir = > break :blk w .OpenFile (pathname , .{
998
+ .dir = self .fd ,
999
+ .access_mask = access_mask ,
1000
+ .share_access = share_access ,
1001
+ .creation = creation ,
1002
+ .io_mode = .blocking ,
1003
+ .open_dir = true ,
1004
+ }) catch | er | switch (er ) {
1005
+ error .WouldBlock = > unreachable ,
1006
+ else = > | e2 | return e2 ,
1007
+ },
1008
+ error .WouldBlock = > unreachable ,
1009
+ else = > | e | return e ,
1010
+ };
1011
+ break :blk res ;
1012
+ };
1013
+ defer w .CloseHandle (h_file );
1014
+
1015
+ // Use of MAX_PATH_BYTES here is valid as the realpath function does not
1016
+ // have a variant that takes an arbitrary-size buffer.
1017
+ // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
1018
+ // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
1019
+ // paths. musl supports passing NULL but restricts the output to PATH_MAX
1020
+ // anyway.
1021
+ var buffer : [MAX_PATH_BYTES ]u8 = undefined ;
1022
+ const out_path = try os .getFdPath (h_file , & buffer );
1023
+
1024
+ if (out_path .len > out_buffer .len ) {
1025
+ return error .NameTooLong ;
1026
+ }
1027
+
1028
+ mem .copy (u8 , out_buffer , out_path );
1029
+
1030
+ return out_buffer [0.. out_path .len ];
1031
+ }
1032
+
1033
+ /// Same as `Dir.realpath` except caller must free the returned memory.
1034
+ /// See also `Dir.realpath`.
1035
+ pub fn realpathAlloc (self : Dir , allocator : * Allocator , pathname : []const u8 ) ! []u8 {
1036
+ // Use of MAX_PATH_BYTES here is valid as the realpath function does not
1037
+ // have a variant that takes an arbitrary-size buffer.
1038
+ // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
1039
+ // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
1040
+ // paths. musl supports passing NULL but restricts the output to PATH_MAX
1041
+ // anyway.
1042
+ var buf : [MAX_PATH_BYTES ]u8 = undefined ;
1043
+ return allocator .dupe (u8 , try self .realpath (pathname , buf [0.. ]));
1044
+ }
1045
+
929
1046
/// Changes the current working directory to the open directory handle.
930
1047
/// This modifies global state and can have surprising effects in multi-
931
1048
/// threaded applications. Most applications and especially libraries should
@@ -2060,7 +2177,7 @@ pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 {
2060
2177
}
2061
2178
2062
2179
/// `realpath`, except caller must free the returned memory.
2063
- /// TODO integrate with `Dir`
2180
+ /// See also `Dir.realpath`.
2064
2181
pub fn realpathAlloc (allocator : * Allocator , pathname : []const u8 ) ! []u8 {
2065
2182
// Use of MAX_PATH_BYTES here is valid as the realpath function does not
2066
2183
// have a variant that takes an arbitrary-size buffer.
0 commit comments