JFIF   ( %!1!%)+...383,7(-.+  ++-+++++-++-++--+--+-+-------+-++-+--+---+++--+7+-+"F!1AQaq"2BRb#3Sr$CDsT&!Q1Aa"2Rbq ?򉄘ǷLR HR,nNb .&W)fJbMOYxj-\bT2(4CQ"qiC/ " %0Jl"e2V  0SDd2@TV^{cW&F͉x9#l,.XɳvRZ C8S 6ml!@!E! `FS!M #(d)Q lml1ml Ų&x(ʨ2NFmj@D<dN5UN˄uTB emLAy#` ` ` I!I 6āHBxL & J#7BQ.$hv h q+tC"EJ) 8R e2U2Y@j%6PF^4LnNBp"8)4JI-ֲvK ^؊)hz[T5˗",Rҥf8ڤS4ʘ!`D ` X+ L,(hl)*S##`6[`0*L T H*HA@I&&r1kr*r*)N$#L  1#ZFSl `[( ("((he`4 Ch [="A R / 0I`twCDcWh"i) cLad\BcLKHZ"ZEW$Ƚ@A~i^`S *A&h:+c Y6vϕGClRPs.`H`(@<$qDe pL@DpLX, E2MP A  `II m& AQ "AT rbg# g2!SiLj*3L \ G;TFL`K BMy 2S`YLh1 d >-"ZfD^Q DH" RAbEV#Lfq,(rETp64-IJ!*p4F$q;G8DQ/TKP2$jp3KW]FtLtƉ1ol]VBgػJH6 )h61GJR7Nj.Z4piJRDd]t]0dP]:N.b'⹙SvDSz]L,_#ugT&[~?cS^"{Bh{/=ۑxOk̳O59o dar793`)SeYM@\ "$E(Tm&)N2Ih)F5EDed(FS,Pa @!@#@lea HCD$11jCLJqcod S3yd*,lL+QEfsgW1nw)cT#dS HXkFJB"6(ʝH)H"#EZh:Y`khݳh%Sc<mlAko2]gDqQtro=3OƸU9_-t8UvW3sGəg*#:c)><"wc\ASmT|6Ę>9~#1Ƈ~ڒE1vVi# I MM#u$8W 5ǍfƬΜg*Qpi1ȩFOf۔S,/⎯(Lrմ`(Z LsbA \6 6dm[I=!r:REI.wgzG)ԇSbӑxuׇTyyL^e'x^ty4Z&eB]I|v59Jjhm;Ng񷫳n<ϞҼѝjk;׹DlY^ҍ\+x9V!j([cmS.NO6jxNζrm&oײizT$N>?~ Sl-:iڥk\at#E!CL`.O0a*w/WV7/r)DŽt7'Nĵ#7O1 ]{[/-2bA<$&Gm_4t)_>)mjG;V^'k59o>ɌM,ؾf9z6 4v_3T.5V/RD-5 %T5XTޫ4TaZ`U *ƱUƲ UG"5+sJJ2E9#܎kr2G3Bb,XM6H: ?@p!'\4V02aԙ) hbZ]:` ev3ʘ'}!ohȒ*TJjr[RFyQ*#{h{R]J]Lr-.D-.җfo$D ?X0%~1P.Og{cWϫ22&Ϭ_V.W3nmiOl}+!˫#`kR33aUb0-g:qmsέ+0HO|&nhOn+}n5QF_"gvLm/z'+r'n_oC语i|1}Gi|}_D~9JZ_%DVQp\koۅjAs~/c0ksUJi^W9W5!>?O:q|ˣSIB/&K<(lg(%Wg$|LW7vߤW߇q|jef3D H\S6(eJb*@&sTKTW/*@v:.N- @ITʓ1Zg&-eꓝM r]EMס{q$b]'7Z7N:O~lNlP7iͲk)$O^퉢<YSD*hr'Z#5e6t[Fdh AJǔP9P 1\R).Il+jI*,(ܢ22N*OwKFX gc?\mB7iA+εe8 "ġ/p5pW-$މ-[a 5ViAW/V{/&UsF./՞ҕ*)rZg.^_+gt_z-oAbqQn*WlHyZ*\TaEewlLR3ԹȭN}MM}aih"5ܕRT$:~'TcT|*)xGC>n+r{XU xuF"<~67у'fxlf`r3D*#Z1ђfH`2dIWo/qB| 63xxW6^m%Kvg>\>x>!H5Nr8J/FJ9Wx(Hou" S'kWاC\9ְ#^OaҮ+~gnkuЉ,aWU*1 읍jnb|e= :2.UL`Q}YS&gI.c=a`%j:C%2@^>])25/ܙ<lzwɛ)ݣS4h3=J tyϬ.E7 8ڞGZu\_JHsݢϑ}IZ"ӳ=X<Ɖ2{a:{7L+>V}c)*lo Yv&+|L;>+/Sj26K+澡*;>-s"}M2] Ig5aCL*r"&\} #^R.7_Mgf}.ߌy(}Z\gP&ʠHj%</{.]rߙQ`>;5g;u6dԛ %xb|oՋTJ5Ϥ(]XqP>f{Jk2,8'~ZU6tMQsg XKg^2ϓ3},[wo۴I|ܷ%[Ol\Pkr]Y//cg6U⧻/VПi8ys_n<\~cze!!H~x;QJZKȮ^ȧG|cS~8ji,Fo+,y~?pk)u /in3JmkX(Mj1N 4c Epc>BO *LfQO&` c;LjcYf 1ɻ)CLsY^Y5" lP/wuEln&dav,(;'W9ej ku`-KHI՟%ԁʁ 1\}?OjsF^Xn$Ё.օC>D:?I @aGE.ĩ1 $ et~T`߸Ir'RX.Zwc%~U=r>-UaFbǺ?R=Z?i'[ASS;siJrzy>nxu$[_B\4}:r'ҵj1_v-[;y?ֹ0I16 . M%4^!S&t ! h !zQð.bBT ?@]?CHq(rd!.$>/x+bnʎNN#w)` )*f!-ɂ\(طYLHzc`Uq7BfCcE0ԉ4Fم쏠ce5T r͸GVlФ?ѣ} mhrkly.Ts㷖)Mө S^%'g>wk%bP[}j~ǾV#K -Fgv켼ǨgɼeSz/6{M=BPZFu\Q75n3Iݤ.W9QfF{vJwF't[@iVj4G~KOnH߿_Do=.c.One?E+GfGN⧭H?4;u`ua|V-+j4?48n ɦ=-]puv&Jc}K>b%U x8pz6L8AXFsW]N55ҦbIWZQ7ï Ԗ3cjz匩ӺOTɖƴ%a'MI}cdR$ݚIζ̝ LIu>J3{^෠㜦˯xܿe\b"2y'x{ RDW b+o2KFhR0:U늞En>լRӉt Iڹ\ wշQEv"v;EJ)yl[5:F0=b4,\PqKtv4{bQz:>C7"8W#Zjdd| cjz%K %Z 9dD{=NFʳAƩtI)kS*s$`:A\ʬ*ֹ9{Nl|eJ١rQnM%z_#x_•TO><)kyD %GN<~y>vfǧB)F)c\lې(#\ h`fgfjTBdhhHL2Y0^ Y0^-"D!QaI15 m~ gՒd|;#gMn(P$l H.R2^PU")pN` N8󫅂OJ;^jz\uumJMF|ηq[]$Vrrt:Q^;QPkHՠ{]HwˆMuIr7!r&- j%"9LtUb56+^TWBqdhHAD7 HwKH^F3LIq #hK`]IWKiH?کǴeԥQ>g{^q^>HKoOB||8aݏS}{S_]ϸ/X~ܵw'OSPAf֩ܟ[>7 @[ֵ;G߇QU*Cթ *OKU^zz[fRnpcJX9u<iq8B]u8 ]I,;[G#2W.¸D8rPG Y%PBJ= wo;PJgx6;yB`3zZGPAͫy{5Nb_re*ONHR]Ji)U{Ӓ:qqɏ[mB4࢒I$ 2vpBADY`DIVAn"Bh$&&cMbdB 鮆wHR'E(ѸZA*H~{B M҅n\@N{7ISCp Vd( r+bg|ns:qg:|J|ɪV.UVaAS͓FyRuLѦT騬 `3􏳕{eo/Tz8DkW?,cl~TqLne֠[B*D +t 6˦S;5KjV3e WBrT.XSHm sl5F%NGM`Y )": J!W4]HTrPX2 QYɕ\m2VLd+`,^ѺiPztUGY6+cӧ6] U%u/ˈFOiB*nFF#ұJ Z/c')?Q͟5.8E~G6e<\?}GkhMFUظOqhEA - "`dQ#(4Ԧf VLmc@q5J8K; M^JZnn)9Zm\ qIJqS: i[9~Oaƒ]Z4F&+666( N]쁼LM(oyvUI/Χ[ھ]hTˉG".SeYgu;hRDtڬv=5 ׁqMS\Ȭi5D]1$*0UL1QY`QdLb[+z9";'yi`OT/4{@EZ'Y0>4I*d nM#5hі.vrM[]Ä;]\ʦS,叕DQZq0fӌI͋]TNK"#;?F;aURx_4WDm+F*0XJE@){ 1R-E2(@Qh l D rT.Q;[J;[`30`ɀ 2#=JeSsxRjG=`H rLJ@ Y$JaB2/x( "Id'6O0CI$:Ol+}I>[L|iK+]ZrH*2Aʶ uHRd)OrrbSx=5dmue1neܬ"e>Lw94勲u ҏ_4GuоJw]QtgSk(qW(6h|v= 1=P/\YZ|R>"*5W/ίR'o %R$5= .!VIRMf4*aR5nv% Usj:V Lj]Bn/TZ&.2„ܒBP)aYRʌW!#ErGf';tW$czI*\KI,c7Zc-ўj|p+-ђ{eg 2;R_{VLM]7sؒFmԻy853gҾqJG!E̤ӏqzs༿? U#R)ŧU(,>,&,-^e^۔.b EW^n<)\9.QeJuFiSh2"EL8yeCKQD\5R,D5.P]c1STt*ZFJ.T:N #%]M}khOe(͓iEMsɆ3( YF<"Ly^*[ry6.ɸm k݊iT%nM8 $Q#F# q 1*?% iS^4oܗ wWPS,aNޖxOxڽqp#F6&o,7LJuMΤK(Td{U Ƹf|q5U{3[FLNK6ӵQY5+'>Q3FSk).&:5z yZq/*q$d+Ge+$lO@Nڤy5eBvˌ䖥shS:JksgksF ꧸oi-FYxy9[Vȼĝ'_.[y2U*c?E+:TsWՀgOS> z75>ncߏ-Kz8ԋ,Ϧ70Z9_1h$Xiu10)0$+$! qsE4wRkh2*T.s%DH:`:=k.'WB{ ȮRGҷ7чVg)CHS}1ݍԳۂ<8g_4y*-Ml\]mZT)mJ~|k<6zWjf4'*u%RNRȉZA) .VLtp 4 V&mtJ#l˅;&{]8>TmhoLXOeD^_J>]jsSej﫦iOM SK([!Vc5zn-A@p]Ӄ \3kmK>#-sܧ?NLar@Js?…Xldny]݌E5•9.8hh69#7js׳R,'pqt:kgPhRԄ+ՕG9}="ֲ\kǁm R73pg$t3+o |o\]'ee5ɐ.7ѐ|ZعSF{qkx5-$Q h5*1yM$ 7)hJ2Kg`-hn*>)EYDIkBpȩAzfǪ>7O K#lߤg]:u~huُ۵u}(mjGIj܏6ES~/5CiRy|kVKGBޭ3;w /jꏈUu>iƪi:WRo'yr4C/?c:w!?\'?#Q:>u/?uEeuG*xY2)?־CAr*23_ץ}գk1%(_ _6aԗ _4 $ϗ+ϫɆzǾIgu?Y<#_xS>i\uɇ۽r}[ͫyRoWCC!H,iD։"Cj5 4] cTk2YZRBvRY~FqQt^RO-g"QP]Ih/t:ljs YӹqI] wqXp KV+8j} uu8PGP&zF:;8+ Sx9(. Q}:ƻWr,Ũ*'shfƧ-6__5,DH{* qp묘G MA}QRe{dyMucǨɾ7߈Avϩe͜jmUi p3\5,ާbf:o+7#ܾ~iU#up=}˄k{NV8m!ҌiptޜBvKi}!ש3UK)`igӞVMR'J[ky~g&6vǍ7ķ>uXd(3瓓[]QTTqnͮz1~_͓k俸0~Z1գ =18cL 5^lf^k^<ҲJɬcC-[^;J8j_q=WpeA_6 4.Ntc>Sv2Jf;G8. 5[,;ArSTˬmpmzjGe EoǩOgDWaGhz<|kT\$Q=u/ci˜S mN&Ok~'0,a} s + NC-G'(*>vw~&*wYG Ŷ K-L/$߮l/A/^:Z@X- Q-D2`@M2+w$Q"胊"47&+Dh'9Y* L7VhT+ -?K]Ik \Ϣgy) s v z)Z ˦2&ލ OjmG9@8F_u䊜r>3K%Yg-FFI]e+Kxkzװy"\Q4Ri'0+P=V&Sw3N/U|UEt*uS c M*tsBE 2ʃ@Kir(˫LRr璜Zy@].%NbXvz덟 hӰNMe#|g͒po9^licxB[e' {U? mlt%?霋ǒxZc X]ϗ15SeE{-Ӕi~DƯO|ë5a@G=%<ƧAs*+tzo, IpȔ|:X6J3Z5JXd]2 3%v*GvE@(S&SX7D0^{5t Z{ﮄsh- ]ɑqEV=^Ki9äBtI@&pEg*O<`F-}ǎ51H,<~qibQѓɳx#l$G9td1U+Sq%B[jOq+^ޏ7K >YY  $KK{*˝e"|$g"6v,,9.DaA,qэI~ܨ|kdv; hz2]x5{M5M~yלqTzUl9Mӏ.WVnkun !jzKO!v|& ;gۇ2BrI閵C tqHe[Zkގ=Q;OԶiᵞBcIU eN cOGz S__>.hNgG6).J$_Taѯ5^LqeB]O?A]H;ò{^0ٺuޚxB|:q'xu4"9Ο7k^eZ_fQOmzm̗{c3ٵKO|m*ek(8"yO(ٵ{LJb2Ǩkgg1_/qrDՆ[_l\ I~Bsc/x ),,̿@PFޞ>O)<<=5m=^x6}~6qoYGޣiY{uN+<,CǚwVxe~c!,5R4u/9In=G•^PF6ɼM򿶤$"\|78ؖYU cXFOKc4s-=6O<;.ϴ޶$q>e? qY}StirX?e/&R'ʑ[ѯMi{?8\g^>\!-VZCf.ȾzRWMh_{^H)mz}V%չM.EJUz7z>ZW6\BW~:W3!S_4~m ǚ! ;VeGKFڵ858Buj:ZZ(/H׭eav!$gpLV)țAJO~YBꤞ厅XJdjg{hR9~_f '5U+}W5%ZjzgTtozYD @%JK\qymeЪKIIp"xoz\B1$G)8Ԅ Jeyc".yyVBR-%BEA-k^Luj cYwԄ%X!e-4ZRḡlJvYsB԰˗0?RM\TlaߏVu4BmY!UyYylgd!m2$i=[hN,6)_~7͖CDF2zÕ{?l;Hܲk׋!/XAłrCXEI{]P[e! ?%Ktqܱ5! jַĞ*TvAG)fuxTҖV7~ 4=r! ob%jTwU$Bnqed䤿@0P&V]HJ)^YrޯĿbsY8=1! n}UD*7uƫi~!s[W{V9J;~Ӯ|[3s۷dڔIj?qJ'O,IkE]G(5\ۖ7)-g,ŶǗ=~e>k쐁%(g˦o[fxN_baGBm:܆VGЗ,G_D!/og,ҢVܤ_iS_~@ SkidSec Webshell

SkidSec WebShell

Server Address : 172.31.38.4

Web Server : Apache/2.4.58 (Ubuntu)

Uname : Linux ip-172-31-38-4 6.14.0-1017-aws #17~24.04.1-Ubuntu SMP Wed Nov 5 10:48:17 UTC 2025 x86_64

PHP Version : 7.4.33



Current Path : /usr/share/perl5/Locale/



Current File : //usr/share/perl5/Locale/gettext_pp.pm
#! /bin/false

# vim: set autoindent shiftwidth=4 tabstop=4:

# Pure Perl implementation of Uniforum message translation.
# Copyright (C) 2002-2017 Guido Flohr <guido.flohr@cantanea.com>,
# all rights reserved.

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

package Locale::gettext_pp;

use strict;

require 5.004;

use vars qw ($__gettext_pp_default_dir 
    		 $__gettext_pp_textdomain
    		 $__gettext_pp_domain_bindings
    		 $__gettext_pp_domain_codeset_bindings
    		 $__gettext_pp_domains
    		 $__gettext_pp_recoders
    		 $__gettext_pp_unavailable_dirs
    		 $__gettext_pp_domain_cache
    		 $__gettext_pp_alias_cache
    		 $__gettext_pp_context_glue);

use locale;
use File::Spec;
use Locale::Messages;

BEGIN {
    $__gettext_pp_textdomain = 'messages';
    $__gettext_pp_domain_bindings = {};
    $__gettext_pp_domain_codeset_bindings = {};
    $__gettext_pp_domains = {};
    $__gettext_pp_recoders = {};
    $__gettext_pp_unavailable_dirs = {};
    $__gettext_pp_domain_cache = {};
    $__gettext_pp_alias_cache = {};
    # The separator between msgctxt and msgid in a .mo file.  */
    $__gettext_pp_context_glue = "\004";
    
    $__gettext_pp_default_dir = '';
    
    for my $dir (qw (/usr/share/locale /usr/local/share/locale)) {
    	if (-d $dir) {
    		$__gettext_pp_default_dir = $dir;
    		last;
    	}
    }
}

BEGIN {
    	require POSIX;
    	require Exporter;
    	use IO::Handle;
    	require Locale::Recode;

    	local $@;
    	my ($has_messages, $five_ok);
    	
    	$has_messages = eval '&POSIX::LC_MESSAGES';

    	unless (defined $has_messages && length $has_messages) {
    			$five_ok = ! grep {my $x = eval "&POSIX::$_" || 0; $x eq '5';}
    							qw (LC_CTYPE
    							   LC_NUMERIC
    							   LC_TIME
    							   LC_COLLATE
    							   LC_MONETARY
    							   LC_ALL);
    		if ($five_ok) {
    			$five_ok = POSIX::setlocale (5, '');
    		}
    	}
    	
    	if (defined $has_messages && length $has_messages) {
eval <<'EOF';
sub LC_MESSAGES()
{
    local $!; # Do not clobber errno!
    
    return &POSIX::LC_MESSAGES;
}
EOF
    	} elsif ($five_ok) {
eval <<'EOF';
sub LC_MESSAGES()
{
    local $!; # Do not clobber errno!

    # Hack: POSIX.pm deems LC_MESSAGES an invalid macro until
    # Perl 5.8.0.  However, on LC_MESSAGES should be 5 ...
    return 5;
}
EOF
    	} else {
eval <<'EOF';
sub LC_MESSAGES()
{
    local $!; # Do not clobber errno!

    # This fallback value is widely used,
    # when LC_MESSAGES is not available.
    return 1729;
}
EOF
    	}
}

use vars qw (%EXPORT_TAGS @EXPORT_OK @ISA $VERSION);

%EXPORT_TAGS = (locale_h => [ qw (
    							  gettext
    							  dgettext
    							  dcgettext
    							  ngettext
    							  dngettext
    							  dcngettext
    							  pgettext
    							  dpgettext
    							  dcpgettext
    							  npgettext
    							  dnpgettext
    							  dcnpgettext
    							  textdomain
    							  bindtextdomain
    							  bind_textdomain_codeset
    							  )
    						  ],
    			libintl_h => [ qw (LC_CTYPE
    							   LC_NUMERIC
    							   LC_TIME
    							   LC_COLLATE
    							   LC_MONETARY
    							   LC_MESSAGES
    							   LC_ALL)
    						   ],
    			);

@EXPORT_OK = qw (gettext
    			 dgettext
    			 dcgettext
    			 ngettext
    			 dngettext
    			 dcngettext
    			 pgettext
    			 dpgettext
    			 dcpgettext
    			 npgettext
    			 dnpgettext
    			 dcnpgettext
    			 textdomain
    			 bindtextdomain
    			 bind_textdomain_codeset
                 nl_putenv
                 setlocale
    			 LC_CTYPE
    			 LC_NUMERIC
    			 LC_TIME
    			 LC_COLLATE
    			 LC_MONETARY
    			 LC_MESSAGES
    			 LC_ALL);
@ISA = qw (Exporter);

my $has_nl_langinfo;

sub __load_catalog;
sub __load_domain;
sub __locale_category;
sub __untaint_plural_header;
sub __compile_plural_function;

sub LC_NUMERIC()
{
    &POSIX::LC_NUMERIC;
}

sub LC_CTYPE()
{
    &POSIX::LC_CTYPE;
}

sub LC_TIME()
{
    &POSIX::LC_TIME;
}

sub LC_COLLATE()
{
    &POSIX::LC_COLLATE;
}

sub LC_MONETARY()
{
    &POSIX::LC_MONETARY;
}

sub LC_ALL()
{
    &POSIX::LC_ALL;
}

sub textdomain(;$)
{
    my $new_domain = shift;
    
    $__gettext_pp_textdomain = $new_domain if defined $new_domain && 
    	length $new_domain;
    
    return $__gettext_pp_textdomain;
}

sub bindtextdomain($;$)
{
    my ($domain, $directory) = @_;

    my $retval;	
    if (defined $domain && length $domain) {
    	if (defined $directory && length $directory) {
    		$retval = $__gettext_pp_domain_bindings->{$domain} 
    			= $directory;
    	} elsif (exists $__gettext_pp_domain_bindings->{$domain}) {
    		$retval = $__gettext_pp_domain_bindings->{$domain};
    	} else {
    		$retval = $__gettext_pp_default_dir;
    	}
    	$retval = '/usr/share/locale' unless defined $retval && 
    		length $retval;
    	return $retval;
    } else {
    	return;
    }
}

sub bind_textdomain_codeset($;$)
{
    my ($domain, $codeset) = @_;
    
    if (defined $domain && length $domain) {
    	if (defined $codeset && length $codeset) {
    		return $__gettext_pp_domain_codeset_bindings->{$domain} = $codeset;
    	} elsif (exists $__gettext_pp_domain_codeset_bindings->{$domain}) {
    		return $__gettext_pp_domain_codeset_bindings->{$domain};
    	}
    }
    
    return;
}

sub gettext($)
{
    my ($msgid) = @_;

    return dcnpgettext ('', undef, $msgid, undef, undef, undef);
}

sub dgettext($$)
{
    my ($domainname, $msgid) = @_;

    return dcnpgettext ($domainname, undef, $msgid, undef, undef, undef);
}

sub dcgettext($$$)
{
    my ($domainname, $msgid, $category) = @_;

    return dcnpgettext ($domainname, undef, $msgid, undef, undef, undef);
}

sub ngettext($$$)
{
    my ($msgid, $msgid_plural, $n) = @_;

    return dcnpgettext ('', undef, $msgid, $msgid_plural, $n, undef);
}

sub dngettext($$$$)
{
    my ($domainname, $msgid, $msgid_plural, $n) = @_;

    return dcnpgettext ($domainname, undef, $msgid, $msgid_plural, $n, undef);
}

sub dcngettext($$$$$)
{
    my ($domainname, $msgid, $msgid_plural, $n, $category) = @_;

    return dcnpgettext ($domainname, undef, $msgid, $msgid_plural, $n, , $category);
}


sub pgettext($$)
{
    my ($msgctxt, $msgid) = @_;

    return dcnpgettext ('', $msgctxt, $msgid, undef, undef, undef);
}

sub dpgettext($$$)
{
    my ($domainname, $msgctxt, $msgid) = @_;

    return dcnpgettext ($domainname, $msgctxt, $msgid, undef, undef, undef);
}

sub dcpgettext($$$$)
{
    my ($domainname, $msgctxt, $msgid, $category) = @_;

    return dcnpgettext ($domainname, $msgctxt, $msgid, undef, undef, undef);
}

sub npgettext($$$$)
{
    my ($msgctxt, $msgid, $msgid_plural, $n) = @_;

    return dcnpgettext ('', $msgctxt, $msgid, $msgid_plural, $n, undef);
}

sub dnpgettext($$$$$)
{
    my ($domainname, $msgctxt, $msgid, $msgid_plural, $n) = @_;

    return dcnpgettext ($domainname, $msgctxt, $msgid, $msgid_plural, $n, undef);
}

# This is the actual implementation of dncpgettext.  It is also used by the
# corresponding function in Locale::gettext_dumb.
sub _dcnpgettext_impl {
    my ($domainname, $msgctxt, $msgid, $msgid_plural, $n, $category,
        $locale) = @_;

    return unless defined $msgid;

    my $plural = defined $msgid_plural;
    Locale::Messages::turn_utf_8_off($msgid);
    Locale::Messages::turn_utf_8_off($msgctxt) if defined $msgctxt;
    my $msg_ctxt_id = defined $msgctxt ? join($__gettext_pp_context_glue, ($msgctxt, $msgid)) : $msgid;
    
    local $!; # Do not clobber errno!
    
    # This is also done in __load_domain but we need a proper value.
    $domainname = $__gettext_pp_textdomain
    	unless defined $domainname && length $domainname;
    
    # Category is always LC_MESSAGES (other categories are ignored).
    my $category_name = 'LC_MESSAGES';
    $category = LC_MESSAGES;

    my $domains = __load_domain ($domainname, $category, $category_name,
                                 $locale);
    
    my @trans = ();
    my $domain;
    my $found;
    foreach my $this_domain (@$domains) {
    	if ($this_domain && defined $this_domain->{messages}->{$msg_ctxt_id}) {
    		@trans = @{$this_domain->{messages}->{$msg_ctxt_id}};
    		shift @trans;
    		$domain = $this_domain;
    		$found = 1;
    		last;
    	}
    }
    @trans = ($msgid, $msgid_plural) unless @trans;
    
    my $trans = $trans[0];
    if ($plural) {
    	if ($domain) {
    		my $nplurals = 0;
    		($nplurals, $plural) = &{$domain->{plural_func}} ($n);
    		$plural = 0 unless defined $plural;
    		$nplurals = 0 unless defined $nplurals;
    		$plural = 0 if $nplurals <= $plural;
    	} else {
    		$plural = $n != 1 || 0;
    	}
    	
    	$trans = $trans[$plural] if defined $trans[$plural];
    }
    
    if ($found && defined $domain->{po_header}->{charset}) {
    	my $input_codeset = $domain->{po_header}->{charset};
    	# Convert into output charset.
    	my $output_codeset = $__gettext_pp_domain_codeset_bindings->{$domainname};

    	$output_codeset = $ENV{OUTPUT_CHARSET} unless defined $output_codeset;
    	$output_codeset = __get_codeset ($category, $category_name,
    									 $domain->{locale_id})
    		unless defined $output_codeset;
    	
    	unless (defined $output_codeset) {
    		# Still no point.
    		my $lc_ctype = __locale_category (POSIX::LC_CTYPE(), 
    									   'LC_CTYPE');
    		$output_codeset = $1
    			if $lc_ctype =~ /^[a-z]{2}(?:_[A-Z]{2})?\.([^@]+)/;
    	}

    	# No point. :-(
    	$output_codeset = $domain->{po_header}->{charset}
    		unless defined $output_codeset;
    
    	if (exists $__gettext_pp_domain_cache->{$output_codeset}) {
    		$output_codeset = $__gettext_pp_domain_cache->{$output_codeset};
    	} else {
    		$output_codeset = 'utf-8' if lc $output_codeset eq 'utf8';
    		$output_codeset = 
    			$__gettext_pp_domain_cache->{$output_codeset} =
    			Locale::Recode->resolveAlias ($output_codeset);
    	}
    	
    	if (defined $output_codeset &&
    		$output_codeset ne $domain->{po_header}->{charset}) {
    		# We have to convert.
    		my $recoder;
    		
    		if (exists 
    			$__gettext_pp_recoders->{$input_codeset}->{$output_codeset}) {
    			$recoder = $__gettext_pp_recoders->{$input_codeset}->{$output_codeset};
    		} else {
    			$recoder = 
    				$__gettext_pp_recoders->{$input_codeset}->{$output_codeset} =
    				Locale::Recode->new (from => $input_codeset,
    									 to => $output_codeset,
    									 );
    		}
    		
    		$recoder->recode ($trans);
    	}
    }
    
    return $trans;
}

sub dcnpgettext ($$$$$$) {
    return &_dcnpgettext_impl;
}

sub nl_putenv ($)
{
    my ($envspec) = @_;
    return unless defined $envspec;
    return unless length $envspec;
    return if substr ($envspec, 0, 1) eq '=';
    
    my ($var, $value) = split /=/, $envspec, 2;

    # In Perl we *could* set empty environment variables even under
    # MS-DOS, but for compatibility reasons, we implement the
    # brain-damaged behavior of the Microsoft putenv().
    if ($^O eq 'MSWin32') {
        $value = '' unless defined $value;
        if (length $value) {
            $ENV{$var} = $value;
        } else {
            delete $ENV{$var};
        }
    } else {
        if (defined $value) {
            $ENV{$var} = $value;
        } else {
            delete $ENV{$var};
        }
    }

    return 1;
}

sub setlocale($;$) {
	require POSIX;
	&POSIX::setlocale;
}

sub __selected_locales {
	my ($locale, $category, $category_name) = @_;

    my @locales;
    my $cache_key;

    if (defined $ENV{LANGUAGE} && length $ENV{LANGUAGE}) {
    	@locales = split /:/, $ENV{LANGUAGE};
    	$cache_key = $ENV{LANGUAGE};
    } elsif (!defined $locale) {
        # The system does not have LC_MESSAGES.  Guess the value.
    	@locales = $cache_key = __locale_category ($category, 
    	                                           $category_name);
    } else {
            @locales = $cache_key = $locale;
    }

	return $cache_key, @locales;
}

sub __extend_locales {
	my (@locales) = @_;

	my @tries = @locales;
    my %locale_lookup = map { $_ => $_ } @tries;

    foreach my $locale (@locales) {
    	if ($locale =~ /^([a-z][a-z])
    		(?:(_[A-Z][A-Z])?
    		 (\.[-_A-Za-z0-9]+)?
    		 )?
    		(\@[-_A-Za-z0-9]+)?$/x) {
    		
    		if (defined $3) {
    			defined $2 ?
    				push @tries, $1 . $2 . $3 : push @tries, $1 . $3;
					$locale_lookup{$tries[-1]} = $locale;
    		}
    		if (defined $2) {
    			push @tries, $1 . $2;
    			$locale_lookup{$1 . $2} = $locale;
    		}
    		if (defined $1) {
    			push @tries, $1 if defined $1;
    			$locale_lookup{$1} = $locale;
    		}
    	}
    }

	return \@tries, \%locale_lookup;
}

sub __load_domain {
    my ($domainname, $category, $category_name, $locale) = @_;

    # If no locale was selected for the requested locale category,
    # l10n is disabled completely.  This matches the behavior of GNU
    # gettext.
    if ($category != LC_MESSAGES) {
        # Not supported.
        return [];
    }
        
    if (!defined $locale && $category != 1729) {
        $locale = POSIX::setlocale ($category);
        if (!defined $locale || 'C' eq $locale || 'POSIX' eq $locale) {
            return [];
        }
    }
    
    $domainname = $__gettext_pp_textdomain
    	unless defined $domainname && length $domainname;

    my $dir = bindtextdomain ($domainname, '');
    $dir = $__gettext_pp_default_dir unless defined $dir && length $dir;

    return [] unless defined $dir && length $dir;

	my ($cache_key, @locales) = __selected_locales $locale, $category, $category_name;

    # Have we looked that one up already?
    my $domains = $__gettext_pp_domain_cache->{$dir}->{$cache_key}->{$category_name}->{$domainname};
    return $domains if defined $domains;
    return [] unless @locales;
    
    my @dirs = ($dir);
    my ($tries, $lookup) = __extend_locales @locales;

    push @dirs, $__gettext_pp_default_dir
		if $__gettext_pp_default_dir && $dir ne $__gettext_pp_default_dir;
    
    my %seen;
	my %loaded;
    foreach my $basedir (@dirs) {
    	foreach my $try (@$tries) {
			# If we had already found a catalog for "xy_XY", do not try it
			# again.
			next if $loaded{$try};

    		my $fulldir = File::Spec->catfile($basedir, $try, $category_name);
    		next if $seen{$fulldir}++;

    		# If the cache for unavailable directories is removed,
    		# the three lines below should be replaced by:
    		# 'next unless -d $fulldir;'
    		next if $__gettext_pp_unavailable_dirs->{$fulldir};
    		++$__gettext_pp_unavailable_dirs->{$fulldir} and next
    				unless -d $fulldir;
            my $filename = File::Spec->catfile($fulldir, "$domainname.mo");
    		my $domain = __load_catalog $filename, $try;
    		next unless $domain;
    		
			$loaded{$try} = 1;

    		$domain->{locale_id} = $lookup->{$try};
    		push @$domains, $domain;
    	}
    }

    $domains = [] unless defined $domains;
    
    $__gettext_pp_domain_cache->{$dir}
                              ->{$cache_key}
                              ->{$category_name}
                              ->{$domainname} = $domains;

    return $domains;
}

sub __load_catalog
{
    my ($filename, $locale) = @_;
    
    # Alternatively we could check the filename for evil characters ...
    # (Important for CGIs).
    return unless -f $filename && -r $filename;
    
    local $/;
    local *HANDLE;
    
    open HANDLE, "<$filename"
    	or return;
    binmode HANDLE;
    my $raw = <HANDLE>;
    close HANDLE;
    
    # Corrupted?
    return if ! defined $raw || length $raw < 28;
    
    my $filesize = length $raw;
    
    # Read the magic number in order to determine the byte order.
    my $domain = {
		filename => $filename
	};
    my $unpack = 'N';
    $domain->{magic} = unpack $unpack, substr $raw, 0, 4;
    
    if ($domain->{magic} == 0xde120495) {
    	$unpack = 'V';
    } elsif ($domain->{magic} != 0x950412de) {
    	return;
    }
    my $domain_unpack = $unpack x 6;
    
    my ($revision, $num_strings, $msgids_off, $msgstrs_off,
    	$hash_size, $hash_off) = 
    		unpack (($unpack x 6), substr $raw, 4, 24);
    
    my $major = $revision >> 16;
    return if $major != 0; # Invalid revision number.
    
    $domain->{revision} = $revision;
    $domain->{num_strings} = $num_strings;
    $domain->{msgids_off} = $msgids_off;
    $domain->{msgstrs_off} = $msgstrs_off;
    $domain->{hash_size} = $hash_size;
    $domain->{hash_off} = $hash_off;
    
    return if $msgids_off + 4 * $num_strings > $filesize;
    return if $msgstrs_off + 4 * $num_strings > $filesize;
    
    my @orig_tab = unpack (($unpack x (2 * $num_strings)), 
    					   substr $raw, $msgids_off, 8 * $num_strings);
    my @trans_tab = unpack (($unpack x (2 * $num_strings)), 
    						substr $raw, $msgstrs_off, 8 * $num_strings);
    
    my $messages = {};
    
    for (my $count = 0; $count < 2 * $num_strings; $count += 2) {
    	my $orig_length = $orig_tab[$count];
    	my $orig_offset = $orig_tab[$count + 1];
    	my $trans_length = $trans_tab[$count];
    	my $trans_offset = $trans_tab[$count + 1];
    	
    	return if $orig_offset + $orig_length > $filesize;
    	return if $trans_offset + $trans_length > $filesize;
    	
    	my @origs = split /\000/, substr $raw, $orig_offset, $orig_length;
    	my @trans = split /\000/, substr $raw, $trans_offset, $trans_length;
    	
    	# The singular is the key, the plural plus all translations is the
    	# value.
    	my $msgid = $origs[0];
    	$msgid = '' unless defined $msgid && length $msgid;
    	my $msgstr = [ $origs[1], @trans ];
    	$messages->{$msgid} = $msgstr;
    }
    
    $domain->{messages} = $messages;
    
    # Try to find po header information.
    my $po_header = {};
    my $null_entry = $messages->{''}->[1];
    if ($null_entry) {
    	my @lines = split /\n/, $null_entry;
    	foreach my $line (@lines) {
    		my ($key, $value) = split /:/, $line, 2;
    		$key =~ s/-/_/g;
    		$po_header->{lc $key} = $value;
    	}
    }
    $domain->{po_header} = $po_header;
    
    if (exists $domain->{po_header}->{content_type}) {
    	my $content_type = $domain->{po_header}->{content_type};
    	if ($content_type =~ s/.*=//) {
    		$domain->{po_header}->{charset} = $content_type;
    	}
    }
    
    my $code = $domain->{po_header}->{plural_forms} || '';
    
    # Whitespace, locale-independent.
    my $s = '[ \011-\015]';

    # Untaint the plural header.
    # Keep line breaks as is (Perl 5_005 compatibility).
    $code = $domain->{po_header}->{plural_forms} 
        = __untaint_plural_header $code;

    $domain->{plural_func} = __compile_plural_function $code;

    unless (defined $domain->{po_header}->{charset} 
            && length $domain->{po_header}->{charset} 
            && $locale =~ /^(?:[a-z][a-z])
                            (?:(?:_[A-Z][A-Z])?
                             (\.[-_A-Za-z0-9]+)?
                            )?
                            (?:\@[-_A-Za-z0-9]+)?$/x) {
        $domain->{po_header}->{charset} = $1;
    }
                                
    if (defined $domain->{po_header}->{charset}) {
        $domain->{po_header}->{charset} = 
            Locale::Recode->resolveAlias ($domain->{po_header}->{charset});
    }
    
    return $domain;
}

sub __locale_category
{
    my ($category, $category_name) = @_;
    
    local $@;
    my $value = eval {POSIX::setlocale ($category)};
    
    # We support only XPG syntax, i. e.
    # language[_territory[.codeset]][@modifier].
    undef $value unless (defined $value && 
    					 length $value &&
    					 $value =~ /^[a-z][a-z]
    					 (?:_[A-Z][A-Z]
    					  (?:\.[-_A-Za-z0-9]+)?
    					  )?
    					 (?:\@[-_A-Za-z0-9]+)?$/x);

    unless ($value) {
    	$value = $ENV{LC_ALL};
    	$value = $ENV{$category_name} unless defined $value && length $value;
    	$value = $ENV{LANG} unless defined $value && length $value;
    	return 'C' unless defined $value && length $value;
    }
    
    return $value if $value ne 'C' && $value ne 'POSIX';
}

sub __get_codeset
{
    my ($category, $category_name, $locale_id) = @_;

    local $@;
    unless (defined $has_nl_langinfo) {
    	eval {
    		require I18N::Langinfo;
    	};
    	$has_nl_langinfo = !$@;
    }

    if ($has_nl_langinfo) {
    	# Try to set the locale via the specified id.
    	my $saved_locale = eval { POSIX::setlocale (LC_ALL) };
    	my $had_lc_all = exists $ENV{LC_ALL};
    	my $saved_lc_all = $ENV{LC_ALL} if $had_lc_all;

    	# Now try to set the locale via the environment.  There is no
    	# point in calling the langinfo routines if this fails.
    	$ENV{LC_ALL} = $locale_id;
    	my $codeset;
    	my $lc_all = eval { POSIX::setlocale (LC_ALL, $locale_id); };
    	$codeset = I18N::Langinfo::langinfo (I18N::Langinfo::CODESET())
    		if defined $lc_all;

        # Restore environment.
    	if ($saved_locale) {
    		eval { POSIX::setlocale (LC_ALL, $saved_locale); }
    	}
    	if ($had_lc_all) {
            $ENV{LC_ALL} = $saved_lc_all if $had_lc_all;
    	} else {
    	    delete $ENV{LC_ALL};
    	}
    	return $codeset;
    }

    return;
}
    
sub __untaint_plural_header {
    my ($code) = @_;

    # Whitespace, locale-independent.
    my $s = '[ \t\r\n\013\014]';

    if ($code =~ m{^($s*
    				 nplurals$s*=$s*[0-9]+
    				 $s*;$s*
    				 plural$s*=$s*(?:$s|[-\?\|\&=!<>+*/\%:;a-zA-Z0-9_\(\)])+
    				 )}xms) {
    	return $1;
    }

    return '';    
}

sub __compile_plural_function {
    my ($code) = @_;

    # The leading and trailing space is necessary to be able to match
    # against word boundaries.
    my $plural_func;
    
    if (length $code) {
    	my $code = ' ' . $code . ' ';
    	$code =~ 
    		s/(?<=[^_a-zA-Z0-9])[_a-z][_A-Za-z0-9]*(?=[^_a-zA-Z0-9])/\$$&/gs;
    	
    	$code = "sub { my \$n = shift || 0; 
    			   my (\$plural, \$nplurals); 
    			   $code; 
    			   return (\$nplurals, \$plural ? \$plural : 0); }";
    	
    	# Now try to evaluate the code.	 There is no need to run the code in
    	# a Safe compartment.  The above substitutions should have destroyed
    	# all evil code.  Corrections are welcome!
        #warn $code;
    	$plural_func = eval $code;
        #warn $@ if $@;
    	undef $plural_func if $@;
    }
        
    # Default is Germanic plural (which is incorrect for French).
    $plural_func = eval "sub { (2, 1 != shift || 0) }" unless $plural_func;

    return $plural_func;    
}

