From d4466dab2f6081d21af0ccaa0f0bf635639c5c42 Mon Sep 17 00:00:00 2001 From: litlighilit Date: Wed, 29 Jan 2025 18:24:43 +0800 Subject: [PATCH 1/7] feat(os): getHomeDir(username) --- lib/std/private/osappdirs.nim | 92 ++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim index d0c2f7afccada..5cbabe8b4b9e0 100644 --- a/lib/std/private/osappdirs.nim +++ b/lib/std/private/osappdirs.nim @@ -4,9 +4,83 @@ include system/inclrtl import std/envvars import std/private/ospaths2 + +when defined(unix): # XXX: suitable? + import std/posix + + proc getPwDir(p: ptr Passwd, res: var string): bool = + let cstr = p.pw_dir + if cstr.isNil: + return + + res = $cstr + result = true + + const DEFAULT_BUFFER_SIZE = 1024 + proc getHomeDir*(username: string): string {.rtl, extern: "nos$1OfUser", + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the home directory of the user `username`. + ## + ## Returns an empty string if `pwd.getpwnam(username).pw_dir` is `NULL` + ## (Most likely under vxworks) + ## + ## This proc is wrapped by the `expandTilde proc`_ + ## for the convenience of processing paths coming from user configuration files. + ## + # replace "~username" with `pwd.getpwnam(username).pw_dir` + # translated from CPython's pwd.getpwnam, a.k.a. pwd_getpwnam_impl in Modules/pwdmodule.c + var bufsize = sysconf(SC_GETPW_R_SIZE_MAX) + if bufsize == -1: + bufsize = DEFAULT_BUFFER_SIZE + + let name_chars = cstring username + + var + nomem = false + p: ptr Passwd = nil + buf: cstring = nil + when declared(getpwnam_r): + var pwd: Passwd + while true: + let buf2 = cast[cstring](reallocShared(buf, bufsize)) + if buf2.isNil: + p = nil + nomem = true + break + + buf = buf2 + let status = getpwnam_r(name_chars, pwd.addr, buf, bufsize, p.addr) + if status != 0: + p = nil + if not p.isNil or status != ERANGE: + break + + if bufsize > int.high shr 1: + nomem = true + break + + bufsize = bufsize shl 1 + else: + p = getpwnam(name_chars) + + defer: deallocShared buf + if p.isNil: + if nomem: + raise newException(OutOfMemDefect, "") + else: + raise newException(KeyError, + "getpwnam(): name not found: " & username.repr) + else: + result = "" + if not getPwDir(p, result): + return + + proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the home directory of the current user. + ## + ## Returns an empty string if failed to get an valid result ## ## This proc is wrapped by the `expandTilde proc`_ ## for the convenience of processing paths coming from user configuration files. @@ -22,8 +96,22 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", import std/os assert getHomeDir() == expandTilde("~") - when defined(windows): return getEnv("USERPROFILE") & "\\" - else: return getEnv("HOME") & "/" + template ret(res) = + result = res + if result != "": + result.add DirSep + return + when defined(windows): ret getEnv("USERPROFILE") + elif declared(getpwuid): + if existsEnv("HOME"): + ret getEnv("HOME") + let pwd = getpwuid(getuid()) + if pwd.isNil: + return + let cstr = pwd.pw_dir + if cstr.isNil: return + ret $cstr + else: ret getEnv("HOME") proc getDataDir*(): string {.rtl, extern: "nos$1" tags: [ReadEnvEffect, ReadIOEffect].} = From b9c9e902f5f4776100219d93e02a52c9583d8f3e Mon Sep 17 00:00:00 2001 From: litlighilit Date: Wed, 29 Jan 2025 18:26:09 +0800 Subject: [PATCH 2/7] feat(os): expandTilde supports "~bob". closes #24655 --- lib/pure/os.nim | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 1fac8f87447f5..f69dfa1f6da2d 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -127,14 +127,38 @@ proc expandTilde*(path: string): string {. assert expandTilde("~/foo/bar") == getHomeDir() / "foo/bar" assert expandTilde("/foo/bar") == "/foo/bar" + template tryGetHomeOrRet = + result = getHomeDir() + if result == "": + result = path + return + if len(path) == 0 or path[0] != '~': result = path elif len(path) == 1: - result = getHomeDir() + tryGetHomeOrRet elif (path[1] in {DirSep, AltSep}): - result = getHomeDir() / path.substr(2) + tryGetHomeOrRet + result = result / path.substr(2) + elif compiles(getHomeDir("bob")): + # handle path beginning with `~bob` and `~bob/` + # which means home of bob + var i = path.find(DirSep, 1) + if i < 0: + i = len(path) + + let + name = path[1.. Date: Wed, 29 Jan 2025 18:47:24 +0800 Subject: [PATCH 3/7] fixup: expandTilde might raise unlisted KeyError --- lib/std/private/osappdirs.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim index 5cbabe8b4b9e0..56ccd48be8e77 100644 --- a/lib/std/private/osappdirs.nim +++ b/lib/std/private/osappdirs.nim @@ -67,9 +67,9 @@ when defined(unix): # XXX: suitable? if p.isNil: if nomem: raise newException(OutOfMemDefect, "") - else: - raise newException(KeyError, - "getpwnam(): name not found: " & username.repr) + #else: ...KeyError "getpwnam(): name not found: " & username.repr + # XXX: do not raise KeyError, as it used to raise no CatchableError. + # e.g. `osproc.findExe(data.sysCommand, true, ExeExts)` expects so else: result = "" if not getPwDir(p, result): From a7ec8536e3539032943bd84f27f58c581e9dacab Mon Sep 17 00:00:00 2001 From: litlighilit Date: Fri, 31 Jan 2025 13:16:31 +0800 Subject: [PATCH 4/7] fixup: getHomeDir(username): reallocShared may undefined --- lib/std/private/osappdirs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim index 56ccd48be8e77..be53769804ba0 100644 --- a/lib/std/private/osappdirs.nim +++ b/lib/std/private/osappdirs.nim @@ -39,7 +39,7 @@ when defined(unix): # XXX: suitable? nomem = false p: ptr Passwd = nil buf: cstring = nil - when declared(getpwnam_r): + when declared(getpwnam_r) and declared(reallocShared): var pwd: Passwd while true: let buf2 = cast[cstring](reallocShared(buf, bufsize)) From 86108dec8cc90515cecb8dfe6ecff3c358384979 Mon Sep 17 00:00:00 2001 From: litlighilit Date: Fri, 31 Jan 2025 16:55:28 +0800 Subject: [PATCH 5/7] fixup: getHomeDir(username): deallocShared may undefined --- lib/std/private/osappdirs.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim index be53769804ba0..cd4bbd4cfac82 100644 --- a/lib/std/private/osappdirs.nim +++ b/lib/std/private/osappdirs.nim @@ -60,10 +60,10 @@ when defined(unix): # XXX: suitable? break bufsize = bufsize shl 1 + defer: deallocShared buf else: p = getpwnam(name_chars) - - defer: deallocShared buf + if p.isNil: if nomem: raise newException(OutOfMemDefect, "") From ac22f7c7a0819641cab00d442d99909fe5732902 Mon Sep 17 00:00:00 2001 From: litlighilit Date: Fri, 31 Jan 2025 17:02:40 +0800 Subject: [PATCH 6/7] fixup: not compile on Windows --- lib/pure/os.nim | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index f69dfa1f6da2d..7842c16277e20 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -140,26 +140,27 @@ proc expandTilde*(path: string): string {. elif (path[1] in {DirSep, AltSep}): tryGetHomeOrRet result = result / path.substr(2) - elif compiles(getHomeDir("bob")): - # handle path beginning with `~bob` and `~bob/` - # which means home of bob - var i = path.find(DirSep, 1) - if i < 0: - i = len(path) + else: + when compiles(getHomeDir("bob")): + # handle path beginning with `~bob` and `~bob/` + # which means home of bob + var i = path.find(DirSep, 1) + if i < 0: + i = len(path) - let - name = path[1.. Date: Fri, 31 Jan 2025 18:34:27 +0800 Subject: [PATCH 7/7] fixup: SC_GETPW_R_SIZE_MAX undefined on MacOS,freertos --- lib/std/private/osappdirs.nim | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim index cd4bbd4cfac82..ce08a10d5984e 100644 --- a/lib/std/private/osappdirs.nim +++ b/lib/std/private/osappdirs.nim @@ -29,17 +29,20 @@ when defined(unix): # XXX: suitable? ## # replace "~username" with `pwd.getpwnam(username).pw_dir` # translated from CPython's pwd.getpwnam, a.k.a. pwd_getpwnam_impl in Modules/pwdmodule.c - var bufsize = sysconf(SC_GETPW_R_SIZE_MAX) - if bufsize == -1: - bufsize = DEFAULT_BUFFER_SIZE - let name_chars = cstring username var nomem = false p: ptr Passwd = nil - buf: cstring = nil - when declared(getpwnam_r) and declared(reallocShared): + + when declared(getpwnam_r) and declared(SC_GETPW_R_SIZE_MAX) and + declared(reallocShared): + var + buf: cstring = nil + bufsize = sysconf(SC_GETPW_R_SIZE_MAX) + if bufsize == -1: + bufsize = DEFAULT_BUFFER_SIZE + var pwd: Passwd while true: let buf2 = cast[cstring](reallocShared(buf, bufsize))