Skip to content

Instantly share code, notes, and snippets.

@rwstauner
Forked from gugod/Makefile
Created May 9, 2011 16:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rwstauner/962861 to your computer and use it in GitHub Desktop.
Save rwstauner/962861 to your computer and use it in GitHub Desktop.
#!/usr/bin/env perl
# This chunk of stuff was generated by App::FatPacker. To find the original
# file's code, look for the end of this BEGIN block or the string 'FATPACK'
BEGIN {
my %fatpacked;
$fatpacked{"Devel/PatchPerl.pm"} = <<'DEVEL_PATCHPERL';
package Devel::PatchPerl;
BEGIN {
$Devel::PatchPerl::VERSION = '0.32';
}
# ABSTRACT: Patch perl source a la Devel::PPort's buildperl.pl
use strict;
use warnings;
use File::pushd qw[pushd];
use File::Spec;
use IO::File;
use IPC::Cmd qw[can_run run];
use Devel::PatchPerl::Hints qw[hint_file];
use vars qw[@ISA @EXPORT_OK];
@ISA = qw(Exporter);
@EXPORT_OK = qw(patch_source);
my $patch_exe = can_run('patch');
my @patch = (
{
perl => [
qr/^5\.00[01234]/,
qw/
5.005
5.005_01
5.005_02
5.005_03
/,
],
subs => [
[ \&_patch_db, 1 ],
],
},
{
perl => [
qw/
5.6.0
5.6.1
5.7.0
5.7.1
5.7.2
5.7.3
5.8.0
/,
],
subs => [
[ \&_patch_db, 3 ],
],
},
{
perl => [
qr/^5\.004_0[1234]$/,
],
subs => [
[ \&_patch_doio ],
],
},
{
perl => [
qw/
5.005
5.005_01
5.005_02
/,
],
subs => [
[ \&_patch_sysv, old_format => 1 ],
],
},
{
perl => [
qw/
5.005_03
5.005_04
/,
qr/^5\.6\.[0-2]$/,
qr/^5\.7\.[0-3]$/,
qr/^5\.8\.[0-8]$/,
qr/^5\.9\.[0-5]$/
],
subs => [
[ \&_patch_sysv, old_format => 0 ],
],
},
{
perl => [
qr/^5\.004_05$/,
qr/^5\.005(?:_0[1-4])?$/,
qr/^5\.6\.[01]$/,
],
subs => [
[ \&_patch_configure ],
[ \&_patch_makedepend_lc ],
],
},
{
perl => [
'5.8.0',
],
subs => [
[ \&_patch_makedepend_lc ],
],
},
{
perl => [
qr/.*/,
],
subs => [
[ \&_patch_hints ],
],
},
{
perl => [
qr/^5\.6\.[0-2]$/,
qr/^5\.7\.[0-3]$/,
qr/^5\.8\.[0-8]$/,
],
subs => [
[ \&_patch_makedepend_SH ],
],
},
{
perl => [
qr/^5\.1[0-2]/,
],
subs => [
[ \&_patch_archive_tar_tests ],
],
},
);
sub patch_source {
my $vers = shift;
$vers = shift if eval { $vers->isa(__PACKAGE__) };
my $source = shift || '.';
if ( !$vers ) {
$vers = _determine_version($source);
if ( $vers ) {
warn "Auto-guessed '$vers'\n";
}
else {
die "You didn't provide a perl version and I don't appear to be in a perl source tree\n";
}
}
$source = File::Spec->rel2abs($source);
warn "No patch utility found\n" unless $patch_exe;
{
my $dir = pushd( $source );
for my $p ( grep { _is( $_->{perl}, $vers ) } @patch ) {
for my $s (@{$p->{subs}}) {
my($sub, @args) = @$s;
push @args, $vers unless scalar @args;
$sub->(@args);
}
}
}
}
sub _is
{
my($s1, $s2) = @_;
defined $s1 != defined $s2 and return 0;
ref $s2 and ($s1, $s2) = ($s2, $s1);
if (ref $s1) {
if (ref $s1 eq 'ARRAY') {
_is($_, $s2) and return 1 for @$s1;
return 0;
}
return $s2 =~ $s1;
}
return $s1 eq $s2;
}
sub _patch
{
my($patch) = @_;
print "patching $_\n" for $patch =~ /^\+{3}\s+(\S+)/gm;
my $diff = 'tmp.diff';
_write_or_die($diff, $patch);
_run_or_die("$patch_exe -f -s -p0 <$diff");
unlink $diff or die "unlink $diff: $!\n";
}
sub _write_or_die
{
my($file, $data) = @_;
my $fh = IO::File->new(">$file") or die "$file: $!\n";
$fh->print($data);
}
sub _run_or_die
{
# print "[running @_]\n";
die unless scalar run( command => [ @_ ], verbose => 1 );
}
sub _determine_version {
my ($source) = @_;
my $patchlevel_h = File::Spec->catfile($source, 'patchlevel.h');
return unless -e $patchlevel_h;
my $version;
{
open my $fh, '<', $patchlevel_h;
my @vers;
while (<$fh>) {
chomp;
next unless /^#define PERL_[RVS]/;
push @vers, (split /\s+/)[2];
}
$version = join '.', @vers;
}
return $version;
}
sub _patch_hints {
return unless my ($file,$data) = hint_file();
my $path = File::Spec->catfile( 'hints', $file );
chmod 0755, $path or die "$!\n";
open my $fh, '>', $path or die "$!\n";
print $fh $data;
close $fh;
return 1;
}
sub _patch_db
{
my $ver = shift;
print "patching ext/DB_File/DB_File.xs\n";
_run_or_die($^X, '-pi.bak', '-e', "s/<db.h>/<db$ver\\/db.h>/", 'ext/DB_File/DB_File.xs');
unlink 'ext/DB_File/DB_File.xs.bak' if -e 'ext/DB_File/DB_File.xs.bak';
}
sub _patch_doio
{
_patch(<<'END');
--- doio.c.org 2004-06-07 23:14:45.000000000 +0200
+++ doio.c 2003-11-04 08:03:03.000000000 +0100
@@ -75,6 +75,16 @@
# endif
#endif
+#if _SEM_SEMUN_UNDEFINED
+union semun
+{
+ int val;
+ struct semid_ds *buf;
+ unsigned short int *array;
+ struct seminfo *__buf;
+};
+#endif
+
bool
do_open(gv,name,len,as_raw,rawmode,rawperm,supplied_fp)
GV *gv;
END
}
sub _patch_sysv
{
my %opt = @_;
# check if patching is required
return if $^O ne 'linux' or -f '/usr/include/asm/page.h';
if ($opt{old_format}) {
_patch(<<'END');
--- ext/IPC/SysV/SysV.xs.org 1998-07-20 10:20:07.000000000 +0200
+++ ext/IPC/SysV/SysV.xs 2007-08-12 10:51:06.000000000 +0200
@@ -3,9 +3,6 @@
#include "XSUB.h"
#include <sys/types.h>
-#ifdef __linux__
-#include <asm/page.h>
-#endif
#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
#include <sys/ipc.h>
#ifdef HAS_MSG
END
}
else {
_patch(<<'END');
--- ext/IPC/SysV/SysV.xs.org 2007-08-11 00:12:46.000000000 +0200
+++ ext/IPC/SysV/SysV.xs 2007-08-11 00:10:51.000000000 +0200
@@ -3,9 +3,6 @@
#include "XSUB.h"
#include <sys/types.h>
-#ifdef __linux__
-# include <asm/page.h>
-#endif
#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
#ifndef HAS_SEM
# include <sys/ipc.h>
END
}
}
sub _patch_configure
{
_patch(<<'END');
--- Configure
+++ Configure
@@ -3380,6 +3380,18 @@
test "X$gfpthkeep" != Xy && gfpth=""
EOSC
+# gcc 3.1 complains about adding -Idirectories that it already knows about,
+# so we will take those off from locincpth.
+case "$gccversion" in
+3*)
+ echo "main(){}">try.c
+ for incdir in `$cc -v -c try.c 2>&1 | \
+ sed '1,/^#include <\.\.\.>/d;/^End of search list/,$d;s/^ //'` ; do
+ locincpth=`echo $locincpth | sed s!$incdir!!`
+ done
+ $rm -f try try.*
+esac
+
: What should the include directory be ?
echo " "
$echo $n "Hmm... $c"
END
}
sub _patch_makedepend_lc
{
_patch(<<'END');
--- makedepend.SH
+++ makedepend.SH
@@ -58,6 +58,10 @@ case $PERL_CONFIG_SH in
;;
esac
+# Avoid localized gcc/cc messages
+LC_ALL=C
+export LC_ALL
+
# We need .. when we are in the x2p directory if we are using the
# cppstdin wrapper script.
# Put .. and . first so that we pick up the present cppstdin, not
END
}
sub _patch_makedepend_SH
{
my $perl = shift;
SWITCH: {
# If 5.6.0
if ( $perl eq '5.6.0' ) {
_patch(<<'BADGER');
--- makedepend.SH.org 2000-03-02 18:12:26.000000000 +0000
+++ makedepend.SH 2010-09-01 10:13:37.000000000 +0100
@@ -1,5 +1,5 @@
#! /bin/sh
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -29,6 +29,13 @@
!GROK!THIS!
$spitshell >>makedepend <<'!NO!SUBS!'
+if test -d .depending; then
+ echo "$0: Already running, exiting."
+ exit 0
+fi
+
+mkdir .depending
+
# This script should be called with
# sh ./makedepend MAKE=$(MAKE)
case "$1" in
@@ -37,7 +44,7 @@
export PATH || (echo "OOPS, this isn't sh. Desperation time. I will feed myself to sh."; sh \$0; kill \$\$)
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -51,6 +58,11 @@
;;
esac
+# Avoid localized gcc messages
+case "$ccname" in
+ gcc) LC_ALL=C ; export LC_ALL ;;
+esac
+
# We need .. when we are in the x2p directory if we are using the
# cppstdin wrapper script.
# Put .. and . first so that we pick up the present cppstdin, not
@@ -58,6 +70,10 @@
PATH=".$path_sep..$path_sep$PATH"
export PATH
+case "$osname" in
+amigaos) cat=/bin/cat ;; # must be absolute
+esac
+
$cat /dev/null >.deptmp
$rm -f *.c.c c/*.c.c
if test -f Makefile; then
@@ -67,7 +83,6 @@
# to be out of date. I don't know if OS/2 has touch, so do this:
case "$osname" in
os2) ;;
- netbsd) ;;
*) $touch $firstmakefile ;;
esac
fi
@@ -99,25 +114,20 @@
$echo *.c | $tr ' ' $trnl | $egrep -v '\*' >.clist)
for file in `$cat .clist`; do
# for file in `cat /dev/null`; do
- if [ "$osname" = uwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g"
- else
- if [ "$osname" = os2 ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$archname" = cygwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- uwinfix=
- fi
- fi
- fi
+ case "$osname" in
+ uwin) uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g" ;;
+ os2) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ cygwin) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ posix-bc) uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/" ;;
+ vos) uwinfix="-e s/\#/\\\#/" ;;
+ *) uwinfix="" ;;
+ esac
case "$file" in
*.c) filebase=`basename $file .c` ;;
*.y) filebase=`basename $file .y` ;;
esac
case "$file" in
- */*) finc="-I`echo $file | sed 's#/[^/]*$##`" ;;
+ */*) finc="-I`echo $file | sed 's#/[^/]*$##'`" ;;
*) finc= ;;
esac
$echo "Finding dependencies for $filebase$_o."
@@ -130,22 +140,45 @@
-e 's|\\$||' \
-e p \
-e '}' ) >UU/$file.c
+
if [ "$osname" = os390 -a "$file" = perly.c ]; then
$echo '#endif' >>UU/$file.c
fi
- $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
- $sed \
- -e '1d' \
- -e '/^#.*<stdin>/d' \
- -e '/^#.*"-"/d' \
- -e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
- -e 's/^[ ]*#[ ]*line/#/' \
- -e '/^# *[0-9][0-9]* *[".\/]/!d' \
- -e 's/^.*"\(.*\)".*$/'$filebase'\$(OBJ_EXT): \1/' \
- -e 's/^# *[0-9][0-9]* \(.*\)$/'$filebase'\$(OBJ_EXT): \1/' \
- -e 's|: \./|: |' \
- -e 's|\.c\.c|.c|' $uwinfix | \
- $uniq | $sort | $uniq >> .deptmp
+
+ if [ "$osname" = os390 ]; then
+ $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
+ $sed \
+ -e '/^#.*<stdin>/d' \
+ -e '/^#.*"-"/d' \
+ -e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
+ -e 's/^[ ]*#[ ]*line/#/' \
+ -e '/^# *[0-9][0-9]* *[".\/]/!d' \
+ -e 's/^.*"\(.*\)".*$/'$filebase'\$(OBJ_EXT): \1/' \
+ -e 's/^# *[0-9][0-9]* \(.*\)$/'$filebase'\$(OBJ_EXT): \1/' \
+ -e 's|: \./|: |' \
+ -e 's|\.c\.c|.c|' $uwinfix | \
+ $uniq | $sort | $uniq >> .deptmp
+ else
+ $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c >.cout 2>.cerr
+ $sed \
+ -e '1d' \
+ -e '/^#.*<stdin>/d' \
+ -e '/^#.*<builtin>/d' \
+ -e '/^#.*<built-in>/d' \
+ -e '/^#.*<command line>/d' \
+ -e '/^#.*<command-line>/d' \
+ -e '/^#.*"-"/d' \
+ -e '/^#.*"\/.*\/"/d' \
+ -e '/: file path prefix .* never used$/d' \
+ -e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
+ -e 's/^[ ]*#[ ]*line/#/' \
+ -e '/^# *[0-9][0-9]* *[".\/]/!d' \
+ -e 's/^.*"\(.*\)".*$/'$filebase'\$(OBJ_EXT): \1/' \
+ -e 's/^# *[0-9][0-9]* \(.*\)$/'$filebase'\$(OBJ_EXT): \1/' \
+ -e 's|: \./|: |' \
+ -e 's|\.c\.c|.c|' $uwinfix .cout .cerr| \
+ $uniq | $sort | $uniq >> .deptmp
+ fi
done
$sed <$mf >$mf.new -e '1,/^# AUTOMATICALLY/!d'
@@ -177,6 +210,10 @@
$echo "Updating $mf..."
$echo "# If this runs make out of memory, delete /usr/include lines." \
>> $mf.new
+ if [ "$osname" = vos ]; then
+ $sed 's|.incl.c|.h|' .deptmp >.deptmp.vos
+ mv -f .deptmp.vos .deptmp
+ fi
$sed 's|^\(.*\$(OBJ_EXT):\) *\(.*/.*\.c\) *$|\1 \2; '"$defrule \2|" .deptmp \
>>$mf.new
else
@@ -208,7 +245,8 @@
$cp $mf.new $mf
$rm $mf.new
$echo "# WARNING: Put nothing here or make depend will gobble it up!" >> $mf
-$rm -rf .deptmp UU .shlist .clist .hlist .hsed
+$rm -rf .deptmp UU .shlist .clist .hlist .hsed .cout .cerr
+rmdir .depending
!NO!SUBS!
$eunicefix makedepend
BADGER
last SWITCH;
}
# If 5.6.1
if ( $perl eq '5.6.1' ) {
_patch(<<'BADGER');
--- makedepend.SH.org 2001-03-19 07:33:17.000000000 +0000
+++ makedepend.SH 2010-09-01 10:14:47.000000000 +0100
@@ -1,5 +1,5 @@
#! /bin/sh
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -29,6 +29,13 @@
!GROK!THIS!
$spitshell >>makedepend <<'!NO!SUBS!'
+if test -d .depending; then
+ echo "$0: Already running, exiting."
+ exit 0
+fi
+
+mkdir .depending
+
# This script should be called with
# sh ./makedepend MAKE=$(MAKE)
case "$1" in
@@ -37,7 +44,7 @@
export PATH || (echo "OOPS, this isn't sh. Desperation time. I will feed myself to sh."; sh \$0; kill \$\$)
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -51,6 +58,11 @@
;;
esac
+# Avoid localized gcc messages
+case "$ccname" in
+ gcc) LC_ALL=C ; export LC_ALL ;;
+esac
+
# We need .. when we are in the x2p directory if we are using the
# cppstdin wrapper script.
# Put .. and . first so that we pick up the present cppstdin, not
@@ -58,6 +70,10 @@
PATH=".$path_sep..$path_sep$PATH"
export PATH
+case "$osname" in
+amigaos) cat=/bin/cat ;; # must be absolute
+esac
+
$cat /dev/null >.deptmp
$rm -f *.c.c c/*.c.c
if test -f Makefile; then
@@ -67,7 +83,6 @@
# to be out of date. I don't know if OS/2 has touch, so do this:
case "$osname" in
os2) ;;
- netbsd) ;;
*) $touch $firstmakefile ;;
esac
fi
@@ -99,29 +114,20 @@
$echo *.c | $tr ' ' $trnl | $egrep -v '\*' >.clist)
for file in `$cat .clist`; do
# for file in `cat /dev/null`; do
- if [ "$osname" = uwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g"
- else
- if [ "$osname" = os2 ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$archname" = cygwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$osname" = posix-bc ]; then
- uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/"
- else
- uwinfix=
- fi
- fi
- fi
- fi
+ case "$osname" in
+ uwin) uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g" ;;
+ os2) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ cygwin) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ posix-bc) uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/" ;;
+ vos) uwinfix="-e s/\#/\\\#/" ;;
+ *) uwinfix="" ;;
+ esac
case "$file" in
*.c) filebase=`basename $file .c` ;;
*.y) filebase=`basename $file .y` ;;
esac
case "$file" in
- */*) finc="-I`echo $file | sed 's#/[^/]*$##`" ;;
+ */*) finc="-I`echo $file | sed 's#/[^/]*$##'`" ;;
*) finc= ;;
esac
$echo "Finding dependencies for $filebase$_o."
@@ -134,10 +140,12 @@
-e 's|\\$||' \
-e p \
-e '}' ) >UU/$file.c
+
+ if [ "$osname" = os390 -a "$file" = perly.c ]; then
+ $echo '#endif' >>UU/$file.c
+ fi
+
if [ "$osname" = os390 ]; then
- if [ "$file" = perly.c ]; then
- $echo '#endif' >>UU/$file.c
- fi
$cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
$sed \
-e '/^#.*<stdin>/d' \
@@ -151,18 +159,24 @@
-e 's|\.c\.c|.c|' $uwinfix | \
$uniq | $sort | $uniq >> .deptmp
else
- $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
+ $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c >.cout 2>.cerr
$sed \
-e '1d' \
-e '/^#.*<stdin>/d' \
+ -e '/^#.*<builtin>/d' \
+ -e '/^#.*<built-in>/d' \
+ -e '/^#.*<command line>/d' \
+ -e '/^#.*<command-line>/d' \
-e '/^#.*"-"/d' \
+ -e '/^#.*"\/.*\/"/d' \
+ -e '/: file path prefix .* never used$/d' \
-e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
-e 's/^[ ]*#[ ]*line/#/' \
-e '/^# *[0-9][0-9]* *[".\/]/!d' \
-e 's/^.*"\(.*\)".*$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's/^# *[0-9][0-9]* \(.*\)$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's|: \./|: |' \
- -e 's|\.c\.c|.c|' $uwinfix | \
+ -e 's|\.c\.c|.c|' $uwinfix .cout .cerr| \
$uniq | $sort | $uniq >> .deptmp
fi
done
@@ -196,6 +210,10 @@
$echo "Updating $mf..."
$echo "# If this runs make out of memory, delete /usr/include lines." \
>> $mf.new
+ if [ "$osname" = vos ]; then
+ $sed 's|.incl.c|.h|' .deptmp >.deptmp.vos
+ mv -f .deptmp.vos .deptmp
+ fi
$sed 's|^\(.*\$(OBJ_EXT):\) *\(.*/.*\.c\) *$|\1 \2; '"$defrule \2|" .deptmp \
>>$mf.new
else
@@ -227,7 +245,8 @@
$cp $mf.new $mf
$rm $mf.new
$echo "# WARNING: Put nothing here or make depend will gobble it up!" >> $mf
-$rm -rf .deptmp UU .shlist .clist .hlist .hsed
+$rm -rf .deptmp UU .shlist .clist .hlist .hsed .cout .cerr
+rmdir .depending
!NO!SUBS!
$eunicefix makedepend
BADGER
last SWITCH;
}
# If 5.6.2
if ( $perl eq '5.6.2' ) {
_patch(<<'BADGER');
--- makedepend.SH.org 2003-07-30 23:46:59.000000000 +0100
+++ makedepend.SH 2010-09-01 10:15:47.000000000 +0100
@@ -1,5 +1,5 @@
#! /bin/sh
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -29,6 +29,13 @@
!GROK!THIS!
$spitshell >>makedepend <<'!NO!SUBS!'
+if test -d .depending; then
+ echo "$0: Already running, exiting."
+ exit 0
+fi
+
+mkdir .depending
+
# This script should be called with
# sh ./makedepend MAKE=$(MAKE)
case "$1" in
@@ -37,7 +44,7 @@
export PATH || (echo "OOPS, this isn't sh. Desperation time. I will feed myself to sh."; sh \$0; kill \$\$)
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -63,6 +70,10 @@
PATH=".$path_sep..$path_sep$PATH"
export PATH
+case "$osname" in
+amigaos) cat=/bin/cat ;; # must be absolute
+esac
+
$cat /dev/null >.deptmp
$rm -f *.c.c c/*.c.c
if test -f Makefile; then
@@ -72,7 +83,6 @@
# to be out of date. I don't know if OS/2 has touch, so do this:
case "$osname" in
os2) ;;
- netbsd) ;;
*) $touch $firstmakefile ;;
esac
fi
@@ -104,29 +114,20 @@
$echo *.c | $tr ' ' $trnl | $egrep -v '\*' >.clist)
for file in `$cat .clist`; do
# for file in `cat /dev/null`; do
- if [ "$osname" = uwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g"
- else
- if [ "$osname" = os2 ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$archname" = cygwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$osname" = posix-bc ]; then
- uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/"
- else
- uwinfix=
- fi
- fi
- fi
- fi
+ case "$osname" in
+ uwin) uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g" ;;
+ os2) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ cygwin) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ posix-bc) uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/" ;;
+ vos) uwinfix="-e s/\#/\\\#/" ;;
+ *) uwinfix="" ;;
+ esac
case "$file" in
*.c) filebase=`basename $file .c` ;;
*.y) filebase=`basename $file .y` ;;
esac
case "$file" in
- */*) finc="-I`echo $file | sed 's#/[^/]*$##`" ;;
+ */*) finc="-I`echo $file | sed 's#/[^/]*$##'`" ;;
*) finc= ;;
esac
$echo "Finding dependencies for $filebase$_o."
@@ -139,10 +140,12 @@
-e 's|\\$||' \
-e p \
-e '}' ) >UU/$file.c
+
+ if [ "$osname" = os390 -a "$file" = perly.c ]; then
+ $echo '#endif' >>UU/$file.c
+ fi
+
if [ "$osname" = os390 ]; then
- if [ "$file" = perly.c ]; then
- $echo '#endif' >>UU/$file.c
- fi
$cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
$sed \
-e '/^#.*<stdin>/d' \
@@ -156,21 +159,24 @@
-e 's|\.c\.c|.c|' $uwinfix | \
$uniq | $sort | $uniq >> .deptmp
else
- $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
+ $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c >.cout 2>.cerr
$sed \
-e '1d' \
-e '/^#.*<stdin>/d' \
- -e '/^#.*<builtin>/d' \
- -e '/^#.*<built-in>/d' \
- -e '/^#.*<command line>/d' \
+ -e '/^#.*<builtin>/d' \
+ -e '/^#.*<built-in>/d' \
+ -e '/^#.*<command line>/d' \
+ -e '/^#.*<command-line>/d' \
-e '/^#.*"-"/d' \
+ -e '/^#.*"\/.*\/"/d' \
+ -e '/: file path prefix .* never used$/d' \
-e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
-e 's/^[ ]*#[ ]*line/#/' \
-e '/^# *[0-9][0-9]* *[".\/]/!d' \
-e 's/^.*"\(.*\)".*$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's/^# *[0-9][0-9]* \(.*\)$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's|: \./|: |' \
- -e 's|\.c\.c|.c|' $uwinfix | \
+ -e 's|\.c\.c|.c|' $uwinfix .cout .cerr| \
$uniq | $sort | $uniq >> .deptmp
fi
done
@@ -204,6 +210,10 @@
$echo "Updating $mf..."
$echo "# If this runs make out of memory, delete /usr/include lines." \
>> $mf.new
+ if [ "$osname" = vos ]; then
+ $sed 's|.incl.c|.h|' .deptmp >.deptmp.vos
+ mv -f .deptmp.vos .deptmp
+ fi
$sed 's|^\(.*\$(OBJ_EXT):\) *\(.*/.*\.c\) *$|\1 \2; '"$defrule \2|" .deptmp \
>>$mf.new
else
@@ -235,7 +245,8 @@
$cp $mf.new $mf
$rm $mf.new
$echo "# WARNING: Put nothing here or make depend will gobble it up!" >> $mf
-$rm -rf .deptmp UU .shlist .clist .hlist .hsed
+$rm -rf .deptmp UU .shlist .clist .hlist .hsed .cout .cerr
+rmdir .depending
!NO!SUBS!
$eunicefix makedepend
BADGER
last SWITCH;
}
# If 5.7.0
if ( $perl eq '5.7.0' ) {
_patch(<<'BADGER');
--- makedepend.SH.org 2000-08-13 19:35:04.000000000 +0100
+++ makedepend.SH 2010-09-01 10:47:14.000000000 +0100
@@ -1,5 +1,5 @@
#! /bin/sh
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -29,6 +29,13 @@
!GROK!THIS!
$spitshell >>makedepend <<'!NO!SUBS!'
+if test -d .depending; then
+ echo "$0: Already running, exiting."
+ exit 0
+fi
+
+mkdir .depending
+
# This script should be called with
# sh ./makedepend MAKE=$(MAKE)
case "$1" in
@@ -37,7 +44,7 @@
export PATH || (echo "OOPS, this isn't sh. Desperation time. I will feed myself to sh."; sh \$0; kill \$\$)
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -51,6 +58,11 @@
;;
esac
+# Avoid localized gcc messages
+case "$ccname" in
+ gcc) LC_ALL=C ; export LC_ALL ;;
+esac
+
# We need .. when we are in the x2p directory if we are using the
# cppstdin wrapper script.
# Put .. and . first so that we pick up the present cppstdin, not
@@ -58,6 +70,10 @@
PATH=".$path_sep..$path_sep$PATH"
export PATH
+case "$osname" in
+amigaos) cat=/bin/cat ;; # must be absolute
+esac
+
$cat /dev/null >.deptmp
$rm -f *.c.c c/*.c.c
if test -f Makefile; then
@@ -67,7 +83,6 @@
# to be out of date. I don't know if OS/2 has touch, so do this:
case "$osname" in
os2) ;;
- netbsd) ;;
*) $touch $firstmakefile ;;
esac
fi
@@ -99,25 +114,20 @@
$echo *.c | $tr ' ' $trnl | $egrep -v '\*' >.clist)
for file in `$cat .clist`; do
# for file in `cat /dev/null`; do
- if [ "$osname" = uwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g"
- else
- if [ "$osname" = os2 ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$archname" = cygwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- uwinfix=
- fi
- fi
- fi
+ case "$osname" in
+ uwin) uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g" ;;
+ os2) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ cygwin) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ posix-bc) uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/" ;;
+ vos) uwinfix="-e s/\#/\\\#/" ;;
+ *) uwinfix="" ;;
+ esac
case "$file" in
*.c) filebase=`basename $file .c` ;;
*.y) filebase=`basename $file .y` ;;
esac
case "$file" in
- */*) finc="-I`echo $file | sed 's#/[^/]*$##`" ;;
+ */*) finc="-I`echo $file | sed 's#/[^/]*$##'`" ;;
*) finc= ;;
esac
$echo "Finding dependencies for $filebase$_o."
@@ -130,10 +140,12 @@
-e 's|\\$||' \
-e p \
-e '}' ) >UU/$file.c
+
+ if [ "$osname" = os390 -a "$file" = perly.c ]; then
+ $echo '#endif' >>UU/$file.c
+ fi
+
if [ "$osname" = os390 ]; then
- if [ "$file" = perly.c ]; then
- $echo '#endif' >>UU/$file.c
- fi
$cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
$sed \
-e '/^#.*<stdin>/d' \
@@ -147,18 +159,24 @@
-e 's|\.c\.c|.c|' $uwinfix | \
$uniq | $sort | $uniq >> .deptmp
else
- $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
+ $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c >.cout 2>.cerr
$sed \
-e '1d' \
-e '/^#.*<stdin>/d' \
+ -e '/^#.*<builtin>/d' \
+ -e '/^#.*<built-in>/d' \
+ -e '/^#.*<command line>/d' \
+ -e '/^#.*<command-line>/d' \
-e '/^#.*"-"/d' \
+ -e '/^#.*"\/.*\/"/d' \
+ -e '/: file path prefix .* never used$/d' \
-e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
-e 's/^[ ]*#[ ]*line/#/' \
-e '/^# *[0-9][0-9]* *[".\/]/!d' \
-e 's/^.*"\(.*\)".*$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's/^# *[0-9][0-9]* \(.*\)$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's|: \./|: |' \
- -e 's|\.c\.c|.c|' $uwinfix | \
+ -e 's|\.c\.c|.c|' $uwinfix .cout .cerr| \
$uniq | $sort | $uniq >> .deptmp
fi
done
@@ -192,6 +210,10 @@
$echo "Updating $mf..."
$echo "# If this runs make out of memory, delete /usr/include lines." \
>> $mf.new
+ if [ "$osname" = vos ]; then
+ $sed 's|.incl.c|.h|' .deptmp >.deptmp.vos
+ mv -f .deptmp.vos .deptmp
+ fi
$sed 's|^\(.*\$(OBJ_EXT):\) *\(.*/.*\.c\) *$|\1 \2; '"$defrule \2|" .deptmp \
>>$mf.new
else
@@ -223,7 +245,8 @@
$cp $mf.new $mf
$rm $mf.new
$echo "# WARNING: Put nothing here or make depend will gobble it up!" >> $mf
-$rm -rf .deptmp UU .shlist .clist .hlist .hsed
+$rm -rf .deptmp UU .shlist .clist .hlist .hsed .cout .cerr
+rmdir .depending
!NO!SUBS!
$eunicefix makedepend
BADGER
last SWITCH;
}
# If 5.7.1
if ( $perl eq '5.7.1' ) {
_patch(<<'BADGER');
--- makedepend.SH.org 2001-03-11 16:30:08.000000000 +0000
+++ makedepend.SH 2010-09-01 10:44:54.000000000 +0100
@@ -1,5 +1,5 @@
#! /bin/sh
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -29,6 +29,13 @@
!GROK!THIS!
$spitshell >>makedepend <<'!NO!SUBS!'
+if test -d .depending; then
+ echo "$0: Already running, exiting."
+ exit 0
+fi
+
+mkdir .depending
+
# This script should be called with
# sh ./makedepend MAKE=$(MAKE)
case "$1" in
@@ -37,7 +44,7 @@
export PATH || (echo "OOPS, this isn't sh. Desperation time. I will feed myself to sh."; sh \$0; kill \$\$)
-case $CONFIGDOTSH in
+case $PERL_CONFIG_SH in
'')
if test -f config.sh; then TOP=.;
elif test -f ../config.sh; then TOP=..;
@@ -51,6 +58,11 @@
;;
esac
+# Avoid localized gcc messages
+case "$ccname" in
+ gcc) LC_ALL=C ; export LC_ALL ;;
+esac
+
# We need .. when we are in the x2p directory if we are using the
# cppstdin wrapper script.
# Put .. and . first so that we pick up the present cppstdin, not
@@ -58,6 +70,10 @@
PATH=".$path_sep..$path_sep$PATH"
export PATH
+case "$osname" in
+amigaos) cat=/bin/cat ;; # must be absolute
+esac
+
$cat /dev/null >.deptmp
$rm -f *.c.c c/*.c.c
if test -f Makefile; then
@@ -67,7 +83,6 @@
# to be out of date. I don't know if OS/2 has touch, so do this:
case "$osname" in
os2) ;;
- netbsd) ;;
*) $touch $firstmakefile ;;
esac
fi
@@ -99,29 +114,20 @@
$echo *.c | $tr ' ' $trnl | $egrep -v '\*' >.clist)
for file in `$cat .clist`; do
# for file in `cat /dev/null`; do
- if [ "$osname" = uwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g"
- else
- if [ "$osname" = os2 ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$archname" = cygwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$osname" = posix-bc ]; then
- uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/"
- else
- uwinfix=
- fi
- fi
- fi
- fi
+ case "$osname" in
+ uwin) uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g" ;;
+ os2) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ cygwin) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ posix-bc) uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/" ;;
+ vos) uwinfix="-e s/\#/\\\#/" ;;
+ *) uwinfix="" ;;
+ esac
case "$file" in
*.c) filebase=`basename $file .c` ;;
*.y) filebase=`basename $file .y` ;;
esac
case "$file" in
- */*) finc="-I`echo $file | sed 's#/[^/]*$##`" ;;
+ */*) finc="-I`echo $file | sed 's#/[^/]*$##'`" ;;
*) finc= ;;
esac
$echo "Finding dependencies for $filebase$_o."
@@ -134,10 +140,12 @@
-e 's|\\$||' \
-e p \
-e '}' ) >UU/$file.c
+
+ if [ "$osname" = os390 -a "$file" = perly.c ]; then
+ $echo '#endif' >>UU/$file.c
+ fi
+
if [ "$osname" = os390 ]; then
- if [ "$file" = perly.c ]; then
- $echo '#endif' >>UU/$file.c
- fi
$cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
$sed \
-e '/^#.*<stdin>/d' \
@@ -151,18 +159,24 @@
-e 's|\.c\.c|.c|' $uwinfix | \
$uniq | $sort | $uniq >> .deptmp
else
- $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
+ $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c >.cout 2>.cerr
$sed \
-e '1d' \
-e '/^#.*<stdin>/d' \
+ -e '/^#.*<builtin>/d' \
+ -e '/^#.*<built-in>/d' \
+ -e '/^#.*<command line>/d' \
+ -e '/^#.*<command-line>/d' \
-e '/^#.*"-"/d' \
+ -e '/^#.*"\/.*\/"/d' \
+ -e '/: file path prefix .* never used$/d' \
-e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
-e 's/^[ ]*#[ ]*line/#/' \
-e '/^# *[0-9][0-9]* *[".\/]/!d' \
-e 's/^.*"\(.*\)".*$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's/^# *[0-9][0-9]* \(.*\)$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's|: \./|: |' \
- -e 's|\.c\.c|.c|' $uwinfix | \
+ -e 's|\.c\.c|.c|' $uwinfix .cout .cerr| \
$uniq | $sort | $uniq >> .deptmp
fi
done
@@ -196,6 +210,10 @@
$echo "Updating $mf..."
$echo "# If this runs make out of memory, delete /usr/include lines." \
>> $mf.new
+ if [ "$osname" = vos ]; then
+ $sed 's|.incl.c|.h|' .deptmp >.deptmp.vos
+ mv -f .deptmp.vos .deptmp
+ fi
$sed 's|^\(.*\$(OBJ_EXT):\) *\(.*/.*\.c\) *$|\1 \2; '"$defrule \2|" .deptmp \
>>$mf.new
else
@@ -227,7 +245,8 @@
$cp $mf.new $mf
$rm $mf.new
$echo "# WARNING: Put nothing here or make depend will gobble it up!" >> $mf
-$rm -rf .deptmp UU .shlist .clist .hlist .hsed
+$rm -rf .deptmp UU .shlist .clist .hlist .hsed .cout .cerr
+rmdir .depending
!NO!SUBS!
$eunicefix makedepend
BADGER
last SWITCH;
}
# If 5.7.2
if ( $perl eq '5.7.2' ) {
_patch(<<'BADGER');
--- makedepend.SH.org 2001-07-09 15:11:05.000000000 +0100
+++ makedepend.SH 2010-09-01 10:45:32.000000000 +0100
@@ -18,10 +18,6 @@
*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
esac
-case "$osname" in
-amigaos) cat=/bin/cat ;; # must be absolute
-esac
-
echo "Extracting makedepend (with variable substitutions)"
rm -f makedepend
$spitshell >makedepend <<!GROK!THIS!
@@ -33,6 +29,13 @@
!GROK!THIS!
$spitshell >>makedepend <<'!NO!SUBS!'
+if test -d .depending; then
+ echo "$0: Already running, exiting."
+ exit 0
+fi
+
+mkdir .depending
+
# This script should be called with
# sh ./makedepend MAKE=$(MAKE)
case "$1" in
@@ -55,6 +58,11 @@
;;
esac
+# Avoid localized gcc messages
+case "$ccname" in
+ gcc) LC_ALL=C ; export LC_ALL ;;
+esac
+
# We need .. when we are in the x2p directory if we are using the
# cppstdin wrapper script.
# Put .. and . first so that we pick up the present cppstdin, not
@@ -62,6 +70,10 @@
PATH=".$path_sep..$path_sep$PATH"
export PATH
+case "$osname" in
+amigaos) cat=/bin/cat ;; # must be absolute
+esac
+
$cat /dev/null >.deptmp
$rm -f *.c.c c/*.c.c
if test -f Makefile; then
@@ -71,7 +83,6 @@
# to be out of date. I don't know if OS/2 has touch, so do this:
case "$osname" in
os2) ;;
- netbsd) ;;
*) $touch $firstmakefile ;;
esac
fi
@@ -103,29 +114,20 @@
$echo *.c | $tr ' ' $trnl | $egrep -v '\*' >.clist)
for file in `$cat .clist`; do
# for file in `cat /dev/null`; do
- if [ "$osname" = uwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g"
- else
- if [ "$osname" = os2 ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$archname" = cygwin ]; then
- uwinfix="-e s,\\\\\\\\,/,g"
- else
- if [ "$osname" = posix-bc ]; then
- uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/"
- else
- uwinfix=
- fi
- fi
- fi
- fi
+ case "$osname" in
+ uwin) uwinfix="-e s,\\\\\\\\,/,g -e s,\\([a-zA-Z]\\):/,/\\1/,g" ;;
+ os2) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ cygwin) uwinfix="-e s,\\\\\\\\,/,g" ;;
+ posix-bc) uwinfix="-e s/\\*POSIX(\\(.*\\))/\\1/" ;;
+ vos) uwinfix="-e s/\#/\\\#/" ;;
+ *) uwinfix="" ;;
+ esac
case "$file" in
*.c) filebase=`basename $file .c` ;;
*.y) filebase=`basename $file .y` ;;
esac
case "$file" in
- */*) finc="-I`echo $file | sed 's#/[^/]*$##`" ;;
+ */*) finc="-I`echo $file | sed 's#/[^/]*$##'`" ;;
*) finc= ;;
esac
$echo "Finding dependencies for $filebase$_o."
@@ -138,10 +140,12 @@
-e 's|\\$||' \
-e p \
-e '}' ) >UU/$file.c
+
+ if [ "$osname" = os390 -a "$file" = perly.c ]; then
+ $echo '#endif' >>UU/$file.c
+ fi
+
if [ "$osname" = os390 ]; then
- if [ "$file" = perly.c ]; then
- $echo '#endif' >>UU/$file.c
- fi
$cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
$sed \
-e '/^#.*<stdin>/d' \
@@ -155,18 +159,24 @@
-e 's|\.c\.c|.c|' $uwinfix | \
$uniq | $sort | $uniq >> .deptmp
else
- $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
+ $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c >.cout 2>.cerr
$sed \
-e '1d' \
-e '/^#.*<stdin>/d' \
+ -e '/^#.*<builtin>/d' \
+ -e '/^#.*<built-in>/d' \
+ -e '/^#.*<command line>/d' \
+ -e '/^#.*<command-line>/d' \
-e '/^#.*"-"/d' \
+ -e '/^#.*"\/.*\/"/d' \
+ -e '/: file path prefix .* never used$/d' \
-e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
-e 's/^[ ]*#[ ]*line/#/' \
-e '/^# *[0-9][0-9]* *[".\/]/!d' \
-e 's/^.*"\(.*\)".*$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's/^# *[0-9][0-9]* \(.*\)$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's|: \./|: |' \
- -e 's|\.c\.c|.c|' $uwinfix | \
+ -e 's|\.c\.c|.c|' $uwinfix .cout .cerr| \
$uniq | $sort | $uniq >> .deptmp
fi
done
@@ -200,6 +210,10 @@
$echo "Updating $mf..."
$echo "# If this runs make out of memory, delete /usr/include lines." \
>> $mf.new
+ if [ "$osname" = vos ]; then
+ $sed 's|.incl.c|.h|' .deptmp >.deptmp.vos
+ mv -f .deptmp.vos .deptmp
+ fi
$sed 's|^\(.*\$(OBJ_EXT):\) *\(.*/.*\.c\) *$|\1 \2; '"$defrule \2|" .deptmp \
>>$mf.new
else
@@ -231,7 +245,8 @@
$cp $mf.new $mf
$rm $mf.new
$echo "# WARNING: Put nothing here or make depend will gobble it up!" >> $mf
-$rm -rf .deptmp UU .shlist .clist .hlist .hsed
+$rm -rf .deptmp UU .shlist .clist .hlist .hsed .cout .cerr
+rmdir .depending
!NO!SUBS!
$eunicefix makedepend
BADGER
last SWITCH;
}
# If 5.7.3
if ( $perl eq '5.7.3' ) {
_patch(<<'BADGER');
--- makedepend.SH.org 2002-03-05 01:10:22.000000000 +0000
+++ makedepend.SH 2010-09-01 10:46:13.000000000 +0100
@@ -18,10 +18,6 @@
*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
esac
-case "$osname" in
-amigaos) cat=/bin/cat ;; # must be absolute
-esac
-
echo "Extracting makedepend (with variable substitutions)"
rm -f makedepend
$spitshell >makedepend <<!GROK!THIS!
@@ -33,6 +29,13 @@
!GROK!THIS!
$spitshell >>makedepend <<'!NO!SUBS!'
+if test -d .depending; then
+ echo "$0: Already running, exiting."
+ exit 0
+fi
+
+mkdir .depending
+
# This script should be called with
# sh ./makedepend MAKE=$(MAKE)
case "$1" in
@@ -55,6 +58,11 @@
;;
esac
+# Avoid localized gcc messages
+case "$ccname" in
+ gcc) LC_ALL=C ; export LC_ALL ;;
+esac
+
# We need .. when we are in the x2p directory if we are using the
# cppstdin wrapper script.
# Put .. and . first so that we pick up the present cppstdin, not
@@ -62,6 +70,10 @@
PATH=".$path_sep..$path_sep$PATH"
export PATH
+case "$osname" in
+amigaos) cat=/bin/cat ;; # must be absolute
+esac
+
$cat /dev/null >.deptmp
$rm -f *.c.c c/*.c.c
if test -f Makefile; then
@@ -71,7 +83,6 @@
# to be out of date. I don't know if OS/2 has touch, so do this:
case "$osname" in
os2) ;;
- netbsd) ;;
*) $touch $firstmakefile ;;
esac
fi
@@ -116,7 +127,7 @@
*.y) filebase=`basename $file .y` ;;
esac
case "$file" in
- */*) finc="-I`echo $file | sed 's#/[^/]*$##`" ;;
+ */*) finc="-I`echo $file | sed 's#/[^/]*$##'`" ;;
*) finc= ;;
esac
$echo "Finding dependencies for $filebase$_o."
@@ -129,6 +140,11 @@
-e 's|\\$||' \
-e p \
-e '}' ) >UU/$file.c
+
+ if [ "$osname" = os390 -a "$file" = perly.c ]; then
+ $echo '#endif' >>UU/$file.c
+ fi
+
if [ "$osname" = os390 ]; then
$cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
$sed \
@@ -143,13 +159,16 @@
-e 's|\.c\.c|.c|' $uwinfix | \
$uniq | $sort | $uniq >> .deptmp
else
- $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c 2>&1 |
+ $cppstdin $finc -I. $cppflags $cppminus <UU/$file.c >.cout 2>.cerr
$sed \
-e '1d' \
-e '/^#.*<stdin>/d' \
-e '/^#.*<builtin>/d' \
+ -e '/^#.*<built-in>/d' \
-e '/^#.*<command line>/d' \
+ -e '/^#.*<command-line>/d' \
-e '/^#.*"-"/d' \
+ -e '/^#.*"\/.*\/"/d' \
-e '/: file path prefix .* never used$/d' \
-e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
-e 's/^[ ]*#[ ]*line/#/' \
@@ -157,7 +176,7 @@
-e 's/^.*"\(.*\)".*$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's/^# *[0-9][0-9]* \(.*\)$/'$filebase'\$(OBJ_EXT): \1/' \
-e 's|: \./|: |' \
- -e 's|\.c\.c|.c|' $uwinfix | \
+ -e 's|\.c\.c|.c|' $uwinfix .cout .cerr| \
$uniq | $sort | $uniq >> .deptmp
fi
done
@@ -191,6 +210,10 @@
$echo "Updating $mf..."
$echo "# If this runs make out of memory, delete /usr/include lines." \
>> $mf.new
+ if [ "$osname" = vos ]; then
+ $sed 's|.incl.c|.h|' .deptmp >.deptmp.vos
+ mv -f .deptmp.vos .deptmp
+ fi
$sed 's|^\(.*\$(OBJ_EXT):\) *\(.*/.*\.c\) *$|\1 \2; '"$defrule \2|" .deptmp \
>>$mf.new
else
@@ -222,7 +245,8 @@
$cp $mf.new $mf
$rm $mf.new
$echo "# WARNING: Put nothing here or make depend will gobble it up!" >> $mf
-$rm -rf .deptmp UU .shlist .clist .hlist .hsed
+$rm -rf .deptmp UU .shlist .clist .hlist .hsed .cout .cerr
+rmdir .depending
!NO!SUBS!
$eunicefix makedepend
BADGER
last SWITCH;
}
# If 5.8.0
if ( $perl eq '5.8.0' ) {
_patch(<<'BADGER');
--- makedepend.SH.org 2002-07-09 15:06:42.000000000 +0100
+++ makedepend.SH 2010-09-01 10:16:37.000000000 +0100
@@ -58,6 +58,11 @@
;;
esac
+# Avoid localized gcc messages
+case "$ccname" in
+ gcc) LC_ALL=C ; export LC_ALL ;;
+esac
+
# We need .. when we are in the x2p directory if we are using the
# cppstdin wrapper script.
# Put .. and . first so that we pick up the present cppstdin, not
@@ -78,7 +83,6 @@
# to be out of date. I don't know if OS/2 has touch, so do this:
case "$osname" in
os2) ;;
- netbsd) ;;
*) $touch $firstmakefile ;;
esac
fi
@@ -123,7 +127,7 @@
*.y) filebase=`basename $file .y` ;;
esac
case "$file" in
- */*) finc="-I`echo $file | sed 's#/[^/]*$##`" ;;
+ */*) finc="-I`echo $file | sed 's#/[^/]*$##'`" ;;
*) finc= ;;
esac
$echo "Finding dependencies for $filebase$_o."
@@ -136,6 +140,11 @@
-e 's|\\$||' \
-e p \
-e '}' ) >UU/$file.c
+
+ if [ "$osname" = os390 -a "$file" = perly.c ]; then
+ $echo '#endif' >>UU/$file.c
+ fi
+
if [ "$osname" = os390 ]; then
$cppstdin $finc -I. $cppflags $cppminus <UU/$file.c |
$sed \
@@ -157,7 +166,9 @@
-e '/^#.*<builtin>/d' \
-e '/^#.*<built-in>/d' \
-e '/^#.*<command line>/d' \
+ -e '/^#.*<command-line>/d' \
-e '/^#.*"-"/d' \
+ -e '/^#.*"\/.*\/"/d' \
-e '/: file path prefix .* never used$/d' \
-e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
-e 's/^[ ]*#[ ]*line/#/' \
@@ -199,6 +210,10 @@
$echo "Updating $mf..."
$echo "# If this runs make out of memory, delete /usr/include lines." \
>> $mf.new
+ if [ "$osname" = vos ]; then
+ $sed 's|.incl.c|.h|' .deptmp >.deptmp.vos
+ mv -f .deptmp.vos .deptmp
+ fi
$sed 's|^\(.*\$(OBJ_EXT):\) *\(.*/.*\.c\) *$|\1 \2; '"$defrule \2|" .deptmp \
>>$mf.new
else
BADGER
last SWITCH;
}
# If 5.8.[12345678]
_patch(<<'BADGER');
--- makedepend.SH.org 2003-06-05 19:11:10.000000000 +0100
+++ makedepend.SH 2010-09-01 10:24:39.000000000 +0100
@@ -83,7 +83,6 @@
# to be out of date. I don't know if OS/2 has touch, so do this:
case "$osname" in
os2) ;;
- netbsd) ;;
*) $touch $firstmakefile ;;
esac
fi
@@ -128,7 +127,7 @@
*.y) filebase=`basename $file .y` ;;
esac
case "$file" in
- */*) finc="-I`echo $file | sed 's#/[^/]*$##`" ;;
+ */*) finc="-I`echo $file | sed 's#/[^/]*$##'`" ;;
*) finc= ;;
esac
$echo "Finding dependencies for $filebase$_o."
@@ -167,7 +166,9 @@
-e '/^#.*<builtin>/d' \
-e '/^#.*<built-in>/d' \
-e '/^#.*<command line>/d' \
+ -e '/^#.*<command-line>/d' \
-e '/^#.*"-"/d' \
+ -e '/^#.*"\/.*\/"/d' \
-e '/: file path prefix .* never used$/d' \
-e 's#\.[0-9][0-9]*\.c#'"$file.c#" \
-e 's/^[ ]*#[ ]*line/#/' \
@@ -209,6 +210,10 @@
$echo "Updating $mf..."
$echo "# If this runs make out of memory, delete /usr/include lines." \
>> $mf.new
+ if [ "$osname" = vos ]; then
+ $sed 's|.incl.c|.h|' .deptmp >.deptmp.vos
+ mv -f .deptmp.vos .deptmp
+ fi
$sed 's|^\(.*\$(OBJ_EXT):\) *\(.*/.*\.c\) *$|\1 \2; '"$defrule \2|" .deptmp \
>>$mf.new
else
BADGER
}
}
sub _patch_archive_tar_tests
{
my $perl = shift;
if ($perl =~ /^5\.10/) {
_patch(<<'END');
--- lib/Archive/Tar/t/02_methods.t
+++ lib/Archive/Tar/t/02_methods.t
@@ -70,6 +70,20 @@ my $LONG_FILE = qq[directory/really-really-really-really-really-really-really-re
my $TOO_LONG = ($^O eq 'MSWin32' or $^O eq 'cygwin' or $^O eq 'VMS')
&& length( cwd(). $LONG_FILE ) > 247;
+if(!$TOO_LONG) {
+ my $alt = File::Spec->catfile( cwd(), $LONG_FILE);
+ eval 'mkpath([$alt]);';
+ if($@)
+ {
+ $TOO_LONG = 1;
+ }
+ else
+ {
+ $@ = '';
+ my $base = File::Spec->catfile( cwd(), 'directory');
+ rmtree $base;
+ }
+}
### warn if we are going to skip long file names
if ($TOO_LONG) {
diag("No long filename support - long filename extraction disabled") if ! $ENV{PERL_CORE};
END
}
else {
_patch(<<'END');
--- cpan/Archive-Tar/t/02_methods.t
+++ cpan/Archive-Tar/t/02_methods.t
@@ -70,6 +70,20 @@ my $LONG_FILE = qq[directory/really-really-really-really-really-really-really-re
my $TOO_LONG = ($^O eq 'MSWin32' or $^O eq 'cygwin' or $^O eq 'VMS')
&& length( cwd(). $LONG_FILE ) > 247;
+if(!$TOO_LONG) {
+ my $alt = File::Spec->catfile( cwd(), $LONG_FILE);
+ eval 'mkpath([$alt]);';
+ if($@)
+ {
+ $TOO_LONG = 1;
+ }
+ else
+ {
+ $@ = '';
+ my $base = File::Spec->catfile( cwd(), 'directory');
+ rmtree $base;
+ }
+}
### warn if we are going to skip long file names
if ($TOO_LONG) {
diag("No long filename support - long filename extraction disabled") if ! $ENV{PERL_CORE};
END
}
}
qq[patchin'];
__END__
=pod
=head1 NAME
Devel::PatchPerl - Patch perl source a la Devel::PPort's buildperl.pl
=head1 VERSION
version 0.32
=head1 SYNOPSIS
use strict;
use warnings;
use Devel::PatchPerl;
Devel::PatchPerl->patch_source( '5.6.1', '/path/to/untarred/perl/source/perl-5.6.1' );
=head1 DESCRIPTION
Devel::PatchPerl is a modularisation of the patching code contained in L<Devel::PPort>'s
C<buildperl.pl>.
It does not build perls, it merely provides an interface to the source patching
functionality.
=head1 FUNCTION
=over
=item C<patch_source>
Takes two parameters, a C<perl> version and the path to unwrapped perl source for that version.
It dies on any errors.
If you don't supply a C<perl> version, it will attempt to auto-determine the
C<perl> version from the specified path.
If you don't supply the path to unwrapped perl source, it will assume the
current working directory.
=back
=head1 SEE ALSO
L<Devel::PPPort>
=head1 AUTHOR
Chris Williams <chris@bingosnet.co.uk>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011 by Chris Williams and Marcus Holland-Moritz.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
DEVEL_PATCHPERL
$fatpacked{"Devel/PatchPerl/Hints.pm"} = <<'DEVEL_PATCHPERL_HINTS';
package Devel::PatchPerl::Hints;
BEGIN {
$Devel::PatchPerl::Hints::VERSION = '0.32';
}
#ABSTRACT: replacement 'hints' files
use strict;
use warnings;
use MIME::Base64 qw[decode_base64];
use File::Spec;
our @ISA = qw[Exporter];
our @EXPORT_OK = qw[hint_file];
my %hints = (
'netbsd' =>
'IyBoaW50cy9uZXRic2Quc2gKIwojIFBsZWFzZSBjaGVjayB3aXRoIHBhY2thZ2VzQG5ldGJzZC5v
cmcgYmVmb3JlIG1ha2luZyBtb2RpZmljYXRpb25zCiMgdG8gdGhpcyBmaWxlLgoKY2FzZSAiJGFy
Y2huYW1lIiBpbgonJykKICAgIGFyY2huYW1lPWB1bmFtZSAtbWAtJHtvc25hbWV9CiAgICA7Owpl
c2FjCgojIE5ldEJTRCBrZWVwcyBkeW5hbWljIGxvYWRpbmcgZGwqKCkgZnVuY3Rpb25zIGluIC91
c3IvbGliL2NydDAubywKIyBzbyBDb25maWd1cmUgZG9lc24ndCBmaW5kIHRoZW0gKHVubGVzcyB5
b3UgYWJhbmRvbiB0aGUgbm0gc2NhbikuCiMgQWxzbywgTmV0QlNEIDAuOWEgd2FzIHRoZSBmaXJz
dCByZWxlYXNlIHRvIGludHJvZHVjZSBzaGFyZWQKIyBsaWJyYXJpZXMuCiMKY2FzZSAiJG9zdmVy
cyIgaW4KMC45fDAuOCopCgl1c2VkbD0iJHVuZGVmIgoJOzsKKikKCWNhc2UgYHVuYW1lIC1tYCBp
bgoJcG1heCkKCQkjIE5ldEJTRCAxLjMgYW5kIDEuMy4xIG9uIHBtYXggc2hpcHBlZCBhbiBgb2xk
JyBsZC5zbywKCQkjIHdoaWNoIHdpbGwgbm90IHdvcmsuCgkJY2FzZSAiJG9zdmVycyIgaW4KCQkx
LjN8MS4zLjEpCgkJCWRfZGxvcGVuPSR1bmRlZgoJCQk7OwoJCWVzYWMKCQk7OwoJZXNhYwoJaWYg
dGVzdCAtZiAvdXNyL2xpYmV4ZWMvbGQuZWxmX3NvOyB0aGVuCgkJIyBFTEYKCQlkX2Rsb3Blbj0k
ZGVmaW5lCgkJZF9kbGVycm9yPSRkZWZpbmUKCQljY2NkbGZsYWdzPSItRFBJQyAtZlBJQyAkY2Nj
ZGxmbGFncyIKCQlsZGRsZmxhZ3M9Ii0td2hvbGUtYXJjaGl2ZSAtc2hhcmVkICRsZGRsZmxhZ3Mi
CgkJcnBhdGhmbGFnPSItV2wsLXJwYXRoLCIKCQljYXNlICIkb3N2ZXJzIiBpbgoJCTEuWzAtNV0q
KQoJCQkjCgkJCSMgSW5jbHVkZSB0aGUgd2hvbGUgbGliZ2NjLmEgaW50byB0aGUgcGVybCBleGVj
dXRhYmxlCgkJCSMgc28gdGhhdCBjZXJ0YWluIHN5bWJvbHMgbmVlZGVkIGJ5IGxvYWRhYmxlIG1v
ZHVsZXMKCQkJIyBidWlsdCBhcyBDKysgb2JqZWN0cyAoX19laF9hbGxvYywgX19wdXJlX3ZpcnR1
YWwsCgkJCSMgZXRjLikgd2lsbCBhbHdheXMgYmUgZGVmaW5lZC4KCQkJIwoJCQljY2RsZmxhZ3M9
Ii1XbCwtd2hvbGUtYXJjaGl2ZSAtbGdjYyBcCgkJCQktV2wsLW5vLXdob2xlLWFyY2hpdmUgLVds
LC1FICRjY2RsZmxhZ3MiCgkJCTs7CgkJKikKCQkJY2NkbGZsYWdzPSItV2wsLUUgJGNjZGxmbGFn
cyIKCQkJOzsKCQllc2FjCgllbGlmIHRlc3QgLWYgL3Vzci9saWJleGVjL2xkLnNvOyB0aGVuCgkJ
IyBhLm91dAoJCWRfZGxvcGVuPSRkZWZpbmUKCQlkX2RsZXJyb3I9JGRlZmluZQoJCWNjY2RsZmxh
Z3M9Ii1EUElDIC1mUElDICRjY2NkbGZsYWdzIgoJCWxkZGxmbGFncz0iLUJzaGFyZWFibGUgJGxk
ZGxmbGFncyIKCQlycGF0aGZsYWc9Ii1SIgoJZWxzZQoJCWRfZGxvcGVuPSR1bmRlZgoJCXJwYXRo
ZmxhZz0KCWZpCgk7Owplc2FjCgojIG5ldGJzZCBoYWQgdGhlc2UgYnV0IHRoZXkgZG9uJ3QgcmVh
bGx5IHdvcmsgYXMgYWR2ZXJ0aXNlZCwgaW4gdGhlCiMgdmVyc2lvbnMgbGlzdGVkIGJlbG93LiAg
aWYgdGhleSBhcmUgZGVmaW5lZCwgdGhlbiB0aGVyZSBpc24ndCBhCiMgd2F5IHRvIG1ha2UgcGVy
bCBjYWxsIHNldHVpZCgpIG9yIHNldGdpZCgpLiAgaWYgdGhleSBhcmVuJ3QsIHRoZW4KIyAoJDws
ICQ+KSA9ICgkdSwgJHUpOyB3aWxsIHdvcmsgKHNhbWUgZm9yICQoLyQpKS4gIHRoaXMgaXMgYmVj
YXVzZQojIHlvdSBjYW4gbm90IGNoYW5nZSB0aGUgcmVhbCB1c2VyaWQgb2YgYSBwcm9jZXNzIHVu
ZGVyIDQuNEJTRC4KIyBuZXRic2QgZml4ZWQgdGhpcyBpbiAxLjMuMi4KY2FzZSAiJG9zdmVycyIg
aW4KMC45KnwxLlswMTJdKnwxLjN8MS4zLjEpCglkX3NldHJlZ2lkPSIkdW5kZWYiCglkX3NldHJl
dWlkPSIkdW5kZWYiCgk7Owplc2FjCmNhc2UgIiRvc3ZlcnMiIGluCjAuOSp8MS4qfDIuKnwzLip8
NC4qfDUuKikKCWRfZ2V0cHJvdG9lbnRfcj0iJHVuZGVmIgoJZF9nZXRwcm90b2J5bmFtZV9yPSIk
dW5kZWYiCglkX2dldHByb3RvYnludW1iZXJfcj0iJHVuZGVmIgoJZF9zZXRwcm90b2VudF9yPSIk
dW5kZWYiCglkX2VuZHByb3RvZW50X3I9IiR1bmRlZiIKCWRfZ2V0c2VydmVudF9yPSIkdW5kZWYi
CglkX2dldHNlcnZieW5hbWVfcj0iJHVuZGVmIgoJZF9nZXRzZXJ2Ynlwb3J0X3I9IiR1bmRlZiIK
CWRfc2V0c2VydmVudF9yPSIkdW5kZWYiCglkX2VuZHNlcnZlbnRfcj0iJHVuZGVmIgoJZF9nZXRw
cm90b2VudF9yX3Byb3RvPSIwIgoJZF9nZXRwcm90b2J5bmFtZV9yX3Byb3RvPSIwIgoJZF9nZXRw
cm90b2J5bnVtYmVyX3JfcHJvdG89IjAiCglkX3NldHByb3RvZW50X3JfcHJvdG89IjAiCglkX2Vu
ZHByb3RvZW50X3JfcHJvdG89IjAiCglkX2dldHNlcnZlbnRfcl9wcm90bz0iMCIKCWRfZ2V0c2Vy
dmJ5bmFtZV9yX3Byb3RvPSIwIgoJZF9nZXRzZXJ2Ynlwb3J0X3JfcHJvdG89IjAiCglkX3NldHNl
cnZlbnRfcl9wcm90bz0iMCIKCWRfZW5kc2VydmVudF9yX3Byb3RvPSIwIgoJOzsKZXNhYwoKIyBU
aGVzZSBhcmUgb2Jzb2xldGUgaW4gYW55IG5ldGJzZC4KZF9zZXRyZ2lkPSIkdW5kZWYiCmRfc2V0
cnVpZD0iJHVuZGVmIgoKIyB0aGVyZSdzIG5vIHByb2JsZW0gd2l0aCB2Zm9yay4KdXNldmZvcms9
dHJ1ZQoKIyBUaGlzIGlzIHRoZXJlIGJ1dCBpbiBtYWNoaW5lL2llZWVmcF9oLgppZWVlZnBfaD0i
ZGVmaW5lIgoKIyBUaGlzIHNjcmlwdCBVVS91c2V0aHJlYWRzLmNidSB3aWxsIGdldCAnY2FsbGVk
LWJhY2snIGJ5IENvbmZpZ3VyZQojIGFmdGVyIGl0IGhhcyBwcm9tcHRlZCB0aGUgdXNlciBmb3Ig
d2hldGhlciB0byB1c2UgdGhyZWFkcy4KY2F0ID4gVVUvdXNldGhyZWFkcy5jYnUgPDwnRU9DQlUn
CmNhc2UgIiR1c2V0aHJlYWRzIiBpbgokZGVmaW5lfHRydWV8W3lZXSopCglscHRocmVhZD0KCWZv
ciB4eHggaW4gcHRocmVhZDsgZG8KCQlmb3IgeXl5IGluICRsb2NsaWJwdGggJHBsaWJwdGggJGds
aWJwdGggZHVtbXk7IGRvCgkJCXp6ej0keXl5L2xpYiR4eHguYQoJCQlpZiB0ZXN0IC1mICIkenp6
IjsgdGhlbgoJCQkJbHB0aHJlYWQ9JHh4eAoJCQkJYnJlYWs7CgkJCWZpCgkJCXp6ej0keXl5L2xp
YiR4eHguc28KCQkJaWYgdGVzdCAtZiAiJHp6eiI7IHRoZW4KCQkJCWxwdGhyZWFkPSR4eHgKCQkJ
CWJyZWFrOwoJCQlmaQoJCQl6eno9YGxzICR5eXkvbGliJHh4eC5zby4qIDI+L2Rldi9udWxsYAoJ
CQlpZiB0ZXN0ICJYJHp6eiIgIT0gWDsgdGhlbgoJCQkJbHB0aHJlYWQ9JHh4eAoJCQkJYnJlYWs7
CgkJCWZpCgkJZG9uZQoJCWlmIHRlc3QgIlgkbHB0aHJlYWQiICE9IFg7IHRoZW4KCQkJYnJlYWs7
CgkJZmkKCWRvbmUKCWlmIHRlc3QgIlgkbHB0aHJlYWQiICE9IFg7IHRoZW4KCQkjIEFkZCAtbHB0
aHJlYWQuCgkJbGlic3dhbnRlZD0iJGxpYnN3YW50ZWQgJGxwdGhyZWFkIgoJCSMgVGhlcmUgaXMg
bm8gbGliY19yIGFzIG9mIE5ldEJTRCAxLjUuMiwgc28gbm8gYyAtPiBjX3IuCgkJIyBUaGlzIHdp
bGwgYmUgcmV2aXNpdGVkIHdoZW4gTmV0QlNEIGdhaW5zIGEgbmF0aXZlIHB0aHJlYWRzCgkJIyBp
bXBsZW1lbnRhdGlvbi4KCWVsc2UKCQllY2hvICIkMDogTm8gUE9TSVggdGhyZWFkcyBsaWJyYXJ5
ICgtbHB0aHJlYWQpIGZvdW5kLiAgIiBcCgkJICAgICAiWW91IG1heSB3YW50IHRvIGluc3RhbGwg
R05VIHB0aC4gIEFib3J0aW5nLiIgPiY0CgkJZXhpdCAxCglmaQoJdW5zZXQgbHB0aHJlYWQKCgkj
IHNldmVyYWwgcmVlbnRyYW50IGZ1bmN0aW9ucyBhcmUgZW1iZWRkZWQgaW4gbGliYywgYnV0IGhh
dmVuJ3QKCSMgYmVlbiBhZGRlZCB0byB0aGUgaGVhZGVyIGZpbGVzIHlldC4gIExldCdzIGhvbGQg
b2ZmIG9uIHVzaW5nCgkjIHRoZW0gdW50aWwgdGhleSBhcmUgYSB2YWxpZCBwYXJ0IG9mIHRoZSBB
UEkKCWNhc2UgIiRvc3ZlcnMiIGluCglbMDEyXS4qfDMuWzAtMV0pCgkJZF9nZXRwcm90b2J5bmFt
ZV9yPSR1bmRlZgoJCWRfZ2V0cHJvdG9ieW51bWJlcl9yPSR1bmRlZgoJCWRfZ2V0cHJvdG9lbnRf
cj0kdW5kZWYKCQlkX2dldHNlcnZieW5hbWVfcj0kdW5kZWYKCQlkX2dldHNlcnZieXBvcnRfcj0k
dW5kZWYKCQlkX2dldHNlcnZlbnRfcj0kdW5kZWYKCQlkX3NldHByb3RvZW50X3I9JHVuZGVmCgkJ
ZF9zZXRzZXJ2ZW50X3I9JHVuZGVmCgkJZF9lbmRwcm90b2VudF9yPSR1bmRlZgoJCWRfZW5kc2Vy
dmVudF9yPSR1bmRlZiA7OwoJZXNhYwoJOzsKCmVzYWMKRU9DQlUKCiMgU2V0IHNlbnNpYmxlIGRl
ZmF1bHRzIGZvciBOZXRCU0Q6IGxvb2sgZm9yIGxvY2FsIHNvZnR3YXJlIGluCiMgL3Vzci9wa2cg
KE5ldEJTRCBQYWNrYWdlcyBDb2xsZWN0aW9uKSBhbmQgaW4gL3Vzci9sb2NhbC4KIwpsb2NsaWJw
dGg9Ii91c3IvcGtnL2xpYiAvdXNyL2xvY2FsL2xpYiIKbG9jaW5jcHRoPSIvdXNyL3BrZy9pbmNs
dWRlIC91c3IvbG9jYWwvaW5jbHVkZSIKY2FzZSAiJHJwYXRoZmxhZyIgaW4KJycpCglsZGZsYWdz
PQoJOzsKKikKCWxkZmxhZ3M9Cglmb3IgeXl5IGluICRsb2NsaWJwdGg7IGRvCgkJbGRmbGFncz0i
JGxkZmxhZ3MgJHJwYXRoZmxhZyR5eXkiCglkb25lCgk7Owplc2FjCgpjYXNlIGB1bmFtZSAtbWAg
aW4KYWxwaGEpCiAgICBlY2hvICdpbnQgbWFpbigpIHt9JyA+IHRyeS5jCiAgICBnY2M9YCR7Y2M6
LWNjfSAtdiAtYyB0cnkuYyAyPiYxfGdyZXAgJ2djYyB2ZXJzaW9uIGVnY3MtMidgCiAgICBjYXNl
ICIkZ2NjIiBpbgogICAgJycgfCAiZ2NjIHZlcnNpb24gZWdjcy0yLjk1LiJbMy05XSopIDs7ICMg
Mi45NS4zIG9yIGJldHRlciBva2F5CiAgICAqKQljYXQgPiY0IDw8RU9GCioqKgoqKiogWW91ciBn
Y2MgKCRnY2MpIGlzIGtub3duIHRvIGJlCioqKiB0b28gYnVnZ3kgb24gbmV0YnNkL2FscGhhIHRv
IGNvbXBpbGUgUGVybCB3aXRoIG9wdGltaXphdGlvbi4KKioqIEl0IGlzIHN1Z2dlc3RlZCB5b3Ug
aW5zdGFsbCB0aGUgbGFuZy9nY2MgcGFja2FnZSB3aGljaCBzaG91bGQKKioqIGhhdmUgYXQgbGVh
c3QgZ2NjIDIuOTUuMyB3aGljaCBzaG91bGQgd29yayBva2F5OiB1c2UgZm9yIGV4YW1wbGUKKioq
IENvbmZpZ3VyZSAtRGNjPS91c3IvcGtnL2djYy0yLjk1LjMvYmluL2NjLiAgWW91IGNvdWxkIGFs
c28KKioqIENvbmZpZ3VyZSAtRG9wdGltaXplPS1PMCB0byBjb21waWxlIFBlcmwgd2l0aG91dCBh
bnkgb3B0aW1pemF0aW9uCioqKiBidXQgdGhhdCBpcyBub3QgcmVjb21tZW5kZWQuCioqKgpFT0YK
CWV4aXQgMQoJOzsKICAgIGVzYWMKICAgIHJtIC1mIHRyeS4qCiAgICA7Owplc2FjCgojIE5ldEJT
RC9zcGFyYyAxLjUuMy8xLjYuMSBkdW1wcyBjb3JlIGluIHRoZSBzZW1pZF9kcyB0ZXN0IG9mIENv
bmZpZ3VyZS4KY2FzZSBgdW5hbWUgLW1gIGluCnNwYXJjKSBkX3NlbWN0bF9zZW1pZF9kcz11bmRl
ZiA7Owplc2FjCgojIG1hbGxvYyB3cmFwIHdvcmtzCmNhc2UgIiR1c2VtYWxsb2N3cmFwIiBpbgon
JykgdXNlbWFsbG9jd3JhcD0nZGVmaW5lJyA7Owplc2FjCgojIGRvbid0IHVzZSBwZXJsIG1hbGxv
YyBieSBkZWZhdWx0CmNhc2UgIiR1c2VteW1hbGxvYyIgaW4KJycpIHVzZW15bWFsbG9jPW4gOzsK
ZXNhYwo=',
'freebsd' =>
'IyBPcmlnaW5hbCBiYXNlZCBvbiBpbmZvIGZyb20KIyBDYXJsIE0uIEZvbmdoZWlzZXIgPGNtZkBp
bnMuaW5mb25ldC5uZXQ+CiMgRGF0ZTogVGh1LCAyOCBKdWwgMTk5NCAxOToxNzowNSAtMDUwMCAo
Q0RUKQojCiMgQWRkaXRpb25hbCAxLjEuNSBkZWZpbmVzIGZyb20gCiMgT2xsaXZpZXIgUm9iZXJ0
IDxPbGxpdmllci5Sb2JlcnRAa2VsdGlhLmZybXVnLmZyLm5ldD4KIyBEYXRlOiBXZWQsIDI4IFNl
cCAxOTk0IDAwOjM3OjQ2ICswMTAwIChNRVQpCiMKIyBBZGRpdGlvbmFsIDIuKiBkZWZpbmVzIGZy
b20KIyBPbGxpdmllciBSb2JlcnQgPE9sbGl2aWVyLlJvYmVydEBrZWx0aWEuZnJtdWcuZnIubmV0
PgojIERhdGU6IFNhdCwgOCBBcHIgMTk5NSAyMDo1Mzo0MSArMDIwMCAoTUVUIERTVCkKIwojIEFk
ZGl0aW9uYWwgMi4wLjUgYW5kIDIuMSBkZWZpbmVkIGZyb20KIyBPbGxpdmllciBSb2JlcnQgPE9s
bGl2aWVyLlJvYmVydEBrZWx0aWEuZnJtdWcuZnIubmV0PgojIERhdGU6IEZyaSwgMTIgTWF5IDE5
OTUgMTQ6MzA6MzggKzAyMDAgKE1FVCBEU1QpCiMKIyBBZGRpdGlvbmFsIDIuMiBkZWZpbmVzIGZy
b20KIyBNYXJrIE11cnJheSA8bWFya0Bncm9uZGFyLnphPgojIERhdGU6IFdlZCwgNiBOb3YgMTk5
NiAwOTo0NDo1OCArMDIwMCAoTUVUKQojCiMgTW9kaWZpZWQgdG8gZW5zdXJlIHdlIHJlcGxhY2Ug
LWxjIHdpdGggLWxjX3IsIGFuZAojIHRvIHB1dCBpbiBwbGFjZS1ob2xkZXJzIGZvciB2YXJpb3Vz
IHNwZWNpZmljIGhpbnRzLgojIEFuZHkgRG91Z2hlcnR5IDxkb3VnaGVyYUBsYWZheWV0dGUuZWR1
PgojIERhdGU6IFR1ZSBNYXIgMTAgMTY6MDc6MDAgRVNUIDE5OTgKIwojIFN1cHBvcnQgZm9yIEZy
ZWVCU0QvRUxGCiMgT2xsaXZpZXIgUm9iZXJ0IDxyb2JlcnRvQGtlbHRpYS5mcmVlbml4LmZyPgoj
IERhdGU6IFdlZCBTZXAgIDIgMTY6MjI6MTIgQ0VTVCAxOTk4CiMKIyBUaGUgdHdvIGZsYWdzICIt
ZnBpYyAtRFBJQyIgYXJlIHVzZWQgdG8gaW5kaWNhdGUgYQojIHdpbGwtYmUtc2hhcmVkIG9iamVj
dC4gIENvbmZpZ3VyZSB3aWxsIGd1ZXNzIHRoZSAtZnBpYywgKGFuZCB0aGUKIyAtRFBJQyBpcyBu
b3QgdXNlZCBieSBwZXJsIHByb3BlcikgYnV0IHRoZSBmdWxsIGRlZmluZSBpcyBpbmNsdWRlZCB0
byAKIyBiZSBjb25zaXN0ZW50IHdpdGggdGhlIEZyZWVCU0QgZ2VuZXJhbCBzaGFyZWQgbGlicyBi
dWlsZGluZyBwcm9jZXNzLgojCiMgc2V0cmV1aWQgYW5kIGZyaWVuZHMgYXJlIGluaGVyZW50bHkg
YnJva2VuIGluIGFsbCB2ZXJzaW9ucyBvZiBGcmVlQlNECiMgYmVmb3JlIDIuMS1jdXJyZW50IChi
ZWZvcmUgYXBwcm94IGRhdGUgNC8xNS85NSkuIEl0IGlzIGZpeGVkIGluIDIuMC41CiMgYW5kIHdo
YXQtd2lsbC1iZS0yLjEKIwoKY2FzZSAiJG9zdmVycyIgaW4KMC4qfDEuMCopCgl1c2VkbD0iJHVu
ZGVmIgoJOzsKMS4xKikKCW1hbGxvY3R5cGU9J3ZvaWQgKicKCWdyb3Vwc3R5cGU9J2ludCcKCWRf
c2V0cmVnaWQ9J3VuZGVmJwoJZF9zZXRyZXVpZD0ndW5kZWYnCglkX3NldHJnaWQ9J3VuZGVmJwoJ
ZF9zZXRydWlkPSd1bmRlZicKCTs7CjIuMC1yZWxlYXNlKikKCWRfc2V0cmVnaWQ9J3VuZGVmJwoJ
ZF9zZXRyZXVpZD0ndW5kZWYnCglkX3NldHJnaWQ9J3VuZGVmJwoJZF9zZXRydWlkPSd1bmRlZicK
CTs7CiMKIyBUcnlpbmcgdG8gY292ZXIgMi4wLjUsIDIuMS1jdXJyZW50IGFuZCBmdXR1cmUgMi4x
LzIuMgojIEl0IGRvZXMgbm90IGNvdmVydCBhbGwgMi4xLWN1cnJlbnQgdmVyc2lvbnMgYXMgdGhl
IG91dHB1dCBvZiB1bmFtZQojIGNoYW5nZWQgYSBmZXcgdGltZXMuCiMKIyBFdmVuIHRob3VnaCBz
ZXRldWlkL3NldGVnaWQgYXJlIGF2YWlsYWJsZSwgdGhleSd2ZSBiZWVuIHR1cm5lZCBvZmYKIyBi
ZWNhdXNlIHBlcmwgaXNuJ3QgY29kZWQgd2l0aCBzYXZlZCBzZXRbdWddaWQgdmFyaWFibGVzIGlu
IG1pbmQuCiMgSW4gYWRkaXRpb24sIGEgc21hbGwgcGF0Y2ggaXMgcmVxdWlyZWQgdG8gc3VpZHBl
cmwgdG8gYXZvaWQgYSBzZWN1cml0eQojIHByb2JsZW0gd2l0aCBGcmVlQlNELgojCjIuMC41Knwy
LjAtYnVpbHQqfDIuMSopCiAJdXNldmZvcms9J3RydWUnCgljYXNlICIkdXNlbXltYWxsb2MiIGlu
CgkgICAgIiIpIHVzZW15bWFsbG9jPSduJwoJICAgICAgICA7OwoJZXNhYwoJZF9zZXRyZWdpZD0n
ZGVmaW5lJwoJZF9zZXRyZXVpZD0nZGVmaW5lJwoJZF9zZXRlZ2lkPSd1bmRlZicKCWRfc2V0ZXVp
ZD0ndW5kZWYnCgl0ZXN0IC1yIC4vYnJva2VuLWRiLm1zZyAmJiAuIC4vYnJva2VuLWRiLm1zZwoJ
OzsKIwojIDIuMiBhbmQgYWJvdmUgaGF2ZSBwaGttYWxsb2MoMykuCiMgZG9uJ3QgdXNlIC1sbWFs
bG9jIChtYXliZSB0aGVyZSdzIGFuIG9sZCBvbmUgZnJvbSAxLjEuNS4xIGZsb2F0aW5nIGFyb3Vu
ZCkKMi4yKikKIAl1c2V2Zm9yaz0ndHJ1ZScKCWNhc2UgIiR1c2VteW1hbGxvYyIgaW4KCSAgICAi
IikgdXNlbXltYWxsb2M9J24nCgkgICAgICAgIDs7Cgllc2FjCglsaWJzd2FudGVkPWBlY2hvICRs
aWJzd2FudGVkIHwgc2VkICdzLyBtYWxsb2MgLyAvJ2AKCWxpYnN3YW50ZWQ9YGVjaG8gJGxpYnN3
YW50ZWQgfCBzZWQgJ3MvIGJpbmQgLyAvJ2AKCSMgaWNvbnYgZ29uZSBpbiBQZXJsIDUuOC4xLCBi
dXQgaWYgc29tZW9uZSBjb21waWxlcyA1LjguMCBvciBlYXJsaWVyLgoJbGlic3dhbnRlZD1gZWNo
byAkbGlic3dhbnRlZCB8IHNlZCAncy8gaWNvbnYgLyAvJ2AKCWRfc2V0cmVnaWQ9J2RlZmluZScK
CWRfc2V0cmV1aWQ9J2RlZmluZScKCWRfc2V0ZWdpZD0nZGVmaW5lJwoJZF9zZXRldWlkPSdkZWZp
bmUnCgkjIGRfZG9zdWlkPSdkZWZpbmUnICMgT2Jzb2xldGUuCgk7OwoqKQl1c2V2Zm9yaz0ndHJ1
ZScKCWNhc2UgIiR1c2VteW1hbGxvYyIgaW4KCSAgICAiIikgdXNlbXltYWxsb2M9J24nCgkgICAg
ICAgIDs7Cgllc2FjCglsaWJzd2FudGVkPWBlY2hvICRsaWJzd2FudGVkIHwgc2VkICdzLyBtYWxs
b2MgLyAvJ2AKCTs7CmVzYWMKCiMgRHluYW1pYyBMb2FkaW5nIGZsYWdzIGhhdmUgbm90IGNoYW5n
ZWQgbXVjaCwgc28gdGhleSBhcmUgc2VwYXJhdGVkCiMgb3V0IGhlcmUgdG8gYXZvaWQgZHVwbGlj
YXRpbmcgdGhlbSBldmVyeXdoZXJlLgpjYXNlICIkb3N2ZXJzIiBpbgowLip8MS4wKikgOzsKCjEq
fDIqKQljY2NkbGZsYWdzPSctRFBJQyAtZnBpYycKCWxkZGxmbGFncz0iLUJzaGFyZWFibGUgJGxk
ZGxmbGFncyIKCTs7CgozKnw0Knw1Knw2KikKICAgICAgICBvYmpmb3JtYXQ9YC91c3IvYmluL29i
amZvcm1hdGAKICAgICAgICBpZiBbIHgkb2JqZm9ybWF0ID0geGFvdXQgXTsgdGhlbgogICAgICAg
ICAgICBpZiBbIC1lIC91c3IvbGliL2FvdXQgXTsgdGhlbgogICAgICAgICAgICAgICAgbGlicHRo
PSIvdXNyL2xpYi9hb3V0IC91c3IvbG9jYWwvbGliIC91c3IvbGliIgogICAgICAgICAgICAgICAg
Z2xpYnB0aD0iL3Vzci9saWIvYW91dCAvdXNyL2xvY2FsL2xpYiAvdXNyL2xpYiIKICAgICAgICAg
ICAgZmkKICAgICAgICAgICAgbGRkbGZsYWdzPSctQnNoYXJlYWJsZScKICAgICAgICBlbHNlCiAg
ICAgICAgICAgIGxpYnB0aD0iL3Vzci9saWIgL3Vzci9sb2NhbC9saWIiCiAgICAgICAgICAgIGds
aWJwdGg9Ii91c3IvbGliIC91c3IvbG9jYWwvbGliIgogICAgICAgICAgICBsZGZsYWdzPSItV2ws
LUUgIgogICAgICAgICAgICBsZGRsZmxhZ3M9Ii1zaGFyZWQgIgogICAgICAgIGZpCiAgICAgICAg
Y2NjZGxmbGFncz0nLURQSUMgLWZQSUMnCiAgICAgICAgOzsKKikKICAgICAgIGxpYnB0aD0iL3Vz
ci9saWIgL3Vzci9sb2NhbC9saWIiCiAgICAgICBnbGlicHRoPSIvdXNyL2xpYiAvdXNyL2xvY2Fs
L2xpYiIKICAgICAgIGxkZmxhZ3M9Ii1XbCwtRSAiCiAgICAgICAgbGRkbGZsYWdzPSItc2hhcmVk
ICIKICAgICAgICBjY2NkbGZsYWdzPSctRFBJQyAtZlBJQycKICAgICAgIDs7CmVzYWMKCmNhc2Ug
IiRvc3ZlcnMiIGluCjAqfDEqfDIqfDMqKSA7OwoKKikKCWNjZmxhZ3M9IiR7Y2NmbGFnc30gLURI
QVNfRlBTRVRNQVNLIC1ESEFTX0ZMT0FUSU5HUE9JTlRfSCIKCWlmIC91c3IvYmluL2ZpbGUgLUwg
L3Vzci9saWIvbGliYy5zbyB8IC91c3IvYmluL2dyZXAgLXZxICJub3Qgc3RyaXBwZWQiIDsgdGhl
bgoJICAgIHVzZW5tPWZhbHNlCglmaQogICAgICAgIDs7CmVzYWMKCmNhdCA8PCdFT00nID4mNAoK
U29tZSB1c2VycyBoYXZlIHJlcG9ydGVkIHRoYXQgQ29uZmlndXJlIGhhbHRzIHdoZW4gdGVzdGlu
ZyBmb3IKdGhlIE9fTk9OQkxPQ0sgc3ltYm9sIHdpdGggYSBzeW50YXggZXJyb3IuICBUaGlzIGlz
IGFwcGFyZW50bHkgYQpzaCBlcnJvci4gIFJlcnVubmluZyBDb25maWd1cmUgd2l0aCBrc2ggYXBw
YXJlbnRseSBmaXhlcyB0aGUKcHJvYmxlbS4gIFRyeQoJa3NoIENvbmZpZ3VyZSBbeW91ciBvcHRp
b25zXQoKRU9NCgojIEZyb206IEFudG9uIEJlcmV6aW4gPHRvYmV6QHBsYWIua3UuZGs+CiMgVG86
IHBlcmw1LXBvcnRlcnNAcGVybC5vcmcKIyBTdWJqZWN0OiBbUEFUQ0ggNS4wMDVfNTRdIENvbmZp
Z3VyZSAtIGhpbnRzL2ZyZWVic2Quc2ggc2lnbmFsIGhhbmRsZXIgdHlwZQojIERhdGU6IDMwIE5v
diAxOTk4IDE5OjQ2OjI0ICswMTAwCiMgTWVzc2FnZS1JRDogPDg2NHNyaGh2Y3YuZnNmQGxpb24u
cGxhYi5rdS5kaz4KCnNpZ25hbF90PSd2b2lkJwpkX3ZvaWRzaWc9J2RlZmluZScKCiMgc2V0IGxp
YnBlcmwuc28uWC5YIGZvciAyLjIuWApjYXNlICIkb3N2ZXJzIiBpbgoyLjIqKQogICAgIyB1bmZv
cnR1bmF0ZWx5IHRoaXMgY29kZSBnZXRzIGV4ZWN1dGVkIGJlZm9yZQogICAgIyB0aGUgZXF1aXZh
bGVudCBpbiB0aGUgbWFpbiBDb25maWd1cmUgc28gd2UgY29weSBhIGxpdHRsZQogICAgIyBmcm9t
IENvbmZpZ3VyZSBYWFggQ29uZmlndXJlIHNob3VsZCBiZSBmaXhlZC4KICAgIGlmICR0ZXN0IC1y
ICRzcmMvcGF0Y2hsZXZlbC5oO3RoZW4KICAgICAgIHBhdGNobGV2ZWw9YGF3ayAnL2RlZmluZVsg
CV0rUEVSTF9WRVJTSU9OLyB7cHJpbnQgJDN9JyAkc3JjL3BhdGNobGV2ZWwuaGAKICAgICAgIHN1
YnZlcnNpb249YGF3ayAnL2RlZmluZVsgCV0rUEVSTF9TVUJWRVJTSU9OLyB7cHJpbnQgJDN9JyAk
c3JjL3BhdGNobGV2ZWwuaGAKICAgIGVsc2UKICAgICAgIHBhdGNobGV2ZWw9MAogICAgICAgc3Vi
dmVyc2lvbj0wCiAgICBmaQogICAgbGlicGVybD0ibGlicGVybC5zby4kcGF0Y2hsZXZlbC4kc3Vi
dmVyc2lvbiIKICAgIHVuc2V0IHBhdGNobGV2ZWwKICAgIHVuc2V0IHN1YnZlcnNpb24KICAgIDs7
CmVzYWMKCiMgVGhpcyBzY3JpcHQgVVUvdXNldGhyZWFkcy5jYnUgd2lsbCBnZXQgJ2NhbGxlZC1i
YWNrJyBieSBDb25maWd1cmUgCiMgYWZ0ZXIgaXQgaGFzIHByb21wdGVkIHRoZSB1c2VyIGZvciB3
aGV0aGVyIHRvIHVzZSB0aHJlYWRzLgpjYXQgPiBVVS91c2V0aHJlYWRzLmNidSA8PCdFT0NCVScK
Y2FzZSAiJHVzZXRocmVhZHMiIGluCiRkZWZpbmV8dHJ1ZXxbeVldKikKICAgICAgICBsY19yPWAv
c2Jpbi9sZGNvbmZpZyAtcnxncmVwICc6LWxjX3InfGF3ayAne3ByaW50ICRORn0nfHNlZCAtbiAn
JHAnYAogICAgICAgIGNhc2UgIiRvc3ZlcnMiIGluICAKCTAqfDEqfDIuMCp8Mi4xKikgICBjYXQg
PDxFT00gPiY0CkkgZGlkIG5vdCBrbm93IHRoYXQgRnJlZUJTRCAkb3N2ZXJzIHN1cHBvcnRzIFBP
U0lYIHRocmVhZHMuCgpGZWVsIGZyZWUgdG8gdGVsbCBwZXJsYnVnQHBlcmwub3JnIG90aGVyd2lz
ZS4KRU9NCgkgICAgICBleGl0IDEKCSAgICAgIDs7CgogICAgICAgIDIuMi5bMC03XSopCiAgICAg
ICAgICAgICAgY2F0IDw8RU9NID4mNApQT1NJWCB0aHJlYWRzIGFyZSBub3Qgc3VwcG9ydGVkIHdl
bGwgYnkgRnJlZUJTRCAkb3N2ZXJzLgoKUGxlYXNlIGNvbnNpZGVyIHVwZ3JhZGluZyB0byBhdCBs
ZWFzdCBGcmVlQlNEIDIuMi44LApvciBwcmVmZXJhYmx5IHRvIHRoZSBtb3N0IHJlY2VudCAtUkVM
RUFTRSBvciAtU1RBQkxFCnZlcnNpb24gKHNlZSBodHRwOi8vd3d3LmZyZWVic2Qub3JnL3JlbGVh
c2VzLykuCgooV2hpbGUgMi4yLjcgZG9lcyBoYXZlIHB0aHJlYWRzLCBpdCBoYXMgc29tZSBwcm9i
bGVtcwogd2l0aCB0aGUgY29tYmluYXRpb24gb2YgdGhyZWFkcyBhbmQgcGlwZXMgYW5kIHRoZXJl
Zm9yZQogbWFueSBQZXJsIHRlc3RzIHdpbGwgZWl0aGVyIGhhbmcgb3IgZmFpbC4pCkVPTQoJICAg
ICAgZXhpdCAxCgkgICAgICA7OwoKCVszLTVdLiopCgkgICAgICBpZiBbICEgLXIgIiRsY19yIiBd
OyB0aGVuCgkgICAgICBjYXQgPDxFT00gPiY0ClBPU0lYIHRocmVhZHMgc2hvdWxkIGJlIHN1cHBv
cnRlZCBieSBGcmVlQlNEICRvc3ZlcnMgLS0KYnV0IHlvdXIgc3lzdGVtIGlzIG1pc3NpbmcgdGhl
IHNoYXJlZCBsaWJjX3IuCigvc2Jpbi9sZGNvbmZpZyAtciBkb2Vzbid0IGZpbmQgYW55KS4KCkNv
bnNpZGVyIHVzaW5nIHRoZSBsYXRlc3QgU1RBQkxFIHJlbGVhc2UuCkVPTQoJCSBleGl0IDEKCSAg
ICAgIGZpCgkgICAgICAjIDUwMDAxNiBpcyB0aGUgZmlyc3Qgb3NyZWxkYXRlIGluIHdoaWNoIG9u
ZSBjb3VsZAoJICAgICAgIyBqdXN0IGxpbmsgYWdhaW5zdCBsaWJjX3Igd2l0aG91dCBkaXNwb3Np
bmcgb2YgbGliYwoJICAgICAgIyBhdCB0aGUgc2FtZSB0aW1lLiAgNTAwMDE2IC4uLiB1cCB0byB3
aGF0ZXZlciBpdCB3YXMKCSAgICAgICMgb24gdGhlIDMxc3Qgb2YgQXVndXN0IDIwMDMgY2FuIHN0
aWxsIGJlIHVzZWQgd2l0aCAtcHRocmVhZCwKCSAgICAgICMgYnV0IGl0IGlzIG5vdCBuZWNlc3Nh
cnkuCgoJICAgICAgIyBBbnRvbiBCZXJlemluIHNheXMgdGhhdCBwb3N0IDUwMHNvbWV0aGluZyB3
ZSdyZSB3cm9uZyB0byBiZQoJICAgICAgIyB0byBiZSB1c2luZyAtbGNfciwgYW5kIHNob3VsZCBq
dXN0IGJlIHVzaW5nIC1wdGhyZWFkIG9uIHRoZQoJICAgICAgIyBsaW5rZXIgbGluZS4KCSAgICAg
ICMgU28gcHJlc3VtYWJseSByZWFsbHkgd2Ugc2hvdWxkIGJlIGNoZWNraW5nIHRoYXQgJG9zdmVy
IGlzIDUuKikKCSAgICAgICMgYW5kIHRoYXQgYC9zYmluL3N5c2N0bCAtbiBrZXJuLm9zcmVsZGF0
ZWAgLWdlIDUwMDAxNgoJICAgICAgIyBvciAtbHQgNTAwc29tZXRoaW5nIGFuZCBvbmx5IGluIHRo
YXQgcmFuZ2Ugbm90IGRvaW5nIHRoaXM6CgkgICAgICBsZGZsYWdzPSItcHRocmVhZCAkbGRmbGFn
cyIKCgkgICAgICAjIEJvdGggaW4gNC54IGFuZCA1LnggZ2V0aG9zdGJ5YWRkcl9yIGV4aXN0cyBi
dXQKCSAgICAgICMgaXQgaXMgIlRlbXBvcmFyeSBmdW5jdGlvbiwgbm90IHRocmVhZHNhZmUiLi4u
CgkgICAgICAjIFByZXN1bWFibHkgZWFybGllciBpdCBkaWRuJ3QgZXZlbiBleGlzdC4KCSAgICAg
IGRfZ2V0aG9zdGJ5YWRkcl9yPSJ1bmRlZiIKCSAgICAgIGRfZ2V0aG9zdGJ5YWRkcl9yX3Byb3Rv
PSIwIgoJICAgICAgOzsKCgkqKQoJICAgICAgIyA3LnggZG9lc24ndCBpbnN0YWxsIGxpYmNfciBi
eSBkZWZhdWx0LCBhbmQgQ29uZmlndXJlCgkgICAgICAjIHdvdWxkIGZhaWwgaW4gdGhlIGNvZGUg
Zm9sbG93aW5nCgkgICAgICAjCgkgICAgICAjIGdldGhvc3RieWFkZHJfcigpIGFwcGVhcnMgdG8g
aGF2ZSBiZWVuIGltcGxlbWVudGVkIGluIDYueCsKCSAgICAgIGxkZmxhZ3M9Ii1wdGhyZWFkICRs
ZGZsYWdzIgoJICAgICAgOzsKCgllc2FjCgogICAgICAgIGNhc2UgIiRvc3ZlcnMiIGluCiAgICAg
ICAgWzEtNF0qKQoJICAgIHNldCBgZWNobyBYICIkbGlic3dhbnRlZCAifCBzZWQgLWUgJ3MvIGMg
LyBjX3IgLydgCgkgICAgc2hpZnQKCSAgICBsaWJzd2FudGVkPSIkKiIKCSAgICA7OwogICAgICAg
ICopCgkgICAgc2V0IGBlY2hvIFggIiRsaWJzd2FudGVkICJ8IHNlZCAtZSAncy8gYyAvLydgCgkg
ICAgc2hpZnQKCSAgICBsaWJzd2FudGVkPSIkKiIKCSAgICA7OwoJZXNhYwoJICAgIAoJIyBDb25m
aWd1cmUgd2lsbCBwcm9iYWJseSBwaWNrIHRoZSB3cm9uZyBsaWJjIHRvIHVzZSBmb3Igbm0gc2Nh
bi4KCSMgVGhlIHNhZmVzdCBxdWljay1maXggaXMganVzdCB0byBub3QgdXNlIG5tIGF0IGFsbC4u
LgoJdXNlbm09ZmFsc2UKCiAgICAgICAgY2FzZSAiJG9zdmVycyIgaW4KICAgICAgICAyLjIuOCop
CiAgICAgICAgICAgICMgLi4uIGJ1dCB0aGlzIGRvZXMgbm90IGFwcGx5IGZvciAyLjIuOCAtIHdl
IGtub3cgaXQncyBzYWZlCiAgICAgICAgICAgIGxpYmM9IiRsY19yIgogICAgICAgICAgICB1c2Vu
bT10cnVlCiAgICAgICAgICAgOzsKICAgICAgICBlc2FjCgogICAgICAgIHVuc2V0IGxjX3IKCgkj
IEV2ZW4gd2l0aCB0aGUgbWFsbG9jIG11dGV4ZXMgdGhlIFBlcmwgbWFsbG9jIGRvZXMgbm90Cgkj
IHNlZW0gdG8gYmUgdGhyZWFkc2FmZSBpbiBGcmVlQlNEPwoJY2FzZSAiJHVzZW15bWFsbG9jIiBp
bgoJJycpIHVzZW15bWFsbG9jPW4gOzsKCWVzYWMKZXNhYwpFT0NCVQoKIyBtYWxsb2Mgd3JhcCB3
b3JrcwpjYXNlICIkdXNlbWFsbG9jd3JhcCIgaW4KJycpIHVzZW1hbGxvY3dyYXA9J2RlZmluZScg
OzsKZXNhYwoKIyBYWFggVW5kZXIgRnJlZUJTRCA2LjAgKGFuZCBwcm9iYWJseSBtb3N0IG90aGVy
IHNpbWlsYXIgdmVyc2lvbnMpCiMgUGVybF9kaWUoTlVMTCkgZ2VuZXJhdGVzIGEgd2FybmluZzoK
IyAgICBwcF9zeXMuYzo0OTE6IHdhcm5pbmc6IG51bGwgZm9ybWF0IHN0cmluZwojIENvbmZpZ3Vy
ZSBzdXBwb3NlZGVseSB0ZXN0cyBmb3IgdGhpcywgYnV0IGFwcGFyZW50bHkgdGhlIHRlc3QgZG9l
c24ndAojIHdvcmsuICBWb2x1bnRlZXJzIHdpdGggRnJlZUJTRCBhcmUgbmVlZGVkIHRvIGltcHJv
dmluZyB0aGUgQ29uZmlndXJlIHRlc3QuCiMgTWVhbndoaWxlLCB0aGUgZm9sbG93aW5nIHdvcmth
cm91bmQgc2hvdWxkIGJlIHNhZmUgb24gYWxsIHZlcnNpb25zCiMgb2YgRnJlZUJTRC4KZF9wcmlu
dGZfZm9ybWF0X251bGw9J3VuZGVmJwo=',
'openbsd' =>
'IyBoaW50cy9vcGVuYnNkLnNoCiMKIyBoaW50cyBmaWxlIGZvciBPcGVuQlNEOyBUb2RkIE1pbGxl
ciA8bWlsbGVydEBvcGVuYnNkLm9yZz4KIyBFZGl0ZWQgdG8gYWxsb3cgQ29uZmlndXJlIGNvbW1h
bmQtbGluZSBvdmVycmlkZXMgYnkKIyAgQW5keSBEb3VnaGVydHkgPGRvdWdoZXJhQGxhZmF5ZXR0
ZS5lZHU+CiMKIyBUbyBidWlsZCB3aXRoIGRpc3RyaWJ1dGlvbiBwYXRocywgdXNlOgojCS4vQ29u
ZmlndXJlIC1kZXMgLURvcGVuYnNkX2Rpc3RyaWJ1dGlvbj1kZWZpbmVkCiMKCiMgSW4gT3BlbkJT
RCA+IDMuNywgdXNlIHBlcmwncyBtYWxsb2MgW3BlcmwgIzc1NzQyXQpjYXNlICIkb3N2ZXJzIiBp
bgozLls4OV0qfFs0LTldKikKICAgIHRlc3QgIiR1c2VteW1hbGxvYyIgfHwgdXNlbXltYWxsb2M9
eQogICAgOzsKZXNhYwoKIyBtYWxsb2Mgd3JhcCB3b3JrcwpjYXNlICIkdXNlbWFsbG9jd3JhcCIg
aW4KJycpIHVzZW1hbGxvY3dyYXA9J2RlZmluZScgOzsKZXNhYwoKIyBDdXJyZW50bHksIHZmb3Jr
KDIpIGlzIG5vdCBhIHJlYWwgd2luIG92ZXIgZm9yaygyKS4KdXNldmZvcms9IiR1bmRlZiIKCiMg
SW4gT3BlbkJTRCA8IDMuMywgdGhlIHNldHJlP1t1Z11pZCgpIGFyZSBlbXVsYXRlZCB1c2luZyB0
aGUKIyBfUE9TSVhfU0FWRURfSURTIGZ1bmN0aW9uYWxpdHkgd2hpY2ggZG9lcyBub3QgaGF2ZSB0
aGUgc2FtZQojIHNlbWFudGljcyBhcyA0LjNCU0QuICBTdGFydGluZyB3aXRoIE9wZW5CU0QgMy4z
LCB0aGUgb3JpZ2luYWwKIyBzZW1hbnRpY3MgaGF2ZSBiZWVuIHJlc3RvcmVkLgpjYXNlICIkb3N2
ZXJzIiBpbgpbMC0yXS4qfDMuWzAtMl0pCglkX3NldHJlZ2lkPSR1bmRlZgoJZF9zZXRyZXVpZD0k
dW5kZWYKCWRfc2V0cmdpZD0kdW5kZWYKCWRfc2V0cnVpZD0kdW5kZWYKZXNhYwoKIwojIE5vdCBh
bGwgcGxhdGZvcm1zIHN1cHBvcnQgZHluYW1pYyBsb2FkaW5nLi4uCiMgRm9yIHRoZSBjYXNlIG9m
ICIkb3BlbmJzZF9kaXN0cmlidXRpb24iLCB0aGUgaGludHMgZmlsZQojIG5lZWRzIHRvIGtub3cg
d2hldGhlciB3ZSBhcmUgdXNpbmcgZHluYW1pYyBsb2FkaW5nIHNvIHRoYXQKIyBpdCBjYW4gc2V0
IHRoZSBsaWJwZXJsIG5hbWUgYXBwcm9wcmlhdGVseS4KIyBBbGxvdyBjb21tYW5kIGxpbmUgb3Zl
cnJpZGVzLgojCkFSQ0g9YGFyY2ggfCBzZWQgJ3MvXk9wZW5CU0QuLy8nYApjYXNlICIke0FSQ0h9
LSR7b3N2ZXJzfSIgaW4KYWxwaGEtMi5bMC04XXxtaXBzLTIuWzAtOF18cG93ZXJwYy0yLlswLTdd
fG04OGstKnxocHBhLSp8dmF4LSopCgl0ZXN0IC16ICIkdXNlZGwiICYmIHVzZWRsPSR1bmRlZgoJ
OzsKKikKCXRlc3QgLXogIiR1c2VkbCIgJiYgdXNlZGw9JGRlZmluZQoJIyBXZSB1c2UgLWZQSUMg
aGVyZSBiZWNhdXNlIC1mcGljIGlzICpOT1QqIGVub3VnaCBmb3Igc29tZSBvZiB0aGUKCSMgZXh0
ZW5zaW9ucyBsaWtlIFRrIG9uIHNvbWUgT3BlbkJTRCBwbGF0Zm9ybXMgKGllOiBzcGFyYykKCWNj
Y2RsZmxhZ3M9Ii1EUElDIC1mUElDICRjY2NkbGZsYWdzIgoJY2FzZSAiJG9zdmVycyIgaW4KCVsw
MV0uKnwyLlswLTddfDIuWzAtN10uKikKCQlsZGRsZmxhZ3M9Ii1Cc2hhcmVhYmxlICRsZGRsZmxh
Z3MiCgkJOzsKCTIuWzgtOV18My4wKQoJCWxkPSR7Y2M6LWNjfQoJCWxkZGxmbGFncz0iLXNoYXJl
ZCAtZlBJQyAkbGRkbGZsYWdzIgoJCTs7CgkqKSAjIGZyb20gMy4xIG9ud2FyZHMKCQlsZD0ke2Nj
Oi1jY30KCQlsZGRsZmxhZ3M9Ii1zaGFyZWQgLWZQSUMgJGxkZGxmbGFncyIKCQlsaWJzd2FudGVk
PWBlY2hvICRsaWJzd2FudGVkIHwgc2VkICdzLyBkbCAvIC8nYAoJCTs7Cgllc2FjCgoJIyBXZSBu
ZWVkIHRvIGZvcmNlIGxkIHRvIGV4cG9ydCBzeW1ib2xzIG9uIEVMRiBwbGF0Zm9ybXMuCgkjIFdp
dGhvdXQgdGhpcywgZGxvcGVuKCkgaXMgY3JpcHBsZWQuCglFTEY9YCR7Y2M6LWNjfSAtZE0gLUUg
LSA8L2Rldi9udWxsIHwgZ3JlcCBfX0VMRl9fYAoJdGVzdCAtbiAiJEVMRiIgJiYgbGRmbGFncz0i
LVdsLC1FICRsZGZsYWdzIgoJOzsKZXNhYwoKIwojIFR3ZWFrcyBmb3IgdmFyaW91cyB2ZXJzaW9u
cyBvZiBPcGVuQlNECiMKY2FzZSAiJG9zdmVycyIgaW4KMi41KQoJIyBPcGVuQlNEIDIuNSBoYXMg
YnJva2VuIG9kYm0gc3VwcG9ydAoJaV9kYm09JHVuZGVmCgk7Owplc2FjCgojIE9wZW5CU0QgZG9l
c24ndCBuZWVkIGxpYmNyeXB0IGJ1dCBtYW55IGZvbGtzIGtlZXAgYSBzdHViIGxpYgojIGFyb3Vu
ZCBmb3Igb2xkIE5ldEJTRCBiaW5hcmllcy4KbGlic3dhbnRlZD1gZWNobyAkbGlic3dhbnRlZCB8
IHNlZCAncy8gY3J5cHQgLyAvJ2AKCiMgQ29uZmlndXJlIGNhbid0IGZpZ3VyZSB0aGlzIG91dCBu
b24taW50ZXJhY3RpdmVseQpkX3N1aWRzYWZlPSRkZWZpbmUKCiMgY2MgaXMgZ2NjIHNvIHdlIGNh
biBkbyBiZXR0ZXIgdGhhbiAtTwojIEFsbG93IGEgY29tbWFuZC1saW5lIG92ZXJyaWRlLCBzdWNo
IGFzIC1Eb3B0aW1pemU9LWcKY2FzZSAke0FSQ0h9IGluCm04OGspCiAgIG9wdGltaXplPSctTzAn
CiAgIDs7CmhwcGEpCiAgIG9wdGltaXplPSctTzAnCiAgIDs7CiopCiAgIHRlc3QgIiRvcHRpbWl6
ZSIgfHwgb3B0aW1pemU9Jy1PMicKICAgOzsKZXNhYwoKIyBUaGlzIHNjcmlwdCBVVS91c2V0aHJl
YWRzLmNidSB3aWxsIGdldCAnY2FsbGVkLWJhY2snIGJ5IENvbmZpZ3VyZSAKIyBhZnRlciBpdCBo
YXMgcHJvbXB0ZWQgdGhlIHVzZXIgZm9yIHdoZXRoZXIgdG8gdXNlIHRocmVhZHMuCmNhdCA+IFVV
L3VzZXRocmVhZHMuY2J1IDw8J0VPQ0JVJwpjYXNlICIkdXNldGhyZWFkcyIgaW4KJGRlZmluZXx0
cnVlfFt5WV0qKQoJIyBhbnkgb3BlbmJzZCB2ZXJzaW9uIGRlcGVuZGVuY2llcyB3aXRoIHB0aHJl
YWRzPwoJY2NmbGFncz0iLXB0aHJlYWQgJGNjZmxhZ3MiCglsZGZsYWdzPSItcHRocmVhZCAkbGRm
bGFncyIKCWNhc2UgIiRvc3ZlcnMiIGluCglbMC0yXS4qfDMuWzAtMl0pCgkJIyBDaGFuZ2UgZnJv
bSAtbGMgdG8gLWxjX3IKCQlzZXQgYGVjaG8gIlggJGxpYnN3YW50ZWQgIiB8IHNlZCAncy8gYyAv
IGNfciAvJ2AKCQlzaGlmdAoJCWxpYnN3YW50ZWQ9IiQqIgoJOzsKCWVzYWMKCWNhc2UgIiRvc3Zl
cnMiIGluCglbMDEyXS4qfDMuWzAtNl0pCiAgICAgICAgCSMgQnJva2VuIGF0IGxlYXN0IHVwIHRv
IE9wZW5CU0QgMy42LCB3ZSdsbCBzZWUgYWJvdXQgMy43CgkJZF9nZXRzZXJ2YnluYW1lX3I9JHVu
ZGVmIDs7Cgllc2FjCmVzYWMKRU9DQlUKCiMgVGhpcyBzY3JpcHQgVVUvdXNlNjRiaXRpbnQuY2J1
IHdpbGwgZ2V0ICdjYWxsZWQtYmFjaycgYnkgQ29uZmlndXJlIAojIGFmdGVyIGl0IGhhcyBwcm9t
cHRlZCB0aGUgdXNlciBmb3Igd2hldGhlciB0byB1c2UgNjQtYml0bmVzcy4KY2F0ID4gVVUvdXNl
NjRiaXRpbnQuY2J1IDw8J0VPQ0JVJwpjYXNlICIkdXNlNjRiaXRpbnQiIGluCiRkZWZpbmV8dHJ1
ZXxbeVldKikKCWVjaG8gIiAiCgllY2hvICJDaGVja2luZyBpZiB5b3VyIEMgbGlicmFyeSBoYXMg
YnJva2VuIDY0LWJpdCBmdW5jdGlvbnMuLi4iID4mNAoJJGNhdCA+Y2hlY2suYyA8PEVPQ1AKI2lu
Y2x1ZGUgPHN0ZGlvLmg+CnR5cGVkZWYgJHVxdWFkdHlwZSBteVVMTDsKaW50IG1haW4gKHZvaWQp
CnsKICAgIHN0cnVjdCB7Cglkb3VibGUgZDsKCW15VUxMICB1OwogICAgfSAqcCwgdGVzdFtdID0g
ewoJezQyOTQ5NjczMDMuMTUsIDQyOTQ5NjczMDNVTEx9LAoJezQyOTQ5NjcyOTQuMiwgIDQyOTQ5
NjcyOTRVTEx9LAoJezQyOTQ5NjcyOTUuNywgIDQyOTQ5NjcyOTVVTEx9LAoJezAuMCwgMFVMTH0K
ICAgIH07CiAgICBmb3IgKHAgPSB0ZXN0OyBwLT51OyBwKyspIHsKCW15VUxMIHggPSAobXlVTEwp
cC0+ZDsKCWlmICh4ICE9IHAtPnUpIHsKCSAgICBwcmludGYoImJ1Z2d5XG4iKTsKCSAgICByZXR1
cm4gMDsKCX0KICAgIH0KICAgIHByaW50Zigib2tcbiIpOwogICAgcmV0dXJuIDA7Cn0KRU9DUAoJ
c2V0IGNoZWNrCglpZiBldmFsICRjb21waWxlX29rOyB0aGVuCgkgICAgbGliY3F1YWQ9YC4vY2hl
Y2tgCgkgICAgZWNobyAiWW91ciBDIGxpYnJhcnkncyA2NC1iaXQgZnVuY3Rpb25zIGFyZSAkbGli
Y3F1YWQuIgoJZWxzZQoJICAgIGVjaG8gIihJIGNhbid0IHNlZW0gdG8gY29tcGlsZSB0aGUgdGVz
dCBwcm9ncmFtLikiCgkgICAgZWNobyAiQXNzdW1pbmcgdGhhdCB5b3VyIEMgbGlicmFyeSdzIDY0
LWJpdCBmdW5jdGlvbnMgYXJlIG9rLiIKCSAgICBsaWJjcXVhZD0ib2siCglmaQoJJHJtIC1mIGNo
ZWNrLmMgY2hlY2sKCgljYXNlICIkbGliY3F1YWQiIGluCgkgICAgYnVnZ3kqKQoJCWNhdCA+JjQg
PDxFT00KCioqKiBZb3UgaGF2ZSBhIEMgbGlicmFyeSB3aXRoIGJyb2tlbiA2NC1iaXQgZnVuY3Rp
b25zLgoqKiogNjQtYml0IHN1cHBvcnQgZG9lcyBub3Qgd29yayByZWxpYWJseSBpbiB0aGlzIGNv
bmZpZ3VyYXRpb24uCioqKiBQbGVhc2UgcmVydW4gQ29uZmlndXJlIHdpdGhvdXQgLUR1c2U2NGJp
dGludCBhbmQvb3IgLUR1c2Vtb3JlYml0cy4KKioqIENhbm5vdCBjb250aW51ZSwgYWJvcnRpbmcu
CgpFT00KCQlleGl0IDEKCQk7OwoJZXNhYwplc2FjCkVPQ0JVCgojIFdoZW4gYnVpbGRpbmcgaW4g
dGhlIE9wZW5CU0QgdHJlZSB3ZSB1c2UgZGlmZmVyZW50IHBhdGhzCiMgVGhpcyBpcyBvbmx5IHBh
cnQgb2YgdGhlIHN0b3J5LCB0aGUgcmVzdCBjb21lcyBmcm9tIGNvbmZpZy5vdmVyCmNhc2UgIiRv
cGVuYnNkX2Rpc3RyaWJ1dGlvbiIgaW4KJyd8JHVuZGVmfGZhbHNlKSA7OwoqKQoJIyBXZSBwdXQg
dGhpbmdzIGluIC91c3IsIG5vdCAvdXNyL2xvY2FsCglwcmVmaXg9Jy91c3InCglwcmVmaXhleHA9
Jy91c3InCglzeXNtYW49Jy91c3Ivc2hhcmUvbWFuL21hbjEnCglsaWJwdGg9Jy91c3IvbGliJwoJ
Z2xpYnB0aD0nL3Vzci9saWInCgkjIExvY2FsIHRoaW5ncywgaG93ZXZlciwgZG8gZ28gaW4gL3Vz
ci9sb2NhbAoJc2l0ZXByZWZpeD0nL3Vzci9sb2NhbCcKCXNpdGVwcmVmaXhleHA9Jy91c3IvbG9j
YWwnCgkjIFBvcnRzIGluc3RhbGxzIG5vbi1zdGQgbGlicyBpbiAvdXNyL2xvY2FsL2xpYiBzbyBs
b29rIHRoZXJlIHRvbwoJbG9jaW5jcHRoPScvdXNyL2xvY2FsL2luY2x1ZGUnCglsb2NsaWJwdGg9
Jy91c3IvbG9jYWwvbGliJwoJIyBMaW5rIHBlcmwgd2l0aCBzaGFyZWQgbGlicGVybAoJaWYgWyAi
JHVzZWRsIiA9ICIkZGVmaW5lIiAtYSAtciBzaGxpYl92ZXJzaW9uIF07IHRoZW4KCQl1c2VzaHJw
bGliPXRydWUKCQlsaWJwZXJsPWAuIC4vc2hsaWJfdmVyc2lvbjsgZWNobyBsaWJwZXJsLnNvLiR7
bWFqb3J9LiR7bWlub3J9YAoJZmkKCTs7CmVzYWMKCiMgZW5kCg==',
'linux' =>
'IyBoaW50cy9saW51eC5zaAojIE9yaWdpbmFsIHZlcnNpb24gYnkgcnNhbmRlcnMKIyBBZGRpdGlv
bmFsIHN1cHBvcnQgYnkgS2VubmV0aCBBbGJhbm93c2tpIDxramFoZHNAa2phaGRzLmNvbT4KIwoj
IEVMRiBzdXBwb3J0IGJ5IEguSi4gTHUgPGhqbEBueW5leHN0LmNvbT4KIyBBZGRpdGlvbmFsIGlu
Zm8gZnJvbSBOaWdlbCBIZWFkIDxuaGVhZEBFU09DLmJpdG5ldD4KIyBhbmQgS2VubmV0aCBBbGJh
bm93c2tpIDxramFoZHNAa2phaGRzLmNvbT4KIwojIENvbnNvbGlkYXRlZCBieSBBbmR5IERvdWdo
ZXJ0eSA8ZG91Z2hlcmFAbGFmYXlldHRlLmVkdT4KIwojIFVwZGF0ZWQgVGh1IEZlYiAgOCAxMTo1
NjoxMCBFU1QgMTk5NgoKIyBVcGRhdGVkIFRodSBNYXkgMzAgMTA6NTA6MjIgRURUIDE5OTYgYnkg
PGRvdWdoZXJhQGxhZmF5ZXR0ZS5lZHU+CgojIFVwZGF0ZWQgRnJpIEp1biAyMSAxMTowNzo1NCBF
RFQgMTk5NgojIE5EQk0gc3VwcG9ydCBmb3IgRUxGIHJlLWVuYWJsZWQgYnkgPGtqYWhkc0BramFo
ZHMuY29tPgoKIyBObyB2ZXJzaW9uIG9mIExpbnV4IHN1cHBvcnRzIHNldHVpZCBzY3JpcHRzLgpk
X3N1aWRzYWZlPSd1bmRlZicKCiMgRGViaWFuIGFuZCBSZWQgSGF0LCBhbmQgcGVyaGFwcyBvdGhl
ciB2ZW5kb3JzLCBwcm92aWRlIGJvdGggcnVudGltZSBhbmQKIyBkZXZlbG9wbWVudCBwYWNrYWdl
cyBmb3Igc29tZSBsaWJyYXJpZXMuICBUaGUgcnVudGltZSBwYWNrYWdlcyBjb250YWluIHNoYXJl
ZAojIGxpYnJhcmllcyB3aXRoIHZlcnNpb24gaW5mb3JtYXRpb24gaW4gdGhlaXIgbmFtZXMgKGUu
Zy4sIGxpYmdkYm0uc28uMS43LjMpOwojIHRoZSBkZXZlbG9wbWVudCBwYWNrYWdlcyBzdXBwbGVt
ZW50IHRoaXMgd2l0aCB2ZXJzaW9ubGVzcyBzaGFyZWQgbGlicmFyaWVzCiMgKGUuZy4sIGxpYmdk
Ym0uc28pLgojCiMgSWYgeW91IHdhbnQgdG8gbGluayBhZ2FpbnN0IHN1Y2ggYSBsaWJyYXJ5LCB5
b3UgbXVzdCBpbnN0YWxsIHRoZSBkZXZlbG9wbWVudAojIHZlcnNpb24gb2YgdGhlIHBhY2thZ2Uu
CiMKIyBUaGVzZSBwYWNrYWdlcyB1c2UgYSAtZGV2IG5hbWluZyBjb252ZW50aW9uIGluIGJvdGgg
RGViaWFuIGFuZCBSZWQgSGF0OgojICAgbGliZ2RibWcxICAobm9uLWRldmVsb3BtZW50IHZlcnNp
b24gb2YgR05VIGxpYmMgMi1saW5rZWQgR0RCTSBsaWJyYXJ5KQojICAgbGliZ2RibWcxLWRldiAo
ZGV2ZWxvcG1lbnQgdmVyc2lvbiBvZiBHTlUgbGliYyAyLWxpbmtlZCBHREJNIGxpYnJhcnkpCiMg
U28gbWFrZSBzdXJlIHRoYXQgZm9yIGFueSBsaWJyYXJpZXMgeW91IHdpc2ggdG8gbGluayBQZXJs
IHdpdGggdW5kZXIKIyBEZWJpYW4gb3IgUmVkIEhhdCB5b3UgaGF2ZSB0aGUgLWRldiBwYWNrYWdl
cyBpbnN0YWxsZWQuCgojIFN1U0UgTGludXggY2FuIGJlIHVzZWQgYXMgY3Jvc3MtY29tcGlsYXRp
b24gaG9zdCBmb3IgQ3JheSBYVDQgQ2F0YW1vdW50L1FrLgppZiB0ZXN0IC1kIC9vcHQveHQtcGUK
dGhlbgogIGNhc2UgImBjYyAtViAyPiYxYCIgaW4KICAqY2F0YW1vdW50KikgLiBoaW50cy9jYXRh
bW91bnQuc2g7IHJldHVybiA7OwogIGVzYWMKZmkKCiMgU29tZSBvcGVyYXRpbmcgc3lzdGVtcyAo
ZS5nLiwgU29sYXJpcyAyLjYpIHdpbGwgbGluayB0byBhIHZlcnNpb25lZCBzaGFyZWQKIyBsaWJy
YXJ5IGltcGxpY2l0bHkuICBGb3IgZXhhbXBsZSwgb24gU29sYXJpcywgYGxkIGZvby5vIC1sZ2Ri
bScgd2lsbCBmaW5kIGFuCiMgYXBwcm9wcmlhdGUgdmVyc2lvbiBvZiBsaWJnZGJtLCBpZiBvbmUg
aXMgYXZhaWxhYmxlOyBMaW51eCwgaG93ZXZlciwgZG9lc24ndAojIGRvIHRoZSBpbXBsaWNpdCBt
YXBwaW5nLgppZ25vcmVfdmVyc2lvbmVkX3NvbGlicz0neScKCiMgQlNEIGNvbXBhdGliaWxpdHkg
bGlicmFyeSBubyBsb25nZXIgbmVlZGVkCiMgJ2thZmZlJyBoYXMgYSAvdXNyL2xpYi9saWJuZXQu
c28gd2hpY2ggaXMgbm90IGF0IGFsbCByZWxldmFudCBmb3IgcGVybC4KIyBiaW5kIGNhdXNlcyBp
c3N1ZXMgd2l0aCBzZXZlcmFsIHJlZW50cmFudCBmdW5jdGlvbnMKc2V0IGBlY2hvIFggIiRsaWJz
d2FudGVkICJ8IHNlZCAtZSAncy8gYnNkIC8gLycgLWUgJ3MvIG5ldCAvIC8nIC1lICdzLyBiaW5k
IC8gLydgCnNoaWZ0CmxpYnN3YW50ZWQ9IiQqIgoKIyBEZWJpYW4gNC4wIHB1dHMgbmRibSBpbiB0
aGUgLWxnZGJtX2NvbXBhdCBsaWJyYXJ5LgpsaWJzd2FudGVkPSIkbGlic3dhbnRlZCBnZGJtX2Nv
bXBhdCIKCiMgSWYgeW91IGhhdmUgZ2xpYmMsIHRoZW4gcmVwb3J0IHRoZSB2ZXJzaW9uIGZvciAu
L215Y29uZmlnIGJ1ZyByZXBvcnRpbmcuCiMgKENvbmZpZ3VyZSBkb2Vzbid0IG5lZWQgdG8ga25v
dyB0aGUgc3BlY2lmaWMgdmVyc2lvbiBzaW5jZSBpdCBqdXN0IHVzZXMKIyBnY2MgdG8gbG9hZCB0
aGUgbGlicmFyeSBmb3IgYWxsIHRlc3RzLikKIyBXZSBkb24ndCB1c2UgX19HTElCQ19fIGFuZCAg
X19HTElCQ19NSU5PUl9fIGJlY2F1c2UgdGhleQojIGFyZSBpbnN1ZmZpY2llbnRseSBwcmVjaXNl
IHRvIGRpc3Rpbmd1aXNoIHRoaW5ncyBsaWtlCiMgbGliYy0yLjAuNiBhbmQgbGliYy0yLjAuNy4K
aWYgdGVzdCAtTCAvbGliL2xpYmMuc28uNjsgdGhlbgogICAgbGliYz1gbHMgLWwgL2xpYi9saWJj
LnNvLjYgfCBhd2sgJ3twcmludCAkTkZ9J2AKICAgIGxpYmM9L2xpYi8kbGliYwpmaQoKIyBDb25m
aWd1cmUgbWF5IGZhaWwgdG8gZmluZCBsc3RhdCgpIHNpbmNlIGl0J3MgYSBzdGF0aWMvaW5saW5l
CiMgZnVuY3Rpb24gaW4gPHN5cy9zdGF0Lmg+LgpkX2xzdGF0PWRlZmluZQoKIyBtYWxsb2Mgd3Jh
cCB3b3JrcwpjYXNlICIkdXNlbWFsbG9jd3JhcCIgaW4KJycpIHVzZW1hbGxvY3dyYXA9J2RlZmlu
ZScgOzsKZXNhYwoKIyBUaGUgc3lzdGVtIG1hbGxvYygpIGlzIGFib3V0IGFzIGZhc3QgYW5kIGFz
IGZydWdhbCBhcyBwZXJsJ3MuCiMgU2luY2UgdGhlIHN5c3RlbSBtYWxsb2MoKSBoYXMgYmVlbiB0
aGUgZGVmYXVsdCBzaW5jZSBhdCBsZWFzdAojIDUuMDAxLCB3ZSBtaWdodCBhcyB3ZWxsIGxlYXZl
IGl0IHRoYXQgd2F5LiAgLS1BRCAgMTAgSmFuIDIwMDIKY2FzZSAiJHVzZW15bWFsbG9jIiBpbgon
JykgdXNlbXltYWxsb2M9J24nIDs7CmVzYWMKCiMgQ2hlY2sgaWYgd2UncmUgYWJvdXQgdG8gdXNl
IEludGVsJ3MgSUNDIGNvbXBpbGVyCmNhc2UgImAke2NjOi1jY30gLVYgMj4mMWAiIGluCioiSW50
ZWwoUikgQysrIENvbXBpbGVyIip8KiJJbnRlbChSKSBDIENvbXBpbGVyIiopCiAgICAjIHJlY29y
ZCB0aGUgdmVyc2lvbiwgZm9ybWF0czoKICAgICMgaWNjIChJQ0MpIDEwLjEgMjAwODA4MDEKICAg
ICMgaWNwYyAoSUNDKSAxMC4xIDIwMDgwODAxCiAgICAjIGZvbGxvd2VkIGJ5IGEgY29weXJpZ2h0
IG9uIHRoZSBzZWNvbmQgbGluZQogICAgY2N2ZXJzaW9uPWAke2NjOi1jY30gLS12ZXJzaW9uIHwg
c2VkIC1uIC1lICdzL15pY3BcP2MgXCgoSUNDKSBcKVw/Ly9wJ2AKICAgICMgVGhpcyBpcyBuZWVk
ZWQgZm9yIENvbmZpZ3VyZSdzIHByb3RvdHlwZSBjaGVja3MgdG8gd29yayBjb3JyZWN0bHkKICAg
ICMgVGhlIC1tcCBmbGFnIGlzIG5lZWRlZCB0byBwYXNzIHZhcmlvdXMgZmxvYXRpbmcgcG9pbnQg
cmVsYXRlZCB0ZXN0cwogICAgIyBUaGUgLW5vLWdjYyBmbGFnIGlzIG5lZWRlZCBvdGhlcndpc2Us
IGljYyBwcmV0ZW5kcyAocG9vcmx5KSB0byBiZSBnY2MKICAgIGNjZmxhZ3M9Ii13ZTE0NyAtbXAg
LW5vLWdjYyAkY2NmbGFncyIKICAgICMgUHJldmVudCByZWxvY2F0aW9uIGVycm9ycyBvbiA2NGJp
dHMgYXJjaAogICAgY2FzZSAiYHVuYW1lIC1tYCIgaW4KCSppYTY0KnwqeDg2XzY0KikKCSAgICBj
Y2NkbGZsYWdzPSctZlBJQycKCTs7CiAgICBlc2FjCiAgICAjIElmIHdlJ3JlIHVzaW5nIElDQywg
d2UgdXN1YWxseSB3YW50IHRoZSBiZXN0IHBlcmZvcm1hbmNlCiAgICBjYXNlICIkb3B0aW1pemUi
IGluCiAgICAnJykgb3B0aW1pemU9Jy1PMycgOzsKICAgIGVzYWMKICAgIDs7CioiIFN1biAiKiJD
IiopCiAgICAjIFN1bidzIEMgY29tcGlsZXIsIHdoaWNoIG1pZ2h0IGhhdmUgYSAndGFnJyBuYW1l
IGJldHdlZW4KICAgICMgJ1N1bicgYW5kIHRoZSAnQyc6ICBFeGFtcGxlczoKICAgICMgY2M6IFN1
biBDIDUuOSBMaW51eF9pMzg2IFBhdGNoIDEyNDg3MS0wMSAyMDA3LzA3LzMxCiAgICAjIGNjOiBT
dW4gQ2VyZXMgQyA1LjEwIExpbnV4X2kzODYgMjAwOC8wNy8xMAogICAgdGVzdCAiJG9wdGltaXpl
IiB8fCBvcHRpbWl6ZT0nLXhPMicKICAgIGNjY2RsZmxhZ3M9Jy1LUElDJwogICAgbGRkbGZsYWdz
PSctRyAtQmR5bmFtaWMnCiAgICAjIFN1biBDIGRvZXNuJ3Qgc3VwcG9ydCBnY2MgYXR0cmlidXRl
cywgYnV0LCBpbiBtYW55IGNhc2VzLCBkb2Vzbid0CiAgICAjIGNvbXBsYWluIGVpdGhlci4gIE5v
dCBhbGwgY2FzZXMsIHRob3VnaC4KICAgIGRfYXR0cmlidXRlX2Zvcm1hdD0ndW5kZWYnCiAgICBk
X2F0dHJpYnV0ZV9tYWxsb2M9J3VuZGVmJwogICAgZF9hdHRyaWJ1dGVfbm9ubnVsbD0ndW5kZWYn
CiAgICBkX2F0dHJpYnV0ZV9ub3JldHVybj0ndW5kZWYnCiAgICBkX2F0dHJpYnV0ZV9wdXJlPSd1
bmRlZicKICAgIGRfYXR0cmlidXRlX3VudXNlZD0ndW5kZWYnCiAgICBkX2F0dHJpYnV0ZV93YXJu
X3VudXNlZF9yZXN1bHQ9J3VuZGVmJwogICAgOzsKZXNhYwoKY2FzZSAiJG9wdGltaXplIiBpbgoj
IHVzZSAtTzIgYnkgZGVmYXVsdCA7IC1PMyBkb2Vzbid0IHNlZW0gdG8gYnJpbmcgc2lnbmlmaWNh
bnQgYmVuZWZpdHMgd2l0aCBnY2MKJycpCiAgICBvcHRpbWl6ZT0nLU8yJwogICAgY2FzZSAiYHVu
YW1lIC1tYCIgaW4KICAgICAgICBwcGMqKQogICAgICAgICAgICAjIG9uIHBwYywgaXQgc2VlbXMg
dGhhdCBnY2MgKGF0IGxlYXN0IGdjYyAzLjMuMikgaXNuJ3QgaGFwcHkKICAgICAgICAgICAgIyB3
aXRoIC1PMiA7IHNvIGRvd25ncmFkZSB0byAtTzEuCiAgICAgICAgICAgIG9wdGltaXplPSctTzEn
CiAgICAgICAgOzsKICAgICAgICBpYTY0KikKICAgICAgICAgICAgIyBUaGlzIGFyY2hpdGVjdHVy
ZSBoYXMgaGFkIHZhcmlvdXMgcHJvYmxlbXMgd2l0aCBnY2MncwogICAgICAgICAgICAjIGluIHRo
ZSAzLjIsIDMuMywgYW5kIDMuNCByZWxlYXNlcyB3aGVuIG9wdGltaXplZCB0byAtTzIuICBTZWUK
ICAgICAgICAgICAgIyBSVCAjMzcxNTYgZm9yIGEgZGlzY3Vzc2lvbiBvZiB0aGUgcHJvYmxlbS4K
ICAgICAgICAgICAgY2FzZSAiYCR7Y2M6LWdjY30gLXYgMj4mMWAiIGluCiAgICAgICAgICAgICoi
dmVyc2lvbiAzLjIiKnwqInZlcnNpb24gMy4zIip8KiJ2ZXJzaW9uIDMuNCIqKQogICAgICAgICAg
ICAgICAgY2NmbGFncz0iLWZuby1kZWxldGUtbnVsbC1wb2ludGVyLWNoZWNrcyAkY2NmbGFncyIK
ICAgICAgICAgICAgOzsKICAgICAgICAgICAgZXNhYwogICAgICAgIDs7CiAgICBlc2FjCiAgICA7
Owplc2FjCgojIFVidW50dSAxMS4wNCAoYW5kIGxhdGVyLCBwcmVzdW1hYmx5KSBkb2Vzbid0IGtl
ZXAgbW9zdCBsaWJyYXJpZXMKIyAoc3VjaCBhcyAtbG0pIGluIC9saWIgb3IgL3Vzci9saWIuICBT
byB3ZSBoYXZlIHRvIGFzayBnY2MgdG8gdGVsbCB1cwojIHdoZXJlIHRvIGxvb2suICBXZSBkb24n
dCB3YW50IGdjYydzIG93biBsaWJyYXJpZXMsIGhvd2V2ZXIsIHNvIHdlCiMgZmlsdGVyIHRob3Nl
IG91dC4KIyBUaGlzIGNvdWxkIGJlIGNvbmRpdGlvbmFsIG9uIFVuYnVudHUsIGJ1dCBvdGhlciBk
aXN0cmlidXRpb25zIG1heQojIGZvbGxvdyBzdWl0LCBhbmQgdGhpcyBzY2hlbWUgc2VlbXMgdG8g
d29yayBldmVuIG9uIHJhdGhlciBvbGQgZ2NjJ3MuCiMgVGhpcyB1bmNvbmRpdGlvbmFsbHkgdXNl
cyBnY2MgYmVjYXVzZSBldmVuIGlmIHRoZSB1c2VyIGlzIHVzaW5nIGFub3RoZXIKIyBjb21waWxl
ciwgd2Ugc3RpbGwgbmVlZCB0byBmaW5kIHRoZSBtYXRoIGxpYnJhcnkgYW5kIGZyaWVuZHMsIGFu
ZCBJIGRvbid0CiMga25vdyBob3cgb3RoZXIgY29tcGlsZXJzIHdpbGwgY29wZSB3aXRoIHRoYXQg
c2l0dWF0aW9uLgojIFN0aWxsLCBhcyBhbiBlc2NhcGUgaGF0Y2gsIGFsbG93IENvbmZpZ3VyZSBj
b21tYW5kIGxpbmUgb3ZlcnJpZGVzIHRvCiMgcGxpYnB0aCB0byBieXBhc3MgdGhpcyBjaGVjay4K
Y2FzZSAiJHBsaWJwdGgiIGluCicnKSBwbGlicHRoPWBnY2MgLXByaW50LXNlYXJjaC1kaXJzIHwg
Z3JlcCBsaWJyYXJpZXMgfAoJY3V0IC1mMi0gLWQ9IHwgdHIgJzonICR0cm5sIHwgZ3JlcCAtdiAn
Z2NjJyB8IHNlZCAtZSAnczovJDo6J2AKICAgIHNldCBYICRwbGlicHRoICMgQ29sbGFwc2UgYWxs
IGVudHJpZXMgb24gb25lIGxpbmUKICAgIHNoaWZ0CiAgICBwbGlicHRoPSIkKiIKICAgIDs7CmVz
YWMKCiMgQXJlIHdlIHVzaW5nIEVMRj8gIFRoYW5rcyB0byBLZW5uZXRoIEFsYmFub3dza2kgPGtq
YWhkc0BramFoZHMuY29tPgojIGZvciB0aGlzIHRlc3QuCmNhdCA+dHJ5LmMgPDwnRU9NJwovKiBU
ZXN0IGZvciB3aGV0aGVyIEVMRiBiaW5hcmllcyBhcmUgcHJvZHVjZWQgKi8KI2luY2x1ZGUgPGZj
bnRsLmg+CiNpbmNsdWRlIDxzdGRsaWIuaD4KI2luY2x1ZGUgPHVuaXN0ZC5oPgptYWluKCkgewoJ
Y2hhciBidWZmZXJbNF07CglpbnQgaT1vcGVuKCJhLm91dCIsT19SRE9OTFkpOwoJaWYoaT09LTEp
CgkJZXhpdCgxKTsgLyogZmFpbCAqLwoJaWYocmVhZChpLCZidWZmZXJbMF0sNCk8NCkKCQlleGl0
KDEpOyAvKiBmYWlsICovCglpZihidWZmZXJbMF0gIT0gMTI3IHx8IGJ1ZmZlclsxXSAhPSAnRScg
fHwKICAgICAgICAgICBidWZmZXJbMl0gIT0gJ0wnIHx8IGJ1ZmZlclszXSAhPSAnRicpCgkJZXhp
dCgxKTsgLyogZmFpbCAqLwoJZXhpdCgwKTsgLyogc3VjY2VlZCAoeWVzLCBpdCdzIEVMRikgKi8K
fQpFT00KaWYgJHtjYzotZ2NjfSB0cnkuYyA+L2Rldi9udWxsIDI+JjEgJiYgJHJ1biAuL2Eub3V0
OyB0aGVuCiAgICBjYXQgPDwnRU9NJyA+JjQKCllvdSBhcHBlYXIgdG8gaGF2ZSBFTEYgc3VwcG9y
dC4gIEknbGwgdHJ5IHRvIHVzZSBpdCBmb3IgZHluYW1pYyBsb2FkaW5nLgpJZiBkeW5hbWljIGxv
YWRpbmcgZG9lc24ndCB3b3JrLCByZWFkIGhpbnRzL2xpbnV4LnNoIGZvciBmdXJ0aGVyIGluZm9y
bWF0aW9uLgpFT00KCmVsc2UKICAgIGNhdCA8PCdFT00nID4mNAoKWW91IGRvbid0IGhhdmUgYW4g
RUxGIGdjYy4gIEkgd2lsbCB1c2UgZGxkIGlmIHBvc3NpYmxlLiAgSWYgeW91IGFyZQp1c2luZyBh
IHZlcnNpb24gb2YgRExEIGVhcmxpZXIgdGhhbiAzLjIuNiwgb3IgZG9uJ3QgaGF2ZSBpdCBhdCBh
bGwsIHlvdQpzaG91bGQgcHJvYmFibHkgdXBncmFkZS4gSWYgeW91IGFyZSBmb3JjZWQgdG8gdXNl
IDMuMi40LCB5b3Ugc2hvdWxkCnVuY29tbWVudCBhIGNvdXBsZSBvZiBsaW5lcyBpbiBoaW50cy9s
aW51eC5zaCBhbmQgcmVzdGFydCBDb25maWd1cmUgc28KdGhhdCBzaGFyZWQgbGlicmFyaWVzIHdp
bGwgYmUgZGlzYWxsb3dlZC4KCkVPTQogICAgbGRkbGZsYWdzPSItciAkbGRkbGZsYWdzIgogICAg
IyBUaGVzZSBlbXB0eSB2YWx1ZXMgYXJlIHNvIHRoYXQgQ29uZmlndXJlIGRvZXNuJ3QgcHV0IGlu
IHRoZQogICAgIyBMaW51eCBFTEYgdmFsdWVzLgogICAgY2NkbGZsYWdzPScgJwogICAgY2NjZGxm
bGFncz0nICcKICAgIGNjZmxhZ3M9Ii1ET1ZSX0RCTF9ESUc9MTQgJGNjZmxhZ3MiCiAgICBzbz0n
c2EnCiAgICBkbGV4dD0nbycKICAgIG5tX3NvX29wdD0nICcKICAgICMjIElmIHlvdSBhcmUgdXNp
bmcgRExEIDMuMi40IHdoaWNoIGRvZXMgbm90IHN1cHBvcnQgc2hhcmVkIGxpYnMsCiAgICAjIyB1
bmNvbW1lbnQgdGhlIG5leHQgdHdvIGxpbmVzOgogICAgI2xkZmxhZ3M9Ii1zdGF0aWMiCiAgICAj
c289J25vbmUnCgoJIyBJbiBhZGRpdGlvbiwgb24gc29tZSBzeXN0ZW1zIHRoZXJlIGlzIGEgcHJv
YmxlbSB3aXRoIHBlcmwgYW5kIE5EQk0KCSMgd2hpY2ggY2F1c2VzIEFueURCTSBhbmQgTkRCTV9G
aWxlIHRvIGxvY2sgdXAuIFRoaXMgaXMgZXZpZGVuY2VkCgkjIGluIHRoZSB0ZXN0cyBhcyBBbnlE
Qk0ganVzdCBmcmVlemluZy4gIEFwcGFyZW50bHksIHRoaXMgb25seQoJIyBoYXBwZW5zIG9uIGEu
b3V0IHN5c3RlbXMsIHNvIHdlIGRpc2FibGUgTkRCTSBmb3IgYWxsIGEub3V0IGxpbnV4CgkjIHN5
c3RlbXMuICBJZiBzb21lb25lIGNhbiBzdWdnZXN0IGEgbW9yZSByb2J1c3QgdGVzdAoJIyAgdGhh
dCB3b3VsZCBiZSBhcHByZWNpYXRlZC4KCSMKCSMgTW9yZSBpbmZvOgoJIyBEYXRlOiBXZWQsIDcg
RmViIDE5OTYgMDM6MjE6MDQgKzA5MDAKCSMgRnJvbTogSmVmZnJleSBGcmllZGwgPGpmcmllZGxA
bmZmLm5jbC5vbXJvbi5jby5qcD4KCSMKCSMgSSB0cmllZCBjb21waWxpbmcgd2l0aCBEQk0gc3Vw
cG9ydCBhbmQgc3VyZSBlbm91Z2ggdGhpbmdzIGxvY2tlZCB1cAoJIyBqdXN0IGFzIGFkdmVydGlz
ZWQuIENoZWNraW5nIGludG8gaXQsIEkgZm91bmQgdGhhdCB0aGUgbG9ja3VwIHdhcwoJIyBkdXJp
bmcgdGhlIGNhbGwgdG8gZGJtX29wZW4uIE5vdCAqaW4qIGRibV9vcGVuIC0tIGJ1dCBiZXR3ZWVu
IHRoZSBjYWxsCgkjIHRvIGFuZCB0aGUganVtcCBpbnRvLgoJIwoJIyBUbyBtYWtlIGEgbG9uZyBz
dG9yeSBzaG9ydCwgbWFraW5nIHN1cmUgdGhhdCB0aGUgKi5hIGFuZCAqLnNhIHBhaXJzIG9mCgkj
ICAgL3Vzci9saWIvbGlie20sZGIsZ2RibX0ue2Esc2F9CgkjIHdlcmUgcGVyZmVjdGx5IGluIHN5
bmMgdG9vayBjYXJlIG9mIGl0LgoJIwoJIyBUaGlzIHdpbGwgZ2VuZXJhdGUgYSBoYXJtbGVzcyBX
aG9hIFRoZXJlISBtZXNzYWdlCgljYXNlICIkZF9kYm1fb3BlbiIgaW4KCScnKQljYXQgPDwnRU9N
JyA+JjQKCkRpc2FibGluZyBuZGJtLiAgVGhpcyB3aWxsIGdlbmVyYXRlIGEgV2hvYSBUaGVyZSBt
ZXNzYWdlIGluIENvbmZpZ3VyZS4KUmVhZCBoaW50cy9saW51eC5zaCBmb3IgZnVydGhlciBpbmZv
cm1hdGlvbi4KRU9NCgkJIyBZb3UgY2FuIG92ZXJyaWRlIHRoaXMgd2l0aCBDb25maWd1cmUgLURk
X2RibV9vcGVuCgkJZF9kYm1fb3Blbj11bmRlZgoJCTs7Cgllc2FjCmZpCgpybSAtZiB0cnkuYyBh
Lm91dAoKaWYgL2Jpbi9zaCAtYyBleGl0OyB0aGVuCiAgZWNobyAnJwogIGVjaG8gJ1lvdSBhcHBl
YXIgdG8gaGF2ZSBhIHdvcmtpbmcgYmFzaC4gIEdvb2QuJwplbHNlCiAgY2F0IDw8ICdFT00nID4m
NAoKKioqKioqKioqKioqKioqKioqKioqKiogV2FybmluZyEgKioqKioqKioqKioqKioqKioqKioq
Ckl0IHdvdWxkIGFwcGVhciB5b3UgaGF2ZSBhIGRlZmVjdGl2ZSBiYXNoIHNoZWxsIGluc3RhbGxl
ZC4gVGhpcyBpcyBsaWtlbHkgdG8KZ2l2ZSB5b3UgYSBmYWlsdXJlIG9mIG9wL2V4ZWMgdGVzdCAj
NSBkdXJpbmcgdGhlIHRlc3QgcGhhc2Ugb2YgdGhlIGJ1aWxkLApVcGdyYWRpbmcgdG8gYSByZWNl
bnQgdmVyc2lvbiAoMS4xNC40IG9yIGxhdGVyKSBzaG91bGQgZml4IHRoZSBwcm9ibGVtLgoqKioq
KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioKRU9NCgpm
aQoKIyBPbiBTUEFSQ2xpbnV4LAojIFRoZSBmb2xsb3dpbmcgY3NoIGNvbnNpc3RlbnRseSBjb3Jl
ZHVtcGVkIGluIHRoZSB0ZXN0IGRpcmVjdG9yeQojICIvaG9tZS9taWtlZGxyL3Blcmw1LjAwM185
NC90IiwgdGhvdWdoIG5vdCBtb3N0IG90aGVyIGRpcmVjdG9yaWVzLgoKI05hbWUgICAgICAgIDog
Y3NoICAgICAgICAgICAgICAgICAgICBEaXN0cmlidXRpb246IFJlZCBIYXQgTGludXggKFJlbWJy
YW5kdCkKI1ZlcnNpb24gICAgIDogNS4yLjYgICAgICAgICAgICAgICAgICAgICAgICBWZW5kb3I6
IFJlZCBIYXQgU29mdHdhcmUKI1JlbGVhc2UgICAgIDogMyAgICAgICAgICAgICAgICAgICAgICAg
IEJ1aWxkIERhdGU6IEZyaSBNYXkgMjQgMTk6NDI6MTQgMTk5NgojSW5zdGFsbCBkYXRlOiBUaHUg
SnVsIDExIDE2OjIwOjE0IDE5OTYgQnVpbGQgSG9zdDogaXRjaHkucmVkaGF0LmNvbQojR3JvdXAg
ICAgICAgOiBTaGVsbHMgICAgICAgICAgICAgICAgICAgU291cmNlIFJQTTogY3NoLTUuMi42LTMu
c3JjLnJwbQojU2l6ZSAgICAgICAgOiAxODQ0MTcKI0Rlc2NyaXB0aW9uIDogQlNEIGMtc2hlbGwK
CiMgRm9yIHRoaXMgcmVhc29uIEkgc3VnZ2VzdCB1c2luZyB0aGUgbXVjaCBidWctZml4ZWQgdGNz
aCBmb3IgZ2xvYmJpbmcKIyB3aGVyZSBhdmFpbGFibGUuCgojIE5vdmVtYmVyIDIwMDE6ICBUaGF0
IHdhcm5pbmcncyBwcmV0dHkgb2xkIG5vdyBhbmQgcHJvYmFibHkgbm90IHNvCiMgcmVsZXZhbnQs
IGVzcGVjaWFsbHkgc2luY2UgcGVybCBub3cgdXNlcyBGaWxlOjpHbG9iIGZvciBnbG9iYmluZy4K
IyBXZSdsbCBzdGlsbCBsb29rIGZvciB0Y3NoLCBidXQgdG9uZSBkb3duIHRoZSB3YXJuaW5ncy4K
IyBBbmR5IERvdWdoZXJ0eSwgTm92LiA2LCAyMDAxCmlmICRjc2ggLWMgJ2VjaG8gJHZlcnNpb24n
ID4vZGV2L251bGwgMj4mMTsgdGhlbgogICAgZWNobyAnWW91ciBjc2ggaXMgcmVhbGx5IHRjc2gu
ICBHb29kLicKZWxzZQogICAgaWYgeHh4PWAuL1VVL2xvYyB0Y3NoIGJsdXJmbCAkcHRoYDsgJHRl
c3QgLWYgIiR4eHgiOyB0aGVuCgllY2hvICJGb3VuZCB0Y3NoLiAgSSdsbCB1c2UgaXQgZm9yIGds
b2JiaW5nLiIKCSMgV2UgY2FuJ3QgY2hhbmdlIENvbmZpZ3VyZSdzIHNldHRpbmcgb2YgJGNzaCwg
ZHVlIHRvIHRoZSB3YXkKCSMgQ29uZmlndXJlIGhhbmRsZXMgJGRfcG9ydGFibGUgYW5kIGNvbW1h
bmRzIGZvdW5kIGluICRsb2NsaXN0LgoJIyBXZSBjYW4gc2V0IHRoZSB2YWx1ZSBmb3IgQ1NIIGlu
IGNvbmZpZy5oIGJ5IHNldHRpbmcgZnVsbF9jc2guCglmdWxsX2NzaD0keHh4CiAgICBlbGlmIFsg
LWYgIiRjc2giIF07IHRoZW4KCWVjaG8gIkNvdWxkbid0IGZpbmQgdGNzaC4gIENzaC1iYXNlZCBn
bG9iYmluZyBtaWdodCBiZSBicm9rZW4uIgogICAgZmkKZmkKCiMgU2hpbXBlaSBZYW1hc2hpdGEg
PHNoaW1wZWlAc29jcmF0ZXMucGF0bmV0LmNhbHRlY2guZWR1PgojIE1lc3NhZ2UtSWQ6IDwzM0VG
MTYzNC5CMzZCNjUwMEBwb2JveC5jb20+CiMKIyBUaGUgRFIyIG9mIE1rTGludXggKG9zbmFtZT1s
aW51eCxhcmNobmFtZT1wcGMtbGludXgpIG1heSBuZWVkCiMgc3BlY2lhbCBmbGFncyBwYXNzZWQg
aW4gb3JkZXIgZm9yIGR5bmFtaWMgbG9hZGluZyB0byB3b3JrLgojIGluc3RlYWQgb2YgdGhlIHJl
Y29tbWVuZGVkOgojCiMgY2NkbGZsYWdzPSctcmR5bmFtaWMnCiMKIyBpdCBzaG91bGQgYmU6CiMg
Y2NkbGZsYWdzPSctV2wsLUUnCiMKIyBTbyBpZiB5b3VyIERSMiAoRFIzIGNhbWUgb3V0IHN1bW1l
ciAxOTk4LCBjb25zaWRlciB1cGdyYWRpbmcpCiMgaGFzIHByb2JsZW1zIHdpdGggZHluYW1pYyBs
b2FkaW5nLCB1bmNvbW1lbnQgdGhlCiMgZm9sbG93aW5nIHRocmVlIGxpbmVzLCBtYWtlIGRpc3Rj
bGVhbiwgYW5kIHJlLUNvbmZpZ3VyZToKI2Nhc2UgImB1bmFtZSAtciB8IHNlZCAncy9eWzAtOS4t
XSovLydgYGFyY2hgIiBpbgojJ29zZm1hY2gzcHBjJykgY2NkbGZsYWdzPSctV2wsLUUnIDs7CiNl
c2FjCgpjYXNlICJgdW5hbWUgLW1gIiBpbgpzcGFyYyopCgljYXNlICIkY2NjZGxmbGFncyIgaW4K
CSotZnBpYyopIGNjY2RsZmxhZ3M9ImBlY2hvICRjY2NkbGZsYWdzfHNlZCAncy8tZnBpYy8tZlBJ
Qy8nYCIgOzsKCSotZlBJQyopIDs7CgkqKQkgY2NjZGxmbGFncz0iJGNjY2RsZmxhZ3MgLWZQSUMi
IDs7Cgllc2FjCgk7Owplc2FjCgojIFN1U0U4LjIgaGFzIC91c3IvbGliL2xpYm5kYm0qIHdoaWNo
IGFyZSBsZCBzY3JpcHRzIHJhdGhlciB0aGFuCiMgdHJ1ZSBsaWJyYXJpZXMuIFRoZSBzY3JpcHRz
IGNhdXNlIGJpbmRpbmcgYWdhaW5zdCBzdGF0aWMKIyB2ZXJzaW9uIG9mIC1sZ2RibSB3aGljaCBp
cyBhIGJhZCBpZGVhLiBTbyBpZiB3ZSBoYXZlICdubScKIyBtYWtlIHN1cmUgaXQgY2FuIHJlYWQg
dGhlIGZpbGUKIyBOSS1TIDIwMDMvMDgvMDcKaWYgWyAtciAvdXNyL2xpYi9saWJuZGJtLnNvICAt
YSAgLXggL3Vzci9iaW4vbm0gXSA7IHRoZW4KICAgaWYgL3Vzci9iaW4vbm0gL3Vzci9saWIvbGli
bmRibS5zbyA+L2Rldi9udWxsIDI+JjEgOyB0aGVuCiAgICBlY2hvICdZb3VyIHNoYXJlZCAtbG5k
Ym0gc2VlbXMgdG8gYmUgYSByZWFsIGxpYnJhcnkuJwogICBlbHNlCiAgICBlY2hvICdZb3VyIHNo
YXJlZCAtbG5kYm0gaXMgbm90IGEgcmVhbCBsaWJyYXJ5LicKICAgIHNldCBgZWNobyBYICIkbGli
c3dhbnRlZCAifCBzZWQgLWUgJ3MvIG5kYm0gLyAvJ2AKICAgIHNoaWZ0CiAgICBsaWJzd2FudGVk
PSIkKiIKICAgZmkKZmkKCgojIFRoaXMgc2NyaXB0IFVVL3VzZXRocmVhZHMuY2J1IHdpbGwgZ2V0
ICdjYWxsZWQtYmFjaycgYnkgQ29uZmlndXJlCiMgYWZ0ZXIgaXQgaGFzIHByb21wdGVkIHRoZSB1
c2VyIGZvciB3aGV0aGVyIHRvIHVzZSB0aHJlYWRzLgpjYXQgPiBVVS91c2V0aHJlYWRzLmNidSA8
PCdFT0NCVScKaWYgZ2V0Y29uZiBHTlVfTElCUFRIUkVBRF9WRVJTSU9OIHwgZ3JlcCBOUFRMID4v
ZGV2L251bGwgMj4vZGV2L251bGwKdGhlbgogICAgdGhyZWFkc2hhdmVwaWRzPSIiCmVsc2UKICAg
IHRocmVhZHNoYXZlcGlkcz0iLURUSFJFQURTX0hBVkVfUElEUyIKZmkKY2FzZSAiJHVzZXRocmVh
ZHMiIGluCiRkZWZpbmV8dHJ1ZXxbeVldKikKICAgICAgICBjY2ZsYWdzPSItRF9SRUVOVFJBTlQg
LURfR05VX1NPVVJDRSAkdGhyZWFkc2hhdmVwaWRzICRjY2ZsYWdzIgogICAgICAgIGlmIGVjaG8g
JGxpYnN3YW50ZWQgfCBncmVwIC12IHB0aHJlYWQgPi9kZXYvbnVsbAogICAgICAgIHRoZW4KICAg
ICAgICAgICAgc2V0IGBlY2hvIFggIiRsaWJzd2FudGVkICJ8IHNlZCAtZSAncy8gYyAvIHB0aHJl
YWQgYyAvJ2AKICAgICAgICAgICAgc2hpZnQKICAgICAgICAgICAgbGlic3dhbnRlZD0iJCoiCiAg
ICAgICAgZmkKCgkjIFNvbWVob3cgYXQgbGVhc3QgaW4gRGViaWFuIDIuMiB0aGVzZSBtYW5hZ2Ug
dG8gZXNjYXBlCgkjIHRoZSAjZGVmaW5lIGZvcmVzdCBvZiA8ZmVhdHVyZXMuaD4gYW5kIDx0aW1l
Lmg+IHNvIHRoYXQKCSMgdGhlIGhhc3Byb3RvIG1hY3JvIG9mIENvbmZpZ3VyZSBkb2Vzbid0IHNl
ZSB0aGVzZSBwcm90b3MsCgkjIGV2ZW4gd2l0aCB0aGUgLURfR05VX1NPVVJDRS4KCglkX2FzY3Rp
bWVfcl9wcm90bz0iJGRlZmluZSIKCWRfY3J5cHRfcl9wcm90bz0iJGRlZmluZSIKCWRfY3RpbWVf
cl9wcm90bz0iJGRlZmluZSIKCWRfZ210aW1lX3JfcHJvdG89IiRkZWZpbmUiCglkX2xvY2FsdGlt
ZV9yX3Byb3RvPSIkZGVmaW5lIgoJZF9yYW5kb21fcl9wcm90bz0iJGRlZmluZSIKCgk7Owplc2Fj
CkVPQ0JVCgpjYXQgPiBVVS91c2VsYXJnZWZpbGVzLmNidSA8PCdFT0NCVScKIyBUaGlzIHNjcmlw
dCBVVS91c2VsYXJnZWZpbGVzLmNidSB3aWxsIGdldCAnY2FsbGVkLWJhY2snIGJ5IENvbmZpZ3Vy
ZQojIGFmdGVyIGl0IGhhcyBwcm9tcHRlZCB0aGUgdXNlciBmb3Igd2hldGhlciB0byB1c2UgbGFy
Z2UgZmlsZXMuCmNhc2UgIiR1c2VsYXJnZWZpbGVzIiBpbgonJ3wkZGVmaW5lfHRydWV8W3lZXSop
CiMgS2VlcCB0aGlzIGluIHRoZSBsZWZ0IG1hcmdpbi4KY2NmbGFnc191c2VsYXJnZWZpbGVzPSIt
RF9MQVJHRUZJTEVfU09VUkNFIC1EX0ZJTEVfT0ZGU0VUX0JJVFM9NjQiCgoJY2NmbGFncz0iJGNj
ZmxhZ3MgJGNjZmxhZ3NfdXNlbGFyZ2VmaWxlcyIKCTs7CmVzYWMKRU9DQlUKCiMgUHVyaWZ5IGZh
aWxzIHRvIGxpbmsgUGVybCBpZiBhICItbGMiIGlzIHBhc3NlZCBpbnRvIGl0cyBsaW5rZXIKIyBk
dWUgdG8gZHVwbGljYXRlIHN5bWJvbHMuCmNhc2UgIiRQVVJJRlkiIGluCiRkZWZpbmV8dHJ1ZXxb
eVldKikKICAgIHNldCBgZWNobyBYICIkbGlic3dhbnRlZCAifCBzZWQgLWUgJ3MvIGMgLyAvJ2AK
ICAgIHNoaWZ0CiAgICBsaWJzd2FudGVkPSIkKiIKICAgIDs7CmVzYWMKCiMgSWYgd2UgYXJlIHVz
aW5nIGcrKyB3ZSBtdXN0IHVzZSBubSBhbmQgZm9yY2Ugb3Vyc2VsdmVzIHRvIHVzZQojIHRoZSAv
dXNyL2xpYi9saWJjLmEgKHJlc2V0dGluZyB0aGUgbGliYyBiZWxvdyB0byBhbiBlbXB0eSBzdHJp
bmcKIyBtYWtlcyBDb25maWd1cmUgdG8gbG9vayBmb3IgdGhlIHJpZ2h0IG9uZSkgYmVjYXVzZSB0
aGUgc3ltYm9sCiMgc2Nhbm5pbmcgdHJpY2tzIG9mIENvbmZpZ3VyZSB3aWxsIGNyYXNoIGFuZCBi
dXJuIGhvcnJpYmx5LgpjYXNlICIkY2MiIGluCipnKysqKSB1c2VubT10cnVlCiAgICAgICBsaWJj
PScnCiAgICAgICA7Owplc2FjCgojIElmIHVzaW5nIGcrKywgdGhlIENvbmZpZ3VyZSBzY2FuIGZv
ciBkbG9wZW4oKSBhbmQgKGVzcGVjaWFsbHkpCiMgZGxlcnJvcigpIG1pZ2h0IGZhaWwsIGVhc2ll
ciBqdXN0IHRvIGZvcmNpYmx5IGhpbnQgdGhlbSBpbi4KY2FzZSAiJGNjIiBpbgoqZysrKikKICBk
X2Rsb3Blbj0nZGVmaW5lJwogIGRfZGxlcnJvcj0nZGVmaW5lJwogIDs7CmVzYWMKCiMgVW5kZXIg
c29tZSBjaXJjdW1zdGFuY2VzIGxpYmRiIGNhbiBnZXQgYnVpbHQgaW4gc3VjaCBhIHdheSBhcyB0
bwojIG5lZWQgcHRocmVhZCBleHBsaWNpdGx5IGxpbmtlZC4KCmxpYmRiX25lZWRzX3B0aHJlYWQ9
Ik4iCgppZiBlY2hvICIgJGxpYnN3YW50ZWQgIiB8IGdyZXAgLXYgIiBwdGhyZWFkICIgPi9kZXYv
bnVsbAp0aGVuCiAgIGlmIGVjaG8gIiAkbGlic3dhbnRlZCAiIHwgZ3JlcCAiIGRiICIgPi9kZXYv
bnVsbAogICB0aGVuCiAgICAgZm9yIERCRElSIGluICRnbGlicHRoCiAgICAgZG8KICAgICAgIERC
TElCPSIkREJESVIvbGliZGIuc28iCiAgICAgICBpZiBbIC1mICREQkxJQiBdCiAgICAgICB0aGVu
CiAgICAgICAgIGlmIG5tIC11ICREQkxJQiB8IGdyZXAgcHRocmVhZCA+L2Rldi9udWxsCiAgICAg
ICAgIHRoZW4KICAgICAgICAgICBpZiBsZGQgJERCTElCIHwgZ3JlcCBwdGhyZWFkID4vZGV2L251
bGwKICAgICAgICAgICB0aGVuCiAgICAgICAgICAgICBsaWJkYl9uZWVkc19wdGhyZWFkPSJOIgog
ICAgICAgICAgIGVsc2UKICAgICAgICAgICAgIGxpYmRiX25lZWRzX3B0aHJlYWQ9IlkiCiAgICAg
ICAgICAgZmkKICAgICAgICAgZmkKICAgICAgIGZpCiAgICAgZG9uZQogICBmaQpmaQoKY2FzZSAi
JGxpYmRiX25lZWRzX3B0aHJlYWQiIGluCiAgIlkiKQogICAgbGlic3dhbnRlZD0iJGxpYnN3YW50
ZWQgcHRocmVhZCIKICAgIDs7CmVzYWMK==',
);
my %files = (
'freebsd' => 'freebsd.sh',
'netbsd' => 'netbsd.sh',
'openbsd' => 'openbsd.sh',
'linux' => 'linux.sh',
);
sub hint_file {
my $os = shift;
$os = shift if eval { $os->isa(__PACKAGE__) };
$os = $^O unless $os;
return unless defined $hints{ $os };
my $content = decode_base64( $hints{ $os } );
return $content unless wantarray;
return ( $files{ $os }, $content );
}
qq'nudge nudge wink wink';
__END__
=pod
=head1 NAME
Devel::PatchPerl::Hints - replacement 'hints' files
=head1 VERSION
version 0.32
=head1 SYNOPSIS
use Devel::PatchPerl::Hints;
if ( my $content = Devel::PatchPerl::Hints->hint_file() ) {
chmod 0755, 'hints/netbsd.sh' or die "$!";
open my $hints, '>', 'hints/netbsd.sh' or die "$!";
print $hints $content;
close $hints;
}
=head1 DESCRIPTION
Sometimes there is a problem with Perls C<hints> file for a particular
perl port. This module provides fixed C<hints> files encoded using
C<MIME::Base64>.
=head1 FUNCTION
The function is exported, but has to implicitly imported into the
requesting package.
use Devel::PatchPerl::Hints qw[hint_file];
It may also be called as a class method:
use Devel::PatchPerl::Hints;
my $content = Devel::PatchPerl::Hints->hint_file();
=over
=item C<hint_file>
Takes an optional argument which is the OS name ( as would be returned by C<$^O> ).
By default it will use C<$^O>.
In a scalar context, Will return the decoded content of the C<hints> file suitable for writing straight to a
file handle or undef list if there isn't an applicable C<hints> file for the given or derived
OS.
If called in a list context, will return a list, the first item will be the name of the C<hints> file that
will need to be amended, the second item will be a string with the decoded content of the C<hints> file suitable
for writing straight to a file handle. Otherwise an empty list will be returned.
=back
=head1 AUTHOR
Chris Williams <chris@bingosnet.co.uk>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011 by Chris Williams and Marcus Holland-Moritz.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
DEVEL_PATCHPERL_HINTS
$fatpacked{"File/pushd.pm"} = <<'FILE_PUSHD';
package File::pushd;
$VERSION = '1.00';
@EXPORT = qw( pushd tempd );
@ISA = qw( Exporter );
use 5.004;
use strict;
#use warnings;
use Exporter;
use Carp;
use Cwd qw( cwd abs_path );
use File::Path qw( rmtree );
use File::Temp qw();
use File::Spec;
use overload
q{""} => sub { File::Spec->canonpath( $_[0]->{_pushd} ) },
fallback => 1;
#--------------------------------------------------------------------------#
# pushd()
#--------------------------------------------------------------------------#
sub pushd {
my ($target_dir) = @_;
my $orig = cwd;
my $dest;
eval { $dest = $target_dir ? abs_path( $target_dir ) : $orig };
croak "Can't locate directory $target_dir: $@" if $@;
if ($dest ne $orig) {
chdir $dest or croak "Can't chdir to $dest\: $!";
}
my $self = bless {
_pushd => $dest,
_original => $orig
}, __PACKAGE__;
return $self;
}
#--------------------------------------------------------------------------#
# tempd()
#--------------------------------------------------------------------------#
sub tempd {
my $dir = pushd( File::Temp::tempdir( CLEANUP => 0 ) );
$dir->{_tempd} = 1;
return $dir;
}
#--------------------------------------------------------------------------#
# preserve()
#--------------------------------------------------------------------------#
sub preserve {
my $self = shift;
return 1 if ! $self->{"_tempd"};
if ( @_ == 0 ) {
return $self->{_preserve} = 1;
}
else {
return $self->{_preserve} = $_[0] ? 1 : 0;
}
}
#--------------------------------------------------------------------------#
# DESTROY()
# Revert to original directory as object is destroyed and cleanup
# if necessary
#--------------------------------------------------------------------------#
sub DESTROY {
my ($self) = @_;
my $orig = $self->{_original};
chdir $orig if $orig; # should always be so, but just in case...
if ( $self->{_tempd} &&
!$self->{_preserve} ) {
eval { rmtree( $self->{_pushd} ) };
carp $@ if $@;
}
}
1; #this line is important and will help the module return a true value
__END__
=begin wikidoc
= NAME
File::pushd - change directory temporarily for a limited scope
= VERSION
This documentation describes version %%VERSION%%.
= SYNOPSIS
use File::pushd;
chdir $ENV{HOME};
# change directory again for a limited scope
{
my $dir = pushd( '/tmp' );
# working directory changed to /tmp
}
# working directory has reverted to $ENV{HOME}
# tempd() is equivalent to pushd( File::Temp::tempdir )
{
my $dir = tempd();
}
# object stringifies naturally as an absolute path
{
my $dir = pushd( '/tmp' );
my $filename = File::Spec->catfile( $dir, "somefile.txt" );
# gives /tmp/somefile.txt
}
= DESCRIPTION
File::pushd does a temporary {chdir} that is easily and automatically
reverted, similar to {pushd} in some Unix command shells. It works by
creating an object that caches the original working directory. When the object
is destroyed, the destructor calls {chdir} to revert to the original working
directory. By storing the object in a lexical variable with a limited scope,
this happens automatically at the end of the scope.
This is very handy when working with temporary directories for tasks like
testing; a function is provided to streamline getting a temporary
directory from [File::Temp].
For convenience, the object stringifies as the canonical form of the absolute
pathname of the directory entered.
= USAGE
use File::pushd;
Using File::pushd automatically imports the {pushd} and {tempd} functions.
== pushd
{
my $dir = pushd( $target_directory );
}
Caches the current working directory, calls {chdir} to change to the target
directory, and returns a File::pushd object. When the object is
destroyed, the working directory reverts to the original directory.
The provided target directory can be a relative or absolute path. If
called with no arguments, it uses the current directory as its target and
returns to the current directory when the object is destroyed.
== tempd
{
my $dir = tempd();
}
This function is like {pushd} but automatically creates and calls {chdir} to
a temporary directory created by [File::Temp]. Unlike normal [File::Temp]
cleanup which happens at the end of the program, this temporary directory is
removed when the object is destroyed. (But also see {preserve}.) A warning
will be issued if the directory cannot be removed.
== preserve
{
my $dir = tempd();
$dir->preserve; # mark to preserve at end of scope
$dir->preserve(0); # mark to delete at end of scope
}
Controls whether a temporary directory will be cleaned up when the object is
destroyed. With no arguments, {preserve} sets the directory to be preserved.
With an argument, the directory will be preserved if the argument is true, or
marked for cleanup if the argument is false. Only {tempd} objects may be
marked for cleanup. (Target directories to {pushd} are always preserved.)
{preserve} returns true if the directory will be preserved, and false
otherwise.
= SEE ALSO
* [File::chdir]
= BUGS
Please report any bugs or feature using the CPAN Request Tracker.
Bugs can be submitted through the web interface at
[http://rt.cpan.org/Dist/Display.html?Queue=File-pushd]
When submitting a bug or request, please include a test-file or a patch to an
existing test-file that illustrates the bug or desired feature.
= AUTHOR
David A. Golden (DAGOLDEN)
= COPYRIGHT AND LICENSE
Copyright (c) 2005, 2006, 2007 by David A. Golden
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[http://www.apache.org/licenses/LICENSE-2.0]
Files produced as output though the use of this software, including
generated copies of boilerplate templates provided with this software,
shall not be considered Derivative Works, but shall be considered the
original work of the Licensor.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=end wikidoc
FILE_PUSHD
$fatpacked{"Locale/Maketext/Extract.pm"} = <<'LOCALE_MAKETEXT_EXTRACT';
package Locale::Maketext::Extract;
$Locale::Maketext::Extract::VERSION = '0.38';
use strict;
use Locale::Maketext::Lexicon();
=head1 NAME
Locale::Maketext::Extract - Extract translatable strings from source
=head1 SYNOPSIS
my $Ext = Locale::Maketext::Extract->new;
$Ext->read_po('messages.po');
$Ext->extract_file($_) for <*.pl>;
# Set $entries_are_in_gettext_format if the .pl files above use
# loc('%1') instead of loc('[_1]')
$Ext->compile($entries_are_in_gettext_format);
$Ext->write_po('messages.po');
-----------------------------------
### Specifying parser plugins ###
my $Ext = Locale::Maketext::Extract->new(
# Specify which parser plugins to use
plugins => {
# Use Perl parser, process files with extension .pl .pm .cgi
perl => [],
# Use YAML parser, process all files
yaml => ['*'],
# Use TT2 parser, process files with extension .tt2 .tt .html
# or which match the regex
tt2 => [
'tt2',
'tt',
'html',
qr/\.tt2?\./
],
# Use My::Module as a parser for all files
'My::Module' => ['*'],
},
# Warn if a parser can't process a file
warnings => 1,
# List processed files
verbose => 1,
);
=head1 DESCRIPTION
This module can extract translatable strings from files, and write
them back to PO files. It can also parse existing PO files and merge
their contents with newly extracted strings.
A command-line utility, L<xgettext.pl>, is installed with this module
as well.
The format parsers are loaded as plugins, so it is possible to define
your own parsers.
Following formats of input files are supported:
=over 4
=item Perl source files (plugin: perl)
Valid localization function names are: C<translate>, C<maketext>,
C<gettext>, C<loc>, C<x>, C<_> and C<__>.
For a slightly more accurate, but much slower Perl parser, you can use the PPI
plugin. This does not have a short name (like C<perl>), but must be specified
in full.
=item HTML::Mason (plugin: mason)
Strings inside C<E<lt>&|/lE<gt>I<...>E<lt>/&E<gt>> and
C<E<lt>&|/locE<gt>I<...>E<lt>/&E<gt>> are extracted.
=item Template Toolkit (plugin: tt2)
Valid forms are:
[% | l(arg1,argn) %]string[% END %]
[% 'string' | l(arg1,argn) %]
[% l('string',arg1,argn) %]
FILTER and | are interchangeable
l and loc are interchangeable
args are optional
=item Text::Template (plugin: text)
Sentences between C<STARTxxx> and C<ENDxxx> are extracted individually.
=item YAML (plugin: yaml)
Valid forms are _"string" or _'string', eg:
title: _"My title"
desc: _'My "quoted" string'
Quotes do not have to be escaped, so you could also do:
desc: _"My "quoted" string"
=item HTML::FormFu (plugin: formfu)
HTML::FormFu uses a config-file to generate forms, with built in
support for localizing errors, labels etc.
We extract the text after C<_loc: >:
content_loc: this is the string
message_loc: ['Max string length: [_1]', 10]
=item Generic Template (plugin: generic)
Strings inside {{...}} are extracted.
=back
=head1 METHODS
=head2 Constructor
new()
new(
plugins => {...},
warnings => 1 | 0,
verbose => 0 | 1 | 2 | 3,
)
See L</"Plugins">, L</"Warnings"> and L</"Verbose"> for details
=head2 Plugins
$ext->plugins({...});
Locale::Maketext::Extract uses plugins (see below for the list)
to parse different formats.
Each plugin can also specify which file types it can parse.
# use only the YAML plugin
# only parse files with the default extension list defined in the plugin
# ie .yaml .yml .conf
$ext->plugins({
yaml => [],
})
# use only the Perl plugin
# parse all file types
$ext->plugins({
perl => '*'
})
$ext->plugins({
tt2 => [
'tt', # matches base filename against /\.tt$/
qr/\.tt2?\./, # matches base filename against regex
\&my_filter, # codref called
]
})
sub my_filter {
my ($base_filename,$path_to_file) = @_;
return 1 | 0;
}
# Specify your own parser
# only parse files with the default extension list defined in the plugin
$ext->plugins({
'My::Extract::Parser' => []
})
By default, if no plugins are specified, then it uses all of the builtin
plugins, and overrides the file types specified in each plugin
- instead, each plugin is tried for every file.
=head3 Available plugins
=over 4
=item C<perl> : L<Locale::Maketext::Extract::Plugin::Perl>
For a slightly more accurate but much slower Perl parser, you can use
the PPI plugin. This does not have a short name, but must be specified in
full, ie: L<Locale::Maketext::Extract::Plugin::PPI>
=item C<tt2> : L<Locale::Maketext::Extract::Plugin::TT2>
=item C<yaml> : L<Locale::Maketext::Extract::Plugin::YAML>
=item C<formfu> : L<Locale::Maketext::Extract::Plugin::FormFu>
=item C<mason> : L<Locale::Maketext::Extract::Plugin::Mason>
=item C<text> : L<Locale::Maketext::Extract::Plugin::TextTemplate>
=item C<generic> : L<Locale::Maketext::Extract::Plugin::Generic>
=back
Also, see L<Locale::Maketext::Extract::Plugin::Base> for details of how to
write your own plugin.
=head2 Warnings
Because the YAML and TT2 plugins use proper parsers, rather than just regexes,
if a source file is not valid and it is unable to parse the file, then the
parser will throw an error and abort parsing.
The next enabled plugin will be tried.
By default, you will not see these errors. If you would like to see them,
then enable warnings via new(). All parse errors will be printed to STDERR.
=head2 Verbose
If you would like to see which files have been processed, which plugins were
used, and which strings were extracted, then enable C<verbose>. If no
acceptable plugin was found, or no strings were extracted, then the file
is not listed:
$ext = Locale::Extract->new( verbose => 1 | 2 | 3);
OR
xgettext.pl ... -v # files reported
xgettext.pl ... -v -v # files and plugins reported
xgettext.pl ... -v -v -v # files, plugins and strings reported
=cut
our %Known_Plugins = (
perl => 'Locale::Maketext::Extract::Plugin::Perl',
yaml => 'Locale::Maketext::Extract::Plugin::YAML',
tt2 => 'Locale::Maketext::Extract::Plugin::TT2',
text => 'Locale::Maketext::Extract::Plugin::TextTemplate',
mason => 'Locale::Maketext::Extract::Plugin::Mason',
generic => 'Locale::Maketext::Extract::Plugin::Generic',
formfu => 'Locale::Maketext::Extract::Plugin::FormFu',
);
sub new {
my $class = shift;
my %params = @_;
my $plugins = delete $params{plugins}
|| { map { $_ => '*' } keys %Known_Plugins };
Locale::Maketext::Lexicon::set_option( 'keep_fuzzy' => 1 );
my $self = bless( { header => '',
entries => {},
compiled_entries => {},
lexicon => {},
warnings => 0,
verbose => 0,
wrap => 0,
%params,
},
$class
);
$self->{verbose} ||= 0;
die "No plugins defined in new()"
unless $plugins;
$self->plugins($plugins);
return $self;
}
=head2 Accessors
header, set_header
lexicon, set_lexicon, msgstr, set_msgstr
entries, set_entries, entry, add_entry, del_entry
compiled_entries, set_compiled_entries, compiled_entry,
add_compiled_entry, del_compiled_entry
clear
=cut
sub header { $_[0]{header} || _default_header() }
sub set_header { $_[0]{header} = $_[1] }
sub lexicon { $_[0]{lexicon} }
sub set_lexicon { $_[0]{lexicon} = $_[1] || {}; delete $_[0]{lexicon}{''}; }
sub msgstr { $_[0]{lexicon}{ $_[1] } }
sub set_msgstr { $_[0]{lexicon}{ $_[1] } = $_[2] }
sub entries { $_[0]{entries} }
sub set_entries { $_[0]{entries} = $_[1] || {} }
sub compiled_entries { $_[0]{compiled_entries} }
sub set_compiled_entries { $_[0]{compiled_entries} = $_[1] || {} }
sub entry { @{ $_[0]->entries->{ $_[1] } || [] } }
sub add_entry { push @{ $_[0]->entries->{ $_[1] } }, $_[2] }
sub del_entry { delete $_[0]->entries->{ $_[1] } }
sub compiled_entry { @{ $_[0]->compiled_entries->{ $_[1] } || [] } }
sub add_compiled_entry { push @{ $_[0]->compiled_entries->{ $_[1] } }, $_[2] }
sub del_compiled_entry { delete $_[0]->compiled_entries->{ $_[1] } }
sub plugins {
my $self = shift;
if (@_) {
my @plugins;
my %params = %{ shift @_ };
foreach my $name ( keys %params ) {
my $plugin_class = $Known_Plugins{$name} || $name;
my $filename = $plugin_class . '.pm';
$filename =~ s/::/\//g;
local $@;
eval {
require $filename && 1;
1;
} or next;
push @plugins, $plugin_class->new( $params{$name} );
}
$self->{plugins} = \@plugins;
}
return $self->{plugins} || [];
}
sub clear {
$_[0]->set_header;
$_[0]->set_lexicon;
$_[0]->set_comments;
$_[0]->set_fuzzy;
$_[0]->set_entries;
$_[0]->set_compiled_entries;
}
=head2 PO File manipulation
=head3 method read_po ($file)
=cut
sub read_po {
my ( $self, $file ) = @_;
print STDERR "READING PO FILE : $file\n"
if $self->{verbose};
my $header = '';
local ( *LEXICON, $_ );
open LEXICON, $file or die $!;
while (<LEXICON>) {
( 1 .. /^$/ ) or last;
$header .= $_;
}
1 while chomp $header;
$self->set_header("$header\n");
require Locale::Maketext::Lexicon::Gettext;
my $lexicon = {};
my $comments = {};
my $fuzzy = {};
$self->set_compiled_entries( {} );
if ( defined($_) ) {
( $lexicon, $comments, $fuzzy )
= Locale::Maketext::Lexicon::Gettext->parse( $_, <LEXICON> );
}
# Internally the lexicon is in gettext format already.
$self->set_lexicon( { map _maketext_to_gettext($_), %$lexicon } );
$self->set_comments($comments);
$self->set_fuzzy($fuzzy);
close LEXICON;
}
sub msg_comment {
my $self = shift;
my $msgid = shift;
my $comment = $self->{comments}->{$msgid};
return $comment;
}
sub msg_fuzzy {
return $_[0]->{fuzzy}{$_[1]} ? ', fuzzy' : '';
}
sub set_comments {
$_[0]->{comments} = $_[1];
}
sub set_fuzzy {
$_[0]->{fuzzy} = $_[1];
}
=head3 method write_po ($file, $add_format_marker?)
=cut
sub write_po {
my ( $self, $file, $add_format_marker ) = @_;
print STDERR "WRITING PO FILE : $file\n"
if $self->{verbose};
local *LEXICON;
open LEXICON, ">$file" or die "Can't write to $file$!\n";
print LEXICON $self->header;
foreach my $msgid ( $self->msgids ) {
$self->normalize_space($msgid);
print LEXICON "\n";
if ( my $comment = $self->msg_comment($msgid) ) {
my @lines = split "\n", $comment;
print LEXICON map {"# $_\n"} @lines;
}
print LEXICON $self->msg_variables($msgid);
print LEXICON $self->msg_positions($msgid);
my $flags = $self->msg_fuzzy($msgid);
$flags.= $self->msg_format($msgid) if $add_format_marker;
print LEXICON "#$flags\n" if $flags;
print LEXICON $self->msg_out($msgid);
}
print STDERR "DONE\n\n"
if $self->{verbose};
}
=head2 Extraction
extract
extract_file
=cut
sub extract {
my $self = shift;
my $file = shift;
my $content = shift;
local $@;
my ( @messages, $total, $error_found );
$total = 0;
my $verbose = $self->{verbose};
foreach my $plugin ( @{ $self->plugins } ) {
if ( $plugin->known_file_type($file) ) {
pos($content) = 0;
my $success = eval { $plugin->extract($content); 1; };
if ($success) {
my $entries = $plugin->entries;
if ( $verbose > 1 && @$entries ) {
push @messages,
" - "
. ref($plugin)
. ' - Strings extracted : '
. ( scalar @$entries );
}
for my $entry (@$entries) {
my ( $string, $line, $vars ) = @$entry;
$self->add_entry( $string => [ $file, $line, $vars ] );
if ( $verbose > 2 ) {
$vars = '' if !defined $vars;
# pad string
$string =~ s/\n/\n /g;
push @messages,
sprintf( qq[ - %-8s "%s" (%s)],
$line . ':',
$string, $vars
),
;
}
}
$total += @$entries;
}
else {
$error_found++;
if ( $self->{warnings} ) {
push @messages,
"Error parsing '$file' with plugin "
. ( ref $plugin )
. ": \n $@\n";
}
}
$plugin->clear;
}
}
print STDERR " * $file\n - Total strings extracted : $total"
. ( $error_found ? ' [ERROR ] ' : '' ) . "\n"
if $verbose
&& ( $total || $error_found );
print STDERR join( "\n", @messages ) . "\n"
if @messages;
}
sub extract_file {
my ( $self, $file ) = @_;
local ( $/, *FH );
open FH, $file or die "Error reading from file '$file' : $!";
my $content = scalar <FH>;
$self->extract( $file => $content );
close FH;
}
=head2 Compilation
=head3 compile($entries_are_in_gettext_style?)
Merges the C<entries> into C<compiled_entries>.
If C<$entries_are_in_gettext_style> is true, the previously extracted entries
are assumed to be in the B<Gettext> style (e.g. C<%1>).
Otherwise they are assumed to be in B<Maketext> style (e.g. C<[_1]>) and are
converted into B<Gettext> style before merging into C<compiled_entries>.
The C<entries> are I<not> cleared after each compilation; use
C<->set_entries()> to clear them if you need to extract from sources with
varying styles.
=cut
sub compile {
my ( $self, $entries_are_in_gettext_style ) = @_;
my $entries = $self->entries;
my $lexicon = $self->lexicon;
my $comp = $self->compiled_entries;
while ( my ( $k, $v ) = each %$entries ) {
my $compiled_key = ( ($entries_are_in_gettext_style)
? $k
: _maketext_to_gettext($k)
);
$comp->{$compiled_key} = $v;
$lexicon->{$compiled_key} = ''
unless exists $lexicon->{$compiled_key};
}
return %$lexicon;
}
=head3 normalize_space
=cut
my %Escapes = map { ( "\\$_" => eval("qq(\\$_)") ) } qw(t r f b a e);
sub normalize_space {
my ( $self, $msgid ) = @_;
my $nospace = $msgid;
$nospace =~ s/ +$//;
return
unless ( !$self->has_msgid($msgid) and $self->has_msgid($nospace) );
$self->set_msgstr( $msgid => $self->msgstr($nospace)
. ( ' ' x ( length($msgid) - length($nospace) ) ) );
}
=head2 Lexicon accessors
msgids, has_msgid,
msgstr, set_msgstr
msg_positions, msg_variables, msg_format, msg_out
=cut
sub msgids { sort keys %{ $_[0]{lexicon} } }
sub has_msgid {
my $msg_str = $_[0]->msgstr( $_[1] );
return defined $msg_str ? length $msg_str : 0;
}
sub msg_positions {
my ( $self, $msgid ) = @_;
my %files = ( map { ( " $_->[0]:$_->[1]" => 1 ) }
$self->compiled_entry($msgid) );
return $self->{wrap}
? join( "\n", ( map { '#:' . $_ } sort( keys %files ) ), '' )
: join( '', '#:', sort( keys %files ), "\n" );
}
sub msg_variables {
my ( $self, $msgid ) = @_;
my $out = '';
my %seen;
foreach my $entry ( grep { $_->[2] } $self->compiled_entry($msgid) ) {
my ( $file, $line, $var ) = @$entry;
$var =~ s/^\s*,\s*//;
$var =~ s/\s*$//;
$out .= "#. ($var)\n" unless !length($var) or $seen{$var}++;
}
return $out;
}
sub msg_format {
my ( $self, $msgid ) = @_;
return ", perl-maketext-format"
if $msgid =~ /%(?:[1-9]\d*|\w+\([^\)]*\))/;
return '';
}
sub msg_out {
my ( $self, $msgid ) = @_;
my $msgstr = $self->msgstr($msgid);
return "msgid " . _format($msgid) . "msgstr " . _format($msgstr);
}
=head2 Internal utilities
_default_header
_maketext_to_gettext
_escape
_format
=cut
sub _default_header {
return << '.';
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
.
}
sub _maketext_to_gettext {
my $text = shift;
return '' unless defined $text;
$text =~ s{((?<!~)(?:~~)*)\[_([1-9]\d*|\*)\]}
{$1%$2}g;
$text =~ s{((?<!~)(?:~~)*)\[([A-Za-z#*]\w*),([^\]]+)\]}
{"$1%$2(" . _escape($3) . ')'}eg;
$text =~ s/~([\~\[\]])/$1/g;
return $text;
}
sub _escape {
my $text = shift;
$text =~ s/\b_([1-9]\d*)/%$1/g;
return $text;
}
sub _format {
my $str = shift;
$str =~ s/(?=[\\"])/\\/g;
while ( my ( $char, $esc ) = each %Escapes ) {
$str =~ s/$esc/$char/g;
}
return "\"$str\"\n" unless $str =~ /\n/;
my $multi_line = ( $str =~ /\n(?!\z)/ );
$str =~ s/\n/\\n"\n"/g;
if ( $str =~ /\n"$/ ) {
chop $str;
}
else {
$str .= "\"\n";
}
return $multi_line ? qq(""\n"$str) : qq("$str);
}
1;
=head1 ACKNOWLEDGMENTS
Thanks to Jesse Vincent for contributing to an early version of this
module.
Also to Alain Barbet, who effectively re-wrote the source parser with a
flex-like algorithm.
=head1 SEE ALSO
L<xgettext.pl>, L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2003-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
LOCALE_MAKETEXT_EXTRACT
$fatpacked{"Locale/Maketext/Extract/Plugin/Base.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_PLUGIN_BASE';
package Locale::Maketext::Extract::Plugin::Base;
use strict;
use File::Basename qw(fileparse);
=head1 NAME
Locale::Maketext::Extract::Plugin::Base - Base module for format parser plugins
=head1 SYNOPSIS
package My::Parser::Plugin;
use base qw(Locale::Maketext::Extract::Plugin::Base);
sub file_types {
return [qw( ext ext2 )]
}
sub extract {
my $self = shift;
my $filename = shift;
local $_ = shift;
my $line = 1;
while (my $found = $self->routine_to_extract_strings) {
$self->add_entry($str,[$filename,$line,$vars])
}
return;
}
=head1 DESCRIPTION
All format parser plugins in Locale::Maketext::Extract inherit from
Locale::Maketext::Extract::Plugin::Base.
If you want to write your own custom parser plugin, you will need to inherit
from this module, and provide C<file_types()> and C<extract()> methods,
as shown above.
=head1 METHODS
=over 4
=item new()
$plugin = My::Parser->new(
@file_types # Optionally specify a list of recognised file types
)
=cut
sub new {
my $class = shift;
my $self = bless {
entries => [],
}, $class;
$self->_compile_file_types(@_);
return $self;
}
=item add_entry()
$plugin->add_entry($str,$line,$vars)
=cut
sub add_entry {
my $self = shift;
push @{$self->{entries}},[@_];
}
=item C<entries()>
$entries = $plugin->entries;
=cut
#===================================
sub entries {
#===================================
my $self = shift;
return $self->{entries};
}
=item C<clear()>
$plugin->clear
Clears all stored entries.
=cut
#===================================
sub clear {
#===================================
my $self = shift;
$self->{entries}=[];
}
=item file_types()
@default_file_types = $plugin->file_types
Returns a list of recognised file types that your module knows how to parse.
Each file type can be one of:
=over 4
=item * A plain string
'pl' => base filename is matched against qr/\.pl$/
'*' => all files are accepted
=item * A regex
qr/\.tt2?\./ => base filename is matched against this regex
=item * A codref
sub {} => this codref is called as $coderef->($base_filename,$path_to_file)
It should return true or false
=back
=cut
sub file_types {
die "Please override sub file_types() to return "
. "a list of recognised file extensions, or regexes";
}
=item extract()
$plugin->extract($filecontents);
extract() is the method that will be called to process the contents of the
current file.
When it finds a string that should be extracted, it should call
$self->add_entry($string,$line,$vars])
where C<$vars> refers to any arguments that are being passed to the localise
function. For instance:
l("You found [quant,_1,file,files]",files_found)
string: "You found [quant,_1,file,files]"
vars : (files_found)
IMPORTANT: a single plugin instance is used for all files, so if you plan
on storing state information in the C<$plugin> object, this should be cleared
out at the beginning of C<extract()>
=cut
sub extract {
die "Please override sub extract()";
}
sub _compile_file_types {
my $self = shift;
my @file_types
= ref $_[0] eq 'ARRAY'
? @{ shift @_ }
: @_;
@file_types = $self->file_types
unless @file_types;
my @checks;
if ( grep { $_ eq '*' } @file_types ) {
$self->{file_checks} = [ sub {1} ];
return;
}
foreach my $type (@file_types) {
if ( ref $type eq 'CODE' ) {
push @checks, $type;
next;
}
else {
my $regex
= ref $type
? $type
: qr/^.*\.\Q$type\E$/;
push @checks, sub { $_[0] =~ m/$regex/ };
}
}
$self->{file_checks} = \@checks;
}
=item known_file_type()
if ($plugin->known_file_type($filename_with_path)) {
....
}
Determines whether the current file should be handled by this parser, based
either on the list of file_types specified when this object was created,
or the default file_types specified in the module.
=cut
sub known_file_type {
my $self = shift;
my ( $name, $path ) = fileparse( shift @_ );
foreach my $check ( @{ $self->{file_checks} } ) {
return 1 if $check->( $name, $path );
}
return 0;
}
=back
=head1 SEE ALSO
=over 4
=item L<xgettext.pl>
for extracting translatable strings from common template
systems and perl source files.
=item L<Locale::Maketext::Lexicon>
=item L<Locale::Maketext::Extract::Plugin::Perl>
=item L<Locale::Maketext::Extract::Plugin::PPI>
=item L<Locale::Maketext::Extract::Plugin::TT2>
=item L<Locale::Maketext::Extract::Plugin::YAML>
=item L<Locale::Maketext::Extract::Plugin::FormFu>
=item L<Locale::Maketext::Extract::Plugin::Mason>
=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
=head1 AUTHORS
Clinton Gormley [DRTECH] E<lt>clinton@traveljury.comE<gt>
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
1;
LOCALE_MAKETEXT_EXTRACT_PLUGIN_BASE
$fatpacked{"Locale/Maketext/Extract/Plugin/FormFu.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_PLUGIN_FORMFU';
package Locale::Maketext::Extract::Plugin::FormFu;
use strict;
use base qw(Locale::Maketext::Extract::Plugin::Base);
=head1 NAME
Locale::Maketext::Extract::Plugin::FormFu - FormFu format parser
=head1 SYNOPSIS
$plugin = Locale::Maketext::Extract::Plugin::FormFu->new(
$lexicon # A Locale::Maketext::Extract object
@file_types # Optionally specify a list of recognised file types
)
$plugin->extract($filename,$filecontents);
=head1 DESCRIPTION
HTML::FormFu uses a config-file to generate forms, with built in support
for localizing errors, labels etc.
=head1 SHORT PLUGIN NAME
formfu
=head1 VALID FORMATS
We extract the text after any key which ends in C<_loc>:
content_loc: this is the string
message_loc: ['Max length [_1]', 10]
=head1 KNOWN FILE TYPES
=over 4
=item .yaml
=item .yml
=item .conf
=back
=head1 REQUIRES
L<YAML>
=head1 NOTES
The docs for the YAML module describes it as alpha code. It is not as tolerant
of errors as L<YAML::Syck>. However, because it is pure Perl, it is easy
to hook into.
I have seen it enter endless loops, so if xgettext.pl hangs, try running it
again with C<--verbose --verbose> (twice) enabled, so that you can see if
the fault lies with YAML. If it does, either correct the YAML source file,
or use the file_types to exclude that file.
=cut
sub file_types {
return qw( yaml yml conf );
}
sub extract {
my $self = shift;
my $data = shift;
my $y = Locale::Maketext::Extract::Plugin::FormFu::Extractor->new();
$y->load($data);
foreach my $entry ( @{ $y->found } ) {
$self->add_entry(@$entry);
}
}
package Locale::Maketext::Extract::Plugin::FormFu::Extractor;
use base qw(YAML::Loader);
#===================================
sub new {
#===================================
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{found} = [];
return $self;
}
#===================================
sub _check_key {
#===================================
my $self = shift;
my ( $key, $value, $line ) = @_;
my ( $str, $vars );
if ( ref $value ) {
return if ref $value ne 'ARRAY';
$str = shift @$value;
$vars = join( ', ', @$value );
}
else {
$str = $value;
}
return
unless $key
&& $key =~ /_loc$/
&& defined $str;
push @{ $self->{found} }, [ $str, $line, $vars ];
}
#===================================
sub _parse_mapping {
#===================================
my $self = shift;
my ($anchor) = @_;
my $mapping = {};
$self->anchor2node->{$anchor} = $mapping;
my $key;
while ( not $self->done
and $self->indent == $self->offset->[ $self->level ] )
{
# If structured key:
if ( $self->{content} =~ s/^\?\s*// ) {
$self->preface( $self->content );
$self->_parse_next_line(YAML::Loader::COLLECTION);
$key = $self->_parse_node();
$key = "$key";
}
# If "default" key (equals sign)
elsif ( $self->{content} =~ s/^\=\s*// ) {
$key = YAML::Loader::VALUE;
}
# If "comment" key (slash slash)
elsif ( $self->{content} =~ s/^\=\s*// ) {
$key = YAML::Loader::COMMENT;
}
# Regular scalar key:
else {
$self->inline( $self->content );
$key = $self->_parse_inline();
$key = "$key";
$self->content( $self->inline );
$self->inline('');
}
unless ( $self->{content} =~ s/^:\s*// ) {
$self->die('YAML_LOAD_ERR_BAD_MAP_ELEMENT');
}
$self->preface( $self->content );
my $line = $self->line;
$self->_parse_next_line(YAML::Loader::COLLECTION);
my $value = $self->_parse_node();
if ( exists $mapping->{$key} ) {
$self->warn('YAML_LOAD_WARN_DUPLICATE_KEY');
}
else {
$mapping->{$key} = $value;
$self->_check_key( $key, $value, $line );
}
}
return $mapping;
}
#===================================
sub _parse_inline_mapping {
#===================================
my $self = shift;
my ($anchor) = @_;
my $node = {};
my $start_line = $self->{_start_line};
$self->anchor2node->{$anchor} = $node;
$self->die('YAML_PARSE_ERR_INLINE_MAP')
unless $self->{inline} =~ s/^\{\s*//;
while ( not $self->{inline} =~ s/^\s*\}// ) {
my $key = $self->_parse_inline();
$self->die('YAML_PARSE_ERR_INLINE_MAP')
unless $self->{inline} =~ s/^\: \s*//;
my $value = $self->_parse_inline();
if ( exists $node->{$key} ) {
$self->warn('YAML_LOAD_WARN_DUPLICATE_KEY');
}
else {
$node->{$key} = $value;
$self->_check_key( $key, $value, $start_line );
}
next if $self->inline =~ /^\s*\}/;
$self->die('YAML_PARSE_ERR_INLINE_MAP')
unless $self->{inline} =~ s/^\,\s*//;
}
return $node;
}
#===================================
sub _parse_next_line {
#===================================
my $self = shift;
$self->{_start_line} = $self->line;
$self->SUPER::_parse_next_line(@_);
}
#===================================
sub found {
#===================================
my $self = shift;
return $self->{found};
}
=head1 SEE ALSO
=over 4
=item L<xgettext.pl>
for extracting translatable strings from common template
systems and perl source files.
=item L<YAML>
=item L<HTML::FormFu>
=item L<Locale::Maketext::Lexicon>
=item L<Locale::Maketext::Extract::Plugin::Base>
=item L<Locale::Maketext::Extract::Plugin::Perl>
=item L<Locale::Maketext::Extract::Plugin::TT2>
=item L<Locale::Maketext::Extract::Plugin::YAML>
=item L<Locale::Maketext::Extract::Plugin::Mason>
=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
=head1 AUTHORS
Clinton Gormley E<lt>clint@traveljury.comE<gt>
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
1;
LOCALE_MAKETEXT_EXTRACT_PLUGIN_FORMFU
$fatpacked{"Locale/Maketext/Extract/Plugin/Generic.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_PLUGIN_GENERIC';
package Locale::Maketext::Extract::Plugin::Generic;
use strict;
use base qw(Locale::Maketext::Extract::Plugin::Base);
=head1 NAME
Locale::Maketext::Extract::Plugin::Generic - Generic template parser
=head1 SYNOPSIS
$plugin = Locale::Maketext::Extract::Plugin::Generic->new(
$lexicon # A Locale::Maketext::Extract object
@file_types # Optionally specify a list of recognised file types
)
$plugin->extract($filename,$filecontents);
=head1 DESCRIPTION
Extracts strings to localise from generic templates.
=head1 SHORT PLUGIN NAME
generic
=head1 VALID FORMATS
Strings inside {{...}} are extracted.
=head1 KNOWN FILE TYPES
=over 4
=item All file types
=back
=cut
sub file_types {
return qw( * );
}
sub extract {
my $self = shift;
local $_ = shift;
my $line = 1;
# Generic Template:
$line = 1; pos($_) = 0;
while (m/\G(.*?(?<!\{)\{\{(?!\{)(.*?)\}\})/sg) {
my ($vars, $str) = ('', $2);
$line += ( () = ($1 =~ /\n/g) ); # cryptocontext!
$self->add_entry($str, $line, $vars );
}
my $quoted = '(\')([^\\\']*(?:\\.[^\\\']*)*)(\')|(\")([^\\\"]*(?:\\.[^\\\"]*)*)(\")';
# Comment-based mark: "..." # loc
$line = 1; pos($_) = 0;
while (m/\G(.*?($quoted)[\}\)\],;]*\s*\#\s*loc\s*$)/smog) {
my $str = substr($2, 1, -1);
$line += ( () = ( $1 =~ /\n/g ) ); # cryptocontext!
$str =~ s/\\(["'])/$1/g;
$self->add_entry($str, $line, '' );
}
# Comment-based pair mark: "..." => "..." # loc_pair
$line = 1; pos($_) = 0;
while (m/\G(.*?(\w+)\s*=>\s*($quoted)[\}\)\],;]*\s*\#\s*loc_pair\s*$)/smg) {
my $key = $2;
my $val = substr($3, 1, -1);
$line += ( () = ( $1 =~ /\n/g ) ); # cryptocontext!
$key =~ s/\\(["'])/$1/g;
$val =~ s/\\(["'])/$1/g;
$self->add_entry($val, $line, '' );
}
}
=head1 SEE ALSO
=over 4
=item L<xgettext.pl>
for extracting translatable strings from common template
systems and perl source files.
=item L<Locale::Maketext::Lexicon>
=item L<Locale::Maketext::Extract::Plugin::Base>
=item L<Locale::Maketext::Extract::Plugin::FormFu>
=item L<Locale::Maketext::Extract::Plugin::Perl>
=item L<Locale::Maketext::Extract::Plugin::TT2>
=item L<Locale::Maketext::Extract::Plugin::YAML>
=item L<Locale::Maketext::Extract::Plugin::Mason>
=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
=back
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
1;
LOCALE_MAKETEXT_EXTRACT_PLUGIN_GENERIC
$fatpacked{"Locale/Maketext/Extract/Plugin/Mason.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_PLUGIN_MASON';
package Locale::Maketext::Extract::Plugin::Mason;
use strict;
use base qw(Locale::Maketext::Extract::Plugin::Base);
=head1 NAME
Locale::Maketext::Extract::Plugin::Mason - Mason format parser
=head1 SYNOPSIS
$plugin = Locale::Maketext::Extract::Plugin::Mason->new(
$lexicon # A Locale::Maketext::Extract object
@file_types # Optionally specify a list of recognised file types
)
$plugin->extract($filename,$filecontents);
=head1 DESCRIPTION
Extracts strings to localise from Mason files.
=head1 SHORT PLUGIN NAME
mason
=head1 VALID FORMATS
Strings inside <&|/l>...</&> and <&|/loc>...</&> are extracted.
=head1 KNOWN FILE TYPES
=over 4
=item All file types
=back
=cut
sub file_types {
return qw( * );
}
sub extract {
my $self = shift;
local $_ = shift;
my $line = 1;
# HTML::Mason
while (m!\G(.*?<&\|/l(?:oc)?(.*?)&>(.*?)</&>)!sg) {
my ($vars, $str) = ($2, $3);
$line += ( () = ($1 =~ /\n/g) ); # cryptocontext!
$self->add_entry($str, $line, $vars );
}
}
=head1 SEE ALSO
=over 4
=item L<xgettext.pl>
for extracting translatable strings from common template
systems and perl source files.
=item L<Locale::Maketext::Lexicon>
=item L<Locale::Maketext::Extract::Plugin::Base>
=item L<Locale::Maketext::Extract::Plugin::FormFu>
=item L<Locale::Maketext::Extract::Plugin::Perl>
=item L<Locale::Maketext::Extract::Plugin::TT2>
=item L<Locale::Maketext::Extract::Plugin::YAML>
=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
1;
LOCALE_MAKETEXT_EXTRACT_PLUGIN_MASON
$fatpacked{"Locale/Maketext/Extract/Plugin/PPI.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_PLUGIN_PPI';
package Locale::Maketext::Extract::Plugin::PPI;
use strict;
use base qw(Locale::Maketext::Extract::Plugin::Base);
use PPI();
=head1 NAME
Locale::Maketext::Extract::Plugin::PPI - Perl format parser
=head1 SYNOPSIS
$plugin = Locale::Maketext::Extract::Plugin::PPI->new(
$lexicon # A Locale::Maketext::Extract object
@file_types # Optionally specify a list of recognised file types
)
$plugin->extract($filename,$filecontents);
=head1 DESCRIPTION
Does exactly the same thing as the L<Locale::Maketext::Extract::Plugin::Perl>
parser, but more accurately, and more slowly. Considerably more slowly! For this
reason it isn't a built-in plugin.
=head1 SHORT PLUGIN NAME
none - the module must be specified in full
=head1 VALID FORMATS
Valid localization function names are:
=over 4
=item translate
=item maketext
=item gettext
=item loc
=item x
=item _
=item __
=back
=head1 KNOWN FILE TYPES
=over 4
=item .pm
=item .pl
=item .cgi
=back
=cut
sub file_types {
return qw( pm pl cgi );
}
my %subnames = map { $_ => 1 } qw (translate maketext gettext loc x __);
#===================================
sub extract {
#===================================
my $self = shift;
my $text = shift;
my $doc = PPI::Document->new( \$text, index_locations => 1 );
foreach my $statement ( @{ $doc->find('PPI::Statement') } ) {
my @children = $statement->schildren;
while ( my $child = shift @children ) {
next
unless @children
&& ( $child->isa('PPI::Token::Word')
&& $subnames{ $child->content }
|| $child->isa('PPI::Token::Magic')
&& $child->content eq '_' );
my $list = shift @children;
next
unless $list->isa('PPI::Structure::List')
&& $list->schildren;
$self->_check_arg_list($list);
}
}
}
#===================================
sub _check_arg_list {
#===================================
my $self = shift;
my $list = shift;
my @args = ( $list->schildren )[0]->schildren;
my $final_string = '';
my ( $line, $mode );
while ( my $string_el = shift @args ) {
return
unless $string_el->isa('PPI::Token::Quote')
|| $string_el->isa('PPI::Token::HereDoc');
$line ||= $string_el->location->[0];
my $string;
if ( $string_el->isa('PPI::Token::HereDoc') ) {
$string = join( '', $string_el->heredoc );
$mode
= $string_el->{_mode} eq 'interpolate'
? 'double'
: 'literal';
}
else {
$string = $string_el->string;
$mode
= $string_el->isa('PPI::Token::Quote::Literal') ? 'literal'
: ( $string_el->isa('PPI::Token::Quote::Double')
|| $string_el->isa('PPI::Token::Quote::Interpolate') )
? 'double'
: 'single';
}
if ( $mode eq 'double' ) {
return
if !!( $string =~ /(?<!\\)(?:\\\\)*[\$\@]/ );
$string = eval qq("$string");
}
elsif ( $mode eq 'single' ) {
$string =~ s/\\'/'/g;
}
# $string =~ s/(?<!\\)\\//g;
$string =~ s/\\\\/\\/g;
# unless $mode eq 'literal';
$final_string .= $string;
my $next_op = shift @args;
last
unless $next_op
&& $next_op->isa('PPI::Token::Operator')
&& $next_op->content eq '.';
}
return unless $final_string;
my $vars = join( '', map { $_->content } @args );
$self->add_entry( $final_string, $line, $vars );
}
=head1 SEE ALSO
=over 4
=item L<xgettext.pl>
for extracting translatable strings from common template
systems and perl source files.
=item L<Locale::Maketext::Lexicon>
=item L<Locale::Maketext::Extract::Plugin::Base>
=item L<Locale::Maketext::Extract::Plugin::Perl>
=item L<Locale::Maketext::Extract::Plugin::FormFu>
=item L<Locale::Maketext::Extract::Plugin::Perl>
=item L<Locale::Maketext::Extract::Plugin::TT2>
=item L<Locale::Maketext::Extract::Plugin::YAML>
=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
1;
LOCALE_MAKETEXT_EXTRACT_PLUGIN_PPI
$fatpacked{"Locale/Maketext/Extract/Plugin/Perl.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_PLUGIN_PERL';
package Locale::Maketext::Extract::Plugin::Perl;
use strict;
use base qw(Locale::Maketext::Extract::Plugin::Base);
=head1 NAME
Locale::Maketext::Extract::Plugin::Perl - Perl format parser
=head1 SYNOPSIS
$plugin = Locale::Maketext::Extract::Plugin::Perl->new(
$lexicon # A Locale::Maketext::Extract object
@file_types # Optionally specify a list of recognised file types
)
$plugin->extract($filename,$filecontents);
=head1 DESCRIPTION
Extracts strings to localise (including HEREDOCS and
concatenated strings) from Perl code.
This Perl parser is very fast and very good, but not perfect - it does make
mistakes. The PPI parser (L<Locale::Maketext::Extract::Plugin::PPI>) is more
accurate, but a lot slower, and so is not enabled by default.
=head1 SHORT PLUGIN NAME
perl
=head1 VALID FORMATS
Valid localization function names are:
=over 4
=item translate
=item maketext
=item gettext
=item loc
=item x
=item _
=item __
=back
=head1 KNOWN FILE TYPES
=over 4
=item .pm
=item .pl
=item .cgi
=back
=cut
use constant NUL => 0;
use constant BEG => 1;
use constant PAR => 2;
use constant HERE => 10;
use constant QUO1 => 3;
use constant QUO2 => 4;
use constant QUO3 => 5;
use constant QUO4 => 6;
use constant QUO5 => 7;
use constant QUO6 => 8;
use constant QUO7 => 9;
sub file_types {
return qw( pm pl cgi );
}
sub extract {
my $self = shift;
local $_ = shift;
local $SIG{__WARN__} = sub { die @_ };
# Perl code:
my ( $state, $line_offset, $str, $str_part, $vars, $quo, $heredoc )
= ( 0, 0 );
my $orig = 1 + ( () = ( ( my $__ = $_ ) =~ /\n/g ) );
PARSER: {
$_ = substr( $_, pos($_) ) if ( pos($_) );
my $line = $orig - ( () = ( ( my $__ = $_ ) =~ /\n/g ) );
# various ways to spell the localization function
$state == NUL
&& m/\b(translate|maketext|gettext|__?|loc(?:ali[sz]e)?|x)/gc
&& do { $state = BEG; redo };
$state == BEG && m/^([\s\t\n]*)/gc && redo;
# begin ()
$state == BEG
&& m/^([\S\(])\s*/gc
&& do { $state = ( ( $1 eq '(' ) ? PAR : NUL ); redo };
# concat
$state == PAR
&& defined($str)
&& m/^(\s*\.\s*)/gc
&& do { $line_offset += ( () = ( ( my $__ = $1 ) =~ /\n/g ) ); redo };
# str_part
$state == PAR && defined($str_part) && do {
if ( ( $quo == QUO1 ) || ( $quo == QUO5 ) ) {
$str_part =~ s/\\([\\'])/$1/g
if ($str_part); # normalize q strings
}
elsif ( $quo != QUO6 ) {
$str_part =~ s/(\\(?:[0x]..|c?.))/"qq($1)"/eeg
if ($str_part); # normalize qq / qx strings
}
$str .= $str_part;
undef $str_part;
undef $quo;
redo;
};
# begin or end of string
$state == PAR && m/^(\')/gc && do { $state = $quo = QUO1; redo };
$state == QUO1 && m/^([^'\\]+)/gc && do { $str_part .= $1; redo };
$state == QUO1 && m/^((?:\\.)+)/gcs && do { $str_part .= $1; redo };
$state == QUO1 && m/^\'/gc && do { $state = PAR; redo };
$state == PAR && m/^\"/gc && do { $state = $quo = QUO2; redo };
$state == QUO2 && m/^([^"\\]+)/gc && do { $str_part .= $1; redo };
$state == QUO2 && m/^((?:\\.)+)/gcs && do { $str_part .= $1; redo };
$state == QUO2 && m/^\"/gc && do { $state = PAR; redo };
$state == PAR && m/^\`/gc && do { $state = $quo = QUO3; redo };
$state == QUO3 && m/^([^\`]*)/gc && do { $str_part .= $1; redo };
$state == QUO3 && m/^\`/gc && do { $state = PAR; redo };
$state == PAR && m/^qq\{/gc && do { $state = $quo = QUO4; redo };
$state == QUO4 && m/^([^\}]*)/gc && do { $str_part .= $1; redo };
$state == QUO4 && m/^\}/gc && do { $state = PAR; redo };
$state == PAR && m/^q\{/gc && do { $state = $quo = QUO5; redo };
$state == QUO5 && m/^([^\}]*)/gc && do { $str_part .= $1; redo };
$state == QUO5 && m/^\}/gc && do { $state = PAR; redo };
# find heredoc terminator, then get the
#heredoc and go back to current position
$state == PAR
&& m/^<<\s*\'/gc
&& do { $state = $quo = QUO6; $heredoc = ''; redo };
$state == QUO6 && m/^([^'\\\n]+)/gc && do { $heredoc .= $1; redo };
$state == QUO6 && m/^((?:\\.)+)/gc && do { $heredoc .= $1; redo };
$state == QUO6
&& m/^\'/gc
&& do { $state = HERE; $heredoc =~ s/\\\'/\'/g; redo };
$state == PAR
&& m/^<<\s*\"/gc
&& do { $state = $quo = QUO7; $heredoc = ''; redo };
$state == QUO7 && m/^([^"\\\n]+)/gc && do { $heredoc .= $1; redo };
$state == QUO7 && m/^((?:\\.)+)/gc && do { $heredoc .= $1; redo };
$state == QUO7
&& m/^\"/gc
&& do { $state = HERE; $heredoc =~ s/\\\"/\"/g; redo };
$state == PAR
&& m/^<<(\w*)/gc
&& do { $state = HERE; $quo = QUO7; $heredoc = $1; redo };
# jump ahaid and get the heredoc, then s/// also
# resets the pos and we are back at the current pos
$state == HERE
&& m/^.*\r?\n/gc
&& s/\G(.*?\r?\n)$heredoc(\r?\n)//s
&& do { $state = PAR; $str_part .= $1; $line_offset++; redo };
# end ()
#
$state == PAR && m/^\s*[\)]/gc && do {
$state = NUL;
$vars =~ s/[\n\r]//g if ($vars);
$self->add_entry( $str,
$line - ( () = $str =~ /\n/g ) - $line_offset,
$vars )
if $str;
undef $str;
undef $vars;
undef $heredoc;
$line_offset = 0;
redo;
};
# a line of vars
$state == PAR && m/^([^\)]*)/gc && do { $vars .= "$1\n"; redo };
}
}
=head1 SEE ALSO
=over 4
=item L<xgettext.pl>
for extracting translatable strings from common template
systems and perl source files.
=item L<Locale::Maketext::Lexicon>
=item L<Locale::Maketext::Extract::Plugin::Base>
=item L<Locale::Maketext::Extract::Plugin::PPI>
=item L<Locale::Maketext::Extract::Plugin::FormFu>
=item L<Locale::Maketext::Extract::Plugin::TT2>
=item L<Locale::Maketext::Extract::Plugin::YAML>
=item L<Locale::Maketext::Extract::Plugin::Mason>
=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
1;
LOCALE_MAKETEXT_EXTRACT_PLUGIN_PERL
$fatpacked{"Locale/Maketext/Extract/Plugin/TT2.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_PLUGIN_TT2';
package Locale::Maketext::Extract::Plugin::TT2;
use strict;
use base qw(Locale::Maketext::Extract::Plugin::Base);
use Template::Constants qw( :debug );
use Template::Parser;
=head1 NAME
Locale::Maketext::Extract::Plugin::TT2 - Template Toolkit format parser
=head1 SYNOPSIS
$plugin = Locale::Maketext::Extract::Plugin::TT2->new(
$lexicon # A Locale::Maketext::Extract object
@file_types # Optionally specify a list of recognised file types
)
$plugin->extract($filename,$filecontents);
=head1 DESCRIPTION
Extracts strings to localise from Template Toolkit templates.
=head1 SHORT PLUGIN NAME
tt2
=head1 VALID FORMATS
Valid formats are:
=over 4
=item [% |l(args) %]string[% END %]
=item [% 'string' | l(args) %]
=item [% l('string',args) %]
=back
l and loc are interchangeable.
| and FILTER are interchangeable.
=head1 KNOWN FILE TYPES
=over 4
=item .tt
=item .tt2
=item .html
=item .tt.*
=item .tt2.*
=back
=head1 REQUIRES
L<Template>
=head1 NOTES
=over 4
=item *
B<BEWARE> Using the C<loc> form can give false positives if you use the Perl parser
plugin on TT files. If you want to use the C<loc> form, then you should
specify the file types that you want to the Perl plugin to parse, or enable
the default file types, eg:
xgetext.pl -P perl .... # default file types
xgettext.pl -P perl=pl,pm ... # specified file types
=item *
The string-to-be-localised must be a string, not a variable. We try not
to extract calls to your localise function which contain variables eg:
l('string',arg) # extracted
l(var,arg) # not extracted
This doesn't work for block filters, so don't do that. Eg:
[% FILTER l %]
string [% var %] # BAD!
[% END %]
=item *
Getting the right line number is difficult in TT. Often it'll be a range
of lines, or it may be thrown out by the use of PRE_CHOMP or POST_CHOMP. It will
always be within a few lines of the correct location.
=item *
If you have PRE/POST_CHOMP enabled by default in your templates, then you should
extract the strings using the same values. In order to set them, you can
use the following wrapper script:
#!/usr/bin/perl
use Locale::Maketext::Extract::Run qw(xgettext);
use Locale::Maketext::Extract::Plugin::TT2();
%Locale::Maketext::Extract::Plugin::TT2::PARSER_OPTIONS = (
PRE_CHOMP => 1, # or 2
POST_CHOMP => 1, # or 2
# Also START/END_TAG, ANYCASE, INTERPOLATE, V1DOLLAR, EVAL_PERL
);
xgettext(@ARGV);
=back
=cut
# import strip_quotes
*strip_quotes
= \&Locale::Maketext::Extract::Plugin::TT2::Directive::strip_quotes;
our %PARSER_OPTIONS;
#===================================
sub file_types {
#===================================
return ( qw( tt tt2 html ), qr/\.tt2?\./ );
}
my %Escapes = map { ( "\\$_" => eval("qq(\\$_)") ) } qw(t n r f b a e);
#===================================
sub extract {
#===================================
my $self = shift;
my $data = shift;
$Template::Directive::PRETTY = 1;
my $parser =
Locale::Maketext::Extract::Plugin::TT2::Parser->new(
%PARSER_OPTIONS,
FACTORY => 'Locale::Maketext::Extract::Plugin::TT2::Directive',
FILE_INFO => 0,
);
_init_overrides($parser);
$parser->{extracted} = [];
$Locale::Maketext::Extract::Plugin::TT2::Directive::PARSER
= $parser; # hack
$parser->parse($data)
|| die $parser->error;
foreach my $entry ( @{ $parser->{extracted} } ) {
$entry->[2] =~ s/^\((.*)\)$/$1/s; # Remove () from vars
$_ =~ s/\\'/'/gs # Unescape \'
for @{$entry}[ 0, 2 ];
$entry->[2] =~ s/\\(?!")/\\\\/gs; # Escape all \ not followed by "
# Escape argument lists correctly
while ( my ( $char, $esc ) = each %Escapes ) {
$entry->[2] =~ s/$esc/$char/g;
}
$entry->[1] =~ s/\D+.*$//;
$self->add_entry(@$entry);
}
}
#===================================
sub _init_overrides {
#===================================
my $parser = shift;
# Override the concatenation sub to return _ instead of .
my $states = $parser->{STATES};
foreach my $state ( @{$states} ) {
if ( my $CAT_no = $state->{ACTIONS}{CAT} ) {
my $CAT_rule_no
= $states->[ $states->[$CAT_no]{GOTOS}{expr} ]->{DEFAULT};
# override the TT::Grammar sub which cats two args
$parser->{RULES}[ -$CAT_rule_no ][2] = sub {
my $first = ( $_[1] );
my $second = ( $_[3] );
if ( strip_quotes($first) && strip_quotes($second) ) {
# both are literal
return "'${first}${second}'";
}
else {
# at least one is an ident
return "$_[1] _ $_[3]";
}
};
last;
}
}
}
#===================================
#===================================
package Locale::Maketext::Extract::Plugin::TT2::Parser;
#===================================
#===================================
use base 'Template::Parser';
# disabled location() because it was adding unneccessary text
# to filter blocks
#===================================
sub location {''}
#===================================
# Custom TT parser for Locale::Maketext::Lexicon
#
# Written by Andy Wardley http://wardley.org/
#
# 18 September 2008
#
#-----------------------------------------------------------------------
# custom directive generator to capture filters, variables and
# massage a few other elements to make life easy.
#-----------------------------------------------------------------------
#===================================
#===================================
package Locale::Maketext::Extract::Plugin::TT2::Directive;
#===================================
#===================================
use base 'Template::Directive';
our $PARSER;
#===================================
sub textblock {
#===================================
my ( $class, $text ) = @_;
$text =~ s/([\\'])/\\$1/g;
return "'$text'";
}
#===================================
sub ident {
#===================================
my ( $class, $ident ) = @_;
return "NULL" unless @$ident;
if ( scalar @$ident <= 2 && !$ident->[1] ) {
my $var = $ident->[0];
$var =~ s/^'(.+)'$/$1/;
return $var;
}
else {
my @source = @$ident;
my @dotted;
my $first = 1;
my $first_literal;
while (@source) {
my ( $name, $args ) = splice( @source, 0, 2 );
if ($first) {
strip_quotes($name);
my $first_arg = $args && @$args ? $args->[0] : '';
$first_literal = strip_quotes($first_arg);
$first--;
}
elsif ( !strip_quotes($name) && $name =~ /\D/ ) {
$name = '$' . $name;
}
$name .= join_args($args);
push( @dotted, $name );
}
if ( $first_literal
&& ( $ident->[0] eq "'l'" or $ident->[0] eq "'loc'" ) )
{
my $string = shift @{ $ident->[1] };
strip_quotes($string);
$string =~ s/\\\\/\\/g;
my $args = join_args( $ident->[1] );
push @{ $PARSER->{extracted} },
[ $string, ${ $PARSER->{LINE} }, $args ];
}
return join( '.', @dotted );
}
}
#===================================
sub text {
#===================================
my ( $class, $text ) = @_;
$text =~ s/\\/\\\\/g;
return "'$text'";
}
#===================================
sub quoted {
#===================================
my ( $class, $items ) = @_;
return '' unless @$items;
return ( $items->[0] ) if scalar @$items == 1;
return '(' . join( ' _ ', @$items ) . ')';
}
#===================================
sub args {
#===================================
my ( $class, $args ) = @_;
my $hash = shift @$args;
push( @$args, '{ ' . join( ', ', @$hash ) . ' }' ) # named params
if @$hash;
return $args;
}
#===================================
sub get {
#===================================
my ( $class, $expr ) = @_;
return $expr;
}
#===================================
sub filter {
#===================================
my ( $class, $lnameargs, $block ) = @_;
my ( $name, $args, $alias ) = @$lnameargs;
$name = $name->[0];
return ''
unless $name eq "'l'"
or $name eq "'loc'";
if ( strip_quotes($block) ) {
$block =~ s/\\\\/\\/g;
$args = join_args( $class->args($args) );
# NOTE: line number is at end of block, and can be a range
my ($end) = ( ${ $PARSER->{LINE} } =~ /^(\d+)/ );
my $start = $end;
# rewind line count for newlines
$start -= $block =~ tr/\n//;
my $line = $start == $end ? $start : "$start-$end";
push @{ $PARSER->{extracted} }, [ $block, $line, $args ];
}
return '';
}
# strips outer single quotes from a string (modifies original string)
# returns true if stripped, or false
#===================================
sub strip_quotes {
#===================================
return scalar $_[0] =~ s/^'(.*)'$/$1/s;
}
#===================================
sub join_args {
#===================================
my $args = shift;
return '' unless $args && @$args;
my @new_args = (@$args);
for (@new_args) {
s/\\\\/\\/g;
if ( strip_quotes($_) ) {
s/"/\\"/g;
$_ = qq{"$_"};
}
}
return '(' . join( ', ', @new_args ) . ')';
}
=head1 ACKNOWLEDGEMENTS
Thanks to Andy Wardley for writing the Template::Directive subclass which
made this possible.
=head1 SEE ALSO
=over 4
=item L<xgettext.pl>
for extracting translatable strings from common template
systems and perl source files.
=item L<Locale::Maketext::Lexicon>
=item L<Locale::Maketext::Extract::Plugin::Base>
=item L<Locale::Maketext::Extract::Plugin::FormFu>
=item L<Locale::Maketext::Extract::Plugin::Perl>
=item L<Locale::Maketext::Extract::Plugin::YAML>
=item L<Locale::Maketext::Extract::Plugin::Mason>
=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
=item L<Locale::Maketext::Extract::Plugin::Generic>
=item L<Template::Toolkit>
=back
=head1 AUTHORS
Clinton Gormley E<lt>clint@traveljury.comE<gt>
Andy Wardley http://wardley.org
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
1;
LOCALE_MAKETEXT_EXTRACT_PLUGIN_TT2
$fatpacked{"Locale/Maketext/Extract/Plugin/TextTemplate.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_PLUGIN_TEXTTEMPLATE';
package Locale::Maketext::Extract::Plugin::TextTemplate;
use strict;
use base qw(Locale::Maketext::Extract::Plugin::Base);
use vars qw($VERSION);
$VERSION = '0.31';
=head1 NAME
Locale::Maketext::Extract::Plugin::TextTemplate - Text::Template format parser
=head1 SYNOPSIS
$plugin = Locale::Maketext::Extract::Plugin::TextTemplate->new(
$lexicon # A Locale::Maketext::Extract object
@file_types # Optionally specify a list of recognised file types
)
$plugin->extract($filename,$filecontents);
=head1 DESCRIPTION
Extracts strings to localise from Text::Template files
=head1 SHORT PLUGIN NAME
text
=head1 VALID FORMATS
Sentences between STARTxxx and ENDxxx are extracted individually.
=head1 KNOWN FILE TYPES
=over 4
=item All file types
=back
=cut
sub file_types {
return qw( * );
}
sub extract {
my $self = shift;
local $_ = shift;
my $line = 1; pos($_) = 0;
# Text::Template
if ($_=~/^STARTTEXT$/m and $_=~ /^ENDTEXT$/m) {
require HTML::Parser;
require Lingua::EN::Sentence;
{
package Locale::Maketext::Extract::Plugin::TextTemplate::Parser;
our @ISA = 'HTML::Parser';
*{'text'} = sub {
my ($self, $str, $is_cdata) = @_;
my $sentences = Lingua::EN::Sentence::get_sentences($str) or return;
$str =~ s/\n/ /g; $str =~ s/^\s+//; $str =~ s/\s+$//;
$self->add_entry($str , $line);
};
}
my $p = Locale::Maketext::Extract::Plugin::TextTemplate::Parser->new;
while (m/\G((.*?)^(?:START|END)[A-Z]+$)/smg) {
my ($str) = ($2);
$line += ( () = ($1 =~ /\n/g) ); # cryptocontext!
$p->parse($str); $p->eof;
}
$_ = '';
}
}
=head1 SEE ALSO
=over 4
=item L<xgettext.pl>
for extracting translatable strings from common template
systems and perl source files.
=item L<Locale::Maketext::Lexicon>
=item L<Locale::Maketext::Extract::Plugin::Base>
=item L<Locale::Maketext::Extract::Plugin::FormFu>
=item L<Locale::Maketext::Extract::Plugin::Perl>
=item L<Locale::Maketext::Extract::Plugin::TT2>
=item L<Locale::Maketext::Extract::Plugin::YAML>
=item L<Locale::Maketext::Extract::Plugin::Mason>
=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
1;
LOCALE_MAKETEXT_EXTRACT_PLUGIN_TEXTTEMPLATE
$fatpacked{"Locale/Maketext/Extract/Plugin/YAML.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_PLUGIN_YAML';
package Locale::Maketext::Extract::Plugin::YAML;
use strict;
use base qw(Locale::Maketext::Extract::Plugin::Base);
=head1 NAME
Locale::Maketext::Extract::Plugin::YAML - YAML format parser
=head1 SYNOPSIS
$plugin = Locale::Maketext::Extract::Plugin::YAML->new(
$lexicon # A Locale::Maketext::Extract object
@file_types # Optionally specify a list of recognised file types
)
$plugin->extract($filename,$filecontents);
=head1 DESCRIPTION
Extracts strings to localise from YAML files.
=head1 SHORT PLUGIN NAME
yaml
=head1 VALID FORMATS
Valid formats are:
=over 4
=item *
key: _"string"
=item *
key: _'string'
=item *
key: _'string with embedded 'quotes''
=item *
key: |-
_'my folded
string
to translate'
Note, the left hand side of the folded string must line up with the C<_>,
otherwise YAML adds spaces at the beginning of each line.
=item *
key: |-
_'my block
string
to translate
'
Note, you must use the trailing C<-> so that YAMl doesn't add a carriage
return after your final quote.
=back
=head1 KNOWN FILE TYPES
=over 4
=item .yaml
=item .yml
=item .conf
=back
=head1 REQUIRES
L<YAML>
=head1 NOTES
The docs for the YAML module describes it as alpha code. It is not as tolerant
of errors as L<YAML::Syck>. However, because it is pure Perl, it is easy
to hook into.
I have seen it enter endless loops, so if xgettext.pl hangs, try running it
again with C<--verbose --verbose> (twice) enabled, so that you can see if
the fault lies with YAML. If it does, either correct the YAML source file,
or use the file_types to exclude that file.
=cut
sub file_types {
return qw( yaml yml conf );
}
sub extract {
my $self = shift;
my $data = shift;
my $y = Locale::Maketext::Extract::Plugin::YAML::Extractor->new();
$y->load($data);
foreach my $entry (@{$y->found}) {
$self->add_entry(@$entry)
}
}
package Locale::Maketext::Extract::Plugin::YAML::Extractor;
use base qw(YAML::Loader);
#===================================
sub new {
#===================================
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{found} = [];
return $self;
}
#===================================
sub check_scalar {
#===================================
my $self = shift;
my $node = $_[0];
if ( defined $node && !ref $node && $node =~ /^__?(["'])(.+)\1$/s ) {
my $string = $2;
my $line = $_[1];
push @{ $self->{found} }, [ $string, $line ];
}
return $node;
}
sub _parse_node {
my $self = shift;
my $line = $self->{_start_line}||=length($self->preface) ? $self->line - 1 : $self->line;
my $node = $self->SUPER::_parse_node(@_);
$self->{start_line} = 0;
return $self->check_scalar($node,$line);
}
sub _parse_inline_seq {
my $self = shift;
my $line = $self->{_start_line}||=$self->line;
my $node = $self->SUPER::_parse_inline_seq(@_);
foreach (@$node) {
$self->check_scalar( $_, $line );
}
$self->{start_line} = 0;
return $node;
}
sub _parse_inline_mapping {
my $self = shift;
my $line = $self->{_start_line}||=$self->line;
my $node = $self->SUPER::_parse_inline_mapping(@_);
foreach ( values %$node ) {
$self->check_scalar( $_, $line );
}
$self->{start_line} = 0;
return $node;
}
#===================================
sub _parse_next_line {
#===================================
my $self = shift;
$self->{_start_line} = $self->line
if $_[0] == YAML::Loader::COLLECTION;
$self->SUPER::_parse_next_line(@_);
}
sub found {
my $self = shift;
return $self->{found};
}
=head1 SEE ALSO
=over 4
=item L<xgettext.pl>
for extracting translatable strings from common template
systems and perl source files.
=item L<YAML>
=item L<Locale::Maketext::Lexicon>
=item L<Locale::Maketext::Extract::Plugin::Base>
=item L<Locale::Maketext::Extract::Plugin::FormFu>
=item L<Locale::Maketext::Extract::Plugin::Perl>
=item L<Locale::Maketext::Extract::Plugin::TT2>
=item L<Locale::Maketext::Extract::Plugin::Mason>
=item L<Locale::Maketext::Extract::Plugin::TextTemplate>
=item L<Locale::Maketext::Extract::Plugin::Generic>
=back
=head1 AUTHORS
Clinton Gormley E<lt>clint@traveljury.comE<gt>
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
1;
LOCALE_MAKETEXT_EXTRACT_PLUGIN_YAML
$fatpacked{"Locale/Maketext/Extract/Run.pm"} = <<'LOCALE_MAKETEXT_EXTRACT_RUN';
package Locale::Maketext::Extract::Run;
$Locale::Maketext::Lexicon::Extract::Run::VERSION = '0.35';
use strict;
use vars qw( @ISA @EXPORT_OK );
use File::Spec::Functions qw(catfile);
=head1 NAME
Locale::Maketext::Extract::Run - Module interface to xgettext.pl
=head1 SYNOPSIS
use Locale::Maketext::Extract::Run 'xgettext';
xgettext(@ARGV);
=cut
use Cwd;
use Config ();
use File::Find;
use Getopt::Long;
use Locale::Maketext::Extract;
use Exporter;
use constant HAS_SYMLINK => ( $Config::Config{d_symlink} ? 1 : 0 );
@ISA = 'Exporter';
@EXPORT_OK = 'xgettext';
sub xgettext { __PACKAGE__->run(@_) }
sub run {
my $self = shift;
local @ARGV = @_;
my %opts;
Getopt::Long::Configure("no_ignore_case");
Getopt::Long::GetOptions( \%opts,
'f|files-from:s@',
'D|directory:s@',
'u|use-gettext-style|unescaped',
'g|gnu-gettext',
'o|output:s@',
'd|default-domain:s',
'p|output-dir:s@',
'P|plugin:s@',
'W|wrap!',
'w|warnings!',
'v|verbose+',
'h|help',
) or help();
help() if $opts{h};
my %extract_options = %{ $self->_parse_extract_options( \%opts ) };
my @po = @{ $opts{o} || [ ( $opts{d} || 'messages' ) . '.po' ] };
foreach my $file ( @{ $opts{f} || [] } ) {
open FILE, $file or die "Cannot open $file: $!";
while (<FILE>) {
chomp;
push @ARGV, $_ if -r and !-d;
}
}
foreach my $dir ( @{ $opts{D} || [] } ) {
File::Find::find( {
wanted => sub {
if (-d) {
$File::Find::prune
= /^(\.svn|blib|autogen|var|m4|local|CVS|\.git)$/;
return;
}
# Only extract from non-binary, normal files
return unless (-f or -s) and -T;
return
if (/\.po$|\.bak$|~|,D|,B$/i)
|| (/^[\.#]/);
push @ARGV, $File::Find::name;
},
follow => HAS_SYMLINK,
},
$dir
);
}
@ARGV = ('-') unless @ARGV;
s!^\.[/\\]!! for @ARGV;
my $cwd = getcwd();
my $Ext = Locale::Maketext::Extract->new(%extract_options);
foreach my $dir ( @{ $opts{p} || ['.'] } ) {
$Ext->extract_file($_) for grep !/\.po$/i, @ARGV;
foreach my $po (@po) {
$Ext->read_po($po) if -r $po and -s _;
$Ext->compile( $opts{u} ) or next;
$Ext->write_po( catfile( $dir, $po ), $opts{g} );
}
}
}
sub _parse_extract_options {
my $self = shift;
my $opts = shift;
# If a list of plugins is specified, then we use those modules
# plus their default list of file extensionse
# and warnings enabled by default
my %extract_options
= ( verbose => $opts->{v}, wrap => $opts->{W} || 0 );
if ( my $plugin_args = $opts->{P} ) {
# file extension with potentially multiple dots eg .tt.html
my %plugins;
foreach my $param (@$plugin_args) {
my ( $plugin, $args )
= ( $param =~ /^([a-z_]\w+(?:::\w+)*)(?:=(.+))?$/i );
die "Couldn't understand plugin option '$param'"
unless $plugin;
my @extensions;
if ($args) {
foreach my $arg ( split /,/, $args ) {
if ( $arg eq '*' ) {
@extensions = ('*');
last;
}
my ($extension) = ( $arg =~ /^\.?(\w+(?:\.\w+)*)$/ );
die "Couldn't understand '$arg' in plugin '$param'"
unless defined $extension;
push @extensions, $extension;
}
}
$plugins{$plugin} = \@extensions;
}
$extract_options{plugins} = \%plugins;
$extract_options{warnings} = exists $opts->{w} ? $opts->{w} : 1;
}
# otherwise we default to the original xgettext.pl modules
# with warnings disabled by default
else {
$extract_options{warnings} = $opts->{w};
}
return \%extract_options;
}
sub help {
local $SIG{__WARN__} = sub { };
{ exec "perldoc $0"; }
{ exec "pod2text $0"; }
}
1;
=head1 COPYRIGHT
Copyright 2003-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
LOCALE_MAKETEXT_EXTRACT_RUN
$fatpacked{"Locale/Maketext/Lexicon.pm"} = <<'LOCALE_MAKETEXT_LEXICON';
package Locale::Maketext::Lexicon;
$Locale::Maketext::Lexicon::VERSION = '0.86';
use 5.004;
use strict;
=head1 NAME
Locale::Maketext::Lexicon - Use other catalog formats in Maketext
=head1 VERSION
This document describes version 0.80 of Locale::Maketext::Lexicon,
released December 29, 2008.
=head1 SYNOPSIS
As part of a localization class, automatically glob for available
lexicons:
package Hello::I18N;
use base 'Locale::Maketext';
use Locale::Maketext::Lexicon {
'*' => [Gettext => '/usr/local/share/locale/*/LC_MESSAGES/hello.mo'],
### Uncomment to fallback when a key is missing from lexicons
# _auto => 1,
### Uncomment to decode lexicon entries into Unicode strings
# _decode => 1,
### Uncomment to load and parse everything right away
# _preload => 1,
### Uncomment to use %1 / %quant(%1) instead of [_1] / [quant, _1]
# _style => 'gettext',
};
Explicitly specify languages, during compile- or run-time:
package Hello::I18N;
use base 'Locale::Maketext';
use Locale::Maketext::Lexicon {
de => [Gettext => 'hello_de.po'],
fr => [
Gettext => 'hello_fr.po',
Gettext => 'local/hello/fr.po',
],
};
# ... incrementally add new lexicons
Locale::Maketext::Lexicon->import({
de => [Gettext => 'local/hello/de.po'],
})
Alternatively, as part of a localization subclass:
package Hello::I18N::de;
use base 'Hello::I18N';
use Locale::Maketext::Lexicon (Gettext => \*DATA);
__DATA__
# Some sample data
msgid ""
msgstr ""
"Project-Id-Version: Hello 1.3.22.1\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
#: Hello.pm:10
msgid "Hello, World!"
msgstr "Hallo, Welt!"
#: Hello.pm:11
msgid "You have %quant(%1,piece) of mail."
msgstr "Sie haben %quant(%1,Poststueck,Poststuecken)."
=head1 DESCRIPTION
This module provides lexicon-handling modules to read from other
localization formats, such as I<Gettext>, I<Msgcat>, and so on.
If you are unfamiliar with the concept of lexicon modules, please
consult L<Locale::Maketext> and the C<webl10n> HTML files in the C<docs/>
directory of this module.
A command-line utility L<xgettext.pl> is also installed with this
module, for extracting translatable strings from source files.
=head2 The C<import> function
The C<import()> function accepts two forms of arguments:
=over 4
=item (I<format> => I<source> ... )
This form takes any number of argument pairs (usually one);
I<source> may be a file name, a filehandle, or an array reference.
For each such pair, it pass the contents specified by the second
argument to B<Locale::Maketext::Lexicon::I<format>>->parse as a
plain list, and export its return value as the C<%Lexicon> hash
in the calling package.
In the case that there are multiple such pairs, the lexicon
defined by latter ones overrides earlier ones.
=item { I<language> => [ I<format>, I<source> ... ] ... }
This form accepts a hash reference. It will export a C<%Lexicon>
into the subclasses specified by each I<language>, using the process
described above. It is designed to alleviate the need to set up a
separate subclass for each localized language, and just use the catalog
files.
This module will convert the I<language> arguments into lowercase,
and replace all C<-> with C<_>, so C<zh_TW> and C<zh-tw> will both
map to the C<zh_tw> subclass.
If I<language> begins with C<_>, it is taken as an option that
controls how lexicons are parsed. See L</Options> for a list
of available options.
The C<*> is a special I<language>; it must be used in conjunction
with a filename that also contains C<*>; all matched files with
a valid language code in the place of C<*> will be automatically
prepared as a lexicon subclass. If there is multiple C<*> in
the filename, the last one is used as the language name.
=back
=head2 Options
=over 4
=item C<_auto>
If set to a true value, missing lookups on lexicons are handled
silently, as if an C<Auto> lexicon has been appended on all
language lexicons.
=item C<_decode>
If set to a true value, source entries will be converted into
utf8-strings (available in Perl 5.6.1 or later). This feature
needs the B<Encode> or B<Encode::compat> module.
Currently, only the C<Gettext> backend supports this option.
=item C<_encoding>
This option only has effect when C<_decode> is set to true.
It specifies an encoding to store lexicon entries, instead of
utf8-strings.
If C<_encoding> is set to C<locale>, the encoding from the
current locale setting is used.
=item C<_preload>
By default parsing is delayed until first use of the lexicon,
set this option to true value to parse it asap. Increment
adding lexicons forces parsing.
=back
=head2 Subclassing format handlers
If you wish to override how sources specified in different data types
are handled, please use a subclass that overrides C<lexicon_get_I<TYPE>>.
XXX: not documented well enough yet. Patches welcome.
=head1 NOTES
When you attempt to localize an entry missing in the lexicon, Maketext
will throw an exception by default. To inhibit this behaviour, override
the C<_AUTO> key in your language subclasses, for example:
$Hello::I18N::en::Lexicon{_AUTO} = 1; # autocreate missing keys
If you want to implement a new C<Lexicon::*> backend module, please note
that C<parse()> takes an array containing the B<source strings> from the
specified filehandle or filename, which are I<not> C<chomp>ed. Although
if the source is an array reference, its elements will probably not contain
any newline characters anyway.
The C<parse()> function should return a hash reference, which will be
assigned to the I<typeglob> (C<*Lexicon>) of the language module. All
it amounts to is that if the returned reference points to a tied hash,
the C<%Lexicon> will be aliased to the same tied hash if it was not
initialized previously.
=cut
our %Opts;
sub option { shift if ref( $_[0] ); $Opts{ lc $_[0] } }
sub set_option { shift if ref( $_[0] ); $Opts{ lc $_[0] } = $_[1] }
sub encoding {
my $encoding = option( @_, 'encoding' ) or return;
return $encoding unless lc($encoding) eq 'locale';
local $^W; # no warnings 'uninitialized', really.
my ( $country_language, $locale_encoding );
local $@;
eval {
require I18N::Langinfo;
$locale_encoding
= I18N::Langinfo::langinfo( I18N::Langinfo::CODESET() );
}
or eval {
require Win32::Console;
$locale_encoding = 'cp' . Win32::Console::OutputCP();
};
if ( !$locale_encoding ) {
foreach my $key (qw( LANGUAGE LC_ALL LC_MESSAGES LANG )) {
$ENV{$key} =~ /^([^.]+)\.([^.:]+)/ or next;
( $country_language, $locale_encoding ) = ( $1, $2 );
last;
}
}
if ( defined $locale_encoding
&& lc($locale_encoding) eq 'euc'
&& defined $country_language )
{
if ( $country_language =~ /^ja_JP|japan(?:ese)?$/i ) {
$locale_encoding = 'euc-jp';
}
elsif ( $country_language =~ /^ko_KR|korean?$/i ) {
$locale_encoding = 'euc-kr';
}
elsif ( $country_language =~ /^zh_CN|chin(?:a|ese)?$/i ) {
$locale_encoding = 'euc-cn';
}
elsif ( $country_language =~ /^zh_TW|taiwan(?:ese)?$/i ) {
$locale_encoding = 'euc-tw';
}
}
return $locale_encoding;
}
sub import {
my $class = shift;
return unless @_;
my %entries;
if ( UNIVERSAL::isa( $_[0], 'HASH' ) ) {
# a hashref with $lang as keys, [$format, $src ...] as values
%entries = %{ $_[0] };
}
elsif ( @_ % 2 == 0 ) {
%entries = ( '' => [ splice @_, 0, 2 ], @_ );
}
# expand the wildcard entry
if ( my $wild_entry = delete $entries{'*'} ) {
while ( my ( $format, $src ) = splice( @$wild_entry, 0, 2 ) ) {
next if ref($src); # XXX: implement globbing for the 'Tie' backend
my $pattern = quotemeta($src);
$pattern =~ s/\\\*(?=[^*]+$)/\([-\\w]+\)/g or next;
$pattern =~ s/\\\*/.*?/g;
$pattern =~ s/\\\?/./g;
$pattern =~ s/\\\[/[/g;
$pattern =~ s/\\\]/]/g;
$pattern =~ s[\\\{(.*?)\\\\}][
'(?:'.join('|', split(/,/, $1)).')'
]eg;
require File::Glob;
foreach my $file ( File::Glob::bsd_glob($src) ) {
$file =~ /$pattern/ or next;
push @{ $entries{$1} }, ( $format => $file ) if $1;
}
delete $entries{$1}
unless !defined($1)
or exists $entries{$1} and @{ $entries{$1} };
}
}
%Opts = ();
foreach my $key ( grep /^_/, keys %entries ) {
set_option( lc( substr( $key, 1 ) ) => delete( $entries{$key} ) );
}
my $OptsRef = {%Opts};
while ( my ( $lang, $entry ) = each %entries ) {
my $export = caller;
if ( length $lang ) {
# normalize language tag to Maketext's subclass convention
$lang = lc($lang);
$lang =~ s/-/_/g;
$export .= "::$lang";
}
my @pairs = @{ $entry || [] } or die "no format specified";
while ( my ( $format, $src ) = splice( @pairs, 0, 2 ) ) {
if ( defined($src) and !ref($src) and $src =~ /\*/ ) {
unshift( @pairs, $format => $_ )
for File::Glob::bsd_glob($src);
next;
}
my @content
= eval { $class->lexicon_get( $src, scalar caller(1), $lang ); };
next if $@ and $@ =~ /^next\b/;
die $@ if $@;
no strict 'refs';
eval "use $class\::$format; 1" or die $@;
if ( %{"$export\::Lexicon"} ) {
my $lexicon = \%{"$export\::Lexicon"};
if ( my $obj = tied %$lexicon ) {
# if it's our tied hash then force loading
# otherwise late load will rewrite
$obj->_force if $obj->isa(__PACKAGE__);
}
# clear the memoized cache for old entries:
Locale::Maketext->clear_isa_scan;
my $new = "$class\::$format"->parse(@content);
# avoid hash rebuild, on big sets
@{$lexicon}{ keys %$new } = values %$new;
}
else {
local $^W if $] >= 5.009; # no warnings 'once', really.
tie %{"$export\::Lexicon"}, __PACKAGE__,
{
Opts => $OptsRef,
Export => "$export\::Lexicon",
Class => "$class\::$format",
Content => \@content,
};
tied( %{"$export\::Lexicon"} )->_force
if $OptsRef->{'preload'};
}
length $lang or next;
# Avoid re-entry
my $caller = caller();
next if $export->isa($caller);
push( @{"$export\::ISA"}, scalar caller );
if ( my $style = option('style') ) {
my $cref
= $class->can( lc("_style_$style") )
->( $class, $export->can('maketext') )
or die "Unknown style: $style";
# Avoid redefinition warnings
local $SIG{__WARN__} = sub {1};
*{"$export\::maketext"} = $cref;
}
}
}
}
sub _style_gettext {
my ( $self, $orig ) = @_;
require Locale::Maketext::Lexicon::Gettext;
sub {
my $lh = shift;
my $str = shift;
return $orig->(
$lh,
Locale::Maketext::Lexicon::Gettext::_gettext_to_maketext($str), @_
);
}
}
sub TIEHASH {
my ( $class, $args ) = @_;
return bless( $args, $class );
}
{
no strict 'refs';
sub _force {
my $args = shift;
unless ( $args->{'Done'} ) {
$args->{'Done'} = 1;
local *Opts = $args->{Opts};
*{ $args->{Export} }
= $args->{Class}->parse( @{ $args->{Content} } );
$args->{'Export'}{'_AUTO'} = 1
if option('auto');
}
return $args->{'Export'};
}
sub FETCH { _force( $_[0] )->{ $_[1] } }
sub EXISTS { _force( $_[0] )->{ $_[1] } }
sub DELETE { delete _force( $_[0] )->{ $_[1] } }
sub SCALAR { scalar %{ _force( $_[0] ) } }
sub STORE { _force( $_[0] )->{ $_[1] } = $_[2] }
sub CLEAR { %{ _force( $_[0] )->{ $_[1] } } = () }
sub NEXTKEY { each %{ _force( $_[0] ) } }
sub FIRSTKEY {
my $hash = _force( $_[0] );
my $a = scalar keys %$hash;
each %$hash;
}
}
sub lexicon_get {
my ( $class, $src, $caller, $lang ) = @_;
return unless defined $src;
foreach my $type ( qw(ARRAY HASH SCALAR GLOB), ref($src) ) {
next unless UNIVERSAL::isa( $src, $type );
my $method = 'lexicon_get_' . lc($type);
die "cannot handle source $type for $src: no $method defined"
unless $class->can($method);
return $class->$method( $src, $caller, $lang );
}
# default handler
return $class->lexicon_get_( $src, $caller, $lang );
}
# for scalarrefs and arrayrefs we just dereference the $src
sub lexicon_get_scalar { ${ $_[1] } }
sub lexicon_get_array { @{ $_[1] } }
sub lexicon_get_hash {
my ( $class, $src, $caller, $lang ) = @_;
return map { $_ => $src->{$_} } sort keys %$src;
}
sub lexicon_get_glob {
my ( $class, $src, $caller, $lang ) = @_;
no strict 'refs';
local $^W if $] >= 5.009; # no warnings 'once', really.
# be extra magical and check for DATA section
if ( eof($src) and $src eq \*{"$caller\::DATA"}
or $src eq \*{"main\::DATA"} )
{
# okay, the *DATA isn't initiated yet. let's read.
#
require FileHandle;
my $fh = FileHandle->new;
my $package = ( ( $src eq \*{"main\::DATA"} ) ? 'main' : $caller );
if ( $package eq 'main' and -e $0 ) {
$fh->open($0) or die "Can't open $0: $!";
}
else {
my $level = 1;
while ( my ( $pkg, $filename ) = caller( $level++ ) ) {
next unless $pkg eq $package;
next unless -e $filename;
next;
$fh->open($filename) or die "Can't open $filename: $!";
last;
}
}
while (<$fh>) {
# okay, this isn't foolproof, but good enough
last if /^__DATA__$/;
}
return <$fh>;
}
# fh containing the lines
my $pos = tell($src);
my @lines = <$src>;
seek( $src, $pos, 0 );
return @lines;
}
# assume filename - search path, open and return its contents
sub lexicon_get_ {
my ( $class, $src, $caller, $lang ) = @_;
$src = $class->lexicon_find( $src, $caller, $lang );
defined $src or die 'next';
require FileHandle;
my $fh = FileHandle->new;
$fh->open($src) or die "Cannot read $src (called by $caller): $!";
binmode($fh);
return <$fh>;
}
sub lexicon_find {
my ( $class, $src, $caller, $lang ) = @_;
return $src if -e $src;
require File::Spec;
my @path = split '::', $caller;
push @path, $lang if length $lang;
while (@path) {
foreach (@INC) {
my $file = File::Spec->catfile( $_, @path, $src );
return $file if -e $file;
}
pop @path;
}
return undef;
}
1;
=head1 ACKNOWLEDGMENTS
Thanks to Jesse Vincent for suggesting this module to be written.
Thanks also to Sean M. Burke for coming up with B<Locale::Maketext>
in the first place, and encouraging me to experiment with alternative
Lexicon syntaxes.
Thanks also to Yi Ma Mao for providing the MO file parsing subroutine,
as well as inspiring me to implement file globbing and transcoding
support.
See the F<AUTHORS> file in the distribution for a list of people who
have sent helpful patches, ideas or comments.
=head1 SEE ALSO
L<xgettext.pl> for extracting translatable strings from common template
systems and perl source files.
L<Locale::Maketext>, L<Locale::Maketext::Lexicon::Auto>,
L<Locale::Maketext::Lexicon::Gettext>, L<Locale::Maketext::Lexicon::Msgcat>,
L<Locale::Maketext::Lexicon::Tie>
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002-2008 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
LOCALE_MAKETEXT_LEXICON
$fatpacked{"Locale/Maketext/Lexicon/Auto.pm"} = <<'LOCALE_MAKETEXT_LEXICON_AUTO';
package Locale::Maketext::Lexicon::Auto;
$Locale::Maketext::Lexicon::Auto::VERSION = '0.10';
use strict;
=head1 NAME
Locale::Maketext::Lexicon::Auto - Auto fallback lexicon for Maketext
=head1 SYNOPSIS
package Hello::I18N;
use base 'Locale::Maketext';
use Locale::Maketext::Lexicon {
en => ['Auto'],
# ... other languages
};
=head1 DESCRIPTION
This module builds a simple Lexicon hash that contains nothing but
C<( '_AUTO' =E<gt> 1)>, which tells C<Locale::Maketext> that no
localizing is needed -- just use the lookup key as the returned string.
It is especially useful if you're starting to prototype a program, and
do not want to deal with the localization files yet.
=head1 CAVEATS
If the key to C<-E<gt>maketext> begins with a C<_>, C<Locale::Maketext>
will still throw an exception. See L<Locale::Maketext/CONTROLLING LOOKUP
FAILURE> for how to prevent it.
=cut
sub parse {
+{ _AUTO => 1 };
}
1;
=head1 SEE ALSO
L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002, 2003, 2004, 2007 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
LOCALE_MAKETEXT_LEXICON_AUTO
$fatpacked{"Locale/Maketext/Lexicon/Gettext.pm"} = <<'LOCALE_MAKETEXT_LEXICON_GETTEXT';
package Locale::Maketext::Lexicon::Gettext;
$Locale::Maketext::Lexicon::Gettext::VERSION = '0.17';
use strict;
=head1 NAME
Locale::Maketext::Lexicon::Gettext - PO and MO file parser for Maketext
=head1 SYNOPSIS
Called via B<Locale::Maketext::Lexicon>:
package Hello::I18N;
use base 'Locale::Maketext';
use Locale::Maketext::Lexicon {
de => [Gettext => 'hello/de.mo'],
};
Directly calling C<parse()>:
use Locale::Maketext::Lexicon::Gettext;
my %Lexicon = %{ Locale::Maketext::Lexicon::Gettext->parse(<DATA>) };
__DATA__
#: Hello.pm:10
msgid "Hello, World!"
msgstr "Hallo, Welt!"
#: Hello.pm:11
msgid "You have %quant(%1,piece) of mail."
msgstr "Sie haben %quant(%1,Poststueck,Poststuecken)."
=head1 DESCRIPTION
This module implements a perl-based C<Gettext> parser for
B<Locale::Maketext>. It transforms all C<%1>, C<%2>, <%*>... sequences
to C<[_1]>, C<[_2]>, C<[_*]>, and so on. It accepts either plain PO
file, or a MO file which will be handled with a pure-perl parser
adapted from Imacat's C<Locale::Maketext::Gettext>.
Since version 0.03, this module also looks for C<%I<function>(I<args...>)>
in the lexicon strings, and transform it to C<[I<function>,I<args...>]>.
Any C<%1>, C<%2>... sequences inside the I<args> will have their percent
signs (C<%>) replaced by underscores (C<_>).
The name of I<function> above should begin with a letter or underscore,
followed by any number of alphanumeric characters and/or underscores.
As an exception, the function name may also consist of a single asterisk
(C<*>) or pound sign (C<#>), which are C<Locale::Maketext>'s shorthands
for C<quant> and C<numf>, respectively.
As an additional feature, this module also parses MIME-header style
metadata specified in the null msgstr (C<"">), and add them to the
C<%Lexicon> with a C<__> prefix. For example, the example above will
set C<__Content-Type> to C<text/plain; charset=iso8859-1>, without
the newline or the colon.
Any normal entry that duplicates a metadata entry takes precedence.
Hence, a C<msgid "__Content-Type"> line occurs anywhere should override
the above value.
=head1 OPTIONS
=head2 use_fuzzy
When parsing PO files, fuzzy entries (entries marked with C<#, fuzzy>)
are silently ignored. If you wish to use fuzzy entries, specify a true
value to the C<_use_fuzzy> option:
use Locale::Maketext::Lexicon {
de => [Gettext => 'hello/de.mo'],
_use_fuzzy => 1,
};
=head2 allow_empty
When parsing PO files, empty entries (entries with C<msgstr "">) are
silently ignored. If you wish to allow empty entries, specify a true
value to the C<_allow_empty> option:
use Locale::Maketext::Lexicon {
de => [Gettext => 'hello/de.mo'],
_allow_empty => 1,
};
=cut
my ( $InputEncoding, $OutputEncoding, $DoEncoding );
sub input_encoding {$InputEncoding}
sub output_encoding {$OutputEncoding}
sub parse {
my $self = shift;
my ( %var, $key, @ret );
my @metadata;
my @comments;
my @fuzzy;
$InputEncoding = $OutputEncoding = $DoEncoding = undef;
use Carp;
Carp::cluck "Undefined source called\n" unless defined $_[0];
# Check for magic string of MO files
return parse_mo( join( '', @_ ) )
if ( $_[0] =~ /^\x95\x04\x12\xde/ or $_[0] =~ /^\xde\x12\x04\x95/ );
local $^W; # no 'uninitialized' warnings, please.
require Locale::Maketext::Lexicon;
my $KeepFuzzy = Locale::Maketext::Lexicon::option('keep_fuzzy');
my $UseFuzzy = $KeepFuzzy
|| Locale::Maketext::Lexicon::option('use_fuzzy');
my $AllowEmpty = Locale::Maketext::Lexicon::option('allow_empty');
my $process = sub {
if ( length( $var{msgstr} ) and ( $UseFuzzy or !$var{fuzzy} ) ) {
push @ret, ( map transform($_), @var{ 'msgid', 'msgstr' } );
}
elsif ($AllowEmpty) {
push @ret, ( transform( $var{msgid} ), '' );
}
if ( $var{msgid} eq '' ) {
push @metadata, parse_metadata( $var{msgstr} );
}
else {
push @comments, $var{msgid}, $var{msgcomment};
}
if ( $KeepFuzzy && $var{fuzzy} ) {
push @fuzzy, $var{msgid}, 1;
}
%var = ();
};
# Parse PO files
foreach (@_) {
s/[\015\012]*\z//; # fix CRLF issues
/^(msgid|msgstr) +"(.*)" *$/
? do { # leading strings
$var{$1} = $2;
$key = $1;
}
:
/^"(.*)" *$/
? do { # continued strings
$var{$key} .= $1;
}
:
/^# (.*)$/
? do { # user comments
$var{msgcomment} .= $1 . "\n";
}
:
/^#, +(.*) *$/
? do { # control variables
$var{$_} = 1 for split( /,\s+/, $1 );
}
:
/^ *$/ && %var
? do { # interpolate string escapes
$process->($_);
}
: ();
}
# do not silently skip last entry
$process->() if keys %var != 0;
push @ret, map { transform($_) } @var{ 'msgid', 'msgstr' }
if length $var{msgstr};
push @metadata, parse_metadata( $var{msgstr} )
if $var{msgid} eq '';
return wantarray
? ( { @metadata, @ret }, {@comments}, {@fuzzy} )
: ( { @metadata, @ret } );
}
sub parse_metadata {
return map {
(/^([^\x00-\x1f\x80-\xff :=]+):\s*(.*)$/)
? ( $1 eq 'Content-Type' )
? do {
my $enc = $2;
if ( $enc =~ /\bcharset=\s*([-\w]+)/i ) {
$InputEncoding = $1 || '';
$OutputEncoding
= Locale::Maketext::Lexicon::encoding()
|| '';
$InputEncoding = 'utf8'
if $InputEncoding =~ /^utf-?8$/i;
$OutputEncoding = 'utf8'
if $OutputEncoding =~ /^utf-?8$/i;
if ( Locale::Maketext::Lexicon::option('decode')
and ( !$OutputEncoding
or $InputEncoding ne $OutputEncoding )
)
{
require Encode::compat if $] < 5.007001;
require Encode;
$DoEncoding = 1;
}
}
( "__Content-Type", $enc );
}
: ( "__$1", $2 )
: ();
} split( /\r*\n+\r*/, transform(pop) );
}
sub transform {
my $str = shift;
if ( $DoEncoding and $InputEncoding ) {
$str
= ( $InputEncoding eq 'utf8' )
? Encode::decode_utf8($str)
: Encode::decode( $InputEncoding, $str );
}
$str =~ s/\\([0x]..|c?.)/qq{"\\$1"}/eeg;
if ( $DoEncoding and $OutputEncoding ) {
$str
= ( $OutputEncoding eq 'utf8' )
? Encode::encode_utf8($str)
: Encode::encode( $OutputEncoding, $str );
}
return _gettext_to_maketext($str);
}
sub _gettext_to_maketext {
my $str = shift;
$str =~ s{([\~\[\]])}{~$1}g;
$str =~ s{
([%\\]%) # 1 - escaped sequence
|
% (?:
([A-Za-z#*]\w*) # 2 - function call
\(([^\)]*)\) # 3 - arguments
|
([1-9]\d*|\*) # 4 - variable
)
}{
$1 ? $1
: $2 ? "\[$2,"._unescape($3)."]"
: "[_$4]"
}egx;
$str;
}
sub _unescape {
join( ',',
map { /\A(\s*)%([1-9]\d*|\*)(\s*)\z/ ? "$1_$2$3" : $_ }
split( /,/, $_[0] ) );
}
# This subroutine was derived from Locale::Maketext::Gettext::readmo()
# under the Perl License; the original author is Yi Ma Mao (IMACAT).
sub parse_mo {
my $content = shift;
my $tmpl = ( substr( $content, 0, 4 ) eq "\xde\x12\x04\x95" ) ? 'V' : 'N';
# Check the MO format revision number
# There is only one revision now: revision 0.
return if unpack( $tmpl, substr( $content, 4, 4 ) ) > 0;
my ( $num, $offo, $offt );
# Number of strings
$num = unpack $tmpl, substr( $content, 8, 4 );
# Offset to the beginning of the original strings
$offo = unpack $tmpl, substr( $content, 12, 4 );
# Offset to the beginning of the translated strings
$offt = unpack $tmpl, substr( $content, 16, 4 );
my ( @metadata, @ret );
for ( 0 .. $num - 1 ) {
my ( $len, $off, $stro, $strt );
# The first word is the length of the string
$len = unpack $tmpl, substr( $content, $offo + $_ * 8, 4 );
# The second word is the offset of the string
$off = unpack $tmpl, substr( $content, $offo + $_ * 8 + 4, 4 );
# Original string
$stro = substr( $content, $off, $len );
# The first word is the length of the string
$len = unpack $tmpl, substr( $content, $offt + $_ * 8, 4 );
# The second word is the offset of the string
$off = unpack $tmpl, substr( $content, $offt + $_ * 8 + 4, 4 );
# Translated string
$strt = substr( $content, $off, $len );
# Hash it
push @metadata, parse_metadata($strt) if $stro eq '';
push @ret, ( map transform($_), $stro, $strt ) if length $strt;
}
return { @metadata, @ret };
}
1;
=head1 SEE ALSO
L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002, 2003, 2004, 2007 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
LOCALE_MAKETEXT_LEXICON_GETTEXT
$fatpacked{"Locale/Maketext/Lexicon/Msgcat.pm"} = <<'LOCALE_MAKETEXT_LEXICON_MSGCAT';
package Locale::Maketext::Lexicon::Msgcat;
$Locale::Maketext::Lexicon::Msgcat::VERSION = '0.03';
use strict;
=head1 NAME
Locale::Maketext::Lexicon::Msgcat - Msgcat catalog parser Maketext
=head1 SYNOPSIS
package Hello::I18N;
use base 'Locale::Maketext';
use Locale::Maketext::Lexicon {
en => ['Msgcat', 'en_US/hello.pl.m'],
};
package main;
my $lh = Hello::I18N->get_handle('en');
print $lh->maketext(1,2); # set 1, msg 2
print $lh->maketext("1,2"); # same thing
=head1 DESCRIPTION
This module parses one or more Msgcat catalogs in plain text format,
and returns a Lexicon hash, which may be looked up either with a
two-argument form (C<$set_id, $msg_id>) or as a single string
(C<"$set_id,$msg_id">).
=head1 NOTES
All special characters (C<[>, C<]> and C<~>) in catalogs will be
escaped so they lose their magic meanings. That means C<-E<gt>maketext>
calls to this lexicon will I<not> take any additional arguments.
=cut
sub parse {
my $set = 0;
my $msg = undef;
my ($qr, $qq, $qc) = (qr//, '', '');
my @out;
# Set up the msgcat handler
{
no strict 'refs';
no warnings 'once';
*{Locale::Maketext::msgcat} = \&_msgcat;
}
# Parse *.m files; Locale::Msgcat objects and *.cat are not yet supported.
foreach (@_) {
s/[\015\012]*\z//; # fix CRLF issues
/^\$set (\d+)/
? do { # set_id
$set = int($1);
push @out, $1, "[msgcat,$1,_1]";
}
:
/^\$quote (.)/
? do { # quote character
$qc = $1;
$qq = quotemeta($1);
$qr = qr/$qq?/;
}
:
/^(\d+) ($qr)(.*?)\2(\\?)$/
? do { # msg_id and msg_str
local $^W;
push @out, "$set," . int($1);
if ($4) {
$msg = $3;
}
else {
push @out, unescape($qq, $qc, $3);
undef $msg;
}
}
:
(defined $msg and /^($qr)(.*?)\1(\\?)$/)
? do { # continued string
local $^W;
if ($3) {
$msg .= $2;
}
else {
push @out, unescape($qq, $qc, $msg . $2);
undef $msg;
}
}
: ();
}
push @out, '' if defined $msg;
return {@out};
}
sub _msgcat {
my ($self, $set_id, $msg_id, @args) = @_;
return $self->maketext(int($set_id) . ',' . int($msg_id), @args);
}
sub unescape {
my ($qq, $qc, $str) = @_;
$str =~ s/(\\([ntvbrf\\$qq]))/($2 eq $qc) ? $qc : eval qq("$1")/e;
$str =~ s/([\~\[\]])/~$1/g;
return $str;
}
1;
=head1 SEE ALSO
L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002, 2003, 2004, 2007 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
LOCALE_MAKETEXT_LEXICON_MSGCAT
$fatpacked{"Locale/Maketext/Lexicon/Tie.pm"} = <<'LOCALE_MAKETEXT_LEXICON_TIE';
package Locale::Maketext::Lexicon::Tie;
$Locale::Maketext::Lexicon::Tie::VERSION = '0.05';
use strict;
use Symbol ();
=head1 NAME
Locale::Maketext::Lexicon::Tie - Use tied hashes as lexicons for Maketext
=head1 SYNOPSIS
package Hello::I18N;
use base 'Locale::Maketext';
use Locale::Maketext::Lexicon {
en => [ Tie => [ DB_File => 'en.db' ] ],
};
=head1 DESCRIPTION
This module lets you easily C<tie> the C<%Lexicon> hash to a database
or other data sources. It takes an array reference of arguments, and
passes them directly to C<tie()>.
Entries will then be fetched whenever it is used; this module does not
cache them.
=cut
sub parse {
my $self = shift;
my $mod = shift;
my $sym = Symbol::gensym();
# Load the target module into memory
{
no strict 'refs';
eval "use $mod; 1" or die $@ unless %{"$mod\::"};
}
# Perform the actual tie
tie %{*$sym}, $mod, @_;
# Returns the GLOB reference, so %Lexicon will be tied too
return $sym;
}
1;
=head1 SEE ALSO
L<Locale::Maketext>, L<Locale::Maketext::Lexicon>
=head1 AUTHORS
Audrey Tang E<lt>cpan@audreyt.orgE<gt>
=head1 COPYRIGHT
Copyright 2002, 2003, 2004, 2007 by Audrey Tang E<lt>cpan@audreyt.orgE<gt>.
This software is released under the MIT license cited below.
=head2 The "MIT" License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut
LOCALE_MAKETEXT_LEXICON_TIE
$fatpacked{"darwin-2level/version.pm"} = <<'DARWIN-2LEVEL_VERSION';
#!perl -w
package version;
use 5.005_04;
use strict;
use vars qw(@ISA $VERSION $CLASS $STRICT $LAX *declare *qv);
$VERSION = 0.88;
$CLASS = 'version';
#--------------------------------------------------------------------------#
# Version regexp components
#--------------------------------------------------------------------------#
# Fraction part of a decimal version number. This is a common part of
# both strict and lax decimal versions
my $FRACTION_PART = qr/\.[0-9]+/;
# First part of either decimal or dotted-decimal strict version number.
# Unsigned integer with no leading zeroes (except for zero itself) to
# avoid confusion with octal.
my $STRICT_INTEGER_PART = qr/0|[1-9][0-9]*/;
# First part of either decimal or dotted-decimal lax version number.
# Unsigned integer, but allowing leading zeros. Always interpreted
# as decimal. However, some forms of the resulting syntax give odd
# results if used as ordinary Perl expressions, due to how perl treats
# octals. E.g.
# version->new("010" ) == 10
# version->new( 010 ) == 8
# version->new( 010.2) == 82 # "8" . "2"
my $LAX_INTEGER_PART = qr/[0-9]+/;
# Second and subsequent part of a strict dotted-decimal version number.
# Leading zeroes are permitted, and the number is always decimal.
# Limited to three digits to avoid overflow when converting to decimal
# form and also avoid problematic style with excessive leading zeroes.
my $STRICT_DOTTED_DECIMAL_PART = qr/\.[0-9]{1,3}/;
# Second and subsequent part of a lax dotted-decimal version number.
# Leading zeroes are permitted, and the number is always decimal. No
# limit on the numerical value or number of digits, so there is the
# possibility of overflow when converting to decimal form.
my $LAX_DOTTED_DECIMAL_PART = qr/\.[0-9]+/;
# Alpha suffix part of lax version number syntax. Acts like a
# dotted-decimal part.
my $LAX_ALPHA_PART = qr/_[0-9]+/;
#--------------------------------------------------------------------------#
# Strict version regexp definitions
#--------------------------------------------------------------------------#
# Strict decimal version number.
my $STRICT_DECIMAL_VERSION =
qr/ $STRICT_INTEGER_PART $FRACTION_PART? /x;
# Strict dotted-decimal version number. Must have both leading "v" and
# at least three parts, to avoid confusion with decimal syntax.
my $STRICT_DOTTED_DECIMAL_VERSION =
qr/ v $STRICT_INTEGER_PART $STRICT_DOTTED_DECIMAL_PART{2,} /x;
# Complete strict version number syntax -- should generally be used
# anchored: qr/ \A $STRICT \z /x
$STRICT =
qr/ $STRICT_DECIMAL_VERSION | $STRICT_DOTTED_DECIMAL_VERSION /x;
#--------------------------------------------------------------------------#
# Lax version regexp definitions
#--------------------------------------------------------------------------#
# Lax decimal version number. Just like the strict one except for
# allowing an alpha suffix or allowing a leading or trailing
# decimal-point
my $LAX_DECIMAL_VERSION =
qr/ $LAX_INTEGER_PART (?: \. | $FRACTION_PART $LAX_ALPHA_PART? )?
|
$FRACTION_PART $LAX_ALPHA_PART?
/x;
# Lax dotted-decimal version number. Distinguished by having either
# leading "v" or at least three non-alpha parts. Alpha part is only
# permitted if there are at least two non-alpha parts. Strangely
# enough, without the leading "v", Perl takes .1.2 to mean v0.1.2,
# so when there is no "v", the leading part is optional
my $LAX_DOTTED_DECIMAL_VERSION =
qr/
v $LAX_INTEGER_PART (?: $LAX_DOTTED_DECIMAL_PART+ $LAX_ALPHA_PART? )?
|
$LAX_INTEGER_PART? $LAX_DOTTED_DECIMAL_PART{2,} $LAX_ALPHA_PART?
/x;
# Complete lax version number syntax -- should generally be used
# anchored: qr/ \A $LAX \z /x
#
# The string 'undef' is a special case to make for easier handling
# of return values from ExtUtils::MM->parse_version
$LAX =
qr/ undef | $LAX_DECIMAL_VERSION | $LAX_DOTTED_DECIMAL_VERSION /x;
#--------------------------------------------------------------------------#
eval "use version::vxs $VERSION";
if ( $@ ) { # don't have the XS version installed
eval "use version::vpp $VERSION"; # don't tempt fate
die "$@" if ( $@ );
push @ISA, "version::vpp";
local $^W;
*version::qv = \&version::vpp::qv;
*version::declare = \&version::vpp::declare;
*version::_VERSION = \&version::vpp::_VERSION;
if ($] >= 5.009000 && $] < 5.011004) {
no strict 'refs';
*version::stringify = \&version::vpp::stringify;
*{'version::(""'} = \&version::vpp::stringify;
*version::new = \&version::vpp::new;
*version::parse = \&version::vpp::parse;
}
}
else { # use XS module
push @ISA, "version::vxs";
local $^W;
*version::declare = \&version::vxs::declare;
*version::qv = \&version::vxs::qv;
*version::_VERSION = \&version::vxs::_VERSION;
*version::vcmp = \&version::vxs::VCMP;
if ($] >= 5.009000 && $] < 5.011004) {
no strict 'refs';
*version::stringify = \&version::vxs::stringify;
*{'version::(""'} = \&version::vxs::stringify;
*version::new = \&version::vxs::new;
*version::parse = \&version::vxs::parse;
}
}
# Preloaded methods go here.
sub import {
no strict 'refs';
my ($class) = shift;
# Set up any derived class
unless ($class eq 'version') {
local $^W;
*{$class.'::declare'} = \&version::declare;
*{$class.'::qv'} = \&version::qv;
}
my %args;
if (@_) { # any remaining terms are arguments
map { $args{$_} = 1 } @_
}
else { # no parameters at all on use line
%args =
(
qv => 1,
'UNIVERSAL::VERSION' => 1,
);
}
my $callpkg = caller();
if (exists($args{declare})) {
*{$callpkg.'::declare'} =
sub {return $class->declare(shift) }
unless defined(&{$callpkg.'::declare'});
}
if (exists($args{qv})) {
*{$callpkg.'::qv'} =
sub {return $class->qv(shift) }
unless defined(&{$callpkg.'::qv'});
}
if (exists($args{'UNIVERSAL::VERSION'})) {
local $^W;
*UNIVERSAL::VERSION
= \&version::_VERSION;
}
if (exists($args{'VERSION'})) {
*{$callpkg.'::VERSION'} = \&version::_VERSION;
}
if (exists($args{'is_strict'})) {
*{$callpkg.'::is_strict'} = \&version::is_strict
unless defined(&{$callpkg.'::is_strict'});
}
if (exists($args{'is_lax'})) {
*{$callpkg.'::is_lax'} = \&version::is_lax
unless defined(&{$callpkg.'::is_lax'});
}
}
sub is_strict { defined $_[0] && $_[0] =~ qr/ \A $STRICT \z /x }
sub is_lax { defined $_[0] && $_[0] =~ qr/ \A $LAX \z /x }
1;
DARWIN-2LEVEL_VERSION
$fatpacked{"darwin-2level/version/vxs.pm"} = <<'DARWIN-2LEVEL_VERSION_VXS';
#!perl -w
package version::vxs;
use 5.005_03;
use strict;
use vars qw(@ISA $VERSION $CLASS );
$VERSION = 0.88;
$CLASS = 'version::vxs';
eval {
require XSLoader;
local $^W; # shut up the 'redefined' warning for UNIVERSAL::VERSION
XSLoader::load('version::vxs', $VERSION);
1;
} or do {
require DynaLoader;
push @ISA, 'DynaLoader';
local $^W; # shut up the 'redefined' warning for UNIVERSAL::VERSION
bootstrap version::vxs $VERSION;
};
# Preloaded methods go here.
1;
DARWIN-2LEVEL_VERSION_VXS
s/^ //mg for values %fatpacked;
unshift @INC, sub {
if (my $fat = $fatpacked{$_[1]}) {
open my $fh, '<', \$fat
or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
return $fh;
}
return
};
} # END OF FATPACK CODE
package patchperl;
# ABSTRACT: patch a perl source tree
use strict;
use warnings;
use Devel::PatchPerl;
Devel::PatchPerl->patch_source(undef, $ARGV[0]);
__END__
=pod
=head1 NAME
patchperl - patch a perl source tree
=head1 VERSION
version 0.32
=head1 AUTHOR
Chris Williams <chris@bingosnet.co.uk>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011 by Chris Williams and Marcus Holland-Moritz.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment