1111var TOKEN_WHITESPACE = RegExp ( / [ \t \v \f \r ] + / ) ;
1212var TOKEN_LINE_CONTINUATION = RegExp ( / \\ [ \t ] * $ / ) ;
1313var TOKEN_COMMENT = RegExp ( / ^ # .* $ / ) ;
14+ var TOKEN_ESCAPE_DIRECTIVE = RegExp ( / ^ # [ \t ] * e s c a p e [ \t ] * = [ \t ] * ( .) .* $ / ) ;
15+
1416var 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 ) ;
0 commit comments