summary refs log tree commit diff stats
path: root/lib/system/fatal.nim
diff options
context:
space:
mode:
authorSolitudeSF <solitudesf@protonmail.com>2019-06-14 20:00:23 +0300
committerSolitudeSF <solitudesf@protonmail.com>2019-06-14 20:00:23 +0300
commite96afd6324a4a2e98994c9cd8f6e43101dd5525d (patch)
tree6f7d50c58067bfb84a85f850de33ffd86f99a3b5 /lib/system/fatal.nim
parent57a83df7452dd715319d927602a0fb72a4cccfbd (diff)
downloadNim-e96afd6324a4a2e98994c9cd8f6e43101dd5525d.tar.gz
[bugfix] Add `.47` as supported libssl version
Diffstat (limited to 'lib/system/fatal.nim')
0 files changed, 0 insertions, 0 deletions
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
#
#
#           The Nim Compiler
#        (c) Copyright 2018 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## Path handling utilities for Nim. Strictly typed code in order
## to avoid the never ending time sink in getting path handling right.
## Might be a candidate for the stdlib later.

import os, strutils

type
  AbsoluteFile* = distinct string
  AbsoluteDir* = distinct string
  RelativeFile* = distinct string
  RelativeDir* = distinct string

proc isEmpty*(x: AbsoluteFile): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: AbsoluteDir): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: RelativeFile): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: RelativeDir): bool {.inline.} = x.string.len == 0

proc copyFile*(source, dest: AbsoluteFile) =
  os.copyFile(source.string, dest.string)

proc removeFile*(x: AbsoluteFile) {.borrow.}

proc splitFile*(x: AbsoluteFile): tuple[dir: AbsoluteDir, name, ext: string] =
  let (a, b, c) = splitFile(x.string)
  result = (dir: AbsoluteDir(a), name: b, ext: c)

proc extractFilename*(x: AbsoluteFile): string {.borrow.}

proc fileExists*(x: AbsoluteFile): bool {.borrow.}
proc dirExists*(x: AbsoluteDir): bool {.borrow.}

proc quoteShell*(x: AbsoluteFile): string {.borrow.}
proc quoteShell*(x: AbsoluteDir): string {.borrow.}

proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.}

proc createDir*(x: AbsoluteDir) {.borrow.}

type
  PathIter = object
    i, prev: int
    notFirst: bool

proc hasNext(it: PathIter; x: string): bool =
  it.i < x.len

proc next(it: var PathIter; x: string): (int, int) =
  it.prev = it.i
  if not it.notFirst and x[it.i] in {DirSep, AltSep}:
    # absolute path:
    inc it.i
  else:
    while it.i < x.len and x[it.i] notin {DirSep, AltSep}: inc it.i
  if it.i > it.prev:
    result = (it.prev, it.i-1)
  elif hasNext(it, x):
    result = next(it, x)

  # skip all separators:
  while it.i < x.len and x[it.i] in {DirSep, AltSep}: inc it.i
  it.notFirst = true

iterator dirs(x: string): (int, int) =
  var it: PathIter
  while hasNext(it, x): yield next(it, x)

when false:
  iterator dirs(x: string): (int, int) =
    var i = 0
    var first = true
    while i < x.len:
      let prev = i
      if first and x[i] in {DirSep, AltSep}:
        # absolute path:
        inc i
      else:
        while i < x.len and x[i] notin {DirSep, AltSep}: inc i
      if i > prev:
        yield (prev, i-1)
      first = false
      # skip all separators:
      while i < x.len and x[i] in {DirSep, AltSep}: inc i

proc isDot(x: string; bounds: (int, int)): bool =
  bounds[1] == bounds[0] and x[bounds[0]] == '.'

proc isDotDot(x: string; bounds: (int, int)): bool =
  bounds[1] == bounds[0] + 1 and x[bounds[0]] == '.' and x[bounds[0]+1] == '.'

proc isSlash(x: string; bounds: (int, int)): bool =
  bounds[1] == bounds[0] and x[bounds[0]] in {DirSep, AltSep}

proc canon(x: string; result: var string; state: var int) =
  # state: 0th bit set if isAbsolute path. Other bits count
  # the number of path components.
  for b in dirs(x):
    if (state shr 1 == 0) and isSlash(x, b):
      result.add DirSep
      state = state or 1
    elif result.len > (state and 1) and isDotDot(x, b):
      var d = result.len
      # f/..
      while d > (state and 1) and result[d-1] != DirSep:
        dec d
      if d > 0: setLen(result, d-1)
    elif isDot(x, b):
      discard "discard the dot"
    elif b[1] > b[0]:
      if result.len > 0 and result[^1] != DirSep:
        result.add DirSep
      result.add substr(x, b[0], b[1])
    inc state, 2

proc canon(x: string): string =
  # - Turn multiple slashes into single slashes.
  # - Resolve '/foo/../bar' to '/bar'.
  # - Remove './' from the path.
  result = newStringOfCap(x.len)
  var state = 0
  canon(x, result, state)

when FileSystemCaseSensitive:
  template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b)
else:
  template `!=?`(a, b: char): bool = a != b

