File Coverage

blib/lib/Log/Report/Lexicon/POTcompact.pm
Criterion Covered Total %
statement 74 79 93.6
branch 39 50 78.0
condition 11 17 64.7
subroutine 15 16 93.7
pod 5 6 83.3
total 144 168 85.7


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution Log-Report-Lexicon version 1.15.
2             # The POD got stripped from this file by OODoc version 3.05.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2007-2025 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11             #oodist: *** DO NOT USE THIS VERSION FOR PRODUCTION ***
12             #oodist: This file contains OODoc-style documentation which will get stripped
13             #oodist: during its release in the distribution. You can use this file for
14             #oodist: testing, however the code of this development version may be broken!
15              
16             package Log::Report::Lexicon::POTcompact;{
17             our $VERSION = '1.15';
18             }
19              
20 6     6   383288 use base 'Log::Report::Lexicon::Table';
  6         12  
  6         2562  
21              
22 6     6   43 use warnings;
  6         12  
  6         303  
23 6     6   50 use strict;
  6         30  
  6         170  
24              
25 6     6   30 use Log::Report 'log-report-lexicon';
  6         9  
  6         28  
26 6     6   1752 use Log::Report::Util qw/escape_chars unescape_chars/;
  6         13  
  6         394  
27              
28 6     6   49 use Encode qw/find_encoding/;
  6         20  
  6         11413  
29              
30             sub _unescape($$);
31             sub _escape($$);
32              
33             #--------------------
34              
35             sub read($@)
36 12     12 1 818 { my ($class, $fn, %args) = @_;
37 12         38 my $charset = $args{charset};
38              
39 12         23 my $self = bless +{}, $class;
40              
41             # Try to pick-up charset from the filename (which may contain a modifier)
42 12 50 66     47 $charset = $1
43             if !$charset && $fn =~ m!\.([\w-]+)(?:\@[^/\\]+)?\.po$!i;
44              
45 12         27 my $fh;
46 12 100       35 if($charset)
47 1 50   2   45 { open $fh, "<:encoding($charset):crlf", $fn
  2         1612  
  2         36  
  2         46  
48             or fault __x"cannot read in {charset} from file {fn}", charset => $charset, fn => $fn;
49             }
50             else
51 11 50       606 { open $fh, '<:raw:crlf', $fn
52             or fault __x"cannot read from file {fn} (unknown charset)", fn=>$fn;
53             }
54              
55             # Speed!
56 12         1580 my $msgctxt = '';
57 12         16 my ($last, $msgid, @msgstr);
58 12   50     81 my $index = $self->{index} ||= {};
59              
60             my $add = sub {
61 225 100   225   277 unless($charset)
62 11 50       20 { $msgid eq ''
63             or error __x"header not found for charset in {fn}", fn => $fn;
64              
65 11 50       96 $charset = $msgstr[0] =~ m/^content-type:.*?charset=["']?([\w-]+)/mi ? $1
66             : error __x"cannot detect charset in {fn}", fn => $fn;
67              
68 11 50       35 my $enc = find_encoding($charset)
69             or error __x"unsupported charset {charset} in {fn}", charset => $charset, fn => $fn;
70              
71 11         5217 trace "auto-detected charset $charset for $fn";
72 11         390 binmode $fh, ":encoding($charset):crlf";
73              
74 11         1146 $_ = $enc->decode($_) for @msgstr, $msgctxt;
75             }
76              
77 225 100       501 $index->{"$msgid#$msgctxt"} = @msgstr > 1 ? [@msgstr] : $msgstr[0];
78 225         251 ($msgctxt, $msgid, @msgstr) = ('');
79 12         62 };
80              
81             LINE:
82 12         822 while(my $line = $fh->getline)
83 1103 100       31049 { next if substr($line, 0, 1) eq '#';
84              
85 801 100       1261 if($line =~ m/^\s*$/) # blank line starts new
86 232 100       387 { $add->() if @msgstr;
87 232         1114 next LINE;
88             }
89              
90 569 50 66     1925 if($line =~ s/^msgctxt\s+//)
    100          
    100          
    100          
    100          
91 0         0 { $msgctxt = _unescape $line, $fn;
92 0         0 $last = \$msgctxt;
93             }
94             elsif($line =~ s/^msgid\s+//)
95 225         270 { $msgid = _unescape $line, $fn;
96 225         1529 $last = \$msgid;
97             }
98             elsif($line =~ s/^msgstr\[(\d+)\]\s*//)
99 4         7 { $last = \($msgstr[$1] = _unescape $line, $fn);
100             }
101             elsif($line =~ s/^msgstr\s+//)
102 224         254 { $msgstr[0] = _unescape $line, $fn;
103 224         1479 $last = \$msgstr[0];
104             }
105             elsif($last && $line =~ m/^\s*\"/)
106 115         146 { $$last .= _unescape $line, $fn;
107             }
108             }
109 12 100       31 $add->() if @msgstr; # don't forget the last
110              
111 12 50       159 close $fh
112             or failure __x"failed reading from file {fn}", fn => $fn;
113              
114 12         40 $self->{origcharset} = $charset;
115 12         20 $self->{filename} = $fn;
116 12         63 $self->setupPluralAlgorithm;
117 12         205 $self;
118             }
119              
120             #--------------------
121              
122 22     22 1 3921 sub filename() { $_[0]->{filename} }
123 11     11 1 38 sub originalCharset() { $_[0]->{origcharset} }
124              
125             #--------------------
126              
127 0     0 0 0 sub index() { $_[0]->{index} }
128             # The index is a HASH with "$msg#$msgctxt" keys. If there is no
129             # $msgctxt, then there still is the #
130              
131              
132 26   50 26 1 168 sub msgid($) { $_[0]->{index}{$_[1].'#'.($_[2]//'')} }
133              
134              
135              
136             # speed!!!
137             sub msgstr($;$$)
138 13     13 1 1684 { my ($self, $msgid, $count, $ctxt) = @_;
139              
140 13   50     77 $ctxt //= '';
141 13 50       85 my $po = $self->{index}{"$msgid#$ctxt"}
142             or return undef;
143              
144 13 100       54 ref $po # no plurals defined
145             or return $po;
146              
147 9 50 50     268 $po->[$self->{algo}->($count // 1)] || $po->[$self->{algo}->(1)];
148             }
149              
150             #
151             ### internal helper routines, shared with ::PO.pm and ::POT.pm
152             #
153              
154             sub _unescape($$)
155 686 50   686   1673 { unless( $_[0] =~ m/^\s*\"(.*)\"\s*$/ )
156 0         0 { warning __x"string '{text}' not between quotes at {location}", text => $_[0], location => $_[1];
157 0         0 return $_[0];
158             }
159 686         959 unescape_chars $1;
160             }
161              
162             sub _escape($$)
163 64 100 100 64   575 { my @escaped = map { '"' . escape_chars($_) . '"' }
  100         720  
164             defined $_[0] && length $_[0] ? split(/(?<=\n)/, $_[0]) : '';
165              
166 64 100       691 unshift @escaped, '""' if @escaped > 1;
167 64         281 join $_[1], @escaped;
168             }
169              
170             1;