154 lines
3.3 KiB
V
154 lines
3.3 KiB
V
module os
|
|
|
|
// low level operations with file descriptors/handles
|
|
|
|
$if !windows {
|
|
#include <sys/select.h>
|
|
#include <sys/ioctl.h>
|
|
}
|
|
|
|
$if windows {
|
|
#include <winsock2.h>
|
|
}
|
|
|
|
#flag windows -lws2_32
|
|
|
|
// fd_close closes the file descriptor. It returns 0 on success.
|
|
pub fn fd_close(fd int) int {
|
|
if fd == -1 {
|
|
return 0
|
|
}
|
|
return C.close(fd)
|
|
}
|
|
|
|
// fd_write writes the given string to the file descriptor.
|
|
// It blocks until all the data in the string is written.
|
|
pub fn fd_write(fd int, s string) {
|
|
if fd == -1 {
|
|
return
|
|
}
|
|
mut sp := s.str
|
|
mut remaining := s.len
|
|
for remaining > 0 {
|
|
written := int(C.write(fd, sp, remaining))
|
|
if written < 0 {
|
|
return
|
|
}
|
|
remaining = remaining - written
|
|
sp = unsafe { voidptr(usize(sp) + usize(written)) }
|
|
}
|
|
}
|
|
|
|
// fd_slurp reads all the remaining data from the file descriptor.
|
|
pub fn fd_slurp(fd int) []string {
|
|
mut res := []string{}
|
|
if fd == -1 {
|
|
return res
|
|
}
|
|
for {
|
|
s, b := fd_read(fd, 4096)
|
|
if b <= 0 {
|
|
break
|
|
}
|
|
res << s
|
|
}
|
|
return res
|
|
}
|
|
|
|
// fd_read reads data from the file descriptor. It returns the read data, and how many bytes were read.
|
|
// On signal interruption (EINTR), the read is retried so callers do not see spurious empty results
|
|
// when a signal (for example a Boehm GC stop-the-world signal in a multi-threaded program) interrupts
|
|
// a blocking read.
|
|
pub fn fd_read(fd int, maxbytes int) (string, int) {
|
|
if fd == -1 {
|
|
return '', 0
|
|
}
|
|
unsafe {
|
|
mut buf := malloc_noscan(maxbytes + 1)
|
|
mut nbytes := 0
|
|
for {
|
|
$if !windows {
|
|
C.errno = 0
|
|
}
|
|
nbytes = int(C.read(fd, buf, maxbytes))
|
|
if nbytes >= 0 {
|
|
break
|
|
}
|
|
$if !windows {
|
|
if C.errno == C.EINTR {
|
|
continue
|
|
}
|
|
}
|
|
break
|
|
}
|
|
if nbytes < 0 {
|
|
free(buf)
|
|
return '', nbytes
|
|
}
|
|
buf[nbytes] = 0
|
|
return tos(buf, nbytes), nbytes
|
|
}
|
|
}
|
|
|
|
@[typedef]
|
|
pub struct C.fd_set {}
|
|
|
|
pub struct C.timeval {
|
|
pub:
|
|
tv_sec u64
|
|
tv_usec u64
|
|
}
|
|
|
|
fn C.select(ndfs i32, readfds &C.fd_set, writefds &C.fd_set, exceptfds &C.fd_set, timeout &C.timeval) i32
|
|
|
|
$if !windows {
|
|
fn C.ioctl(fd i32, request u64, args ...voidptr) i32
|
|
}
|
|
|
|
$if windows {
|
|
fn C._get_osfhandle(fd int) voidptr
|
|
fn C.PeekNamedPipe(hNamedPipe voidptr, lpBuffer voidptr, nBufferSize i32, lpBytesRead voidptr, lpTotalBytesAvail voidptr, lpBytesLeftThisMessage voidptr) bool
|
|
}
|
|
|
|
// These are C macros, but from the V's point of view, can be treated as C functions:
|
|
fn C.FD_ZERO(fdset &C.fd_set)
|
|
fn C.FD_SET(fd i32, fdset &C.fd_set)
|
|
fn C.FD_ISSET(fd i32, fdset &C.fd_set) i32
|
|
|
|
// fd_is_pending returns true, when there is pending data, waiting to be read from file descriptor `fd`.
|
|
// If the file descriptor is closed, or if reading from it, will block (there is no data), fd_is_pending returns false.
|
|
pub fn fd_is_pending(fd int) bool {
|
|
if fd == -1 {
|
|
return false
|
|
}
|
|
mut bytes_avail := int(0)
|
|
$if windows {
|
|
handle := voidptr(C._get_osfhandle(fd))
|
|
if handle != voidptr(-1) {
|
|
if C.PeekNamedPipe(handle, unsafe { nil }, int(0), unsafe { nil },
|
|
voidptr(&bytes_avail), unsafe { nil })
|
|
{
|
|
return bytes_avail > 0
|
|
}
|
|
}
|
|
} $else {
|
|
if C.ioctl(fd, u64(C.FIONREAD), &bytes_avail) == 0 {
|
|
return bytes_avail > 0
|
|
}
|
|
}
|
|
read_set := C.fd_set{}
|
|
C.FD_ZERO(&read_set)
|
|
C.FD_SET(fd, &read_set)
|
|
mut ts := C.timeval{
|
|
tv_sec: 0
|
|
tv_usec: 0
|
|
}
|
|
res := C.select(fd + 1, &read_set, C.NULL, C.NULL, &ts)
|
|
if res > 0 {
|
|
if C.FD_ISSET(fd, &read_set) != 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|