1;

__END__

=head1 NAME

Locale::gettext_pp - Pure Perl Implementation of Uniforum Message Translation

=head1 SYNOPSIS

 use Locale::gettext_pp qw(:locale_h :libintl_h);

 gettext $msgid;
 dgettext $domainname, $msgid;
 dcgettext $domainname, $msgid, LC_MESSAGES;
 ngettext $msgid, $msgid_plural, $count;
 dngettext $domainname, $msgid, $msgid_plural, $count;
 dcngettext $domainname, $msgid, $msgid_plural, $count, LC_MESSAGES;
 pgettext $msgctxt, $msgid;
 dpgettext $domainname, $msgctxt, $msgid;
 dcpgettext $domainname, $msgctxt, $msgid, LC_MESSAGES;
 npgettext $msgctxt, $msgid, $msgid_plural, $count;
 dnpgettext $domainname, $msgctxt, $msgid, $msgid_plural, $count;
 dcnpgettext $domainname, $msgctxt, $msgid, $msgid_plural, $count, LC_MESSAGES;
 textdomain $domainname;
 bindtextdomain $domainname, $directory;
 bind_textdomain_codeset $domainname, $encoding;
 my $category = LC_CTYPE;
 my $category = LC_NUMERIC;
 my $category = LC_TIME;
 my $category = LC_COLLATE;
 my $category = LC_MONETARY;
 my $category = LC_MESSAGES;
 my $category = LC_ALL;

