make/scripts/webrev.ksh

Tue, 16 Apr 2013 13:25:39 -0400

author
jgish
date
Tue, 16 Apr 2013 13:25:39 -0400
changeset 690
b95c5c8ee60a
parent 678
26a4456cb19e
child 792
67f64101616e
permissions
-rw-r--r--

8011347: JKD-8009824 has broken webrev with some ksh versions
Reviewed-by: mduigou

     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=24.0-hg+jbs
    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 http-equiv="cache-control" content="no-cache" />
    43 <meta http-equiv="Pragma" content="no-cache" />
    44 <meta http-equiv="Expires" content="-1" />
    45 <!--
    46    Note to customizers: the body of the webrev is IDed as SUNWwebrev
    47    to allow easy overriding by users of webrev via the userContent.css
    48    mechanism available in some browsers.
    50    For example, to have all "removed" information be red instead of
    51    brown, set a rule in your userContent.css file like:
    53        body#SUNWwebrev span.removed { color: red ! important; }
    54 -->
    55 <style type="text/css" media="screen">
    56 body {
    57     background-color: #eeeeee;
    58 }
    59 hr {
    60     border: none 0;
    61     border-top: 1px solid #aaa;
    62     height: 1px;
    63 }
    64 div.summary {
    65     font-size: .8em;
    66     border-bottom: 1px solid #aaa;
    67     padding-left: 1em;
    68     padding-right: 1em;
    69 }
    70 div.summary h2 {
    71     margin-bottom: 0.3em;
    72 }
    73 div.summary table th {
    74     text-align: right;
    75     vertical-align: top;
    76     white-space: nowrap;
    77 }
    78 span.lineschanged {
    79     font-size: 0.7em;
    80 }
    81 span.oldmarker {
    82     color: red;
    83     font-size: large;
    84     font-weight: bold;
    85 }
    86 span.newmarker {
    87     color: green;
    88     font-size: large;
    89     font-weight: bold;
    90 }
    91 span.removed {
    92     color: brown;
    93 }
    94 span.changed {
    95     color: blue;
    96 }
    97 span.new {
    98     color: blue;
    99     font-weight: bold;
   100 }
   101 a.print { font-size: x-small; }
   103 </style>
   105 <style type="text/css" media="print">
   106 pre { font-size: 0.8em; font-family: courier, monospace; }
   107 span.removed { color: #444; font-style: italic }
   108 span.changed { font-weight: bold; }
   109 span.new { font-weight: bold; }
   110 span.newmarker { font-size: 1.2em; font-weight: bold; }
   111 span.oldmarker { font-size: 1.2em; font-weight: bold; }
   112 a.print {display: none}
   113 hr { border: none 0; border-top: 1px solid #aaa; height: 1px; }
   114 </style>
   115 '
   117 #
   118 # UDiffs need a slightly different CSS rule for 'new' items (we don't
   119 # want them to be bolded as we do in cdiffs or sdiffs).
   120 #
   121 UDIFFCSS='
   122 <style type="text/css" media="screen">
   123 span.new {
   124     color: blue;
   125     font-weight: normal;
   126 }
   127 </style>
   128 '
   130 #
   131 # input_cmd | html_quote | output_cmd
   132 # or
   133 # html_quote filename | output_cmd
   134 #
   135 # Make a piece of source code safe for display in an HTML <pre> block.
   136 #
   137 html_quote()
   138 {
   139 	sed -e "s/&/\&amp;/g" -e "s/</\&lt;/g" -e "s/>/\&gt;/g" "$@" | expand
   140 }
   142 #
   143 # input_cmd | bug2url | output_cmd
   144 #
   145 # Scan for bugids and insert <a> links to the relevent bug database.
   146 #
   147 bug2url()
   148 {
   149 	sed -e 's|[0-9]\{5,\}|<a href=\"'$BUGURL$IDPREFIX'&\">&</a>|g'
   150 }
   152 #
   153 # input_cmd | sac2url | output_cmd
   154 #
   155 # Scan for ARC cases and insert <a> links to the relevent SAC database.
   156 # This is slightly complicated because inside the SWAN, SAC cases are
   157 # grouped by ARC: PSARC/2006/123.  But on OpenSolaris.org, they are
   158 # referenced as 2006/123 (without labelling the ARC).
   159 #
   160 sac2url()
   161 {
   162 	if [[ -z $Oflag ]]; then
   163 	    sed -e 's|\([A-Z]\{1,2\}ARC\)[ /]\([0-9]\{4\}\)/\([0-9]\{3\}\)|<a href=\"'$SACURL'\1/\2/\3\">\1 \2/\3</a>|g'
   164 	else
   165 	    sed -e 's|\([A-Z]\{1,2\}ARC\)[ /]\([0-9]\{4\}\)/\([0-9]\{3\}\)|<a href=\"'$SACURL'/\2/\3\">\1 \2/\3</a>|g'
   166 	fi
   167 }
   169 #
   170 # strip_unchanged <infile> | output_cmd
   171 #
   172 # Removes chunks of sdiff documents that have not changed. This makes it
   173 # easier for a code reviewer to find the bits that have changed.
   174 #
   175 # Deleted lines of text are replaced by a horizontal rule. Some
   176 # identical lines are retained before and after the changed lines to
   177 # provide some context.  The number of these lines is controlled by the
   178 # variable C in the $AWK script below.
   179 #
   180 # The script detects changed lines as any line that has a "<span class="
   181 # string embedded (unchanged lines have no particular class and are not
   182 # part of a <span>).  Blank lines (without a sequence number) are also
   183 # detected since they flag lines that have been inserted or deleted.
   184 #
   185 strip_unchanged()
   186 {
   187 	$AWK '
   188 	BEGIN	{ C = c = 20 }
   189 	NF == 0 || /span class=/ {
   190 		if (c > C) {
   191 			c -= C
   192 			inx = 0
   193 			if (c > C) {
   194 				print "\n</pre><hr></hr><pre>"
   195 				inx = c % C
   196 				c = C
   197 			}
   199 			for (i = 0; i < c; i++)
   200 				print ln[(inx + i) % C]
   201 		}
   202 		c = 0;
   203 		print
   204 		next
   205 	}
   206 	{	if (c >= C) {
   207 			ln[c % C] = $0
   208 			c++;
   209 			next;
   210 		}
   211 		c++;
   212 		print
   213 	}
   214 	END	{ if (c > (C * 2)) print "\n</pre><hr></hr>" }
   216 	' $1
   217 }
   219 #
   220 # sdiff_to_html
   221 #
   222 # This function takes two files as arguments, obtains their diff, and
   223 # processes the diff output to present the files as an HTML document with
   224 # the files displayed side-by-side, differences shown in color.  It also
   225 # takes a delta comment, rendered as an HTML snippet, as the third
   226 # argument.  The function takes two files as arguments, then the name of
   227 # file, the path, and the comment.  The HTML will be delivered on stdout,
   228 # e.g.
   229 #
   230 #   $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \
   231 #         new/usr/src/tools/scripts/webrev.sh \
   232 #         webrev.sh usr/src/tools/scripts \
   233 #         '<a href="https://jbs.oracle.com/bugs/browse/JDK-1234567">
   234 #          JDK-1234567</a> my bugid' > <file>.html
   235 #
   236 # framed_sdiff() is then called which creates $2.frames.html
   237 # in the webrev tree.
   238 #
   239 # FYI: This function is rather unusual in its use of awk.  The initial
   240 # diff run produces conventional diff output showing changed lines mixed
   241 # with editing codes.  The changed lines are ignored - we're interested in
   242 # the editing codes, e.g.
   243 #
   244 #      8c8
   245 #      57a61
   246 #      63c66,76
   247 #      68,93d80
   248 #      106d90
   249 #      108,110d91
   250 #
   251 #  These editing codes are parsed by the awk script and used to generate
   252 #  another awk script that generates HTML, e.g the above lines would turn
   253 #  into something like this:
   254 #
   255 #      BEGIN { printf "<pre>\n" }
   256 #      function sp(n) {for (i=0;i<n;i++)printf "\n"}
   257 #      function wl(n) {printf "<font color=%s>%4d %s </font>\n", n, NR, $0}
   258 #      NR==8           {wl("#7A7ADD");next}
   259 #      NR==54          {wl("#7A7ADD");sp(3);next}
   260 #      NR==56          {wl("#7A7ADD");next}
   261 #      NR==57          {wl("black");printf "\n"; next}
   262 #        :               :
   263 #
   264 #  This script is then run on the original source file to generate the
   265 #  HTML that corresponds to the source file.
   266 #
   267 #  The two HTML files are then combined into a single piece of HTML that
   268 #  uses an HTML table construct to present the files side by side.  You'll
   269 #  notice that the changes are color-coded:
   270 #
   271 #   black     - unchanged lines
   272 #   blue      - changed lines
   273 #   bold blue - new lines
   274 #   brown     - deleted lines
   275 #
   276 #  Blank lines are inserted in each file to keep unchanged lines in sync
   277 #  (side-by-side).  This format is familiar to users of sdiff(1) or
   278 #  Teamware's filemerge tool.
   279 #
   280 sdiff_to_html()
   281 {
   282 	diff -b $1 $2 > /tmp/$$.diffs
   284 	TNAME=$3
   285 	TPATH=$4
   286 	COMMENT=$5
   288 	#
   289 	#  Now we have the diffs, generate the HTML for the old file.
   290 	#
   291 	$AWK '
   292 	BEGIN	{
   293 		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
   294 		printf "function removed() "
   295 		printf "{printf \"<span class=\\\"removed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   296 		printf "function changed() "
   297 		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   298 		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
   299 }
   300 	/^</	{next}
   301 	/^>/	{next}
   302 	/^---/	{next}
   304 	{
   305 	split($1, a, /[cad]/) ;
   306 	if (index($1, "a")) {
   307 		if (a[1] == 0) {
   308 			n = split(a[2], r, /,/);
   309 			if (n == 1)
   310 				printf "BEGIN\t\t{sp(1)}\n"
   311 			else
   312 				printf "BEGIN\t\t{sp(%d)}\n",\
   313 				(r[2] - r[1]) + 1
   314 			next
   315 		}
   317 		printf "NR==%s\t\t{", a[1]
   318 		n = split(a[2], r, /,/);
   319 		s = r[1];
   320 		if (n == 1)
   321 			printf "bl();printf \"\\n\"; next}\n"
   322 		else {
   323 			n = r[2] - r[1]
   324 			printf "bl();sp(%d);next}\n",\
   325 			(r[2] - r[1]) + 1
   326 		}
   327 		next
   328 	}
   329 	if (index($1, "d")) {
   330 		n = split(a[1], r, /,/);
   331 		n1 = r[1]
   332 		n2 = r[2]
   333 		if (n == 1)
   334 			printf "NR==%s\t\t{removed(); next}\n" , n1
   335 		else
   336 			printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
   337 		next
   338 	}
   339 	if (index($1, "c")) {
   340 		n = split(a[1], r, /,/);
   341 		n1 = r[1]
   342 		n2 = r[2]
   343 		final = n2
   344 		d1 = 0
   345 		if (n == 1)
   346 			printf "NR==%s\t\t{changed();" , n1
   347 		else {
   348 			d1 = n2 - n1
   349 			printf "NR==%s,NR==%s\t{changed();" , n1, n2
   350 		}
   351 		m = split(a[2], r, /,/);
   352 		n1 = r[1]
   353 		n2 = r[2]
   354 		if (m > 1) {
   355 			d2  = n2 - n1
   356 			if (d2 > d1) {
   357 				if (n > 1) printf "if (NR==%d)", final
   358 				printf "sp(%d);", d2 - d1
   359 			}
   360 		}
   361 		printf "next}\n" ;
   363 		next
   364 	}
   365 	}
   367 	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
   368 	' /tmp/$$.diffs > /tmp/$$.file1
   370 	#
   371 	#  Now generate the HTML for the new file
   372 	#
   373 	$AWK '
   374 	BEGIN	{
   375 		printf "function sp(n) {for (i=0;i<n;i++)printf \"\\n\"}\n"
   376 		printf "function new() "
   377 		printf "{printf \"<span class=\\\"new\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   378 		printf "function changed() "
   379 		printf "{printf \"<span class=\\\"changed\\\">%%4d %%s</span>\\n\", NR, $0}\n"
   380 		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
   381 	}
   383 	/^</	{next}
   384 	/^>/	{next}
   385 	/^---/	{next}
   387 	{
   388 	split($1, a, /[cad]/) ;
   389 	if (index($1, "d")) {
   390 		if (a[2] == 0) {
   391 			n = split(a[1], r, /,/);
   392 			if (n == 1)
   393 				printf "BEGIN\t\t{sp(1)}\n"
   394 			else
   395 				printf "BEGIN\t\t{sp(%d)}\n",\
   396 				(r[2] - r[1]) + 1
   397 			next
   398 		}
   400 		printf "NR==%s\t\t{", a[2]
   401 		n = split(a[1], r, /,/);
   402 		s = r[1];
   403 		if (n == 1)
   404 			printf "bl();printf \"\\n\"; next}\n"
   405 		else {
   406 			n = r[2] - r[1]
   407 			printf "bl();sp(%d);next}\n",\
   408 			(r[2] - r[1]) + 1
   409 		}
   410 		next
   411 	}
   412 	if (index($1, "a")) {
   413 		n = split(a[2], r, /,/);
   414 		n1 = r[1]
   415 		n2 = r[2]
   416 		if (n == 1)
   417 			printf "NR==%s\t\t{new() ; next}\n" , n1
   418 		else
   419 			printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
   420 		next
   421 	}
   422 	if (index($1, "c")) {
   423 		n = split(a[2], r, /,/);
   424 		n1 = r[1]
   425 		n2 = r[2]
   426 		final = n2
   427 		d2 = 0;
   428 		if (n == 1) {
   429 			final = n1
   430 			printf "NR==%s\t\t{changed();" , n1
   431 		} else {
   432 			d2 = n2 - n1
   433 			printf "NR==%s,NR==%s\t{changed();" , n1, n2
   434 		}
   435 		m = split(a[1], r, /,/);
   436 		n1 = r[1]
   437 		n2 = r[2]
   438 		if (m > 1) {
   439 			d1  = n2 - n1
   440 			if (d1 > d2) {
   441 				if (n > 1) printf "if (NR==%d)", final
   442 				printf "sp(%d);", d1 - d2
   443 			}
   444 		}
   445 		printf "next}\n" ;
   446 		next
   447 	}
   448 	}
   449 	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
   450 	' /tmp/$$.diffs > /tmp/$$.file2
   452 	#
   453 	# Post-process the HTML files by running them back through $AWK
   454 	#
   455 	html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html
   457 	html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html
   459 	#
   460 	# Now combine into a valid HTML file and side-by-side into a table
   461 	#
   462 	print "$HTML<head>$STDHEAD"
   463 	print "<title>$WNAME Sdiff $TPATH </title>"
   464 	print "</head><body id=\"SUNWwebrev\">"
   465 	print "<h2>$TPATH/$TNAME</h2>"
   466         print "<a class=\"print\" href=\"javascript:print()\">Print this page</a>"
   467 	print "<pre>$COMMENT</pre>\n"
   468 	print "<table><tr valign=\"top\">"
   469 	print "<td><pre>"
   471 	strip_unchanged /tmp/$$.file1.html
   473 	print "</pre></td><td><pre>"
   475 	strip_unchanged /tmp/$$.file2.html
   477 	print "</pre></td>"
   478 	print "</tr></table>"
   479 	print "</body></html>"
   481 	framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \
   482 	    "$COMMENT"
   483 }
   486 #
   487 # framed_sdiff <filename> <filepath> <lhsfile> <rhsfile> <comment>
   488 #
   489 # Expects lefthand and righthand side html files created by sdiff_to_html.
   490 # We use insert_anchors() to augment those with HTML navigation anchors,
   491 # and then emit the main frame.  Content is placed into:
   492 #
   493 #    $WDIR/DIR/$TNAME.lhs.html
   494 #    $WDIR/DIR/$TNAME.rhs.html
   495 #    $WDIR/DIR/$TNAME.frames.html
   496 #
   497 # NOTE: We rely on standard usage of $WDIR and $DIR.
   498 #
   499 function framed_sdiff
   500 {
   501 	typeset TNAME=$1
   502 	typeset TPATH=$2
   503 	typeset lhsfile=$3
   504 	typeset rhsfile=$4
   505 	typeset comments=$5
   506 	typeset RTOP
   508 	# Enable html files to access WDIR via a relative path.
   509 	RTOP=$(relative_dir $TPATH $WDIR)
   511 	# Make the rhs/lhs files and output the frameset file.
   512 	print "$HTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html
   514 	cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF
   515 	    <script type="text/javascript" src="$RTOP/ancnav.js"></script>
   516 	    </head>
   517 	    <body id="SUNWwebrev" onkeypress="keypress(event);">
   518 	    <a name="0"></a>
   519 	    <pre>$comments</pre><hr></hr>
   520 	EOF
   522 	cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html
   524 	insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html
   525 	insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html
   527 	close='</body></html>'
   529 	print $close >> $WDIR/$DIR/$TNAME.lhs.html
   530 	print $close >> $WDIR/$DIR/$TNAME.rhs.html
   532 	print "$FRAMEHTML<head>$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html
   533 	print "<title>$WNAME Framed-Sdiff " \
   534 	    "$TPATH/$TNAME</title> </head>" >> $WDIR/$DIR/$TNAME.frames.html
   535 	cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF
   536 	  <frameset rows="*,60">
   537 	    <frameset cols="50%,50%">
   538 	      <frame src="$TNAME.lhs.html" scrolling="auto" name="lhs" />
   539 	      <frame src="$TNAME.rhs.html" scrolling="auto" name="rhs" />
   540 	    </frameset>
   541 	  <frame src="$RTOP/ancnav.html" scrolling="no" marginwidth="0"
   542 	   marginheight="0" name="nav" />
   543 	  <noframes>
   544             <body id="SUNWwebrev">
   545 	      Alas 'frames' webrev requires that your browser supports frames
   546 	      and has the feature enabled.
   547             </body>
   548 	  </noframes>
   549 	  </frameset>
   550 	</html>
   551 	EOF
   552 }
   555 #
   556 # fix_postscript
   557 #
   558 # Merge codereview output files to a single conforming postscript file, by:
   559 # 	- removing all extraneous headers/trailers
   560 #	- making the page numbers right
   561 #	- removing pages devoid of contents which confuse some
   562 #	  postscript readers.
   563 #
   564 # From Casper.
   565 #
   566 function fix_postscript
   567 {
   568 	infile=$1
   570 	cat > /tmp/$$.crmerge.pl << \EOF
   572 	print scalar(<>);		# %!PS-Adobe---
   573 	print "%%Orientation: Landscape\n";
   575 	$pno = 0;
   576 	$doprint = 1;
   578 	$page = "";
   580 	while (<>) {
   581 		next if (/^%%Pages:\s*\d+/);
   583 		if (/^%%Page:/) {
   584 			if ($pno == 0 || $page =~ /\)S/) {
   585 				# Header or single page containing text
   586 				print "%%Page: ? $pno\n" if ($pno > 0);
   587 				print $page;
   588 				$pno++;
   589 			} else {
   590 				# Empty page, skip it.
   591 			}
   592 			$page = "";
   593 			$doprint = 1;
   594 			next;
   595 		}
   597 		# Skip from %%Trailer of one document to Endprolog
   598 		# %%Page of the next
   599 		$doprint = 0 if (/^%%Trailer/);
   600 		$page .= $_ if ($doprint);
   601 	}
   603 	if ($page =~ /\)S/) {
   604 		print "%%Page: ? $pno\n";
   605 		print $page;
   606 	} else {
   607 		$pno--;
   608 	}
   609 	print "%%Trailer\n%%Pages: $pno\n";
   610 EOF
   612 	$PERL /tmp/$$.crmerge.pl < $infile
   613 }
   616 #
   617 # input_cmd | insert_anchors | output_cmd
   618 #
   619 # Flag blocks of difference with sequentially numbered invisible
   620 # anchors.  These are used to drive the frames version of the
   621 # sdiffs output.
   622 #
   623 # NOTE: Anchor zero flags the top of the file irrespective of changes,
   624 # an additional anchor is also appended to flag the bottom.
   625 #
   626 # The script detects changed lines as any line that has a "<span
   627 # class=" string embedded (unchanged lines have no class set and are
   628 # not part of a <span>.  Blank lines (without a sequence number)
   629 # are also detected since they flag lines that have been inserted or
   630 # deleted.
   631 #
   632 function insert_anchors
   633 {
   634 	$AWK '
   635 	function ia() {
   636 		# This should be able to be a singleton <a /> but that
   637 		# seems to trigger a bug in firefox a:hover rule processing
   638 		printf "<a name=\"%d\" id=\"anc%d\"></a>", anc, anc++;
   639 	}
   641 	BEGIN {
   642 		anc=1;
   643 		inblock=1;
   644 		printf "<pre>\n";
   645 	}
   646 	NF == 0 || /^<span class=/ {
   647 		if (inblock == 0) {
   648 			ia();
   649 			inblock=1;
   650 		}
   651 		print;
   652 		next;
   653 	}
   654 	{
   655 		inblock=0;
   656 		print;
   657 	}
   658 	END {
   659 		ia();
   661 		printf "<b style=\"font-size: large; color: red\">";
   662 		printf "--- EOF ---</b>"
   663         	for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
   664 		printf "</pre>"
   665 		printf "<form name=\"eof\">";
   666 		printf "<input name=\"value\" value=\"%d\" type=\"hidden\" />",
   667 		    anc - 1;
   668 		printf "</form>";
   669 	}
   670 	' $1
   671 }
   674 #
   675 # relative_dir
   676 #
   677 # Print a relative return path from $1 to $2.  For example if
   678 # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview,
   679 # this function would print "../../../../".
   680 #
   681 # In the event that $1 is not in $2 a warning is printed to stderr,
   682 # and $2 is returned-- the result of this is that the resulting webrev
   683 # is not relocatable.
   684 #
   685 function relative_dir
   686 {
   687     d1=$1
   688     d2=$2
   689     if [[ "$d1" == "." ]]; then
   690 	print "."
   691     else
   692 	typeset cur="${d1##$d2?(/)}"
   693 	typeset ret=""
   694 	if [[ $d2 == $cur ]]; then   # Should never happen.
   695 		# Should never happen.
   696 		print -u2 "\nWARNING: relative_dir: \"$1\" not relative "
   697 		print -u2 "to \"$2\".  Check input paths.  Framed webrev "
   698 		print -u2 "will not be relocatable!"
   699 		print $2
   700 		return
   701 	fi
   703 	while [[ -n ${cur} ]];
   704 	do
   705 		cur=${cur%%*(/)*([!/])}
   706 		if [[ -z $ret ]]; then
   707 			ret=".."
   708 		else
   709 			ret="../$ret"
   710 		fi
   711 	done
   712 	print $ret
   713     fi
   714 }
   717 #
   718 # frame_nav_js
   719 #
   720 # Emit javascript for frame navigation
   721 #
   722 function frame_nav_js
   723 {
   724 cat << \EOF
   725 var myInt;
   726 var scrolling=0;
   727 var sfactor = 3;
   728 var scount=10;
   730 function scrollByPix() {
   731 	if (scount<=0) {
   732 		sfactor*=1.2;
   733 		scount=10;
   734 	}
   735 	parent.lhs.scrollBy(0,sfactor);
   736 	parent.rhs.scrollBy(0,sfactor);
   737 	scount--;
   738 }
   740 function scrollToAnc(num) {
   742 	// Update the value of the anchor in the form which we use as
   743 	// storage for this value.  setAncValue() will take care of
   744 	// correcting for overflow and underflow of the value and return
   745 	// us the new value.
   746 	num = setAncValue(num);
   748 	// Set location and scroll back a little to expose previous
   749 	// lines.
   750 	//
   751 	// Note that this could be improved: it is possible although
   752 	// complex to compute the x and y position of an anchor, and to
   753 	// scroll to that location directly.
   754 	//
   755 	parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num);
   756 	parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num);
   758 	parent.lhs.scrollBy(0,-30);
   759 	parent.rhs.scrollBy(0,-30);
   760 }
   762 function getAncValue()
   763 {
   764 	return (parseInt(parent.nav.document.diff.real.value));
   765 }
   767 function setAncValue(val)
   768 {
   769 	if (val <= 0) {
   770 		val = 0;
   771 		parent.nav.document.diff.real.value = val;
   772 		parent.nav.document.diff.display.value = "BOF";
   773 		return (val);
   774 	}
   776 	//
   777 	// The way we compute the max anchor value is to stash it
   778 	// inline in the left and right hand side pages-- it's the same
   779 	// on each side, so we pluck from the left.
   780 	//
   781 	maxval = parent.lhs.document.eof.value.value;
   782 	if (val < maxval) {
   783 		parent.nav.document.diff.real.value = val;
   784 		parent.nav.document.diff.display.value = val.toString();
   785 		return (val);
   786 	}
   788 	// this must be: val >= maxval
   789 	val = maxval;
   790 	parent.nav.document.diff.real.value = val;
   791 	parent.nav.document.diff.display.value = "EOF";
   792 	return (val);
   793 }
   795 function stopScroll() {
   796 	if (scrolling==1) {
   797 		clearInterval(myInt);
   798 		scrolling=0;
   799 	}
   800 }
   802 function startScroll() {
   803 	stopScroll();
   804 	scrolling=1;
   805 	myInt=setInterval("scrollByPix()",10);
   806 }
   808 function handlePress(b) {
   810 	switch (b) {
   811 	    case 1 :
   812 		scrollToAnc(-1);
   813 		break;
   814 	    case 2 :
   815 		scrollToAnc(getAncValue() - 1);
   816 		break;
   817 	    case 3 :
   818 		sfactor=-3;
   819 		startScroll();
   820 		break;
   821 	    case 4 :
   822 		sfactor=3;
   823 		startScroll();
   824 		break;
   825 	    case 5 :
   826 		scrollToAnc(getAncValue() + 1);
   827 		break;
   828 	    case 6 :
   829 		scrollToAnc(999999);
   830 		break;
   831 	}
   832 }
   834 function handleRelease(b) {
   835 	stopScroll();
   836 }
   838 function keypress(ev) {
   839 	var keynum;
   840 	var keychar;
   842 	if (window.event) { // IE
   843 		keynum = ev.keyCode;
   844 	} else if (ev.which) { // non-IE
   845 		keynum = ev.which;
   846 	}
   848 	keychar = String.fromCharCode(keynum);
   850 	if (keychar == "k") {
   851 		handlePress(2);
   852 		return (0);
   853 	} else if (keychar == "j" || keychar == " ") {
   854 		handlePress(5);
   855 		return (0);
   856 	}
   857 	return (1);
   858 }
   860 function ValidateDiffNum(){
   861 	val = parent.nav.document.diff.display.value;
   862 	if (val == "EOF") {
   863 		scrollToAnc(999999);
   864 		return;
   865 	}
   867 	if (val == "BOF") {
   868 		scrollToAnc(0);
   869 		return;
   870 	}
   872         i=parseInt(val);
   873         if (isNaN(i)) {
   874                 parent.nav.document.diff.display.value = getAncValue();
   875         } else {
   876                 scrollToAnc(i);
   877         }
   878         return false;
   879 }
   881 EOF
   882 }
   884 #
   885 # frame_navigation
   886 #
   887 # Output anchor navigation file for framed sdiffs.
   888 #
   889 function frame_navigation
   890 {
   891 	print "$HTML<head>$STDHEAD"
   893 	cat << \EOF
   894 <title>Anchor Navigation</title>
   895 <meta http-equiv="Content-Script-Type" content="text/javascript" />
   896 <meta http-equiv="Content-Type" content="text/html" />
   898 <style type="text/css">
   899     div.button td { padding-left: 5px; padding-right: 5px;
   900 		    background-color: #eee; text-align: center;
   901 		    border: 1px #444 outset; cursor: pointer; }
   902     div.button a { font-weight: bold; color: black }
   903     div.button td:hover { background: #ffcc99; }
   904 </style>
   905 EOF
   907 	print "<script type=\"text/javascript\" src=\"ancnav.js\"></script>"
   909 	cat << \EOF
   910 </head>
   911 <body id="SUNWwebrev" bgcolor="#eeeeee" onload="document.diff.real.focus();"
   912 	onkeypress="keypress(event);">
   913     <noscript lang="javascript">
   914       <center>
   915 	<p><big>Framed Navigation controls require Javascript</big><br />
   916 	Either this browser is incompatable or javascript is not enabled</p>
   917       </center>
   918     </noscript>
   919     <table width="100%" border="0" align="center">
   920 	<tr>
   921           <td valign="middle" width="25%">Diff navigation:
   922           Use 'j' and 'k' for next and previous diffs; or use buttons
   923           at right</td>
   924 	  <td align="center" valign="top" width="50%">
   925 	    <div class="button">
   926 	      <table border="0" align="center">
   927                   <tr>
   928 		    <td>
   929 		      <a onMouseDown="handlePress(1);return true;"
   930 			 onMouseUp="handleRelease(1);return true;"
   931 			 onMouseOut="handleRelease(1);return true;"
   932 			 onClick="return false;"
   933 			 title="Go to Beginning Of file">BOF</a></td>
   934 		    <td>
   935 		      <a onMouseDown="handlePress(3);return true;"
   936 			 onMouseUp="handleRelease(3);return true;"
   937 			 onMouseOut="handleRelease(3);return true;"
   938 			 title="Scroll Up: Press and Hold to accelerate"
   939 			 onClick="return false;">Scroll Up</a></td>
   940 		    <td>
   941 		      <a onMouseDown="handlePress(2);return true;"
   942 			 onMouseUp="handleRelease(2);return true;"
   943 			 onMouseOut="handleRelease(2);return true;"
   944 			 title="Go to previous Diff"
   945 			 onClick="return false;">Prev Diff</a>
   946 		    </td></tr>
   948 		  <tr>
   949 		    <td>
   950 		      <a onMouseDown="handlePress(6);return true;"
   951 			 onMouseUp="handleRelease(6);return true;"
   952 			 onMouseOut="handleRelease(6);return true;"
   953 			 onClick="return false;"
   954 			 title="Go to End Of File">EOF</a></td>
   955 		    <td>
   956 		      <a onMouseDown="handlePress(4);return true;"
   957 			 onMouseUp="handleRelease(4);return true;"
   958 			 onMouseOut="handleRelease(4);return true;"
   959 			 title="Scroll Down: Press and Hold to accelerate"
   960 			 onClick="return false;">Scroll Down</a></td>
   961 		    <td>
   962 		      <a onMouseDown="handlePress(5);return true;"
   963 			 onMouseUp="handleRelease(5);return true;"
   964 			 onMouseOut="handleRelease(5);return true;"
   965 			 title="Go to next Diff"
   966 			 onClick="return false;">Next Diff</a></td>
   967 		  </tr>
   968               </table>
   969 	    </div>
   970 	  </td>
   971 	  <th valign="middle" width="25%">
   972 	    <form action="" name="diff" onsubmit="return ValidateDiffNum();">
   973 		<input name="display" value="BOF" size="8" type="text" />
   974 		<input name="real" value="0" size="8" type="hidden" />
   975 	    </form>
   976 	  </th>
   977 	</tr>
   978     </table>
   979   </body>
   980 </html>
   981 EOF
   982 }
   986 #
   987 # diff_to_html <filename> <filepath> { U | C } <comment>
   988 #
   989 # Processes the output of diff to produce an HTML file representing either
   990 # context or unified diffs.
   991 #
   992 diff_to_html()
   993 {
   994 	TNAME=$1
   995 	TPATH=$2
   996 	DIFFTYPE=$3
   997 	COMMENT=$4
   999 	print "$HTML<head>$STDHEAD"
  1000 	print "<title>$WNAME ${DIFFTYPE}diff $TPATH</title>"
  1002 	if [[ $DIFFTYPE == "U" ]]; then
  1003 		print "$UDIFFCSS"
  1004 	fi
  1006 	cat <<-EOF
  1007 	</head>
  1008 	<body id="SUNWwebrev">
  1009 	<h2>$TPATH</h2>
  1010         <a class="print" href="javascript:print()">Print this page</a>
  1011 	<pre>$COMMENT</pre>
  1012         <pre>
  1013 EOF
  1015 	html_quote | $AWK '
  1016 	/^--- new/	{ next }
  1017 	/^\+\+\+ new/	{ next }
  1018 	/^--- old/	{ next }
  1019 	/^\*\*\* old/	{ next }
  1020 	/^\*\*\*\*/	{ next }
  1021 	/^-------/	{ printf "<center><h1>%s</h1></center>\n", $0; next }
  1022 	/^\@\@.*\@\@$/	{ printf "</pre><hr /><pre>\n";
  1023 			  printf "<span class=\"newmarker\">%s</span>\n", $0;
  1024 			  next}
  1026 	/^\*\*\*/	{ printf "<hr /><span class=\"oldmarker\">%s</span>\n", $0;
  1027 			  next}
  1028 	/^---/		{ printf "<span class=\"newmarker\">%s</span>\n", $0;
  1029 			  next}
  1030 	/^\+/		{printf "<span class=\"new\">%s</span>\n", $0; next}
  1031 	/^!/		{printf "<span class=\"changed\">%s</span>\n", $0; next}
  1032 	/^-/		{printf "<span class=\"removed\">%s</span>\n", $0; next}
  1033 			{printf "%s\n", $0; next}
  1036 	print "</pre></body></html>\n"
  1041 # source_to_html { new | old } <filename>
  1043 # Process a plain vanilla source file to transform it into an HTML file.
  1045 source_to_html()
  1047 	WHICH=$1
  1048 	TNAME=$2
  1050 	print "$HTML<head>$STDHEAD"
  1051 	print "<title>$WHICH $TNAME</title>"
  1052 	print "<body id=\"SUNWwebrev\">"
  1053 	print "<pre>"
  1054 	html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
  1055 	print "</pre></body></html>"
  1059 # teamwarecomments {text|html} parent-file child-file
  1061 # Find the first delta in the child that's not in the parent.  Get the
  1062 # newest delta from the parent, get all deltas from the child starting
  1063 # with that delta, and then get all info starting with the second oldest
  1064 # delta in that list (the first delta unique to the child).
  1066 # This code adapted from Bill Shannon's "spc" script
  1068 comments_from_teamware()
  1070 	fmt=$1
  1071 	pfile=$PWS/$2
  1072 	cfile=$CWS/$3
  1074 	psid=$($SCCS prs -d:I: $pfile 2>/dev/null)
  1075 	if [[ -z "$psid" ]]; then
  1076 	    psid=1.1
  1077 	fi
  1079 	set -A sids $($SCCS prs -l -r$psid -d:I: $cfile 2>/dev/null)
  1080 	N=${#sids[@]}
  1082 	nawkprg='
  1083 		/^COMMENTS:/	{p=1; next}
  1084 		/^D [0-9]+\.[0-9]+/ {printf "--- %s ---\n", $2; p=0; }
  1085 		NF == 0u	{ next }
  1086 		{if (p==0) next; print $0 }'
  1088 	if [[ $N -ge 2 ]]; then
  1089 		sid1=${sids[$((N-2))]}	# Gets 2nd to last sid
  1091 		if [[ $fmt == "text" ]]; then
  1092 			$SCCS prs -l -r$sid1 $cfile  2>/dev/null | \
  1093 			    $AWK "$nawkprg"
  1094 			return
  1095 		fi
  1097 		$SCCS prs -l -r$sid1 $cfile  2>/dev/null | \
  1098 		    html_quote | bug2url | sac2url | $AWK "$nawkprg"
  1099 	fi
  1103 # wxcomments {text|html} filepath
  1105 # Given the pathname of a file, find its location in a "wx" active file
  1106 # list and print the following sccs comment.  Output is either text or
  1107 # HTML; if the latter, embedded bugids (sequence of 5 or more digits) are
  1108 # turned into URLs.
  1110 comments_from_wx()
  1112 	typeset fmt=$1
  1113 	typeset p=$2
  1115 	comm=`$AWK '
  1116 	$1 == "'$p'" {
  1117 		do getline ; while (NF > 0)
  1118 		getline
  1119 		while (NF > 0) { print ; getline }
  1120 		exit
  1121 	}' < $wxfile`
  1123 	if [[ $fmt == "text" ]]; then
  1124 		print "$comm"
  1125 		return
  1126 	fi
  1128 	print "$comm" | html_quote | bug2url | sac2url
  1131 comments_from_mercurial()
  1133 	fmt=$1
  1134 	pfile=$PWS/$2
  1135 	cfile=$CWS/$3
  1137         logdir=`dirname $cfile`
  1138         logf=`basename $cfile`
  1139         if [ -d $logdir ]; then
  1140             ( cd $logdir;
  1141 	        active=`hg status $logf 2>/dev/null`
  1142                 # If the output from 'hg status' is not empty, it means the file
  1143                 # hasn't been committed, so don't fetch comments.
  1144 	        if [[ -z $active ]] ; then
  1145                     if [[ -n $ALL_CREV ]]; then
  1146                         rev_opt=
  1147                         for rev in $ALL_CREV; do
  1148                             rev_opt="$rev_opt --rev $rev"
  1149                         done
  1150                         comm=`hg log $rev_opt --follow --template 'rev {rev} : {desc}\n' $logf`
  1151                     elif [[ -n $FIRST_CREV ]]; then
  1152 		        comm=`hg log --rev $FIRST_CREV:tip --follow --template 'rev {rev} : {desc}\n' $logf`
  1153                     else
  1154 		        comm=`hg log -l1 --follow --template 'rev {rev} : {desc}\n' $logf`
  1155                     fi
  1156 	        else
  1157 	            comm=""
  1158 	        fi
  1159 	        if [[ $fmt == "text" ]]; then
  1160 	            print "$comm"
  1161 	            return
  1162 	        fi
  1164 	        print "$comm" | html_quote | bug2url | sac2url
  1166         fi
  1171 # getcomments {text|html} filepath parentpath
  1173 # Fetch the comments depending on what SCM mode we're in.
  1175 getcomments()
  1177 	typeset fmt=$1
  1178 	typeset p=$2
  1179 	typeset pp=$3
  1181 	if [[ -n $wxfile ]]; then
  1182 		comments_from_wx $fmt $p
  1183 	else
  1184 		if [[ $SCM_MODE == "teamware" ]]; then
  1185 			comments_from_teamware $fmt $pp $p
  1186 		elif [[ $SCM_MODE == "mercurial" ]]; then
  1187 			comments_from_mercurial $fmt $pp $p
  1188 		fi
  1189 	fi
  1193 # printCI <total-changed> <inserted> <deleted> <modified> <unchanged>
  1195 # Print out Code Inspection figures similar to sccs-prt(1) format.
  1197 function printCI
  1199 	integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5
  1200 	typeset str
  1201 	if (( tot == 1 )); then
  1202 		str="line"
  1203 	else
  1204 		str="lines"
  1205 	fi
  1206 	printf '%d %s changed: %d ins; %d del; %d mod; %d unchg' \
  1207 	    $tot $str $ins $del $mod $unc
  1212 # difflines <oldfile> <newfile>
  1214 # Calculate and emit number of added, removed, modified and unchanged lines,
  1215 # and total lines changed, the sum of added + removed + modified.
  1217 function difflines
  1219 	integer tot mod del ins unc err
  1220 	typeset filename
  1222 	eval $( diff -e $1 $2 | $AWK '
  1223 	# Change range of lines: N,Nc
  1224 	/^[0-9]*,[0-9]*c$/ {
  1225 		n=split(substr($1,1,length($1)-1), counts, ",");
  1226 		if (n != 2) {
  1227 		    error=2
  1228 		    exit;
  1231 		# 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines.
  1232 		# following would be 5 - 3 = 2! Hence +1 for correction.
  1234 		r=(counts[2]-counts[1])+1;
  1237 		# Now count replacement lines: each represents a change instead
  1238 		# of a delete, so increment c and decrement r.
  1240 		while (getline != /^\.$/) {
  1241 			c++;
  1242 			r--;
  1245 		# If there were more replacement lines than original lines,
  1246 		# then r will be negative; in this case there are no deletions,
  1247 		# but there are r changes that should be counted as adds, and
  1248 		# since r is negative, subtract it from a and add it to c.
  1250 		if (r < 0) {
  1251 			a-=r;
  1252 			c+=r;
  1256 		# If there were more original lines than replacement lines, then
  1257 		# r will be positive; in this case, increment d by that much.
  1259 		if (r > 0) {
  1260 			d+=r;
  1262 		next;
  1265 	# Change lines: Nc
  1266 	/^[0-9].*c$/ {
  1267 		# The first line is a replacement; any more are additions.
  1268 		if (getline != /^\.$/) {
  1269 			c++;
  1270 			while (getline != /^\.$/) a++;
  1272 		next;
  1275 	# Add lines: both Na and N,Na
  1276 	/^[0-9].*a$/ {
  1277 		while (getline != /^\.$/) a++;
  1278 		next;
  1281 	# Delete range of lines: N,Nd
  1282 	/^[0-9]*,[0-9]*d$/ {
  1283 		n=split(substr($1,1,length($1)-1), counts, ",");
  1284 		if (n != 2) {
  1285 			error=2
  1286 			exit;
  1289 		# 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines.
  1290 		# following would be 5 - 3 = 2! Hence +1 for correction.
  1292 		r=(counts[2]-counts[1])+1;
  1293 		d+=r;
  1294 		next;
  1297 	# Delete line: Nd.   For example 10d says line 10 is deleted.
  1298 	/^[0-9]*d$/ {d++; next}
  1300 	# Should not get here!
  1302 		error=1;
  1303 		exit;
  1306 	# Finish off - print results
  1307 	END {
  1308 		printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n",
  1309 		    (c+d+a), c, d, a, error);
  1310 	}' )
  1312 	# End of $AWK, Check to see if any trouble occurred.
  1313 	if (( $? > 0 || err > 0 )); then
  1314 		print "Unexpected Error occurred reading" \
  1315 		    "\`diff -e $1 $2\`: \$?=$?, err=" $err
  1316 		return
  1317 	fi
  1319 	# Accumulate totals
  1320 	(( TOTL += tot ))
  1321 	(( TMOD += mod ))
  1322 	(( TDEL += del ))
  1323 	(( TINS += ins ))
  1324 	# Calculate unchanged lines
  1325 	unc=`wc -l < $1`
  1326 	if (( unc > 0 )); then
  1327 		(( unc -= del + mod ))
  1328 		(( TUNC += unc ))
  1329 	fi
  1330 	# print summary
  1331 	print "<span class=\"lineschanged\">\c"
  1332 	printCI $tot $ins $del $mod $unc
  1333 	print "</span>"
  1338 # flist_from_wx
  1340 # Sets up webrev to source its information from a wx-formatted file.
  1341 # Sets the global 'wxfile' variable.
  1343 function flist_from_wx
  1345 	typeset argfile=$1
  1346 	if [[ -n ${argfile%%/*} ]]; then
  1348 		# If the wx file pathname is relative then make it absolute
  1349 		# because the webrev does a "cd" later on.
  1351 		wxfile=$PWD/$argfile
  1352 	else
  1353 		wxfile=$argfile
  1354 	fi
  1356 	$AWK '{ c = 1; print;
  1357 	  while (getline) {
  1358 		if (NF == 0) { c = -c; continue }
  1359 		if (c > 0) print
  1361 	}' $wxfile > $FLIST
  1363 	print " Done."
  1367 # flist_from_teamware [ <args-to-putback-n> ]
  1369 # Generate the file list by extracting file names from a putback -n.  Some
  1370 # names may come from the "update/create" messages and others from the
  1371 # "currently checked out" warning.  Renames are detected here too.  Extract
  1372 # values for CODEMGR_WS and CODEMGR_PARENT from the output of the putback
  1373 # -n as well, but remove them if they are already defined.
  1375 function flist_from_teamware
  1377 	if [[ -n $codemgr_parent ]]; then
  1378 		if [[ ! -d $codemgr_parent/Codemgr_wsdata ]]; then
  1379 			print -u2 "parent $codemgr_parent doesn't look like a" \
  1380 			    "valid teamware workspace"
  1381 			exit 1
  1382 		fi
  1383 		parent_args="-p $codemgr_parent"
  1384 	fi
  1386 	print " File list from: 'putback -n $parent_args $*' ... \c"
  1388 	putback -n $parent_args $* 2>&1 |
  1389 	    $AWK '
  1390 		/^update:|^create:/	{print $2}
  1391 		/^Parent workspace:/	{printf("CODEMGR_PARENT=%s\n",$3)}
  1392 		/^Child workspace:/	{printf("CODEMGR_WS=%s\n",$3)}
  1393 		/^The following files are currently checked out/ {p = 1; next}
  1394 		NF == 0			{p=0 ; next}
  1395 		/^rename/		{old=$3}
  1396 		$1 == "to:"		{print $2, old}
  1397 		/^"/			{next}
  1398 		p == 1			{print $1}' |
  1399 	    sort -r -k 1,1 -u | sort > $FLIST
  1401 	print " Done."
  1404 function outgoing_from_mercurial_forest
  1406     hg foutgoing --template 'rev: {rev}\n' $OUTPWS | $FILTER | $AWK '
  1407         BEGIN           {ntree=0}
  1408         /^comparing/    {next}
  1409         /^no changes/   {next}
  1410         /^searching/    {next}
  1411 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2);
  1412                          trees[ntree++] = tree;
  1413                          revs[tree]=-1;
  1414                          next}
  1415         /^rev:/   {rev=$2+0;
  1416                    if (revs[tree] == -1 || rev < revs[tree])
  1417                         { revs[tree] = rev; };
  1418                   next;}
  1419         END       {for (tree in trees)
  1420                         { rev=revs[trees[tree]];
  1421                           if (rev > 0)
  1422                                 {printf("%s %d\n",trees[tree],rev-1)}
  1423                         }}' | while read LINE
  1424     do
  1425         set - $LINE
  1426         TREE=$1
  1427         REV=$2
  1428         A=`hg -R $CWS/$TREE log --rev $REV --template '{node}'`
  1429         FSTAT_OPT="--rev $A"
  1430         print "Revision: $A $REV" >> $FLIST
  1431         treestatus $TREE
  1432     done
  1435 function flist_from_mercurial_forest
  1437     rm -f $FLIST
  1438     if [ -z "$Nflag" ]; then
  1439         print " File list from hg foutgoing $PWS ..."
  1440         outgoing_from_mercurial_forest
  1441         HG_LIST_FROM_COMMIT=1
  1442     fi
  1443     if [ ! -f $FLIST ]; then
  1444         # hg commit hasn't been run see what is lying around
  1445         print "\n No outgoing, perhaps you haven't commited."
  1446         print " File list from hg fstatus -mard ...\c"
  1447         FSTAT_OPT=
  1448         fstatus
  1449         HG_LIST_FROM_COMMIT=
  1450     fi
  1451     print " Done."
  1455 # Used when dealing with the result of 'hg foutgoing'
  1456 # When now go down the tree and generate the change list
  1458 function treestatus
  1460     TREE=$1
  1461     HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT"
  1463     $HGCMD -mdn 2>/dev/null | $FILTER | while read F
  1464     do
  1465         echo $TREE/$F
  1466     done >> $FLIST
  1468     # Then all the added files
  1469     # But some of these could have been "moved" or renamed ones or copied ones
  1470     # so let's make sure we get the proper info
  1471     # hg status -aC will produce something like:
  1472     #	A subdir/File3
  1473     #	A subdir/File4
  1474     #	  File4
  1475     #	A subdir/File5
  1476     # The first and last are simple addition while the middle one
  1477     # is a move/rename or a copy.  We can't distinguish from a rename vs a copy
  1478     # without also getting the status of removed files.  The middle case above
  1479     # is a rename if File4 is also shown a being removed.  If File4 is not a 
  1480     # removed file, then the middle case is a copy from File4 to subdir/File4
  1481     # FIXME - we're not distinguishing copy from rename
  1482     $HGCMD -aC | $FILTER | while read LINE; do
  1483 	ldone=""
  1484 	while [ -z "$ldone" ]; do
  1485 	    ldone="1"
  1486 	    set - $LINE
  1487 	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1488 		AFILE=$2
  1489 		if read LINE2; then
  1490 		    set - $LINE2
  1491 		    if [ $# -eq 1 ]; then
  1492 			echo $TREE/$AFILE $TREE/$1 >>$FLIST
  1493 		    elif [ $# -eq 2 ]; then
  1494 			echo $TREE/$AFILE >>$FLIST
  1495 			LINE=$LINE2
  1496 			ldone=""
  1497 		    fi
  1498 		else
  1499 		    echo $TREE/$AFILE >>$FLIST
  1500 		fi
  1501 	    fi
  1502 	done
  1503     done
  1504     $HGCMD -rn | $FILTER | while read RFILE; do
  1505 	grep "$TREE/$RFILE" $FLIST >/dev/null
  1506 	if [ $? -eq 1 ]; then
  1507 	    echo $TREE/$RFILE >>$FLIST
  1508 	fi
  1509     done
  1512 function fstatus
  1515     # forest extension is still being changed. For instance the output
  1516     # of fstatus used to no prepend the tree path to filenames, but
  1517     # this has changed recently. AWK code below does try to handle both
  1518     # cases
  1520     hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1521 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1522 	$1 != ""	{n=index($1,tree);
  1523 			 if (n == 0)
  1524 				{ printf("%s/%s\n",tree,$1)}
  1525 			 else
  1526 				{ printf("%s\n",$1)}}' >> $FLIST
  1529     # There is a bug in the output of fstatus -aC on recent versions: it
  1530     # inserts a space between the name of the tree and the filename of the
  1531     # old file. e.g.:
  1533     # $ hg fstatus -aC
  1534     # [.]
  1536     # [MyWS]
  1537     # A MyWS/subdir/File2
  1538     #  MyWS/ File2
  1540     # [MyWS2]
  1543     hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1544 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1545 	/^A .*/		{n=index($2,tree);
  1546 			 if (n == 0)
  1547 				{ printf("A %s/%s\n",tree,$2)}
  1548 			 else
  1549 				{ printf("A %s\n",$2)};
  1550 			 next}
  1551 	/^ /		{n=index($1,tree);
  1552 			 if (n == 0)
  1553 				{ printf("%s/%s\n",tree,$1)}
  1554 			 else
  1555 				{ if (NF == 2)
  1556 					printf("%s/%s\n",tree,$2)
  1557 				  else
  1558 					printf("%s\n",$1)
  1559 				};
  1560 			 next}
  1561 	' | while read LINE; do
  1562 	ldone=""
  1563 	while [ -z "$ldone" ]; do
  1564 	    ldone="1"
  1565 	    set - $LINE
  1566 	    if [ $# -eq 2 -a "$1" == "A" ]; then
  1567 		AFILE=$2
  1568 		if read LINE2; then
  1569 		    set - $LINE2
  1570 		    if [ $# -eq 1 ]; then
  1571 			echo $AFILE $1 >>$FLIST
  1572 		    elif [ $# -eq 2 ]; then
  1573 			echo $AFILE >>$FLIST
  1574 			LINE=$LINE2
  1575 			ldone=""
  1576 		    fi
  1577 		else
  1578 		    echo $AFILE >>$FLIST
  1579 		fi
  1580 	    fi
  1581 	done
  1582     done
  1583     hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK '
  1584 	/^\[.*\]$/	{tree=substr($1,2,length($1)-2); next}
  1585 	$1 != ""	{n=index($1,tree);
  1586 			 if (n == 0)
  1587 				{ printf("%s/%s\n",tree,$1)}
  1588 			 else
  1589 				{ printf("%s\n",$1)}}' | while read RFILE; do
  1590 	grep "$RFILE" $FLIST >/dev/null
  1591 	if [ $? -eq 1 ]; then
  1592 	    echo $RFILE >>$FLIST
  1593 	fi
  1594     done
  1598 # flist_from_mercurial $PWS
  1600 # Only local file based repositories are supported at present
  1601 # since even though we can determine the list from the parent finding
  1602 # the changes is harder.
  1604 # We first look for any outgoing files, this is for when the user has
  1605 # run hg commit.  If we don't find any then we look with hg status.
  1607 # We need at least one of default-push or default paths set in .hg/hgrc
  1608 # If neither are set we don't know who to compare with.
  1610 function flist_from_mercurial
  1612 #	if [ "${PWS##ssh://}" != "$PWS" -o \
  1613 #	     "${PWS##http://}" != "$PWS" -o \
  1614 #	     "${PWS##https://}" != "$PWS" ]; then
  1615 #		print "Remote Mercurial repositories not currently supported."
  1616 #		print "Set default and/or default-push to a local repository"
  1617 #		exit
  1618 #	fi
  1619     if [[ -n $forestflag ]]; then
  1620         HG_LIST_FROM_COMMIT=
  1621 	flist_from_mercurial_forest
  1622     else
  1623         STATUS_REV=
  1624         if [[ -n $rflag ]]; then
  1625             STATUS_REV="--rev $PARENT_REV"
  1626         elif [[ -n $OUTREV ]]; then
  1627             STATUS_REV="--rev $OUTREV"
  1628         else
  1629             # hg commit hasn't been run see what is lying around
  1630             print "\n No outgoing, perhaps you haven't commited."
  1631         fi
  1632 	# First let's list all the modified or deleted files
  1634 	hg status $STATUS_REV -mdn | $FILTER > $FLIST
  1636 	# Then all the added files
  1637 	# But some of these could have been "moved" or renamed ones
  1638 	# so let's make sure we get the proper info
  1639 	# hg status -aC will produce something like:
  1640 	#	A subdir/File3
  1641 	#	A subdir/File4
  1642 	#	  File4
  1643 	#	A subdir/File5
  1644         # The first and last are simple addition while the middle one
  1645         # is a move/rename or a copy.  We can't distinguish from a rename vs a copy
  1646         # without also getting the status of removed files.  The middle case above
  1647         # is a rename if File4 is also shown a being removed.  If File4 is not a 
  1648         # removed file, then the middle case is a copy from File4 to subdir/File4
  1649         # FIXME - we're not distinguishing copy from rename
  1651 	hg status $STATUS_REV -aC | $FILTER >$FLIST.temp
  1652 	while read LINE; do
  1653 	    ldone=""
  1654 	    while [ -z "$ldone" ]; do
  1655 		ldone="1"
  1656 		set - $LINE
  1657 		if [ $# -eq 2 -a "$1" == "A" ]; then
  1658 		    AFILE=$2
  1659 		    if read LINE2; then
  1660 			set - $LINE2
  1661 			if [ $# -eq 1 ]; then
  1662 			    echo $AFILE $1 >>$FLIST
  1663 			elif [ $# -eq 2 ]; then
  1664 			    echo $AFILE >>$FLIST
  1665 			    LINE=$LINE2
  1666 			    ldone=""
  1667 			fi
  1668 		    else
  1669 			echo $AFILE >>$FLIST
  1670 		    fi
  1671 		fi
  1672 	    done
  1673 	done < $FLIST.temp
  1674 	hg status $STATUS_REV -rn | $FILTER > $FLIST.temp
  1675 	while read RFILE; do
  1676 	    grep "$RFILE" $FLIST >/dev/null
  1677 	    if [ $? -eq 1 ]; then
  1678 		echo $RFILE >>$FLIST
  1679 	    fi
  1680 	done < $FLIST.temp
  1681 	rm -f $FLIST.temp
  1682     fi
  1685 function env_from_flist
  1687 	[[ -r $FLIST ]] || return
  1690 	# Use "eval" to set env variables that are listed in the file
  1691 	# list.  Then copy those into our local versions of those
  1692 	# variables if they have not been set already.
  1694 	eval `sed -e "s/#.*$//" $FLIST | grep = `
  1696 	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
  1699 	# Check to see if CODEMGR_PARENT is set in the flist file.
  1701 	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
  1702 	    codemgr_parent=$CODEMGR_PARENT
  1706 # detect_scm
  1708 # We dynamically test the SCM type; this allows future extensions to
  1709 # new SCM types
  1711 function detect_scm
  1714 	# If CODEMGR_WS is specified in the flist file, we assume teamware.
  1716 	if [[ -r $FLIST ]]; then
  1717 		egrep '^CODEMGR_WS=' $FLIST > /dev/null 2>&1
  1718 		if [[ $? -eq 0 ]]; then
  1719 			print "teamware"
  1720 			return
  1721 		fi
  1722 	fi
  1725 	# The presence of $CODEMGR_WS and a Codemgr_wsdata directory
  1726 	# is our clue that this is a teamware workspace.
  1727 	# Same if true if current directory has a Codemgr_wsdata sub-dir
  1729 	if [[ -z "$CODEMGR_WS" ]]; then
  1730 	    CODEMGR_WS=`workspace name 2>/dev/null`
  1731 	fi
  1733 	if [[ -n $CODEMGR_WS && -d "$CODEMGR_WS/Codemgr_wsdata" ]]; then
  1734 		print "teamware"
  1735 	elif [[ -d $PWD/Codemgr_wsdata ]]; then
  1736 		print "teamware"
  1737 	elif hg root >/dev/null ; then
  1738 		print "mercurial"
  1739 	else
  1740 		print "unknown"
  1741 	fi
  1745 # Extract the parent workspace from the Codemgr_wsdata/parent file
  1747 function parent_from_teamware
  1749     if [[ -f "$1/Codemgr_wsdata/parent" ]]; then
  1750 	tail -1 "$1/Codemgr_wsdata/parent"
  1751     fi
  1754 function look_for_prog
  1756 	typeset path
  1757 	typeset ppath
  1758 	typeset progname=$1
  1760 	DEVTOOLS=
  1761 	OS=`uname`
  1762 	if [[ "$OS" == "SunOS" ]]; then
  1763 	    DEVTOOLS="/java/devtools/`uname -p`/bin"
  1764 	elif [[ "$OS" == "Linux" ]]; then
  1765 	    DEVTOOLS="/java/devtools/linux/bin"
  1766 	fi
  1768 	ppath=$PATH
  1769 	ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin
  1770 	ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin
  1771 	ppath=$ppath:/opt/onbld/bin/`uname -p`
  1772 	ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS
  1774 	PATH=$ppath prog=`whence $progname`
  1775 	if [[ -n $prog ]]; then
  1776 		print $prog
  1777 	fi
  1780 function build_old_new_teamware
  1782 	# If the child's version doesn't exist then
  1783 	# get a readonly copy.
  1785 	if [[ ! -f $F && -f SCCS/s.$F ]]; then
  1786 		$SCCS get -s $F
  1787 	fi
  1790 	# Snag new version of file.
  1792 	rm -f $newdir/$DIR/$F
  1793 	cp $F $newdir/$DIR/$F
  1796 	# Get the parent's version of the file. First see whether the
  1797 	# child's version is checked out and get the parent's version
  1798 	# with keywords expanded or unexpanded as appropriate.
  1800 	if [ -f $PWS/$PDIR/SCCS/s.$PF -o \
  1801 	    -f $PWS/$PDIR/SCCS/p.$PF ]; then
  1802 		rm -f $olddir/$PDIR/$PF
  1803 		if [ -f SCCS/p.$F ]; then
  1804 			$SCCS get -s -p -k $PWS/$PDIR/$PF \
  1805 			    > $olddir/$PDIR/$PF
  1806 		else
  1807 			$SCCS get -s -p    $PWS/$PDIR/$PF \
  1808 			    > $olddir/$PDIR/$PF
  1809 		fi
  1810 	else
  1811 		if [[ -f $PWS/$PDIR/$PF ]]; then
  1812 			# Parent is not a real workspace, but just a raw
  1813 			# directory tree - use the file that's there as
  1814 			# the old file.
  1816 			rm -f $olddir/$DIR/$F
  1817 			cp $PWS/$PDIR/$PF $olddir/$DIR/$F
  1818 		fi
  1819 	fi
  1823 # Find the parent for $1
  1825 function find_outrev
  1827     crev=$1
  1828     prev=`hg log -r $crev --template '{parents}\n'`
  1829     if [[ -z "$prev" ]]
  1830     then
  1831 	# No specific parent means previous changeset is parent
  1832 	prev=`expr $crev - 1`
  1833     else
  1834 	# Format is either of the following two:
  1835 	# 546:7df6fcf1183b
  1836 	# 548:16f1915bb5cd 547:ffaa4e775815
  1837 	prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'`
  1838     fi
  1839     print $prev
  1842 function extract_ssh_infos
  1844     CMD=$1
  1845     if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then
  1846 	ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'`
  1847 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'`
  1848 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'`
  1849     else
  1850 	ssh_user=
  1851 	ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'`
  1852 	ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'`
  1853     fi
  1857 function build_old_new_mercurial
  1859 	olddir=$1
  1860 	newdir=$2
  1861 	DIR=$3
  1862 	F=$4
  1864 	# new version of the file.
  1866 	rm -rf $newdir/$DIR/$F
  1867 	if [ -f $F ]; then
  1868 	    cp $F  $newdir/$DIR/$F
  1869 	fi
  1872 	# Old version of the file.
  1874 	rm -rf $olddir/$DIR/$F
  1876 	if [ -n "$PWS" ]; then
  1877 	    if expr "$PWS" : 'ssh://' >/dev/null
  1878 	    then
  1879 		extract_ssh_infos $PWS
  1880 		if [ -n "$ssh_user" ]; then
  1881 		    parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1882 		else
  1883 		    parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir"
  1884 		fi
  1885 	    else
  1886 		parent="hg -R $PWS --cwd $PWS"
  1887 	    fi
  1888 	else
  1889 	    parent=""
  1890 	fi
  1892 	if [ -z "$rename" ]; then
  1893 	    if [ -n "$rflag" ]; then
  1894 		parentrev=$PARENT_REV
  1895 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1896                 parentrev=$OUTREV
  1897 	    else
  1898                 if [[ -n $HG_BRANCH ]]; then
  1899                     parentrev=$HG_BRANCH
  1900                 else
  1901 		    parentrev="tip"
  1902                 fi
  1903 	    fi
  1905 	    if [ -n "$parentrev" ]; then
  1906 		if [ -z "$parent" ]; then
  1907 		    hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null
  1908 		else
  1909 		    # when specifying a workspace we have to provide
  1910 		    # the full path
  1911 		    $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null
  1912 		fi
  1913 	    fi
  1914 	else
  1915 	    # It's a rename (or a move), or a copy, so let's make sure we move
  1916 	    # to the right directory first, then restore it once done
  1917 	    current_dir=`pwd`
  1918 	    cd $CWS/$PDIR
  1919 	    if [ -n "$rflag" ]; then
  1920 		parentrev=$PARENT_REV
  1921 	    elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then
  1922                 parentrev=$OUTREV
  1923 	    fi
  1924 	    if [ -z "$parentrev" ]; then
  1925 		parentrev=`hg log -l1 $PF | $AWK -F: '/changeset/ {print $2}'`
  1926 	    fi
  1927 	    if [ -n "$parentrev" ]; then
  1928 		mkdir -p $olddir/$PDIR
  1929 		if [ -z "$parent" ]; then
  1930 		    hg cat --rev $parentrev --output $olddir/$PDIR/$PF $PF 2>/dev/null
  1931 		else
  1932 		    $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null
  1933 		fi
  1934 	    fi
  1935 	    cd $current_dir
  1936 	fi
  1939 function build_old_new
  1941 	if [[ $SCM_MODE == "teamware" ]]; then
  1942 		build_old_new_teamware $@
  1943 	fi
  1945 	if [[ $SCM_MODE == "mercurial" ]]; then
  1946 		build_old_new_mercurial $@
  1947 	fi
  1952 # Usage message.
  1954 function usage
  1956 	print "Usage:\twebrev [common-options]
  1957 	webrev [common-options] ( <file> | - )
  1958 	webrev [common-options] -w <wx file>
  1959 	webrev [common-options] -l [arguments to 'putback']
  1961 Options:
  1962 	-v: Print the version of this tool.
  1963         -b: Do not ignore changes in the amount of white space.
  1964         -c <CR#>: Include link to CR (aka bugid) in the main page.
  1965 	-O: Print bugids/arc cases suitable for OpenJDK.
  1966 	-i <filename>: Include <filename> in the index.html file.
  1967 	-o <outdir>: Output webrev to specified directory.
  1968 	-p <compare-against>: Use specified parent wkspc or basis for comparison
  1969 	-w <wxfile>: Use specified wx active file.
  1970         -u <username>: Use that username instead of 'guessing' one.
  1971 	-m: Forces the use of Mercurial
  1972 	-t: Forces the use of Teamware
  1974 Mercurial only options:
  1975 	-r rev: Compare against a specified revision
  1976 	-N: Skip 'hg outgoing', use only 'hg status'
  1977 	-f: Use the forest extension
  1979 Environment:
  1980 	WDIR: Control the output directory.
  1981 	WEBREV_BUGURL: Control the URL prefix for bugids.
  1982 	WEBREV_SACURL: Control the URL prefix for ARC cases.
  1984 SCM Environment:
  1985 	Teamware: CODEMGR_WS: Workspace location.
  1986 	Teamware: CODEMGR_PARENT: Parent workspace location.
  1990 	exit 2
  1995 # Main program starts here
  1998 LANG="C"
  1999 LC_ALL="C"
  2000 export LANG LC_ALL
  2001 trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15
  2003 set +o noclobber
  2005 [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff`
  2006 [[ -z $WX ]] && WX=`look_for_prog wx`
  2007 [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview`
  2008 [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf`
  2009 [[ -z $PERL ]] && PERL=`look_for_prog perl`
  2010 [[ -z $SCCS ]] && SCCS=`look_for_prog sccs`
  2011 [[ -z $AWK ]] && AWK=`look_for_prog nawk`
  2012 [[ -z $AWK ]] && AWK=`look_for_prog gawk`
  2013 [[ -z $AWK ]] && AWK=`look_for_prog awk`
  2014 [[ -z $WSPACE ]] && WSPACE=`look_for_prog workspace`
  2015 [[ -z $JAR ]] && JAR=`look_for_prog jar`
  2016 [[ -z $ZIP ]] && ZIP=`look_for_prog zip`
  2017 [[ -z $GETENT ]] && GETENT=`look_for_prog getent`
  2018 [[ -z $WGET ]] && WGET=`look_for_prog wget`
  2020 if uname | grep CYGWIN >/dev/null
  2021 then
  2022         ISWIN=1
  2023         # Under windows mercurial outputs '\' instead of '/'
  2024         FILTER="tr '\\\\' '/'"
  2025 else
  2026         FILTER="cat"
  2027 fi
  2029 if [[ ! -x $PERL ]]; then
  2030 	print -u2 "Error: No perl interpreter found.  Exiting."
  2031 	exit 1
  2032 fi
  2035 # These aren't fatal, but we want to note them to the user.
  2036 # We don't warn on the absence of 'wx' until later when we've
  2037 # determined that we actually need to try to invoke it.
  2039 # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found."
  2040 # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found."
  2041 # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found."
  2043 # Declare global total counters.
  2044 integer TOTL TINS TDEL TMOD TUNC
  2046 flist_mode=
  2047 flist_file=
  2048 bflag=
  2049 iflag=
  2050 oflag=
  2051 pflag=
  2052 uflag=
  2053 lflag=
  2054 wflag=
  2055 Oflag=
  2056 rflag=
  2057 Nflag=
  2058 forestflag=
  2059 while getopts "c:i:o:p:r:u:lmtwONvfb" opt
  2060 do
  2061 	case $opt in
  2062         b)      bflag=1;;
  2064 	i)	iflag=1
  2065 		INCLUDE_FILE=$OPTARG;;
  2067 	o)	oflag=1
  2068 		WDIR=$OPTARG;;
  2070 	p)	pflag=1
  2071 		codemgr_parent=$OPTARG;;
  2073 	u)      uflag=1
  2074 		username=$OPTARG;;
  2076         c)      if [[ -z $CRID ]]; then
  2077                    CRID=$OPTARG
  2078                 else
  2079                    CRID="$CRID $OPTARG"
  2080                 fi;;
  2082 	m)	SCM_MODE="mercurial";;
  2084 	t)	SCM_MODE="teamware";;
  2087 	# If -l has been specified, we need to abort further options
  2088 	# processing, because subsequent arguments are going to be
  2089 	# arguments to 'putback -n'.
  2091 	l)	lflag=1
  2092 		break;;
  2094 	w)	wflag=1;;
  2096 	O)	Oflag=1;;
  2098 	N)	Nflag=1;;
  2100 	f)	forestflag=1;;
  2102 	r)	rflag=1
  2103 		PARENT_REV=$OPTARG;;
  2105 	v)	print "$0 version: $WEBREV_UPDATED";;
  2108 	?)	usage;;
  2109 	esac
  2110 done
  2112 FLIST=/tmp/$$.flist
  2113 HG_LIST_FROM_COMMIT=
  2115 if [[ -n $wflag && -n $lflag ]]; then
  2116 	usage
  2117 fi
  2119 if [[ -n $forestflag && -n $rflag ]]; then
  2120     print "The -r <rev> flag is incompatible with the use of forests"
  2121     exit 2
  2122 fi
  2125 # If this manually set as the parent, and it appears to be an earlier webrev,
  2126 # then note that fact and set the parent to the raw_files/new subdirectory.
  2128 if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then
  2129 	parent_webrev="$codemgr_parent"
  2130 	codemgr_parent="$codemgr_parent/raw_files/new"
  2131 fi
  2133 if [[ -z $wflag && -z $lflag ]]; then
  2134 	shift $(($OPTIND - 1))
  2136 	if [[ $1 == "-" ]]; then
  2137 		cat > $FLIST
  2138 		flist_mode="stdin"
  2139 		flist_done=1
  2140 		shift
  2141 	elif [[ -n $1 ]]; then
  2142 		if [[ ! -r $1 ]]; then
  2143 			print -u2 "$1: no such file or not readable"
  2144 			usage
  2145 		fi
  2146 		cat $1 > $FLIST
  2147 		flist_mode="file"
  2148 		flist_file=$1
  2149 		flist_done=1
  2150 		shift
  2151 	else
  2152 		flist_mode="auto"
  2153 	fi
  2154 fi
  2157 # Before we go on to further consider -l and -w, work out which SCM we think
  2158 # is in use.
  2160 if [[ -z $SCM_MODE ]]; then
  2161     SCM_MODE=`detect_scm $FLIST`
  2162 fi
  2163 if [[ $SCM_MODE == "unknown" ]]; then
  2164 	print -u2 "Unable to determine SCM type currently in use."
  2165 	print -u2 "For teamware: webrev looks for \$CODEMGR_WS either in"
  2166 	print -u2 "              the environment or in the file list."
  2167 	print -u2 "For mercurial: webrev runs 'hg root'."
  2168 	exit 1
  2169 fi
  2171 print -u2 "   SCM detected: $SCM_MODE"
  2174 if [[ $SCM_MODE == "mercurial" ]]; then
  2176     # determine Workspace and parent workspace paths
  2178     CWS=`hg root | $FILTER`
  2179     if [[ -n $pflag && -z "$PWS" ]]; then
  2180 	OUTPWS=$codemgr_parent
  2181         # Let's try to expand it if it's an alias defined in [paths]
  2182         tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  2183         if [[ -n $tmp ]]; then
  2184             OUTPWS="$tmp"
  2185         fi
  2186         if [[ -n $rflag ]]; then
  2187 	    if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then
  2188 	        PWS=$codemgr_parent
  2189 	    else
  2190 	        PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER`
  2191 	    fi
  2192         fi
  2193     fi
  2195     # OUTPWS is the parent repository to use when using 'hg outgoing'
  2197     if [[ -z $Nflag ]]; then
  2198         if [[ -n $forestflag ]]; then
  2200             # for forest we have to rely on properly set default and
  2201             # default-push because they can be different from the top one.
  2202             # unless of course it was explicitely speficied with -p
  2203             if [[ -z $pflag ]]; then
  2204                 OUTPWS=
  2205             fi
  2206         else
  2208             # Unfortunately mercurial is bugged and doesn't handle
  2209             # aliases correctly in 'hg path default'
  2210             # So let's do it ourselves. Sigh...
  2211             if [[ -z "$OUTPWS" ]]; then
  2212                 OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  2213             fi
  2214             # Still empty, means no default-push
  2215             if [[ -z "$OUTPWS" ]]; then
  2216                 OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER`
  2217             fi
  2218             # Let's try to expand it if it's an alias defined in [paths]
  2219             tmp=`hg path $OUTPWS 2>/dev/null | $FILTER`
  2220             if [[ -n $tmp ]]; then
  2221                 OUTPWS="$tmp"
  2222             fi
  2223         fi
  2224     fi
  2226     # OUTPWS may contain username:password, let's make sure we remove the
  2227     # sensitive information before we print out anything in the HTML
  2229     OUTPWS2=$OUTPWS
  2230     if [[ -n $OUTPWS ]]; then
  2231 	if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then
  2232 	    # Remove everything between '://' and '@'
  2233 	    OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'`
  2234 	fi
  2235     fi
  2237     if [[ -z $HG_BRANCH ]]; then
  2238         HG_BRANCH=`hg branch`
  2239         if [ "$HG_BRANCH" == "default" ]; then
  2241             # 'default' means no particular branch, so let's cancel that
  2243             HG_BRANCH=
  2244         fi
  2245     fi
  2247     if [[ -z $forestflag ]]; then
  2248         if [[ -z $Nflag ]]; then
  2250             # If no "-N", always do "hg outgoing" against parent
  2251             # repository to determine list of outgoing revisions.
  2253             ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n`
  2254             if [[ -n $ALL_CREV ]]; then
  2255                 FIRST_CREV=`echo "$ALL_CREV" | head -1`
  2257                 # If no "-r", choose revision to compare against by
  2258                 # finding the latest revision not in the outgoing list.
  2260                 if [[ -z $rflag ]]; then
  2261                     OUTREV=`find_outrev "$FIRST_CREV"`
  2262                     if [[ -n $OUTREV ]]; then
  2263                         HG_LIST_FROM_COMMIT=1
  2264                     fi
  2265                 fi
  2266             fi
  2267         elif [[ -n $rflag ]]; then
  2269             # If skipping "hg outgoing" but still comparing against a
  2270             # specific revision (not the tip), set revision for comment
  2271             # accumulation.
  2273             FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'`
  2274             FIRST_CREV=`expr $FIRST_CREV + 1`
  2275         fi
  2276     fi
  2277     #Let's check if a merge is needed, if so, issue a warning
  2278     PREV=`hg parent | grep '^tag:.*tip$'`
  2279     if [[ -z $PREV ]]; then
  2280         print "WARNING: parent rev is not tip. Maybe an update or merge is needed"
  2281     fi
  2282 fi
  2284 if [[ -n $lflag ]]; then
  2286 	# If the -l flag is given instead of the name of a file list,
  2287 	# then generate the file list by extracting file names from a
  2288 	# putback -n.
  2290 	shift $(($OPTIND - 1))
  2291 	if [[ $SCM_MODE == "teamware" ]]; then
  2292 		flist_from_teamware "$*"
  2293 	elif [[ $SCM_MODE == "mercurial" ]]; then
  2294 		flist_from_mercurial
  2295 	fi
  2296 	flist_done=1
  2297 	shift $#
  2299 elif [[ -n $wflag ]]; then
  2301 	# If the -w is given then assume the file list is in Bonwick's "wx"
  2302 	# command format, i.e.  pathname lines alternating with SCCS comment
  2303 	# lines with blank lines as separators.  Use the SCCS comments later
  2304 	# in building the index.html file.
  2306 	shift $(($OPTIND - 1))
  2307 	wxfile=$1
  2308 	if [[ -z $wxfile && -n $CODEMGR_WS ]]; then
  2309 		if [[ -r $CODEMGR_WS/wx/active ]]; then
  2310 			wxfile=$CODEMGR_WS/wx/active
  2311 		fi
  2312 	fi
  2314 	[[ -z $wxfile ]] && print -u2 "wx file not specified, and could not " \
  2315 	    "be auto-detected (check \$CODEMGR_WS)" && exit 1
  2317 	print -u2 " File list from: wx 'active' file '$wxfile' ... \c"
  2318 	flist_from_wx $wxfile
  2319 	flist_done=1
  2320 	if [[ -n "$*" ]]; then
  2321 		shift
  2322 	fi
  2323 elif [[ $flist_mode == "stdin" ]]; then
  2324 	print -u2 " File list from: standard input"
  2325 elif [[ $flist_mode == "file" ]]; then
  2326 	print -u2 " File list from: $flist_file"
  2327 fi
  2329 if [[ $# -gt 0 ]]; then
  2330 	print -u2 "WARNING: unused arguments: $*"
  2331 fi
  2333 if [[ $SCM_MODE == "teamware" ]]; then
  2335 	# Parent (internally $codemgr_parent) and workspace ($codemgr_ws) can
  2336 	# be set in a number of ways, in decreasing precedence:
  2338 	#      1) on the command line (only for the parent)
  2339 	#      2) in the user environment
  2340 	#      3) in the flist
  2341 	#      4) automatically based on the workspace (only for the parent)
  2345 	# Here is case (2): the user environment
  2347 	[[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS
  2348 	[[ -z $codemgr_ws && -n $WSPACE ]] && codemgr_ws=`$WSPACE name`
  2350 	if [[ -n $codemgr_ws && ! -d $codemgr_ws ]]; then
  2351 		print -u2 "$codemgr_ws: no such workspace"
  2352 		exit 1
  2353 	fi
  2355 	[[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \
  2356 	    codemgr_parent=$CODEMGR_PARENT
  2358 	if [[ -n $codemgr_parent && ! -d $codemgr_parent ]]; then
  2359 		print -u2 "$codemgr_parent: no such directory"
  2360 		exit 1
  2361 	fi
  2364 	# If we're in auto-detect mode and we haven't already gotten the file
  2365 	# list, then see if we can get it by probing for wx.
  2367 	if [[ -z $flist_done && $flist_mode == "auto" && -n $codemgr_ws ]]; then
  2368 		if [[ ! -x $WX ]]; then
  2369 			print -u2 "WARNING: wx not found!"
  2370 		fi
  2373 		# We need to use wx list -w so that we get renamed files, etc.
  2374 		# but only if a wx active file exists-- otherwise wx will
  2375 		# hang asking us to initialize our wx information.
  2377 		if [[ -x $WX && -f $codemgr_ws/wx/active ]]; then
  2378 			print -u2 " File list from: 'wx list -w' ... \c"
  2379 			$WX list -w > $FLIST
  2380 			$WX comments > /tmp/$$.wx_comments
  2381 			wxfile=/tmp/$$.wx_comments
  2382 			print -u2 "done"
  2383 			flist_done=1
  2384 		fi
  2385 	fi
  2388 	# If by hook or by crook we've gotten a file list by now (perhaps
  2389 	# from the command line), eval it to extract environment variables from
  2390 	# it: This is step (3).
  2392 	env_from_flist
  2395 	# Continuing step (3): If we still have no file list, we'll try to get
  2396 	# it from teamware.
  2398 	if [[ -z $flist_done ]]; then
  2399 		flist_from_teamware
  2400 		env_from_flist
  2401 	fi
  2403 	if [[ -z $codemgr_ws && -d $PWD/Codemgr_wsdata ]]; then
  2404 	    codemgr_ws=$PWD
  2405 	fi
  2407 	# Observe true directory name of CODEMGR_WS, as used later in
  2408 	# webrev title.
  2410 	if [[ -n $codemgr_ws ]]; then
  2411 	    codemgr_ws=$(cd $codemgr_ws;print $PWD)
  2412 	fi
  2414 	if [[ -n $codemgr_parent ]]; then
  2415 	    codemgr_parent=$(cd $codemgr_parent;print $PWD)
  2416 	fi
  2419 	# (4) If we still don't have a value for codemgr_parent, get it
  2420 	# from workspace.
  2422 	[[ -z $codemgr_parent && -n $WSPACE ]] && codemgr_parent=`$WSPACE parent`
  2423 	[[ -z $codemgr_parent ]] && codemgr_parent=`parent_from_teamware $codemgr_ws`
  2425 	if [[ ! -d $codemgr_parent ]]; then
  2426 	    print -u2 "$CODEMGR_PARENT: no such parent workspace"
  2427 	    exit 1
  2428 	fi
  2431 	# Reset CODEMGR_WS to make sure teamware commands are happy.
  2433 	CODEMGR_WS=$codemgr_ws
  2434 	CWS=$codemgr_ws
  2435 	PWS=$codemgr_parent
  2436 elif [[ $SCM_MODE == "mercurial" ]]; then
  2437     if [[ -z $flist_done ]]; then
  2438 	flist_from_mercurial $PWS
  2439     fi
  2440 fi
  2443 # If the user didn't specify a -i option, check to see if there is a
  2444 # webrev-info file in the workspace directory.
  2446 if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then
  2447 	iflag=1
  2448 	INCLUDE_FILE="$CWS/webrev-info"
  2449 fi
  2451 if [[ -n $iflag ]]; then
  2452 	if [[ ! -r $INCLUDE_FILE ]]; then
  2453 		print -u2 "include file '$INCLUDE_FILE' does not exist or is" \
  2454 		    "not readable."
  2455 		exit 1
  2456 	else
  2458 		# $INCLUDE_FILE may be a relative path, and the script alters
  2459 		# PWD, so we just stash a copy in /tmp.
  2461 		cp $INCLUDE_FILE /tmp/$$.include
  2462 	fi
  2463 fi
  2466 # Output directory.
  2468 if [[ -z $WDIR ]]; then
  2469     WDIR=$CWS/webrev
  2470 else
  2471     # If the output directory doesn't end with '/webrev' or '/webrev/'
  2472     # then add '/webrev'. This is for backward compatibility
  2473     if ! expr $WDIR : '.*/webrev/\?$' >/dev/null
  2474     then
  2475 	WDIR=$WDIR/webrev
  2476     fi
  2477 fi
  2478 # WDIR=${WDIR:-$CWS/webrev}
  2481 # Name of the webrev, derived from the workspace name; in the
  2482 # future this could potentially be an option.
  2484 # Let's keep what's after the last '/'
  2485 WNAME=${CWS##*/}
  2488 # If WDIR doesn't start with '/' or 'x:' prepend the current dir
  2490 if [ ${WDIR%%/*} ]; then
  2491     if [[ -n $ISWIN ]]; then
  2492         if [ ${WDIR%%[A-Za-z]:*} ]; then
  2493 	    WDIR=$PWD/$WDIR
  2494         fi
  2495     else
  2496 	WDIR=$PWD/$WDIR
  2497     fi
  2498 fi
  2500 if [[ ! -d $WDIR ]]; then
  2501 	mkdir -p $WDIR
  2502 	[[ $? != 0 ]] && exit 1
  2503 fi
  2506 # Summarize what we're going to do.
  2508 print "      Workspace: $CWS"
  2509 if [[ -n $parent_webrev ]]; then
  2510     print "Compare against: webrev at $parent_webrev"
  2511 elif [[ -n $OUTPWS2 ]]; then
  2512     print "Compare against: $OUTPWS2"
  2513 fi
  2514 if [[ -n $HG_BRANCH ]]; then
  2515     print "         Branch: $HG_BRANCH"
  2516 fi
  2517 if [[ -n $rflag ]]; then
  2518         print "Compare against version: $PARENT_REV"
  2519 fi
  2520 [[ -n $INCLUDE_FILE ]] && print "      Including: $INCLUDE_FILE"
  2521 print "      Output to: $WDIR"
  2524 # Save the file list in the webrev dir
  2526 [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list
  2529 #    Bug IDs will be replaced by a URL.  Order of precedence
  2530 #    is: default location, $WEBREV_BUGURL, the -O flag.
  2532 BUGURL='https://jbs.oracle.com/bugs/browse/'
  2533 [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL"
  2534 if [[ -n "$Oflag" ]]; then
  2535     CRID=`echo $CRID | sed -e 's/JDK-//'`
  2536     BUGURL='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id='
  2537     IDPREFIX=''
  2538 else
  2539     IDPREFIX='JDK-'
  2540 fi
  2544 #    Likewise, ARC cases will be replaced by a URL.  Order of precedence
  2545 #    is: default, $WEBREV_SACURL, the -O flag.
  2547 #    Note that -O also triggers different substitution behavior for
  2548 #    SACURL.  See sac2url().
  2550 SACURL='http://sac.eng.sun.com'
  2551 [[ -n $WEBREV_SACURL ]] && SACURL="$WEBREV_SACURL"
  2552 [[ -n $Oflag ]] && \
  2553     SACURL='http://www.opensolaris.org/os/community/arc/caselog'
  2555 rm -f $WDIR/$WNAME.patch
  2556 rm -f $WDIR/$WNAME.changeset
  2557 rm -f $WDIR/$WNAME.ps
  2558 rm -f $WDIR/$WNAME.pdf
  2560 touch $WDIR/$WNAME.patch
  2562 print "   Output Files:"
  2565 # Clean up the file list: Remove comments, blank lines and env variables.
  2567 sed -e "s/#.*$//" -e "/=/d" -e "/^[   ]*$/d" $FLIST > /tmp/$$.flist.clean
  2568 FLIST=/tmp/$$.flist.clean
  2571 # Clean up residual raw files
  2573 if [ -d $WDIR/raw_files ]; then
  2574     rm -rf $WDIR/raw_files 2>/dev/null
  2575 fi
  2578 # Should we ignore changes in white spaces when generating diffs?
  2580 if [[ -n $bflag ]]; then
  2581     DIFFOPTS="-t"
  2582 else
  2583     DIFFOPTS="-bt"
  2584 fi
  2586 # First pass through the files: generate the per-file webrev HTML-files.
  2588 while read LINE
  2589 do
  2590 	set - $LINE
  2591 	P=$1
  2593         if [[ $1 == "Revision:" ]]; then
  2594             OUTREV=$2
  2595             continue
  2596         fi
  2598 	# Normally, each line in the file list is just a pathname of a
  2599 	# file that has been modified or created in the child.  A file
  2600 	# that is renamed in the child workspace has two names on the
  2601 	# line: new name followed by the old name.
  2603 	oldname=""
  2604 	oldpath=""
  2605 	rename=
  2606 	if [[ $# -eq 2 ]]; then
  2607 		PP=$2			# old filename
  2608 		oldname=" (was $PP)"
  2609 		oldpath="$PP"
  2610 		rename=1
  2611         	PDIR=${PP%/*}
  2612         	if [[ $PDIR == $PP ]]; then
  2613 			PDIR="."   # File at root of workspace
  2614 		fi
  2616 		PF=${PP##*/}
  2618 	        DIR=${P%/*}
  2619 	        if [[ $DIR == $P ]]; then
  2620 			DIR="."   # File at root of workspace
  2621 		fi
  2623 		F=${P##*/}
  2624         else
  2625 	        DIR=${P%/*}
  2626 	        if [[ "$DIR" == "$P" ]]; then
  2627 			DIR="."   # File at root of workspace
  2628 		fi
  2630 		F=${P##*/}
  2632 		PP=$P
  2633 		PDIR=$DIR
  2634 		PF=$F
  2635 	fi
  2637         # Make the webrev directory if necessary as it may have been
  2638         # removed because it was empty
  2639         if [ ! -d $CWS/$DIR ]; then
  2640 	    mkdir -p $CWS/$DIR
  2641         fi
  2643 	COMM=`getcomments html $P $PP`
  2645 	print "\t$P$oldname\n\t\t\c"
  2647 	# Make the webrev mirror directory if necessary
  2648 	mkdir -p $WDIR/$DIR
  2650 	# cd to the directory so the names are short
  2651 	cd $CWS/$DIR
  2654 	# If we're in OpenSolaris mode, we enforce a minor policy:
  2655 	# help to make sure the reviewer doesn't accidentally publish
  2656 	# source which is in usr/closed/*
  2658 	if [[ -n $Oflag ]]; then
  2659 		pclosed=${P##usr/closed/}
  2660 		if [[ $pclosed != $P ]]; then
  2661 			print "*** Omitting closed source for OpenSolaris" \
  2662 			    "mode review"
  2663 			continue
  2664 		fi
  2665 	fi
  2668 	# We stash old and new files into parallel directories in /tmp
  2669 	# and do our diffs there.  This makes it possible to generate
  2670 	# clean looking diffs which don't have absolute paths present.
  2672 	olddir=$WDIR/raw_files/old
  2673 	newdir=$WDIR/raw_files/new
  2674 	mkdir -p $olddir
  2675 	mkdir -p $newdir
  2676 	mkdir -p $olddir/$PDIR
  2677 	mkdir -p $newdir/$DIR
  2679 	build_old_new $olddir $newdir $DIR $F
  2681 	if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then
  2682 		print "*** Error: file not in parent or child"
  2683 		continue
  2684 	fi
  2686 	cd $WDIR/raw_files
  2687 	ofile=old/$PDIR/$PF
  2688 	nfile=new/$DIR/$F
  2690 	mv_but_nodiff=
  2691 	cmp $ofile $nfile > /dev/null 2>&1
  2692 	if [[ $? == 0 && $rename == 1 ]]; then
  2693 		mv_but_nodiff=1
  2694 	fi
  2697         # Cleaning up
  2699         rm -f $WDIR/$DIR/$F.cdiff.html
  2700         rm -f $WDIR/$DIR/$F.udiff.html
  2701         rm -f $WDIR/$DIR/$F.wdiff.html
  2702         rm -f $WDIR/$DIR/$F.sdiff.html
  2703         rm -f $WDIR/$DIR/$F-.html
  2704         rm -f $WDIR/$DIR/$F.html
  2706 	its_a_jar=
  2707 	if expr $F : '.*\.jar' >/dev/null; then
  2708 	    its_a_jar=1
  2709 	    # It's a JAR file, let's do it differntly
  2710 	    if [[ -z $JAR ]]; then
  2711 		print "No access to jar, so can't produce diffs for jar files"
  2712 	    else
  2713 		if [ -f $ofile ]; then
  2714 		    $JAR -tvf $ofile >"$ofile".lst
  2715 		fi
  2716 		if [ -f $nfile ]; then
  2717 		    $JAR -tvf $nfile >"$nfile".lst
  2718 		fi
  2720 		if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2722 		    ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff
  2723 		    diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2724 			> $WDIR/$DIR/$F.cdiff.html
  2725 		    print " cdiffs\c"
  2727 		    ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff
  2728 		    diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2729 			> $WDIR/$DIR/$F.udiff.html
  2731 		    print " udiffs\c"
  2733 		    if [[ -x $WDIFF ]]; then
  2734 			$WDIFF -c "$COMM" \
  2735 			    -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \
  2736 			    $WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2737 			if [[ $? -eq 0 ]]; then
  2738 			    print " wdiffs\c"
  2739 			else
  2740 			    print " wdiffs[fail]\c"
  2741 			fi
  2742 		    fi
  2744 		    sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2745 			> $WDIR/$DIR/$F.sdiff.html
  2746 		    print " sdiffs\c"
  2748 		    print " frames\c"
  2750 		    rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2752 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2754 		elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2755 		# renamed file: may also have differences
  2756 		    difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count
  2757 		elif [[ -f $nfile ]]; then
  2758 		# new file: count added lines
  2759 		    difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count
  2760 		elif [[ -f $ofile ]]; then
  2761 		# old file: count deleted lines
  2762 		    difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count
  2763 		fi
  2764 	    fi
  2765 	else
  2768 	    # If we have old and new versions of the file then run the
  2769 	    # appropriate diffs.  This is complicated by a couple of factors:
  2771 	    #	- renames must be handled specially: we emit a 'remove'
  2772 	    #	  diff and an 'add' diff
  2773 	    #	- new files and deleted files must be handled specially
  2774 	    #	- Solaris patch(1m) can't cope with file creation
  2775 	    #	  (and hence renames) as of this writing.
  2776 	    #   - To make matters worse, gnu patch doesn't interpret the
  2777 	    #	  output of Solaris diff properly when it comes to
  2778 	    #	  adds and deletes.  We need to do some "cleansing"
  2779 	    #     transformations:
  2780 	    # 	    [to add a file] @@ -1,0 +X,Y @@  -->  @@ -0,0 +X,Y @@
  2781 	    #	    [to del a file] @@ -X,Y +1,0 @@  -->  @@ -X,Y +0,0 @@
  2783 	    cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'"
  2784 	    cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'"
  2786             if [[ ! "$HG_LIST_FROM_COMMIT" -eq 1 || ! $flist_mode == "auto" ]];
  2787             then
  2788               # Only need to generate a patch file here if there are no commits in outgoing
  2789               # or if we've specified a file list
  2790               rm -f $WDIR/$DIR/$F.patch
  2791               if [[ -z $rename ]]; then
  2792                   if [ ! -f $ofile ]; then
  2793                       diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2794                           > $WDIR/$DIR/$F.patch
  2795                   elif [ ! -f $nfile ]; then
  2796                       diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2797                           > $WDIR/$DIR/$F.patch
  2798                   else
  2799                       diff -u $ofile $nfile > $WDIR/$DIR/$F.patch
  2800                   fi
  2801               else
  2802                   diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \
  2803                       > $WDIR/$DIR/$F.patch
  2805                   diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \
  2806                       >> $WDIR/$DIR/$F.patch
  2808               fi
  2812             # Tack the patch we just made onto the accumulated patch for the
  2813             # whole wad.
  2815               cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch
  2816             fi
  2818             print " patch\c"
  2820 	    if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then
  2822 		${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff
  2823 		diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \
  2824 		    > $WDIR/$DIR/$F.cdiff.html
  2825 		print " cdiffs\c"
  2827 		${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff
  2828 		diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \
  2829 		    > $WDIR/$DIR/$F.udiff.html
  2831 		print " udiffs\c"
  2833 		if [[ -x $WDIFF ]]; then
  2834 		    $WDIFF -c "$COMM" \
  2835 			-t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \
  2836 			$WDIR/$DIR/$F.wdiff.html 2>/dev/null
  2837 		    if [[ $? -eq 0 ]]; then
  2838 			print " wdiffs\c"
  2839 		    else
  2840 			print " wdiffs[fail]\c"
  2841 		    fi
  2842 		fi
  2844 		sdiff_to_html $ofile $nfile $F $DIR "$COMM" \
  2845 		    > $WDIR/$DIR/$F.sdiff.html
  2846 		print " sdiffs\c"
  2848 		print " frames\c"
  2850 		rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff
  2852 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2854 	    elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then
  2855 		# renamed file: may also have differences
  2856 		difflines $ofile $nfile > $WDIR/$DIR/$F.count
  2857 	    elif [[ -f $nfile ]]; then
  2858 		# new file: count added lines
  2859 		difflines /dev/null $nfile > $WDIR/$DIR/$F.count
  2860 	    elif [[ -f $ofile ]]; then
  2861 		# old file: count deleted lines
  2862 		difflines $ofile /dev/null > $WDIR/$DIR/$F.count
  2863 	    fi
  2864 	fi
  2866 	# Now we generate the postscript for this file.  We generate diffs
  2867 	# only in the event that there is delta, or the file is new (it seems
  2868 	# tree-killing to print out the contents of deleted files).
  2870 	if [[ -f $nfile ]]; then
  2871 		ocr=$ofile
  2872 		[[ ! -f $ofile ]] && ocr=/dev/null
  2874 		if [[ -z $mv_but_nodiff ]]; then
  2875 			textcomm=`getcomments text $P $PP`
  2876 			if [[ -x $CODEREVIEW ]]; then
  2877 				$CODEREVIEW -y "$textcomm" \
  2878 				    -e $ocr $nfile \
  2879 				    > /tmp/$$.psfile 2>/dev/null &&
  2880 				    cat /tmp/$$.psfile >> $WDIR/$WNAME.ps
  2881 				if [[ $? -eq 0 ]]; then
  2882 					print " ps\c"
  2883 				else
  2884 					print " ps[fail]\c"
  2885 				fi
  2886 			fi
  2887 		fi
  2888 	fi
  2890 	if [[ -f $ofile && -z $mv_but_nodiff ]]; then
  2891 	    if [[ -n $its_a_jar ]]; then
  2892 		source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html
  2893 	    else
  2894 		source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html
  2895 	    fi
  2896 		print " old\c"
  2897 	fi
  2899 	if [[ -f $nfile ]]; then
  2900 	    if [[ -n $its_a_jar ]]; then
  2901 		source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html
  2902 	    else
  2903 		source_to_html New $P < $nfile > $WDIR/$DIR/$F.html
  2904 	    fi
  2905 		print " new\c"
  2906 	fi
  2908 	print
  2909 done < $FLIST
  2911 # Create the new style mercurial patch here using hg export -r [all-revs] -g -o $CHANGESETPATH
  2912 if [[ $SCM_MODE == "mercurial" ]]; then
  2913   if [[ "$HG_LIST_FROM_COMMIT" -eq 1 && $flist_mode == "auto" ]]; then
  2914     EXPORTCHANGESET="$WNAME.changeset"
  2915     CHANGESETPATH=${WDIR}/${EXPORTCHANGESET}
  2916     rm -f $CHANGESETPATH
  2917     touch $CHANGESETPATH
  2918     if [[ -n $ALL_CREV ]]; then
  2919       rev_opt=
  2920       for rev in $ALL_CREV; do
  2921         rev_opt="$rev_opt --rev $rev"
  2922       done
  2923     elif [[ -n $FIRST_CREV ]]; then
  2924       rev_opt="--rev $FIRST_CREV"
  2925     fi
  2927     if [[ -n $rev_opt ]]; then
  2928       (cd $CWS;hg export -g $rev_opt -o $CHANGESETPATH)
  2929       echo "Created changeset: $CHANGESETPATH" 1>&2
  2930       # Use it in place of the jdk.patch created above
  2931       rm -f $WDIR/$WNAME.patch
  2932     fi
  2933   set +x
  2934   fi
  2935 fi
  2937 frame_nav_js > $WDIR/ancnav.js
  2938 frame_navigation > $WDIR/ancnav.html
  2940 if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then
  2941 	print " Generating PDF: \c"
  2942 	fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf
  2943 	print "Done."
  2944 fi
  2946 # Now build the index.html file that contains
  2947 # links to the source files and their diffs.
  2949 cd $CWS
  2951 # Save total changed lines for Code Inspection.
  2952 print "$TOTL" > $WDIR/TotalChangedLines
  2954 print "     index.html: \c"
  2955 INDEXFILE=$WDIR/index.html
  2956 exec 3<&1			# duplicate stdout to FD3.
  2957 exec 1<&-			# Close stdout.
  2958 exec > $INDEXFILE		# Open stdout to index file.
  2960 print "$HTML<head>"
  2961 print "<meta name=\"scm\" content=\"$SCM_MODE\" />"
  2962 print "$STDHEAD"
  2963 print "<title>$WNAME</title>"
  2964 print "</head>"
  2965 print "<body id=\"SUNWwebrev\">"
  2966 print "<div class=\"summary\">"
  2967 print "<h2>Code Review for $WNAME</h2>"
  2969 print "<table>"
  2971 if [[ -z $uflag ]]
  2972 then
  2973     if [[ $SCM_MODE == "mercurial" ]]
  2974     then
  2976         # Let's try to extract the user name from the .hgrc file
  2978 	username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'`
  2979     fi
  2981     if [[ -z $username ]]
  2982     then
  2984         # Figure out the username and gcos name.  To maintain compatibility
  2985         # with passwd(4), we must support '&' substitutions.
  2987 	username=`id | cut -d '(' -f 2 | cut -d ')' -f 1`
  2988 	if [[ -x $GETENT ]]; then
  2989 	    realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1`
  2990 	fi
  2991 	userupper=`print "$username" | sed 's/\<./\u&/g'`
  2992 	realname=`print $realname | sed s/\&/$userupper/`
  2993     fi
  2994 fi
  2996 date="on `date`"
  2998 if [[ -n "$username" && -n "$realname" ]]; then
  2999 	print "<tr><th>Prepared by:</th>"
  3000 	print "<td>$realname ($username) $date</td></tr>"
  3001 elif [[ -n "$username" ]]; then
  3002 	print "<tr><th>Prepared by:</th><td>$username $date</td></tr>"
  3003 fi
  3005 print "<tr><th>Workspace:</th><td>$CWS</td></tr>"
  3006 if [[ -n $parent_webrev ]]; then
  3007         print "<tr><th>Compare against:</th><td>"
  3008 	print "webrev at $parent_webrev"
  3009 else
  3010     if [[ -n $OUTPWS2 ]]; then
  3011         print "<tr><th>Compare against:</th><td>"
  3012 	print "$OUTPWS2"
  3013     fi
  3014 fi
  3015 print "</td></tr>"
  3016 if [[ -n $rflag ]]; then
  3017     print "<tr><th>Compare against version:</th><td>$PARENT_REV</td></tr>"
  3018 elif [[ -n $OUTREV ]]; then
  3019     if [[ -z $forestflag ]]; then
  3020         print "<tr><th>Compare against version:</th><td>$OUTREV</td></tr>"
  3021     fi
  3022 fi
  3023 if [[ -n $HG_BRANCH ]]; then
  3024     print "<tr><th>Branch:</th><td>$HG_BRANCH</td></tr>"
  3025 fi
  3027 print "<tr><th>Summary of changes:</th><td>"
  3028 printCI $TOTL $TINS $TDEL $TMOD $TUNC
  3029 print "</td></tr>"
  3031 if [[ -f $WDIR/$WNAME.patch ]]; then
  3032   print "<tr><th>Patch of changes:</th><td>"
  3033   print "<a href=\"$WNAME.patch\">$WNAME.patch</a></td></tr>"
  3034 elif [[ -f $CHANGESETPATH ]]; then
  3035   print "<tr><th>Changeset:</th><td>"
  3036   print "<a href=\"$EXPORTCHANGESET\">$EXPORTCHANGESET</a></td></tr>"
  3037 fi
  3039 if [[ -f $WDIR/$WNAME.pdf ]]; then
  3040 	print "<tr><th>Printable review:</th><td>"
  3041 	print "<a href=\"$WNAME.pdf\">$WNAME.pdf</a></td></tr>"
  3042 fi
  3044 if [[ -n "$iflag" ]]; then
  3045 	print "<tr><th>Author comments:</th><td><div>"
  3046 	cat /tmp/$$.include
  3047 	print "</div></td></tr>"
  3048 fi
  3049 # Add links to referenced CRs, if any
  3050 # external URL has a <title> like:
  3051 # <title>Bug ID: 6641309 Wrong Cookie separator used in HttpURLConnection</title>
  3052 # while internal URL has <title> like:
  3053 # <title>[#JDK-6641309] Wrong Cookie separator used in HttpURLConnection</title>
  3055 if [[ -n $CRID ]]; then
  3056     for id in $CRID
  3057     do
  3058         if [[ -z "$Oflag" ]]; then
  3059             #add "JDK-" to raw bug id for jbs links.
  3060             id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'`
  3061         fi
  3062         print "<tr><th>Bug id:</th><td>"
  3063         url="${BUGURL}${id}"
  3064         if [[ -n "$Oflag" ]]; then
  3065             cleanup='s/Bug ID: \([0-9]\{5,\}\) \(.*\)/JDK-\1 : \2/'
  3066         else
  3067             cleanup='s|\[#\(JDK-[0-9]\{5,\}\)\] \(.*\)|\1 : \2|'
  3068         fi
  3069         if [[ -n $WGET ]]; then
  3070             msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '<title>' | sed 's/<title>\(.*\)<\/title>/\1/' | sed "$cleanup" | html_quote`
  3071         fi
  3072         if [[ -z $msg ]]; then
  3073             msg="${id}"
  3074         fi
  3076         print "<a href=\"$url\">$msg</a>"
  3078         print "</td></tr>"
  3079     done
  3080 fi
  3081 print "<tr><th>Legend:</th><td>"
  3082 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>"
  3083 print "</table>"
  3084 print "</div>"
  3087 # Second pass through the files: generate the rest of the index file
  3089 while read LINE
  3090 do
  3091 	set - $LINE
  3092         if [[ $1 == "Revision:" ]]; then
  3093             FIRST_CREV=`expr $3 + 1`
  3094             continue
  3095         fi
  3096 	P=$1
  3098 	if [[ $# == 2 ]]; then
  3099 		PP=$2
  3100 		oldname=" <i>(was $PP)</i>"
  3102 	else
  3103 		PP=$P
  3104 		oldname=""
  3105 	fi
  3107 	DIR=${P%/*}
  3108 	if [[ $DIR == $P ]]; then
  3109 		DIR="."   # File at root of workspace
  3110 	fi
  3112 	# Avoid processing the same file twice.
  3113 	# It's possible for renamed files to
  3114 	# appear twice in the file list
  3116 	F=$WDIR/$P
  3118 	print "<p><code>"
  3120 	# If there's a diffs file, make diffs links
  3122         NODIFFS=
  3123 	if [[ -f $F.cdiff.html ]]; then
  3124 		print "<a href=\"$P.cdiff.html\">Cdiffs</a>"
  3125 		print "<a href=\"$P.udiff.html\">Udiffs</a>"
  3127 		if [[ -f $F.wdiff.html && -x $WDIFF ]]; then
  3128 			print "<a href=\"$P.wdiff.html\">Wdiffs</a>"
  3129 		fi
  3131 		print "<a href=\"$P.sdiff.html\">Sdiffs</a>"
  3133 		print "<a href=\"$P.frames.html\">Frames</a>"
  3134 	else
  3135                 NODIFFS=1
  3136 		print " ------ ------ ------"
  3138 		if [[ -x $WDIFF ]]; then
  3139 			print " ------"
  3140 		fi
  3142 		print " ------"
  3143 	fi
  3145 	# If there's an old file, make the link
  3147         NOOLD=
  3148 	if [[ -f $F-.html ]]; then
  3149 		print "<a href=\"$P-.html\">Old</a>"
  3150 	else
  3151                 NOOLD=1
  3152 		print " ---"
  3153 	fi
  3155 	# If there's an new file, make the link
  3157         NONEW=
  3158 	if [[ -f $F.html ]]; then
  3159 		print "<a href=\"$P.html\">New</a>"
  3160 	else
  3161                 NONEW=1
  3162 		print " ---"
  3163 	fi
  3165 	if [[ -f $F.patch ]]; then
  3166 		print "<a href=\"$P.patch\">Patch</a>"
  3167 	else
  3168 		print " -----"
  3169 	fi
  3171 	if [[ -f $WDIR/raw_files/new/$P ]]; then
  3172 		print "<a href=\"raw_files/new/$P\">Raw</a>"
  3173 	else
  3174 		print " ---"
  3175 	fi
  3176         print "</code>"
  3177         if [[ -n $NODIFFS && -z $oldname ]]; then
  3178             if [[ -n $NOOLD ]]; then
  3179                 print "<font color=green><b>$P</b></font>"
  3180             elif [[ -n $NONEW ]]; then
  3181                 print "<font color=red><b>$P</b></font>"
  3182             fi
  3183         else
  3184 	    print "<b>$P</b> $oldname"
  3185         fi
  3188 	# Check for usr/closed
  3190 	if [ ! -z "$Oflag" ]; then
  3191 		if [[ $P == usr/closed/* ]]; then
  3192 			print "&nbsp;&nbsp;<i>Closed source: omitted from" \
  3193 			    "this review</i>"
  3194 		fi
  3195 	fi
  3197 	print "</p><blockquote>\c"
  3198 	# Insert delta comments if any
  3199 	comments=`getcomments html $P $PP`
  3200 	if [ -n "$comments" ]; then
  3201 	    print "<pre>$comments</pre>"
  3202 	fi
  3204 	# Add additional comments comment
  3206 	print "<!-- Add comments to explain changes in $P here -->"
  3208 	# Add count of changes.
  3210 	if [[ -f $F.count ]]; then
  3211 	    cat $F.count
  3212 	    rm $F.count
  3213 	fi
  3214         print "</blockquote>"
  3215 done < $FLIST
  3217 print
  3218 print
  3219 print "<hr />"
  3220 print "<p style=\"font-size: small\">"
  3221 print "This code review page was prepared using <b>$0</b>"
  3222 print "(vers $WEBREV_UPDATED)."
  3223 print "</body>"
  3224 print "</html>"
  3226 if [[ -n $ZIP ]]; then
  3227     # Let's generate a zip file for convenience
  3228     cd $WDIR/..
  3229     if [ -f webrev.zip ]; then
  3230 	rm webrev.zip
  3231     fi
  3232     $ZIP -r webrev webrev >/dev/null 2>&1
  3233 fi
  3235 exec 1<&-			# Close FD 1.
  3236 exec 1<&3			# dup FD 3 to restore stdout.
  3237 exec 3<&-			# close FD 3.
  3239 print "Done."
  3240 print "Output to: $WDIR"

mercurial