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