]> git.donarmstrong.com Git - perltidy.git/commitdiff
New upstream release (closes: #613417)
authordon <don@8f7917da-ec0b-0410-a553-b9b0e350d17e>
Mon, 14 Feb 2011 23:11:34 +0000 (23:11 +0000)
committerdon <don@8f7917da-ec0b-0410-a553-b9b0e350d17e>
Mon, 14 Feb 2011 23:11:34 +0000 (23:11 +0000)
BUGS
CHANGES
MANIFEST
META.yml
TODO
bin/perltidy
debian/changelog
docs/perltidy.1
examples/filter_example.in [new file with mode: 0644]
examples/filter_example.pl [new file with mode: 0644]
lib/Perl/Tidy.pm

diff --git a/BUGS b/BUGS
index 12a470dc88c1fd42afb7b5c12afcc48b18d9cff5..f43ea14b37a3dc0cfaa0b95b658ada20526522fd 100644 (file)
--- a/BUGS
+++ b/BUGS
@@ -15,3 +15,17 @@ Perltidy open BUGS
     Since the --extrude option is typically only used for testing perltidy,
     this type of error should not normally occur in practice.
 
     Since the --extrude option is typically only used for testing perltidy,
     this type of error should not normally occur in practice.
 
+  In some rare instances the formatting can oscillate between two states
+    The following example was sent by Denis Moskowitz, Oct 29 2010:
+
+    grep { $_->foo ne 'bar' } # asdfa asdf asdf asdf asdf asdf asdf asdf
+    asdf asdf asdf @baz;
+
+    With standard parameters this oscillates between the above and:
+
+    grep { $_->foo ne 'bar' } # asdfa asdf asdf asdf asdf asdf asdf asdf
+    asdf asdf asdf @baz;
+
+    A workaround here would be to make a slight change to the side comment
+    length.
+
diff --git a/CHANGES b/CHANGES
index b04607e79381ddf9f93520fc176fa2ed6f019540..1d8905b0488525e9b90cbebd2849c0abf88c6ece 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,53 @@
 Perltidy Change Log
 Perltidy Change Log
+  2010 12 17
+     - added new flag -it=n or --iterations=n
+       This flag causes perltidy to do n complete iterations.  
+       For most purposes the default of n=1 should be satisfactory.  However n=2
+       can be useful when a major style change is being made, or when code is being
+       beautified on check-in to a source code control system.  The run time will be
+       approximately proportional to n, and it should seldom be necessary to use a
+       value greater than n=2.  Thanks to Jonathan Swartz
+
+     - A configuration file pathname begins with three dots, e.g.
+       ".../.perltidyrc", indicates that the file should be searched for starting
+       in the current directory and working upwards. This makes it easier to have
+       multiple projects each with their own .perltidyrc in their root directories.
+       Thanks to Jonathan Swartz for this patch.
+
+     - Added flag --notidy which disables all formatting and causes the input to be
+       copied unchanged.  This can be useful in conjunction with hierarchical
+       F<.perltidyrc> files to prevent unwanted tidying.
+       Thanks to Jonathan Swartz for this patch.
+
+     - Added prefilters and postfilters in the call to the Tidy.pm module.
+       Prefilters and postfilters. The prefilter is a code reference that 
+       will be applied to the source before tidying, and the postfilter 
+       is a code reference to the result before outputting.  
+
+       Thanks to Jonathan Swartz for this patch.  He writes:
+       This is useful for all manner of customizations. For example, I use
+       it to convert the 'method' keyword to 'sub' so that perltidy will work for
+       Method::Signature::Simple code:
+
+       Perl::Tidy::perltidy(
+          prefilter => sub { $_ = $_[0]; s/^method (.*)/sub $1 \#__METHOD/gm; return $_ },
+          postfilter => sub { $_ = $_[0]; s/^sub (.*?)\s* \#__METHOD/method $1/gm; return $_ }
+       );
+
+     - The starting indentation level of sections of code entabbed with -et=n
+       is correctly guessed if it was also produced with the same -et=n flag.  This
+       keeps the indentation stable on repeated formatting passes within an editor.
+       Thanks to Sam Kington and Glenn.
+
+     - Functions with prototype '&' had a space between the function and opening
+       peren.  This space now only occurs if the flag --space-function-paren (-sfp)
+       is set.  Thanks to Zrajm Akfohg.
+
+     - Patch to never put spaces around a bare word in braces beginning with ^ as in:
+         my $before = ${^PREMATCH};
+       even if requested with the -bt=0 flag because any spaces cause a syntax error in perl.
+       Thanks to Fabrice Dulanoy.
+
   2009 06 16
      - Allow configuration file to be 'perltidy.ini' for Windows systems.
        i.e. C:\Documents and Settings\User\perltidy.ini
   2009 06 16
      - Allow configuration file to be 'perltidy.ini' for Windows systems.
        i.e. C:\Documents and Settings\User\perltidy.ini
index f3b4e31e88cf1c83a8332f9f352a704025e04227..24bb10c80d67ea90ad97fc75e5c5c195591a64a9 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -31,6 +31,8 @@ examples/perlxmltok.pl
 examples/pt.bat
 examples/testfa.t
 examples/testff.t
 examples/pt.bat
 examples/testfa.t
 examples/testff.t
+examples/filter_example.pl
+examples/filter_example.in
 lib/Perl/Tidy.pm
 pm2pl
 t/test.t
 lib/Perl/Tidy.pm
 pm2pl
 t/test.t
index b6a84607392a66fef62c13b782ca65f04ff71761..54aac2734b0218c31eaf21ce755692697f511eb5 100644 (file)
--- a/META.yml
+++ b/META.yml
@@ -1,6 +1,6 @@
 --- #YAML:1.0
 name:                Perl-Tidy
 --- #YAML:1.0
 name:                Perl-Tidy
-version:             20090616
+version:             20101217
 abstract:            indent and reformat perl scripts
 license:             ~
 author:              
 abstract:            indent and reformat perl scripts
 license:             ~
 author:              
diff --git a/TODO b/TODO
index 8b0aa0fd57eb74a414805063cb9f1fea3a1fb7d5..bb1f87437ca0513e9afcfbb5b1c5b2de608bbf2b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,27 +1,6 @@
 Perltidy TODO List
     This is a partial "wish-list" of features to add and things to do.
 
 Perltidy TODO List
     This is a partial "wish-list" of features to add and things to do.
 
-  Perltidy not idempotent
-    Perltidy does a single pass but iterates locally to try to satisfy the
-    various constraints and formatting parameters, but in some cases it can
-    take a second pass to reach a stable format, particularly when major
-    format style changes are made. It would be useful to have a flag to
-    force a second pass, say --passes=2.
-
-    For now, a workaround for a single file is
-
-      perltidy file.pl -st | perltidy -o file.tdy
-
-  The starting indentation level is not correctly guessed when tabs are used
-    When formatting a snippet which does not begin at zero indentation,
-    perltidy must guess the starting indentation. The -sil flag can be used
-    to specify this, but this is tedious. In general we cannot know the
-    indentation and can only guess, but we can at least require that if
-    perltidy is run twice on the same snippet, with the same parameters,
-    that the indentation be correctly guessed on the second pass. Currently,
-    this is only true if there are no tabs in the leading whitespace. A
-    patch has been submitted to fix this but it needs a little work still.
-
   -b does not work through the Tidy module
     The use of -b in the following does not work:
 
   -b does not work through the Tidy module
     The use of -b in the following does not work:
 
index c9c8ffc676b087b1ae2a3e6db71590d65de31abe..b39c71f66005555a9b14748d435fba79f4cfc514 100755 (executable)
@@ -305,6 +305,11 @@ name of .perltidyrc.  There must not be a space on either side of the
 would cause file F<testcfg> to be used instead of the 
 default F<.perltidyrc>.
 
 would cause file F<testcfg> to be used instead of the 
 default F<.perltidyrc>.
 
+A pathname begins with three dots, e.g. ".../.perltidyrc", indicates that
+the file should be searched for starting in the current directory and
+working upwards. This makes it easier to have multiple projects each with
+their own .perltidyrc in their root directories.
+
 =item B<-opt>,   B<--show-options>      
 
 Write a list of all options used to the F<.LOG> file.  
 =item B<-opt>,   B<--show-options>      
 
 Write a list of all options used to the F<.LOG> file.  
@@ -325,6 +330,15 @@ as non-text, and this flag forces perltidy to process them.
 
 =over 4
 
 
 =over 4
 
+=item B<--notidy>
+
+This flag disables all formatting and causes the input to be copied unchanged
+to the output except for possible changes in line ending characters and any
+pre- and post-filters.  This can be useful in conjunction with a hierarchical
+set of F<.perltidyrc> files to avoid unwanted code tidying.  See also
+L<Skipping Selected Sections of Code> for a way to avoid tidying specific
+sections of code.
+
 =item B<-l=n>, B<--maximum-line-length=n>
 
 The default maximum line length is n=80 characters.  Perltidy will try
 =item B<-l=n>, B<--maximum-line-length=n>
 
 The default maximum line length is n=80 characters.  Perltidy will try
@@ -434,6 +448,17 @@ input comes from a filename (rather than stdin, for example).  If
 perltidy has trouble determining the input file line ending, it will
 revert to the default behavior of using the line ending of the host system.
 
 perltidy has trouble determining the input file line ending, it will
 revert to the default behavior of using the line ending of the host system.
 
+=item B<-it=n>,   B<--iterations=n>
+
+This flag causes perltidy to do B<n> complete iterations.  The reason for this
+flag is that code beautification is a somewhat iterative process and in some
+cases the output from perltidy can be different if it is applied a second time.
+For most purposes the default of B<n=1> should be satisfactory.  However B<n=2>
+can be useful when a major style change is being made, or when code is being
+beautified on check-in to a source code control system.  The run time will be
+approximately proportional to B<n>, and it should seldom be necessary to use a
+value greater than B<n=2>.  This flag has no effect when perltidy is used to generate html.
+
 =back
 
 =head2 Code Indentation Control
 =back
 
 =head2 Code Indentation Control
@@ -2794,7 +2819,7 @@ perlstyle(1), Perl::Tidy(3)
 
 =head1 VERSION
 
 
 =head1 VERSION
 
-This man page documents perltidy version 20090616.
+This man page documents perltidy version 20101217.
 
 =head1 CREDITS
 
 
 =head1 CREDITS
 
@@ -2819,7 +2844,7 @@ see the CHANGES file.
 
 =head1 COPYRIGHT
 
 
 =head1 COPYRIGHT
 
-Copyright (c) 2000-2008 by Steve Hancock
+Copyright (c) 2000-2010 by Steve Hancock
 
 =head1 LICENSE
 
 
 =head1 LICENSE
 
index 54c7130aeaf69a1c3f8da439b2487c965e91df63..b10f0b145062bbf03cd4fce30f2ff0797b332418 100644 (file)
@@ -1,3 +1,9 @@
+perltidy (20101217-1) unstable; urgency=low
+
+  * New upstream release (closes: #613417)
+
+ -- Don Armstrong <don@debian.org>  Mon, 14 Feb 2011 15:10:43 -0800
+
 perltidy (20090616-1) unstable; urgency=low
 
   * New upstream release
 perltidy (20090616-1) unstable; urgency=low
 
   * New upstream release
index d7941da92f738b6a12ce07c5d7ed1d15b9266e69..0805ace41a265f77158b3541b44580a6f5eeae6a 100644 (file)
 .\" ========================================================================
 .\"
 .IX Title "PERLTIDY 1"
 .\" ========================================================================
 .\"
 .IX Title "PERLTIDY 1"
-.TH PERLTIDY 1 "2009-06-16" "perl v5.10.0" "User Contributed Perl Documentation"
+.TH PERLTIDY 1 "2010-12-13" "perl v5.10.0" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -429,6 +429,11 @@ name of .perltidyrc.  There must not be a space on either side of the
 .Sp
 would cause file \fItestcfg\fR to be used instead of the 
 default \fI.perltidyrc\fR.
 .Sp
 would cause file \fItestcfg\fR to be used instead of the 
 default \fI.perltidyrc\fR.
+.Sp
+A pathname begins with three dots, e.g. \*(L".../.perltidyrc\*(R", indicates that
+the file should be searched for starting in the current directory and
+working upwards. This makes it easier to have multiple projects each with
+their own .perltidyrc in their root directories.
 .IP "\fB\-opt\fR,   \fB\-\-show\-options\fR" 4
 .IX Item "-opt,   --show-options"
 Write a list of all options used to the \fI.LOG\fR file.  
 .IP "\fB\-opt\fR,   \fB\-\-show\-options\fR" 4
 .IX Item "-opt,   --show-options"
 Write a list of all options used to the \fI.LOG\fR file.  
@@ -443,6 +448,14 @@ as non-text, and this flag forces perltidy to process them.
 .IX Header "FORMATTING OPTIONS"
 .SS "Basic Options"
 .IX Subsection "Basic Options"
 .IX Header "FORMATTING OPTIONS"
 .SS "Basic Options"
 .IX Subsection "Basic Options"
+.IP "\fB\-\-notidy\fR" 4
+.IX Item "--notidy"
+This flag disables all formatting and causes the input to be copied unchanged
+to the output except for possible changes in line ending characters and any
+pre\- and post-filters.  This can be useful in conjunction with a hierarchical
+set of \fI.perltidyrc\fR files to avoid unwanted code tidying.  See also
+\&\*(L"Skipping Selected Sections of Code\*(R" for a way to avoid tidying specific
+sections of code.
 .IP "\fB\-l=n\fR, \fB\-\-maximum\-line\-length=n\fR" 4
 .IX Item "-l=n, --maximum-line-length=n"
 The default maximum line length is n=80 characters.  Perltidy will try
 .IP "\fB\-l=n\fR, \fB\-\-maximum\-line\-length=n\fR" 4
 .IX Item "-l=n, --maximum-line-length=n"
 The default maximum line length is n=80 characters.  Perltidy will try
@@ -542,6 +555,16 @@ endings as the input file, if possible.  It should work for
 input comes from a filename (rather than stdin, for example).  If
 perltidy has trouble determining the input file line ending, it will
 revert to the default behavior of using the line ending of the host system.
 input comes from a filename (rather than stdin, for example).  If
 perltidy has trouble determining the input file line ending, it will
 revert to the default behavior of using the line ending of the host system.
+.IP "\fB\-it=n\fR,   \fB\-\-iterations=n\fR" 4
+.IX Item "-it=n,   --iterations=n"
+This flag causes perltidy to do \fBn\fR complete iterations.  The reason for this
+flag is that code beautification is a somewhat iterative process and in some
+cases the output from perltidy can be different if it is applied a second time.
+For most purposes the default of \fBn=1\fR should be satisfactory.  However \fBn=2\fR
+can be useful when a major style change is being made, or when code is being
+beautified on check-in to a source code control system.  The run time will be
+approximately proportional to \fBn\fR, and it should seldom be necessary to use a
+value greater than \fBn=2\fR.  This flag has no effect when perltidy is used to generate html.
 .SS "Code Indentation Control"
 .IX Subsection "Code Indentation Control"
 .IP "\fB\-ci=n\fR, \fB\-\-continuation\-indentation=n\fR" 4
 .SS "Code Indentation Control"
 .IX Subsection "Code Indentation Control"
 .IP "\fB\-ci=n\fR, \fB\-\-continuation\-indentation=n\fR" 4
@@ -2879,7 +2902,7 @@ purpose of this rule is to prevent generating confusing filenames such as
 \&\fIperlstyle\fR\|(1), \fIPerl::Tidy\fR\|(3)
 .SH "VERSION"
 .IX Header "VERSION"
 \&\fIperlstyle\fR\|(1), \fIPerl::Tidy\fR\|(3)
 .SH "VERSION"
 .IX Header "VERSION"
-This man page documents perltidy version 20090616.
+This man page documents perltidy version 20101217.
 .SH "CREDITS"
 .IX Header "CREDITS"
 Michael Cartmell supplied code for adaptation to \s-1VMS\s0 and helped with
 .SH "CREDITS"
 .IX Header "CREDITS"
 Michael Cartmell supplied code for adaptation to \s-1VMS\s0 and helped with
@@ -2903,7 +2926,7 @@ see the \s-1CHANGES\s0 file.
 .Ve
 .SH "COPYRIGHT"
 .IX Header "COPYRIGHT"
 .Ve
 .SH "COPYRIGHT"
 .IX Header "COPYRIGHT"
-Copyright (c) 2000\-2008 by Steve Hancock
+Copyright (c) 2000\-2010 by Steve Hancock
 .SH "LICENSE"
 .IX Header "LICENSE"
 This package is free software; you can redistribute it and/or modify it
 .SH "LICENSE"
 .IX Header "LICENSE"
 This package is free software; you can redistribute it and/or modify it
diff --git a/examples/filter_example.in b/examples/filter_example.in
new file mode 100644 (file)
index 0000000..70172e5
--- /dev/null
@@ -0,0 +1,16 @@
+# input file for testing filter_example.pl
+use Method::Signatures::Simple;
+
+ method foo { $self->bar }
+
+       # with signature
+    method foo($bar, %opts) { $self->bar(reverse $bar) if $opts{rev};
+    }
+
+    # attributes
+    method foo : lvalue { $self->{foo} 
+}
+
+ # change invocant name
+    method 
+foo ($class: $bar) { $class->bar($bar) }
diff --git a/examples/filter_example.pl b/examples/filter_example.pl
new file mode 100644 (file)
index 0000000..7704ca4
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/perl -w
+use Perl::Tidy;
+
+# Illustrate use of prefilter and postfilter parameters to perltidy.  
+# This example program uses a prefilter it to convert the 'method'
+# keyword to 'sub', and a postfilter to convert back, so that perltidy will
+# work for Method::Signature::Simple code.  
+# NOTE: This program illustrates the use of filters but has not been
+# extensively tested.  
+
+# usage:
+#   perl filter_example.pl filter_example.in
+#
+# How it works:
+# 1. First the prefilter changes lines beginning with 'method foo' to 'sub
+# METHOD_foo'
+# 2. Then perltidy formats the code
+# 3. Then the postfilter changes 'sub METHOD_' to 'method ' everywhere.
+# (This assumes that there are no methods named METHOD_*, and that the keyword
+# method always begins a line in the input file).  
+#
+# Debugging hints: 
+# 1. Try commenting out the postfilter and running with 
+# the --notidy option to see what the prefilter alone is doing.
+# 2. Then run with both pre- and post ters with --notidy to be sure
+# that the postfilter properly undoes the prefilter.
+
+my $arg_string = undef;
+Perl::Tidy::perltidy(
+    argv => $arg_string,
+    prefilter =>
+      sub { $_ = $_[0]; s/^\s*method\s+(\w.*)/sub METHOD_$1/gm; return $_ },
+    postfilter =>
+      sub { $_ = $_[0]; s/sub\s+METHOD_/method /gm; return $_ }
+);
+__END__
+
+# Try running on the following code (file filter_example.in):
+
+use Method::Signatures::Simple;
+
+ method foo { $self->bar }
+
+       # with signature
+    method foo($bar, %opts) { $self->bar(reverse $bar) if $opts{rev};
+    }
+
+    # attributes
+    method foo : lvalue { $self->{foo} 
+}
+
+ # change invocant name
+    method 
+foo ($class: $bar) { $class->bar($bar) }
index c190f80f4246262eec5afda75cbe9ab7ef67e5bc..35cea35cb2c99f2dea3d6c3e7b6c8fc051cd885e 100644 (file)
@@ -1,3 +1,4 @@
+#
 ############################################################
 #
 #    perltidy - a perl script indenter and formatter
 ############################################################
 #
 #    perltidy - a perl script indenter and formatter
@@ -27,7 +28,7 @@
 #
 #      perltidy Tidy.pm
 #
 #
 #      perltidy Tidy.pm
 #
-#    Code Contributions:
+#    Code Contributions: See ChangeLog.html for a complete history.
 #      Michael Cartmell supplied code for adaptation to VMS and helped with
 #        v-strings.
 #      Hugh S. Myers supplied sub streamhandle and the supporting code to
 #      Michael Cartmell supplied code for adaptation to VMS and helped with
 #        v-strings.
 #      Hugh S. Myers supplied sub streamhandle and the supporting code to
 #      Sebastien Aperghis-Tramoni supplied a patch for the defined or operator.
 #      Dan Tyrell contributed a patch for binary I/O.
 #      Ueli Hugenschmidt contributed a patch for -fpsc
 #      Sebastien Aperghis-Tramoni supplied a patch for the defined or operator.
 #      Dan Tyrell contributed a patch for binary I/O.
 #      Ueli Hugenschmidt contributed a patch for -fpsc
+#      Sam Kington supplied a patch to identify the initial indentation of
+#      entabbed code.
+#      jonathan swartz supplied patches for:
+#      * .../ pattern, which looks upwards from directory
+#      * --notidy, to be used in directories where we want to avoid
+#        accidentally tidying
+#      * prefilter and postfilter
+#      * iterations option
+#
 #      Many others have supplied key ideas, suggestions, and bug reports;
 #        see the CHANGES file.
 #
 #      Many others have supplied key ideas, suggestions, and bug reports;
 #        see the CHANGES file.
 #
@@ -61,11 +71,12 @@ use vars qw{
 @ISA    = qw( Exporter );
 @EXPORT = qw( &perltidy );
 
 @ISA    = qw( Exporter );
 @EXPORT = qw( &perltidy );
 
+use Cwd;
 use IO::File;
 use File::Basename;
 
 BEGIN {
 use IO::File;
 use File::Basename;
 
 BEGIN {
-    ( $VERSION = q($Id: Tidy.pm,v 1.74 2009/06/16 13:56:49 perltidy Exp $) ) =~ s/^.*\s+(\d+)\/(\d+)\/(\d+).*$/$1$2$3/; # all one line for MakeMaker
+    ( $VERSION = q($Id: Tidy.pm,v 1.74 2010/12/17 13:56:49 perltidy Exp $) ) =~ s/^.*\s+(\d+)\/(\d+)\/(\d+).*$/$1$2$3/; # all one line for MakeMaker
 }
 
 sub streamhandle {
 }
 
 sub streamhandle {
@@ -335,6 +346,8 @@ sub make_temporary_filename {
             dump_options_category => undef,
             dump_options_range    => undef,
             dump_abbreviations    => undef,
             dump_options_category => undef,
             dump_options_range    => undef,
             dump_abbreviations    => undef,
+            prefilter             => undef,
+            postfilter            => undef,
         );
 
         # don't overwrite callers ARGV
         );
 
         # don't overwrite callers ARGV
@@ -383,6 +396,8 @@ EOM
         my $source_stream      = $input_hash{'source'};
         my $stderr_stream      = $input_hash{'stderr'};
         my $user_formatter     = $input_hash{'formatter'};
         my $source_stream      = $input_hash{'source'};
         my $stderr_stream      = $input_hash{'stderr'};
         my $user_formatter     = $input_hash{'formatter'};
+        my $prefilter          = $input_hash{'prefilter'};
+        my $postfilter         = $input_hash{'postfilter'};
 
         # various dump parameters
         my $dump_options_type     = $input_hash{'dump_options_type'};
 
         # various dump parameters
         my $dump_options_type     = $input_hash{'dump_options_type'};
@@ -769,6 +784,20 @@ EOM
                 $rpending_logfile_message );
             next unless ($source_object);
 
                 $rpending_logfile_message );
             next unless ($source_object);
 
+            # Prefilters and postfilters: The prefilter is a code reference
+            # that will be applied to the source before tidying, and the
+            # postfilter is a code reference to the result before outputting.
+            if ($prefilter) {
+                my $buf = '';
+                while ( my $line = $source_object->get_line() ) {
+                    $buf .= $line;
+                }
+                $buf = $prefilter->($buf);
+
+                $source_object = Perl::Tidy::LineSource->new( \$buf, $rOpts,
+                    $rpending_logfile_message );
+            }
+
             # register this file name with the Diagnostics package
             $diagnostics_object->set_input_file($input_file)
               if $diagnostics_object;
             # register this file name with the Diagnostics package
             $diagnostics_object->set_input_file($input_file)
               if $diagnostics_object;
@@ -863,9 +892,19 @@ EOM
             if   ( defined($line_separator) ) { $binmode        = 1 }
             else                              { $line_separator = "\n" }
 
             if   ( defined($line_separator) ) { $binmode        = 1 }
             else                              { $line_separator = "\n" }
 
-            my $sink_object =
-              Perl::Tidy::LineSink->new( $output_file, $tee_file,
-                $line_separator, $rOpts, $rpending_logfile_message, $binmode );
+            my ( $sink_object, $postfilter_buffer );
+            if ($postfilter) {
+                $sink_object =
+                  Perl::Tidy::LineSink->new( \$postfilter_buffer, $tee_file,
+                    $line_separator, $rOpts, $rpending_logfile_message,
+                    $binmode );
+            }
+            else {
+                $sink_object =
+                  Perl::Tidy::LineSink->new( $output_file, $tee_file,
+                    $line_separator, $rOpts, $rpending_logfile_message,
+                    $binmode );
+            }
 
             #---------------------------------------------------------------
             # initialize the error logger
 
             #---------------------------------------------------------------
             # initialize the error logger
@@ -898,65 +937,106 @@ EOM
                   Perl::Tidy::Debugger->new( $fileroot . $dot . "DEBUG" );
             }
 
                   Perl::Tidy::Debugger->new( $fileroot . $dot . "DEBUG" );
             }
 
-            #---------------------------------------------------------------
-            # create a formatter for this file : html writer or pretty printer
-            #---------------------------------------------------------------
+            # loop over iterations
+            my $max_iterations    = $rOpts->{'iterations'};
+            my $sink_object_final = $sink_object;
+            for ( my $iter = 1 ; $iter <= $max_iterations ; $iter++ ) {
+                my $temp_buffer;
 
 
-            # we have to delete any old formatter because, for safety,
-            # the formatter will check to see that there is only one.
-            $formatter = undef;
+                # local copies of some debugging objects which get deleted
+                # after first iteration, but will reappear after this loop
+                my $debugger_object    = $debugger_object;
+                my $logger_object      = $logger_object;
+                my $diagnostics_object = $diagnostics_object;
 
 
-            if ($user_formatter) {
-                $formatter = $user_formatter;
-            }
-            elsif ( $rOpts->{'format'} eq 'html' ) {
-                $formatter =
-                  Perl::Tidy::HtmlWriter->new( $fileroot, $output_file,
-                    $actual_output_extension, $html_toc_extension,
-                    $html_src_extension );
-            }
-            elsif ( $rOpts->{'format'} eq 'tidy' ) {
-                $formatter = Perl::Tidy::Formatter->new(
+                # output to temp buffer until last iteration
+                if ( $iter < $max_iterations ) {
+                    $sink_object =
+                      Perl::Tidy::LineSink->new( \$temp_buffer, $tee_file,
+                        $line_separator, $rOpts, $rpending_logfile_message,
+                        $binmode );
+                }
+                else {
+                    $sink_object = $sink_object_final;
+
+                    # terminate some debugging output after first pass
+                    # to avoid needless output.
+                    $debugger_object    = undef;
+                    $logger_object      = undef;
+                    $diagnostics_object = undef;
+                }
+
+              #---------------------------------------------------------------
+              # create a formatter for this file : html writer or pretty printer
+              #---------------------------------------------------------------
+
+                # we have to delete any old formatter because, for safety,
+                # the formatter will check to see that there is only one.
+                $formatter = undef;
+
+                if ($user_formatter) {
+                    $formatter = $user_formatter;
+                }
+                elsif ( $rOpts->{'format'} eq 'html' ) {
+                    $formatter =
+                      Perl::Tidy::HtmlWriter->new( $fileroot, $output_file,
+                        $actual_output_extension, $html_toc_extension,
+                        $html_src_extension );
+                }
+                elsif ( $rOpts->{'format'} eq 'tidy' ) {
+                    $formatter = Perl::Tidy::Formatter->new(
+                        logger_object      => $logger_object,
+                        diagnostics_object => $diagnostics_object,
+                        sink_object        => $sink_object,
+                    );
+                }
+                else {
+                    die "I don't know how to do -format=$rOpts->{'format'}\n";
+                }
+
+                unless ($formatter) {
+                    die
+                      "Unable to continue with $rOpts->{'format'} formatting\n";
+                }
+
+                #---------------------------------------------------------------
+                # create the tokenizer for this file
+                #---------------------------------------------------------------
+                $tokenizer = undef;    # must destroy old tokenizer
+                $tokenizer = Perl::Tidy::Tokenizer->new(
+                    source_object      => $source_object,
                     logger_object      => $logger_object,
                     logger_object      => $logger_object,
+                    debugger_object    => $debugger_object,
                     diagnostics_object => $diagnostics_object,
                     diagnostics_object => $diagnostics_object,
-                    sink_object        => $sink_object,
+                    starting_level => $rOpts->{'starting-indentation-level'},
+                    tabs           => $rOpts->{'tabs'},
+                    entab_leading_space => $rOpts->{'entab-leading-whitespace'},
+                    indent_columns      => $rOpts->{'indent-columns'},
+                    look_for_hash_bang  => $rOpts->{'look-for-hash-bang'},
+                    look_for_autoloader => $rOpts->{'look-for-autoloader'},
+                    look_for_selfloader => $rOpts->{'look-for-selfloader'},
+                    trim_qw             => $rOpts->{'trim-qw'},
                 );
                 );
-            }
-            else {
-                die "I don't know how to do -format=$rOpts->{'format'}\n";
-            }
 
 
-            unless ($formatter) {
-                die "Unable to continue with $rOpts->{'format'} formatting\n";
-            }
+                #---------------------------------------------------------------
+                # now we can do it
+                #---------------------------------------------------------------
+                process_this_file( $tokenizer, $formatter );
 
 
-            #---------------------------------------------------------------
-            # create the tokenizer for this file
-            #---------------------------------------------------------------
-            $tokenizer = undef;                     # must destroy old tokenizer
-            $tokenizer = Perl::Tidy::Tokenizer->new(
-                source_object       => $source_object,
-                logger_object       => $logger_object,
-                debugger_object     => $debugger_object,
-                diagnostics_object  => $diagnostics_object,
-                starting_level      => $rOpts->{'starting-indentation-level'},
-                tabs                => $rOpts->{'tabs'},
-                indent_columns      => $rOpts->{'indent-columns'},
-                look_for_hash_bang  => $rOpts->{'look-for-hash-bang'},
-                look_for_autoloader => $rOpts->{'look-for-autoloader'},
-                look_for_selfloader => $rOpts->{'look-for-selfloader'},
-                trim_qw             => $rOpts->{'trim-qw'},
-            );
+                #---------------------------------------------------------------
+                # close the input source and report errors
+                #---------------------------------------------------------------
+                $source_object->close_input_file();
 
 
-            #---------------------------------------------------------------
-            # now we can do it
-            #---------------------------------------------------------------
-            process_this_file( $tokenizer, $formatter );
+                # line source for next iteration (if any) comes from the current
+                # temporary buffer
+                if ( $iter < $max_iterations ) {
+                    $source_object =
+                      Perl::Tidy::LineSource->new( \$temp_buffer, $rOpts,
+                        $rpending_logfile_message );
+                }
 
 
-            #---------------------------------------------------------------
-            # close the input source and report errors
-            #---------------------------------------------------------------
-            $source_object->close_input_file();
+            }    # end loop over iterations
 
             # get file names to use for syntax check
             my $ifname = $source_object->get_input_file_copy_name();
 
             # get file names to use for syntax check
             my $ifname = $source_object->get_input_file_copy_name();
@@ -1006,6 +1086,17 @@ EOM
             $sink_object->close_output_file()    if $sink_object;
             $debugger_object->close_debug_file() if $debugger_object;
 
             $sink_object->close_output_file()    if $sink_object;
             $debugger_object->close_debug_file() if $debugger_object;
 
+            if ($postfilter) {
+                my $new_sink =
+                  Perl::Tidy::LineSink->new( $output_file, $tee_file,
+                    $line_separator, $rOpts, $rpending_logfile_message,
+                    $binmode );
+                my $buf = $postfilter->($postfilter_buffer);
+                foreach my $line ( split( "\n", $buf ) ) {
+                    $new_sink->write_line($line);
+                }
+            }
+
             my $infile_syntax_ok = 0;    # -1 no  0=don't know   1 yes
             if ($output_file) {
 
             my $infile_syntax_ok = 0;    # -1 no  0=don't know   1 yes
             if ($output_file) {
 
@@ -1178,6 +1269,7 @@ sub generate_options {
       npro
       recombine!
       valign!
       npro
       recombine!
       valign!
+      notidy
     );
 
     my $category = 13;    # Debugging
     );
 
     my $category = 13;    # Debugging
@@ -1226,6 +1318,7 @@ sub generate_options {
     $add_option->( 'backup-file-extension',      'bext',  '=s' );
     $add_option->( 'force-read-binary',          'f',     '!' );
     $add_option->( 'format',                     'fmt',   '=s' );
     $add_option->( 'backup-file-extension',      'bext',  '=s' );
     $add_option->( 'force-read-binary',          'f',     '!' );
     $add_option->( 'format',                     'fmt',   '=s' );
+    $add_option->( 'iterations',                 'it',    '=i' );
     $add_option->( 'logfile',                    'log',   '!' );
     $add_option->( 'logfile-gap',                'g',     ':i' );
     $add_option->( 'outfile',                    'o',     '=s' );
     $add_option->( 'logfile',                    'log',   '!' );
     $add_option->( 'logfile-gap',                'g',     ':i' );
     $add_option->( 'outfile',                    'o',     '=s' );
@@ -1521,6 +1614,7 @@ sub generate_options {
       hanging-side-comments
       indent-block-comments
       indent-columns=4
       hanging-side-comments
       indent-block-comments
       indent-columns=4
+      iterations=1
       keep-old-blank-lines=1
       long-block-line-count=8
       look-for-autoloader
       keep-old-blank-lines=1
       long-block-line-count=8
       look-for-autoloader
@@ -1819,6 +1913,21 @@ sub process_command_line {
 "Only one -pro=filename allowed, using '$2' instead of '$config_file'\n";
             }
             $config_file = $2;
 "Only one -pro=filename allowed, using '$2' instead of '$config_file'\n";
             }
             $config_file = $2;
+
+            # resolve <dir>/.../<file>, meaning look upwards from directory
+            if ( defined($config_file) ) {
+                if ( my ( $start_dir, $search_file ) =
+                    ( $config_file =~ m{^(.*)\.\.\./(.*)$} ) )
+                {
+                    $start_dir = '.' if !$start_dir;
+                    $start_dir = Cwd::realpath($start_dir);
+                    if ( my $found_file =
+                        find_file_upwards( $start_dir, $search_file ) )
+                    {
+                        $config_file = $found_file;
+                    }
+                }
+            }
             unless ( -e $config_file ) {
                 warn "cannot find file given with -pro=$config_file: $!\n";
                 $config_file = "";
             unless ( -e $config_file ) {
                 warn "cannot find file given with -pro=$config_file: $!\n";
                 $config_file = "";
@@ -2062,6 +2171,20 @@ sub check_options {
         }
     }
 
         }
     }
 
+    # check iteration count and quietly fix if necessary:
+    # - iterations option only applies to code beautification mode
+    # - it shouldn't be nessary to use more than about 2 iterations
+    if ( $rOpts->{'format'} ne 'tidy' ) {
+        $rOpts->{'iterations'} = 1;
+    }
+    elsif ( defined( $rOpts->{'iterations'} ) ) {
+        if    ( $rOpts->{'iterations'} <= 0 ) { $rOpts->{'iterations'} = 1 }
+        elsif ( $rOpts->{'iterations'} > 5 )  { $rOpts->{'iterations'} = 5 }
+    }
+    else {
+        $rOpts->{'iterations'} = 1;
+    }
+
     # see if user set a non-negative logfile-gap
     if ( defined( $rOpts->{'logfile-gap'} ) && $rOpts->{'logfile-gap'} >= 0 ) {
 
     # see if user set a non-negative logfile-gap
     if ( defined( $rOpts->{'logfile-gap'} ) && $rOpts->{'logfile-gap'} >= 0 ) {
 
@@ -2132,6 +2255,26 @@ EOM
     }
 }
 
     }
 }
 
+sub find_file_upwards {
+    my ( $search_dir, $search_file ) = @_;
+
+    $search_dir  =~ s{/+$}{};
+    $search_file =~ s{^/+}{};
+
+    while (1) {
+        my $try_path = "$search_dir/$search_file";
+        if ( -f $try_path ) {
+            return $try_path;
+        }
+        elsif ( $search_dir eq '/' ) {
+            return undef;
+        }
+        else {
+            $search_dir = dirname($search_dir);
+        }
+    }
+}
+
 sub expand_command_abbreviations {
 
     # go through @ARGV and expand any abbreviations
 sub expand_command_abbreviations {
 
     # go through @ARGV and expand any abbreviations
@@ -2816,7 +2959,7 @@ sub show_version {
     print <<"EOM";
 This is perltidy, v$VERSION 
 
     print <<"EOM";
 This is perltidy, v$VERSION 
 
-Copyright 2000-2009, Steve Hancock
+Copyright 2000-2010, Steve Hancock
 
 Perltidy is free software and may be copied under the terms of the GNU
 General Public License, which is included in the distribution files.
 
 Perltidy is free software and may be copied under the terms of the GNU
 General Public License, which is included in the distribution files.
@@ -6011,6 +6154,12 @@ sub write_line {
     my $line_type  = $line_of_tokens->{_line_type};
     my $input_line = $line_of_tokens->{_line_text};
 
     my $line_type  = $line_of_tokens->{_line_type};
     my $input_line = $line_of_tokens->{_line_text};
 
+    if ( $rOpts->{notidy} ) {
+        write_unindented_line($input_line);
+        $last_line_type = $line_type;
+        return;
+    }
+
     # _line_type codes are:
     #   SYSTEM         - system-specific code before hash-bang line
     #   CODE           - line of perl code (including comments)
     # _line_type codes are:
     #   SYSTEM         - system-specific code before hash-bang line
     #   CODE           - line of perl code (including comments)
@@ -7844,6 +7993,21 @@ sub set_white_space_flag {
                 }
                 else { $tightness = $tightness{$last_token} }
 
                 }
                 else { $tightness = $tightness{$last_token} }
 
+    #=================================================================
+    # Patch for fabrice_bug.pl
+    # We must always avoid spaces around a bare word beginning with ^ as in:
+    #    my $before = ${^PREMATCH};
+    # Because all of the following cause an error in perl:
+    #    my $before = ${ ^PREMATCH };
+    #    my $before = ${ ^PREMATCH};
+    #    my $before = ${^PREMATCH };
+    # So if brace tightness flag is -bt=0 we must temporarily reset to bt=1.
+    # Note that here we must set tightness=1 and not 2 so that the closing space
+    # is also avoided (via the $j_tight_closing_paren flag in coding)
+                if ( $type eq 'w' && $token =~ /^\^/ ) { $tightness = 1 }
+
+              #=================================================================
+
                 if ( $tightness <= 0 ) {
                     $ws = WS_YES;
                 }
                 if ( $tightness <= 0 ) {
                     $ws = WS_YES;
                 }
@@ -7967,7 +8131,7 @@ sub set_white_space_flag {
             # 'w' and 'i' checks for something like:
             #   myfun(    &myfun(   ->myfun(
             # -----------------------------------------------------
             # 'w' and 'i' checks for something like:
             #   myfun(    &myfun(   ->myfun(
             # -----------------------------------------------------
-            elsif (( $last_type =~ /^[wU]$/ )
+            elsif (( $last_type =~ /^[wUG]$/ )
                 || ( $last_type =~ /^[wi]$/ && $last_token =~ /^(\&|->)/ ) )
             {
                 $ws = WS_NO unless ($rOpts_space_function_paren);
                 || ( $last_type =~ /^[wi]$/ && $last_token =~ /^(\&|->)/ ) )
             {
                 $ws = WS_NO unless ($rOpts_space_function_paren);
@@ -8921,10 +9085,12 @@ sub set_white_space_flag {
                         # that this is a block and not an anonomyous
                         # hash (blktype.t, blktype1.t)
                         && ( $block_type !~ /^[\{\};]$/ )
                         # that this is a block and not an anonomyous
                         # hash (blktype.t, blktype1.t)
                         && ( $block_type !~ /^[\{\};]$/ )
-                        
+
                         # patch: and do not add semi-colons for recently
                         # added block types (see tmp/semicolon.t)
                         # patch: and do not add semi-colons for recently
                         # added block types (see tmp/semicolon.t)
-                        && ( $block_type !~ /^(switch|case|given|when|default)$/)
+                        && ( $block_type !~
+                            /^(switch|case|given|when|default)$/ )
+
                         # it seems best not to add semicolons in these
                         # special block types: sort|map|grep
                         && ( !$is_sort_map_grep{$block_type} )
                         # it seems best not to add semicolons in these
                         # special block types: sort|map|grep
                         && ( !$is_sort_map_grep{$block_type} )
@@ -20885,6 +21051,7 @@ sub new {
         starting_level       => undef,
         indent_columns       => 4,
         tabs                 => 0,
         starting_level       => undef,
         indent_columns       => 4,
         tabs                 => 0,
+        entab_leading_space  => undef,
         look_for_hash_bang   => 0,
         trim_qw              => 1,
         look_for_autoloader  => 1,
         look_for_hash_bang   => 0,
         trim_qw              => 1,
         look_for_autoloader  => 1,
@@ -20938,6 +21105,7 @@ sub new {
         _starting_level                     => $args{starting_level},
         _know_starting_level                => defined( $args{starting_level} ),
         _tabs                               => $args{tabs},
         _starting_level                     => $args{starting_level},
         _know_starting_level                => defined( $args{starting_level} ),
         _tabs                               => $args{tabs},
+        _entab_leading_space                => $args{entab_leading_space},
         _indent_columns                     => $args{indent_columns},
         _look_for_hash_bang                 => $args{look_for_hash_bang},
         _trim_qw                            => $args{trim_qw},
         _indent_columns                     => $args{indent_columns},
         _look_for_hash_bang                 => $args{look_for_hash_bang},
         _trim_qw                            => $args{trim_qw},
@@ -21622,6 +21790,7 @@ sub find_starting_indentation_level {
         my $i                            = 0;
         my $structural_indentation_level = -1; # flag for find_indentation_level
 
         my $i                            = 0;
         my $structural_indentation_level = -1; # flag for find_indentation_level
 
+        # keep looking at lines until we find a hash bang or piece of code
         my $msg = "";
         while ( $line =
             $tokenizer_self->{_line_buffer_object}->peek_ahead( $i++ ) )
         my $msg = "";
         while ( $line =
             $tokenizer_self->{_line_buffer_object}->peek_ahead( $i++ ) )
@@ -21632,8 +21801,8 @@ sub find_starting_indentation_level {
                 $starting_level = 0;
                 last;
             }
                 $starting_level = 0;
                 last;
             }
-            next if ( $line =~ /^\s*#/ );      # must not be comment
-            next if ( $line =~ /^\s*$/ );      # must not be blank
+            next if ( $line =~ /^\s*#/ );    # skip past comments
+            next if ( $line =~ /^\s*$/ );    # skip past blank lines
             ( $starting_level, $msg ) =
               find_indentation_level( $line, $structural_indentation_level );
             if ($msg) { write_logfile_entry("$msg") }
             ( $starting_level, $msg ) =
               find_indentation_level( $line, $structural_indentation_level );
             if ($msg) { write_logfile_entry("$msg") }
@@ -21689,7 +21858,17 @@ sub find_indentation_level {
 
         $know_input_tabstr = 0;
 
 
         $know_input_tabstr = 0;
 
-        if ( $tokenizer_self->{_tabs} ) {
+        # When -et=n is used for the output formatting, we will assume that
+        # tabs in the input formatting were also produced with -et=n.  This may
+        # not be true, but it is the best guess because it will keep leading
+        # whitespace unchanged on repeated formatting on small pieces of code
+        # when -et=n is used.  Thanks to Sam Kington for this patch.
+        if ( my $tabsize = $tokenizer_self->{_entab_leading_space} ) {
+            $leading_whitespace =~ s{^ (\t*) }
+           { " " x (length($1) * $tabsize) }xe;
+            $input_tabstr = " " x $tokenizer_self->{_indent_columns};
+        }
+        elsif ( $tokenizer_self->{_tabs} ) {
             $input_tabstr = "\t";
             if ( length($leading_whitespace) > 0 ) {
                 if ( $leading_whitespace !~ /\t/ ) {
             $input_tabstr = "\t";
             if ( length($leading_whitespace) > 0 ) {
                 if ( $leading_whitespace !~ /\t/ ) {
@@ -28245,6 +28424,8 @@ Perl::Tidy - Parses and beautifies perl source
         formatter         => $formatter,           # callback object (see below)
         dump_options      => $dump_options,
         dump_options_type => $dump_options_type,
         formatter         => $formatter,           # callback object (see below)
         dump_options      => $dump_options,
         dump_options_type => $dump_options_type,
+        prefilter         => $prefilter_coderef,
+        postfilter        => $postfilter_coderef,
     );
 
 =head1 DESCRIPTION
     );
 
 =head1 DESCRIPTION
@@ -28362,6 +28543,23 @@ If the B<dump_abbreviations> parameter is given, it must be the reference to a
 hash.  This hash will receive all abbreviations used by Perl::Tidy.  See the
 demo program F<perltidyrc_dump.pl> for example usage.
 
 hash.  This hash will receive all abbreviations used by Perl::Tidy.  See the
 demo program F<perltidyrc_dump.pl> for example usage.
 
+=item prefilter
+
+A code reference that will be applied to the source before tidying. It is
+expected to take the full content as a string in its input, and output the
+transformed content.
+
+=item postfilter
+
+A code reference that will be applied to the tidied result before outputting.
+It is expected to take the full content as a string in its input, and output
+the transformed content.
+
+Note: A convenient way to check the function of your custom prefilter and
+postfilter code is to use the --notidy option, first with just the prefilter
+and then with both the prefilter and postfilter.  See also the file
+B<filter_example.pl> in the perltidy distribution.
+
 =back
 
 =head1 EXAMPLE
 =back
 
 =head1 EXAMPLE
@@ -28537,7 +28735,7 @@ to perltidy.
 
 =head1 VERSION
 
 
 =head1 VERSION
 
-This man page documents Perl::Tidy version 20090616.
+This man page documents Perl::Tidy version 20101217.
 
 =head1 AUTHOR
 
 
 =head1 AUTHOR