make/scripts/webrev.ksh

Thu, 17 Oct 2013 14:07:57 -0700

author
mduigou
date
Thu, 17 Oct 2013 14:07:57 -0700
changeset 851
9ec6626d43bb
parent 812
d23177734b28
child 907
c1029b02ca87
permissions
-rw-r--r--

8026062: webrev.ksh: fix bug title web scraping, remove teamware, sac, "open bug", -l and wxfile support
Reviewed-by: weijun, dsamersoff, darcy, jrose, tbell

     1 #!/bin/ksh -p
     2 #
     3 # CDDL HEADER START
     4 #
     5 # The contents of this file are subject to the terms of the
     6 # Common Development and Distribution License (the "License").
     7 # You may not use this file except in compliance with the License.
     8 #
     9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
    10 # or http://www.opensolaris.org/os/licensing.
    11 # See the License for the specific language governing permissions
    12 # and limitations under the License.
    13 #
    14 # When distributing Covered Code, include this CDDL HEADER in each
    15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
    16 # If applicable, add the following below this CDDL HEADER, with the
    17 # fields enclosed by brackets "[]" replaced with your own identifying
    18 # information: Portions Copyright [yyyy] [name of copyright owner]
    19 #
    20 # CDDL HEADER END
    21 #
    22 # Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
    23 # Use is subject to license terms.
    24 #
    25 # This script takes a file list and a workspace and builds a set of html files
    26 # suitable for doing a code review of source changes via a web page.
    27 # Documentation is available via 'webrev -h'.
    28 #
    30 WEBREV_UPDATED=25.0-hg+openjdk.java.net
    32 HTML='<?xml version="1.0"?>
    33 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    34     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    35 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
    37 FRAMEHTML='<?xml version="1.0"?>
    38 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
    39     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
    40 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n'
    42 STDHEAD='<meta charset="utf-8">
    43 <meta http-equiv="cache-control" content="no-cache" />
    44 <meta http-equiv="Pragma" content="no-cache" />
    45 <meta http-equiv="Expires" content="-1" />
    46 <!--
    47    Note to customizers: the body of the webrev is IDed as SUNWwebrev
    48    to allow easy overriding by users of webrev via the userContent.css
    49    mechanism available in some browsers.
    51    For example, to have all "removed" information be red instead of
    52    brown, set a rule in your userContent.css file like:
    54        body#SUNWwebrev span.removed { color: red ! important; }
    55 -->
    56 <style type="text/css" media="screen">
    57 body {
    58     background-color: #eeeeee;
    59 }
    60 hr {
    61     border: none 0;
    62     border-top: 1px solid #aaa;
    63     height: 1px;
    64 }
    65 div.summary {
    66     font-size: .8em;
    67     border-bottom: 1px solid #aaa;
    68     padding-left: 1em;
    69     padding-right: 1em;
    70 }
    71 div.summary h2 {
    72     margin-bottom: 0.3em;
    73 }
    74 div.summary table th {
    75     text-align: right;
    76     vertical-align: top;
    77     white-space: nowrap;
    78 }
    79 span.lineschanged {
    80     font-size: 0.7em;
    81 }
    82 span.oldmarker {
    83     color: red;
    84     font-size: large;
    85     font-weight: bold;
    86 }
    87 span.newmarker {
    88     color: green;
    89     font-size: large;
    90     font-weight: bold;
    91 }
    92 span.removed {
    93     color: brown;
    94 }
    95 span.changed {
    96     color: blue;
    97 }
    98 span.new {
    99     color: blue;
   100     font-weight: bold;
   101 }
   102 a.print { font-size: x-small; }
   104 </style>
   106 <style type="text/css" media="print">
   107 pre { font-size: 0.8em; font-family: courier, monospace; }
   108 span.removed { color: #444; font-style: italic }
   109 span.changed { font-weight: bold; }
   110 span.new { font-weight: bold; }
   111 span.newmarker { font-size: 1.2em; font-weight: bold; }
   112 span.oldmarker { font-size: 1.2em; font-weight: bold; }
   113 a.print {display: none}
   114 hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
   115 </style>
   116 '
   118 #
   119 # UDiffs need a slightly different CSS rule for 'new' items (we don't
   120 # want them to be bolded as we do in cdiffs or sdiffs).
   121 #
   122 UDIFFCSS='
   123 <style type="text/css" media="screen">
   124 span.new {
   125     color: blue;
   126     font-weight: normal;
   127 }
   128 </style>
   129 '
   131 #
   132 # input_cmd | html_quote | output_cmd
   133 # or
   134 # html_quote filename | output_cmd
   135 #
   136 # Make a piece of source code safe for display in an HTML <pre> block.
   137 #
   138 html_quote()
   139 {
   140 	sed -e "s/&/\&amp;/g" -e "s/&amp;#\([x]*[0-9A-Fa-f]\{2,5\}\);/\&#\1;/g" -e "s/</\&lt;/g" -e "s/>/\&gt;/g" "$@" | expand
   141 }
   143 #
   144 # input_cmd | html_quote | output_cmd
   145 # or
   146 # html_dequote filename | output_cmd
   147 #
   148 # Replace HTML entities with literals
   149 #
   150 html_dequote()
   151 {
   152 	sed -e "s/&quot;/\"/g" -e "s/&apos;/\'/g" -e "s/&amp;/\&/g" -e "s/&lt;/<'/g" -e "s/&gt;/>/g" "$@" | expand
   153 }
   155 #
   156 # input_cmd | bug2url | output_cmd
   157 #
   158 # Scan for bugids and insert <a> links to the relevent bug database.
   159 #
   160 bug2url()
   161 {
   162 	sed -e 's|[0-9]\{5,\}|<a href=\"'$BUGURL$IDPREFIX'&\">&</a>|g'
   163 }
   165 #
   166 # strip_unchanged <infile> | output_cmd
   167 #
   168 # Removes chunks of sdiff documents that have not changed. This makes it
   169 # easier for a code reviewer to find the bits that have changed.
   170 #
   171 # Deleted lines of text are replaced by a horizontal rule. Some
   172 # identical lines are retained before and after the changed lines to
   173 # provide some context.  The number of these lines is controlled by the
   174 # variable C in the $AWK script below.
   175 #
   176 # The script detects changed lines as any line that has a "<span class="
   177 # string embedded (unchanged lines have no particular class and are not
   178 # part of a <span>).  Blank lines (without a sequence number) are also
   179 # detected since they flag lines that have been inserted or deleted.
   180 #
   181 strip_unchanged()
   182 {
   183 	$AWK '
   184 	BEGIN	{ C = c = 20 }
   185 	NF == 0 || /span class=/ {
   186 		if (c > C) {
   187 			c -= C
   188 			inx = 0
   189 			if (c > C) {
   190 				print "\n</pre><hr></hr><pre>"
   191 				inx = c % C
   192 				c = C
   193 			}
   195 			for (i = 0; i < c; i++)
   196 				print ln[(inx + i) % C]
   197 		}
   198 		c = 0;
   199 		print
   200 		next
   201 	}
   202 	{	if (c >= C) {
   203 			ln[c % C] = $0
   204 			c++;
   205 			next;
   206 		}
   207 		c++;
   208 		print
   209 	}
   210 	END	{ if (c > (C * 2)) print "\n</pre><hr></hr>" }
   212 	' $1
   213 }
   215 #
   216 # sdiff_to_html
   217 #
   218 # This function takes two files as arguments, obtains their diff, and
   219 # processes the diff output to present the files as an HTML document with
   220 # the files displayed side-by-side, differences shown in color.  It also
   221 # takes a delta comment, rendered as an HTML snippet, as the third
   222 # argument.  The function takes two files as arguments, then the name of
   223 # file, the path, and the comment.  The HTML will be delivered on stdout,
   224 # e.g.
   225 #
   226 #   $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
   227 #         new/usr/src/tools/scripts/webrev.sh \
   228 #         webrev.sh usr/src/tools/scripts \
   229 #         '<a href="https://bugs.openjdk.java.net/browse/JDK-1234567">
   230 #          JDK-1234567</a> my bugid' > <file>.html
   231 #
   232 # framed_sdiff() is then called which creates $2.frames.html
   233 # in the webrev tree.
   234 #
   235 # FYI: This function is rather unusual in its use of awk.  The initial
   236 # diff run produces conventional diff output showing changed lines mixed
   237 # with editing codes.  The changed lines are ignored - we're interested in
   238 # the editing codes, e.g.
   239 #
   240 #      8c8
   241 #      57a61
   242 #      63c66,76
   243 #      68,93d80
   244 #      106d90
   245 #      108,110d91
   246 #
   247 #  These editing codes are parsed by the awk script and used to generate
   248 #  another awk script that generates HTML, e.g the above lines would turn
   249 #  into something like this:
   250 #
   251 #      BEGIN { printf "<pre>\n" }
   252 #      function sp(n) {for (i=0;i<n;i++)printf "\n"}
   253 #      function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0}
   254 #      NR==8           {wl("#7A7ADD");next}
   255 #      NR==54          {wl("#7A7ADD");sp(3);next}
   256 #      NR==56          {wl("#7A7ADD");next}
   257 #      NR==57          {wl("black");printf "\n"; next}
   258 #        :               :
   259 #
   260 #  This script is then run on the original source file to generate the
   261 #  HTML that corresponds to the source file.
   262 #
   263 #  The two HTML files are then combined into a single piece of HTML that
   264 #  uses an HTML table construct to present the files side by side.  You'll
   265 #  notice that the changes are color-coded:
   266 #
   267 #   black     - unchanged lines
   268 #   blue      - changed lines
   269 #   bold blue - new lines
   270 #   brown     - deleted lines
   271 #
   272 #  Blank lines are inserted in each file to keep unchanged lines in sync
   273 #  (side-by-side).  This format is familiar to users of sdiff(1) or
   274 #  Teamware's filemerge tool.
   275 #
   276 sdiff_to_html()
   277 {
   278 	diff -b $1 $2 > /tmp/$$.diffs
   280 	TNAME=$3
   281 	TPATH=$4
   282 	COMMENT=$5
   284 	#
   285 	#  Now we have the diffs, generate the HTML for the old file.
   286 	#
   287 	$AWK '
   288 	BEGIN	{
   289 		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
   290 		printf "function removed() "
   291 		printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   292 		printf "function changed() "
   293 		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   294 		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
   295 }
   296 	/^</	{next}
   297 	/^>/	{next}
   298 	/^---/	{next}
   300 	{
   301 	split($1, a, /[cad]/) ;
   302 	if (index($1, "a")) {
   303 		if (a[1] == 0) {
   304 			n = split(a[2], r, /,/);
   305 			if (n == 1)
   306 				printf "BEGIN\t\t{sp(1)}\n"
   307 			else
   308 				printf "BEGIN\t\t{sp(%d)}\n",\
   309 				(r[2] - r[1]) + 1
   310 			next
   311 		}
   313 		printf "NR==%s\t\t{", a[1]
   314 		n = split(a[2], r, /,/);
   315 		s = r[1];
   316 		if (n == 1)
   317 			printf "bl();printf \"\\n\"; next}\n"
   318 		else {
   319 			n = r[2] - r[1]
   320 			printf "bl();sp(%d);next}\n",\
   321 			(r[2] - r[1]) + 1
   322 		}
   323 		next
   324 	}
   325 	if (index($1, "d")) {
   326 		n = split(a[1], r, /,/);
   327 		n1 = r[1]
   328 		n2 = r[2]
   329 		if (n == 1)
   330 			printf "NR==%s\t\t{removed(); next}\n" , n1
   331 		else
   332 			printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
   333 		next
   334 	}
   335 	if (index($1, "c")) {
   336 		n = split(a[1], r, /,/);
   337 		n1 = r[1]
   338 		n2 = r[2]
   339 		final = n2
   340 		d1 = 0
   341 		if (n == 1)
   342 			printf "NR==%s\t\t{changed();" , n1
   343 		else {
   344 			d1 = n2 - n1
   345 			printf "NR==%s,NR==%s\t{changed();" , n1, n2
   346 		}
   347 		m = split(a[2], r, /,/);
   348 		n1 = r[1]
   349 		n2 = r[2]
   350 		if (m > 1) {
   351 			d2  = n2 - n1
   352 			if (d2 > d1) {
   353 				if (n > 1) printf "if (NR==%d)", final
   354 				printf "sp(%d);", d2 - d1
   355 			}
   356 		}
   357 		printf "next}\n" ;
   359 		next
   360 	}
   361 	}
   363 	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
   364 	' /tmp/$$.diffs > /tmp/$$.file1
   366 	#
   367 	#  Now generate the HTML for the new file
   368 	#
   369 	$AWK '
   370 	BEGIN	{
   371 		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
   372 		printf "function new() "
   373 		printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   374 		printf "function changed() "
   375 		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   376 		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
   377 	}
   379 	/^</	{next}
   380 	/^>/	{next}
   381 	/^---/	{next}
   383 	{
   384 	split($1, a, /[cad]/) ;
   385 	if (index($1, "d")) {
   386 		if (a[2] == 0) {
   387 			n = split(a[1], r, /,/);
   388 			if (n == 1)
   389 				printf "BEGIN\t\t{sp(1)}\n"
   390 			else
   391 				printf "BEGIN\t\t{sp(%d)}\n",\
   392 				(r[2] - r[1]) + 1
   393 			next
   394 		}
   396 		printf "NR==%s\t\t{", a[2]
   397 		n = split(a[1], r, /,/);
   398 		s = r[1];
   399 		if (n == 1)
   400 			printf "bl();printf \"\\n\"; next}\n"
   401 		else {
   402 			n = r[2] - r[1]
   403 			printf "bl();sp(%d);next}\n",\
   404 			(r[2] - r[1]) + 1
   405 		}
   406 		next
   407 	}
   408 	if (index($1, "a")) {
   409 		n = split(a[2], r, /,/);
   410 		n1 = r[1]
   411 		n2 = r[2]
   412 		if (n == 1)
   413 			printf "NR==%s\t\t{new() ; next}\n" , n1
   414 		else
   415 			printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
   416 		next
   417 	}
   418 	if (index($1, "c")) {
   419 		n = split(a[2], r, /,/);
   420 		n1 = r[1]
   421 		n2 = r[2]
   422 		final = n2
   423 		d2 = 0;
   424 		if (n == 1) {
   425 			final = n1
   426 			printf "NR==%s\t\t{changed();" , n1
   427 		} else {
   428 			d2 = n2 - n1
   429 			printf "NR==%s,NR==%s\t{changed();" , n1, n2
   430 		}
   431 		m = split(a[1], r, /,/);
   432 		n1 = r[1]
   433 		n2 = r[2]
   434 		if (m > 1) {
   435 			d1  = n2 - n1
   436 			if (d1 > d2) {
   437 				if (n > 1) printf "if (NR==%d)", final
   438 				printf "sp(%d);", d1 - d2
   439 			}
   440 		}
   441 		printf "next}\n" ;
   442 		next
   443 	}
   444 	}
   445 	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
   446 	' /tmp/$$.diffs > /tmp/$$.file2
   448 	#
   449 	# Post-process the HTML files by running them back through $AWK
   450 	#
   451 	html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html
   453 	html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html
   455 	#
   456 	# Now combine into a valid HTML file and side-by-side into a table
   457 	#
   458 	print "$HTML<head>$STDHEAD"
   459 	print "<title>$WNAME Sdiff $TPATH </title>"
   460 	print "</head><body id=\"SUNWwebrev\">"
   461 	print "<h2>$TPATH/$TNAME</h2>"
   462         print "<a class=\"print\" href=\"javascript:print()\">Print this page</a>"
   463 	print "<pre>$COMMENT</pre>\n"
   464 	print "<table><tr valign=\"top\">"
   465 	print "<td><pre>"
   467 	strip_unchanged /tmp/$$.file1.html
   469 	print "</pre></td><td><pre>"
   471 	strip_unchanged /tmp/$$.file2.html
   473 	print "</pre></td>"
   474 	print "</tr></table>"
   475 	print "</body></html>"
   477 	framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \
   478 	    "$COMMENT"
   479 }
   482 #
   483 # framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment>
   484 #
   485 # Expects lefthand and righthand side html files created by sdiff_to_html.
   486 # We use insert_anchors() to augment those with HTML navigation anchors,
   487 # and then emit the main frame.  Content is placed into:
   488 #
   489 #    $WDIR/DIR/$TNAME.lhs.html
   490 #    $WDIR/DIR/$TNAME.rhs.html
   491 #    $WDIR/DIR/$TNAME.frames.html
   492 #
   493 # NOTE: We rely on standard usage of $WDIR and $DIR.
   494 #
   495 function framed_sdiff
   496 {
   497 	typeset TNAME=$1
   498 	typeset TPATH=$2
   499 	typeset lhsfile=$3
   500 	typeset rhsfile=$4
   501 	typeset comments=$5
   502 	typeset RTOP
   504 	# Enable html files to access WDIR via a relative path.
   505 	RTOP=$(relative_dir $TPATH $WDIR)
   507 	# Make the rhs/lhs files and output the frameset file.
   508 	print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
   510 	cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF
   511 	    <script type="text/javascript" src="$RTOP/ancnav.js"></script>
   512 	    </head>
   513 	    <body id="SUNWwebrev" onkeypress="keypress(event);">
   514 	    <a name="0"></a>
   515 	    <pre>$comments</pre><hr></hr>
   516 	EOF
   518 	cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html
   520 	insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
   521 	insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
   523 	close='</body></html>'
   525 	print $close >> $WDIR/$DIR/$TNAME.lhs.html
   526 	print $close >> $WDIR/$DIR/$TNAME.rhs.html
   528 	print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html
   529 	print "<title>$WNAME Framed-Sdiff " \
   530 	    "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html
   531 	cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF
   532 	  <frameset rows="*,60">
   533 	    <frameset cols="50%,50%">
   534 	      <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs" />
   535 	      <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs" />
   536 	    </frameset>
   537 	  <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0"
   538 	   marginheight="0" name="nav" />
   539 	  <noframes>
   540             <body id="SUNWwebrev">
   541 	      Alas 'frames' webrev requires that your browser supports frames
   542 	      and has the feature enabled.
   543             </body>
   544 	  </noframes>
   545 	  </frameset>
   546 	</html>
   547 	EOF
   548 }
   551 #
   552 # fix_postscript
   553 #
   554 # Merge codereview output files to a single conforming postscript file, by:
   555 # 	- removing all extraneous headers/trailers
   556 #	- making the page numbers right
   557 #	- removing pages devoid of contents which confuse some
   558 #	  postscript readers.
   559 #
   560 # From Casper.
   561 #
   562 function fix_postscript
   563 {
   564 	infile=$1
   566 	cat > /tmp/$$.crmerge.pl << \EOF
   568 	print scalar(<>);		# %!PS-Adobe---
   569 	print "%%Orientation: Landscape\n";
   571 	$pno = 0;
   572 	$doprint = 1;
   574 	$page = "";
   576 	while (<>) {
   577 		next if (/^%%Pages:\s*\d+/);
   579 		if (/^%%Page:/) {
   580 			if ($pno == 0 || $page =~ /\)S/) {
   581 				# Header or single page containing text
   582 				print "%%Page: ? $pno\n" if ($pno > 0);
   583 				print $page;
   584 				$pno++;
   585 			} else {
   586 				# Empty page, skip it.
   587 			}
   588 			$page = "";
   589 			$doprint = 1;
   590 			next;
   591 		}
   593 		# Skip from %%Trailer of one document to Endprolog
   594 		# %%Page of the next
   595 		$doprint = 0 if (/^%%Trailer/);
   596 		$page .= $_ if ($doprint);
   597 	}
   599 	if ($page =~ /\)S/) {
   600 		print "%%Page: ? $pno\n";
   601 		print $page;
   602 	} else {
   603 		$pno--;
   604 	}
   605 	print "%%Trailer\n%%Pages: $pno\n";
   606 EOF
   608 	$PERL /tmp/$$.crmerge.pl < $infile
   609 }
   612 #
   613 # input_cmd | insert_anchors | output_cmd
   614 #
   615 # Flag blocks of difference with sequentially numbered invisible
   616 # anchors.  These are used to drive the frames version of the
   617 # sdiffs output.
   618 #
   619 # NOTE: Anchor zero flags the top of the file irrespective of changes,
   620 # an additional anchor is also appended to flag the bottom.
   621 #
   622 # The script detects changed lines as any line that has a "<span
   623 # class=" string embedded (unchanged lines have no class set and are
   624 # not part of a <span>.  Blank lines (without a sequence number)
   625 # are also detected since they flag lines that have been inserted or
   626 # deleted.
   627 #
   628 function insert_anchors
   629 {
   630 	$AWK '
   631 	function ia() {
   632 		# This should be able to be a singleton <a /> but that
   633 		# seems to trigger a bug in firefox a:hover rule processing
   634 		printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++;
   635 	}
   637 	BEGIN {
   638 		anc=1;
   639 		inblock=1;
   640 		printf "<pre>\n";
   641 	}
   642 	NF == 0 || /^<span class=/ {
   643 		if (inblock == 0) {
   644 			ia();
   645 			inblock=1;
   646 		}
   647 		print;
   648 		next;
   649 	}
   650 	{
   651 		inblock=0;
   652 		print;
   653 	}
   654 	END {
   655 		ia();
   657 		printf "<b style=\"font-size: large; color: red\">";
   658 		printf "--- EOF ---</b>"
   659         	for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
   660 		printf "</pre>"
   661 		printf "<form name=\"eof\">";
   662 		printf "<input name=\"value\" value=\"%d\" type=\"hidden\" />",
   663 		    anc - 1;
   664 		printf "</form>";
   665 	}
   666 	' $1
   667 }
   670 #
   671 # relative_dir
   672 #
   673 # Print a relative return path from $1 to $2.  For example if
   674 # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview,
   675 # this function would print "../../../../".
   676 #
   677 # In the event that $1 is not in $2 a warning is printed to stderr,
   678 # and $2 is returned-- the result of this is that the resulting webrev
   679 # is not relocatable.
   680 #
   681 function relative_dir
   682 {
   683     d1=$1
   684     d2=$2
   685     if [[ "$d1" == "." ]]; then
   686 	print "."
   687     else
   688 	typeset cur="${d1##$d2?(/)}"
   689 	typeset ret=""
   690 	if [[ $d2 == $cur ]]; then   # Should never happen.
   691 		# Should never happen.
   692 		print -u2 "\nWARNING: relative_dir: \"$1\" not relative "
   693 		print -u2 "to \"$2\".  Check input paths.  Framed webrev "
   694 		print -u2 "will not be relocatable!"
   695 		print $2
   696 		return
   697 	fi
   699 	while [[ -n ${cur} ]];
   700 	do
   701 		cur=${cur%%*(/)*([!/])}
   702 		if [[ -z $ret ]]; then
   703 			ret=".."
   704 		else
   705 			ret="../$ret"
   706 		fi
   707 	done
   708 	print $ret
   709     fi
   710 }
   713 #
   714 # frame_nav_js
   715 #
   716 # Emit javascript for frame navigation
   717 #
   718 function frame_nav_js
   719 {
   720 cat << \EOF
   721 var myInt;
   722 var scrolling=0;
   723 var sfactor = 3;
   724 var scount=10;
   726 function scrollByPix() {
   727 	if (scount<=0) {
   728 		sfactor*=1.2;
   729 		scount=10;
   730 	}
   731 	parent.lhs.scrollBy(0,sfactor);
   732 	parent.rhs.scrollBy(0,sfactor);
   733 	scount--;
   734 }
   736 function scrollToAnc(num) {
   738 	// Update the value of the anchor in the form which we use as
   739 	// storage for this value.  setAncValue() will take care of
   740 	// correcting for overflow and underflow of the value and return
   741 	// us the new value.
   742 	num = setAncValue(num);
   744 	// Set location and scroll back a little to expose previous
   745 	// lines.
   746 	//
   747 	// Note that this could be improved: it is possible although
   748 	// complex to compute the x and y position of an anchor, and to
   749 	// scroll to that location directly.
   750 	//
   751 	parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num);
   752 	parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num);
   754 	parent.lhs.scrollBy(0,-30);
   755 	parent.rhs.scrollBy(0,-30);
   756 }
   758 function getAncValue()
   759 {
   760 	return (parseInt(parent.nav.document.diff.real.value));
   761 }
   763 function setAncValue(val)
   764 {
   765 	if (val <= 0) {
   766 		val = 0;
   767 		parent.nav.document.diff.real.value = val;
   768 		parent.nav.document.diff.display.value = "BOF";
   769 		return (val);
   770 	}
   772 	//
   773 	// The way we compute the max anchor value is to stash it
   774 	// inline in the left and right hand side pages-- it's the same
   775 	// on each side, so we pluck from the left.
   776 	//
   777 	maxval = parent.lhs.document.eof.value.value;
   778 	if (val < maxval) {
   779 		parent.nav.document.diff.real.value = val;
   780 		parent.nav.document.diff.display.value = val.toString();
   781 		return (val);
   782 	}
   784 	// this must be: val >= maxval
   785 	val = maxval;
   786 	parent.nav.document.diff.real.value = val;
   787 	parent.nav.document.diff.display.value = "EOF";
   788 	return (val);
   789 }
   791 function stopScroll() {
   792 	if (scrolling==1) {
   793 		clearInterval(myInt);
   794 		scrolling=0;
   795 	}
   796 }
   798 function startScroll() {
   799 	stopScroll();
   800 	scrolling=1;
   801 	myInt=setInterval("scrollByPix()",10);
   802 }
   804 function handlePress(b) {
   806 	switch (b) {
   807 	    case 1 :
   808 		scrollToAnc(-1);
   809 		break;
   810 	    case 2 :
   811 		scrollToAnc(getAncValue() - 1);
   812 		break;
   813 	    case 3 :
   814 		sfactor=-3;
   815 		startScroll();
   816 		break;
   817 	    case 4 :
   818 		sfactor=3;
   819 		startScroll();
   820 		break;
   821 	    case 5 :
   822 		scrollToAnc(getAncValue() + 1);
   823 		break;
   824 	    case 6 :
   825 		scrollToAnc(999999);
   826 		break;
   827 	}
   828 }
   830 function handleRelease(b) {
   831 	stopScroll();
   832 }
   834 function keypress(ev) {
   835 	var keynum;
   836 	var keychar;
   838 	if (window.event) { // IE
   839 		keynum = ev.keyCode;
   840 	} else if (ev.which) { // non-IE
   841 		keynum = ev.which;
   842 	}
   844 	keychar = String.fromCharCode(keynum);
   846 	if (keychar == "k") {
   847 		handlePress(2);
   848 		return (0);
   849 	} else if (keychar == "j" || keychar == " ") {
   850 		handlePress(5);
   851 		return (0);
   852 	}
   853 	return (1);
   854 }
   856 function ValidateDiffNum(){
   857 	val = parent.nav.document.diff.display.value;
   858 	if (val == "EOF") {
   859 		scrollToAnc(999999);
   860 		return;
   861 	}
   863 	if (val == "BOF") {
   864 		scrollToAnc(0);
   865 		return;
   866 	}
   868         i=parseInt(val);
   869         if (isNaN(i)) {
   870                 parent.nav.document.diff.display.value = getAncValue();
   871         } else {
   872                 scrollToAnc(i);
   873         }
   874         return false;
   875 }
   877 EOF
   878 }
   880 #
   881 # frame_navigation
   882 #
   883 # Output anchor navigation file for framed sdiffs.
   884 #
   885 function frame_navigation
   886 {
   887 	print "$HTML<head>$STDHEAD"
   889 	cat << \EOF
   890 <title>Anchor Navigation</title>
   891 <meta http-equiv="Content-Script-Type" content="text/javascript" />
   892 <meta http-equiv="Content-Type" content="text/html" />
   894 <style type="text/css">
   895     div.button td { padding-left: 5px; padding-right: 5px;
   896 		    background-color: #eee; text-align: center;
   897 		    border: 1px #444 outset; cursor: pointer; }
   898     div.button a { font-weight: bold; color: black }
   899     div.button td:hover { background: #ffcc99; }
   900 </style>
   901 EOF
   903 	print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
   905 	cat << \EOF
   906 </head>
   907 <body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();"
   908 	onkeypress="keypress(event);">
   909     <noscript lang="javascript">
   910       <center>
   911 	<p><big>Framed Navigation controls require Javascript</big><br />
   912 	Either this browser is incompatable or javascript is not enabled</p>
   913       </center>
   914     </noscript>
   915     <table width="100%" border="0" align="center">
   916 	<tr>
   917           <td valign="middle" width="25%">Diff navigation:
   918           Use 'j' and 'k' for next and previous diffs; or use buttons
   919           at right</td>
   920 	  <td align="center" valign="top" width="50%">
   921 	    <div class="button">
   922 	      <table border="0" align="center">
   923                   <tr>
   924 		    <td>
   925 		      <a onMouseDown="handlePress(1);return true;"
   926 			 onMouseUp="handleRelease(1);return true;"
   927 			 onMouseOut="handleRelease(1);return true;"
   928 			 onClick="return false;"
   929 			 title="Go to Beginning Of file">BOF</a></td>
   930 		    <td>
   931 		      <a onMouseDown="handlePress(3);return true;"
   932 			 onMouseUp="handleRelease(3);return true;"
   933 			 onMouseOut="handleRelease(3);return true;"
   934 			 title="Scroll Up: Press and Hold to accelerate"
   935 			 onClick="return false;">Scroll Up</a></td>
   936 		    <td>
   937 		      <a onMouseDown="handlePress(2);return true;"
   938 			 onMouseUp="handleRelease(2);return true;"
   939 			 onMouseOut="handleRelease(2);return true;"
   940 			 title="Go to previous Diff"
   941 			 onClick="return false;">Prev Diff</a>
   942 		    </td></tr>
   944 		  <tr>
   945 		    <td>
   946 		      <a onMouseDown="handlePress(6);return true;"
   947 			 onMouseUp="handleRelease(6);return true;"
   948 			 onMouseOut="handleRelease(6);return true;"
   949 			 onClick="return false;"
   950 			 title="Go to End Of File">EOF</a></td>
   951 		    <td>
   952 		      <a onMouseDown="handlePress(4);return true;"
   953 			 onMouseUp="handleRelease(4);return true;"
   954 			 onMouseOut="handleRelease(4);return true;"
   955 			 title="Scroll Down: Press and Hold to accelerate"
   956 			 onClick="return false;">Scroll Down</a></td>
   957 		    <td>
   958 		      <a onMouseDown="handlePress(5);return true;"
   959 			 onMouseUp="handleRelease(5);return true;"
   960 			 onMouseOut="handleRelease(5);return true;"
   961 			 title="Go to next Diff"
   962 			 onClick="return false;">Next Diff</a></td>
   963 		  </tr>
   964               </table>
   965 	    </div>
   966 	  </td>
   967 	  <th valign="middle" width="25%">
   968 	    <form action="" name="diff" onsubmit="return ValidateDiffNum();">
   969 		<input name="display" value="BOF" size="8" type="text" />
   970 		<input name="real" value="0" size="8" type="hidden" />
   971 	    </form>
   972 	  </th>
   973 	</tr>
   974     </table>
   975   </body>
   976 </html>
   977 EOF
   978 }
   982 #
   983 # diff_to_html <filename> <filepath> { U | C } <comment>
   984 #
   985 # Processes the output of diff to produce an HTML file representing either
   986 # context or unified diffs.
   987 #
   988 diff_to_html()
   989 {
   990 	TNAME=$1
   991 	TPATH=$2
   992 	DIFFTYPE=$3
   993 	COMMENT=$4
   995 	print "$HTML<head>$STDHEAD"
   996 	print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
   998 	if [[ $DIFFTYPE == "U" ]]; then
   999 		print "$UDIFFCSS"
  1000 	fi
  1002 	cat <<-EOF
  1003 	</head>
  1004 	<body id="SUNWwebrev">
  1005 	<h2>$TPATH</h2>
  1006         <a class="print" href="javascript:print()">Print this page</a>
  1007 	<pre>$COMMENT</pre>
  1008         <pre>
  1009 EOF
  1011 	html_quote | $AWK '
  1012 	/^--- new/	{ next }
  1013 	/^\+\+\+ new/	{ next }
  1014 	/^--- old/	{ next }
  1015 	/^\*\*\* old/	{ next }
  1016 	/^\*\*\*\*/	{ next }
  1017 	/^-------/	{ printf "<center><h1>%s</h1></center>\n", $0; next }
  1018 	/^\@\@.*\@\@$/	{ printf "</pre><hr /><pre>\n";
  1019 			  printf "<span class=\"newmarker\">%s</span>\n", $0;
  1020 			  next}
  1022 	/^\*\*\*/	{ printf "<hr /><span class=\"oldmarker\">%s</span>\n", $0;
  1023 			  next}
  1024 	/^---/		{ printf "<span class=\"newmarker\">%s</span>\n", $0;
  1025 			  next}
  1026 	/^\+/		{printf "<span class=\"new\">%s</span>\n", $0; next}
  1027 	/^!/		{printf "<span class=\"changed\">%s</span>\n", $0; next}
  1028 	/^-/		{printf "<span class=\"removed\">%s</span>\n", $0; next}
  1029 			{printf "%s\n", $0; next}
  1032 	print "</pre></body></html>\n"
  1037 # source_to_html { new | old } <filename>
  1039 # Process a plain vanilla source file to transform it into an HTML file.
  1041 source_to_html()
  1043 	WHICH=$1
  1044 	TNAME=$2
  1046 	print "$HTML<head>$STDHEAD"
  1047 	print "<title>$WHICH $TNAME</title>"
  1048 	print "<body id=\"SUNWwebrev\">"
  1049 	print "<pre>"
  1050 	html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
  1051 	print "</pre></body></html>"
  1054 comments_from_mercurial()
  1056 	fmt=$1
  1057 	pfile=$PWS/$2
  1058 	cfile=$CWS/$3
  1060         logdir=`dirname $cfile`
  1061         logf=`basename $cfile`
  1062         if [ -d $logdir ]; then
  1063             ( cd $logdir;
  1064 	        active=`hg status $logf 2>/dev/null`
  1065                 # If the output from 'hg status' is not empty, it means the file
  1066                 # hasn't been committed, so don't fetch comments.
  1067 	        if [[ -z $active ]] ; then
  1068                     if [[ -n $ALL_CREV ]]; then
  1069                         rev_opt=
  1070                         for rev in $ALL_CREV; do
  1071                             rev_opt="$rev_opt --rev $rev"
  1072                         done
  1073                         comm=`hg log $rev_opt --follow --template 'rev {rev} : {desc}\n' $logf`
  1074                     elif [[ -n $FIRST_CREV ]]; then
  1075 		        comm=`hg log --rev $FIRST_CREV:tip --follow --template 'rev {rev} : {desc}\n' $logf`
  1076                     else
  1077 		        comm=`hg log -l1 --follow --template 'rev {rev} : {desc}\n' $logf`
  1078                     fi
  1079 	        else
  1080 	            comm=""
  1081 	        fi
  1082 	        if [[ $fmt == "text" ]]; then
  1083 	            print "$comm"
  1084 	            return
  1085 	        fi
  1087 	        print "$comm" | html_quote | bug2url
  1089         fi
  1094 # getcomments {text|html} filepath parentpath
  1096 # Fetch the comments depending on what SCM mode we're in.
  1098 getcomments()
  1100 	typeset fmt=$1
  1101 	typeset p=$2
  1102 	typeset pp=$3
  1104 	comments_from_mercurial $fmt $pp $p
  1108 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
  1110 # Print out Code Inspection figures similar to sccs-prt(1) format.
  1112 function printCI
  1114 	integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
  1115 	typeset str
  1116 	if (( tot == 1 )); then
  1117 		str="line"
  1118 	else
  1119 		str="lines"
  1120 	fi
  1121 	printf '%d %s changed: %d ins; %d del; %d mod; %d unchg' \
  1122 	    $tot $str $ins $del $mod $unc
  1127 # difflines <oldfile> <newfile>
  1129 # Calculate and emit number of added, removed, modified and unchanged lines,
  1130 # and total lines changed, the sum of added + removed + modified.
  1132 function difflines
  1134 	integer tot mod del ins unc err
  1135 	typeset filename
  1137 	eval $( diff -e $1 $2 | $AWK '
  1138 	# Change range of lines: N,Nc
  1139 	/^[0-9]*,[0-9]*c$/ {
  1140 		n=split(substr($1,1,length($1)-1), counts, ",");
  1141 		if (n != 2) {
  1142 		    error=2
  1143 		    exit;
  1146 		# 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines.
  1147 		# following would be 5 - 3 = 2! Hence +1 for correction.
  1149 		r=(counts[2]-counts[1])+1;
  1152 		# Now count replacement lines: each represents a change instead
  1153 		# of a delete, so increment c and decrement r.
  1155 		while (getline != /^\.$/) {
  1156 			c++;
  1157 			r--;
  1160 		# If there were more replacement lines than original lines,
  1161 		# then r will be negative; in this case there are no deletions,
  1162 		# but there are r changes that should be counted as adds, and
  1163 		# since r is negative, subtract it from a and add it to c.
  1165 		if (r < 0) {
  1166 			a-=r;
  1167 			c+=r;
  1171 		# If there were more original lines than replacement lines, then
  1172 		# r will be positive; in this case, increment d by that much.
  1174 		if (r > 0) {
  1175 			d+=r;
  1177 		next;
  1180 	# Change lines: Nc
  1181 	/^[0-9].*c$/ {
  1182 		# The first line is a replacement; any more are additions.
  1183 		if (getline != /^\.$/) {
  1184 			c++;
  1185 			while (getline != /^\.$/) a++;
  1187 		next;
  1190 	# Add lines: both Na and N,Na
  1191 	/^[0-9].*a$/ {
  1192 		while (getline != /^\.$/) a++;
  1193 		next;
  1196 	# Delete range of lines: N,Nd
  1197 	/^[0-9]*,[0-9]*d$/ {
  1198 		n=split(substr($1,1,length($1)-1), counts, ",");
  1199 		if (n != 2) {
  1200 			error=2
  1201 			exit;
  1204 		# 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines.
  1205 		# following would be 5 - 3 = 2! Hence +1 for correction.
  1207 		r=(counts[2]-counts[1])+1;
  1208 		d+=r;
  1209 		next;
  1212 	# Delete line: Nd.   For example 10d says line 10 is deleted.
  1213 	/^[0-9]*d$/ {d++; next}
  1215 	# Should not get here!
  1217 		error=1;
  1218 		exit;
  1221 	# Finish off - print results
  1222 	END {
  1223 		printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n",
  1224 		    (c+d+a), c, d, a, error);
  1225 	}' )
  1227 	# End of $AWK, Check to see if any trouble occurred.
  1228 	if (( $? > 0 || err > 0 )); then
  1229 		print "Unexpected Error occurred reading" \
  1230 		    "\`diff -e $1 $2\`: \$?=$?, err=" $err
  1231 		return
  1232 	fi
  1234 	# Accumulate totals
  1235 	(( TOTL += tot ))
  1236 	(( TMOD += mod ))
  1237 	(( TDEL += del ))
  1238 	(( TINS += ins ))
  1239 	# Calculate unchanged lines
  1240 	unc=`wc -l < $1`
  1241 	if (( unc > 0 )); then
  1242 		(( unc -= del + mod ))
  1243 		(( TUNC += unc ))
  1244 	fi
  1245 	# print summary
  1246 	print "<span class=\"lineschanged\">\c"
  1247 	printCI $tot $ins $del $mod $unc
  1248 	print "</span>"
  1251 function outgoing_from_mercurial_forest
  1253     hg foutgoing --template 'rev: {rev}\n' $OUTPWS | $FILTER | $AWK '
  1254         BEGIN           {ntree=0}
  1255         /^comparing/    {next}
  1256         /^no changes/   {next}
  1257         /^searching/    {next}
  1258 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2);
  1259                          trees[ntree++] = tree;
  1260                          revs[tree]=-1;
  1261                          next}
  1262         /^rev:/   {rev=$2+0;
  1263                    if (revs[tree] == -1 || rev < revs[tree])
  1264                         { revs[tree] = rev; };
  1265                   next;}
  1266         END       {for (tree in trees)
  1267                         { rev=revs[trees[tree]];
  1268                           if (rev > 0)
  1269                                 {printf("%s %d\n",trees[tree],rev-1)}
  1270                         }}' | while read LINE
  1271     do
  1272         set - $LINE
  1273         TREE=$1
  1274         REV=$2
  1275         A=`hg -R $CWS/$TREE log --rev $REV --template '{node}'`
  1276         FSTAT_OPT="--rev $A"
  1277         print "Revision: $A $REV" >> $FLIST
  1278         treestatus $TREE
  1279     done
  1282 function flist_from_mercurial_forest
  1284     rm -f $FLIST
  1285     if [ -z "$Nflag" ]; then
  1286         print " File list from hg foutgoing $PWS ..."
  1287         outgoing_from_mercurial_forest
  1288         HG_LIST_FROM_COMMIT=1
  1289     fi
  1290     if [ ! -f $FLIST ]; then
  1291         # hg commit hasn't been run see what is lying around
  1292         print "\n No outgoing, perhaps you haven't commited."
  1293         print " File list from hg fstatus -mard ...\c"
  1294         FSTAT_OPT=
  1295         fstatus
  1296         HG_LIST_FROM_COMMIT=
  1297     fi
  1298     print " Done."
  1302 # Used when dealing with the result of 'hg foutgoing'
  1303 # When now go down the tree and generate the change list
  1305 function treestatus
  1307     TREE=$1
  1308     HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT"
  1310     $HGCMD -mdn 2>/dev/null | $FILTER | while read F
  1311     do
  1312         echo $TREE/$F
  1313     done >> $FLIST
  1315     # Then all the added files
  1316     # But some of these could have been "moved" or renamed ones or copied ones
  1317     # so let's make sure we get the proper info
  1318     # hg status -aC will produce something like:
  1319     #	A subdir/File3
  1320     #	A subdir/File4
  1321     #	  File4
  1322     #	A subdir/File5
  1323     # The first and last are simple addition while the middle one
  1324     # is a move/rename or a copy.  We can't distinguish from a rename vs a copy
  1325     # without also getting the status of removed files.  The middle case above
  1326     # is a rename if File4 is also shown a being removed.  If File4 is not a
  1327     # removed file, then the middle case is a copy from File4 to subdir/File4
  1328     # FIXME - we're not distinguishing copy from rename
  1329     $HGCMD -aC | $FILTER | while read LINE; do
  1330 	ldone=""
  1331 	while [ -z "$ldone" ]; do
  1332 	    ldone="1"
  1333 	    set - $LINE
  1334 	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1335 		AFILE=$2
  1336 		if read LINE2; then
  1337 		    set - $LINE2
  1338 		    if [ $# -eq 1 ]; then
  1339 			echo $TREE/$AFILE $TREE/$1 >>$FLIST
  1340 		    elif [ $# -eq 2 ]; then
  1341 			echo $TREE/$AFILE >>$FLIST
  1342 			LINE=$LINE2
  1343 			ldone=""
  1344 		    fi
  1345 		else
  1346 		    echo $TREE/$AFILE >>$FLIST
  1347 		fi
  1348 	    fi
  1349 	done
  1350     done
  1351     $HGCMD -rn | $FILTER | while read RFILE; do
  1352 	grep "$TREE/$RFILE" $FLIST >/dev/null
  1353 	if [ $? -eq 1 ]; then
  1354 	    echo $TREE/$RFILE >>$FLIST
  1355 	fi
  1356     done
  1359 function fstatus
  1362     # forest extension is still being changed. For instance the output
  1363     # of fstatus used to no prepend the tree path to filenames, but
  1364     # this has changed recently. AWK code below does try to handle both
  1365     # cases
  1367     hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1368 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1369 	$1 != ""	{n=index($1,tree);
  1370 			 if (n == 0)
  1371 				{ printf("%s/%s\n",tree,$1)}
  1372 			 else
  1373 				{ printf("%s\n",$1)}}' >> $FLIST
  1376     # There is a bug in the output of fstatus -aC on recent versions: it
  1377     # inserts a space between the name of the tree and the filename of the
  1378     # old file. e.g.:
  1380     # $ hg fstatus -aC
  1381     # [.]
  1383     # [MyWS]
  1384     # A MyWS/subdir/File2
  1385     #  MyWS/ File2
  1387     # [MyWS2]
  1390     hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1391 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1392 	/^A .*/		{n=index($2,tree);
  1393 			 if (n == 0)
  1394 				{ printf("A %s/%s\n",tree,$2)}
  1395 			 else
  1396 				{ printf("A %s\n",$2)};
  1397 			 next}
  1398 	/^ /		{n=index($1,tree);
  1399 			 if (n == 0)
  1400 				{ printf("%s/%s\n",tree,$1)}
  1401 			 else
  1402 				{ if (NF == 2)
  1403 					printf("%s/%s\n",tree,$2)
  1404 				  else
  1405 					printf("%s\n",$1)
  1406 				};
  1407 			 next}
  1408 	' | while read LINE; do
  1409 	ldone=""
  1410 	while [ -z "$ldone" ]; do
  1411 	    ldone="1"
  1412 	    set - $LINE
  1413 	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1414 		AFILE=$2
  1415 		if read LINE2; then
  1416 		    set - $LINE2
  1417 		    if [ $# -eq 1 ]; then
  1418 			echo $AFILE $1 >>$FLIST
  1419 		    elif [ $# -eq 2 ]; then
  1420 			echo $AFILE >>$FLIST
  1421 			LINE=$LINE2
  1422 			ldone=""
  1423 		    fi
  1424 		else
  1425 		    echo $AFILE >>$FLIST
  1426 		fi
  1427 	    fi
  1428 	done
  1429     done
  1430     hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1431 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1432 	$1 != ""	{n=index($1,tree);
  1433 			 if (n == 0)
  1434 				{ printf("%s/%s\n",tree,$1)}
  1435 			 else
  1436 				{ printf("%s\n",$1)}}' | while read RFILE; do
  1437 	grep "$RFILE" $FLIST >/dev/null
  1438 	if [ $? -eq 1 ]; then
  1439 	    echo $RFILE >>$FLIST
  1440 	fi
  1441     done
  1445 # flist_from_mercurial $PWS
  1447 # Only local file based repositories are supported at present
  1448 # since even though we can determine the list from the parent finding
  1449 # the changes is harder.
  1451 # We first look for any outgoing files, this is for when the user has
  1452 # run hg commit.  If we don't find any then we look with hg status.
  1454 # We need at least one of default-push or default paths set in .hg/hgrc
  1455 # If neither are set we don't know who to compare with.
  1457 function flist_from_mercurial
  1459 #	if [ "${PWS##ssh://}" != "$PWS" -o \
  1460 #	     "${PWS##http://}" != "$PWS" -o \
  1461 #	     "${PWS##https://}" != "$PWS" ]; then
  1462 #		print "Remote Mercurial repositories not currently supported."
  1463 #		print "Set default and/or default-push to a local repository"
  1464 #		exit
  1465 #	fi
  1466     if [[ -n $forestflag ]]; then
  1467         HG_LIST_FROM_COMMIT=
  1468 	flist_from_mercurial_forest
  1469     else
  1470         STATUS_REV=
  1471         if [[ -n $rflag ]]; then
  1472             STATUS_REV="--rev $PARENT_REV"
  1473         elif [[ -n $OUTREV ]]; then
  1474             STATUS_REV="--rev $OUTREV"
  1475         else
  1476             # hg commit hasn't been run see what is lying around
  1477             print "\n No outgoing, perhaps you haven't commited."
  1478         fi
  1479 	# First let's list all the modified or deleted files
  1481 	hg status $STATUS_REV -mdn | $FILTER > $FLIST
  1483 	# Then all the added files
  1484 	# But some of these could have been "moved" or renamed ones
  1485 	# so let's make sure we get the proper info
  1486 	# hg status -aC will produce something like:
  1487 	#	A subdir/File3
  1488 	#	A subdir/File4
  1489 	#	  File4
  1490 	#	A subdir/File5
  1491         # The first and last are simple addition while the middle one
  1492         # is a move/rename or a copy.  We can't distinguish from a rename vs a copy
  1493         # without also getting the status of removed files.  The middle case above
  1494         # is a rename if File4 is also shown a being removed.  If File4 is not a
  1495         # removed file, then the middle case is a copy from File4 to subdir/File4
  1496         # FIXME - we're not distinguishing copy from rename
  1498 	hg status $STATUS_REV -aC | $FILTER >$FLIST.temp
  1499 	while read LINE; do
  1500 	    ldone=""
  1501 	    while [ -z "$ldone" ]; do
  1502 		ldone="1"
  1503 		set - $LINE
  1504 		if [ $# -eq 2 -a "$1" == "A" ]; then
  1505 		    AFILE=$2
  1506 		    if read LINE2; then
  1507 			set - $LINE2
  1508 			if [ $# -eq 1 ]; then
  1509 			    echo $AFILE $1 >>$FLIST
  1510 			elif [ $# -eq 2 ]; then
  1511 			    echo $AFILE >>$FLIST
  1512 			    LINE=$LINE2
  1513 			    ldone=""
  1514 			fi
  1515 		    else
  1516 			echo $AFILE >>$FLIST
  1517 		    fi
  1518 		fi
  1519 	    done
  1520 	done < $FLIST.temp
  1521 	hg status $STATUS_REV -rn | $FILTER > $FLIST.temp
  1522 	while read RFILE; do
  1523 	    grep "$RFILE" $FLIST >/dev/null
  1524 	    if [ $? -eq 1 ]; then
  1525 		echo $RFILE >>$FLIST
  1526 	    fi
  1527 	done < $FLIST.temp
  1528 	rm -f $FLIST.temp
  1529     fi
  1532 function env_from_flist
  1534 	[[ -r $FLIST ]] || return
  1537 	# Use "eval" to set env variables that are listed in the file
  1538 	# list.  Then copy those into our local versions of those
  1539 	# variables if they have not been set already.
  1541 	eval `sed -e "s/#.*$//" $FLIST | grep = `
  1543 	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
  1546 	# Check to see if CODEMGR_PARENT is set in the flist file.
  1548 	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
  1549 	    codemgr_parent=$CODEMGR_PARENT
  1553 # detect_scm
  1555 # We dynamically test the SCM type; this allows future extensions to
  1556 # new SCM types
  1558 function detect_scm
  1560 	if hg root >/dev/null ; then
  1561 		print "mercurial"
  1562 	else
  1563 		print "unknown"
  1564 	fi
  1567 function look_for_prog
  1569 	typeset path
  1570 	typeset ppath
  1571 	typeset progname=$1
  1573 	DEVTOOLS=
  1574 	OS=`uname`
  1575 	if [[ "$OS" == "SunOS" ]]; then
  1576 	    DEVTOOLS="/java/devtools/`uname -p`/bin"
  1577 	elif [[ "$OS" == "Linux" ]]; then
  1578 	    DEVTOOLS="/java/devtools/linux/bin"
  1579 	fi
  1581 	ppath=$PATH
  1582 	ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
  1583 	ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
  1584 	ppath=$ppath:/opt/onbld/bin/`uname -p`
  1585 	ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS
  1587 	PATH=$ppath prog=`whence $progname`
  1588 	if [[ -n $prog ]]; then
  1589 		print $prog
  1590 	fi
  1594 # Find the parent for $1
  1596 function find_outrev
  1598     crev=$1
  1599     prev=`hg log -r $crev --template '{parents}\n'`
  1600     if [[ -z "$prev" ]]
  1601     then
  1602 	# No specific parent means previous changeset is parent
  1603 	prev=`expr $crev - 1`
  1604     else
  1605 	# Format is either of the following two:
  1606 	# 546:7df6fcf1183b
  1607 	# 548:16f1915bb5cd 547:ffaa4e775815
  1608 	prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'`
  1609     fi
  1610     print $prev
  1613 function extract_ssh_infos
  1615     CMD=$1
  1616     if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then
  1617 	ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'`
  1618 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'`
  1619 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'`
  1620     else
  1621 	ssh_user=
  1622 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'`
  1623 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'`
  1624     fi
  1628 function build_old_new_mercurial
  1630 	olddir=$1
  1631 	newdir=$2
  1632 	DIR=$3
  1633 	F=$4
  1635 	# new version of the file.
  1637 	rm -rf $newdir/$DIR/$F
  1638 	if [ -f $F ]; then
  1639 	    cp $F  $newdir/$DIR/$F
  1640 	fi
  1643 	# Old version of the file.
  1645 	rm -rf $olddir/$DIR/$F
  1647 	if [ -n "$PWS" ]; then
  1648 	    if expr "$PWS" : 'ssh://' >/dev/null
  1649 	    then
  1650 		extract_ssh_infos $PWS
  1651 		if [ -n "$ssh_user" ]; then
  1652 		    parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1653 		else
  1654 		    parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1655 		fi
  1656 	    else
  1657 		parent="hg -R $PWS --cwd $PWS"
  1658 	    fi
  1659 	else
  1660 	    parent=""
  1661 	fi
  1663 	if [ -z "$rename" ]; then
  1664 	    if [ -n "$rflag" ]; then
  1665 		parentrev=$PARENT_REV
  1666 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1667                 parentrev=$OUTREV
  1668 	    else
  1669                 if [[ -n $HG_BRANCH ]]; then
  1670                     parentrev=$HG_BRANCH
  1671                 else
  1672 		    parentrev="tip"
  1673                 fi
  1674 	    fi
  1676 	    if [ -n "$parentrev" ]; then
  1677 		if [ -z "$parent" ]; then
  1678 		    hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null
  1679 		else
  1680 		    # when specifying a workspace we have to provide
  1681 		    # the full path
  1682 		    $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null
  1683 		fi
  1684 	    fi
  1685 	else
  1686 	    # It's a rename (or a move), or a copy, so let's make sure we move
  1687 	    # to the right directory first, then restore it once done
  1688 	    current_dir=`pwd`
  1689 	    cd $CWS/$PDIR
  1690 	    if [ -n "$rflag" ]; then
  1691 		parentrev=$PARENT_REV
  1692 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1693                 parentrev=$OUTREV
  1694 	    fi
  1695 	    if [ -z "$parentrev" ]; then
  1696 		parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'`
  1697 	    fi
  1698 	    if [ -n "$parentrev" ]; then
  1699 		mkdir -p $olddir/$PDIR
  1700 		if [ -z "$parent" ]; then
  1701 		    hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null
  1702 		else
  1703 		    $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
  1704 		fi
  1705 	    fi
  1706 	    cd $current_dir
  1707 	fi
  1710 function build_old_new
  1712 	if [[ $SCM_MODE == "mercurial" ]]; then
  1713 		build_old_new_mercurial $@
  1714 	fi
  1719 # Usage message.
  1721 function usage
  1723 	print "Usage:\twebrev [options]
  1724 	webrev [options] ( <file> | - )
  1726 Options:
  1727 	-v: Print the version of this tool.
  1728         -b: Do not ignore changes in the amount of white space.
  1729         -c <CR#>: Include link to CR (aka bugid) in the main page.
  1730 	-i <filename>: Include <filename> in the index.html file.
  1731 	-o <outdir>: Output webrev to specified directory.
  1732 	-p <compare-against>: Use specified parent wkspc or basis for comparison
  1733         -u <username>: Use that username instead of 'guessing' one.
  1734 	-m: Forces the use of Mercurial
  1736 Mercurial only options:
  1737 	-r rev: Compare against a specified revision
  1738 	-N: Skip 'hg outgoing', use only 'hg status'
  1739 	-f: Use the forest extension
  1741 Arguments:
  1742 	<file>: Optional file containing list of files to include in webrev
  1743         -: read list of files to include in webrev from standard input
  1745 Environment:
  1746 	WDIR: Control the output directory.
  1747 	WEBREV_BUGURL: Control the URL prefix for bugids.
  1751 	exit 2
  1756 # Main program starts here
  1759 LANG="C"
  1760 LC_ALL="C"
  1761 export LANG LC_ALL
  1762 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
  1764 set +o noclobber
  1766 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
  1767 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
  1768 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
  1769 [[ -z $PERL ]] && PERL=`look_for_prog perl`
  1770 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
  1771 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
  1772 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
  1773 [[ -z $AWK ]] && AWK=`look_for_prog awk`
  1774 [[ -z $JAR ]] && JAR=`look_for_prog jar`
  1775 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
  1776 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
  1777 [[ -z $WGET ]] && WGET=`look_for_prog wget`
  1779 if uname | grep CYGWIN >/dev/null
  1780 then
  1781         ISWIN=1
  1782         # Under windows mercurial outputs '\' instead of '/'
  1783         FILTER="tr '\\\\' '/'"
  1784 else
  1785         FILTER="cat"
  1786 fi
  1788 if [[ ! -x $PERL ]]; then
  1789 	print -u2 "Error: No perl interpreter found.  Exiting."
  1790 	exit 1
  1791 fi
  1794 # These aren't fatal, but we want to note them to the user.
  1796 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
  1797 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
  1798 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
  1800 # Declare global total counters.
  1801 integer TOTL TINS TDEL TMOD TUNC
  1803 flist_mode=
  1804 flist_file=
  1805 bflag=
  1806 iflag=
  1807 oflag=
  1808 pflag=
  1809 uflag=
  1810 Oflag=
  1811 rflag=
  1812 Nflag=
  1813 forestflag=
  1814 while getopts "c:i:o:p:r:u:mONvfb" opt
  1815 do
  1816 	case $opt in
  1817         b)      bflag=1;;
  1819 	i)	iflag=1
  1820 		INCLUDE_FILE=$OPTARG;;
  1822 	o)	oflag=1
  1823 		WDIR=$OPTARG;;
  1825 	p)	pflag=1
  1826 		codemgr_parent=$OPTARG;;
  1828 	u)      uflag=1
  1829 		username=$OPTARG;;
  1831         c)      if [[ -z $CRID ]]; then
  1832                    CRID=$OPTARG
  1833                 else
  1834                    CRID="$CRID $OPTARG"
  1835                 fi;;
  1837 	m)	SCM_MODE="mercurial";;
  1839 	O)	Oflag=1;; # ignored (bugs are now all visible at bugs.openjdk.java.net)
  1841 	N)	Nflag=1;;
  1843 	f)	forestflag=1;;
  1845 	r)	rflag=1
  1846 		PARENT_REV=$OPTARG;;
  1848 	v)	print "$0 version: $WEBREV_UPDATED";;
  1851 	?)	usage;;
  1852 	esac
  1853 done
  1855 FLIST=/tmp/$$.flist
  1856 HG_LIST_FROM_COMMIT=
  1858 if [[ -n $forestflag && -n $rflag ]]; then
  1859     print "The -r <rev> flag is incompatible with the use of forests"
  1860     exit 2
  1861 fi
  1864 # If this manually set as the parent, and it appears to be an earlier webrev,
  1865 # then note that fact and set the parent to the raw_files/new subdirectory.
  1867 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
  1868 	parent_webrev="$codemgr_parent"
  1869 	codemgr_parent="$codemgr_parent/raw_files/new"
  1870 fi
  1872 shift $(($OPTIND - 1))
  1874 if [[ $1 == "-" ]]; then
  1875 	cat > $FLIST
  1876 	flist_mode="stdin"
  1877 	flist_done=1
  1878 	shift
  1879 elif [[ -n $1 ]]; then
  1880 	if [[ ! -r $1 ]]; then
  1881 		print -u2 "$1: no such file or not readable"
  1882 		usage
  1883 	fi
  1884 	cat $1 > $FLIST
  1885 	flist_mode="file"
  1886 	flist_file=$1
  1887 	flist_done=1
  1888 	shift
  1889 else
  1890 	flist_mode="auto"
  1891 fi
  1894 # Before we go on to further consider -l and -w, work out which SCM we think
  1895 # is in use.
  1897 if [[ -z $SCM_MODE ]]; then
  1898     SCM_MODE=`detect_scm $FLIST`
  1899 fi
  1900 if [[ $SCM_MODE == "unknown" ]]; then
  1901        print -u2 "Unable to determine SCM type currently in use."
  1902        print -u2 "For mercurial: webrev runs 'hg root'."
  1903        exit 1
  1904 fi
  1906 print -u2 "   SCM detected: $SCM_MODE"
  1909 if [[ $SCM_MODE == "mercurial" ]]; then
  1911     # determine Workspace and parent workspace paths
  1913     CWS=`hg root | $FILTER`
  1914     if [[ -n $pflag && -z "$PWS" ]]; then
  1915 	OUTPWS=$codemgr_parent
  1916         # Let's try to expand it if it's an alias defined in [paths]
  1917         tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  1918         if [[ -n $tmp ]]; then
  1919             OUTPWS="$tmp"
  1920         fi
  1921         if [[ -n $rflag ]]; then
  1922 	    if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
  1923 	        PWS=$codemgr_parent
  1924 	    else
  1925 	        PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
  1926 	    fi
  1927         fi
  1928     fi
  1930     # OUTPWS is the parent repository to use when using 'hg outgoing'
  1932     if [[ -z $Nflag ]]; then
  1933         if [[ -n $forestflag ]]; then
  1935             # for forest we have to rely on properly set default and
  1936             # default-push because they can be different from the top one.
  1937             # unless of course it was explicitly specified with -p
  1938             if [[ -z $pflag ]]; then
  1939                 OUTPWS=
  1940             fi
  1941         else
  1943             # Unfortunately mercurial is bugged and doesn't handle
  1944             # aliases correctly in 'hg path default'
  1945             # So let's do it ourselves. Sigh...
  1946             if [[ -z "$OUTPWS" ]]; then
  1947                 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  1948             fi
  1949             # Still empty, means no default-push
  1950             if [[ -z "$OUTPWS" ]]; then
  1951                 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  1952             fi
  1953             # Let's try to expand it if it's an alias defined in [paths]
  1954             tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  1955             if [[ -n $tmp ]]; then
  1956                 OUTPWS="$tmp"
  1957             fi
  1958         fi
  1959     fi
  1961     # OUTPWS may contain username:password, let's make sure we remove the
  1962     # sensitive information before we print out anything in the HTML
  1964     OUTPWS2=$OUTPWS
  1965     if [[ -n $OUTPWS ]]; then
  1966 	if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
  1967 	    # Remove everything between '://' and '@'
  1968 	    OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
  1969 	fi
  1970     fi
  1972     if [[ -z $HG_BRANCH ]]; then
  1973         HG_BRANCH=`hg branch`
  1974         if [ "$HG_BRANCH" == "default" ]; then
  1976             # 'default' means no particular branch, so let's cancel that
  1978             HG_BRANCH=
  1979         fi
  1980     fi
  1982     if [[ -z $forestflag ]]; then
  1983         if [[ -z $Nflag ]]; then
  1985             # If no "-N", always do "hg outgoing" against parent
  1986             # repository to determine list of outgoing revisions.
  1988             ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
  1989             if [[ -n $ALL_CREV ]]; then
  1990                 FIRST_CREV=`echo "$ALL_CREV" | head -1`
  1992                 # If no "-r", choose revision to compare against by
  1993                 # finding the latest revision not in the outgoing list.
  1995                 if [[ -z $rflag ]]; then
  1996                     OUTREV=`find_outrev "$FIRST_CREV"`
  1997                     if [[ -n $OUTREV ]]; then
  1998                         HG_LIST_FROM_COMMIT=1
  1999                     fi
  2000                 fi
  2001             fi
  2002         elif [[ -n $rflag ]]; then
  2004             # If skipping "hg outgoing" but still comparing against a
  2005             # specific revision (not the tip), set revision for comment
  2006             # accumulation.
  2008             FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
  2009             FIRST_CREV=`expr $FIRST_CREV + 1`
  2010         fi
  2011     fi
  2012     #Let's check if a merge is needed, if so, issue a warning
  2013     PREV=`hg parent | grep '^tag:.*tip$'`
  2014     if [[ -z $PREV ]]; then
  2015         print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
  2016     fi
  2017 fi
  2019 if [[ $flist_mode == "stdin" ]]; then
  2020 	print -u2 " File list from: standard input"
  2021 elif [[ $flist_mode == "file" ]]; then
  2022 	print -u2 " File list from: $flist_file"
  2023 fi
  2025 if [[ $# -gt 0 ]]; then
  2026 	print -u2 "WARNING: unused arguments: $*"
  2027 fi
  2029 if [[ $SCM_MODE == "mercurial" ]]; then
  2030     if [[ -z $flist_done ]]; then
  2031 	flist_from_mercurial $PWS
  2032     fi
  2033 fi
  2036 # If the user didn't specify a -i option, check to see if there is a
  2037 # webrev-info file in the workspace directory.
  2039 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
  2040 	iflag=1
  2041 	INCLUDE_FILE="$CWS/webrev-info"
  2042 fi
  2044 if [[ -n $iflag ]]; then
  2045 	if [[ ! -r $INCLUDE_FILE ]]; then
  2046 		print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
  2047 		    "not readable."
  2048 		exit 1
  2049 	else
  2051 		# $INCLUDE_FILE may be a relative path, and the script alters
  2052 		# PWD, so we just stash a copy in /tmp.
  2054 		cp $INCLUDE_FILE /tmp/$$.include
  2055 	fi
  2056 fi
  2059 # Output directory.
  2061 if [[ -z $WDIR ]]; then
  2062     WDIR=$CWS/webrev
  2063 else
  2064     # If the output directory doesn't end with '/webrev' or '/webrev/'
  2065     # then add '/webrev'. This is for backward compatibility
  2066     if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
  2067     then
  2068 	WDIR=$WDIR/webrev
  2069     fi
  2070 fi
  2071 # WDIR=${WDIR:-$CWS/webrev}
  2074 # Name of the webrev, derived from the workspace name; in the
  2075 # future this could potentially be an option.
  2077 # Let's keep what's after the last '/'
  2078 WNAME=${CWS##*/}
  2081 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
  2083 if [ ${WDIR%%/*} ]; then
  2084     if [[ -n $ISWIN ]]; then
  2085         if [ ${WDIR%%[A-Za-z]:*} ]; then
  2086 	    WDIR=$PWD/$WDIR
  2087         fi
  2088     else
  2089 	WDIR=$PWD/$WDIR
  2090     fi
  2091 fi
  2093 if [[ ! -d $WDIR ]]; then
  2094 	mkdir -p $WDIR
  2095 	[[ $? != 0 ]] && exit 1
  2096 fi
  2099 # Summarize what we're going to do.
  2101 print "      Workspace: $CWS"
  2102 if [[ -n $parent_webrev ]]; then
  2103     print "Compare against: webrev at $parent_webrev"
  2104 elif [[ -n $OUTPWS2 ]]; then
  2105     print "Compare against: $OUTPWS2"
  2106 fi
  2107 if [[ -n $HG_BRANCH ]]; then
  2108     print "         Branch: $HG_BRANCH"
  2109 fi
  2110 if [[ -n $rflag ]]; then
  2111         print "Compare against version: $PARENT_REV"
  2112 fi
  2113 [[ -n $INCLUDE_FILE ]] && print "      Including: $INCLUDE_FILE"
  2114 print "      Output to: $WDIR"
  2117 # Save the file list in the webrev dir
  2119 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
  2122 #    Bug IDs will be replaced by a URL.  Order of precedence
  2123 #    is: default location, $WEBREV_BUGURL
  2125 BUGURL='https://bugs.openjdk.java.net/browse/'
  2126 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
  2127 IDPREFIX='JDK-'
  2130 rm -f $WDIR/$WNAME.patch
  2131 rm -f $WDIR/$WNAME.changeset
  2132 rm -f $WDIR/$WNAME.ps
  2133 rm -f $WDIR/$WNAME.pdf
  2135 touch $WDIR/$WNAME.patch
  2137 print "   Output Files:"
  2140 # Clean up the file list: Remove comments, blank lines and env variables.
  2142 sed -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
  2143 FLIST=/tmp/$$.flist.clean
  2146 # Clean up residual raw files
  2148 if [ -d $WDIR/raw_files ]; then
  2149     rm -rf $WDIR/raw_files 2>/dev/null
  2150 fi
  2153 # Should we ignore changes in white spaces when generating diffs?
  2155 if [[ -n $bflag ]]; then
  2156     DIFFOPTS="-t"
  2157 else
  2158     DIFFOPTS="-bt"
  2159 fi
  2161 # First pass through the files: generate the per-file webrev HTML-files.
  2163 while read LINE
  2164 do
  2165 	set - $LINE
  2166 	P=$1
  2168         if [[ $1 == "Revision:" ]]; then
  2169             OUTREV=$2
  2170             continue
  2171         fi
  2173 	# Normally, each line in the file list is just a pathname of a
  2174 	# file that has been modified or created in the child.  A file
  2175 	# that is renamed in the child workspace has two names on the
  2176 	# line: new name followed by the old name.
  2178 	oldname=""
  2179 	oldpath=""
  2180 	rename=
  2181 	if [[ $# -eq 2 ]]; then
  2182 		PP=$2			# old filename
  2183 		oldname=" (was $PP)"
  2184 		oldpath="$PP"
  2185 		rename=1
  2186         	PDIR=${PP%/*}
  2187         	if [[ $PDIR == $PP ]]; then
  2188 			PDIR="."   # File at root of workspace
  2189 		fi
  2191 		PF=${PP##*/}
  2193 	        DIR=${P%/*}
  2194 	        if [[ $DIR == $P ]]; then
  2195 			DIR="."   # File at root of workspace
  2196 		fi
  2198 		F=${P##*/}
  2199         else
  2200 	        DIR=${P%/*}
  2201 	        if [[ "$DIR" == "$P" ]]; then
  2202 			DIR="."   # File at root of workspace
  2203 		fi
  2205 		F=${P##*/}
  2207 		PP=$P
  2208 		PDIR=$DIR
  2209 		PF=$F
  2210 	fi
  2212         # Make the webrev directory if necessary as it may have been
  2213         # removed because it was empty
  2214         if [ ! -d $CWS/$DIR ]; then
  2215 	    mkdir -p $CWS/$DIR
  2216         fi
  2218 	COMM=`getcomments html $P $PP`
  2220 	print "\t$P$oldname\n\t\t\c"
  2222 	# Make the webrev mirror directory if necessary
  2223 	mkdir -p $WDIR/$DIR
  2225 	# cd to the directory so the names are short
  2226 	cd $CWS/$DIR
  2229 	# We stash old and new files into parallel directories in /tmp
  2230 	# and do our diffs there.  This makes it possible to generate
  2231 	# clean looking diffs which don't have absolute paths present.
  2233 	olddir=$WDIR/raw_files/old
  2234 	newdir=$WDIR/raw_files/new
  2235 	mkdir -p $olddir
  2236 	mkdir -p $newdir
  2237 	mkdir -p $olddir/$PDIR
  2238 	mkdir -p $newdir/$DIR
  2240 	build_old_new $olddir $newdir $DIR $F
  2242 	if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
  2243 		print "*** Error: file not in parent or child"
  2244 		continue
  2245 	fi
  2247 	cd $WDIR/raw_files
  2248 	ofile=old/$PDIR/$PF
  2249 	nfile=new/$DIR/$F
  2251 	mv_but_nodiff=
  2252 	cmp $ofile $nfile > /dev/null 2>&1
  2253 	if [[ $? == 0 && $rename == 1 ]]; then
  2254 		mv_but_nodiff=1
  2255 	fi
  2258         # Cleaning up
  2260         rm -f $WDIR/$DIR/$F.cdiff.html
  2261         rm -f $WDIR/$DIR/$F.udiff.html
  2262         rm -f $WDIR/$DIR/$F.wdiff.html
  2263         rm -f $WDIR/$DIR/$F.sdiff.html
  2264         rm -f $WDIR/$DIR/$F-.html
  2265         rm -f $WDIR/$DIR/$F.html
  2267 	its_a_jar=
  2268 	if expr $F : '.*\.jar' \| $F : '.*\.zip' >/dev/null; then
  2269 	    its_a_jar=1
  2270 	    # It's a JAR or ZIP file, let's do it differently
  2271 	    if [[ -z $JAR ]]; then
  2272 		print "No access to jar, so can't produce diffs for jar or zip files"
  2273 	    else
  2274 		if [ -f $ofile ]; then
  2275 		    $JAR -tvf $ofile >"$ofile".lst
  2276 		fi
  2277 		if [ -f $nfile ]; then
  2278 		    $JAR -tvf $nfile >"$nfile".lst
  2279 		fi
  2281 		if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2283 		    ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
  2284 		    diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2285 			> $WDIR/$DIR/$F.cdiff.html
  2286 		    print " cdiffs\c"
  2288 		    ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
  2289 		    diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2290 			> $WDIR/$DIR/$F.udiff.html
  2292 		    print " udiffs\c"
  2294 		    if [[ -x $WDIFF ]]; then
  2295 			$WDIFF -c "$COMM" \
  2296 			    -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
  2297 			    $WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2298 			if [[ $? -eq 0 ]]; then
  2299 			    print " wdiffs\c"
  2300 			else
  2301 			    print " wdiffs[fail]\c"
  2302 			fi
  2303 		    fi
  2305 		    sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2306 			> $WDIR/$DIR/$F.sdiff.html
  2307 		    print " sdiffs\c"
  2309 		    print " frames\c"
  2311 		    rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2313 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2315 		elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2316 		# renamed file: may also have differences
  2317 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2318 		elif [[ -f $nfile ]]; then
  2319 		# new file: count added lines
  2320 		    difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
  2321 		elif [[ -f $ofile ]]; then
  2322 		# old file: count deleted lines
  2323 		    difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
  2324 		fi
  2325 	    fi
  2326 	else
  2329 	    # If we have old and new versions of the file then run the
  2330 	    # appropriate diffs.  This is complicated by a couple of factors:
  2332 	    #	- renames must be handled specially: we emit a 'remove'
  2333 	    #	  diff and an 'add' diff
  2334 	    #	- new files and deleted files must be handled specially
  2335 	    #	- Solaris patch(1m) can't cope with file creation
  2336 	    #	  (and hence renames) as of this writing.
  2337 	    #   - To make matters worse, gnu patch doesn't interpret the
  2338 	    #	  output of Solaris diff properly when it comes to
  2339 	    #	  adds and deletes.  We need to do some "cleansing"
  2340 	    #     transformations:
  2341 	    # 	    [to add a file] @@ -1,0 +X,Y @@  -->  @@ -0,0 +X,Y @@
  2342 	    #	    [to del a file] @@ -X,Y +1,0 @@  -->  @@ -X,Y +0,0 @@
  2344 	    cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
  2345 	    cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
  2347             if [[ ! "$HG_LIST_FROM_COMMIT" -eq 1 || ! $flist_mode == "auto" ]];
  2348             then
  2349               # Only need to generate a patch file here if there are no commits in outgoing
  2350               # or if we've specified a file list
  2351               rm -f $WDIR/$DIR/$F.patch
  2352               if [[ -z $rename ]]; then
  2353                   if [ ! -f $ofile ]; then
  2354                       diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2355                           > $WDIR/$DIR/$F.patch
  2356                   elif [ ! -f $nfile ]; then
  2357                       diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2358                           > $WDIR/$DIR/$F.patch
  2359                   else
  2360                       diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
  2361                   fi
  2362               else
  2363                   diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2364                       > $WDIR/$DIR/$F.patch
  2366                   diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2367                       >> $WDIR/$DIR/$F.patch
  2369               fi
  2373             # Tack the patch we just made onto the accumulated patch for the
  2374             # whole wad.
  2376               cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
  2377             fi
  2379             print " patch\c"
  2381 	    if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2383 		${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
  2384 		diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2385 		    > $WDIR/$DIR/$F.cdiff.html
  2386 		print " cdiffs\c"
  2388 		${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
  2389 		diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2390 		    > $WDIR/$DIR/$F.udiff.html
  2392 		print " udiffs\c"
  2394 		if [[ -x $WDIFF ]]; then
  2395 		    $WDIFF -c "$COMM" \
  2396 			-t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
  2397 			$WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2398 		    if [[ $? -eq 0 ]]; then
  2399 			print " wdiffs\c"
  2400 		    else
  2401 			print " wdiffs[fail]\c"
  2402 		    fi
  2403 		fi
  2405 		sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2406 		    > $WDIR/$DIR/$F.sdiff.html
  2407 		print " sdiffs\c"
  2409 		print " frames\c"
  2411 		rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2413 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2415 	    elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2416 		# renamed file: may also have differences
  2417 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2418 	    elif [[ -f $nfile ]]; then
  2419 		# new file: count added lines
  2420 		difflines /dev/null $nfile > $WDIR/$DIR/$F.count
  2421 	    elif [[ -f $ofile ]]; then
  2422 		# old file: count deleted lines
  2423 		difflines $ofile /dev/null > $WDIR/$DIR/$F.count
  2424 	    fi
  2425 	fi
  2427 	# Now we generate the postscript for this file.  We generate diffs
  2428 	# only in the event that there is delta, or the file is new (it seems
  2429 	# tree-killing to print out the contents of deleted files).
  2431 	if [[ -f $nfile ]]; then
  2432 		ocr=$ofile
  2433 		[[ ! -f $ofile ]] && ocr=/dev/null
  2435 		if [[ -z $mv_but_nodiff ]]; then
  2436 			textcomm=`getcomments text $P $PP`
  2437 			if [[ -x $CODEREVIEW ]]; then
  2438 				$CODEREVIEW -y "$textcomm" \
  2439 				    -e $ocr $nfile \
  2440 				    > /tmp/$$.psfile 2>/dev/null &&
  2441 				    cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
  2442 				if [[ $? -eq 0 ]]; then
  2443 					print " ps\c"
  2444 				else
  2445 					print " ps[fail]\c"
  2446 				fi
  2447 			fi
  2448 		fi
  2449 	fi
  2451 	if [[ -f $ofile && -z $mv_but_nodiff ]]; then
  2452 	    if [[ -n $its_a_jar ]]; then
  2453 		source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
  2454 	    else
  2455 		source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
  2456 	    fi
  2457 		print " old\c"
  2458 	fi
  2460 	if [[ -f $nfile ]]; then
  2461 	    if [[ -n $its_a_jar ]]; then
  2462 		source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
  2463 	    else
  2464 		source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
  2465 	    fi
  2466 		print " new\c"
  2467 	fi
  2469 	print
  2470 done < $FLIST
  2472 # Create the new style mercurial patch here using hg export -r [all-revs] -g -o $CHANGESETPATH
  2473 if [[ $SCM_MODE == "mercurial" ]]; then
  2474   if [[ "$HG_LIST_FROM_COMMIT" -eq 1 && $flist_mode == "auto" ]]; then
  2475     EXPORTCHANGESET="$WNAME.changeset"
  2476     CHANGESETPATH=${WDIR}/${EXPORTCHANGESET}
  2477     rm -f $CHANGESETPATH
  2478     touch $CHANGESETPATH
  2479     if [[ -n $ALL_CREV ]]; then
  2480       rev_opt=
  2481       for rev in $ALL_CREV; do
  2482         rev_opt="$rev_opt --rev $rev"
  2483       done
  2484     elif [[ -n $FIRST_CREV ]]; then
  2485       rev_opt="--rev $FIRST_CREV"
  2486     fi
  2488     if [[ -n $rev_opt ]]; then
  2489       (cd $CWS;hg export -g $rev_opt -o $CHANGESETPATH)
  2490       echo "Created changeset: $CHANGESETPATH" 1>&2
  2491       # Use it in place of the jdk.patch created above
  2492       rm -f $WDIR/$WNAME.patch
  2493     fi
  2494   set +x
  2495   fi
  2496 fi
  2498 frame_nav_js > $WDIR/ancnav.js
  2499 frame_navigation > $WDIR/ancnav.html
  2501 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
  2502 	print " Generating PDF: \c"
  2503 	fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
  2504 	print "Done."
  2505 fi
  2507 # Now build the index.html file that contains
  2508 # links to the source files and their diffs.
  2510 cd $CWS
  2512 # Save total changed lines for Code Inspection.
  2513 print "$TOTL" > $WDIR/TotalChangedLines
  2515 print "     index.html: \c"
  2516 INDEXFILE=$WDIR/index.html
  2517 exec 3<&1			# duplicate stdout to FD3.
  2518 exec 1<&-			# Close stdout.
  2519 exec > $INDEXFILE		# Open stdout to index file.
  2521 print "$HTML<head>"
  2522 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
  2523 print "$STDHEAD"
  2524 print "<title>$WNAME</title>"
  2525 print "</head>"
  2526 print "<body id=\"SUNWwebrev\">"
  2527 print "<div class=\"summary\">"
  2528 print "<h2>Code Review for $WNAME</h2>"
  2530 print "<table>"
  2532 if [[ -z $uflag ]]; then
  2533     if [[ $SCM_MODE == "mercurial" ]]; then
  2535         # Let's try to extract the user name from the .hgrc file
  2537 	username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
  2538     fi
  2540     if [[ -z $username ]]; then
  2542         # Figure out the username and gcos name.  To maintain compatibility
  2543         # with passwd(4), we must support '&' substitutions.
  2545 	username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
  2546 	if [[ -x $GETENT ]]; then
  2547 	    realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
  2548 	fi
  2549 	userupper=`print "$username" | sed 's/\<./\u&/g'`
  2550 	realname=`print $realname | sed s/\&/$userupper/`
  2551     fi
  2552 fi
  2554 date="on `date`"
  2556 if [[ -n "$username" && -n "$realname" ]]; then
  2557 	print "<tr><th>Prepared by:</th>"
  2558 	print "<td>$realname ($username) $date</td></tr>"
  2559 elif [[ -n "$username" ]]; then
  2560 	print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
  2561 fi
  2563 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
  2564 if [[ -n $parent_webrev ]]; then
  2565         print "<tr><th>Compare against:</th><td>"
  2566 	print "webrev at $parent_webrev"
  2567 else
  2568     if [[ -n $OUTPWS2 ]]; then
  2569         print "<tr><th>Compare against:</th><td>"
  2570 	print "$OUTPWS2"
  2571     fi
  2572 fi
  2573 print "</td></tr>"
  2574 if [[ -n $rflag ]]; then
  2575     print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
  2576 elif [[ -n $OUTREV ]]; then
  2577     if [[ -z $forestflag ]]; then
  2578         print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
  2579     fi
  2580 fi
  2581 if [[ -n $HG_BRANCH ]]; then
  2582     print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
  2583 fi
  2585 print "<tr><th>Summary of changes:</th><td>"
  2586 printCI $TOTL $TINS $TDEL $TMOD $TUNC
  2587 print "</td></tr>"
  2589 if [[ -f $WDIR/$WNAME.patch ]]; then
  2590   print "<tr><th>Patch of changes:</th><td>"
  2591   print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
  2592 elif [[ -f $CHANGESETPATH ]]; then
  2593   print "<tr><th>Changeset:</th><td>"
  2594   print "<a href=\"$EXPORTCHANGESET\">$EXPORTCHANGESET</a></td></tr>"
  2595 fi
  2597 if [[ -f $WDIR/$WNAME.pdf ]]; then
  2598 	print "<tr><th>Printable review:</th><td>"
  2599 	print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
  2600 fi
  2602 if [[ -n "$iflag" ]]; then
  2603 	print "<tr><th>Author comments:</th><td><div>"
  2604 	cat /tmp/$$.include
  2605 	print "</div></td></tr>"
  2606 fi
  2607 # Add links to referenced CRs, if any
  2608 # URL has a <title> like:
  2609 # <title>[#JDK-8024688] b106-lambda: j.u.Map.merge doesn&#39;t work as specified if contains key:null pair - Java Bug System</title>
  2610 # we format this to:
  2611 # JDK-8024688: b106-lambda: j.u.Map.merge doesn't work as specified if contains key:null pair
  2612 if [[ -n $CRID ]]; then
  2613     for id in $CRID
  2614     do
  2615         #add "JDK-" to raw bug id for openjdk.java.net links.
  2616         id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'`
  2618         print "<tr><th>Bug id:</th><td>"
  2619         url="${BUGURL}${id}"
  2621         if [[ -n $WGET ]]; then
  2622             msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '<title>' | sed 's/<title>\[#\(.*\)\] \(.*\) - Java Bug System<\/title>/\1 : \2/' | html_dequote | html_quote`
  2623         fi
  2624         if [[ -z $msg ]]; then
  2625             msg="${id}"
  2626         fi
  2628         print "<a href=\"$url\">$msg</a>"
  2630         print "</td></tr>"
  2631     done
  2632 fi
  2633 print "<tr><th>Legend:</th><td>"
  2634 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>"
  2635 print "</table>"
  2636 print "</div>"
  2639 # Second pass through the files: generate the rest of the index file
  2641 while read LINE
  2642 do
  2643 	set - $LINE
  2644         if [[ $1 == "Revision:" ]]; then
  2645             FIRST_CREV=`expr $3 + 1`
  2646             continue
  2647         fi
  2648 	P=$1
  2650 	if [[ $# == 2 ]]; then
  2651 		PP=$2
  2652 		oldname=" <i>(was $PP)</i>"
  2654 	else
  2655 		PP=$P
  2656 		oldname=""
  2657 	fi
  2659 	DIR=${P%/*}
  2660 	if [[ $DIR == $P ]]; then
  2661 		DIR="."   # File at root of workspace
  2662 	fi
  2664 	# Avoid processing the same file twice.
  2665 	# It's possible for renamed files to
  2666 	# appear twice in the file list
  2668 	F=$WDIR/$P
  2670 	print "<p><code>"
  2672 	# If there's a diffs file, make diffs links
  2674         NODIFFS=
  2675 	if [[ -f $F.cdiff.html ]]; then
  2676 		print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
  2677 		print "<a href=\"$P.udiff.html\">Udiffs</a>"
  2679 		if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
  2680 			print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
  2681 		fi
  2683 		print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
  2685 		print "<a href=\"$P.frames.html\">Frames</a>"
  2686 	else
  2687                 NODIFFS=1
  2688 		print " ------ ------ ------"
  2690 		if [[ -x $WDIFF ]]; then
  2691 			print " ------"
  2692 		fi
  2694 		print " ------"
  2695 	fi
  2697 	# If there's an old file, make the link
  2699         NOOLD=
  2700 	if [[ -f $F-.html ]]; then
  2701 		print "<a href=\"$P-.html\">Old</a>"
  2702 	else
  2703                 NOOLD=1
  2704 		print " ---"
  2705 	fi
  2707 	# If there's an new file, make the link
  2709         NONEW=
  2710 	if [[ -f $F.html ]]; then
  2711 		print "<a href=\"$P.html\">New</a>"
  2712 	else
  2713                 NONEW=1
  2714 		print " ---"
  2715 	fi
  2717 	if [[ -f $F.patch ]]; then
  2718 		print "<a href=\"$P.patch\">Patch</a>"
  2719 	else
  2720 		print " -----"
  2721 	fi
  2723 	if [[ -f $WDIR/raw_files/new/$P ]]; then
  2724 		print "<a href=\"raw_files/new/$P\">Raw</a>"
  2725 	else
  2726 		print " ---"
  2727 	fi
  2728         print "</code>"
  2729         if [[ -n $NODIFFS && -z $oldname ]]; then
  2730             if [[ -n $NOOLD ]]; then
  2731                 print "<font color=green><b>$P</b></font>"
  2732             elif [[ -n $NONEW ]]; then
  2733                 print "<font color=red><b>$P</b></font>"
  2734             fi
  2735         else
  2736 	    print "<b>$P</b> $oldname"
  2737         fi
  2739 	print "</p><blockquote>\c"
  2740 	# Insert delta comments if any
  2741 	comments=`getcomments html $P $PP`
  2742 	if [ -n "$comments" ]; then
  2743 	    print "<pre>$comments</pre>"
  2744 	fi
  2746 	# Add additional comments comment
  2748 	print "<!-- Add comments to explain changes in $P here -->"
  2750 	# Add count of changes.
  2752 	if [[ -f $F.count ]]; then
  2753 	    cat $F.count
  2754 	    rm $F.count
  2755 	fi
  2756         print "</blockquote>"
  2757 done < $FLIST
  2759 print
  2760 print
  2761 print "<hr />"
  2762 print "<p style=\"font-size: small\">"
  2763 print "This code review page was prepared using <b>$0</b>"
  2764 print "(vers $WEBREV_UPDATED)."
  2765 print "</body>"
  2766 print "</html>"
  2768 if [[ -n $ZIP ]]; then
  2769     # Let's generate a zip file for convenience
  2770     cd $WDIR/..
  2771     if [ -f webrev.zip ]; then
  2772 	rm webrev.zip
  2773     fi
  2774     $ZIP -r webrev webrev >/dev/null 2>&1
  2775 fi
  2777 exec 1<&-			# Close FD 1.
  2778 exec 1<&3			# dup FD 3 to restore stdout.
  2779 exec 3<&-			# close FD 3.
  2781 print "Done."
  2782 print "Output to: $WDIR"

mercurial