Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/perl
      2 
      3 use strict;
      4 use File::Find;
      5 use File::Temp qw/ tempfile tempdir /;
      6 use Getopt::Std;
      7 use Pod::Usage;
      8 use Text::Tabs;
      9 
     10 =head1 NAME
     11 
     12 B<sed-sources> -- Performs multiple sed commands on files with the ability to expand or unexpand tabs.
     13 
     14 =head1 SYNOPSIS
     15 
     16 B<sed-sources> [options] [file dir ...]
     17 
     18 =head1 DESCRIPTION
     19 
     20 Performs multiple sed commands (modify builtin %seds hash) on source files
     21 or any sources in directories. If no arguments are given, STDIN will be used
     22 as the source. If source files or directories are specified as arguments,
     23 all files will be transformed and overwritten with new versions. Use the B<-p>
     24 option to preview changes to STDOUT, or use the B<-b> option to make a backup
     25 or the original files.
     26 
     27 =head1 OPTIONS
     28 
     29 =over
     30 
     31 =item B<-b>
     32 
     33 Backup original source file by appending ".bak" before overwriting with the
     34 newly transformed file.
     35 
     36 =item B<-g>
     37 
     38 Display verbose debug logging.
     39 
     40 =item B<-e>
     41 
     42 Expand tabs to spaces (in addition to doing sed substitutions).
     43 
     44 =item B<-u>
     45 
     46 Unexpand spaces to tabs (in addition to doing sed substitutions).
     47 
     48 =item B<-p>
     49 
     50 Preview changes to STDOUT without modifying original source files.
     51 
     52 =item B<-r>
     53 
     54 Skip variants when doing multiple files (no _profile or _debug variants). 
     55 
     56 =item B<-t N>
     57 
     58 Set the number of spaces per tab (default is 4) to use when expanding or
     59 unexpanding.
     60 
     61 =back
     62 
     63 =head1 EXAMPLES
     64  
     65 # Recursively process all source files in the current working directory 
     66 # and and subdirectories and also expand tabs to spaces. All source files 
     67 # will be overwritten with the newly transformed source files.
     68 
     69 % sed-sources -e $cwd
     70 
     71 # Recursively process all source files in the current working directory 
     72 # and and subdirectories and also unexpand spaces to tabs and preview the
     73 # results to STDOUT
     74 
     75 % sed-sources -p -u $cwd
     76 
     77 # Same as above except use 8 spaces per tab. 
     78 
     79 % sed-sources -p -u -t8 $cwd
     80 
     81 =cut
     82 
     83 
     84 our $opt_b = 0;	# Backup original file?
     85 our $opt_g = 0;	# Verbose debug output?
     86 our $opt_e = 0;	# Expand tabs to spaces?
     87 our $opt_h = 0; # Show help?
     88 our $opt_m = 0;	# Show help manpage style?
     89 our $opt_p = 0;	# Preview changes to STDOUT?
     90 our $opt_t = 4;	# Number of spaces per tab?
     91 our $opt_u = 0;	# Unexpand spaces to tabs?
     92 getopts('eghmpt:u'); 
     93 
     94 $opt_m and show_manpage();
     95 $opt_h and help();
     96 
     97 our %seds = (
     98 	'\s+$' => "\n",		# Get rid of spaces at the end of a line
     99 	'^\s+$' => "\n",	# Get rid spaces on lines that are all spaces
    100 );
    101 
    102 
    103 sub show_manpage { exit pod2usage( verbose => 2 ); };
    104 sub help { exit pod2usage( verbose => 3, noperldoc => 1 ); };
    105 
    106 
    107 #----------------------------------------------------------------------
    108 # process_opened_file_handle
    109 #---------------------------------------------------------------------- 
    110 sub process_opened_file_handle
    111 {
    112 	my $in_fh = shift;
    113 	my $out_fh = shift;
    114 
    115 	# Set the number of spaces per tab for expand/unexpand
    116 	$tabstop = $opt_t; 
    117 	
    118 	while (my $line = <$in_fh>) 
    119 	{
    120 		foreach my $key (keys %seds)
    121 		{
    122 			my $value = $seds{"$key"};
    123 			$line =~ s/$key/$value/g;
    124 		}	
    125 		if ($opt_e) {
    126 			print $out_fh expand $line;
    127 		} elsif ($opt_u) {
    128 			print $out_fh unexpand $line;
    129 		} else {
    130 			print $out_fh $line;
    131 		}
    132 	}
    133 }
    134 
    135 #----------------------------------------------------------------------
    136 # process_file
    137 #---------------------------------------------------------------------- 
    138 sub process_file
    139 {
    140 	my $in_path = shift;
    141 	if (-T $in_path) 
    142 	{ 
    143 		my $out_fh;
    144 		my $out_path;
    145 		if ($opt_p)
    146 		{
    147 			# Preview to STDOUT
    148 			$out_fh = *STDOUT;
    149 			print "#---------------------------------------------------------------------- \n";
    150 			print "# BEGIN: '$in_path'\n";
    151 			print "#---------------------------------------------------------------------- \n";
    152 		}
    153 		else
    154 		{
    155 			($out_fh, $out_path) = tempfile();			
    156 			$opt_g and print "temporary for '$in_path' is '$out_path'\n";
    157 		}
    158 		open (IN, "<$in_path") or die "error: can't open '$in_path' for reading: $!";
    159 		process_opened_file_handle (*IN, $out_fh);
    160 		
    161 
    162 		# Close our input file
    163 		close (IN);
    164 
    165 		if ($opt_p)
    166 		{
    167 			print "#---------------------------------------------------------------------- \n";
    168 			print "# END: '$in_path'\n";
    169 			print "#---------------------------------------------------------------------- \n";
    170 			print "\n\n";
    171 		}
    172 		else
    173 		{
    174 			# Close the output file if it wasn't STDOUT
    175 			close ($out_fh);
    176 		
    177 			# Backup file if requested
    178 			if ($opt_b)
    179 			{
    180 				my $backup_command = "cp '$in_path' '$in_path.bak'";
    181 				$opt_g and print "\% $backup_command\n";
    182 				system ($backup_command);
    183 			}
    184 		
    185 			# Copy temp file over original
    186 			my $copy_command = "cp '$out_path' '$in_path'";
    187 			$opt_g and print "\% $copy_command\n";
    188 			system ($copy_command);
    189 		}
    190 	}
    191 }
    192 
    193 our @valid_extensions = ( "h", "cpp", "c", "m", "mm" );
    194 
    195 #----------------------------------------------------------------------
    196 # find_callback
    197 #---------------------------------------------------------------------- 
    198 sub find_callback
    199 {
    200 	my $file = $_;
    201 	my $fullpath = $File::Find::name;
    202 
    203 	foreach my $ext (@valid_extensions)
    204 	{
    205 		my $ext_regex = "\\.$ext\$";
    206 		if ($fullpath =~ /$ext_regex/i)
    207 		{
    208 			print "processing: '$fullpath'\n";
    209 			process_file ($fullpath);
    210 			return;
    211 		}
    212 	}
    213 	print "  ignoring: '$fullpath'\n";
    214 }
    215 
    216 
    217 #----------------------------------------------------------------------
    218 # main
    219 #---------------------------------------------------------------------- 
    220 sub main
    221 {
    222 	if (@ARGV == 0)
    223 	{
    224 		# no args, take from STDIN and put to STDOUT
    225 		process_opened_file_handle (*STDIN, *STDOUT);
    226 	}
    227 	else
    228 	{
    229 		# Got args, any files we run into parse them, any directories
    230 		# we run into, search them for files
    231 		my $path;
    232 		foreach $path (@ARGV)
    233 		{
    234 			if (-f $path)
    235 			{
    236 				print "processing: '$path'\n";
    237 				process_file ($path);
    238 			}
    239 			else
    240 			{
    241 				print " searching: '$path'\n";
    242 				find(\&find_callback, $path);					
    243 			}
    244 		}
    245 	}
    246 }
    247 
    248 
    249 
    250 # call the main function
    251 main();