summary refs log tree commit diff stats
diff options
context:
space:
mode:
authordom96 <dominikpicheta@googlemail.com>2011-05-07 23:17:10 +0100
committerdom96 <dominikpicheta@googlemail.com>2011-05-07 23:17:10 +0100
commit0db6f3c00f7975d49b5d4d1c061d6f29162176af (patch)
tree70136af40049a5a83e7e6ddee7a3fe2a38d4a661
parent73c355176653e5b643f2bbbdc4f967cc24b3ee3b (diff)
downloadNim-0db6f3c00f7975d49b5d4d1c061d6f29162176af.tar.gz
select() for processes; copyDir() for os.
-rwxr-xr-xlib/pure/os.nim46
-rwxr-xr-xlib/pure/osproc.nim82
-rwxr-xr-xlib/windows/windows.nim7
-rwxr-xr-xlib/windows/winlean.nim14
4 files changed, 129 insertions, 20 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index e044fe605..81a038de7 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -147,6 +147,24 @@ const
     ## The character which separates the base filename from the extension;
     ## for example, the '.' in ``os.nim``.
 
+proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} =
+  ## Retrieves the operating system's error flag, ``errno`` on Posix and 
+  ## ``GetLastError`` on Windows.
+  result = ""
+  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
+  else:
+    if errno != 0'i32:
+      result = $os.strerror(errno)
+
 proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1".} =
   ## raises an EOS exception with the given message ``msg``.
   ## If ``msg == ""``, the operating system's error flag
@@ -154,19 +172,9 @@ proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1".} =
   ## ``GetLastError`` is checked before ``errno``.
   ## If no error flag is set, the message ``unknown OS error`` is used.
   if len(msg) == 0:
-    when defined(Windows):
-      var err = GetLastError()
-      if err != 0'i32:
-        # sigh, why is this is so difficult?
-        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)
-          raise newException(EOS, m)
-    if errno != 0'i32:
-      raise newException(EOS, $os.strerror(errno))
+    var m = OSErrorMsg()
+    if m != "":
+      raise newException(EOS, m)
     else:
       raise newException(EOS, "unknown OS error")
   else:
@@ -942,6 +950,18 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1".} =
     if dir[i] in {dirsep, altsep}: rawCreateDir(copy(dir, 0, i-1))
   rawCreateDir(dir)
 
+proc copyDir*(source, dest: string) {.rtl, extern: "nos$1".} =
+  ## Copies a directory from `source` to `dest`. If this fails, `EOS` is raised.
+  createDir(dest)
+  for kind, path in walkDir(source):
+    var noSource = path.copy(source.len()+1)
+    case kind
+    of pcFile:
+      copyFile(path, dest / noSource)
+    of pcDir:
+      copyDir(path, dest / noSource)
+    else: nil
+
 proc parseCmdLine*(c: string): seq[string] {.
   noSideEffect, rtl, extern: "nos$1".} =
   ## Splits a command line into several components;  
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index e13f508b6..a539e5f39 100755
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2010 Andreas Rumpf
+#        (c) Copyright 2011 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -43,7 +43,7 @@ proc execProcess*(command: string,
                   options: set[TProcessOption] = {poStdErrToStdOut,
                                                   poUseShell}): string {.
                                                   rtl, extern: "nosp$1".}
