Skip to content

Commit 01e7d6b

Browse files
authored
Merge pull request #2 from joyent/GH-1
TritonDataCenter/sdc-docker#1 Escape parser directive is not handled
2 parents ba6b4ce + 26b07de commit 01e7d6b

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

parser.js

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
var TOKEN_WHITESPACE = RegExp(/[\t\v\f\r ]+/);
1212
var TOKEN_LINE_CONTINUATION = RegExp(/\\[ \t]*$/);
1313
var TOKEN_COMMENT = RegExp(/^#.*$/);
14+
var TOKEN_ESCAPE_DIRECTIVE = RegExp(/^#[ \t]*escape[ \t]*=[ \t]*(.).*$/);
15+
1416
var errDockerfileNotStringArray = new Error('When using JSON array syntax, '
1517
+ 'arrays must be comprised of strings only.');
1618

@@ -19,6 +21,11 @@ function isSpace(s) {
1921
return s.match(/^\s$/);
2022
}
2123

24+
// Escape special regular expression characters in the provided string.
25+
function regexEscape(str) {
26+
return str.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&");
27+
}
28+
2229
/**
2330
* Parsers are dispatch calls that parse a single unit of text into a cmd.args
2431
* object which contains the statement details. Dockerfiles have varied (but not
@@ -294,8 +301,10 @@ function splitCommand(line) {
294301
}
295302

296303
// parse a line and return the remainder.
297-
function parseLine(line, lineno) {
304+
function parseLine(line, lineno, options) {
298305
var command = null;
306+
var lineContinuationRegex = (options && options.lineContinuationRegex
307+
|| TOKEN_LINE_CONTINUATION);
299308

300309
line = line.trim();
301310

@@ -310,9 +319,9 @@ function parseLine(line, lineno) {
310319
return { command: command, remainder: '' };
311320
}
312321

313-
if (line.match(TOKEN_LINE_CONTINUATION)) {
322+
if (line.match(lineContinuationRegex)) {
314323
// Line continues on next line.
315-
var remainder = line.replace(TOKEN_LINE_CONTINUATION, '', 'g');
324+
var remainder = line.replace(lineContinuationRegex, '', 'g');
316325
return { command: null, remainder: remainder };
317326
}
318327

@@ -361,7 +370,10 @@ function parse(contents, options) {
361370
var line;
362371
var lineno;
363372
var lines = contents.split(/[\r?\n]/);
373+
var lookingForDirectives = true;
374+
var parseOptions = {};
364375
var parseResult;
376+
var regexMatch;
365377
var remainder = '';
366378
var includeComments = options && options['includeComments'];
367379

@@ -373,7 +385,30 @@ function parse(contents, options) {
373385
line = lines[i];
374386
}
375387

376-
parseResult = parseLine(line, lineno);
388+
if (lookingForDirectives) {
389+
// Handle the parser directive '# escape=<char>. Parser directives
390+
// must precede any builder instruction or other comments, and
391+
// cannot be repeated.
392+
regexMatch = line.match(TOKEN_ESCAPE_DIRECTIVE);
393+
if (regexMatch) {
394+
if (regexMatch[1] != '`' && regexMatch[1] != '\\') {
395+
throw new Error('invalid ESCAPE "' + regexMatch[1]
396+
+ '". Must be ` or \\');
397+
}
398+
if (parseOptions.lineContinuationRegex) {
399+
throw new Error(
400+
'only one escape parser directive can be used');
401+
}
402+
parseOptions.lineContinuationRegex = RegExp(
403+
regexEscape(regexMatch[1]) + '[ \t]*$');
404+
continue;
405+
}
406+
}
407+
// Once a comment, empty line or builder instruction has been processed,
408+
// Docker no longer looks for parser directives.
409+
lookingForDirectives = false;
410+
411+
parseResult = parseLine(line, lineno, parseOptions);
377412
if (parseResult.command) {
378413
if (parseResult.command.name !== 'COMMENT' || includeComments) {
379414
commands.push(parseResult.command);

test/test_parser.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,34 @@ tape('multiline', function (t) {
136136

137137
t.end();
138138
});
139+
140+
141+
tape('escape parser directive', function (t) {
142+
var lines = [
143+
'#escape = `',
144+
'',
145+
'FROM image',
146+
'MAINTAINER [email protected]',
147+
'ENV GOPATH `',
148+
'\go',
149+
'MAINTAINER "Docker `',
150+
'IO <io@`',
151+
'docker.com>"'
152+
];
153+
var dockerFile = lines.join('\n');
154+
155+
var commands = dockerfileParser.parse(dockerFile);
156+
t.equal(commands.length, 4);
157+
t.deepEqual(commands[2].args, { GOPATH: 'go' });
158+
t.equal(commands[3].args, '"Docker IO <[email protected]>"');
159+
160+
// Try again, this time using the default escape directive.
161+
dockerFile = dockerFile.replace(/`/g, '\\');
162+
163+
commands = dockerfileParser.parse(dockerFile);
164+
t.equal(commands.length, 4);
165+
t.deepEqual(commands[2].args, { GOPATH: 'go' });
166+
t.equal(commands[3].args, '"Docker IO <[email protected]>"');
167+
168+
t.end();
169+
});

0 commit comments

Comments
 (0)