@@ -375,99 +375,97 @@ func (r *FileDiffReader) ReadExtendedHeaders() ([]string, error) {
375
375
// readQuotedFilename extracts a quoted filename from the beginning of a string,
376
376
// returning the unquoted filename and any remaining text after the filename.
377
377
func readQuotedFilename (text string ) (value string , remainder string , err error ) {
378
- if text [0 ] != '"' {
379
- panic ( "caller must ensure filename is quoted! " + text )
378
+ if text == "" || text [0 ] != '"' {
379
+ return "" , "" , fmt . Errorf ( `string must start with a '"': %s` , text )
380
380
}
381
381
382
382
// The end quote is the first quote NOT preceeded by an uneven number of backslashes.
383
- var i , j int
384
- for i = 1 ; i < len ( text ); i ++ {
385
- if text [ i ] == '"' {
386
- // walk backwards and find first non-backslash
387
- for j = i - 1 ; j > 0 && text [j ] == '\\' ; j -- {
388
- }
389
- numberOfBackslashes := i - j - 1
390
- if numberOfBackslashes % 2 == 0 {
391
- break
392
- }
383
+ numberOfBackslashes := 0
384
+ for i , c := range text {
385
+ if c == '"' && i > 0 && numberOfBackslashes % 2 == 0 {
386
+ value , err = strconv . Unquote ( text [: i + 1 ])
387
+ remainder = text [i + 1 :]
388
+ return
389
+ } else if c == '\\' {
390
+ numberOfBackslashes ++
391
+ } else {
392
+ numberOfBackslashes = 0
393
393
}
394
394
}
395
- if i == len (text ) {
396
- return "" , "" , fmt .Errorf (`end of string found while searching for '"': %s` , text )
397
- }
398
-
399
- value , err = strconv .Unquote (text [:i + 1 ])
400
- remainder = text [i + 1 :]
401
- return
395
+ return "" , "" , fmt .Errorf (`end of string found while searching for '"': %s` , text )
402
396
}
403
397
404
398
// parseDiffGitArgs extracts the two filenames from a 'diff --git' line.
405
- func parseDiffGitArgs (diffArgs string ) (bool , string , string ) {
399
+ // Returns false on syntax error, true if syntax is valid. Even with a
400
+ // valid syntax, it may be impossible to extract filenames; if so, the
401
+ // function returns ("", "", true).
402
+ func parseDiffGitArgs (diffArgs string ) (string , string , bool ) {
406
403
length := len (diffArgs )
407
404
if length < 3 {
408
- return false , "" , ""
405
+ return "" , "" , false
409
406
}
410
407
411
408
if diffArgs [0 ] != '"' && diffArgs [length - 1 ] != '"' {
412
409
// Both filenames are unquoted.
413
410
firstSpace := strings .IndexByte (diffArgs , ' ' )
414
411
if firstSpace <= 0 || firstSpace == length - 1 {
415
- return false , "" , ""
412
+ return "" , "" , false
416
413
}
417
414
418
415
secondSpace := strings .IndexByte (diffArgs [firstSpace + 1 :], ' ' )
419
416
if secondSpace == - 1 {
420
417
if diffArgs [firstSpace + 1 ] == '"' {
421
- return false , "" , ""
418
+ // The second filename begins with '"', but doesn't end with one.
419
+ return "" , "" , false
422
420
}
423
- return true , diffArgs [:firstSpace ], diffArgs [firstSpace + 1 :]
421
+ return diffArgs [:firstSpace ], diffArgs [firstSpace + 1 :], true
424
422
}
425
423
426
424
// One or both filenames contain a space, but the names are
427
425
// unquoted. Here, the 'diff --git' syntax is ambiguous, and
428
426
// we have to obtain the filenames elsewhere (e.g. from the
429
- // chunk headers or extended headers). HOWEVER, if the file
427
+ // hunk headers or extended headers). HOWEVER, if the file
430
428
// is newly created and empty, there IS no other place to
431
429
// find the filename. In this case, the two filenames are
432
430
// identical (except for the leading 'a/' prefix), and we have
433
431
// to handle that case here.
434
432
first := diffArgs [:length / 2 ]
435
433
second := diffArgs [length / 2 + 1 :]
436
434
if len (first ) >= 3 && length % 2 == 1 && first [1 ] == '/' && first [1 :] == second [1 :] {
437
- return true , first , second
435
+ return first , second , true
438
436
}
439
437
440
438
// The syntax is (unfortunately) valid, but we could not extract
441
439
// the filenames.
442
- return true , "" , ""
440
+ return "" , "" , true
443
441
}
444
442
445
443
if diffArgs [0 ] == '"' {
446
444
first , remainder , err := readQuotedFilename (diffArgs )
447
445
if err != nil || len (remainder ) < 2 || remainder [0 ] != ' ' {
448
- return false , "" , ""
446
+ return "" , "" , false
449
447
}
450
448
if remainder [1 ] == '"' {
451
449
second , remainder , err := readQuotedFilename (remainder [1 :])
452
450
if remainder != "" || err != nil {
453
- return false , "" , ""
451
+ return "" , "" , false
454
452
}
455
- return true , first , second
453
+ return first , second , true
456
454
}
457
- return true , first , remainder [1 :]
455
+ return first , remainder [1 :], true
458
456
}
459
457
460
458
// In this case, second argument MUST be quoted (or it's a syntax error)
461
459
i := strings .IndexByte (diffArgs , '"' )
462
460
if i == - 1 || i + 2 >= length || diffArgs [i - 1 ] != ' ' {
463
- return false , "" , ""
461
+ return "" , "" , false
464
462
}
465
463
466
464
second , remainder , err := readQuotedFilename (diffArgs [i :])
467
465
if remainder != "" || err != nil {
468
- return false , "" , ""
466
+ return "" , "" , false
469
467
}
470
- return true , diffArgs [:i - 1 ], second
468
+ return diffArgs [:i - 1 ], second , true
471
469
}
472
470
473
471
// handleEmpty detects when FileDiff was an empty diff and will not have any hunks
@@ -509,7 +507,7 @@ func handleEmpty(fd *FileDiff) (wasEmpty bool) {
509
507
}
510
508
511
509
var success bool
512
- success , fd .OrigName , fd .NewName = parseDiffGitArgs (fd .Extended [0 ][len ("diff --git " ):])
510
+ fd .OrigName , fd .NewName , success = parseDiffGitArgs (fd .Extended [0 ][len ("diff --git " ):])
513
511
if isNewFile {
514
512
fd .OrigName = "/dev/null"
515
513
}
@@ -522,9 +520,9 @@ func handleEmpty(fd *FileDiff) (wasEmpty bool) {
522
520
if success && (isCopy || isRename ) && fd .OrigName == "" && fd .NewName == "" {
523
521
diffArgs := fd .Extended [0 ][len ("diff --git " ):]
524
522
525
- reconstruct := func (header string , prefix string , whichFile int , result * string ) bool {
523
+ tryReconstruct := func (header string , prefix string , whichFile int , result * string ) {
526
524
if ! strings .HasPrefix (header , prefix ) {
527
- return false
525
+ return
528
526
}
529
527
rawFilename := header [len (prefix ):]
530
528
@@ -536,18 +534,17 @@ func handleEmpty(fd *FileDiff) (wasEmpty bool) {
536
534
prefixLetterIndex = len (diffArgs ) - len (rawFilename ) - 2
537
535
}
538
536
if prefixLetterIndex < 0 || diffArgs [prefixLetterIndex + 1 ] != '/' {
539
- return false
537
+ return
540
538
}
541
539
542
540
* result = diffArgs [prefixLetterIndex :prefixLetterIndex + 2 ] + rawFilename
543
- return true
544
541
}
545
542
546
543
for _ , header := range fd .Extended {
547
- _ = reconstruct (header , "copy from " , 1 , & fd .OrigName ) ||
548
- reconstruct (header , "copy to " , 2 , & fd .NewName ) ||
549
- reconstruct (header , "rename from " , 1 , & fd .OrigName ) ||
550
- reconstruct (header , "rename to " , 2 , & fd .NewName )
544
+ tryReconstruct (header , "copy from " , 1 , & fd .OrigName )
545
+ tryReconstruct (header , "copy to " , 2 , & fd .NewName )
546
+ tryReconstruct (header , "rename from " , 1 , & fd .OrigName )
547
+ tryReconstruct (header , "rename to " , 2 , & fd .NewName )
551
548
}
552
549
}
553
550
return success
0 commit comments