#!/usr/local/bin/perl -w eval 'exec /usr/local/bin/perl -w -S $0 ${1+"$@"}' if 0; # not running under some shell # # A Perl implementation of expand(1) and unexpand(1) for the Perl Power # Tools project by Thierry Bezecourt . # # I don't use Text::Tabs, because : # - it doesn't handle tags which are set at specified places on the line # - it doesn't recognize backspace characters # # Please see the pod documentation at the end of this file. # # 99/03/07 : first version # use strict; my $tabstop = 8; my @tabstops; my @files; sub usage($;$) { print <<"EOF"; Usage: expand [-h] [-tabstop] [-tab1, tab2, ...] [file ...] EOF print STDERR $_[1] if $_[1]; exit $_[0]; } usage(1) unless scalar @ARGV > 0; usage(0) if grep /^-h$/, @ARGV; # at most one argument if($ARGV[0] =~ /-(.*)/) { @tabstops = split(/,/, $1); usage(1) if grep /\D/, @tabstops; # only integer arguments are allowed shift @ARGV; } @files = @ARGV; my %line_desc; # $tabstop is used only if multiple tab stops have not been defined if(scalar @tabstops == 0) { $tabstop = 8; } elsif(scalar @tabstops == 1) { $tabstop = $tabstops[0]; } else { my $howfar = 1; my %tabs; map { $tabs{$_} = 1 } @tabstops; for (my $i = $tabstops[$#tabstops]-1; $i >= 0; $i--) { # how far is the $i-th column from the next tab $line_desc{$i} = $howfar; $howfar = 0 if defined $tabs{$i}; $howfar++; } } sub do_expand(@) { my $incr; for my $line(@_) { my $curs = 0; for my $c (split //, $line) { if($c eq "\010") { # backspace $curs -= 2; $curs = -1 unless $curs >= 0; } if($c eq "\t") { if(scalar @tabstops > 0) { if(defined($line_desc{$curs}) ){ $incr = $line_desc{$curs}; } else { # Jupiter, and beyond the infinite $incr = 1; } } else { $incr = $curs%$tabstop ? ($tabstop - $curs%$tabstop) : $tabstop; } print " " x $incr; $curs += $incr; } else { print $c; $curs++; } } } } for my $file (@files) { open IN, $file or usage(1, "couldn't open '$file' for reading: $!'"); do_expand ; close IN; } __END__ =head1 NAME expand, unexpand - expand tabs to spaces, and vice versa =head1 SYNOPSIS expand [B<-h>] [B<-tabstop>] [B<-tab1, tab2, ...>] [B ...] unexpand [B<-h>] [B<-a>] [B<-tabstop>] [B<-tab1, tab2, ...>] [B ...] =head1 DESCRIPTION I processes the named files or the standard input writing the standard output with tabs changed into blanks. Backspace characters are preserved into the output and decrement the column count for tab calculations. I is useful for pre-processing character files (before sorting, looking at specific columns, etc.) that contain tabs. If a single B argument is given, tabs are set B spaces apart instead of the default 8. If multiple tabstops are given then the tabs are set at those specific columns. I puts tabs back into the data from the standard input or the named files and writes the result on the standard output. Option (with I and I): =over 4 =item -h Print a usage message and exit with a status code indicating success. =back Option (with I only): =over 4 =item -a By default, only leading blanks and tabs are reconverted to maximal strings of tabs. If the B<-a> option is given, tabs are inserted whenever they would compress the resultant file by replacing two or more characters. =back =head1 AUTHOR =for html The Perl implementation was written by Thierry Bézecourt for the Perl Power Tools project, March 1999. =for html This documentation comes from the BSD expand(1) man page. =head1 COPYRIGHT and LICENSE This program is free and open software. You may use, modify, distribute, and sell this program (and any modified variants) in any way you wish, provided you do not restrict others from doing the same. =cut