From 546de995beb7c95a07daaccd2e60f75087a6a144 Mon Sep 17 00:00:00 2001 From: AmanKashyap0807 Date: Tue, 10 Feb 2026 03:20:17 +0530 Subject: [PATCH 1/6] Fix fread clipboard handling on Windows (fixes #1292) --- NEWS.md | 2 ++ R/fread.R | 23 +++++++++++++++++++++++ inst/tests/tests.Rraw | 8 ++++++++ 3 files changed, 33 insertions(+) diff --git a/NEWS.md b/NEWS.md index fd0ee8bf1..774527c71 100644 --- a/NEWS.md +++ b/NEWS.md @@ -38,6 +38,8 @@ 4. `rowwiseDT()` now provides a helpful error message when a complex object that is not a list (e.g., a function) is provided as a cell value, instructing the user to wrap it in `list()`, [#7219](https://github.com/Rdatatable/data.table/issues/7219). Thanks @kylebutts for the report and @venom1204 for the fix. +5. `fread()` now recognizes Windows clipboard tokens such as `clipboard`, avoiding shell execution failures, [#1292](https://github.com/Rdatatable/data.table/issues/1292). Thanks @mbacou for the report and @AmanKashyap0807 for the fix. + ### Notes 1. {data.table} now depends on R 3.5.0 (2018). diff --git a/R/fread.R b/R/fread.R index bc8509c71..f5a7ae5c4 100644 --- a/R/fread.R +++ b/R/fread.R @@ -63,6 +63,29 @@ yaml=FALSE, tmpdir=tempdir(), tz="UTC") # input is data itself containing at least one \n or \r } else if (startsWith(input, " ")) { stopf("input= contains no \\n or \\r, but starts with a space. Please remove the leading space, or use text=, file= or cmd=") + } else if (grepl("^clipboard(-[0-9]+)?$", tolower(input))) { + is_windows = identical(.Platform$OS.type, "windows") + if (is_windows) { + clip = tryCatch(utils::readClipboard(), error = identity) + # for errors due to permissions, clipboard locked or system errors + if (inherits(clip, "error")) { + stopf("Reading clipboard failed on Windows: %s", conditionMessage(clip)) + } + if (!length(clip) || all(!nzchar(trimws(clip)))) { + stopf("Clipboard is empty.") + } + tmpFile = tempfile(tmpdir=tmpdir) + on.exit(unlink(tmpFile), add=TRUE) + tryCatch({ + writeLines(paste(clip, collapse="\n"), tmpFile, useBytes=TRUE) + file = tmpFile + }, error = function(e) { + stopf("Writing clipboard to temporary file failed: %s. Check tmpdir=%s.", conditionMessage(e), tmpdir) + }) + } else { + # Note: macOS (pbpaste) and Linux (xclip/xsel) support discussed in #1292 + stopf("Clipboard reading is supported on Windows only.") + } } else if (length(grep(' ', input, fixed=TRUE)) && !file.exists(gsub("^file://", "", input))) { # file name or path containing spaces is not a command. file.exists() doesn't understand file:// (#7550) cmd = input if (input_has_vars && getOption("datatable.fread.input.cmd.message", TRUE)) { diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index f30467dae..18c72e155 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21515,3 +21515,11 @@ test(2365.1, melt(df_melt, id.vars=1:2), melt(dt_melt, id.vars=1:2)) df_dcast = data.frame(a = c("x", "y"), b = 1:2, v = 3:4) dt_dcast = data.table(a = c("x", "y"), b = 1:2, v = 3:4) test(2365.2, dcast(df_dcast, a ~ b, value.var = "v"), dcast(dt_dcast, a ~ b, value.var = "v")) + +# Test fread clipboard input on Windows (issue #1292) +if (.Platform$OS.type == "windows") local({ + temp <- c("a\tb", "1\t2") + utils::writeClipboard(temp) + on.exit(utils::writeClipboard(""), add = TRUE) + test(2366, fread("clipboard-128"), data.table(a = 1L, b = 2L)) +}) \ No newline at end of file From c68136d0f3b9e745305cb479d6ba5bf0c94126b9 Mon Sep 17 00:00:00 2001 From: AmanKashyap0807 Date: Tue, 10 Feb 2026 04:21:45 +0530 Subject: [PATCH 2/6] addressing linting --- R/fread.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/fread.R b/R/fread.R index f5a7ae5c4..fe40a240c 100644 --- a/R/fread.R +++ b/R/fread.R @@ -67,11 +67,11 @@ yaml=FALSE, tmpdir=tempdir(), tz="UTC") is_windows = identical(.Platform$OS.type, "windows") if (is_windows) { clip = tryCatch(utils::readClipboard(), error = identity) - # for errors due to permissions, clipboard locked or system errors + # for errors due to permissions, clipboard locked or system errors if (inherits(clip, "error")) { stopf("Reading clipboard failed on Windows: %s", conditionMessage(clip)) } - if (!length(clip) || all(!nzchar(trimws(clip)))) { + if (!length(clip) || !all(nzchar(trimws(clip)))) { stopf("Clipboard is empty.") } tmpFile = tempfile(tmpdir=tmpdir) From 43b8a2288594d442d4fe436569fdfc8284a5cbd8 Mon Sep 17 00:00:00 2001 From: AmanKashyap0807 Date: Tue, 17 Feb 2026 09:01:46 +0530 Subject: [PATCH 3/6] Tweak fread clipboard checks and error handling on Windows --- R/fread.R | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/R/fread.R b/R/fread.R index fe40a240c..9ebc20f5e 100644 --- a/R/fread.R +++ b/R/fread.R @@ -66,22 +66,14 @@ yaml=FALSE, tmpdir=tempdir(), tz="UTC") } else if (grepl("^clipboard(-[0-9]+)?$", tolower(input))) { is_windows = identical(.Platform$OS.type, "windows") if (is_windows) { - clip = tryCatch(utils::readClipboard(), error = identity) # for errors due to permissions, clipboard locked or system errors - if (inherits(clip, "error")) { - stopf("Reading clipboard failed on Windows: %s", conditionMessage(clip)) - } - if (!length(clip) || !all(nzchar(trimws(clip)))) { + clip = tryCatch(utils::readClipboard(), + error = function(e) stopf("Reading clipboard failed on Windows: %s", conditionMessage(e)) + ) + if (!length(clip) || !any(nzchar(trimws(clip)))) { stopf("Clipboard is empty.") } - tmpFile = tempfile(tmpdir=tmpdir) - on.exit(unlink(tmpFile), add=TRUE) - tryCatch({ - writeLines(paste(clip, collapse="\n"), tmpFile, useBytes=TRUE) - file = tmpFile - }, error = function(e) { - stopf("Writing clipboard to temporary file failed: %s. Check tmpdir=%s.", conditionMessage(e), tmpdir) - }) + input = paste(clip, collapse="\n") } else { # Note: macOS (pbpaste) and Linux (xclip/xsel) support discussed in #1292 stopf("Clipboard reading is supported on Windows only.") From b5a2a7cd7c194f7231a5fb131d82f02e7172b0ad Mon Sep 17 00:00:00 2001 From: AmanKashyap0807 Date: Thu, 19 Feb 2026 15:29:47 +0530 Subject: [PATCH 4/6] Add Linux (X11/Wayland) and macOS clipboard support to fread --- NEWS.md | 2 +- R/fread.R | 35 +++++++++++++++++++++++++++-------- inst/tests/tests.Rraw | 41 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index 774527c71..6bae66d6a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -38,7 +38,7 @@ 4. `rowwiseDT()` now provides a helpful error message when a complex object that is not a list (e.g., a function) is provided as a cell value, instructing the user to wrap it in `list()`, [#7219](https://github.com/Rdatatable/data.table/issues/7219). Thanks @kylebutts for the report and @venom1204 for the fix. -5. `fread()` now recognizes Windows clipboard tokens such as `clipboard`, avoiding shell execution failures, [#1292](https://github.com/Rdatatable/data.table/issues/1292). Thanks @mbacou for the report and @AmanKashyap0807 for the fix. +5. `fread("clipboard")` now works on Linux (via `xclip`, `xsel`, or `wl-paste`), macOS (via `pbpaste`), and Windows, [#1292](https://github.com/Rdatatable/data.table/issues/1292). Thanks @mbacou for the report, @ben-schwen for the suggestion, and @AmanKashyap0807 for the fix. ### Notes diff --git a/R/fread.R b/R/fread.R index 9ebc20f5e..6b18aa24a 100644 --- a/R/fread.R +++ b/R/fread.R @@ -64,20 +64,39 @@ yaml=FALSE, tmpdir=tempdir(), tz="UTC") } else if (startsWith(input, " ")) { stopf("input= contains no \\n or \\r, but starts with a space. Please remove the leading space, or use text=, file= or cmd=") } else if (grepl("^clipboard(-[0-9]+)?$", tolower(input))) { - is_windows = identical(.Platform$OS.type, "windows") - if (is_windows) { - # for errors due to permissions, clipboard locked or system errors + os_type = .Platform$OS.type + if (identical(os_type, "windows")) { clip = tryCatch(utils::readClipboard(), error = function(e) stopf("Reading clipboard failed on Windows: %s", conditionMessage(e)) ) - if (!length(clip) || !any(nzchar(trimws(clip)))) { - stopf("Clipboard is empty.") + } else if (identical(os_type, "unix")) { + sysname = Sys.info()[["sysname"]] + clip_cmd = if (identical(sysname, "Darwin")) { + "pbpaste" + } else if (identical(sysname, "Linux")) { + has_wl = nzchar(Sys.which("wl-paste")) + if (nzchar(Sys.getenv("WAYLAND_DISPLAY")) && has_wl) "wl-paste --no-newline" + else if (nzchar(Sys.which("xclip"))) "xclip -o -selection clipboard" + else if (nzchar(Sys.which("xsel"))) "xsel --clipboard --output" + else if (has_wl) "wl-paste --no-newline" + else stopf("Clipboard reading on Linux requires 'xclip', 'xsel', or 'wl-paste' to be installed and on PATH.") + } else { + stopf("Clipboard reading is not supported on this platform.") + } + clip = tryCatch(system(clip_cmd, intern = TRUE), + error = function(e) stopf("Reading clipboard failed: %s", conditionMessage(e)) + ) + status = attr(clip, "status") + if (!is.null(status) && status != 0L) { + stopf("Reading clipboard failed (exit %d). Ensure '%s' is working.", status, clip_cmd) } - input = paste(clip, collapse="\n") } else { - # Note: macOS (pbpaste) and Linux (xclip/xsel) support discussed in #1292 - stopf("Clipboard reading is supported on Windows only.") + stopf("Clipboard reading is not supported on this platform.") + } + if (!length(clip) || !any(nzchar(trimws(clip)))) { + stopf("Clipboard is empty.") } + input = paste(clip, collapse = "\n") } else if (length(grep(' ', input, fixed=TRUE)) && !file.exists(gsub("^file://", "", input))) { # file name or path containing spaces is not a command. file.exists() doesn't understand file:// (#7550) cmd = input if (input_has_vars && getOption("datatable.fread.input.cmd.message", TRUE)) { diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 18c72e155..00bbe5547 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21518,8 +21518,47 @@ test(2365.2, dcast(df_dcast, a ~ b, value.var = "v"), dcast(dt_dcast, a ~ b, val # Test fread clipboard input on Windows (issue #1292) if (.Platform$OS.type == "windows") local({ - temp <- c("a\tb", "1\t2") + temp = c("a\tb", "1\t2") utils::writeClipboard(temp) on.exit(utils::writeClipboard(""), add = TRUE) test(2366, fread("clipboard-128"), data.table(a = 1L, b = 2L)) +}) + +# Test fread clipboard input on macOS (issue #1292) +if (.Platform$OS.type == "unix" && identical(Sys.info()[["sysname"]], "Darwin")) local({ + if (nzchar(Sys.which("pbcopy"))) { + con = pipe("pbcopy", "w") + writeLines(c("a\tb", "1\t2"), con) + close(con) + on.exit({ + cl = pipe("pbcopy", "w") + writeLines("", cl) + close(cl) + }, add=TRUE) + test(2366.1, fread("clipboard"), data.table(a=1L, b=2L)) + } +}) + +# Test fread clipboard input on Linux via xclip/xsel/wl-paste (issue #1292) +if (.Platform$OS.type == "unix" && identical(Sys.info()[["sysname"]], "Linux")) local({ + on_wayland = nzchar(Sys.getenv("WAYLAND_DISPLAY")) + has_wl = nzchar(Sys.which("wl-copy")) && nzchar(Sys.which("wl-paste")) + has_xclip = nzchar(Sys.which("xclip")) + has_xsel = nzchar(Sys.which("xsel")) + writer = if (on_wayland && has_wl) "wl-copy" + else if (has_xclip) "xclip -i -selection clipboard" + else if (has_xsel) "xsel --clipboard --input" + else if (has_wl) "wl-copy" + else "" + if (nzchar(writer)) { + con = pipe(writer, "w") + writeLines(c("a\tb", "1\t2"), con) + close(con) + on.exit({ + cl = pipe(writer, "w") + writeLines("", cl) + close(cl) + }, add=TRUE) + test(2366.2, fread("clipboard"), data.table(a=1L, b=2L)) + } }) \ No newline at end of file From 6c34cbaa02dde8c0062926db61cc132a3e27b8ee Mon Sep 17 00:00:00 2001 From: AmanKashyap0807 Date: Thu, 19 Feb 2026 16:21:39 +0530 Subject: [PATCH 5/6] correct wl-paste redundancy and replace stopf with warning for non supported device --- R/fread.R | 8 ++------ inst/tests/tests.Rraw | 4 +--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/R/fread.R b/R/fread.R index 6b18aa24a..99c003b1e 100644 --- a/R/fread.R +++ b/R/fread.R @@ -74,14 +74,10 @@ yaml=FALSE, tmpdir=tempdir(), tz="UTC") clip_cmd = if (identical(sysname, "Darwin")) { "pbpaste" } else if (identical(sysname, "Linux")) { - has_wl = nzchar(Sys.which("wl-paste")) - if (nzchar(Sys.getenv("WAYLAND_DISPLAY")) && has_wl) "wl-paste --no-newline" + if (nzchar(Sys.which("wl-paste"))) "wl-paste --no-newline" else if (nzchar(Sys.which("xclip"))) "xclip -o -selection clipboard" else if (nzchar(Sys.which("xsel"))) "xsel --clipboard --output" - else if (has_wl) "wl-paste --no-newline" else stopf("Clipboard reading on Linux requires 'xclip', 'xsel', or 'wl-paste' to be installed and on PATH.") - } else { - stopf("Clipboard reading is not supported on this platform.") } clip = tryCatch(system(clip_cmd, intern = TRUE), error = function(e) stopf("Reading clipboard failed: %s", conditionMessage(e)) @@ -91,7 +87,7 @@ yaml=FALSE, tmpdir=tempdir(), tz="UTC") stopf("Reading clipboard failed (exit %d). Ensure '%s' is working.", status, clip_cmd) } } else { - stopf("Clipboard reading is not supported on this platform.") + warning("Clipboard reading is not supported on this platform.") } if (!length(clip) || !any(nzchar(trimws(clip)))) { stopf("Clipboard is empty.") diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 0240f93c8..41236886e 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21546,14 +21546,12 @@ if (.Platform$OS.type == "unix" && identical(Sys.info()[["sysname"]], "Darwin")) # Test fread clipboard input on Linux via xclip/xsel/wl-paste (issue #1292) if (.Platform$OS.type == "unix" && identical(Sys.info()[["sysname"]], "Linux")) local({ - on_wayland = nzchar(Sys.getenv("WAYLAND_DISPLAY")) has_wl = nzchar(Sys.which("wl-copy")) && nzchar(Sys.which("wl-paste")) has_xclip = nzchar(Sys.which("xclip")) has_xsel = nzchar(Sys.which("xsel")) - writer = if (on_wayland && has_wl) "wl-copy" + writer = if (has_wl) "wl-copy" else if (has_xclip) "xclip -i -selection clipboard" else if (has_xsel) "xsel --clipboard --input" - else if (has_wl) "wl-copy" else "" if (nzchar(writer)) { con = pipe(writer, "w") From b95ca4c3a98916d86c399689491a03e782eb9988 Mon Sep 17 00:00:00 2001 From: AmanKashyap0807 Date: Thu, 19 Feb 2026 16:32:40 +0530 Subject: [PATCH 6/6] fix linter warning of CI workflow --- R/fread.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/fread.R b/R/fread.R index 99c003b1e..a6e73bbb8 100644 --- a/R/fread.R +++ b/R/fread.R @@ -87,7 +87,7 @@ yaml=FALSE, tmpdir=tempdir(), tz="UTC") stopf("Reading clipboard failed (exit %d). Ensure '%s' is working.", status, clip_cmd) } } else { - warning("Clipboard reading is not supported on this platform.") + warning("Clipboard reading is not supported on this platform.", call. = FALSE) } if (!length(clip) || !any(nzchar(trimws(clip)))) { stopf("Clipboard is empty.")