| #!/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(); |