ntpath.nim 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. # This module is inspired by Python's `ntpath.py` module.
  2. import std/[
  3. strutils,
  4. ]
  5. # Adapted `splitdrive` function from the following commits in Python source
  6. # code:
  7. # 5a607a3ee5e81bdcef3f886f9d20c1376a533df4 (2009): Initial UNC handling (by Mark Hammond)
  8. # 2ba0fd5767577954f331ecbd53596cd8035d7186 (2022): Support for "UNC"-device paths (by Barney Gale)
  9. #
  10. # FAQ: Why use `strip` below? `\\?\UNC` is the start of a "UNC symbolic link",
  11. # which is a special UNC form. Running `strip` differentiates `\\?\UNC\` (a UNC
  12. # symbolic link) from e.g. `\\?\UNCD` (UNCD is the server in the UNC path).
  13. func splitDrive*(p: string): tuple[drive, path: string] =
  14. ## Splits a Windows path into a drive and path part. The drive can be e.g.
  15. ## `C:`. It can also be a UNC path (`\\server\drive\path`).
  16. ##
  17. ## The equivalent `splitDrive` for POSIX systems always returns empty drive.
  18. ## Therefore this proc is only necessary on DOS-like file systems (together
  19. ## with Nim's `doslikeFileSystem` conditional variable).
  20. ##
  21. ## This proc's use case is to extract `path` such that it can be manipulated
  22. ## like a POSIX path.
  23. runnableExamples:
  24. doAssert splitDrive("C:") == ("C:", "")
  25. doAssert splitDrive(r"C:\") == (r"C:", r"\")
  26. doAssert splitDrive(r"\\server\drive\foo\bar") == (r"\\server\drive", r"\foo\bar")
  27. doAssert splitDrive(r"\\?\UNC\server\share\dir") == (r"\\?\UNC\server\share", r"\dir")
  28. result = ("", p)
  29. if p.len < 2:
  30. return
  31. const sep = '\\'
  32. let normp = p.replace('/', sep)
  33. if p.len > 2 and normp[0] == sep and normp[1] == sep and normp[2] != sep:
  34. # is a UNC path:
  35. # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
  36. # \\machine\mountpoint\directory\etc\...
  37. # directory ^^^^^^^^^^^^^^^
  38. let start = block:
  39. const unc = "\\\\?\\UNC" # Length is 7
  40. let idx = min(8, normp.len)
  41. if unc == normp[0..<idx].strip(chars = {sep}, leading = false).toUpperAscii:
  42. 8
  43. else:
  44. 2
  45. let index = normp.find(sep, start)
  46. if index == -1:
  47. return
  48. var index2 = normp.find(sep, index + 1)
  49. # a UNC path can't have two slashes in a row (after the initial two)
  50. if index2 == index + 1:
  51. return
  52. if index2 == -1:
  53. index2 = p.len
  54. return (p[0..<index2], p[index2..^1])
  55. if p[1] == ':':
  56. return (p[0..1], p[2..^1])