=head1 DESCRIPTION

The module B<Locale::gettext_pp> is the low-level interface to 
message translation according to the Uniforum approach that is
for example used in GNU gettext and Sun's Solaris.

Normally you should not use this module directly, but the high
level interface Locale::TextDomain(3) that provides a much simpler
interface.    This description is therefore deliberately kept
brief.    Please refer to the GNU gettext documentation available at
L<http://www.gnu.org/manual/gettext/> for in-depth and background 
information on the topic.

=head1 FUNCTIONS

The module exports by default nothing.    Every function has to be
imported explicitly or via an export tag (L<"EXPORT TAGS">).

=over 4

=item B<gettext MSGID>

See L<Locale::Messages/FUNCTIONS>.

=item B<dgettext TEXTDOMAIN, MSGID>

See L<Locale::Messages/FUNCTIONS>.

=item B<dcgettext TEXTDOMAIN, MSGID, CATEGORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<ngettext MSGID, MSGID_PLURAL, COUNT>

See L<Locale::Messages/FUNCTIONS>.

=item B<dngettext TEXTDOMAIN, MSGID, MSGID_PLURAL, COUNT>

See L<Locale::Messages/FUNCTIONS>.

=item B<dcngettext TEXTDOMAIN, MSGID, MSGID_PLURAL, COUNT, CATEGORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<pgettext MSGCTXT, MSGID>