-  ## A convience procedure that executes ``command`` with ``startProcess``
+  ## A convenience procedure that executes ``command`` with ``startProcess``
   ## and returns its output as a string.
 
 proc executeProcess*(command: string,
@@ -101,6 +101,9 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
 proc waitForExit*(p: PProcess): int {.rtl, extern: "nosp$1".}
   ## waits for the process to finish and returns `p`'s error code.
 
+proc peekExitCode*(p: PProcess): int
+  ## return -1 if the process is still running. Otherwise the process' exit code
+
 proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
   ## returns ``p``'s input stream for writing to
 
@@ -196,6 +199,12 @@ proc execProcesses*(cmds: openArray[string],
       var p = startProcessAux(cmds[i], options=options)
       result = max(waitForExit(p), result)
 
+proc select*(readfds: var seq[PProcess], timeout = 500): int
+  ## select with a sensible Nimrod interface. `timeout` is in miliseconds.
+  ## Specify -1 for no timeout. Returns the number of file handles that are
+  ## ready. The processes that are ready to be read from are removed from 
+  ## `readfds`.
+
 when not defined(useNimRtl):
   proc execProcess(command: string,
                    options: set[TProcessOption] = {poStdErrToStdOut,
@@ -366,6 +375,14 @@ when defined(Windows) and not defined(useNimRtl):
     result = res
     discard CloseHandle(p.FProcessHandle)
 
+  proc peekExitCode(p: PProcess): int =
+    var b = waitForSingleObject(p.FProcessHandle, 50) == WAIT_TIMEOUT
+    if b: result = -1
+    else: 
+      var res: int32
+      discard GetExitCodeProcess(p.FProcessHandle, res)
+      return res
+
   proc inputStream(p: PProcess): PStream =
     result = newFileHandleStream(p.inputHandle)
 
@@ -398,6 +415,24 @@ when defined(Windows) and not defined(useNimRtl):
         result = -1
       discard CloseHandle(Process)
 
+  proc select(readfds: var seq[PProcess], timeout = 500): int = 
+    assert readfds.len <= MAXIMUM_WAIT_OBJECTS
+    var rfds: TWOHandleArray
+    for i in 0..readfds.len()-1:
+      rfds[i] = readfds[i].FProcessHandle
+    
+    var ret = waitForMultipleObjects(readfds.len, 
+                                     addr(rfds), 0'i32, timeout)
+    case ret
+    of WAIT_TIMEOUT:
+      return 0
+    of WAIT_FAILED:
+      OSError()
+    else:
+      var i = ret - WAIT_OBJECT_0
+      readfds.del(i)
+      return 1
+
 elif not defined(useNimRtl):
   const
     readIdx = 0
@@ -435,7 +470,7 @@ elif not defined(useNimRtl):
     var
       p_stdin, p_stdout, p_stderr: array [0..1, cint]
     new(result)
-    result.exitCode = 3 # for ``waitForExit``
+    result.exitCode = -3 # for ``waitForExit``
     if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32:
       OSError("failed to create a pipe")
     var Pid = fork()
@@ -504,13 +539,19 @@ elif not defined(useNimRtl):
     #if waitPid(p.id, p.exitCode, 0) == int(p.id):
     # ``waitPid`` fails if the process is not running anymore. But then
     # ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
-    # initialized with 3, wrong success exit codes are prevented.
+    # initialized with -3, wrong success exit codes are prevented.
     var oldExitCode = p.exitCode
     if waitPid(p.id, p.exitCode, 0) < 0:
       # failed, so restore old exitCode
       p.exitCode = oldExitCode
     result = int(p.exitCode)
 
+  proc peekExitCode(p: PProcess): int =
+    var b = waitPid(p.id, p.exitCode, WNOHANG) == int(p.id)
+    if b: result = -1
+    elif p.exitCode == -3: result = -1
+    else: result = p.exitCode
+
   proc inputStream(p: PProcess): PStream =
     var f: TFile
     if not open(f, p.inputHandle, fmWrite): OSError()
@@ -531,6 +572,39 @@ elif not defined(useNimRtl):
   proc execCmd(command: string): int =
     result = csystem(command)
 
+  proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) = 
+    FD_ZERO(fd)
+    for i in items(s): 
+      m = max(m, int(i.outputHandle))
+      FD_SET(cint(i.outputHandle), fd)
+     
+  proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) = 
+    var i = 0
+    var L = s.len
+    while i < L:
+      if FD_ISSET(cint(s[i].outputHandle), fd) != 0'i32:
+        s[i] = s[L-1]
+        dec(L)
+      else:
+        inc(i)
+    setLen(s, L)
+
+  proc select(readfds: var seq[PProcess], timeout = 500): int = 
+    var tv: TTimeVal
+    tv.tv_sec = 0
+    tv.tv_usec = timeout * 1000
+    
+    var rd: TFdSet
+    var m = 0
+    createFdSet((rd), readfds, m)
+    
+    if timeout != -1:
+      result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
+    else:
+      result = int(select(cint(m+1), addr(rd), nil, nil, nil))
+    
+    pruneProcessSet(readfds, (rd))
+
 when isMainModule:
   var x = execProcess("gcc -v")
   echo "ECHO ", x
diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim
index 579775c2d..c8c977d9b 100755
--- a/lib/windows/windows.nim
+++ b/lib/windows/windows.nim
@@ -360,6 +360,7 @@ type
     RASP_PppIp = 0x00008021, RASP_PppIpx = 0x0000802B, RASP_PppNbf = 0x0000803F,

     RASP_Amb = 0x00010000

   SECURITY_IMPERSONATION_LEVEL* = enum

+

     SecurityAnonymous, SecurityIdentification, SecurityImpersonation,

     SecurityDelegation

   SID_NAME_USE* = enum

@@ -3761,6 +3762,7 @@ const
   SMTO_NORMAL* = 0

   # SetBkMode

   OPAQUE* = 2

+

   TRANSPARENT* = 1

   # SetDebugErrorLevel

   SLE_ERROR* = 1

@@ -16215,6 +16217,7 @@ when defined(winUnicode):
   proc GetEnvironmentStrings*(): LPWSTR{.stdcall, dynlib: "kernel32",

       importc: "GetEnvironmentStringsW".}

   proc FreeEnvironmentStrings*(para1: LPWSTR): WINBOOL{.stdcall,

+

       dynlib: "kernel32", importc: "FreeEnvironmentStringsW".}

   proc FormatMessage*(dwFlags: DWORD, lpSource: LPCVOID, dwMessageId: DWORD,

                       dwLanguageId: DWORD, lpBuffer: LPWSTR, nSize: DWORD,

@@ -18715,9 +18718,7 @@ proc ReleaseMutex*(hMutex: HANDLE): WINBOOL{.stdcall, dynlib: "kernel32",
     importc: "ReleaseMutex".}

 proc WaitForSingleObject*(hHandle: HANDLE, dwMilliseconds: DWORD): DWORD{.

     stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}

-proc WaitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray,

-                             bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{.

-    stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}

+

 proc Sleep*(dwMilliseconds: DWORD){.stdcall, dynlib: "kernel32",

                                     importc: "Sleep".}

 proc LoadResource*(hModule: HINST, hResInfo: HRSRC): HGLOBAL{.stdcall,

diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index e8c93d8b1..75f2fc4df 100755
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -13,6 +13,7 @@
 type
   THandle* = int
   WINBOOL* = int32
+  DWORD* = int32
 
   TSECURITY_ATTRIBUTES* {.final, pure.} = object
     nLength*: int32
@@ -52,7 +53,9 @@ const
   IDLE_PRIORITY_CLASS* = 64'i32
   NORMAL_PRIORITY_CLASS* = 32'i32
   REALTIME_PRIORITY_CLASS* = 256'i32
+  WAIT_OBJECT_0* = 0'i32
   WAIT_TIMEOUT* = 0x00000102'i32
+  WAIT_FAILED* = 0xFFFFFFFF'i32
   INFINITE* = -1'i32
 
   STD_INPUT_HANDLE* = -10'i32
@@ -383,3 +386,14 @@ proc freeaddrinfo*(ai: ptr TAddrInfo) {.
 
 proc inet_ntoa*(i: TInAddr): cstring {.
   stdcall, importc, dynlib: ws2dll.}
+
+const
+  MAXIMUM_WAIT_OBJECTS* = 0x00000040
+
+type
+  TWOHandleArray* = array[0..MAXIMUM_WAIT_OBJECTS - 1, THANDLE]
+  PWOHandleArray* = ptr TWOHandleArray
+
+proc WaitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray,
+                             bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{.
+    stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}