summary refs log tree commit diff stats
path: root/lib/pure/includes/oserr.nim
blob: 0889d7383517e358ade0410d53184ab9451b3a43 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
## Include file that implements 'osErrorMsg' and friends. Do not import it!

when not declared(ospaths):
  {.error: "This is an include file for ospaths.nim!".}

when not defined(nimscript):
  var errno {.importc, header: "<errno.h>".}: cint

  proc c_strerror(errnum: cint): cstring {.
    importc: "strerror", header: "<string.h>".}

  when defined(windows):
    import winlean

proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
  ## Retrieves the operating system's error flag, ``errno``.
  ## On Windows ``GetLastError`` is checked before ``errno``.
  ## Returns "" if no error occurred.
  ##
  ## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc.

  result = ""
  when defined(Windows) and not defined(nimscript):
    var err = getLastError()
    if err != 0'i32:
      when useWinUnicode:
        var msgbuf: WideCString
        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
                          nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
          result = $msgbuf
          if msgbuf != nil: localFree(cast[pointer](msgbuf))
      else:
        var msgbuf: cstring
        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
                          nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
          result = $msgbuf
          if msgbuf != nil: localFree(msgbuf)
  when not defined(nimscript):
    if errno != 0'i32:
      result = $c_strerror(errno)

{.push warning[deprecated]: off.}
proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
                                       deprecated.} =
  ## raises an OSError exception with the given message ``msg``.
  ## If ``msg == ""``, the operating system's error flag
  ## (``errno``) is converted to a readable error message. On Windows
  ## ``GetLastError`` is checked before ``errno``.
  ## If no error flag is set, the message ``unknown OS error`` is used.
  ##
  ## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc.
  if len(msg) == 0:
    var m = osErrorMsg()
    raise newException(OSError, if m.len > 0: m else: "unknown OS error")
  else:
    raise newException(OSError, msg)
{.pop.}

proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
proc `$`*(err: OSErrorCode): string {.borrow.}

proc osErrorMsg*(errorCode: OSErrorCode): string =
  ## Converts an OS error code into a human readable string.
  ##
  ## The error code can be retrieved using the ``osLastError`` proc.
  ##
  ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be
  ## returned.
  ##
  ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to
  ## make this procedure use the non-unicode Win API calls to retrieve the
  ## message.
  result = ""
  when defined(nimscript):
    discard
  elif defined(Windows):
    if errorCode != OSErrorCode(0'i32):
      when useWinUnicode:
        var msgbuf: WideCString
        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
          result = $msgbuf
          if msgbuf != nil: localFree(cast[pointer](msgbuf))
      else:
        var msgbuf: cstring
        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
          result = $msgbuf
          if msgbuf != nil: localFree(msgbuf)
  else:
    if errorCode != OSErrorCode(0'i32):
      result = $c_strerror(errorCode.int32)

proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
  ## Raises an ``OSError`` exception. The ``errorCode`` will determine the
  ## message, ``osErrorMsg`` will be used to get this message.
  ##
  ## The error code can be retrieved using the ``osLastError`` proc.
  ##
  ## If the error code is ``0`` or an error message could not be retrieved,
  ## the message ``unknown OS error`` will be used.
  var e: ref OSError; new(e)
  e.errorCode = errorCode.int32
  if additionalInfo.len == 0:
    e.msg = osErrorMsg(errorCode)
  else:
    e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
  if e.msg == "":
    e.msg = "unknown OS error"
  raise e

{.push stackTrace:off.}
proc osLastError*(): OSErrorCode =
  ## Retrieves the last operating system error code.
  ##
  ## This procedure is useful in the event when an OS call fails. In that case
  ## this procedure will return the error code describing the reason why the
  ## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert
  ## this code into a string.
  ##
  ## **Warning**:
  ## The behaviour of this procedure varies between Windows and POSIX systems.
  ## On Windows some OS calls can reset the error code to ``0`` causing this
  ## procedure to return ``0``. It is therefore advised to call this procedure
  ## immediately after an OS call fails. On POSIX systems this is not a problem.
  when defined(nimscript):
    discard
  elif defined(windows):
    result = OSErrorCode(getLastError())
  else:
    result = OSErrorCode(errno)
{.pop.}