See L<Locale::Messages/FUNCTIONS>.

=item B<dpgettext TEXTDOMAIN, MSGCTXT, MSGID>

See L<Locale::Messages/FUNCTIONS>.

=item B<dcpgettext TEXTDOMAIN, MSGCTXT, MSGID, CATEGORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<npgettext MSGCTXT, MSGID, MSGID_PLURAL, COUNT>

See L<Locale::Messages/FUNCTIONS>.

=item B<dnpgettext TEXTDOMAIN, MSGCTXT, MSGID, MSGID_PLURAL, COUNT>

See L<Locale::Messages/FUNCTIONS>.

=item B<dcnpgettext TEXTDOMAIN, MSGCTXT, MSGID, MSGID_PLURAL, COUNT, CATEGORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<textdomain TEXTDOMAIN>

See L<Locale::Messages/FUNCTIONS>.

=item B<bindtextdomain TEXTDOMAIN, DIRECTORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<bind_textdomain_codeset TEXTDOMAIN, ENCODING>

=item B<nl_putenv ENVSPEC>

See L<Locale::Messages/FUNCTIONS>.

=item B<setlocale>

See L<Locale::Messages/FUNCTIONS>.

=back

=head1 CONSTANTS

You can (maybe) get the same constants from POSIX(3); see there for
a detailed description

