xref: /aosp_15_r20/external/cldr/tools/scripts/web/bugdiffs (revision 912701f9769bb47905792267661f0baf2b85bed5)
1*912701f9SAndroid Build Coastguard Worker#!/usr/bin/perl -w
2*912701f9SAndroid Build Coastguard Worker#--*-Perl-*--
3*912701f9SAndroid Build Coastguard Worker
4*912701f9SAndroid Build Coastguard Worker# NOTES:
5*912701f9SAndroid Build Coastguard Worker#
6*912701f9SAndroid Build Coastguard Worker# 'tagscan' refers to the procedure of examining the CVS data (rlog output
7*912701f9SAndroid Build Coastguard Worker# for each file) and determining what bug IDs exist between two tags.
8*912701f9SAndroid Build Coastguard Worker#
9*912701f9SAndroid Build Coastguard Worker# 'dcuthelp' refers to the procedures of examining the CVS rlog cache
10*912701f9SAndroid Build Coastguard Worker# given a tag and a list of bugs, and helping to incorporate those bug
11*912701f9SAndroid Build Coastguard Worker# fixes into the tag.  For this to occur, in each file, any changes after
12*912701f9SAndroid Build Coastguard Worker# tag within the bug list must be contiguous and must begin in the tag's
13*912701f9SAndroid Build Coastguard Worker# revision.
14*912701f9SAndroid Build Coastguard Worker#
15*912701f9SAndroid Build Coastguard Worker# Params:
16*912701f9SAndroid Build Coastguard Worker#  debug - if set, output debugging info
17*912701f9SAndroid Build Coastguard Worker#  user - user name
18*912701f9SAndroid Build Coastguard Worker#  path_info - override actual path info, for debugging, e.g., "/form"
19*912701f9SAndroid Build Coastguard Worker#  mod - module(s) list
20*912701f9SAndroid Build Coastguard Worker#  include_attic - if set, include Attic during search (ignored by default)
21*912701f9SAndroid Build Coastguard Worker
22*912701f9SAndroid Build Coastguard Workeruse strict;
23*912701f9SAndroid Build Coastguard Workeruse CGI;
24*912701f9SAndroid Build Coastguard Worker#use CGI::Carp qw(fatalsToBrowser); # Do NOT use this -- doesn't work
25*912701f9SAndroid Build Coastguard Workeruse File::Path;
26*912701f9SAndroid Build Coastguard Workeruse IO::Handle;
27*912701f9SAndroid Build Coastguard Workeruse Time::Local 'timelocal_nocheck';
28*912701f9SAndroid Build Coastguard Workeruse Carp;
29*912701f9SAndroid Build Coastguard Worker#use Data::Dumper;
30*912701f9SAndroid Build Coastguard Worker
31*912701f9SAndroid Build Coastguard Workeruse vars qw($QUERY $DEBUG $USER $TITLE $CLDR
32*912701f9SAndroid Build Coastguard Worker	    $DIFF_URL $DIFF_URL_SUFFIX $CVSWEB_REP_ID $CVSWEB_REP_SUFF $LOG_URL_SUFFIX $SHOW_URL $SHOW_URL_SUFFIX $LOG_URL
33*912701f9SAndroid Build Coastguard Worker	    $CVSROOT $BASE_REV %MOD_ABBREV $DEFAULT_MOD $NO_JITTERBUG
34*912701f9SAndroid Build Coastguard Worker	    $CACHE $INSTA $INSTA_ATTIC
35*912701f9SAndroid Build Coastguard Worker	    $UPDATE_COUNT $UPDATE_ATTIC_COUNT $UPDATE_NONATTIC_COUNT
36*912701f9SAndroid Build Coastguard Worker	    $TAGSCAN_TAG_LO $TAGSCAN_TAG_HI %TAGSCAN_IDS $TAGSCAN_COUNT
37*912701f9SAndroid Build Coastguard Worker	    $TAGSCAN_TAG_HI_DATE
38*912701f9SAndroid Build Coastguard Worker	    %TAGSCAN_ALLTAGS %TAGSCAN_WHY
39*912701f9SAndroid Build Coastguard Worker	    $DCUTHELP_TAG %DCUTHELP_IDS
40*912701f9SAndroid Build Coastguard Worker	    @DCUTHELP_BADFILES $DCUTHELP_COUNT @DCUTHELP_RETAGS
41*912701f9SAndroid Build Coastguard Worker	    @TAGLESS_FILES @BRANCHED_FILES @NO_JITTERBUG_FILES
42*912701f9SAndroid Build Coastguard Worker	    %MODE_MAP $NOW $YEAR $CVS_MSG_KW
43*912701f9SAndroid Build Coastguard Worker	    );
44*912701f9SAndroid Build Coastguard Worker
45*912701f9SAndroid Build Coastguard Worker&initGlobals;
46*912701f9SAndroid Build Coastguard Worker&main;
47*912701f9SAndroid Build Coastguard Workerexit(0);
48*912701f9SAndroid Build Coastguard Worker
49*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
50*912701f9SAndroid Build Coastguard Workersub initGlobals() {
51*912701f9SAndroid Build Coastguard Worker    $QUERY = new CGI;
52*912701f9SAndroid Build Coastguard Worker
53*912701f9SAndroid Build Coastguard Worker    $DEBUG = $QUERY->param('debug');
54*912701f9SAndroid Build Coastguard Worker    $CLDR=1;
55*912701f9SAndroid Build Coastguard Worker
56*912701f9SAndroid Build Coastguard Worker    # User name, if any.  We try to propagate the user name so a logged-in
57*912701f9SAndroid Build Coastguard Worker    # jitterbug user can stay that way.
58*912701f9SAndroid Build Coastguard Worker    $USER = $QUERY->param('user');
59*912701f9SAndroid Build Coastguard Worker
60*912701f9SAndroid Build Coastguard Worker    $CVSWEB_REP_ID = "ICU";
61*912701f9SAndroid Build Coastguard Worker
62*912701f9SAndroid Build Coastguard Worker    if ($CLDR == 0) {
63*912701f9SAndroid Build Coastguard Worker    	$TITLE="ICU Jitterbug Diffs";
64*912701f9SAndroid Build Coastguard Worker    } else {
65*912701f9SAndroid Build Coastguard Worker	$TITLE="CLDR Jitterbug Diffs";
66*912701f9SAndroid Build Coastguard Worker    }
67*912701f9SAndroid Build Coastguard Worker    #$CVSWEB_REP_SUFF = "&cvsroot=" . $CVSWEB_REP_ID;
68*912701f9SAndroid Build Coastguard Worker    $CVSWEB_REP_SUFF = "";
69*912701f9SAndroid Build Coastguard Worker
70*912701f9SAndroid Build Coastguard Worker    # The following URLs should be suffixed with a module name
71*912701f9SAndroid Build Coastguard Worker    # such as "icu/icu".
72*912701f9SAndroid Build Coastguard Worker
73*912701f9SAndroid Build Coastguard Worker    # Display the diffs between two revisions of a file
74*912701f9SAndroid Build Coastguard Worker    # E.g., suffix with "/icu/icu/license.html.diff?r1=1.2&r2=1.3"
75*912701f9SAndroid Build Coastguard Worker    $DIFF_URL = "http://www.unicode.org/cgi-bin/viewcvs.cgi"; # No trailing "/"
76*912701f9SAndroid Build Coastguard Worker    $DIFF_URL_SUFFIX = $CVSWEB_REP_SUFF;
77*912701f9SAndroid Build Coastguard Worker
78*912701f9SAndroid Build Coastguard Worker    # Display a specific file revision
79*912701f9SAndroid Build Coastguard Worker    # E.g., suffix with "/icu/icu/license.html?rev=1.1$SHOW_URL_SUFFIX"
80*912701f9SAndroid Build Coastguard Worker    $SHOW_URL = $DIFF_URL; # No trailing "/"
81*912701f9SAndroid Build Coastguard Worker    $SHOW_URL_SUFFIX = "&content-type=text/x-cvsweb-markup" . $CVSWEB_REP_SUFF;
82*912701f9SAndroid Build Coastguard Worker
83*912701f9SAndroid Build Coastguard Worker    # Display the CVS log for a file
84*912701f9SAndroid Build Coastguard Worker    # E.g., suffix with "/icu/icu/license.html"
85*912701f9SAndroid Build Coastguard Worker    $LOG_URL = $DIFF_URL; # No trailing "/"
86*912701f9SAndroid Build Coastguard Worker    $LOG_URL_SUFFIX = $CVSWEB_REP_SUFF;
87*912701f9SAndroid Build Coastguard Worker
88*912701f9SAndroid Build Coastguard Worker    # CVS root
89*912701f9SAndroid Build Coastguard Worker    if ( $CLDR == 0 ) {
90*912701f9SAndroid Build Coastguard Worker	$CVSROOT = "/data/mirrors/icu"; # Must NOT end with "/"
91*912701f9SAndroid Build Coastguard Worker    } else {
92*912701f9SAndroid Build Coastguard Worker	$CVSROOT = "/home/cvsroot";
93*912701f9SAndroid Build Coastguard Worker    }
94*912701f9SAndroid Build Coastguard Worker
95*912701f9SAndroid Build Coastguard Worker    # A fake revision number indicating the slot before the oldest revision in
96*912701f9SAndroid Build Coastguard Worker    # the rlog history.  Not user visible.
97*912701f9SAndroid Build Coastguard Worker    $BASE_REV = "0";
98*912701f9SAndroid Build Coastguard Worker
99*912701f9SAndroid Build Coastguard Worker    if ($CLDR == 0) {
100*912701f9SAndroid Build Coastguard Worker    # Recognized abbreviated module names.
101*912701f9SAndroid Build Coastguard Worker    %MOD_ABBREV = (
102*912701f9SAndroid Build Coastguard Worker        icu          => 'icu',
103*912701f9SAndroid Build Coastguard Worker        icuapps      => 'icuapps',
104*912701f9SAndroid Build Coastguard Worker        icu4j        => 'icu4j',
105*912701f9SAndroid Build Coastguard Worker        icu4jni      => 'icu4jni',
106*912701f9SAndroid Build Coastguard Worker        unicodetools => 'unicodetools',
107*912701f9SAndroid Build Coastguard Worker        charset      => 'charset',
108*912701f9SAndroid Build Coastguard Worker    );
109*912701f9SAndroid Build Coastguard Worker
110*912701f9SAndroid Build Coastguard Worker    # Default modules to search
111*912701f9SAndroid Build Coastguard Worker    $DEFAULT_MOD = 'icu icu4j';
112*912701f9SAndroid Build Coastguard Worker    } else {
113*912701f9SAndroid Build Coastguard Worker    # Recognized abbreviated module names.
114*912701f9SAndroid Build Coastguard Worker    %MOD_ABBREV = (
115*912701f9SAndroid Build Coastguard Worker        cldr      => 'cldr',
116*912701f9SAndroid Build Coastguard Worker        common      => 'cldr/common',
117*912701f9SAndroid Build Coastguard Worker    );
118*912701f9SAndroid Build Coastguard Worker
119*912701f9SAndroid Build Coastguard Worker    # Default modules to search
120*912701f9SAndroid Build Coastguard Worker    $DEFAULT_MOD = 'common';
121*912701f9SAndroid Build Coastguard Worker    }
122*912701f9SAndroid Build Coastguard Worker
123*912701f9SAndroid Build Coastguard Worker
124*912701f9SAndroid Build Coastguard Worker    # Magic Jitterbug ID used when a CVS checkin does not include a
125*912701f9SAndroid Build Coastguard Worker    # Jitterbug ID.  Should be unlikely (or impossible) to be a real
126*912701f9SAndroid Build Coastguard Worker    # Jitterbug ID.
127*912701f9SAndroid Build Coastguard Worker    $NO_JITTERBUG = 9999987;
128*912701f9SAndroid Build Coastguard Worker
129*912701f9SAndroid Build Coastguard Worker    # Root of our cache of CVS meta-information.  Right now this cache
130*912701f9SAndroid Build Coastguard Worker    # takes the form of a mirror of /usr/cvs.  We only mirror
131*912701f9SAndroid Build Coastguard Worker    # /usr/cvs/icu/icu and /usr/cvs/icu4j/icu4j at this point.  All CVS
132*912701f9SAndroid Build Coastguard Worker    # files (*,v) have an identically named file in the same location in
133*912701f9SAndroid Build Coastguard Worker    # the cache.  Currently the cache file is the output of rlog.  In the
134*912701f9SAndroid Build Coastguard Worker    # future a more compressed form could be used (although there isn't
135*912701f9SAndroid Build Coastguard Worker    # much to be gained, maybe 10%).  Instead of grepping over the CVS
136*912701f9SAndroid Build Coastguard Worker    # repository, we grep over the cache.  This cuts the grep time by
137*912701f9SAndroid Build Coastguard Worker    # about 90%.  Before using the cache, we update it by walking through
138*912701f9SAndroid Build Coastguard Worker    # the CVS repository and checking file mod dates.  Any file that's
139*912701f9SAndroid Build Coastguard Worker    # been changed gets updated in the cache.
140*912701f9SAndroid Build Coastguard Worker    # Use real path; link causes problems.
141*912701f9SAndroid Build Coastguard Worker    #$CACHE = "/www/software10/cgi-bin/icu/grepj.cache";
142*912701f9SAndroid Build Coastguard Worker    if($CLDR==0) {
143*912701f9SAndroid Build Coastguard Worker    	$CACHE = "/tmp/icu-grepj.cache"; # No trailing "/"
144*912701f9SAndroid Build Coastguard Worker    } else {
145*912701f9SAndroid Build Coastguard Worker    	$CACHE = "/tmp/icu-grepj-cldr.cache"; # No trailing "/"
146*912701f9SAndroid Build Coastguard Worker    }
147*912701f9SAndroid Build Coastguard Worker
148*912701f9SAndroid Build Coastguard Worker    # Another cache that holds the results of the last searches.
149*912701f9SAndroid Build Coastguard Worker    # Invalidate this cache whenever the main cache needs updating.
150*912701f9SAndroid Build Coastguard Worker    # This cache consists of files named "1234".  Each file
151*912701f9SAndroid Build Coastguard Worker    # contains the final HTML for that bug ID.  Searches that include
152*912701f9SAndroid Build Coastguard Worker    # the attic are kept in a subdirectory 'Attic'.
153*912701f9SAndroid Build Coastguard Worker    $INSTA = "$CACHE/insta";
154*912701f9SAndroid Build Coastguard Worker    $INSTA_ATTIC = "$INSTA/Attic";
155*912701f9SAndroid Build Coastguard Worker
156*912701f9SAndroid Build Coastguard Worker    # Count of updated cache files
157*912701f9SAndroid Build Coastguard Worker    $UPDATE_COUNT = 0;
158*912701f9SAndroid Build Coastguard Worker    $UPDATE_ATTIC_COUNT = 0;
159*912701f9SAndroid Build Coastguard Worker    $UPDATE_NONATTIC_COUNT = 0;
160*912701f9SAndroid Build Coastguard Worker
161*912701f9SAndroid Build Coastguard Worker    # Dispatch table mapping path_info to sub
162*912701f9SAndroid Build Coastguard Worker    %MODE_MAP = (
163*912701f9SAndroid Build Coastguard Worker	'/top'         =>  \&emit_top,
164*912701f9SAndroid Build Coastguard Worker	'/form'        =>  \&emit_form,
165*912701f9SAndroid Build Coastguard Worker	'/difflist'    =>  \&emit_difflist,
166*912701f9SAndroid Build Coastguard Worker	'/nav'         =>  \&emit_nav,
167*912701f9SAndroid Build Coastguard Worker	'/result'      =>  \&emit_result,
168*912701f9SAndroid Build Coastguard Worker	'/help'        =>  \&emit_help,
169*912701f9SAndroid Build Coastguard Worker	'/admintop'    =>  \&emit_admintop,
170*912701f9SAndroid Build Coastguard Worker	'/adminform'   =>  \&emit_adminform,
171*912701f9SAndroid Build Coastguard Worker	'/adminresult' =>  \&emit_adminresult,
172*912701f9SAndroid Build Coastguard Worker        '/localdiff'   =>  \&emit_localdiff,
173*912701f9SAndroid Build Coastguard Worker    );
174*912701f9SAndroid Build Coastguard Worker
175*912701f9SAndroid Build Coastguard Worker    $NOW = time();
176*912701f9SAndroid Build Coastguard Worker    $YEAR = 1900+@{[localtime]}[5]; # Get the current year
177*912701f9SAndroid Build Coastguard Worker
178*912701f9SAndroid Build Coastguard Worker    # Regex for grepping for jitterbug checkin comments
179*912701f9SAndroid Build Coastguard Worker    # Will be surrounded by parens
180*912701f9SAndroid Build Coastguard Worker    if($CLDR == 0) {
181*912701f9SAndroid Build Coastguard Worker	$CVS_MSG_KW = "jitterbug|fixed";
182*912701f9SAndroid Build Coastguard Worker    } else {
183*912701f9SAndroid Build Coastguard Worker    	$CVS_MSG_KW = "cldrbug";
184*912701f9SAndroid Build Coastguard Worker    }
185*912701f9SAndroid Build Coastguard Worker}
186*912701f9SAndroid Build Coastguard Worker
187*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
188*912701f9SAndroid Build Coastguard Worker# This script generates various frames within framesets.  The 'mode'
189*912701f9SAndroid Build Coastguard Worker# parameter determines which frame is generated.
190*912701f9SAndroid Build Coastguard Workersub main() {
191*912701f9SAndroid Build Coastguard Worker
192*912701f9SAndroid Build Coastguard Worker    STDOUT->autoflush(1); # Make progress output appear progressively...
193*912701f9SAndroid Build Coastguard Worker
194*912701f9SAndroid Build Coastguard Worker    my $needed = 'h'; # next up: 'h'eader or 'e'nd_html
195*912701f9SAndroid Build Coastguard Worker
196*912701f9SAndroid Build Coastguard Worker    eval {
197*912701f9SAndroid Build Coastguard Worker	local $SIG{'__DIE__'}; # disable installed DIE hooks
198*912701f9SAndroid Build Coastguard Worker	local $SIG{'__WARN__'} = sub {  die $_[0]; }; # transmute warnings
199*912701f9SAndroid Build Coastguard Worker
200*912701f9SAndroid Build Coastguard Worker	# The path info specifies what we are being called to emit.
201*912701f9SAndroid Build Coastguard Worker	# This script emits the frameset and the frames within it
202*912701f9SAndroid Build Coastguard Worker	# depending on this param.  For the URL
203*912701f9SAndroid Build Coastguard Worker	# "http://oss.software.ibm.com/cvs/icu-jinfo/foo", the path
204*912701f9SAndroid Build Coastguard Worker	# info is "/foo".  The path info can be overridden (for debugging)
205*912701f9SAndroid Build Coastguard Worker	# with a CGI param of "path_info=/bar".
206*912701f9SAndroid Build Coastguard Worker	my $path_info = $QUERY->path_info;
207*912701f9SAndroid Build Coastguard Worker	if ($QUERY->param('path_info')) {
208*912701f9SAndroid Build Coastguard Worker	    $path_info = $QUERY->param('path_info');
209*912701f9SAndroid Build Coastguard Worker	}
210*912701f9SAndroid Build Coastguard Worker
211*912701f9SAndroid Build Coastguard Worker	# Simplify it:  "/foo/..." or "/foo&..." => "/foo"
212*912701f9SAndroid Build Coastguard Worker	$path_info =~ s|(\w)\W.*|$1|;
213*912701f9SAndroid Build Coastguard Worker	$path_info ||= '/top'; # default
214*912701f9SAndroid Build Coastguard Worker
215*912701f9SAndroid Build Coastguard Worker	my $fn = $MODE_MAP{$path_info};
216*912701f9SAndroid Build Coastguard Worker	die "unknown path_info \"$path_info\"" unless ($fn);
217*912701f9SAndroid Build Coastguard Worker
218*912701f9SAndroid Build Coastguard Worker	if ($path_info ne '/localdiff') {
219*912701f9SAndroid Build Coastguard Worker	    print $QUERY->header;
220*912701f9SAndroid Build Coastguard Worker	    $needed = 'e';
221*912701f9SAndroid Build Coastguard Worker	}
222*912701f9SAndroid Build Coastguard Worker
223*912701f9SAndroid Build Coastguard Worker	$fn->();
224*912701f9SAndroid Build Coastguard Worker    };
225*912701f9SAndroid Build Coastguard Worker
226*912701f9SAndroid Build Coastguard Worker    if ($@) {
227*912701f9SAndroid Build Coastguard Worker	if ($needed eq 'h') {
228*912701f9SAndroid Build Coastguard Worker	    print $QUERY->header;
229*912701f9SAndroid Build Coastguard Worker	    $needed = 'e';
230*912701f9SAndroid Build Coastguard Worker	}
231*912701f9SAndroid Build Coastguard Worker	print "<hr><b>Internal error: ", $@,
232*912701f9SAndroid Build Coastguard Worker              "<br>Please contact <a href=\"mailto:alanliu\@us.ibm.com\">Alan</a></b>";
233*912701f9SAndroid Build Coastguard Worker    }
234*912701f9SAndroid Build Coastguard Worker
235*912701f9SAndroid Build Coastguard Worker    if ($needed eq 'e') {
236*912701f9SAndroid Build Coastguard Worker	print $QUERY->end_html;
237*912701f9SAndroid Build Coastguard Worker    }
238*912701f9SAndroid Build Coastguard Worker}
239*912701f9SAndroid Build Coastguard Worker
240*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
241*912701f9SAndroid Build Coastguard Worker# Create URL for the reviewer index
242*912701f9SAndroid Build Coastguard Worker# @param user (or empty string if none)
243*912701f9SAndroid Build Coastguard Workersub reviewersURL {
244*912701f9SAndroid Build Coastguard Worker    my $user = shift || '';
245*912701f9SAndroid Build Coastguard Worker    $user = "?user=$user" if ($user);
246*912701f9SAndroid Build Coastguard Worker    return "http://bugs.icu-project.org/cgibin/private/byname/review$user";
247*912701f9SAndroid Build Coastguard Worker}
248*912701f9SAndroid Build Coastguard Worker
249*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
250*912701f9SAndroid Build Coastguard Worker# Create URL for jitterbug
251*912701f9SAndroid Build Coastguard Worker# @param user (or empty string if none)
252*912701f9SAndroid Build Coastguard Worker# @param ID (or empty if none);
253*912701f9SAndroid Build Coastguard Workersub jitterbugURL {
254*912701f9SAndroid Build Coastguard Worker    my $user = shift || '';
255*912701f9SAndroid Build Coastguard Worker    my $id = shift || '';
256*912701f9SAndroid Build Coastguard Worker
257*912701f9SAndroid Build Coastguard Worker   if($CLDR == 0) {
258*912701f9SAndroid Build Coastguard Worker    if ($id ne '') {
259*912701f9SAndroid Build Coastguard Worker	if ($user) {
260*912701f9SAndroid Build Coastguard Worker	    return "http://bugs.icu-project.org/cgibin/private/icu-bugs-private?;user=$user;findid=$id";
261*912701f9SAndroid Build Coastguard Worker	} else {
262*912701f9SAndroid Build Coastguard Worker	    return "http://bugs.icu-project.org/cgibin/icu-bugs?findid=$id";
263*912701f9SAndroid Build Coastguard Worker	}
264*912701f9SAndroid Build Coastguard Worker    } else {
265*912701f9SAndroid Build Coastguard Worker	if ($user) {
266*912701f9SAndroid Build Coastguard Worker	    return "http://bugs.icu-project.org/cgibin/private/icu-bugs-private?;user=$user;";
267*912701f9SAndroid Build Coastguard Worker	} else {
268*912701f9SAndroid Build Coastguard Worker	    return "http://bugs.icu-project.org/cgibin/icu-bugs";
269*912701f9SAndroid Build Coastguard Worker	}
270*912701f9SAndroid Build Coastguard Worker    }
271*912701f9SAndroid Build Coastguard Worker  } else {
272*912701f9SAndroid Build Coastguard Worker    if ($id ne '') {
273*912701f9SAndroid Build Coastguard Worker	if ($user) {
274*912701f9SAndroid Build Coastguard Worker	    return "http://bugs.icu-project.org/cgibin/cldr/locale-bugs-private?;user=$user;findid=$id";
275*912701f9SAndroid Build Coastguard Worker	} else {
276*912701f9SAndroid Build Coastguard Worker	    return "http://bugs.icu-project.org/cgibin/locale-bugs?findid=$id";
277*912701f9SAndroid Build Coastguard Worker	}
278*912701f9SAndroid Build Coastguard Worker    } else {
279*912701f9SAndroid Build Coastguard Worker	if ($user) {
280*912701f9SAndroid Build Coastguard Worker	    return "http://bugs.icu-project.org/cgibin/cldr/locale-bugs-private?;user=$user;";
281*912701f9SAndroid Build Coastguard Worker	} else {
282*912701f9SAndroid Build Coastguard Worker	    return "http://bugs.icu-project.org/cgibin/locale-bugs";
283*912701f9SAndroid Build Coastguard Worker	}
284*912701f9SAndroid Build Coastguard Worker    }
285*912701f9SAndroid Build Coastguard Worker   }
286*912701f9SAndroid Build Coastguard Worker}
287*912701f9SAndroid Build Coastguard Worker
288*912701f9SAndroid Build Coastguard Worker######################################################################
289*912701f9SAndroid Build Coastguard Worker# HTML GUI
290*912701f9SAndroid Build Coastguard Worker######################################################################
291*912701f9SAndroid Build Coastguard Worker
292*912701f9SAndroid Build Coastguard Worker# Emit the HTML for the top frameset in normal (bug diffs) mode
293*912701f9SAndroid Build Coastguard Workersub emit_top {
294*912701f9SAndroid Build Coastguard Worker    # Propagate url parameters down to the frames within the frameset
295*912701f9SAndroid Build Coastguard Worker
296*912701f9SAndroid Build Coastguard Worker    my $self = $QUERY->url(-full=>1, -query=>1);
297*912701f9SAndroid Build Coastguard Worker    my $f  = urlPathInfo($self, '/form');
298*912701f9SAndroid Build Coastguard Worker    my $dl = urlPathInfo($self, '/difflist');
299*912701f9SAndroid Build Coastguard Worker    my $n  = urlPathInfo($self, '/nav');
300*912701f9SAndroid Build Coastguard Worker    my $r  = urlPathInfo($self, '/result');
301*912701f9SAndroid Build Coastguard Worker
302*912701f9SAndroid Build Coastguard Worker    print <<END;
303*912701f9SAndroid Build Coastguard Worker<html><head><title>$TITLE</title></head>
304*912701f9SAndroid Build Coastguard Worker<!--$self-->
305*912701f9SAndroid Build Coastguard Worker<frameset cols="300,*">
306*912701f9SAndroid Build Coastguard Worker <frameset rows="135,*">
307*912701f9SAndroid Build Coastguard Worker  <frame src="$f" name="form" scrolling=no>
308*912701f9SAndroid Build Coastguard Worker  <frame src="$dl" name="difflist">
309*912701f9SAndroid Build Coastguard Worker </frameset>
310*912701f9SAndroid Build Coastguard Worker <frame src="$r" name="result">
311*912701f9SAndroid Build Coastguard Worker</frameset>
312*912701f9SAndroid Build Coastguard WorkerEND
313*912701f9SAndroid Build Coastguard Worker
314*912701f9SAndroid Build Coastguard Worker# <frameset rows="30,*">
315*912701f9SAndroid Build Coastguard Worker#  <frame src="$n" name="nav" scrolling=no>
316*912701f9SAndroid Build Coastguard Worker#  <frame src="$r" name="result">
317*912701f9SAndroid Build Coastguard Worker# </frameset>
318*912701f9SAndroid Build Coastguard Worker}
319*912701f9SAndroid Build Coastguard Worker
320*912701f9SAndroid Build Coastguard Workersub emit_form {
321*912701f9SAndroid Build Coastguard Worker    print $QUERY->start_html(-title=>$TITLE,
322*912701f9SAndroid Build Coastguard Worker                             -target=>'difflist');
323*912701f9SAndroid Build Coastguard Worker
324*912701f9SAndroid Build Coastguard Worker    my $script_name = $QUERY->script_name;
325*912701f9SAndroid Build Coastguard Worker
326*912701f9SAndroid Build Coastguard Worker    print $QUERY->startform(-action=>urlPathInfo($script_name, '/difflist'),
327*912701f9SAndroid Build Coastguard Worker                            -target=>'difflist',
328*912701f9SAndroid Build Coastguard Worker			    -method=>'GET');
329*912701f9SAndroid Build Coastguard Worker
330*912701f9SAndroid Build Coastguard Worker    my $user = $QUERY->param('user') || '';
331*912701f9SAndroid Build Coastguard Worker
332*912701f9SAndroid Build Coastguard Worker    print "<H2>$TITLE"; # h1 too big
333*912701f9SAndroid Build Coastguard Worker    print " <FONT SIZE=-1>($user)</FONT>" if ($user);
334*912701f9SAndroid Build Coastguard Worker    print "</H2>";
335*912701f9SAndroid Build Coastguard Worker
336*912701f9SAndroid Build Coastguard Worker    print "ID? ",$QUERY->textfield(-name=>'id',-size=>5)
337*912701f9SAndroid Build Coastguard Worker	, $QUERY->submit(-name=>'Search')
338*912701f9SAndroid Build Coastguard Worker	, " <FONT SIZE=-1><A href=\""
339*912701f9SAndroid Build Coastguard Worker	, urlPathInfo($script_name, '/help')
340*912701f9SAndroid Build Coastguard Worker	, "\">Help</A></FONT>";
341*912701f9SAndroid Build Coastguard Worker
342*912701f9SAndroid Build Coastguard Worker    print "\&nbsp;<FONT SIZE=-1>"
343*912701f9SAndroid Build Coastguard Worker	, "<A href=\"", urlPathInfo($script_name, '/admintop')
344*912701f9SAndroid Build Coastguard Worker	, "?user=$user\" target=\"_top\">Admin</A></FONT>";
345*912701f9SAndroid Build Coastguard Worker
346*912701f9SAndroid Build Coastguard Worker    print "<BR>\nModules:&nbsp;";
347*912701f9SAndroid Build Coastguard Worker    print $QUERY->textfield(-name=>'mod',
348*912701f9SAndroid Build Coastguard Worker			    -default=>$DEFAULT_MOD,
349*912701f9SAndroid Build Coastguard Worker			    -size=>30);
350*912701f9SAndroid Build Coastguard Worker
351*912701f9SAndroid Build Coastguard Worker    print "<BR>\n";
352*912701f9SAndroid Build Coastguard Worker
353*912701f9SAndroid Build Coastguard Worker    print "<FONT SIZE=-1>";
354*912701f9SAndroid Build Coastguard Worker    print $QUERY->checkbox(-name=>"include_attic",
355*912701f9SAndroid Build Coastguard Worker                           -label=>"Incl. Attic");
356*912701f9SAndroid Build Coastguard Worker    print $QUERY->checkbox(-name=>"localdiff",
357*912701f9SAndroid Build Coastguard Worker			   -label=>"Local Diff");
358*912701f9SAndroid Build Coastguard Worker    print "</FONT>";
359*912701f9SAndroid Build Coastguard Worker
360*912701f9SAndroid Build Coastguard Worker    print "\&nbsp;<A href=\"", reviewersURL($user), "\" target=\"_top\" title=\"List bugs by reviewer\">Reviewers</A>";
361*912701f9SAndroid Build Coastguard Worker
362*912701f9SAndroid Build Coastguard Worker    print "\&nbsp;<A href=\"", jitterbugURL($user), "\" target=\"_top\" title=\"Go to main Jitterbug page\">Jitterbug</A>";
363*912701f9SAndroid Build Coastguard Worker
364*912701f9SAndroid Build Coastguard Worker    # Propagate params that don't have corresponding form elements
365*912701f9SAndroid Build Coastguard Worker    print $QUERY->hidden('user');
366*912701f9SAndroid Build Coastguard Worker    print $QUERY->hidden('debug');
367*912701f9SAndroid Build Coastguard Worker    if($CLDR==1) {
368*912701f9SAndroid Build Coastguard Worker      print $QUERY->hidden('cldr');
369*912701f9SAndroid Build Coastguard Worker    }
370*912701f9SAndroid Build Coastguard Worker
371*912701f9SAndroid Build Coastguard Worker    print $QUERY->end_form;
372*912701f9SAndroid Build Coastguard Worker}
373*912701f9SAndroid Build Coastguard Worker
374*912701f9SAndroid Build Coastguard Workersub emit_nav {
375*912701f9SAndroid Build Coastguard Worker    print $QUERY->start_html(-title=>$TITLE,
376*912701f9SAndroid Build Coastguard Worker                             -target=>'result');
377*912701f9SAndroid Build Coastguard Worker    print "Under construction: Navigation bar goes here";
378*912701f9SAndroid Build Coastguard Worker}
379*912701f9SAndroid Build Coastguard Worker
380*912701f9SAndroid Build Coastguard Workersub emit_difflist {
381*912701f9SAndroid Build Coastguard Worker    print $QUERY->start_html(-title=>$TITLE,
382*912701f9SAndroid Build Coastguard Worker                             -target=>'result');
383*912701f9SAndroid Build Coastguard Worker
384*912701f9SAndroid Build Coastguard Worker    ############################################################
385*912701f9SAndroid Build Coastguard Worker    # ID
386*912701f9SAndroid Build Coastguard Worker
387*912701f9SAndroid Build Coastguard Worker    my $ID = $QUERY->param('id') || '';
388*912701f9SAndroid Build Coastguard Worker    $ID =~ s/\s//g;
389*912701f9SAndroid Build Coastguard Worker
390*912701f9SAndroid Build Coastguard Worker    #print "<br/><b>query:</b>";
391*912701f9SAndroid Build Coastguard Worker    #print $QUERY->Dump;
392*912701f9SAndroid Build Coastguard Worker    #print "<br/>";
393*912701f9SAndroid Build Coastguard Worker
394*912701f9SAndroid Build Coastguard Worker    if ($ID eq '') {
395*912701f9SAndroid Build Coastguard Worker	print "(Warning: search, but No ID given.)<br/> \n";
396*912701f9SAndroid Build Coastguard Worker        &emit_help;
397*912701f9SAndroid Build Coastguard Worker        return;
398*912701f9SAndroid Build Coastguard Worker    }
399*912701f9SAndroid Build Coastguard Worker
400*912701f9SAndroid Build Coastguard Worker    if ($ID =~ /^0*(\d+)$/) {
401*912701f9SAndroid Build Coastguard Worker	$ID = $1;
402*912701f9SAndroid Build Coastguard Worker    } else {
403*912701f9SAndroid Build Coastguard Worker        print "\"$ID\" is not a valid Jitterbug ID.  Please ";
404*912701f9SAndroid Build Coastguard Worker        print "enter one or more decimal digits.";
405*912701f9SAndroid Build Coastguard Worker        return;
406*912701f9SAndroid Build Coastguard Worker    }
407*912701f9SAndroid Build Coastguard Worker
408*912701f9SAndroid Build Coastguard Worker    ############################################################
409*912701f9SAndroid Build Coastguard Worker    # User
410*912701f9SAndroid Build Coastguard Worker
411*912701f9SAndroid Build Coastguard Worker    my $user = $QUERY->param('user');
412*912701f9SAndroid Build Coastguard Worker
413*912701f9SAndroid Build Coastguard Worker    ############################################################
414*912701f9SAndroid Build Coastguard Worker    # Modules
415*912701f9SAndroid Build Coastguard Worker
416*912701f9SAndroid Build Coastguard Worker    my @m;
417*912701f9SAndroid Build Coastguard Worker    return if (!parseMod(\@m)); # what modules are we searching?
418*912701f9SAndroid Build Coastguard Worker
419*912701f9SAndroid Build Coastguard Worker    my $localDiff = $QUERY->param('localdiff');
420*912701f9SAndroid Build Coastguard Worker
421*912701f9SAndroid Build Coastguard Worker    # Only use the INSTA cache for standard module searches.
422*912701f9SAndroid Build Coastguard Worker    my $isStd = (join(' ', sort @m) eq 'icu/icu icu4j/icu4j')
423*912701f9SAndroid Build Coastguard Worker	&& !$localDiff;
424*912701f9SAndroid Build Coastguard Worker
425*912701f9SAndroid Build Coastguard Worker    ############################################################
426*912701f9SAndroid Build Coastguard Worker    # Output
427*912701f9SAndroid Build Coastguard Worker
428*912701f9SAndroid Build Coastguard Worker    print "What is Jitterbug ", jitterbugLink($user, $ID), "?";
429*912701f9SAndroid Build Coastguard Worker
430*912701f9SAndroid Build Coastguard Worker    foreach (@m) {
431*912701f9SAndroid Build Coastguard Worker	updateCacheDir($_);
432*912701f9SAndroid Build Coastguard Worker    }
433*912701f9SAndroid Build Coastguard Worker
434*912701f9SAndroid Build Coastguard Worker    # If the cache has been updated then the instaCache entries
435*912701f9SAndroid Build Coastguard Worker    # are all invalid and must be deleted.  Otherwise try to
436*912701f9SAndroid Build Coastguard Worker    # look up the diffs from the instaCache.
437*912701f9SAndroid Build Coastguard Worker    mkpath($INSTA_ATTIC, 0, 0777);
438*912701f9SAndroid Build Coastguard Worker    if ($UPDATE_COUNT) {
439*912701f9SAndroid Build Coastguard Worker	print "done ($UPDATE_NONATTIC_COUNT,$UPDATE_ATTIC_COUNT).";
440*912701f9SAndroid Build Coastguard Worker	resetInstaCache(0);
441*912701f9SAndroid Build Coastguard Worker    } elsif ($isStd) {
442*912701f9SAndroid Build Coastguard Worker	my $diffs = instaGet($ID);
443*912701f9SAndroid Build Coastguard Worker	if ($diffs) {
444*912701f9SAndroid Build Coastguard Worker	    print $diffs;
445*912701f9SAndroid Build Coastguard Worker	    print "<BR><EM><FONT SIZE=-1>(Results from cache)</FONT></EM>";
446*912701f9SAndroid Build Coastguard Worker	    return;
447*912701f9SAndroid Build Coastguard Worker	}
448*912701f9SAndroid Build Coastguard Worker    }
449*912701f9SAndroid Build Coastguard Worker
450*912701f9SAndroid Build Coastguard Worker    # If we don't find the ID in the instaCache, then generate
451*912701f9SAndroid Build Coastguard Worker    # the diffs the hard way and store the result in the
452*912701f9SAndroid Build Coastguard Worker    # instaCache.
453*912701f9SAndroid Build Coastguard Worker    my $diffs;
454*912701f9SAndroid Build Coastguard Worker    foreach my $module (@m) {
455*912701f9SAndroid Build Coastguard Worker	debugOut("module $module") if ($DEBUG);
456*912701f9SAndroid Build Coastguard Worker	my $m = $module;
457*912701f9SAndroid Build Coastguard Worker	$m =~ s|^.+/||;
458*912701f9SAndroid Build Coastguard Worker        $diffs .= out("<HR><CENTER><B><FONT SIZE=+1>", uc($m),
459*912701f9SAndroid Build Coastguard Worker		      "</FONT></B></CENTER><HR>");
460*912701f9SAndroid Build Coastguard Worker        debugOut("+generateDiffsList($ID, $module)") if ($DEBUG);
461*912701f9SAndroid Build Coastguard Worker        $diffs .= generateDiffsList($ID, $module);
462*912701f9SAndroid Build Coastguard Worker        debugOut("-generateDiffsList($ID, $module)") if ($DEBUG);
463*912701f9SAndroid Build Coastguard Worker    }
464*912701f9SAndroid Build Coastguard Worker    instaPut($ID, $diffs) if ($isStd);
465*912701f9SAndroid Build Coastguard Worker}
466*912701f9SAndroid Build Coastguard Worker
467*912701f9SAndroid Build Coastguard Workersub emit_localdiff {
468*912701f9SAndroid Build Coastguard Worker    print $QUERY->header(-type=>'application/octet-stream',
469*912701f9SAndroid Build Coastguard Worker			 -attachment=>'localdiff.bat');
470*912701f9SAndroid Build Coastguard Worker    my $file = $QUERY->param('file');
471*912701f9SAndroid Build Coastguard Worker    my $r1 = $QUERY->param('r1');
472*912701f9SAndroid Build Coastguard Worker    my $r2 = $QUERY->param('r2');
473*912701f9SAndroid Build Coastguard Worker    my $mod = $QUERY->param('m');
474*912701f9SAndroid Build Coastguard Worker    my $leaf = $file;
475*912701f9SAndroid Build Coastguard Worker    $leaf =~ s|.*[/\\]([^/\\]+)+$|$1|;
476*912701f9SAndroid Build Coastguard Worker    $file = "$mod/$file";
477*912701f9SAndroid Build Coastguard Worker    my $eol = "\015\012"; # DOS eol
478*912701f9SAndroid Build Coastguard Worker    print "cd %TEMP%$eol";
479*912701f9SAndroid Build Coastguard Worker    print "mkdir grepj$eol";
480*912701f9SAndroid Build Coastguard Worker    print "cd grepj$eol";
481*912701f9SAndroid Build Coastguard Worker    print "set CVSROOT=:pserver:$USER\@oss.software.ibm.com:/usr/cvs/$mod$eol";
482*912701f9SAndroid Build Coastguard Worker    print "cvs checkout -p -r $r1 $file > $leaf-$r1$eol";
483*912701f9SAndroid Build Coastguard Worker    print "cvs checkout -p -r $r2 $file > $leaf-$r2$eol";
484*912701f9SAndroid Build Coastguard Worker    print "start wincmp $leaf-$r1 $leaf-$r2$eol";
485*912701f9SAndroid Build Coastguard Worker    print "del \%0$eol";
486*912701f9SAndroid Build Coastguard Worker}
487*912701f9SAndroid Build Coastguard Worker
488*912701f9SAndroid Build Coastguard Workersub emit_result {
489*912701f9SAndroid Build Coastguard Worker    print $QUERY->start_html(-title=>$TITLE);
490*912701f9SAndroid Build Coastguard Worker}
491*912701f9SAndroid Build Coastguard Worker
492*912701f9SAndroid Build Coastguard Workersub emit_help {
493*912701f9SAndroid Build Coastguard Worker    my $x = join(" ", sort keys(%MOD_ABBREV));
494*912701f9SAndroid Build Coastguard Worker    print <<END;
495*912701f9SAndroid Build Coastguard WorkerSearch the ICU and ICU4J CVS repositories for changes committed against
496*912701f9SAndroid Build Coastguard Workera specific Jitterbug.
497*912701f9SAndroid Build Coastguard Worker
498*912701f9SAndroid Build Coastguard Worker<P>For a change to be recognized,
499*912701f9SAndroid Build Coastguard Workerits commit comment must start with "<CODE>Jitterbug <B>n</B></CODE>",
500*912701f9SAndroid Build Coastguard Workerwhere <CODE><B>n</B></CODE> is the bug ID.
501*912701f9SAndroid Build Coastguard Worker
502*912701f9SAndroid Build Coastguard Worker<P>The search generates a list of all files changes for this bug,
503*912701f9SAndroid Build Coastguard Workertogether with the specific revisions in each
504*912701f9SAndroid Build Coastguard Workerfile that are relevant (there may be more than one).
505*912701f9SAndroid Build Coastguard Worker
506*912701f9SAndroid Build Coastguard Worker<P>In the diff list,
507*912701f9SAndroid Build Coastguard Workerselect a <B>file name link</B> to see the CVS log
508*912701f9SAndroid Build Coastguard Workerfor that file.
509*912701f9SAndroid Build Coastguard Worker
510*912701f9SAndroid Build Coastguard Worker<P>Select a <B>revision link</B> to see changes
511*912701f9SAndroid Build Coastguard Workerchecked in against that revision.  "Diff" revision links
512*912701f9SAndroid Build Coastguard Workershow diffs against the previous revision.  "View" links
513*912701f9SAndroid Build Coastguard Workershow initial check in revisions.
514*912701f9SAndroid Build Coastguard Worker
515*912701f9SAndroid Build Coastguard Worker<P>If a file contains more than one revision relevant to this
516*912701f9SAndroid Build Coastguard WorkerJitterbug ID, then an <B>overall revision link</B> will be available.
517*912701f9SAndroid Build Coastguard WorkerUse this to see the effect of all changes at once.  <I>If the revisions
518*912701f9SAndroid Build Coastguard Workerare not contiguous, then this diff will contain changes
519*912701f9SAndroid Build Coastguard Workernot related to this Jitterbug.</I>  In that case you may
520*912701f9SAndroid Build Coastguard Workerprefer to view the individual diffs instead.
521*912701f9SAndroid Build Coastguard Worker
522*912701f9SAndroid Build Coastguard Worker<P><B>Incl. Attic</B> causes files under any directory named
523*912701f9SAndroid Build Coastguard Worker"Attic" to be included.
524*912701f9SAndroid Build Coastguard Worker
525*912701f9SAndroid Build Coastguard Worker<P><B>Local Diff</B> enables special links that look like this [*]
526*912701f9SAndroid Build Coastguard Workerwhich cause your browser to download a Windows batch file.  The
527*912701f9SAndroid Build Coastguard Workerbatch file, when executed, will bring up the relevant diffs in
528*912701f9SAndroid Build Coastguard WorkerCompare It!.  For this to work, you need the following:
529*912701f9SAndroid Build Coastguard Worker
530*912701f9SAndroid Build Coastguard Worker<UL><LI><B>cvs</B> must be on your PATH.  For example, you may
531*912701f9SAndroid Build Coastguard Workeradd <CODE>C:\\Program Files\\GNU\\WinCVS 1.2</CODE> to your PATH.
532*912701f9SAndroid Build Coastguard Worker<LI><B>wincmp</B> must be on your PATH.  This is the Compare It!
533*912701f9SAndroid Build Coastguard Workerexecutable.  For example, you may add <CODE>C:\\Program Files\\Compare
534*912701f9SAndroid Build Coastguard WorkerIt!</CODE> to your PATH.
535*912701f9SAndroid Build Coastguard Worker<LI>You must be "logged in" for the cvs checkouts to work.  If your
536*912701f9SAndroid Build Coastguard Workername is present in parentheses next to "ICU Jitterbug Diffs" in the
537*912701f9SAndroid Build Coastguard Workerupper left frame, you are logged in.
538*912701f9SAndroid Build Coastguard Worker</UL>
539*912701f9SAndroid Build Coastguard Worker
540*912701f9SAndroid Build Coastguard Worker<P><B>Modules</B> lists the modules to be searched.  By default
541*912701f9SAndroid Build Coastguard Workerthis is "icu icu4j" but any modules (under /usr/cvs) may be listed.
542*912701f9SAndroid Build Coastguard WorkerFull module names (e.g., "icu/icuapps") may be used.  The following
543*912701f9SAndroid Build Coastguard Workerabbreviations are recognized:  <CODE>$x</CODE>.
544*912701f9SAndroid Build Coastguard WorkerEND
545*912701f9SAndroid Build Coastguard Worker}
546*912701f9SAndroid Build Coastguard Worker
547*912701f9SAndroid Build Coastguard Worker######################################################################
548*912701f9SAndroid Build Coastguard Worker# Admin GUI
549*912701f9SAndroid Build Coastguard Worker######################################################################
550*912701f9SAndroid Build Coastguard Worker
551*912701f9SAndroid Build Coastguard Worker# Emit the HTML for the top frameset in admin mode
552*912701f9SAndroid Build Coastguard Workersub emit_admintop {
553*912701f9SAndroid Build Coastguard Worker    # Propagate url parameters down to the frames within the frameset
554*912701f9SAndroid Build Coastguard Worker
555*912701f9SAndroid Build Coastguard Worker    my $self = $QUERY->url(-full=>1, -query=>1);
556*912701f9SAndroid Build Coastguard Worker    my $f = urlPathInfo($self, '/adminform');
557*912701f9SAndroid Build Coastguard Worker    my $r = urlPathInfo($self, '/adminresult');
558*912701f9SAndroid Build Coastguard Worker    my $TITLETXT = $TITLE;
559*912701f9SAndroid Build Coastguard Worker
560*912701f9SAndroid Build Coastguard Worker    #if ($id ne '') {
561*912701f9SAndroid Build Coastguard Worker#`h	TITLETXT = "$id - $TITLETXT";
562*912701f9SAndroid Build Coastguard Worker  #  }
563*912701f9SAndroid Build Coastguard Worker
564*912701f9SAndroid Build Coastguard Worker    print <<END;
565*912701f9SAndroid Build Coastguard Worker<html><head><title>$TITLE</title></head>
566*912701f9SAndroid Build Coastguard Worker<frameset cols="300,*">
567*912701f9SAndroid Build Coastguard Worker  <frame src="$f" name="adminform" scrolling=yes>
568*912701f9SAndroid Build Coastguard Worker  <frame src="$r" name="adminresult">
569*912701f9SAndroid Build Coastguard Worker</frameset>
570*912701f9SAndroid Build Coastguard WorkerEND
571*912701f9SAndroid Build Coastguard Worker}
572*912701f9SAndroid Build Coastguard Worker
573*912701f9SAndroid Build Coastguard Worker# Print the admin input form.
574*912701f9SAndroid Build Coastguard Workersub emit_adminform {
575*912701f9SAndroid Build Coastguard Worker
576*912701f9SAndroid Build Coastguard Worker    print $QUERY->start_html(-title=>$TITLE,
577*912701f9SAndroid Build Coastguard Worker                             -target=>'adminresult');
578*912701f9SAndroid Build Coastguard Worker
579*912701f9SAndroid Build Coastguard Worker    my $script_name = $QUERY->script_name;
580*912701f9SAndroid Build Coastguard Worker
581*912701f9SAndroid Build Coastguard Worker    print $QUERY->startform(-action=>urlPathInfo($script_name, '/adminresult'),
582*912701f9SAndroid Build Coastguard Worker                            -TARGET=>'adminresult');
583*912701f9SAndroid Build Coastguard Worker
584*912701f9SAndroid Build Coastguard Worker    print "<FONT SIZE=+2><B>Administrative Tools</B></FONT>";
585*912701f9SAndroid Build Coastguard Worker
586*912701f9SAndroid Build Coastguard Worker    my $user = $QUERY->param('user');
587*912701f9SAndroid Build Coastguard Worker    my $u = $user ? "?user=$user" : '';
588*912701f9SAndroid Build Coastguard Worker    print "\&nbsp;<FONT SIZE=-1>"
589*912701f9SAndroid Build Coastguard Worker	, "<A href=\"$script_name$u\" target=\"_top\">Back</A></FONT><BR>";
590*912701f9SAndroid Build Coastguard Worker
591*912701f9SAndroid Build Coastguard Worker    print '<FONT SIZE=-1>Tags may be specified in full, e.g. '
592*912701f9SAndroid Build Coastguard Worker	, '"release-2-4", or as release numbers, such as "2.4".  ',
593*912701f9SAndroid Build Coastguard Worker	'Specify module(s) here for commands below.',
594*912701f9SAndroid Build Coastguard Worker	'</FONT><BR>';
595*912701f9SAndroid Build Coastguard Worker
596*912701f9SAndroid Build Coastguard Worker    print "Modules:&nbsp;";
597*912701f9SAndroid Build Coastguard Worker    print $QUERY->textfield(-name=>'mod',
598*912701f9SAndroid Build Coastguard Worker			    -default=>$DEFAULT_MOD,
599*912701f9SAndroid Build Coastguard Worker			    -size=>30);
600*912701f9SAndroid Build Coastguard Worker    print "<HR>";
601*912701f9SAndroid Build Coastguard Worker
602*912701f9SAndroid Build Coastguard Worker    print "<B>List Bugs Between CVS Tags</B><BR>";
603*912701f9SAndroid Build Coastguard Worker    print "<TABLE><TR><TD nowrap>Start Tag:</TD><TD>";
604*912701f9SAndroid Build Coastguard Worker    print $QUERY->textfield(-name=>'tag_lo',-size=>30);
605*912701f9SAndroid Build Coastguard Worker    print "</TD></TR><TR><TD nowrap>End Tag:</TD><TD>";
606*912701f9SAndroid Build Coastguard Worker    print $QUERY->textfield(-name=>'tag_hi',-size=>30);
607*912701f9SAndroid Build Coastguard Worker    print "</TD></TR><TR><TD></TD><TD>";
608*912701f9SAndroid Build Coastguard Worker    print $QUERY->submit(-name=>'Find Bugs');
609*912701f9SAndroid Build Coastguard Worker    print "</TD></TR></TABLE>";
610*912701f9SAndroid Build Coastguard Worker    print '<FONT SIZE=-1>Bugs are listed that occur after the start tag, up to and including the end tag.  Specify module(s) above.</FONT>';
611*912701f9SAndroid Build Coastguard Worker
612*912701f9SAndroid Build Coastguard Worker    print "<HR>\n";
613*912701f9SAndroid Build Coastguard Worker
614*912701f9SAndroid Build Coastguard Worker    print "<B>DCUT Helper</B><BR>";
615*912701f9SAndroid Build Coastguard Worker    print "<TABLE><TR><TD>Tag:</TD><TD>";
616*912701f9SAndroid Build Coastguard Worker    print $QUERY->textfield(-name=>'dcut_tag',-size=>33);
617*912701f9SAndroid Build Coastguard Worker    print "</TD></TR><TR VALIGN=TOP><TD>Bug IDs:</TD><TD>";
618*912701f9SAndroid Build Coastguard Worker    print $QUERY->textarea(-name=>'dcut_ids',-rows=>8,-columns=>26);
619*912701f9SAndroid Build Coastguard Worker    print "</TD></TR><TR><TD></TD><TD>";
620*912701f9SAndroid Build Coastguard Worker    print $QUERY->submit(-name=>'Check');
621*912701f9SAndroid Build Coastguard Worker    print "</TD></TR></TABLE>";
622*912701f9SAndroid Build Coastguard Worker    print '<FONT SIZE=-1>Enter a CVS tag and list of bugs to incorporate '
623*912701f9SAndroid Build Coastguard Worker	, 'those bugs into the tag.  '
624*912701f9SAndroid Build Coastguard Worker	, 'Specify module(s) above.</FONT>';
625*912701f9SAndroid Build Coastguard Worker
626*912701f9SAndroid Build Coastguard Worker    print "<HR>\n";
627*912701f9SAndroid Build Coastguard Worker
628*912701f9SAndroid Build Coastguard Worker    print $QUERY->submit(-name=>'Reset Insta Cache'), "<BR>";
629*912701f9SAndroid Build Coastguard Worker    print '<FONT SIZE=-1>The insta cache contains the HTML output for previous'
630*912701f9SAndroid Build Coastguard Worker        , ' bug diff search results.  In some cases (typically during script'
631*912701f9SAndroid Build Coastguard Worker        , ' development), it can get out of sync.</FONT>';
632*912701f9SAndroid Build Coastguard Worker
633*912701f9SAndroid Build Coastguard Worker    print "<HR>\n";
634*912701f9SAndroid Build Coastguard Worker
635*912701f9SAndroid Build Coastguard Worker    print $QUERY->submit(-name=>'Delete Cache File:'), "&nbsp;";
636*912701f9SAndroid Build Coastguard Worker    print $QUERY->textfield(-name=>'del_cache',-size=>17), "<BR>";
637*912701f9SAndroid Build Coastguard Worker    print '<FONT SIZE=-1 >Delete a file from the cache.  Path is relative'
638*912701f9SAndroid Build Coastguard Worker	, ' to cache root and must begin with the module path'
639*912701f9SAndroid Build Coastguard Worker	, ' (e.g. "icu/icu").</FONT>';
640*912701f9SAndroid Build Coastguard Worker
641*912701f9SAndroid Build Coastguard Worker    # Propagate params that don't have corresponding form elements
642*912701f9SAndroid Build Coastguard Worker    print $QUERY->hidden('user');
643*912701f9SAndroid Build Coastguard Worker    print $QUERY->hidden('debug');
644*912701f9SAndroid Build Coastguard Worker
645*912701f9SAndroid Build Coastguard Worker    print $QUERY->end_form;
646*912701f9SAndroid Build Coastguard Worker}
647*912701f9SAndroid Build Coastguard Worker
648*912701f9SAndroid Build Coastguard Worker# Implement the admin functions.
649*912701f9SAndroid Build Coastguard Workersub emit_adminresult {
650*912701f9SAndroid Build Coastguard Worker    print $QUERY->start_html(-title=>$TITLE);
651*912701f9SAndroid Build Coastguard Worker
652*912701f9SAndroid Build Coastguard Worker    if ($QUERY->param('Find Bugs')) {
653*912701f9SAndroid Build Coastguard Worker	&do_tagscan;
654*912701f9SAndroid Build Coastguard Worker	return;
655*912701f9SAndroid Build Coastguard Worker    }
656*912701f9SAndroid Build Coastguard Worker
657*912701f9SAndroid Build Coastguard Worker    if ($QUERY->param('Check')) {
658*912701f9SAndroid Build Coastguard Worker	&do_dcuthelp;
659*912701f9SAndroid Build Coastguard Worker	return;
660*912701f9SAndroid Build Coastguard Worker    }
661*912701f9SAndroid Build Coastguard Worker
662*912701f9SAndroid Build Coastguard Worker    if ($QUERY->param('Reset Insta Cache')) {
663*912701f9SAndroid Build Coastguard Worker	resetInstaCache(1);
664*912701f9SAndroid Build Coastguard Worker	print "Cache at $INSTA has been erased.";
665*912701f9SAndroid Build Coastguard Worker	return;
666*912701f9SAndroid Build Coastguard Worker    }
667*912701f9SAndroid Build Coastguard Worker
668*912701f9SAndroid Build Coastguard Worker    if ($QUERY->param('Delete Cache File:')) {
669*912701f9SAndroid Build Coastguard Worker	my $f = $QUERY->param('del_cache');
670*912701f9SAndroid Build Coastguard Worker	# Careful here -- don't let the user delete anything but a
671*912701f9SAndroid Build Coastguard Worker	# legitimate cache file.  Watch out for "..", "~", "$", etc.
672*912701f9SAndroid Build Coastguard Worker	if ($f !~ m|^[a-z0-9_]+(/[a-z0-9_]+)+\.[a-z0-9]+$|i) {
673*912701f9SAndroid Build Coastguard Worker	    print "\"$f\" does not look like a valid path.";
674*912701f9SAndroid Build Coastguard Worker	    return;
675*912701f9SAndroid Build Coastguard Worker	}
676*912701f9SAndroid Build Coastguard Worker	$f = $CACHE . '/' . $f . ',v';
677*912701f9SAndroid Build Coastguard Worker 	if (! -e $f) {
678*912701f9SAndroid Build Coastguard Worker	    print "\"$f\" does not exist.";
679*912701f9SAndroid Build Coastguard Worker	    return;
680*912701f9SAndroid Build Coastguard Worker	}
681*912701f9SAndroid Build Coastguard Worker 	if (! -f $f) {
682*912701f9SAndroid Build Coastguard Worker	    print "\"$f\" is not a file.";
683*912701f9SAndroid Build Coastguard Worker	    return;
684*912701f9SAndroid Build Coastguard Worker	}
685*912701f9SAndroid Build Coastguard Worker	unlink($f);
686*912701f9SAndroid Build Coastguard Worker	# This check doesn't seem to work.
687*912701f9SAndroid Build Coastguard Worker 	#if (! -e $f) {
688*912701f9SAndroid Build Coastguard Worker	#    print "Error: Could not delete \"$f\".";
689*912701f9SAndroid Build Coastguard Worker	#    return;
690*912701f9SAndroid Build Coastguard Worker	#} else {
691*912701f9SAndroid Build Coastguard Worker	    print "Cache file \"$f\" deleted.";
692*912701f9SAndroid Build Coastguard Worker	#}
693*912701f9SAndroid Build Coastguard Worker	return;
694*912701f9SAndroid Build Coastguard Worker    }
695*912701f9SAndroid Build Coastguard Worker}
696*912701f9SAndroid Build Coastguard Worker
697*912701f9SAndroid Build Coastguard Worker######################################################################
698*912701f9SAndroid Build Coastguard Worker# Jitterbug diffs
699*912701f9SAndroid Build Coastguard Worker######################################################################
700*912701f9SAndroid Build Coastguard Worker
701*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
702*912701f9SAndroid Build Coastguard Worker# Find the diffs for a jitterbug and display them.
703*912701f9SAndroid Build Coastguard Worker# Also display other useful links for this bug.
704*912701f9SAndroid Build Coastguard Worker# Param: ID number
705*912701f9SAndroid Build Coastguard Worker# Param: module name ("icu/icu" or "icu4j/icu4j" or other)
706*912701f9SAndroid Build Coastguard Worker# Return: The generated HTML.  Also print it to STDOUT
707*912701f9SAndroid Build Coastguard Worker# on the fly.
708*912701f9SAndroid Build Coastguard Workersub generateDiffsList {
709*912701f9SAndroid Build Coastguard Worker    my $ID = shift;
710*912701f9SAndroid Build Coastguard Worker    my $module = shift;
711*912701f9SAndroid Build Coastguard Worker    my $result;
712*912701f9SAndroid Build Coastguard Worker
713*912701f9SAndroid Build Coastguard Worker    my $greproot = "$CACHE/$module";
714*912701f9SAndroid Build Coastguard Worker    my $log_url  = "$LOG_URL/$module/";
715*912701f9SAndroid Build Coastguard Worker    my $show_url = "$SHOW_URL/$module/";
716*912701f9SAndroid Build Coastguard Worker    my $diff_url = "$DIFF_URL/$module/";
717*912701f9SAndroid Build Coastguard Worker
718*912701f9SAndroid Build Coastguard Worker    # ID matching pattern
719*912701f9SAndroid Build Coastguard Worker    my $pat = "0*$ID";
720*912701f9SAndroid Build Coastguard Worker
721*912701f9SAndroid Build Coastguard Worker    # During merging, the bug IDs 1-98 for icu4j were migrated to
722*912701f9SAndroid Build Coastguard Worker    # 1301-1398.  Therefore, when the user requests a bug in the range
723*912701f9SAndroid Build Coastguard Worker    # 1301-1398, we search under both n and n-1300 in icu4j
724*912701f9SAndroid Build Coastguard Worker    # repository.
725*912701f9SAndroid Build Coastguard Worker    if ($module =~ /^icu4j/ && $ID >= 1301 && $ID <= 1398) {
726*912701f9SAndroid Build Coastguard Worker        my $ID2 = $ID - 1300;
727*912701f9SAndroid Build Coastguard Worker        $pat = "($pat|0*$ID2)";
728*912701f9SAndroid Build Coastguard Worker    }
729*912701f9SAndroid Build Coastguard Worker
730*912701f9SAndroid Build Coastguard Worker    # -E use extended regexp
731*912701f9SAndroid Build Coastguard Worker    # -i ignore case
732*912701f9SAndroid Build Coastguard Worker    # -I ignore binary files
733*912701f9SAndroid Build Coastguard Worker    # -l stop at first match and list file name
734*912701f9SAndroid Build Coastguard Worker    # -r recurse
735*912701f9SAndroid Build Coastguard Worker    # N/A now that we cache the rlog output
736*912701f9SAndroid Build Coastguard Worker    #my $flags = $ignoreBinaries ? "-EiIlr" : "-Eilr";
737*912701f9SAndroid Build Coastguard Worker
738*912701f9SAndroid Build Coastguard Worker    # (1 of 3 REGEXPS) SEE ALSO other regexps; keep them in sync
739*912701f9SAndroid Build Coastguard Worker    # TODO improve error handling in following line
740*912701f9SAndroid Build Coastguard Worker    my @files = `grep -Eilr "($CVS_MSG_KW)[ \\t]*$pat\\b" $greproot`;
741*912701f9SAndroid Build Coastguard Worker
742*912701f9SAndroid Build Coastguard Worker    if (!$QUERY->param('include_attic')) {
743*912701f9SAndroid Build Coastguard Worker        @files = grep(!m|/attic/|i, @files);
744*912701f9SAndroid Build Coastguard Worker    }
745*912701f9SAndroid Build Coastguard Worker
746*912701f9SAndroid Build Coastguard Worker    if (@files < 1) {
747*912701f9SAndroid Build Coastguard Worker        $result .= out("No changes found for Jitterbug $ID.\n");
748*912701f9SAndroid Build Coastguard Worker        return $result;
749*912701f9SAndroid Build Coastguard Worker    }
750*912701f9SAndroid Build Coastguard Worker
751*912701f9SAndroid Build Coastguard Worker    $result .= out("<FONT SIZE=-1>");
752*912701f9SAndroid Build Coastguard Worker
753*912701f9SAndroid Build Coastguard Worker    my $first = 1;
754*912701f9SAndroid Build Coastguard Worker
755*912701f9SAndroid Build Coastguard Worker    foreach my $f (sort cmpfiles @files) {
756*912701f9SAndroid Build Coastguard Worker        my @r = findRevisions($f, $pat);
757*912701f9SAndroid Build Coastguard Worker
758*912701f9SAndroid Build Coastguard Worker        if ($first) {
759*912701f9SAndroid Build Coastguard Worker            $first = 0;
760*912701f9SAndroid Build Coastguard Worker        } else {
761*912701f9SAndroid Build Coastguard Worker            $result .= out("<HR>\n");
762*912701f9SAndroid Build Coastguard Worker        }
763*912701f9SAndroid Build Coastguard Worker
764*912701f9SAndroid Build Coastguard Worker	my $localDiff = $QUERY->param('localdiff');
765*912701f9SAndroid Build Coastguard Worker
766*912701f9SAndroid Build Coastguard Worker        my $relFile = $f;
767*912701f9SAndroid Build Coastguard Worker        $relFile =~ s/^$greproot\///;
768*912701f9SAndroid Build Coastguard Worker        $relFile =~ s/,v//;
769*912701f9SAndroid Build Coastguard Worker        my $a = '';
770*912701f9SAndroid Build Coastguard Worker        my $b = $relFile;
771*912701f9SAndroid Build Coastguard Worker        if ($b =~ m|(.*/)(.+)|) {
772*912701f9SAndroid Build Coastguard Worker            ($a ,$b) = ($1, $2);
773*912701f9SAndroid Build Coastguard Worker        }
774*912701f9SAndroid Build Coastguard Worker        $result .= out("$a<A href=\"$log_url$relFile?$LOG_URL_SUFFIX\" title=\"View CVS log for $b\"><B>$b</B></A><BR>");
775*912701f9SAndroid Build Coastguard Worker        if (@r > 1) {
776*912701f9SAndroid Build Coastguard Worker            # Show diff of earliest to latest.
777*912701f9SAndroid Build Coastguard Worker            my $discontiguous = 0;
778*912701f9SAndroid Build Coastguard Worker            for (my $i=0; $i<$#r; $i++) { # [sic] from first to last-1
779*912701f9SAndroid Build Coastguard Worker                if ($r[$i]->{old} ne $r[$i+1]->{new}) {
780*912701f9SAndroid Build Coastguard Worker                    $discontiguous = 1;
781*912701f9SAndroid Build Coastguard Worker                    last;
782*912701f9SAndroid Build Coastguard Worker                }
783*912701f9SAndroid Build Coastguard Worker            }
784*912701f9SAndroid Build Coastguard Worker            my $new = $r[0]->{new};
785*912701f9SAndroid Build Coastguard Worker            my $old = $r[$#r]->{old};
786*912701f9SAndroid Build Coastguard Worker            $result .= out("<CENTER>");
787*912701f9SAndroid Build Coastguard Worker	    if ($discontiguous) {
788*912701f9SAndroid Build Coastguard Worker		$result .= out("<B>Contains other changes: </B>");
789*912701f9SAndroid Build Coastguard Worker	    }
790*912701f9SAndroid Build Coastguard Worker            if ($old eq $BASE_REV) {
791*912701f9SAndroid Build Coastguard Worker                $result .= out("<A href=\"$show_url$relFile?rev=$new$SHOW_URL_SUFFIX\">");
792*912701f9SAndroid Build Coastguard Worker                $result .= out("<B>View $new</B></A>");
793*912701f9SAndroid Build Coastguard Worker            } else {
794*912701f9SAndroid Build Coastguard Worker                $result .= out("<A href=\"$diff_url$relFile?r1=$old&r2=$new$DIFF_URL_SUFFIX\">");
795*912701f9SAndroid Build Coastguard Worker                $result .= out("<B>Diff $new vs $old</B></A>");
796*912701f9SAndroid Build Coastguard Worker		if ($localDiff) {
797*912701f9SAndroid Build Coastguard Worker		    my $self = $QUERY->url(-full=>1, -query=>1);
798*912701f9SAndroid Build Coastguard Worker		    my $url = urlPathInfo($self, '/localdiff');
799*912701f9SAndroid Build Coastguard Worker		    my $mod = $module;
800*912701f9SAndroid Build Coastguard Worker		    $mod =~ s|/.+||;
801*912701f9SAndroid Build Coastguard Worker		    out(" [<A href=\"$url;m=$mod;file=$relFile;r1=$old;r2=$new$DIFF_URL_SUFFIX\">*</A>]");
802*912701f9SAndroid Build Coastguard Worker		}
803*912701f9SAndroid Build Coastguard Worker            }
804*912701f9SAndroid Build Coastguard Worker
805*912701f9SAndroid Build Coastguard Worker            # Construct contiguous ranges if the overall diff is
806*912701f9SAndroid Build Coastguard Worker            # discontiguous.
807*912701f9SAndroid Build Coastguard Worker            if ($discontiguous) {
808*912701f9SAndroid Build Coastguard Worker                my @ranges;
809*912701f9SAndroid Build Coastguard Worker                my $start = 0;
810*912701f9SAndroid Build Coastguard Worker                for (my $i=0; $i<$#r; $i++) { # [sic] from first to last-1
811*912701f9SAndroid Build Coastguard Worker                    if ($r[$i]->{old} ne $r[$i+1]->{new}) {
812*912701f9SAndroid Build Coastguard Worker                        push @ranges, [$start, $i];
813*912701f9SAndroid Build Coastguard Worker                        $start = $i+1;
814*912701f9SAndroid Build Coastguard Worker                    }
815*912701f9SAndroid Build Coastguard Worker                }
816*912701f9SAndroid Build Coastguard Worker                push @ranges, [$start, $#r];
817*912701f9SAndroid Build Coastguard Worker                my $first = 1;
818*912701f9SAndroid Build Coastguard Worker                foreach my $range (@ranges) {
819*912701f9SAndroid Build Coastguard Worker                    my $new = $r[$range->[0]]->{new};
820*912701f9SAndroid Build Coastguard Worker                    my $old = $r[$range->[1]]->{old};
821*912701f9SAndroid Build Coastguard Worker                    if ($first) {
822*912701f9SAndroid Build Coastguard Worker                        $result .= out("<BR>\n(");
823*912701f9SAndroid Build Coastguard Worker                        $first = 0;
824*912701f9SAndroid Build Coastguard Worker                    } else {
825*912701f9SAndroid Build Coastguard Worker                        $result .= out("<BR>\n");
826*912701f9SAndroid Build Coastguard Worker                    }
827*912701f9SAndroid Build Coastguard Worker                    if ($old eq $BASE_REV) {
828*912701f9SAndroid Build Coastguard Worker                        $result .= out("<A href=\"$show_url$relFile?rev=$new$SHOW_URL_SUFFIX\">");
829*912701f9SAndroid Build Coastguard Worker                        $result .= out("View $new</A>");
830*912701f9SAndroid Build Coastguard Worker                    } else {
831*912701f9SAndroid Build Coastguard Worker                        $result .= out("<A href=\"$diff_url$relFile?r1=$old&r2=$new$DIFF_URL_SUFFIX\">");
832*912701f9SAndroid Build Coastguard Worker                        $result .= out("Diff $new vs $old</A>");
833*912701f9SAndroid Build Coastguard Worker			if ($localDiff) {
834*912701f9SAndroid Build Coastguard Worker			    my $self = $QUERY->url(-full=>1, -query=>1);
835*912701f9SAndroid Build Coastguard Worker			    my $url = urlPathInfo($self, '/localdiff');
836*912701f9SAndroid Build Coastguard Worker			    my $mod = $module;
837*912701f9SAndroid Build Coastguard Worker			    $mod =~ s|/.+||;
838*912701f9SAndroid Build Coastguard Worker			    out(" [<A href=\"$url;m=$mod;file=$relFile;r1=$old;r2=$new$DIFF_URL_SUFFIX\">*</A>]");
839*912701f9SAndroid Build Coastguard Worker			}
840*912701f9SAndroid Build Coastguard Worker                    }
841*912701f9SAndroid Build Coastguard Worker                }
842*912701f9SAndroid Build Coastguard Worker                $result .= out(")");
843*912701f9SAndroid Build Coastguard Worker            }
844*912701f9SAndroid Build Coastguard Worker
845*912701f9SAndroid Build Coastguard Worker            $result .= out("</CENTER>");
846*912701f9SAndroid Build Coastguard Worker        }
847*912701f9SAndroid Build Coastguard Worker
848*912701f9SAndroid Build Coastguard Worker        for (my $i=0; $i<@r; $i++) {
849*912701f9SAndroid Build Coastguard Worker            my $h = $r[$i];
850*912701f9SAndroid Build Coastguard Worker            my $new = $h->{new};
851*912701f9SAndroid Build Coastguard Worker            my $old = $h->{old};
852*912701f9SAndroid Build Coastguard Worker            if ($old eq $BASE_REV) {
853*912701f9SAndroid Build Coastguard Worker                $result .= out("<A href=\"$show_url$relFile?rev=$new$SHOW_URL_SUFFIX\">");
854*912701f9SAndroid Build Coastguard Worker                $result .= out("<B>View $new</B></A>");
855*912701f9SAndroid Build Coastguard Worker            } else {
856*912701f9SAndroid Build Coastguard Worker                $result .= out("<A href=\"$diff_url$relFile?r1=$old&r2=$new$DIFF_URL_SUFFIX\">");
857*912701f9SAndroid Build Coastguard Worker                $result .= out("<B>Diff $new</B></A>");
858*912701f9SAndroid Build Coastguard Worker		if ($localDiff) {
859*912701f9SAndroid Build Coastguard Worker		    my $self = $QUERY->url(-full=>1, -query=>1);
860*912701f9SAndroid Build Coastguard Worker		    my $url = urlPathInfo($self, '/localdiff');
861*912701f9SAndroid Build Coastguard Worker		    my $mod = $module;
862*912701f9SAndroid Build Coastguard Worker		    $mod =~ s|/.+||;
863*912701f9SAndroid Build Coastguard Worker		    out(" [<A href=\"$url;m=$mod;file=$relFile;r1=$old;r2=$new$DIFF_URL_SUFFIX\">*</A>]");
864*912701f9SAndroid Build Coastguard Worker		}
865*912701f9SAndroid Build Coastguard Worker            }
866*912701f9SAndroid Build Coastguard Worker            $result .= out(" <EM>", $h->{date}, "</EM> by <EM>", $h->{author}, "</EM><BR>");
867*912701f9SAndroid Build Coastguard Worker            $result .= out($h->{comment});
868*912701f9SAndroid Build Coastguard Worker            $result .= out("<BR>\n");
869*912701f9SAndroid Build Coastguard Worker        }
870*912701f9SAndroid Build Coastguard Worker    }
871*912701f9SAndroid Build Coastguard Worker
872*912701f9SAndroid Build Coastguard Worker    $result .= out("</FONT>");
873*912701f9SAndroid Build Coastguard Worker    $result;
874*912701f9SAndroid Build Coastguard Worker}
875*912701f9SAndroid Build Coastguard Worker
876*912701f9SAndroid Build Coastguard Worker# Sort criterion for file diffs
877*912701f9SAndroid Build Coastguard Workersub cmpfiles {
878*912701f9SAndroid Build Coastguard Worker    my $aa = $a;
879*912701f9SAndroid Build Coastguard Worker    my $bb = $b;
880*912701f9SAndroid Build Coastguard Worker    $aa =~ s|/unicode(/[^/]+)$|$1|;
881*912701f9SAndroid Build Coastguard Worker    $bb =~ s|/unicode(/[^/]+)$|$1|;
882*912701f9SAndroid Build Coastguard Worker    $aa =~ s|\.h,|.1h,|;
883*912701f9SAndroid Build Coastguard Worker    $bb =~ s|\.h,|.1h,|;
884*912701f9SAndroid Build Coastguard Worker    return $aa cmp $bb;
885*912701f9SAndroid Build Coastguard Worker}
886*912701f9SAndroid Build Coastguard Worker
887*912701f9SAndroid Build Coastguard Worker# Sort criterion for revision numbers, e.g. "1.9" vs "1.10"
888*912701f9SAndroid Build Coastguard Workersub cmprevs {
889*912701f9SAndroid Build Coastguard Worker    my @a = split('\.', $a);
890*912701f9SAndroid Build Coastguard Worker    my @b = split('\.', $b);
891*912701f9SAndroid Build Coastguard Worker    for (my $i=0; $i<=$#a && $i<=$#b; ++$i) {
892*912701f9SAndroid Build Coastguard Worker        my $c = $b[$i] - $a[$i];
893*912701f9SAndroid Build Coastguard Worker        return $c if ($c);
894*912701f9SAndroid Build Coastguard Worker    }
895*912701f9SAndroid Build Coastguard Worker    return $#b - $#a;
896*912701f9SAndroid Build Coastguard Worker}
897*912701f9SAndroid Build Coastguard Worker
898*912701f9SAndroid Build Coastguard Worker######################################################################
899*912701f9SAndroid Build Coastguard Worker# tagscan
900*912701f9SAndroid Build Coastguard Worker######################################################################
901*912701f9SAndroid Build Coastguard Worker
902*912701f9SAndroid Build Coastguard Worker# Perform a "tagscan" and emit the results.  A tagscan is a scan of
903*912701f9SAndroid Build Coastguard Worker# the CVS rlog cache in which bug IDs between two tags are compiled.
904*912701f9SAndroid Build Coastguard Worker# If a file is marked 'dead' it is ignored.  If it was created after
905*912701f9SAndroid Build Coastguard Worker# the latest date of the HI tag (as determined by checking _every_
906*912701f9SAndroid Build Coastguard Worker# file's date for that tag) then it is ignored.
907*912701f9SAndroid Build Coastguard Workersub do_tagscan {
908*912701f9SAndroid Build Coastguard Worker    $TAGSCAN_TAG_LO = expandTag($QUERY->param('tag_lo'));
909*912701f9SAndroid Build Coastguard Worker    $TAGSCAN_TAG_HI = expandTag($QUERY->param('tag_hi'));
910*912701f9SAndroid Build Coastguard Worker
911*912701f9SAndroid Build Coastguard Worker    $TAGSCAN_TAG_HI_DATE = '';
912*912701f9SAndroid Build Coastguard Worker
913*912701f9SAndroid Build Coastguard Worker    if (!$TAGSCAN_TAG_LO || !$TAGSCAN_TAG_HI) {
914*912701f9SAndroid Build Coastguard Worker	print "Please enter two CVS tags and try again.";
915*912701f9SAndroid Build Coastguard Worker	return;
916*912701f9SAndroid Build Coastguard Worker    }
917*912701f9SAndroid Build Coastguard Worker
918*912701f9SAndroid Build Coastguard Worker    my $user = $QUERY->param('user');
919*912701f9SAndroid Build Coastguard Worker
920*912701f9SAndroid Build Coastguard Worker    my @m;
921*912701f9SAndroid Build Coastguard Worker    return if (!parseMod(\@m)); # what modules are we searching?
922*912701f9SAndroid Build Coastguard Worker
923*912701f9SAndroid Build Coastguard Worker    # Slight limitation -- our tagLink will only refer to the first module
924*912701f9SAndroid Build Coastguard Worker    print "Searching module(s) <B>", join(", ", @m)
925*912701f9SAndroid Build Coastguard Worker	, "</B> for bugs after tag <B>",
926*912701f9SAndroid Build Coastguard Worker	tagLink($TAGSCAN_TAG_LO,$m[0],'grepj_2'),
927*912701f9SAndroid Build Coastguard Worker	"</B> up to and including tag <B>",
928*912701f9SAndroid Build Coastguard Worker	tagLink($TAGSCAN_TAG_HI,$m[0],'grepj_2'),
929*912701f9SAndroid Build Coastguard Worker	"</B>.  <EM>Note: Dead files and Attic files will be ignored.</EM><BR>\n";
930*912701f9SAndroid Build Coastguard Worker
931*912701f9SAndroid Build Coastguard Worker    foreach (@m) {
932*912701f9SAndroid Build Coastguard Worker	updateCacheDir($_);
933*912701f9SAndroid Build Coastguard Worker    }
934*912701f9SAndroid Build Coastguard Worker
935*912701f9SAndroid Build Coastguard Worker    if ($UPDATE_COUNT) {
936*912701f9SAndroid Build Coastguard Worker	print "done ($UPDATE_NONATTIC_COUNT,$UPDATE_ATTIC_COUNT).";
937*912701f9SAndroid Build Coastguard Worker    }
938*912701f9SAndroid Build Coastguard Worker
939*912701f9SAndroid Build Coastguard Worker    %TAGSCAN_IDS = ();
940*912701f9SAndroid Build Coastguard Worker#at	%TAGSCAN_ALLTAGS = ();
941*912701f9SAndroid Build Coastguard Worker    %TAGSCAN_WHY = ();
942*912701f9SAndroid Build Coastguard Worker    $TAGSCAN_COUNT = 0;
943*912701f9SAndroid Build Coastguard Worker    print "<HR>Scanning CVS tree for bug IDs...";
944*912701f9SAndroid Build Coastguard Worker    foreach (@m) {
945*912701f9SAndroid Build Coastguard Worker	tagscanDir($_);
946*912701f9SAndroid Build Coastguard Worker    }
947*912701f9SAndroid Build Coastguard Worker    print "done.<HR>";
948*912701f9SAndroid Build Coastguard Worker
949*912701f9SAndroid Build Coastguard Worker    # Filter out tagless files that were created after the HI tag
950*912701f9SAndroid Build Coastguard Worker    # date.
951*912701f9SAndroid Build Coastguard Worker    my @a;
952*912701f9SAndroid Build Coastguard Worker    foreach my $f (@TAGLESS_FILES) {
953*912701f9SAndroid Build Coastguard Worker	my $d = getRev11Date("$CACHE/$f");
954*912701f9SAndroid Build Coastguard Worker	if ($d && $d le $TAGSCAN_TAG_HI_DATE) {
955*912701f9SAndroid Build Coastguard Worker	    push @a, $f;
956*912701f9SAndroid Build Coastguard Worker	}
957*912701f9SAndroid Build Coastguard Worker    }
958*912701f9SAndroid Build Coastguard Worker    @TAGLESS_FILES = @a;
959*912701f9SAndroid Build Coastguard Worker
960*912701f9SAndroid Build Coastguard Worker    if (@NO_JITTERBUG_FILES) {
961*912701f9SAndroid Build Coastguard Worker	print "The following revisions have no associated Jitterbug, or the bug number could not be parsed from the checkin comment.\n";
962*912701f9SAndroid Build Coastguard Worker	print "Checkins older than a year are not listed.\n";
963*912701f9SAndroid Build Coastguard Worker	print "<BLOCKQUOTE>";
964*912701f9SAndroid Build Coastguard Worker	print join("<BR>\n",
965*912701f9SAndroid Build Coastguard Worker		   map {logLink($_->[0],'grepj_2') .
966*912701f9SAndroid Build Coastguard Worker			", " . $_->[1] . "<BR><CODE>" .
967*912701f9SAndroid Build Coastguard Worker			$_->[2] . "</CODE>"}
968*912701f9SAndroid Build Coastguard Worker		   @NO_JITTERBUG_FILES);
969*912701f9SAndroid Build Coastguard Worker	print "</BLOCKQUOTE><HR>\n";
970*912701f9SAndroid Build Coastguard Worker    }
971*912701f9SAndroid Build Coastguard Worker
972*912701f9SAndroid Build Coastguard Worker    if (@TAGLESS_FILES) {
973*912701f9SAndroid Build Coastguard Worker	print "<EM>The following ", scalar @TAGLESS_FILES
974*912701f9SAndroid Build Coastguard Worker	    , " files were ignored because they are missing one or both tags."
975*912701f9SAndroid Build Coastguard Worker	    , " </EM>Files created after <B>$TAGSCAN_TAG_HI</B> should not be listed"
976*912701f9SAndroid Build Coastguard Worker	    , " here.\n<BLOCKQUOTE>";
977*912701f9SAndroid Build Coastguard Worker	print join("<BR>\n",
978*912701f9SAndroid Build Coastguard Worker		   map {logLink($_,'grepj_2')}
979*912701f9SAndroid Build Coastguard Worker		   @TAGLESS_FILES)
980*912701f9SAndroid Build Coastguard Worker	    , "</BLOCKQUOTE><HR>\n";
981*912701f9SAndroid Build Coastguard Worker    }
982*912701f9SAndroid Build Coastguard Worker
983*912701f9SAndroid Build Coastguard Worker    if (@BRANCHED_FILES) {
984*912701f9SAndroid Build Coastguard Worker	print "<EM>The following ", scalar @BRANCHED_FILES
985*912701f9SAndroid Build Coastguard Worker	    , " files were ignored because the tags occur on different"
986*912701f9SAndroid Build Coastguard Worker	    , " branches.\n</EM><BLOCKQUOTE>";
987*912701f9SAndroid Build Coastguard Worker	print join("<BR>\n",
988*912701f9SAndroid Build Coastguard Worker		   map {logLink($_->[0],'grepj_2') .
989*912701f9SAndroid Build Coastguard Worker			": " . $_->[1] . " => " . $_->[2]}
990*912701f9SAndroid Build Coastguard Worker		   @BRANCHED_FILES)
991*912701f9SAndroid Build Coastguard Worker	    , "</BLOCKQUOTE><HR>\n";
992*912701f9SAndroid Build Coastguard Worker    }
993*912701f9SAndroid Build Coastguard Worker
994*912701f9SAndroid Build Coastguard Worker#at	print "Other tags seen: ",
995*912701f9SAndroid Build Coastguard Worker#at	      join(" ",
996*912701f9SAndroid Build Coastguard Worker#at		   map {my $a=tagToRelease($_); $a?"$_($a)":"$_*"}
997*912701f9SAndroid Build Coastguard Worker#at                sort keys %TAGSCAN_ALLTAGS), "\n<HR>";
998*912701f9SAndroid Build Coastguard Worker
999*912701f9SAndroid Build Coastguard Worker    print "Details: "
1000*912701f9SAndroid Build Coastguard Worker	, join("; ",
1001*912701f9SAndroid Build Coastguard Worker	       map {"(" . jitterbugLink($user, $_, 'grepj_2') .
1002*912701f9SAndroid Build Coastguard Worker                    ": " . join(", ",
1003*912701f9SAndroid Build Coastguard Worker                 map {s|^.+?/||; s|,v$||; $_} sort keys %{$TAGSCAN_WHY{$_}}) . ")"}
1004*912701f9SAndroid Build Coastguard Worker	       sort {$a<=>$b} keys %TAGSCAN_WHY)
1005*912701f9SAndroid Build Coastguard Worker	, "<HR>\n";
1006*912701f9SAndroid Build Coastguard Worker
1007*912701f9SAndroid Build Coastguard Worker    print "Jitterbug IDs found (",scalar keys %TAGSCAN_IDS,"): "
1008*912701f9SAndroid Build Coastguard Worker	, join(", ",
1009*912701f9SAndroid Build Coastguard Worker	       map {jitterbugLink($user, $_, 'grepj_2')}
1010*912701f9SAndroid Build Coastguard Worker	       sort {$a<=>$b} keys %TAGSCAN_IDS);
1011*912701f9SAndroid Build Coastguard Worker
1012*912701f9SAndroid Build Coastguard Worker    my $bugs = join(',', sort {$a<=>$b} keys %TAGSCAN_IDS);
1013*912701f9SAndroid Build Coastguard Worker    print <<END;
1014*912701f9SAndroid Build Coastguard Worker  <form method=post action=http://bugs.icu-project.org/cgibin/private/tasklist/buglist.html>
1015*912701f9SAndroid Build Coastguard Worker    <input type=hidden name=tag1 value=$TAGSCAN_TAG_LO>
1016*912701f9SAndroid Build Coastguard Worker    <input type=hidden name=tag2 value=$TAGSCAN_TAG_HI>
1017*912701f9SAndroid Build Coastguard Worker    <input type=hidden name=bugs value="$bugs">
1018*912701f9SAndroid Build Coastguard Worker   <input type=submit value="Bug List Report">
1019*912701f9SAndroid Build Coastguard Worker  </form>
1020*912701f9SAndroid Build Coastguard WorkerEND
1021*912701f9SAndroid Build Coastguard Worker    my $bugs2 = join(' ', sort {$a<=>$b} keys %TAGSCAN_IDS);
1022*912701f9SAndroid Build Coastguard Worker    print <<END;
1023*912701f9SAndroid Build Coastguard Worker  <form method=GET action=http://bugs.icu-project.org/cgibin/private/byname/review>
1024*912701f9SAndroid Build Coastguard Worker    <input type=hidden name=user value=$user>
1025*912701f9SAndroid Build Coastguard Worker    <input type=hidden name=bugs value="$bugs2">
1026*912701f9SAndroid Build Coastguard Worker    <input type=hidden name=showclosed value=>
1027*912701f9SAndroid Build Coastguard Worker   <input type=submit value="Reviewer Report">
1028*912701f9SAndroid Build Coastguard Worker  </form>
1029*912701f9SAndroid Build Coastguard WorkerEND
1030*912701f9SAndroid Build Coastguard Worker    print <<END;
1031*912701f9SAndroid Build Coastguard Worker  <form method=GET action=http://bugs.icu-project.org/cgibin/private/byname/assign>
1032*912701f9SAndroid Build Coastguard Worker    <input type=hidden name=user value=$user>
1033*912701f9SAndroid Build Coastguard Worker    <input type=hidden name=bugs value="$bugs2">
1034*912701f9SAndroid Build Coastguard Worker    <input type=hidden name=showclosed value=>
1035*912701f9SAndroid Build Coastguard Worker   <input type=submit value="Assignee Report">
1036*912701f9SAndroid Build Coastguard Worker  </form>
1037*912701f9SAndroid Build Coastguard WorkerEND
1038*912701f9SAndroid Build Coastguard Worker}
1039*912701f9SAndroid Build Coastguard Worker
1040*912701f9SAndroid Build Coastguard Worker# Given a relative path to $CVSROOT, tagscan the
1041*912701f9SAndroid Build Coastguard Worker# corresponding item under $CACHE.  Path may point to a
1042*912701f9SAndroid Build Coastguard Worker# file or a directory.
1043*912701f9SAndroid Build Coastguard Worker# @param relative directory, not ending in "/", e.g. "icu/icu"
1044*912701f9SAndroid Build Coastguard Worker# @param item name in that directory
1045*912701f9SAndroid Build Coastguard Workersub tagscanEntry {
1046*912701f9SAndroid Build Coastguard Worker    my $relDir = shift;
1047*912701f9SAndroid Build Coastguard Worker    my $item = shift; # A file or dir in $CVSROOT/$relDir
1048*912701f9SAndroid Build Coastguard Worker
1049*912701f9SAndroid Build Coastguard Worker    if (-d "$CACHE/$relDir/$item") {
1050*912701f9SAndroid Build Coastguard Worker        tagscanDir("$relDir/$item");
1051*912701f9SAndroid Build Coastguard Worker    } elsif ($item =~ /,v$/) {
1052*912701f9SAndroid Build Coastguard Worker        tagscanFile("$relDir/$item");
1053*912701f9SAndroid Build Coastguard Worker    }
1054*912701f9SAndroid Build Coastguard Worker}
1055*912701f9SAndroid Build Coastguard Worker
1056*912701f9SAndroid Build Coastguard Worker# Given a relative directory path to $CACHE, tagscan the
1057*912701f9SAndroid Build Coastguard Worker# underlying files.
1058*912701f9SAndroid Build Coastguard Worker# @param relative directory, not ending in "/", e.g. "icu/icu"
1059*912701f9SAndroid Build Coastguard Workersub tagscanDir {
1060*912701f9SAndroid Build Coastguard Worker    my $relDir = shift;
1061*912701f9SAndroid Build Coastguard Worker
1062*912701f9SAndroid Build Coastguard Worker    # Ignore stuff in the Attic
1063*912701f9SAndroid Build Coastguard Worker    return if ($relDir eq 'Attic');
1064*912701f9SAndroid Build Coastguard Worker
1065*912701f9SAndroid Build Coastguard Worker    debugOut("+tagscanDir($relDir)") if ($DEBUG);
1066*912701f9SAndroid Build Coastguard Worker
1067*912701f9SAndroid Build Coastguard Worker    my $cacheDir = "$CACHE/$relDir";
1068*912701f9SAndroid Build Coastguard Worker
1069*912701f9SAndroid Build Coastguard Worker    # First tagscan files in this directory
1070*912701f9SAndroid Build Coastguard Worker    opendir(DIR, $cacheDir);
1071*912701f9SAndroid Build Coastguard Worker    my @cacheList = grep !/^\.\.?$/, readdir(DIR);
1072*912701f9SAndroid Build Coastguard Worker    closedir(DIR);
1073*912701f9SAndroid Build Coastguard Worker
1074*912701f9SAndroid Build Coastguard Worker    # Tagscan each individual entry
1075*912701f9SAndroid Build Coastguard Worker    foreach (@cacheList) {
1076*912701f9SAndroid Build Coastguard Worker        tagscanEntry($relDir, $_);
1077*912701f9SAndroid Build Coastguard Worker    }
1078*912701f9SAndroid Build Coastguard Worker
1079*912701f9SAndroid Build Coastguard Worker    debugOut("-tagscanDir($relDir)") if ($DEBUG);
1080*912701f9SAndroid Build Coastguard Worker}
1081*912701f9SAndroid Build Coastguard Worker
1082*912701f9SAndroid Build Coastguard Worker# Given a relative file path to $CVSROOT, tagscan the
1083*912701f9SAndroid Build Coastguard Worker# corresponding file under $CACHE, if necessary.
1084*912701f9SAndroid Build Coastguard Worker# @param relative file path
1085*912701f9SAndroid Build Coastguard Workersub tagscanFile {
1086*912701f9SAndroid Build Coastguard Worker    my $relFile = shift;
1087*912701f9SAndroid Build Coastguard Worker
1088*912701f9SAndroid Build Coastguard Worker    # Display progress; it takes awhile
1089*912701f9SAndroid Build Coastguard Worker    if (++$TAGSCAN_COUNT % 100 == 0) {
1090*912701f9SAndroid Build Coastguard Worker	print " $TAGSCAN_COUNT...";
1091*912701f9SAndroid Build Coastguard Worker    }
1092*912701f9SAndroid Build Coastguard Worker
1093*912701f9SAndroid Build Coastguard Worker    # This file contains the output of rlog.
1094*912701f9SAndroid Build Coastguard Worker    my $file = "$CACHE/$relFile";
1095*912701f9SAndroid Build Coastguard Worker
1096*912701f9SAndroid Build Coastguard Worker    # Parse the rlog file.  Start by extracting the tag names.  Look
1097*912701f9SAndroid Build Coastguard Worker    # for the TAGSCAN_TAG_LO and TAGSCAN_TAG_HI's associated revision
1098*912701f9SAndroid Build Coastguard Worker    # numbers.
1099*912701f9SAndroid Build Coastguard Worker    open(IN, $file);
1100*912701f9SAndroid Build Coastguard Worker    while (<IN>) {
1101*912701f9SAndroid Build Coastguard Worker	last if (/^symbolic names:\s*$/);
1102*912701f9SAndroid Build Coastguard Worker    }
1103*912701f9SAndroid Build Coastguard Worker    my $rev_lo;
1104*912701f9SAndroid Build Coastguard Worker    my $rev_hi;
1105*912701f9SAndroid Build Coastguard Worker    my $rel_min; # lowest release number seen
1106*912701f9SAndroid Build Coastguard Worker    my @odd_tags;
1107*912701f9SAndroid Build Coastguard Worker    if ($TAGSCAN_TAG_HI eq 'HEAD') {
1108*912701f9SAndroid Build Coastguard Worker	$rev_hi = 'HEAD';
1109*912701f9SAndroid Build Coastguard Worker    }
1110*912701f9SAndroid Build Coastguard Worker    while (<IN>) {
1111*912701f9SAndroid Build Coastguard Worker	last if (/^\S/);
1112*912701f9SAndroid Build Coastguard Worker	if (!$rev_lo && /^\s+$TAGSCAN_TAG_LO:\s*(\S+)/) {
1113*912701f9SAndroid Build Coastguard Worker	    $rev_lo = $1;
1114*912701f9SAndroid Build Coastguard Worker	}
1115*912701f9SAndroid Build Coastguard Worker	elsif (!$rev_hi && /^\s+$TAGSCAN_TAG_HI:\s*(\S+)/) {
1116*912701f9SAndroid Build Coastguard Worker	    $rev_hi = $1;
1117*912701f9SAndroid Build Coastguard Worker	}
1118*912701f9SAndroid Build Coastguard Worker	elsif (/^\s+(\S+?):/) {
1119*912701f9SAndroid Build Coastguard Worker	    my $tag = $1;
1120*912701f9SAndroid Build Coastguard Worker#at	    $TAGSCAN_ALLTAGS{$tag} = 1;
1121*912701f9SAndroid Build Coastguard Worker	    my $r = tagToRelease($tag);
1122*912701f9SAndroid Build Coastguard Worker	    if ($r) {
1123*912701f9SAndroid Build Coastguard Worker		if (!$rel_min) {
1124*912701f9SAndroid Build Coastguard Worker		    $rel_min = $r;
1125*912701f9SAndroid Build Coastguard Worker		} elsif ($r < $rel_min) {
1126*912701f9SAndroid Build Coastguard Worker		    $rel_min = $r;
1127*912701f9SAndroid Build Coastguard Worker		}
1128*912701f9SAndroid Build Coastguard Worker	    } else {
1129*912701f9SAndroid Build Coastguard Worker		push @odd_tags, $tag;
1130*912701f9SAndroid Build Coastguard Worker	    }
1131*912701f9SAndroid Build Coastguard Worker	}
1132*912701f9SAndroid Build Coastguard Worker    }
1133*912701f9SAndroid Build Coastguard Worker
1134*912701f9SAndroid Build Coastguard Worker    # Check for dead files.  Look ahead and find the state of the head
1135*912701f9SAndroid Build Coastguard Worker    # revision.
1136*912701f9SAndroid Build Coastguard Worker    my $pos = tell(IN);
1137*912701f9SAndroid Build Coastguard Worker    my $state = '';
1138*912701f9SAndroid Build Coastguard Worker    while (<IN>) {
1139*912701f9SAndroid Build Coastguard Worker	if (/^date:.+state: ([A-Za-z]+)/) {
1140*912701f9SAndroid Build Coastguard Worker	    $state = $1;
1141*912701f9SAndroid Build Coastguard Worker	    last;
1142*912701f9SAndroid Build Coastguard Worker	}
1143*912701f9SAndroid Build Coastguard Worker    }
1144*912701f9SAndroid Build Coastguard Worker    seek(IN,$pos,0);
1145*912701f9SAndroid Build Coastguard Worker
1146*912701f9SAndroid Build Coastguard Worker    # If this file is 'dead', we're done.
1147*912701f9SAndroid Build Coastguard Worker    return if ($state eq 'dead');
1148*912701f9SAndroid Build Coastguard Worker
1149*912701f9SAndroid Build Coastguard Worker    # Usually we find both tags.  However, in several special cases one
1150*912701f9SAndroid Build Coastguard Worker    # or both tags will be missing.
1151*912701f9SAndroid Build Coastguard Worker    if (!$rev_lo || !$rev_hi) {
1152*912701f9SAndroid Build Coastguard Worker	my $ok = 0;
1153*912701f9SAndroid Build Coastguard Worker
1154*912701f9SAndroid Build Coastguard Worker	# If we see the high tag, but not the low, then this may be a
1155*912701f9SAndroid Build Coastguard Worker	# new file (created after the low tag).  To check for this, examine
1156*912701f9SAndroid Build Coastguard Worker	# the other tags.  If this is a new file; we can just scan
1157*912701f9SAndroid Build Coastguard Worker	# from rev_hi all the end of the log (with rev_lo set to '1.1').
1158*912701f9SAndroid Build Coastguard Worker	if ($rev_hi) {
1159*912701f9SAndroid Build Coastguard Worker	    if (!$rel_min) {
1160*912701f9SAndroid Build Coastguard Worker		# The only tag seen was the HI tag.
1161*912701f9SAndroid Build Coastguard Worker		$ok = 1;
1162*912701f9SAndroid Build Coastguard Worker	    } else {
1163*912701f9SAndroid Build Coastguard Worker		my $lo = tagToRelease($TAGSCAN_TAG_LO);
1164*912701f9SAndroid Build Coastguard Worker		if ($lo && $rel_min > $lo && (scalar @odd_tags)==0) {
1165*912701f9SAndroid Build Coastguard Worker		    # Other tags were seen, but all were above the LO tag.
1166*912701f9SAndroid Build Coastguard Worker		    $ok = 1;
1167*912701f9SAndroid Build Coastguard Worker		}
1168*912701f9SAndroid Build Coastguard Worker	    }
1169*912701f9SAndroid Build Coastguard Worker	    $rev_lo = '1.1';
1170*912701f9SAndroid Build Coastguard Worker	}
1171*912701f9SAndroid Build Coastguard Worker
1172*912701f9SAndroid Build Coastguard Worker	if (!$ok) {
1173*912701f9SAndroid Build Coastguard Worker	    push @TAGLESS_FILES, $relFile;
1174*912701f9SAndroid Build Coastguard Worker	    return;
1175*912701f9SAndroid Build Coastguard Worker	}
1176*912701f9SAndroid Build Coastguard Worker    }
1177*912701f9SAndroid Build Coastguard Worker
1178*912701f9SAndroid Build Coastguard Worker    # If the low and high revisions are the same then there are no bugs
1179*912701f9SAndroid Build Coastguard Worker    # to record from this file.
1180*912701f9SAndroid Build Coastguard Worker    if ($rev_lo eq $rev_hi) {
1181*912701f9SAndroid Build Coastguard Worker	# Scan down to get the date of the rev_hi
1182*912701f9SAndroid Build Coastguard Worker	while (<IN>) {
1183*912701f9SAndroid Build Coastguard Worker	    if (/^revision $rev_hi\s*$/) {
1184*912701f9SAndroid Build Coastguard Worker		$_ = <IN>; # Read date line
1185*912701f9SAndroid Build Coastguard Worker		if (/^date: (.+?);/) {
1186*912701f9SAndroid Build Coastguard Worker		    $TAGSCAN_TAG_HI_DATE = $1
1187*912701f9SAndroid Build Coastguard Worker			if ($TAGSCAN_TAG_HI_DATE lt $1);
1188*912701f9SAndroid Build Coastguard Worker		} else {
1189*912701f9SAndroid Build Coastguard Worker		    cantParse('date', $relFile, $_, $rev_hi);
1190*912701f9SAndroid Build Coastguard Worker		}
1191*912701f9SAndroid Build Coastguard Worker	    }
1192*912701f9SAndroid Build Coastguard Worker	}
1193*912701f9SAndroid Build Coastguard Worker	return;
1194*912701f9SAndroid Build Coastguard Worker    }
1195*912701f9SAndroid Build Coastguard Worker
1196*912701f9SAndroid Build Coastguard Worker    my $inRange;
1197*912701f9SAndroid Build Coastguard Worker
1198*912701f9SAndroid Build Coastguard Worker    my @result;
1199*912701f9SAndroid Build Coastguard Worker
1200*912701f9SAndroid Build Coastguard Worker    # The rlog output (the CACHE file) contains a series
1201*912701f9SAndroid Build Coastguard Worker    # of groups of lines, like so:
1202*912701f9SAndroid Build Coastguard Worker    #|----------------------------
1203*912701f9SAndroid Build Coastguard Worker    #|revision 1.40
1204*912701f9SAndroid Build Coastguard Worker    #|date: 2001/08/02 18:24:58;  author: grhoten;  state: Exp;  lines: +82 -73
1205*912701f9SAndroid Build Coastguard Worker    #|jitterbug 1080: general readme.html updates
1206*912701f9SAndroid Build Coastguard Worker    # That is, the first line has the revision #.
1207*912701f9SAndroid Build Coastguard Worker    # The third line has the bug ID.
1208*912701f9SAndroid Build Coastguard Worker
1209*912701f9SAndroid Build Coastguard Worker    # Are revisions on the same branch?
1210*912701f9SAndroid Build Coastguard Worker    my $branch_lo = revToBranch($rev_lo);
1211*912701f9SAndroid Build Coastguard Worker    my $branch_hi = revToBranch($rev_hi);
1212*912701f9SAndroid Build Coastguard Worker    if ($branch_lo eq $branch_hi) {
1213*912701f9SAndroid Build Coastguard Worker
1214*912701f9SAndroid Build Coastguard Worker	while (<IN>) {
1215*912701f9SAndroid Build Coastguard Worker	    if (/^-{20,}$/) {
1216*912701f9SAndroid Build Coastguard Worker		$_ = <IN>; # Read revision line
1217*912701f9SAndroid Build Coastguard Worker		if (/revision (\S+)/) {
1218*912701f9SAndroid Build Coastguard Worker		    my $rev = $1;
1219*912701f9SAndroid Build Coastguard Worker		    last if ($rev eq $rev_lo);
1220*912701f9SAndroid Build Coastguard Worker		    if (!$inRange) {
1221*912701f9SAndroid Build Coastguard Worker			if ($rev eq $rev_hi || $rev_hi eq 'HEAD') {
1222*912701f9SAndroid Build Coastguard Worker			    $inRange = 1;
1223*912701f9SAndroid Build Coastguard Worker			}
1224*912701f9SAndroid Build Coastguard Worker		    }
1225*912701f9SAndroid Build Coastguard Worker		    if ($inRange) {
1226*912701f9SAndroid Build Coastguard Worker			my $date = <IN>; # Read date line
1227*912701f9SAndroid Build Coastguard Worker			$_ = <IN>; # Read comment or branches: line
1228*912701f9SAndroid Build Coastguard Worker			$_ = <IN> if (/^branches:/); # Read line after branches:
1229*912701f9SAndroid Build Coastguard Worker			my $id;
1230*912701f9SAndroid Build Coastguard Worker			if (/^\s*jitterbug\s+0*(\d+)/i) {
1231*912701f9SAndroid Build Coastguard Worker			    $id = $1;
1232*912701f9SAndroid Build Coastguard Worker			} else {
1233*912701f9SAndroid Build Coastguard Worker			    push @NO_JITTERBUG_FILES, [$relFile, $rev, $_]
1234*912701f9SAndroid Build Coastguard Worker				if (noJitterbugFilter($rev, $date));
1235*912701f9SAndroid Build Coastguard Worker			    $id = $NO_JITTERBUG;
1236*912701f9SAndroid Build Coastguard Worker			}
1237*912701f9SAndroid Build Coastguard Worker			push @result, [$rev, $id, $date];
1238*912701f9SAndroid Build Coastguard Worker		    }
1239*912701f9SAndroid Build Coastguard Worker		} else {
1240*912701f9SAndroid Build Coastguard Worker		    cantParse('revision', $relFile, $_);
1241*912701f9SAndroid Build Coastguard Worker		    last; # This is very bad - bail out
1242*912701f9SAndroid Build Coastguard Worker		}
1243*912701f9SAndroid Build Coastguard Worker	    }
1244*912701f9SAndroid Build Coastguard Worker	}
1245*912701f9SAndroid Build Coastguard Worker    }
1246*912701f9SAndroid Build Coastguard Worker
1247*912701f9SAndroid Build Coastguard Worker    elsif ($branch_hi =~ /^\Q$branch_lo\E\./) {
1248*912701f9SAndroid Build Coastguard Worker	# Special case:  E.g., going from 1.25 => 1.25.2.1 means
1249*912701f9SAndroid Build Coastguard Worker	# going from branch 1 to 1.25.2.  We can handle this.
1250*912701f9SAndroid Build Coastguard Worker
1251*912701f9SAndroid Build Coastguard Worker	my @revs = traverseRevisions($rev_lo, $rev_hi);
1252*912701f9SAndroid Build Coastguard Worker
1253*912701f9SAndroid Build Coastguard Worker	#print "[$relFile: ", join(",",@revs), "]";
1254*912701f9SAndroid Build Coastguard Worker
1255*912701f9SAndroid Build Coastguard Worker	shift(@revs); # discard rev_lo
1256*912701f9SAndroid Build Coastguard Worker	my %revs;
1257*912701f9SAndroid Build Coastguard Worker	foreach (@revs) { $revs{$_} = 1; } # convert to hash
1258*912701f9SAndroid Build Coastguard Worker
1259*912701f9SAndroid Build Coastguard Worker	while (<IN>) {
1260*912701f9SAndroid Build Coastguard Worker	    if (/^-{20,}$/) {
1261*912701f9SAndroid Build Coastguard Worker		$_ = <IN>; # Read revision line
1262*912701f9SAndroid Build Coastguard Worker		if (/revision (\S+)/) {
1263*912701f9SAndroid Build Coastguard Worker		    my $rev = $1;
1264*912701f9SAndroid Build Coastguard Worker		    if (exists $revs{$rev}) {
1265*912701f9SAndroid Build Coastguard Worker			delete $revs{$rev};
1266*912701f9SAndroid Build Coastguard Worker			my $date = <IN>; # Read date line
1267*912701f9SAndroid Build Coastguard Worker			if ($rev eq $rev_hi) {
1268*912701f9SAndroid Build Coastguard Worker			    # Record latest date corresponding to HI tag
1269*912701f9SAndroid Build Coastguard Worker			    if ($date =~ /^date: (.+?);/) {
1270*912701f9SAndroid Build Coastguard Worker				$TAGSCAN_TAG_HI_DATE = $1
1271*912701f9SAndroid Build Coastguard Worker				    if ($TAGSCAN_TAG_HI_DATE lt $1);
1272*912701f9SAndroid Build Coastguard Worker			    } else {
1273*912701f9SAndroid Build Coastguard Worker				cantParse('date', $relFile, $date, $rev);
1274*912701f9SAndroid Build Coastguard Worker			    }
1275*912701f9SAndroid Build Coastguard Worker			}
1276*912701f9SAndroid Build Coastguard Worker			$_ = <IN>; # Read comment or branches: line
1277*912701f9SAndroid Build Coastguard Worker			$_ = <IN> if (/^branches:/); # Read line after branches:
1278*912701f9SAndroid Build Coastguard Worker			my $id;
1279*912701f9SAndroid Build Coastguard Worker			if (/^\s*jitterbug\s+0*(\d+)/i) {
1280*912701f9SAndroid Build Coastguard Worker			    $id = $1;
1281*912701f9SAndroid Build Coastguard Worker			    $TAGSCAN_WHY{$id}->{$relFile} = 1;
1282*912701f9SAndroid Build Coastguard Worker			} else {
1283*912701f9SAndroid Build Coastguard Worker			    push @NO_JITTERBUG_FILES, [$relFile, $rev, $_]
1284*912701f9SAndroid Build Coastguard Worker				if (noJitterbugFilter($rev, $date));
1285*912701f9SAndroid Build Coastguard Worker			    $id = $NO_JITTERBUG;
1286*912701f9SAndroid Build Coastguard Worker			}
1287*912701f9SAndroid Build Coastguard Worker			$TAGSCAN_IDS{$id} = 1;
1288*912701f9SAndroid Build Coastguard Worker			last unless (%revs);
1289*912701f9SAndroid Build Coastguard Worker		    }
1290*912701f9SAndroid Build Coastguard Worker		} else {
1291*912701f9SAndroid Build Coastguard Worker		    cantParse('revision', $relFile, $_);
1292*912701f9SAndroid Build Coastguard Worker		    last; # This is very bad - bail out
1293*912701f9SAndroid Build Coastguard Worker		}
1294*912701f9SAndroid Build Coastguard Worker	    }
1295*912701f9SAndroid Build Coastguard Worker	}
1296*912701f9SAndroid Build Coastguard Worker    }
1297*912701f9SAndroid Build Coastguard Worker
1298*912701f9SAndroid Build Coastguard Worker    else {
1299*912701f9SAndroid Build Coastguard Worker	# Tags on different branches
1300*912701f9SAndroid Build Coastguard Worker	push @BRANCHED_FILES, [$relFile, $rev_lo, $rev_hi];
1301*912701f9SAndroid Build Coastguard Worker    }
1302*912701f9SAndroid Build Coastguard Worker
1303*912701f9SAndroid Build Coastguard Worker    close(IN);
1304*912701f9SAndroid Build Coastguard Worker    my $a = \@result;
1305*912701f9SAndroid Build Coastguard Worker
1306*912701f9SAndroid Build Coastguard Worker    foreach my $revision (@$a) {
1307*912701f9SAndroid Build Coastguard Worker	# $revision->[ revision, jitterbug ID, date: line ]
1308*912701f9SAndroid Build Coastguard Worker	$TAGSCAN_IDS{$revision->[1]} = 1;
1309*912701f9SAndroid Build Coastguard Worker	$TAGSCAN_WHY{$revision->[1]}->{$relFile} = 1;
1310*912701f9SAndroid Build Coastguard Worker    }
1311*912701f9SAndroid Build Coastguard Worker
1312*912701f9SAndroid Build Coastguard Worker    if (@$a) {
1313*912701f9SAndroid Build Coastguard Worker	# Record latest date corresponding to HI tag
1314*912701f9SAndroid Build Coastguard Worker	if ($a->[0]->[2] =~ /^date: (.+?);/) {
1315*912701f9SAndroid Build Coastguard Worker	    $TAGSCAN_TAG_HI_DATE = $1
1316*912701f9SAndroid Build Coastguard Worker		if ($TAGSCAN_TAG_HI_DATE lt $1);
1317*912701f9SAndroid Build Coastguard Worker	} else {
1318*912701f9SAndroid Build Coastguard Worker	    cantParse('date', $relFile, $a->[0]->[2], $a->[0]->[0]);
1319*912701f9SAndroid Build Coastguard Worker	}
1320*912701f9SAndroid Build Coastguard Worker    }
1321*912701f9SAndroid Build Coastguard Worker}
1322*912701f9SAndroid Build Coastguard Worker
1323*912701f9SAndroid Build Coastguard Worker######################################################################
1324*912701f9SAndroid Build Coastguard Worker# dcuthelp
1325*912701f9SAndroid Build Coastguard Worker######################################################################
1326*912701f9SAndroid Build Coastguard Worker
1327*912701f9SAndroid Build Coastguard Worker# Perform a "dcuthelp" and emit the results.
1328*912701f9SAndroid Build Coastguard Workersub do_dcuthelp {
1329*912701f9SAndroid Build Coastguard Worker    $DCUTHELP_TAG = expandTag($QUERY->param('dcut_tag'));
1330*912701f9SAndroid Build Coastguard Worker    my $ids = $QUERY->param('dcut_ids');
1331*912701f9SAndroid Build Coastguard Worker    my $user = $QUERY->param('user');
1332*912701f9SAndroid Build Coastguard Worker
1333*912701f9SAndroid Build Coastguard Worker    # Process the ID list; create a hash of IDs in %DCUTHELP_IDS
1334*912701f9SAndroid Build Coastguard Worker    $ids =~ s/,/ /g;
1335*912701f9SAndroid Build Coastguard Worker    my @ids = grep { /\S/ } split(/\s+/, $ids);
1336*912701f9SAndroid Build Coastguard Worker    my @bogus = grep { !/^\d+$/ } @ids;
1337*912701f9SAndroid Build Coastguard Worker    if (@bogus) {
1338*912701f9SAndroid Build Coastguard Worker	print "These are not valid Jitterbug IDs: ", join(", ", @bogus);
1339*912701f9SAndroid Build Coastguard Worker	return;
1340*912701f9SAndroid Build Coastguard Worker    }
1341*912701f9SAndroid Build Coastguard Worker    foreach my $id (@ids) {
1342*912701f9SAndroid Build Coastguard Worker	local $_ = $id;
1343*912701f9SAndroid Build Coastguard Worker	s/^0+//;
1344*912701f9SAndroid Build Coastguard Worker	if (!$_) { print "0 is not a valid Jitterbug ID."; return; }
1345*912701f9SAndroid Build Coastguard Worker	if (exists $DCUTHELP_IDS{$_}) { print "$id is duplicated in the Jitterbug ID list."; return; }
1346*912701f9SAndroid Build Coastguard Worker	$DCUTHELP_IDS{$_} = 1;
1347*912701f9SAndroid Build Coastguard Worker    }
1348*912701f9SAndroid Build Coastguard Worker
1349*912701f9SAndroid Build Coastguard Worker    if ($DCUTHELP_TAG!~/\S/ || 0==scalar keys %DCUTHELP_IDS) {
1350*912701f9SAndroid Build Coastguard Worker	print "Please enter a CVS tag and list of Jitterbug IDs and try again.";
1351*912701f9SAndroid Build Coastguard Worker	return;
1352*912701f9SAndroid Build Coastguard Worker    }
1353*912701f9SAndroid Build Coastguard Worker
1354*912701f9SAndroid Build Coastguard Worker    my @m;
1355*912701f9SAndroid Build Coastguard Worker    return if (!parseMod(\@m)); # what modules are we searching?
1356*912701f9SAndroid Build Coastguard Worker
1357*912701f9SAndroid Build Coastguard Worker    # Announce our intentions
1358*912701f9SAndroid Build Coastguard Worker    print "Performing a DCUT check in module(s) <B>", join(", ", @m)
1359*912701f9SAndroid Build Coastguard Worker        , "</B> against tag <B>", tagLink($DCUTHELP_TAG,$m[0],'grepj_2'),
1360*912701f9SAndroid Build Coastguard Worker	"</B>";
1361*912701f9SAndroid Build Coastguard Worker    print " with Jitterbug IDs <B>";
1362*912701f9SAndroid Build Coastguard Worker    print join(", ",
1363*912701f9SAndroid Build Coastguard Worker	       map {jitterbugLink($user, $_, 'grepj_2')}
1364*912701f9SAndroid Build Coastguard Worker	       sort {$a<=>$b} keys %DCUTHELP_IDS)
1365*912701f9SAndroid Build Coastguard Worker	, "</B>";
1366*912701f9SAndroid Build Coastguard Worker    print ".\n";
1367*912701f9SAndroid Build Coastguard Worker
1368*912701f9SAndroid Build Coastguard Worker    foreach (@m) {
1369*912701f9SAndroid Build Coastguard Worker	updateCacheDir($_);
1370*912701f9SAndroid Build Coastguard Worker    }
1371*912701f9SAndroid Build Coastguard Worker
1372*912701f9SAndroid Build Coastguard Worker    if ($UPDATE_COUNT) {
1373*912701f9SAndroid Build Coastguard Worker	print "done ($UPDATE_NONATTIC_COUNT,$UPDATE_ATTIC_COUNT).";
1374*912701f9SAndroid Build Coastguard Worker    }
1375*912701f9SAndroid Build Coastguard Worker
1376*912701f9SAndroid Build Coastguard Worker    $DCUTHELP_COUNT = 0;
1377*912701f9SAndroid Build Coastguard Worker    print "<HR>Scanning CVS tree...";
1378*912701f9SAndroid Build Coastguard Worker    foreach (@m) {
1379*912701f9SAndroid Build Coastguard Worker	dcuthelpDir($_);
1380*912701f9SAndroid Build Coastguard Worker    }
1381*912701f9SAndroid Build Coastguard Worker    print "done.";
1382*912701f9SAndroid Build Coastguard Worker
1383*912701f9SAndroid Build Coastguard Worker    if (@NO_JITTERBUG_FILES) {
1384*912701f9SAndroid Build Coastguard Worker	print "<HR>The following revisions have no associated Jitterbug, or the bug number could not be parsed from the checkin comment.\n";
1385*912701f9SAndroid Build Coastguard Worker	print "Checkins older than a year are not listed.\n";
1386*912701f9SAndroid Build Coastguard Worker	print "<BLOCKQUOTE>";
1387*912701f9SAndroid Build Coastguard Worker	print join("<BR>\n",
1388*912701f9SAndroid Build Coastguard Worker		   map {logLink($_->[0],'grepj_2') .
1389*912701f9SAndroid Build Coastguard Worker			", " . $_->[1] . "<BR><CODE>" .
1390*912701f9SAndroid Build Coastguard Worker			$_->[2] . "</CODE>"}
1391*912701f9SAndroid Build Coastguard Worker		   @NO_JITTERBUG_FILES);
1392*912701f9SAndroid Build Coastguard Worker	print "</BLOCKQUOTE>\n";
1393*912701f9SAndroid Build Coastguard Worker    }
1394*912701f9SAndroid Build Coastguard Worker
1395*912701f9SAndroid Build Coastguard Worker    my %tagless;
1396*912701f9SAndroid Build Coastguard Worker    if (@TAGLESS_FILES) {
1397*912701f9SAndroid Build Coastguard Worker	print "<HR><EM>The following ", scalar @TAGLESS_FILES
1398*912701f9SAndroid Build Coastguard Worker	    , " files are missing the tag <B>"
1399*912701f9SAndroid Build Coastguard Worker	    , $DCUTHELP_TAG, "</B>.  They were treated as if the tag existed "
1400*912701f9SAndroid Build Coastguard Worker	    , "on the initial revision.</EM>\n<BLOCKQUOTE>";
1401*912701f9SAndroid Build Coastguard Worker	print join("<BR>\n",
1402*912701f9SAndroid Build Coastguard Worker		   map {logLink($_, 'grepj_2')}
1403*912701f9SAndroid Build Coastguard Worker		   @TAGLESS_FILES);
1404*912701f9SAndroid Build Coastguard Worker	print "</BLOCKQUOTE>\n";
1405*912701f9SAndroid Build Coastguard Worker	for my $f (@TAGLESS_FILES) { $tagless{$f} = 1; }
1406*912701f9SAndroid Build Coastguard Worker    }
1407*912701f9SAndroid Build Coastguard Worker
1408*912701f9SAndroid Build Coastguard Worker    if (@BRANCHED_FILES) {
1409*912701f9SAndroid Build Coastguard Worker	print "<HR><EM><B>Error: The following ", scalar @BRANCHED_FILES
1410*912701f9SAndroid Build Coastguard Worker	    , " files contain the listed bug changes on different "
1411*912701f9SAndroid Build Coastguard Worker	    , " branches.\n</B></EM><BLOCKQUOTE>";
1412*912701f9SAndroid Build Coastguard Worker	print join("<BR>\n",
1413*912701f9SAndroid Build Coastguard Worker		   map {logLink($_->[0],'grepj_2') .
1414*912701f9SAndroid Build Coastguard Worker			": " . $_->[1] . ", " . $_->[2]}
1415*912701f9SAndroid Build Coastguard Worker		   @BRANCHED_FILES)
1416*912701f9SAndroid Build Coastguard Worker	    , "</BLOCKQUOTE>\n";
1417*912701f9SAndroid Build Coastguard Worker    }
1418*912701f9SAndroid Build Coastguard Worker
1419*912701f9SAndroid Build Coastguard Worker    if (@DCUTHELP_BADFILES) {
1420*912701f9SAndroid Build Coastguard Worker	print "<HR><EM><B>Error: The following "
1421*912701f9SAndroid Build Coastguard Worker	    , scalar @DCUTHELP_BADFILES,
1422*912701f9SAndroid Build Coastguard Worker	    " files contain intermingled bug fixes not specified in the list.",
1423*912701f9SAndroid Build Coastguard Worker	    "</B></EM>\n<BLOCKQUOTE>";
1424*912701f9SAndroid Build Coastguard Worker	my %badids;
1425*912701f9SAndroid Build Coastguard Worker	foreach (@DCUTHELP_BADFILES) {
1426*912701f9SAndroid Build Coastguard Worker	    my $relFile = $_->[0];
1427*912701f9SAndroid Build Coastguard Worker	    my $ids = $_->[1];
1428*912701f9SAndroid Build Coastguard Worker	    print logLink($relFile, 'grepj_2'), ": "
1429*912701f9SAndroid Build Coastguard Worker		, join(", ",
1430*912701f9SAndroid Build Coastguard Worker		       map {jitterbugLink($user, $_, 'grepj_2')}
1431*912701f9SAndroid Build Coastguard Worker		       @$ids)
1432*912701f9SAndroid Build Coastguard Worker		, "<BR>\n";
1433*912701f9SAndroid Build Coastguard Worker	    foreach my $i (@$ids) { $badids{$i} = 1; }
1434*912701f9SAndroid Build Coastguard Worker	}
1435*912701f9SAndroid Build Coastguard Worker	print "</BLOCKQUOTE>\n";
1436*912701f9SAndroid Build Coastguard Worker	print "Jitterbug changes not in the list: "
1437*912701f9SAndroid Build Coastguard Worker            , join(", ",
1438*912701f9SAndroid Build Coastguard Worker                   map {jitterbugLink($user, $_, 'grepj_2')}
1439*912701f9SAndroid Build Coastguard Worker                   sort {$a<=>$b} keys %badids)
1440*912701f9SAndroid Build Coastguard Worker            , "\n";
1441*912701f9SAndroid Build Coastguard Worker    }
1442*912701f9SAndroid Build Coastguard Worker
1443*912701f9SAndroid Build Coastguard Worker    if (@DCUTHELP_RETAGS) {
1444*912701f9SAndroid Build Coastguard Worker	print "<HR>CVS commands to update the tags in files containing "
1445*912701f9SAndroid Build Coastguard Worker	    ,"only the listed bugs (copy & paste into a shell window).";
1446*912701f9SAndroid Build Coastguard Worker	if (@DCUTHELP_BADFILES || @BRANCHED_FILES) {
1447*912701f9SAndroid Build Coastguard Worker	    print "<B>WARNING!  Some files (see above) contain other bug changes!  Files below are all \"legal\" but you may wish to address above problems before retagging.</B>";
1448*912701f9SAndroid Build Coastguard Worker	}
1449*912701f9SAndroid Build Coastguard Worker	print "<BR><BR><CODE><FONT SIZE=-1>";
1450*912701f9SAndroid Build Coastguard Worker	print "cd $CVSROOT<BR>\n";
1451*912701f9SAndroid Build Coastguard Worker	# Two passes, one for normal files, another for tagless
1452*912701f9SAndroid Build Coastguard Worker	my $tagless_count = 0;
1453*912701f9SAndroid Build Coastguard Worker	for (my $pass=0; $pass<2; ++$pass) {
1454*912701f9SAndroid Build Coastguard Worker	    print "<FONT COLOR=\"#0000FF\"># The following files do not contain the tag $DCUTHELP_TAG<BR>\n" if ($pass);
1455*912701f9SAndroid Build Coastguard Worker	    foreach (@DCUTHELP_RETAGS) {
1456*912701f9SAndroid Build Coastguard Worker		my $relFile = $_->[0];
1457*912701f9SAndroid Build Coastguard Worker		if ($pass == 0) {
1458*912701f9SAndroid Build Coastguard Worker		    if ($tagless{$relFile}) {
1459*912701f9SAndroid Build Coastguard Worker			++$tagless_count;
1460*912701f9SAndroid Build Coastguard Worker			next;
1461*912701f9SAndroid Build Coastguard Worker		    }
1462*912701f9SAndroid Build Coastguard Worker		} else {
1463*912701f9SAndroid Build Coastguard Worker		    next unless ($tagless{$relFile});
1464*912701f9SAndroid Build Coastguard Worker		}
1465*912701f9SAndroid Build Coastguard Worker		my $rev_hi = $_->[1];
1466*912701f9SAndroid Build Coastguard Worker		$relFile =~ s/,v$//;
1467*912701f9SAndroid Build Coastguard Worker		my $onBranch = ($rev_hi =~ /\d+\.\d+\.\d+/);
1468*912701f9SAndroid Build Coastguard Worker		print "<FONT COLOR=\"#FF0000\">" if ($onBranch);
1469*912701f9SAndroid Build Coastguard Worker		print "cvs tag -F -r $rev_hi $DCUTHELP_TAG $relFile";
1470*912701f9SAndroid Build Coastguard Worker		print "</FONT>" if ($onBranch);
1471*912701f9SAndroid Build Coastguard Worker		print "<BR>\n";
1472*912701f9SAndroid Build Coastguard Worker	    }
1473*912701f9SAndroid Build Coastguard Worker	    last unless ($tagless_count);
1474*912701f9SAndroid Build Coastguard Worker	    print "</FONT>\n" if ($pass);
1475*912701f9SAndroid Build Coastguard Worker	}
1476*912701f9SAndroid Build Coastguard Worker	print "</FONT></CODE>";
1477*912701f9SAndroid Build Coastguard Worker    } else {
1478*912701f9SAndroid Build Coastguard Worker	print "<HR>Nothing to do; no clean checkins for bugs "
1479*912701f9SAndroid Build Coastguard Worker	    , join(", ",
1480*912701f9SAndroid Build Coastguard Worker		   map {jitterbugLink($user, $_, 'grepj_2')}
1481*912701f9SAndroid Build Coastguard Worker		   sort {$a<=>$b} keys %DCUTHELP_IDS)
1482*912701f9SAndroid Build Coastguard Worker	    , " after "
1483*912701f9SAndroid Build Coastguard Worker	    , tagLink($DCUTHELP_TAG,$m[0],'grepj_2')
1484*912701f9SAndroid Build Coastguard Worker	    , " in module(s) <B>"
1485*912701f9SAndroid Build Coastguard Worker	    , join(", ", @m), "</B>.\n"
1486*912701f9SAndroid Build Coastguard Worker	    ;
1487*912701f9SAndroid Build Coastguard Worker    }
1488*912701f9SAndroid Build Coastguard Worker}
1489*912701f9SAndroid Build Coastguard Worker
1490*912701f9SAndroid Build Coastguard Worker# Given a relative path to $CVSROOT, dcuthelp the
1491*912701f9SAndroid Build Coastguard Worker# corresponding item under $CACHE.  Path may point to a
1492*912701f9SAndroid Build Coastguard Worker# file or a directory.
1493*912701f9SAndroid Build Coastguard Worker# @param relative directory, not ending in "/", e.g. "icu/icu"
1494*912701f9SAndroid Build Coastguard Worker# @param item name in that directory
1495*912701f9SAndroid Build Coastguard Workersub dcuthelpEntry {
1496*912701f9SAndroid Build Coastguard Worker    my $relDir = shift;
1497*912701f9SAndroid Build Coastguard Worker    my $item = shift; # A file or dir in $CVSROOT/$relDir
1498*912701f9SAndroid Build Coastguard Worker
1499*912701f9SAndroid Build Coastguard Worker    # Ignore stuff in the Attic
1500*912701f9SAndroid Build Coastguard Worker    return if ($item eq 'Attic');
1501*912701f9SAndroid Build Coastguard Worker
1502*912701f9SAndroid Build Coastguard Worker    if (-d "$CACHE/$relDir/$item") {
1503*912701f9SAndroid Build Coastguard Worker        dcuthelpDir("$relDir/$item");
1504*912701f9SAndroid Build Coastguard Worker    } elsif ($item =~ /,v$/) {
1505*912701f9SAndroid Build Coastguard Worker        dcuthelpFile("$relDir/$item");
1506*912701f9SAndroid Build Coastguard Worker    }
1507*912701f9SAndroid Build Coastguard Worker}
1508*912701f9SAndroid Build Coastguard Worker
1509*912701f9SAndroid Build Coastguard Worker# Given a relative directory path to $CACHE, dcuthelp the
1510*912701f9SAndroid Build Coastguard Worker# underlying files.
1511*912701f9SAndroid Build Coastguard Worker# @param relative directory, not ending in "/", e.g. "icu/icu"
1512*912701f9SAndroid Build Coastguard Workersub dcuthelpDir {
1513*912701f9SAndroid Build Coastguard Worker    my $relDir = shift;
1514*912701f9SAndroid Build Coastguard Worker
1515*912701f9SAndroid Build Coastguard Worker    debugOut("dcuthelpDir($relDir)") if ($DEBUG);
1516*912701f9SAndroid Build Coastguard Worker
1517*912701f9SAndroid Build Coastguard Worker    my $cacheDir = "$CACHE/$relDir";
1518*912701f9SAndroid Build Coastguard Worker
1519*912701f9SAndroid Build Coastguard Worker    # First dcuthelp files in this directory
1520*912701f9SAndroid Build Coastguard Worker    opendir(DIR, $cacheDir);
1521*912701f9SAndroid Build Coastguard Worker    my @cacheList = grep !/^\.\.?$/, readdir(DIR);
1522*912701f9SAndroid Build Coastguard Worker    closedir(DIR);
1523*912701f9SAndroid Build Coastguard Worker
1524*912701f9SAndroid Build Coastguard Worker    # Dcuthelp each individual entry
1525*912701f9SAndroid Build Coastguard Worker    foreach (@cacheList) {
1526*912701f9SAndroid Build Coastguard Worker        dcuthelpEntry($relDir, $_);
1527*912701f9SAndroid Build Coastguard Worker    }
1528*912701f9SAndroid Build Coastguard Worker}
1529*912701f9SAndroid Build Coastguard Worker
1530*912701f9SAndroid Build Coastguard Worker# Given a relative file path to $CVSROOT, dcuthelp the
1531*912701f9SAndroid Build Coastguard Worker# corresponding file under $CACHE.
1532*912701f9SAndroid Build Coastguard Worker# @param relative file path
1533*912701f9SAndroid Build Coastguard Workersub dcuthelpFile {
1534*912701f9SAndroid Build Coastguard Worker    my $relFile = shift;
1535*912701f9SAndroid Build Coastguard Worker
1536*912701f9SAndroid Build Coastguard Worker    # Display progress; it takes awhile
1537*912701f9SAndroid Build Coastguard Worker    if (++$DCUTHELP_COUNT % 100 == 0) {
1538*912701f9SAndroid Build Coastguard Worker	print " $DCUTHELP_COUNT...";
1539*912701f9SAndroid Build Coastguard Worker    }
1540*912701f9SAndroid Build Coastguard Worker
1541*912701f9SAndroid Build Coastguard Worker    # This file contains the output of rlog.
1542*912701f9SAndroid Build Coastguard Worker    my $file = "$CACHE/$relFile";
1543*912701f9SAndroid Build Coastguard Worker
1544*912701f9SAndroid Build Coastguard Worker    # Parse the rlog file.  Start by extracting the tag names.  Look
1545*912701f9SAndroid Build Coastguard Worker    # for the DCUTHELP_TAG and its associated revision
1546*912701f9SAndroid Build Coastguard Worker    # number.
1547*912701f9SAndroid Build Coastguard Worker    open(IN, $file);
1548*912701f9SAndroid Build Coastguard Worker    while (<IN>) {
1549*912701f9SAndroid Build Coastguard Worker	last if (/^symbolic names:\s*$/);
1550*912701f9SAndroid Build Coastguard Worker    }
1551*912701f9SAndroid Build Coastguard Worker    my $rev_tag = '';
1552*912701f9SAndroid Build Coastguard Worker    while (<IN>) {
1553*912701f9SAndroid Build Coastguard Worker	last if (/^\S/);
1554*912701f9SAndroid Build Coastguard Worker	if (/^\s+$DCUTHELP_TAG:\s*(\S+)/) {
1555*912701f9SAndroid Build Coastguard Worker	    $rev_tag = $1;
1556*912701f9SAndroid Build Coastguard Worker	    last;
1557*912701f9SAndroid Build Coastguard Worker	}
1558*912701f9SAndroid Build Coastguard Worker    }
1559*912701f9SAndroid Build Coastguard Worker
1560*912701f9SAndroid Build Coastguard Worker    # Check for dead files.  Look ahead and find the state of the head
1561*912701f9SAndroid Build Coastguard Worker    # revision.
1562*912701f9SAndroid Build Coastguard Worker    my $pos = tell(IN);
1563*912701f9SAndroid Build Coastguard Worker    my $state = '';
1564*912701f9SAndroid Build Coastguard Worker    while (<IN>) {
1565*912701f9SAndroid Build Coastguard Worker	if (/^date:.+state: ([A-Za-z]+)/) {
1566*912701f9SAndroid Build Coastguard Worker	    $state = $1;
1567*912701f9SAndroid Build Coastguard Worker	    last;
1568*912701f9SAndroid Build Coastguard Worker	}
1569*912701f9SAndroid Build Coastguard Worker    }
1570*912701f9SAndroid Build Coastguard Worker    seek(IN,$pos,0);
1571*912701f9SAndroid Build Coastguard Worker
1572*912701f9SAndroid Build Coastguard Worker    # If this file is 'dead', we're done.
1573*912701f9SAndroid Build Coastguard Worker    return if ($state eq 'dead');
1574*912701f9SAndroid Build Coastguard Worker
1575*912701f9SAndroid Build Coastguard Worker    # If the tag is missing, record the fact.  Continue to process
1576*912701f9SAndroid Build Coastguard Worker    # the file as if the tag existed on the earliest revision.
1577*912701f9SAndroid Build Coastguard Worker    # This allows the tagging of newly added files.
1578*912701f9SAndroid Build Coastguard Worker    if (!$rev_tag) {
1579*912701f9SAndroid Build Coastguard Worker	push @TAGLESS_FILES, $relFile;
1580*912701f9SAndroid Build Coastguard Worker    }
1581*912701f9SAndroid Build Coastguard Worker
1582*912701f9SAndroid Build Coastguard Worker    # I'm going to assume the rlog output (the CACHE file) contains a series
1583*912701f9SAndroid Build Coastguard Worker    # of groups of lines, like so:
1584*912701f9SAndroid Build Coastguard Worker    #|----------------------------
1585*912701f9SAndroid Build Coastguard Worker    #|revision 1.40
1586*912701f9SAndroid Build Coastguard Worker    #|date: 2001/08/02 18:24:58;  author: grhoten;  state: Exp;  lines: +82 -73
1587*912701f9SAndroid Build Coastguard Worker    #|jitterbug 1080: general readme.html updates
1588*912701f9SAndroid Build Coastguard Worker    # That is, the first line has the revision #.
1589*912701f9SAndroid Build Coastguard Worker    # The third line has the bug ID.  Sometimes the third line has a
1590*912701f9SAndroid Build Coastguard Worker    # branch field.
1591*912701f9SAndroid Build Coastguard Worker
1592*912701f9SAndroid Build Coastguard Worker    # Find bug IDs later than the given tag, and record any that aren't
1593*912701f9SAndroid Build Coastguard Worker    # on the allowed list.  Locate $rev_hi - the high
1594*912701f9SAndroid Build Coastguard Worker    # revision of any bug found in the list.
1595*912701f9SAndroid Build Coastguard Worker    my @problem_ids; # Bug IDs between $rev_tag and $rev_hi not in the list
1596*912701f9SAndroid Build Coastguard Worker    my $rev_hi;
1597*912701f9SAndroid Build Coastguard Worker    my $bottom_rev = ''; # Last revision in the file
1598*912701f9SAndroid Build Coastguard Worker    while (<IN>) {
1599*912701f9SAndroid Build Coastguard Worker        if (/^-{20,}$/) {
1600*912701f9SAndroid Build Coastguard Worker	    $_ = <IN>; # Read revision line
1601*912701f9SAndroid Build Coastguard Worker	    if (/revision (\S+)/) {
1602*912701f9SAndroid Build Coastguard Worker		my $rev = $1;
1603*912701f9SAndroid Build Coastguard Worker		$bottom_rev = $rev;
1604*912701f9SAndroid Build Coastguard Worker		if ($rev eq $rev_tag) {
1605*912701f9SAndroid Build Coastguard Worker		    # Scan remainder of file to record last rev
1606*912701f9SAndroid Build Coastguard Worker		    while (<IN>) {
1607*912701f9SAndroid Build Coastguard Worker			if (/^-{20,}$/) {
1608*912701f9SAndroid Build Coastguard Worker			    $_ = <IN>; # Read revision line
1609*912701f9SAndroid Build Coastguard Worker			    $bottom_rev = $1 if (/revision (\S+)/);
1610*912701f9SAndroid Build Coastguard Worker			}
1611*912701f9SAndroid Build Coastguard Worker		    }
1612*912701f9SAndroid Build Coastguard Worker		    last;
1613*912701f9SAndroid Build Coastguard Worker		}
1614*912701f9SAndroid Build Coastguard Worker		my $date = <IN>; # Read date line
1615*912701f9SAndroid Build Coastguard Worker		$_ = <IN>; # Read comment or branches: line
1616*912701f9SAndroid Build Coastguard Worker		$_ = <IN> if (/^branches:/); # Read line after branches:
1617*912701f9SAndroid Build Coastguard Worker		my $id;
1618*912701f9SAndroid Build Coastguard Worker		if (/^\s*jitterbug\s+0*(\d+)/i) {
1619*912701f9SAndroid Build Coastguard Worker		    $id = $1;
1620*912701f9SAndroid Build Coastguard Worker		} else {
1621*912701f9SAndroid Build Coastguard Worker		    push @NO_JITTERBUG_FILES, [$relFile, $rev, $_]
1622*912701f9SAndroid Build Coastguard Worker			if (noJitterbugFilter($rev, $date));
1623*912701f9SAndroid Build Coastguard Worker		    $id = $NO_JITTERBUG;
1624*912701f9SAndroid Build Coastguard Worker		}
1625*912701f9SAndroid Build Coastguard Worker		my $in_list = (exists $DCUTHELP_IDS{$id});
1626*912701f9SAndroid Build Coastguard Worker#		# Handle tagless files a little differently
1627*912701f9SAndroid Build Coastguard Worker#		if (!$rev_tag) {
1628*912701f9SAndroid Build Coastguard Worker#		    if (!$rev_hi) {
1629*912701f9SAndroid Build Coastguard Worker#			if ($in_list) {
1630*912701f9SAndroid Build Coastguard Worker#			    $rev_hi = $rev;
1631*912701f9SAndroid Build Coastguard Worker#			} else {
1632*912701f9SAndroid Build Coastguard Worker#			}
1633*912701f9SAndroid Build Coastguard Worker#		    }
1634*912701f9SAndroid Build Coastguard Worker#
1635*912701f9SAndroid Build Coastguard Worker#		}
1636*912701f9SAndroid Build Coastguard Worker		if (!$rev_hi) {
1637*912701f9SAndroid Build Coastguard Worker		    if ($in_list) {
1638*912701f9SAndroid Build Coastguard Worker			$rev_hi = $rev;
1639*912701f9SAndroid Build Coastguard Worker		    }
1640*912701f9SAndroid Build Coastguard Worker		} else {
1641*912701f9SAndroid Build Coastguard Worker		    if (!$in_list) {
1642*912701f9SAndroid Build Coastguard Worker			push @problem_ids, $id;
1643*912701f9SAndroid Build Coastguard Worker		    }
1644*912701f9SAndroid Build Coastguard Worker		}
1645*912701f9SAndroid Build Coastguard Worker	    } else {
1646*912701f9SAndroid Build Coastguard Worker		cantParse('revision', $relFile, $_);
1647*912701f9SAndroid Build Coastguard Worker	    }
1648*912701f9SAndroid Build Coastguard Worker        }
1649*912701f9SAndroid Build Coastguard Worker    }
1650*912701f9SAndroid Build Coastguard Worker
1651*912701f9SAndroid Build Coastguard Worker    # If the bottom revision looks like a branch, then we need
1652*912701f9SAndroid Build Coastguard Worker    # to do extra processing.  Branch revisions are listed at the
1653*912701f9SAndroid Build Coastguard Worker    # end of the rlog output.
1654*912701f9SAndroid Build Coastguard Worker    if ($bottom_rev =~ /\d+\.\d+\.\d+\.\d+/ &&
1655*912701f9SAndroid Build Coastguard Worker	$bottom_rev ne '1.1.1.1') {
1656*912701f9SAndroid Build Coastguard Worker
1657*912701f9SAndroid Build Coastguard Worker	# This file contains branches; do special handling
1658*912701f9SAndroid Build Coastguard Worker
1659*912701f9SAndroid Build Coastguard Worker	# Parse all the revisions and form a branch tree.
1660*912701f9SAndroid Build Coastguard Worker	# Construct a hash (%tree) of revision numbers to jitterbugs.
1661*912701f9SAndroid Build Coastguard Worker	# In addition, "$rev-" maps to a ref to an array of branches,
1662*912701f9SAndroid Build Coastguard Worker	# if any.
1663*912701f9SAndroid Build Coastguard Worker	my %tree;
1664*912701f9SAndroid Build Coastguard Worker	seek(IN,0,0); # rewind to start
1665*912701f9SAndroid Build Coastguard Worker	while (<IN>) {
1666*912701f9SAndroid Build Coastguard Worker	    if (/^-{20,}$/) {
1667*912701f9SAndroid Build Coastguard Worker		$_ = <IN>; # Read revision line
1668*912701f9SAndroid Build Coastguard Worker		if (/revision (\S+)/) {
1669*912701f9SAndroid Build Coastguard Worker		    my $rev = $1;
1670*912701f9SAndroid Build Coastguard Worker		    my $date = <IN>; # Read date line
1671*912701f9SAndroid Build Coastguard Worker		    $_ = <IN>; # Read comment or branches: line
1672*912701f9SAndroid Build Coastguard Worker		    if (/^branches:\s*(.*)/) {
1673*912701f9SAndroid Build Coastguard Worker			my @branches = split(/;\s*/, $1);
1674*912701f9SAndroid Build Coastguard Worker			$tree{$rev . '-'} = \@branches;
1675*912701f9SAndroid Build Coastguard Worker			$_ = <IN>; # Read comment line
1676*912701f9SAndroid Build Coastguard Worker		    }
1677*912701f9SAndroid Build Coastguard Worker		    my $id;
1678*912701f9SAndroid Build Coastguard Worker		    if (/^\s*jitterbug\s+0*(\d+)/i) {
1679*912701f9SAndroid Build Coastguard Worker			$id = $1;
1680*912701f9SAndroid Build Coastguard Worker		    } else {
1681*912701f9SAndroid Build Coastguard Worker			push @NO_JITTERBUG_FILES, [$relFile, $rev, $_]
1682*912701f9SAndroid Build Coastguard Worker			    if (noJitterbugFilter($rev, $date));
1683*912701f9SAndroid Build Coastguard Worker			$id = $NO_JITTERBUG;
1684*912701f9SAndroid Build Coastguard Worker		    }
1685*912701f9SAndroid Build Coastguard Worker		    $tree{$rev} = $id;
1686*912701f9SAndroid Build Coastguard Worker		} else {
1687*912701f9SAndroid Build Coastguard Worker		    cantParse('revision', $relFile, $_);
1688*912701f9SAndroid Build Coastguard Worker		}
1689*912701f9SAndroid Build Coastguard Worker	    }
1690*912701f9SAndroid Build Coastguard Worker	}
1691*912701f9SAndroid Build Coastguard Worker
1692*912701f9SAndroid Build Coastguard Worker#	print "[$relFile: ";
1693*912701f9SAndroid Build Coastguard Worker#	print join("; ",
1694*912701f9SAndroid Build Coastguard Worker#		   map {$_ . " => " .
1695*912701f9SAndroid Build Coastguard Worker#		       (ref($tree{$_})
1696*912701f9SAndroid Build Coastguard Worker#			?("(".join(",",@{$tree{$_}}).")")
1697*912701f9SAndroid Build Coastguard Worker#			:$tree{$_})}
1698*912701f9SAndroid Build Coastguard Worker#		   sort keys %tree);
1699*912701f9SAndroid Build Coastguard Worker
1700*912701f9SAndroid Build Coastguard Worker	$rev_hi = dcuthelpScan(\%tree, $rev_tag, 1);
1701*912701f9SAndroid Build Coastguard Worker
1702*912701f9SAndroid Build Coastguard Worker#	print ": scan=>$rev_hi]";
1703*912701f9SAndroid Build Coastguard Worker
1704*912701f9SAndroid Build Coastguard Worker	@problem_ids = ();
1705*912701f9SAndroid Build Coastguard Worker	if ($rev_hi =~ /;/) {
1706*912701f9SAndroid Build Coastguard Worker	    # Tags on different branches
1707*912701f9SAndroid Build Coastguard Worker	    my @a = split(/;/, $rev_hi);
1708*912701f9SAndroid Build Coastguard Worker	    unshift @a, $relFile;
1709*912701f9SAndroid Build Coastguard Worker	    push @BRANCHED_FILES, \@a;
1710*912701f9SAndroid Build Coastguard Worker	    return;
1711*912701f9SAndroid Build Coastguard Worker	} elsif ($rev_hi) {
1712*912701f9SAndroid Build Coastguard Worker	    my @revs = traverseRevisions($rev_tag, $rev_hi);
1713*912701f9SAndroid Build Coastguard Worker
1714*912701f9SAndroid Build Coastguard Worker	    shift(@revs); # discard rev_lo
1715*912701f9SAndroid Build Coastguard Worker	    my %revs;
1716*912701f9SAndroid Build Coastguard Worker	    foreach (@revs) { $revs{$_} = 1; } # convert to hash
1717*912701f9SAndroid Build Coastguard Worker
1718*912701f9SAndroid Build Coastguard Worker	    seek(IN,0,0); # rewind to start
1719*912701f9SAndroid Build Coastguard Worker	    while (<IN>) {
1720*912701f9SAndroid Build Coastguard Worker		if (/^-{20,}$/) {
1721*912701f9SAndroid Build Coastguard Worker		    $_ = <IN>; # Read revision line
1722*912701f9SAndroid Build Coastguard Worker		    if (/revision (\S+)/) {
1723*912701f9SAndroid Build Coastguard Worker			my $rev = $1;
1724*912701f9SAndroid Build Coastguard Worker			if (exists $revs{$rev}) {
1725*912701f9SAndroid Build Coastguard Worker			    delete $revs{$rev};
1726*912701f9SAndroid Build Coastguard Worker			    my $date = <IN>; # Read date line
1727*912701f9SAndroid Build Coastguard Worker			    $_ = <IN>; # Read comment or branches: line
1728*912701f9SAndroid Build Coastguard Worker			    $_ = <IN> if (/^branches:/); # Read line after branches:
1729*912701f9SAndroid Build Coastguard Worker			    my $id;
1730*912701f9SAndroid Build Coastguard Worker			    if (/^\s*jitterbug\s+0*(\d+)/i) {
1731*912701f9SAndroid Build Coastguard Worker				$id = $1;
1732*912701f9SAndroid Build Coastguard Worker			    } else {
1733*912701f9SAndroid Build Coastguard Worker				push @NO_JITTERBUG_FILES, [$relFile, $rev, $_]
1734*912701f9SAndroid Build Coastguard Worker				    if (noJitterbugFilter($rev, $date));
1735*912701f9SAndroid Build Coastguard Worker				$id = $NO_JITTERBUG;
1736*912701f9SAndroid Build Coastguard Worker			    }
1737*912701f9SAndroid Build Coastguard Worker			    if (!exists $DCUTHELP_IDS{$id}) {
1738*912701f9SAndroid Build Coastguard Worker				push @problem_ids, $id;
1739*912701f9SAndroid Build Coastguard Worker			    }
1740*912701f9SAndroid Build Coastguard Worker			    last unless (%revs);
1741*912701f9SAndroid Build Coastguard Worker			}
1742*912701f9SAndroid Build Coastguard Worker		    } else {
1743*912701f9SAndroid Build Coastguard Worker			cantParse('revision', $relFile, $_);
1744*912701f9SAndroid Build Coastguard Worker			last; # This is very bad - bail out
1745*912701f9SAndroid Build Coastguard Worker		    }
1746*912701f9SAndroid Build Coastguard Worker		}
1747*912701f9SAndroid Build Coastguard Worker	    }
1748*912701f9SAndroid Build Coastguard Worker	}
1749*912701f9SAndroid Build Coastguard Worker    }
1750*912701f9SAndroid Build Coastguard Worker
1751*912701f9SAndroid Build Coastguard Worker    if (@problem_ids) {
1752*912701f9SAndroid Build Coastguard Worker	my @a = sortedUniqueInts(@problem_ids);
1753*912701f9SAndroid Build Coastguard Worker	push @DCUTHELP_BADFILES, [$relFile, \@a];
1754*912701f9SAndroid Build Coastguard Worker    } elsif ($rev_hi) {
1755*912701f9SAndroid Build Coastguard Worker	# This file is okay; record the data needed for moving the tag
1756*912701f9SAndroid Build Coastguard Worker	push @DCUTHELP_RETAGS, [$relFile, $rev_hi];
1757*912701f9SAndroid Build Coastguard Worker    }
1758*912701f9SAndroid Build Coastguard Worker
1759*912701f9SAndroid Build Coastguard Worker    close(IN);
1760*912701f9SAndroid Build Coastguard Worker}
1761*912701f9SAndroid Build Coastguard Worker
1762*912701f9SAndroid Build Coastguard Worker# Given a revision tree (see dcuthelpFile), look for %DCUTHELP_IDS
1763*912701f9SAndroid Build Coastguard Worker# bugs along various branches, starting at a given revision.  Proceed
1764*912701f9SAndroid Build Coastguard Worker# along the branch of the given revision by incrementing it using
1765*912701f9SAndroid Build Coastguard Worker# incRev().  If any revision along the way is a branch point, follow
1766*912701f9SAndroid Build Coastguard Worker# that branch by recursing.  If found on two split branches,
1767*912701f9SAndroid Build Coastguard Worker# return 'rev;rev'.  If not found at all, return ''.  If found on
1768*912701f9SAndroid Build Coastguard Worker# exactly one branch, return the furthest revision at which it was
1769*912701f9SAndroid Build Coastguard Worker# found.
1770*912701f9SAndroid Build Coastguard Worker#
1771*912701f9SAndroid Build Coastguard Worker# @param tree, as created by dcuthelpFile
1772*912701f9SAndroid Build Coastguard Worker# @param first revision to examine
1773*912701f9SAndroid Build Coastguard Worker# @param if true, exclude given revision from bug search
1774*912701f9SAndroid Build Coastguard Worker#        but not from branch analysis.
1775*912701f9SAndroid Build Coastguard Worker#
1776*912701f9SAndroid Build Coastguard Worker# @return either a revision, or 'rev;rev' if the bugs occur
1777*912701f9SAndroid Build Coastguard Worker#         on two split branches, or '' if the bugs aren't seen.
1778*912701f9SAndroid Build Coastguard Workersub dcuthelpScan {
1779*912701f9SAndroid Build Coastguard Worker    my $tree = shift; # parsed revision tree; see dcuthelpFile
1780*912701f9SAndroid Build Coastguard Worker    my $rev = shift; # rev to start at
1781*912701f9SAndroid Build Coastguard Worker    my $exclusive = shift || ''; # is $rev exclusive?
1782*912701f9SAndroid Build Coastguard Worker
1783*912701f9SAndroid Build Coastguard Worker#   print "[scan $tree $rev $exclusive]";
1784*912701f9SAndroid Build Coastguard Worker
1785*912701f9SAndroid Build Coastguard Worker    # If there are no branches between $rev and the end of its branch,
1786*912701f9SAndroid Build Coastguard Worker    # then return the top revision at which one of %DCUTHELP_IDS is seen.
1787*912701f9SAndroid Build Coastguard Worker    my $branchrev = ''; # First rev at which branch was seen, if any
1788*912701f9SAndroid Build Coastguard Worker    my $lastbugrev = ''; # Last rev at which bug was seen
1789*912701f9SAndroid Build Coastguard Worker    my $r;
1790*912701f9SAndroid Build Coastguard Worker    for ($r=$rev ;exists $tree->{$r}; $r=incRev($r)) {
1791*912701f9SAndroid Build Coastguard Worker#	print "{$r}";
1792*912701f9SAndroid Build Coastguard Worker	if (exists $DCUTHELP_IDS{$tree->{$r}}) {
1793*912701f9SAndroid Build Coastguard Worker	    $lastbugrev = $r;
1794*912701f9SAndroid Build Coastguard Worker	}
1795*912701f9SAndroid Build Coastguard Worker	if (exists $tree->{"$r-"}) {
1796*912701f9SAndroid Build Coastguard Worker	    $branchrev = $r;
1797*912701f9SAndroid Build Coastguard Worker	    last;
1798*912701f9SAndroid Build Coastguard Worker	}
1799*912701f9SAndroid Build Coastguard Worker    }
1800*912701f9SAndroid Build Coastguard Worker
1801*912701f9SAndroid Build Coastguard Worker    # If $exclusive it true, can't return this rev.
1802*912701f9SAndroid Build Coastguard Worker    if ($exclusive && ($lastbugrev eq $rev)) {
1803*912701f9SAndroid Build Coastguard Worker	$lastbugrev = '';
1804*912701f9SAndroid Build Coastguard Worker    }
1805*912701f9SAndroid Build Coastguard Worker
1806*912701f9SAndroid Build Coastguard Worker    # If there are no branches we are done.
1807*912701f9SAndroid Build Coastguard Worker    if (!$branchrev) {
1808*912701f9SAndroid Build Coastguard Worker	return $lastbugrev;
1809*912701f9SAndroid Build Coastguard Worker    }
1810*912701f9SAndroid Build Coastguard Worker
1811*912701f9SAndroid Build Coastguard Worker    # Otherwise, examine the n branches and the continuation of
1812*912701f9SAndroid Build Coastguard Worker    # this branch separately.  Convert branch revisions to the first
1813*912701f9SAndroid Build Coastguard Worker    # rev on each branch, e.g., "1.14.2" => "1.14.2.1"
1814*912701f9SAndroid Build Coastguard Worker    my @branches = map {"$_.1"} @{$tree->{"$branchrev-"}};
1815*912701f9SAndroid Build Coastguard Worker    $r = incRev($branchrev);
1816*912701f9SAndroid Build Coastguard Worker    push @branches, $r if (exists $tree->{$r});
1817*912701f9SAndroid Build Coastguard Worker
1818*912701f9SAndroid Build Coastguard Worker    $r = '';
1819*912701f9SAndroid Build Coastguard Worker    foreach (@branches) {
1820*912701f9SAndroid Build Coastguard Worker	my $a = dcuthelpScan($tree, $_);
1821*912701f9SAndroid Build Coastguard Worker	return $a if ($a =~ /;/);
1822*912701f9SAndroid Build Coastguard Worker	if ($a) {
1823*912701f9SAndroid Build Coastguard Worker	    if ($r) {
1824*912701f9SAndroid Build Coastguard Worker		# Our bugs were seen on more than one branch
1825*912701f9SAndroid Build Coastguard Worker		return "$r;$a";
1826*912701f9SAndroid Build Coastguard Worker	    }
1827*912701f9SAndroid Build Coastguard Worker	    $r = $a;
1828*912701f9SAndroid Build Coastguard Worker	}
1829*912701f9SAndroid Build Coastguard Worker    }
1830*912701f9SAndroid Build Coastguard Worker
1831*912701f9SAndroid Build Coastguard Worker    # If we haven't seen it on any branches, use result up to the
1832*912701f9SAndroid Build Coastguard Worker    # branch point, found above.
1833*912701f9SAndroid Build Coastguard Worker    $r ||= $lastbugrev;
1834*912701f9SAndroid Build Coastguard Worker
1835*912701f9SAndroid Build Coastguard Worker    return $r;
1836*912701f9SAndroid Build Coastguard Worker}
1837*912701f9SAndroid Build Coastguard Worker
1838*912701f9SAndroid Build Coastguard Worker######################################################################
1839*912701f9SAndroid Build Coastguard Worker# CVS rlog cache
1840*912701f9SAndroid Build Coastguard Worker######################################################################
1841*912701f9SAndroid Build Coastguard Worker
1842*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
1843*912701f9SAndroid Build Coastguard Worker# Given a relative path to $CVSROOT, update the
1844*912701f9SAndroid Build Coastguard Worker# corresponding item under $CACHE.  Path may point to a
1845*912701f9SAndroid Build Coastguard Worker# file or a directory.
1846*912701f9SAndroid Build Coastguard Worker# @param relative directory, not ending in "/", e.g. "icu/icu"
1847*912701f9SAndroid Build Coastguard Worker# @param item name in that directory
1848*912701f9SAndroid Build Coastguard Workersub updateCacheEntry {
1849*912701f9SAndroid Build Coastguard Worker    my $relDir = shift;
1850*912701f9SAndroid Build Coastguard Worker    my $item = shift; # A file or dir in $CVSROOT/$relDir
1851*912701f9SAndroid Build Coastguard Worker
1852*912701f9SAndroid Build Coastguard Worker    if (-d "$CVSROOT/$relDir/$item") {
1853*912701f9SAndroid Build Coastguard Worker        updateCacheDir("$relDir/$item");
1854*912701f9SAndroid Build Coastguard Worker    } elsif ($item =~ /,v$/) {
1855*912701f9SAndroid Build Coastguard Worker        updateCacheFile("$relDir/$item");
1856*912701f9SAndroid Build Coastguard Worker    }
1857*912701f9SAndroid Build Coastguard Worker}
1858*912701f9SAndroid Build Coastguard Worker
1859*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
1860*912701f9SAndroid Build Coastguard Worker# Given a relative directory path to $CVSROOT, update the
1861*912701f9SAndroid Build Coastguard Worker# corresponding directory under $CACHE.
1862*912701f9SAndroid Build Coastguard Worker# @param relative directory, not ending in "/", e.g. "icu/icu"
1863*912701f9SAndroid Build Coastguard Workersub updateCacheDir {
1864*912701f9SAndroid Build Coastguard Worker    my $relDir = shift;
1865*912701f9SAndroid Build Coastguard Worker
1866*912701f9SAndroid Build Coastguard Worker    debugOut("+updateCacheDir($relDir)") if ($DEBUG);
1867*912701f9SAndroid Build Coastguard Worker
1868*912701f9SAndroid Build Coastguard Worker    my $cvsDir = "$CVSROOT/$relDir";
1869*912701f9SAndroid Build Coastguard Worker    my $cacheDir = "$CACHE/$relDir";
1870*912701f9SAndroid Build Coastguard Worker
1871*912701f9SAndroid Build Coastguard Worker    # First update files in this directory
1872*912701f9SAndroid Build Coastguard Worker    opendir(DIR, $cvsDir);
1873*912701f9SAndroid Build Coastguard Worker    my @cvsList = grep !/^\.\.?$/ && $_ ne 'CVS', readdir(DIR);
1874*912701f9SAndroid Build Coastguard Worker    closedir(DIR);
1875*912701f9SAndroid Build Coastguard Worker    my %cvsPruneHash;
1876*912701f9SAndroid Build Coastguard Worker    foreach (@cvsList) { $cvsPruneHash{$_} = 1; }
1877*912701f9SAndroid Build Coastguard Worker    if (!$QUERY->param('include_attic')) {
1878*912701f9SAndroid Build Coastguard Worker        @cvsList = grep !/^attic$/i, @cvsList;
1879*912701f9SAndroid Build Coastguard Worker    }
1880*912701f9SAndroid Build Coastguard Worker    my %cvsHash;
1881*912701f9SAndroid Build Coastguard Worker    foreach (@cvsList) { $cvsHash{$_} = 1; }
1882*912701f9SAndroid Build Coastguard Worker
1883*912701f9SAndroid Build Coastguard Worker    # Update/create the cache directory.  If it doesn't exist,
1884*912701f9SAndroid Build Coastguard Worker    # create it.  If it does, prune out any obsolete entries.
1885*912701f9SAndroid Build Coastguard Worker    if (-d $cacheDir) {
1886*912701f9SAndroid Build Coastguard Worker        if (!opendir(DIR, $cacheDir)) {
1887*912701f9SAndroid Build Coastguard Worker            print "Can't open dir $cacheDir: $!";
1888*912701f9SAndroid Build Coastguard Worker	    debugOut("-!updateCacheDir($relDir)") if ($DEBUG);
1889*912701f9SAndroid Build Coastguard Worker            return;
1890*912701f9SAndroid Build Coastguard Worker        }
1891*912701f9SAndroid Build Coastguard Worker        my @cacheList = grep !/^\.\.?$/, readdir(DIR);
1892*912701f9SAndroid Build Coastguard Worker        closedir(DIR);
1893*912701f9SAndroid Build Coastguard Worker
1894*912701f9SAndroid Build Coastguard Worker        # Delete things that don't exist in CVS
1895*912701f9SAndroid Build Coastguard Worker        foreach (@cacheList) {
1896*912701f9SAndroid Build Coastguard Worker            if (!exists $cvsPruneHash{$_}) {
1897*912701f9SAndroid Build Coastguard Worker		debugOut ( " Removing $cacheDir/$_ .." ) if ($DEBUG);
1898*912701f9SAndroid Build Coastguard Worker                rmtree("$cacheDir/$_", 0, 1);
1899*912701f9SAndroid Build Coastguard Worker            }
1900*912701f9SAndroid Build Coastguard Worker        }
1901*912701f9SAndroid Build Coastguard Worker    } else {
1902*912701f9SAndroid Build Coastguard Worker        mkpath($cacheDir, 0, 0777);
1903*912701f9SAndroid Build Coastguard Worker    }
1904*912701f9SAndroid Build Coastguard Worker
1905*912701f9SAndroid Build Coastguard Worker    # Update each individual entry
1906*912701f9SAndroid Build Coastguard Worker    foreach (@cvsList) {
1907*912701f9SAndroid Build Coastguard Worker        updateCacheEntry($relDir, $_);
1908*912701f9SAndroid Build Coastguard Worker    }
1909*912701f9SAndroid Build Coastguard Worker
1910*912701f9SAndroid Build Coastguard Worker    debugOut("-updateCacheDir($relDir)") if ($DEBUG);
1911*912701f9SAndroid Build Coastguard Worker}
1912*912701f9SAndroid Build Coastguard Worker
1913*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
1914*912701f9SAndroid Build Coastguard Worker# Given a relative file path to $CVSROOT, update the
1915*912701f9SAndroid Build Coastguard Worker# corresponding file under $CACHE, if necessary.
1916*912701f9SAndroid Build Coastguard Worker# @param relative file path
1917*912701f9SAndroid Build Coastguard Workersub updateCacheFile {
1918*912701f9SAndroid Build Coastguard Worker    my $relFile = shift;
1919*912701f9SAndroid Build Coastguard Worker
1920*912701f9SAndroid Build Coastguard Worker    if (! -e "$CACHE/$relFile" ||
1921*912701f9SAndroid Build Coastguard Worker        (-M "$CACHE/$relFile" > -M "$CVSROOT/$relFile")) {
1922*912701f9SAndroid Build Coastguard Worker        if (!$UPDATE_COUNT) {
1923*912701f9SAndroid Build Coastguard Worker            print "<HR>Updating cache...";
1924*912701f9SAndroid Build Coastguard Worker	    if(! -e "$CACHE/$relFile") {
1925*912701f9SAndroid Build Coastguard Worker		debugOut ( " because $CACHE/$relFile was not cached.." ) if ($DEBUG);
1926*912701f9SAndroid Build Coastguard Worker	    } else {
1927*912701f9SAndroid Build Coastguard Worker		debugOut ( " because $relFile was updated.." ) if ($DEBUG);
1928*912701f9SAndroid Build Coastguard Worker	    }
1929*912701f9SAndroid Build Coastguard Worker        } elsif ($UPDATE_COUNT % 25 == 0) {
1930*912701f9SAndroid Build Coastguard Worker	    print " $UPDATE_COUNT...";
1931*912701f9SAndroid Build Coastguard Worker	}
1932*912701f9SAndroid Build Coastguard Worker        ++$UPDATE_COUNT;
1933*912701f9SAndroid Build Coastguard Worker	if ($relFile =~ m|/attic/|i) {
1934*912701f9SAndroid Build Coastguard Worker	    ++$UPDATE_ATTIC_COUNT;
1935*912701f9SAndroid Build Coastguard Worker	} else {
1936*912701f9SAndroid Build Coastguard Worker	    ++$UPDATE_NONATTIC_COUNT;
1937*912701f9SAndroid Build Coastguard Worker	}
1938*912701f9SAndroid Build Coastguard Worker	my $f = "$CACHE/$relFile";
1939*912701f9SAndroid Build Coastguard Worker	command("rlog $CVSROOT/$relFile > $f", $f);
1940*912701f9SAndroid Build Coastguard Worker	my $size = -s $f;
1941*912701f9SAndroid Build Coastguard Worker	if ($size <= 0) {
1942*912701f9SAndroid Build Coastguard Worker	    print " <B>{Fatal Error: rlog of $relFile failed}</B> ";
1943*912701f9SAndroid Build Coastguard Worker	    unlink($f);
1944*912701f9SAndroid Build Coastguard Worker	}
1945*912701f9SAndroid Build Coastguard Worker	command("touch -r $CVSROOT/$relFile $f");
1946*912701f9SAndroid Build Coastguard Worker    }
1947*912701f9SAndroid Build Coastguard Worker}
1948*912701f9SAndroid Build Coastguard Worker
1949*912701f9SAndroid Build Coastguard Worker######################################################################
1950*912701f9SAndroid Build Coastguard Worker# instaCache
1951*912701f9SAndroid Build Coastguard Worker######################################################################
1952*912701f9SAndroid Build Coastguard Worker
1953*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
1954*912701f9SAndroid Build Coastguard Worker# Lookup an ID in the instaCache, and return the diffs stored
1955*912701f9SAndroid Build Coastguard Worker# there.  If there is no entry for the ID, then return the
1956*912701f9SAndroid Build Coastguard Worker# empty string.  The ID will be suffixed with 'a' if the
1957*912701f9SAndroid Build Coastguard Worker# Attic is included.
1958*912701f9SAndroid Build Coastguard Workersub instaGet {
1959*912701f9SAndroid Build Coastguard Worker    my $id = shift;
1960*912701f9SAndroid Build Coastguard Worker    my $diffs;
1961*912701f9SAndroid Build Coastguard Worker    my $dir = $QUERY->param('include_attic') ? $INSTA_ATTIC : $INSTA;
1962*912701f9SAndroid Build Coastguard Worker    my $file = "$dir/$id";
1963*912701f9SAndroid Build Coastguard Worker    if (-e $file) {
1964*912701f9SAndroid Build Coastguard Worker	if (open(IN, $file)) {
1965*912701f9SAndroid Build Coastguard Worker	    while (<IN>) { $diffs .= $_; }
1966*912701f9SAndroid Build Coastguard Worker	    close(IN);
1967*912701f9SAndroid Build Coastguard Worker	}
1968*912701f9SAndroid Build Coastguard Worker    }
1969*912701f9SAndroid Build Coastguard Worker    return $diffs;
1970*912701f9SAndroid Build Coastguard Worker}
1971*912701f9SAndroid Build Coastguard Worker
1972*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
1973*912701f9SAndroid Build Coastguard Worker# Store diffs for the given ID in the instaCache.  The ID will be
1974*912701f9SAndroid Build Coastguard Worker# suffixed with 'a' if the Attic is included.
1975*912701f9SAndroid Build Coastguard Workersub instaPut {
1976*912701f9SAndroid Build Coastguard Worker    my $id = shift;
1977*912701f9SAndroid Build Coastguard Worker    my $diffs = shift;
1978*912701f9SAndroid Build Coastguard Worker    my $dir = $QUERY->param('include_attic') ? $INSTA_ATTIC : $INSTA;
1979*912701f9SAndroid Build Coastguard Worker    my $file = "$dir/$id";
1980*912701f9SAndroid Build Coastguard Worker    open(IN, ">$file") or return;
1981*912701f9SAndroid Build Coastguard Worker    print IN $diffs;
1982*912701f9SAndroid Build Coastguard Worker    close(IN);
1983*912701f9SAndroid Build Coastguard Worker}
1984*912701f9SAndroid Build Coastguard Worker
1985*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
1986*912701f9SAndroid Build Coastguard Worker# Reset the instaCache by deleting all entries.  We need
1987*912701f9SAndroid Build Coastguard Worker# to do this whenever the main cache is invalidated.
1988*912701f9SAndroid Build Coastguard Worker# Param: if true, then force reset of all instaCaches.
1989*912701f9SAndroid Build Coastguard Worker# Otherwise do a smart reset based on the update counts.
1990*912701f9SAndroid Build Coastguard Workersub resetInstaCache {
1991*912701f9SAndroid Build Coastguard Worker    if (shift) {
1992*912701f9SAndroid Build Coastguard Worker	command("rm -rf $INSTA"); # Recursive
1993*912701f9SAndroid Build Coastguard Worker	return;
1994*912701f9SAndroid Build Coastguard Worker    }
1995*912701f9SAndroid Build Coastguard Worker
1996*912701f9SAndroid Build Coastguard Worker    # If there have been changes to non-Attic files, we
1997*912701f9SAndroid Build Coastguard Worker    # have to reset everything.
1998*912701f9SAndroid Build Coastguard Worker    if ($UPDATE_NONATTIC_COUNT) {
1999*912701f9SAndroid Build Coastguard Worker	# The following will fail with:
2000*912701f9SAndroid Build Coastguard Worker	# rm: cannot remove `/tmp/icu-grepj.cache/insta/Attic': Is a directory
2001*912701f9SAndroid Build Coastguard Worker	#command("rm -f $INSTA/*") if (-d $INSTA);
2002*912701f9SAndroid Build Coastguard Worker	command("find $INSTA -type f -maxdepth 1 -exec rm {} \\;")
2003*912701f9SAndroid Build Coastguard Worker	    if (-d $INSTA);
2004*912701f9SAndroid Build Coastguard Worker    } else {
2005*912701f9SAndroid Build Coastguard Worker	# Otherwise just clear the attic instaCache
2006*912701f9SAndroid Build Coastguard Worker	command("rm -f $INSTA_ATTIC/*") if (-d $INSTA_ATTIC);
2007*912701f9SAndroid Build Coastguard Worker    }
2008*912701f9SAndroid Build Coastguard Worker}
2009*912701f9SAndroid Build Coastguard Worker
2010*912701f9SAndroid Build Coastguard Worker######################################################################
2011*912701f9SAndroid Build Coastguard Worker# CVS Utilities
2012*912701f9SAndroid Build Coastguard Worker######################################################################
2013*912701f9SAndroid Build Coastguard Worker
2014*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
2015*912701f9SAndroid Build Coastguard Worker# Get the date corresponding to the revision 1.1 in the
2016*912701f9SAndroid Build Coastguard Worker# given rlog output.  We use this as the "creation date" for the
2017*912701f9SAndroid Build Coastguard Worker# corresponding CVS file.
2018*912701f9SAndroid Build Coastguard Worker# @param absolute rlog output file path (in the cache)
2019*912701f9SAndroid Build Coastguard Worker# @return date string of the form "2002/08/23 23:21:38"
2020*912701f9SAndroid Build Coastguard Workersub getRev11Date {
2021*912701f9SAndroid Build Coastguard Worker    my $file = shift;
2022*912701f9SAndroid Build Coastguard Worker
2023*912701f9SAndroid Build Coastguard Worker    # Parse the rlog file.  Return the date line for 1.1
2024*912701f9SAndroid Build Coastguard Worker    open(IN, $file);
2025*912701f9SAndroid Build Coastguard Worker    while (<IN>) {
2026*912701f9SAndroid Build Coastguard Worker	if (/^-{20,}$/) {
2027*912701f9SAndroid Build Coastguard Worker	    $_ = <IN>;
2028*912701f9SAndroid Build Coastguard Worker	    if (/revision 1.1$/) {
2029*912701f9SAndroid Build Coastguard Worker		$_ = <IN>;
2030*912701f9SAndroid Build Coastguard Worker		if (/^date: (.+?);/) {
2031*912701f9SAndroid Build Coastguard Worker		    return $1;
2032*912701f9SAndroid Build Coastguard Worker		}
2033*912701f9SAndroid Build Coastguard Worker	    }
2034*912701f9SAndroid Build Coastguard Worker	}
2035*912701f9SAndroid Build Coastguard Worker    }
2036*912701f9SAndroid Build Coastguard Worker    close(IN);
2037*912701f9SAndroid Build Coastguard Worker
2038*912701f9SAndroid Build Coastguard Worker    ''; # Parse failure - should never happen
2039*912701f9SAndroid Build Coastguard Worker}
2040*912701f9SAndroid Build Coastguard Worker
2041*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
2042*912701f9SAndroid Build Coastguard Worker# Given a ,v file, find the revisions containing the
2043*912701f9SAndroid Build Coastguard Worker# jitterbug ID change.  Return an array of hash refs.
2044*912701f9SAndroid Build Coastguard Worker# Newest revision is first, that is, it is $result[0].
2045*912701f9SAndroid Build Coastguard Worker# Each hash has:
2046*912701f9SAndroid Build Coastguard Worker#   new (revision#)
2047*912701f9SAndroid Build Coastguard Worker#   old (revision#)
2048*912701f9SAndroid Build Coastguard Worker#   date
2049*912701f9SAndroid Build Coastguard Worker#   author
2050*912701f9SAndroid Build Coastguard Worker#   comment
2051*912701f9SAndroid Build Coastguard Worker# If the very first revision is labeled with the jitterbug
2052*912701f9SAndroid Build Coastguard Worker# $ID, then {old} will be $BASE_REV.
2053*912701f9SAndroid Build Coastguard Worker#
2054*912701f9SAndroid Build Coastguard Workersub findRevisions {
2055*912701f9SAndroid Build Coastguard Worker    my $file = shift;
2056*912701f9SAndroid Build Coastguard Worker    my $pat = shift;
2057*912701f9SAndroid Build Coastguard Worker    my @result;
2058*912701f9SAndroid Build Coastguard Worker
2059*912701f9SAndroid Build Coastguard Worker    # rlog output:
2060*912701f9SAndroid Build Coastguard Worker    #|revision 1.3
2061*912701f9SAndroid Build Coastguard Worker    #|date: 1999/10/14 22:14:04;  author: schererm;  state: Exp;  lines: +4 -2
2062*912701f9SAndroid Build Coastguard Worker    #|jitterbug 14: echo off now and use the Release versions of the tools
2063*912701f9SAndroid Build Coastguard Worker    #|----------------------------
2064*912701f9SAndroid Build Coastguard Worker    #|revision 1.2
2065*912701f9SAndroid Build Coastguard Worker    #|date: 1999/10/13 01:10:24;  author: schererm;  state: Exp;  lines: +9 -6
2066*912701f9SAndroid Build Coastguard Worker    #|jitterbug 15: windows: genrb puts .res files into the current directory
2067*912701f9SAndroid Build Coastguard Worker    #|more text
2068*912701f9SAndroid Build Coastguard Worker    #|----------------------------
2069*912701f9SAndroid Build Coastguard Worker    #|revision 1.1
2070*912701f9SAndroid Build Coastguard Worker    #|date: 1999/10/12 21:50:30;  author: schererm;  state: Exp;
2071*912701f9SAndroid Build Coastguard Worker    #|jitterbug 14: Windows: create a batch file to make the /icu/data files
2072*912701f9SAndroid Build Coastguard Worker    #|=============================================================================
2073*912701f9SAndroid Build Coastguard Worker
2074*912701f9SAndroid Build Coastguard Worker    # We read our rlog info from the cache now
2075*912701f9SAndroid Build Coastguard Worker    my %log; # $log{<revision>} = <block of text>
2076*912701f9SAndroid Build Coastguard Worker    my $l=''; my $r='';
2077*912701f9SAndroid Build Coastguard Worker    open(IN, $file);
2078*912701f9SAndroid Build Coastguard Worker    while (<IN>) {
2079*912701f9SAndroid Build Coastguard Worker        if (/^-{20,}$/) {
2080*912701f9SAndroid Build Coastguard Worker	    $log{$r} = $l if ($r);
2081*912701f9SAndroid Build Coastguard Worker            $l = $r = '';
2082*912701f9SAndroid Build Coastguard Worker        } elsif ($r) {
2083*912701f9SAndroid Build Coastguard Worker            $l .= $_;
2084*912701f9SAndroid Build Coastguard Worker        } else {
2085*912701f9SAndroid Build Coastguard Worker	    if (/revision\s+(\S+)/) {
2086*912701f9SAndroid Build Coastguard Worker		$r = $1;
2087*912701f9SAndroid Build Coastguard Worker		die "Duplicate revision $r in $file" if (exists $log{$r});
2088*912701f9SAndroid Build Coastguard Worker	    }
2089*912701f9SAndroid Build Coastguard Worker	}
2090*912701f9SAndroid Build Coastguard Worker    }
2091*912701f9SAndroid Build Coastguard Worker    close(IN);
2092*912701f9SAndroid Build Coastguard Worker    $log{$r} = $l if ($r);
2093*912701f9SAndroid Build Coastguard Worker
2094*912701f9SAndroid Build Coastguard Worker    for $r (sort cmprevs keys %log) {
2095*912701f9SAndroid Build Coastguard Worker        local $_ = $log{$r};
2096*912701f9SAndroid Build Coastguard Worker
2097*912701f9SAndroid Build Coastguard Worker        # (2 of 3 REGEXPS) SEE ALSO other regexps; keep them in sync
2098*912701f9SAndroid Build Coastguard Worker        if (/^\s*(?:$CVS_MSG_KW)\s*$pat\b/im) {
2099*912701f9SAndroid Build Coastguard Worker            my %h;
2100*912701f9SAndroid Build Coastguard Worker            $h{new} = $r;
2101*912701f9SAndroid Build Coastguard Worker	    my $rold = decRev($r);
2102*912701f9SAndroid Build Coastguard Worker            if (exists $log{$rold}) {
2103*912701f9SAndroid Build Coastguard Worker                $h{old} = $rold;
2104*912701f9SAndroid Build Coastguard Worker            } else {
2105*912701f9SAndroid Build Coastguard Worker                $h{old} = $BASE_REV;
2106*912701f9SAndroid Build Coastguard Worker            }
2107*912701f9SAndroid Build Coastguard Worker            if (/date:\s*(.+?);/) {
2108*912701f9SAndroid Build Coastguard Worker                $h{date} = $1;
2109*912701f9SAndroid Build Coastguard Worker            }
2110*912701f9SAndroid Build Coastguard Worker            if (/author:\s*(.+?);/) {
2111*912701f9SAndroid Build Coastguard Worker                $h{author} = $1;
2112*912701f9SAndroid Build Coastguard Worker            }
2113*912701f9SAndroid Build Coastguard Worker
2114*912701f9SAndroid Build Coastguard Worker            # (3 of 3 REGEXPS) SEE ALSO other regexps; keep them in sync
2115*912701f9SAndroid Build Coastguard Worker            if (/^\s*(?:$CVS_MSG_KW)\s*$pat\b(.*)/ism) {
2116*912701f9SAndroid Build Coastguard Worker                local $_ = $1;
2117*912701f9SAndroid Build Coastguard Worker                s/^\s*:?\s*//;
2118*912701f9SAndroid Build Coastguard Worker                s/\s*----+\s*$//;
2119*912701f9SAndroid Build Coastguard Worker                s/\s*====+\s*$//;
2120*912701f9SAndroid Build Coastguard Worker                s/\s*\n+\s*/ /g;
2121*912701f9SAndroid Build Coastguard Worker                $h{comment} = $_;
2122*912701f9SAndroid Build Coastguard Worker            }
2123*912701f9SAndroid Build Coastguard Worker            push @result, \%h;
2124*912701f9SAndroid Build Coastguard Worker        }
2125*912701f9SAndroid Build Coastguard Worker    }
2126*912701f9SAndroid Build Coastguard Worker
2127*912701f9SAndroid Build Coastguard Worker    @result;
2128*912701f9SAndroid Build Coastguard Worker}
2129*912701f9SAndroid Build Coastguard Worker
2130*912701f9SAndroid Build Coastguard Worker######################################################################
2131*912701f9SAndroid Build Coastguard Worker# CVS tag parsing
2132*912701f9SAndroid Build Coastguard Worker######################################################################
2133*912701f9SAndroid Build Coastguard Worker
2134*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
2135*912701f9SAndroid Build Coastguard Worker# Given a tag name like this: "2.1", expand it to "release-2-1".
2136*912701f9SAndroid Build Coastguard Worker# Convert 'head' (case insens.) to 'HEAD'.
2137*912701f9SAndroid Build Coastguard Worker# Otherwise leave it alone.
2138*912701f9SAndroid Build Coastguard Workersub expandTag {
2139*912701f9SAndroid Build Coastguard Worker    local $_ = shift;
2140*912701f9SAndroid Build Coastguard Worker    s/^\s+//;
2141*912701f9SAndroid Build Coastguard Worker    s/\s+$//;
2142*912701f9SAndroid Build Coastguard Worker    if (/^\d+(\.\d+)/) {
2143*912701f9SAndroid Build Coastguard Worker	s|\.|-|g;
2144*912701f9SAndroid Build Coastguard Worker	$_ = "release-" . $_;
2145*912701f9SAndroid Build Coastguard Worker    } elsif (/^head$/i) {
2146*912701f9SAndroid Build Coastguard Worker	$_ = 'HEAD';
2147*912701f9SAndroid Build Coastguard Worker    }
2148*912701f9SAndroid Build Coastguard Worker    $_;
2149*912701f9SAndroid Build Coastguard Worker}
2150*912701f9SAndroid Build Coastguard Worker
2151*912701f9SAndroid Build Coastguard Worker#---------------------------------------------------------------------
2152*912701f9SAndroid Build Coastguard Worker# Given a tag name like this: "release-1-5-0-d03", return a normalized
2153*912701f9SAndroid Build Coastguard Worker# release number.  The release number in this case would be 1500003.
2154*912701f9SAndroid Build Coastguard Worker# The final release (no 'd') "release-1-5-0" is 1500099; that is, it
2155*912701f9SAndroid Build Coastguard Worker# behaves like "d99".  Up to 5 digits are allowed prior to the 'd'
2156*912701f9SAndroid Build Coastguard Worker# number (if any).  This should suffice; in practice we use only 4
2157*912701f9SAndroid Build Coastguard Worker# (e.g., "release-1-4-1-2").  Assume all numbers are single digits
2158*912701f9SAndroid Build Coastguard Worker# except for the 'd' number.  The tag must start with /release-?/.
2159*912701f9SAndroid Build Coastguard Worker# All digits must be separated by '-', except the '-' before the 'd03'
2160*912701f9SAndroid Build Coastguard Worker# may be omitted.  One or two digits are allowed after the 'd'.
2161*912701f9SAndroid Build Coastguard Worker# Trailing text after an otherwise valid tag, with no 'd', is treated
2162*912701f9SAndroid Build Coastguard Worker# as a 'd' of 00, e.g., "release-2-0-2s-branch".
2163*912701f9SAndroid Build Coastguard Worker#
2164*912701f9SAndroid Build Coastguard Worker# @param a tag string, like "release-1-5-0-d03"
2165*912701f9SAndroid Build Coastguard Worker# @param a release integer, that can be compared numerically,
2166*912701f9SAndroid Build Coastguard Worker#        like 1500003, or if the tag can't be parsed.
2167*912701f9SAndroid Build Coastguard Workersub tagToRelease {
2168*912701f9SAndroid Build Coastguard Worker    local $_ = shift;
2169*912701f9SAndroid Build Coastguard Worker    if (s/^release-?//i) {
2170*912701f9SAndroid Build Coastguard Worker	my @a;
2171*912701f9SAndroid Build Coastguard Worker	my $d = -1;
2172*912701f9SAndroid Build Coastguard Worker	for (;;) {
2173*912701f9SAndroid Build Coastguard Worker	    if (s/^(\d)-// ||
2174*912701f9SAndroid Build Coastguard Worker		s/^(\d)$// ||
2175*912701f9SAndroid Build Coastguard Worker		s/(\d)(\D)/$2/) { # e.g., "release-1-4-2d01"
2176*912701f9SAndroid Build Coastguard Worker		push @a, $1;
2177*912701f9SAndroid Build Coastguard Worker	    } elsif ($d<0 && s/^d(\d{1,2})$//) {
2178*912701f9SAndroid Build Coastguard Worker		$d = $1;
2179*912701f9SAndroid Build Coastguard Worker	    } else {
2180*912701f9SAndroid Build Coastguard Worker		last;
2181*912701f9SAndroid Build Coastguard Worker	    }
2182*912701f9SAndroid Build Coastguard Worker	}
2183*912701f9SAndroid Build Coastguard Worker	# If we have some trailing non-standard text, and no 'd',
2184*912701f9SAndroid Build Coastguard Worker	# then treat it as a 'd' of 00.
2185*912701f9SAndroid Build Coastguard Worker	if ($_ && $d<0 && (scalar @a)>0) {
2186*912701f9SAndroid Build Coastguard Worker	    $_ = '';
2187*912701f9SAndroid Build Coastguard Worker	    $d = 0;
2188*912701f9SAndroid Build Coastguard Worker	}
2189*912701f9SAndroid Build Coastguard Worker	if (!$_) {
2190*912701f9SAndroid Build Coastguard Worker	    push @a, (0, 0, 0, 0); # Pad with 0's
2191*912701f9SAndroid Build Coastguard Worker	    @a = @a[0..4];
2192*912701f9SAndroid Build Coastguard Worker	    return join('',@a) . sprintf("%02d", $d<0?99:$d);
2193*912701f9SAndroid Build Coastguard Worker	}
2194*912701f9SAndroid Build Coastguard Worker    }
2195*912701f9SAndroid Build Coastguard Worker    0; # parse failure
2196*912701f9SAndroid Build Coastguard Worker}
2197*912701f9SAndroid Build Coastguard Worker
2198*912701f9SAndroid Build Coastguard Worker######################################################################
2199*912701f9SAndroid Build Coastguard Worker# Utilities
2200*912701f9SAndroid Build Coastguard Worker######################################################################
2201*912701f9SAndroid Build Coastguard Worker
2202*912701f9SAndroid Build Coastguard Worker# Output a string in debug mode
2203*912701f9SAndroid Build Coastguard Worker# Usage:  debugOut("string") if ($DEBUG);
2204*912701f9SAndroid Build Coastguard Workersub debugOut {
2205*912701f9SAndroid Build Coastguard Worker    print "<P><FONT SIZE=-1><B>", join(" ", @_), "</B></FONT></P>";
2206*912701f9SAndroid Build Coastguard Worker}
2207*912701f9SAndroid Build Coastguard Worker
2208*912701f9SAndroid Build Coastguard Worker#|# Set or change a GET param of a URL.  If the param exists,
2209*912701f9SAndroid Build Coastguard Worker#|# change it.  If it doesn't, add it.
2210*912701f9SAndroid Build Coastguard Worker#|# @param a URL, with or without trailing parameters
2211*912701f9SAndroid Build Coastguard Worker#|# @param a parameter string of the form a=b, a=, or a
2212*912701f9SAndroid Build Coastguard Worker#|# @param modified URL
2213*912701f9SAndroid Build Coastguard Worker#|sub urlParam {
2214*912701f9SAndroid Build Coastguard Worker#|    my $url = shift;
2215*912701f9SAndroid Build Coastguard Worker#|    my $param = shift;
2216*912701f9SAndroid Build Coastguard Worker#|    my $key = $param;
2217*912701f9SAndroid Build Coastguard Worker#|    $key =~ s/=.*//;
2218*912701f9SAndroid Build Coastguard Worker#|    if ($url =~ s/([\?&;])$key=[^&;]*/$1$param/ ||
2219*912701f9SAndroid Build Coastguard Worker#|	$url =~ s/([\?&;])$key$/$1$param/) {
2220*912701f9SAndroid Build Coastguard Worker#|	return $url;
2221*912701f9SAndroid Build Coastguard Worker#|    }
2222*912701f9SAndroid Build Coastguard Worker#|    $url . ($url =~ /\?/ ? '&' : '?') . $param;
2223*912701f9SAndroid Build Coastguard Worker#|}
2224*912701f9SAndroid Build Coastguard Worker
2225*912701f9SAndroid Build Coastguard Worker# Append the given path-info to the given URL
2226*912701f9SAndroid Build Coastguard Worker# Param: URL, possibly including '?xxx=yyy' params, NOT ending in '/'
2227*912701f9SAndroid Build Coastguard Worker# Param: Path info, MUST start with '/'
2228*912701f9SAndroid Build Coastguard Workersub urlPathInfo {
2229*912701f9SAndroid Build Coastguard Worker    my $url = shift;
2230*912701f9SAndroid Build Coastguard Worker    my $pi = shift;
2231*912701f9SAndroid Build Coastguard Worker    if ($url =~ s|\?|$pi?|) {
2232*912701f9SAndroid Build Coastguard Worker    } else {
2233*912701f9SAndroid Build Coastguard Worker	$url .= $pi;
2234*912701f9SAndroid Build Coastguard Worker    }
2235*912701f9SAndroid Build Coastguard Worker    $url;
2236*912701f9SAndroid Build Coastguard Worker}
2237*912701f9SAndroid Build Coastguard Worker
2238*912701f9SAndroid Build Coastguard Worker# Parse the module params given by the user
2239*912701f9SAndroid Build Coastguard Worker# @param ref to array to receive list of modules.  Prior contents will
2240*912701f9SAndroid Build Coastguard Worker#        be lost.
2241*912701f9SAndroid Build Coastguard Worker# @return 1 on success, or 0 if bad or no modules were seen.
2242*912701f9SAndroid Build Coastguard Workersub parseMod {
2243*912701f9SAndroid Build Coastguard Worker    my $m = shift; # ref to array
2244*912701f9SAndroid Build Coastguard Worker    my @badMod;
2245*912701f9SAndroid Build Coastguard Worker
2246*912701f9SAndroid Build Coastguard Worker    my $mod = $QUERY->param('mod') || $DEFAULT_MOD;
2247*912701f9SAndroid Build Coastguard Worker    $mod =~ s|^\s+||;
2248*912701f9SAndroid Build Coastguard Worker    $mod =~ s|\s+$||;
2249*912701f9SAndroid Build Coastguard Worker    $mod =~ s|\s+| |g;
2250*912701f9SAndroid Build Coastguard Worker    @$m = split(' ', $mod);
2251*912701f9SAndroid Build Coastguard Worker    foreach (@$m) {
2252*912701f9SAndroid Build Coastguard Worker	# !Modify element of @m in place!
2253*912701f9SAndroid Build Coastguard Worker	$_ = $MOD_ABBREV{$_} if (exists $MOD_ABBREV{$_});
2254*912701f9SAndroid Build Coastguard Worker	push @badMod, $_ if (! -d "$CVSROOT/$_");
2255*912701f9SAndroid Build Coastguard Worker    }
2256*912701f9SAndroid Build Coastguard Worker    if (@badMod) {
2257*912701f9SAndroid Build Coastguard Worker	print "Invalid modules: <CODE>",
2258*912701f9SAndroid Build Coastguard Worker	      join(" ", @badMod), "</CODE>";
2259*912701f9SAndroid Build Coastguard Worker	print "<BR>Did you try the full module name (e.g. \"icu/charset\")?  Only some modules can be abbreviated: <CODE>", join(" ", sort keys %MOD_ABBREV), "</CODE>.";
2260*912701f9SAndroid Build Coastguard Worker	return 0;
2261*912701f9SAndroid Build Coastguard Worker    }
2262*912701f9SAndroid Build Coastguard Worker    1;
2263*912701f9SAndroid Build Coastguard Worker}
2264*912701f9SAndroid Build Coastguard Worker
2265*912701f9SAndroid Build Coastguard Worker# Return the HTML for a link to the given jitterbug.
2266*912701f9SAndroid Build Coastguard Worker# @param user
2267*912701f9SAndroid Build Coastguard Worker# @param bug ID
2268*912701f9SAndroid Build Coastguard Worker# @param OPTIONAL target
2269*912701f9SAndroid Build Coastguard Worker# @return HTML for A tag
2270*912701f9SAndroid Build Coastguard Workersub jitterbugLink {
2271*912701f9SAndroid Build Coastguard Worker    my $user = shift;
2272*912701f9SAndroid Build Coastguard Worker    my $id = shift;
2273*912701f9SAndroid Build Coastguard Worker    my $targ = shift || '';
2274*912701f9SAndroid Build Coastguard Worker    if ($id eq $NO_JITTERBUG) {
2275*912701f9SAndroid Build Coastguard Worker	return "<EM>no jitterbug</EM>";
2276*912701f9SAndroid Build Coastguard Worker    }
2277*912701f9SAndroid Build Coastguard Worker    $targ = " target=\"$targ\"" if ($targ);
2278*912701f9SAndroid Build Coastguard Worker    "<A href=\"" . jitterbugURL($user, $id) . "\"$targ>$id</A>";
2279*912701f9SAndroid Build Coastguard Worker}
2280*912701f9SAndroid Build Coastguard Worker
2281*912701f9SAndroid Build Coastguard Worker# Return the HTML for a link to the WebCVS log of a file.
2282*912701f9SAndroid Build Coastguard Worker# @param relative path (from $CVSROOT) to file, optionally with
2283*912701f9SAndroid Build Coastguard Worker#        trailing ",v"
2284*912701f9SAndroid Build Coastguard Worker# @param OPTIONAL target
2285*912701f9SAndroid Build Coastguard Worker# @return HTML for A tag
2286*912701f9SAndroid Build Coastguard Workersub logLink {
2287*912701f9SAndroid Build Coastguard Worker    my $relFile = shift;
2288*912701f9SAndroid Build Coastguard Worker    my $targ = shift;
2289*912701f9SAndroid Build Coastguard Worker    $targ = " target=\"$targ\"" if ($targ);
2290*912701f9SAndroid Build Coastguard Worker    $relFile =~ s/,v$//;
2291*912701f9SAndroid Build Coastguard Worker    "<A href=\"$LOG_URL/$relFile\"$targ>$relFile</A>";
2292*912701f9SAndroid Build Coastguard Worker}
2293*912701f9SAndroid Build Coastguard Worker
2294*912701f9SAndroid Build Coastguard Worker# Return the HTML for a link to the WebCVS "tag" page.  This will
2295*912701f9SAndroid Build Coastguard Worker# just be the page for the root of the given module, with the given
2296*912701f9SAndroid Build Coastguard Worker# tag selected.
2297*912701f9SAndroid Build Coastguard Worker# @param tag
2298*912701f9SAndroid Build Coastguard Worker# @param module, e.g., "icu/icu"
2299*912701f9SAndroid Build Coastguard Worker# @param OPTIONAL target
2300*912701f9SAndroid Build Coastguard Worker# @return HTML for A tag
2301*912701f9SAndroid Build Coastguard Workersub tagLink {
2302*912701f9SAndroid Build Coastguard Worker    my $tag = shift;
2303*912701f9SAndroid Build Coastguard Worker    my $mod = shift;
2304*912701f9SAndroid Build Coastguard Worker    my $targ = shift;
2305*912701f9SAndroid Build Coastguard Worker    $targ = " target=\"$targ\"" if ($targ);
2306*912701f9SAndroid Build Coastguard Worker    "<A href=\"$LOG_URL/$mod/?only_with_tag=$tag\"$targ>$tag</A>";
2307*912701f9SAndroid Build Coastguard Worker}
2308*912701f9SAndroid Build Coastguard Worker
2309*912701f9SAndroid Build Coastguard Worker# Emit an error (in HTML) about failing to parse a line.
2310*912701f9SAndroid Build Coastguard Worker# @param what can't be parsed, e.g., 'revision'
2311*912701f9SAndroid Build Coastguard Worker# @param relative file path, e.g., 'icu/icu/readme.html'
2312*912701f9SAndroid Build Coastguard Worker# @param the line that can't be parsed
2313*912701f9SAndroid Build Coastguard Worker# @param revision
2314*912701f9SAndroid Build Coastguard Workersub cantParse {
2315*912701f9SAndroid Build Coastguard Worker    my $what = shift;
2316*912701f9SAndroid Build Coastguard Worker    my $relFile = shift;
2317*912701f9SAndroid Build Coastguard Worker    my $line = shift;
2318*912701f9SAndroid Build Coastguard Worker    my $rev = shift;
2319*912701f9SAndroid Build Coastguard Worker    $rev = ', '.$rev if ($rev);
2320*912701f9SAndroid Build Coastguard Worker    print "<BR>Error: Can't parse $what in "
2321*912701f9SAndroid Build Coastguard Worker	, logLink($relFile, 'grepj_2'), "$rev:<BR>\n";
2322*912701f9SAndroid Build Coastguard Worker    print "<CODE>$line</CODE><BR>";
2323*912701f9SAndroid Build Coastguard Worker}
2324*912701f9SAndroid Build Coastguard Worker
2325*912701f9SAndroid Build Coastguard Worker# Print the given string(s) to STDOUT and also return the
2326*912701f9SAndroid Build Coastguard Worker# output as a single string.
2327*912701f9SAndroid Build Coastguard Workersub out {
2328*912701f9SAndroid Build Coastguard Worker    local $_ = join('', @_);
2329*912701f9SAndroid Build Coastguard Worker    print;
2330*912701f9SAndroid Build Coastguard Worker    $_;
2331*912701f9SAndroid Build Coastguard Worker}
2332*912701f9SAndroid Build Coastguard Worker
2333*912701f9SAndroid Build Coastguard Worker# Given an array of numbers, return a sorted unique list.
2334*912701f9SAndroid Build Coastguard Workersub sortedUniqueInts {
2335*912701f9SAndroid Build Coastguard Worker    my @a = @_;
2336*912701f9SAndroid Build Coastguard Worker    my %a;
2337*912701f9SAndroid Build Coastguard Worker    foreach (@a) {
2338*912701f9SAndroid Build Coastguard Worker	s/^0+(\d)/$1/;
2339*912701f9SAndroid Build Coastguard Worker	$a{$_} = 1;
2340*912701f9SAndroid Build Coastguard Worker    }
2341*912701f9SAndroid Build Coastguard Worker    sort {$a<=>$b} keys %a;
2342*912701f9SAndroid Build Coastguard Worker}
2343*912701f9SAndroid Build Coastguard Worker
2344*912701f9SAndroid Build Coastguard Worker# Convert a revision number to a branch number.
2345*912701f9SAndroid Build Coastguard Worker# Generally this means dropping the last dotted integer, but if
2346*912701f9SAndroid Build Coastguard Worker# the last two dotted integers are 0.n, then the 0. must be dropped:
2347*912701f9SAndroid Build Coastguard Worker# 1.14.0.2 => 1.14.2.  (This is a magic CVS revision representing
2348*912701f9SAndroid Build Coastguard Worker# the branch.)  Also 'HEAD' is branch '1'.
2349*912701f9SAndroid Build Coastguard Workersub revToBranch {
2350*912701f9SAndroid Build Coastguard Worker    local $_ = shift;
2351*912701f9SAndroid Build Coastguard Worker    s/\.0(\.\d+)$/$1/ || s/\.\d+$// || s/HEAD/1/;
2352*912701f9SAndroid Build Coastguard Worker    $_;
2353*912701f9SAndroid Build Coastguard Worker}
2354*912701f9SAndroid Build Coastguard Worker
2355*912701f9SAndroid Build Coastguard Worker# Given two CVS revisions, return a sequence of revisions traversing
2356*912701f9SAndroid Build Coastguard Worker# the logical path between them.
2357*912701f9SAndroid Build Coastguard Worker#
2358*912701f9SAndroid Build Coastguard Worker# WARNING!: The revisions must actually have a path between them.  If
2359*912701f9SAndroid Build Coastguard Worker# you pass in 1.4 => 1.2 or 1.5 => 1.2.2.4 the sub will run
2360*912701f9SAndroid Build Coastguard Worker# infinitely.
2361*912701f9SAndroid Build Coastguard Worker#
2362*912701f9SAndroid Build Coastguard Worker# @param low revision, e.g. 1.2 or 1.2.0.4
2363*912701f9SAndroid Build Coastguard Worker# @param high revision, e.g., 1.5.2.3
2364*912701f9SAndroid Build Coastguard Worker# @return an array of revisions from low to high inclusive
2365*912701f9SAndroid Build Coastguard Workersub traverseRevisions {
2366*912701f9SAndroid Build Coastguard Worker    my $rev_lo = shift;
2367*912701f9SAndroid Build Coastguard Worker    my $rev_hi = shift;
2368*912701f9SAndroid Build Coastguard Worker    my @a = split(/\./, $rev_lo);
2369*912701f9SAndroid Build Coastguard Worker    my @limit = split(/\./, $rev_hi);
2370*912701f9SAndroid Build Coastguard Worker    my @list;
2371*912701f9SAndroid Build Coastguard Worker    for (;;) {
2372*912701f9SAndroid Build Coastguard Worker	push @list, join('.', @a);
2373*912701f9SAndroid Build Coastguard Worker	if (@a == @limit) {
2374*912701f9SAndroid Build Coastguard Worker	    last if ($a[-1] == $limit[-1]);
2375*912701f9SAndroid Build Coastguard Worker	    # Fall through
2376*912701f9SAndroid Build Coastguard Worker	} else {
2377*912701f9SAndroid Build Coastguard Worker	    my $a = join('.', @a);
2378*912701f9SAndroid Build Coastguard Worker	    if ($rev_hi =~ /^\Q$a\E\./) {
2379*912701f9SAndroid Build Coastguard Worker		push @a, $limit[@a];
2380*912701f9SAndroid Build Coastguard Worker		push @a, 1;
2381*912701f9SAndroid Build Coastguard Worker		next;
2382*912701f9SAndroid Build Coastguard Worker	    }
2383*912701f9SAndroid Build Coastguard Worker	    # Else fall through
2384*912701f9SAndroid Build Coastguard Worker	}
2385*912701f9SAndroid Build Coastguard Worker
2386*912701f9SAndroid Build Coastguard Worker	if ($a[-2] == 0) {
2387*912701f9SAndroid Build Coastguard Worker	    # Handle magic CVS revisions like 1.14.0.2
2388*912701f9SAndroid Build Coastguard Worker	    $a[-2] = $a[-1];
2389*912701f9SAndroid Build Coastguard Worker	    $a[-1] = 1;
2390*912701f9SAndroid Build Coastguard Worker	} else {
2391*912701f9SAndroid Build Coastguard Worker	    $a[-1]++;
2392*912701f9SAndroid Build Coastguard Worker	}
2393*912701f9SAndroid Build Coastguard Worker    }
2394*912701f9SAndroid Build Coastguard Worker    @list;
2395*912701f9SAndroid Build Coastguard Worker}
2396*912701f9SAndroid Build Coastguard Worker
2397*912701f9SAndroid Build Coastguard Worker# Given a CVS numeric revision, increment it (increment last integer)
2398*912701f9SAndroid Build Coastguard Workersub incRev {
2399*912701f9SAndroid Build Coastguard Worker    local $_ = shift;
2400*912701f9SAndroid Build Coastguard Worker    if (/(\d+)$/) {
2401*912701f9SAndroid Build Coastguard Worker	my $i = $1 + 1;
2402*912701f9SAndroid Build Coastguard Worker	s/\d+$/$i/;
2403*912701f9SAndroid Build Coastguard Worker	return $_;
2404*912701f9SAndroid Build Coastguard Worker    }
2405*912701f9SAndroid Build Coastguard Worker    die "Can't increment $_";
2406*912701f9SAndroid Build Coastguard Worker}
2407*912701f9SAndroid Build Coastguard Worker
2408*912701f9SAndroid Build Coastguard Worker# Given a CVS numeric revisions, decrement it.  This handles
2409*912701f9SAndroid Build Coastguard Worker# branches.  If the resulting revision number goes to zero,
2410*912701f9SAndroid Build Coastguard Worker# return BASE_REV.  Does not handle magic revisions like 1.14.0.2.
2411*912701f9SAndroid Build Coastguard Worker# 1.3 => 1.2
2412*912701f9SAndroid Build Coastguard Worker# 1.3.2.1 => 1.3
2413*912701f9SAndroid Build Coastguard Worker# 1.3.2.2 => 1.3.2.1
2414*912701f9SAndroid Build Coastguard Workersub decRev {
2415*912701f9SAndroid Build Coastguard Worker    local $_ = shift;
2416*912701f9SAndroid Build Coastguard Worker    if (/(\d+)$/) {
2417*912701f9SAndroid Build Coastguard Worker	my $i = $1 - 1;
2418*912701f9SAndroid Build Coastguard Worker	if ($i >= 1) {
2419*912701f9SAndroid Build Coastguard Worker	    s/\d+$/$i/;
2420*912701f9SAndroid Build Coastguard Worker	} elsif (s/(^1\.\d+)\.2\.1$/$1/) {
2421*912701f9SAndroid Build Coastguard Worker	    # 1.3.2.1 => 1.3
2422*912701f9SAndroid Build Coastguard Worker	} else {
2423*912701f9SAndroid Build Coastguard Worker	    return $BASE_REV;
2424*912701f9SAndroid Build Coastguard Worker	}
2425*912701f9SAndroid Build Coastguard Worker	return $_;
2426*912701f9SAndroid Build Coastguard Worker    }
2427*912701f9SAndroid Build Coastguard Worker    die "Can't decrement $_";
2428*912701f9SAndroid Build Coastguard Worker}
2429*912701f9SAndroid Build Coastguard Worker
2430*912701f9SAndroid Build Coastguard Worker# Given a date string, in CVS format, like "2003/05/29 22:10:17",
2431*912701f9SAndroid Build Coastguard Worker# return the duration $NOW - x, in days.
2432*912701f9SAndroid Build Coastguard Workersub ageInDays {
2433*912701f9SAndroid Build Coastguard Worker    local $_ = shift;
2434*912701f9SAndroid Build Coastguard Worker    if (m|(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)|) {
2435*912701f9SAndroid Build Coastguard Worker	my ($y,$m,$d,$H,$M,$S) = ($1,$2-1,$3,$4,$5,$6);
2436*912701f9SAndroid Build Coastguard Worker	if ($y =~ /^\d\d$/) {
2437*912701f9SAndroid Build Coastguard Worker	    $y = 100*int($YEAR / 100) + $y;
2438*912701f9SAndroid Build Coastguard Worker	    $y -= 100 if ($y > $YEAR);
2439*912701f9SAndroid Build Coastguard Worker	}
2440*912701f9SAndroid Build Coastguard Worker	return ($NOW - timelocal_nocheck($S,$M,$H,$d,$m,$y)) / 86400.0;
2441*912701f9SAndroid Build Coastguard Worker    } else {
2442*912701f9SAndroid Build Coastguard Worker	die "Can't parse date $_\n";
2443*912701f9SAndroid Build Coastguard Worker    }
2444*912701f9SAndroid Build Coastguard Worker}
2445*912701f9SAndroid Build Coastguard Worker
2446*912701f9SAndroid Build Coastguard Worker# Filter for which files we care about that don't have jitterbugs.
2447*912701f9SAndroid Build Coastguard Worker# Our rule is that if the checkin is over a year old, we don't care
2448*912701f9SAndroid Build Coastguard Worker# about it.  We used to also require the revision to be 1.1 or 1.1.1.1
2449*912701f9SAndroid Build Coastguard Worker# to be ignored, but we dropped this.
2450*912701f9SAndroid Build Coastguard Workersub noJitterbugFilter {
2451*912701f9SAndroid Build Coastguard Worker    my $rev = shift;
2452*912701f9SAndroid Build Coastguard Worker    my $date = shift;
2453*912701f9SAndroid Build Coastguard Worker    #if ($rev eq '1.1' || $rev eq '1.1.1.1') {
2454*912701f9SAndroid Build Coastguard Worker	return ageInDays($date) <= 365.25;
2455*912701f9SAndroid Build Coastguard Worker    #}
2456*912701f9SAndroid Build Coastguard Worker    #1;
2457*912701f9SAndroid Build Coastguard Worker}
2458*912701f9SAndroid Build Coastguard Worker
2459*912701f9SAndroid Build Coastguard Worker# Execute a command, trapping errors.
2460*912701f9SAndroid Build Coastguard Worker# Options second arg: Path to a file to delete upon failure
2461*912701f9SAndroid Build Coastguard Workersub command {
2462*912701f9SAndroid Build Coastguard Worker    my $cmd = shift;
2463*912701f9SAndroid Build Coastguard Worker    my $fileToDeleteOnFailure = shift;
2464*912701f9SAndroid Build Coastguard Worker
2465*912701f9SAndroid Build Coastguard Worker    my $err = "$CACHE/grepj.stderr";
2466*912701f9SAndroid Build Coastguard Worker    my $status = system($cmd . " 2> $err");
2467*912701f9SAndroid Build Coastguard Worker    if ($status != 0) {
2468*912701f9SAndroid Build Coastguard Worker	unlink($fileToDeleteOnFailure) if defined($fileToDeleteOnFailure);
2469*912701f9SAndroid Build Coastguard Worker	print "<HR><B>Fatal Error: "
2470*912701f9SAndroid Build Coastguard Worker	    . "\"$cmd\" exited with value "
2471*912701f9SAndroid Build Coastguard Worker	    . ($status >> 8)
2472*912701f9SAndroid Build Coastguard Worker	    . " (signal " . ($status & 127) . ")"
2473*912701f9SAndroid Build Coastguard Worker	    . (($status & 128) ? " (core dumped)" : "")
2474*912701f9SAndroid Build Coastguard Worker	    . "<BR></B>";
2475*912701f9SAndroid Build Coastguard Worker	print "stderr:<BR>";
2476*912701f9SAndroid Build Coastguard Worker	if (open(IN, $err)) {
2477*912701f9SAndroid Build Coastguard Worker	    while (<IN>) {
2478*912701f9SAndroid Build Coastguard Worker		print $_, "<BR>";
2479*912701f9SAndroid Build Coastguard Worker	    }
2480*912701f9SAndroid Build Coastguard Worker	    close(IN);
2481*912701f9SAndroid Build Coastguard Worker	}
2482*912701f9SAndroid Build Coastguard Worker	croak "Couldn't execute \"$cmd\"";
2483*912701f9SAndroid Build Coastguard Worker    }
2484*912701f9SAndroid Build Coastguard Worker}
2485*912701f9SAndroid Build Coastguard Worker
2486*912701f9SAndroid Build Coastguard Worker#eof
2487