summary refs log tree commit diff stats
path: root/lib/std
diff options
context:
space:
mode:
authorRegis Caillaud <35006197+Clonkk@users.noreply.github.com>2020-10-27 08:52:40 +0100
committerGitHub <noreply@github.com>2020-10-27 08:52:40 +0100
commit12143d90c828bb4142751a6511ce790aac8aca51 (patch)
treed90a02034654a81261b9ec2cb43e3247035bc67a /lib/std
parent066f3ebc2ae00af10f477dd9c07b94f1f161030d (diff)
downloadNim-12143d90c828bb4142751a6511ce790aac8aca51.tar.gz
Add C function dup and dup2 posix to system/io (#15675)
* * Add handle to dup and dup2 posix as duplicate and duplicateTo in std/ioutils.
* Added small test & changelog entry

* Fixed import in tioutils
removed when isMainModule

* * Nest test inside block. Rename proc var -> let in captureStdout
* Renamed tmpfile to iotuils.txt
* Added block: # duplicate, duplicateTo
* Improved docstring
* Clean non-idiomatic code
* Added runnable examples

* rm 2 trailing space in expected output

* Made syntax prettier

* Runnable example: file in getTempDir()

* Tmp -> Temp

* Fixed runnableExamples on windows
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/ioutils.nim85
1 files changed, 85 insertions, 0 deletions
diff --git a/lib/std/ioutils.nim b/lib/std/ioutils.nim
new file mode 100644
index 000000000..bef61c54a
--- /dev/null
+++ b/lib/std/ioutils.nim
@@ -0,0 +1,85 @@
+when defined(windows):
+  proc c_dup(oldfd: FileHandle): FileHandle {.
+    importc: "_dup", header: "<io.h>".}
+  proc c_dup2(oldfd: FileHandle, newfd: FileHandle): cint {.
+    importc: "_dup2", header: "<io.h>".}
+else:
+  proc c_dup(oldfd: FileHandle): FileHandle{.
+    importc: "dup", header: "<unistd.h>".}
+  proc c_dup2(oldfd: FileHandle, newfd: FileHandle): cint {.
+    importc: "dup2", header: "<unistd.h>".}
+
+# when false:
+#   const SupportIoctlInheritCtl = (defined(linux) or defined(bsd)) and
+#                                 not defined(nimscript)
+#   when SupportIoctlInheritCtl:
+#     var
+#       FIOCLEX {.importc, header: "<sys/ioctl.h>".}: cint
+#       FIONCLEX {.importc, header: "<sys/ioctl.h>".}: cint
+
+## Also defined in std/posix and system/io
+proc strerror(errnum: cint): cstring {.importc, header: "<string.h>".}
+when not defined(nimscript):
+  var errno {.importc, header: "<errno.h>".}: cint ## error variable
+
+template checkError(ret: cint) =
+  if ret == -1:
+    when not defined(nimscript):
+      raise newException(IOError, $strerror(errno))
+    else:
+      doAssert(false)
+
+proc duplicate*(oldfd: FileHandle): FileHandle =
+  ##[
+  Return a copy of the file handle `oldfd`.
+  After a successful return, both `FileHandle` may be used interchangeably.
+  They refer to the same open file description and share file offset and status flags.
+  Calls POSIX function `dup` on Posix platform and `_dup` on Windows
+  ]##
+  runnableExamples:
+    # stdoutDuplicate is a copy of stdout FileHandle that points to STDOUT
+    let stdoutDuplicate = duplicate(stdout.getFileHandle())
+    # Writing to stdoutDuplicate will write to stdout
+    doAssert(stdoutDuplicate != stdout.getFileHandle())
+    # On windows, opening a file from a FileHandle does not work
+    when not defined(windows):
+      var f : File
+      let res = open(f, stdoutDuplicate, mode=fmWrite)
+      let msg = "This is a test message that will be displayed ! \n"
+      f.write(msg)
+      # Output "Test"
+      f.close()
+
+  result = c_dup(oldfd)
+  checkError(result)
+
+proc duplicateTo*(oldfd: FileHandle, newfd: FileHandle) =
+  ##[
+  Perform the same task a `duplicate` but instead of using the lowest unused file descriptor
+  it uses the FileHandle` specified by `newfd`.
+  Calls POSIX function `dup2` on Posix platform and `_dup2` on Windows.
+  ]##
+  runnableExamples:
+    import os
+    # Redirect stdout to a file temporarily
+    let tmpFileName = getTempDir() / "hidden_output.txt"
+    let stdoutFileno = stdout.getFileHandle()
+    let stdoutDupFd = duplicate(stdoutFileno)
+
+    # Create a new file
+    let tmpFile: File = open(tmpFileName, fmAppend)
+    let tmpFileFd: FileHandle = tmpFile.getFileHandle()
+
+    # stdoutFileno now writes to tmpFile
+    duplicateTo(tmpFileFd, stdoutFileno)
+    echo "This is not displayed, but written to tmpFile instead !"
+
+    # Close file & restore stdout
+    tmpFile.close()
+    duplicateTo(stdoutDupFd, stdoutFileno)
+
+    # stdout is now restored !
+    echo "This is displayed"
+
+  let retValue = c_dup2(oldfd, newfd)
+  checkError(retValue)