@@ -535,9 +535,9 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons
535535 else {
536536 /* Emit just the prefix, then the rest. */
537537 emit_line (ecbdata -> file , set , reset , line , ecbdata -> nparents );
538- ( void ) check_and_emit_line (line + ecbdata -> nparents ,
539- len - ecbdata -> nparents , ecbdata -> ws_rule ,
540- ecbdata -> file , set , reset , ws );
538+ ws_check_emit (line + ecbdata -> nparents ,
539+ len - ecbdata -> nparents , ecbdata -> ws_rule ,
540+ ecbdata -> file , set , reset , ws );
541541 }
542542}
543543
@@ -1136,42 +1136,85 @@ static void free_diffstat_info(struct diffstat_t *diffstat)
11361136struct checkdiff_t {
11371137 struct xdiff_emit_state xm ;
11381138 const char * filename ;
1139- int lineno , color_diff ;
1139+ int lineno ;
1140+ struct diff_options * o ;
11401141 unsigned ws_rule ;
11411142 unsigned status ;
1142- FILE * file ;
1143+ int trailing_blanks_start ;
11431144};
11441145
1146+ static int is_conflict_marker (const char * line , unsigned long len )
1147+ {
1148+ char firstchar ;
1149+ int cnt ;
1150+
1151+ if (len < 8 )
1152+ return 0 ;
1153+ firstchar = line [0 ];
1154+ switch (firstchar ) {
1155+ case '=' : case '>' : case '<' :
1156+ break ;
1157+ default :
1158+ return 0 ;
1159+ }
1160+ for (cnt = 1 ; cnt < 7 ; cnt ++ )
1161+ if (line [cnt ] != firstchar )
1162+ return 0 ;
1163+ /* line[0] thru line[6] are same as firstchar */
1164+ if (firstchar == '=' ) {
1165+ /* divider between ours and theirs? */
1166+ if (len != 8 || line [7 ] != '\n' )
1167+ return 0 ;
1168+ } else if (len < 8 || !isspace (line [7 ])) {
1169+ /* not divider before ours nor after theirs */
1170+ return 0 ;
1171+ }
1172+ return 1 ;
1173+ }
1174+
11451175static void checkdiff_consume (void * priv , char * line , unsigned long len )
11461176{
11471177 struct checkdiff_t * data = priv ;
1148- const char * ws = diff_get_color (data -> color_diff , DIFF_WHITESPACE );
1149- const char * reset = diff_get_color (data -> color_diff , DIFF_RESET );
1150- const char * set = diff_get_color (data -> color_diff , DIFF_FILE_NEW );
1178+ int color_diff = DIFF_OPT_TST (data -> o , COLOR_DIFF );
1179+ const char * ws = diff_get_color (color_diff , DIFF_WHITESPACE );
1180+ const char * reset = diff_get_color (color_diff , DIFF_RESET );
1181+ const char * set = diff_get_color (color_diff , DIFF_FILE_NEW );
11511182 char * err ;
11521183
11531184 if (line [0 ] == '+' ) {
11541185 unsigned bad ;
11551186 data -> lineno ++ ;
1156- bad = check_and_emit_line (line + 1 , len - 1 ,
1157- data -> ws_rule , NULL , NULL , NULL , NULL );
1187+ if (!ws_blank_line (line + 1 , len - 1 , data -> ws_rule ))
1188+ data -> trailing_blanks_start = 0 ;
1189+ else if (!data -> trailing_blanks_start )
1190+ data -> trailing_blanks_start = data -> lineno ;
1191+ if (is_conflict_marker (line + 1 , len - 1 )) {
1192+ data -> status |= 1 ;
1193+ fprintf (data -> o -> file ,
1194+ "%s:%d: leftover conflict marker\n" ,
1195+ data -> filename , data -> lineno );
1196+ }
1197+ bad = ws_check (line + 1 , len - 1 , data -> ws_rule );
11581198 if (!bad )
11591199 return ;
11601200 data -> status |= bad ;
11611201 err = whitespace_error_string (bad );
1162- fprintf (data -> file , "%s:%d: %s.\n" , data -> filename , data -> lineno , err );
1202+ fprintf (data -> o -> file , "%s:%d: %s.\n" ,
1203+ data -> filename , data -> lineno , err );
11631204 free (err );
1164- emit_line (data -> file , set , reset , line , 1 );
1165- ( void ) check_and_emit_line (line + 1 , len - 1 , data -> ws_rule ,
1166- data -> file , set , reset , ws );
1167- } else if (line [0 ] == ' ' )
1205+ emit_line (data -> o -> file , set , reset , line , 1 );
1206+ ws_check_emit (line + 1 , len - 1 , data -> ws_rule ,
1207+ data -> o -> file , set , reset , ws );
1208+ } else if (line [0 ] == ' ' ) {
11681209 data -> lineno ++ ;
1169- else if (line [0 ] == '@' ) {
1210+ data -> trailing_blanks_start = 0 ;
1211+ } else if (line [0 ] == '@' ) {
11701212 char * plus = strchr (line , '+' );
11711213 if (plus )
11721214 data -> lineno = strtol (plus , NULL , 10 ) - 1 ;
11731215 else
11741216 die ("invalid diff" );
1217+ data -> trailing_blanks_start = 0 ;
11751218 }
11761219}
11771220
@@ -1544,8 +1587,9 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
15441587
15451588static void builtin_checkdiff (const char * name_a , const char * name_b ,
15461589 const char * attr_path ,
1547- struct diff_filespec * one ,
1548- struct diff_filespec * two , struct diff_options * o )
1590+ struct diff_filespec * one ,
1591+ struct diff_filespec * two ,
1592+ struct diff_options * o )
15491593{
15501594 mmfile_t mf1 , mf2 ;
15511595 struct checkdiff_t data ;
@@ -1557,13 +1601,18 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
15571601 data .xm .consume = checkdiff_consume ;
15581602 data .filename = name_b ? name_b : name_a ;
15591603 data .lineno = 0 ;
1560- data .color_diff = DIFF_OPT_TST ( o , COLOR_DIFF ) ;
1604+ data .o = o ;
15611605 data .ws_rule = whitespace_rule (attr_path );
1562- data .file = o -> file ;
15631606
15641607 if (fill_mmfile (& mf1 , one ) < 0 || fill_mmfile (& mf2 , two ) < 0 )
15651608 die ("unable to read files to diff" );
15661609
1610+ /*
1611+ * All the other codepaths check both sides, but not checking
1612+ * the "old" side here is deliberate. We are checking the newly
1613+ * introduced changes, and as long as the "new" side is text, we
1614+ * can and should check what it introduces.
1615+ */
15671616 if (diff_filespec_is_binary (two ))
15681617 goto free_and_return ;
15691618 else {
@@ -1577,6 +1626,12 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
15771626 ecb .outf = xdiff_outf ;
15781627 ecb .priv = & data ;
15791628 xdi_diff (& mf1 , & mf2 , & xpp , & xecfg , & ecb );
1629+
1630+ if (data .trailing_blanks_start ) {
1631+ fprintf (o -> file , "%s:%d: ends with blank lines.\n" ,
1632+ data .filename , data .trailing_blanks_start );
1633+ data .status = 1 ; /* report errors */
1634+ }
15801635 }
15811636 free_and_return :
15821637 diff_free_filespec_data (one );
0 commit comments