1use strict; use warnings;
2
3package Text::Tabs;
4
5BEGIN { require Exporter; *import = \&Exporter::import }
6
7our @EXPORT = qw( expand unexpand $tabstop );
8
9our $VERSION = '2021.0814';
10our $SUBVERSION = 'modern'; # back-compat vestige
11
12our $tabstop = 8;
13
14sub expand {
15	my @l;
16	my $pad;
17	for ( @_ ) {
18		defined or do { push @l, ''; next };
19		my $s = '';
20		for (split(/^/m, $_, -1)) {
21			my $offs;
22			for (split(/\t/, $_, -1)) {
23				if (defined $offs) {
24					$pad = $tabstop - $offs % $tabstop;
25					$s .= " " x $pad;
26				}
27				$s .= $_;
28				$offs = () = /\PM/g;
29			}
30		}
31		push(@l, $s);
32	}
33	return @l if wantarray;
34	return $l[0];
35}
36
37sub unexpand
38{
39	my (@l) = @_;
40	my @e;
41	my $x;
42	my $line;
43	my @lines;
44	my $lastbit;
45	my $ts_as_space = " " x $tabstop;
46	for $x (@l) {
47		defined $x or next;
48		@lines = split("\n", $x, -1);
49		for $line (@lines) {
50			$line = expand($line);
51			@e = split(/((?:\PM\pM*){$tabstop})/,$line,-1);
52			$lastbit = pop(@e);
53			$lastbit = ''
54				unless defined $lastbit;
55			$lastbit = "\t"
56				if $lastbit eq $ts_as_space;
57			for $_ (@e) {
58				s/  +$/\t/;
59			}
60			$line = join('',@e, $lastbit);
61		}
62		$x = join("\n", @lines);
63	}
64	return @l if wantarray;
65	return $l[0];
66}
67
681;
69
70__END__
71
72=head1 NAME
73
74Text::Tabs - expand and unexpand tabs like unix expand(1) and unexpand(1)
75
76=head1 SYNOPSIS
77
78  use Text::Tabs;
79
80  $tabstop = 4;  # default = 8
81  @lines_without_tabs = expand(@lines_with_tabs);
82  @lines_with_tabs = unexpand(@lines_without_tabs);
83
84=head1 DESCRIPTION
85
86Text::Tabs does most of what the unix utilities expand(1) and unexpand(1)
87do.  Given a line with tabs in it, C<expand> replaces those tabs with
88the appropriate number of spaces.  Given a line with or without tabs in
89it, C<unexpand> adds tabs when it can save bytes by doing so,
90like the C<unexpand -a> command.
91
92Unlike the old unix utilities, this module correctly accounts for
93any Unicode combining characters (such as diacriticals) that may occur
94in each line for both expansion and unexpansion.  These are overstrike
95characters that do not increment the logical position.  Make sure
96you have the appropriate Unicode settings enabled.
97
98=head1 EXPORTS
99
100The following are exported:
101
102=over 4
103
104=item expand
105
106=item unexpand
107
108=item $tabstop
109
110The C<$tabstop> variable controls how many column positions apart each
111tabstop is.  The default is 8.
112
113Please note that C<local($tabstop)> doesn't do the right thing and if you want
114to use C<local> to override C<$tabstop>, you need to use
115C<local($Text::Tabs::tabstop)>.
116
117=back
118
119=head1 EXAMPLE
120
121  #!perl
122  # unexpand -a
123  use Text::Tabs;
124
125  while (<>) {
126    print unexpand $_;
127  }
128
129Instead of the shell's C<expand> command, use:
130
131  perl -MText::Tabs -n -e 'print expand $_'
132
133Instead of the shell's C<unexpand -a> command, use:
134
135  perl -MText::Tabs -n -e 'print unexpand $_'
136
137=head1 BUGS
138
139Text::Tabs handles only tabs (C<"\t">) and combining characters (C</\pM/>).  It doesn't
140count backwards for backspaces (C<"\t">), omit other non-printing control characters (C</\pC/>),
141or otherwise deal with any other zero-, half-, and full-width characters.
142
143=head1 LICENSE
144
145Copyright (C) 1996-2002,2005,2006 David Muir Sharnoff.
146Copyright (C) 2005 Aristotle Pagaltzis
147Copyright (C) 2012-2013 Google, Inc.
148This module may be modified, used, copied, and redistributed at your own risk.
149Although allowed by the preceding license, please do not publicly
150redistribute modified versions of this code with the name "Text::Tabs"
151unless it passes the unmodified Text::Tabs test suite.
152