@@ -20,6 +20,7 @@ static const char *color_grep_slots[] = {
2020 [GREP_COLOR_FILENAME ] = "filename" ,
2121 [GREP_COLOR_FUNCTION ] = "function" ,
2222 [GREP_COLOR_LINENO ] = "lineNumber" ,
23+ [GREP_COLOR_COLUMNNO ] = "column" ,
2324 [GREP_COLOR_MATCH_CONTEXT ] = "matchContext" ,
2425 [GREP_COLOR_MATCH_SELECTED ] = "matchSelected" ,
2526 [GREP_COLOR_SELECTED ] = "selected" ,
@@ -59,6 +60,7 @@ void init_grep_defaults(void)
5960 color_set (opt -> colors [GREP_COLOR_FILENAME ], "" );
6061 color_set (opt -> colors [GREP_COLOR_FUNCTION ], "" );
6162 color_set (opt -> colors [GREP_COLOR_LINENO ], "" );
63+ color_set (opt -> colors [GREP_COLOR_COLUMNNO ], "" );
6264 color_set (opt -> colors [GREP_COLOR_MATCH_CONTEXT ], GIT_COLOR_BOLD_RED );
6365 color_set (opt -> colors [GREP_COLOR_MATCH_SELECTED ], GIT_COLOR_BOLD_RED );
6466 color_set (opt -> colors [GREP_COLOR_SELECTED ], "" );
@@ -110,6 +112,10 @@ int grep_config(const char *var, const char *value, void *cb)
110112 opt -> linenum = git_config_bool (var , value );
111113 return 0 ;
112114 }
115+ if (!strcmp (var , "grep.column" )) {
116+ opt -> columnnum = git_config_bool (var , value );
117+ return 0 ;
118+ }
113119
114120 if (!strcmp (var , "grep.fullname" )) {
115121 opt -> relative = !git_config_bool (var , value );
@@ -157,6 +163,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
157163 opt -> extended_regexp_option = def -> extended_regexp_option ;
158164 opt -> pattern_type_option = def -> pattern_type_option ;
159165 opt -> linenum = def -> linenum ;
166+ opt -> columnnum = def -> columnnum ;
160167 opt -> max_depth = def -> max_depth ;
161168 opt -> pathname = def -> pathname ;
162169 opt -> relative = def -> relative ;
@@ -1244,11 +1251,11 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
12441251 return hit ;
12451252}
12461253
1247- static int match_expr_eval (struct grep_expr * x , char * bol , char * eol ,
1248- enum grep_context ctx , int collect_hits )
1254+ static int match_expr_eval (struct grep_opt * opt , struct grep_expr * x , char * bol ,
1255+ char * eol , enum grep_context ctx , ssize_t * col ,
1256+ ssize_t * icol , int collect_hits )
12491257{
12501258 int h = 0 ;
1251- regmatch_t match ;
12521259
12531260 if (!x )
12541261 die ("Not a valid grep expression" );
@@ -1257,25 +1264,52 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
12571264 h = 1 ;
12581265 break ;
12591266 case GREP_NODE_ATOM :
1260- h = match_one_pattern (x -> u .atom , bol , eol , ctx , & match , 0 );
1267+ {
1268+ regmatch_t tmp ;
1269+ h = match_one_pattern (x -> u .atom , bol , eol , ctx ,
1270+ & tmp , 0 );
1271+ if (h && (* col < 0 || tmp .rm_so < * col ))
1272+ * col = tmp .rm_so ;
1273+ }
12611274 break ;
12621275 case GREP_NODE_NOT :
1263- h = !match_expr_eval (x -> u .unary , bol , eol , ctx , 0 );
1276+ /*
1277+ * Upon visiting a GREP_NODE_NOT, col and icol become swapped.
1278+ */
1279+ h = !match_expr_eval (opt , x -> u .unary , bol , eol , ctx , icol , col ,
1280+ 0 );
12641281 break ;
12651282 case GREP_NODE_AND :
1266- if (!match_expr_eval (x -> u .binary .left , bol , eol , ctx , 0 ))
1267- return 0 ;
1268- h = match_expr_eval (x -> u .binary .right , bol , eol , ctx , 0 );
1283+ h = match_expr_eval (opt , x -> u .binary .left , bol , eol , ctx , col ,
1284+ icol , 0 );
1285+ if (h || opt -> columnnum ) {
1286+ /*
1287+ * Don't short-circuit AND when given --column, since a
1288+ * NOT earlier in the tree may turn this into an OR. In
1289+ * this case, see the below comment.
1290+ */
1291+ h &= match_expr_eval (opt , x -> u .binary .right , bol , eol ,
1292+ ctx , col , icol , 0 );
1293+ }
12691294 break ;
12701295 case GREP_NODE_OR :
1271- if (!collect_hits )
1272- return (match_expr_eval (x -> u .binary .left ,
1273- bol , eol , ctx , 0 ) ||
1274- match_expr_eval (x -> u .binary .right ,
1275- bol , eol , ctx , 0 ));
1276- h = match_expr_eval (x -> u .binary .left , bol , eol , ctx , 0 );
1277- x -> u .binary .left -> hit |= h ;
1278- h |= match_expr_eval (x -> u .binary .right , bol , eol , ctx , 1 );
1296+ if (!(collect_hits || opt -> columnnum )) {
1297+ /*
1298+ * Don't short-circuit OR when given --column (or
1299+ * collecting hits) to ensure we don't skip a later
1300+ * child that would produce an earlier match.
1301+ */
1302+ return (match_expr_eval (opt , x -> u .binary .left , bol , eol ,
1303+ ctx , col , icol , 0 ) ||
1304+ match_expr_eval (opt , x -> u .binary .right , bol ,
1305+ eol , ctx , col , icol , 0 ));
1306+ }
1307+ h = match_expr_eval (opt , x -> u .binary .left , bol , eol , ctx , col ,
1308+ icol , 0 );
1309+ if (collect_hits )
1310+ x -> u .binary .left -> hit |= h ;
1311+ h |= match_expr_eval (opt , x -> u .binary .right , bol , eol , ctx , col ,
1312+ icol , collect_hits );
12791313 break ;
12801314 default :
12811315 die ("Unexpected node type (internal error) %d" , x -> node );
@@ -1286,27 +1320,43 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
12861320}
12871321
12881322static int match_expr (struct grep_opt * opt , char * bol , char * eol ,
1289- enum grep_context ctx , int collect_hits )
1323+ enum grep_context ctx , ssize_t * col ,
1324+ ssize_t * icol , int collect_hits )
12901325{
12911326 struct grep_expr * x = opt -> pattern_expression ;
1292- return match_expr_eval (x , bol , eol , ctx , collect_hits );
1327+ return match_expr_eval (opt , x , bol , eol , ctx , col , icol , collect_hits );
12931328}
12941329
12951330static int match_line (struct grep_opt * opt , char * bol , char * eol ,
1331+ ssize_t * col , ssize_t * icol ,
12961332 enum grep_context ctx , int collect_hits )
12971333{
12981334 struct grep_pat * p ;
1299- regmatch_t match ;
1335+ int hit = 0 ;
13001336
13011337 if (opt -> extended )
1302- return match_expr (opt , bol , eol , ctx , collect_hits );
1338+ return match_expr (opt , bol , eol , ctx , col , icol ,
1339+ collect_hits );
13031340
13041341 /* we do not call with collect_hits without being extended */
13051342 for (p = opt -> pattern_list ; p ; p = p -> next ) {
1306- if (match_one_pattern (p , bol , eol , ctx , & match , 0 ))
1307- return 1 ;
1343+ regmatch_t tmp ;
1344+ if (match_one_pattern (p , bol , eol , ctx , & tmp , 0 )) {
1345+ hit |= 1 ;
1346+ if (!opt -> columnnum ) {
1347+ /*
1348+ * Without --column, any single match on a line
1349+ * is enough to know that it needs to be
1350+ * printed. With --column, scan _all_ patterns
1351+ * to find the earliest.
1352+ */
1353+ break ;
1354+ }
1355+ if (* col < 0 || tmp .rm_so < * col )
1356+ * col = tmp .rm_so ;
1357+ }
13081358 }
1309- return 0 ;
1359+ return hit ;
13101360}
13111361
13121362static int match_next_pattern (struct grep_pat * p , char * bol , char * eol ,
@@ -1355,7 +1405,7 @@ static int next_match(struct grep_opt *opt, char *bol, char *eol,
13551405}
13561406
13571407static void show_line (struct grep_opt * opt , char * bol , char * eol ,
1358- const char * name , unsigned lno , char sign )
1408+ const char * name , unsigned lno , ssize_t cno , char sign )
13591409{
13601410 int rest = eol - bol ;
13611411 const char * match_color , * line_color = NULL ;
@@ -1390,6 +1440,17 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
13901440 output_color (opt , buf , strlen (buf ), opt -> colors [GREP_COLOR_LINENO ]);
13911441 output_sep (opt , sign );
13921442 }
1443+ /*
1444+ * Treat 'cno' as the 1-indexed offset from the start of a non-context
1445+ * line to its first match. Otherwise, 'cno' is 0 indicating that we are
1446+ * being called with a context line.
1447+ */
1448+ if (opt -> columnnum && cno ) {
1449+ char buf [32 ];
1450+ xsnprintf (buf , sizeof (buf ), "%" PRIuMAX , (uintmax_t )cno );
1451+ output_color (opt , buf , strlen (buf ), opt -> colors [GREP_COLOR_COLUMNNO ]);
1452+ output_sep (opt , sign );
1453+ }
13931454 if (opt -> color ) {
13941455 regmatch_t match ;
13951456 enum grep_context ctx = GREP_CONTEXT_BODY ;
@@ -1495,7 +1556,7 @@ static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
14951556 break ;
14961557
14971558 if (match_funcname (opt , gs , bol , eol )) {
1498- show_line (opt , bol , eol , gs -> name , lno , '=' );
1559+ show_line (opt , bol , eol , gs -> name , lno , 0 , '=' );
14991560 break ;
15001561 }
15011562 }
@@ -1560,7 +1621,7 @@ static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
15601621
15611622 while (* eol != '\n' )
15621623 eol ++ ;
1563- show_line (opt , bol , eol , gs -> name , cur , sign );
1624+ show_line (opt , bol , eol , gs -> name , cur , 0 , sign );
15641625 bol = eol + 1 ;
15651626 cur ++ ;
15661627 }
@@ -1759,6 +1820,8 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
17591820 while (left ) {
17601821 char * eol , ch ;
17611822 int hit ;
1823+ ssize_t cno ;
1824+ ssize_t col = -1 , icol = -1 ;
17621825
17631826 /*
17641827 * look_ahead() skips quickly to the line that possibly
@@ -1782,7 +1845,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
17821845 if ((ctx == GREP_CONTEXT_HEAD ) && (eol == bol ))
17831846 ctx = GREP_CONTEXT_BODY ;
17841847
1785- hit = match_line (opt , bol , eol , ctx , collect_hits );
1848+ hit = match_line (opt , bol , eol , & col , & icol , ctx , collect_hits );
17861849 * eol = ch ;
17871850
17881851 if (collect_hits )
@@ -1823,7 +1886,18 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
18231886 show_pre_context (opt , gs , bol , eol , lno );
18241887 else if (opt -> funcname )
18251888 show_funcname_line (opt , gs , bol , lno );
1826- show_line (opt , bol , eol , gs -> name , lno , ':' );
1889+ cno = opt -> invert ? icol : col ;
1890+ if (cno < 0 ) {
1891+ /*
1892+ * A negative cno indicates that there was no
1893+ * match on the line. We are thus inverted and
1894+ * being asked to show all lines that _don't_
1895+ * match a given expression. Therefore, set cno
1896+ * to 0 to suggest the whole line matches.
1897+ */
1898+ cno = 0 ;
1899+ }
1900+ show_line (opt , bol , eol , gs -> name , lno , cno + 1 , ':' );
18271901 last_hit = lno ;
18281902 if (opt -> funcbody )
18291903 show_function = 1 ;
@@ -1852,7 +1926,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
18521926 /* If the last hit is within the post context,
18531927 * we need to show this line.
18541928 */
1855- show_line (opt , bol , eol , gs -> name , lno , '-' );
1929+ show_line (opt , bol , eol , gs -> name , lno , col + 1 , '-' );
18561930 }
18571931
18581932 next_line :
0 commit comments