| #!/usr/bin/perl |
| |
| use strict; |
| use File::Find; |
| use File::Temp qw/ tempfile tempdir /; |
| use Getopt::Std; |
| use Pod::Usage; |
| use Text::Tabs; |
| |
| =head1 NAME |
| |
| B<sed-sources> -- Performs multiple sed commands on files with the ability to expand or unexpand tabs. |
| |
| =head1 SYNOPSIS |
| |
| B<sed-sources> [options] [file dir ...] |
| |
| =head1 DESCRIPTION |
| |
| Performs multiple sed commands (modify builtin %seds hash) on source files |
| or any sources in directories. If no arguments are given, STDIN will be used |
| as the source. If source files or directories are specified as arguments, |
| all files will be transformed and overwritten with new versions. Use the B<-p> |
| option to preview changes to STDOUT, or use the B<-b> option to make a backup |
| or the original files. |
| |
| =head1 OPTIONS |
| |
| =over |
| |
| =item B<-b> |
| |
| Backup original source file by appending ".bak" before overwriting with the |
| newly transformed file. |
| |
| =item B<-g> |
| |
| Display verbose debug logging. |
| |
| =item B<-e> |
| |
| Expand tabs to spaces (in addition to doing sed substitutions). |
| |
| =item B<-u> |
| |
| Unexpand spaces to tabs (in addition to doing sed substitutions). |
| |
| =item B<-p> |
| |
| Preview changes to STDOUT without modifying original source files. |
| |
| =item B<-r> |
| |
| Skip variants when doing multiple files (no _profile or _debug variants). |
| |
| =item B<-t N> |
| |
| Set the number of spaces per tab (default is 4) to use when expanding or |
| unexpanding. |
| |
| =back |
| |
| =head1 EXAMPLES |
| |
| # Recursively process all source files in the current working directory |
| # and subdirectories and also expand tabs to spaces. All source files |
| # will be overwritten with the newly transformed source files. |
| |
| % sed-sources -e $cwd |
| |
| # Recursively process all source files in the current working directory |
| # and subdirectories and also unexpand spaces to tabs and preview the |
| # results to STDOUT |
| |
| % sed-sources -p -u $cwd |
| |
| # Same as above except use 8 spaces per tab. |
| |
| % sed-sources -p -u -t8 $cwd |
| |
| =cut |
| |
| |
| our $opt_b = 0; # Backup original file? |
| our $opt_g = 0; # Verbose debug output? |
| our $opt_e = 0; # Expand tabs to spaces? |
| our $opt_h = 0; # Show help? |
| our $opt_m = 0; # Show help manpage style? |
| our $opt_p = 0; # Preview changes to STDOUT? |
| our $opt_t = 4; # Number of spaces per tab? |
| our $opt_u = 0; # Unexpand spaces to tabs? |
| getopts('eghmpt:u'); |
| |
| $opt_m and show_manpage(); |
| $opt_h and help(); |
| |
| our %seds = ( |
| '\s+$' => "\n", # Get rid of spaces at the end of a line |
| '^\s+$' => "\n", # Get rid spaces on lines that are all spaces |
| ); |
| |
| |
| sub show_manpage { exit pod2usage( verbose => 2 ); }; |
| sub help { exit pod2usage( verbose => 3, noperldoc => 1 ); }; |
| |
| |
| #---------------------------------------------------------------------- |
| # process_opened_file_handle |
| #---------------------------------------------------------------------- |
| sub process_opened_file_handle |
| { |
| my $in_fh = shift; |
| my $out_fh = shift; |
| |
| # Set the number of spaces per tab for expand/unexpand |
| $tabstop = $opt_t; |
| |
| while (my $line = <$in_fh>) |
| { |
| foreach my $key (keys %seds) |
| { |
| my $value = $seds{"$key"}; |
| $line =~ s/$key/$value/g; |
| } |
| if ($opt_e) { |
| print $out_fh expand $line; |
| } elsif ($opt_u) { |
| print $out_fh unexpand $line; |
| } else { |
| print $out_fh $line; |
| } |
| } |
| } |
| |
| #---------------------------------------------------------------------- |
| # process_file |
| #---------------------------------------------------------------------- |
| sub process_file |
| { |
| my $in_path = shift; |
| if (-T $in_path) |
| { |
| my $out_fh; |
| my $out_path; |
| if ($opt_p) |
| { |
| # Preview to STDOUT |
| $out_fh = *STDOUT; |
| print "#---------------------------------------------------------------------- \n"; |
| print "# BEGIN: '$in_path'\n"; |
| print "#---------------------------------------------------------------------- \n"; |
| } |
| else |
| { |
| ($out_fh, $out_path) = tempfile(); |
| $opt_g and print "temporary for '$in_path' is '$out_path'\n"; |
| } |
| open (IN, "<$in_path") or die "error: can't open '$in_path' for reading: $!"; |
| process_opened_file_handle (*IN, $out_fh); |
| |
| |
| # Close our input file |
| close (IN); |
| |
| if ($opt_p) |
| { |
| print "#---------------------------------------------------------------------- \n"; |
| print "# END: '$in_path'\n"; |
| print "#---------------------------------------------------------------------- \n"; |
| print "\n\n"; |
| } |
| else |
| { |
| # Close the output file if it wasn't STDOUT |
| close ($out_fh); |
| |
| # Backup file if requested |
| if ($opt_b) |
| { |
| my $backup_command = "cp '$in_path' '$in_path.bak'"; |
| $opt_g and print "\% $backup_command\n"; |
| system ($backup_command); |
| } |
| |
| # Copy temp file over original |
| my $copy_command = "cp '$out_path' '$in_path'"; |
| $opt_g and print "\% $copy_command\n"; |
| system ($copy_command); |
| } |
| } |
| } |
| |
| our @valid_extensions = ( "h", "cpp", "c", "m", "mm" ); |
| |
| #---------------------------------------------------------------------- |
| # find_callback |
| #---------------------------------------------------------------------- |
| sub find_callback |
| { |
| my $file = $_; |
| my $fullpath = $File::Find::name; |
| |
| foreach my $ext (@valid_extensions) |
| { |
| my $ext_regex = "\\.$ext\$"; |
| if ($fullpath =~ /$ext_regex/i) |
| { |
| print "processing: '$fullpath'\n"; |
| process_file ($fullpath); |
| return; |
| } |
| } |
| print " ignoring: '$fullpath'\n"; |
| } |
| |
| |
| #---------------------------------------------------------------------- |
| # main |
| #---------------------------------------------------------------------- |
| sub main |
| { |
| if (@ARGV == 0) |
| { |
| # no args, take from STDIN and put to STDOUT |
| process_opened_file_handle (*STDIN, *STDOUT); |
| } |
| else |
| { |
| # Got args, any files we run into parse them, any directories |
| # we run into, search them for files |
| my $path; |
| foreach $path (@ARGV) |
| { |
| if (-f $path) |
| { |
| print "processing: '$path'\n"; |
| process_file ($path); |
| } |
| else |
| { |
| print " searching: '$path'\n"; |
| find(\&find_callback, $path); |
| } |
| } |
| } |
| } |
| |
| |
| |
| # call the main function |
| main(); |