| ... | @@ -11,7 +11,6 @@ pub const stderr_fileno = 2; | ... | @@ -11,7 +11,6 @@ pub const stderr_fileno = 2; |
| 11 | | 11 | |
| 12 | pub var stdin = InStream { | 12 | pub var stdin = InStream { |
| 13 | .fd = stdin_fileno, | 13 | .fd = stdin_fileno, |
| 14 | .offset = 0, | | |
| 15 | }; | 14 | }; |
| 16 | | 15 | |
| 17 | pub var stdout = OutStream { | 16 | pub var stdout = OutStream { |
| ... | @@ -37,9 +36,6 @@ pub error Unexpected; | ... | @@ -37,9 +36,6 @@ pub error Unexpected; |
| 37 | | 36 | |
| 38 | pub error DiskQuota; | 37 | pub error DiskQuota; |
| 39 | pub error FileTooBig; | 38 | pub error FileTooBig; |
| 40 | // TODO hide interrupts at this layer by retrying. Users can use the linux specific APIs if they | | |
| 41 | // want to handle interrupts. | | |
| 42 | pub error SigInterrupt; | | |
| 43 | pub error Io; | 39 | pub error Io; |
| 44 | pub error NoSpaceLeft; | 40 | pub error NoSpaceLeft; |
| 45 | pub error BadPerm; | 41 | pub error BadPerm; |
| ... | @@ -113,34 +109,42 @@ pub struct OutStream { | ... | @@ -113,34 +109,42 @@ pub struct OutStream { |
| 113 | } | 109 | } |
| 114 | | 110 | |
| 115 | pub fn flush(os: &OutStream) -> %void { | 111 | pub fn flush(os: &OutStream) -> %void { |
| 116 | const write_ret = linux.write(os.fd, &os.buffer[0], os.index); | 112 | while (true) { |
| 117 | const write_err = linux.getErrno(write_ret); | 113 | const write_ret = linux.write(os.fd, &os.buffer[0], os.index); |
| 118 | if (write_err > 0) { | 114 | const write_err = linux.getErrno(write_ret); |
| 119 | return switch (write_err) { | 115 | if (write_err > 0) { |
| 120 | errno.EINVAL => unreachable{}, | 116 | return switch (write_err) { |
| 121 | errno.EDQUOT => error.DiskQuota, | 117 | errno.EINTR => continue, |
| 122 | errno.EFBIG => error.FileTooBig, | 118 | |
| 123 | errno.EINTR => error.SigInterrupt, | 119 | errno.EINVAL => unreachable{}, |
| 124 | errno.EIO => error.Io, | 120 | errno.EDQUOT => error.DiskQuota, |
| 125 | errno.ENOSPC => error.NoSpaceLeft, | 121 | errno.EFBIG => error.FileTooBig, |
| 126 | errno.EPERM => error.BadPerm, | 122 | errno.EIO => error.Io, |
| 127 | errno.EPIPE => error.PipeFail, | 123 | errno.ENOSPC => error.NoSpaceLeft, |
| 128 | else => error.Unexpected, | 124 | errno.EPERM => error.BadPerm, |
| | 125 | errno.EPIPE => error.PipeFail, |
| | 126 | else => error.Unexpected, |
| | 127 | } |
| 129 | } | 128 | } |
| | 129 | os.index = 0; |
| | 130 | return; |
| 130 | } | 131 | } |
| 131 | os.index = 0; | | |
| 132 | } | 132 | } |
| 133 | | 133 | |
| 134 | pub fn close(os: &OutStream) -> %void { | 134 | pub fn close(os: &OutStream) -> %void { |
| 135 | const close_ret = linux.close(os.fd); | 135 | while (true) { |
| 136 | const close_err = linux.getErrno(close_ret); | 136 | const close_ret = linux.close(os.fd); |
| 137 | if (close_err > 0) { | 137 | const close_err = linux.getErrno(close_ret); |
| 138 | return switch (close_err) { | 138 | if (close_err > 0) { |
| 139 | errno.EIO => error.Io, | 139 | return switch (close_err) { |
| 140 | errno.EBADF => error.BadFd, | 140 | errno.EINTR => continue, |
| 141 | errno.EINTR => error.SigInterrupt, | 141 | |
| 142 | else => error.Unexpected, | 142 | errno.EIO => error.Io, |
| | 143 | errno.EBADF => error.BadFd, |
| | 144 | else => error.Unexpected, |
| | 145 | } |
| 143 | } | 146 | } |
| | 147 | return; |
| 144 | } | 148 | } |
| 145 | } | 149 | } |
| 146 | } | 150 | } |
| ... | @@ -149,47 +153,62 @@ pub struct OutStream { | ... | @@ -149,47 +153,62 @@ pub struct OutStream { |
| 149 | // BufferedInStream API goes on top of minimal InStream API. | 153 | // BufferedInStream API goes on top of minimal InStream API. |
| 150 | pub struct InStream { | 154 | pub struct InStream { |
| 151 | fd: i32, | 155 | fd: i32, |
| 152 | offset: usize, | | |
| 153 | | 156 | |
| 154 | /// Call close to clean up. | 157 | /// Call close to clean up. |
| 155 | pub fn open(is: &InStream, path: []const u8) -> %void { | 158 | pub fn open(is: &InStream, path: []const u8) -> %void { |
| 156 | const result = linux.open(path, linux.O_LARGEFILE|linux.O_RDONLY, 0); | 159 | switch (@compileVar("os")) { |
| 157 | const err = linux.getErrno(result); | 160 | linux => { |
| 158 | if (err > 0) { | 161 | while (true) { |
| 159 | return switch (err) { | 162 | const result = linux.open(path, linux.O_LARGEFILE|linux.O_RDONLY, 0); |
| 160 | errno.EFAULT => unreachable{}, | 163 | const err = linux.getErrno(result); |
| 161 | errno.EINVAL => unreachable{}, | 164 | if (err > 0) { |
| 162 | errno.EACCES => error.BadPerm, | 165 | return switch (err) { |
| 163 | errno.EFBIG, errno.EOVERFLOW => error.FileTooBig, | 166 | errno.EINTR => continue, |
| 164 | errno.EINTR => error.SigInterrupt, | 167 | |
| 165 | errno.EISDIR => error.IsDir, | 168 | errno.EFAULT => unreachable{}, |
| 166 | errno.ELOOP => error.SymLinkLoop, | 169 | errno.EINVAL => unreachable{}, |
| 167 | errno.EMFILE => error.ProcessFdQuotaExceeded, | 170 | errno.EACCES => error.BadPerm, |
| 168 | errno.ENAMETOOLONG => error.NameTooLong, | 171 | errno.EFBIG, errno.EOVERFLOW => error.FileTooBig, |
| 169 | errno.ENFILE => error.SystemFdQuotaExceeded, | 172 | errno.EISDIR => error.IsDir, |
| 170 | errno.ENODEV => error.NoDevice, | 173 | errno.ELOOP => error.SymLinkLoop, |
| 171 | errno.ENOENT => error.PathNotFound, | 174 | errno.EMFILE => error.ProcessFdQuotaExceeded, |
| 172 | errno.ENOMEM => error.NoMem, | 175 | errno.ENAMETOOLONG => error.NameTooLong, |
| 173 | errno.ENOSPC => error.NoSpaceLeft, | 176 | errno.ENFILE => error.SystemFdQuotaExceeded, |
| 174 | errno.ENOTDIR => error.NotDir, | 177 | errno.ENODEV => error.NoDevice, |
| 175 | errno.EPERM => error.BadPerm, | 178 | errno.ENOENT => error.PathNotFound, |
| 176 | else => error.Unexpected, | 179 | errno.ENOMEM => error.NoMem, |
| 177 | } | 180 | errno.ENOSPC => error.NoSpaceLeft, |
| | 181 | errno.ENOTDIR => error.NotDir, |
| | 182 | errno.EPERM => error.BadPerm, |
| | 183 | else => error.Unexpected, |
| | 184 | } |
| | 185 | } |
| | 186 | is.fd = i32(result); |
| | 187 | return; |
| | 188 | } |
| | 189 | }, |
| | 190 | else => @compileErr("unsupported OS"), |
| 178 | } | 191 | } |
| 179 | is.fd = i32(result); | | |
| 180 | is.offset = 0; | | |
| 181 | } | 192 | } |
| 182 | | 193 | |
| 183 | pub fn close(is: &InStream) -> %void { | 194 | pub fn close(is: &InStream) -> %void { |
| 184 | const close_ret = linux.close(is.fd); | 195 | switch (@compileVar("os")) { |
| 185 | const close_err = linux.getErrno(close_ret); | 196 | linux => { |
| 186 | if (close_err > 0) { | 197 | while (true) { |
| 187 | return switch (close_err) { | 198 | const close_ret = linux.close(is.fd); |
| 188 | errno.EIO => error.Io, | 199 | const close_err = linux.getErrno(close_ret); |
| 189 | errno.EBADF => error.BadFd, | 200 | if (close_err > 0) { |
| 190 | errno.EINTR => error.SigInterrupt, | 201 | return switch (close_err) { |
| 191 | else => error.Unexpected, | 202 | errno.EINTR => continue, |
| 192 | } | 203 | |
| | 204 | errno.EIO => error.Io, |
| | 205 | errno.EBADF => error.BadFd, |
| | 206 | else => error.Unexpected, |
| | 207 | } |
| | 208 | } |
| | 209 | } |
| | 210 | }, |
| | 211 | else => @compileErr("unsupported OS"), |
| 193 | } | 212 | } |
| 194 | } | 213 | } |
| 195 | | 214 | |
| ... | @@ -198,12 +217,14 @@ pub struct InStream { | ... | @@ -198,12 +217,14 @@ pub struct InStream { |
| 198 | pub fn read(is: &InStream, buf: []u8) -> %usize { | 217 | pub fn read(is: &InStream, buf: []u8) -> %usize { |
| 199 | switch (@compileVar("os")) { | 218 | switch (@compileVar("os")) { |
| 200 | linux => { | 219 | linux => { |
| 201 | while (true) { | 220 | var index: usize = 0; |
| 202 | const amt_read = linux.pread(is.fd, buf.ptr, buf.len, is.offset); | 221 | while (index < buf.len) { |
| | 222 | const amt_read = linux.read(is.fd, &buf[index], buf.len - index); |
| 203 | const read_err = linux.getErrno(amt_read); | 223 | const read_err = linux.getErrno(amt_read); |
| 204 | if (read_err > 0) { | 224 | if (read_err > 0) { |
| 205 | switch (read_err) { | 225 | switch (read_err) { |
| 206 | errno.EINTR => continue, | 226 | errno.EINTR => continue, |
| | 227 | |
| 207 | errno.EINVAL => unreachable{}, | 228 | errno.EINVAL => unreachable{}, |
| 208 | errno.EFAULT => unreachable{}, | 229 | errno.EFAULT => unreachable{}, |
| 209 | errno.EBADF => return error.BadFd, | 230 | errno.EBADF => return error.BadFd, |
| ... | @@ -211,9 +232,10 @@ pub struct InStream { | ... | @@ -211,9 +232,10 @@ pub struct InStream { |
| 211 | else => return error.Unexpected, | 232 | else => return error.Unexpected, |
| 212 | } | 233 | } |
| 213 | } | 234 | } |
| 214 | is.offset += amt_read; | 235 | if (amt_read == 0) return index; |
| 215 | return amt_read; | 236 | index += amt_read; |
| 216 | } | 237 | } |
| | 238 | return index; |
| 217 | }, | 239 | }, |
| 218 | else => @compileErr("unsupported OS"), | 240 | else => @compileErr("unsupported OS"), |
| 219 | } | 241 | } |
| ... | @@ -261,14 +283,67 @@ pub struct InStream { | ... | @@ -261,14 +283,67 @@ pub struct InStream { |
| 261 | } | 283 | } |
| 262 | | 284 | |
| 263 | pub fn seekForward(is: &InStream, amount: usize) -> %void { | 285 | pub fn seekForward(is: &InStream, amount: usize) -> %void { |
| 264 | is.offset += amount; | 286 | switch (@compileVar("os")) { |
| | 287 | linux => { |
| | 288 | const result = linux.lseek(is.fd, amount, linux.SEEK_CUR); |
| | 289 | const err = linux.getErrno(result); |
| | 290 | if (err > 0) { |
| | 291 | return switch (err) { |
| | 292 | errno.EBADF => error.BadFd, |
| | 293 | errno.EINVAL => error.Unseekable, |
| | 294 | errno.EOVERFLOW => error.Unseekable, |
| | 295 | errno.ESPIPE => error.Unseekable, |
| | 296 | errno.ENXIO => error.Unseekable, |
| | 297 | else => error.Unexpected, |
| | 298 | }; |
| | 299 | } |
| | 300 | }, |
| | 301 | else => @compileErr("unsupported OS"), |
| | 302 | } |
| 265 | } | 303 | } |
| 266 | | 304 | |
| 267 | pub fn seekTo(is: &InStream, pos: usize) -> %void { | 305 | pub fn seekTo(is: &InStream, pos: usize) -> %void { |
| 268 | is.offset = pos; | 306 | switch (@compileVar("os")) { |
| | 307 | linux => { |
| | 308 | const result = linux.lseek(is.fd, pos, linux.SEEK_SET); |
| | 309 | const err = linux.getErrno(result); |
| | 310 | if (err > 0) { |
| | 311 | return switch (err) { |
| | 312 | errno.EBADF => error.BadFd, |
| | 313 | errno.EINVAL => error.Unseekable, |
| | 314 | errno.EOVERFLOW => error.Unseekable, |
| | 315 | errno.ESPIPE => error.Unseekable, |
| | 316 | errno.ENXIO => error.Unseekable, |
| | 317 | else => error.Unexpected, |
| | 318 | }; |
| | 319 | } |
| | 320 | }, |
| | 321 | else => @compileErr("unsupported OS"), |
| | 322 | } |
| | 323 | } |
| | 324 | |
| | 325 | pub fn getPos(is: &InStream) -> %usize { |
| | 326 | switch (@compileVar("os")) { |
| | 327 | linux => { |
| | 328 | const result = linux.lseek(is.fd, 0, linux.SEEK_CUR); |
| | 329 | const err = linux.getErrno(result); |
| | 330 | if (err > 0) { |
| | 331 | return switch (err) { |
| | 332 | errno.EBADF => error.BadFd, |
| | 333 | errno.EINVAL => error.Unseekable, |
| | 334 | errno.EOVERFLOW => error.Unseekable, |
| | 335 | errno.ESPIPE => error.Unseekable, |
| | 336 | errno.ENXIO => error.Unseekable, |
| | 337 | else => error.Unexpected, |
| | 338 | }; |
| | 339 | } |
| | 340 | return result; |
| | 341 | }, |
| | 342 | else => @compileErr("unsupported OS"), |
| | 343 | } |
| 269 | } | 344 | } |
| 270 | | 345 | |
| 271 | pub fn endPos(is: &InStream) -> %usize { | 346 | pub fn getEndPos(is: &InStream) -> %usize { |
| 272 | var stat: linux.stat = undefined; | 347 | var stat: linux.stat = undefined; |
| 273 | const err = linux.getErrno(linux.fstat(is.fd, &stat)); | 348 | const err = linux.getErrno(linux.fstat(is.fd, &stat)); |
| 274 | if (err > 0) { | 349 | if (err > 0) { |