Skip to content
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
23 changes: 23 additions & 0 deletions R/fread.R
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Comment thread
AmanKashyap0807 marked this conversation as resolved.
Outdated
is_windows = identical(.Platform$OS.type, "windows")
if (is_windows) {
clip = tryCatch(utils::readClipboard(), error = identity)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't normally use namespacing for functions from utils, see e.g. head/tail

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't have blanket utils import so it'll need to be included:

importFrom(utils, capture.output, contrib.url, download.file, flush.console, getS3method, head, packageVersion, tail, untar, unzip)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readClipboard() is documented as Windows‑only in the utils::clipboard help:
https://search.r-project.org/R/refmans/utils/help/clipboard.html
There are also reports on non‑Windows where readClipboard() simply isn’t available even with utils loaded,
(https://stackoverflow.com/questions/24365249/readclipboard-removed-from-utils-package)
so importing it unconditionally in NAMESPACE risks errors when the package is loaded on those platforms. That’s why I kept utils::readClipboard() inside the Windows guard.

Regarding head/tail, data.table::last() has a small fallback that explicitly calls utils::head and utils::tail instead of using them unqualified everywhere, and that pattern was in my mind when I treated utils::readClipboard() as a similar Windows‑specific special case. I’m happy to change this to plain readClipboard() with a NAMESPACE import if you prefer and are confident it behaves well on non‑Windows platforms.

Comment thread
AmanKashyap0807 marked this conversation as resolved.
Outdated
# 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)))) {
Comment thread
AmanKashyap0807 marked this conversation as resolved.
Outdated
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)
})
Comment thread
AmanKashyap0807 marked this conversation as resolved.
Outdated
} else {
# Note: macOS (pbpaste) and Linux (xclip/xsel) support discussed in #1292
stopf("Clipboard reading is supported on Windows only.")
Comment thread
AmanKashyap0807 marked this conversation as resolved.
Outdated
}
} 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)) {
Expand Down
8 changes: 8 additions & 0 deletions inst/tests/tests.Rraw
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid there's no portable way to test this without violating CRAN policy:

Packages should not write in the user’s home filespace (including clipboards)

'https://cran.r-project.org/src/contrib/PACKAGES.in' |> url() |> read.dcf() |> _[,'X-CRAN-Comment'] |> grep(pattern = 'clipb', value = TRUE)
# [1] "Archived on 2019-10-16 for policy violation.\n\nSometimes attempts to write to the clipboard."
# [2] "Archived on 2019-01-06 for policy violation.\n\nwriting to clipboard hung the check run."

Emptying the clipboard afterwards is not enough, as it could previously contain something else, e.g., an image, which we have no way to read and restore later.

test(2366, fread("clipboard-128"), data.table(a = 1L, b = 2L))
})
Loading