-
Notifications
You must be signed in to change notification settings - Fork 2
/
ft.nim
92 lines (87 loc) · 4.22 KB
/
ft.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
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
import std/[strutils, posix], cligen/[osUt, statx]
when not declared(stdout): import std/syncio
proc eval(ch: char, st: Statx, euid: Uid, egid: Gid): bool =
let m = Mode(st.stx_mode)
case ch
of 'e': st.stx_blksize == 1 # exists in any way
of 'b': m.S_ISBLK # is block special
of 'c': m.S_ISCHR # is character special
of 'd': m.S_ISDIR # is a directory
of 'f': m.S_ISREG # is a regular file
of 'l','L': m.S_ISLNK # is a symbolic Link
of 'p': m.S_ISFIFO # is a named pipe {aka FIFO}
of 'S': m.S_ISSOCK # is a Socket
of 's': st.stx_size > 0 # has a size greater than zero
of 'h': st.stx_nlink > 1 # is a hard link; nlink > 1
of 'N': st.stx_mtime > st.stx_atime # New; modify time > access time
of 'u': (m and 0o4000) != 0 # its set-user-ID bit is set
of 'g': (m and 0o2000) != 0 # is set-group-ID
of 'k': (m and 0o1000) != 0 # has its sticky bit set
of 'O': st.stx_uid == euid.uint32 # is Owned by the effective user ID
of 'G': st.stx_gid == egid.uint32 # is owned by effective Group ID
of 'r': (m and 0o4) != 0 # user can read; Q: Add access(2) way?
of 'w': (m and 0o2) != 0 # user can write
of 'x': (m and 0o1) != 0 # user can execute | traverse
of 'R': (m and 0o400) != 0 # world can read
of 'W': (m and 0o200) != 0 # world can write
of 'X': (m and 0o100) != 0 # world can execute | traverse
of 'A': (m and 0o040) != 0 # group can read
of 'I': (m and 0o020) != 0 # group can write
of 'E': (m and 0o010) != 0 # group can execute | traverse
else: false # Any unknown char => false
proc maybeFlip(flip, val: bool): bool = (if flip: not val else: val)
proc isType*(path, expr: string; euid: Uid, egid: Gid): bool =
var st: Statx # Re-purpose stx_blksize as an lstat-Ok code
st.stx_blksize = if lstat(path, st) == 0: 1u32 else: 0u32
var flip = false
result = true
for ch in expr:
case ch
of '^': flip = not flip
else:
result = result and maybeFlip(flip, ch.eval(st, euid, egid))
if not result: return
flip = false
proc ft*(file="", delim='\n', term='\n', pattern="$1", quiet=false, expr="e",
paths: seq[string]): int =
## Batch (in both predicates & targets) `test` / `[` . Emit subset of paths
## that pass `expr`. E.g.: `$(ft -eL \*)` =~ Zsh extended glob `\*(@)`. Can
## also read stdin as in `find -type f|ft -ew`. (Yes, could cobble together
## with GNU `find -files0-from` less tersely & with more quoting concerns.)
## Maybe counter-intuitively, exit with status = match count (0=NONE).
let (euid, egid) = (geteuid(), getegid()) # only do this *once*
let it = both(paths, fileStrings(file, delim))
var st = 0
for path in it():
if isType(path, expr, euid, egid):
if not quiet: stdout.write pattern % [path], term
inc st
st
when isMainModule: import cligen;include cligen/mergeCfgEnv;dispatch ft, help={
"file" : "optional input ( `\"-\"` | !tty = ``stdin`` )",
"delim" : "input file delimiter; `\\\\0` -> NUL",
"term" : "output path terminator",
"pattern": "emit a \\$1-using pattern; E.g. \"match:\\$1\"",
"quiet" : "Do not emit; Just count as exit status",
"expr" :"""Concatenated extended one-letter test(1) codes
e (e)xists in any way
b is (b)lock special
c is (c)haracter special
d is a (d)irectory
f is a regular (f)ile
l|L is a symbolic (l)ink; NOTE: h differs!
p is a named (p)ipe {aka FIFO}
S is a (S)ocket;CASE differs from ls/find
s has a (s)ize greater than zero
h is a (h)ard link; Link count > 1
N (N)ew; modify time > access time
k has its stic(k)y bit set
u its set-(u)ser-ID bit is set
g is set-(g)roup-ID
O is (O)wned by the effective user ID
G is owned by effective (G)roup ID
r|R|A user|World|Group can (r)ead
w|W|I user|World|Group can (w)rite
x|X|E user|World|Group can e(x)ecute|travers
In all cases a file must exist for 'true'
Codes are logically ANDed; '^' prefix => NOT"""}