├── perl ├── 0.a │ ├── .htaccess │ └── 1.a │ │ ├── .htaccess │ │ └── 2.a │ │ ├── .htaccess │ │ └── 3.a │ │ └── .htaccess ├── 0.b │ ├── .htaccess │ └── 1.d │ │ ├── .htaccess │ │ └── 2.b │ │ └── .htaccess ├── 0.c │ ├── .htaccess │ └── 1.g │ │ └── .htaccess ├── htconf.php ├── htconf.pl ├── htcrawler.php ├── htcrawler.pl └── diffplphp.diff ├── README └── htaccess.php /perl/0.a/.htaccess: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /perl/0.b/.htaccess: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /perl/0.c/.htaccess: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /perl/0.a/1.a/.htaccess: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /perl/0.b/1.d/.htaccess: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /perl/0.c/1.g/.htaccess: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /perl/0.a/1.a/2.a/.htaccess: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /perl/0.a/1.a/2.a/3.a/.htaccess: -------------------------------------------------------------------------------- 1 | ApacheParam 1 2 | ApacheParam 2 3 | -------------------------------------------------------------------------------- /perl/0.b/1.d/2.b/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | ##Comment line! 3 | ## 4 | RedirectBase blalbh 5 | SomethingElse 6 | ApacheParam3 doo 7 | RedirectBase Again! 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This script recursively searches from the present working directory for .htaccess files. It converts the directives found within the .htaccess files into the apropriate format for addition within the httpd.conf configuration file. 2 | 3 | This is useful as .htaccess files are incredibly easy to work with during development, but a major performance issue in production. Get the best of both worlds, let your devs work on the fly, then merge as part of your deployment process 4 | 5 | 6 | -------------------------------------------------------------------------------- /perl/htconf.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ##Comment line! 32 | ## 33 | RedirectBase blalbh 34 | # WARNING The above line contains RedirectBase which may not convert directly to a conf file. Please check manually 35 | SomethingElse 36 | ApacheParam3 doo 37 | RedirectBase Again! 38 | # WARNING The above line contains RedirectBase which may not convert directly to a conf file. Please check manually 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ApacheParam 1 52 | ApacheParam 2 53 | 54 | 55 | # A total of 2 warnings were encountered. Please read through the file and correct any noted problems 56 | # Please test before going live, no guarantees! 57 | -------------------------------------------------------------------------------- /perl/htconf.pl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ##Comment line! 19 | ## 20 | 21 | ####WARNING! RedirectBase' does't convert correctly. Check manually! 22 | RedirectBase blalbh 23 | SomethingElse 24 | 25 | ####WARNING! Apache params not supported. ;-) 26 | ApacheParam3 doo 27 | 28 | ####WARNING! RedirectBase' does't convert correctly. Check manually! 29 | RedirectBase Again! 30 | 31 | 32 | 33 | ####WARNING! Apache params not supported. ;-) 34 | ApacheParam 1 35 | 36 | ####WARNING! Apache params not supported. ;-) 37 | ApacheParam 2 38 | 39 | 40 | ###################################################### 41 | #Total Warnings: 5 42 | #%ErrorTypeFound = ( 43 | # 'RedirectBase' => 2, 44 | # 'OtherParamLabel' => 3 45 | # ); 46 | ###################################################### 47 | 48 | ###################################################### 49 | ## Total Files processed: 10 50 | ###################################################### 51 | 52 | ## Please test before going live, no guarantees! 53 | -------------------------------------------------------------------------------- /perl/htcrawler.php: -------------------------------------------------------------------------------- 1 | php htaccess.php > ~/htaccess.conf 12 | * Filtering to exclude (substring match) 13 | * /var/www/domain.com> php htaccess.php evilDirectory > ~/htaccess.conf 14 | */ 15 | 16 | //Start from the present working directory, recurse from here. Using the real path avoids a bug where .htaccess files in the PWD are omitted from results on some systems 17 | $startPath = realpath("./"); 18 | $ite= new RecursiveDirectoryIterator($startPath); 19 | 20 | //Lets give people a hand, skip these directories. Especially helpful when run on dev systems 21 | $filters = array(".svn", ".cvs"); 22 | 23 | //Merge base set of filters with any from the command line 24 | if(count($argv) > 0) 25 | { 26 | unset($argv[0]); 27 | $filters = array_merge($filters, $argv); 28 | } 29 | 30 | //Iterate recursively through everything from here on in, of course filtering out stuff from the filter list 31 | foreach (new fileFilter(new RecursiveIteratorIterator($ite), $filters) as $filename=>$cur) 32 | { 33 | $htaccessFiles[] = $filename; 34 | } 35 | 36 | //No files? Quit now! 37 | if (count($htaccessFiles) == 0) 38 | { 39 | die("No .htaccess files found"); 40 | } 41 | 42 | //Sort the list, place least depth first. This is important to allow overrides from sub-directories to occur correctly 43 | usort($htaccessFiles, 'sorter'); 44 | 45 | //Warnings encountered 46 | $flags = 0; 47 | 48 | //Iterate over found files (sorted now) and read them in. 49 | foreach($htaccessFiles as $file) 50 | { 51 | //Grab the file and print out the bit 52 | $path = realpath(pathinfo($file, PATHINFO_DIRNAME)); 53 | echo "\n"; 54 | $lines = file($file); 55 | if(count($lines) > 0) 56 | { 57 | //Tab the file in, check for RedirectBase which may cause problems 58 | foreach($lines as $line) 59 | { 60 | echo "\t$line"; 61 | if(stripos($line, "RedirectBase") !== FALSE) 62 | { 63 | //Not tabbed! See what happens there? 64 | echo "# WARNING The above line contains RedirectBase which may not convert directly to a conf file. Please check manually\n"; 65 | $flags++; 66 | } 67 | } 68 | 69 | //Handle issues where files don't end with a newline 70 | if (in_array(substr($line, -1), array("\n", "\r"))) 71 | { 72 | echo "\n\n"; 73 | }else 74 | { 75 | echo "\n\n\n"; 76 | } 77 | }else{ 78 | //File was empty, leave the stub in 79 | echo "\n\n\n"; 80 | } 81 | } 82 | 83 | //Check for warnings 84 | if ($flags > 0) 85 | { 86 | echo "# A total of $flags warnings were encountered. Please read through the file and correct any noted problems\n"; 87 | }else 88 | { 89 | echo "# No warnings detected \n"; 90 | } 91 | 92 | echo "# Please test before going live, no guarantees! \n"; 93 | 94 | 95 | 96 | 97 | //Sort by the number of path segments, least first 98 | function sorter($a, $b) 99 | { 100 | $a = count(explode("/", $a)); 101 | $b = count(explode("/", $b)); 102 | if($a == $b) 103 | { 104 | return 0; 105 | } 106 | if($a > $b) 107 | { 108 | return 1; 109 | } 110 | return -1; 111 | } 112 | 113 | 114 | 115 | //Filter out specified files. 116 | class fileFilter extends FilterIterator 117 | { 118 | private $filters; 119 | public function __construct(Iterator $iterator, $filters) 120 | { 121 | parent::__construct($iterator); 122 | $this->filters = $filters; 123 | } 124 | 125 | public function accept() 126 | { 127 | $dir = $this->getInnerIterator()->current(); 128 | foreach($this->filters as $filter) 129 | { 130 | if(strpos($dir, $filter) !== false) 131 | { 132 | return false; 133 | } 134 | } 135 | 136 | if (strpos($dir, ".htaccess") !== false) 137 | { 138 | return true; 139 | } 140 | //echo "Skipping $dir\n"; 141 | return false; 142 | } 143 | } 144 | 145 | -------------------------------------------------------------------------------- /htaccess.php: -------------------------------------------------------------------------------- 1 | php htaccess.php > ~/htaccess.conf 20 | * Filtering to exclude (substring match) 21 | * /var/www/domain.com> php htaccess.php evilDirectory > ~/htaccess.conf 22 | */ 23 | 24 | //Start from the present working directory, recurse from here. Using the real path avoids a bug where .htaccess files in the PWD are omitted from results on some systems 25 | $startPath = realpath("./"); 26 | $ite= new RecursiveDirectoryIterator($startPath); 27 | 28 | //Lets give people a hand, skip these directories. Especially helpful when run on dev systems 29 | $filters = array(".svn", ".cvs", ".git"); 30 | 31 | //Merge base set of filters with any from the command line 32 | if(count($argv) > 0) 33 | { 34 | unset($argv[0]); 35 | $filters = array_merge($filters, $argv); 36 | } 37 | 38 | //Iterate recursively through everything from here on in, of course filtering out stuff from the filter list 39 | foreach (new fileFilter(new RecursiveIteratorIterator($ite), $filters) as $filename=>$cur) 40 | { 41 | $htaccessFiles[] = $filename; 42 | } 43 | 44 | //No files? Quit now! 45 | if (count($htaccessFiles) == 0) 46 | { 47 | die("No .htaccess files found"); 48 | } 49 | 50 | //Sort the list, place least depth first. This is important to allow overrides from sub-directories to occur correctly 51 | usort($htaccessFiles, 'sorter'); 52 | 53 | //Warnings encountered 54 | $flags = 0; 55 | 56 | //Iterate over found files (sorted now) and read them in. 57 | foreach($htaccessFiles as $file) 58 | { 59 | //Grab the file and print out the bit 60 | $path = realpath(pathinfo($file, PATHINFO_DIRNAME)); 61 | echo "\n"; 62 | $lines = file($file); 63 | if(count($lines) > 0) 64 | { 65 | //Tab the file in, check for RedirectBase which may cause problems 66 | foreach($lines as $line) 67 | { 68 | echo "\t$line"; 69 | if(stripos($line, "RedirectBase") !== FALSE) 70 | { 71 | //Not tabbed! See what happens there? 72 | echo "# WARNING The above line contains RedirectBase which may not convert directly to a conf file. Please check manually\n"; 73 | $flags++; 74 | } 75 | } 76 | 77 | //Handle issues where files don't end with a newline 78 | if (in_array(substr($line, -1), array("\n", "\r"))) 79 | { 80 | echo "\n\n"; 81 | }else 82 | { 83 | echo "\n\n\n"; 84 | } 85 | }else{ 86 | //File was empty, leave the stub in 87 | echo "\n\n\n"; 88 | } 89 | } 90 | 91 | //Check for warnings 92 | if ($flags > 0) 93 | { 94 | echo "# A total of $flags warnings were encountered. Please read through the file and correct any noted problems\n"; 95 | }else 96 | { 97 | echo "# No warnings detected \n"; 98 | } 99 | 100 | echo "# Please test before going live, no guarantees! \n"; 101 | 102 | 103 | 104 | 105 | //Sort by the number of path segments, least first 106 | function sorter($a, $b) 107 | { 108 | $a = count(explode("/", $a)); 109 | $b = count(explode("/", $b)); 110 | if($a == $b) 111 | { 112 | return 0; 113 | } 114 | if($a > $b) 115 | { 116 | return 1; 117 | } 118 | return -1; 119 | } 120 | 121 | 122 | 123 | //Filter out specified files. 124 | class fileFilter extends FilterIterator 125 | { 126 | private $filters; 127 | public function __construct(Iterator $iterator, $filters) 128 | { 129 | parent::__construct($iterator); 130 | $this->filters = $filters; 131 | } 132 | 133 | public function accept() 134 | { 135 | $dir = $this->getInnerIterator()->current(); 136 | foreach($this->filters as $filter) 137 | { 138 | if(strpos($dir, $filter) !== false) 139 | { 140 | return false; 141 | } 142 | } 143 | 144 | $filename = DIRECTORY_SEPARATOR . '.htaccess'; 145 | if (substr($dir, - strlen($filename), strlen($filename)) == $filename) 146 | { 147 | return true; 148 | } 149 | //echo "Skipping $dir\n"; 150 | return false; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /perl/htcrawler.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | ## 3 | # 2009 Kurt P. Hundeck (whereiskurt@gmail.com) 4 | # 5 | # Recursively search through '--path' for files that match '--match' regex, 6 | # slurp those files up, and output them as descendents of apache conf 7 | # '$slurped htaccess.conf 11 | # or 12 | # perl htcrawler.pl --path=./ --match='\.htaccess$' > htaccess.conf 13 | # or 14 | # ./htcrawler.pl --path=/var/www/somesite.com/ > somesite.com.conf 15 | # 16 | # NOTE: It's the 'AccessFileName .htaccess' (default), it *could* be something 17 | # else, use '--match' to override. 18 | # 19 | use strict; 20 | use warnings; 21 | 22 | use English; 23 | use Data::Dumper; 24 | use Getopt::Long qw(GetOptions); 25 | use File::Find qw(find); 26 | use File::Spec::Functions qw(rel2abs splitdir); 27 | use File::Basename qw(dirname); 28 | 29 | ##Set some reasonable defaults 30 | my %argv = ( 31 | 'startpath' => dirname(rel2abs($0)), ## Real fullpath of script, default. 32 | 'match' => qr{\.htaccess$}, ## Regex for ".htaccess" files 33 | 'skip' => qr{^((.cvs)|(.svn))$}, 34 | ); 35 | 36 | ##Parse commandline 37 | GetOptions ( 'startpath|path=s' => \$argv{'startpath'} , 38 | 'match=s' => \$argv{'match'} , 39 | 'skip=s' => \$argv{'skip'} , 40 | ) or die "Can't parse commandline (!)"; 41 | 42 | my @matches; ##Holds the fullnames of matching files (/dir1/dir2/.htaccess) 43 | 44 | ##Find files that match our criteria. 45 | File::Find::find({ 'preprocess' => \&skiprcs , #Filter hook 46 | 'wanted' => \&wanted , } #Check match hook 47 | , $argv{'startpath'}); 48 | 49 | sub skiprcs { 50 | ## 'grep' through the arglist (@_) and return things that 51 | ## don't match .cvs|.svn 52 | return grep { !/$argv{'skip'}/ } @_; 53 | }; 54 | 55 | sub wanted { 56 | ##Add *fullpath* filename to matches, if we matched. 57 | if ($File::Find::name =~ m/$argv{'match'}/i) { 58 | push @matches, $File::Find::name; 59 | } 60 | } 61 | 62 | if (not @matches ) { 63 | die "No files matched '$argv{'match'}' in path '$argv{'startpath'}'.\n"; 64 | } 65 | 66 | ##Sort by shortest path depth (ie. '/folder' before '/folder/subfolder') 67 | ##by comparying the length of arrays returned by 'splitdir' 68 | @matches = sort { splitdir($a) <=> splitdir($b) } @matches; 69 | 70 | my (%errflag, $errcount); 71 | 72 | ## Hash lookup to support different failures for conversion. Certain 73 | ## pragmas won't convert 1-to-1 (like "RedirectBase"), these are the rules. 74 | my %error_lkp = ( 75 | 'RedirectBase' => { 76 | 're'=>qr/RedirectBase/ix, 77 | 'msg'=>"##WARNING! RedirectBase' does't convert correctly. Check manually!", 78 | }, 79 | 'OtherParamLabel' => { 80 | 're'=>qr/ApacheParam*/ix, 81 | 'msg'=>"##WARNING! Apache params not supported. ;-)" 82 | }, 83 | ); 84 | 85 | foreach my $file (@matches) { 86 | ##Perl5 'slurp' $file idiom 87 | my $text = do { local( @ARGV, $RS ) = $file ; <> } ; 88 | 89 | if ($text) { 90 | ##Cleanup leading/trailing whitespace 91 | $text =~ s/^(.+?)\s*$/$1/gmix; #ltrim() 92 | $text =~ s/^\s*(.+?)$/$1/gmix; #rtrim() 93 | 94 | # Use the error lookup to see if the text contains things that don't 95 | # convert from .htaccess (like RedirectBase..) 96 | while( my ($key, $value) = each(%error_lkp)) { 97 | my $re = $value->{'re'}; #Each error has it's own match pattern 98 | my $msg = $value->{'msg'}; #and error message. 99 | 100 | ##If the text matched the error regex, insert a warning $msg 101 | ## and track/count it. 102 | if (my $count = $text =~ s/^(.*?$re.*?)$/$RS##$msg$RS$1/gmix) { 103 | $errflag{$key}+=$count; 104 | $errcount +=$count; 105 | } 106 | } 107 | 108 | ##Add a "tab" infront of each line 109 | $text =~ s/^(.+?)$/\t$1/gmix; 110 | ##Add a newline, back. 111 | $text .= $RS; 112 | } 113 | else { 114 | $text = ""; ##NOTE: $text is undef until we make it an "empty string" 115 | } 116 | 117 | my $dir = dirname($file); 118 | print "$RS"; 119 | print $text; 120 | print "$RS"; 121 | 122 | } 123 | 124 | ## Output a commented block (Apache style) with any erros we found. 125 | if ($errcount > 0) { 126 | ##Convert our %errflag to a nicely Dump'd output. 127 | my $errors = Data::Dumper->Dump( [ \%errflag ], [ qw(*ErrorTypeFound) ] ); 128 | 129 | ##Append '#' infront of each line of $errors. 130 | $errors =~ s/^(.+?)$/#$1/gmix; 131 | 132 | print $RS; 133 | print "######################################################$RS"; 134 | print "#Total Warnings: $errcount $RS"; 135 | print $errors; 136 | print "######################################################$RS"; 137 | print $RS; 138 | } 139 | 140 | print "######################################################$RS"; 141 | print "## Total Files processed: " . scalar @matches . $RS; 142 | print "######################################################$RS"; 143 | print $RS; 144 | print "## Please test before going live, no guarantees! $RS"; 145 | -------------------------------------------------------------------------------- /perl/diffplphp.diff: -------------------------------------------------------------------------------- 1 | 1,144c1,144 2 | < php htaccess.php > ~/htaccess.conf 13 | < * Filtering to exclude (substring match) 14 | < * /var/www/domain.com> php htaccess.php evilDirectory > ~/htaccess.conf 15 | < */ 16 | < 17 | < //Start from the present working directory, recurse from here. Using the real path avoids a bug where .htaccess files in the PWD are omitted from results on some systems 18 | < $startPath = realpath("./"); 19 | < $ite= new RecursiveDirectoryIterator($startPath); 20 | < 21 | < //Lets give people a hand, skip these directories. Especially helpful when run on dev systems 22 | < $filters = array(".svn", ".cvs"); 23 | < 24 | < //Merge base set of filters with any from the command line 25 | < if(count($argv) > 0) 26 | < { 27 | < unset($argv[0]); 28 | < $filters = array_merge($filters, $argv); 29 | < } 30 | < 31 | < //Iterate recursively through everything from here on in, of course filtering out stuff from the filter list 32 | < foreach (new fileFilter(new RecursiveIteratorIterator($ite), $filters) as $filename=>$cur) 33 | < { 34 | < $htaccessFiles[] = $filename; 35 | < } 36 | < 37 | < //No files? Quit now! 38 | < if (count($htaccessFiles) == 0) 39 | < { 40 | < die("No .htaccess files found"); 41 | < } 42 | < 43 | < //Sort the list, place least depth first. This is important to allow overrides from sub-directories to occur correctly 44 | < usort($htaccessFiles, 'sorter'); 45 | < 46 | < //Warnings encountered 47 | < $flags = 0; 48 | < 49 | < //Iterate over found files (sorted now) and read them in. 50 | < foreach($htaccessFiles as $file) 51 | < { 52 | < //Grab the file and print out the bit 53 | < $path = realpath(pathinfo($file, PATHINFO_DIRNAME)); 54 | < echo "\n"; 55 | < $lines = file($file); 56 | < if(count($lines) > 0) 57 | < { 58 | < //Tab the file in, check for RedirectBase which may cause problems 59 | < foreach($lines as $line) 60 | < { 61 | < echo "\t$line"; 62 | < if(stripos($line, "RedirectBase") !== FALSE) 63 | < { 64 | < //Not tabbed! See what happens there? 65 | < echo "# WARNING The above line contains RedirectBase which may not convert directly to a conf file. Please check manually\n"; 66 | < $flags++; 67 | < } 68 | < } 69 | < 70 | < //Handle issues where files don't end with a newline 71 | < if (in_array(substr($line, -1), array("\n", "\r"))) 72 | < { 73 | < echo "\n\n"; 74 | < }else 75 | < { 76 | < echo "\n\n\n"; 77 | < } 78 | < }else{ 79 | < //File was empty, leave the stub in 80 | < echo "\n\n\n"; 81 | < } 82 | < } 83 | < 84 | < //Check for warnings 85 | < if ($flags > 0) 86 | < { 87 | < echo "# A total of $flags warnings were encountered. Please read through the file and correct any noted problems\n"; 88 | < }else 89 | < { 90 | < echo "# No warnings detected \n"; 91 | < } 92 | < 93 | < echo "# Please test before going live, no guarantees! \n"; 94 | < 95 | < 96 | < 97 | < 98 | < //Sort by the number of path segments, least first 99 | < function sorter($a, $b) 100 | < { 101 | < $a = count(explode("/", $a)); 102 | < $b = count(explode("/", $b)); 103 | < if($a == $b) 104 | < { 105 | < return 0; 106 | < } 107 | < if($a > $b) 108 | < { 109 | < return 1; 110 | < } 111 | < return -1; 112 | < } 113 | < 114 | < 115 | < 116 | < //Filter out specified files. 117 | < class fileFilter extends FilterIterator 118 | < { 119 | < private $filters; 120 | < public function __construct(Iterator $iterator, $filters) 121 | < { 122 | < parent::__construct($iterator); 123 | < $this->filters = $filters; 124 | < } 125 | < 126 | < public function accept() 127 | < { 128 | < $dir = $this->getInnerIterator()->current(); 129 | < foreach($this->filters as $filter) 130 | < { 131 | < if(strpos($dir, $filter) !== false) 132 | < { 133 | < return false; 134 | < } 135 | < } 136 | < 137 | < if (strpos($dir, ".htaccess") !== false) 138 | < { 139 | < return true; 140 | < } 141 | < //echo "Skipping $dir\n"; 142 | < return false; 143 | < } 144 | < } 145 | < 146 | --- 147 | > #!/usr/bin/perl 148 | > ## 149 | > # 2009 Kurt P. Hundeck (whereiskurt@gmail.com) 150 | > # 151 | > # Recursively search through '--path' for files that match '--match' regex, 152 | > # slurp those files up, and output them as descendents of apache conf 153 | > # '$slurped # 155 | > # Usage: 156 | > # perl htcrawler.pl > htaccess.conf 157 | > # or 158 | > # perl htcrawler.pl --path=./ --match='\.htaccess$' > htaccess.conf 159 | > # or 160 | > # ./htcrawler.pl --path=/var/www/somesite.com/ > somesite.com.conf 161 | > # 162 | > # NOTE: It's the 'AccessFileName .htaccess' (default), it *could* be something 163 | > # else, use '--match' to override. 164 | > # 165 | > use strict; 166 | > use warnings; 167 | > 168 | > use English; 169 | > use Data::Dumper; 170 | > use Getopt::Long qw(GetOptions); 171 | > use File::Find qw(find); 172 | > use File::Spec::Functions qw(rel2abs splitdir); 173 | > use File::Basename qw(dirname); 174 | > 175 | > ##Set some reasonable defaults 176 | > my %argv = ( 177 | > 'startpath' => dirname(rel2abs($0)), ## Real fullpath of script, default. 178 | > 'match' => qr{\.htaccess$}, ## Regex for ".htaccess" files 179 | > 'skip' => qr{^((.cvs)|(.svn))$}, 180 | > ); 181 | > 182 | > ##Parse commandline 183 | > GetOptions ( 'startpath|path=s' => \$argv{'startpath'} , 184 | > 'match=s' => \$argv{'match'} , 185 | > 'skip=s' => \$argv{'skip'} , 186 | > ) or die "Can't parse commandline (!)"; 187 | > 188 | > my @matches; ##Holds the fullnames of matching files (/dir1/dir2/.htaccess) 189 | > 190 | > ##Find files that match our criteria. 191 | > File::Find::find({ 'preprocess' => \&skiprcs , #Filter hook 192 | > 'wanted' => \&wanted , } #Check match hook 193 | > , $argv{'startpath'}); 194 | > 195 | > sub skiprcs { 196 | > ## 'grep' through the arglist (@_) and return things that 197 | > ## don't match .cvs|.svn 198 | > return grep { !/$argv{'skip'}/ } @_; 199 | > }; 200 | > 201 | > sub wanted { 202 | > ##Add *fullpath* filename to matches, if we matched. 203 | > if ($File::Find::name =~ m/$argv{'match'}/i) { 204 | > push @matches, $File::Find::name; 205 | > } 206 | > } 207 | > 208 | > if (not @matches ) { 209 | > die "No files matched '$argv{'match'}' in path '$argv{'startpath'}'.\n"; 210 | > } 211 | > 212 | > ##Sort by shortest path depth (ie. '/folder' before '/folder/subfolder') 213 | > ##by comparying the length of arrays returned by 'splitdir' 214 | > @matches = sort { splitdir($a) <=> splitdir($b) } @matches; 215 | > 216 | > my (%errflag, $errcount); 217 | > 218 | > ## Hash lookup to support different failures for conversion. Certain 219 | > ## pragmas won't convert 1-to-1 (like "RedirectBase"), these are the rules. 220 | > my %error_lkp = ( 221 | > 'RedirectBase' => { 222 | > 're'=>qr/RedirectBase/ix, 223 | > 'msg'=>"##WARNING! RedirectBase' does't convert correctly. Check manually!", 224 | > }, 225 | > 'OtherParamLabel' => { 226 | > 're'=>qr/ApacheParam*/ix, 227 | > 'msg'=>"##WARNING! Apache params not supported. ;-)" 228 | > }, 229 | > ); 230 | > 231 | > foreach my $file (@matches) { 232 | > ##Perl5 'slurp' $file idiom 233 | > my $text = do { local( @ARGV, $RS ) = $file ; <> } ; 234 | > 235 | > if ($text) { 236 | > ##Cleanup leading/trailing whitespace 237 | > $text =~ s/^(.+?)\s*$/$1/gmix; #ltrim() 238 | > $text =~ s/^\s*(.+?)$/$1/gmix; #rtrim() 239 | > 240 | > # Use the error lookup to see if the text contains things that don't 241 | > # convert from .htaccess (like RedirectBase..) 242 | > while( my ($key, $value) = each(%error_lkp)) { 243 | > my $re = $value->{'re'}; #Each error has it's own match pattern 244 | > my $msg = $value->{'msg'}; #and error message. 245 | > 246 | > ##If the text matched the error regex, insert a warning $msg 247 | > ## and track/count it. 248 | > if (my $count = $text =~ s/^(.*?$re.*?)$/$RS##$msg$RS$1/gmix) { 249 | > $errflag{$key}+=$count; 250 | > $errcount +=$count; 251 | > } 252 | > } 253 | > 254 | > ##Add a "tab" infront of each line 255 | > $text =~ s/^(.+?)$/\t$1/gmix; 256 | > ##Add a newline, back. 257 | > $text .= $RS; 258 | > } 259 | > else { 260 | > $text = ""; ##NOTE: $text is undef until we make it an "empty string" 261 | > } 262 | > 263 | > my $dir = dirname($file); 264 | > print "$RS"; 265 | > print $text; 266 | > print "$RS"; 267 | > 268 | > } 269 | > 270 | > ## Output a commented block (Apache style) with any erros we found. 271 | > if ($errcount > 0) { 272 | > ##Convert our %errflag to a nicely Dump'd output. 273 | > my $errors = Data::Dumper->Dump( [ \%errflag ], [ qw(*ErrorTypeFound) ] ); 274 | > 275 | > ##Append '#' infront of each line of $errors. 276 | > $errors =~ s/^(.+?)$/#$1/gmix; 277 | > 278 | > print $RS; 279 | > print "######################################################$RS"; 280 | > print "#Total Warnings: $errcount $RS"; 281 | > print $errors; 282 | > print "######################################################$RS"; 283 | > print $RS; 284 | > } 285 | > 286 | > print "######################################################$RS"; 287 | > print "## Total Files processed: " . scalar @matches . $RS; 288 | > print "######################################################$RS"; 289 | > print $RS; 290 | > print "## Please test before going live, no guarantees! $RS"; 291 | --------------------------------------------------------------------------------