1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
#+private
#+build darwin, netbsd, freebsd, openbsd
package os
import "base:runtime"
import "core:sys/posix"
import "core:time"
internal_stat :: proc(stat: posix.stat_t, fullpath: string) -> (fi: File_Info) {
fi.fullpath = fullpath
_, fi.name = split_path(fi.fullpath)
fi.inode = u128(stat.st_ino)
fi.size = i64(stat.st_size)
fi.mode = transmute(Permissions)u32(transmute(posix._mode_t)(stat.st_mode - posix.S_IFMT))
fi.type = .Undetermined
switch {
case posix.S_ISBLK(stat.st_mode):
fi.type = .Block_Device
case posix.S_ISCHR(stat.st_mode):
fi.type = .Character_Device
case posix.S_ISDIR(stat.st_mode):
fi.type = .Directory
case posix.S_ISFIFO(stat.st_mode):
fi.type = .Named_Pipe
case posix.S_ISLNK(stat.st_mode):
fi.type = .Symlink
case posix.S_ISREG(stat.st_mode):
fi.type = .Regular
case posix.S_ISSOCK(stat.st_mode):
fi.type = .Socket
}
fi.creation_time = timespec_time(stat.st_birthtimespec)
fi.modification_time = timespec_time(stat.st_mtim)
fi.access_time = timespec_time(stat.st_atim)
timespec_time :: proc(t: posix.timespec) -> time.Time {
return time.Time{_nsec = i64(t.tv_sec) * 1e9 + i64(t.tv_nsec)}
}
return
}
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if f == nil || f.impl == nil {
err = .Invalid_File
return
}
impl := (^File_Impl)(f.impl)
stat: posix.stat_t
if posix.fstat(impl.fd, &stat) != .OK {
err = _get_platform_error()
return
}
fullpath := clone_string(impl.name, allocator) or_return
return internal_stat(stat, fullpath), nil
}
_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if name == "" {
err = .Invalid_Path
return
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
cname := clone_to_cstring(name, temp_allocator) or_return
fd := posix.open(cname, {})
if fd == -1 {
err = _get_platform_error()
return
}
defer posix.close(fd)
fullpath := _posix_absolute_path(fd, name, allocator) or_return
stat: posix.stat_t
if posix.stat(fullpath, &stat) != .OK {
err = _get_platform_error()
return
}
return internal_stat(stat, string(fullpath)), nil
}
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if name == "" {
err = .Invalid_Path
return
}
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
// NOTE: can't use realpath or open (+ fcntl F_GETPATH) here because it tries to resolve symlinks.
// NOTE: This might not be correct when given "/symlink/foo.txt",
// you would want that to resolve "/symlink", but not resolve "foo.txt".
fullpath := clean_path(name, temp_allocator) or_return
assert(len(fullpath) > 0)
switch {
case fullpath[0] == '/':
// nothing.
case fullpath == ".":
fullpath = getwd(temp_allocator) or_return
case len(fullpath) > 1 && fullpath[0] == '.' && fullpath[1] == '/':
fullpath = fullpath[2:]
fallthrough
case:
fullpath = concatenate({
getwd(temp_allocator) or_return,
"/",
fullpath,
}, temp_allocator) or_return
}
stat: posix.stat_t
c_fullpath := clone_to_cstring(fullpath, temp_allocator) or_return
if posix.lstat(c_fullpath, &stat) != .OK {
err = _get_platform_error()
return
}
fullpath = clone_string(fullpath, allocator) or_return
return internal_stat(stat, fullpath), nil
}
_same_file :: proc(fi1, fi2: File_Info) -> bool {
return fi1.fullpath == fi2.fullpath
}
_is_reserved_name :: proc(path: string) -> bool {
return false
}
|