@@ -457,6 +457,76 @@ static int guess_p_value(const char *nameline)
457457 return val ;
458458}
459459
460+ /*
461+ * Does the ---/+++ line has the POSIX timestamp after the last HT?
462+ * GNU diff puts epoch there to signal a creation/deletion event. Is
463+ * this such a timestamp?
464+ */
465+ static int has_epoch_timestamp (const char * nameline )
466+ {
467+ /*
468+ * We are only interested in epoch timestamp; any non-zero
469+ * fraction cannot be one, hence "(\.0+)?" in the regexp below.
470+ * For the same reason, the date must be either 1969-12-31 or
471+ * 1970-01-01, and the seconds part must be "00".
472+ */
473+ const char stamp_regexp [] =
474+ "^(1969-12-31|1970-01-01)"
475+ " "
476+ "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
477+ " "
478+ "([-+][0-2][0-9][0-5][0-9])\n" ;
479+ const char * timestamp = NULL , * cp ;
480+ static regex_t * stamp ;
481+ regmatch_t m [10 ];
482+ int zoneoffset ;
483+ int hourminute ;
484+ int status ;
485+
486+ for (cp = nameline ; * cp != '\n' ; cp ++ ) {
487+ if (* cp == '\t' )
488+ timestamp = cp + 1 ;
489+ }
490+ if (!timestamp )
491+ return 0 ;
492+ if (!stamp ) {
493+ stamp = xmalloc (sizeof (* stamp ));
494+ if (regcomp (stamp , stamp_regexp , REG_EXTENDED )) {
495+ warning ("Cannot prepare timestamp regexp %s" ,
496+ stamp_regexp );
497+ return 0 ;
498+ }
499+ }
500+
501+ status = regexec (stamp , timestamp , ARRAY_SIZE (m ), m , 0 );
502+ if (status ) {
503+ if (status != REG_NOMATCH )
504+ warning ("regexec returned %d for input: %s" ,
505+ status , timestamp );
506+ return 0 ;
507+ }
508+
509+ zoneoffset = strtol (timestamp + m [3 ].rm_so + 1 , NULL , 10 );
510+ zoneoffset = (zoneoffset / 100 ) * 60 + (zoneoffset % 100 );
511+ if (timestamp [m [3 ].rm_so ] == '-' )
512+ zoneoffset = - zoneoffset ;
513+
514+ /*
515+ * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
516+ * (west of GMT) or 1970-01-01 (east of GMT)
517+ */
518+ if ((zoneoffset < 0 && memcmp (timestamp , "1969-12-31" , 10 )) ||
519+ (0 <= zoneoffset && memcmp (timestamp , "1970-01-01" , 10 )))
520+ return 0 ;
521+
522+ hourminute = (strtol (timestamp + 11 , NULL , 10 ) * 60 +
523+ strtol (timestamp + 14 , NULL , 10 ) -
524+ zoneoffset );
525+
526+ return ((zoneoffset < 0 && hourminute == 1440 ) ||
527+ (0 <= zoneoffset && !hourminute ));
528+ }
529+
460530/*
461531 * Get the name etc info from the ---/+++ lines of a traditional patch header
462532 *
@@ -493,7 +563,17 @@ static void parse_traditional_patch(const char *first, const char *second, struc
493563 } else {
494564 name = find_name (first , NULL , p_value , TERM_SPACE | TERM_TAB );
495565 name = find_name (second , name , p_value , TERM_SPACE | TERM_TAB );
496- patch -> old_name = patch -> new_name = name ;
566+ if (has_epoch_timestamp (first )) {
567+ patch -> is_new = 1 ;
568+ patch -> is_delete = 0 ;
569+ patch -> new_name = name ;
570+ } else if (has_epoch_timestamp (second )) {
571+ patch -> is_new = 0 ;
572+ patch -> is_delete = 1 ;
573+ patch -> old_name = name ;
574+ } else {
575+ patch -> old_name = patch -> new_name = name ;
576+ }
497577 }
498578 if (!name )
499579 die ("unable to find filename in patch at line %d" , linenr );
0 commit comments