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