1

I need to wrap all double quoted strings in all .cpp & .h file in a directory with a macro _T()

All files are unicode.

Can anyone help me write a perl or bash shell?

I know that perl should be great in it. I just know a bit of bash shell and cannot completely make it automatically working.

Now I use ^(?!#)(.*)(".*?") and $1_T($2) in sublime Text2, I don't know why it is just part of replace. ( some strings add _T() while some not). And some file like readme.txt *.poj etc should not be replace.

And avoiding repeat replace, I know the \b is word boundary. But ^(?!#).*\b(!_T\(")(".*?")\b seem not working.

Kishimo
  • 83
  • 5
  • The problem is one of matching balanced text, and it suffers from many pitfalls including the possibility escaped embedded quotes. This StackOverflow article will be helpful: http://stackoverflow.com/q/4445674/716443 ...not that it suggests the Text::Balanced module, or for a regexp approach, Regexp::Common – DavidO Mar 22 '14 at 17:45

1 Answers1

1

This is a harder problem then you realize, however a quickie solution could be the following:

use strict;
use warnings;

for my $file (glob("*.cpp"), glob("*.h")) {
    local @ARGV = $file;
    local $^I = '.bak';
    die "Backup already exists for $file" if -e "$file$^I";
    while (<>) {
        if (! /^#include/) {
            s/("(?:(?>[^"\\]+)|\\.)*")/_T($1)/g;
        }
        print;
    }
    # unlink "$file$^I"; # If you want to delete the backup
}

I would of course change the for loop to a single file during testing:

for my $file ("single_file_to_test.cpp") {

And you can uncomment the unlink command if you'd like the delete the backups that are placed in .bak

Miller
  • 34,344
  • 4
  • 33
  • 55
  • 1
    It would be useful to include a "--dryrun" command, where all changes just go to the screen. Or possibly to just recommend doing a `git checkout -b quotemacro` before running, and then a `git diff` afterward to see that the changes were as expected/desired. – DavidO Mar 22 '14 at 17:50
  • If the goal was 100% production ready code, yes. But honestly, I think that replacing the `for` loop in the code is a better and safer way of testing. There's no risk of forgetting to do `--dryrun` while tabbing through old commands. Additionally, since I have it save a backup file by default, can just do a `diff file.cpp file.cpp.bak` to see the changes that were made. So overall this accomplishes your intent. – Miller Mar 22 '14 at 17:55
  • You're right; you have taken steps to reduce the risks. My goal is to alert the OP that it *is* risky to trust sweeping code changes to an untested script, and to mitigate the risk by sandboxing the changes for full review. – DavidO Mar 22 '14 at 18:02
  • 1
    Yes, it is risky, and it's good of you to alert him. I went ahead and threw in an additional die command that will exit if a backup already exists so repeated runs of the script will never clobber a backup. Even if he blindly runs this code as it stand right now, he will lose no data. – Miller Mar 22 '14 at 18:06
  • Can it avoid add macro to `#include "myfile.h"` , maybe I just need to add ^(?!#) to your regex? – Kishimo Mar 23 '14 at 02:20
  • Keep things simple, no need to edit the current regex. Instead just add a condition before the substitution. (Edited the post). – Miller Mar 23 '14 at 02:23
  • Awesome!! It works well! I don't know how to thanks you!! I use it with shell script `for dir in $(ls -d */);do pushd ${dir%%/}; ~/replace.pl;popd;done;`. In perl it maybe need File::Find to callback subroutine. Whatever it just works very well for me. Thankyou again! – Kishimo Mar 23 '14 at 03:26
  • Cool, glad it works as you desired. Feel free to mark this as the answer. And yes, you could integrate this with [`File::Find`](http://perldoc.perl.org/File/Find.html) very easily if you wanted. It also might be nice to add logging for the exact changes that are being made, and output that to a separate file. It would be fairly easy to do that, but will leave that up to you. – Miller Mar 23 '14 at 03:30