make/scripts/webrev.ksh

changeset 314
47ad81d343e8
child 616
168dd033604a
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/make/scripts/webrev.ksh	Tue Mar 01 11:54:05 2011 -0800
     1.3 @@ -0,0 +1,3182 @@
     1.4 +#!/bin/ksh -p
     1.5 +#
     1.6 +# CDDL HEADER START
     1.7 +#
     1.8 +# The contents of this file are subject to the terms of the
     1.9 +# Common Development and Distribution License (the "License").
    1.10 +# You may not use this file except in compliance with the License.
    1.11 +#
    1.12 +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
    1.13 +# or http://www.opensolaris.org/os/licensing.
    1.14 +# See the License for the specific language governing permissions
    1.15 +# and limitations under the License.
    1.16 +#
    1.17 +# When distributing Covered Code, include this CDDL HEADER in each
    1.18 +# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
    1.19 +# If applicable, add the following below this CDDL HEADER, with the
    1.20 +# fields enclosed by brackets "[]" replaced with your own identifying
    1.21 +# information: Portions Copyright [yyyy] [name of copyright owner]
    1.22 +#
    1.23 +# CDDL HEADER END
    1.24 +#
    1.25 +# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
    1.26 +# Use is subject to license terms.
    1.27 +#
    1.28 +# This script takes a file list and a workspace and builds a set of html files
    1.29 +# suitable for doing a code review of source changes via a web page.
    1.30 +# Documentation is available via 'webrev -h'.
    1.31 +#
    1.32 +
    1.33 +WEBREV_UPDATED=23.18-hg
    1.34 +
    1.35 +HTML='<?xml version="1.0"?>
    1.36 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    1.37 +    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    1.38 +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
    1.39 +
    1.40 +FRAMEHTML='<?xml version="1.0"?>
    1.41 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
    1.42 +    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
    1.43 +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
    1.44 +
    1.45 +STDHEAD='<meta http-equiv="cache-control" content="no-cache" />
    1.46 +<meta http-equiv="Pragma" content="no-cache" />
    1.47 +<meta http-equiv="Expires" content="-1" />
    1.48 +<!--
    1.49 +   Note to customizers: the body of the webrev is IDed as SUNWwebrev
    1.50 +   to allow easy overriding by users of webrev via the userContent.css
    1.51 +   mechanism available in some browsers.
    1.52 +
    1.53 +   For example, to have all "removed" information be red instead of
    1.54 +   brown, set a rule in your userContent.css file like:
    1.55 +
    1.56 +       body#SUNWwebrev span.removed { color: red ! important; }
    1.57 +-->
    1.58 +<style type="text/css" media="screen">
    1.59 +body {
    1.60 +    background-color: #eeeeee;
    1.61 +}
    1.62 +hr {
    1.63 +    border: none 0;
    1.64 +    border-top: 1px solid #aaa;
    1.65 +    height: 1px;
    1.66 +}
    1.67 +div.summary {
    1.68 +    font-size: .8em;
    1.69 +    border-bottom: 1px solid #aaa;
    1.70 +    padding-left: 1em;
    1.71 +    padding-right: 1em;
    1.72 +}
    1.73 +div.summary h2 {
    1.74 +    margin-bottom: 0.3em;
    1.75 +}
    1.76 +div.summary table th {
    1.77 +    text-align: right;
    1.78 +    vertical-align: top;
    1.79 +    white-space: nowrap;
    1.80 +}
    1.81 +span.lineschanged {
    1.82 +    font-size: 0.7em;
    1.83 +}
    1.84 +span.oldmarker {
    1.85 +    color: red;
    1.86 +    font-size: large;
    1.87 +    font-weight: bold;
    1.88 +}
    1.89 +span.newmarker {
    1.90 +    color: green;
    1.91 +    font-size: large;
    1.92 +    font-weight: bold;
    1.93 +}
    1.94 +span.removed {
    1.95 +    color: brown;
    1.96 +}
    1.97 +span.changed {
    1.98 +    color: blue;
    1.99 +}
   1.100 +span.new {
   1.101 +    color: blue;
   1.102 +    font-weight: bold;
   1.103 +}
   1.104 +a.print { font-size: x-small; }
   1.105 +
   1.106 +</style>
   1.107 +
   1.108 +<style type="text/css" media="print">
   1.109 +pre { font-size: 0.8em; font-family: courier, monospace; }
   1.110 +span.removed { color: #444; font-style: italic }
   1.111 +span.changed { font-weight: bold; }
   1.112 +span.new { font-weight: bold; }
   1.113 +span.newmarker { font-size: 1.2em; font-weight: bold; }
   1.114 +span.oldmarker { font-size: 1.2em; font-weight: bold; }
   1.115 +a.print {display: none}
   1.116 +hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
   1.117 +</style>
   1.118 +'
   1.119 +
   1.120 +#
   1.121 +# UDiffs need a slightly different CSS rule for 'new' items (we don't
   1.122 +# want them to be bolded as we do in cdiffs or sdiffs).
   1.123 +#
   1.124 +UDIFFCSS='
   1.125 +<style type="text/css" media="screen">
   1.126 +span.new {
   1.127 +    color: blue;
   1.128 +    font-weight: normal;
   1.129 +}
   1.130 +</style>
   1.131 +'
   1.132 +
   1.133 +#
   1.134 +# input_cmd | html_quote | output_cmd
   1.135 +# or
   1.136 +# html_quote filename | output_cmd
   1.137 +#
   1.138 +# Make a piece of source code safe for display in an HTML <pre> block.
   1.139 +#
   1.140 +html_quote()
   1.141 +{
   1.142 +	sed -e "s/&/\&amp;/g" -e "s/</\&lt;/g" -e "s/>/\&gt;/g" "$@" | expand
   1.143 +}
   1.144 +
   1.145 +#
   1.146 +# input_cmd | bug2url | output_cmd
   1.147 +#
   1.148 +# Scan for bugids and insert <a> links to the relevent bug database.
   1.149 +#
   1.150 +bug2url()
   1.151 +{
   1.152 +	sed -e 's|[0-9]\{5,\}|<a href=\"'$BUGURL'&\">&</a>|g'
   1.153 +}
   1.154 +
   1.155 +#
   1.156 +# input_cmd | sac2url | output_cmd
   1.157 +#
   1.158 +# Scan for ARC cases and insert <a> links to the relevent SAC database.
   1.159 +# This is slightly complicated because inside the SWAN, SAC cases are
   1.160 +# grouped by ARC: PSARC/2006/123.  But on OpenSolaris.org, they are
   1.161 +# referenced as 2006/123 (without labelling the ARC).
   1.162 +#
   1.163 +sac2url()
   1.164 +{
   1.165 +	if [[ -z $Oflag ]]; then
   1.166 +	    sed -e 's|\([A-Z]\{1,2\}ARC\)[ /]\([0-9]\{4\}\)/\([0-9]\{3\}\)|<a href=\"'$SACURL'\1/\2/\3\">\1 \2/\3</a>|g'
   1.167 +	else
   1.168 +	    sed -e 's|\([A-Z]\{1,2\}ARC\)[ /]\([0-9]\{4\}\)/\([0-9]\{3\}\)|<a href=\"'$SACURL'/\2/\3\">\1 \2/\3</a>|g'
   1.169 +	fi
   1.170 +}
   1.171 +
   1.172 +#
   1.173 +# strip_unchanged <infile> | output_cmd
   1.174 +#
   1.175 +# Removes chunks of sdiff documents that have not changed. This makes it
   1.176 +# easier for a code reviewer to find the bits that have changed.
   1.177 +#
   1.178 +# Deleted lines of text are replaced by a horizontal rule. Some
   1.179 +# identical lines are retained before and after the changed lines to
   1.180 +# provide some context.  The number of these lines is controlled by the
   1.181 +# variable C in the $AWK script below.
   1.182 +#
   1.183 +# The script detects changed lines as any line that has a "<span class="
   1.184 +# string embedded (unchanged lines have no particular class and are not
   1.185 +# part of a <span>).  Blank lines (without a sequence number) are also
   1.186 +# detected since they flag lines that have been inserted or deleted.
   1.187 +#
   1.188 +strip_unchanged()
   1.189 +{
   1.190 +	$AWK '
   1.191 +	BEGIN	{ C = c = 20 }
   1.192 +	NF == 0 || /span class=/ {
   1.193 +		if (c > C) {
   1.194 +			c -= C
   1.195 +			inx = 0
   1.196 +			if (c > C) {
   1.197 +				print "\n</pre><hr></hr><pre>"
   1.198 +				inx = c % C
   1.199 +				c = C
   1.200 +			}
   1.201 +
   1.202 +			for (i = 0; i < c; i++)
   1.203 +				print ln[(inx + i) % C]
   1.204 +		}
   1.205 +		c = 0;
   1.206 +		print
   1.207 +		next
   1.208 +	}
   1.209 +	{	if (c >= C) {
   1.210 +			ln[c % C] = $0
   1.211 +			c++;
   1.212 +			next;
   1.213 +		}
   1.214 +		c++;
   1.215 +		print
   1.216 +	}
   1.217 +	END	{ if (c > (C * 2)) print "\n</pre><hr></hr>" }
   1.218 +
   1.219 +	' $1
   1.220 +}
   1.221 +
   1.222 +#
   1.223 +# sdiff_to_html
   1.224 +#
   1.225 +# This function takes two files as arguments, obtains their diff, and
   1.226 +# processes the diff output to present the files as an HTML document with
   1.227 +# the files displayed side-by-side, differences shown in color.  It also
   1.228 +# takes a delta comment, rendered as an HTML snippet, as the third
   1.229 +# argument.  The function takes two files as arguments, then the name of
   1.230 +# file, the path, and the comment.  The HTML will be delivered on stdout,
   1.231 +# e.g.
   1.232 +#
   1.233 +#   $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
   1.234 +#         new/usr/src/tools/scripts/webrev.sh \
   1.235 +#         webrev.sh usr/src/tools/scripts \
   1.236 +#         '<a href="http://monaco.sfbay.sun.com/detail.jsp?cr=1234567">
   1.237 +#          1234567</a> my bugid' > <file>.html
   1.238 +#
   1.239 +# framed_sdiff() is then called which creates $2.frames.html
   1.240 +# in the webrev tree.
   1.241 +#
   1.242 +# FYI: This function is rather unusual in its use of awk.  The initial
   1.243 +# diff run produces conventional diff output showing changed lines mixed
   1.244 +# with editing codes.  The changed lines are ignored - we're interested in
   1.245 +# the editing codes, e.g.
   1.246 +#
   1.247 +#      8c8
   1.248 +#      57a61
   1.249 +#      63c66,76
   1.250 +#      68,93d80
   1.251 +#      106d90
   1.252 +#      108,110d91
   1.253 +#
   1.254 +#  These editing codes are parsed by the awk script and used to generate
   1.255 +#  another awk script that generates HTML, e.g the above lines would turn
   1.256 +#  into something like this:
   1.257 +#
   1.258 +#      BEGIN { printf "<pre>\n" }
   1.259 +#      function sp(n) {for (i=0;i<n;i++)printf "\n"}
   1.260 +#      function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0}
   1.261 +#      NR==8           {wl("#7A7ADD");next}
   1.262 +#      NR==54          {wl("#7A7ADD");sp(3);next}
   1.263 +#      NR==56          {wl("#7A7ADD");next}
   1.264 +#      NR==57          {wl("black");printf "\n"; next}
   1.265 +#        :               :
   1.266 +#
   1.267 +#  This script is then run on the original source file to generate the
   1.268 +#  HTML that corresponds to the source file.
   1.269 +#
   1.270 +#  The two HTML files are then combined into a single piece of HTML that
   1.271 +#  uses an HTML table construct to present the files side by side.  You'll
   1.272 +#  notice that the changes are color-coded:
   1.273 +#
   1.274 +#   black     - unchanged lines
   1.275 +#   blue      - changed lines
   1.276 +#   bold blue - new lines
   1.277 +#   brown     - deleted lines
   1.278 +#
   1.279 +#  Blank lines are inserted in each file to keep unchanged lines in sync
   1.280 +#  (side-by-side).  This format is familiar to users of sdiff(1) or
   1.281 +#  Teamware's filemerge tool.
   1.282 +#
   1.283 +sdiff_to_html()
   1.284 +{
   1.285 +	diff -b $1 $2 > /tmp/$$.diffs
   1.286 +
   1.287 +	TNAME=$3
   1.288 +	TPATH=$4
   1.289 +	COMMENT=$5
   1.290 +
   1.291 +	#
   1.292 +	#  Now we have the diffs, generate the HTML for the old file.
   1.293 +	#
   1.294 +	$AWK '
   1.295 +	BEGIN	{
   1.296 +		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
   1.297 +		printf "function removed() "
   1.298 +		printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   1.299 +		printf "function changed() "
   1.300 +		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   1.301 +		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
   1.302 +}
   1.303 +	/^</	{next}
   1.304 +	/^>/	{next}
   1.305 +	/^---/	{next}
   1.306 +
   1.307 +	{
   1.308 +	split($1, a, /[cad]/) ;
   1.309 +	if (index($1, "a")) {
   1.310 +		if (a[1] == 0) {
   1.311 +			n = split(a[2], r, /,/);
   1.312 +			if (n == 1)
   1.313 +				printf "BEGIN\t\t{sp(1)}\n"
   1.314 +			else
   1.315 +				printf "BEGIN\t\t{sp(%d)}\n",\
   1.316 +				(r[2] - r[1]) + 1
   1.317 +			next
   1.318 +		}
   1.319 +
   1.320 +		printf "NR==%s\t\t{", a[1]
   1.321 +		n = split(a[2], r, /,/);
   1.322 +		s = r[1];
   1.323 +		if (n == 1)
   1.324 +			printf "bl();printf \"\\n\"; next}\n"
   1.325 +		else {
   1.326 +			n = r[2] - r[1]
   1.327 +			printf "bl();sp(%d);next}\n",\
   1.328 +			(r[2] - r[1]) + 1
   1.329 +		}
   1.330 +		next
   1.331 +	}
   1.332 +	if (index($1, "d")) {
   1.333 +		n = split(a[1], r, /,/);
   1.334 +		n1 = r[1]
   1.335 +		n2 = r[2]
   1.336 +		if (n == 1)
   1.337 +			printf "NR==%s\t\t{removed(); next}\n" , n1
   1.338 +		else
   1.339 +			printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
   1.340 +		next
   1.341 +	}
   1.342 +	if (index($1, "c")) {
   1.343 +		n = split(a[1], r, /,/);
   1.344 +		n1 = r[1]
   1.345 +		n2 = r[2]
   1.346 +		final = n2
   1.347 +		d1 = 0
   1.348 +		if (n == 1)
   1.349 +			printf "NR==%s\t\t{changed();" , n1
   1.350 +		else {
   1.351 +			d1 = n2 - n1
   1.352 +			printf "NR==%s,NR==%s\t{changed();" , n1, n2
   1.353 +		}
   1.354 +		m = split(a[2], r, /,/);
   1.355 +		n1 = r[1]
   1.356 +		n2 = r[2]
   1.357 +		if (m > 1) {
   1.358 +			d2  = n2 - n1
   1.359 +			if (d2 > d1) {
   1.360 +				if (n > 1) printf "if (NR==%d)", final
   1.361 +				printf "sp(%d);", d2 - d1
   1.362 +			}
   1.363 +		}
   1.364 +		printf "next}\n" ;
   1.365 +
   1.366 +		next
   1.367 +	}
   1.368 +	}
   1.369 +
   1.370 +	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
   1.371 +	' /tmp/$$.diffs > /tmp/$$.file1
   1.372 +
   1.373 +	#
   1.374 +	#  Now generate the HTML for the new file
   1.375 +	#
   1.376 +	$AWK '
   1.377 +	BEGIN	{
   1.378 +		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
   1.379 +		printf "function new() "
   1.380 +		printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   1.381 +		printf "function changed() "
   1.382 +		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   1.383 +		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
   1.384 +	}
   1.385 +
   1.386 +	/^</	{next}
   1.387 +	/^>/	{next}
   1.388 +	/^---/	{next}
   1.389 +
   1.390 +	{
   1.391 +	split($1, a, /[cad]/) ;
   1.392 +	if (index($1, "d")) {
   1.393 +		if (a[2] == 0) {
   1.394 +			n = split(a[1], r, /,/);
   1.395 +			if (n == 1)
   1.396 +				printf "BEGIN\t\t{sp(1)}\n"
   1.397 +			else
   1.398 +				printf "BEGIN\t\t{sp(%d)}\n",\
   1.399 +				(r[2] - r[1]) + 1
   1.400 +			next
   1.401 +		}
   1.402 +
   1.403 +		printf "NR==%s\t\t{", a[2]
   1.404 +		n = split(a[1], r, /,/);
   1.405 +		s = r[1];
   1.406 +		if (n == 1)
   1.407 +			printf "bl();printf \"\\n\"; next}\n"
   1.408 +		else {
   1.409 +			n = r[2] - r[1]
   1.410 +			printf "bl();sp(%d);next}\n",\
   1.411 +			(r[2] - r[1]) + 1
   1.412 +		}
   1.413 +		next
   1.414 +	}
   1.415 +	if (index($1, "a")) {
   1.416 +		n = split(a[2], r, /,/);
   1.417 +		n1 = r[1]
   1.418 +		n2 = r[2]
   1.419 +		if (n == 1)
   1.420 +			printf "NR==%s\t\t{new() ; next}\n" , n1
   1.421 +		else
   1.422 +			printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
   1.423 +		next
   1.424 +	}
   1.425 +	if (index($1, "c")) {
   1.426 +		n = split(a[2], r, /,/);
   1.427 +		n1 = r[1]
   1.428 +		n2 = r[2]
   1.429 +		final = n2
   1.430 +		d2 = 0;
   1.431 +		if (n == 1) {
   1.432 +			final = n1
   1.433 +			printf "NR==%s\t\t{changed();" , n1
   1.434 +		} else {
   1.435 +			d2 = n2 - n1
   1.436 +			printf "NR==%s,NR==%s\t{changed();" , n1, n2
   1.437 +		}
   1.438 +		m = split(a[1], r, /,/);
   1.439 +		n1 = r[1]
   1.440 +		n2 = r[2]
   1.441 +		if (m > 1) {
   1.442 +			d1  = n2 - n1
   1.443 +			if (d1 > d2) {
   1.444 +				if (n > 1) printf "if (NR==%d)", final
   1.445 +				printf "sp(%d);", d1 - d2
   1.446 +			}
   1.447 +		}
   1.448 +		printf "next}\n" ;
   1.449 +		next
   1.450 +	}
   1.451 +	}
   1.452 +	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
   1.453 +	' /tmp/$$.diffs > /tmp/$$.file2
   1.454 +
   1.455 +	#
   1.456 +	# Post-process the HTML files by running them back through $AWK
   1.457 +	#
   1.458 +	html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html
   1.459 +
   1.460 +	html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html
   1.461 +
   1.462 +	#
   1.463 +	# Now combine into a valid HTML file and side-by-side into a table
   1.464 +	#
   1.465 +	print "$HTML<head>$STDHEAD"
   1.466 +	print "<title>$WNAME Sdiff $TPATH </title>"
   1.467 +	print "</head><body id=\"SUNWwebrev\">"
   1.468 +	print "<h2>$TPATH/$TNAME</h2>"
   1.469 +        print "<a class=\"print\" href=\"javascript:print()\">Print this page</a>"
   1.470 +	print "<pre>$COMMENT</pre>\n"
   1.471 +	print "<table><tr valign=\"top\">"
   1.472 +	print "<td><pre>"
   1.473 +
   1.474 +	strip_unchanged /tmp/$$.file1.html
   1.475 +
   1.476 +	print "</pre></td><td><pre>"
   1.477 +
   1.478 +	strip_unchanged /tmp/$$.file2.html
   1.479 +
   1.480 +	print "</pre></td>"
   1.481 +	print "</tr></table>"
   1.482 +	print "</body></html>"
   1.483 +
   1.484 +	framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \
   1.485 +	    "$COMMENT"
   1.486 +}
   1.487 +
   1.488 +
   1.489 +#
   1.490 +# framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment>
   1.491 +#
   1.492 +# Expects lefthand and righthand side html files created by sdiff_to_html.
   1.493 +# We use insert_anchors() to augment those with HTML navigation anchors,
   1.494 +# and then emit the main frame.  Content is placed into:
   1.495 +#
   1.496 +#    $WDIR/DIR/$TNAME.lhs.html
   1.497 +#    $WDIR/DIR/$TNAME.rhs.html
   1.498 +#    $WDIR/DIR/$TNAME.frames.html
   1.499 +#
   1.500 +# NOTE: We rely on standard usage of $WDIR and $DIR.
   1.501 +#
   1.502 +function framed_sdiff
   1.503 +{
   1.504 +	typeset TNAME=$1
   1.505 +	typeset TPATH=$2
   1.506 +	typeset lhsfile=$3
   1.507 +	typeset rhsfile=$4
   1.508 +	typeset comments=$5
   1.509 +	typeset RTOP
   1.510 +
   1.511 +	# Enable html files to access WDIR via a relative path.
   1.512 +	RTOP=$(relative_dir $TPATH $WDIR)
   1.513 +
   1.514 +	# Make the rhs/lhs files and output the frameset file.
   1.515 +	print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
   1.516 +
   1.517 +	cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF
   1.518 +	    <script type="text/javascript" src="$RTOP/ancnav.js"></script>
   1.519 +	    </head>
   1.520 +	    <body id="SUNWwebrev" onkeypress="keypress(event);">
   1.521 +	    <a name="0"></a>
   1.522 +	    <pre>$comments</pre><hr></hr>
   1.523 +	EOF
   1.524 +
   1.525 +	cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html
   1.526 +
   1.527 +	insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
   1.528 +	insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
   1.529 +
   1.530 +	close='</body></html>'
   1.531 +
   1.532 +	print $close >> $WDIR/$DIR/$TNAME.lhs.html
   1.533 +	print $close >> $WDIR/$DIR/$TNAME.rhs.html
   1.534 +
   1.535 +	print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html
   1.536 +	print "<title>$WNAME Framed-Sdiff " \
   1.537 +	    "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html
   1.538 +	cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF
   1.539 +	  <frameset rows="*,60">
   1.540 +	    <frameset cols="50%,50%">
   1.541 +	      <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs" />
   1.542 +	      <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs" />
   1.543 +	    </frameset>
   1.544 +	  <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0"
   1.545 +	   marginheight="0" name="nav" />
   1.546 +	  <noframes>
   1.547 +            <body id="SUNWwebrev">
   1.548 +	      Alas 'frames' webrev requires that your browser supports frames
   1.549 +	      and has the feature enabled.
   1.550 +            </body>
   1.551 +	  </noframes>
   1.552 +	  </frameset>
   1.553 +	</html>
   1.554 +	EOF
   1.555 +}
   1.556 +
   1.557 +
   1.558 +#
   1.559 +# fix_postscript
   1.560 +#
   1.561 +# Merge codereview output files to a single conforming postscript file, by:
   1.562 +# 	- removing all extraneous headers/trailers
   1.563 +#	- making the page numbers right
   1.564 +#	- removing pages devoid of contents which confuse some
   1.565 +#	  postscript readers.
   1.566 +#
   1.567 +# From Casper.
   1.568 +#
   1.569 +function fix_postscript
   1.570 +{
   1.571 +	infile=$1
   1.572 +
   1.573 +	cat > /tmp/$$.crmerge.pl << \EOF
   1.574 +
   1.575 +	print scalar(<>);		# %!PS-Adobe---
   1.576 +	print "%%Orientation: Landscape\n";
   1.577 +
   1.578 +	$pno = 0;
   1.579 +	$doprint = 1;
   1.580 +
   1.581 +	$page = "";
   1.582 +
   1.583 +	while (<>) {
   1.584 +		next if (/^%%Pages:\s*\d+/);
   1.585 +
   1.586 +		if (/^%%Page:/) {
   1.587 +			if ($pno == 0 || $page =~ /\)S/) {
   1.588 +				# Header or single page containing text
   1.589 +				print "%%Page: ? $pno\n" if ($pno > 0);
   1.590 +				print $page;
   1.591 +				$pno++;
   1.592 +			} else {
   1.593 +				# Empty page, skip it.
   1.594 +			}
   1.595 +			$page = "";
   1.596 +			$doprint = 1;
   1.597 +			next;
   1.598 +		}
   1.599 +
   1.600 +		# Skip from %%Trailer of one document to Endprolog
   1.601 +		# %%Page of the next
   1.602 +		$doprint = 0 if (/^%%Trailer/);
   1.603 +		$page .= $_ if ($doprint);
   1.604 +	}
   1.605 +
   1.606 +	if ($page =~ /\)S/) {
   1.607 +		print "%%Page: ? $pno\n";
   1.608 +		print $page;
   1.609 +	} else {
   1.610 +		$pno--;
   1.611 +	}
   1.612 +	print "%%Trailer\n%%Pages: $pno\n";
   1.613 +EOF
   1.614 +
   1.615 +	$PERL /tmp/$$.crmerge.pl < $infile
   1.616 +}
   1.617 +
   1.618 +
   1.619 +#
   1.620 +# input_cmd | insert_anchors | output_cmd
   1.621 +#
   1.622 +# Flag blocks of difference with sequentially numbered invisible
   1.623 +# anchors.  These are used to drive the frames version of the
   1.624 +# sdiffs output.
   1.625 +#
   1.626 +# NOTE: Anchor zero flags the top of the file irrespective of changes,
   1.627 +# an additional anchor is also appended to flag the bottom.
   1.628 +#
   1.629 +# The script detects changed lines as any line that has a "<span
   1.630 +# class=" string embedded (unchanged lines have no class set and are
   1.631 +# not part of a <span>.  Blank lines (without a sequence number)
   1.632 +# are also detected since they flag lines that have been inserted or
   1.633 +# deleted.
   1.634 +#
   1.635 +function insert_anchors
   1.636 +{
   1.637 +	$AWK '
   1.638 +	function ia() {
   1.639 +		# This should be able to be a singleton <a /> but that
   1.640 +		# seems to trigger a bug in firefox a:hover rule processing
   1.641 +		printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++;
   1.642 +	}
   1.643 +
   1.644 +	BEGIN {
   1.645 +		anc=1;
   1.646 +		inblock=1;
   1.647 +		printf "<pre>\n";
   1.648 +	}
   1.649 +	NF == 0 || /^<span class=/ {
   1.650 +		if (inblock == 0) {
   1.651 +			ia();
   1.652 +			inblock=1;
   1.653 +		}
   1.654 +		print;
   1.655 +		next;
   1.656 +	}
   1.657 +	{
   1.658 +		inblock=0;
   1.659 +		print;
   1.660 +	}
   1.661 +	END {
   1.662 +		ia();
   1.663 +
   1.664 +		printf "<b style=\"font-size: large; color: red\">";
   1.665 +		printf "--- EOF ---</b>"
   1.666 +        	for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
   1.667 +		printf "</pre>"
   1.668 +		printf "<form name=\"eof\">";
   1.669 +		printf "<input name=\"value\" value=\"%d\" type=\"hidden\" />",
   1.670 +		    anc - 1;
   1.671 +		printf "</form>";
   1.672 +	}
   1.673 +	' $1
   1.674 +}
   1.675 +
   1.676 +
   1.677 +#
   1.678 +# relative_dir
   1.679 +#
   1.680 +# Print a relative return path from $1 to $2.  For example if
   1.681 +# $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview,
   1.682 +# this function would print "../../../../".
   1.683 +#
   1.684 +# In the event that $1 is not in $2 a warning is printed to stderr,
   1.685 +# and $2 is returned-- the result of this is that the resulting webrev
   1.686 +# is not relocatable.
   1.687 +#
   1.688 +function relative_dir
   1.689 +{
   1.690 +    d1=$1
   1.691 +    d2=$2
   1.692 +    if [[ "$d1" == "." ]]; then
   1.693 +	print "."
   1.694 +    else
   1.695 +	typeset cur="${d1##$d2?(/)}"
   1.696 +	typeset ret=""
   1.697 +	if [[ $d2 == $cur ]]; then   # Should never happen.
   1.698 +		# Should never happen.
   1.699 +		print -u2 "\nWARNING: relative_dir: \"$1\" not relative "
   1.700 +		print -u2 "to \"$2\".  Check input paths.  Framed webrev "
   1.701 +		print -u2 "will not be relocatable!"
   1.702 +		print $2
   1.703 +		return
   1.704 +	fi
   1.705 +
   1.706 +	while [[ -n ${cur} ]];
   1.707 +	do
   1.708 +		cur=${cur%%*(/)*([!/])}
   1.709 +		if [[ -z $ret ]]; then
   1.710 +			ret=".."
   1.711 +		else
   1.712 +			ret="../$ret"
   1.713 +		fi
   1.714 +	done
   1.715 +	print $ret
   1.716 +    fi
   1.717 +}
   1.718 +
   1.719 +
   1.720 +#
   1.721 +# frame_nav_js
   1.722 +#
   1.723 +# Emit javascript for frame navigation
   1.724 +#
   1.725 +function frame_nav_js
   1.726 +{
   1.727 +cat << \EOF
   1.728 +var myInt;
   1.729 +var scrolling=0;
   1.730 +var sfactor = 3;
   1.731 +var scount=10;
   1.732 +
   1.733 +function scrollByPix() {
   1.734 +	if (scount<=0) {
   1.735 +		sfactor*=1.2;
   1.736 +		scount=10;
   1.737 +	}
   1.738 +	parent.lhs.scrollBy(0,sfactor);
   1.739 +	parent.rhs.scrollBy(0,sfactor);
   1.740 +	scount--;
   1.741 +}
   1.742 +
   1.743 +function scrollToAnc(num) {
   1.744 +
   1.745 +	// Update the value of the anchor in the form which we use as
   1.746 +	// storage for this value.  setAncValue() will take care of
   1.747 +	// correcting for overflow and underflow of the value and return
   1.748 +	// us the new value.
   1.749 +	num = setAncValue(num);
   1.750 +
   1.751 +	// Set location and scroll back a little to expose previous
   1.752 +	// lines.
   1.753 +	//
   1.754 +	// Note that this could be improved: it is possible although
   1.755 +	// complex to compute the x and y position of an anchor, and to
   1.756 +	// scroll to that location directly.
   1.757 +	//
   1.758 +	parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num);
   1.759 +	parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num);
   1.760 +
   1.761 +	parent.lhs.scrollBy(0,-30);
   1.762 +	parent.rhs.scrollBy(0,-30);
   1.763 +}
   1.764 +
   1.765 +function getAncValue()
   1.766 +{
   1.767 +	return (parseInt(parent.nav.document.diff.real.value));
   1.768 +}
   1.769 +
   1.770 +function setAncValue(val)
   1.771 +{
   1.772 +	if (val <= 0) {
   1.773 +		val = 0;
   1.774 +		parent.nav.document.diff.real.value = val;
   1.775 +		parent.nav.document.diff.display.value = "BOF";
   1.776 +		return (val);
   1.777 +	}
   1.778 +
   1.779 +	//
   1.780 +	// The way we compute the max anchor value is to stash it
   1.781 +	// inline in the left and right hand side pages-- it's the same
   1.782 +	// on each side, so we pluck from the left.
   1.783 +	//
   1.784 +	maxval = parent.lhs.document.eof.value.value;
   1.785 +	if (val < maxval) {
   1.786 +		parent.nav.document.diff.real.value = val;
   1.787 +		parent.nav.document.diff.display.value = val.toString();
   1.788 +		return (val);
   1.789 +	}
   1.790 +
   1.791 +	// this must be: val >= maxval
   1.792 +	val = maxval;
   1.793 +	parent.nav.document.diff.real.value = val;
   1.794 +	parent.nav.document.diff.display.value = "EOF";
   1.795 +	return (val);
   1.796 +}
   1.797 +
   1.798 +function stopScroll() {
   1.799 +	if (scrolling==1) {
   1.800 +		clearInterval(myInt);
   1.801 +		scrolling=0;
   1.802 +	}
   1.803 +}
   1.804 +
   1.805 +function startScroll() {
   1.806 +	stopScroll();
   1.807 +	scrolling=1;
   1.808 +	myInt=setInterval("scrollByPix()",10);
   1.809 +}
   1.810 +
   1.811 +function handlePress(b) {
   1.812 +
   1.813 +	switch (b) {
   1.814 +	    case 1 :
   1.815 +		scrollToAnc(-1);
   1.816 +		break;
   1.817 +	    case 2 :
   1.818 +		scrollToAnc(getAncValue() - 1);
   1.819 +		break;
   1.820 +	    case 3 :
   1.821 +		sfactor=-3;
   1.822 +		startScroll();
   1.823 +		break;
   1.824 +	    case 4 :
   1.825 +		sfactor=3;
   1.826 +		startScroll();
   1.827 +		break;
   1.828 +	    case 5 :
   1.829 +		scrollToAnc(getAncValue() + 1);
   1.830 +		break;
   1.831 +	    case 6 :
   1.832 +		scrollToAnc(999999);
   1.833 +		break;
   1.834 +	}
   1.835 +}
   1.836 +
   1.837 +function handleRelease(b) {
   1.838 +	stopScroll();
   1.839 +}
   1.840 +
   1.841 +function keypress(ev) {
   1.842 +	var keynum;
   1.843 +	var keychar;
   1.844 +
   1.845 +	if (window.event) { // IE
   1.846 +		keynum = ev.keyCode;
   1.847 +	} else if (ev.which) { // non-IE
   1.848 +		keynum = ev.which;
   1.849 +	}
   1.850 +
   1.851 +	keychar = String.fromCharCode(keynum);
   1.852 +
   1.853 +	if (keychar == "k") {
   1.854 +		handlePress(2);
   1.855 +		return (0);
   1.856 +	} else if (keychar == "j" || keychar == " ") {
   1.857 +		handlePress(5);
   1.858 +		return (0);
   1.859 +	}
   1.860 +	return (1);
   1.861 +}
   1.862 +
   1.863 +function ValidateDiffNum(){
   1.864 +	val = parent.nav.document.diff.display.value;
   1.865 +	if (val == "EOF") {
   1.866 +		scrollToAnc(999999);
   1.867 +		return;
   1.868 +	}
   1.869 +
   1.870 +	if (val == "BOF") {
   1.871 +		scrollToAnc(0);
   1.872 +		return;
   1.873 +	}
   1.874 +
   1.875 +        i=parseInt(val);
   1.876 +        if (isNaN(i)) {
   1.877 +                parent.nav.document.diff.display.value = getAncValue();
   1.878 +        } else {
   1.879 +                scrollToAnc(i);
   1.880 +        }
   1.881 +        return false;
   1.882 +}
   1.883 +
   1.884 +EOF
   1.885 +}
   1.886 +
   1.887 +#
   1.888 +# frame_navigation
   1.889 +#
   1.890 +# Output anchor navigation file for framed sdiffs.
   1.891 +#
   1.892 +function frame_navigation
   1.893 +{
   1.894 +	print "$HTML<head>$STDHEAD"
   1.895 +
   1.896 +	cat << \EOF
   1.897 +<title>Anchor Navigation</title>
   1.898 +<meta http-equiv="Content-Script-Type" content="text/javascript" />
   1.899 +<meta http-equiv="Content-Type" content="text/html" />
   1.900 +
   1.901 +<style type="text/css">
   1.902 +    div.button td { padding-left: 5px; padding-right: 5px;
   1.903 +		    background-color: #eee; text-align: center;
   1.904 +		    border: 1px #444 outset; cursor: pointer; }
   1.905 +    div.button a { font-weight: bold; color: black }
   1.906 +    div.button td:hover { background: #ffcc99; }
   1.907 +</style>
   1.908 +EOF
   1.909 +
   1.910 +	print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
   1.911 +
   1.912 +	cat << \EOF
   1.913 +</head>
   1.914 +<body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();"
   1.915 +	onkeypress="keypress(event);">
   1.916 +    <noscript lang="javascript">
   1.917 +      <center>
   1.918 +	<p><big>Framed Navigation controls require Javascript</big><br />
   1.919 +	Either this browser is incompatable or javascript is not enabled</p>
   1.920 +      </center>
   1.921 +    </noscript>
   1.922 +    <table width="100%" border="0" align="center">
   1.923 +	<tr>
   1.924 +          <td valign="middle" width="25%">Diff navigation:
   1.925 +          Use 'j' and 'k' for next and previous diffs; or use buttons
   1.926 +          at right</td>
   1.927 +	  <td align="center" valign="top" width="50%">
   1.928 +	    <div class="button">
   1.929 +	      <table border="0" align="center">
   1.930 +                  <tr>
   1.931 +		    <td>
   1.932 +		      <a onMouseDown="handlePress(1);return true;"
   1.933 +			 onMouseUp="handleRelease(1);return true;"
   1.934 +			 onMouseOut="handleRelease(1);return true;"
   1.935 +			 onClick="return false;"
   1.936 +			 title="Go to Beginning Of file">BOF</a></td>
   1.937 +		    <td>
   1.938 +		      <a onMouseDown="handlePress(3);return true;"
   1.939 +			 onMouseUp="handleRelease(3);return true;"
   1.940 +			 onMouseOut="handleRelease(3);return true;"
   1.941 +			 title="Scroll Up: Press and Hold to accelerate"
   1.942 +			 onClick="return false;">Scroll Up</a></td>
   1.943 +		    <td>
   1.944 +		      <a onMouseDown="handlePress(2);return true;"
   1.945 +			 onMouseUp="handleRelease(2);return true;"
   1.946 +			 onMouseOut="handleRelease(2);return true;"
   1.947 +			 title="Go to previous Diff"
   1.948 +			 onClick="return false;">Prev Diff</a>
   1.949 +		    </td></tr>
   1.950 +
   1.951 +		  <tr>
   1.952 +		    <td>
   1.953 +		      <a onMouseDown="handlePress(6);return true;"
   1.954 +			 onMouseUp="handleRelease(6);return true;"
   1.955 +			 onMouseOut="handleRelease(6);return true;"
   1.956 +			 onClick="return false;"
   1.957 +			 title="Go to End Of File">EOF</a></td>
   1.958 +		    <td>
   1.959 +		      <a onMouseDown="handlePress(4);return true;"
   1.960 +			 onMouseUp="handleRelease(4);return true;"
   1.961 +			 onMouseOut="handleRelease(4);return true;"
   1.962 +			 title="Scroll Down: Press and Hold to accelerate"
   1.963 +			 onClick="return false;">Scroll Down</a></td>
   1.964 +		    <td>
   1.965 +		      <a onMouseDown="handlePress(5);return true;"
   1.966 +			 onMouseUp="handleRelease(5);return true;"
   1.967 +			 onMouseOut="handleRelease(5);return true;"
   1.968 +			 title="Go to next Diff"
   1.969 +			 onClick="return false;">Next Diff</a></td>
   1.970 +		  </tr>
   1.971 +              </table>
   1.972 +	    </div>
   1.973 +	  </td>
   1.974 +	  <th valign="middle" width="25%">
   1.975 +	    <form action="" name="diff" onsubmit="return ValidateDiffNum();">
   1.976 +		<input name="display" value="BOF" size="8" type="text" />
   1.977 +		<input name="real" value="0" size="8" type="hidden" />
   1.978 +	    </form>
   1.979 +	  </th>
   1.980 +	</tr>
   1.981 +    </table>
   1.982 +  </body>
   1.983 +</html>
   1.984 +EOF
   1.985 +}
   1.986 +
   1.987 +
   1.988 +
   1.989 +#
   1.990 +# diff_to_html <filename> <filepath> { U | C } <comment>
   1.991 +#
   1.992 +# Processes the output of diff to produce an HTML file representing either
   1.993 +# context or unified diffs.
   1.994 +#
   1.995 +diff_to_html()
   1.996 +{
   1.997 +	TNAME=$1
   1.998 +	TPATH=$2
   1.999 +	DIFFTYPE=$3
  1.1000 +	COMMENT=$4
  1.1001 +
  1.1002 +	print "$HTML<head>$STDHEAD"
  1.1003 +	print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
  1.1004 +
  1.1005 +	if [[ $DIFFTYPE == "U" ]]; then
  1.1006 +		print "$UDIFFCSS"
  1.1007 +	fi
  1.1008 +
  1.1009 +	cat <<-EOF
  1.1010 +	</head>
  1.1011 +	<body id="SUNWwebrev">
  1.1012 +	<h2>$TPATH</h2>
  1.1013 +        <a class="print" href="javascript:print()">Print this page</a>
  1.1014 +	<pre>$COMMENT</pre>
  1.1015 +        <pre>
  1.1016 +EOF
  1.1017 +
  1.1018 +	html_quote | $AWK '
  1.1019 +	/^--- new/	{ next }
  1.1020 +	/^\+\+\+ new/	{ next }
  1.1021 +	/^--- old/	{ next }
  1.1022 +	/^\*\*\* old/	{ next }
  1.1023 +	/^\*\*\*\*/	{ next }
  1.1024 +	/^-------/	{ printf "<center><h1>%s</h1></center>\n", $0; next }
  1.1025 +	/^\@\@.*\@\@$/	{ printf "</pre><hr /><pre>\n";
  1.1026 +			  printf "<span class=\"newmarker\">%s</span>\n", $0;
  1.1027 +			  next}
  1.1028 +
  1.1029 +	/^\*\*\*/	{ printf "<hr /><span class=\"oldmarker\">%s</span>\n", $0;
  1.1030 +			  next}
  1.1031 +	/^---/		{ printf "<span class=\"newmarker\">%s</span>\n", $0;
  1.1032 +			  next}
  1.1033 +	/^\+/		{printf "<span class=\"new\">%s</span>\n", $0; next}
  1.1034 +	/^!/		{printf "<span class=\"changed\">%s</span>\n", $0; next}
  1.1035 +	/^-/		{printf "<span class=\"removed\">%s</span>\n", $0; next}
  1.1036 +			{printf "%s\n", $0; next}
  1.1037 +	'
  1.1038 +
  1.1039 +	print "</pre></body></html>\n"
  1.1040 +}
  1.1041 +
  1.1042 +
  1.1043 +#
  1.1044 +# source_to_html { new | old } <filename>
  1.1045 +#
  1.1046 +# Process a plain vanilla source file to transform it into an HTML file.
  1.1047 +#
  1.1048 +source_to_html()
  1.1049 +{
  1.1050 +	WHICH=$1
  1.1051 +	TNAME=$2
  1.1052 +
  1.1053 +	print "$HTML<head>$STDHEAD"
  1.1054 +	print "<title>$WHICH $TNAME</title>"
  1.1055 +	print "<body id=\"SUNWwebrev\">"
  1.1056 +	print "<pre>"
  1.1057 +	html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
  1.1058 +	print "</pre></body></html>"
  1.1059 +}
  1.1060 +
  1.1061 +#
  1.1062 +# teamwarecomments {text|html} parent-file child-file
  1.1063 +#
  1.1064 +# Find the first delta in the child that's not in the parent.  Get the
  1.1065 +# newest delta from the parent, get all deltas from the child starting
  1.1066 +# with that delta, and then get all info starting with the second oldest
  1.1067 +# delta in that list (the first delta unique to the child).
  1.1068 +#
  1.1069 +# This code adapted from Bill Shannon's "spc" script
  1.1070 +#
  1.1071 +comments_from_teamware()
  1.1072 +{
  1.1073 +	fmt=$1
  1.1074 +	pfile=$PWS/$2
  1.1075 +	cfile=$CWS/$3
  1.1076 +
  1.1077 +	psid=$($SCCS prs -d:I: $pfile 2>/dev/null)
  1.1078 +	if [[ -z "$psid" ]]; then
  1.1079 +	    psid=1.1
  1.1080 +	fi
  1.1081 +
  1.1082 +	set -A sids $($SCCS prs -l -r$psid -d:I: $cfile 2>/dev/null)
  1.1083 +	N=${#sids[@]}
  1.1084 +
  1.1085 +	nawkprg='
  1.1086 +		/^COMMENTS:/	{p=1; next}
  1.1087 +		/^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; }
  1.1088 +		NF == 0u	{ next }
  1.1089 +		{if (p==0) next; print $0 }'
  1.1090 +
  1.1091 +	if [[ $N -ge 2 ]]; then
  1.1092 +		sid1=${sids[$((N-2))]}	# Gets 2nd to last sid
  1.1093 +
  1.1094 +		if [[ $fmt == "text" ]]; then
  1.1095 +			$SCCS prs -l -r$sid1 $cfile  2>/dev/null | \
  1.1096 +			    $AWK "$nawkprg"
  1.1097 +			return
  1.1098 +		fi
  1.1099 +
  1.1100 +		$SCCS prs -l -r$sid1 $cfile  2>/dev/null | \
  1.1101 +		    html_quote | bug2url | sac2url | $AWK "$nawkprg"
  1.1102 +	fi
  1.1103 +}
  1.1104 +
  1.1105 +#
  1.1106 +# wxcomments {text|html} filepath
  1.1107 +#
  1.1108 +# Given the pathname of a file, find its location in a "wx" active file
  1.1109 +# list and print the following sccs comment.  Output is either text or
  1.1110 +# HTML; if the latter, embedded bugids (sequence of 5 or more digits) are
  1.1111 +# turned into URLs.
  1.1112 +#
  1.1113 +comments_from_wx()
  1.1114 +{
  1.1115 +	typeset fmt=$1
  1.1116 +	typeset p=$2
  1.1117 +
  1.1118 +	comm=`$AWK '
  1.1119 +	$1 == "'$p'" {
  1.1120 +		do getline ; while (NF > 0)
  1.1121 +		getline
  1.1122 +		while (NF > 0) { print ; getline }
  1.1123 +		exit
  1.1124 +	}' < $wxfile`
  1.1125 +
  1.1126 +	if [[ $fmt == "text" ]]; then
  1.1127 +		print "$comm"
  1.1128 +		return
  1.1129 +	fi
  1.1130 +
  1.1131 +	print "$comm" | html_quote | bug2url | sac2url
  1.1132 +}
  1.1133 +
  1.1134 +comments_from_mercurial()
  1.1135 +{
  1.1136 +	fmt=$1
  1.1137 +	pfile=$PWS/$2
  1.1138 +	cfile=$CWS/$3
  1.1139 +
  1.1140 +        logdir=`dirname $cfile`
  1.1141 +        logf=`basename $cfile`
  1.1142 +        if [ -d $logdir ]; then
  1.1143 +            ( cd $logdir;
  1.1144 +	        active=`hg status $logf 2>/dev/null`
  1.1145 +                # If the output from 'hg status' is not empty, it means the file
  1.1146 +                # hasn't been committed, so don't fetch comments.
  1.1147 +	        if [[ -z $active ]] ; then
  1.1148 +                    if [[ -n $ALL_CREV ]]; then
  1.1149 +                        rev_opt=
  1.1150 +                        for rev in $ALL_CREV; do
  1.1151 +                            rev_opt="$rev_opt --rev $rev"
  1.1152 +                        done
  1.1153 +                        comm=`hg log $rev_opt --follow --template 'rev {rev} : {desc}\n' $logf`
  1.1154 +                    elif [[ -n $FIRST_CREV ]]; then
  1.1155 +		        comm=`hg log --rev $FIRST_CREV:tip --follow --template 'rev {rev} : {desc}\n' $logf`
  1.1156 +                    else
  1.1157 +		        comm=`hg log -l1 --follow --template 'rev {rev} : {desc}\n' $logf`
  1.1158 +                    fi
  1.1159 +	        else
  1.1160 +	            comm=""
  1.1161 +	        fi
  1.1162 +	        if [[ $fmt == "text" ]]; then
  1.1163 +	            print "$comm"
  1.1164 +	            return
  1.1165 +	        fi
  1.1166 +	  
  1.1167 +	        print "$comm" | html_quote | bug2url | sac2url
  1.1168 +                )
  1.1169 +        fi
  1.1170 +}
  1.1171 +
  1.1172 +
  1.1173 +#
  1.1174 +# getcomments {text|html} filepath parentpath
  1.1175 +#
  1.1176 +# Fetch the comments depending on what SCM mode we're in.
  1.1177 +#
  1.1178 +getcomments()
  1.1179 +{
  1.1180 +	typeset fmt=$1
  1.1181 +	typeset p=$2
  1.1182 +	typeset pp=$3
  1.1183 +
  1.1184 +	if [[ -n $wxfile ]]; then
  1.1185 +		comments_from_wx $fmt $p
  1.1186 +	else
  1.1187 +		if [[ $SCM_MODE == "teamware" ]]; then
  1.1188 +			comments_from_teamware $fmt $pp $p
  1.1189 +		elif [[ $SCM_MODE == "mercurial" ]]; then
  1.1190 +			comments_from_mercurial $fmt $pp $p
  1.1191 +		fi
  1.1192 +	fi
  1.1193 +}
  1.1194 +
  1.1195 +#
  1.1196 +# printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
  1.1197 +#
  1.1198 +# Print out Code Inspection figures similar to sccs-prt(1) format.
  1.1199 +#
  1.1200 +function printCI
  1.1201 +{
  1.1202 +	integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
  1.1203 +	typeset str
  1.1204 +	if (( tot == 1 )); then
  1.1205 +		str="line"
  1.1206 +	else
  1.1207 +		str="lines"
  1.1208 +	fi
  1.1209 +	printf '%d %s changed: %d ins; %d del; %d mod; %d unchg' \
  1.1210 +	    $tot $str $ins $del $mod $unc
  1.1211 +}
  1.1212 +
  1.1213 +
  1.1214 +#
  1.1215 +# difflines <oldfile> <newfile>
  1.1216 +#
  1.1217 +# Calculate and emit number of added, removed, modified and unchanged lines,
  1.1218 +# and total lines changed, the sum of added + removed + modified.
  1.1219 +#
  1.1220 +function difflines
  1.1221 +{
  1.1222 +	integer tot mod del ins unc err
  1.1223 +	typeset filename
  1.1224 +
  1.1225 +	eval $( diff -e $1 $2 | $AWK '
  1.1226 +	# Change range of lines: N,Nc
  1.1227 +	/^[0-9]*,[0-9]*c$/ {
  1.1228 +		n=split(substr($1,1,length($1)-1), counts, ",");
  1.1229 +		if (n != 2) {
  1.1230 +		    error=2
  1.1231 +		    exit;
  1.1232 +		}
  1.1233 +		#
  1.1234 +		# 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines.
  1.1235 +		# following would be 5 - 3 = 2! Hence +1 for correction.
  1.1236 +		#
  1.1237 +		r=(counts[2]-counts[1])+1;
  1.1238 +
  1.1239 +		#
  1.1240 +		# Now count replacement lines: each represents a change instead
  1.1241 +		# of a delete, so increment c and decrement r.
  1.1242 +		#
  1.1243 +		while (getline != /^\.$/) {
  1.1244 +			c++;
  1.1245 +			r--;
  1.1246 +		}
  1.1247 +		#
  1.1248 +		# If there were more replacement lines than original lines,
  1.1249 +		# then r will be negative; in this case there are no deletions,
  1.1250 +		# but there are r changes that should be counted as adds, and
  1.1251 +		# since r is negative, subtract it from a and add it to c.
  1.1252 +		#
  1.1253 +		if (r < 0) {
  1.1254 +			a-=r;
  1.1255 +			c+=r;
  1.1256 +		}
  1.1257 +
  1.1258 +		#
  1.1259 +		# If there were more original lines than replacement lines, then
  1.1260 +		# r will be positive; in this case, increment d by that much.
  1.1261 +		#
  1.1262 +		if (r > 0) {
  1.1263 +			d+=r;
  1.1264 +		}
  1.1265 +		next;
  1.1266 +	}
  1.1267 +
  1.1268 +	# Change lines: Nc
  1.1269 +	/^[0-9].*c$/ {
  1.1270 +		# The first line is a replacement; any more are additions.
  1.1271 +		if (getline != /^\.$/) {
  1.1272 +			c++;
  1.1273 +			while (getline != /^\.$/) a++;
  1.1274 +		}
  1.1275 +		next;
  1.1276 +	}
  1.1277 +
  1.1278 +	# Add lines: both Na and N,Na
  1.1279 +	/^[0-9].*a$/ {
  1.1280 +		while (getline != /^\.$/) a++;
  1.1281 +		next;
  1.1282 +	}
  1.1283 +
  1.1284 +	# Delete range of lines: N,Nd
  1.1285 +	/^[0-9]*,[0-9]*d$/ {
  1.1286 +		n=split(substr($1,1,length($1)-1), counts, ",");
  1.1287 +		if (n != 2) {
  1.1288 +			error=2
  1.1289 +			exit;
  1.1290 +		}
  1.1291 +		#
  1.1292 +		# 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines.
  1.1293 +		# following would be 5 - 3 = 2! Hence +1 for correction.
  1.1294 +		#
  1.1295 +		r=(counts[2]-counts[1])+1;
  1.1296 +		d+=r;
  1.1297 +		next;
  1.1298 +	}
  1.1299 +
  1.1300 +	# Delete line: Nd.   For example 10d says line 10 is deleted.
  1.1301 +	/^[0-9]*d$/ {d++; next}
  1.1302 +
  1.1303 +	# Should not get here!
  1.1304 +	{
  1.1305 +		error=1;
  1.1306 +		exit;
  1.1307 +	}
  1.1308 +
  1.1309 +	# Finish off - print results
  1.1310 +	END {
  1.1311 +		printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n",
  1.1312 +		    (c+d+a), c, d, a, error);
  1.1313 +	}' )
  1.1314 +
  1.1315 +	# End of $AWK, Check to see if any trouble occurred.
  1.1316 +	if (( $? > 0 || err > 0 )); then
  1.1317 +		print "Unexpected Error occurred reading" \
  1.1318 +		    "\`diff -e $1 $2\`: \$?=$?, err=" $err
  1.1319 +		return
  1.1320 +	fi
  1.1321 +
  1.1322 +	# Accumulate totals
  1.1323 +	(( TOTL += tot ))
  1.1324 +	(( TMOD += mod ))
  1.1325 +	(( TDEL += del ))
  1.1326 +	(( TINS += ins ))
  1.1327 +	# Calculate unchanged lines
  1.1328 +	unc=`wc -l < $1`
  1.1329 +	if (( unc > 0 )); then
  1.1330 +		(( unc -= del + mod ))
  1.1331 +		(( TUNC += unc ))
  1.1332 +	fi
  1.1333 +	# print summary
  1.1334 +	print "<span class=\"lineschanged\">\c"
  1.1335 +	printCI $tot $ins $del $mod $unc
  1.1336 +	print "</span>"
  1.1337 +}
  1.1338 +
  1.1339 +
  1.1340 +#
  1.1341 +# flist_from_wx
  1.1342 +#
  1.1343 +# Sets up webrev to source its information from a wx-formatted file.
  1.1344 +# Sets the global 'wxfile' variable.
  1.1345 +#
  1.1346 +function flist_from_wx
  1.1347 +{
  1.1348 +	typeset argfile=$1
  1.1349 +	if [[ -n ${argfile%%/*} ]]; then
  1.1350 +		#
  1.1351 +		# If the wx file pathname is relative then make it absolute
  1.1352 +		# because the webrev does a "cd" later on.
  1.1353 +		#
  1.1354 +		wxfile=$PWD/$argfile
  1.1355 +	else
  1.1356 +		wxfile=$argfile
  1.1357 +	fi
  1.1358 +
  1.1359 +	$AWK '{ c = 1; print;
  1.1360 +	  while (getline) {
  1.1361 +		if (NF == 0) { c = -c; continue }
  1.1362 +		if (c > 0) print
  1.1363 +	  }
  1.1364 +	}' $wxfile > $FLIST
  1.1365 +
  1.1366 +	print " Done."
  1.1367 +}
  1.1368 +
  1.1369 +#
  1.1370 +# flist_from_teamware [ <args-to-putback-n> ]
  1.1371 +#
  1.1372 +# Generate the file list by extracting file names from a putback -n.  Some
  1.1373 +# names may come from the "update/create" messages and others from the
  1.1374 +# "currently checked out" warning.  Renames are detected here too.  Extract
  1.1375 +# values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback
  1.1376 +# -n as well, but remove them if they are already defined.
  1.1377 +#
  1.1378 +function flist_from_teamware
  1.1379 +{
  1.1380 +	if [[ -n $codemgr_parent ]]; then
  1.1381 +		if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then
  1.1382 +			print -u2 "parent $codemgr_parent doesn't look like a" \
  1.1383 +			    "valid teamware workspace"
  1.1384 +			exit 1
  1.1385 +		fi
  1.1386 +		parent_args="-p $codemgr_parent"
  1.1387 +	fi
  1.1388 +
  1.1389 +	print " File list from: 'putback -n $parent_args $*' ... \c"
  1.1390 +
  1.1391 +	putback -n $parent_args $* 2>&1 |
  1.1392 +	    $AWK '
  1.1393 +		/^update:|^create:/	{print $2}
  1.1394 +		/^Parent workspace:/	{printf("CODEMGR_PARENT=%s\n",$3)}
  1.1395 +		/^Child workspace:/	{printf("CODEMGR_WS=%s\n",$3)}
  1.1396 +		/^The following files are currently checked out/ {p = 1; next}
  1.1397 +		NF == 0			{p=0 ; next}
  1.1398 +		/^rename/		{old=$3}
  1.1399 +		$1 == "to:"		{print $2, old}
  1.1400 +		/^"/			{next}
  1.1401 +		p == 1			{print $1}' |
  1.1402 +	    sort -r -k 1,1 -u | sort > $FLIST
  1.1403 +
  1.1404 +	print " Done."
  1.1405 +}
  1.1406 +
  1.1407 +function outgoing_from_mercurial_forest
  1.1408 +{
  1.1409 +    hg foutgoing --template 'rev: {rev}\n' $OUTPWS | $FILTER | $AWK '
  1.1410 +        BEGIN           {ntree=0}
  1.1411 +        /^comparing/    {next}
  1.1412 +        /^no changes/   {next}
  1.1413 +        /^searching/    {next}
  1.1414 +	/^\[.*\]$/	{tree=substr($1,2,length($1)-2);
  1.1415 +                         trees[ntree++] = tree;
  1.1416 +                         revs[tree]=-1;
  1.1417 +                         next}
  1.1418 +        /^rev:/   {rev=$2+0;
  1.1419 +                   if (revs[tree] == -1 || rev < revs[tree])
  1.1420 +                        { revs[tree] = rev; };
  1.1421 +                  next;}
  1.1422 +        END       {for (tree in trees)
  1.1423 +                        { rev=revs[trees[tree]];
  1.1424 +                          if (rev > 0) 
  1.1425 +                                {printf("%s %d\n",trees[tree],rev-1)}
  1.1426 +                        }}' | while read LINE
  1.1427 +    do
  1.1428 +        set - $LINE
  1.1429 +        TREE=$1
  1.1430 +        REV=$2
  1.1431 +        A=`hg -R $CWS/$TREE log --rev $REV --template '{node}'`
  1.1432 +        FSTAT_OPT="--rev $A"
  1.1433 +        print "Revision: $A $REV" >> $FLIST
  1.1434 +        treestatus $TREE
  1.1435 +    done
  1.1436 +}
  1.1437 +
  1.1438 +function flist_from_mercurial_forest
  1.1439 +{
  1.1440 +    rm -f $FLIST
  1.1441 +    if [ -z "$Nflag" ]; then
  1.1442 +	print " File list from hg foutgoing $PWS ..."
  1.1443 +        outgoing_from_mercurial_forest
  1.1444 +        HG_LIST_FROM_COMMIT=1
  1.1445 +    fi
  1.1446 +    if [ ! -f $FLIST ]; then
  1.1447 +        # hg commit hasn't been run see what is lying around
  1.1448 +	print "\n No outgoing, perhaps you haven't commited."
  1.1449 +	print " File list from hg fstatus -mard ...\c"
  1.1450 +        FSTAT_OPT=
  1.1451 +        fstatus
  1.1452 +        HG_LIST_FROM_COMMIT=0
  1.1453 +    fi
  1.1454 +    print " Done."
  1.1455 +}
  1.1456 +
  1.1457 +#
  1.1458 +# Used when dealing with the result of 'hg foutgoing'
  1.1459 +# When now go down the tree and generate the change list
  1.1460 +#
  1.1461 +function treestatus
  1.1462 +{
  1.1463 +    TREE=$1
  1.1464 +    HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT"
  1.1465 +    
  1.1466 +    $HGCMD -mdn 2>/dev/null | $FILTER | while read F
  1.1467 +    do
  1.1468 +        echo $TREE/$F
  1.1469 +    done >> $FLIST
  1.1470 +
  1.1471 +    # Then all the added files
  1.1472 +    # But some of these could have been "moved" or renamed ones
  1.1473 +    # so let's make sure we get the proper info
  1.1474 +    # hg status -aC will produce something like:
  1.1475 +    #	A subdir/File3
  1.1476 +    #	A subdir/File4
  1.1477 +    #	  File4
  1.1478 +    #	A subdir/File5
  1.1479 +    # The first and last are simple addition while the middle one
  1.1480 +    # is a move/rename
  1.1481 +
  1.1482 +    $HGCMD -aC | $FILTER | while read LINE; do
  1.1483 +	ldone=""
  1.1484 +	while [ -z "$ldone" ]; do
  1.1485 +	    ldone="1"
  1.1486 +	    set - $LINE
  1.1487 +	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1.1488 +		AFILE=$2
  1.1489 +		if read LINE2; then
  1.1490 +		    set - $LINE2
  1.1491 +		    if [ $# -eq 1 ]; then
  1.1492 +			echo $TREE/$AFILE $TREE/$1 >>$FLIST
  1.1493 +		    elif [ $# -eq 2 ]; then
  1.1494 +			echo $TREE/$AFILE >>$FLIST
  1.1495 +			LINE=$LINE2
  1.1496 +			ldone=""
  1.1497 +		    fi
  1.1498 +		else
  1.1499 +		    echo $TREE/$AFILE >>$FLIST
  1.1500 +		fi
  1.1501 +	    fi
  1.1502 +	done
  1.1503 +    done
  1.1504 +    $HGCMD -rn | $FILTER | while read RFILE; do
  1.1505 +	grep "$TREE/$RFILE" $FLIST >/dev/null
  1.1506 +	if [ $? -eq 1 ]; then
  1.1507 +	    echo $TREE/$RFILE >>$FLIST
  1.1508 +	fi
  1.1509 +    done
  1.1510 +}
  1.1511 +
  1.1512 +function fstatus
  1.1513 +{
  1.1514 +    #
  1.1515 +    # forest extension is still being changed. For instance the output
  1.1516 +    # of fstatus used to no prepend the tree path to filenames, but
  1.1517 +    # this has changed recently. AWK code below does try to handle both
  1.1518 +    # cases
  1.1519 +    #
  1.1520 +    hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1.1521 +	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1.1522 +	$1 != ""	{n=index($1,tree);
  1.1523 +			 if (n == 0)
  1.1524 +				{ printf("%s/%s\n",tree,$1)}
  1.1525 +			 else
  1.1526 +				{ printf("%s\n",$1)}}' >> $FLIST
  1.1527 +
  1.1528 +    #
  1.1529 +    # There is a bug in the output of fstatus -aC on recent versions: it
  1.1530 +    # inserts a space between the name of the tree and the filename of the
  1.1531 +    # old file. e.g.:
  1.1532 +    #
  1.1533 +    # $ hg fstatus -aC
  1.1534 +    # [.]
  1.1535 +    #
  1.1536 +    # [MyWS]
  1.1537 +    # A MyWS/subdir/File2
  1.1538 +    #  MyWS/ File2
  1.1539 +    #
  1.1540 +    # [MyWS2]
  1.1541 +    #
  1.1542 +
  1.1543 +    hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1.1544 +	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1.1545 +	/^A .*/		{n=index($2,tree);
  1.1546 +			 if (n == 0)
  1.1547 +				{ printf("A %s/%s\n",tree,$2)}
  1.1548 +			 else
  1.1549 +				{ printf("A %s\n",$2)}; 
  1.1550 +			 next}
  1.1551 +	/^ /		{n=index($1,tree);
  1.1552 +			 if (n == 0)
  1.1553 +				{ printf("%s/%s\n",tree,$1)}
  1.1554 +			 else
  1.1555 +				{ if (NF == 2)
  1.1556 +					printf("%s/%s\n",tree,$2)
  1.1557 +				  else
  1.1558 +					printf("%s\n",$1)
  1.1559 +				};
  1.1560 +			 next}
  1.1561 +	' | while read LINE; do
  1.1562 +	ldone=""
  1.1563 +	while [ -z "$ldone" ]; do
  1.1564 +	    ldone="1"
  1.1565 +	    set - $LINE
  1.1566 +	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1.1567 +		AFILE=$2
  1.1568 +		if read LINE2; then
  1.1569 +		    set - $LINE2
  1.1570 +		    if [ $# -eq 1 ]; then
  1.1571 +			echo $AFILE $1 >>$FLIST
  1.1572 +		    elif [ $# -eq 2 ]; then
  1.1573 +			echo $AFILE >>$FLIST
  1.1574 +			LINE=$LINE2
  1.1575 +			ldone=""
  1.1576 +		    fi
  1.1577 +		else
  1.1578 +		    echo $AFILE >>$FLIST
  1.1579 +		fi
  1.1580 +	    fi
  1.1581 +	done
  1.1582 +    done
  1.1583 +    hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1.1584 +	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1.1585 +	$1 != ""	{n=index($1,tree);
  1.1586 +			 if (n == 0)
  1.1587 +				{ printf("%s/%s\n",tree,$1)}
  1.1588 +			 else
  1.1589 +				{ printf("%s\n",$1)}}' | while read RFILE; do
  1.1590 +	grep "$RFILE" $FLIST >/dev/null
  1.1591 +	if [ $? -eq 1 ]; then
  1.1592 +	    echo $RFILE >>$FLIST
  1.1593 +	fi
  1.1594 +    done
  1.1595 +}
  1.1596 +
  1.1597 +#
  1.1598 +# flist_from_mercurial $PWS
  1.1599 +#
  1.1600 +# Only local file based repositories are supported at present
  1.1601 +# since even though we can determine the list from the parent finding
  1.1602 +# the changes is harder.
  1.1603 +#
  1.1604 +# We first look for any outgoing files, this is for when the user has
  1.1605 +# run hg commit.  If we don't find any then we look with hg status.
  1.1606 +#
  1.1607 +# We need at least one of default-push or default paths set in .hg/hgrc
  1.1608 +# If neither are set we don't know who to compare with.
  1.1609 +
  1.1610 +function flist_from_mercurial 
  1.1611 +{
  1.1612 +#	if [ "${PWS##ssh://}" != "$PWS" -o \
  1.1613 +#	     "${PWS##http://}" != "$PWS" -o \
  1.1614 +#	     "${PWS##https://}" != "$PWS" ]; then
  1.1615 +#		print "Remote Mercurial repositories not currently supported."
  1.1616 +#		print "Set default and/or default-push to a local repository"
  1.1617 +#		exit
  1.1618 +#	fi
  1.1619 +    if [[ -n $forestflag ]]; then
  1.1620 +        HG_LIST_FROM_COMMIT=
  1.1621 +	flist_from_mercurial_forest
  1.1622 +    else
  1.1623 +        STATUS_REV=
  1.1624 +        if [[ -n $rflag ]]; then
  1.1625 +            STATUS_REV="--rev $PARENT_REV"
  1.1626 +        elif [[ -n $OUTREV ]]; then
  1.1627 +            STATUS_REV="--rev $OUTREV"
  1.1628 +        else
  1.1629 +            # hg commit hasn't been run see what is lying around
  1.1630 +            print "\n No outgoing, perhaps you haven't commited."
  1.1631 +        fi
  1.1632 +	# First let's list all the modified or deleted files
  1.1633 +
  1.1634 +	hg status $STATUS_REV -mdn | $FILTER > $FLIST
  1.1635 +
  1.1636 +	# Then all the added files
  1.1637 +	# But some of these could have been "moved" or renamed ones
  1.1638 +	# so let's make sure we get the proper info
  1.1639 +	# hg status -aC will produce something like:
  1.1640 +	#	A subdir/File3
  1.1641 +	#	A subdir/File4
  1.1642 +	#	  File4
  1.1643 +	#	A subdir/File5
  1.1644 +	# The first and last are simple addition while the middle one
  1.1645 +	# is a move/rename
  1.1646 +
  1.1647 +	hg status $STATUS_REV -aC | $FILTER >$FLIST.temp
  1.1648 +	while read LINE; do
  1.1649 +	    ldone=""
  1.1650 +	    while [ -z "$ldone" ]; do
  1.1651 +		ldone="1"
  1.1652 +		set - $LINE
  1.1653 +		if [ $# -eq 2 -a "$1" == "A" ]; then
  1.1654 +		    AFILE=$2
  1.1655 +		    if read LINE2; then
  1.1656 +			set - $LINE2
  1.1657 +			if [ $# -eq 1 ]; then
  1.1658 +			    echo $AFILE $1 >>$FLIST
  1.1659 +			elif [ $# -eq 2 ]; then
  1.1660 +			    echo $AFILE >>$FLIST
  1.1661 +			    LINE=$LINE2
  1.1662 +			    ldone=""
  1.1663 +			fi
  1.1664 +		    else
  1.1665 +			echo $AFILE >>$FLIST
  1.1666 +		    fi
  1.1667 +		fi
  1.1668 +	    done
  1.1669 +	done < $FLIST.temp
  1.1670 +	hg status $STATUS_REV -rn | $FILTER > $FLIST.temp
  1.1671 +	while read RFILE; do
  1.1672 +	    grep "$RFILE" $FLIST >/dev/null
  1.1673 +	    if [ $? -eq 1 ]; then
  1.1674 +		echo $RFILE >>$FLIST
  1.1675 +	    fi
  1.1676 +	done < $FLIST.temp
  1.1677 +	rm -f $FLIST.temp
  1.1678 +    fi
  1.1679 +}
  1.1680 +
  1.1681 +function env_from_flist
  1.1682 +{
  1.1683 +	[[ -r $FLIST ]] || return
  1.1684 +
  1.1685 +	#
  1.1686 +	# Use "eval" to set env variables that are listed in the file
  1.1687 +	# list.  Then copy those into our local versions of those
  1.1688 +	# variables if they have not been set already.
  1.1689 +	#
  1.1690 +	eval `sed -e "s/#.*$//" $FLIST | grep = `
  1.1691 +
  1.1692 +	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
  1.1693 +
  1.1694 +	#
  1.1695 +	# Check to see if CODEMGR_PARENT is set in the flist file.
  1.1696 +	#
  1.1697 +	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
  1.1698 +	    codemgr_parent=$CODEMGR_PARENT
  1.1699 +}
  1.1700 +
  1.1701 +#
  1.1702 +# detect_scm
  1.1703 +#
  1.1704 +# We dynamically test the SCM type; this allows future extensions to
  1.1705 +# new SCM types
  1.1706 +#
  1.1707 +function detect_scm
  1.1708 +{
  1.1709 +	#
  1.1710 +	# If CODEMGR_WS is specified in the flist file, we assume teamware.
  1.1711 +	#
  1.1712 +	if [[ -r $FLIST ]]; then
  1.1713 +		egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1
  1.1714 +		if [[ $? -eq 0 ]]; then
  1.1715 +			print "teamware"
  1.1716 +			return
  1.1717 +		fi
  1.1718 +	fi
  1.1719 +
  1.1720 +	#
  1.1721 +	# The presence of $CODEMGR_WS and a Codemgr_wsdata directory
  1.1722 +	# is our clue that this is a teamware workspace.
  1.1723 +	# Same if true if current directory has a Codemgr_wsdata sub-dir
  1.1724 +	#
  1.1725 +	if [[ -z "$CODEMGR_WS" ]]; then
  1.1726 +	    CODEMGR_WS=`workspace name 2>/dev/null`
  1.1727 +	fi
  1.1728 +
  1.1729 +	if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then
  1.1730 +		print "teamware"
  1.1731 +	elif [[ -d $PWD/Codemgr_wsdata ]]; then
  1.1732 +		print "teamware"
  1.1733 +	elif hg root >/dev/null ; then
  1.1734 +		print "mercurial"
  1.1735 +	else
  1.1736 +		print "unknown"
  1.1737 +	fi
  1.1738 +}
  1.1739 +
  1.1740 +#
  1.1741 +# Extract the parent workspace from the Codemgr_wsdata/parent file
  1.1742 +#
  1.1743 +function parent_from_teamware
  1.1744 +{
  1.1745 +    if [[ -f "$1/Codemgr_wsdata/parent" ]]; then
  1.1746 +	tail -1 "$1/Codemgr_wsdata/parent"
  1.1747 +    fi
  1.1748 +}
  1.1749 +
  1.1750 +function look_for_prog
  1.1751 +{
  1.1752 +	typeset path
  1.1753 +	typeset ppath
  1.1754 +	typeset progname=$1
  1.1755 +
  1.1756 +	DEVTOOLS=
  1.1757 +	OS=`uname`
  1.1758 +	if [[ "$OS" == "SunOS" ]]; then
  1.1759 +	    DEVTOOLS="/java/devtools/`uname -p`/bin"
  1.1760 +	elif [[ "$OS" == "Linux" ]]; then
  1.1761 +	    DEVTOOLS="/java/devtools/linux/bin"
  1.1762 +	fi
  1.1763 +	    
  1.1764 +	ppath=$PATH
  1.1765 +	ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
  1.1766 +	ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
  1.1767 +	ppath=$ppath:/opt/onbld/bin/`uname -p`
  1.1768 +	ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS
  1.1769 +
  1.1770 +	PATH=$ppath prog=`whence $progname`
  1.1771 +	if [[ -n $prog ]]; then
  1.1772 +		print $prog
  1.1773 +	fi
  1.1774 +}
  1.1775 +
  1.1776 +function build_old_new_teamware
  1.1777 +{
  1.1778 +	# If the child's version doesn't exist then
  1.1779 +	# get a readonly copy.
  1.1780 +
  1.1781 +	if [[ ! -f $F && -f SCCS/s.$F ]]; then
  1.1782 +		$SCCS get -s $F
  1.1783 +	fi
  1.1784 +
  1.1785 +	#
  1.1786 +	# Snag new version of file.
  1.1787 +	#
  1.1788 +	rm -f $newdir/$DIR/$F
  1.1789 +	cp $F $newdir/$DIR/$F
  1.1790 +
  1.1791 +	#
  1.1792 +	# Get the parent's version of the file. First see whether the
  1.1793 +	# child's version is checked out and get the parent's version
  1.1794 +	# with keywords expanded or unexpanded as appropriate.
  1.1795 +	#
  1.1796 +	if [ -f $PWS/$PDIR/SCCS/s.$PF -o \
  1.1797 +	    -f $PWS/$PDIR/SCCS/p.$PF ]; then
  1.1798 +		rm -f $olddir/$PDIR/$PF
  1.1799 +		if [ -f SCCS/p.$F ]; then
  1.1800 +			$SCCS get -s -p -k $PWS/$PDIR/$PF \
  1.1801 +			    > $olddir/$PDIR/$PF
  1.1802 +		else
  1.1803 +			$SCCS get -s -p    $PWS/$PDIR/$PF \
  1.1804 +			    > $olddir/$PDIR/$PF
  1.1805 +		fi
  1.1806 +	else
  1.1807 +		if [[ -f $PWS/$PDIR/$PF ]]; then
  1.1808 +			# Parent is not a real workspace, but just a raw
  1.1809 +			# directory tree - use the file that's there as
  1.1810 +			# the old file.
  1.1811 +
  1.1812 +			rm -f $olddir/$DIR/$F
  1.1813 +			cp $PWS/$PDIR/$PF $olddir/$DIR/$F
  1.1814 +		fi
  1.1815 +	fi
  1.1816 +}
  1.1817 +
  1.1818 +#
  1.1819 +# Find the parent for $1
  1.1820 +#
  1.1821 +function find_outrev
  1.1822 +{
  1.1823 +    crev=$1
  1.1824 +    prev=`hg log -r $crev --template '{parents}\n'`
  1.1825 +    if [[ -z "$prev" ]]
  1.1826 +    then
  1.1827 +	# No specific parent means previous changeset is parent
  1.1828 +	prev=`expr $crev - 1`
  1.1829 +    else
  1.1830 +	# Format is either of the following two:
  1.1831 +	# 546:7df6fcf1183b
  1.1832 +	# 548:16f1915bb5cd 547:ffaa4e775815
  1.1833 +	prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'`
  1.1834 +    fi
  1.1835 +    print $prev
  1.1836 +}
  1.1837 +
  1.1838 +function extract_ssh_infos
  1.1839 +{
  1.1840 +    CMD=$1
  1.1841 +    if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then
  1.1842 +	ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'`
  1.1843 +	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'`
  1.1844 +	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'`
  1.1845 +    else
  1.1846 +	ssh_user=
  1.1847 +	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'`
  1.1848 +	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'`
  1.1849 +    fi
  1.1850 +    
  1.1851 +}
  1.1852 +
  1.1853 +function build_old_new_mercurial
  1.1854 +{
  1.1855 +	olddir=$1
  1.1856 +	newdir=$2
  1.1857 +	DIR=$3
  1.1858 +	F=$4
  1.1859 +	#
  1.1860 +	# new version of the file.
  1.1861 +	#
  1.1862 +	rm -rf $newdir/$DIR/$F
  1.1863 +	if [ -f $F ]; then
  1.1864 +	    cp $F  $newdir/$DIR/$F
  1.1865 +	fi
  1.1866 +
  1.1867 +	#
  1.1868 +	# Old version of the file.
  1.1869 +	#
  1.1870 +	rm -rf $olddir/$DIR/$F
  1.1871 +
  1.1872 +	if [ -n "$PWS" ]; then
  1.1873 +	    if expr "$PWS" : 'ssh://' >/dev/null
  1.1874 +	    then
  1.1875 +		extract_ssh_infos $PWS
  1.1876 +		if [ -n "$ssh_user" ]; then
  1.1877 +		    parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1.1878 +		else
  1.1879 +		    parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1.1880 +		fi
  1.1881 +	    else
  1.1882 +		parent="hg -R $PWS --cwd $PWS"
  1.1883 +	    fi
  1.1884 +	else
  1.1885 +	    parent=""
  1.1886 +	fi
  1.1887 +
  1.1888 +	if [ -z "$rename" ]; then
  1.1889 +	    if [ -n "$rflag" ]; then
  1.1890 +		parentrev=$PARENT_REV
  1.1891 +	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1.1892 +                parentrev=$OUTREV
  1.1893 +	    else
  1.1894 +                if [[ -n $HG_BRANCH ]]; then
  1.1895 +                    parentrev=$HG_BRANCH
  1.1896 +                else
  1.1897 +		    parentrev="tip"
  1.1898 +                fi
  1.1899 +	    fi
  1.1900 +
  1.1901 +	    if [ -n "$parentrev" ]; then
  1.1902 +		if [ -z "$parent" ]; then
  1.1903 +		    hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null
  1.1904 +		else
  1.1905 +		    # when specifying a workspace we have to provide
  1.1906 +		    # the full path
  1.1907 +		    $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null
  1.1908 +		fi
  1.1909 +	    fi
  1.1910 +	else
  1.1911 +	    # It's a rename (or a move), so let's make sure we move
  1.1912 +	    # to the right directory first, then restore it once done
  1.1913 +	    current_dir=`pwd`
  1.1914 +	    cd $CWS/$PDIR
  1.1915 +	    if [ -n "$rflag" ]; then
  1.1916 +		parentrev=$PARENT_REV
  1.1917 +	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1.1918 +                parentrev=$OUTREV
  1.1919 +	    fi
  1.1920 +	    if [ -z "$parentrev" ]; then
  1.1921 +		parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'`
  1.1922 +	    fi
  1.1923 +	    if [ -n "$parentrev" ]; then
  1.1924 +		mkdir -p $olddir/$PDIR
  1.1925 +		if [ -z "$parent" ]; then
  1.1926 +		    hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null
  1.1927 +		else
  1.1928 +		    $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
  1.1929 +		fi
  1.1930 +	    fi
  1.1931 +	    cd $current_dir
  1.1932 +	fi
  1.1933 +}
  1.1934 +
  1.1935 +function build_old_new
  1.1936 +{
  1.1937 +	if [[ $SCM_MODE == "teamware" ]]; then
  1.1938 +		build_old_new_teamware $@
  1.1939 +	fi
  1.1940 +
  1.1941 +	if [[ $SCM_MODE == "mercurial" ]]; then
  1.1942 +		build_old_new_mercurial $@
  1.1943 +	fi
  1.1944 +}
  1.1945 +
  1.1946 +
  1.1947 +#
  1.1948 +# Usage message.
  1.1949 +#
  1.1950 +function usage
  1.1951 +{
  1.1952 +	print "Usage:\twebrev [common-options]
  1.1953 +	webrev [common-options] ( <file> | - )
  1.1954 +	webrev [common-options] -w <wx file>
  1.1955 +	webrev [common-options] -l [arguments to 'putback']
  1.1956 +
  1.1957 +Options:
  1.1958 +	-v: Print the version of this tool.
  1.1959 +        -b: Do not ignore changes in the amount of white space.
  1.1960 +        -c <CR#>: Include link to CR (aka bugid) in the main page.
  1.1961 +	-O: Print bugids/arc cases suitable for OpenJDK.
  1.1962 +	-i <filename>: Include <filename> in the index.html file.
  1.1963 +	-o <outdir>: Output webrev to specified directory.
  1.1964 +	-p <compare-against>: Use specified parent wkspc or basis for comparison
  1.1965 +	-w <wxfile>: Use specified wx active file.
  1.1966 +        -u <username>: Use that username instead of 'guessing' one.
  1.1967 +	-m: Forces the use of Mercurial
  1.1968 +	-t: Forces the use of Teamware
  1.1969 +
  1.1970 +Mercurial only options:
  1.1971 +	-r rev: Compare against a specified revision
  1.1972 +	-N: Skip 'hg outgoing', use only 'hg status'
  1.1973 +	-f: Use the forest extension
  1.1974 +
  1.1975 +Environment:
  1.1976 +	WDIR: Control the output directory.
  1.1977 +	WEBREV_BUGURL: Control the URL prefix for bugids.
  1.1978 +	WEBREV_SACURL: Control the URL prefix for ARC cases.
  1.1979 +
  1.1980 +SCM Environment:
  1.1981 +	Teamware: CODEMGR_WS: Workspace location.
  1.1982 +	Teamware: CODEMGR_PARENT: Parent workspace location.
  1.1983 +
  1.1984 +"
  1.1985 +
  1.1986 +	exit 2
  1.1987 +}
  1.1988 +
  1.1989 +#
  1.1990 +#
  1.1991 +# Main program starts here
  1.1992 +#
  1.1993 +#
  1.1994 +LANG="C"
  1.1995 +LC_ALL="C"
  1.1996 +export LANG LC_ALL
  1.1997 +trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
  1.1998 +
  1.1999 +set +o noclobber
  1.2000 +
  1.2001 +[[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
  1.2002 +[[ -z $WX ]] && WX=`look_for_prog wx`
  1.2003 +[[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
  1.2004 +[[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
  1.2005 +[[ -z $PERL ]] && PERL=`look_for_prog perl`
  1.2006 +[[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
  1.2007 +[[ -z $AWK ]] && AWK=`look_for_prog nawk`
  1.2008 +[[ -z $AWK ]] && AWK=`look_for_prog gawk`
  1.2009 +[[ -z $AWK ]] && AWK=`look_for_prog awk`
  1.2010 +[[ -z $WSPACE ]] && WSPACE=`look_for_prog workspace`
  1.2011 +[[ -z $JAR ]] && JAR=`look_for_prog jar`
  1.2012 +[[ -z $ZIP ]] && ZIP=`look_for_prog zip`
  1.2013 +[[ -z $GETENT ]] && GETENT=`look_for_prog getent`
  1.2014 +[[ -z $WGET ]] && WGET=`look_for_prog wget`
  1.2015 +
  1.2016 +if uname | grep CYGWIN >/dev/null
  1.2017 +then
  1.2018 +        ISWIN=1
  1.2019 +        # Under windows mercurial outputs '\' instead of '/'
  1.2020 +        FILTER="tr '\\\\' '/'"
  1.2021 +else
  1.2022 +        FILTER="cat"
  1.2023 +fi
  1.2024 +
  1.2025 +if [[ ! -x $PERL ]]; then
  1.2026 +	print -u2 "Error: No perl interpreter found.  Exiting."
  1.2027 +	exit 1
  1.2028 +fi
  1.2029 +
  1.2030 +#
  1.2031 +# These aren't fatal, but we want to note them to the user.
  1.2032 +# We don't warn on the absence of 'wx' until later when we've
  1.2033 +# determined that we actually need to try to invoke it.
  1.2034 +#
  1.2035 +# [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
  1.2036 +# [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
  1.2037 +# [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
  1.2038 +
  1.2039 +# Declare global total counters.
  1.2040 +integer TOTL TINS TDEL TMOD TUNC
  1.2041 +
  1.2042 +flist_mode=
  1.2043 +flist_file=
  1.2044 +bflag=
  1.2045 +iflag=
  1.2046 +oflag=
  1.2047 +pflag=
  1.2048 +uflag=
  1.2049 +lflag=
  1.2050 +wflag=
  1.2051 +Oflag=
  1.2052 +rflag=
  1.2053 +Nflag=
  1.2054 +forestflag=
  1.2055 +while getopts "c:i:o:p:r:u:lmtwONvfb" opt
  1.2056 +do
  1.2057 +	case $opt in
  1.2058 +        b)      bflag=1;;
  1.2059 +
  1.2060 +	i)	iflag=1
  1.2061 +		INCLUDE_FILE=$OPTARG;;
  1.2062 +
  1.2063 +	o)	oflag=1
  1.2064 +		WDIR=$OPTARG;;
  1.2065 +
  1.2066 +	p)	pflag=1
  1.2067 +		codemgr_parent=$OPTARG;;
  1.2068 +
  1.2069 +	u)      uflag=1
  1.2070 +		username=$OPTARG;;
  1.2071 +
  1.2072 +        c)      if [[ -z $CRID ]]; then
  1.2073 +                   CRID=$OPTARG
  1.2074 +                else
  1.2075 +                   CRID="$CRID $OPTARG"
  1.2076 +                fi;;
  1.2077 +
  1.2078 +	m)	SCM_MODE="mercurial";;
  1.2079 +
  1.2080 +	t)	SCM_MODE="teamware";;
  1.2081 +
  1.2082 +	#
  1.2083 +	# If -l has been specified, we need to abort further options
  1.2084 +	# processing, because subsequent arguments are going to be
  1.2085 +	# arguments to 'putback -n'.
  1.2086 +	#
  1.2087 +	l)	lflag=1
  1.2088 +		break;;
  1.2089 +
  1.2090 +	w)	wflag=1;;
  1.2091 +
  1.2092 +	O)	Oflag=1;;
  1.2093 +
  1.2094 +	N)	Nflag=1;;
  1.2095 +
  1.2096 +	f)	forestflag=1;;
  1.2097 +
  1.2098 +	r)	rflag=1
  1.2099 +		PARENT_REV=$OPTARG;;
  1.2100 +
  1.2101 +	v)	print "$0 version: $WEBREV_UPDATED";;
  1.2102 +		
  1.2103 +
  1.2104 +	?)	usage;;
  1.2105 +	esac
  1.2106 +done
  1.2107 +
  1.2108 +FLIST=/tmp/$$.flist
  1.2109 +
  1.2110 +if [[ -n $wflag && -n $lflag ]]; then
  1.2111 +	usage
  1.2112 +fi
  1.2113 +
  1.2114 +if [[ -n $forestflag && -n $rflag ]]; then
  1.2115 +    print "The -r <rev> flag is incompatible with the use of forests"
  1.2116 +    exit 2
  1.2117 +fi
  1.2118 +
  1.2119 +#
  1.2120 +# If this manually set as the parent, and it appears to be an earlier webrev,
  1.2121 +# then note that fact and set the parent to the raw_files/new subdirectory.
  1.2122 +#
  1.2123 +if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
  1.2124 +	parent_webrev="$codemgr_parent"
  1.2125 +	codemgr_parent="$codemgr_parent/raw_files/new"
  1.2126 +fi
  1.2127 +
  1.2128 +if [[ -z $wflag && -z $lflag ]]; then
  1.2129 +	shift $(($OPTIND - 1))
  1.2130 +
  1.2131 +	if [[ $1 == "-" ]]; then
  1.2132 +		cat > $FLIST
  1.2133 +		flist_mode="stdin"
  1.2134 +		flist_done=1
  1.2135 +		shift
  1.2136 +	elif [[ -n $1 ]]; then
  1.2137 +		if [[ ! -r $1 ]]; then
  1.2138 +			print -u2 "$1: no such file or not readable"
  1.2139 +			usage
  1.2140 +		fi
  1.2141 +		cat $1 > $FLIST
  1.2142 +		flist_mode="file"
  1.2143 +		flist_file=$1
  1.2144 +		flist_done=1
  1.2145 +		shift
  1.2146 +	else
  1.2147 +		flist_mode="auto"
  1.2148 +	fi
  1.2149 +fi
  1.2150 +
  1.2151 +#
  1.2152 +# Before we go on to further consider -l and -w, work out which SCM we think
  1.2153 +# is in use.
  1.2154 +#
  1.2155 +if [[ -z $SCM_MODE ]]; then
  1.2156 +    SCM_MODE=`detect_scm $FLIST`
  1.2157 +fi
  1.2158 +if [[ $SCM_MODE == "unknown" ]]; then
  1.2159 +	print -u2 "Unable to determine SCM type currently in use."
  1.2160 +	print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in"
  1.2161 +	print -u2 "              the environment or in the file list."
  1.2162 +	print -u2 "For mercurial: webrev runs 'hg root'."
  1.2163 +	exit 1
  1.2164 +fi
  1.2165 +
  1.2166 +print -u2 "   SCM detected: $SCM_MODE"
  1.2167 +
  1.2168 +
  1.2169 +if [[ $SCM_MODE == "mercurial" ]]; then
  1.2170 +    #
  1.2171 +    # determine Workspace and parent workspace paths
  1.2172 +    #
  1.2173 +    CWS=`hg root | $FILTER`
  1.2174 +    if [[ -n $pflag && -z "$PWS" ]]; then
  1.2175 +	OUTPWS=$codemgr_parent
  1.2176 +        # Let's try to expand it if it's an alias defined in [paths]
  1.2177 +        tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  1.2178 +        if [[ -n $tmp ]]; then
  1.2179 +            OUTPWS="$tmp"
  1.2180 +        fi
  1.2181 +        if [[ -n $rflag ]]; then
  1.2182 +	    if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
  1.2183 +	        PWS=$codemgr_parent
  1.2184 +	    else
  1.2185 +	        PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
  1.2186 +	    fi
  1.2187 +        fi
  1.2188 +    fi
  1.2189 +    #
  1.2190 +    # OUTPWS is the parent repository to use when using 'hg outgoing'
  1.2191 +    #
  1.2192 +    if [[ -z $Nflag ]]; then
  1.2193 +        if [[ -n $forestflag ]]; then
  1.2194 +            #
  1.2195 +            # for forest we have to rely on properly set default and
  1.2196 +            # default-push because they can be different from the top one.
  1.2197 +            # unless of course it was explicitely speficied with -p
  1.2198 +            if [[ -z $pflag ]]; then
  1.2199 +                OUTPWS=
  1.2200 +            fi
  1.2201 +        else
  1.2202 +            #
  1.2203 +            # Unfortunately mercurial is bugged and doesn't handle
  1.2204 +            # aliases correctly in 'hg path default'
  1.2205 +            # So let's do it ourselves. Sigh...
  1.2206 +            if [[ -z "$OUTPWS" ]]; then
  1.2207 +                OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  1.2208 +            fi
  1.2209 +            # Still empty, means no default-push
  1.2210 +            if [[ -z "$OUTPWS" ]]; then
  1.2211 +                OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  1.2212 +            fi
  1.2213 +            # Let's try to expand it if it's an alias defined in [paths]
  1.2214 +            tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  1.2215 +            if [[ -n $tmp ]]; then
  1.2216 +                OUTPWS="$tmp"
  1.2217 +            fi
  1.2218 +        fi
  1.2219 +    fi
  1.2220 +    #
  1.2221 +    # OUTPWS may contain username:password, let's make sure we remove the
  1.2222 +    # sensitive information before we print out anything in the HTML
  1.2223 +    #
  1.2224 +    OUTPWS2=$OUTPWS
  1.2225 +    if [[ -n $OUTPWS ]]; then
  1.2226 +	if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
  1.2227 +	    # Remove everything between '://' and '@'
  1.2228 +	    OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
  1.2229 +	fi
  1.2230 +    fi
  1.2231 +
  1.2232 +    if [[ -z $HG_BRANCH ]]; then
  1.2233 +        HG_BRANCH=`hg branch`
  1.2234 +        if [ "$HG_BRANCH" == "default" ]; then
  1.2235 +            #
  1.2236 +            # 'default' means no particular branch, so let's cancel that
  1.2237 +            #
  1.2238 +            HG_BRANCH=
  1.2239 +        fi
  1.2240 +    fi
  1.2241 +
  1.2242 +    if [[ -z $forestflag ]]; then
  1.2243 +        if [[ -z $Nflag ]]; then
  1.2244 +            #
  1.2245 +            # If no "-N", always do "hg outgoing" against parent
  1.2246 +            # repository to determine list of outgoing revisions.
  1.2247 +            #
  1.2248 +            ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
  1.2249 +            if [[ -n $ALL_CREV ]]; then
  1.2250 +                FIRST_CREV=`echo "$ALL_CREV" | head -1`
  1.2251 +                #
  1.2252 +                # If no "-r", choose revision to compare against by
  1.2253 +                # finding the latest revision not in the outgoing list.
  1.2254 +                #
  1.2255 +                if [[ -z $rflag ]]; then
  1.2256 +                    OUTREV=`find_outrev "$FIRST_CREV"`
  1.2257 +                    if [[ -n $OUTREV ]]; then
  1.2258 +                        HG_LIST_FROM_COMMIT=1
  1.2259 +                    fi
  1.2260 +                fi
  1.2261 +            fi
  1.2262 +        elif [[ -n $rflag ]]; then
  1.2263 +            #
  1.2264 +            # If skipping "hg outgoing" but still comparing against a
  1.2265 +            # specific revision (not the tip), set revision for comment
  1.2266 +            # accumulation.
  1.2267 +            #
  1.2268 +            FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
  1.2269 +            FIRST_CREV=`expr $FIRST_CREV + 1`
  1.2270 +        fi
  1.2271 +    fi
  1.2272 +    #Let's check if a merge is needed, if so, issue a warning
  1.2273 +    PREV=`hg parent | grep '^tag:.*tip$'`
  1.2274 +    if [[ -z $PREV ]]; then
  1.2275 +        print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
  1.2276 +    fi
  1.2277 +fi
  1.2278 +
  1.2279 +if [[ -n $lflag ]]; then
  1.2280 +	#
  1.2281 +	# If the -l flag is given instead of the name of a file list,
  1.2282 +	# then generate the file list by extracting file names from a
  1.2283 +	# putback -n.
  1.2284 +	#
  1.2285 +	shift $(($OPTIND - 1))
  1.2286 +	if [[ $SCM_MODE == "teamware" ]]; then
  1.2287 +		flist_from_teamware "$*"
  1.2288 +	elif [[ $SCM_MODE == "mercurial" ]]; then
  1.2289 +		flist_from_mercurial
  1.2290 +	fi
  1.2291 +	flist_done=1
  1.2292 +	shift $#
  1.2293 +
  1.2294 +elif [[ -n $wflag ]]; then
  1.2295 +	#
  1.2296 +	# If the -w is given then assume the file list is in Bonwick's "wx"
  1.2297 +	# command format, i.e.  pathname lines alternating with SCCS comment
  1.2298 +	# lines with blank lines as separators.  Use the SCCS comments later
  1.2299 +	# in building the index.html file.
  1.2300 +	#
  1.2301 +	shift $(($OPTIND - 1))
  1.2302 +	wxfile=$1
  1.2303 +	if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
  1.2304 +		if [[ -r $CODEMGR_WS/wx/active ]]; then
  1.2305 +			wxfile=$CODEMGR_WS/wx/active
  1.2306 +		fi
  1.2307 +	fi
  1.2308 +
  1.2309 +	[[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
  1.2310 +	    "be auto-detected (check \$CODEMGR_WS)" && exit 1
  1.2311 +
  1.2312 +	print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
  1.2313 +	flist_from_wx $wxfile
  1.2314 +	flist_done=1
  1.2315 +	if [[ -n "$*" ]]; then
  1.2316 +		shift
  1.2317 +	fi
  1.2318 +elif [[ $flist_mode == "stdin" ]]; then
  1.2319 +	print -u2 " File list from: standard input"
  1.2320 +elif [[ $flist_mode == "file" ]]; then
  1.2321 +	print -u2 " File list from: $flist_file"
  1.2322 +fi
  1.2323 +
  1.2324 +if [[ $# -gt 0 ]]; then
  1.2325 +	print -u2 "WARNING: unused arguments: $*"
  1.2326 +fi
  1.2327 +
  1.2328 +if [[ $SCM_MODE == "teamware" ]]; then
  1.2329 +	#
  1.2330 +	# Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can
  1.2331 +	# be set in a number of ways, in decreasing precedence:
  1.2332 +	#
  1.2333 +	#      1) on the command line (only for the parent)
  1.2334 +	#      2) in the user environment
  1.2335 +	#      3) in the flist
  1.2336 +	#      4) automatically based on the workspace (only for the parent)
  1.2337 +	#
  1.2338 +
  1.2339 +	#
  1.2340 +	# Here is case (2): the user environment
  1.2341 +	#
  1.2342 +	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
  1.2343 +	[[ -z $codemgr_ws && -n $WSPACE ]] && codemgr_ws=`$WSPACE name`
  1.2344 +	    
  1.2345 +	if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
  1.2346 +		print -u2 "$codemgr_ws: no such workspace"
  1.2347 +		exit 1
  1.2348 +	fi
  1.2349 +
  1.2350 +	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
  1.2351 +	    codemgr_parent=$CODEMGR_PARENT
  1.2352 +
  1.2353 +	if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
  1.2354 +		print -u2 "$codemgr_parent: no such directory"
  1.2355 +		exit 1
  1.2356 +	fi
  1.2357 +
  1.2358 +	#
  1.2359 +	# If we're in auto-detect mode and we haven't already gotten the file
  1.2360 +	# list, then see if we can get it by probing for wx.
  1.2361 +	#
  1.2362 +	if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
  1.2363 +		if [[ ! -x $WX ]]; then
  1.2364 +			print -u2 "WARNING: wx not found!"
  1.2365 +		fi
  1.2366 +
  1.2367 +		#
  1.2368 +		# We need to use wx list -w so that we get renamed files, etc.
  1.2369 +		# but only if a wx active file exists-- otherwise wx will
  1.2370 +		# hang asking us to initialize our wx information.
  1.2371 +		#
  1.2372 +		if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
  1.2373 +			print -u2 " File list from: 'wx list -w' ... \c"
  1.2374 +			$WX list -w > $FLIST
  1.2375 +			$WX comments > /tmp/$$.wx_comments
  1.2376 +			wxfile=/tmp/$$.wx_comments
  1.2377 +			print -u2 "done"
  1.2378 +			flist_done=1
  1.2379 +		fi
  1.2380 +	fi
  1.2381 +
  1.2382 +	#
  1.2383 +	# If by hook or by crook we've gotten a file list by now (perhaps
  1.2384 +	# from the command line), eval it to extract environment variables from
  1.2385 +	# it: This is step (3).
  1.2386 +	#
  1.2387 +	env_from_flist
  1.2388 +
  1.2389 +	#
  1.2390 +	# Continuing step (3): If we still have no file list, we'll try to get
  1.2391 +	# it from teamware.
  1.2392 +	#
  1.2393 +	if [[ -z $flist_done ]]; then
  1.2394 +		flist_from_teamware
  1.2395 +		env_from_flist
  1.2396 +	fi
  1.2397 +
  1.2398 +	if [[ -z $codemgr_ws && -d $PWD/Codemgr_wsdata ]]; then
  1.2399 +	    codemgr_ws=$PWD
  1.2400 +	fi
  1.2401 +	#
  1.2402 +	# Observe true directory name of CODEMGR_WS, as used later in
  1.2403 +	# webrev title.
  1.2404 +	#
  1.2405 +	if [[ -n $codemgr_ws ]]; then
  1.2406 +	    codemgr_ws=$(cd $codemgr_ws;print $PWD)
  1.2407 +	fi
  1.2408 +
  1.2409 +	if [[ -n $codemgr_parent ]]; then
  1.2410 +	    codemgr_parent=$(cd $codemgr_parent;print $PWD)
  1.2411 +	fi
  1.2412 +
  1.2413 +	#
  1.2414 +	# (4) If we still don't have a value for codemgr_parent, get it
  1.2415 +	# from workspace.
  1.2416 +	#
  1.2417 +	[[ -z $codemgr_parent && -n $WSPACE ]] && codemgr_parent=`$WSPACE parent`
  1.2418 +	[[ -z $codemgr_parent ]] && codemgr_parent=`parent_from_teamware $codemgr_ws`
  1.2419 +
  1.2420 +	if [[ ! -d $codemgr_parent ]]; then
  1.2421 +	    print -u2 "$CODEMGR_PARENT: no such parent workspace"
  1.2422 +	    exit 1
  1.2423 +	fi
  1.2424 +
  1.2425 +	#
  1.2426 +	# Reset CODEMGR_WS to make sure teamware commands are happy.
  1.2427 +	#
  1.2428 +	CODEMGR_WS=$codemgr_ws
  1.2429 +	CWS=$codemgr_ws
  1.2430 +	PWS=$codemgr_parent
  1.2431 +elif [[ $SCM_MODE == "mercurial" ]]; then
  1.2432 +    if [[ -z $flist_done ]]; then
  1.2433 +	flist_from_mercurial $PWS
  1.2434 +    fi
  1.2435 +fi
  1.2436 +
  1.2437 +#
  1.2438 +# If the user didn't specify a -i option, check to see if there is a
  1.2439 +# webrev-info file in the workspace directory.
  1.2440 +#
  1.2441 +if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
  1.2442 +	iflag=1
  1.2443 +	INCLUDE_FILE="$CWS/webrev-info"
  1.2444 +fi
  1.2445 +
  1.2446 +if [[ -n $iflag ]]; then
  1.2447 +	if [[ ! -r $INCLUDE_FILE ]]; then
  1.2448 +		print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
  1.2449 +		    "not readable."
  1.2450 +		exit 1
  1.2451 +	else
  1.2452 +		#
  1.2453 +		# $INCLUDE_FILE may be a relative path, and the script alters
  1.2454 +		# PWD, so we just stash a copy in /tmp.
  1.2455 +		#
  1.2456 +		cp $INCLUDE_FILE /tmp/$$.include
  1.2457 +	fi
  1.2458 +fi
  1.2459 +
  1.2460 +#
  1.2461 +# Output directory.
  1.2462 +#
  1.2463 +if [[ -z $WDIR ]]; then
  1.2464 +    WDIR=$CWS/webrev
  1.2465 +else
  1.2466 +    # If the output directory doesn't end with '/webrev' or '/webrev/'
  1.2467 +    # then add '/webrev'. This is for backward compatibility
  1.2468 +    if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
  1.2469 +    then
  1.2470 +	WDIR=$WDIR/webrev
  1.2471 +    fi
  1.2472 +fi
  1.2473 +# WDIR=${WDIR:-$CWS/webrev}
  1.2474 +
  1.2475 +#
  1.2476 +# Name of the webrev, derived from the workspace name; in the
  1.2477 +# future this could potentially be an option.
  1.2478 +#
  1.2479 +# Let's keep what's after the last '/'
  1.2480 +WNAME=${CWS##*/}
  1.2481 +
  1.2482 +#
  1.2483 +# If WDIR doesn't start with '/' or 'x:' prepend the current dir
  1.2484 +#
  1.2485 +if [ ${WDIR%%/*} ]; then
  1.2486 +    if [[ -n $ISWIN ]]; then
  1.2487 +        if [ ${WDIR%%[A-Za-z]:*} ]; then
  1.2488 +	    WDIR=$PWD/$WDIR
  1.2489 +        fi
  1.2490 +    else
  1.2491 +	WDIR=$PWD/$WDIR
  1.2492 +    fi
  1.2493 +fi
  1.2494 +
  1.2495 +if [[ ! -d $WDIR ]]; then
  1.2496 +	mkdir -p $WDIR
  1.2497 +	[[ $? != 0 ]] && exit 1
  1.2498 +fi
  1.2499 +
  1.2500 +#
  1.2501 +# Summarize what we're going to do.
  1.2502 +#
  1.2503 +print "      Workspace: $CWS"
  1.2504 +if [[ -n $parent_webrev ]]; then
  1.2505 +    print "Compare against: webrev at $parent_webrev"
  1.2506 +elif [[ -n $OUTPWS2 ]]; then
  1.2507 +    print "Compare against: $OUTPWS2"
  1.2508 +fi
  1.2509 +if [[ -n $HG_BRANCH ]]; then
  1.2510 +    print "         Branch: $HG_BRANCH"
  1.2511 +fi
  1.2512 +if [[ -n $rflag ]]; then
  1.2513 +        print "Compare against version: $PARENT_REV"
  1.2514 +fi
  1.2515 +[[ -n $INCLUDE_FILE ]] && print "      Including: $INCLUDE_FILE"
  1.2516 +print "      Output to: $WDIR"
  1.2517 +
  1.2518 +#
  1.2519 +# Save the file list in the webrev dir
  1.2520 +#
  1.2521 +[[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
  1.2522 +
  1.2523 +#
  1.2524 +#    Bug IDs will be replaced by a URL.  Order of precedence
  1.2525 +#    is: default location, $WEBREV_BUGURL, the -O flag.
  1.2526 +#
  1.2527 +BUGURL='http://monaco.sfbay.sun.com/detail.jsp?cr='
  1.2528 +[[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
  1.2529 +[[ -n "$Oflag" ]] && \
  1.2530 +    BUGURL='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id='
  1.2531 +
  1.2532 +#
  1.2533 +#    Likewise, ARC cases will be replaced by a URL.  Order of precedence
  1.2534 +#    is: default, $WEBREV_SACURL, the -O flag.
  1.2535 +#
  1.2536 +#    Note that -O also triggers different substitution behavior for
  1.2537 +#    SACURL.  See sac2url().
  1.2538 +#
  1.2539 +SACURL='http://sac.eng.sun.com'
  1.2540 +[[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL"
  1.2541 +[[ -n $Oflag ]] && \
  1.2542 +    SACURL='http://www.opensolaris.org/os/community/arc/caselog'
  1.2543 +
  1.2544 +rm -f $WDIR/$WNAME.patch
  1.2545 +rm -f $WDIR/$WNAME.ps
  1.2546 +rm -f $WDIR/$WNAME.pdf
  1.2547 +
  1.2548 +touch $WDIR/$WNAME.patch
  1.2549 +
  1.2550 +print "   Output Files:"
  1.2551 +
  1.2552 +#
  1.2553 +# Clean up the file list: Remove comments, blank lines and env variables.
  1.2554 +#
  1.2555 +sed -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
  1.2556 +FLIST=/tmp/$$.flist.clean
  1.2557 +
  1.2558 +#
  1.2559 +# Clean up residual raw files
  1.2560 +#
  1.2561 +if [ -d $WDIR/raw_files ]; then
  1.2562 +    rm -rf $WDIR/raw_files 2>/dev/null
  1.2563 +fi
  1.2564 +
  1.2565 +#
  1.2566 +# Should we ignore changes in white spaces when generating diffs?
  1.2567 +# 
  1.2568 +if [[ -n $bflag ]]; then
  1.2569 +    DIFFOPTS="-t"
  1.2570 +else
  1.2571 +    DIFFOPTS="-bt"
  1.2572 +fi
  1.2573 +#
  1.2574 +# First pass through the files: generate the per-file webrev HTML-files.
  1.2575 +#
  1.2576 +while read LINE
  1.2577 +do
  1.2578 +	set - $LINE
  1.2579 +	P=$1
  1.2580 +
  1.2581 +        if [[ $1 == "Revision:" ]]; then
  1.2582 +            OUTREV=$2
  1.2583 +            continue
  1.2584 +        fi
  1.2585 +	#
  1.2586 +	# Normally, each line in the file list is just a pathname of a
  1.2587 +	# file that has been modified or created in the child.  A file
  1.2588 +	# that is renamed in the child workspace has two names on the
  1.2589 +	# line: new name followed by the old name.
  1.2590 +	#
  1.2591 +	oldname=""
  1.2592 +	oldpath=""
  1.2593 +	rename=
  1.2594 +	if [[ $# -eq 2 ]]; then
  1.2595 +		PP=$2			# old filename
  1.2596 +		oldname=" (was $PP)"
  1.2597 +		oldpath="$PP"
  1.2598 +		rename=1
  1.2599 +        	PDIR=${PP%/*}
  1.2600 +        	if [[ $PDIR == $PP ]]; then
  1.2601 +			PDIR="."   # File at root of workspace
  1.2602 +		fi
  1.2603 +
  1.2604 +		PF=${PP##*/}
  1.2605 +
  1.2606 +	        DIR=${P%/*}
  1.2607 +	        if [[ $DIR == $P ]]; then
  1.2608 +			DIR="."   # File at root of workspace
  1.2609 +		fi
  1.2610 +
  1.2611 +		F=${P##*/}
  1.2612 +        else
  1.2613 +	        DIR=${P%/*}
  1.2614 +	        if [[ "$DIR" == "$P" ]]; then
  1.2615 +			DIR="."   # File at root of workspace
  1.2616 +		fi
  1.2617 +
  1.2618 +		F=${P##*/}
  1.2619 +
  1.2620 +		PP=$P
  1.2621 +		PDIR=$DIR
  1.2622 +		PF=$F
  1.2623 +	fi
  1.2624 +
  1.2625 +        # Make the webrev directory if necessary as it may have been
  1.2626 +        # removed because it was empty
  1.2627 +        if [ ! -d $CWS/$DIR ]; then
  1.2628 +	    mkdir -p $CWS/$DIR
  1.2629 +        fi
  1.2630 +
  1.2631 +	COMM=`getcomments html $P $PP`
  1.2632 +
  1.2633 +	print "\t$P$oldname\n\t\t\c"
  1.2634 +
  1.2635 +	# Make the webrev mirror directory if necessary
  1.2636 +	mkdir -p $WDIR/$DIR
  1.2637 +
  1.2638 +	# cd to the directory so the names are short
  1.2639 +	cd $CWS/$DIR
  1.2640 +
  1.2641 +	#
  1.2642 +	# If we're in OpenSolaris mode, we enforce a minor policy:
  1.2643 +	# help to make sure the reviewer doesn't accidentally publish
  1.2644 +	# source which is in usr/closed/*
  1.2645 +	#
  1.2646 +	if [[ -n $Oflag ]]; then
  1.2647 +		pclosed=${P##usr/closed/}
  1.2648 +		if [[ $pclosed != $P ]]; then
  1.2649 +			print "*** Omitting closed source for OpenSolaris" \
  1.2650 +			    "mode review"
  1.2651 +			continue
  1.2652 +		fi
  1.2653 +	fi
  1.2654 +
  1.2655 +	#
  1.2656 +	# We stash old and new files into parallel directories in /tmp
  1.2657 +	# and do our diffs there.  This makes it possible to generate
  1.2658 +	# clean looking diffs which don't have absolute paths present.
  1.2659 +	#
  1.2660 +	olddir=$WDIR/raw_files/old
  1.2661 +	newdir=$WDIR/raw_files/new
  1.2662 +	mkdir -p $olddir
  1.2663 +	mkdir -p $newdir
  1.2664 +	mkdir -p $olddir/$PDIR
  1.2665 +	mkdir -p $newdir/$DIR
  1.2666 +
  1.2667 +	build_old_new $olddir $newdir $DIR $F
  1.2668 +
  1.2669 +	if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
  1.2670 +		print "*** Error: file not in parent or child"
  1.2671 +		continue
  1.2672 +	fi
  1.2673 +
  1.2674 +	cd $WDIR/raw_files
  1.2675 +	ofile=old/$PDIR/$PF
  1.2676 +	nfile=new/$DIR/$F
  1.2677 +
  1.2678 +	mv_but_nodiff=
  1.2679 +	cmp $ofile $nfile > /dev/null 2>&1
  1.2680 +	if [[ $? == 0 && $rename == 1 ]]; then
  1.2681 +		mv_but_nodiff=1
  1.2682 +	fi
  1.2683 +
  1.2684 +        #
  1.2685 +        # Cleaning up
  1.2686 +        #
  1.2687 +        rm -f $WDIR/$DIR/$F.cdiff.html
  1.2688 +        rm -f $WDIR/$DIR/$F.udiff.html
  1.2689 +        rm -f $WDIR/$DIR/$F.wdiff.html
  1.2690 +        rm -f $WDIR/$DIR/$F.sdiff.html
  1.2691 +        rm -f $WDIR/$DIR/$F-.html
  1.2692 +        rm -f $WDIR/$DIR/$F.html
  1.2693 +
  1.2694 +	its_a_jar=
  1.2695 +	if expr $F : '.*\.jar' >/dev/null; then
  1.2696 +	    its_a_jar=1
  1.2697 +	    # It's a JAR file, let's do it differntly
  1.2698 +	    if [[ -z $JAR ]]; then
  1.2699 +		print "No access to jar, so can't produce diffs for jar files"
  1.2700 +	    else
  1.2701 +		if [ -f $ofile ]; then
  1.2702 +		    $JAR -tvf $ofile >"$ofile".lst
  1.2703 +		fi
  1.2704 +		if [ -f $nfile ]; then
  1.2705 +		    $JAR -tvf $nfile >"$nfile".lst
  1.2706 +		fi
  1.2707 +
  1.2708 +		if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  1.2709 +
  1.2710 +		    ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
  1.2711 +		    diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  1.2712 +			> $WDIR/$DIR/$F.cdiff.html
  1.2713 +		    print " cdiffs\c"
  1.2714 +
  1.2715 +		    ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
  1.2716 +		    diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  1.2717 +			> $WDIR/$DIR/$F.udiff.html
  1.2718 +
  1.2719 +		    print " udiffs\c"
  1.2720 +
  1.2721 +		    if [[ -x $WDIFF ]]; then
  1.2722 +			$WDIFF -c "$COMM" \
  1.2723 +			    -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
  1.2724 +			    $WDIR/$DIR/$F.wdiff.html 2>/dev/null
  1.2725 +			if [[ $? -eq 0 ]]; then
  1.2726 +			    print " wdiffs\c"
  1.2727 +			else
  1.2728 +			    print " wdiffs[fail]\c"
  1.2729 +			fi
  1.2730 +		    fi
  1.2731 +
  1.2732 +		    sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  1.2733 +			> $WDIR/$DIR/$F.sdiff.html
  1.2734 +		    print " sdiffs\c"
  1.2735 +
  1.2736 +		    print " frames\c"
  1.2737 +
  1.2738 +		    rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  1.2739 +
  1.2740 +		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  1.2741 +
  1.2742 +		elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  1.2743 +		# renamed file: may also have differences
  1.2744 +		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  1.2745 +		elif [[ -f $nfile ]]; then
  1.2746 +		# new file: count added lines
  1.2747 +		    difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
  1.2748 +		elif [[ -f $ofile ]]; then
  1.2749 +		# old file: count deleted lines
  1.2750 +		    difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
  1.2751 +		fi
  1.2752 +	    fi
  1.2753 +	else
  1.2754 +	    
  1.2755 +	    #
  1.2756 +	    # If we have old and new versions of the file then run the
  1.2757 +	    # appropriate diffs.  This is complicated by a couple of factors:
  1.2758 +	    #
  1.2759 +	    #	- renames must be handled specially: we emit a 'remove'
  1.2760 +	    #	  diff and an 'add' diff
  1.2761 +	    #	- new files and deleted files must be handled specially
  1.2762 +	    #	- Solaris patch(1m) can't cope with file creation
  1.2763 +	    #	  (and hence renames) as of this writing.
  1.2764 +	    #   - To make matters worse, gnu patch doesn't interpret the
  1.2765 +	    #	  output of Solaris diff properly when it comes to
  1.2766 +	    #	  adds and deletes.  We need to do some "cleansing"
  1.2767 +	    #     transformations:
  1.2768 +	    # 	    [to add a file] @@ -1,0 +X,Y @@  -->  @@ -0,0 +X,Y @@
  1.2769 +	    #	    [to del a file] @@ -X,Y +1,0 @@  -->  @@ -X,Y +0,0 @@
  1.2770 +	    #
  1.2771 +	    cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
  1.2772 +	    cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
  1.2773 +
  1.2774 +	    rm -f $WDIR/$DIR/$F.patch
  1.2775 +	    if [[ -z $rename ]]; then
  1.2776 +		if [ ! -f $ofile ]; then
  1.2777 +		    diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  1.2778 +			> $WDIR/$DIR/$F.patch
  1.2779 +		elif [ ! -f $nfile ]; then
  1.2780 +		    diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  1.2781 +			> $WDIR/$DIR/$F.patch
  1.2782 +		else
  1.2783 +		    diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
  1.2784 +		fi
  1.2785 +	    else
  1.2786 +		diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  1.2787 +		    > $WDIR/$DIR/$F.patch
  1.2788 +
  1.2789 +		diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  1.2790 +		    >> $WDIR/$DIR/$F.patch
  1.2791 +
  1.2792 +	    fi
  1.2793 +
  1.2794 +
  1.2795 +	#
  1.2796 +	# Tack the patch we just made onto the accumulated patch for the
  1.2797 +	# whole wad.
  1.2798 +	#
  1.2799 +	    cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
  1.2800 +
  1.2801 +	    print " patch\c"
  1.2802 +
  1.2803 +	    if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  1.2804 +
  1.2805 +		${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
  1.2806 +		diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  1.2807 +		    > $WDIR/$DIR/$F.cdiff.html
  1.2808 +		print " cdiffs\c"
  1.2809 +
  1.2810 +		${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
  1.2811 +		diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  1.2812 +		    > $WDIR/$DIR/$F.udiff.html
  1.2813 +
  1.2814 +		print " udiffs\c"
  1.2815 +
  1.2816 +		if [[ -x $WDIFF ]]; then
  1.2817 +		    $WDIFF -c "$COMM" \
  1.2818 +			-t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
  1.2819 +			$WDIR/$DIR/$F.wdiff.html 2>/dev/null
  1.2820 +		    if [[ $? -eq 0 ]]; then
  1.2821 +			print " wdiffs\c"
  1.2822 +		    else
  1.2823 +			print " wdiffs[fail]\c"
  1.2824 +		    fi
  1.2825 +		fi
  1.2826 +
  1.2827 +		sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  1.2828 +		    > $WDIR/$DIR/$F.sdiff.html
  1.2829 +		print " sdiffs\c"
  1.2830 +
  1.2831 +		print " frames\c"
  1.2832 +
  1.2833 +		rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  1.2834 +
  1.2835 +		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  1.2836 +
  1.2837 +	    elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  1.2838 +		# renamed file: may also have differences
  1.2839 +		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  1.2840 +	    elif [[ -f $nfile ]]; then
  1.2841 +		# new file: count added lines
  1.2842 +		difflines /dev/null $nfile > $WDIR/$DIR/$F.count
  1.2843 +	    elif [[ -f $ofile ]]; then
  1.2844 +		# old file: count deleted lines
  1.2845 +		difflines $ofile /dev/null > $WDIR/$DIR/$F.count
  1.2846 +	    fi
  1.2847 +	fi
  1.2848 +	#
  1.2849 +	# Now we generate the postscript for this file.  We generate diffs
  1.2850 +	# only in the event that there is delta, or the file is new (it seems
  1.2851 +	# tree-killing to print out the contents of deleted files).
  1.2852 +	#
  1.2853 +	if [[ -f $nfile ]]; then
  1.2854 +		ocr=$ofile
  1.2855 +		[[ ! -f $ofile ]] && ocr=/dev/null
  1.2856 +
  1.2857 +		if [[ -z $mv_but_nodiff ]]; then
  1.2858 +			textcomm=`getcomments text $P $PP`
  1.2859 +			if [[ -x $CODEREVIEW ]]; then
  1.2860 +				$CODEREVIEW -y "$textcomm" \
  1.2861 +				    -e $ocr $nfile \
  1.2862 +				    > /tmp/$$.psfile 2>/dev/null &&
  1.2863 +				    cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
  1.2864 +				if [[ $? -eq 0 ]]; then
  1.2865 +					print " ps\c"
  1.2866 +				else
  1.2867 +					print " ps[fail]\c"
  1.2868 +				fi
  1.2869 +			fi
  1.2870 +		fi
  1.2871 +	fi
  1.2872 +
  1.2873 +	if [[ -f $ofile && -z $mv_but_nodiff ]]; then
  1.2874 +	    if [[ -n $its_a_jar ]]; then
  1.2875 +		source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
  1.2876 +	    else
  1.2877 +		source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
  1.2878 +	    fi
  1.2879 +		print " old\c"
  1.2880 +	fi
  1.2881 +
  1.2882 +	if [[ -f $nfile ]]; then
  1.2883 +	    if [[ -n $its_a_jar ]]; then
  1.2884 +		source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
  1.2885 +	    else
  1.2886 +		source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
  1.2887 +	    fi
  1.2888 +		print " new\c"
  1.2889 +	fi
  1.2890 +
  1.2891 +	print
  1.2892 +done < $FLIST
  1.2893 +
  1.2894 +frame_nav_js > $WDIR/ancnav.js
  1.2895 +frame_navigation > $WDIR/ancnav.html
  1.2896 +
  1.2897 +if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
  1.2898 +	print " Generating PDF: \c"
  1.2899 +	fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
  1.2900 +	print "Done."
  1.2901 +fi
  1.2902 +
  1.2903 +# Now build the index.html file that contains
  1.2904 +# links to the source files and their diffs.
  1.2905 +
  1.2906 +cd $CWS
  1.2907 +
  1.2908 +# Save total changed lines for Code Inspection.
  1.2909 +print "$TOTL" > $WDIR/TotalChangedLines
  1.2910 +
  1.2911 +print "     index.html: \c"
  1.2912 +INDEXFILE=$WDIR/index.html
  1.2913 +exec 3<&1			# duplicate stdout to FD3.
  1.2914 +exec 1<&-			# Close stdout.
  1.2915 +exec > $INDEXFILE		# Open stdout to index file.
  1.2916 +
  1.2917 +print "$HTML<head>"
  1.2918 +print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
  1.2919 +print "$STDHEAD"
  1.2920 +print "<title>$WNAME</title>"
  1.2921 +print "</head>"
  1.2922 +print "<body id=\"SUNWwebrev\">"
  1.2923 +print "<div class=\"summary\">"
  1.2924 +print "<h2>Code Review for $WNAME</h2>"
  1.2925 +
  1.2926 +print "<table>"
  1.2927 +
  1.2928 +if [[ -z $uflag ]]
  1.2929 +then
  1.2930 +    if [[ $SCM_MODE == "mercurial" ]]
  1.2931 +    then
  1.2932 +        #
  1.2933 +        # Let's try to extract the user name from the .hgrc file
  1.2934 +        #
  1.2935 +	username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
  1.2936 +    fi
  1.2937 +
  1.2938 +    if [[ -z $username ]]
  1.2939 +    then
  1.2940 +        #
  1.2941 +        # Figure out the username and gcos name.  To maintain compatibility
  1.2942 +        # with passwd(4), we must support '&' substitutions.
  1.2943 +        #
  1.2944 +	username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
  1.2945 +	if [[ -x $GETENT ]]; then
  1.2946 +	    realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
  1.2947 +	fi
  1.2948 +	userupper=`print "$username" | sed 's/\<./\u&/g'`
  1.2949 +	realname=`print $realname | sed s/\&/$userupper/`
  1.2950 +    fi
  1.2951 +fi
  1.2952 +
  1.2953 +date="on `date`"
  1.2954 +
  1.2955 +if [[ -n "$username" && -n "$realname" ]]; then
  1.2956 +	print "<tr><th>Prepared by:</th>"
  1.2957 +	print "<td>$realname ($username) $date</td></tr>"
  1.2958 +elif [[ -n "$username" ]]; then
  1.2959 +	print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
  1.2960 +fi
  1.2961 +
  1.2962 +print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
  1.2963 +if [[ -n $parent_webrev ]]; then
  1.2964 +        print "<tr><th>Compare against:</th><td>"
  1.2965 +	print "webrev at $parent_webrev"
  1.2966 +else
  1.2967 +    if [[ -n $OUTPWS2 ]]; then
  1.2968 +        print "<tr><th>Compare against:</th><td>"
  1.2969 +	print "$OUTPWS2"
  1.2970 +    fi
  1.2971 +fi
  1.2972 +print "</td></tr>"
  1.2973 +if [[ -n $rflag ]]; then
  1.2974 +    print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
  1.2975 +elif [[ -n $OUTREV ]]; then
  1.2976 +    if [[ -z $forestflag ]]; then
  1.2977 +        print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
  1.2978 +    fi
  1.2979 +fi
  1.2980 +if [[ -n $HG_BRANCH ]]; then
  1.2981 +    print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
  1.2982 +fi
  1.2983 +
  1.2984 +print "<tr><th>Summary of changes:</th><td>"
  1.2985 +printCI $TOTL $TINS $TDEL $TMOD $TUNC
  1.2986 +print "</td></tr>"
  1.2987 +
  1.2988 +if [[ -f $WDIR/$WNAME.patch ]]; then
  1.2989 +	print "<tr><th>Patch of changes:</th><td>"
  1.2990 +	print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
  1.2991 +fi
  1.2992 +if [[ -f $WDIR/$WNAME.pdf ]]; then
  1.2993 +	print "<tr><th>Printable review:</th><td>"
  1.2994 +	print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
  1.2995 +fi
  1.2996 +
  1.2997 +if [[ -n "$iflag" ]]; then
  1.2998 +	print "<tr><th>Author comments:</th><td><div>"
  1.2999 +	cat /tmp/$$.include
  1.3000 +	print "</div></td></tr>"
  1.3001 +fi
  1.3002 +# Add links to referenced CRs, if any
  1.3003 +# external URL has a <title> like:
  1.3004 +# <title>Bug ID: 6641309 Wrong Cookie separator used in HttpURLConnection</title>
  1.3005 +# while internal URL has <title> like:
  1.3006 +# <title>6641309: Wrong Cookie separator used in HttpURLConnection</title>
  1.3007 +#
  1.3008 +if [[ -n $CRID ]]; then
  1.3009 +    for id in $CRID
  1.3010 +    do
  1.3011 +        print "<tr><th>Bug id:</th><td>"
  1.3012 +        url="${BUGURL}${id}"
  1.3013 +        if [[ -n $WGET ]]; then
  1.3014 +            msg=`$WGET -q $url -O - | grep '<title>' | sed 's/<title>\(.*\)<\/title>/\1/' | sed 's/Bug ID://'`
  1.3015 +        fi
  1.3016 +        if [[ -n $msg ]]; then
  1.3017 +            print "<a href=\"$url\">$msg</a>"
  1.3018 +        else
  1.3019 +            print $id | bug2url
  1.3020 +        fi
  1.3021 +        
  1.3022 +        print "</td></tr>"
  1.3023 +    done
  1.3024 +fi
  1.3025 +print "<tr><th>Legend:</th><td>"
  1.3026 +print "<b>Modified file</b><br><font color=red><b>Deleted file</b></font><br><font color=green><b>New file</b></font></td></tr>"
  1.3027 +print "</table>"
  1.3028 +print "</div>"
  1.3029 +
  1.3030 +#
  1.3031 +# Second pass through the files: generate the rest of the index file
  1.3032 +#
  1.3033 +while read LINE
  1.3034 +do
  1.3035 +	set - $LINE
  1.3036 +        if [[ $1 == "Revision:" ]]; then
  1.3037 +            FIRST_CREV=`expr $3 + 1`
  1.3038 +            continue
  1.3039 +        fi
  1.3040 +	P=$1
  1.3041 +
  1.3042 +	if [[ $# == 2 ]]; then
  1.3043 +		PP=$2
  1.3044 +		oldname=" <i>(was $PP)</i>"
  1.3045 +
  1.3046 +	else
  1.3047 +		PP=$P
  1.3048 +		oldname=""
  1.3049 +	fi
  1.3050 +
  1.3051 +	DIR=${P%/*}
  1.3052 +	if [[ $DIR == $P ]]; then
  1.3053 +		DIR="."   # File at root of workspace
  1.3054 +	fi
  1.3055 +
  1.3056 +	# Avoid processing the same file twice.
  1.3057 +	# It's possible for renamed files to
  1.3058 +	# appear twice in the file list
  1.3059 +
  1.3060 +	F=$WDIR/$P
  1.3061 +
  1.3062 +	print "<p><code>"
  1.3063 +
  1.3064 +	# If there's a diffs file, make diffs links
  1.3065 +
  1.3066 +        NODIFFS=
  1.3067 +	if [[ -f $F.cdiff.html ]]; then
  1.3068 +		print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
  1.3069 +		print "<a href=\"$P.udiff.html\">Udiffs</a>"
  1.3070 +
  1.3071 +		if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
  1.3072 +			print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
  1.3073 +		fi
  1.3074 +
  1.3075 +		print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
  1.3076 +
  1.3077 +		print "<a href=\"$P.frames.html\">Frames</a>"
  1.3078 +	else
  1.3079 +                NODIFFS=1
  1.3080 +		print " ------ ------ ------"
  1.3081 +
  1.3082 +		if [[ -x $WDIFF ]]; then
  1.3083 +			print " ------"
  1.3084 +		fi
  1.3085 +
  1.3086 +		print " ------"
  1.3087 +	fi
  1.3088 +
  1.3089 +	# If there's an old file, make the link
  1.3090 +
  1.3091 +        NOOLD=
  1.3092 +	if [[ -f $F-.html ]]; then
  1.3093 +		print "<a href=\"$P-.html\">Old</a>"
  1.3094 +	else
  1.3095 +                NOOLD=1
  1.3096 +		print " ---"
  1.3097 +	fi
  1.3098 +
  1.3099 +	# If there's an new file, make the link
  1.3100 +
  1.3101 +        NONEW=
  1.3102 +	if [[ -f $F.html ]]; then
  1.3103 +		print "<a href=\"$P.html\">New</a>"
  1.3104 +	else
  1.3105 +                NONEW=1
  1.3106 +		print " ---"
  1.3107 +	fi
  1.3108 +
  1.3109 +	if [[ -f $F.patch ]]; then
  1.3110 +		print "<a href=\"$P.patch\">Patch</a>"
  1.3111 +	else
  1.3112 +		print " -----"
  1.3113 +	fi
  1.3114 +
  1.3115 +	if [[ -f $WDIR/raw_files/new/$P ]]; then
  1.3116 +		print "<a href=\"raw_files/new/$P\">Raw</a>"
  1.3117 +	else
  1.3118 +		print " ---"
  1.3119 +	fi
  1.3120 +        print "</code>"
  1.3121 +        if [[ -n $NODIFFS && -z $oldname ]]; then
  1.3122 +            if [[ -n $NOOLD ]]; then
  1.3123 +                print "<font color=green><b>$P</b></font>"
  1.3124 +            elif [[ -n $NONEW ]]; then
  1.3125 +                print "<font color=red><b>$P</b></font>"
  1.3126 +            fi
  1.3127 +        else
  1.3128 +	    print "<b>$P</b> $oldname"
  1.3129 +        fi
  1.3130 +
  1.3131 +	#
  1.3132 +	# Check for usr/closed
  1.3133 +	#
  1.3134 +	if [ ! -z "$Oflag" ]; then
  1.3135 +		if [[ $P == usr/closed/* ]]; then
  1.3136 +			print "&nbsp;&nbsp;<i>Closed source: omitted from" \
  1.3137 +			    "this review</i>"
  1.3138 +		fi
  1.3139 +	fi
  1.3140 +
  1.3141 +	print "</p><blockquote>\c"
  1.3142 +	# Insert delta comments if any
  1.3143 +	comments=`getcomments html $P $PP`
  1.3144 +	if [ -n "$comments" ]; then
  1.3145 +	    print "<pre>$comments</pre>"
  1.3146 +	fi
  1.3147 +
  1.3148 +	# Add additional comments comment
  1.3149 +
  1.3150 +	print "<!-- Add comments to explain changes in $P here -->"
  1.3151 +
  1.3152 +	# Add count of changes.
  1.3153 +
  1.3154 +	if [[ -f $F.count ]]; then
  1.3155 +	    cat $F.count
  1.3156 +	    rm $F.count
  1.3157 +	fi
  1.3158 +        print "</blockquote>"
  1.3159 +done < $FLIST
  1.3160 +
  1.3161 +print
  1.3162 +print
  1.3163 +print "<hr />"
  1.3164 +print "<p style=\"font-size: small\">"
  1.3165 +print "This code review page was prepared using <b>$0</b>"
  1.3166 +print "(vers $WEBREV_UPDATED)."
  1.3167 +print "</body>"
  1.3168 +print "</html>"
  1.3169 +
  1.3170 +if [[ -n $ZIP ]]; then
  1.3171 +    # Let's generate a zip file for convenience
  1.3172 +    cd $WDIR/..
  1.3173 +    if [ -f webrev.zip ]; then
  1.3174 +	rm webrev.zip
  1.3175 +    fi
  1.3176 +    $ZIP -r webrev webrev >/dev/null 2>&1
  1.3177 +fi
  1.3178 +
  1.3179 +exec 1<&-			# Close FD 1.
  1.3180 +exec 1<&3			# dup FD 3 to restore stdout.
  1.3181 +exec 3<&-			# close FD 3.
  1.3182 +
  1.3183 +print "Done."
  1.3184 +print "Output to: $WDIR"
  1.3185 +

mercurial