@@ -416,48 +416,190 @@ static char *squash_slash(char *name)
416416 return name ;
417417}
418418
419- static char * find_name (const char * line , char * def , int p_value , int terminate )
419+ static char * find_name_gnu (const char * line , char * def , int p_value )
420420{
421- int len ;
422- const char * start = NULL ;
421+ struct strbuf name = STRBUF_INIT ;
422+ char * cp ;
423423
424- if (p_value == 0 )
425- start = line ;
424+ /*
425+ * Proposed "new-style" GNU patch/diff format; see
426+ * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
427+ */
428+ if (unquote_c_style (& name , line , NULL )) {
429+ strbuf_release (& name );
430+ return NULL ;
431+ }
426432
427- if (* line == '"' ) {
428- struct strbuf name = STRBUF_INIT ;
433+ for (cp = name .buf ; p_value ; p_value -- ) {
434+ cp = strchr (cp , '/' );
435+ if (!cp ) {
436+ strbuf_release (& name );
437+ return NULL ;
438+ }
439+ cp ++ ;
440+ }
429441
430- /*
431- * Proposed "new-style" GNU patch/diff format; see
432- * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
433- */
434- if (!unquote_c_style (& name , line , NULL )) {
435- char * cp ;
442+ /* name can later be freed, so we need
443+ * to memmove, not just return cp
444+ */
445+ strbuf_remove (& name , 0 , cp - name .buf );
446+ free (def );
447+ if (root )
448+ strbuf_insert (& name , 0 , root , root_len );
449+ return squash_slash (strbuf_detach (& name , NULL ));
450+ }
436451
437- for (cp = name .buf ; p_value ; p_value -- ) {
438- cp = strchr (cp , '/' );
439- if (!cp )
440- break ;
441- cp ++ ;
442- }
443- if (cp ) {
444- /* name can later be freed, so we need
445- * to memmove, not just return cp
446- */
447- strbuf_remove (& name , 0 , cp - name .buf );
448- free (def );
449- if (root )
450- strbuf_insert (& name , 0 , root , root_len );
451- return squash_slash (strbuf_detach (& name , NULL ));
452- }
453- }
454- strbuf_release (& name );
452+ static size_t tz_len (const char * line , size_t len )
453+ {
454+ const char * tz , * p ;
455+
456+ if (len < strlen (" +0500" ) || line [len - strlen (" +0500" )] != ' ' )
457+ return 0 ;
458+ tz = line + len - strlen (" +0500" );
459+
460+ if (tz [1 ] != '+' && tz [1 ] != '-' )
461+ return 0 ;
462+
463+ for (p = tz + 2 ; p != line + len ; p ++ )
464+ if (!isdigit (* p ))
465+ return 0 ;
466+
467+ return line + len - tz ;
468+ }
469+
470+ static size_t date_len (const char * line , size_t len )
471+ {
472+ const char * date , * p ;
473+
474+ if (len < strlen ("72-02-05" ) || line [len - strlen ("-05" )] != '-' )
475+ return 0 ;
476+ p = date = line + len - strlen ("72-02-05" );
477+
478+ if (!isdigit (* p ++ ) || !isdigit (* p ++ ) || * p ++ != '-' ||
479+ !isdigit (* p ++ ) || !isdigit (* p ++ ) || * p ++ != '-' ||
480+ !isdigit (* p ++ ) || !isdigit (* p ++ )) /* Not a date. */
481+ return 0 ;
482+
483+ if (date - line >= strlen ("19" ) &&
484+ isdigit (date [-1 ]) && isdigit (date [-2 ])) /* 4-digit year */
485+ date -= strlen ("19" );
486+
487+ return line + len - date ;
488+ }
489+
490+ static size_t short_time_len (const char * line , size_t len )
491+ {
492+ const char * time , * p ;
493+
494+ if (len < strlen (" 07:01:32" ) || line [len - strlen (":32" )] != ':' )
495+ return 0 ;
496+ p = time = line + len - strlen (" 07:01:32" );
497+
498+ /* Permit 1-digit hours? */
499+ if (* p ++ != ' ' ||
500+ !isdigit (* p ++ ) || !isdigit (* p ++ ) || * p ++ != ':' ||
501+ !isdigit (* p ++ ) || !isdigit (* p ++ ) || * p ++ != ':' ||
502+ !isdigit (* p ++ ) || !isdigit (* p ++ )) /* Not a time. */
503+ return 0 ;
504+
505+ return line + len - time ;
506+ }
507+
508+ static size_t fractional_time_len (const char * line , size_t len )
509+ {
510+ const char * p ;
511+ size_t n ;
512+
513+ /* Expected format: 19:41:17.620000023 */
514+ if (!len || !isdigit (line [len - 1 ]))
515+ return 0 ;
516+ p = line + len - 1 ;
517+
518+ /* Fractional seconds. */
519+ while (p > line && isdigit (* p ))
520+ p -- ;
521+ if (* p != '.' )
522+ return 0 ;
523+
524+ /* Hours, minutes, and whole seconds. */
525+ n = short_time_len (line , p - line );
526+ if (!n )
527+ return 0 ;
528+
529+ return line + len - p + n ;
530+ }
531+
532+ static size_t trailing_spaces_len (const char * line , size_t len )
533+ {
534+ const char * p ;
535+
536+ /* Expected format: ' ' x (1 or more) */
537+ if (!len || line [len - 1 ] != ' ' )
538+ return 0 ;
539+
540+ p = line + len ;
541+ while (p != line ) {
542+ p -- ;
543+ if (* p != ' ' )
544+ return line + len - (p + 1 );
455545 }
456546
457- for (;;) {
547+ /* All spaces! */
548+ return len ;
549+ }
550+
551+ static size_t diff_timestamp_len (const char * line , size_t len )
552+ {
553+ const char * end = line + len ;
554+ size_t n ;
555+
556+ /*
557+ * Posix: 2010-07-05 19:41:17
558+ * GNU: 2010-07-05 19:41:17.620000023 -0500
559+ */
560+
561+ if (!isdigit (end [-1 ]))
562+ return 0 ;
563+
564+ n = tz_len (line , end - line );
565+ end -= n ;
566+
567+ n = short_time_len (line , end - line );
568+ if (!n )
569+ n = fractional_time_len (line , end - line );
570+ end -= n ;
571+
572+ n = date_len (line , end - line );
573+ if (!n ) /* No date. Too bad. */
574+ return 0 ;
575+ end -= n ;
576+
577+ if (end == line ) /* No space before date. */
578+ return 0 ;
579+ if (end [-1 ] == '\t' ) { /* Success! */
580+ end -- ;
581+ return line + len - end ;
582+ }
583+ if (end [-1 ] != ' ' ) /* No space before date. */
584+ return 0 ;
585+
586+ /* Whitespace damage. */
587+ end -= trailing_spaces_len (line , end - line );
588+ return line + len - end ;
589+ }
590+
591+ static char * find_name_common (const char * line , char * def , int p_value ,
592+ const char * end , int terminate )
593+ {
594+ int len ;
595+ const char * start = NULL ;
596+
597+ if (p_value == 0 )
598+ start = line ;
599+ while (line != end ) {
458600 char c = * line ;
459601
460- if (isspace (c )) {
602+ if (! end && isspace (c )) {
461603 if (c == '\n' )
462604 break ;
463605 if (name_terminate (start , line - start , c , terminate ))
@@ -497,6 +639,37 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
497639 return squash_slash (xmemdupz (start , len ));
498640}
499641
642+ static char * find_name (const char * line , char * def , int p_value , int terminate )
643+ {
644+ if (* line == '"' ) {
645+ char * name = find_name_gnu (line , def , p_value );
646+ if (name )
647+ return name ;
648+ }
649+
650+ return find_name_common (line , def , p_value , NULL , terminate );
651+ }
652+
653+ static char * find_name_traditional (const char * line , char * def , int p_value )
654+ {
655+ size_t len = strlen (line );
656+ size_t date_len ;
657+
658+ if (* line == '"' ) {
659+ char * name = find_name_gnu (line , def , p_value );
660+ if (name )
661+ return name ;
662+ }
663+
664+ len = strchrnul (line , '\n' ) - line ;
665+ date_len = diff_timestamp_len (line , len );
666+ if (!date_len )
667+ return find_name_common (line , def , p_value , NULL , TERM_TAB );
668+ len -= date_len ;
669+
670+ return find_name_common (line , def , p_value , line + len , 0 );
671+ }
672+
500673static int count_slashes (const char * cp )
501674{
502675 int cnt = 0 ;
@@ -519,7 +692,7 @@ static int guess_p_value(const char *nameline)
519692
520693 if (is_dev_null (nameline ))
521694 return -1 ;
522- name = find_name (nameline , NULL , 0 , TERM_SPACE | TERM_TAB );
695+ name = find_name_traditional (nameline , NULL , 0 );
523696 if (!name )
524697 return -1 ;
525698 cp = strchr (name , '/' );
@@ -638,16 +811,16 @@ static void parse_traditional_patch(const char *first, const char *second, struc
638811 if (is_dev_null (first )) {
639812 patch -> is_new = 1 ;
640813 patch -> is_delete = 0 ;
641- name = find_name (second , NULL , p_value , TERM_SPACE | TERM_TAB );
814+ name = find_name_traditional (second , NULL , p_value );
642815 patch -> new_name = name ;
643816 } else if (is_dev_null (second )) {
644817 patch -> is_new = 0 ;
645818 patch -> is_delete = 1 ;
646- name = find_name (first , NULL , p_value , TERM_SPACE | TERM_TAB );
819+ name = find_name_traditional (first , NULL , p_value );
647820 patch -> old_name = name ;
648821 } else {
649- name = find_name (first , NULL , p_value , TERM_SPACE | TERM_TAB );
650- name = find_name (second , name , p_value , TERM_SPACE | TERM_TAB );
822+ name = find_name_traditional (first , NULL , p_value );
823+ name = find_name_traditional (second , name , p_value );
651824 if (has_epoch_timestamp (first )) {
652825 patch -> is_new = 1 ;
653826 patch -> is_delete = 0 ;
0 commit comments