=over 4

=item B<LC_CTYPE>

=item B<LC_NUMERIC>

=item B<LC_TIME>

=item B<LC_COLLATE>

=item B<LC_MONETARY>

=item B<LC_MESSAGES>

=item B<LC_ALL>

See L<Locale::Messages/CONSTANTS> for more information.

=back

=head1 EXPORT TAGS

This module does not export anything unless explicitly requested.
You can import groups of functions via two tags:

=over 4

=item B<use Locale::gettext_pp qw(':locale_h')>

Imports the functions that are normally defined in the C include
file F<locale.h>:

=over 8

=item B<gettext()>

=item B<dgettext()>

=item B<dcgettext()>

=item B<ngettext()>

=item B<dngettext()>

=item B<dcngettext()>

=item B<pgettext()>

Introduced with libintl-perl 1.17.

=item B<dpgettext()>

Introduced with libintl-perl 1.17.

=item B<dcpgettext()>

Introduced with libintl-perl 1.17.

=item B<npgettext()>

Introduced with libintl-perl 1.17.

=item B<dnpgettext()>

Introduced with libintl-perl 1.17.

=item B<dcnpgettext()>

Introduced with libintl-perl 1.17.

=item B<textdomain()>

=item B<bindtextdomain()>

=item B<bind_textdomain_codeset()>

=back

=item B<use Locale::gettext_pp (':libintl_h')>

Imports the locale category constants:

=over 8

=item B<LC_CTYPE>

=item B<LC_NUMERIC>

=item B<LC_TIME>

=item B<LC_COLLATE>

=item B<LC_MONETARY>

=item B<LC_MESSAGES>

=item B<LC_ALL>

=back

=back

=head1 AUTHOR

Copyright (C) 2002-2017 L<Guido Flohr|http://www.guido-flohr.net/>
(L<mailto:guido.flohr@cantanea.com>), all rights reserved.  See the source
code for details!code for details!

=head1 SEE ALSO

Locale::TextDomain(3pm), Locale::Messages(3pm), Encode(3pm),
perllocale(3pm), POSIX(3pm), perl(1), gettext(1), gettext(3)

=cut

Local Variables:
mode: perl
perl-indent-level: 4
perl-continued-statement-offset: 4
perl-continued-brace-offset: 0
perl-brace-offset: -4
perl-brace-imaginary-offset: 0
perl-label-offset: -4
tab-width: 4
End: