-
Notifications
You must be signed in to change notification settings - Fork 0
Implement find -exec (v2 deprecated) #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ffd5112
fdfcda6
45331b5
b3d04ee
6188545
10e4148
682a62b
2691ffb
7335e90
27b3d5e
139d228
27c52f1
1698078
e823302
c95fb6c
cd0786f
4fc005f
5d1151a
4afc9fb
b187f82
f87d171
793f6da
a081c95
92d809d
289e635
c75c0d9
935ffd1
ccea45b
89730d0
ee3c48a
00825cc
96351e6
b6e4e11
7386574
e9e8085
fcbda04
2e3d805
a939740
43b8b56
c1a2380
3d540c5
d6da039
e2e511e
b29a889
3665f7c
cdb1ad8
b961c4f
00e872e
fbe0aaf
1ecb071
894fcd4
6bc9c94
e21613f
1fd9265
5859439
9203b69
49d858a
11bf6c0
7f957a1
af78619
ff78c00
f96ebbf
ba66935
7375410
257315f
90dd433
a9fbd9a
2396dab
f262c4d
c3b4bfa
cced11b
4a43391
f4b613a
2cd4a86
9f64956
f45ea57
32c6ee0
e4e0849
5e11ab8
c33f0c0
fb62074
3f4af8d
3e9f677
db40685
14ffe75
62bcc0c
7131084
697f917
d7d3787
2374b34
3d656cd
bfa5228
988ef6e
936ed41
12f4464
5e08194
265b822
5a34e27
7a29efb
5719bce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,24 +24,26 @@ const ( | |
| type exprKind int | ||
|
|
||
| const ( | ||
| exprName exprKind = iota // -name pattern | ||
| exprIName // -iname pattern | ||
| exprPath // -path pattern | ||
| exprIPath // -ipath pattern | ||
| exprType // -type c | ||
| exprSize // -size n[cwbkMG] | ||
| exprEmpty // -empty | ||
| exprNewer // -newer file | ||
| exprMtime // -mtime n | ||
| exprMmin // -mmin n | ||
| exprPrint // -print | ||
| exprPrint0 // -print0 | ||
| exprPrune // -prune | ||
| exprTrue // -true | ||
| exprFalse // -false | ||
| exprAnd // expr -a expr or expr expr (implicit) | ||
| exprOr // expr -o expr | ||
| exprNot // ! expr or -not expr | ||
| exprName exprKind = iota // -name pattern | ||
| exprIName // -iname pattern | ||
| exprPath // -path pattern | ||
| exprIPath // -ipath pattern | ||
| exprType // -type c | ||
| exprSize // -size n[cwbkMG] | ||
| exprEmpty // -empty | ||
| exprNewer // -newer file | ||
| exprMtime // -mtime n | ||
| exprMmin // -mmin n | ||
| exprPrint // -print | ||
| exprPrint0 // -print0 | ||
| exprPrune // -prune | ||
| exprExec // -exec command {} ; | ||
| exprExecDir // -execdir command {} ; | ||
| exprTrue // -true | ||
| exprFalse // -false | ||
| exprAnd // expr -a expr or expr expr (implicit) | ||
| exprOr // expr -o expr | ||
| exprNot // ! expr or -not expr | ||
| ) | ||
|
|
||
| // cmpOp represents a comparison operator for numeric predicates. | ||
|
|
@@ -73,21 +75,28 @@ type sizeUnit struct { | |
| unit byte // one of: c w b k M G (default 'b' if omitted) | ||
| } | ||
|
|
||
| // maxExecArgs limits the number of arguments that can be accumulated in | ||
| // -exec/-execdir batch mode (+) to prevent memory exhaustion. | ||
| const maxExecArgs = 10000 | ||
|
|
||
| // expr is a node in the find expression AST. | ||
| type expr struct { | ||
| kind exprKind | ||
| strVal string // pattern for name/iname/path/ipath, type char, file path for newer | ||
| sizeVal sizeUnit // for -size | ||
| numVal int64 // for -mtime, -mmin | ||
| numCmp cmpOp // comparison operator for numeric predicates | ||
| left *expr // for and/or | ||
| right *expr // for and/or | ||
| operand *expr // for not | ||
| kind exprKind | ||
| strVal string // pattern for name/iname/path/ipath, type char, file path for newer | ||
| sizeVal sizeUnit // for -size | ||
| numVal int64 // for -mtime, -mmin | ||
| numCmp cmpOp // comparison operator for numeric predicates | ||
| left *expr // for and/or | ||
| right *expr // for and/or | ||
| operand *expr // for not | ||
| execArgs []string // for -exec/-execdir: command and arguments (with {} placeholder) | ||
| execBatch bool // for -exec/-execdir: true if terminated by + (batch mode) | ||
| } | ||
|
|
||
| // isAction returns true if this expression is an output action. | ||
| func (e *expr) isAction() bool { | ||
| return e.kind == exprPrint || e.kind == exprPrint0 | ||
| return e.kind == exprPrint || e.kind == exprPrint0 || | ||
| e.kind == exprExec || e.kind == exprExecDir | ||
| } | ||
|
|
||
| // hasAction checks if any node in the expression tree is an action. | ||
|
|
@@ -120,8 +129,6 @@ type parseResult struct { | |
|
|
||
| // blocked predicates that are forbidden for sandbox safety. | ||
| var blockedPredicates = map[string]string{ | ||
| "-exec": "arbitrary command execution is blocked", | ||
| "-execdir": "arbitrary command execution is blocked", | ||
| "-delete": "file deletion is blocked", | ||
| "-ok": "interactive execution is blocked", | ||
| "-okdir": "interactive execution is blocked", | ||
|
|
@@ -311,6 +318,10 @@ func (p *parser) parsePrimary() (*expr, error) { | |
| return p.parseNumericPredicate(exprMtime) | ||
| case "-mmin": | ||
| return p.parseNumericPredicate(exprMmin) | ||
| case "-exec": | ||
| return p.parseExecPredicate(exprExec) | ||
| case "-execdir": | ||
| return p.parseExecPredicate(exprExecDir) | ||
| case "-print": | ||
| return &expr{kind: exprPrint}, nil | ||
| case "-print0": | ||
|
|
@@ -459,6 +470,60 @@ func (p *parser) parseDepthOption(isMax bool) (*expr, error) { | |
| return &expr{kind: exprTrue}, nil | ||
| } | ||
|
|
||
| // parseExecPredicate parses -exec/-execdir arguments. | ||
| // Syntax: -exec command [args...] ; | ||
| // | ||
| // -exec command [args...] {} + | ||
| // | ||
| // The `;` terminator must be a separate argument (the shell handles `\;`). | ||
| // The `+` terminator enables batch mode (multiple files per invocation); | ||
| // in batch mode `{}` must be the last argument before `+`. | ||
| // `{}` is optional in `;` mode — when absent, the command runs without | ||
| // the matched path in its arguments (matching GNU find behaviour). | ||
| func (p *parser) parseExecPredicate(kind exprKind) (*expr, error) { | ||
| name := "-exec" | ||
| if kind == exprExecDir { | ||
| name = "-execdir" | ||
| } | ||
| if p.pos >= len(p.args) { | ||
| return nil, fmt.Errorf("find: %s: missing command", name) | ||
| } | ||
|
|
||
| // maxExecCmdArgs limits the number of fixed arguments in an -exec/-execdir | ||
| // command template to prevent pathological parser inputs. | ||
| const maxExecCmdArgs = 1024 | ||
|
|
||
| var cmdArgs []string | ||
| placeholderCount := 0 | ||
| for p.pos < len(p.args) { | ||
| tok := p.args[p.pos] | ||
| p.pos++ | ||
|
|
||
| if tok == ";" { | ||
| if len(cmdArgs) == 0 { | ||
| return nil, fmt.Errorf("find: %s: missing command", name) | ||
| } | ||
| return &expr{kind: kind, execArgs: cmdArgs, execBatch: false}, nil | ||
| } | ||
| if tok == "+" && placeholderCount > 0 && len(cmdArgs) > 0 && cmdArgs[len(cmdArgs)-1] == "{}" { | ||
| // Batch mode: {} must be the last arg before +. | ||
| // GNU find rejects multiple {} in batch mode. | ||
|
Comment on lines
+508
to
+510
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This Useful? React with 👍 / 👎. |
||
| if placeholderCount > 1 { | ||
| return nil, fmt.Errorf("find: %s: only one instance of '{}' is supported with -exec ... +", name) | ||
| } | ||
| return &expr{kind: kind, execArgs: cmdArgs, execBatch: true}, nil | ||
| } | ||
| if strings.Contains(tok, "{}") { | ||
| placeholderCount++ | ||
AlexandreYang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| cmdArgs = append(cmdArgs, tok) | ||
AlexandreYang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if len(cmdArgs) > maxExecCmdArgs { | ||
| return nil, fmt.Errorf("find: %s: too many arguments (limit %d)", name, maxExecCmdArgs) | ||
| } | ||
| } | ||
| return nil, fmt.Errorf("find: missing terminator for %s (expected ';' or '+')", name) | ||
| } | ||
|
|
||
| // parseSize parses a -size argument like "+10k", "-5M", "100c". | ||
| func parseSize(s string) (sizeUnit, error) { | ||
| if len(s) == 0 { | ||
|
|
@@ -541,6 +606,10 @@ func (k exprKind) String() string { | |
| return "-or" | ||
| case exprNot: | ||
| return "-not" | ||
| case exprExec: | ||
| return "-exec" | ||
| case exprExecDir: | ||
| return "-execdir" | ||
| default: | ||
| return "unknown" | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.