1 2#' Processx connections 3#' 4#' These functions are currently experimental and will change 5#' in the future. Note that processx connections are _not_ 6#' compatible with R's built-in connection system. 7#' 8#' `conn_create_fd()` creates a connection from a file descriptor. 9#' 10#' @param fd Integer scalar, a Unix file descriptor. 11#' @param encoding Encoding of the readable connection when reading. 12#' @param close Whether to close the OS file descriptor when closing 13#' the connection. Sometimes you want to leave it open, and use it again 14#' in a `conn_create_fd` call. 15#' Encoding to re-encode `str` into when writing. 16#' 17#' @rdname processx_connections 18#' @export 19 20conn_create_fd <- function(fd, encoding = "", close = TRUE) { 21 assert_that( 22 is_integerish_scalar(fd), 23 is_string(encoding), 24 is_flag(close)) 25 fd <- as.integer(fd) 26 rethrow_call(c_processx_connection_create_fd, fd, encoding, close) 27} 28 29#' @details 30#' `conn_create_pipepair()` creates a pair of connected connections, the 31#' first one is writeable, the second one is readable. 32#' 33#' @param nonblocking Whether the writeable and the readable ends of 34#' the pipe should be non-blocking connections. 35#' 36#' @rdname processx_connections 37#' @export 38 39conn_create_pipepair <- function(encoding = "", 40 nonblocking = c(TRUE, FALSE)) { 41 assert_that( 42 is_string(encoding), 43 is.logical(nonblocking), length(nonblocking) == 2, 44 !any(is.na(nonblocking))) 45 rethrow_call(c_processx_connection_create_pipepair, encoding, nonblocking) 46} 47 48#' @details 49#' `conn_read_chars()` reads UTF-8 characters from the connections. If the 50#' connection itself is not UTF-8 encoded, it re-encodes it. 51#' 52#' @param con Processx connection object. 53#' @param n Number of characters or lines to read. -1 means all available 54#' characters or lines. 55#' 56#' @rdname processx_connections 57#' @export 58 59conn_read_chars <- function(con, n = -1) 60 UseMethod("conn_read_chars", con) 61 62#' @rdname processx_connections 63#' @export 64 65conn_read_chars.processx_connection <- function(con, n = -1) { 66 processx_conn_read_chars(con, n) 67} 68 69#' @rdname processx_connections 70#' @export 71 72processx_conn_read_chars <- function(con, n = -1) { 73 assert_that(is_connection(con), is_integerish_scalar(n)) 74 rethrow_call(c_processx_connection_read_chars, con, n) 75} 76 77#' @details 78#' `conn_read_lines()` reads lines from a connection. 79#' 80#' @rdname processx_connections 81#' @export 82 83conn_read_lines <- function(con, n = -1) 84 UseMethod("conn_read_lines", con) 85 86#' @rdname processx_connections 87#' @export 88 89conn_read_lines.processx_connection <- function(con, n = -1) { 90 processx_conn_read_lines(con, n) 91} 92 93#' @rdname processx_connections 94#' @export 95 96processx_conn_read_lines <- function(con, n = -1) { 97 assert_that(is_connection(con), is_integerish_scalar(n)) 98 rethrow_call(c_processx_connection_read_lines, con, n) 99} 100 101#' @details 102#' `conn_is_incomplete()` returns `FALSE` if the connection surely has no 103#' more data. 104#' 105#' @rdname processx_connections 106#' @export 107 108conn_is_incomplete <- function(con) 109 UseMethod("conn_is_incomplete", con) 110 111#' @rdname processx_connections 112#' @export 113 114conn_is_incomplete.processx_connection <- function(con) { 115 processx_conn_is_incomplete(con) 116} 117 118#' @rdname processx_connections 119#' @export 120 121processx_conn_is_incomplete <- function(con) { 122 assert_that(is_connection(con)) 123 ! rethrow_call(c_processx_connection_is_eof, con) 124} 125 126#' @details 127#' `conn_write()` writes a character or raw vector to the connection. 128#' It might not be able to write all bytes into the connection, in which 129#' case it returns the leftover bytes in a raw vector. Call `conn_write()` 130#' again with this raw vector. 131#' 132#' @param str Character or raw vector to write. 133#' @param sep Separator to use if `str` is a character vector. Ignored if 134#' `str` is a raw vector. 135#' 136#' @rdname processx_connections 137#' @export 138 139conn_write <- function(con, str, sep = "\n", encoding = "") 140 UseMethod("conn_write", con) 141 142#' @rdname processx_connections 143#' @export 144 145conn_write.processx_connection <- function(con, str, sep = "\n", 146 encoding = "") { 147 processx_conn_write(con, str, sep, encoding) 148} 149 150#' @rdname processx_connections 151#' @export 152 153processx_conn_write <- function(con, str, sep = "\n", encoding = "") { 154 assert_that( 155 is_connection(con), 156 (is.character(str) && all(! is.na(str))) || is.raw(str), 157 is_string(sep), 158 is_string(encoding)) 159 160 if (is.character(str)) { 161 pstr <- paste(str, collapse = sep) 162 str <- iconv(pstr, "", encoding, toRaw = TRUE)[[1]] 163 } 164 invisible(rethrow_call(c_processx_connection_write_bytes, con, str)) 165} 166 167#' @details 168#' `conn_create_file()` creates a connection to a file. 169#' 170#' @param filename File name. 171#' @param read Whether the connection is readable. 172#' @param write Whethe the connection is writeable. 173#' 174#' @rdname processx_connections 175#' @export 176 177conn_create_file <- function(filename, read = NULL, write = NULL) { 178 if (is.null(read) && is.null(write)) { read <- TRUE; write <- FALSE } 179 if (is.null(read)) read <- !write 180 if (is.null(write)) write <- !read 181 182 assert_that( 183 is_string(filename), 184 is_flag(read), 185 is_flag(write), 186 read || write) 187 188 rethrow_call(c_processx_connection_create_file, filename, read, write) 189} 190 191#' @details 192#' `conn_set_stdout()` set the standard output of the R process, to the 193#' specified connection. 194#' 195#' @param drop Whether to close the original stdout/stderr, or keep it 196#' open and return a connection to it. 197#' 198#' @rdname processx_connections 199#' @export 200 201conn_set_stdout <- function(con, drop = TRUE) { 202 assert_that( 203 is_connection(con), 204 is_flag(drop)) 205 206 flush(stdout()) 207 invisible(rethrow_call(c_processx_connection_set_stdout, con, drop)) 208} 209 210#' @details 211#' `conn_set_stderr()` set the standard error of the R process, to the 212#' specified connection. 213#' 214#' @rdname processx_connections 215#' @export 216 217conn_set_stderr <- function(con, drop = TRUE) { 218 assert_that( 219 is_connection(con), 220 is_flag(drop)) 221 222 flush(stderr()) 223 invisible(rethrow_call(c_processx_connection_set_stderr, con, drop)) 224} 225 226#' @details 227#' `conn_get_fileno()` return the integer file desciptor that belongs to 228#' the connection. 229#' 230#' @rdname processx_connections 231#' @export 232 233conn_get_fileno <- function(con) { 234 rethrow_call(c_processx_connection_get_fileno, con) 235} 236 237#' @details 238#' `conn_disable_inheritance()` can be called to disable the inheritance 239#' of all open handles. Call this function as soon as possible in a new 240#' process to avoid inheriting the inherited handles even further. 241#' The function is best effort to close the handles, it might still leave 242#' some handles open. It should work for `stdin`, `stdout` and `stderr`, 243#' at least. 244#' 245#' @rdname processx_connections 246#' @export 247 248conn_disable_inheritance <- function() { 249 rethrow_call(c_processx_connection_disable_inheritance) 250} 251 252#' @rdname processx_connections 253#' @export 254 255close.processx_connection <- function(con, ...) { 256 processx_conn_close(con, ...) 257} 258 259#' @param ... Extra arguments, for compatibility with the `close()` 260#' generic, currently ignored by processx. 261#' @rdname processx_connections 262#' @export 263 264processx_conn_close <- function(con, ...) { 265 rethrow_call(c_processx_connection_close, con) 266} 267 268#' @details 269#' `is_valid_fd()` returns `TRUE` if `fd` is a valid open file 270#' descriptor. You can use it to check if the R process has standard 271#' input, output or error. E.g. R processes running in GUI (like RGui) 272#' might not have any of the standard streams available. 273#' 274#' If a stream is redirected to the null device (e.g. in a callr 275#' subprocess), that is is still a valid file descriptor. 276#' 277#' @rdname processx_connections 278#' @export 279#' @examples 280#' is_valid_fd(0L) # stdin 281#' is_valid_fd(1L) # stdout 282#' is_valid_fd(2L) # stderr 283 284is_valid_fd <- function(fd) { 285 assert_that(is_integerish_scalar(fd)) 286 fd <- as.integer(fd) 287 rethrow_call(c_processx_is_valid_fd, fd) 288} 289