compiler/tests/fixtures/runtime/posix-files/main.ldn
1-- platform/posix's files: a descriptor is a handle, a failure is the
2-- errno as an atom, and a path is a host string, terminated beneath the
3-- caller. The program makes a scratch directory, opens a file in it
4-- relative to the directory's own descriptor, writes and reads it back,
5-- asks its status, and removes both. Each failure is the errno POSIX
6-- names for it, the same atom on Linux, Darwin and FreeBSD: a missing
7-- name, an exclusive create of a taken one, a file used as a directory, a
8-- directory opened for writing, a closed descriptor, a path holding a NUL
9-- and one longer than PATH_MAX. The program writes each atom's number
10-- on this system, so a failing lane shows what it met.
11import core/text
12import platform/posix
13
14top: []u8 = "posix-files-scratch"
15inner: []u8 = "posix-files-scratch/data"
16
17-- Write LINE to standard output.
18say: (line: []u8) -> none =
19 _ = posix.write(posix.standard_output, line) else 0
20end say
21
22-- Write NAME, `=` and the number PROBLEM has on this system.
23said: (name: []u8, problem: posix.errno) -> none =
24 mut line: [32]u8 = zeroed
25 mut used: usize = 0
26 for byte in name do
27 line[used] = byte
28 inc used
29 end for
30 line[used] = 61
31 inc used
32 used = text.write_u32(line[0..<32], used,
33 u32(posix.number(problem))) else used
34 line[used] = 10
35 say(line[0..<used + 1])
36end said
37
38-- Remove what an earlier run may have left.
39clear: () -> none =
40 posix.unlink_at(posix.working_directory, text.host_over(inner)) else (p)
41 _ = p
42 end
43 posix.remove_directory_at(posix.working_directory,
44 text.host_over(top)) else (p)
45 _ = p
46 end
47end clear
48
49-- Whether opening PATH with OPTIONS, relative to AT, fails with WANTED.
50refused: (at: posix.descriptor, path: text.host_string,
51 options: posix.open_options, wanted: posix.errno, name: []u8)
52 -> (yes: bool) =
53 yes = false
54 opened: posix.descriptor = posix.open_at(at, path, options, 0o644)
55 else (problem)
56 said(name, problem)
57 yes = problem == wanted
58 return
59 end
60 posix.close(opened) else (p)
61 _ = p
62 end
63end refused
64
65public main: () -> (code: i32) =
66 clear()
67 code = 1
68 posix.make_directory_at(posix.working_directory, text.host_over(top),
69 0o755) else (problem)
70 said("mkdir", problem)
71 return
72 end
73 code = 2
74 folder: posix.descriptor =
75 posix.open_at(posix.working_directory, text.host_over(top),
76 (access: posix.read_only, create: false,
77 exclusive: false, truncate: false, append: false,
78 directory: true), 0) else (problem)
79 said("folder", problem)
80 return
81 end
82
83 -- A file made relative to the directory's descriptor, written and
84 -- read back through a second descriptor.
85 code = 3
86 written: posix.descriptor =
87 posix.open_at(folder, text.host_over("data"), posix.writing,
88 0o644) else (problem)
89 said("create", problem)
90 return
91 end
92 count: usize = posix.write(written, "posix\n") else (problem)
93 said("write", problem)
94 return
95 end
96 return when count <> 6
97 posix.close(written) else (problem)
98 said("close", problem)
99 return
100 end
101 code = 4
102 reading: posix.descriptor =
103 posix.open_at(posix.working_directory, text.host_over(inner),
104 posix.reading, 0) else (problem)
105 said("open", problem)
106 return
107 end
108 mut buffer: [16]u8 = zeroed
109 got: usize = posix.read(reading, buffer[0..<16]) else (problem)
110 said("read", problem)
111 return
112 end
113 return when got <> 6 or buffer[0] <> 112 or buffer[5] <> 10
114 ended: usize = posix.read(reading, buffer[0..<16]) else (problem)
115 said("eof", problem)
116 return
117 end
118 return when ended <> 0
119 posix.close(reading) else (problem)
120 said("close", problem)
121 return
122 end
123
124 -- Its status: a regular file of six bytes, the same file by either
125 -- path, inside a directory.
126 code = 5
127 file: posix.status =
128 posix.status_at(folder, text.host_over("data"), false) else (problem)
129 said("stat", problem)
130 return
131 end
132 again: posix.status =
133 posix.status_at(posix.working_directory, text.host_over(inner),
134 true) else (problem)
135 said("stat", problem)
136 return
137 end
138 holder: posix.status =
139 posix.status_at(posix.working_directory, text.host_over(top),
140 true) else (problem)
141 said("stat", problem)
142 return
143 end
144 return when file.kind <> posix.regular or file.size <> 6
145 or file.device <> again.device or file.inode <> again.inode
146 or holder.kind <> posix.directory or holder.inode == file.inode
147
148 -- The failure oracles.
149 code = 6
150 return when not refused(folder, text.host_over("absent"), posix.reading,
151 posix.enoent, "absent")
152 code = 7
153 return when not refused(folder, text.host_over("data"),
154 (access: posix.write_only, create: true,
155 exclusive: true, truncate: false,
156 append: false, directory: false),
157 posix.eexist, "exclusive")
158 code = 8
159 return when not refused(folder, text.host_over("data/below"),
160 posix.reading, posix.enotdir, "through")
161 code = 9
162 return when not refused(posix.working_directory, text.host_over(top),
163 posix.writing, posix.eisdir, "directory")
164 code = 10
165 return when not refused(posix.working_directory,
166 text.host_over("posix\x00files"), posix.reading,
167 posix.einval, "nul")
168 code = 11
169 long: [5000]u8 = [5000 of 97]
170 return when not refused(posix.working_directory,
171 text.host_over(long[0..<5000]), posix.reading,
172 posix.enametoolong, "long")
173 code = 12
174 _ = posix.read(reading, buffer[0..<16]) else (problem)
175 said("closed", problem)
176 return when problem <> posix.ebadf
177 0
178 end
179
180 code = 13
181 posix.close(folder) else (problem)
182 said("close", problem)
183 return
184 end
185 posix.unlink_at(posix.working_directory, text.host_over(inner))
186 else (problem)
187 said("unlink", problem)
188 return
189 end
190 posix.remove_directory_at(posix.working_directory, text.host_over(top))
191 else (problem)
192 said("rmdir", problem)
193 return
194 end
195 code = 42
196end main