2

I am trying to use grep to find all the callers of a particular C function.

E.g.:

void foo()
{
  ...
  ret = my_bar()
}

For all occurrances of my_bar() I want to print the corresponding function name from where my_bar() is called.

I have tried (based on Regex (grep) for multi-line search needed)

grep -Pzo "(?s)^{\N*?.*?my_bar" *.c

using Perl regex, but this doesn't quite work as expected. It starts the match at the function before foo() till my_bar()

Is this possible with grep/perl and regex, or will I have to use tools like cscope?

Community
  • 1
  • 1
Omair
  • 814
  • 1
  • 10
  • 19
  • Found about `lookbehind`, but unfortunately, we cannot use it for arbitrary length matches (except in .NET) – Omair Sep 16 '13 at 07:10
  • 2
    I'd vote for using tools more tailored to the C language. `grep` is more of a line-level tool. – Jonathon Reinhart Sep 16 '13 at 07:11
  • 1
    If you're using `emacs`, the `gtags-mode` can help you. – devnull Sep 16 '13 at 07:16
  • In my case, I am exploiting the coding convention that there will be an open brace '{' at the beginning of the line whenever a function is defined. However, what I am unable to figure out is why non-greedy mode is not finding the smallest such string. – Omair Sep 16 '13 at 08:20
  • 1
    Of course I can use `cscope` or something similar, but I wanted to know if it can be done through regex. – Omair Sep 16 '13 at 08:21
  • SzG's answer solves my problem, but it is still puzzling why the non-greedy mode is not finding the smallest such string in the above grep. – Omair Sep 18 '13 at 06:29

1 Answers1

0

A Perl one-liner that remembers the last function, and prints its name when my_bar() is found. This is quite simplistic, it'll print a function multiple times if it calls my_bar() multiple times, but you get the basic idea.

perl -ne '$fun = $_ if /^\w+ \w+\(.+\)$/; if (/my_bar\(\)/) { print "$fun" }' *.c

The variant below should cover functions with multi-line argument lists and trailing comments as well:

perl -ne '$fun = $_ if /^\w+ \w+\(.+$/; if (/my_bar\(\)/) { print "$fun" }' *.c
SzG
  • 12,333
  • 4
  • 28
  • 41
  • 2
    This could fail in many ways. For example: **(1)** A function call with arguments. **(2)** A comment after function declaration. **(3)** A function call inside comments. – Birei Sep 16 '13 at 07:50
  • Yes (2) and (3) are true. And there are 1000 other ways it could fail, like a macro pointing to `my_bar()`, etc. As to (1), I'm happy to inform my fellow Perl-hackers that in C, if you see `my_bar()`, you can be sure this function never has arguments. Ever. And I fixed a typo in the answer, so now it actually works. – SzG Sep 16 '13 at 12:02
  • The original question was whether you can solve this problem with SIMPLE tools like grep/perl. The answer is mostly yes, but for a PERFECT solution you obviously need a full-blown C parser. Which a Perl one-liner is not. So I'm sorry for my cr@ppy answer. – SzG Sep 16 '13 at 12:07
  • I only wanted to mean that it could fail even with most basic cases. I think it doesn't worth the attempt without a good parser. You would get too fake results. – Birei Sep 16 '13 at 12:22
  • I know that a perfect solution may not be possible without a parser, but can a regex can be made exploiting the coding convention for this particular case - (opening brace for functions should be on a new line)? – Omair Sep 18 '13 at 06:14
  • My code assumes the following about the 1st line of functions: 1) no leading whitespace 2) it fits in one line including arguments 3) no trailing whitespace or comments 4) opening brace on next line. If that's fulfilled it works. Promise! Actually if I dropped the requirement for a closing bracket, it'd become much more flexible. On second thoughts, that's exactly what I'll do now. – SzG Sep 18 '13 at 06:18