Landin library reference source

platform/posix/libc.ldn

1--  platform/posix on Darwin and FreeBSD, over platform/libc: their C
2--  library is their system interface.  A failing call's errno is read
3--  from the calling thread's own cell immediately after it, before any
4--  other call, and becomes the atom the call fails with, so no number is
5--  shared between threads.  A path relative to the working directory goes
6--  to `open` and `stat`, and only a path relative to an open directory to
7--  `openat` and `fstatat`, so a program interposing on the C library meets
8--  the calls it always has.
9
10import core/text
11import platform/libc
12
13fixed if compiler.os == darwin or compiler.os == freebsd then
14    --  The errno the calling thread's last failing call left, as an atom.
15    failure: () -> (problem: errno) =
16        cell: ptr mut i32 = libc.errno_cell()
17        problem = errno_of(cell.val)
18    end failure
19
20    --  The flags `open` takes for `options`.
21    flags_of: (options: open_options) -> (flags: i32) =
22        flags = match options.access
23            read_only: libc.o_rdonly
24            write_only: libc.o_wronly
25            read_write: libc.o_rdwr
26        end match
27        if options.create then flags = flags | libc.o_creat end if
28        if options.exclusive then flags = flags | libc.o_excl end if
29        if options.truncate then flags = flags | libc.o_trunc end if
30        if options.append then flags = flags | libc.o_append end if
31        if options.directory then flags = flags | libc.o_directory end if
32    end flags_of
33
34    --  `open`, or `openat` relative to an open directory, with the
35    --  permission bits as the one variadic argument only when creating.
36    opened_at: (at: descriptor, path: ptr u8, flags: i32, permissions: u32)
37               -> (result: i32) =
38        if at == working_directory then
39            result = if (flags & libc.o_creat) <> 0
40            then libc.open(path, flags, i32(permissions))
41            else libc.open(path, flags) end if
42        else
43            result = if (flags & libc.o_creat) <> 0
44            then libc.openat(i32(at), path, flags, i32(permissions))
45            else libc.openat(i32(at), path, flags) end if
46        end if
47    end opened_at
48
49    system_open: (at: descriptor, path: ptr u8, options: open_options,
50                  permissions: u32) -> (opened: descriptor) ! errno =
51        result: i32 = opened_at(at, path, flags_of(options), permissions)
52        fail failure() when result < 0
53        opened = descriptor(result)
54    end system_open
55
56    system_read: (source: descriptor, into: []mut u8)
57                 -> (count: usize) ! errno =
58        mut spare: u8 = 0
59        base: ptr mut u8 = if lenof into > 0 then addr into[0]
60                           else addr spare end if
61        result: isize = libc.read(i32(source), base, lenof into)
62        fail failure() when result < 0
63        count = usize(result)
64    end system_read
65
66    system_write: (to: descriptor, bytes: []u8) -> (count: usize) ! errno =
67        spare: u8 = 0
68        base: ptr u8 = if lenof bytes > 0 then addr bytes[0]
69                       else addr spare end if
70        result: isize = libc.write(i32(to), base, lenof bytes)
71        fail failure() when result < 0
72        count = usize(result)
73    end system_write
74
75    system_close: (closing: descriptor) -> none ! errno =
76        fail failure() when libc.close(i32(closing)) <> 0
77    end system_close
78
79    --  The record is checked against the native header's size before it
80    --  is used, as abi/core-io-file-identity holds it to that header: a
81    --  disagreement is `unlisted`, which no errno describes.
82    system_status: (at: descriptor, path: ptr u8, follow: bool)
83                   -> (result: status) ! errno =
84        fail unlisted when alignof libc.file_status <> 8
85          or sizeof libc.file_status <> libc.file_status_size
86        mut record: libc.file_status = zeroed
87        outcome: i32 =
88            if at == working_directory and follow
89            then libc.stat(path, addr record)
90            else libc.fstatat(i32(at), path, addr record,
91                              if follow then 0
92                              else libc.at_symlink_nofollow end if) end if
93        fail failure() when outcome <> 0
94        result = (device: u64(record.device), inode: u64(record.inode),
95                  kind: kind_of_mode(u32(record.mode)),
96                  size: u64(record.bytes))
97    end system_status
98
99    --  The directory is opened with o_directory, which makes anything else
100    --  ENOTDIR, and a C directory stream made over it, which then owns the
101    --  descriptor.
102    system_open_directory: (at: descriptor, path: ptr u8)
103                           -> (opened: directory_stream) ! errno =
104        result: i32 = opened_at(at, path, libc.o_rdonly | libc.o_directory, 0)
105        fail failure() when result < 0
106        made: libc.directory_stream = libc.fdopendir(result)
107        match made
108            libc.no_directory: begin
109                problem: errno = failure()
110                _ = libc.close(result)
111                fail problem
112            end
113            ptr (stream): opened = (descriptor: descriptor(result),
114                                    stream: usize(stream), filled: 0,
115                                    cursor: 0, finished: false)
116        end match
117    end system_open_directory
118
119    --  The stream's next record, its name copied into the buffer with a
120    --  NUL after it, since the record is the C library's and good only
121    --  until the next readdir.  The buffer holds any name a record can:
122    --  `directory_buffer` is larger than every d_name.  readdir's end and
123    --  its failure are told apart by errno, zeroed before the call.
124    system_read_directory: (inout opened: directory_stream, buffer: []mut u8)
125                           -> (item: directory_entry from buffer) ! errno =
126        cell: ptr mut i32 = libc.errno_cell()
127        cell.val = 0
128        if opened.finished then
129            item = (name: text.host_over(buffer[0..<0]), kind: unknown_kind)
130            return
131        end if
132        stream: ptr mut u8 = ptr(opened.stream)
133        record: libc.directory_entry = libc.readdir(stream)
134        match record
135            libc.no_directory: begin
136                fail errno_of(cell.val) when cell.val <> 0
137                opened.finished = true
138                item = (name: text.host_over(buffer[0..<0]),
139                        kind: unknown_kind)
140            end
141            ptr (found): begin
142                length: usize = usize(found.val.name_length)
143                fail unlisted when length >= lenof buffer
144                for at in usize(0)..<length do
145                    buffer[at] = found.val.name[at]
146                end for
147                buffer[length] = 0
148                item = (name: text.host_over(buffer[0..<length]),
149                        kind: kind_of_type(found.val.kind))
150            end
151        end match
152    end system_read_directory
153
154    system_close_directory: (opened: directory_stream) -> none ! errno =
155        stream: ptr mut u8 = ptr(opened.stream)
156        fail failure() when libc.closedir(stream) <> 0
157    end system_close_directory
158
159    system_make_directory: (at: descriptor, path: ptr u8, permissions: u32)
160                           -> none ! errno =
161        fail failure() when libc.mkdirat(i32(at), path, u16(permissions)) <> 0
162    end system_make_directory
163
164    system_remove: (at: descriptor, path: ptr u8, directory: bool)
165                   -> none ! errno =
166        flags: i32 = if directory then libc.at_removedir else 0 end if
167        fail failure() when libc.unlinkat(i32(at), path, flags) <> 0
168    end system_remove
169
170    system_map: (length: usize) -> (base: ptr mut u8) ! errno =
171        result: usize = libc.mmap(0, length, libc.prot_read | libc.prot_write,
172                                  libc.map_private | libc.map_anonymous, -1,
173                                  0)
174        fail failure() when result == libc.map_failed
175        base = ptr(result)
176    end system_map
177
178    system_unmap: (base: ptr mut u8, length: usize) -> none ! errno =
179        fail failure() when libc.munmap(usize(base), length) <> 0
180    end system_unmap
181
182    system_page_size: () -> (bytes: usize) =
183        bytes = usize(libc.getpagesize())
184    end system_page_size
185
186    system_exit: (status: i32) -> noreturn =
187        libc._exit(status)
188    end system_exit
189
190    system_poll: (entries: []mut poll_entry, milliseconds: i32)
191                 -> (ready: usize) ! errno =
192        mut spare: libc.poll_record = zeroed
193        records: ptr mut libc.poll_record =
194            if lenof entries > 0 then ptr(usize(addr entries[0]))
195            else addr spare end if
196        result: i32 = libc.poll(records, libc.poll_count(lenof entries),
197                                milliseconds)
198        fail failure() when result < 0
199        ready = usize(result)
200    end system_poll
201end if