-
Notifications
You must be signed in to change notification settings - Fork 2
/
okpaths.nim
60 lines (51 loc) · 2.43 KB
/
okpaths.nim
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
import std/[os, posix, strutils, sets]
if paramCount() < 1 or paramCount() mod 5 != 0: quit """Usage:
okpaths ENVAR [DELIM(:) [ITYPE{bcdpfls}(d) [PERMS{rwx}(x) [DEDUP{FL*}(F)]]]]
echos re-assembled value for $ENVAR delimited by char DELIM where each element
kept is i-node type ITYPE with permissions PERMS & optional de-duplication.
Eg., PATH=`okpaths PATH` keeps only existing (d)irs executable(x) by an invoking
user. DEPDUP starting with 'F' means keep F)irst use, while 'L' keeps L)ast use
& other means no de-dup (this is case-insensitive). So, eval `okpaths PATH` is
nice in rc/init scripts for Unix shells.
Blocks of the 5 params can repeat (since fork&exec add to shell init time).""",0
for shf in countup(0, paramCount()-1, 5):
let delim = if paramCount()>1+shf: paramStr(2+shf)[0] else: ':'
let kinds = if paramCount()>2+shf: paramStr(3+shf) else: "d"
let perms = if paramCount()>3+shf: paramStr(4+shf) else: "rx"
let dedup = if paramCount()>4+shf: paramStr(5+shf).toUpper[0] else: 'F'
func kind(mode: Mode): char =
if mode.S_ISBLK : 'b'
elif mode.S_ISCHR : 'c'
elif mode.S_ISDIR : 'd'
elif mode.S_ISFIFO: 'p'
elif mode.S_ISREG : 'f'
elif (when not defined(windows): mode.S_ISLNK else: false): 'l'
elif (when not defined(windows): mode.S_ISSOCK else: false): 's'
else: '.'
proc perm(perms: string): cint =
if 'r' in perms: result = result or R_OK
if 'w' in perms: result = result or W_OK
if 'x' in perms: result = result or X_OK
let prms = perm(perms)
var res: seq[string] # Result to output (re-joined with delim)
var ids: seq[Ino] # i-node identity; [i] tracks res[i]
var st: Stat
var did: HashSet[Ino]
for e in paramStr(1+shf).getEnv.split(delim):
let ec = e.cstring
if stat(ec, st) == 0 and st.st_mode.kind in kinds and access(ec, prms) == 0:
if dedup == 'F': # F)irst retention
if st.st_ino notin did: # Only add if have not already
res.add e
did.incl st.st_ino
elif dedup == 'L': # L)ast retention
if st.st_ino in did: # Already added; First delete [old]
let ino = ids.find(st.st_ino)
res.delete ino
ids.delete ino
did.incl st.st_ino # Add it
ids.add st.st_ino
res.add e
else: # Not de-duplicating
res.add e
echo paramStr(1+shf),"=",join(res, $delim)