File Coverage

blib/lib/App/optex/xform.pm
Criterion Covered Total %
statement 32 69 46.3
branch 0 20 0.0
condition 0 4 0.0
subroutine 11 14 78.5
pod 0 3 0.0
total 43 110 39.0


line stmt bran cond sub pod time code
1             package App::optex::xform;
2              
3             our $VERSION = "1.05";
4              
5             =encoding utf-8
6              
7             =head1 NAME
8              
9             xform - data transform filter module for optex
10              
11             =head1 SYNOPSIS
12              
13             optex -Mxform
14              
15             =head1 DESCRIPTION
16              
17             B is a filter module for B command which transform STDIN
18             into different form to make it convenient to manipulate, and recover
19             to the original form after the process.
20              
21             Transformed data have to be appear in exactly same order as original
22             data.
23              
24             =head1 OPTION
25              
26             =over 7
27              
28             =item B<--xform-ansi>
29              
30             Transform ANSI terminal sequence into printable string, and recover.
31              
32             =item B<--xform-utf8>
33              
34             Transform multibyte Non-ASCII chracters into single-byte sequene, and
35             recover.
36              
37             =item B<--xform-bin>
38              
39             Transform non-printable binary characters into printable string, and
40             recover.
41              
42             =item B<--xform-visible>=I<0|1|2>
43              
44             Specify the character set used for transformation. This option overrides
45             the default C parameter of C.
46              
47             =over 4
48              
49             =item B<0>
50              
51             Use both printable and non-printable characters.
52              
53             =item B<1>
54              
55             Use printable characters first, then non-printable characters if needed.
56              
57             =item B<2>
58              
59             Use only printable characters (default).
60              
61             =back
62              
63             This option can be combined with any xform mode (ansi, utf8, bin, generic).
64              
65             =back
66              
67             =head1 EXAMPLE
68              
69             $ jot 100 | egrep --color=always .+ | optex column -Mxform --xform-ansi -x
70              
71             Use C<--xform-visible> to control character set used for transformation:
72              
73             $ optex -Mxform --xform-visible=2 --xform-ansi cat colored.txt
74              
75             $ optex -Mxform --xform-visible=1 --xform-utf8 command
76              
77             =head1 SEE ALSO
78              
79             L, L,
80              
81             L, L,
82             L
83              
84             L
85              
86             =head1 AUTHOR
87              
88             Kazumasa Utashiro
89              
90             =head1 LICENSE
91              
92             Copyright 2020-2025 Kazumasa Utashiro.
93              
94             This library is free software; you can redistribute it and/or modify
95             it under the same terms as Perl itself.
96              
97             =cut
98              
99 1     1   419460 use v5.14;
  1         4  
100 1     1   9 use warnings;
  1         2  
  1         108  
101 1     1   28 use Carp;
  1         2  
  1         145  
102 1     1   777 use utf8;
  1         340  
  1         6  
103 1     1   711 use open IO => 'utf8', ':std';
  1         2166  
  1         8  
104 1     1   814 use Data::Dumper;
  1         11000  
  1         100  
105 1     1   813 use Hash::Util qw(lock_keys);
  1         4825  
  1         8  
106              
107 1     1   877 use Text::Conceal;
  1         94659  
  1         64  
108 1     1   10 use Text::VisualWidth::PP qw(vwidth);
  1         3  
  1         76  
109 1     1   729 use Text::ANSI::Fold::Util qw(ansi_width);
  1         1203  
  1         1060  
110              
111             my %concealer;
112              
113             my %option = (
114             visible => undef,
115             );
116             lock_keys(%option);
117              
118             my %param = (
119             ansi => {
120             length => \&ansi_width,
121             match => qr/\e\[.*?(?:\e\[0*m)+(?:\e\[0*K)*/,
122             visible => 2,
123             },
124             utf8 => {
125             length => \&vwidth,
126             match => qr/\P{ASCII}+/,
127             visible => 2,
128             },
129             binary => {
130             length => sub { length $_[0] },
131             match => qr/[^\x0a\x20-\x7e]+/a,
132             visible => 2,
133             binmode => ':raw',
134             },
135             generic => {
136             length => sub { length $_[0] },
137             match => qr/.+/,
138             visible => 2,
139             },
140             );
141              
142             sub encode {
143 0     0 0   my %arg = @_;
144 0           my $mode = $arg{mode};
145 0 0         my $param = { %{$param{$mode}} } or die "$mode: unkown mode\n";
  0            
146 0           my $binmode = delete $param->{binmode};
147             # Override parameters with user-specified options
148 0           for my $key (grep { defined $option{$_} } keys %option) {
  0            
149 0           $param->{$key} = $option{$key};
150             }
151 0           my $conceal = Text::Conceal->new(%$param);
152 0 0         $concealer{$mode} and die "$mode: encoding repeated\n";
153 0 0         if ($binmode) {
154 0 0         binmode STDIN, $binmode or die "$binmode: $!";
155             }
156 0           local $_ = do { local $/; <> };
  0            
  0            
157 0   0       $_ // die $!;
158 0 0         if ($conceal) {
159 0           $conceal->encode($_);
160 0           $concealer{$mode} = $conceal;
161             }
162 0           return $_;
163             }
164              
165             sub decode {
166 0     0 0   my %arg = @_;
167 0           my $mode = $arg{mode};
168 0 0         $param{$mode} or die "$mode: unkown mode\n";
169 0 0         if (my $binmode = $param{binmode}) {
170 0           binmode STDIN, $binmode;
171             }
172 0   0       local $_ = do { local $/; <> } // return;
  0            
  0            
173 0 0         if (my $conceal = $concealer{$mode}) {
174 0           $conceal->decode($_);
175             } else {
176 0           die "$mode: not encoded\n";
177             }
178 1     1   10 use Encode ();
  1         2  
  1         272  
179 0 0         $_ = Encode::decode('utf8', $_) if not utf8::is_utf8($_);
180 0           print $_;
181             }
182              
183             sub set {
184 0     0 0   while (my($k, $v) = splice(@_, 0, 2)) {
185 0 0         exists $option{$k} or next;
186 0           $option{$k} = $v;
187             }
188 0           ();
189             }
190              
191             1;
192              
193             __DATA__