0

I want to write a script in sh that for each given file, for each printf occurrence, it will count the number of %'s (or alternatively the number of parameters the printf function receives) and rename the printf to printfX, when X is that number.

I got stuck because printf doesn't end with new line...

e.g.

printf("hello "
       "world %d\n", 1);

should return

printf1("hello "
       "world %d\n", 1);
Alex
  • 131
  • 1
  • 11
  • 2
    Before trying to come up with a solution, may I ask why you want to do this? If you want to have a wrapper for `printf`, you can do it with variadic arguments so you don't have to write `printf1`, `printf2`, `printf3` etc. – Shahbaz Aug 09 '12 at 15:09
  • In general I want to achieve the following - for each instance of printf I want to eliminate the format string and all I want to be left with is the number of parameters and the parameters. e.g. printf("hello %d %c",hello, char) => printf2(2,hello,char); – Alex Aug 09 '12 at 15:13
  • How would you then know the type of the parameter? What would you do with `printf("%s %d", str, num);`? – Shahbaz Aug 09 '12 at 15:14
  • actually I intend not to support %s (for this specific usage it is OK) – Alex Aug 09 '12 at 15:16
  • even so, `printf("%d %c", num, ch);` prints something like `12 x` while yours cannot differentiate between the two. Are you trying to print everything as `int`? You know better the context of the usage for this, but my gut says there is a better solution for what you are asking. If you tell us why you want to do this, we may offer you a more elegant solution. ;) – Shahbaz Aug 09 '12 at 15:22
  • I will lose the type - printfX is a macro (named differently... printfX is just an example) that does a casting to ulong for all the params, and because I don't support %s, the casting will not result in any data loss this way). – Alex Aug 09 '12 at 15:23
  • If you have one number that can never happen, say `0xf0000000`, you can do: `#define MY_PRINTF(format, ...) my_printf(0, __VA_ARGS__, 0xf0000000)` and implement `my_printf` as `void my_printf(int dummy, ...);`. Then inside `my_printf`, use `va_list`, start getting the parameters until you hit `0xf0000000`. Note that all parameters are automatically promoted to `int`, so make sure you read `int` by `int`. Your problem would then become renaming `printf` to `MY_PRINTF` which a simple `sed` can do. – Shahbaz Aug 09 '12 at 15:36
  • If I'll go the way you've suggested (and that I've already considered) I can't see how can I traverse the __va_args__ (given I dont have the format string - the main idea of my question is to lose the format string and still maintain the parameters) without knowing their size? The way I described above will give me the ability to up-cast all the params to the biggest type, so that I could go through the __va_args__. – Alex Aug 09 '12 at 15:48
  • I'm not sure actually. I know that all integer types get promoted to `int` (or `unsigned int`), except `long long` (maybe `long` also?) when passed through `...`. If you don't have long long, it shouldn't be a problem. Better check the standard. – Shahbaz Aug 09 '12 at 16:27
  • I do have unsinged long long int, and atually it is the biggest type, so I decided to cast to this type. – Alex Aug 09 '12 at 18:21

2 Answers2

1

There is no simple (= as in script) solution for this because to parse C code, you need an LL(1) parser.

But you can format all the code in the same way so your script can handle the different cases. So what you should do first is find all lines with printf but without );:

grep "printf" ...files... | grep -v ");"

Then fix these files until you don't get any output any more.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • Move the original file to a new place and write a new file. PS: `cut` and `grep` are no parers, they are mere input filters. For one, they can't backtrack and process recursive token trees. – Aaron Digulla Aug 09 '12 at 15:43
  • Since I have no idea what you can't see, I can't help :-) – Aaron Digulla Aug 10 '12 at 08:12
  • I can't see how to solve the problem I presented... the answer you provided is not practical because it will obviously break in the future - I can't assume that from now on all the programmers using my code will use a one line printf convention... – Alex Aug 10 '12 at 19:16
  • Create a test case which runs the check script and makes sure it doesn't produce any output. – Aaron Digulla Aug 13 '12 at 07:37
0

As a starting point, a simple Perl script to calculate the number of % characters in double quotes before a comma or closing paren.

perl -0777pe 's/printf\s*\(\s*\"((?:[^"]*\"\s*\")*[^"]*)\"\s*([,)]\s*)/
  my ($f, $c, $t) = ($1, $1, $2);
  sprintf("printf%i(\"%s\"%s", ($c =~ tr-%--), $f, $t) /ges'  files ...

This will substitute inside quoted strings, comments, etc; it will catch fprintf and sprintf and myownsacredprintf as well as plain printf.

tripleee
  • 175,061
  • 34
  • 275
  • 318