proc relativeTo(full, base: string; sep = DirSep): string =
  if full.len == 0: return ""
  var f, b: PathIter
  var ff = (0, -1)
  var bb = (0, -1) # (int, int)
  result = newStringOfCap(full.len)
  # skip the common prefix:
  while f.hasNext(full) and b.hasNext(base):
    ff = next(f, full)
    bb = next(b, base)
    let diff = ff[1] - ff[0]
    if diff != bb[1] - bb[0]: break
    var same = true
    for i in 0..diff:
      if full[i + ff[0]] !=? base[i + bb[0]]:
        same = false
        break
    if not same: break
    ff = (0, -1)
    bb = (0, -1)
  #  for i in 0..diff:
  #    result.add base[i + bb[0]]

  # /foo/bar/xxx/ -- base
  # /foo/bar/baz  -- full path
  #   ../baz
  # every directory that is in 'base', needs to add '..'
  while true:
    if bb[1] >= bb[0]:
      if result.len > 0 and result[^1] != sep:
        result.add sep
      result.add ".."
    if not b.hasNext(base): break
    bb = b.next(base)

  # add the rest of 'full':
  while true:
    if ff[1] >= ff[0]:
      if result.len > 0 and result[^1] != sep:
        result.add sep
      for i in 0..ff[1] - ff[0]:
        result.add full[i + ff[0]]
    if not f.hasNext(full): break
    ff = f.next(full)

when true:
  proc eqImpl(x, y: string): bool =
    when FileSystemCaseSensitive:
      result = cmpIgnoreCase(canon x, canon y) == 0
    else:
      result = canon(x) == canon(y)

  proc `==`*(x, y: AbsoluteFile): bool = eqImpl(x.string, y.string)
  proc `==`*(x, y: AbsoluteDir): bool = eqImpl(x.string, y.string)
  proc `==`*(x, y: RelativeFile): bool = eqImpl(x.string, y.string)
  proc `==`*(x, y: RelativeDir): bool = eqImpl(x.string, y.string)

  proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
    #assert isAbsolute(base.string)
    assert(not isAbsolute(f.string))
    result = AbsoluteFile newStringOfCap(base.string.len + f.string.len)
    var state = 0
    canon(base.string, result.string, state)
    canon(f.string, result.string, state)

  proc `/`*(base: AbsoluteDir; f: RelativeDir): AbsoluteDir =
    #assert isAbsolute(base.string)
    assert(not isAbsolute(f.string))
    result = AbsoluteDir newStringOfCap(base.string.len + f.string.len)
    var state = 0
    canon(base.string, result.string, state)
    canon(f.string, result.string, state)

  proc relativeTo*(fullPath: AbsoluteFile, baseFilename: AbsoluteDir;
                   sep = DirSep): RelativeFile =
    RelativeFile(relativeTo(fullPath.string, baseFilename.string, sep))

  proc toAbsolute*(file: string; base: AbsoluteDir): AbsoluteFile =
    if isAbsolute(file): result = AbsoluteFile(file)
    else: result = base / RelativeFile file

  proc changeFileExt*(x: AbsoluteFile; ext: string): AbsoluteFile {.borrow.}
  proc changeFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.}

  proc addFileExt*(x: AbsoluteFile; ext: string): AbsoluteFile {.borrow.}
  proc addFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.}

  proc writeFile*(x: AbsoluteFile; content: string) {.borrow.}

when isMainModule and defined(posix):
  doAssert canon"/foo/../bar" == "/bar"
  doAssert canon"foo/../bar" == "bar"

  doAssert canon"/f/../bar///" == "/bar"
  doAssert canon"f/..////bar" == "bar"

  doAssert canon"../bar" == "../bar"
  doAssert canon"/../bar" == "/../bar"

  doAssert canon("foo/../../bar/") == "../bar"
  doAssert canon("./bla/blob/") == "bla/blob"
  doAssert canon(".hiddenFile") == ".hiddenFile"
  doAssert canon("./bla/../../blob/./zoo.nim") == "../blob/zoo.nim"

  doAssert canon("C:/file/to/this/long") == "C:/file/to/this/long"
  doAssert canon("") == ""
  doAssert canon("foobar") == "foobar"
  doAssert canon("f/////////") == "f"

  doAssert relativeTo("/foo/bar//baz.nim", "/foo") == "bar/baz.nim"

  doAssert relativeTo("/Users/me/bar/z.nim", "/Users/other/bad") == "../../me/bar/z.nim"

  doAssert relativeTo("/Users/me/bar/z.nim", "/Users/other") == "../me/bar/z.nim"
  doAssert relativeTo("/Users///me/bar//z.nim", "//Users/") == "me/bar/z.nim"
  doAssert relativeTo("/Users/me/bar/z.nim", "/Users/me") == "bar/z.nim"
  doAssert relativeTo("", "/users/moo") == ""
  doAssert relativeTo("foo", "") == "foo"

  doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim"
  doAssert relativeTo("/foo/bar.nim", "/foo/") == "bar.nim"

when isMainModule and defined(windows):
  let nasty = string(AbsoluteDir(r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\linkedPkgs\pkgB-#head\../../simplePkgs/pkgB-#head/") / RelativeFile"pkgA/module.nim")
  doAssert nasty == r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\simplePkgs\pkgB-#head\pkgA\module.nim"