@@ -20,6 +20,7 @@ static const char *color_grep_slots[] = {
20
20
[GREP_COLOR_FILENAME ] = "filename" ,
21
21
[GREP_COLOR_FUNCTION ] = "function" ,
22
22
[GREP_COLOR_LINENO ] = "lineNumber" ,
23
+ [GREP_COLOR_COLUMNNO ] = "column" ,
23
24
[GREP_COLOR_MATCH_CONTEXT ] = "matchContext" ,
24
25
[GREP_COLOR_MATCH_SELECTED ] = "matchSelected" ,
25
26
[GREP_COLOR_SELECTED ] = "selected" ,
@@ -59,6 +60,7 @@ void init_grep_defaults(void)
59
60
color_set (opt -> colors [GREP_COLOR_FILENAME ], "" );
60
61
color_set (opt -> colors [GREP_COLOR_FUNCTION ], "" );
61
62
color_set (opt -> colors [GREP_COLOR_LINENO ], "" );
63
+ color_set (opt -> colors [GREP_COLOR_COLUMNNO ], "" );
62
64
color_set (opt -> colors [GREP_COLOR_MATCH_CONTEXT ], GIT_COLOR_BOLD_RED );
63
65
color_set (opt -> colors [GREP_COLOR_MATCH_SELECTED ], GIT_COLOR_BOLD_RED );
64
66
color_set (opt -> colors [GREP_COLOR_SELECTED ], "" );
@@ -110,6 +112,10 @@ int grep_config(const char *var, const char *value, void *cb)
110
112
opt -> linenum = git_config_bool (var , value );
111
113
return 0 ;
112
114
}
115
+ if (!strcmp (var , "grep.column" )) {
116
+ opt -> columnnum = git_config_bool (var , value );
117
+ return 0 ;
118
+ }
113
119
114
120
if (!strcmp (var , "grep.fullname" )) {
115
121
opt -> relative = !git_config_bool (var , value );
@@ -157,6 +163,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
157
163
opt -> extended_regexp_option = def -> extended_regexp_option ;
158
164
opt -> pattern_type_option = def -> pattern_type_option ;
159
165
opt -> linenum = def -> linenum ;
166
+ opt -> columnnum = def -> columnnum ;
160
167
opt -> max_depth = def -> max_depth ;
161
168
opt -> pathname = def -> pathname ;
162
169
opt -> relative = def -> relative ;
@@ -1244,11 +1251,11 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
1244
1251
return hit ;
1245
1252
}
1246
1253
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 )
1249
1257
{
1250
1258
int h = 0 ;
1251
- regmatch_t match ;
1252
1259
1253
1260
if (!x )
1254
1261
die ("Not a valid grep expression" );
@@ -1257,25 +1264,52 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
1257
1264
h = 1 ;
1258
1265
break ;
1259
1266
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
+ }
1261
1274
break ;
1262
1275
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 );
1264
1281
break ;
1265
1282
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
+ }
1269
1294
break ;
1270
1295
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 );
1279
1313
break ;
1280
1314
default :
1281
1315
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,
1286
1320
}
1287
1321
1288
1322
static 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 )
1290
1325
{
1291
1326
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 );
1293
1328
}
1294
1329
1295
1330
static int match_line (struct grep_opt * opt , char * bol , char * eol ,
1331
+ ssize_t * col , ssize_t * icol ,
1296
1332
enum grep_context ctx , int collect_hits )
1297
1333
{
1298
1334
struct grep_pat * p ;
1299
- regmatch_t match ;
1335
+ int hit = 0 ;
1300
1336
1301
1337
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 );
1303
1340
1304
1341
/* we do not call with collect_hits without being extended */
1305
1342
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
+ }
1308
1358
}
1309
- return 0 ;
1359
+ return hit ;
1310
1360
}
1311
1361
1312
1362
static 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,
1355
1405
}
1356
1406
1357
1407
static 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 )
1359
1409
{
1360
1410
int rest = eol - bol ;
1361
1411
const char * match_color , * line_color = NULL ;
@@ -1390,6 +1440,17 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
1390
1440
output_color (opt , buf , strlen (buf ), opt -> colors [GREP_COLOR_LINENO ]);
1391
1441
output_sep (opt , sign );
1392
1442
}
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
+ }
1393
1454
if (opt -> color ) {
1394
1455
regmatch_t match ;
1395
1456
enum grep_context ctx = GREP_CONTEXT_BODY ;
@@ -1495,7 +1556,7 @@ static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
1495
1556
break ;
1496
1557
1497
1558
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 , '=' );
1499
1560
break ;
1500
1561
}
1501
1562
}
@@ -1560,7 +1621,7 @@ static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
1560
1621
1561
1622
while (* eol != '\n' )
1562
1623
eol ++ ;
1563
- show_line (opt , bol , eol , gs -> name , cur , sign );
1624
+ show_line (opt , bol , eol , gs -> name , cur , 0 , sign );
1564
1625
bol = eol + 1 ;
1565
1626
cur ++ ;
1566
1627
}
@@ -1759,6 +1820,8 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
1759
1820
while (left ) {
1760
1821
char * eol , ch ;
1761
1822
int hit ;
1823
+ ssize_t cno ;
1824
+ ssize_t col = -1 , icol = -1 ;
1762
1825
1763
1826
/*
1764
1827
* 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
1782
1845
if ((ctx == GREP_CONTEXT_HEAD ) && (eol == bol ))
1783
1846
ctx = GREP_CONTEXT_BODY ;
1784
1847
1785
- hit = match_line (opt , bol , eol , ctx , collect_hits );
1848
+ hit = match_line (opt , bol , eol , & col , & icol , ctx , collect_hits );
1786
1849
* eol = ch ;
1787
1850
1788
1851
if (collect_hits )
@@ -1823,7 +1886,18 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
1823
1886
show_pre_context (opt , gs , bol , eol , lno );
1824
1887
else if (opt -> funcname )
1825
1888
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 , ':' );
1827
1901
last_hit = lno ;
1828
1902
if (opt -> funcbody )
1829
1903
show_function = 1 ;
@@ -1852,7 +1926,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
1852
1926
/* If the last hit is within the post context,
1853
1927
* we need to show this line.
1854
1928
*/
1855
- show_line (opt , bol , eol , gs -> name , lno , '-' );
1929
+ show_line (opt , bol , eol , gs -> name , lno , col + 1 , '-' );
1856
1930
}
1857
1931
1858
1932
next_line :
0 commit comments