win64 is a supported target; bugfix: nimrod c -r on windows; stdlib uses wide char versions of the WinAPI

This commit is contained in:
Araq
2012-03-04 21:44:56 +01:00
parent 34d3c042af
commit ff4a69b624
22 changed files with 927 additions and 195 deletions

View File

@@ -57,15 +57,23 @@ proc open*(filename: string, mode: TFileMode = fmRead,
OSError()
# return false
#raise newException(EIO, msg)
result.fHandle = CreateFileA(
filename,
if readonly: GENERIC_READ else: GENERIC_ALL,
FILE_SHARE_READ,
nil,
if newFileSize != -1: CREATE_ALWAYS else: OPEN_EXISTING,
if readonly: FILE_ATTRIBUTE_READONLY else: FILE_ATTRIBUTE_TEMPORARY,
0)
template callCreateFile(winApiProc, filename: expr): expr =
winApiProc(
filename,
if readonly: GENERIC_READ else: GENERIC_ALL,
FILE_SHARE_READ,
nil,
if newFileSize != -1: CREATE_ALWAYS else: OPEN_EXISTING,
if readonly: FILE_ATTRIBUTE_READONLY else: FILE_ATTRIBUTE_TEMPORARY,
0)
when useWinUnicode:
var f = allocWideCString(filename)
result.fHandle = callCreateFile(CreateFileW, f)
dealloc f
else:
result.fHandle = callCreateFile(CreateFileA, filename)
if result.fHandle == INVALID_HANDLE_VALUE:
fail "error opening file"
@@ -81,7 +89,9 @@ proc open*(filename: string, mode: TFileMode = fmRead,
(SetEndOfFile(result.fHandle) == 0):
fail "error setting file size"
result.mapHandle = CreateFileMapping(
# since the strings are always 'nil', we simply always call
# CreateFileMappingW which should be slightly faster anyway:
result.mapHandle = CreateFileMappingW(
result.fHandle, nil,
if readonly: PAGE_READONLY else: PAGE_READWRITE,
0, 0, nil)

View File

@@ -168,13 +168,18 @@ proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} =
when defined(Windows):
var err = GetLastError()
if err != 0'i32:
var msgbuf: cstring
if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200,
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
var m = $msgbuf
if msgbuf != nil:
LocalFree(msgbuf)
result = m
when useWinUnicode:
var msgbuf: widecstring
if FormatMessageW(0x00000100 or 0x00001000 or 0x00000200,
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
result = $msgbuf
if msgbuf != nil: LocalFree(msgbuf)
else:
var msgbuf: cstring
if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200,
nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
result = $msgbuf
if msgbuf != nil: LocalFree(msgbuf)
if errno != 0'i32:
result = $os.strerror(errno)
@@ -236,10 +241,47 @@ proc UnixToNativePath*(path: string): string {.
add result, path[i]
inc(i)
when defined(windows):
template wrapUnary(varname, winApiProc, arg: expr) =
var tmp = allocWideCString(arg)
var varname = winApiProc(tmp)
dealloc tmp
template wrapBinary(varname, winApiProc, arg, arg2: expr) =
var tmp2 = allocWideCString(arg)
var varname = winApiProc(tmp2, arg2)
dealloc tmp2
when useWinUnicode:
proc FindFirstFile(a: string, b: var TWIN32_FIND_DATA): THandle =
var aa = allocWideCString(a)
result = FindFirstFileW(aa, b)
dealloc aa
template FindNextFile(a, b: expr): expr = FindNextFileW(a, b)
template getCommandLine(): expr = getCommandLineW()
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
result = f.cFilename[0].int == ord('.')
template getFilename(f: expr): expr =
$cast[WideCString](addr(f.cFilename[0]))
else:
template FindFirstFile(a, b: expr): expr = FindFirstFileA(a, b)
template FindNextFile(a, b: expr): expr = FindNextFileA(a, b)
template getCommandLine(): expr = getCommandLineA()
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
result = f.cFilename[0] == '.'
template getFilename(f: expr): expr = $f.cFilename
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1".} =
## Returns true if the file exists, false otherwise.
when defined(windows):
var a = GetFileAttributesA(filename)
when useWinUnicode:
wrapUnary(a, GetFileAttributesW, filename)
else:
var a = GetFileAttributesA(filename)
if a != -1'i32:
result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
else:
@@ -250,7 +292,10 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1".} =
## Returns true iff the directory `dir` exists. If `dir` is a file, false
## is returned.
when defined(windows):
var a = GetFileAttributesA(dir)
when useWinUnicode:
wrapUnary(a, GetFileAttributesW, dir)
else:
var a = GetFileAttributesA(dir)
if a != -1'i32:
result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
else:
@@ -265,7 +310,7 @@ proc getLastModificationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
return res.st_mtime
else:
var f: TWIN32_Find_Data
var h = findfirstFileA(file, f)
var h = findfirstFile(file, f)
if h == -1'i32: OSError()
result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
findclose(h)
@@ -278,7 +323,7 @@ proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} =
return res.st_atime
else:
var f: TWIN32_Find_Data
var h = findfirstFileA(file, f)
var h = findfirstFile(file, f)
if h == -1'i32: OSError()
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
findclose(h)
@@ -291,7 +336,7 @@ proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
return res.st_ctime
else:
var f: TWIN32_Find_Data
var h = findfirstFileA(file, f)
var h = findfirstFile(file, f)
if h == -1'i32: OSError()
result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
findclose(h)
@@ -304,12 +349,20 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
proc getCurrentDir*(): string {.rtl, extern: "nos$1".} =
## Returns the `current working directory`:idx:.
const bufsize = 512 # should be enough
result = newString(bufsize)
when defined(windows):
var L = GetCurrentDirectoryA(bufsize, result)
if L == 0'i32: OSError()
setLen(result, L)
when useWinUnicode:
var res = cast[wideCString](alloc0(bufsize+1))
var L = GetCurrentDirectoryW(bufsize, res)
result = res$L
dealloc res
if L == 0'i32: OSError()
else:
result = newString(bufsize)
var L = GetCurrentDirectoryA(bufsize, result)
if L == 0'i32: OSError()
setLen(result, L)
else:
result = newString(bufsize)
if getcwd(result, bufsize) != nil:
setlen(result, c_strlen(result))
else:
@@ -319,7 +372,13 @@ proc setCurrentDir*(newDir: string) {.inline.} =
## Sets the `current working directory`:idx:; `EOS` is raised if
## `newDir` cannot been set.
when defined(Windows):
if SetCurrentDirectoryA(newDir) == 0'i32: OSError()
when useWinUnicode:
var x = allocWideCString(newDir)
let res = SetCurrentDirectoryW(x)
dealloc x
if res == 0'i32: OSError()
else:
if SetCurrentDirectoryA(newDir) == 0'i32: OSError()
else:
if chdir(newDir) != 0'i32: OSError()
@@ -377,8 +436,7 @@ proc SplitPath*(path: string): tuple[head, tail: string] {.
## SplitPath("bin") -> ("", "bin")
## SplitPath("/bin") -> ("", "bin")
## SplitPath("") -> ("", "")
var
sepPos = -1
var sepPos = -1
for i in countdown(len(path)-1, 0):
if path[i] in {dirsep, altsep}:
sepPos = i
@@ -504,11 +562,24 @@ proc extractFilename*(path: string): string {.
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1".} =
## Returns the full path of `filename`, raises EOS in case of an error.
when defined(windows):
var unused: cstring
result = newString(3072)
var L = GetFullPathNameA(filename, 3072'i32, result, unused)
if L <= 0'i32 or L >= 3072'i32: OSError()
setLen(result, L)
const bufsize = 3072'i32
when useWinUnicode:
var unused: widecstring
var res = cast[widecstring](alloc(bufsize*2+2))
var f = allocWideCString(filename)
var L = GetFullPathNameW(f, bufsize, res, unused)
dealloc f
if L <= 0'i32 or L >= bufsize:
dealloc res
OSError()
result = res$L
dealloc res
else:
var unused: cstring
result = newString(bufsize)
var L = GetFullPathNameA(filename, bufsize, result, unused)
if L <= 0'i32 or L >= bufsize: OSError()
setLen(result, L)
elif defined(macosx):
# On Mac OS X 10.5, realpath does not allocate the buffer on its own
var pathBuffer: cstring = newString(pathMax)
@@ -585,14 +656,26 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
## sym-linked paths to the same file or directory.
when defined(Windows):
var success = true
template OpenHandle(path: expr): expr =
CreateFileA(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
FILE_SHARE_WRITE, nil, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0)
var f1 = OpenHandle(path1)
var f2 = OpenHandle(path2)
when useWinUnicode:
var p1 = allocWideCString(path1)
var p2 = allocWideCString(path2)
template OpenHandle(path: expr): expr =
CreateFileW(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
FILE_SHARE_WRITE, nil, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0)
var f1 = OpenHandle(p1)
var f2 = OpenHandle(p2)
else:
template OpenHandle(path: expr): expr =
CreateFileA(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
FILE_SHARE_WRITE, nil, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0)
var f1 = OpenHandle(path1)
var f2 = OpenHandle(path2)
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
var fi1, fi2: TBY_HANDLE_FILE_INFORMATION
@@ -607,13 +690,13 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
discard CloseHandle(f1)
discard CloseHandle(f2)
when useWinUnicode:
dealloc p1
dealloc p2
if not success:
OSError()
if not success: OSError()
else:
var
a, b: TStat
var a, b: TStat
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
OSError()
else:
@@ -653,7 +736,15 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1".} =
## Copies a file from `source` to `dest`. If this fails,
## `EOS` is raised.
when defined(Windows):
if CopyFileA(source, dest, 0'i32) == 0'i32: OSError()
when useWinUnicode:
var s = allocWideCString(source)
var d = allocWideCString(dest)
let res = CopyFileW(s, d, 0'i32)
dealloc s
dealloc d
if res == 0'i32: OSError()
else:
if CopyFileA(source, dest, 0'i32) == 0'i32: OSError()
else:
# generic version of copyFile which works for any platform:
const bufSize = 8000 # better for memory manager
@@ -710,23 +801,39 @@ var
when defined(windows):
# because we support Windows GUI applications, things get really
# messy here...
proc strEnd(cstr: CString, c = 0'i32): CString {.
importc: "strchr", header: "<string.h>".}
when useWinUnicode:
proc strEnd(cstr: wideCString, c = 0'i32): wideCString {.
importc: "wcschr", header: "<string.h>".}
else:
proc strEnd(cstr: CString, c = 0'i32): CString {.
importc: "strchr", header: "<string.h>".}
proc getEnvVarsC() =
if not envComputed:
environment = @[]
var
env = getEnvironmentStringsA()
e = env
if e == nil: return # an error occured
while True:
var eend = strEnd(e)
add(environment, $e)
e = cast[CString](cast[TAddress](eend)+1)
if eend[1] == '\0': break
when useWinUnicode:
var
env = getEnvironmentStringsW()
e = env
if e == nil: return # an error occured
while True:
var eend = strEnd(e)
add(environment, $e)
e = cast[wideCString](cast[TAddress](eend)+2)
if eend[1].int == 0: break
discard FreeEnvironmentStringsW(env)
else:
var
env = getEnvironmentStringsA()
e = env
if e == nil: return # an error occured
while True:
var eend = strEnd(e)
add(environment, $e)
e = cast[CString](cast[TAddress](eend)+1)
if eend[1] == '\0': break
discard FreeEnvironmentStringsA(env)
envComputed = true
discard FreeEnvironmentStringsA(env)
else:
const
@@ -804,8 +911,15 @@ proc putEnv*(key, val: string) =
if cputenv(environment[indx]) != 0'i32:
OSError()
else:
if SetEnvironmentVariableA(key, val) == 0'i32:
OSError()
when useWinUnicode:
var k = allocWideCString(key)
var v = allocWideCString(val)
let res = SetEnvironmentVariableW(k, v)
dealloc k
dealloc v
if res == 0'i32: OSError()
else:
if SetEnvironmentVariableA(key, val) == 0'i32: OSError()
iterator envPairs*(): tuple[key, value: TaintedString] =
## Iterate over all `environments variables`:idx:. In the first component
@@ -821,18 +935,18 @@ iterator walkFiles*(pattern: string): string =
## Iterate over all the files that match the `pattern`. On POSIX this uses
## the `glob`:idx: call.
##
## `pattern` is OS dependant, but at least the "\*.ext"
## `pattern` is OS dependent, but at least the "\*.ext"
## notation is supported.
when defined(windows):
var
f: TWin32FindData
res: int
res = findfirstFileA(pattern, f)
res = findfirstFile(pattern, f)
if res != -1:
while true:
if f.cFileName[0] != '.':
yield splitFile(pattern).dir / extractFilename($f.cFileName)
if findnextFileA(res, f) == 0'i32: break
if not skipFindData(f):
yield splitFile(pattern).dir / extractFilename(getFilename(f))
if findnextFile(res, f) == 0'i32: break
findclose(res)
else: # here we use glob
var
@@ -878,15 +992,15 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
## dirA/fileA2.txt
when defined(windows):
var f: TWIN32_Find_Data
var h = findfirstFileA(dir / "*", f)
var h = findfirstFile(dir / "*", f)
if h != -1:
while true:
var k = pcFile
if f.cFilename[0] != '.':
if not skipFindData(f):
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
k = pcDir
yield (k, dir / extractFilename($f.cFilename))
if findnextFileA(h, f) == 0'i32: break
yield (k, dir / extractFilename(getFilename(f)))
if findnextFile(h, f) == 0'i32: break
findclose(h)
else:
var d = openDir(dir)
@@ -929,7 +1043,11 @@ iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string =
proc rawRemoveDir(dir: string) =
when defined(windows):
if RemoveDirectoryA(dir) == 0'i32 and GetLastError() != 3'i32 and
when useWinUnicode:
wrapUnary(res, RemoveDirectoryW, dir)
else:
var res = RemoveDirectoryA(dir)
if res == 0'i32 and GetLastError() != 3'i32 and
GetLastError() != 18'i32: OSError()
else:
if rmdir(dir) != 0'i32 and errno != ENOENT: OSError()
@@ -949,7 +1067,11 @@ proc rawCreateDir(dir: string) =
if mkdir(dir, 0o711) != 0'i32 and errno != EEXIST:
OSError()
else:
if CreateDirectoryA(dir, nil) == 0'i32 and GetLastError() != 183'i32:
when useWinUnicode:
wrapUnary(res, CreateDirectoryW, dir)
else:
var res = CreateDirectoryA(dir)
if res == 0'i32 and GetLastError() != 183'i32:
OSError()
proc createDir*(dir: string) {.rtl, extern: "nos$1".} =
@@ -1097,7 +1219,10 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
if (a.st_mode and S_IWOTH) != 0'i32: result.incl(fpOthersWrite)
if (a.st_mode and S_IXOTH) != 0'i32: result.incl(fpOthersExec)
else:
var res = GetFileAttributesA(filename)
when useWinUnicode:
wrapUnary(res, GetFileAttributesW, filename)
else:
var res = GetFileAttributesA(filename)
if res == -1'i32: OSError()
if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
@@ -1126,14 +1251,20 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
if chmod(filename, p) != 0: OSError()
else:
var res = GetFileAttributesA(filename)
when useWinUnicode:
wrapUnary(res, GetFileAttributesW, filename)
else:
var res = GetFileAttributesA(filename)
if res == -1'i32: OSError()
if fpUserWrite in permissions:
res = res and not FILE_ATTRIBUTE_READONLY
else:
res = res or FILE_ATTRIBUTE_READONLY
if SetFileAttributesA(filename, res) == - 1'i32:
OSError()
when useWinUnicode:
wrapBinary(res2, SetFileAttributesW, filename, res)
else:
var res2 = SetFileAttributesA(filename, res)
if res2 == - 1'i32: OSError()
proc inclFilePermissions*(filename: string,
permissions: set[TFilePermission]) {.
@@ -1181,7 +1312,7 @@ when defined(windows):
proc paramCount*(): int {.rtl, extern: "nos$1".} =
## Returns the number of `command line arguments`:idx: given to the
## application.
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
result = ownArgv.len-1
proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1".} =
@@ -1190,7 +1321,7 @@ when defined(windows):
##
## `i` should be in the range `1..paramCount()`, else
## the `EOutOfIndex` exception is raised.
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
return TaintedString(ownArgv[i])
elif not defined(createNimRtl):
@@ -1232,9 +1363,14 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1".} =
# *BSD (and maybe Darwin too):
# /proc/<pid>/file
when defined(windows):
result = newString(256)
var len = getModuleFileNameA(0, result, 256)
setlen(result, int(len))
when useWinUnicode:
var buf = cast[wideCString](alloc(256*2))
var len = getModuleFileNameW(0, buf, 256)
result = buf$len
else:
result = newString(256)
var len = getModuleFileNameA(0, result, 256)
setlen(result, int(len))
elif defined(linux) or defined(aix):
result = getApplAux("/proc/self/exe")
elif defined(solaris):
@@ -1290,7 +1426,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1".} =
## returns the file size of `file`. Can raise ``EOS``.
when defined(windows):
var a: TWin32FindData
var resA = findfirstFileA(file, a)
var resA = findfirstFile(file, a)
if resA == -1: OSError()
result = rdFileSize(a)
findclose(resA)

View File

@@ -343,8 +343,18 @@ when defined(Windows) and not defined(useNimRtl):
if len(workingDir) > 0: wd = workingDir
if env != nil: e = buildEnv(env)
if poEchoCmd in options: echo($cmdl)
success = winlean.CreateProcess(nil,
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
when useWinUnicode:
var tmp = allocWideCString(cmdl)
var ee = allocWideCString(e)
var wwd = allocWideCString(wd)
success = winlean.CreateProcessW(nil,
tmp, nil, nil, 1, NORMAL_PRIORITY_CLASS, ee, wwd, SI, ProcInfo)
if tmp != nil: dealloc tmp
if ee != nil: dealloc ee
if wwd != nil: dealloc wwd
else:
success = winlean.CreateProcessA(nil,
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
if poParentStreams notin options:
FileClose(si.hStdInput)
@@ -417,8 +427,15 @@ when defined(Windows) and not defined(useNimRtl):
SI.hStdError = GetStdHandle(STD_ERROR_HANDLE)
SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
if winlean.CreateProcess(nil, command, nil, nil, 0,
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo) == 0:
when useWinUnicode:
var c = allocWideCString(command)
var res = winlean.CreateProcessW(nil, c, nil, nil, 0,
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
dealloc c
else:
var res = winlean.CreateProcessA(nil, command, nil, nil, 0,
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
if res == 0:
OSError()
else:
Process = ProcInfo.hProcess

View File

@@ -34,9 +34,6 @@ proc ftell(f: TFile): int {.importc: "ftell", noDecl.}
proc setvbuf(stream: TFile, buf: pointer, typ, size: cint): cint {.
importc, nodecl.}
proc freopen(path, mode: cstring, stream: TFile): TFile {.importc: "freopen",
nodecl.}
proc write(f: TFile, c: cstring) = fputs(c, f)
var
@@ -161,7 +158,33 @@ proc rawEcho(x: string) {.inline, compilerproc.} = write(stdout, x)
proc rawEchoNL() {.inline, compilerproc.} = write(stdout, "\n")
# interface to the C procs:
proc fopen(filename, mode: CString): pointer {.importc: "fopen", noDecl.}
when defined(windows) and not defined(useWinAnsi):
include "system/widestrs"
proc wfopen(filename, mode: widecstring): pointer {.
importc: "_wfopen", nodecl.}
proc wfreopen(filename, mode: widecstring, stream: TFile): TFile {.
importc: "_wfreopen", nodecl.}
proc fopen(filename, mode: CString): pointer =
var f = allocWideCString(filename)
var m = allocWideCString(mode)
result = wfopen(f, m)
dealloc m
dealloc f
proc freopen(filename, mode: cstring, stream: TFile): TFile =
var f = allocWideCString(filename)
var m = allocWideCString(mode)
result = wfreopen(f, m, stream)
dealloc m
dealloc f
else:
proc fopen(filename, mode: CString): pointer {.importc: "fopen", noDecl.}
proc freopen(filename, mode: cstring, stream: TFile): TFile {.
importc: "freopen", nodecl.}
const
FormatOpen: array [TFileMode, string] = ["rb", "wb", "w+b", "r+b", "ab"]

260
lib/system/widestrs.nim Normal file
View File

@@ -0,0 +1,260 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nimrod support for C/C++'s `wide strings`:idx:. This is part of the system
## module! Do not import it directly!
type
TUtf16Char* = distinct int16
WideCString* = ptr array[0.. 1_000_000, TUtf16Char]
proc len*(w: WideCString): int =
## returns the length of a widestring. This traverses the whole string to
## find the binary zero end marker!
while int16(w[result]) != 0'i16: inc result
when true:
const
UNI_REPLACEMENT_CHAR = TUtf16Char(0xFFFD'i16)
UNI_MAX_BMP = 0x0000FFFF
UNI_MAX_UTF16 = 0x0010FFFF
UNI_MAX_UTF32 = 0x7FFFFFFF
UNI_MAX_LEGAL_UTF32 = 0x0010FFFF
halfShift = 10
halfBase = 0x0010000
halfMask = 0x3FF
UNI_SUR_HIGH_START = 0xD800
UNI_SUR_HIGH_END = 0xDBFF
UNI_SUR_LOW_START = 0xDC00
UNI_SUR_LOW_END = 0xDFFF
template ones(n: expr): expr = ((1 shl n)-1)
template fastRuneAt(s: cstring, i: int, result: expr, doInc = true) =
## Returns the unicode character ``s[i]`` in `result`. If ``doInc == true``
## `i` is incremented by the number of bytes that have been processed.
bind ones
if ord(s[i]) <=% 127:
result = ord(s[i])
when doInc: inc(i)
elif ord(s[i]) shr 5 == 0b110:
#assert(ord(s[i+1]) shr 6 == 0b10)
result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))
when doInc: inc(i, 2)
elif ord(s[i]) shr 4 == 0b1110:
#assert(ord(s[i+1]) shr 6 == 0b10)
#assert(ord(s[i+2]) shr 6 == 0b10)
result = (ord(s[i]) and ones(4)) shl 12 or
(ord(s[i+1]) and ones(6)) shl 6 or
(ord(s[i+2]) and ones(6))
when doInc: inc(i, 3)
elif ord(s[i]) shr 3 == 0b11110:
#assert(ord(s[i+1]) shr 6 == 0b10)
#assert(ord(s[i+2]) shr 6 == 0b10)
#assert(ord(s[i+3]) shr 6 == 0b10)
result = (ord(s[i]) and ones(3)) shl 18 or
(ord(s[i+1]) and ones(6)) shl 12 or
(ord(s[i+2]) and ones(6)) shl 6 or
(ord(s[i+3]) and ones(6))
when doInc: inc(i, 4)
else:
result = 0xFFFD
when doInc: inc(i)
iterator runes(s: cstring): int =
var
i = 0
result: int
while s[i] != '\0':
fastRuneAt(s, i, result, true)
yield result
proc allocWideCString*(source: cstring, L: int): WideCString =
## free after usage with `dealloc`.
result = cast[wideCString](alloc(L * 4 + 2))
var d = 0
for ch in runes(source):
if ch <=% UNI_MAX_BMP:
if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_LOW_END:
result[d] = UNI_REPLACEMENT_CHAR
else:
result[d] = TUtf16Char(toU16(ch))
elif ch >% UNI_MAX_UTF16:
result[d] = UNI_REPLACEMENT_CHAR
else:
let ch = ch -% halfBase
result[d] = TUtf16Char(toU16((ch shr halfShift) +% UNI_SUR_HIGH_START))
inc d
result[d] = TUtf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START))
inc d
result[d] = TUtf16Char(0'i16)
proc allocWideCString*(s: cstring): WideCString =
## free after usage with `dealloc`.
if s.isNil: return nil
when not defined(c_strlen):
proc c_strlen(a: CString): int {.nodecl, noSideEffect, importc: "strlen".}
let L = cstrlen(s)
result = allocWideCString(s, L)
proc allocWideCString*(s: string): WideCString =
## free after usage with `dealloc`.
result = allocWideCString(s, s.len)
proc `$`*(w: wideCString, estimate: int): string =
result = newStringOfCap(estimate + estimate shr 2)
var i = 0
while w[i].int16 != 0'i16:
var ch = w[i].int
inc i
if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_HIGH_END:
# If the 16 bits following the high surrogate are in the source buffer...
let ch2 = w[i].int
# If it's a low surrogate, convert to UTF32:
if ch2 >=% UNI_SUR_LOW_START and ch2 <=% UNI_SUR_LOW_END:
ch = ((ch -% UNI_SUR_HIGH_START) shr halfShift) +%
(ch2 -% UNI_SUR_LOW_START) +% halfBase
inc i
if ch <=% 127:
result.add chr(ch)
elif ch <=% 0x07FF:
result.add chr((ch shr 6) or 0b110_00000)
result.add chr((ch and ones(6)) or 0b10_000000)
elif ch <=% 0xFFFF:
result.add chr(ch shr 12 or 0b1110_0000)
result.add chr(ch shr 6 and ones(6) or 0b10_0000_00)
result.add chr(ch and ones(6) or 0b10_0000_00)
elif ch <=% 0x0010FFFF:
result.add chr(ch shr 18 or 0b1111_0000)
result.add chr(ch shr 12 and ones(6) or 0b10_0000_00)
result.add chr(ch shr 6 and ones(6) or 0b10_0000_00)
result.add chr(ch and ones(6) or 0b10_0000_00)
else:
# replacement char:
result.add chr(0xFFFD shr 12 or 0b1110_0000)
result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
result.add chr(0xFFFD and ones(6) or 0b10_0000_00)
proc `$`*(s: WideCString): string =
result = s $ 80
else:
const
utf8Encoding = 65001
proc MultiByteToWideChar*(
CodePage: int32,
dwFlags: int32,
lpMultiByteStr: cstring,
cbMultiByte: cint,
lpWideCharStr: WideCString,
cchWideChar: cint): cint {.
stdcall, importc: "MultiByteToWideChar", dynlib: "kernel32".}
proc WideCharToMultiByte*(
CodePage: int32,
dwFlags: int32,
lpWideCharStr: WideCString,
cchWideChar: cint,
lpMultiByteStr: cstring,
cbMultiByte: cint,
lpDefaultChar: cstring=nil,
lpUsedDefaultChar: pointer=nil): cint {.
stdcall, importc: "WideCharToMultiByte", dynlib: "kernel32".}
proc raiseEncodingError() {.noinline, noreturn.} =
raise newException(EOS, "error in unicode conversion")
proc `$`*(s: WideCString, len: int): string =
# special case: empty string: needed because MultiByteToWideChar
# returns 0 in case of error:
if len == 0: return ""
# educated guess of capacity:
var cap = len + len shr 2
result = newStringOfCap(cap)
let m = WideCharToMultiByte(
CodePage = utf8Encoding,
dwFlags = 0'i32,
lpWideCharStr = s,
cchWideChar = cint(len),
lpMultiByteStr = cstring(result),
cbMultiByte = cap)
if m == 0:
# try again; ask for capacity:
cap = WideCharToMultiByte(
CodePage = utf8Encoding,
dwFlags = 0'i32,
lpWideCharStr = s,
cchWideChar = cint(len),
lpMultiByteStr = nil,
cbMultiByte = cint(0))
# and do the conversion properly:
result = newStringOfCap(cap)
let m = WideCharToMultiByte(
CodePage = utf8Encoding,
dwFlags = 0'i32,
lpWideCharStr = s,
cchWideChar = cint(len),
lpMultiByteStr = cstring(result),
cbMultiByte = cap)
if m == 0: raiseEncodingError()
setLen(result, m)
elif m <= cap:
setLen(result, m)
else:
sysAssert(false, "") # cannot happen
proc `$`*(s: WideCString): string =
result = s $ s.len
proc allocWideCString*(s: string): WideCString =
## free after usage with `dealloc`.
let cap = s.len+1
result = cast[wideCString](alloc0(cap * 2))
# special case: empty string: needed because MultiByteToWideChar
# return 0 in case of error:
if s.len == 0: return
# convert to utf-16 LE
let m = MultiByteToWideChar(CodePage = utf8Encoding, dwFlags = 0'i32,
lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len),
lpWideCharStr = result,
cchWideChar = cint(cap))
if m == 0: raiseEncodingError()
proc allocWideCString*(s: cstring): WideCString =
## free after usage with `dealloc`.
if s.isNil: return nil
when not defined(c_strlen):
proc c_strlen(a: CString): int {.nodecl, noSideEffect, importc: "strlen".}
let len = cstrlen(s)
let cap = len+1
result = cast[wideCString](alloc0(cap * 2))
# special case: empty string: needed because MultiByteToWideChar
# return 0 in case of error:
if s.len == 0: return
# convert to utf-16 LE
let m = MultiByteToWideChar(CodePage = utf8Encoding, dwFlags = 0'i32,
lpMultiByteStr = s,
cbMultiByte = cint(len),
lpWideCharStr = result,
cchWideChar = cint(cap))
if m == 0: raiseEncodingError()

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -10,6 +10,9 @@
## This module implements a small wrapper for some needed Win API procedures,
## so that the Nimrod compiler does not depend on the huge Windows module.
const
useWinUnicode* = not defined(useWinAnsi)
type
THandle* = int
LONG* = int
@@ -63,6 +66,11 @@ type
nFileIndexHigh*: DWORD
nFileIndexLow*: DWORD
when useWinUnicode:
type TWinChar* = TUtf16Char
else:
type TWinChar* = char
const
STARTF_USESHOWWINDOW* = 1'i32
STARTF_USESTDHANDLES* = 256'i32
@@ -100,15 +108,27 @@ proc CreatePipe*(hReadPipe, hWritePipe: var THandle,
lpPipeAttributes: var TSECURITY_ATTRIBUTES,
nSize: int32): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreatePipe".}
proc CreateProcess*(lpApplicationName, lpCommandLine: cstring,
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
bInheritHandles: WINBOOL, dwCreationFlags: int32,
lpEnvironment: pointer, lpCurrentDirectory: cstring,
lpStartupInfo: var TSTARTUPINFO,
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreateProcessA".}
when useWinUnicode:
proc CreateProcessW*(lpApplicationName, lpCommandLine: widecstring,
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
bInheritHandles: WINBOOL, dwCreationFlags: int32,
lpEnvironment: pointer, lpCurrentDirectory: widecstring,
lpStartupInfo: var TSTARTUPINFO,
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreateProcessW".}
else:
proc CreateProcessA*(lpApplicationName, lpCommandLine: cstring,
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
bInheritHandles: WINBOOL, dwCreationFlags: int32,
lpEnvironment: pointer, lpCurrentDirectory: cstring,
lpStartupInfo: var TSTARTUPINFO,
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "CreateProcessA".}
proc SuspendThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",
importc: "SuspendThread".}
@@ -132,27 +152,53 @@ proc FlushFileBuffers*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "FlushFileBuffers".}
proc GetLastError*(): int32 {.importc, stdcall, dynlib: "kernel32".}
proc FormatMessageA*(dwFlags: int32, lpSource: pointer,
when useWinUnicode:
proc FormatMessageW*(dwFlags: int32, lpSource: pointer,
dwMessageId, dwLanguageId: int32,
lpBuffer: pointer, nSize: int32,
Arguments: pointer): int32 {.
importc, stdcall, dynlib: "kernel32".}
else:
proc FormatMessageA*(dwFlags: int32, lpSource: pointer,
dwMessageId, dwLanguageId: int32,
lpBuffer: pointer, nSize: int32,
Arguments: pointer): int32 {.
importc, stdcall, dynlib: "kernel32".}
proc LocalFree*(p: pointer) {.importc, stdcall, dynlib: "kernel32".}
proc GetCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc SetCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc CreateDirectoryA*(pathName: cstring, security: Pointer): int32 {.
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc RemoveDirectoryA*(lpPathName: cstring): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc SetEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
when useWinUnicode:
proc GetCurrentDirectoryW*(nBufferLength: int32,
lpBuffer: widecstring): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc SetCurrentDirectoryW*(lpPathName: widecstring): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc CreateDirectoryW*(pathName: widecstring, security: Pointer=nil): int32 {.
importc: "CreateDirectoryW", dynlib: "kernel32", stdcall.}
proc RemoveDirectoryW*(lpPathName: widecstring): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc SetEnvironmentVariableW*(lpName, lpValue: widecstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc GetModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc GetModuleFileNameW*(handle: THandle, buf: wideCString,
size: int32): int32 {.importc,
dynlib: "kernel32", stdcall.}
else:
proc GetCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc SetCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc CreateDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {.
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc RemoveDirectoryA*(lpPathName: cstring): int32 {.
importc, dynlib: "kernel32", stdcall.}
proc SetEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc GetModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
importc, dynlib: "kernel32", stdcall.}
const
FILE_ATTRIBUTE_ARCHIVE* = 32'i32
FILE_ATTRIBUTE_COMPRESSED* = 2048'i32
@@ -174,36 +220,69 @@ type
nFileSizeLow*: int32
dwReserved0: int32
dwReserved1: int32
cFileName*: array[0..(MAX_PATH) - 1, char]
cAlternateFileName*: array[0..13, char]
cFileName*: array[0..(MAX_PATH) - 1, TWinChar]
cAlternateFileName*: array[0..13, TWinChar]
when useWinUnicode:
proc FindFirstFileW*(lpFileName: widecstring,
lpFindFileData: var TWIN32_FIND_DATA): THANDLE {.
stdcall, dynlib: "kernel32", importc: "FindFirstFileW".}
proc FindNextFileW*(hFindFile: THANDLE,
lpFindFileData: var TWIN32_FIND_DATA): int32 {.
stdcall, dynlib: "kernel32", importc: "FindNextFileW".}
else:
proc FindFirstFileA*(lpFileName: cstring,
lpFindFileData: var TWIN32_FIND_DATA): THANDLE {.
stdcall, dynlib: "kernel32", importc: "FindFirstFileA".}
proc FindNextFileA*(hFindFile: THANDLE,
lpFindFileData: var TWIN32_FIND_DATA): int32 {.
stdcall, dynlib: "kernel32", importc: "FindNextFileA".}
proc FindFirstFileA*(lpFileName: cstring,
lpFindFileData: var TWIN32_FIND_DATA): THANDLE {.
stdcall, dynlib: "kernel32", importc: "FindFirstFileA".}
proc FindNextFileA*(hFindFile: THANDLE,
lpFindFileData: var TWIN32_FIND_DATA): int32 {.
stdcall, dynlib: "kernel32", importc: "FindNextFileA".}
proc FindClose*(hFindFile: THANDLE) {.stdcall, dynlib: "kernel32",
importc: "FindClose".}
proc GetFullPathNameA*(lpFileName: cstring, nBufferLength: int32,
lpBuffer: cstring, lpFilePart: var cstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc GetFileAttributesA*(lpFileName: cstring): int32 {.
when useWinUnicode:
proc GetFullPathNameW*(lpFileName: widecstring, nBufferLength: int32,
lpBuffer: widecstring,
lpFilePart: var widecstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc SetFileAttributesA*(lpFileName: cstring, dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
proc GetFileAttributesW*(lpFileName: widecstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc SetFileAttributesW*(lpFileName: widecstring,
dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesW".}
proc CopyFileA*(lpExistingFileName, lpNewFileName: CString,
bFailIfExists: cint): cint {.
importc, stdcall, dynlib: "kernel32".}
proc CopyFileW*(lpExistingFileName, lpNewFileName: wideCString,
bFailIfExists: cint): cint {.
importc, stdcall, dynlib: "kernel32".}
proc GetEnvironmentStringsA*(): cstring {.
stdcall, dynlib: "kernel32", importc.}
proc FreeEnvironmentStringsA*(para1: cstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc GetEnvironmentStringsW*(): widecstring {.
stdcall, dynlib: "kernel32", importc.}
proc FreeEnvironmentStringsW*(para1: widecstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc GetCommandLineA*(): CString {.importc, stdcall, dynlib: "kernel32".}
proc GetCommandLineW*(): wideCString {.importc, stdcall, dynlib: "kernel32".}
else:
proc GetFullPathNameA*(lpFileName: cstring, nBufferLength: int32,
lpBuffer: cstring, lpFilePart: var cstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc GetFileAttributesA*(lpFileName: cstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc SetFileAttributesA*(lpFileName: cstring,
dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
proc CopyFileA*(lpExistingFileName, lpNewFileName: CString,
bFailIfExists: cint): cint {.
importc, stdcall, dynlib: "kernel32".}
proc GetEnvironmentStringsA*(): cstring {.
stdcall, dynlib: "kernel32", importc.}
proc FreeEnvironmentStringsA*(para1: cstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc GetCommandLineA*(): CString {.importc, stdcall, dynlib: "kernel32".}
proc rdFileTime*(f: TFILETIME): int64 =
result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
@@ -217,11 +296,18 @@ proc GetSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var TFILETIME) {.
proc Sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
importc: "Sleep".}
proc ShellExecute*(HWND: THandle, lpOperation, lpFile,
lpParameters, lpDirectory: cstring,
nShowCmd: int32): THandle{.
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".}
when useWinUnicode:
proc ShellExecuteW*(HWND: THandle, lpOperation, lpFile,
lpParameters, lpDirectory: widecstring,
nShowCmd: int32): THandle{.
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW".}
else:
proc ShellExecuteA*(HWND: THandle, lpOperation, lpFile,
lpParameters, lpDirectory: cstring,
nShowCmd: int32): THandle{.
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".}
proc GetFileInformationByHandle*(hFile: THandle,
lpFileInformation: ptr TBY_HANDLE_FILE_INFORMATION): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle".}
@@ -446,11 +532,18 @@ const
FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32
proc CreateFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
lpSecurityAttributes: pointer,
dwCreationDisposition, dwFlagsAndAttributes: DWORD,
hTemplateFile: THANDLE): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileA".}
when useWinUnicode:
proc CreateFileW*(lpFileName: widecstring, dwDesiredAccess, dwShareMode: DWORD,
lpSecurityAttributes: pointer,
dwCreationDisposition, dwFlagsAndAttributes: DWORD,
hTemplateFile: THANDLE): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileW".}
else:
proc CreateFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
lpSecurityAttributes: pointer,
dwCreationDisposition, dwFlagsAndAttributes: DWORD,
hTemplateFile: THANDLE): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileA".}
proc SetEndOfFile*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "SetEndOfFile".}
@@ -469,11 +562,20 @@ proc MapViewOfFileEx*(hFileMappingObject: THANDLE, dwDesiredAccess: DWORD,
lpBaseAddress: pointer): pointer{.
stdcall, dynlib: "kernel32", importc: "MapViewOfFileEx".}
proc CreateFileMapping*(hFile: THANDLE,
lpFileMappingAttributes: pointer,
flProtect, dwMaximumSizeHigh: DWORD,
dwMaximumSizeLow: DWORD, lpName: cstring): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileMappingA".}
proc CreateFileMappingW*(hFile: THANDLE,
lpFileMappingAttributes: pointer,
flProtect, dwMaximumSizeHigh: DWORD,
dwMaximumSizeLow: DWORD,
lpName: pointer): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".}
when not useWinUnicode:
proc CreateFileMappingA*(hFile: THANDLE,
lpFileMappingAttributes: pointer,
flProtect, dwMaximumSizeHigh: DWORD,
dwMaximumSizeLow: DWORD, lpName: cstring): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileMappingA".}
proc UnmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "UnmapViewOfFile".}