Pewarta Proxy

📋 Server Request & Proxy Logs

Loading active proxy settings...
Developed by Khairul Adha (adhexa.id)
Mengambil log server...

⚙️ Pengaturan Proxy Target

Skip to content

Commit b78d19c

Browse files
committed
[New] parse: support redirecting output despite noclobber (>|)
`beep >| boop` was tokenized as `>` and `|`, which reads as a redirection followed by a pipe. `>|` (POSIX `CLOBBER`) is now a single `{ op: '>|' }`. Since `>|` is matched first, `>||` parses as `>|` and `|` rather than `>` and `||`, and `>|&` as `>|` and `&` rather than `>` and `|&`. `>>|` still parses as `>>` and `|`. `quote` accepts the new operator, and `ControlOperator['op']` includes it. Code that looks for `>` to find output redirection must also look for `>|`.
1 parent 21cc333 commit b78d19c

6 files changed

Lines changed: 30 additions & 4 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ commands.
137137

138138
Each entry of `args` may be a string, or one of the object shapes that
139139
`parse` emits: `{ op }` (where `op` is one of the control operators
140-
`||`, `&&`, `;;`, `|&`, `<(`, `<<<`, `<<-`, `<<`, `>>`, `>&`, `<&`,
141-
`<>`, `&`, `;`, `(`, `)`, `|`, `<`, `>`),
140+
`||`, `&&`, `;;`, `|&`, `<(`, `<<<`, `<<-`, `<<`, `>>`, `>&`, `>|`,
141+
`<&`, `<>`, `&`, `;`, `(`, `)`, `|`, `<`, `>`),
142142
`{ op: 'glob', pattern }`, or `{ comment }`. Any
143143
other object shape, an unrecognized `op`, a `pattern`/`comment`
144144
containing line terminators, or a string containing line terminators

‎parse.d.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
declare namespace parse {
22
/** A shell control operator. */
33
export interface ControlOperator {
4-
op: '||' | '&&' | ';;' | '|&' | '<(' | '<<<' | '<<-' | '<<' | '>>' | '>&' | '<&' | '<>' | '&' | ';' | '(' | ')' | '|' | '<' | '>';
4+
op: '||' | '&&' | ';;' | '|&' | '<(' | '<<<' | '<<-' | '<<' | '>>' | '>&' | '>|' | '<&' | '<>' | '&' | ';' | '(' | ')' | '|' | '<' | '>';
55
}
66

77
/** A glob pattern parsed from the shell command. */

‎parse.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var CONTROL = /** @type {const} */ ('(?:') + /** @type {const} */ ([
2020
'\\<\\<-',
2121
'\\<\\<(?!\\()', // `<<(` stays `<` and `<(`, as zsh reads it; other shells reject it
2222
'>>',
23-
'>\\&',
23+
'>[&|]', // `>&` and `>|`
2424
'<[&>]', // `<&` and `<>`
2525
'[&;()|<>]'
2626
]).join(/** @type {const} */ ('|')) + /** @type {const} */ (')');

‎quote.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ var OPS = /** @type {const} */ ([
1414
'<<',
1515
'>>',
1616
'>&',
17+
'>|',
1718
'<&',
1819
'<>',
1920
'&',

‎test/op.js‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,25 @@ test('opening a file for reading and writing', function (t) {
175175
t.end();
176176
});
177177

178+
test('redirecting output despite noclobber', function (t) {
179+
t.same(parse('beep >| boop'), ['beep', { op: '>|' }, 'boop']);
180+
t.same(parse('beep>|boop'), ['beep', { op: '>|' }, 'boop']);
181+
t.same(parse('beep 2>|boop'), ['beep', '2', { op: '>|' }, 'boop']);
182+
183+
t.same(parse('beep > | boop'), ['beep', { op: '>' }, { op: '|' }, 'boop'], 'separated');
184+
t.same(parse('beep \\>\\| boop'), ['beep', '>|', 'boop'], 'escaped');
185+
t.same(parse('beep ">|" boop'), ['beep', '>|', 'boop'], 'quoted');
186+
t.same(parse('beep >| boop', {}, { escape: '^' }), ['beep', { op: '>|' }, 'boop'], 'with a custom escape');
187+
t.same(parse('beep ^>^| boop', {}, { escape: '^' }), ['beep', '>|', 'boop'], 'escaped with a custom escape');
188+
189+
t.same(parse('beep >|| boop'), ['beep', { op: '>|' }, { op: '|' }, 'boop'], '`>|` wins over `||`');
190+
t.same(parse('beep >|& boop'), ['beep', { op: '>|' }, { op: '&' }, 'boop'], '`>|` wins over `|&`');
191+
t.same(parse('beep <>| boop'), ['beep', { op: '<>' }, { op: '|' }, 'boop'], '`<>` wins over `>|`');
192+
t.same(parse('beep |> boop'), ['beep', { op: '|' }, { op: '>' }, 'boop'], '`|>` is not an operator');
193+
194+
t.end();
195+
});
196+
178197
test('glob patterns', function (t) {
179198
t.same(
180199
parse('tap test/*.test.js'),

‎test/quote.js‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ test('quote ops: opening a file for reading and writing', function (t) {
136136
t.end();
137137
});
138138

139+
test('quote ops: redirecting output despite noclobber', function (t) {
140+
t.equal(quote([{ op: '>|' }]), '\\>\\|');
141+
t.equal(quote(parse('beep >| boop')), 'beep \\>\\| boop');
142+
t.end();
143+
});
144+
139145
test('quote ops: rejects line terminators (GHSA-w7jw-789q-3m8p)', function (t) {
140146
t['throws'](function () { quote([{ op: ';\nid' }]); }, TypeError, 'newline in op');
141147
t['throws'](function () { quote([{ op: ';\rid' }]); }, TypeError, 'carriage return in op');

0 commit comments

Comments
 (0)