make/scripts/webrev.ksh

Thu, 31 Aug 2017 15:40:18 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:40:18 +0800
changeset 1133
50aaf272884f
parent 907
c1029b02ca87
parent 0
75a576e87639
permissions
-rw-r--r--

merge

     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.1-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 	    hg_root=`hg root`
  1690 	    cd $CWS
  1691 	    if [ -n "$rflag" ]; then
  1692 		parentrev=$PARENT_REV
  1693 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1694                 parentrev=$OUTREV
  1695 	    fi
  1696 	    if [ -z "$parentrev" ]; then
  1697 		parentrev=`hg log -l1 $PDIR/$PF | $AWK -F: '/changeset/ {print $2}'`
  1698 	    fi
  1699 	    if [ -n "$parentrev" ]; then
  1700 		mkdir -p $olddir/$PDIR
  1701 		if [ -z "$parent" ]; then
  1702 		    hg cat -R $hg_root --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
  1703 		else
  1704 		    $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
  1705 		fi
  1706 	    fi
  1707 	    cd $current_dir
  1708 	fi
  1711 function build_old_new
  1713 	if [[ $SCM_MODE == "mercurial" ]]; then
  1714 		build_old_new_mercurial $@
  1715 	fi
  1720 # Usage message.
  1722 function usage
  1724 	print "Usage:\twebrev [options]
  1725 	webrev [options] ( <file> | - )
  1727 Options:
  1728 	-v: Print the version of this tool.
  1729         -b: Do not ignore changes in the amount of white space.
  1730         -c <CR#>: Include link to CR (aka bugid) in the main page.
  1731 	-i <filename>: Include <filename> in the index.html file.
  1732 	-o <outdir>: Output webrev to specified directory.
  1733 	-p <compare-against>: Use specified parent wkspc or basis for comparison
  1734         -u <username>: Use that username instead of 'guessing' one.
  1735 	-m: Forces the use of Mercurial
  1737 Mercurial only options:
  1738 	-r rev: Compare against a specified revision
  1739 	-N: Skip 'hg outgoing', use only 'hg status'
  1740 	-f: Use the forest extension
  1742 Arguments:
  1743 	<file>: Optional file containing list of files to include in webrev
  1744         -: read list of files to include in webrev from standard input
  1746 Environment:
  1747 	WDIR: Control the output directory.
  1748 	WEBREV_BUGURL: Control the URL prefix for bugids.
  1752 	exit 2
  1757 # Main program starts here
  1760 LANG="C"
  1761 LC_ALL="C"
  1762 export LANG LC_ALL
  1763 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
  1765 set +o noclobber
  1767 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
  1768 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
  1769 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
  1770 [[ -z $PERL ]] && PERL=`look_for_prog perl`
  1771 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
  1772 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
  1773 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
  1774 [[ -z $AWK ]] && AWK=`look_for_prog awk`
  1775 [[ -z $JAR ]] && JAR=`look_for_prog jar`
  1776 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
  1777 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
  1778 [[ -z $WGET ]] && WGET=`look_for_prog wget`
  1780 if uname | grep CYGWIN >/dev/null
  1781 then
  1782         ISWIN=1
  1783         # Under windows mercurial outputs '\' instead of '/'
  1784         FILTER="tr '\\\\' '/'"
  1785 else
  1786         FILTER="cat"
  1787 fi
  1789 if [[ ! -x $PERL ]]; then
  1790 	print -u2 "Error: No perl interpreter found.  Exiting."
  1791 	exit 1
  1792 fi
  1795 # These aren't fatal, but we want to note them to the user.
  1797 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
  1798 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
  1799 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
  1801 # Declare global total counters.
  1802 integer TOTL TINS TDEL TMOD TUNC
  1804 flist_mode=
  1805 flist_file=
  1806 bflag=
  1807 iflag=
  1808 oflag=
  1809 pflag=
  1810 uflag=
  1811 Oflag=
  1812 rflag=
  1813 Nflag=
  1814 forestflag=
  1815 while getopts "c:i:o:p:r:u:mONvfb" opt
  1816 do
  1817 	case $opt in
  1818         b)      bflag=1;;
  1820 	i)	iflag=1
  1821 		INCLUDE_FILE=$OPTARG;;
  1823 	o)	oflag=1
  1824 		WDIR=$OPTARG;;
  1826 	p)	pflag=1
  1827 		codemgr_parent=$OPTARG;;
  1829 	u)      uflag=1
  1830 		username=$OPTARG;;
  1832         c)      if [[ -z $CRID ]]; then
  1833                    CRID=$OPTARG
  1834                 else
  1835                    CRID="$CRID $OPTARG"
  1836                 fi;;
  1838 	m)	SCM_MODE="mercurial";;
  1840 	O)	Oflag=1;; # ignored (bugs are now all visible at bugs.openjdk.java.net)
  1842 	N)	Nflag=1;;
  1844 	f)	forestflag=1;;
  1846 	r)	rflag=1
  1847 		PARENT_REV=$OPTARG;;
  1849 	v)	print "$0 version: $WEBREV_UPDATED";;
  1852 	?)	usage;;
  1853 	esac
  1854 done
  1856 FLIST=/tmp/$$.flist
  1857 HG_LIST_FROM_COMMIT=
  1859 if [[ -n $forestflag && -n $rflag ]]; then
  1860     print "The -r <rev> flag is incompatible with the use of forests"
  1861     exit 2
  1862 fi
  1865 # If this manually set as the parent, and it appears to be an earlier webrev,
  1866 # then note that fact and set the parent to the raw_files/new subdirectory.
  1868 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
  1869 	parent_webrev="$codemgr_parent"
  1870 	codemgr_parent="$codemgr_parent/raw_files/new"
  1871 fi
  1873 shift $(($OPTIND - 1))
  1875 if [[ $1 == "-" ]]; then
  1876 	cat > $FLIST
  1877 	flist_mode="stdin"
  1878 	flist_done=1
  1879 	shift
  1880 elif [[ -n $1 ]]; then
  1881 	if [[ ! -r $1 ]]; then
  1882 		print -u2 "$1: no such file or not readable"
  1883 		usage
  1884 	fi
  1885 	cat $1 > $FLIST
  1886 	flist_mode="file"
  1887 	flist_file=$1
  1888 	flist_done=1
  1889 	shift
  1890 else
  1891 	flist_mode="auto"
  1892 fi
  1895 # Before we go on to further consider -l and -w, work out which SCM we think
  1896 # is in use.
  1898 if [[ -z $SCM_MODE ]]; then
  1899     SCM_MODE=`detect_scm $FLIST`
  1900 fi
  1901 if [[ $SCM_MODE == "unknown" ]]; then
  1902        print -u2 "Unable to determine SCM type currently in use."
  1903        print -u2 "For mercurial: webrev runs 'hg root'."
  1904        exit 1
  1905 fi
  1907 print -u2 "   SCM detected: $SCM_MODE"
  1910 if [[ $SCM_MODE == "mercurial" ]]; then
  1912     # determine Workspace and parent workspace paths
  1914     CWS=`hg root | $FILTER`
  1915     if [[ -n $pflag && -z "$PWS" ]]; then
  1916 	OUTPWS=$codemgr_parent
  1917         # Let's try to expand it if it's an alias defined in [paths]
  1918         tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  1919         if [[ -n $tmp ]]; then
  1920             OUTPWS="$tmp"
  1921         fi
  1922         if [[ -n $rflag ]]; then
  1923 	    if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
  1924 	        PWS=$codemgr_parent
  1925 	    else
  1926 	        PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
  1927 	    fi
  1928         fi
  1929     fi
  1931     # OUTPWS is the parent repository to use when using 'hg outgoing'
  1933     if [[ -z $Nflag ]]; then
  1934         if [[ -n $forestflag ]]; then
  1936             # for forest we have to rely on properly set default and
  1937             # default-push because they can be different from the top one.
  1938             # unless of course it was explicitly specified with -p
  1939             if [[ -z $pflag ]]; then
  1940                 OUTPWS=
  1941             fi
  1942         else
  1944             # Unfortunately mercurial is bugged and doesn't handle
  1945             # aliases correctly in 'hg path default'
  1946             # So let's do it ourselves. Sigh...
  1947             if [[ -z "$OUTPWS" ]]; then
  1948                 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  1949             fi
  1950             # Still empty, means no default-push
  1951             if [[ -z "$OUTPWS" ]]; then
  1952                 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  1953             fi
  1954             # Let's try to expand it if it's an alias defined in [paths]
  1955             tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  1956             if [[ -n $tmp ]]; then
  1957                 OUTPWS="$tmp"
  1958             fi
  1959         fi
  1960     fi
  1962     # OUTPWS may contain username:password, let's make sure we remove the
  1963     # sensitive information before we print out anything in the HTML
  1965     OUTPWS2=$OUTPWS
  1966     if [[ -n $OUTPWS ]]; then
  1967 	if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
  1968 	    # Remove everything between '://' and '@'
  1969 	    OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
  1970 	fi
  1971     fi
  1973     if [[ -z $HG_BRANCH ]]; then
  1974         HG_BRANCH=`hg branch`
  1975         if [ "$HG_BRANCH" == "default" ]; then
  1977             # 'default' means no particular branch, so let's cancel that
  1979             HG_BRANCH=
  1980         fi
  1981     fi
  1983     if [[ -z $forestflag ]]; then
  1984         if [[ -z $Nflag ]]; then
  1986             # If no "-N", always do "hg outgoing" against parent
  1987             # repository to determine list of outgoing revisions.
  1989             ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
  1990             if [[ -n $ALL_CREV ]]; then
  1991                 FIRST_CREV=`echo "$ALL_CREV" | head -1`
  1993                 # If no "-r", choose revision to compare against by
  1994                 # finding the latest revision not in the outgoing list.
  1996                 if [[ -z $rflag ]]; then
  1997                     OUTREV=`find_outrev "$FIRST_CREV"`
  1998                     if [[ -n $OUTREV ]]; then
  1999                         HG_LIST_FROM_COMMIT=1
  2000                     fi
  2001                 fi
  2002             fi
  2003         elif [[ -n $rflag ]]; then
  2005             # If skipping "hg outgoing" but still comparing against a
  2006             # specific revision (not the tip), set revision for comment
  2007             # accumulation.
  2009             FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
  2010             FIRST_CREV=`expr $FIRST_CREV + 1`
  2011         fi
  2012     fi
  2013     #Let's check if a merge is needed, if so, issue a warning
  2014     PREV=`hg parent | grep '^tag:.*tip$'`
  2015     if [[ -z $PREV ]]; then
  2016         print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
  2017     fi
  2018 fi
  2020 if [[ $flist_mode == "stdin" ]]; then
  2021 	print -u2 " File list from: standard input"
  2022 elif [[ $flist_mode == "file" ]]; then
  2023 	print -u2 " File list from: $flist_file"
  2024 fi
  2026 if [[ $# -gt 0 ]]; then
  2027 	print -u2 "WARNING: unused arguments: $*"
  2028 fi
  2030 if [[ $SCM_MODE == "mercurial" ]]; then
  2031     if [[ -z $flist_done ]]; then
  2032 	flist_from_mercurial $PWS
  2033     fi
  2034 fi
  2037 # If the user didn't specify a -i option, check to see if there is a
  2038 # webrev-info file in the workspace directory.
  2040 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
  2041 	iflag=1
  2042 	INCLUDE_FILE="$CWS/webrev-info"
  2043 fi
  2045 if [[ -n $iflag ]]; then
  2046 	if [[ ! -r $INCLUDE_FILE ]]; then
  2047 		print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
  2048 		    "not readable."
  2049 		exit 1
  2050 	else
  2052 		# $INCLUDE_FILE may be a relative path, and the script alters
  2053 		# PWD, so we just stash a copy in /tmp.
  2055 		cp $INCLUDE_FILE /tmp/$$.include
  2056 	fi
  2057 fi
  2060 # Output directory.
  2062 if [[ -z $WDIR ]]; then
  2063     WDIR=$CWS/webrev
  2064 else
  2065     # If the output directory doesn't end with '/webrev' or '/webrev/'
  2066     # then add '/webrev'. This is for backward compatibility
  2067     if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
  2068     then
  2069 	WDIR=$WDIR/webrev
  2070     fi
  2071 fi
  2072 # WDIR=${WDIR:-$CWS/webrev}
  2075 # Name of the webrev, derived from the workspace name; in the
  2076 # future this could potentially be an option.
  2078 # Let's keep what's after the last '/'
  2079 WNAME=${CWS##*/}
  2082 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
  2084 if [ ${WDIR%%/*} ]; then
  2085     if [[ -n $ISWIN ]]; then
  2086         if [ ${WDIR%%[A-Za-z]:*} ]; then
  2087 	    WDIR=$PWD/$WDIR
  2088         fi
  2089     else
  2090 	WDIR=$PWD/$WDIR
  2091     fi
  2092 fi
  2094 if [[ ! -d $WDIR ]]; then
  2095 	mkdir -p $WDIR
  2096 	[[ $? != 0 ]] && exit 1
  2097 fi
  2100 # Summarize what we're going to do.
  2102 print "      Workspace: $CWS"
  2103 if [[ -n $parent_webrev ]]; then
  2104     print "Compare against: webrev at $parent_webrev"
  2105 elif [[ -n $OUTPWS2 ]]; then
  2106     print "Compare against: $OUTPWS2"
  2107 fi
  2108 if [[ -n $HG_BRANCH ]]; then
  2109     print "         Branch: $HG_BRANCH"
  2110 fi
  2111 if [[ -n $rflag ]]; then
  2112         print "Compare against version: $PARENT_REV"
  2113 fi
  2114 [[ -n $INCLUDE_FILE ]] && print "      Including: $INCLUDE_FILE"
  2115 print "      Output to: $WDIR"
  2118 # Save the file list in the webrev dir
  2120 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
  2123 #    Bug IDs will be replaced by a URL.  Order of precedence
  2124 #    is: default location, $WEBREV_BUGURL
  2126 BUGURL='https://bugs.openjdk.java.net/browse/'
  2127 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
  2128 IDPREFIX='JDK-'
  2131 rm -f $WDIR/$WNAME.patch
  2132 rm -f $WDIR/$WNAME.changeset
  2133 rm -f $WDIR/$WNAME.ps
  2134 rm -f $WDIR/$WNAME.pdf
  2136 touch $WDIR/$WNAME.patch
  2138 print "   Output Files:"
  2141 # Clean up the file list: Remove comments, blank lines and env variables.
  2143 sed -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
  2144 FLIST=/tmp/$$.flist.clean
  2147 # Clean up residual raw files
  2149 if [ -d $WDIR/raw_files ]; then
  2150     rm -rf $WDIR/raw_files 2>/dev/null
  2151 fi
  2154 # Should we ignore changes in white spaces when generating diffs?
  2156 if [[ -n $bflag ]]; then
  2157     DIFFOPTS="-t"
  2158 else
  2159     DIFFOPTS="-bt"
  2160 fi
  2162 # First pass through the files: generate the per-file webrev HTML-files.
  2164 while read LINE
  2165 do
  2166 	set - $LINE
  2167 	P=$1
  2169         if [[ $1 == "Revision:" ]]; then
  2170             OUTREV=$2
  2171             continue
  2172         fi
  2174 	# Normally, each line in the file list is just a pathname of a
  2175 	# file that has been modified or created in the child.  A file
  2176 	# that is renamed in the child workspace has two names on the
  2177 	# line: new name followed by the old name.
  2179 	oldname=""
  2180 	oldpath=""
  2181 	rename=
  2182 	if [[ $# -eq 2 ]]; then
  2183 		PP=$2			# old filename
  2184 		oldname=" (was $PP)"
  2185 		oldpath="$PP"
  2186 		rename=1
  2187         	PDIR=${PP%/*}
  2188         	if [[ $PDIR == $PP ]]; then
  2189 			PDIR="."   # File at root of workspace
  2190 		fi
  2192 		PF=${PP##*/}
  2194 	        DIR=${P%/*}
  2195 	        if [[ $DIR == $P ]]; then
  2196 			DIR="."   # File at root of workspace
  2197 		fi
  2199 		F=${P##*/}
  2200         else
  2201 	        DIR=${P%/*}
  2202 	        if [[ "$DIR" == "$P" ]]; then
  2203 			DIR="."   # File at root of workspace
  2204 		fi
  2206 		F=${P##*/}
  2208 		PP=$P
  2209 		PDIR=$DIR
  2210 		PF=$F
  2211 	fi
  2213         # Make the webrev directory if necessary as it may have been
  2214         # removed because it was empty
  2215         if [ ! -d $CWS/$DIR ]; then
  2216 	    mkdir -p $CWS/$DIR
  2217         fi
  2219 	COMM=`getcomments html $P $PP`
  2221 	print "\t$P$oldname\n\t\t\c"
  2223 	# Make the webrev mirror directory if necessary
  2224 	mkdir -p $WDIR/$DIR
  2226 	# cd to the directory so the names are short
  2227 	cd $CWS/$DIR
  2230 	# We stash old and new files into parallel directories in /tmp
  2231 	# and do our diffs there.  This makes it possible to generate
  2232 	# clean looking diffs which don't have absolute paths present.
  2234 	olddir=$WDIR/raw_files/old
  2235 	newdir=$WDIR/raw_files/new
  2236 	mkdir -p $olddir
  2237 	mkdir -p $newdir
  2238 	mkdir -p $olddir/$PDIR
  2239 	mkdir -p $newdir/$DIR
  2241 	build_old_new $olddir $newdir $DIR $F
  2243 	if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
  2244 		print "*** Error: file not in parent or child"
  2245 		continue
  2246 	fi
  2248 	cd $WDIR/raw_files
  2249 	ofile=old/$PDIR/$PF
  2250 	nfile=new/$DIR/$F
  2252 	mv_but_nodiff=
  2253 	cmp $ofile $nfile > /dev/null 2>&1
  2254 	if [[ $? == 0 && $rename == 1 ]]; then
  2255 		mv_but_nodiff=1
  2256 	fi
  2259         # Cleaning up
  2261         rm -f $WDIR/$DIR/$F.cdiff.html
  2262         rm -f $WDIR/$DIR/$F.udiff.html
  2263         rm -f $WDIR/$DIR/$F.wdiff.html
  2264         rm -f $WDIR/$DIR/$F.sdiff.html
  2265         rm -f $WDIR/$DIR/$F-.html
  2266         rm -f $WDIR/$DIR/$F.html
  2268 	its_a_jar=
  2269 	if expr $F : '.*\.jar' \| $F : '.*\.zip' >/dev/null; then
  2270 	    its_a_jar=1
  2271 	    # It's a JAR or ZIP file, let's do it differently
  2272 	    if [[ -z $JAR ]]; then
  2273 		print "No access to jar, so can't produce diffs for jar or zip files"
  2274 	    else
  2275 		if [ -f $ofile ]; then
  2276 		    $JAR -tvf $ofile >"$ofile".lst
  2277 		fi
  2278 		if [ -f $nfile ]; then
  2279 		    $JAR -tvf $nfile >"$nfile".lst
  2280 		fi
  2282 		if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2284 		    ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
  2285 		    diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2286 			> $WDIR/$DIR/$F.cdiff.html
  2287 		    print " cdiffs\c"
  2289 		    ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
  2290 		    diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2291 			> $WDIR/$DIR/$F.udiff.html
  2293 		    print " udiffs\c"
  2295 		    if [[ -x $WDIFF ]]; then
  2296 			$WDIFF -c "$COMM" \
  2297 			    -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
  2298 			    $WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2299 			if [[ $? -eq 0 ]]; then
  2300 			    print " wdiffs\c"
  2301 			else
  2302 			    print " wdiffs[fail]\c"
  2303 			fi
  2304 		    fi
  2306 		    sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2307 			> $WDIR/$DIR/$F.sdiff.html
  2308 		    print " sdiffs\c"
  2310 		    print " frames\c"
  2312 		    rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2314 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2316 		elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2317 		# renamed file: may also have differences
  2318 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2319 		elif [[ -f $nfile ]]; then
  2320 		# new file: count added lines
  2321 		    difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
  2322 		elif [[ -f $ofile ]]; then
  2323 		# old file: count deleted lines
  2324 		    difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
  2325 		fi
  2326 	    fi
  2327 	else
  2330 	    # If we have old and new versions of the file then run the
  2331 	    # appropriate diffs.  This is complicated by a couple of factors:
  2333 	    #	- renames must be handled specially: we emit a 'remove'
  2334 	    #	  diff and an 'add' diff
  2335 	    #	- new files and deleted files must be handled specially
  2336 	    #	- Solaris patch(1m) can't cope with file creation
  2337 	    #	  (and hence renames) as of this writing.
  2338 	    #   - To make matters worse, gnu patch doesn't interpret the
  2339 	    #	  output of Solaris diff properly when it comes to
  2340 	    #	  adds and deletes.  We need to do some "cleansing"
  2341 	    #     transformations:
  2342 	    # 	    [to add a file] @@ -1,0 +X,Y @@  -->  @@ -0,0 +X,Y @@
  2343 	    #	    [to del a file] @@ -X,Y +1,0 @@  -->  @@ -X,Y +0,0 @@
  2345 	    cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
  2346 	    cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
  2348             if [[ ! "$HG_LIST_FROM_COMMIT" -eq 1 || ! $flist_mode == "auto" ]];
  2349             then
  2350               # Only need to generate a patch file here if there are no commits in outgoing
  2351               # or if we've specified a file list
  2352               rm -f $WDIR/$DIR/$F.patch
  2353               if [[ -z $rename ]]; then
  2354                   if [ ! -f $ofile ]; then
  2355                       diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2356                           > $WDIR/$DIR/$F.patch
  2357                   elif [ ! -f $nfile ]; then
  2358                       diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2359                           > $WDIR/$DIR/$F.patch
  2360                   else
  2361                       diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
  2362                   fi
  2363               else
  2364                   diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2365                       > $WDIR/$DIR/$F.patch
  2367                   diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2368                       >> $WDIR/$DIR/$F.patch
  2370               fi
  2374             # Tack the patch we just made onto the accumulated patch for the
  2375             # whole wad.
  2377               cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
  2378             fi
  2380             print " patch\c"
  2382 	    if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2384 		${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
  2385 		diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2386 		    > $WDIR/$DIR/$F.cdiff.html
  2387 		print " cdiffs\c"
  2389 		${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
  2390 		diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2391 		    > $WDIR/$DIR/$F.udiff.html
  2393 		print " udiffs\c"
  2395 		if [[ -x $WDIFF ]]; then
  2396 		    $WDIFF -c "$COMM" \
  2397 			-t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
  2398 			$WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2399 		    if [[ $? -eq 0 ]]; then
  2400 			print " wdiffs\c"
  2401 		    else
  2402 			print " wdiffs[fail]\c"
  2403 		    fi
  2404 		fi
  2406 		sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2407 		    > $WDIR/$DIR/$F.sdiff.html
  2408 		print " sdiffs\c"
  2410 		print " frames\c"
  2412 		rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2414 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2416 	    elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2417 		# renamed file: may also have differences
  2418 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2419 	    elif [[ -f $nfile ]]; then
  2420 		# new file: count added lines
  2421 		difflines /dev/null $nfile > $WDIR/$DIR/$F.count
  2422 	    elif [[ -f $ofile ]]; then
  2423 		# old file: count deleted lines
  2424 		difflines $ofile /dev/null > $WDIR/$DIR/$F.count
  2425 	    fi
  2426 	fi
  2428 	# Now we generate the postscript for this file.  We generate diffs
  2429 	# only in the event that there is delta, or the file is new (it seems
  2430 	# tree-killing to print out the contents of deleted files).
  2432 	if [[ -f $nfile ]]; then
  2433 		ocr=$ofile
  2434 		[[ ! -f $ofile ]] && ocr=/dev/null
  2436 		if [[ -z $mv_but_nodiff ]]; then
  2437 			textcomm=`getcomments text $P $PP`
  2438 			if [[ -x $CODEREVIEW ]]; then
  2439 				$CODEREVIEW -y "$textcomm" \
  2440 				    -e $ocr $nfile \
  2441 				    > /tmp/$$.psfile 2>/dev/null &&
  2442 				    cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
  2443 				if [[ $? -eq 0 ]]; then
  2444 					print " ps\c"
  2445 				else
  2446 					print " ps[fail]\c"
  2447 				fi
  2448 			fi
  2449 		fi
  2450 	fi
  2452 	if [[ -f $ofile && -z $mv_but_nodiff ]]; then
  2453 	    if [[ -n $its_a_jar ]]; then
  2454 		source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
  2455 	    else
  2456 		source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
  2457 	    fi
  2458 		print " old\c"
  2459 	fi
  2461 	if [[ -f $nfile ]]; then
  2462 	    if [[ -n $its_a_jar ]]; then
  2463 		source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
  2464 	    else
  2465 		source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
  2466 	    fi
  2467 		print " new\c"
  2468 	fi
  2470 	print
  2471 done < $FLIST
  2473 # Create the new style mercurial patch here using hg export -r [all-revs] -g -o $CHANGESETPATH
  2474 if [[ $SCM_MODE == "mercurial" ]]; then
  2475   if [[ "$HG_LIST_FROM_COMMIT" -eq 1 && $flist_mode == "auto" ]]; then
  2476     EXPORTCHANGESET="$WNAME.changeset"
  2477     CHANGESETPATH=${WDIR}/${EXPORTCHANGESET}
  2478     rm -f $CHANGESETPATH
  2479     touch $CHANGESETPATH
  2480     if [[ -n $ALL_CREV ]]; then
  2481       rev_opt=
  2482       for rev in $ALL_CREV; do
  2483         rev_opt="$rev_opt --rev $rev"
  2484       done
  2485     elif [[ -n $FIRST_CREV ]]; then
  2486       rev_opt="--rev $FIRST_CREV"
  2487     fi
  2489     if [[ -n $rev_opt ]]; then
  2490       (cd $CWS;hg export -g $rev_opt -o $CHANGESETPATH)
  2491       echo "Created changeset: $CHANGESETPATH" 1>&2
  2492       # Use it in place of the jdk.patch created above
  2493       rm -f $WDIR/$WNAME.patch
  2494     fi
  2495   set +x
  2496   fi
  2497 fi
  2499 frame_nav_js > $WDIR/ancnav.js
  2500 frame_navigation > $WDIR/ancnav.html
  2502 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
  2503 	print " Generating PDF: \c"
  2504 	fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
  2505 	print "Done."
  2506 fi
  2508 # Now build the index.html file that contains
  2509 # links to the source files and their diffs.
  2511 cd $CWS
  2513 # Save total changed lines for Code Inspection.
  2514 print "$TOTL" > $WDIR/TotalChangedLines
  2516 print "     index.html: \c"
  2517 INDEXFILE=$WDIR/index.html
  2518 exec 3<&1			# duplicate stdout to FD3.
  2519 exec 1<&-			# Close stdout.
  2520 exec > $INDEXFILE		# Open stdout to index file.
  2522 print "$HTML<head>"
  2523 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
  2524 print "$STDHEAD"
  2525 print "<title>$WNAME</title>"
  2526 print "</head>"
  2527 print "<body id=\"SUNWwebrev\">"
  2528 print "<div class=\"summary\">"
  2529 print "<h2>Code Review for $WNAME</h2>"
  2531 print "<table>"
  2533 if [[ -z $uflag ]]; then
  2534     if [[ $SCM_MODE == "mercurial" ]]; then
  2536         # Let's try to extract the user name from the .hgrc file
  2538 	username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
  2539     fi
  2541     if [[ -z $username ]]; then
  2543         # Figure out the username and gcos name.  To maintain compatibility
  2544         # with passwd(4), we must support '&' substitutions.
  2546 	username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
  2547 	if [[ -x $GETENT ]]; then
  2548 	    realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
  2549 	fi
  2550 	userupper=`print "$username" | sed 's/\<./\u&/g'`
  2551 	realname=`print $realname | sed s/\&/$userupper/`
  2552     fi
  2553 fi
  2555 date="on `date`"
  2557 if [[ -n "$username" && -n "$realname" ]]; then
  2558 	print "<tr><th>Prepared by:</th>"
  2559 	print "<td>$realname ($username) $date</td></tr>"
  2560 elif [[ -n "$username" ]]; then
  2561 	print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
  2562 fi
  2564 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
  2565 if [[ -n $parent_webrev ]]; then
  2566         print "<tr><th>Compare against:</th><td>"
  2567 	print "webrev at $parent_webrev"
  2568 else
  2569     if [[ -n $OUTPWS2 ]]; then
  2570         print "<tr><th>Compare against:</th><td>"
  2571 	print "$OUTPWS2"
  2572     fi
  2573 fi
  2574 print "</td></tr>"
  2575 if [[ -n $rflag ]]; then
  2576     print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
  2577 elif [[ -n $OUTREV ]]; then
  2578     if [[ -z $forestflag ]]; then
  2579         print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
  2580     fi
  2581 fi
  2582 if [[ -n $HG_BRANCH ]]; then
  2583     print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
  2584 fi
  2586 print "<tr><th>Summary of changes:</th><td>"
  2587 printCI $TOTL $TINS $TDEL $TMOD $TUNC
  2588 print "</td></tr>"
  2590 if [[ -f $WDIR/$WNAME.patch ]]; then
  2591   print "<tr><th>Patch of changes:</th><td>"
  2592   print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
  2593 elif [[ -f $CHANGESETPATH ]]; then
  2594   print "<tr><th>Changeset:</th><td>"
  2595   print "<a href=\"$EXPORTCHANGESET\">$EXPORTCHANGESET</a></td></tr>"
  2596 fi
  2598 if [[ -f $WDIR/$WNAME.pdf ]]; then
  2599 	print "<tr><th>Printable review:</th><td>"
  2600 	print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
  2601 fi
  2603 if [[ -n "$iflag" ]]; then
  2604 	print "<tr><th>Author comments:</th><td><div>"
  2605 	cat /tmp/$$.include
  2606 	print "</div></td></tr>"
  2607 fi
  2608 # Add links to referenced CRs, if any
  2609 # URL has a <title> like:
  2610 # <title>[#JDK-8024688] b106-lambda: j.u.Map.merge doesn&#39;t work as specified if contains key:null pair - Java Bug System</title>
  2611 # we format this to:
  2612 # JDK-8024688: b106-lambda: j.u.Map.merge doesn't work as specified if contains key:null pair
  2613 if [[ -n $CRID ]]; then
  2614     for id in $CRID
  2615     do
  2616         #add "JDK-" to raw bug id for openjdk.java.net links.
  2617         id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'`
  2619         print "<tr><th>Bug id:</th><td>"
  2620         url="${BUGURL}${id}"
  2622         if [[ -n $WGET ]]; then
  2623             msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '<title>' | sed 's/<title>\[#\(.*\)\] \(.*\) - Java Bug System<\/title>/\1 : \2/' | html_dequote | html_quote`
  2624         fi
  2625         if [[ -z $msg ]]; then
  2626             msg="${id}"
  2627         fi
  2629         print "<a href=\"$url\">$msg</a>"
  2631         print "</td></tr>"
  2632     done
  2633 fi
  2634 print "<tr><th>Legend:</th><td>"
  2635 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>"
  2636 print "</table>"
  2637 print "</div>"
  2640 # Second pass through the files: generate the rest of the index file
  2642 while read LINE
  2643 do
  2644 	set - $LINE
  2645         if [[ $1 == "Revision:" ]]; then
  2646             FIRST_CREV=`expr $3 + 1`
  2647             continue
  2648         fi
  2649 	P=$1
  2651 	if [[ $# == 2 ]]; then
  2652 		PP=$2
  2653 		oldname=" <i>(was $PP)</i>"
  2655 	else
  2656 		PP=$P
  2657 		oldname=""
  2658 	fi
  2660 	DIR=${P%/*}
  2661 	if [[ $DIR == $P ]]; then
  2662 		DIR="."   # File at root of workspace
  2663 	fi
  2665 	# Avoid processing the same file twice.
  2666 	# It's possible for renamed files to
  2667 	# appear twice in the file list
  2669 	F=$WDIR/$P
  2671 	print "<p><code>"
  2673 	# If there's a diffs file, make diffs links
  2675         NODIFFS=
  2676 	if [[ -f $F.cdiff.html ]]; then
  2677 		print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
  2678 		print "<a href=\"$P.udiff.html\">Udiffs</a>"
  2680 		if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
  2681 			print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
  2682 		fi
  2684 		print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
  2686 		print "<a href=\"$P.frames.html\">Frames</a>"
  2687 	else
  2688                 NODIFFS=1
  2689 		print " ------ ------ ------"
  2691 		if [[ -x $WDIFF ]]; then
  2692 			print " ------"
  2693 		fi
  2695 		print " ------"
  2696 	fi
  2698 	# If there's an old file, make the link
  2700         NOOLD=
  2701 	if [[ -f $F-.html ]]; then
  2702 		print "<a href=\"$P-.html\">Old</a>"
  2703 	else
  2704                 NOOLD=1
  2705 		print " ---"
  2706 	fi
  2708 	# If there's an new file, make the link
  2710         NONEW=
  2711 	if [[ -f $F.html ]]; then
  2712 		print "<a href=\"$P.html\">New</a>"
  2713 	else
  2714                 NONEW=1
  2715 		print " ---"
  2716 	fi
  2718 	if [[ -f $F.patch ]]; then
  2719 		print "<a href=\"$P.patch\">Patch</a>"
  2720 	else
  2721 		print " -----"
  2722 	fi
  2724 	if [[ -f $WDIR/raw_files/new/$P ]]; then
  2725 		print "<a href=\"raw_files/new/$P\">Raw</a>"
  2726 	else
  2727 		print " ---"
  2728 	fi
  2729         print "</code>"
  2730         if [[ -n $NODIFFS && -z $oldname ]]; then
  2731             if [[ -n $NOOLD ]]; then
  2732                 print "<font color=green><b>$P</b></font>"
  2733             elif [[ -n $NONEW ]]; then
  2734                 print "<font color=red><b>$P</b></font>"
  2735             fi
  2736         else
  2737 	    print "<b>$P</b> $oldname"
  2738         fi
  2740 	print "</p><blockquote>\c"
  2741 	# Insert delta comments if any
  2742 	comments=`getcomments html $P $PP`
  2743 	if [ -n "$comments" ]; then
  2744 	    print "<pre>$comments</pre>"
  2745 	fi
  2747 	# Add additional comments comment
  2749 	print "<!-- Add comments to explain changes in $P here -->"
  2751 	# Add count of changes.
  2753 	if [[ -f $F.count ]]; then
  2754 	    cat $F.count
  2755 	    rm $F.count
  2756 	fi
  2757         print "</blockquote>"
  2758 done < $FLIST
  2760 print
  2761 print
  2762 print "<hr />"
  2763 print "<p style=\"font-size: small\">"
  2764 print "This code review page was prepared using <b>$0</b>"
  2765 print "(vers $WEBREV_UPDATED)."
  2766 print "</body>"
  2767 print "</html>"
  2769 if [[ -n $ZIP ]]; then
  2770     # Let's generate a zip file for convenience
  2771     cd $WDIR/..
  2772     if [ -f webrev.zip ]; then
  2773 	rm webrev.zip
  2774     fi
  2775     $ZIP -r webrev webrev >/dev/null 2>&1
  2776 fi
  2778 exec 1<&-			# Close FD 1.
  2779 exec 1<&3			# dup FD 3 to restore stdout.
  2780 exec 3<&-			# close FD 3.
  2782 print "Done."
  2783 print "Output to: $WDIR"

mercurial