| 1 |
bas |
1.65 |
#!/usr/bin/perl
|
| 2 |
|
|
#
|
| 3 |
|
|
# This is a little utility designed to keep track of translations
|
| 4 |
|
|
# in the Debian web site Subversion repository.
|
| 5 |
|
|
#
|
| 6 |
|
|
## For information about translation revisions please see
|
| 7 |
|
|
## http://www.debian.org/devel/website/uptodate
|
| 8 |
|
|
#
|
| 9 |
|
|
# Copyright (C) 2008 Bas Zoetekouw <bas@debian.org>
|
| 10 |
|
|
# Based on on code from:
|
| 11 |
|
|
# Copyright (C) 1998 Paolo Molaro <lupus@debian.org>
|
| 12 |
|
|
# Copyright (C) 1999-2003 Peter Karlsson <peterk@debian.org>
|
| 13 |
|
|
# Copyright (C) 2000,2001 Martin Quinson <mquinson@ens-lyon.fr>
|
| 14 |
|
|
#
|
| 15 |
|
|
# This program is free software; you can redistribute it and/or modify
|
| 16 |
|
|
# it under the terms of version 2 of the GNU General Public License as
|
| 17 |
|
|
# published by the Free Software Foundation.
|
| 18 |
|
|
#
|
| 19 |
|
|
## This program is distributed in the hope that it will be useful, but
|
| 20 |
|
|
## WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 21 |
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
| 22 |
|
|
## General Public License for more details.
|
| 23 |
|
|
#
|
| 24 |
|
|
#
|
| 25 |
|
|
# Invocation:
|
| 26 |
bas |
1.82 |
# check_trans.pl [-cdlvqQ] [-C dir] [-p pattern] [-s subtree]
|
| 27 |
bas |
1.84 |
# [-m { -n <num> | -M <email> } [-g] ]
|
| 28 |
|
|
# [-t outputtype]
|
| 29 |
bas |
1.65 |
# [language]
|
| 30 |
|
|
#
|
| 31 |
|
|
# It needs to be run from the top level webwml directory.
|
| 32 |
|
|
# If you don't specify a language on the command line, the language name
|
| 33 |
|
|
# will be loaded from a file called language.conf, if such a file exists.
|
| 34 |
|
|
#
|
| 35 |
|
|
# For example:
|
| 36 |
|
|
# $ check_trans.pl -v italian
|
| 37 |
|
|
# You may also check only some subtrees as in:
|
| 38 |
|
|
# $ check_trans.pl -s devel italian
|
| 39 |
|
|
#
|
| 40 |
|
|
# Options:
|
| 41 |
bas |
1.74 |
# -Q be really quiet (only show errors/warnings on stderr)
|
| 42 |
bas |
1.65 |
# -q just don't whine about missing files
|
| 43 |
|
|
# -v show the status of all files (verbose)
|
| 44 |
|
|
# -V output what we're doing (very verbose)
|
| 45 |
|
|
# -d output diffs
|
| 46 |
|
|
# -l output log messages
|
| 47 |
|
|
# -T output translated diffs
|
| 48 |
|
|
# -p <pattern> include only files matching <pattern>,
|
| 49 |
|
|
# default is *.src|*.wml
|
| 50 |
|
|
# -s <subtree> check only that subtree
|
| 51 |
|
|
# -a output age of translation (if older than 2 months)
|
| 52 |
bas |
1.82 |
# -c disable use of color in the output
|
| 53 |
bas |
1.65 |
#
|
| 54 |
|
|
# Options useful when sending mails:
|
| 55 |
bas |
1.84 |
# -m sends mails to translation maintainers as specified in
|
| 56 |
|
|
# in database in $lang/international/$lang/translator.db.pl
|
| 57 |
bas |
1.65 |
# PLEASE CAREFULLY READ THE BELOW TEXT ABOUT MAKING MAILS!
|
| 58 |
bas |
1.84 |
# -n <1|2|3> send mails of priority upper or equal to 1 (monthly),
|
| 59 |
|
|
# 2 (weekly) or 3 (daily), as specified in the translator
|
| 60 |
|
|
# database
|
| 61 |
|
|
# -M <email> instead of using the translator database, send all email
|
| 62 |
|
|
# the specified address. The translator database is not
|
| 63 |
|
|
# used.
|
| 64 |
|
|
# -g instead of sending mails, dump them to the console
|
| 65 |
|
|
# (no mails will be sent)
|
| 66 |
bas |
1.65 |
#
|
| 67 |
bas |
1.78 |
# GENERATING EMAILS
|
| 68 |
bas |
1.65 |
# If you want to, this script send mails to the maintainer of the mails.
|
| 69 |
|
|
# BEWARE, SOME PEOPLE DO NOT LIKE TO RECEIVE AUTOMATIC MAILS!
|
| 70 |
|
|
#
|
| 71 |
|
|
# PREREQUISITES:
|
| 72 |
|
|
# You will need one database:
|
| 73 |
|
|
# - one in which to get info about translators and the frequency at
|
| 74 |
|
|
# which they want to get mails. It must be named
|
| 75 |
bas |
1.84 |
# webwml/$lang/international/$lang/translator.db.pl
|
| 76 |
bas |
1.65 |
# Please refer to the French one for more info.
|
| 77 |
|
|
#
|
| 78 |
|
|
# USAGE:
|
| 79 |
bas |
1.84 |
# If you give the "-g" option, all mails are written to the console. No
|
| 80 |
|
|
# mails are sent out at all. This is useful for debugging.
|
| 81 |
|
|
# If you specify an email addres with the "-M" options, all mails are sent
|
| 82 |
|
|
# to the specified addressee. No mails are sent to any other addresses. It
|
| 83 |
|
|
# is useful if you want to run it for yourself.
|
| 84 |
|
|
# Without either of these options, real mails will be sent to real
|
| 85 |
|
|
# addresses.
|
| 86 |
|
|
# MAKE SURE THE ADDRESSEES REALLY WANT TO GET THESE MAILS!
|
| 87 |
italian |
1.1 |
|
| 88 |
|
|
use Getopt::Std;
|
| 89 |
bas |
1.65 |
use File::Basename;
|
| 90 |
|
|
use File::Spec::Functions;
|
| 91 |
|
|
use Term::ANSIColor;
|
| 92 |
|
|
use Encode;
|
| 93 |
|
|
#use Data::Dumper;
|
| 94 |
|
|
use FindBin;
|
| 95 |
|
|
FindBin::again();
|
| 96 |
barbier |
1.31 |
|
| 97 |
bas |
1.84 |
# These modules reside under webwml/Perl
|
| 98 |
bas |
1.65 |
use lib "$FindBin::Bin/Perl";
|
| 99 |
|
|
use Local::VCS ':all';
|
| 100 |
bas |
1.72 |
use Local::Util qw/ uniq read_file /;
|
| 101 |
french |
1.41 |
use Local::WmlDiffTrans;
|
| 102 |
barbier |
1.36 |
use Webwml::TransCheck;
|
| 103 |
|
|
use Webwml::TransIgnore;
|
| 104 |
|
|
|
| 105 |
bas |
1.65 |
use strict;
|
| 106 |
|
|
use warnings;
|
| 107 |
|
|
|
| 108 |
|
|
|
| 109 |
bas |
1.84 |
# global variable to record verbosity
|
| 110 |
|
|
my $VERBOSE = 0;
|
| 111 |
french |
1.13 |
|
| 112 |
bas |
1.84 |
# default files to process
|
| 113 |
bas |
1.65 |
my $DEFAULT_PATTERN = '(?:\.wml|\.src)$';
|
| 114 |
|
|
|
| 115 |
|
|
# status codes
|
| 116 |
|
|
use constant {
|
| 117 |
|
|
ST_MISSING => 1,
|
| 118 |
|
|
ST_NEEDSUPDATE => 3,
|
| 119 |
|
|
ST_UPTODATE => 4,
|
| 120 |
|
|
ST_NOTATRANSL => 5,
|
| 121 |
|
|
ST_BROKEN => 6,
|
| 122 |
|
|
ST_OBSOLETE => 7,
|
| 123 |
|
|
ST_UNDEFINED => 8,
|
| 124 |
|
|
};
|
| 125 |
|
|
|
| 126 |
|
|
# how to colour each different status
|
| 127 |
|
|
my %COLOURS = (
|
| 128 |
|
|
main::ST_MISSING => 'magenta',
|
| 129 |
|
|
main::ST_NEEDSUPDATE => 'blue',
|
| 130 |
|
|
main::ST_UPTODATE => 'green',
|
| 131 |
|
|
main::ST_NOTATRANSL => 'yellow',
|
| 132 |
|
|
main::ST_BROKEN => 'red',
|
| 133 |
|
|
main::ST_OBSOLETE => 'red',
|
| 134 |
|
|
main::ST_UNDEFINED => 'red',
|
| 135 |
|
|
'warn' => 'bold red',
|
| 136 |
|
|
);
|
| 137 |
|
|
|
| 138 |
bas |
1.84 |
# default values for sending mails
|
| 139 |
|
|
my $MY_EMAIL = q{Debian WWW translation watch <debian-www@lists.debian.org>};
|
| 140 |
|
|
my $DEFAULT_SUBJECT = q{Debian web page translations needing updates};
|
| 141 |
|
|
(my $DEFAULT_BODY = <<"EOF") =~ s/^\t//gm;
|
| 142 |
|
|
Hi!
|
| 143 |
|
|
|
| 144 |
|
|
This is an automatic message providing an overview of Debian webpages
|
| 145 |
|
|
of which the translation is outdated.
|
| 146 |
|
|
|
| 147 |
|
|
Kind regards,
|
| 148 |
|
|
Your automatic daemon.
|
| 149 |
|
|
EOF
|
| 150 |
|
|
|
| 151 |
bas |
1.80 |
# these is called in "main" so need to be declared here
|
| 152 |
bas |
1.65 |
sub switch_var(\$\$);
|
| 153 |
|
|
sub verbose;
|
| 154 |
|
|
|
| 155 |
|
|
#=================================================
|
| 156 |
|
|
#== "main"
|
| 157 |
|
|
#==
|
| 158 |
|
|
{
|
| 159 |
bas |
1.81 |
# install a signal handler to catch Ctrl-C
|
| 160 |
|
|
$SIG{'INT'} = \&handle_INT;
|
| 161 |
|
|
|
| 162 |
bas |
1.84 |
# parse the command line
|
| 163 |
bas |
1.65 |
my ($language,$file_pattern,%OPT) = parse_cmdargs();
|
| 164 |
bas |
1.84 |
|
| 165 |
|
|
# read the tranlator db if we need it (-n was specified)
|
| 166 |
|
|
my %translators = $OPT{n} ? read_translators( $language ) : ();
|
| 167 |
|
|
|
| 168 |
|
|
# this hash will be used to store the emails we want to send out
|
| 169 |
bas |
1.65 |
my %emails_to_send;
|
| 170 |
|
|
|
| 171 |
bas |
1.84 |
# the subdirs where the english and translated files are located
|
| 172 |
bas |
1.65 |
my $english_path = 'english';
|
| 173 |
|
|
my $language_path = $language;
|
| 174 |
|
|
|
| 175 |
bas |
1.84 |
# -s allows the user to restrict processing to a subtree
|
| 176 |
bas |
1.65 |
my $subdir = $OPT{'s'} || undef;
|
| 177 |
|
|
|
| 178 |
|
|
# Global .transignore
|
| 179 |
|
|
my $transignore = Webwml::TransIgnore->new( vcs_get_topdir );
|
| 180 |
|
|
|
| 181 |
|
|
# first get a list with revision information from all files in english...
|
| 182 |
|
|
my %english_revs = vcs_path_info( $english_path,
|
| 183 |
|
|
'recursive' => 1,
|
| 184 |
|
|
'match_pat' => $file_pattern,
|
| 185 |
|
|
);
|
| 186 |
bas |
1.84 |
# ... and in the translation
|
| 187 |
bas |
1.65 |
my %translation_revs = vcs_path_info( $language_path,
|
| 188 |
|
|
'recursive' => 1,
|
| 189 |
|
|
'match_pat' => $file_pattern,
|
| 190 |
|
|
);
|
| 191 |
|
|
|
| 192 |
|
|
# construct a list with all files that either occur in english or
|
| 193 |
|
|
# in the translation
|
| 194 |
|
|
my @files = uniq ( keys %english_revs, keys %translation_revs );
|
| 195 |
|
|
|
| 196 |
|
|
|
| 197 |
|
|
# now check each of the files
|
| 198 |
|
|
foreach my $file (sort @files)
|
| 199 |
|
|
{
|
| 200 |
|
|
# ignore this file?
|
| 201 |
|
|
next if $transignore->is_global( $file );
|
| 202 |
|
|
next if $subdir and not $file =~ m{^$subdir};
|
| 203 |
|
|
|
| 204 |
|
|
# note: $language is the name of the current language we're
|
| 205 |
|
|
# processing, whereas $transl is the name of the language which the
|
| 206 |
|
|
# current file is translated into (which might be english!)
|
| 207 |
|
|
my $orig = 'english';
|
| 208 |
|
|
my $transl = $language;
|
| 209 |
|
|
|
| 210 |
|
|
my $file_orig = catfile( $orig, $file );
|
| 211 |
|
|
my $file_transl = catfile( $transl, $file );
|
| 212 |
|
|
|
| 213 |
|
|
my $revinfo_orig = $english_revs{$file};
|
| 214 |
|
|
my $revinfo_transl = $translation_revs{$file};
|
| 215 |
|
|
|
| 216 |
|
|
# TODO: put this in a separate function
|
| 217 |
|
|
# first we check if the translated file has an origin other than
|
| 218 |
|
|
# english
|
| 219 |
|
|
if ( -e $file_transl )
|
| 220 |
|
|
{
|
| 221 |
|
|
my $transcheck = Webwml::TransCheck->new( $file_transl );
|
| 222 |
|
|
my $original_lang = $transcheck->original();
|
| 223 |
|
|
|
| 224 |
|
|
if ( $original_lang and $original_lang ne 'english' )
|
| 225 |
|
|
{
|
| 226 |
|
|
die( "Unknown original language `$original_lang' "
|
| 227 |
|
|
."for `$file_transl'\n" ) unless -d $original_lang;
|
| 228 |
|
|
|
| 229 |
|
|
verbose "`$file_transl' is translated from $original_lang";
|
| 230 |
|
|
|
| 231 |
|
|
# now, we use the correct (non-english) original file
|
| 232 |
|
|
$file_orig = catfile( $original_lang, $file );
|
| 233 |
|
|
|
| 234 |
|
|
# and find the correct revision info for this file
|
| 235 |
|
|
$revinfo_orig = { vcs_file_info( $file_orig ) };
|
| 236 |
|
|
}
|
| 237 |
|
|
}
|
| 238 |
|
|
|
| 239 |
|
|
# TODO: put this in a separate function
|
| 240 |
|
|
# secondly, we check if perhaps the original file is a translation
|
| 241 |
|
|
# (such as in the case of english/international/Swedish/index.wml)
|
| 242 |
|
|
if ( -e $file_transl and -e $file_orig )
|
| 243 |
|
|
{
|
| 244 |
|
|
my $transcheck = Webwml::TransCheck->new( $file_orig );
|
| 245 |
|
|
my $original_lang = $transcheck->original();
|
| 246 |
|
|
my $rev = $transcheck->revision();
|
| 247 |
|
|
|
| 248 |
|
|
if ( $rev )
|
| 249 |
|
|
{
|
| 250 |
bas |
1.70 |
## This check is too strict: some translators like to translate
|
| 251 |
|
|
##from other translations rather than from the original english
|
| 252 |
|
|
##(see e.g., danish/international/Norwegian.wml)
|
| 253 |
|
|
#if ( not $original_lang )
|
| 254 |
|
|
#{
|
| 255 |
|
|
# # TODO: ideally, this would also be mailed out to the
|
| 256 |
|
|
# # translation team
|
| 257 |
|
|
# warn "`$file_orig' has a revision header but no origin language\n";
|
| 258 |
|
|
# next;
|
| 259 |
|
|
#}
|
| 260 |
bas |
1.65 |
|
| 261 |
bas |
1.70 |
if ( $original_lang and $original_lang eq $language )
|
| 262 |
bas |
1.65 |
{
|
| 263 |
|
|
verbose "`$file_orig' is a translation from $language";
|
| 264 |
|
|
|
| 265 |
|
|
# switch $orig and $transl
|
| 266 |
|
|
switch_var( $orig, $transl );
|
| 267 |
|
|
switch_var( $file_orig, $file_transl );
|
| 268 |
|
|
switch_var( $revinfo_orig, $revinfo_transl );
|
| 269 |
|
|
}
|
| 270 |
|
|
}
|
| 271 |
|
|
|
| 272 |
|
|
}
|
| 273 |
|
|
|
| 274 |
|
|
# determine the status of the file
|
| 275 |
|
|
my ($status,$str,$rev_transl,$maintainer,$maxdelta) = check_file(
|
| 276 |
|
|
$file,
|
| 277 |
|
|
$orig, $transl,
|
| 278 |
|
|
$revinfo_orig, $revinfo_transl,
|
| 279 |
|
|
);
|
| 280 |
|
|
|
| 281 |
|
|
|
| 282 |
|
|
######################################################################
|
| 283 |
|
|
## Everything below is just output logic
|
| 284 |
|
|
######################################################################
|
| 285 |
|
|
|
| 286 |
|
|
# print info
|
| 287 |
|
|
if ( ( $OPT{v} )
|
| 288 |
|
|
or ( $status == ST_MISSING and not $OPT{q} )
|
| 289 |
|
|
or ( $status != ST_MISSING and $status != ST_UPTODATE
|
| 290 |
|
|
and $status != ST_NOTATRANSL )
|
| 291 |
|
|
)
|
| 292 |
|
|
{
|
| 293 |
|
|
print colored( "$str\n", $COLOURS{$status} );
|
| 294 |
|
|
}
|
| 295 |
|
|
|
| 296 |
|
|
# check age of the translation
|
| 297 |
|
|
if ( $OPT{a} and $status == ST_NEEDSUPDATE )
|
| 298 |
|
|
{
|
| 299 |
|
|
my $age = int get_revision_age( $revinfo_transl );
|
| 300 |
|
|
|
| 301 |
|
|
# only warn if the translation is older than 2 weeks
|
| 302 |
|
|
if ( $age > 14 )
|
| 303 |
|
|
{
|
| 304 |
|
|
print colored( "$file is outdated by $age days\n",
|
| 305 |
|
|
$COLOURS{warn} );
|
| 306 |
|
|
}
|
| 307 |
|
|
}
|
| 308 |
|
|
|
| 309 |
bas |
1.76 |
# print log if requested and an update is needed
|
| 310 |
|
|
if ( $OPT{'l'} and $status == ST_NEEDSUPDATE )
|
| 311 |
|
|
{
|
| 312 |
|
|
my $log = get_log(
|
| 313 |
|
|
$file_orig,
|
| 314 |
|
|
$rev_transl,
|
| 315 |
|
|
$revinfo_orig->{'cmt_rev'},
|
| 316 |
|
|
);
|
| 317 |
|
|
print $log;
|
| 318 |
|
|
}
|
| 319 |
|
|
|
| 320 |
bas |
1.65 |
# print diff if requested and an update is needed
|
| 321 |
|
|
if ( $OPT{'d'} and $status == ST_NEEDSUPDATE )
|
| 322 |
|
|
{
|
| 323 |
|
|
my $diff = get_diff(
|
| 324 |
|
|
$file_orig,
|
| 325 |
|
|
$rev_transl,
|
| 326 |
|
|
$revinfo_orig->{'cmt_rev'},
|
| 327 |
|
|
);
|
| 328 |
|
|
print $diff;
|
| 329 |
|
|
}
|
| 330 |
|
|
|
| 331 |
|
|
# print text diff, if requested and an update is needed
|
| 332 |
|
|
if ( $OPT{'T'} and $status == ST_NEEDSUPDATE )
|
| 333 |
|
|
{
|
| 334 |
|
|
my $diff = get_diff_txt(
|
| 335 |
|
|
$file_orig,
|
| 336 |
|
|
$rev_transl,
|
| 337 |
|
|
$revinfo_orig->{'cmt_rev'},
|
| 338 |
|
|
$file_transl
|
| 339 |
|
|
);
|
| 340 |
|
|
print $diff;
|
| 341 |
|
|
}
|
| 342 |
|
|
|
| 343 |
|
|
|
| 344 |
|
|
# prepare a mail to be sent
|
| 345 |
|
|
if ( $OPT{'m'} and $status != ST_UPTODATE )
|
| 346 |
|
|
{
|
| 347 |
bas |
1.84 |
# -M was specified, so all mails to there
|
| 348 |
|
|
if ( $OPT{'M'} )
|
| 349 |
|
|
{
|
| 350 |
|
|
$maintainer = 'default';
|
| 351 |
|
|
|
| 352 |
|
|
# don't send mail about untranslated files if -q was specified
|
| 353 |
|
|
$maintainer = 'none'
|
| 354 |
|
|
if $status == ST_MISSING and $OPT{'q'}
|
| 355 |
|
|
}
|
| 356 |
|
|
else # addresses from the database is used
|
| 357 |
|
|
{
|
| 358 |
|
|
# handle special case maintainer fields
|
| 359 |
|
|
$maintainer = 'unmaintained'
|
| 360 |
|
|
unless $maintainer and exists $translators{$maintainer};
|
| 361 |
|
|
$maintainer = 'untranslated'
|
| 362 |
|
|
if $status == ST_MISSING;
|
| 363 |
|
|
}
|
| 364 |
|
|
|
| 365 |
|
|
verbose "Found maintainer $maintainer for this file";
|
| 366 |
bas |
1.65 |
|
| 367 |
|
|
# mail to send to the maintainer
|
| 368 |
|
|
push @{ $emails_to_send{$maintainer} }, {
|
| 369 |
|
|
'file' => $file,
|
| 370 |
|
|
'status' => $status,
|
| 371 |
|
|
'info' => $str,
|
| 372 |
|
|
'last_trans_rev' => $rev_transl,
|
| 373 |
bas |
1.84 |
};
|
| 374 |
bas |
1.65 |
|
| 375 |
bas |
1.84 |
# additionally, if -n was specified, also see if we need to
|
| 376 |
|
|
# send a mail to maxdelta
|
| 377 |
|
|
if ( $OPT{'n'} and $status != ST_MISSING and -e $file_orig )
|
| 378 |
bas |
1.65 |
{
|
| 379 |
|
|
$maxdelta ||= $translators{maxdelta}{maxdelta} || 5;
|
| 380 |
|
|
|
| 381 |
bas |
1.84 |
my $delta;
|
| 382 |
|
|
$delta = vcs_count_changes( $file_orig, $rev_transl, 'HEAD' );
|
| 383 |
|
|
|
| 384 |
|
|
if ( $delta >= $maxdelta )
|
| 385 |
bas |
1.66 |
{
|
| 386 |
|
|
push @{ $emails_to_send{'maxdelta'} }, {
|
| 387 |
|
|
'file' => $file,
|
| 388 |
|
|
'status' => $status,
|
| 389 |
|
|
'info' => $str,
|
| 390 |
|
|
'delta' => $delta,
|
| 391 |
|
|
'last_trans_rev' => $rev_transl,
|
| 392 |
|
|
}
|
| 393 |
bas |
1.65 |
}
|
| 394 |
|
|
}
|
| 395 |
|
|
|
| 396 |
|
|
}
|
| 397 |
|
|
|
| 398 |
|
|
}
|
| 399 |
|
|
|
| 400 |
bas |
1.84 |
send_email( \%emails_to_send, \%translators, $language,
|
| 401 |
|
|
$OPT{'n'}, $OPT{'M'}, $OPT{'g'} );
|
| 402 |
bas |
1.65 |
|
| 403 |
|
|
exit 0;
|
| 404 |
|
|
}
|
| 405 |
|
|
die("Never reached");
|
| 406 |
|
|
|
| 407 |
|
|
|
| 408 |
|
|
#=================================================
|
| 409 |
|
|
#== swich two variables around
|
| 410 |
|
|
#==
|
| 411 |
|
|
sub switch_var(\$\$)
|
| 412 |
|
|
{
|
| 413 |
|
|
my $a = shift;
|
| 414 |
|
|
my $b = shift;
|
| 415 |
|
|
|
| 416 |
|
|
my $c = $$a;
|
| 417 |
|
|
$$a = $$b;
|
| 418 |
|
|
$$b = $c;
|
| 419 |
|
|
}
|
| 420 |
|
|
|
| 421 |
|
|
|
| 422 |
|
|
#=================================================
|
| 423 |
|
|
#== output verbose messages
|
| 424 |
|
|
#==
|
| 425 |
|
|
sub verbose
|
| 426 |
|
|
{
|
| 427 |
|
|
return unless $VERBOSE;
|
| 428 |
|
|
print @_, "\n";
|
| 429 |
|
|
}
|
| 430 |
|
|
|
| 431 |
bas |
1.81 |
#=================================================
|
| 432 |
|
|
#== handles INT signal
|
| 433 |
|
|
#==
|
| 434 |
|
|
sub handle_INT
|
| 435 |
|
|
{
|
| 436 |
|
|
# reset terminal color
|
| 437 |
|
|
print color('reset');
|
| 438 |
|
|
die( "Interrupted by user" );
|
| 439 |
|
|
}
|
| 440 |
bas |
1.65 |
|
| 441 |
|
|
#=================================================
|
| 442 |
|
|
#== send out the emails
|
| 443 |
|
|
#==
|
| 444 |
|
|
sub send_email
|
| 445 |
|
|
{
|
| 446 |
|
|
my $emails = shift or die("No emails specified");
|
| 447 |
|
|
my $translators = shift or die("No translators specified");
|
| 448 |
|
|
my $lang = shift or die("No language specified");
|
| 449 |
bas |
1.84 |
my $priority = shift;
|
| 450 |
|
|
my $default_rec = shift;
|
| 451 |
|
|
my $debug = shift;
|
| 452 |
bas |
1.65 |
|
| 453 |
|
|
foreach my $name (sort keys %$emails)
|
| 454 |
|
|
{
|
| 455 |
bas |
1.84 |
# special case
|
| 456 |
|
|
next if $name eq 'none';
|
| 457 |
|
|
|
| 458 |
bas |
1.65 |
verbose("Preparing email for $name");
|
| 459 |
|
|
|
| 460 |
bas |
1.84 |
my $recipient;
|
| 461 |
|
|
my $subject;
|
| 462 |
|
|
my $mailbody;
|
| 463 |
|
|
|
| 464 |
|
|
# First handle the case in whcih all mail goes to the -M address
|
| 465 |
|
|
if ( $default_rec )
|
| 466 |
|
|
{
|
| 467 |
|
|
# address was already validated while parsing the command line
|
| 468 |
|
|
$recipient = $default_rec;
|
| 469 |
|
|
$subject = $DEFAULT_SUBJECT;
|
| 470 |
|
|
$mailbody = $DEFAULT_BODY;
|
| 471 |
|
|
}
|
| 472 |
|
|
else # handle the case in whcih addresses are fetch from the db
|
| 473 |
|
|
{
|
| 474 |
|
|
# skip unconfigured users
|
| 475 |
|
|
if ( not exists $translators->{$name}
|
| 476 |
|
|
or not $translators->{$name}{'email'} )
|
| 477 |
|
|
{
|
| 478 |
|
|
verbose( "Woops! Can't find info for user `$name'\n" );
|
| 479 |
|
|
next;
|
| 480 |
|
|
}
|
| 481 |
|
|
|
| 482 |
|
|
# check the user's email addres
|
| 483 |
|
|
if ( not Email::Address->parse( $translators->{$name}{'email'} ) )
|
| 484 |
|
|
{
|
| 485 |
|
|
printf STDERR "Can't parse email address `%s' for %s!\n",
|
| 486 |
|
|
$translators->{$name}{'email'}, $name;
|
| 487 |
|
|
next;
|
| 488 |
|
|
}
|
| 489 |
bas |
1.65 |
|
| 490 |
bas |
1.84 |
# skip if the user doesn't want a summary at all
|
| 491 |
|
|
if ( $translators->{$name}{'summary'} < $priority )
|
| 492 |
|
|
{
|
| 493 |
|
|
verbose( "Not sending message to $name (prio "
|
| 494 |
|
|
. $translators->{$name}{'summary'} . " < $priority)" );
|
| 495 |
|
|
next;
|
| 496 |
|
|
}
|
| 497 |
bas |
1.65 |
|
| 498 |
bas |
1.84 |
$recipient = $translators->{$name}{'email'};
|
| 499 |
bas |
1.86 |
$subject = $translators->{'default'}{'mailsubject'};
|
| 500 |
bas |
1.65 |
|
| 501 |
bas |
1.84 |
# read body and interpret perl that's embedded there
|
| 502 |
|
|
$mailbody = read_file_enc( $translators->{'default'}{'mailbody'} )
|
| 503 |
|
|
or die("Can't read $translators->{'default'}{'mailbody'}");
|
| 504 |
|
|
{
|
| 505 |
|
|
# a bit hackish, but I want to keep the curent format of
|
| 506 |
|
|
# the mail body files intact, for now
|
| 507 |
|
|
# so we need to use the same old variable names as the original
|
| 508 |
|
|
# script used
|
| 509 |
|
|
my %translators = %{$translators};
|
| 510 |
|
|
$mailbody =~ s{#(.*?)#}{eval $1}mge;
|
| 511 |
|
|
}
|
| 512 |
bas |
1.65 |
|
| 513 |
bas |
1.84 |
}
|
| 514 |
bas |
1.65 |
|
| 515 |
|
|
my $msg = MIME::Lite->new(
|
| 516 |
|
|
'From' => $MY_EMAIL,
|
| 517 |
bas |
1.84 |
'To' => $recipient,
|
| 518 |
|
|
'Subject' => $subject,
|
| 519 |
bas |
1.65 |
'Type' => 'multipart/mixed',
|
| 520 |
|
|
);
|
| 521 |
|
|
|
| 522 |
|
|
# and attach the body to the mail
|
| 523 |
bas |
1.66 |
my $part = MIME::Lite->new(
|
| 524 |
|
|
'Type' => 'text/plain',
|
| 525 |
bas |
1.85 |
'Data' => encode('utf-8',$mailbody),
|
| 526 |
bas |
1.65 |
);
|
| 527 |
bas |
1.66 |
$part->attr( 'content-type.charset' => 'utf-8' );
|
| 528 |
|
|
$msg->attach( $part );
|
| 529 |
bas |
1.65 |
|
| 530 |
|
|
# attach part about NeedToUpdate files
|
| 531 |
|
|
my $text = '';
|
| 532 |
|
|
foreach my $file ( @{ $emails->{$name} } )
|
| 533 |
|
|
{
|
| 534 |
|
|
next unless $file->{'status'} == ST_NEEDSUPDATE;
|
| 535 |
|
|
$text .= $file->{'info'};
|
| 536 |
|
|
|
| 537 |
|
|
if ( exists $file->{'delta'} )
|
| 538 |
|
|
{
|
| 539 |
|
|
$text .= sprintf( " [out of date by %d revisions]",
|
| 540 |
|
|
$file->{'delta'} );
|
| 541 |
|
|
}
|
| 542 |
|
|
|
| 543 |
|
|
$text .= "\n";
|
| 544 |
|
|
}
|
| 545 |
|
|
$msg->attach(
|
| 546 |
|
|
'Type' => 'TEXT',
|
| 547 |
|
|
'Filename' => 'NeedToUpdate summary',
|
| 548 |
|
|
'Data' => $text,
|
| 549 |
|
|
)
|
| 550 |
|
|
if $text;
|
| 551 |
|
|
|
| 552 |
|
|
# attach part about Missing files
|
| 553 |
bas |
1.84 |
if ( not $default_rec )
|
| 554 |
bas |
1.65 |
{
|
| 555 |
bas |
1.84 |
$text = '';
|
| 556 |
|
|
foreach my $file ( @{ $emails->{$name} } )
|
| 557 |
|
|
{
|
| 558 |
|
|
next unless $file->{'status'} == ST_MISSING;
|
| 559 |
|
|
$text .= sprintf( "%s\n", $file->{'info'} );
|
| 560 |
|
|
}
|
| 561 |
|
|
$msg->attach(
|
| 562 |
|
|
'Type' => 'TEXT',
|
| 563 |
|
|
'Filename' => 'Missing summary',
|
| 564 |
|
|
'Data' => $text,
|
| 565 |
|
|
'Encoding' => 'quoted-printable',
|
| 566 |
|
|
)
|
| 567 |
|
|
if $text;
|
| 568 |
bas |
1.65 |
}
|
| 569 |
|
|
|
| 570 |
|
|
# add diffs, if requested
|
| 571 |
bas |
1.84 |
if ( $default_rec or $priority <= $translators->{$name}{'diff'} )
|
| 572 |
bas |
1.65 |
{
|
| 573 |
|
|
foreach my $file ( @{ $emails->{$name} } )
|
| 574 |
|
|
{
|
| 575 |
|
|
# diffs really only make sense if there is an existing
|
| 576 |
|
|
# translation
|
| 577 |
|
|
next unless $file->{'status'} == ST_NEEDSUPDATE;
|
| 578 |
|
|
|
| 579 |
|
|
my $filename = catfile( 'english', $file->{'file'} );
|
| 580 |
|
|
my $rev = $file->{'last_trans_rev'};
|
| 581 |
|
|
my $diff = get_diff( $filename, $rev, 'HEAD' );
|
| 582 |
|
|
$msg->attach(
|
| 583 |
|
|
'Type' => 'TEXT',
|
| 584 |
|
|
'Filename' => "$filename.diff",
|
| 585 |
|
|
'Data' => $diff,
|
| 586 |
|
|
'Encoding' => 'quoted-printable',
|
| 587 |
|
|
);
|
| 588 |
|
|
}
|
| 589 |
|
|
}
|
| 590 |
|
|
else
|
| 591 |
|
|
{
|
| 592 |
|
|
verbose( "Not attaching diffs (prio "
|
| 593 |
|
|
. $translators->{$name}{'diff'} . " < $priority)" );
|
| 594 |
|
|
}
|
| 595 |
|
|
|
| 596 |
|
|
# add tdiffs, if requested
|
| 597 |
bas |
1.84 |
if ( not $default_rec and $priority <= $translators->{$name}{'tdiff'} )
|
| 598 |
bas |
1.65 |
{
|
| 599 |
|
|
foreach my $file ( @{ $emails->{$name} } )
|
| 600 |
|
|
{
|
| 601 |
|
|
# diffs really only make sense if there is an existing
|
| 602 |
|
|
# translation
|
| 603 |
|
|
next unless $file->{'status'} == ST_NEEDSUPDATE;
|
| 604 |
|
|
|
| 605 |
|
|
my $filename = catfile( 'english', $file->{'file'} );
|
| 606 |
|
|
my $filename2 = catfile( $lang, $file->{'file'} );
|
| 607 |
|
|
my $rev = $file->{'last_trans_rev'};
|
| 608 |
|
|
my $tdiff = get_diff_txt( $filename, $rev, 'HEAD',
|
| 609 |
|
|
$filename2 );
|
| 610 |
|
|
$msg->attach(
|
| 611 |
|
|
'Type' => 'TEXT',
|
| 612 |
|
|
'Filename' => "$filename.tdiff",
|
| 613 |
|
|
'Data' => $tdiff,
|
| 614 |
|
|
'Encoding' => 'quoted-printable',
|
| 615 |
|
|
);
|
| 616 |
|
|
}
|
| 617 |
|
|
}
|
| 618 |
|
|
else
|
| 619 |
|
|
{
|
| 620 |
|
|
verbose( "Not attaching tdiffs (prio "
|
| 621 |
bas |
1.84 |
. $translators->{$name}{'tdiff'} . " < $priority)" )
|
| 622 |
|
|
unless $default_rec;
|
| 623 |
bas |
1.65 |
}
|
| 624 |
|
|
|
| 625 |
|
|
# add logs, if requested
|
| 626 |
bas |
1.84 |
if ( $default_rec or $priority <= $translators->{$name}{'logs'} )
|
| 627 |
bas |
1.65 |
{
|
| 628 |
|
|
foreach my $file ( @{ $emails->{$name} } )
|
| 629 |
|
|
{
|
| 630 |
|
|
# logs really only make sense if there is an existing
|
| 631 |
|
|
# translation
|
| 632 |
|
|
next unless $file->{'status'} == ST_NEEDSUPDATE;
|
| 633 |
|
|
|
| 634 |
|
|
my $filename = catfile( 'english', $file->{'file'} );
|
| 635 |
|
|
my $rev = $file->{'last_trans_rev'};
|
| 636 |
|
|
my $log = get_log( $filename, $rev, 'HEAD' );
|
| 637 |
|
|
my $part = MIME::Lite->new(
|
| 638 |
bas |
1.66 |
'Type' => 'TEXT',
|
| 639 |
bas |
1.65 |
'Filename' => "$filename.log",
|
| 640 |
|
|
'Data' => $log,
|
| 641 |
|
|
'Encoding' => 'quoted-printable',
|
| 642 |
|
|
);
|
| 643 |
|
|
$part->attr( 'content-type.charset' => 'utf-8' );
|
| 644 |
|
|
$msg->attach( $part );
|
| 645 |
|
|
}
|
| 646 |
|
|
}
|
| 647 |
|
|
else
|
| 648 |
|
|
{
|
| 649 |
|
|
verbose( "Not attaching logs (prio "
|
| 650 |
|
|
. $translators->{$name}{'logs'} . " < $priority)" );
|
| 651 |
|
|
}
|
| 652 |
|
|
|
| 653 |
|
|
# add file, if requested
|
| 654 |
bas |
1.84 |
if ( not $default_rec and $priority <= $translators->{$name}{'file'} )
|
| 655 |
bas |
1.65 |
{
|
| 656 |
|
|
foreach my $file ( @{ $emails->{$name} } )
|
| 657 |
|
|
{
|
| 658 |
|
|
my $filename = catfile( $lang, $file->{'file'} );
|
| 659 |
|
|
my $part = MIME::Lite->new(
|
| 660 |
|
|
'Type' => 'text/wml',
|
| 661 |
|
|
'Filename' => $filename,
|
| 662 |
|
|
'Path' => $filename,
|
| 663 |
|
|
'Encoding' => 'quoted-printable',
|
| 664 |
|
|
);
|
| 665 |
|
|
$part->attr( 'content-type.charset' => get_file_charset($filename) );
|
| 666 |
|
|
$msg->attach( $part );
|
| 667 |
|
|
|
| 668 |
|
|
}
|
| 669 |
|
|
}
|
| 670 |
|
|
else
|
| 671 |
|
|
{
|
| 672 |
|
|
verbose( "Not attaching files (prio "
|
| 673 |
bas |
1.84 |
. $translators->{$name}{'file'} . " < $priority)" )
|
| 674 |
|
|
unless $default_rec;
|
| 675 |
bas |
1.65 |
}
|
| 676 |
|
|
|
| 677 |
|
|
|
| 678 |
|
|
|
| 679 |
|
|
# check if we really want to send the mail
|
| 680 |
bas |
1.84 |
if ( $debug )
|
| 681 |
bas |
1.65 |
{
|
| 682 |
bas |
1.84 |
print color('bold yellow');
|
| 683 |
|
|
print '*'x72, "\n";
|
| 684 |
|
|
printf "Would send email to %s (but -g was specified):\n",
|
| 685 |
|
|
$recipient;
|
| 686 |
|
|
print '-'x72, "\n";
|
| 687 |
|
|
print color('reset');
|
| 688 |
|
|
|
| 689 |
|
|
print $msg->as_string;
|
| 690 |
|
|
|
| 691 |
|
|
print color('bold yellow');
|
| 692 |
|
|
print '*'x72, "\n";
|
| 693 |
|
|
print color('reset');
|
| 694 |
bas |
1.65 |
}
|
| 695 |
|
|
else
|
| 696 |
|
|
{
|
| 697 |
bas |
1.84 |
verbose "Sending email to $recipient";
|
| 698 |
|
|
$msg->send or warn("Couldn't send message to $name");
|
| 699 |
bas |
1.65 |
}
|
| 700 |
|
|
}
|
| 701 |
|
|
}
|
| 702 |
|
|
|
| 703 |
|
|
|
| 704 |
|
|
#=================================================
|
| 705 |
|
|
#== return the age of the revision (in days)
|
| 706 |
|
|
#==
|
| 707 |
|
|
sub get_revision_age
|
| 708 |
|
|
{
|
| 709 |
|
|
my $rev_info = shift;
|
| 710 |
|
|
|
| 711 |
|
|
die("No revision info specified") unless ref $rev_info eq 'HASH';
|
| 712 |
|
|
|
| 713 |
|
|
my $rev_timestamp = $rev_info->{'cmt_date'};
|
| 714 |
|
|
my $age = time - $rev_timestamp;
|
| 715 |
|
|
|
| 716 |
|
|
warn( "Timestamp is in the future!" ) if $age < 0;
|
| 717 |
|
|
|
| 718 |
|
|
# return age in days
|
| 719 |
|
|
return $age / ( 60*60*24 );
|
| 720 |
|
|
}
|
| 721 |
|
|
|
| 722 |
|
|
|
| 723 |
|
|
|
| 724 |
|
|
#=================================================
|
| 725 |
|
|
#== get a log
|
| 726 |
|
|
#==
|
| 727 |
|
|
sub get_log
|
| 728 |
|
|
{
|
| 729 |
|
|
my $file = shift or die("No file specified for diff");
|
| 730 |
|
|
my $rev1 = shift;
|
| 731 |
|
|
my $rev2 = shift;
|
| 732 |
|
|
|
| 733 |
|
|
die("NO such file `$file'") unless -e $file;
|
| 734 |
|
|
|
| 735 |
|
|
my @log = vcs_get_log( $file, $rev1, $rev2 );
|
| 736 |
|
|
|
| 737 |
|
|
# remove the first item of the log, because we only want
|
| 738 |
|
|
# to see when changed in the (left-open) range (rev1,rev2]
|
| 739 |
|
|
shift @log;
|
| 740 |
|
|
|
| 741 |
|
|
# format it nicely
|
| 742 |
|
|
my $str = '-' x 78 . "\n";
|
| 743 |
|
|
foreach my $l (@log)
|
| 744 |
|
|
{
|
| 745 |
|
|
chomp $l->{'message'};
|
| 746 |
|
|
|
| 747 |
bas |
1.66 |
$str .= sprintf( "%s | %s | %s\n",
|
| 748 |
bas |
1.65 |
$l->{'rev'}, $l->{'author'}, scalar localtime $l->{'date'} );
|
| 749 |
|
|
$str .= "\n";
|
| 750 |
|
|
$str .= $l->{'message'} . "\n";
|
| 751 |
|
|
$str .= "\n";
|
| 752 |
|
|
|
| 753 |
|
|
$str .= '-' x 78 . "\n";
|
| 754 |
|
|
|
| 755 |
|
|
}
|
| 756 |
|
|
|
| 757 |
|
|
|
| 758 |
|
|
return $str;
|
| 759 |
|
|
}
|
| 760 |
|
|
|
| 761 |
|
|
#=================================================
|
| 762 |
|
|
#== get a diff
|
| 763 |
|
|
#==
|
| 764 |
|
|
sub get_diff
|
| 765 |
|
|
{
|
| 766 |
|
|
my $file = shift or die("No file specified for diff");
|
| 767 |
|
|
my $rev1 = shift;
|
| 768 |
|
|
my $rev2 = shift;
|
| 769 |
|
|
|
| 770 |
|
|
die("NO such file `$file'") unless -e $file;
|
| 771 |
|
|
|
| 772 |
|
|
my %diffs = vcs_get_diff( $file, $rev1, $rev2 );
|
| 773 |
|
|
|
| 774 |
|
|
# just glue all diffs together and return it as a big string
|
| 775 |
|
|
my $difftxt = join( '', values %diffs );
|
| 776 |
|
|
|
| 777 |
|
|
return $difftxt;
|
| 778 |
|
|
}
|
| 779 |
|
|
|
| 780 |
bas |
1.69 |
#=================================================
|
| 781 |
bas |
1.65 |
#== get a diff while trying to match html tags
|
| 782 |
|
|
#==
|
| 783 |
|
|
sub get_diff_txt
|
| 784 |
|
|
{
|
| 785 |
|
|
my $english_file = shift or die("No file specified");
|
| 786 |
|
|
my $rev1 = shift or die("No revision specified");
|
| 787 |
|
|
my $rev2 = shift or die("No revision specified");
|
| 788 |
|
|
my $transl_file = shift or die("No transl file specified");
|
| 789 |
|
|
|
| 790 |
|
|
die("No such file $english_file") unless -e $english_file;
|
| 791 |
|
|
die("No such file $transl_file") unless -e $transl_file;
|
| 792 |
|
|
|
| 793 |
|
|
# Get old revision file
|
| 794 |
|
|
my @english_txt = split( "\n", vcs_get_file( $english_file, $rev1 ) );
|
| 795 |
|
|
|
| 796 |
|
|
# Get translation file
|
| 797 |
bas |
1.72 |
my $transl_txt = read_file( $transl_file )
|
| 798 |
|
|
or die("Couln't read `$transl_file': $!");
|
| 799 |
|
|
my @transl_txt = split( "\n", $transl_txt );
|
| 800 |
bas |
1.65 |
|
| 801 |
|
|
# Get diff lines
|
| 802 |
|
|
my @diff_txt = split( "\n", get_diff( $english_file, $rev1, $rev2 ) );
|
| 803 |
|
|
|
| 804 |
|
|
# do the matching
|
| 805 |
|
|
my $txt = Local::WmlDiffTrans::find_trans_parts(
|
| 806 |
|
|
\@english_txt,
|
| 807 |
|
|
\@transl_txt,
|
| 808 |
|
|
\@diff_txt
|
| 809 |
|
|
);
|
| 810 |
|
|
|
| 811 |
|
|
return $txt;
|
| 812 |
|
|
}
|
| 813 |
|
|
|
| 814 |
|
|
|
| 815 |
|
|
#=================================================
|
| 816 |
|
|
#== show help from the top of this file
|
| 817 |
|
|
#==
|
| 818 |
|
|
sub show_help
|
| 819 |
|
|
{
|
| 820 |
|
|
# read the help from the comments above and display it
|
| 821 |
|
|
open( my $me, '<', $0 ) or die "Unable to display help: $!\n";
|
| 822 |
|
|
|
| 823 |
bas |
1.78 |
while ( my $line = <$me> )
|
| 824 |
bas |
1.65 |
{
|
| 825 |
bas |
1.78 |
last if $line =~ m{^use};
|
| 826 |
|
|
print "\n" if $line =~ m{^#$};
|
| 827 |
|
|
next unless $line =~ m{^# };
|
| 828 |
bas |
1.65 |
|
| 829 |
bas |
1.78 |
$line =~ s{^# ?}{};
|
| 830 |
italian |
1.1 |
|
| 831 |
bas |
1.78 |
print $line;
|
| 832 |
peterk |
1.21 |
}
|
| 833 |
bas |
1.65 |
|
| 834 |
|
|
close( $me );
|
| 835 |
|
|
}
|
| 836 |
|
|
|
| 837 |
|
|
|
| 838 |
|
|
#=================================================
|
| 839 |
|
|
#== parse command line options and read defaults
|
| 840 |
|
|
#==
|
| 841 |
|
|
sub parse_cmdargs
|
| 842 |
|
|
{
|
| 843 |
|
|
my %OPT;
|
| 844 |
|
|
$OPT{s} = '';
|
| 845 |
|
|
|
| 846 |
|
|
# parse options
|
| 847 |
bas |
1.84 |
if ( not getopts( 'acdghlmM:n:p:Qqs:TvV', \%OPT ) )
|
| 848 |
bas |
1.65 |
{
|
| 849 |
|
|
show_help();
|
| 850 |
|
|
exit -1;
|
| 851 |
|
|
}
|
| 852 |
|
|
|
| 853 |
|
|
# show help
|
| 854 |
|
|
if ( $OPT{h} )
|
| 855 |
|
|
{
|
| 856 |
|
|
show_help();
|
| 857 |
|
|
exit 0;
|
| 858 |
|
|
}
|
| 859 |
|
|
|
| 860 |
|
|
# handle verbosity setting
|
| 861 |
bas |
1.74 |
if ( ( $OPT{'v'} or $OPT{'V'} ) and ( $OPT{'q'} or $OPT{'Q'} ) )
|
| 862 |
bas |
1.65 |
{
|
| 863 |
|
|
die "you can't have both verbose and quiet, doh!\n";
|
| 864 |
|
|
}
|
| 865 |
|
|
$VERBOSE = 1 if $OPT{'V'};
|
| 866 |
|
|
$OPT{'v'} = 1 if $OPT{'V'};
|
| 867 |
|
|
|
| 868 |
bas |
1.74 |
# handle really quiet setting
|
| 869 |
|
|
if ( $OPT{'Q'} )
|
| 870 |
|
|
{
|
| 871 |
|
|
# redirect stdout to /dev/null
|
| 872 |
|
|
close( STDOUT );
|
| 873 |
bas |
1.75 |
open( STDOUT, '>', '/dev/null' )
|
| 874 |
bas |
1.74 |
or die( "Can't redirect STDOUT to /dev/null: $!" );
|
| 875 |
|
|
}
|
| 876 |
|
|
|
| 877 |
bas |
1.82 |
# handle -c (disable color) setting
|
| 878 |
|
|
if ( $OPT{'c'} )
|
| 879 |
|
|
{
|
| 880 |
|
|
# nice feature of Term::ANSIColor
|
| 881 |
|
|
$ENV{'ANSI_COLORS_DISABLED'} = '1';
|
| 882 |
|
|
}
|
| 883 |
bas |
1.84 |
else
|
| 884 |
|
|
{
|
| 885 |
|
|
# we need flushed STDOUT putput, because otherwise the colours wills
|
| 886 |
|
|
# blend into STDERR
|
| 887 |
|
|
$| = 1;
|
| 888 |
|
|
}
|
| 889 |
bas |
1.82 |
|
| 890 |
bas |
1.65 |
# handle -s (subtree check) setting
|
| 891 |
|
|
if ( $OPT{s})
|
| 892 |
|
|
{
|
| 893 |
|
|
verbose "Checking subtree $OPT{s} only\n";
|
| 894 |
|
|
}
|
| 895 |
|
|
|
| 896 |
bas |
1.84 |
# check validity of mail options
|
| 897 |
|
|
# if -m is specified, either -n or -M must also be given
|
| 898 |
|
|
# furthermore, the argument to -n must be 1, 2, or 3, and
|
| 899 |
|
|
# the argument to -M must be a valid email address
|
| 900 |
bas |
1.67 |
if ( $OPT{'m'} )
|
| 901 |
|
|
{
|
| 902 |
bas |
1.84 |
# load additional module we need for mail
|
| 903 |
bas |
1.67 |
eval {
|
| 904 |
|
|
require MIME::Lite;
|
| 905 |
|
|
import MIME::Lite;
|
| 906 |
|
|
};
|
| 907 |
|
|
die "The module MIME::Lite could not be loaded.\n"
|
| 908 |
|
|
."Please install libmime-lite-perl\n" if $@;
|
| 909 |
|
|
|
| 910 |
|
|
eval {
|
| 911 |
|
|
require Email::Address;
|
| 912 |
|
|
import Email::Address;
|
| 913 |
|
|
};
|
| 914 |
|
|
die "The module Email::Address could not be loaded.\n"
|
| 915 |
|
|
."Please install libemail-address-perl\n" if $@;
|
| 916 |
bas |
1.84 |
|
| 917 |
|
|
# now check the options
|
| 918 |
|
|
if ( $OPT{'n'} and $OPT{'M'} )
|
| 919 |
|
|
{
|
| 920 |
|
|
die "You can't specify both -n and -M\n";
|
| 921 |
|
|
}
|
| 922 |
|
|
elsif ( $OPT{'n'} )
|
| 923 |
|
|
{
|
| 924 |
|
|
die "Invalid priority `$OPT{n}'. "
|
| 925 |
|
|
."Please set -n value to 1, 2 or 3.\n"
|
| 926 |
|
|
unless $OPT{'n'} =~ m{^[123]$}
|
| 927 |
|
|
}
|
| 928 |
|
|
elsif ( $OPT{'M'} )
|
| 929 |
|
|
{
|
| 930 |
|
|
die "Invalid email address `$OPT{M}'\n"
|
| 931 |
|
|
unless Email::Address->parse( $OPT{M} );
|
| 932 |
|
|
}
|
| 933 |
|
|
else
|
| 934 |
|
|
{
|
| 935 |
|
|
die "You specified -m (send mails), but you didn't specify "
|
| 936 |
|
|
."either -n or -M, so I don't knwo where to send my mails\n";
|
| 937 |
|
|
}
|
| 938 |
|
|
|
| 939 |
bas |
1.67 |
}
|
| 940 |
|
|
|
| 941 |
bas |
1.65 |
if ( $OPT{'g'} and not $OPT{'m'} )
|
| 942 |
|
|
{
|
| 943 |
|
|
die "Option -g (debuG mail) without -m (use mail) "
|
| 944 |
|
|
."really doesn't make much sense\n";
|
| 945 |
|
|
}
|
| 946 |
|
|
|
| 947 |
|
|
# include only files matching $filename
|
| 948 |
|
|
my $file_pattern = $OPT{'p'} || $DEFAULT_PATTERN;
|
| 949 |
|
|
|
| 950 |
|
|
my $translation = shift @ARGV || '';
|
| 951 |
|
|
|
| 952 |
|
|
# language configuration
|
| 953 |
|
|
if ( not $translation )
|
| 954 |
|
|
{
|
| 955 |
|
|
if ( exists $ENV{DWWW_LANG} )
|
| 956 |
|
|
{
|
| 957 |
|
|
$translation = $ENV{DWWW_LANG};
|
| 958 |
|
|
}
|
| 959 |
|
|
elsif ( -e "language.conf" )
|
| 960 |
|
|
{
|
| 961 |
|
|
open( my $conf, '<', 'language.conf' )
|
| 962 |
|
|
or die("Can't read language.conf: $!\n");
|
| 963 |
|
|
while (<$conf>)
|
| 964 |
|
|
{
|
| 965 |
|
|
next if /^#/;
|
| 966 |
|
|
chomp;
|
| 967 |
|
|
$translation = $_;
|
| 968 |
|
|
last;
|
| 969 |
|
|
}
|
| 970 |
|
|
close $conf;
|
| 971 |
|
|
}
|
| 972 |
|
|
}
|
| 973 |
|
|
|
| 974 |
|
|
# Remove slash from the end
|
| 975 |
|
|
$translation =~ s{/$}{};
|
| 976 |
|
|
|
| 977 |
|
|
if ( $translation eq '' )
|
| 978 |
|
|
{
|
| 979 |
|
|
die "Language not defined in DWWW_LANG, language.conf, "
|
| 980 |
|
|
."or on command line\n";
|
| 981 |
|
|
}
|
| 982 |
|
|
|
| 983 |
|
|
return ($translation,$file_pattern,%OPT);
|
| 984 |
|
|
}
|
| 985 |
|
|
|
| 986 |
|
|
#=================================================
|
| 987 |
|
|
#== read the translators from translator.db
|
| 988 |
|
|
#==
|
| 989 |
|
|
sub read_translators
|
| 990 |
|
|
{
|
| 991 |
|
|
my $lang = shift or die("Internal error: no language specified");
|
| 992 |
|
|
|
| 993 |
|
|
my %translators;
|
| 994 |
|
|
|
| 995 |
|
|
my $db_file = catfile( $lang, 'international', $lang, 'translator.db.pl' );
|
| 996 |
|
|
|
| 997 |
|
|
verbose "Reading translation database $db_file";
|
| 998 |
|
|
|
| 999 |
|
|
if ( -e $db_file)
|
| 1000 |
|
|
{
|
| 1001 |
|
|
require $db_file;
|
| 1002 |
|
|
|
| 1003 |
|
|
verbose "READ TRANSLATOR DB: $db_file\n";
|
| 1004 |
|
|
|
| 1005 |
|
|
%translators = %{ init_translators() };
|
| 1006 |
|
|
|
| 1007 |
|
|
if ( exists $translators{default} )
|
| 1008 |
|
|
{
|
| 1009 |
|
|
my @field_list = keys %{ $translators{default} };
|
| 1010 |
|
|
foreach my $user (keys %translators)
|
| 1011 |
|
|
{
|
| 1012 |
|
|
next if $user eq 'default';
|
| 1013 |
|
|
foreach my $f (@field_list)
|
| 1014 |
|
|
{
|
| 1015 |
|
|
$translators{$user}{$f} = $translators{default}{$f}
|
| 1016 |
|
|
unless exists $translators{$user}{$f};
|
| 1017 |
|
|
}
|
| 1018 |
|
|
}
|
| 1019 |
|
|
}
|
| 1020 |
bas |
1.84 |
}
|
| 1021 |
|
|
else
|
| 1022 |
bas |
1.65 |
{
|
| 1023 |
bas |
1.84 |
die "File `$db_file' doesn't exist!\n"
|
| 1024 |
|
|
."I need my DBs to send mails.\n"
|
| 1025 |
bas |
1.65 |
."Please read the comments in the script and try again\n";
|
| 1026 |
|
|
}
|
| 1027 |
|
|
|
| 1028 |
|
|
return %translators;
|
| 1029 |
peterk |
1.21 |
}
|
| 1030 |
joy |
1.32 |
|
| 1031 |
bas |
1.65 |
#=================================================
|
| 1032 |
|
|
#== check if a single file is up to date
|
| 1033 |
|
|
#== returns ($status,$message)
|
| 1034 |
|
|
#== where status is one of the ST_* constants (see top of file)
|
| 1035 |
|
|
#==
|
| 1036 |
|
|
sub check_file
|
| 1037 |
peterk |
1.57 |
{
|
| 1038 |
bas |
1.65 |
my $file = shift;
|
| 1039 |
|
|
my $orig = shift;
|
| 1040 |
|
|
my $lang = shift;
|
| 1041 |
|
|
my $english_rev = shift; # might be undef
|
| 1042 |
|
|
my $translation_rev = shift; # might be undef
|
| 1043 |
|
|
|
| 1044 |
|
|
die("Internal error: insufficient arguments")
|
| 1045 |
|
|
unless $file and $orig and $lang;
|
| 1046 |
|
|
|
| 1047 |
|
|
# filename of english and translated files
|
| 1048 |
|
|
my $file_orig = catfile( $orig, $file );
|
| 1049 |
|
|
my $file_translation = catfile( $lang, $file );
|
| 1050 |
|
|
|
| 1051 |
|
|
# revision of the latest change in the english file
|
| 1052 |
|
|
my $orig_last_change = $english_rev ? $english_rev->{cmt_rev} : 'n/a';
|
| 1053 |
|
|
|
| 1054 |
|
|
# revision of the english file that was translated
|
| 1055 |
|
|
my $transcheck = Webwml::TransCheck->new( $file_translation );
|
| 1056 |
|
|
my $translation_last_change = $transcheck->revision() || 'n/a';
|
| 1057 |
|
|
my $translation_translator = $transcheck->maintainer() || undef;
|
| 1058 |
|
|
my $translation_maxdelta = $transcheck->maxdelta() || undef;
|
| 1059 |
|
|
|
| 1060 |
|
|
verbose "Checking $file_translation, $orig revision $orig_last_change";
|
| 1061 |
|
|
|
| 1062 |
|
|
# status information
|
| 1063 |
|
|
my $status = undef;
|
| 1064 |
|
|
my $str = undef;
|
| 1065 |
|
|
|
| 1066 |
|
|
# at this point, there are several possibilities:
|
| 1067 |
|
|
# 1) file exists both in english and translation
|
| 1068 |
|
|
# 2) file exists only in english
|
| 1069 |
|
|
# 3) file exists only in translation
|
| 1070 |
|
|
# 4) file exists in neither original or translation: can't happen
|
| 1071 |
|
|
# we handle those cases one by one
|
| 1072 |
|
|
|
| 1073 |
|
|
# 1) both files exist
|
| 1074 |
|
|
if ( -e $file_orig and -e $file_translation )
|
| 1075 |
|
|
{
|
| 1076 |
|
|
# now check if both files have correct revisions
|
| 1077 |
|
|
# again, three cases
|
| 1078 |
|
|
# 1a) original file doesn't have a revision (can't happen)
|
| 1079 |
|
|
# 1b) translated file doesn't have a revision (error in wml file)
|
| 1080 |
|
|
# 1c) revision of both files is known
|
| 1081 |
|
|
|
| 1082 |
|
|
# 1a) no revision for english file
|
| 1083 |
|
|
if ( $orig_last_change eq 'n/a' )
|
| 1084 |
|
|
{
|
| 1085 |
|
|
# this can't happen: something must be wrong with this script
|
| 1086 |
|
|
die( "internal error: no revision for english file" );
|
| 1087 |
|
|
}
|
| 1088 |
|
|
|
| 1089 |
|
|
# 1b) no revision on translated file: error
|
| 1090 |
|
|
elsif ( $translation_last_change eq 'n/a' )
|
| 1091 |
|
|
{
|
| 1092 |
|
|
$status = ST_UNDEFINED;
|
| 1093 |
|
|
$str = "Unknown status of $file_translation "
|
| 1094 |
|
|
."(revision should be $orig_last_change)";
|
| 1095 |
|
|
}
|
| 1096 |
|
|
|
| 1097 |
|
|
# 1c) both files have revisions
|
| 1098 |
|
|
else
|
| 1099 |
|
|
{
|
| 1100 |
|
|
# check the revisions to see if they're up to date
|
| 1101 |
bas |
1.75 |
my $cmp = vcs_cmp_rev( $translation_last_change,
|
| 1102 |
bas |
1.65 |
$orig_last_change );
|
| 1103 |
|
|
|
| 1104 |
|
|
if ( $cmp == 0 ) # revisions equal
|
| 1105 |
|
|
{
|
| 1106 |
|
|
# up to date
|
| 1107 |
|
|
$str = "UpToDate $file_translation";
|
| 1108 |
|
|
$status = ST_UPTODATE;
|
| 1109 |
|
|
}
|
| 1110 |
|
|
elsif ( $cmp == -1 ) # $translation_last_change < $orig_last_change
|
| 1111 |
|
|
{
|
| 1112 |
|
|
# out of date
|
| 1113 |
|
|
$status = ST_NEEDSUPDATE;
|
| 1114 |
|
|
$str = "NeedToUpdate $file_translation from revision "
|
| 1115 |
|
|
."$translation_last_change to revision $orig_last_change";
|
| 1116 |
|
|
}
|
| 1117 |
|
|
else # $translation_last_change > $orig_last_change
|
| 1118 |
|
|
{
|
| 1119 |
|
|
# weirdness: translation is newer than original
|
| 1120 |
|
|
$status = ST_BROKEN;
|
| 1121 |
|
|
$str = "Broken revision number r$translation_last_change "
|
| 1122 |
|
|
."for $file_translation, it should be $orig_last_change";
|
| 1123 |
|
|
}
|
| 1124 |
|
|
}
|
| 1125 |
|
|
}
|
| 1126 |
|
|
|
| 1127 |
|
|
# 2) original file exists, but translation is missing
|
| 1128 |
|
|
elsif ( -e $file_orig and not -e $file_translation )
|
| 1129 |
|
|
{
|
| 1130 |
|
|
$status = ST_MISSING;
|
| 1131 |
|
|
$str = "Missing $file_translation version $orig_last_change";
|
| 1132 |
|
|
}
|
| 1133 |
|
|
|
| 1134 |
|
|
# 3) translation exists, but original is missing
|
| 1135 |
|
|
elsif ( not -e $file_orig and -e $file_translation )
|
| 1136 |
|
|
{
|
| 1137 |
|
|
# the translated file doesn't have a translation header,
|
| 1138 |
|
|
# so it probably is an original
|
| 1139 |
|
|
if ( $translation_last_change eq 'n/a' )
|
| 1140 |
|
|
{
|
| 1141 |
|
|
$status = ST_NOTATRANSL;
|
| 1142 |
|
|
$str = "NotATranslation $file_translation";
|
| 1143 |
|
|
}
|
| 1144 |
bas |
1.75 |
# otherwise, it has a translation header,
|
| 1145 |
bas |
1.65 |
# so the english file was removed
|
| 1146 |
|
|
else
|
| 1147 |
|
|
{
|
| 1148 |
|
|
$status = ST_OBSOLETE;
|
| 1149 |
|
|
$str = "Obsolete $file_translation";
|
| 1150 |
|
|
}
|
| 1151 |
|
|
}
|
| 1152 |
|
|
|
| 1153 |
|
|
# neither original nor translation exists
|
| 1154 |
|
|
else
|
| 1155 |
|
|
{
|
| 1156 |
|
|
# this should never occur, because it means the function was
|
| 1157 |
|
|
# called with an invalid argument
|
| 1158 |
|
|
die( "Internal error: file not present in english nor $lang" );
|
| 1159 |
|
|
}
|
| 1160 |
|
|
|
| 1161 |
|
|
# add name of translator
|
| 1162 |
|
|
$str .= " (maintainer $translation_translator)" if $translation_translator;
|
| 1163 |
|
|
|
| 1164 |
|
|
return ($status,$str,$translation_last_change,
|
| 1165 |
|
|
$translation_translator,$translation_maxdelta);
|
| 1166 |
peterk |
1.57 |
}
|
| 1167 |
|
|
|
| 1168 |
joy |
1.56 |
|
| 1169 |
bas |
1.65 |
# get the encoding of a certain file, by looking for wmlrc
|
| 1170 |
|
|
sub get_file_charset
|
| 1171 |
|
|
{
|
| 1172 |
|
|
my $file = shift or croak("No file specified");
|
| 1173 |
italian |
1.1 |
|
| 1174 |
bas |
1.65 |
# default charset
|
| 1175 |
|
|
my $charset = 'utf-8';
|
| 1176 |
italian |
1.1 |
|
| 1177 |
bas |
1.65 |
# read the wmlrc file
|
| 1178 |
|
|
my $wmlrc_dir = dirname($file);
|
| 1179 |
|
|
while ( not -e catfile( $wmlrc_dir, '.wmlrc' ) )
|
| 1180 |
|
|
{
|
| 1181 |
|
|
$wmlrc_dir = dirname $wmlrc_dir;
|
| 1182 |
|
|
last if length( $wmlrc_dir ) < 3
|
| 1183 |
|
|
}
|
| 1184 |
barbier |
1.36 |
|
| 1185 |
bas |
1.65 |
# now read the wmlrc file to find the charset
|
| 1186 |
|
|
my $wmlrc = catfile( $wmlrc_dir,'.wmlrc' );
|
| 1187 |
|
|
if ( open( my $fd, '<', $wmlrc ) )
|
| 1188 |
|
|
{
|
| 1189 |
|
|
while ( my $line = <$fd> )
|
| 1190 |
|
|
{
|
| 1191 |
|
|
next unless $line =~ m{CHARSET=(.*?)\s*$};
|
| 1192 |
|
|
$charset = $1;
|
| 1193 |
|
|
last;
|
| 1194 |
|
|
}
|
| 1195 |
|
|
close($fd);
|
| 1196 |
|
|
}
|
| 1197 |
|
|
else
|
| 1198 |
|
|
{
|
| 1199 |
|
|
verbose "wmlrc for `$file' not found; assuming $charset charset";
|
| 1200 |
|
|
}
|
| 1201 |
barbier |
1.43 |
|
| 1202 |
bas |
1.65 |
return $charset;
|
| 1203 |
|
|
}
|
| 1204 |
|
|
|
| 1205 |
|
|
sub read_file_enc
|
| 1206 |
|
|
{
|
| 1207 |
|
|
my $file = shift or croak("No file specified");
|
| 1208 |
|
|
|
| 1209 |
|
|
my $charset = get_file_charset( $file );
|
| 1210 |
french |
1.41 |
|
| 1211 |
bas |
1.85 |
return read_file( $file, $charset );
|
| 1212 |
italian |
1.1 |
}
|
| 1213 |
bas |
1.65 |
|
| 1214 |
|
|
__END__
|