diff --git a/stew/io2.nim b/stew/io2.nim index edf2940..ccc222b 100644 --- a/stew/io2.nim +++ b/stew/io2.nim @@ -183,6 +183,8 @@ when defined(windows): lpNewFilePointer: ptr int64, dwMoveMethod: uint32): int32 {. importc: "SetFilePointerEx", dynlib: "kernel32", stdcall, sideEffect.} + proc setEndOfFile(hFile: uint): int32 {. + importc: "SetEndOfFile", dynlib: "kernel32", stdcall, sideEffect.} proc lockFileEx(hFile: uint, dwFlags, dwReserved: uint32, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh: uint32, lpOverlapped: pointer): uint32 {. @@ -1341,6 +1343,45 @@ proc setFilePos*(handle: IoHandle, offset: int64, else: ok() +proc truncate*(handle: IoHandle, length: int64): IoResult[void] = + ## Procedure cause the regular file referenced by handle ``handle`` to be + ## truncated to a size of precisely ``length`` bytes. + when defined(windows): + let res1 = setFilePointerEx(uint(handle), length, nil, FILE_BEGIN) + if res1 == 0: + return err(ioLastError()) + let res2 = setEndOfFile(uint(handle)) + if res2 == 0: + return err(ioLastError()) + ok() + else: + let res = posix.ftruncate(cint(handle), Off(length)) + if res == -1: + err(ioLastError()) + else: + ok() + +proc truncate*(pathName: string, length: int64): IoResult[void] = + ## Procedure cause the regular file referenced by path ``pathName`` to be + ## truncated to a size of precisely ``length`` bytes. + when defined(windows): + let + flags = {OpenFlags.Write} + handle = openFile(pathName, flags).valueOr: + return err(error) + res = truncate(handle, length) + if res.isErr(): + discard closeFile(handle) + return err(res.error) + ? closeFile(handle) + ok() + else: + let res = posix.truncate(pathName, Off(length)) + if res == -1: + err(ioLastError()) + else: + ok() + proc checkFileSize*(value: int64): IoResult[int] = ## Checks if ``value`` fits into supported by Nim string/sequence indexing ## mechanism. diff --git a/tests/test_io2.nim b/tests/test_io2.nim index 29bad78..59a93fc 100644 --- a/tests/test_io2.nim +++ b/tests/test_io2.nim @@ -792,3 +792,54 @@ suite "OS Input/Output procedures test suite": check: res.isOk() len(res.get()) > 0 + + test "truncate(IoHandle) test": + let flags = {OpenFlags.Create, OpenFlags.Write} + block: + let fdres = openFile("testtruncate1", flags) + check: + fdres.isOk() + truncate(fdres.get(), 100).isOk() + closeFile(fdres.get()).isOk() + + block: + let res = getFileSize("testtruncate1") + check: + res.isOk() + res.get() == 100 + + block: + let fdres = openFile("testtruncate1", flags) + check: + fdres.isOk() + truncate(fdres.get(), 0).isOk() + closeFile(fdres.get()).isOk() + + block: + let res = getFileSize("testtruncate1") + check: + res.isOk() + res.get() == 0 + + check removeFile("testtruncate1").isOk() + + test "truncate(path) test": + check: + writeFile("testtruncate2", "TEST", 0o644).isOk() + truncate("testtruncate2", 200).isOk() + + block: + let res = getFileSize("testtruncate2") + check: + res.isOk() + res.get() == 200 + + check truncate("testtruncate2", 0).isOk() + + block: + let res = getFileSize("testtruncate2") + check: + res.isOk() + res.get() == 0 + + check removeFile("testtruncate2").isOk()